[
  {
    "path": ".gitattributes",
    "content": "* text=auto\n\n*.adoc     text\n*.html     text\n*.in       text\n*.json     text\n*.md       text\n*.proto    text\n*.py       text\n*.rs       text\n*.service  text\n*.sh       text\n*.toml     text\n*.txt      text\n*.x        text\n*.yml      text\n\n.vscode/*.json linguist-language=JSON-with-Comments\n\n# Configure changelog files to use union merge strategy\n# This prevents merge conflicts by automatically combining changes from both branches\nCHANGELOG.md merge=union\nchangelog.md merge=union\nCHANGELOG.txt merge=union\nchangelog.txt merge=union\n\n*.raw    binary\n*.bin    binary\n*.png    binary\n*.jpg    binary\n*.jpeg   binary\n*.gif    binary\n*.ico    binary\n*.mov    binary\n*.mp4    binary\n*.mp3    binary\n*.flv    binary\n*.fla    binary\n*.swf    binary\n*.gz     binary\n*.zip    binary\n*.7z     binary\n*.ttf    binary\n*.eot    binary\n*.woff   binary\n*.pyc    binary\n*.pdf    binary\n*.ez     binary\n*.bz2    binary\n*.swp    binary\n"
  },
  {
    "path": ".github/ci/book.sh",
    "content": "#!/bin/bash\n## on push branch=main\n## priority -100\n## dedup dequeue\n## cooldown 15m\n\nset -euxo pipefail\n\nmake -C docs\n\nexport KUBECONFIG=/ci/secrets/kubeconfig.yml\nPOD=$(kubectl get po -l app=website -o jsonpath={.items[0].metadata.name})\n\nmkdir -p build\nmv docs/book build/book\ntar -C build -cf book.tar book\nkubectl exec $POD -- mkdir -p /usr/share/nginx/html\nkubectl cp book.tar $POD:/usr/share/nginx/html/\nkubectl exec $POD -- find /usr/share/nginx/html\nkubectl exec $POD -- tar -C /usr/share/nginx/html -xvf /usr/share/nginx/html/book.tar\n"
  },
  {
    "path": ".github/ci/build-nightly.sh",
    "content": "#!/bin/bash\n## on push branch~=gh-readonly-queue/main/.*\n## on pull_request\n\nset -euo pipefail\n\nexport RUSTUP_HOME=/ci/cache/rustup\nexport CARGO_HOME=/ci/cache/cargo\nexport CARGO_TARGET_DIR=/ci/cache/target\nexport PATH=$CARGO_HOME/bin:$PATH\nmv rust-toolchain-nightly.toml rust-toolchain.toml\n\n# needed for \"dumb HTTP\" transport support\n# used when pointing stm32-metapac to a CI-built one.\nexport CARGO_NET_GIT_FETCH_WITH_CLI=true\n\n# Restore lockfiles\nif [ -f /ci/cache/lockfiles.tar ]; then\n    echo Restoring lockfiles...\n    tar xf /ci/cache/lockfiles.tar\nfi\n\nhashtime restore /ci/cache/filetime.json || true\nhashtime save /ci/cache/filetime.json\n\ncargo install --git https://github.com/embassy-rs/cargo-embassy-devtool --locked --rev 8f4cfa11324c582467c2aab161ef963ff7a2b884\n\n./ci-nightly.sh\n\n# Save lockfiles\necho Saving lockfiles...\nfind . -type f -name Cargo.lock -exec tar -cf /ci/cache/lockfiles.tar '{}' \\+\n"
  },
  {
    "path": ".github/ci/build-xtensa.sh",
    "content": "#!/bin/bash\n## on push branch~=gh-readonly-queue/main/.*\n## on pull_request\n\nset -euo pipefail\n\nexport RUSTUP_HOME=/ci/cache/rustup\nexport CARGO_HOME=/ci/cache/cargo\nexport CARGO_TARGET_DIR=/ci/cache/target\nexport PATH=$CARGO_HOME/bin:$PATH\n\n# needed for \"dumb HTTP\" transport support\n# used when pointing stm32-metapac to a CI-built one.\nexport CARGO_NET_GIT_FETCH_WITH_CLI=true\n\ncargo install espup --locked\nespup install --toolchain-version 1.92.0.0\n\n# Restore lockfiles\nif [ -f /ci/cache/lockfiles.tar ]; then\n    echo Restoring lockfiles...\n    tar xf /ci/cache/lockfiles.tar\nfi\n\nhashtime restore /ci/cache/filetime.json || true\nhashtime save /ci/cache/filetime.json\n\ncargo install --git https://github.com/embassy-rs/cargo-embassy-devtool --locked --rev 8f4cfa11324c582467c2aab161ef963ff7a2b884\n\n./ci-xtensa.sh\n\n# Save lockfiles\necho Saving lockfiles...\nfind . -type f -name Cargo.lock -exec tar -cf /ci/cache/lockfiles.tar '{}' \\+\n"
  },
  {
    "path": ".github/ci/build.sh",
    "content": "#!/bin/bash\n## on push branch~=gh-readonly-queue/main/.*\n## on pull_request\n\nset -euo pipefail\n\nexport RUSTUP_HOME=/ci/cache/rustup\nexport CARGO_HOME=/ci/cache/cargo\nexport CARGO_TARGET_DIR=/ci/cache/target\nexport PATH=$CARGO_HOME/bin:$PATH\nif [ -f /ci/secrets/teleprobe-token.txt ]; then\n    echo Got teleprobe token!\n    export TELEPROBE_HOST=https://teleprobe.embassy.dev\n    export TELEPROBE_TOKEN=$(cat /ci/secrets/teleprobe-token.txt)\n    export TELEPROBE_CACHE=/ci/cache/teleprobe_cache.json\nfi\n\n# needed for \"dumb HTTP\" transport support\n# used when pointing stm32-metapac to a CI-built one.\nexport CARGO_NET_GIT_FETCH_WITH_CLI=true\n\n# Restore lockfiles\nif [ -f /ci/cache/lockfiles.tar ]; then\n    echo Restoring lockfiles...\n    tar xf /ci/cache/lockfiles.tar\nfi\n\nhashtime restore /ci/cache/filetime.json || true\nhashtime save /ci/cache/filetime.json\n\ncargo install --git https://github.com/embassy-rs/cargo-embassy-devtool --locked --rev 8f4cfa11324c582467c2aab161ef963ff7a2b884\n\n./ci.sh\n\n# Save lockfiles\necho Saving lockfiles...\nfind . -type f -name Cargo.lock -exec tar -cf /ci/cache/lockfiles.tar '{}' \\+\n"
  },
  {
    "path": ".github/ci/doc.sh",
    "content": "#!/bin/bash\n## on push branch=main\n## priority -100\n## dedup dequeue\n## cooldown 15m\n\nset -euxo pipefail\n\nexport RUSTUP_HOME=/ci/cache/rustup\nexport CARGO_HOME=/ci/cache/cargo\nexport CARGO_TARGET_DIR=/ci/cache/target\nexport PATH=$CARGO_HOME/bin:$PATH\nmv rust-toolchain-nightly.toml rust-toolchain.toml\n\ncargo install --git https://github.com/embassy-rs/cargo-embassy-devtool --locked --rev 8f4cfa11324c582467c2aab161ef963ff7a2b884\ncargo install --git https://github.com/embassy-rs/docserver --locked --rev 09805a79beef037d283192146e2b546cb1c0e931\n\ncargo embassy-devtool doc -o webroot\n\nexport KUBECONFIG=/ci/secrets/kubeconfig.yml\nPOD=$(kubectl get po -l app=docserver -o jsonpath={.items[0].metadata.name})\nkubectl cp webroot/crates $POD:/data\nkubectl cp webroot/static $POD:/data\n"
  },
  {
    "path": ".github/ci/janitor.sh",
    "content": "#!/bin/bash\n## on push branch~=gh-readonly-queue/main/.*\n## on pull_request\n\nset -euo pipefail\n\nexport RUSTUP_HOME=/ci/cache/rustup\nexport CARGO_HOME=/ci/cache/cargo\nexport CARGO_TARGET_DIR=/ci/cache/target\nexport PATH=$CARGO_HOME/bin:$PATH\n\ncargo install --git https://github.com/embassy-rs/cargo-embassy-devtool --locked --rev 8f4cfa11324c582467c2aab161ef963ff7a2b884\n\ncargo embassy-devtool check-crlf\ncargo embassy-devtool check-manifest\n"
  },
  {
    "path": ".github/ci/rustfmt.sh",
    "content": "#!/bin/bash\n## on push branch~=gh-readonly-queue/main/.*\n## on pull_request\n\nset -euo pipefail\n\nexport RUSTUP_HOME=/ci/cache/rustup\nexport CARGO_HOME=/ci/cache/cargo\nexport CARGO_TARGET_DIR=/ci/cache/target\nmv rust-toolchain-nightly.toml rust-toolchain.toml\n\nfind . -name '*.rs' -not -path '*target*' | xargs rustfmt --check  --skip-children --unstable-features --edition 2024\n"
  },
  {
    "path": ".github/ci/test-nightly.sh",
    "content": "#!/bin/bash\n## on push branch~=gh-readonly-queue/main/.*\n## on pull_request\n\nset -euo pipefail\n\nexport RUSTUP_HOME=/ci/cache/rustup\nexport CARGO_HOME=/ci/cache/cargo\nexport CARGO_TARGET_DIR=/ci/cache/target\nmv rust-toolchain-nightly.toml rust-toolchain.toml\n\ncargo test --manifest-path ./embassy-executor/Cargo.toml\ncargo test --manifest-path ./embassy-executor/Cargo.toml --features nightly\n\nMIRIFLAGS=-Zmiri-ignore-leaks cargo miri test --manifest-path ./embassy-executor/Cargo.toml\nMIRIFLAGS=-Zmiri-ignore-leaks cargo miri test --manifest-path ./embassy-executor/Cargo.toml --features nightly\nMIRIFLAGS=-Zmiri-ignore-leaks cargo miri test --manifest-path ./embassy-sync/Cargo.toml\n"
  },
  {
    "path": ".github/ci/test.sh",
    "content": "#!/bin/bash\n## on push branch~=gh-readonly-queue/main/.*\n## on pull_request\n\nset -euo pipefail\n\nexport RUSTUP_HOME=/ci/cache/rustup\nexport CARGO_HOME=/ci/cache/cargo\nexport CARGO_TARGET_DIR=/ci/cache/target\n\n# needed for \"dumb HTTP\" transport support\n# used when pointing stm32-metapac to a CI-built one.\nexport CARGO_NET_GIT_FETCH_WITH_CLI=true\n\ncargo test --manifest-path ./embassy-executor/Cargo.toml --features metadata-name\ncargo test --manifest-path ./embassy-futures/Cargo.toml\ncargo test --manifest-path ./embassy-sync/Cargo.toml\ncargo test --manifest-path ./embassy-embedded-hal/Cargo.toml\ncargo test --manifest-path ./embassy-hal-internal/Cargo.toml\ncargo test --manifest-path ./embassy-time/Cargo.toml --features mock-driver,embassy-time-queue-utils/generic-queue-8\ncargo test --manifest-path ./embassy-time-driver/Cargo.toml\n\ncargo test --manifest-path ./embassy-boot/Cargo.toml\ncargo test --manifest-path ./embassy-boot/Cargo.toml --features ed25519-dalek\ncargo test --manifest-path ./embassy-boot/Cargo.toml --features ed25519-salty\n\ncargo test --manifest-path ./embassy-nrf/Cargo.toml --no-default-features --features nrf52840,time-driver-rtc1,gpiote\n\ncargo test --manifest-path ./embassy-rp/Cargo.toml --no-default-features --features time-driver,rp2040,_test\ncargo test --manifest-path ./embassy-rp/Cargo.toml --no-default-features --features time-driver,rp235xa,_test\n\ncargo test --manifest-path ./embassy-stm32/Cargo.toml --no-default-features --features stm32f429vg,time-driver-any,exti,single-bank\ncargo test --manifest-path ./embassy-stm32/Cargo.toml --no-default-features --features stm32f429vg,time-driver-any,exti,dual-bank\ncargo test --manifest-path ./embassy-stm32/Cargo.toml --no-default-features --features stm32f732ze,time-driver-any,exti\ncargo test --manifest-path ./embassy-stm32/Cargo.toml --no-default-features --features stm32f769ni,time-driver-any,exti,single-bank\ncargo test --manifest-path ./embassy-stm32/Cargo.toml --no-default-features --features stm32f769ni,time-driver-any,exti,dual-bank\n\ncargo test --manifest-path ./embassy-net-adin1110/Cargo.toml\ncargo test --manifest-path ./embassy-usb-dfu/Cargo.toml --features dfu"
  },
  {
    "path": ".github/workflows/matrix-bot-issues.yml",
    "content": "name: Matrix bot for issues\non:\n  issues:\n    types: [opened]\n\njobs:\n  notify:\n    if: github.repository == 'embassy-rs/embassy'\n    runs-on: ubuntu-latest\n    continue-on-error: true\n    steps:\n      - name: send message\n        uses: actions/github-script@v8\n        with:\n          script: |\n            const action = context.payload.action;\n            const title = context.payload.issue.title;\n            const issueBody = context.payload.issue.body;\n            const url = context.payload.issue.html_url;\n\n            // Determine message prefix\n            let prefix;\n            let roomId;\n            if (action === 'opened' && (title + ' ' + issueBody).toLowerCase().includes('esp32')) {\n              // Nag danielb\n              prefix = 'New Issue';\n              roomId = '!oENPKPHeKwiPfIVHER:matrix.org';\n            } else {\n              return; // Unknown action, skip\n            }\n\n            // HTML escape the title\n            const titleEscaped = title\n              .replace(/&/g, '&amp;')\n              .replace(/</g, '&lt;')\n              .replace(/>/g, '&gt;')\n              .replace(/\"/g, '&quot;')\n              .replace(/'/g, '&#39;');\n\n            const body = `${prefix}: ${title} - ${url}`;\n            const formattedBody = `${prefix}: <a href=\"${url}\">${titleEscaped}</a>`;\n\n            const accessToken = process.env.MATRIX_ACCESS_TOKEN;\n\n            const response = await fetch(\n              `https://matrix.org/_matrix/client/r0/rooms/${roomId}/send/m.room.message`,\n              {\n                method: 'POST',\n                headers: {\n                  'Content-Type': 'application/json',\n                  'Authorization': `Bearer ${accessToken}`\n                },\n                body: JSON.stringify({\n                  msgtype: 'm.text',\n                  format: 'org.matrix.custom.html',\n                  body: body,\n                  formatted_body: formattedBody\n                })\n              }\n            );\n\n            if (!response.ok) {\n              throw new Error(`Matrix API request failed: ${response.status} ${response.statusText}`);\n            }\n        env:\n          MATRIX_ACCESS_TOKEN: ${{ secrets.MATRIX_ACCESS_TOKEN }}\n"
  },
  {
    "path": ".github/workflows/matrix-bot.yml",
    "content": "name: Matrix bot\non:\n  pull_request_target:\n    types: [opened, closed]\n\njobs:\n  notify:\n    if: github.repository == 'embassy-rs/embassy'\n    runs-on: ubuntu-latest\n    continue-on-error: true\n    steps:\n      - name: send message\n        uses: actions/github-script@v8\n        with:\n          script: |\n            const action = context.payload.action;\n            const merged = context.payload.pull_request.merged;\n            const title = context.payload.pull_request.title;\n            const url = context.payload.pull_request.html_url;\n\n            // Determine message prefix\n            let prefix;\n            if (action === 'opened') {\n              prefix = 'New PR';\n            } else if (action === 'closed' && merged) {\n              prefix = 'PR merged';\n            } else if (action === 'closed' && !merged) {\n              prefix = 'PR closed without merging';\n            } else {\n              return; // Unknown action, skip\n            }\n\n            // HTML escape the title\n            const titleEscaped = title\n              .replace(/&/g, '&amp;')\n              .replace(/</g, '&lt;')\n              .replace(/>/g, '&gt;')\n              .replace(/\"/g, '&quot;')\n              .replace(/'/g, '&#39;');\n\n            const body = `${prefix}: ${title} - ${url}`;\n            const formattedBody = `${prefix}: <a href=\"${url}\">${titleEscaped}</a>`;\n\n            const roomId = '!YoLPkieCYHGzdjUhOK:matrix.org';\n            const accessToken = process.env.MATRIX_ACCESS_TOKEN;\n\n            const response = await fetch(\n              `https://matrix.org/_matrix/client/r0/rooms/${roomId}/send/m.room.message`,\n              {\n                method: 'POST',\n                headers: {\n                  'Content-Type': 'application/json',\n                  'Authorization': `Bearer ${accessToken}`\n                },\n                body: JSON.stringify({\n                  msgtype: 'm.text',\n                  format: 'org.matrix.custom.html',\n                  body: body,\n                  formatted_body: formattedBody\n                })\n              }\n            );\n\n            if (!response.ok) {\n              throw new Error(`Matrix API request failed: ${response.status} ${response.statusText}`);\n            }\n        env:\n          MATRIX_ACCESS_TOKEN: ${{ secrets.MATRIX_ACCESS_TOKEN }}\n"
  },
  {
    "path": ".gitignore",
    "content": "target\ntarget_ci\ntarget_ci_stable\nCargo.lock\nthird_party\nwebroot\n/Cargo.toml\nout/\n# editor artifacts\n.zed\n.neoconf.json\n*.vim\n**/.idea\n**/.nvim.lua\n**/.cargo\n!**/tests/**/.cargo\n!**/examples/**/.cargo\n"
  },
  {
    "path": ".helix/languages.toml",
    "content": "[language-server.rust-analyzer.config.cargo]\nallTargets = false\nnoDefaultFeatures = true\ntarget = \"thumbv8m.main-none-eabihf\"\nfeatures = [\"stm32n657x0\", \"time-driver-any\", \"unstable-pac\", \"exti\"]\n\n[language-server.rust-analyzer.config.check]\nallTargets = false\nnoDefaultFeatures = true\ntarget = \"thumbv8m.main-none-eabihf\"\nfeatures = [\"stm32n657x0\", \"time-driver-any\", \"unstable-pac\", \"exti\"]\n"
  },
  {
    "path": ".vscode/.gitignore",
    "content": "*.cortex-debug.*.json\nlaunch.json\ntasks.json\n*.cfg\n"
  },
  {
    "path": ".vscode/extensions.json",
    "content": "{\n\t// See https://go.microsoft.com/fwlink/?LinkId=827846 to learn about workspace recommendations.\n\t// Extension identifier format: ${publisher}.${name}. Example: vscode.csharp\n\t// List of extensions which should be recommended for users of this workspace.\n\t\"recommendations\": [\n\t\t\"rust-lang.rust-analyzer\",\n\t\t\"tamasfe.even-better-toml\",\n\t],\n\t// List of extensions recommended by VS Code that should not be recommended for users of this workspace.\n\t\"unwantedRecommendations\": []\n}"
  },
  {
    "path": ".vscode/settings.json",
    "content": "{\n  \"[toml]\": {\n    \"editor.formatOnSave\": false\n  },\n  \"[markdown]\": {\n    \"editor.formatOnSave\": false\n  },\n  \"rust-analyzer.rustfmt.extraArgs\": [\n    \"+nightly\"\n  ],\n  \"rust-analyzer.check.allTargets\": false,\n  \"rust-analyzer.check.noDefaultFeatures\": true,\n  \"rust-analyzer.cargo.noDefaultFeatures\": true,\n  \"rust-analyzer.showUnlinkedFileNotification\": false,\n  // Uncomment the target of your chip.\n  //\"rust-analyzer.cargo.target\": \"thumbv6m-none-eabi\",\n  //\"rust-analyzer.cargo.target\": \"thumbv7m-none-eabi\",\n  \"rust-analyzer.cargo.target\": \"thumbv7em-none-eabi\",\n  //\"rust-analyzer.cargo.target\": \"thumbv7em-none-eabihf\",\n  //\"rust-analyzer.cargo.target\": \"thumbv8m.main-none-eabihf\",\n  \"rust-analyzer.cargo.features\": [\n    // Comment out these features when working on the examples. Most example crates do not have any cargo features.\n    \"stm32f107rb\",\n    \"time-driver-any\",\n    \"unstable-pac\",\n    \"exti\",\n    \"rt\",\n  ],\n  \"rust-analyzer.linkedProjects\": [\n    \"embassy-stm32/Cargo.toml\",\n    // To work on the examples, comment the line above and all of the cargo.features lines,\n    // then uncomment ONE line below to select the chip you want to work on.\n    // This makes rust-analyzer work on the example crate and all its dependencies.\n    // \"examples/mspm0c1104/Cargo.toml\",\n    // \"examples/mspm0g3507/Cargo.toml\",\n    // \"examples/mspm0g3519/Cargo.toml\",\n    // \"examples/mspm0g5187/Cargo.toml\",\n    // \"examples/mspm0l1306/Cargo.toml\",\n    // \"examples/mspm0l2228/Cargo.toml\",\n    // \"examples/nrf52840-rtic/Cargo.toml\",\n    // \"examples/nrf5340/Cargo.toml\",\n    // \"examples/nrf-rtos-trace/Cargo.toml\",\n    // \"examples/mimxrt1011/Cargo.toml\",\n    // \"examples/mimxrt1062-evk/Cargo.toml\",\n    // \"examples/rp/Cargo.toml\",\n    // \"examples/std/Cargo.toml\",\n    // \"examples/stm32c0/Cargo.toml\",\n    // \"examples/stm32f0/Cargo.toml\",\n    // \"examples/stm32f1/Cargo.toml\",\n    // \"examples/stm32f2/Cargo.toml\",\n    // \"examples/stm32f3/Cargo.toml\",\n    // \"examples/stm32f334/Cargo.toml\",\n    // \"examples/stm32f4/Cargo.toml\",\n    // \"examples/stm32f7/Cargo.toml\",\n    // \"examples/stm32g0/Cargo.toml\",\n    // \"examples/stm32g4/Cargo.toml\",\n    // \"examples/stm32h5/Cargo.toml\",\n    // \"examples/stm32h7/Cargo.toml\",\n    // \"examples/stm32l0/Cargo.toml\",\n    // \"examples/stm32l1/Cargo.toml\",\n    // \"examples/stm32l4/Cargo.toml\",\n    // \"examples/stm32l5/Cargo.toml\",\n    // \"examples/stm32u5/Cargo.toml\",\n    // \"examples/stm32wb/Cargo.toml\",\n    // \"examples/stm32wl/Cargo.toml\",\n    // \"examples/wasm/Cargo.toml\",\n  ],\n}"
  },
  {
    "path": "LICENSE-APACHE",
    "content": "                              Apache License\n                        Version 2.0, January 2004\n                     http://www.apache.org/licenses/\n\nTERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n1. Definitions.\n\n   \"License\" shall mean the terms and conditions for use, reproduction,\n   and distribution as defined by Sections 1 through 9 of this document.\n\n   \"Licensor\" shall mean the copyright owner or entity authorized by\n   the copyright owner that is granting the License.\n\n   \"Legal Entity\" shall mean the union of the acting entity and all\n   other entities that control, are controlled by, or are under common\n   control with that entity. For the purposes of this definition,\n   \"control\" means (i) the power, direct or indirect, to cause the\n   direction or management of such entity, whether by contract or\n   otherwise, or (ii) ownership of fifty percent (50%) or more of the\n   outstanding shares, or (iii) beneficial ownership of such entity.\n\n   \"You\" (or \"Your\") shall mean an individual or Legal Entity\n   exercising permissions granted by this License.\n\n   \"Source\" form shall mean the preferred form for making modifications,\n   including but not limited to software source code, documentation\n   source, and configuration files.\n\n   \"Object\" form shall mean any form resulting from mechanical\n   transformation or translation of a Source form, including but\n   not limited to compiled object code, generated documentation,\n   and conversions to other media types.\n\n   \"Work\" shall mean the work of authorship, whether in Source or\n   Object form, made available under the License, as indicated by a\n   copyright notice that is included in or attached to the work\n   (an example is provided in the Appendix below).\n\n   \"Derivative Works\" shall mean any work, whether in Source or Object\n   form, that is based on (or derived from) the Work and for which the\n   editorial revisions, annotations, elaborations, or other modifications\n   represent, as a whole, an original work of authorship. For the purposes\n   of this License, Derivative Works shall not include works that remain\n   separable from, or merely link (or bind by name) to the interfaces of,\n   the Work and Derivative Works thereof.\n\n   \"Contribution\" shall mean any work of authorship, including\n   the original version of the Work and any modifications or additions\n   to that Work or Derivative Works thereof, that is intentionally\n   submitted to Licensor for inclusion in the Work by the copyright owner\n   or by an individual or Legal Entity authorized to submit on behalf of\n   the copyright owner. For the purposes of this definition, \"submitted\"\n   means any form of electronic, verbal, or written communication sent\n   to the Licensor or its representatives, including but not limited to\n   communication on electronic mailing lists, source code control systems,\n   and issue tracking systems that are managed by, or on behalf of, the\n   Licensor for the purpose of discussing and improving the Work, but\n   excluding communication that is conspicuously marked or otherwise\n   designated in writing by the copyright owner as \"Not a Contribution.\"\n\n   \"Contributor\" shall mean Licensor and any individual or Legal Entity\n   on behalf of whom a Contribution has been received by Licensor and\n   subsequently incorporated within the Work.\n\n2. Grant of Copyright License. Subject to the terms and conditions of\n   this License, each Contributor hereby grants to You a perpetual,\n   worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n   copyright license to reproduce, prepare Derivative Works of,\n   publicly display, publicly perform, sublicense, and distribute the\n   Work and such Derivative Works in Source or Object form.\n\n3. Grant of Patent License. Subject to the terms and conditions of\n   this License, each Contributor hereby grants to You a perpetual,\n   worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n   (except as stated in this section) patent license to make, have made,\n   use, offer to sell, sell, import, and otherwise transfer the Work,\n   where such license applies only to those patent claims licensable\n   by such Contributor that are necessarily infringed by their\n   Contribution(s) alone or by combination of their Contribution(s)\n   with the Work to which such Contribution(s) was submitted. If You\n   institute patent litigation against any entity (including a\n   cross-claim or counterclaim in a lawsuit) alleging that the Work\n   or a Contribution incorporated within the Work constitutes direct\n   or contributory patent infringement, then any patent licenses\n   granted to You under this License for that Work shall terminate\n   as of the date such litigation is filed.\n\n4. Redistribution. You may reproduce and distribute copies of the\n   Work or Derivative Works thereof in any medium, with or without\n   modifications, and in Source or Object form, provided that You\n   meet the following conditions:\n\n   (a) You must give any other recipients of the Work or\n       Derivative Works a copy of this License; and\n\n   (b) You must cause any modified files to carry prominent notices\n       stating that You changed the files; and\n\n   (c) You must retain, in the Source form of any Derivative Works\n       that You distribute, all copyright, patent, trademark, and\n       attribution notices from the Source form of the Work,\n       excluding those notices that do not pertain to any part of\n       the Derivative Works; and\n\n   (d) If the Work includes a \"NOTICE\" text file as part of its\n       distribution, then any Derivative Works that You distribute must\n       include a readable copy of the attribution notices contained\n       within such NOTICE file, excluding those notices that do not\n       pertain to any part of the Derivative Works, in at least one\n       of the following places: within a NOTICE text file distributed\n       as part of the Derivative Works; within the Source form or\n       documentation, if provided along with the Derivative Works; or,\n       within a display generated by the Derivative Works, if and\n       wherever such third-party notices normally appear. The contents\n       of the NOTICE file are for informational purposes only and\n       do not modify the License. You may add Your own attribution\n       notices within Derivative Works that You distribute, alongside\n       or as an addendum to the NOTICE text from the Work, provided\n       that such additional attribution notices cannot be construed\n       as modifying the License.\n\n   You may add Your own copyright statement to Your modifications and\n   may provide additional or different license terms and conditions\n   for use, reproduction, or distribution of Your modifications, or\n   for any such Derivative Works as a whole, provided Your use,\n   reproduction, and distribution of the Work otherwise complies with\n   the conditions stated in this License.\n\n5. Submission of Contributions. Unless You explicitly state otherwise,\n   any Contribution intentionally submitted for inclusion in the Work\n   by You to the Licensor shall be under the terms and conditions of\n   this License, without any additional terms or conditions.\n   Notwithstanding the above, nothing herein shall supersede or modify\n   the terms of any separate license agreement you may have executed\n   with Licensor regarding such Contributions.\n\n6. Trademarks. This License does not grant permission to use the trade\n   names, trademarks, service marks, or product names of the Licensor,\n   except as required for reasonable and customary use in describing the\n   origin of the Work and reproducing the content of the NOTICE file.\n\n7. Disclaimer of Warranty. Unless required by applicable law or\n   agreed to in writing, Licensor provides the Work (and each\n   Contributor provides its Contributions) on an \"AS IS\" BASIS,\n   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n   implied, including, without limitation, any warranties or conditions\n   of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n   PARTICULAR PURPOSE. You are solely responsible for determining the\n   appropriateness of using or redistributing the Work and assume any\n   risks associated with Your exercise of permissions under this License.\n\n8. Limitation of Liability. In no event and under no legal theory,\n   whether in tort (including negligence), contract, or otherwise,\n   unless required by applicable law (such as deliberate and grossly\n   negligent acts) or agreed to in writing, shall any Contributor be\n   liable to You for damages, including any direct, indirect, special,\n   incidental, or consequential damages of any character arising as a\n   result of this License or out of the use or inability to use the\n   Work (including but not limited to damages for loss of goodwill,\n   work stoppage, computer failure or malfunction, or any and all\n   other commercial damages or losses), even if such Contributor\n   has been advised of the possibility of such damages.\n\n9. Accepting Warranty or Additional Liability. While redistributing\n   the Work or Derivative Works thereof, You may choose to offer,\n   and charge a fee for, acceptance of support, warranty, indemnity,\n   or other liability obligations and/or rights consistent with this\n   License. However, in accepting such obligations, You may act only\n   on Your own behalf and on Your sole responsibility, not on behalf\n   of any other Contributor, and only if You agree to indemnify,\n   defend, and hold each Contributor harmless for any liability\n   incurred by, or claims asserted against, such Contributor by reason\n   of your accepting any such warranty or additional liability.\n\nEND OF TERMS AND CONDITIONS\n\nAPPENDIX: How to apply the Apache License to your work.\n\n   To apply the Apache License to your work, attach the following\n   boilerplate notice, with the fields enclosed by brackets \"[]\"\n   replaced with your own identifying information. (Don't include\n   the brackets!)  The text should be enclosed in the appropriate\n   comment syntax for the file format. We also recommend that a\n   file or class name and description of purpose be included on the\n   same \"printed page\" as the copyright notice for easier\n   identification within third-party archives.\n\nCopyright (c) Embassy project contributors\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n\thttp://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n"
  },
  {
    "path": "LICENSE-CC-BY-SA",
    "content": "Attribution-ShareAlike 4.0 International\n\n=======================================================================\n\nCreative Commons Corporation (\"Creative Commons\") is not a law firm and\ndoes not provide legal services or legal advice. Distribution of\nCreative Commons public licenses does not create a lawyer-client or\nother relationship. Creative Commons makes its licenses and related\ninformation available on an \"as-is\" basis. Creative Commons gives no\nwarranties regarding its licenses, any material licensed under their\nterms and conditions, or any related information. Creative Commons\ndisclaims all liability for damages resulting from their use to the\nfullest extent possible.\n\nUsing Creative Commons Public Licenses\n\nCreative Commons public licenses provide a standard set of terms and\nconditions that creators and other rights holders may use to share\noriginal works of authorship and other material subject to copyright\nand certain other rights specified in the public license below. The\nfollowing considerations are for informational purposes only, are not\nexhaustive, and do not form part of our licenses.\n\n     Considerations for licensors: Our public licenses are\n     intended for use by those authorized to give the public\n     permission to use material in ways otherwise restricted by\n     copyright and certain other rights. Our licenses are\n     irrevocable. Licensors should read and understand the terms\n     and conditions of the license they choose before applying it.\n     Licensors should also secure all rights necessary before\n     applying our licenses so that the public can reuse the\n     material as expected. Licensors should clearly mark any\n     material not subject to the license. This includes other CC-\n     licensed material, or material used under an exception or\n     limitation to copyright. More considerations for licensors:\n    wiki.creativecommons.org/Considerations_for_licensors\n\n     Considerations for the public: By using one of our public\n     licenses, a licensor grants the public permission to use the\n     licensed material under specified terms and conditions. If\n     the licensor's permission is not necessary for any reason--for\n     example, because of any applicable exception or limitation to\n     copyright--then that use is not regulated by the license. Our\n     licenses grant only permissions under copyright and certain\n     other rights that a licensor has authority to grant. Use of\n     the licensed material may still be restricted for other\n     reasons, including because others have copyright or other\n     rights in the material. A licensor may make special requests,\n     such as asking that all changes be marked or described.\n     Although not required by our licenses, you are encouraged to\n     respect those requests where reasonable. More considerations\n     for the public:\n    wiki.creativecommons.org/Considerations_for_licensees\n\n=======================================================================\n\nCreative Commons Attribution-ShareAlike 4.0 International Public\nLicense\n\nBy exercising the Licensed Rights (defined below), You accept and agree\nto be bound by the terms and conditions of this Creative Commons\nAttribution-ShareAlike 4.0 International Public License (\"Public\nLicense\"). To the extent this Public License may be interpreted as a\ncontract, You are granted the Licensed Rights in consideration of Your\nacceptance of these terms and conditions, and the Licensor grants You\nsuch rights in consideration of benefits the Licensor receives from\nmaking the Licensed Material available under these terms and\nconditions.\n\n\nSection 1 -- Definitions.\n\n  a. Adapted Material means material subject to Copyright and Similar\n     Rights that is derived from or based upon the Licensed Material\n     and in which the Licensed Material is translated, altered,\n     arranged, transformed, or otherwise modified in a manner requiring\n     permission under the Copyright and Similar Rights held by the\n     Licensor. For purposes of this Public License, where the Licensed\n     Material is a musical work, performance, or sound recording,\n     Adapted Material is always produced where the Licensed Material is\n     synched in timed relation with a moving image.\n\n  b. Adapter's License means the license You apply to Your Copyright\n     and Similar Rights in Your contributions to Adapted Material in\n     accordance with the terms and conditions of this Public License.\n\n  c. BY-SA Compatible License means a license listed at\n     creativecommons.org/compatiblelicenses, approved by Creative\n     Commons as essentially the equivalent of this Public License.\n\n  d. Copyright and Similar Rights means copyright and/or similar rights\n     closely related to copyright including, without limitation,\n     performance, broadcast, sound recording, and Sui Generis Database\n     Rights, without regard to how the rights are labeled or\n     categorized. For purposes of this Public License, the rights\n     specified in Section 2(b)(1)-(2) are not Copyright and Similar\n     Rights.\n\n  e. Effective Technological Measures means those measures that, in the\n     absence of proper authority, may not be circumvented under laws\n     fulfilling obligations under Article 11 of the WIPO Copyright\n     Treaty adopted on December 20, 1996, and/or similar international\n     agreements.\n\n  f. Exceptions and Limitations means fair use, fair dealing, and/or\n     any other exception or limitation to Copyright and Similar Rights\n     that applies to Your use of the Licensed Material.\n\n  g. License Elements means the license attributes listed in the name\n     of a Creative Commons Public License. The License Elements of this\n     Public License are Attribution and ShareAlike.\n\n  h. Licensed Material means the artistic or literary work, database,\n     or other material to which the Licensor applied this Public\n     License.\n\n  i. Licensed Rights means the rights granted to You subject to the\n     terms and conditions of this Public License, which are limited to\n     all Copyright and Similar Rights that apply to Your use of the\n     Licensed Material and that the Licensor has authority to license.\n\n  j. Licensor means the individual(s) or entity(ies) granting rights\n     under this Public License.\n\n  k. Share means to provide material to the public by any means or\n     process that requires permission under the Licensed Rights, such\n     as reproduction, public display, public performance, distribution,\n     dissemination, communication, or importation, and to make material\n     available to the public including in ways that members of the\n     public may access the material from a place and at a time\n     individually chosen by them.\n\n  l. Sui Generis Database Rights means rights other than copyright\n     resulting from Directive 96/9/EC of the European Parliament and of\n     the Council of 11 March 1996 on the legal protection of databases,\n     as amended and/or succeeded, as well as other essentially\n     equivalent rights anywhere in the world.\n\n  m. You means the individual or entity exercising the Licensed Rights\n     under this Public License. Your has a corresponding meaning.\n\n\nSection 2 -- Scope.\n\n  a. License grant.\n\n       1. Subject to the terms and conditions of this Public License,\n          the Licensor hereby grants You a worldwide, royalty-free,\n          non-sublicensable, non-exclusive, irrevocable license to\n          exercise the Licensed Rights in the Licensed Material to:\n\n            a. reproduce and Share the Licensed Material, in whole or\n               in part; and\n\n            b. produce, reproduce, and Share Adapted Material.\n\n       2. Exceptions and Limitations. For the avoidance of doubt, where\n          Exceptions and Limitations apply to Your use, this Public\n          License does not apply, and You do not need to comply with\n          its terms and conditions.\n\n       3. Term. The term of this Public License is specified in Section\n          6(a).\n\n       4. Media and formats; technical modifications allowed. The\n          Licensor authorizes You to exercise the Licensed Rights in\n          all media and formats whether now known or hereafter created,\n          and to make technical modifications necessary to do so. The\n          Licensor waives and/or agrees not to assert any right or\n          authority to forbid You from making technical modifications\n          necessary to exercise the Licensed Rights, including\n          technical modifications necessary to circumvent Effective\n          Technological Measures. For purposes of this Public License,\n          simply making modifications authorized by this Section 2(a)\n          (4) never produces Adapted Material.\n\n       5. Downstream recipients.\n\n            a. Offer from the Licensor -- Licensed Material. Every\n               recipient of the Licensed Material automatically\n               receives an offer from the Licensor to exercise the\n               Licensed Rights under the terms and conditions of this\n               Public License.\n\n            b. Additional offer from the Licensor -- Adapted Material.\n               Every recipient of Adapted Material from You\n               automatically receives an offer from the Licensor to\n               exercise the Licensed Rights in the Adapted Material\n               under the conditions of the Adapter's License You apply.\n\n            c. No downstream restrictions. You may not offer or impose\n               any additional or different terms or conditions on, or\n               apply any Effective Technological Measures to, the\n               Licensed Material if doing so restricts exercise of the\n               Licensed Rights by any recipient of the Licensed\n               Material.\n\n       6. No endorsement. Nothing in this Public License constitutes or\n          may be construed as permission to assert or imply that You\n          are, or that Your use of the Licensed Material is, connected\n          with, or sponsored, endorsed, or granted official status by,\n          the Licensor or others designated to receive attribution as\n          provided in Section 3(a)(1)(A)(i).\n\n  b. Other rights.\n\n       1. Moral rights, such as the right of integrity, are not\n          licensed under this Public License, nor are publicity,\n          privacy, and/or other similar personality rights; however, to\n          the extent possible, the Licensor waives and/or agrees not to\n          assert any such rights held by the Licensor to the limited\n          extent necessary to allow You to exercise the Licensed\n          Rights, but not otherwise.\n\n       2. Patent and trademark rights are not licensed under this\n          Public License.\n\n       3. To the extent possible, the Licensor waives any right to\n          collect royalties from You for the exercise of the Licensed\n          Rights, whether directly or through a collecting society\n          under any voluntary or waivable statutory or compulsory\n          licensing scheme. In all other cases the Licensor expressly\n          reserves any right to collect such royalties.\n\n\nSection 3 -- License Conditions.\n\nYour exercise of the Licensed Rights is expressly made subject to the\nfollowing conditions.\n\n  a. Attribution.\n\n       1. If You Share the Licensed Material (including in modified\n          form), You must:\n\n            a. retain the following if it is supplied by the Licensor\n               with the Licensed Material:\n\n                 i. identification of the creator(s) of the Licensed\n                    Material and any others designated to receive\n                    attribution, in any reasonable manner requested by\n                    the Licensor (including by pseudonym if\n                    designated);\n\n                ii. a copyright notice;\n\n               iii. a notice that refers to this Public License;\n\n                iv. a notice that refers to the disclaimer of\n                    warranties;\n\n                 v. a URI or hyperlink to the Licensed Material to the\n                    extent reasonably practicable;\n\n            b. indicate if You modified the Licensed Material and\n               retain an indication of any previous modifications; and\n\n            c. indicate the Licensed Material is licensed under this\n               Public License, and include the text of, or the URI or\n               hyperlink to, this Public License.\n\n       2. You may satisfy the conditions in Section 3(a)(1) in any\n          reasonable manner based on the medium, means, and context in\n          which You Share the Licensed Material. For example, it may be\n          reasonable to satisfy the conditions by providing a URI or\n          hyperlink to a resource that includes the required\n          information.\n\n       3. If requested by the Licensor, You must remove any of the\n          information required by Section 3(a)(1)(A) to the extent\n          reasonably practicable.\n\n  b. ShareAlike.\n\n     In addition to the conditions in Section 3(a), if You Share\n     Adapted Material You produce, the following conditions also apply.\n\n       1. The Adapter's License You apply must be a Creative Commons\n          license with the same License Elements, this version or\n          later, or a BY-SA Compatible License.\n\n       2. You must include the text of, or the URI or hyperlink to, the\n          Adapter's License You apply. You may satisfy this condition\n          in any reasonable manner based on the medium, means, and\n          context in which You Share Adapted Material.\n\n       3. You may not offer or impose any additional or different terms\n          or conditions on, or apply any Effective Technological\n          Measures to, Adapted Material that restrict exercise of the\n          rights granted under the Adapter's License You apply.\n\n\nSection 4 -- Sui Generis Database Rights.\n\nWhere the Licensed Rights include Sui Generis Database Rights that\napply to Your use of the Licensed Material:\n\n  a. for the avoidance of doubt, Section 2(a)(1) grants You the right\n     to extract, reuse, reproduce, and Share all or a substantial\n     portion of the contents of the database;\n\n  b. if You include all or a substantial portion of the database\n     contents in a database in which You have Sui Generis Database\n     Rights, then the database in which You have Sui Generis Database\n     Rights (but not its individual contents) is Adapted Material,\n\n     including for purposes of Section 3(b); and\n  c. You must comply with the conditions in Section 3(a) if You Share\n     all or a substantial portion of the contents of the database.\n\nFor the avoidance of doubt, this Section 4 supplements and does not\nreplace Your obligations under this Public License where the Licensed\nRights include other Copyright and Similar Rights.\n\n\nSection 5 -- Disclaimer of Warranties and Limitation of Liability.\n\n  a. UNLESS OTHERWISE SEPARATELY UNDERTAKEN BY THE LICENSOR, TO THE\n     EXTENT POSSIBLE, THE LICENSOR OFFERS THE LICENSED MATERIAL AS-IS\n     AND AS-AVAILABLE, AND MAKES NO REPRESENTATIONS OR WARRANTIES OF\n     ANY KIND CONCERNING THE LICENSED MATERIAL, WHETHER EXPRESS,\n     IMPLIED, STATUTORY, OR OTHER. THIS INCLUDES, WITHOUT LIMITATION,\n     WARRANTIES OF TITLE, MERCHANTABILITY, FITNESS FOR A PARTICULAR\n     PURPOSE, NON-INFRINGEMENT, ABSENCE OF LATENT OR OTHER DEFECTS,\n     ACCURACY, OR THE PRESENCE OR ABSENCE OF ERRORS, WHETHER OR NOT\n     KNOWN OR DISCOVERABLE. WHERE DISCLAIMERS OF WARRANTIES ARE NOT\n     ALLOWED IN FULL OR IN PART, THIS DISCLAIMER MAY NOT APPLY TO YOU.\n\n  b. TO THE EXTENT POSSIBLE, IN NO EVENT WILL THE LICENSOR BE LIABLE\n     TO YOU ON ANY LEGAL THEORY (INCLUDING, WITHOUT LIMITATION,\n     NEGLIGENCE) OR OTHERWISE FOR ANY DIRECT, SPECIAL, INDIRECT,\n     INCIDENTAL, CONSEQUENTIAL, PUNITIVE, EXEMPLARY, OR OTHER LOSSES,\n     COSTS, EXPENSES, OR DAMAGES ARISING OUT OF THIS PUBLIC LICENSE OR\n     USE OF THE LICENSED MATERIAL, EVEN IF THE LICENSOR HAS BEEN\n     ADVISED OF THE POSSIBILITY OF SUCH LOSSES, COSTS, EXPENSES, OR\n     DAMAGES. WHERE A LIMITATION OF LIABILITY IS NOT ALLOWED IN FULL OR\n     IN PART, THIS LIMITATION MAY NOT APPLY TO YOU.\n\n  c. The disclaimer of warranties and limitation of liability provided\n     above shall be interpreted in a manner that, to the extent\n     possible, most closely approximates an absolute disclaimer and\n     waiver of all liability.\n\n\nSection 6 -- Term and Termination.\n\n  a. This Public License applies for the term of the Copyright and\n     Similar Rights licensed here. However, if You fail to comply with\n     this Public License, then Your rights under this Public License\n     terminate automatically.\n\n  b. Where Your right to use the Licensed Material has terminated under\n     Section 6(a), it reinstates:\n\n       1. automatically as of the date the violation is cured, provided\n          it is cured within 30 days of Your discovery of the\n          violation; or\n\n       2. upon express reinstatement by the Licensor.\n\n     For the avoidance of doubt, this Section 6(b) does not affect any\n     right the Licensor may have to seek remedies for Your violations\n     of this Public License.\n\n  c. For the avoidance of doubt, the Licensor may also offer the\n     Licensed Material under separate terms or conditions or stop\n     distributing the Licensed Material at any time; however, doing so\n     will not terminate this Public License.\n\n  d. Sections 1, 5, 6, 7, and 8 survive termination of this Public\n     License.\n\n\nSection 7 -- Other Terms and Conditions.\n\n  a. The Licensor shall not be bound by any additional or different\n     terms or conditions communicated by You unless expressly agreed.\n\n  b. Any arrangements, understandings, or agreements regarding the\n     Licensed Material not stated herein are separate from and\n     independent of the terms and conditions of this Public License.\n\n\nSection 8 -- Interpretation.\n\n  a. For the avoidance of doubt, this Public License does not, and\n     shall not be interpreted to, reduce, limit, restrict, or impose\n     conditions on any use of the Licensed Material that could lawfully\n     be made without permission under this Public License.\n\n  b. To the extent possible, if any provision of this Public License is\n     deemed unenforceable, it shall be automatically reformed to the\n     minimum extent necessary to make it enforceable. If the provision\n     cannot be reformed, it shall be severed from this Public License\n     without affecting the enforceability of the remaining terms and\n     conditions.\n\n  c. No term or condition of this Public License will be waived and no\n     failure to comply consented to unless expressly agreed to by the\n     Licensor.\n\n  d. Nothing in this Public License constitutes or may be interpreted\n     as a limitation upon, or waiver of, any privileges and immunities\n     that apply to the Licensor or You, including from the legal\n     processes of any jurisdiction or authority.\n\n\n=======================================================================\n\nCreative Commons is not a party to its public\nlicenses. Notwithstanding, Creative Commons may elect to apply one of\nits public licenses to material it publishes and in those instances\nwill be considered the “Licensor.” The text of the Creative Commons\npublic licenses is dedicated to the public domain under the CC0 Public\nDomain Dedication. Except for the limited purpose of indicating that\nmaterial is shared under a Creative Commons public license or as\notherwise permitted by the Creative Commons policies published at\ncreativecommons.org/policies, Creative Commons does not authorize the\nuse of the trademark \"Creative Commons\" or any other trademark or logo\nof Creative Commons without its prior written consent including,\nwithout limitation, in connection with any unauthorized modifications\nto any of its public licenses or any other arrangements,\nunderstandings, or agreements concerning use of licensed material. For\nthe avoidance of doubt, this paragraph does not form part of the\npublic licenses.\n\nCreative Commons may be contacted at creativecommons.org.\n\n"
  },
  {
    "path": "LICENSE-MIT",
    "content": "Copyright (c) Embassy project contributors\n\nPermission is hereby granted, free of charge, to any\nperson obtaining a copy of this software and associated\ndocumentation files (the \"Software\"), to deal in the\nSoftware without restriction, including without\nlimitation the rights to use, copy, modify, merge,\npublish, distribute, sublicense, and/or sell copies of\nthe Software, and to permit persons to whom the Software\nis furnished to do so, subject to the following\nconditions:\n\nThe above copyright notice and this permission notice\nshall be included in all copies or substantial portions\nof the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF\nANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED\nTO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A\nPARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT\nSHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\nCLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\nOF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR\nIN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\nDEALINGS IN THE SOFTWARE.\n"
  },
  {
    "path": "NOTICE.md",
    "content": "# Notices for Embassy\n\nThis content is produced and maintained by the Embassy project contributors.\n\n## Copyright\n\nAll content is the property of the respective authors or their employers.\nFor more information regarding authorship of content, please consult the\nlisted source code repository logs.\n\n## Declared Project Licenses\n\nThis program and the accompanying materials are made available under the terms\nof the Apache Software License 2.0 which is available at\nhttps://www.apache.org/licenses/LICENSE-2.0, or the MIT license which is\navailable at https://opensource.org/licenses/MIT\n"
  },
  {
    "path": "README.md",
    "content": "# Embassy\n\nEmbassy is the next-generation framework for embedded applications. Write safe, correct, and energy-efficient embedded code faster, using the Rust programming language, its async facilities, and the Embassy libraries.\n\n## [Documentation](https://embassy.dev/book/index.html) - [API reference](https://docs.embassy.dev/) - [Website](https://embassy.dev/) - [Chat](https://matrix.to/#/#embassy-rs:matrix.org)\n\n## Rust + async ❤️ embedded\n\nThe Rust programming language is blazingly fast and memory-efficient, with no runtime, garbage collector, or OS. It catches a wide variety of bugs at compile time, thanks to its full memory- and thread-safety, and expressive type system.\n\nRust's [async/await](https://rust-lang.github.io/async-book/) allows for unprecedentedly easy and efficient multitasking in embedded systems. Tasks get transformed at compile time into state machines that get run cooperatively. It requires no dynamic memory allocation and runs on a single stack, so no per-task stack size tuning is required. It obsoletes the need for a traditional RTOS with kernel context switching, and is [faster and smaller than one!](https://tweedegolf.nl/en/blog/65/async-rust-vs-rtos-showdown)\n\n## Batteries included\n\n- **Hardware Abstraction Layers**\n    - HALs implement safe, idiomatic Rust APIs to use the hardware capabilities, so raw register manipulation is not needed. The Embassy project maintains HALs for select hardware, but you can still use HALs from other projects with Embassy.\n    - [embassy-stm32](https://docs.embassy.dev/embassy-stm32/), for all STM32 microcontroller families.\n    - [embassy-nrf](https://docs.embassy.dev/embassy-nrf/), for the Nordic Semiconductor nRF52, nRF53, nRF54 and nRF91 series.\n    - [embassy-rp](https://docs.embassy.dev/embassy-rp/), for the Raspberry Pi RP2040 and RP23xx microcontrollers.\n    - [embassy-mspm0](https://docs.embassy.dev/embassy-mspm0/), for the Texas Instruments MSPM0 microcontrollers.\n    - [embassy-mcxa](https://docs.embassy.dev/embassy-mcxa/), for NXP's MCX-A series of microcontrollers.\n    - [esp-rs](https://github.com/esp-rs), for the Espressif Systems ESP32 series of chips.\n        - Embassy HAL support for Espressif chips, as well as Async Wi-Fi, Bluetooth, and ESP-NOW, is being developed in the [esp-rs/esp-hal](https://github.com/esp-rs/esp-hal) repository.\n    - [ch32-hal](https://github.com/ch32-rs/ch32-hal), for the WCH 32-bit RISC-V(CH32V) series of chips.\n    - [mpfs-hal](https://github.com/AlexCharlton/mpfs-hal), for the Microchip PolarFire SoC.\n    - [py32-hal](https://github.com/py32-rs/py32-hal), for the Puya Semiconductor PY32 series of microcontrollers.\n\n- **Time that Just Works** -\n  No more messing with hardware timers. [embassy_time](https://docs.embassy.dev/embassy-time) provides Instant, Duration, and Timer types that are globally available and never overflow.\n\n- **Real-time ready** -\n  Tasks on the same async executor run cooperatively, but you can create multiple executors with different priorities so that higher priority tasks preempt lower priority ones. See the [example](https://github.com/embassy-rs/embassy/blob/main/examples/nrf52840/src/bin/multiprio.rs).\n\n- **Low-power ready** -\n  Easily build devices with years of battery life. The async executor automatically puts the core to sleep when there's no work to do. Tasks are woken by interrupts, there is no busy-loop polling while waiting.\n\n- **Networking** -\n  The [embassy-net](https://docs.embassy.dev/embassy-net/) network stack implements extensive networking functionality, including Ethernet, IP, TCP, UDP, ICMP, and DHCP. Async drastically simplifies managing timeouts and serving multiple connections concurrently.\n\n- **Bluetooth**\n    - The [trouble](https://github.com/embassy-rs/trouble) crate provides a Bluetooth Low Energy 4.x and 5.x Host that runs on any microcontroller implementing the [bt-hci](https://github.com/embassy-rs/bt-hci) traits (currently\n      `nRF52`, `nrf54`, `rp2040`, `rp23xx` and `esp32` and `serial` controllers are supported).\n    - The [nrf-softdevice](https://github.com/embassy-rs/nrf-softdevice) crate provides Bluetooth Low Energy 4.x and 5.x support for nRF52 microcontrollers.\n    - The [embassy-stm32-wpan](https://github.com/embassy-rs/embassy/tree/main/embassy-stm32-wpan) crate provides Bluetooth Low Energy 5.x support for stm32wb microcontrollers.\n\n- **LoRa** -\n  The [lora-rs](https://github.com/lora-rs/lora-rs) project provides an async LoRa and LoRaWAN stack that works well on Embassy.\n\n- **USB** -\n  [embassy-usb](https://docs.embassy.dev/embassy-usb/) implements a device-side USB stack. Implementations for common classes such as USB serial (CDC ACM) and USB HID are available, and a rich builder API allows building your own.\n\n- **Bootloader and DFU** -\n  [embassy-boot](https://github.com/embassy-rs/embassy/tree/main/embassy-boot) is a lightweight bootloader supporting firmware application upgrades in a power-fail-safe way, with trial boots and rollbacks.\n\n## Sneak peek\n\n```rust,ignore\nuse defmt::info;\nuse embassy_executor::Spawner;\nuse embassy_time::{Duration, Timer};\nuse embassy_nrf::gpio::{AnyPin, Input, Level, Output, OutputDrive, Pin, Pull};\nuse embassy_nrf::{Peri, Peripherals};\n\n// Declare async tasks\n#[embassy_executor::task]\nasync fn blink(pin: Peri<'static, AnyPin>) {\n    let mut led = Output::new(pin, Level::Low, OutputDrive::Standard);\n\n    loop {\n        // Timekeeping is globally available, no need to mess with hardware timers.\n        led.set_high();\n        Timer::after_millis(150).await;\n        led.set_low();\n        Timer::after_millis(150).await;\n    }\n}\n\n// Main is itself an async task as well.\n#[embassy_executor::main]\nasync fn main(spawner: Spawner) {\n    let p = embassy_nrf::init(Default::default());\n\n    // Spawned tasks run in the background, concurrently.\n    spawner.spawn(blink(p.P0_13.into()).unwrap());\n\n    let mut button = Input::new(p.P0_11, Pull::Up);\n    loop {\n        // Asynchronously wait for GPIO events, allowing other tasks\n        // to run, or the core to sleep.\n        button.wait_for_low().await;\n        info!(\"Button pressed!\");\n        button.wait_for_high().await;\n        info!(\"Button released!\");\n    }\n}\n```\n\n## Examples\n\nExamples are found in the\n`examples/` folder separated by the chip manufacturer they are designed to run on. For example:\n\n* `examples/nrf52840` run on the\n  `nrf52840-dk` board (PCA10056) but should be easily adaptable to other nRF52 chips and boards.\n* `examples/nrf5340` run on the `nrf5340-dk` board (PCA10095).\n* `examples/stm32xx` for the various STM32 families.\n* `examples/rp` are for the RP2040 and RP235x chips.\n* `examples/mcxa` run on the `FRDM-MCXA266` board.\n* `examples/std` are designed to run locally on your PC.\n\n### Running examples\n\n- Install `probe-rs` following the instructions at <https://probe.rs>.\n- Change directory to the sample's base directory. For example:\n\n```bash\ncd examples/nrf52840\n```\n\n- Ensure `Cargo.toml` sets the right feature for the name of the chip you are programming.\n  If this name is incorrect, the example may fail to run or immediately crash\n  after being programmed.\n\n- Ensure `.cargo/config.toml` contains the name of the chip you are programming.\n\n- Run the example\n\nFor example:\n\n```bash\ncargo run --release --bin blinky\n```\n\nFor more help getting started, see [Getting Started][1] and [Running the Examples][2].\n\n## Developing Embassy with Rust Analyzer-based editors\n\nThe [Rust Analyzer](https://rust-analyzer.github.io/) is used by [Visual Studio Code](https://code.visualstudio.com/)\nand others. Given the multiple targets that Embassy serves, there is no Cargo workspace file. Instead, the Rust Analyzer\nmust be told of the target project to work with. In the case of Visual Studio Code,\nplease refer to the `.vscode/settings.json` file's `rust-analyzer.linkedProjects`setting.\n\n## Minimum supported Rust version (MSRV)\n\nEmbassy is guaranteed to compile on stable Rust 1.75 and up. It *might*\ncompile with older versions, but that may change in any new patch release.\n\n## Why the name?\n\nEMBedded ASYnc! :)\n\n## License\n\nEmbassy is licensed under either of\n\n- Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or\n  <http://www.apache.org/licenses/LICENSE-2.0>)\n- MIT license ([LICENSE-MIT](LICENSE-MIT) or <http://opensource.org/licenses/MIT>)\n\nat your option.\n\n## Contribution\n\nUnless you explicitly state otherwise, any contribution intentionally submitted\nfor inclusion in the work by you, as defined in the Apache-2.0 license, shall be\ndual licensed as above, without any additional terms or conditions.\n\n[1]: https://embassy-rs.github.io/embassy-book/embassy/dev/getting_started.html\n[2]: https://github.com/embassy-rs/embassy/wiki/Running-the-Examples\n"
  },
  {
    "path": "RELEASE.md",
    "content": "# RELEASE.md\n\nThis document outlines the process for releasing Embassy crates using `cargo-release` and the `release/bump-dependency.sh` script.\n\nWhen releasing a crate, keep in mind that you may need to recursively release dependencies as well. \n\n## Prerequisites\n\n- Install [`cargo-release`](https://github.com/crate-ci/cargo-release):\n\n  ```sh\n  cargo binstall cargo-release\n\n## Crate release\n\nCheck if there are changes to the public API since the last release. If there is a breaking change, follow\nthe process for creating a minor release. Otherewise, follow the process for creating a new patch release.\n\nKeep in mind that some crates may need the --features and --target flags passed to cargo release. For more information on that,\nlook at the `Cargo.toml` files.\n\n### Patch release (no breaking public API changes)\n\n```\ncd embassy-nrf/\ncargo release patch --features time,defmt,unstable-pac,gpiote,time-driver-rtc1,nrf52840 --target thumbv7em-none-eabi\n\n# If dry-run is OK (no missing dependencies on crates.io) \ncargo release patch --execute --features time,defmt,unstable-pac,gpiote,time-driver-rtc1,nrf52840 --target thumbv7em-none-eabi\n```\n\n### Minor release\n\n```\n# Bump versions in crate files\n./release/bump-dependency.sh embassy-nrf 0.4.0\n\n# Commit version bump\ngit commit -am 'chore: update to `embassy-nrf` v0.4.0'\n\n# Release crate\ncd embassy-nrf/\ncargo release minor --features time,defmt,unstable-pac,gpiote,time-driver-rtc1,nrf52840 --target thumbv7em-none-eabi\n\n# If dry-run is OK (no missing dependencies on crates.io) \ncargo release minor --execute --features time,defmt,unstable-pac,gpiote,time-driver-rtc1,nrf52840 --target thumbv7em-none-eabi\n```\n\n## Push tags\n\nPush the git tags that `cargo release` created earlier:\n\n```\ngit push --tags\n```\n## Reference\n\n* [PR introducing release automation](https://github.com/embassy-rs/embassy/pull/4289)\n"
  },
  {
    "path": "ci-nightly.sh",
    "content": "#!/bin/bash\n\nset -eo pipefail\n\nexport RUSTFLAGS=-Dwarnings\nexport DEFMT_LOG=trace,embassy_hal_internal=debug,embassy_net_esp_hosted=debug,cyw43=info,cyw43_pio=info,smoltcp=info\nif [[ -z \"${CARGO_TARGET_DIR}\" ]]; then\n    export CARGO_TARGET_DIR=target_ci\nfi\n\ncargo embassy-devtool build --group nightly\n"
  },
  {
    "path": "ci-xtensa.sh",
    "content": "#!/bin/bash\n\nset -eo pipefail\n\nexport RUSTFLAGS=-Dwarnings\nexport DEFMT_LOG=trace,embassy_hal_internal=debug,embassy_net_esp_hosted=debug,cyw43=info,cyw43_pio=info,smoltcp=info\nexport RUSTUP_TOOLCHAIN=esp\nif [[ -z \"${CARGO_TARGET_DIR}\" ]]; then\n    export CARGO_TARGET_DIR=target_ci\nfi\n\ncargo embassy-devtool build --group xtensa\n"
  },
  {
    "path": "ci.sh",
    "content": "#!/bin/bash\n\nset -eo pipefail\n\nif ! command -v cargo-batch &> /dev/null; then\n    echo \"cargo-batch could not be found. Install it with the following command:\"\n    echo \"\"\n    echo \"    cargo install --git https://github.com/embassy-rs/cargo-batch cargo --bin cargo-batch --locked\"\n    echo \"\"\n    exit 1\nfi\n\nif ! command -v cargo-embassy-devtool &> /dev/null; then\n    echo \"cargo-embassy-devtool could not be found. Install it with the following command:\"\n    echo \"\"\n    echo \"    cargo install --git https://github.com/embassy-rs/cargo-embassy-devtool --locked\"\n    echo \"\"\n    exit 1\nfi\n\nexport RUSTFLAGS=-Dwarnings\nexport DEFMT_LOG=trace,embassy_hal_internal=debug,embassy_net_esp_hosted=debug,cyw43=info,cyw43_pio=info,smoltcp=info\nif [[ -z \"${CARGO_TARGET_DIR}\" ]]; then\n    export CARGO_TARGET_DIR=target_ci\nfi\n\ncargo embassy-devtool build\n\n# temporarily disabled, these boards are dead.\nrm -rf out/tests/stm32f103c8\nrm -rf out/tests/nrf52840-dk\nrm -rf out/tests/nrf52833-dk\nrm -rf out/tests/nrf5340-dk\n\n# disabled because these boards are not on the shelf\nrm -rf out/tests/mspm0g3507\n\nrm out/tests/stm32wb55rg/wpan_mac\nrm out/tests/stm32wb55rg/wpan_ble\n\n# unstable, I think it's running out of RAM?\nrm out/tests/stm32f207zg/eth\n\n# temporarily disabled, flaky.\nrm out/tests/stm32f207zg/usart_rx_ringbuffered\nrm out/tests/stm32l152re/usart_rx_ringbuffered\n\n# doesn't work, gives \"noise error\", no idea why. usart_dma does pass.\nrm out/tests/stm32u5a5zj/usart\n\n# probe-rs error: \"multi-core ram flash start not implemented yet\"\n# As of 2025-02-17 these tests work when run from flash\nrm out/tests/pimoroni-pico-plus-2/multicore\nrm out/tests/pimoroni-pico-plus-2/gpio_multicore\nrm out/tests/pimoroni-pico-plus-2/spinlock_mutex_multicore\n# Doesn't work when run from ram on the 2350\nrm out/tests/pimoroni-pico-plus-2/flash\n# This test passes locally but fails on the HIL, no idea why\nrm out/tests/pimoroni-pico-plus-2/i2c\n# The pico2 plus doesn't have the adcs hooked up like the picoW does.\nrm out/tests/pimoroni-pico-plus-2/adc\n# temporarily disabled\nrm out/tests/pimoroni-pico-plus-2/pwm\nrm out/tests/frdm-mcx-a266/trng\n\n# flaky\nrm out/tests/rpi-pico/pwm\nrm out/tests/rpi-pico/cyw43-perf\nrm out/tests/rpi-pico/uart_buffered\nrm out/tests/rpi-pico/spi_async\n\nrm out/tests/stm32h563zi/usart_dma\n\n# tests are implemented but the HIL test farm doesn't actually have these boards, yet\nrm -rf out/tests/stm32c071rb\nrm -rf out/tests/stm32f100rd\nrm -rf out/tests/stm32f107vc\n\nif [[ -z \"${TELEPROBE_TOKEN-}\" ]]; then\n    echo No teleprobe token found, skipping running HIL tests\n    exit\nfi\n\nteleprobe client run -r out/tests\n"
  },
  {
    "path": "cyw43/CHANGELOG.md",
    "content": "# Changelog\n\nAll notable changes to this project will be documented in this file.\n\nThe format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),\nand this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).\n\n<!-- next-header -->\n## Unreleased - ReleaseDate\n\n## 0.7.0 - 2026-03-10\n\n- Reset WPA security before creating secure AP\n- Add `get_rssi()` to `Controller`\n- Implement link state monitoring\n- Always unsubscribe to cyw43 events (fix event leak)\n- Add SDIO support for STM32\n- Allow specifying nvram, get LWB+ working\n- Improve join handling with better error reporting\n- Replace `num_enum` dependency with internal macro\n- Bump bt-hci to 0.8.0.\n- Update to embedded-io 0.7\n- Update embassy-sync to 0.8.0\n- Update embassy-net-driver-channel to 0.4.0\n\n## 0.6.0 - 2025-11-27\n\n- Updated documentation for Control::join() #4678\n- Bump bt-hci to 0.6.0.\n- Add error handling to HCI transport implementation.\n- Reset WPA security on AP creation #4709\n\n## 0.5.0 - 2025-08-28\n\n- bump bt-hci to 0.4.0\n\n## 0.4.1 - 2025-08-26 (yanked)\n\n- bump bt-hci to 0.4.0\n\n## 0.4.0 - 2025-07-15\n\n- bump embassy-sync to 0.7.0\n- bump bt-hci to 0.3.0\n- make State::new const fn\n\n## 0.3.0 - 2025-01-05\n\n- Update `embassy-time` to 0.4.0\n- Add Bluetooth support.\n- Add WPA3 support.\n- Expand wifi security configuration options.\n\n## 0.2.0 - 2024-08-05\n\n- Update to new versions of embassy-{time,sync}\n- Add more fields to the BssInfo packet struct #2461\n- Extend the Scan API #2282\n- Reuse buf to reduce stack usage #2580\n- Add MAC address getter to cyw43 controller #2818\n- Add function to join WPA2 network with precomputed PSK. #2885\n- Add function to close soft AP. #3042\n- Fixing missing re-export #3211\n\n## 0.1.0 - 2024-01-11\n\n- First release\n"
  },
  {
    "path": "cyw43/Cargo.toml",
    "content": "[package]\nname = \"cyw43\"\nversion = \"0.7.0\"\nedition = \"2024\"\ndescription = \"Rust driver for the CYW43439 WiFi chip, used in the Raspberry Pi Pico W.\"\nkeywords = [\"embedded\", \"cyw43\", \"embassy-net\", \"embedded-hal-async\", \"wifi\"]\ncategories = [\"embedded\", \"hardware-support\", \"no-std\", \"network-programming\", \"asynchronous\"]\nlicense = \"MIT OR Apache-2.0\"\nrepository = \"https://github.com/embassy-rs/embassy\"\ndocumentation = \"https://docs.embassy.dev/cyw43\"\n\n[features]\ndefmt = [\"dep:defmt\", \"heapless/defmt\", \"embassy-time/defmt\", \"bt-hci?/defmt\", \"embedded-io-async?/defmt\"]\nlog = [\"dep:log\", \"dep:derive_more\", \"derive_more/display\"]\nbluetooth = [\"dep:bt-hci\", \"dep:embedded-io-async\"]\n\n# Fetch console logs from the WiFi firmware and forward them to `log` or `defmt`.\nfirmware-logs = []\n\n[dependencies]\nembassy-time = { version = \"0.5.1\", path = \"../embassy-time\"}\nembassy-sync = { version = \"0.8.0\", path = \"../embassy-sync\"}\nembassy-futures = { version = \"0.1.2\", path = \"../embassy-futures\"}\nembassy-net-driver-channel = { version = \"0.4.0\", path = \"../embassy-net-driver-channel\"}\n\ndefmt = { version = \"1.0.1\", optional = true }\nlog = { version = \"0.4.17\", optional = true }\nderive_more = { version = \"2.1.1\", optional = true, default-features =false }\n\ncortex-m = \"0.7.6\"\ncortex-m-rt = \"0.7.0\"\nfutures = { version = \"0.3.17\", default-features = false, features = [\"async-await\", \"cfg-target-has-atomic\", \"unstable\"] }\n\nembedded-hal-1 = { package = \"embedded-hal\", version = \"1.0\" }\nembedded-hal-async = { version = \"1.0\" }\nheapless = \"0.9\"\naligned = \"0.4.3\"\n\n# Bluetooth deps\nembedded-io-async = { version = \"0.7.0\", optional = true }\nbt-hci = { version = \"0.8\", optional = true }\n\n[package.metadata.embassy]\nbuild = [\n    {target = \"thumbv6m-none-eabi\", features = []},\n    {target = \"thumbv6m-none-eabi\", features = [\"log\"]},\n    {target = \"thumbv6m-none-eabi\", features = [\"defmt\"]},\n    {target = \"thumbv6m-none-eabi\", features = [\"firmware-logs\", \"log\"]},\n    {target = \"thumbv6m-none-eabi\", features = [\"defmt\", \"firmware-logs\"]},\n    {target = \"thumbv6m-none-eabi\", features = [\"bluetooth\", \"firmware-logs\", \"log\"]},\n    {target = \"thumbv6m-none-eabi\", features = [\"bluetooth\", \"defmt\", \"firmware-logs\"]},\n]\n\n[package.metadata.embassy_docs]\nsrc_base = \"https://github.com/embassy-rs/embassy/blob/cyw43-v$VERSION/cyw43/src/\"\nsrc_base_git = \"https://github.com/embassy-rs/embassy/blob/$COMMIT/cyw43/src/\"\ntarget = \"thumbv6m-none-eabi\"\nfeatures = [\"defmt\", \"firmware-logs\"]\n\n[package.metadata.docs.rs]\nfeatures = [\"defmt\", \"firmware-logs\"]\n"
  },
  {
    "path": "cyw43/README.md",
    "content": "# cyw43\n\nRust driver for the CYW43439 wifi+bluetooth chip. Implementation based on [Infineon/wifi-host-driver](https://github.com/Infineon/wifi-host-driver).\n\nWorks on the following boards:\n\n- Raspberry Pi Pico W (RP2040)\n- Raspberry Pi Pico 2 W (RP2350A)\n- Pimoroni Pico Plus 2 W (RP2350B)\n- Any board with Raspberry Pi RM2 radio module.\n- Any board with the CYW43439 chip, and possibly others if the protocol is similar enough.\n\n## Features\n\nWorking:\n\n- WiFi support\n    - Station mode (joining an AP).\n    - AP mode (creating an AP)\n    - Scanning\n    - Sending and receiving Ethernet frames.\n    - Using the default MAC address.\n    - [`embassy-net`](https://embassy.dev) integration.\n    - RP2040 PIO driver for the nonstandard half-duplex SPI used in the Pico W.\n    - Using IRQ for device events, no busy polling.\n    - GPIO support (for LED on the Pico W).\n- Bluetooth support\n    - Bluetooth Classic + LE HCI commands.\n    - Concurrent operation with WiFi.\n    - Implements the [bt-hci](https://crates.io/crates/bt-hci) controller traits.\n    - Works with the [TrouBLE](https://github.com/embassy-rs/trouble) bluetooth LE stack. Check its repo for examples using `cyw43`.\n\n## Running the WiFi examples\n\n- Install `probe-rs` following the instructions at <https://probe.rs>.\n- `cd examples/rp`\n### Example 1: Scan the wifi stations\n- `cargo run --release --bin wifi_scan`\n### Example 2: Create an access point (IP and credentials in the code)\n- `cargo run --release --bin wifi_ap_tcp_server`\n### Example 3: Connect to an existing network and create a server\n- `cargo run --release --bin wifi_tcp_server`\n\nAfter a few seconds, you should see that DHCP picks up an IP address like this\n```\n11.944489 DEBUG Acquired IP configuration:\n11.944517 DEBUG    IP address:      192.168.0.250/24\n11.944620 DEBUG    Default gateway: 192.168.0.33\n11.944722 DEBUG    DNS server 0:    192.168.0.33\n```\nThis example implements a TCP echo server on port 1234. You can try connecting to it with:\n```\nnc 192.168.0.250 1234\n```\nSend it some data, you should see it echoed back and printed in the firmware's logs.\n"
  },
  {
    "path": "cyw43/src/bluetooth.rs",
    "content": "use core::cell::RefCell;\nuse core::future::Future;\nuse core::mem::MaybeUninit;\n\nuse bt_hci::transport::WithIndicator;\nuse bt_hci::{ControllerToHostPacket, FromHciBytes, FromHciBytesError, HostToControllerPacket, PacketKind, WriteHci};\nuse embassy_futures::yield_now;\nuse embassy_sync::blocking_mutex::raw::NoopRawMutex;\nuse embassy_sync::zerocopy_channel;\nuse embassy_time::{Duration, Timer};\nuse embedded_io_async::ErrorKind;\n\nuse crate::consts::*;\nuse crate::runner::Bus;\npub use crate::spi::SpiBusCyw43;\nuse crate::util::round_up;\nuse crate::{CHIP, util};\n\npub(crate) struct BtState {\n    rx: [BtPacketBuf; 4],\n    tx: [BtPacketBuf; 4],\n    inner: MaybeUninit<BtStateInnre<'static>>,\n}\n\nimpl BtState {\n    pub const fn new() -> Self {\n        Self {\n            rx: [const { BtPacketBuf::new() }; 4],\n            tx: [const { BtPacketBuf::new() }; 4],\n            inner: MaybeUninit::uninit(),\n        }\n    }\n}\n\nstruct BtStateInnre<'d> {\n    rx: zerocopy_channel::Channel<'d, NoopRawMutex, BtPacketBuf>,\n    tx: zerocopy_channel::Channel<'d, NoopRawMutex, BtPacketBuf>,\n}\n\n/// Bluetooth driver.\npub struct BtDriver<'d> {\n    rx: RefCell<zerocopy_channel::Receiver<'d, NoopRawMutex, BtPacketBuf>>,\n    tx: RefCell<zerocopy_channel::Sender<'d, NoopRawMutex, BtPacketBuf>>,\n}\n\npub(crate) struct BtRunner<'d> {\n    pub(crate) tx_chan: zerocopy_channel::Receiver<'d, NoopRawMutex, BtPacketBuf>,\n    rx_chan: zerocopy_channel::Sender<'d, NoopRawMutex, BtPacketBuf>,\n\n    // Bluetooth circular buffers\n    addr: u32,\n    h2b_write_pointer: u32,\n    b2h_read_pointer: u32,\n}\n\nconst BT_HCI_MTU: usize = 1024;\n\n/// Represents a packet of size MTU.\npub(crate) struct BtPacketBuf {\n    pub(crate) len: usize,\n    pub(crate) buf: [u8; BT_HCI_MTU],\n}\n\nimpl BtPacketBuf {\n    /// Create a new packet buffer.\n    pub const fn new() -> Self {\n        Self {\n            len: 0,\n            buf: [0; BT_HCI_MTU],\n        }\n    }\n}\n\npub(crate) fn new<'d>(state: &'d mut BtState) -> (BtRunner<'d>, BtDriver<'d>) {\n    // safety: this is a self-referential struct, however:\n    // - it can't move while the `'d` borrow is active.\n    // - when the borrow ends, the dangling references inside the MaybeUninit will never be used again.\n    let state_uninit: *mut MaybeUninit<BtStateInnre<'d>> =\n        (&mut state.inner as *mut MaybeUninit<BtStateInnre<'static>>).cast();\n    let state = unsafe { &mut *state_uninit }.write(BtStateInnre {\n        rx: zerocopy_channel::Channel::new(&mut state.rx[..]),\n        tx: zerocopy_channel::Channel::new(&mut state.tx[..]),\n    });\n\n    let (rx_sender, rx_receiver) = state.rx.split();\n    let (tx_sender, tx_receiver) = state.tx.split();\n\n    (\n        BtRunner {\n            tx_chan: tx_receiver,\n            rx_chan: rx_sender,\n\n            addr: 0,\n            h2b_write_pointer: 0,\n            b2h_read_pointer: 0,\n        },\n        BtDriver {\n            rx: RefCell::new(rx_receiver),\n            tx: RefCell::new(tx_sender),\n        },\n    )\n}\n\npub(crate) struct CybtFwCb<'a> {\n    pub p_next_line_start: &'a [u8],\n}\n\npub(crate) struct HexFileData<'a> {\n    pub addr_mode: i32,\n    pub hi_addr: u16,\n    pub dest_addr: u32,\n    pub p_ds: &'a mut [u8],\n}\n\npub(crate) fn read_firmware_patch_line(p_btfw_cb: &mut CybtFwCb, hfd: &mut HexFileData) -> u32 {\n    let mut abs_base_addr32 = 0;\n\n    loop {\n        let num_bytes = p_btfw_cb.p_next_line_start[0];\n        p_btfw_cb.p_next_line_start = &p_btfw_cb.p_next_line_start[1..];\n\n        let addr = (p_btfw_cb.p_next_line_start[0] as u16) << 8 | p_btfw_cb.p_next_line_start[1] as u16;\n        p_btfw_cb.p_next_line_start = &p_btfw_cb.p_next_line_start[2..];\n\n        let line_type = p_btfw_cb.p_next_line_start[0];\n        p_btfw_cb.p_next_line_start = &p_btfw_cb.p_next_line_start[1..];\n\n        if num_bytes == 0 {\n            break;\n        }\n\n        hfd.p_ds[..num_bytes as usize].copy_from_slice(&p_btfw_cb.p_next_line_start[..num_bytes as usize]);\n        p_btfw_cb.p_next_line_start = &p_btfw_cb.p_next_line_start[num_bytes as usize..];\n\n        match line_type {\n            BTFW_HEX_LINE_TYPE_EXTENDED_ADDRESS => {\n                hfd.hi_addr = (hfd.p_ds[0] as u16) << 8 | hfd.p_ds[1] as u16;\n                hfd.addr_mode = BTFW_ADDR_MODE_EXTENDED;\n            }\n            BTFW_HEX_LINE_TYPE_EXTENDED_SEGMENT_ADDRESS => {\n                hfd.hi_addr = (hfd.p_ds[0] as u16) << 8 | hfd.p_ds[1] as u16;\n                hfd.addr_mode = BTFW_ADDR_MODE_SEGMENT;\n            }\n            BTFW_HEX_LINE_TYPE_ABSOLUTE_32BIT_ADDRESS => {\n                abs_base_addr32 = (hfd.p_ds[0] as u32) << 24\n                    | (hfd.p_ds[1] as u32) << 16\n                    | (hfd.p_ds[2] as u32) << 8\n                    | hfd.p_ds[3] as u32;\n                hfd.addr_mode = BTFW_ADDR_MODE_LINEAR32;\n            }\n            BTFW_HEX_LINE_TYPE_DATA => {\n                hfd.dest_addr = addr as u32;\n                match hfd.addr_mode {\n                    BTFW_ADDR_MODE_EXTENDED => hfd.dest_addr += (hfd.hi_addr as u32) << 16,\n                    BTFW_ADDR_MODE_SEGMENT => hfd.dest_addr += (hfd.hi_addr as u32) << 4,\n                    BTFW_ADDR_MODE_LINEAR32 => hfd.dest_addr += abs_base_addr32,\n                    _ => {}\n                }\n                return num_bytes as u32;\n            }\n            _ => {}\n        }\n    }\n    0\n}\n\nimpl<'a> BtRunner<'a> {\n    pub(crate) async fn init_bluetooth(&mut self, bus: &mut impl Bus, firmware: &[u8]) {\n        trace!(\"init_bluetooth\");\n        bus.bp_write32(CHIP.bluetooth_base_address + BT2WLAN_PWRUP_ADDR, BT2WLAN_PWRUP_WAKE)\n            .await;\n        Timer::after(Duration::from_millis(2)).await;\n        self.upload_bluetooth_firmware(bus, firmware).await;\n        self.wait_bt_ready(bus).await;\n        self.init_bt_buffers(bus).await;\n        self.wait_bt_awake(bus).await;\n        self.bt_set_host_ready(bus).await;\n        self.bt_toggle_intr(bus).await;\n    }\n\n    pub(crate) async fn upload_bluetooth_firmware(&mut self, bus: &mut impl Bus, firmware: &[u8]) {\n        // read version\n        let version_length = firmware[0];\n        let _version = &firmware[1..=version_length as usize];\n        // skip version + 1 extra byte as per cybt_shared_bus_driver.c\n        let firmware = &firmware[version_length as usize + 2..];\n        // buffers\n        let mut data_buffer: [u8; 0x100] = [0; 0x100];\n        let mut aligned_data_buffer: [u8; 0x100] = [0; 0x100];\n        // structs\n        let mut btfw_cb = CybtFwCb {\n            p_next_line_start: firmware,\n        };\n        let mut hfd = HexFileData {\n            addr_mode: BTFW_ADDR_MODE_EXTENDED,\n            hi_addr: 0,\n            dest_addr: 0,\n            p_ds: &mut data_buffer,\n        };\n        loop {\n            let num_fw_bytes = read_firmware_patch_line(&mut btfw_cb, &mut hfd);\n            if num_fw_bytes == 0 {\n                break;\n            }\n            let fw_bytes = &hfd.p_ds[0..num_fw_bytes as usize];\n            let mut dest_start_addr = hfd.dest_addr + CHIP.bluetooth_base_address;\n            let mut aligned_data_buffer_index: usize = 0;\n            // pad start\n            if !util::is_aligned(dest_start_addr, 4) {\n                let num_pad_bytes = dest_start_addr % 4;\n                let padded_dest_start_addr = util::round_down(dest_start_addr, 4);\n                let memory_value = bus.bp_read32(padded_dest_start_addr).await;\n                let memory_value_bytes = memory_value.to_le_bytes();\n                // Copy the previous memory value's bytes to the start\n                for i in 0..num_pad_bytes as usize {\n                    aligned_data_buffer[aligned_data_buffer_index] = memory_value_bytes[i];\n                    aligned_data_buffer_index += 1;\n                }\n                // Copy the firmware bytes after the padding bytes\n                for i in 0..num_fw_bytes as usize {\n                    aligned_data_buffer[aligned_data_buffer_index] = fw_bytes[i];\n                    aligned_data_buffer_index += 1;\n                }\n                dest_start_addr = padded_dest_start_addr;\n            } else {\n                // Directly copy fw_bytes into aligned_data_buffer if no start padding is required\n                for i in 0..num_fw_bytes as usize {\n                    aligned_data_buffer[aligned_data_buffer_index] = fw_bytes[i];\n                    aligned_data_buffer_index += 1;\n                }\n            }\n            // pad end\n            let mut dest_end_addr = dest_start_addr + aligned_data_buffer_index as u32;\n            if !util::is_aligned(dest_end_addr, 4) {\n                let offset = dest_end_addr % 4;\n                let num_pad_bytes_end = 4 - offset;\n                let padded_dest_end_addr = util::round_down(dest_end_addr, 4);\n                let memory_value = bus.bp_read32(padded_dest_end_addr).await;\n                let memory_value_bytes = memory_value.to_le_bytes();\n                // Append the necessary memory bytes to pad the end of aligned_data_buffer\n                for i in offset..4 {\n                    aligned_data_buffer[aligned_data_buffer_index] = memory_value_bytes[i as usize];\n                    aligned_data_buffer_index += 1;\n                }\n                dest_end_addr += num_pad_bytes_end;\n            } else {\n                // pad end alignment not needed\n            }\n            let buffer_to_write = &aligned_data_buffer[0..aligned_data_buffer_index as usize];\n            assert!(dest_start_addr % 4 == 0);\n            assert!(dest_end_addr % 4 == 0);\n            assert!(aligned_data_buffer_index % 4 == 0);\n            bus.bp_write(dest_start_addr, buffer_to_write).await;\n        }\n    }\n\n    pub(crate) async fn wait_bt_ready(&mut self, bus: &mut impl Bus) {\n        trace!(\"wait_bt_ready\");\n        let mut success = false;\n        for _ in 0..300 {\n            let val = bus.bp_read32(BT_CTRL_REG_ADDR).await;\n            trace!(\"BT_CTRL_REG_ADDR = {:08x}\", val);\n            if val & BTSDIO_REG_FW_RDY_BITMASK != 0 {\n                success = true;\n                break;\n            }\n            Timer::after(Duration::from_millis(1)).await;\n        }\n        assert!(success == true);\n    }\n\n    pub(crate) async fn wait_bt_awake(&mut self, bus: &mut impl Bus) {\n        trace!(\"wait_bt_awake\");\n        let mut success = false;\n        for _ in 0..300 {\n            let val = bus.bp_read32(BT_CTRL_REG_ADDR).await;\n            trace!(\"BT_CTRL_REG_ADDR = {:08x}\", val);\n            if val & BTSDIO_REG_BT_AWAKE_BITMASK != 0 {\n                success = true;\n                break;\n            }\n            Timer::after(Duration::from_millis(1)).await;\n        }\n        assert!(success == true);\n    }\n\n    pub(crate) async fn bt_set_host_ready(&mut self, bus: &mut impl Bus) {\n        trace!(\"bt_set_host_ready\");\n        let old_val = bus.bp_read32(HOST_CTRL_REG_ADDR).await;\n        // TODO: do we need to swap endianness on this read?\n        let new_val = old_val | BTSDIO_REG_SW_RDY_BITMASK;\n        bus.bp_write32(HOST_CTRL_REG_ADDR, new_val).await;\n    }\n\n    // TODO: use this\n    #[allow(dead_code)]\n    pub(crate) async fn bt_set_awake(&mut self, bus: &mut impl Bus, awake: bool) {\n        trace!(\"bt_set_awake\");\n        let old_val = bus.bp_read32(HOST_CTRL_REG_ADDR).await;\n        // TODO: do we need to swap endianness on this read?\n        let new_val = if awake {\n            old_val | BTSDIO_REG_WAKE_BT_BITMASK\n        } else {\n            old_val & !BTSDIO_REG_WAKE_BT_BITMASK\n        };\n        bus.bp_write32(HOST_CTRL_REG_ADDR, new_val).await;\n    }\n\n    pub(crate) async fn bt_toggle_intr(&mut self, bus: &mut impl Bus) {\n        trace!(\"bt_toggle_intr\");\n        let old_val = bus.bp_read32(HOST_CTRL_REG_ADDR).await;\n        // TODO: do we need to swap endianness on this read?\n        let new_val = old_val ^ BTSDIO_REG_DATA_VALID_BITMASK;\n        bus.bp_write32(HOST_CTRL_REG_ADDR, new_val).await;\n    }\n\n    // TODO: use this\n    #[allow(dead_code)]\n    pub(crate) async fn bt_set_intr(&mut self, bus: &mut impl Bus) {\n        trace!(\"bt_set_intr\");\n        let old_val = bus.bp_read32(HOST_CTRL_REG_ADDR).await;\n        let new_val = old_val | BTSDIO_REG_DATA_VALID_BITMASK;\n        bus.bp_write32(HOST_CTRL_REG_ADDR, new_val).await;\n    }\n\n    pub(crate) async fn init_bt_buffers(&mut self, bus: &mut impl Bus) {\n        trace!(\"init_bt_buffers\");\n        self.addr = bus.bp_read32(WLAN_RAM_BASE_REG_ADDR).await;\n        assert!(self.addr != 0);\n        trace!(\"wlan_ram_base_addr = {:08x}\", self.addr);\n        bus.bp_write32(self.addr + BTSDIO_OFFSET_HOST2BT_IN, 0).await;\n        bus.bp_write32(self.addr + BTSDIO_OFFSET_HOST2BT_OUT, 0).await;\n        bus.bp_write32(self.addr + BTSDIO_OFFSET_BT2HOST_IN, 0).await;\n        bus.bp_write32(self.addr + BTSDIO_OFFSET_BT2HOST_OUT, 0).await;\n    }\n\n    async fn bt_bus_request(&mut self, bus: &mut impl Bus) {\n        // TODO: CYW43_THREAD_ENTER mutex?\n        self.bt_set_awake(bus, true).await;\n        self.wait_bt_awake(bus).await;\n    }\n\n    pub(crate) async fn hci_write(&mut self, bus: &mut impl Bus) {\n        self.bt_bus_request(bus).await;\n\n        // NOTE(unwrap): we only call this when we do have a packet in the queue.\n        let buf = self.tx_chan.try_receive().unwrap();\n        debug!(\"HCI tx: {:02x}\", crate::fmt::Bytes(&buf.buf[..buf.len]));\n\n        let len = buf.len as u32 - 1; // len doesn't include hci type byte\n        let rounded_len = round_up(len, 4);\n        let total_len = 4 + rounded_len;\n\n        let read_pointer = bus.bp_read32(self.addr + BTSDIO_OFFSET_HOST2BT_OUT).await;\n        let available = read_pointer.wrapping_sub(self.h2b_write_pointer + 4) % BTSDIO_FWBUF_SIZE;\n        if available < total_len {\n            warn!(\n                \"bluetooth tx queue full, retrying. len {} available {}\",\n                total_len, available\n            );\n            yield_now().await;\n            return;\n        }\n\n        // Build header\n        let mut header = [0u8; 4];\n        header[0] = len as u8;\n        header[1] = (len >> 8) as u8;\n        header[2] = (len >> 16) as u8;\n        header[3] = buf.buf[0]; // HCI type byte\n\n        // Write header\n        let addr = self.addr + BTSDIO_OFFSET_HOST_WRITE_BUF + self.h2b_write_pointer;\n        bus.bp_write(addr, &header).await;\n        self.h2b_write_pointer = (self.h2b_write_pointer + 4) % BTSDIO_FWBUF_SIZE;\n\n        // Write payload.\n        let payload = &buf.buf[1..][..rounded_len as usize];\n        if self.h2b_write_pointer as usize + payload.len() > BTSDIO_FWBUF_SIZE as usize {\n            // wraparound\n            let n = BTSDIO_FWBUF_SIZE - self.h2b_write_pointer;\n            let addr = self.addr + BTSDIO_OFFSET_HOST_WRITE_BUF + self.h2b_write_pointer;\n            bus.bp_write(addr, &payload[..n as usize]).await;\n            let addr = self.addr + BTSDIO_OFFSET_HOST_WRITE_BUF;\n            bus.bp_write(addr, &payload[n as usize..]).await;\n        } else {\n            // no wraparound\n            let addr = self.addr + BTSDIO_OFFSET_HOST_WRITE_BUF + self.h2b_write_pointer;\n            bus.bp_write(addr, payload).await;\n        }\n        self.h2b_write_pointer = (self.h2b_write_pointer + payload.len() as u32) % BTSDIO_FWBUF_SIZE;\n\n        // Update pointer.\n        bus.bp_write32(self.addr + BTSDIO_OFFSET_HOST2BT_IN, self.h2b_write_pointer)\n            .await;\n\n        self.bt_toggle_intr(bus).await;\n\n        self.tx_chan.receive_done();\n    }\n\n    async fn bt_has_work(&mut self, bus: &mut impl Bus) -> bool {\n        let int_status = bus.bp_read32(CHIP.sdiod_core_base_address + SDIO_INT_STATUS).await;\n        if int_status & I_HMB_FC_CHANGE != 0 {\n            bus.bp_write32(\n                CHIP.sdiod_core_base_address + SDIO_INT_STATUS,\n                int_status & I_HMB_FC_CHANGE,\n            )\n            .await;\n            return true;\n        }\n        return false;\n    }\n\n    pub(crate) async fn handle_irq(&mut self, bus: &mut impl Bus) {\n        if self.bt_has_work(bus).await {\n            loop {\n                // Check if we have data.\n                let write_pointer = bus.bp_read32(self.addr + BTSDIO_OFFSET_BT2HOST_IN).await;\n                let available = write_pointer.wrapping_sub(self.b2h_read_pointer) % BTSDIO_FWBUF_SIZE;\n                if available == 0 {\n                    break;\n                }\n\n                // read header\n                let mut header = [0u8; 4];\n                let addr = self.addr + BTSDIO_OFFSET_HOST_READ_BUF + self.b2h_read_pointer;\n                bus.bp_read(addr, &mut header).await;\n\n                // calc length\n                let len = header[0] as u32 | ((header[1]) as u32) << 8 | ((header[2]) as u32) << 16;\n                let rounded_len = round_up(len, 4);\n                if available < 4 + rounded_len {\n                    warn!(\"ringbuf data not enough for a full packet?\");\n                    break;\n                }\n                self.b2h_read_pointer = (self.b2h_read_pointer + 4) % BTSDIO_FWBUF_SIZE;\n\n                // Obtain a buf from the channel.\n                let buf = self.rx_chan.send().await;\n\n                buf.buf[0] = header[3]; // hci packet type\n                let payload = &mut buf.buf[1..][..rounded_len as usize];\n                if self.b2h_read_pointer as usize + payload.len() > BTSDIO_FWBUF_SIZE as usize {\n                    // wraparound\n                    let n = BTSDIO_FWBUF_SIZE - self.b2h_read_pointer;\n                    let addr = self.addr + BTSDIO_OFFSET_HOST_READ_BUF + self.b2h_read_pointer;\n                    bus.bp_read(addr, &mut payload[..n as usize]).await;\n                    let addr = self.addr + BTSDIO_OFFSET_HOST_READ_BUF;\n                    bus.bp_read(addr, &mut payload[n as usize..]).await;\n                } else {\n                    // no wraparound\n                    let addr = self.addr + BTSDIO_OFFSET_HOST_READ_BUF + self.b2h_read_pointer;\n                    bus.bp_read(addr, payload).await;\n                }\n                self.b2h_read_pointer = (self.b2h_read_pointer + payload.len() as u32) % BTSDIO_FWBUF_SIZE;\n                bus.bp_write32(self.addr + BTSDIO_OFFSET_BT2HOST_OUT, self.b2h_read_pointer)\n                    .await;\n\n                buf.len = 1 + len as usize;\n                debug!(\"HCI rx: {:02x}\", crate::fmt::Bytes(&buf.buf[..buf.len]));\n\n                self.rx_chan.send_done();\n\n                self.bt_toggle_intr(bus).await;\n            }\n        }\n    }\n}\n\n/// HCI transport error.\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\n#[derive(Debug)]\npub enum Error {\n    /// I/O error.\n    Io(ErrorKind),\n}\n\nimpl From<FromHciBytesError> for Error {\n    fn from(e: FromHciBytesError) -> Self {\n        match e {\n            FromHciBytesError::InvalidSize => Error::Io(ErrorKind::InvalidInput),\n            FromHciBytesError::InvalidValue => Error::Io(ErrorKind::InvalidData),\n        }\n    }\n}\n\nimpl core::fmt::Display for Error {\n    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {\n        core::fmt::Debug::fmt(self, f)\n    }\n}\n\nimpl core::error::Error for Error {}\n\nimpl<'d> embedded_io_async::ErrorType for BtDriver<'d> {\n    type Error = Error;\n}\n\nimpl embedded_io_async::Error for Error {\n    fn kind(&self) -> ErrorKind {\n        match self {\n            Self::Io(e) => *e,\n        }\n    }\n}\n\nimpl<'d> bt_hci::transport::Transport for BtDriver<'d> {\n    fn read<'a>(&self, rx: &'a mut [u8]) -> impl Future<Output = Result<ControllerToHostPacket<'a>, Self::Error>> {\n        async {\n            let ch = &mut *self.rx.borrow_mut();\n            let buf = ch.receive().await;\n            let n = buf.len;\n            assert!(n < rx.len());\n            rx[..n].copy_from_slice(&buf.buf[..n]);\n            ch.receive_done();\n\n            let kind = PacketKind::from_hci_bytes_complete(&rx[..1])?;\n            let (pkt, _) = ControllerToHostPacket::from_hci_bytes_with_kind(kind, &rx[1..n])?;\n            Ok(pkt)\n        }\n    }\n\n    /// Write a complete HCI packet from the tx buffer\n    fn write<T: HostToControllerPacket>(&self, val: &T) -> impl Future<Output = Result<(), Self::Error>> {\n        async {\n            let ch = &mut *self.tx.borrow_mut();\n            let buf = ch.send().await;\n            let buf_len = buf.buf.len();\n            let mut slice = &mut buf.buf[..];\n            WithIndicator::new(val)\n                .write_hci(&mut slice)\n                .map_err(|_| Error::Io(ErrorKind::Other))?;\n            buf.len = buf_len - slice.len();\n            ch.send_done();\n            Ok(())\n        }\n    }\n}\n"
  },
  {
    "path": "cyw43/src/consts.rs",
    "content": "#![allow(unused)]\n\npub(crate) const FUNC_BUS: u32 = 0;\npub(crate) const FUNC_BACKPLANE: u32 = 1;\npub(crate) const FUNC_WLAN: u32 = 2;\npub(crate) const FUNC_BT: u32 = 3;\n\n// Register addresses\npub(crate) const REG_BUS_CTRL: u32 = 0x0;\npub(crate) const REG_BUS_RESPONSE_DELAY: u32 = 0x1;\npub(crate) const REG_BUS_STATUS_ENABLE: u32 = 0x2;\npub(crate) const REG_BUS_INTERRUPT: u32 = 0x04; // 16 bits - Interrupt status\npub(crate) const REG_BUS_INTERRUPT_ENABLE: u32 = 0x06; // 16 bits - Interrupt mask\npub(crate) const REG_BUS_STATUS: u32 = 0x8;\npub(crate) const REG_BUS_TEST_RO: u32 = 0x14;\npub(crate) const REG_BUS_TEST_RW: u32 = 0x18;\npub(crate) const REG_BUS_RESP_DELAY: u32 = 0x1c;\n\n// SPI_BUS_CONTROL Bits\npub(crate) const WORD_LENGTH_32: u32 = 0x1;\npub(crate) const ENDIAN_BIG: u32 = 0x2;\npub(crate) const CLOCK_PHASE: u32 = 0x4;\npub(crate) const CLOCK_POLARITY: u32 = 0x8;\npub(crate) const HIGH_SPEED: u32 = 0x10;\npub(crate) const INTERRUPT_POLARITY_HIGH: u32 = 0x20;\npub(crate) const WAKE_UP: u32 = 0x80;\n\n// SPI_STATUS_ENABLE bits\npub(crate) const STATUS_ENABLE: u32 = 0x01;\npub(crate) const INTR_WITH_STATUS: u32 = 0x02;\npub(crate) const RESP_DELAY_ALL: u32 = 0x04;\npub(crate) const DWORD_PKT_LEN_EN: u32 = 0x08;\npub(crate) const CMD_ERR_CHK_EN: u32 = 0x20;\npub(crate) const DATA_ERR_CHK_EN: u32 = 0x40;\n\n// SPI_STATUS_REGISTER bits\npub(crate) const SPI_STATUS_REGISTER: u32 = 0x00000008;\npub(crate) const INITIAL_READ: u32 = 0x04;\n\npub(crate) const STATUS_DATA_NOT_AVAILABLE: u32 = 0x00000001;\npub(crate) const STATUS_UNDERFLOW: u32 = 0x00000002;\npub(crate) const STATUS_OVERFLOW: u32 = 0x00000004;\npub(crate) const STATUS_F2_INTR: u32 = 0x00000008;\npub(crate) const STATUS_F3_INTR: u32 = 0x00000010;\npub(crate) const STATUS_F2_RX_READY: u32 = 0x00000020;\npub(crate) const STATUS_F3_RX_READY: u32 = 0x00000040;\npub(crate) const STATUS_HOST_CMD_DATA_ERR: u32 = 0x00000080;\npub(crate) const STATUS_F2_PKT_AVAILABLE: u32 = 0x00000100;\npub(crate) const STATUS_F2_PKT_LEN_MASK: u32 = 0x000FFE00;\npub(crate) const STATUS_F2_PKT_LEN_SHIFT: u32 = 9;\npub(crate) const STATUS_F3_PKT_AVAILABLE: u32 = 0x00100000;\npub(crate) const STATUS_F3_PKT_LEN_MASK: u32 = 0xFFE00000;\npub(crate) const STATUS_F3_PKT_LEN_SHIFT: u32 = 21;\n\npub(crate) const REG_BACKPLANE_GPIO_SELECT: u32 = 0x10005;\npub(crate) const REG_BACKPLANE_GPIO_OUTPUT: u32 = 0x10006;\npub(crate) const REG_BACKPLANE_GPIO_ENABLE: u32 = 0x10007;\npub(crate) const REG_BACKPLANE_FUNCTION2_WATERMARK: u32 = 0x10008;\npub(crate) const REG_BACKPLANE_DEVICE_CONTROL: u32 = 0x10009;\npub(crate) const REG_BACKPLANE_BACKPLANE_ADDRESS_LOW: u32 = 0x1000A;\npub(crate) const REG_BACKPLANE_BACKPLANE_ADDRESS_MID: u32 = 0x1000B;\npub(crate) const REG_BACKPLANE_BACKPLANE_ADDRESS_HIGH: u32 = 0x1000C;\npub(crate) const REG_BACKPLANE_FRAME_CONTROL: u32 = 0x1000D;\npub(crate) const REG_BACKPLANE_CHIP_CLOCK_CSR: u32 = 0x1000E;\npub(crate) const REG_BACKPLANE_PULL_UP: u32 = 0x1000F;\npub(crate) const REG_BACKPLANE_READ_FRAME_BC_LOW: u32 = 0x1001B;\npub(crate) const REG_BACKPLANE_READ_FRAME_BC_HIGH: u32 = 0x1001C;\npub(crate) const REG_BACKPLANE_WAKEUP_CTRL: u32 = 0x1001E;\npub(crate) const REG_BACKPLANE_SLEEP_CSR: u32 = 0x1001F;\n\npub(crate) const I_HMB_SW_MASK: u32 = 0x000000f0;\npub(crate) const I_HMB_FC_CHANGE: u32 = 1 << 5;\npub(crate) const SDIO_INT_STATUS: u32 = 0x20;\npub(crate) const SDIO_INT_HOST_MASK: u32 = 0x24;\npub(crate) const SDIO_FUNCTION_INT_MASK: u32 = 0x34;\npub(crate) const SDIO_TO_SB_MAILBOX: u32 = 0x40;\npub(crate) const SDIO_TO_SB_MAILBOX_DATA: u32 = 0x48;\npub(crate) const SDIO_TO_HOST_MAILBOX_DATA: u32 = 0x4C;\npub(crate) const SDIO_SLEEP_CSR: u32 = 0x1001F;\npub(crate) const SBSDIO_SLPCSR_KEEP_WL_KS: u32 = 1 << 0;\n\npub(crate) const SMB_DEV_INT: u32 = 1 << 3;\npub(crate) const SMB_INT_ACK: u32 = 1 << 1;\npub(crate) const I_HMB_HOST_INT: u32 = 1 << 7;\npub(crate) const I_HMB_DATA_FWHALT: u32 = 0x0010;\n\npub(crate) const HOSTINTMASK: u32 = 0x000000F0;\npub(crate) const BUS_SD_DATA_WIDTH_MASK: u32 = 0x03;\npub(crate) const BUS_SD_DATA_WIDTH_4BIT: u32 = 0x02;\npub(crate) const SDIO_SPEED_EHS: u32 = 0x02;\npub(crate) const SDIOD_CCCR_BRCM_CARDCAP_SECURE_MODE: u32 = 0x80;\npub(crate) const SBSDIO_DEVICE_CTL: u32 = 0x10009;\npub(crate) const SBSDIO_DEVCTL_ADDR_RST: u32 = 0x40;\npub(crate) const SDIO_CORE_CHIPID_REG: u32 = 0x330;\n\npub(crate) const SBSDIO_FUNC1_SBADDRLOW: u32 = 0x1000A;\npub(crate) const SBSDIO_FUNC1_SBADDRMID: u32 = 0x1000B;\npub(crate) const SBSDIO_FUNC1_SBADDRHIGH: u32 = 0x1000C;\n\npub(crate) const SPI_F2_WATERMARK: u8 = 0x20;\npub(crate) const SDIO_F2_WATERMARK: u8 = 0x08;\n\npub(crate) const BACKPLANE_WINDOW_SIZE: usize = 0x8000;\npub(crate) const BACKPLANE_ADDRESS_MASK: u32 = 0x7FFF;\npub(crate) const BACKPLANE_ADDRESS_32BIT_FLAG: u32 = 0x08000;\npub(crate) const BACKPLANE_MAX_TRANSFER_SIZE: usize = 64;\npub(crate) const BLOCK_BUFFER_SIZE: usize = 1024;\n// Active Low Power (ALP) clock constants\npub(crate) const BACKPLANE_ALP_AVAIL_REQ: u8 = 0x08;\npub(crate) const BACKPLANE_ALP_AVAIL: u8 = 0x40;\npub(crate) const BACKPLANE_FORCE_HW_CLKREQ_OFF: u8 = 0x20;\npub(crate) const BACKPLANE_FORCE_ALP: u8 = 0x01;\npub(crate) const BACKPLANE_FORCE_HT: u32 = 0x02;\n\n// Broadcom AMBA (Advanced Microcontroller Bus Architecture) Interconnect\n// (AI) pub (crate) constants\npub(crate) const AI_IOCTRL_OFFSET: u32 = 0x408;\npub(crate) const AI_IOCTRL_BIT_FGC: u8 = 0x0002;\npub(crate) const AI_IOCTRL_BIT_CLOCK_EN: u8 = 0x0001;\npub(crate) const AI_IOCTRL_BIT_CPUHALT: u8 = 0x0020;\n\npub(crate) const AI_RESETCTRL_OFFSET: u32 = 0x800;\npub(crate) const AI_RESETCTRL_BIT_RESET: u8 = 1;\n\npub(crate) const AI_RESETSTATUS_OFFSET: u32 = 0x804;\n\npub(crate) const TEST_PATTERN: u32 = 0x12345678;\npub(crate) const FEEDBEAD: u32 = 0xFEEDBEAD;\n\n// SPI_INTERRUPT_REGISTER and SPI_INTERRUPT_ENABLE_REGISTER Bits\npub(crate) const IRQ_DATA_UNAVAILABLE: u16 = 0x0001; // Requested data not available; Clear by writing a \"1\"\npub(crate) const IRQ_F2_F3_FIFO_RD_UNDERFLOW: u16 = 0x0002;\npub(crate) const IRQ_F2_F3_FIFO_WR_OVERFLOW: u16 = 0x0004;\npub(crate) const IRQ_COMMAND_ERROR: u16 = 0x0008; // Cleared by writing 1\npub(crate) const IRQ_DATA_ERROR: u16 = 0x0010; // Cleared by writing 1\npub(crate) const IRQ_F2_PACKET_AVAILABLE: u16 = 0x0020;\npub(crate) const IRQ_F3_PACKET_AVAILABLE: u16 = 0x0040;\npub(crate) const IRQ_F1_OVERFLOW: u16 = 0x0080; // Due to last write. Bkplane has pending write requests\npub(crate) const IRQ_MISC_INTR0: u16 = 0x0100;\npub(crate) const IRQ_MISC_INTR1: u16 = 0x0200;\npub(crate) const IRQ_MISC_INTR2: u16 = 0x0400;\npub(crate) const IRQ_MISC_INTR3: u16 = 0x0800;\npub(crate) const IRQ_MISC_INTR4: u16 = 0x1000;\npub(crate) const IRQ_F1_INTR: u16 = 0x2000;\npub(crate) const IRQ_F2_INTR: u16 = 0x4000;\npub(crate) const IRQ_F3_INTR: u16 = 0x8000;\n\npub(crate) const CHANNEL_TYPE_CONTROL: u8 = 0;\npub(crate) const CHANNEL_TYPE_EVENT: u8 = 1;\npub(crate) const CHANNEL_TYPE_DATA: u8 = 2;\n\n// CYW_SPID command structure constants.\npub(crate) const WRITE: bool = true;\npub(crate) const READ: bool = false;\npub(crate) const INC_ADDR: bool = true;\npub(crate) const FIXED_ADDR: bool = false;\n\npub(crate) const AES_ENABLED: u32 = 0x0004;\npub(crate) const WPA2_SECURITY: u32 = 0x00400000;\n\npub(crate) const MIN_PSK_LEN: usize = 8;\npub(crate) const MAX_PSK_LEN: usize = 64;\n\n// Bluetooth firmware extraction constants.\npub(crate) const BTFW_ADDR_MODE_UNKNOWN: i32 = 0;\npub(crate) const BTFW_ADDR_MODE_EXTENDED: i32 = 1;\npub(crate) const BTFW_ADDR_MODE_SEGMENT: i32 = 2;\npub(crate) const BTFW_ADDR_MODE_LINEAR32: i32 = 3;\n\npub(crate) const BTFW_HEX_LINE_TYPE_DATA: u8 = 0;\npub(crate) const BTFW_HEX_LINE_TYPE_END_OF_DATA: u8 = 1;\npub(crate) const BTFW_HEX_LINE_TYPE_EXTENDED_SEGMENT_ADDRESS: u8 = 2;\npub(crate) const BTFW_HEX_LINE_TYPE_EXTENDED_ADDRESS: u8 = 4;\npub(crate) const BTFW_HEX_LINE_TYPE_ABSOLUTE_32BIT_ADDRESS: u8 = 5;\n\n// Bluetooth constants.\npub(crate) const SPI_RESP_DELAY_F1: u32 = 0x001d;\npub(crate) const WHD_BUS_SPI_BACKPLANE_READ_PADD_SIZE: u8 = 4;\n\npub(crate) const BT2WLAN_PWRUP_WAKE: u32 = 3;\npub(crate) const BT2WLAN_PWRUP_ADDR: u32 = 0x640894;\n\npub(crate) const BT_CTRL_REG_ADDR: u32 = 0x18000c7c;\npub(crate) const HOST_CTRL_REG_ADDR: u32 = 0x18000d6c;\npub(crate) const WLAN_RAM_BASE_REG_ADDR: u32 = 0x18000d68;\n\npub(crate) const BTSDIO_REG_DATA_VALID_BITMASK: u32 = 1 << 1;\npub(crate) const BTSDIO_REG_BT_AWAKE_BITMASK: u32 = 1 << 8;\npub(crate) const BTSDIO_REG_WAKE_BT_BITMASK: u32 = 1 << 17;\npub(crate) const BTSDIO_REG_SW_RDY_BITMASK: u32 = 1 << 24;\npub(crate) const BTSDIO_REG_FW_RDY_BITMASK: u32 = 1 << 24;\n\npub(crate) const BTSDIO_FWBUF_SIZE: u32 = 0x1000;\npub(crate) const BTSDIO_OFFSET_HOST_WRITE_BUF: u32 = 0;\npub(crate) const BTSDIO_OFFSET_HOST_READ_BUF: u32 = BTSDIO_FWBUF_SIZE;\n\npub(crate) const BTSDIO_OFFSET_HOST2BT_IN: u32 = 0x00002000;\npub(crate) const BTSDIO_OFFSET_HOST2BT_OUT: u32 = 0x00002004;\npub(crate) const BTSDIO_OFFSET_BT2HOST_IN: u32 = 0x00002008;\npub(crate) const BTSDIO_OFFSET_BT2HOST_OUT: u32 = 0x0000200C;\n\npub(crate) const SDIOD_CCCR_IOEN: u32 = 0x02;\npub(crate) const SDIOD_CCCR_IORDY: u32 = 0x03;\npub(crate) const SDIOD_CCCR_INTEN: u32 = 0x04;\npub(crate) const SDIOD_CCCR_BICTRL: u32 = 0x07;\npub(crate) const SDIOD_CCCR_BLKSIZE_0: u32 = 0x10;\npub(crate) const SDIOD_CCCR_SPEED_CONTROL: u32 = 0x13;\npub(crate) const SDIOD_CCCR_BRCM_CARDCAP: u32 = 0xf0;\npub(crate) const SDIOD_SEP_INT_CTL: u32 = 0xf2;\npub(crate) const SDIOD_CCCR_F1BLKSIZE_0: u32 = 0x110;\npub(crate) const SDIOD_CCCR_F2BLKSIZE_0: u32 = 0x210;\npub(crate) const SDIOD_CCCR_F2BLKSIZE_1: u32 = 0x211;\npub(crate) const INTR_CTL_MASTER_EN: u32 = 0x01;\npub(crate) const INTR_CTL_FUNC1_EN: u32 = 0x02;\npub(crate) const INTR_CTL_FUNC2_EN: u32 = 0x04;\npub(crate) const SDIO_FUNC_ENABLE_1: u32 = 0x02;\npub(crate) const SDIO_FUNC_ENABLE_2: u32 = 0x04;\npub(crate) const SDIO_FUNC_READY_1: u32 = 0x02;\npub(crate) const SDIO_FUNC_READY_2: u32 = 0x04;\npub(crate) const SDIO_64B_BLOCK: u32 = 64;\npub(crate) const SDIO_CHIP_CLOCK_CSR: u32 = 0x1000e;\npub(crate) const SDIO_PULL_UP: u32 = 0x1000f;\n\n// SDIOD_SEP_INT_CTL bits\npub(crate) const SEP_INTR_CTL_MASK: u32 = 0x01; // out-of-band interrupt mask\npub(crate) const SEP_INTR_CTL_EN: u32 = 0x02; // out-of-band interrupt output enable\npub(crate) const SEP_INTR_CTL_POL: u32 = 0x04; // out-of-band interrupt polarity\n\npub(crate) const CHIPCOMMON_BASE_ADDRESS: u32 = 0x18000000;\npub(crate) const SDIO_BASE_ADDRESS: u32 = 0x18002000;\n\n// Security type (authentication and encryption types are combined using bit mask)\n#[allow(non_camel_case_types)]\n#[derive(Copy, Clone, PartialEq)]\n#[repr(u32)]\npub(crate) enum Security {\n    OPEN = 0,\n    WPA2_AES_PSK = WPA2_SECURITY | AES_ENABLED,\n}\n\ncrate::util::enum_from_u8! {\n    #[allow(non_camel_case_types)]\n    #[derive(Copy, Clone, PartialEq)]\n    enum EStatus {\n        #[default]\n        Unknown = 0xFF,\n        /// operation was successful\n        SUCCESS = 0,\n        /// operation failed\n        FAIL = 1,\n        /// operation timed out\n        TIMEOUT = 2,\n        /// failed due to no matching network found\n        NO_NETWORKS = 3,\n        /// operation was aborted\n        ABORT = 4,\n        /// protocol failure: packet not ack'd\n        NO_ACK = 5,\n        /// AUTH or ASSOC packet was unsolicited\n        UNSOLICITED = 6,\n        /// attempt to assoc to an auto auth configuration\n        ATTEMPT = 7,\n        /// scan results are incomplete\n        PARTIAL = 8,\n        /// scan aborted by another scan\n        NEWSCAN = 9,\n        /// scan aborted due to assoc in progress\n        NEWASSOC = 10,\n        /// 802.11h quiet period started\n        _11HQUIET = 11,\n        /// user disabled scanning (WLC_SET_SCANSUPPRESS)\n        SUPPRESS = 12,\n        /// no allowable channels to scan\n        NOCHANS = 13,\n        /// scan aborted due to CCX fast roam\n        CCXFASTRM = 14,\n        /// abort channel select\n        CS_ABORT = 15,\n    }\n}\n\nimpl PartialEq<EStatus> for u32 {\n    fn eq(&self, other: &EStatus) -> bool {\n        *self == *other as Self\n    }\n}\n\n#[allow(dead_code)]\npub(crate) struct FormatStatus(pub u32);\n\n#[cfg(feature = \"defmt\")]\nimpl defmt::Format for FormatStatus {\n    fn format(&self, fmt: defmt::Formatter) {\n        macro_rules! implm {\n            ($($name:ident),*) => {\n                $(\n                    if self.0 & $name > 0 {\n                        defmt::write!(fmt, \" | {}\", &stringify!($name)[7..]);\n                    }\n                )*\n            };\n        }\n\n        implm!(\n            STATUS_DATA_NOT_AVAILABLE,\n            STATUS_UNDERFLOW,\n            STATUS_OVERFLOW,\n            STATUS_F2_INTR,\n            STATUS_F3_INTR,\n            STATUS_F2_RX_READY,\n            STATUS_F3_RX_READY,\n            STATUS_HOST_CMD_DATA_ERR,\n            STATUS_F2_PKT_AVAILABLE,\n            STATUS_F3_PKT_AVAILABLE\n        );\n    }\n}\n\n#[cfg(feature = \"log\")]\nimpl core::fmt::Debug for FormatStatus {\n    fn fmt(&self, fmt: &mut core::fmt::Formatter) -> core::fmt::Result {\n        macro_rules! implm {\n            ($($name:ident),*) => {\n                $(\n                    if self.0 & $name > 0 {\n                        core::write!(fmt, \" | {}\", &stringify!($name)[7..])?;\n                    }\n                )*\n            };\n        }\n\n        implm!(\n            STATUS_DATA_NOT_AVAILABLE,\n            STATUS_UNDERFLOW,\n            STATUS_OVERFLOW,\n            STATUS_F2_INTR,\n            STATUS_F3_INTR,\n            STATUS_F2_RX_READY,\n            STATUS_F3_RX_READY,\n            STATUS_HOST_CMD_DATA_ERR,\n            STATUS_F2_PKT_AVAILABLE,\n            STATUS_F3_PKT_AVAILABLE\n        );\n        Ok(())\n    }\n}\n\n#[cfg(feature = \"log\")]\nimpl core::fmt::Display for FormatStatus {\n    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {\n        core::fmt::Debug::fmt(self, f)\n    }\n}\n\n#[allow(dead_code)]\npub(crate) struct FormatInterrupt(pub u16);\n\n#[cfg(feature = \"defmt\")]\nimpl defmt::Format for FormatInterrupt {\n    fn format(&self, fmt: defmt::Formatter) {\n        macro_rules! implm {\n            ($($name:ident),*) => {\n                $(\n                    if self.0 & $name > 0 {\n                        defmt::write!(fmt, \" | {}\", &stringify!($name)[4..]);\n                    }\n                )*\n            };\n        }\n\n        implm!(\n            IRQ_DATA_UNAVAILABLE,\n            IRQ_F2_F3_FIFO_RD_UNDERFLOW,\n            IRQ_F2_F3_FIFO_WR_OVERFLOW,\n            IRQ_COMMAND_ERROR,\n            IRQ_DATA_ERROR,\n            IRQ_F2_PACKET_AVAILABLE,\n            IRQ_F3_PACKET_AVAILABLE,\n            IRQ_F1_OVERFLOW,\n            IRQ_MISC_INTR0,\n            IRQ_MISC_INTR1,\n            IRQ_MISC_INTR2,\n            IRQ_MISC_INTR3,\n            IRQ_MISC_INTR4,\n            IRQ_F1_INTR,\n            IRQ_F2_INTR,\n            IRQ_F3_INTR\n        );\n    }\n}\n\n#[cfg(feature = \"log\")]\nimpl core::fmt::Debug for FormatInterrupt {\n    fn fmt(&self, fmt: &mut core::fmt::Formatter) -> core::fmt::Result {\n        macro_rules! implm {\n            ($($name:ident),*) => {\n                $(\n                    if self.0 & $name > 0 {\n                        core::write!(fmt, \" | {}\", &stringify!($name)[7..])?;\n                    }\n                )*\n            };\n        }\n\n        implm!(\n            IRQ_DATA_UNAVAILABLE,\n            IRQ_F2_F3_FIFO_RD_UNDERFLOW,\n            IRQ_F2_F3_FIFO_WR_OVERFLOW,\n            IRQ_COMMAND_ERROR,\n            IRQ_DATA_ERROR,\n            IRQ_F2_PACKET_AVAILABLE,\n            IRQ_F3_PACKET_AVAILABLE,\n            IRQ_F1_OVERFLOW,\n            IRQ_MISC_INTR0,\n            IRQ_MISC_INTR1,\n            IRQ_MISC_INTR2,\n            IRQ_MISC_INTR3,\n            IRQ_MISC_INTR4,\n            IRQ_F1_INTR,\n            IRQ_F2_INTR,\n            IRQ_F3_INTR\n        );\n        Ok(())\n    }\n}\n\n#[cfg(feature = \"log\")]\nimpl core::fmt::Display for FormatInterrupt {\n    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {\n        core::fmt::Debug::fmt(self, f)\n    }\n}\n\n#[derive(Copy, Clone, Debug)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\n#[repr(u32)]\npub(crate) enum Ioctl {\n    GetMagic = 0,\n    GetVersion = 1,\n    Up = 2,\n    Down = 3,\n    GetLoop = 4,\n    SetLoop = 5,\n    Dump = 6,\n    GetMsglevel = 7,\n    SetMsglevel = 8,\n    GetPromisc = 9,\n    SetPromisc = 10,\n    GetRate = 12,\n    GetInstance = 14,\n    GetInfra = 19,\n    SetInfra = 20,\n    GetAuth = 21,\n    SetAuth = 22,\n    GetBssid = 23,\n    SetBssid = 24,\n    GetSsid = 25,\n    SetSsid = 26,\n    Restart = 27,\n    GetChannel = 29,\n    SetChannel = 30,\n    GetSrl = 31,\n    SetSrl = 32,\n    GetLrl = 33,\n    SetLrl = 34,\n    GetPlcphdr = 35,\n    SetPlcphdr = 36,\n    GetRadio = 37,\n    SetRadio = 38,\n    GetPhytype = 39,\n    DumpRate = 40,\n    SetRateParams = 41,\n    GetKey = 44,\n    SetKey = 45,\n    GetRegulatory = 46,\n    SetRegulatory = 47,\n    GetPassiveScan = 48,\n    SetPassiveScan = 49,\n    Scan = 50,\n    ScanResults = 51,\n    Disassoc = 52,\n    Reassoc = 53,\n    GetRoamTrigger = 54,\n    SetRoamTrigger = 55,\n    GetRoamDelta = 56,\n    SetRoamDelta = 57,\n    GetRoamScanPeriod = 58,\n    SetRoamScanPeriod = 59,\n    Evm = 60,\n    GetTxant = 61,\n    SetTxant = 62,\n    GetAntdiv = 63,\n    SetAntdiv = 64,\n    GetClosed = 67,\n    SetClosed = 68,\n    GetMaclist = 69,\n    SetMaclist = 70,\n    GetRateset = 71,\n    SetRateset = 72,\n    Longtrain = 74,\n    GetBcnprd = 75,\n    SetBcnprd = 76,\n    GetDtimprd = 77,\n    SetDtimprd = 78,\n    GetSrom = 79,\n    SetSrom = 80,\n    GetWepRestrict = 81,\n    SetWepRestrict = 82,\n    GetCountry = 83,\n    SetCountry = 84,\n    GetPm = 85,\n    SetPm = 86,\n    GetWake = 87,\n    SetWake = 88,\n    GetForcelink = 90,\n    SetForcelink = 91,\n    FreqAccuracy = 92,\n    CarrierSuppress = 93,\n    GetPhyreg = 94,\n    SetPhyreg = 95,\n    GetRadioreg = 96,\n    SetRadioreg = 97,\n    GetRevinfo = 98,\n    GetUcantdiv = 99,\n    SetUcantdiv = 100,\n    RReg = 101,\n    WReg = 102,\n    GetMacmode = 105,\n    SetMacmode = 106,\n    GetMonitor = 107,\n    SetMonitor = 108,\n    GetGmode = 109,\n    SetGmode = 110,\n    GetLegacyErp = 111,\n    SetLegacyErp = 112,\n    GetRxAnt = 113,\n    GetCurrRateset = 114,\n    GetScansuppress = 115,\n    SetScansuppress = 116,\n    GetAp = 117,\n    SetAp = 118,\n    GetEapRestrict = 119,\n    SetEapRestrict = 120,\n    ScbAuthorize = 121,\n    ScbDeauthorize = 122,\n    GetWdslist = 123,\n    SetWdslist = 124,\n    GetAtim = 125,\n    SetAtim = 126,\n    GetRssi = 127,\n    GetPhyantdiv = 128,\n    SetPhyantdiv = 129,\n    ApRxOnly = 130,\n    GetTxPathPwr = 131,\n    SetTxPathPwr = 132,\n    GetWsec = 133,\n    SetWsec = 134,\n    GetPhyNoise = 135,\n    GetBssInfo = 136,\n    GetPktcnts = 137,\n    GetLazywds = 138,\n    SetLazywds = 139,\n    GetBandlist = 140,\n    GetBand = 141,\n    SetBand = 142,\n    ScbDeauthenticate = 143,\n    GetShortslot = 144,\n    GetShortslotOverride = 145,\n    SetShortslotOverride = 146,\n    GetShortslotRestrict = 147,\n    SetShortslotRestrict = 148,\n    GetGmodeProtection = 149,\n    GetGmodeProtectionOverride = 150,\n    SetGmodeProtectionOverride = 151,\n    Upgrade = 152,\n    GetIgnoreBcns = 155,\n    SetIgnoreBcns = 156,\n    GetScbTimeout = 157,\n    SetScbTimeout = 158,\n    GetAssoclist = 159,\n    GetClk = 160,\n    SetClk = 161,\n    GetUp = 162,\n    Out = 163,\n    GetWpaAuth = 164,\n    SetWpaAuth = 165,\n    GetUcflags = 166,\n    SetUcflags = 167,\n    GetPwridx = 168,\n    SetPwridx = 169,\n    GetTssi = 170,\n    GetSupRatesetOverride = 171,\n    SetSupRatesetOverride = 172,\n    GetProtectionControl = 178,\n    SetProtectionControl = 179,\n    GetPhylist = 180,\n    EncryptStrength = 181,\n    DecryptStatus = 182,\n    GetKeySeq = 183,\n    GetScanChannelTime = 184,\n    SetScanChannelTime = 185,\n    GetScanUnassocTime = 186,\n    SetScanUnassocTime = 187,\n    GetScanHomeTime = 188,\n    SetScanHomeTime = 189,\n    GetScanNprobes = 190,\n    SetScanNprobes = 191,\n    GetPrbRespTimeout = 192,\n    SetPrbRespTimeout = 193,\n    GetAtten = 194,\n    SetAtten = 195,\n    GetShmem = 196,\n    SetShmem = 197,\n    SetWsecTest = 200,\n    ScbDeauthenticateForReason = 201,\n    TkipCountermeasures = 202,\n    GetPiomode = 203,\n    SetPiomode = 204,\n    SetAssocPrefer = 205,\n    GetAssocPrefer = 206,\n    SetRoamPrefer = 207,\n    GetRoamPrefer = 208,\n    SetLed = 209,\n    GetLed = 210,\n    GetInterferenceMode = 211,\n    SetInterferenceMode = 212,\n    GetChannelQa = 213,\n    StartChannelQa = 214,\n    GetChannelSel = 215,\n    StartChannelSel = 216,\n    GetValidChannels = 217,\n    GetFakefrag = 218,\n    SetFakefrag = 219,\n    GetPwroutPercentage = 220,\n    SetPwroutPercentage = 221,\n    SetBadFramePreempt = 222,\n    GetBadFramePreempt = 223,\n    SetLeapList = 224,\n    GetLeapList = 225,\n    GetCwmin = 226,\n    SetCwmin = 227,\n    GetCwmax = 228,\n    SetCwmax = 229,\n    GetWet = 230,\n    SetWet = 231,\n    GetPub = 232,\n    GetKeyPrimary = 235,\n    SetKeyPrimary = 236,\n    GetAciArgs = 238,\n    SetAciArgs = 239,\n    UnsetCallback = 240,\n    SetCallback = 241,\n    GetRadar = 242,\n    SetRadar = 243,\n    SetSpectManagment = 244,\n    GetSpectManagment = 245,\n    WdsGetRemoteHwaddr = 246,\n    WdsGetWpaSup = 247,\n    SetCsScanTimer = 248,\n    GetCsScanTimer = 249,\n    MeasureRequest = 250,\n    Init = 251,\n    SendQuiet = 252,\n    Keepalive = 253,\n    SendPwrConstraint = 254,\n    UpgradeStatus = 255,\n    CurrentPwr = 256,\n    GetScanPassiveTime = 257,\n    SetScanPassiveTime = 258,\n    LegacyLinkBehavior = 259,\n    GetChannelsInCountry = 260,\n    GetCountryList = 261,\n    GetVar = 262,\n    SetVar = 263,\n    NvramGet = 264,\n    NvramSet = 265,\n    NvramDump = 266,\n    Reboot = 267,\n    SetWsecPmk = 268,\n    GetAuthMode = 269,\n    SetAuthMode = 270,\n    GetWakeentry = 271,\n    SetWakeentry = 272,\n    NdconfigItem = 273,\n    Nvotpw = 274,\n    Otpw = 275,\n    IovBlockGet = 276,\n    IovModulesGet = 277,\n    SoftReset = 278,\n    GetAllowMode = 279,\n    SetAllowMode = 280,\n    GetDesiredBssid = 281,\n    SetDesiredBssid = 282,\n    DisassocMyap = 283,\n    GetNbands = 284,\n    GetBandstates = 285,\n    GetWlcBssInfo = 286,\n    GetAssocInfo = 287,\n    GetOidPhy = 288,\n    SetOidPhy = 289,\n    SetAssocTime = 290,\n    GetDesiredSsid = 291,\n    GetChanspec = 292,\n    GetAssocState = 293,\n    SetPhyState = 294,\n    GetScanPending = 295,\n    GetScanreqPending = 296,\n    GetPrevRoamReason = 297,\n    SetPrevRoamReason = 298,\n    GetBandstatesPi = 299,\n    GetPhyState = 300,\n    GetBssWpaRsn = 301,\n    GetBssWpa2Rsn = 302,\n    GetBssBcnTs = 303,\n    GetIntDisassoc = 304,\n    SetNumPeers = 305,\n    GetNumBss = 306,\n    GetWsecPmk = 318,\n    GetRandomBytes = 319,\n}\n\npub(crate) const WSEC_TKIP: u32 = 0x02;\npub(crate) const WSEC_AES: u32 = 0x04;\n\npub(crate) const AUTH_OPEN: u32 = 0x00;\npub(crate) const AUTH_SAE: u32 = 0x03;\n\npub(crate) const MFP_NONE: u32 = 0;\npub(crate) const MFP_CAPABLE: u32 = 1;\npub(crate) const MFP_REQUIRED: u32 = 2;\n\npub(crate) const WPA_AUTH_DISABLED: u32 = 0x0000;\npub(crate) const WPA_AUTH_WPA_PSK: u32 = 0x0004;\npub(crate) const WPA_AUTH_WPA2_PSK: u32 = 0x0080;\npub(crate) const WPA_AUTH_WPA3_SAE_PSK: u32 = 0x40000;\n"
  },
  {
    "path": "cyw43/src/control.rs",
    "content": "use core::cmp::{max, min};\nuse core::iter::zip;\nuse core::sync::atomic::AtomicBool;\nuse core::sync::atomic::Ordering::Relaxed;\n\nuse embassy_net_driver_channel as ch;\nuse embassy_net_driver_channel::driver::HardwareAddress;\nuse embassy_time::{Duration, Timer};\n\nuse crate::consts::*;\nuse crate::events::{Event, EventSubscriber, Events};\nuse crate::fmt::Bytes;\nuse crate::ioctl::{IoctlState, IoctlType};\nuse crate::structs::*;\nuse crate::{PowerManagementMode, countries, events};\n\n/// Join errors.\n#[derive(Debug)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub enum JoinError {\n    /// Network not found.\n    NetworkNotFound,\n    /// Failure to join network. Contains the status code from the SET_SSID event.\n    JoinFailure(u8),\n    /// Authentication failure for a secure network.\n    AuthenticationFailure,\n}\n\n/// Multicast errors.\n#[derive(Debug)]\npub enum AddMulticastAddressError {\n    /// Not a multicast address.\n    NotMulticast,\n    /// No free address slots.\n    NoFreeSlots,\n}\n\n/// Control driver.\npub struct Control<'a> {\n    state_ch: ch::StateRunner<'a>,\n    events: &'a Events,\n    ioctl_state: &'a IoctlState,\n    secure_network: &'a AtomicBool,\n}\n\n/// WiFi scan type.\n#[derive(Copy, Clone, Debug)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub enum ScanType {\n    /// Active scan: the station actively transmits probes that make APs respond.\n    /// Faster, but uses more power.\n    Active,\n    /// Passive scan: the station doesn't transmit any probes, just listens for beacons.\n    /// Slower, but uses less power.\n    Passive,\n}\n\n/// Scan options.\n#[derive(Clone, Debug)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\n#[non_exhaustive]\npub struct ScanOptions {\n    /// SSID to scan for.\n    pub ssid: Option<heapless::String<32>>,\n    /// If set to `None`, all APs will be returned. If set to `Some`, only APs\n    /// with the specified BSSID will be returned.\n    pub bssid: Option<[u8; 6]>,\n    /// Number of probes to send on each channel.\n    pub nprobes: Option<u16>,\n    /// Time to spend waiting on the home channel.\n    pub home_time: Option<Duration>,\n    /// Scan type: active or passive.\n    pub scan_type: ScanType,\n    /// Period of time to wait on each channel when passive scanning.\n    pub dwell_time: Option<Duration>,\n}\n\nimpl Default for ScanOptions {\n    fn default() -> Self {\n        Self {\n            ssid: None,\n            bssid: None,\n            nprobes: None,\n            home_time: None,\n            scan_type: ScanType::Passive,\n            dwell_time: None,\n        }\n    }\n}\n\n/// Authentication type, used in [`JoinOptions::auth`].\n#[derive(Copy, Clone, Debug, PartialEq, Eq)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub enum JoinAuth {\n    /// Open network\n    Open,\n    /// WPA only\n    Wpa,\n    /// WPA2 only\n    Wpa2,\n    /// WPA3 only\n    Wpa3,\n    /// WPA2 + WPA3\n    Wpa2Wpa3,\n}\n\n/// Options for [`Control::join`].\n#[derive(Clone, Debug)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\n#[non_exhaustive]\npub struct JoinOptions<'a> {\n    /// Authentication type. Default `Wpa2Wpa3`.\n    pub auth: JoinAuth,\n    /// Enable TKIP encryption. Default false.\n    pub cipher_tkip: bool,\n    /// Enable AES encryption. Default true.\n    pub cipher_aes: bool,\n    /// Passphrase. Default empty.\n    pub passphrase: &'a [u8],\n    /// If false, `passphrase` is the human-readable passphrase string.\n    /// If true, `passphrase` is the result of applying the PBKDF2 hash to the\n    /// passphrase string. This makes it possible to avoid storing unhashed passwords.\n    ///\n    /// This is not compatible with WPA3.\n    /// Default false.\n    pub passphrase_is_prehashed: bool,\n}\n\nimpl<'a> JoinOptions<'a> {\n    /// Create a new `JoinOptions` for joining open networks.\n    pub fn new_open() -> Self {\n        Self {\n            auth: JoinAuth::Open,\n            cipher_tkip: false,\n            cipher_aes: false,\n            passphrase: &[],\n            passphrase_is_prehashed: false,\n        }\n    }\n\n    /// Create a new `JoinOptions` for joining encrypted networks.\n    ///\n    /// Defaults to supporting WPA2+WPA3 with AES only, you may edit\n    /// the returned options to change this.\n    pub fn new(passphrase: &'a [u8]) -> Self {\n        let mut this = Self::default();\n        this.passphrase = passphrase;\n        this\n    }\n}\n\nimpl<'a> Default for JoinOptions<'a> {\n    fn default() -> Self {\n        Self {\n            auth: JoinAuth::Wpa2Wpa3,\n            cipher_tkip: false,\n            cipher_aes: true,\n            passphrase: &[],\n            passphrase_is_prehashed: false,\n        }\n    }\n}\n\nimpl<'a> Control<'a> {\n    pub(crate) fn new(\n        state_ch: ch::StateRunner<'a>,\n        event_sub: &'a Events,\n        ioctl_state: &'a IoctlState,\n        secure_network: &'a AtomicBool,\n    ) -> Self {\n        Self {\n            state_ch,\n            events: event_sub,\n            ioctl_state,\n            secure_network,\n        }\n    }\n\n    async fn load_clm(&mut self, clm: &[u8]) {\n        const CHUNK_SIZE: usize = 1024;\n\n        debug!(\"Downloading CLM...\");\n\n        let mut offs = 0;\n        for chunk in clm.chunks(CHUNK_SIZE) {\n            let mut flag = DOWNLOAD_FLAG_HANDLER_VER;\n            if offs == 0 {\n                flag |= DOWNLOAD_FLAG_BEGIN;\n            }\n            offs += chunk.len();\n            if offs == clm.len() {\n                flag |= DOWNLOAD_FLAG_END;\n            }\n\n            let header = DownloadHeader {\n                flag,\n                dload_type: DOWNLOAD_TYPE_CLM,\n                len: chunk.len() as _,\n                crc: 0,\n            };\n            let mut buf = [0; 8 + 12 + CHUNK_SIZE];\n            buf[0..8].copy_from_slice(b\"clmload\\x00\");\n            buf[8..20].copy_from_slice(&header.to_bytes());\n            buf[20..][..chunk.len()].copy_from_slice(&chunk);\n            self.ioctl(IoctlType::Set, Ioctl::SetVar, 0, &mut buf[..8 + 12 + chunk.len()])\n                .await;\n        }\n\n        // check clmload ok\n        assert_eq!(self.get_iovar_u32(\"clmload_status\").await, 0);\n    }\n\n    /// Initialize WiFi controller.\n    pub async fn init(&mut self, clm: &[u8]) {\n        self.load_clm(&clm).await;\n\n        debug!(\"Configuring misc stuff...\");\n\n        // Disable tx gloming which transfers multiple packets in one request.\n        // 'glom' is short for \"conglomerate\" which means \"gather together into\n        // a compact mass\".\n        self.set_iovar_u32(\"bus:txglom\", 0).await;\n        self.set_iovar_u32(\"apsta\", 1).await;\n\n        // read MAC addr.\n        let mac_addr = self.address().await;\n        debug!(\"mac addr: {:02x}\", Bytes(&mac_addr));\n\n        let country = countries::WORLD_WIDE_XX;\n        let country_info = CountryInfo {\n            country_abbrev: [country.code[0], country.code[1], 0, 0],\n            country_code: [country.code[0], country.code[1], 0, 0],\n            rev: if country.rev == 0 { -1 } else { country.rev as _ },\n        };\n        self.set_iovar(\"country\", &country_info.to_bytes()).await;\n\n        // set country takes some time, next ioctls fail if we don't wait.\n        Timer::after_millis(100).await;\n\n        // Set antenna to chip antenna\n        self.ioctl_set_u32(Ioctl::SetAntdiv, 0, 0).await;\n\n        self.set_iovar_u32(\"bus:txglom\", 0).await;\n        Timer::after_millis(100).await;\n        //self.set_iovar_u32(\"apsta\", 1).await; // this crashes, also we already did it before...??\n        //Timer::after_millis(100).await;\n        self.set_iovar_u32(\"ampdu_ba_wsize\", 8).await;\n        Timer::after_millis(100).await;\n        self.set_iovar_u32(\"ampdu_mpdu\", 4).await;\n        Timer::after_millis(100).await;\n        //self.set_iovar_u32(\"ampdu_rx_factor\", 0).await; // this crashes\n\n        //Timer::after_millis(100).await;\n\n        // evts\n        let mut evts = EventMask {\n            iface: 0,\n            events: [0xFF; 24],\n        };\n\n        // Disable spammy uninteresting events.\n        evts.unset(Event::RADIO);\n        evts.unset(Event::IF);\n        evts.unset(Event::PROBREQ_MSG);\n        evts.unset(Event::PROBREQ_MSG_RX);\n        evts.unset(Event::PROBRESP_MSG);\n        evts.unset(Event::PROBRESP_MSG);\n        evts.unset(Event::ROAM);\n\n        self.set_iovar(\"bsscfg:event_msgs\", &evts.to_bytes()).await;\n\n        Timer::after_millis(100).await;\n\n        // set wifi up\n        self.up().await;\n\n        Timer::after_millis(100).await;\n\n        self.ioctl_set_u32(Ioctl::SetGmode, 0, 1).await; // SET_GMODE = auto\n        self.ioctl_set_u32(Ioctl::SetBand, 0, 0).await; // SET_BAND = any\n\n        Timer::after_millis(100).await;\n\n        self.state_ch.set_hardware_address(HardwareAddress::Ethernet(mac_addr));\n\n        debug!(\"cyw43 control init done\");\n    }\n\n    /// Set the WiFi interface up.\n    async fn up(&mut self) {\n        self.ioctl(IoctlType::Set, Ioctl::Up, 0, &mut []).await;\n    }\n\n    /// Set the interface down.\n    async fn down(&mut self) {\n        self.ioctl(IoctlType::Set, Ioctl::Down, 0, &mut []).await;\n    }\n\n    /// Set power management mode.\n    pub async fn set_power_management(&mut self, mode: PowerManagementMode) {\n        // power save mode\n        let mode_num = mode.mode();\n        if mode_num == 2 {\n            self.set_iovar_u32(\"pm2_sleep_ret\", mode.sleep_ret_ms() as u32).await;\n            self.set_iovar_u32(\"bcn_li_bcn\", mode.beacon_period() as u32).await;\n            self.set_iovar_u32(\"bcn_li_dtim\", mode.dtim_period() as u32).await;\n            self.set_iovar_u32(\"assoc_listen\", mode.assoc() as u32).await;\n        }\n        self.ioctl_set_u32(Ioctl::SetPm, 0, mode_num).await;\n    }\n\n    /// Join a network with the provided SSID using the specified options.\n    pub async fn join(&mut self, ssid: &str, options: JoinOptions<'_>) -> Result<(), JoinError> {\n        self.set_iovar_u32(\"ampdu_ba_wsize\", 8).await;\n\n        if options.auth == JoinAuth::Open {\n            self.ioctl_set_u32(Ioctl::SetWsec, 0, 0).await;\n            self.set_iovar_u32x2(\"bsscfg:sup_wpa\", 0, 0).await;\n            self.ioctl_set_u32(Ioctl::SetInfra, 0, 1).await;\n            self.ioctl_set_u32(Ioctl::SetAuth, 0, 0).await;\n            self.ioctl_set_u32(Ioctl::SetWpaAuth, 0, WPA_AUTH_DISABLED).await;\n        } else {\n            let mut wsec = 0;\n            if options.cipher_aes {\n                wsec |= WSEC_AES;\n            }\n            if options.cipher_tkip {\n                wsec |= WSEC_TKIP;\n            }\n            self.ioctl_set_u32(Ioctl::SetWsec, 0, wsec).await;\n\n            self.set_iovar_u32x2(\"bsscfg:sup_wpa\", 0, 1).await;\n            self.set_iovar_u32x2(\"bsscfg:sup_wpa2_eapver\", 0, 0xFFFF_FFFF).await;\n            self.set_iovar_u32x2(\"bsscfg:sup_wpa_tmo\", 0, 2500).await;\n\n            Timer::after_millis(100).await;\n\n            let (wpa12, wpa3, auth, mfp, wpa_auth) = match options.auth {\n                JoinAuth::Open => unreachable!(),\n                JoinAuth::Wpa => (true, false, AUTH_OPEN, MFP_NONE, WPA_AUTH_WPA_PSK),\n                JoinAuth::Wpa2 => (true, false, AUTH_OPEN, MFP_CAPABLE, WPA_AUTH_WPA2_PSK),\n                JoinAuth::Wpa3 => (false, true, AUTH_SAE, MFP_REQUIRED, WPA_AUTH_WPA3_SAE_PSK),\n                JoinAuth::Wpa2Wpa3 => (true, true, AUTH_SAE, MFP_CAPABLE, WPA_AUTH_WPA3_SAE_PSK),\n            };\n\n            if wpa12 {\n                let mut flags = 0;\n                if !options.passphrase_is_prehashed {\n                    flags |= 1;\n                }\n                let mut pfi = PassphraseInfo {\n                    len: options.passphrase.len() as _,\n                    flags,\n                    passphrase: [0; 64],\n                };\n                pfi.passphrase[..options.passphrase.len()].copy_from_slice(options.passphrase);\n                Timer::after_millis(3).await;\n                self.ioctl(IoctlType::Set, Ioctl::SetWsecPmk, 0, &mut pfi.to_bytes())\n                    .await;\n            }\n\n            if wpa3 {\n                let mut pfi = SaePassphraseInfo {\n                    len: options.passphrase.len() as _,\n                    passphrase: [0; 128],\n                };\n                pfi.passphrase[..options.passphrase.len()].copy_from_slice(options.passphrase);\n                Timer::after_millis(3).await;\n                self.set_iovar(\"sae_password\", &pfi.to_bytes()).await;\n            }\n\n            self.ioctl_set_u32(Ioctl::SetInfra, 0, 1).await;\n            self.ioctl_set_u32(Ioctl::SetAuth, 0, auth).await;\n            self.set_iovar_u32(\"mfp\", mfp).await;\n            self.ioctl_set_u32(Ioctl::SetWpaAuth, 0, wpa_auth).await;\n        }\n\n        let mut i = SsidInfo {\n            len: ssid.len() as _,\n            ssid: [0; 32],\n        };\n        i.ssid[..ssid.len()].copy_from_slice(ssid.as_bytes());\n\n        let secure_network = options.auth != JoinAuth::Open;\n        self.secure_network.store(secure_network, Relaxed);\n        self.wait_for_join(i, secure_network).await\n    }\n\n    async fn wait_for_join(&mut self, i: SsidInfo, secure_network: bool) -> Result<(), JoinError> {\n        struct UnsubscribeOnDrop<'a>(&'a Events);\n\n        impl Drop for UnsubscribeOnDrop<'_> {\n            fn drop(&mut self) {\n                self.0.mask.disable_all();\n            }\n        }\n\n        let _uod = UnsubscribeOnDrop(&self.events);\n\n        self.events.mask.enable(&[Event::SET_SSID, Event::AUTH, Event::PSK_SUP]);\n        let mut subscriber = self.events.queue.subscriber().unwrap();\n        // the actual join operation starts here\n        // we make sure to enable events before so we don't miss any\n\n        self.ioctl(IoctlType::Set, Ioctl::SetSsid, 0, &mut i.to_bytes()).await;\n\n        // To complete the join on an open network, we wait for a SET_SSID event with status SUCCESS\n        // For secured networks, we wait for a PSK_SUP event with status 6 \"UNSOLICITED\"\n        let result = loop {\n            let msg = subscriber.next_message_pure().await;\n\n            let status = EStatus::from(msg.header.status as u8);\n            match (msg.header.event_type, status, secure_network) {\n                // Join operation ends with SET_SSID event for open networks\n                (Event::SET_SSID, EStatus::SUCCESS, false) => break Ok(()),\n                (Event::SET_SSID, EStatus::NO_NETWORKS, _) => break Err(JoinError::NetworkNotFound),\n                (Event::SET_SSID, status, _) if status != EStatus::SUCCESS => {\n                    break Err(JoinError::JoinFailure(status as u8));\n                }\n                // Ignore PSK_SUP \"ABORT\" which is sometimes sent before successful join\n                (Event::PSK_SUP, EStatus::ABORT, true) => {}\n                // Event PSK_SUP with status 6 \"UNSOLICITED\" indicates success for secure networks\n                (Event::PSK_SUP, EStatus::UNSOLICITED, true) => break Ok(()),\n                // Events indicating authentication failure, possibly due to incorrect password\n                (Event::PSK_SUP, _, true) | (Event::AUTH, EStatus::FAIL, true) => {\n                    break Err(JoinError::AuthenticationFailure);\n                }\n                _ => {}\n            };\n        };\n\n        match result {\n            Ok(()) => debug!(\"JOINED\"),\n            Err(JoinError::JoinFailure(status)) => debug!(\"JOIN failed: status={}\", status),\n            Err(JoinError::NetworkNotFound) => debug!(\"JOIN failed: network not found\"),\n            Err(JoinError::AuthenticationFailure) => debug!(\"JOIN failed: authentication failure\"),\n        };\n\n        result\n    }\n\n    /// Set GPIO pin on WiFi chip.\n    pub async fn gpio_set(&mut self, gpio_n: u8, gpio_en: bool) {\n        assert!(gpio_n < 3);\n        self.set_iovar_u32x2(\"gpioout\", 1 << gpio_n, if gpio_en { 1 << gpio_n } else { 0 })\n            .await\n    }\n\n    /// Start open access point.\n    pub async fn start_ap_open(&mut self, ssid: &str, channel: u8) {\n        self.start_ap(ssid, \"\", Security::OPEN, channel).await;\n    }\n\n    /// Start WPA2 protected access point.\n    pub async fn start_ap_wpa2(&mut self, ssid: &str, passphrase: &str, channel: u8) {\n        self.start_ap(ssid, passphrase, Security::WPA2_AES_PSK, channel).await;\n    }\n\n    async fn start_ap(&mut self, ssid: &str, passphrase: &str, security: Security, channel: u8) {\n        if security != Security::OPEN\n            && (passphrase.as_bytes().len() < MIN_PSK_LEN || passphrase.as_bytes().len() > MAX_PSK_LEN)\n        {\n            panic!(\"Passphrase is too short or too long\");\n        }\n\n        // Temporarily set wifi down\n        self.down().await;\n\n        // Turn off APSTA mode\n        self.set_iovar_u32(\"apsta\", 0).await;\n\n        // Set wifi up again\n        self.up().await;\n\n        // Disable authentication\n        self.ioctl_set_u32(Ioctl::SetAuth, 0, AUTH_OPEN).await;\n\n        // Turn on AP mode\n        self.ioctl_set_u32(Ioctl::SetAp, 0, 1).await;\n\n        // Set SSID\n        let mut i = SsidInfoWithIndex {\n            index: 0,\n            ssid_info: SsidInfo {\n                len: ssid.as_bytes().len() as _,\n                ssid: [0; 32],\n            },\n        };\n        i.ssid_info.ssid[..ssid.as_bytes().len()].copy_from_slice(ssid.as_bytes());\n        self.set_iovar(\"bsscfg:ssid\", &i.to_bytes()).await;\n\n        // Set channel number\n        self.ioctl_set_u32(Ioctl::SetChannel, 0, channel as u32).await;\n\n        // Set security\n        self.set_iovar_u32x2(\"bsscfg:wsec\", 0, (security as u32) & 0xFF).await;\n\n        if security != Security::OPEN {\n            self.set_iovar_u32x2(\"bsscfg:wpa_auth\", 0, 0x0084).await; // wpa_auth = WPA2_AUTH_PSK | WPA_AUTH_PSK\n\n            Timer::after_millis(100).await;\n\n            // Set passphrase\n            let mut pfi = PassphraseInfo {\n                len: passphrase.as_bytes().len() as _,\n                flags: 1, // WSEC_PASSPHRASE\n                passphrase: [0; 64],\n            };\n            pfi.passphrase[..passphrase.as_bytes().len()].copy_from_slice(passphrase.as_bytes());\n            self.ioctl(IoctlType::Set, Ioctl::SetWsecPmk, 0, &mut pfi.to_bytes())\n                .await;\n        }\n\n        // Change mutlicast rate from 1 Mbps to 11 Mbps\n        self.set_iovar_u32(\"2g_mrate\", 11000000 / 500000).await;\n\n        // Start AP\n        self.set_iovar_u32x2(\"bss\", 0, 1).await; // bss = BSS_UP\n    }\n\n    /// Closes access point.\n    pub async fn close_ap(&mut self) {\n        // Stop AP\n        self.set_iovar_u32x2(\"bss\", 0, 0).await; // bss = BSS_DOWN\n\n        // Turn off AP mode\n        self.ioctl_set_u32(Ioctl::SetAp, 0, 0).await;\n\n        // Temporarily set wifi down\n        self.down().await;\n\n        // Turn on APSTA mode\n        self.set_iovar_u32(\"apsta\", 1).await;\n\n        // Set wifi up again\n        self.up().await;\n    }\n\n    /// Add specified address to the list of hardware addresses the device\n    /// listens on. The address must be a Group address (I/G bit set). Up\n    /// to 10 addresses are supported by the firmware. Returns the number of\n    /// address slots filled after adding, or an error.\n    pub async fn add_multicast_address(&mut self, address: [u8; 6]) -> Result<usize, AddMulticastAddressError> {\n        // The firmware seems to ignore non-multicast addresses, so let's\n        // prevent the user from adding them and wasting space.\n        if address[0] & 0x01 != 1 {\n            return Err(AddMulticastAddressError::NotMulticast);\n        }\n\n        let mut buf = [0; 64];\n        self.get_iovar(\"mcast_list\", &mut buf).await;\n\n        let n = u32::from_le_bytes(buf[..4].try_into().unwrap()) as usize;\n        let (used, free) = buf[4..].split_at_mut(n * 6);\n\n        if used.chunks(6).any(|a| a == address) {\n            return Ok(n);\n        }\n\n        if free.len() < 6 {\n            return Err(AddMulticastAddressError::NoFreeSlots);\n        }\n\n        free[..6].copy_from_slice(&address);\n        let n = n + 1;\n        buf[..4].copy_from_slice(&(n as u32).to_le_bytes());\n\n        self.set_iovar_v::<80>(\"mcast_list\", &buf).await;\n        Ok(n)\n    }\n\n    /// Retrieve the list of configured multicast hardware addresses.\n    pub async fn list_multicast_addresses(&mut self, result: &mut [[u8; 6]; 10]) -> usize {\n        let mut buf = [0; 64];\n        self.get_iovar(\"mcast_list\", &mut buf).await;\n\n        let n = u32::from_le_bytes(buf[..4].try_into().unwrap()) as usize;\n        let used = &buf[4..][..n * 6];\n\n        for (addr, output) in zip(used.chunks(6), result.iter_mut()) {\n            output.copy_from_slice(addr)\n        }\n\n        n\n    }\n\n    /// Retrieve the latest RSSI value\n    pub async fn get_rssi(&mut self) -> i32 {\n        let mut rssi_buf = [0u8; 4];\n        let n = self.ioctl(IoctlType::Get, Ioctl::GetRssi, 0, &mut rssi_buf).await;\n        assert_eq!(n, 4);\n        i32::from_ne_bytes(rssi_buf)\n    }\n\n    async fn set_iovar_u32x2(&mut self, name: &str, val1: u32, val2: u32) {\n        let mut buf = [0; 8];\n        buf[0..4].copy_from_slice(&val1.to_le_bytes());\n        buf[4..8].copy_from_slice(&val2.to_le_bytes());\n        self.set_iovar(name, &buf).await\n    }\n\n    async fn set_iovar_u32(&mut self, name: &str, val: u32) {\n        self.set_iovar(name, &val.to_le_bytes()).await\n    }\n\n    async fn get_iovar_u32(&mut self, name: &str) -> u32 {\n        let mut buf = [0; 4];\n        let len = self.get_iovar(name, &mut buf).await;\n        assert_eq!(len, 4);\n        u32::from_le_bytes(buf)\n    }\n\n    async fn set_iovar(&mut self, name: &str, val: &[u8]) {\n        self.set_iovar_v::<196>(name, val).await\n    }\n\n    async fn set_iovar_v<const BUFSIZE: usize>(&mut self, name: &str, val: &[u8]) {\n        debug!(\"iovar set {} = {:02x}\", name, Bytes(val));\n\n        let mut buf = [0; BUFSIZE];\n        buf[..name.len()].copy_from_slice(name.as_bytes());\n        buf[name.len()] = 0;\n        buf[name.len() + 1..][..val.len()].copy_from_slice(val);\n\n        let total_len = name.len() + 1 + val.len();\n        self.ioctl_inner(IoctlType::Set, Ioctl::SetVar, 0, &mut buf[..total_len])\n            .await;\n    }\n\n    // TODO this is not really working, it always returns all zeros.\n    async fn get_iovar(&mut self, name: &str, res: &mut [u8]) -> usize {\n        debug!(\"iovar get {}\", name);\n\n        let mut buf = [0; 64];\n        buf[..name.len()].copy_from_slice(name.as_bytes());\n        buf[name.len()] = 0;\n\n        let total_len = max(name.len() + 1, res.len());\n        let res_len = self\n            .ioctl_inner(IoctlType::Get, Ioctl::GetVar, 0, &mut buf[..total_len])\n            .await;\n\n        let out_len = min(res.len(), res_len);\n        res[..out_len].copy_from_slice(&buf[..out_len]);\n        out_len\n    }\n\n    async fn ioctl_set_u32(&mut self, cmd: Ioctl, iface: u32, val: u32) {\n        let mut buf = val.to_le_bytes();\n        self.ioctl(IoctlType::Set, cmd, iface, &mut buf).await;\n    }\n\n    async fn ioctl(&mut self, kind: IoctlType, cmd: Ioctl, iface: u32, buf: &mut [u8]) -> usize {\n        if kind == IoctlType::Set {\n            debug!(\"ioctl set {:?} iface {} = {:02x}\", cmd, iface, Bytes(buf));\n        }\n        let n = self.ioctl_inner(kind, cmd, iface, buf).await;\n        n\n    }\n\n    async fn ioctl_inner(&mut self, kind: IoctlType, cmd: Ioctl, iface: u32, buf: &mut [u8]) -> usize {\n        struct CancelOnDrop<'a>(&'a IoctlState);\n\n        impl CancelOnDrop<'_> {\n            fn defuse(self) {\n                core::mem::forget(self);\n            }\n        }\n\n        impl Drop for CancelOnDrop<'_> {\n            fn drop(&mut self) {\n                self.0.cancel_ioctl();\n            }\n        }\n\n        let ioctl = CancelOnDrop(self.ioctl_state);\n        let resp_len = ioctl.0.do_ioctl(kind, cmd, iface, buf).await;\n        ioctl.defuse();\n\n        resp_len\n    }\n\n    /// Start a wifi scan\n    ///\n    /// Returns a `Stream` of networks found by the device\n    ///\n    /// # Note\n    /// Device events are currently implemented using a bounded queue.\n    /// To not miss any events, you should make sure to always await the stream.\n    pub async fn scan(&mut self, scan_opts: ScanOptions) -> Scanner<'_> {\n        const SCANTYPE_ACTIVE: u8 = 0;\n        const SCANTYPE_PASSIVE: u8 = 1;\n\n        let dwell_time = match scan_opts.dwell_time {\n            None => !0,\n            Some(t) => {\n                let mut t = t.as_millis() as u32;\n                if t == !0 {\n                    t = !0 - 1;\n                }\n                t\n            }\n        };\n\n        let mut active_time = !0;\n        let mut passive_time = !0;\n        let scan_type = match scan_opts.scan_type {\n            ScanType::Active => {\n                active_time = dwell_time;\n                SCANTYPE_ACTIVE\n            }\n            ScanType::Passive => {\n                passive_time = dwell_time;\n                SCANTYPE_PASSIVE\n            }\n        };\n\n        let scan_params = ScanParams {\n            version: 1,\n            action: 1,\n            sync_id: 1,\n            ssid_len: scan_opts.ssid.as_ref().map(|e| e.as_bytes().len() as u32).unwrap_or(0),\n            ssid: scan_opts\n                .ssid\n                .map(|e| {\n                    let mut ssid = [0; 32];\n                    ssid[..e.as_bytes().len()].copy_from_slice(e.as_bytes());\n                    ssid\n                })\n                .unwrap_or([0; 32]),\n            bssid: scan_opts.bssid.unwrap_or([0xff; 6]),\n            bss_type: 2,\n            scan_type,\n            nprobes: scan_opts.nprobes.unwrap_or(!0).into(),\n            active_time,\n            passive_time,\n            home_time: scan_opts.home_time.map(|e| e.as_millis() as u32).unwrap_or(!0),\n            channel_num: 0,\n            channel_list: [0; 1],\n        };\n\n        self.events.mask.enable(&[Event::ESCAN_RESULT]);\n        let subscriber = self.events.queue.subscriber().unwrap();\n        self.set_iovar_v::<256>(\"escan\", &scan_params.to_bytes()).await;\n\n        Scanner {\n            subscriber,\n            events: &self.events,\n        }\n    }\n    /// Leave the wifi, with which we are currently associated.\n    pub async fn leave(&mut self) {\n        self.ioctl(IoctlType::Set, Ioctl::Disassoc, 0, &mut []).await;\n        info!(\"Disassociated\")\n    }\n\n    /// Gets the MAC address of the device\n    pub async fn address(&mut self) -> [u8; 6] {\n        let mut mac_addr = [0; 6];\n        assert_eq!(self.get_iovar(\"cur_etheraddr\", &mut mac_addr).await, 6);\n        mac_addr\n    }\n}\n\n/// WiFi network scanner.\npub struct Scanner<'a> {\n    subscriber: EventSubscriber<'a>,\n    events: &'a Events,\n}\n\nimpl Scanner<'_> {\n    /// Wait for the next found network.\n    pub async fn next(&mut self) -> Option<BssInfo> {\n        let event = self.subscriber.next_message_pure().await;\n        if event.header.status != EStatus::PARTIAL {\n            self.events.mask.disable_all();\n            return None;\n        }\n\n        if let events::Payload::BssInfo(bss) = event.payload {\n            Some(bss)\n        } else {\n            None\n        }\n    }\n}\n\nimpl Drop for Scanner<'_> {\n    fn drop(&mut self) {\n        self.events.mask.disable_all();\n    }\n}\n"
  },
  {
    "path": "cyw43/src/countries.rs",
    "content": "#![allow(unused)]\n\npub struct Country {\n    pub code: [u8; 2],\n    pub rev: u16,\n}\n\n/// AF Afghanistan\npub const AFGHANISTAN: Country = Country { code: *b\"AF\", rev: 0 };\n/// AL Albania\npub const ALBANIA: Country = Country { code: *b\"AL\", rev: 0 };\n/// DZ Algeria\npub const ALGERIA: Country = Country { code: *b\"DZ\", rev: 0 };\n/// AS American_Samoa\npub const AMERICAN_SAMOA: Country = Country { code: *b\"AS\", rev: 0 };\n/// AO Angola\npub const ANGOLA: Country = Country { code: *b\"AO\", rev: 0 };\n/// AI Anguilla\npub const ANGUILLA: Country = Country { code: *b\"AI\", rev: 0 };\n/// AG Antigua_and_Barbuda\npub const ANTIGUA_AND_BARBUDA: Country = Country { code: *b\"AG\", rev: 0 };\n/// AR Argentina\npub const ARGENTINA: Country = Country { code: *b\"AR\", rev: 0 };\n/// AM Armenia\npub const ARMENIA: Country = Country { code: *b\"AM\", rev: 0 };\n/// AW Aruba\npub const ARUBA: Country = Country { code: *b\"AW\", rev: 0 };\n/// AU Australia\npub const AUSTRALIA: Country = Country { code: *b\"AU\", rev: 0 };\n/// AT Austria\npub const AUSTRIA: Country = Country { code: *b\"AT\", rev: 0 };\n/// AZ Azerbaijan\npub const AZERBAIJAN: Country = Country { code: *b\"AZ\", rev: 0 };\n/// BS Bahamas\npub const BAHAMAS: Country = Country { code: *b\"BS\", rev: 0 };\n/// BH Bahrain\npub const BAHRAIN: Country = Country { code: *b\"BH\", rev: 0 };\n/// 0B Baker_Island\npub const BAKER_ISLAND: Country = Country { code: *b\"0B\", rev: 0 };\n/// BD Bangladesh\npub const BANGLADESH: Country = Country { code: *b\"BD\", rev: 0 };\n/// BB Barbados\npub const BARBADOS: Country = Country { code: *b\"BB\", rev: 0 };\n/// BY Belarus\npub const BELARUS: Country = Country { code: *b\"BY\", rev: 0 };\n/// BE Belgium\npub const BELGIUM: Country = Country { code: *b\"BE\", rev: 0 };\n/// BZ Belize\npub const BELIZE: Country = Country { code: *b\"BZ\", rev: 0 };\n/// BJ Benin\npub const BENIN: Country = Country { code: *b\"BJ\", rev: 0 };\n/// BM Bermuda\npub const BERMUDA: Country = Country { code: *b\"BM\", rev: 0 };\n/// BT Bhutan\npub const BHUTAN: Country = Country { code: *b\"BT\", rev: 0 };\n/// BO Bolivia\npub const BOLIVIA: Country = Country { code: *b\"BO\", rev: 0 };\n/// BA Bosnia_and_Herzegovina\npub const BOSNIA_AND_HERZEGOVINA: Country = Country { code: *b\"BA\", rev: 0 };\n/// BW Botswana\npub const BOTSWANA: Country = Country { code: *b\"BW\", rev: 0 };\n/// BR Brazil\npub const BRAZIL: Country = Country { code: *b\"BR\", rev: 0 };\n/// IO British_Indian_Ocean_Territory\npub const BRITISH_INDIAN_OCEAN_TERRITORY: Country = Country { code: *b\"IO\", rev: 0 };\n/// BN Brunei_Darussalam\npub const BRUNEI_DARUSSALAM: Country = Country { code: *b\"BN\", rev: 0 };\n/// BG Bulgaria\npub const BULGARIA: Country = Country { code: *b\"BG\", rev: 0 };\n/// BF Burkina_Faso\npub const BURKINA_FASO: Country = Country { code: *b\"BF\", rev: 0 };\n/// BI Burundi\npub const BURUNDI: Country = Country { code: *b\"BI\", rev: 0 };\n/// KH Cambodia\npub const CAMBODIA: Country = Country { code: *b\"KH\", rev: 0 };\n/// CM Cameroon\npub const CAMEROON: Country = Country { code: *b\"CM\", rev: 0 };\n/// CA Canada\npub const CANADA: Country = Country { code: *b\"CA\", rev: 0 };\n/// CA Canada Revision 950\npub const CANADA_REV950: Country = Country { code: *b\"CA\", rev: 950 };\n/// CV Cape_Verde\npub const CAPE_VERDE: Country = Country { code: *b\"CV\", rev: 0 };\n/// KY Cayman_Islands\npub const CAYMAN_ISLANDS: Country = Country { code: *b\"KY\", rev: 0 };\n/// CF Central_African_Republic\npub const CENTRAL_AFRICAN_REPUBLIC: Country = Country { code: *b\"CF\", rev: 0 };\n/// TD Chad\npub const CHAD: Country = Country { code: *b\"TD\", rev: 0 };\n/// CL Chile\npub const CHILE: Country = Country { code: *b\"CL\", rev: 0 };\n/// CN China\npub const CHINA: Country = Country { code: *b\"CN\", rev: 0 };\n/// CX Christmas_Island\npub const CHRISTMAS_ISLAND: Country = Country { code: *b\"CX\", rev: 0 };\n/// CO Colombia\npub const COLOMBIA: Country = Country { code: *b\"CO\", rev: 0 };\n/// KM Comoros\npub const COMOROS: Country = Country { code: *b\"KM\", rev: 0 };\n/// CG Congo\npub const CONGO: Country = Country { code: *b\"CG\", rev: 0 };\n/// CD Congo,_The_Democratic_Republic_Of_The\npub const CONGO_THE_DEMOCRATIC_REPUBLIC_OF_THE: Country = Country { code: *b\"CD\", rev: 0 };\n/// CR Costa_Rica\npub const COSTA_RICA: Country = Country { code: *b\"CR\", rev: 0 };\n/// CI Cote_D'ivoire\npub const COTE_DIVOIRE: Country = Country { code: *b\"CI\", rev: 0 };\n/// HR Croatia\npub const CROATIA: Country = Country { code: *b\"HR\", rev: 0 };\n/// CU Cuba\npub const CUBA: Country = Country { code: *b\"CU\", rev: 0 };\n/// CY Cyprus\npub const CYPRUS: Country = Country { code: *b\"CY\", rev: 0 };\n/// CZ Czech_Republic\npub const CZECH_REPUBLIC: Country = Country { code: *b\"CZ\", rev: 0 };\n/// DK Denmark\npub const DENMARK: Country = Country { code: *b\"DK\", rev: 0 };\n/// DJ Djibouti\npub const DJIBOUTI: Country = Country { code: *b\"DJ\", rev: 0 };\n/// DM Dominica\npub const DOMINICA: Country = Country { code: *b\"DM\", rev: 0 };\n/// DO Dominican_Republic\npub const DOMINICAN_REPUBLIC: Country = Country { code: *b\"DO\", rev: 0 };\n/// AU G'Day mate!\npub const DOWN_UNDER: Country = Country { code: *b\"AU\", rev: 0 };\n/// EC Ecuador\npub const ECUADOR: Country = Country { code: *b\"EC\", rev: 0 };\n/// EG Egypt\npub const EGYPT: Country = Country { code: *b\"EG\", rev: 0 };\n/// SV El_Salvador\npub const EL_SALVADOR: Country = Country { code: *b\"SV\", rev: 0 };\n/// GQ Equatorial_Guinea\npub const EQUATORIAL_GUINEA: Country = Country { code: *b\"GQ\", rev: 0 };\n/// ER Eritrea\npub const ERITREA: Country = Country { code: *b\"ER\", rev: 0 };\n/// EE Estonia\npub const ESTONIA: Country = Country { code: *b\"EE\", rev: 0 };\n/// ET Ethiopia\npub const ETHIOPIA: Country = Country { code: *b\"ET\", rev: 0 };\n/// FK Falkland_Islands_(Malvinas)\npub const FALKLAND_ISLANDS_MALVINAS: Country = Country { code: *b\"FK\", rev: 0 };\n/// FO Faroe_Islands\npub const FAROE_ISLANDS: Country = Country { code: *b\"FO\", rev: 0 };\n/// FJ Fiji\npub const FIJI: Country = Country { code: *b\"FJ\", rev: 0 };\n/// FI Finland\npub const FINLAND: Country = Country { code: *b\"FI\", rev: 0 };\n/// FR France\npub const FRANCE: Country = Country { code: *b\"FR\", rev: 0 };\n/// GF French_Guina\npub const FRENCH_GUINA: Country = Country { code: *b\"GF\", rev: 0 };\n/// PF French_Polynesia\npub const FRENCH_POLYNESIA: Country = Country { code: *b\"PF\", rev: 0 };\n/// TF French_Southern_Territories\npub const FRENCH_SOUTHERN_TERRITORIES: Country = Country { code: *b\"TF\", rev: 0 };\n/// GA Gabon\npub const GABON: Country = Country { code: *b\"GA\", rev: 0 };\n/// GM Gambia\npub const GAMBIA: Country = Country { code: *b\"GM\", rev: 0 };\n/// GE Georgia\npub const GEORGIA: Country = Country { code: *b\"GE\", rev: 0 };\n/// DE Germany\npub const GERMANY: Country = Country { code: *b\"DE\", rev: 0 };\n/// E0 European_Wide Revision 895\npub const EUROPEAN_WIDE_REV895: Country = Country { code: *b\"E0\", rev: 895 };\n/// GH Ghana\npub const GHANA: Country = Country { code: *b\"GH\", rev: 0 };\n/// GI Gibraltar\npub const GIBRALTAR: Country = Country { code: *b\"GI\", rev: 0 };\n/// GR Greece\npub const GREECE: Country = Country { code: *b\"GR\", rev: 0 };\n/// GD Grenada\npub const GRENADA: Country = Country { code: *b\"GD\", rev: 0 };\n/// GP Guadeloupe\npub const GUADELOUPE: Country = Country { code: *b\"GP\", rev: 0 };\n/// GU Guam\npub const GUAM: Country = Country { code: *b\"GU\", rev: 0 };\n/// GT Guatemala\npub const GUATEMALA: Country = Country { code: *b\"GT\", rev: 0 };\n/// GG Guernsey\npub const GUERNSEY: Country = Country { code: *b\"GG\", rev: 0 };\n/// GN Guinea\npub const GUINEA: Country = Country { code: *b\"GN\", rev: 0 };\n/// GW Guinea-bissau\npub const GUINEA_BISSAU: Country = Country { code: *b\"GW\", rev: 0 };\n/// GY Guyana\npub const GUYANA: Country = Country { code: *b\"GY\", rev: 0 };\n/// HT Haiti\npub const HAITI: Country = Country { code: *b\"HT\", rev: 0 };\n/// VA Holy_See_(Vatican_City_State)\npub const HOLY_SEE_VATICAN_CITY_STATE: Country = Country { code: *b\"VA\", rev: 0 };\n/// HN Honduras\npub const HONDURAS: Country = Country { code: *b\"HN\", rev: 0 };\n/// HK Hong_Kong\npub const HONG_KONG: Country = Country { code: *b\"HK\", rev: 0 };\n/// HU Hungary\npub const HUNGARY: Country = Country { code: *b\"HU\", rev: 0 };\n/// IS Iceland\npub const ICELAND: Country = Country { code: *b\"IS\", rev: 0 };\n/// IN India\npub const INDIA: Country = Country { code: *b\"IN\", rev: 0 };\n/// ID Indonesia\npub const INDONESIA: Country = Country { code: *b\"ID\", rev: 0 };\n/// IR Iran,_Islamic_Republic_Of\npub const IRAN_ISLAMIC_REPUBLIC_OF: Country = Country { code: *b\"IR\", rev: 0 };\n/// IQ Iraq\npub const IRAQ: Country = Country { code: *b\"IQ\", rev: 0 };\n/// IE Ireland\npub const IRELAND: Country = Country { code: *b\"IE\", rev: 0 };\n/// IL Israel\npub const ISRAEL: Country = Country { code: *b\"IL\", rev: 0 };\n/// IT Italy\npub const ITALY: Country = Country { code: *b\"IT\", rev: 0 };\n/// JM Jamaica\npub const JAMAICA: Country = Country { code: *b\"JM\", rev: 0 };\n/// JP Japan\npub const JAPAN: Country = Country { code: *b\"JP\", rev: 0 };\n/// JE Jersey\npub const JERSEY: Country = Country { code: *b\"JE\", rev: 0 };\n/// JO Jordan\npub const JORDAN: Country = Country { code: *b\"JO\", rev: 0 };\n/// KZ Kazakhstan\npub const KAZAKHSTAN: Country = Country { code: *b\"KZ\", rev: 0 };\n/// KE Kenya\npub const KENYA: Country = Country { code: *b\"KE\", rev: 0 };\n/// KI Kiribati\npub const KIRIBATI: Country = Country { code: *b\"KI\", rev: 0 };\n/// KR Korea,_Republic_Of\npub const KOREA_REPUBLIC_OF: Country = Country { code: *b\"KR\", rev: 1 };\n/// 0A Kosovo\npub const KOSOVO: Country = Country { code: *b\"0A\", rev: 0 };\n/// KW Kuwait\npub const KUWAIT: Country = Country { code: *b\"KW\", rev: 0 };\n/// KG Kyrgyzstan\npub const KYRGYZSTAN: Country = Country { code: *b\"KG\", rev: 0 };\n/// LA Lao_People's_Democratic_Repubic\npub const LAO_PEOPLES_DEMOCRATIC_REPUBIC: Country = Country { code: *b\"LA\", rev: 0 };\n/// LV Latvia\npub const LATVIA: Country = Country { code: *b\"LV\", rev: 0 };\n/// LB Lebanon\npub const LEBANON: Country = Country { code: *b\"LB\", rev: 0 };\n/// LS Lesotho\npub const LESOTHO: Country = Country { code: *b\"LS\", rev: 0 };\n/// LR Liberia\npub const LIBERIA: Country = Country { code: *b\"LR\", rev: 0 };\n/// LY Libyan_Arab_Jamahiriya\npub const LIBYAN_ARAB_JAMAHIRIYA: Country = Country { code: *b\"LY\", rev: 0 };\n/// LI Liechtenstein\npub const LIECHTENSTEIN: Country = Country { code: *b\"LI\", rev: 0 };\n/// LT Lithuania\npub const LITHUANIA: Country = Country { code: *b\"LT\", rev: 0 };\n/// LU Luxembourg\npub const LUXEMBOURG: Country = Country { code: *b\"LU\", rev: 0 };\n/// MO Macao\npub const MACAO: Country = Country { code: *b\"MO\", rev: 0 };\n/// MK Macedonia,_Former_Yugoslav_Republic_Of\npub const MACEDONIA_FORMER_YUGOSLAV_REPUBLIC_OF: Country = Country { code: *b\"MK\", rev: 0 };\n/// MG Madagascar\npub const MADAGASCAR: Country = Country { code: *b\"MG\", rev: 0 };\n/// MW Malawi\npub const MALAWI: Country = Country { code: *b\"MW\", rev: 0 };\n/// MY Malaysia\npub const MALAYSIA: Country = Country { code: *b\"MY\", rev: 0 };\n/// MV Maldives\npub const MALDIVES: Country = Country { code: *b\"MV\", rev: 0 };\n/// ML Mali\npub const MALI: Country = Country { code: *b\"ML\", rev: 0 };\n/// MT Malta\npub const MALTA: Country = Country { code: *b\"MT\", rev: 0 };\n/// IM Man,_Isle_Of\npub const MAN_ISLE_OF: Country = Country { code: *b\"IM\", rev: 0 };\n/// MQ Martinique\npub const MARTINIQUE: Country = Country { code: *b\"MQ\", rev: 0 };\n/// MR Mauritania\npub const MAURITANIA: Country = Country { code: *b\"MR\", rev: 0 };\n/// MU Mauritius\npub const MAURITIUS: Country = Country { code: *b\"MU\", rev: 0 };\n/// YT Mayotte\npub const MAYOTTE: Country = Country { code: *b\"YT\", rev: 0 };\n/// MX Mexico\npub const MEXICO: Country = Country { code: *b\"MX\", rev: 0 };\n/// FM Micronesia,_Federated_States_Of\npub const MICRONESIA_FEDERATED_STATES_OF: Country = Country { code: *b\"FM\", rev: 0 };\n/// MD Moldova,_Republic_Of\npub const MOLDOVA_REPUBLIC_OF: Country = Country { code: *b\"MD\", rev: 0 };\n/// MC Monaco\npub const MONACO: Country = Country { code: *b\"MC\", rev: 0 };\n/// MN Mongolia\npub const MONGOLIA: Country = Country { code: *b\"MN\", rev: 0 };\n/// ME Montenegro\npub const MONTENEGRO: Country = Country { code: *b\"ME\", rev: 0 };\n/// MS Montserrat\npub const MONTSERRAT: Country = Country { code: *b\"MS\", rev: 0 };\n/// MA Morocco\npub const MOROCCO: Country = Country { code: *b\"MA\", rev: 0 };\n/// MZ Mozambique\npub const MOZAMBIQUE: Country = Country { code: *b\"MZ\", rev: 0 };\n/// MM Myanmar\npub const MYANMAR: Country = Country { code: *b\"MM\", rev: 0 };\n/// NA Namibia\npub const NAMIBIA: Country = Country { code: *b\"NA\", rev: 0 };\n/// NR Nauru\npub const NAURU: Country = Country { code: *b\"NR\", rev: 0 };\n/// NP Nepal\npub const NEPAL: Country = Country { code: *b\"NP\", rev: 0 };\n/// NL Netherlands\npub const NETHERLANDS: Country = Country { code: *b\"NL\", rev: 0 };\n/// AN Netherlands_Antilles\npub const NETHERLANDS_ANTILLES: Country = Country { code: *b\"AN\", rev: 0 };\n/// NC New_Caledonia\npub const NEW_CALEDONIA: Country = Country { code: *b\"NC\", rev: 0 };\n/// NZ New_Zealand\npub const NEW_ZEALAND: Country = Country { code: *b\"NZ\", rev: 0 };\n/// NI Nicaragua\npub const NICARAGUA: Country = Country { code: *b\"NI\", rev: 0 };\n/// NE Niger\npub const NIGER: Country = Country { code: *b\"NE\", rev: 0 };\n/// NG Nigeria\npub const NIGERIA: Country = Country { code: *b\"NG\", rev: 0 };\n/// NF Norfolk_Island\npub const NORFOLK_ISLAND: Country = Country { code: *b\"NF\", rev: 0 };\n/// MP Northern_Mariana_Islands\npub const NORTHERN_MARIANA_ISLANDS: Country = Country { code: *b\"MP\", rev: 0 };\n/// NO Norway\npub const NORWAY: Country = Country { code: *b\"NO\", rev: 0 };\n/// OM Oman\npub const OMAN: Country = Country { code: *b\"OM\", rev: 0 };\n/// PK Pakistan\npub const PAKISTAN: Country = Country { code: *b\"PK\", rev: 0 };\n/// PW Palau\npub const PALAU: Country = Country { code: *b\"PW\", rev: 0 };\n/// PA Panama\npub const PANAMA: Country = Country { code: *b\"PA\", rev: 0 };\n/// PG Papua_New_Guinea\npub const PAPUA_NEW_GUINEA: Country = Country { code: *b\"PG\", rev: 0 };\n/// PY Paraguay\npub const PARAGUAY: Country = Country { code: *b\"PY\", rev: 0 };\n/// PE Peru\npub const PERU: Country = Country { code: *b\"PE\", rev: 0 };\n/// PH Philippines\npub const PHILIPPINES: Country = Country { code: *b\"PH\", rev: 0 };\n/// PL Poland\npub const POLAND: Country = Country { code: *b\"PL\", rev: 0 };\n/// PT Portugal\npub const PORTUGAL: Country = Country { code: *b\"PT\", rev: 0 };\n/// PR Pueto_Rico\npub const PUETO_RICO: Country = Country { code: *b\"PR\", rev: 0 };\n/// QA Qatar\npub const QATAR: Country = Country { code: *b\"QA\", rev: 0 };\n/// RE Reunion\npub const REUNION: Country = Country { code: *b\"RE\", rev: 0 };\n/// RO Romania\npub const ROMANIA: Country = Country { code: *b\"RO\", rev: 0 };\n/// RU Russian_Federation\npub const RUSSIAN_FEDERATION: Country = Country { code: *b\"RU\", rev: 0 };\n/// RW Rwanda\npub const RWANDA: Country = Country { code: *b\"RW\", rev: 0 };\n/// KN Saint_Kitts_and_Nevis\npub const SAINT_KITTS_AND_NEVIS: Country = Country { code: *b\"KN\", rev: 0 };\n/// LC Saint_Lucia\npub const SAINT_LUCIA: Country = Country { code: *b\"LC\", rev: 0 };\n/// PM Saint_Pierre_and_Miquelon\npub const SAINT_PIERRE_AND_MIQUELON: Country = Country { code: *b\"PM\", rev: 0 };\n/// VC Saint_Vincent_and_The_Grenadines\npub const SAINT_VINCENT_AND_THE_GRENADINES: Country = Country { code: *b\"VC\", rev: 0 };\n/// WS Samoa\npub const SAMOA: Country = Country { code: *b\"WS\", rev: 0 };\n/// MF Sanit_Martin_/_Sint_Marteen\npub const SANIT_MARTIN_SINT_MARTEEN: Country = Country { code: *b\"MF\", rev: 0 };\n/// ST Sao_Tome_and_Principe\npub const SAO_TOME_AND_PRINCIPE: Country = Country { code: *b\"ST\", rev: 0 };\n/// SA Saudi_Arabia\npub const SAUDI_ARABIA: Country = Country { code: *b\"SA\", rev: 0 };\n/// SN Senegal\npub const SENEGAL: Country = Country { code: *b\"SN\", rev: 0 };\n/// RS Serbia\npub const SERBIA: Country = Country { code: *b\"RS\", rev: 0 };\n/// SC Seychelles\npub const SEYCHELLES: Country = Country { code: *b\"SC\", rev: 0 };\n/// SL Sierra_Leone\npub const SIERRA_LEONE: Country = Country { code: *b\"SL\", rev: 0 };\n/// SG Singapore\npub const SINGAPORE: Country = Country { code: *b\"SG\", rev: 0 };\n/// SK Slovakia\npub const SLOVAKIA: Country = Country { code: *b\"SK\", rev: 0 };\n/// SI Slovenia\npub const SLOVENIA: Country = Country { code: *b\"SI\", rev: 0 };\n/// SB Solomon_Islands\npub const SOLOMON_ISLANDS: Country = Country { code: *b\"SB\", rev: 0 };\n/// SO Somalia\npub const SOMALIA: Country = Country { code: *b\"SO\", rev: 0 };\n/// ZA South_Africa\npub const SOUTH_AFRICA: Country = Country { code: *b\"ZA\", rev: 0 };\n/// ES Spain\npub const SPAIN: Country = Country { code: *b\"ES\", rev: 0 };\n/// LK Sri_Lanka\npub const SRI_LANKA: Country = Country { code: *b\"LK\", rev: 0 };\n/// SR Suriname\npub const SURINAME: Country = Country { code: *b\"SR\", rev: 0 };\n/// SZ Swaziland\npub const SWAZILAND: Country = Country { code: *b\"SZ\", rev: 0 };\n/// SE Sweden\npub const SWEDEN: Country = Country { code: *b\"SE\", rev: 0 };\n/// CH Switzerland\npub const SWITZERLAND: Country = Country { code: *b\"CH\", rev: 0 };\n/// SY Syrian_Arab_Republic\npub const SYRIAN_ARAB_REPUBLIC: Country = Country { code: *b\"SY\", rev: 0 };\n/// TW Taiwan,_Province_Of_China\npub const TAIWAN_PROVINCE_OF_CHINA: Country = Country { code: *b\"TW\", rev: 0 };\n/// TJ Tajikistan\npub const TAJIKISTAN: Country = Country { code: *b\"TJ\", rev: 0 };\n/// TZ Tanzania,_United_Republic_Of\npub const TANZANIA_UNITED_REPUBLIC_OF: Country = Country { code: *b\"TZ\", rev: 0 };\n/// TH Thailand\npub const THAILAND: Country = Country { code: *b\"TH\", rev: 0 };\n/// TG Togo\npub const TOGO: Country = Country { code: *b\"TG\", rev: 0 };\n/// TO Tonga\npub const TONGA: Country = Country { code: *b\"TO\", rev: 0 };\n/// TT Trinidad_and_Tobago\npub const TRINIDAD_AND_TOBAGO: Country = Country { code: *b\"TT\", rev: 0 };\n/// TN Tunisia\npub const TUNISIA: Country = Country { code: *b\"TN\", rev: 0 };\n/// TR Turkey\npub const TURKEY: Country = Country { code: *b\"TR\", rev: 0 };\n/// TM Turkmenistan\npub const TURKMENISTAN: Country = Country { code: *b\"TM\", rev: 0 };\n/// TC Turks_and_Caicos_Islands\npub const TURKS_AND_CAICOS_ISLANDS: Country = Country { code: *b\"TC\", rev: 0 };\n/// TV Tuvalu\npub const TUVALU: Country = Country { code: *b\"TV\", rev: 0 };\n/// UG Uganda\npub const UGANDA: Country = Country { code: *b\"UG\", rev: 0 };\n/// UA Ukraine\npub const UKRAINE: Country = Country { code: *b\"UA\", rev: 0 };\n/// AE United_Arab_Emirates\npub const UNITED_ARAB_EMIRATES: Country = Country { code: *b\"AE\", rev: 0 };\n/// GB United_Kingdom\npub const UNITED_KINGDOM: Country = Country { code: *b\"GB\", rev: 0 };\n/// US United_States\npub const UNITED_STATES: Country = Country { code: *b\"US\", rev: 0 };\n/// US United_States Revision 4\npub const UNITED_STATES_REV4: Country = Country { code: *b\"US\", rev: 4 };\n/// Q1 United_States Revision 931\npub const UNITED_STATES_REV931: Country = Country { code: *b\"Q1\", rev: 931 };\n/// Q2 United_States_(No_DFS)\npub const UNITED_STATES_NO_DFS: Country = Country { code: *b\"Q2\", rev: 0 };\n/// UM United_States_Minor_Outlying_Islands\npub const UNITED_STATES_MINOR_OUTLYING_ISLANDS: Country = Country { code: *b\"UM\", rev: 0 };\n/// UY Uruguay\npub const URUGUAY: Country = Country { code: *b\"UY\", rev: 0 };\n/// UZ Uzbekistan\npub const UZBEKISTAN: Country = Country { code: *b\"UZ\", rev: 0 };\n/// VU Vanuatu\npub const VANUATU: Country = Country { code: *b\"VU\", rev: 0 };\n/// VE Venezuela\npub const VENEZUELA: Country = Country { code: *b\"VE\", rev: 0 };\n/// VN Viet_Nam\npub const VIET_NAM: Country = Country { code: *b\"VN\", rev: 0 };\n/// VG Virgin_Islands,_British\npub const VIRGIN_ISLANDS_BRITISH: Country = Country { code: *b\"VG\", rev: 0 };\n/// VI Virgin_Islands,_U.S.\npub const VIRGIN_ISLANDS_US: Country = Country { code: *b\"VI\", rev: 0 };\n/// WF Wallis_and_Futuna\npub const WALLIS_AND_FUTUNA: Country = Country { code: *b\"WF\", rev: 0 };\n/// 0C West_Bank\npub const WEST_BANK: Country = Country { code: *b\"0C\", rev: 0 };\n/// EH Western_Sahara\npub const WESTERN_SAHARA: Country = Country { code: *b\"EH\", rev: 0 };\n/// Worldwide Locale Revision 983\npub const WORLD_WIDE_XV_REV983: Country = Country { code: *b\"XV\", rev: 983 };\n/// Worldwide Locale (passive Ch12-14)\npub const WORLD_WIDE_XX: Country = Country { code: *b\"XX\", rev: 0 };\n/// Worldwide Locale (passive Ch12-14) Revision 17\npub const WORLD_WIDE_XX_REV17: Country = Country { code: *b\"XX\", rev: 17 };\n/// YE Yemen\npub const YEMEN: Country = Country { code: *b\"YE\", rev: 0 };\n/// ZM Zambia\npub const ZAMBIA: Country = Country { code: *b\"ZM\", rev: 0 };\n/// ZW Zimbabwe\npub const ZIMBABWE: Country = Country { code: *b\"ZW\", rev: 0 };\n"
  },
  {
    "path": "cyw43/src/events.rs",
    "content": "#![allow(dead_code)]\n#![allow(non_camel_case_types)]\n\nuse core::cell::RefCell;\n\nuse embassy_sync::blocking_mutex::raw::NoopRawMutex;\nuse embassy_sync::pubsub::{PubSubChannel, Subscriber};\n\nuse crate::structs::BssInfo;\n\ncrate::util::enum_from_u8! {\n    #[derive(Debug, Clone, Copy, PartialEq, Eq)]\n    #[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\n    enum Event {\n        #[default]\n        Unknown = 0xFF,\n        /// indicates status of set SSID\n        SET_SSID = 0,\n        /// differentiates join IBSS from found (START) IBSS\n        JOIN = 1,\n        /// STA founded an IBSS or AP started a BSS\n        START = 2,\n        /// 802.11 AUTH request\n        AUTH = 3,\n        /// 802.11 AUTH indication\n        AUTH_IND = 4,\n        /// 802.11 DEAUTH request\n        DEAUTH = 5,\n        /// 802.11 DEAUTH indication\n        DEAUTH_IND = 6,\n        /// 802.11 ASSOC request\n        ASSOC = 7,\n        /// 802.11 ASSOC indication\n        ASSOC_IND = 8,\n        /// 802.11 REASSOC request\n        REASSOC = 9,\n        /// 802.11 REASSOC indication\n        REASSOC_IND = 10,\n        /// 802.11 DISASSOC request\n        DISASSOC = 11,\n        /// 802.11 DISASSOC indication\n        DISASSOC_IND = 12,\n        /// 802.11h Quiet period started\n        QUIET_START = 13,\n        /// 802.11h Quiet period ended\n        QUIET_END = 14,\n        /// BEACONS received/lost indication\n        BEACON_RX = 15,\n        /// generic link indication\n        LINK = 16,\n        /// TKIP MIC error occurred\n        MIC_ERROR = 17,\n        /// NDIS style link indication\n        NDIS_LINK = 18,\n        /// roam attempt occurred: indicate status & reason\n        ROAM = 19,\n        /// change in dot11FailedCount (txfail)\n        TXFAIL = 20,\n        /// WPA2 pmkid cache indication\n        PMKID_CACHE = 21,\n        /// current AP's TSF value went backward\n        RETROGRADE_TSF = 22,\n        /// AP was pruned from join list for reason\n        PRUNE = 23,\n        /// report AutoAuth table entry match for join attempt\n        AUTOAUTH = 24,\n        /// Event encapsulating an EAPOL message\n        EAPOL_MSG = 25,\n        /// Scan results are ready or scan was aborted\n        SCAN_COMPLETE = 26,\n        /// indicate to host addts fail/success\n        ADDTS_IND = 27,\n        /// indicate to host delts fail/success\n        DELTS_IND = 28,\n        /// indicate to host of beacon transmit\n        BCNSENT_IND = 29,\n        /// Send the received beacon up to the host\n        BCNRX_MSG = 30,\n        /// indicate to host loss of beacon\n        BCNLOST_MSG = 31,\n        /// before attempting to roam\n        ROAM_PREP = 32,\n        /// PFN network found event\n        PFN_NET_FOUND = 33,\n        /// PFN network lost event\n        PFN_NET_LOST = 34,\n        RESET_COMPLETE = 35,\n        JOIN_START = 36,\n        ROAM_START = 37,\n        ASSOC_START = 38,\n        IBSS_ASSOC = 39,\n        RADIO = 40,\n        /// PSM microcode watchdog fired\n        PSM_WATCHDOG = 41,\n        /// CCX association start\n        CCX_ASSOC_START = 42,\n        /// CCX association abort\n        CCX_ASSOC_ABORT = 43,\n        /// probe request received\n        PROBREQ_MSG = 44,\n        SCAN_CONFIRM_IND = 45,\n        /// WPA Handshake\n        PSK_SUP = 46,\n        COUNTRY_CODE_CHANGED = 47,\n        /// WMMAC excedded medium time\n        EXCEEDED_MEDIUM_TIME = 48,\n        /// WEP ICV error occurred\n        ICV_ERROR = 49,\n        /// Unsupported unicast encrypted frame\n        UNICAST_DECODE_ERROR = 50,\n        /// Unsupported multicast encrypted frame\n        MULTICAST_DECODE_ERROR = 51,\n        TRACE = 52,\n        /// BT-AMP HCI event\n        BTA_HCI_EVENT = 53,\n        /// I/F change (for wlan host notification)\n        IF = 54,\n        /// P2P Discovery listen state expires\n        P2P_DISC_LISTEN_COMPLETE = 55,\n        /// indicate RSSI change based on configured levels\n        RSSI = 56,\n        /// PFN best network batching event\n        PFN_BEST_BATCHING = 57,\n        EXTLOG_MSG = 58,\n        /// Action frame reception\n        ACTION_FRAME = 59,\n        /// Action frame Tx complete\n        ACTION_FRAME_COMPLETE = 60,\n        /// assoc request received\n        PRE_ASSOC_IND = 61,\n        /// re-assoc request received\n        PRE_REASSOC_IND = 62,\n        /// channel adopted (xxx: obsoleted)\n        CHANNEL_ADOPTED = 63,\n        /// AP started\n        AP_STARTED = 64,\n        /// AP stopped due to DFS\n        DFS_AP_STOP = 65,\n        /// AP resumed due to DFS\n        DFS_AP_RESUME = 66,\n        /// WAI stations event\n        WAI_STA_EVENT = 67,\n        /// event encapsulating an WAI message\n        WAI_MSG = 68,\n        /// escan result event\n        ESCAN_RESULT = 69,\n        /// action frame off channel complete\n        ACTION_FRAME_OFF_CHAN_COMPLETE = 70,\n        /// probe response received\n        PROBRESP_MSG = 71,\n        /// P2P Probe request received\n        P2P_PROBREQ_MSG = 72,\n        DCS_REQUEST = 73,\n        /// credits for D11 FIFOs. [AC0,AC1,AC2,AC3,BC_MC,ATIM]\n        FIFO_CREDIT_MAP = 74,\n        /// Received action frame event WITH wl_event_rx_frame_data_t header\n        ACTION_FRAME_RX = 75,\n        /// Wake Event timer fired, used for wake WLAN test mode\n        WAKE_EVENT = 76,\n        /// Radio measurement complete\n        RM_COMPLETE = 77,\n        /// Synchronize TSF with the host\n        HTSFSYNC = 78,\n        /// request an overlay IOCTL/iovar from the host\n        OVERLAY_REQ = 79,\n        CSA_COMPLETE_IND = 80,\n        /// excess PM Wake Event to inform host\n        EXCESS_PM_WAKE_EVENT = 81,\n        /// no PFN networks around\n        PFN_SCAN_NONE = 82,\n        /// last found PFN network gets lost\n        PFN_SCAN_ALLGONE = 83,\n        GTK_PLUMBED = 84,\n        /// 802.11 ASSOC indication for NDIS only\n        ASSOC_IND_NDIS = 85,\n        /// 802.11 REASSOC indication for NDIS only\n        REASSOC_IND_NDIS = 86,\n        ASSOC_REQ_IE = 87,\n        ASSOC_RESP_IE = 88,\n        /// association recreated on resume\n        ASSOC_RECREATED = 89,\n        /// rx action frame event for NDIS only\n        ACTION_FRAME_RX_NDIS = 90,\n        /// authentication request received\n        AUTH_REQ = 91,\n        /// fast assoc recreation failed\n        SPEEDY_RECREATE_FAIL = 93,\n        /// port-specific event and payload (e.g. NDIS)\n        NATIVE = 94,\n        /// event for tx pkt delay suddently jump\n        PKTDELAY_IND = 95,\n        /// AWDL AW period starts\n        AWDL_AW = 96,\n        /// AWDL Master/Slave/NE master role event\n        AWDL_ROLE = 97,\n        /// Generic AWDL event\n        AWDL_EVENT = 98,\n        /// NIC AF txstatus\n        NIC_AF_TXS = 99,\n        /// NAN event\n        NAN = 100,\n        BEACON_FRAME_RX = 101,\n        /// desired service found\n        SERVICE_FOUND = 102,\n        /// GAS fragment received\n        GAS_FRAGMENT_RX = 103,\n        /// GAS sessions all complete\n        GAS_COMPLETE = 104,\n        /// New device found by p2p offload\n        P2PO_ADD_DEVICE = 105,\n        /// device has been removed by p2p offload\n        P2PO_DEL_DEVICE = 106,\n        /// WNM event to notify STA enter sleep mode\n        WNM_STA_SLEEP = 107,\n        /// Indication of MAC tx failures (exhaustion of 802.11 retries) exceeding threshold(s)\n        TXFAIL_THRESH = 108,\n        /// Proximity Detection event\n        PROXD = 109,\n        /// AWDL RX Probe response\n        AWDL_RX_PRB_RESP = 111,\n        /// AWDL RX Action Frames\n        AWDL_RX_ACT_FRAME = 112,\n        /// AWDL Wowl nulls\n        AWDL_WOWL_NULLPKT = 113,\n        /// AWDL Phycal status\n        AWDL_PHYCAL_STATUS = 114,\n        /// AWDL OOB AF status\n        AWDL_OOB_AF_STATUS = 115,\n        /// Interleaved Scan status\n        AWDL_SCAN_STATUS = 116,\n        /// AWDL AW Start\n        AWDL_AW_START = 117,\n        /// AWDL AW End\n        AWDL_AW_END = 118,\n        /// AWDL AW Extensions\n        AWDL_AW_EXT = 119,\n        AWDL_PEER_CACHE_CONTROL = 120,\n        CSA_START_IND = 121,\n        CSA_DONE_IND = 122,\n        CSA_FAILURE_IND = 123,\n        /// CCA based channel quality report\n        CCA_CHAN_QUAL = 124,\n        /// to report change in BSSID while roaming\n        BSSID = 125,\n        /// tx error indication\n        TX_STAT_ERROR = 126,\n        /// credit check for BCMC supported\n        BCMC_CREDIT_SUPPORT = 127,\n        /// psta primary interface indication\n        PSTA_PRIMARY_INTF_IND = 128,\n        /// Handover Request Initiated\n        BT_WIFI_HANDOVER_REQ = 130,\n        /// Southpaw TxInhibit notification\n        SPW_TXINHIBIT = 131,\n        /// FBT Authentication Request Indication\n        FBT_AUTH_REQ_IND = 132,\n        /// Enhancement addition for RSSI\n        RSSI_LQM = 133,\n        /// Full probe/beacon (IEs etc) results\n        PFN_GSCAN_FULL_RESULT = 134,\n        /// Significant change in rssi of bssids being tracked\n        PFN_SWC = 135,\n        /// a STA been authroized for traffic\n        AUTHORIZED = 136,\n        /// probe req with wl_event_rx_frame_data_t header\n        PROBREQ_MSG_RX = 137,\n        /// PFN completed scan of network list\n        PFN_SCAN_COMPLETE = 138,\n        /// RMC Event\n        RMC_EVENT = 139,\n        /// DPSTA interface indication\n        DPSTA_INTF_IND = 140,\n        /// RRM Event\n        RRM = 141,\n        /// ULP entry event\n        ULP = 146,\n        /// TCP Keep Alive Offload Event\n        TKO = 151,\n        /// authentication request received\n        EXT_AUTH_REQ = 187,\n        /// authentication request received\n        EXT_AUTH_FRAME_RX = 188,\n        /// mgmt frame Tx complete\n        MGMT_FRAME_TXSTATUS = 189,\n        /// highest val + 1 for range checking\n        LAST = 190,\n    }\n}\n\n// TODO this PubSub can probably be replaced with shared memory to make it a bit more efficient.\npub type EventQueue = PubSubChannel<NoopRawMutex, Message, 2, 1, 1>;\npub type EventSubscriber<'a> = Subscriber<'a, NoopRawMutex, Message, 2, 1, 1>;\n\npub struct Events {\n    pub queue: EventQueue,\n    pub mask: SharedEventMask,\n}\n\nimpl Events {\n    pub const fn new() -> Self {\n        Self {\n            queue: EventQueue::new(),\n            mask: SharedEventMask::new(),\n        }\n    }\n}\n\n#[derive(Clone, Copy)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub struct Status {\n    pub event_type: Event,\n    pub status: u32,\n}\n\n#[derive(Copy, Clone)]\npub enum Payload {\n    None,\n    BssInfo(BssInfo),\n}\n\n#[derive(Copy, Clone)]\n\npub struct Message {\n    pub header: Status,\n    pub payload: Payload,\n}\n\nimpl Message {\n    pub fn new(status: Status, payload: Payload) -> Self {\n        Self {\n            header: status,\n            payload,\n        }\n    }\n}\n\nstruct EventMask {\n    mask: [u32; Self::WORD_COUNT],\n}\n\nimpl EventMask {\n    const WORD_COUNT: usize = ((Event::LAST as u32 + (u32::BITS - 1)) / u32::BITS) as usize;\n\n    const fn new() -> Self {\n        Self {\n            mask: [0; Self::WORD_COUNT],\n        }\n    }\n\n    fn enable(&mut self, event: Event) {\n        let n = event as u32;\n        let word = n / u32::BITS;\n        let bit = n % u32::BITS;\n\n        self.mask[word as usize] |= 1 << bit;\n    }\n\n    fn disable(&mut self, event: Event) {\n        let n = event as u32;\n        let word = n / u32::BITS;\n        let bit = n % u32::BITS;\n\n        self.mask[word as usize] &= !(1 << bit);\n    }\n\n    fn is_enabled(&self, event: Event) -> bool {\n        let n = event as u32;\n        let word = n / u32::BITS;\n        let bit = n % u32::BITS;\n\n        self.mask[word as usize] & (1 << bit) > 0\n    }\n}\n\npub struct SharedEventMask {\n    mask: RefCell<EventMask>,\n}\n\nimpl SharedEventMask {\n    pub const fn new() -> Self {\n        Self {\n            mask: RefCell::new(EventMask::new()),\n        }\n    }\n\n    pub fn enable(&self, events: &[Event]) {\n        let mut mask = self.mask.borrow_mut();\n        for event in events {\n            mask.enable(*event);\n        }\n    }\n\n    #[allow(dead_code)]\n    pub fn disable(&self, events: &[Event]) {\n        let mut mask = self.mask.borrow_mut();\n        for event in events {\n            mask.disable(*event);\n        }\n    }\n\n    pub fn disable_all(&self) {\n        let mut mask = self.mask.borrow_mut();\n        mask.mask = Default::default();\n    }\n\n    pub fn is_enabled(&self, event: Event) -> bool {\n        let mask = self.mask.borrow();\n        mask.is_enabled(event)\n    }\n}\n\nimpl Default for SharedEventMask {\n    fn default() -> Self {\n        Self::new()\n    }\n}\n"
  },
  {
    "path": "cyw43/src/fmt.rs",
    "content": "#![macro_use]\n#![allow(unused)]\n\nuse core::fmt::{Debug, Display, LowerHex};\n\n#[cfg(all(feature = \"defmt\", feature = \"log\"))]\ncompile_error!(\"You may not enable both `defmt` and `log` features.\");\n\n#[collapse_debuginfo(yes)]\nmacro_rules! assert {\n    ($($x:tt)*) => {\n        {\n            #[cfg(not(feature = \"defmt\"))]\n            ::core::assert!($($x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::assert!($($x)*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! assert_eq {\n    ($($x:tt)*) => {\n        {\n            #[cfg(not(feature = \"defmt\"))]\n            ::core::assert_eq!($($x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::assert_eq!($($x)*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! assert_ne {\n    ($($x:tt)*) => {\n        {\n            #[cfg(not(feature = \"defmt\"))]\n            ::core::assert_ne!($($x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::assert_ne!($($x)*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! debug_assert {\n    ($($x:tt)*) => {\n        {\n            #[cfg(not(feature = \"defmt\"))]\n            ::core::debug_assert!($($x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::debug_assert!($($x)*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! debug_assert_eq {\n    ($($x:tt)*) => {\n        {\n            #[cfg(not(feature = \"defmt\"))]\n            ::core::debug_assert_eq!($($x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::debug_assert_eq!($($x)*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! debug_assert_ne {\n    ($($x:tt)*) => {\n        {\n            #[cfg(not(feature = \"defmt\"))]\n            ::core::debug_assert_ne!($($x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::debug_assert_ne!($($x)*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! todo {\n    ($($x:tt)*) => {\n        {\n            #[cfg(not(feature = \"defmt\"))]\n            ::core::todo!($($x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::todo!($($x)*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! unreachable {\n    ($($x:tt)*) => {\n        {\n            #[cfg(not(feature = \"defmt\"))]\n            ::core::unreachable!($($x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::unreachable!($($x)*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! panic {\n    ($($x:tt)*) => {\n        {\n            #[cfg(not(feature = \"defmt\"))]\n            ::core::panic!($($x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::panic!($($x)*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! trace {\n    ($s:literal $(, $x:expr)* $(,)?) => {\n        {\n            #[cfg(feature = \"log\")]\n            ::log::trace!($s $(, $x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::trace!($s $(, $x)*);\n            #[cfg(not(any(feature = \"log\", feature=\"defmt\")))]\n            let _ = ($( & $x ),*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! debug {\n    ($s:literal $(, $x:expr)* $(,)?) => {\n        {\n            #[cfg(feature = \"log\")]\n            ::log::debug!($s $(, $x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::debug!($s $(, $x)*);\n            #[cfg(not(any(feature = \"log\", feature=\"defmt\")))]\n            let _ = ($( & $x ),*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! info {\n    ($s:literal $(, $x:expr)* $(,)?) => {\n        {\n            #[cfg(feature = \"log\")]\n            ::log::info!($s $(, $x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::info!($s $(, $x)*);\n            #[cfg(not(any(feature = \"log\", feature=\"defmt\")))]\n            let _ = ($( & $x ),*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! warn {\n    ($s:literal $(, $x:expr)* $(,)?) => {\n        {\n            #[cfg(feature = \"log\")]\n            ::log::warn!($s $(, $x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::warn!($s $(, $x)*);\n            #[cfg(not(any(feature = \"log\", feature=\"defmt\")))]\n            let _ = ($( & $x ),*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! error {\n    ($s:literal $(, $x:expr)* $(,)?) => {\n        {\n            #[cfg(feature = \"log\")]\n            ::log::error!($s $(, $x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::error!($s $(, $x)*);\n            #[cfg(not(any(feature = \"log\", feature=\"defmt\")))]\n            let _ = ($( & $x ),*);\n        }\n    };\n}\n\n#[cfg(feature = \"defmt\")]\n#[collapse_debuginfo(yes)]\nmacro_rules! unwrap {\n    ($($x:tt)*) => {\n        ::defmt::unwrap!($($x)*)\n    };\n}\n\n#[cfg(not(feature = \"defmt\"))]\n#[collapse_debuginfo(yes)]\nmacro_rules! unwrap {\n    ($arg:expr) => {\n        match $crate::fmt::Try::into_result($arg) {\n            ::core::result::Result::Ok(t) => t,\n            ::core::result::Result::Err(e) => {\n                ::core::panic!(\"unwrap of `{}` failed: {:?}\", ::core::stringify!($arg), e);\n            }\n        }\n    };\n    ($arg:expr, $($msg:expr),+ $(,)? ) => {\n        match $crate::fmt::Try::into_result($arg) {\n            ::core::result::Result::Ok(t) => t,\n            ::core::result::Result::Err(e) => {\n                ::core::panic!(\"unwrap of `{}` failed: {}: {:?}\", ::core::stringify!($arg), ::core::format_args!($($msg,)*), e);\n            }\n        }\n    }\n}\n\n#[derive(Debug, Copy, Clone, Eq, PartialEq)]\npub struct NoneError;\n\npub trait Try {\n    type Ok;\n    type Error;\n    fn into_result(self) -> Result<Self::Ok, Self::Error>;\n}\n\nimpl<T> Try for Option<T> {\n    type Ok = T;\n    type Error = NoneError;\n\n    #[inline]\n    fn into_result(self) -> Result<T, NoneError> {\n        self.ok_or(NoneError)\n    }\n}\n\nimpl<T, E> Try for Result<T, E> {\n    type Ok = T;\n    type Error = E;\n\n    #[inline]\n    fn into_result(self) -> Self {\n        self\n    }\n}\n\npub(crate) struct Bytes<'a>(pub &'a [u8]);\n\nimpl<'a> Debug for Bytes<'a> {\n    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {\n        write!(f, \"{:#02x?}\", self.0)\n    }\n}\n\nimpl<'a> Display for Bytes<'a> {\n    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {\n        write!(f, \"{:#02x?}\", self.0)\n    }\n}\n\nimpl<'a> LowerHex for Bytes<'a> {\n    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {\n        write!(f, \"{:#02x?}\", self.0)\n    }\n}\n\n#[cfg(feature = \"defmt\")]\nimpl<'a> defmt::Format for Bytes<'a> {\n    fn format(&self, fmt: defmt::Formatter) {\n        defmt::write!(fmt, \"{:02x}\", self.0)\n    }\n}\n"
  },
  {
    "path": "cyw43/src/ioctl.rs",
    "content": "use core::cell::{Cell, RefCell};\nuse core::future::{Future, poll_fn};\nuse core::task::{Poll, Waker};\n\nuse embassy_sync::waitqueue::WakerRegistration;\n\nuse crate::consts::Ioctl;\nuse crate::fmt::Bytes;\n\n#[derive(Clone, Copy, PartialEq, Eq)]\npub enum IoctlType {\n    Get = 0,\n    Set = 2,\n}\n\n#[derive(Clone, Copy)]\npub struct PendingIoctl {\n    pub buf: *mut [u8],\n    pub kind: IoctlType,\n    pub cmd: Ioctl,\n    pub iface: u32,\n}\n\n#[derive(Clone, Copy)]\nenum IoctlStateInner {\n    Pending(PendingIoctl),\n    Sent { buf: *mut [u8] },\n    Done { resp_len: usize },\n}\n\nstruct Wakers {\n    control: WakerRegistration,\n    runner: WakerRegistration,\n}\n\nimpl Wakers {\n    const fn new() -> Self {\n        Self {\n            control: WakerRegistration::new(),\n            runner: WakerRegistration::new(),\n        }\n    }\n}\n\npub struct IoctlState {\n    state: Cell<IoctlStateInner>,\n    wakers: RefCell<Wakers>,\n}\n\nimpl IoctlState {\n    pub const fn new() -> Self {\n        Self {\n            state: Cell::new(IoctlStateInner::Done { resp_len: 0 }),\n            wakers: RefCell::new(Wakers::new()),\n        }\n    }\n\n    fn wake_control(&self) {\n        self.wakers.borrow_mut().control.wake();\n    }\n\n    fn register_control(&self, waker: &Waker) {\n        self.wakers.borrow_mut().control.register(waker);\n    }\n\n    fn wake_runner(&self) {\n        self.wakers.borrow_mut().runner.wake();\n    }\n\n    fn register_runner(&self, waker: &Waker) {\n        self.wakers.borrow_mut().runner.register(waker);\n    }\n\n    pub fn wait_complete(&self) -> impl Future<Output = usize> + '_ {\n        poll_fn(|cx| {\n            if let IoctlStateInner::Done { resp_len } = self.state.get() {\n                Poll::Ready(resp_len)\n            } else {\n                self.register_control(cx.waker());\n                Poll::Pending\n            }\n        })\n    }\n\n    pub fn wait_pending(&self) -> impl Future<Output = PendingIoctl> + '_ {\n        poll_fn(|cx| {\n            if let IoctlStateInner::Pending(pending) = self.state.get() {\n                self.state.set(IoctlStateInner::Sent { buf: pending.buf });\n                Poll::Ready(pending)\n            } else {\n                self.register_runner(cx.waker());\n                Poll::Pending\n            }\n        })\n    }\n\n    pub fn cancel_ioctl(&self) {\n        self.state.set(IoctlStateInner::Done { resp_len: 0 });\n    }\n\n    pub async fn do_ioctl(&self, kind: IoctlType, cmd: Ioctl, iface: u32, buf: &mut [u8]) -> usize {\n        self.state\n            .set(IoctlStateInner::Pending(PendingIoctl { buf, kind, cmd, iface }));\n        self.wake_runner();\n        self.wait_complete().await\n    }\n\n    pub fn ioctl_done(&self, response: &[u8]) {\n        if let IoctlStateInner::Sent { buf } = self.state.get() {\n            trace!(\"IOCTL Response: {:02x}\", Bytes(response));\n\n            // TODO fix this\n            (unsafe { &mut *buf }[..response.len()]).copy_from_slice(response);\n\n            self.state.set(IoctlStateInner::Done {\n                resp_len: response.len(),\n            });\n            self.wake_control();\n        } else {\n            warn!(\"IOCTL Response but no pending Ioctl\");\n        }\n    }\n}\n"
  },
  {
    "path": "cyw43/src/lib.rs",
    "content": "#![no_std]\n#![no_main]\n#![allow(async_fn_in_trait)]\n#![allow(unsafe_op_in_unsafe_fn)]\n#![deny(unused_must_use)]\n#![doc = include_str!(\"../README.md\")]\n#![warn(missing_docs)]\n\n// This mod MUST go first, so that the others see its macros.\npub(crate) mod fmt;\n\n#[cfg(feature = \"bluetooth\")]\n/// Bluetooth module.\npub mod bluetooth;\nmod consts;\nmod control;\nmod countries;\nmod events;\nmod ioctl;\nmod runner;\nmod sdio;\nmod spi;\nmod structs;\nmod util;\n\nuse core::sync::atomic::AtomicBool;\n\npub use aligned::{A4, Aligned};\nuse embassy_net_driver_channel as ch;\nuse embedded_hal_1::digital::OutputPin;\nuse events::Events;\nuse ioctl::IoctlState;\n\npub use crate::control::{\n    AddMulticastAddressError, Control, JoinAuth, JoinError, JoinOptions, ScanOptions, ScanType, Scanner,\n};\npub use crate::runner::Runner;\npub use crate::sdio::{SdioBus, SdioBusCyw43};\npub use crate::spi::{SpiBus, SpiBusCyw43};\npub use crate::structs::BssInfo;\n\nconst MTU: usize = 1514;\n\n#[allow(unused)]\n#[derive(Clone, Copy, PartialEq, Eq)]\nenum Core {\n    WLAN = 0,\n    SOCSRAM = 1,\n    SDIOD = 2,\n}\n\nimpl Core {\n    fn base_addr(&self) -> u32 {\n        match self {\n            Self::WLAN => CHIP.arm_core_base_address,\n            Self::SOCSRAM => CHIP.socsram_wrapper_base_address,\n            Self::SDIOD => CHIP.sdiod_core_base_address,\n        }\n    }\n}\n\n#[allow(unused)]\nstruct Chip {\n    arm_core_base_address: u32,\n    socsram_base_address: u32,\n    bluetooth_base_address: u32,\n    socsram_wrapper_base_address: u32,\n    sdiod_core_base_address: u32,\n    pmu_base_address: u32,\n    chip_ram_size: u32,\n    atcm_ram_base_address: u32,\n    socram_srmem_size: u32,\n    chanspec_band_mask: u32,\n    chanspec_band_2g: u32,\n    chanspec_band_5g: u32,\n    chanspec_band_shift: u32,\n    chanspec_bw_10: u32,\n    chanspec_bw_20: u32,\n    chanspec_bw_40: u32,\n    chanspec_bw_mask: u32,\n    chanspec_bw_shift: u32,\n    chanspec_ctl_sb_lower: u32,\n    chanspec_ctl_sb_upper: u32,\n    chanspec_ctl_sb_none: u32,\n    chanspec_ctl_sb_mask: u32,\n}\n\nconst WRAPPER_REGISTER_OFFSET: u32 = 0x100000;\n\n// Data for CYW43439\nconst CHIP: Chip = Chip {\n    arm_core_base_address: 0x18003000 + WRAPPER_REGISTER_OFFSET,\n    socsram_base_address: 0x18004000,\n    bluetooth_base_address: 0x19000000,\n    socsram_wrapper_base_address: 0x18004000 + WRAPPER_REGISTER_OFFSET,\n    sdiod_core_base_address: 0x18002000,\n    pmu_base_address: 0x18000000,\n    chip_ram_size: 512 * 1024,\n    atcm_ram_base_address: 0,\n    socram_srmem_size: 64 * 1024,\n    chanspec_band_mask: 0xc000,\n    chanspec_band_2g: 0x0000,\n    chanspec_band_5g: 0xc000,\n    chanspec_band_shift: 14,\n    chanspec_bw_10: 0x0800,\n    chanspec_bw_20: 0x1000,\n    chanspec_bw_40: 0x1800,\n    chanspec_bw_mask: 0x3800,\n    chanspec_bw_shift: 11,\n    chanspec_ctl_sb_lower: 0x0000,\n    chanspec_ctl_sb_upper: 0x0100,\n    chanspec_ctl_sb_none: 0x0000,\n    chanspec_ctl_sb_mask: 0x0700,\n};\n\n/// Driver state.\npub struct State {\n    ioctl_state: IoctlState,\n    net: NetState,\n    #[cfg(feature = \"bluetooth\")]\n    bt: bluetooth::BtState,\n}\n\nstruct NetState {\n    ch: ch::State<MTU, 4, 4>,\n    events: Events,\n    secure_network: AtomicBool,\n}\n\nimpl State {\n    /// Create new driver state holder.\n    pub const fn new() -> Self {\n        Self {\n            ioctl_state: IoctlState::new(),\n            net: NetState {\n                ch: ch::State::new(),\n                events: Events::new(),\n                secure_network: AtomicBool::new(false),\n            },\n            #[cfg(feature = \"bluetooth\")]\n            bt: bluetooth::BtState::new(),\n        }\n    }\n}\n\n/// Power management modes.\n#[derive(Debug, Clone, Copy, PartialEq, Eq)]\npub enum PowerManagementMode {\n    /// Custom, officially unsupported mode. Use at your own risk.\n    /// All power-saving features set to their max at only a marginal decrease in power consumption\n    /// as oppposed to `Aggressive`.\n    SuperSave,\n\n    /// Aggressive power saving mode.\n    Aggressive,\n\n    /// The default mode.\n    PowerSave,\n\n    /// Performance is prefered over power consumption but still some power is conserved as opposed to\n    /// `None`.\n    Performance,\n\n    /// Unlike all the other PM modes, this lowers the power consumption at all times at the cost of\n    /// a much lower throughput.\n    ThroughputThrottling,\n\n    /// No power management is configured. This consumes the most power.\n    None,\n}\n\nimpl Default for PowerManagementMode {\n    fn default() -> Self {\n        Self::PowerSave\n    }\n}\n\nimpl PowerManagementMode {\n    fn sleep_ret_ms(&self) -> u16 {\n        match self {\n            PowerManagementMode::SuperSave => 2000,\n            PowerManagementMode::Aggressive => 2000,\n            PowerManagementMode::PowerSave => 200,\n            PowerManagementMode::Performance => 20,\n            PowerManagementMode::ThroughputThrottling => 0, // value doesn't matter\n            PowerManagementMode::None => 0,                 // value doesn't matter\n        }\n    }\n\n    fn beacon_period(&self) -> u8 {\n        match self {\n            PowerManagementMode::SuperSave => 255,\n            PowerManagementMode::Aggressive => 1,\n            PowerManagementMode::PowerSave => 1,\n            PowerManagementMode::Performance => 1,\n            PowerManagementMode::ThroughputThrottling => 0, // value doesn't matter\n            PowerManagementMode::None => 0,                 // value doesn't matter\n        }\n    }\n\n    fn dtim_period(&self) -> u8 {\n        match self {\n            PowerManagementMode::SuperSave => 255,\n            PowerManagementMode::Aggressive => 1,\n            PowerManagementMode::PowerSave => 1,\n            PowerManagementMode::Performance => 1,\n            PowerManagementMode::ThroughputThrottling => 0, // value doesn't matter\n            PowerManagementMode::None => 0,                 // value doesn't matter\n        }\n    }\n\n    fn assoc(&self) -> u8 {\n        match self {\n            PowerManagementMode::SuperSave => 255,\n            PowerManagementMode::Aggressive => 10,\n            PowerManagementMode::PowerSave => 10,\n            PowerManagementMode::Performance => 1,\n            PowerManagementMode::ThroughputThrottling => 0, // value doesn't matter\n            PowerManagementMode::None => 0,                 // value doesn't matter\n        }\n    }\n\n    fn mode(&self) -> u32 {\n        match self {\n            PowerManagementMode::ThroughputThrottling => 1,\n            PowerManagementMode::None => 0,\n            _ => 2,\n        }\n    }\n}\n\n/// Embassy-net driver.\npub type NetDriver<'a> = ch::Device<'a, MTU>;\n\n/// Create a new instance of the CYW43 driver.\n///\n/// Returns a handle to the network device, control handle and a runner for driving the low level\n/// stack.\npub async fn new<'a, PWR, SPI>(\n    state: &'a mut State,\n    pwr: PWR,\n    spi: SPI,\n    firmware: &Aligned<A4, [u8]>,\n    nvram: &Aligned<A4, [u8]>,\n) -> (NetDriver<'a>, Control<'a>, Runner<'a, SpiBus<PWR, SPI>>)\nwhere\n    PWR: OutputPin,\n    SPI: SpiBusCyw43,\n{\n    let (ch_runner, device) = ch::new(&mut state.net.ch, ch::driver::HardwareAddress::Ethernet([0; 6]));\n    let state_ch = ch_runner.state_runner();\n\n    let mut runner = Runner::new(\n        ch_runner,\n        SpiBus::new(pwr, spi),\n        &state.ioctl_state,\n        &state.net.events,\n        &state.net.secure_network,\n        #[cfg(feature = \"bluetooth\")]\n        None,\n    );\n\n    runner.init(firmware, nvram, None).await.unwrap();\n    let control = Control::new(\n        state_ch,\n        &state.net.events,\n        &state.ioctl_state,\n        &state.net.secure_network,\n    );\n\n    (device, control, runner)\n}\n\n/// Create a new instance of the CYW43 driver.\n///\n/// Returns a handle to the network device, control handle and a runner for driving the low level\n/// stack.\npub async fn new_sdio<'a, SDIO>(\n    state: &'a mut State,\n    sdio: SDIO,\n    firmware: &Aligned<A4, [u8]>,\n    nvram: &Aligned<A4, [u8]>,\n) -> (NetDriver<'a>, Control<'a>, Runner<'a, SdioBus<SDIO>>)\nwhere\n    SDIO: SdioBusCyw43<64>,\n{\n    let (ch_runner, device) = ch::new(&mut state.net.ch, ch::driver::HardwareAddress::Ethernet([0; 6]));\n    let state_ch = ch_runner.state_runner();\n\n    let mut runner = Runner::new(\n        ch_runner,\n        SdioBus::new(sdio),\n        &state.ioctl_state,\n        &state.net.events,\n        &state.net.secure_network,\n        #[cfg(feature = \"bluetooth\")]\n        None,\n    );\n\n    runner.init(firmware, nvram, None).await.unwrap();\n    let control = Control::new(\n        state_ch,\n        &state.net.events,\n        &state.ioctl_state,\n        &state.net.secure_network,\n    );\n\n    (device, control, runner)\n}\n\n/// Create a new instance of the CYW43 driver.\n///\n/// Returns a handle to the network device, control handle and a runner for driving the low level\n/// stack.\n#[cfg(feature = \"bluetooth\")]\npub async fn new_with_bluetooth<'a, PWR, SPI>(\n    state: &'a mut State,\n    pwr: PWR,\n    spi: SPI,\n    wifi_firmware: &Aligned<A4, [u8]>,\n    bluetooth_firmware: &Aligned<A4, [u8]>,\n    nvram: &Aligned<A4, [u8]>,\n) -> (\n    NetDriver<'a>,\n    bluetooth::BtDriver<'a>,\n    Control<'a>,\n    Runner<'a, SpiBus<PWR, SPI>>,\n)\nwhere\n    PWR: OutputPin,\n    SPI: SpiBusCyw43,\n{\n    let (ch_runner, device) = ch::new(&mut state.net.ch, ch::driver::HardwareAddress::Ethernet([0; 6]));\n    let state_ch = ch_runner.state_runner();\n\n    let (bt_runner, bt_driver) = bluetooth::new(&mut state.bt);\n    let mut runner = Runner::new(\n        ch_runner,\n        SpiBus::new(pwr, spi),\n        &state.ioctl_state,\n        &state.net.events,\n        &state.net.secure_network,\n        #[cfg(feature = \"bluetooth\")]\n        Some(bt_runner),\n    );\n\n    runner\n        .init(wifi_firmware, nvram, Some(bluetooth_firmware))\n        .await\n        .unwrap();\n    let control = Control::new(\n        state_ch,\n        &state.net.events,\n        &state.ioctl_state,\n        &state.net.secure_network,\n    );\n\n    (device, bt_driver, control, runner)\n}\n\n/// Include bytes aligned to A4 in the binary\n#[macro_export]\nmacro_rules! aligned_bytes {\n    ($path:expr) => {{\n        {\n            static BYTES: &cyw43::Aligned<cyw43::A4, [u8]> = &cyw43::Aligned(*include_bytes!($path));\n\n            BYTES\n        }\n    }};\n}\n"
  },
  {
    "path": "cyw43/src/runner.rs",
    "content": "use core::sync::atomic::AtomicBool;\nuse core::sync::atomic::Ordering::Relaxed;\n\nuse aligned::{A4, Aligned};\nuse embassy_futures::select::{Either4, select4};\nuse embassy_net_driver_channel as ch;\nuse embassy_net_driver_channel::driver::LinkState;\nuse embassy_time::{Duration, Timer, block_for};\n\nuse crate::consts::*;\nuse crate::events::{Event, Events, Status};\nuse crate::fmt::Bytes;\nuse crate::ioctl::{IoctlState, IoctlType, PendingIoctl};\npub use crate::spi::SpiBusCyw43;\nuse crate::structs::*;\nuse crate::util::{aligned_mut, aligned_ref, slice8_mut, slice16_mut, try_until};\nuse crate::{CHIP, Core, MTU, events};\n\n#[cfg(feature = \"firmware-logs\")]\nstruct LogState {\n    addr: u32,\n    last_idx: usize,\n    buf: [u8; 256],\n    buf_count: usize,\n}\n\n#[cfg(feature = \"firmware-logs\")]\nimpl Default for LogState {\n    fn default() -> Self {\n        Self {\n            addr: Default::default(),\n            last_idx: Default::default(),\n            buf: [0; 256],\n            buf_count: Default::default(),\n        }\n    }\n}\n\npub(crate) enum BusType {\n    Spi,\n    Sdio,\n}\n\npub(crate) trait SealedBus {\n    const TYPE: BusType;\n\n    async fn init(&mut self, bluetooth_enabled: bool);\n    async fn wlan_read(&mut self, buf: &mut Aligned<A4, [u8]>);\n    async fn wlan_write(&mut self, buf: &Aligned<A4, [u8]>);\n    #[allow(unused)]\n    async fn bp_read(&mut self, addr: u32, data: &mut [u8]);\n    async fn bp_write(&mut self, addr: u32, data: &[u8]);\n    async fn bp_read8(&mut self, addr: u32) -> u8;\n    async fn bp_write8(&mut self, addr: u32, val: u8);\n    async fn bp_read16(&mut self, addr: u32) -> u16;\n    #[allow(unused)]\n    async fn bp_write16(&mut self, addr: u32, val: u16);\n    #[allow(unused)]\n    async fn bp_read32(&mut self, addr: u32) -> u32;\n    async fn bp_write32(&mut self, addr: u32, val: u32);\n    async fn read8(&mut self, func: u32, addr: u32) -> u8;\n    async fn write8(&mut self, func: u32, addr: u32, val: u8);\n    async fn read16(&mut self, func: u32, addr: u32) -> u16;\n    async fn write16(&mut self, func: u32, addr: u32, val: u16);\n    async fn read32(&mut self, func: u32, addr: u32) -> u32;\n    #[allow(unused)]\n    async fn write32(&mut self, func: u32, addr: u32, val: u32);\n    async fn wait_for_event(&mut self);\n}\n\n#[allow(private_bounds)]\npub trait Bus: SealedBus {}\nimpl<T: SealedBus> Bus for T {}\n\n/// Driver communicating with the WiFi chip.\npub struct Runner<'a, BUS> {\n    ch: ch::Runner<'a, MTU>,\n    pub(crate) bus: BUS,\n\n    ioctl_state: &'a IoctlState,\n    ioctl_id: u16,\n    sdpcm_seq: u8,\n    sdpcm_seq_max: u8,\n\n    events: &'a Events,\n\n    secure_network: &'a AtomicBool,\n    join_ok: bool,\n    key_exchange_ok: bool,\n    auth_ok: bool,\n\n    #[cfg(feature = \"firmware-logs\")]\n    log: LogState,\n\n    #[cfg(feature = \"bluetooth\")]\n    pub(crate) bt: Option<crate::bluetooth::BtRunner<'a>>,\n}\n\nimpl<'a, BUS> Runner<'a, BUS>\nwhere\n    BUS: Bus,\n{\n    pub(crate) fn new(\n        ch: ch::Runner<'a, MTU>,\n        bus: BUS,\n        ioctl_state: &'a IoctlState,\n        events: &'a Events,\n        secure_network: &'a AtomicBool,\n        #[cfg(feature = \"bluetooth\")] bt: Option<crate::bluetooth::BtRunner<'a>>,\n    ) -> Self {\n        Self {\n            ch,\n            bus,\n            ioctl_state,\n            ioctl_id: 0,\n            sdpcm_seq: 0,\n            sdpcm_seq_max: 1,\n            events,\n            secure_network,\n            join_ok: false,\n            key_exchange_ok: false,\n            auth_ok: false,\n            #[cfg(feature = \"firmware-logs\")]\n            log: LogState::default(),\n            #[cfg(feature = \"bluetooth\")]\n            bt,\n        }\n    }\n\n    pub(crate) async fn init(\n        &mut self,\n        wifi_fw: &Aligned<A4, [u8]>,\n        nvram: &Aligned<A4, [u8]>,\n        bt_fw: Option<&[u8]>,\n    ) -> Result<(), ()> {\n        self.bus.init(bt_fw.is_some()).await;\n\n        // Init ALP (Active Low Power) clock\n        debug!(\"init alp\");\n        match BUS::TYPE {\n            BusType::Spi => {\n                self.bus\n                    .write8(FUNC_BACKPLANE, REG_BACKPLANE_CHIP_CLOCK_CSR, BACKPLANE_ALP_AVAIL_REQ)\n                    .await;\n\n                // Not present in whd driver\n                debug!(\"set f2 watermark\");\n                self.bus\n                    .write8(FUNC_BACKPLANE, REG_BACKPLANE_FUNCTION2_WATERMARK, 0x10)\n                    .await;\n                let watermark = self.bus.read8(FUNC_BACKPLANE, REG_BACKPLANE_FUNCTION2_WATERMARK).await;\n                debug!(\"watermark = {:02x}\", watermark);\n                assert!(watermark == 0x10);\n            }\n            BusType::Sdio => {\n                self.bus\n                    .write8(\n                        FUNC_BACKPLANE,\n                        REG_BACKPLANE_CHIP_CLOCK_CSR,\n                        BACKPLANE_FORCE_HW_CLKREQ_OFF | BACKPLANE_ALP_AVAIL_REQ | BACKPLANE_FORCE_ALP,\n                    )\n                    .await;\n            }\n        }\n\n        debug!(\"waiting for clock...\");\n        if !try_until(\n            async || self.bus.read8(FUNC_BACKPLANE, REG_BACKPLANE_CHIP_CLOCK_CSR).await & BACKPLANE_ALP_AVAIL != 0,\n            Duration::from_millis(100),\n        )\n        .await\n        {\n            debug!(\"timeout while waiting for alp clock!\");\n            return Err(());\n        }\n        debug!(\"clock ok\");\n\n        // clear request for ALP\n        debug!(\"clear request for ALP\");\n        self.bus.write8(FUNC_BACKPLANE, REG_BACKPLANE_CHIP_CLOCK_CSR, 0).await;\n\n        let chip_id = match BUS::TYPE {\n            BusType::Spi => self.bus.bp_read16(CHIPCOMMON_BASE_ADDRESS).await,\n            BusType::Sdio => {\n                // Disable the extra sdio pull-ups\n                // self.bus.write8(FUNC_BACKPLANE, SDIO_PULL_UP, 0).await;\n\n                // Enable f1 and f2\n                self.bus\n                    .write8(\n                        FUNC_BUS,\n                        SDIOD_CCCR_IOEN,\n                        (SDIO_FUNC_ENABLE_1 | SDIO_FUNC_ENABLE_2) as u8,\n                    )\n                    .await;\n\n                // Enable out-of-band interrupt signal\n                // whd_bus_sdio_init_oob_intr\n\n                // Note: only GPIO0 using rising edge is currently supported\n                self.bus\n                    .write8(\n                        FUNC_BUS,\n                        SDIOD_SEP_INT_CTL,\n                        (SEP_INTR_CTL_MASK | SEP_INTR_CTL_EN | SEP_INTR_CTL_POL) as u8,\n                    )\n                    .await;\n\n                // Enable f2 interrupt only\n                self.bus\n                    .write8(\n                        FUNC_BUS,\n                        SDIOD_CCCR_INTEN,\n                        (INTR_CTL_MASTER_EN | INTR_CTL_FUNC2_EN) as u8,\n                    )\n                    .await;\n\n                self.bus.read8(FUNC_BUS, SDIOD_CCCR_IORDY).await;\n\n                let reg = self.bus.read8(FUNC_BUS, SDIOD_CCCR_BRCM_CARDCAP).await;\n                if reg & SDIOD_CCCR_BRCM_CARDCAP_SECURE_MODE as u8 != 0 {\n                    debug!(\"chip supports bootloader handshake\");\n\n                    let devctrl = self.bus.read8(FUNC_BACKPLANE, SBSDIO_DEVICE_CTL).await;\n\n                    self.bus\n                        .write8(\n                            FUNC_BACKPLANE,\n                            SBSDIO_DEVICE_CTL,\n                            devctrl | SBSDIO_DEVCTL_ADDR_RST as u8,\n                        )\n                        .await;\n\n                    let addr_low = self.bus.read8(FUNC_BACKPLANE, SBSDIO_FUNC1_SBADDRLOW).await as u32;\n                    let addr_mid = self.bus.read8(FUNC_BACKPLANE, SBSDIO_FUNC1_SBADDRMID).await as u32;\n                    let addr_high = self.bus.read8(FUNC_BACKPLANE, SBSDIO_FUNC1_SBADDRHIGH).await as u32;\n\n                    let reg_addr = ((addr_low << 8) | (addr_mid << 16) | (addr_high << 24)) + SDIO_CORE_CHIPID_REG;\n\n                    self.bus.write8(FUNC_BACKPLANE, SBSDIO_DEVICE_CTL, devctrl).await;\n\n                    self.bus.bp_read16(reg_addr).await\n                } else {\n                    self.bus.bp_read16(CHIPCOMMON_BASE_ADDRESS).await\n                }\n            }\n        };\n\n        debug!(\"chip ID: {}\", chip_id);\n\n        // Upload firmware.\n        self.core_disable(Core::WLAN).await;\n        self.core_disable(Core::SOCSRAM).await; // TODO: is this needed if we reset right after?\n        self.core_reset(Core::SOCSRAM).await;\n\n        // this is 4343x specific stuff: Disable remap for SRAM_3\n        self.bus.bp_write32(CHIP.socsram_base_address + 0x10, 3).await;\n        self.bus.bp_write32(CHIP.socsram_base_address + 0x44, 0).await;\n\n        let ram_addr = CHIP.atcm_ram_base_address;\n\n        debug!(\"loading fw\");\n        self.bus.bp_write(ram_addr, wifi_fw).await;\n\n        debug!(\"loading nvram\");\n        // Round up to 4 bytes.\n        let nvram_len = (nvram.len() + 3) / 4 * 4;\n        self.bus\n            .bp_write(ram_addr + CHIP.chip_ram_size - 4 - nvram_len as u32, nvram)\n            .await;\n\n        let nvram_len_words = nvram_len as u32 / 4;\n        let nvram_len_magic = (!nvram_len_words << 16) | nvram_len_words;\n        self.bus\n            .bp_write32(ram_addr + CHIP.chip_ram_size - 4, nvram_len_magic)\n            .await;\n\n        // Start core!\n        debug!(\"starting up core...\");\n        self.core_reset(Core::WLAN).await;\n        assert!(self.core_is_up(Core::WLAN).await);\n\n        // wait until HT clock is available; takes about 29ms\n        debug!(\"waiting for HT clock...\");\n        while self.bus.read8(FUNC_BACKPLANE, REG_BACKPLANE_CHIP_CLOCK_CSR).await & 0x80 == 0 {}\n\n        // \"Set up the interrupt mask and enable interrupts\"\n        debug!(\"setup interrupt mask\");\n        self.bus\n            .bp_write32(CHIP.sdiod_core_base_address + SDIO_INT_HOST_MASK, I_HMB_SW_MASK)\n            .await;\n\n        match BUS::TYPE {\n            BusType::Sdio => {\n                self.bus\n                    .bp_write8(CHIP.sdiod_core_base_address + SDIO_FUNCTION_INT_MASK, 2 | 1)\n                    .await;\n\n                // \"Lower F2 Watermark to avoid DMA Hang in F2 when SD Clock is stopped.\"\n                // Sounds scary...\n                self.bus\n                    .write8(FUNC_BACKPLANE, REG_BACKPLANE_FUNCTION2_WATERMARK, SDIO_F2_WATERMARK)\n                    .await;\n            }\n\n            BusType::Spi => {\n                // Set up the interrupt mask and enable interrupts\n                if bt_fw.is_some() {\n                    debug!(\"bluetooth setup interrupt mask\");\n                    self.bus\n                        .bp_write32(CHIP.sdiod_core_base_address + SDIO_INT_HOST_MASK, I_HMB_FC_CHANGE)\n                        .await;\n                }\n\n                self.bus\n                    .write16(FUNC_BUS, REG_BUS_INTERRUPT_ENABLE, IRQ_F2_PACKET_AVAILABLE)\n                    .await;\n\n                // \"Lower F2 Watermark to avoid DMA Hang in F2 when SD Clock is stopped.\"\n                // Sounds scary...\n                self.bus\n                    .write8(FUNC_BACKPLANE, REG_BACKPLANE_FUNCTION2_WATERMARK, SPI_F2_WATERMARK)\n                    .await;\n            }\n        }\n\n        // wait for F2 to be ready\n        debug!(\"waiting for F2 to be ready...\");\n        if !try_until(\n            async || match BUS::TYPE {\n                BusType::Sdio => self.bus.read8(FUNC_BUS, SDIOD_CCCR_IORDY).await as u32 & SDIO_FUNC_READY_2 != 0,\n                BusType::Spi => self.bus.read32(FUNC_BUS, REG_BUS_STATUS).await & STATUS_F2_RX_READY != 0,\n            },\n            Duration::from_millis(1000),\n        )\n        .await\n        {\n            debug!(\"timeout while waiting for function 2 to be ready\");\n            return Err(());\n        }\n\n        match BUS::TYPE {\n            BusType::Sdio => {\n                self.bus\n                    .write8(FUNC_BACKPLANE, SDIO_SLEEP_CSR, SBSDIO_SLPCSR_KEEP_WL_KS as u8)\n                    .await;\n\n                self.bus\n                    .write8(FUNC_BACKPLANE, SDIO_SLEEP_CSR, SBSDIO_SLPCSR_KEEP_WL_KS as u8)\n                    .await;\n\n                assert!(self.bus.read8(FUNC_BACKPLANE, SDIO_SLEEP_CSR).await & SBSDIO_SLPCSR_KEEP_WL_KS as u8 != 0);\n            }\n            BusType::Spi => {}\n        }\n\n        // Some random configs related to sleep.\n        // These aren't needed if we don't want to sleep the bus.\n        // TODO do we need to sleep the bus to read the irq line, due to\n        // being on the same pin as MOSI/MISO?\n\n        /*\n        let mut val = self.bus.read8(FUNC_BACKPLANE, REG_BACKPLANE_WAKEUP_CTRL).await;\n        val |= 0x02; // WAKE_TILL_HT_AVAIL\n        self.bus.write8(FUNC_BACKPLANE, REG_BACKPLANE_WAKEUP_CTRL, val).await;\n        self.bus.write8(FUNC_BUS, 0xF0, 0x08).await; // SDIOD_CCCR_BRCM_CARDCAP.CMD_NODEC = 1\n        self.bus.write8(FUNC_BACKPLANE, REG_BACKPLANE_CHIP_CLOCK_CSR, 0x02).await; // SBSDIO_FORCE_HT\n\n        let mut val = self.bus.read8(FUNC_BACKPLANE, REG_BACKPLANE_SLEEP_CSR).await;\n        val |= 0x01; // SBSDIO_SLPCSR_KEEP_SDIO_ON\n        self.bus.write8(FUNC_BACKPLANE, REG_BACKPLANE_SLEEP_CSR, val).await;\n         */\n\n        // clear pulls\n        debug!(\"clear pad pulls\");\n        self.bus.write8(FUNC_BACKPLANE, REG_BACKPLANE_PULL_UP, 0).await;\n        let _ = self.bus.read8(FUNC_BACKPLANE, REG_BACKPLANE_PULL_UP).await;\n\n        // start HT clock\n        self.bus\n            .write8(FUNC_BACKPLANE, REG_BACKPLANE_CHIP_CLOCK_CSR, 0x10)\n            .await; // SBSDIO_HT_AVAIL_REQ\n        debug!(\"waiting for HT clock...\");\n        while self.bus.read8(FUNC_BACKPLANE, REG_BACKPLANE_CHIP_CLOCK_CSR).await & 0x80 == 0 {}\n        debug!(\"clock ok\");\n\n        #[cfg(feature = \"firmware-logs\")]\n        self.log_init().await;\n\n        #[cfg(feature = \"bluetooth\")]\n        if let Some(bt_fw) = bt_fw {\n            self.bt.as_mut().unwrap().init_bluetooth(&mut self.bus, bt_fw).await;\n        }\n\n        debug!(\"cyw43 runner init done\");\n\n        Ok(())\n    }\n\n    #[cfg(feature = \"firmware-logs\")]\n    async fn log_init(&mut self) {\n        // Initialize shared memory for logging.\n\n        let addr = CHIP.atcm_ram_base_address + CHIP.chip_ram_size - 4 - CHIP.socram_srmem_size;\n        let shared_addr = self.bus.bp_read32(addr).await;\n        debug!(\"shared_addr {:08x}\", shared_addr);\n\n        let mut shared: Aligned<A4, [u8; _]> = Aligned([0; SharedMemData::SIZE]);\n        self.bus.bp_read(shared_addr, &mut shared[..]).await;\n        let shared = SharedMemData::from_bytes(&shared);\n\n        self.log.addr = shared.console_addr + 8;\n    }\n\n    #[cfg(feature = \"firmware-logs\")]\n    async fn log_read(&mut self) {\n        // Read log struct\n        let mut log: Aligned<A4, [u8; _]> = Aligned([0; SharedMemLog::SIZE]);\n        self.bus.bp_read(self.log.addr, &mut log[..]).await;\n        let log = SharedMemLog::from_bytes(&log);\n\n        let idx = log.idx as usize;\n\n        // If pointer hasn't moved, no need to do anything.\n        if idx == self.log.last_idx {\n            return;\n        }\n\n        // Read entire buf for now. We could read only what we need, but then we\n        // run into annoying alignment issues in `bp_read`.\n        let mut buf: Aligned<A4, [u8; _]> = Aligned([0; 0x400]);\n        self.bus.bp_read(log.buf, &mut buf[..]).await;\n\n        while self.log.last_idx != idx as usize {\n            let b = buf[self.log.last_idx];\n            if b == b'\\r' || b == b'\\n' {\n                if self.log.buf_count != 0 {\n                    let s = unsafe { core::str::from_utf8_unchecked(&self.log.buf[..self.log.buf_count]) };\n                    debug!(\"LOGS: {}\", s);\n                    self.log.buf_count = 0;\n                }\n            } else if self.log.buf_count < self.log.buf.len() {\n                self.log.buf[self.log.buf_count] = b;\n                self.log.buf_count += 1;\n            }\n\n            self.log.last_idx += 1;\n            if self.log.last_idx == 0x400 {\n                self.log.last_idx = 0;\n            }\n        }\n    }\n\n    /// Run the CYW43 event handling loop.\n    pub async fn run(mut self) -> ! {\n        let mut buf = [0; 512];\n        loop {\n            #[cfg(feature = \"firmware-logs\")]\n            self.log_read().await;\n\n            if self.has_credit() {\n                let ioctl = self.ioctl_state.wait_pending();\n                let wifi_tx = self.ch.tx_buf();\n                #[cfg(feature = \"bluetooth\")]\n                let bt_tx = async {\n                    match &mut self.bt {\n                        Some(bt) => bt.tx_chan.receive().await,\n                        None => core::future::pending().await,\n                    }\n                };\n                #[cfg(not(feature = \"bluetooth\"))]\n                let bt_tx = core::future::pending::<()>();\n\n                // interrupts aren't working yet for bluetooth. Do busy-polling instead.\n                // Note for this to work `ev` has to go last in the `select()`. It prefers\n                // first futures if they're ready, so other select branches don't get starved.`\n                #[cfg(feature = \"bluetooth\")]\n                let ev = core::future::ready(());\n                #[cfg(not(feature = \"bluetooth\"))]\n                let ev = self.bus.wait_for_event();\n\n                match select4(ioctl, wifi_tx, bt_tx, ev).await {\n                    Either4::First(PendingIoctl {\n                        buf: iobuf,\n                        kind,\n                        cmd,\n                        iface,\n                    }) => {\n                        self.send_ioctl(kind, cmd, iface, unsafe { &*iobuf }, &mut buf).await;\n                        self.check_status(&mut buf).await;\n                    }\n                    Either4::Second(packet) => {\n                        trace!(\"tx pkt {:02x}\", Bytes(&packet[..packet.len().min(48)]));\n\n                        let buf8 = slice8_mut(&mut buf);\n\n                        // There MUST be 2 bytes of padding between the SDPCM and BDC headers.\n                        // And ONLY for data packets!\n                        // No idea why, but the firmware will append two zero bytes to the tx'd packets\n                        // otherwise. If the packet is exactly 1514 bytes (the max MTU), this makes it\n                        // be oversized and get dropped.\n                        // WHD adds it here https://github.com/Infineon/wifi-host-driver/blob/c04fcbb6b0d049304f376cf483fd7b1b570c8cd5/WiFi_Host_Driver/src/include/whd_sdpcm.h#L90\n                        // and adds it to the header size her https://github.com/Infineon/wifi-host-driver/blob/c04fcbb6b0d049304f376cf483fd7b1b570c8cd5/WiFi_Host_Driver/src/whd_sdpcm.c#L597\n                        // ¯\\_(ツ)_/¯\n                        const PADDING_SIZE: usize = 2;\n                        let total_len = SdpcmHeader::SIZE + PADDING_SIZE + BdcHeader::SIZE + packet.len();\n\n                        let seq = self.sdpcm_seq;\n                        self.sdpcm_seq = self.sdpcm_seq.wrapping_add(1);\n\n                        let sdpcm_header = SdpcmHeader {\n                            len: total_len as u16, // TODO does this len need to be rounded up to u32?\n                            len_inv: !total_len as u16,\n                            sequence: seq,\n                            channel_and_flags: CHANNEL_TYPE_DATA,\n                            next_length: 0,\n                            header_length: (SdpcmHeader::SIZE + PADDING_SIZE) as _,\n                            wireless_flow_control: 0,\n                            bus_data_credit: 0,\n                            reserved: [0, 0],\n                        };\n\n                        let bdc_header = BdcHeader {\n                            flags: BDC_VERSION << BDC_VERSION_SHIFT,\n                            priority: 0,\n                            flags2: 0,\n                            data_offset: 0,\n                        };\n                        trace!(\"tx {:?}\", sdpcm_header);\n                        trace!(\"    {:?}\", bdc_header);\n\n                        buf8[0..SdpcmHeader::SIZE].copy_from_slice(&sdpcm_header.to_bytes());\n                        buf8[SdpcmHeader::SIZE + PADDING_SIZE..][..BdcHeader::SIZE]\n                            .copy_from_slice(&bdc_header.to_bytes());\n                        buf8[SdpcmHeader::SIZE + PADDING_SIZE + BdcHeader::SIZE..][..packet.len()]\n                            .copy_from_slice(packet);\n\n                        let total_len = (total_len + 3) & !3; // round up to 4byte\n\n                        trace!(\"    {:02x}\", Bytes(&buf8[..total_len.min(48)]));\n\n                        self.bus.wlan_write(&aligned_ref(&buf)[..total_len]).await;\n                        self.ch.tx_done();\n                        self.check_status(&mut buf).await;\n                    }\n                    Either4::Third(_) => {\n                        #[cfg(feature = \"bluetooth\")]\n                        self.bt.as_mut().unwrap().hci_write(&mut self.bus).await;\n                    }\n                    Either4::Fourth(()) => {\n                        self.handle_irq(&mut buf).await;\n\n                        // If we do busy-polling, make sure to yield.\n                        // `handle_irq` will only do a 32bit read if there's no work to do, which is really fast.\n                        // Depending on optimization level, it is possible that the 32-bit read finishes on\n                        // first poll, so it never yields and we starve all other tasks.\n                        #[cfg(feature = \"bluetooth\")]\n                        embassy_futures::yield_now().await;\n                    }\n                }\n            } else {\n                warn!(\"TX stalled\");\n                match BUS::TYPE {\n                    BusType::Sdio => {\n                        // whd_bus_sdio_poke_wlan\n                        self.bus\n                            .bp_write32(CHIP.sdiod_core_base_address + SDIO_TO_SB_MAILBOX, SMB_DEV_INT)\n                            .await;\n                    }\n                    BusType::Spi => {}\n                }\n                self.bus.wait_for_event().await;\n                self.handle_irq(&mut buf).await;\n            }\n        }\n    }\n\n    /// Wait for IRQ on F2 packet available\n    async fn handle_irq(&mut self, buf: &mut [u32; 512]) {\n        match BUS::TYPE {\n            BusType::Sdio => {\n                // TODO: get irqs working\n                self.check_status(buf).await;\n\n                // whd_bus_sdio_packet_available_to_read\n                let irq = self.bus.bp_read32(CHIP.sdiod_core_base_address + SDIO_INT_STATUS).await;\n                if irq & I_HMB_HOST_INT == 0 {\n                    // return;\n                }\n\n                let hmb_data = self\n                    .bus\n                    .bp_read32(CHIP.sdiod_core_base_address + SDIO_TO_HOST_MAILBOX_DATA)\n                    .await;\n                self.bus\n                    .bp_write32(CHIP.sdiod_core_base_address + SDIO_TO_SB_MAILBOX, SMB_INT_ACK)\n                    .await;\n\n                trace!(\"hmb ack\");\n                if hmb_data & I_HMB_DATA_FWHALT != 0 {\n                    debug!(\"hmb data fault\");\n                }\n\n                // Clear irq must be done here to avoid a race\n                if irq & HOSTINTMASK != 0 {\n                    trace!(\"clear irq\");\n                    self.bus\n                        .bp_write32(CHIP.sdiod_core_base_address + SDIO_INT_STATUS, irq & HOSTINTMASK)\n                        .await;\n                }\n            }\n            BusType::Spi => {\n                // Receive stuff\n                let irq = self.bus.read16(FUNC_BUS, REG_BUS_INTERRUPT).await;\n                if irq != 0 {\n                    trace!(\"irq{}\", FormatInterrupt(irq));\n                }\n\n                if irq & IRQ_F2_PACKET_AVAILABLE != 0 {\n                    self.check_status(buf).await;\n                }\n\n                if irq & IRQ_DATA_UNAVAILABLE != 0 {\n                    // this seems to be ignorable with no ill effects.\n                    trace!(\"IRQ DATA_UNAVAILABLE, clearing...\");\n                    self.bus.write16(FUNC_BUS, REG_BUS_INTERRUPT, 1).await;\n                }\n\n                #[cfg(feature = \"bluetooth\")]\n                if let Some(bt) = &mut self.bt {\n                    bt.handle_irq(&mut self.bus).await;\n                }\n            }\n        }\n    }\n\n    /// Handle F2 events while status register is set\n    async fn check_status(&mut self, buf: &mut [u32; 512]) {\n        loop {\n            match BUS::TYPE {\n                BusType::Spi => {\n                    let status = self.bus.read32(FUNC_BUS, SPI_STATUS_REGISTER).await;\n                    trace!(\"check status{}\", FormatStatus(status));\n\n                    if status & STATUS_F2_PKT_AVAILABLE != 0 {\n                        let len = (status & STATUS_F2_PKT_LEN_MASK) >> STATUS_F2_PKT_LEN_SHIFT;\n                        self.bus.wlan_read(&mut aligned_mut(buf)[..len as usize]).await;\n                        trace!(\"rx {:02x}\", Bytes(&slice8_mut(buf)[..(len as usize).min(48)]));\n                        self.rx(&mut slice8_mut(buf)[..len as usize]);\n                    } else {\n                        break;\n                    }\n                }\n                BusType::Sdio => {\n                    self.bus.wlan_read(&mut aligned_mut(&mut buf[..1])).await;\n                    let (len, len_inv) = {\n                        let hwtag = slice16_mut(&mut buf[..1]);\n\n                        (hwtag[0], hwtag[1])\n                    };\n\n                    if (len | len_inv) == 0 || (len ^ len_inv) != 0xFFFF {\n                        trace!(\"hwtag mismatch (hwtag[0] = {}, hwtag[1] = {})\", len, len_inv);\n                        break;\n                    }\n\n                    trace!(\"pkt ready...\");\n                    let len = len as usize;\n                    if len > INITIAL_READ as usize {\n                        self.bus\n                            .wlan_read(&mut aligned_mut(&mut buf[1..])[..len - INITIAL_READ as usize])\n                            .await;\n                    } else {\n                        // TODO: investigate this condition\n                        trace!(\"no extra space required\");\n                        continue;\n                    }\n\n                    if len == SdpcmHeader::SIZE {\n                        let Some((sdpcm_header, _)) = SdpcmHeader::parse(slice8_mut(&mut buf[..3])) else {\n                            debug!(\"failed to parse sdpcm header\");\n                            break;\n                        };\n\n                        self.update_credit(&sdpcm_header);\n                    } else if len > SdpcmHeader::SIZE {\n                        trace!(\"rx {:02x}\", Bytes(&slice8_mut(buf)[..len.min(48)]));\n                        self.rx(&mut slice8_mut(buf)[..len]);\n                    }\n                }\n            }\n        }\n    }\n\n    fn rx(&mut self, packet: &mut [u8]) {\n        let Some((sdpcm_header, payload)) = SdpcmHeader::parse(packet) else {\n            return;\n        };\n\n        self.update_credit(&sdpcm_header);\n\n        let channel = sdpcm_header.channel_and_flags & 0x0f;\n\n        match channel {\n            CHANNEL_TYPE_CONTROL => {\n                let Some((cdc_header, response)) = CdcHeader::parse(payload) else {\n                    return;\n                };\n                trace!(\"    {:?}\", cdc_header);\n\n                if cdc_header.id == self.ioctl_id {\n                    if cdc_header.status != 0 {\n                        // TODO: propagate error instead\n                        panic!(\"IOCTL error {}\", cdc_header.status as i32);\n                    }\n\n                    self.ioctl_state.ioctl_done(response);\n                }\n            }\n            CHANNEL_TYPE_EVENT => {\n                let Some((_, bdc_packet)) = BdcHeader::parse(payload) else {\n                    warn!(\"BDC event, incomplete header\");\n                    return;\n                };\n\n                let Some((event_packet, evt_data)) = EventPacket::parse(bdc_packet) else {\n                    warn!(\"BDC event, incomplete data\");\n                    return;\n                };\n\n                const ETH_P_LINK_CTL: u16 = 0x886c; // HPNA, wlan link local tunnel, according to linux if_ether.h\n                if event_packet.eth.ether_type != ETH_P_LINK_CTL {\n                    warn!(\n                        \"unexpected ethernet type 0x{:04x}, expected Broadcom ether type 0x{:04x}\",\n                        event_packet.eth.ether_type, ETH_P_LINK_CTL\n                    );\n                    return;\n                }\n                const BROADCOM_OUI: &[u8] = &[0x00, 0x10, 0x18];\n                if event_packet.hdr.oui != BROADCOM_OUI {\n                    warn!(\n                        \"unexpected ethernet OUI {:02x}, expected Broadcom OUI {:02x}\",\n                        Bytes(&event_packet.hdr.oui),\n                        Bytes(BROADCOM_OUI)\n                    );\n                    return;\n                }\n                const BCMILCP_SUBTYPE_VENDOR_LONG: u16 = 32769;\n                if event_packet.hdr.subtype != BCMILCP_SUBTYPE_VENDOR_LONG {\n                    warn!(\"unexpected subtype {}\", event_packet.hdr.subtype);\n                    return;\n                }\n\n                const BCMILCP_BCM_SUBTYPE_EVENT: u16 = 1;\n                if event_packet.hdr.user_subtype != BCMILCP_BCM_SUBTYPE_EVENT {\n                    warn!(\"unexpected user_subtype {}\", event_packet.hdr.subtype);\n                    return;\n                }\n\n                let event_type = Event::from(event_packet.msg.event_type as u8);\n                let status = EStatus::from(event_packet.msg.status as u8);\n                debug!(\n                    \"=== EVENT {:?}: {:?} {:02x}\",\n                    event_type,\n                    event_packet.msg,\n                    Bytes(evt_data)\n                );\n\n                let update_link_status = match (\n                    event_type,\n                    status,\n                    event_packet.msg.flags,\n                    event_packet.msg.reason,\n                    event_packet.msg.auth_type,\n                ) {\n                    // Events indicating that the link is down\n                    // Event LINK with flag 0 indicates link down. reason = 1: loss of signal (e.g. out of range), reason = 2: controlled network shutdown\n                    // Event AUTH with status FAIL, reason 16, and auth_type 3 is specific for WPA3 networks\n                    (Event::LINK, EStatus::SUCCESS, 0, ..)\n                    | (Event::DEAUTH, EStatus::SUCCESS, ..)\n                    | (Event::AUTH, EStatus::FAIL, _, 16, 3) => {\n                        self.auth_ok = false;\n                        self.join_ok = false;\n                        self.key_exchange_ok = false;\n                        true\n                    }\n                    // Update auth flag. Ignore unsolicited events.\n                    // When changing passwords on a WPA3 AP which we are already connected to, or we roam to, PSK_SUP events indicating\n                    // success are still sent. Only the AUTH events indicate failure and this flag helps cover that scenario\n                    (Event::AUTH, status, ..) if status != EStatus::UNSOLICITED => {\n                        self.auth_ok = status == EStatus::SUCCESS;\n                        debug!(\"auth_ok flag: {}\", self.auth_ok as u8);\n                        false\n                    }\n                    // Successfully joined the network. Open or WPA3 networks are now fully connected - WPA1/2 networks additionally require a successful key exchange.\n                    (Event::JOIN, EStatus::SUCCESS, ..) => {\n                        self.join_ok = true;\n                        true\n                    }\n\n                    // Key exchange events (PSK_SUP) for secure networks\n                    // The status codes for PSK_SUP events seem to have different meanings from other event types\n\n                    // Successful key exchange, indicated by a PSK_SUP event with status 6 \"UNSOLICITED\"\n                    // Disregard if auth_ok is false, which can happen in WPA3 networks\n                    (Event::PSK_SUP, EStatus::UNSOLICITED, 0, 0, _) => {\n                        if self.auth_ok {\n                            self.key_exchange_ok = true;\n                            true\n                        } else {\n                            false\n                        }\n                    }\n                    // Ignore PSK_SUP events with reason 14 as they are often sent when the device roams from one AP to another\n                    (Event::PSK_SUP, _, _, 14, _) => false,\n                    // Other PSK_SUP events indicate key exchange errors\n                    (Event::PSK_SUP, ..) => {\n                        self.key_exchange_ok = false;\n                        true\n                    }\n                    _ => false,\n                };\n\n                if update_link_status {\n                    let secure_network = self.secure_network.load(Relaxed);\n                    let link_state = if self.join_ok && (!secure_network || self.key_exchange_ok) {\n                        LinkState::Up\n                    } else {\n                        LinkState::Down\n                    };\n\n                    self.ch.set_link_state(link_state);\n\n                    debug!(\n                        \"link_ok: {}, secure_network: {}, auth_ok: {}, password_ok: {}, link_state {}\",\n                        self.join_ok as u8,\n                        secure_network as u8,\n                        self.auth_ok as u8,\n                        self.key_exchange_ok as u8,\n                        link_state as u8\n                    );\n                }\n\n                if self.events.mask.is_enabled(event_type) {\n                    let status = event_packet.msg.status;\n                    let event_payload = match event_type {\n                        Event::ESCAN_RESULT if status == EStatus::PARTIAL => {\n                            let Some((_, bss_info)) = ScanResults::parse(evt_data) else {\n                                return;\n                            };\n                            let Some(bss_info) = BssInfo::parse(bss_info) else {\n                                return;\n                            };\n                            events::Payload::BssInfo(*bss_info)\n                        }\n                        Event::ESCAN_RESULT => events::Payload::None,\n                        _ => events::Payload::None,\n                    };\n\n                    // this intentionally uses the non-blocking publish immediate\n                    // publish() is a deadlock risk in the current design as awaiting here prevents ioctls\n                    // The `Runner` always yields when accessing the device, so consumers always have a chance to receive the event\n                    // (if they are actively awaiting the queue)\n                    self.events\n                        .queue\n                        .immediate_publisher()\n                        .publish_immediate(events::Message::new(Status { event_type, status }, event_payload));\n                }\n            }\n            CHANNEL_TYPE_DATA => {\n                let Some((_, packet)) = BdcHeader::parse(payload) else {\n                    return;\n                };\n                trace!(\"rx pkt {:02x}\", Bytes(&packet[..packet.len().min(48)]));\n\n                match self.ch.try_rx_buf() {\n                    Some(buf) => {\n                        buf[..packet.len()].copy_from_slice(packet);\n                        self.ch.rx_done(packet.len())\n                    }\n                    None => warn!(\"failed to push rxd packet to the channel.\"),\n                }\n            }\n            _ => {}\n        }\n    }\n\n    fn update_credit(&mut self, sdpcm_header: &SdpcmHeader) {\n        if sdpcm_header.channel_and_flags & 0xf < 3 {\n            let mut sdpcm_seq_max = sdpcm_header.bus_data_credit;\n            if sdpcm_seq_max.wrapping_sub(self.sdpcm_seq) > 0x40 {\n                sdpcm_seq_max = self.sdpcm_seq + 2;\n            }\n            self.sdpcm_seq_max = sdpcm_seq_max;\n        }\n    }\n\n    fn has_credit(&self) -> bool {\n        self.sdpcm_seq != self.sdpcm_seq_max && self.sdpcm_seq_max.wrapping_sub(self.sdpcm_seq) & 0x80 == 0\n    }\n\n    async fn send_ioctl(&mut self, kind: IoctlType, cmd: Ioctl, iface: u32, data: &[u8], buf: &mut [u32; 512]) {\n        let buf8 = slice8_mut(buf);\n\n        let total_len = SdpcmHeader::SIZE + CdcHeader::SIZE + data.len();\n\n        let sdpcm_seq = self.sdpcm_seq;\n        self.sdpcm_seq = self.sdpcm_seq.wrapping_add(1);\n        self.ioctl_id = self.ioctl_id.wrapping_add(1);\n\n        let sdpcm_header = SdpcmHeader {\n            len: total_len as u16, // TODO does this len need to be rounded up to u32?\n            len_inv: !total_len as u16,\n            sequence: sdpcm_seq,\n            channel_and_flags: CHANNEL_TYPE_CONTROL,\n            next_length: 0,\n            header_length: SdpcmHeader::SIZE as _,\n            wireless_flow_control: 0,\n            bus_data_credit: 0,\n            reserved: [0, 0],\n        };\n\n        let cdc_header = CdcHeader {\n            cmd: cmd as u32,\n            len: data.len() as _,\n            flags: kind as u16 | (iface as u16) << 12,\n            id: self.ioctl_id,\n            status: 0,\n        };\n        trace!(\"tx {:?}\", sdpcm_header);\n        trace!(\"    {:?}\", cdc_header);\n\n        buf8[0..SdpcmHeader::SIZE].copy_from_slice(&sdpcm_header.to_bytes());\n        buf8[SdpcmHeader::SIZE..][..CdcHeader::SIZE].copy_from_slice(&cdc_header.to_bytes());\n        buf8[SdpcmHeader::SIZE + CdcHeader::SIZE..][..data.len()].copy_from_slice(data);\n\n        let total_len = (total_len + 3) & !3; // round up to 4byte,\n        trace!(\"    {:02x}\", Bytes(&buf8[..total_len.min(48)]));\n\n        self.bus.wlan_write(&aligned_ref(buf)[..total_len]).await;\n    }\n\n    async fn core_disable(&mut self, core: Core) {\n        let base = core.base_addr();\n\n        // Dummy read?\n        let _ = self.bus.bp_read8(base + AI_RESETCTRL_OFFSET).await;\n\n        // Check it isn't already reset\n        let r = self.bus.bp_read8(base + AI_RESETCTRL_OFFSET).await;\n        if r & AI_RESETCTRL_BIT_RESET != 0 {\n            return;\n        }\n\n        self.bus.bp_write8(base + AI_IOCTRL_OFFSET, 0).await;\n        let _ = self.bus.bp_read8(base + AI_IOCTRL_OFFSET).await;\n\n        block_for(Duration::from_millis(1));\n\n        self.bus\n            .bp_write8(base + AI_RESETCTRL_OFFSET, AI_RESETCTRL_BIT_RESET)\n            .await;\n        let _ = self.bus.bp_read8(base + AI_RESETCTRL_OFFSET).await;\n    }\n\n    async fn core_reset(&mut self, core: Core) {\n        self.core_disable(core).await;\n\n        let base = core.base_addr();\n        self.bus\n            .bp_write8(base + AI_IOCTRL_OFFSET, AI_IOCTRL_BIT_FGC | AI_IOCTRL_BIT_CLOCK_EN)\n            .await;\n        let _ = self.bus.bp_read8(base + AI_IOCTRL_OFFSET).await;\n\n        self.bus.bp_write8(base + AI_RESETCTRL_OFFSET, 0).await;\n\n        Timer::after_millis(1).await;\n\n        self.bus\n            .bp_write8(base + AI_IOCTRL_OFFSET, AI_IOCTRL_BIT_CLOCK_EN)\n            .await;\n        let _ = self.bus.bp_read8(base + AI_IOCTRL_OFFSET).await;\n\n        Timer::after_millis(1).await;\n    }\n\n    async fn core_is_up(&mut self, core: Core) -> bool {\n        let base = core.base_addr();\n\n        let io = self.bus.bp_read8(base + AI_IOCTRL_OFFSET).await;\n        if io & (AI_IOCTRL_BIT_FGC | AI_IOCTRL_BIT_CLOCK_EN) != AI_IOCTRL_BIT_CLOCK_EN {\n            debug!(\"core_is_up: returning false due to bad ioctrl {:02x}\", io);\n            return false;\n        }\n\n        let r = self.bus.bp_read8(base + AI_RESETCTRL_OFFSET).await;\n        if r & (AI_RESETCTRL_BIT_RESET) != 0 {\n            debug!(\"core_is_up: returning false due to bad resetctrl {:02x}\", r);\n            return false;\n        }\n\n        true\n    }\n}\n"
  },
  {
    "path": "cyw43/src/sdio.rs",
    "content": "use core::slice;\n\nuse aligned::{A4, Aligned};\nuse embassy_time::{Duration, Timer};\n\nuse crate::consts::*;\nuse crate::runner::{BusType, SealedBus};\nuse crate::util::{aligned_mut, aligned_ref, slice32_mut, slice32_ref, try_until};\n\n// macro_rules! ALIGN_UINT {\n//     ($val:expr, $align:expr) => {\n//         ((($val) + ($align) - 1) & !(($align) - 1))\n//     };\n// }\n//\n// macro_rules! WRITE_BYTES_PAD {\n//     ($len:expr) => {\n//         ALIGN_UINT!($len, 64)\n//     };\n// }\n\n#[derive(Clone, Copy, PartialEq, Debug)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\n#[cfg_attr(feature = \"log\", derive(derive_more::Display))]\nenum Mode {\n    Block,\n    Byte,\n}\n\n#[derive(Clone, Copy, PartialEq, Debug)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\n#[cfg_attr(feature = \"log\", derive(derive_more::Display))]\nenum Word {\n    U8,\n    U16,\n    U32,\n}\n\nconst BLOCK_SIZE: usize = BACKPLANE_MAX_TRANSFER_SIZE;\n\nfn cmd53_arg(write: bool, func: u32, addr: u32, mode: Mode, len: usize) -> u32 {\n    let (len, block_mode) = match mode {\n        Mode::Block => (len / BLOCK_SIZE, 1u32),\n        Mode::Byte => (len, 0u32),\n    };\n\n    let op_code = 1;\n\n    (write as u32) << 31 | func << 28 | block_mode << 27 | op_code << 26 | (addr & 0x1ffff) << 9 | len as u32\n}\n\n/// Custom Spi Trait that _only_ supports the bus operation of the cyw43\n/// Implementors are expected to hold the CS pin low during an operation.\npub trait SdioBusCyw43<const SIZE: usize> {\n    /// The error type for the BlockDevice implementation.\n    type Error: core::fmt::Debug;\n\n    /// Set the bus to high speed 4-bit frequency\n    fn set_bus_to_high_speed(&mut self, frequency: u32) -> Result<(), Self::Error>;\n\n    /// Issue CMD52\n    async fn cmd52(&mut self, arg: u32) -> Result<u16, Self::Error>;\n\n    /// Issue CMD53 in block read mode\n    async fn cmd53_block_read(&mut self, arg: u32, blocks: &mut [Aligned<A4, [u8; SIZE]>]) -> Result<(), Self::Error>;\n\n    /// Issue CMD53 in byte read mode\n    async fn cmd53_byte_read(&mut self, arg: u32, buffer: &mut Aligned<A4, [u8]>) -> Result<(), Self::Error>;\n\n    /// Issue CMD53 in block write mode\n    async fn cmd53_block_write(&mut self, arg: u32, blocks: &[Aligned<A4, [u8; SIZE]>]) -> Result<(), Self::Error>;\n\n    /// Issue CMD53 in byte write mode\n    async fn cmd53_byte_write(&mut self, arg: u32, buffer: &Aligned<A4, [u8]>) -> Result<(), Self::Error>;\n\n    /// Wait for events from the Device. A typical implementation would wait for the IRQ pin to be high.\n    /// The default implementation always reports ready, resulting in active polling of the device.\n    async fn wait_for_event(&mut self) {\n        Timer::after_millis(800).await;\n    }\n}\n\nimpl<const SIZE: usize, T: SdioBusCyw43<SIZE>> SdioBusCyw43<SIZE> for &mut T {\n    type Error = T::Error;\n\n    fn set_bus_to_high_speed(&mut self, frequency: u32) -> Result<(), Self::Error> {\n        T::set_bus_to_high_speed(self, frequency)\n    }\n\n    async fn cmd52(&mut self, arg: u32) -> Result<u16, Self::Error> {\n        T::cmd52(self, arg).await\n    }\n\n    async fn cmd53_block_read(&mut self, arg: u32, blocks: &mut [Aligned<A4, [u8; SIZE]>]) -> Result<(), Self::Error> {\n        T::cmd53_block_read(self, arg, blocks).await\n    }\n\n    async fn cmd53_byte_read(&mut self, arg: u32, buffer: &mut Aligned<A4, [u8]>) -> Result<(), Self::Error> {\n        T::cmd53_byte_read(self, arg, buffer).await\n    }\n\n    async fn cmd53_block_write(&mut self, arg: u32, blocks: &[Aligned<A4, [u8; SIZE]>]) -> Result<(), Self::Error> {\n        T::cmd53_block_write(self, arg, blocks).await\n    }\n\n    async fn cmd53_byte_write(&mut self, arg: u32, buffer: &Aligned<A4, [u8]>) -> Result<(), Self::Error> {\n        T::cmd53_byte_write(self, arg, buffer).await\n    }\n\n    async fn wait_for_event(&mut self) {\n        T::wait_for_event(self).await\n    }\n}\n\n/// Doc\npub struct SdioBus<SDIO> {\n    backplane_window: u32,\n    sdio: SDIO,\n}\n\nimpl<SDIO> SdioBus<SDIO>\nwhere\n    SDIO: SdioBusCyw43<BLOCK_SIZE>,\n{\n    pub(crate) fn new(sdio: SDIO) -> Self {\n        Self {\n            backplane_window: 0xAAAA_AAAA,\n            sdio,\n        }\n    }\n\n    async fn backplane_readn(&mut self, addr: u32, word: Word) -> u32 {\n        trace!(\"backplane_readn addr = {:08x} len = {}\", addr, word);\n\n        self.backplane_set_window(addr).await;\n\n        let mut bus_addr = addr & BACKPLANE_ADDRESS_MASK;\n        if word == Word::U32 {\n            bus_addr |= BACKPLANE_ADDRESS_32BIT_FLAG;\n        }\n\n        let val = match word {\n            Word::U8 => self.read8(FUNC_BACKPLANE, bus_addr).await as u32,\n            Word::U16 => self.read16(FUNC_BACKPLANE, bus_addr).await as u32,\n            Word::U32 => self.read32(FUNC_BACKPLANE, bus_addr).await,\n        };\n\n        trace!(\"backplane_readn addr = {:08x} len = {} val = {:08x}\", addr, word, val);\n\n        self.backplane_set_window(CHIPCOMMON_BASE_ADDRESS).await;\n\n        return val;\n    }\n\n    async fn backplane_writen(&mut self, addr: u32, val: u32, word: Word) {\n        trace!(\"backplane_writen addr = {:08x} len = {} val = {:08x}\", addr, word, val);\n\n        self.backplane_set_window(addr).await;\n\n        let mut bus_addr = addr & BACKPLANE_ADDRESS_MASK;\n        if word == Word::U32 {\n            bus_addr |= BACKPLANE_ADDRESS_32BIT_FLAG;\n        }\n\n        let _ = match word {\n            Word::U8 => self.write8(FUNC_BACKPLANE, bus_addr, val.try_into().unwrap()).await,\n            Word::U16 => self.write16(FUNC_BACKPLANE, bus_addr, val.try_into().unwrap()).await,\n            Word::U32 => self.write32(FUNC_BACKPLANE, bus_addr, val).await,\n        };\n\n        self.backplane_set_window(CHIPCOMMON_BASE_ADDRESS).await;\n    }\n\n    async fn backplane_set_window(&mut self, addr: u32) {\n        let new_window = addr & !BACKPLANE_ADDRESS_MASK;\n\n        if (new_window >> 24) as u8 != (self.backplane_window >> 24) as u8 {\n            self.write8(\n                FUNC_BACKPLANE,\n                REG_BACKPLANE_BACKPLANE_ADDRESS_HIGH,\n                (new_window >> 24) as u8,\n            )\n            .await;\n        }\n        if (new_window >> 16) as u8 != (self.backplane_window >> 16) as u8 {\n            self.write8(\n                FUNC_BACKPLANE,\n                REG_BACKPLANE_BACKPLANE_ADDRESS_MID,\n                (new_window >> 16) as u8,\n            )\n            .await;\n        }\n        if (new_window >> 8) as u8 != (self.backplane_window >> 8) as u8 {\n            self.write8(\n                FUNC_BACKPLANE,\n                REG_BACKPLANE_BACKPLANE_ADDRESS_LOW,\n                (new_window >> 8) as u8,\n            )\n            .await;\n        }\n        self.backplane_window = new_window;\n    }\n\n    async fn cmd52(&mut self, write: bool, func: u32, addr: u32, val: u8) -> u8 {\n        let arg: u32 = (write as u32) << 31 | func << 28 | (addr & 0x1ffff) << 9 | (val as u32 & 0xff);\n\n        let result = self.sdio.cmd52(arg).await.unwrap_or(u16::MAX) as u8;\n\n        // trace!(\"cmd52: 0x{:08x} 0x{:08x}\", arg, result);\n\n        result\n    }\n\n    async fn cmd53_write(&mut self, func: u32, mut addr: u32, buf: &Aligned<A4, [u8]>) {\n        let byte_part = size_of_val(buf) % BLOCK_SIZE;\n        let block_part = size_of_val(buf) - byte_part;\n\n        if block_part > 0 {\n            let buf = &buf[..block_part];\n\n            if self\n                .sdio\n                .cmd53_block_write(cmd53_arg(true, func, addr, Mode::Block, buf.len()), unsafe {\n                    slice::from_raw_parts(buf.as_ptr() as *mut _, size_of_val(buf) / BLOCK_SIZE)\n                })\n                .await\n                .is_err()\n            {\n                debug!(\"cmd53 block read failed\");\n            }\n\n            addr += block_part as u32;\n        }\n\n        if byte_part > 0 {\n            let buf = &buf[block_part..];\n\n            if self\n                .sdio\n                .cmd53_byte_write(cmd53_arg(true, func, addr, Mode::Byte, buf.len()), buf)\n                .await\n                .is_err()\n            {\n                debug!(\"cmd53 byte read failed (size: {})\", size_of_val(buf));\n            }\n        }\n    }\n\n    async fn cmd53_read(&mut self, func: u32, mut addr: u32, buf: &mut Aligned<A4, [u8]>) {\n        let byte_part = size_of_val(buf) % BLOCK_SIZE;\n        let block_part = size_of_val(buf) - byte_part;\n\n        if block_part > 0 {\n            let buf = &mut buf[..block_part];\n\n            if self\n                .sdio\n                .cmd53_block_read(cmd53_arg(false, func, addr, Mode::Block, buf.len()), unsafe {\n                    slice::from_raw_parts_mut(buf.as_mut_ptr() as *mut _, size_of_val(buf) / BLOCK_SIZE)\n                })\n                .await\n                .is_err()\n            {\n                debug!(\"cmd53 block read failed\");\n            }\n\n            addr += block_part as u32;\n        }\n\n        if byte_part > 0 {\n            let buf = &mut buf[block_part..];\n\n            if self\n                .sdio\n                .cmd53_byte_read(cmd53_arg(false, func, addr, Mode::Byte, buf.len()), buf)\n                .await\n                .is_err()\n            {\n                debug!(\"cmd53 byte read failed (size: {})\", size_of_val(buf));\n            }\n        }\n    }\n}\n\nimpl<SDIO> SealedBus for SdioBus<SDIO>\nwhere\n    SDIO: SdioBusCyw43<64>,\n{\n    const TYPE: BusType = BusType::Sdio;\n\n    async fn init(&mut self, _bluetooth_enabled: bool) {\n        // whd_bus_sdio_init\n\n        // set up backplane\n        if !try_until(\n            async || {\n                self.write8(FUNC_BUS, SDIOD_CCCR_IOEN, SDIO_FUNC_ENABLE_1 as u8).await;\n\n                self.read8(FUNC_BUS, SDIOD_CCCR_IOEN).await as u32 == SDIO_FUNC_ENABLE_1\n            },\n            Duration::from_millis(500),\n        )\n        .await\n        {\n            debug!(\"timeout while setting up the backplane\");\n            return;\n        }\n\n        debug!(\"backplane is up\");\n\n        // Read the bus width and set to 4 bits (1-bit bus is not currently supported)\n        let reg = self.read8(FUNC_BUS, SDIOD_CCCR_BICTRL).await as u32;\n\n        self.write8(\n            FUNC_BUS,\n            SDIOD_CCCR_BICTRL,\n            ((reg & !BUS_SD_DATA_WIDTH_MASK) | BUS_SD_DATA_WIDTH_4BIT) as u8,\n        )\n        .await;\n\n        // Set the block size\n        if !try_until(\n            async || {\n                self.write8(FUNC_BUS, SDIOD_CCCR_BLKSIZE_0, SDIO_64B_BLOCK as u8).await;\n\n                self.read8(FUNC_BUS, SDIOD_CCCR_BLKSIZE_0).await as u32 == SDIO_64B_BLOCK\n            },\n            Duration::from_millis(500),\n        )\n        .await\n        {\n            debug!(\"timeout while setting block size\");\n            return;\n        }\n\n        self.write8(FUNC_BUS, SDIOD_CCCR_BLKSIZE_0, SDIO_64B_BLOCK as u8).await;\n        self.write8(FUNC_BUS, SDIOD_CCCR_F1BLKSIZE_0, SDIO_64B_BLOCK as u8)\n            .await;\n        self.write8(FUNC_BUS, SDIOD_CCCR_F2BLKSIZE_0, SDIO_64B_BLOCK as u8)\n            .await;\n        self.write8(FUNC_BUS, SDIOD_CCCR_F2BLKSIZE_1, 0).await;\n\n        // Enable/Disable Client interrupts\n        self.write8(\n            FUNC_BUS,\n            SDIOD_CCCR_INTEN,\n            (INTR_CTL_MASTER_EN | INTR_CTL_FUNC1_EN | INTR_CTL_FUNC2_EN) as u8,\n        )\n        .await;\n\n        self.sdio.set_bus_to_high_speed(25_000_000).unwrap();\n\n        // enable more than 25MHz bus\n        let reg = self.read8(FUNC_BUS, SDIOD_CCCR_SPEED_CONTROL).await as u32;\n        if reg & 1 != 0 {\n            self.write8(FUNC_BUS, SDIOD_CCCR_SPEED_CONTROL, (reg | SDIO_SPEED_EHS) as u8)\n                .await;\n\n            self.sdio.set_bus_to_high_speed(50_000_000).unwrap();\n        }\n\n        // Wait till the backplane is ready\n        if !try_until(\n            async || self.read8(FUNC_BUS, SDIOD_CCCR_IORDY).await as u32 & SDIO_FUNC_READY_1 != 0,\n            Duration::from_millis(500),\n        )\n        .await\n        {\n            debug!(\"timeout while waiting for backplane to be ready\");\n            return;\n        }\n    }\n\n    async fn wlan_read(&mut self, buf: &mut Aligned<A4, [u8]>) {\n        self.cmd53_read(FUNC_WLAN, 0, buf).await;\n    }\n\n    async fn wlan_write(&mut self, buf: &Aligned<A4, [u8]>) {\n        self.cmd53_write(FUNC_WLAN, 0, buf).await;\n    }\n\n    #[allow(unused)]\n    async fn bp_read(&mut self, mut addr: u32, mut data: &mut [u8]) {\n        trace!(\"bp_read addr = {:08x}, len = {}\", addr, data.len());\n\n        // It seems the HW force-aligns the addr\n        // to 2 if data.len() >= 2\n        // to 4 if data.len() >= 4\n        // To simplify, enforce 4-align for now.\n        assert!(addr % 4 == 0);\n        assert!(data.as_ptr() as u32 % 4 == 0);\n\n        // align buffer len\n        // note: safe because align is checked above\n        let mut data = aligned_mut(slice32_mut(unsafe { core::mem::transmute(data) }));\n\n        while !data.is_empty() {\n            // Ensure transfer doesn't cross a window boundary.\n            let window_offs = addr & BACKPLANE_ADDRESS_MASK;\n            let window_remaining = BACKPLANE_WINDOW_SIZE - window_offs as usize;\n\n            let len = data.len().min(BLOCK_BUFFER_SIZE).min(window_remaining);\n            let buf = &mut data[..len];\n\n            self.backplane_set_window(addr).await;\n\n            self.cmd53_read(FUNC_BACKPLANE, addr & BACKPLANE_ADDRESS_MASK as u32, buf)\n                .await;\n\n            // Advance ptr.\n            addr += len as u32;\n            data = &mut data[len..];\n        }\n\n        self.backplane_set_window(CHIPCOMMON_BASE_ADDRESS).await;\n    }\n\n    /// A.K.A. cyw43_download_resource\n    async fn bp_write(&mut self, mut addr: u32, data: &[u8]) {\n        trace!(\"bp_write addr = {:08x}, len = {}\", addr, data.len());\n\n        // It seems the HW force-aligns the addr\n        // to 2 if data.len() >= 2\n        // to 4 if data.len() >= 4\n        // To simplify, enforce 4-align for now.\n        assert!(addr % 4 == 0);\n        assert!(data.as_ptr() as u32 % 4 == 0);\n\n        // align buffer len\n        // note: safe because align is checked above\n        let mut data = aligned_ref(slice32_ref(unsafe { core::mem::transmute(data) }));\n\n        while !data.is_empty() {\n            // Ensure transfer doesn't cross a window boundary.\n            let window_offs = addr & BACKPLANE_ADDRESS_MASK;\n            let window_remaining = BACKPLANE_WINDOW_SIZE - window_offs as usize;\n\n            let len = data.len().min(BLOCK_BUFFER_SIZE).min(window_remaining);\n            let buf = &data[..len];\n\n            self.backplane_set_window(addr).await;\n\n            self.cmd53_write(FUNC_BACKPLANE, addr & BACKPLANE_ADDRESS_MASK as u32, buf)\n                .await;\n\n            // Advance ptr.\n            addr += len as u32;\n            data = &data[len..];\n        }\n\n        self.backplane_set_window(CHIPCOMMON_BASE_ADDRESS).await;\n\n        // TODO: implement verify download\n    }\n\n    async fn bp_read8(&mut self, addr: u32) -> u8 {\n        self.backplane_readn(addr, Word::U8).await as u8\n    }\n\n    async fn bp_write8(&mut self, addr: u32, val: u8) {\n        self.backplane_writen(addr, val as u32, Word::U8).await\n    }\n\n    async fn bp_read16(&mut self, addr: u32) -> u16 {\n        self.backplane_readn(addr, Word::U16).await as u16\n    }\n\n    #[allow(unused)]\n    async fn bp_write16(&mut self, addr: u32, val: u16) {\n        self.backplane_writen(addr, val as u32, Word::U16).await\n    }\n\n    #[allow(unused)]\n    async fn bp_read32(&mut self, addr: u32) -> u32 {\n        self.backplane_readn(addr, Word::U32).await\n    }\n\n    async fn bp_write32(&mut self, addr: u32, val: u32) {\n        self.backplane_writen(addr, val, Word::U32).await\n    }\n\n    async fn read8(&mut self, func: u32, addr: u32) -> u8 {\n        self.cmd52(false, func, addr, 0).await.into()\n    }\n\n    async fn write8(&mut self, func: u32, addr: u32, val: u8) {\n        self.cmd52(true, func, addr, val).await;\n    }\n\n    async fn read16(&mut self, func: u32, addr: u32) -> u16 {\n        let mut val = [0u32];\n        self.cmd53_read(func, addr, &mut aligned_mut(&mut val)[..2]).await;\n\n        u16::from_be_bytes(val[0].to_be_bytes()[..2].try_into().unwrap())\n    }\n\n    #[allow(unused)]\n    async fn write16(&mut self, func: u32, addr: u32, val: u16) {\n        self.cmd53_write(func, addr, &aligned_ref(&[val as u32])[..2]).await;\n    }\n\n    async fn read32(&mut self, func: u32, addr: u32) -> u32 {\n        let mut val = [0u32];\n        self.cmd53_read(func, addr, &mut aligned_mut(&mut val)).await;\n\n        val[0]\n    }\n\n    #[allow(unused)]\n    async fn write32(&mut self, func: u32, addr: u32, val: u32) {\n        self.cmd53_write(func, addr, &aligned_ref(&[val])).await;\n    }\n\n    async fn wait_for_event(&mut self) {\n        Timer::after(Duration::from_millis(10)).await;\n        // self.sdio.wait_for_event().await;\n    }\n}\n"
  },
  {
    "path": "cyw43/src/spi.rs",
    "content": "use aligned::{A4, Aligned};\nuse embassy_futures::yield_now;\nuse embassy_time::Timer;\nuse embedded_hal_1::digital::OutputPin;\nuse futures::FutureExt;\n\nuse crate::consts::*;\nuse crate::runner::{BusType, SealedBus};\nuse crate::util::{slice8_mut, slice32_mut, slice32_ref};\n\n/// Custom Spi Trait that _only_ supports the bus operation of the cyw43\n/// Implementors are expected to hold the CS pin low during an operation.\npub trait SpiBusCyw43 {\n    /// Issues a write command on the bus\n    /// First 32 bits of `word` are expected to be a cmd word\n    async fn cmd_write(&mut self, write: &[u32]) -> u32;\n\n    /// Issues a read command on the bus\n    /// `write` is expected to be a 32 bit cmd word\n    /// `read` will contain the response of the device\n    /// Backplane reads have a response delay that produces one extra unspecified word at the beginning of `read`.\n    /// Callers that want to read `n` word from the backplane, have to provide a slice that is `n+1` words long.\n    async fn cmd_read(&mut self, write: u32, read: &mut [u32]) -> u32;\n\n    /// Wait for events from the Device. A typical implementation would wait for the IRQ pin to be high.\n    /// The default implementation always reports ready, resulting in active polling of the device.\n    async fn wait_for_event(&mut self) {\n        yield_now().await;\n    }\n}\n\n/// Doc\npub struct SpiBus<PWR, SPI> {\n    backplane_window: u32,\n    pwr: PWR,\n    spi: SPI,\n    status: u32,\n}\n\nimpl<PWR, SPI> SpiBus<PWR, SPI>\nwhere\n    PWR: OutputPin,\n    SPI: SpiBusCyw43,\n{\n    pub(crate) fn new(pwr: PWR, spi: SPI) -> Self {\n        Self {\n            backplane_window: 0xAAAA_AAAA,\n            pwr,\n            spi,\n            status: 0,\n        }\n    }\n\n    async fn backplane_readn(&mut self, addr: u32, len: u32) -> u32 {\n        trace!(\"backplane_readn addr = {:08x} len = {}\", addr, len);\n\n        self.backplane_set_window(addr).await;\n\n        let mut bus_addr = addr & BACKPLANE_ADDRESS_MASK;\n        if len == 4 {\n            bus_addr |= BACKPLANE_ADDRESS_32BIT_FLAG;\n        }\n\n        let val = self.readn(FUNC_BACKPLANE, bus_addr, len).await;\n\n        trace!(\"backplane_readn addr = {:08x} len = {} val = {:08x}\", addr, len, val);\n\n        return val;\n    }\n\n    async fn backplane_writen(&mut self, addr: u32, val: u32, len: u32) {\n        trace!(\"backplane_writen addr = {:08x} len = {} val = {:08x}\", addr, len, val);\n\n        self.backplane_set_window(addr).await;\n\n        let mut bus_addr = addr & BACKPLANE_ADDRESS_MASK;\n        if len == 4 {\n            bus_addr |= BACKPLANE_ADDRESS_32BIT_FLAG;\n        }\n        self.writen(FUNC_BACKPLANE, bus_addr, val, len).await;\n    }\n\n    async fn backplane_set_window(&mut self, addr: u32) {\n        let new_window = addr & !BACKPLANE_ADDRESS_MASK;\n\n        if (new_window >> 24) as u8 != (self.backplane_window >> 24) as u8 {\n            self.write8(\n                FUNC_BACKPLANE,\n                REG_BACKPLANE_BACKPLANE_ADDRESS_HIGH,\n                (new_window >> 24) as u8,\n            )\n            .await;\n        }\n        if (new_window >> 16) as u8 != (self.backplane_window >> 16) as u8 {\n            self.write8(\n                FUNC_BACKPLANE,\n                REG_BACKPLANE_BACKPLANE_ADDRESS_MID,\n                (new_window >> 16) as u8,\n            )\n            .await;\n        }\n        if (new_window >> 8) as u8 != (self.backplane_window >> 8) as u8 {\n            self.write8(\n                FUNC_BACKPLANE,\n                REG_BACKPLANE_BACKPLANE_ADDRESS_LOW,\n                (new_window >> 8) as u8,\n            )\n            .await;\n        }\n        self.backplane_window = new_window;\n    }\n\n    async fn readn(&mut self, func: u32, addr: u32, len: u32) -> u32 {\n        let cmd = cmd_word(READ, INC_ADDR, func, addr, len);\n        let mut buf = [0; 2];\n        // if we are reading from the backplane, we need an extra word for the response delay\n        let len = if func == FUNC_BACKPLANE { 2 } else { 1 };\n\n        self.status = self.spi.cmd_read(cmd, &mut buf[..len]).await;\n\n        // if we read from the backplane, the result is in the second word, after the response delay\n        if func == FUNC_BACKPLANE { buf[1] } else { buf[0] }\n    }\n\n    async fn writen(&mut self, func: u32, addr: u32, val: u32, len: u32) {\n        let cmd = cmd_word(WRITE, INC_ADDR, func, addr, len);\n\n        self.status = self.spi.cmd_write(&[cmd, val]).await;\n    }\n\n    async fn read32_swapped(&mut self, func: u32, addr: u32) -> u32 {\n        let cmd = cmd_word(READ, INC_ADDR, func, addr, 4);\n        let cmd = swap16(cmd);\n        let mut buf = [0; 1];\n\n        self.status = self.spi.cmd_read(cmd, &mut buf).await;\n\n        swap16(buf[0])\n    }\n\n    async fn write32_swapped(&mut self, func: u32, addr: u32, val: u32) {\n        let cmd = cmd_word(WRITE, INC_ADDR, func, addr, 4);\n        let buf = [swap16(cmd), swap16(val)];\n\n        self.status = self.spi.cmd_write(&buf).await;\n    }\n}\n\nimpl<PWR, SPI> SealedBus for SpiBus<PWR, SPI>\nwhere\n    PWR: OutputPin,\n    SPI: SpiBusCyw43,\n{\n    const TYPE: BusType = BusType::Spi;\n\n    async fn init(&mut self, bluetooth_enabled: bool) {\n        // Reset\n        trace!(\"WL_REG off/on\");\n        self.pwr.set_low().unwrap();\n        Timer::after_millis(20).await;\n        self.pwr.set_high().unwrap();\n        Timer::after_millis(250).await;\n\n        trace!(\"read REG_BUS_TEST_RO\");\n        while self\n            .read32_swapped(FUNC_BUS, REG_BUS_TEST_RO)\n            .inspect(|v| trace!(\"{:#x}\", v))\n            .await\n            != FEEDBEAD\n        {}\n\n        trace!(\"write REG_BUS_TEST_RW\");\n        self.write32_swapped(FUNC_BUS, REG_BUS_TEST_RW, TEST_PATTERN).await;\n        let val = self.read32_swapped(FUNC_BUS, REG_BUS_TEST_RW).await;\n        trace!(\"{:#x}\", val);\n        assert_eq!(val, TEST_PATTERN);\n\n        trace!(\"read REG_BUS_CTRL\");\n        let val = self.read32_swapped(FUNC_BUS, REG_BUS_CTRL).await;\n        trace!(\"{:#010b}\", (val & 0xff));\n\n        // 32-bit word length, little endian (which is the default endianess).\n        // TODO: C library is uint32_t val = WORD_LENGTH_32 | HIGH_SPEED_MODE| ENDIAN_BIG | INTERRUPT_POLARITY_HIGH | WAKE_UP | 0x4 << (8 * SPI_RESPONSE_DELAY) | INTR_WITH_STATUS << (8 * SPI_STATUS_ENABLE);\n        trace!(\"write REG_BUS_CTRL\");\n        self.write32_swapped(\n            FUNC_BUS,\n            REG_BUS_CTRL,\n            WORD_LENGTH_32\n                | HIGH_SPEED\n                | INTERRUPT_POLARITY_HIGH\n                | WAKE_UP\n                | 0x4 << (8 * REG_BUS_RESPONSE_DELAY)\n                | STATUS_ENABLE << (8 * REG_BUS_STATUS_ENABLE)\n                | INTR_WITH_STATUS << (8 * REG_BUS_STATUS_ENABLE),\n        )\n        .await;\n\n        trace!(\"read REG_BUS_CTRL\");\n        let val = self.read8(FUNC_BUS, REG_BUS_CTRL).await;\n        trace!(\"{:#b}\", val);\n\n        // TODO: C doesn't do this? i doubt it messes anything up\n        trace!(\"read REG_BUS_TEST_RO\");\n        let val = self.read32(FUNC_BUS, REG_BUS_TEST_RO).await;\n        trace!(\"{:#x}\", val);\n        assert_eq!(val, FEEDBEAD);\n\n        // TODO: C doesn't do this? i doubt it messes anything up\n        trace!(\"read REG_BUS_TEST_RW\");\n        let val = self.read32(FUNC_BUS, REG_BUS_TEST_RW).await;\n        trace!(\"{:#x}\", val);\n        assert_eq!(val, TEST_PATTERN);\n\n        trace!(\"write SPI_RESP_DELAY_F1 CYW43_BACKPLANE_READ_PAD_LEN_BYTES\");\n        self.write8(FUNC_BUS, SPI_RESP_DELAY_F1, WHD_BUS_SPI_BACKPLANE_READ_PADD_SIZE)\n            .await;\n\n        // TODO: Make sure error interrupt bits are clear?\n        // cyw43_write_reg_u8(self, BUS_FUNCTION, SPI_INTERRUPT_REGISTER, DATA_UNAVAILABLE | COMMAND_ERROR | DATA_ERROR | F1_OVERFLOW) != 0)\n        trace!(\"Make sure error interrupt bits are clear\");\n        self.write8(\n            FUNC_BUS,\n            REG_BUS_INTERRUPT,\n            (IRQ_DATA_UNAVAILABLE | IRQ_COMMAND_ERROR | IRQ_DATA_ERROR | IRQ_F1_OVERFLOW) as u8,\n        )\n        .await;\n\n        // Enable a selection of interrupts\n        // TODO: why not all of these F2_F3_FIFO_RD_UNDERFLOW | F2_F3_FIFO_WR_OVERFLOW | COMMAND_ERROR | DATA_ERROR | F2_PACKET_AVAILABLE | F1_OVERFLOW | F1_INTR\n        trace!(\"enable a selection of interrupts\");\n        let mut val = IRQ_F2_F3_FIFO_RD_UNDERFLOW\n            | IRQ_F2_F3_FIFO_WR_OVERFLOW\n            | IRQ_COMMAND_ERROR\n            | IRQ_DATA_ERROR\n            | IRQ_F2_PACKET_AVAILABLE\n            | IRQ_F1_OVERFLOW;\n        if bluetooth_enabled {\n            val = val | IRQ_F1_INTR;\n        }\n        self.write16(FUNC_BUS, REG_BUS_INTERRUPT_ENABLE, val).await;\n    }\n\n    async fn wlan_read(&mut self, buf: &mut Aligned<A4, [u8]>) {\n        let len_in_u8 = buf.len() as u32;\n        let buf = slice32_mut(buf);\n\n        let cmd = cmd_word(READ, INC_ADDR, FUNC_WLAN, 0, len_in_u8);\n        let len_in_u32 = (len_in_u8 as usize + 3) / 4;\n\n        self.status = self.spi.cmd_read(cmd, &mut buf[..len_in_u32]).await;\n    }\n\n    async fn wlan_write(&mut self, buf: &Aligned<A4, [u8]>) {\n        let buf = slice32_ref(buf);\n        let cmd = cmd_word(WRITE, INC_ADDR, FUNC_WLAN, 0, buf.len() as u32 * 4);\n        //TODO try to remove copy?\n        let mut cmd_buf = [0_u32; 513];\n        cmd_buf[0] = cmd;\n        cmd_buf[1..][..buf.len()].copy_from_slice(buf);\n\n        self.status = self.spi.cmd_write(&cmd_buf[..buf.len() + 1]).await;\n    }\n\n    #[allow(unused)]\n    async fn bp_read(&mut self, mut addr: u32, mut data: &mut [u8]) {\n        trace!(\"bp_read addr = {:08x}\", addr);\n\n        // It seems the HW force-aligns the addr\n        // to 2 if data.len() >= 2\n        // to 4 if data.len() >= 4\n        // To simplify, enforce 4-align for now.\n        assert!(addr % 4 == 0);\n\n        // Backplane read buffer has one extra word for the response delay.\n        let mut buf = [0u32; BACKPLANE_MAX_TRANSFER_SIZE / 4 + 1];\n\n        while !data.is_empty() {\n            // Ensure transfer doesn't cross a window boundary.\n            let window_offs = addr & BACKPLANE_ADDRESS_MASK;\n            let window_remaining = BACKPLANE_WINDOW_SIZE - window_offs as usize;\n\n            let len = data.len().min(BACKPLANE_MAX_TRANSFER_SIZE).min(window_remaining);\n\n            self.backplane_set_window(addr).await;\n\n            let cmd = cmd_word(READ, INC_ADDR, FUNC_BACKPLANE, window_offs, len as u32);\n\n            // round `buf` to word boundary, add one extra word for the response delay\n            self.status = self.spi.cmd_read(cmd, &mut buf[..(len + 3) / 4 + 1]).await;\n\n            // when writing out the data, we skip the response-delay byte\n            data[..len].copy_from_slice(&slice8_mut(&mut buf[1..])[..len]);\n\n            // Advance ptr.\n            addr += len as u32;\n            data = &mut data[len..];\n        }\n    }\n\n    async fn bp_write(&mut self, mut addr: u32, mut data: &[u8]) {\n        trace!(\"bp_write addr = {:08x}\", addr);\n\n        // It seems the HW force-aligns the addr\n        // to 2 if data.len() >= 2\n        // to 4 if data.len() >= 4\n        // To simplify, enforce 4-align for now.\n        assert!(addr % 4 == 0);\n\n        let mut buf = [0u32; BACKPLANE_MAX_TRANSFER_SIZE / 4 + 1];\n\n        while !data.is_empty() {\n            // Ensure transfer doesn't cross a window boundary.\n            let window_offs = addr & BACKPLANE_ADDRESS_MASK;\n            let window_remaining = BACKPLANE_WINDOW_SIZE - window_offs as usize;\n\n            let len = data.len().min(BACKPLANE_MAX_TRANSFER_SIZE).min(window_remaining);\n            slice8_mut(&mut buf[1..])[..len].copy_from_slice(&data[..len]);\n\n            self.backplane_set_window(addr).await;\n\n            let cmd = cmd_word(WRITE, INC_ADDR, FUNC_BACKPLANE, window_offs, len as u32);\n            buf[0] = cmd;\n\n            self.status = self.spi.cmd_write(&buf[..(len + 3) / 4 + 1]).await;\n\n            // Advance ptr.\n            addr += len as u32;\n            data = &data[len..];\n        }\n    }\n\n    async fn bp_read8(&mut self, addr: u32) -> u8 {\n        self.backplane_readn(addr, 1).await as u8\n    }\n\n    async fn bp_write8(&mut self, addr: u32, val: u8) {\n        self.backplane_writen(addr, val as u32, 1).await\n    }\n\n    async fn bp_read16(&mut self, addr: u32) -> u16 {\n        self.backplane_readn(addr, 2).await as u16\n    }\n\n    #[allow(unused)]\n    async fn bp_write16(&mut self, addr: u32, val: u16) {\n        self.backplane_writen(addr, val as u32, 2).await\n    }\n\n    #[allow(unused)]\n    async fn bp_read32(&mut self, addr: u32) -> u32 {\n        self.backplane_readn(addr, 4).await\n    }\n\n    async fn bp_write32(&mut self, addr: u32, val: u32) {\n        self.backplane_writen(addr, val, 4).await\n    }\n\n    async fn read8(&mut self, func: u32, addr: u32) -> u8 {\n        self.readn(func, addr, 1).await as u8\n    }\n\n    async fn write8(&mut self, func: u32, addr: u32, val: u8) {\n        self.writen(func, addr, val as u32, 1).await\n    }\n\n    async fn read16(&mut self, func: u32, addr: u32) -> u16 {\n        self.readn(func, addr, 2).await as u16\n    }\n\n    #[allow(unused)]\n    async fn write16(&mut self, func: u32, addr: u32, val: u16) {\n        self.writen(func, addr, val as u32, 2).await\n    }\n\n    async fn read32(&mut self, func: u32, addr: u32) -> u32 {\n        if func == FUNC_BUS && addr == SPI_STATUS_REGISTER && self.status != 0 {\n            let status = self.status;\n            self.status = 0;\n\n            status\n        } else {\n            self.readn(func, addr, 4).await\n        }\n    }\n\n    #[allow(unused)]\n    async fn write32(&mut self, func: u32, addr: u32, val: u32) {\n        self.writen(func, addr, val, 4).await\n    }\n\n    async fn wait_for_event(&mut self) {\n        self.spi.wait_for_event().await;\n    }\n}\n\nfn swap16(x: u32) -> u32 {\n    x.rotate_left(16)\n}\n\nfn cmd_word(write: bool, incr: bool, func: u32, addr: u32, len: u32) -> u32 {\n    (write as u32) << 31 | (incr as u32) << 30 | (func & 0b11) << 28 | (addr & 0x1FFFF) << 11 | (len & 0x7FF)\n}\n"
  },
  {
    "path": "cyw43/src/structs.rs",
    "content": "use crate::events::Event;\nuse crate::fmt::Bytes;\n\nmacro_rules! impl_bytes {\n    ($t:ident) => {\n        impl $t {\n            /// Bytes consumed by this type.\n            pub const SIZE: usize = core::mem::size_of::<Self>();\n\n            /// Convert to byte array.\n            #[allow(unused)]\n            pub fn to_bytes(&self) -> [u8; Self::SIZE] {\n                unsafe { core::mem::transmute(*self) }\n            }\n\n            /// Create from byte array.\n            #[allow(unused)]\n            pub fn from_bytes(bytes: &[u8; Self::SIZE]) -> &Self {\n                let alignment = core::mem::align_of::<Self>();\n                assert_eq!(\n                    bytes.as_ptr().align_offset(alignment),\n                    0,\n                    \"{} is not aligned\",\n                    core::any::type_name::<Self>()\n                );\n                unsafe { core::mem::transmute(bytes) }\n            }\n\n            /// Create from mutable byte array.\n            #[allow(unused)]\n            pub fn from_bytes_mut(bytes: &mut [u8; Self::SIZE]) -> &mut Self {\n                let alignment = core::mem::align_of::<Self>();\n                assert_eq!(\n                    bytes.as_ptr().align_offset(alignment),\n                    0,\n                    \"{} is not aligned\",\n                    core::any::type_name::<Self>()\n                );\n\n                unsafe { core::mem::transmute(bytes) }\n            }\n        }\n    };\n}\n\n#[derive(Clone, Copy)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\n#[repr(C)]\npub struct SharedMemData {\n    pub flags: u32,\n    pub trap_addr: u32,\n    pub assert_exp_addr: u32,\n    pub assert_file_addr: u32,\n    pub assert_line: u32,\n    pub console_addr: u32,\n    pub msgtrace_addr: u32,\n    pub fwid: u32,\n}\nimpl_bytes!(SharedMemData);\n\n#[derive(Clone, Copy)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\n#[repr(C)]\npub struct SharedMemLog {\n    pub buf: u32,\n    pub buf_size: u32,\n    pub idx: u32,\n    pub out_idx: u32,\n}\nimpl_bytes!(SharedMemLog);\n\n#[derive(Debug, Clone, Copy)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\n#[repr(C)]\npub struct SdpcmHeader {\n    pub len: u16,\n    pub len_inv: u16,\n    /// Rx/Tx sequence number\n    pub sequence: u8,\n    ///  4 MSB Channel number, 4 LSB arbitrary flag\n    pub channel_and_flags: u8,\n    /// Length of next data frame, reserved for Tx\n    pub next_length: u8,\n    /// Data offset\n    pub header_length: u8,\n    /// Flow control bits, reserved for Tx\n    pub wireless_flow_control: u8,\n    /// Maximum Sequence number allowed by firmware for Tx\n    pub bus_data_credit: u8,\n    /// Reserved\n    pub reserved: [u8; 2],\n}\nimpl_bytes!(SdpcmHeader);\n\nimpl SdpcmHeader {\n    pub fn parse(packet: &mut [u8]) -> Option<(&mut Self, &mut [u8])> {\n        let packet_len = packet.len();\n        if packet_len < Self::SIZE {\n            warn!(\"packet too short, len={}\", packet.len());\n            return None;\n        }\n        let (sdpcm_header, sdpcm_packet) = packet.split_at_mut(Self::SIZE);\n        let sdpcm_header = Self::from_bytes_mut(sdpcm_header.try_into().unwrap());\n        trace!(\"rx {:?}\", sdpcm_header);\n\n        if sdpcm_header.len != !sdpcm_header.len_inv {\n            warn!(\"len inv mismatch\");\n            return None;\n        }\n\n        if sdpcm_header.len as usize != packet_len {\n            warn!(\"len from header doesn't match len from spi\");\n            return None;\n        }\n\n        let sdpcm_packet = &mut sdpcm_packet[(sdpcm_header.header_length as usize - Self::SIZE)..];\n        Some((sdpcm_header, sdpcm_packet))\n    }\n}\n\n#[derive(Debug, Clone, Copy)]\n#[repr(C, packed(2))]\npub struct CdcHeader {\n    pub cmd: u32,\n    pub len: u32,\n    pub flags: u16,\n    pub id: u16,\n    pub status: u32,\n}\nimpl_bytes!(CdcHeader);\n\n#[cfg(feature = \"defmt\")]\nimpl defmt::Format for CdcHeader {\n    fn format(&self, fmt: defmt::Formatter) {\n        fn copy<T: Copy>(t: T) -> T {\n            t\n        }\n\n        defmt::write!(\n            fmt,\n            \"CdcHeader{{cmd: {=u32:08x}, len: {=u32:08x}, flags: {=u16:04x}, id: {=u16:04x}, status: {=u32:08x}}}\",\n            copy(self.cmd),\n            copy(self.len),\n            copy(self.flags),\n            copy(self.id),\n            copy(self.status),\n        )\n    }\n}\n\nimpl CdcHeader {\n    pub fn parse(packet: &mut [u8]) -> Option<(&mut Self, &mut [u8])> {\n        if packet.len() < Self::SIZE {\n            warn!(\"payload too short, len={}\", packet.len());\n            return None;\n        }\n\n        let (cdc_header, payload) = packet.split_at_mut(Self::SIZE);\n        let cdc_header = Self::from_bytes_mut(cdc_header.try_into().unwrap());\n\n        let payload = &mut payload[..cdc_header.len as usize];\n        Some((cdc_header, payload))\n    }\n}\n\npub const BDC_VERSION: u8 = 2;\npub const BDC_VERSION_SHIFT: u8 = 4;\n\n#[derive(Debug, Clone, Copy)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\n#[repr(C)]\npub struct BdcHeader {\n    pub flags: u8,\n    /// 802.1d Priority (low 3 bits)\n    pub priority: u8,\n    pub flags2: u8,\n    /// Offset from end of BDC header to packet data, in 4-uint8_t words. Leaves room for optional headers.\n    pub data_offset: u8,\n}\nimpl_bytes!(BdcHeader);\n\nimpl BdcHeader {\n    pub fn parse(packet: &mut [u8]) -> Option<(&mut Self, &mut [u8])> {\n        if packet.len() < Self::SIZE {\n            return None;\n        }\n\n        let (bdc_header, bdc_packet) = packet.split_at_mut(Self::SIZE);\n        let bdc_header = Self::from_bytes_mut(bdc_header.try_into().unwrap());\n        trace!(\"    {:?}\", bdc_header);\n\n        let packet_start = 4 * bdc_header.data_offset as usize;\n\n        let bdc_packet = bdc_packet.get_mut(packet_start..)?;\n        trace!(\"    {:02x}\", Bytes(&bdc_packet[..bdc_packet.len().min(36)]));\n\n        Some((bdc_header, bdc_packet))\n    }\n}\n\n#[derive(Clone, Copy)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\n#[repr(C)]\npub struct EthernetHeader {\n    pub destination_mac: [u8; 6],\n    pub source_mac: [u8; 6],\n    pub ether_type: u16,\n}\n\nimpl EthernetHeader {\n    /// Swap endianness.\n    pub fn byteswap(&mut self) {\n        self.ether_type = self.ether_type.to_be();\n    }\n}\n\n#[derive(Clone, Copy)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\n#[repr(C)]\npub struct EventHeader {\n    pub subtype: u16,\n    pub length: u16,\n    pub version: u8,\n    pub oui: [u8; 3],\n    pub user_subtype: u16,\n}\n\nimpl EventHeader {\n    pub fn byteswap(&mut self) {\n        self.subtype = self.subtype.to_be();\n        self.length = self.length.to_be();\n        self.user_subtype = self.user_subtype.to_be();\n    }\n}\n\n#[derive(Debug, Clone, Copy)]\n// #[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\n#[repr(C, packed(2))]\npub struct EventMessage {\n    /// version   \n    pub version: u16,\n    /// see flags below\n    pub flags: u16,\n    /// Message (see below)\n    pub event_type: u32,\n    /// Status code (see below)\n    pub status: u32,\n    /// Reason code (if applicable)\n    pub reason: u32,\n    /// WLC_E_AUTH\n    pub auth_type: u32,\n    /// data buf\n    pub datalen: u32,\n    /// Station address (if applicable)\n    pub addr: [u8; 6],\n    /// name of the incoming packet interface\n    pub ifname: [u8; 16],\n    /// destination OS i/f index\n    pub ifidx: u8,\n    /// source bsscfg index\n    pub bsscfgidx: u8,\n}\nimpl_bytes!(EventMessage);\n\n#[cfg(feature = \"defmt\")]\nimpl defmt::Format for EventMessage {\n    fn format(&self, fmt: defmt::Formatter) {\n        let event_type = self.event_type;\n        let status = self.status;\n        let reason = self.reason;\n        let auth_type = self.auth_type;\n        let datalen = self.datalen;\n\n        defmt::write!(\n            fmt,\n            \"EventMessage {{ \\\n            version: {=u16}, \\\n            flags: {=u16}, \\\n            event_type: {=u32}, \\\n            status: {=u32}, \\\n            reason: {=u32}, \\\n            auth_type: {=u32}, \\\n            datalen: {=u32}, \\\n            addr: {=[u8; 6]:x}, \\\n            ifname: {=[u8; 16]:x}, \\\n            ifidx: {=u8}, \\\n            bsscfgidx: {=u8}, \\\n        }} \",\n            self.version,\n            self.flags,\n            event_type,\n            status,\n            reason,\n            auth_type,\n            datalen,\n            self.addr,\n            self.ifname,\n            self.ifidx,\n            self.bsscfgidx\n        );\n    }\n}\n\nimpl EventMessage {\n    pub fn byteswap(&mut self) {\n        self.version = self.version.to_be();\n        self.flags = self.flags.to_be();\n        self.event_type = self.event_type.to_be();\n        self.status = self.status.to_be();\n        self.reason = self.reason.to_be();\n        self.auth_type = self.auth_type.to_be();\n        self.datalen = self.datalen.to_be();\n    }\n}\n\n#[derive(Clone, Copy)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\n#[repr(C, packed(2))]\npub struct EventPacket {\n    pub eth: EthernetHeader,\n    pub hdr: EventHeader,\n    pub msg: EventMessage,\n}\nimpl_bytes!(EventPacket);\n\nimpl EventPacket {\n    pub fn parse(packet: &mut [u8]) -> Option<(&mut Self, &mut [u8])> {\n        if packet.len() < Self::SIZE {\n            return None;\n        }\n\n        let (event_header, event_packet) = packet.split_at_mut(Self::SIZE);\n        let event_header = Self::from_bytes_mut(event_header.try_into().unwrap());\n        // warn!(\"event_header {:x}\", event_header as *const _);\n        event_header.byteswap();\n\n        let event_packet = event_packet.get_mut(..event_header.msg.datalen as usize)?;\n\n        Some((event_header, event_packet))\n    }\n\n    pub fn byteswap(&mut self) {\n        self.eth.byteswap();\n        self.hdr.byteswap();\n        self.msg.byteswap();\n    }\n}\n\n#[derive(Clone, Copy)]\n#[repr(C)]\npub struct DownloadHeader {\n    pub flag: u16, //\n    pub dload_type: u16,\n    pub len: u32,\n    pub crc: u32,\n}\nimpl_bytes!(DownloadHeader);\n\n#[allow(unused)]\npub const DOWNLOAD_FLAG_NO_CRC: u16 = 0x0001;\npub const DOWNLOAD_FLAG_BEGIN: u16 = 0x0002;\npub const DOWNLOAD_FLAG_END: u16 = 0x0004;\npub const DOWNLOAD_FLAG_HANDLER_VER: u16 = 0x1000;\n\n// Country Locale Matrix (CLM)\npub const DOWNLOAD_TYPE_CLM: u16 = 2;\n\n#[derive(Clone, Copy)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\n#[repr(C)]\npub struct CountryInfo {\n    pub country_abbrev: [u8; 4],\n    pub rev: i32,\n    pub country_code: [u8; 4],\n}\nimpl_bytes!(CountryInfo);\n\n#[derive(Clone, Copy)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\n#[repr(C)]\npub struct SsidInfo {\n    pub len: u32,\n    pub ssid: [u8; 32],\n}\nimpl_bytes!(SsidInfo);\n\n#[derive(Clone, Copy)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\n#[repr(C)]\npub struct PassphraseInfo {\n    pub len: u16,\n    pub flags: u16,\n    pub passphrase: [u8; 64],\n}\nimpl_bytes!(PassphraseInfo);\n\n#[derive(Clone, Copy)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\n#[repr(C)]\npub struct SaePassphraseInfo {\n    pub len: u16,\n    pub passphrase: [u8; 128],\n}\nimpl_bytes!(SaePassphraseInfo);\n\n#[derive(Clone, Copy)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\n#[repr(C)]\npub struct SsidInfoWithIndex {\n    pub index: u32,\n    pub ssid_info: SsidInfo,\n}\nimpl_bytes!(SsidInfoWithIndex);\n\n#[derive(Clone, Copy)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\n#[repr(C)]\npub struct EventMask {\n    pub iface: u32,\n    pub events: [u8; 24],\n}\nimpl_bytes!(EventMask);\n\nimpl EventMask {\n    pub fn unset(&mut self, evt: Event) {\n        let evt = evt as u8 as usize;\n        self.events[evt / 8] &= !(1 << (evt % 8));\n    }\n}\n\n/// Parameters for a wifi scan\n#[derive(Clone, Copy)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\n#[repr(C)]\npub struct ScanParams {\n    pub version: u32,\n    pub action: u16,\n    pub sync_id: u16,\n    pub ssid_len: u32,\n    pub ssid: [u8; 32],\n    pub bssid: [u8; 6],\n    pub bss_type: u8,\n    pub scan_type: u8,\n    pub nprobes: u32,\n    pub active_time: u32,\n    pub passive_time: u32,\n    pub home_time: u32,\n    pub channel_num: u32,\n    pub channel_list: [u16; 1],\n}\nimpl_bytes!(ScanParams);\n\n/// Wifi Scan Results Header, followed by `bss_count` `BssInfo`\n#[derive(Clone, Copy)]\n// #[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\n#[repr(C, packed(2))]\npub struct ScanResults {\n    pub buflen: u32,\n    pub version: u32,\n    pub sync_id: u16,\n    pub bss_count: u16,\n}\nimpl_bytes!(ScanResults);\n\nimpl ScanResults {\n    pub fn parse(packet: &mut [u8]) -> Option<(&mut ScanResults, &mut [u8])> {\n        if packet.len() < ScanResults::SIZE {\n            return None;\n        }\n\n        let (scan_results, bssinfo) = packet.split_at_mut(ScanResults::SIZE);\n        let scan_results = ScanResults::from_bytes_mut(scan_results.try_into().unwrap());\n\n        if scan_results.bss_count > 0 && bssinfo.len() < BssInfo::SIZE {\n            warn!(\"Scan result, incomplete BssInfo\");\n            return None;\n        }\n\n        Some((scan_results, bssinfo))\n    }\n}\n\n/// Wifi Scan Result\n#[derive(Clone, Copy)]\n// #[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\n#[repr(C, packed(2))]\n#[non_exhaustive]\npub struct BssInfo {\n    /// Version.\n    pub version: u32,\n    /// Length.\n    pub length: u32,\n    /// BSSID.\n    pub bssid: [u8; 6],\n    /// Beacon period.\n    pub beacon_period: u16,\n    /// Capability.\n    pub capability: u16,\n    /// SSID length.\n    pub ssid_len: u8,\n    /// SSID.\n    pub ssid: [u8; 32],\n    reserved1: [u8; 1],\n    /// Number of rates in the rates field.\n    pub rateset_count: u32,\n    /// Rates in 500kpbs units.\n    pub rates: [u8; 16],\n    /// Channel specification.\n    pub chanspec: u16,\n    /// Announcement traffic indication message.\n    pub atim_window: u16,\n    /// Delivery traffic indication message.\n    pub dtim_period: u8,\n    reserved2: [u8; 1],\n    /// Receive signal strength (in dbM).\n    pub rssi: i16,\n    /// Received noise (in dbM).\n    pub phy_noise: i8,\n    /// 802.11n capability.\n    pub n_cap: u8,\n    reserved3: [u8; 2],\n    /// 802.11n BSS capabilities.\n    pub nbss_cap: u32,\n    /// 802.11n control channel number.\n    pub ctl_ch: u8,\n    reserved4: [u8; 3],\n    reserved32: [u32; 1],\n    /// Flags.\n    pub flags: u8,\n    /// VHT capability.\n    pub vht_cap: u8,\n    reserved5: [u8; 2],\n    /// 802.11n BSS required MCS.\n    pub basic_mcs: [u8; 16],\n    /// Information Elements (IE) offset.\n    pub ie_offset: u16,\n    /// Length of Information Elements (IE) in bytes.\n    pub ie_length: u32,\n    /// Average signal-to-noise (SNR) ratio during frame reception.\n    pub snr: i16,\n    // there will be more stuff here\n}\nimpl_bytes!(BssInfo);\n\nimpl BssInfo {\n    pub(crate) fn parse(packet: &mut [u8]) -> Option<&mut Self> {\n        if packet.len() < BssInfo::SIZE {\n            return None;\n        }\n\n        Some(BssInfo::from_bytes_mut(\n            packet[..BssInfo::SIZE].as_mut().try_into().unwrap(),\n        ))\n    }\n}\n"
  },
  {
    "path": "cyw43/src/util.rs",
    "content": "#![allow(unused)]\n\nuse core::slice;\n\nuse aligned::{A4, Aligned};\nuse embassy_time::{Duration, Ticker};\n\n/// Defines a `repr(u8)` enum and implements a `from()` associated function to instantiate it from\n/// a `u8`, defaulting to the variant decorated with `#[default]`.\nmacro_rules! enum_from_u8 {\n    (\n        $( #[$enum_attr:meta] )*\n        enum $enum:ident {\n            // NOTE: The default variant must be the first variant.\n            // Additionally, the `#[default]` attribute must be placed before any other attributes\n            // on the variant, to avoid a parsing ambiguity.\n            #[default]\n            $( #[$default_variant_attr:meta] )*\n            $default_variant:ident = $default_value:literal,\n            $(\n                $( #[$variant_attr:meta] )*\n                $variant:ident = $value:literal\n            ),+\n            $(,)?\n        }\n    ) => {\n        $( #[$enum_attr] )*\n        #[repr(u8)]\n        pub enum $enum {\n            $( #[$default_variant_attr] )*\n            $default_variant = $default_value,\n            $(\n                $( #[$variant_attr] )*\n                $variant = $value\n            ),+\n        }\n\n        impl $enum {\n            pub fn from(value: u8) -> Self {\n                match value {\n                    $default_value => Self::$default_variant,\n                    $( $value => Self::$variant ),+,\n                    _ => Self::$default_variant,\n                }\n            }\n        }\n    };\n}\npub(crate) use enum_from_u8;\n\npub(crate) const fn slice8_mut(x: &mut [u32]) -> &mut [u8] {\n    let len = size_of_val(x);\n    unsafe { slice::from_raw_parts_mut(x.as_mut_ptr() as _, len) }\n}\n\npub(crate) const fn slice16_mut(x: &mut [u32]) -> &mut [u16] {\n    let len = size_of_val(x) / 2;\n    unsafe { slice::from_raw_parts_mut(x.as_mut_ptr() as _, len) }\n}\n\npub(crate) const fn aligned_mut(x: &mut [u32]) -> &mut Aligned<A4, [u8]> {\n    let len = size_of_val(x);\n    unsafe { core::mem::transmute(slice::from_raw_parts_mut(x.as_mut_ptr() as *mut u8, len)) }\n}\n\npub(crate) const fn aligned_ref(x: &[u32]) -> &Aligned<A4, [u8]> {\n    let len = size_of_val(x);\n    unsafe { core::mem::transmute(slice::from_raw_parts(x.as_ptr() as *const u8, len)) }\n}\n\npub(crate) const fn slice32_mut(x: &mut Aligned<A4, [u8]>) -> &mut [u32] {\n    let len = (size_of_val(x) + 3) / 4;\n    unsafe { slice::from_raw_parts_mut(x as *mut Aligned<A4, [u8]> as *mut u32, len) }\n}\n\npub(crate) const fn slice32_ref(x: &Aligned<A4, [u8]>) -> &[u32] {\n    let len = (size_of_val(x) + 3) / 4;\n    unsafe { slice::from_raw_parts(x as *const Aligned<A4, [u8]> as *const u32, len) }\n}\n\npub(crate) fn is_aligned(a: u32, x: u32) -> bool {\n    (a & (x - 1)) == 0\n}\n\npub(crate) fn round_down(x: u32, a: u32) -> u32 {\n    x & !(a - 1)\n}\n\npub(crate) fn round_up(x: u32, a: u32) -> u32 {\n    ((x + a - 1) / a) * a\n}\n\npub(crate) async fn try_until(mut func: impl AsyncFnMut() -> bool, duration: Duration) -> bool {\n    let tick = Duration::from_millis(1);\n    let mut ticker = Ticker::every(tick);\n    let ticks = duration.as_ticks() / tick.as_ticks();\n\n    for _ in 0..ticks {\n        if func().await {\n            return true;\n        }\n\n        ticker.next().await;\n    }\n\n    false\n}\n"
  },
  {
    "path": "cyw43-firmware/.gitignore",
    "content": "*.exe\n*.pdb"
  },
  {
    "path": "cyw43-firmware/LICENSE-permissive-binary-license-1.0.txt",
    "content": "Permissive Binary License\n\nVersion 1.0, July 2019\n\nRedistribution.  Redistribution and use in binary form, without\nmodification, are permitted provided that the following conditions are\nmet:\n\n1) Redistributions must reproduce the above copyright notice and the\n   following disclaimer in the documentation and/or other materials\n   provided with the distribution.\n\n2) Unless to the extent explicitly permitted by law, no reverse\n   engineering, decompilation, or disassembly of this software is\n   permitted.\n\n3) Redistribution as part of a software development kit must include the\n   accompanying file named �DEPENDENCIES� and any dependencies listed in\n   that file.\n\n4) Neither the name of the copyright holder nor the names of its\n   contributors may be used to endorse or promote products derived from\n   this software without specific prior written permission.\n\nLimited patent license. The copyright holders (and contributors) grant a\nworldwide, non-exclusive, no-charge, royalty-free patent license to\nmake, have made, use, offer to sell, sell, import, and otherwise\ntransfer this software, where such license applies only to those patent\nclaims licensable by the copyright holders (and contributors) that are\nnecessarily infringed by this software. This patent license shall not\napply to any combinations that include this software.  No hardware is\nlicensed hereunder.\n\nIf you institute patent litigation against any entity (including a\ncross-claim or counterclaim in a lawsuit) alleging that the software\nitself infringes your patent(s), then your rights granted under this\nlicense shall terminate as of the date such litigation is filed.\n\nDISCLAIMER. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND\nCONTRIBUTORS \"AS IS.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT\nNOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS\nFOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\nHOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\nSPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED\nTO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\nPROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF\nLIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING\nNEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\nSOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
  },
  {
    "path": "cyw43-firmware/README.md",
    "content": "# WiFi + Bluetooth firmware blobs\n\nFirmware obtained from https://github.com/georgerobotics/cyw43-driver/tree/main/firmware\n\nLicensed under the [Infineon Permissive Binary License](./LICENSE-permissive-binary-license-1.0.txt)\n\n## Changelog\n\n* 2023-08-21: synced with `a1dc885` - Update 43439 fw + clm to come from `wb43439A0_7_95_49_00_combined.h` + add Bluetooth firmware\n* 2023-07-28: synced with `ad3bad0` - Update 43439 fw from 7.95.55 to 7.95.62\n\n## Notes\n\nIf you update these files, please update the lengths in the `tests/rp/src/bin/cyw43_perf.rs` test (which relies on these files running from RAM).\n"
  },
  {
    "path": "cyw43-firmware/write_nvrams.rs",
    "content": "use std::fs::File;\nuse std::io::{self, BufWriter, Write};\n\nfn main() -> io::Result<()> {\n    let nvrams = [\n        (\n            \"rp2040\",\n            &b\"NVRAMRev=$Rev$\\x00\\\nmanfid=0x2d0\\x00\\\nprodid=0x0727\\x00\\\nvendid=0x14e4\\x00\\\ndevid=0x43e2\\x00\\\nboardtype=0x0887\\x00\\\nboardrev=0x1100\\x00\\\nboardnum=22\\x00\\\nmacaddr=00:A0:50:b5:59:5e\\x00\\\nsromrev=11\\x00\\\nboardflags=0x00404001\\x00\\\nboardflags3=0x04000000\\x00\\\nxtalfreq=37400\\x00\\\nnocrc=1\\x00\\\nag0=255\\x00\\\naa2g=1\\x00\\\nccode=ALL\\x00\\\npa0itssit=0x20\\x00\\\nextpagain2g=0\\x00\\\npa2ga0=-168,6649,-778\\x00\\\nAvVmid_c0=0x0,0xc8\\x00\\\ncckpwroffset0=5\\x00\\\nmaxp2ga0=84\\x00\\\ntxpwrbckof=6\\x00\\\ncckbw202gpo=0\\x00\\\nlegofdmbw202gpo=0x66111111\\x00\\\nmcsbw202gpo=0x77711111\\x00\\\npropbw202gpo=0xdd\\x00\\\nofdmdigfilttype=18\\x00\\\nofdmdigfilttypebe=18\\x00\\\npapdmode=1\\x00\\\npapdvalidtest=1\\x00\\\npacalidx2g=45\\x00\\\npapdepsoffset=-30\\x00\\\npapdendidx=58\\x00\\\nltecxmux=0\\x00\\\nltecxpadnum=0x0102\\x00\\\nltecxfnsel=0x44\\x00\\\nltecxgcigpio=0x01\\x00\\\nil0macaddr=00:90:4c:c5:12:38\\x00\\\nwl0id=0x431b\\x00\\\ndeadman_to=0xffffffff\\x00\\\nmuxenab=0x100\\x00\\\nspurconfig=0x3\\x00\\\nglitch_based_crsmin=1\\x00\\\nbtc_mode=1\\x00\\\n\\x00\\x00\"[..],\n        ),\n        (\n            \"sterling_lwb+\",\n            &b\"NVRAMRev=$Rev$\\x00\\\nmanfid=0x2d0\\x00\\\nprodid=0x0727\\x00\\\nvendid=0x14e4\\x00\\\ndevid=0x43e2\\x00\\\nboardtype=0x0887\\x00\\\nboardrev=0x1102\\x00\\\nboardnum=22\\x00\\\nmacaddr=00:A0:50:b5:59:5e\\x00\\\nsromrev=11\\x00\\\nboardflags=0x00404001\\x00\\\nboardflags3=0x08000000\\x00\\\nxtalfreq=26000\\x00\\\nnocrc=1\\x00\\\nag0=255\\x00\\\naa2g=1\\x00\\\nrssicorrnorm=0\\x00\\\npa0itssit=0x20\\x00\\\nextpagain2g=0\\x00\\\npa2ga0=-155,6912,-779\\x00\\\nAvVmid_c0=0x0,0xc8\\x00\\\ncckpwroffset0=5\\x00\\\nmaxp2ga0=78\\x00\\\ntxpwrbckof=6\\x00\\\ncckbw202gpo=0\\x00\\\nlegofdmbw202gpo=0x40000000\\x00\\\nmcsbw202gpo=0x60000000\\x00\\\npropbw202gpo=0xdd\\x00\\\nofdmdigfilttype=18\\x00\\\nofdmdigfilttypebe=18\\x00\\\npapdmode=1\\x00\\\npapdvalidtest=1\\x00\\\npacalidx2g=45\\x00\\\npapdepsoffset=-30\\x00\\\npapdendidx=58\\x00\\\nil0macaddr=00:90:4c:c5:12:38\\x00\\\nwl0id=0x431b\\x00\\\ndeadman_to=0xffffffff\\x00\\\nmuxenab=0x1\\x00\\\nspurconfig=0x3\\x00\\\nglitch_based_crsmin=1\\x00\\\nbtc_mode=0\\x00\\\nbt_default_ant=0\\x00\\\nedonthd20l=-72\\x00\\\nedoffthd20ul=-78\\x00\\\n\\x00\\x00\"[..],\n        ),\n    ];\n\n    for (name, bytes) in nvrams {\n        let file = File::create(format!(\"nvram_{}.bin\", name))?;\n        let mut writer = BufWriter::new(file);\n\n        writer.write_all(bytes)?;\n        writer.flush()?;\n\n        println!(\"Wrote {}\", name);\n    }\n\n    Ok(())\n}\n"
  },
  {
    "path": "cyw43-pio/CHANGELOG.md",
    "content": "# Changelog\n\nAll notable changes to this project will be documented in this file.\n\nThe format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),\nand this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).\n\n<!-- next-header -->\n## Unreleased - ReleaseDate\n\n## 0.10.0 - 2026-03-10\n\n- Updated to use new DMA `Channel` driver struct from embassy-rp\n- Update cyw43 0.7.0\n- Update embassy-rp 0.10.0\n\n## 0.9.0 - 2025-11-27\n\n- Select pio program based on core clock speed #4792\n\n## 0.8.0 - 2025-08-28\n\n- Bump cyw43 version\n\n## 0.7.0 - 2025-08-26\n\n## 0.6.0 - 2025-08-04\n\n## 0.5.1 - 2025-07-16\n\n## 0.5.0 - 2025-07-15\n\n- Update embassy-rp to 0.5.0\n\n## 0.3.0 - 2025-01-05\n\n- Update embassy-time to 0.4.0\n- Update cyw43 to 0.3.0\n- Update embassy-rp to 0.3.0\n\n## 0.2.0 - 2024-08-05\n\n- Update to cyw43 0.2.0\n- Update to embassy-rp 0.2.0\n\n## 0.1.0 - 2024-01-11\n\n- First release\n"
  },
  {
    "path": "cyw43-pio/Cargo.toml",
    "content": "[package]\nname = \"cyw43-pio\"\nversion = \"0.10.0\"\nedition = \"2024\"\ndescription = \"RP2040 PIO SPI implementation for cyw43\"\nkeywords = [\"embedded\", \"cyw43\", \"embassy-net\", \"embedded-hal-async\", \"wifi\"]\ncategories = [\"embedded\", \"hardware-support\", \"no-std\", \"network-programming\", \"asynchronous\"]\nlicense = \"MIT OR Apache-2.0\"\nrepository = \"https://github.com/embassy-rs/embassy\"\ndocumentation = \"https://docs.embassy.dev/cyw43-pio\"\n\n[dependencies]\ncyw43 = { version = \"0.7.0\", path = \"../cyw43\" }\nembassy-rp = { version = \"0.10.0\", path = \"../embassy-rp\" }\nfixed = \"1.23.1\"\ndefmt = { version = \"1.0.1\", optional = true }\n\n[features]\ndefmt = [\"dep:defmt\"]\n\n[package.metadata.embassy]\nbuild = [\n    {target = \"thumbv6m-none-eabi\", features = [\"embassy-rp/rp2040\"]},\n]\n\n[package.metadata.embassy_docs]\nsrc_base = \"https://github.com/embassy-rs/embassy/blob/cyw43-pio-v$VERSION/cyw43-pio/src/\"\nsrc_base_git = \"https://github.com/embassy-rs/embassy/blob/$COMMIT/cyw43-pio/src/\"\ntarget = \"thumbv6m-none-eabi\"\nfeatures = [\"embassy-rp/rp2040\"]\n"
  },
  {
    "path": "cyw43-pio/README.md",
    "content": "# cyw43-pio\n\nRP2040 PIO driver for the nonstandard half-duplex SPI used in the Pico W. The PIO driver offloads SPI communication with the WiFi chip and improves throughput.\n"
  },
  {
    "path": "cyw43-pio/src/lib.rs",
    "content": "#![no_std]\n#![allow(async_fn_in_trait)]\n#![doc = include_str!(\"../README.md\")]\n#![warn(missing_docs)]\n\nuse core::slice;\n\nuse cyw43::SpiBusCyw43;\nuse embassy_rp::Peri;\nuse embassy_rp::clocks::clk_sys_freq;\nuse embassy_rp::dma::Channel;\nuse embassy_rp::gpio::{Drive, Level, Output, Pull, SlewRate};\nuse embassy_rp::pio::program::pio_asm;\nuse embassy_rp::pio::{Common, Config, Direction, Instance, Irq, PioPin, ShiftDirection, StateMachine};\nuse fixed::FixedU32;\nuse fixed::types::extra::U8;\n\n/// SPI comms driven by PIO.\npub struct PioSpi<'d, PIO: Instance, const SM: usize> {\n    cs: Output<'d>,\n    sm: StateMachine<'d, PIO, SM>,\n    irq: Irq<'d, PIO, 0>,\n    dma: Channel<'d>,\n    wrap_target: u8,\n}\n\n/// Clock divider used for most applications\n/// With default core clock configuration:\n/// RP2350: 150Mhz / 2 = 75Mhz pio clock -> 37.5Mhz GSPI clock\n/// RP2040: 133Mhz / 2 = 66.5Mhz pio clock -> 33.25Mhz GSPI clock\npub const DEFAULT_CLOCK_DIVIDER: FixedU32<U8> = FixedU32::from_bits(0x0200);\n\n/// Clock divider used to overclock the cyw43\n/// With default core clock configuration:\n/// RP2350: 150Mhz / 1 = 150Mhz pio clock -> 75Mhz GSPI clock (50% greater that manufacturer\n/// recommended 50Mhz)\n/// RP2040: 133Mhz / 1 = 133Mhz pio clock -> 66.5Mhz GSPI clock (33% greater that manufacturer\n/// recommended 50Mhz)\npub const OVERCLOCK_CLOCK_DIVIDER: FixedU32<U8> = FixedU32::from_bits(0x0100);\n\n/// Clock divider used with the RM2\n/// With default core clock configuration:\n/// RP2350: 150Mhz / 3 = 50Mhz pio clock -> 25Mhz GSPI clock\n/// RP2040: 133Mhz / 3 = 44.33Mhz pio clock -> 22.16Mhz GSPI clock\npub const RM2_CLOCK_DIVIDER: FixedU32<U8> = FixedU32::from_bits(0x0300);\n\nimpl<'d, PIO, const SM: usize> PioSpi<'d, PIO, SM>\nwhere\n    PIO: Instance,\n{\n    /// Create a new instance of PioSpi.\n    pub fn new(\n        common: &mut Common<'d, PIO>,\n        mut sm: StateMachine<'d, PIO, SM>,\n        clock_divider: FixedU32<U8>,\n        irq: Irq<'d, PIO, 0>,\n        cs: Output<'d>,\n        dio: Peri<'d, impl PioPin>,\n        clk: Peri<'d, impl PioPin>,\n        dma: Channel<'d>,\n    ) -> Self {\n        let effective_pio_frequency = (clk_sys_freq() as f32 / clock_divider.to_num::<f32>()) as u32;\n\n        #[cfg(feature = \"defmt\")]\n        defmt::trace!(\"Effective pio frequency: {}Hz\", effective_pio_frequency);\n\n        // Non-integer pio clock dividers are achieved by introducing clock jitter resulting in a\n        // combination of long and short cycles. The long and short cycles average to achieve the\n        // requested clock speed.\n        // This can be a problem for peripherals that expect a consistent clock / have a clock\n        // speed upper bound that is violated by the short cycles. The cyw43 seems to handle the\n        // jitter well, but we emit a warning to recommend an integer divider anyway.\n        if clock_divider.frac() != FixedU32::<U8>::ZERO {\n            #[cfg(feature = \"defmt\")]\n            defmt::trace!(\n                \"Configured clock divider is not a whole number. Some clock cycles may violate the maximum recommended GSPI speed. Use at your own risk.\"\n            );\n        }\n\n        // Different pio programs must be used for different pio clock speeds.\n        // The programs used below are based on the pico SDK: https://github.com/raspberrypi/pico-sdk/blob/master/src/rp2_common/pico_cyw43_driver/cyw43_bus_pio_spi.pio\n        // The clock speed cutoff for each program has been determined experimentally:\n        // > 100Mhz -> Overclock program\n        // [75Mhz, 100Mhz] -> High speed program\n        // [0, 75Mhz) -> Low speed program\n        let loaded_program = if effective_pio_frequency > 100_000_000 {\n            // Any frequency > 100Mhz is overclocking the chip (manufacturer recommends max 50Mhz GSPI\n            // clock)\n            // Example:\n            // * RP2040 @ 133Mhz (stock) with OVERCLOCK_CLOCK_DIVIDER (133MHz)\n            #[cfg(feature = \"defmt\")]\n            defmt::trace!(\n                \"Configured clock divider results in a GSPI frequency greater than the manufacturer recommendation (50Mhz). Use at your own risk.\"\n            );\n\n            let overclock_program = pio_asm!(\n                \".side_set 1\"\n\n                \".wrap_target\"\n                // write out x-1 bits\n                \"lp:\"\n                \"out pins, 1    side 0\"\n                \"jmp x-- lp     side 1\"\n                // switch directions\n                \"set pindirs, 0 side 0\"\n                \"nop            side 1\"\n                \"nop            side 0\"\n                // read in y-1 bits\n                \"lp2:\"\n                \"in pins, 1     side 1\"\n                \"jmp y-- lp2    side 0\"\n\n                // wait for event and irq host\n                \"wait 1 pin 0   side 0\"\n                \"irq 0          side 0\"\n\n                \".wrap\"\n            );\n            common.load_program(&overclock_program.program)\n        } else if effective_pio_frequency >= 75_000_000 {\n            // Experimentally determined cutoff.\n            // Notably includes the stock RP2350 configured with clk_div of 2 (150Mhz base clock / 2 = 75Mhz)\n            // but does not include stock RP2040 configured with clk_div of 2 (133Mhz base clock / 2 = 66.5Mhz)\n            // Example:\n            // * RP2350 @ 150Mhz (stock) with DEFAULT_CLOCK_DIVIDER (75Mhz)\n            // * RP2XXX @ 200Mhz with DEFAULT_CLOCK_DIVIDER (100Mhz)\n            #[cfg(feature = \"defmt\")]\n            defmt::trace!(\"Using high speed pio program.\");\n            let high_speed_program = pio_asm!(\n                \".side_set 1\"\n\n                \".wrap_target\"\n                // write out x-1 bits\n                \"lp:\"\n                \"out pins, 1    side 0\"\n                \"jmp x-- lp     side 1\"\n                // switch directions\n                \"set pindirs, 0 side 0\"\n                \"nop            side 1\"\n                // read in y-1 bits\n                \"lp2:\"\n                \"in pins, 1     side 0\"\n                \"jmp y-- lp2    side 1\"\n\n                // wait for event and irq host\n                \"wait 1 pin 0   side 0\"\n                \"irq 0          side 0\"\n\n                \".wrap\"\n            );\n            common.load_program(&high_speed_program.program)\n        } else {\n            // Low speed\n            // Examples:\n            // * RP2040 @ 133Mhz (stock) with DEFAULT_CLOCK_DIVIDER (66.5Mhz)\n            // * RP2040 @ 133Mhz (stock) with RM2_CLOCK_DIVIDER (44.3Mhz)\n            // * RP2350 @ 150Mhz (stock) with RM2_CLOCK_DIVIDER (50Mhz)\n            #[cfg(feature = \"defmt\")]\n            defmt::trace!(\"Using low speed pio program.\");\n            let low_speed_program = pio_asm!(\n                \".side_set 1\"\n\n                \".wrap_target\"\n                // write out x-1 bits\n                \"lp:\"\n                \"out pins, 1    side 0\"\n                \"jmp x-- lp     side 1\"\n                // switch directions\n                \"set pindirs, 0 side 0\"\n                \"nop            side 0\"\n                // read in y-1 bits\n                \"lp2:\"\n                \"in pins, 1     side 1\"\n                \"jmp y-- lp2    side 0\"\n\n                // wait for event and irq host\n                \"wait 1 pin 0   side 0\"\n                \"irq 0          side 0\"\n\n                \".wrap\"\n            );\n            common.load_program(&low_speed_program.program)\n        };\n\n        let mut pin_io: embassy_rp::pio::Pin<PIO> = common.make_pio_pin(dio);\n        pin_io.set_pull(Pull::None);\n        pin_io.set_schmitt(true);\n        pin_io.set_input_sync_bypass(true);\n        pin_io.set_drive_strength(Drive::_12mA);\n        pin_io.set_slew_rate(SlewRate::Fast);\n\n        let mut pin_clk = common.make_pio_pin(clk);\n        pin_clk.set_drive_strength(Drive::_12mA);\n        pin_clk.set_slew_rate(SlewRate::Fast);\n\n        let mut cfg = Config::default();\n        cfg.use_program(&loaded_program, &[&pin_clk]);\n        cfg.set_out_pins(&[&pin_io]);\n        cfg.set_in_pins(&[&pin_io]);\n        cfg.set_set_pins(&[&pin_io]);\n        cfg.shift_out.direction = ShiftDirection::Left;\n        cfg.shift_out.auto_fill = true;\n        //cfg.shift_out.threshold = 32;\n        cfg.shift_in.direction = ShiftDirection::Left;\n        cfg.shift_in.auto_fill = true;\n        //cfg.shift_in.threshold = 32;\n        cfg.clock_divider = clock_divider;\n\n        sm.set_config(&cfg);\n\n        sm.set_pin_dirs(Direction::Out, &[&pin_clk, &pin_io]);\n        sm.set_pins(Level::Low, &[&pin_clk, &pin_io]);\n\n        Self {\n            cs,\n            sm,\n            irq,\n            dma,\n            wrap_target: loaded_program.wrap.target,\n        }\n    }\n\n    /// Write data to peripheral and return status.\n    pub async fn write(&mut self, write: &[u32]) -> u32 {\n        self.sm.set_enable(false);\n        let write_bits = write.len() * 32 - 1;\n        let read_bits = 31;\n\n        #[cfg(feature = \"defmt\")]\n        defmt::trace!(\"write={} read={}\", write_bits, read_bits);\n\n        unsafe {\n            self.sm.set_x(write_bits as u32);\n            self.sm.set_y(read_bits as u32);\n            self.sm.set_pindir(0b1);\n            self.sm.exec_jmp(self.wrap_target);\n        }\n\n        self.sm.set_enable(true);\n\n        self.sm.tx().dma_push(&mut self.dma, write, false).await;\n\n        let mut status = 0;\n        self.sm\n            .rx()\n            .dma_pull(&mut self.dma, slice::from_mut(&mut status), false)\n            .await;\n        status\n    }\n\n    /// Send command and read response into buffer.\n    pub async fn cmd_read(&mut self, cmd: u32, read: &mut [u32]) -> u32 {\n        self.sm.set_enable(false);\n        let write_bits = 31;\n        let read_bits = read.len() * 32 + 32 - 1;\n\n        #[cfg(feature = \"defmt\")]\n        defmt::trace!(\"cmd_read write={} read={}\", write_bits, read_bits);\n\n        #[cfg(feature = \"defmt\")]\n        defmt::trace!(\"cmd_read cmd = {:02x} len = {}\", cmd, read.len());\n\n        unsafe {\n            self.sm.set_y(read_bits as u32);\n            self.sm.set_x(write_bits as u32);\n            self.sm.set_pindir(0b1);\n            self.sm.exec_jmp(self.wrap_target);\n        }\n\n        // self.cs.set_low();\n        self.sm.set_enable(true);\n\n        self.sm.tx().dma_push(&mut self.dma, slice::from_ref(&cmd), false).await;\n        self.sm.rx().dma_pull(&mut self.dma, read, false).await;\n\n        let mut status = 0;\n        self.sm\n            .rx()\n            .dma_pull(&mut self.dma, slice::from_mut(&mut status), false)\n            .await;\n\n        #[cfg(feature = \"defmt\")]\n        defmt::trace!(\"cmd_read cmd = {:02x} len = {} read = {:08x}\", cmd, read.len(), read);\n\n        status\n    }\n}\n\nimpl<'d, PIO, const SM: usize> SpiBusCyw43 for PioSpi<'d, PIO, SM>\nwhere\n    PIO: Instance,\n{\n    async fn cmd_write(&mut self, write: &[u32]) -> u32 {\n        self.cs.set_low();\n        let status = self.write(write).await;\n        self.cs.set_high();\n        status\n    }\n\n    async fn cmd_read(&mut self, write: u32, read: &mut [u32]) -> u32 {\n        self.cs.set_low();\n        let status = self.cmd_read(write, read).await;\n        self.cs.set_high();\n        status\n    }\n\n    async fn wait_for_event(&mut self) {\n        self.irq.wait().await;\n    }\n}\n"
  },
  {
    "path": "docs/Makefile",
    "content": "all:\n\tasciidoctor -d book -D book/ index.adoc\n\tcp -r images book\n\nclean:\n\trm -rf book\n\n.PHONY: all clean\n"
  },
  {
    "path": "docs/README.md",
    "content": "# embassy docs\n\nThe documentation hosted at [https://embassy.dev/book](https://embassy.dev/book). Building the documentation requires the [asciidoctor](https://asciidoctor.org/) tool, and can built running `make` in this folder:\n\n```\nmake\n```\n\nThen open the generated file `thebook/index.html`.\n\n## License\n\nThe Embassy Docs (this folder) is distributed under the following licenses:\n\n* The code samples and free-standing Cargo projects contained within these docs are licensed under the terms of both the [MIT License] and the [Apache License v2.0].\n* The written prose contained within these docs are licensed under the terms of the Creative Commons [CC-BY-SA v4.0] license.\n\nCopies of the licenses used by this project may also be found here:\n\n* [MIT License Hosted]\n* [Apache License v2.0 Hosted]\n* [CC-BY-SA v4.0 Hosted]\n\n[MIT License]: ./../LICENSE-MIT\n[Apache License v2.0]: ./../LICENSE-APACHE\n[CC-BY-SA v4.0]: ./../LICENSE-CC-BY-SA\n[MIT License Hosted]: https://opensource.org/licenses/MIT\n[Apache License v2.0 Hosted]: http://www.apache.org/licenses/LICENSE-2.0\n[CC-BY-SA v4.0 Hosted]: https://creativecommons.org/licenses/by-sa/4.0/legalcode\n"
  },
  {
    "path": "docs/examples/basic/.cargo/config.toml",
    "content": "[target.'cfg(all(target_arch = \"arm\", target_os = \"none\"))']\n# replace nRF82840_xxAA with your chip as listed in `probe-rs chip list`\nrunner = \"probe-rs run --chip nRF52840_xxAA\"\n\n[build]\ntarget = \"thumbv7em-none-eabi\"\n\n[env]\nDEFMT_LOG = \"trace\"\n"
  },
  {
    "path": "docs/examples/basic/Cargo.toml",
    "content": "[package]\nauthors = [\"Dario Nieuwenhuis <dirbaio@dirbaio.net>\"]\nedition = \"2024\"\nname = \"embassy-basic-example\"\nversion = \"0.1.0\"\nlicense = \"MIT OR Apache-2.0\"\n\npublish = false\n[dependencies]\nembassy-executor = { version = \"0.10.0\", path = \"../../../embassy-executor\", features = [\"defmt\", \"platform-cortex-m\", \"executor-thread\"] }\nembassy-time = { version = \"0.5.1\", path = \"../../../embassy-time\", features = [\"defmt\"] }\nembassy-nrf = { version = \"0.10.0\", path = \"../../../embassy-nrf\", features = [\"defmt\", \"nrf52840\", \"time-driver-rtc1\", \"gpiote\"] }\n\ndefmt = \"1.0.1\"\ndefmt-rtt = \"1.0.0\"\n\ncortex-m = { version = \"0.7.6\", features = [\"critical-section-single-core\"] }\ncortex-m-rt = \"0.7.0\"\npanic-probe = { version = \"1.0.0\", features = [\"print-defmt\"] }\n\n[package.metadata.embassy]\nbuild = [\n  { target = \"thumbv7em-none-eabi\" }\n]\n"
  },
  {
    "path": "docs/examples/basic/build.rs",
    "content": "//! This build script copies the `memory.x` file from the crate root into\n//! a directory where the linker can always find it at build time.\n//! For many projects this is optional, as the linker always searches the\n//! project root directory -- wherever `Cargo.toml` is. However, if you\n//! are using a workspace or have a more complicated build setup, this\n//! build script becomes required. Additionally, by requesting that\n//! Cargo re-run the build script whenever `memory.x` is changed,\n//! updating `memory.x` ensures a rebuild of the application with the\n//! new memory settings.\n\nuse std::env;\nuse std::fs::File;\nuse std::io::Write;\nuse std::path::PathBuf;\n\nfn main() {\n    // Put `memory.x` in our output directory and ensure it's\n    // on the linker search path.\n    let out = &PathBuf::from(env::var_os(\"OUT_DIR\").unwrap());\n    File::create(out.join(\"memory.x\"))\n        .unwrap()\n        .write_all(include_bytes!(\"memory.x\"))\n        .unwrap();\n    println!(\"cargo:rustc-link-search={}\", out.display());\n\n    // By default, Cargo will re-run a build script whenever\n    // any file in the project changes. By specifying `memory.x`\n    // here, we ensure the build script is only re-run when\n    // `memory.x` is changed.\n    println!(\"cargo:rerun-if-changed=memory.x\");\n\n    println!(\"cargo:rustc-link-arg-bins=--nmagic\");\n    println!(\"cargo:rustc-link-arg-bins=-Tlink.x\");\n    println!(\"cargo:rustc-link-arg-bins=-Tdefmt.x\");\n}\n"
  },
  {
    "path": "docs/examples/basic/memory.x",
    "content": "MEMORY\n{\n  /* NOTE 1 K = 1 KiBi = 1024 bytes */\n  /* These values correspond to the NRF52840 with Softdevices S140 7.0.1 */\n  FLASH : ORIGIN = 0x00000000, LENGTH = 1024K\n  RAM : ORIGIN = 0x20000000, LENGTH = 256K\n}\n"
  },
  {
    "path": "docs/examples/basic/src/main.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_nrf::Peri;\nuse embassy_nrf::gpio::{AnyPin, Input, Level, Output, OutputDrive, Pull};\nuse embassy_time::Timer;\nuse {defmt_rtt as _, panic_probe as _};\n\n// Declare async tasks\n#[embassy_executor::task]\nasync fn blink(pin: Peri<'static, AnyPin>) {\n    let mut led = Output::new(pin, Level::Low, OutputDrive::Standard);\n\n    loop {\n        // Timekeeping is globally available, no need to mess with hardware timers.\n        led.set_high();\n        Timer::after_millis(150).await;\n        led.set_low();\n        Timer::after_millis(150).await;\n    }\n}\n\n// Main is itself an async task as well.\n#[embassy_executor::main]\nasync fn main(spawner: Spawner) {\n    // Initialize the embassy-nrf HAL.\n    let p = embassy_nrf::init(Default::default());\n\n    // Spawned tasks run in the background, concurrently.\n    spawner.spawn(blink(p.P0_13.into()).unwrap());\n\n    let mut button = Input::new(p.P0_11, Pull::Up);\n    loop {\n        // Asynchronously wait for GPIO events, allowing other tasks\n        // to run, or the core to sleep.\n        button.wait_for_low().await;\n        info!(\"Button pressed!\");\n        button.wait_for_high().await;\n        info!(\"Button released!\");\n    }\n}\n"
  },
  {
    "path": "docs/examples/layer-by-layer/.cargo/config.toml",
    "content": "[target.'cfg(all(target_arch = \"arm\", target_os = \"none\"))']\n# replace your chip as listed in `probe-rs chip list`\nrunner = \"probe-rs run --chip STM32L475VG\"\n\nrustflags = [\n    \"-C\", \"link-arg=--nmagic\",\n    \"-C\", \"link-arg=-Tlink.x\",\n    \"-C\", \"link-arg=-Tdefmt.x\",\n]\n\n[build]\ntarget = \"thumbv7em-none-eabihf\"\n\n[env]\nDEFMT_LOG = \"trace\"\n"
  },
  {
    "path": "docs/examples/layer-by-layer/Cargo.toml",
    "content": "[workspace]\nresolver = \"2\"\nmembers = [\n    \"blinky-pac\",\n    \"blinky-hal\",\n    \"blinky-async\",\n]\n\n[profile.release]\ncodegen-units = 1\ndebug = 2\ndebug-assertions = false\nincremental = false\nlto = \"fat\"\nopt-level = 's'\noverflow-checks = false\n"
  },
  {
    "path": "docs/examples/layer-by-layer/blinky-async/Cargo.toml",
    "content": "[package]\nname = \"blinky-async\"\nversion = \"0.1.0\"\nedition = \"2024\"\nlicense = \"MIT OR Apache-2.0\"\n\npublish = false\n[dependencies]\ncortex-m = { version = \"0.7\", features = [\"critical-section-single-core\"] }\ncortex-m-rt = \"0.7\"\nembassy-stm32 = { version = \"0.6.0\", path = \"../../../../embassy-stm32\", features = [\"stm32l475vg\", \"memory-x\", \"exti\"]  }\nembassy-executor = { version = \"0.10.0\", path = \"../../../../embassy-executor\", features = [\"platform-cortex-m\", \"executor-thread\"] }\n\ndefmt = \"1.0.1\"\ndefmt-rtt = \"1.0.0\"\npanic-probe = { version = \"1.0.0\", features = [\"print-defmt\"] }\n\n[package.metadata.embassy]\nbuild = [\n  { target = \"thumbv7em-none-eabi\" }\n]\n"
  },
  {
    "path": "docs/examples/layer-by-layer/blinky-async/src/main.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse embassy_executor::Spawner;\nuse embassy_stm32::exti::{self, ExtiInput};\nuse embassy_stm32::gpio::{Level, Output, Pull, Speed};\nuse embassy_stm32::{bind_interrupts, interrupt};\nuse {defmt_rtt as _, panic_probe as _};\n\nbind_interrupts!(\n    pub struct Irqs{\n        EXTI15_10  => exti::InterruptHandler<interrupt::typelevel::EXTI15_10 >;\n});\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let p = embassy_stm32::init(Default::default());\n    let mut led = Output::new(p.PB14, Level::Low, Speed::VeryHigh);\n    let mut button = ExtiInput::new(p.PC13, p.EXTI13, Pull::Up, Irqs);\n\n    loop {\n        button.wait_for_any_edge().await;\n        if button.is_low() {\n            led.set_high();\n        } else {\n            led.set_low();\n        }\n    }\n}\n"
  },
  {
    "path": "docs/examples/layer-by-layer/blinky-hal/Cargo.toml",
    "content": "[package]\nname = \"blinky-hal\"\nversion = \"0.1.0\"\nedition = \"2024\"\nlicense = \"MIT OR Apache-2.0\"\n\npublish = false\n[dependencies]\ncortex-m-rt = \"0.7\"\ncortex-m = { version = \"0.7\", features = [\"critical-section-single-core\"] }\nembassy-stm32 = { version = \"0.6.0\", path = \"../../../../embassy-stm32\", features = [\"stm32l475vg\", \"memory-x\"] }\n\ndefmt = \"1.0.1\"\ndefmt-rtt = \"1.0.0\"\npanic-probe = { version = \"1.0.0\", features = [\"print-defmt\"] }\n\n[package.metadata.embassy]\nbuild = [\n  { target = \"thumbv7em-none-eabi\" }\n]\n"
  },
  {
    "path": "docs/examples/layer-by-layer/blinky-hal/src/main.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse cortex_m_rt::entry;\nuse embassy_stm32::gpio::{Input, Level, Output, Pull, Speed};\nuse {defmt_rtt as _, panic_probe as _};\n\n#[entry]\nfn main() -> ! {\n    let p = embassy_stm32::init(Default::default());\n    let mut led = Output::new(p.PB14, Level::High, Speed::VeryHigh);\n    let button = Input::new(p.PC13, Pull::Up);\n\n    loop {\n        if button.is_low() {\n            led.set_high();\n        } else {\n            led.set_low();\n        }\n    }\n}\n"
  },
  {
    "path": "docs/examples/layer-by-layer/blinky-irq/Cargo.toml",
    "content": "[workspace]\n\n[package]\nname = \"blinky-irq\"\nversion = \"0.1.0\"\nedition = \"2024\"\nlicense = \"MIT OR Apache-2.0\"\n\npublish = false\n[dependencies]\ncortex-m = { version = \"0.7\", features = [\"critical-section-single-core\"] }\ncortex-m-rt = { version = \"0.7\" }\nembassy-stm32 = { version = \"0.6.0\", path = \"../../../../embassy-stm32\", features = [\"stm32l475vg\", \"memory-x\", \"unstable-pac\"] }\n\ndefmt = \"1.0.1\"\ndefmt-rtt = \"1.0.0\"\npanic-probe = { version = \"1.0.0\", features = [\"print-defmt\"] }\n\n[package.metadata.embassy]\nbuild = [\n  { target = \"thumbv7em-none-eabi\" }\n]\n"
  },
  {
    "path": "docs/examples/layer-by-layer/blinky-irq/src/main.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse core::cell::RefCell;\n\nuse cortex_m::interrupt::Mutex;\nuse cortex_m::peripheral::NVIC;\nuse cortex_m_rt::entry;\nuse embassy_stm32::gpio::{Input, Level, Output, Pull, Speed};\nuse embassy_stm32::{interrupt, pac};\nuse {defmt_rtt as _, panic_probe as _};\n\nstatic BUTTON: Mutex<RefCell<Option<Input<'static>>>> = Mutex::new(RefCell::new(None));\nstatic LED: Mutex<RefCell<Option<Output<'static>>>> = Mutex::new(RefCell::new(None));\n\n#[entry]\nfn main() -> ! {\n    let p = embassy_stm32::init(Default::default());\n    let led = Output::new(p.PB14, Level::Low, Speed::Low);\n    let mut button = Input::new(p.PC13, Pull::Up);\n\n    cortex_m::interrupt::free(|cs| {\n        enable_interrupt(&mut button);\n\n        LED.borrow(cs).borrow_mut().replace(led);\n        BUTTON.borrow(cs).borrow_mut().replace(button);\n\n        unsafe { NVIC::unmask(pac::Interrupt::EXTI15_10) };\n    });\n\n    loop {\n        cortex_m::asm::wfe();\n    }\n}\n\n#[interrupt]\nfn EXTI15_10() {\n    cortex_m::interrupt::free(|cs| {\n        let mut button = BUTTON.borrow(cs).borrow_mut();\n        let button = button.as_mut().unwrap();\n\n        let mut led = LED.borrow(cs).borrow_mut();\n        let led = led.as_mut().unwrap();\n        if check_interrupt(button) {\n            if button.is_low() {\n                led.set_high();\n            } else {\n                led.set_low();\n            }\n        }\n        clear_interrupt(button);\n    });\n}\n//\n//\n//\n//\n//\n//\n// \"Hidden\" HAL-like methods for doing interrupts with embassy. Hardcode pin just to give audience an idea of what it looks like\n\nconst PORT: u8 = 2;\nconst PIN: usize = 13;\nfn check_interrupt(_pin: &mut Input<'static>) -> bool {\n    let exti = pac::EXTI;\n    let pin = PIN;\n    let lines = exti.pr(0).read();\n    lines.line(pin)\n}\n\nfn clear_interrupt(_pin: &mut Input<'static>) {\n    let exti = pac::EXTI;\n    let pin = PIN;\n    let mut lines = exti.pr(0).read();\n    lines.set_line(pin, true);\n    exti.pr(0).write_value(lines);\n}\n\nfn enable_interrupt(_pin: &mut Input<'static>) {\n    cortex_m::interrupt::free(|_| {\n        let rcc = pac::RCC;\n        rcc.apb2enr().modify(|w| w.set_syscfgen(true));\n\n        let port = PORT;\n        let pin = PIN;\n        let syscfg = pac::SYSCFG;\n        let exti = pac::EXTI;\n        syscfg.exticr(pin / 4).modify(|w| w.set_exti(pin % 4, port));\n        exti.imr(0).modify(|w| w.set_line(pin, true));\n        exti.rtsr(0).modify(|w| w.set_line(pin, true));\n        exti.ftsr(0).modify(|w| w.set_line(pin, true));\n    });\n}\n"
  },
  {
    "path": "docs/examples/layer-by-layer/blinky-pac/Cargo.toml",
    "content": "[package]\nname = \"blinky-pac\"\nversion = \"0.1.0\"\nedition = \"2024\"\nlicense = \"MIT OR Apache-2.0\"\n\npublish = false\n[dependencies]\ncortex-m = { version = \"0.7\", features = [\"critical-section-single-core\"] }\ncortex-m-rt = \"0.7\"\nstm32-metapac = { version = \"21\", features = [\"stm32l475vg\"] }\n\ndefmt = \"1.0.1\"\ndefmt-rtt = \"1.0.0\"\npanic-probe = { version = \"1.0.0\", features = [\"print-defmt\"] }\n\n[package.metadata.embassy]\nbuild = [\n  { target = \"thumbv7em-none-eabi\" }\n]\n"
  },
  {
    "path": "docs/examples/layer-by-layer/blinky-pac/src/main.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse pac::gpio::vals;\nuse {defmt_rtt as _, panic_probe as _, stm32_metapac as pac};\n\n#[cortex_m_rt::entry]\nfn main() -> ! {\n    // Enable GPIO clock\n    let rcc = pac::RCC;\n    rcc.ahb2enr().modify(|w| {\n        w.set_gpioben(true);\n        w.set_gpiocen(true);\n    });\n\n    rcc.ahb2rstr().modify(|w| {\n        w.set_gpiobrst(true);\n        w.set_gpiocrst(true);\n        w.set_gpiobrst(false);\n        w.set_gpiocrst(false);\n    });\n\n    // Setup button\n    let gpioc = pac::GPIOC;\n    const BUTTON_PIN: usize = 13;\n    gpioc.pupdr().modify(|w| w.set_pupdr(BUTTON_PIN, vals::Pupdr::PULL_UP));\n    gpioc.otyper().modify(|w| w.set_ot(BUTTON_PIN, vals::Ot::PUSH_PULL));\n    gpioc.moder().modify(|w| w.set_moder(BUTTON_PIN, vals::Moder::INPUT));\n\n    // Setup LED\n    let gpiob = pac::GPIOB;\n    const LED_PIN: usize = 14;\n    gpiob.pupdr().modify(|w| w.set_pupdr(LED_PIN, vals::Pupdr::FLOATING));\n    gpiob.otyper().modify(|w| w.set_ot(LED_PIN, vals::Ot::PUSH_PULL));\n    gpiob.moder().modify(|w| w.set_moder(LED_PIN, vals::Moder::OUTPUT));\n\n    // Main loop\n    loop {\n        if gpioc.idr().read().idr(BUTTON_PIN) == vals::Idr::LOW {\n            gpiob.bsrr().write(|w| w.set_bs(LED_PIN, true));\n        } else {\n            gpiob.bsrr().write(|w| w.set_br(LED_PIN, true));\n        }\n    }\n}\n"
  },
  {
    "path": "docs/examples/layer-by-layer/memory.x",
    "content": "MEMORY\n{\n    FLASH : ORIGIN = 0x08000000, LENGTH = 2048K /* BANK_1 */\n    RAM   : ORIGIN = 0x20000000, LENGTH =  640K /* SRAM */\n}\n"
  },
  {
    "path": "docs/images/embassy_executor.drawio",
    "content": "<mxfile host=\"app.diagrams.net\" modified=\"2021-12-10T08:37:46.039Z\" agent=\"5.0 (Macintosh)\" etag=\"F30pje_XkDZMiNl31GPU\" version=\"15.9.4\" type=\"device\"><diagram id=\"6eJnfHJiGOpK0Luz4X_S\" name=\"Page-1\">7Vnhb6IwFP9r+LgFKFX8OJ13u2S3LLcld98uFQo0Q2qgTt1ff2UUsa0IU5y6XLIs9PX1WX7v1997VQOMpsvvKZpFP6mPY8M2/aUBbg3bHjg9/j83rAoDdPuFIUyJX5isyvBE3rAwmsI6Jz7OJEdGaczITDZ6NEmwxyQbSlO6kN0CGsufOkMh1gxPHop162/is6iwutCs7HeYhFH5yZYpZqaodBaGLEI+XWyYwNgAo5RSVjxNlyMc59iVuBTrvtXMrjeW4oS1WfDAsjuIh89/fv8NyGhxv0zMH1dOEeUVxXPxwuMl9uaMpmLTbFUikdJ54uM8mGWA4SIiDD/NkJfPLnjquS1i01hMZy+YeZEYBCSORzTmMfNAIHA97Hm5E0vpC96YmbjQ4ciCodgVThle1r6utQaRkw/TKWbpiruUC3oC95UyXlRptMrcRBspLP2QYE64Dl2Byx8Evh/AGmpYP6PshVus42HtI+wGW7HueS6eBN1g7djnhnVPw1rDGCf+TS4QfOTFKMuIJ8Na5cDsCmTsa2KjQMx3SOeph5uPLENpiFkT3fSUpThGjLzK+9iGv1j6SAnf4TrVoC+nGlhKCov9i1WbcqQEcpoCFS+oBXqnw/p99mdIX2OIdc3HjzRfqlKF5+0eTXhl47TAGXlDk/cpU2YMikmY5HTiOcWcAcP8UBFeS27ExJT4fr5QZpNUwETkqmw0HMx69h+Q+oOOnlsnc/oRvLCSosqc3T+1zNl6/RZgP3QI9hGgA4OTQ/exEpHQJD+4PsqiNYbtikVb6dex28AGboGmtB2o6I6rpKa/r6KrgZzPVXQAWmSUp+FJDDMcTnkKxpVp+LGeQCJDJ/LUSYPgtmwQnJOyzlTIYu7LOqiIMmzHOp5ntNpwm+UOWf2Ge9b2DdftS/Uf7Ha37V3u/KHYb7cHRq8ezroHAjf87xdG/mrrISrbob3aH7WHEtjz4HBowNtdJaeuTWrsiQp1qC1QV+a12Td7Ug6sw5heutAgyPBxFE+/Up6J4n3ulQheguJB2HDhaat4XHpkmqoFuyPFU+u5ZTVIGNzpfyQN09s4W9awR05xkoRfRMVqbvUXrWL6jfFUKtahJrX9msY+qShZqijt2/zbaj9nH0eU1D7JsT9DZAYaQ2GrL4suR1fcJl2xXXjuQlLeN89ASE56AWwrPTU5/38BbNXeKBfA4yiPY2mUBl9LeRzT2KU85jXouRLuV/bpdIgPqx9sC/fqV28w/gc=</diagram></mxfile>"
  },
  {
    "path": "docs/images/embassy_irq.drawio",
    "content": "<mxfile host=\"app.diagrams.net\" modified=\"2021-12-10T09:18:58.548Z\" agent=\"5.0 (Macintosh)\" etag=\"zuUk3bLZCCXvlBU683Ur\" version=\"15.9.4\" type=\"device\"><diagram id=\"6eJnfHJiGOpK0Luz4X_S\" name=\"Page-1\">5VlNc9owEP01HJOxLdsxx0Bokpk0zZRM0p4ywl6wBmMRWWDIr6+EBf4QKQ6tIbQcPOittLLe7j5J0ELdyeKa4Wn4lQYQtSwjWLTQVcuy2rYrnhJYZoDjXWTAiJEgg8wc6JM3UKCh0BkJICl15JRGnEzLoE/jGHxewjBjNC13G9KoPOsUj0AD+j6OdPSZBDzMUM8xcvwGyChcz2wayjLB684KSEIc0LQAoV4LdRmlPPs2WXQhktytecnGfXnHunkxBjGvM+CeJzcOdB5/PL8MSTe9W8TG7ZmdeZnjaKYW3FuAP+OUqZfmyzUTjM7iAKQzs4U6aUg49KfYl9ZUhF5gIZ9EypyMgfuhagxJFHVpJHxKRyjA4A192YkzOoaCxfU9GAyFRV+bWu4cGIdFAVJrvQY6Ac6Wosva6irel5V2mofRXMcmLIRw3Q+rzBltXOfkii+K3w9w7WhcP+JkfOo829Zn49nVeNY4hji4lOIgWn6Ek4T4ZVrzGBgNkpzQGfNhd3VyzEbAd2cWBCUt00PGIMKczMvSto1/NfSBEvHKm1Cji3KokVkJYbYgNaooRRVH9i5H2Yo1R6t02Kxn/wy50DLEPG/JHHUjQXNnIMTPHfFV6DIkIPM19ECl/wwWkxcsepaJeNzhgdgQRUZBQt7wYGUyysmGIzKKZSaK/ACRPB1ZfERsQZfKMCFBIAeWE1FPq99Vg9of1Rvku1KNQq+dNX9UtZa+Fd1KPthsKjPnBseCgwZ3paHng7+1kAeeYztGM7sS8g6oli9P8/YrfvVf3O8/A3Y9f5oTdGZqtDeolrVJrkjZfvJp1dVPuxG5tNrlWIvD7znybNd01NPdUz1Rxa9nn+dOxdNuSky3JpClJZBTW0zvKSfD5SeS09oyWc7I9yvrAHK6dXL0f5X1Vg6cZsq6Un6ovecpCFX0wW7uFLSVHn3DtWXhPuMxnHztoWPWnn7RewBGpiEwLMf2OeY6wwc4yFy2u3avIywjhgMiCCyOWn2Ut756rRD74YxBYcQVYSJEhMpYppDkOr0jSB+7Qx7/VPQ575B/RT6do6qnXRE9ZO2pnm5VhquOGlZP/Q5p1T72XKaYyKXcipm//aOnH/eYCuyd7Oln513FauiyUpFgx9izLqvHo037QHXZ1kKPZF0Wfks48cLymiks0cz/i8iCkf+hg3q/AA==</diagram></mxfile>"
  },
  {
    "path": "docs/index.adoc",
    "content": ":description: Embassy Book\n:sectanchors:\n:doctype: book\n:toc:\n:toc-placement: left\n:toclevels: 2\n:imagesdir: images\n:source-highlighter: rouge\n\n# Embassy Book\n\nWelcome to the Embassy Book. The Embassy Book is for everyone who wants to use Embassy and understand how Embassy works.\n\ninclude::pages/overview.adoc[leveloffset = 1]\ninclude::pages/beginners.adoc[leveloffset = 1]\ninclude::pages/developer.adoc[leveloffset = 1]\ninclude::pages/system.adoc[leveloffset = 1]\ninclude::pages/faq.adoc[leveloffset = 1]\n"
  },
  {
    "path": "docs/pages/basic_application.adoc",
    "content": "= A basic Embassy application\n\nSo you've got one of the examples running, but what now? Let's go through a simple Embassy application for the nRF52 DK to understand it better.\n\n== Main\n\nThe full example can be found link:https://github.com/embassy-rs/embassy/tree/main/docs/examples/basic[here].\n\nNOTE: If you’re using VS Code and rust-analyzer to view and edit the examples, you may need to make some changes to `.vscode/settings.json` to tell it which project we’re working on. Follow the instructions commented in that file to get rust-analyzer working correctly.\n\n=== Bare metal\n\nThe first thing you’ll notice are two attributes at the top of the file. These tell the compiler that the program has no access to std, and that there is no main function (because it is not run by an OS).\n\n[source,rust]\n----\ninclude::../examples/basic/src/main.rs[lines=\"1..2\"]\n----\n\n=== Dealing with errors\n\nThen, what follows are some declarations on how to deal with panics and faults. During development, a good practice is to rely on `defmt-rtt` and `panic-probe` to print diagnostics to the terminal:\n\n[source,rust]\n----\ninclude::../examples/basic/src/main.rs[lines=\"9\"]\n----\n\n=== Task declaration\n\nAfter a bit of import declaration, the tasks run by the application should be declared:\n\n[source,rust]\n----\ninclude::../examples/basic/src/main.rs[lines=\"12..23\"]\n----\n\nAn embassy task must be declared `async`, and may NOT take generic arguments. In this case, we are handed the LED that should be blinked and the interval of the blinking.\n\nNOTE: Notice that there is no busy waiting going on in this task. It is using the Embassy timer to yield execution, allowing the microcontroller to sleep in between the blinking.\n\n=== Main\n\nThe main entry point of an Embassy application is defined using the `#[embassy_executor::main]` macro. The entry point is passed a `Spawner`, which it can use to spawn other tasks.\n\nWe then initialize the HAL with a default config, which gives us a `Peripherals` struct we can use to access the MCU’s various peripherals. In this case, we want to configure one of the pins as a GPIO output driving the LED:\n\n[source,rust]\n----\ninclude::../examples/basic/src/main.rs[lines=\"26..-1\"]\n----\n\nWhat happens when the `blinker` task has been spawned and main returns? Well, the main entry point is actually just like any other task, except that you can only have one and it takes some specific type arguments. The magic lies within the `#[embassy_executor::main]` macro. The macro does the following:\n\n. Creates an Embassy Executor\n. Defines a main task for the entry point\n. Runs the executor spawning the main task\n\nThere is also a way to run the executor without using the macro, in which case you have to create the `Executor` instance yourself.\n\n== The Cargo.toml\n\nThe project definition needs to contain the embassy dependencies:\n\n[source,toml]\n----\ninclude::../examples/basic/Cargo.toml[lines=\"10..12\"]\n----\n\nDepending on your microcontroller, you may need to replace `embassy-nrf` with something else (`embassy-stm32` for STM32. Remember to update feature flags as well).\n\nIn this particular case, the nrf52840 chip is selected, and the RTC1 peripheral is used as the time driver.\n"
  },
  {
    "path": "docs/pages/beginners.adoc",
    "content": "= For beginners\n\nThe articles in this section are primarily aimed at users new to Embassy,\nshowing how to get started, how to structure your project and other best practices.\n\ninclude::getting_started.adoc[leveloffset = 2]\ninclude::basic_application.adoc[leveloffset = 2]\ninclude::project_structure.adoc[leveloffset = 2]\ninclude::new_project.adoc[leveloffset = 2]\ninclude::best_practices.adoc[leveloffset = 2]\ninclude::layer_by_layer.adoc[leveloffset = 2]\n\n== What's next?\n\nNow that you're familiar with the basics, the xref:_system_description[System description] section covers the executor, HALs, bootloader, and other Embassy components in more detail.\n"
  },
  {
    "path": "docs/pages/best_practices.adoc",
    "content": "= Best Practices\n\nOver time, a couple of best practices have emerged. The following list should serve as a guideline for developers writing embedded software in _Rust_, especially in the context of the _Embassy_ framework.\n\n== Passing Buffers by Reference\nIt may be tempting to pass arrays or wrappers, like link:https://docs.rs/heapless/latest/heapless/[`heapless::Vec`],\nto a function or return one just like you would with a `std::Vec`. However, in most embedded applications you don't\nwant to spend resources on an allocator and end up placing buffers on the stack. This, however, can easily blow up\nyour stack if you are not careful.\n\nConsider the following example:\n[,rust]\n----\nfn process_buffer(mut buf: [u8; 1024]) -> [u8; 1024] {\n    // do stuff and return new buffer\n    for elem in buf.iter_mut() {\n        *elem = 0;\n    }\n    buf\n}\n\npub fn main() -> () {\n    let buf = [1u8; 1024];\n    let buf_new = process_buffer(buf);\n    // do stuff with buf_new\n    ()\n}\n----\nWhen calling `process_buffer` in your program, a copy of the buffer you pass to the function will be created,\nconsuming another 1024 bytes.\nAfter the processing, another 1024 byte buffer will be placed on the stack to be returned to the caller.\n(You can check the assembly, there will be two memcopy operations, e.g., `bl __aeabi_memcpy` when compiling for a Cortex-M processor.)\n\n*Possible Solution:*\n\nPass the data by reference and not by value on both, the way in and the way out.\nFor example, you could return a slice of the input buffer as the output.\nRequiring the lifetime of the input slice and the output slice to be the same, the memory safety of this procedure will be enforced by the compiler.\n\n[,rust]\n----\nfn process_buffer<'a>(buf: &'a mut [u8]) -> &'a mut[u8] {\n    for elem in buf.iter_mut() {\n        *elem = 0;\n    }\n    buf\n}\n\npub fn main() -> () {\n    let mut buf = [1u8; 1024];\n    let buf_new = process_buffer(&mut buf);\n    // do stuff with buf_new\n    ()\n}\n----\n"
  },
  {
    "path": "docs/pages/bootloader.adoc",
    "content": "= Bootloader\n\n`embassy-boot` is a lightweight bootloader supporting firmware application upgrades in a power-fail-safe way, with trial boots and rollbacks.\n\nThe update method used is referred to as an A/B partition update scheme.\n\nWith a general-purpose OS, A/B partition update is accomplished by directly booting either the A or B partition depending on the update state.\nTo accomplish the same goal in a way that is portable across all microcontrollers, `embassy-boot` swaps data page by page (in both directions) between the DFU and the Active partition when a firmware update is triggered. +\nBecause the original Active application is moved into the DFU partition during this update, the operation can be reversed if the update is interrupted or the new firmware does not flag that it booted successfully. +\nSee the design section for more details on how this is implemented.\n\nThe bootloader can be used either as a library or be flashed directly if you are happy with the default configuration and capabilities.\n\nBy design, the bootloader does not provide any network capabilities. Networking capabilities for fetching new firmware can be provided by the user application, using the bootloader as a library for updating the firmware, or by using the bootloader as a library and adding this capability yourself.\n\nThe bootloader supports both internal and external flash by relying on the `embedded-storage` traits. The bootloader optionally supports the verification of firmware that has been digitally signed (recommended).\n\n\n== Hardware support\n\nThe bootloader supports:\n\n* nRF52 with and without softdevice\n* STM32 L4, WB, WL, L1, L0, F3, F7 and H7\n* Raspberry Pi: RP2040\n\nIn general, the bootloader works on any platform that implements the `embedded-storage` traits for its internal flash, but may require custom initialization code to work.\n\nSTM32L0x1 devices require the `flash-erase-zero` feature to be enabled.\n\n== Design\n\nimage::bootloader_flash.png[Bootloader flash layout]\n\nThe bootloader divides the storage into 4 main partitions, configurable when creating the bootloader\ninstance or via linker scripts:\n\n* BOOTLOADER - Where the bootloader is placed. The bootloader itself consumes about 8kB of flash, but if you need to debug it and have space available, increasing this to 24kB will allow you to run the bootloader with probe-rs.\n* ACTIVE - Where the main application is placed. The bootloader will attempt to load the application at the start of this partition. This partition is only written to by the bootloader. The size required for this partition depends on the size of your application.\n* DFU - Where the application-to-be-swapped is placed. This partition is written to by the application. This partition must be at least 1 page bigger than the ACTIVE partition, since the swap algorithm uses the extra space to ensure power safe copy of data:\n+\nPartition Size~dfu~= Partition Size~active~+ Page Size~active~\n+\nAll values are specified in bytes.\n\n* BOOTLOADER STATE - Where the bootloader stores the current state describing if the active and dfu partitions need to be swapped. When the new firmware has been written to the DFU partition, a magic field is written to instruct the bootloader that the partitions should be swapped. This partition must be able to store a magic field as well as the partition swap progress. The partition size is given by:\n+\nPartition Size~state~ = (2 × Write Size~state~) + (4 × Write Size~state~ × Partition Size~active~ / Page Size~active~)\n+\nAll values are specified in bytes.\n\nThe partitions for ACTIVE (+BOOTLOADER), DFU and BOOTLOADER_STATE may be placed in separate flash. The page size used by the bootloader is determined by the lowest common multiple of the ACTIVE and DFU page sizes.\nThe BOOTLOADER_STATE partition must be big enough to store two words, plus four words per page in the ACTIVE partition.\n\nThe bootloader has a platform-agnostic part, which implements the power fail safe swapping algorithm given the boundaries set by the partitions. The platform-specific part is a minimal shim that provides additional functionality such as watchdogs or supporting the nRF52 softdevice.\n\nNOTE: The linker scripts for the application and bootloader look similar, but the FLASH region must point to the BOOTLOADER partition for the bootloader, and the ACTIVE partition for the application.\n\n=== FirmwareUpdater\n\nThe `FirmwareUpdater` is an object for conveniently flashing firmware to the DFU partition and subsequently marking it as being ready for swapping with the active partition on the next reset. Its principle methods are `write_firmware`, which is called once per the size of the flash \"write block\" (typically 4KiB), and `mark_updated`, which is the final call.\n\n=== Verification\n\nThe bootloader supports the verification of firmware that has been flashed to the DFU partition. Verification requires that firmware has been signed digitally using link:https://ed25519.cr.yp.to/[`ed25519`] signatures. With verification enabled, the `FirmwareUpdater::verify_and_mark_updated` method is called in place of `mark_updated`. A public key and signature are required, along with the actual length of the firmware that has been flashed. If verification fails then the firmware will not be marked as updated and therefore be rejected.\n\nSignatures are normally conveyed with the firmware to be updated and not written to flash. How signatures are provided is a firmware responsibility.\n\nTo enable verification use either the `ed25519-dalek` or `ed25519-salty` features when depending on the `embassy-boot` crate. We recommend `ed25519-salty` at this time due to its small size.\n\n==== Tips on keys and signing with ed25519\n\nEd25519 is a public key signature system where you are responsible for keeping the private key secure. We recommend embedding the *public* key in your program so that it can be easily passed to `verify_and_mark_updated`. An example declaration of the public key in your firmware:\n\n[source, rust]\n----\nstatic PUBLIC_SIGNING_KEY: &[u8] = include_bytes!(\"key.pub\");\n----\n\nSignatures are often conveyed along with firmware by appending them.\n\nEd25519 keys can be generated by a variety of tools. We recommend link:https://man.openbsd.org/signify[`signify`] as it is in wide use to sign and verify OpenBSD distributions, and is straightforward to use.\n\nThe following set of Bash commands can be used to generate public and private keys on Unix platforms, and also generate a local `key.pub` file with the `signify` file headers removed. Declare a `SECRETS_DIR` environment variable in a secure location.\n\n[source, bash]\n----\nsignify -G -n -p $SECRETS_DIR/key.pub -s $SECRETS_DIR/key.sec\ntail -n1 $SECRETS_DIR/key.pub | base64 -d -i - | dd ibs=10 skip=1 > key.pub\nchmod 700 $SECRETS_DIR/key.sec\nexport SECRET_SIGNING_KEY=$(tail -n1 $SECRETS_DIR/key.sec)\n----\n\nThen, to sign your firmware given a declaration of `FIRMWARE_DIR` and a firmware filename of `myfirmware`:\n\n[source, bash]\n----\nshasum -a 512 -b $FIRMWARE_DIR/myfirmware | head -c128 | xxd -p -r > $SECRETS_DIR/message.txt\nsignify -S -s $SECRETS_DIR/key.sec -m $SECRETS_DIR/message.txt -x $SECRETS_DIR/message.txt.sig\ncp $FIRMWARE_DIR/myfirmware $FIRMWARE_DIR/myfirmware+signed\ntail -n1 $SECRETS_DIR/message.txt.sig | base64 -d -i - | dd ibs=10 skip=1 >> $FIRMWARE_DIR/myfirmware+signed\n----\n\nRemember, guard the `$SECRETS_DIR/key.sec` key as compromising it means that another party can sign your firmware.\n"
  },
  {
    "path": "docs/pages/developer.adoc",
    "content": "= For developers\n\nThis section covers the internals of Embassy, aimed at contributors and developers who want to understand how Embassy is built or add support for new hardware.\n\ninclude::developer_stm32.adoc[leveloffset = 2]"
  },
  {
    "path": "docs/pages/developer_stm32.adoc",
    "content": "= STM32\n\n== Understanding metapac\n\nWhen a project that imports `embassy-stm32` is compiled, that project selects the feature corresponding to the chip that project is using. Based on that feature, `embassy-stm32` selects supported link:https://anysilicon.com/ip-intellectual-property-core-semiconductors/[IP] for the chip, and enables the corresponding HAL implementations. But how does `embassy-stm32` know what IP the chip contains, out of the hundreds of chips that we support? It's a long story that starts with `stm32-data-sources`.\n\n== `stm32-data-sources`\n\nlink:https://github.com/embassy-rs/stm32-data-sources[`stm32-data-sources`] is a mostly barren repository. It has no README, no documentation, and few watchers. But it's the core of what makes `embassy-stm32` possible. The data for every chip that we support is taken in part from a corresponding XML file like link:https://github.com/embassy-rs/stm32-data-sources/blob/b8b85202e22a954d6c59d4a43d9795d34cff05cf/cubedb/mcu/STM32F051K4Ux.xml[`STM32F051K4Ux.xml`]. In that file, you'll see lines like the following:\n\n[source,xml]\n----\n    <IP InstanceName=\"I2C1\" Name=\"I2C\" Version=\"i2c2_v1_1_Cube\"/>\n    <!-- snip  -->\n    <IP ConfigFile=\"TIM-STM32F0xx\" InstanceName=\"TIM1\" Name=\"TIM1_8F0\" Version=\"gptimer2_v2_x_Cube\"/>\n----\n\nThese lines indicate that this chip has an i2c, and that it's version is \"v1_1\". It also indicates that it has a general purpose timer that with a version of \"v2_x\". From this data, it's possible to determine which implementations should be included in `embassy-stm32`. But actually doing that is another matter.\n\n\n== `stm32-data`\n\nWhile all users of this project are familiar with `embassy-stm32`, fewer are familiar with the project that powers it: `stm32-data`. This project doesn't just aim to generate data for `embassy-stm32`, but for machine consumption in general. To achieve this, information from multiple files from the `stm32-data-sources` project are combined and parsed to assign register block implementations for each supported IP. The core of this matching resides in `chips.rs`:\n\n[source,rust]\n----\n    (\".*:I2C:i2c2_v1_1\", (\"i2c\", \"v2\", \"I2C\")),\n    // snip\n    (r\".*TIM\\d.*:gptimer.*\", (\"timer\", \"v1\", \"TIM_GP16\")),\n----\n\nIn this case, the i2c version corresponds to our \"v2\" and the general purpose timer version corresponds to our \"v1\". Therefore, the `i2c_v2.yaml` and `timer_v1.yaml` register block implementations are assigned to those IP, respectively. The result is that these lines are generated in `STM32F051K4.json`:\n\n[source,json]\n----\n    {\n        \"name\": \"I2C1\",\n        \"address\": 1073763328,\n        \"registers\": {\n            \"kind\": \"i2c\",\n            \"version\": \"v2\",\n            \"block\": \"I2C\"\n        },\n        // snip\n    }\n    // snip\n    {\n        \"name\": \"TIM1\",\n        \"address\": 1073818624,\n        \"registers\": {\n            \"kind\": \"timer\",\n            \"version\": \"v1\",\n            \"block\": \"TIM_ADV\"\n        },\n        // snip\n    }\n----\n\nIn addition to register blocks, data for pin and RCC mapping is also generated and consumed by `embassy-stm32`. `stm32-metapac-gen` is used to package and publish the data as a crate.\n\n\n== `embassy-stm32`\n\nIn the `lib.rs` file located in the root of `embassy-stm32`, you'll see this line:\n\n[source,rust]\n----\n#[cfg(i2c)]\npub mod i2c;\n----\n\nAnd in the `mod.rs` of the i2c mod, you'll see this:\n\n[source,rust]\n----\n#[cfg_attr(i2c_v2, path = \"v2.rs\")]\n----\n\nBecause i2c is supported for STM32F051K4 and its version corresponds to our \"v2\", the `i2c` and `i2c_v2`, configuration directives will be present, and `embassy-stm32` will include these files, respectively. This and other configuration directives and tables are generated from the data for chip, allowing `embassy-stm32` to expressively and clearly adapt logic and implementations to what is required for each chip. Compared to other projects across the embedded ecosystem, `embassy-stm32` is the only project that can re-use code across the entire stm32 lineup and remove difficult-to-implement unsafe logic to the HAL.\n"
  },
  {
    "path": "docs/pages/embassy_in_the_wild.adoc",
    "content": "= Embassy in the wild!\n\nHere are known examples of real-world projects which make use of Embassy. Feel free to link:https://github.com/embassy-rs/embassy/blob/main/docs/pages/embassy_in_the_wild.adoc[add more]!\n\n_newer entries at the top_\n\n* link:https://github.com/tendan/remote_rc_bt_esp32[ESPress WheeLE: Remote-Controlled Rover via Bluetooth LE]\n** Robust, energy-efficient, and safety-focused remote control solution for small-scale robotic platforms using instructions received via Bluetooth LE.\n* link:https://github.com/thataquarel/protovolt[ProtoV MINI: A USB-C mini lab power supply]\n** A dual-channel USB PD powered breadboard power supply based on the RP2040, running embedded graphics. Open-source schematics and firmware.\n* link:https://github.com/Dawson-HEP/opentrig/[Opentrig: A particle physics trigger and data acquisition system]\n** Digital event trigger with threshold, data acquisition system designed to interface with AIDA-2020 TLU systems, tested at the DESY II Test Beam Facility. Based on the RP2040, and Embassy's async event handling.\n* link:https://github.com/1-rafael-1/air-quality-monitor[Air Quality Monitor]\n** Air Quality Monitor based on rp2350 board, ens160 and aht21 sensors and ssd1306 display. Code and 3D printable enclosure included.\n* link:https://github.com/CarlKCarlK/clock[Embassy Clock: Layered, modular bare-metal clock with emulation]\n** A `no_std` Raspberry Pi Pico clock demonstrating layered Embassy tasks (Display->Blinker->Clock) for clean separation of multiplexing, blinking, and UI logic. Features single-button HH:MM/MM:SS time-set UI, heapless data structures, and a Renode emulator for hardware-free testing. See link:https://medium.com/@carlmkadie/how-rust-embassy-shine-on-embedded-devices-part-2-aad1adfccf72[this article] for details.\n* link:https://github.com/1-rafael-1/simple-robot[A simple tracked robot based on Raspberry Pi Pico 2]\n** A hobbyist project building a tracked robot with basic autonomous and manual drive mode.\n* link:https://github.com/1-rafael-1/pi-pico-alarmclock-rust[A Raspberry Pi Pico W Alarmclock]\n** A hobbyist project building an alarm clock around a Pi Pico W complete with code, components list and enclosure design files.\n* link:https://github.com/haobogu/rmk/[RMK: A feature-rich Rust keyboard firmware]\n** RMK has built-in layer support, wireless(BLE) support, real-time key editing support using vial, and more!\n** Targets STM32, RP2040, nRF52 and ESP32 MCUs\n* link:https://github.com/cbruiz/printhor/[Printhor: The highly reliable but not necessarily functional 3D printer firmware]\n** Targets some STM32 MCUs\n* link:https://github.com/card-io-ecg/card-io-fw[Card/IO firmware] - firmware for an open source ECG device\n** Targets the ESP32-S3 or ESP32-C6 MCU\n* The link:https://github.com/lora-rs/lora-rs[lora-rs] project includes link:https://github.com/lora-rs/lora-rs/tree/main/examples/stm32l0/src/bin[various standalone examples] for NRF52840, RP2040, STM32L0 and STM32WL\n* link:https://github.com/matoushybl/air-force-one[Air force one: A simple air quality monitoring system]\n** Targets nRF52 and uses nrf-softdevice\n\n* link:https://github.com/schmettow/ylab-edge-go[YLab Edge Go] and link:https://github.com/schmettow/ylab-edge-pro[YLab Edge Pro] projects develop\nfirmware (RP2040, STM32) for capturing physiological data in behavioural science research. Included so far are:\n** biopotentials (analog ports)\n** motion capture (6-axis accelerometers)\n** air quality (CO2, Temp, Humidity)\n** comes with an app for capturing and visualizing data [link:https://github.com/schmettow/ystudio-zero[Ystudio]]\n"
  },
  {
    "path": "docs/pages/examples.adoc",
    "content": "= Examples\n\nEmbassy provides examples for all HALs supported. You can find them in the `examples/` folder.\n\n\nMain loop example\n\n[source,rust]\n----\ninclude::../examples/examples/std/src/bin/tick.rs[]\n----\n"
  },
  {
    "path": "docs/pages/faq.adoc",
    "content": "= Frequently Asked Questions\n\nThese are a list of unsorted, commonly asked questions and answers.\n\nPlease feel free to add items to link:https://github.com/embassy-rs/embassy/edit/main/docs/pages/faq.adoc[this page], especially if someone in the chat answered a question for you!\n\n== How to deploy to RP2040 or RP235x without a debugging probe.\n\nInstall link:https://github.com/raspberrypi/pico-sdk-tools/releases[Picotool] for uploading the binary.\n\nConfigure the runner to use this tool, add this to `.cargo/config.toml`:\n[source,toml]\n----\n[target.'cfg(all(target_arch = \"arm\", target_os = \"none\"))']\nrunner = \"picotool load --update --verify --execute -t elf\"\n----\n\nPicotool will detect your device and upload the binary, skipping identical flash sectors (the `--update` command-line flag), verify that the binary was written correctly (`--verify`), and then run your new code (`--execute`). Run `picotool help load` for more information.\n\n== Missing main macro\n\nIf you see an error like this:\n\n[source,rust]\n----\n#[embassy_executor::main]\n|                   ^^^^ could not find `main` in `embassy_executor`\n----\n\nYou are likely missing some features of the `embassy-executor` crate.\n\nFor Cortex-M targets, check whether ALL of the following features are enabled in your `Cargo.toml` for the `embassy-executor` crate:\n\n* `arch-cortex-m`\n* `executor-thread`\n\nFor ESP32, consider using the executors and `#[main]` macro provided by link:https://crates.io/crates/esp-hal[esp-hal].\n\n== Why is my binary so big?\n\nThe first step to managing your binary size is to set up your link:https://doc.rust-lang.org/cargo/reference/profiles.html[profiles].\n\n[source,toml]\n----\n[profile.release]\nlto = true\nopt-level = \"s\"\nincremental = false\ncodegen-units = 1\n# note: debug = true is okay - debuginfo isn't flashed to the device!\ndebug = true\n----\n\nAll of these flags are elaborated on in the Rust Book page linked above.\n\n=== My binary is still big... filled with `std::fmt` stuff!\n\nThis means your code is sufficiently complex that `panic!` invocation's formatting requirements could not be optimized out, despite your usage of `panic-halt` or `panic-reset`.\n\nYou can remedy this by adding the following to your `.cargo/config.toml`:\n\n[source,toml]\n----\n[unstable]\nbuild-std = [\"core\"]\nbuild-std-features = [\"panic_immediate_abort\"]\n----\n\nThis replaces all panics with a `UDF` (undefined) instruction.\n\nDepending on your chipset, this will exhibit different behavior.\n\nRefer to the spec for your chipset, but for `thumbv6m`, it results in a hardfault. Which can be configured like so:\n\n[source,rust]\n----\n#[exception]\nunsafe fn HardFault(_frame: &ExceptionFrame) -> ! {\n    SCB::sys_reset() // <- you could do something other than reset\n}\n----\n\nRefer to cortex-m's link:https://docs.rs/cortex-m-rt/latest/cortex_m_rt/attr.exception.html[exception handling] for more info.\n\n== `embassy-time` throws linker errors\n\nIf you see linker error like this:\n\n[source,text]\n----\n  = note: rust-lld: error: undefined symbol: _embassy_time_now\n          >>> referenced by driver.rs:127 (src/driver.rs:127)\n          >>>               embassy_time-846f66f1620ad42c.embassy_time.4f6a638abb75dd4c-cgu.0.rcgu.o:(embassy_time::driver::now::hefb1f99d6e069842) in archive Devel/Embedded/pogodyna/target/thumbv7em-none-eabihf/debug/deps/libembassy_time-846f66f1620ad42c.rlib\n\n          rust-lld: error: undefined symbol: _embassy_time_schedule_wake\n          >>> referenced by driver.rs:144 (src/driver.rs:144)\n          >>>               embassy_time-846f66f1620ad42c.embassy_time.4f6a638abb75dd4c-cgu.0.rcgu.o:(embassy_time::driver::schedule_wake::h530a5b1f444a6d5b) in archive Devel/Embedded/pogodyna/target/thumbv7em-none-eabihf/debug/deps/libembassy_time-846f66f1620ad42c.rlib\n----\n\nYou probably need to enable a time driver for your HAL (not in `embassy-time`!). For example with `embassy-stm32`, you might need to enable `time-driver-any`:\n\n[source,toml]\n----\n[dependencies.embassy-stm32]\nversion = \"0.1.0\"\nfeatures = [\n    # ...\n    \"time-driver-any\", # Add this line!\n    # ...\n]\n----\n\nIf you are in the early project setup phase and not using anything from the HAL, make sure the HAL is explicitly used to prevent the linker removing it as dead code by adding this line to your source:\n\n[source,rust]\n----\nuse embassy_stm32 as _;\n----\n\nAnother common error you may experience is:\n\n[source,text]\n----\n = note: rust-lld: error: undefined symbol: __pender\n          >>> referenced by mod.rs:373 (src/raw/mod.rs:373)\n          >>>               embassy_executor-e78174e249bca7f4.embassy_executor.1e9d60fc90940543-cgu.0.rcgu.o:(embassy_executor::raw::Pender::pend::h0f19b6e01762e4cd) in archive [...]libembassy_executor-e78174e249bca7f4.rlib\n----\n\nThere are two possible causes to this error:\n\n* You are using `embassy-executor` without enabling one of the architecture-specific features, but you are using a HAL that does not bring its own executors. For example, for Cortex-M (like the RP2040), you need to enable the `arch-cortex-m` feature of `embassy-executor`.\n* You are not using `embassy-executor`. In this case, you need to enable the one of the `generic-queue-X` features of `embassy-time`.\n\n== Error: `Only one package in the dependency graph may specify the same links value.`\n\nYou have multiple versions of the same crate in your dependency tree. This means that some of your\nembassy crates are coming from crates.io, and some from git, each of them pulling in a different set\nof dependencies.\n\nTo resolve this issue, make sure to only use a single source for all your embassy crates!\nTo do this, you should patch your dependencies to use git sources using `[patch.crates.io]`\nand maybe `[patch.'https://github.com/embassy-rs/embassy.git']`.\n\nExample:\n\n[source,toml]\n----\n[patch.crates-io]\nembassy-time-queue-utils = { git = \"https://github.com/embassy-rs/embassy.git\", rev = \"7f8af8a\" }\nembassy-time-driver = { git = \"https://github.com/embassy-rs/embassy.git\", rev = \"7f8af8a\" }\n# embassy-time = { git = \"https://github.com/embassy-rs/embassy.git\", rev = \"7f8af8a\" }\n----\n\nNote that the git revision should match any other embassy patches or git dependencies that you are using!\n\n== How can I optimize the speed of my embassy-stm32 program?\n\n* Make sure RCC is set up to go as fast as possible\n* Make sure link:https://docs.rs/cortex-m/latest/cortex_m/peripheral/struct.SCB.html[flash cache] is enabled\n* build with `--release`\n* Set the following keys for the release profile in your `Cargo.toml`:\n    ** `opt-level = \"s\"`\n    ** `lto = \"fat\"`\n* Set the following keys in the `[unstable]` section of your `.cargo/config.toml`\n    ** `build-std = [\"core\"]`\n    ** `build-std-features = [\"panic_immediate_abort\"]`\n* When using `InterruptExecutor`:\n    ** disable `executor-thread`\n    ** make `main` spawn everything, then enable link:https://docs.rs/cortex-m/latest/cortex_m/peripheral/struct.SCB.html#method.set_sleeponexit[SCB.SLEEPONEXIT] and `loop { cortex_m::asm::wfi() }`\n    ** *Note:*  If you need 2 priority levels, using 2 interrupt executors is better than 1 thread executor + 1 interrupt executor.\n\n== Can I use manual ISRs alongside Embassy?\n\nYes! This can be useful if you need to respond to an event as fast as possible, and the latency caused by the usual “ISR, wake, return from ISR, context switch to woken task” flow is too much for your application. \n\nYou may simply define a `#[interrupt] fn INTERRUPT_NAME() {}` handler as you would link:https://docs.rust-embedded.org/book/start/interrupts.html[in any other embedded rust project].\n\nOr you may define a struct implementing the `embassy-[family]::interrupt::typelevel::Handler` trait with an on_interrupt() method, and bind it to the interrupt vector via the `bind_interrupts!` macro, which introduces only a single indirection. This allows the mixing of manual ISRs with Embassy driver-defined ISRs; handlers will be called directly in the order they appear in the macro.\n\n== How can I measure resource usage (CPU, RAM, etc.)?\n\n=== For CPU Usage:\n\nThere are a couple techniques that have been documented, generally you want to measure how long you are spending in the idle or low priority loop.\n\nWe need to document specifically how to do this in embassy, but link:https://blog.japaric.io/cpu-monitor/[this older post] describes the general process.\n\nIf you end up doing this, please update this section with more specific examples!\n\n=== For Static Memory Usage\n\nTools like `cargo size` and `cargo nm` from `cargo-binutils` can tell you the size of any globals or other static usage. Specifically you will want to see the size of the `.data` and `.bss` sections, which together make up the total global/static memory usage.\n\n=== For Max Stack Usage\n\nCheck out link:https://github.com/Dirbaio/cargo-call-stack/[`cargo-call-stack`] for statically calculating worst-case stack usage. There are some caveats and inaccuracies possible with this, but this is a good way to get the general idea. See link:https://github.com/dirbaio/cargo-call-stack#known-limitations[the README] for more details.\n\n== The memory definition for my STM chip seems wrong, how do I define a `memory.x` file?\n\nIt could happen that your project compiles, flashes but fails to run. The following situation can be true for your setup:\n\nThe `memory.x` is generated automatically when enabling the `memory-x` feature on the `embassy-stm32` crate in the `Cargo.toml` file.\nThis, in turn, uses `stm32-metapac` to generate the `memory.x` file for you. Unfortunately, more often than not this memory definition is not correct.\n\nYou can override this by adding your own `memory.x` file. Such a file could look like this:\n```\nMEMORY\n{\n  FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 1024K\n  RAM (xrw)  : ORIGIN = 0x20000000, LENGTH = 320K\n}\n\n_stack_start = ORIGIN(RAM) + LENGTH(RAM);\n```\n\nPlease refer to the STM32 documentation for the specific values suitable for your board and setup. The STM32 Cube examples often contain a linker script `.ld` file.\nLook for the `MEMORY` section and try to determine the FLASH and RAM sizes and section start.\n\nIf you find a case where the memory.x is wrong, please report it on link:https://github.com/embassy-rs/stm32-data/issues/301[this Github issue] so other users are not caught by surprise.\n\n== The USB examples are not working on my board, is there anything else I need to configure?\n\nIf you are trying out the USB examples and your device does not connect, the most common issues are listed below.\n\n=== Incorrect RCC config\n\nCheck your board and crystal/oscillator, in particular make sure that `HSE` is set to the correct value, e.g. `8_000_000` Hertz if your board does indeed run on a 8 MHz oscillator.\n\n=== VBUS detection on STM32 platform\n\nThe USB specification requires that all USB devices monitor the bus for detection of plugging/unplugging actions. The devices must pull-up the D+ or D- lane as soon as the host supplies VBUS.\n\nSee the docs, for example at link:https://docs.embassy.dev/embassy-stm32/git/stm32f401vc/usb/struct.Config.html[`usb/struct.Config.html`] for information on how to enable/disable `vbus_detection`.\n\nWhen the device is powered only from the USB bus that simultaneously serves as the data connection, this is optional. (If there's no power in VBUS the device would be off anyway, so it's safe to always assume there's power in VBUS, i.e. the USB cable is always plugged in). If your device doesn't have the required connections in place to allow VBUS sensing (see below), then this option needs to be set to `false` to work.\n\nWhen the device is powered from another power source and therefore can stay powered through USB cable plug/unplug events, then this must be implemented and `vbus_detection` MUST be set to `true`.\n\nIf your board is powered from the USB and you are unsure whether it supports `vbus_detection`, consult the schematics of your board to see if VBUS is connected to PA9 for USB Full Speed or PB13 for USB High Speed, vice versa, possibly with a voltage divider. When designing your own hardware, see ST application note AN4879 (in particular section 2.6) and the reference manual of your specific chip for more details.\n\n== Known issues (details and/or mitigations)\n\nThese are issues that are commonly reported. Help wanted fixing them, or improving the UX when possible!\n\n=== STM32H5 and STM32H7 power issues\n\nSTM32 chips with built-in power management (SMPS and LDO) settings often cause user problems when the configuration does not match how the board was designed.\n\nSettings from the examples, or even from other working boards, may not work on YOUR board, because they are wired differently.\n\nAdditionally, some PWR settings require a full device reboot (and enough time to discharge any power capacitors!), making this hard to troubleshoot. Also, some\n\"wrong\" power settings will ALMOST work, meaning it will sometimes work on some boots, or for a while, but crash unexpectedly.\n\nThere is not a fix for this yet, as it is board/hardware dependant. See link:https://github.com/embassy-rs/embassy/issues/2806[this tracking issue] for more details\n\n=== STM32 BDMA only working out of some RAM regions\n\nThe STM32 BDMA controller included in some STM32H7 chips has to be configured to use only certain regions of RAM,\notherwise the transfer will fail.\n\nIf you see errors that look like this:\n\n[source,plain]\n----\nDMA: error on BDMA@1234ABCD channel 4\n----\n\nYou need to set up your linker script to define a special region for this area and copy data to that region before using with BDMA.\n\nGeneral steps:\n\n1. Find out which memory region BDMA has access to. You can get this information from the bus matrix and the memory mapping table in the STM32 datasheet.\n2. Add the memory region to `memory.x`, you can modify the generated one from https://github.com/embassy-rs/stm32-data-generated/tree/main/data/chips.\n3. You might need to modify `build.rs` to make cargo pick up the modified `memory.x`.\n4. In your code, access the defined memory region using `#[unsafe(link_section = \".xxx\")]`\n5. Copy data to that region before using BDMA.\n\nSee link:https://github.com/embassy-rs/embassy/blob/main/examples/stm32h7/src/bin/spi_bdma.rs[SMT32H7 SPI BDMA example] for more details.\n\n== How do I switch to the `main` branch?\n\nSometimes to test new changes or fixes, you'll want to switch your project to using a version from GitHub.\n\nYou can add a section to your `Cargo.toml` file like this, you'll need to patch ALL embassy crates to the same revision:\n\nUsing `patch` will replace all direct AND indirect dependencies.\n\nSee the link:https://embassy.dev/book/#_starting_a_new_project[new project docs] for more details on this approach.\n\n[source,toml]\n----\n[patch.crates-io]\n# make sure to get the latest git rev from github, you can see the latest one here:\n# https://github.com/embassy-rs/embassy/commits/main/\nembassy-embedded-hal = { git = \"https://github.com/embassy-rs/embassy\",     rev = \"4cade64ebd34bf93458f17cfe85c5f710d0ff13c\" }\nembassy-executor     = { git = \"https://github.com/embassy-rs/embassy\",     rev = \"4cade64ebd34bf93458f17cfe85c5f710d0ff13c\" }\nembassy-rp           = { git = \"https://github.com/embassy-rs/embassy\",     rev = \"4cade64ebd34bf93458f17cfe85c5f710d0ff13c\" }\nembassy-sync         = { git = \"https://github.com/embassy-rs/embassy\",     rev = \"4cade64ebd34bf93458f17cfe85c5f710d0ff13c\" }\nembassy-time         = { git = \"https://github.com/embassy-rs/embassy\",     rev = \"4cade64ebd34bf93458f17cfe85c5f710d0ff13c\" }\nembassy-usb          = { git = \"https://github.com/embassy-rs/embassy\",     rev = \"4cade64ebd34bf93458f17cfe85c5f710d0ff13c\" }\nembassy-usb-driver   = { git = \"https://github.com/embassy-rs/embassy\",     rev = \"4cade64ebd34bf93458f17cfe85c5f710d0ff13c\" }\n----\n\n== How do I add support for a new microcontroller to embassy?\n\nThis is particularly for cortex-m, and potentially risc-v, where there is already support for basics like interrupt handling, or even already embassy-executor support for your architecture.\n\nThis is a *much harder path* than just using Embassy on an already supported chip. If you are a beginner, consider using embassy on an existing, well supported chip for a while, before you decide to write drivers from scratch. It's also worth reading the existing source of supported Embassy HALs, to get a feel for how drivers are implemented for various chips. You should already be comfortable reading and writing unsafe code, and understanding the responsibilities of writing safe abstractions for users of your HAL.\n\nThis is not the only possible approach, but if you are looking for where to start, this is a reasonable way to tackle the task:\n\n1. First, drop by the Matrix room or search around to see if someone has already started writing drivers, either in Embassy or otherwise in Rust. You might not have to start from scratch!\n2. Make sure the target is supported in probe-rs, it likely is, and if not, there is likely a cmsis-pack you can use to add support so that flashing and debugging is possible. You will definitely appreciate being able to debug with SWD or JTAG when writing drivers!\n3. See if there is an SVD (or SVDs, if it's a family) available, if it is, run it through chiptool to create a PAC for low level register access. If not, there are other ways (like scraping the PDF datasheets or existing C header files), but these are more work than starting from the SVD file to define peripheral memory locations necessary for writing drivers.\n4. Either make a fork of embassy repo, and add your target there, or make a repo that just contains the PAC and an empty HAL. It doesn't necessarily have to live in the embassy repo at first.\n5. Get a hello world binary working on your chip, either with minimal HAL or just PAC access, use delays and blink a light or send some raw data on some interface, make sure it works and you can flash, debug with defmt + RTT, write a proper linker script, etc.\n6. Get basic timer operations and timer interrupts working, upgrade your blinking application to use hardware timers and interrupts, and ensure they are accurate (with a logic analyzer or oscilloscope, if possible).\n7. Implement the embassy-time driver API with your timer and timer interrupt code, so that you can use embassy-time operations in your drivers and applications.\n8. Then start implementing whatever peripherals you need, like GPIOs, UART, SPI, I2C, etc. This is the largest part of the work, and will likely continue for a while! Don't feel like you need 100% coverage of all peripherals at first, this is likely to be an ongoing process over time.\n9. Start implementing the embedded-hal, embedded-io, and embedded-hal-async traits on top of your HAL drivers, once you start having more features completed. This will allow users to use standard external device drivers (e.g. sensors, actuators, displays, etc.) with your HAL.\n10. Discuss upstreaming the PAC/HAL for embassy support, or make sure your drivers are added to the awesome-embedded-rust list so that people can find it.\n\n== Multiple Tasks, or one task with multiple futures?\n\nSome examples end like this in main:\n\n[source,rust]\n----\n// Run everything concurrently.\n// If we had made everything `'static` above instead, we could do this using separate tasks instead.\njoin(usb_fut, join(echo_fut, log_fut)).await;\n----\n\nThere are two main ways to handle concurrency in Embassy:\n\n1. Spawn multiple tasks, e.g. with `#[embassy_executor::task]`\n2. Manage multiple futures inside ONE task using `join()` or `select()` (as shown above)\n\nIn general, either of these approaches will work. The main differences of these approaches are:\n\nWhen using **separate tasks**, each task needs its own RAM allocation, so there's a little overhead for each task, so one task that does three things will likely be a little bit smaller than three tasks that do one thing (not a lot, probably a couple dozen bytes). In contrast, with **multiple futures in one task**, you don't need multiple task allocations, and it will generally be easier to share data, or use borrowed resources, inside of a single task.\nAn example showcasing some methods for sharing things between tasks link:https://github.com/embassy-rs/embassy/blob/main/examples/rp/src/bin/sharing.rs[can be found here].\n\nBut when it comes to \"waking\" tasks, for example when a data transfer is complete or a button is pressed, it's faster to wake a dedicated task, because that task does not need to check which future is actually ready. `join` and `select` must check ALL of the futures they are managing to see which one (or which ones) are ready to do more work. This is because all Rust executors (like Embassy or Tokio) only have the ability to wake tasks, not specific futures. This means you will use slightly less CPU time juggling futures when using dedicated tasks.\n\nPractically, there's not a LOT of difference either way - so go with what makes it easier for you and your code first, but there will be some details that are slightly different in each case.\n\n== splitting peripherals resources between tasks\n\nThere are two ways to split resources between tasks, either manually assigned or by a convenient macro. See link:https://github.com/embassy-rs/embassy/blob/main/examples/rp/src/bin/assign_resources.rs[this example]\n\n== My code/driver works in debug mode, but not release mode (or with LTO)\n\nIssues like these while implementing drivers often fall into one of the following general causes, which are a good list of common errors to check for:\n\n1. Some kind of race condition - the faster code means you miss an interrupt or something\n2. Some kind of UB, if you have unsafe code, or something like DMA with fences missing\n3. Some kind of hardware errata, or some hardware misconfiguration like wrong clock speeds\n4. Some issue with an interrupt handler, either enabling, disabling, or re-enabling of interrupts when necessary\n5. Some kind of async issue, like not registering wakers fully before checking flags, or not registering or pending wakers at the right time\n\n== How can I prevent the thread-mode executor from going to sleep? ==\n\nIn some cases you might want to prevent the thread-mode executor from going to sleep, for example when doing so would result in current spikes that reduce analog performance.\nAs a workaround, you can spawn a task that yields in a loop, preventing the executor from going to sleep. Note that this may increase power consumption.\n\n[source,rust]\n----\n#[embassy_executor::task]\nasync fn idle() {\n    loop { embassy_futures::yield_now().await; }\n}\n----\n\n== Why is my bootloader restarting in loop?\n\n== Troubleshooting Bootloader Restart Loops\n\nIf your bootloader restarts in a loop, there could be multiple reasons. Here are some things to check:\n\n=== Validate the `memory.x` File\nThe bootloader performs critical checks when creating partitions using the addresses defined in `memory.x`. Ensure the following assertions hold true:\n\n[source,rust]\n----\nconst {\n    core::assert!(Self::PAGE_SIZE % ACTIVE::WRITE_SIZE as u32 == 0);\n    core::assert!(Self::PAGE_SIZE % ACTIVE::ERASE_SIZE as u32 == 0);\n    core::assert!(Self::PAGE_SIZE % DFU::WRITE_SIZE as u32 == 0);\n    core::assert!(Self::PAGE_SIZE % DFU::ERASE_SIZE as u32 == 0);\n}\n\n// Ensure enough progress pages to store copy progress\nassert_eq!(0, Self::PAGE_SIZE % aligned_buf.len() as u32);\nassert!(aligned_buf.len() >= STATE::WRITE_SIZE);\nassert_eq!(0, aligned_buf.len() % ACTIVE::WRITE_SIZE);\nassert_eq!(0, aligned_buf.len() % DFU::WRITE_SIZE);\n----\n\nIf any of these assertions fail, the bootloader will likely restart in a loop. This failure might not log any messages (e.g., when using `defmt`). Confirm that your `memory.x` file and flash memory align with these requirements.\n\n=== Handling Panic Logging\nSome panic errors might log messages, but certain microcontrollers reset before the message is fully printed. To ensure panic messages are logged, add a delay using no-operation (NOP) instructions before the reset:\n\n[source,rust]\n----\n#[panic_handler]\nfn panic(_info: &core::panic::PanicInfo) -> ! {\n    for _ in 0..10_000_000 {\n        cortex_m::asm::nop();\n    }\n    cortex_m::asm::udf();\n}\n----\n\n=== Feed the watchdog\n\n\nSome `embassy-boot` implementations (like `embassy-boot-nrf` and `embassy-boot-rp`) rely on a watchdog timer to detect application failure. The bootloader will restart if your application code does not properly feed the watchdog timer. Make sure to feed it correctly.\n"
  },
  {
    "path": "docs/pages/getting_started.adoc",
    "content": "= Getting started\n\nSo you want to try Embassy, great! To get started, there are a few tools you need to install:\n\n* link:https://rustup.rs/[rustup] - the Rust toolchain is needed to compile Rust code.\n* link:https://probe.rs/[probe-rs] - to flash the firmware on your device. If you already have other tools like `OpenOCD` setup, you can use that as well.\n* link:https://github.com/knurling-rs/flip-link[flip-link] - to link certain example binaries with stack overflow protection.\n\nIf you don't have any supported board, don't worry: you can also run embassy on your PC using the `std` examples.\n\n== Getting a board with examples\n\nEmbassy supports many microcontroller families, but the quickest way to get started is by using a board which Embassy has existing example code for.\n\nThis list is non-exhaustive. If your board isn’t included here, check the link:https://github.com/embassy-rs/embassy/tree/main/examples[examples folder] to see if example code has been written for it.\n\n=== nRF kits\n\n* link:https://www.nordicsemi.com/Products/Development-hardware/nrf52-dk[nRF52 DK]\n* link:https://www.nordicsemi.com/Products/Development-hardware/nRF9160-DK[nRF9160 DK]\n\n=== STM32 kits\n\n* link:https://www.st.com/en/evaluation-tools/nucleo-h743zi.html[STM32 Nucleo-144 development board with STM32H743ZI MCU]\n* link:https://www.st.com/en/evaluation-tools/nucleo-f429zi.html[STM32 Nucleo-144 development board with STM32F429ZI MCU]\n* link:https://www.st.com/en/evaluation-tools/b-l4s5i-iot01a.html[STM32L4+ Discovery kit IoT node, low-power wireless, BLE, NFC, WiFi]\n* link:https://www.st.com/en/evaluation-tools/b-l072z-lrwan1.html[STM32L0 Discovery kit LoRa, Sigfox, low-power wireless]\n* link:https://www.st.com/en/evaluation-tools/nucleo-wl55jc.html[STM32 Nucleo-64 development board with STM32WL55JCI MCU]\n* link:https://www.st.com/en/evaluation-tools/b-u585i-iot02a.html[Discovery kit for IoT node with STM32U5 series]\n\n\n=== RP2040 kits\n\n* link:https://www.raspberrypi.com/products/raspberry-pi-pico/[Raspberry Pi Pico]\n\n=== ESP32\n\n* link:https://github.com/esp-rs/esp-rust-board[ESP32C3]\n\n== Running an example\n\nFirst you need to clone the link:https://github.com/embassy-rs/embassy[github repository];\n\n[source, bash]\n----\ngit clone https://github.com/embassy-rs/embassy.git\ncd embassy\n----\n\nOnce you have a copy of the repository, find the examples folder for your board and build an example program. `blinky` is a good choice as all it does is blink an LED – the embedded world’s equivalent of “Hello World”.\n\n[source, bash]\n----\ncd examples/nrf52840\ncargo build --bin blinky --release\n----\n\nOnce you’ve confirmed you can build the example, connect your computer to your board with a debug probe and run it on hardware:\n\n[source, bash]\n----\ncargo run --bin blinky --release\n----\n\nIf everything worked correctly, you should see a blinking LED on your board, and debug output similar to this on your computer:\n\n[source]\n----\n    Finished dev [unoptimized + debuginfo] target(s) in 1m 56s\n     Running `probe-rs run --chip STM32F407VGTx target/thumbv7em-none-eabi/debug/blinky`\n(HOST) INFO  flashing program (71.36 KiB)\n(HOST) INFO  success!\n────────────────────────────────────────────────────────────────────────────────\n0 INFO  Hello World!\n└─ blinky::__embassy_main::task::{generator#0} @ src/bin/blinky.rs:18\n1 INFO  high\n└─ blinky::__embassy_main::task::{generator#0} @ src/bin/blinky.rs:23\n2 INFO  low\n└─ blinky::__embassy_main::task::{generator#0} @ src/bin/blinky.rs:27\n3 INFO  high\n└─ blinky::__embassy_main::task::{generator#0} @ src/bin/blinky.rs:23\n4 INFO  low\n└─ blinky::__embassy_main::task::{generator#0} @ src/bin/blinky.rs:27\n----\n\nNOTE: How does the `+cargo run+` command know how to connect to our board and program it? In each `examples` folder, there’s a `.cargo/config.toml` file which tells cargo to use link:https://probe.rs/[probe-rs] as the runner for ARM binaries in that folder. probe-rs handles communication with the debug probe and MCU. In order for this to work, probe-rs needs to know which chip it’s programming, so you’ll have to edit this file if you want to run examples on other chips.\n\n=== It didn’t work!\n\nIf you are having issues when running `+cargo run --release+`, please check the following:\n\n* You are specifying the correct `+--chip+` on the command line, OR\n* You have set `+.cargo/config.toml+`’s run line to the correct chip, AND\n* You have changed `+examples/Cargo.toml+`’s HAL (e.g. embassy-stm32) dependency's feature to use the correct chip (replace the existing stm32xxxx feature)\n\nAt this point the project should run. If you do not see a blinky LED for blinky, for example, be sure to check the code is toggling your board's LED pin.\n\nIf you are trying to run an example with `+cargo run --release+` and you see the following output:\n[source]\n----\n0.000000 INFO Hello World!\n└─ <invalid location: defmt frame-index: 14>\n0.000000 DEBUG rcc: Clocks { sys: Hertz(80000000), apb1: Hertz(80000000), apb1_tim: Hertz(80000000), apb2: Hertz(80000000), apb2_tim: Hertz(80000000), ahb1: Hertz(80000000), ahb2: Hertz(80000000), ahb3: Hertz(80000000) }\n└─ <invalid location: defmt frame-index: 124>\n0.000061 TRACE allocating type=Interrupt mps=8 interval_ms=255, dir=In\n└─ <invalid location: defmt frame-index: 68>\n0.000091 TRACE   index=1\n└─ <invalid location: defmt frame-index: 72>\n----\n\nTo get rid of the frame-index error add the following to your `Cargo.toml`:\n\n[source,toml]\n----\n[profile.release]\ndebug = 2\n----\n\nIf you’re getting an extremely long error message containing something like the following:\n\n[source]\n----\nerror[E0463]: can't find crate for `std`\n  |\n  = note: the `thumbv6m-none-eabi` target may not support the standard library\n  = note: `std` is required by `stable_deref_trait` because it does not declare `#![no_std]`\n----\n\nMake sure that you didn’t accidentally run `+cargo add probe-rs+` (which adds it as a dependency) instead of link:https://probe.rs/docs/getting-started/installation/[correctly installing probe-rs].\n\nIf you’re using a raspberry pi pico-w, make sure you’re running `+cargo run --bin wifi_blinky --release+` rather than the regular blinky. The pico-w’s on-board LED is connected to the WiFi chip, which needs to be initialized before the LED can be blinked.\n\nIf you’re using an rp2040 debug probe (e.g. the pico probe) and are having issues after running `probe-rs info`, unplug and reconnect the probe, letting it power cycle. Running `probe-rs info` is link:https://github.com/probe-rs/probe-rs/issues/1849[known to put the pico probe into an unusable state].\n\n:embassy-dev-faq-link-with-hash: https://embassy.dev/book/#_frequently_asked_questions\n:embassy-matrix-channel: https://matrix.to/#/#embassy-rs:matrix.org\n\nIf you’re still having problems, check the {embassy-dev-faq-link-with-hash}[FAQ], or ask for help in the {embassy-matrix-channel}[Embassy Chat Room].\n\n== What's next?\n\nCongratulations, you have your first Embassy application running! Here are some suggestions for where to go from here:\n\n* Read more about the xref:_embassy_executor[executor].\n* Read more about the xref:_hardware_abstraction_layer_hal[HAL].\n* Start xref:_a_basic_embassy_application[writing your application].\n* Learn how to xref:_starting_a_new_project[start a new embassy project by adapting an example].\n"
  },
  {
    "path": "docs/pages/hal.adoc",
    "content": "= Hardware Abstraction Layer (HAL)\n\nEmbassy provides HALs for several microcontroller families:\n\n* `embassy-nrf` for the nRF microcontrollers from Nordic Semiconductor\n* `embassy-stm32` for STM32 microcontrollers from ST Microelectronics\n* `embassy-rp` for the Raspberry Pi RP2040 and RP235x microcontrollers\n\nThese HALs implement async/await functionality for most peripherals while also implementing the\nasync traits in `embedded-hal` and `embedded-hal-async`. You can also use these HALs with another executor.\n\nFor the ESP32 series, there is an link:https://github.com/esp-rs/esp-hal[esp-hal] which you can use.\n\nFor the WCH 32-bit RISC-V series, there is an link:https://github.com/ch32-rs/ch32-hal[ch32-hal], which you can use.\n\nFor the Microchip PolarFire SoC, there is link:https://github.com/AlexCharlton/mpfs-hal[mpfs-hal].\n\nFor the Puya Semiconductor PY32 series, there is link:https://github.com/py32-rs/py32-hal[py32-hal]."
  },
  {
    "path": "docs/pages/imxrt.adoc",
    "content": "= Embassy iMXRT HAL\n\nThe link:https://github.com/embassy-rs/embassy/tree/main/embassy-imxrt[Embassy iMXRT HAL] is based on the following PACs (Peripheral Access Crate):\n\n* link:https://github.com/OpenDevicePartnership/mimxrt685s-pac[mimxrt685s-pac]\n* link:https://github.com/OpenDevicePartnership/mimxrt633s-pac[mimxrt633s-pac]\n\n== Peripherals\n\nThe following peripherals have a HAL implementation at present\n\n* CRC\n* DMA\n* GPIO\n* RNG\n* UART\n"
  },
  {
    "path": "docs/pages/layer_by_layer.adoc",
    "content": "= From bare metal to async Rust\n\nIf you're new to Embassy, it can be overwhelming to grasp all the terminology and concepts. This guide aims to clarify the different layers in Embassy, which problem each layer solves for the application writer.\n\nThis guide uses the STM32 IOT01A board, but should be easy to translate to any STM32 chip. For nRF, the PAC itself is not maintained within the Embassy project, but the concepts and the layers are similar.\n\nThe application we'll write is a simple 'push button, blink led' application, which is great for illustrating input and output handling for each of the examples we'll go through. We'll start at the Peripheral Access Crate (PAC) example and end at the async example.\n\n== PAC version\n\nThe PAC is the lowest API for accessing peripherals and registers, if you don't count reading/writing directly to memory addresses. It provides distinct types to make accessing peripheral registers easier, but it does little to prevent you from configuring or coordinating those registers incorrectly.\n\nWriting an application using the PAC directly is therefore not recommended, but if the functionality you want to use is not exposed in the upper layers, that's what you need to use.\n\nThe blinky app using PAC is shown below:\n\n[source,rust]\n----\ninclude::../examples/layer-by-layer/blinky-pac/src/main.rs[]\n----\n\nAs you can see, a lot of code is needed to enable the peripheral clocks and to configure the input pins and the output pins of the application.\n\nAnother downside of this application is that it is busy-looping while polling the button state. This prevents the microcontroller from utilizing any sleep mode to save power.\n\n== HAL version\n\nTo simplify our application, we can use the HAL instead. The HAL exposes higher level APIs that handle details such as:\n\n* Automatically enabling the peripheral clock when you're using the peripheral\n* Deriving and applying register configuration from higher level types\n* Implementing the embedded-hal traits to make peripherals useful in third party drivers\n\nThe HAL example is shown below:\n\n[source,rust]\n----\ninclude::../examples/layer-by-layer/blinky-hal/src/main.rs[]\n----\n\nAs you can see, the application becomes a lot simpler, even without using any async code. The `Input` and `Output` types hide all the details of accessing the GPIO registers and allow you to use a much simpler API for querying the state of the button and toggling the LED output.\n\nThe same downside from the PAC example still applies though: the application is busy looping and consuming more power than necessary.\n\n== Interrupt driven\n\nTo save power, we need to configure the application so that it can be notified when the button is pressed using an interrupt.\n\nOnce the interrupt is configured, the application can instruct the microcontroller to enter a sleep mode, consuming very little power.\n\nGiven Embassy focus on async Rust (which we'll come back to after this example), the example application must use a combination of the HAL and PAC in order to use interrupts. For this reason, the application also contains some helper functions to access the PAC (not shown below).\n\n[source,rust]\n----\ninclude::../examples/layer-by-layer/blinky-irq/src/main.rs[lines=\"1..57\"]\n----\n\nThe simple application is now more complex again, primarily because of the need to keep the button and LED states in the global scope where it is accessible by the main application loop, as well as the interrupt handler.\n\nTo do that, the types must be guarded by a mutex, and interrupts must be disabled whenever we are accessing this global state to gain access to the peripherals.\n\nLuckily, there is an elegant solution to this problem when using Embassy.\n\n== Async version\n\nIt's time to use the Embassy capabilities to its fullest. At the core, Embassy has an async executor, or a runtime for async tasks if you will. The executor polls a set of tasks (defined at compile time), and whenever a task `blocks`, the executor will run another task, or put the microcontroller to sleep.\n\n[source,rust]\n----\ninclude::../examples/layer-by-layer/blinky-async/src/main.rs[]\n----\n\nThe async version looks very similar to the HAL version, apart from a few minor details:\n\n* The main entry point is annotated with a different macro and has an async type signature. This macro creates and starts an Embassy runtime instance and launches the main application task. Using the `Spawner` instance, the application may spawn other tasks.\n* The peripheral initialization is done by the main macro, and is handed to the main task.\n* Before checking the button state, the application is awaiting a transition in the pin state (low -> high or high -> low).\n\nWhen `button.wait_for_any_edge().await` is called, the executor will pause the main task and put the microcontroller in sleep mode, unless there are other tasks that can run. On this chip, interrupt signals on EXTI lines 10-15 (including the button on EXTI line 13) raise the hardware interrupt EXTI15_10. This interrupt handler has been bound (using `bind_interrupts!`) to call the `InterruptHandler` provided by the exti module, so that whenever an interrupt is raised, the task awaiting the button via `wait_for_any_edge()` will be woken up.\n\nThe minimal overhead of the executor and the ability to run multiple tasks \"concurrently\" combined with the enormous simplification of the application, makes `async` a great fit for embedded.\n\n== Summary\n\nWe have seen how the same application can be written at the different abstraction levels in Embassy. First starting out at the PAC level, then using the HAL, then using interrupts, and then using interrupts indirectly using async Rust.\n"
  },
  {
    "path": "docs/pages/mcxa.adoc",
    "content": "= Embassy MCX-A HAL\n\nThe link:https://github.com/embassy-rs/embassy/tree/main/embassy-mcxa[Embassy MCX-A HAL] is based on the following PAC (Peripheral Access Crate):\n\n* link:https://github.com/embassy-rs/nxp-pac[nxp-pac]\n\n== Peripherals\n\nThe following peripherals have a HAL implementation at present\n\n* Clocks\n* GPIO\n* ADC\n* CLKOUT\n* I2C\n* I3C\n* LPUart\n* OSTimer\n* RTC\n* SPI controller\n* TRNG\n* WWDT\n* CDOG\n* CTIMER\n* CRC\n* DMA\n"
  },
  {
    "path": "docs/pages/microchip.adoc",
    "content": "= Embassy Microchip HAL\n\nThe link:https://github.com/embassy-rs/embassy/tree/main/embassy-microchip[Embassy Microchip HAL] is based on the following PAC (Peripheral Access Crate):\n\n* link:https://github.com/OpenDevicePartnership/mec17xx-pac[mec17xx-pac]\n\n== Peripherals\n\nThe following peripherals have a HAL implementation at present\n\n* GPIO\n* I2C\n* PWM\n* TACH\n* Time driver (using the CCT)\n"
  },
  {
    "path": "docs/pages/new_project.adoc",
    "content": "= Starting a new project\n\nOnce you’ve successfully xref:#_getting_started[run some example projects], the next step is to make a standalone Embassy project.\n\n== Tools for generating Embassy projects\n\n=== CLI\n- link:https://github.com/adinack/cargo-embassy[cargo-embassy] (STM32 and NRF)\n\n=== cargo-generate\n- link:https://github.com/lulf/embassy-template[embassy-template] (STM32, NRF, and RP)\n- link:https://github.com/bentwire/embassy-rp2040-template[embassy-rp2040-template] (RP)\n\n=== esp-generate\n- link:https://github.com/esp-rs/esp-generate[esp-generate] (ESP32 using esp-hal)\n\n== Starting a project from scratch\n\nAs an example, let’s create a new embassy project from scratch for a STM32G474. The same instructions are applicable for any supported chip with some minor changes.\n\nRun:\n\n[source,bash]\n----\ncargo new stm32g474-example\ncd stm32g474-example\n----\n\nto create an empty rust project:\n\n[source]\n----\nstm32g474-example\n├── Cargo.toml\n└── src\n    └── main.rs\n----\n\nLooking in link:https://github.com/embassy-rs/embassy/tree/main/examples[the Embassy examples], we can see there’s a `stm32g4` folder. Find `src/blinky.rs` and copy its contents into our `src/main.rs`.\n\n=== The .cargo/config.toml\n\nCurrently, we’d need to provide cargo with a target triple every time we run `cargo build` or `cargo run`. Let’s spare ourselves that work by copying `.cargo/config.toml` from `examples/stm32g4` into our project.\n\n[source]\n----\nstm32g474-example\n├── .cargo\n│   └── config.toml\n├── Cargo.toml\n└── src\n    └── main.rs\n----\n\nIn addition to a target triple, `.cargo/config.toml` contains a `runner` key which allows us to conveniently run our project on hardware with `cargo run` via probe-rs. In order for this to work, we need to provide the correct chip ID. We can do this by checking `probe-rs chip list`:\n\n[source,bash]\n----\n$ probe-rs chip list | grep -i stm32g474re\n        STM32G474RETx\n----\n\nand copying `STM32G474RETx` into `.cargo/config.toml` as so:\n\n[source,toml]\n----\n[target.'cfg(all(target_arch = \"arm\", target_os = \"none\"))']\n# replace STM32G071C8Rx with your chip as listed in `probe-rs chip list`\nrunner = \"probe-rs run --chip STM32G474RETx\"\n----\n\n=== Cargo.toml\n\nNow that cargo knows what target to compile for (and probe-rs knows what chip to run it on), we’re ready to add some dependencies.\n\nLooking in `examples/stm32g4/Cargo.toml`, we can see that the examples require a number of embassy crates. For blinky, we’ll only need three of them: `embassy-stm32`, `embassy-executor` and `embassy-time`.\n\n\nAt the time of writing, embassy is already published to crates.io. Therefore, dependencies can easily be added in Cargo.toml.\n\n[source,toml]\n----\n[dependencies]\nembassy-stm32 = { version = \"0.1.0\", features =  [\"defmt\", \"time-driver-any\", \"stm32g474re\", \"memory-x\", \"unstable-pac\", \"exti\"] }\nembassy-executor = { version = \"0.6.3\", features = [\"nightly\", \"platform-cortex-m\", \"executor-thread\", \"defmt\"] }\nembassy-time = { version = \"0.3.2\", features = [\"defmt\", \"defmt-timestamp-uptime\", \"tick-hz-32_768\"] }\n----\n\nPreviously, embassy needed to be installed straight from the git repository. Installing from git is still useful, if you want to checkout a specific revision of an embassy crate which is not yet published.\nThe recommended way of doing so is:\n\n* Copy the required `embassy-*` lines from the example `Cargo.toml`\n* Make any necessary changes to `features`, e.g. requiring the `stm32g474re` feature of `embassy-stm32`\n* Remove the `path = \"\"` keys in the `embassy-*` entries\n* Create a `[patch.crates-io]` section, with entries for each embassy crate we need. These should all contain identical values: a link to the git repository, and a reference to the commit we’re checking out. Assuming you want the latest commit, you can find it by running `git ls-remote https://github.com/embassy-rs/embassy.git HEAD`\n\nNOTE: When using this method, it’s necessary that the `version` keys in `[dependencies]` match up with the versions defined in each crate’s `Cargo.toml` in the specificed `rev` under `[patch.crates.io]`. This means that when updating, you have to a pick a new revision, change everything in `[patch.crates.io]` to match it, and then correct any versions under `[dependencies]` which have changed.\n\nAn example Cargo.toml file follows. Note that the `version` values must match the versions defined in the embassy crates at the git revision you are checking out, which may differ from the latest published versions on crates.io:\n\n[source,toml]\n----\n[dependencies]\nembassy-stm32 = {version = \"0.1.0\", features =  [\"defmt\", \"time-driver-any\", \"stm32g474re\", \"memory-x\", \"unstable-pac\", \"exti\"]}\nembassy-executor = { version = \"0.6.3\", features = [\"platform-cortex-m\", \"executor-thread\", \"defmt\"] }\nembassy-time = { version = \"0.3.2\", features = [\"defmt\", \"defmt-timestamp-uptime\", \"tick-hz-32_768\"] }\n\n[patch.crates-io]\nembassy-time = { git = \"https://github.com/embassy-rs/embassy\", rev = \"7703f47c1ecac029f603033b7977d9a2becef48c\" }\nembassy-executor = { git = \"https://github.com/embassy-rs/embassy\", rev = \"7703f47c1ecac029f603033b7977d9a2becef48c\" }\nembassy-stm32 = { git = \"https://github.com/embassy-rs/embassy\", rev = \"7703f47c1ecac029f603033b7977d9a2becef48c\" }\n----\n\nThere are a few other dependencies we need to build the project, but fortunately they’re much simpler to install. Copy their lines from the example `Cargo.toml` to the `[dependencies]` section in the new `Cargo.toml`:\n\n[source,toml]\n----\ndefmt = \"0.3.5\"\ndefmt-rtt = \"0.4.0\"\ncortex-m = {version = \"0.7.7\", features = [\"critical-section-single-core\"]}\ncortex-m-rt = \"0.7.3\"\npanic-probe = \"0.3.1\"\n----\n\nThese are the bare minimum dependencies required to run `blinky.rs`, but it’s worth taking a look at the other dependencies specified in the example `Cargo.toml`, and noting what features are required for use with embassy – for example `futures = { version = \"0.3.17\", default-features = false, features = [\"async-await\"] }`.\n\nFinally, copy the `[profile.release]` section from the example `Cargo.toml` into ours:\n\n[source,toml]\n----\n[profile.release]\ndebug = 2\n----\n\n=== rust-toolchain.toml\n\nBefore we can build our project, we need to add an additional file to tell cargo which toolchain to use. Copy the `rust-toolchain.toml` from the embassy repo to ours, and trim the list of targets down to only the target triple relevent for our project — in this case, `thumbv7em-none-eabi`:\n\n[source]\n----\nstm32g474-example\n├── .cargo\n│   └── config.toml\n├── Cargo.toml\n├── rust-toolchain.toml\n└── src\n    └── main.rs\n----\n\n[source,toml]\n----\n# Before upgrading check that everything is available on all tier1 targets here:\n# https://rust-lang.github.io/rustup-components-history\n[toolchain]\nchannel = \"1.85\"\ncomponents = [ \"rust-src\", \"rustfmt\", \"llvm-tools\", \"miri\" ]\ntargets = [\"thumbv7em-none-eabi\"]\n----\n\n=== build.rs\n\nIn order to produce a working binary for our target, cargo requires a custom build script. Copy `build.rs` from the example to our project:\n\n[source]\n----\nstm32g474-example\n├── build.rs\n├── .cargo\n│   └── config.toml\n├── Cargo.toml\n├── rust-toolchain.toml\n└── src\n    └── main.rs\n----\n\n=== Building and running\n\nAt this point, we‘re finally ready to build and run our project! Connect your board via a debug probe and run:\n\n[source,bash]\n----\ncargo run --release\n----\n\nshould result in a blinking LED (if there’s one attached to the pin in `src/main.rs` – change it if not!) and the following output:\n\n[source]\n----\n   Compiling stm32g474-example v0.1.0 (/home/you/stm32g474-example)\n    Finished release [optimized + debuginfo] target(s) in 0.22s\n     Running `probe-rs run --chip STM32G474RETx target/thumbv7em-none-eabi/release/stm32g474-example`\n     Erasing sectors ✔ [00:00:00] [#########################################################] 18.00 KiB/18.00 KiB @ 54.09 KiB/s (eta 0s )\n Programming pages   ✔ [00:00:00] [#########################################################] 17.00 KiB/17.00 KiB @ 35.91 KiB/s (eta 0s )    Finished in 0.817s\n0.000000 TRACE BDCR configured: 00008200\n└─ embassy_stm32::rcc::bd::{impl#3}::init::{closure#4} @ /home/you/.cargo/git/checkouts/embassy-9312dcb0ed774b29/7703f47/embassy-stm32/src/fmt.rs:117\n0.000000 DEBUG rcc: Clocks { sys: Hertz(16000000), pclk1: Hertz(16000000), pclk1_tim: Hertz(16000000), pclk2: Hertz(16000000), pclk2_tim: Hertz(16000000), hclk1: Hertz(16000000), hclk2: Hertz(16000000), pll1_p: None, adc: None, adc34: None, rtc: Some(Hertz(32000)) }\n└─ embassy_stm32::rcc::set_freqs @ /home/you/.cargo/git/checkouts/embassy-9312dcb0ed774b29/7703f47/embassy-stm32/src/fmt.rs:130\n0.000000 INFO  Hello World!\n└─ embassy_stm32g474::____embassy_main_task::{async_fn#0} @ src/main.rs:14\n0.000091 INFO  high\n└─ embassy_stm32g474::____embassy_main_task::{async_fn#0} @ src/main.rs:19\n0.300201 INFO  low\n└─ embassy_stm32g474::____embassy_main_task::{async_fn#0} @ src/main.rs:23\n----\n"
  },
  {
    "path": "docs/pages/nrf.adoc",
    "content": "= Embassy nRF HAL\n\nThe link:https://github.com/embassy-rs/embassy/tree/main/embassy-nrf[Embassy nRF HAL] is based on the PACs (Peripheral Access Crate) from link:https://github.com/nrf-rs/[nrf-rs].\n\n== Timer driver\n\nThe nRF timer driver operates at 32768 Hz by default.\n\n== Peripherals\n\nThe following peripherals have a HAL implementation at present\n\n* PWM\n* SPIM\n* QSPI\n* NVMC\n* GPIOTE\n* RNG\n* TIMER\n* WDT\n* TEMP\n* PPI\n* UARTE\n* TWIM\n* SAADC\n\n== Bluetooth\n\nFor bluetooth, you can use the link:https://github.com/embassy-rs/nrf-softdevice[nrf-softdevice] crate.\n"
  },
  {
    "path": "docs/pages/overview.adoc",
    "content": "= Introduction\n\nEmbassy is a project to make async/await a first-class option for embedded development.\n\n== What is async?\n\nWhen handling I/O, software must call functions that block program execution until the I/O operation completes. When running inside of an OS such as Linux, such functions generally transfer control to the kernel so that another task (known as a “thread”) can be executed if available, or the CPU can be put to sleep until another task is ready.\n\nBecause an OS cannot presume that threads will behave cooperatively, threads are relatively resource-intensive and may be forcibly interrupted if they do not transfer control back to the kernel within an allotted time. If tasks could instead be presumed to behave cooperatively, it would be possible to create tasks that are almost free when compared to a traditional OS thread.\n\nIn other programming languages, these lightweight tasks are known as “coroutines” or ”goroutines”. In Rust, they are implemented with async. Async-await works by transforming each async function into an object called a future. When a future blocks on I/O the future yields, and the scheduler, called an executor, can select a different future to execute.\n\nCompared to alternatives such as an RTOS, async can yield better performance and lower power consumption because the executor doesn't have to guess when a future is ready to execute. However, program size may be higher than other alternatives, which may be a problem for certain space-constrained devices with very low memory. On the devices Embassy supports, such as STM32 and nRF, memory is generally large enough to accommodate the modestly-increased program size.\n\n== What is Embassy?\n\nThe Embassy project consists of several crates that you can use together or independently:\n\n=== Executor\nThe link:https://docs.embassy.dev/embassy-executor/[embassy-executor] is an async/await executor that generally executes a fixed number of tasks, allocated at startup, though more can be added later.  The executor may also provide a system timer that you can use for both async and blocking delays. For less than one microsecond, blocking delays should be used because the cost of context-switching is too high and the executor will be unable to provide accurate timing.\n\n=== Hardware Abstraction Layers\nHALs implement safe Rust API which let you use peripherals such as USART, UART, I2C, SPI, CAN, and USB without having to directly manipulate registers.\n\nEmbassy provides implementations of both async and blocking APIs where it makes sense. DMA (Direct Memory Access) is an example where async is a good fit, whereas GPIO states are a better fit for a blocking API.\n\nThe Embassy project maintains HALs for select hardware, but you can still use HALs from other projects with Embassy.\n\n* link:https://docs.embassy.dev/embassy-stm32/[embassy-stm32], for all STM32 microcontroller families.\n* link:https://docs.embassy.dev/embassy-nrf/[embassy-nrf], for the Nordic Semiconductor nRF52, nRF53, nRF54, nRF91 series.\n* link:https://docs.embassy.dev/embassy-rp/[embassy-rp], for the Raspberry Pi RP2040 as well as RP235x microcontroller.\n* link:https://docs.embassy.dev/embassy-mspm0/[embassy-mspm0], for the Texas Instruments MSPM0 microcontrollers.\n* link:https://github.com/esp-rs/esp-hal[esp-hal], for the Espressif Systems ESP32 series of chips.\n* link:https://github.com/ch32-rs/ch32-hal[ch32-hal], for the WCH 32-bit RISC-V(CH32V) series of chips.\n* link:https://github.com/AlexCharlton/mpfs-hal[mpfs-hal], for the Microchip PolarFire SoC.\n* link:https://github.com/py32-rs/py32-hal[py32-hal], for the Puya Semiconductor PY32 series of chips.\n\nNOTE: A common question is if one can use the Embassy HALs standalone. Yes, it is possible! There is no dependency on the executor within the HALs. You can even use them without async,\nas they implement both the link:https://github.com/rust-embedded/embedded-hal[Embedded HAL] blocking and async traits.\n\n=== Networking\nThe link:https://docs.embassy.dev/embassy-net/[embassy-net] network stack implements extensive networking functionality, including Ethernet, IP, TCP, UDP, ICMP and DHCP. Async drastically simplifies managing timeouts and serving multiple connections concurrently. Several drivers for WiFi and Ethernet chips can be found.\n\n=== Bluetooth\n\n* The link:https://github.com/embassy-rs/trouble[trouble] crate provides a Bluetooth Low Energy 4.x and 5.x Host that runs on any microcontroller implementing the link:https://github.com/embassy-rs/bt-hci[bt-hci] traits (currently `nRF52`, `nrf54`, `rp2040`, `rp23xx` and `esp32` and `serial` controllers are supported).\n* The link:https://github.com/embassy-rs/nrf-softdevice[nrf-softdevice] crate provides Bluetooth Low Energy 4.x and 5.x support for nRF52 microcontrollers.\n\n=== LoRa\nlink:https://github.com/lora-rs/lora-rs[lora-rs] supports LoRa networking on a wide range of LoRa radios, fully integrated with a Rust LoRaWAN implementation. It provides four crates — lora-phy, lora-modulation, lorawan-encoding, and lorawan-device — and basic examples for various development boards. It has support for STM32WL wireless microcontrollers or Semtech SX127x transceivers, among others.\n\n=== USB\nlink:https://docs.embassy.dev/embassy-usb/[embassy-usb] implements a device-side USB stack. Implementations for common classes such as USB serial (CDC ACM) and USB HID are available, and a rich builder API allows building your own.\n\n=== Bootloader and DFU\nlink:https://github.com/embassy-rs/embassy/tree/main/embassy-boot[embassy-boot] is a lightweight bootloader supporting firmware application upgrades in a power-fail-safe way, with trial boots and rollbacks.\n\n== What is DMA?\n\nFor most I/O in embedded devices, the peripheral doesn't directly support the transmission of multiple bytes at once, with CAN being a notable exception. Instead, the MCU must write each byte, one at a time, and then wait until the peripheral is ready to send the next. For high I/O rates, this can pose a problem if the MCU must devote an increasing portion of its time handling each byte. The solution to this problem is to use the Direct Memory Access controller.\n\nThe Direct Memory Access controller (DMA) is a controller that is present in MCUs that Embassy supports, including STM32 and nRF. The DMA allows the MCU to set up a transfer, either send or receive, and then wait for the transfer to complete. With DMA, once started, no MCU intervention is required until the transfer is complete, meaning that the MCU can perform other computation, or set up other I/O while the transfer is in progress. For high I/O rates, DMA can cut the time that the MCU spends handling I/O by over half. However, because DMA is more complex to set-up, it is less widely used in the embedded community. Embassy aims to change that by making DMA the first choice rather than the last. Using Embassy, there's no additional tuning required once I/O rates increase because your application is already set-up to handle them.\n\n== Examples\n\nEmbassy provides examples for all HALs supported. You can find them in the `examples/` folder.\n\n\nMain loop example\n\n[source,rust]\n----\ninclude::../examples/examples/std/src/bin/tick.rs[]\n----\n\ninclude::embassy_in_the_wild.adoc[leveloffset = 2]\n\n== Resources\n\nFor more reading material on async Rust and Embassy:\n\n* link:https://tweedegolf.nl/en/blog/65/async-rust-vs-rtos-showdown[Comparison of FreeRTOS and Embassy]\n* link:https://dev.to/apollolabsbin/series/20707[Tutorials]\n* link:https://blog.drogue.io/firmware-updates-part-1/[Firmware Updates with Embassy]\n\nVideos:\n\n* link:https://www.youtube.com/watch?v=pDd5mXBF4tY[Intro to Embassy]\n* link:https://www.youtube.com/watch?v=wni5h5vIPhU[From Zero to Async in Embedded Rust]\n"
  },
  {
    "path": "docs/pages/project_structure.adoc",
    "content": "= Project Structure\n\nThere are many ways to configure embassy and its components for your exact application. The link:https://github.com/embassy-rs/embassy/tree/main/examples[examples] directory for each chipset demonstrates how your project structure should look. Let's break it down:\n\nThe toplevel file structure of your project should look like this:\n[source,plain]\n----\n{} = Maybe\n\nmy-project\n|- .cargo\n|  |- config.toml\n|- src\n|  |- main.rs\n|- build.rs\n|- Cargo.toml\n|- {memory.x}\n|- rust-toolchain.toml\n----\n\n[discrete]\n== .cargo/config.toml\n\nThis directory/file describes what platform you're on, and configures link:https://github.com/probe-rs/probe-rs[probe-rs] to deploy to your device.\n\nHere is a minimal example:\n\n[source,toml]\n----\n[target.thumbv6m-none-eabi] # <-change for your platform\nrunner = 'probe-rs run --chip STM32F031K6Tx' # <- change for your chip\n\n[build]\ntarget = \"thumbv6m-none-eabi\" # <-change for your platform\n\n[env]\nDEFMT_LOG = \"trace\" # <- can change to info, warn, or error\n----\n\n[discrete]\n== build.rs\n\nThis is the build script for your project. It links defmt (what is link:https://defmt.ferrous-systems.com[defmt]?) and the `memory.x` file if needed. This file is pretty specific for each chipset, just copy and paste from the corresponding link:https://github.com/embassy-rs/embassy/tree/main/examples[example].\n\n[discrete]\n== Cargo.toml\n\nThis is your manifest file, where you can configure all of the embassy components to use the features you need.\n\n[discrete]\n=== Features\n\n[discrete]\n==== Time\n- tick-hz-x: Configures the tick rate of `embassy-time`. Higher tick rate means higher precision, and higher CPU wakes.\n- defmt-timestamp-uptime: defmt log entries will display the uptime in seconds.\n\n...more to come\n\n[discrete]\n== memory.x\n\nThis file outlines the flash/ram usage of your program. It is especially useful when using link:https://github.com/embassy-rs/nrf-softdevice[nrf-softdevice] on an nRF5x.\n\nHere is an example for using S140 with an nRF52840:\n\n[source,x]\n----\nMEMORY\n{\n  /* NOTE 1 K = 1 KiBi = 1024 bytes */\n  /* These values correspond to the NRF52840 with Softdevices S140 7.0.1 */\n  FLASH : ORIGIN = 0x00027000, LENGTH = 868K\n  RAM : ORIGIN = 0x20020000, LENGTH = 128K\n}\n----\n\n[discrete]\n== rust-toolchain.toml\n\nThis file configures the rust version and configuration to use.\n\nA minimal example:\n\n[source,toml]\n----\n[toolchain]\nchannel = \"1.85\" # <- as of writing, this is the exact rust version embassy uses\ncomponents = [ \"rust-src\", \"rustfmt\" ] # <- optionally add \"llvm-tools-preview\" for some extra features like \"cargo size\"\ntargets = [\n    \"thumbv6m-none-eabi\" # <- change for your platform\n]\n----\n"
  },
  {
    "path": "docs/pages/runtime.adoc",
    "content": "= Embassy executor\n\nThe Embassy executor is an async/await executor designed for embedded usage along with support functionality for interrupts and timers.\n\n== Features\n\n* No `alloc`, no heap needed. Tasks are statically allocated.\n* No \"fixed capacity\" data structures, executor works with 1 or 1000 tasks without needing config/tuning.\n* Integrated timer queue: sleeping is easy, just do `Timer::after_secs(1).await;`.\n* No busy-loop polling: CPU sleeps when there's no work to do, using interrupts or `WFE/SEV`.\n* Efficient polling: a wake will only poll the woken task, not all of them.\n* Fair: a task can't monopolize CPU time even if it's constantly being woken. All other tasks get a chance to run before a given task gets polled for the second time.\n* Multiple executor instances can be created, to run tasks at different priority levels. This allows higher-priority tasks to preempt lower-priority tasks.\n\n== Executor\n\nThe executor works as follows:\n\n. When a task is created, it is polled.\n. The task attempts to make progress until it reaches a point where it would be blocked. This may happen whenever a task is `.await`-ing an async function. When that happens, the task yields execution by returning `Poll::Pending`.\n. Once a task yields, the executor enqueues the task at the end of the run queue and proceeds to poll the next task in the queue.\n\nWhen a task is finished or canceled, it will not be enqueued again.\n\nIMPORTANT: The executor relies on tasks not blocking indefinitely, this would prevent the executor regaining control and scheduling another task.\n\nimage::embassy_executor.png[Executor model]\n\nIf you use the `#[embassy_executor::main]` macro in your application, it creates the `Executor` for you and spawns the main entry point as the first task. You can also create the Executor manually, and you can in fact create multiple Executors.\n\n\n== Interrupts\n\nInterrupts are a common way for peripherals to signal completion of some operation and fits well with the async execution model. The following diagram describes a typical application flow where (1) a task is polled and is attempting to make progress. The task then (2) instructs the peripheral to perform some operation, and awaits. After some time has passed, (3) an interrupt is raised, marking the completion of the operation.\n\nThe peripheral HAL then (4) ensures that interrupt signals are routed to the peripheral and updates the peripheral state with the results of the operation. The executor is then (5) notified that the task should be polled, which it will do.\n\nimage::embassy_irq.png[Interrupt handling]\n\nNOTE: A special executor named `InterruptExecutor` can be driven by an interrupt. This can be used to drive tasks at different priority levels by creating multiple `InterruptExecutor` instances.\n\n== Time\n\nEmbassy features an internal timer queue enabled by the `time` feature flag. When enabled, Embassy assumes that a time `Driver` implementation exists for the platform. Embassy provides time drivers for the nRF, STM32, RPi Pico, WASM and Std platforms.\n\nNOTE: Some embedded platforms' timer drivers might only support a limited number of alarms. Make sure that the number of tasks using the timer simultaneously do not exceed this limit.\n\nThe timer speed is configurable at compile time using the `time-tick-<frequency>`. At present, the timer may be configured to run at 1000 Hz, 32768 Hz, or 1 MHz. Before changing the defaults, make sure the target HAL supports the particular frequency setting.\n\nNOTE: If you do not require timers in your application, not enabling the `time` feature can save some CPU cycles and reduce power usage.\n"
  },
  {
    "path": "docs/pages/sharing_peripherals.adoc",
    "content": "= Sharing peripherals between tasks\n\nOften, more than one task needs to access the same resource (pin, communication interface, etc.). Embassy provides many different synchronization primitives in the link:https://crates.io/crates/embassy-sync[embassy-sync] crate.\n\nThe following examples show different ways for two tasks to use the on-board LED on a Raspberry Pi Pico board simultaneously.\n\n== Sharing using a Mutex\n\nUsing mutual exclusion is the simplest way to share a peripheral.\n\nTIP: Dependencies needed to run this example link:#_the_cargo_toml[can be found here].\n[,rust]\n----\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_rp::gpio;\nuse embassy_sync::blocking_mutex::raw::ThreadModeRawMutex;\nuse embassy_sync::mutex::Mutex;\nuse embassy_time::{Duration, Ticker};\nuse gpio::{AnyPin, Level, Output};\nuse {defmt_rtt as _, panic_probe as _};\n\ntype LedType = Mutex<ThreadModeRawMutex, Option<Output<'static, AnyPin>>>;\nstatic LED: LedType = Mutex::new(None);\n\n#[embassy_executor::main]\nasync fn main(spawner: Spawner) {\n    let p = embassy_rp::init(Default::default());\n    // set the content of the global LED reference to the real LED pin\n    let led = Output::new(AnyPin::from(p.PIN_25), Level::High);\n    // inner scope is so that once the mutex is written to, the MutexGuard is dropped, thus the\n    // Mutex is released\n    {\n        *(LED.lock().await) = Some(led);\n    }\n    let dt = 100 * 1_000_000;\n    let k = 1.003;\n\n    unwrap!(spawner.spawn(toggle_led(&LED, Duration::from_nanos(dt))));\n    unwrap!(spawner.spawn(toggle_led(&LED, Duration::from_nanos((dt as f64 * k) as u64))));\n}\n\n// A pool size of 2 means you can spawn two instances of this task.\n#[embassy_executor::task(pool_size = 2)]\nasync fn toggle_led(led: &'static LedType, delay: Duration) {\n    let mut ticker = Ticker::every(delay);\n    loop {\n        {\n            let mut led_unlocked = led.lock().await;\n            if let Some(pin_ref) = led_unlocked.as_mut() {\n                pin_ref.toggle();\n            }\n        }\n        ticker.next().await;\n    }\n}\n----\n\nThe structure facilitating access to the resource is the defined `LedType`.\n\n=== Why so complicated\n\nUnwrapping the layers gives insight into why each one is needed.\n\n==== `Mutex<RawMutexType, T>`\n\nThe mutex is there so if one task gets the resource first and begins modifying it, all other tasks wanting to write will have to wait (the `led.lock().await` will return immediately if no task has locked the mutex, and will block if it is accessed somewhere else). \n\n==== `Option<T>`\n\nThe `LED` variable needs to be defined outside the main task as references accepted by tasks need to be `'static`. However, if it is outside the main task, it cannot be initialised to point to any pin, as the pins themselves are not initialised. Thus, it is set to `None`. \n\n==== `Output<AnyPin>`\n\nTo indicate that the pin will be set to an Output. The `AnyPin` could have been `embassy_rp::peripherals::PIN_25`, however this option lets the `toggle_led` function be more generic. \n\n== Sharing using a Channel\n\nA channel is another way to ensure exclusive access to a resource. Using a channel is great in the cases where the access can happen at a later point in time, allowing you to enqueue operations and do other things.\n\nTIP: Dependencies needed to run this example link:#_the_cargo_toml[can be found here].\n[,rust]\n----\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_rp::gpio;\nuse embassy_sync::blocking_mutex::raw::ThreadModeRawMutex;\nuse embassy_sync::channel::{Channel, Sender};\nuse embassy_time::{Duration, Ticker};\nuse gpio::{AnyPin, Level, Output};\nuse {defmt_rtt as _, panic_probe as _};\n\nenum LedState {\n     Toggle,\n}\nstatic CHANNEL: Channel<ThreadModeRawMutex, LedState, 64> = Channel::new();\n\n#[embassy_executor::main]\nasync fn main(spawner: Spawner) {\n    let p = embassy_rp::init(Default::default());\n    let mut led = Output::new(AnyPin::from(p.PIN_25), Level::High);\n\n    let dt = 100 * 1_000_000;\n    let k = 1.003;\n\n    unwrap!(spawner.spawn(toggle_led(CHANNEL.sender(), Duration::from_nanos(dt))));\n    unwrap!(spawner.spawn(toggle_led(CHANNEL.sender(), Duration::from_nanos((dt as f64 * k) as u64))));\n\n    loop {\n        match CHANNEL.receive().await {\n            LedState::Toggle => led.toggle(),\n        }\n    }\n}\n\n// A pool size of 2 means you can spawn two instances of this task.\n#[embassy_executor::task(pool_size = 2)]\nasync fn toggle_led(control: Sender<'static, ThreadModeRawMutex, LedState, 64>, delay: Duration) {\n    let mut ticker = Ticker::every(delay);\n    loop {\n        control.send(LedState::Toggle).await;\n        ticker.next().await;\n    }\n}\n----\n\nThis example replaces the Mutex with a Channel, and uses another task (the main loop) to drive the LED. The advantage of this approach is that only a single task references the peripheral, separating concerns. However, using a Mutex has a lower overhead and might be necessary if you need to ensure\nthat the operation is completed before continuing to do other work in your task.\n\nAn example showcasing more methods for sharing link:https://github.com/embassy-rs/embassy/blob/main/examples/rp/src/bin/sharing.rs[can be found here].\n\n== Sharing an I2C or SPI bus between multiple devices\n\nAn example of how to deal with multiple devices sharing a common I2C or SPI bus link:https://github.com/embassy-rs/embassy/blob/main/examples/rp/src/bin/shared_bus.rs[can be found here].\n"
  },
  {
    "path": "docs/pages/stm32.adoc",
    "content": "= Embassy STM32 HAL\n\nThe link:https://github.com/embassy-rs/embassy/tree/main/embassy-stm32[Embassy STM32 HAL] is based on the `stm32-metapac` project.\n\n== The infinite variant problem\n\nSTM32 microcontrollers come in many families, and flavors and supporting all of them is a big undertaking. Embassy has taken advantage of the fact\nthat the STM32 peripheral versions are shared across chip families. Instead of re-implementing the SPI\nperipheral for every STM32 chip family, embassy has a single SPI implementation that depends on\ncode-generated register types that are identical for STM32 families with the same version of a given peripheral.\n\n=== The metapac\n\nThe `stm32-metapac` module uses pre-generated chip and register definitions for STM32 chip families to generate register types. This is done at compile time based on Cargo feature flags.\n\nThe chip and register definitions are located in a separate module, `stm32-data`, which is modified whenever a bug is found in the definitions, or when adding support for new chip families.\n\n=== The HAL\n\nThe `embassy-stm32` module contains the HAL implementation for all STM32 families. The implementation uses automatically derived feature flags to support the correct version of a given peripheral for a given chip family.\n\n== Timer driver\n\nThe STM32 timer driver operates at 32768 Hz by default.\n"
  },
  {
    "path": "docs/pages/system.adoc",
    "content": "= System description\n\nThis section describes different parts of Embassy in more detail.\n\ninclude::runtime.adoc[leveloffset = 2]\ninclude::bootloader.adoc[leveloffset = 2]\ninclude::time_keeping.adoc[leveloffset = 2]\ninclude::hal.adoc[leveloffset = 2]\ninclude::imxrt.adoc[leveloffset = 2]\ninclude::mcxa.adoc[leveloffset = 2]\ninclude::nrf.adoc[leveloffset = 2]\ninclude::stm32.adoc[leveloffset = 2]\ninclude::sharing_peripherals.adoc[leveloffset = 2]\n"
  },
  {
    "path": "docs/pages/time_keeping.adoc",
    "content": "= Time-keeping\n\nIn an embedded program, delaying a task is one of the most common actions taken. In an event loop, delays will need to be inserted to ensure\nthat other tasks have a chance to run before the next iteration of the loop is called, if no other I/O is performed. Embassy provides abstractions\nto delay the current task for a specified interval of time.\n\nThe interface for time-keeping in Embassy is handled by the link:https://crates.io/crates/embassy-time[embassy-time] crate. The types can be used with the internal\ntimer queue in link:https://crates.io/crates/embassy-executor[embassy-executor] or a custom timer queue implementation.\n\n== Timer\n\nThe `embassy_time::Timer` type provides two timing methods.\n\n`Timer::at` creates a future that completes at the specified `Instant`, relative to the system boot time.\n`Timer::after` creates a future that completes after the specified `Duration`, relative to when the future was created.\n\nAn example of a delay is provided as follows:\n\nTIP: Dependencies needed to run this example link:#_the_cargo_toml[can be found here].\n[,rust]\n----\nuse embassy_executor::task;\nuse embassy_time::{Duration, Timer};\n\n#[task]\n/// Task that ticks periodically\nasync fn tick_periodic() -> ! {\n    loop {\n        rprintln!(\"tick!\");\n        // async sleep primitive, suspends the task for 500ms.\n        Timer::after(Duration::from_millis(500)).await;\n    }\n}\n----\n\n== Delay\n\nThe `embassy_time::Delay` type provides an implementation of the link:https://docs.rs/embedded-hal/1.0.0/embedded_hal/delay/index.html[embedded-hal] and\nlink:https://docs.rs/embedded-hal-async/latest/embedded_hal_async/delay/index.html[embedded-hal-async] traits. This can be used for drivers\nthat expect a generic delay implementation to be provided.\n\nAn example of how this can be used:\n\nTIP: Dependencies needed to run this example link:#_the_cargo_toml[can be found here].\n[,rust]\n----\nuse embassy_executor::task;\nuse embassy_time::Delay;\n\n#[task]\n/// Task that ticks periodically\nasync fn tick_periodic() -> ! {\n    loop {\n        rprintln!(\"tick!\");\n        // async sleep primitive, suspends the task for 500ms.\n        generic_delay(Delay).await\n    }\n}\n\nasync fn generic_delay<D: embedded_hal_async::delay::DelayNs>(delay: D) {\n      delay.delay_ms(500).await;\n}\n----\n"
  },
  {
    "path": "embassy-boot/CHANGELOG.md",
    "content": "# Changelog\n\nAll notable changes to this project will be documented in this file.\n\nThe format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),\nand this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).\n\n<!-- next-header -->\n## Unreleased - ReleaseDate\n\n## 0.7.0 - 2026-03-10\n\n- Fixed documentation and assertion of STATE partition size requirements\n- Added documentation for package features\n- Made `read_state` on `BootLoader` public\n- Update embassy-embedded-hal to 0.6.0\n- Update embassy-sync to 0.8.0\n\n## 0.6.1 - 2025-08-26\n\n- First release with changelog.\n"
  },
  {
    "path": "embassy-boot/Cargo.toml",
    "content": "[package]\nedition = \"2024\"\nname = \"embassy-boot\"\nversion = \"0.7.0\"\ndescription = \"A lightweight bootloader supporting firmware updates in a power-fail-safe way, with trial boots and rollbacks.\"\nlicense = \"MIT OR Apache-2.0\"\nrepository = \"https://github.com/embassy-rs/embassy\"\ndocumentation = \"https://docs.embassy.dev/embassy-boot\"\ncategories = [\n    \"embedded\",\n    \"no-std\",\n    \"asynchronous\",\n]\n\n[package.metadata.embassy_docs]\nsrc_base = \"https://github.com/embassy-rs/embassy/blob/embassy-boot-v$VERSION/embassy-boot/src/\"\nsrc_base_git = \"https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-boot/src/\"\ntarget = \"thumbv7em-none-eabi\"\nfeatures = [\"defmt\"]\n\n[package.metadata.docs.rs]\nfeatures = [\"defmt\"]\n\n[lib]\n\n[dependencies]\ndefmt = { version = \"1.0.1\", optional = true }\ndigest = \"0.10\"\ndocument-features = \"0.2.7\"\nlog = { version = \"0.4\", optional = true }\ned25519-dalek = { version = \"2\", default-features = false, features = [\"digest\"], optional = true }\nembassy-embedded-hal = { version = \"0.6.0\", path = \"../embassy-embedded-hal\" }\nembassy-sync = { version = \"0.8.0\", path = \"../embassy-sync\" }\nembedded-storage = \"0.3.1\"\nembedded-storage-async = { version = \"0.4.1\" }\nsalty = { version = \"0.3\", optional = true }\nsignature = { version = \"2.0\", default-features = false }\n\n[dev-dependencies]\nlog = \"0.4\"\nenv_logger = \"0.9\"\nrand = \"0.8\"\nfutures = { version = \"0.3\", features = [\"executor\"] }\nsha1 = \"0.10.5\"\ncritical-section = { version = \"1.1.1\", features = [\"std\"] }\ned25519-dalek = { version = \"2\", default-features = false, features = [\"std\", \"rand_core\", \"digest\"]  }\n\n[features]\n## Use [`defmt`](https://docs.rs/defmt/latest/defmt/) for logging\ndefmt = [\"dep:defmt\"]\n## Use log for logging\nlog = [\"dep:log\"]\n\n## Enable for devices that set erased flash bytes to `0x00` instead of the usual `0xFF`\nflash-erase-zero = []\n\n#! ## Firmware Signing\n#! Enable one of these features to allow verification of DFU signatures with\n#! `FirmwareUpdater::verify_and_mark_updated`.\n\n## Use the `ed25519-dalek` package to verify DFU signatures.\ned25519-dalek = [\"dep:ed25519-dalek\", \"_verify\"]\n## Use the `salty` package to verify DFU signatures.\ned25519-salty = [\"dep:salty\", \"_verify\"]\n\n#Internal features\n_verify = []\n"
  },
  {
    "path": "embassy-boot/README.md",
    "content": "# embassy-boot\n\nAn [Embassy](https://embassy.dev) project.\n\nA lightweight bootloader supporting firmware updates in a power-fail-safe way, with trial boots and rollbacks.\n\nThe bootloader can be used either as a library or be flashed directly with the default configuration derived from linker scripts.\n\nBy design, the bootloader does not provide any network capabilities. Networking capabilities for fetching new firmware can be provided by the user application, using the bootloader as a library for updating the firmware, or by using the bootloader as a library and adding this capability yourself.\n\n## Overview\n\nThe bootloader divides the storage into 4 main partitions, configurable when creating the bootloader instance or via linker scripts:\n\n* BOOTLOADER - Where the bootloader is placed. The bootloader itself consumes about 8kB of flash, but if you need to debug it and have space available, increasing this to 24kB will allow you to run the bootloader with probe-rs.\n* ACTIVE - Where the main application is placed. The bootloader will attempt to load the application at the start of this partition. The minimum size required for this partition is the size of your application.\n* DFU - Where the application-to-be-swapped is placed. This partition is written to by the application. This partition must be at least 1 page bigger than the ACTIVE partition.\n* BOOTLOADER STATE - Where the bootloader stores the current state describing if the active and dfu partitions need to be swapped.\n\nFor any partition, the following preconditions are required:\n\n* Partitions must be aligned on the page size.\n* Partitions must be a multiple of the page size.\n\nThe linker scripts for the application and bootloader look similar, but the FLASH region must point to the BOOTLOADER partition for the bootloader, and the ACTIVE partition for the application.\n\nFor more details on the bootloader, see [the documentation](https://embassy.dev/book/#_bootloader).\n\n## Hardware support\n\nThe bootloader supports different hardware in separate crates:\n\n* `embassy-boot-nrf` - for the nRF microcontrollers.\n* `embassy-boot-rp` - for the RP2040 microcontrollers.\n* `embassy-boot-stm32` - for the STM32 microcontrollers.\n"
  },
  {
    "path": "embassy-boot/src/boot_loader.rs",
    "content": "use core::cell::RefCell;\n\nuse embassy_embedded_hal::flash::partition::BlockingPartition;\nuse embassy_sync::blocking_mutex::Mutex;\nuse embassy_sync::blocking_mutex::raw::NoopRawMutex;\nuse embedded_storage::nor_flash::{NorFlash, NorFlashError, NorFlashErrorKind};\n\nuse crate::{DFU_DETACH_MAGIC, REVERT_MAGIC, STATE_ERASE_VALUE, SWAP_MAGIC, State};\n\n/// Errors returned by bootloader\n#[derive(PartialEq, Eq, Debug)]\npub enum BootError {\n    /// Error from flash.\n    Flash(NorFlashErrorKind),\n    /// Invalid bootloader magic\n    BadMagic,\n}\n\n#[cfg(feature = \"defmt\")]\nimpl defmt::Format for BootError {\n    fn format(&self, fmt: defmt::Formatter) {\n        match self {\n            BootError::Flash(_) => defmt::write!(fmt, \"BootError::Flash(_)\"),\n            BootError::BadMagic => defmt::write!(fmt, \"BootError::BadMagic\"),\n        }\n    }\n}\n\nimpl<E> From<E> for BootError\nwhere\n    E: NorFlashError,\n{\n    fn from(error: E) -> Self {\n        BootError::Flash(error.kind())\n    }\n}\n\n/// Bootloader flash configuration holding the three flashes used by the bootloader\n///\n/// If only a single flash is actually used, then that flash should be partitioned into three partitions before use.\n/// The easiest way to do this is to use [`BootLoaderConfig::from_linkerfile_blocking`] which will partition\n/// the provided flash according to symbols defined in the linkerfile.\npub struct BootLoaderConfig<ACTIVE, DFU, STATE> {\n    /// Flash type used for the active partition - the partition which will be booted from.\n    pub active: ACTIVE,\n    /// Flash type used for the dfu partition - the partition which will be swapped in when requested.\n    pub dfu: DFU,\n    /// Flash type used for the state partition.\n    pub state: STATE,\n}\n\nimpl<'a, ACTIVE: NorFlash, DFU: NorFlash, STATE: NorFlash>\n    BootLoaderConfig<\n        BlockingPartition<'a, NoopRawMutex, ACTIVE>,\n        BlockingPartition<'a, NoopRawMutex, DFU>,\n        BlockingPartition<'a, NoopRawMutex, STATE>,\n    >\n{\n    /// Constructs a `BootLoaderConfig` instance from flash memory and address symbols defined in the linker file.\n    ///\n    /// This method initializes `BlockingPartition` instances for the active, DFU (Device Firmware Update),\n    /// and state partitions, leveraging start and end addresses specified by the linker. These partitions\n    /// are critical for managing firmware updates, application state, and boot operations within the bootloader.\n    ///\n    /// # Parameters\n    /// - `active_flash`: A reference to a mutex-protected `RefCell` for the active partition's flash interface.\n    /// - `dfu_flash`: A reference to a mutex-protected `RefCell` for the DFU partition's flash interface.\n    /// - `state_flash`: A reference to a mutex-protected `RefCell` for the state partition's flash interface.\n    ///\n    /// # Safety\n    /// The method contains `unsafe` blocks for dereferencing raw pointers that represent the start and end addresses\n    /// of the bootloader's partitions in flash memory. It is crucial that these addresses are accurately defined\n    /// in the memory.x file to prevent undefined behavior.\n    ///\n    /// The caller must ensure that the memory regions defined by these symbols are valid and that the flash memory\n    /// interfaces provided are compatible with these regions.\n    ///\n    /// # Returns\n    /// A `BootLoaderConfig` instance with `BlockingPartition` instances for the active, DFU, and state partitions.\n    ///\n    /// # Example\n    /// ```ignore\n    /// // Assume `active_flash`, `dfu_flash`, and `state_flash` all share the same flash memory interface.\n    /// let layout = Flash::new_blocking(p.FLASH).into_blocking_regions();\n    /// let flash = Mutex::new(RefCell::new(layout.bank1_region));\n    ///\n    /// let config = BootLoaderConfig::from_linkerfile_blocking(&flash, &flash, &flash);\n    /// // `config` can now be used to create a `BootLoader` instance for managing boot operations.\n    /// ```\n    /// Working examples can be found in the bootloader examples folder.\n    // #[cfg(target_os = \"none\")]\n    pub fn from_linkerfile_blocking(\n        active_flash: &'a Mutex<NoopRawMutex, RefCell<ACTIVE>>,\n        dfu_flash: &'a Mutex<NoopRawMutex, RefCell<DFU>>,\n        state_flash: &'a Mutex<NoopRawMutex, RefCell<STATE>>,\n    ) -> Self {\n        unsafe extern \"C\" {\n            static __bootloader_state_start: u32;\n            static __bootloader_state_end: u32;\n            static __bootloader_active_start: u32;\n            static __bootloader_active_end: u32;\n            static __bootloader_dfu_start: u32;\n            static __bootloader_dfu_end: u32;\n        }\n\n        let active = unsafe {\n            let start = &__bootloader_active_start as *const u32 as u32;\n            let end = &__bootloader_active_end as *const u32 as u32;\n            trace!(\"ACTIVE: 0x{:x} - 0x{:x}\", start, end);\n\n            BlockingPartition::new(active_flash, start, end - start)\n        };\n        let dfu = unsafe {\n            let start = &__bootloader_dfu_start as *const u32 as u32;\n            let end = &__bootloader_dfu_end as *const u32 as u32;\n            trace!(\"DFU: 0x{:x} - 0x{:x}\", start, end);\n\n            BlockingPartition::new(dfu_flash, start, end - start)\n        };\n        let state = unsafe {\n            let start = &__bootloader_state_start as *const u32 as u32;\n            let end = &__bootloader_state_end as *const u32 as u32;\n            trace!(\"STATE: 0x{:x} - 0x{:x}\", start, end);\n\n            BlockingPartition::new(state_flash, start, end - start)\n        };\n\n        Self { active, dfu, state }\n    }\n}\n\n/// BootLoader works with any flash implementing embedded_storage.\npub struct BootLoader<ACTIVE: NorFlash, DFU: NorFlash, STATE: NorFlash> {\n    active: ACTIVE,\n    dfu: DFU,\n    /// The state partition has the following format:\n    /// All ranges are in multiples of WRITE_SIZE bytes.\n    /// N = Active partition size divided by WRITE_SIZE.\n    /// | Range              | Description                                                                      |\n    /// | 0..1               | Magic indicating bootloader state. BOOT_MAGIC means boot, SWAP_MAGIC means swap. |\n    /// | 1..2               | Progress validity. ERASE_VALUE means valid, !ERASE_VALUE means invalid.          |\n    /// | 2..(2 + 2N)        | Progress index used while swapping                                               |\n    /// | (2 + 2N)..(2 + 4N) | Progress index used while reverting\n    state: STATE,\n}\n\nimpl<ACTIVE: NorFlash, DFU: NorFlash, STATE: NorFlash> BootLoader<ACTIVE, DFU, STATE> {\n    /// Get the page size which is the \"unit of operation\" within the bootloader.\n    const PAGE_SIZE: u32 = if ACTIVE::ERASE_SIZE > DFU::ERASE_SIZE {\n        ACTIVE::ERASE_SIZE as u32\n    } else {\n        DFU::ERASE_SIZE as u32\n    };\n\n    /// Create a new instance of a bootloader with the flash partitions.\n    ///\n    /// - All partitions must be aligned with the PAGE_SIZE const generic parameter.\n    /// - The dfu partition must be at least PAGE_SIZE bigger than the active partition.\n    pub fn new(config: BootLoaderConfig<ACTIVE, DFU, STATE>) -> Self {\n        Self {\n            active: config.active,\n            dfu: config.dfu,\n            state: config.state,\n        }\n    }\n\n    /// Perform necessary boot preparations like swapping images.\n    ///\n    /// The DFU partition is assumed to be 1 page bigger than the active partition for the swap\n    /// algorithm to work correctly.\n    ///\n    /// The provided aligned_buf argument must satisfy any alignment requirements\n    /// given by the partition flashes. All flash operations will use this buffer.\n    ///\n    /// ## SWAPPING\n    ///\n    /// Assume a flash size of 3 pages for the active partition, and 4 pages for the DFU partition.\n    /// The swap index contains the copy progress, as to allow continuation of the copy process on\n    /// power failure. The index counter is represented within 1 or more pages (depending on total\n    /// flash size), where a page X is considered swapped if index at location (`X + WRITE_SIZE`)\n    /// contains a zero value. This ensures that index updates can be performed atomically and\n    /// avoid a situation where the wrong index value is set (page write size is \"atomic\").\n    ///\n    ///\n    /// | Partition | Swap Index | Page 0 | Page 1 | Page 3 | Page 4 |\n    /// |-----------|------------|--------|--------|--------|--------|\n    /// |    Active |          0 |      1 |      2 |      3 |      - |\n    /// |       DFU |          0 |      4 |      5 |      6 |      X |\n    ///\n    /// The algorithm starts by copying 'backwards', and after the first step, the layout is\n    /// as follows:\n    ///\n    /// | Partition | Swap Index | Page 0 | Page 1 | Page 3 | Page 4 |\n    /// |-----------|------------|--------|--------|--------|--------|\n    /// |    Active |          1 |      1 |      2 |      6 |      - |\n    /// |       DFU |          1 |      4 |      5 |      6 |      3 |\n    ///\n    /// The next iteration performs the same steps\n    ///\n    /// | Partition | Swap Index | Page 0 | Page 1 | Page 3 | Page 4 |\n    /// |-----------|------------|--------|--------|--------|--------|\n    /// |    Active |          2 |      1 |      5 |      6 |      - |\n    /// |       DFU |          2 |      4 |      5 |      2 |      3 |\n    ///\n    /// And again until we're done\n    ///\n    /// | Partition | Swap Index | Page 0 | Page 1 | Page 3 | Page 4 |\n    /// |-----------|------------|--------|--------|--------|--------|\n    /// |    Active |          3 |      4 |      5 |      6 |      - |\n    /// |       DFU |          3 |      4 |      1 |      2 |      3 |\n    ///\n    /// ## REVERTING\n    ///\n    /// The reverting algorithm uses the swap index to discover that images were swapped, but that\n    /// the application failed to mark the boot successful. In this case, the revert algorithm will\n    /// run.\n    ///\n    /// The revert index is located separately from the swap index, to ensure that revert can continue\n    /// on power failure.\n    ///\n    /// The revert algorithm works forwards, by starting copying into the 'unused' DFU page at the start.\n    ///\n    /// | Partition | Revert Index | Page 0 | Page 1 | Page 3 | Page 4 |\n    /// |-----------|--------------|--------|--------|--------|--------|\n    /// |    Active |            3 |      1 |      5 |      6 |      - |\n    /// |       DFU |            3 |      4 |      1 |      2 |      3 |\n    ///\n    ///\n    /// | Partition | Revert Index | Page 0 | Page 1 | Page 3 | Page 4 |\n    /// |-----------|--------------|--------|--------|--------|--------|\n    /// |    Active |            3 |      1 |      2 |      6 |      - |\n    /// |       DFU |            3 |      4 |      5 |      2 |      3 |\n    ///\n    /// | Partition | Revert Index | Page 0 | Page 1 | Page 3 | Page 4 |\n    /// |-----------|--------------|--------|--------|--------|--------|\n    /// |    Active |            3 |      1 |      2 |      3 |      - |\n    /// |       DFU |            3 |      4 |      5 |      6 |      3 |\n    ///\n    pub fn prepare_boot(&mut self, aligned_buf: &mut [u8]) -> Result<State, BootError> {\n        const {\n            core::assert!(Self::PAGE_SIZE % ACTIVE::WRITE_SIZE as u32 == 0);\n            core::assert!(Self::PAGE_SIZE % ACTIVE::ERASE_SIZE as u32 == 0);\n            core::assert!(Self::PAGE_SIZE % DFU::WRITE_SIZE as u32 == 0);\n            core::assert!(Self::PAGE_SIZE % DFU::ERASE_SIZE as u32 == 0);\n        }\n\n        // Ensure we have enough progress pages to store copy progress\n        assert_eq!(0, Self::PAGE_SIZE % aligned_buf.len() as u32);\n        assert!(aligned_buf.len() >= STATE::WRITE_SIZE);\n        assert_eq!(0, aligned_buf.len() % ACTIVE::WRITE_SIZE);\n        assert_eq!(0, aligned_buf.len() % DFU::WRITE_SIZE);\n\n        // Ensure our partitions are able to handle boot operations\n        assert_partitions(&self.active, &self.dfu, &self.state, Self::PAGE_SIZE);\n\n        // Copy contents from partition N to active\n        let state = self.read_state(aligned_buf)?;\n        if state == State::Swap {\n            //\n            // Check if we already swapped. If we're in the swap state, this means we should revert\n            // since the app has failed to mark boot as successful\n            //\n            if !self.is_swapped(aligned_buf)? {\n                trace!(\"Swapping\");\n                self.swap(aligned_buf)?;\n                trace!(\"Swapping done\");\n            } else {\n                trace!(\"Reverting\");\n                self.revert(aligned_buf)?;\n\n                let state_word = &mut aligned_buf[..STATE::WRITE_SIZE];\n\n                // Invalidate progress\n                state_word.fill(!STATE_ERASE_VALUE);\n                self.state.write(STATE::WRITE_SIZE as u32, state_word)?;\n\n                // Clear magic and progress\n                self.state.erase(0, self.state.capacity() as u32)?;\n\n                // Set magic\n                state_word.fill(REVERT_MAGIC);\n                self.state.write(0, state_word)?;\n            }\n        }\n        Ok(state)\n    }\n\n    /// Read the magic state from flash\n    pub fn read_state(&mut self, aligned_buf: &mut [u8]) -> Result<State, BootError> {\n        let state_word = &mut aligned_buf[..STATE::WRITE_SIZE];\n        self.state.read(0, state_word)?;\n\n        if !state_word.iter().any(|&b| b != SWAP_MAGIC) {\n            Ok(State::Swap)\n        } else if !state_word.iter().any(|&b| b != DFU_DETACH_MAGIC) {\n            Ok(State::DfuDetach)\n        } else if !state_word.iter().any(|&b| b != REVERT_MAGIC) {\n            Ok(State::Revert)\n        } else {\n            Ok(State::Boot)\n        }\n    }\n\n    fn is_swapped(&mut self, aligned_buf: &mut [u8]) -> Result<bool, BootError> {\n        let page_count = self.active.capacity() / Self::PAGE_SIZE as usize;\n        let progress = self.current_progress(aligned_buf)?;\n\n        Ok(progress >= page_count * 2)\n    }\n\n    fn current_progress(&mut self, aligned_buf: &mut [u8]) -> Result<usize, BootError> {\n        let write_size = STATE::WRITE_SIZE as u32;\n        let max_index = ((self.state.capacity() - STATE::WRITE_SIZE) / STATE::WRITE_SIZE) - 2;\n        let state_word = &mut aligned_buf[..write_size as usize];\n\n        self.state.read(write_size, state_word)?;\n        if state_word.iter().any(|&b| b != STATE_ERASE_VALUE) {\n            // Progress is invalid\n            return Ok(max_index);\n        }\n\n        for index in 0..max_index {\n            self.state.read((2 + index) as u32 * write_size, state_word)?;\n\n            if state_word.iter().any(|&b| b == STATE_ERASE_VALUE) {\n                return Ok(index);\n            }\n        }\n        Ok(max_index)\n    }\n\n    fn update_progress(&mut self, progress_index: usize, aligned_buf: &mut [u8]) -> Result<(), BootError> {\n        let state_word = &mut aligned_buf[..STATE::WRITE_SIZE];\n        state_word.fill(!STATE_ERASE_VALUE);\n        self.state\n            .write((2 + progress_index) as u32 * STATE::WRITE_SIZE as u32, state_word)?;\n        Ok(())\n    }\n\n    fn copy_page_once_to_active(\n        &mut self,\n        progress_index: usize,\n        from_offset: u32,\n        to_offset: u32,\n        aligned_buf: &mut [u8],\n    ) -> Result<(), BootError> {\n        if self.current_progress(aligned_buf)? <= progress_index {\n            let page_size = Self::PAGE_SIZE as u32;\n\n            self.active.erase(to_offset, to_offset + page_size)?;\n\n            for offset_in_page in (0..page_size).step_by(aligned_buf.len()) {\n                self.dfu.read(from_offset + offset_in_page as u32, aligned_buf)?;\n                self.active.write(to_offset + offset_in_page as u32, aligned_buf)?;\n            }\n\n            self.update_progress(progress_index, aligned_buf)?;\n        }\n        Ok(())\n    }\n\n    fn copy_page_once_to_dfu(\n        &mut self,\n        progress_index: usize,\n        from_offset: u32,\n        to_offset: u32,\n        aligned_buf: &mut [u8],\n    ) -> Result<(), BootError> {\n        if self.current_progress(aligned_buf)? <= progress_index {\n            let page_size = Self::PAGE_SIZE as u32;\n\n            self.dfu.erase(to_offset as u32, to_offset + page_size)?;\n\n            for offset_in_page in (0..page_size).step_by(aligned_buf.len()) {\n                self.active.read(from_offset + offset_in_page as u32, aligned_buf)?;\n                self.dfu.write(to_offset + offset_in_page as u32, aligned_buf)?;\n            }\n\n            self.update_progress(progress_index, aligned_buf)?;\n        }\n        Ok(())\n    }\n\n    fn swap(&mut self, aligned_buf: &mut [u8]) -> Result<(), BootError> {\n        let page_count = self.active.capacity() as u32 / Self::PAGE_SIZE;\n        for page_num in 0..page_count {\n            let progress_index = (page_num * 2) as usize;\n\n            // Copy active page to the 'next' DFU page.\n            let active_from_offset = (page_count - 1 - page_num) * Self::PAGE_SIZE;\n            let dfu_to_offset = (page_count - page_num) * Self::PAGE_SIZE;\n            //trace!(\"Copy active {} to dfu {}\", active_from_offset, dfu_to_offset);\n            self.copy_page_once_to_dfu(progress_index, active_from_offset, dfu_to_offset, aligned_buf)?;\n\n            // Copy DFU page to the active page\n            let active_to_offset = (page_count - 1 - page_num) * Self::PAGE_SIZE;\n            let dfu_from_offset = (page_count - 1 - page_num) * Self::PAGE_SIZE;\n            //trace!(\"Copy dfy {} to active {}\", dfu_from_offset, active_to_offset);\n            self.copy_page_once_to_active(progress_index + 1, dfu_from_offset, active_to_offset, aligned_buf)?;\n        }\n\n        Ok(())\n    }\n\n    fn revert(&mut self, aligned_buf: &mut [u8]) -> Result<(), BootError> {\n        let page_count = self.active.capacity() as u32 / Self::PAGE_SIZE;\n        for page_num in 0..page_count {\n            let progress_index = (page_count * 2 + page_num * 2) as usize;\n\n            // Copy the bad active page to the DFU page\n            let active_from_offset = page_num * Self::PAGE_SIZE;\n            let dfu_to_offset = page_num * Self::PAGE_SIZE;\n            self.copy_page_once_to_dfu(progress_index, active_from_offset, dfu_to_offset, aligned_buf)?;\n\n            // Copy the DFU page back to the active page\n            let active_to_offset = page_num * Self::PAGE_SIZE;\n            let dfu_from_offset = (page_num + 1) * Self::PAGE_SIZE;\n            self.copy_page_once_to_active(progress_index + 1, dfu_from_offset, active_to_offset, aligned_buf)?;\n        }\n\n        Ok(())\n    }\n}\n\nfn assert_partitions<ACTIVE: NorFlash, DFU: NorFlash, STATE: NorFlash>(\n    active: &ACTIVE,\n    dfu: &DFU,\n    state: &STATE,\n    page_size: u32,\n) {\n    assert_eq!(active.capacity() as u32 % page_size, 0);\n    assert_eq!(dfu.capacity() as u32 % page_size, 0);\n    // DFU partition has to be bigger than ACTIVE partition to handle swap algorithm\n    assert!(dfu.capacity() as u32 - active.capacity() as u32 >= page_size);\n    assert!(2 + 4 * (active.capacity() as u32 / page_size) <= state.capacity() as u32 / STATE::WRITE_SIZE as u32);\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n    use crate::mem_flash::MemFlash;\n\n    #[test]\n    #[should_panic]\n    fn test_range_asserts() {\n        const ACTIVE_SIZE: usize = 4194304 - 4096;\n        const DFU_SIZE: usize = 4194304;\n        const STATE_SIZE: usize = 4096;\n        static ACTIVE: MemFlash<ACTIVE_SIZE, 4, 4> = MemFlash::new(0xFF);\n        static DFU: MemFlash<DFU_SIZE, 4, 4> = MemFlash::new(0xFF);\n        static STATE: MemFlash<STATE_SIZE, 4, 4> = MemFlash::new(0xFF);\n        assert_partitions(&ACTIVE, &DFU, &STATE, 4096);\n    }\n}\n"
  },
  {
    "path": "embassy-boot/src/digest_adapters/ed25519_dalek.rs",
    "content": "use digest::typenum::U64;\nuse digest::{FixedOutput, HashMarker, OutputSizeUser, Update};\nuse ed25519_dalek::Digest;\n\npub struct Sha512(ed25519_dalek::Sha512);\n\nimpl Default for Sha512 {\n    fn default() -> Self {\n        Self(ed25519_dalek::Sha512::new())\n    }\n}\n\nimpl Update for Sha512 {\n    fn update(&mut self, data: &[u8]) {\n        Digest::update(&mut self.0, data)\n    }\n}\n\nimpl FixedOutput for Sha512 {\n    fn finalize_into(self, out: &mut digest::Output<Self>) {\n        let result = self.0.finalize();\n        out.as_mut_slice().copy_from_slice(result.as_slice())\n    }\n}\n\nimpl OutputSizeUser for Sha512 {\n    type OutputSize = U64;\n}\n\nimpl HashMarker for Sha512 {}\n"
  },
  {
    "path": "embassy-boot/src/digest_adapters/mod.rs",
    "content": "#[cfg(feature = \"ed25519-dalek\")]\npub(crate) mod ed25519_dalek;\n\n#[cfg(feature = \"ed25519-salty\")]\npub(crate) mod salty;\n"
  },
  {
    "path": "embassy-boot/src/digest_adapters/salty.rs",
    "content": "use digest::typenum::U64;\nuse digest::{FixedOutput, HashMarker, OutputSizeUser, Update};\n\npub struct Sha512(salty::Sha512);\n\nimpl Default for Sha512 {\n    fn default() -> Self {\n        Self(salty::Sha512::new())\n    }\n}\n\nimpl Update for Sha512 {\n    fn update(&mut self, data: &[u8]) {\n        self.0.update(data)\n    }\n}\n\nimpl FixedOutput for Sha512 {\n    fn finalize_into(self, out: &mut digest::Output<Self>) {\n        let result = self.0.finalize();\n        out.as_mut_slice().copy_from_slice(result.as_slice())\n    }\n}\n\nimpl OutputSizeUser for Sha512 {\n    type OutputSize = U64;\n}\n\nimpl HashMarker for Sha512 {}\n"
  },
  {
    "path": "embassy-boot/src/firmware_updater/asynch.rs",
    "content": "use digest::Digest;\n#[cfg(target_os = \"none\")]\nuse embassy_embedded_hal::flash::partition::Partition;\n#[cfg(target_os = \"none\")]\nuse embassy_sync::blocking_mutex::raw::NoopRawMutex;\nuse embedded_storage_async::nor_flash::NorFlash;\n\nuse super::FirmwareUpdaterConfig;\nuse crate::{BOOT_MAGIC, DFU_DETACH_MAGIC, FirmwareUpdaterError, STATE_ERASE_VALUE, SWAP_MAGIC, State};\n\n/// FirmwareUpdater is an application API for interacting with the BootLoader without the ability to\n/// 'mess up' the internal bootloader state\npub struct FirmwareUpdater<'d, DFU: NorFlash, STATE: NorFlash> {\n    dfu: DFU,\n    state: FirmwareState<'d, STATE>,\n    last_erased_dfu_sector_index: Option<usize>,\n}\n\n#[cfg(target_os = \"none\")]\nimpl<'a, DFU: NorFlash, STATE: NorFlash>\n    FirmwareUpdaterConfig<Partition<'a, NoopRawMutex, DFU>, Partition<'a, NoopRawMutex, STATE>>\n{\n    /// Create a firmware updater config from the flash and address symbols defined in the linkerfile\n    pub fn from_linkerfile(\n        dfu_flash: &'a embassy_sync::mutex::Mutex<NoopRawMutex, DFU>,\n        state_flash: &'a embassy_sync::mutex::Mutex<NoopRawMutex, STATE>,\n    ) -> Self {\n        unsafe extern \"C\" {\n            static __bootloader_state_start: u32;\n            static __bootloader_state_end: u32;\n            static __bootloader_dfu_start: u32;\n            static __bootloader_dfu_end: u32;\n        }\n\n        let dfu = unsafe {\n            let start = &__bootloader_dfu_start as *const u32 as u32;\n            let end = &__bootloader_dfu_end as *const u32 as u32;\n            trace!(\"DFU: 0x{:x} - 0x{:x}\", start, end);\n\n            Partition::new(dfu_flash, start, end - start)\n        };\n        let state = unsafe {\n            let start = &__bootloader_state_start as *const u32 as u32;\n            let end = &__bootloader_state_end as *const u32 as u32;\n            trace!(\"STATE: 0x{:x} - 0x{:x}\", start, end);\n\n            Partition::new(state_flash, start, end - start)\n        };\n\n        Self { dfu, state }\n    }\n}\n\nimpl<'d, DFU: NorFlash, STATE: NorFlash> FirmwareUpdater<'d, DFU, STATE> {\n    /// Create a firmware updater instance with partition ranges for the update and state partitions.\n    pub fn new(config: FirmwareUpdaterConfig<DFU, STATE>, aligned: &'d mut [u8]) -> Self {\n        Self {\n            dfu: config.dfu,\n            state: FirmwareState::new(config.state, aligned),\n            last_erased_dfu_sector_index: None,\n        }\n    }\n\n    /// Obtain the current state.\n    ///\n    /// This is useful to check if the bootloader has just done a swap, in order\n    /// to do verifications and self-tests of the new image before calling\n    /// `mark_booted`.\n    pub async fn get_state(&mut self) -> Result<State, FirmwareUpdaterError> {\n        self.state.get_state().await\n    }\n\n    /// Verify the DFU given a public key. If there is an error then DO NOT\n    /// proceed with updating the firmware as it must be signed with a\n    /// corresponding private key (otherwise it could be malicious firmware).\n    ///\n    /// Mark to trigger firmware swap on next boot if verify succeeds.\n    ///\n    /// If the \"ed25519-salty\" feature is set (or another similar feature) then the signature is expected to have\n    /// been generated from a SHA-512 digest of the firmware bytes.\n    ///\n    /// If no signature feature is set then this method will always return a\n    /// signature error.\n    #[cfg(feature = \"_verify\")]\n    pub async fn verify_and_mark_updated(\n        &mut self,\n        _public_key: &[u8; 32],\n        _signature: &[u8; 64],\n        _update_len: u32,\n    ) -> Result<(), FirmwareUpdaterError> {\n        assert!(_update_len <= self.dfu.capacity() as u32);\n\n        self.state.verify_booted().await?;\n\n        #[cfg(feature = \"ed25519-dalek\")]\n        {\n            use ed25519_dalek::{Signature, SignatureError, Verifier, VerifyingKey};\n\n            use crate::digest_adapters::ed25519_dalek::Sha512;\n\n            let into_signature_error = |e: SignatureError| FirmwareUpdaterError::Signature(e.into());\n\n            let public_key = VerifyingKey::from_bytes(_public_key).map_err(into_signature_error)?;\n            let signature = Signature::from_bytes(_signature);\n\n            let mut chunk_buf = [0; 2];\n            let mut message = [0; 64];\n            self.hash::<Sha512>(_update_len, &mut chunk_buf, &mut message).await?;\n\n            public_key.verify(&message, &signature).map_err(into_signature_error)?;\n            return self.state.mark_updated().await;\n        }\n        #[cfg(feature = \"ed25519-salty\")]\n        {\n            use salty::{PublicKey, Signature};\n\n            use crate::digest_adapters::salty::Sha512;\n\n            fn into_signature_error<E>(_: E) -> FirmwareUpdaterError {\n                FirmwareUpdaterError::Signature(signature::Error::default())\n            }\n\n            let public_key = PublicKey::try_from(_public_key).map_err(into_signature_error)?;\n            let signature = Signature::try_from(_signature).map_err(into_signature_error)?;\n\n            let mut message = [0; 64];\n            let mut chunk_buf = [0; 2];\n            self.hash::<Sha512>(_update_len, &mut chunk_buf, &mut message).await?;\n\n            let r = public_key.verify(&message, &signature);\n            trace!(\n                \"Verifying with public key {}, signature {} and message {} yields ok: {}\",\n                public_key.to_bytes(),\n                signature.to_bytes(),\n                message,\n                r.is_ok()\n            );\n            r.map_err(into_signature_error)?;\n            return self.state.mark_updated().await;\n        }\n        #[cfg(not(any(feature = \"ed25519-dalek\", feature = \"ed25519-salty\")))]\n        {\n            Err(FirmwareUpdaterError::Signature(signature::Error::new()))\n        }\n    }\n\n    /// Verify the update in DFU with any digest.\n    pub async fn hash<D: Digest>(\n        &mut self,\n        update_len: u32,\n        chunk_buf: &mut [u8],\n        output: &mut [u8],\n    ) -> Result<(), FirmwareUpdaterError> {\n        let mut digest = D::new();\n        for offset in (0..update_len).step_by(chunk_buf.len()) {\n            self.dfu.read(offset, chunk_buf).await?;\n            let len = core::cmp::min((update_len - offset) as usize, chunk_buf.len());\n            digest.update(&chunk_buf[..len]);\n        }\n        output.copy_from_slice(digest.finalize().as_slice());\n        Ok(())\n    }\n\n    /// Read a slice of data from the DFU storage peripheral, starting the read\n    /// operation at the given address offset, and reading `buf.len()` bytes.\n    ///\n    /// # Errors\n    ///\n    /// Returns an error if the arguments are not aligned or out of bounds.\n    pub async fn read_dfu(&mut self, offset: u32, buf: &mut [u8]) -> Result<(), FirmwareUpdaterError> {\n        self.dfu.read(offset, buf).await?;\n        Ok(())\n    }\n\n    /// Mark to trigger firmware swap on next boot.\n    #[cfg(not(feature = \"_verify\"))]\n    pub async fn mark_updated(&mut self) -> Result<(), FirmwareUpdaterError> {\n        self.state.mark_updated().await\n    }\n\n    /// Mark to trigger USB DFU on next boot.\n    pub async fn mark_dfu(&mut self) -> Result<(), FirmwareUpdaterError> {\n        self.state.verify_booted().await?;\n        self.state.mark_dfu().await\n    }\n\n    /// Mark firmware boot successful and stop rollback on reset.\n    pub async fn mark_booted(&mut self) -> Result<(), FirmwareUpdaterError> {\n        self.state.mark_booted().await\n    }\n\n    /// Writes firmware data to the device.\n    ///\n    /// This function writes the given data to the firmware area starting at the specified offset.\n    /// It handles sector erasures and data writes while verifying the device is in a proper state\n    /// for firmware updates. The function ensures that only unerased sectors are erased before\n    /// writing and efficiently handles the writing process across sector boundaries and in\n    /// various configurations (data size, sector size, etc.).\n    ///\n    /// # Arguments\n    ///\n    /// * `offset` - The starting offset within the firmware area where data writing should begin.\n    /// * `data` - A slice of bytes representing the firmware data to be written. It must be a\n    /// multiple of NorFlash WRITE_SIZE.\n    ///\n    /// # Returns\n    ///\n    /// A `Result<(), FirmwareUpdaterError>` indicating the success or failure of the write operation.\n    ///\n    /// # Errors\n    ///\n    /// This function will return an error if:\n    ///\n    /// - The device is not in a proper state to receive firmware updates (e.g., not booted).\n    /// - There is a failure erasing a sector before writing.\n    /// - There is a failure writing data to the device.\n    pub async fn write_firmware(&mut self, offset: usize, data: &[u8]) -> Result<(), FirmwareUpdaterError> {\n        // Make sure we are running a booted firmware to avoid reverting to a bad state.\n        self.state.verify_booted().await?;\n\n        // Initialize variables to keep track of the remaining data and the current offset.\n        let mut remaining_data = data;\n        let mut offset = offset;\n\n        // Continue writing as long as there is data left to write.\n        while !remaining_data.is_empty() {\n            // Compute the current sector and its boundaries.\n            let current_sector = offset / DFU::ERASE_SIZE;\n            let sector_start = current_sector * DFU::ERASE_SIZE;\n            let sector_end = sector_start + DFU::ERASE_SIZE;\n            // Determine if the current sector needs to be erased before writing.\n            let need_erase = self\n                .last_erased_dfu_sector_index\n                .map_or(true, |last_erased_sector| current_sector != last_erased_sector);\n\n            // If the sector needs to be erased, erase it and update the last erased sector index.\n            if need_erase {\n                self.dfu.erase(sector_start as u32, sector_end as u32).await?;\n                self.last_erased_dfu_sector_index = Some(current_sector);\n            }\n\n            // Calculate the size of the data chunk that can be written in the current iteration.\n            let write_size = core::cmp::min(remaining_data.len(), sector_end - offset);\n            // Split the data to get the current chunk to be written and the remaining data.\n            let (data_chunk, rest) = remaining_data.split_at(write_size);\n\n            // Write the current data chunk.\n            self.dfu.write(offset as u32, data_chunk).await?;\n\n            // Update the offset and remaining data for the next iteration.\n            remaining_data = rest;\n            offset += write_size;\n        }\n\n        Ok(())\n    }\n\n    /// Prepare for an incoming DFU update by erasing the entire DFU area and\n    /// returning its `Partition`.\n    ///\n    /// Using this instead of `write_firmware` allows for an optimized API in\n    /// exchange for added complexity.\n    pub async fn prepare_update(&mut self) -> Result<&mut DFU, FirmwareUpdaterError> {\n        self.state.verify_booted().await?;\n        self.dfu.erase(0, self.dfu.capacity() as u32).await?;\n\n        Ok(&mut self.dfu)\n    }\n}\n\n/// Manages the state partition of the firmware update.\n///\n/// Can be used standalone for more fine grained control, or as part of the updater.\npub struct FirmwareState<'d, STATE> {\n    state: STATE,\n    aligned: &'d mut [u8],\n}\n\nimpl<'d, STATE: NorFlash> FirmwareState<'d, STATE> {\n    /// Create a firmware state instance from a FirmwareUpdaterConfig with a buffer for magic content and state partition.\n    ///\n    /// # Safety\n    ///\n    /// The `aligned` buffer must have a size of STATE::WRITE_SIZE, and follow the alignment rules for the flash being read from\n    /// and written to.\n    pub fn from_config<DFU: NorFlash>(config: FirmwareUpdaterConfig<DFU, STATE>, aligned: &'d mut [u8]) -> Self {\n        Self::new(config.state, aligned)\n    }\n\n    /// Create a firmware state instance with a buffer for magic content and state partition.\n    ///\n    /// # Safety\n    ///\n    /// The `aligned` buffer must have a size of maximum of STATE::WRITE_SIZE and STATE::READ_SIZE,\n    /// and follow the alignment rules for the flash being read from and written to.\n    pub fn new(state: STATE, aligned: &'d mut [u8]) -> Self {\n        assert_eq!(aligned.len(), STATE::WRITE_SIZE.max(STATE::READ_SIZE));\n        Self { state, aligned }\n    }\n\n    // Make sure we are running a booted firmware to avoid reverting to a bad state.\n    async fn verify_booted(&mut self) -> Result<(), FirmwareUpdaterError> {\n        let state = self.get_state().await?;\n        if state == State::Boot || state == State::DfuDetach || state == State::Revert {\n            Ok(())\n        } else {\n            Err(FirmwareUpdaterError::BadState)\n        }\n    }\n\n    /// Obtain the current state.\n    ///\n    /// This is useful to check if the bootloader has just done a swap, in order\n    /// to do verifications and self-tests of the new image before calling\n    /// `mark_booted`.\n    pub async fn get_state(&mut self) -> Result<State, FirmwareUpdaterError> {\n        self.state.read(0, &mut self.aligned).await?;\n        Ok(State::from(&self.aligned[..STATE::WRITE_SIZE]))\n    }\n\n    /// Mark to trigger firmware swap on next boot.\n    pub async fn mark_updated(&mut self) -> Result<(), FirmwareUpdaterError> {\n        self.set_magic(SWAP_MAGIC).await\n    }\n\n    /// Mark to trigger USB DFU on next boot.\n    pub async fn mark_dfu(&mut self) -> Result<(), FirmwareUpdaterError> {\n        self.set_magic(DFU_DETACH_MAGIC).await\n    }\n\n    /// Mark firmware boot successful and stop rollback on reset.\n    pub async fn mark_booted(&mut self) -> Result<(), FirmwareUpdaterError> {\n        self.set_magic(BOOT_MAGIC).await\n    }\n\n    async fn set_magic(&mut self, magic: u8) -> Result<(), FirmwareUpdaterError> {\n        self.state.read(0, &mut self.aligned).await?;\n\n        if self.aligned[..STATE::WRITE_SIZE].iter().any(|&b| b != magic) {\n            // Read progress validity\n            if STATE::READ_SIZE <= 2 * STATE::WRITE_SIZE {\n                self.state.read(STATE::WRITE_SIZE as u32, &mut self.aligned).await?;\n            } else {\n                self.aligned.rotate_left(STATE::WRITE_SIZE);\n            }\n\n            if self.aligned[..STATE::WRITE_SIZE]\n                .iter()\n                .any(|&b| b != STATE_ERASE_VALUE)\n            {\n                // The current progress validity marker is invalid\n            } else {\n                // Invalidate progress\n                self.aligned.fill(!STATE_ERASE_VALUE);\n                self.state\n                    .write(STATE::WRITE_SIZE as u32, &self.aligned[..STATE::WRITE_SIZE])\n                    .await?;\n            }\n\n            // Clear magic and progress\n            self.state.erase(0, self.state.capacity() as u32).await?;\n\n            // Set magic\n            self.aligned.fill(magic);\n            self.state.write(0, &self.aligned[..STATE::WRITE_SIZE]).await?;\n        }\n        Ok(())\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use embassy_embedded_hal::flash::partition::Partition;\n    use embassy_sync::blocking_mutex::raw::NoopRawMutex;\n    use embassy_sync::mutex::Mutex;\n    use futures::executor::block_on;\n    use sha1::{Digest, Sha1};\n\n    use super::*;\n    use crate::mem_flash::MemFlash;\n\n    #[test]\n    fn can_verify_sha1() {\n        let flash = Mutex::<NoopRawMutex, _>::new(MemFlash::<131072, 4096, 8>::default());\n        let state = Partition::new(&flash, 0, 4096);\n        let dfu = Partition::new(&flash, 65536, 65536);\n        let mut aligned = [0; 8];\n\n        let update = [0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66];\n        let mut to_write = [0; 4096];\n        to_write[..7].copy_from_slice(update.as_slice());\n\n        let mut updater = FirmwareUpdater::new(FirmwareUpdaterConfig { dfu, state }, &mut aligned);\n        block_on(updater.write_firmware(0, to_write.as_slice())).unwrap();\n        let mut chunk_buf = [0; 2];\n        let mut hash = [0; 20];\n        block_on(updater.hash::<Sha1>(update.len() as u32, &mut chunk_buf, &mut hash)).unwrap();\n\n        assert_eq!(Sha1::digest(update).as_slice(), hash);\n    }\n\n    #[test]\n    fn can_verify_sha1_sector_bigger_than_chunk() {\n        let flash = Mutex::<NoopRawMutex, _>::new(MemFlash::<131072, 4096, 8>::default());\n        let state = Partition::new(&flash, 0, 4096);\n        let dfu = Partition::new(&flash, 65536, 65536);\n        let mut aligned = [0; 8];\n\n        let update = [0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66];\n        let mut to_write = [0; 4096];\n        to_write[..7].copy_from_slice(update.as_slice());\n\n        let mut updater = FirmwareUpdater::new(FirmwareUpdaterConfig { dfu, state }, &mut aligned);\n        let mut offset = 0;\n        for chunk in to_write.chunks(1024) {\n            block_on(updater.write_firmware(offset, chunk)).unwrap();\n            offset += chunk.len();\n        }\n        let mut chunk_buf = [0; 2];\n        let mut hash = [0; 20];\n        block_on(updater.hash::<Sha1>(update.len() as u32, &mut chunk_buf, &mut hash)).unwrap();\n\n        assert_eq!(Sha1::digest(update).as_slice(), hash);\n    }\n\n    #[test]\n    fn can_verify_sha1_sector_smaller_than_chunk() {\n        let flash = Mutex::<NoopRawMutex, _>::new(MemFlash::<131072, 1024, 8>::default());\n        let state = Partition::new(&flash, 0, 4096);\n        let dfu = Partition::new(&flash, 65536, 65536);\n        let mut aligned = [0; 8];\n\n        let update = [0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66];\n        let mut to_write = [0; 4096];\n        to_write[..7].copy_from_slice(update.as_slice());\n\n        let mut updater = FirmwareUpdater::new(FirmwareUpdaterConfig { dfu, state }, &mut aligned);\n        let mut offset = 0;\n        for chunk in to_write.chunks(2048) {\n            block_on(updater.write_firmware(offset, chunk)).unwrap();\n            offset += chunk.len();\n        }\n        let mut chunk_buf = [0; 2];\n        let mut hash = [0; 20];\n        block_on(updater.hash::<Sha1>(update.len() as u32, &mut chunk_buf, &mut hash)).unwrap();\n\n        assert_eq!(Sha1::digest(update).as_slice(), hash);\n    }\n\n    #[test]\n    fn can_verify_sha1_cross_sector_boundary() {\n        let flash = Mutex::<NoopRawMutex, _>::new(MemFlash::<131072, 1024, 8>::default());\n        let state = Partition::new(&flash, 0, 4096);\n        let dfu = Partition::new(&flash, 65536, 65536);\n        let mut aligned = [0; 8];\n\n        let update = [0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66];\n        let mut to_write = [0; 4096];\n        to_write[..7].copy_from_slice(update.as_slice());\n\n        let mut updater = FirmwareUpdater::new(FirmwareUpdaterConfig { dfu, state }, &mut aligned);\n        let mut offset = 0;\n        for chunk in to_write.chunks(896) {\n            block_on(updater.write_firmware(offset, chunk)).unwrap();\n            offset += chunk.len();\n        }\n        let mut chunk_buf = [0; 2];\n        let mut hash = [0; 20];\n        block_on(updater.hash::<Sha1>(update.len() as u32, &mut chunk_buf, &mut hash)).unwrap();\n\n        assert_eq!(Sha1::digest(update).as_slice(), hash);\n    }\n}\n"
  },
  {
    "path": "embassy-boot/src/firmware_updater/blocking.rs",
    "content": "use digest::Digest;\n#[cfg(target_os = \"none\")]\nuse embassy_embedded_hal::flash::partition::BlockingPartition;\n#[cfg(target_os = \"none\")]\nuse embassy_sync::blocking_mutex::raw::NoopRawMutex;\nuse embedded_storage::nor_flash::NorFlash;\n\nuse super::FirmwareUpdaterConfig;\nuse crate::{BOOT_MAGIC, DFU_DETACH_MAGIC, FirmwareUpdaterError, STATE_ERASE_VALUE, SWAP_MAGIC, State};\n\n/// Blocking FirmwareUpdater is an application API for interacting with the BootLoader without the ability to\n/// 'mess up' the internal bootloader state\npub struct BlockingFirmwareUpdater<'d, DFU: NorFlash, STATE: NorFlash> {\n    dfu: DFU,\n    state: BlockingFirmwareState<'d, STATE>,\n    last_erased_dfu_sector_index: Option<usize>,\n}\n\n#[cfg(target_os = \"none\")]\nimpl<'a, DFU: NorFlash, STATE: NorFlash>\n    FirmwareUpdaterConfig<BlockingPartition<'a, NoopRawMutex, DFU>, BlockingPartition<'a, NoopRawMutex, STATE>>\n{\n    /// Constructs a `FirmwareUpdaterConfig` instance from flash memory and address symbols defined in the linker file.\n    ///\n    /// This method initializes `BlockingPartition` instances for the DFU (Device Firmware Update), and state\n    /// partitions, leveraging start and end addresses specified by the linker. These partitions are critical\n    /// for managing firmware updates, application state, and boot operations within the bootloader.\n    ///\n    /// # Parameters\n    /// - `dfu_flash`: A reference to a mutex-protected `RefCell` for the DFU partition's flash interface.\n    /// - `state_flash`: A reference to a mutex-protected `RefCell` for the state partition's flash interface.\n    ///\n    /// # Safety\n    /// The method contains `unsafe` blocks for dereferencing raw pointers that represent the start and end addresses\n    /// of the bootloader's partitions in flash memory. It is crucial that these addresses are accurately defined\n    /// in the memory.x file to prevent undefined behavior.\n    ///\n    /// The caller must ensure that the memory regions defined by these symbols are valid and that the flash memory\n    /// interfaces provided are compatible with these regions.\n    ///\n    /// # Returns\n    /// A `FirmwareUpdaterConfig` instance with `BlockingPartition` instances for the DFU, and state partitions.\n    ///\n    /// # Example\n    /// ```ignore\n    /// // Assume `dfu_flash`, and `state_flash` share the same flash memory interface.\n    /// let layout = Flash::new_blocking(p.FLASH).into_blocking_regions();\n    /// let flash = Mutex::new(RefCell::new(layout.bank1_region));\n    ///\n    /// let config = FirmwareUpdaterConfig::from_linkerfile_blocking(&flash, &flash);\n    /// // `config` can now be used to create a `FirmwareUpdater` instance for managing boot operations.\n    /// ```\n    /// Working examples can be found in the bootloader examples folder.\n    pub fn from_linkerfile_blocking(\n        dfu_flash: &'a embassy_sync::blocking_mutex::Mutex<NoopRawMutex, core::cell::RefCell<DFU>>,\n        state_flash: &'a embassy_sync::blocking_mutex::Mutex<NoopRawMutex, core::cell::RefCell<STATE>>,\n    ) -> Self {\n        unsafe extern \"C\" {\n            static __bootloader_state_start: u32;\n            static __bootloader_state_end: u32;\n            static __bootloader_dfu_start: u32;\n            static __bootloader_dfu_end: u32;\n        }\n\n        let dfu = unsafe {\n            let start = &__bootloader_dfu_start as *const u32 as u32;\n            let end = &__bootloader_dfu_end as *const u32 as u32;\n            trace!(\"DFU: 0x{:x} - 0x{:x}\", start, end);\n\n            BlockingPartition::new(dfu_flash, start, end - start)\n        };\n        let state = unsafe {\n            let start = &__bootloader_state_start as *const u32 as u32;\n            let end = &__bootloader_state_end as *const u32 as u32;\n            trace!(\"STATE: 0x{:x} - 0x{:x}\", start, end);\n\n            BlockingPartition::new(state_flash, start, end - start)\n        };\n\n        Self { dfu, state }\n    }\n}\n\nimpl<'d, DFU: NorFlash, STATE: NorFlash> BlockingFirmwareUpdater<'d, DFU, STATE> {\n    /// Create a firmware updater instance with partition ranges for the update and state partitions.\n    ///\n    /// # Safety\n    ///\n    /// The `aligned` buffer must have a size of STATE::WRITE_SIZE, and follow the alignment rules for the flash being read from\n    /// and written to.\n    pub fn new(config: FirmwareUpdaterConfig<DFU, STATE>, aligned: &'d mut [u8]) -> Self {\n        Self {\n            dfu: config.dfu,\n            state: BlockingFirmwareState::new(config.state, aligned),\n            last_erased_dfu_sector_index: None,\n        }\n    }\n\n    /// Obtain the current state.\n    ///\n    /// This is useful to check if the bootloader has just done a swap, in order\n    /// to do verifications and self-tests of the new image before calling\n    /// `mark_booted`.\n    pub fn get_state(&mut self) -> Result<State, FirmwareUpdaterError> {\n        self.state.get_state()\n    }\n\n    /// Verify the DFU given a public key. If there is an error then DO NOT\n    /// proceed with updating the firmware as it must be signed with a\n    /// corresponding private key (otherwise it could be malicious firmware).\n    ///\n    /// Mark to trigger firmware swap on next boot if verify succeeds.\n    ///\n    /// If the \"ed25519-salty\" feature is set (or another similar feature) then the signature is expected to have\n    /// been generated from a SHA-512 digest of the firmware bytes.\n    ///\n    /// If no signature feature is set then this method will always return a\n    /// signature error.\n    #[cfg(feature = \"_verify\")]\n    pub fn verify_and_mark_updated(\n        &mut self,\n        _public_key: &[u8; 32],\n        _signature: &[u8; 64],\n        _update_len: u32,\n    ) -> Result<(), FirmwareUpdaterError> {\n        assert!(_update_len <= self.dfu.capacity() as u32);\n\n        self.state.verify_booted()?;\n\n        #[cfg(feature = \"ed25519-dalek\")]\n        {\n            use ed25519_dalek::{Signature, SignatureError, Verifier, VerifyingKey};\n\n            use crate::digest_adapters::ed25519_dalek::Sha512;\n\n            let into_signature_error = |e: SignatureError| FirmwareUpdaterError::Signature(e.into());\n\n            let public_key = VerifyingKey::from_bytes(_public_key).map_err(into_signature_error)?;\n            let signature = Signature::from_bytes(_signature);\n\n            let mut message = [0; 64];\n            let mut chunk_buf = [0; 2];\n            self.hash::<Sha512>(_update_len, &mut chunk_buf, &mut message)?;\n\n            public_key.verify(&message, &signature).map_err(into_signature_error)?;\n            return self.state.mark_updated();\n        }\n        #[cfg(feature = \"ed25519-salty\")]\n        {\n            use salty::{PublicKey, Signature};\n\n            use crate::digest_adapters::salty::Sha512;\n\n            fn into_signature_error<E>(_: E) -> FirmwareUpdaterError {\n                FirmwareUpdaterError::Signature(signature::Error::default())\n            }\n\n            let public_key = PublicKey::try_from(_public_key).map_err(into_signature_error)?;\n            let signature = Signature::try_from(_signature).map_err(into_signature_error)?;\n\n            let mut message = [0; 64];\n            let mut chunk_buf = [0; 2];\n            self.hash::<Sha512>(_update_len, &mut chunk_buf, &mut message)?;\n\n            let r = public_key.verify(&message, &signature);\n            trace!(\n                \"Verifying with public key {}, signature {} and message {} yields ok: {}\",\n                public_key.to_bytes(),\n                signature.to_bytes(),\n                message,\n                r.is_ok()\n            );\n            r.map_err(into_signature_error)?;\n            return self.state.mark_updated();\n        }\n        #[cfg(not(any(feature = \"ed25519-dalek\", feature = \"ed25519-salty\")))]\n        {\n            Err(FirmwareUpdaterError::Signature(signature::Error::new()))\n        }\n    }\n\n    /// Verify the update in DFU with any digest.\n    pub fn hash<D: Digest>(\n        &mut self,\n        update_len: u32,\n        chunk_buf: &mut [u8],\n        output: &mut [u8],\n    ) -> Result<(), FirmwareUpdaterError> {\n        let mut digest = D::new();\n        for offset in (0..update_len).step_by(chunk_buf.len()) {\n            self.dfu.read(offset, chunk_buf)?;\n            let len = core::cmp::min((update_len - offset) as usize, chunk_buf.len());\n            digest.update(&chunk_buf[..len]);\n        }\n        output.copy_from_slice(digest.finalize().as_slice());\n        Ok(())\n    }\n\n    /// Read a slice of data from the DFU storage peripheral, starting the read\n    /// operation at the given address offset, and reading `buf.len()` bytes.\n    ///\n    /// # Errors\n    ///\n    /// Returns an error if the arguments are not aligned or out of bounds.\n    pub fn read_dfu(&mut self, offset: u32, buf: &mut [u8]) -> Result<(), FirmwareUpdaterError> {\n        self.dfu.read(offset, buf)?;\n        Ok(())\n    }\n\n    /// Mark to trigger firmware swap on next boot.\n    #[cfg(not(feature = \"_verify\"))]\n    pub fn mark_updated(&mut self) -> Result<(), FirmwareUpdaterError> {\n        self.state.mark_updated()\n    }\n\n    /// Mark to trigger USB DFU device on next boot.\n    pub fn mark_dfu(&mut self) -> Result<(), FirmwareUpdaterError> {\n        self.state.verify_booted()?;\n        self.state.mark_dfu()\n    }\n\n    /// Mark firmware boot successful and stop rollback on reset.\n    pub fn mark_booted(&mut self) -> Result<(), FirmwareUpdaterError> {\n        self.state.mark_booted()\n    }\n\n    /// Writes firmware data to the device.\n    ///\n    /// This function writes the given data to the firmware area starting at the specified offset.\n    /// It handles sector erasures and data writes while verifying the device is in a proper state\n    /// for firmware updates. The function ensures that only unerased sectors are erased before\n    /// writing and efficiently handles the writing process across sector boundaries and in\n    /// various configurations (data size, sector size, etc.).\n    ///\n    /// # Arguments\n    ///\n    /// * `offset` - The starting offset within the firmware area where data writing should begin.\n    /// * `data` - A slice of bytes representing the firmware data to be written. It must be a\n    /// multiple of NorFlash WRITE_SIZE.\n    ///\n    /// # Returns\n    ///\n    /// A `Result<(), FirmwareUpdaterError>` indicating the success or failure of the write operation.\n    ///\n    /// # Errors\n    ///\n    /// This function will return an error if:\n    ///\n    /// - The device is not in a proper state to receive firmware updates (e.g., not booted).\n    /// - There is a failure erasing a sector before writing.\n    /// - There is a failure writing data to the device.\n    pub fn write_firmware(&mut self, offset: usize, data: &[u8]) -> Result<(), FirmwareUpdaterError> {\n        // Make sure we are running a booted firmware to avoid reverting to a bad state.\n        self.state.verify_booted()?;\n\n        // Initialize variables to keep track of the remaining data and the current offset.\n        let mut remaining_data = data;\n        let mut offset = offset;\n\n        // Continue writing as long as there is data left to write.\n        while !remaining_data.is_empty() {\n            // Compute the current sector and its boundaries.\n            let current_sector = offset / DFU::ERASE_SIZE;\n            let sector_start = current_sector * DFU::ERASE_SIZE;\n            let sector_end = sector_start + DFU::ERASE_SIZE;\n            // Determine if the current sector needs to be erased before writing.\n            let need_erase = self\n                .last_erased_dfu_sector_index\n                .map_or(true, |last_erased_sector| current_sector != last_erased_sector);\n\n            // If the sector needs to be erased, erase it and update the last erased sector index.\n            if need_erase {\n                self.dfu.erase(sector_start as u32, sector_end as u32)?;\n                self.last_erased_dfu_sector_index = Some(current_sector);\n            }\n\n            // Calculate the size of the data chunk that can be written in the current iteration.\n            let write_size = core::cmp::min(remaining_data.len(), sector_end - offset);\n            // Split the data to get the current chunk to be written and the remaining data.\n            let (data_chunk, rest) = remaining_data.split_at(write_size);\n\n            // Write the current data chunk.\n            self.dfu.write(offset as u32, data_chunk)?;\n\n            // Update the offset and remaining data for the next iteration.\n            remaining_data = rest;\n            offset += write_size;\n        }\n\n        Ok(())\n    }\n\n    /// Prepare for an incoming DFU update by erasing the entire DFU area and\n    /// returning its `Partition`.\n    ///\n    /// Using this instead of `write_firmware` allows for an optimized API in\n    /// exchange for added complexity.\n    pub fn prepare_update(&mut self) -> Result<&mut DFU, FirmwareUpdaterError> {\n        self.state.verify_booted()?;\n        self.dfu.erase(0, self.dfu.capacity() as u32)?;\n\n        Ok(&mut self.dfu)\n    }\n}\n\n/// Manages the state partition of the firmware update.\n///\n/// Can be used standalone for more fine grained control, or as part of the updater.\npub struct BlockingFirmwareState<'d, STATE> {\n    state: STATE,\n    aligned: &'d mut [u8],\n}\n\nimpl<'d, STATE: NorFlash> BlockingFirmwareState<'d, STATE> {\n    /// Creates a firmware state instance from a FirmwareUpdaterConfig, with a buffer for magic content and state partition.\n    ///\n    /// # Safety\n    ///\n    /// The `aligned` buffer must have a size of STATE::WRITE_SIZE, and follow the alignment rules for the flash being read from\n    /// and written to.\n    pub fn from_config<DFU: NorFlash>(config: FirmwareUpdaterConfig<DFU, STATE>, aligned: &'d mut [u8]) -> Self {\n        Self::new(config.state, aligned)\n    }\n\n    /// Create a firmware state instance with a buffer for magic content and state partition.\n    ///\n    /// # Safety\n    ///\n    /// The `aligned` buffer must have a size of STATE::WRITE_SIZE, and follow the alignment rules for the flash being read from\n    /// and written to.\n    pub fn new(state: STATE, aligned: &'d mut [u8]) -> Self {\n        assert_eq!(aligned.len(), STATE::WRITE_SIZE);\n        Self { state, aligned }\n    }\n\n    // Make sure we are running a booted firmware to avoid reverting to a bad state.\n    fn verify_booted(&mut self) -> Result<(), FirmwareUpdaterError> {\n        let state = self.get_state()?;\n        if state == State::Boot || state == State::DfuDetach || state == State::Revert {\n            Ok(())\n        } else {\n            Err(FirmwareUpdaterError::BadState)\n        }\n    }\n\n    /// Obtain the current state.\n    ///\n    /// This is useful to check if the bootloader has just done a swap, in order\n    /// to do verifications and self-tests of the new image before calling\n    /// `mark_booted`.\n    pub fn get_state(&mut self) -> Result<State, FirmwareUpdaterError> {\n        self.state.read(0, &mut self.aligned)?;\n        Ok(State::from(&self.aligned))\n    }\n\n    /// Mark to trigger firmware swap on next boot.\n    pub fn mark_updated(&mut self) -> Result<(), FirmwareUpdaterError> {\n        self.set_magic(SWAP_MAGIC)\n    }\n\n    /// Mark to trigger USB DFU on next boot.\n    pub fn mark_dfu(&mut self) -> Result<(), FirmwareUpdaterError> {\n        self.set_magic(DFU_DETACH_MAGIC)\n    }\n\n    /// Mark firmware boot successful and stop rollback on reset.\n    pub fn mark_booted(&mut self) -> Result<(), FirmwareUpdaterError> {\n        self.set_magic(BOOT_MAGIC)\n    }\n\n    fn set_magic(&mut self, magic: u8) -> Result<(), FirmwareUpdaterError> {\n        self.state.read(0, &mut self.aligned)?;\n\n        if self.aligned.iter().any(|&b| b != magic) {\n            // Read progress validity\n            self.state.read(STATE::WRITE_SIZE as u32, &mut self.aligned)?;\n\n            if self.aligned.iter().any(|&b| b != STATE_ERASE_VALUE) {\n                // The current progress validity marker is invalid\n            } else {\n                // Invalidate progress\n                self.aligned.fill(!STATE_ERASE_VALUE);\n                self.state.write(STATE::WRITE_SIZE as u32, &self.aligned)?;\n            }\n\n            // Clear magic and progress\n            self.state.erase(0, self.state.capacity() as u32)?;\n\n            // Set magic\n            self.aligned.fill(magic);\n            self.state.write(0, &self.aligned)?;\n        }\n        Ok(())\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use core::cell::RefCell;\n\n    use embassy_embedded_hal::flash::partition::BlockingPartition;\n    use embassy_sync::blocking_mutex::Mutex;\n    use embassy_sync::blocking_mutex::raw::NoopRawMutex;\n    use sha1::{Digest, Sha1};\n\n    use super::*;\n    use crate::mem_flash::MemFlash;\n\n    #[test]\n    fn can_verify_sha1() {\n        let flash = Mutex::<NoopRawMutex, _>::new(RefCell::new(MemFlash::<131072, 4096, 8>::default()));\n        let state = BlockingPartition::new(&flash, 0, 4096);\n        let dfu = BlockingPartition::new(&flash, 65536, 65536);\n        let mut aligned = [0; 8];\n\n        let update = [0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66];\n        let mut to_write = [0; 4096];\n        to_write[..7].copy_from_slice(update.as_slice());\n\n        let mut updater = BlockingFirmwareUpdater::new(FirmwareUpdaterConfig { dfu, state }, &mut aligned);\n        updater.write_firmware(0, to_write.as_slice()).unwrap();\n        let mut chunk_buf = [0; 2];\n        let mut hash = [0; 20];\n        updater\n            .hash::<Sha1>(update.len() as u32, &mut chunk_buf, &mut hash)\n            .unwrap();\n\n        assert_eq!(Sha1::digest(update).as_slice(), hash);\n    }\n\n    #[test]\n    fn can_verify_sha1_sector_bigger_than_chunk() {\n        let flash = Mutex::<NoopRawMutex, _>::new(RefCell::new(MemFlash::<131072, 4096, 8>::default()));\n        let state = BlockingPartition::new(&flash, 0, 4096);\n        let dfu = BlockingPartition::new(&flash, 65536, 65536);\n        let mut aligned = [0; 8];\n\n        let update = [0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66];\n        let mut to_write = [0; 4096];\n        to_write[..7].copy_from_slice(update.as_slice());\n\n        let mut updater = BlockingFirmwareUpdater::new(FirmwareUpdaterConfig { dfu, state }, &mut aligned);\n        let mut offset = 0;\n        for chunk in to_write.chunks(1024) {\n            updater.write_firmware(offset, chunk).unwrap();\n            offset += chunk.len();\n        }\n        let mut chunk_buf = [0; 2];\n        let mut hash = [0; 20];\n        updater\n            .hash::<Sha1>(update.len() as u32, &mut chunk_buf, &mut hash)\n            .unwrap();\n\n        assert_eq!(Sha1::digest(update).as_slice(), hash);\n    }\n\n    #[test]\n    fn can_verify_sha1_sector_smaller_than_chunk() {\n        let flash = Mutex::<NoopRawMutex, _>::new(RefCell::new(MemFlash::<131072, 1024, 8>::default()));\n        let state = BlockingPartition::new(&flash, 0, 4096);\n        let dfu = BlockingPartition::new(&flash, 65536, 65536);\n        let mut aligned = [0; 8];\n\n        let update = [0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66];\n        let mut to_write = [0; 4096];\n        to_write[..7].copy_from_slice(update.as_slice());\n\n        let mut updater = BlockingFirmwareUpdater::new(FirmwareUpdaterConfig { dfu, state }, &mut aligned);\n        let mut offset = 0;\n        for chunk in to_write.chunks(2048) {\n            updater.write_firmware(offset, chunk).unwrap();\n            offset += chunk.len();\n        }\n        let mut chunk_buf = [0; 2];\n        let mut hash = [0; 20];\n        updater\n            .hash::<Sha1>(update.len() as u32, &mut chunk_buf, &mut hash)\n            .unwrap();\n\n        assert_eq!(Sha1::digest(update).as_slice(), hash);\n    }\n\n    #[test]\n    fn can_verify_sha1_cross_sector_boundary() {\n        let flash = Mutex::<NoopRawMutex, _>::new(RefCell::new(MemFlash::<131072, 1024, 8>::default()));\n        let state = BlockingPartition::new(&flash, 0, 4096);\n        let dfu = BlockingPartition::new(&flash, 65536, 65536);\n        let mut aligned = [0; 8];\n\n        let update = [0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66];\n        let mut to_write = [0; 4096];\n        to_write[..7].copy_from_slice(update.as_slice());\n\n        let mut updater = BlockingFirmwareUpdater::new(FirmwareUpdaterConfig { dfu, state }, &mut aligned);\n        let mut offset = 0;\n        for chunk in to_write.chunks(896) {\n            updater.write_firmware(offset, chunk).unwrap();\n            offset += chunk.len();\n        }\n        let mut chunk_buf = [0; 2];\n        let mut hash = [0; 20];\n        updater\n            .hash::<Sha1>(update.len() as u32, &mut chunk_buf, &mut hash)\n            .unwrap();\n\n        assert_eq!(Sha1::digest(update).as_slice(), hash);\n    }\n}\n"
  },
  {
    "path": "embassy-boot/src/firmware_updater/mod.rs",
    "content": "mod asynch;\nmod blocking;\n\npub use asynch::{FirmwareState, FirmwareUpdater};\npub use blocking::{BlockingFirmwareState, BlockingFirmwareUpdater};\nuse embedded_storage::nor_flash::{NorFlashError, NorFlashErrorKind};\n\n/// Firmware updater flash configuration holding the two flashes used by the updater\n///\n/// If only a single flash is actually used, then that flash should be partitioned into two partitions before use.\n/// The easiest way to do this is to use [`FirmwareUpdaterConfig::from_linkerfile`] or [`FirmwareUpdaterConfig::from_linkerfile_blocking`] which will partition\n/// the provided flash according to symbols defined in the linkerfile.\npub struct FirmwareUpdaterConfig<DFU, STATE> {\n    /// The dfu flash partition\n    pub dfu: DFU,\n    /// The state flash partition\n    pub state: STATE,\n}\n\n/// Errors returned by FirmwareUpdater\n#[derive(Debug)]\npub enum FirmwareUpdaterError {\n    /// Error from flash.\n    Flash(NorFlashErrorKind),\n    /// Signature errors.\n    Signature(signature::Error),\n    /// Bad state.\n    BadState,\n}\n\n#[cfg(feature = \"defmt\")]\nimpl defmt::Format for FirmwareUpdaterError {\n    fn format(&self, fmt: defmt::Formatter) {\n        match self {\n            FirmwareUpdaterError::Flash(_) => defmt::write!(fmt, \"FirmwareUpdaterError::Flash(_)\"),\n            FirmwareUpdaterError::Signature(_) => defmt::write!(fmt, \"FirmwareUpdaterError::Signature(_)\"),\n            FirmwareUpdaterError::BadState => defmt::write!(fmt, \"FirmwareUpdaterError::BadState\"),\n        }\n    }\n}\n\nimpl<E> From<E> for FirmwareUpdaterError\nwhere\n    E: NorFlashError,\n{\n    fn from(error: E) -> Self {\n        FirmwareUpdaterError::Flash(error.kind())\n    }\n}\n"
  },
  {
    "path": "embassy-boot/src/fmt.rs",
    "content": "#![macro_use]\n#![allow(unused)]\n\nuse core::fmt::{Debug, Display, LowerHex};\n\n#[cfg(all(feature = \"defmt\", feature = \"log\"))]\ncompile_error!(\"You may not enable both `defmt` and `log` features.\");\n\n#[collapse_debuginfo(yes)]\nmacro_rules! assert {\n    ($($x:tt)*) => {\n        {\n            #[cfg(not(feature = \"defmt\"))]\n            ::core::assert!($($x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::assert!($($x)*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! assert_eq {\n    ($($x:tt)*) => {\n        {\n            #[cfg(not(feature = \"defmt\"))]\n            ::core::assert_eq!($($x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::assert_eq!($($x)*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! assert_ne {\n    ($($x:tt)*) => {\n        {\n            #[cfg(not(feature = \"defmt\"))]\n            ::core::assert_ne!($($x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::assert_ne!($($x)*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! debug_assert {\n    ($($x:tt)*) => {\n        {\n            #[cfg(not(feature = \"defmt\"))]\n            ::core::debug_assert!($($x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::debug_assert!($($x)*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! debug_assert_eq {\n    ($($x:tt)*) => {\n        {\n            #[cfg(not(feature = \"defmt\"))]\n            ::core::debug_assert_eq!($($x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::debug_assert_eq!($($x)*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! debug_assert_ne {\n    ($($x:tt)*) => {\n        {\n            #[cfg(not(feature = \"defmt\"))]\n            ::core::debug_assert_ne!($($x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::debug_assert_ne!($($x)*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! todo {\n    ($($x:tt)*) => {\n        {\n            #[cfg(not(feature = \"defmt\"))]\n            ::core::todo!($($x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::todo!($($x)*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! unreachable {\n    ($($x:tt)*) => {\n        {\n            #[cfg(not(feature = \"defmt\"))]\n            ::core::unreachable!($($x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::unreachable!($($x)*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! panic {\n    ($($x:tt)*) => {\n        {\n            #[cfg(not(feature = \"defmt\"))]\n            ::core::panic!($($x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::panic!($($x)*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! trace {\n    ($s:literal $(, $x:expr)* $(,)?) => {\n        {\n            #[cfg(feature = \"log\")]\n            ::log::trace!($s $(, $x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::trace!($s $(, $x)*);\n            #[cfg(not(any(feature = \"log\", feature=\"defmt\")))]\n            let _ = ($( & $x ),*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! debug {\n    ($s:literal $(, $x:expr)* $(,)?) => {\n        {\n            #[cfg(feature = \"log\")]\n            ::log::debug!($s $(, $x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::debug!($s $(, $x)*);\n            #[cfg(not(any(feature = \"log\", feature=\"defmt\")))]\n            let _ = ($( & $x ),*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! info {\n    ($s:literal $(, $x:expr)* $(,)?) => {\n        {\n            #[cfg(feature = \"log\")]\n            ::log::info!($s $(, $x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::info!($s $(, $x)*);\n            #[cfg(not(any(feature = \"log\", feature=\"defmt\")))]\n            let _ = ($( & $x ),*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! warn {\n    ($s:literal $(, $x:expr)* $(,)?) => {\n        {\n            #[cfg(feature = \"log\")]\n            ::log::warn!($s $(, $x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::warn!($s $(, $x)*);\n            #[cfg(not(any(feature = \"log\", feature=\"defmt\")))]\n            let _ = ($( & $x ),*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! error {\n    ($s:literal $(, $x:expr)* $(,)?) => {\n        {\n            #[cfg(feature = \"log\")]\n            ::log::error!($s $(, $x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::error!($s $(, $x)*);\n            #[cfg(not(any(feature = \"log\", feature=\"defmt\")))]\n            let _ = ($( & $x ),*);\n        }\n    };\n}\n\n#[cfg(feature = \"defmt\")]\n#[collapse_debuginfo(yes)]\nmacro_rules! unwrap {\n    ($($x:tt)*) => {\n        ::defmt::unwrap!($($x)*)\n    };\n}\n\n#[cfg(not(feature = \"defmt\"))]\n#[collapse_debuginfo(yes)]\nmacro_rules! unwrap {\n    ($arg:expr) => {\n        match $crate::fmt::Try::into_result($arg) {\n            ::core::result::Result::Ok(t) => t,\n            ::core::result::Result::Err(e) => {\n                ::core::panic!(\"unwrap of `{}` failed: {:?}\", ::core::stringify!($arg), e);\n            }\n        }\n    };\n    ($arg:expr, $($msg:expr),+ $(,)? ) => {\n        match $crate::fmt::Try::into_result($arg) {\n            ::core::result::Result::Ok(t) => t,\n            ::core::result::Result::Err(e) => {\n                ::core::panic!(\"unwrap of `{}` failed: {}: {:?}\", ::core::stringify!($arg), ::core::format_args!($($msg,)*), e);\n            }\n        }\n    }\n}\n\n#[derive(Debug, Copy, Clone, Eq, PartialEq)]\npub struct NoneError;\n\npub trait Try {\n    type Ok;\n    type Error;\n    fn into_result(self) -> Result<Self::Ok, Self::Error>;\n}\n\nimpl<T> Try for Option<T> {\n    type Ok = T;\n    type Error = NoneError;\n\n    #[inline]\n    fn into_result(self) -> Result<T, NoneError> {\n        self.ok_or(NoneError)\n    }\n}\n\nimpl<T, E> Try for Result<T, E> {\n    type Ok = T;\n    type Error = E;\n\n    #[inline]\n    fn into_result(self) -> Self {\n        self\n    }\n}\n\npub(crate) struct Bytes<'a>(pub &'a [u8]);\n\nimpl<'a> Debug for Bytes<'a> {\n    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {\n        write!(f, \"{:#02x?}\", self.0)\n    }\n}\n\nimpl<'a> Display for Bytes<'a> {\n    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {\n        write!(f, \"{:#02x?}\", self.0)\n    }\n}\n\nimpl<'a> LowerHex for Bytes<'a> {\n    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {\n        write!(f, \"{:#02x?}\", self.0)\n    }\n}\n\n#[cfg(feature = \"defmt\")]\nimpl<'a> defmt::Format for Bytes<'a> {\n    fn format(&self, fmt: defmt::Formatter) {\n        defmt::write!(fmt, \"{:02x}\", self.0)\n    }\n}\n"
  },
  {
    "path": "embassy-boot/src/lib.rs",
    "content": "#![no_std]\n#![allow(async_fn_in_trait)]\n#![allow(unsafe_op_in_unsafe_fn)]\n#![warn(missing_docs)]\n#![doc = include_str!(\"../README.md\")]\n\n//! ## Feature flags\n#![doc = document_features::document_features!(feature_label = r#\"<span class=\"stab portability\"><code>{feature}</code></span>\"#)]\n\nmod fmt;\n\nmod boot_loader;\nmod digest_adapters;\nmod firmware_updater;\n#[cfg(test)]\nmod mem_flash;\n#[cfg(test)]\nmod test_flash;\n\n// The expected value of the flash after an erase\n// TODO: Use the value provided by NorFlash when available\n#[cfg(not(feature = \"flash-erase-zero\"))]\npub(crate) const STATE_ERASE_VALUE: u8 = 0xFF;\n#[cfg(feature = \"flash-erase-zero\")]\npub(crate) const STATE_ERASE_VALUE: u8 = 0x00;\n\npub use boot_loader::{BootError, BootLoader, BootLoaderConfig};\npub use firmware_updater::{\n    BlockingFirmwareState, BlockingFirmwareUpdater, FirmwareState, FirmwareUpdater, FirmwareUpdaterConfig,\n    FirmwareUpdaterError,\n};\n\npub(crate) const REVERT_MAGIC: u8 = 0xC0;\npub(crate) const BOOT_MAGIC: u8 = 0xD0;\npub(crate) const SWAP_MAGIC: u8 = 0xF0;\npub(crate) const DFU_DETACH_MAGIC: u8 = 0xE0;\n\n/// The state of the bootloader after running prepare.\n#[derive(PartialEq, Eq, Debug)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub enum State {\n    /// Bootloader is ready to boot the active partition.\n    Boot,\n    /// Bootloader has swapped the active partition with the dfu partition and will attempt boot.\n    Swap,\n    /// Bootloader has reverted the active partition with the dfu partition and will attempt boot.\n    Revert,\n    /// Application has received a request to reboot into DFU mode to apply an update.\n    DfuDetach,\n}\n\nimpl<T> From<T> for State\nwhere\n    T: AsRef<[u8]>,\n{\n    fn from(magic: T) -> State {\n        let magic = magic.as_ref();\n        if !magic.iter().any(|&b| b != SWAP_MAGIC) {\n            State::Swap\n        } else if !magic.iter().any(|&b| b != REVERT_MAGIC) {\n            State::Revert\n        } else if !magic.iter().any(|&b| b != DFU_DETACH_MAGIC) {\n            State::DfuDetach\n        } else {\n            State::Boot\n        }\n    }\n}\n\n/// Buffer aligned to 32 byte boundary, largest known alignment requirement for embassy-boot.\n#[repr(align(32))]\npub struct AlignedBuffer<const N: usize>(pub [u8; N]);\n\nimpl<const N: usize> AsRef<[u8]> for AlignedBuffer<N> {\n    fn as_ref(&self) -> &[u8] {\n        &self.0\n    }\n}\n\nimpl<const N: usize> AsMut<[u8]> for AlignedBuffer<N> {\n    fn as_mut(&mut self) -> &mut [u8] {\n        &mut self.0\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    #![allow(unused_imports)]\n\n    use embedded_storage::nor_flash::{NorFlash, ReadNorFlash};\n    use embedded_storage_async::nor_flash::NorFlash as AsyncNorFlash;\n    use futures::executor::block_on;\n\n    use super::*;\n    use crate::boot_loader::BootLoaderConfig;\n    use crate::firmware_updater::FirmwareUpdaterConfig;\n    use crate::mem_flash::MemFlash;\n    use crate::test_flash::{AsyncTestFlash, BlockingTestFlash};\n\n    /*\n    #[test]\n    fn test_bad_magic() {\n        let mut flash = MemFlash([0xff; 131072]);\n        let mut flash = SingleFlashConfig::new(&mut flash);\n\n        let mut bootloader = BootLoader::<4096>::new(ACTIVE, DFU, STATE);\n\n        assert_eq!(\n            bootloader.prepare_boot(&mut flash),\n            Err(BootError::BadMagic)\n        );\n    }\n    */\n\n    #[test]\n    fn test_boot_state() {\n        let flash = BlockingTestFlash::new(BootLoaderConfig {\n            active: MemFlash::<57344, 4096, 4>::default(),\n            dfu: MemFlash::<61440, 4096, 4>::default(),\n            state: MemFlash::<4096, 4096, 4>::default(),\n        });\n\n        flash.state().write(0, &[BOOT_MAGIC; 4]).unwrap();\n\n        let mut bootloader = BootLoader::new(BootLoaderConfig {\n            active: flash.active(),\n            dfu: flash.dfu(),\n            state: flash.state(),\n        });\n\n        let mut page = [0; 4096];\n        assert_eq!(State::Boot, bootloader.prepare_boot(&mut page).unwrap());\n    }\n\n    #[test]\n    #[cfg(not(feature = \"_verify\"))]\n    fn test_swap_state() {\n        const FIRMWARE_SIZE: usize = 57344;\n        let flash = AsyncTestFlash::new(BootLoaderConfig {\n            active: MemFlash::<FIRMWARE_SIZE, 4096, 4>::default(),\n            dfu: MemFlash::<61440, 4096, 4>::default(),\n            state: MemFlash::<4096, 4096, 4>::default(),\n        });\n\n        const ORIGINAL: [u8; FIRMWARE_SIZE] = [0x55; FIRMWARE_SIZE];\n        const UPDATE: [u8; FIRMWARE_SIZE] = [0xAA; FIRMWARE_SIZE];\n        let mut aligned = [0; 4];\n\n        block_on(flash.active().erase(0, ORIGINAL.len() as u32)).unwrap();\n        block_on(flash.active().write(0, &ORIGINAL)).unwrap();\n\n        let mut updater = FirmwareUpdater::new(\n            FirmwareUpdaterConfig {\n                dfu: flash.dfu(),\n                state: flash.state(),\n            },\n            &mut aligned,\n        );\n        block_on(updater.write_firmware(0, &UPDATE)).unwrap();\n        block_on(updater.mark_updated()).unwrap();\n\n        // Writing after marking updated is not allowed until marked as booted.\n        let res: Result<(), FirmwareUpdaterError> = block_on(updater.write_firmware(0, &UPDATE));\n        assert!(matches!(res, Err::<(), _>(FirmwareUpdaterError::BadState)));\n\n        let flash = flash.into_blocking();\n        let mut bootloader = BootLoader::new(BootLoaderConfig {\n            active: flash.active(),\n            dfu: flash.dfu(),\n            state: flash.state(),\n        });\n\n        let mut page = [0; 1024];\n        assert_eq!(State::Swap, bootloader.prepare_boot(&mut page).unwrap());\n\n        let mut read_buf = [0; FIRMWARE_SIZE];\n        flash.active().read(0, &mut read_buf).unwrap();\n        assert_eq!(UPDATE, read_buf);\n        // First DFU page is untouched\n        flash.dfu().read(4096, &mut read_buf).unwrap();\n        assert_eq!(ORIGINAL, read_buf);\n\n        // Running again should cause a revert\n        assert_eq!(State::Swap, bootloader.prepare_boot(&mut page).unwrap());\n\n        // Next time we know it was reverted\n        assert_eq!(State::Revert, bootloader.prepare_boot(&mut page).unwrap());\n\n        let mut read_buf = [0; FIRMWARE_SIZE];\n        flash.active().read(0, &mut read_buf).unwrap();\n        assert_eq!(ORIGINAL, read_buf);\n        // Last DFU page is untouched\n        flash.dfu().read(0, &mut read_buf).unwrap();\n        assert_eq!(UPDATE, read_buf);\n\n        // Mark as booted\n        let flash = flash.into_async();\n        let mut updater = FirmwareUpdater::new(\n            FirmwareUpdaterConfig {\n                dfu: flash.dfu(),\n                state: flash.state(),\n            },\n            &mut aligned,\n        );\n        block_on(updater.mark_booted()).unwrap();\n\n        let flash = flash.into_blocking();\n        let mut bootloader = BootLoader::new(BootLoaderConfig {\n            active: flash.active(),\n            dfu: flash.dfu(),\n            state: flash.state(),\n        });\n        assert_eq!(State::Boot, bootloader.prepare_boot(&mut page).unwrap());\n    }\n\n    #[test]\n    #[cfg(not(feature = \"_verify\"))]\n    fn test_swap_state_active_page_biggest() {\n        const FIRMWARE_SIZE: usize = 12288;\n        let flash = AsyncTestFlash::new(BootLoaderConfig {\n            active: MemFlash::<12288, 4096, 8>::random(),\n            dfu: MemFlash::<16384, 2048, 8>::random(),\n            state: MemFlash::<2048, 128, 4>::random(),\n        });\n\n        const ORIGINAL: [u8; FIRMWARE_SIZE] = [0x55; FIRMWARE_SIZE];\n        const UPDATE: [u8; FIRMWARE_SIZE] = [0xAA; FIRMWARE_SIZE];\n        let mut aligned = [0; 4];\n\n        block_on(flash.active().erase(0, ORIGINAL.len() as u32)).unwrap();\n        block_on(flash.active().write(0, &ORIGINAL)).unwrap();\n\n        let mut updater = FirmwareUpdater::new(\n            FirmwareUpdaterConfig {\n                dfu: flash.dfu(),\n                state: flash.state(),\n            },\n            &mut aligned,\n        );\n        block_on(updater.write_firmware(0, &UPDATE)).unwrap();\n        block_on(updater.mark_updated()).unwrap();\n\n        let flash = flash.into_blocking();\n        let mut bootloader = BootLoader::new(BootLoaderConfig {\n            active: flash.active(),\n            dfu: flash.dfu(),\n            state: flash.state(),\n        });\n\n        let mut page = [0; 4096];\n        assert_eq!(State::Swap, bootloader.prepare_boot(&mut page).unwrap());\n\n        let mut read_buf = [0; FIRMWARE_SIZE];\n        flash.active().read(0, &mut read_buf).unwrap();\n        assert_eq!(UPDATE, read_buf);\n        // First DFU page is untouched\n        flash.dfu().read(4096, &mut read_buf).unwrap();\n        assert_eq!(ORIGINAL, read_buf);\n    }\n\n    #[test]\n    #[cfg(not(feature = \"_verify\"))]\n    fn test_swap_state_dfu_page_biggest() {\n        const FIRMWARE_SIZE: usize = 12288;\n        let flash = AsyncTestFlash::new(BootLoaderConfig {\n            active: MemFlash::<FIRMWARE_SIZE, 2048, 4>::random(),\n            dfu: MemFlash::<16384, 4096, 8>::random(),\n            state: MemFlash::<2048, 128, 4>::random(),\n        });\n\n        const ORIGINAL: [u8; FIRMWARE_SIZE] = [0x55; FIRMWARE_SIZE];\n        const UPDATE: [u8; FIRMWARE_SIZE] = [0xAA; FIRMWARE_SIZE];\n        let mut aligned = [0; 4];\n\n        block_on(flash.active().erase(0, ORIGINAL.len() as u32)).unwrap();\n        block_on(flash.active().write(0, &ORIGINAL)).unwrap();\n\n        let mut updater = FirmwareUpdater::new(\n            FirmwareUpdaterConfig {\n                dfu: flash.dfu(),\n                state: flash.state(),\n            },\n            &mut aligned,\n        );\n        block_on(updater.write_firmware(0, &UPDATE)).unwrap();\n        block_on(updater.mark_updated()).unwrap();\n\n        let flash = flash.into_blocking();\n        let mut bootloader = BootLoader::new(BootLoaderConfig {\n            active: flash.active(),\n            dfu: flash.dfu(),\n            state: flash.state(),\n        });\n        let mut page = [0; 4096];\n        assert_eq!(State::Swap, bootloader.prepare_boot(&mut page).unwrap());\n\n        let mut read_buf = [0; FIRMWARE_SIZE];\n        flash.active().read(0, &mut read_buf).unwrap();\n        assert_eq!(UPDATE, read_buf);\n        // First DFU page is untouched\n        flash.dfu().read(4096, &mut read_buf).unwrap();\n        assert_eq!(ORIGINAL, read_buf);\n    }\n\n    #[test]\n    #[cfg(feature = \"_verify\")]\n    fn test_verify() {\n        // The following key setup is based on:\n        // https://docs.rs/ed25519-dalek/latest/ed25519_dalek/#example\n\n        use ed25519_dalek::{Digest, Sha512, Signature, Signer, SigningKey, VerifyingKey};\n        use rand::rngs::OsRng;\n\n        let mut csprng = OsRng {};\n        let keypair = SigningKey::generate(&mut csprng);\n\n        let firmware: &[u8] = b\"This are bytes that would otherwise be firmware bytes for DFU.\";\n        let mut digest = Sha512::new();\n        digest.update(&firmware);\n        let message = digest.finalize();\n        let signature: Signature = keypair.sign(&message);\n\n        let public_key = keypair.verifying_key();\n\n        // Setup flash\n        let flash = BlockingTestFlash::new(BootLoaderConfig {\n            active: MemFlash::<0, 0, 0>::default(),\n            dfu: MemFlash::<4096, 4096, 4>::default(),\n            state: MemFlash::<4096, 4096, 4>::default(),\n        });\n\n        let firmware_len = firmware.len();\n\n        let mut write_buf = [0; 4096];\n        write_buf[0..firmware_len].copy_from_slice(firmware);\n        flash.dfu().write(0, &write_buf).unwrap();\n\n        // On with the test\n        let flash = flash.into_async();\n        let mut aligned = [0; 4];\n        let mut updater = FirmwareUpdater::new(\n            FirmwareUpdaterConfig {\n                dfu: flash.dfu(),\n                state: flash.state(),\n            },\n            &mut aligned,\n        );\n\n        assert!(\n            block_on(updater.verify_and_mark_updated(\n                &public_key.to_bytes(),\n                &signature.to_bytes(),\n                firmware_len as u32,\n            ))\n            .is_ok()\n        );\n    }\n}\n"
  },
  {
    "path": "embassy-boot/src/mem_flash.rs",
    "content": "#![allow(unused)]\n\nuse core::ops::{Bound, Range, RangeBounds};\n\nuse embedded_storage::nor_flash::{ErrorType, NorFlash, NorFlashError, NorFlashErrorKind, ReadNorFlash};\nuse embedded_storage_async::nor_flash::{NorFlash as AsyncNorFlash, ReadNorFlash as AsyncReadNorFlash};\n\npub struct MemFlash<const SIZE: usize, const ERASE_SIZE: usize, const WRITE_SIZE: usize> {\n    pub mem: [u8; SIZE],\n    pub pending_write_successes: Option<usize>,\n}\n\n#[derive(Debug)]\npub struct MemFlashError;\n\nimpl<const SIZE: usize, const ERASE_SIZE: usize, const WRITE_SIZE: usize> MemFlash<SIZE, ERASE_SIZE, WRITE_SIZE> {\n    pub const fn new(fill: u8) -> Self {\n        Self {\n            mem: [fill; SIZE],\n            pending_write_successes: None,\n        }\n    }\n\n    #[cfg(test)]\n    pub fn random() -> Self {\n        let mut mem = [0; SIZE];\n        for byte in mem.iter_mut() {\n            *byte = rand::random::<u8>();\n        }\n        Self {\n            mem,\n            pending_write_successes: None,\n        }\n    }\n\n    fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), MemFlashError> {\n        let len = bytes.len();\n        bytes.copy_from_slice(&self.mem[offset as usize..offset as usize + len]);\n        Ok(())\n    }\n\n    fn write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), MemFlashError> {\n        let offset = offset as usize;\n        assert!(bytes.len() % WRITE_SIZE == 0);\n        assert!(offset % WRITE_SIZE == 0);\n        assert!(offset + bytes.len() <= SIZE);\n\n        if let Some(pending_successes) = self.pending_write_successes {\n            if pending_successes > 0 {\n                self.pending_write_successes = Some(pending_successes - 1);\n            } else {\n                return Err(MemFlashError);\n            }\n        }\n\n        for ((offset, mem_byte), new_byte) in self\n            .mem\n            .iter_mut()\n            .enumerate()\n            .skip(offset)\n            .take(bytes.len())\n            .zip(bytes)\n        {\n            assert_eq!(0xFF, *mem_byte, \"Offset {} is not erased\", offset);\n            *mem_byte = *new_byte;\n        }\n\n        Ok(())\n    }\n\n    fn erase(&mut self, from: u32, to: u32) -> Result<(), MemFlashError> {\n        let from = from as usize;\n        let to = to as usize;\n        assert!(from % ERASE_SIZE == 0);\n        assert!(to % ERASE_SIZE == 0, \"To: {}, erase size: {}\", to, ERASE_SIZE);\n        for i in from..to {\n            self.mem[i] = 0xFF;\n        }\n        Ok(())\n    }\n\n    pub fn program(&mut self, offset: u32, bytes: &[u8]) -> Result<(), MemFlashError> {\n        let offset = offset as usize;\n        assert!(bytes.len() % WRITE_SIZE == 0);\n        assert!(offset % WRITE_SIZE == 0);\n        assert!(offset + bytes.len() <= SIZE);\n\n        self.mem[offset..offset + bytes.len()].copy_from_slice(bytes);\n\n        Ok(())\n    }\n}\n\nimpl<const SIZE: usize, const ERASE_SIZE: usize, const WRITE_SIZE: usize> Default\n    for MemFlash<SIZE, ERASE_SIZE, WRITE_SIZE>\n{\n    fn default() -> Self {\n        Self::new(0xFF)\n    }\n}\n\nimpl<const SIZE: usize, const ERASE_SIZE: usize, const WRITE_SIZE: usize> ErrorType\n    for MemFlash<SIZE, ERASE_SIZE, WRITE_SIZE>\n{\n    type Error = MemFlashError;\n}\n\nimpl NorFlashError for MemFlashError {\n    fn kind(&self) -> NorFlashErrorKind {\n        NorFlashErrorKind::Other\n    }\n}\n\nimpl<const SIZE: usize, const ERASE_SIZE: usize, const WRITE_SIZE: usize> ReadNorFlash\n    for MemFlash<SIZE, ERASE_SIZE, WRITE_SIZE>\n{\n    const READ_SIZE: usize = 1;\n\n    fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Self::Error> {\n        self.read(offset, bytes)\n    }\n\n    fn capacity(&self) -> usize {\n        SIZE\n    }\n}\n\nimpl<const SIZE: usize, const ERASE_SIZE: usize, const WRITE_SIZE: usize> NorFlash\n    for MemFlash<SIZE, ERASE_SIZE, WRITE_SIZE>\n{\n    const WRITE_SIZE: usize = WRITE_SIZE;\n    const ERASE_SIZE: usize = ERASE_SIZE;\n\n    fn write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Self::Error> {\n        self.write(offset, bytes)\n    }\n\n    fn erase(&mut self, from: u32, to: u32) -> Result<(), Self::Error> {\n        self.erase(from, to)\n    }\n}\n\nimpl<const SIZE: usize, const ERASE_SIZE: usize, const WRITE_SIZE: usize> AsyncReadNorFlash\n    for MemFlash<SIZE, ERASE_SIZE, WRITE_SIZE>\n{\n    const READ_SIZE: usize = 1;\n\n    async fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Self::Error> {\n        self.read(offset, bytes)\n    }\n\n    fn capacity(&self) -> usize {\n        SIZE\n    }\n}\n\nimpl<const SIZE: usize, const ERASE_SIZE: usize, const WRITE_SIZE: usize> AsyncNorFlash\n    for MemFlash<SIZE, ERASE_SIZE, WRITE_SIZE>\n{\n    const WRITE_SIZE: usize = WRITE_SIZE;\n    const ERASE_SIZE: usize = ERASE_SIZE;\n\n    async fn write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Self::Error> {\n        self.write(offset, bytes)\n    }\n\n    async fn erase(&mut self, from: u32, to: u32) -> Result<(), Self::Error> {\n        self.erase(from, to)\n    }\n}\n"
  },
  {
    "path": "embassy-boot/src/test_flash/asynch.rs",
    "content": "use embassy_embedded_hal::flash::partition::Partition;\nuse embassy_sync::blocking_mutex::raw::NoopRawMutex;\nuse embassy_sync::mutex::Mutex;\nuse embedded_storage_async::nor_flash::NorFlash;\n\nuse crate::BootLoaderConfig;\n\npub struct AsyncTestFlash<ACTIVE, DFU, STATE>\nwhere\n    ACTIVE: NorFlash,\n    DFU: NorFlash,\n    STATE: NorFlash,\n{\n    active: Mutex<NoopRawMutex, ACTIVE>,\n    dfu: Mutex<NoopRawMutex, DFU>,\n    state: Mutex<NoopRawMutex, STATE>,\n}\n\nimpl<ACTIVE, DFU, STATE> AsyncTestFlash<ACTIVE, DFU, STATE>\nwhere\n    ACTIVE: NorFlash,\n    DFU: NorFlash,\n    STATE: NorFlash,\n{\n    pub fn new(config: BootLoaderConfig<ACTIVE, DFU, STATE>) -> Self {\n        Self {\n            active: Mutex::new(config.active),\n            dfu: Mutex::new(config.dfu),\n            state: Mutex::new(config.state),\n        }\n    }\n\n    pub fn active(&self) -> Partition<NoopRawMutex, ACTIVE> {\n        Self::create_partition(&self.active)\n    }\n\n    pub fn dfu(&self) -> Partition<NoopRawMutex, DFU> {\n        Self::create_partition(&self.dfu)\n    }\n\n    pub fn state(&self) -> Partition<NoopRawMutex, STATE> {\n        Self::create_partition(&self.state)\n    }\n\n    fn create_partition<T: NorFlash>(mutex: &Mutex<NoopRawMutex, T>) -> Partition<NoopRawMutex, T> {\n        Partition::new(mutex, 0, unwrap!(mutex.try_lock()).capacity() as u32)\n    }\n}\n\nimpl<ACTIVE, DFU, STATE> AsyncTestFlash<ACTIVE, DFU, STATE>\nwhere\n    ACTIVE: NorFlash + embedded_storage::nor_flash::NorFlash,\n    DFU: NorFlash + embedded_storage::nor_flash::NorFlash,\n    STATE: NorFlash + embedded_storage::nor_flash::NorFlash,\n{\n    pub fn into_blocking(self) -> super::BlockingTestFlash<ACTIVE, DFU, STATE> {\n        let config = BootLoaderConfig {\n            active: self.active.into_inner(),\n            dfu: self.dfu.into_inner(),\n            state: self.state.into_inner(),\n        };\n        super::BlockingTestFlash::new(config)\n    }\n}\n"
  },
  {
    "path": "embassy-boot/src/test_flash/blocking.rs",
    "content": "use core::cell::RefCell;\n\nuse embassy_embedded_hal::flash::partition::BlockingPartition;\nuse embassy_sync::blocking_mutex::Mutex;\nuse embassy_sync::blocking_mutex::raw::NoopRawMutex;\nuse embedded_storage::nor_flash::NorFlash;\n\nuse crate::BootLoaderConfig;\n\npub struct BlockingTestFlash<ACTIVE, DFU, STATE>\nwhere\n    ACTIVE: NorFlash,\n    DFU: NorFlash,\n    STATE: NorFlash,\n{\n    active: Mutex<NoopRawMutex, RefCell<ACTIVE>>,\n    dfu: Mutex<NoopRawMutex, RefCell<DFU>>,\n    state: Mutex<NoopRawMutex, RefCell<STATE>>,\n}\n\nimpl<ACTIVE, DFU, STATE> BlockingTestFlash<ACTIVE, DFU, STATE>\nwhere\n    ACTIVE: NorFlash,\n    DFU: NorFlash,\n    STATE: NorFlash,\n{\n    pub fn new(config: BootLoaderConfig<ACTIVE, DFU, STATE>) -> Self {\n        Self {\n            active: Mutex::new(RefCell::new(config.active)),\n            dfu: Mutex::new(RefCell::new(config.dfu)),\n            state: Mutex::new(RefCell::new(config.state)),\n        }\n    }\n\n    pub fn active(&self) -> BlockingPartition<NoopRawMutex, ACTIVE> {\n        Self::create_partition(&self.active)\n    }\n\n    pub fn dfu(&self) -> BlockingPartition<NoopRawMutex, DFU> {\n        Self::create_partition(&self.dfu)\n    }\n\n    pub fn state(&self) -> BlockingPartition<NoopRawMutex, STATE> {\n        Self::create_partition(&self.state)\n    }\n\n    pub fn create_partition<T: NorFlash>(\n        mutex: &Mutex<NoopRawMutex, RefCell<T>>,\n    ) -> BlockingPartition<NoopRawMutex, T> {\n        BlockingPartition::new(mutex, 0, mutex.lock(|f| f.borrow().capacity()) as u32)\n    }\n}\n\nimpl<ACTIVE, DFU, STATE> BlockingTestFlash<ACTIVE, DFU, STATE>\nwhere\n    ACTIVE: NorFlash + embedded_storage_async::nor_flash::NorFlash,\n    DFU: NorFlash + embedded_storage_async::nor_flash::NorFlash,\n    STATE: NorFlash + embedded_storage_async::nor_flash::NorFlash,\n{\n    pub fn into_async(self) -> super::AsyncTestFlash<ACTIVE, DFU, STATE> {\n        let config = BootLoaderConfig {\n            active: self.active.into_inner().into_inner(),\n            dfu: self.dfu.into_inner().into_inner(),\n            state: self.state.into_inner().into_inner(),\n        };\n        super::AsyncTestFlash::new(config)\n    }\n}\n"
  },
  {
    "path": "embassy-boot/src/test_flash/mod.rs",
    "content": "mod asynch;\nmod blocking;\n\npub(crate) use asynch::AsyncTestFlash;\npub(crate) use blocking::BlockingTestFlash;\n"
  },
  {
    "path": "embassy-boot-nrf/CHANGELOG.md",
    "content": "# Changelog\n\nAll notable changes to this project will be documented in this file.\n\nThe format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),\nand this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).\n\n<!-- next-header -->\n## Unreleased - ReleaseDate\n\n## 0.11.0 - 2026-03-10\n\n- Update embassy-sync to 0.8.0\n- Update embassy-nrf to 0.10.0\n- Update embassy-boot to 0.7.0\n\n## 0.10.0 - 2025-12-15\n\n- Bumped embassy-nrf to 0.9.0\n\n## 0.9.0 - 2025-09-30\n\n- Bumped embassy-nrf to 0.8.0\n\n## 0.8.0 - 2025-08-26\n\n## 0.1.1 - 2025-08-15\n\n- First release with changelog.\n"
  },
  {
    "path": "embassy-boot-nrf/Cargo.toml",
    "content": "[package]\nedition = \"2024\"\nname = \"embassy-boot-nrf\"\nversion = \"0.11.0\"\ndescription = \"Bootloader lib for nRF chips\"\nlicense = \"MIT OR Apache-2.0\"\nrepository = \"https://github.com/embassy-rs/embassy\"\ndocumentation = \"https://docs.embassy.dev/embassy-boot-nrf\"\ncategories = [\n    \"embedded\",\n    \"no-std\",\n    \"asynchronous\",\n]\n\n[package.metadata.embassy]\nbuild = [\n    {target = \"thumbv7em-none-eabi\", features = [\"embassy-nrf/nrf52840\"]},\n    {target = \"thumbv8m.main-none-eabihf\", features = [\"embassy-nrf/nrf5340-app-s\"]},\n    {target = \"thumbv8m.main-none-eabihf\", features = [\"embassy-nrf/nrf9160-ns\"]},\n    {target = \"thumbv8m.main-none-eabihf\", features = [\"embassy-nrf/nrf9120-ns\"]},\n    {target = \"thumbv8m.main-none-eabihf\", features = [\"embassy-nrf/nrf9151-ns\"]},\n    {target = \"thumbv8m.main-none-eabihf\", features = [\"embassy-nrf/nrf9161-ns\"]},\n]\n\n\n[package.metadata.embassy_docs]\nsrc_base = \"https://github.com/embassy-rs/embassy/blob/embassy-boot-nrf-v$VERSION/embassy-boot-nrf/src/\"\nsrc_base_git = \"https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-boot-nrf/src/\"\nfeatures = [\"embassy-nrf/nrf52840\"]\ntarget = \"thumbv7em-none-eabi\"\n\n[lib]\n\n[dependencies]\ndefmt = { version = \"1.0.1\", optional = true }\nlog = { version = \"0.4.17\", optional = true }\n\nembassy-sync = { version = \"0.8.0\", path = \"../embassy-sync\" }\nembassy-nrf = { version = \"0.10.0\", path = \"../embassy-nrf\", default-features = false }\nembassy-boot = { version = \"0.7.0\", path = \"../embassy-boot\" }\ncortex-m = { version = \"0.7.6\" }\ncortex-m-rt = { version = \"0.7\" }\nembedded-storage = \"0.3.1\"\nembedded-storage-async = { version = \"0.4.1\" }\ncfg-if = \"1.0.0\"\n\nnrf-softdevice-mbr = { version = \"0.2.0\", optional = true }\n\n[features]\ndefmt = [\n    \"dep:defmt\",\n    \"embassy-boot/defmt\",\n    \"embassy-nrf/defmt\",\n]\nlog = [\"dep:log\"]\nsoftdevice = [\n    \"dep:nrf-softdevice-mbr\",\n]\n"
  },
  {
    "path": "embassy-boot-nrf/README.md",
    "content": "# embassy-boot-nrf\n\nAn [Embassy](https://embassy.dev) project.\n\nAn adaptation of `embassy-boot` for nRF.\n\n## Features\n\n- Load applications with or without the softdevice.\n- Configure bootloader partitions based on linker script.\n- Using watchdog timer to detect application failure.\n\n## Working with a SoftDevice\n\nWhen a SoftDevice is present, it handles starting the bootloader and the application as needed.\n\nThe SoftDevice architecture supports the bootloader via a configurable base address, referred to as `BOOTLOADERADDR`, in the application flash region. This address can be specified either:\n\n1. At the `MBR_BOOTLOADER_ADDR` location in flash memory (defined in `nrf_mbr.h`), or\n2. In the `UICR.NRFFW[0]` register.\n\nThe `UICR.NRFFW[0]` register is used only if `MBR_BOOTLOADER_ADDR` has its default value of `0xFFFFFFFF`. This bootloader relies on the latter approach.\n\nIn the `memory.x` linker script, there is a section `.uicr_bootloader_start_address` (origin `0x10001014`, length `0x4`) that stores the `BOOTLOADERADDR` value.\nEnsure that `__bootloader_start` is set to the origin address of the bootloader partition.\n\nWhen a bootloader is present, the SoftDevice forwards interrupts to it and executes the bootloader reset handler, defined in the bootloader's vector table at `BOOTLOADERADDR`.\n\nOnce the bootloader loads the application, the SoftDevice initiates the Application Reset Handler, defined in the application’s vector table at APP_CODE_BASE hardcoded in the SoftDevice.\nThe active partition's origin **must** match the `APP_CODE_BASE` value hardcoded within the SoftDevice. This value can be found in the release notes for each SoftDevice version.\n"
  },
  {
    "path": "embassy-boot-nrf/src/fmt.rs",
    "content": "#![macro_use]\n#![allow(unused)]\n\nuse core::fmt::{Debug, Display, LowerHex};\n\n#[cfg(all(feature = \"defmt\", feature = \"log\"))]\ncompile_error!(\"You may not enable both `defmt` and `log` features.\");\n\n#[collapse_debuginfo(yes)]\nmacro_rules! assert {\n    ($($x:tt)*) => {\n        {\n            #[cfg(not(feature = \"defmt\"))]\n            ::core::assert!($($x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::assert!($($x)*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! assert_eq {\n    ($($x:tt)*) => {\n        {\n            #[cfg(not(feature = \"defmt\"))]\n            ::core::assert_eq!($($x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::assert_eq!($($x)*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! assert_ne {\n    ($($x:tt)*) => {\n        {\n            #[cfg(not(feature = \"defmt\"))]\n            ::core::assert_ne!($($x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::assert_ne!($($x)*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! debug_assert {\n    ($($x:tt)*) => {\n        {\n            #[cfg(not(feature = \"defmt\"))]\n            ::core::debug_assert!($($x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::debug_assert!($($x)*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! debug_assert_eq {\n    ($($x:tt)*) => {\n        {\n            #[cfg(not(feature = \"defmt\"))]\n            ::core::debug_assert_eq!($($x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::debug_assert_eq!($($x)*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! debug_assert_ne {\n    ($($x:tt)*) => {\n        {\n            #[cfg(not(feature = \"defmt\"))]\n            ::core::debug_assert_ne!($($x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::debug_assert_ne!($($x)*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! todo {\n    ($($x:tt)*) => {\n        {\n            #[cfg(not(feature = \"defmt\"))]\n            ::core::todo!($($x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::todo!($($x)*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! unreachable {\n    ($($x:tt)*) => {\n        {\n            #[cfg(not(feature = \"defmt\"))]\n            ::core::unreachable!($($x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::unreachable!($($x)*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! panic {\n    ($($x:tt)*) => {\n        {\n            #[cfg(not(feature = \"defmt\"))]\n            ::core::panic!($($x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::panic!($($x)*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! trace {\n    ($s:literal $(, $x:expr)* $(,)?) => {\n        {\n            #[cfg(feature = \"log\")]\n            ::log::trace!($s $(, $x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::trace!($s $(, $x)*);\n            #[cfg(not(any(feature = \"log\", feature=\"defmt\")))]\n            let _ = ($( & $x ),*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! debug {\n    ($s:literal $(, $x:expr)* $(,)?) => {\n        {\n            #[cfg(feature = \"log\")]\n            ::log::debug!($s $(, $x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::debug!($s $(, $x)*);\n            #[cfg(not(any(feature = \"log\", feature=\"defmt\")))]\n            let _ = ($( & $x ),*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! info {\n    ($s:literal $(, $x:expr)* $(,)?) => {\n        {\n            #[cfg(feature = \"log\")]\n            ::log::info!($s $(, $x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::info!($s $(, $x)*);\n            #[cfg(not(any(feature = \"log\", feature=\"defmt\")))]\n            let _ = ($( & $x ),*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! warn {\n    ($s:literal $(, $x:expr)* $(,)?) => {\n        {\n            #[cfg(feature = \"log\")]\n            ::log::warn!($s $(, $x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::warn!($s $(, $x)*);\n            #[cfg(not(any(feature = \"log\", feature=\"defmt\")))]\n            let _ = ($( & $x ),*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! error {\n    ($s:literal $(, $x:expr)* $(,)?) => {\n        {\n            #[cfg(feature = \"log\")]\n            ::log::error!($s $(, $x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::error!($s $(, $x)*);\n            #[cfg(not(any(feature = \"log\", feature=\"defmt\")))]\n            let _ = ($( & $x ),*);\n        }\n    };\n}\n\n#[cfg(feature = \"defmt\")]\n#[collapse_debuginfo(yes)]\nmacro_rules! unwrap {\n    ($($x:tt)*) => {\n        ::defmt::unwrap!($($x)*)\n    };\n}\n\n#[cfg(not(feature = \"defmt\"))]\n#[collapse_debuginfo(yes)]\nmacro_rules! unwrap {\n    ($arg:expr) => {\n        match $crate::fmt::Try::into_result($arg) {\n            ::core::result::Result::Ok(t) => t,\n            ::core::result::Result::Err(e) => {\n                ::core::panic!(\"unwrap of `{}` failed: {:?}\", ::core::stringify!($arg), e);\n            }\n        }\n    };\n    ($arg:expr, $($msg:expr),+ $(,)? ) => {\n        match $crate::fmt::Try::into_result($arg) {\n            ::core::result::Result::Ok(t) => t,\n            ::core::result::Result::Err(e) => {\n                ::core::panic!(\"unwrap of `{}` failed: {}: {:?}\", ::core::stringify!($arg), ::core::format_args!($($msg,)*), e);\n            }\n        }\n    }\n}\n\n#[derive(Debug, Copy, Clone, Eq, PartialEq)]\npub struct NoneError;\n\npub trait Try {\n    type Ok;\n    type Error;\n    fn into_result(self) -> Result<Self::Ok, Self::Error>;\n}\n\nimpl<T> Try for Option<T> {\n    type Ok = T;\n    type Error = NoneError;\n\n    #[inline]\n    fn into_result(self) -> Result<T, NoneError> {\n        self.ok_or(NoneError)\n    }\n}\n\nimpl<T, E> Try for Result<T, E> {\n    type Ok = T;\n    type Error = E;\n\n    #[inline]\n    fn into_result(self) -> Self {\n        self\n    }\n}\n\npub(crate) struct Bytes<'a>(pub &'a [u8]);\n\nimpl<'a> Debug for Bytes<'a> {\n    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {\n        write!(f, \"{:#02x?}\", self.0)\n    }\n}\n\nimpl<'a> Display for Bytes<'a> {\n    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {\n        write!(f, \"{:#02x?}\", self.0)\n    }\n}\n\nimpl<'a> LowerHex for Bytes<'a> {\n    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {\n        write!(f, \"{:#02x?}\", self.0)\n    }\n}\n\n#[cfg(feature = \"defmt\")]\nimpl<'a> defmt::Format for Bytes<'a> {\n    fn format(&self, fmt: defmt::Formatter) {\n        defmt::write!(fmt, \"{:02x}\", self.0)\n    }\n}\n"
  },
  {
    "path": "embassy-boot-nrf/src/lib.rs",
    "content": "#![no_std]\n#![allow(unsafe_op_in_unsafe_fn)]\n#![warn(missing_docs)]\n#![doc = include_str!(\"../README.md\")]\nmod fmt;\n\npub use embassy_boot::{\n    AlignedBuffer, BlockingFirmwareState, BlockingFirmwareUpdater, BootError, BootLoaderConfig, FirmwareState,\n    FirmwareUpdater, FirmwareUpdaterConfig,\n};\nuse embassy_nrf::nvmc::PAGE_SIZE;\nuse embassy_nrf::{Peri, wdt};\nuse embedded_storage::nor_flash::{ErrorType, NorFlash, ReadNorFlash};\n\n/// A bootloader for nRF devices.\npub struct BootLoader<const BUFFER_SIZE: usize = PAGE_SIZE>;\n\nimpl<const BUFFER_SIZE: usize> BootLoader<BUFFER_SIZE> {\n    /// Inspect the bootloader state and perform actions required before booting, such as swapping firmware\n    pub fn prepare<ACTIVE: NorFlash, DFU: NorFlash, STATE: NorFlash>(\n        config: BootLoaderConfig<ACTIVE, DFU, STATE>,\n    ) -> Self {\n        if let Ok(loader) = Self::try_prepare::<ACTIVE, DFU, STATE>(config) {\n            loader\n        } else {\n            // Use explicit panic instead of .expect() to ensure this gets routed via defmt/etc.\n            // properly\n            panic!(\"Boot prepare error\")\n        }\n    }\n\n    /// Inspect the bootloader state and perform actions required before booting, such as swapping firmware\n    pub fn try_prepare<ACTIVE: NorFlash, DFU: NorFlash, STATE: NorFlash>(\n        config: BootLoaderConfig<ACTIVE, DFU, STATE>,\n    ) -> Result<Self, BootError> {\n        let mut aligned_buf = AlignedBuffer([0; BUFFER_SIZE]);\n        let mut boot = embassy_boot::BootLoader::new(config);\n        let _state = boot.prepare_boot(aligned_buf.as_mut())?;\n        Ok(Self)\n    }\n\n    /// Boots the application without softdevice mechanisms.\n    ///\n    /// # Safety\n    ///\n    /// This modifies the stack pointer and reset vector and will run code placed in the active partition.\n    #[cfg(not(feature = \"softdevice\"))]\n    pub unsafe fn load(self, start: u32) -> ! {\n        let mut p = cortex_m::Peripherals::steal();\n        p.SCB.invalidate_icache();\n        p.SCB.vtor.write(start);\n        cortex_m::asm::bootload(start as *const u32)\n    }\n\n    /// Boots the application assuming softdevice is present.\n    ///\n    /// # Safety\n    ///\n    /// This modifies the stack pointer and reset vector and will run code placed in the active partition.\n    #[cfg(feature = \"softdevice\")]\n    pub unsafe fn load(self, _app: u32) -> ! {\n        use nrf_softdevice_mbr as mbr;\n        const NRF_SUCCESS: u32 = 0;\n\n        // Address of softdevice which we'll forward interrupts to\n        let addr = 0x1000;\n        let mut cmd = mbr::sd_mbr_command_t {\n            command: mbr::NRF_MBR_COMMANDS_SD_MBR_COMMAND_IRQ_FORWARD_ADDRESS_SET,\n            params: mbr::sd_mbr_command_t__bindgen_ty_1 {\n                irq_forward_address_set: mbr::sd_mbr_command_irq_forward_address_set_t { address: addr },\n            },\n        };\n        let ret = mbr::sd_mbr_command(&mut cmd);\n        assert_eq!(ret, NRF_SUCCESS);\n\n        let msp = *(addr as *const u32);\n        let rv = *((addr + 4) as *const u32);\n\n        trace!(\"msp = {=u32:x}, rv = {=u32:x}\", msp, rv);\n\n        // These instructions perform the following operations:\n        //\n        // * Modify control register to use MSP as stack pointer (clear spsel bit)\n        // * Synchronize instruction barrier\n        // * Initialize stack pointer (0x1000)\n        // * Set link register to not return (0xFF)\n        // * Jump to softdevice reset vector\n        core::arch::asm!(\n            \"mrs {tmp}, CONTROL\",\n            \"bics {tmp}, {spsel}\",\n            \"msr CONTROL, {tmp}\",\n            \"isb\",\n            \"msr MSP, {msp}\",\n            \"mov lr, {new_lr}\",\n            \"bx {rv}\",\n            // `out(reg) _` is not permitted in a `noreturn` asm! call,\n            // so instead use `in(reg) 0` and don't restore it afterwards.\n            tmp = in(reg) 0,\n            spsel = in(reg) 2,\n            new_lr = in(reg) 0xFFFFFFFFu32,\n            msp = in(reg) msp,\n            rv = in(reg) rv,\n            options(noreturn),\n        );\n    }\n}\n\n/// A flash implementation that wraps any flash and will pet a watchdog when touching flash.\npub struct WatchdogFlash<FLASH> {\n    flash: FLASH,\n    wdt: wdt::WatchdogHandle,\n}\n\nimpl<FLASH> WatchdogFlash<FLASH> {\n    /// Start a new watchdog with a given flash and WDT peripheral and a timeout\n    pub fn start(flash: FLASH, wdt: Peri<'static, impl wdt::Instance>, config: wdt::Config) -> Self {\n        let (_wdt, [wdt]) = match wdt::Watchdog::try_new(wdt, config) {\n            Ok(x) => x,\n            Err(_) => {\n                // In case the watchdog is already running, just spin and let it expire, since\n                // we can't configure it anyway. This usually happens when we first program\n                // the device and the watchdog was previously active\n                info!(\"Watchdog already active with wrong config, waiting for it to timeout...\");\n                loop {}\n            }\n        };\n        Self { flash, wdt }\n    }\n}\n\nimpl<FLASH: ErrorType> ErrorType for WatchdogFlash<FLASH> {\n    type Error = FLASH::Error;\n}\n\nimpl<FLASH: NorFlash> NorFlash for WatchdogFlash<FLASH> {\n    const WRITE_SIZE: usize = FLASH::WRITE_SIZE;\n    const ERASE_SIZE: usize = FLASH::ERASE_SIZE;\n\n    fn erase(&mut self, from: u32, to: u32) -> Result<(), Self::Error> {\n        self.wdt.pet();\n        self.flash.erase(from, to)\n    }\n    fn write(&mut self, offset: u32, data: &[u8]) -> Result<(), Self::Error> {\n        self.wdt.pet();\n        self.flash.write(offset, data)\n    }\n}\n\nimpl<FLASH: ReadNorFlash> ReadNorFlash for WatchdogFlash<FLASH> {\n    const READ_SIZE: usize = FLASH::READ_SIZE;\n    fn read(&mut self, offset: u32, data: &mut [u8]) -> Result<(), Self::Error> {\n        self.wdt.pet();\n        self.flash.read(offset, data)\n    }\n    fn capacity(&self) -> usize {\n        self.flash.capacity()\n    }\n}\n"
  },
  {
    "path": "embassy-boot-rp/CHANGELOG.md",
    "content": "# Changelog\n\nAll notable changes to this project will be documented in this file.\n\nThe format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),\nand this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).\n\n<!-- next-header -->\n## Unreleased - ReleaseDate\n\n## 0.10.0 - 2026-03-10\n\n- Update embassy-sync 0.8.0\n- Update embassy-rp 0.10.0\n- Update embassy-boot 0.7.0\n\n## 0.9.0 - 2025-11-27\n\n## 0.8.0 - 2025-08-26\n\n## 0.1.1 - 2025-08-15\n\n- First release with changelog.\n"
  },
  {
    "path": "embassy-boot-rp/Cargo.toml",
    "content": "[package]\nedition = \"2024\"\nname = \"embassy-boot-rp\"\nversion = \"0.10.0\"\ndescription = \"Bootloader lib for RP2040 chips\"\nlicense = \"MIT OR Apache-2.0\"\nrepository = \"https://github.com/embassy-rs/embassy\"\ndocumentation = \"https://docs.embassy.dev/embassy-boot-rp\"\ncategories = [\n    \"embedded\",\n    \"no-std\",\n    \"asynchronous\",\n]\n\n[package.metadata.embassy]\nbuild = [\n    {target = \"thumbv6m-none-eabi\", features = [\"embassy-rp/rp2040\"]},\n]\n\n\n[package.metadata.embassy_docs]\nsrc_base = \"https://github.com/embassy-rs/embassy/blob/embassy-boot-rp-v$VERSION/src/\"\nsrc_base_git = \"https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-boot-rp/src/\"\ntarget = \"thumbv6m-none-eabi\"\nfeatures = [\"embassy-rp/rp2040\"]\n\n[lib]\n\n[dependencies]\ndefmt = { version = \"1.0.1\", optional = true }\nlog = { version = \"0.4\", optional = true }\n\nembassy-sync = { version = \"0.8.0\", path = \"../embassy-sync\" }\nembassy-rp = { version = \"0.10.0\", path = \"../embassy-rp\", default-features = false }\nembassy-boot = { version = \"0.7.0\", path = \"../embassy-boot\" }\nembassy-time = { version = \"0.5.1\", path = \"../embassy-time\" }\n\ncortex-m = { version = \"0.7.6\" }\ncortex-m-rt = { version = \"0.7\" }\nembedded-storage = \"0.3.1\"\nembedded-storage-async = { version = \"0.4.1\" }\ncfg-if = \"1.0.0\"\n\n[features]\ndefmt = [\n    \"dep:defmt\",\n    \"embassy-boot/defmt\",\n    \"embassy-rp/defmt\",\n]\nlog = [\n    \"dep:log\",\n    \"embassy-boot/log\",\n    \"embassy-rp/log\",\n]\n\n[profile.dev]\ndebug = 2\ndebug-assertions = true\nincremental = false\nopt-level = 'z'\noverflow-checks = true\n\n[profile.release]\ncodegen-units = 1\ndebug = 2\ndebug-assertions = false\nincremental = false\nlto = 'fat'\nopt-level = 'z'\noverflow-checks = false\n\n# do not optimize proc-macro crates = faster builds from scratch\n[profile.dev.build-override]\ncodegen-units = 8\ndebug = false\ndebug-assertions = false\nopt-level = 0\noverflow-checks = false\n\n[profile.release.build-override]\ncodegen-units = 8\ndebug = false\ndebug-assertions = false\nopt-level = 0\noverflow-checks = false\n"
  },
  {
    "path": "embassy-boot-rp/README.md",
    "content": "# embassy-boot-rp\n\nAn [Embassy](https://embassy.dev) project.\n\nAn adaptation of `embassy-boot` for RP2040.\n\nNOTE: The applications using this bootloader should not link with the `link-rp.x` linker script.\n\n## Features\n\n* Configure bootloader partitions based on linker script.\n* Load applications from active partition.\n"
  },
  {
    "path": "embassy-boot-rp/build.rs",
    "content": "use std::env;\n\nfn main() {\n    let target = env::var(\"TARGET\").unwrap();\n    if target.starts_with(\"thumbv6m-\") {\n        println!(\"cargo:rustc-cfg=armv6m\");\n    }\n    println!(\"cargo:rustc-check-cfg=cfg(armv6m)\");\n}\n"
  },
  {
    "path": "embassy-boot-rp/src/fmt.rs",
    "content": "#![macro_use]\n#![allow(unused)]\n\nuse core::fmt::{Debug, Display, LowerHex};\n\n#[cfg(all(feature = \"defmt\", feature = \"log\"))]\ncompile_error!(\"You may not enable both `defmt` and `log` features.\");\n\n#[collapse_debuginfo(yes)]\nmacro_rules! assert {\n    ($($x:tt)*) => {\n        {\n            #[cfg(not(feature = \"defmt\"))]\n            ::core::assert!($($x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::assert!($($x)*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! assert_eq {\n    ($($x:tt)*) => {\n        {\n            #[cfg(not(feature = \"defmt\"))]\n            ::core::assert_eq!($($x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::assert_eq!($($x)*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! assert_ne {\n    ($($x:tt)*) => {\n        {\n            #[cfg(not(feature = \"defmt\"))]\n            ::core::assert_ne!($($x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::assert_ne!($($x)*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! debug_assert {\n    ($($x:tt)*) => {\n        {\n            #[cfg(not(feature = \"defmt\"))]\n            ::core::debug_assert!($($x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::debug_assert!($($x)*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! debug_assert_eq {\n    ($($x:tt)*) => {\n        {\n            #[cfg(not(feature = \"defmt\"))]\n            ::core::debug_assert_eq!($($x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::debug_assert_eq!($($x)*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! debug_assert_ne {\n    ($($x:tt)*) => {\n        {\n            #[cfg(not(feature = \"defmt\"))]\n            ::core::debug_assert_ne!($($x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::debug_assert_ne!($($x)*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! todo {\n    ($($x:tt)*) => {\n        {\n            #[cfg(not(feature = \"defmt\"))]\n            ::core::todo!($($x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::todo!($($x)*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! unreachable {\n    ($($x:tt)*) => {\n        {\n            #[cfg(not(feature = \"defmt\"))]\n            ::core::unreachable!($($x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::unreachable!($($x)*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! panic {\n    ($($x:tt)*) => {\n        {\n            #[cfg(not(feature = \"defmt\"))]\n            ::core::panic!($($x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::panic!($($x)*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! trace {\n    ($s:literal $(, $x:expr)* $(,)?) => {\n        {\n            #[cfg(feature = \"log\")]\n            ::log::trace!($s $(, $x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::trace!($s $(, $x)*);\n            #[cfg(not(any(feature = \"log\", feature=\"defmt\")))]\n            let _ = ($( & $x ),*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! debug {\n    ($s:literal $(, $x:expr)* $(,)?) => {\n        {\n            #[cfg(feature = \"log\")]\n            ::log::debug!($s $(, $x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::debug!($s $(, $x)*);\n            #[cfg(not(any(feature = \"log\", feature=\"defmt\")))]\n            let _ = ($( & $x ),*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! info {\n    ($s:literal $(, $x:expr)* $(,)?) => {\n        {\n            #[cfg(feature = \"log\")]\n            ::log::info!($s $(, $x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::info!($s $(, $x)*);\n            #[cfg(not(any(feature = \"log\", feature=\"defmt\")))]\n            let _ = ($( & $x ),*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! warn {\n    ($s:literal $(, $x:expr)* $(,)?) => {\n        {\n            #[cfg(feature = \"log\")]\n            ::log::warn!($s $(, $x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::warn!($s $(, $x)*);\n            #[cfg(not(any(feature = \"log\", feature=\"defmt\")))]\n            let _ = ($( & $x ),*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! error {\n    ($s:literal $(, $x:expr)* $(,)?) => {\n        {\n            #[cfg(feature = \"log\")]\n            ::log::error!($s $(, $x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::error!($s $(, $x)*);\n            #[cfg(not(any(feature = \"log\", feature=\"defmt\")))]\n            let _ = ($( & $x ),*);\n        }\n    };\n}\n\n#[cfg(feature = \"defmt\")]\n#[collapse_debuginfo(yes)]\nmacro_rules! unwrap {\n    ($($x:tt)*) => {\n        ::defmt::unwrap!($($x)*)\n    };\n}\n\n#[cfg(not(feature = \"defmt\"))]\n#[collapse_debuginfo(yes)]\nmacro_rules! unwrap {\n    ($arg:expr) => {\n        match $crate::fmt::Try::into_result($arg) {\n            ::core::result::Result::Ok(t) => t,\n            ::core::result::Result::Err(e) => {\n                ::core::panic!(\"unwrap of `{}` failed: {:?}\", ::core::stringify!($arg), e);\n            }\n        }\n    };\n    ($arg:expr, $($msg:expr),+ $(,)? ) => {\n        match $crate::fmt::Try::into_result($arg) {\n            ::core::result::Result::Ok(t) => t,\n            ::core::result::Result::Err(e) => {\n                ::core::panic!(\"unwrap of `{}` failed: {}: {:?}\", ::core::stringify!($arg), ::core::format_args!($($msg,)*), e);\n            }\n        }\n    }\n}\n\n#[derive(Debug, Copy, Clone, Eq, PartialEq)]\npub struct NoneError;\n\npub trait Try {\n    type Ok;\n    type Error;\n    fn into_result(self) -> Result<Self::Ok, Self::Error>;\n}\n\nimpl<T> Try for Option<T> {\n    type Ok = T;\n    type Error = NoneError;\n\n    #[inline]\n    fn into_result(self) -> Result<T, NoneError> {\n        self.ok_or(NoneError)\n    }\n}\n\nimpl<T, E> Try for Result<T, E> {\n    type Ok = T;\n    type Error = E;\n\n    #[inline]\n    fn into_result(self) -> Self {\n        self\n    }\n}\n\npub(crate) struct Bytes<'a>(pub &'a [u8]);\n\nimpl<'a> Debug for Bytes<'a> {\n    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {\n        write!(f, \"{:#02x?}\", self.0)\n    }\n}\n\nimpl<'a> Display for Bytes<'a> {\n    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {\n        write!(f, \"{:#02x?}\", self.0)\n    }\n}\n\nimpl<'a> LowerHex for Bytes<'a> {\n    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {\n        write!(f, \"{:#02x?}\", self.0)\n    }\n}\n\n#[cfg(feature = \"defmt\")]\nimpl<'a> defmt::Format for Bytes<'a> {\n    fn format(&self, fmt: defmt::Formatter) {\n        defmt::write!(fmt, \"{:02x}\", self.0)\n    }\n}\n"
  },
  {
    "path": "embassy-boot-rp/src/lib.rs",
    "content": "#![no_std]\n#![allow(unsafe_op_in_unsafe_fn)]\n#![warn(missing_docs)]\n#![doc = include_str!(\"../README.md\")]\nmod fmt;\n\npub use embassy_boot::{\n    AlignedBuffer, BlockingFirmwareState, BlockingFirmwareUpdater, BootError, BootLoaderConfig, FirmwareState,\n    FirmwareUpdater, FirmwareUpdaterConfig, State,\n};\nuse embassy_rp::Peri;\nuse embassy_rp::flash::{Blocking, ERASE_SIZE, Flash};\nuse embassy_rp::peripherals::{FLASH, WATCHDOG};\nuse embassy_rp::watchdog::Watchdog;\nuse embassy_time::Duration;\nuse embedded_storage::nor_flash::{ErrorType, NorFlash, ReadNorFlash};\n\n/// A bootloader for RP2040 devices.\npub struct BootLoader<const BUFFER_SIZE: usize = ERASE_SIZE> {\n    /// The reported state of the bootloader after preparing for boot\n    pub state: State,\n}\n\nimpl<const BUFFER_SIZE: usize> BootLoader<BUFFER_SIZE> {\n    /// Inspect the bootloader state and perform actions required before booting, such as swapping firmware\n    pub fn prepare<ACTIVE: NorFlash, DFU: NorFlash, STATE: NorFlash>(\n        config: BootLoaderConfig<ACTIVE, DFU, STATE>,\n    ) -> Self {\n        if let Ok(loader) = Self::try_prepare::<ACTIVE, DFU, STATE>(config) {\n            loader\n        } else {\n            // Use explicit panic instead of .expect() to ensure this gets routed via defmt/etc.\n            // properly\n            panic!(\"Boot prepare error\")\n        }\n    }\n\n    /// Inspect the bootloader state and perform actions required before booting, such as swapping firmware\n    pub fn try_prepare<ACTIVE: NorFlash, DFU: NorFlash, STATE: NorFlash>(\n        config: BootLoaderConfig<ACTIVE, DFU, STATE>,\n    ) -> Result<Self, BootError> {\n        let mut aligned_buf = AlignedBuffer([0; BUFFER_SIZE]);\n        let mut boot = embassy_boot::BootLoader::new(config);\n        let state = boot.prepare_boot(aligned_buf.as_mut())?;\n        Ok(Self { state })\n    }\n\n    /// Boots the application.\n    ///\n    /// # Safety\n    ///\n    /// This modifies the stack pointer and reset vector and will run code placed in the active partition.\n    pub unsafe fn load(self, start: u32) -> ! {\n        trace!(\"Loading app at 0x{:x}\", start);\n        #[allow(unused_mut)]\n        let mut p = cortex_m::Peripherals::steal();\n        #[cfg(not(armv6m))]\n        p.SCB.invalidate_icache();\n        p.SCB.vtor.write(start);\n\n        cortex_m::asm::bootload(start as *const u32)\n    }\n}\n\n/// A flash implementation that will feed a watchdog when touching flash.\npub struct WatchdogFlash<'d, const SIZE: usize> {\n    flash: Flash<'d, FLASH, Blocking, SIZE>,\n    watchdog: Watchdog,\n    timeout: Duration,\n}\n\nimpl<'d, const SIZE: usize> WatchdogFlash<'d, SIZE> {\n    /// Start a new watchdog with a given flash and watchdog peripheral and a timeout\n    pub fn start(flash: Peri<'static, FLASH>, watchdog: Peri<'static, WATCHDOG>, timeout: Duration) -> Self {\n        let flash = Flash::<_, Blocking, SIZE>::new_blocking(flash);\n        let mut watchdog = Watchdog::new(watchdog);\n        watchdog.start(timeout);\n        Self {\n            flash,\n            watchdog,\n            timeout,\n        }\n    }\n}\n\nimpl<'d, const SIZE: usize> ErrorType for WatchdogFlash<'d, SIZE> {\n    type Error = <Flash<'d, FLASH, Blocking, SIZE> as ErrorType>::Error;\n}\n\nimpl<'d, const SIZE: usize> NorFlash for WatchdogFlash<'d, SIZE> {\n    const WRITE_SIZE: usize = <Flash<'d, FLASH, Blocking, SIZE> as NorFlash>::WRITE_SIZE;\n    const ERASE_SIZE: usize = <Flash<'d, FLASH, Blocking, SIZE> as NorFlash>::ERASE_SIZE;\n\n    fn erase(&mut self, from: u32, to: u32) -> Result<(), Self::Error> {\n        self.watchdog.feed(self.timeout);\n        self.flash.blocking_erase(from, to)\n    }\n    fn write(&mut self, offset: u32, data: &[u8]) -> Result<(), Self::Error> {\n        self.watchdog.feed(self.timeout);\n        self.flash.blocking_write(offset, data)\n    }\n}\n\nimpl<'d, const SIZE: usize> ReadNorFlash for WatchdogFlash<'d, SIZE> {\n    const READ_SIZE: usize = <Flash<'d, FLASH, Blocking, SIZE> as ReadNorFlash>::READ_SIZE;\n    fn read(&mut self, offset: u32, data: &mut [u8]) -> Result<(), Self::Error> {\n        self.watchdog.feed(self.timeout);\n        self.flash.blocking_read(offset, data)\n    }\n    fn capacity(&self) -> usize {\n        self.flash.capacity()\n    }\n}\n"
  },
  {
    "path": "embassy-boot-stm32/CHANGELOG.md",
    "content": "# Changelog\n\nAll notable changes to this project will be documented in this file.\n\nThe format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),\nand this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).\n\n<!-- next-header -->\n## Unreleased - ReleaseDate\n\n## 0.8.0 - 2026-03-10\n\n- Update embassy-sync to 0.8.0\n- Update embassy-stm32 to 0.6.0\n- Update embassy-boot to 0.7.0\n\n## 0.7.0 - 2026-01-04\n\n- Update embassy-stm32 version\n\n## 0.6.0 - 2025-08-26\n\n## 0.1.1 - 2025-08-15\n\n- First release with changelog.\n"
  },
  {
    "path": "embassy-boot-stm32/Cargo.toml",
    "content": "[package]\nedition = \"2024\"\nname = \"embassy-boot-stm32\"\nversion = \"0.8.0\"\ndescription = \"Bootloader lib for STM32 chips\"\nlicense = \"MIT OR Apache-2.0\"\nrepository = \"https://github.com/embassy-rs/embassy\"\ndocumentation = \"https://docs.embassy.dev/embassy-boot-stm32\"\ncategories = [\n    \"embedded\",\n    \"no-std\",\n    \"asynchronous\",\n]\n\n[package.metadata.embassy]\nbuild = [\n    {target = \"thumbv7em-none-eabi\", features = [\"embassy-stm32/stm32l496zg\"]},\n]\n\n\n[package.metadata.embassy_docs]\nsrc_base = \"https://github.com/embassy-rs/embassy/blob/embassy-boot-stm32-v$VERSION/embassy-boot-stm32/src/\"\nsrc_base_git = \"https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-boot-stm32/src/\"\nfeatures = [\"embassy-stm32/stm32f429zi\"]\ntarget = \"thumbv7em-none-eabi\"\n\n[lib]\n\n[dependencies]\ndefmt = { version = \"1.0.1\", optional = true }\nlog = { version = \"0.4\", optional = true }\n\nembassy-sync = { version = \"0.8.0\", path = \"../embassy-sync\" }\nembassy-stm32 = { version = \"0.6.0\", path = \"../embassy-stm32\", default-features = false }\nembassy-boot = { version = \"0.7.0\", path = \"../embassy-boot\" }\ncortex-m = { version = \"0.7.6\" }\ncortex-m-rt = { version = \"0.7\" }\nembedded-storage = \"0.3.1\"\nembedded-storage-async = { version = \"0.4.1\" }\ncfg-if = \"1.0.0\"\n\n[features]\ndefmt = [\"dep:defmt\", \"embassy-boot/defmt\", \"embassy-stm32/defmt\"]\nlog = [\"dep:log\", \"embassy-boot/log\", \"embassy-stm32/log\"]\n\n[profile.dev]\ndebug = 2\ndebug-assertions = true\nincremental = false\nopt-level = 'z'\noverflow-checks = true\n\n[profile.release]\ncodegen-units = 1\ndebug = 2\ndebug-assertions = false\nincremental = false\nlto = 'fat'\nopt-level = 'z'\noverflow-checks = false\n\n# do not optimize proc-macro crates = faster builds from scratch\n[profile.dev.build-override]\ncodegen-units = 8\ndebug = false\ndebug-assertions = false\nopt-level = 0\noverflow-checks = false\n\n[profile.release.build-override]\ncodegen-units = 8\ndebug = false\ndebug-assertions = false\nopt-level = 0\noverflow-checks = false\n"
  },
  {
    "path": "embassy-boot-stm32/README.md",
    "content": "# embassy-boot-stm32\n\nAn [Embassy](https://embassy.dev) project.\n\nAn adaptation of `embassy-boot` for STM32.\n\n## Features\n\n* Configure bootloader partitions based on linker script.\n* Load applications from active partition.\n"
  },
  {
    "path": "embassy-boot-stm32/build.rs",
    "content": "use std::env;\n\nfn main() {\n    let target = env::var(\"TARGET\").unwrap();\n    if target.starts_with(\"thumbv6m-\") {\n        println!(\"cargo:rustc-cfg=armv6m\");\n    }\n    println!(\"cargo:rustc-check-cfg=cfg(armv6m)\");\n}\n"
  },
  {
    "path": "embassy-boot-stm32/src/fmt.rs",
    "content": "#![macro_use]\n#![allow(unused)]\n\nuse core::fmt::{Debug, Display, LowerHex};\n\n#[cfg(all(feature = \"defmt\", feature = \"log\"))]\ncompile_error!(\"You may not enable both `defmt` and `log` features.\");\n\n#[collapse_debuginfo(yes)]\nmacro_rules! assert {\n    ($($x:tt)*) => {\n        {\n            #[cfg(not(feature = \"defmt\"))]\n            ::core::assert!($($x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::assert!($($x)*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! assert_eq {\n    ($($x:tt)*) => {\n        {\n            #[cfg(not(feature = \"defmt\"))]\n            ::core::assert_eq!($($x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::assert_eq!($($x)*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! assert_ne {\n    ($($x:tt)*) => {\n        {\n            #[cfg(not(feature = \"defmt\"))]\n            ::core::assert_ne!($($x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::assert_ne!($($x)*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! debug_assert {\n    ($($x:tt)*) => {\n        {\n            #[cfg(not(feature = \"defmt\"))]\n            ::core::debug_assert!($($x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::debug_assert!($($x)*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! debug_assert_eq {\n    ($($x:tt)*) => {\n        {\n            #[cfg(not(feature = \"defmt\"))]\n            ::core::debug_assert_eq!($($x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::debug_assert_eq!($($x)*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! debug_assert_ne {\n    ($($x:tt)*) => {\n        {\n            #[cfg(not(feature = \"defmt\"))]\n            ::core::debug_assert_ne!($($x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::debug_assert_ne!($($x)*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! todo {\n    ($($x:tt)*) => {\n        {\n            #[cfg(not(feature = \"defmt\"))]\n            ::core::todo!($($x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::todo!($($x)*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! unreachable {\n    ($($x:tt)*) => {\n        {\n            #[cfg(not(feature = \"defmt\"))]\n            ::core::unreachable!($($x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::unreachable!($($x)*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! panic {\n    ($($x:tt)*) => {\n        {\n            #[cfg(not(feature = \"defmt\"))]\n            ::core::panic!($($x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::panic!($($x)*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! trace {\n    ($s:literal $(, $x:expr)* $(,)?) => {\n        {\n            #[cfg(feature = \"log\")]\n            ::log::trace!($s $(, $x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::trace!($s $(, $x)*);\n            #[cfg(not(any(feature = \"log\", feature=\"defmt\")))]\n            let _ = ($( & $x ),*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! debug {\n    ($s:literal $(, $x:expr)* $(,)?) => {\n        {\n            #[cfg(feature = \"log\")]\n            ::log::debug!($s $(, $x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::debug!($s $(, $x)*);\n            #[cfg(not(any(feature = \"log\", feature=\"defmt\")))]\n            let _ = ($( & $x ),*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! info {\n    ($s:literal $(, $x:expr)* $(,)?) => {\n        {\n            #[cfg(feature = \"log\")]\n            ::log::info!($s $(, $x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::info!($s $(, $x)*);\n            #[cfg(not(any(feature = \"log\", feature=\"defmt\")))]\n            let _ = ($( & $x ),*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! warn {\n    ($s:literal $(, $x:expr)* $(,)?) => {\n        {\n            #[cfg(feature = \"log\")]\n            ::log::warn!($s $(, $x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::warn!($s $(, $x)*);\n            #[cfg(not(any(feature = \"log\", feature=\"defmt\")))]\n            let _ = ($( & $x ),*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! error {\n    ($s:literal $(, $x:expr)* $(,)?) => {\n        {\n            #[cfg(feature = \"log\")]\n            ::log::error!($s $(, $x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::error!($s $(, $x)*);\n            #[cfg(not(any(feature = \"log\", feature=\"defmt\")))]\n            let _ = ($( & $x ),*);\n        }\n    };\n}\n\n#[cfg(feature = \"defmt\")]\n#[collapse_debuginfo(yes)]\nmacro_rules! unwrap {\n    ($($x:tt)*) => {\n        ::defmt::unwrap!($($x)*)\n    };\n}\n\n#[cfg(not(feature = \"defmt\"))]\n#[collapse_debuginfo(yes)]\nmacro_rules! unwrap {\n    ($arg:expr) => {\n        match $crate::fmt::Try::into_result($arg) {\n            ::core::result::Result::Ok(t) => t,\n            ::core::result::Result::Err(e) => {\n                ::core::panic!(\"unwrap of `{}` failed: {:?}\", ::core::stringify!($arg), e);\n            }\n        }\n    };\n    ($arg:expr, $($msg:expr),+ $(,)? ) => {\n        match $crate::fmt::Try::into_result($arg) {\n            ::core::result::Result::Ok(t) => t,\n            ::core::result::Result::Err(e) => {\n                ::core::panic!(\"unwrap of `{}` failed: {}: {:?}\", ::core::stringify!($arg), ::core::format_args!($($msg,)*), e);\n            }\n        }\n    }\n}\n\n#[derive(Debug, Copy, Clone, Eq, PartialEq)]\npub struct NoneError;\n\npub trait Try {\n    type Ok;\n    type Error;\n    fn into_result(self) -> Result<Self::Ok, Self::Error>;\n}\n\nimpl<T> Try for Option<T> {\n    type Ok = T;\n    type Error = NoneError;\n\n    #[inline]\n    fn into_result(self) -> Result<T, NoneError> {\n        self.ok_or(NoneError)\n    }\n}\n\nimpl<T, E> Try for Result<T, E> {\n    type Ok = T;\n    type Error = E;\n\n    #[inline]\n    fn into_result(self) -> Self {\n        self\n    }\n}\n\npub(crate) struct Bytes<'a>(pub &'a [u8]);\n\nimpl<'a> Debug for Bytes<'a> {\n    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {\n        write!(f, \"{:#02x?}\", self.0)\n    }\n}\n\nimpl<'a> Display for Bytes<'a> {\n    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {\n        write!(f, \"{:#02x?}\", self.0)\n    }\n}\n\nimpl<'a> LowerHex for Bytes<'a> {\n    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {\n        write!(f, \"{:#02x?}\", self.0)\n    }\n}\n\n#[cfg(feature = \"defmt\")]\nimpl<'a> defmt::Format for Bytes<'a> {\n    fn format(&self, fmt: defmt::Formatter) {\n        defmt::write!(fmt, \"{:02x}\", self.0)\n    }\n}\n"
  },
  {
    "path": "embassy-boot-stm32/src/lib.rs",
    "content": "#![no_std]\n#![allow(unsafe_op_in_unsafe_fn)]\n#![warn(missing_docs)]\n#![doc = include_str!(\"../README.md\")]\nmod fmt;\n\npub use embassy_boot::{\n    AlignedBuffer, BlockingFirmwareState, BlockingFirmwareUpdater, BootError, BootLoaderConfig, FirmwareState,\n    FirmwareUpdater, FirmwareUpdaterConfig, State,\n};\nuse embedded_storage::nor_flash::NorFlash;\n\n/// A bootloader for STM32 devices.\npub struct BootLoader {\n    /// The reported state of the bootloader after preparing for boot\n    pub state: State,\n}\n\nimpl BootLoader {\n    /// Inspect the bootloader state and perform actions required before booting, such as swapping firmware\n    pub fn prepare<ACTIVE: NorFlash, DFU: NorFlash, STATE: NorFlash, const BUFFER_SIZE: usize>(\n        config: BootLoaderConfig<ACTIVE, DFU, STATE>,\n    ) -> Self {\n        if let Ok(loader) = Self::try_prepare::<ACTIVE, DFU, STATE, BUFFER_SIZE>(config) {\n            loader\n        } else {\n            // Use explicit panic instead of .expect() to ensure this gets routed via defmt/etc.\n            // properly\n            panic!(\"Boot prepare error\")\n        }\n    }\n\n    /// Inspect the bootloader state and perform actions required before booting, such as swapping firmware\n    pub fn try_prepare<ACTIVE: NorFlash, DFU: NorFlash, STATE: NorFlash, const BUFFER_SIZE: usize>(\n        config: BootLoaderConfig<ACTIVE, DFU, STATE>,\n    ) -> Result<Self, BootError> {\n        let mut aligned_buf = AlignedBuffer([0; BUFFER_SIZE]);\n        let mut boot = embassy_boot::BootLoader::new(config);\n        let state = boot.prepare_boot(aligned_buf.as_mut())?;\n        Ok(Self { state })\n    }\n\n    /// Boots the application.\n    ///\n    /// # Safety\n    ///\n    /// This modifies the stack pointer and reset vector and will run code placed in the active partition.\n    pub unsafe fn load(self, start: u32) -> ! {\n        trace!(\"Loading app at 0x{:x}\", start);\n        #[allow(unused_mut)]\n        let mut p = cortex_m::Peripherals::steal();\n        #[cfg(not(armv6m))]\n        p.SCB.invalidate_icache();\n        p.SCB.vtor.write(start);\n\n        cortex_m::asm::bootload(start as *const u32)\n    }\n}\n"
  },
  {
    "path": "embassy-embedded-hal/CHANGELOG.md",
    "content": "# Changelog for embassy-embedded-hal\n\nAll notable changes to this project will be documented in this file.\n\nThe format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),\nand this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).\n\n<!-- next-header -->\n## Unreleased - ReleaseDate\n\n## 0.6.0 - 2026-03-10\n\n- Shared I2c busses now impl `Clone`\n- Upgrade embassy-sync to 0.8.0\n\n## 0.5.0 - 2025-08-27\n\n## 0.4.0 - 2025-08-03\n\n- `SpiDevice` cancel safety: always set CS pin to high on drop\n- Update `embassy-sync` to v0.7.0\n\n## 0.3.2 - 2025-08-03\n\n- Reverted changes in 0.3.1\n- Reexport `SetConfig`, `GetConfig` traits from v0.4.0.\n\n## 0.3.1 - 2025-07-16\n\nYANKED due to embassy-sync upgrade being a breaking change.\n\n- `SpiDevice` cancel safety: always set CS pin to high on drop\n- Update `embassy-sync` to v0.7.0\n\n## 0.3.0 - 2025-01-05\n\n- The `std` feature has been removed\n- Updated `embassy-time` to v0.4\n\n## 0.2.0 - 2024-08-05\n\n- Add Clone derive to flash Partition in embassy-embedded-hal\n- Add support for all word sizes to async shared spi\n- Add Copy and 'static constraint to Word type in SPI structs\n- Improve flexibility by introducing SPI word size as a generic parameter\n- Allow changing Spi/I2cDeviceWithConfig's config at runtime\n- Impl `MultiwriteNorFlash` for `BlockingAsync`\n\n## 0.1.0 - 2024-01-10\n\n- First release\n"
  },
  {
    "path": "embassy-embedded-hal/Cargo.toml",
    "content": "[package]\nname = \"embassy-embedded-hal\"\nversion = \"0.6.0\"\nedition = \"2024\"\nlicense = \"MIT OR Apache-2.0\"\ndescription = \"Collection of utilities to use `embedded-hal` and `embedded-storage` traits with Embassy.\"\nrepository = \"https://github.com/embassy-rs/embassy\"\ndocumentation = \"https://docs.embassy.dev/embassy-embedded-hal\"\ncategories = [\n    \"embedded\",\n    \"no-std\",\n    \"asynchronous\",\n]\n\n[package.metadata.embassy]\nbuild = [\n    {target = \"thumbv7em-none-eabi\", features = []},\n    {target = \"thumbv7em-none-eabi\", features = [\"time\"]},\n]\n\n\n[package.metadata.embassy_docs]\nsrc_base = \"https://github.com/embassy-rs/embassy/blob/embassy-embedded-hal-v$VERSION/embassy-embedded-hal/src/\"\nsrc_base_git = \"https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-embedded-hal/src/\"\ntarget = \"x86_64-unknown-linux-gnu\"\n\n[features]\ndefmt = [\"dep:defmt\"]\ntime = [\"dep:embassy-time\"]\n\n[dependencies]\nembassy-hal-internal = { version = \"0.5.0\", path = \"../embassy-hal-internal\" }\nembassy-futures = { version = \"0.1.2\", path = \"../embassy-futures\" }\nembassy-sync = { version = \"0.8.0\", path = \"../embassy-sync\" }\nembassy-time = { version = \"0.5.1\", path = \"../embassy-time\", optional = true }\nembedded-hal-02 = { package = \"embedded-hal\", version = \"0.2.6\", features = [\n    \"unproven\",\n] }\nembedded-hal-1 = { package = \"embedded-hal\", version = \"1.0\" }\nembedded-hal-async = { version = \"1.0\" }\nembedded-storage = \"0.3.1\"\nembedded-storage-async = { version = \"0.4.1\" }\nnb = \"1.0.0\"\n\ndefmt = { version = \"1.0.1\", optional = true }\n\n[dev-dependencies]\ncritical-section = { version = \"1.1.1\", features = [\"std\"] }\nfutures-test = \"0.3.17\"\n"
  },
  {
    "path": "embassy-embedded-hal/README.md",
    "content": "# embassy-embedded-hal\n\nCollection of utilities to use `embedded-hal` and `embedded-storage` traits with Embassy.\n\n- Shared SPI and I2C buses, both blocking and async, with a `SetConfig` trait allowing changing bus configuration (e.g. frequency) between devices on the same bus.\n- Async utilities\n    - Adapters to convert from blocking to (fake) async.\n    - Adapters to insert yields on trait operations.\n- Flash utilities\n    - Split a flash memory into smaller partitions.\n    - Concatenate flash memories together.\n    - Simulated in-memory flash.\n"
  },
  {
    "path": "embassy-embedded-hal/src/adapter/blocking_async.rs",
    "content": "/// Wrapper that implements async traits using blocking implementations.\n///\n/// This allows driver writers to depend on the async traits while still supporting embedded-hal peripheral implementations.\n///\n/// BlockingAsync will implement any async trait that maps to embedded-hal traits implemented for the wrapped driver.\n///\n/// Driver users are then free to choose which implementation that is available to them.\npub struct BlockingAsync<T> {\n    wrapped: T,\n}\n\nimpl<T> BlockingAsync<T> {\n    /// Create a new instance of a wrapper for a given peripheral.\n    pub fn new(wrapped: T) -> Self {\n        Self { wrapped }\n    }\n}\n\n//\n// I2C implementations\n//\nimpl<T, E> embedded_hal_1::i2c::ErrorType for BlockingAsync<T>\nwhere\n    E: embedded_hal_1::i2c::Error + 'static,\n    T: embedded_hal_1::i2c::I2c<Error = E>,\n{\n    type Error = E;\n}\n\nimpl<T, E> embedded_hal_async::i2c::I2c for BlockingAsync<T>\nwhere\n    E: embedded_hal_1::i2c::Error + 'static,\n    T: embedded_hal_1::i2c::I2c<Error = E>,\n{\n    async fn read(&mut self, address: u8, read: &mut [u8]) -> Result<(), Self::Error> {\n        self.wrapped.read(address, read)\n    }\n\n    async fn write(&mut self, address: u8, write: &[u8]) -> Result<(), Self::Error> {\n        self.wrapped.write(address, write)\n    }\n\n    async fn write_read(&mut self, address: u8, write: &[u8], read: &mut [u8]) -> Result<(), Self::Error> {\n        self.wrapped.write_read(address, write, read)\n    }\n\n    async fn transaction(\n        &mut self,\n        address: u8,\n        operations: &mut [embedded_hal_1::i2c::Operation<'_>],\n    ) -> Result<(), Self::Error> {\n        self.wrapped.transaction(address, operations)\n    }\n}\n\n//\n// SPI implementatinos\n//\n\nimpl<T, E> embedded_hal_async::spi::ErrorType for BlockingAsync<T>\nwhere\n    E: embedded_hal_async::spi::Error,\n    T: embedded_hal_1::spi::SpiBus<Error = E>,\n{\n    type Error = E;\n}\n\nimpl<T, E> embedded_hal_async::spi::SpiBus<u8> for BlockingAsync<T>\nwhere\n    E: embedded_hal_async::spi::Error,\n    T: embedded_hal_1::spi::SpiBus<Error = E>,\n{\n    async fn flush(&mut self) -> Result<(), Self::Error> {\n        Ok(())\n    }\n\n    async fn write(&mut self, data: &[u8]) -> Result<(), Self::Error> {\n        self.wrapped.write(data)?;\n        Ok(())\n    }\n\n    async fn read(&mut self, data: &mut [u8]) -> Result<(), Self::Error> {\n        self.wrapped.read(data)?;\n        Ok(())\n    }\n\n    async fn transfer(&mut self, read: &mut [u8], write: &[u8]) -> Result<(), Self::Error> {\n        self.wrapped.transfer(read, write)?;\n        Ok(())\n    }\n\n    async fn transfer_in_place(&mut self, data: &mut [u8]) -> Result<(), Self::Error> {\n        self.wrapped.transfer_in_place(data)?;\n        Ok(())\n    }\n}\n\n/// NOR flash wrapper\nuse embedded_storage::nor_flash::{ErrorType, MultiwriteNorFlash, NorFlash, ReadNorFlash};\nuse embedded_storage_async::nor_flash::{\n    MultiwriteNorFlash as AsyncMultiwriteNorFlash, NorFlash as AsyncNorFlash, ReadNorFlash as AsyncReadNorFlash,\n};\n\nimpl<T> ErrorType for BlockingAsync<T>\nwhere\n    T: ErrorType,\n{\n    type Error = T::Error;\n}\n\nimpl<T> AsyncNorFlash for BlockingAsync<T>\nwhere\n    T: NorFlash,\n{\n    const WRITE_SIZE: usize = <T as NorFlash>::WRITE_SIZE;\n    const ERASE_SIZE: usize = <T as NorFlash>::ERASE_SIZE;\n\n    async fn write(&mut self, offset: u32, data: &[u8]) -> Result<(), Self::Error> {\n        self.wrapped.write(offset, data)\n    }\n\n    async fn erase(&mut self, from: u32, to: u32) -> Result<(), Self::Error> {\n        self.wrapped.erase(from, to)\n    }\n}\n\nimpl<T> AsyncReadNorFlash for BlockingAsync<T>\nwhere\n    T: ReadNorFlash,\n{\n    const READ_SIZE: usize = <T as ReadNorFlash>::READ_SIZE;\n    async fn read(&mut self, address: u32, data: &mut [u8]) -> Result<(), Self::Error> {\n        self.wrapped.read(address, data)\n    }\n\n    fn capacity(&self) -> usize {\n        self.wrapped.capacity()\n    }\n}\n\nimpl<T> AsyncMultiwriteNorFlash for BlockingAsync<T> where T: MultiwriteNorFlash {}\n"
  },
  {
    "path": "embassy-embedded-hal/src/adapter/mod.rs",
    "content": "//! Adapters between embedded-hal traits.\n\nmod blocking_async;\nmod yielding_async;\n\npub use blocking_async::BlockingAsync;\npub use yielding_async::YieldingAsync;\n"
  },
  {
    "path": "embassy-embedded-hal/src/adapter/yielding_async.rs",
    "content": "use embassy_futures::yield_now;\n\n/// Wrapper that yields for each operation to the wrapped instance\n///\n/// This can be used in combination with [super::BlockingAsync] to enforce yields\n/// between long running blocking operations.\npub struct YieldingAsync<T> {\n    wrapped: T,\n}\n\nimpl<T> YieldingAsync<T> {\n    /// Create a new instance of a wrapper that yields after each operation.\n    pub fn new(wrapped: T) -> Self {\n        Self { wrapped }\n    }\n}\n\n//\n// I2C implementations\n//\nimpl<T> embedded_hal_1::i2c::ErrorType for YieldingAsync<T>\nwhere\n    T: embedded_hal_1::i2c::ErrorType,\n{\n    type Error = T::Error;\n}\n\nimpl<T> embedded_hal_async::i2c::I2c for YieldingAsync<T>\nwhere\n    T: embedded_hal_async::i2c::I2c,\n{\n    async fn read(&mut self, address: u8, read: &mut [u8]) -> Result<(), Self::Error> {\n        self.wrapped.read(address, read).await?;\n        yield_now().await;\n        Ok(())\n    }\n\n    async fn write(&mut self, address: u8, write: &[u8]) -> Result<(), Self::Error> {\n        self.wrapped.write(address, write).await?;\n        yield_now().await;\n        Ok(())\n    }\n\n    async fn write_read(&mut self, address: u8, write: &[u8], read: &mut [u8]) -> Result<(), Self::Error> {\n        self.wrapped.write_read(address, write, read).await?;\n        yield_now().await;\n        Ok(())\n    }\n\n    async fn transaction(\n        &mut self,\n        address: u8,\n        operations: &mut [embedded_hal_1::i2c::Operation<'_>],\n    ) -> Result<(), Self::Error> {\n        self.wrapped.transaction(address, operations).await?;\n        yield_now().await;\n        Ok(())\n    }\n}\n\n//\n// SPI implementations\n//\n\nimpl<T> embedded_hal_async::spi::ErrorType for YieldingAsync<T>\nwhere\n    T: embedded_hal_async::spi::ErrorType,\n{\n    type Error = T::Error;\n}\n\nimpl<T, Word: 'static + Copy> embedded_hal_async::spi::SpiBus<Word> for YieldingAsync<T>\nwhere\n    T: embedded_hal_async::spi::SpiBus<Word>,\n{\n    async fn flush(&mut self) -> Result<(), Self::Error> {\n        self.wrapped.flush().await?;\n        yield_now().await;\n        Ok(())\n    }\n\n    async fn write(&mut self, data: &[Word]) -> Result<(), Self::Error> {\n        self.wrapped.write(data).await?;\n        yield_now().await;\n        Ok(())\n    }\n\n    async fn read(&mut self, data: &mut [Word]) -> Result<(), Self::Error> {\n        self.wrapped.read(data).await?;\n        yield_now().await;\n        Ok(())\n    }\n\n    async fn transfer(&mut self, read: &mut [Word], write: &[Word]) -> Result<(), Self::Error> {\n        self.wrapped.transfer(read, write).await?;\n        yield_now().await;\n        Ok(())\n    }\n\n    async fn transfer_in_place(&mut self, words: &mut [Word]) -> Result<(), Self::Error> {\n        self.wrapped.transfer_in_place(words).await?;\n        yield_now().await;\n        Ok(())\n    }\n}\n\n///\n/// NOR flash implementations\n///\nimpl<T: embedded_storage::nor_flash::ErrorType> embedded_storage::nor_flash::ErrorType for YieldingAsync<T> {\n    type Error = T::Error;\n}\n\nimpl<T: embedded_storage_async::nor_flash::ReadNorFlash> embedded_storage_async::nor_flash::ReadNorFlash\n    for YieldingAsync<T>\n{\n    const READ_SIZE: usize = T::READ_SIZE;\n\n    async fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Self::Error> {\n        self.wrapped.read(offset, bytes).await?;\n        Ok(())\n    }\n\n    fn capacity(&self) -> usize {\n        self.wrapped.capacity()\n    }\n}\n\nimpl<T: embedded_storage_async::nor_flash::NorFlash> embedded_storage_async::nor_flash::NorFlash for YieldingAsync<T> {\n    const WRITE_SIZE: usize = T::WRITE_SIZE;\n    const ERASE_SIZE: usize = T::ERASE_SIZE;\n\n    async fn write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Self::Error> {\n        self.wrapped.write(offset, bytes).await?;\n        yield_now().await;\n        Ok(())\n    }\n\n    async fn erase(&mut self, from: u32, to: u32) -> Result<(), Self::Error> {\n        // Yield between each actual erase\n        for from in (from..to).step_by(T::ERASE_SIZE) {\n            let to = core::cmp::min(from + T::ERASE_SIZE as u32, to);\n            self.wrapped.erase(from, to).await?;\n            yield_now().await;\n        }\n        Ok(())\n    }\n}\n\nimpl<T: embedded_storage_async::nor_flash::MultiwriteNorFlash> embedded_storage_async::nor_flash::MultiwriteNorFlash\n    for YieldingAsync<T>\n{\n}\n\n#[cfg(test)]\nmod tests {\n    use embedded_storage_async::nor_flash::NorFlash;\n\n    use super::*;\n    use crate::flash::mem_flash::MemFlash;\n\n    #[futures_test::test]\n    async fn can_erase() {\n        let flash = MemFlash::<1024, 128, 4>::new(0x00);\n        let mut yielding = YieldingAsync::new(flash);\n\n        yielding.erase(0, 256).await.unwrap();\n\n        let flash = yielding.wrapped;\n        assert_eq!(2, flash.erases.len());\n        assert_eq!((0, 128), flash.erases[0]);\n        assert_eq!((128, 256), flash.erases[1]);\n    }\n}\n"
  },
  {
    "path": "embassy-embedded-hal/src/flash/concat_flash.rs",
    "content": "use embedded_storage::nor_flash::{ErrorType, MultiwriteNorFlash, NorFlash, NorFlashError, ReadNorFlash};\nuse embedded_storage_async::nor_flash::{\n    MultiwriteNorFlash as AsyncMultiwriteNorFlash, NorFlash as AsyncNorFlash, ReadNorFlash as AsyncReadNorFlash,\n};\n\n/// Convenience helper for concatenating two consecutive flashes into one.\n/// This is especially useful if used with \"flash regions\", where one may\n/// want to concatenate multiple regions into one larger region.\npub struct ConcatFlash<First, Second>(First, Second);\n\nimpl<First, Second> ConcatFlash<First, Second> {\n    /// Create a new flash that concatenates two consecutive flashes.\n    pub fn new(first: First, second: Second) -> Self {\n        Self(first, second)\n    }\n}\n\nconst fn get_read_size(first_read_size: usize, second_read_size: usize) -> usize {\n    if first_read_size != second_read_size {\n        panic!(\"The read size for the concatenated flashes must be the same\");\n    }\n    first_read_size\n}\n\nconst fn get_write_size(first_write_size: usize, second_write_size: usize) -> usize {\n    if first_write_size != second_write_size {\n        panic!(\"The write size for the concatenated flashes must be the same\");\n    }\n    first_write_size\n}\n\nconst fn get_max_erase_size(first_erase_size: usize, second_erase_size: usize) -> usize {\n    let max_erase_size = if first_erase_size > second_erase_size {\n        first_erase_size\n    } else {\n        second_erase_size\n    };\n    if max_erase_size % first_erase_size != 0 || max_erase_size % second_erase_size != 0 {\n        panic!(\"The erase sizes for the concatenated flashes must have have a gcd equal to the max erase size\");\n    }\n    max_erase_size\n}\n\nimpl<First, Second, E> ErrorType for ConcatFlash<First, Second>\nwhere\n    First: ErrorType<Error = E>,\n    Second: ErrorType<Error = E>,\n    E: NorFlashError,\n{\n    type Error = E;\n}\n\nimpl<First, Second, E> ReadNorFlash for ConcatFlash<First, Second>\nwhere\n    First: ReadNorFlash<Error = E>,\n    Second: ReadNorFlash<Error = E>,\n    E: NorFlashError,\n{\n    const READ_SIZE: usize = get_read_size(First::READ_SIZE, Second::READ_SIZE);\n\n    fn read(&mut self, mut offset: u32, mut bytes: &mut [u8]) -> Result<(), E> {\n        if offset < self.0.capacity() as u32 {\n            let len = core::cmp::min(self.0.capacity() - offset as usize, bytes.len());\n            self.0.read(offset, &mut bytes[..len])?;\n            offset += len as u32;\n            bytes = &mut bytes[len..];\n        }\n\n        if !bytes.is_empty() {\n            self.1.read(offset - self.0.capacity() as u32, bytes)?;\n        }\n\n        Ok(())\n    }\n\n    fn capacity(&self) -> usize {\n        self.0.capacity() + self.1.capacity()\n    }\n}\n\nimpl<First, Second, E> NorFlash for ConcatFlash<First, Second>\nwhere\n    First: NorFlash<Error = E>,\n    Second: NorFlash<Error = E>,\n    E: NorFlashError,\n{\n    const WRITE_SIZE: usize = get_write_size(First::WRITE_SIZE, Second::WRITE_SIZE);\n    const ERASE_SIZE: usize = get_max_erase_size(First::ERASE_SIZE, Second::ERASE_SIZE);\n\n    fn write(&mut self, mut offset: u32, mut bytes: &[u8]) -> Result<(), E> {\n        if offset < self.0.capacity() as u32 {\n            let len = core::cmp::min(self.0.capacity() - offset as usize, bytes.len());\n            self.0.write(offset, &bytes[..len])?;\n            offset += len as u32;\n            bytes = &bytes[len..];\n        }\n\n        if !bytes.is_empty() {\n            self.1.write(offset - self.0.capacity() as u32, bytes)?;\n        }\n\n        Ok(())\n    }\n\n    fn erase(&mut self, mut from: u32, to: u32) -> Result<(), E> {\n        if from < self.0.capacity() as u32 {\n            let to = core::cmp::min(self.0.capacity() as u32, to);\n            self.0.erase(from, to)?;\n            from = self.0.capacity() as u32;\n        }\n\n        if from < to {\n            self.1\n                .erase(from - self.0.capacity() as u32, to - self.0.capacity() as u32)?;\n        }\n\n        Ok(())\n    }\n}\n\nimpl<First, Second, E> MultiwriteNorFlash for ConcatFlash<First, Second>\nwhere\n    First: MultiwriteNorFlash<Error = E>,\n    Second: MultiwriteNorFlash<Error = E>,\n    E: NorFlashError,\n{\n}\n\nimpl<First, Second, E> AsyncReadNorFlash for ConcatFlash<First, Second>\nwhere\n    First: AsyncReadNorFlash<Error = E>,\n    Second: AsyncReadNorFlash<Error = E>,\n    E: NorFlashError,\n{\n    const READ_SIZE: usize = get_read_size(First::READ_SIZE, Second::READ_SIZE);\n\n    async fn read(&mut self, mut offset: u32, mut bytes: &mut [u8]) -> Result<(), E> {\n        if offset < self.0.capacity() as u32 {\n            let len = core::cmp::min(self.0.capacity() - offset as usize, bytes.len());\n            self.0.read(offset, &mut bytes[..len]).await?;\n            offset += len as u32;\n            bytes = &mut bytes[len..];\n        }\n\n        if !bytes.is_empty() {\n            self.1.read(offset - self.0.capacity() as u32, bytes).await?;\n        }\n\n        Ok(())\n    }\n\n    fn capacity(&self) -> usize {\n        self.0.capacity() + self.1.capacity()\n    }\n}\n\nimpl<First, Second, E> AsyncNorFlash for ConcatFlash<First, Second>\nwhere\n    First: AsyncNorFlash<Error = E>,\n    Second: AsyncNorFlash<Error = E>,\n    E: NorFlashError,\n{\n    const WRITE_SIZE: usize = get_write_size(First::WRITE_SIZE, Second::WRITE_SIZE);\n    const ERASE_SIZE: usize = get_max_erase_size(First::ERASE_SIZE, Second::ERASE_SIZE);\n\n    async fn write(&mut self, mut offset: u32, mut bytes: &[u8]) -> Result<(), E> {\n        if offset < self.0.capacity() as u32 {\n            let len = core::cmp::min(self.0.capacity() - offset as usize, bytes.len());\n            self.0.write(offset, &bytes[..len]).await?;\n            offset += len as u32;\n            bytes = &bytes[len..];\n        }\n\n        if !bytes.is_empty() {\n            self.1.write(offset - self.0.capacity() as u32, bytes).await?;\n        }\n\n        Ok(())\n    }\n\n    async fn erase(&mut self, mut from: u32, to: u32) -> Result<(), E> {\n        if from < self.0.capacity() as u32 {\n            let to = core::cmp::min(self.0.capacity() as u32, to);\n            self.0.erase(from, to).await?;\n            from = self.0.capacity() as u32;\n        }\n\n        if from < to {\n            self.1\n                .erase(from - self.0.capacity() as u32, to - self.0.capacity() as u32)\n                .await?;\n        }\n\n        Ok(())\n    }\n}\n\nimpl<First, Second, E> AsyncMultiwriteNorFlash for ConcatFlash<First, Second>\nwhere\n    First: AsyncMultiwriteNorFlash<Error = E>,\n    Second: AsyncMultiwriteNorFlash<Error = E>,\n    E: NorFlashError,\n{\n}\n\n#[cfg(test)]\nmod tests {\n    use embedded_storage::nor_flash::{NorFlash, ReadNorFlash};\n\n    use super::ConcatFlash;\n    use crate::flash::mem_flash::MemFlash;\n\n    #[test]\n    fn can_write_and_read_across_flashes() {\n        let first = MemFlash::<64, 16, 4>::default();\n        let second = MemFlash::<64, 64, 4>::default();\n        let mut f = ConcatFlash::new(first, second);\n\n        f.write(60, &[0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88]).unwrap();\n\n        assert_eq!(&[0x11, 0x22, 0x33, 0x44], &f.0.mem[60..]);\n        assert_eq!(&[0x55, 0x66, 0x77, 0x88], &f.1.mem[0..4]);\n\n        let mut read_buf = [0; 8];\n        f.read(60, &mut read_buf).unwrap();\n\n        assert_eq!(&[0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88], &read_buf);\n    }\n\n    #[test]\n    fn can_erase_across_flashes() {\n        let first = MemFlash::<128, 16, 4>::new(0x00);\n        let second = MemFlash::<128, 64, 4>::new(0x00);\n        let mut f = ConcatFlash::new(first, second);\n\n        f.erase(64, 192).unwrap();\n\n        assert_eq!(&[0x00; 64], &f.0.mem[0..64]);\n        assert_eq!(&[0xff; 64], &f.0.mem[64..128]);\n        assert_eq!(&[0xff; 64], &f.1.mem[0..64]);\n        assert_eq!(&[0x00; 64], &f.1.mem[64..128]);\n    }\n}\n"
  },
  {
    "path": "embassy-embedded-hal/src/flash/mem_flash.rs",
    "content": "use alloc::vec::Vec;\n\nuse embedded_storage::nor_flash::{ErrorType, NorFlash, ReadNorFlash};\nuse embedded_storage_async::nor_flash::{NorFlash as AsyncNorFlash, ReadNorFlash as AsyncReadNorFlash};\n\nextern crate alloc;\n\npub(crate) struct MemFlash<const SIZE: usize, const ERASE_SIZE: usize, const WRITE_SIZE: usize> {\n    pub mem: [u8; SIZE],\n    pub writes: Vec<(u32, usize)>,\n    pub erases: Vec<(u32, u32)>,\n}\n\nimpl<const SIZE: usize, const ERASE_SIZE: usize, const WRITE_SIZE: usize> MemFlash<SIZE, ERASE_SIZE, WRITE_SIZE> {\n    #[allow(unused)]\n    pub const fn new(fill: u8) -> Self {\n        Self {\n            mem: [fill; SIZE],\n            writes: Vec::new(),\n            erases: Vec::new(),\n        }\n    }\n\n    fn read(&mut self, offset: u32, bytes: &mut [u8]) {\n        let len = bytes.len();\n        bytes.copy_from_slice(&self.mem[offset as usize..offset as usize + len]);\n    }\n\n    fn write(&mut self, offset: u32, bytes: &[u8]) {\n        self.writes.push((offset, bytes.len()));\n        let offset = offset as usize;\n        assert_eq!(0, bytes.len() % WRITE_SIZE);\n        assert_eq!(0, offset % WRITE_SIZE);\n        assert!(offset + bytes.len() <= SIZE);\n\n        self.mem[offset..offset + bytes.len()].copy_from_slice(bytes);\n    }\n\n    fn erase(&mut self, from: u32, to: u32) {\n        self.erases.push((from, to));\n        let from = from as usize;\n        let to = to as usize;\n        assert_eq!(0, from % ERASE_SIZE);\n        assert_eq!(0, to % ERASE_SIZE);\n        self.mem[from..to].fill(0xff);\n    }\n}\n\nimpl<const SIZE: usize, const ERASE_SIZE: usize, const WRITE_SIZE: usize> Default\n    for MemFlash<SIZE, ERASE_SIZE, WRITE_SIZE>\n{\n    fn default() -> Self {\n        Self::new(0xff)\n    }\n}\n\nimpl<const SIZE: usize, const ERASE_SIZE: usize, const WRITE_SIZE: usize> ErrorType\n    for MemFlash<SIZE, ERASE_SIZE, WRITE_SIZE>\n{\n    type Error = core::convert::Infallible;\n}\n\nimpl<const SIZE: usize, const ERASE_SIZE: usize, const WRITE_SIZE: usize> ReadNorFlash\n    for MemFlash<SIZE, ERASE_SIZE, WRITE_SIZE>\n{\n    const READ_SIZE: usize = 1;\n\n    fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Self::Error> {\n        self.read(offset, bytes);\n        Ok(())\n    }\n\n    fn capacity(&self) -> usize {\n        SIZE\n    }\n}\n\nimpl<const SIZE: usize, const ERASE_SIZE: usize, const WRITE_SIZE: usize> NorFlash\n    for MemFlash<SIZE, ERASE_SIZE, WRITE_SIZE>\n{\n    const WRITE_SIZE: usize = WRITE_SIZE;\n    const ERASE_SIZE: usize = ERASE_SIZE;\n\n    fn write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Self::Error> {\n        self.write(offset, bytes);\n        Ok(())\n    }\n\n    fn erase(&mut self, from: u32, to: u32) -> Result<(), Self::Error> {\n        self.erase(from, to);\n        Ok(())\n    }\n}\n\nimpl<const SIZE: usize, const ERASE_SIZE: usize, const WRITE_SIZE: usize> AsyncReadNorFlash\n    for MemFlash<SIZE, ERASE_SIZE, WRITE_SIZE>\n{\n    const READ_SIZE: usize = 1;\n\n    async fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Self::Error> {\n        self.read(offset, bytes);\n        Ok(())\n    }\n\n    fn capacity(&self) -> usize {\n        SIZE\n    }\n}\n\nimpl<const SIZE: usize, const ERASE_SIZE: usize, const WRITE_SIZE: usize> AsyncNorFlash\n    for MemFlash<SIZE, ERASE_SIZE, WRITE_SIZE>\n{\n    const WRITE_SIZE: usize = WRITE_SIZE;\n    const ERASE_SIZE: usize = ERASE_SIZE;\n\n    async fn write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Self::Error> {\n        self.write(offset, bytes);\n        Ok(())\n    }\n\n    async fn erase(&mut self, from: u32, to: u32) -> Result<(), Self::Error> {\n        self.erase(from, to);\n        Ok(())\n    }\n}\n"
  },
  {
    "path": "embassy-embedded-hal/src/flash/mod.rs",
    "content": "//! Utilities related to flash.\n\nmod concat_flash;\n#[cfg(test)]\npub(crate) mod mem_flash;\npub mod partition;\n\npub use concat_flash::ConcatFlash;\n"
  },
  {
    "path": "embassy-embedded-hal/src/flash/partition/asynch.rs",
    "content": "use embassy_sync::blocking_mutex::raw::RawMutex;\nuse embassy_sync::mutex::Mutex;\nuse embedded_storage::nor_flash::ErrorType;\nuse embedded_storage_async::nor_flash::{MultiwriteNorFlash, NorFlash, ReadNorFlash};\n\nuse super::Error;\n\n/// A logical partition of an underlying shared flash\n///\n/// A partition holds an offset and a size of the flash,\n/// and is restricted to operate with that range.\n/// There is no guarantee that muliple partitions on the same flash\n/// operate on mutually exclusive ranges - such a separation is up to\n/// the user to guarantee.\npub struct Partition<'a, M: RawMutex, T: NorFlash> {\n    flash: &'a Mutex<M, T>,\n    offset: u32,\n    size: u32,\n}\n\nimpl<'a, M: RawMutex, T: NorFlash> Clone for Partition<'a, M, T> {\n    fn clone(&self) -> Self {\n        Self {\n            flash: self.flash,\n            offset: self.offset,\n            size: self.size,\n        }\n    }\n}\n\nimpl<'a, M: RawMutex, T: NorFlash> Partition<'a, M, T> {\n    /// Create a new partition\n    pub const fn new(flash: &'a Mutex<M, T>, offset: u32, size: u32) -> Self {\n        if offset % T::READ_SIZE as u32 != 0 || offset % T::WRITE_SIZE as u32 != 0 || offset % T::ERASE_SIZE as u32 != 0\n        {\n            panic!(\"Partition offset must be a multiple of read, write and erase size\");\n        }\n        if size % T::READ_SIZE as u32 != 0 || size % T::WRITE_SIZE as u32 != 0 || size % T::ERASE_SIZE as u32 != 0 {\n            panic!(\"Partition size must be a multiple of read, write and erase size\");\n        }\n        Self { flash, offset, size }\n    }\n\n    /// Get the partition offset within the flash\n    pub const fn offset(&self) -> u32 {\n        self.offset\n    }\n\n    /// Get the partition size\n    pub const fn size(&self) -> u32 {\n        self.size\n    }\n}\n\nimpl<M: RawMutex, T: NorFlash> ErrorType for Partition<'_, M, T> {\n    type Error = Error<T::Error>;\n}\n\nimpl<M: RawMutex, T: NorFlash> ReadNorFlash for Partition<'_, M, T> {\n    const READ_SIZE: usize = T::READ_SIZE;\n\n    async fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Self::Error> {\n        if offset + bytes.len() as u32 > self.size {\n            return Err(Error::OutOfBounds);\n        }\n\n        let mut flash = self.flash.lock().await;\n        flash.read(self.offset + offset, bytes).await.map_err(Error::Flash)\n    }\n\n    fn capacity(&self) -> usize {\n        self.size as usize\n    }\n}\n\nimpl<M: RawMutex, T: NorFlash> NorFlash for Partition<'_, M, T> {\n    const WRITE_SIZE: usize = T::WRITE_SIZE;\n    const ERASE_SIZE: usize = T::ERASE_SIZE;\n\n    async fn write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Self::Error> {\n        if offset + bytes.len() as u32 > self.size {\n            return Err(Error::OutOfBounds);\n        }\n\n        let mut flash = self.flash.lock().await;\n        flash.write(self.offset + offset, bytes).await.map_err(Error::Flash)\n    }\n\n    async fn erase(&mut self, from: u32, to: u32) -> Result<(), Self::Error> {\n        if to > self.size {\n            return Err(Error::OutOfBounds);\n        }\n\n        let mut flash = self.flash.lock().await;\n        flash\n            .erase(self.offset + from, self.offset + to)\n            .await\n            .map_err(Error::Flash)\n    }\n}\n\nimpl<M: RawMutex, T: MultiwriteNorFlash> MultiwriteNorFlash for Partition<'_, M, T> {}\n\n#[cfg(test)]\nmod tests {\n    use embassy_sync::blocking_mutex::raw::NoopRawMutex;\n\n    use super::*;\n    use crate::flash::mem_flash::MemFlash;\n\n    #[futures_test::test]\n    async fn can_read() {\n        let mut flash = MemFlash::<1024, 128, 4>::default();\n        flash.mem[132..132 + 8].fill(0xAA);\n\n        let flash = Mutex::<NoopRawMutex, _>::new(flash);\n        let mut partition = Partition::new(&flash, 128, 256);\n\n        let mut read_buf = [0; 8];\n        partition.read(4, &mut read_buf).await.unwrap();\n\n        assert!(!read_buf.iter().any(|&x| x != 0xAA));\n    }\n\n    #[futures_test::test]\n    async fn can_write() {\n        let flash = MemFlash::<1024, 128, 4>::default();\n\n        let flash = Mutex::<NoopRawMutex, _>::new(flash);\n        let mut partition = Partition::new(&flash, 128, 256);\n\n        let write_buf = [0xAA; 8];\n        partition.write(4, &write_buf).await.unwrap();\n\n        let flash = flash.try_lock().unwrap();\n        assert!(!flash.mem[132..132 + 8].iter().any(|&x| x != 0xAA));\n    }\n\n    #[futures_test::test]\n    async fn can_erase() {\n        let flash = MemFlash::<1024, 128, 4>::new(0x00);\n\n        let flash = Mutex::<NoopRawMutex, _>::new(flash);\n        let mut partition = Partition::new(&flash, 128, 256);\n\n        partition.erase(0, 128).await.unwrap();\n\n        let flash = flash.try_lock().unwrap();\n        assert!(!flash.mem[128..256].iter().any(|&x| x != 0xFF));\n    }\n}\n"
  },
  {
    "path": "embassy-embedded-hal/src/flash/partition/blocking.rs",
    "content": "use core::cell::RefCell;\n\nuse embassy_sync::blocking_mutex::Mutex;\nuse embassy_sync::blocking_mutex::raw::RawMutex;\nuse embedded_storage::nor_flash::{ErrorType, MultiwriteNorFlash, NorFlash, ReadNorFlash};\n\nuse super::Error;\n\n/// A logical partition of an underlying shared flash\n///\n/// A partition holds an offset and a size of the flash,\n/// and is restricted to operate with that range.\n/// There is no guarantee that muliple partitions on the same flash\n/// operate on mutually exclusive ranges - such a separation is up to\n/// the user to guarantee.\npub struct BlockingPartition<'a, M: RawMutex, T: NorFlash> {\n    flash: &'a Mutex<M, RefCell<T>>,\n    offset: u32,\n    size: u32,\n}\n\nimpl<'a, M: RawMutex, T: NorFlash> Clone for BlockingPartition<'a, M, T> {\n    fn clone(&self) -> Self {\n        Self {\n            flash: self.flash,\n            offset: self.offset,\n            size: self.size,\n        }\n    }\n}\n\nimpl<'a, M: RawMutex, T: NorFlash> BlockingPartition<'a, M, T> {\n    /// Create a new partition\n    pub const fn new(flash: &'a Mutex<M, RefCell<T>>, offset: u32, size: u32) -> Self {\n        if offset % T::READ_SIZE as u32 != 0 || offset % T::WRITE_SIZE as u32 != 0 || offset % T::ERASE_SIZE as u32 != 0\n        {\n            panic!(\"Partition offset must be a multiple of read, write and erase size\");\n        }\n        if size % T::READ_SIZE as u32 != 0 || size % T::WRITE_SIZE as u32 != 0 || size % T::ERASE_SIZE as u32 != 0 {\n            panic!(\"Partition size must be a multiple of read, write and erase size\");\n        }\n        Self { flash, offset, size }\n    }\n\n    /// Get the partition offset within the flash\n    pub const fn offset(&self) -> u32 {\n        self.offset\n    }\n\n    /// Get the partition size\n    pub const fn size(&self) -> u32 {\n        self.size\n    }\n}\n\nimpl<M: RawMutex, T: NorFlash> ErrorType for BlockingPartition<'_, M, T> {\n    type Error = Error<T::Error>;\n}\n\nimpl<M: RawMutex, T: NorFlash> ReadNorFlash for BlockingPartition<'_, M, T> {\n    const READ_SIZE: usize = T::READ_SIZE;\n\n    fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Self::Error> {\n        if offset + bytes.len() as u32 > self.size {\n            return Err(Error::OutOfBounds);\n        }\n\n        self.flash.lock(|flash| {\n            flash\n                .borrow_mut()\n                .read(self.offset + offset, bytes)\n                .map_err(Error::Flash)\n        })\n    }\n\n    fn capacity(&self) -> usize {\n        self.size as usize\n    }\n}\n\nimpl<M: RawMutex, T: NorFlash> NorFlash for BlockingPartition<'_, M, T> {\n    const WRITE_SIZE: usize = T::WRITE_SIZE;\n    const ERASE_SIZE: usize = T::ERASE_SIZE;\n\n    fn write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Self::Error> {\n        if offset + bytes.len() as u32 > self.size {\n            return Err(Error::OutOfBounds);\n        }\n\n        self.flash.lock(|flash| {\n            flash\n                .borrow_mut()\n                .write(self.offset + offset, bytes)\n                .map_err(Error::Flash)\n        })\n    }\n\n    fn erase(&mut self, from: u32, to: u32) -> Result<(), Self::Error> {\n        if to > self.size {\n            return Err(Error::OutOfBounds);\n        }\n\n        self.flash.lock(|flash| {\n            flash\n                .borrow_mut()\n                .erase(self.offset + from, self.offset + to)\n                .map_err(Error::Flash)\n        })\n    }\n}\n\nimpl<M: RawMutex, T: MultiwriteNorFlash> MultiwriteNorFlash for BlockingPartition<'_, M, T> {}\n\n#[cfg(test)]\nmod tests {\n    use embassy_sync::blocking_mutex::raw::NoopRawMutex;\n\n    use super::*;\n    use crate::flash::mem_flash::MemFlash;\n\n    #[test]\n    fn can_read() {\n        let mut flash = MemFlash::<1024, 128, 4>::default();\n        flash.mem[132..132 + 8].fill(0xAA);\n\n        let flash = Mutex::<NoopRawMutex, _>::new(RefCell::new(flash));\n        let mut partition = BlockingPartition::new(&flash, 128, 256);\n\n        let mut read_buf = [0; 8];\n        partition.read(4, &mut read_buf).unwrap();\n\n        assert!(!read_buf.iter().any(|&x| x != 0xAA));\n    }\n\n    #[test]\n    fn can_write() {\n        let flash = MemFlash::<1024, 128, 4>::default();\n\n        let flash = Mutex::<NoopRawMutex, _>::new(RefCell::new(flash));\n        let mut partition = BlockingPartition::new(&flash, 128, 256);\n\n        let write_buf = [0xAA; 8];\n        partition.write(4, &write_buf).unwrap();\n\n        let flash = flash.into_inner().take();\n        assert!(!flash.mem[132..132 + 8].iter().any(|&x| x != 0xAA));\n    }\n\n    #[test]\n    fn can_erase() {\n        let flash = MemFlash::<1024, 128, 4>::new(0x00);\n\n        let flash = Mutex::<NoopRawMutex, _>::new(RefCell::new(flash));\n        let mut partition = BlockingPartition::new(&flash, 128, 256);\n\n        partition.erase(0, 128).unwrap();\n\n        let flash = flash.into_inner().take();\n        assert!(!flash.mem[128..256].iter().any(|&x| x != 0xFF));\n    }\n}\n"
  },
  {
    "path": "embassy-embedded-hal/src/flash/partition/mod.rs",
    "content": "//! Flash Partition utilities\n\nuse embedded_storage::nor_flash::{NorFlashError, NorFlashErrorKind};\n\nmod asynch;\nmod blocking;\n\npub use asynch::Partition;\npub use blocking::BlockingPartition;\n\n/// Partition error\n#[derive(Debug, PartialEq, Eq)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub enum Error<T> {\n    /// The requested flash area is outside the partition\n    OutOfBounds,\n    /// Underlying flash error\n    Flash(T),\n}\n\nimpl<T: NorFlashError> NorFlashError for Error<T> {\n    fn kind(&self) -> NorFlashErrorKind {\n        match self {\n            Error::OutOfBounds => NorFlashErrorKind::OutOfBounds,\n            Error::Flash(f) => f.kind(),\n        }\n    }\n}\n"
  },
  {
    "path": "embassy-embedded-hal/src/lib.rs",
    "content": "#![no_std]\n#![allow(async_fn_in_trait)]\n#![warn(missing_docs)]\n#![doc = include_str!(\"../README.md\")]\n\npub mod adapter;\npub mod flash;\npub mod shared_bus;\n\n/// Set the configuration of a peripheral driver.\n///\n/// This trait is intended to be implemented by peripheral drivers such as SPI\n/// and I2C. It allows changing the configuration at runtime.\n///\n/// The exact type of the \"configuration\" is defined by each individual driver, since different\n/// drivers support different options. Therefore it is defined as an associated type.\n///\n/// For example, it is used by [`SpiDeviceWithConfig`](crate::shared_bus::asynch::spi::SpiDeviceWithConfig) and\n///  [`I2cDeviceWithConfig`](crate::shared_bus::asynch::i2c::I2cDeviceWithConfig) to allow different\n/// devices on the same bus to use different communication settings.\npub trait SetConfig {\n    /// The configuration type used by this driver.\n    type Config;\n\n    /// The error type that can occur if `set_config` fails.\n    type ConfigError;\n\n    /// Set the configuration of the driver.\n    fn set_config(&mut self, config: &Self::Config) -> Result<(), Self::ConfigError>;\n}\n\n/// Get the configuration of a peripheral driver.\npub trait GetConfig {\n    /// The configuration type used by this driver.\n    type Config;\n\n    /// Get the configuration of the driver.\n    fn get_config(&self) -> Self::Config;\n}\n"
  },
  {
    "path": "embassy-embedded-hal/src/shared_bus/asynch/i2c.rs",
    "content": "//! Asynchronous shared I2C bus\n//!\n//! # Example (nrf52)\n//!\n//! ```rust,ignore\n//! use embassy_embedded_hal::shared_bus::asynch::i2c::I2cDevice;\n//! use embassy_sync::mutex::Mutex;\n//! use embassy_sync::blocking_mutex::raw::NoopRawMutex;\n//!\n//! static I2C_BUS: StaticCell<Mutex<NoopRawMutex, Twim<TWISPI0>>> = StaticCell::new();\n//! let config = twim::Config::default();\n//! let i2c = Twim::new(p.TWISPI0, Irqs, p.P0_03, p.P0_04, config);\n//! let i2c_bus = Mutex::new(i2c);\n//! let i2c_bus = I2C_BUS.init(i2c_bus);\n//!\n//! // Device 1, using embedded-hal-async compatible driver for QMC5883L compass\n//! let i2c_dev1 = I2cDevice::new(i2c_bus);\n//! let compass = QMC5883L::new(i2c_dev1).await.unwrap();\n//!\n//! // Device 2, using embedded-hal-async compatible driver for Mpu6050 accelerometer\n//! let i2c_dev2 = I2cDevice::new(i2c_bus);\n//! let mpu = Mpu6050::new(i2c_dev2);\n//! ```\n\nuse embassy_sync::blocking_mutex::raw::RawMutex;\nuse embassy_sync::mutex::Mutex;\nuse embedded_hal_async::i2c;\n\nuse crate::SetConfig;\nuse crate::shared_bus::I2cDeviceError;\n\n/// I2C device on a shared bus.\npub struct I2cDevice<'a, M: RawMutex, BUS> {\n    bus: &'a Mutex<M, BUS>,\n}\n\nimpl<'a, M: RawMutex, BUS> I2cDevice<'a, M, BUS> {\n    /// Create a new `I2cDevice`.\n    pub fn new(bus: &'a Mutex<M, BUS>) -> Self {\n        Self { bus }\n    }\n}\n\nimpl<'a, M: RawMutex, BUS> Clone for I2cDevice<'a, M, BUS> {\n    fn clone(&self) -> Self {\n        Self { bus: self.bus }\n    }\n}\n\nimpl<'a, M: RawMutex, BUS> i2c::ErrorType for I2cDevice<'a, M, BUS>\nwhere\n    BUS: i2c::ErrorType,\n{\n    type Error = I2cDeviceError<BUS::Error>;\n}\n\nimpl<M, BUS> i2c::I2c for I2cDevice<'_, M, BUS>\nwhere\n    M: RawMutex,\n    BUS: i2c::I2c,\n{\n    async fn read(&mut self, address: u8, read: &mut [u8]) -> Result<(), I2cDeviceError<BUS::Error>> {\n        let mut bus = self.bus.lock().await;\n        bus.read(address, read).await.map_err(I2cDeviceError::I2c)?;\n        Ok(())\n    }\n\n    async fn write(&mut self, address: u8, write: &[u8]) -> Result<(), I2cDeviceError<BUS::Error>> {\n        let mut bus = self.bus.lock().await;\n        bus.write(address, write).await.map_err(I2cDeviceError::I2c)?;\n        Ok(())\n    }\n\n    async fn write_read(\n        &mut self,\n        address: u8,\n        write: &[u8],\n        read: &mut [u8],\n    ) -> Result<(), I2cDeviceError<BUS::Error>> {\n        let mut bus = self.bus.lock().await;\n        bus.write_read(address, write, read)\n            .await\n            .map_err(I2cDeviceError::I2c)?;\n        Ok(())\n    }\n\n    async fn transaction(\n        &mut self,\n        address: u8,\n        operations: &mut [embedded_hal_async::i2c::Operation<'_>],\n    ) -> Result<(), I2cDeviceError<BUS::Error>> {\n        let mut bus = self.bus.lock().await;\n        bus.transaction(address, operations)\n            .await\n            .map_err(I2cDeviceError::I2c)?;\n        Ok(())\n    }\n}\n\n/// I2C device on a shared bus, with its own configuration.\n///\n/// This is like [`I2cDevice`], with an additional bus configuration that's applied\n/// to the bus before each use using [`SetConfig`]. This allows different\n/// devices on the same bus to use different communication settings.\npub struct I2cDeviceWithConfig<'a, M: RawMutex, BUS: SetConfig> {\n    bus: &'a Mutex<M, BUS>,\n    config: BUS::Config,\n}\n\nimpl<'a, M: RawMutex, BUS: SetConfig> I2cDeviceWithConfig<'a, M, BUS> {\n    /// Create a new `I2cDeviceWithConfig`.\n    pub fn new(bus: &'a Mutex<M, BUS>, config: BUS::Config) -> Self {\n        Self { bus, config }\n    }\n\n    /// Change the device's config at runtime\n    pub fn set_config(&mut self, config: BUS::Config) {\n        self.config = config;\n    }\n}\n\nimpl<'a, M: RawMutex, BUS: SetConfig> Clone for I2cDeviceWithConfig<'a, M, BUS>\nwhere\n    BUS::Config: Clone,\n{\n    fn clone(&self) -> Self {\n        Self {\n            bus: self.bus,\n            config: self.config.clone(),\n        }\n    }\n}\n\nimpl<'a, M, BUS> i2c::ErrorType for I2cDeviceWithConfig<'a, M, BUS>\nwhere\n    BUS: i2c::ErrorType,\n    M: RawMutex,\n    BUS: SetConfig,\n{\n    type Error = I2cDeviceError<BUS::Error>;\n}\n\nimpl<M, BUS> i2c::I2c for I2cDeviceWithConfig<'_, M, BUS>\nwhere\n    M: RawMutex,\n    BUS: i2c::I2c + SetConfig,\n{\n    async fn read(&mut self, address: u8, buffer: &mut [u8]) -> Result<(), I2cDeviceError<BUS::Error>> {\n        let mut bus = self.bus.lock().await;\n        bus.set_config(&self.config).map_err(|_| I2cDeviceError::Config)?;\n        bus.read(address, buffer).await.map_err(I2cDeviceError::I2c)?;\n        Ok(())\n    }\n\n    async fn write(&mut self, address: u8, bytes: &[u8]) -> Result<(), I2cDeviceError<BUS::Error>> {\n        let mut bus = self.bus.lock().await;\n        bus.set_config(&self.config).map_err(|_| I2cDeviceError::Config)?;\n        bus.write(address, bytes).await.map_err(I2cDeviceError::I2c)?;\n        Ok(())\n    }\n\n    async fn write_read(\n        &mut self,\n        address: u8,\n        wr_buffer: &[u8],\n        rd_buffer: &mut [u8],\n    ) -> Result<(), I2cDeviceError<BUS::Error>> {\n        let mut bus = self.bus.lock().await;\n        bus.set_config(&self.config).map_err(|_| I2cDeviceError::Config)?;\n        bus.write_read(address, wr_buffer, rd_buffer)\n            .await\n            .map_err(I2cDeviceError::I2c)?;\n        Ok(())\n    }\n\n    async fn transaction(&mut self, address: u8, operations: &mut [i2c::Operation<'_>]) -> Result<(), Self::Error> {\n        let mut bus = self.bus.lock().await;\n        bus.set_config(&self.config).map_err(|_| I2cDeviceError::Config)?;\n        bus.transaction(address, operations)\n            .await\n            .map_err(I2cDeviceError::I2c)?;\n        Ok(())\n    }\n}\n"
  },
  {
    "path": "embassy-embedded-hal/src/shared_bus/asynch/mod.rs",
    "content": "//! Asynchronous shared bus implementations for embedded-hal-async\npub mod i2c;\npub mod spi;\n"
  },
  {
    "path": "embassy-embedded-hal/src/shared_bus/asynch/spi.rs",
    "content": "//! Asynchronous shared SPI bus\n//!\n//! # Example (nrf52)\n//!\n//! ```rust,ignore\n//! use embassy_embedded_hal::shared_bus::spi::SpiDevice;\n//! use embassy_sync::mutex::Mutex;\n//! use embassy_sync::blocking_mutex::raw::NoopRawMutex;\n//!\n//! static SPI_BUS: StaticCell<Mutex<NoopRawMutex, spim::Spim<SPI3>>> = StaticCell::new();\n//! let mut config = spim::Config::default();\n//! config.frequency = spim::Frequency::M32;\n//! let spi = spim::Spim::new_txonly(p.SPI3, Irqs, p.P0_15, p.P0_18, config);\n//! let spi_bus = Mutex::new(spi);\n//! let spi_bus = SPI_BUS.init(spi_bus);\n//!\n//! // Device 1, using embedded-hal-async compatible driver for ST7735 LCD display\n//! let cs_pin1 = Output::new(p.P0_24, Level::Low, OutputDrive::Standard);\n//! let spi_dev1 = SpiDevice::new(spi_bus, cs_pin1);\n//! let display1 = ST7735::new(spi_dev1, dc1, rst1, Default::default(), 160, 128);\n//!\n//! // Device 2\n//! let cs_pin2 = Output::new(p.P0_24, Level::Low, OutputDrive::Standard);\n//! let spi_dev2 = SpiDevice::new(spi_bus, cs_pin2);\n//! let display2 = ST7735::new(spi_dev2, dc2, rst2, Default::default(), 160, 128);\n//! ```\n\nuse embassy_hal_internal::drop::OnDrop;\nuse embassy_sync::blocking_mutex::raw::RawMutex;\nuse embassy_sync::mutex::Mutex;\nuse embedded_hal_1::digital::OutputPin;\nuse embedded_hal_1::spi::Operation;\nuse embedded_hal_async::spi;\n\nuse crate::SetConfig;\nuse crate::shared_bus::SpiDeviceError;\n\n/// SPI device on a shared bus.\npub struct SpiDevice<'a, M: RawMutex, BUS, CS> {\n    bus: &'a Mutex<M, BUS>,\n    cs: CS,\n}\n\nimpl<'a, M: RawMutex, BUS, CS> SpiDevice<'a, M, BUS, CS> {\n    /// Create a new `SpiDevice`.\n    pub fn new(bus: &'a Mutex<M, BUS>, cs: CS) -> Self {\n        Self { bus, cs }\n    }\n}\n\nimpl<'a, M: RawMutex, BUS, CS> spi::ErrorType for SpiDevice<'a, M, BUS, CS>\nwhere\n    BUS: spi::ErrorType,\n    CS: OutputPin,\n{\n    type Error = SpiDeviceError<BUS::Error, CS::Error>;\n}\n\nimpl<M, BUS, CS, Word> spi::SpiDevice<Word> for SpiDevice<'_, M, BUS, CS>\nwhere\n    M: RawMutex,\n    BUS: spi::SpiBus<Word>,\n    CS: OutputPin,\n    Word: Copy + 'static,\n{\n    async fn transaction(&mut self, operations: &mut [spi::Operation<'_, Word>]) -> Result<(), Self::Error> {\n        if cfg!(not(feature = \"time\")) && operations.iter().any(|op| matches!(op, Operation::DelayNs(_))) {\n            return Err(SpiDeviceError::DelayNotSupported);\n        }\n\n        let mut bus = self.bus.lock().await;\n        self.cs.set_low().map_err(SpiDeviceError::Cs)?;\n\n        let cs_drop = OnDrop::new(|| {\n            // This drop guard deasserts CS pin if the async operation is cancelled.\n            // Errors are ignored in this drop handler, as there's nothing we can do about them.\n            // If the async operation is completed without cancellation, this handler will not\n            // be run, and the CS pin will be deasserted with proper error handling.\n            let _ = self.cs.set_high();\n        });\n\n        let op_res = 'ops: {\n            for op in operations {\n                let res = match op {\n                    Operation::Read(buf) => bus.read(buf).await,\n                    Operation::Write(buf) => bus.write(buf).await,\n                    Operation::Transfer(read, write) => bus.transfer(read, write).await,\n                    Operation::TransferInPlace(buf) => bus.transfer_in_place(buf).await,\n                    #[cfg(not(feature = \"time\"))]\n                    Operation::DelayNs(_) => unreachable!(),\n                    #[cfg(feature = \"time\")]\n                    Operation::DelayNs(ns) => match bus.flush().await {\n                        Err(e) => Err(e),\n                        Ok(()) => {\n                            embassy_time::Timer::after_nanos(*ns as _).await;\n                            Ok(())\n                        }\n                    },\n                };\n                if let Err(e) = res {\n                    break 'ops Err(e);\n                }\n            }\n            Ok(())\n        };\n\n        // On failure, it's important to still flush and deassert CS.\n        let flush_res = bus.flush().await;\n\n        // Now that all the async operations are done, we defuse the CS guard,\n        // and manually set the CS pin low (to better handle the possible errors).\n        cs_drop.defuse();\n        let cs_res = self.cs.set_high();\n\n        op_res.map_err(SpiDeviceError::Spi)?;\n        flush_res.map_err(SpiDeviceError::Spi)?;\n        cs_res.map_err(SpiDeviceError::Cs)?;\n\n        Ok(())\n    }\n}\n\n/// SPI device on a shared bus, with its own configuration.\n///\n/// This is like [`SpiDevice`], with an additional bus configuration that's applied\n/// to the bus before each use using [`SetConfig`]. This allows different\n/// devices on the same bus to use different communication settings.\npub struct SpiDeviceWithConfig<'a, M: RawMutex, BUS: SetConfig, CS> {\n    bus: &'a Mutex<M, BUS>,\n    cs: CS,\n    config: BUS::Config,\n}\n\nimpl<'a, M: RawMutex, BUS: SetConfig, CS> SpiDeviceWithConfig<'a, M, BUS, CS> {\n    /// Create a new `SpiDeviceWithConfig`.\n    pub fn new(bus: &'a Mutex<M, BUS>, cs: CS, config: BUS::Config) -> Self {\n        Self { bus, cs, config }\n    }\n\n    /// Change the device's config at runtime\n    pub fn set_config(&mut self, config: BUS::Config) {\n        self.config = config;\n    }\n}\n\nimpl<'a, M, BUS, CS> spi::ErrorType for SpiDeviceWithConfig<'a, M, BUS, CS>\nwhere\n    BUS: spi::ErrorType + SetConfig,\n    CS: OutputPin,\n    M: RawMutex,\n{\n    type Error = SpiDeviceError<BUS::Error, CS::Error>;\n}\n\nimpl<M, BUS, CS, Word> spi::SpiDevice<Word> for SpiDeviceWithConfig<'_, M, BUS, CS>\nwhere\n    M: RawMutex,\n    BUS: spi::SpiBus<Word> + SetConfig,\n    CS: OutputPin,\n    Word: Copy + 'static,\n{\n    async fn transaction(&mut self, operations: &mut [spi::Operation<'_, Word>]) -> Result<(), Self::Error> {\n        if cfg!(not(feature = \"time\")) && operations.iter().any(|op| matches!(op, Operation::DelayNs(_))) {\n            return Err(SpiDeviceError::DelayNotSupported);\n        }\n\n        let mut bus = self.bus.lock().await;\n        bus.set_config(&self.config).map_err(|_| SpiDeviceError::Config)?;\n        self.cs.set_low().map_err(SpiDeviceError::Cs)?;\n\n        let cs_drop = OnDrop::new(|| {\n            // Please see comment in SpiDevice for an explanation of this drop handler.\n            let _ = self.cs.set_high();\n        });\n\n        let op_res = 'ops: {\n            for op in operations {\n                let res = match op {\n                    Operation::Read(buf) => bus.read(buf).await,\n                    Operation::Write(buf) => bus.write(buf).await,\n                    Operation::Transfer(read, write) => bus.transfer(read, write).await,\n                    Operation::TransferInPlace(buf) => bus.transfer_in_place(buf).await,\n                    #[cfg(not(feature = \"time\"))]\n                    Operation::DelayNs(_) => unreachable!(),\n                    #[cfg(feature = \"time\")]\n                    Operation::DelayNs(ns) => match bus.flush().await {\n                        Err(e) => Err(e),\n                        Ok(()) => {\n                            embassy_time::Timer::after_nanos(*ns as _).await;\n                            Ok(())\n                        }\n                    },\n                };\n                if let Err(e) = res {\n                    break 'ops Err(e);\n                }\n            }\n            Ok(())\n        };\n\n        // On failure, it's important to still flush and deassert CS.\n        let flush_res = bus.flush().await;\n        cs_drop.defuse();\n        let cs_res = self.cs.set_high();\n\n        op_res.map_err(SpiDeviceError::Spi)?;\n        flush_res.map_err(SpiDeviceError::Spi)?;\n        cs_res.map_err(SpiDeviceError::Cs)?;\n\n        Ok(())\n    }\n}\n"
  },
  {
    "path": "embassy-embedded-hal/src/shared_bus/blocking/i2c.rs",
    "content": "//! Blocking shared I2C bus\n//!\n//! # Example (nrf52)\n//!\n//! ```rust,ignore\n//! use embassy_embedded_hal::shared_bus::blocking::i2c::I2cDevice;\n//! use embassy_sync::blocking_mutex::{NoopMutex, raw::NoopRawMutex};\n//!\n//! static I2C_BUS: StaticCell<NoopMutex<RefCell<Twim<TWISPI0>>>> = StaticCell::new();\n//! let i2c = Twim::new(p.TWISPI0, Irqs, p.P0_03, p.P0_04, Config::default());\n//! let i2c_bus = NoopMutex::new(RefCell::new(i2c));\n//! let i2c_bus = I2C_BUS.init(i2c_bus);\n//!\n//! let i2c_dev1 = I2cDevice::new(i2c_bus);\n//! let mpu = Mpu6050::new(i2c_dev1);\n//! ```\n\nuse core::cell::RefCell;\n\nuse embassy_sync::blocking_mutex::Mutex;\nuse embassy_sync::blocking_mutex::raw::RawMutex;\nuse embedded_hal_1::i2c::{ErrorType, I2c, Operation};\n\nuse crate::SetConfig;\nuse crate::shared_bus::I2cDeviceError;\n\n/// I2C device on a shared bus.\npub struct I2cDevice<'a, M: RawMutex, BUS> {\n    bus: &'a Mutex<M, RefCell<BUS>>,\n}\n\nimpl<'a, M: RawMutex, BUS> I2cDevice<'a, M, BUS> {\n    /// Create a new `I2cDevice`.\n    pub fn new(bus: &'a Mutex<M, RefCell<BUS>>) -> Self {\n        Self { bus }\n    }\n}\n\nimpl<'a, M: RawMutex, BUS> Clone for I2cDevice<'a, M, BUS> {\n    fn clone(&self) -> Self {\n        Self { bus: self.bus }\n    }\n}\n\nimpl<'a, M: RawMutex, BUS> ErrorType for I2cDevice<'a, M, BUS>\nwhere\n    BUS: ErrorType,\n{\n    type Error = I2cDeviceError<BUS::Error>;\n}\n\nimpl<M, BUS> I2c for I2cDevice<'_, M, BUS>\nwhere\n    M: RawMutex,\n    BUS: I2c,\n{\n    fn read(&mut self, address: u8, buffer: &mut [u8]) -> Result<(), Self::Error> {\n        self.bus\n            .lock(|bus| bus.borrow_mut().read(address, buffer).map_err(I2cDeviceError::I2c))\n    }\n\n    fn write(&mut self, address: u8, bytes: &[u8]) -> Result<(), Self::Error> {\n        self.bus\n            .lock(|bus| bus.borrow_mut().write(address, bytes).map_err(I2cDeviceError::I2c))\n    }\n\n    fn write_read(&mut self, address: u8, wr_buffer: &[u8], rd_buffer: &mut [u8]) -> Result<(), Self::Error> {\n        self.bus.lock(|bus| {\n            bus.borrow_mut()\n                .write_read(address, wr_buffer, rd_buffer)\n                .map_err(I2cDeviceError::I2c)\n        })\n    }\n\n    fn transaction<'a>(&mut self, address: u8, operations: &mut [Operation<'a>]) -> Result<(), Self::Error> {\n        self.bus.lock(|bus| {\n            bus.borrow_mut()\n                .transaction(address, operations)\n                .map_err(I2cDeviceError::I2c)\n        })\n    }\n}\n\nimpl<M, BUS, E> embedded_hal_02::blocking::i2c::Write for I2cDevice<'_, M, BUS>\nwhere\n    M: RawMutex,\n    BUS: embedded_hal_02::blocking::i2c::Write<Error = E>,\n{\n    type Error = I2cDeviceError<E>;\n\n    fn write(&mut self, addr: u8, bytes: &[u8]) -> Result<(), Self::Error> {\n        self.bus\n            .lock(|bus| bus.borrow_mut().write(addr, bytes).map_err(I2cDeviceError::I2c))\n    }\n}\n\nimpl<M, BUS, E> embedded_hal_02::blocking::i2c::Read for I2cDevice<'_, M, BUS>\nwhere\n    M: RawMutex,\n    BUS: embedded_hal_02::blocking::i2c::Read<Error = E>,\n{\n    type Error = I2cDeviceError<E>;\n\n    fn read(&mut self, addr: u8, bytes: &mut [u8]) -> Result<(), Self::Error> {\n        self.bus\n            .lock(|bus| bus.borrow_mut().read(addr, bytes).map_err(I2cDeviceError::I2c))\n    }\n}\n\nimpl<M, BUS, E> embedded_hal_02::blocking::i2c::WriteRead for I2cDevice<'_, M, BUS>\nwhere\n    M: RawMutex,\n    BUS: embedded_hal_02::blocking::i2c::WriteRead<Error = E>,\n{\n    type Error = I2cDeviceError<E>;\n\n    fn write_read<'w>(&mut self, addr: u8, bytes: &'w [u8], buffer: &'w mut [u8]) -> Result<(), Self::Error> {\n        self.bus.lock(|bus| {\n            bus.borrow_mut()\n                .write_read(addr, bytes, buffer)\n                .map_err(I2cDeviceError::I2c)\n        })\n    }\n}\n\n/// I2C device on a shared bus, with its own configuration.\n///\n/// This is like [`I2cDevice`], with an additional bus configuration that's applied\n/// to the bus before each use using [`SetConfig`]. This allows different\n/// devices on the same bus to use different communication settings.\npub struct I2cDeviceWithConfig<'a, M: RawMutex, BUS: SetConfig> {\n    bus: &'a Mutex<M, RefCell<BUS>>,\n    config: BUS::Config,\n}\n\nimpl<'a, M: RawMutex, BUS: SetConfig> I2cDeviceWithConfig<'a, M, BUS> {\n    /// Create a new `I2cDeviceWithConfig`.\n    pub fn new(bus: &'a Mutex<M, RefCell<BUS>>, config: BUS::Config) -> Self {\n        Self { bus, config }\n    }\n\n    /// Change the device's config at runtime\n    pub fn set_config(&mut self, config: BUS::Config) {\n        self.config = config;\n    }\n}\n\nimpl<'a, M: RawMutex, BUS: SetConfig> Clone for I2cDeviceWithConfig<'a, M, BUS>\nwhere\n    BUS::Config: Clone,\n{\n    fn clone(&self) -> Self {\n        Self {\n            bus: self.bus,\n            config: self.config.clone(),\n        }\n    }\n}\n\nimpl<'a, M, BUS> ErrorType for I2cDeviceWithConfig<'a, M, BUS>\nwhere\n    M: RawMutex,\n    BUS: ErrorType + SetConfig,\n{\n    type Error = I2cDeviceError<BUS::Error>;\n}\n\nimpl<M, BUS> I2c for I2cDeviceWithConfig<'_, M, BUS>\nwhere\n    M: RawMutex,\n    BUS: I2c + SetConfig,\n{\n    fn read(&mut self, address: u8, buffer: &mut [u8]) -> Result<(), Self::Error> {\n        self.bus.lock(|bus| {\n            let mut bus = bus.borrow_mut();\n            bus.set_config(&self.config).map_err(|_| I2cDeviceError::Config)?;\n            bus.read(address, buffer).map_err(I2cDeviceError::I2c)\n        })\n    }\n\n    fn write(&mut self, address: u8, bytes: &[u8]) -> Result<(), Self::Error> {\n        self.bus.lock(|bus| {\n            let mut bus = bus.borrow_mut();\n            bus.set_config(&self.config).map_err(|_| I2cDeviceError::Config)?;\n            bus.write(address, bytes).map_err(I2cDeviceError::I2c)\n        })\n    }\n\n    fn write_read(&mut self, address: u8, wr_buffer: &[u8], rd_buffer: &mut [u8]) -> Result<(), Self::Error> {\n        self.bus.lock(|bus| {\n            let mut bus = bus.borrow_mut();\n            bus.set_config(&self.config).map_err(|_| I2cDeviceError::Config)?;\n            bus.write_read(address, wr_buffer, rd_buffer)\n                .map_err(I2cDeviceError::I2c)\n        })\n    }\n\n    fn transaction<'a>(&mut self, address: u8, operations: &mut [Operation<'a>]) -> Result<(), Self::Error> {\n        self.bus.lock(|bus| {\n            let mut bus = bus.borrow_mut();\n            bus.set_config(&self.config).map_err(|_| I2cDeviceError::Config)?;\n            bus.transaction(address, operations).map_err(I2cDeviceError::I2c)\n        })\n    }\n}\n"
  },
  {
    "path": "embassy-embedded-hal/src/shared_bus/blocking/mod.rs",
    "content": "//! Blocking shared bus implementations for embedded-hal\npub mod i2c;\npub mod spi;\n"
  },
  {
    "path": "embassy-embedded-hal/src/shared_bus/blocking/spi.rs",
    "content": "//! Blocking shared SPI bus\n//!\n//! # Example (nrf52)\n//!\n//! ```rust,ignore\n//! use embassy_embedded_hal::shared_bus::blocking::spi::SpiDevice;\n//! use embassy_sync::blocking_mutex::{NoopMutex, raw::NoopRawMutex};\n//!\n//! static SPI_BUS: StaticCell<NoopMutex<RefCell<Spim<SPI3>>>> = StaticCell::new();\n//! let spi = Spim::new_txonly(p.SPI3, Irqs, p.P0_15, p.P0_18, Config::default());\n//! let spi_bus = NoopMutex::new(RefCell::new(spi));\n//! let spi_bus = SPI_BUS.init(spi_bus);\n//!\n//! // Device 1, using embedded-hal compatible driver for ST7735 LCD display\n//! let cs_pin1 = Output::new(p.P0_24, Level::Low, OutputDrive::Standard);\n//! let spi_dev1 = SpiDevice::new(spi_bus, cs_pin1);\n//! let display1 = ST7735::new(spi_dev1, dc1, rst1, Default::default(), false, 160, 128);\n//! ```\n\nuse core::cell::RefCell;\n\nuse embassy_sync::blocking_mutex::Mutex;\nuse embassy_sync::blocking_mutex::raw::RawMutex;\nuse embedded_hal_1::digital::OutputPin;\nuse embedded_hal_1::spi::{self, Operation, SpiBus};\n\nuse crate::SetConfig;\nuse crate::shared_bus::SpiDeviceError;\n\n/// SPI device on a shared bus.\npub struct SpiDevice<'a, M: RawMutex, BUS, CS> {\n    bus: &'a Mutex<M, RefCell<BUS>>,\n    cs: CS,\n}\n\nimpl<'a, M: RawMutex, BUS, CS> SpiDevice<'a, M, BUS, CS> {\n    /// Create a new `SpiDevice`.\n    pub fn new(bus: &'a Mutex<M, RefCell<BUS>>, cs: CS) -> Self {\n        Self { bus, cs }\n    }\n}\n\nimpl<'a, M: RawMutex, BUS, CS> spi::ErrorType for SpiDevice<'a, M, BUS, CS>\nwhere\n    BUS: spi::ErrorType,\n    CS: OutputPin,\n{\n    type Error = SpiDeviceError<BUS::Error, CS::Error>;\n}\n\nimpl<BUS, M, CS, Word> embedded_hal_1::spi::SpiDevice<Word> for SpiDevice<'_, M, BUS, CS>\nwhere\n    M: RawMutex,\n    BUS: SpiBus<Word>,\n    CS: OutputPin,\n    Word: Copy + 'static,\n{\n    fn transaction(&mut self, operations: &mut [embedded_hal_1::spi::Operation<'_, Word>]) -> Result<(), Self::Error> {\n        if cfg!(not(feature = \"time\")) && operations.iter().any(|op| matches!(op, Operation::DelayNs(_))) {\n            return Err(SpiDeviceError::DelayNotSupported);\n        }\n\n        self.bus.lock(|bus| {\n            let mut bus = bus.borrow_mut();\n            self.cs.set_low().map_err(SpiDeviceError::Cs)?;\n\n            let op_res = operations.iter_mut().try_for_each(|op| match op {\n                Operation::Read(buf) => bus.read(buf),\n                Operation::Write(buf) => bus.write(buf),\n                Operation::Transfer(read, write) => bus.transfer(read, write),\n                Operation::TransferInPlace(buf) => bus.transfer_in_place(buf),\n                #[cfg(not(feature = \"time\"))]\n                Operation::DelayNs(_) => unreachable!(),\n                #[cfg(feature = \"time\")]\n                Operation::DelayNs(ns) => {\n                    embassy_time::block_for(embassy_time::Duration::from_nanos(*ns as _));\n                    Ok(())\n                }\n            });\n\n            // On failure, it's important to still flush and deassert CS.\n            let flush_res = bus.flush();\n            let cs_res = self.cs.set_high();\n\n            op_res.map_err(SpiDeviceError::Spi)?;\n            flush_res.map_err(SpiDeviceError::Spi)?;\n            cs_res.map_err(SpiDeviceError::Cs)?;\n\n            Ok(())\n        })\n    }\n}\n\n/// SPI device on a shared bus, with its own configuration.\n///\n/// This is like [`SpiDevice`], with an additional bus configuration that's applied\n/// to the bus before each use using [`SetConfig`]. This allows different\n/// devices on the same bus to use different communication settings.\npub struct SpiDeviceWithConfig<'a, M: RawMutex, BUS: SetConfig, CS> {\n    bus: &'a Mutex<M, RefCell<BUS>>,\n    cs: CS,\n    config: BUS::Config,\n}\n\nimpl<'a, M: RawMutex, BUS: SetConfig, CS> SpiDeviceWithConfig<'a, M, BUS, CS> {\n    /// Create a new `SpiDeviceWithConfig`.\n    pub fn new(bus: &'a Mutex<M, RefCell<BUS>>, cs: CS, config: BUS::Config) -> Self {\n        Self { bus, cs, config }\n    }\n\n    /// Change the device's config at runtime\n    pub fn set_config(&mut self, config: BUS::Config) {\n        self.config = config;\n    }\n}\n\nimpl<'a, M, BUS, CS> spi::ErrorType for SpiDeviceWithConfig<'a, M, BUS, CS>\nwhere\n    M: RawMutex,\n    BUS: spi::ErrorType + SetConfig,\n    CS: OutputPin,\n{\n    type Error = SpiDeviceError<BUS::Error, CS::Error>;\n}\n\nimpl<BUS, M, CS, Word> embedded_hal_1::spi::SpiDevice<Word> for SpiDeviceWithConfig<'_, M, BUS, CS>\nwhere\n    M: RawMutex,\n    BUS: SpiBus<Word> + SetConfig,\n    CS: OutputPin,\n    Word: Copy + 'static,\n{\n    fn transaction(&mut self, operations: &mut [embedded_hal_1::spi::Operation<'_, Word>]) -> Result<(), Self::Error> {\n        if cfg!(not(feature = \"time\")) && operations.iter().any(|op| matches!(op, Operation::DelayNs(_))) {\n            return Err(SpiDeviceError::DelayNotSupported);\n        }\n\n        self.bus.lock(|bus| {\n            let mut bus = bus.borrow_mut();\n            bus.set_config(&self.config).map_err(|_| SpiDeviceError::Config)?;\n            self.cs.set_low().map_err(SpiDeviceError::Cs)?;\n\n            let op_res = operations.iter_mut().try_for_each(|op| match op {\n                Operation::Read(buf) => bus.read(buf),\n                Operation::Write(buf) => bus.write(buf),\n                Operation::Transfer(read, write) => bus.transfer(read, write),\n                Operation::TransferInPlace(buf) => bus.transfer_in_place(buf),\n                #[cfg(not(feature = \"time\"))]\n                Operation::DelayNs(_) => unreachable!(),\n                #[cfg(feature = \"time\")]\n                Operation::DelayNs(ns) => {\n                    embassy_time::block_for(embassy_time::Duration::from_nanos(*ns as _));\n                    Ok(())\n                }\n            });\n\n            // On failure, it's important to still flush and deassert CS.\n            let flush_res = bus.flush();\n            let cs_res = self.cs.set_high();\n\n            op_res.map_err(SpiDeviceError::Spi)?;\n            flush_res.map_err(SpiDeviceError::Spi)?;\n            cs_res.map_err(SpiDeviceError::Cs)?;\n            Ok(())\n        })\n    }\n}\n"
  },
  {
    "path": "embassy-embedded-hal/src/shared_bus/mod.rs",
    "content": "//! Shared bus implementations\nuse core::fmt::Debug;\n\nuse embedded_hal_1::{i2c, spi};\n\npub mod asynch;\npub mod blocking;\n\n/// Error returned by I2C device implementations in this crate.\n#[derive(Copy, Clone, Eq, PartialEq, Debug)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub enum I2cDeviceError<BUS> {\n    /// An operation on the inner I2C bus failed.\n    I2c(BUS),\n    /// Configuration of the inner I2C bus failed.\n    Config,\n}\n\nimpl<BUS> i2c::Error for I2cDeviceError<BUS>\nwhere\n    BUS: i2c::Error + Debug,\n{\n    fn kind(&self) -> i2c::ErrorKind {\n        match self {\n            Self::I2c(e) => e.kind(),\n            Self::Config => i2c::ErrorKind::Other,\n        }\n    }\n}\n\n/// Error returned by SPI device implementations in this crate.\n#[derive(Copy, Clone, Eq, PartialEq, Debug)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\n#[non_exhaustive]\npub enum SpiDeviceError<BUS, CS> {\n    /// An operation on the inner SPI bus failed.\n    Spi(BUS),\n    /// Setting the value of the Chip Select (CS) pin failed.\n    Cs(CS),\n    /// Delay operations are not supported when the `time` Cargo feature is not enabled.\n    DelayNotSupported,\n    /// The SPI bus could not be configured.\n    Config,\n}\n\nimpl<BUS, CS> spi::Error for SpiDeviceError<BUS, CS>\nwhere\n    BUS: spi::Error + Debug,\n    CS: Debug,\n{\n    fn kind(&self) -> spi::ErrorKind {\n        match self {\n            Self::Spi(e) => e.kind(),\n            Self::Cs(_) => spi::ErrorKind::Other,\n            Self::DelayNotSupported => spi::ErrorKind::Other,\n            Self::Config => spi::ErrorKind::Other,\n        }\n    }\n}\n"
  },
  {
    "path": "embassy-executor/CHANGELOG.md",
    "content": "# Changelog\n\nAll notable changes to this project will be documented in this file.\n\nThe format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),\nand this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).\n\n<!-- next-header -->\n## Unreleased - ReleaseDate\n\n## 0.10.0 - 2026-03-10\n\n- Added new metadata API for tasks.\n- Main task automatically gets a name of `main` when the `metadata-name` feature is enabled.\n- Upgraded rtos-trace\n- Added optional \"highest priority\" scheduling\n- Added optional \"earliest deadline first\" EDF scheduling\n- Migrate `cortex-ar` to `aarch32-cpu`. The feature name `arch-cortex-ar` remains the same and\n  legacy ARM architectures are not supported.\n- Added `run_until` to `arch-std` variant of `Executor`.\n- Added `__try_embassy_time_queue_item_from_waker`\n- Added fallible `from_waker` getter for `TaskRef`\n- Changed: return error when creating the `SpawnToken`, not when spawning\n- Bump avr-device from 0.7.0 to 0.8.1\n\n## 0.9.1 - 2025-08-31\n\n- Fixed performance regression on some ESP32 MCUs.\n\n## 0.9.0 - 2025-08-26\n\n- Added `extern \"Rust\" fn __embassy_time_queue_item_from_waker`\n- Removed `TaskRef::dangling`\n- Added `embassy-executor-timer-queue` as a dependency\n- Moved the `TimeQueueItem` struct and `timer-item-payload-size-*` features (as `timer-item-size-X-words`) into `embassy-executor-timer-queue`\n\n## 0.8.0 - 2025-07-31\n\n- Added `SpawnToken::id`\n- Task pools are now statically allocated on stable rust. All `task-arena-size-*` features have been removed and are no longer necessary.\n- New trace hooks: `_embassy_trace_poll_start` & `_embassy_trace_task_end`\n- Added task naming capability to tracing infrastructure\n- Added `Executor::id` & `Spawner::executor_id`\n- Disable `critical-section/std` for arch-std\n- Added possibility to select an executor in `#[embassy_executor::main]`\n- Fix AVR executor\n- executor: Make state implementations and their conditions match\n- Added support for Cortex-A and Cortex-R\n- Added support for `-> impl Future<Output = ()>` in `#[task]`\n- Fixed `Send` unsoundness with `-> impl Future` tasks\n- Marked `Spawner::for_current_executor` as `unsafe`\n- `#[task]` now properly marks the generated function as unsafe if the task is marked unsafe\n\n## 0.7.0 - 2025-01-02\n\n- Performance optimizations.\n- Remove feature `integrated-timers`. Starting with `embassy-time-driver` v0.2, `embassy-time` v0.4 the timer queue is now part of the time driver, so it's no longer the executor's responsibility. Therefore, `embassy-executor` no longer provides an `embassy-time-queue-driver` implementation.\n- Added the possibility for timer driver implementations to store arbitrary data in task headers. This can be used to make a timer queue intrusive list, similar to the previous `integrated-timers` feature. Payload size is controlled by the `timer-item-payload-size-X` features.\n- Added `TaskRef::executor` to obtain a reference to a task's executor\n\n## 0.6.3 - 2024-11-12\n\n- Building with the `nightly` feature now works with the Xtensa Rust compiler 1.82.\n- Compare vtable address instead of contents. Saves 44 bytes of flash on cortex-m.\n\n## 0.6.2 - 2024-11-06\n\n- The `nightly` feature no longer requires `nightly-2024-09-06` or newer.\n\n## 0.6.1 - 2024-10-21\n\n- Soundness fix: Deny using `impl Trait` in task arguments. This was previously accidentally allowed when not using the `nightly` feature,\n  and could cause out of bounds memory accesses if spawning the same task mulitple times with different underlying types\n  for the `impl Trait`. Affected versions are 0.4.x, 0.5.0 and 0.6.0, which have been yanked.\n- Add an architecture-agnostic executor that spins waiting for tasks to run, enabled with the `arch-spin` feature.\n- Update for breaking change in the nightly waker_getters API. The `nightly` feature now requires `nightly-2024-09-06` or newer.\n- Improve macro error messages.\n\n## 0.6.0 - 2024-08-05\n\n- Add collapse_debuginfo to fmt.rs macros.\n- initial support for AVR\n- use nightly waker_getters APIs\n\n## 0.5.1 - 2024-10-21\n\n- Soundness fix: Deny using `impl Trait` in task arguments. This was previously accidentally allowed when not using the `nightly` feature,\n  and could cause out of bounds memory accesses if spawning the same task mulitple times with different underlying types\n  for the `impl Trait`. Affected versions are 0.4.x, 0.5.0 and 0.6.0, which have been yanked.\n\n## 0.5.0 - 2024-01-11\n\n- Updated to `embassy-time-driver 0.1`, `embassy-time-queue-driver 0.1`, compatible with `embassy-time v0.3` and higher.\n\n## 0.4.0 - 2023-12-05\n\n- Removed `arch-xtensa`. Use the executor provided by the HAL crate (`esp-hal`, `esp32s3-hal`, etc...) instead.\n- Added an arena allocator for tasks, allowing using the `main` and `task` macros on Rust 1.75 stable. (it is only used if the `nightly` feature is not enabled. When `nightly` is enabled, `type_alias_impl_trait` is used to statically allocate tasks, as before).\n\n## 0.3.3 - 2023-11-15\n\n- Add `main` macro reexport for Xtensa arch.\n- Remove use of `atomic-polyfill`. The executor now has multiple implementations of its internal data structures for cases where the target supports atomics or doesn't.\n\n## 0.3.2 - 2023-11-06\n\n- Use `atomic-polyfill` for `riscv32`\n- Removed unused dependencies (static_cell, futures-util)\n\n## 0.3.1 - 2023-11-01\n\n- Fix spurious \"Found waker not created by the Embassy executor\" error in recent nightlies.\n\n## 0.3.0 - 2023-08-25\n\n- Replaced Pender. Implementations now must define an extern function called `__pender`.\n- Made `raw::AvailableTask` public\n- Made `SpawnToken::new_failed` public\n- You can now use arbitrary expressions to specify `#[task(pool_size = X)]`\n\n## 0.2.1 - 2023-08-10\n\n- Avoid calling `pend()` when waking expired timers\n- Properly reset finished task state with `integrated-timers` enabled\n- Introduce `InterruptExecutor::spawner()`\n- Fix incorrect critical section in Xtensa executor\n\n## 0.2.0 - 2023-04-27\n\n- Replace unnecessary atomics in runqueue\n- add Pender, rework Cargo features.\n- add support for turbo-wakers.\n- Allow TaskStorage to auto-implement `Sync`\n- Use AtomicPtr for signal_ctx, removes 1 unsafe.\n- Replace unsound critical sections with atomics\n\n## 0.1.1 - 2022-11-23\n\n- Fix features for documentation\n\n## 0.1.0 - 2022-11-23\n\n- First release\n"
  },
  {
    "path": "embassy-executor/Cargo.toml",
    "content": "[package]\nname = \"embassy-executor\"\nversion = \"0.10.0\"\nedition = \"2024\"\nlicense = \"MIT OR Apache-2.0\"\ndescription = \"async/await executor designed for embedded usage\"\nrepository = \"https://github.com/embassy-rs/embassy\"\ndocumentation = \"https://docs.embassy.dev/embassy-executor\"\ncategories = [\n    \"embedded\",\n    \"no-std\",\n    \"asynchronous\",\n]\n\n[package.metadata.embassy]\nbuild = [\n    {target = \"thumbv7em-none-eabi\", features = []},\n    {target = \"thumbv7em-none-eabi\", features = [\"log\"]},\n    {target = \"thumbv7em-none-eabi\", features = [\"defmt\"]},\n    {target = \"thumbv6m-none-eabi\", features = [\"defmt\"]},\n    {target = \"thumbv6m-none-eabi\", features = [\"platform-cortex-m\", \"defmt\", \"executor-interrupt\", \"executor-thread\"]},\n    {target = \"thumbv7em-none-eabi\", features = [\"platform-cortex-m\"]},\n    {target = \"thumbv7em-none-eabi\", features = [\"platform-cortex-m\", \"rtos-trace\"]},\n    {target = \"thumbv7em-none-eabi\", features = [\"platform-cortex-m\", \"executor-thread\"]},\n    {target = \"thumbv7em-none-eabi\", features = [\"platform-cortex-m\", \"executor-interrupt\"]},\n    {target = \"thumbv7em-none-eabi\", features = [\"platform-cortex-m\", \"executor-interrupt\", \"executor-thread\"]},\n    {target = \"thumbv7em-none-eabi\", features = [\"platform-cortex-m\", \"executor-interrupt\", \"executor-thread\", \"embassy-time-driver\"]},\n    {target = \"thumbv7em-none-eabi\", features = [\"platform-cortex-m\", \"executor-interrupt\", \"executor-thread\", \"embassy-time-driver\", \"scheduler-priority\"]},\n    {target = \"thumbv7em-none-eabi\", features = [\"platform-cortex-m\", \"executor-interrupt\", \"executor-thread\", \"embassy-time-driver\", \"scheduler-priority\", \"scheduler-deadline\"]},\n    {target = \"thumbv7em-none-eabi\", features = [\"platform-cortex-m\", \"executor-interrupt\", \"executor-thread\", \"embassy-time-driver\", \"scheduler-deadline\"]},\n    {target = \"thumbv7em-none-eabi\", features = [\"platform-cortex-m\", \"executor-interrupt\", \"executor-thread\", \"scheduler-priority\", \"scheduler-deadline\"]},\n    {target = \"thumbv7em-none-eabi\", features = [\"platform-cortex-m\", \"executor-interrupt\", \"executor-thread\", \"scheduler-deadline\"]},\n    {target = \"thumbv7em-none-eabi\", features = [\"platform-cortex-m\", \"executor-interrupt\", \"executor-thread\", \"embassy-time-driver\", \"scheduler-priority\", \"scheduler-deadline\", \"trace\"]},\n    {target = \"thumbv7em-none-eabi\", features = [\"platform-spin\"]},\n    {target = \"thumbv7em-none-eabi\", features = [\"platform-spin\", \"scheduler-deadline\"]},\n    {target = \"armv7a-none-eabi\", features = [\"platform-cortex-ar\", \"executor-thread\"]},\n    {target = \"armv7r-none-eabi\", features = [\"platform-cortex-ar\", \"executor-thread\"]},\n    {target = \"armv7r-none-eabihf\", features = [\"platform-cortex-ar\", \"executor-thread\"]},\n    {target = \"riscv32imac-unknown-none-elf\", features = [\"platform-riscv32\"]},\n    {target = \"riscv32imac-unknown-none-elf\", features = [\"platform-riscv32\", \"executor-thread\"]},\n    {target = \"riscv32imac-unknown-none-elf\", features = [\"platform-riscv32\", \"executor-thread\", \"trace\"]},\n    # Nightly builds\n    {group = \"nightly\", target = \"thumbv7em-none-eabi\", features = [\"nightly\"]},\n    {group = \"nightly\", target = \"thumbv7em-none-eabi\", features = [\"nightly\", \"log\"]},\n    {group = \"nightly\", target = \"thumbv7em-none-eabi\", features = [\"nightly\", \"defmt\"]},\n    {group = \"nightly\", target = \"thumbv6m-none-eabi\", features = [\"nightly\", \"defmt\"]},\n    {group = \"nightly\", target = \"thumbv6m-none-eabi\", features = [\"nightly\", \"defmt\", \"platform-cortex-m\", \"executor-thread\", \"executor-interrupt\"]},\n    {group = \"nightly\", target = \"thumbv7em-none-eabi\", features = [\"nightly\", \"platform-cortex-m\"]},\n    {group = \"nightly\", target = \"thumbv7em-none-eabi\", features = [\"nightly\", \"platform-cortex-m\", \"executor-thread\"]},\n    {group = \"nightly\", target = \"thumbv7em-none-eabi\", features = [\"nightly\", \"platform-cortex-m\", \"executor-interrupt\"]},\n    {group = \"nightly\", target = \"thumbv7em-none-eabi\", features = [\"nightly\", \"platform-cortex-m\", \"executor-thread\", \"executor-interrupt\"]},\n    {group = \"nightly\", target = \"riscv32imac-unknown-none-elf\", features = [\"nightly\", \"platform-riscv32\"]},\n    {group = \"nightly\", target = \"riscv32imac-unknown-none-elf\", features = [\"nightly\", \"platform-riscv32\", \"executor-thread\"]},\n    {group = \"nightly\", target = \"armv7a-none-eabi\", features = [\"nightly\", \"platform-cortex-ar\", \"executor-thread\"]},\n    {group = \"nightly\", target = \"avr-none\", features = [\"nightly\", \"platform-avr\", \"avr-device/atmega328p\"], build-std = [\"core\", \"alloc\"], env = { RUSTFLAGS = \"-Ctarget-cpu=atmega328p\" }},\n    # Xtensa builds\n    {group = \"xtensa\", build-std = [\"core\", \"alloc\"],  target = \"xtensa-esp32-none-elf\", features = []},\n    {group = \"xtensa\", build-std = [\"core\", \"alloc\"],  target = \"xtensa-esp32-none-elf\", features = [\"log\"]},\n    {group = \"xtensa\", build-std = [\"core\", \"alloc\"],  target = \"xtensa-esp32-none-elf\", features = [\"defmt\"]},\n    {group = \"xtensa\", build-std = [\"core\", \"alloc\"],  target = \"xtensa-esp32s2-none-elf\", features = [\"defmt\"]},\n    {group = \"xtensa\", build-std = [\"core\", \"alloc\"],  target = \"xtensa-esp32-none-elf\", features = [\"defmt\", \"platform-spin\", \"executor-thread\"]},\n    {group = \"xtensa\", build-std = [\"core\", \"alloc\"],  target = \"xtensa-esp32s2-none-elf\", features = [\"defmt\", \"platform-spin\", \"executor-thread\"]},\n    {group = \"xtensa\", build-std = [\"core\", \"alloc\"],  target = \"xtensa-esp32s3-none-elf\", features = [\"defmt\", \"platform-spin\", \"executor-thread\"]},\n    {group = \"xtensa\", build-std = [\"core\", \"alloc\"],  target = \"xtensa-esp32-none-elf\", features = [\"platform-spin\"]},\n    {group = \"xtensa\", build-std = [\"core\", \"alloc\"],  target = \"xtensa-esp32-none-elf\", features = [\"platform-spin\", \"rtos-trace\"]},\n    {group = \"xtensa\", build-std = [\"core\", \"alloc\"],  target = \"xtensa-esp32-none-elf\", features = [\"platform-spin\", \"executor-thread\"]},\n]\n\n\n[package.metadata.embassy_docs]\nsrc_base = \"https://github.com/embassy-rs/embassy/blob/embassy-executor-v$VERSION/embassy-executor/src/\"\nsrc_base_git = \"https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-executor/src/\"\nfeatures = [\"defmt\", \"scheduler-deadline\", \"scheduler-priority\"]\nflavors = [\n    { name = \"std\",             target = \"x86_64-unknown-linux-gnu\",     features = [\"platform-std\", \"executor-thread\"] },\n    { name = \"wasm\",            target = \"wasm32-unknown-unknown\",       features = [\"platform-wasm\", \"executor-thread\"] },\n    { name = \"cortex-m\",        target = \"thumbv7em-none-eabi\",          features = [\"platform-cortex-m\", \"executor-thread\", \"executor-interrupt\"] },\n    { name = \"riscv32\",         target = \"riscv32imac-unknown-none-elf\", features = [\"platform-riscv32\", \"executor-thread\"] },\n]\n\n[package.metadata.docs.rs]\ndefault-target = \"thumbv7em-none-eabi\"\ntargets = [\"thumbv7em-none-eabi\"]\nfeatures = [\"defmt\", \"platform-cortex-m\", \"executor-thread\", \"executor-interrupt\", \"scheduler-deadline\", \"scheduler-priority\", \"embassy-time-driver\"]\n\n[dependencies]\ndefmt = { version = \"1.0.1\", optional = true }\nlog = { version = \"0.4.14\", optional = true }\nrtos-trace = { version = \"0.2\", optional = true }\n\nembassy-executor-macros = { version = \"0.8.0\", path = \"../embassy-executor-macros\" }\nembassy-time-driver = { version = \"0.2.2\", path = \"../embassy-time-driver\", optional = true }\nembassy-executor-timer-queue = { version = \"0.1\", path = \"../embassy-executor-timer-queue\" }\ncritical-section = \"1.1\"\n\ndocument-features = \"0.2.7\"\n\n# needed for AVR\nportable-atomic = { version = \"1.5\", optional = true }\n\n# platform-cortex-m dependencies\ncortex-m = { version = \"0.7.6\", optional = true }\n\n# platform-cortex-ar dependencies\naarch32-cpu = { version = \"0.1\", optional = true }\n\n# platform-wasm dependencies\nwasm-bindgen = { version = \"0.2.82\", optional = true }\njs-sys = { version = \"0.3\", optional = true }\n\n# platform-avr dependencies\navr-device = { version = \"0.8.1\", optional = true }\n\n\n[dependencies.cordyceps]\nversion = \"0.3.4\"\nfeatures = [\"no-cache-pad\"]\n\n[dev-dependencies]\ncritical-section = { version = \"1.1\", features = [\"std\"] }\ntrybuild = \"1.0\"\nembassy-sync = { path = \"../embassy-sync\" }\nrustversion = \"1.0.21\"\n\n[features]\n\n## Enable nightly-only features\nnightly = [\"embassy-executor-macros/nightly\"]\n\n## Enable defmt logging\ndefmt = [\"dep:defmt\"]\n\n## Enable log logging\nlog = [\"dep:log\"]\n\n# Enables turbo wakers, which requires patching core. Not surfaced in the docs by default due to\n# being an complicated advanced and undocumented feature.\n# See: https://github.com/embassy-rs/embassy/pull/1263\nturbowakers = []\n\n#! ### Platform\n_platform = [] # some platform was picked\n## STD platform. Enables running the executor on top of `std` threading primitives.\nplatform-std = [\"_platform\"]\n## Cortex-M platform. Uses `WFE`/`SEV` for the thread executor, NVIC interrupts for the interrupt executor.\n## \n## - Only usable on **single-core chips**. (Exception: the thread executor might work if your chip has wired the \"event\" signal between the cores, so `SEV` on one core can break a `WFE` in the other)\n## - Only usable on **bare-metal**. Do not use this if you want to run the executor inside an RTOS thread, you must use RTOS primitives to sleep/notify the thread instead.\n## - Only usable on **privileged mode**. \nplatform-cortex-m = [\"_platform\", \"dep:cortex-m\"]\n## Cortex-A/R platform. Uses `WFE`/`SEV` for the thread executor. Interrupt executor is not supported.\n## \n## - Only usable on **single-core chips**. (Exception: the thread executor might work if your chip has wired the \"event\" signal between the cores, so `SEV` on one core can break a `WFE` in the other)\n## - Only usable on **bare-metal**. Do not use this if you want to run the executor inside an RTOS thread, you must use RTOS primitives to sleep/notify the thread instead.\n## - Only usable on **privileged mode**. \nplatform-cortex-ar = [\"_platform\", \"dep:aarch32-cpu\", \"dep:arm-targets\"]\n## RISC-V 32-bit platform. Uses WFI for the thread executor. Interrupt executor is not supported.\n## \n## - Only usable on **single-core chips**.\n## - Only usable on **bare-metal**. Do not use this if you want to run the executor inside an RTOS thread, you must use RTOS primitives to sleep/notify the thread instead.\n## - Only usable on **privileged mode**. \nplatform-riscv32 = [\"_platform\"]\n## WASM platform.\nplatform-wasm = [\"_platform\", \"dep:wasm-bindgen\", \"dep:js-sys\"]\n## AVR platform. Uses WFI for the thread executor. Interrupt executor is not supported.\nplatform-avr = [\"_platform\", \"dep:portable-atomic\", \"dep:avr-device\"]\n## Spin-loop platform.\n## \n## This \"platform\" implementation is architecture/platform/chip agnostic. The main loop polls the executor constantly without sleeping, and the pender callback simply does nothing. Using this is not recommended, you probably want to use a platform-specific implementation that can sleep instead.\nplatform-spin = [\"_platform\"]\n\n#! ### Metadata\n\n## Enable the `name` field in task metadata.\nmetadata-name = [\"embassy-executor-macros/metadata-name\"]\n\n#! ### Executor\n\n## Enable the thread-mode executor (using WFE/SEV in Cortex-M, WFI in other embedded platforms)\nexecutor-thread = []\n## Enable the interrupt-mode executor (available in Cortex-M only)\nexecutor-interrupt = []\n## Enable tracing hooks\ntrace = [\"_any_trace\"]\n## Enable support for rtos-trace framework\nrtos-trace = [\"_any_trace\", \"metadata-name\", \"dep:rtos-trace\", \"embassy-time-driver\"]\n_any_trace = []\n\n## Enable \"Earliest Deadline First\" Scheduler, using soft-realtime \"deadlines\" to prioritize\n## tasks based on the remaining time before their deadline. Adds some overhead.\nscheduler-deadline = []\n\n## Enable \"Highest Priority First\" Scheduler. Adds some overhead.\nscheduler-priority = []\n\n## Enable the embassy_time_driver dependency.\n## This can unlock extra APIs, for example for the `scheduler-deadline`\nembassy-time-driver = [\"dep:embassy-time-driver\"]\n\n[build-dependencies]\narm-targets = { version = \"0.4\", optional = true }\n"
  },
  {
    "path": "embassy-executor/README.md",
    "content": "# embassy-executor\n\nAn async/await executor designed for embedded usage.\n\n- No `alloc`, no heap needed.\n- Tasks are statically allocated. Each task gets its own `static`, with the exact size to hold the task (or multiple instances of it, if using `pool_size`) calculated automatically at compile time. If tasks don't fit in RAM, this is detected at compile time by the linker. Runtime panics due to running out of memory are not possible.\n- No \"fixed capacity\" data structures, executor works with 1 or 1000 tasks without needing config/tuning.\n- Integrated timer queue: sleeping is easy, just do `Timer::after_secs(1).await;`.\n- No busy-loop polling: CPU sleeps when there's no work to do, using interrupts or `WFE/SEV`.\n- Efficient polling: a wake will only poll the woken task, not all of them.\n- Fair: a task can't monopolize CPU time even if it's constantly being woken. All other tasks get a chance to run before a given task gets polled for the second time.\n- Creating multiple executor instances is supported, to run tasks with multiple priority levels. This allows higher-priority tasks to preempt lower-priority tasks.\n\n## Platforms\n\nThe executor requires a \"platform\" to be defined to work. A platform defines the following things:\n\n- The main loop, which typically consists of an infinite loop of polling the executor then sleeping the current thread/core in a platform-specific way.\n- A \"pender\" callback, which must cause the executor's thread/core to exit sleep so the executor gets polled again. This is called when a task running in the executor is woken.\n\nThe `embassy-executor` crate ships with support for some commonly used platforms, see the crate's feature documentation.\n\nChip-specific executor platform implementations are maintained in their respective HALs:\n\n- `embassy-rp`: multicore support. Enabled with the `executor-interrupt` or `executor-thread` features.\n- `embassy-stm32`: automatic low-power sleep support. Enabled with the `executor-interrupt` or `executor-thread` features.\n- `embassy-mcxa`: automatic low-power sleep support. Enabled with the `executor-platform` feature.\n- `esp-rtos`: ESP32 RTOS support, multicore support. Enabled with the `embassy` feature.\n\nTo use the executor, you must provide exactly one platform implementation, either from this crate, a HAL crate, or a custom one.\n\n## Implementing a custom platform\n\nTo implement your own custom platform, e.g. on top of an RTOS, do the following:\n\n1. define the `__pender` callback.\n\n```rust,ignore\n#[unsafe(export_name = \"__pender\")]\nfn __pender(context: *mut ()) {\n    // `context` is the argument passed to `raw::Executor::new`. Here we're using it\n    // to pass a handle to the RTOS task but you can use it for anything.\n    my_rtos::notify_task(context as _);\n}\n```\n\n2. Wrap the `raw::Executor` into your own `Executor` struct that defines the main loop.\n\n```rust,ignore\npub struct Executor {\n    inner: raw::Executor,\n    not_send: PhantomData<*mut ()>,\n}\n\nimpl Executor {\n    pub fn new() -> Self {\n        Self {\n            inner: raw::Executor::new(my_rtos::task_get_current() as _),\n            not_send: PhantomData,\n        }\n    }\n\n    pub fn run(&'static mut self, init: impl FnOnce(Spawner)) -> ! {\n        init(self.inner.spawner());\n\n        loop {\n            unsafe { self.inner.poll() }\n            my_rtos::task_wait_for_notification();\n        }\n    }\n}\n```"
  },
  {
    "path": "embassy-executor/build.rs",
    "content": "#[path = \"./build_common.rs\"]\nmod common;\n\nfn main() {\n    let mut rustc_cfgs = common::CfgSet::new();\n    common::set_target_cfgs(&mut rustc_cfgs);\n\n    // This is used to exclude legacy architecture support. The raw executor needs to be used for\n    // those architectures because SEV/WFE are not supported.\n    #[cfg(feature = \"platform-cortex-ar\")]\n    arm_targets::process();\n}\n"
  },
  {
    "path": "embassy-executor/build_common.rs",
    "content": "// NOTE: this file is copy-pasted between several Embassy crates, because there is no\n// straightforward way to share this code:\n// - it cannot be placed into the root of the repo and linked from each build.rs using `#[path =\n// \"../build_common.rs\"]`, because `cargo publish` requires that all files published with a crate\n// reside in the crate's directory,\n// - it cannot be symlinked from `embassy-xxx/build_common.rs` to `../build_common.rs`, because\n// symlinks don't work on Windows.\n\nuse std::collections::HashSet;\nuse std::env;\n\n/// Helper for emitting cargo instruction for enabling configs (`cargo:rustc-cfg=X`) and declaring\n/// them (`cargo:rust-check-cfg=cfg(X)`).\n#[derive(Debug)]\npub struct CfgSet {\n    enabled: HashSet<String>,\n    declared: HashSet<String>,\n}\n\nimpl CfgSet {\n    pub fn new() -> Self {\n        Self {\n            enabled: HashSet::new(),\n            declared: HashSet::new(),\n        }\n    }\n\n    /// Enable a config, which can then be used in `#[cfg(...)]` for conditional compilation.\n    ///\n    /// All configs that can potentially be enabled should be unconditionally declared using\n    /// [`Self::declare()`].\n    pub fn enable(&mut self, cfg: impl AsRef<str>) {\n        if self.enabled.insert(cfg.as_ref().to_owned()) {\n            println!(\"cargo:rustc-cfg={}\", cfg.as_ref());\n        }\n    }\n\n    pub fn enable_all(&mut self, cfgs: &[impl AsRef<str>]) {\n        for cfg in cfgs.iter() {\n            self.enable(cfg.as_ref());\n        }\n    }\n\n    /// Declare a valid config for conditional compilation, without enabling it.\n    ///\n    /// This enables rustc to check that the configs in `#[cfg(...)]` attributes are valid.\n    pub fn declare(&mut self, cfg: impl AsRef<str>) {\n        if self.declared.insert(cfg.as_ref().to_owned()) {\n            println!(\"cargo:rustc-check-cfg=cfg({})\", cfg.as_ref());\n        }\n    }\n\n    pub fn declare_all(&mut self, cfgs: &[impl AsRef<str>]) {\n        for cfg in cfgs.iter() {\n            self.declare(cfg.as_ref());\n        }\n    }\n\n    pub fn set(&mut self, cfg: impl Into<String>, enable: bool) {\n        let cfg = cfg.into();\n        if enable {\n            self.enable(cfg.clone());\n        }\n        self.declare(cfg);\n    }\n}\n\n/// Sets configs that describe the target platform.\npub fn set_target_cfgs(cfgs: &mut CfgSet) {\n    let target = env::var(\"TARGET\").unwrap();\n\n    if target.starts_with(\"thumbv6m-\") {\n        cfgs.enable_all(&[\"cortex_m\", \"armv6m\"]);\n    } else if target.starts_with(\"thumbv7m-\") {\n        cfgs.enable_all(&[\"cortex_m\", \"armv7m\"]);\n    } else if target.starts_with(\"thumbv7em-\") {\n        cfgs.enable_all(&[\"cortex_m\", \"armv7m\", \"armv7em\"]);\n    } else if target.starts_with(\"thumbv8m.base\") {\n        cfgs.enable_all(&[\"cortex_m\", \"armv8m\", \"armv8m_base\"]);\n    } else if target.starts_with(\"thumbv8m.main\") {\n        cfgs.enable_all(&[\"cortex_m\", \"armv8m\", \"armv8m_main\"]);\n    }\n    cfgs.declare_all(&[\n        \"cortex_m\",\n        \"armv6m\",\n        \"armv7m\",\n        \"armv7em\",\n        \"armv8m\",\n        \"armv8m_base\",\n        \"armv8m_main\",\n    ]);\n\n    cfgs.set(\"has_fpu\", target.ends_with(\"-eabihf\"));\n}\n"
  },
  {
    "path": "embassy-executor/gen_config.py",
    "content": "import os\n\nabspath = os.path.abspath(__file__)\ndname = os.path.dirname(abspath)\nos.chdir(dname)\n\nfeatures = []\n\n\ndef feature(name, default, min=None, max=None, pow2=None, vals=None, factors=[]):\n    if vals is None:\n        assert min is not None\n        assert max is not None\n\n        vals = set()\n        val = min\n        while val <= max:\n            vals.add(val)\n            for f in factors:\n                if val * f <= max:\n                    vals.add(val * f)\n            if (pow2 == True or (isinstance(pow2, int) and val >= pow2)) and val > 0:\n                val *= 2\n            else:\n                val += 1\n        vals.add(default)\n        vals = sorted(list(vals))\n\n    features.append(\n        {\n            \"name\": name,\n            \"default\": default,\n            \"vals\": vals,\n        }\n    )\n\n\nfeature(\n    \"task_arena_size\", default=4096, min=64, max=1024 * 1024, pow2=True, factors=[3, 5]\n)\n\n# ========= Update Cargo.toml\n\nthings = \"\"\nfor f in features:\n    name = f[\"name\"].replace(\"_\", \"-\")\n    for val in f[\"vals\"]:\n        things += f\"## {val}\"\n        if val == f[\"default\"]:\n            things += \" (default)\\n\"\n        else:\n            things += \"\\n\"\n            \n        things += f\"{name}-{val} = []\"\n        if val == f[\"default\"]:\n            things += \" # Default\"\n        things += \"\\n\"\n    things += \"\\n\"\n\nSEPARATOR_START = \"# BEGIN AUTOGENERATED CONFIG FEATURES\\n\"\nSEPARATOR_END = \"# END AUTOGENERATED CONFIG FEATURES\\n\"\nHELP = \"# Generated by gen_config.py. DO NOT EDIT.\\n\"\nwith open(\"Cargo.toml\", \"r\") as f:\n    data = f.read()\nbefore, data = data.split(SEPARATOR_START, maxsplit=1)\n_, after = data.split(SEPARATOR_END, maxsplit=1)\ndata = before + SEPARATOR_START + HELP + things + SEPARATOR_END + after\nwith open(\"Cargo.toml\", \"w\") as f:\n    f.write(data)\n\n\n# ========= Update build.rs\n\nthings = \"\"\nfor f in features:\n    name = f[\"name\"].upper()\n    things += f'    (\"{name}\", {f[\"default\"]}),\\n'\n\nSEPARATOR_START = \"// BEGIN AUTOGENERATED CONFIG FEATURES\\n\"\nSEPARATOR_END = \"// END AUTOGENERATED CONFIG FEATURES\\n\"\nHELP = \"    // Generated by gen_config.py. DO NOT EDIT.\\n\"\nwith open(\"build.rs\", \"r\") as f:\n    data = f.read()\nbefore, data = data.split(SEPARATOR_START, maxsplit=1)\n_, after = data.split(SEPARATOR_END, maxsplit=1)\ndata = before + SEPARATOR_START + HELP + things + \"    \" + SEPARATOR_END + after\nwith open(\"build.rs\", \"w\") as f:\n    f.write(data)\n"
  },
  {
    "path": "embassy-executor/src/fmt.rs",
    "content": "#![macro_use]\n#![allow(unused)]\n\nuse core::fmt::{Debug, Display, LowerHex};\n\n#[cfg(all(feature = \"defmt\", feature = \"log\"))]\ncompile_error!(\"You may not enable both `defmt` and `log` features.\");\n\n#[collapse_debuginfo(yes)]\nmacro_rules! assert {\n    ($($x:tt)*) => {\n        {\n            #[cfg(not(feature = \"defmt\"))]\n            ::core::assert!($($x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::assert!($($x)*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! assert_eq {\n    ($($x:tt)*) => {\n        {\n            #[cfg(not(feature = \"defmt\"))]\n            ::core::assert_eq!($($x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::assert_eq!($($x)*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! assert_ne {\n    ($($x:tt)*) => {\n        {\n            #[cfg(not(feature = \"defmt\"))]\n            ::core::assert_ne!($($x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::assert_ne!($($x)*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! debug_assert {\n    ($($x:tt)*) => {\n        {\n            #[cfg(not(feature = \"defmt\"))]\n            ::core::debug_assert!($($x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::debug_assert!($($x)*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! debug_assert_eq {\n    ($($x:tt)*) => {\n        {\n            #[cfg(not(feature = \"defmt\"))]\n            ::core::debug_assert_eq!($($x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::debug_assert_eq!($($x)*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! debug_assert_ne {\n    ($($x:tt)*) => {\n        {\n            #[cfg(not(feature = \"defmt\"))]\n            ::core::debug_assert_ne!($($x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::debug_assert_ne!($($x)*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! todo {\n    ($($x:tt)*) => {\n        {\n            #[cfg(not(feature = \"defmt\"))]\n            ::core::todo!($($x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::todo!($($x)*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! unreachable {\n    ($($x:tt)*) => {\n        {\n            #[cfg(not(feature = \"defmt\"))]\n            ::core::unreachable!($($x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::unreachable!($($x)*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! panic {\n    ($($x:tt)*) => {\n        {\n            #[cfg(not(feature = \"defmt\"))]\n            ::core::panic!($($x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::panic!($($x)*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! trace {\n    ($s:literal $(, $x:expr)* $(,)?) => {\n        {\n            #[cfg(feature = \"log\")]\n            ::log::trace!($s $(, $x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::trace!($s $(, $x)*);\n            #[cfg(not(any(feature = \"log\", feature=\"defmt\")))]\n            let _ = ($( & $x ),*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! debug {\n    ($s:literal $(, $x:expr)* $(,)?) => {\n        {\n            #[cfg(feature = \"log\")]\n            ::log::debug!($s $(, $x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::debug!($s $(, $x)*);\n            #[cfg(not(any(feature = \"log\", feature=\"defmt\")))]\n            let _ = ($( & $x ),*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! info {\n    ($s:literal $(, $x:expr)* $(,)?) => {\n        {\n            #[cfg(feature = \"log\")]\n            ::log::info!($s $(, $x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::info!($s $(, $x)*);\n            #[cfg(not(any(feature = \"log\", feature=\"defmt\")))]\n            let _ = ($( & $x ),*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! warn {\n    ($s:literal $(, $x:expr)* $(,)?) => {\n        {\n            #[cfg(feature = \"log\")]\n            ::log::warn!($s $(, $x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::warn!($s $(, $x)*);\n            #[cfg(not(any(feature = \"log\", feature=\"defmt\")))]\n            let _ = ($( & $x ),*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! error {\n    ($s:literal $(, $x:expr)* $(,)?) => {\n        {\n            #[cfg(feature = \"log\")]\n            ::log::error!($s $(, $x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::error!($s $(, $x)*);\n            #[cfg(not(any(feature = \"log\", feature=\"defmt\")))]\n            let _ = ($( & $x ),*);\n        }\n    };\n}\n\n#[cfg(feature = \"defmt\")]\n#[collapse_debuginfo(yes)]\nmacro_rules! unwrap {\n    ($($x:tt)*) => {\n        ::defmt::unwrap!($($x)*)\n    };\n}\n\n#[cfg(not(feature = \"defmt\"))]\n#[collapse_debuginfo(yes)]\nmacro_rules! unwrap {\n    ($arg:expr) => {\n        match $crate::fmt::Try::into_result($arg) {\n            ::core::result::Result::Ok(t) => t,\n            ::core::result::Result::Err(e) => {\n                ::core::panic!(\"unwrap of `{}` failed: {:?}\", ::core::stringify!($arg), e);\n            }\n        }\n    };\n    ($arg:expr, $($msg:expr),+ $(,)? ) => {\n        match $crate::fmt::Try::into_result($arg) {\n            ::core::result::Result::Ok(t) => t,\n            ::core::result::Result::Err(e) => {\n                ::core::panic!(\"unwrap of `{}` failed: {}: {:?}\", ::core::stringify!($arg), ::core::format_args!($($msg,)*), e);\n            }\n        }\n    }\n}\n\n#[derive(Debug, Copy, Clone, Eq, PartialEq)]\npub struct NoneError;\n\npub trait Try {\n    type Ok;\n    type Error;\n    fn into_result(self) -> Result<Self::Ok, Self::Error>;\n}\n\nimpl<T> Try for Option<T> {\n    type Ok = T;\n    type Error = NoneError;\n\n    #[inline]\n    fn into_result(self) -> Result<T, NoneError> {\n        self.ok_or(NoneError)\n    }\n}\n\nimpl<T, E> Try for Result<T, E> {\n    type Ok = T;\n    type Error = E;\n\n    #[inline]\n    fn into_result(self) -> Self {\n        self\n    }\n}\n\npub(crate) struct Bytes<'a>(pub &'a [u8]);\n\nimpl<'a> Debug for Bytes<'a> {\n    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {\n        write!(f, \"{:#02x?}\", self.0)\n    }\n}\n\nimpl<'a> Display for Bytes<'a> {\n    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {\n        write!(f, \"{:#02x?}\", self.0)\n    }\n}\n\nimpl<'a> LowerHex for Bytes<'a> {\n    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {\n        write!(f, \"{:#02x?}\", self.0)\n    }\n}\n\n#[cfg(feature = \"defmt\")]\nimpl<'a> defmt::Format for Bytes<'a> {\n    fn format(&self, fmt: defmt::Formatter) {\n        defmt::write!(fmt, \"{:02x}\", self.0)\n    }\n}\n"
  },
  {
    "path": "embassy-executor/src/lib.rs",
    "content": "#![cfg_attr(not(any(feature = \"platform-std\", feature = \"platform-wasm\")), no_std)]\n#![allow(clippy::new_without_default)]\n#![allow(unsafe_op_in_unsafe_fn)]\n#![doc = include_str!(\"../README.md\")]\n#![warn(missing_docs)]\n\n//! ## Feature flags\n#![doc = document_features::document_features!(feature_label = r#\"<span class=\"stab portability\"><code>{feature}</code></span>\"#)]\n\n// This mod MUST go first, so that the others see its macros.\npub(crate) mod fmt;\n\npub use embassy_executor_macros::task;\n\nmacro_rules! check_at_most_one {\n    (@amo [$($feats:literal)*] [] [$($res:tt)*]) => {\n        #[cfg(any($($res)*))]\n        compile_error!(concat!(\"At most one of these features can be enabled at the same time:\", $(\" `\", $feats, \"`\",)*));\n    };\n    (@amo $feats:tt [$curr:literal $($rest:literal)*] [$($res:tt)*]) => {\n        check_at_most_one!(@amo $feats [$($rest)*] [$($res)* $(all(feature=$curr, feature=$rest),)*]);\n    };\n    ($($f:literal),*$(,)?) => {\n        check_at_most_one!(@amo [$($f)*] [$($f)*] []);\n    };\n}\ncheck_at_most_one!(\n    \"platform-avr\",\n    \"platform-cortex-m\",\n    \"platform-cortex-ar\",\n    \"platform-riscv32\",\n    \"platform-std\",\n    \"platform-wasm\",\n    \"platform-spin\",\n);\n\n#[cfg(feature = \"_platform\")]\n#[cfg_attr(feature = \"platform-avr\", path = \"platform/avr.rs\")]\n#[cfg_attr(feature = \"platform-cortex-m\", path = \"platform/cortex_m.rs\")]\n#[cfg_attr(feature = \"platform-cortex-ar\", path = \"platform/cortex_ar.rs\")]\n#[cfg_attr(feature = \"platform-riscv32\", path = \"platform/riscv32.rs\")]\n#[cfg_attr(feature = \"platform-std\", path = \"platform/std.rs\")]\n#[cfg_attr(feature = \"platform-wasm\", path = \"platform/wasm.rs\")]\n#[cfg_attr(feature = \"platform-spin\", path = \"platform/spin.rs\")]\nmod platform;\n\n#[cfg(not(feature = \"_platform\"))]\npub use embassy_executor_macros::main_unspecified as main;\n#[cfg(feature = \"_platform\")]\n#[allow(unused_imports)] // don't warn if the module is empty.\npub use platform::*;\n\npub mod raw;\n\nmod spawner;\npub use spawner::*;\n\nmod metadata;\npub use metadata::*;\n\n/// Implementation details for embassy macros.\n/// Do not use. Used for macros and HALs only. Not covered by semver guarantees.\n#[doc(hidden)]\n#[cfg(not(feature = \"nightly\"))]\npub mod _export {\n    use core::cell::UnsafeCell;\n    use core::future::Future;\n    use core::mem::MaybeUninit;\n\n    use crate::raw::TaskPool;\n\n    trait TaskReturnValue {}\n    impl TaskReturnValue for () {}\n    impl TaskReturnValue for Never {}\n\n    #[diagnostic::on_unimplemented(\n        message = \"task futures must resolve to `()` or `!`\",\n        note = \"use `async fn` or change the return type to `impl Future<Output = ()>`\"\n    )]\n    #[allow(private_bounds)]\n    pub trait TaskFn<Args>: Copy {\n        type Fut: Future<Output: TaskReturnValue> + 'static;\n    }\n\n    macro_rules! task_fn_impl {\n        ($($Tn:ident),*) => {\n            impl<F, Fut, $($Tn,)*> TaskFn<($($Tn,)*)> for F\n            where\n                F: Copy + FnOnce($($Tn,)*) -> Fut,\n                Fut: Future<Output: TaskReturnValue> + 'static,\n            {\n                type Fut = Fut;\n            }\n        };\n    }\n\n    task_fn_impl!();\n    task_fn_impl!(T0);\n    task_fn_impl!(T0, T1);\n    task_fn_impl!(T0, T1, T2);\n    task_fn_impl!(T0, T1, T2, T3);\n    task_fn_impl!(T0, T1, T2, T3, T4);\n    task_fn_impl!(T0, T1, T2, T3, T4, T5);\n    task_fn_impl!(T0, T1, T2, T3, T4, T5, T6);\n    task_fn_impl!(T0, T1, T2, T3, T4, T5, T6, T7);\n    task_fn_impl!(T0, T1, T2, T3, T4, T5, T6, T7, T8);\n    task_fn_impl!(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9);\n    task_fn_impl!(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10);\n    task_fn_impl!(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11);\n    task_fn_impl!(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12);\n    task_fn_impl!(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13);\n    task_fn_impl!(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14);\n    task_fn_impl!(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15);\n\n    #[allow(private_bounds)]\n    #[repr(C)]\n    pub struct TaskPoolHolder<const SIZE: usize, const ALIGN: usize>\n    where\n        Align<ALIGN>: Alignment,\n    {\n        data: UnsafeCell<[MaybeUninit<u8>; SIZE]>,\n        align: Align<ALIGN>,\n    }\n\n    unsafe impl<const SIZE: usize, const ALIGN: usize> Send for TaskPoolHolder<SIZE, ALIGN> where Align<ALIGN>: Alignment {}\n    unsafe impl<const SIZE: usize, const ALIGN: usize> Sync for TaskPoolHolder<SIZE, ALIGN> where Align<ALIGN>: Alignment {}\n\n    #[allow(private_bounds)]\n    impl<const SIZE: usize, const ALIGN: usize> TaskPoolHolder<SIZE, ALIGN>\n    where\n        Align<ALIGN>: Alignment,\n    {\n        pub const fn get(&self) -> *const u8 {\n            self.data.get().cast()\n        }\n    }\n\n    pub const fn task_pool_size<F, Args, Fut, const POOL_SIZE: usize>(_: F) -> usize\n    where\n        F: TaskFn<Args, Fut = Fut>,\n        Fut: Future + 'static,\n    {\n        size_of::<TaskPool<Fut, POOL_SIZE>>()\n    }\n\n    pub const fn task_pool_align<F, Args, Fut, const POOL_SIZE: usize>(_: F) -> usize\n    where\n        F: TaskFn<Args, Fut = Fut>,\n        Fut: Future + 'static,\n    {\n        align_of::<TaskPool<Fut, POOL_SIZE>>()\n    }\n\n    pub const fn task_pool_new<F, Args, Fut, const POOL_SIZE: usize>(_: F) -> TaskPool<Fut, POOL_SIZE>\n    where\n        F: TaskFn<Args, Fut = Fut>,\n        Fut: Future + 'static,\n    {\n        TaskPool::new()\n    }\n\n    #[allow(private_bounds)]\n    #[repr(transparent)]\n    pub struct Align<const N: usize>([<Self as Alignment>::Archetype; 0])\n    where\n        Self: Alignment;\n\n    trait Alignment {\n        /// A zero-sized type of particular alignment.\n        type Archetype: Copy + Eq + PartialEq + Send + Sync + Unpin;\n    }\n\n    macro_rules! aligns {\n        ($($AlignX:ident: $n:literal,)*) => {\n            $(\n                #[derive(Copy, Clone, Eq, PartialEq)]\n                #[repr(align($n))]\n                struct $AlignX {}\n                impl Alignment for Align<$n> {\n                    type Archetype = $AlignX;\n                }\n            )*\n        };\n    }\n\n    aligns!(\n        Align1:         1,\n        Align2:         2,\n        Align4:         4,\n        Align8:         8,\n        Align16:        16,\n        Align32:        32,\n        Align64:        64,\n        Align128:       128,\n        Align256:       256,\n        Align512:       512,\n        Align1024:      1024,\n        Align2048:      2048,\n        Align4096:      4096,\n        Align8192:      8192,\n        Align16384:     16384,\n    );\n    #[cfg(any(target_pointer_width = \"32\", target_pointer_width = \"64\"))]\n    aligns!(\n        Align32768:     32768,\n        Align65536:     65536,\n        Align131072:    131072,\n        Align262144:    262144,\n        Align524288:    524288,\n        Align1048576:   1048576,\n        Align2097152:   2097152,\n        Align4194304:   4194304,\n        Align8388608:   8388608,\n        Align16777216:  16777216,\n        Align33554432:  33554432,\n        Align67108864:  67108864,\n        Align134217728: 134217728,\n        Align268435456: 268435456,\n        Align536870912: 536870912,\n    );\n\n    #[allow(dead_code)]\n    pub trait HasOutput {\n        type Output;\n    }\n\n    impl<O> HasOutput for fn() -> O {\n        type Output = O;\n    }\n\n    #[allow(dead_code)]\n    pub type Never = <fn() -> ! as HasOutput>::Output;\n}\n\n/// Implementation details for embassy macros.\n/// Do not use. Used for macros and HALs only. Not covered by semver guarantees.\n#[doc(hidden)]\n#[cfg(feature = \"nightly\")]\npub mod _export {\n    #[diagnostic::on_unimplemented(\n        message = \"task futures must resolve to `()` or `!`\",\n        note = \"use `async fn` or change the return type to `impl Future<Output = ()>`\"\n    )]\n    pub trait TaskReturnValue {}\n    impl TaskReturnValue for () {}\n    impl TaskReturnValue for Never {}\n\n    #[allow(dead_code)]\n    pub trait HasOutput {\n        type Output;\n    }\n\n    impl<O> HasOutput for fn() -> O {\n        type Output = O;\n    }\n\n    #[allow(dead_code)]\n    pub type Never = <fn() -> ! as HasOutput>::Output;\n}\n"
  },
  {
    "path": "embassy-executor/src/metadata.rs",
    "content": "#[cfg(feature = \"metadata-name\")]\nuse core::cell::Cell;\nuse core::future::{Future, poll_fn};\n#[cfg(feature = \"scheduler-priority\")]\nuse core::sync::atomic::{AtomicU8, Ordering};\nuse core::task::Poll;\n\n#[cfg(feature = \"metadata-name\")]\nuse critical_section::Mutex;\n\nuse crate::raw;\n#[cfg(feature = \"scheduler-deadline\")]\nuse crate::raw::Deadline;\n\n/// Metadata associated with a task.\npub struct Metadata {\n    #[cfg(feature = \"metadata-name\")]\n    name: Mutex<Cell<Option<&'static str>>>,\n    #[cfg(feature = \"scheduler-priority\")]\n    priority: AtomicU8,\n    #[cfg(feature = \"scheduler-deadline\")]\n    deadline: raw::Deadline,\n}\n\nimpl Metadata {\n    pub(crate) const fn new() -> Self {\n        Self {\n            #[cfg(feature = \"metadata-name\")]\n            name: Mutex::new(Cell::new(None)),\n            #[cfg(feature = \"scheduler-priority\")]\n            priority: AtomicU8::new(0),\n            // NOTE: The deadline is set to zero to allow the initializer to reside in `.bss`. This\n            // will be lazily initalized in `initialize_impl`\n            #[cfg(feature = \"scheduler-deadline\")]\n            deadline: raw::Deadline::new_unset(),\n        }\n    }\n\n    pub(crate) fn reset(&self) {\n        #[cfg(feature = \"metadata-name\")]\n        critical_section::with(|cs| self.name.borrow(cs).set(None));\n\n        #[cfg(feature = \"scheduler-priority\")]\n        self.set_priority(0);\n\n        // By default, deadlines are set to the maximum value, so that any task WITH\n        // a set deadline will ALWAYS be scheduled BEFORE a task WITHOUT a set deadline\n        #[cfg(feature = \"scheduler-deadline\")]\n        self.unset_deadline();\n    }\n\n    /// Get the metadata for the current task.\n    ///\n    /// You can use this to read or modify the current task's metadata.\n    ///\n    /// This function is `async` just to get access to the current async\n    /// context. It returns instantly, it does not block/yield.\n    pub fn for_current_task() -> impl Future<Output = &'static Self> {\n        poll_fn(|cx| Poll::Ready(raw::task_from_waker(cx.waker()).metadata()))\n    }\n\n    /// Get this task's name\n    ///\n    /// NOTE: this takes a critical section.\n    #[cfg(feature = \"metadata-name\")]\n    pub fn name(&self) -> Option<&'static str> {\n        critical_section::with(|cs| self.name.borrow(cs).get())\n    }\n\n    /// Set this task's name\n    ///\n    /// NOTE: this takes a critical section.\n    #[cfg(feature = \"metadata-name\")]\n    pub fn set_name(&self, name: &'static str) {\n        critical_section::with(|cs| self.name.borrow(cs).set(Some(name)))\n    }\n\n    /// Get this task's priority.\n    #[cfg(feature = \"scheduler-priority\")]\n    pub fn priority(&self) -> u8 {\n        self.priority.load(Ordering::Relaxed)\n    }\n\n    /// Set this task's priority.\n    #[cfg(feature = \"scheduler-priority\")]\n    pub fn set_priority(&self, priority: u8) {\n        self.priority.store(priority, Ordering::Relaxed)\n    }\n\n    /// Get this task's deadline.\n    #[cfg(feature = \"scheduler-deadline\")]\n    pub fn deadline(&self) -> u64 {\n        self.deadline.instant_ticks()\n    }\n\n    /// Set this task's deadline.\n    ///\n    /// This method does NOT check whether the deadline has already passed.\n    #[cfg(feature = \"scheduler-deadline\")]\n    pub fn set_deadline(&self, instant_ticks: u64) {\n        self.deadline.set(instant_ticks);\n    }\n\n    /// Remove this task's deadline.\n    /// This brings it back to the defaul where it's not scheduled ahead of other tasks.\n    #[cfg(feature = \"scheduler-deadline\")]\n    pub fn unset_deadline(&self) {\n        self.deadline.set(Deadline::UNSET_TICKS);\n    }\n\n    /// Set this task's deadline `duration_ticks` in the future from when\n    /// this future is polled. This deadline is saturated to the max tick value.\n    ///\n    /// Analogous to `Timer::after`.\n    #[cfg(all(feature = \"scheduler-deadline\", feature = \"embassy-time-driver\"))]\n    pub fn set_deadline_after(&self, duration_ticks: u64) {\n        let now = embassy_time_driver::now();\n\n        // Since ticks is a u64, saturating add is PROBABLY overly cautious, leave\n        // it for now, we can probably make this wrapping_add for performance\n        // reasons later.\n        let deadline = now.saturating_add(duration_ticks);\n\n        self.set_deadline(deadline);\n    }\n\n    /// Set the this task's deadline `increment_ticks` from the previous deadline.\n    ///\n    /// This deadline is saturated to the max tick value.\n    ///\n    /// Note that by default (unless otherwise set), tasks start life with the deadline\n    /// not set, which means this method will have no effect.\n    ///\n    /// Analogous to one increment of `Ticker::every().next()`.\n    ///\n    /// Returns the deadline that was set.\n    #[cfg(feature = \"scheduler-deadline\")]\n    pub fn increment_deadline(&self, duration_ticks: u64) {\n        let last = self.deadline();\n\n        // Since ticks is a u64, saturating add is PROBABLY overly cautious, leave\n        // it for now, we can probably make this wrapping_add for performance\n        // reasons later.\n        let deadline = last.saturating_add(duration_ticks);\n\n        self.set_deadline(deadline);\n    }\n}\n"
  },
  {
    "path": "embassy-executor/src/platform/avr.rs",
    "content": "#[cfg(feature = \"executor-interrupt\")]\ncompile_error!(\"`executor-interrupt` is not supported with `arch-avr`.\");\n\n#[cfg(feature = \"executor-thread\")]\npub use thread::*;\n#[cfg(feature = \"executor-thread\")]\nmod thread {\n    use core::marker::PhantomData;\n\n    pub use embassy_executor_macros::main_avr as main;\n    use portable_atomic::{AtomicBool, Ordering};\n\n    use crate::{Spawner, raw};\n\n    static SIGNAL_WORK_THREAD_MODE: AtomicBool = AtomicBool::new(false);\n\n    #[unsafe(export_name = \"__pender\")]\n    fn __pender(_context: *mut ()) {\n        SIGNAL_WORK_THREAD_MODE.store(true, Ordering::SeqCst);\n    }\n\n    /// avr Executor\n    pub struct Executor {\n        inner: raw::Executor,\n        not_send: PhantomData<*mut ()>,\n    }\n\n    impl Executor {\n        /// Create a new Executor.\n        pub fn new() -> Self {\n            Self {\n                inner: raw::Executor::new(core::ptr::null_mut()),\n                not_send: PhantomData,\n            }\n        }\n\n        /// Run the executor.\n        ///\n        /// The `init` closure is called with a [`Spawner`] that spawns tasks on\n        /// this executor. Use it to spawn the initial task(s). After `init` returns,\n        /// the executor starts running the tasks.\n        ///\n        /// To spawn more tasks later, you may keep copies of the [`Spawner`] (it is `Copy`),\n        /// for example by passing it as an argument to the initial tasks.\n        ///\n        /// This function requires `&'static mut self`. This means you have to store the\n        /// Executor instance in a place where it'll live forever and grants you mutable\n        /// access. There's a few ways to do this:\n        ///\n        /// - a [StaticCell](https://docs.rs/static_cell/latest/static_cell/) (safe)\n        /// - a `static mut` (unsafe)\n        /// - a local variable in a function you know never returns (like `fn main() -> !`), upgrading its lifetime with `transmute`. (unsafe)\n        ///\n        /// This function never returns.\n        pub fn run(&'static mut self, init: impl FnOnce(Spawner)) -> ! {\n            init(self.inner.spawner());\n\n            loop {\n                unsafe {\n                    avr_device::interrupt::disable();\n                    if !SIGNAL_WORK_THREAD_MODE.swap(false, Ordering::SeqCst) {\n                        avr_device::interrupt::enable();\n                        avr_device::asm::sleep();\n                    } else {\n                        avr_device::interrupt::enable();\n                        self.inner.poll();\n                    }\n                }\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "embassy-executor/src/platform/cortex_ar.rs",
    "content": "#[cfg(arm_profile = \"legacy\")]\ncompile_error!(\"`arch-cortex-ar` does not support the legacy ARM profile, WFE/SEV are not available.\");\n\n#[cfg(feature = \"executor-interrupt\")]\ncompile_error!(\"`executor-interrupt` is not supported with `arch-cortex-ar`.\");\n\n#[unsafe(export_name = \"__pender\")]\n#[cfg(any(feature = \"executor-thread\", feature = \"executor-interrupt\"))]\nfn __pender(context: *mut ()) {\n    // `context` is always `usize::MAX` created by `Executor::run`.\n    let context = context as usize;\n\n    #[cfg(feature = \"executor-thread\")]\n    // Try to make Rust optimize the branching away if we only use thread mode.\n    if !cfg!(feature = \"executor-interrupt\") || context == THREAD_PENDER {\n        aarch32_cpu::asm::sev();\n        return;\n    }\n}\n\n#[cfg(feature = \"executor-thread\")]\npub use thread::*;\n#[cfg(feature = \"executor-thread\")]\nmod thread {\n    pub(super) const THREAD_PENDER: usize = usize::MAX;\n\n    use core::marker::PhantomData;\n\n    use aarch32_cpu::asm::wfe;\n    pub use embassy_executor_macros::main_cortex_ar as main;\n\n    use crate::{Spawner, raw};\n\n    /// Thread mode executor, using WFE/SEV.\n    ///\n    /// This is the simplest and most common kind of executor. It runs on\n    /// thread mode (at the lowest priority level), and uses the `WFE` ARM instruction\n    /// to sleep when it has no more work to do. When a task is woken, a `SEV` instruction\n    /// is executed, to make the `WFE` exit from sleep and poll the task.\n    ///\n    /// This executor allows for ultra low power consumption for chips where `WFE`\n    /// triggers low-power sleep without extra steps. If your chip requires extra steps,\n    /// you may use [`raw::Executor`] directly to program custom behavior.\n    pub struct Executor {\n        inner: raw::Executor,\n        not_send: PhantomData<*mut ()>,\n    }\n\n    impl Executor {\n        /// Create a new Executor.\n        pub fn new() -> Self {\n            Self {\n                inner: raw::Executor::new(THREAD_PENDER as *mut ()),\n                not_send: PhantomData,\n            }\n        }\n\n        /// Run the executor.\n        ///\n        /// The `init` closure is called with a [`Spawner`] that spawns tasks on\n        /// this executor. Use it to spawn the initial task(s). After `init` returns,\n        /// the executor starts running the tasks.\n        ///\n        /// To spawn more tasks later, you may keep copies of the [`Spawner`] (it is `Copy`),\n        /// for example by passing it as an argument to the initial tasks.\n        ///\n        /// This function requires `&'static mut self`. This means you have to store the\n        /// Executor instance in a place where it'll live forever and grants you mutable\n        /// access. There's a few ways to do this:\n        ///\n        /// - a [StaticCell](https://docs.rs/static_cell/latest/static_cell/) (safe)\n        /// - a `static mut` (unsafe)\n        /// - a local variable in a function you know never returns (like `fn main() -> !`), upgrading its lifetime with `transmute`. (unsafe)\n        ///\n        /// This function never returns.\n        pub fn run(&'static mut self, init: impl FnOnce(Spawner)) -> ! {\n            init(self.inner.spawner());\n\n            loop {\n                unsafe {\n                    self.inner.poll();\n                }\n                wfe();\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "embassy-executor/src/platform/cortex_m.rs",
    "content": "#[unsafe(export_name = \"__pender\")]\n#[cfg(any(feature = \"executor-thread\", feature = \"executor-interrupt\"))]\nfn __pender(context: *mut ()) {\n    unsafe {\n        // Safety: `context` is either `usize::MAX` created by `Executor::run`, or a valid interrupt\n        // request number given to `InterruptExecutor::start`.\n\n        let context = context as usize;\n\n        #[cfg(feature = \"executor-thread\")]\n        // Try to make Rust optimize the branching away if we only use thread mode.\n        if !cfg!(feature = \"executor-interrupt\") || context == THREAD_PENDER {\n            core::arch::asm!(\"sev\");\n            return;\n        }\n\n        #[cfg(feature = \"executor-interrupt\")]\n        {\n            use cortex_m::interrupt::InterruptNumber;\n            use cortex_m::peripheral::NVIC;\n\n            #[derive(Clone, Copy)]\n            struct Irq(u16);\n            unsafe impl InterruptNumber for Irq {\n                fn number(self) -> u16 {\n                    self.0\n                }\n            }\n\n            let irq = Irq(context as u16);\n\n            // STIR is faster, but is only available in v7 and higher.\n            #[cfg(not(armv6m))]\n            {\n                let mut nvic: NVIC = core::mem::transmute(());\n                nvic.request(irq);\n            }\n\n            #[cfg(armv6m)]\n            NVIC::pend(irq);\n        }\n    }\n}\n\n#[cfg(feature = \"executor-thread\")]\npub use thread::*;\n#[cfg(feature = \"executor-thread\")]\nmod thread {\n    pub(super) const THREAD_PENDER: usize = usize::MAX;\n\n    use core::arch::asm;\n    use core::marker::PhantomData;\n\n    pub use embassy_executor_macros::main_cortex_m as main;\n\n    use crate::{Spawner, raw};\n\n    /// Thread mode executor, using WFE/SEV.\n    ///\n    /// This is the simplest and most common kind of executor. It runs on\n    /// thread mode (at the lowest priority level), and uses the `WFE` ARM instruction\n    /// to sleep when it has no more work to do. When a task is woken, a `SEV` instruction\n    /// is executed, to make the `WFE` exit from sleep and poll the task.\n    ///\n    /// This executor allows for ultra low power consumption for chips where `WFE`\n    /// triggers low-power sleep without extra steps. If your chip requires extra steps,\n    /// you may use [`raw::Executor`] directly to program custom behavior.\n    pub struct Executor {\n        inner: raw::Executor,\n        not_send: PhantomData<*mut ()>,\n    }\n\n    impl Executor {\n        /// Create a new Executor.\n        pub fn new() -> Self {\n            Self {\n                inner: raw::Executor::new(THREAD_PENDER as *mut ()),\n                not_send: PhantomData,\n            }\n        }\n\n        /// Run the executor.\n        ///\n        /// The `init` closure is called with a [`Spawner`] that spawns tasks on\n        /// this executor. Use it to spawn the initial task(s). After `init` returns,\n        /// the executor starts running the tasks.\n        ///\n        /// To spawn more tasks later, you may keep copies of the [`Spawner`] (it is `Copy`),\n        /// for example by passing it as an argument to the initial tasks.\n        ///\n        /// This function requires `&'static mut self`. This means you have to store the\n        /// Executor instance in a place where it'll live forever and grants you mutable\n        /// access. There's a few ways to do this:\n        ///\n        /// - a [StaticCell](https://docs.rs/static_cell/latest/static_cell/) (safe)\n        /// - a `static mut` (unsafe)\n        /// - a local variable in a function you know never returns (like `fn main() -> !`), upgrading its lifetime with `transmute`. (unsafe)\n        ///\n        /// This function never returns.\n        pub fn run(&'static mut self, init: impl FnOnce(Spawner)) -> ! {\n            init(self.inner.spawner());\n\n            loop {\n                unsafe {\n                    self.inner.poll();\n                    asm!(\"wfe\");\n                };\n            }\n        }\n    }\n}\n\n#[cfg(feature = \"executor-interrupt\")]\npub use interrupt::*;\n#[cfg(feature = \"executor-interrupt\")]\nmod interrupt {\n    use core::cell::{Cell, UnsafeCell};\n    use core::mem::MaybeUninit;\n\n    use cortex_m::interrupt::InterruptNumber;\n    use cortex_m::peripheral::NVIC;\n    use critical_section::Mutex;\n\n    use crate::raw;\n\n    /// Interrupt mode executor.\n    ///\n    /// This executor runs tasks in interrupt mode. The interrupt handler is set up\n    /// to poll tasks, and when a task is woken the interrupt is pended from software.\n    ///\n    /// This allows running async tasks at a priority higher than thread mode. One\n    /// use case is to leave thread mode free for non-async tasks. Another use case is\n    /// to run multiple executors: one in thread mode for low priority tasks and another in\n    /// interrupt mode for higher priority tasks. Higher priority tasks will preempt lower\n    /// priority ones.\n    ///\n    /// It is even possible to run multiple interrupt mode executors at different priorities,\n    /// by assigning different priorities to the interrupts. For an example on how to do this,\n    /// See the 'multiprio' example for 'embassy-nrf'.\n    ///\n    /// To use it, you have to pick an interrupt that won't be used by the hardware.\n    /// Some chips reserve some interrupts for this purpose, sometimes named \"software interrupts\" (SWI).\n    /// If this is not the case, you may use an interrupt from any unused peripheral.\n    ///\n    /// It is somewhat more complex to use, it's recommended to use the thread-mode\n    /// [`Executor`](crate::Executor) instead, if it works for your use case.\n    pub struct InterruptExecutor {\n        started: Mutex<Cell<bool>>,\n        executor: UnsafeCell<MaybeUninit<raw::Executor>>,\n    }\n\n    unsafe impl Send for InterruptExecutor {}\n    unsafe impl Sync for InterruptExecutor {}\n\n    impl InterruptExecutor {\n        /// Create a new, not started `InterruptExecutor`.\n        #[inline]\n        pub const fn new() -> Self {\n            Self {\n                started: Mutex::new(Cell::new(false)),\n                executor: UnsafeCell::new(MaybeUninit::uninit()),\n            }\n        }\n\n        /// Executor interrupt callback.\n        ///\n        /// # Safety\n        ///\n        /// - You MUST call this from the interrupt handler, and from nowhere else.\n        /// - You must not call this before calling `start()`.\n        pub unsafe fn on_interrupt(&'static self) {\n            let executor = unsafe { (&*self.executor.get()).assume_init_ref() };\n            executor.poll();\n        }\n\n        /// Start the executor.\n        ///\n        /// This initializes the executor, enables the interrupt, and returns.\n        /// The executor keeps running in the background through the interrupt.\n        ///\n        /// This returns a [`SendSpawner`] you can use to spawn tasks on it. A [`SendSpawner`]\n        /// is returned instead of a [`Spawner`](crate::Spawner) because the executor effectively runs in a\n        /// different \"thread\" (the interrupt), so spawning tasks on it is effectively\n        /// sending them.\n        ///\n        /// To obtain a [`Spawner`](crate::Spawner) for this executor, use [`Spawner::for_current_executor()`](crate::Spawner::for_current_executor()) from\n        /// a task running in it.\n        ///\n        /// # Interrupt requirements\n        ///\n        /// You must write the interrupt handler yourself, and make it call [`on_interrupt()`](Self::on_interrupt).\n        ///\n        /// This method already enables (unmasks) the interrupt, you must NOT do it yourself.\n        ///\n        /// You must set the interrupt priority before calling this method. You MUST NOT\n        /// do it after.\n        ///\n        /// [`SendSpawner`]: crate::SendSpawner\n        pub fn start(&'static self, irq: impl InterruptNumber) -> crate::SendSpawner {\n            if critical_section::with(|cs| self.started.borrow(cs).replace(true)) {\n                panic!(\"InterruptExecutor::start() called multiple times on the same executor.\");\n            }\n\n            unsafe {\n                (&mut *self.executor.get())\n                    .as_mut_ptr()\n                    .write(raw::Executor::new(irq.number() as *mut ()))\n            }\n\n            let executor = unsafe { (&*self.executor.get()).assume_init_ref() };\n\n            unsafe { NVIC::unmask(irq) }\n\n            executor.spawner().make_send()\n        }\n\n        /// Get a SendSpawner for this executor\n        ///\n        /// This returns a [`SendSpawner`](crate::SendSpawner) you can use to spawn tasks on this\n        /// executor.\n        ///\n        /// This MUST only be called on an executor that has already been started.\n        /// The function will panic otherwise.\n        pub fn spawner(&'static self) -> crate::SendSpawner {\n            if !critical_section::with(|cs| self.started.borrow(cs).get()) {\n                panic!(\"InterruptExecutor::spawner() called on uninitialized executor.\");\n            }\n            let executor = unsafe { (&*self.executor.get()).assume_init_ref() };\n            executor.spawner().make_send()\n        }\n    }\n}\n"
  },
  {
    "path": "embassy-executor/src/platform/riscv32.rs",
    "content": "#[cfg(feature = \"executor-interrupt\")]\ncompile_error!(\"`executor-interrupt` is not supported with `arch-riscv32`.\");\n\n#[cfg(feature = \"executor-thread\")]\npub use thread::*;\n#[cfg(feature = \"executor-thread\")]\nmod thread {\n    use core::marker::PhantomData;\n    use core::sync::atomic::{AtomicBool, Ordering};\n\n    pub use embassy_executor_macros::main_riscv as main;\n\n    use crate::{Spawner, raw};\n\n    /// global atomic used to keep track of whether there is work to do since sev() is not available on RISCV\n    static SIGNAL_WORK_THREAD_MODE: AtomicBool = AtomicBool::new(false);\n\n    #[unsafe(export_name = \"__pender\")]\n    fn __pender(_context: *mut ()) {\n        SIGNAL_WORK_THREAD_MODE.store(true, Ordering::SeqCst);\n    }\n\n    /// RISCV32 Executor\n    pub struct Executor {\n        inner: raw::Executor,\n        not_send: PhantomData<*mut ()>,\n    }\n\n    impl Executor {\n        /// Create a new Executor.\n        pub fn new() -> Self {\n            Self {\n                inner: raw::Executor::new(core::ptr::null_mut()),\n                not_send: PhantomData,\n            }\n        }\n\n        /// Run the executor.\n        ///\n        /// The `init` closure is called with a [`Spawner`] that spawns tasks on\n        /// this executor. Use it to spawn the initial task(s). After `init` returns,\n        /// the executor starts running the tasks.\n        ///\n        /// To spawn more tasks later, you may keep copies of the [`Spawner`] (it is `Copy`),\n        /// for example by passing it as an argument to the initial tasks.\n        ///\n        /// This function requires `&'static mut self`. This means you have to store the\n        /// Executor instance in a place where it'll live forever and grants you mutable\n        /// access. There's a few ways to do this:\n        ///\n        /// - a [StaticCell](https://docs.rs/static_cell/latest/static_cell/) (safe)\n        /// - a `static mut` (unsafe)\n        /// - a local variable in a function you know never returns (like `fn main() -> !`), upgrading its lifetime with `transmute`. (unsafe)\n        ///\n        /// This function never returns.\n        pub fn run(&'static mut self, init: impl FnOnce(Spawner)) -> ! {\n            init(self.inner.spawner());\n\n            loop {\n                unsafe {\n                    self.inner.poll();\n                    // we do not care about race conditions between the load and store operations, interrupts\n                    //will only set this value to true.\n                    critical_section::with(|_| {\n                        // if there is work to do, loop back to polling\n                        // TODO can we relax this?\n                        if SIGNAL_WORK_THREAD_MODE.load(Ordering::SeqCst) {\n                            SIGNAL_WORK_THREAD_MODE.store(false, Ordering::SeqCst);\n                        }\n                        // if not, wait for interrupt\n                        else {\n                            core::arch::asm!(\"wfi\");\n                        }\n                    });\n                    // if an interrupt occurred while waiting, it will be serviced here\n                }\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "embassy-executor/src/platform/spin.rs",
    "content": "#[cfg(feature = \"executor-interrupt\")]\ncompile_error!(\"`executor-interrupt` is not supported with `arch-spin`.\");\n\n#[cfg(feature = \"executor-thread\")]\npub use thread::*;\n#[cfg(feature = \"executor-thread\")]\nmod thread {\n    use core::marker::PhantomData;\n\n    pub use embassy_executor_macros::main_spin as main;\n\n    use crate::{Spawner, raw};\n\n    #[unsafe(export_name = \"__pender\")]\n    fn __pender(_context: *mut ()) {}\n\n    /// Spin Executor\n    pub struct Executor {\n        inner: raw::Executor,\n        not_send: PhantomData<*mut ()>,\n    }\n\n    impl Executor {\n        /// Create a new Executor.\n        pub fn new() -> Self {\n            Self {\n                inner: raw::Executor::new(core::ptr::null_mut()),\n                not_send: PhantomData,\n            }\n        }\n\n        /// Run the executor.\n        ///\n        /// The `init` closure is called with a [`Spawner`] that spawns tasks on\n        /// this executor. Use it to spawn the initial task(s). After `init` returns,\n        /// the executor starts running the tasks.\n        ///\n        /// To spawn more tasks later, you may keep copies of the [`Spawner`] (it is `Copy`),\n        /// for example by passing it as an argument to the initial tasks.\n        ///\n        /// This function requires `&'static mut self`. This means you have to store the\n        /// Executor instance in a place where it'll live forever and grants you mutable\n        /// access. There's a few ways to do this:\n        ///\n        /// - a [StaticCell](https://docs.rs/static_cell/latest/static_cell/) (safe)\n        /// - a `static mut` (unsafe)\n        /// - a local variable in a function you know never returns (like `fn main() -> !`), upgrading its lifetime with `transmute`. (unsafe)\n        ///\n        /// This function never returns.\n        pub fn run(&'static mut self, init: impl FnOnce(Spawner)) -> ! {\n            init(self.inner.spawner());\n\n            loop {\n                unsafe { self.inner.poll() };\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "embassy-executor/src/platform/std.rs",
    "content": "#[cfg(feature = \"executor-interrupt\")]\ncompile_error!(\"`executor-interrupt` is not supported with `arch-std`.\");\n\n#[cfg(feature = \"executor-thread\")]\npub use thread::*;\n#[cfg(feature = \"executor-thread\")]\nmod thread {\n    use std::marker::PhantomData;\n    use std::sync::{Condvar, Mutex};\n\n    pub use embassy_executor_macros::main_std as main;\n\n    use crate::{Spawner, raw};\n\n    #[unsafe(export_name = \"__pender\")]\n    fn __pender(context: *mut ()) {\n        let signaler: &'static Signaler = unsafe { std::mem::transmute(context) };\n        signaler.signal()\n    }\n\n    /// Single-threaded std-based executor.\n    pub struct Executor {\n        inner: raw::Executor,\n        not_send: PhantomData<*mut ()>,\n        signaler: &'static Signaler,\n    }\n\n    impl Executor {\n        /// Create a new Executor.\n        pub fn new() -> Self {\n            let signaler = Box::leak(Box::new(Signaler::new()));\n            Self {\n                inner: raw::Executor::new(signaler as *mut Signaler as *mut ()),\n                not_send: PhantomData,\n                signaler,\n            }\n        }\n\n        /// Run the executor.\n        ///\n        /// The `init` closure is called with a [`Spawner`] that spawns tasks on\n        /// this executor. Use it to spawn the initial task(s). After `init` returns,\n        /// the executor starts running the tasks.\n        ///\n        /// To spawn more tasks later, you may keep copies of the [`Spawner`] (it is `Copy`),\n        /// for example by passing it as an argument to the initial tasks.\n        ///\n        /// This function requires `&'static mut self`. This means you have to store the\n        /// Executor instance in a place where it'll live forever and grants you mutable\n        /// access. There's a few ways to do this:\n        ///\n        /// - a [StaticCell](https://docs.rs/static_cell/latest/static_cell/) (safe)\n        /// - a `static mut` (unsafe)\n        /// - a local variable in a function you know never returns (like `fn main() -> !`), upgrading its lifetime with `transmute`. (unsafe)\n        ///\n        /// This function never returns.\n        pub fn run(&'static mut self, init: impl FnOnce(Spawner)) -> ! {\n            self.run_until(init, || false);\n            unreachable!()\n        }\n\n        /// Run the executor until a flag is raised.\n        ///\n        /// This function is identical to `Executor::run()` apart from offering a `done` flag to stop execution.\n        pub fn run_until(&'static mut self, init: impl FnOnce(Spawner), mut done: impl FnMut() -> bool) {\n            init(self.inner.spawner());\n\n            loop {\n                unsafe { self.inner.poll() };\n\n                if done() {\n                    break;\n                }\n\n                self.signaler.wait();\n            }\n        }\n    }\n\n    struct Signaler {\n        mutex: Mutex<bool>,\n        condvar: Condvar,\n    }\n\n    impl Signaler {\n        fn new() -> Self {\n            Self {\n                mutex: Mutex::new(false),\n                condvar: Condvar::new(),\n            }\n        }\n\n        fn wait(&self) {\n            let mut signaled = self.mutex.lock().unwrap();\n            while !*signaled {\n                signaled = self.condvar.wait(signaled).unwrap();\n            }\n            *signaled = false;\n        }\n\n        fn signal(&self) {\n            let mut signaled = self.mutex.lock().unwrap();\n            *signaled = true;\n            self.condvar.notify_one();\n        }\n    }\n}\n"
  },
  {
    "path": "embassy-executor/src/platform/wasm.rs",
    "content": "#[cfg(feature = \"executor-interrupt\")]\ncompile_error!(\"`executor-interrupt` is not supported with `arch-wasm`.\");\n\n#[cfg(feature = \"executor-thread\")]\npub use thread::*;\n#[cfg(feature = \"executor-thread\")]\nmod thread {\n\n    use core::marker::PhantomData;\n\n    pub use embassy_executor_macros::main_wasm as main;\n    use js_sys::Promise;\n    use wasm_bindgen::prelude::*;\n\n    use crate::raw::util::UninitCell;\n    use crate::{Spawner, raw};\n\n    #[unsafe(export_name = \"__pender\")]\n    fn __pender(context: *mut ()) {\n        let signaler: &'static WasmContext = unsafe { std::mem::transmute(context) };\n        let _ = signaler.promise.then(unsafe { signaler.closure.as_mut() });\n    }\n\n    pub(crate) struct WasmContext {\n        promise: Promise,\n        closure: UninitCell<Closure<dyn FnMut(JsValue)>>,\n    }\n\n    impl WasmContext {\n        pub fn new() -> Self {\n            Self {\n                promise: Promise::resolve(&JsValue::undefined()),\n                closure: UninitCell::uninit(),\n            }\n        }\n    }\n\n    /// WASM executor, wasm_bindgen to schedule tasks on the JS event loop.\n    pub struct Executor {\n        inner: raw::Executor,\n        ctx: &'static WasmContext,\n        not_send: PhantomData<*mut ()>,\n    }\n\n    impl Executor {\n        /// Create a new Executor.\n        pub fn new() -> Self {\n            let ctx = Box::leak(Box::new(WasmContext::new()));\n            Self {\n                inner: raw::Executor::new(ctx as *mut WasmContext as *mut ()),\n                ctx,\n                not_send: PhantomData,\n            }\n        }\n\n        /// Run the executor.\n        ///\n        /// The `init` closure is called with a [`Spawner`] that spawns tasks on\n        /// this executor. Use it to spawn the initial task(s). After `init` returns,\n        /// the executor starts running the tasks.\n        ///\n        /// To spawn more tasks later, you may keep copies of the [`Spawner`] (it is `Copy`),\n        /// for example by passing it as an argument to the initial tasks.\n        ///\n        /// This function requires `&'static mut self`. This means you have to store the\n        /// Executor instance in a place where it'll live forever and grants you mutable\n        /// access. There's a few ways to do this:\n        ///\n        /// - a [StaticCell](https://docs.rs/static_cell/latest/static_cell/) (safe)\n        /// - a `static mut` (unsafe)\n        /// - a local variable in a function you know never returns (like `fn main() -> !`), upgrading its lifetime with `transmute`. (unsafe)\n        pub fn start(&'static mut self, init: impl FnOnce(Spawner)) {\n            unsafe {\n                let executor = &self.inner;\n                let future = Closure::new(move |_| {\n                    executor.poll();\n                });\n                self.ctx.closure.write_in_place(|| future);\n                init(self.inner.spawner());\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "embassy-executor/src/raw/deadline.rs",
    "content": "use core::sync::atomic::{AtomicU32, Ordering};\n\n/// A type for interacting with the deadline of the current task\n///\n/// Requires the `scheduler-deadline` feature.\n///\n/// Note: Interacting with the deadline should be done locally in a task.\n/// In theory you could try to set or read the deadline from another task,\n/// but that will result in weird (though not unsound) behavior.\npub(crate) struct Deadline {\n    instant_ticks_hi: AtomicU32,\n    instant_ticks_lo: AtomicU32,\n}\n\nimpl Deadline {\n    pub(crate) const fn new(instant_ticks: u64) -> Self {\n        Self {\n            instant_ticks_hi: AtomicU32::new((instant_ticks >> 32) as u32),\n            instant_ticks_lo: AtomicU32::new(instant_ticks as u32),\n        }\n    }\n\n    pub(crate) const fn new_unset() -> Self {\n        Self::new(Self::UNSET_TICKS)\n    }\n\n    pub(crate) fn set(&self, instant_ticks: u64) {\n        self.instant_ticks_hi\n            .store((instant_ticks >> 32) as u32, Ordering::Relaxed);\n        self.instant_ticks_lo.store(instant_ticks as u32, Ordering::Relaxed);\n    }\n\n    /// Deadline value in ticks, same time base and ticks as `embassy-time`\n    pub(crate) fn instant_ticks(&self) -> u64 {\n        let hi = self.instant_ticks_hi.load(Ordering::Relaxed) as u64;\n        let lo = self.instant_ticks_lo.load(Ordering::Relaxed) as u64;\n\n        (hi << 32) | lo\n    }\n\n    /// Sentinel value representing an \"unset\" deadline, which has lower priority\n    /// than any other set deadline value\n    pub(crate) const UNSET_TICKS: u64 = u64::MAX;\n}\n"
  },
  {
    "path": "embassy-executor/src/raw/mod.rs",
    "content": "//! Raw executor.\n//!\n//! This module exposes \"raw\" Executor and Task structs for more low level control.\n//!\n//! ## WARNING: here be dragons!\n//!\n//! Using this module requires respecting subtle safety contracts. If you can, prefer using the safe\n//! [executor wrappers](crate::Executor) and the [`embassy_executor::task`](embassy_executor_macros::task) macro, which are fully safe.\n\nmod run_queue;\n\n#[cfg_attr(all(cortex_m, target_has_atomic = \"32\"), path = \"state_atomics_arm.rs\")]\n#[cfg_attr(\n    all(not(cortex_m), any(target_has_atomic = \"8\", target_has_atomic = \"32\")),\n    path = \"state_atomics.rs\"\n)]\n#[cfg_attr(\n    not(any(target_has_atomic = \"8\", target_has_atomic = \"32\")),\n    path = \"state_critical_section.rs\"\n)]\nmod state;\n\n#[cfg(feature = \"_any_trace\")]\npub mod trace;\npub(crate) mod util;\n#[cfg_attr(feature = \"turbowakers\", path = \"waker_turbo.rs\")]\nmod waker;\n\n#[cfg(feature = \"scheduler-deadline\")]\nmod deadline;\n\nuse core::future::Future;\nuse core::marker::PhantomData;\nuse core::mem;\nuse core::pin::Pin;\nuse core::ptr::NonNull;\n#[cfg(not(feature = \"platform-avr\"))]\nuse core::sync::atomic::AtomicPtr;\nuse core::sync::atomic::Ordering;\nuse core::task::{Context, Poll, Waker};\n\n#[cfg(feature = \"scheduler-deadline\")]\npub(crate) use deadline::Deadline;\nuse embassy_executor_timer_queue::TimerQueueItem;\n#[cfg(feature = \"platform-avr\")]\nuse portable_atomic::AtomicPtr;\n\nuse self::run_queue::{RunQueue, RunQueueItem};\nuse self::state::State;\nuse self::util::{SyncUnsafeCell, UninitCell};\npub use self::waker::task_from_waker;\nuse self::waker::try_task_from_waker;\nuse super::SpawnToken;\nuse crate::{Metadata, SpawnError};\n\n#[unsafe(no_mangle)]\nextern \"Rust\" fn __embassy_time_queue_item_from_waker(waker: &Waker) -> &'static mut TimerQueueItem {\n    unsafe { task_from_waker(waker).timer_queue_item() }\n}\n\n#[unsafe(no_mangle)]\nextern \"Rust\" fn __try_embassy_time_queue_item_from_waker(waker: &Waker) -> Option<&'static mut TimerQueueItem> {\n    unsafe { try_task_from_waker(waker).map(|task| task.timer_queue_item()) }\n}\n\n/// Raw task header for use in task pointers.\n///\n/// A task can be in one of the following states:\n///\n/// - Not spawned: the task is ready to spawn.\n/// - `SPAWNED`: the task is currently spawned and may be running.\n/// - `RUN_ENQUEUED`: the task is enqueued to be polled. Note that the task may be `!SPAWNED`.\n///    In this case, the `RUN_ENQUEUED` state will be cleared when the task is next polled, without\n///    polling the task's future.\n///\n/// A task's complete life cycle is as follows:\n///\n/// ```text\n/// ┌────────────┐   ┌────────────────────────┐\n/// │Not spawned │◄─5┤Not spawned|Run enqueued│\n/// │            ├6─►│                        │\n/// └─────┬──────┘   └──────▲─────────────────┘\n///       1                 │\n///       │    ┌────────────┘\n///       │    4\n/// ┌─────▼────┴─────────┐\n/// │Spawned|Run enqueued│\n/// │                    │\n/// └─────┬▲─────────────┘\n///       2│\n///       │3\n/// ┌─────▼┴─────┐\n/// │  Spawned   │\n/// │            │\n/// └────────────┘\n/// ```\n///\n/// Transitions:\n/// - 1: Task is spawned - `AvailableTask::claim -> Executor::spawn`\n/// - 2: During poll - `RunQueue::dequeue_all -> State::run_dequeue`\n/// - 3: Task wakes itself, waker wakes task, or task exits - `Waker::wake -> wake_task -> State::run_enqueue`\n/// - 4: A run-queued task exits - `TaskStorage::poll -> Poll::Ready`\n/// - 5: Task is dequeued. The task's future is not polled, because exiting the task replaces its `poll_fn`.\n/// - 6: A task is waken when it is not spawned - `wake_task -> State::run_enqueue`\npub(crate) struct TaskHeader {\n    pub(crate) state: State,\n    pub(crate) run_queue_item: RunQueueItem,\n\n    pub(crate) executor: AtomicPtr<SyncExecutor>,\n    poll_fn: SyncUnsafeCell<Option<unsafe fn(TaskRef)>>,\n\n    /// Integrated timer queue storage. This field should not be accessed outside of the timer queue.\n    pub(crate) timer_queue_item: TimerQueueItem,\n\n    pub(crate) metadata: Metadata,\n\n    #[cfg(feature = \"rtos-trace\")]\n    all_tasks_next: AtomicPtr<TaskHeader>,\n}\n\n/// This is essentially a `&'static TaskStorage<F>` where the type of the future has been erased.\n#[derive(Debug, Clone, Copy, PartialEq)]\npub struct TaskRef {\n    ptr: NonNull<TaskHeader>,\n}\n\nunsafe impl Send for TaskRef where &'static TaskHeader: Send {}\nunsafe impl Sync for TaskRef where &'static TaskHeader: Sync {}\n\nimpl TaskRef {\n    fn new<F: Future + 'static>(task: &'static TaskStorage<F>) -> Self {\n        Self {\n            ptr: NonNull::from(task).cast(),\n        }\n    }\n\n    /// Safety: The pointer must have been obtained with `Task::as_ptr`\n    pub(crate) unsafe fn from_ptr(ptr: *const TaskHeader) -> Self {\n        Self {\n            ptr: NonNull::new_unchecked(ptr as *mut TaskHeader),\n        }\n    }\n\n    pub(crate) fn header(self) -> &'static TaskHeader {\n        unsafe { self.ptr.as_ref() }\n    }\n\n    pub(crate) fn metadata(self) -> &'static Metadata {\n        unsafe { &self.ptr.as_ref().metadata }\n    }\n\n    /// Returns a reference to the executor that the task is currently running on.\n    pub unsafe fn executor(self) -> Option<&'static Executor> {\n        let executor = self.header().executor.load(Ordering::Relaxed);\n        executor.as_ref().map(|e| Executor::wrap(e))\n    }\n\n    /// Returns a mutable reference to the timer queue item.\n    ///\n    /// Safety\n    ///\n    /// This function must only be called in the context of the integrated timer queue.\n    pub unsafe fn timer_queue_item(mut self) -> &'static mut TimerQueueItem {\n        unsafe { &mut self.ptr.as_mut().timer_queue_item }\n    }\n\n    /// The returned pointer is valid for the entire TaskStorage.\n    pub(crate) fn as_ptr(self) -> *const TaskHeader {\n        self.ptr.as_ptr()\n    }\n\n    /// Returns the task ID.\n    /// This can be used in combination with rtos-trace to match task names with IDs\n    pub fn id(&self) -> u32 {\n        self.as_ptr() as u32\n    }\n}\n\n/// Raw storage in which a task can be spawned.\n///\n/// This struct holds the necessary memory to spawn one task whose future is `F`.\n/// At a given time, the `TaskStorage` may be in spawned or not-spawned state. You\n/// may spawn it with [`TaskStorage::spawn()`], which will fail if it is already spawned.\n///\n/// A `TaskStorage` must live forever, it may not be deallocated even after the task has finished\n/// running. Hence the relevant methods require `&'static self`. It may be reused, however.\n///\n/// Internally, the [embassy_executor::task](embassy_executor_macros::task) macro allocates an array of `TaskStorage`s\n/// in a `static`. The most common reason to use the raw `Task` is to have control of where\n/// the memory for the task is allocated: on the stack, or on the heap with e.g. `Box::leak`, etc.\n\n// repr(C) is needed to guarantee that the Task is located at offset 0\n// This makes it safe to cast between TaskHeader and TaskStorage pointers.\n#[repr(C)]\npub struct TaskStorage<F: Future + 'static> {\n    raw: TaskHeader,\n    future: UninitCell<F>, // Valid if STATE_SPAWNED\n}\n\nunsafe fn poll_exited(_p: TaskRef) {\n    // Nothing to do, the task is already !SPAWNED and dequeued.\n}\n\nimpl<F: Future + 'static> TaskStorage<F> {\n    const NEW: Self = Self::new();\n\n    /// Create a new TaskStorage, in not-spawned state.\n    pub const fn new() -> Self {\n        Self {\n            raw: TaskHeader {\n                state: State::new(),\n                run_queue_item: RunQueueItem::new(),\n                executor: AtomicPtr::new(core::ptr::null_mut()),\n                // Note: this is lazily initialized so that a static `TaskStorage` will go in `.bss`\n                poll_fn: SyncUnsafeCell::new(None),\n\n                timer_queue_item: TimerQueueItem::new(),\n                metadata: Metadata::new(),\n                #[cfg(feature = \"rtos-trace\")]\n                all_tasks_next: AtomicPtr::new(core::ptr::null_mut()),\n            },\n            future: UninitCell::uninit(),\n        }\n    }\n\n    /// Try to spawn the task.\n    ///\n    /// The `future` closure constructs the future. It's only called if spawning is\n    /// actually possible. It is a closure instead of a simple `future: F` param to ensure\n    /// the future is constructed in-place, avoiding a temporary copy in the stack thanks to\n    /// NRVO optimizations.\n    ///\n    /// This function will fail if the task is already spawned and has not finished running.\n    /// In this case, the error is delayed: a \"poisoned\" SpawnToken is returned, which will\n    /// cause [`Spawner::spawn()`](super::Spawner::spawn) to return the error.\n    ///\n    /// Once the task has finished running, you may spawn it again. It is allowed to spawn it\n    /// on a different executor.\n    pub fn spawn(&'static self, future: impl FnOnce() -> F) -> Result<SpawnToken<impl Sized>, SpawnError> {\n        let task = AvailableTask::claim(self);\n        match task {\n            Some(task) => Ok(task.initialize(future)),\n            None => Err(SpawnError::Busy),\n        }\n    }\n\n    unsafe fn poll(p: TaskRef) {\n        let this = &*p.as_ptr().cast::<TaskStorage<F>>();\n\n        let future = Pin::new_unchecked(this.future.as_mut());\n        let waker = waker::from_task(p);\n        let mut cx = Context::from_waker(&waker);\n        match future.poll(&mut cx) {\n            Poll::Ready(_) => {\n                #[cfg(feature = \"_any_trace\")]\n                let exec_ptr: *const SyncExecutor = this.raw.executor.load(Ordering::Relaxed);\n\n                // As the future has finished and this function will not be called\n                // again, we can safely drop the future here.\n                this.future.drop_in_place();\n\n                // We replace the poll_fn with a despawn function, so that the task is cleaned up\n                // when the executor polls it next.\n                this.raw.poll_fn.set(Some(poll_exited));\n\n                // Make sure we despawn last, so that other threads can only spawn the task\n                // after we're done with it.\n                this.raw.state.despawn();\n\n                #[cfg(feature = \"_any_trace\")]\n                trace::task_end(exec_ptr, &p);\n            }\n            Poll::Pending => {}\n        }\n\n        // the compiler is emitting a virtual call for waker drop, but we know\n        // it's a noop for our waker.\n        mem::forget(waker);\n    }\n\n    #[doc(hidden)]\n    #[allow(dead_code)]\n    fn _assert_sync(self) {\n        fn assert_sync<T: Sync>(_: T) {}\n\n        assert_sync(self)\n    }\n}\n\n/// An uninitialized [`TaskStorage`].\npub struct AvailableTask<F: Future + 'static> {\n    task: &'static TaskStorage<F>,\n}\n\nimpl<F: Future + 'static> AvailableTask<F> {\n    /// Try to claim a [`TaskStorage`].\n    ///\n    /// This function returns `None` if a task has already been spawned and has not finished running.\n    pub fn claim(task: &'static TaskStorage<F>) -> Option<Self> {\n        task.raw.state.spawn().then(|| Self { task })\n    }\n\n    fn initialize_impl<S>(self, future: impl FnOnce() -> F) -> SpawnToken<S> {\n        unsafe {\n            self.task.raw.metadata.reset();\n            self.task.raw.poll_fn.set(Some(TaskStorage::<F>::poll));\n            self.task.future.write_in_place(future);\n\n            let task = TaskRef::new(self.task);\n\n            SpawnToken::new(task)\n        }\n    }\n\n    /// Initialize the [`TaskStorage`] to run the given future.\n    pub fn initialize(self, future: impl FnOnce() -> F) -> SpawnToken<F> {\n        self.initialize_impl::<F>(future)\n    }\n\n    /// Initialize the [`TaskStorage`] to run the given future.\n    ///\n    /// # Safety\n    ///\n    /// `future` must be a closure of the form `move || my_async_fn(args)`, where `my_async_fn`\n    /// is an `async fn`, NOT a hand-written `Future`.\n    #[doc(hidden)]\n    pub unsafe fn __initialize_async_fn<FutFn>(self, future: impl FnOnce() -> F) -> SpawnToken<FutFn> {\n        // When send-spawning a task, we construct the future in this thread, and effectively\n        // \"send\" it to the executor thread by enqueuing it in its queue. Therefore, in theory,\n        // send-spawning should require the future `F` to be `Send`.\n        //\n        // The problem is this is more restrictive than needed. Once the future is executing,\n        // it is never sent to another thread. It is only sent when spawning. It should be\n        // enough for the task's arguments to be Send. (and in practice it's super easy to\n        // accidentally make your futures !Send, for example by holding an `Rc` or a `&RefCell` across an `.await`.)\n        //\n        // We can do it by sending the task args and constructing the future in the executor thread\n        // on first poll. However, this cannot be done in-place, so it'll waste stack space for a copy\n        // of the args.\n        //\n        // Luckily, an `async fn` future contains just the args when freshly constructed. So, if the\n        // args are Send, it's OK to send a !Send future, as long as we do it before first polling it.\n        //\n        // (Note: this is how the generators are implemented today, it's not officially guaranteed yet,\n        // but it's possible it'll be guaranteed in the future. See zulip thread:\n        // https://rust-lang.zulipchat.com/#narrow/stream/187312-wg-async/topic/.22only.20before.20poll.22.20Send.20futures )\n        //\n        // The `FutFn` captures all the args, so if it's Send, the task can be send-spawned.\n        // This is why we return `SpawnToken<FutFn>` below.\n        //\n        // This ONLY holds for `async fn` futures. The other `spawn` methods can be called directly\n        // by the user, with arbitrary hand-implemented futures. This is why these return `SpawnToken<F>`.\n        self.initialize_impl::<FutFn>(future)\n    }\n}\n\n/// Raw storage that can hold up to N tasks of the same type.\n///\n/// This is essentially a `[TaskStorage<F>; N]`.\npub struct TaskPool<F: Future + 'static, const N: usize> {\n    pool: [TaskStorage<F>; N],\n}\n\nimpl<F: Future + 'static, const N: usize> TaskPool<F, N> {\n    /// Create a new TaskPool, with all tasks in non-spawned state.\n    pub const fn new() -> Self {\n        Self {\n            pool: [TaskStorage::NEW; N],\n        }\n    }\n\n    fn spawn_impl<T>(&'static self, future: impl FnOnce() -> F) -> Result<SpawnToken<T>, SpawnError> {\n        match self.pool.iter().find_map(AvailableTask::claim) {\n            Some(task) => Ok(task.initialize_impl::<T>(future)),\n            None => Err(SpawnError::Busy),\n        }\n    }\n\n    /// Try to spawn a task in the pool.\n    ///\n    /// See [`TaskStorage::spawn()`] for details.\n    ///\n    /// This will loop over the pool and spawn the task in the first storage that\n    /// is currently free. If none is free, a \"poisoned\" SpawnToken is returned,\n    /// which will cause [`Spawner::spawn()`](super::Spawner::spawn) to return the error.\n    pub fn spawn(&'static self, future: impl FnOnce() -> F) -> Result<SpawnToken<impl Sized>, SpawnError> {\n        self.spawn_impl::<F>(future)\n    }\n\n    /// Like spawn(), but allows the task to be send-spawned if the args are Send even if\n    /// the future is !Send.\n    ///\n    /// Not covered by semver guarantees. DO NOT call this directly. Intended to be used\n    /// by the Embassy macros ONLY.\n    ///\n    /// SAFETY: `future` must be a closure of the form `move || my_async_fn(args)`, where `my_async_fn`\n    /// is an `async fn`, NOT a hand-written `Future`.\n    #[doc(hidden)]\n    pub unsafe fn _spawn_async_fn<FutFn>(&'static self, future: FutFn) -> Result<SpawnToken<impl Sized>, SpawnError>\n    where\n        FutFn: FnOnce() -> F,\n    {\n        // See the comment in AvailableTask::__initialize_async_fn for explanation.\n        self.spawn_impl::<FutFn>(future)\n    }\n}\n\n#[derive(Clone, Copy)]\npub(crate) struct Pender(*mut ());\n\nunsafe impl Send for Pender {}\nunsafe impl Sync for Pender {}\n\nimpl Pender {\n    pub(crate) fn pend(self) {\n        unsafe extern \"Rust\" {\n            fn __pender(context: *mut ());\n        }\n        unsafe { __pender(self.0) };\n    }\n}\n\npub(crate) struct SyncExecutor {\n    run_queue: RunQueue,\n    pender: Pender,\n}\n\nimpl SyncExecutor {\n    pub(crate) fn new(pender: Pender) -> Self {\n        Self {\n            run_queue: RunQueue::new(),\n            pender,\n        }\n    }\n\n    /// Enqueue a task in the task queue\n    ///\n    /// # Safety\n    /// - `task` must be a valid pointer to a spawned task.\n    /// - `task` must be set up to run in this executor.\n    /// - `task` must NOT be already enqueued (in this executor or another one).\n    #[inline(always)]\n    unsafe fn enqueue(&self, task: TaskRef, l: state::Token) {\n        #[cfg(feature = \"_any_trace\")]\n        trace::task_ready_begin(self, &task);\n\n        if self.run_queue.enqueue(task, l) {\n            self.pender.pend();\n        }\n    }\n\n    pub(super) unsafe fn spawn(&'static self, task: TaskRef) {\n        task.header()\n            .executor\n            .store((self as *const Self).cast_mut(), Ordering::Relaxed);\n\n        #[cfg(feature = \"_any_trace\")]\n        trace::task_new(self, &task);\n\n        state::locked(|l| {\n            self.enqueue(task, l);\n        })\n    }\n\n    /// # Safety\n    ///\n    /// Same as [`Executor::poll`], plus you must only call this on the thread this executor was created.\n    pub(crate) unsafe fn poll(&'static self) {\n        #[cfg(feature = \"_any_trace\")]\n        trace::poll_start(self);\n\n        self.run_queue.dequeue_all(|p| {\n            let task = p.header();\n\n            #[cfg(feature = \"_any_trace\")]\n            trace::task_exec_begin(self, &p);\n\n            // Run the task\n            task.poll_fn.get().unwrap_unchecked()(p);\n\n            #[cfg(feature = \"_any_trace\")]\n            trace::task_exec_end(self, &p);\n        });\n\n        #[cfg(feature = \"_any_trace\")]\n        trace::executor_idle(self)\n    }\n}\n\n/// Raw executor.\n///\n/// This is the core of the Embassy executor. It is low-level, requiring manual\n/// handling of wakeups and task polling. If you can, prefer using one of the\n/// [higher level executors](crate::Executor).\n///\n/// The raw executor leaves it up to you to handle wakeups and scheduling:\n///\n/// - To get the executor to do work, call `poll()`. This will poll all queued tasks (all tasks\n///   that \"want to run\").\n/// - You must supply a pender function, as shown below. The executor will call it to notify you\n///   it has work to do. You must arrange for `poll()` to be called as soon as possible.\n/// - Enabling `arch-xx` features will define a pender function for you. This means that you\n///   are limited to using the executors provided to you by the architecture/platform\n///   implementation. If you need a different executor, you must not enable `arch-xx` features.\n///\n/// The pender can be called from *any* context: any thread, any interrupt priority\n/// level, etc. It may be called synchronously from any `Executor` method call as well.\n/// You must deal with this correctly.\n///\n/// In particular, you must NOT call `poll` directly from the pender callback, as this violates\n/// the requirement for `poll` to not be called reentrantly.\n///\n/// The pender function must be exported with the name `__pender` and have the following signature:\n///\n/// ```rust\n/// #[unsafe(export_name = \"__pender\")]\n/// fn pender(context: *mut ()) {\n///    // schedule `poll()` to be called\n/// }\n/// ```\n///\n/// The `context` argument is a piece of arbitrary data the executor will pass to the pender.\n/// You can set the `context` when calling [`Executor::new()`]. You can use it to, for example,\n/// differentiate between executors, or to pass a pointer to a callback that should be called.\n#[repr(transparent)]\npub struct Executor {\n    pub(crate) inner: SyncExecutor,\n\n    _not_sync: PhantomData<*mut ()>,\n}\n\nimpl Executor {\n    pub(crate) unsafe fn wrap(inner: &SyncExecutor) -> &Self {\n        mem::transmute(inner)\n    }\n\n    /// Create a new executor.\n    ///\n    /// When the executor has work to do, it will call the pender function and pass `context` to it.\n    ///\n    /// See [`Executor`] docs for details on the pender.\n    pub fn new(context: *mut ()) -> Self {\n        Self {\n            inner: SyncExecutor::new(Pender(context)),\n            _not_sync: PhantomData,\n        }\n    }\n\n    /// Spawn a task in this executor.\n    ///\n    /// # Safety\n    ///\n    /// `task` must be a valid pointer to an initialized but not-already-spawned task.\n    ///\n    /// It is OK to use `unsafe` to call this from a thread that's not the executor thread.\n    /// In this case, the task's Future must be Send. This is because this is effectively\n    /// sending the task to the executor thread.\n    pub(super) unsafe fn spawn(&'static self, task: TaskRef) {\n        self.inner.spawn(task)\n    }\n\n    /// Poll all queued tasks in this executor.\n    ///\n    /// This loops over all tasks that are queued to be polled (i.e. they're\n    /// freshly spawned or they've been woken). Other tasks are not polled.\n    ///\n    /// You must call `poll` after receiving a call to the pender. It is OK\n    /// to call `poll` even when not requested by the pender, but it wastes\n    /// energy.\n    ///\n    /// # Safety\n    ///\n    /// You must NOT call `poll` reentrantly on the same executor.\n    ///\n    /// In particular, note that `poll` may call the pender synchronously. Therefore, you\n    /// must NOT directly call `poll()` from the pender callback. Instead, the callback has to\n    /// somehow schedule for `poll()` to be called later, at a time you know for sure there's\n    /// no `poll()` already running.\n    pub unsafe fn poll(&'static self) {\n        self.inner.poll()\n    }\n\n    /// Get a spawner that spawns tasks in this executor.\n    ///\n    /// It is OK to call this method multiple times to obtain multiple\n    /// `Spawner`s. You may also copy `Spawner`s.\n    pub fn spawner(&'static self) -> super::Spawner {\n        super::Spawner::new(self)\n    }\n\n    /// Get a unique ID for this Executor.\n    pub fn id(&'static self) -> usize {\n        &self.inner as *const SyncExecutor as usize\n    }\n}\n\n/// Wake a task by `TaskRef`.\n///\n/// You can obtain a `TaskRef` from a `Waker` using [`task_from_waker`].\npub fn wake_task(task: TaskRef) {\n    let header = task.header();\n    header.state.run_enqueue(|l| {\n        // We have just marked the task as scheduled, so enqueue it.\n        unsafe {\n            let executor = header.executor.load(Ordering::Relaxed).as_ref().unwrap_unchecked();\n            executor.enqueue(task, l);\n        }\n    });\n}\n\n/// Wake a task by `TaskRef` without calling pend.\n///\n/// You can obtain a `TaskRef` from a `Waker` using [`task_from_waker`].\npub fn wake_task_no_pend(task: TaskRef) {\n    let header = task.header();\n    header.state.run_enqueue(|l| {\n        // We have just marked the task as scheduled, so enqueue it.\n        unsafe {\n            let executor = header.executor.load(Ordering::Relaxed).as_ref().unwrap_unchecked();\n            executor.run_queue.enqueue(task, l);\n        }\n    });\n}\n"
  },
  {
    "path": "embassy-executor/src/raw/run_queue.rs",
    "content": "use core::ptr::{NonNull, addr_of_mut};\n\nuse cordyceps::Linked;\n#[cfg(any(feature = \"scheduler-priority\", feature = \"scheduler-deadline\"))]\nuse cordyceps::SortedList;\nuse cordyceps::sorted_list::Links;\n\n#[cfg(target_has_atomic = \"ptr\")]\ntype TransferStack<T> = cordyceps::TransferStack<T>;\n\n#[cfg(not(target_has_atomic = \"ptr\"))]\ntype TransferStack<T> = MutexTransferStack<T>;\n\nuse super::{TaskHeader, TaskRef};\n\n/// Use `cordyceps::sorted_list::Links` as the singly linked list\n/// for RunQueueItems.\npub(crate) type RunQueueItem = Links<TaskHeader>;\n\n/// Implements the `Linked` trait, allowing for singly linked list usage\n/// of any of cordyceps' `TransferStack` (used for the atomic runqueue),\n/// `SortedList` (used with the DRS scheduler), or `Stack`, which is\n/// popped atomically from the `TransferStack`.\nunsafe impl Linked<Links<TaskHeader>> for TaskHeader {\n    type Handle = TaskRef;\n\n    // Convert a TaskRef into a TaskHeader ptr\n    fn into_ptr(r: TaskRef) -> NonNull<TaskHeader> {\n        r.ptr\n    }\n\n    // Convert a TaskHeader into a TaskRef\n    unsafe fn from_ptr(ptr: NonNull<TaskHeader>) -> TaskRef {\n        TaskRef { ptr }\n    }\n\n    // Given a pointer to a TaskHeader, obtain a pointer to the Links structure,\n    // which can be used to traverse to other TaskHeader nodes in the linked list\n    unsafe fn links(ptr: NonNull<TaskHeader>) -> NonNull<Links<TaskHeader>> {\n        let ptr: *mut TaskHeader = ptr.as_ptr();\n        NonNull::new_unchecked(addr_of_mut!((*ptr).run_queue_item))\n    }\n}\n\n/// Atomic task queue using a very, very simple lock-free linked-list queue:\n///\n/// To enqueue a task, task.next is set to the old head, and head is atomically set to task.\n///\n/// Dequeuing is done in batches: the queue is emptied by atomically replacing head with\n/// null. Then the batch is iterated following the next pointers until null is reached.\n///\n/// Note that batches will be iterated in the reverse order as they were enqueued. This is OK\n/// for our purposes: it can't create fairness problems since the next batch won't run until the\n/// current batch is completely processed, so even if a task enqueues itself instantly (for example\n/// by waking its own waker) can't prevent other tasks from running.\npub(crate) struct RunQueue {\n    stack: TransferStack<TaskHeader>,\n}\n\nimpl RunQueue {\n    pub const fn new() -> Self {\n        Self {\n            stack: TransferStack::new(),\n        }\n    }\n\n    /// Enqueues an item. Returns true if the queue was empty.\n    ///\n    /// # Safety\n    ///\n    /// `item` must NOT be already enqueued in any queue.\n    #[inline(always)]\n    pub(crate) unsafe fn enqueue(&self, task: TaskRef, _tok: super::state::Token) -> bool {\n        self.stack.push_was_empty(\n            task,\n            #[cfg(not(target_has_atomic = \"ptr\"))]\n            _tok,\n        )\n    }\n\n    /// # Standard atomic runqueue\n    ///\n    /// Empty the queue, then call `on_task` for each task that was in the queue.\n    /// NOTE: It is OK for `on_task` to enqueue more tasks. In this case they're left in the queue\n    /// and will be processed by the *next* call to `dequeue_all`, *not* the current one.\n    #[cfg(not(any(feature = \"scheduler-priority\", feature = \"scheduler-deadline\")))]\n    pub(crate) fn dequeue_all(&self, on_task: impl Fn(TaskRef)) {\n        let taken = self.stack.take_all();\n        for taskref in taken {\n            run_dequeue(&taskref);\n            on_task(taskref);\n        }\n    }\n\n    /// # Earliest Deadline First Scheduler\n    ///\n    /// This algorithm will loop until all enqueued tasks are processed.\n    ///\n    /// Before polling a task, all currently enqueued tasks will be popped from the\n    /// runqueue, and will be added to the working `sorted` list, a linked-list that\n    /// sorts tasks by their deadline, with nearest deadline items in the front, and\n    /// furthest deadline items in the back.\n    ///\n    /// After popping and sorting all pending tasks, the SOONEST task will be popped\n    /// from the front of the queue, and polled by calling `on_task` on it.\n    ///\n    /// This process will repeat until the local `sorted` queue AND the global\n    /// runqueue are both empty, at which point this function will return.\n    #[cfg(any(feature = \"scheduler-priority\", feature = \"scheduler-deadline\"))]\n    pub(crate) fn dequeue_all(&self, on_task: impl Fn(TaskRef)) {\n        let mut sorted = SortedList::<TaskHeader>::new_with_cmp(|lhs, rhs| {\n            // compare by priority first\n            #[cfg(feature = \"scheduler-priority\")]\n            {\n                let lp = lhs.metadata.priority();\n                let rp = rhs.metadata.priority();\n                if lp != rp {\n                    return lp.cmp(&rp).reverse();\n                }\n            }\n            // compare deadlines in case of tie.\n            #[cfg(feature = \"scheduler-deadline\")]\n            {\n                let ld = lhs.metadata.deadline();\n                let rd = rhs.metadata.deadline();\n                if ld != rd {\n                    return ld.cmp(&rd);\n                }\n            }\n            core::cmp::Ordering::Equal\n        });\n\n        loop {\n            // For each loop, grab any newly pended items\n            let taken = self.stack.take_all();\n\n            // Sort these into the list - this is potentially expensive! We do an\n            // insertion sort of new items, which iterates the linked list.\n            //\n            // Something on the order of `O(n * m)`, where `n` is the number\n            // of new tasks, and `m` is the number of already pending tasks.\n            sorted.extend(taken);\n\n            // Pop the task with the SOONEST deadline. If there are no tasks\n            // pending, then we are done.\n            let Some(taskref) = sorted.pop_front() else {\n                return;\n            };\n\n            // We got one task, mark it as dequeued, and process the task.\n            run_dequeue(&taskref);\n            on_task(taskref);\n        }\n    }\n}\n\n/// atomic state does not require a cs...\n#[cfg(target_has_atomic = \"ptr\")]\n#[inline(always)]\nfn run_dequeue(taskref: &TaskRef) {\n    taskref.header().state.run_dequeue();\n}\n\n/// ...while non-atomic state does\n#[cfg(not(target_has_atomic = \"ptr\"))]\n#[inline(always)]\nfn run_dequeue(taskref: &TaskRef) {\n    critical_section::with(|cs| {\n        taskref.header().state.run_dequeue(cs);\n    })\n}\n\n/// A wrapper type that acts like TransferStack by wrapping a normal Stack in a CS mutex\n#[cfg(not(target_has_atomic = \"ptr\"))]\nstruct MutexTransferStack<T: Linked<cordyceps::stack::Links<T>>> {\n    inner: critical_section::Mutex<core::cell::UnsafeCell<cordyceps::Stack<T>>>,\n}\n\n#[cfg(not(target_has_atomic = \"ptr\"))]\nimpl<T: Linked<cordyceps::stack::Links<T>>> MutexTransferStack<T> {\n    const fn new() -> Self {\n        Self {\n            inner: critical_section::Mutex::new(core::cell::UnsafeCell::new(cordyceps::Stack::new())),\n        }\n    }\n\n    /// Push an item to the transfer stack, returning whether the stack was previously empty\n    fn push_was_empty(&self, item: T::Handle, token: super::state::Token) -> bool {\n        // SAFETY: The critical-section mutex guarantees that there is no *concurrent* access\n        // for the lifetime of the token, but does NOT protect against re-entrant access.\n        // However, we never *return* the reference, nor do we recurse (or call another method\n        // like `take_all`) that could ever allow for re-entrant aliasing. Therefore, the\n        // presence of the critical section is sufficient to guarantee exclusive access to\n        // the `inner` field for the purposes of this function.\n        let inner = unsafe { &mut *self.inner.borrow(token).get() };\n        let is_empty = inner.is_empty();\n        inner.push(item);\n        is_empty\n    }\n\n    fn take_all(&self) -> cordyceps::Stack<T> {\n        critical_section::with(|cs| {\n            // SAFETY: The critical-section mutex guarantees that there is no *concurrent* access\n            // for the lifetime of the token, but does NOT protect against re-entrant access.\n            // However, we never *return* the reference, nor do we recurse (or call another method\n            // like `push_was_empty`) that could ever allow for re-entrant aliasing. Therefore, the\n            // presence of the critical section is sufficient to guarantee exclusive access to\n            // the `inner` field for the purposes of this function.\n            let inner = unsafe { &mut *self.inner.borrow(cs).get() };\n            inner.take_all()\n        })\n    }\n}\n"
  },
  {
    "path": "embassy-executor/src/raw/state_atomics.rs",
    "content": "// Prefer pointer-width atomic operations, as narrower ones may be slower.\n#[cfg(all(target_pointer_width = \"32\", target_has_atomic = \"32\"))]\ntype AtomicState = core::sync::atomic::AtomicU32;\n#[cfg(not(all(target_pointer_width = \"32\", target_has_atomic = \"32\")))]\ntype AtomicState = core::sync::atomic::AtomicU8;\n\n#[cfg(all(target_pointer_width = \"32\", target_has_atomic = \"32\"))]\ntype StateBits = u32;\n#[cfg(not(all(target_pointer_width = \"32\", target_has_atomic = \"32\")))]\ntype StateBits = u8;\n\nuse core::sync::atomic::Ordering;\n\n#[derive(Clone, Copy)]\npub(crate) struct Token(());\n\n/// Creates a token and passes it to the closure.\n///\n/// This is a no-op replacement for `CriticalSection::with` because we don't need any locking.\npub(crate) fn locked<R>(f: impl FnOnce(Token) -> R) -> R {\n    f(Token(()))\n}\n\n/// Task is spawned (has a future)\npub(crate) const STATE_SPAWNED: StateBits = 1 << 0;\n/// Task is in the executor run queue\npub(crate) const STATE_RUN_QUEUED: StateBits = 1 << 1;\n\npub(crate) struct State {\n    state: AtomicState,\n}\n\nimpl State {\n    pub const fn new() -> State {\n        Self {\n            state: AtomicState::new(0),\n        }\n    }\n\n    /// If task is idle, mark it as spawned + run_queued and return true.\n    #[inline(always)]\n    pub fn spawn(&self) -> bool {\n        self.state\n            .compare_exchange(0, STATE_SPAWNED | STATE_RUN_QUEUED, Ordering::AcqRel, Ordering::Acquire)\n            .is_ok()\n    }\n\n    /// Unmark the task as spawned.\n    #[inline(always)]\n    pub fn despawn(&self) {\n        self.state.fetch_and(!STATE_SPAWNED, Ordering::AcqRel);\n    }\n\n    /// Mark the task as run-queued if it's spawned and isn't already run-queued. Run the given\n    /// function if the task was successfully marked.\n    #[inline(always)]\n    pub fn run_enqueue(&self, f: impl FnOnce(Token)) {\n        let prev = self.state.fetch_or(STATE_RUN_QUEUED, Ordering::AcqRel);\n        if prev & STATE_RUN_QUEUED == 0 {\n            locked(f);\n        }\n    }\n\n    /// Unmark the task as run-queued. Return whether the task is spawned.\n    #[inline(always)]\n    pub fn run_dequeue(&self) {\n        self.state.fetch_and(!STATE_RUN_QUEUED, Ordering::AcqRel);\n    }\n}\n"
  },
  {
    "path": "embassy-executor/src/raw/state_atomics_arm.rs",
    "content": "use core::sync::atomic::{AtomicBool, AtomicU32, Ordering, compiler_fence};\n\n#[derive(Clone, Copy)]\npub(crate) struct Token(());\n\n/// Creates a token and passes it to the closure.\n///\n/// This is a no-op replacement for `CriticalSection::with` because we don't need any locking.\npub(crate) fn locked<R>(f: impl FnOnce(Token) -> R) -> R {\n    f(Token(()))\n}\n\n// Must be kept in sync with the layout of `State`!\npub(crate) const STATE_SPAWNED: u32 = 1 << 0;\npub(crate) const STATE_RUN_QUEUED: u32 = 1 << 8;\n\n#[repr(C, align(4))]\npub(crate) struct State {\n    /// Task is spawned (has a future)\n    spawned: AtomicBool,\n    /// Task is in the executor run queue\n    run_queued: AtomicBool,\n    pad: AtomicBool,\n    pad2: AtomicBool,\n}\n\nimpl State {\n    pub const fn new() -> State {\n        Self {\n            spawned: AtomicBool::new(false),\n            run_queued: AtomicBool::new(false),\n            pad: AtomicBool::new(false),\n            pad2: AtomicBool::new(false),\n        }\n    }\n\n    fn as_u32(&self) -> &AtomicU32 {\n        unsafe { &*(self as *const _ as *const AtomicU32) }\n    }\n\n    /// If task is idle, mark it as spawned + run_queued and return true.\n    #[inline(always)]\n    pub fn spawn(&self) -> bool {\n        compiler_fence(Ordering::Release);\n        let r = self\n            .as_u32()\n            .compare_exchange(\n                0,\n                STATE_SPAWNED | STATE_RUN_QUEUED,\n                Ordering::Relaxed,\n                Ordering::Relaxed,\n            )\n            .is_ok();\n        compiler_fence(Ordering::Acquire);\n        r\n    }\n\n    /// Unmark the task as spawned.\n    #[inline(always)]\n    pub fn despawn(&self) {\n        compiler_fence(Ordering::Release);\n        self.spawned.store(false, Ordering::Relaxed);\n    }\n\n    /// Mark the task as run-queued if it's spawned and isn't already run-queued. Run the given\n    /// function if the task was successfully marked.\n    #[inline(always)]\n    pub fn run_enqueue(&self, f: impl FnOnce(Token)) {\n        let old = self.run_queued.swap(true, Ordering::AcqRel);\n\n        if !old {\n            locked(f);\n        }\n    }\n\n    /// Unmark the task as run-queued. Return whether the task is spawned.\n    #[inline(always)]\n    pub fn run_dequeue(&self) {\n        compiler_fence(Ordering::Release);\n\n        self.run_queued.store(false, Ordering::Relaxed);\n    }\n}\n"
  },
  {
    "path": "embassy-executor/src/raw/state_critical_section.rs",
    "content": "use core::cell::Cell;\n\nuse critical_section::{CriticalSection, Mutex};\npub(crate) use critical_section::{CriticalSection as Token, with as locked};\n\n#[cfg(target_arch = \"avr\")]\ntype StateBits = u8;\n#[cfg(not(target_arch = \"avr\"))]\ntype StateBits = usize;\n\n/// Task is spawned (has a future)\npub(crate) const STATE_SPAWNED: StateBits = 1 << 0;\n/// Task is in the executor run queue\npub(crate) const STATE_RUN_QUEUED: StateBits = 1 << 1;\n\npub(crate) struct State {\n    state: Mutex<Cell<StateBits>>,\n}\n\nimpl State {\n    pub const fn new() -> State {\n        Self {\n            state: Mutex::new(Cell::new(0)),\n        }\n    }\n\n    fn update<R>(&self, f: impl FnOnce(&mut StateBits) -> R) -> R {\n        critical_section::with(|cs| self.update_with_cs(cs, f))\n    }\n\n    fn update_with_cs<R>(&self, cs: CriticalSection<'_>, f: impl FnOnce(&mut StateBits) -> R) -> R {\n        let s = self.state.borrow(cs);\n        let mut val = s.get();\n        let r = f(&mut val);\n        s.set(val);\n        r\n    }\n\n    /// If task is idle, mark it as spawned + run_queued and return true.\n    #[inline(always)]\n    pub fn spawn(&self) -> bool {\n        self.update(|s| {\n            if *s == 0 {\n                *s = STATE_SPAWNED | STATE_RUN_QUEUED;\n                true\n            } else {\n                false\n            }\n        })\n    }\n\n    /// Unmark the task as spawned.\n    #[inline(always)]\n    pub fn despawn(&self) {\n        self.update(|s| *s &= !STATE_SPAWNED);\n    }\n\n    /// Mark the task as run-queued if it's spawned and isn't already run-queued. Run the given\n    /// function if the task was successfully marked.\n    #[inline(always)]\n    pub fn run_enqueue(&self, f: impl FnOnce(Token)) {\n        critical_section::with(|cs| {\n            if self.update_with_cs(cs, |s| {\n                let ok = *s & STATE_RUN_QUEUED == 0;\n                *s |= STATE_RUN_QUEUED;\n                ok\n            }) {\n                f(cs);\n            }\n        });\n    }\n\n    /// Unmark the task as run-queued. Return whether the task is spawned.\n    #[inline(always)]\n    pub fn run_dequeue(&self, cs: CriticalSection<'_>) {\n        self.update_with_cs(cs, |s| *s &= !STATE_RUN_QUEUED)\n    }\n}\n"
  },
  {
    "path": "embassy-executor/src/raw/trace.rs",
    "content": "//! # Tracing\n//!\n//! The `trace` feature enables a number of callbacks that can be used to track the\n//! lifecycle of tasks and/or executors.\n//!\n//! Callbacks will have one or both of the following IDs passed to them:\n//!\n//! 1. A `task_id`, a `u32` value unique to a task for the duration of the time it is valid\n//! 2. An `executor_id`, a `u32` value unique to an executor for the duration of the time it is\n//!    valid\n//!\n//! Today, both `task_id` and `executor_id` are u32s containing the least significant 32 bits of\n//! the address of the task or executor, however this is NOT a stable guarantee, and MAY change\n//! at any time.\n//!\n//! IDs are only guaranteed to be unique for the duration of time the item is valid. If a task\n//! ends, and is re-spawned, it MAY or MAY NOT have the same ID. For tasks, this valid time is defined\n//! as the time between `_embassy_trace_task_new` and `_embassy_trace_task_end` for a given task.\n//! For executors, this time is not defined, but is often \"forever\" for practical embedded\n//! programs.\n//!\n//! Callbacks can be used by enabling the `trace` feature, and providing implementations of the\n//! `extern \"Rust\"` functions below. All callbacks must be implemented.\n//!\n//! ## Task Tracing lifecycle\n//!\n//! ```text\n//! ┌ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─\n//!        │(1)                                            │\n//! │      │\n//!   ╔════▼════╗ (2) ┌─────────┐ (3) ┌─────────┐          │\n//! │ ║ SPAWNED ║────▶│ WAITING │────▶│ RUNNING │\n//!   ╚═════════╝     └─────────┘     └─────────┘          │\n//! │                 ▲         ▲     │    │    │\n//!                   │           (4)      │    │(6)       │\n//! │                 │(7)      └ ─ ─ ┘    │    │\n//!                   │                    │    │          │\n//! │             ┌──────┐             (5) │    │  ┌─────┐\n//!               │ IDLE │◀────────────────┘    └─▶│ END │ │\n//! │             └──────┘                         └─────┘\n//!   ┌──────────────────────┐                             │\n//! └ ┤ Task Trace Lifecycle │─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─\n//!   └──────────────────────┘\n//! ```\n//!\n//! 1. A task is spawned, `_embassy_trace_task_new` is called\n//! 2. A task is enqueued for the first time, `_embassy_trace_task_ready_begin` is called\n//! 3. A task is polled, `_embassy_trace_task_exec_begin` is called\n//! 4. WHILE a task is polled, the task is re-awoken, and `_embassy_trace_task_ready_begin` is\n//!      called. The task does not IMMEDIATELY move state, until polling is complete and the\n//!      RUNNING state is existed. `_embassy_trace_task_exec_end` is called when polling is\n//!      complete, marking the transition to WAITING\n//! 5. Polling is complete, `_embassy_trace_task_exec_end` is called\n//! 6. The task has completed, and `_embassy_trace_task_end` is called\n//! 7. A task is awoken, `_embassy_trace_task_ready_begin` is called\n//!\n//! ## Executor Tracing lifecycle\n//!\n//! ```text\n//! ┌ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─\n//!       │(1)                                             │\n//! │     │\n//!   ╔═══▼══╗   (2)     ┌────────────┐  (3)  ┌─────────┐  │\n//! │ ║ IDLE ║──────────▶│ SCHEDULING │──────▶│ POLLING │\n//!   ╚══════╝           └────────────┘       └─────────┘  │\n//! │     ▲              │            ▲            │\n//!       │      (5)     │            │  (4)       │       │\n//! │     └──────────────┘            └────────────┘\n//!   ┌──────────────────────────┐                         │\n//! └ ┤ Executor Trace Lifecycle │─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─\n//!   └──────────────────────────┘\n//! ```\n//!\n//! 1. The executor is started (no associated trace)\n//! 2. A task on this executor is awoken. `_embassy_trace_task_ready_begin` is called\n//!      when this occurs, and `_embassy_trace_poll_start` is called when the executor\n//!      actually begins running\n//! 3. The executor has decided a task to poll. `_embassy_trace_task_exec_begin` is called\n//! 4. The executor finishes polling the task. `_embassy_trace_task_exec_end` is called\n//! 5. The executor has finished polling tasks. `_embassy_trace_executor_idle` is called\n\n#![allow(unused)]\n\nuse core::cell::UnsafeCell;\nuse core::sync::atomic::{AtomicPtr, AtomicUsize, Ordering};\n\n#[cfg(feature = \"rtos-trace\")]\nuse rtos_trace::TaskInfo;\n\nuse crate::raw::{SyncExecutor, TaskHeader, TaskRef};\nuse crate::spawner::{SpawnError, SpawnToken, Spawner};\n\n/// Global task tracker instance\n///\n/// This static provides access to the global task tracker which maintains\n/// a list of all tasks in the system. It's automatically updated by the\n/// task lifecycle hooks in the trace module.\n#[cfg(feature = \"rtos-trace\")]\npub(crate) static TASK_TRACKER: TaskTracker = TaskTracker::new();\n\n/// A thread-safe tracker for all tasks in the system\n///\n/// This struct uses an intrusive linked list approach to track all tasks\n/// without additional memory allocations. It maintains a global list of\n/// tasks that can be traversed to find all currently existing tasks.\n#[cfg(feature = \"rtos-trace\")]\npub(crate) struct TaskTracker {\n    head: AtomicPtr<TaskHeader>,\n}\n\n#[cfg(feature = \"rtos-trace\")]\nimpl TaskTracker {\n    /// Creates a new empty task tracker\n    ///\n    /// Initializes a tracker with no tasks in its list.\n    pub const fn new() -> Self {\n        Self {\n            head: AtomicPtr::new(core::ptr::null_mut()),\n        }\n    }\n\n    /// Adds a task to the tracker\n    ///\n    /// This method inserts a task at the head of the intrusive linked list.\n    /// The operation is thread-safe and lock-free, using atomic operations\n    /// to ensure consistency even when called from different contexts.\n    ///\n    /// # Arguments\n    /// * `task` - The task reference to add to the tracker\n    pub fn add(&self, task: TaskRef) {\n        let task_ptr = task.as_ptr();\n\n        loop {\n            let current_head = self.head.load(Ordering::Acquire);\n            unsafe {\n                (*task_ptr).all_tasks_next.store(current_head, Ordering::Relaxed);\n            }\n\n            if self\n                .head\n                .compare_exchange(current_head, task_ptr.cast_mut(), Ordering::Release, Ordering::Relaxed)\n                .is_ok()\n            {\n                break;\n            }\n        }\n    }\n\n    /// Performs an operation on each task in the tracker\n    ///\n    /// This method traverses the entire list of tasks and calls the provided\n    /// function for each task. This allows inspecting or processing all tasks\n    /// in the system without modifying the tracker's structure.\n    ///\n    /// # Arguments\n    /// * `f` - A function to call for each task in the tracker\n    pub fn for_each<F>(&self, mut f: F)\n    where\n        F: FnMut(TaskRef),\n    {\n        let mut current = self.head.load(Ordering::Acquire);\n        while !current.is_null() {\n            let task = unsafe { TaskRef::from_ptr(current) };\n            f(task);\n\n            current = unsafe { (*current).all_tasks_next.load(Ordering::Acquire) };\n        }\n    }\n}\n\n#[cfg(feature = \"trace\")]\nunsafe extern \"Rust\" {\n    /// This callback is called when the executor begins polling. This will always\n    /// be paired with a later call to `_embassy_trace_executor_idle`.\n    ///\n    /// This marks the EXECUTOR state transition from IDLE -> SCHEDULING.\n    fn _embassy_trace_poll_start(executor_id: u32);\n\n    /// This callback is called AFTER a task is initialized/allocated, and BEFORE\n    /// it is enqueued to run for the first time. If the task ends (and does not\n    /// loop \"forever\"), there will be a matching call to `_embassy_trace_task_end`.\n    ///\n    /// Tasks start life in the SPAWNED state.\n    fn _embassy_trace_task_new(executor_id: u32, task_id: u32);\n\n    /// This callback is called AFTER a task is destructed/freed. This will always\n    /// have a prior matching call to `_embassy_trace_task_new`.\n    fn _embassy_trace_task_end(executor_id: u32, task_id: u32);\n\n    /// This callback is called AFTER a task has been dequeued from the runqueue,\n    /// and BEFORE the task is polled. There will always be a matching call to\n    /// `_embassy_trace_task_exec_end`.\n    ///\n    /// This marks the TASK state transition from WAITING -> RUNNING\n    /// This marks the EXECUTOR state transition from SCHEDULING -> POLLING\n    fn _embassy_trace_task_exec_begin(executor_id: u32, task_id: u32);\n\n    /// This callback is called AFTER a task has completed polling. There will\n    /// always be a matching call to `_embassy_trace_task_exec_begin`.\n    ///\n    /// This marks the TASK state transition from either:\n    /// * RUNNING -> IDLE - if there were no `_embassy_trace_task_ready_begin` events\n    ///     for this task since the last `_embassy_trace_task_exec_begin` for THIS task\n    /// * RUNNING -> WAITING - if there WAS a `_embassy_trace_task_ready_begin` event\n    ///     for this task since the last `_embassy_trace_task_exec_begin` for THIS task\n    ///\n    /// This marks the EXECUTOR state transition from POLLING -> SCHEDULING\n    fn _embassy_trace_task_exec_end(excutor_id: u32, task_id: u32);\n\n    /// This callback is called AFTER the waker for a task is awoken, and BEFORE it\n    /// is added to the run queue.\n    ///\n    /// If the given task is currently RUNNING, this marks no state change, BUT the\n    /// RUNNING task will then move to the WAITING stage when polling is complete.\n    ///\n    /// If the given task is currently IDLE, this marks the TASK state transition\n    /// from IDLE -> WAITING.\n    ///\n    /// NOTE: This may be called from an interrupt, outside the context of the current\n    /// task or executor.\n    fn _embassy_trace_task_ready_begin(executor_id: u32, task_id: u32);\n\n    /// This callback is called AFTER all dequeued tasks in a single call to poll\n    /// have been processed. This will always be paired with a call to\n    /// `_embassy_trace_executor_idle`.\n    ///\n    /// This marks the EXECUTOR state transition from SCHEDULING -> IDLE\n    fn _embassy_trace_executor_idle(executor_id: u32);\n}\n\n#[inline]\npub(crate) fn poll_start(executor: &SyncExecutor) {\n    #[cfg(feature = \"trace\")]\n    unsafe {\n        _embassy_trace_poll_start(executor as *const _ as u32)\n    }\n}\n\n#[inline]\npub(crate) fn task_new(executor: &SyncExecutor, task: &TaskRef) {\n    #[cfg(feature = \"trace\")]\n    unsafe {\n        _embassy_trace_task_new(executor as *const _ as u32, task.as_ptr() as u32)\n    }\n\n    #[cfg(feature = \"rtos-trace\")]\n    {\n        rtos_trace::trace::task_new(task.as_ptr() as u32);\n        let name = task.metadata().name().unwrap_or(\"unnamed task\\0\");\n        let info = rtos_trace::TaskInfo {\n            name,\n            priority: 0,\n            stack_base: 0,\n            stack_size: 0,\n        };\n        rtos_trace::trace::task_send_info(task.id(), info);\n    }\n\n    #[cfg(feature = \"rtos-trace\")]\n    TASK_TRACKER.add(*task);\n}\n\n#[inline]\npub(crate) fn task_end(executor: *const SyncExecutor, task: &TaskRef) {\n    #[cfg(feature = \"trace\")]\n    unsafe {\n        _embassy_trace_task_end(executor as u32, task.as_ptr() as u32)\n    }\n}\n\n#[inline]\npub(crate) fn task_ready_begin(executor: &SyncExecutor, task: &TaskRef) {\n    #[cfg(feature = \"trace\")]\n    unsafe {\n        _embassy_trace_task_ready_begin(executor as *const _ as u32, task.as_ptr() as u32)\n    }\n    #[cfg(feature = \"rtos-trace\")]\n    rtos_trace::trace::task_ready_begin(task.as_ptr() as u32);\n}\n\n#[inline]\npub(crate) fn task_exec_begin(executor: &SyncExecutor, task: &TaskRef) {\n    #[cfg(feature = \"trace\")]\n    unsafe {\n        _embassy_trace_task_exec_begin(executor as *const _ as u32, task.as_ptr() as u32)\n    }\n    #[cfg(feature = \"rtos-trace\")]\n    rtos_trace::trace::task_exec_begin(task.as_ptr() as u32);\n}\n\n#[inline]\npub(crate) fn task_exec_end(executor: &SyncExecutor, task: &TaskRef) {\n    #[cfg(feature = \"trace\")]\n    unsafe {\n        _embassy_trace_task_exec_end(executor as *const _ as u32, task.as_ptr() as u32)\n    }\n    #[cfg(feature = \"rtos-trace\")]\n    rtos_trace::trace::task_exec_end();\n}\n\n#[inline]\npub(crate) fn executor_idle(executor: &SyncExecutor) {\n    #[cfg(feature = \"trace\")]\n    unsafe {\n        _embassy_trace_executor_idle(executor as *const _ as u32)\n    }\n    #[cfg(feature = \"rtos-trace\")]\n    rtos_trace::trace::system_idle();\n}\n\n/// Returns an iterator over all active tasks in the system\n///\n/// This function provides a convenient way to iterate over all tasks\n/// that are currently tracked in the system. The returned iterator\n/// yields each task in the global task tracker.\n///\n/// # Returns\n/// An iterator that yields `TaskRef` items for each task\n#[cfg(feature = \"rtos-trace\")]\nfn get_all_active_tasks() -> impl Iterator<Item = TaskRef> + 'static {\n    struct TaskIterator<'a> {\n        tracker: &'a TaskTracker,\n        current: *mut TaskHeader,\n    }\n\n    impl<'a> Iterator for TaskIterator<'a> {\n        type Item = TaskRef;\n\n        fn next(&mut self) -> Option<Self::Item> {\n            if self.current.is_null() {\n                return None;\n            }\n\n            let task = unsafe { TaskRef::from_ptr(self.current) };\n            self.current = unsafe { (*self.current).all_tasks_next.load(Ordering::Acquire) };\n\n            Some(task)\n        }\n    }\n\n    TaskIterator {\n        tracker: &TASK_TRACKER,\n        current: TASK_TRACKER.head.load(Ordering::Acquire),\n    }\n}\n\n/// Perform an action on each active task\n#[cfg(feature = \"rtos-trace\")]\nfn with_all_active_tasks<F>(f: F)\nwhere\n    F: FnMut(TaskRef),\n{\n    TASK_TRACKER.for_each(f);\n}\n\n#[cfg(feature = \"rtos-trace\")]\nimpl rtos_trace::RtosTraceOSCallbacks for crate::raw::SyncExecutor {\n    fn task_list() {\n        with_all_active_tasks(|task| {\n            let info = rtos_trace::TaskInfo {\n                name: task.metadata().name().unwrap_or(\"unnamed task\\0\"),\n                priority: 0,\n                stack_base: 0,\n                stack_size: 0,\n            };\n            rtos_trace::trace::task_send_info(task.id(), info);\n        });\n    }\n    fn time() -> u64 {\n        const fn gcd(a: u64, b: u64) -> u64 {\n            if b == 0 { a } else { gcd(b, a % b) }\n        }\n\n        const GCD_1M: u64 = gcd(embassy_time_driver::TICK_HZ, 1_000_000);\n        embassy_time_driver::now() * (1_000_000 / GCD_1M) / (embassy_time_driver::TICK_HZ / GCD_1M)\n    }\n}\n\n#[cfg(feature = \"rtos-trace\")]\nrtos_trace::global_os_callbacks! {SyncExecutor}\n"
  },
  {
    "path": "embassy-executor/src/raw/util.rs",
    "content": "use core::cell::UnsafeCell;\nuse core::mem::MaybeUninit;\nuse core::ptr;\n\npub(crate) struct UninitCell<T>(MaybeUninit<UnsafeCell<T>>);\nimpl<T> UninitCell<T> {\n    pub const fn uninit() -> Self {\n        Self(MaybeUninit::uninit())\n    }\n\n    pub unsafe fn as_mut_ptr(&self) -> *mut T {\n        (*self.0.as_ptr()).get()\n    }\n\n    #[allow(clippy::mut_from_ref)]\n    pub unsafe fn as_mut(&self) -> &mut T {\n        &mut *self.as_mut_ptr()\n    }\n\n    #[inline(never)]\n    pub unsafe fn write_in_place(&self, func: impl FnOnce() -> T) {\n        ptr::write(self.as_mut_ptr(), func())\n    }\n\n    pub unsafe fn drop_in_place(&self) {\n        ptr::drop_in_place(self.as_mut_ptr())\n    }\n}\n\nunsafe impl<T> Sync for UninitCell<T> {}\n\n#[repr(transparent)]\npub struct SyncUnsafeCell<T> {\n    value: UnsafeCell<T>,\n}\n\nunsafe impl<T: Sync> Sync for SyncUnsafeCell<T> {}\n\nimpl<T> SyncUnsafeCell<T> {\n    #[inline]\n    pub const fn new(value: T) -> Self {\n        Self {\n            value: UnsafeCell::new(value),\n        }\n    }\n\n    pub unsafe fn set(&self, value: T) {\n        *self.value.get() = value;\n    }\n\n    pub unsafe fn get(&self) -> T\n    where\n        T: Copy,\n    {\n        *self.value.get()\n    }\n}\n"
  },
  {
    "path": "embassy-executor/src/raw/waker.rs",
    "content": "use core::task::{RawWaker, RawWakerVTable, Waker};\n\nuse super::{TaskHeader, TaskRef, wake_task};\n\nstatic VTABLE: RawWakerVTable = RawWakerVTable::new(clone, wake, wake, drop);\n\nunsafe fn clone(p: *const ()) -> RawWaker {\n    RawWaker::new(p, &VTABLE)\n}\n\nunsafe fn wake(p: *const ()) {\n    wake_task(TaskRef::from_ptr(p as *const TaskHeader))\n}\n\nunsafe fn drop(_: *const ()) {\n    // nop\n}\n\npub(crate) unsafe fn from_task(p: TaskRef) -> Waker {\n    Waker::from_raw(RawWaker::new(p.as_ptr() as _, &VTABLE))\n}\n\n/// Get a task pointer from a waker.\n///\n/// This can be used as an optimization in wait queues to store task pointers\n/// (1 word) instead of full Wakers (2 words). This saves a bit of RAM and helps\n/// avoid dynamic dispatch.\n///\n/// You can use the returned task pointer to wake the task with [`wake_task`].\n///\n/// # Panics\n///\n/// Panics if the waker is not created by the Embassy executor.\npub fn task_from_waker(waker: &Waker) -> TaskRef {\n    unwrap!(\n        try_task_from_waker(waker),\n        \"Found waker not created by the Embassy executor. Unless the generic timer queue is enabled, `embassy_time::Timer` only works with the Embassy executor.\"\n    )\n}\n\npub(crate) fn try_task_from_waker(waker: &Waker) -> Option<TaskRef> {\n    // make sure to compare vtable addresses. Doing `==` on the references\n    // will compare the contents, which is slower.\n    if waker.vtable() as *const _ != &VTABLE as *const _ {\n        return None;\n    }\n    // safety: our wakers are always created with `TaskRef::as_ptr`\n    Some(unsafe { TaskRef::from_ptr(waker.data() as *const TaskHeader) })\n}\n"
  },
  {
    "path": "embassy-executor/src/raw/waker_turbo.rs",
    "content": "use core::ptr::NonNull;\nuse core::task::Waker;\n\nuse super::{TaskHeader, TaskRef, wake_task};\n\npub(crate) unsafe fn from_task(p: TaskRef) -> Waker {\n    Waker::from_turbo_ptr(NonNull::new_unchecked(p.as_ptr() as _))\n}\n\n/// Get a task pointer from a waker.\n///\n/// This can be used as an optimization in wait queues to store task pointers\n/// (1 word) instead of full Wakers (2 words). This saves a bit of RAM and helps\n/// avoid dynamic dispatch.\n///\n/// You can use the returned task pointer to wake the task with [`wake_task`](super::wake_task).\n///\n/// # Panics\n///\n/// Panics if the waker is not created by the Embassy executor.\npub fn task_from_waker(waker: &Waker) -> TaskRef {\n    let ptr = waker.as_turbo_ptr().as_ptr();\n\n    // safety: our wakers are always created with `TaskRef::as_ptr`\n    unsafe { TaskRef::from_ptr(ptr as *const TaskHeader) }\n}\n\npub(crate) fn try_task_from_waker(waker: &Waker) -> Option<TaskRef> {\n    Some(task_from_waker(waker))\n}\n\n#[inline(never)]\n#[unsafe(no_mangle)]\nfn _turbo_wake(ptr: NonNull<()>) {\n    // safety: our wakers are always created with `TaskRef::as_ptr`\n    let task = unsafe { TaskRef::from_ptr(ptr.as_ptr() as *const TaskHeader) };\n    wake_task(task)\n}\n"
  },
  {
    "path": "embassy-executor/src/spawner.rs",
    "content": "use core::future::{Future, poll_fn};\nuse core::marker::PhantomData;\nuse core::mem;\nuse core::sync::atomic::Ordering;\nuse core::task::Poll;\n\nuse super::raw;\nuse crate::Metadata;\n\n/// Token to spawn a newly-created task in an executor.\n///\n/// When calling a task function (like `#[embassy_executor::task] async fn my_task() { ... }`), the returned\n/// value is a `SpawnToken` that represents an instance of the task, ready to spawn. You must\n/// then spawn it into an executor, typically with [`Spawner::spawn()`].\n///\n/// The generic parameter `S` determines whether the task can be spawned in executors\n/// in other threads or not. If `S: Send`, it can, which allows spawning it into a [`SendSpawner`].\n/// If not, it can't, so it can only be spawned into the current thread's executor, with [`Spawner`].\n///\n/// # Panics\n///\n/// Dropping a SpawnToken instance panics. You may not \"abort\" spawning a task in this way.\n/// Once you've invoked a task function and obtained a SpawnToken, you *must* spawn it.\n#[must_use = \"Calling a task function does nothing on its own. You must spawn the returned SpawnToken, typically with Spawner::spawn()\"]\npub struct SpawnToken<S> {\n    pub(crate) raw_task: raw::TaskRef,\n    phantom: PhantomData<*mut S>,\n}\n\nimpl<S> SpawnToken<S> {\n    pub(crate) unsafe fn new(raw_task: raw::TaskRef) -> Self {\n        Self {\n            raw_task,\n            phantom: PhantomData,\n        }\n    }\n\n    /// Returns the task ID.\n    /// This can be used in combination with rtos-trace to match task names with IDs\n    pub fn id(&self) -> u32 {\n        self.raw_task.id()\n    }\n\n    /// Get the metadata for this task. You can use this to set metadata fields\n    /// prior to spawning it.\n    pub fn metadata(&self) -> &Metadata {\n        self.raw_task.metadata()\n    }\n}\n\nimpl<S> Drop for SpawnToken<S> {\n    fn drop(&mut self) {\n        // TODO deallocate the task instead.\n        panic!(\"SpawnToken instances may not be dropped. You must pass them to Spawner::spawn()\")\n    }\n}\n\n/// Error returned when spawning a task.\n#[derive(Copy, Clone)]\npub enum SpawnError {\n    /// Too many instances of this task are already running.\n    ///\n    /// By default, a task marked with `#[embassy_executor::task]` can only have one instance\n    /// running at a time. You may allow multiple instances to run in parallel with\n    /// `#[embassy_executor::task(pool_size = 4)]`, at the cost of higher RAM usage.\n    Busy,\n}\n\nimpl core::fmt::Debug for SpawnError {\n    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {\n        core::fmt::Display::fmt(self, f)\n    }\n}\n\nimpl core::fmt::Display for SpawnError {\n    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {\n        match self {\n            SpawnError::Busy => write!(\n                f,\n                \"Busy - Too many instances of this task are already running. Check the `pool_size` attribute of the task.\"\n            ),\n        }\n    }\n}\n\n#[cfg(feature = \"defmt\")]\nimpl defmt::Format for SpawnError {\n    fn format(&self, f: defmt::Formatter) {\n        match self {\n            SpawnError::Busy => defmt::write!(\n                f,\n                \"Busy - Too many instances of this task are already running. Check the `pool_size` attribute of the task.\"\n            ),\n        }\n    }\n}\n\nimpl core::error::Error for SpawnError {}\n\n/// Handle to spawn tasks into an executor.\n///\n/// This Spawner can spawn any task (Send and non-Send ones), but it can\n/// only be used in the executor thread (it is not Send itself).\n///\n/// If you want to spawn tasks from another thread, use [SendSpawner].\n#[derive(Copy, Clone)]\npub struct Spawner {\n    pub(crate) executor: &'static raw::Executor,\n    not_send: PhantomData<*mut ()>,\n}\n\nimpl Spawner {\n    pub(crate) fn new(executor: &'static raw::Executor) -> Self {\n        Self {\n            executor,\n            not_send: PhantomData,\n        }\n    }\n\n    /// Get a Spawner for the current executor.\n    ///\n    /// This function is `async` just to get access to the current async\n    /// context. It returns instantly, it does not block/yield.\n    ///\n    /// Using this method is discouraged due to it being unsafe. Consider the following\n    /// alternatives instead:\n    ///\n    /// - Pass the initial `Spawner` as an argument to tasks. Note that it's `Copy`, so you can\n    ///   make as many copies of it as you want.\n    /// - Use `SendSpawner::for_current_executor()` instead, which is safe but can only be used\n    ///   if task arguments are `Send`.\n    ///\n    /// The only case where using this method is absolutely required is obtaining the `Spawner`\n    /// for an `InterruptExecutor`.\n    ///\n    /// # Safety\n    ///\n    /// You must only execute this with an async `Context` created by the Embassy executor.\n    /// You must not execute it with manually-created `Context`s.\n    ///\n    /// # Panics\n    ///\n    /// Panics if the current executor is not an Embassy executor.\n    pub unsafe fn for_current_executor() -> impl Future<Output = Self> {\n        poll_fn(|cx| {\n            let task = raw::task_from_waker(cx.waker());\n            let executor = unsafe {\n                task.header()\n                    .executor\n                    .load(Ordering::Relaxed)\n                    .as_ref()\n                    .unwrap_unchecked()\n            };\n            let executor = unsafe { raw::Executor::wrap(executor) };\n            Poll::Ready(Self::new(executor))\n        })\n    }\n\n    /// Spawn a task into an executor.\n    ///\n    /// You obtain the `token` by calling a task function (i.e. one marked with `#[embassy_executor::task]`).\n    pub fn spawn<S>(&self, token: SpawnToken<S>) {\n        let task = token.raw_task;\n        mem::forget(token);\n        unsafe { self.executor.spawn(task) }\n    }\n\n    /// Convert this Spawner to a SendSpawner. This allows you to send the\n    /// spawner to other threads, but the spawner loses the ability to spawn\n    /// non-Send tasks.\n    pub fn make_send(&self) -> SendSpawner {\n        SendSpawner::new(&self.executor.inner)\n    }\n\n    /// Return the unique ID of this Spawner's Executor.\n    pub fn executor_id(&self) -> usize {\n        self.executor.id()\n    }\n}\n\n/// Handle to spawn tasks into an executor from any thread.\n///\n/// This Spawner can be used from any thread (it is Send), but it can\n/// only spawn Send tasks. The reason for this is spawning is effectively\n/// \"sending\" the tasks to the executor thread.\n///\n/// If you want to spawn non-Send tasks, use [Spawner].\n#[derive(Copy, Clone)]\npub struct SendSpawner {\n    executor: &'static raw::SyncExecutor,\n}\n\nimpl SendSpawner {\n    pub(crate) fn new(executor: &'static raw::SyncExecutor) -> Self {\n        Self { executor }\n    }\n\n    /// Get a Spawner for the current executor.\n    ///\n    /// This function is `async` just to get access to the current async\n    /// context. It returns instantly, it does not block/yield.\n    ///\n    /// # Panics\n    ///\n    /// Panics if the current executor is not an Embassy executor.\n    pub fn for_current_executor() -> impl Future<Output = Self> {\n        poll_fn(|cx| {\n            let task = raw::task_from_waker(cx.waker());\n            let executor = unsafe {\n                task.header()\n                    .executor\n                    .load(Ordering::Relaxed)\n                    .as_ref()\n                    .unwrap_unchecked()\n            };\n            Poll::Ready(Self::new(executor))\n        })\n    }\n\n    /// Spawn a task into an executor.\n    ///\n    /// You obtain the `token` by calling a task function (i.e. one marked with `#[embassy_executor::task]`).\n    pub fn spawn<S: Send>(&self, token: SpawnToken<S>) {\n        let header = token.raw_task;\n        mem::forget(token);\n        unsafe { self.executor.spawn(header) }\n    }\n}\n"
  },
  {
    "path": "embassy-executor/tests/test.rs",
    "content": "#![cfg_attr(feature = \"nightly\", feature(impl_trait_in_assoc_type))]\n#![cfg_attr(feature = \"nightly\", feature(never_type))]\n\nuse std::boxed::Box;\nuse std::future::{Future, poll_fn};\nuse std::sync::{Arc, Mutex};\nuse std::task::Poll;\n\nuse embassy_executor::raw::Executor;\nuse embassy_executor::{Spawner, task};\n\n#[unsafe(export_name = \"__pender\")]\nfn __pender(context: *mut ()) {\n    unsafe {\n        let trace = &*(context as *const Trace);\n        trace.push(\"pend\");\n    }\n}\n\n#[derive(Clone)]\nstruct Trace {\n    trace: Arc<Mutex<Vec<&'static str>>>,\n}\n\nimpl Trace {\n    fn new() -> Self {\n        Self {\n            trace: Arc::new(Mutex::new(Vec::new())),\n        }\n    }\n    fn push(&self, value: &'static str) {\n        self.trace.lock().unwrap().push(value)\n    }\n\n    fn get(&self) -> Vec<&'static str> {\n        self.trace.lock().unwrap().clone()\n    }\n}\n\nfn setup() -> (&'static Executor, Trace) {\n    let trace = Trace::new();\n    let context = Box::leak(Box::new(trace.clone())) as *mut _ as *mut ();\n    let executor = &*Box::leak(Box::new(Executor::new(context)));\n\n    (executor, trace)\n}\n\n#[test]\nfn executor_noop() {\n    let (executor, trace) = setup();\n    unsafe { executor.poll() };\n    assert!(trace.get().is_empty())\n}\n\n#[test]\nfn executor_task() {\n    #[task]\n    async fn task1(trace: Trace) {\n        trace.push(\"poll task1\")\n    }\n\n    #[task]\n    async fn task2() -> ! {\n        panic!()\n    }\n\n    let (executor, trace) = setup();\n    executor.spawner().spawn(task1(trace.clone()).unwrap());\n\n    unsafe { executor.poll() };\n    unsafe { executor.poll() };\n\n    assert_eq!(\n        trace.get(),\n        &[\n            \"pend\",       // spawning a task pends the executor\n            \"poll task1\", // poll only once.\n        ]\n    )\n}\n\n#[test]\nfn executor_task_rpit() {\n    #[task]\n    fn task1(trace: Trace) -> impl Future<Output = ()> {\n        async move { trace.push(\"poll task1\") }\n    }\n\n    #[cfg(feature = \"nightly\")]\n    #[task]\n    fn task2() -> impl Future<Output = !> {\n        async { panic!() }\n    }\n\n    let (executor, trace) = setup();\n    executor.spawner().spawn(task1(trace.clone()).unwrap());\n\n    unsafe { executor.poll() };\n    unsafe { executor.poll() };\n\n    assert_eq!(\n        trace.get(),\n        &[\n            \"pend\",       // spawning a task pends the executor\n            \"poll task1\", // poll only once.\n        ]\n    )\n}\n\n#[test]\nfn executor_task_self_wake() {\n    #[task]\n    async fn task1(trace: Trace) {\n        poll_fn(|cx| {\n            trace.push(\"poll task1\");\n            cx.waker().wake_by_ref();\n            Poll::Pending\n        })\n        .await\n    }\n\n    let (executor, trace) = setup();\n    executor.spawner().spawn(task1(trace.clone()).unwrap());\n\n    unsafe { executor.poll() };\n    unsafe { executor.poll() };\n\n    assert_eq!(\n        trace.get(),\n        &[\n            \"pend\",       // spawning a task pends the executor\n            \"poll task1\", //\n            \"pend\",       // task self-wakes\n            \"poll task1\", //\n            \"pend\",       // task self-wakes\n        ]\n    )\n}\n\n#[test]\nfn executor_task_self_wake_twice() {\n    #[task]\n    async fn task1(trace: Trace) {\n        poll_fn(|cx| {\n            trace.push(\"poll task1\");\n            cx.waker().wake_by_ref();\n            trace.push(\"poll task1 wake 2\");\n            cx.waker().wake_by_ref();\n            Poll::Pending\n        })\n        .await\n    }\n\n    let (executor, trace) = setup();\n    executor.spawner().spawn(task1(trace.clone()).unwrap());\n\n    unsafe { executor.poll() };\n    unsafe { executor.poll() };\n\n    assert_eq!(\n        trace.get(),\n        &[\n            \"pend\",              // spawning a task pends the executor\n            \"poll task1\",        //\n            \"pend\",              // task self-wakes\n            \"poll task1 wake 2\", // task self-wakes again, shouldn't pend\n            \"poll task1\",        //\n            \"pend\",              // task self-wakes\n            \"poll task1 wake 2\", // task self-wakes again, shouldn't pend\n        ]\n    )\n}\n\n#[test]\nfn waking_after_completion_does_not_poll() {\n    use embassy_sync::waitqueue::AtomicWaker;\n\n    #[task]\n    async fn task1(trace: Trace, waker: &'static AtomicWaker) {\n        poll_fn(|cx| {\n            trace.push(\"poll task1\");\n            waker.register(cx.waker());\n            Poll::Ready(())\n        })\n        .await\n    }\n\n    let waker = Box::leak(Box::new(AtomicWaker::new()));\n\n    let (executor, trace) = setup();\n    executor.spawner().spawn(task1(trace.clone(), waker).unwrap());\n\n    unsafe { executor.poll() };\n    waker.wake();\n    unsafe { executor.poll() };\n\n    // Exited task may be waken but is not polled\n    waker.wake();\n    waker.wake();\n    unsafe { executor.poll() }; // Clears running status\n\n    // Can respawn waken-but-dead task\n    executor.spawner().spawn(task1(trace.clone(), waker).unwrap());\n\n    unsafe { executor.poll() };\n\n    assert_eq!(\n        trace.get(),\n        &[\n            \"pend\",       // spawning a task pends the executor\n            \"poll task1\", //\n            \"pend\",       // manual wake, gets cleared by poll\n            \"pend\",       // manual wake, single pend for two wakes\n            \"pend\",       // respawning a task pends the executor\n            \"poll task1\", //\n        ]\n    )\n}\n\n#[test]\nfn waking_with_old_waker_after_respawn() {\n    use embassy_sync::waitqueue::AtomicWaker;\n\n    async fn yield_now(trace: Trace) {\n        let mut yielded = false;\n        poll_fn(|cx| {\n            if yielded {\n                Poll::Ready(())\n            } else {\n                trace.push(\"yield_now\");\n                yielded = true;\n                cx.waker().wake_by_ref();\n                Poll::Pending\n            }\n        })\n        .await\n    }\n\n    #[task]\n    async fn task1(trace: Trace, waker: &'static AtomicWaker) {\n        yield_now(trace.clone()).await;\n        poll_fn(|cx| {\n            trace.push(\"poll task1\");\n            waker.register(cx.waker());\n            Poll::Ready(())\n        })\n        .await;\n    }\n\n    let waker = Box::leak(Box::new(AtomicWaker::new()));\n\n    let (executor, trace) = setup();\n    executor.spawner().spawn(task1(trace.clone(), waker).unwrap());\n\n    unsafe { executor.poll() };\n    unsafe { executor.poll() }; // progress to registering the waker\n    waker.wake();\n    unsafe { executor.poll() };\n    // Task has exited\n\n    assert_eq!(\n        trace.get(),\n        &[\n            \"pend\",       // spawning a task pends the executor\n            \"yield_now\",  //\n            \"pend\",       // yield_now wakes the task\n            \"poll task1\", //\n            \"pend\",       // task self-wakes\n        ]\n    );\n\n    // Can respawn task on another executor\n    let (other_executor, other_trace) = setup();\n    other_executor\n        .spawner()\n        .spawn(task1(other_trace.clone(), waker).unwrap());\n\n    unsafe { other_executor.poll() }; // just run to the yield_now\n    waker.wake(); // trigger old waker registration\n    unsafe { executor.poll() };\n    unsafe { other_executor.poll() };\n\n    // First executor's trace has not changed\n    assert_eq!(\n        trace.get(),\n        &[\n            \"pend\",       // spawning a task pends the executor\n            \"yield_now\",  //\n            \"pend\",       // yield_now wakes the task\n            \"poll task1\", //\n            \"pend\",       // task self-wakes\n        ]\n    );\n\n    assert_eq!(\n        other_trace.get(),\n        &[\n            \"pend\",       // spawning a task pends the executor\n            \"yield_now\",  //\n            \"pend\",       // manual wake, gets cleared by poll\n            \"poll task1\", //\n        ]\n    );\n}\n\n#[test]\nfn executor_task_cfg_args() {\n    // simulate cfg'ing away argument c\n    #[task]\n    async fn task1(a: u32, b: u32, #[cfg(any())] c: u32) {\n        let (_, _) = (a, b);\n    }\n\n    #[task]\n    async fn task2(a: u32, b: u32, #[cfg(all())] c: u32) {\n        let (_, _, _) = (a, b, c);\n    }\n}\n\n#[test]\nfn recursive_task() {\n    #[embassy_executor::task(pool_size = 2)]\n    async fn task1() {\n        let spawner = unsafe { Spawner::for_current_executor().await };\n        spawner.spawn(task1().unwrap());\n    }\n}\n\n#[cfg(feature = \"metadata-name\")]\n#[test]\nfn task_metadata() {\n    #[task]\n    async fn task1(expected_name: Option<&'static str>) {\n        use embassy_executor::Metadata;\n        assert_eq!(Metadata::for_current_task().await.name(), expected_name);\n    }\n\n    // check no task name\n    let (executor, _) = setup();\n    executor.spawner().spawn(task1(None).unwrap());\n    unsafe { executor.poll() };\n\n    // check setting task name\n    let token = task1(Some(\"foo\")).unwrap();\n    token.metadata().set_name(\"foo\");\n    executor.spawner().spawn(token);\n    unsafe { executor.poll() };\n\n    let token = task1(Some(\"bar\")).unwrap();\n    token.metadata().set_name(\"bar\");\n    executor.spawner().spawn(token);\n    unsafe { executor.poll() };\n\n    // check name is cleared if the task pool slot is recycled.\n    let (executor, _) = setup();\n    executor.spawner().spawn(task1(None).unwrap());\n    unsafe { executor.poll() };\n}\n"
  },
  {
    "path": "embassy-executor/tests/ui/abi.rs",
    "content": "#![cfg_attr(feature = \"nightly\", feature(impl_trait_in_assoc_type))]\n\nstruct Foo<'a>(&'a ());\n\n#[embassy_executor::task]\nasync extern \"C\" fn task() {}\n\nfn main() {}\n"
  },
  {
    "path": "embassy-executor/tests/ui/abi.stderr",
    "content": "error: task functions must not have an ABI qualifier\n --> tests/ui/abi.rs:6:1\n  |\n6 | async extern \"C\" fn task() {}\n  | ^^^^^^^^^^^^^^^^^^^^^^^^^^\n"
  },
  {
    "path": "embassy-executor/tests/ui/bad_return.rs",
    "content": "#![cfg_attr(feature = \"nightly\", feature(impl_trait_in_assoc_type))]\n\nstruct Foo<'a>(&'a ());\n\n#[embassy_executor::task]\nasync fn task() -> u32 {\n    5\n}\n\nfn main() {}\n"
  },
  {
    "path": "embassy-executor/tests/ui/bad_return.stderr",
    "content": "error: task functions must either not return a value, return `()` or return `!`\n --> tests/ui/bad_return.rs:6:1\n  |\n6 | async fn task() -> u32 {\n  | ^^^^^^^^^^^^^^^^^^^^^^\n"
  },
  {
    "path": "embassy-executor/tests/ui/bad_return_impl_future.rs",
    "content": "#![cfg_attr(feature = \"nightly\", feature(impl_trait_in_assoc_type))]\nuse core::future::Future;\n\n#[embassy_executor::task]\nfn task() -> impl Future<Output = u32> {\n    async { 5 }\n}\n\nfn main() {}\n"
  },
  {
    "path": "embassy-executor/tests/ui/bad_return_impl_future.stderr",
    "content": "error[E0277]: task futures must resolve to `()` or `!`\n --> tests/ui/bad_return_impl_future.rs:5:4\n  |\n4 | #[embassy_executor::task]\n  | ------------------------- required by a bound introduced by this call\n5 | fn task() -> impl Future<Output = u32> {\n  |    ^^^^ the trait `TaskFn<_>` is not implemented for fn item `fn() -> impl Future<Output = u32> {__task_task}`\n  |\n  = note: use `async fn` or change the return type to `impl Future<Output = ()>`\nnote: required by a bound in `task_pool_size`\n --> src/lib.rs\n  |\n  |     pub const fn task_pool_size<F, Args, Fut, const POOL_SIZE: usize>(_: F) -> usize\n  |                  -------------- required by a bound in this function\n  |     where\n  |         F: TaskFn<Args, Fut = Fut>,\n  |                         ^^^^^^^^^ required by this bound in `task_pool_size`\n\nerror[E0277]: task futures must resolve to `()` or `!`\n --> tests/ui/bad_return_impl_future.rs:4:1\n  |\n4 | #[embassy_executor::task]\n  | ^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `TaskFn<_>` is not implemented for fn item `fn() -> impl Future<Output = u32> {__task_task}`\n  |\n  = note: use `async fn` or change the return type to `impl Future<Output = ()>`\nnote: required by a bound in `task_pool_size`\n --> src/lib.rs\n  |\n  |     pub const fn task_pool_size<F, Args, Fut, const POOL_SIZE: usize>(_: F) -> usize\n  |                  -------------- required by a bound in this function\n  |     where\n  |         F: TaskFn<Args, Fut = Fut>,\n  |            ^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `task_pool_size`\n  = note: this error originates in the attribute macro `embassy_executor::task` (in Nightly builds, run with -Z macro-backtrace for more info)\n\nerror[E0277]: task futures must resolve to `()` or `!`\n --> tests/ui/bad_return_impl_future.rs:5:4\n  |\n4 | #[embassy_executor::task]\n  | ------------------------- required by a bound introduced by this call\n5 | fn task() -> impl Future<Output = u32> {\n  |    ^^^^ the trait `TaskFn<_>` is not implemented for fn item `fn() -> impl Future<Output = u32> {__task_task}`\n  |\n  = note: use `async fn` or change the return type to `impl Future<Output = ()>`\nnote: required by a bound in `task_pool_align`\n --> src/lib.rs\n  |\n  |     pub const fn task_pool_align<F, Args, Fut, const POOL_SIZE: usize>(_: F) -> usize\n  |                  --------------- required by a bound in this function\n  |     where\n  |         F: TaskFn<Args, Fut = Fut>,\n  |                         ^^^^^^^^^ required by this bound in `task_pool_align`\n\nerror[E0277]: task futures must resolve to `()` or `!`\n --> tests/ui/bad_return_impl_future.rs:4:1\n  |\n4 | #[embassy_executor::task]\n  | ^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `TaskFn<_>` is not implemented for fn item `fn() -> impl Future<Output = u32> {__task_task}`\n  |\n  = note: use `async fn` or change the return type to `impl Future<Output = ()>`\nnote: required by a bound in `task_pool_align`\n --> src/lib.rs\n  |\n  |     pub const fn task_pool_align<F, Args, Fut, const POOL_SIZE: usize>(_: F) -> usize\n  |                  --------------- required by a bound in this function\n  |     where\n  |         F: TaskFn<Args, Fut = Fut>,\n  |            ^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `task_pool_align`\n  = note: this error originates in the attribute macro `embassy_executor::task` (in Nightly builds, run with -Z macro-backtrace for more info)\n\nerror[E0277]: task futures must resolve to `()` or `!`\n --> tests/ui/bad_return_impl_future.rs:5:4\n  |\n4 | #[embassy_executor::task]\n  | ------------------------- required by a bound introduced by this call\n5 | fn task() -> impl Future<Output = u32> {\n  |    ^^^^ the trait `TaskFn<_>` is not implemented for fn item `fn() -> impl Future<Output = u32> {__task_task}`\n  |\n  = note: use `async fn` or change the return type to `impl Future<Output = ()>`\nnote: required by a bound in `__task_pool_get`\n --> tests/ui/bad_return_impl_future.rs:4:1\n  |\n4 | #[embassy_executor::task]\n  | ^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `__task_pool_get`\n  = note: this error originates in the attribute macro `embassy_executor::task` (in Nightly builds, run with -Z macro-backtrace for more info)\n\nerror[E0277]: task futures must resolve to `()` or `!`\n --> tests/ui/bad_return_impl_future.rs:5:4\n  |\n4 | #[embassy_executor::task]\n  | ------------------------- required by a bound introduced by this call\n5 | fn task() -> impl Future<Output = u32> {\n  |    ^^^^ the trait `TaskFn<_>` is not implemented for fn item `fn() -> impl Future<Output = u32> {__task_task}`\n  |\n  = note: use `async fn` or change the return type to `impl Future<Output = ()>`\nnote: required by a bound in `task_pool_new`\n --> src/lib.rs\n  |\n  |     pub const fn task_pool_new<F, Args, Fut, const POOL_SIZE: usize>(_: F) -> TaskPool<Fut, POOL_SIZE>\n  |                  ------------- required by a bound in this function\n  |     where\n  |         F: TaskFn<Args, Fut = Fut>,\n  |                         ^^^^^^^^^ required by this bound in `task_pool_new`\n\nerror[E0277]: task futures must resolve to `()` or `!`\n --> tests/ui/bad_return_impl_future.rs:4:1\n  |\n4 | #[embassy_executor::task]\n  | ^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `TaskFn<_>` is not implemented for fn item `fn() -> impl Future<Output = u32> {__task_task}`\n  |\n  = note: use `async fn` or change the return type to `impl Future<Output = ()>`\nnote: required by a bound in `task_pool_new`\n --> src/lib.rs\n  |\n  |     pub const fn task_pool_new<F, Args, Fut, const POOL_SIZE: usize>(_: F) -> TaskPool<Fut, POOL_SIZE>\n  |                  ------------- required by a bound in this function\n  |     where\n  |         F: TaskFn<Args, Fut = Fut>,\n  |            ^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `task_pool_new`\n  = note: this error originates in the attribute macro `embassy_executor::task` (in Nightly builds, run with -Z macro-backtrace for more info)\n"
  },
  {
    "path": "embassy-executor/tests/ui/bad_return_impl_future_nightly.rs",
    "content": "#![cfg_attr(feature = \"nightly\", feature(impl_trait_in_assoc_type))]\nuse core::future::Future;\n\n#[embassy_executor::task]\nfn task() -> impl Future<Output = u32> {\n    async { 5 }\n}\n\nfn main() {}\n"
  },
  {
    "path": "embassy-executor/tests/ui/bad_return_impl_future_nightly.stderr",
    "content": "error[E0277]: task futures must resolve to `()` or `!`\n --> tests/ui/bad_return_impl_future_nightly.rs:4:1\n  |\n4 | #[embassy_executor::task]\n  | ^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `TaskReturnValue` is not implemented for `u32`\n  |\n  = note: use `async fn` or change the return type to `impl Future<Output = ()>`\nhelp: the following other types implement trait `TaskReturnValue`\n --> src/lib.rs\n  |\n  |     impl TaskReturnValue for () {}\n  |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^ `()`\n  |     impl TaskReturnValue for Never {}\n  |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `<fn() -> ! as HasOutput>::Output`\n"
  },
  {
    "path": "embassy-executor/tests/ui/generics.rs",
    "content": "#![cfg_attr(feature = \"nightly\", feature(impl_trait_in_assoc_type))]\n\nstruct Foo<'a>(&'a ());\n\n#[embassy_executor::task]\nasync fn task<T: Sized>(_t: T) {}\n\nfn main() {}\n"
  },
  {
    "path": "embassy-executor/tests/ui/generics.stderr",
    "content": "error: task functions must not be generic\n --> tests/ui/generics.rs:6:1\n  |\n6 | async fn task<T: Sized>(_t: T) {}\n  | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n"
  },
  {
    "path": "embassy-executor/tests/ui/impl_trait.rs",
    "content": "#![cfg_attr(feature = \"nightly\", feature(impl_trait_in_assoc_type))]\n\n#[embassy_executor::task]\nasync fn foo(_x: impl Sized) {}\n\nfn main() {}\n"
  },
  {
    "path": "embassy-executor/tests/ui/impl_trait.stderr",
    "content": "error: `impl Trait` is not allowed in task arguments. It is syntax sugar for generics, and tasks can't be generic.\n --> tests/ui/impl_trait.rs:4:18\n  |\n4 | async fn foo(_x: impl Sized) {}\n  |                  ^^^^^^^^^^\n"
  },
  {
    "path": "embassy-executor/tests/ui/impl_trait_nested.rs",
    "content": "#![cfg_attr(feature = \"nightly\", feature(impl_trait_in_assoc_type))]\n\nstruct Foo<T>(T);\n\n#[embassy_executor::task]\nasync fn foo(_x: Foo<impl Sized + 'static>) {}\n\nfn main() {}\n"
  },
  {
    "path": "embassy-executor/tests/ui/impl_trait_nested.stderr",
    "content": "error: `impl Trait` is not allowed in task arguments. It is syntax sugar for generics, and tasks can't be generic.\n --> tests/ui/impl_trait_nested.rs:6:22\n  |\n6 | async fn foo(_x: Foo<impl Sized + 'static>) {}\n  |                      ^^^^^^^^^^^^^^^^^^^^\n"
  },
  {
    "path": "embassy-executor/tests/ui/impl_trait_static.rs",
    "content": "#![cfg_attr(feature = \"nightly\", feature(impl_trait_in_assoc_type))]\n\n#[embassy_executor::task]\nasync fn foo(_x: impl Sized + 'static) {}\n\nfn main() {}\n"
  },
  {
    "path": "embassy-executor/tests/ui/impl_trait_static.stderr",
    "content": "error: `impl Trait` is not allowed in task arguments. It is syntax sugar for generics, and tasks can't be generic.\n --> tests/ui/impl_trait_static.rs:4:18\n  |\n4 | async fn foo(_x: impl Sized + 'static) {}\n  |                  ^^^^^^^^^^^^^^^^^^^^\n"
  },
  {
    "path": "embassy-executor/tests/ui/nonstatic_ref_anon.rs",
    "content": "#![cfg_attr(feature = \"nightly\", feature(impl_trait_in_assoc_type))]\n\n#[embassy_executor::task]\nasync fn foo(_x: &'_ u32) {}\n\nfn main() {}\n"
  },
  {
    "path": "embassy-executor/tests/ui/nonstatic_ref_anon.stderr",
    "content": "error: Arguments for tasks must live forever. Try using the `'static` lifetime.\n --> tests/ui/nonstatic_ref_anon.rs:4:19\n  |\n4 | async fn foo(_x: &'_ u32) {}\n  |                   ^^\n"
  },
  {
    "path": "embassy-executor/tests/ui/nonstatic_ref_anon_nested.rs",
    "content": "#![cfg_attr(feature = \"nightly\", feature(impl_trait_in_assoc_type))]\n\n#[embassy_executor::task]\nasync fn foo(_x: &'static &'_ u32) {}\n\nfn main() {}\n"
  },
  {
    "path": "embassy-executor/tests/ui/nonstatic_ref_anon_nested.stderr",
    "content": "error: Arguments for tasks must live forever. Try using the `'static` lifetime.\n --> tests/ui/nonstatic_ref_anon_nested.rs:4:28\n  |\n4 | async fn foo(_x: &'static &'_ u32) {}\n  |                            ^^\n"
  },
  {
    "path": "embassy-executor/tests/ui/nonstatic_ref_elided.rs",
    "content": "#![cfg_attr(feature = \"nightly\", feature(impl_trait_in_assoc_type))]\n\n#[embassy_executor::task]\nasync fn foo(_x: &u32) {}\n\nfn main() {}\n"
  },
  {
    "path": "embassy-executor/tests/ui/nonstatic_ref_elided.stderr",
    "content": "error: Arguments for tasks must live forever. Try using the `'static` lifetime.\n --> tests/ui/nonstatic_ref_elided.rs:4:18\n  |\n4 | async fn foo(_x: &u32) {}\n  |                  ^\n"
  },
  {
    "path": "embassy-executor/tests/ui/nonstatic_ref_generic.rs",
    "content": "#![cfg_attr(feature = \"nightly\", feature(impl_trait_in_assoc_type))]\n\n#[embassy_executor::task]\nasync fn foo<'a>(_x: &'a u32) {}\n\nfn main() {}\n"
  },
  {
    "path": "embassy-executor/tests/ui/nonstatic_ref_generic.stderr",
    "content": "error: task functions must not be generic\n --> tests/ui/nonstatic_ref_generic.rs:4:1\n  |\n4 | async fn foo<'a>(_x: &'a u32) {}\n  | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\nerror: Arguments for tasks must live forever. Try using the `'static` lifetime.\n --> tests/ui/nonstatic_ref_generic.rs:4:23\n  |\n4 | async fn foo<'a>(_x: &'a u32) {}\n  |                       ^^\n"
  },
  {
    "path": "embassy-executor/tests/ui/nonstatic_struct_anon.rs",
    "content": "#![cfg_attr(feature = \"nightly\", feature(impl_trait_in_assoc_type))]\n\nstruct Foo<'a>(&'a ());\n\n#[embassy_executor::task]\nasync fn task(_x: Foo<'_>) {}\n\nfn main() {}\n"
  },
  {
    "path": "embassy-executor/tests/ui/nonstatic_struct_anon.stderr",
    "content": "error: Arguments for tasks must live forever. Try using the `'static` lifetime.\n --> tests/ui/nonstatic_struct_anon.rs:6:23\n  |\n6 | async fn task(_x: Foo<'_>) {}\n  |                       ^^\n"
  },
  {
    "path": "embassy-executor/tests/ui/nonstatic_struct_elided.rs",
    "content": "#![cfg_attr(feature = \"nightly\", feature(impl_trait_in_assoc_type))]\n\nstruct Foo<'a>(&'a ());\n\n#[embassy_executor::task]\nasync fn task(_x: Foo) {}\n\nfn main() {}\n"
  },
  {
    "path": "embassy-executor/tests/ui/nonstatic_struct_elided.stderr",
    "content": "error[E0726]: implicit elided lifetime not allowed here\n --> tests/ui/nonstatic_struct_elided.rs:6:19\n  |\n6 | async fn task(_x: Foo) {}\n  |                   ^^^ expected lifetime parameter\n  |\nhelp: indicate the anonymous lifetime\n  |\n6 | async fn task(_x: Foo<'_>) {}\n  |                      ++++\n\nerror: lifetime may not live long enough\n --> tests/ui/nonstatic_struct_elided.rs:5:1\n  |\n5 | #[embassy_executor::task]\n  | ^^^^^^^^^^^^^^^^^^^^^^^^^ returning this value requires that `'1` must outlive `'static`\n6 | async fn task(_x: Foo) {}\n  |               -- has type `Foo<'1>`\n  |\n  = note: this error originates in the attribute macro `embassy_executor::task` (in Nightly builds, run with -Z macro-backtrace for more info)\nhelp: to declare that `impl Sized` captures data from argument `_x`, you can add an explicit `'_` lifetime bound\n  |\n5 | #[embassy_executor::task] + '_\n  |                           ++++\n"
  },
  {
    "path": "embassy-executor/tests/ui/nonstatic_struct_generic.rs",
    "content": "#![cfg_attr(feature = \"nightly\", feature(impl_trait_in_assoc_type))]\n\nstruct Foo<'a>(&'a ());\n\n#[embassy_executor::task]\nasync fn task<'a>(_x: Foo<'a>) {}\n\nfn main() {}\n"
  },
  {
    "path": "embassy-executor/tests/ui/nonstatic_struct_generic.stderr",
    "content": "error: task functions must not be generic\n --> tests/ui/nonstatic_struct_generic.rs:6:1\n  |\n6 | async fn task<'a>(_x: Foo<'a>) {}\n  | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\nerror: Arguments for tasks must live forever. Try using the `'static` lifetime.\n --> tests/ui/nonstatic_struct_generic.rs:6:27\n  |\n6 | async fn task<'a>(_x: Foo<'a>) {}\n  |                           ^^\n"
  },
  {
    "path": "embassy-executor/tests/ui/not_async.rs",
    "content": "#![cfg_attr(feature = \"nightly\", feature(impl_trait_in_assoc_type))]\n\nstruct Foo<'a>(&'a ());\n\n#[embassy_executor::task]\nfn task() {}\n\nfn main() {}\n"
  },
  {
    "path": "embassy-executor/tests/ui/not_async.stderr",
    "content": "error: task functions must be async\n --> tests/ui/not_async.rs:6:1\n  |\n6 | fn task() {}\n  | ^^^^^^^^^\n"
  },
  {
    "path": "embassy-executor/tests/ui/return_impl_future_nonsend.rs",
    "content": "#![cfg_attr(feature = \"nightly\", feature(impl_trait_in_assoc_type))]\n\nuse core::future::Future;\n\nuse embassy_executor::SendSpawner;\n\n#[embassy_executor::task]\nfn task() -> impl Future<Output = ()> {\n    // runs in spawning thread\n    let non_send: *mut () = core::ptr::null_mut();\n    async move {\n        // runs in executor thread\n        println!(\"{}\", non_send as usize);\n    }\n}\n\nfn send_spawn(s: SendSpawner) {\n    s.spawn(task().unwrap());\n}\n\nfn main() {}\n"
  },
  {
    "path": "embassy-executor/tests/ui/return_impl_future_nonsend.stderr",
    "content": "error: future cannot be sent between threads safely\n  --> tests/ui/return_impl_future_nonsend.rs:18:13\n   |\n18 |     s.spawn(task().unwrap());\n   |             ^^^^^^^^^^^^^^^ future created by async block is not `Send`\n   |\n   = help: within `impl Sized`, the trait `Send` is not implemented for `*mut ()`\nnote: captured value is not `Send`\n  --> tests/ui/return_impl_future_nonsend.rs:13:24\n   |\n13 |         println!(\"{}\", non_send as usize);\n   |                        ^^^^^^^^ has type `*mut ()` which is not `Send`\nnote: required by a bound in `SendSpawner::spawn`\n  --> src/spawner.rs\n   |\n   |     pub fn spawn<S: Send>(&self, token: SpawnToken<S>) {\n   |                     ^^^^ required by this bound in `SendSpawner::spawn`\n"
  },
  {
    "path": "embassy-executor/tests/ui/return_impl_send.rs",
    "content": "#![cfg_attr(feature = \"nightly\", feature(impl_trait_in_assoc_type))]\n\n#[embassy_executor::task]\nfn task() -> impl Send {}\n\nfn main() {}\n"
  },
  {
    "path": "embassy-executor/tests/ui/return_impl_send.stderr",
    "content": "error[E0277]: task futures must resolve to `()` or `!`\n --> tests/ui/return_impl_send.rs:4:4\n  |\n3 | #[embassy_executor::task]\n  | ------------------------- required by a bound introduced by this call\n4 | fn task() -> impl Send {}\n  |    ^^^^ the trait `TaskFn<_>` is not implemented for fn item `fn() -> impl Send {__task_task}`\n  |\n  = note: use `async fn` or change the return type to `impl Future<Output = ()>`\nnote: required by a bound in `task_pool_size`\n --> src/lib.rs\n  |\n  |     pub const fn task_pool_size<F, Args, Fut, const POOL_SIZE: usize>(_: F) -> usize\n  |                  -------------- required by a bound in this function\n  |     where\n  |         F: TaskFn<Args, Fut = Fut>,\n  |                         ^^^^^^^^^ required by this bound in `task_pool_size`\n\nerror[E0277]: task futures must resolve to `()` or `!`\n --> tests/ui/return_impl_send.rs:3:1\n  |\n3 | #[embassy_executor::task]\n  | ^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `TaskFn<_>` is not implemented for fn item `fn() -> impl Send {__task_task}`\n  |\n  = note: use `async fn` or change the return type to `impl Future<Output = ()>`\nnote: required by a bound in `task_pool_size`\n --> src/lib.rs\n  |\n  |     pub const fn task_pool_size<F, Args, Fut, const POOL_SIZE: usize>(_: F) -> usize\n  |                  -------------- required by a bound in this function\n  |     where\n  |         F: TaskFn<Args, Fut = Fut>,\n  |            ^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `task_pool_size`\n  = note: this error originates in the attribute macro `embassy_executor::task` (in Nightly builds, run with -Z macro-backtrace for more info)\n\nerror[E0277]: task futures must resolve to `()` or `!`\n --> tests/ui/return_impl_send.rs:4:4\n  |\n3 | #[embassy_executor::task]\n  | ------------------------- required by a bound introduced by this call\n4 | fn task() -> impl Send {}\n  |    ^^^^ the trait `TaskFn<_>` is not implemented for fn item `fn() -> impl Send {__task_task}`\n  |\n  = note: use `async fn` or change the return type to `impl Future<Output = ()>`\nnote: required by a bound in `task_pool_align`\n --> src/lib.rs\n  |\n  |     pub const fn task_pool_align<F, Args, Fut, const POOL_SIZE: usize>(_: F) -> usize\n  |                  --------------- required by a bound in this function\n  |     where\n  |         F: TaskFn<Args, Fut = Fut>,\n  |                         ^^^^^^^^^ required by this bound in `task_pool_align`\n\nerror[E0277]: task futures must resolve to `()` or `!`\n --> tests/ui/return_impl_send.rs:3:1\n  |\n3 | #[embassy_executor::task]\n  | ^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `TaskFn<_>` is not implemented for fn item `fn() -> impl Send {__task_task}`\n  |\n  = note: use `async fn` or change the return type to `impl Future<Output = ()>`\nnote: required by a bound in `task_pool_align`\n --> src/lib.rs\n  |\n  |     pub const fn task_pool_align<F, Args, Fut, const POOL_SIZE: usize>(_: F) -> usize\n  |                  --------------- required by a bound in this function\n  |     where\n  |         F: TaskFn<Args, Fut = Fut>,\n  |            ^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `task_pool_align`\n  = note: this error originates in the attribute macro `embassy_executor::task` (in Nightly builds, run with -Z macro-backtrace for more info)\n\nerror[E0277]: task futures must resolve to `()` or `!`\n --> tests/ui/return_impl_send.rs:4:4\n  |\n3 | #[embassy_executor::task]\n  | ------------------------- required by a bound introduced by this call\n4 | fn task() -> impl Send {}\n  |    ^^^^ the trait `TaskFn<_>` is not implemented for fn item `fn() -> impl Send {__task_task}`\n  |\n  = note: use `async fn` or change the return type to `impl Future<Output = ()>`\nnote: required by a bound in `__task_pool_get`\n --> tests/ui/return_impl_send.rs:3:1\n  |\n3 | #[embassy_executor::task]\n  | ^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `__task_pool_get`\n  = note: this error originates in the attribute macro `embassy_executor::task` (in Nightly builds, run with -Z macro-backtrace for more info)\n\nerror[E0277]: `impl Send` is not a future\n --> tests/ui/return_impl_send.rs:3:1\n  |\n3 | #[embassy_executor::task]\n  | ^^^^^^^^^^^^^^^^^^^^^^^^^ `impl Send` is not a future\n  |\n  = help: the trait `Future` is not implemented for `impl Send`\nnote: required by a bound in `TaskPool::<F, N>::spawn`\n --> src/raw/mod.rs\n  |\n  | impl<F: Future + 'static, const N: usize> TaskPool<F, N> {\n  |         ^^^^^^ required by this bound in `TaskPool::<F, N>::spawn`\n...\n  |     pub fn spawn(&'static self, future: impl FnOnce() -> F) -> Result<SpawnToken<impl Sized>, SpawnError> {\n  |            ----- required by a bound in this associated function\n  = note: this error originates in the attribute macro `embassy_executor::task` (in Nightly builds, run with -Z macro-backtrace for more info)\n\nerror[E0277]: task futures must resolve to `()` or `!`\n --> tests/ui/return_impl_send.rs:4:4\n  |\n3 | #[embassy_executor::task]\n  | ------------------------- required by a bound introduced by this call\n4 | fn task() -> impl Send {}\n  |    ^^^^ the trait `TaskFn<_>` is not implemented for fn item `fn() -> impl Send {__task_task}`\n  |\n  = note: use `async fn` or change the return type to `impl Future<Output = ()>`\nnote: required by a bound in `task_pool_new`\n --> src/lib.rs\n  |\n  |     pub const fn task_pool_new<F, Args, Fut, const POOL_SIZE: usize>(_: F) -> TaskPool<Fut, POOL_SIZE>\n  |                  ------------- required by a bound in this function\n  |     where\n  |         F: TaskFn<Args, Fut = Fut>,\n  |                         ^^^^^^^^^ required by this bound in `task_pool_new`\n\nerror[E0277]: task futures must resolve to `()` or `!`\n --> tests/ui/return_impl_send.rs:3:1\n  |\n3 | #[embassy_executor::task]\n  | ^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `TaskFn<_>` is not implemented for fn item `fn() -> impl Send {__task_task}`\n  |\n  = note: use `async fn` or change the return type to `impl Future<Output = ()>`\nnote: required by a bound in `task_pool_new`\n --> src/lib.rs\n  |\n  |     pub const fn task_pool_new<F, Args, Fut, const POOL_SIZE: usize>(_: F) -> TaskPool<Fut, POOL_SIZE>\n  |                  ------------- required by a bound in this function\n  |     where\n  |         F: TaskFn<Args, Fut = Fut>,\n  |            ^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `task_pool_new`\n  = note: this error originates in the attribute macro `embassy_executor::task` (in Nightly builds, run with -Z macro-backtrace for more info)\n"
  },
  {
    "path": "embassy-executor/tests/ui/return_impl_send_nightly.rs",
    "content": "#![cfg_attr(feature = \"nightly\", feature(impl_trait_in_assoc_type))]\n\n#[embassy_executor::task]\nfn task() -> impl Send {}\n\nfn main() {}\n"
  },
  {
    "path": "embassy-executor/tests/ui/return_impl_send_nightly.stderr",
    "content": "error[E0277]: `impl Send` is not a future\n --> tests/ui/return_impl_send_nightly.rs:3:1\n  |\n3 | #[embassy_executor::task]\n  | ^^^^^^^^^^^^^^^^^^^^^^^^^\n  | |\n  | `impl Send` is not a future\n  | return type was inferred to be `impl Send` here\n  |\n  = help: the trait `Future` is not implemented for `impl Send`\n"
  },
  {
    "path": "embassy-executor/tests/ui/self.rs",
    "content": "#![cfg_attr(feature = \"nightly\", feature(impl_trait_in_assoc_type))]\n\nstruct Foo<'a>(&'a ());\n\n#[embassy_executor::task]\nasync fn task(self) {}\n\nfn main() {}\n"
  },
  {
    "path": "embassy-executor/tests/ui/self.stderr",
    "content": "error: task functions must not have `self` arguments\n --> tests/ui/self.rs:6:15\n  |\n6 | async fn task(self) {}\n  |               ^^^^\n\nerror: `self` parameter is only allowed in associated functions\n --> tests/ui/self.rs:6:15\n  |\n6 | async fn task(self) {}\n  |               ^^^^ not semantically valid as function parameter\n  |\n  = note: associated functions are those in `impl` or `trait` definitions\n"
  },
  {
    "path": "embassy-executor/tests/ui/self_ref.rs",
    "content": "#![cfg_attr(feature = \"nightly\", feature(impl_trait_in_assoc_type))]\n\nstruct Foo<'a>(&'a ());\n\n#[embassy_executor::task]\nasync fn task(&mut self) {}\n\nfn main() {}\n"
  },
  {
    "path": "embassy-executor/tests/ui/self_ref.stderr",
    "content": "error: task functions must not have `self` arguments\n --> tests/ui/self_ref.rs:6:15\n  |\n6 | async fn task(&mut self) {}\n  |               ^^^^^^^^^\n\nerror: `self` parameter is only allowed in associated functions\n --> tests/ui/self_ref.rs:6:15\n  |\n6 | async fn task(&mut self) {}\n  |               ^^^^^^^^^ not semantically valid as function parameter\n  |\n  = note: associated functions are those in `impl` or `trait` definitions\n"
  },
  {
    "path": "embassy-executor/tests/ui/spawn_nonsend.rs",
    "content": "#![cfg_attr(feature = \"nightly\", feature(impl_trait_in_assoc_type))]\n\nuse embassy_executor::SendSpawner;\n\n#[embassy_executor::task]\nasync fn task(non_send: *mut ()) {\n    println!(\"{}\", non_send as usize);\n}\n\nfn send_spawn(s: SendSpawner) {\n    s.spawn(task(core::ptr::null_mut()).unwrap());\n}\n\nfn main() {}\n"
  },
  {
    "path": "embassy-executor/tests/ui/spawn_nonsend.stderr",
    "content": "error[E0277]: `*mut ()` cannot be sent between threads safely\n  --> tests/ui/spawn_nonsend.rs:11:13\n   |\n 5 | #[embassy_executor::task]\n   | ------------------------- within this `impl Sized`\n...\n11 |     s.spawn(task(core::ptr::null_mut()).unwrap());\n   |       ----- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `*mut ()` cannot be sent between threads safely\n   |       |\n   |       required by a bound introduced by this call\n   |\n   = help: within `impl Sized`, the trait `Send` is not implemented for `*mut ()`\nnote: required because it's used within this closure\n  --> tests/ui/spawn_nonsend.rs:5:1\n   |\n 5 | #[embassy_executor::task]\n   | ^^^^^^^^^^^^^^^^^^^^^^^^^\nnote: required because it appears within the type `impl Sized`\n  --> src/raw/mod.rs\n   |\n   |     pub unsafe fn _spawn_async_fn<FutFn>(&'static self, future: FutFn) -> Result<SpawnToken<impl Sized>, SpawnError>\n   |                                                                                             ^^^^^^^^^^\nnote: required because it appears within the type `impl Sized`\n  --> tests/ui/spawn_nonsend.rs:5:1\n   |\n 5 | #[embassy_executor::task]\n   | ^^^^^^^^^^^^^^^^^^^^^^^^^\nnote: required by a bound in `SendSpawner::spawn`\n  --> src/spawner.rs\n   |\n   |     pub fn spawn<S: Send>(&self, token: SpawnToken<S>) {\n   |                     ^^^^ required by this bound in `SendSpawner::spawn`\n   = note: this error originates in the attribute macro `embassy_executor::task` (in Nightly builds, run with -Z macro-backtrace for more info)\n"
  },
  {
    "path": "embassy-executor/tests/ui/task_safety_attribute.rs",
    "content": "#![cfg_attr(feature = \"nightly\", feature(impl_trait_in_assoc_type))]\n#![deny(unused_unsafe)]\n\nuse std::mem;\n\n#[embassy_executor::task]\nasync fn safe() {}\n\n#[embassy_executor::task]\nasync unsafe fn not_safe() {}\n\n#[unsafe(export_name = \"__pender\")]\nfn pender(_: *mut ()) {\n    // The test doesn't link if we don't include this.\n    // We never call this anyway.\n}\n\nfn main() {\n    let _forget_me = safe();\n    // SAFETY: not_safe has not safety preconditions\n    let _forget_me2 = unsafe { not_safe() };\n\n    mem::forget(_forget_me);\n    mem::forget(_forget_me2);\n}\n"
  },
  {
    "path": "embassy-executor/tests/ui/type_error.rs",
    "content": "#![cfg_attr(feature = \"nightly\", feature(impl_trait_in_assoc_type))]\n\n#[embassy_executor::task]\nasync fn task() {\n    5\n}\n\nfn main() {}\n"
  },
  {
    "path": "embassy-executor/tests/ui/type_error.stderr",
    "content": "error[E0308]: mismatched types\n --> tests/ui/type_error.rs:5:5\n  |\n4 | async fn task() {\n  |                - help: try adding a return type: `-> i32`\n5 |     5\n  |     ^ expected `()`, found integer\n"
  },
  {
    "path": "embassy-executor/tests/ui/unsafe_op_in_unsafe_task.rs",
    "content": "#![cfg_attr(feature = \"nightly\", feature(impl_trait_in_assoc_type))]\n#![deny(unsafe_op_in_unsafe_fn)]\n\n#[embassy_executor::task]\nasync unsafe fn task() {\n    let x = 5;\n    (&x as *const i32).read();\n}\n\nfn main() {}\n"
  },
  {
    "path": "embassy-executor/tests/ui/unsafe_op_in_unsafe_task.stderr",
    "content": "error[E0133]: call to unsafe function `std::ptr::const_ptr::<impl *const T>::read` is unsafe and requires unsafe block\n --> tests/ui/unsafe_op_in_unsafe_task.rs:7:5\n  |\n7 |     (&x as *const i32).read();\n  |     ^^^^^^^^^^^^^^^^^^^^^^^^^ call to unsafe function\n  |\n  = note: for more information, see <https://doc.rust-lang.org/edition-guide/rust-2024/unsafe-op-in-unsafe-fn.html>\n  = note: consult the function's documentation for information on how to avoid undefined behavior\nnote: an unsafe function restricts its caller, but its body is safe by default\n --> tests/ui/unsafe_op_in_unsafe_task.rs:5:1\n  |\n5 | async unsafe fn task() {\n  | ^^^^^^^^^^^^^^^^^^^^^^\nnote: the lint level is defined here\n --> tests/ui/unsafe_op_in_unsafe_task.rs:2:9\n  |\n2 | #![deny(unsafe_op_in_unsafe_fn)]\n  |         ^^^^^^^^^^^^^^^^^^^^^^\n"
  },
  {
    "path": "embassy-executor/tests/ui/where_clause.rs",
    "content": "#![cfg_attr(feature = \"nightly\", feature(impl_trait_in_assoc_type))]\n\nstruct Foo<'a>(&'a ());\n\n#[embassy_executor::task]\nasync fn task()\nwhere\n    (): Sized,\n{\n}\n\nfn main() {}\n"
  },
  {
    "path": "embassy-executor/tests/ui/where_clause.stderr",
    "content": "error: task functions must not have `where` clauses\n --> tests/ui/where_clause.rs:6:1\n  |\n6 | / async fn task()\n7 | | where\n8 | |     (): Sized,\n  | |______________^\n"
  },
  {
    "path": "embassy-executor/tests/ui.rs",
    "content": "#[cfg(not(miri))]\n#[test]\nfn ui() {\n    let t = trybuild::TestCases::new();\n    t.compile_fail(\"tests/ui/abi.rs\");\n    t.compile_fail(\"tests/ui/bad_return.rs\");\n    t.compile_fail(\"tests/ui/generics.rs\");\n    t.compile_fail(\"tests/ui/impl_trait_nested.rs\");\n    t.compile_fail(\"tests/ui/impl_trait.rs\");\n    t.compile_fail(\"tests/ui/impl_trait_static.rs\");\n    t.compile_fail(\"tests/ui/nonstatic_ref_anon_nested.rs\");\n    t.compile_fail(\"tests/ui/nonstatic_ref_anon.rs\");\n    t.compile_fail(\"tests/ui/nonstatic_ref_elided.rs\");\n    t.compile_fail(\"tests/ui/nonstatic_ref_generic.rs\");\n    t.compile_fail(\"tests/ui/nonstatic_struct_anon.rs\");\n    #[cfg(not(feature = \"nightly\"))] // we can't catch this case with the macro, so the output changes on nightly.\n    t.compile_fail(\"tests/ui/nonstatic_struct_elided.rs\");\n    t.compile_fail(\"tests/ui/nonstatic_struct_generic.rs\");\n    t.compile_fail(\"tests/ui/not_async.rs\");\n    t.compile_fail(\"tests/ui/spawn_nonsend.rs\");\n    t.compile_fail(\"tests/ui/return_impl_future_nonsend.rs\");\n    if rustversion::cfg!(stable) {\n        // output is slightly different on nightly\n        t.compile_fail(\"tests/ui/bad_return_impl_future.rs\");\n        t.compile_fail(\"tests/ui/return_impl_send.rs\");\n    }\n    if cfg!(feature = \"nightly\") {\n        t.compile_fail(\"tests/ui/bad_return_impl_future_nightly.rs\");\n        t.compile_fail(\"tests/ui/return_impl_send_nightly.rs\");\n    }\n    t.compile_fail(\"tests/ui/self_ref.rs\");\n    t.compile_fail(\"tests/ui/self.rs\");\n    t.compile_fail(\"tests/ui/type_error.rs\");\n    t.compile_fail(\"tests/ui/where_clause.rs\");\n    t.compile_fail(\"tests/ui/unsafe_op_in_unsafe_task.rs\");\n\n    t.pass(\"tests/ui/task_safety_attribute.rs\");\n}\n"
  },
  {
    "path": "embassy-executor-macros/CHANGELOG.md",
    "content": "# Changelog\n\nAll notable changes to this project will be documented in this file.\n\nThe format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),\nand this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).\n\n<!-- next-header -->\n## Unreleased - ReleaseDate\n\n## 0.8.0 - 2026-03-12\n\n- Update rust release id\n- Add task name capability\n- Follow executor api change\n"
  },
  {
    "path": "embassy-executor-macros/Cargo.toml",
    "content": "[package]\nname = \"embassy-executor-macros\"\nversion = \"0.8.0\"\nedition = \"2024\"\nlicense = \"MIT OR Apache-2.0\"\ndescription = \"macros for creating the entry point and tasks for embassy-executor\"\nrepository = \"https://github.com/embassy-rs/embassy\"\ndocumentation = \"https://docs.embassy.dev/embassy-executor-macros\"\ncategories = [\n    \"embedded\",\n    \"no-std\",\n    \"asynchronous\",\n]\n\n[dependencies]\nsyn = { version = \"2.0.15\", features = [\"full\", \"visit\"] }\nquote = \"1.0.9\"\ndarling = \"0.20.1\"\nproc-macro2 = \"1.0.29\"\n\n[lib]\nproc-macro = true\n\n[features]\nnightly = []\nmetadata-name = []\n"
  },
  {
    "path": "embassy-executor-macros/README.md",
    "content": "# embassy-executor-macros\n\nAn [Embassy](https://embassy.dev) project.\n\nNOTE: Do not use this crate directly. The macros are re-exported by `embassy-executor`.\n"
  },
  {
    "path": "embassy-executor-macros/src/lib.rs",
    "content": "#![doc = include_str!(\"../README.md\")]\nextern crate proc_macro;\n\nuse proc_macro::TokenStream;\n\nmod macros;\nmod util;\nuse macros::*;\n\n/// Declares an async task that can be run by `embassy-executor`. The optional `pool_size` parameter can be used to specify how\n/// many concurrent tasks can be spawned (default is 1) for the function.\n///\n///\n/// The following restrictions apply:\n///\n/// * The function must be declared `async`.\n/// * The function must not use generics.\n/// * The optional `pool_size` attribute must be 1 or greater.\n///\n///\n/// ## Examples\n///\n/// Declaring a task taking no arguments:\n///\n/// ``` rust\n/// #[embassy_executor::task]\n/// async fn mytask() {\n///     // Function body\n/// }\n/// ```\n///\n/// Declaring a task with a given pool size:\n///\n/// ``` rust\n/// #[embassy_executor::task(pool_size = 4)]\n/// async fn mytask() {\n///     // Function body\n/// }\n/// ```\n#[proc_macro_attribute]\npub fn task(args: TokenStream, item: TokenStream) -> TokenStream {\n    task::run(args.into(), item.into()).into()\n}\n\n#[proc_macro_attribute]\npub fn main_avr(args: TokenStream, item: TokenStream) -> TokenStream {\n    main::run(args.into(), item.into(), &main::ARCH_AVR).into()\n}\n\n/// Creates a new `executor` instance and declares an application entry point for Cortex-M spawning the corresponding function body as an async task.\n///\n/// The following restrictions apply:\n///\n/// * The function must accept exactly 1 parameter, an `embassy_executor::Spawner` handle that it can use to spawn additional tasks.\n/// * The function must be declared `async`.\n/// * The function must not use generics.\n/// * Only a single `main` task may be declared.\n///\n/// ## Examples\n/// Spawning a task:\n///\n/// ``` rust\n/// #[embassy_executor::main]\n/// async fn main(_s: embassy_executor::Spawner) {\n///     // Function body\n/// }\n/// ```\n#[proc_macro_attribute]\npub fn main_cortex_m(args: TokenStream, item: TokenStream) -> TokenStream {\n    main::run(args.into(), item.into(), &main::ARCH_CORTEX_M).into()\n}\n\n/// Creates a new `executor` instance and declares an application entry point for Cortex-A/R\n/// spawning the corresponding function body as an async task.\n///\n/// The following restrictions apply:\n///\n/// * The function must accept exactly 1 parameter, an `embassy_executor::Spawner` handle that it\n///   can use to spawn additional tasks.\n/// * The function must be declared `async`.\n/// * The function must not use generics.\n/// * Only a single `main` task may be declared.\n///\n/// ## Examples\n/// Spawning a task:\n///\n/// ``` rust\n/// #[embassy_executor::main]\n/// async fn main(_s: embassy_executor::Spawner) {\n///     // Function body\n/// }\n/// ```\n#[proc_macro_attribute]\npub fn main_cortex_ar(args: TokenStream, item: TokenStream) -> TokenStream {\n    main::run(args.into(), item.into(), &main::ARCH_CORTEX_AR).into()\n}\n\n/// Creates a new `executor` instance and declares an architecture agnostic application entry point spawning\n/// the corresponding function body as an async task.\n///\n/// The following restrictions apply:\n///\n/// * The function must accept exactly 1 parameter, an `embassy_executor::Spawner` handle that it can use to spawn additional tasks.\n/// * The function must be declared `async`.\n/// * The function must not use generics.\n/// * Only a single `main` task may be declared.\n///\n/// A user-defined entry macro must provided via the `entry` argument\n///\n/// ## Examples\n/// Spawning a task:\n/// ``` rust\n/// #[embassy_executor::main(entry = \"qingke_rt::entry\")]\n/// async fn main(_s: embassy_executor::Spawner) {\n///     // Function body\n/// }\n/// ```\n#[proc_macro_attribute]\npub fn main_spin(args: TokenStream, item: TokenStream) -> TokenStream {\n    main::run(args.into(), item.into(), &main::ARCH_SPIN).into()\n}\n\n/// Creates a new `executor` instance and declares an application entry point for RISC-V spawning the corresponding function body as an async task.\n///\n/// The following restrictions apply:\n///\n/// * The function must accept exactly 1 parameter, an `embassy_executor::Spawner` handle that it can use to spawn additional tasks.\n/// * The function must be declared `async`.\n/// * The function must not use generics.\n/// * Only a single `main` task may be declared.\n///\n/// A user-defined entry macro can be optionally provided via the `entry` argument to override the default of `riscv_rt::entry`.\n///\n/// ## Examples\n/// Spawning a task:\n///\n/// ``` rust\n/// #[embassy_executor::main]\n/// async fn main(_s: embassy_executor::Spawner) {\n///     // Function body\n/// }\n/// ```\n///\n/// Spawning a task using a custom entry macro:\n/// ``` rust\n/// #[embassy_executor::main(entry = \"esp_riscv_rt::entry\")]\n/// async fn main(_s: embassy_executor::Spawner) {\n///     // Function body\n/// }\n/// ```\n#[proc_macro_attribute]\npub fn main_riscv(args: TokenStream, item: TokenStream) -> TokenStream {\n    main::run(args.into(), item.into(), &main::ARCH_RISCV).into()\n}\n\n/// Creates a new `executor` instance and declares an application entry point for STD spawning the corresponding function body as an async task.\n///\n/// The following restrictions apply:\n///\n/// * The function must accept exactly 1 parameter, an `embassy_executor::Spawner` handle that it can use to spawn additional tasks.\n/// * The function must be declared `async`.\n/// * The function must not use generics.\n/// * Only a single `main` task may be declared.\n///\n/// ## Examples\n/// Spawning a task:\n///\n/// ``` rust\n/// #[embassy_executor::main]\n/// async fn main(_s: embassy_executor::Spawner) {\n///     // Function body\n/// }\n/// ```\n#[proc_macro_attribute]\npub fn main_std(args: TokenStream, item: TokenStream) -> TokenStream {\n    main::run(args.into(), item.into(), &main::ARCH_STD).into()\n}\n\n/// Creates a new `executor` instance and declares an application entry point for WASM spawning the corresponding function body as an async task.\n///\n/// The following restrictions apply:\n///\n/// * The function must accept exactly 1 parameter, an `embassy_executor::Spawner` handle that it can use to spawn additional tasks.\n/// * The function must be declared `async`.\n/// * The function must not use generics.\n/// * Only a single `main` task may be declared.\n///\n/// ## Examples\n/// Spawning a task:\n///\n/// ``` rust\n/// #[embassy_executor::main]\n/// async fn main(_s: embassy_executor::Spawner) {\n///     // Function body\n/// }\n/// ```\n#[proc_macro_attribute]\npub fn main_wasm(args: TokenStream, item: TokenStream) -> TokenStream {\n    main::run(args.into(), item.into(), &main::ARCH_WASM).into()\n}\n\n/// Creates a new `executor` instance and declares an application entry point for an unspecified architecture, spawning the corresponding function body as an async task.\n///\n/// The following restrictions apply:\n///\n/// * The function must accept exactly 1 parameter, an `embassy_executor::Spawner` handle that it can use to spawn additional tasks.\n/// * The function must be declared `async`.\n/// * The function must not use generics.\n/// * Only a single `main` task may be declared.\n///\n/// A user-defined entry macro and executor type must be provided via the `entry` and `executor` arguments of the `main` macro.\n///\n/// ## Examples\n/// Spawning a task:\n/// ``` rust\n/// #[embassy_executor::main(entry = \"your_hal::entry\", executor = \"your_hal::Executor\")]\n/// async fn main(_s: embassy_executor::Spawner) {\n///     // Function body\n/// }\n/// ```\n#[proc_macro_attribute]\npub fn main_unspecified(args: TokenStream, item: TokenStream) -> TokenStream {\n    main::run(args.into(), item.into(), &main::ARCH_UNSPECIFIED).into()\n}\n"
  },
  {
    "path": "embassy-executor-macros/src/macros/main.rs",
    "content": "use std::str::FromStr;\n\nuse darling::FromMeta;\nuse darling::export::NestedMeta;\nuse proc_macro2::TokenStream;\nuse quote::quote;\nuse syn::{ReturnType, Type};\n\nuse crate::util::*;\n\nenum Flavor {\n    Standard,\n    Wasm,\n}\n\npub(crate) struct Arch {\n    default_entry: Option<&'static str>,\n    flavor: Flavor,\n    executor_required: bool,\n}\n\npub static ARCH_AVR: Arch = Arch {\n    default_entry: Some(\"avr_device::entry\"),\n    flavor: Flavor::Standard,\n    executor_required: false,\n};\n\npub static ARCH_RISCV: Arch = Arch {\n    default_entry: Some(\"riscv_rt::entry\"),\n    flavor: Flavor::Standard,\n    executor_required: false,\n};\n\npub static ARCH_CORTEX_M: Arch = Arch {\n    default_entry: Some(\"cortex_m_rt::entry\"),\n    flavor: Flavor::Standard,\n    executor_required: false,\n};\n\npub static ARCH_CORTEX_AR: Arch = Arch {\n    default_entry: None,\n    flavor: Flavor::Standard,\n    executor_required: false,\n};\n\npub static ARCH_SPIN: Arch = Arch {\n    default_entry: None,\n    flavor: Flavor::Standard,\n    executor_required: false,\n};\n\npub static ARCH_STD: Arch = Arch {\n    default_entry: None,\n    flavor: Flavor::Standard,\n    executor_required: false,\n};\n\npub static ARCH_WASM: Arch = Arch {\n    default_entry: Some(\"wasm_bindgen::prelude::wasm_bindgen(start)\"),\n    flavor: Flavor::Wasm,\n    executor_required: false,\n};\n\npub static ARCH_UNSPECIFIED: Arch = Arch {\n    default_entry: None,\n    flavor: Flavor::Standard,\n    executor_required: true,\n};\n\n#[derive(Debug, FromMeta, Default)]\nstruct Args {\n    #[darling(default)]\n    entry: Option<String>,\n    #[darling(default)]\n    executor: Option<String>,\n}\n\npub fn run(args: TokenStream, item: TokenStream, arch: &Arch) -> TokenStream {\n    let mut errors = TokenStream::new();\n\n    // If any of the steps for this macro fail, we still want to expand to an item that is as close\n    // to the expected output as possible. This helps out IDEs such that completions and other\n    // related features keep working.\n    let f: ItemFn = match syn::parse2(item.clone()) {\n        Ok(x) => x,\n        Err(e) => return token_stream_with_error(item, e),\n    };\n\n    let args = match NestedMeta::parse_meta_list(args) {\n        Ok(x) => x,\n        Err(e) => return token_stream_with_error(item, e),\n    };\n\n    let args = match Args::from_list(&args) {\n        Ok(x) => x,\n        Err(e) => {\n            errors.extend(e.write_errors());\n            Args::default()\n        }\n    };\n\n    let fargs = f.sig.inputs.clone();\n\n    if f.sig.asyncness.is_none() {\n        error(&mut errors, &f.sig, \"main function must be async\");\n    }\n    if !f.sig.generics.params.is_empty() {\n        error(&mut errors, &f.sig, \"main function must not be generic\");\n    }\n    if !f.sig.generics.where_clause.is_none() {\n        error(&mut errors, &f.sig, \"main function must not have `where` clauses\");\n    }\n    if !f.sig.abi.is_none() {\n        error(&mut errors, &f.sig, \"main function must not have an ABI qualifier\");\n    }\n    if !f.sig.variadic.is_none() {\n        error(&mut errors, &f.sig, \"main function must not be variadic\");\n    }\n    match &f.sig.output {\n        ReturnType::Default => {}\n        ReturnType::Type(_, ty) => match &**ty {\n            Type::Tuple(tuple) if tuple.elems.is_empty() => {}\n            Type::Never(_) => {}\n            _ => error(\n                &mut errors,\n                &f.sig,\n                \"main function must either not return a value, return `()` or return `!`\",\n            ),\n        },\n    }\n\n    if fargs.len() != 1 {\n        error(&mut errors, &f.sig, \"main function must have 1 argument: the spawner.\");\n    }\n\n    let entry = match (args.entry.as_deref(), arch.default_entry.as_deref()) {\n        (None, None) => TokenStream::new(),\n        (Some(x), _) | (None, Some(x)) if x == \"\" => TokenStream::new(),\n        (Some(x), _) | (None, Some(x)) => match TokenStream::from_str(x) {\n            Ok(x) => quote!(#[#x]),\n            Err(e) => {\n                error(&mut errors, &f.sig, e);\n                TokenStream::new()\n            }\n        },\n    };\n\n    let executor = match (args.executor.as_deref(), arch.executor_required) {\n        (None, true) => {\n            error(\n                &mut errors,\n                &f.sig,\n                \"\\\nNo architecture selected for embassy-executor. Make sure you've enabled one of the `arch-*` features in your Cargo.toml.\n\nAlternatively, if you would like to use a custom executor implementation, specify it with the `executor` argument.\nFor example: `#[embassy_executor::main(entry = ..., executor = \\\"some_crate::Executor\\\")]\",\n            );\n            \"\"\n        }\n        (Some(x), _) => x,\n        (None, _) => \"::embassy_executor::Executor\",\n    };\n\n    let executor = TokenStream::from_str(executor).unwrap_or_else(|e| {\n        error(&mut errors, &f.sig, e);\n        TokenStream::new()\n    });\n\n    let f_body = f.body;\n    let out = &f.sig.output;\n\n    let name_main_task = if cfg!(feature = \"metadata-name\") {\n        quote!(\n            main_task.metadata().set_name(\"main\\0\");\n        )\n    } else {\n        quote!()\n    };\n\n    let (main_ret, mut main_body) = match arch.flavor {\n        Flavor::Standard => (\n            quote!(!),\n            quote! {\n                unsafe fn __make_static<T>(t: &mut T) -> &'static mut T {\n                    unsafe { ::core::mem::transmute(t) }\n                }\n\n                let mut executor = #executor::new();\n                let executor = unsafe { __make_static(&mut executor) };\n                executor.run(|spawner| {\n                    let main_task = __embassy_main(spawner).unwrap();\n                    #name_main_task\n                    spawner.spawn(main_task);\n                })\n            },\n        ),\n        Flavor::Wasm => (\n            quote!(Result<(), wasm_bindgen::JsValue>),\n            quote! {\n                let executor = ::std::boxed::Box::leak(::std::boxed::Box::new(#executor::new()));\n\n                executor.start(|spawner| {\n                    let main_task = __embassy_main(spawner).unwrap();\n                    #name_main_task\n                    spawner.spawn(main_task);\n                });\n\n                Ok(())\n            },\n        ),\n    };\n\n    let mut main_attrs = TokenStream::new();\n    for attr in f.attrs {\n        main_attrs.extend(quote!(#attr));\n    }\n\n    if !errors.is_empty() {\n        main_body = quote! {loop{}};\n    }\n\n    let result = quote! {\n        #[::embassy_executor::task()]\n        #[allow(clippy::future_not_send)]\n        async fn __embassy_main(#fargs) #out {\n            #f_body\n        }\n\n        #entry\n        #main_attrs\n        fn main() -> #main_ret {\n            #main_body\n        }\n\n        #errors\n    };\n\n    result\n}\n"
  },
  {
    "path": "embassy-executor-macros/src/macros/mod.rs",
    "content": "pub mod main;\npub mod task;\n"
  },
  {
    "path": "embassy-executor-macros/src/macros/task.rs",
    "content": "use std::str::FromStr;\n\nuse darling::FromMeta;\nuse darling::export::NestedMeta;\nuse proc_macro2::{Span, TokenStream};\nuse quote::{format_ident, quote};\nuse syn::visit::{self, Visit};\nuse syn::{Expr, ExprLit, Lit, LitInt, ReturnType, Type, Visibility};\n\nuse crate::util::*;\n\n#[derive(Debug, FromMeta, Default)]\nstruct Args {\n    #[darling(default)]\n    pool_size: Option<syn::Expr>,\n    /// Use this to override the `embassy_executor` crate path. Defaults to `::embassy_executor`.\n    #[darling(default)]\n    embassy_executor: Option<syn::Expr>,\n}\n\npub fn run(args: TokenStream, item: TokenStream) -> TokenStream {\n    let mut errors = TokenStream::new();\n\n    // If any of the steps for this macro fail, we still want to expand to an item that is as close\n    // to the expected output as possible. This helps out IDEs such that completions and other\n    // related features keep working.\n    let f: ItemFn = match syn::parse2(item.clone()) {\n        Ok(x) => x,\n        Err(e) => return token_stream_with_error(item, e),\n    };\n\n    let args = match NestedMeta::parse_meta_list(args) {\n        Ok(x) => x,\n        Err(e) => return token_stream_with_error(item, e),\n    };\n\n    let args = match Args::from_list(&args) {\n        Ok(x) => x,\n        Err(e) => {\n            errors.extend(e.write_errors());\n            Args::default()\n        }\n    };\n\n    let pool_size = args.pool_size.unwrap_or(Expr::Lit(ExprLit {\n        attrs: vec![],\n        lit: Lit::Int(LitInt::new(\"1\", Span::call_site())),\n    }));\n\n    let embassy_executor = args\n        .embassy_executor\n        .unwrap_or(Expr::Verbatim(TokenStream::from_str(\"::embassy_executor\").unwrap()));\n\n    let returns_impl_trait = match &f.sig.output {\n        ReturnType::Type(_, ty) => matches!(**ty, Type::ImplTrait(_)),\n        _ => false,\n    };\n    if f.sig.asyncness.is_none() && !returns_impl_trait {\n        error(&mut errors, &f.sig, \"task functions must be async\");\n    }\n    if !f.sig.generics.params.is_empty() {\n        error(&mut errors, &f.sig, \"task functions must not be generic\");\n    }\n    if !f.sig.generics.where_clause.is_none() {\n        error(&mut errors, &f.sig, \"task functions must not have `where` clauses\");\n    }\n    if !f.sig.abi.is_none() {\n        error(&mut errors, &f.sig, \"task functions must not have an ABI qualifier\");\n    }\n    if !f.sig.variadic.is_none() {\n        error(&mut errors, &f.sig, \"task functions must not be variadic\");\n    }\n    if f.sig.asyncness.is_some() {\n        match &f.sig.output {\n            ReturnType::Default => {}\n            ReturnType::Type(_, ty) => match &**ty {\n                Type::Tuple(tuple) if tuple.elems.is_empty() => {}\n                Type::Never(_) => {}\n                _ => error(\n                    &mut errors,\n                    &f.sig,\n                    \"task functions must either not return a value, return `()` or return `!`\",\n                ),\n            },\n        }\n    }\n\n    let mut args = Vec::new();\n    let mut fargs = f.sig.inputs.clone();\n\n    for arg in fargs.iter_mut() {\n        match arg {\n            syn::FnArg::Receiver(_) => {\n                error(&mut errors, arg, \"task functions must not have `self` arguments\");\n            }\n            syn::FnArg::Typed(t) => {\n                check_arg_ty(&mut errors, &t.ty);\n                match t.pat.as_mut() {\n                    syn::Pat::Ident(id) => {\n                        id.mutability = None;\n                        args.push((id.clone(), t.attrs.clone()));\n                    }\n                    _ => {\n                        error(\n                            &mut errors,\n                            arg,\n                            \"pattern matching in task arguments is not yet supported\",\n                        );\n                    }\n                }\n            }\n        }\n    }\n\n    // Copy the generics + where clause to avoid more spurious errors.\n    let generics = &f.sig.generics;\n    let where_clause = &f.sig.generics.where_clause;\n    let unsafety = &f.sig.unsafety;\n    let visibility = &f.vis;\n\n    // assemble the original input arguments,\n    // including any attributes that may have\n    // been applied previously\n    let mut full_args = Vec::new();\n    for (arg, cfgs) in args {\n        full_args.push(quote!(\n            #(#cfgs)*\n            #arg\n        ));\n    }\n\n    let task_ident = f.sig.ident.clone();\n    let task_inner_ident = format_ident!(\"__{}_task\", task_ident);\n\n    let task_inner_future_output = match &f.sig.output {\n        ReturnType::Default => quote! {-> impl ::core::future::Future<Output = ()>},\n        // Special case the never type since we can't stuff it into a `impl Future<Output = !>`\n        ReturnType::Type(arrow, maybe_never)\n            if f.sig.asyncness.is_some() && matches!(**maybe_never, Type::Never(_)) =>\n        {\n            quote! {\n                #arrow impl ::core::future::Future<Output=#embassy_executor::_export::Never>\n            }\n        }\n        ReturnType::Type(arrow, maybe_never) if matches!(**maybe_never, Type::Never(_)) => quote! {\n            #arrow #maybe_never\n        },\n        // Grab the arrow span, why not\n        ReturnType::Type(arrow, typ) if f.sig.asyncness.is_some() => quote! {\n            #arrow impl ::core::future::Future<Output = #typ>\n        },\n        // We assume that if `f` isn't async, it must return `-> impl Future<...>`\n        // This is checked using traits later\n        ReturnType::Type(arrow, typ) => quote! {\n            #arrow #typ\n        },\n    };\n\n    // We have to rename the function since it might be recursive;\n    let mut task_inner_function = f.clone();\n    let task_inner_function_ident = format_ident!(\"__{}_task_inner_function\", task_ident);\n    task_inner_function.sig.ident = task_inner_function_ident.clone();\n    task_inner_function.vis = Visibility::Inherited;\n\n    let task_inner_body = if errors.is_empty() {\n        quote! {\n            #task_inner_function\n\n            // SAFETY: All the preconditions to `#task_ident` apply to\n            //         all contexts `#task_inner_ident` is called in\n            #unsafety {\n                #task_inner_function_ident(#(#full_args,)*)\n            }\n        }\n    } else {\n        quote! {\n            async {::core::todo!()}\n        }\n    };\n\n    let task_inner = quote! {\n        #visibility fn #task_inner_ident #generics (#fargs)\n        #task_inner_future_output\n        #where_clause\n        {\n            #task_inner_body\n        }\n    };\n\n    let spawn = if returns_impl_trait {\n        quote!(spawn)\n    } else {\n        quote!(_spawn_async_fn)\n    };\n\n    #[cfg(feature = \"nightly\")]\n    let mut task_outer_body = quote! {\n        trait _EmbassyInternalTaskTrait {\n            type Fut: ::core::future::Future<Output: #embassy_executor::_export::TaskReturnValue> + 'static;\n            fn construct(#fargs) -> Self::Fut;\n        }\n\n        impl _EmbassyInternalTaskTrait for () {\n            type Fut = impl core::future::Future<Output: #embassy_executor::_export::TaskReturnValue> + 'static;\n            fn construct(#fargs) -> Self::Fut {\n                #task_inner_ident(#(#full_args,)*)\n            }\n        }\n\n        const POOL_SIZE: usize = #pool_size;\n        static POOL: #embassy_executor::raw::TaskPool<<() as _EmbassyInternalTaskTrait>::Fut, POOL_SIZE> = #embassy_executor::raw::TaskPool::new();\n        unsafe { POOL.#spawn(move || <() as _EmbassyInternalTaskTrait>::construct(#(#full_args,)*)) }\n    };\n    #[cfg(not(feature = \"nightly\"))]\n    let mut task_outer_body = quote! {\n        const fn __task_pool_get<F, Args, Fut>(_: F) -> &'static #embassy_executor::raw::TaskPool<Fut, POOL_SIZE>\n        where\n            F: #embassy_executor::_export::TaskFn<Args, Fut = Fut>,\n            Fut: ::core::future::Future + 'static,\n        {\n            unsafe { &*POOL.get().cast() }\n        }\n\n        const POOL_SIZE: usize = #pool_size;\n        static POOL: #embassy_executor::_export::TaskPoolHolder<\n            {#embassy_executor::_export::task_pool_size::<_, _, _, POOL_SIZE>(#task_inner_ident)},\n            {#embassy_executor::_export::task_pool_align::<_, _, _, POOL_SIZE>(#task_inner_ident)},\n        > = unsafe { ::core::mem::transmute(#embassy_executor::_export::task_pool_new::<_, _, _, POOL_SIZE>(#task_inner_ident)) };\n        unsafe { __task_pool_get(#task_inner_ident).#spawn(move || #task_inner_ident(#(#full_args,)*)) }\n    };\n\n    let task_outer_attrs = &f.attrs;\n\n    if !errors.is_empty() {\n        task_outer_body = quote! {\n            #![allow(unused_variables, unreachable_code)]\n            let _x: ::core::result::Result<#embassy_executor::SpawnToken<()>, #embassy_executor::SpawnError> = ::core::todo!();\n            _x\n        };\n    }\n\n    let result = quote! {\n        // This is the user's task function, renamed.\n        // We put it outside the #task_ident fn below, because otherwise\n        // the items defined there (such as POOL) would be in scope\n        // in the user's code.\n        #[doc(hidden)]\n        #task_inner\n\n        #(#task_outer_attrs)*\n        #visibility #unsafety fn #task_ident #generics (#fargs) -> ::core::result::Result<#embassy_executor::SpawnToken<impl Sized>, #embassy_executor::SpawnError> #where_clause{\n            #task_outer_body\n        }\n\n        #errors\n    };\n\n    result\n}\n\nfn check_arg_ty(errors: &mut TokenStream, ty: &Type) {\n    struct Visitor<'a> {\n        errors: &'a mut TokenStream,\n    }\n\n    impl<'a, 'ast> Visit<'ast> for Visitor<'a> {\n        fn visit_type_reference(&mut self, i: &'ast syn::TypeReference) {\n            // Only check for elided lifetime here. If not elided, it's checked by `visit_lifetime`.\n            if i.lifetime.is_none() {\n                error(\n                    self.errors,\n                    i.and_token,\n                    \"Arguments for tasks must live forever. Try using the `'static` lifetime.\",\n                )\n            }\n            visit::visit_type_reference(self, i);\n        }\n\n        fn visit_lifetime(&mut self, i: &'ast syn::Lifetime) {\n            if i.ident.to_string() != \"static\" {\n                error(\n                    self.errors,\n                    i,\n                    \"Arguments for tasks must live forever. Try using the `'static` lifetime.\",\n                )\n            }\n        }\n\n        fn visit_type_impl_trait(&mut self, i: &'ast syn::TypeImplTrait) {\n            error(\n                self.errors,\n                i,\n                \"`impl Trait` is not allowed in task arguments. It is syntax sugar for generics, and tasks can't be generic.\",\n            );\n        }\n    }\n\n    Visit::visit_type(&mut Visitor { errors }, ty);\n}\n"
  },
  {
    "path": "embassy-executor-macros/src/util.rs",
    "content": "use std::fmt::Display;\n\nuse proc_macro2::{TokenStream, TokenTree};\nuse quote::{ToTokens, TokenStreamExt};\nuse syn::parse::{Parse, ParseStream};\nuse syn::{AttrStyle, Attribute, Signature, Token, Visibility, braced, bracketed, token};\n\npub fn token_stream_with_error(mut tokens: TokenStream, error: syn::Error) -> TokenStream {\n    tokens.extend(error.into_compile_error());\n    tokens\n}\n\npub fn error<A: ToTokens, T: Display>(s: &mut TokenStream, obj: A, msg: T) {\n    s.extend(syn::Error::new_spanned(obj.into_token_stream(), msg).into_compile_error())\n}\n\n/// Function signature and body.\n///\n/// Same as `syn`'s `ItemFn` except we keep the body as a TokenStream instead of\n/// parsing it. This makes the macro not error if there's a syntax error in the body,\n/// which helps IDE autocomplete work better.\n#[derive(Debug, Clone)]\npub struct ItemFn {\n    pub attrs: Vec<Attribute>,\n    pub vis: Visibility,\n    pub sig: Signature,\n    pub brace_token: token::Brace,\n    pub body: TokenStream,\n}\n\nimpl Parse for ItemFn {\n    fn parse(input: ParseStream) -> syn::Result<Self> {\n        let mut attrs = input.call(Attribute::parse_outer)?;\n        let vis: Visibility = input.parse()?;\n        let sig: Signature = input.parse()?;\n\n        let content;\n        let brace_token = braced!(content in input);\n        while content.peek(Token![#]) && content.peek2(Token![!]) {\n            let content2;\n            attrs.push(Attribute {\n                pound_token: content.parse()?,\n                style: AttrStyle::Inner(content.parse()?),\n                bracket_token: bracketed!(content2 in content),\n                meta: content2.parse()?,\n            });\n        }\n\n        let mut body = Vec::new();\n        while !content.is_empty() {\n            body.push(content.parse::<TokenTree>()?);\n        }\n        let body = body.into_iter().collect();\n\n        Ok(ItemFn {\n            attrs,\n            vis,\n            sig,\n            brace_token,\n            body,\n        })\n    }\n}\n\nimpl ToTokens for ItemFn {\n    fn to_tokens(&self, tokens: &mut TokenStream) {\n        tokens.append_all(self.attrs.iter().filter(|a| matches!(a.style, AttrStyle::Outer)));\n        self.vis.to_tokens(tokens);\n        self.sig.to_tokens(tokens);\n        self.brace_token.surround(tokens, |tokens| {\n            tokens.append_all(self.body.clone());\n        });\n    }\n}\n"
  },
  {
    "path": "embassy-executor-timer-queue/CHANGELOG.md",
    "content": "# Changelog for embassy-executor-timer-queue\n\nAll notable changes to this project will be documented in this file.\n\nThe format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),\nand this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).\n\n## 0.1.0 - 2025-08-25\n\n- Initial implementation\n"
  },
  {
    "path": "embassy-executor-timer-queue/Cargo.toml",
    "content": "[package]\nname = \"embassy-executor-timer-queue\"\nversion = \"0.1.0\"\nedition = \"2024\"\ndescription = \"Timer queue item and interface between embassy-executor and timer queues\"\nrepository = \"https://github.com/embassy-rs/embassy\"\ndocumentation = \"https://docs.embassy.dev/embassy-executor-timer-queue\"\nreadme = \"README.md\"\nlicense = \"MIT OR Apache-2.0\"\ncategories = [\n    \"embedded\",\n    \"no-std\",\n    \"concurrency\",\n    \"asynchronous\",\n]\n\n[dependencies]\n\n[features]\n#! ### Timer Queue Item Size\n#! Sets the size of the timer items.\n\n## 4 words\ntimer-item-size-4-words = []\n\n## 6 words\ntimer-item-size-6-words = []\n\n## 8 words\ntimer-item-size-8-words = []\n\n[package.metadata.embassy_docs]\nsrc_base = \"https://github.com/embassy-rs/embassy/blob/embassy-executor-timer-queue-v$VERSION/embassy-executor-timer-queue/src/\"\nsrc_base_git = \"https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-executor-timer-queue/src/\"\ntarget = \"x86_64-unknown-linux-gnu\"\n\n[package.metadata.embassy]\nbuild = [\n    {target = \"thumbv7em-none-eabi\", features = []},\n    {target = \"thumbv6m-none-eabi\", features = []},\n]\n"
  },
  {
    "path": "embassy-executor-timer-queue/README.md",
    "content": "# embassy-executor-time-queue\n\nThis crate defines the timer queue item that embassy-executor provides, and a way to access it, for\nexecutor-integrated timer queues. The crate decouples the release cycle of embassy-executor from\nthat of the queue implementations'.\n\nAs a HAL implementer, you only need to depend on this crate if you want to implement executor-integrated\ntimer queues yourself, without using [`embassy-time-queue-utils`](https://crates.io/crates/embassy-time-queue-utils).\n\nAs a HAL user, you should not need to depend on this crate.\n"
  },
  {
    "path": "embassy-executor-timer-queue/src/lib.rs",
    "content": "//! Timer queue item for embassy-executor integrated timer queues\n//!\n//! `embassy-executor` provides the memory needed to implement integrated timer queues. This crate\n//! exists to separate that memory from `embassy-executor` itself, to decouple the timer queue's\n//! release cycle from `embassy-executor`.\n//!\n//! This crate contains two things:\n//! - [`TimerQueueItem`]: The item type that can be requested from the executor. The size of this\n//!   type can be configured using the `timer-item-size-N-words` Cargo features.\n//! - The expectation that `extern \"Rust\" fn __embassy_time_queue_item_from_waker(waker: &Waker) -> &mut TimerQueueItem`\n//!   is implemented (by `embassy-executor`, most likely). This function must return a mutable\n//!   reference to the `TimerQueueItem` associated with the given waker.\n//!\n//! As a queue implementor, you will need to choose one of the `timer-item-size-N-words` features to\n//! select a queue item size. You can then define your own item type, which must be\n//! `#[repr(align(8))]` (or less) and must fit into the size you selected.\n//!\n//! You can access the `TimerQueueItem` from a `Waker` using the [`from_embassy_waker`](TimerQueueItem::from_embassy_waker)\n//! method. You can then use the [`as_ref`](TimerQueueItem::as_ref) and [`as_mut`](TimerQueueItem::as_mut)\n//! methods to reinterpret the data stored in the item as your custom item type.\n#![no_std]\n\nuse core::task::Waker;\n\nconst ITEM_WORDS: usize = if cfg!(feature = \"timer-item-size-8-words\") {\n    8\n} else if cfg!(feature = \"timer-item-size-6-words\") {\n    6\n} else if cfg!(feature = \"timer-item-size-4-words\") {\n    4\n} else {\n    0\n};\n\n/// The timer queue item provided by the executor.\n///\n/// This type is opaque, it only provides the raw storage for a queue item. The queue implementation\n/// is responsible for reinterpreting the contents of the item using [`TimerQueueItem::as_ref`] and\n/// [`TimerQueueItem::as_mut`].\n#[repr(align(8))]\npub struct TimerQueueItem {\n    data: [usize; ITEM_WORDS],\n}\n\nimpl TimerQueueItem {\n    /// Creates a new, zero-initialized `TimerQueueItem`.\n    pub const fn new() -> Self {\n        Self { data: [0; ITEM_WORDS] }\n    }\n\n    /// Retrieves the `TimerQueueItem` reference that belongs to the task of the waker.\n    ///\n    /// Panics if called with a non-embassy waker.\n    ///\n    /// # Safety\n    ///\n    /// The caller must ensure they are not violating Rust's aliasing rules - it is not allowed\n    /// to use this method to create multiple mutable references to the same `TimerQueueItem` at\n    /// the same time.\n    ///\n    /// This function must only be called in the context of a timer queue implementation.\n    pub unsafe fn from_embassy_waker(waker: &Waker) -> &'static mut Self {\n        unsafe extern \"Rust\" {\n            // Waker -> TimerQueueItem, validates that Waker is an embassy Waker.\n            fn __embassy_time_queue_item_from_waker(waker: &Waker) -> &'static mut TimerQueueItem;\n        }\n        unsafe { __embassy_time_queue_item_from_waker(waker) }\n    }\n\n    /// Access the data as a reference to a type `T`.\n    ///\n    /// Safety:\n    ///\n    /// - The type must be valid when zero-initialized.\n    /// - The timer queue should only be interpreted as a single type `T` during its lifetime.\n    pub unsafe fn as_ref<T>(&self) -> &T {\n        const { validate::<T>() }\n        unsafe { &*(self.data.as_ptr() as *const T) }\n    }\n\n    /// Access the data as a reference to a type `T`.\n    ///\n    /// Safety:\n    ///\n    /// - The type must be valid when zero-initialized.\n    /// - The timer queue should only be interpreted as a single type `T` during its lifetime.\n    pub unsafe fn as_mut<T>(&self) -> &mut T {\n        const { validate::<T>() }\n        unsafe { &mut *(self.data.as_ptr() as *mut T) }\n    }\n}\n\nconst fn validate<T>() {\n    const {\n        assert!(\n            core::mem::size_of::<TimerQueueItem>() >= core::mem::size_of::<T>(),\n            \"embassy-executor-timer-queue item size is smaller than the requested type. Select a larger timer-item-size-N-words feature.\"\n        );\n        assert!(\n            core::mem::align_of::<TimerQueueItem>() >= core::mem::align_of::<T>(),\n            \"the alignment of the requested type is greater than 8\"\n        );\n    }\n}\n"
  },
  {
    "path": "embassy-futures/CHANGELOG.md",
    "content": "# Changelog for embassy-futures\n\nAll notable changes to this project will be documented in this file.\n\nThe format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),\nand this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).\n\n<!-- next-header -->\n## Unreleased - ReleaseDate\n\n## 0.1.2 - 2025-08-26\n\n- Preserve location information for `defmt` in `fmt` calls ([#3085](https://github.com/embassy-rs/embassy/pull/3085))\n- Fixed soundness issue in `select_slice` ([#3328](https://github.com/embassy-rs/embassy/pull/3328))\n- Added `select5` and `select6` ([#3430](https://github.com/embassy-rs/embassy/pull/3430))\n- Added `is_x` methods for all `EitherN` enum variants (#[3650](https://github.com/embassy-rs/embassy/pull/3650))\n"
  },
  {
    "path": "embassy-futures/Cargo.toml",
    "content": "[package]\nname = \"embassy-futures\"\nversion = \"0.1.2\"\nedition = \"2024\"\ndescription = \"no-std, no-alloc utilities for working with futures\"\nrepository = \"https://github.com/embassy-rs/embassy\"\ndocumentation = \"https://docs.embassy.dev/embassy-futures\"\nreadme = \"README.md\"\nlicense = \"MIT OR Apache-2.0\"\ncategories = [\n    \"embedded\",\n    \"no-std\",\n    \"concurrency\",\n    \"asynchronous\",\n]\n\n[package.metadata.embassy_docs]\nsrc_base = \"https://github.com/embassy-rs/embassy/blob/embassy-futures-v$VERSION/embassy-futures/src/\"\nsrc_base_git = \"https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-futures/src/\"\nfeatures = [\"defmt\"]\ntarget = \"thumbv7em-none-eabi\"\n\n[package.metadata.docs.rs]\nfeatures = [\"defmt\"]\n\n[dependencies]\ndefmt = { version = \"1.0.1\", optional = true }\nlog = { version = \"0.4.14\", optional = true }\n\n[features]\ndefmt = [\"dep:defmt\"]\nlog = [\"dep:log\"]\n"
  },
  {
    "path": "embassy-futures/README.md",
    "content": "# embassy-futures\n\nAn [Embassy](https://embassy.dev) project.\n\nUtilities for working with futures, compatible with `no_std` and not using `alloc`. Optimized for code size,\nideal for embedded systems.\n\n- Future combinators, like [`join`](join) and [`select`](select)\n- Utilities to use `async` without a fully fledged executor: [`block_on`](block_on::block_on) and [`yield_now`](yield_now::yield_now).\n\n## Interoperability\n\nFutures from this crate can run on any executor.\n"
  },
  {
    "path": "embassy-futures/src/block_on.rs",
    "content": "use core::future::Future;\nuse core::pin::Pin;\nuse core::ptr;\nuse core::task::{Context, Poll, RawWaker, RawWakerVTable, Waker};\n\nstatic VTABLE: RawWakerVTable = RawWakerVTable::new(|_| RawWaker::new(ptr::null(), &VTABLE), |_| {}, |_| {}, |_| {});\n\n/// Run a future to completion using a busy loop.\n///\n/// This calls `.poll()` on the future in a busy loop, which blocks\n/// the current thread at 100% cpu usage until the future is done. The\n/// future's `Waker` mechanism is not used.\n///\n/// You can use this to run multiple futures concurrently with [`join`][crate::join].\n///\n/// It's suitable for systems with no or limited concurrency and without\n/// strict requirements around power consumption. For more complex use\n/// cases, prefer using a \"real\" executor like `embassy-executor`, which\n/// supports multiple tasks, and putting the core to sleep when no task\n/// needs to do work.\npub fn block_on<F: Future>(mut fut: F) -> F::Output {\n    // safety: we don't move the future after this line.\n    let mut fut = unsafe { Pin::new_unchecked(&mut fut) };\n\n    let raw_waker = RawWaker::new(ptr::null(), &VTABLE);\n    let waker = unsafe { Waker::from_raw(raw_waker) };\n    let mut cx = Context::from_waker(&waker);\n    loop {\n        if let Poll::Ready(res) = fut.as_mut().poll(&mut cx) {\n            return res;\n        }\n    }\n}\n\n/// Poll a future once.\npub fn poll_once<F: Future>(mut fut: F) -> Poll<F::Output> {\n    // safety: we don't move the future after this line.\n    let mut fut = unsafe { Pin::new_unchecked(&mut fut) };\n\n    let raw_waker = RawWaker::new(ptr::null(), &VTABLE);\n    let waker = unsafe { Waker::from_raw(raw_waker) };\n    let mut cx = Context::from_waker(&waker);\n\n    fut.as_mut().poll(&mut cx)\n}\n"
  },
  {
    "path": "embassy-futures/src/fmt.rs",
    "content": "#![macro_use]\n#![allow(unused)]\n\nuse core::fmt::{Debug, Display, LowerHex};\n\n#[cfg(all(feature = \"defmt\", feature = \"log\"))]\ncompile_error!(\"You may not enable both `defmt` and `log` features.\");\n\n#[collapse_debuginfo(yes)]\nmacro_rules! assert {\n    ($($x:tt)*) => {\n        {\n            #[cfg(not(feature = \"defmt\"))]\n            ::core::assert!($($x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::assert!($($x)*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! assert_eq {\n    ($($x:tt)*) => {\n        {\n            #[cfg(not(feature = \"defmt\"))]\n            ::core::assert_eq!($($x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::assert_eq!($($x)*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! assert_ne {\n    ($($x:tt)*) => {\n        {\n            #[cfg(not(feature = \"defmt\"))]\n            ::core::assert_ne!($($x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::assert_ne!($($x)*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! debug_assert {\n    ($($x:tt)*) => {\n        {\n            #[cfg(not(feature = \"defmt\"))]\n            ::core::debug_assert!($($x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::debug_assert!($($x)*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! debug_assert_eq {\n    ($($x:tt)*) => {\n        {\n            #[cfg(not(feature = \"defmt\"))]\n            ::core::debug_assert_eq!($($x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::debug_assert_eq!($($x)*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! debug_assert_ne {\n    ($($x:tt)*) => {\n        {\n            #[cfg(not(feature = \"defmt\"))]\n            ::core::debug_assert_ne!($($x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::debug_assert_ne!($($x)*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! todo {\n    ($($x:tt)*) => {\n        {\n            #[cfg(not(feature = \"defmt\"))]\n            ::core::todo!($($x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::todo!($($x)*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! unreachable {\n    ($($x:tt)*) => {\n        {\n            #[cfg(not(feature = \"defmt\"))]\n            ::core::unreachable!($($x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::unreachable!($($x)*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! panic {\n    ($($x:tt)*) => {\n        {\n            #[cfg(not(feature = \"defmt\"))]\n            ::core::panic!($($x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::panic!($($x)*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! trace {\n    ($s:literal $(, $x:expr)* $(,)?) => {\n        {\n            #[cfg(feature = \"log\")]\n            ::log::trace!($s $(, $x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::trace!($s $(, $x)*);\n            #[cfg(not(any(feature = \"log\", feature=\"defmt\")))]\n            let _ = ($( & $x ),*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! debug {\n    ($s:literal $(, $x:expr)* $(,)?) => {\n        {\n            #[cfg(feature = \"log\")]\n            ::log::debug!($s $(, $x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::debug!($s $(, $x)*);\n            #[cfg(not(any(feature = \"log\", feature=\"defmt\")))]\n            let _ = ($( & $x ),*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! info {\n    ($s:literal $(, $x:expr)* $(,)?) => {\n        {\n            #[cfg(feature = \"log\")]\n            ::log::info!($s $(, $x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::info!($s $(, $x)*);\n            #[cfg(not(any(feature = \"log\", feature=\"defmt\")))]\n            let _ = ($( & $x ),*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! warn {\n    ($s:literal $(, $x:expr)* $(,)?) => {\n        {\n            #[cfg(feature = \"log\")]\n            ::log::warn!($s $(, $x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::warn!($s $(, $x)*);\n            #[cfg(not(any(feature = \"log\", feature=\"defmt\")))]\n            let _ = ($( & $x ),*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! error {\n    ($s:literal $(, $x:expr)* $(,)?) => {\n        {\n            #[cfg(feature = \"log\")]\n            ::log::error!($s $(, $x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::error!($s $(, $x)*);\n            #[cfg(not(any(feature = \"log\", feature=\"defmt\")))]\n            let _ = ($( & $x ),*);\n        }\n    };\n}\n\n#[cfg(feature = \"defmt\")]\n#[collapse_debuginfo(yes)]\nmacro_rules! unwrap {\n    ($($x:tt)*) => {\n        ::defmt::unwrap!($($x)*)\n    };\n}\n\n#[cfg(not(feature = \"defmt\"))]\n#[collapse_debuginfo(yes)]\nmacro_rules! unwrap {\n    ($arg:expr) => {\n        match $crate::fmt::Try::into_result($arg) {\n            ::core::result::Result::Ok(t) => t,\n            ::core::result::Result::Err(e) => {\n                ::core::panic!(\"unwrap of `{}` failed: {:?}\", ::core::stringify!($arg), e);\n            }\n        }\n    };\n    ($arg:expr, $($msg:expr),+ $(,)? ) => {\n        match $crate::fmt::Try::into_result($arg) {\n            ::core::result::Result::Ok(t) => t,\n            ::core::result::Result::Err(e) => {\n                ::core::panic!(\"unwrap of `{}` failed: {}: {:?}\", ::core::stringify!($arg), ::core::format_args!($($msg,)*), e);\n            }\n        }\n    }\n}\n\n#[derive(Debug, Copy, Clone, Eq, PartialEq)]\npub struct NoneError;\n\npub trait Try {\n    type Ok;\n    type Error;\n    fn into_result(self) -> Result<Self::Ok, Self::Error>;\n}\n\nimpl<T> Try for Option<T> {\n    type Ok = T;\n    type Error = NoneError;\n\n    #[inline]\n    fn into_result(self) -> Result<T, NoneError> {\n        self.ok_or(NoneError)\n    }\n}\n\nimpl<T, E> Try for Result<T, E> {\n    type Ok = T;\n    type Error = E;\n\n    #[inline]\n    fn into_result(self) -> Self {\n        self\n    }\n}\n\npub(crate) struct Bytes<'a>(pub &'a [u8]);\n\nimpl<'a> Debug for Bytes<'a> {\n    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {\n        write!(f, \"{:#02x?}\", self.0)\n    }\n}\n\nimpl<'a> Display for Bytes<'a> {\n    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {\n        write!(f, \"{:#02x?}\", self.0)\n    }\n}\n\nimpl<'a> LowerHex for Bytes<'a> {\n    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {\n        write!(f, \"{:#02x?}\", self.0)\n    }\n}\n\n#[cfg(feature = \"defmt\")]\nimpl<'a> defmt::Format for Bytes<'a> {\n    fn format(&self, fmt: defmt::Formatter) {\n        defmt::write!(fmt, \"{:02x}\", self.0)\n    }\n}\n"
  },
  {
    "path": "embassy-futures/src/join.rs",
    "content": "//! Wait for multiple futures to complete.\n\nuse core::future::Future;\nuse core::mem::MaybeUninit;\nuse core::pin::Pin;\nuse core::task::{Context, Poll};\nuse core::{fmt, mem};\n\n#[derive(Debug)]\nenum MaybeDone<Fut: Future> {\n    /// A not-yet-completed future\n    Future(/* #[pin] */ Fut),\n    /// The output of the completed future\n    Done(Fut::Output),\n    /// The empty variant after the result of a [`MaybeDone`] has been\n    /// taken using the [`take_output`](MaybeDone::take_output) method.\n    Gone,\n}\n\nimpl<Fut: Future> MaybeDone<Fut> {\n    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> bool {\n        let this = unsafe { self.get_unchecked_mut() };\n        match this {\n            Self::Future(fut) => match unsafe { Pin::new_unchecked(fut) }.poll(cx) {\n                Poll::Ready(res) => {\n                    *this = Self::Done(res);\n                    true\n                }\n                Poll::Pending => false,\n            },\n            _ => true,\n        }\n    }\n\n    fn take_output(&mut self) -> Fut::Output {\n        match &*self {\n            Self::Done(_) => {}\n            Self::Future(_) | Self::Gone => panic!(\"take_output when MaybeDone is not done.\"),\n        }\n        match mem::replace(self, Self::Gone) {\n            MaybeDone::Done(output) => output,\n            _ => unreachable!(),\n        }\n    }\n}\n\nimpl<Fut: Future + Unpin> Unpin for MaybeDone<Fut> {}\n\nmacro_rules! generate {\n    ($(\n        $(#[$doc:meta])*\n        ($Join:ident, <$($Fut:ident),*>),\n    )*) => ($(\n        $(#[$doc])*\n        #[must_use = \"futures do nothing unless you `.await` or poll them\"]\n        #[allow(non_snake_case)]\n        pub struct $Join<$($Fut: Future),*> {\n            $(\n                $Fut: MaybeDone<$Fut>,\n            )*\n        }\n\n        impl<$($Fut),*> fmt::Debug for $Join<$($Fut),*>\n        where\n            $(\n                $Fut: Future + fmt::Debug,\n                $Fut::Output: fmt::Debug,\n            )*\n        {\n            fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n                f.debug_struct(stringify!($Join))\n                    $(.field(stringify!($Fut), &self.$Fut))*\n                    .finish()\n            }\n        }\n\n        impl<$($Fut: Future),*> $Join<$($Fut),*> {\n            #[allow(non_snake_case)]\n            fn new($($Fut: $Fut),*) -> Self {\n                Self {\n                    $($Fut: MaybeDone::Future($Fut)),*\n                }\n            }\n        }\n\n        impl<$($Fut: Future),*> Future for $Join<$($Fut),*> {\n            type Output = ($($Fut::Output),*);\n\n            fn poll(\n                self: Pin<&mut Self>, cx: &mut Context<'_>\n            ) -> Poll<Self::Output> {\n                let this = unsafe { self.get_unchecked_mut() };\n                let mut all_done = true;\n                $(\n                    all_done &= unsafe { Pin::new_unchecked(&mut this.$Fut) }.poll(cx);\n                )*\n\n                if all_done {\n                    Poll::Ready(($(this.$Fut.take_output()), *))\n                } else {\n                    Poll::Pending\n                }\n            }\n        }\n    )*)\n}\n\ngenerate! {\n    /// Future for the [`join`](join()) function.\n    (Join, <Fut1, Fut2>),\n\n    /// Future for the [`join3`] function.\n    (Join3, <Fut1, Fut2, Fut3>),\n\n    /// Future for the [`join4`] function.\n    (Join4, <Fut1, Fut2, Fut3, Fut4>),\n\n    /// Future for the [`join5`] function.\n    (Join5, <Fut1, Fut2, Fut3, Fut4, Fut5>),\n}\n\n/// Joins the result of two futures, waiting for them both to complete.\n///\n/// This function will return a new future which awaits both futures to\n/// complete. The returned future will finish with a tuple of both results.\n///\n/// Note that this function consumes the passed futures and returns a\n/// wrapped version of it.\n///\n/// # Examples\n///\n/// ```\n/// # embassy_futures::block_on(async {\n///\n/// let a = async { 1 };\n/// let b = async { 2 };\n/// let pair = embassy_futures::join::join(a, b).await;\n///\n/// assert_eq!(pair, (1, 2));\n/// # });\n/// ```\npub fn join<Fut1, Fut2>(future1: Fut1, future2: Fut2) -> Join<Fut1, Fut2>\nwhere\n    Fut1: Future,\n    Fut2: Future,\n{\n    Join::new(future1, future2)\n}\n\n/// Joins the result of three futures, waiting for them all to complete.\n///\n/// This function will return a new future which awaits all futures to\n/// complete. The returned future will finish with a tuple of all results.\n///\n/// Note that this function consumes the passed futures and returns a\n/// wrapped version of it.\n///\n/// # Examples\n///\n/// ```\n/// # embassy_futures::block_on(async {\n///\n/// let a = async { 1 };\n/// let b = async { 2 };\n/// let c = async { 3 };\n/// let res = embassy_futures::join::join3(a, b, c).await;\n///\n/// assert_eq!(res, (1, 2, 3));\n/// # });\n/// ```\npub fn join3<Fut1, Fut2, Fut3>(future1: Fut1, future2: Fut2, future3: Fut3) -> Join3<Fut1, Fut2, Fut3>\nwhere\n    Fut1: Future,\n    Fut2: Future,\n    Fut3: Future,\n{\n    Join3::new(future1, future2, future3)\n}\n\n/// Joins the result of four futures, waiting for them all to complete.\n///\n/// This function will return a new future which awaits all futures to\n/// complete. The returned future will finish with a tuple of all results.\n///\n/// Note that this function consumes the passed futures and returns a\n/// wrapped version of it.\n///\n/// # Examples\n///\n/// ```\n/// # embassy_futures::block_on(async {\n///\n/// let a = async { 1 };\n/// let b = async { 2 };\n/// let c = async { 3 };\n/// let d = async { 4 };\n/// let res = embassy_futures::join::join4(a, b, c, d).await;\n///\n/// assert_eq!(res, (1, 2, 3, 4));\n/// # });\n/// ```\npub fn join4<Fut1, Fut2, Fut3, Fut4>(\n    future1: Fut1,\n    future2: Fut2,\n    future3: Fut3,\n    future4: Fut4,\n) -> Join4<Fut1, Fut2, Fut3, Fut4>\nwhere\n    Fut1: Future,\n    Fut2: Future,\n    Fut3: Future,\n    Fut4: Future,\n{\n    Join4::new(future1, future2, future3, future4)\n}\n\n/// Joins the result of five futures, waiting for them all to complete.\n///\n/// This function will return a new future which awaits all futures to\n/// complete. The returned future will finish with a tuple of all results.\n///\n/// Note that this function consumes the passed futures and returns a\n/// wrapped version of it.\n///\n/// # Examples\n///\n/// ```\n/// # embassy_futures::block_on(async {\n///\n/// let a = async { 1 };\n/// let b = async { 2 };\n/// let c = async { 3 };\n/// let d = async { 4 };\n/// let e = async { 5 };\n/// let res = embassy_futures::join::join5(a, b, c, d, e).await;\n///\n/// assert_eq!(res, (1, 2, 3, 4, 5));\n/// # });\n/// ```\npub fn join5<Fut1, Fut2, Fut3, Fut4, Fut5>(\n    future1: Fut1,\n    future2: Fut2,\n    future3: Fut3,\n    future4: Fut4,\n    future5: Fut5,\n) -> Join5<Fut1, Fut2, Fut3, Fut4, Fut5>\nwhere\n    Fut1: Future,\n    Fut2: Future,\n    Fut3: Future,\n    Fut4: Future,\n    Fut5: Future,\n{\n    Join5::new(future1, future2, future3, future4, future5)\n}\n\n// =====================================================\n\n/// Future for the [`join_array`] function.\n#[must_use = \"futures do nothing unless you `.await` or poll them\"]\npub struct JoinArray<Fut: Future, const N: usize> {\n    futures: [MaybeDone<Fut>; N],\n}\n\nimpl<Fut: Future, const N: usize> fmt::Debug for JoinArray<Fut, N>\nwhere\n    Fut: Future + fmt::Debug,\n    Fut::Output: fmt::Debug,\n{\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        f.debug_struct(\"JoinArray\").field(\"futures\", &self.futures).finish()\n    }\n}\n\nimpl<Fut: Future, const N: usize> Future for JoinArray<Fut, N> {\n    type Output = [Fut::Output; N];\n    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {\n        let this = unsafe { self.get_unchecked_mut() };\n        let mut all_done = true;\n        for f in this.futures.iter_mut() {\n            all_done &= unsafe { Pin::new_unchecked(f) }.poll(cx);\n        }\n\n        if all_done {\n            let mut array: [MaybeUninit<Fut::Output>; N] = unsafe { MaybeUninit::uninit().assume_init() };\n            for i in 0..N {\n                array[i].write(this.futures[i].take_output());\n            }\n            Poll::Ready(unsafe { (&array as *const _ as *const [Fut::Output; N]).read() })\n        } else {\n            Poll::Pending\n        }\n    }\n}\n\n/// Joins the result of an array of futures, waiting for them all to complete.\n///\n/// This function will return a new future which awaits all futures to\n/// complete. The returned future will finish with a tuple of all results.\n///\n/// Note that this function consumes the passed futures and returns a\n/// wrapped version of it.\n///\n/// # Examples\n///\n/// ```\n/// # embassy_futures::block_on(async {\n///\n/// async fn foo(n: u32) -> u32 { n }\n/// let a = foo(1);\n/// let b = foo(2);\n/// let c = foo(3);\n/// let res = embassy_futures::join::join_array([a, b, c]).await;\n///\n/// assert_eq!(res, [1, 2, 3]);\n/// # });\n/// ```\npub fn join_array<Fut: Future, const N: usize>(futures: [Fut; N]) -> JoinArray<Fut, N> {\n    JoinArray {\n        futures: futures.map(MaybeDone::Future),\n    }\n}\n"
  },
  {
    "path": "embassy-futures/src/lib.rs",
    "content": "#![no_std]\n#![doc = include_str!(\"../README.md\")]\n#![warn(missing_docs)]\n\n// This mod MUST go first, so that the others see its macros.\npub(crate) mod fmt;\n\nmod block_on;\nmod yield_now;\n\npub mod join;\npub mod select;\n\npub use block_on::*;\npub use yield_now::*;\n"
  },
  {
    "path": "embassy-futures/src/select.rs",
    "content": "//! Wait for the first of several futures to complete.\n\nuse core::future::Future;\nuse core::pin::Pin;\nuse core::task::{Context, Poll};\n\n/// Result for [`select`].\n#[derive(Debug, Clone)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub enum Either<A, B> {\n    /// First future finished first.\n    First(A),\n    /// Second future finished first.\n    Second(B),\n}\n\nimpl<A, B> Either<A, B> {\n    /// Did the first future complete first?\n    pub fn is_first(&self) -> bool {\n        matches!(self, Either::First(_))\n    }\n\n    /// Did the second future complete first?\n    pub fn is_second(&self) -> bool {\n        matches!(self, Either::Second(_))\n    }\n}\n\n/// Wait for one of two futures to complete.\n///\n/// This function returns a new future which polls all the futures.\n/// When one of them completes, it will complete with its result value.\n///\n/// The other future is dropped.\npub fn select<A, B>(a: A, b: B) -> Select<A, B>\nwhere\n    A: Future,\n    B: Future,\n{\n    Select { a, b }\n}\n\n/// Future for the [`select`] function.\n#[derive(Debug)]\n#[must_use = \"futures do nothing unless you `.await` or poll them\"]\npub struct Select<A, B> {\n    a: A,\n    b: B,\n}\n\nimpl<A: Unpin, B: Unpin> Unpin for Select<A, B> {}\n\nimpl<A, B> Future for Select<A, B>\nwhere\n    A: Future,\n    B: Future,\n{\n    type Output = Either<A::Output, B::Output>;\n\n    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {\n        let this = unsafe { self.get_unchecked_mut() };\n        let a = unsafe { Pin::new_unchecked(&mut this.a) };\n        let b = unsafe { Pin::new_unchecked(&mut this.b) };\n        if let Poll::Ready(x) = a.poll(cx) {\n            return Poll::Ready(Either::First(x));\n        }\n        if let Poll::Ready(x) = b.poll(cx) {\n            return Poll::Ready(Either::Second(x));\n        }\n        Poll::Pending\n    }\n}\n\n// ====================================================================\n\n/// Result for [`select3`].\n#[derive(Debug, Clone)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub enum Either3<A, B, C> {\n    /// First future finished first.\n    First(A),\n    /// Second future finished first.\n    Second(B),\n    /// Third future finished first.\n    Third(C),\n}\n\nimpl<A, B, C> Either3<A, B, C> {\n    /// Did the first future complete first?\n    pub fn is_first(&self) -> bool {\n        matches!(self, Either3::First(_))\n    }\n\n    /// Did the second future complete first?\n    pub fn is_second(&self) -> bool {\n        matches!(self, Either3::Second(_))\n    }\n\n    /// Did the third future complete first?\n    pub fn is_third(&self) -> bool {\n        matches!(self, Either3::Third(_))\n    }\n}\n\n/// Same as [`select`], but with more futures.\npub fn select3<A, B, C>(a: A, b: B, c: C) -> Select3<A, B, C>\nwhere\n    A: Future,\n    B: Future,\n    C: Future,\n{\n    Select3 { a, b, c }\n}\n\n/// Future for the [`select3`] function.\n#[derive(Debug)]\n#[must_use = \"futures do nothing unless you `.await` or poll them\"]\npub struct Select3<A, B, C> {\n    a: A,\n    b: B,\n    c: C,\n}\n\nimpl<A, B, C> Future for Select3<A, B, C>\nwhere\n    A: Future,\n    B: Future,\n    C: Future,\n{\n    type Output = Either3<A::Output, B::Output, C::Output>;\n\n    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {\n        let this = unsafe { self.get_unchecked_mut() };\n        let a = unsafe { Pin::new_unchecked(&mut this.a) };\n        let b = unsafe { Pin::new_unchecked(&mut this.b) };\n        let c = unsafe { Pin::new_unchecked(&mut this.c) };\n        if let Poll::Ready(x) = a.poll(cx) {\n            return Poll::Ready(Either3::First(x));\n        }\n        if let Poll::Ready(x) = b.poll(cx) {\n            return Poll::Ready(Either3::Second(x));\n        }\n        if let Poll::Ready(x) = c.poll(cx) {\n            return Poll::Ready(Either3::Third(x));\n        }\n        Poll::Pending\n    }\n}\n\n// ====================================================================\n\n/// Result for [`select4`].\n#[derive(Debug, Clone)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub enum Either4<A, B, C, D> {\n    /// First future finished first.\n    First(A),\n    /// Second future finished first.\n    Second(B),\n    /// Third future finished first.\n    Third(C),\n    /// Fourth future finished first.\n    Fourth(D),\n}\n\nimpl<A, B, C, D> Either4<A, B, C, D> {\n    /// Did the first future complete first?\n    pub fn is_first(&self) -> bool {\n        matches!(self, Either4::First(_))\n    }\n\n    /// Did the second future complete first?\n    pub fn is_second(&self) -> bool {\n        matches!(self, Either4::Second(_))\n    }\n\n    /// Did the third future complete first?\n    pub fn is_third(&self) -> bool {\n        matches!(self, Either4::Third(_))\n    }\n\n    /// Did the fourth future complete first?\n    pub fn is_fourth(&self) -> bool {\n        matches!(self, Either4::Fourth(_))\n    }\n}\n\n/// Same as [`select`], but with more futures.\npub fn select4<A, B, C, D>(a: A, b: B, c: C, d: D) -> Select4<A, B, C, D>\nwhere\n    A: Future,\n    B: Future,\n    C: Future,\n    D: Future,\n{\n    Select4 { a, b, c, d }\n}\n\n/// Future for the [`select4`] function.\n#[derive(Debug)]\n#[must_use = \"futures do nothing unless you `.await` or poll them\"]\npub struct Select4<A, B, C, D> {\n    a: A,\n    b: B,\n    c: C,\n    d: D,\n}\n\nimpl<A, B, C, D> Future for Select4<A, B, C, D>\nwhere\n    A: Future,\n    B: Future,\n    C: Future,\n    D: Future,\n{\n    type Output = Either4<A::Output, B::Output, C::Output, D::Output>;\n\n    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {\n        let this = unsafe { self.get_unchecked_mut() };\n        let a = unsafe { Pin::new_unchecked(&mut this.a) };\n        let b = unsafe { Pin::new_unchecked(&mut this.b) };\n        let c = unsafe { Pin::new_unchecked(&mut this.c) };\n        let d = unsafe { Pin::new_unchecked(&mut this.d) };\n        if let Poll::Ready(x) = a.poll(cx) {\n            return Poll::Ready(Either4::First(x));\n        }\n        if let Poll::Ready(x) = b.poll(cx) {\n            return Poll::Ready(Either4::Second(x));\n        }\n        if let Poll::Ready(x) = c.poll(cx) {\n            return Poll::Ready(Either4::Third(x));\n        }\n        if let Poll::Ready(x) = d.poll(cx) {\n            return Poll::Ready(Either4::Fourth(x));\n        }\n        Poll::Pending\n    }\n}\n\n// ====================================================================\n\n/// Result for [`select5`].\n#[derive(Debug, Clone)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub enum Either5<A, B, C, D, E> {\n    /// First future finished first.\n    First(A),\n    /// Second future finished first.\n    Second(B),\n    /// Third future finished first.\n    Third(C),\n    /// Fourth future finished first.\n    Fourth(D),\n    /// Fifth future finished first.\n    Fifth(E),\n}\n\nimpl<A, B, C, D, E> Either5<A, B, C, D, E> {\n    /// Did the first future complete first?\n    pub fn is_first(&self) -> bool {\n        matches!(self, Either5::First(_))\n    }\n\n    /// Did the second future complete first?\n    pub fn is_second(&self) -> bool {\n        matches!(self, Either5::Second(_))\n    }\n\n    /// Did the third future complete first?\n    pub fn is_third(&self) -> bool {\n        matches!(self, Either5::Third(_))\n    }\n\n    /// Did the fourth future complete first?\n    pub fn is_fourth(&self) -> bool {\n        matches!(self, Either5::Fourth(_))\n    }\n\n    /// Did the fifth future complete first?\n    pub fn is_fifth(&self) -> bool {\n        matches!(self, Either5::Fifth(_))\n    }\n}\n\n/// Same as [`select`], but with more futures.\npub fn select5<A, B, C, D, E>(a: A, b: B, c: C, d: D, e: E) -> Select5<A, B, C, D, E>\nwhere\n    A: Future,\n    B: Future,\n    C: Future,\n    D: Future,\n    E: Future,\n{\n    Select5 { a, b, c, d, e }\n}\n\n/// Future for the [`select5`] function.\n#[derive(Debug)]\n#[must_use = \"futures do nothing unless you `.await` or poll them\"]\npub struct Select5<A, B, C, D, E> {\n    a: A,\n    b: B,\n    c: C,\n    d: D,\n    e: E,\n}\n\nimpl<A, B, C, D, E> Future for Select5<A, B, C, D, E>\nwhere\n    A: Future,\n    B: Future,\n    C: Future,\n    D: Future,\n    E: Future,\n{\n    type Output = Either5<A::Output, B::Output, C::Output, D::Output, E::Output>;\n\n    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {\n        let this = unsafe { self.get_unchecked_mut() };\n        let a = unsafe { Pin::new_unchecked(&mut this.a) };\n        let b = unsafe { Pin::new_unchecked(&mut this.b) };\n        let c = unsafe { Pin::new_unchecked(&mut this.c) };\n        let d = unsafe { Pin::new_unchecked(&mut this.d) };\n        let e = unsafe { Pin::new_unchecked(&mut this.e) };\n        if let Poll::Ready(x) = a.poll(cx) {\n            return Poll::Ready(Either5::First(x));\n        }\n        if let Poll::Ready(x) = b.poll(cx) {\n            return Poll::Ready(Either5::Second(x));\n        }\n        if let Poll::Ready(x) = c.poll(cx) {\n            return Poll::Ready(Either5::Third(x));\n        }\n        if let Poll::Ready(x) = d.poll(cx) {\n            return Poll::Ready(Either5::Fourth(x));\n        }\n        if let Poll::Ready(x) = e.poll(cx) {\n            return Poll::Ready(Either5::Fifth(x));\n        }\n        Poll::Pending\n    }\n}\n\n// ====================================================================\n\n/// Result for [`select6`].\n#[derive(Debug, Clone)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub enum Either6<A, B, C, D, E, F> {\n    /// First future finished first.\n    First(A),\n    /// Second future finished first.\n    Second(B),\n    /// Third future finished first.\n    Third(C),\n    /// Fourth future finished first.\n    Fourth(D),\n    /// Fifth future finished first.\n    Fifth(E),\n    /// Sixth future finished first.\n    Sixth(F),\n}\n\nimpl<A, B, C, D, E, F> Either6<A, B, C, D, E, F> {\n    /// Did the first future complete first?\n    pub fn is_first(&self) -> bool {\n        matches!(self, Either6::First(_))\n    }\n\n    /// Did the second future complete first?\n    pub fn is_second(&self) -> bool {\n        matches!(self, Either6::Second(_))\n    }\n\n    /// Did the third future complete first?\n    pub fn is_third(&self) -> bool {\n        matches!(self, Either6::Third(_))\n    }\n\n    /// Did the fourth future complete first?\n    pub fn is_fourth(&self) -> bool {\n        matches!(self, Either6::Fourth(_))\n    }\n\n    /// Did the fifth future complete first?\n    pub fn is_fifth(&self) -> bool {\n        matches!(self, Either6::Fifth(_))\n    }\n\n    /// Did the sixth future complete first?\n    pub fn is_sixth(&self) -> bool {\n        matches!(self, Either6::Sixth(_))\n    }\n}\n\n/// Same as [`select`], but with more futures.\npub fn select6<A, B, C, D, E, F>(a: A, b: B, c: C, d: D, e: E, f: F) -> Select6<A, B, C, D, E, F>\nwhere\n    A: Future,\n    B: Future,\n    C: Future,\n    D: Future,\n    E: Future,\n    F: Future,\n{\n    Select6 { a, b, c, d, e, f }\n}\n\n/// Future for the [`select6`] function.\n#[derive(Debug)]\n#[must_use = \"futures do nothing unless you `.await` or poll them\"]\npub struct Select6<A, B, C, D, E, F> {\n    a: A,\n    b: B,\n    c: C,\n    d: D,\n    e: E,\n    f: F,\n}\n\nimpl<A, B, C, D, E, F> Future for Select6<A, B, C, D, E, F>\nwhere\n    A: Future,\n    B: Future,\n    C: Future,\n    D: Future,\n    E: Future,\n    F: Future,\n{\n    type Output = Either6<A::Output, B::Output, C::Output, D::Output, E::Output, F::Output>;\n\n    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {\n        let this = unsafe { self.get_unchecked_mut() };\n        let a = unsafe { Pin::new_unchecked(&mut this.a) };\n        let b = unsafe { Pin::new_unchecked(&mut this.b) };\n        let c = unsafe { Pin::new_unchecked(&mut this.c) };\n        let d = unsafe { Pin::new_unchecked(&mut this.d) };\n        let e = unsafe { Pin::new_unchecked(&mut this.e) };\n        let f = unsafe { Pin::new_unchecked(&mut this.f) };\n        if let Poll::Ready(x) = a.poll(cx) {\n            return Poll::Ready(Either6::First(x));\n        }\n        if let Poll::Ready(x) = b.poll(cx) {\n            return Poll::Ready(Either6::Second(x));\n        }\n        if let Poll::Ready(x) = c.poll(cx) {\n            return Poll::Ready(Either6::Third(x));\n        }\n        if let Poll::Ready(x) = d.poll(cx) {\n            return Poll::Ready(Either6::Fourth(x));\n        }\n        if let Poll::Ready(x) = e.poll(cx) {\n            return Poll::Ready(Either6::Fifth(x));\n        }\n        if let Poll::Ready(x) = f.poll(cx) {\n            return Poll::Ready(Either6::Sixth(x));\n        }\n        Poll::Pending\n    }\n}\n\n// ====================================================================\n\n/// Future for the [`select_array`] function.\n#[derive(Debug)]\n#[must_use = \"futures do nothing unless you `.await` or poll them\"]\npub struct SelectArray<Fut, const N: usize> {\n    inner: [Fut; N],\n}\n\n/// Creates a new future which will select over an array of futures.\n///\n/// The returned future will wait for any future to be ready. Upon\n/// completion the item resolved will be returned, along with the index of the\n/// future that was ready.\n///\n/// If the array is empty, the resulting future will be Pending forever.\npub fn select_array<Fut: Future, const N: usize>(arr: [Fut; N]) -> SelectArray<Fut, N> {\n    SelectArray { inner: arr }\n}\n\nimpl<Fut: Future, const N: usize> Future for SelectArray<Fut, N> {\n    type Output = (Fut::Output, usize);\n\n    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {\n        // Safety: Since `self` is pinned, `inner` cannot move. Since `inner` cannot move,\n        // its elements also cannot move. Therefore it is safe to access `inner` and pin\n        // references to the contained futures.\n        let item = unsafe {\n            self.get_unchecked_mut()\n                .inner\n                .iter_mut()\n                .enumerate()\n                .find_map(|(i, f)| match Pin::new_unchecked(f).poll(cx) {\n                    Poll::Pending => None,\n                    Poll::Ready(e) => Some((i, e)),\n                })\n        };\n\n        match item {\n            Some((idx, res)) => Poll::Ready((res, idx)),\n            None => Poll::Pending,\n        }\n    }\n}\n\n// ====================================================================\n\n/// Future for the [`select_slice`] function.\n#[derive(Debug)]\n#[must_use = \"futures do nothing unless you `.await` or poll them\"]\npub struct SelectSlice<'a, Fut> {\n    inner: Pin<&'a mut [Fut]>,\n}\n\n/// Creates a new future which will select over a slice of futures.\n///\n/// The returned future will wait for any future to be ready. Upon\n/// completion the item resolved will be returned, along with the index of the\n/// future that was ready.\n///\n/// If the slice is empty, the resulting future will be Pending forever.\npub fn select_slice<'a, Fut: Future>(slice: Pin<&'a mut [Fut]>) -> SelectSlice<'a, Fut> {\n    SelectSlice { inner: slice }\n}\n\nimpl<'a, Fut: Future> Future for SelectSlice<'a, Fut> {\n    type Output = (Fut::Output, usize);\n\n    fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {\n        // Safety: refer to\n        //   https://users.rust-lang.org/t/working-with-pinned-slices-are-there-any-structurally-pinning-vec-like-collection-types/50634/2\n        #[inline(always)]\n        fn pin_iter<T>(slice: Pin<&mut [T]>) -> impl Iterator<Item = Pin<&mut T>> {\n            unsafe { slice.get_unchecked_mut().iter_mut().map(|v| Pin::new_unchecked(v)) }\n        }\n        for (i, fut) in pin_iter(self.inner.as_mut()).enumerate() {\n            if let Poll::Ready(res) = fut.poll(cx) {\n                return Poll::Ready((res, i));\n            }\n        }\n\n        Poll::Pending\n    }\n}\n"
  },
  {
    "path": "embassy-futures/src/yield_now.rs",
    "content": "use core::future::Future;\nuse core::pin::Pin;\nuse core::task::{Context, Poll};\n\n/// Yield from the current task once, allowing other tasks to run.\n///\n/// This can be used to easily and quickly implement simple async primitives\n/// without using wakers. The following snippet will wait for a condition to\n/// hold, while still allowing other tasks to run concurrently (not monopolizing\n/// the executor thread).\n///\n/// ```rust\n/// # use embassy_futures::{block_on, yield_now};\n/// # async fn test_fn() {\n/// # let mut iter_count: u32 = 0;\n/// # let mut some_condition = || { iter_count += 1; iter_count > 10 };\n/// while !some_condition() {\n///     yield_now().await;\n/// }\n/// # }\n/// # block_on(test_fn());\n/// ```\n///\n/// The downside is this will spin in a busy loop, using 100% of the CPU, while\n/// using wakers correctly would allow the CPU to sleep while waiting.\n///\n/// The internal implementation is: on first poll the future wakes itself and\n/// returns `Poll::Pending`. On second poll, it returns `Poll::Ready`.\npub fn yield_now() -> impl Future<Output = ()> {\n    YieldNowFuture { yielded: false }\n}\n\n#[must_use = \"futures do nothing unless you `.await` or poll them\"]\nstruct YieldNowFuture {\n    yielded: bool,\n}\n\nimpl Future for YieldNowFuture {\n    type Output = ();\n    fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {\n        if self.yielded {\n            Poll::Ready(())\n        } else {\n            self.yielded = true;\n            cx.waker().wake_by_ref();\n            Poll::Pending\n        }\n    }\n}\n"
  },
  {
    "path": "embassy-hal-internal/CHANGELOG.md",
    "content": "# Changelog\n\nAll notable changes to this project will be documented in this file.\n\nThe format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),\nand this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).\n\n<!-- next-header -->\n## Unreleased - ReleaseDate\n\n## 0.5.0 - 2026-03-20\n\n- Make peripheral steal() const\n\n## 0.4.0 - 2026-01-04\n\n- Bump rust edition\n- Add function to return the number of available items\n\n## 0.1.1 - 2025-08-15\n\n- First release with changelog.\n"
  },
  {
    "path": "embassy-hal-internal/Cargo.toml",
    "content": "[package]\nname = \"embassy-hal-internal\"\nversion = \"0.5.0\"\nedition = \"2024\"\nlicense = \"MIT OR Apache-2.0\"\ndescription = \"Internal implementation details for Embassy HALs. DO NOT USE DIRECTLY.\"\nrepository = \"https://github.com/embassy-rs/embassy\"\ndocumentation = \"https://docs.embassy.dev/embassy-hal-internal\"\ncategories = [\n    \"embedded\",\n    \"no-std\",\n    \"asynchronous\",\n]\n\n[features]\n\ndefmt = [\"dep:defmt\"]\nlog = [\"dep:log\"]\n\n# Define the number of NVIC priority bits.\nprio-bits-0 = []\nprio-bits-1 = []\nprio-bits-2 = []\nprio-bits-3 = []\nprio-bits-4 = []\nprio-bits-5 = []\nprio-bits-6 = []\nprio-bits-7 = []\nprio-bits-8 = []\n\ncortex-m = [\"dep:cortex-m\", \"dep:critical-section\"]\n\n[dependencies]\ndefmt = { version = \"1.0.1\", optional = true }\nlog = { version = \"0.4.14\", optional = true }\n\nnum-traits = { version = \"0.2.14\", default-features = false }\n\ncortex-m = { version = \"0.7.6\", optional = true }\ncritical-section = { version = \"1\", optional = true }\n"
  },
  {
    "path": "embassy-hal-internal/README.md",
    "content": "# embassy-hal-internal\n\nAn [Embassy](https://embassy.dev) project.\n\nInternal implementation details for Embassy HALs. DO NOT USE DIRECTLY. Embassy HALs (`embassy-nrf`, `embassy-stm32`, `embassy-rp`) already reexport\neverything you need to use them effectively.\n"
  },
  {
    "path": "embassy-hal-internal/build.rs",
    "content": "#[path = \"./build_common.rs\"]\nmod common;\n\nfn main() {\n    let mut cfgs = common::CfgSet::new();\n    common::set_target_cfgs(&mut cfgs);\n}\n"
  },
  {
    "path": "embassy-hal-internal/build_common.rs",
    "content": "// NOTE: this file is copy-pasted between several Embassy crates, because there is no\n// straightforward way to share this code:\n// - it cannot be placed into the root of the repo and linked from each build.rs using `#[path =\n// \"../build_common.rs\"]`, because `cargo publish` requires that all files published with a crate\n// reside in the crate's directory,\n// - it cannot be symlinked from `embassy-xxx/build_common.rs` to `../build_common.rs`, because\n// symlinks don't work on Windows.\n\nuse std::collections::HashSet;\nuse std::env;\n\n/// Helper for emitting cargo instruction for enabling configs (`cargo:rustc-cfg=X`) and declaring\n/// them (`cargo:rust-check-cfg=cfg(X)`).\n#[derive(Debug)]\npub struct CfgSet {\n    enabled: HashSet<String>,\n    declared: HashSet<String>,\n}\n\nimpl CfgSet {\n    pub fn new() -> Self {\n        Self {\n            enabled: HashSet::new(),\n            declared: HashSet::new(),\n        }\n    }\n\n    /// Enable a config, which can then be used in `#[cfg(...)]` for conditional compilation.\n    ///\n    /// All configs that can potentially be enabled should be unconditionally declared using\n    /// [`Self::declare()`].\n    pub fn enable(&mut self, cfg: impl AsRef<str>) {\n        if self.enabled.insert(cfg.as_ref().to_owned()) {\n            println!(\"cargo:rustc-cfg={}\", cfg.as_ref());\n        }\n    }\n\n    pub fn enable_all(&mut self, cfgs: &[impl AsRef<str>]) {\n        for cfg in cfgs.iter() {\n            self.enable(cfg.as_ref());\n        }\n    }\n\n    /// Declare a valid config for conditional compilation, without enabling it.\n    ///\n    /// This enables rustc to check that the configs in `#[cfg(...)]` attributes are valid.\n    pub fn declare(&mut self, cfg: impl AsRef<str>) {\n        if self.declared.insert(cfg.as_ref().to_owned()) {\n            println!(\"cargo:rustc-check-cfg=cfg({})\", cfg.as_ref());\n        }\n    }\n\n    pub fn declare_all(&mut self, cfgs: &[impl AsRef<str>]) {\n        for cfg in cfgs.iter() {\n            self.declare(cfg.as_ref());\n        }\n    }\n\n    pub fn set(&mut self, cfg: impl Into<String>, enable: bool) {\n        let cfg = cfg.into();\n        if enable {\n            self.enable(cfg.clone());\n        }\n        self.declare(cfg);\n    }\n}\n\n/// Sets configs that describe the target platform.\npub fn set_target_cfgs(cfgs: &mut CfgSet) {\n    let target = env::var(\"TARGET\").unwrap();\n\n    if target.starts_with(\"thumbv6m-\") {\n        cfgs.enable_all(&[\"cortex_m\", \"armv6m\"]);\n    } else if target.starts_with(\"thumbv7m-\") {\n        cfgs.enable_all(&[\"cortex_m\", \"armv7m\"]);\n    } else if target.starts_with(\"thumbv7em-\") {\n        cfgs.enable_all(&[\"cortex_m\", \"armv7m\", \"armv7em\"]);\n    } else if target.starts_with(\"thumbv8m.base\") {\n        cfgs.enable_all(&[\"cortex_m\", \"armv8m\", \"armv8m_base\"]);\n    } else if target.starts_with(\"thumbv8m.main\") {\n        cfgs.enable_all(&[\"cortex_m\", \"armv8m\", \"armv8m_main\"]);\n    }\n    cfgs.declare_all(&[\n        \"cortex_m\",\n        \"armv6m\",\n        \"armv7m\",\n        \"armv7em\",\n        \"armv8m\",\n        \"armv8m_base\",\n        \"armv8m_main\",\n    ]);\n\n    cfgs.set(\"has_fpu\", target.ends_with(\"-eabihf\"));\n}\n"
  },
  {
    "path": "embassy-hal-internal/src/atomic_ring_buffer.rs",
    "content": "//! Atomic reusable ringbuffer.\nuse core::sync::atomic::{AtomicPtr, AtomicUsize, Ordering};\nuse core::{ptr, slice};\n\n/// Atomic reusable ringbuffer\n///\n/// This ringbuffer implementation is designed to be stored in a `static`,\n/// therefore all methods take `&self` and not `&mut self`.\n///\n/// It is \"reusable\": when created it has no backing buffer, you can give it\n/// one with `init` and take it back with `deinit`, and init it again in the\n/// future if needed. This is very non-idiomatic, but helps a lot when storing\n/// it in a `static`.\n///\n/// One concurrent writer and one concurrent reader are supported, even at\n/// different execution priorities (like main and irq).\npub struct RingBuffer {\n    #[doc(hidden)]\n    pub buf: AtomicPtr<u8>,\n    len: AtomicUsize,\n\n    // start and end wrap at len*2, not at len.\n    // This allows distinguishing \"full\" and \"empty\".\n    // full is when start+len == end (modulo len*2)\n    // empty is when start == end\n    //\n    // This avoids having to consider the ringbuffer \"full\" at len-1 instead of len.\n    // The usual solution is adding a \"full\" flag, but that can't be made atomic\n    #[doc(hidden)]\n    pub start: AtomicUsize,\n    #[doc(hidden)]\n    pub end: AtomicUsize,\n}\n\n/// A type which can only read from a ring buffer.\npub struct Reader<'a>(&'a RingBuffer);\n\n/// A type which can only write to a ring buffer.\npub struct Writer<'a>(&'a RingBuffer);\n\nimpl RingBuffer {\n    /// Create a new empty ringbuffer.\n    pub const fn new() -> Self {\n        Self {\n            buf: AtomicPtr::new(core::ptr::null_mut()),\n            len: AtomicUsize::new(0),\n            start: AtomicUsize::new(0),\n            end: AtomicUsize::new(0),\n        }\n    }\n\n    /// Initialize the ring buffer with a buffer.\n    ///\n    /// # Safety\n    /// - The buffer (`buf .. buf+len`) must be valid memory until `deinit` is called.\n    /// - Must not be called concurrently with any other methods.\n    pub unsafe fn init(&self, buf: *mut u8, len: usize) {\n        // Ordering: it's OK to use `Relaxed` because this is not called\n        // concurrently with other methods.\n        self.buf.store(buf, Ordering::Relaxed);\n        self.len.store(len, Ordering::Relaxed);\n        self.start.store(0, Ordering::Relaxed);\n        self.end.store(0, Ordering::Relaxed);\n    }\n\n    /// Deinitialize the ringbuffer.\n    ///\n    /// After calling this, the ringbuffer becomes empty, as if it was\n    /// just created with `new()`.\n    ///\n    /// # Safety\n    /// - Must not be called concurrently with any other methods.\n    pub unsafe fn deinit(&self) {\n        // Ordering: it's OK to use `Relaxed` because this is not called\n        // concurrently with other methods.\n        self.buf.store(ptr::null_mut(), Ordering::Relaxed);\n        self.len.store(0, Ordering::Relaxed);\n        self.start.store(0, Ordering::Relaxed);\n        self.end.store(0, Ordering::Relaxed);\n    }\n\n    /// Create a reader.\n    ///\n    /// # Safety\n    ///\n    /// - Only one reader can exist at a time.\n    /// - Ringbuffer must be initialized.\n    pub unsafe fn reader(&self) -> Reader<'_> {\n        Reader(self)\n    }\n\n    /// Try creating a reader, fails if not initialized.\n    ///\n    /// # Safety\n    ///\n    /// Only one reader can exist at a time.\n    pub unsafe fn try_reader(&self) -> Option<Reader<'_>> {\n        if self.buf.load(Ordering::Relaxed).is_null() {\n            return None;\n        }\n        Some(Reader(self))\n    }\n\n    /// Create a writer.\n    ///\n    /// # Safety\n    ///\n    /// - Only one writer can exist at a time.\n    /// - Ringbuffer must be initialized.\n    pub unsafe fn writer(&self) -> Writer<'_> {\n        Writer(self)\n    }\n\n    /// Try creating a writer, fails if not initialized.\n    ///\n    /// # Safety\n    ///\n    /// Only one writer can exist at a time.\n    pub unsafe fn try_writer(&self) -> Option<Writer<'_>> {\n        if self.buf.load(Ordering::Relaxed).is_null() {\n            return None;\n        }\n        Some(Writer(self))\n    }\n\n    /// Return if buffer is available.\n    pub fn is_available(&self) -> bool {\n        !self.buf.load(Ordering::Relaxed).is_null() && self.len.load(Ordering::Relaxed) != 0\n    }\n\n    /// Return length of buffer.\n    pub fn len(&self) -> usize {\n        self.len.load(Ordering::Relaxed)\n    }\n\n    /// Return number of items available to read.\n    pub fn available(&self) -> usize {\n        let end = self.end.load(Ordering::Relaxed);\n        let len = self.len.load(Ordering::Relaxed);\n        let start = self.start.load(Ordering::Relaxed);\n        if end >= start {\n            end - start\n        } else {\n            2 * len - start + end\n        }\n    }\n\n    /// Check if buffer is full.\n    pub fn is_full(&self) -> bool {\n        let len = self.len.load(Ordering::Relaxed);\n        let start = self.start.load(Ordering::Relaxed);\n        let end = self.end.load(Ordering::Relaxed);\n\n        self.wrap(start + len) == end\n    }\n\n    /// Check if buffer is at least half full.\n    pub fn is_half_full(&self) -> bool {\n        self.available() >= self.len.load(Ordering::Relaxed) / 2\n    }\n\n    /// Check if buffer is empty.\n    pub fn is_empty(&self) -> bool {\n        let start = self.start.load(Ordering::Relaxed);\n        let end = self.end.load(Ordering::Relaxed);\n\n        start == end\n    }\n\n    fn wrap(&self, mut n: usize) -> usize {\n        let len = self.len.load(Ordering::Relaxed);\n\n        if n >= len * 2 {\n            n -= len * 2\n        }\n        n\n    }\n}\n\nimpl<'a> Writer<'a> {\n    /// Push data into the buffer in-place.\n    ///\n    /// The closure `f` is called with a free part of the buffer, it must write\n    /// some data to it and return the amount of bytes written.\n    pub fn push(&mut self, f: impl FnOnce(&mut [u8]) -> usize) -> usize {\n        let (p, n) = self.push_buf();\n        let buf = unsafe { slice::from_raw_parts_mut(p, n) };\n        let n = f(buf);\n        self.push_done(n);\n        n\n    }\n\n    /// Push one data byte.\n    ///\n    /// Returns true if pushed successfully.\n    pub fn push_one(&mut self, val: u8) -> bool {\n        let n = self.push(|f| match f {\n            [] => 0,\n            [x, ..] => {\n                *x = val;\n                1\n            }\n        });\n        n != 0\n    }\n\n    /// Get a buffer where data can be pushed to.\n    ///\n    /// Equivalent to [`Self::push_buf`] but returns a slice.\n    pub fn push_slice(&mut self) -> &mut [u8] {\n        let (data, len) = self.push_buf();\n        unsafe { slice::from_raw_parts_mut(data, len) }\n    }\n\n    /// Get up to two buffers where data can be pushed to.\n    ///\n    /// Equivalent to [`Self::push_bufs`] but returns slices.\n    pub fn push_slices(&mut self) -> [&mut [u8]; 2] {\n        let [(d0, l0), (d1, l1)] = self.push_bufs();\n        unsafe { [slice::from_raw_parts_mut(d0, l0), slice::from_raw_parts_mut(d1, l1)] }\n    }\n\n    /// Get a buffer where data can be pushed to.\n    ///\n    /// Write data to the start of the buffer, then call `push_done` with\n    /// however many bytes you've pushed.\n    ///\n    /// The buffer is suitable to DMA to.\n    ///\n    /// If the ringbuf is full, size=0 will be returned.\n    ///\n    /// The buffer stays valid as long as no other `Writer` method is called\n    /// and `init`/`deinit` aren't called on the ringbuf.\n    pub fn push_buf(&mut self) -> (*mut u8, usize) {\n        // Ordering: popping writes `start` last, so we read `start` first.\n        // Read it with Acquire ordering, so that the next accesses can't be reordered up past it.\n        let mut start = self.0.start.load(Ordering::Acquire);\n        let buf = self.0.buf.load(Ordering::Relaxed);\n        let len = self.0.len.load(Ordering::Relaxed);\n        let mut end = self.0.end.load(Ordering::Relaxed);\n\n        let empty = start == end;\n\n        if start >= len {\n            start -= len\n        }\n        if end >= len {\n            end -= len\n        }\n\n        if start == end && !empty {\n            // full\n            return (buf, 0);\n        }\n        let n = if start > end { start - end } else { len - end };\n\n        trace!(\"  ringbuf: push_buf {:?}..{:?}\", end, end + n);\n        (unsafe { buf.add(end) }, n)\n    }\n\n    /// Get up to two buffers where data can be pushed to.\n    ///\n    /// Write data starting at the beginning of the first buffer, then call\n    /// `push_done` with however many bytes you've pushed.\n    ///\n    /// The buffers are suitable to DMA to.\n    ///\n    /// If the ringbuf is full, both buffers will be zero length.\n    /// If there is only area available, the second buffer will be zero length.\n    ///\n    /// The buffer stays valid as long as no other `Writer` method is called\n    /// and `init`/`deinit` aren't called on the ringbuf.\n    pub fn push_bufs(&mut self) -> [(*mut u8, usize); 2] {\n        // Ordering: as per push_buf()\n        let mut start = self.0.start.load(Ordering::Acquire);\n        let buf = self.0.buf.load(Ordering::Relaxed);\n        let len = self.0.len.load(Ordering::Relaxed);\n        let mut end = self.0.end.load(Ordering::Relaxed);\n\n        let empty = start == end;\n\n        if start >= len {\n            start -= len\n        }\n        if end >= len {\n            end -= len\n        }\n\n        if start == end && !empty {\n            // full\n            return [(buf, 0), (buf, 0)];\n        }\n        let n0 = if start > end { start - end } else { len - end };\n        let n1 = if start <= end { start } else { 0 };\n\n        trace!(\"  ringbuf: push_bufs [{:?}..{:?}, {:?}..{:?}]\", end, end + n0, 0, n1);\n        [(unsafe { buf.add(end) }, n0), (buf, n1)]\n    }\n\n    /// Mark n bytes as written and advance the write index.\n    pub fn push_done(&mut self, n: usize) {\n        trace!(\"  ringbuf: push {:?}\", n);\n        let end = self.0.end.load(Ordering::Relaxed);\n\n        // Ordering: write `end` last, with Release ordering.\n        // The ordering ensures no preceding memory accesses (such as writing\n        // the actual data in the buffer) can be reordered down past it, which\n        // will guarantee the reader sees them after reading from `end`.\n        self.0.end.store(self.0.wrap(end + n), Ordering::Release);\n    }\n}\n\nimpl<'a> Reader<'a> {\n    /// Pop data from the buffer in-place.\n    ///\n    /// The closure `f` is called with the next data, it must process\n    /// some data from it and return the amount of bytes processed.\n    pub fn pop(&mut self, f: impl FnOnce(&[u8]) -> usize) -> usize {\n        let (p, n) = self.pop_buf();\n        let buf = unsafe { slice::from_raw_parts(p, n) };\n        let n = f(buf);\n        self.pop_done(n);\n        n\n    }\n\n    /// Pop one data byte.\n    ///\n    /// Returns true if popped successfully.\n    pub fn pop_one(&mut self) -> Option<u8> {\n        let mut res = None;\n        self.pop(|f| match f {\n            &[] => 0,\n            &[x, ..] => {\n                res = Some(x);\n                1\n            }\n        });\n        res\n    }\n\n    /// Get a buffer where data can be popped from.\n    ///\n    /// Equivalent to [`Self::pop_buf`] but returns a slice.\n    pub fn pop_slice(&mut self) -> &mut [u8] {\n        let (data, len) = self.pop_buf();\n        unsafe { slice::from_raw_parts_mut(data, len) }\n    }\n\n    /// Get a buffer where data can be popped from.\n    ///\n    /// Read data from the start of the buffer, then call `pop_done` with\n    /// however many bytes you've processed.\n    ///\n    /// The buffer is suitable to DMA from.\n    ///\n    /// If the ringbuf is empty, size=0 will be returned.\n    ///\n    /// The buffer stays valid as long as no other `Reader` method is called\n    /// and `init`/`deinit` aren't called on the ringbuf.\n    pub fn pop_buf(&mut self) -> (*mut u8, usize) {\n        // Ordering: pushing writes `end` last, so we read `end` first.\n        // Read it with Acquire ordering, so that the next accesses can't be reordered up past it.\n        // This is needed to guarantee we \"see\" the data written by the writer.\n        let mut end = self.0.end.load(Ordering::Acquire);\n        let buf = self.0.buf.load(Ordering::Relaxed);\n        let len = self.0.len.load(Ordering::Relaxed);\n        let mut start = self.0.start.load(Ordering::Relaxed);\n\n        if start == end {\n            return (buf, 0);\n        }\n\n        if start >= len {\n            start -= len\n        }\n        if end >= len {\n            end -= len\n        }\n\n        let n = if end > start { end - start } else { len - start };\n\n        trace!(\"  ringbuf: pop_buf {:?}..{:?}\", start, start + n);\n        (unsafe { buf.add(start) }, n)\n    }\n\n    /// Mark n bytes as read and allow advance the read index.\n    pub fn pop_done(&mut self, n: usize) {\n        trace!(\"  ringbuf: pop {:?}\", n);\n\n        let start = self.0.start.load(Ordering::Relaxed);\n\n        // Ordering: write `start` last, with Release ordering.\n        // The ordering ensures no preceding memory accesses (such as reading\n        // the actual data) can be reordered down past it. This is necessary\n        // because writing to `start` is effectively freeing the read part of the\n        // buffer, which \"gives permission\" to the writer to write to it again.\n        // Therefore, all buffer accesses must be completed before this.\n        self.0.start.store(self.0.wrap(start + n), Ordering::Release);\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n\n    #[test]\n    fn push_pop() {\n        let mut b = [0; 4];\n        let rb = RingBuffer::new();\n        unsafe {\n            rb.init(b.as_mut_ptr(), 4);\n\n            assert_eq!(rb.is_empty(), true);\n            assert_eq!(rb.is_half_full(), false);\n            assert_eq!(rb.is_full(), false);\n\n            rb.writer().push(|buf| {\n                assert_eq!(4, buf.len());\n                buf[0] = 1;\n                buf[1] = 2;\n                buf[2] = 3;\n                buf[3] = 4;\n                4\n            });\n\n            assert_eq!(rb.is_empty(), false);\n            assert_eq!(rb.is_half_full(), true);\n            assert_eq!(rb.is_full(), true);\n\n            rb.writer().push(|buf| {\n                // If it's full, we can push 0 bytes.\n                assert_eq!(0, buf.len());\n                0\n            });\n\n            assert_eq!(rb.is_empty(), false);\n            assert_eq!(rb.is_half_full(), true);\n            assert_eq!(rb.is_full(), true);\n\n            rb.reader().pop(|buf| {\n                assert_eq!(4, buf.len());\n                assert_eq!(1, buf[0]);\n                1\n            });\n\n            assert_eq!(rb.is_empty(), false);\n            assert_eq!(rb.is_half_full(), true);\n            assert_eq!(rb.is_full(), false);\n\n            rb.reader().pop(|buf| {\n                assert_eq!(3, buf.len());\n                0\n            });\n\n            assert_eq!(rb.is_empty(), false);\n            assert_eq!(rb.is_half_full(), true);\n            assert_eq!(rb.is_full(), false);\n\n            rb.reader().pop(|buf| {\n                assert_eq!(3, buf.len());\n                assert_eq!(2, buf[0]);\n                assert_eq!(3, buf[1]);\n                2\n            });\n            rb.reader().pop(|buf| {\n                assert_eq!(1, buf.len());\n                assert_eq!(4, buf[0]);\n                1\n            });\n\n            assert_eq!(rb.is_empty(), true);\n            assert_eq!(rb.is_half_full(), false);\n            assert_eq!(rb.is_full(), false);\n\n            rb.reader().pop(|buf| {\n                assert_eq!(0, buf.len());\n                0\n            });\n\n            rb.writer().push(|buf| {\n                assert_eq!(4, buf.len());\n                buf[0] = 10;\n                1\n            });\n\n            assert_eq!(rb.is_empty(), false);\n            assert_eq!(rb.is_half_full(), false);\n            assert_eq!(rb.is_full(), false);\n\n            rb.writer().push(|buf| {\n                assert_eq!(3, buf.len());\n                buf[0] = 11;\n                1\n            });\n\n            assert_eq!(rb.is_empty(), false);\n            assert_eq!(rb.is_half_full(), true);\n            assert_eq!(rb.is_full(), false);\n\n            rb.writer().push(|buf| {\n                assert_eq!(2, buf.len());\n                buf[0] = 12;\n                1\n            });\n\n            assert_eq!(rb.is_empty(), false);\n            assert_eq!(rb.is_half_full(), true);\n            assert_eq!(rb.is_full(), false);\n\n            rb.writer().push(|buf| {\n                assert_eq!(1, buf.len());\n                buf[0] = 13;\n                1\n            });\n\n            assert_eq!(rb.is_empty(), false);\n            assert_eq!(rb.is_half_full(), true);\n            assert_eq!(rb.is_full(), true);\n        }\n    }\n\n    #[test]\n    fn zero_len() {\n        let mut b = [0; 0];\n\n        let rb = RingBuffer::new();\n        unsafe {\n            rb.init(b.as_mut_ptr(), b.len());\n\n            assert_eq!(rb.is_empty(), true);\n            assert_eq!(rb.is_half_full(), true);\n            assert_eq!(rb.is_full(), true);\n\n            rb.writer().push(|buf| {\n                assert_eq!(0, buf.len());\n                0\n            });\n\n            rb.reader().pop(|buf| {\n                assert_eq!(0, buf.len());\n                0\n            });\n        }\n    }\n\n    #[test]\n    fn push_slices() {\n        let mut b = [0; 4];\n        let rb = RingBuffer::new();\n        unsafe {\n            rb.init(b.as_mut_ptr(), 4);\n\n            /* push 3 -> [1 2 3 x] */\n            let mut w = rb.writer();\n            let ps = w.push_slices();\n            assert_eq!(4, ps[0].len());\n            assert_eq!(0, ps[1].len());\n            ps[0][0] = 1;\n            ps[0][1] = 2;\n            ps[0][2] = 3;\n            w.push_done(3);\n            drop(w);\n\n            /* pop 2 -> [x x 3 x] */\n            rb.reader().pop(|buf| {\n                assert_eq!(3, buf.len());\n                assert_eq!(1, buf[0]);\n                assert_eq!(2, buf[1]);\n                assert_eq!(3, buf[2]);\n                2\n            });\n\n            /* push 3 -> [5 6 3 4] */\n            let mut w = rb.writer();\n            let ps = w.push_slices();\n            assert_eq!(1, ps[0].len());\n            assert_eq!(2, ps[1].len());\n            ps[0][0] = 4;\n            ps[1][0] = 5;\n            ps[1][1] = 6;\n            w.push_done(3);\n            drop(w);\n\n            /* buf is now full */\n            let mut w = rb.writer();\n            let ps = w.push_slices();\n            assert_eq!(0, ps[0].len());\n            assert_eq!(0, ps[1].len());\n\n            /* pop 2 -> [5 6 x x] */\n            rb.reader().pop(|buf| {\n                assert_eq!(2, buf.len());\n                assert_eq!(3, buf[0]);\n                assert_eq!(4, buf[1]);\n                2\n            });\n\n            /* should now have one push slice again */\n            let mut w = rb.writer();\n            let ps = w.push_slices();\n            assert_eq!(2, ps[0].len());\n            assert_eq!(0, ps[1].len());\n            drop(w);\n\n            /* pop 2 -> [x x x x] */\n            rb.reader().pop(|buf| {\n                assert_eq!(2, buf.len());\n                assert_eq!(5, buf[0]);\n                assert_eq!(6, buf[1]);\n                2\n            });\n\n            /* should now have two push slices */\n            let mut w = rb.writer();\n            let ps = w.push_slices();\n            assert_eq!(2, ps[0].len());\n            assert_eq!(2, ps[1].len());\n            drop(w);\n\n            /* make sure we exercise all wrap around cases properly */\n            for _ in 0..10 {\n                /* should be empty, push 1 */\n                let mut w = rb.writer();\n                let ps = w.push_slices();\n                assert_eq!(4, ps[0].len() + ps[1].len());\n                w.push_done(1);\n                drop(w);\n\n                /* should have 1 element */\n                let mut w = rb.writer();\n                let ps = w.push_slices();\n                assert_eq!(3, ps[0].len() + ps[1].len());\n                drop(w);\n\n                /* pop 1 */\n                rb.reader().pop(|buf| {\n                    assert_eq!(1, buf.len());\n                    1\n                });\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "embassy-hal-internal/src/drop.rs",
    "content": "//! Types for controlling when drop is invoked.\nuse core::mem;\nuse core::mem::MaybeUninit;\n\n/// A type to delay the drop handler invocation.\n#[must_use = \"to delay the drop handler invocation to the end of the scope\"]\npub struct OnDrop<F: FnOnce()> {\n    f: MaybeUninit<F>,\n}\n\nimpl<F: FnOnce()> OnDrop<F> {\n    /// Create a new instance.\n    pub fn new(f: F) -> Self {\n        Self { f: MaybeUninit::new(f) }\n    }\n\n    /// Prevent drop handler from running.\n    pub fn defuse(self) {\n        mem::forget(self)\n    }\n}\n\nimpl<F: FnOnce()> Drop for OnDrop<F> {\n    fn drop(&mut self) {\n        unsafe { self.f.as_ptr().read()() }\n    }\n}\n\n/// An explosive ordinance that panics if it is improperly disposed of.\n///\n/// This is to forbid dropping futures, when there is absolutely no other choice.\n///\n/// To correctly dispose of this device, call the [defuse](struct.DropBomb.html#method.defuse)\n/// method before this object is dropped.\n#[must_use = \"to delay the drop bomb invokation to the end of the scope\"]\npub struct DropBomb {\n    _private: (),\n}\n\nimpl DropBomb {\n    /// Create a new instance.\n    pub fn new() -> Self {\n        Self { _private: () }\n    }\n\n    /// Defuses the bomb, rendering it safe to drop.\n    pub fn defuse(self) {\n        mem::forget(self)\n    }\n}\n\nimpl Drop for DropBomb {\n    fn drop(&mut self) {\n        panic!(\"boom\")\n    }\n}\n"
  },
  {
    "path": "embassy-hal-internal/src/fmt.rs",
    "content": "#![macro_use]\n#![allow(unused)]\n\nuse core::fmt::{Debug, Display, LowerHex};\n\n#[cfg(all(feature = \"defmt\", feature = \"log\"))]\ncompile_error!(\"You may not enable both `defmt` and `log` features.\");\n\n#[collapse_debuginfo(yes)]\nmacro_rules! assert {\n    ($($x:tt)*) => {\n        {\n            #[cfg(not(feature = \"defmt\"))]\n            ::core::assert!($($x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::assert!($($x)*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! assert_eq {\n    ($($x:tt)*) => {\n        {\n            #[cfg(not(feature = \"defmt\"))]\n            ::core::assert_eq!($($x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::assert_eq!($($x)*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! assert_ne {\n    ($($x:tt)*) => {\n        {\n            #[cfg(not(feature = \"defmt\"))]\n            ::core::assert_ne!($($x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::assert_ne!($($x)*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! debug_assert {\n    ($($x:tt)*) => {\n        {\n            #[cfg(not(feature = \"defmt\"))]\n            ::core::debug_assert!($($x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::debug_assert!($($x)*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! debug_assert_eq {\n    ($($x:tt)*) => {\n        {\n            #[cfg(not(feature = \"defmt\"))]\n            ::core::debug_assert_eq!($($x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::debug_assert_eq!($($x)*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! debug_assert_ne {\n    ($($x:tt)*) => {\n        {\n            #[cfg(not(feature = \"defmt\"))]\n            ::core::debug_assert_ne!($($x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::debug_assert_ne!($($x)*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! todo {\n    ($($x:tt)*) => {\n        {\n            #[cfg(not(feature = \"defmt\"))]\n            ::core::todo!($($x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::todo!($($x)*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! unreachable {\n    ($($x:tt)*) => {\n        {\n            #[cfg(not(feature = \"defmt\"))]\n            ::core::unreachable!($($x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::unreachable!($($x)*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! panic {\n    ($($x:tt)*) => {\n        {\n            #[cfg(not(feature = \"defmt\"))]\n            ::core::panic!($($x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::panic!($($x)*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! trace {\n    ($s:literal $(, $x:expr)* $(,)?) => {\n        {\n            #[cfg(feature = \"log\")]\n            ::log::trace!($s $(, $x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::trace!($s $(, $x)*);\n            #[cfg(not(any(feature = \"log\", feature=\"defmt\")))]\n            let _ = ($( & $x ),*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! debug {\n    ($s:literal $(, $x:expr)* $(,)?) => {\n        {\n            #[cfg(feature = \"log\")]\n            ::log::debug!($s $(, $x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::debug!($s $(, $x)*);\n            #[cfg(not(any(feature = \"log\", feature=\"defmt\")))]\n            let _ = ($( & $x ),*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! info {\n    ($s:literal $(, $x:expr)* $(,)?) => {\n        {\n            #[cfg(feature = \"log\")]\n            ::log::info!($s $(, $x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::info!($s $(, $x)*);\n            #[cfg(not(any(feature = \"log\", feature=\"defmt\")))]\n            let _ = ($( & $x ),*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! warn {\n    ($s:literal $(, $x:expr)* $(,)?) => {\n        {\n            #[cfg(feature = \"log\")]\n            ::log::warn!($s $(, $x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::warn!($s $(, $x)*);\n            #[cfg(not(any(feature = \"log\", feature=\"defmt\")))]\n            let _ = ($( & $x ),*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! error {\n    ($s:literal $(, $x:expr)* $(,)?) => {\n        {\n            #[cfg(feature = \"log\")]\n            ::log::error!($s $(, $x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::error!($s $(, $x)*);\n            #[cfg(not(any(feature = \"log\", feature=\"defmt\")))]\n            let _ = ($( & $x ),*);\n        }\n    };\n}\n\n#[cfg(feature = \"defmt\")]\n#[collapse_debuginfo(yes)]\nmacro_rules! unwrap {\n    ($($x:tt)*) => {\n        ::defmt::unwrap!($($x)*)\n    };\n}\n\n#[cfg(not(feature = \"defmt\"))]\n#[collapse_debuginfo(yes)]\nmacro_rules! unwrap {\n    ($arg:expr) => {\n        match $crate::fmt::Try::into_result($arg) {\n            ::core::result::Result::Ok(t) => t,\n            ::core::result::Result::Err(e) => {\n                ::core::panic!(\"unwrap of `{}` failed: {:?}\", ::core::stringify!($arg), e);\n            }\n        }\n    };\n    ($arg:expr, $($msg:expr),+ $(,)? ) => {\n        match $crate::fmt::Try::into_result($arg) {\n            ::core::result::Result::Ok(t) => t,\n            ::core::result::Result::Err(e) => {\n                ::core::panic!(\"unwrap of `{}` failed: {}: {:?}\", ::core::stringify!($arg), ::core::format_args!($($msg,)*), e);\n            }\n        }\n    }\n}\n\n#[derive(Debug, Copy, Clone, Eq, PartialEq)]\npub struct NoneError;\n\npub trait Try {\n    type Ok;\n    type Error;\n    fn into_result(self) -> Result<Self::Ok, Self::Error>;\n}\n\nimpl<T> Try for Option<T> {\n    type Ok = T;\n    type Error = NoneError;\n\n    #[inline]\n    fn into_result(self) -> Result<T, NoneError> {\n        self.ok_or(NoneError)\n    }\n}\n\nimpl<T, E> Try for Result<T, E> {\n    type Ok = T;\n    type Error = E;\n\n    #[inline]\n    fn into_result(self) -> Self {\n        self\n    }\n}\n\npub(crate) struct Bytes<'a>(pub &'a [u8]);\n\nimpl<'a> Debug for Bytes<'a> {\n    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {\n        write!(f, \"{:#02x?}\", self.0)\n    }\n}\n\nimpl<'a> Display for Bytes<'a> {\n    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {\n        write!(f, \"{:#02x?}\", self.0)\n    }\n}\n\nimpl<'a> LowerHex for Bytes<'a> {\n    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {\n        write!(f, \"{:#02x?}\", self.0)\n    }\n}\n\n#[cfg(feature = \"defmt\")]\nimpl<'a> defmt::Format for Bytes<'a> {\n    fn format(&self, fmt: defmt::Formatter) {\n        defmt::write!(fmt, \"{:02x}\", self.0)\n    }\n}\n"
  },
  {
    "path": "embassy-hal-internal/src/interrupt.rs",
    "content": "//! Interrupt handling for cortex-m devices.\nuse core::mem;\nuse core::sync::atomic::{Ordering, compiler_fence};\n\nuse cortex_m::interrupt::InterruptNumber;\nuse cortex_m::peripheral::NVIC;\nuse critical_section::CriticalSection;\n\n/// Generate a standard `mod interrupt` for a HAL.\n#[macro_export]\nmacro_rules! interrupt_mod {\n    ($($irqs:ident),* $(,)?) => {\n        #[cfg(feature = \"rt\")]\n        pub use cortex_m_rt::interrupt;\n\n        /// Interrupt definitions.\n        pub mod interrupt {\n            pub use $crate::interrupt::{InterruptExt, Priority};\n            pub use crate::pac::Interrupt::*;\n            pub use crate::pac::Interrupt;\n\n            /// Type-level interrupt infrastructure.\n            ///\n            /// This module contains one *type* per interrupt. This is used for checking at compile time that\n            /// the interrupts are correctly bound to HAL drivers.\n            ///\n            /// As an end user, you shouldn't need to use this module directly. Use the [`crate::bind_interrupts!`] macro\n            /// to bind interrupts, and the [`crate::interrupt`] module to manually register interrupt handlers and manipulate\n            /// interrupts directly (pending/unpending, enabling/disabling, setting the priority, etc...)\n            pub mod typelevel {\n                use super::InterruptExt;\n\n                trait SealedInterrupt {}\n\n                /// Type-level interrupt.\n                ///\n                /// This trait is implemented for all typelevel interrupt types in this module.\n                pub trait Interrupt: SealedInterrupt {\n\n                    /// Interrupt enum variant.\n                    ///\n                    /// This allows going from typelevel interrupts (one type per interrupt) to\n                    /// non-typelevel interrupts (a single `Interrupt` enum type, with one variant per interrupt).\n                    const IRQ: super::Interrupt;\n\n                    /// Enable the interrupt.\n                    #[inline]\n                    unsafe fn enable() {\n                        Self::IRQ.enable()\n                    }\n\n                    /// Disable the interrupt.\n                    #[inline]\n                    fn disable() {\n                        Self::IRQ.disable()\n                    }\n\n                    /// Check if interrupt is enabled.\n                    #[inline]\n                    fn is_enabled() -> bool {\n                        Self::IRQ.is_enabled()\n                    }\n\n                    /// Check if interrupt is pending.\n                    #[inline]\n                    fn is_pending() -> bool {\n                        Self::IRQ.is_pending()\n                    }\n\n                    /// Set interrupt pending.\n                    #[inline]\n                    fn pend() {\n                        Self::IRQ.pend()\n                    }\n\n                    /// Unset interrupt pending.\n                    #[inline]\n                    fn unpend() {\n                        Self::IRQ.unpend()\n                    }\n\n                    /// Get the priority of the interrupt.\n                    #[inline]\n                    fn get_priority() -> crate::interrupt::Priority {\n                        Self::IRQ.get_priority()\n                    }\n\n                    /// Set the interrupt priority.\n                    #[inline]\n                    fn set_priority(prio: crate::interrupt::Priority) {\n                        Self::IRQ.set_priority(prio)\n                    }\n\n                    /// Set the interrupt priority with an already-acquired critical section\n                    #[inline]\n                    fn set_priority_with_cs(cs: critical_section::CriticalSection, prio: crate::interrupt::Priority) {\n                        Self::IRQ.set_priority_with_cs(cs, prio)\n                    }\n                }\n\n                $(\n                    #[allow(non_camel_case_types)]\n                    #[doc=stringify!($irqs)]\n                    #[doc=\" typelevel interrupt.\"]\n                    pub enum $irqs {}\n                    impl SealedInterrupt for $irqs{}\n                    impl Interrupt for $irqs {\n                        const IRQ: super::Interrupt = super::Interrupt::$irqs;\n                    }\n                )*\n\n                /// Interrupt handler trait.\n                ///\n                /// Drivers that need to handle interrupts implement this trait.\n                /// The user must ensure `on_interrupt()` is called every time the interrupt fires.\n                /// Drivers must use use [`Binding`] to assert at compile time that the user has done so.\n                pub trait Handler<I: Interrupt> {\n                    /// Interrupt handler function.\n                    ///\n                    /// Must be called every time the `I` interrupt fires, synchronously from\n                    /// the interrupt handler context.\n                    ///\n                    /// # Safety\n                    ///\n                    /// This function must ONLY be called from the interrupt handler for `I`.\n                    unsafe fn on_interrupt();\n                }\n\n                /// Compile-time assertion that an interrupt has been bound to a handler.\n                ///\n                /// For the vast majority of cases, you should use the `bind_interrupts!`\n                /// macro instead of writing `unsafe impl`s of this trait.\n                ///\n                /// # Safety\n                ///\n                /// By implementing this trait, you are asserting that you have arranged for `H::on_interrupt()`\n                /// to be called every time the `I` interrupt fires.\n                ///\n                /// This allows drivers to check bindings at compile-time.\n                pub unsafe trait Binding<I: Interrupt, H: Handler<I>>: Copy {}\n            }\n        }\n    };\n}\n\n/// Represents an interrupt type that can be configured by embassy to handle\n/// interrupts.\npub unsafe trait InterruptExt: InterruptNumber + Copy {\n    /// Enable the interrupt.\n    #[inline]\n    unsafe fn enable(self) {\n        compiler_fence(Ordering::SeqCst);\n        NVIC::unmask(self)\n    }\n\n    /// Disable the interrupt.\n    #[inline]\n    fn disable(self) {\n        NVIC::mask(self);\n        compiler_fence(Ordering::SeqCst);\n    }\n\n    /// Check if interrupt is being handled.\n    #[inline]\n    #[cfg(not(armv6m))]\n    fn is_active(self) -> bool {\n        NVIC::is_active(self)\n    }\n\n    /// Check if interrupt is enabled.\n    #[inline]\n    fn is_enabled(self) -> bool {\n        NVIC::is_enabled(self)\n    }\n\n    /// Check if interrupt is pending.\n    #[inline]\n    fn is_pending(self) -> bool {\n        NVIC::is_pending(self)\n    }\n\n    /// Set interrupt pending.\n    #[inline]\n    fn pend(self) {\n        NVIC::pend(self)\n    }\n\n    /// Unset interrupt pending.\n    #[inline]\n    fn unpend(self) {\n        NVIC::unpend(self)\n    }\n\n    /// Get the priority of the interrupt.\n    #[inline]\n    fn get_priority(self) -> Priority {\n        Priority::from(NVIC::get_priority(self))\n    }\n\n    /// Set the interrupt priority.\n    #[inline]\n    fn set_priority(self, prio: Priority) {\n        unsafe {\n            let mut nvic: cortex_m::peripheral::NVIC = mem::transmute(());\n\n            // On thumbv6, set_priority must do a RMW to change 8bit in a 32bit reg.\n            #[cfg(armv6m)]\n            critical_section::with(|_| nvic.set_priority(self, prio.into()));\n            // On thumbv7+, set_priority does an atomic 8bit write, so no CS needed.\n            #[cfg(not(armv6m))]\n            nvic.set_priority(self, prio.into());\n        }\n    }\n\n    /// Set the interrupt priority with an already-acquired critical section\n    ///\n    /// Equivalent to `set_priority`, except you pass a `CriticalSection` to prove\n    /// you've already acquired a critical section. This prevents acquiring another\n    /// one, which saves code size.\n    #[inline]\n    fn set_priority_with_cs(self, _cs: CriticalSection, prio: Priority) {\n        unsafe {\n            let mut nvic: cortex_m::peripheral::NVIC = mem::transmute(());\n            nvic.set_priority(self, prio.into());\n        }\n    }\n}\n\nunsafe impl<T: InterruptNumber + Copy> InterruptExt for T {}\n\nimpl From<u8> for Priority {\n    fn from(priority: u8) -> Self {\n        unsafe { mem::transmute(priority & PRIO_MASK) }\n    }\n}\n\nimpl From<Priority> for u8 {\n    fn from(p: Priority) -> Self {\n        p as u8\n    }\n}\n\n#[cfg(feature = \"prio-bits-0\")]\nconst PRIO_MASK: u8 = 0x00;\n#[cfg(feature = \"prio-bits-1\")]\nconst PRIO_MASK: u8 = 0x80;\n#[cfg(feature = \"prio-bits-2\")]\nconst PRIO_MASK: u8 = 0xc0;\n#[cfg(feature = \"prio-bits-3\")]\nconst PRIO_MASK: u8 = 0xe0;\n#[cfg(feature = \"prio-bits-4\")]\nconst PRIO_MASK: u8 = 0xf0;\n#[cfg(feature = \"prio-bits-5\")]\nconst PRIO_MASK: u8 = 0xf8;\n#[cfg(feature = \"prio-bits-6\")]\nconst PRIO_MASK: u8 = 0xfc;\n#[cfg(feature = \"prio-bits-7\")]\nconst PRIO_MASK: u8 = 0xfe;\n#[cfg(feature = \"prio-bits-8\")]\nconst PRIO_MASK: u8 = 0xff;\n\n/// The interrupt priority level.\n///\n/// NOTE: The contents of this enum differ according to the set `prio-bits-*` Cargo feature.\n#[cfg(feature = \"prio-bits-0\")]\n#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\n#[repr(u8)]\n#[allow(missing_docs)]\npub enum Priority {\n    P0 = 0x0,\n}\n\n/// The interrupt priority level.\n///\n/// NOTE: The contents of this enum differ according to the set `prio-bits-*` Cargo feature.\n#[cfg(feature = \"prio-bits-1\")]\n#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\n#[repr(u8)]\n#[allow(missing_docs)]\npub enum Priority {\n    P0 = 0x0,\n    P1 = 0x80,\n}\n\n/// The interrupt priority level.\n///\n/// NOTE: The contents of this enum differ according to the set `prio-bits-*` Cargo feature.\n#[cfg(feature = \"prio-bits-2\")]\n#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\n#[repr(u8)]\n#[allow(missing_docs)]\npub enum Priority {\n    P0 = 0x0,\n    P1 = 0x40,\n    P2 = 0x80,\n    P3 = 0xc0,\n}\n\n/// The interrupt priority level.\n///\n/// NOTE: The contents of this enum differ according to the set `prio-bits-*` Cargo feature.\n#[cfg(feature = \"prio-bits-3\")]\n#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\n#[repr(u8)]\n#[allow(missing_docs)]\npub enum Priority {\n    P0 = 0x0,\n    P1 = 0x20,\n    P2 = 0x40,\n    P3 = 0x60,\n    P4 = 0x80,\n    P5 = 0xa0,\n    P6 = 0xc0,\n    P7 = 0xe0,\n}\n\n/// The interrupt priority level.\n///\n/// NOTE: The contents of this enum differ according to the set `prio-bits-*` Cargo feature.\n#[cfg(feature = \"prio-bits-4\")]\n#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\n#[repr(u8)]\n#[allow(missing_docs)]\npub enum Priority {\n    P0 = 0x0,\n    P1 = 0x10,\n    P2 = 0x20,\n    P3 = 0x30,\n    P4 = 0x40,\n    P5 = 0x50,\n    P6 = 0x60,\n    P7 = 0x70,\n    P8 = 0x80,\n    P9 = 0x90,\n    P10 = 0xa0,\n    P11 = 0xb0,\n    P12 = 0xc0,\n    P13 = 0xd0,\n    P14 = 0xe0,\n    P15 = 0xf0,\n}\n\n/// The interrupt priority level.\n///\n/// NOTE: The contents of this enum differ according to the set `prio-bits-*` Cargo feature.\n#[cfg(feature = \"prio-bits-5\")]\n#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\n#[repr(u8)]\n#[allow(missing_docs)]\npub enum Priority {\n    P0 = 0x0,\n    P1 = 0x8,\n    P2 = 0x10,\n    P3 = 0x18,\n    P4 = 0x20,\n    P5 = 0x28,\n    P6 = 0x30,\n    P7 = 0x38,\n    P8 = 0x40,\n    P9 = 0x48,\n    P10 = 0x50,\n    P11 = 0x58,\n    P12 = 0x60,\n    P13 = 0x68,\n    P14 = 0x70,\n    P15 = 0x78,\n    P16 = 0x80,\n    P17 = 0x88,\n    P18 = 0x90,\n    P19 = 0x98,\n    P20 = 0xa0,\n    P21 = 0xa8,\n    P22 = 0xb0,\n    P23 = 0xb8,\n    P24 = 0xc0,\n    P25 = 0xc8,\n    P26 = 0xd0,\n    P27 = 0xd8,\n    P28 = 0xe0,\n    P29 = 0xe8,\n    P30 = 0xf0,\n    P31 = 0xf8,\n}\n\n/// The interrupt priority level.\n///\n/// NOTE: The contents of this enum differ according to the set `prio-bits-*` Cargo feature.\n#[cfg(feature = \"prio-bits-6\")]\n#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\n#[repr(u8)]\n#[allow(missing_docs)]\npub enum Priority {\n    P0 = 0x0,\n    P1 = 0x4,\n    P2 = 0x8,\n    P3 = 0xc,\n    P4 = 0x10,\n    P5 = 0x14,\n    P6 = 0x18,\n    P7 = 0x1c,\n    P8 = 0x20,\n    P9 = 0x24,\n    P10 = 0x28,\n    P11 = 0x2c,\n    P12 = 0x30,\n    P13 = 0x34,\n    P14 = 0x38,\n    P15 = 0x3c,\n    P16 = 0x40,\n    P17 = 0x44,\n    P18 = 0x48,\n    P19 = 0x4c,\n    P20 = 0x50,\n    P21 = 0x54,\n    P22 = 0x58,\n    P23 = 0x5c,\n    P24 = 0x60,\n    P25 = 0x64,\n    P26 = 0x68,\n    P27 = 0x6c,\n    P28 = 0x70,\n    P29 = 0x74,\n    P30 = 0x78,\n    P31 = 0x7c,\n    P32 = 0x80,\n    P33 = 0x84,\n    P34 = 0x88,\n    P35 = 0x8c,\n    P36 = 0x90,\n    P37 = 0x94,\n    P38 = 0x98,\n    P39 = 0x9c,\n    P40 = 0xa0,\n    P41 = 0xa4,\n    P42 = 0xa8,\n    P43 = 0xac,\n    P44 = 0xb0,\n    P45 = 0xb4,\n    P46 = 0xb8,\n    P47 = 0xbc,\n    P48 = 0xc0,\n    P49 = 0xc4,\n    P50 = 0xc8,\n    P51 = 0xcc,\n    P52 = 0xd0,\n    P53 = 0xd4,\n    P54 = 0xd8,\n    P55 = 0xdc,\n    P56 = 0xe0,\n    P57 = 0xe4,\n    P58 = 0xe8,\n    P59 = 0xec,\n    P60 = 0xf0,\n    P61 = 0xf4,\n    P62 = 0xf8,\n    P63 = 0xfc,\n}\n\n/// The interrupt priority level.\n///\n/// NOTE: The contents of this enum differ according to the set `prio-bits-*` Cargo feature.\n#[cfg(feature = \"prio-bits-7\")]\n#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\n#[repr(u8)]\n#[allow(missing_docs)]\npub enum Priority {\n    P0 = 0x0,\n    P1 = 0x2,\n    P2 = 0x4,\n    P3 = 0x6,\n    P4 = 0x8,\n    P5 = 0xa,\n    P6 = 0xc,\n    P7 = 0xe,\n    P8 = 0x10,\n    P9 = 0x12,\n    P10 = 0x14,\n    P11 = 0x16,\n    P12 = 0x18,\n    P13 = 0x1a,\n    P14 = 0x1c,\n    P15 = 0x1e,\n    P16 = 0x20,\n    P17 = 0x22,\n    P18 = 0x24,\n    P19 = 0x26,\n    P20 = 0x28,\n    P21 = 0x2a,\n    P22 = 0x2c,\n    P23 = 0x2e,\n    P24 = 0x30,\n    P25 = 0x32,\n    P26 = 0x34,\n    P27 = 0x36,\n    P28 = 0x38,\n    P29 = 0x3a,\n    P30 = 0x3c,\n    P31 = 0x3e,\n    P32 = 0x40,\n    P33 = 0x42,\n    P34 = 0x44,\n    P35 = 0x46,\n    P36 = 0x48,\n    P37 = 0x4a,\n    P38 = 0x4c,\n    P39 = 0x4e,\n    P40 = 0x50,\n    P41 = 0x52,\n    P42 = 0x54,\n    P43 = 0x56,\n    P44 = 0x58,\n    P45 = 0x5a,\n    P46 = 0x5c,\n    P47 = 0x5e,\n    P48 = 0x60,\n    P49 = 0x62,\n    P50 = 0x64,\n    P51 = 0x66,\n    P52 = 0x68,\n    P53 = 0x6a,\n    P54 = 0x6c,\n    P55 = 0x6e,\n    P56 = 0x70,\n    P57 = 0x72,\n    P58 = 0x74,\n    P59 = 0x76,\n    P60 = 0x78,\n    P61 = 0x7a,\n    P62 = 0x7c,\n    P63 = 0x7e,\n    P64 = 0x80,\n    P65 = 0x82,\n    P66 = 0x84,\n    P67 = 0x86,\n    P68 = 0x88,\n    P69 = 0x8a,\n    P70 = 0x8c,\n    P71 = 0x8e,\n    P72 = 0x90,\n    P73 = 0x92,\n    P74 = 0x94,\n    P75 = 0x96,\n    P76 = 0x98,\n    P77 = 0x9a,\n    P78 = 0x9c,\n    P79 = 0x9e,\n    P80 = 0xa0,\n    P81 = 0xa2,\n    P82 = 0xa4,\n    P83 = 0xa6,\n    P84 = 0xa8,\n    P85 = 0xaa,\n    P86 = 0xac,\n    P87 = 0xae,\n    P88 = 0xb0,\n    P89 = 0xb2,\n    P90 = 0xb4,\n    P91 = 0xb6,\n    P92 = 0xb8,\n    P93 = 0xba,\n    P94 = 0xbc,\n    P95 = 0xbe,\n    P96 = 0xc0,\n    P97 = 0xc2,\n    P98 = 0xc4,\n    P99 = 0xc6,\n    P100 = 0xc8,\n    P101 = 0xca,\n    P102 = 0xcc,\n    P103 = 0xce,\n    P104 = 0xd0,\n    P105 = 0xd2,\n    P106 = 0xd4,\n    P107 = 0xd6,\n    P108 = 0xd8,\n    P109 = 0xda,\n    P110 = 0xdc,\n    P111 = 0xde,\n    P112 = 0xe0,\n    P113 = 0xe2,\n    P114 = 0xe4,\n    P115 = 0xe6,\n    P116 = 0xe8,\n    P117 = 0xea,\n    P118 = 0xec,\n    P119 = 0xee,\n    P120 = 0xf0,\n    P121 = 0xf2,\n    P122 = 0xf4,\n    P123 = 0xf6,\n    P124 = 0xf8,\n    P125 = 0xfa,\n    P126 = 0xfc,\n    P127 = 0xfe,\n}\n\n/// The interrupt priority level.\n///\n/// NOTE: The contents of this enum differ according to the set `prio-bits-*` Cargo feature.\n#[cfg(feature = \"prio-bits-8\")]\n#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\n#[repr(u8)]\n#[allow(missing_docs)]\npub enum Priority {\n    P0 = 0x0,\n    P1 = 0x1,\n    P2 = 0x2,\n    P3 = 0x3,\n    P4 = 0x4,\n    P5 = 0x5,\n    P6 = 0x6,\n    P7 = 0x7,\n    P8 = 0x8,\n    P9 = 0x9,\n    P10 = 0xa,\n    P11 = 0xb,\n    P12 = 0xc,\n    P13 = 0xd,\n    P14 = 0xe,\n    P15 = 0xf,\n    P16 = 0x10,\n    P17 = 0x11,\n    P18 = 0x12,\n    P19 = 0x13,\n    P20 = 0x14,\n    P21 = 0x15,\n    P22 = 0x16,\n    P23 = 0x17,\n    P24 = 0x18,\n    P25 = 0x19,\n    P26 = 0x1a,\n    P27 = 0x1b,\n    P28 = 0x1c,\n    P29 = 0x1d,\n    P30 = 0x1e,\n    P31 = 0x1f,\n    P32 = 0x20,\n    P33 = 0x21,\n    P34 = 0x22,\n    P35 = 0x23,\n    P36 = 0x24,\n    P37 = 0x25,\n    P38 = 0x26,\n    P39 = 0x27,\n    P40 = 0x28,\n    P41 = 0x29,\n    P42 = 0x2a,\n    P43 = 0x2b,\n    P44 = 0x2c,\n    P45 = 0x2d,\n    P46 = 0x2e,\n    P47 = 0x2f,\n    P48 = 0x30,\n    P49 = 0x31,\n    P50 = 0x32,\n    P51 = 0x33,\n    P52 = 0x34,\n    P53 = 0x35,\n    P54 = 0x36,\n    P55 = 0x37,\n    P56 = 0x38,\n    P57 = 0x39,\n    P58 = 0x3a,\n    P59 = 0x3b,\n    P60 = 0x3c,\n    P61 = 0x3d,\n    P62 = 0x3e,\n    P63 = 0x3f,\n    P64 = 0x40,\n    P65 = 0x41,\n    P66 = 0x42,\n    P67 = 0x43,\n    P68 = 0x44,\n    P69 = 0x45,\n    P70 = 0x46,\n    P71 = 0x47,\n    P72 = 0x48,\n    P73 = 0x49,\n    P74 = 0x4a,\n    P75 = 0x4b,\n    P76 = 0x4c,\n    P77 = 0x4d,\n    P78 = 0x4e,\n    P79 = 0x4f,\n    P80 = 0x50,\n    P81 = 0x51,\n    P82 = 0x52,\n    P83 = 0x53,\n    P84 = 0x54,\n    P85 = 0x55,\n    P86 = 0x56,\n    P87 = 0x57,\n    P88 = 0x58,\n    P89 = 0x59,\n    P90 = 0x5a,\n    P91 = 0x5b,\n    P92 = 0x5c,\n    P93 = 0x5d,\n    P94 = 0x5e,\n    P95 = 0x5f,\n    P96 = 0x60,\n    P97 = 0x61,\n    P98 = 0x62,\n    P99 = 0x63,\n    P100 = 0x64,\n    P101 = 0x65,\n    P102 = 0x66,\n    P103 = 0x67,\n    P104 = 0x68,\n    P105 = 0x69,\n    P106 = 0x6a,\n    P107 = 0x6b,\n    P108 = 0x6c,\n    P109 = 0x6d,\n    P110 = 0x6e,\n    P111 = 0x6f,\n    P112 = 0x70,\n    P113 = 0x71,\n    P114 = 0x72,\n    P115 = 0x73,\n    P116 = 0x74,\n    P117 = 0x75,\n    P118 = 0x76,\n    P119 = 0x77,\n    P120 = 0x78,\n    P121 = 0x79,\n    P122 = 0x7a,\n    P123 = 0x7b,\n    P124 = 0x7c,\n    P125 = 0x7d,\n    P126 = 0x7e,\n    P127 = 0x7f,\n    P128 = 0x80,\n    P129 = 0x81,\n    P130 = 0x82,\n    P131 = 0x83,\n    P132 = 0x84,\n    P133 = 0x85,\n    P134 = 0x86,\n    P135 = 0x87,\n    P136 = 0x88,\n    P137 = 0x89,\n    P138 = 0x8a,\n    P139 = 0x8b,\n    P140 = 0x8c,\n    P141 = 0x8d,\n    P142 = 0x8e,\n    P143 = 0x8f,\n    P144 = 0x90,\n    P145 = 0x91,\n    P146 = 0x92,\n    P147 = 0x93,\n    P148 = 0x94,\n    P149 = 0x95,\n    P150 = 0x96,\n    P151 = 0x97,\n    P152 = 0x98,\n    P153 = 0x99,\n    P154 = 0x9a,\n    P155 = 0x9b,\n    P156 = 0x9c,\n    P157 = 0x9d,\n    P158 = 0x9e,\n    P159 = 0x9f,\n    P160 = 0xa0,\n    P161 = 0xa1,\n    P162 = 0xa2,\n    P163 = 0xa3,\n    P164 = 0xa4,\n    P165 = 0xa5,\n    P166 = 0xa6,\n    P167 = 0xa7,\n    P168 = 0xa8,\n    P169 = 0xa9,\n    P170 = 0xaa,\n    P171 = 0xab,\n    P172 = 0xac,\n    P173 = 0xad,\n    P174 = 0xae,\n    P175 = 0xaf,\n    P176 = 0xb0,\n    P177 = 0xb1,\n    P178 = 0xb2,\n    P179 = 0xb3,\n    P180 = 0xb4,\n    P181 = 0xb5,\n    P182 = 0xb6,\n    P183 = 0xb7,\n    P184 = 0xb8,\n    P185 = 0xb9,\n    P186 = 0xba,\n    P187 = 0xbb,\n    P188 = 0xbc,\n    P189 = 0xbd,\n    P190 = 0xbe,\n    P191 = 0xbf,\n    P192 = 0xc0,\n    P193 = 0xc1,\n    P194 = 0xc2,\n    P195 = 0xc3,\n    P196 = 0xc4,\n    P197 = 0xc5,\n    P198 = 0xc6,\n    P199 = 0xc7,\n    P200 = 0xc8,\n    P201 = 0xc9,\n    P202 = 0xca,\n    P203 = 0xcb,\n    P204 = 0xcc,\n    P205 = 0xcd,\n    P206 = 0xce,\n    P207 = 0xcf,\n    P208 = 0xd0,\n    P209 = 0xd1,\n    P210 = 0xd2,\n    P211 = 0xd3,\n    P212 = 0xd4,\n    P213 = 0xd5,\n    P214 = 0xd6,\n    P215 = 0xd7,\n    P216 = 0xd8,\n    P217 = 0xd9,\n    P218 = 0xda,\n    P219 = 0xdb,\n    P220 = 0xdc,\n    P221 = 0xdd,\n    P222 = 0xde,\n    P223 = 0xdf,\n    P224 = 0xe0,\n    P225 = 0xe1,\n    P226 = 0xe2,\n    P227 = 0xe3,\n    P228 = 0xe4,\n    P229 = 0xe5,\n    P230 = 0xe6,\n    P231 = 0xe7,\n    P232 = 0xe8,\n    P233 = 0xe9,\n    P234 = 0xea,\n    P235 = 0xeb,\n    P236 = 0xec,\n    P237 = 0xed,\n    P238 = 0xee,\n    P239 = 0xef,\n    P240 = 0xf0,\n    P241 = 0xf1,\n    P242 = 0xf2,\n    P243 = 0xf3,\n    P244 = 0xf4,\n    P245 = 0xf5,\n    P246 = 0xf6,\n    P247 = 0xf7,\n    P248 = 0xf8,\n    P249 = 0xf9,\n    P250 = 0xfa,\n    P251 = 0xfb,\n    P252 = 0xfc,\n    P253 = 0xfd,\n    P254 = 0xfe,\n    P255 = 0xff,\n}\n"
  },
  {
    "path": "embassy-hal-internal/src/lib.rs",
    "content": "#![no_std]\n#![allow(clippy::new_without_default)]\n#![allow(unsafe_op_in_unsafe_fn)]\n#![doc = include_str!(\"../README.md\")]\n#![warn(missing_docs)]\n\n// This mod MUST go first, so that the others see its macros.\npub(crate) mod fmt;\n\npub mod atomic_ring_buffer;\npub mod drop;\nmod macros;\nmod peripheral;\npub mod ratio;\npub use peripheral::{Peri, PeripheralType};\n\n#[cfg(feature = \"cortex-m\")]\npub mod interrupt;\n"
  },
  {
    "path": "embassy-hal-internal/src/macros.rs",
    "content": "/// Types for the peripheral singletons.\n#[macro_export]\nmacro_rules! peripherals_definition {\n    ($($(#[$cfg:meta])? $name:ident),*$(,)?) => {\n        /// Types for the peripheral singletons.\n        pub mod peripherals {\n            $(\n                $(#[$cfg])?\n                #[allow(non_camel_case_types)]\n                #[doc = concat!(stringify!($name), \" peripheral\")]\n                #[derive(Debug)]\n                #[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\n                pub struct $name { _private: () }\n\n                $(#[$cfg])?\n                impl $name {\n                    /// Unsafely create an instance of this peripheral out of thin air.\n                    ///\n                    /// # Safety\n                    ///\n                    /// You must ensure that you're only using one instance of this type at a time.\n                    #[inline]\n                    pub const unsafe fn steal() -> $crate::Peri<'static, Self> {\n                        $crate::Peri::new_unchecked(Self{ _private: ()})\n                    }\n                }\n\n                $(#[$cfg])?\n                $crate::impl_peripheral!($name);\n            )*\n        }\n    };\n}\n\n/// Define the peripherals struct.\n#[macro_export]\nmacro_rules! peripherals_struct {\n    ($($(#[$cfg:meta])? $name:ident),*$(,)?) => {\n        /// Struct containing all the peripheral singletons.\n        ///\n        /// To obtain the peripherals, you must initialize the HAL, by calling [`crate::init`].\n        #[allow(non_snake_case)]\n        pub struct Peripherals {\n            $(\n                #[doc = concat!(stringify!($name), \" peripheral\")]\n                $(#[$cfg])?\n                pub $name: $crate::Peri<'static, peripherals::$name>,\n            )*\n        }\n\n        impl Peripherals {\n            ///Returns all the peripherals *once*\n            #[inline]\n            pub(crate) fn take() -> Self {\n                critical_section::with(Self::take_with_cs)\n            }\n\n            ///Returns all the peripherals *once*\n            #[inline]\n            pub(crate) fn take_with_cs(_cs: critical_section::CriticalSection) -> Self {\n                #[unsafe(no_mangle)]\n                static mut _EMBASSY_DEVICE_PERIPHERALS: bool = false;\n\n                // safety: OK because we're inside a CS.\n                unsafe {\n                    if _EMBASSY_DEVICE_PERIPHERALS {\n                        panic!(\"init called more than once!\")\n                    }\n                    _EMBASSY_DEVICE_PERIPHERALS = true;\n                    Self::steal()\n                }\n            }\n        }\n\n        impl Peripherals {\n            /// Unsafely create an instance of this peripheral out of thin air.\n            ///\n            /// # Safety\n            ///\n            /// You must ensure that you're only using one instance of this type at a time.\n            #[inline]\n            pub unsafe fn steal() -> Self {\n                Self {\n                    $(\n                        $(#[$cfg])?\n                        $name: peripherals::$name::steal(),\n                    )*\n                }\n            }\n        }\n    };\n}\n\n/// Defining peripheral type.\n#[macro_export]\nmacro_rules! peripherals {\n    ($($(#[$cfg:meta])? $name:ident),*$(,)?) => {\n        $crate::peripherals_definition!(\n            $(\n                $(#[$cfg])?\n                $name,\n            )*\n        );\n        $crate::peripherals_struct!(\n            $(\n                $(#[$cfg])?\n                $name,\n            )*\n        );\n    };\n}\n\n/// Implement the peripheral trait.\n#[macro_export]\nmacro_rules! impl_peripheral {\n    ($type:ident<$($T:ident $(: $bound:tt $(+ $others:tt )*)?),*>) => {\n        impl<$($T: $($bound $(+$others)*)?),*> Copy for $type <$($T),*> {}\n        impl<$($T: $($bound $(+$others)*)?),*> Clone for $type <$($T),*> {\n            fn clone(&self) -> Self {\n                *self\n            }\n        }\n        impl<$($T: $($bound $(+$others)*)?),*> PeripheralType for $type <$($T),*> {}\n    };\n\n    ($type:ident) => {\n        impl Copy for $type {}\n        impl Clone for $type {\n            fn clone(&self) -> Self {\n                *self\n            }\n        }\n        impl $crate::PeripheralType for $type {}\n    };\n}\n"
  },
  {
    "path": "embassy-hal-internal/src/peripheral.rs",
    "content": "use core::marker::PhantomData;\nuse core::ops::Deref;\n\n/// An exclusive reference to a peripheral.\n///\n/// This is functionally the same as a `&'a mut T`. There's a few advantages in having\n/// a dedicated struct instead:\n///\n/// - Memory efficiency: Peripheral singletons are typically either zero-sized (for concrete\n///   peripherals like `PA9` or `SPI4`) or very small (for example `AnyPin`, which is 1 byte).\n///   However `&mut T` is always 4 bytes for 32-bit targets, even if T is zero-sized.\n///   Peripheral stores a copy of `T` instead, so it's the same size.\n/// - Code size efficiency. If the user uses the same driver with both `SPI4` and `&mut SPI4`,\n///   the driver code would be monomorphized two times. With Peri, the driver is generic\n///   over a lifetime only. `SPI4` becomes `Peri<'static, SPI4>`, and `&mut SPI4` becomes\n///   `Peri<'a, SPI4>`. Lifetimes don't cause monomorphization.\npub struct Peri<'a, T: PeripheralType> {\n    inner: T,\n    _lifetime: PhantomData<&'a mut T>,\n}\n\nimpl<'a, T: PeripheralType> Peri<'a, T> {\n    /// Create a new owned a peripheral.\n    ///\n    /// For use by HALs only.\n    ///\n    /// If you're an end user you shouldn't use this, you should use `steal()`\n    /// on the actual peripheral types instead.\n    #[inline]\n    #[doc(hidden)]\n    pub const unsafe fn new_unchecked(inner: T) -> Self {\n        Self {\n            inner,\n            _lifetime: PhantomData,\n        }\n    }\n\n    /// Unsafely clone (duplicate) a peripheral singleton.\n    ///\n    /// # Safety\n    ///\n    /// This returns an owned clone of the peripheral. You must manually ensure\n    /// only one copy of the peripheral is in use at a time. For example, don't\n    /// create two SPI drivers on `SPI1`, because they will \"fight\" each other.\n    ///\n    /// You should strongly prefer using `reborrow()` instead. It returns a\n    /// `Peri` that borrows `self`, which allows the borrow checker\n    /// to enforce this at compile time.\n    pub const unsafe fn clone_unchecked(&self) -> Peri<'a, T> {\n        Peri::new_unchecked(self.inner)\n    }\n\n    /// Reborrow into a \"child\" Peri.\n    ///\n    /// `self` will stay borrowed until the child Peripheral is dropped.\n    pub const fn reborrow(&mut self) -> Peri<'_, T> {\n        // safety: we're returning the clone inside a new Peripheral that borrows\n        // self, so user code can't use both at the same time.\n        unsafe { self.clone_unchecked() }\n    }\n\n    /// Map the inner peripheral using `Into`.\n    ///\n    /// This converts from `Peri<'a, T>` to `Peri<'a, U>`, using an\n    /// `Into` impl to convert from `T` to `U`.\n    ///\n    /// For example, this can be useful to.into() GPIO pins: converting from Peri<'a, PB11>` to `Peri<'a, AnyPin>`.\n    #[inline]\n    pub fn into<U>(self) -> Peri<'a, U>\n    where\n        T: Into<U>,\n        U: PeripheralType,\n    {\n        unsafe { Peri::new_unchecked(self.inner.into()) }\n    }\n}\n\nimpl<'a, T: PeripheralType> Deref for Peri<'a, T> {\n    type Target = T;\n\n    #[inline]\n    fn deref(&self) -> &Self::Target {\n        &self.inner\n    }\n}\n\nimpl<'a, T: PeripheralType + core::fmt::Debug> core::fmt::Debug for Peri<'a, T> {\n    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {\n        self.inner.fmt(f)\n    }\n}\n\nimpl<'a, T: PeripheralType + core::fmt::Display> core::fmt::Display for Peri<'a, T> {\n    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {\n        self.inner.fmt(f)\n    }\n}\n\n#[cfg(feature = \"defmt\")]\nimpl<'a, T: PeripheralType + defmt::Format> defmt::Format for Peri<'a, T> {\n    fn format(&self, fmt: defmt::Formatter) {\n        self.inner.format(fmt);\n    }\n}\n\n/// Marker trait for peripheral types.\npub trait PeripheralType: Copy + Sized {}\n"
  },
  {
    "path": "embassy-hal-internal/src/ratio.rs",
    "content": "//! Types for dealing with rational numbers.\nuse core::ops::{Add, Div, Mul};\n\nuse num_traits::{CheckedAdd, CheckedDiv, CheckedMul};\n\n/// Represents the ratio between two numbers.\n#[derive(Copy, Clone, Debug)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub struct Ratio<T> {\n    /// Numerator.\n    numer: T,\n    /// Denominator.\n    denom: T,\n}\n\nimpl<T> Ratio<T> {\n    /// Creates a new `Ratio`.\n    #[inline(always)]\n    pub const fn new_raw(numer: T, denom: T) -> Ratio<T> {\n        Ratio { numer, denom }\n    }\n\n    /// Gets an immutable reference to the numerator.\n    #[inline(always)]\n    pub const fn numer(&self) -> &T {\n        &self.numer\n    }\n\n    /// Gets an immutable reference to the denominator.\n    #[inline(always)]\n    pub const fn denom(&self) -> &T {\n        &self.denom\n    }\n}\n\nimpl<T: CheckedDiv> Ratio<T> {\n    /// Converts to an integer, rounding towards zero.\n    #[inline(always)]\n    pub fn to_integer(&self) -> T {\n        unwrap!(self.numer().checked_div(self.denom()))\n    }\n}\n\nimpl<T: CheckedMul> Div<T> for Ratio<T> {\n    type Output = Self;\n\n    #[inline(always)]\n    fn div(mut self, rhs: T) -> Self::Output {\n        self.denom = unwrap!(self.denom().checked_mul(&rhs));\n        self\n    }\n}\n\nimpl<T: CheckedMul> Mul<T> for Ratio<T> {\n    type Output = Self;\n\n    #[inline(always)]\n    fn mul(mut self, rhs: T) -> Self::Output {\n        self.numer = unwrap!(self.numer().checked_mul(&rhs));\n        self\n    }\n}\n\nimpl<T: CheckedMul + CheckedAdd> Add<T> for Ratio<T> {\n    type Output = Self;\n\n    #[inline(always)]\n    fn add(mut self, rhs: T) -> Self::Output {\n        self.numer = unwrap!(unwrap!(self.denom().checked_mul(&rhs)).checked_add(self.numer()));\n        self\n    }\n}\n\nmacro_rules! impl_from_for_float {\n    ($from:ident) => {\n        impl From<Ratio<$from>> for f32 {\n            #[inline(always)]\n            fn from(r: Ratio<$from>) -> Self {\n                (r.numer as f32) / (r.denom as f32)\n            }\n        }\n\n        impl From<Ratio<$from>> for f64 {\n            #[inline(always)]\n            fn from(r: Ratio<$from>) -> Self {\n                (r.numer as f64) / (r.denom as f64)\n            }\n        }\n    };\n}\n\nimpl_from_for_float!(u8);\nimpl_from_for_float!(u16);\nimpl_from_for_float!(u32);\nimpl_from_for_float!(u64);\nimpl_from_for_float!(u128);\nimpl_from_for_float!(i8);\nimpl_from_for_float!(i16);\nimpl_from_for_float!(i32);\nimpl_from_for_float!(i64);\nimpl_from_for_float!(i128);\n\nimpl<T: core::fmt::Display> core::fmt::Display for Ratio<T> {\n    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {\n        core::write!(f, \"{} / {}\", self.numer(), self.denom())\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use super::Ratio;\n\n    #[test]\n    fn basics() {\n        let mut r = Ratio::new_raw(1, 2) + 2;\n        assert_eq!(*r.numer(), 5);\n        assert_eq!(*r.denom(), 2);\n        assert_eq!(r.to_integer(), 2);\n\n        r = r * 2;\n        assert_eq!(*r.numer(), 10);\n        assert_eq!(*r.denom(), 2);\n        assert_eq!(r.to_integer(), 5);\n\n        r = r / 2;\n        assert_eq!(*r.numer(), 10);\n        assert_eq!(*r.denom(), 4);\n        assert_eq!(r.to_integer(), 2);\n    }\n}\n"
  },
  {
    "path": "embassy-imxrt/CHANGELOG.md",
    "content": "# Changelog\n\nAll notable changes to this project will be documented in this file.\n\nThe format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),\nand this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).\n\n<!-- next-header -->\n## Unreleased - ReleaseDate\n\n- First release with changelog.\n"
  },
  {
    "path": "embassy-imxrt/Cargo.toml",
    "content": "[package]\nname = \"embassy-imxrt\"\nversion = \"0.1.0\"\nedition = \"2024\"\nlicense = \"MIT OR Apache-2.0\"\ndescription = \"Embassy Hardware Abstraction Layer (HAL) for the IMXRT microcontroller\"\nkeywords = [\"embedded\", \"async\", \"imxrt\", \"rt600\", \"embedded-hal\"]\ncategories = [\"embedded\", \"hardware-support\", \"no-std\", \"asynchronous\"]\nrepository = \"https://github.com/embassy-rs/embassy\"\ndocumentation = \"https://docs.embassy.dev/embassy-imxrt\"\n\npublish = false\n[package.metadata.embassy]\nbuild = [\n    {target = \"thumbv8m.main-none-eabihf\", features = [\"defmt\", \"mimxrt633s\", \"time\", \"time-driver-rtc\", \"unstable-pac\"]},\n    {target = \"thumbv8m.main-none-eabihf\", features = [\"defmt\", \"mimxrt685s\", \"time\", \"time-driver-rtc\", \"unstable-pac\"]},\n]\n\n[package.metadata.embassy_docs]\nsrc_base = \"https://github.com/embassy-rs/embassy/blob/embassy-imxrt-v$VERSION/embassy-imxrt/src/\"\nsrc_base_git = \"https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-imxrt/src/\"\nfeatures = [\"defmt\", \"unstable-pac\", \"time\", \"time-driver-os-timer\"]\nflavors = [\n    { regex_feature = \"mimxrt6.*\", target = \"thumbv8m.main-none-eabihf\" }\n]\n\n[package.metadata.docs.rs]\nfeatures = [\"mimxrt685s\", \"defmt\", \"unstable-pac\", \"time\", \"time-driver\"]\nrustdoc-args = [\"--cfg\", \"docsrs\"]\n\n[features]\ndefault = [\"rt\"]\n\n## Cortex-M runtime (enabled by default)\nrt = [\n    \"mimxrt685s-pac?/rt\",\n    \"mimxrt633s-pac?/rt\",\n]\n\n## Enable defmt\ndefmt = [\"dep:defmt\", \"embassy-hal-internal/defmt\", \"embassy-sync/defmt\", \"mimxrt685s-pac?/defmt\", \"mimxrt633s-pac?/defmt\"]\n\n## Enable features requiring `embassy-time`\ntime = [\"dep:embassy-time\", \"embassy-embedded-hal/time\"]\n\n## Enable custom embassy time-driver implementation, using 32KHz RTC\ntime-driver-rtc = [\"_time-driver\", \"embassy-time-driver?/tick-hz-1_000\"]\n\n## Enable custom embassy time-driver implementation, using 1MHz OS Timer\ntime-driver-os-timer = [\"_time-driver\", \"embassy-time-driver?/tick-hz-1_000_000\"]\n\n_time-driver = [\"dep:embassy-time-driver\", \"dep:embassy-time-queue-utils\", \"embassy-embedded-hal/time\"]\n\n## Reexport the PAC for the currently enabled chip at `embassy_imxrt::pac` (unstable)\nunstable-pac = []\n\n# Features starting with `_` are for internal use only. They're not intended\n# to be enabled by other crates, and are not covered by semver guarantees.\n\n_mimxrt685s = []\n_mimxrt633s = [\"_espi\"]\n\n# Peripherals\n_espi = []\n\n#! ### Chip selection features\n## MIMXRT685S\nmimxrt685s = [\"mimxrt685s-pac\", \"_mimxrt685s\"]\n## MIMXRT633S\nmimxrt633s = [\"mimxrt633s-pac\", \"_mimxrt633s\"]\n\n[dependencies]\nembassy-sync = { version = \"0.8.0\", path = \"../embassy-sync\" }\nembassy-time-driver = { version = \"0.2.2\", path = \"../embassy-time-driver\", optional = true }\nembassy-time-queue-utils = { version = \"0.3.0\", path = \"../embassy-time-queue-utils\", optional = true }\nembassy-time = { version = \"0.5.1\", path = \"../embassy-time\", optional = true }\nembassy-hal-internal = { version = \"0.5.0\", path = \"../embassy-hal-internal\", features = [\"cortex-m\", \"prio-bits-3\"] }\nembassy-embedded-hal = { version = \"0.6.0\", path = \"../embassy-embedded-hal\", default-features = false }\nembassy-futures = { version = \"0.1.2\", path = \"../embassy-futures\" }\n\ndefmt = { version = \"1.0.1\", optional = true }\nlog = { version = \"0.4.14\", optional = true }\nnb = \"1.0.0\"\ncfg-if = \"1.0.0\"\ncortex-m-rt = \">=0.7.3,<0.8\"\ncortex-m = \"0.7.6\"\ncritical-section = \"1.1\"\nembedded-io = { version = \"0.7.1\" }\nembedded-io-async = { version = \"0.7.0\" }\nfixed = \"1.23.1\"\n\nrand-core-06 = { package = \"rand_core\", version = \"0.6\" }\nrand-core-09 = { package = \"rand_core\", version = \"0.9\" }\n\nembedded-hal-02 = { package = \"embedded-hal\", version = \"0.2.6\", features = [\n    \"unproven\",\n] }\nembedded-hal-1 = { package = \"embedded-hal\", version = \"1.0\" }\nembedded-hal-async = { version = \"1.0\" }\nembedded-hal-nb = { version = \"1.0\" }\n\ndocument-features = \"0.2.7\"\npaste = \"1.0\"\n\n# PACs\nmimxrt685s-pac = { version = \"0.5.0\", optional = true, features = [\"rt\", \"critical-section\"] }\nmimxrt633s-pac = { version = \"0.5.0\", optional = true, features = [\"rt\", \"critical-section\"] }\n"
  },
  {
    "path": "embassy-imxrt/README.md",
    "content": "# Embassy iMXRT HAL\n\n## Introduction\n\nHALs implement safe, idiomatic Rust APIs to use the hardware capabilities, so\nraw register manipulation is not needed.\n\nThe Embassy iMXRT HAL targets the NXP iMXRT Family of MCUs. The HAL implements\nboth blocking and async APIs for many peripherals. The benefit of using the\nasync APIs is that the HAL takes care of waiting for peripherals to complete\noperations in low power mode and handling of interrupts, so that applications\ncan focus on business logic.\n\nNOTE: The Embassy HALs can be used both for non-async and async operations. For\nasync, you can choose which runtime you want to use.\n\nFor a complete list of available peripherals and features, see the\n[embassy-imxrt documentation](https://docs.embassy.dev/embassy-imxrt).\n\n## Hardware support\n\nThe `embassy-imxrt` HAL currently supports two main variants of the iMXRT\nfamily:\n\n* MIMXRT685S\n  ([examples](https://github.com/OpenDevicePartnership/embassy-imxrt/tree/main/examples/rt685s-evk))\n* MIMXRT633s\n  ([examples](https://github.com/OpenDevicePartnership/embassy-imxrt/tree/main/examples/rt633))\n\nSeveral peripherals are supported and tested on both supported chip variants. To\ncheck what's available, make sure to the MCU you're targetting in the top menu\nin the [documentation](https://docs.embassy.dev/embassy-imxrt).\n\n## TrustZone support\n\nTrustZone support is yet to be implemented.\n\n## Time driver\n\nIf the `time-driver` feature is enabled, the HAL uses the RTC peripheral as a\nglobal time driver for [embassy-time](https://crates.io/crates/embassy-time),\nwith a tick rate of 32768 Hz.\n\n## Embedded-hal\n\nThe `embassy-imxrt` HAL implements the traits from\n[embedded-hal](https://crates.io/crates/embedded-hal) (v0.2 and 1.0) and\n[embedded-hal-async](https://crates.io/crates/embedded-hal-async), as well as\n[embedded-io](https://crates.io/crates/embedded-io) and\n[embedded-io-async](https://crates.io/crates/embedded-io-async).\n\n## Interoperability\n\nThis crate can run on any executor.\n\nOptionally, some features requiring\n[`embassy-time`](https://crates.io/crates/embassy-time) can be activated with\nthe `time` feature. If you enable it, you must link an `embassy-time` driver in\nyour project.\n"
  },
  {
    "path": "embassy-imxrt/src/chips/mimxrt633s.rs",
    "content": "pub use mimxrt633s_pac as pac;\n\n#[allow(clippy::missing_safety_doc)]\npub mod interrupts {\n    embassy_hal_internal::interrupt_mod!(\n        ACMP,\n        ADC0,\n        CASPER,\n        CTIMER0,\n        CTIMER1,\n        CTIMER2,\n        CTIMER3,\n        CTIMER4,\n        DMA0,\n        DMA1,\n        DMIC0,\n        ESPI,\n        FLEXCOMM0,\n        FLEXCOMM1,\n        FLEXCOMM14,\n        FLEXCOMM15,\n        FLEXCOMM2,\n        FLEXCOMM3,\n        FLEXCOMM4,\n        FLEXCOMM5,\n        FLEXCOMM6,\n        FLEXCOMM7,\n        FLEXSPI,\n        GPIO_INTA,\n        GPIO_INTB,\n        HASHCRYPT,\n        HWVAD0,\n        HYPERVISOR,\n        I3C0,\n        MRT0,\n        MU_A,\n        OS_EVENT,\n        PIN_INT0,\n        PIN_INT1,\n        PIN_INT2,\n        PIN_INT3,\n        PIN_INT4,\n        PIN_INT5,\n        PIN_INT6,\n        PIN_INT7,\n        PMC_PMIC,\n        POWERQUAD,\n        PUF,\n        RNG,\n        RTC,\n        SCT0,\n        SECUREVIOLATION,\n        SGPIO_INTA,\n        SGPIO_INTB,\n        USB,\n        USBPHY_DCD,\n        USB_WAKEUP,\n        USDHC0,\n        USDHC1,\n        UTICK0,\n        WDT0,\n        WDT1,\n    );\n}\n\nembassy_hal_internal::peripherals!(\n    ACMP,\n    ADC0,\n    CASPER,\n    CRC,\n    CTIMER0_COUNT_CHANNEL0,\n    CTIMER0_COUNT_CHANNEL1,\n    CTIMER0_COUNT_CHANNEL2,\n    CTIMER0_COUNT_CHANNEL3,\n    CTIMER0_CAPTURE_CHANNEL0,\n    CTIMER0_CAPTURE_CHANNEL1,\n    CTIMER0_CAPTURE_CHANNEL2,\n    CTIMER0_CAPTURE_CHANNEL3,\n    CTIMER1_COUNT_CHANNEL0,\n    CTIMER1_COUNT_CHANNEL1,\n    CTIMER1_COUNT_CHANNEL2,\n    CTIMER1_COUNT_CHANNEL3,\n    CTIMER1_CAPTURE_CHANNEL0,\n    CTIMER1_CAPTURE_CHANNEL1,\n    CTIMER1_CAPTURE_CHANNEL2,\n    CTIMER1_CAPTURE_CHANNEL3,\n    CTIMER2_COUNT_CHANNEL0,\n    CTIMER2_COUNT_CHANNEL1,\n    CTIMER2_COUNT_CHANNEL2,\n    CTIMER2_COUNT_CHANNEL3,\n    CTIMER2_CAPTURE_CHANNEL0,\n    CTIMER2_CAPTURE_CHANNEL1,\n    CTIMER2_CAPTURE_CHANNEL2,\n    CTIMER2_CAPTURE_CHANNEL3,\n    CTIMER3_COUNT_CHANNEL0,\n    CTIMER3_COUNT_CHANNEL1,\n    CTIMER3_COUNT_CHANNEL2,\n    CTIMER3_COUNT_CHANNEL3,\n    CTIMER3_CAPTURE_CHANNEL0,\n    CTIMER3_CAPTURE_CHANNEL1,\n    CTIMER3_CAPTURE_CHANNEL2,\n    CTIMER3_CAPTURE_CHANNEL3,\n    CTIMER4_COUNT_CHANNEL0,\n    CTIMER4_COUNT_CHANNEL1,\n    CTIMER4_COUNT_CHANNEL2,\n    CTIMER4_COUNT_CHANNEL3,\n    CTIMER4_CAPTURE_CHANNEL0,\n    CTIMER4_CAPTURE_CHANNEL1,\n    CTIMER4_CAPTURE_CHANNEL2,\n    CTIMER4_CAPTURE_CHANNEL3,\n    DMA0,\n    DMA0_CH0,\n    DMA0_CH1,\n    DMA0_CH2,\n    DMA0_CH3,\n    DMA0_CH4,\n    DMA0_CH5,\n    DMA0_CH6,\n    DMA0_CH7,\n    DMA0_CH8,\n    DMA0_CH9,\n    DMA0_CH10,\n    DMA0_CH11,\n    DMA0_CH12,\n    DMA0_CH13,\n    DMA0_CH14,\n    DMA0_CH15,\n    DMA0_CH16,\n    DMA0_CH17,\n    DMA0_CH18,\n    DMA0_CH19,\n    DMA0_CH20,\n    DMA0_CH21,\n    DMA0_CH22,\n    DMA0_CH23,\n    DMA0_CH24,\n    DMA0_CH25,\n    DMA0_CH26,\n    DMA0_CH27,\n    DMA0_CH28,\n    DMA0_CH29,\n    DMA0_CH30,\n    DMA0_CH31,\n    DMA0_CH32,\n    DMA1,\n    DMA1_CH0,\n    DMA1_CH1,\n    DMA1_CH2,\n    DMA1_CH3,\n    DMA1_CH4,\n    DMA1_CH5,\n    DMA1_CH6,\n    DMA1_CH7,\n    DMA1_CH8,\n    DMA1_CH9,\n    DMA1_CH10,\n    DMA1_CH11,\n    DMA1_CH12,\n    DMA1_CH13,\n    DMA1_CH14,\n    DMA1_CH15,\n    DMA1_CH16,\n    DMA1_CH17,\n    DMA1_CH18,\n    DMA1_CH19,\n    DMA1_CH20,\n    DMA1_CH21,\n    DMA1_CH22,\n    DMA1_CH23,\n    DMA1_CH24,\n    DMA1_CH25,\n    DMA1_CH26,\n    DMA1_CH27,\n    DMA1_CH28,\n    DMA1_CH29,\n    DMA1_CH30,\n    DMA1_CH31,\n    DMA1_CH32,\n    DMIC0,\n    DSPWAKE,\n    ESPI,\n    FLEXCOMM0,\n    FLEXCOMM1,\n    FLEXCOMM14,\n    FLEXCOMM15,\n    FLEXCOMM2,\n    FLEXCOMM3,\n    FLEXCOMM4,\n    FLEXCOMM5,\n    FLEXCOMM6,\n    FLEXCOMM7,\n    FLEXSPI,\n    FREQME,\n    GPIO_INTA,\n    GPIO_INTB,\n    HASHCRYPT,\n    HSGPIO0,\n    HSGPIO1,\n    HSGPIO2,\n    HSGPIO3,\n    HSGPIO4,\n    HSGPIO5,\n    HSGPIO6,\n    HSGPIO7,\n    HWVAD0,\n    HYPERVISOR,\n    I3C0,\n    MRT0,\n    MU_A,\n    OS_EVENT,\n    PIN_INT0,\n    PIN_INT1,\n    PIN_INT2,\n    PIN_INT3,\n    PIN_INT4,\n    PIN_INT5,\n    PIN_INT6,\n    PIN_INT7,\n    PIO0_0,\n    PIO0_1,\n    PIO0_10,\n    PIO0_11,\n    PIO0_12,\n    PIO0_13,\n    PIO0_14,\n    PIO0_15,\n    PIO0_16,\n    PIO0_17,\n    PIO0_18,\n    PIO0_19,\n    PIO0_2,\n    PIO0_20,\n    PIO0_21,\n    PIO0_22,\n    PIO0_23,\n    PIO0_24,\n    PIO0_25,\n    PIO0_26,\n    PIO0_27,\n    PIO0_28,\n    PIO0_29,\n    PIO0_3,\n    PIO0_30,\n    PIO0_31,\n    PIO0_4,\n    PIO0_5,\n    PIO0_6,\n    PIO0_7,\n    PIO0_8,\n    PIO0_9,\n    PIO1_0,\n    PIO1_1,\n    PIO1_10,\n    PIO1_11,\n    PIO1_12,\n    PIO1_13,\n    PIO1_14,\n    PIO1_15,\n    PIO1_16,\n    PIO1_17,\n    PIO1_18,\n    PIO1_19,\n    PIO1_2,\n    PIO1_20,\n    PIO1_21,\n    PIO1_22,\n    PIO1_23,\n    PIO1_24,\n    PIO1_25,\n    PIO1_26,\n    PIO1_27,\n    PIO1_28,\n    PIO1_29,\n    PIO1_3,\n    PIO1_30,\n    PIO1_31,\n    PIO1_4,\n    PIO1_5,\n    PIO1_6,\n    PIO1_7,\n    PIO1_8,\n    PIO1_9,\n    PIO2_0,\n    PIO2_1,\n    PIO2_10,\n    PIO2_11,\n    PIO2_12,\n    PIO2_13,\n    PIO2_14,\n    PIO2_15,\n    PIO2_16,\n    PIO2_17,\n    PIO2_18,\n    PIO2_19,\n    PIO2_2,\n    PIO2_20,\n    PIO2_21,\n    PIO2_22,\n    PIO2_23,\n    PIO2_24,\n    PIO2_25,\n    PIO2_26,\n    PIO2_27,\n    PIO2_28,\n    PIO2_29,\n    PIO2_3,\n    PIO2_30,\n    PIO2_31,\n    PIO2_4,\n    PIO2_5,\n    PIO2_6,\n    PIO2_7,\n    PIO2_8,\n    PIO2_9,\n    PIO3_0,\n    PIO3_1,\n    PIO3_10,\n    PIO3_11,\n    PIO3_12,\n    PIO3_13,\n    PIO3_14,\n    PIO3_15,\n    PIO3_16,\n    PIO3_17,\n    PIO3_18,\n    PIO3_19,\n    PIO3_2,\n    PIO3_20,\n    PIO3_21,\n    PIO3_22,\n    PIO3_23,\n    PIO3_24,\n    PIO3_25,\n    PIO3_26,\n    PIO3_27,\n    PIO3_28,\n    PIO3_29,\n    PIO3_3,\n    PIO3_30,\n    PIO3_31,\n    PIO3_4,\n    PIO3_5,\n    PIO3_6,\n    PIO3_7,\n    PIO3_8,\n    PIO3_9,\n    PIO4_0,\n    PIO4_1,\n    PIO4_10,\n    PIO4_2,\n    PIO4_3,\n    PIO4_4,\n    PIO4_5,\n    PIO4_6,\n    PIO4_7,\n    PIO4_8,\n    PIO4_9,\n    PIO7_24,\n    PIO7_25,\n    PIO7_26,\n    PIO7_27,\n    PIO7_28,\n    PIO7_29,\n    PIO7_30,\n    PIO7_31,\n    PIOFC15_SCL,\n    PIOFC15_SDA,\n    PMC_PMIC,\n    PIMCTL,\n    POWERQUAD,\n    PUF,\n    RNG,\n    RTC,\n    SCT0,\n    SECGPIO,\n    SECUREVIOLATION,\n    SEMA42,\n    SGPIO_INTA,\n    SGPIO_INTB,\n    USBHSD,\n    USBHSH,\n    USBPHY,\n    USB_WAKEUP,\n    USDHC0,\n    USDHC1,\n    UTICK0,\n    WDT0,\n    WDT1,\n);\n"
  },
  {
    "path": "embassy-imxrt/src/chips/mimxrt685s.rs",
    "content": "pub use mimxrt685s_pac as pac;\n\n#[allow(clippy::missing_safety_doc)]\npub mod interrupts {\n    embassy_hal_internal::interrupt_mod!(\n        ACMP,\n        ADC0,\n        CASPER,\n        CTIMER0,\n        CTIMER1,\n        CTIMER2,\n        CTIMER3,\n        CTIMER4,\n        DMA0,\n        DMA1,\n        DMIC0,\n        DSPWAKE,\n        FLEXCOMM0,\n        FLEXCOMM1,\n        FLEXCOMM14,\n        FLEXCOMM15,\n        FLEXCOMM2,\n        FLEXCOMM3,\n        FLEXCOMM4,\n        FLEXCOMM5,\n        FLEXCOMM6,\n        FLEXCOMM7,\n        FLEXSPI,\n        GPIO_INTA,\n        GPIO_INTB,\n        HASHCRYPT,\n        HWVAD0,\n        HYPERVISOR,\n        I3C0,\n        MRT0,\n        MU_A,\n        OS_EVENT,\n        PIN_INT0,\n        PIN_INT1,\n        PIN_INT2,\n        PIN_INT3,\n        PIN_INT4,\n        PIN_INT5,\n        PIN_INT6,\n        PIN_INT7,\n        PMC_PMIC,\n        POWERQUAD,\n        PUF,\n        RNG,\n        RTC,\n        SCT0,\n        SECUREVIOLATION,\n        SGPIO_INTA,\n        SGPIO_INTB,\n        USB,\n        USBPHY_DCD,\n        USB_WAKEUP,\n        USDHC0,\n        USDHC1,\n        UTICK0,\n        WDT0,\n        WDT1,\n    );\n}\n\nembassy_hal_internal::peripherals!(\n    ACMP,\n    ADC0,\n    CASPER,\n    CRC,\n    CTIMER0_COUNT_CHANNEL0,\n    CTIMER0_COUNT_CHANNEL1,\n    CTIMER0_COUNT_CHANNEL2,\n    CTIMER0_COUNT_CHANNEL3,\n    CTIMER0_CAPTURE_CHANNEL0,\n    CTIMER0_CAPTURE_CHANNEL1,\n    CTIMER0_CAPTURE_CHANNEL2,\n    CTIMER0_CAPTURE_CHANNEL3,\n    CTIMER1_COUNT_CHANNEL0,\n    CTIMER1_COUNT_CHANNEL1,\n    CTIMER1_COUNT_CHANNEL2,\n    CTIMER1_COUNT_CHANNEL3,\n    CTIMER1_CAPTURE_CHANNEL0,\n    CTIMER1_CAPTURE_CHANNEL1,\n    CTIMER1_CAPTURE_CHANNEL2,\n    CTIMER1_CAPTURE_CHANNEL3,\n    CTIMER2_COUNT_CHANNEL0,\n    CTIMER2_COUNT_CHANNEL1,\n    CTIMER2_COUNT_CHANNEL2,\n    CTIMER2_COUNT_CHANNEL3,\n    CTIMER2_CAPTURE_CHANNEL0,\n    CTIMER2_CAPTURE_CHANNEL1,\n    CTIMER2_CAPTURE_CHANNEL2,\n    CTIMER2_CAPTURE_CHANNEL3,\n    CTIMER3_COUNT_CHANNEL0,\n    CTIMER3_COUNT_CHANNEL1,\n    CTIMER3_COUNT_CHANNEL2,\n    CTIMER3_COUNT_CHANNEL3,\n    CTIMER3_CAPTURE_CHANNEL0,\n    CTIMER3_CAPTURE_CHANNEL1,\n    CTIMER3_CAPTURE_CHANNEL2,\n    CTIMER3_CAPTURE_CHANNEL3,\n    CTIMER4_COUNT_CHANNEL0,\n    CTIMER4_COUNT_CHANNEL1,\n    CTIMER4_COUNT_CHANNEL2,\n    CTIMER4_COUNT_CHANNEL3,\n    CTIMER4_CAPTURE_CHANNEL0,\n    CTIMER4_CAPTURE_CHANNEL1,\n    CTIMER4_CAPTURE_CHANNEL2,\n    CTIMER4_CAPTURE_CHANNEL3,\n    DMA0,\n    DMA0_CH0,\n    DMA0_CH1,\n    DMA0_CH2,\n    DMA0_CH3,\n    DMA0_CH4,\n    DMA0_CH5,\n    DMA0_CH6,\n    DMA0_CH7,\n    DMA0_CH8,\n    DMA0_CH9,\n    DMA0_CH10,\n    DMA0_CH11,\n    DMA0_CH12,\n    DMA0_CH13,\n    DMA0_CH14,\n    DMA0_CH15,\n    DMA0_CH16,\n    DMA0_CH17,\n    DMA0_CH18,\n    DMA0_CH19,\n    DMA0_CH20,\n    DMA0_CH21,\n    DMA0_CH22,\n    DMA0_CH23,\n    DMA0_CH24,\n    DMA0_CH25,\n    DMA0_CH26,\n    DMA0_CH27,\n    DMA0_CH28,\n    DMA0_CH29,\n    DMA0_CH30,\n    DMA0_CH31,\n    DMA0_CH32,\n    DMA1,\n    DMA1_CH0,\n    DMA1_CH1,\n    DMA1_CH2,\n    DMA1_CH3,\n    DMA1_CH4,\n    DMA1_CH5,\n    DMA1_CH6,\n    DMA1_CH7,\n    DMA1_CH8,\n    DMA1_CH9,\n    DMA1_CH10,\n    DMA1_CH11,\n    DMA1_CH12,\n    DMA1_CH13,\n    DMA1_CH14,\n    DMA1_CH15,\n    DMA1_CH16,\n    DMA1_CH17,\n    DMA1_CH18,\n    DMA1_CH19,\n    DMA1_CH20,\n    DMA1_CH21,\n    DMA1_CH22,\n    DMA1_CH23,\n    DMA1_CH24,\n    DMA1_CH25,\n    DMA1_CH26,\n    DMA1_CH27,\n    DMA1_CH28,\n    DMA1_CH29,\n    DMA1_CH30,\n    DMA1_CH31,\n    DMA1_CH32,\n    DMIC0,\n    DSPWAKE,\n    FLEXCOMM0,\n    FLEXCOMM1,\n    FLEXCOMM14,\n    FLEXCOMM15,\n    FLEXCOMM2,\n    FLEXCOMM3,\n    FLEXCOMM4,\n    FLEXCOMM5,\n    FLEXCOMM6,\n    FLEXCOMM7,\n    FLEXSPI,\n    FREQME,\n    GPIO_INTA,\n    GPIO_INTB,\n    HASHCRYPT,\n    HSGPIO0,\n    HSGPIO1,\n    HSGPIO2,\n    HSGPIO3,\n    HSGPIO4,\n    HSGPIO5,\n    HSGPIO6,\n    HSGPIO7,\n    HWVAD0,\n    HYPERVISOR,\n    I3C0,\n    MRT0,\n    MU_A,\n    OS_EVENT,\n    PIN_INT0,\n    PIN_INT1,\n    PIN_INT2,\n    PIN_INT3,\n    PIN_INT4,\n    PIN_INT5,\n    PIN_INT6,\n    PIN_INT7,\n    PIO0_0,\n    PIO0_1,\n    PIO0_10,\n    PIO0_11,\n    PIO0_12,\n    PIO0_13,\n    PIO0_14,\n    PIO0_15,\n    PIO0_16,\n    PIO0_17,\n    PIO0_18,\n    PIO0_19,\n    PIO0_2,\n    PIO0_20,\n    PIO0_21,\n    PIO0_22,\n    PIO0_23,\n    PIO0_24,\n    PIO0_25,\n    PIO0_26,\n    PIO0_27,\n    PIO0_28,\n    PIO0_29,\n    PIO0_3,\n    PIO0_30,\n    PIO0_31,\n    PIO0_4,\n    PIO0_5,\n    PIO0_6,\n    PIO0_7,\n    PIO0_8,\n    PIO0_9,\n    PIO1_0,\n    PIO1_1,\n    PIO1_10,\n    PIO1_11,\n    PIO1_12,\n    PIO1_13,\n    PIO1_14,\n    PIO1_15,\n    PIO1_16,\n    PIO1_17,\n    PIO1_18,\n    PIO1_19,\n    PIO1_2,\n    PIO1_20,\n    PIO1_21,\n    PIO1_22,\n    PIO1_23,\n    PIO1_24,\n    PIO1_25,\n    PIO1_26,\n    PIO1_27,\n    PIO1_28,\n    PIO1_29,\n    PIO1_3,\n    PIO1_30,\n    PIO1_31,\n    PIO1_4,\n    PIO1_5,\n    PIO1_6,\n    PIO1_7,\n    PIO1_8,\n    PIO1_9,\n    PIO2_0,\n    PIO2_1,\n    PIO2_10,\n    PIO2_11,\n    PIO2_12,\n    PIO2_13,\n    PIO2_14,\n    PIO2_15,\n    PIO2_16,\n    PIO2_17,\n    PIO2_18,\n    PIO2_19,\n    PIO2_2,\n    PIO2_20,\n    PIO2_21,\n    PIO2_22,\n    PIO2_23,\n    PIO2_24,\n    PIO2_25,\n    PIO2_26,\n    PIO2_27,\n    PIO2_28,\n    PIO2_29,\n    PIO2_3,\n    PIO2_30,\n    PIO2_31,\n    PIO2_4,\n    PIO2_5,\n    PIO2_6,\n    PIO2_7,\n    PIO2_8,\n    PIO2_9,\n    PIO3_0,\n    PIO3_1,\n    PIO3_10,\n    PIO3_11,\n    PIO3_12,\n    PIO3_13,\n    PIO3_14,\n    PIO3_15,\n    PIO3_16,\n    PIO3_17,\n    PIO3_18,\n    PIO3_19,\n    PIO3_2,\n    PIO3_20,\n    PIO3_21,\n    PIO3_22,\n    PIO3_23,\n    PIO3_24,\n    PIO3_25,\n    PIO3_26,\n    PIO3_27,\n    PIO3_28,\n    PIO3_29,\n    PIO3_3,\n    PIO3_30,\n    PIO3_31,\n    PIO3_4,\n    PIO3_5,\n    PIO3_6,\n    PIO3_7,\n    PIO3_8,\n    PIO3_9,\n    PIO4_0,\n    PIO4_1,\n    PIO4_10,\n    PIO4_2,\n    PIO4_3,\n    PIO4_4,\n    PIO4_5,\n    PIO4_6,\n    PIO4_7,\n    PIO4_8,\n    PIO4_9,\n    PIO7_24,\n    PIO7_25,\n    PIO7_26,\n    PIO7_27,\n    PIO7_28,\n    PIO7_29,\n    PIO7_30,\n    PIO7_31,\n    PIOFC15_SCL,\n    PIOFC15_SDA,\n    PMC_PMIC,\n    PIMCTL,\n    POWERQUAD,\n    PUF,\n    RNG,\n    RTC,\n    SCT0,\n    SECGPIO,\n    SECUREVIOLATION,\n    SEMA42,\n    SGPIO_INTA,\n    SGPIO_INTB,\n    USBHSD,\n    USBHSH,\n    USBPHY,\n    USB_WAKEUP,\n    USDHC0,\n    USDHC1,\n    UTICK0,\n    WDT0,\n    WDT1,\n);\n"
  },
  {
    "path": "embassy-imxrt/src/clocks.rs",
    "content": "//! Clock configuration for the `RT6xx`\nuse core::sync::atomic::{AtomicU8, AtomicU32, Ordering};\n\nuse paste::paste;\n\nuse crate::pac;\n\n/// Clock configuration;\n#[derive(Clone, Copy, Debug, PartialEq, Eq)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub enum Clocks {\n    /// Low power oscillator\n    Lposc,\n    /// System Frequency Resonance Oscillator (SFRO)\n    Sfro,\n    /// Real Time Clock\n    Rtc,\n    /// Feed-forward Ring Oscillator\n    Ffro, // This includes that div2 and div4 variations\n    /// External Clock Input\n    ClkIn,\n    /// AHB Clock\n    Hclk,\n    /// Main Clock\n    MainClk,\n    /// Main PLL Clock\n    MainPllClk, // also has aux0,aux1,dsp, and audio pll's downstream\n    /// System Clock\n    SysClk,\n    /// System Oscillator\n    SysOscClk,\n    /// ADC Clock\n    Adc,\n}\n\n/// Clock configuration.\npub struct ClockConfig {\n    /// low-power oscillator config\n    pub lposc: LposcConfig,\n    /// 16Mhz internal oscillator config\n    pub sfro: SfroConfig,\n    /// Real Time Clock config\n    pub rtc: RtcClkConfig,\n    /// 48/60 Mhz internal oscillator config\n    pub ffro: FfroConfig,\n    // pub pll: Option<PllPfdConfig>, //potentially covered in main pll clk\n    /// External Clock-In config\n    pub clk_in: ClkInConfig,\n    /// AHB bus clock config\n    pub hclk: HclkConfig,\n    /// Main Clock config\n    pub main_clk: MainClkConfig,\n    /// Main Pll clock config\n    pub main_pll_clk: MainPllClkConfig,\n    /// Software concept to be used with systick, doesn't map to a register\n    pub sys_clk: SysClkConfig,\n    /// System Oscillator Config\n    pub sys_osc: SysOscConfig,\n    // todo: move ADC here\n}\n\nimpl ClockConfig {\n    /// Clock configuration derived from external crystal.\n    #[must_use]\n    pub fn crystal() -> Self {\n        const CORE_CPU_FREQ: u32 = 500_000_000;\n        const PLL_CLK_FREQ: u32 = 528_000_000;\n        const SYS_CLK_FREQ: u32 = CORE_CPU_FREQ / 2;\n        Self {\n            lposc: LposcConfig {\n                state: State::Enabled,\n                freq: AtomicU32::new(Into::into(LposcFreq::Lp1m)),\n            },\n            sfro: SfroConfig { state: State::Enabled },\n            rtc: RtcClkConfig {\n                state: State::Enabled,\n                wake_alarm_state: State::Disabled,\n                sub_second_state: State::Disabled,\n                freq: AtomicU32::new(Into::into(RtcFreq::Default1Hz)),\n                rtc_int: RtcInterrupts::None,\n            },\n            ffro: FfroConfig {\n                state: State::Enabled,\n                freq: AtomicU32::new(Into::into(FfroFreq::Ffro48m)),\n            },\n            //pll: Some(PllConfig {}),//includes aux0 and aux1 pll\n            clk_in: ClkInConfig {\n                state: State::Disabled,\n                // This is an externally sourced clock\n                // Don't give it an initial frequency\n                freq: Some(AtomicU32::new(0)),\n            },\n            hclk: HclkConfig { state: State::Disabled },\n            main_clk: MainClkConfig {\n                state: State::Enabled,\n                //FFRO divided by 4 is reset values of Main Clk Sel A, Sel B\n                src: MainClkSrc::FFRO,\n                div_int: AtomicU32::new(4),\n                freq: AtomicU32::new(CORE_CPU_FREQ),\n            },\n            main_pll_clk: MainPllClkConfig {\n                state: State::Enabled,\n                src: MainPllClkSrc::SFRO,\n                freq: AtomicU32::new(PLL_CLK_FREQ),\n                mult: AtomicU8::new(16),\n                pfd0: 19, //\n                pfd1: 0,  // future field\n                pfd2: 19, // 0x13\n                pfd3: 0,  // future field\n                aux0_div: 0,\n                aux1_div: 0,\n            },\n            sys_clk: SysClkConfig {\n                sysclkfreq: AtomicU32::new(SYS_CLK_FREQ),\n            },\n            sys_osc: SysOscConfig { state: State::Enabled },\n            //adc: Some(AdcConfig {}), // TODO: add config\n        }\n    }\n}\n\n#[derive(Clone, Copy, Debug, PartialEq, Eq)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\n/// Clock state enum\npub enum State {\n    /// Clock is enabled\n    Enabled,\n    /// Clock is disabled\n    Disabled,\n}\n\n#[derive(Clone, Copy, Debug, PartialEq, Eq)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\n/// Low Power Oscillator valid frequencies\npub enum LposcFreq {\n    /// 1 `MHz` oscillator\n    Lp1m,\n    /// 32kHz oscillator\n    Lp32k,\n}\n\nimpl From<LposcFreq> for u32 {\n    fn from(value: LposcFreq) -> Self {\n        match value {\n            LposcFreq::Lp1m => 1_000_000,\n            LposcFreq::Lp32k => 32_768,\n        }\n    }\n}\n\nimpl TryFrom<u32> for LposcFreq {\n    type Error = ClockError;\n    fn try_from(value: u32) -> Result<Self, Self::Error> {\n        match value {\n            1_000_000 => Ok(LposcFreq::Lp1m),\n            32_768 => Ok(LposcFreq::Lp32k),\n            _ => Err(ClockError::InvalidFrequency),\n        }\n    }\n}\n\n/// Low power oscillator config\npub struct LposcConfig {\n    state: State,\n    // low power osc\n    freq: AtomicU32,\n}\n\nconst SFRO_FREQ: u32 = 16_000_000;\n/// SFRO config\npub struct SfroConfig {\n    state: State,\n}\n\n/// Valid RTC frequencies\npub enum RtcFreq {\n    /// \"Alarm\" aka 1Hz clock\n    Default1Hz,\n    /// \"Wake\" aka 1kHz clock\n    HighResolution1khz,\n    /// 32kHz clock\n    SubSecond32kHz,\n}\n\nimpl From<RtcFreq> for u32 {\n    fn from(value: RtcFreq) -> Self {\n        match value {\n            RtcFreq::Default1Hz => 1,\n            RtcFreq::HighResolution1khz => 1_000,\n            RtcFreq::SubSecond32kHz => 32_768,\n        }\n    }\n}\n\nimpl TryFrom<u32> for RtcFreq {\n    type Error = ClockError;\n    fn try_from(value: u32) -> Result<Self, Self::Error> {\n        match value {\n            1 => Ok(RtcFreq::Default1Hz),\n            1_000 => Ok(RtcFreq::HighResolution1khz),\n            32_768 => Ok(RtcFreq::SubSecond32kHz),\n            _ => Err(ClockError::InvalidFrequency),\n        }\n    }\n}\n\n/// RTC Interrupt options\npub enum RtcInterrupts {\n    /// No interrupts are set\n    None,\n    /// 1Hz RTC clock aka Alarm interrupt set\n    Alarm,\n    /// 1kHz RTC clock aka Wake interrupt set\n    Wake,\n}\n\nimpl From<RtcInterrupts> for u8 {\n    fn from(value: RtcInterrupts) -> Self {\n        match value {\n            RtcInterrupts::None => 0b00,\n            RtcInterrupts::Alarm => 0b01,\n            RtcInterrupts::Wake => 0b10,\n        }\n    }\n}\n/// RTC clock config.\npub struct RtcClkConfig {\n    /// 1 Hz Clock state\n    pub state: State,\n    /// 1kHz Clock state\n    pub wake_alarm_state: State,\n    /// 32kHz Clock state\n    pub sub_second_state: State,\n    /// RTC clock source.\n    pub freq: AtomicU32,\n    /// RTC Interrupt\n    pub rtc_int: RtcInterrupts,\n}\n\n/// Valid FFRO Frequencies\npub enum FfroFreq {\n    /// 48 Mhz Internal Oscillator\n    Ffro48m,\n    /// 60 `MHz` Internal Oscillator\n    Ffro60m,\n}\n\n/// FFRO Clock Config\npub struct FfroConfig {\n    /// FFRO Clock state\n    state: State,\n    /// FFRO Frequency\n    freq: AtomicU32,\n}\n\nimpl From<FfroFreq> for u32 {\n    fn from(value: FfroFreq) -> Self {\n        match value {\n            FfroFreq::Ffro48m => 48_000_000,\n            FfroFreq::Ffro60m => 60_000_000,\n        }\n    }\n}\n\nimpl TryFrom<u32> for FfroFreq {\n    type Error = ClockError;\n    fn try_from(value: u32) -> Result<Self, Self::Error> {\n        match value {\n            48_000_000 => Ok(FfroFreq::Ffro48m),\n            60_000_000 => Ok(FfroFreq::Ffro60m),\n            _ => Err(ClockError::InvalidFrequency),\n        }\n    }\n}\n\n/// PLL clock source\n#[derive(Clone, Copy, Debug, PartialEq, Eq)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub enum MainPllClkSrc {\n    /// SFRO\n    SFRO,\n    /// External Clock\n    ClkIn,\n    /// FFRO\n    FFRO,\n}\n\n/// Transform from Source Clock enum to Clocks\nimpl From<MainPllClkSrc> for Clocks {\n    fn from(value: MainPllClkSrc) -> Self {\n        match value {\n            MainPllClkSrc::SFRO => Clocks::Sfro,\n            MainPllClkSrc::ClkIn => Clocks::ClkIn,\n            MainPllClkSrc::FFRO => Clocks::Ffro,\n        }\n    }\n}\n\nimpl TryFrom<Clocks> for MainPllClkSrc {\n    type Error = ClockError;\n    fn try_from(value: Clocks) -> Result<Self, Self::Error> {\n        match value {\n            Clocks::Sfro => Ok(MainPllClkSrc::SFRO),\n            Clocks::Ffro => Ok(MainPllClkSrc::FFRO),\n            Clocks::ClkIn => Ok(MainPllClkSrc::ClkIn),\n            _ => Err(ClockError::ClockNotSupported),\n        }\n    }\n}\n\n/// PLL configuration.\npub struct MainPllClkConfig {\n    /// Clock active state\n    pub state: State,\n    /// Main clock source.\n    pub src: MainPllClkSrc,\n    /// Main clock frequency\n    pub freq: AtomicU32,\n    //TODO: numerator and denominator not used but present in register\n    /// Multiplication factor.\n    pub mult: AtomicU8,\n    // the following are actually 6-bits not 8\n    /// Fractional divider 0, main pll clock\n    pub pfd0: u8,\n    /// Fractional divider 1\n    pub pfd1: u8,\n    /// Fractional divider 2\n    pub pfd2: u8,\n    /// Fractional divider 3\n    pub pfd3: u8,\n    // Aux dividers\n    /// aux divider 0\n    pub aux0_div: u8,\n    /// aux divider 1\n    pub aux1_div: u8,\n}\n/// External input clock config\npub struct ClkInConfig {\n    /// External clock input state\n    state: State,\n    /// External clock input rate\n    freq: Option<AtomicU32>,\n}\n\n/// AHB clock config\npub struct HclkConfig {\n    /// divider to turn main clk into hclk for AHB bus\n    pub state: State,\n}\n\n/// Main clock source.\n#[derive(Clone, Copy, Debug, PartialEq, Eq)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub enum MainClkSrc {\n    /// FFRO divided by 4\n    FFROdiv4, // probably don't need since it'll be covered by div_int\n    /// External Clock\n    ClkIn,\n    /// Low Power Oscillator\n    Lposc,\n    /// FFRO\n    FFRO,\n    /// SFRO\n    SFRO,\n    /// Main PLL Clock\n    PllMain,\n    /// RTC 32kHz oscillator.\n    RTC32k,\n}\n\nimpl From<MainClkSrc> for Clocks {\n    fn from(value: MainClkSrc) -> Self {\n        match value {\n            MainClkSrc::ClkIn => Clocks::ClkIn,\n            MainClkSrc::Lposc => Clocks::Lposc,\n            MainClkSrc::FFRO => Clocks::Ffro,\n            MainClkSrc::SFRO => Clocks::Sfro,\n            MainClkSrc::PllMain => Clocks::MainPllClk,\n            MainClkSrc::RTC32k => Clocks::Rtc,\n            MainClkSrc::FFROdiv4 => Clocks::Ffro,\n        }\n    }\n}\n\nimpl TryFrom<Clocks> for MainClkSrc {\n    type Error = ClockError;\n    fn try_from(value: Clocks) -> Result<Self, Self::Error> {\n        match value {\n            Clocks::ClkIn => Ok(MainClkSrc::ClkIn),\n            Clocks::Lposc => Ok(MainClkSrc::Lposc),\n            Clocks::Sfro => Ok(MainClkSrc::SFRO),\n            Clocks::MainPllClk => Ok(MainClkSrc::PllMain),\n            Clocks::Rtc => Ok(MainClkSrc::RTC32k),\n            Clocks::Ffro => Ok(MainClkSrc::FFRO),\n            _ => Err(ClockError::ClockNotSupported),\n        }\n    }\n}\n\n/// Main clock config.\npub struct MainClkConfig {\n    /// Main clock state\n    pub state: State,\n    /// Main clock source.\n    pub src: MainClkSrc,\n    /// Main clock divider.\n    pub div_int: AtomicU32,\n    /// Clock Frequency\n    pub freq: AtomicU32,\n}\n\n/// System Core Clock config, SW concept for systick\npub struct SysClkConfig {\n    /// keeps track of the system core clock frequency\n    /// future use with systick\n    pub sysclkfreq: AtomicU32,\n}\n\n/// System Oscillator Config\npub struct SysOscConfig {\n    /// Clock State\n    pub state: State,\n}\nconst SYS_OSC_DEFAULT_FREQ: u32 = 24_000_000;\n\n/// Clock Errors\n#[derive(Clone, Copy, Debug, PartialEq, Eq)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub enum ClockError {\n    /// Error due to attempting to change a clock with the wrong config block\n    ClockMismatch,\n    /// Error due to attempting to modify a clock that's not yet been enabled\n    ClockNotEnabled,\n    /// Error due to attempting to set a clock source that's not a supported option\n    ClockNotSupported,\n    /// Error due to attempting to set a clock to an invalid frequency\n    InvalidFrequency,\n    /// Error due to attempting to modify a clock output with an invalid divider\n    InvalidDiv,\n    /// Error due to attempting to modify a clock output with an invalid multiplier\n    InvalidMult,\n}\n\n/// Trait to configure one of the clocks\npub trait ConfigurableClock {\n    /// Reset the clock, will enable it\n    fn disable(&self) -> Result<(), ClockError>;\n    /// Enable the clock\n    fn enable_and_reset(&self) -> Result<(), ClockError>;\n    /// Return the clock rate (Hz)\n    fn get_clock_rate(&self) -> Result<u32, ClockError>;\n    /// Set the desired clock rate (Hz)\n    fn set_clock_rate(&mut self, div: u8, mult: u8, freq: u32) -> Result<(), ClockError>;\n    /// Returns whether this clock is enabled\n    fn is_enabled(&self) -> bool;\n}\n\nimpl LposcConfig {\n    /// Initializes low-power oscillator.\n    fn init_lposc() {\n        // Enable low power oscillator\n        // SAFETY: unsafe needed to take pointer to Sysctl0, only happens once during init\n        let sysctl0 = unsafe { crate::pac::Sysctl0::steal() };\n        sysctl0.pdruncfg0_clr().write(|w| w.lposc_pd().clr_pdruncfg0());\n\n        // Wait for low-power oscillator to be ready (typically 64 us)\n        // Busy loop seems better here than trying to shoe-in an async delay\n        // SAFETY: unsafe needed to take pointer to Clkctl0, needed to validate HW is ready\n        let clkctl0 = unsafe { crate::pac::Clkctl0::steal() };\n        while clkctl0.lposcctl0().read().clkrdy().bit_is_clear() {}\n    }\n}\nimpl ConfigurableClock for LposcConfig {\n    fn enable_and_reset(&self) -> Result<(), ClockError> {\n        LposcConfig::init_lposc();\n        Ok(())\n    }\n    fn disable(&self) -> Result<(), ClockError> {\n        // SAFETY: unsafe needed to take pointer to Sysctl0, needed to power down the LPOSC HW\n        let sysctl0 = unsafe { crate::pac::Sysctl0::steal() };\n        sysctl0.pdruncfg0_set().write(|w| w.lposc_pd().set_pdruncfg0());\n        // Wait until LPOSC disabled\n        while !sysctl0.pdruncfg0().read().lposc_pd().is_power_down() {}\n        Ok(())\n    }\n    fn get_clock_rate(&self) -> Result<u32, ClockError> {\n        Ok(self.freq.load(Ordering::Relaxed))\n    }\n    fn set_clock_rate(&mut self, _div: u8, _mult: u8, freq: u32) -> Result<(), ClockError> {\n        if let Ok(r) = <u32 as TryInto<LposcFreq>>::try_into(freq) {\n            match r {\n                LposcFreq::Lp1m => {\n                    self.freq\n                        .store(LposcFreq::Lp1m as u32, core::sync::atomic::Ordering::Relaxed);\n                    Ok(())\n                }\n                LposcFreq::Lp32k => {\n                    self.freq\n                        .store(LposcFreq::Lp1m as u32, core::sync::atomic::Ordering::Relaxed);\n                    Ok(())\n                }\n            }\n        } else {\n            Err(ClockError::InvalidFrequency)\n        }\n    }\n    fn is_enabled(&self) -> bool {\n        self.state == State::Enabled\n    }\n}\n\nimpl FfroConfig {\n    /// Necessary register writes to initialize the FFRO clock\n    pub fn init_ffro_clk() {\n        // SAFETY: unsafe needed to take pointer to Sysctl0, only to power up FFRO\n        let sysctl0 = unsafe { crate::pac::Sysctl0::steal() };\n\n        /* Power on FFRO (48/60MHz) */\n        sysctl0.pdruncfg0_clr().write(|w| w.ffro_pd().clr_pdruncfg0());\n\n        // SAFETY: unsafe needed to take pointer to Clkctl0, only to set proper ffro update mode\n        let clkctl0 = unsafe { crate::pac::Clkctl0::steal() };\n\n        clkctl0.ffroctl1().write(|w| w.update().normal_mode());\n\n        // No FFRO enable/disable control in CLKCTL.\n        // Delay enough for FFRO to be stable in case it was just powered on\n        delay_loop_clocks(50, 12_000_000);\n    }\n}\n\nimpl ConfigurableClock for FfroConfig {\n    fn enable_and_reset(&self) -> Result<(), ClockError> {\n        // SAFETY: should be called once\n        FfroConfig::init_ffro_clk();\n        // default is 48 MHz\n        Ok(())\n    }\n    fn disable(&self) -> Result<(), ClockError> {\n        // SAFETY: unsafe needed to take pointer to Sysctl0, only to power down FFRO\n        let sysctl0 = unsafe { crate::pac::Sysctl0::steal() };\n        sysctl0.pdruncfg0_set().write(|w| w.ffro_pd().set_pdruncfg0());\n        delay_loop_clocks(30, 12_000_000);\n        // Wait until FFRO disabled\n        while !sysctl0.pdruncfg0().read().ffro_pd().is_power_down() {}\n        Ok(())\n    }\n    fn get_clock_rate(&self) -> Result<u32, ClockError> {\n        Ok(self.freq.load(Ordering::Relaxed))\n    }\n    fn set_clock_rate(&mut self, _div: u8, _mult: u8, freq: u32) -> Result<(), ClockError> {\n        if let Ok(r) = <u32 as TryInto<FfroFreq>>::try_into(freq) {\n            match r {\n                FfroFreq::Ffro48m => {\n                    // SAFETY: unsafe needed to take pointer to Clkctl0, needed to set the right HW frequency\n                    let clkctl0 = unsafe { crate::pac::Clkctl0::steal() };\n                    clkctl0.ffroctl1().write(|w| w.update().update_safe_mode());\n                    clkctl0.ffroctl0().write(|w| w.trim_range().ffro_48mhz());\n                    clkctl0.ffroctl1().write(|w| w.update().normal_mode());\n\n                    self.freq\n                        .store(FfroFreq::Ffro48m as u32, core::sync::atomic::Ordering::Relaxed);\n                    Ok(())\n                }\n                FfroFreq::Ffro60m => {\n                    // SAFETY: unsafe needed to take pointer to Clkctl0, needed to set the right HW frequency\n                    let clkctl0 = unsafe { crate::pac::Clkctl0::steal() };\n                    clkctl0.ffroctl1().write(|w| w.update().update_safe_mode());\n                    clkctl0.ffroctl0().write(|w| w.trim_range().ffro_60mhz());\n                    clkctl0.ffroctl1().write(|w| w.update().normal_mode());\n\n                    self.freq\n                        .store(FfroFreq::Ffro60m as u32, core::sync::atomic::Ordering::Relaxed);\n                    Ok(())\n                }\n            }\n        } else {\n            error!(\"failed to convert desired clock rate, {:#}, to FFRO Freq\", freq);\n            Err(ClockError::InvalidFrequency)\n        }\n    }\n    fn is_enabled(&self) -> bool {\n        self.state == State::Enabled\n    }\n}\n\nimpl ConfigurableClock for SfroConfig {\n    fn enable_and_reset(&self) -> Result<(), ClockError> {\n        // SAFETY: unsafe needed to take pointer to Sysctl0, only to power up SFRO\n        let sysctl0 = unsafe { crate::pac::Sysctl0::steal() };\n        sysctl0.pdruncfg0_clr().write(|w| w.sfro_pd().clr_pdruncfg0());\n        // wait until ready\n        while !sysctl0.pdruncfg0().read().sfro_pd().is_enabled() {}\n        Ok(())\n    }\n    fn disable(&self) -> Result<(), ClockError> {\n        // SAFETY: unsafe needed to take pointer to Sysctl0, only to power down SFRO\n        let sysctl0 = unsafe { crate::pac::Sysctl0::steal() };\n        sysctl0.pdruncfg0_set().write(|w| w.sfro_pd().set_pdruncfg0());\n        delay_loop_clocks(30, 12_000_000);\n        // Wait until SFRO disabled\n        while !sysctl0.pdruncfg0().read().sfro_pd().is_power_down() {}\n        Ok(())\n    }\n    fn get_clock_rate(&self) -> Result<u32, ClockError> {\n        if self.state == State::Enabled {\n            Ok(SFRO_FREQ)\n        } else {\n            Err(ClockError::ClockNotEnabled)\n        }\n    }\n    fn set_clock_rate(&mut self, _div: u8, _mult: u8, freq: u32) -> Result<(), ClockError> {\n        if self.state == State::Enabled {\n            if freq == SFRO_FREQ {\n                Ok(())\n            } else {\n                Err(ClockError::InvalidFrequency)\n            }\n        } else {\n            Err(ClockError::ClockNotEnabled)\n        }\n    }\n    fn is_enabled(&self) -> bool {\n        self.state == State::Enabled\n    }\n}\n\n/// A Clock with multiple options for clock source\npub trait MultiSourceClock {\n    /// Returns which clock is being used as the clock source and its rate\n    fn get_clock_source_and_rate(&self, clock: &Clocks) -> Result<(Clocks, u32), ClockError>;\n    /// Sets a specific clock source and its associated rate\n    fn set_clock_source_and_rate(\n        &mut self,\n        clock_src_config: &mut impl ConfigurableClock,\n        clock_src: &Clocks,\n        rate: u32,\n    ) -> Result<(), ClockError>;\n}\n\nimpl MultiSourceClock for MainPllClkConfig {\n    fn get_clock_source_and_rate(&self, clock: &Clocks) -> Result<(Clocks, u32), ClockError> {\n        match clock {\n            Clocks::MainPllClk => {\n                let converted_clock = Clocks::from(self.src);\n                Ok((converted_clock, self.freq.load(Ordering::Relaxed)))\n            }\n            _ => Err(ClockError::ClockMismatch),\n        }\n    }\n    fn set_clock_source_and_rate(\n        &mut self,\n        clock_src_config: &mut impl ConfigurableClock,\n        clock_src: &Clocks,\n        rate: u32,\n    ) -> Result<(), ClockError> {\n        if let Ok(c) = <Clocks as TryInto<MainPllClkSrc>>::try_into(*clock_src) {\n            match c {\n                MainPllClkSrc::ClkIn => {\n                    self.src = MainPllClkSrc::ClkIn;\n                    // div mult and rate don't matter since this is an external clock\n                    self.set_clock_rate(1, 1, rate)\n                }\n                MainPllClkSrc::FFRO => {\n                    // FFRO Clock is divided by 2\n                    let r = clock_src_config.get_clock_rate()?;\n                    let base_rate = r / 2;\n                    let m = MainPllClkConfig::calc_mult(rate, base_rate)?;\n\n                    self.src = MainPllClkSrc::FFRO;\n                    self.set_clock_rate(2, m, rate)\n                }\n                MainPllClkSrc::SFRO => {\n                    if !clock_src_config.is_enabled() {\n                        return Err(ClockError::ClockNotEnabled);\n                    }\n                    // check if desired frequency is a valid multiple of 16m SFRO clock\n                    let m = MainPllClkConfig::calc_mult(rate, SFRO_FREQ)?;\n                    self.src = MainPllClkSrc::SFRO;\n                    self.set_clock_rate(1, m, rate)\n                }\n            }\n        } else {\n            Err(ClockError::ClockNotSupported)\n        }\n    }\n}\n\nimpl ConfigurableClock for MainPllClkConfig {\n    fn enable_and_reset(&self) -> Result<(), ClockError> {\n        MainPllClkConfig::init_syspll();\n\n        MainPllClkConfig::init_syspll_pfd0(self.pfd0);\n\n        MainPllClkConfig::init_syspll_pfd2(self.pfd2);\n        Ok(())\n    }\n    fn disable(&self) -> Result<(), ClockError> {\n        if self.is_enabled() {\n            Err(ClockError::ClockNotSupported)\n        } else {\n            Err(ClockError::ClockNotEnabled)\n        }\n    }\n    fn get_clock_rate(&self) -> Result<u32, ClockError> {\n        if self.is_enabled() {\n            let (_c, rate) = self.get_clock_source_and_rate(&Clocks::MainPllClk)?;\n            Ok(rate)\n        } else {\n            Err(ClockError::ClockNotEnabled)\n        }\n    }\n    fn set_clock_rate(&mut self, div: u8, mult: u8, freq: u32) -> Result<(), ClockError> {\n        if self.is_enabled() {\n            // SAFETY: unsafe needed to take pointers to Sysctl0 and Clkctl0\n            let clkctl0 = unsafe { crate::pac::Clkctl0::steal() };\n            let sysctl0 = unsafe { crate::pac::Sysctl0::steal() };\n\n            // Power down pll before changes\n            sysctl0\n                .pdruncfg0_set()\n                .write(|w| w.syspllldo_pd().set_pdruncfg0().syspllana_pd().set_pdruncfg0());\n\n            let desired_freq: u64 = self.freq.load(Ordering::Relaxed).into();\n\n            match self.src {\n                c if c == MainPllClkSrc::ClkIn || c == MainPllClkSrc::FFRO || c == MainPllClkSrc::SFRO => {\n                    let mut base_rate;\n                    match c {\n                        MainPllClkSrc::ClkIn => {\n                            clkctl0.syspll0clksel().write(|w| w.sel().sysxtal_clk());\n                            let r = self.get_clock_rate()?;\n                            base_rate = r;\n                        }\n                        MainPllClkSrc::FFRO => {\n                            delay_loop_clocks(1000, desired_freq);\n                            match clkctl0.ffroctl0().read().trim_range().is_ffro_48mhz() {\n                                true => base_rate = Into::into(FfroFreq::Ffro48m),\n                                false => base_rate = Into::into(FfroFreq::Ffro60m),\n                            }\n                            if div == 2 {\n                                clkctl0.syspll0clksel().write(|w| w.sel().ffro_div_2());\n                                delay_loop_clocks(150, desired_freq);\n                                base_rate /= 2;\n                            } else {\n                                return Err(ClockError::InvalidDiv);\n                            }\n                        }\n                        MainPllClkSrc::SFRO => {\n                            base_rate = SFRO_FREQ;\n                            clkctl0.syspll0clksel().write(|w| w.sel().sfro_clk());\n                        }\n                    };\n                    base_rate *= u32::from(mult);\n                    if base_rate != freq {\n                        // make sure to power syspll back up before returning the error\n                        // Clear System PLL reset\n                        clkctl0.syspll0ctl0().write(|w| w.reset().normal());\n                        // Power up SYSPLL\n                        sysctl0\n                            .pdruncfg0_clr()\n                            .write(|w| w.syspllana_pd().clr_pdruncfg0().syspllldo_pd().clr_pdruncfg0());\n                        return Err(ClockError::InvalidFrequency);\n                    }\n                    // SAFETY: unsafe needed to write the bits for the num and demon fields\n                    clkctl0.syspll0num().write(|w| unsafe { w.num().bits(0b0) });\n                    clkctl0.syspll0denom().write(|w| unsafe { w.denom().bits(0b1) });\n                    delay_loop_clocks(30, desired_freq);\n                    self.mult.store(mult, Ordering::Relaxed);\n                    match mult {\n                        16 => {\n                            clkctl0.syspll0ctl0().modify(|_r, w| w.mult().div_16());\n                        }\n                        17 => {\n                            clkctl0.syspll0ctl0().modify(|_r, w| w.mult().div_17());\n                        }\n                        20 => {\n                            clkctl0.syspll0ctl0().modify(|_r, w| w.mult().div_20());\n                        }\n                        22 => {\n                            clkctl0.syspll0ctl0().modify(|_r, w| w.mult().div_22());\n                        }\n                        27 => {\n                            clkctl0.syspll0ctl0().modify(|_r, w| w.mult().div_27());\n                        }\n                        33 => {\n                            clkctl0.syspll0ctl0().modify(|_r, w| w.mult().div_33());\n                        }\n                        _ => return Err(ClockError::InvalidMult),\n                    }\n                    // Clear System PLL reset\n                    clkctl0.syspll0ctl0().modify(|_r, w| w.reset().normal());\n                    // Power up SYSPLL\n                    sysctl0\n                        .pdruncfg0_clr()\n                        .write(|w| w.syspllana_pd().clr_pdruncfg0().syspllldo_pd().clr_pdruncfg0());\n\n                    // Set System PLL HOLDRINGOFF_ENA\n                    clkctl0.syspll0ctl0().modify(|_, w| w.holdringoff_ena().enable());\n                    delay_loop_clocks(75, desired_freq);\n\n                    // Clear System PLL HOLDRINGOFF_ENA\n                    clkctl0.syspll0ctl0().modify(|_, w| w.holdringoff_ena().dsiable());\n                    delay_loop_clocks(15, desired_freq);\n\n                    // gate the output and clear bits.\n                    // SAFETY: unsafe needed to write the bits for pfd0\n                    clkctl0\n                        .syspll0pfd()\n                        .modify(|_, w| unsafe { w.pfd0_clkgate().gated().pfd0().bits(0x0) });\n                    // set pfd bits and un-gate the clock output\n                    // output is multiplied by syspll * 18/pfd0_bits\n                    // SAFETY: unsafe needed to write the bits for pfd0\n                    clkctl0\n                        .syspll0pfd()\n                        .modify(|_r, w| unsafe { w.pfd0_clkgate().not_gated().pfd0().bits(0x12) });\n                    // wait for ready bit to be set\n                    delay_loop_clocks(50, desired_freq);\n                    while clkctl0.syspll0pfd().read().pfd0_clkrdy().bit_is_clear() {}\n                    // clear by writing a 1\n                    clkctl0.syspll0pfd().modify(|_, w| w.pfd0_clkrdy().set_bit());\n\n                    Ok(())\n                }\n                _ => Err(ClockError::ClockNotSupported),\n            }\n        } else {\n            Err(ClockError::ClockNotEnabled)\n        }\n    }\n    fn is_enabled(&self) -> bool {\n        self.state == State::Enabled\n    }\n}\n\nimpl MainPllClkConfig {\n    /// Calculate the mult value of a desired frequency, return error if invalid\n    pub(self) fn calc_mult(rate: u32, base_freq: u32) -> Result<u8, ClockError> {\n        const VALIDMULTS: [u8; 6] = [16, 17, 20, 22, 27, 33];\n        if rate > base_freq && rate % base_freq == 0 {\n            let mult = (rate / base_freq) as u8;\n            if VALIDMULTS.into_iter().any(|i| i == mult) {\n                Ok(mult)\n            } else {\n                Err(ClockError::InvalidFrequency)\n            }\n        } else {\n            Err(ClockError::InvalidFrequency)\n        }\n    }\n    pub(self) fn init_syspll() {\n        // SAFETY: unsafe needed to take pointers to Sysctl0 and Clkctl0\n        let clkctl0 = unsafe { crate::pac::Clkctl0::steal() };\n        let sysctl0 = unsafe { crate::pac::Sysctl0::steal() };\n\n        // Power down SYSPLL before change fractional settings\n        sysctl0\n            .pdruncfg0_set()\n            .write(|w| w.syspllldo_pd().set_pdruncfg0().syspllana_pd().set_pdruncfg0());\n\n        clkctl0.syspll0clksel().write(|w| w.sel().ffro_div_2());\n        // SAFETY: unsafe needed to write the bits for both num and denom\n        clkctl0.syspll0num().write(|w| unsafe { w.num().bits(0x0) });\n        clkctl0.syspll0denom().write(|w| unsafe { w.denom().bits(0x1) });\n\n        // kCLOCK_SysPllMult22\n        clkctl0.syspll0ctl0().modify(|_, w| w.mult().div_22());\n\n        // Clear System PLL reset\n        clkctl0.syspll0ctl0().modify(|_, w| w.reset().normal());\n\n        // Power up SYSPLL\n        sysctl0\n            .pdruncfg0_clr()\n            .write(|w| w.syspllldo_pd().clr_pdruncfg0().syspllana_pd().clr_pdruncfg0());\n        delay_loop_clocks((150 & 0xFFFF) / 2, 12_000_000);\n\n        // Set System PLL HOLDRINGOFF_ENA\n        clkctl0.syspll0ctl0().modify(|_, w| w.holdringoff_ena().enable());\n        delay_loop_clocks((150 & 0xFFFF) / 2, 12_000_000);\n\n        // Clear System PLL HOLDRINGOFF_ENA\n        clkctl0.syspll0ctl0().modify(|_, w| w.holdringoff_ena().dsiable());\n        delay_loop_clocks((15 & 0xFFFF) / 2, 12_000_000);\n    }\n    /// enables default settings for pfd2 bits\n    pub(self) fn init_syspll_pfd2(config_bits: u8) {\n        // SAFETY: unsafe needed to take pointer to Clkctl0 and write specific bits\n        // needed to change the output of pfd0\n        unsafe {\n            let clkctl0 = crate::pac::Clkctl0::steal();\n\n            // Disable the clock output first.\n            // SAFETY: unsafe needed to write the bits for pfd2\n            clkctl0\n                .syspll0pfd()\n                .modify(|_, w| w.pfd2_clkgate().gated().pfd2().bits(0x0));\n\n            // Set the new value and enable output.\n            // SAFETY: unsafe needed to write the bits for pfd2\n            clkctl0\n                .syspll0pfd()\n                .modify(|_, w| w.pfd2_clkgate().not_gated().pfd2().bits(config_bits));\n\n            // Wait for output becomes stable.\n            while clkctl0.syspll0pfd().read().pfd2_clkrdy().bit_is_clear() {}\n\n            // Clear ready status flag.\n            clkctl0.syspll0pfd().modify(|_, w| w.pfd2_clkrdy().clear_bit());\n        }\n    }\n    /// Enables default settings for pfd0\n    pub(self) fn init_syspll_pfd0(config_bits: u8) {\n        // SAFETY: unsafe needed to take pointer to Clkctl0 and write specific bits\n        // needed to change the output of pfd0\n        unsafe {\n            let clkctl0 = crate::pac::Clkctl0::steal();\n            // Disable the clock output first\n            clkctl0\n                .syspll0pfd()\n                .modify(|_, w| w.pfd0_clkgate().gated().pfd0().bits(0x0));\n\n            // Set the new value and enable output\n            clkctl0\n                .syspll0pfd()\n                .modify(|_, w| w.pfd0_clkgate().not_gated().pfd0().bits(config_bits));\n\n            // Wait for output becomes stable\n            while clkctl0.syspll0pfd().read().pfd0_clkrdy().bit_is_clear() {}\n\n            // Clear ready status flag\n            clkctl0.syspll0pfd().modify(|_, w| w.pfd0_clkrdy().clear_bit());\n        }\n    }\n}\n\nimpl MainClkConfig {\n    fn init_main_clk() {\n        // SAFETY:: unsafe needed to take pointers to Clkctl0 and Clkctl1\n        // used to set the right HW frequency\n        let clkctl0 = unsafe { crate::pac::Clkctl0::steal() };\n        let clkctl1 = unsafe { crate::pac::Clkctl1::steal() };\n\n        clkctl0.mainclkselb().write(|w| w.sel().main_pll_clk());\n\n        // Set PFC0DIV divider to value 2, Subtract 1 since 0-> 1, 1-> 2, etc...\n        clkctl0.pfcdiv(0).modify(|_, w| w.reset().set_bit());\n        // SAFETY: unsafe needed to write the bits for pfcdiv\n        clkctl0\n            .pfcdiv(0)\n            .write(|w| unsafe { w.div().bits(2 - 1).halt().clear_bit() });\n        while clkctl0.pfcdiv(0).read().reqflag().bit_is_set() {}\n\n        // Set FRGPLLCLKDIV divider to value 12, Subtract 1 since 0-> 1, 1-> 2, etc...\n        clkctl1.frgpllclkdiv().modify(|_, w| w.reset().set_bit());\n        // SAFETY: unsafe needed to write the bits for frgpllclkdiv\n        clkctl1\n            .frgpllclkdiv()\n            .write(|w| unsafe { w.div().bits(12 - 1).halt().clear_bit() });\n        while clkctl1.frgpllclkdiv().read().reqflag().bit_is_set() {}\n    }\n}\nimpl MultiSourceClock for MainClkConfig {\n    fn get_clock_source_and_rate(&self, clock: &Clocks) -> Result<(Clocks, u32), ClockError> {\n        match clock {\n            Clocks::MainClk => {\n                let div: u32 = if self.src == MainClkSrc::FFROdiv4 { 4 } else { 1 };\n                let converted_clock = Clocks::from(self.src);\n                match ConfigurableClock::get_clock_rate(self) {\n                    Ok(_rate) => {\n                        // SAFETY: unsafe needed to take pointer to Clkctl0\n                        // needed to calculate the clock rate from the bits written in the registers\n                        let clkctl0 = unsafe { crate::pac::Clkctl0::steal() };\n                        if self.src == MainClkSrc::PllMain && clkctl0.syspll0ctl0().read().bypass().is_programmed_clk()\n                        {\n                            let mut temp;\n                            temp = self.freq.load(Ordering::Relaxed)\n                                * u32::from(clkctl0.syspll0ctl0().read().mult().bits());\n                            temp = (u64::from(temp) * 18 / u64::from(clkctl0.syspll0pfd().read().pfd0().bits())) as u32;\n                            return Ok((converted_clock, temp));\n                        }\n                        Ok((converted_clock, self.freq.load(Ordering::Relaxed) / div))\n                    }\n                    Err(clk_err) => Err(clk_err),\n                }\n            }\n            _ => Err(ClockError::ClockMismatch),\n        }\n    }\n    fn set_clock_source_and_rate(\n        &mut self,\n        clock_src_config: &mut impl ConfigurableClock,\n        clock_src: &Clocks,\n        rate: u32,\n    ) -> Result<(), ClockError> {\n        if !clock_src_config.is_enabled() {\n            return Err(ClockError::ClockNotEnabled);\n        }\n        if let Ok(c) = <Clocks as TryInto<MainClkSrc>>::try_into(*clock_src) {\n            // SAFETY: unsafe needed to take pointer to Clkctl0\n            // needed to change the clock source\n            let clkctl0 = unsafe { crate::pac::Clkctl0::steal() };\n            match c {\n                MainClkSrc::ClkIn => {\n                    self.src = MainClkSrc::ClkIn;\n\n                    clkctl0.mainclksela().write(|w| w.sel().sysxtal_clk());\n                    clkctl0.mainclkselb().write(|w| w.sel().main_1st_clk());\n                    Ok(())\n                }\n                // the following will yield the same result as if compared to FFROdiv4\n                MainClkSrc::FFRO | MainClkSrc::FFROdiv4 => match rate {\n                    div4 if div4 == (FfroFreq::Ffro60m as u32) / 4 || div4 == (FfroFreq::Ffro48m as u32) / 4 => {\n                        self.src = MainClkSrc::FFROdiv4;\n                        self.freq.store(div4, Ordering::Relaxed);\n\n                        clkctl0.mainclksela().write(|w| w.sel().ffro_div_4());\n                        clkctl0.mainclkselb().write(|w| w.sel().main_1st_clk());\n                        Ok(())\n                    }\n                    div1 if div1 == FfroFreq::Ffro60m as u32 || div1 == FfroFreq::Ffro48m as u32 => {\n                        self.src = MainClkSrc::FFRO;\n                        self.freq.store(div1, Ordering::Relaxed);\n\n                        clkctl0.mainclksela().write(|w| w.sel().ffro_clk());\n                        clkctl0.mainclkselb().write(|w| w.sel().main_1st_clk());\n                        Ok(())\n                    }\n                    _ => Err(ClockError::InvalidFrequency),\n                },\n                MainClkSrc::Lposc => {\n                    if let Ok(r) = <u32 as TryInto<LposcFreq>>::try_into(rate) {\n                        match r {\n                            LposcFreq::Lp1m => {\n                                self.src = MainClkSrc::Lposc;\n                                self.freq.store(rate, Ordering::Relaxed);\n\n                                clkctl0.mainclksela().write(|w| w.sel().lposc());\n                                clkctl0.mainclkselb().write(|w| w.sel().main_1st_clk());\n                                Ok(())\n                            }\n                            LposcFreq::Lp32k => Err(ClockError::InvalidFrequency),\n                        }\n                    } else {\n                        Err(ClockError::InvalidFrequency)\n                    }\n                }\n                MainClkSrc::SFRO => {\n                    if rate == SFRO_FREQ {\n                        self.src = MainClkSrc::SFRO;\n                        self.freq.store(rate, Ordering::Relaxed);\n                        clkctl0.mainclkselb().write(|w| w.sel().sfro_clk());\n                        Ok(())\n                    } else {\n                        Err(ClockError::InvalidFrequency)\n                    }\n                }\n                MainClkSrc::PllMain => {\n                    let r = rate;\n                    // From Section 4.6.1.1 Pll Limitations of the RT6xx User manual\n                    let pll_max = 572_000_000;\n                    let pll_min = 80_000_000;\n                    if pll_min <= r && r <= pll_max {\n                        clkctl0.mainclkselb().write(|w| w.sel().main_pll_clk());\n                        self.src = MainClkSrc::PllMain;\n                        self.freq.store(r, Ordering::Relaxed);\n                        Ok(())\n                    } else {\n                        Err(ClockError::InvalidFrequency)\n                    }\n                }\n                MainClkSrc::RTC32k => {\n                    if rate == RtcFreq::SubSecond32kHz as u32 {\n                        self.src = MainClkSrc::RTC32k;\n                        self.freq.store(rate, Ordering::Relaxed);\n                        clkctl0.mainclkselb().write(|w| w.sel().rtc_32k_clk());\n                        Ok(())\n                    } else {\n                        Err(ClockError::InvalidFrequency)\n                    }\n                }\n            }\n        } else {\n            Err(ClockError::ClockNotSupported)\n        }\n    }\n}\n\nimpl ConfigurableClock for MainClkConfig {\n    fn enable_and_reset(&self) -> Result<(), ClockError> {\n        MainClkConfig::init_main_clk();\n        Ok(())\n    }\n    fn disable(&self) -> Result<(), ClockError> {\n        Err(ClockError::ClockNotSupported)\n    }\n    fn get_clock_rate(&self) -> Result<u32, ClockError> {\n        let (_c, rate) = MainClkConfig::get_clock_source_and_rate(self, &Clocks::MainClk)?;\n        Ok(rate)\n    }\n    fn set_clock_rate(&mut self, _div: u8, _mult: u8, _freq: u32) -> Result<(), ClockError> {\n        Err(ClockError::ClockNotSupported)\n    }\n    fn is_enabled(&self) -> bool {\n        self.state == State::Enabled\n    }\n}\n\nimpl ConfigurableClock for ClkInConfig {\n    fn enable_and_reset(&self) -> Result<(), ClockError> {\n        // External Input, no hw writes needed\n        Ok(())\n    }\n    fn disable(&self) -> Result<(), ClockError> {\n        error!(\"Attempting to reset a clock input\");\n        Err(ClockError::ClockNotSupported)\n    }\n    fn get_clock_rate(&self) -> Result<u32, ClockError> {\n        if self.freq.is_some() {\n            Ok(self.freq.as_ref().unwrap().load(Ordering::Relaxed))\n        } else {\n            Err(ClockError::ClockNotEnabled)\n        }\n    }\n    fn set_clock_rate(&mut self, _div: u8, _mult: u8, freq: u32) -> Result<(), ClockError> {\n        self.freq.as_ref().unwrap().store(freq, Ordering::Relaxed);\n        Ok(())\n    }\n    fn is_enabled(&self) -> bool {\n        self.state == State::Enabled\n    }\n}\n\nimpl RtcClkConfig {\n    /// Register writes to initialize the RTC Clock\n    fn init_rtc_clk() {\n        // SAFETY: unsafe needed to take pointer to Clkctl0, Clkctl1, and RTC\n        // needed to enable the RTC HW\n        let cc0 = unsafe { pac::Clkctl0::steal() };\n        let cc1 = unsafe { pac::Clkctl1::steal() };\n        let r = unsafe { pac::Rtc::steal() };\n        // Enable the RTC peripheral clock\n        cc1.pscctl2_set().write(|w| w.rtc_lite_clk_set().set_clock());\n        // Make sure the reset bit is cleared amd RTC OSC is powered up\n        r.ctrl().modify(|_, w| w.swreset().not_in_reset().rtc_osc_pd().enable());\n\n        // set initial match value, note that with a 15 bit count-down timer this would\n        // typically be 0x8000, but we are \"doing some clever things\" in time-driver.rs,\n        // read more about it in the comments there\n        // SAFETY: unsafe needed to write the bits\n        r.wake().write(|w| unsafe { w.bits(0xA) });\n\n        // Enable 32K OSC\n        cc0.osc32khzctl0().write(|w| w.ena32khz().enabled());\n\n        // enable rtc clk\n        r.ctrl().modify(|_, w| w.rtc_en().enable());\n    }\n}\n\nimpl ConfigurableClock for RtcClkConfig {\n    fn enable_and_reset(&self) -> Result<(), ClockError> {\n        // should only be called once if previously disabled\n        RtcClkConfig::init_rtc_clk();\n        Ok(())\n    }\n    fn disable(&self) -> Result<(), ClockError> {\n        Err(ClockError::ClockNotSupported)\n    }\n    fn set_clock_rate(&mut self, _div: u8, _mult: u8, freq: u32) -> Result<(), ClockError> {\n        if let Ok(r) = <u32 as TryInto<RtcFreq>>::try_into(freq) {\n            // SAFETY: unsafe needed to take pointer to RTC\n            // needed to enable the HW for the different RTC frequencies, powered down by default\n            let rtc = unsafe { crate::pac::Rtc::steal() };\n            match r {\n                RtcFreq::Default1Hz => {\n                    if rtc.ctrl().read().rtc_en().is_enable() {\n                    } else {\n                        rtc.ctrl().modify(|_r, w| w.rtc_en().enable());\n                    }\n                    Ok(())\n                }\n                RtcFreq::HighResolution1khz => {\n                    if rtc.ctrl().read().rtc1khz_en().is_enable() {\n                    } else {\n                        rtc.ctrl().modify(|_r, w| w.rtc1khz_en().enable());\n                    }\n                    Ok(())\n                }\n                RtcFreq::SubSecond32kHz => {\n                    if rtc.ctrl().read().rtc_subsec_ena().is_enable() {\n                    } else {\n                        rtc.ctrl().modify(|_r, w| w.rtc_subsec_ena().enable());\n                    }\n                    Ok(())\n                }\n            }\n        } else {\n            Err(ClockError::InvalidFrequency)\n        }\n    }\n    // unlike the others, since this provides multiple clocks, return the fastest one\n    fn get_clock_rate(&self) -> Result<u32, ClockError> {\n        if self.sub_second_state == State::Enabled {\n            Ok(RtcFreq::SubSecond32kHz as u32)\n        } else if self.wake_alarm_state == State::Enabled {\n            Ok(RtcFreq::HighResolution1khz as u32)\n        } else if self.state == State::Enabled {\n            Ok(RtcFreq::Default1Hz as u32)\n        } else {\n            Err(ClockError::ClockNotEnabled)\n        }\n    }\n    fn is_enabled(&self) -> bool {\n        self.state == State::Enabled\n    }\n}\n\nimpl SysClkConfig {\n    /// Updates the system core clock frequency, SW concept used for systick\n    fn update_sys_core_clock(&self) {}\n}\n\nimpl ConfigurableClock for SysOscConfig {\n    fn enable_and_reset(&self) -> Result<(), ClockError> {\n        if self.state == State::Enabled {\n            return Ok(());\n        }\n\n        // SAFETY: unsafe needed to take pointers to Sysctl0 and Clkctl0, needed to modify clock HW\n        let clkctl0 = unsafe { crate::pac::Clkctl0::steal() };\n        let sysctl0 = unsafe { crate::pac::Sysctl0::steal() };\n\n        // Let CPU run on ffro for safe switching\n        clkctl0.mainclksela().write(|w| w.sel().ffro_clk());\n        clkctl0.mainclksela().write(|w| w.sel().ffro_div_4());\n\n        // Power on SYSXTAL\n        sysctl0.pdruncfg0_clr().write(|w| w.sysxtal_pd().clr_pdruncfg0());\n\n        // Enable system OSC\n        clkctl0\n            .sysoscctl0()\n            .write(|w| w.lp_enable().lp().bypass_enable().normal_mode());\n\n        delay_loop_clocks(260, SYS_OSC_DEFAULT_FREQ.into());\n        Ok(())\n    }\n    fn disable(&self) -> Result<(), ClockError> {\n        // SAFETY: unsafe needed to take pointers to Sysctl0 and Clkctl0, needed to modify clock HW\n        let clkctl0 = unsafe { crate::pac::Clkctl0::steal() };\n        let sysctl0 = unsafe { crate::pac::Sysctl0::steal() };\n\n        // Let CPU run on ffro for safe switching\n        clkctl0.mainclksela().write(|w| w.sel().ffro_clk());\n        clkctl0.mainclksela().write(|w| w.sel().ffro_div_4());\n\n        // Power on SYSXTAL\n        sysctl0.pdruncfg0_set().write(|w| w.sysxtal_pd().set_pdruncfg0());\n        Ok(())\n    }\n    fn get_clock_rate(&self) -> Result<u32, ClockError> {\n        if self.state == State::Enabled {\n            Ok(SYS_OSC_DEFAULT_FREQ)\n        } else {\n            Err(ClockError::ClockNotEnabled)\n        }\n    }\n    fn is_enabled(&self) -> bool {\n        self.state == State::Enabled\n    }\n    fn set_clock_rate(&mut self, _div: u8, _mult: u8, _freq: u32) -> Result<(), ClockError> {\n        Err(ClockError::ClockNotSupported)\n    }\n}\n\n/// Method to delay for a certain number of microseconds given a clock rate\npub fn delay_loop_clocks(usec: u64, freq_mhz: u64) {\n    let mut ticks = usec * freq_mhz / 1_000_000 / 4;\n    if ticks > u64::from(u32::MAX) {\n        ticks = u64::from(u32::MAX);\n    }\n    // won't panic since we check value above\n    cortex_m::asm::delay(ticks as u32);\n}\n\n/// Configure the pad voltage pmc registers for all 3 vddio ranges\nfn set_pad_voltage_range() {\n    // SAFETY: unsafe needed to take pointer to PNC as well as to write specific bits\n    unsafe {\n        let pmc = crate::pac::Pmc::steal();\n        // Set up IO voltages\n        // all 3 ranges need to be 1.71-1.98V which is 01\n        pmc.padvrange().write(|w| {\n            w.vddio_0range()\n                .bits(0b01)\n                .vddio_1range()\n                .bits(0b01)\n                .vddio_2range()\n                .bits(0b01)\n        });\n    }\n}\n\n/// Initialize AHB clock\nfn init_syscpuahb_clk() {\n    // SAFETY: unsafe needed to take pointer to Clkctl0\n    let clkctl0 = unsafe { crate::pac::Clkctl0::steal() };\n    // SAFETY: unsafe needed to write the bits\n    // Set syscpuahbclkdiv to value 2, Subtract 1 since 0-> 1, 1-> 2, etc...\n    clkctl0.syscpuahbclkdiv().write(|w| unsafe { w.div().bits(2 - 1) });\n\n    while clkctl0.syscpuahbclkdiv().read().reqflag().bit_is_set() {}\n}\n\n/// `ClockOut` config\npub struct ClockOutConfig {\n    src: ClkOutSrc,\n    div: u8,\n}\n\n/// `ClockOut` sources\n#[derive(Clone, Copy, Debug, PartialEq, Eq)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\n/// `ClockOut` sources\npub enum ClkOutSrc {\n    /// No Source, reduce power consumption\n    None,\n    /// SFRO clock\n    Sfro,\n    /// External input clock\n    ClkIn,\n    /// Low-power oscillator\n    Lposc,\n    /// FFRO clock\n    Ffro,\n    /// Main clock\n    MainClk,\n    /// Main DSP clock\n    DspMainClk,\n    /// Main Pll clock\n    MainPllClk,\n    /// `SysPll` Aux0 clock\n    Aux0PllClk,\n    /// `SysPll` DSP clock\n    DspPllClk,\n    /// `SysPll` Aux1 clock\n    Aux1PllClk,\n    /// Audio Pll clock\n    AudioPllClk,\n    /// 32 `KHz` RTC\n    RTC32k,\n}\n\n/// Initialize the `ClkOutConfig`\nimpl ClockOutConfig {\n    /// Default configuration for Clock out\n    #[must_use]\n    pub fn default_config() -> Self {\n        Self {\n            src: ClkOutSrc::None,\n            div: 0,\n        }\n    }\n\n    /// Enable the Clock Out output\n    pub fn enable_and_reset(&mut self) -> Result<(), ClockError> {\n        self.set_clkout_source_and_div(self.src, self.div)?;\n        Ok(())\n    }\n\n    /// Disable Clock Out output and select None as the source to conserve power\n    pub fn disable(&mut self) -> Result<(), ClockError> {\n        self.set_clkout_source_and_div(ClkOutSrc::None, 0)?;\n        Ok(())\n    }\n\n    /// Set the source of the Clock Out pin\n    fn set_clkout_source(&mut self, src: ClkOutSrc) -> Result<(), ClockError> {\n        // SAFETY: unsafe needed to take pointers to Clkctl1, needed to set source in HW\n        let cc1 = unsafe { pac::Clkctl1::steal() };\n        match src {\n            ClkOutSrc::None => {\n                cc1.clkoutsel0().write(|w| w.sel().none());\n                cc1.clkoutsel1().write(|w| w.sel().none());\n            }\n            ClkOutSrc::Sfro => {\n                cc1.clkoutsel0().write(|w| w.sel().sfro_clk());\n                cc1.clkoutsel1().write(|w| w.sel().clkoutsel0_output());\n            }\n            ClkOutSrc::ClkIn => {\n                cc1.clkoutsel0().write(|w| w.sel().xtalin_clk());\n                cc1.clkoutsel1().write(|w| w.sel().clkoutsel0_output());\n            }\n            ClkOutSrc::Lposc => {\n                cc1.clkoutsel0().write(|w| w.sel().lposc());\n                cc1.clkoutsel1().write(|w| w.sel().clkoutsel0_output());\n            }\n            ClkOutSrc::Ffro => {\n                cc1.clkoutsel0().write(|w| w.sel().ffro_clk());\n                cc1.clkoutsel1().write(|w| w.sel().clkoutsel0_output());\n            }\n            ClkOutSrc::MainClk => {\n                cc1.clkoutsel0().write(|w| w.sel().main_clk());\n                cc1.clkoutsel1().write(|w| w.sel().clkoutsel0_output());\n            }\n            ClkOutSrc::DspMainClk => {\n                cc1.clkoutsel0().write(|w| w.sel().dsp_main_clk());\n                cc1.clkoutsel1().write(|w| w.sel().clkoutsel0_output());\n            }\n            ClkOutSrc::MainPllClk => {\n                cc1.clkoutsel0().write(|w| w.sel().none());\n                cc1.clkoutsel1().write(|w| w.sel().main_pll_clk());\n            }\n            ClkOutSrc::Aux0PllClk => {\n                cc1.clkoutsel0().write(|w| w.sel().none());\n                cc1.clkoutsel1().write(|w| w.sel().syspll0_aux0_pll_clk());\n            }\n            ClkOutSrc::DspPllClk => {\n                cc1.clkoutsel0().write(|w| w.sel().none());\n                cc1.clkoutsel1().write(|w| w.sel().dsp_pll_clk());\n            }\n            ClkOutSrc::AudioPllClk => {\n                cc1.clkoutsel0().write(|w| w.sel().none());\n                cc1.clkoutsel1().write(|w| w.sel().audio_pll_clk());\n            }\n            ClkOutSrc::Aux1PllClk => {\n                cc1.clkoutsel0().write(|w| w.sel().none());\n                cc1.clkoutsel1().write(|w| w.sel().syspll0_aux1_pll_clk());\n            }\n            ClkOutSrc::RTC32k => {\n                cc1.clkoutsel0().write(|w| w.sel().none());\n                cc1.clkoutsel1().write(|w| w.sel().rtc_clk_32khz());\n            }\n        }\n        self.src = src;\n        Ok(())\n    }\n    /// set the clock out divider\n    /// note that 1 will be added to div when mapping to the divider\n    /// so bits(0) -> divide by 1\n    /// ...\n    /// bits(255)-> divide by 256\n    pub fn set_clkout_divider(&self, div: u8) -> Result<(), ClockError> {\n        // don't wait for clock to be ready if there's no source\n        if self.src != ClkOutSrc::None {\n            let cc1 = unsafe { pac::Clkctl1::steal() };\n\n            cc1.clkoutdiv()\n                .modify(|_, w| unsafe { w.div().bits(div).halt().clear_bit() });\n            while cc1.clkoutdiv().read().reqflag().bit_is_set() {}\n        }\n        Ok(())\n    }\n    /// set the source and divider for the clockout pin\n    pub fn set_clkout_source_and_div(&mut self, src: ClkOutSrc, div: u8) -> Result<(), ClockError> {\n        self.set_clkout_source(src)?;\n\n        self.set_clkout_divider(div)?;\n\n        Ok(())\n    }\n}\n\n/// Using the config, enables all desired clocks to desired clock rates\nfn init_clock_hw(config: ClockConfig) -> Result<(), ClockError> {\n    if let Err(e) = config.rtc.enable_and_reset() {\n        return Err(e);\n    }\n\n    if let Err(e) = config.lposc.enable_and_reset() {\n        return Err(e);\n    }\n\n    if let Err(e) = config.ffro.enable_and_reset() {\n        return Err(e);\n    }\n\n    if let Err(e) = config.sfro.enable_and_reset() {\n        return Err(e);\n    }\n\n    if let Err(e) = config.sys_osc.enable_and_reset() {\n        return Err(e);\n    }\n\n    if let Err(e) = config.main_pll_clk.enable_and_reset() {\n        return Err(e);\n    }\n\n    // Move FLEXSPI clock source from main clock to FFRO to avoid instruction/data fetch issue in XIP when\n    // updating PLL and main clock.\n    // SAFETY: unsafe needed to take pointers to Clkctl0\n    let cc0 = unsafe { pac::Clkctl0::steal() };\n    cc0.flexspifclksel().write(|w| w.sel().ffro_clk());\n\n    // Move ESPI clock source to FFRO\n    #[cfg(feature = \"_espi\")]\n    {\n        cc0.espiclksel().write(|w| w.sel().use_48_60m());\n    }\n\n    init_syscpuahb_clk();\n\n    if let Err(e) = config.main_clk.enable_and_reset() {\n        return Err(e);\n    }\n\n    config.sys_clk.update_sys_core_clock();\n    Ok(())\n}\n\n/// SAFETY: must be called exactly once at bootup\npub(crate) unsafe fn init(config: ClockConfig) -> Result<(), ClockError> {\n    init_clock_hw(config)?;\n\n    // set VDDIO ranges 0-2\n    set_pad_voltage_range();\n    Ok(())\n}\n\n///Trait to expose perph clocks\ntrait SealedSysconPeripheral {\n    fn enable_perph_clock();\n    fn reset_perph();\n    fn disable_perph_clock();\n}\n\n/// Clock and Reset control for peripherals\n#[allow(private_bounds)]\npub trait SysconPeripheral: SealedSysconPeripheral + 'static {}\n/// Enables and resets peripheral `T`.\n///\n/// # Safety\n///\n/// Peripheral must not be in use.\npub fn enable_and_reset<T: SysconPeripheral>() {\n    T::enable_perph_clock();\n    T::reset_perph();\n}\n\n/// Enables peripheral `T`.\npub fn enable<T: SysconPeripheral>() {\n    T::enable_perph_clock();\n}\n\n/// Reset peripheral `T`.\npub fn reset<T: SysconPeripheral>() {\n    T::reset_perph();\n}\n\n/// Disables peripheral `T`.\n///\n/// # Safety\n///\n/// Peripheral must not be in use.\npub fn disable<T: SysconPeripheral>() {\n    T::disable_perph_clock();\n}\nmacro_rules! impl_perph_clk {\n    ($peripheral:ident, $clkctl:ident, $clkreg:ident, $rstctl:ident, $rstreg:ident, $bit:expr) => {\n        impl SealedSysconPeripheral for crate::peripherals::$peripheral {\n            fn enable_perph_clock() {\n                // SAFETY: unsafe needed to take pointers to Rstctl1 and Clkctl1\n                let cc1 = unsafe { pac::$clkctl::steal() };\n\n                paste! {\n                    // SAFETY: unsafe due to the use of bits()\n                    cc1.[<$clkreg _set>]().write(|w| unsafe { w.bits(1 << $bit) });\n                }\n            }\n\n            fn reset_perph() {\n                // SAFETY: unsafe needed to take pointers to Rstctl1 and Clkctl1\n                let rc1 = unsafe { pac::$rstctl::steal() };\n\n                paste! {\n                    // SAFETY: unsafe due to the use of bits()\n                    rc1.[<$rstreg _clr>]().write(|w| unsafe { w.bits(1 << $bit) });\n                }\n            }\n\n            fn disable_perph_clock() {\n                // SAFETY: unsafe needed to take pointers to Rstctl1 and Clkctl1\n                let cc1 = unsafe { pac::$clkctl::steal() };\n\n                paste! {\n                    // SAFETY: unsafe due to the use of bits()\n                    cc1.[<$clkreg _clr>]().write(|w| unsafe { w.bits(1 << $bit) });\n                }\n            }\n        }\n\n        impl SysconPeripheral for crate::peripherals::$peripheral {}\n    };\n}\n\n// These should enabled once the relevant peripherals are implemented.\n// impl_perph_clk!(GPIOINTCTL, Clkctl1, pscctl2, Rstctl1, prstctl2, 30);\n// impl_perph_clk!(OTP, Clkctl0, pscctl0, Rstctl0, prstctl0, 17);\n\n// impl_perph_clk!(ROM_CTL_128KB, Clkctl0, pscctl0, Rstctl0, prstctl0, 2);\n// impl_perph_clk!(USBHS_SRAM, Clkctl0, pscctl0, Rstctl0, prstctl0, 23);\n\nimpl_perph_clk!(PIMCTL, Clkctl1, pscctl2, Rstctl1, prstctl2, 31);\nimpl_perph_clk!(ACMP, Clkctl0, pscctl1, Rstctl0, prstctl1, 15);\nimpl_perph_clk!(ADC0, Clkctl0, pscctl1, Rstctl0, prstctl1, 16);\nimpl_perph_clk!(CASPER, Clkctl0, pscctl0, Rstctl0, prstctl0, 9);\nimpl_perph_clk!(CRC, Clkctl1, pscctl1, Rstctl1, prstctl1, 16);\nimpl_perph_clk!(CTIMER0_COUNT_CHANNEL0, Clkctl1, pscctl2, Rstctl1, prstctl2, 0);\nimpl_perph_clk!(CTIMER1_COUNT_CHANNEL0, Clkctl1, pscctl2, Rstctl1, prstctl2, 1);\nimpl_perph_clk!(CTIMER2_COUNT_CHANNEL0, Clkctl1, pscctl2, Rstctl1, prstctl2, 2);\nimpl_perph_clk!(CTIMER3_COUNT_CHANNEL0, Clkctl1, pscctl2, Rstctl1, prstctl2, 3);\nimpl_perph_clk!(CTIMER4_COUNT_CHANNEL0, Clkctl1, pscctl2, Rstctl1, prstctl2, 4);\nimpl_perph_clk!(DMA0, Clkctl1, pscctl1, Rstctl1, prstctl1, 23);\nimpl_perph_clk!(DMA1, Clkctl1, pscctl1, Rstctl1, prstctl1, 24);\nimpl_perph_clk!(DMIC0, Clkctl1, pscctl0, Rstctl1, prstctl0, 24);\n\n#[cfg(feature = \"_espi\")]\nimpl_perph_clk!(ESPI, Clkctl0, pscctl1, Rstctl0, prstctl1, 7);\n\nimpl_perph_clk!(FLEXCOMM0, Clkctl1, pscctl0, Rstctl1, prstctl0, 8);\nimpl_perph_clk!(FLEXCOMM1, Clkctl1, pscctl0, Rstctl1, prstctl0, 9);\nimpl_perph_clk!(FLEXCOMM14, Clkctl1, pscctl0, Rstctl1, prstctl0, 22);\nimpl_perph_clk!(FLEXCOMM15, Clkctl1, pscctl0, Rstctl1, prstctl0, 23);\nimpl_perph_clk!(FLEXCOMM2, Clkctl1, pscctl0, Rstctl1, prstctl0, 10);\nimpl_perph_clk!(FLEXCOMM3, Clkctl1, pscctl0, Rstctl1, prstctl0, 11);\nimpl_perph_clk!(FLEXCOMM4, Clkctl1, pscctl0, Rstctl1, prstctl0, 12);\nimpl_perph_clk!(FLEXCOMM5, Clkctl1, pscctl0, Rstctl1, prstctl0, 13);\nimpl_perph_clk!(FLEXCOMM6, Clkctl1, pscctl0, Rstctl1, prstctl0, 14);\nimpl_perph_clk!(FLEXCOMM7, Clkctl1, pscctl0, Rstctl1, prstctl0, 15);\nimpl_perph_clk!(FLEXSPI, Clkctl0, pscctl0, Rstctl0, prstctl0, 16);\nimpl_perph_clk!(FREQME, Clkctl1, pscctl1, Rstctl1, prstctl1, 31);\nimpl_perph_clk!(HASHCRYPT, Clkctl0, pscctl0, Rstctl0, prstctl0, 10);\nimpl_perph_clk!(HSGPIO0, Clkctl1, pscctl1, Rstctl1, prstctl1, 0);\nimpl_perph_clk!(HSGPIO1, Clkctl1, pscctl1, Rstctl1, prstctl1, 1);\nimpl_perph_clk!(HSGPIO2, Clkctl1, pscctl1, Rstctl1, prstctl1, 2);\nimpl_perph_clk!(HSGPIO3, Clkctl1, pscctl1, Rstctl1, prstctl1, 3);\nimpl_perph_clk!(HSGPIO4, Clkctl1, pscctl1, Rstctl1, prstctl1, 4);\nimpl_perph_clk!(HSGPIO5, Clkctl1, pscctl1, Rstctl1, prstctl1, 5);\nimpl_perph_clk!(HSGPIO6, Clkctl1, pscctl1, Rstctl1, prstctl1, 6);\nimpl_perph_clk!(HSGPIO7, Clkctl1, pscctl1, Rstctl1, prstctl1, 7);\nimpl_perph_clk!(I3C0, Clkctl1, pscctl2, Rstctl1, prstctl2, 16);\nimpl_perph_clk!(MRT0, Clkctl1, pscctl2, Rstctl1, prstctl2, 8);\nimpl_perph_clk!(MU_A, Clkctl1, pscctl1, Rstctl1, prstctl1, 28);\nimpl_perph_clk!(OS_EVENT, Clkctl1, pscctl0, Rstctl1, prstctl0, 27);\nimpl_perph_clk!(POWERQUAD, Clkctl0, pscctl0, Rstctl0, prstctl0, 8);\nimpl_perph_clk!(PUF, Clkctl0, pscctl0, Rstctl0, prstctl0, 11);\nimpl_perph_clk!(RNG, Clkctl0, pscctl0, Rstctl0, prstctl0, 12);\nimpl_perph_clk!(RTC, Clkctl1, pscctl2, Rstctl1, prstctl2, 7);\nimpl_perph_clk!(SCT0, Clkctl0, pscctl0, Rstctl0, prstctl0, 24);\nimpl_perph_clk!(SECGPIO, Clkctl0, pscctl1, Rstctl0, prstctl1, 24);\nimpl_perph_clk!(SEMA42, Clkctl1, pscctl1, Rstctl1, prstctl1, 29);\nimpl_perph_clk!(USBHSD, Clkctl0, pscctl0, Rstctl0, prstctl0, 21);\nimpl_perph_clk!(USBHSH, Clkctl0, pscctl0, Rstctl0, prstctl0, 22);\nimpl_perph_clk!(USBPHY, Clkctl0, pscctl0, Rstctl0, prstctl0, 20);\nimpl_perph_clk!(USDHC0, Clkctl0, pscctl1, Rstctl0, prstctl1, 2);\nimpl_perph_clk!(USDHC1, Clkctl0, pscctl1, Rstctl0, prstctl1, 3);\nimpl_perph_clk!(UTICK0, Clkctl0, pscctl2, Rstctl0, prstctl2, 0);\nimpl_perph_clk!(WDT0, Clkctl0, pscctl2, Rstctl0, prstctl2, 1);\nimpl_perph_clk!(WDT1, Clkctl1, pscctl2, Rstctl1, prstctl2, 10);\n"
  },
  {
    "path": "embassy-imxrt/src/crc.rs",
    "content": "//! Cyclic Redundancy Check (CRC)\n\nuse core::marker::PhantomData;\n\nuse crate::clocks::{SysconPeripheral, enable_and_reset};\npub use crate::pac::crc_engine::mode::CrcPolynomial as Polynomial;\nuse crate::{Peri, PeripheralType, peripherals};\n\n/// CRC driver.\npub struct Crc<'d> {\n    info: Info,\n    _config: Config,\n    _lifetime: PhantomData<&'d ()>,\n}\n\n/// CRC configuration\npub struct Config {\n    /// Polynomial to be used\n    pub polynomial: Polynomial,\n\n    /// Reverse bit order of input?\n    pub reverse_in: bool,\n\n    /// 1's complement input?\n    pub complement_in: bool,\n\n    /// Reverse CRC bit order?\n    pub reverse_out: bool,\n\n    /// 1's complement CRC?\n    pub complement_out: bool,\n\n    /// CRC Seed\n    pub seed: u32,\n}\n\nimpl Config {\n    /// Create a new CRC config.\n    #[must_use]\n    pub fn new(\n        polynomial: Polynomial,\n        reverse_in: bool,\n        complement_in: bool,\n        reverse_out: bool,\n        complement_out: bool,\n        seed: u32,\n    ) -> Self {\n        Config {\n            polynomial,\n            reverse_in,\n            complement_in,\n            reverse_out,\n            complement_out,\n            seed,\n        }\n    }\n}\n\nimpl Default for Config {\n    fn default() -> Self {\n        Self {\n            polynomial: Polynomial::CrcCcitt,\n            reverse_in: false,\n            complement_in: false,\n            reverse_out: false,\n            complement_out: false,\n            seed: 0xffff,\n        }\n    }\n}\n\nimpl<'d> Crc<'d> {\n    /// Instantiates new CRC peripheral and initializes to default values.\n    pub fn new<T: Instance>(_peripheral: Peri<'d, T>, config: Config) -> Self {\n        // enable CRC clock\n        enable_and_reset::<T>();\n\n        let mut instance = Self {\n            info: T::info(),\n            _config: config,\n            _lifetime: PhantomData,\n        };\n\n        instance.reconfigure();\n        instance\n    }\n\n    /// Reconfigured the CRC peripheral.\n    fn reconfigure(&mut self) {\n        self.info.regs.mode().write(|w| {\n            w.crc_poly()\n                .variant(self._config.polynomial)\n                .bit_rvs_wr()\n                .variant(self._config.reverse_in)\n                .cmpl_wr()\n                .variant(self._config.complement_in)\n                .bit_rvs_sum()\n                .variant(self._config.reverse_out)\n                .cmpl_sum()\n                .variant(self._config.complement_out)\n        });\n\n        // Init CRC value\n        self.info\n            .regs\n            .seed()\n            .write(|w| unsafe { w.crc_seed().bits(self._config.seed) });\n    }\n\n    /// Feeds a byte into the CRC peripheral. Returns the computed checksum.\n    pub fn feed_byte(&mut self, byte: u8) -> u32 {\n        self.info.regs.wr_data8().write(|w| unsafe { w.bits(byte) });\n\n        self.info.regs.sum().read().bits()\n    }\n\n    /// Feeds an slice of bytes into the CRC peripheral. Returns the computed checksum.\n    pub fn feed_bytes(&mut self, bytes: &[u8]) -> u32 {\n        let (prefix, data, suffix) = unsafe { bytes.align_to::<u32>() };\n\n        for b in prefix {\n            self.info.regs.wr_data8().write(|w| unsafe { w.bits(*b) });\n        }\n\n        for d in data {\n            self.info.regs.wr_data32().write(|w| unsafe { w.bits(*d) });\n        }\n\n        for b in suffix {\n            self.info.regs.wr_data8().write(|w| unsafe { w.bits(*b) });\n        }\n\n        self.info.regs.sum().read().bits()\n    }\n\n    /// Feeds a halfword into the CRC peripheral. Returns the computed checksum.\n    pub fn feed_halfword(&mut self, halfword: u16) -> u32 {\n        self.info.regs.wr_data16().write(|w| unsafe { w.bits(halfword) });\n\n        self.info.regs.sum().read().bits()\n    }\n\n    /// Feeds an slice of halfwords into the CRC peripheral. Returns the computed checksum.\n    pub fn feed_halfwords(&mut self, halfwords: &[u16]) -> u32 {\n        for halfword in halfwords {\n            self.info.regs.wr_data16().write(|w| unsafe { w.bits(*halfword) });\n        }\n\n        self.info.regs.sum().read().bits()\n    }\n\n    /// Feeds a words into the CRC peripheral. Returns the computed checksum.\n    pub fn feed_word(&mut self, word: u32) -> u32 {\n        self.info.regs.wr_data32().write(|w| unsafe { w.bits(word) });\n\n        self.info.regs.sum().read().bits()\n    }\n\n    /// Feeds an slice of words into the CRC peripheral. Returns the computed checksum.\n    pub fn feed_words(&mut self, words: &[u32]) -> u32 {\n        for word in words {\n            self.info.regs.wr_data32().write(|w| unsafe { w.bits(*word) });\n        }\n\n        self.info.regs.sum().read().bits()\n    }\n}\n\nstruct Info {\n    regs: crate::pac::CrcEngine,\n}\n\ntrait SealedInstance {\n    fn info() -> Info;\n}\n\n/// CRC instance trait.\n#[allow(private_bounds)]\npub trait Instance: SealedInstance + PeripheralType + SysconPeripheral + 'static + Send {}\n\nimpl Instance for peripherals::CRC {}\n\nimpl SealedInstance for peripherals::CRC {\n    fn info() -> Info {\n        // SAFETY: safe from single executor\n        Info {\n            regs: unsafe { crate::pac::CrcEngine::steal() },\n        }\n    }\n}\n"
  },
  {
    "path": "embassy-imxrt/src/dma.rs",
    "content": "//! DMA driver.\n\nuse core::future::Future;\nuse core::pin::Pin;\nuse core::sync::atomic::{Ordering, compiler_fence};\nuse core::task::{Context, Poll};\n\nuse embassy_hal_internal::{Peri, PeripheralType, impl_peripheral};\nuse embassy_sync::waitqueue::AtomicWaker;\nuse pac::dma0::channel::cfg::Periphreqen;\nuse pac::dma0::channel::xfercfg::{Dstinc, Srcinc, Width};\n\nuse crate::clocks::enable_and_reset;\nuse crate::interrupt::InterruptExt;\nuse crate::peripherals::DMA0;\nuse crate::sealed::Sealed;\nuse crate::{BitIter, interrupt, pac, peripherals};\n\npub(crate) const MAX_CHUNK_SIZE: usize = 1024;\n\n#[cfg(feature = \"rt\")]\n#[interrupt]\nfn DMA0() {\n    let reg = unsafe { crate::pac::Dma0::steal() };\n\n    if reg.intstat().read().activeerrint().bit() {\n        let err = reg.errint0().read().bits();\n\n        for channel in BitIter(err) {\n            error!(\"DMA error interrupt on channel {}!\", channel);\n            reg.errint0().write(|w| unsafe { w.err().bits(1 << channel) });\n            CHANNEL_WAKERS[channel as usize].wake();\n        }\n    }\n\n    if reg.intstat().read().activeint().bit() {\n        let ia = reg.inta0().read().bits();\n\n        for channel in BitIter(ia) {\n            reg.inta0().write(|w| unsafe { w.ia().bits(1 << channel) });\n            CHANNEL_WAKERS[channel as usize].wake();\n        }\n    }\n}\n\n/// Initialize DMA controllers (DMA0 only, for now)\npub(crate) unsafe fn init() {\n    let sysctl0 = crate::pac::Sysctl0::steal();\n    let dmactl0 = crate::pac::Dma0::steal();\n\n    enable_and_reset::<DMA0>();\n\n    interrupt::DMA0.disable();\n    interrupt::DMA0.set_priority(interrupt::Priority::P3);\n\n    dmactl0.ctrl().modify(|_, w| w.enable().set_bit());\n\n    // Set channel descriptor SRAM base address\n    // Descriptor base must be 1K aligned\n    let descriptor_base = core::ptr::addr_of!(DESCRIPTORS.descs) as u32;\n    dmactl0.srambase().write(|w| w.bits(descriptor_base));\n\n    // Ensure AHB priority it highest (M4 == DMAC0)\n    sysctl0.ahbmatrixprior().modify(|_, w| w.m4().bits(0));\n\n    interrupt::DMA0.unpend();\n    interrupt::DMA0.enable();\n}\n\n/// DMA read.\n///\n/// SAFETY: Slice must point to a valid location reachable by DMA.\npub unsafe fn read<'a, C: Channel, W: Word>(ch: Peri<'a, C>, from: *const W, to: *mut [W]) -> Transfer<'a, C> {\n    let count = (to.len().div_ceil(W::size() as usize) - 1) as isize;\n\n    copy_inner(\n        ch,\n        from as *const u32,\n        (to as *mut u32).byte_offset(count * W::size()),\n        W::width(),\n        count,\n        false,\n        true,\n        true,\n    )\n}\n\n/// DMA write.\n///\n/// SAFETY: Slice must point to a valid location reachable by DMA.\npub unsafe fn write<'a, C: Channel, W: Word>(ch: Peri<'a, C>, from: *const [W], to: *mut W) -> Transfer<'a, C> {\n    let count = (from.len().div_ceil(W::size() as usize) - 1) as isize;\n\n    copy_inner(\n        ch,\n        (from as *const u32).byte_offset(count * W::size()),\n        to as *mut u32,\n        W::width(),\n        count,\n        true,\n        false,\n        true,\n    )\n}\n\n/// DMA copy between slices.\n///\n/// SAFETY: Slices must point to locations reachable by DMA.\npub unsafe fn copy<'a, C: Channel, W: Word>(ch: Peri<'a, C>, from: &[W], to: &mut [W]) -> Transfer<'a, C> {\n    let from_len = from.len();\n    let to_len = to.len();\n    assert_eq!(from_len, to_len);\n\n    let count = (from_len.div_ceil(W::size() as usize) - 1) as isize;\n\n    copy_inner(\n        ch,\n        from.as_ptr().byte_offset(count * W::size()) as *const u32,\n        to.as_mut_ptr().byte_offset(count * W::size()) as *mut u32,\n        W::width(),\n        count,\n        true,\n        true,\n        false,\n    )\n}\n\nfn copy_inner<'a, C: Channel>(\n    ch: Peri<'a, C>,\n    from: *const u32,\n    to: *mut u32,\n    width: Width,\n    count: isize,\n    incr_read: bool,\n    incr_write: bool,\n    periph: bool,\n) -> Transfer<'a, C> {\n    let p = ch.regs();\n\n    unsafe {\n        DESCRIPTORS.descs[ch.number() as usize].src = from as u32;\n        DESCRIPTORS.descs[ch.number() as usize].dest = to as u32;\n    }\n\n    compiler_fence(Ordering::SeqCst);\n\n    p.errint0().write(|w| unsafe { w.err().bits(1 << ch.number()) });\n    p.inta0().write(|w| unsafe { w.ia().bits(1 << ch.number()) });\n\n    p.channel(ch.number().into()).cfg().write(|w| {\n        unsafe { w.chpriority().bits(0) }\n            .periphreqen()\n            .variant(match periph {\n                false => Periphreqen::Disabled,\n                true => Periphreqen::Enabled,\n            })\n            .hwtrigen()\n            .clear_bit()\n    });\n\n    p.intenset0().write(|w| unsafe { w.inten().bits(1 << ch.number()) });\n\n    p.channel(ch.number().into()).xfercfg().write(|w| {\n        unsafe { w.xfercount().bits(count as u16) }\n            .cfgvalid()\n            .set_bit()\n            .clrtrig()\n            .set_bit()\n            .reload()\n            .clear_bit()\n            .setinta()\n            .set_bit()\n            .width()\n            .variant(width)\n            .srcinc()\n            .variant(match incr_read {\n                false => Srcinc::NoIncrement,\n                true => Srcinc::WidthX1,\n                // REVISIT: what about WidthX2 and WidthX4?\n            })\n            .dstinc()\n            .variant(match incr_write {\n                false => Dstinc::NoIncrement,\n                true => Dstinc::WidthX1,\n                // REVISIT: what about WidthX2 and WidthX4?\n            })\n    });\n\n    p.enableset0().write(|w| unsafe { w.ena().bits(1 << ch.number()) });\n\n    p.channel(ch.number().into())\n        .xfercfg()\n        .modify(|_, w| w.swtrig().set_bit());\n\n    compiler_fence(Ordering::SeqCst);\n\n    Transfer::new(ch)\n}\n\n/// DMA transfer driver.\n#[must_use = \"futures do nothing unless you `.await` or poll them\"]\npub struct Transfer<'a, C: Channel> {\n    channel: Peri<'a, C>,\n}\n\nimpl<'a, C: Channel> Transfer<'a, C> {\n    pub(crate) fn new(channel: Peri<'a, C>) -> Self {\n        Self { channel }\n    }\n\n    pub(crate) fn abort(&mut self) -> usize {\n        let p = self.channel.regs();\n\n        p.abort0().write(|w| w.channel(self.channel.number()).set_bit());\n        while p.busy0().read().bsy().bits() & (1 << self.channel.number()) != 0 {}\n\n        p.enableclr0()\n            .write(|w| unsafe { w.clr().bits(1 << self.channel.number()) });\n\n        let width: u8 = p\n            .channel(self.channel.number().into())\n            .xfercfg()\n            .read()\n            .width()\n            .variant()\n            .unwrap()\n            .into();\n\n        let count = p\n            .channel(self.channel.number().into())\n            .xfercfg()\n            .read()\n            .xfercount()\n            .bits()\n            + 1;\n\n        usize::from(count) * usize::from(width)\n    }\n}\n\nimpl<'a, C: Channel> Drop for Transfer<'a, C> {\n    fn drop(&mut self) {\n        self.abort();\n    }\n}\n\nimpl<'a, C: Channel> Unpin for Transfer<'a, C> {}\nimpl<'a, C: Channel> Future for Transfer<'a, C> {\n    type Output = ();\n\n    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {\n        // Re-register the waker on each call to poll() because any calls to\n        // wake will deregister the waker.\n        CHANNEL_WAKERS[self.channel.number() as usize].register(cx.waker());\n\n        if self.channel.regs().active0().read().act().bits() & (1 << self.channel.number()) == 0 {\n            Poll::Ready(())\n        } else {\n            Poll::Pending\n        }\n    }\n}\n\n/// DMA channel descriptor\n#[derive(Copy, Clone)]\n#[repr(C)]\nstruct Descriptor {\n    reserved: u32,\n    src: u32,\n    dest: u32,\n    link: u32,\n}\n\nimpl Descriptor {\n    const fn new() -> Self {\n        Self {\n            reserved: 0,\n            src: 0,\n            dest: 0,\n            link: 0,\n        }\n    }\n}\n\n#[repr(align(1024))]\nstruct Descriptors {\n    descs: [Descriptor; CHANNEL_COUNT],\n}\n\nimpl Descriptors {\n    const fn new() -> Self {\n        Self {\n            descs: [const { Descriptor::new() }; CHANNEL_COUNT],\n        }\n    }\n}\n\nstatic mut DESCRIPTORS: Descriptors = Descriptors::new();\nstatic CHANNEL_WAKERS: [AtomicWaker; CHANNEL_COUNT] = [const { AtomicWaker::new() }; CHANNEL_COUNT];\npub(crate) const CHANNEL_COUNT: usize = 33;\n\n/// DMA channel interface.\n#[allow(private_bounds)]\npub trait Channel: PeripheralType + Sealed + Into<AnyChannel> + Sized + 'static {\n    /// Channel number.\n    fn number(&self) -> u8;\n\n    /// Channel registry block.\n    fn regs(&self) -> &'static pac::dma0::RegisterBlock {\n        unsafe { &*crate::pac::Dma0::ptr() }\n    }\n}\n\n/// DMA word.\n#[allow(private_bounds)]\npub trait Word: Sealed {\n    /// Transfer width.\n    fn width() -> Width;\n\n    /// Size in bytes for the width.\n    fn size() -> isize;\n}\n\nimpl Sealed for u8 {}\nimpl Word for u8 {\n    fn width() -> Width {\n        Width::Bit8\n    }\n\n    fn size() -> isize {\n        1\n    }\n}\n\nimpl Sealed for u16 {}\nimpl Word for u16 {\n    fn width() -> Width {\n        Width::Bit16\n    }\n\n    fn size() -> isize {\n        2\n    }\n}\n\nimpl Sealed for u32 {}\nimpl Word for u32 {\n    fn width() -> Width {\n        Width::Bit32\n    }\n\n    fn size() -> isize {\n        4\n    }\n}\n\n/// Type erased DMA channel.\npub struct AnyChannel {\n    number: u8,\n}\n\nimpl_peripheral!(AnyChannel);\n\nimpl Sealed for AnyChannel {}\nimpl Channel for AnyChannel {\n    fn number(&self) -> u8 {\n        self.number\n    }\n}\n\nmacro_rules! channel {\n    ($name:ident, $num:expr) => {\n        impl Sealed for peripherals::$name {}\n        impl Channel for peripherals::$name {\n            fn number(&self) -> u8 {\n                $num\n            }\n        }\n\n        impl From<peripherals::$name> for crate::dma::AnyChannel {\n            fn from(val: peripherals::$name) -> Self {\n                Self { number: val.number() }\n            }\n        }\n    };\n}\n\nchannel!(DMA0_CH0, 0);\nchannel!(DMA0_CH1, 1);\nchannel!(DMA0_CH2, 2);\nchannel!(DMA0_CH3, 3);\nchannel!(DMA0_CH4, 4);\nchannel!(DMA0_CH5, 5);\nchannel!(DMA0_CH6, 6);\nchannel!(DMA0_CH7, 7);\nchannel!(DMA0_CH8, 8);\nchannel!(DMA0_CH9, 9);\nchannel!(DMA0_CH10, 10);\nchannel!(DMA0_CH11, 11);\nchannel!(DMA0_CH12, 12);\nchannel!(DMA0_CH13, 13);\nchannel!(DMA0_CH14, 14);\nchannel!(DMA0_CH15, 15);\nchannel!(DMA0_CH16, 16);\nchannel!(DMA0_CH17, 17);\nchannel!(DMA0_CH18, 18);\nchannel!(DMA0_CH19, 19);\nchannel!(DMA0_CH20, 20);\nchannel!(DMA0_CH21, 21);\nchannel!(DMA0_CH22, 22);\nchannel!(DMA0_CH23, 23);\nchannel!(DMA0_CH24, 24);\nchannel!(DMA0_CH25, 25);\nchannel!(DMA0_CH26, 26);\nchannel!(DMA0_CH27, 27);\nchannel!(DMA0_CH28, 28);\nchannel!(DMA0_CH29, 29);\nchannel!(DMA0_CH30, 30);\nchannel!(DMA0_CH31, 31);\nchannel!(DMA0_CH32, 32);\n"
  },
  {
    "path": "embassy-imxrt/src/flexcomm/mod.rs",
    "content": "//! Implements Flexcomm interface wrapper for easier usage across modules\n\npub mod spi;\npub mod uart;\n\nuse paste::paste;\n\nuse crate::clocks::{SysconPeripheral, enable_and_reset};\nuse crate::peripherals::{\n    FLEXCOMM0, FLEXCOMM1, FLEXCOMM2, FLEXCOMM3, FLEXCOMM4, FLEXCOMM5, FLEXCOMM6, FLEXCOMM7, FLEXCOMM14, FLEXCOMM15,\n};\nuse crate::{PeripheralType, pac};\n\n/// clock selection option\n#[derive(Copy, Clone, Debug)]\npub enum Clock {\n    /// SFRO\n    Sfro,\n\n    /// FFRO\n    Ffro,\n\n    /// `AUDIO_PLL`\n    AudioPll,\n\n    /// MASTER\n    Master,\n\n    /// FCn_FRG with Main clock source\n    FcnFrgMain,\n\n    /// FCn_FRG with Pll clock source\n    FcnFrgPll,\n\n    /// FCn_FRG with Sfro clock source\n    FcnFrgSfro,\n\n    /// FCn_FRG with Ffro clock source\n    FcnFrgFfro,\n\n    /// disabled\n    None,\n}\n\n/// do not allow implementation of trait outside this mod\nmod sealed {\n    /// trait does not get re-exported outside flexcomm mod, allowing us to safely expose only desired APIs\n    pub trait Sealed {}\n}\n\n/// primary low-level flexcomm interface\npub(crate) trait FlexcommLowLevel: sealed::Sealed + PeripheralType + SysconPeripheral + 'static + Send {\n    // fetch the flexcomm register block for direct manipulation\n    fn reg() -> &'static pac::flexcomm0::RegisterBlock;\n\n    // set the clock select for this flexcomm instance and remove from reset\n    fn enable(clk: Clock);\n}\n\nmacro_rules! impl_flexcomm {\n    ($($idx:expr),*) => {\n\t$(\n\t    paste!{\n\t\timpl sealed::Sealed for crate::peripherals::[<FLEXCOMM $idx>] {}\n\n\t\timpl FlexcommLowLevel for crate::peripherals::[<FLEXCOMM $idx>] {\n\t\t    fn reg() -> &'static crate::pac::flexcomm0::RegisterBlock {\n\t\t\t// SAFETY: safe from single executor, enforce\n\t\t\t// via peripheral reference lifetime tracking\n\t\t\tunsafe {\n\t\t\t    &*crate::pac::[<Flexcomm $idx>]::ptr()\n\t\t\t}\n\t\t    }\n\n\t\t    fn enable(clk: Clock) {\n\t\t\t// SAFETY: safe from single executor\n\t\t\tlet clkctl1 = unsafe { crate::pac::Clkctl1::steal() };\n\n\t\t\tclkctl1.flexcomm($idx).fcfclksel().write(|w| match clk {\n\t\t\t    Clock::Sfro => w.sel().sfro_clk(),\n\t\t\t    Clock::Ffro => w.sel().ffro_clk(),\n\t\t\t    Clock::AudioPll => w.sel().audio_pll_clk(),\n\t\t\t    Clock::Master => w.sel().master_clk(),\n\t\t\t    Clock::FcnFrgMain => w.sel().fcn_frg_clk(),\n\t\t\t    Clock::FcnFrgPll => w.sel().fcn_frg_clk(),\n\t\t\t    Clock::FcnFrgSfro => w.sel().fcn_frg_clk(),\n\t\t\t    Clock::FcnFrgFfro => w.sel().fcn_frg_clk(),\n\t\t\t    Clock::None => w.sel().none(), // no clock? throw an error?\n\t\t\t});\n\n\t\t\tclkctl1.flexcomm($idx).frgclksel().write(|w| match clk {\n\t\t\t    Clock::FcnFrgMain => w.sel().main_clk(),\n\t\t\t    Clock::FcnFrgPll => w.sel().frg_pll_clk(),\n\t\t\t    Clock::FcnFrgSfro => w.sel().sfro_clk(),\n\t\t\t    Clock::FcnFrgFfro => w.sel().ffro_clk(),\n\t\t\t    _ => w.sel().none(),    // not using frg ...\n\t\t\t});\n\n\t\t\t// todo: add support for frg div/mult\n\t\t\tclkctl1\n\t\t\t    .flexcomm($idx)\n\t\t\t    .frgctl()\n\t\t\t    .write(|w|\n\t\t\t\t   // SAFETY: unsafe only used for .bits() call\n\t\t\t\t   unsafe { w.mult().bits(0) });\n\n\t\t\tenable_and_reset::<[<FLEXCOMM $idx>]>();\n\t\t    }\n\t\t}\n\t    }\n        )*\n    }\n}\n\nimpl_flexcomm!(0, 1, 2, 3, 4, 5, 6, 7);\n\n// TODO: FLEXCOMM 14 is untested. Enable SPI support on FLEXCOMM14\n// Add special case FLEXCOMM14\nimpl sealed::Sealed for crate::peripherals::FLEXCOMM14 {}\n\nimpl FlexcommLowLevel for crate::peripherals::FLEXCOMM14 {\n    fn reg() -> &'static crate::pac::flexcomm0::RegisterBlock {\n        // SAFETY: safe from single executor, enforce\n        // via peripheral reference lifetime tracking\n        unsafe { &*crate::pac::Flexcomm14::ptr() }\n    }\n\n    fn enable(clk: Clock) {\n        // SAFETY: safe from single executor\n        let clkctl1 = unsafe { crate::pac::Clkctl1::steal() };\n\n        clkctl1.fc14fclksel().write(|w| match clk {\n            Clock::Sfro => w.sel().sfro_clk(),\n            Clock::Ffro => w.sel().ffro_clk(),\n            Clock::AudioPll => w.sel().audio_pll_clk(),\n            Clock::Master => w.sel().master_clk(),\n            Clock::FcnFrgMain => w.sel().fcn_frg_clk(),\n            Clock::FcnFrgPll => w.sel().fcn_frg_clk(),\n            Clock::FcnFrgSfro => w.sel().fcn_frg_clk(),\n            Clock::FcnFrgFfro => w.sel().fcn_frg_clk(),\n            Clock::None => w.sel().none(), // no clock? throw an error?\n        });\n\n        clkctl1.frg14clksel().write(|w| match clk {\n            Clock::FcnFrgMain => w.sel().main_clk(),\n            Clock::FcnFrgPll => w.sel().frg_pll_clk(),\n            Clock::FcnFrgSfro => w.sel().sfro_clk(),\n            Clock::FcnFrgFfro => w.sel().ffro_clk(),\n            _ => w.sel().none(), // not using frg ...\n        });\n\n        // todo: add support for frg div/mult\n        clkctl1.frg14ctl().write(|w|\n                // SAFETY: unsafe only used for .bits() call\n                unsafe { w.mult().bits(0) });\n\n        enable_and_reset::<FLEXCOMM14>();\n    }\n}\n\n// Add special case FLEXCOMM15\nimpl sealed::Sealed for crate::peripherals::FLEXCOMM15 {}\n\nimpl FlexcommLowLevel for crate::peripherals::FLEXCOMM15 {\n    fn reg() -> &'static crate::pac::flexcomm0::RegisterBlock {\n        // SAFETY: safe from single executor, enforce\n        // via peripheral reference lifetime tracking\n        unsafe { &*crate::pac::Flexcomm15::ptr() }\n    }\n\n    fn enable(clk: Clock) {\n        // SAFETY: safe from single executor\n        let clkctl1 = unsafe { crate::pac::Clkctl1::steal() };\n\n        clkctl1.fc15fclksel().write(|w| match clk {\n            Clock::Sfro => w.sel().sfro_clk(),\n            Clock::Ffro => w.sel().ffro_clk(),\n            Clock::AudioPll => w.sel().audio_pll_clk(),\n            Clock::Master => w.sel().master_clk(),\n            Clock::FcnFrgMain => w.sel().fcn_frg_clk(),\n            Clock::FcnFrgPll => w.sel().fcn_frg_clk(),\n            Clock::FcnFrgSfro => w.sel().fcn_frg_clk(),\n            Clock::FcnFrgFfro => w.sel().fcn_frg_clk(),\n            Clock::None => w.sel().none(), // no clock? throw an error?\n        });\n        clkctl1.frg15clksel().write(|w| match clk {\n            Clock::FcnFrgMain => w.sel().main_clk(),\n            Clock::FcnFrgPll => w.sel().frg_pll_clk(),\n            Clock::FcnFrgSfro => w.sel().sfro_clk(),\n            Clock::FcnFrgFfro => w.sel().ffro_clk(),\n            _ => w.sel().none(), // not using frg ...\n        });\n        // todo: add support for frg div/mult\n        clkctl1.frg15ctl().write(|w|\n                // SAFETY: unsafe only used for .bits() call\n                unsafe { w.mult().bits(0) });\n\n        enable_and_reset::<FLEXCOMM15>();\n    }\n}\n\nmacro_rules! into_mode {\n    ($mode:ident, $($fc:ident),*) => {\n        paste! {\n            /// Sealed Mode trait\n            trait [<SealedInto $mode:camel>]: FlexcommLowLevel {}\n\n            /// Select mode of operation\n            #[allow(private_bounds)]\n            pub trait [<Into $mode:camel>]: [<SealedInto $mode:camel>] {\n                /// Set mode of operation\n                fn [<into_ $mode>]() {\n                    Self::reg().pselid().write(|w| w.persel().[<$mode>]());\n                }\n            }\n        }\n\n\t$(\n\t    paste!{\n\t\timpl [<SealedInto $mode:camel>] for crate::peripherals::$fc {}\n\t\timpl [<Into $mode:camel>] for crate::peripherals::$fc {}\n\t    }\n\t)*\n    }\n}\n\ninto_mode!(\n    usart, FLEXCOMM0, FLEXCOMM1, FLEXCOMM2, FLEXCOMM3, FLEXCOMM4, FLEXCOMM5, FLEXCOMM6, FLEXCOMM7\n);\ninto_mode!(\n    spi, FLEXCOMM0, FLEXCOMM1, FLEXCOMM2, FLEXCOMM3, FLEXCOMM4, FLEXCOMM5, FLEXCOMM6, FLEXCOMM7, FLEXCOMM14\n);\ninto_mode!(\n    i2c, FLEXCOMM0, FLEXCOMM1, FLEXCOMM2, FLEXCOMM3, FLEXCOMM4, FLEXCOMM5, FLEXCOMM6, FLEXCOMM7, FLEXCOMM15\n);\n\ninto_mode!(\n    i2s_transmit,\n    FLEXCOMM0,\n    FLEXCOMM1,\n    FLEXCOMM2,\n    FLEXCOMM3,\n    FLEXCOMM4,\n    FLEXCOMM5,\n    FLEXCOMM6,\n    FLEXCOMM7\n);\n\ninto_mode!(\n    i2s_receive,\n    FLEXCOMM0,\n    FLEXCOMM1,\n    FLEXCOMM2,\n    FLEXCOMM3,\n    FLEXCOMM4,\n    FLEXCOMM5,\n    FLEXCOMM6,\n    FLEXCOMM7\n);\n"
  },
  {
    "path": "embassy-imxrt/src/flexcomm/spi.rs",
    "content": "//! Serial Peripheral Interface (SPI) driver.\n\nuse core::future::{Future, poll_fn};\nuse core::marker::PhantomData;\nuse core::task::Poll;\n\nuse embassy_embedded_hal::SetConfig;\nuse embassy_hal_internal::{Peri, PeripheralType};\nuse embassy_sync::waitqueue::AtomicWaker;\npub use embedded_hal_1::spi::{MODE_0, MODE_1, MODE_2, MODE_3, Mode, Phase, Polarity};\nuse paste::paste;\n\nuse crate::flexcomm::Clock;\nuse crate::gpio::{AnyPin, GpioPin as Pin};\nuse crate::interrupt;\nuse crate::interrupt::typelevel::Interrupt;\nuse crate::iopctl::{DriveMode, DriveStrength, Inverter, IopctlPin, Pull, SlewRate};\nuse crate::pac::spi0::cfg::{Cpha, Cpol};\n\n/// Driver move trait.\n#[allow(private_bounds)]\npub trait IoMode: sealed::Sealed {}\n\n/// Blocking mode.\npub struct Blocking;\nimpl sealed::Sealed for Blocking {}\nimpl IoMode for Blocking {}\n\n/// Async mode.\npub struct Async;\nimpl sealed::Sealed for Async {}\nimpl IoMode for Async {}\n\n/// Spi errors.\n#[derive(Debug, Clone, Copy, PartialEq, Eq)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\n#[non_exhaustive]\npub enum Error {\n    // No errors for now.\n}\n\n/// Spi driver.\npub struct Spi<'a, M: IoMode> {\n    info: Info,\n    _phantom: PhantomData<&'a M>,\n}\n\nimpl<'a> Spi<'a, Blocking> {\n    /// Create a SPI driver in blocking mode.\n    pub fn new_blocking<T: Instance>(\n        _inner: Peri<'a, T>,\n        sck: Peri<'a, impl SckPin<T> + 'a>,\n        mosi: Peri<'a, impl MosiPin<T> + 'a>,\n        miso: Peri<'a, impl MisoPin<T> + 'a>,\n        config: Config,\n    ) -> Self {\n        sck.as_sck();\n        mosi.as_mosi();\n        miso.as_miso();\n\n        Self::new_inner(_inner, Some(sck.into()), Some(mosi.into()), Some(miso.into()), config)\n    }\n\n    /// Create a TX-only SPI driver in blocking mode.\n    pub fn new_blocking_txonly<T: Instance>(\n        _inner: Peri<'a, T>,\n        sck: Peri<'a, impl SckPin<T> + 'a>,\n        mosi: Peri<'a, impl MosiPin<T> + 'a>,\n        config: Config,\n    ) -> Self {\n        sck.as_sck();\n        mosi.as_mosi();\n\n        Self::new_inner(_inner, Some(sck.into()), Some(mosi.into()), None, config)\n    }\n\n    /// Create an RX-only SPI driver in blocking mode.\n    pub fn new_blocking_rxonly<T: Instance>(\n        _inner: Peri<'a, T>,\n        sck: Peri<'a, impl SckPin<T> + 'a>,\n        miso: Peri<'a, impl MisoPin<T> + 'a>,\n        config: Config,\n    ) -> Self {\n        sck.as_sck();\n        miso.as_miso();\n\n        Self::new_inner(_inner, Some(sck.into()), None, Some(miso.into()), config)\n    }\n\n    /// Create an internal-loopback SPI driver in blocking mode.\n    ///\n    /// WARNING: This is only useful for testing as it doesn't use any\n    /// external pins.\n    pub fn new_blocking_loopback<T: Instance>(_inner: Peri<'a, T>, config: Config) -> Self {\n        Self::new_inner(_inner, None, None, None, config)\n    }\n}\n\nimpl<'a, M: IoMode> Spi<'a, M> {\n    /// Read data from Spi blocking execution until done.\n    pub fn blocking_read(&mut self, data: &mut [u8]) -> Result<(), Error> {\n        critical_section::with(|_| {\n            self.info\n                .regs\n                .fifostat()\n                .modify(|_, w| w.txerr().set_bit().rxerr().set_bit());\n\n            for word in data.iter_mut() {\n                // wait until we have data in the RxFIFO.\n                while self.info.regs.fifostat().read().rxnotempty().bit_is_clear() {}\n\n                self.info\n                    .regs\n                    .fifowr()\n                    .write(|w| unsafe { w.txdata().bits(*word as u16).len().bits(7) });\n\n                *word = self.info.regs.fiford().read().rxdata().bits() as u8;\n            }\n        });\n\n        self.flush()\n    }\n\n    /// Write data to Spi blocking execution until done.\n    pub fn blocking_write(&mut self, data: &[u8]) -> Result<(), Error> {\n        critical_section::with(|_| {\n            self.info\n                .regs\n                .fifostat()\n                .modify(|_, w| w.txerr().set_bit().rxerr().set_bit());\n\n            for (i, word) in data.iter().enumerate() {\n                // wait until we have space in the TxFIFO.\n                while self.info.regs.fifostat().read().txnotfull().bit_is_clear() {}\n\n                self.info.regs.fifowr().write(|w| {\n                    unsafe { w.txdata().bits(*word as u16).len().bits(7) }\n                        .rxignore()\n                        .set_bit();\n\n                    if i == data.len() - 1 {\n                        w.eot().set_bit();\n                    }\n\n                    w\n                });\n            }\n        });\n\n        self.flush()\n    }\n\n    /// Transfer data to SPI blocking execution until done.\n    pub fn blocking_transfer(&mut self, read: &mut [u8], write: &[u8]) -> Result<(), Error> {\n        let len = read.len().max(write.len());\n\n        critical_section::with(|_| {\n            self.info\n                .regs\n                .fifostat()\n                .modify(|_, w| w.txerr().set_bit().rxerr().set_bit());\n\n            for i in 0..len {\n                let wb = write.get(i).copied().unwrap_or(0);\n\n                // wait until we have space in the TxFIFO.\n                while self.info.regs.fifostat().read().txnotfull().bit_is_clear() {}\n\n                self.info.regs.fifowr().write(|w| {\n                    unsafe { w.txdata().bits(wb as u16).len().bits(7) };\n\n                    if i == len - 1 {\n                        w.eot().set_bit();\n                    }\n\n                    w\n                });\n\n                // wait until we have data in the RxFIFO.\n                while self.info.regs.fifostat().read().rxnotempty().bit_is_clear() {}\n\n                let rb = self.info.regs.fiford().read().rxdata().bits() as u8;\n\n                if let Some(r) = read.get_mut(i) {\n                    *r = rb;\n                }\n            }\n        });\n\n        self.flush()\n    }\n\n    /// Transfer data in place to SPI blocking execution until done.\n    pub fn blocking_transfer_in_place(&mut self, data: &mut [u8]) -> Result<(), Error> {\n        critical_section::with(|_| {\n            self.info\n                .regs\n                .fifostat()\n                .modify(|_, w| w.txerr().set_bit().rxerr().set_bit());\n\n            for word in data {\n                // wait until we have space in the TxFIFO.\n                while self.info.regs.fifostat().read().txnotfull().bit_is_clear() {}\n                self.info\n                    .regs\n                    .fifowr()\n                    .write(|w| unsafe { w.txdata().bits(*word as u16) });\n\n                // wait until we have data in the RxFIFO.\n                while self.info.regs.fifostat().read().rxnotempty().bit_is_clear() {}\n                *word = self.info.regs.fiford().read().rxdata().bits() as u8;\n            }\n        });\n\n        self.flush()\n    }\n\n    /// Block execution until Spi is done.\n    pub fn flush(&mut self) -> Result<(), Error> {\n        let regs = self.info.regs;\n        while regs.stat().read().mstidle().bit_is_clear() {}\n        Ok(())\n    }\n}\n\nimpl<'a> Spi<'a, Async> {\n    /// Create a SPI driver in async mode.\n    pub fn new_async<T: Instance>(\n        _inner: Peri<'a, T>,\n        sck: Peri<'a, impl SckPin<T> + 'a>,\n        mosi: Peri<'a, impl MosiPin<T> + 'a>,\n        miso: Peri<'a, impl MisoPin<T> + 'a>,\n        _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'a,\n        config: Config,\n    ) -> Self {\n        sck.as_sck();\n        mosi.as_mosi();\n        miso.as_miso();\n\n        T::Interrupt::unpend();\n        unsafe { T::Interrupt::enable() };\n\n        Self::new_inner(_inner, Some(sck.into()), Some(mosi.into()), Some(miso.into()), config)\n    }\n\n    /// Create a TX-only SPI driver in async mode.\n    pub fn new_async_txonly<T: Instance>(\n        _inner: Peri<'a, T>,\n        sck: Peri<'a, impl SckPin<T> + 'a>,\n        mosi: Peri<'a, impl MosiPin<T> + 'a>,\n        _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'a,\n        config: Config,\n    ) -> Self {\n        sck.as_sck();\n        mosi.as_mosi();\n\n        T::Interrupt::unpend();\n        unsafe { T::Interrupt::enable() };\n\n        Self::new_inner(_inner, Some(sck.into()), Some(mosi.into()), None, config)\n    }\n\n    /// Create an RX-only SPI driver in async mode.\n    pub fn new_async_rxonly<T: Instance>(\n        _inner: Peri<'a, T>,\n        sck: Peri<'a, impl SckPin<T> + 'a>,\n        miso: Peri<'a, impl MisoPin<T> + 'a>,\n        _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'a,\n        config: Config,\n    ) -> Self {\n        sck.as_sck();\n        miso.as_miso();\n\n        T::Interrupt::unpend();\n        unsafe { T::Interrupt::enable() };\n\n        Self::new_inner(_inner, Some(sck.into()), None, Some(miso.into()), config)\n    }\n\n    /// Create an internal-loopback SPI driver in async mode.\n    ///\n    /// WARNING: This is only useful for testing as it doesn't use any\n    /// external pins.\n    pub fn new_async_loopback<T: Instance>(\n        _inner: Peri<'a, T>,\n        _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'a,\n        config: Config,\n    ) -> Self {\n        T::Interrupt::unpend();\n        unsafe { T::Interrupt::enable() };\n\n        Self::new_inner(_inner, None, None, None, config)\n    }\n\n    /// Read data from Spi async execution until done.\n    pub async fn async_read(&mut self, data: &mut [u8]) -> Result<(), Error> {\n        critical_section::with(|_| {\n            self.info\n                .regs\n                .fifostat()\n                .modify(|_, w| w.txerr().set_bit().rxerr().set_bit());\n        });\n\n        for word in data.iter_mut() {\n            // wait until we have data in the RxFIFO.\n            self.wait_for(\n                |me| {\n                    if me.info.regs.fifostat().read().rxnotempty().bit_is_set() {\n                        Poll::Ready(())\n                    } else {\n                        Poll::Pending\n                    }\n                },\n                |me| {\n                    me.info\n                        .regs\n                        .fifointenset()\n                        .write(|w| w.rxlvl().set_bit().rxerr().set_bit());\n                },\n            )\n            .await;\n\n            self.info\n                .regs\n                .fifowr()\n                .write(|w| unsafe { w.txdata().bits(*word as u16).len().bits(7) });\n\n            *word = self.info.regs.fiford().read().rxdata().bits() as u8;\n        }\n\n        self.async_flush().await;\n\n        Ok(())\n    }\n\n    /// Write data to Spi async execution until done.\n    pub async fn async_write(&mut self, data: &[u8]) -> Result<(), Error> {\n        critical_section::with(|_| {\n            self.info\n                .regs\n                .fifostat()\n                .modify(|_, w| w.txerr().set_bit().rxerr().set_bit());\n        });\n\n        for (i, word) in data.iter().enumerate() {\n            // wait until we have space in the TxFIFO.\n            self.wait_for(\n                |me| {\n                    if me.info.regs.fifostat().read().txnotfull().bit_is_set() {\n                        Poll::Ready(())\n                    } else {\n                        Poll::Pending\n                    }\n                },\n                |me| {\n                    me.info\n                        .regs\n                        .fifointenset()\n                        .write(|w| w.txlvl().set_bit().txerr().set_bit());\n                },\n            )\n            .await;\n\n            self.info.regs.fifowr().write(|w| {\n                unsafe { w.txdata().bits(*word as u16).len().bits(7) }\n                    .rxignore()\n                    .set_bit();\n\n                if i == data.len() - 1 {\n                    w.eot().set_bit();\n                }\n\n                w\n            });\n        }\n\n        self.async_flush().await;\n\n        Ok(())\n    }\n\n    /// Transfer data to SPI async execution until done.\n    pub async fn async_transfer(&mut self, read: &mut [u8], write: &[u8]) -> Result<(), Error> {\n        let len = read.len().max(write.len());\n\n        critical_section::with(|_| {\n            self.info\n                .regs\n                .fifostat()\n                .modify(|_, w| w.txerr().set_bit().rxerr().set_bit());\n        });\n\n        for i in 0..len {\n            let wb = write.get(i).copied().unwrap_or(0);\n\n            // wait until we have space in the TxFIFO.\n            self.wait_for(\n                |me| {\n                    if me.info.regs.fifostat().read().txnotfull().bit_is_set() {\n                        Poll::Ready(())\n                    } else {\n                        Poll::Pending\n                    }\n                },\n                |me| {\n                    me.info.regs.fifotrig().write(|w| w.txlvlena().set_bit());\n                    me.info\n                        .regs\n                        .fifointenset()\n                        .write(|w| w.txlvl().set_bit().txerr().set_bit());\n                },\n            )\n            .await;\n\n            self.info.regs.fifowr().write(|w| {\n                unsafe { w.txdata().bits(wb as u16).len().bits(7) };\n\n                if i == len - 1 {\n                    w.eot().set_bit();\n                }\n\n                w\n            });\n\n            // wait until we have data in the RxFIFO.\n            self.wait_for(\n                |me| {\n                    if me.info.regs.fifostat().read().rxnotempty().bit_is_set() {\n                        Poll::Ready(())\n                    } else {\n                        Poll::Pending\n                    }\n                },\n                |me| {\n                    me.info.regs.fifotrig().write(|w| w.rxlvlena().set_bit());\n                    me.info\n                        .regs\n                        .fifointenset()\n                        .write(|w| w.rxlvl().set_bit().rxerr().set_bit());\n                },\n            )\n            .await;\n\n            let rb = self.info.regs.fiford().read().rxdata().bits() as u8;\n\n            if let Some(r) = read.get_mut(i) {\n                *r = rb;\n            }\n        }\n\n        self.async_flush().await;\n\n        Ok(())\n    }\n\n    /// Transfer data in place to SPI async execution until done.\n    pub async fn async_transfer_in_place(&mut self, data: &mut [u8]) -> Result<(), Error> {\n        critical_section::with(|_| {\n            self.info\n                .regs\n                .fifostat()\n                .modify(|_, w| w.txerr().set_bit().rxerr().set_bit());\n        });\n\n        for word in data {\n            // wait until we have space in the TxFIFO.\n            self.wait_for(\n                |me| {\n                    if me.info.regs.fifostat().read().txnotfull().bit_is_set() {\n                        Poll::Ready(())\n                    } else {\n                        Poll::Pending\n                    }\n                },\n                |me| {\n                    me.info\n                        .regs\n                        .fifointenset()\n                        .write(|w| w.txlvl().set_bit().txerr().set_bit());\n                },\n            )\n            .await;\n\n            self.info\n                .regs\n                .fifowr()\n                .write(|w| unsafe { w.txdata().bits(*word as u16) });\n\n            // wait until we have data in the RxFIFO.\n            self.wait_for(\n                |me| {\n                    if me.info.regs.fifostat().read().rxnotempty().bit_is_set() {\n                        Poll::Ready(())\n                    } else {\n                        Poll::Pending\n                    }\n                },\n                |me| {\n                    me.info\n                        .regs\n                        .fifointenset()\n                        .write(|w| w.rxlvl().set_bit().rxerr().set_bit());\n                },\n            )\n            .await;\n\n            *word = self.info.regs.fiford().read().rxdata().bits() as u8;\n        }\n\n        self.async_flush().await;\n\n        Ok(())\n    }\n\n    /// Async flush.\n    pub fn async_flush(&mut self) -> impl Future<Output = ()> + use<'_, 'a> {\n        self.wait_for(\n            |me| {\n                if me.info.regs.stat().read().mstidle().bit_is_set() {\n                    Poll::Ready(())\n                } else {\n                    Poll::Pending\n                }\n            },\n            |me| {\n                me.info.regs.intenset().write(|w| w.mstidleen().set_bit());\n            },\n        )\n    }\n\n    /// Calls `f` to check if we are ready or not.\n    /// If not, `g` is called once the waker is set (to eg enable the required interrupts).\n    fn wait_for<F, U, G>(&mut self, mut f: F, mut g: G) -> impl Future<Output = U> + use<'_, 'a, F, U, G>\n    where\n        F: FnMut(&mut Self) -> Poll<U>,\n        G: FnMut(&mut Self),\n    {\n        poll_fn(move |cx| {\n            // Register waker before checking condition, to ensure that wakes/interrupts\n            // aren't lost between f() and g()\n            self.info.waker.register(cx.waker());\n            let r = f(self);\n\n            if r.is_pending() {\n                g(self);\n            }\n\n            r\n        })\n    }\n}\n\nimpl<'a, M: IoMode> Spi<'a, M> {\n    fn new_inner<T: Instance>(\n        _inner: Peri<'a, T>,\n        sck: Option<Peri<'a, AnyPin>>,\n        mosi: Option<Peri<'a, AnyPin>>,\n        miso: Option<Peri<'a, AnyPin>>,\n        config: Config,\n    ) -> Self {\n        // REVISIT: allow selecting from multiple clocks.\n        let clk = Self::clock(&config);\n\n        T::enable(clk);\n        T::into_spi();\n\n        Self::apply_config(T::info().regs, &config);\n\n        let info = T::info();\n        let regs = info.regs;\n\n        critical_section::with(|_| match (sck.is_some(), mosi.is_some(), miso.is_some()) {\n            (true, true, true) => {\n                regs.fifocfg().modify(|_, w| {\n                    w.enabletx()\n                        .set_bit()\n                        .emptytx()\n                        .set_bit()\n                        .enablerx()\n                        .set_bit()\n                        .emptyrx()\n                        .set_bit()\n                });\n            }\n            (true, false, true) => {\n                regs.fifocfg().modify(|_, w| {\n                    w.enabletx()\n                        .set_bit()\n                        .emptytx()\n                        .clear_bit()\n                        .enablerx()\n                        .set_bit()\n                        .emptyrx()\n                        .set_bit()\n                });\n            }\n            (true, true, false) => {\n                regs.fifocfg().modify(|_, w| {\n                    w.enabletx()\n                        .set_bit()\n                        .emptytx()\n                        .set_bit()\n                        .enablerx()\n                        .clear_bit()\n                        .emptyrx()\n                        .set_bit()\n                });\n            }\n            (false, _, _) => {\n                regs.fifocfg().modify(|_, w| {\n                    w.enabletx()\n                        .set_bit()\n                        .emptytx()\n                        .set_bit()\n                        .enablerx()\n                        .set_bit()\n                        .emptyrx()\n                        .set_bit()\n                });\n                regs.cfg().modify(|_, w| w.loop_().enabled());\n            }\n            _ => {}\n        });\n\n        Self {\n            info,\n            _phantom: PhantomData,\n        }\n    }\n\n    fn set_config(&mut self, config: &Config) {\n        Self::apply_config(self.info.regs, config);\n    }\n\n    fn clock(config: &Config) -> Clock {\n        const SFRO_CLOCK_SPEED_HZ: u32 = 16_000_000;\n\n        if config.frequency > SFRO_CLOCK_SPEED_HZ {\n            Clock::Ffro\n        } else {\n            Clock::Sfro\n        }\n    }\n\n    fn clock_frequency(clock: Clock) -> u32 {\n        match clock {\n            Clock::Sfro => 16_000_000,\n            Clock::Ffro => 48_000_000,\n            _ => unreachable!(),\n        }\n    }\n\n    fn apply_config(regs: &'static crate::pac::spi0::RegisterBlock, config: &Config) {\n        let polarity = if config.mode.polarity == Polarity::IdleLow {\n            Cpol::Low\n        } else {\n            Cpol::High\n        };\n\n        let phase = if config.mode.phase == Phase::CaptureOnFirstTransition {\n            Cpha::Change\n        } else {\n            Cpha::Capture\n        };\n\n        let clk = Self::clock(config);\n        let div = Self::clock_frequency(clk) / config.frequency - 1;\n\n        critical_section::with(|_| {\n            // disable SPI every time we need to modify configuration.\n            regs.cfg().modify(|_, w| w.enable().disabled());\n\n            regs.cfg().modify(|_, w| {\n                w.cpha()\n                    .variant(phase)\n                    .cpol()\n                    .variant(polarity)\n                    .loop_()\n                    .disabled()\n                    .master()\n                    .master_mode()\n            });\n\n            regs.div().write(|w| unsafe { w.divval().bits(div as u16) });\n\n            regs.cfg().modify(|_, w| w.enable().enabled());\n        });\n    }\n}\n\n/// Spi config.\n#[derive(Clone)]\npub struct Config {\n    /// Frequency in Hertz.\n    pub frequency: u32,\n    /// SPI operating mode.\n    pub mode: Mode,\n}\n\nimpl Default for Config {\n    fn default() -> Self {\n        Self {\n            frequency: 1_000_000,\n            mode: MODE_0,\n        }\n    }\n}\n\nstruct Info {\n    regs: &'static crate::pac::spi0::RegisterBlock,\n    waker: &'static AtomicWaker,\n}\n\n// SAFETY: safety for Send here is the same as the other accessors to\n// unsafe blocks: it must be done from a single executor context.\n//\n// This is a temporary workaround -- a better solution might be to\n// refactor Info to no longer maintain a reference to regs, but\n// instead look up the correct register set and then perform\n// operations within an unsafe block as we do for other peripherals\nunsafe impl Send for Info {}\n\ntrait SealedInstance {\n    fn info() -> Info;\n}\n\n/// Spi interrupt handler.\npub struct InterruptHandler<T: Instance> {\n    _phantom: PhantomData<T>,\n}\n\nimpl<T: Instance> interrupt::typelevel::Handler<T::Interrupt> for InterruptHandler<T> {\n    unsafe fn on_interrupt() {\n        let waker = T::info().waker;\n        let stat = T::info().regs.fifointstat().read();\n\n        if stat.perint().bit_is_set() {\n            T::info().regs.intenclr().write(|w| w.mstidle().clear_bit_by_one());\n        }\n\n        if stat.txlvl().bit_is_set() {\n            T::info().regs.fifointenclr().write(|w| w.txlvl().set_bit());\n        }\n\n        if stat.txerr().bit_is_set() {\n            T::info().regs.fifointenclr().write(|w| w.txerr().set_bit());\n        }\n\n        if stat.rxlvl().bit_is_set() {\n            T::info().regs.fifointenclr().write(|w| w.rxlvl().set_bit());\n        }\n\n        if stat.rxerr().bit_is_set() {\n            T::info().regs.fifointenclr().write(|w| w.rxerr().set_bit());\n        }\n\n        waker.wake();\n    }\n}\n\n/// Spi instance trait.\n#[allow(private_bounds)]\npub trait Instance: crate::flexcomm::IntoSpi + SealedInstance + PeripheralType + 'static + Send {\n    /// Interrupt for this Spi instance.\n    type Interrupt: interrupt::typelevel::Interrupt;\n}\n\nmacro_rules! impl_instance {\n    ($($n:expr),*) => {\n        $(\n            paste!{\n                impl SealedInstance for crate::peripherals::[<FLEXCOMM $n>] {\n                    #[inline]\n                    fn info() -> Info {\n                        static WAKER: AtomicWaker = AtomicWaker::new();\n\n                        Info {\n                            regs: unsafe { &*crate::pac::[<Spi $n>]::ptr() },\n                            waker: &WAKER,\n                        }\n                    }\n                }\n\n                impl Instance for crate::peripherals::[<FLEXCOMM $n>] {\n                    type Interrupt = crate::interrupt::typelevel::[<FLEXCOMM $n>];\n                }\n            }\n        )*\n    }\n}\n\nimpl_instance!(0, 1, 2, 3, 4, 5, 6, 7, 14);\n\nmod sealed {\n    /// Seal a trait\n    pub trait Sealed {}\n}\n\nimpl<T: Pin> sealed::Sealed for T {}\n\n/// IO configuration trait for Spi clk\npub trait SckPin<T: Instance>: Pin + sealed::Sealed + PeripheralType {\n    /// convert the pin to appropriate function for Spi clk usage.\n    fn as_sck(&self);\n}\n\n/// IO configuration trait for Spi mosi\npub trait MosiPin<T: Instance>: Pin + sealed::Sealed + PeripheralType {\n    /// convert the pin to appropriate function for Spi mosi usage.\n    fn as_mosi(&self);\n}\n\n/// IO configuration trait for Spi miso\npub trait MisoPin<T: Instance>: Pin + sealed::Sealed + PeripheralType {\n    /// convert the pin to appropriate function for Spi miso usage.\n    fn as_miso(&self);\n}\n\nmacro_rules! impl_pin_trait {\n    ($fcn:ident, $mode:ident, $($pin:ident, $fn:ident),*) => {\n        paste! {\n            $(\n                impl [<$mode:camel Pin>]<crate::peripherals::$fcn> for crate::peripherals::$pin {\n                    fn [<as_ $mode>](&self) {\n                        // UM11147 table 530 pg 518\n                        self.set_function(crate::iopctl::Function::$fn)\n                            .set_pull(Pull::None)\n                            .enable_input_buffer()\n                            .set_slew_rate(SlewRate::Standard)\n                            .set_drive_strength(DriveStrength::Normal)\n                            .disable_analog_multiplex()\n                            .set_drive_mode(DriveMode::PushPull)\n                            .set_input_inverter(Inverter::Disabled);\n                    }\n                }\n            )*\n        }\n    }\n}\n\n// FLEXCOMM0\nimpl_pin_trait!(FLEXCOMM0, sck, PIO0_0, F1, PIO3_0, F5);\nimpl_pin_trait!(FLEXCOMM0, miso, PIO0_1, F1, PIO3_1, F5);\nimpl_pin_trait!(FLEXCOMM0, mosi, PIO0_2, F1, PIO3_2, F5);\n\n// FLEXCOMM1\nimpl_pin_trait!(FLEXCOMM1, sck, PIO0_7, F1, PIO7_25, F1);\nimpl_pin_trait!(FLEXCOMM1, miso, PIO0_8, F1, PIO7_26, F1);\nimpl_pin_trait!(FLEXCOMM1, mosi, PIO0_9, F1, PIO7_28, F1);\n\n// FLEXCOMM2\nimpl_pin_trait!(FLEXCOMM2, sck, PIO0_14, F1, PIO7_29, F5);\nimpl_pin_trait!(FLEXCOMM2, miso, PIO0_15, F1, PIO7_30, F5);\nimpl_pin_trait!(FLEXCOMM2, mosi, PIO0_16, F1, PIO7_31, F5);\n\n// FLEXCOMM3\nimpl_pin_trait!(FLEXCOMM3, sck, PIO0_21, F1);\nimpl_pin_trait!(FLEXCOMM3, miso, PIO0_22, F1);\nimpl_pin_trait!(FLEXCOMM3, mosi, PIO0_23, F1);\n\n// FLEXCOMM4\nimpl_pin_trait!(FLEXCOMM4, sck, PIO0_28, F1);\nimpl_pin_trait!(FLEXCOMM4, miso, PIO0_29, F1);\nimpl_pin_trait!(FLEXCOMM4, mosi, PIO0_30, F1);\n\n// FLEXCOMM5\nimpl_pin_trait!(FLEXCOMM5, sck, PIO1_3, F1, PIO3_15, F5);\nimpl_pin_trait!(FLEXCOMM5, miso, PIO1_4, F1, PIO3_16, F5);\nimpl_pin_trait!(FLEXCOMM5, mosi, PIO1_5, F1, PIO3_17, F5);\n\n// FLEXCOMM6\nimpl_pin_trait!(FLEXCOMM6, sck, PIO3_25, F1);\nimpl_pin_trait!(FLEXCOMM6, miso, PIO3_26, F1);\nimpl_pin_trait!(FLEXCOMM6, mosi, PIO3_27, F1);\n\n// FLEXCOMM7\nimpl_pin_trait!(FLEXCOMM7, sck, PIO4_0, F1);\nimpl_pin_trait!(FLEXCOMM7, miso, PIO4_1, F1);\nimpl_pin_trait!(FLEXCOMM7, mosi, PIO4_2, F1);\n\n// FLEXCOMM14\nimpl_pin_trait!(FLEXCOMM14, sck, PIO1_11, F1);\nimpl_pin_trait!(FLEXCOMM14, miso, PIO1_12, F1);\nimpl_pin_trait!(FLEXCOMM14, mosi, PIO1_13, F1);\n\n/// Spi Tx DMA trait.\n#[allow(private_bounds)]\npub trait TxDma<T: Instance>: crate::dma::Channel {}\n\n/// Spi Rx DMA trait.\n#[allow(private_bounds)]\npub trait RxDma<T: Instance>: crate::dma::Channel {}\n\nmacro_rules! impl_dma {\n    ($fcn:ident, $mode:ident, $dma:ident) => {\n        paste! {\n            impl [<$mode Dma>]<crate::peripherals::$fcn> for crate::peripherals::$dma {}\n        }\n    };\n}\n\nimpl_dma!(FLEXCOMM0, Rx, DMA0_CH0);\nimpl_dma!(FLEXCOMM0, Tx, DMA0_CH1);\n\nimpl_dma!(FLEXCOMM1, Rx, DMA0_CH2);\nimpl_dma!(FLEXCOMM1, Tx, DMA0_CH3);\n\nimpl_dma!(FLEXCOMM2, Rx, DMA0_CH4);\nimpl_dma!(FLEXCOMM2, Tx, DMA0_CH5);\n\nimpl_dma!(FLEXCOMM3, Rx, DMA0_CH6);\nimpl_dma!(FLEXCOMM3, Tx, DMA0_CH7);\n\nimpl_dma!(FLEXCOMM4, Rx, DMA0_CH8);\nimpl_dma!(FLEXCOMM4, Tx, DMA0_CH9);\n\nimpl_dma!(FLEXCOMM5, Rx, DMA0_CH10);\nimpl_dma!(FLEXCOMM5, Tx, DMA0_CH11);\n\nimpl_dma!(FLEXCOMM6, Rx, DMA0_CH12);\nimpl_dma!(FLEXCOMM6, Tx, DMA0_CH13);\n\nimpl_dma!(FLEXCOMM7, Rx, DMA0_CH14);\nimpl_dma!(FLEXCOMM7, Tx, DMA0_CH15);\n\nimpl_dma!(FLEXCOMM14, Rx, DMA0_CH16);\nimpl_dma!(FLEXCOMM14, Tx, DMA0_CH17);\n\n// ==============================\n\nimpl<'d, M: IoMode> embedded_hal_02::blocking::spi::Transfer<u8> for Spi<'d, M> {\n    type Error = Error;\n    fn transfer<'w>(&mut self, words: &'w mut [u8]) -> Result<&'w [u8], Self::Error> {\n        self.blocking_transfer_in_place(words)?;\n        Ok(words)\n    }\n}\n\nimpl<'d, M: IoMode> embedded_hal_02::blocking::spi::Write<u8> for Spi<'d, M> {\n    type Error = Error;\n\n    fn write(&mut self, words: &[u8]) -> Result<(), Self::Error> {\n        self.blocking_write(words)\n    }\n}\n\nimpl embedded_hal_1::spi::Error for Error {\n    fn kind(&self) -> embedded_hal_1::spi::ErrorKind {\n        match *self {}\n    }\n}\n\nimpl<'d, M: IoMode> embedded_hal_1::spi::ErrorType for Spi<'d, M> {\n    type Error = Error;\n}\n\nimpl<'d, M: IoMode> embedded_hal_1::spi::SpiBus<u8> for Spi<'d, M> {\n    fn flush(&mut self) -> Result<(), Self::Error> {\n        self.flush()\n    }\n\n    fn read(&mut self, words: &mut [u8]) -> Result<(), Self::Error> {\n        self.blocking_read(words)\n    }\n\n    fn write(&mut self, words: &[u8]) -> Result<(), Self::Error> {\n        self.blocking_write(words)\n    }\n\n    fn transfer(&mut self, read: &mut [u8], write: &[u8]) -> Result<(), Self::Error> {\n        self.blocking_transfer(read, write)\n    }\n\n    fn transfer_in_place(&mut self, words: &mut [u8]) -> Result<(), Self::Error> {\n        self.blocking_transfer_in_place(words)\n    }\n}\n\nimpl<'d> embedded_hal_async::spi::SpiBus<u8> for Spi<'d, Async> {\n    async fn flush(&mut self) -> Result<(), Self::Error> {\n        self.async_flush().await;\n\n        Ok(())\n    }\n\n    async fn write(&mut self, words: &[u8]) -> Result<(), Self::Error> {\n        self.async_write(words).await\n    }\n\n    async fn read(&mut self, words: &mut [u8]) -> Result<(), Self::Error> {\n        self.async_read(words).await\n    }\n\n    async fn transfer(&mut self, read: &mut [u8], write: &[u8]) -> Result<(), Self::Error> {\n        self.async_transfer(read, write).await\n    }\n\n    async fn transfer_in_place(&mut self, words: &mut [u8]) -> Result<(), Self::Error> {\n        self.async_transfer_in_place(words).await\n    }\n}\n\nimpl<'d, M: IoMode> SetConfig for Spi<'d, M> {\n    type Config = Config;\n    type ConfigError = ();\n    fn set_config(&mut self, config: &Self::Config) -> Result<(), ()> {\n        self.set_config(config);\n\n        Ok(())\n    }\n}\n"
  },
  {
    "path": "embassy-imxrt/src/flexcomm/uart.rs",
    "content": "//! Universal Asynchronous Receiver Transmitter (UART) driver.\n\nuse core::future::poll_fn;\nuse core::marker::PhantomData;\nuse core::sync::atomic::{AtomicU8, Ordering, compiler_fence};\nuse core::task::Poll;\n\nuse embassy_futures::select::{Either, select};\nuse embassy_hal_internal::drop::OnDrop;\nuse embassy_hal_internal::{Peri, PeripheralType};\nuse embassy_sync::waitqueue::AtomicWaker;\nuse paste::paste;\n\nuse crate::dma::AnyChannel;\nuse crate::flexcomm::Clock;\nuse crate::gpio::{AnyPin, GpioPin as Pin};\nuse crate::interrupt::typelevel::Interrupt;\nuse crate::iopctl::{DriveMode, DriveStrength, Inverter, IopctlPin, Pull, SlewRate};\nuse crate::pac::usart0::cfg::{Clkpol, Datalen, Loop, Paritysel as Parity, Stoplen, Syncen, Syncmst};\nuse crate::pac::usart0::ctl::Cc;\nuse crate::sealed::Sealed;\nuse crate::{dma, interrupt};\n\n/// Driver move trait.\n#[allow(private_bounds)]\npub trait Mode: Sealed {}\n\n/// Blocking mode.\npub struct Blocking;\nimpl Sealed for Blocking {}\nimpl Mode for Blocking {}\n\n/// Async mode.\npub struct Async;\nimpl Sealed for Async {}\nimpl Mode for Async {}\n\n/// Uart driver.\npub struct Uart<'a, M: Mode> {\n    tx: UartTx<'a, M>,\n    rx: UartRx<'a, M>,\n}\n\n/// Uart TX driver.\npub struct UartTx<'a, M: Mode> {\n    info: Info,\n    tx_dma: Option<Peri<'a, AnyChannel>>,\n    _phantom: PhantomData<(&'a (), M)>,\n}\n\n/// Uart RX driver.\npub struct UartRx<'a, M: Mode> {\n    info: Info,\n    rx_dma: Option<Peri<'a, AnyChannel>>,\n    _phantom: PhantomData<(&'a (), M)>,\n}\n\n/// UART config\n#[derive(Clone, Copy)]\npub struct Config {\n    /// Baudrate of the Uart\n    pub baudrate: u32,\n    /// data length\n    pub data_bits: Datalen,\n    /// Parity\n    pub parity: Parity,\n    /// Stop bits\n    pub stop_bits: Stoplen,\n    /// Polarity of the clock\n    pub clock_polarity: Clkpol,\n    /// Sync/ Async operation selection\n    pub operation: Syncen,\n    /// Sync master/slave mode selection (only applicable in sync mode)\n    pub sync_mode_master_select: Syncmst,\n    /// USART continuous Clock generation enable in synchronous master mode.\n    pub continuous_clock: Cc,\n    /// Normal/ loopback mode\n    pub loopback_mode: Loop,\n    /// Clock type\n    pub clock: Clock,\n}\n\nimpl Default for Config {\n    /// Default configuration for single channel sampling.\n    fn default() -> Self {\n        Self {\n            baudrate: 115_200,\n            data_bits: Datalen::Bit8,\n            parity: Parity::NoParity,\n            stop_bits: Stoplen::Bit1,\n            clock_polarity: Clkpol::FallingEdge,\n            operation: Syncen::AsynchronousMode,\n            sync_mode_master_select: Syncmst::Slave,\n            continuous_clock: Cc::ClockOnCharacter,\n            loopback_mode: Loop::Normal,\n            clock: crate::flexcomm::Clock::Sfro,\n        }\n    }\n}\n\n/// Uart Errors\n#[derive(Debug, Copy, Clone, Eq, PartialEq)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub enum Error {\n    /// Read error\n    Read,\n\n    /// Buffer overflow\n    Overrun,\n\n    /// Noise error\n    Noise,\n\n    /// Framing error\n    Framing,\n\n    /// Parity error\n    Parity,\n\n    /// Failure\n    Fail,\n\n    /// Invalid argument\n    InvalidArgument,\n\n    /// Uart baud rate cannot be supported with the given clock\n    UnsupportedBaudrate,\n\n    /// RX FIFO Empty\n    RxFifoEmpty,\n\n    /// TX FIFO Full\n    TxFifoFull,\n\n    /// TX Busy\n    TxBusy,\n}\n/// shorthand for -> `Result<T>`\npub type Result<T> = core::result::Result<T, Error>;\n\nimpl<'a, M: Mode> UartTx<'a, M> {\n    fn new_inner<T: Instance>(tx_dma: Option<Peri<'a, AnyChannel>>) -> Self {\n        let uarttx = Self {\n            info: T::info(),\n            tx_dma,\n            _phantom: PhantomData,\n        };\n        uarttx.info.refcnt.fetch_add(1, Ordering::Relaxed);\n        uarttx\n    }\n}\n\nimpl<'a, M: Mode> Drop for UartTx<'a, M> {\n    fn drop(&mut self) {\n        if self.info.refcnt.fetch_sub(1, Ordering::Relaxed) == 1 {\n            while self.info.regs.stat().read().txidle().bit_is_clear() {}\n\n            self.info.regs.fifointenclr().modify(|_, w| {\n                w.txerr()\n                    .set_bit()\n                    .rxerr()\n                    .set_bit()\n                    .txlvl()\n                    .set_bit()\n                    .rxlvl()\n                    .set_bit()\n            });\n\n            self.info\n                .regs\n                .fifocfg()\n                .modify(|_, w| w.dmatx().clear_bit().dmarx().clear_bit());\n\n            self.info.regs.cfg().modify(|_, w| w.enable().disabled());\n        }\n    }\n}\n\nimpl<'a> UartTx<'a, Blocking> {\n    /// Create a new UART which can only send data\n    /// Unidirectional Uart - Tx only\n    pub fn new_blocking<T: Instance>(_inner: Peri<'a, T>, tx: Peri<'a, impl TxPin<T>>, config: Config) -> Result<Self> {\n        tx.as_tx();\n\n        let _tx = tx.into();\n        Uart::<Blocking>::init::<T>(Some(_tx), None, None, None, config)?;\n\n        Ok(Self::new_inner::<T>(None))\n    }\n\n    fn write_byte_internal(&mut self, byte: u8) -> Result<()> {\n        // SAFETY: unsafe only used for .bits()\n        self.info\n            .regs\n            .fifowr()\n            .write(|w| unsafe { w.txdata().bits(u16::from(byte)) });\n\n        Ok(())\n    }\n\n    fn blocking_write_byte(&mut self, byte: u8) -> Result<()> {\n        while self.info.regs.fifostat().read().txnotfull().bit_is_clear() {}\n\n        // Prevent the compiler from reordering write_byte_internal()\n        // before the loop above.\n        compiler_fence(Ordering::Release);\n\n        self.write_byte_internal(byte)\n    }\n\n    fn write_byte(&mut self, byte: u8) -> Result<()> {\n        if self.info.regs.fifostat().read().txnotfull().bit_is_clear() {\n            Err(Error::TxFifoFull)\n        } else {\n            self.write_byte_internal(byte)\n        }\n    }\n\n    /// Transmit the provided buffer blocking execution until done.\n    pub fn blocking_write(&mut self, buf: &[u8]) -> Result<()> {\n        for x in buf {\n            self.blocking_write_byte(*x)?;\n        }\n\n        Ok(())\n    }\n\n    /// Transmit the provided buffer. Non-blocking version, bails out\n    /// if it would block.\n    pub fn write(&mut self, buf: &[u8]) -> Result<()> {\n        for x in buf {\n            self.write_byte(*x)?;\n        }\n\n        Ok(())\n    }\n\n    /// Flush UART TX blocking execution until done.\n    pub fn blocking_flush(&mut self) -> Result<()> {\n        while self.info.regs.stat().read().txidle().bit_is_clear() {}\n        Ok(())\n    }\n\n    /// Flush UART TX.\n    pub fn flush(&mut self) -> Result<()> {\n        if self.info.regs.stat().read().txidle().bit_is_clear() {\n            Err(Error::TxBusy)\n        } else {\n            Ok(())\n        }\n    }\n}\n\nimpl<'a, M: Mode> UartRx<'a, M> {\n    fn new_inner<T: Instance>(rx_dma: Option<Peri<'a, AnyChannel>>) -> Self {\n        let uartrx = Self {\n            info: T::info(),\n            rx_dma,\n            _phantom: PhantomData,\n        };\n        uartrx.info.refcnt.fetch_add(1, Ordering::Relaxed);\n        uartrx\n    }\n}\n\nimpl<'a, M: Mode> Drop for UartRx<'a, M> {\n    fn drop(&mut self) {\n        if self.info.refcnt.fetch_sub(1, Ordering::Relaxed) == 1 {\n            while self.info.regs.stat().read().rxidle().bit_is_clear() {}\n\n            self.info.regs.fifointenclr().modify(|_, w| {\n                w.txerr()\n                    .set_bit()\n                    .rxerr()\n                    .set_bit()\n                    .txlvl()\n                    .set_bit()\n                    .rxlvl()\n                    .set_bit()\n            });\n\n            self.info\n                .regs\n                .fifocfg()\n                .modify(|_, w| w.dmatx().clear_bit().dmarx().clear_bit());\n\n            self.info.regs.cfg().modify(|_, w| w.enable().disabled());\n        }\n    }\n}\n\nimpl<'a> UartRx<'a, Blocking> {\n    /// Create a new blocking UART which can only receive data\n    pub fn new_blocking<T: Instance>(_inner: Peri<'a, T>, rx: Peri<'a, impl RxPin<T>>, config: Config) -> Result<Self> {\n        rx.as_rx();\n\n        let _rx = rx.into();\n        Uart::<Blocking>::init::<T>(None, Some(_rx), None, None, config)?;\n\n        Ok(Self::new_inner::<T>(None))\n    }\n}\n\nimpl UartRx<'_, Blocking> {\n    fn read_byte_internal(&mut self) -> Result<u8> {\n        if self.info.regs.fifostat().read().rxerr().bit_is_set() {\n            self.info.regs.fifocfg().modify(|_, w| w.emptyrx().set_bit());\n            self.info.regs.fifostat().modify(|_, w| w.rxerr().set_bit());\n            Err(Error::Read)\n        } else if self.info.regs.stat().read().parityerrint().bit_is_set() {\n            self.info.regs.stat().modify(|_, w| w.parityerrint().clear_bit_by_one());\n            Err(Error::Parity)\n        } else if self.info.regs.stat().read().framerrint().bit_is_set() {\n            self.info.regs.stat().modify(|_, w| w.framerrint().clear_bit_by_one());\n            Err(Error::Framing)\n        } else if self.info.regs.stat().read().rxnoiseint().bit_is_set() {\n            self.info.regs.stat().modify(|_, w| w.rxnoiseint().clear_bit_by_one());\n            Err(Error::Noise)\n        } else {\n            let byte = self.info.regs.fiford().read().rxdata().bits() as u8;\n            Ok(byte)\n        }\n    }\n\n    fn read_byte(&mut self) -> Result<u8> {\n        if self.info.regs.fifostat().read().rxnotempty().bit_is_clear() {\n            Err(Error::RxFifoEmpty)\n        } else {\n            self.read_byte_internal()\n        }\n    }\n\n    fn blocking_read_byte(&mut self) -> Result<u8> {\n        while self.info.regs.fifostat().read().rxnotempty().bit_is_clear() {}\n\n        // Prevent the compiler from reordering read_byte_internal()\n        // before the loop above.\n        compiler_fence(Ordering::Acquire);\n\n        self.read_byte_internal()\n    }\n\n    /// Read from UART RX. Non-blocking version, bails out if it would\n    /// block.\n    pub fn read(&mut self, buf: &mut [u8]) -> Result<()> {\n        for b in buf.iter_mut() {\n            *b = self.read_byte()?;\n        }\n\n        Ok(())\n    }\n\n    /// Read from UART RX blocking execution until done.\n    pub fn blocking_read(&mut self, buf: &mut [u8]) -> Result<()> {\n        for b in buf.iter_mut() {\n            *b = self.blocking_read_byte()?;\n        }\n\n        Ok(())\n    }\n}\n\nimpl<'a, M: Mode> Uart<'a, M> {\n    fn init<T: Instance>(\n        tx: Option<Peri<'a, AnyPin>>,\n        rx: Option<Peri<'a, AnyPin>>,\n        rts: Option<Peri<'a, AnyPin>>,\n        cts: Option<Peri<'a, AnyPin>>,\n        config: Config,\n    ) -> Result<()> {\n        T::enable(config.clock);\n        T::into_usart();\n\n        let regs = T::info().regs;\n\n        if tx.is_some() {\n            regs.fifocfg().modify(|_, w| w.emptytx().set_bit().enabletx().enabled());\n\n            // clear FIFO error\n            regs.fifostat().write(|w| w.txerr().set_bit());\n        }\n\n        if rx.is_some() {\n            regs.fifocfg().modify(|_, w| w.emptyrx().set_bit().enablerx().enabled());\n\n            // clear FIFO error\n            regs.fifostat().write(|w| w.rxerr().set_bit());\n        }\n\n        if rts.is_some() && cts.is_some() {\n            regs.cfg().modify(|_, w| w.ctsen().enabled());\n        }\n\n        Self::set_baudrate_inner::<T>(config.baudrate, config.clock)?;\n        Self::set_uart_config::<T>(config);\n\n        Ok(())\n    }\n\n    fn get_fc_freq(clock: Clock) -> Result<u32> {\n        match clock {\n            Clock::Sfro => Ok(16_000_000),\n            Clock::Ffro => Ok(48_000_000),\n            // We only support Sfro and Ffro now.\n            _ => Err(Error::InvalidArgument),\n        }\n    }\n\n    fn set_baudrate_inner<T: Instance>(baudrate: u32, clock: Clock) -> Result<()> {\n        // Get source clock frequency according to clock type.\n        let source_clock_hz = Self::get_fc_freq(clock)?;\n\n        if baudrate == 0 {\n            return Err(Error::InvalidArgument);\n        }\n\n        let regs = T::info().regs;\n\n        // If synchronous master mode is enabled, only configure the BRG value.\n        if regs.cfg().read().syncen().is_synchronous_mode() {\n            // Master\n            if regs.cfg().read().syncmst().is_master() {\n                // Calculate the BRG value\n                let brgval = (source_clock_hz / baudrate) - 1;\n\n                // SAFETY: unsafe only used for .bits()\n                regs.brg().write(|w| unsafe { w.brgval().bits(brgval as u16) });\n            }\n        } else {\n            // Smaller values of OSR can make the sampling position within a\n            // data bit less accurate and may potentially cause more noise\n            // errors or incorrect data.\n            let (_, osr, brg) = (8..16).rev().fold(\n                (u32::MAX, u32::MAX, u32::MAX),\n                |(best_diff, best_osr, best_brg), osrval| {\n                    // Compare source_clock_hz agaist with ((osrval + 1) * baudrate) to make sure\n                    // (source_clock_hz / ((osrval + 1) * baudrate)) is not less than 0.\n                    if source_clock_hz < ((osrval + 1) * baudrate) {\n                        (best_diff, best_osr, best_brg)\n                    } else {\n                        let brgval = (source_clock_hz / ((osrval + 1) * baudrate)) - 1;\n                        // We know brgval will not be less than 0 now, it should have already been a valid u32 value,\n                        // then compare it agaist with 65535.\n                        if brgval > 65535 {\n                            (best_diff, best_osr, best_brg)\n                        } else {\n                            // Calculate the baud rate based on the BRG value\n                            let candidate = source_clock_hz / ((osrval + 1) * (brgval + 1));\n\n                            // Calculate the difference between the\n                            // current baud rate and the desired baud rate\n                            let diff = (candidate as i32 - baudrate as i32).unsigned_abs();\n\n                            // Check if the current calculated difference is the best so far\n                            if diff < best_diff {\n                                (diff, osrval, brgval)\n                            } else {\n                                (best_diff, best_osr, best_brg)\n                            }\n                        }\n                    }\n                },\n            );\n\n            // Value over range\n            if brg > 65535 {\n                return Err(Error::UnsupportedBaudrate);\n            }\n\n            // SAFETY: unsafe only used for .bits()\n            regs.osr().write(|w| unsafe { w.osrval().bits(osr as u8) });\n\n            // SAFETY: unsafe only used for .bits()\n            regs.brg().write(|w| unsafe { w.brgval().bits(brg as u16) });\n        }\n\n        Ok(())\n    }\n\n    fn set_uart_config<T: Instance>(config: Config) {\n        let regs = T::info().regs;\n\n        regs.cfg().modify(|_, w| w.enable().disabled());\n\n        regs.cfg().modify(|_, w| {\n            w.datalen()\n                .variant(config.data_bits)\n                .stoplen()\n                .variant(config.stop_bits)\n                .paritysel()\n                .variant(config.parity)\n                .loop_()\n                .variant(config.loopback_mode)\n                .syncen()\n                .variant(config.operation)\n                .clkpol()\n                .variant(config.clock_polarity)\n        });\n\n        regs.cfg().modify(|_, w| w.enable().enabled());\n    }\n\n    /// Split the Uart into a transmitter and receiver, which is particularly\n    /// useful when having two tasks correlating to transmitting and receiving.\n    pub fn split(self) -> (UartTx<'a, M>, UartRx<'a, M>) {\n        (self.tx, self.rx)\n    }\n\n    /// Split the Uart into a transmitter and receiver by mutable reference,\n    /// which is particularly useful when having two tasks correlating to\n    /// transmitting and receiving.\n    pub fn split_ref(&mut self) -> (&mut UartTx<'a, M>, &mut UartRx<'a, M>) {\n        (&mut self.tx, &mut self.rx)\n    }\n}\n\nimpl<'a> Uart<'a, Blocking> {\n    /// Create a new blocking UART\n    pub fn new_blocking<T: Instance>(\n        _inner: Peri<'a, T>,\n        tx: Peri<'a, impl TxPin<T>>,\n        rx: Peri<'a, impl RxPin<T>>,\n        config: Config,\n    ) -> Result<Self> {\n        tx.as_tx();\n        rx.as_rx();\n\n        let tx = tx.into();\n        let rx = rx.into();\n\n        Self::init::<T>(Some(tx), Some(rx), None, None, config)?;\n\n        Ok(Self {\n            tx: UartTx::new_inner::<T>(None),\n            rx: UartRx::new_inner::<T>(None),\n        })\n    }\n\n    /// Read from UART RX blocking execution until done.\n    pub fn blocking_read(&mut self, buf: &mut [u8]) -> Result<()> {\n        self.rx.blocking_read(buf)\n    }\n\n    /// Read from UART RX. Non-blocking version, bails out if it would\n    /// block.\n    pub fn read(&mut self, buf: &mut [u8]) -> Result<()> {\n        self.rx.read(buf)\n    }\n\n    /// Transmit the provided buffer blocking execution until done.\n    pub fn blocking_write(&mut self, buf: &[u8]) -> Result<()> {\n        self.tx.blocking_write(buf)\n    }\n\n    /// Transmit the provided buffer. Non-blocking version, bails out\n    /// if it would block.\n    pub fn write(&mut self, buf: &[u8]) -> Result<()> {\n        self.tx.write(buf)\n    }\n\n    /// Flush UART TX blocking execution until done.\n    pub fn blocking_flush(&mut self) -> Result<()> {\n        self.tx.blocking_flush()\n    }\n\n    /// Flush UART TX.\n    pub fn flush(&mut self) -> Result<()> {\n        self.tx.flush()\n    }\n}\n\nimpl<'a> UartTx<'a, Async> {\n    /// Create a new DMA enabled UART which can only send data\n    pub fn new_async<T: Instance>(\n        _inner: Peri<'a, T>,\n        tx: Peri<'a, impl TxPin<T>>,\n        _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'a,\n        tx_dma: Peri<'a, impl TxDma<T>>,\n        config: Config,\n    ) -> Result<Self> {\n        tx.as_tx();\n\n        let _tx = tx.into();\n        Uart::<Async>::init::<T>(Some(_tx), None, None, None, config)?;\n\n        T::Interrupt::unpend();\n        unsafe { T::Interrupt::enable() };\n\n        Ok(Self::new_inner::<T>(Some(tx_dma.into())))\n    }\n\n    /// Transmit the provided buffer asynchronously.\n    pub async fn write(&mut self, buf: &[u8]) -> Result<()> {\n        let regs = self.info.regs;\n\n        // Disable DMA on completion/cancellation\n        let _dma_guard = OnDrop::new(|| {\n            regs.fifocfg().modify(|_, w| w.dmatx().disabled());\n        });\n\n        for chunk in buf.chunks(dma::MAX_CHUNK_SIZE) {\n            regs.fifocfg().modify(|_, w| w.dmatx().enabled());\n\n            let ch = self.tx_dma.as_mut().unwrap().reborrow();\n            let transfer = unsafe { dma::write(ch, chunk, regs.fifowr().as_ptr() as *mut u8) };\n\n            let res = select(\n                transfer,\n                poll_fn(|cx| {\n                    UART_WAKERS[self.info.index].register(cx.waker());\n\n                    self.info.regs.intenset().write(|w| {\n                        w.framerren()\n                            .set_bit()\n                            .parityerren()\n                            .set_bit()\n                            .rxnoiseen()\n                            .set_bit()\n                            .aberren()\n                            .set_bit()\n                    });\n\n                    let stat = self.info.regs.stat().read();\n\n                    self.info.regs.stat().write(|w| {\n                        w.framerrint()\n                            .clear_bit_by_one()\n                            .parityerrint()\n                            .clear_bit_by_one()\n                            .rxnoiseint()\n                            .clear_bit_by_one()\n                            .aberr()\n                            .clear_bit_by_one()\n                    });\n\n                    if stat.framerrint().bit_is_set() {\n                        Poll::Ready(Err(Error::Framing))\n                    } else if stat.parityerrint().bit_is_set() {\n                        Poll::Ready(Err(Error::Parity))\n                    } else if stat.rxnoiseint().bit_is_set() {\n                        Poll::Ready(Err(Error::Noise))\n                    } else if stat.aberr().bit_is_set() {\n                        Poll::Ready(Err(Error::Fail))\n                    } else {\n                        Poll::Pending\n                    }\n                }),\n            )\n            .await;\n\n            match res {\n                Either::First(()) | Either::Second(Ok(())) => (),\n                Either::Second(e) => return e,\n            }\n        }\n\n        Ok(())\n    }\n\n    /// Flush UART TX asynchronously.\n    pub async fn flush(&mut self) -> Result<()> {\n        self.wait_on(\n            |me| {\n                if me.info.regs.stat().read().txidle().bit_is_set() {\n                    Poll::Ready(Ok(()))\n                } else {\n                    Poll::Pending\n                }\n            },\n            |me| {\n                me.info.regs.intenset().write(|w| w.txidleen().set_bit());\n            },\n        )\n        .await\n    }\n\n    /// Calls `f` to check if we are ready or not.\n    /// If not, `g` is called once the waker is set (to eg enable the required interrupts).\n    async fn wait_on<F, U, G>(&mut self, mut f: F, mut g: G) -> U\n    where\n        F: FnMut(&mut Self) -> Poll<U>,\n        G: FnMut(&mut Self),\n    {\n        poll_fn(|cx| {\n            // Register waker before checking condition, to ensure that wakes/interrupts\n            // aren't lost between f() and g()\n            UART_WAKERS[self.info.index].register(cx.waker());\n            let r = f(self);\n\n            if r.is_pending() {\n                g(self);\n            }\n\n            r\n        })\n        .await\n    }\n}\n\nimpl<'a> UartRx<'a, Async> {\n    /// Create a new DMA enabled UART which can only receive data\n    pub fn new_async<T: Instance>(\n        _inner: Peri<'a, T>,\n        rx: Peri<'a, impl RxPin<T>>,\n        _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'a,\n        rx_dma: Peri<'a, impl RxDma<T>>,\n        config: Config,\n    ) -> Result<Self> {\n        rx.as_rx();\n\n        let _rx = rx.into();\n        Uart::<Async>::init::<T>(None, Some(_rx), None, None, config)?;\n\n        T::Interrupt::unpend();\n        unsafe { T::Interrupt::enable() };\n\n        Ok(Self::new_inner::<T>(Some(rx_dma.into())))\n    }\n\n    /// Read from UART RX asynchronously.\n    pub async fn read(&mut self, buf: &mut [u8]) -> Result<()> {\n        let regs = self.info.regs;\n\n        // Disable DMA on completion/cancellation\n        let _dma_guard = OnDrop::new(|| {\n            regs.fifocfg().modify(|_, w| w.dmarx().disabled());\n        });\n\n        for chunk in buf.chunks_mut(dma::MAX_CHUNK_SIZE) {\n            regs.fifocfg().modify(|_, w| w.dmarx().enabled());\n\n            let ch = self.rx_dma.as_mut().unwrap().reborrow();\n            let transfer = unsafe { dma::read(ch, regs.fiford().as_ptr() as *const u8, chunk) };\n\n            let res = select(\n                transfer,\n                poll_fn(|cx| {\n                    UART_WAKERS[self.info.index].register(cx.waker());\n\n                    self.info.regs.intenset().write(|w| {\n                        w.framerren()\n                            .set_bit()\n                            .parityerren()\n                            .set_bit()\n                            .rxnoiseen()\n                            .set_bit()\n                            .aberren()\n                            .set_bit()\n                    });\n\n                    let stat = self.info.regs.stat().read();\n\n                    self.info.regs.stat().write(|w| {\n                        w.framerrint()\n                            .clear_bit_by_one()\n                            .parityerrint()\n                            .clear_bit_by_one()\n                            .rxnoiseint()\n                            .clear_bit_by_one()\n                            .aberr()\n                            .clear_bit_by_one()\n                    });\n\n                    if stat.framerrint().bit_is_set() {\n                        Poll::Ready(Err(Error::Framing))\n                    } else if stat.parityerrint().bit_is_set() {\n                        Poll::Ready(Err(Error::Parity))\n                    } else if stat.rxnoiseint().bit_is_set() {\n                        Poll::Ready(Err(Error::Noise))\n                    } else if stat.aberr().bit_is_set() {\n                        Poll::Ready(Err(Error::Fail))\n                    } else {\n                        Poll::Pending\n                    }\n                }),\n            )\n            .await;\n\n            match res {\n                Either::First(()) | Either::Second(Ok(())) => (),\n                Either::Second(e) => return e,\n            }\n        }\n\n        Ok(())\n    }\n}\n\nimpl<'a> Uart<'a, Async> {\n    /// Create a new DMA enabled UART\n    pub fn new_async<T: Instance>(\n        _inner: Peri<'a, T>,\n        tx: Peri<'a, impl TxPin<T>>,\n        rx: Peri<'a, impl RxPin<T>>,\n        _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'a,\n        tx_dma: Peri<'a, impl TxDma<T>>,\n        rx_dma: Peri<'a, impl RxDma<T>>,\n        config: Config,\n    ) -> Result<Self> {\n        tx.as_tx();\n        rx.as_rx();\n\n        let tx = tx.into();\n        let rx = rx.into();\n\n        T::Interrupt::unpend();\n        unsafe { T::Interrupt::enable() };\n\n        Self::init::<T>(Some(tx), Some(rx), None, None, config)?;\n\n        Ok(Self {\n            tx: UartTx::new_inner::<T>(Some(tx_dma.into())),\n            rx: UartRx::new_inner::<T>(Some(rx_dma.into())),\n        })\n    }\n\n    /// Create a new DMA enabled UART with hardware flow control (RTS/CTS)\n    pub fn new_with_rtscts<T: Instance>(\n        _inner: Peri<'a, T>,\n        tx: Peri<'a, impl TxPin<T>>,\n        rx: Peri<'a, impl RxPin<T>>,\n        rts: Peri<'a, impl RtsPin<T>>,\n        cts: Peri<'a, impl CtsPin<T>>,\n        _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'a,\n        tx_dma: Peri<'a, impl TxDma<T>>,\n        rx_dma: Peri<'a, impl RxDma<T>>,\n        config: Config,\n    ) -> Result<Self> {\n        tx.as_tx();\n        rx.as_rx();\n        rts.as_rts();\n        cts.as_cts();\n\n        let tx = tx.into();\n        let rx = rx.into();\n        let rts = rts.into();\n        let cts = cts.into();\n\n        Self::init::<T>(Some(tx), Some(rx), Some(rts), Some(cts), config)?;\n\n        Ok(Self {\n            tx: UartTx::new_inner::<T>(Some(tx_dma.into())),\n            rx: UartRx::new_inner::<T>(Some(rx_dma.into())),\n        })\n    }\n\n    /// Read from UART RX.\n    pub async fn read(&mut self, buf: &mut [u8]) -> Result<()> {\n        self.rx.read(buf).await\n    }\n\n    /// Transmit the provided buffer.\n    pub async fn write(&mut self, buf: &[u8]) -> Result<()> {\n        self.tx.write(buf).await\n    }\n\n    /// Flush UART TX.\n    pub async fn flush(&mut self) -> Result<()> {\n        self.tx.flush().await\n    }\n}\n\nimpl embedded_hal_02::serial::Read<u8> for UartRx<'_, Blocking> {\n    type Error = Error;\n\n    fn read(&mut self) -> core::result::Result<u8, nb::Error<Self::Error>> {\n        let mut buf = [0; 1];\n\n        match self.read(&mut buf) {\n            Ok(_) => Ok(buf[0]),\n            Err(Error::RxFifoEmpty) => Err(nb::Error::WouldBlock),\n            Err(e) => Err(nb::Error::Other(e)),\n        }\n    }\n}\n\nimpl embedded_hal_02::serial::Write<u8> for UartTx<'_, Blocking> {\n    type Error = Error;\n\n    fn write(&mut self, word: u8) -> core::result::Result<(), nb::Error<Self::Error>> {\n        match self.write(&[word]) {\n            Ok(_) => Ok(()),\n            Err(Error::TxFifoFull) => Err(nb::Error::WouldBlock),\n            Err(e) => Err(nb::Error::Other(e)),\n        }\n    }\n\n    fn flush(&mut self) -> core::result::Result<(), nb::Error<Self::Error>> {\n        match self.flush() {\n            Ok(_) => Ok(()),\n            Err(Error::TxBusy) => Err(nb::Error::WouldBlock),\n            Err(e) => Err(nb::Error::Other(e)),\n        }\n    }\n}\n\nimpl embedded_hal_02::blocking::serial::Write<u8> for UartTx<'_, Blocking> {\n    type Error = Error;\n\n    fn bwrite_all(&mut self, buffer: &[u8]) -> core::result::Result<(), Self::Error> {\n        self.blocking_write(buffer)\n    }\n\n    fn bflush(&mut self) -> core::result::Result<(), Self::Error> {\n        self.blocking_flush()\n    }\n}\n\nimpl embedded_hal_02::serial::Read<u8> for Uart<'_, Blocking> {\n    type Error = Error;\n\n    fn read(&mut self) -> core::result::Result<u8, nb::Error<Self::Error>> {\n        embedded_hal_02::serial::Read::read(&mut self.rx)\n    }\n}\n\nimpl embedded_hal_02::serial::Write<u8> for Uart<'_, Blocking> {\n    type Error = Error;\n\n    fn write(&mut self, word: u8) -> core::result::Result<(), nb::Error<Self::Error>> {\n        embedded_hal_02::serial::Write::write(&mut self.tx, word)\n    }\n\n    fn flush(&mut self) -> core::result::Result<(), nb::Error<Self::Error>> {\n        embedded_hal_02::serial::Write::flush(&mut self.tx)\n    }\n}\n\nimpl embedded_hal_02::blocking::serial::Write<u8> for Uart<'_, Blocking> {\n    type Error = Error;\n\n    fn bwrite_all(&mut self, buffer: &[u8]) -> core::result::Result<(), Self::Error> {\n        self.blocking_write(buffer)\n    }\n\n    fn bflush(&mut self) -> core::result::Result<(), Self::Error> {\n        self.blocking_flush()\n    }\n}\n\nimpl embedded_hal_nb::serial::Error for Error {\n    fn kind(&self) -> embedded_hal_nb::serial::ErrorKind {\n        match *self {\n            Self::Framing => embedded_hal_nb::serial::ErrorKind::FrameFormat,\n            Self::Overrun => embedded_hal_nb::serial::ErrorKind::Overrun,\n            Self::Parity => embedded_hal_nb::serial::ErrorKind::Parity,\n            Self::Noise => embedded_hal_nb::serial::ErrorKind::Noise,\n            _ => embedded_hal_nb::serial::ErrorKind::Other,\n        }\n    }\n}\n\nimpl embedded_hal_nb::serial::ErrorType for UartRx<'_, Blocking> {\n    type Error = Error;\n}\n\nimpl embedded_hal_nb::serial::ErrorType for UartTx<'_, Blocking> {\n    type Error = Error;\n}\n\nimpl embedded_hal_nb::serial::ErrorType for Uart<'_, Blocking> {\n    type Error = Error;\n}\n\nimpl embedded_hal_nb::serial::Read for UartRx<'_, Blocking> {\n    fn read(&mut self) -> nb::Result<u8, Self::Error> {\n        let mut buf = [0; 1];\n\n        match self.read(&mut buf) {\n            Ok(_) => Ok(buf[0]),\n            Err(Error::RxFifoEmpty) => Err(nb::Error::WouldBlock),\n            Err(e) => Err(nb::Error::Other(e)),\n        }\n    }\n}\n\nimpl embedded_hal_nb::serial::Write for UartTx<'_, Blocking> {\n    fn write(&mut self, word: u8) -> nb::Result<(), Self::Error> {\n        match self.write(&[word]) {\n            Ok(_) => Ok(()),\n            Err(Error::TxFifoFull) => Err(nb::Error::WouldBlock),\n            Err(e) => Err(nb::Error::Other(e)),\n        }\n    }\n\n    fn flush(&mut self) -> nb::Result<(), Self::Error> {\n        match self.flush() {\n            Ok(_) => Ok(()),\n            Err(Error::TxBusy) => Err(nb::Error::WouldBlock),\n            Err(e) => Err(nb::Error::Other(e)),\n        }\n    }\n}\n\nimpl embedded_hal_nb::serial::Read for Uart<'_, Blocking> {\n    fn read(&mut self) -> core::result::Result<u8, nb::Error<Self::Error>> {\n        embedded_hal_02::serial::Read::read(&mut self.rx)\n    }\n}\n\nimpl embedded_hal_nb::serial::Write for Uart<'_, Blocking> {\n    fn write(&mut self, char: u8) -> nb::Result<(), Self::Error> {\n        self.blocking_write(&[char]).map_err(nb::Error::Other)\n    }\n\n    fn flush(&mut self) -> nb::Result<(), Self::Error> {\n        self.blocking_flush().map_err(nb::Error::Other)\n    }\n}\n\nstruct Info {\n    regs: &'static crate::pac::usart0::RegisterBlock,\n    index: usize,\n    refcnt: AtomicU8,\n}\n\ntrait SealedInstance {\n    fn info() -> Info;\n    fn index() -> usize;\n}\n\n/// UART interrupt handler.\npub struct InterruptHandler<T: Instance> {\n    _phantom: PhantomData<T>,\n}\n\nconst UART_COUNT: usize = 8;\nstatic UART_WAKERS: [AtomicWaker; UART_COUNT] = [const { AtomicWaker::new() }; UART_COUNT];\n\nimpl<T: Instance> interrupt::typelevel::Handler<T::Interrupt> for InterruptHandler<T> {\n    unsafe fn on_interrupt() {\n        let waker = &UART_WAKERS[T::index()];\n        let regs = T::info().regs;\n        let stat = regs.intstat().read();\n\n        if stat.txidle().bit_is_set()\n            || stat.framerrint().bit_is_set()\n            || stat.parityerrint().bit_is_set()\n            || stat.rxnoiseint().bit_is_set()\n            || stat.aberrint().bit_is_set()\n        {\n            regs.intenclr().write(|w| {\n                w.txidleclr()\n                    .set_bit()\n                    .framerrclr()\n                    .set_bit()\n                    .parityerrclr()\n                    .set_bit()\n                    .rxnoiseclr()\n                    .set_bit()\n                    .aberrclr()\n                    .set_bit()\n            });\n        }\n\n        waker.wake();\n    }\n}\n\n/// UART instance trait.\n#[allow(private_bounds)]\npub trait Instance: crate::flexcomm::IntoUsart + SealedInstance + PeripheralType + 'static + Send {\n    /// Interrupt for this UART instance.\n    type Interrupt: interrupt::typelevel::Interrupt;\n}\n\nmacro_rules! impl_instance {\n    ($($n:expr),*) => {\n        $(\n            paste!{\n                impl SealedInstance for crate::peripherals::[<FLEXCOMM $n>] {\n                    fn info() -> Info {\n                        Info {\n                            regs: unsafe { &*crate::pac::[<Usart $n>]::ptr() },\n                            index: $n,\n\t\t\t    refcnt: AtomicU8::new(0),\n                        }\n                    }\n\n                    #[inline]\n                    fn index() -> usize {\n                        $n\n                    }\n                }\n\n                impl Instance for crate::peripherals::[<FLEXCOMM $n>] {\n                    type Interrupt = crate::interrupt::typelevel::[<FLEXCOMM $n>];\n                }\n            }\n        )*\n    };\n}\n\nimpl_instance!(0, 1, 2, 3, 4, 5, 6, 7);\n\nimpl<T: Pin> Sealed for T {}\n\n/// io configuration trait for Uart Tx configuration\npub trait TxPin<T: Instance>: Pin + Sealed + PeripheralType {\n    /// convert the pin to appropriate function for Uart Tx  usage\n    fn as_tx(&self);\n}\n\n/// io configuration trait for Uart Rx configuration\npub trait RxPin<T: Instance>: Pin + Sealed + PeripheralType {\n    /// convert the pin to appropriate function for Uart Rx  usage\n    fn as_rx(&self);\n}\n\n/// io configuration trait for Uart Cts\npub trait CtsPin<T: Instance>: Pin + Sealed + PeripheralType {\n    /// convert the pin to appropriate function for Uart Cts usage\n    fn as_cts(&self);\n}\n\n/// io configuration trait for Uart Rts\npub trait RtsPin<T: Instance>: Pin + Sealed + PeripheralType {\n    /// convert the pin to appropriate function for Uart Rts usage\n    fn as_rts(&self);\n}\n\nmacro_rules! impl_pin_trait {\n    ($fcn:ident, $mode:ident, $($pin:ident, $fn:ident),*) => {\n        paste! {\n            $(\n                impl [<$mode:camel Pin>]<crate::peripherals::$fcn> for crate::peripherals::$pin {\n                    fn [<as_ $mode>](&self) {\n                        // UM11147 table 507 pg 495\n                        self.set_function(crate::iopctl::Function::$fn)\n                            .set_pull(Pull::None)\n                            .enable_input_buffer()\n                            .set_slew_rate(SlewRate::Standard)\n                            .set_drive_strength(DriveStrength::Normal)\n                            .disable_analog_multiplex()\n                            .set_drive_mode(DriveMode::PushPull)\n                            .set_input_inverter(Inverter::Disabled);\n                    }\n                }\n            )*\n        }\n    };\n}\n\n// FLEXCOMM0\nimpl_pin_trait!(FLEXCOMM0, tx, PIO0_1, F1, PIO3_1, F5);\nimpl_pin_trait!(FLEXCOMM0, rx, PIO0_2, F1, PIO3_2, F5);\nimpl_pin_trait!(FLEXCOMM0, cts, PIO0_3, F1, PIO3_3, F5);\nimpl_pin_trait!(FLEXCOMM0, rts, PIO0_4, F1, PIO3_4, F5);\n\n// FLEXCOMM1\nimpl_pin_trait!(FLEXCOMM1, tx, PIO0_8, F1, PIO7_26, F1);\nimpl_pin_trait!(FLEXCOMM1, rx, PIO0_9, F1, PIO7_27, F1);\nimpl_pin_trait!(FLEXCOMM1, cts, PIO0_10, F1, PIO7_28, F1);\nimpl_pin_trait!(FLEXCOMM1, rts, PIO0_11, F1, PIO7_29, F1);\n\n// FLEXCOMM2\nimpl_pin_trait!(FLEXCOMM2, tx, PIO0_15, F1, PIO7_30, F5);\nimpl_pin_trait!(FLEXCOMM2, rx, PIO0_16, F1, PIO7_31, F5);\nimpl_pin_trait!(FLEXCOMM2, cts, PIO0_17, F1, PIO4_8, F5);\nimpl_pin_trait!(FLEXCOMM2, rts, PIO0_18, F1);\n\n// FLEXCOMM3\nimpl_pin_trait!(FLEXCOMM3, tx, PIO0_22, F1);\nimpl_pin_trait!(FLEXCOMM3, rx, PIO0_23, F1);\nimpl_pin_trait!(FLEXCOMM3, cts, PIO0_24, F1);\nimpl_pin_trait!(FLEXCOMM3, rts, PIO0_25, F1);\n\n// FLEXCOMM4\nimpl_pin_trait!(FLEXCOMM4, tx, PIO0_29, F1);\nimpl_pin_trait!(FLEXCOMM4, rx, PIO0_30, F1);\nimpl_pin_trait!(FLEXCOMM4, cts, PIO0_31, F1);\nimpl_pin_trait!(FLEXCOMM4, rts, PIO1_0, F1);\n\n// FLEXCOMM5\nimpl_pin_trait!(FLEXCOMM5, tx, PIO1_4, F1, PIO3_16, F5);\nimpl_pin_trait!(FLEXCOMM5, rx, PIO1_5, F1, PIO3_17, F5);\nimpl_pin_trait!(FLEXCOMM5, cts, PIO1_6, F1, PIO3_18, F5);\nimpl_pin_trait!(FLEXCOMM5, rts, PIO1_7, F1, PIO3_23, F5);\n\n// FLEXCOMM6\nimpl_pin_trait!(FLEXCOMM6, tx, PIO3_26, F1);\nimpl_pin_trait!(FLEXCOMM6, rx, PIO3_27, F1);\nimpl_pin_trait!(FLEXCOMM6, cts, PIO3_28, F1);\nimpl_pin_trait!(FLEXCOMM6, rts, PIO3_29, F1);\n\n// FLEXCOMM7\nimpl_pin_trait!(FLEXCOMM7, tx, PIO4_1, F1);\nimpl_pin_trait!(FLEXCOMM7, rx, PIO4_2, F1);\nimpl_pin_trait!(FLEXCOMM7, cts, PIO4_3, F1);\nimpl_pin_trait!(FLEXCOMM7, rts, PIO4_4, F1);\n\n/// UART Tx DMA trait.\n#[allow(private_bounds)]\npub trait TxDma<T: Instance>: crate::dma::Channel {}\n\n/// UART Rx DMA trait.\n#[allow(private_bounds)]\npub trait RxDma<T: Instance>: crate::dma::Channel {}\n\nmacro_rules! impl_dma {\n    ($fcn:ident, $mode:ident, $dma:ident) => {\n        paste! {\n            impl [<$mode Dma>]<crate::peripherals::$fcn> for crate::peripherals::$dma {}\n        }\n    };\n}\n\nimpl_dma!(FLEXCOMM0, Rx, DMA0_CH0);\nimpl_dma!(FLEXCOMM0, Tx, DMA0_CH1);\n\nimpl_dma!(FLEXCOMM1, Rx, DMA0_CH2);\nimpl_dma!(FLEXCOMM1, Tx, DMA0_CH3);\n\nimpl_dma!(FLEXCOMM2, Rx, DMA0_CH4);\nimpl_dma!(FLEXCOMM2, Tx, DMA0_CH5);\n\nimpl_dma!(FLEXCOMM3, Rx, DMA0_CH6);\nimpl_dma!(FLEXCOMM3, Tx, DMA0_CH7);\n\nimpl_dma!(FLEXCOMM4, Rx, DMA0_CH8);\nimpl_dma!(FLEXCOMM4, Tx, DMA0_CH9);\n\nimpl_dma!(FLEXCOMM5, Rx, DMA0_CH10);\nimpl_dma!(FLEXCOMM5, Tx, DMA0_CH11);\n\nimpl_dma!(FLEXCOMM6, Rx, DMA0_CH12);\nimpl_dma!(FLEXCOMM6, Tx, DMA0_CH13);\n\nimpl_dma!(FLEXCOMM7, Rx, DMA0_CH14);\nimpl_dma!(FLEXCOMM7, Tx, DMA0_CH15);\n"
  },
  {
    "path": "embassy-imxrt/src/fmt.rs",
    "content": "#![macro_use]\n#![allow(unused)]\n\nuse core::fmt::{Debug, Display, LowerHex};\n\n#[collapse_debuginfo(yes)]\nmacro_rules! assert {\n    ($($x:tt)*) => {\n        {\n            #[cfg(not(feature = \"defmt\"))]\n            ::core::assert!($($x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::assert!($($x)*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! assert_eq {\n    ($($x:tt)*) => {\n        {\n            #[cfg(not(feature = \"defmt\"))]\n            ::core::assert_eq!($($x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::assert_eq!($($x)*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! assert_ne {\n    ($($x:tt)*) => {\n        {\n            #[cfg(not(feature = \"defmt\"))]\n            ::core::assert_ne!($($x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::assert_ne!($($x)*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! debug_assert {\n    ($($x:tt)*) => {\n        {\n            #[cfg(not(feature = \"defmt\"))]\n            ::core::debug_assert!($($x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::debug_assert!($($x)*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! debug_assert_eq {\n    ($($x:tt)*) => {\n        {\n            #[cfg(not(feature = \"defmt\"))]\n            ::core::debug_assert_eq!($($x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::debug_assert_eq!($($x)*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! debug_assert_ne {\n    ($($x:tt)*) => {\n        {\n            #[cfg(not(feature = \"defmt\"))]\n            ::core::debug_assert_ne!($($x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::debug_assert_ne!($($x)*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! todo {\n    ($($x:tt)*) => {\n        {\n            #[cfg(not(feature = \"defmt\"))]\n            ::core::todo!($($x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::todo!($($x)*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! unreachable {\n    ($($x:tt)*) => {\n        {\n            #[cfg(not(feature = \"defmt\"))]\n            ::core::unreachable!($($x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::unreachable!($($x)*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! panic {\n    ($($x:tt)*) => {\n        {\n            #[cfg(not(feature = \"defmt\"))]\n            ::core::panic!($($x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::panic!($($x)*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! trace {\n    ($s:literal $(, $x:expr)* $(,)?) => {\n        {\n            #[cfg(feature = \"defmt\")]\n            ::defmt::trace!($s $(, $x)*);\n            #[cfg(not(feature = \"defmt\"))]\n            let _ = ($( & $x ),*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! debug {\n    ($s:literal $(, $x:expr)* $(,)?) => {\n        {\n            #[cfg(feature = \"defmt\")]\n            ::defmt::debug!($s $(, $x)*);\n            #[cfg(not(feature = \"defmt\"))]\n            let _ = ($( & $x ),*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! info {\n    ($s:literal $(, $x:expr)* $(,)?) => {\n        {\n            #[cfg(feature = \"defmt\")]\n            ::defmt::info!($s $(, $x)*);\n            #[cfg(not(feature = \"defmt\"))]\n            let _ = ($( & $x ),*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! warn {\n    ($s:literal $(, $x:expr)* $(,)?) => {\n        {\n            #[cfg(feature = \"defmt\")]\n            ::defmt::warn!($s $(, $x)*);\n            #[cfg(not(feature = \"defmt\"))]\n            let _ = ($( & $x ),*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! error {\n    ($s:literal $(, $x:expr)* $(,)?) => {\n        {\n            #[cfg(feature = \"defmt\")]\n            ::defmt::error!($s $(, $x)*);\n            #[cfg(not(feature = \"defmt\"))]\n            let _ = ($( & $x ),*);\n        }\n    };\n}\n\n#[cfg(feature = \"defmt\")]\n#[collapse_debuginfo(yes)]\nmacro_rules! unwrap {\n    ($($x:tt)*) => {\n        ::defmt::unwrap!($($x)*)\n    };\n}\n\n#[cfg(not(feature = \"defmt\"))]\n#[collapse_debuginfo(yes)]\nmacro_rules! unwrap {\n    ($arg:expr) => {\n        match $crate::fmt::Try::into_result($arg) {\n            ::core::result::Result::Ok(t) => t,\n            ::core::result::Result::Err(e) => {\n                ::core::panic!(\"unwrap of `{}` failed: {:?}\", ::core::stringify!($arg), e);\n            }\n        }\n    };\n    ($arg:expr, $($msg:expr),+ $(,)? ) => {\n        match $crate::fmt::Try::into_result($arg) {\n            ::core::result::Result::Ok(t) => t,\n            ::core::result::Result::Err(e) => {\n                ::core::panic!(\"unwrap of `{}` failed: {}: {:?}\", ::core::stringify!($arg), ::core::format_args!($($msg,)*), e);\n            }\n        }\n    }\n}\n\n#[derive(Debug, Copy, Clone, Eq, PartialEq)]\npub struct NoneError;\n\npub trait Try {\n    type Ok;\n    type Error;\n    fn into_result(self) -> Result<Self::Ok, Self::Error>;\n}\n\nimpl<T> Try for Option<T> {\n    type Ok = T;\n    type Error = NoneError;\n\n    #[inline]\n    fn into_result(self) -> Result<T, NoneError> {\n        self.ok_or(NoneError)\n    }\n}\n\nimpl<T, E> Try for Result<T, E> {\n    type Ok = T;\n    type Error = E;\n\n    #[inline]\n    fn into_result(self) -> Self {\n        self\n    }\n}\n\npub(crate) struct Bytes<'a>(pub &'a [u8]);\n\nimpl Debug for Bytes<'_> {\n    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {\n        write!(f, \"{:#02x?}\", self.0)\n    }\n}\n\nimpl Display for Bytes<'_> {\n    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {\n        write!(f, \"{:#02x?}\", self.0)\n    }\n}\n\nimpl LowerHex for Bytes<'_> {\n    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {\n        write!(f, \"{:#02x?}\", self.0)\n    }\n}\n\n#[cfg(feature = \"defmt\")]\nimpl defmt::Format for Bytes<'_> {\n    fn format(&self, fmt: defmt::Formatter) {\n        defmt::write!(fmt, \"{:02x}\", self.0)\n    }\n}\n"
  },
  {
    "path": "embassy-imxrt/src/gpio.rs",
    "content": "//! GPIO\n\nuse core::convert::Infallible;\nuse core::future::Future;\nuse core::marker::PhantomData;\nuse core::pin::Pin as FuturePin;\nuse core::task::{Context, Poll};\n\nuse embassy_hal_internal::interrupt::InterruptExt;\nuse embassy_sync::waitqueue::AtomicWaker;\n\nuse crate::clocks::enable_and_reset;\nuse crate::iopctl::IopctlPin;\npub use crate::iopctl::{AnyPin, DriveMode, DriveStrength, Function, Inverter, Pull, SlewRate};\nuse crate::sealed::Sealed;\nuse crate::{BitIter, Peri, PeripheralType, interrupt, peripherals};\n\n// This should be unique per IMXRT package\nconst PORT_COUNT: usize = 8;\n\n/// Digital input or output level.\n#[derive(Debug, Eq, PartialEq, Copy, Clone)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub enum Level {\n    /// Logic Low\n    Low,\n    /// Logic High\n    High,\n}\n\nimpl From<bool> for Level {\n    fn from(val: bool) -> Self {\n        match val {\n            true => Self::High,\n            false => Self::Low,\n        }\n    }\n}\n\nimpl From<Level> for bool {\n    fn from(level: Level) -> bool {\n        match level {\n            Level::Low => false,\n            Level::High => true,\n        }\n    }\n}\n\n/// Interrupt trigger levels.\n#[derive(Debug, Eq, PartialEq, Copy, Clone)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub enum InterruptType {\n    /// Trigger on level.\n    Level,\n    /// Trigger on edge.\n    Edge,\n}\n\n#[cfg(feature = \"rt\")]\n#[interrupt]\n#[allow(non_snake_case)]\nfn GPIO_INTA() {\n    irq_handler(&GPIO_WAKERS);\n}\n\n#[cfg(feature = \"rt\")]\nfn irq_handler(port_wakers: &[Option<&PortWaker>]) {\n    let reg = unsafe { crate::pac::Gpio::steal() };\n\n    for (port, port_waker) in port_wakers.iter().enumerate() {\n        let Some(port_waker) = port_waker else {\n            continue;\n        };\n\n        let stat = reg.intstata(port).read().bits();\n        for pin in BitIter(stat) {\n            // Clear the interrupt from this pin\n            reg.intstata(port).write(|w| unsafe { w.status().bits(1 << pin) });\n            // Disable interrupt from this pin\n            reg.intena(port)\n                .modify(|r, w| unsafe { w.int_en().bits(r.int_en().bits() & !(1 << pin)) });\n\n            let Some(waker) = port_waker.get_waker(pin as usize) else {\n                continue;\n            };\n\n            waker.wake();\n        }\n    }\n}\n\n/// Initialization Logic\n/// Note: GPIO port clocks are initialized in the clocks module.\npub(crate) fn init() {\n    // Enable GPIO clocks\n    enable_and_reset::<peripherals::HSGPIO0>();\n    enable_and_reset::<peripherals::HSGPIO1>();\n    enable_and_reset::<peripherals::HSGPIO2>();\n    enable_and_reset::<peripherals::HSGPIO3>();\n    enable_and_reset::<peripherals::HSGPIO4>();\n    enable_and_reset::<peripherals::HSGPIO5>();\n    enable_and_reset::<peripherals::HSGPIO6>();\n    enable_and_reset::<peripherals::HSGPIO7>();\n\n    // Enable INTA\n    interrupt::GPIO_INTA.unpend();\n\n    // SAFETY:\n    //\n    // At this point, all GPIO interrupts are masked. No interrupts\n    // will trigger until a pin is configured as Input, which can only\n    // happen after initialization of the HAL\n    unsafe { interrupt::GPIO_INTA.enable() };\n}\n\n/// Input Sense mode.\npub trait Sense: Sealed {}\n\n/// Sense Enabled Flex pin.\n///\n/// This is a true flex pin as the input buffer is enabled.\n/// It can sense its own level when even when configured as an output pin.\npub enum SenseEnabled {}\nimpl Sealed for SenseEnabled {}\nimpl Sense for SenseEnabled {}\n\n/// Sense Enabled Flex pin.\n///\n/// This is **not** a true flex pin as the input buffer is disabled.\n/// It cannot be turned into an input and it cannot see its own state, but it consumes less power.\n/// Consider using a sense enabled flex pin if you need to read the pin's state or turn this into an input,\n/// however note that **power consumption may be increased**.\npub enum SenseDisabled {}\nimpl Sealed for SenseDisabled {}\nimpl Sense for SenseDisabled {}\n\n/// Flex pin.\n///\n/// This pin can be either an input or output pin. The output level register bit will\n/// remain set while not in output mode, so the pin's level will be 'remembered' when it is not in\n/// output mode.\npub struct Flex<'d, S: Sense> {\n    pin: Peri<'d, AnyPin>,\n    _sense_mode: PhantomData<S>,\n}\n\nimpl<S: Sense> Flex<'_, S> {\n    /// Converts pin to output pin\n    ///\n    /// The pin level will be whatever was set before (or low by default). If you want it to begin\n    /// at a specific level, call `set_high`/`set_low` on the pin first.\n    pub fn set_as_output(&mut self, mode: DriveMode, strength: DriveStrength, slew_rate: SlewRate) {\n        self.pin\n            .set_pull(Pull::None)\n            .set_drive_mode(mode)\n            .set_drive_strength(strength)\n            .set_slew_rate(slew_rate);\n\n        self.pin.block().dirset(self.pin.port()).write(|w|\n            // SAFETY: Writing a 0 to bits in this register has no effect,\n            // however PAC has it marked unsafe due to using the bits() method.\n            // There is not currently a \"safe\" method for setting a single-bit.\n            unsafe { w.dirsetp().bits(1 << self.pin.pin()) });\n    }\n\n    /// Set high\n    pub fn set_high(&mut self) {\n        self.pin.block().set(self.pin.port()).write(|w|\n            // SAFETY: Writing a 0 to bits in this register has no effect,\n            // however PAC has it marked unsafe due to using the bits() method.\n            // There is not currently a \"safe\" method for setting a single-bit.\n            unsafe { w.setp().bits(1 << self.pin.pin()) });\n    }\n\n    /// Set low\n    pub fn set_low(&mut self) {\n        self.pin.block().clr(self.pin.port()).write(|w|\n            // SAFETY: Writing a 0 to bits in this register has no effect,\n            // however PAC has it marked unsafe due to using the bits() method.\n            // There is not currently a \"safe\" method for setting a single-bit.\n            unsafe { w.clrp().bits(1 << self.pin.pin()) });\n    }\n\n    /// Set level\n    pub fn set_level(&mut self, level: Level) {\n        match level {\n            Level::High => self.set_high(),\n            Level::Low => self.set_low(),\n        }\n    }\n\n    /// Is the output level high?\n    #[must_use]\n    pub fn is_set_high(&self) -> bool {\n        !self.is_set_low()\n    }\n\n    /// Is the output level low?\n    #[must_use]\n    pub fn is_set_low(&self) -> bool {\n        (self.pin.block().set(self.pin.port()).read().setp().bits() & (1 << self.pin.pin())) == 0\n    }\n\n    /// Toggle\n    pub fn toggle(&mut self) {\n        self.pin.block().not(self.pin.port()).write(|w|\n            // SAFETY: Writing a 0 to bits in this register has no effect,\n            // however PAC has it marked unsafe due to using the bits() method.\n            // There is not currently a \"safe\" method for setting a single-bit.\n            unsafe { w.notp().bits(1 << self.pin.pin()) });\n    }\n}\n\nimpl<S: Sense> Drop for Flex<'_, S> {\n    #[inline]\n    fn drop(&mut self) {\n        critical_section::with(|_| {\n            self.pin.reset();\n        });\n    }\n}\n\nimpl<'d> Flex<'d, SenseEnabled> {\n    /// New flex pin.\n    pub fn new_with_sense(pin: Peri<'d, impl GpioPin>) -> Self {\n        pin.set_function(Function::F0)\n            .disable_analog_multiplex()\n            .enable_input_buffer();\n\n        Self {\n            pin: pin.into(),\n            _sense_mode: PhantomData::<SenseEnabled>,\n        }\n    }\n\n    /// Converts pin to input pin\n    pub fn set_as_input(&mut self, pull: Pull, inverter: Inverter) {\n        self.pin.set_pull(pull).set_input_inverter(inverter);\n\n        self.pin.block().dirclr(self.pin.port()).write(|w|\n                    // SAFETY: Writing a 0 to bits in this register has no effect,\n                    // however PAC has it marked unsafe due to using the bits() method.\n                    // There is not currently a \"safe\" method for setting a single-bit.\n                    unsafe { w.dirclrp().bits(1 << self.pin.pin()) });\n    }\n\n    /// Converts pin to special function pin\n    /// # Safety\n    /// Unsafe to require justifying change from default to a special function\n    ///\n    pub unsafe fn set_as_special_function(&mut self, func: Function) {\n        self.pin.set_function(func);\n    }\n\n    /// Is high?\n    #[must_use]\n    pub fn is_high(&self) -> bool {\n        !self.is_low()\n    }\n\n    /// Is low?\n    #[must_use]\n    pub fn is_low(&self) -> bool {\n        self.pin.block().b(self.pin.port()).b_(self.pin.pin()).read() == 0\n    }\n\n    /// Current level\n    #[must_use]\n    pub fn get_level(&self) -> Level {\n        self.is_high().into()\n    }\n\n    /// Wait until the pin is high. If it is already high, return immediately.\n    #[inline]\n    pub async fn wait_for_high(&mut self) {\n        InputFuture::new(self.pin.reborrow(), InterruptType::Level, Level::High).await;\n    }\n\n    /// Wait until the pin is low. If it is already low, return immediately.\n    #[inline]\n    pub async fn wait_for_low(&mut self) {\n        InputFuture::new(self.pin.reborrow(), InterruptType::Level, Level::Low).await;\n    }\n\n    /// Wait for the pin to undergo a transition from low to high.\n    #[inline]\n    pub async fn wait_for_rising_edge(&mut self) {\n        InputFuture::new(self.pin.reborrow(), InterruptType::Edge, Level::High).await;\n    }\n\n    /// Wait for the pin to undergo a transition from high to low.\n    #[inline]\n    pub async fn wait_for_falling_edge(&mut self) {\n        InputFuture::new(self.pin.reborrow(), InterruptType::Edge, Level::Low).await;\n    }\n\n    /// Wait for the pin to undergo any transition, i.e low to high OR high to low.\n    #[inline]\n    pub async fn wait_for_any_edge(&mut self) {\n        if self.is_high() {\n            InputFuture::new(self.pin.reborrow(), InterruptType::Edge, Level::Low).await;\n        } else {\n            InputFuture::new(self.pin.reborrow(), InterruptType::Edge, Level::High).await;\n        }\n    }\n\n    /// Return a new Flex pin instance with level sensing disabled.\n    ///\n    /// Consumes less power than a flex pin with sensing enabled.\n    #[must_use]\n    pub fn disable_sensing(self) -> Flex<'d, SenseDisabled> {\n        // Cloning the pin is ok since we consume self immediately\n        let new_pin = unsafe { self.pin.clone_unchecked() };\n        drop(self);\n        Flex::<SenseDisabled>::new(new_pin)\n    }\n}\n\nimpl<'d> Flex<'d, SenseDisabled> {\n    /// New flex pin.\n    pub fn new(pin: Peri<'d, impl GpioPin>) -> Self {\n        pin.set_function(Function::F0)\n            .disable_analog_multiplex()\n            .disable_input_buffer();\n\n        Self {\n            pin: pin.into(),\n            _sense_mode: PhantomData::<SenseDisabled>,\n        }\n    }\n\n    /// Return a new Flex pin instance with level sensing enabled.\n    #[must_use]\n    pub fn enable_sensing(self) -> Flex<'d, SenseEnabled> {\n        // Cloning the pin is ok since we consume self immediately\n        let new_pin = unsafe { self.pin.clone_unchecked() };\n        drop(self);\n        Flex::new_with_sense(new_pin)\n    }\n}\n\n/// Input pin\npub struct Input<'d> {\n    // When Input is dropped, Flex's drop() will make sure the pin is reset to its default state.\n    pin: Flex<'d, SenseEnabled>,\n}\n\nimpl<'d> Input<'d> {\n    /// New input pin\n    pub fn new(pin: Peri<'d, impl GpioPin>, pull: Pull, inverter: Inverter) -> Self {\n        let mut pin = Flex::new_with_sense(pin);\n        pin.set_as_input(pull, inverter);\n        Self { pin }\n    }\n\n    /// Is high?\n    #[must_use]\n    pub fn is_high(&self) -> bool {\n        self.pin.is_high()\n    }\n\n    /// Is low?\n    #[must_use]\n    pub fn is_low(&self) -> bool {\n        self.pin.is_low()\n    }\n\n    /// Input level\n    #[must_use]\n    pub fn get_level(&self) -> Level {\n        self.pin.get_level()\n    }\n\n    /// Wait until the pin is high. If it is already high, return immediately.\n    #[inline]\n    pub async fn wait_for_high(&mut self) {\n        self.pin.wait_for_high().await;\n    }\n\n    /// Wait until the pin is low. If it is already low, return immediately.\n    #[inline]\n    pub async fn wait_for_low(&mut self) {\n        self.pin.wait_for_low().await;\n    }\n\n    /// Wait for the pin to undergo a transition from low to high.\n    #[inline]\n    pub async fn wait_for_rising_edge(&mut self) {\n        self.pin.wait_for_rising_edge().await;\n    }\n\n    /// Wait for the pin to undergo a transition from high to low.\n    #[inline]\n    pub async fn wait_for_falling_edge(&mut self) {\n        self.pin.wait_for_falling_edge().await;\n    }\n\n    /// Wait for the pin to undergo any transition, i.e low to high OR high to low.\n    #[inline]\n    pub async fn wait_for_any_edge(&mut self) {\n        self.pin.wait_for_any_edge().await;\n    }\n}\n\n#[must_use = \"futures do nothing unless you `.await` or poll them\"]\nstruct InputFuture<'d> {\n    pin: Peri<'d, AnyPin>,\n}\n\nimpl<'d> InputFuture<'d> {\n    fn new(pin: Peri<'d, impl GpioPin>, int_type: InterruptType, level: Level) -> Self {\n        critical_section::with(|_| {\n            // Clear any existing pending interrupt on this pin\n            pin.block()\n                .intstata(pin.port())\n                .write(|w| unsafe { w.status().bits(1 << pin.pin()) });\n\n            /* Pin interrupt configuration */\n            pin.block().intedg(pin.port()).modify(|r, w| match int_type {\n                InterruptType::Edge => unsafe { w.bits(r.bits() | (1 << pin.pin())) },\n                InterruptType::Level => unsafe { w.bits(r.bits() & !(1 << pin.pin())) },\n            });\n\n            pin.block().intpol(pin.port()).modify(|r, w| match level {\n                Level::High => unsafe { w.bits(r.bits() & !(1 << pin.pin())) },\n                Level::Low => unsafe { w.bits(r.bits() | (1 << pin.pin())) },\n            });\n\n            // Enable pin interrupt on GPIO INT A\n            pin.block()\n                .intena(pin.port())\n                .modify(|r, w| unsafe { w.int_en().bits(r.int_en().bits() | (1 << pin.pin())) });\n        });\n\n        Self { pin: pin.into() }\n    }\n}\n\nimpl Future for InputFuture<'_> {\n    type Output = ();\n\n    fn poll(self: FuturePin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {\n        // We need to register/re-register the waker for each poll because any\n        // calls to wake will deregister the waker.\n        if self.pin.port() >= GPIO_WAKERS.len() {\n            panic!(\"Invalid GPIO port index {}\", self.pin.port());\n        }\n\n        let port_waker = GPIO_WAKERS[self.pin.port()];\n        if port_waker.is_none() {\n            panic!(\"Waker not present for GPIO port {}\", self.pin.port());\n        }\n\n        let waker = port_waker.unwrap().get_waker(self.pin.pin());\n        if waker.is_none() {\n            panic!(\n                \"Waker not present for GPIO pin {}, port {}\",\n                self.pin.pin(),\n                self.pin.port()\n            );\n        }\n        waker.unwrap().register(cx.waker());\n\n        // Double check that the pin interrut has been disabled by IRQ handler\n        if self.pin.block().intena(self.pin.port()).read().bits() & (1 << self.pin.pin()) == 0 {\n            Poll::Ready(())\n        } else {\n            Poll::Pending\n        }\n    }\n}\n\n/// Output pin\n/// Cannot be set as an input and cannot read its own pin state!\n/// Consider using a Flex pin if you want that functionality, at the cost of higher power consumption.\npub struct Output<'d> {\n    // When Output is dropped, Flex's drop() will make sure the pin is reset to its default state.\n    pin: Flex<'d, SenseDisabled>,\n}\n\nimpl<'d> Output<'d> {\n    /// New output pin\n    pub fn new(\n        pin: Peri<'d, impl GpioPin>,\n        initial_output: Level,\n        mode: DriveMode,\n        strength: DriveStrength,\n        slew_rate: SlewRate,\n    ) -> Self {\n        let mut pin = Flex::new(pin);\n        pin.set_level(initial_output);\n        pin.set_as_output(mode, strength, slew_rate);\n\n        Self { pin }\n    }\n\n    /// Set high\n    pub fn set_high(&mut self) {\n        self.pin.set_high();\n    }\n\n    /// Set low\n    pub fn set_low(&mut self) {\n        self.pin.set_low();\n    }\n\n    /// Toggle\n    pub fn toggle(&mut self) {\n        self.pin.toggle();\n    }\n\n    /// Set level\n    pub fn set_level(&mut self, level: Level) {\n        self.pin.set_level(level);\n    }\n\n    /// Is set high?\n    #[must_use]\n    pub fn is_set_high(&self) -> bool {\n        self.pin.is_set_high()\n    }\n\n    /// Is set low?\n    #[must_use]\n    pub fn is_set_low(&self) -> bool {\n        self.pin.is_set_low()\n    }\n}\n\ntrait SealedPin: IopctlPin {\n    fn pin_port(&self) -> usize;\n\n    fn port(&self) -> usize {\n        self.pin_port() / 32\n    }\n\n    fn pin(&self) -> usize {\n        self.pin_port() % 32\n    }\n\n    fn block(&self) -> crate::pac::Gpio {\n        // SAFETY: Assuming GPIO pin specific registers are only accessed through this HAL,\n        // this is safe because the HAL ensures ownership or exclusive mutable references\n        // to pins.\n        unsafe { crate::pac::Gpio::steal() }\n    }\n}\n\n/// GPIO pin trait.\n#[allow(private_bounds)]\npub trait GpioPin: SealedPin + Sized + PeripheralType + Into<AnyPin> + 'static {\n    /// Type-erase the pin.\n    fn degrade(self) -> AnyPin {\n        // SAFETY: This is only called within the GpioPin trait, which is only\n        // implemented within this module on valid pin peripherals and thus\n        // has been verified to be correct.\n        unsafe { AnyPin::steal(self.port() as u8, self.pin() as u8) }\n    }\n}\n\nimpl SealedPin for AnyPin {\n    fn pin_port(&self) -> usize {\n        self.pin_port()\n    }\n}\nimpl GpioPin for AnyPin {}\n\nmacro_rules! impl_pin {\n    ($pin_periph:ident, $pin_port:expr, $pin_no:expr) => {\n        impl SealedPin for crate::peripherals::$pin_periph {\n            fn pin_port(&self) -> usize {\n                $pin_port * 32 + $pin_no\n            }\n        }\n        impl GpioPin for crate::peripherals::$pin_periph {}\n        impl From<crate::peripherals::$pin_periph> for AnyPin {\n            fn from(value: crate::peripherals::$pin_periph) -> Self {\n                value.degrade()\n            }\n        }\n    };\n}\n\n/// Container for pin wakers\nstruct PortWaker {\n    offset: usize,\n    wakers: &'static [AtomicWaker],\n}\n\nimpl PortWaker {\n    fn get_waker(&self, pin: usize) -> Option<&AtomicWaker> {\n        self.wakers.get(pin - self.offset)\n    }\n}\n\nmacro_rules! define_port_waker {\n    ($name:ident, $start:expr, $end:expr) => {\n        mod $name {\n            static PIN_WAKERS: [super::AtomicWaker; $end - $start + 1] =\n                [const { super::AtomicWaker::new() }; $end - $start + 1];\n            pub static WAKER: super::PortWaker = super::PortWaker {\n                offset: $start,\n                wakers: &PIN_WAKERS,\n            };\n        }\n    };\n}\n\n// GPIO port 0\ndefine_port_waker!(port0_waker, 0, 31);\nimpl_pin!(PIO0_0, 0, 0);\nimpl_pin!(PIO0_1, 0, 1);\nimpl_pin!(PIO0_2, 0, 2);\nimpl_pin!(PIO0_3, 0, 3);\nimpl_pin!(PIO0_4, 0, 4);\nimpl_pin!(PIO0_5, 0, 5);\nimpl_pin!(PIO0_6, 0, 6);\nimpl_pin!(PIO0_7, 0, 7);\nimpl_pin!(PIO0_8, 0, 8);\nimpl_pin!(PIO0_9, 0, 9);\nimpl_pin!(PIO0_10, 0, 10);\nimpl_pin!(PIO0_11, 0, 11);\nimpl_pin!(PIO0_12, 0, 12);\nimpl_pin!(PIO0_13, 0, 13);\nimpl_pin!(PIO0_14, 0, 14);\nimpl_pin!(PIO0_15, 0, 15);\nimpl_pin!(PIO0_16, 0, 16);\nimpl_pin!(PIO0_17, 0, 17);\nimpl_pin!(PIO0_18, 0, 18);\nimpl_pin!(PIO0_19, 0, 19);\nimpl_pin!(PIO0_20, 0, 20);\nimpl_pin!(PIO0_21, 0, 21);\nimpl_pin!(PIO0_22, 0, 22);\nimpl_pin!(PIO0_23, 0, 23);\nimpl_pin!(PIO0_24, 0, 24);\nimpl_pin!(PIO0_25, 0, 25);\nimpl_pin!(PIO0_26, 0, 26);\nimpl_pin!(PIO0_27, 0, 27);\nimpl_pin!(PIO0_28, 0, 28);\nimpl_pin!(PIO0_29, 0, 29);\nimpl_pin!(PIO0_30, 0, 30);\nimpl_pin!(PIO0_31, 0, 31);\n\n// GPIO port 1\ndefine_port_waker!(port1_waker, 0, 31);\nimpl_pin!(PIO1_0, 1, 0);\nimpl_pin!(PIO1_1, 1, 1);\nimpl_pin!(PIO1_2, 1, 2);\nimpl_pin!(PIO1_3, 1, 3);\nimpl_pin!(PIO1_4, 1, 4);\nimpl_pin!(PIO1_5, 1, 5);\nimpl_pin!(PIO1_6, 1, 6);\nimpl_pin!(PIO1_7, 1, 7);\nimpl_pin!(PIO1_8, 1, 8);\nimpl_pin!(PIO1_9, 1, 9);\nimpl_pin!(PIO1_10, 1, 10);\nimpl_pin!(PIO1_11, 1, 11);\nimpl_pin!(PIO1_12, 1, 12);\nimpl_pin!(PIO1_13, 1, 13);\nimpl_pin!(PIO1_14, 1, 14);\nimpl_pin!(PIO1_15, 1, 15);\nimpl_pin!(PIO1_16, 1, 16);\nimpl_pin!(PIO1_17, 1, 17);\nimpl_pin!(PIO1_18, 1, 18);\nimpl_pin!(PIO1_19, 1, 19);\nimpl_pin!(PIO1_20, 1, 20);\nimpl_pin!(PIO1_21, 1, 21);\nimpl_pin!(PIO1_22, 1, 22);\nimpl_pin!(PIO1_23, 1, 23);\nimpl_pin!(PIO1_24, 1, 24);\nimpl_pin!(PIO1_25, 1, 25);\nimpl_pin!(PIO1_26, 1, 26);\nimpl_pin!(PIO1_27, 1, 27);\nimpl_pin!(PIO1_28, 1, 28);\nimpl_pin!(PIO1_29, 1, 29);\nimpl_pin!(PIO1_30, 1, 30);\nimpl_pin!(PIO1_31, 1, 31);\n\n// GPIO port 2\ndefine_port_waker!(port2_waker, 0, 31);\nimpl_pin!(PIO2_0, 2, 0);\nimpl_pin!(PIO2_1, 2, 1);\nimpl_pin!(PIO2_2, 2, 2);\nimpl_pin!(PIO2_3, 2, 3);\nimpl_pin!(PIO2_4, 2, 4);\nimpl_pin!(PIO2_5, 2, 5);\nimpl_pin!(PIO2_6, 2, 6);\nimpl_pin!(PIO2_7, 2, 7);\nimpl_pin!(PIO2_8, 2, 8);\nimpl_pin!(PIO2_9, 2, 9);\nimpl_pin!(PIO2_10, 2, 10);\nimpl_pin!(PIO2_11, 2, 11);\nimpl_pin!(PIO2_12, 2, 12);\nimpl_pin!(PIO2_13, 2, 13);\nimpl_pin!(PIO2_14, 2, 14);\nimpl_pin!(PIO2_15, 2, 15);\nimpl_pin!(PIO2_16, 2, 16);\nimpl_pin!(PIO2_17, 2, 17);\nimpl_pin!(PIO2_18, 2, 18);\nimpl_pin!(PIO2_19, 2, 19);\nimpl_pin!(PIO2_20, 2, 20);\nimpl_pin!(PIO2_21, 2, 21);\nimpl_pin!(PIO2_22, 2, 22);\nimpl_pin!(PIO2_23, 2, 23);\nimpl_pin!(PIO2_24, 2, 24);\nimpl_pin!(PIO2_25, 2, 25);\nimpl_pin!(PIO2_26, 2, 26);\nimpl_pin!(PIO2_27, 2, 27);\nimpl_pin!(PIO2_28, 2, 28);\nimpl_pin!(PIO2_29, 2, 29);\nimpl_pin!(PIO2_30, 2, 30);\nimpl_pin!(PIO2_31, 2, 31);\n\n// GPIO port 3\ndefine_port_waker!(port3_waker, 0, 31);\nimpl_pin!(PIO3_0, 3, 0);\nimpl_pin!(PIO3_1, 3, 1);\nimpl_pin!(PIO3_2, 3, 2);\nimpl_pin!(PIO3_3, 3, 3);\nimpl_pin!(PIO3_4, 3, 4);\nimpl_pin!(PIO3_5, 3, 5);\nimpl_pin!(PIO3_6, 3, 6);\nimpl_pin!(PIO3_7, 3, 7);\nimpl_pin!(PIO3_8, 3, 8);\nimpl_pin!(PIO3_9, 3, 9);\nimpl_pin!(PIO3_10, 3, 10);\nimpl_pin!(PIO3_11, 3, 11);\nimpl_pin!(PIO3_12, 3, 12);\nimpl_pin!(PIO3_13, 3, 13);\nimpl_pin!(PIO3_14, 3, 14);\nimpl_pin!(PIO3_15, 3, 15);\nimpl_pin!(PIO3_16, 3, 16);\nimpl_pin!(PIO3_17, 3, 17);\nimpl_pin!(PIO3_18, 3, 18);\nimpl_pin!(PIO3_19, 3, 19);\nimpl_pin!(PIO3_20, 3, 20);\nimpl_pin!(PIO3_21, 3, 21);\nimpl_pin!(PIO3_22, 3, 22);\nimpl_pin!(PIO3_23, 3, 23);\nimpl_pin!(PIO3_24, 3, 24);\nimpl_pin!(PIO3_25, 3, 25);\nimpl_pin!(PIO3_26, 3, 26);\nimpl_pin!(PIO3_27, 3, 27);\nimpl_pin!(PIO3_28, 3, 28);\nimpl_pin!(PIO3_29, 3, 29);\nimpl_pin!(PIO3_30, 3, 30);\nimpl_pin!(PIO3_31, 3, 31);\n\n// GPIO port 4\ndefine_port_waker!(port4_waker, 0, 10);\nimpl_pin!(PIO4_0, 4, 0);\nimpl_pin!(PIO4_1, 4, 1);\nimpl_pin!(PIO4_2, 4, 2);\nimpl_pin!(PIO4_3, 4, 3);\nimpl_pin!(PIO4_4, 4, 4);\nimpl_pin!(PIO4_5, 4, 5);\nimpl_pin!(PIO4_6, 4, 6);\nimpl_pin!(PIO4_7, 4, 7);\nimpl_pin!(PIO4_8, 4, 8);\nimpl_pin!(PIO4_9, 4, 9);\nimpl_pin!(PIO4_10, 4, 10);\n\n// GPIO port 7\ndefine_port_waker!(port7_waker, 24, 31);\nimpl_pin!(PIO7_24, 7, 24);\nimpl_pin!(PIO7_25, 7, 25);\nimpl_pin!(PIO7_26, 7, 26);\nimpl_pin!(PIO7_27, 7, 27);\nimpl_pin!(PIO7_28, 7, 28);\nimpl_pin!(PIO7_29, 7, 29);\nimpl_pin!(PIO7_30, 7, 30);\nimpl_pin!(PIO7_31, 7, 31);\n\nstatic GPIO_WAKERS: [Option<&PortWaker>; PORT_COUNT] = [\n    Some(&port0_waker::WAKER),\n    Some(&port1_waker::WAKER),\n    Some(&port2_waker::WAKER),\n    Some(&port3_waker::WAKER),\n    Some(&port4_waker::WAKER),\n    None,\n    None,\n    Some(&port7_waker::WAKER),\n];\n\nimpl embedded_hal_02::digital::v2::InputPin for Flex<'_, SenseEnabled> {\n    type Error = Infallible;\n\n    #[inline]\n    fn is_high(&self) -> Result<bool, Self::Error> {\n        Ok(self.is_high())\n    }\n\n    #[inline]\n    fn is_low(&self) -> Result<bool, Self::Error> {\n        Ok(self.is_low())\n    }\n}\n\nimpl<S: Sense> embedded_hal_02::digital::v2::OutputPin for Flex<'_, S> {\n    type Error = Infallible;\n\n    #[inline]\n    fn set_high(&mut self) -> Result<(), Self::Error> {\n        self.set_high();\n        Ok(())\n    }\n\n    #[inline]\n    fn set_low(&mut self) -> Result<(), Self::Error> {\n        self.set_low();\n        Ok(())\n    }\n}\n\nimpl embedded_hal_02::digital::v2::StatefulOutputPin for Flex<'_, SenseEnabled> {\n    #[inline]\n    fn is_set_high(&self) -> Result<bool, Self::Error> {\n        Ok(self.is_set_high())\n    }\n\n    #[inline]\n    fn is_set_low(&self) -> Result<bool, Self::Error> {\n        Ok(self.is_set_low())\n    }\n}\n\nimpl<S: Sense> embedded_hal_02::digital::v2::ToggleableOutputPin for Flex<'_, S> {\n    type Error = Infallible;\n\n    #[inline]\n    fn toggle(&mut self) -> Result<(), Self::Error> {\n        self.toggle();\n        Ok(())\n    }\n}\n\nimpl embedded_hal_02::digital::v2::InputPin for Input<'_> {\n    type Error = Infallible;\n\n    #[inline]\n    fn is_high(&self) -> Result<bool, Self::Error> {\n        Ok(self.is_high())\n    }\n\n    #[inline]\n    fn is_low(&self) -> Result<bool, Self::Error> {\n        Ok(self.is_low())\n    }\n}\n\nimpl embedded_hal_02::digital::v2::OutputPin for Output<'_> {\n    type Error = Infallible;\n\n    #[inline]\n    fn set_high(&mut self) -> Result<(), Self::Error> {\n        self.set_high();\n        Ok(())\n    }\n\n    #[inline]\n    fn set_low(&mut self) -> Result<(), Self::Error> {\n        self.set_low();\n        Ok(())\n    }\n}\n\nimpl embedded_hal_02::digital::v2::StatefulOutputPin for Output<'_> {\n    #[inline]\n    fn is_set_high(&self) -> Result<bool, Self::Error> {\n        Ok(self.is_set_high())\n    }\n\n    #[inline]\n    fn is_set_low(&self) -> Result<bool, Self::Error> {\n        Ok(self.is_set_low())\n    }\n}\n\nimpl embedded_hal_02::digital::v2::ToggleableOutputPin for Output<'_> {\n    type Error = Infallible;\n\n    #[inline]\n    fn toggle(&mut self) -> Result<(), Self::Error> {\n        self.toggle();\n        Ok(())\n    }\n}\n\nimpl<S: Sense> embedded_hal_1::digital::ErrorType for Flex<'_, S> {\n    type Error = Infallible;\n}\n\nimpl embedded_hal_1::digital::InputPin for Flex<'_, SenseEnabled> {\n    #[inline]\n    fn is_high(&mut self) -> Result<bool, Self::Error> {\n        // Dereference of self is used here and a few other places to\n        // access the correct method (since different types/traits\n        // share method names)\n        Ok((*self).is_high())\n    }\n\n    #[inline]\n    fn is_low(&mut self) -> Result<bool, Self::Error> {\n        Ok((*self).is_low())\n    }\n}\n\nimpl<S: Sense> embedded_hal_1::digital::OutputPin for Flex<'_, S> {\n    #[inline]\n    fn set_high(&mut self) -> Result<(), Self::Error> {\n        self.set_high();\n        Ok(())\n    }\n\n    #[inline]\n    fn set_low(&mut self) -> Result<(), Self::Error> {\n        self.set_low();\n        Ok(())\n    }\n}\n\nimpl embedded_hal_1::digital::StatefulOutputPin for Flex<'_, SenseEnabled> {\n    #[inline]\n    fn is_set_high(&mut self) -> Result<bool, Self::Error> {\n        Ok((*self).is_set_high())\n    }\n\n    #[inline]\n    fn is_set_low(&mut self) -> Result<bool, Self::Error> {\n        Ok((*self).is_set_low())\n    }\n}\n\nimpl<'d> embedded_hal_async::digital::Wait for Flex<'d, SenseEnabled> {\n    #[inline]\n    async fn wait_for_high(&mut self) -> Result<(), Self::Error> {\n        self.wait_for_high().await;\n        Ok(())\n    }\n\n    #[inline]\n    async fn wait_for_low(&mut self) -> Result<(), Self::Error> {\n        self.wait_for_low().await;\n        Ok(())\n    }\n\n    #[inline]\n    async fn wait_for_rising_edge(&mut self) -> Result<(), Self::Error> {\n        self.wait_for_rising_edge().await;\n        Ok(())\n    }\n\n    #[inline]\n    async fn wait_for_falling_edge(&mut self) -> Result<(), Self::Error> {\n        self.wait_for_falling_edge().await;\n        Ok(())\n    }\n\n    #[inline]\n    async fn wait_for_any_edge(&mut self) -> Result<(), Self::Error> {\n        self.wait_for_any_edge().await;\n        Ok(())\n    }\n}\n\nimpl embedded_hal_1::digital::ErrorType for Input<'_> {\n    type Error = Infallible;\n}\n\nimpl embedded_hal_1::digital::InputPin for Input<'_> {\n    #[inline]\n    fn is_high(&mut self) -> Result<bool, Self::Error> {\n        Ok((*self).is_high())\n    }\n\n    #[inline]\n    fn is_low(&mut self) -> Result<bool, Self::Error> {\n        Ok((*self).is_low())\n    }\n}\n\nimpl<'d> embedded_hal_async::digital::Wait for Input<'d> {\n    #[inline]\n    async fn wait_for_high(&mut self) -> Result<(), Self::Error> {\n        self.wait_for_high().await;\n        Ok(())\n    }\n\n    #[inline]\n    async fn wait_for_low(&mut self) -> Result<(), Self::Error> {\n        self.wait_for_low().await;\n        Ok(())\n    }\n\n    #[inline]\n    async fn wait_for_rising_edge(&mut self) -> Result<(), Self::Error> {\n        self.wait_for_rising_edge().await;\n        Ok(())\n    }\n\n    #[inline]\n    async fn wait_for_falling_edge(&mut self) -> Result<(), Self::Error> {\n        self.wait_for_falling_edge().await;\n        Ok(())\n    }\n\n    #[inline]\n    async fn wait_for_any_edge(&mut self) -> Result<(), Self::Error> {\n        self.wait_for_any_edge().await;\n        Ok(())\n    }\n}\n\nimpl embedded_hal_1::digital::ErrorType for Output<'_> {\n    type Error = Infallible;\n}\n\nimpl embedded_hal_1::digital::OutputPin for Output<'_> {\n    #[inline]\n    fn set_high(&mut self) -> Result<(), Self::Error> {\n        self.set_high();\n        Ok(())\n    }\n\n    #[inline]\n    fn set_low(&mut self) -> Result<(), Self::Error> {\n        self.set_low();\n        Ok(())\n    }\n}\n\nimpl embedded_hal_1::digital::StatefulOutputPin for Output<'_> {\n    #[inline]\n    fn is_set_high(&mut self) -> Result<bool, Self::Error> {\n        Ok((*self).is_set_high())\n    }\n\n    #[inline]\n    fn is_set_low(&mut self) -> Result<bool, Self::Error> {\n        Ok((*self).is_set_low())\n    }\n}\n"
  },
  {
    "path": "embassy-imxrt/src/iopctl.rs",
    "content": "//! IO Pad Controller (IOPCTL)\n//!\n//! Also known as IO Pin Configuration (IOCON)\n\nuse crate::pac::{Iopctl, iopctl};\n\n// A generic pin of any type.\n//\n// The actual pin type used here is arbitrary,\n// as all PioM_N types provide the same methods.\n//\n// Merely need some pin type to cast a raw pointer\n// to in order to access the provided methods.\n#[allow(non_camel_case_types)]\ntype PioM_N = iopctl::Pio0_0;\n\n/// Pin function number.\n#[derive(Debug, Copy, Clone, Eq, PartialEq)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub enum Function {\n    /// Function 0\n    F0,\n    /// Function 1\n    F1,\n    /// Function 2\n    F2,\n    /// Function 3\n    F3,\n    /// Function 4\n    F4,\n    /// Function 5\n    F5,\n    /// Function 6\n    F6,\n    /// Function 7\n    F7,\n    /// Function 8\n    F8,\n}\n\n/// Internal pull-up/down resistors on a pin.\n#[derive(Debug, Copy, Clone, Eq, PartialEq)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub enum Pull {\n    /// No pull-up or pull-down resistor selected\n    None,\n    /// Pull-up resistor\n    Up,\n    /// Pull-down resistor\n    Down,\n}\n\n/// Pin slew rate.\n#[derive(Debug, Copy, Clone, Eq, PartialEq)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub enum SlewRate {\n    /// Standard slew rate\n    Standard,\n    /// Slow slew rate\n    Slow,\n}\n\n/// Output drive strength of a pin.\n#[derive(Debug, Copy, Clone, Eq, PartialEq)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub enum DriveStrength {\n    /// Normal\n    Normal,\n    /// Full\n    Full,\n}\n\n/// Output drive mode of a pin.\n#[derive(Debug, Copy, Clone, Eq, PartialEq)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub enum DriveMode {\n    /// Push-Pull\n    PushPull,\n    /// Pseudo Open-Drain\n    OpenDrain,\n}\n\n/// Input inverter of a pin.\n#[derive(Debug, Copy, Clone, Eq, PartialEq)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub enum Inverter {\n    /// No inverter\n    Disabled,\n    /// Enable input inverter on the input port. A low signal will be\n    /// seen as a high signal by the pin.\n    Enabled,\n}\n\ntrait SealedPin {}\ntrait ToAnyPin: SealedPin {\n    #[inline]\n    fn to_raw(port: u8, pin: u8) -> AnyPin {\n        // SAFETY: This is safe since this is only called from within the module,\n        // where the port and pin numbers have been verified to be correct.\n        unsafe { AnyPin::steal(port, pin) }\n    }\n}\n\ntrait ToFC15Pin: SealedPin {\n    #[inline]\n    fn to_raw(pin: u8) -> FC15Pin {\n        // SAFETY: This is safe since this is only called from within the module,\n        // where the port and pin numbers have been verified to be correct.\n        unsafe { FC15Pin::steal(pin) }\n    }\n}\n\n/// A pin that can be configured via iopctl.\n#[allow(private_bounds)]\npub trait IopctlPin: SealedPin {\n    /// Sets the function number of a pin.\n    ///\n    /// This number corresponds to a specific function that the pin supports.\n    ///\n    /// Typically, function 0 corresponds to GPIO while other numbers correspond to a special function.\n    ///\n    /// See Section 7.5.3 in reference manual for list of pins and their supported functions.\n    fn set_function(&self, function: Function) -> &Self;\n\n    /// Enables either a pull-up or pull-down resistor on a pin.\n    ///\n    /// Setting this to [`Pull::None`] will disable the resistor.\n    fn set_pull(&self, pull: Pull) -> &Self;\n\n    /// Enables the input buffer of a pin.\n    ///\n    /// This must be enabled for any pin acting as an input,\n    /// and some peripheral pins acting as output may need this enabled as well.\n    ///\n    /// If there is any doubt, it is best to enable the input buffer.\n    ///\n    /// See Section 7.4.2.3 of reference manual.\n    fn enable_input_buffer(&self) -> &Self;\n\n    /// Disables the input buffer of a pin.\n    fn disable_input_buffer(&self) -> &Self;\n\n    /// Sets the slew rate of a pin.\n    ///\n    /// This controls the speed at which a pin can toggle,\n    /// which is voltage and load dependent.\n    fn set_slew_rate(&self, slew_rate: SlewRate) -> &Self;\n\n    /// Sets the output drive strength of a pin.\n    ///\n    /// A drive strength of [`DriveStrength::Full`] has twice the\n    /// high and low drive capability of the [`DriveStrength::Normal`] setting.\n    fn set_drive_strength(&self, strength: DriveStrength) -> &Self;\n\n    /// Enables the analog multiplexer of a pin.\n    ///\n    /// This must be called to allow analog functionalities of a pin.\n    ///\n    /// To protect the analog input, [`IopctlPin::set_function`] should be\n    /// called with [`Function::F0`] to disable digital functions.\n    ///\n    /// Additionally, [`IopctlPin::disable_input_buffer`] and [`IopctlPin::set_pull`]\n    /// with [`Pull::None`] should be called.\n    fn enable_analog_multiplex(&self) -> &Self;\n\n    /// Disables the analog multiplexer of a pin.\n    fn disable_analog_multiplex(&self) -> &Self;\n\n    /// Sets the ouput drive mode of a pin.\n    ///\n    /// A pin configured as [`DriveMode::OpenDrain`] actually operates in\n    /// a \"pseudo\" open-drain mode which is somewhat different than true open-drain.\n    ///\n    /// See Section 7.4.2.7 of reference manual.\n    fn set_drive_mode(&self, mode: DriveMode) -> &Self;\n\n    /// Sets the input inverter of an input pin.\n    ///\n    /// Setting this to [`Inverter::Enabled`] will invert\n    /// the input signal.\n    fn set_input_inverter(&self, inverter: Inverter) -> &Self;\n\n    /// Returns a pin to its reset state.\n    fn reset(&self) -> &Self;\n}\n\n/// Represents a pin peripheral created at run-time from given port and pin numbers.\npub struct AnyPin {\n    pin_port: u8,\n    reg: &'static PioM_N,\n}\n\nimpl AnyPin {\n    /// Creates a pin from raw port and pin numbers which can then be configured.\n    ///\n    /// This should ONLY be called when there is no other choice\n    /// (e.g. from a type-erased GPIO pin).\n    ///\n    /// Otherwise, pin peripherals should be configured directly.\n    ///\n    /// # Safety\n    ///\n    /// The caller MUST ensure valid port and pin numbers are provided,\n    /// and that multiple instances of [`AnyPin`] with the same port\n    /// and pin combination are not being used simultaneously.\n    ///\n    /// Failure to uphold these requirements will result in undefined behavior.\n    ///\n    /// See Table 297 in reference manual for a list of valid\n    /// pin and port number combinations.\n    #[must_use]\n    pub unsafe fn steal(port: u8, pin: u8) -> Self {\n        // Calculates the offset from the beginning of the IOPCTL register block\n        // address to the register address representing the pin.\n        //\n        // See Table 297 in reference manual for how this offset is calculated.\n        let offset = ((port as usize) << 7) + ((pin as usize) << 2);\n\n        // SAFETY: This is safe assuming the caller of this function satisfies the safety requirements above.\n        let reg = unsafe { &*Iopctl::ptr().byte_offset(offset as isize).cast() };\n        Self {\n            pin_port: port * 32 + pin,\n            reg,\n        }\n    }\n\n    /// Returns the pin's port and pin combination.\n    #[must_use]\n    pub fn pin_port(&self) -> usize {\n        self.pin_port as usize\n    }\n}\n\n/// Represents a FC15 pin peripheral created at run-time from given pin number.\npub struct FC15Pin {\n    reg: &'static PioM_N,\n}\n\nimpl FC15Pin {\n    /// Creates an FC15 pin from raw pin number which can then be configured.\n    ///\n    /// This should ONLY be called when there is no other choice\n    /// (e.g. from a type-erased GPIO pin).\n    ///\n    /// Otherwise, pin peripherals should be configured directly.\n    ///\n    /// # Safety\n    ///\n    /// The caller MUST ensure valid port and pin numbers are provided,\n    /// and that multiple instances of [`AnyPin`] with the same port\n    /// and pin combination are not being used simultaneously.\n    ///\n    /// Failure to uphold these requirements will result in undefined behavior.\n    ///\n    /// See Table 297 in reference manual for a list of valid\n    /// pin and port number combinations.\n    #[must_use]\n    pub unsafe fn steal(pin: u8) -> Self {\n        // Table 297:  FC15_I2C_SCL offset = 0x400, FC15_I2C_SCL offset = 0x404\n        let iopctl = unsafe { crate::pac::Iopctl::steal() };\n\n        let reg = if pin == 0 {\n            &*iopctl.fc15_i2c_scl().as_ptr().cast()\n        } else {\n            &*iopctl.fc15_i2c_sda().as_ptr().cast()\n        };\n\n        Self { reg }\n    }\n}\n\n// This allows AnyPin/FC15Pin to be used in HAL constructors that require types\n// which impl Peripheral. Used primarily by GPIO HAL to convert type-erased\n// GPIO pins back into an Output or Input pin specifically.\nembassy_hal_internal::impl_peripheral!(AnyPin);\n\nimpl SealedPin for AnyPin {}\n\nembassy_hal_internal::impl_peripheral!(FC15Pin);\n\nimpl SealedPin for FC15Pin {}\n\nmacro_rules! impl_iopctlpin {\n    ($pintype:ident) => {\n        impl IopctlPin for $pintype {\n            fn set_function(&self, function: Function) -> &Self {\n                critical_section::with(|_| match function {\n                    Function::F0 => {\n                        self.reg.modify(|_, w| w.fsel().function_0());\n                    }\n                    Function::F1 => {\n                        self.reg.modify(|_, w| w.fsel().function_1());\n                    }\n                    Function::F2 => {\n                        self.reg.modify(|_, w| w.fsel().function_2());\n                    }\n                    Function::F3 => {\n                        self.reg.modify(|_, w| w.fsel().function_3());\n                    }\n                    Function::F4 => {\n                        self.reg.modify(|_, w| w.fsel().function_4());\n                    }\n                    Function::F5 => {\n                        self.reg.modify(|_, w| w.fsel().function_5());\n                    }\n                    Function::F6 => {\n                        self.reg.modify(|_, w| w.fsel().function_6());\n                    }\n                    Function::F7 => {\n                        self.reg.modify(|_, w| w.fsel().function_7());\n                    }\n                    Function::F8 => {\n                        self.reg.modify(|_, w| w.fsel().function_8());\n                    }\n                });\n                self\n            }\n\n            fn set_pull(&self, pull: Pull) -> &Self {\n                critical_section::with(|_| {\n                    match pull {\n                        Pull::None => {\n                            self.reg.modify(|_, w| w.pupdena().disabled());\n                        }\n                        Pull::Up => {\n                            self.reg.modify(|_, w| w.pupdena().enabled().pupdsel().pull_up());\n                        }\n                        Pull::Down => {\n                            self.reg\n                                .modify(|_, w| w.pupdena().enabled().pupdsel().pull_down());\n                        }\n                    }\n                    self\n                })\n            }\n\n            fn enable_input_buffer(&self) -> &Self {\n                critical_section::with(|_| self.reg.modify(|_, w| w.ibena().enabled()));\n                self\n            }\n\n            fn disable_input_buffer(&self) -> &Self {\n                critical_section::with(|_| self.reg.modify(|_, w| w.ibena().disabled()));\n                self\n            }\n\n            fn set_slew_rate(&self, slew_rate: SlewRate) -> &Self {\n                critical_section::with(|_| match slew_rate {\n                    SlewRate::Standard => {\n                        self.reg.modify(|_, w| w.slewrate().normal());\n                    }\n                    SlewRate::Slow => {\n                        self.reg.modify(|_, w| w.slewrate().slow());\n                    }\n                });\n                self\n            }\n\n            fn set_drive_strength(&self, strength: DriveStrength) -> &Self {\n                critical_section::with(|_| match strength {\n                    DriveStrength::Normal => {\n                        self.reg.modify(|_, w| w.fulldrive().normal_drive());\n                    }\n                    DriveStrength::Full => {\n                        self.reg.modify(|_, w| w.fulldrive().full_drive());\n                    }\n                });\n                self\n            }\n\n            fn enable_analog_multiplex(&self) -> &Self {\n                critical_section::with(|_| self.reg.modify(|_, w| w.amena().enabled()));\n                self\n            }\n\n            fn disable_analog_multiplex(&self) -> &Self {\n                critical_section::with(|_| self.reg.modify(|_, w| w.amena().disabled()));\n                self\n            }\n\n            fn set_drive_mode(&self, mode: DriveMode) -> &Self {\n                critical_section::with(|_| match mode {\n                    DriveMode::PushPull => {\n                        self.reg.modify(|_, w| w.odena().disabled());\n                    }\n                    DriveMode::OpenDrain => {\n                        self.reg.modify(|_, w| w.odena().enabled());\n                    }\n                });\n                self\n            }\n\n            fn set_input_inverter(&self, inverter: Inverter) -> &Self {\n                critical_section::with(|_| match inverter {\n                    Inverter::Disabled => {\n                        self.reg.modify(|_, w| w.iiena().disabled());\n                    }\n                    Inverter::Enabled => {\n                        self.reg.modify(|_, w| w.iiena().enabled());\n                    }\n                });\n                self\n            }\n\n            fn reset(&self) -> &Self {\n                self.reg.reset();\n                self\n            }\n        }\n    };\n}\n\nimpl_iopctlpin!(AnyPin);\nimpl_iopctlpin!(FC15Pin);\n\nmacro_rules! impl_FC15pin {\n    ($pin_periph:ident, $pin_no:expr) => {\n        impl SealedPin for crate::peripherals::$pin_periph {}\n        impl ToFC15Pin for crate::peripherals::$pin_periph {}\n        impl IopctlPin for crate::peripherals::$pin_periph {\n            #[inline]\n            fn set_function(&self, _function: Function) -> &Self {\n                //No function configuration for FC15 pin\n                self\n            }\n\n            #[inline]\n            fn set_pull(&self, pull: Pull) -> &Self {\n                Self::to_raw($pin_no).set_pull(pull);\n                self\n            }\n\n            #[inline]\n            fn enable_input_buffer(&self) -> &Self {\n                Self::to_raw($pin_no).enable_input_buffer();\n                self\n            }\n\n            #[inline]\n            fn disable_input_buffer(&self) -> &Self {\n                Self::to_raw($pin_no).disable_input_buffer();\n                self\n            }\n\n            #[inline]\n            fn set_slew_rate(&self, slew_rate: SlewRate) -> &Self {\n                Self::to_raw($pin_no).set_slew_rate(slew_rate);\n                self\n            }\n\n            #[inline]\n            fn set_drive_strength(&self, strength: DriveStrength) -> &Self {\n                Self::to_raw($pin_no).set_drive_strength(strength);\n                self\n            }\n\n            #[inline]\n            fn enable_analog_multiplex(&self) -> &Self {\n                Self::to_raw($pin_no).enable_analog_multiplex();\n                self\n            }\n\n            #[inline]\n            fn disable_analog_multiplex(&self) -> &Self {\n                Self::to_raw($pin_no).disable_analog_multiplex();\n                self\n            }\n\n            #[inline]\n            fn set_drive_mode(&self, mode: DriveMode) -> &Self {\n                Self::to_raw($pin_no).set_drive_mode(mode);\n                self\n            }\n\n            #[inline]\n            fn set_input_inverter(&self, inverter: Inverter) -> &Self {\n                Self::to_raw($pin_no).set_input_inverter(inverter);\n                self\n            }\n\n            #[inline]\n            fn reset(&self) -> &Self {\n                Self::to_raw($pin_no).reset();\n                self\n            }\n        }\n    };\n}\n\nmacro_rules! impl_pin {\n    ($pin_periph:ident, $pin_port:expr, $pin_no:expr) => {\n        impl SealedPin for crate::peripherals::$pin_periph {}\n        impl ToAnyPin for crate::peripherals::$pin_periph {}\n        impl IopctlPin for crate::peripherals::$pin_periph {\n            #[inline]\n            fn set_function(&self, function: Function) -> &Self {\n                Self::to_raw($pin_port, $pin_no).set_function(function);\n                self\n            }\n\n            #[inline]\n            fn set_pull(&self, pull: Pull) -> &Self {\n                Self::to_raw($pin_port, $pin_no).set_pull(pull);\n                self\n            }\n\n            #[inline]\n            fn enable_input_buffer(&self) -> &Self {\n                Self::to_raw($pin_port, $pin_no).enable_input_buffer();\n                self\n            }\n\n            #[inline]\n            fn disable_input_buffer(&self) -> &Self {\n                Self::to_raw($pin_port, $pin_no).disable_input_buffer();\n                self\n            }\n\n            #[inline]\n            fn set_slew_rate(&self, slew_rate: SlewRate) -> &Self {\n                Self::to_raw($pin_port, $pin_no).set_slew_rate(slew_rate);\n                self\n            }\n\n            #[inline]\n            fn set_drive_strength(&self, strength: DriveStrength) -> &Self {\n                Self::to_raw($pin_port, $pin_no).set_drive_strength(strength);\n                self\n            }\n\n            #[inline]\n            fn enable_analog_multiplex(&self) -> &Self {\n                Self::to_raw($pin_port, $pin_no).enable_analog_multiplex();\n                self\n            }\n\n            #[inline]\n            fn disable_analog_multiplex(&self) -> &Self {\n                Self::to_raw($pin_port, $pin_no).disable_analog_multiplex();\n                self\n            }\n\n            #[inline]\n            fn set_drive_mode(&self, mode: DriveMode) -> &Self {\n                Self::to_raw($pin_port, $pin_no).set_drive_mode(mode);\n                self\n            }\n\n            #[inline]\n            fn set_input_inverter(&self, inverter: Inverter) -> &Self {\n                Self::to_raw($pin_port, $pin_no).set_input_inverter(inverter);\n                self\n            }\n\n            #[inline]\n            fn reset(&self) -> &Self {\n                Self::to_raw($pin_port, $pin_no).reset();\n                self\n            }\n        }\n    };\n}\n\nimpl_pin!(PIO0_0, 0, 0);\nimpl_pin!(PIO0_1, 0, 1);\nimpl_pin!(PIO0_2, 0, 2);\nimpl_pin!(PIO0_3, 0, 3);\nimpl_pin!(PIO0_4, 0, 4);\nimpl_pin!(PIO0_5, 0, 5);\nimpl_pin!(PIO0_6, 0, 6);\nimpl_pin!(PIO0_7, 0, 7);\nimpl_pin!(PIO0_8, 0, 8);\nimpl_pin!(PIO0_9, 0, 9);\nimpl_pin!(PIO0_10, 0, 10);\nimpl_pin!(PIO0_11, 0, 11);\nimpl_pin!(PIO0_12, 0, 12);\nimpl_pin!(PIO0_13, 0, 13);\nimpl_pin!(PIO0_14, 0, 14);\nimpl_pin!(PIO0_15, 0, 15);\nimpl_pin!(PIO0_16, 0, 16);\nimpl_pin!(PIO0_17, 0, 17);\nimpl_pin!(PIO0_18, 0, 18);\nimpl_pin!(PIO0_19, 0, 19);\nimpl_pin!(PIO0_20, 0, 20);\nimpl_pin!(PIO0_21, 0, 21);\nimpl_pin!(PIO0_22, 0, 22);\nimpl_pin!(PIO0_23, 0, 23);\nimpl_pin!(PIO0_24, 0, 24);\nimpl_pin!(PIO0_25, 0, 25);\nimpl_pin!(PIO0_26, 0, 26);\nimpl_pin!(PIO0_27, 0, 27);\nimpl_pin!(PIO0_28, 0, 28);\nimpl_pin!(PIO0_29, 0, 29);\nimpl_pin!(PIO0_30, 0, 30);\nimpl_pin!(PIO0_31, 0, 31);\nimpl_pin!(PIO1_0, 1, 0);\nimpl_pin!(PIO1_1, 1, 1);\nimpl_pin!(PIO1_2, 1, 2);\nimpl_pin!(PIO1_3, 1, 3);\nimpl_pin!(PIO1_4, 1, 4);\nimpl_pin!(PIO1_5, 1, 5);\nimpl_pin!(PIO1_6, 1, 6);\nimpl_pin!(PIO1_7, 1, 7);\nimpl_pin!(PIO1_8, 1, 8);\nimpl_pin!(PIO1_9, 1, 9);\nimpl_pin!(PIO1_10, 1, 10);\nimpl_pin!(PIO1_11, 1, 11);\nimpl_pin!(PIO1_12, 1, 12);\nimpl_pin!(PIO1_13, 1, 13);\nimpl_pin!(PIO1_14, 1, 14);\nimpl_pin!(PIO1_15, 1, 15);\nimpl_pin!(PIO1_16, 1, 16);\nimpl_pin!(PIO1_17, 1, 17);\nimpl_pin!(PIO1_18, 1, 18);\nimpl_pin!(PIO1_19, 1, 19);\nimpl_pin!(PIO1_20, 1, 20);\nimpl_pin!(PIO1_21, 1, 21);\nimpl_pin!(PIO1_22, 1, 22);\nimpl_pin!(PIO1_23, 1, 23);\nimpl_pin!(PIO1_24, 1, 24);\nimpl_pin!(PIO1_25, 1, 25);\nimpl_pin!(PIO1_26, 1, 26);\nimpl_pin!(PIO1_27, 1, 27);\nimpl_pin!(PIO1_28, 1, 28);\nimpl_pin!(PIO1_29, 1, 29);\nimpl_pin!(PIO1_30, 1, 30);\nimpl_pin!(PIO1_31, 1, 31);\nimpl_pin!(PIO2_0, 2, 0);\nimpl_pin!(PIO2_1, 2, 1);\nimpl_pin!(PIO2_2, 2, 2);\nimpl_pin!(PIO2_3, 2, 3);\nimpl_pin!(PIO2_4, 2, 4);\nimpl_pin!(PIO2_5, 2, 5);\nimpl_pin!(PIO2_6, 2, 6);\nimpl_pin!(PIO2_7, 2, 7);\nimpl_pin!(PIO2_8, 2, 8);\nimpl_pin!(PIO2_9, 2, 9);\nimpl_pin!(PIO2_10, 2, 10);\nimpl_pin!(PIO2_11, 2, 11);\nimpl_pin!(PIO2_12, 2, 12);\nimpl_pin!(PIO2_13, 2, 13);\nimpl_pin!(PIO2_14, 2, 14);\nimpl_pin!(PIO2_15, 2, 15);\nimpl_pin!(PIO2_16, 2, 16);\nimpl_pin!(PIO2_17, 2, 17);\nimpl_pin!(PIO2_18, 2, 18);\nimpl_pin!(PIO2_19, 2, 19);\nimpl_pin!(PIO2_20, 2, 20);\nimpl_pin!(PIO2_21, 2, 21);\nimpl_pin!(PIO2_22, 2, 22);\nimpl_pin!(PIO2_23, 2, 23);\nimpl_pin!(PIO2_24, 2, 24);\n\n// Note: These have have reset values of 0x41 to support SWD by default\nimpl_pin!(PIO2_25, 2, 25);\nimpl_pin!(PIO2_26, 2, 26);\n\nimpl_pin!(PIO2_27, 2, 27);\nimpl_pin!(PIO2_28, 2, 28);\nimpl_pin!(PIO2_29, 2, 29);\nimpl_pin!(PIO2_30, 2, 30);\nimpl_pin!(PIO2_31, 2, 31);\nimpl_pin!(PIO3_0, 3, 0);\nimpl_pin!(PIO3_1, 3, 1);\nimpl_pin!(PIO3_2, 3, 2);\nimpl_pin!(PIO3_3, 3, 3);\nimpl_pin!(PIO3_4, 3, 4);\nimpl_pin!(PIO3_5, 3, 5);\nimpl_pin!(PIO3_6, 3, 6);\nimpl_pin!(PIO3_7, 3, 7);\nimpl_pin!(PIO3_8, 3, 8);\nimpl_pin!(PIO3_9, 3, 9);\nimpl_pin!(PIO3_10, 3, 10);\nimpl_pin!(PIO3_11, 3, 11);\nimpl_pin!(PIO3_12, 3, 12);\nimpl_pin!(PIO3_13, 3, 13);\nimpl_pin!(PIO3_14, 3, 14);\nimpl_pin!(PIO3_15, 3, 15);\nimpl_pin!(PIO3_16, 3, 16);\nimpl_pin!(PIO3_17, 3, 17);\nimpl_pin!(PIO3_18, 3, 18);\nimpl_pin!(PIO3_19, 3, 19);\nimpl_pin!(PIO3_20, 3, 20);\nimpl_pin!(PIO3_21, 3, 21);\nimpl_pin!(PIO3_22, 3, 22);\nimpl_pin!(PIO3_23, 3, 23);\nimpl_pin!(PIO3_24, 3, 24);\nimpl_pin!(PIO3_25, 3, 25);\nimpl_pin!(PIO3_26, 3, 26);\nimpl_pin!(PIO3_27, 3, 27);\nimpl_pin!(PIO3_28, 3, 28);\nimpl_pin!(PIO3_29, 3, 29);\nimpl_pin!(PIO3_30, 3, 30);\nimpl_pin!(PIO3_31, 3, 31);\nimpl_pin!(PIO4_0, 4, 0);\nimpl_pin!(PIO4_1, 4, 1);\nimpl_pin!(PIO4_2, 4, 2);\nimpl_pin!(PIO4_3, 4, 3);\nimpl_pin!(PIO4_4, 4, 4);\nimpl_pin!(PIO4_5, 4, 5);\nimpl_pin!(PIO4_6, 4, 6);\nimpl_pin!(PIO4_7, 4, 7);\nimpl_pin!(PIO4_8, 4, 8);\nimpl_pin!(PIO4_9, 4, 9);\nimpl_pin!(PIO4_10, 4, 10);\nimpl_pin!(PIO7_24, 7, 24);\nimpl_pin!(PIO7_25, 7, 25);\nimpl_pin!(PIO7_26, 7, 26);\nimpl_pin!(PIO7_27, 7, 27);\nimpl_pin!(PIO7_28, 7, 28);\nimpl_pin!(PIO7_29, 7, 29);\nimpl_pin!(PIO7_30, 7, 30);\nimpl_pin!(PIO7_31, 7, 31);\n\n// FC15 pins\nimpl_FC15pin!(PIOFC15_SCL, 0);\nimpl_FC15pin!(PIOFC15_SDA, 1);\n"
  },
  {
    "path": "embassy-imxrt/src/lib.rs",
    "content": "#![no_std]\n#![allow(async_fn_in_trait)]\n#![allow(unsafe_op_in_unsafe_fn)]\n#![doc = include_str!(\"../README.md\")]\n#![warn(missing_docs)]\n\n//! ## Feature flags\n#![doc = document_features::document_features!(feature_label = r#\"<span class=\"stab portability\"><code>{feature}</code></span>\"#)]\n\n#[cfg(not(any(feature = \"mimxrt633s\", feature = \"mimxrt685s\",)))]\ncompile_error!(\n    \"No chip feature activated. You must activate exactly one of the following features:\n    mimxrt633s,\n    mimxrt685s,\n    \"\n);\n\n// This mod MUST go first, so that the others see its macros.\npub(crate) mod fmt;\n\npub mod clocks;\npub mod crc;\npub mod dma;\npub mod flexcomm;\npub mod gpio;\npub mod iopctl;\npub mod rng;\n\n#[cfg(feature = \"_time-driver\")]\npub mod time_driver;\n\n// This mod MUST go last, so that it sees all the `impl_foo!' macros\n#[cfg_attr(feature = \"mimxrt633s\", path = \"chips/mimxrt633s.rs\")]\n#[cfg_attr(feature = \"mimxrt685s\", path = \"chips/mimxrt685s.rs\")]\nmod chip;\n\n// Reexports\npub use chip::interrupts::*;\n#[cfg(feature = \"unstable-pac\")]\npub use chip::pac;\n#[cfg(not(feature = \"unstable-pac\"))]\npub(crate) use chip::pac;\npub use chip::{Peripherals, peripherals};\npub use embassy_hal_internal::{Peri, PeripheralType};\n\n#[cfg(feature = \"rt\")]\npub use crate::pac::NVIC_PRIO_BITS;\n\n/// Macro to bind interrupts to handlers.\n///\n/// This defines the right interrupt handlers, and creates a unit struct (like `struct Irqs;`)\n/// and implements the right \\[`Binding`\\]s for it. You can pass this struct to drivers to\n/// prove at compile-time that the right interrupts have been bound.\n///\n/// Example of how to bind one interrupt:\n///\n/// ```rust,ignore\n/// use embassy_imxrt::{bind_interrupts, flexspi, peripherals};\n///\n/// bind_interrupts!(\n///     /// Binds the FLEXSPI interrupt.\n///     struct Irqs {\n///         FLEXSPI_IRQ => flexspi::InterruptHandler<peripherals::FLEXSPI>;\n///     }\n/// );\n/// ```\n///\n// developer note: this macro can't be in `embassy-hal-internal` due to the use of `$crate`.\n#[macro_export]\nmacro_rules! bind_interrupts {\n    ($(#[$attr:meta])* $vis:vis struct $name:ident { $($irq:ident => $($handler:ty),*;)* }) => {\n            #[derive(Copy, Clone)]\n            $(#[$attr])*\n            $vis struct $name;\n\n        $(\n            #[allow(non_snake_case)]\n            #[unsafe(no_mangle)]\n            unsafe extern \"C\" fn $irq() {\n                unsafe {\n                    $(\n                        <$handler as $crate::interrupt::typelevel::Handler<$crate::interrupt::typelevel::$irq>>::on_interrupt();\n                    )*\n                }\n            }\n\n            $(\n                unsafe impl $crate::interrupt::typelevel::Binding<$crate::interrupt::typelevel::$irq, $handler> for $name {}\n            )*\n        )*\n    };\n}\n\n/// HAL configuration for iMX RT600.\npub mod config {\n    use crate::clocks::ClockConfig;\n\n    /// HAL configuration passed when initializing.\n    #[non_exhaustive]\n    pub struct Config {\n        /// Clock configuration.\n        pub clocks: ClockConfig,\n\n        /// RTC Time driver interrupt priority.\n        #[cfg(feature = \"_time-driver\")]\n        pub time_interrupt_priority: crate::interrupt::Priority,\n    }\n\n    impl Default for Config {\n        fn default() -> Self {\n            Self {\n                clocks: ClockConfig::crystal(),\n                #[cfg(feature = \"_time-driver\")]\n                time_interrupt_priority: crate::interrupt::Priority::P0,\n            }\n        }\n    }\n\n    impl Config {\n        /// Create a new configuration with the provided clock config.\n        pub fn new(clocks: ClockConfig) -> Self {\n            Self {\n                clocks,\n                #[cfg(feature = \"_time-driver\")]\n                time_interrupt_priority: crate::interrupt::Priority::P0,\n            }\n        }\n    }\n}\n\n/// Initialize the `embassy-imxrt` HAL with the provided configuration.\n///\n/// This returns the peripheral singletons that can be used for creating drivers.\n///\n/// This should only be called once at startup, otherwise it panics.\npub fn init(config: config::Config) -> Peripherals {\n    // Do this first, so that it panics if user is calling `init` a second time\n    // before doing anything important.\n    let peripherals = Peripherals::take();\n\n    #[cfg(feature = \"_time-driver\")]\n    time_driver::init(config.time_interrupt_priority);\n\n    unsafe {\n        if let Err(e) = clocks::init(config.clocks) {\n            error!(\"unable to initialize Clocks for reason: {:?}\", e);\n            // Panic here?\n        }\n        dma::init();\n    }\n    gpio::init();\n\n    peripherals\n}\n\npub(crate) mod sealed {\n    pub trait Sealed {}\n}\n\n#[cfg(feature = \"rt\")]\nstruct BitIter(u32);\n\n#[cfg(feature = \"rt\")]\nimpl Iterator for BitIter {\n    type Item = u32;\n\n    fn next(&mut self) -> Option<Self::Item> {\n        match self.0.trailing_zeros() {\n            32 => None,\n            b => {\n                self.0 &= !(1 << b);\n                Some(b)\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "embassy-imxrt/src/rng.rs",
    "content": "//! True Random Number Generator (TRNG)\n\nuse core::future::poll_fn;\nuse core::marker::PhantomData;\nuse core::task::Poll;\n\nuse embassy_futures::block_on;\nuse embassy_sync::waitqueue::AtomicWaker;\n\nuse crate::clocks::{SysconPeripheral, enable_and_reset};\nuse crate::interrupt::typelevel::Interrupt;\nuse crate::{Peri, PeripheralType, interrupt, peripherals};\n\nstatic RNG_WAKER: AtomicWaker = AtomicWaker::new();\n\n/// RNG ;error\n#[derive(Debug, PartialEq, Eq, Clone, Copy)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub enum Error {\n    /// Seed error.\n    SeedError,\n\n    /// HW Error.\n    HwError,\n\n    /// Frequency Count Fail\n    FreqCountFail,\n}\n\n/// RNG interrupt handler.\npub struct InterruptHandler<T: Instance> {\n    _phantom: PhantomData<T>,\n}\n\nimpl<T: Instance> interrupt::typelevel::Handler<T::Interrupt> for InterruptHandler<T> {\n    unsafe fn on_interrupt() {\n        let regs = T::info().regs;\n        let int_status = regs.int_status().read();\n\n        if int_status.ent_val().bit_is_set()\n            || int_status.hw_err().bit_is_set()\n            || int_status.frq_ct_fail().bit_is_set()\n        {\n            regs.int_ctrl().modify(|_, w| {\n                w.ent_val()\n                    .ent_val_0()\n                    .hw_err()\n                    .hw_err_0()\n                    .frq_ct_fail()\n                    .frq_ct_fail_0()\n            });\n            RNG_WAKER.wake();\n        }\n    }\n}\n\n/// RNG driver.\npub struct Rng<'d> {\n    info: Info,\n    _lifetime: PhantomData<&'d ()>,\n}\n\nimpl<'d> Rng<'d> {\n    /// Create a new RNG driver.\n    pub fn new<T: Instance>(\n        _inner: Peri<'d, T>,\n        _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd,\n    ) -> Self {\n        enable_and_reset::<T>();\n\n        let mut random = Self {\n            info: T::info(),\n            _lifetime: PhantomData,\n        };\n        random.init();\n\n        T::Interrupt::unpend();\n        unsafe { T::Interrupt::enable() };\n\n        random\n    }\n\n    /// Reset the RNG.\n    pub fn reset(&mut self) {\n        self.info.regs.mctl().write(|w| w.rst_def().set_bit().prgm().set_bit());\n    }\n\n    /// Fill the given slice with random values.\n    pub async fn async_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), Error> {\n        // We have a total of 16 words (512 bits) of entropy at our\n        // disposal. The idea here is to read all bits and copy the\n        // necessary bytes to the slice.\n        for chunk in dest.chunks_mut(64) {\n            self.async_fill_chunk(chunk).await?;\n        }\n\n        Ok(())\n    }\n\n    async fn async_fill_chunk(&mut self, chunk: &mut [u8]) -> Result<(), Error> {\n        // wait for interrupt\n        let res = poll_fn(|cx| {\n            // Check if already ready.\n            // TODO: Is this necessary? Could we just check once after\n            // the waker has been registered?\n            if self.info.regs.int_status().read().ent_val().bit_is_set() {\n                return Poll::Ready(Ok(()));\n            }\n\n            RNG_WAKER.register(cx.waker());\n\n            self.unmask_interrupts();\n\n            let mctl = self.info.regs.mctl().read();\n\n            // Check again if interrupt fired\n            if mctl.ent_val().bit_is_set() {\n                Poll::Ready(Ok(()))\n            } else if mctl.err().bit_is_set() {\n                Poll::Ready(Err(Error::HwError))\n            } else if mctl.fct_fail().bit_is_set() {\n                Poll::Ready(Err(Error::FreqCountFail))\n            } else {\n                Poll::Pending\n            }\n        })\n        .await;\n\n        let bits = self.info.regs.mctl().read();\n\n        if bits.ent_val().bit_is_set() {\n            let mut entropy = [0; 16];\n\n            for (i, item) in entropy.iter_mut().enumerate() {\n                *item = self.info.regs.ent(i).read().bits();\n            }\n\n            // Read MCTL after reading ENT15\n            let _ = self.info.regs.mctl().read();\n\n            if entropy.iter().any(|e| *e == 0) {\n                return Err(Error::SeedError);\n            }\n\n            // SAFETY: entropy is the same for input and output types in\n            // native endianness.\n            let entropy: [u8; 64] = unsafe { core::mem::transmute(entropy) };\n\n            // write bytes to chunk\n            chunk.copy_from_slice(&entropy[..chunk.len()]);\n        }\n\n        res\n    }\n\n    fn mask_interrupts(&mut self) {\n        self.info.regs.int_mask().write(|w| {\n            w.ent_val()\n                .ent_val_0()\n                .hw_err()\n                .hw_err_0()\n                .frq_ct_fail()\n                .frq_ct_fail_0()\n        });\n    }\n\n    fn unmask_interrupts(&mut self) {\n        self.info.regs.int_mask().modify(|_, w| {\n            w.ent_val()\n                .ent_val_1()\n                .hw_err()\n                .hw_err_1()\n                .frq_ct_fail()\n                .frq_ct_fail_1()\n        });\n    }\n\n    fn enable_interrupts(&mut self) {\n        self.info.regs.int_ctrl().write(|w| {\n            w.ent_val()\n                .ent_val_1()\n                .hw_err()\n                .hw_err_1()\n                .frq_ct_fail()\n                .frq_ct_fail_1()\n        });\n    }\n\n    fn init(&mut self) {\n        self.mask_interrupts();\n\n        // Switch TRNG to programming mode\n        self.info.regs.mctl().modify(|_, w| w.prgm().set_bit());\n\n        self.enable_interrupts();\n\n        // Switch TRNG to Run Mode\n        self.info\n            .regs\n            .mctl()\n            .modify(|_, w| w.trng_acc().set_bit().prgm().clear_bit());\n    }\n\n    /// Generate a random u32\n    pub fn blocking_next_u32(&mut self) -> u32 {\n        let mut bytes = [0u8; 4];\n        block_on(self.async_fill_bytes(&mut bytes)).unwrap();\n        u32::from_ne_bytes(bytes)\n    }\n\n    /// Generate a random u64\n    pub fn blocking_next_u64(&mut self) -> u64 {\n        let mut bytes = [0u8; 8];\n        block_on(self.async_fill_bytes(&mut bytes)).unwrap();\n        u64::from_ne_bytes(bytes)\n    }\n\n    /// Fill a slice with random bytes.\n    pub fn blocking_fill_bytes(&mut self, dest: &mut [u8]) {\n        block_on(self.async_fill_bytes(dest)).unwrap();\n    }\n}\n\nimpl<'d> rand_core_06::RngCore for Rng<'d> {\n    fn next_u32(&mut self) -> u32 {\n        self.blocking_next_u32()\n    }\n\n    fn next_u64(&mut self) -> u64 {\n        self.blocking_next_u64()\n    }\n\n    fn fill_bytes(&mut self, dest: &mut [u8]) {\n        self.blocking_fill_bytes(dest);\n    }\n\n    fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), rand_core_06::Error> {\n        self.blocking_fill_bytes(dest);\n        Ok(())\n    }\n}\n\nimpl<'d> rand_core_06::CryptoRng for Rng<'d> {}\n\nimpl<'d> rand_core_09::RngCore for Rng<'d> {\n    fn next_u32(&mut self) -> u32 {\n        self.blocking_next_u32()\n    }\n\n    fn next_u64(&mut self) -> u64 {\n        self.blocking_next_u64()\n    }\n\n    fn fill_bytes(&mut self, dest: &mut [u8]) {\n        self.blocking_fill_bytes(dest);\n    }\n}\n\nimpl<'d> rand_core_09::CryptoRng for Rng<'d> {}\n\nstruct Info {\n    regs: crate::pac::Trng,\n}\n\ntrait SealedInstance {\n    fn info() -> Info;\n}\n\n/// RNG instance trait.\n#[allow(private_bounds)]\npub trait Instance: SealedInstance + PeripheralType + SysconPeripheral + 'static + Send {\n    /// Interrupt for this RNG instance.\n    type Interrupt: interrupt::typelevel::Interrupt;\n}\n\nimpl Instance for peripherals::RNG {\n    type Interrupt = crate::interrupt::typelevel::RNG;\n}\n\nimpl SealedInstance for peripherals::RNG {\n    fn info() -> Info {\n        // SAFETY: safe from single executor\n        Info {\n            regs: unsafe { crate::pac::Trng::steal() },\n        }\n    }\n}\n"
  },
  {
    "path": "embassy-imxrt/src/time_driver.rs",
    "content": "//! Time Driver.\nuse core::cell::{Cell, RefCell};\n#[cfg(feature = \"time-driver-rtc\")]\nuse core::sync::atomic::{AtomicU32, Ordering, compiler_fence};\n\nuse critical_section::CriticalSection;\nuse embassy_sync::blocking_mutex::Mutex;\nuse embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;\nuse embassy_time_driver::Driver;\nuse embassy_time_queue_utils::Queue;\n\n#[cfg(feature = \"time-driver-os-timer\")]\nuse crate::clocks::enable;\nuse crate::interrupt::InterruptExt;\nuse crate::{interrupt, pac};\n\nstruct AlarmState {\n    timestamp: Cell<u64>,\n}\n\nunsafe impl Send for AlarmState {}\n\nimpl AlarmState {\n    const fn new() -> Self {\n        Self {\n            timestamp: Cell::new(u64::MAX),\n        }\n    }\n}\n\n#[cfg(feature = \"time-driver-rtc\")]\nfn rtc() -> &'static pac::rtc::RegisterBlock {\n    unsafe { &*pac::Rtc::ptr() }\n}\n\n/// Calculate the timestamp from the period count and the tick count.\n///\n/// To get `now()`, `period` is read first, then `counter` is read. If the counter value matches\n/// the expected range for the `period` parity, we're done. If it doesn't, this means that\n/// a new period start has raced us between reading `period` and `counter`, so we assume the `counter` value\n/// corresponds to the next period.\n///\n/// the 1kHz RTC counter is 16 bits and RTC doesn't have separate compare channels,\n/// so using a 32 bit GPREG0-2 as counter, compare, and int_en\n/// `period` is a 32bit integer, gpreg 'counter' is 31 bits plus the parity bit for overflow detection\n#[cfg(feature = \"time-driver-rtc\")]\nfn calc_now(period: u32, counter: u32) -> u64 {\n    ((period as u64) << 31) + ((counter ^ ((period & 1) << 31)) as u64)\n}\n\n#[cfg(feature = \"time-driver-rtc\")]\nembassy_time_driver::time_driver_impl!(static DRIVER: Rtc = Rtc {\n    period: AtomicU32::new(0),\n    alarms:  Mutex::const_new(CriticalSectionRawMutex::new(), AlarmState::new()),\n    queue: Mutex::new(RefCell::new(Queue::new())),\n});\n\n#[cfg(feature = \"time-driver-rtc\")]\nstruct Rtc {\n    /// Number of 2^31 periods elapsed since boot.\n    period: AtomicU32,\n    /// Timestamp at which to fire alarm. u64::MAX if no alarm is scheduled.\n    alarms: Mutex<CriticalSectionRawMutex, AlarmState>,\n    queue: Mutex<CriticalSectionRawMutex, RefCell<Queue>>,\n}\n\n#[cfg(feature = \"time-driver-rtc\")]\nimpl Rtc {\n    /// Access the GPREG0 register to use it as a 31-bit counter.\n    #[inline]\n    fn counter_reg(&self) -> &pac::rtc::Gpreg {\n        rtc().gpreg(0)\n    }\n\n    /// Access the GPREG1 register to use it as a compare register for triggering alarms.\n    #[inline]\n    fn compare_reg(&self) -> &pac::rtc::Gpreg {\n        rtc().gpreg(1)\n    }\n\n    /// Access the GPREG2 register to use it to enable or disable interrupts (int_en).\n    #[inline]\n    fn int_en_reg(&self) -> &pac::rtc::Gpreg {\n        rtc().gpreg(2)\n    }\n\n    fn init(&'static self, irq_prio: crate::interrupt::Priority) {\n        let r = rtc();\n        // enable RTC int (1kHz since subsecond doesn't generate an int)\n        r.ctrl().modify(|_r, w| w.rtc1khz_en().set_bit());\n        // TODO: low power support. line above is leaving out write to .wakedpd_en().set_bit())\n        // which enables wake from deep power down\n\n        // safety: Writing to the gregs is always considered unsafe, gpreg1 is used\n        // as a compare register for triggering an alarm so to avoid unnecessary triggers\n        // after initialization, this is set to 0x:FFFF_FFFF\n        self.compare_reg().write(|w| unsafe { w.gpdata().bits(u32::MAX) });\n        // safety: writing a value to the 1kHz RTC wake counter is always considered unsafe.\n        // The following loads 10 into the count-down timer.\n        r.wake().write(|w| unsafe { w.bits(0xA) });\n        interrupt::RTC.set_priority(irq_prio);\n        unsafe { interrupt::RTC.enable() };\n    }\n\n    #[cfg(feature = \"rt\")]\n    fn on_interrupt(&self) {\n        let r = rtc();\n        // This interrupt fires every 10 ticks of the 1kHz RTC high res clk and adds\n        // 10 to the 31 bit counter gpreg0. The 32nd bit is used for parity detection\n        // This is done to avoid needing to calculate # of ticks spent on interrupt\n        // handlers to recalibrate the clock between interrupts\n        //\n        // TODO: this is admittedly not great for power that we're generating this\n        // many interrupts, will probably get updated in future iterations.\n        if r.ctrl().read().wake1khz().bit_is_set() {\n            r.ctrl().modify(|_r, w| w.wake1khz().set_bit());\n            // safety: writing a value to the 1kHz RTC wake counter is always considered unsafe.\n            // The following reloads 10 into the count-down timer after it triggers an int.\n            // The countdown begins anew after the write so time can continue to be measured.\n            r.wake().write(|w| unsafe { w.bits(0xA) });\n            if (self.counter_reg().read().bits() + 0xA) > 0x8000_0000 {\n                // if we're going to \"overflow\", increase the period\n                self.next_period();\n                let rollover_diff = 0x8000_0000 - (self.counter_reg().read().bits() + 0xA);\n                // safety: writing to gpregs is always considered unsafe. In order to\n                // not \"lose\" time when incrementing the period, gpreg0, the extended\n                // counter, is restarted at the # of ticks it would overflow by\n                self.counter_reg().write(|w| unsafe { w.bits(rollover_diff) });\n            } else {\n                self.counter_reg().modify(|r, w| unsafe { w.bits(r.bits() + 0xA) });\n            }\n        }\n\n        critical_section::with(|cs| {\n            // gpreg2 as an \"int_en\" set by next_period(). This is\n            // 1 when the timestamp for the alarm deadline expires\n            // before the counter register overflows again.\n            if self.int_en_reg().read().gpdata().bits() == 1 {\n                // gpreg0 is our extended counter register, check if\n                // our counter is larger than the compare value\n                if self.counter_reg().read().bits() > self.compare_reg().read().bits() {\n                    self.trigger_alarm(cs);\n                }\n            }\n        })\n    }\n\n    #[cfg(feature = \"rt\")]\n    fn next_period(&self) {\n        critical_section::with(|cs| {\n            let period = self\n                .period\n                .fetch_update(Ordering::Relaxed, Ordering::Relaxed, |p| Some(p + 1))\n                .unwrap_or_else(|p| {\n                    trace!(\"Unable to increment period. Time is now inaccurate\");\n                    // TODO: additional error handling beyond logging\n\n                    p\n                });\n            let t = (period as u64) << 31;\n\n            let alarm = &self.alarms.borrow(cs);\n            let at = alarm.timestamp.get();\n            if at < t + 0xc000_0000 {\n                // safety: writing to gpregs is always unsafe, gpreg2 is an alarm\n                // enable. If the alarm must trigger within the next period, then\n                // just enable it. `set_alarm` has already set the correct CC val.\n                self.int_en_reg().write(|w| unsafe { w.gpdata().bits(1) });\n            }\n        })\n    }\n\n    #[must_use]\n    fn set_alarm(&self, cs: CriticalSection, timestamp: u64) -> bool {\n        let alarm = self.alarms.borrow(cs);\n        alarm.timestamp.set(timestamp);\n\n        let t = self.now();\n        if timestamp <= t {\n            // safety: Writing to the gpregs is always unsafe, gpreg2 is\n            // always just used as the alarm enable for the timer driver.\n            // If alarm timestamp has passed the alarm will not fire.\n            // Disarm the alarm and return `false` to indicate that.\n            self.int_en_reg().write(|w| unsafe { w.gpdata().bits(0) });\n\n            alarm.timestamp.set(u64::MAX);\n\n            return false;\n        }\n\n        // If it hasn't triggered yet, setup it by writing to the compare field\n        // An alarm can be delayed, but this is allowed by the Alarm trait contract.\n        // What's not allowed is triggering alarms *before* their scheduled time,\n        let safe_timestamp = timestamp.max(t + 10); //t+3 was done for nrf chip, choosing 10\n\n        // safety: writing to the gregs is always unsafe. When a new alarm is set,\n        // the compare register, gpreg1, is set to the last 31 bits of the timestamp\n        // as the 32nd and final bit is used for the parity check in `next_period`\n        // `period` will be used for the upper bits in a timestamp comparison.\n        self.compare_reg()\n            .modify(|_r, w| unsafe { w.bits(safe_timestamp as u32 & 0x7FFF_FFFF) });\n\n        // The following checks that the difference in timestamp is less than the overflow period\n        let diff = timestamp - t;\n        if diff < 0xc000_0000 {\n            // this is 0b11 << (30). NRF chip used 23 bit periods and checked against 0b11<<22\n\n            // safety: writing to the gpregs is always unsafe. If the alarm\n            // must trigger within the next period, set the \"int enable\"\n            self.int_en_reg().write(|w| unsafe { w.gpdata().bits(1) });\n        } else {\n            // safety: writing to the gpregs is always unsafe. If alarm must trigger\n            // some time after the current period, too far in the future, don't setup\n            // the alarm enable, gpreg2, yet. It will be setup later by `next_period`.\n            self.int_en_reg().write(|w| unsafe { w.gpdata().bits(0) });\n        }\n\n        true\n    }\n\n    #[cfg(feature = \"rt\")]\n    fn trigger_alarm(&self, cs: CriticalSection) {\n        let mut next = self.queue.borrow(cs).borrow_mut().next_expiration(self.now());\n        while !self.set_alarm(cs, next) {\n            next = self.queue.borrow(cs).borrow_mut().next_expiration(self.now());\n        }\n    }\n}\n\n#[cfg(feature = \"time-driver-rtc\")]\nimpl Driver for Rtc {\n    fn now(&self) -> u64 {\n        // `period` MUST be read before `counter`, see comment at the top for details.\n        let period = self.period.load(Ordering::Acquire);\n        compiler_fence(Ordering::Acquire);\n        let counter = self.counter_reg().read().bits();\n        calc_now(period, counter)\n    }\n\n    fn schedule_wake(&self, at: u64, waker: &core::task::Waker) {\n        critical_section::with(|cs| {\n            let mut queue = self.queue.borrow(cs).borrow_mut();\n\n            if queue.schedule_wake(at, waker) {\n                let mut next = queue.next_expiration(self.now());\n                while !self.set_alarm(cs, next) {\n                    next = queue.next_expiration(self.now());\n                }\n            }\n        })\n    }\n}\n\n#[cfg(all(feature = \"rt\", feature = \"time-driver-rtc\"))]\n#[allow(non_snake_case)]\n#[interrupt]\nfn RTC() {\n    DRIVER.on_interrupt()\n}\n\n#[cfg(feature = \"time-driver-os-timer\")]\nfn os() -> &'static pac::ostimer0::RegisterBlock {\n    unsafe { &*pac::Ostimer0::ptr() }\n}\n\n/// Convert gray to decimal\n///\n/// Os Event provides a 64-bit timestamp gray-encoded. All we have to\n/// do here is read both 32-bit halves of the register and convert\n/// from gray to regular binary.\n#[cfg(feature = \"time-driver-os-timer\")]\nfn gray_to_dec(gray: u64) -> u64 {\n    let mut dec = gray;\n\n    dec ^= dec >> 1;\n    dec ^= dec >> 2;\n    dec ^= dec >> 4;\n    dec ^= dec >> 8;\n    dec ^= dec >> 16;\n    dec ^= dec >> 32;\n\n    dec\n}\n\n/// Convert decimal to gray\n///\n/// Before writing match value to the target register, we must convert\n/// it back into gray code.\n#[cfg(feature = \"time-driver-os-timer\")]\nfn dec_to_gray(dec: u64) -> u64 {\n    let gray = dec;\n    gray ^ (gray >> 1)\n}\n\n#[cfg(feature = \"time-driver-os-timer\")]\nembassy_time_driver::time_driver_impl!(static DRIVER: OsTimer = OsTimer {\n    alarms:  Mutex::const_new(CriticalSectionRawMutex::new(), AlarmState::new()),\n    queue: Mutex::new(RefCell::new(Queue::new())),\n});\n\n#[cfg(feature = \"time-driver-os-timer\")]\nstruct OsTimer {\n    /// Timestamp at which to fire alarm. u64::MAX if no alarm is scheduled.\n    alarms: Mutex<CriticalSectionRawMutex, AlarmState>,\n    queue: Mutex<CriticalSectionRawMutex, RefCell<Queue>>,\n}\n\n#[cfg(feature = \"time-driver-os-timer\")]\nimpl OsTimer {\n    fn init(&'static self, irq_prio: crate::interrupt::Priority) {\n        // init alarms\n        critical_section::with(|cs| {\n            let alarm = DRIVER.alarms.borrow(cs);\n            alarm.timestamp.set(u64::MAX);\n        });\n\n        // Enable clocks. Documentation advises AGAINST resetting this\n        // peripheral.\n        enable::<crate::peripherals::OS_EVENT>();\n\n        interrupt::OS_EVENT.disable();\n\n        // Make sure interrupt is masked\n        os().osevent_ctrl().modify(|_, w| w.ostimer_intena().clear_bit());\n\n        // Default to the end of time\n        os().match_l().write(|w| unsafe { w.bits(0xffff_ffff) });\n        os().match_h().write(|w| unsafe { w.bits(0xffff_ffff) });\n\n        interrupt::OS_EVENT.unpend();\n        interrupt::OS_EVENT.set_priority(irq_prio);\n        unsafe { interrupt::OS_EVENT.enable() };\n    }\n\n    fn set_alarm(&self, cs: CriticalSection, timestamp: u64) -> bool {\n        let alarm = self.alarms.borrow(cs);\n        alarm.timestamp.set(timestamp);\n\n        // Wait until we're allowed to write to MATCH_L/MATCH_H\n        // registers\n        while os().osevent_ctrl().read().match_wr_rdy().bit_is_set() {}\n\n        let t = self.now();\n        if timestamp <= t {\n            os().osevent_ctrl().modify(|_, w| w.ostimer_intena().clear_bit());\n            alarm.timestamp.set(u64::MAX);\n            return false;\n        }\n\n        let gray_timestamp = dec_to_gray(timestamp);\n\n        os().match_l()\n            .write(|w| unsafe { w.bits(gray_timestamp as u32 & 0xffff_ffff) });\n        os().match_h()\n            .write(|w| unsafe { w.bits((gray_timestamp >> 32) as u32) });\n        os().osevent_ctrl().modify(|_, w| w.ostimer_intena().set_bit());\n\n        true\n    }\n\n    #[cfg(feature = \"rt\")]\n    fn trigger_alarm(&self, cs: CriticalSection) {\n        let mut next = self.queue.borrow(cs).borrow_mut().next_expiration(self.now());\n        while !self.set_alarm(cs, next) {\n            next = self.queue.borrow(cs).borrow_mut().next_expiration(self.now());\n        }\n    }\n\n    #[cfg(feature = \"rt\")]\n    fn on_interrupt(&self) {\n        critical_section::with(|cs| {\n            if os().osevent_ctrl().read().ostimer_intrflag().bit_is_set() {\n                os().osevent_ctrl()\n                    .modify(|_, w| w.ostimer_intena().clear_bit().ostimer_intrflag().set_bit());\n                self.trigger_alarm(cs);\n            }\n        });\n    }\n}\n\n#[cfg(feature = \"time-driver-os-timer\")]\nimpl Driver for OsTimer {\n    fn now(&self) -> u64 {\n        let mut t = os().evtimerh().read().bits() as u64;\n        t <<= 32;\n        t |= os().evtimerl().read().bits() as u64;\n        gray_to_dec(t)\n    }\n\n    fn schedule_wake(&self, at: u64, waker: &core::task::Waker) {\n        critical_section::with(|cs| {\n            let mut queue = self.queue.borrow(cs).borrow_mut();\n\n            if queue.schedule_wake(at, waker) {\n                let mut next = queue.next_expiration(self.now());\n                while !self.set_alarm(cs, next) {\n                    next = queue.next_expiration(self.now());\n                }\n            }\n        })\n    }\n}\n\n#[cfg(all(feature = \"rt\", feature = \"time-driver-os-timer\"))]\n#[allow(non_snake_case)]\n#[interrupt]\nfn OS_EVENT() {\n    DRIVER.on_interrupt()\n}\n\npub(crate) fn init(irq_prio: crate::interrupt::Priority) {\n    DRIVER.init(irq_prio)\n}\n"
  },
  {
    "path": "embassy-mcxa/.gitignore",
    "content": "# Rust\n/target/\n\n# IDE\n.vscode/\n.idea/\n\n# OS\n.DS_Store\nThumbs.db\n\n# Embedded\n*.bin\n*.hex\n*.elf\n*.map\n\n# Debug\n*.log\n"
  },
  {
    "path": "embassy-mcxa/Cargo.toml",
    "content": "[package]\nname = \"embassy-mcxa\"\nversion = \"0.1.0\"\nedition = \"2024\"\nlicense = \"MIT OR Apache-2.0\"\ndescription = \"Embassy Hardware Abstraction Layer (HAL) for NXP MCXA series of MCUs\"\nrepository = \"https://github.com/embassy-rs/embassy\"\nkeywords = [\"embedded\", \"hal\", \"nxp\", \"mcxa\", \"embassy\"]\ncategories = [\"embedded\", \"hardware-support\", \"no-std\"]\ndocumentation = \"https://docs.embassy.dev/embassy-mcxa\"\n\npublish = false\n\n[package.metadata.embassy]\nbuild = [\n    {target = \"thumbv8m.main-none-eabihf\", features = [\"defmt\", \"unstable-pac\", \"mcxa2xx\"]},\n    {target = \"thumbv8m.main-none-eabihf\", features = [\"defmt\", \"unstable-pac\", \"mcxa5xx\"]},\n]\n\n[package.metadata.embassy_docs]\nsrc_base = \"https://github.com/embassy-rs/embassy/blob/embassy-mcxa-v$VERSION/embassy-mcxa/src/\"\nsrc_base_git = \"https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-mcxa/src/\"\n\nfeatures = [\"defmt\", \"unstable-pac\"]\nflavors = [\n    { name = \"mcx-a256\", target = \"thumbv8m.main-none-eabihf\", features = [\"mcxa2xx\"] },\n    { name = \"mcx-a577\", target = \"thumbv8m.main-none-eabihf\", features = [\"mcxa5xx\"] },\n]\n\n[dependencies]\ncortex-m = { version = \"0.7\", features = [\"critical-section-single-core\", \"inline-asm\"] }\n# If you would like \"device\" to be an optional feature, please open an issue.\ncortex-m-rt = { version = \"0.7\", features = [\"device\"] }\ncritical-section = \"1.2.0\"\ndefmt = { version = \"1.0\", optional = true }\nembassy-embedded-hal = { version = \"0.6.0\", path = \"../embassy-embedded-hal\" }\nembassy-futures = { version = \"0.1.2\", path = \"../embassy-futures\" }\nembassy-hal-internal = { version = \"0.5.0\", path = \"../embassy-hal-internal\", features = [\"cortex-m\", \"prio-bits-3\"] }\nembassy-sync = { version = \"0.8.0\", path = \"../embassy-sync\" }\nembedded-hal-02 = { package = \"embedded-hal\", version = \"0.2.6\", features = [\"unproven\"] }\nembedded-hal-1 = { package = \"embedded-hal\", version = \"1.0\" }\nembedded-hal-async = { version = \"1.0\" }\nembedded-hal-nb = { version = \"1.0\" }\nembedded-io = \"0.7\"\nembedded-io-async = { version = \"0.7.0\" }\nheapless = \"0.9\"\nmaitake-sync = { version = \"0.3.0\", default-features = false, features = [\"critical-section\", \"no-cache-pad\"] }\nnxp-pac = { git = \"https://github.com/embassy-rs/nxp-pac.git\", rev = \"c2f056f666c766563c7a22cb48d5f7a199714d7f\", features = [\"rt\"] }\nnb = \"1.1.0\"\npaste = \"1.0.15\"\n\n# Custom executor support\nembassy-executor = { version = \"0.10.0\", path = \"../embassy-executor\" }\n\n# `time` dependencies\nembassy-time = { version = \"0.5.1\", path = \"../embassy-time\" }\nembassy-time-driver = { version = \"0.2.2\", path = \"../embassy-time-driver\", features = [\"tick-hz-1_000_000\"] }\nembassy-time-queue-utils = { version = \"0.3.0\", path = \"../embassy-time-queue-utils\" }\n\nrand-core-06 = { package = \"rand_core\", version = \"0.6\" }\nrand-core-09 = { package = \"rand_core\", version = \"0.9\" }\nembedded-storage = \"0.3.1\"\nbbqueue = { version = \"0.7.0\", features = [\"disable-cache-padding\"] }\ngrounded = { version = \"0.2.1\", features = [\"cas\", \"critical-section\"] }\n\n[features]\ndefault = [\"rt\", \"swd-swo-as-gpio\"]\n\n# Base defmt feature enables core + panic handler\n# Use with one logger feature: defmt-rtt (preferred) or defmt-uart (fallback)\ndefmt = [\"dep:defmt\", \"nxp-pac/defmt\"]\n\nunstable-pac = []\n\n# Allows for using SOSC pins as normal GPIOs.\n#\n# DISABLING this allows for use of SOSC, but reserves the GPIO pins required.\n# ENABLING this means SOSC cannot be set/configured, but makes the GPIO pins available\nsosc-as-gpio = []\n\n# Allows for using the ROSC (32k external clock) pins as normal GPIOs.\n#\n# DISABLING this allows for use of the 32k clock, but reserves the GPIO pins required.\n# ENABLING this means the 32k clock cannot be set/configured, but makes the GPIO pins available\n#\n# NOTE: Only applicable to MCXA5xx.\nrosc-32k-as-gpio = []\n\n# Allows for using SWDIO and SWCLK pins as normal GPIOs.\n#\n# WARNING: This WILL cause problems if you plan to use defmt over RTT,\n# or otherwise debug the system while running.\n#\n# If this feature is enabled, the relevant pin(s) will be set to floating inputs\n# in the HAL init function.\n#\n# This does NOT include the SWO pin.\nswd-as-gpio = []\n\n# Allows for using the SWD SWO pins as a normal GPIO. This is not used for\n# basic probe-rs/rtt usage, but is sometimes used with ITM.\n#\n# If this feature is enabled, the relevant pin(s) will be set to floating inputs\n# in the HAL init function.\nswd-swo-as-gpio = []\n\n# Allows for using the extra JTAG pins (TDI, ISPMODE_N) as normal GPIOs. You\n# must also select `swd-as-gpio`, `swd-swo-as-gpio`, and MAYBE `dangerous-reset-as-gpio`\n# to recover all JTAG pins. These are called \"extras\" because they aren't also used\n# for normal SWD/SWO.\njtag-extras-as-gpio = []\n\n# Allows for using the reset pin as a normal GPIO.\n#\n# This is NOT recommended, and we have not currently tested whether it remains\n# possible to reset the device and reflash using probe-rs after enabling this\n# feature. Use at your own risk!\ndangerous-reset-as-gpio = []\n\n# dummy feature to silence embassy-hal-internal lint\n#\n# This feature makes no change to embassy-mcxa's operation.\nrt = []\n\n# Enable perf counters\nperf = []\n\n# Enable the custom executor platform implementation, which allows for low-power modes.\n#\n# If this feature is enabled, users should NOT enable `embassy-executor/platform-cortex-m` or any other executor platform.\nexecutor-platform = []\n\n# This feature is not done yet, don't use it!\nunstable-osc32k = []\n\n#\n# Chip family selection\n#\nmcxa2xx = [\"nxp-pac/mcxa256\"]\nmcxa5xx = [\"nxp-pac/mcxa577\"]\n\n[dev-dependencies]\nembassy-executor = { version = \"0.10.0\", path = \"../embassy-executor\", features = [\"platform-cortex-m\", \"executor-interrupt\", \"executor-thread\"] }\nembedded-storage = \"0.3.1\"\npanic-halt = \"1.0.0\"\nstatic_cell = \"2.1.1\"\n"
  },
  {
    "path": "embassy-mcxa/DEVGUIDE.md",
    "content": "# Embassy MCXA Developer's Guide\n\nThis document is intended to assist developers of the `embassy-mcxa` crate.\n\nAs of 2026-01-29, there is currently no \"how to write/maintain a HAL\" guide for `embassy`, so we intend to write up and explain why the embassy-mcxa crate was implemented the way it was, and to serve as a reference for people incrementally building out more features in the future. We also hope to \"upstream\" these docs when possible, to assist with better consistency among embassy HALs in the future.\n\nThis document will be written incrementally. If you see something missing: please do one of the following:\n\n* Open an issue in the embassy github\n* Ask in the embassy matrix chat\n* Open a PR to add the documentation you think is missing\n\n## FRDM Usage Tips\n\n### Recovering from a too-sleepy firmware\n\nIf you have an example that is configured to the DeepSleep state, it will sever the debugger connection once it enters deep sleep. This can mean it will be hard to re-flash since the debugging core is disabled.\n\nTo recover from this state, you can use the ISP mode, which triggers the ROM bootloader:\n\n1. Hold the \"ISP\" button down\n2. Tap and release the \"RESET\" button\n3. Release the \"ISP\" button\n4. Try to flash with probe-rs, the first time will likely fail (I don't know why yet, it probably makes the bootloader upset)\n5. Try to flash again, the second time will likely work.\n\nYou probably want to recover the device by flashing a simple example like the `blinky` example which doesn't attempt to go to deep sleep.\n\n## The `Cargo.toml` file\n\nThis section describes the notable components of the `Cargo.toml` package manifest.\n\n### `package.metadata`\n\nAs an embassy crate, we have a couple of embassy-specific metadata sections.\n\n* `package.metadata.embassy`\n    * This section is used for determining how to build the crate for embassy's CI process.\n* `package.metadata.embassy_docs`\n    * This section is used for determining how to generate embassy's API docs.\n    * See <https://docs.embassy.dev/embassy-mcxa/git/mcx-a256/index.html>.\n    * These docs are rebuilt after each PR is merged, with a short debouncing period.\n\n### Features\n\nWe have a couple of features/kinds of features exposed as part of the crate. For general features, see the `Cargo.toml` docs for what features are activated by default, and what these features do.\n\nNotable features/groupings of features are discussed below.\n\n#### `...-as-gpio` features\n\nSome pins can operate EITHER for GPIO/peripheral use, OR for some kind of dedicated feature, such as SWD/JTAG debugging, external oscillator, etc. Since it is difficult to expose this conditionally in the `Peripherals` struct returned by `hal::init()`, we make this a compile-time feature decision. This is generally reasonable, because when pins are dedicated to a use (or not), this requires board-level electrical wiring, which is not typically reconfigured at runtime.\n\nFor pins covered by `...-as-gpio` features, they are typically in their dedicated feature mode at boot. When an `...-as-gpio` feature is active, the relevant pins will be moved back to the \"disabled\" state at boot, rather than remaining in their default dedicated feature state.\n\nFor example, the `swd-swo-as-gpio` feature is on by default. When this feature is NOT enabled, the pin is used as SWO by default. On the FRDM development board, this causes issues, as this pin is NOT wired up to SWO, and is instead wired up to the I2C/I3C circuit, preventing normal operation.\n\n## The top level of the crate - `lib.rs`\n\nThe `lib.rs` is the top level API of the `embassy-mcxa` crate.\n\n### `embassy_hal_internal::peripherals!`\n\nThe `embassy_hal_internal::peripherals!` macro is used to create the list of peripherals available to users of the HAL after calling `hal::init()`. Each item generates a `Peri<'static, T>`, which is a zero-sized type \"token\", which is used to prove exclusive access to a peripheral. These are often referred to as \"singletons\", as these tokens can only (safely) be created once. For more information on how these tokens are used, see the \"Peripheral Drivers\" section below.\n\nIn this list, we include:\n\n* All hardware peripherals.\n* Any \"synthetic\" peripherals that we also want to exist as a singleton, even if they are not a \"real\" hardware peripheral.\n\nThe generated `Peripherals` struct always creates all items, which means it's not generally possible for functions like `hal::init()` to say \"depending on config, we MIGHT not give you back some pins/peripherals\". For this reason, we make any of these conditionally-returned tokens a crate feature. See the `Cargo.toml` section above for more details.\n\n### `embassy_hal_internal::interrupt_mod!`\n\nThe `embassy_hal_internal::interrupt_mod!` macro is used to generate a number of helper functions, types, and marker traits for each hardware interrupt signal on the chip.\n\nAll interrupts available for a chip should be listed in this macro.\n\n### The `init` function\n\nThis function is also referred to as `hal::init()` in these docs.\n\nThis function is typically one of the first functions called by the user. It takes all configuration values relevant for the lifetime of the firmware, including:\n\n* The priority level for any \"automagically handled\" peripheral interrupts, including:\n    * GPIOs\n    * RTC\n    * OsTimer (used for `embassy-time-driver` impl)\n    * DMA\n* The Clock and Power configurations for Active (running and WFE sleep) and Deep Sleep modes\n\nThis function then performs important \"boot up\" work, including:\n\n* Enabling system level clocks and power based on the user configuration\n* Enabling and configuring \"automagically handled\" peripherals (those listed above)\n* Enabling and configuring the priority of interrupts for \"automagically handled\" peripherals\n\nFinally, when setup is complete, The `init` function returns the `Peripherals` struct, created by the `embassy_hal_internal::peripherals!` macro, containing one `Peri<'static, T>` token for each peripheral.\n\n## Non-Peripheral Components\n\nSome modules of the HAL do not map 1:1 with the memory mapped peripherals of the system. These components are discussed here.\n\n### Clocking and Power subsystem\n\nThe `clocks` module is responsible for setting up the system clock and power configuration of the device. This functionality spans across a few peripherals (`SCG`, `SYSCON`, `VBAT`, `MRCC`, etc.).\n\nSee the doc comments of `src/clocks/mod.rs` for more details regarding the architectural choices of this module.\n\n## Peripheral Drivers\n\nThe majority of `embassy-mcxa` handles high-level drivers for hardware peripherals of the MCXA. These sections discuss \"best practices\" or \"notable oddities\" for these hardware drivers\n\n### General Guidelines\n\nThis section regards patterns that are used for all or most peripheral drivers.\n\n#### Type Erasure and Constructors\n\nIn order to prevent \"monomorphization bloat\", as well as \"cognitive overload\" for HAL users, each peripheral driver should strive to MINIMIZE the number of lifetimes and generics present on the driver. For example, for an I2c peripheral with two GPIO pins, we DO NOT want:\n\n```rust\nstruct<'p, 'c, 'd, P, SCL, SDA, MODE> I2c { /* ... */ }\n\ntype Example = I2c<\n    'periph,                // lifetimes\n    'scl,                   // lifetimes\n    'sda,                   // lifetimes\n    Peri<'periph, I2C0>,    // peripheral instance generic\n    Peri<'scl, P0_2>,       // gpio pin instance generic\n    Peri<'sda, P0_3>,       // gpio pin instance generic\n    Async,                  // operational mode\n>;\n```\n\nInstead, we want to:\n\n* Use a single lifetime where possible, as our HAL driver will \"require\" its parts for the same amount of time\n* Erase ALL peripheral instance generics, instead using runtime storage to store which instances are used for a given peripheral.\n* Retain a single generic for \"Mode\", typically `Blocking` or `Async`, where the latter is often interrupt-enabled and has async methods, while the former doesn't.\n\nThis allows us to create a type that looks as follows:\n\n```rust\nstruct<'a, MODE> I2c { /* ... */ }\n\ntype Example = I2C<'a, Async>;\n```\n\nIn order to retain type safety functionality, we do still use the per-instance and per-peripheral generics, but ONLY at the constructor. This means that constructors will end up looking something like:\n\n```rust\nimpl<'a> I2c<'a, Blocking> {\n    pub fn new<T: Instance>(\n        peri: Peri<'a, T>,\n        scl: Peri<'a, impl SclPin<T>>,\n        sda: Peri<'a, impl SdaPin<T>>,\n        config: Config,\n    ) -> Result<I2c<'a, Blocking>, Error> {\n        // get information like references/pointers to the specific\n        // instance of the peripherals, or per-instance specific setup\n        //\n        // Get pointers for this instance of I2C\n        let info = T::info();\n        // Perform GPIO-specific setup\n        scl.setup_scl();\n        sda.setup_sda();\n        // If we needed to enable interrupts, this is likely bound to the generic\n        // instance:\n        //\n        // T::Interrupt::unpend();\n\n        // ...\n\n        Ok(I2c {\n            info, // hold on to for later!\n            // ...\n        })\n    }\n}\n```\n\n#### Checking Errors\n\nWhen checking errors, ensure that ALL errors are cleared before returning. Otherwise early returns\ncan lead to \"stuck\" errors. Instead of this:\n\n```rust\nfn check_and_clear_rx_errors(info: &'static Info) -> Result<()> {\n    let stat = info.regs().stat().read();\n    if stat.or() {\n        info.regs().stat().write(|w| w.set_or(true));\n        Err(Error::Overrun)\n    } else if stat.pf() {\n        info.regs().stat().write(|w| w.set_pf(true));\n        Err(Error::Parity)\n    } else if stat.fe() {\n        info.regs().stat().write(|w| w.set_fe(true));\n        return Err(Error::Framing);\n    } else if stat.nf() {\n        info.regs().stat().write(|w| w.set_nf(true));\n        return Err(Error::Noise);\n    } else {\n        Ok(())\n    }\n}\n```\n\nEnsure that all errors are cleared:\n\n```rust\nfn check_and_clear_rx_errors(info: &'static Info) -> Result<()> {\n    let stat = info.regs().stat().read();\n\n    // Check for overrun first - other error flags are prevented when OR is set\n    let or_set = stat.or();\n    let pf_set = stat.pf();\n    let fe_set = stat.fe();\n    let nf_set = stat.nf();\n\n    // Clear all errors before returning\n    info.regs().stat().write(|w| {\n        w.set_or(or_set);\n        w.set_pf(pf_set);\n        w.set_fe(fe_set);\n        w.set_nf(nf_set);\n    });\n\n    // Return error source\n    if or_set {\n        Err(Error::Overrun)\n    } else if pf_set {\n        Err(Error::Parity)\n    } else if fe_set {\n        Err(Error::Framing)\n    } else if nf_set {\n        Err(Error::Noise)\n    } else {\n        Ok(())\n    }\n}\n```\n\n#### Error types\n\nWhen creating `Error` types for each peripheral, consider the following high level guidance:\n\n##### Splitting up the Error types\n\nInstead of making one top-level `Error` for the entire peripheral, it it often useful to create multiple error enums. For example, instead of:\n\n```rust\nenum Error {\n    Clocks(ClockError),\n    BadConfig,\n    Timeout,\n    TransferTooLarge,\n}\n\nimpl Example {\n    // Can return `Err(Clocks)` or `Err(BadConfig)`\n    pub fn new(config: Config) -> Result<Self, Error> { /* ... */ }\n\n    // Can return `Err(BadConfig)` or `Err(TransferTooLarge)`\n    pub fn send_u8s(&mut self, mode: Mode, data: &[u8]) -> Result<(), Error> { /* ... */ }\n\n    // Can return `Err(BadConfig)` or `Err(TransferTooLarge)`\n    pub fn send_u16s(&mut self, mode: Mode, data: &[u16]) -> Result<(), Error> { /* ... */ }\n\n    // Can return `Err(Timeout)` or `Err(TransferTooLarge)`\n    pub fn recv(&mut self, data: &mut [u8]) -> Result<usize, Error> { /* ... */ }\n}\n```\n\nIf the same `Error` type is used, the user may need to `match` on errors that are \"impossible\", e.g. a `new()` function returning `Error::Timeout`.\n\nInstead, it might be worth splitting this into *three* errors:\n\n```rust\nenum CreateError {\n    Clocks(ClockError),\n    BadConfig,\n}\n\nenum SendError {\n    BadConfig,\n    TransferTooLarge,\n}\n\nenum RecvError {\n    Timeout,\n    TransferTooLarge,\n}\n\nimpl Example {\n    pub fn new(config: Config) -> Result<Self, CreateError> { /* ... */ }\n    pub fn send_u8s(&mut self, mode: Mode, data: &[u8]) -> Result<(), SendError> { /* ... */ }\n    pub fn send_u16s(&mut self, mode: Mode, data: &[u16]) -> Result<(), SendError> { /* ... */ }\n    pub fn recv(&mut self, data: &mut [u8]) -> Result<usize, RecvError> { /* ... */ }\n}\n```\n\n##### Don't make a `Result` alias\n\nIt *used* to be common to see module specific aliases for `Result`s, e.g.:\n\n```rust\npub type Result<T> = Result<T, Error>;\n```\n\nHowever:\n\n* This can lead to confusion for users if they have multiple `Result`s in scope\n* It pushes for making \"one `Error` per module\", which is the opposite of what is described above\n\n##### Mark errors as `#[non_exhaustive]`\n\nUnless we are **definitely** sure that we have covered all possible kinds of errors for a HAL driver, we should mark the `Error` type(s) as `#[non_exhaustive]`, to prevent making a breaking change when adding a new error type.\n\nFor example:\n\n```rust\n#[non_exhaustive]\nenum RecvError {\n    Timeout,\n    TransferTooLarge,\n}\n```\n\n#### Avoid Wildcard/Glob imports\n\nWe generally want to avoid the use of wildcard/glob imports, like:\n\n```rust\nuse super::*;\nuse other_module::*;\n```\n\nThis can cause [surprising semver breakage], and make the code harder to read.\n\n[surprising semver breakage]: https://predr.ag/blog/breaking-semver-in-rust-by-adding-private-type-or-import/\n"
  },
  {
    "path": "embassy-mcxa/README.md",
    "content": "# Embassy NXP MCX-A MCUs HAL\n\nA Hardware Abstraction Layer (HAL) for the NXP MCX-A family of\nmicrocontrollers using the Embassy async framework. This HAL provides\nsafe, idiomatic Rust interfaces for GPIO, UART, and OSTIMER\nperipherals.\n"
  },
  {
    "path": "embassy-mcxa/src/adc.rs",
    "content": "//! ADC driver\nuse core::marker::PhantomData;\nuse core::ops::{Deref, RangeInclusive};\n\nuse embassy_hal_internal::{Peri, PeripheralType};\nuse maitake_sync::WaitCell;\nuse nxp_pac::adc::vals::{AdcActive, TcompIe, TcompInt};\nuse paste::paste;\n\nuse crate::clocks::periph_helpers::{AdcClockSel, AdcConfig, Div4, PreEnableParts};\nuse crate::clocks::{ClockError, Gate, PoweredClock, WakeGuard, enable_and_reset};\nuse crate::gpio::{AnyPin, GpioPin, SealedPin};\nuse crate::interrupt::typelevel::{Handler, Interrupt};\nuse crate::pac::adc::vals::{\n    Avgs, CalAvgs, CalRdy, CalReq, Calofs, Cmpen, Dozen, Gcc0Rdy, HptExdi, Loop as HwLoop, Mode as ConvMode, Next,\n    Pwrsel, Refsel, Rst, Rstfifo0, Sts, Tcmd, Tpri, Tprictrl,\n};\nuse crate::pac::port::vals::Mux;\nuse crate::pac::{self};\n\n/// Trigger priority policy for ADC conversions.\n#[derive(Debug, Clone, Copy, PartialEq, Eq)]\n#[repr(u8)]\npub enum TriggerPriorityPolicy {\n    ConvPreemptImmediatelyNotAutoResumed = 0,\n    ConvPreemptSoftlyNotAutoResumed = 1,\n    ConvPreemptImmediatelyAutoRestarted = 4,\n    ConvPreemptSoftlyAutoRestarted = 5,\n    ConvPreemptImmediatelyAutoResumed = 12,\n    ConvPreemptSoftlyAutoResumed = 13,\n    ConvPreemptSubsequentlyNotAutoResumed = 2,\n    ConvPreemptSubsequentlyAutoRestarted = 6,\n    ConvPreemptSubsequentlyAutoResumed = 14,\n    TriggerPriorityExceptionDisabled = 16,\n}\n\n/// The reference voltage used by the ADC\n#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]\n#[repr(u8)]\npub enum ReferenceVoltage {\n    #[default]\n    VrefHReferencePin = 0b00,\n    VrefI = 0b01,\n    VddaAnaPin = 0b10,\n}\n\n/// Configuration for the LPADC peripheral.\n#[derive(Debug, Clone, Copy, PartialEq, Eq)]\npub struct Config {\n    /// Control system transition to Stop and Wait power modes while\n    /// ADC is converting.\n    ///\n    /// When enabled in Doze mode, immediate entries to Wait or Stop\n    /// are allowed.\n    ///\n    /// When disabled, the ADC will wait for the current averaging\n    /// iteration/FIFO storage to complete before acknowledging stop\n    /// or wait mode entry.\n    pub enable_in_doze_mode: bool,\n\n    /// Auto-Calibration Averages.\n    pub calibration_average_mode: CalAvgs,\n\n    /// When true, the ADC analog circuits are pre-enabled and ready to execute\n    /// conversions without startup delays (at the cost of higher DC\n    /// current consumption).\n    pub power_pre_enabled: bool,\n\n    /// Power-up delay value (in ADC clock cycles)\n    pub power_up_delay: u8,\n\n    /// Reference voltage source selection\n    pub reference_voltage_source: ReferenceVoltage,\n\n    /// Power configuration selection.\n    pub power_level_mode: Pwrsel,\n\n    /// Trigger priority policy for handling multiple triggers\n    pub trigger_priority_policy: TriggerPriorityPolicy,\n\n    /// Controls the duration of pausing during command execution\n    /// sequencing. The pause delay is a count of (convPauseDelay*4)\n    /// ADCK cycles.\n    ///\n    /// The available value range is in 9-bit.\n    /// When None, the pausing function is not enabled\n    pub conv_pause_delay: Option<u16>,\n\n    /// Power configuration (normal/deep sleep behavior)\n    pub power: PoweredClock,\n\n    /// ADC clock source selection\n    pub source: AdcClockSel,\n\n    /// Clock divider for ADC clock\n    pub div: Div4,\n}\n\nimpl Default for Config {\n    fn default() -> Self {\n        Self {\n            enable_in_doze_mode: true,\n            calibration_average_mode: CalAvgs::NO_AVERAGE,\n            power_pre_enabled: false,\n            power_up_delay: 0x80,\n            reference_voltage_source: Default::default(),\n            power_level_mode: Pwrsel::LOWEST,\n            trigger_priority_policy: TriggerPriorityPolicy::ConvPreemptImmediatelyNotAutoResumed,\n            conv_pause_delay: None,\n            power: PoweredClock::NormalEnabledDeepSleepDisabled,\n            source: AdcClockSel::FroLfDiv,\n            div: Div4::no_div(),\n        }\n    }\n}\n\n/// The ID for a command\n#[derive(Debug, Clone, Copy, PartialEq, Eq)]\n#[repr(u8)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub enum CommandId {\n    Cmd1 = 1,\n    Cmd2 = 2,\n    Cmd3 = 3,\n    Cmd4 = 4,\n    Cmd5 = 5,\n    Cmd6 = 6,\n    Cmd7 = 7,\n}\n\nimpl From<u8> for CommandId {\n    fn from(value: u8) -> Self {\n        match value {\n            1 => Self::Cmd1,\n            2 => Self::Cmd2,\n            3 => Self::Cmd3,\n            4 => Self::Cmd4,\n            5 => Self::Cmd5,\n            6 => Self::Cmd6,\n            7 => Self::Cmd7,\n            _ => unreachable!(),\n        }\n    }\n}\n\n/// Select the compare functionality of the ADC\n#[derive(Debug, Clone, PartialEq, Eq)]\npub enum Compare {\n    /// Do not perform compare operation. Always store the conversion result to the FIFO.\n    Disabled,\n    /// Store conversion result to FIFO at end\n    /// of averaging only if compare is true. If compare is false do not store\n    /// the result to the FIFO. In either the true or false condition, the LOOP\n    /// setting is considered and increments the LOOP counter before deciding\n    /// whether the current command has completed or additional LOOP\n    /// iterations are required.\n    StoreIf(CompareFunction),\n    /// Store conversion result to FIFO at end of\n    /// averaging only if compare is true. Once the true condition is found the\n    /// LOOP setting is considered and increments the LOOP counter before\n    /// deciding whether the current command has completed or additional\n    /// LOOP iterations are required. If the compare is false do not store the\n    /// result to the FIFO. The conversion is repeated without consideration of\n    /// LOOP setting and does not increment the LOOP counter.\n    SkipUntil(CompareFunction),\n}\n\nimpl Compare {\n    fn cmp_en(&self) -> Cmpen {\n        match self {\n            Compare::Disabled => Cmpen::DISABLED_ALWAYS_STORE_RESULT,\n            Compare::StoreIf(_) => Cmpen::COMPARE_RESULT_STORE_IF_TRUE,\n            Compare::SkipUntil(_) => Cmpen::COMPARE_RESULT_KEEP_CONVERTING_UNTIL_TRUE_STORE_IF_TRUE,\n        }\n    }\n\n    /// Get the CVL & CVH values\n    fn get_vals(&self) -> (u16, u16) {\n        match self {\n            Compare::Disabled => (0, 0),\n            Compare::StoreIf(compare_function) | Compare::SkipUntil(compare_function) => compare_function.get_vals(),\n        }\n    }\n}\n\n/// Type that specifies the function used for the compare featue of the ADC.\n///\n/// This determines the `CVL` & `CVH` values.\n#[derive(Debug, Clone, PartialEq, Eq)]\npub enum CompareFunction {\n    /// The compare will succeed when the value is *not* in the specified range\n    OutsideRange(RangeInclusive<u16>),\n    /// The compare will succeed when the value is lower than the specified value\n    LessThan(u16),\n    /// The compare will succeed when the value is higher than the specified value\n    GreaterThan(u16),\n    /// The compare will succeed when the value is in the specified range\n    InsideRange(RangeInclusive<u16>),\n}\n\nimpl CompareFunction {\n    /// Get the CVL & CVH values\n    fn get_vals(&self) -> (u16, u16) {\n        match self {\n            CompareFunction::OutsideRange(range) => {\n                assert!(!range.is_empty());\n                (*range.start(), *range.end())\n            }\n            CompareFunction::LessThan(val) => (*val, u16::MAX),\n            CompareFunction::GreaterThan(val) => (0, *val),\n            CompareFunction::InsideRange(range) => {\n                assert!(!range.is_empty());\n                (*range.end(), *range.start())\n            }\n        }\n    }\n}\n\nenum Channels<'a, T> {\n    Single([Peri<'a, AnyAdcPin<T>>; 1]),\n    Multi(&'a [Peri<'a, AnyAdcPin<T>>]),\n}\n\nimpl<'a, T> Deref for Channels<'a, T> {\n    type Target = [Peri<'a, AnyAdcPin<T>>];\n\n    fn deref(&self) -> &Self::Target {\n        match self {\n            Channels::Single(single) => single,\n            Channels::Multi(multi) => multi,\n        }\n    }\n}\n\n/// A command that can be executed by the ADC\npub struct Command<'a, T> {\n    /// When true, if\n    increment_channel: bool,\n    /// The number of times the command is run. Range = `0..=15`.\n    /// If [Self::increment_channel] is true, the repeats happen on different channels\n    loop_count: u8,\n\n    config: CommandConfig,\n    channels: Channels<'a, T>,\n}\n\nimpl<'a, T: Instance> Command<'a, T> {\n    /// A command that does one conversion on a channel\n    pub fn new_single(channel: Peri<'a, impl Into<AnyAdcPin<T>> + PeripheralType>, config: CommandConfig) -> Self {\n        Self {\n            increment_channel: false,\n            loop_count: 0,\n            config,\n            channels: Channels::Single([channel.into()]),\n        }\n    }\n\n    /// A command that does multiple conversions on a channel.\n    /// - `num_loops`: The amount of times the command is run. Range: `1..=16`\n    pub fn new_looping(\n        channel: Peri<'a, impl Into<AnyAdcPin<T>> + PeripheralType>,\n        num_loops: u8,\n        config: CommandConfig,\n    ) -> Result<Self, Error> {\n        if !(1..=16).contains(&num_loops) {\n            return Err(Error::InvalidConfig);\n        }\n\n        Ok(Self {\n            increment_channel: false,\n            loop_count: num_loops - 1,\n            config,\n            channels: Channels::Single([channel.into()]),\n        })\n    }\n\n    /// A command that does multiple conversions on multiple channels\n    pub fn new_multichannel(channels: &'a [Peri<'a, AnyAdcPin<T>>], config: CommandConfig) -> Result<Self, Error> {\n        if !(1..=15).contains(&channels.len()) {\n            return Err(Error::InvalidConfig);\n        }\n\n        let mut next_channel = channels[0].channel + 1;\n        for pin in channels.iter().skip(1) {\n            if pin.channel != next_channel {\n                return Err(Error::InvalidConfig);\n            }\n            next_channel = pin.channel + 1;\n        }\n\n        Ok(Self {\n            increment_channel: true,\n            loop_count: channels.len() as u8,\n            config,\n            channels: Channels::Multi(channels),\n        })\n    }\n}\n\n/// Configuration for a conversion command\n#[derive(Debug, Clone, PartialEq, Eq)]\npub struct CommandConfig {\n    /// The command that will be executed next.\n    ///\n    /// If None, conversion will end\n    pub chained_command: Option<CommandId>,\n    /// The averaging done on a conversion\n    pub averaging: Avgs,\n    /// The sampling time of a conversion\n    pub sample_time: Sts,\n    /// The compare function being used\n    pub compare: Compare,\n    /// The resolution of a conversion\n    pub resolution: ConvMode,\n    /// When false, the command will not wait for a trigger once the command sequence has been started.\n    /// When true, a trigger is required before the command is started.\n    pub wait_for_trigger: bool,\n}\n\nimpl Default for CommandConfig {\n    fn default() -> Self {\n        Self {\n            chained_command: None,\n            averaging: Avgs::NO_AVERAGE,\n            sample_time: Sts::SAMPLE_3P5,\n            compare: Compare::Disabled,\n            resolution: ConvMode::DATA_12_BITS,\n            wait_for_trigger: false,\n        }\n    }\n}\n\n/// Configuration for a conversion trigger.\n///\n/// Defines how a trigger initiates ADC conversions.\n#[derive(Debug, Clone, Copy, PartialEq, Eq)]\npub struct Trigger {\n    /// The command that is triggered by this trigger\n    pub target_command_id: CommandId,\n    pub delay_power: u8,\n    /// The priority level of the trigger\n    pub priority: Tpri,\n    pub enable_hardware_trigger: bool,\n    pub resync: bool,\n    pub synchronous: bool,\n}\n\nimpl Default for Trigger {\n    fn default() -> Self {\n        Trigger {\n            target_command_id: CommandId::Cmd1,\n            delay_power: 0,\n            priority: Tpri::HIGHEST_PRIORITY,\n            enable_hardware_trigger: false,\n            resync: false,\n            synchronous: false,\n        }\n    }\n}\n\n/// ADC Error types\n#[derive(Debug, Clone, Copy, PartialEq, Eq)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub enum Error {\n    /// FIFO is empty, no conversion result available\n    FifoEmpty,\n    /// FIFO is empty, but the adc is active and a new conversion will be ready soon\n    FifoPending,\n    /// Invalid configuration\n    InvalidConfig,\n    /// Too many commands\n    TooManyCommands,\n    /// Too many triggers\n    TooManyTriggers,\n    /// Tried to call a trigger that was not configured\n    NoTrigger,\n    /// Clock configuration error.\n    ClockSetup(ClockError),\n}\n\n/// Result of an ADC conversion.\n///\n/// Contains the conversion value and metadata about the conversion.\n#[derive(Debug, Clone, Copy, PartialEq, Eq)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub struct Conversion {\n    /// The command that performed this conversion\n    pub command: CommandId,\n    /// For a looping command, the loop index. For a multichannel command, the channel index.\n    pub loop_channel_index: u8,\n    /// The trigger that triggered the command to run\n    pub trigger_id_source: u8,\n    /// The raw value from the ADC\n    pub conv_value: u16,\n}\n\n/// ADC driver instance.\npub struct Adc<'a, M: Mode> {\n    commands: &'a [Command<'a, ()>],\n    num_triggers: u8,\n    info: &'static Info,\n    _wg: Option<WakeGuard>,\n    _mode: PhantomData<M>,\n}\n\nimpl<'a> Adc<'a, Blocking> {\n    /// Create a new blocking instance of the ADC\n    pub fn new_blocking<T: Instance>(\n        _inst: Peri<'a, T>,\n        commands: &'a [Command<'a, T>],\n        triggers: &[Trigger],\n        config: Config,\n    ) -> Result<Self, Error> {\n        // Safety:\n        // We transmute the ADC instance to a `()`. This is fine since the `T` is only a phantomdata.\n        // Because we're now in this function, we don't need this info anymore.\n        let commands = unsafe { core::mem::transmute::<&[Command<'_, T>], &[Command<'_, ()>]>(commands) };\n\n        let parts = unsafe {\n            enable_and_reset::<T>(&AdcConfig {\n                power: config.power,\n                source: config.source,\n                div: config.div,\n            })\n            .map_err(Error::ClockSetup)?\n        };\n\n        Self::new_inner(T::info(), commands, triggers, config, parts)\n    }\n}\n\nimpl<'a> Adc<'a, Async> {\n    /// Create a new async instance of the ADC\n    pub fn new_async<T: Instance>(\n        _inst: Peri<'a, T>,\n        _irq: impl crate::interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'a,\n        commands: &'a [Command<'a, T>],\n        triggers: &[Trigger],\n        config: Config,\n    ) -> Result<Self, Error> {\n        // Safety:\n        // We transmute the ADC instance to a `()`. This is fine since the `T` is only a phantomdata.\n        // Because we're now in this function, we don't need this info anymore.\n        let commands = unsafe { core::mem::transmute::<&[Command<'_, T>], &[Command<'_, ()>]>(commands) };\n\n        let parts = unsafe {\n            enable_and_reset::<T>(&AdcConfig {\n                power: config.power,\n                source: config.source,\n                div: config.div,\n            })\n            .map_err(Error::ClockSetup)?\n        };\n\n        let adc = Self::new_inner(T::info(), commands, triggers, config, parts)?;\n\n        T::Interrupt::unpend();\n        unsafe { T::Interrupt::enable() };\n\n        Ok(adc)\n    }\n\n    /// Reads the current conversion result from the fifo or waits for the next one if it's pending.\n    ///\n    /// If no conversion is pending, None is returned.\n    pub async fn wait_get_conversion(&mut self) -> Option<Conversion> {\n        self.info\n            .wait_cell()\n            .wait_for_value(|| {\n                // Enable the interrupts. They get disabled in the interrupt handler\n                self.info.regs().ie().write(|reg| {\n                    reg.set_fwmie0(true);\n                    reg.set_tcomp_ie(TcompIe::ALL_TRIGGER_COMPLETES_ENABLED);\n                });\n\n                match self.try_get_conversion() {\n                    Ok(result) => Some(Some(result)),\n                    Err(Error::FifoPending) => None,\n                    Err(Error::FifoEmpty) => Some(None),\n                    _ => unreachable!(),\n                }\n            })\n            .await\n            .unwrap()\n    }\n\n    /// Reads the current conversion result from the fifo or waits for the next one even if no conversion is currently pending.\n    ///\n    /// If no conversion is pending, None is returned.\n    pub async fn wait_conversion(&mut self) -> Conversion {\n        self.info\n            .wait_cell()\n            .wait_for_value(|| {\n                // Enable the interrupts. They get disabled in the interrupt handler\n                self.info.regs().ie().write(|reg| {\n                    reg.set_fwmie0(true);\n                    reg.set_tcomp_ie(TcompIe::ALL_TRIGGER_COMPLETES_ENABLED);\n                });\n\n                match self.try_get_conversion() {\n                    Ok(result) => Some(result),\n                    Err(Error::FifoPending) => None,\n                    Err(Error::FifoEmpty) => None,\n                    _ => unreachable!(),\n                }\n            })\n            .await\n            .unwrap()\n    }\n}\n\nimpl<'a, M: Mode> Adc<'a, M> {\n    /// Trigger ADC conversion(s) via software.\n    ///\n    /// Initiates conversion(s) for the trigger(s) specified in the bitmask.\n    /// Each bit in the mask corresponds to a trigger ID (bit 0 = trigger 0, etc.).\n    ///\n    /// # Arguments\n    /// * `trigger_id_mask` - Bitmask of trigger IDs to activate (bit N = trigger N)\n    ///\n    /// # Returns\n    /// * `Ok(())` if the triger mask was valid\n    /// * [Error::NoTrigger] if the mask is calling a trigger that's not configured\n    pub fn do_software_trigger(&mut self, trigger_id_mask: u8) -> Result<(), Error> {\n        if (8 - trigger_id_mask.leading_zeros()) > self.num_triggers as u32 {\n            return Err(Error::NoTrigger);\n        }\n        self.info.regs().swtrig().write(|w| w.0 = trigger_id_mask as u32);\n        Ok(())\n    }\n\n    /// Reset the FIFO buffer.\n    ///\n    /// Clears all pending conversion results from the FIFO.\n    pub fn do_reset_fifo(&mut self) {\n        self.info\n            .regs()\n            .ctrl()\n            .modify(|w| w.set_rstfifo0(Rstfifo0::TRIGGER_RESET));\n    }\n\n    /// Get conversion result from FIFO.\n    ///\n    /// Returns:\n    /// - `Ok(ConvResult)` if a result is available\n    /// - [Error::FifoEmpty] if the FIFO is empty\n    /// - [Error::FifoPending] if the FIFO is empty, but the adc is active\n    pub fn try_get_conversion(&mut self) -> Result<Conversion, Error> {\n        if self.info.regs().fctrl0().read().fcount() == 0 {\n            if self.info.regs().stat().read().adc_active() == AdcActive::BUSY {\n                return Err(Error::FifoPending);\n            }\n            return Err(Error::FifoEmpty);\n        }\n\n        let fifo = self.info.regs().resfifo0().read();\n\n        Ok(Conversion {\n            command: (fifo.cmdsrc() as u8).into(),\n            loop_channel_index: fifo.loopcnt() as u8,\n            trigger_id_source: fifo.tsrc() as u8,\n            conv_value: fifo.d(),\n        })\n    }\n\n    fn new_inner(\n        info: &'static Info,\n        commands: &'a [Command<'a, ()>],\n        triggers: &[Trigger],\n        config: Config,\n        parts: PreEnableParts,\n    ) -> Result<Self, Error> {\n        if commands.len() > 7 {\n            return Err(Error::TooManyCommands);\n        }\n        if triggers.len() > 4 {\n            return Err(Error::TooManyTriggers);\n        }\n\n        // Commands must only chain other existing commands\n        if commands.iter().any(|c| {\n            c.config\n                .chained_command\n                .is_some_and(|cc| (cc as u8 - 1) >= commands.len() as u8)\n        }) {\n            return Err(Error::InvalidConfig);\n        }\n\n        // Triggers must only target existing commands\n        if triggers\n            .iter()\n            .any(|t| (t.target_command_id as u8 - 1) >= commands.len() as u8)\n        {\n            return Err(Error::InvalidConfig);\n        }\n\n        let adc = info.regs();\n\n        /* Reset the module. */\n        adc.ctrl().modify(|w| w.set_rst(Rst::HELD_IN_RESET));\n        adc.ctrl().modify(|w| w.set_rst(Rst::RELEASED_FROM_RESET));\n\n        adc.ctrl().modify(|w| w.set_rstfifo0(Rstfifo0::TRIGGER_RESET));\n\n        /* Disable the module before setting configuration. */\n        adc.ctrl().modify(|w| w.set_adcen(false));\n\n        /* Configure the module generally. */\n        adc.ctrl().modify(|w| {\n            w.set_dozen(if config.enable_in_doze_mode {\n                Dozen::ENABLED\n            } else {\n                Dozen::DISABLED\n            })\n        });\n\n        /* Set calibration average mode. */\n        adc.ctrl().modify(|w| w.set_cal_avgs(config.calibration_average_mode));\n\n        adc.cfg().write(|w| {\n            w.set_pwren(config.power_pre_enabled);\n\n            w.set_pudly(config.power_up_delay);\n            w.set_refsel(Refsel::from_bits(config.reference_voltage_source as u8));\n            w.set_pwrsel(config.power_level_mode);\n            w.set_tprictrl(match config.trigger_priority_policy {\n                TriggerPriorityPolicy::ConvPreemptSoftlyNotAutoResumed\n                | TriggerPriorityPolicy::ConvPreemptSoftlyAutoRestarted\n                | TriggerPriorityPolicy::ConvPreemptSoftlyAutoResumed => Tprictrl::FINISH_CURRENT_ON_PRIORITY,\n                TriggerPriorityPolicy::ConvPreemptSubsequentlyNotAutoResumed\n                | TriggerPriorityPolicy::ConvPreemptSubsequentlyAutoRestarted\n                | TriggerPriorityPolicy::ConvPreemptSubsequentlyAutoResumed => Tprictrl::FINISH_SEQUENCE_ON_PRIORITY,\n                _ => Tprictrl::ABORT_CURRENT_ON_PRIORITY,\n            });\n            w.set_tres(matches!(\n                config.trigger_priority_policy,\n                TriggerPriorityPolicy::ConvPreemptImmediatelyAutoRestarted\n                    | TriggerPriorityPolicy::ConvPreemptSoftlyAutoRestarted\n                    | TriggerPriorityPolicy::ConvPreemptImmediatelyAutoResumed\n                    | TriggerPriorityPolicy::ConvPreemptSoftlyAutoResumed\n                    | TriggerPriorityPolicy::ConvPreemptSubsequentlyAutoRestarted\n                    | TriggerPriorityPolicy::ConvPreemptSubsequentlyAutoResumed\n            ));\n            w.set_tcmdres(matches!(\n                config.trigger_priority_policy,\n                TriggerPriorityPolicy::ConvPreemptImmediatelyAutoResumed\n                    | TriggerPriorityPolicy::ConvPreemptSoftlyAutoResumed\n                    | TriggerPriorityPolicy::ConvPreemptSubsequentlyAutoResumed\n                    | TriggerPriorityPolicy::TriggerPriorityExceptionDisabled\n            ));\n            w.set_hpt_exdi(match config.trigger_priority_policy {\n                TriggerPriorityPolicy::TriggerPriorityExceptionDisabled => HptExdi::DISABLED,\n                _ => HptExdi::ENABLED,\n            });\n        });\n\n        if let Some(pause_delay) = config.conv_pause_delay {\n            adc.pause().write(|w| {\n                w.set_pauseen(true);\n                w.set_pausedly(pause_delay);\n            });\n        } else {\n            adc.pause().write(|w| w.set_pauseen(false));\n        }\n\n        // Set fifo watermark level to 0, so any data will trigger the possible interrupt\n        adc.fctrl0().write(|w| w.set_fwmark(0));\n\n        for (index, command) in commands.iter().enumerate() {\n            for channel in command.channels.deref() {\n                channel.mux();\n            }\n\n            let cmdl = adc.cmdl(index);\n            let cmdh = adc.cmdh(index);\n\n            cmdl.write(|w| {\n                w.set_adch(command.channels[0].channel);\n                w.set_mode(command.config.resolution);\n            });\n\n            cmdh.write(|w| {\n                w.set_next(Next::from_bits(\n                    command.config.chained_command.map(|cc| cc as u8).unwrap_or_default(),\n                ));\n                w.set_loop_(HwLoop::from_bits(command.loop_count));\n                w.set_avgs(command.config.averaging);\n                w.set_sts(command.config.sample_time);\n                w.set_cmpen(command.config.compare.cmp_en());\n                w.set_wait_trig(command.config.wait_for_trigger);\n                w.set_lwi(command.increment_channel);\n            });\n\n            info.regs().cv(index).write(|reg| {\n                let (cvl, cvh) = command.config.compare.get_vals();\n                reg.set_cvl(cvl);\n                reg.set_cvh(cvh);\n            });\n        }\n\n        for (index, trigger) in triggers.iter().enumerate() {\n            let tctrl = adc.tctrl(index);\n\n            tctrl.write(|w| {\n                w.set_tcmd(Tcmd::from_bits(trigger.target_command_id as u8));\n                w.set_tdly(trigger.delay_power);\n                w.set_tpri(trigger.priority);\n                w.set_hten(trigger.enable_hardware_trigger);\n                w.set_rsync(trigger.resync);\n                w.set_tsync(trigger.synchronous);\n            });\n        }\n\n        // Enable ADC\n        adc.ctrl().modify(|w| w.set_adcen(true));\n\n        Ok(Self {\n            commands,\n            num_triggers: triggers.len() as u8,\n            info,\n            _wg: parts.wake_guard,\n            _mode: PhantomData,\n        })\n    }\n\n    /// Perform offset calibration.\n    /// Waits for calibration to complete before returning.\n    pub fn do_offset_calibration(&mut self) {\n        // Enable calibration mode\n        self.info\n            .regs()\n            .ctrl()\n            .modify(|w| w.set_calofs(Calofs::OFFSET_CALIBRATION_REQUEST_PENDING));\n\n        // Wait for calibration to complete (polling status register)\n        while self.info.regs().stat().read().cal_rdy() == CalRdy::NOT_SET {}\n    }\n\n    /// Calculate gain conversion result from gain adjustment factor.\n    ///\n    /// # Arguments\n    /// * `gain_adjustment` - Gain adjustment factor\n    ///\n    /// # Returns\n    /// Gain calibration register value\n    fn get_gain_conv_result(&mut self, mut gain_adjustment: f32) -> u32 {\n        let mut gcra_array = [0u32; 17];\n        let mut gcalr: u32 = 0;\n\n        for i in (1..=17).rev() {\n            let shift = 16 - (i - 1);\n            let step = 1.0 / (1u32 << shift) as f32;\n            let tmp = (gain_adjustment / step) as u32;\n            gcra_array[i - 1] = tmp;\n            gain_adjustment -= tmp as f32 * step;\n        }\n\n        for i in (1..=17).rev() {\n            gcalr += gcra_array[i - 1] << (i - 1);\n        }\n        gcalr\n    }\n\n    /// Perform automatic gain calibration.\n    pub fn do_auto_calibration(&mut self) {\n        self.info\n            .regs()\n            .ctrl()\n            .modify(|w| w.set_cal_req(CalReq::CALIBRATION_REQUEST_PENDING));\n\n        while self.info.regs().gcc0().read().rdy() == Gcc0Rdy::GAIN_CAL_NOT_VALID {}\n\n        let mut gcca = self.info.regs().gcc0().read().gain_cal() as u32;\n        if gcca & 0x8000 != 0 {\n            gcca |= !0xFFFF;\n        }\n\n        let gcra = 131072.0 / (131072.0 - gcca as f32);\n\n        // Write to GCR0\n        self.info.regs().gcr0().write(|w| w.0 = self.get_gain_conv_result(gcra));\n\n        self.info.regs().gcr0().modify(|w| w.set_rdy(true));\n\n        // Wait for calibration to complete (polling status register)\n        while self.info.regs().stat().read().cal_rdy() == CalRdy::NOT_SET {}\n    }\n}\n\nimpl<'a, M: Mode> Drop for Adc<'a, M> {\n    fn drop(&mut self) {\n        // Turn off the ADC\n        self.info.regs().ctrl().modify(|reg| reg.set_adcen(false));\n\n        // Demux all the pins\n        for command in self.commands {\n            for channel in command.channels.deref() {\n                channel.demux();\n            }\n        }\n    }\n}\n\n/// ADC interrupt handler.\npub struct InterruptHandler<T: Instance> {\n    _phantom: PhantomData<T>,\n}\n\nimpl<T: Instance> Handler<T::Interrupt> for InterruptHandler<T> {\n    unsafe fn on_interrupt() {\n        T::PERF_INT_INCR();\n\n        T::info().regs().ie().write(|_| {});\n        // Stat tcomp should go to 0 when `ie` is disabled, but it doesn't.\n        // So we have to do it manually. Errata?\n        T::info()\n            .regs()\n            .stat()\n            .write(|reg| reg.set_tcomp_int(TcompInt::COMPLETION_DETECTED));\n        T::info().wait_cell().wake();\n    }\n}\n\nmod sealed {\n    /// Seal a trait\n    pub trait Sealed {}\n\n    /// Sealed pin trait\n    pub trait SealedAdcPin<T: super::Instance> {}\n}\n\nstruct Info {\n    regs: pac::adc::Adc,\n    wait_cell: WaitCell,\n}\n\nunsafe impl Sync for Info {}\n\nimpl Info {\n    #[inline(always)]\n    fn regs(&self) -> pac::adc::Adc {\n        self.regs\n    }\n\n    #[inline(always)]\n    fn wait_cell(&self) -> &WaitCell {\n        &self.wait_cell\n    }\n}\n\ntrait SealedInstance: Gate<MrccPeriphConfig = AdcConfig> {\n    fn info() -> &'static Info;\n\n    const PERF_INT_INCR: fn();\n}\n\n/// ADC Instance\n#[allow(private_bounds)]\npub trait Instance: SealedInstance + PeripheralType {\n    /// Interrupt for this ADC instance.\n    type Interrupt: Interrupt;\n}\n\nmacro_rules! impl_instance {\n    ($($n:expr),*) => {\n        $(\n            paste!{\n                impl SealedInstance for crate::peripherals::[<ADC $n>] {\n                    fn info() -> &'static Info {\n                        static INFO: Info =\n                        Info {\n                            regs: pac::[<ADC $n>],\n                            wait_cell: WaitCell::new(),\n                        };\n                        &INFO\n                    }\n\n                    const PERF_INT_INCR: fn() = crate::perf_counters::[<incr_interrupt_adc $n>];\n                }\n\n                impl Instance for crate::peripherals::[<ADC $n>] {\n                    type Interrupt = crate::interrupt::typelevel::[<ADC $n>];\n                }\n            }\n        )*\n    };\n}\n\nimpl_instance!(0, 1);\n\n/// Trait implemented by any possible ADC pin\npub trait AdcPin<T: Instance>: sealed::SealedAdcPin<T> + GpioPin + PeripheralType {\n    /// The channel to be used\n    fn channel(&self) -> u8;\n\n    /// Degrade the pin into an [AnyAdcPin]\n    fn degrade(self) -> AnyAdcPin<T> {\n        let channel = self.channel();\n        AnyAdcPin {\n            channel,\n            pin: Some(GpioPin::degrade(self)),\n            _phantom: PhantomData,\n        }\n    }\n}\n\n/// A type-erased ADC pin\npub struct AnyAdcPin<T> {\n    channel: u8,\n    pin: Option<AnyPin>,\n    _phantom: PhantomData<T>,\n}\n\nimpl<T> AnyAdcPin<T> {\n    #[inline]\n    fn mux(&self) {\n        if let Some(pin) = &self.pin {\n            // Set to digital GPIO with input buffer disabled and no pull-ups.\n            // TODO also clear digital output value?\n            pin.set_pull(crate::gpio::Pull::Disabled);\n            pin.set_slew_rate(crate::gpio::SlewRate::Fast.into());\n            pin.set_drive_strength(crate::gpio::DriveStrength::Normal.into());\n            pin.set_function(Mux::MUX0);\n        }\n    }\n\n    #[inline]\n    fn demux(&self) {\n        if let Some(pin) = &self.pin {\n            pin.set_as_disabled()\n        }\n    }\n\n    /// Get the internal temperature sensor pin\n    pub fn temperature() -> Peri<'static, Self> {\n        // Safety: The temp sensor doesn't gate or own anything, so it's fine to give out as many as the user asks\n        unsafe {\n            Peri::new_unchecked(Self {\n                channel: 26,\n                pin: None,\n                _phantom: PhantomData,\n            })\n        }\n    }\n}\n\nimpl<T: Instance, P: AdcPin<T>> From<P> for AnyAdcPin<T> {\n    fn from(value: P) -> Self {\n        AdcPin::degrade(value)\n    }\n}\n\nembassy_hal_internal::impl_peripheral!(AnyAdcPin<T>);\n\n/// Driver mode.\n#[allow(private_bounds)]\npub trait Mode: sealed::Sealed {}\n\n/// Blocking mode.\npub struct Blocking;\nimpl sealed::Sealed for Blocking {}\nimpl Mode for Blocking {}\n\n/// Async mode.\npub struct Async;\nimpl sealed::Sealed for Async {}\nimpl Mode for Async {}\n\nmacro_rules! impl_pin {\n    ($pin:ident, $peri:ident, $channel:literal) => {\n        impl sealed::SealedAdcPin<crate::peripherals::$peri> for crate::peripherals::$pin {}\n\n        impl AdcPin<crate::peripherals::$peri> for crate::peripherals::$pin {\n            #[inline]\n            fn channel(&self) -> u8 {\n                $channel\n            }\n        }\n    };\n}\n\n#[cfg(feature = \"mcxa2xx\")]\nmod mcxa2xx_pins {\n    use super::*;\n\n    impl_pin!(P2_0, ADC0, 0);\n    impl_pin!(P2_4, ADC0, 1);\n    impl_pin!(P2_15, ADC0, 2);\n    impl_pin!(P2_3, ADC0, 3);\n    impl_pin!(P2_2, ADC0, 4);\n    impl_pin!(P2_12, ADC0, 5);\n    impl_pin!(P2_16, ADC0, 6);\n    impl_pin!(P2_7, ADC0, 7);\n    impl_pin!(P0_18, ADC0, 8);\n    impl_pin!(P0_19, ADC0, 9);\n    impl_pin!(P0_20, ADC0, 10);\n    impl_pin!(P0_21, ADC0, 11);\n    impl_pin!(P0_22, ADC0, 12);\n    impl_pin!(P0_23, ADC0, 13);\n    #[cfg(feature = \"jtag-extras-as-gpio\")]\n    impl_pin!(P0_3, ADC0, 14);\n    #[cfg(feature = \"jtag-extras-as-gpio\")]\n    impl_pin!(P0_6, ADC0, 15);\n    impl_pin!(P1_0, ADC0, 16);\n    impl_pin!(P1_1, ADC0, 17);\n    impl_pin!(P1_2, ADC0, 18);\n    impl_pin!(P1_3, ADC0, 19);\n    impl_pin!(P1_4, ADC0, 20);\n    impl_pin!(P1_5, ADC0, 21);\n    impl_pin!(P1_6, ADC0, 22);\n    impl_pin!(P1_7, ADC0, 23);\n\n    // ???\n    // impl_pin!(P1_10, ADC0, 255);\n\n    impl_pin!(P2_1, ADC1, 0);\n    impl_pin!(P2_5, ADC1, 1);\n    impl_pin!(P2_19, ADC1, 2);\n    impl_pin!(P2_6, ADC1, 3);\n    impl_pin!(P2_3, ADC1, 4);\n    impl_pin!(P2_13, ADC1, 5);\n    impl_pin!(P2_17, ADC1, 6);\n    impl_pin!(P2_7, ADC1, 7);\n    impl_pin!(P1_10, ADC1, 8);\n    impl_pin!(P1_11, ADC1, 9);\n    impl_pin!(P1_12, ADC1, 10);\n    impl_pin!(P1_13, ADC1, 11);\n    impl_pin!(P1_14, ADC1, 12);\n    impl_pin!(P1_15, ADC1, 13);\n    // ???\n    // impl_pin!(P1_16, ADC1, 255);\n    // impl_pin!(P1_17, ADC1, 255);\n    // impl_pin!(P1_18, ADC1, 255);\n    // impl_pin!(P1_19, ADC1, 255);\n    // ???\n    impl_pin!(P3_31, ADC1, 20);\n    impl_pin!(P3_30, ADC1, 21);\n    impl_pin!(P3_29, ADC1, 22);\n}\n\n#[cfg(feature = \"mcxa5xx\")]\nmod mcxa5xx_pins {\n    use super::*;\n\n    #[cfg(feature = \"jtag-extras-as-gpio\")]\n    impl_pin!(P0_3, ADC0, 14);\n    #[cfg(feature = \"jtag-extras-as-gpio\")]\n    impl_pin!(P0_6, ADC0, 15);\n    impl_pin!(P0_14, ADC0, 10);\n    impl_pin!(P0_15, ADC0, 11);\n    impl_pin!(P0_18, ADC0, 8);\n    impl_pin!(P0_19, ADC0, 9);\n    impl_pin!(P0_22, ADC0, 12);\n    impl_pin!(P0_23, ADC0, 13);\n\n    impl_pin!(P1_0, ADC0, 16);\n    impl_pin!(P1_1, ADC0, 17);\n    impl_pin!(P1_2, ADC0, 18);\n    impl_pin!(P1_3, ADC0, 19);\n    impl_pin!(P1_4, ADC0, 20);\n    impl_pin!(P1_5, ADC0, 21);\n    impl_pin!(P1_6, ADC0, 22);\n    impl_pin!(P1_7, ADC0, 23);\n    impl_pin!(P1_10, ADC1, 8);\n    impl_pin!(P1_11, ADC1, 9);\n    impl_pin!(P1_12, ADC1, 10);\n    impl_pin!(P1_13, ADC1, 11);\n    impl_pin!(P1_14, ADC1, 12);\n    impl_pin!(P1_15, ADC1, 13);\n    impl_pin!(P1_16, ADC1, 14);\n    impl_pin!(P1_17, ADC1, 15);\n    impl_pin!(P1_18, ADC1, 16);\n    impl_pin!(P1_19, ADC1, 17);\n\n    impl_pin!(P2_0, ADC0, 0);\n    impl_pin!(P2_1, ADC1, 0);\n    impl_pin!(P2_2, ADC0, 4);\n    impl_pin!(P2_3, ADC0, 3);\n    impl_pin!(P2_3, ADC1, 4);\n    impl_pin!(P2_4, ADC0, 1);\n    impl_pin!(P2_5, ADC1, 1);\n    impl_pin!(P2_6, ADC1, 3);\n    impl_pin!(P2_7, ADC0, 7);\n    impl_pin!(P2_7, ADC1, 7);\n    impl_pin!(P2_12, ADC0, 5);\n    impl_pin!(P2_13, ADC1, 5);\n    impl_pin!(P2_15, ADC0, 2);\n    impl_pin!(P2_16, ADC0, 6);\n    impl_pin!(P2_17, ADC1, 6);\n    impl_pin!(P2_19, ADC1, 2);\n\n    #[cfg(feature = \"rosc-32k-as-gpio\")]\n    impl_pin!(P5_0, ADC1, 20);\n    #[cfg(feature = \"rosc-32k-as-gpio\")]\n    impl_pin!(P5_1, ADC1, 21);\n    impl_pin!(P5_2, ADC1, 22);\n}\n"
  },
  {
    "path": "embassy-mcxa/src/cdog.rs",
    "content": "//! Code Watchdog (CDOG) driver for MCXA microcontrollers.\n//!\n//! The CDOG is a hardware watchdog that monitors code execution flow by tracking\n//! a secure counter value and execute a timer. It can detect various fault conditions including:\n//! - Timeout: Code execution takes too long\n//! - Miscompare: Secure counter value doesn't match expected value\n//! - Sequence: Invalid operation sequence\n//! - State: Invalid state transitions\n//! - Address: Invalid memory access\n\nuse embassy_hal_internal::Peri;\nuse embassy_hal_internal::interrupt::InterruptExt;\n\nuse crate::interrupt::typelevel::{self, Handler};\nuse crate::pac::cdog::vals::{Ctrl, DebugHaltCtrl, IrqPause, LockCtrl};\nuse crate::pac::{self};\nuse crate::peripherals::CDOG0;\n\n/// Shorthand for `Result<T>`.\npub type Result<T> = core::result::Result<T, Error>;\n\n/// Errors that can occur when configuring or using the CDOG.\n#[derive(Debug, Clone, Copy, PartialEq, Eq)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub enum Error {\n    /// Watchdog is currently running and cannot be reconfigured\n    WatchdogRunning,\n}\n\n/// Fault control configuration for different fault types.\n///\n/// Determines what action the CDOG takes when a fault is detected.\n#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]\n#[repr(u8)]\npub enum FaultControl {\n    /// Enable system reset on fault detection\n    EnableReset = 1,\n    /// Enable interrupt on fault detection\n    EnableInterrupt = 2,\n    #[default]\n    /// Disable both reset and interrupt\n    DisableBoth = 4,\n}\n\nimpl From<FaultControl> for Ctrl {\n    fn from(val: FaultControl) -> Self {\n        match val {\n            FaultControl::EnableReset => Ctrl::ENABLE_RESET,\n            FaultControl::EnableInterrupt => Ctrl::ENABLE_INTERRUPT,\n            FaultControl::DisableBoth => Ctrl::DISABLE_BOTH,\n        }\n    }\n}\n\n/// Timer pause control during special conditions.\n///\n/// Controls whether the instruction timer continues running or pauses.\n#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]\n#[repr(u8)]\npub enum PauseControl {\n    #[default]\n    /// Keep timer running during IRQ or debug halt\n    RunTimer = 1,\n    /// Pause timer during IRQ or debug halt\n    PauseTimer = 2,\n}\n\n/// Lock control for CDOG configuration.\n///\n/// When locked, configuration registers cannot be modified.\n#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]\n#[repr(u8)]\npub enum LockControl {\n    /// Lock configuration\n    Locked = 1,\n    #[default]\n    /// Unlock configuration\n    Unlocked = 2,\n}\n\n/// CDOG (Code Watchdog) configuration structure.\n///\n/// Defines the behavior of the watchdog for various fault conditions\n/// and operational modes.\n#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]\npub struct Config {\n    /// The timeout period after which the watchdog will trigger\n    pub timeout: FaultControl,\n    pub miscompare: FaultControl,\n    pub sequence: FaultControl,\n    pub state: FaultControl,\n    pub address: FaultControl,\n    pub irq_pause: PauseControl,\n    pub debug_halt: PauseControl,\n    pub lock: LockControl,\n}\n\n/// Code Watchdog peripheral\npub struct Watchdog<'d> {\n    _peri: Peri<'d, CDOG0>,\n    // The register block of the CDOG instance\n    info: pac::cdog::Cdog,\n    /// Software-tracked secure counter value\n    secure_counter: u32,\n}\n\nimpl<'d> Watchdog<'d> {\n    /// Creates a new CDOG instance with the given configuration.\n    ///\n    /// # Arguments\n    /// * `_peri` - Peripheral ownership token\n    /// * `_irq` - Interrupt binding for CDOG0 interrupt handler\n    /// * `config` - Configuration for fault handling and operational modes\n    ///\n    /// # Returns\n    /// * `Ok(Watchdog)` - Successfully configured watchdog\n    /// * `Err(Error::WatchdogRunning)` - Watchdog is already running and cannot be reconfigured\n    pub fn new(\n        _peri: Peri<'d, CDOG0>,\n        _irq: impl crate::interrupt::typelevel::Binding<typelevel::CDOG0, InterruptHandler> + 'd,\n        config: Config,\n    ) -> Result<Self> {\n        let info = pac::CDOG0;\n\n        // Ensure that CDOG is in IDLE mode otherwise writing to CONTROL register will trigger a fault.\n        if info.status().read().curst() == 0xA {\n            return Err(Error::WatchdogRunning);\n        }\n\n        // Clear all pending error flags to prevent immediate reset after enable.\n        // The clearing method depends on whether the module is locked:\n        // - Unlocked (LOCK_CTRL = 10b): Write flag values directly\n        // - Locked (LOCK_CTRL = 01b): Write '1' to clear individual flags\n        let b = info.control().read().lock_ctrl() == LockCtrl::LOCKED;\n        // Locked mode: write '1' to clear each flag\n        info.flags().write(|w| {\n            w.set_to_flag(b);\n            w.set_miscom_flag(b);\n            w.set_seq_flag(b);\n            w.set_cnt_flag(b);\n            w.set_state_flag(b);\n            w.set_addr_flag(b);\n            w.set_por_flag(b);\n        });\n\n        // Configure CONTROL register with the provided config\n        info.control().write(|w| {\n            w.set_timeout_ctrl(config.timeout.into());\n            w.set_miscompare_ctrl(config.miscompare.into());\n            w.set_sequence_ctrl(config.sequence.into());\n            w.set_state_ctrl(config.state.into());\n            w.set_address_ctrl(config.address.into());\n\n            // IRQ pause control\n            match config.irq_pause {\n                PauseControl::RunTimer => w.set_irq_pause(IrqPause::RUN_TIMER),\n                PauseControl::PauseTimer => w.set_irq_pause(IrqPause::PAUSE_TIMER),\n            };\n\n            // Debug halt control\n            match config.debug_halt {\n                PauseControl::RunTimer => w.set_debug_halt_ctrl(DebugHaltCtrl::RUN_TIMER),\n                PauseControl::PauseTimer => w.set_debug_halt_ctrl(DebugHaltCtrl::PAUSE_TIMER),\n            };\n\n            // Lock control\n            match config.lock {\n                LockControl::Locked => w.set_lock_ctrl(LockCtrl::LOCKED),\n                LockControl::Unlocked => w.set_lock_ctrl(LockCtrl::UNLOCKED),\n            }\n        });\n\n        crate::pac::Interrupt::CDOG0.unpend();\n\n        // Safety: `_irq` ensures an Interrupt Handler exists.\n        unsafe { crate::pac::Interrupt::CDOG0.enable() };\n\n        Ok(Self {\n            _peri,\n            info,\n            secure_counter: 0,\n        })\n    }\n\n    /// Starts the watchdog with specified timer and counter values.\n    ///\n    /// # Arguments\n    /// * `instruction_timer_value` - Number of clock cycles before timeout\n    /// * `secure_counter_value` - Initial value of the secure counter\n    ///\n    /// # Note\n    /// If the watchdog is already running, this will stop it first.\n    pub fn start(&mut self, instruction_timer_value: u32, secure_counter_value: u32) {\n        // Ensure the CDOG is in IDLE mode before starting\n        // Status value 0xA indicates ACTIVE state\n        while self.info.status().read().curst() == 0xA {\n            self.stop();\n        }\n\n        // Update internal secure counter tracking\n        self.secure_counter = secure_counter_value;\n\n        // Set the instruction timer reload value (timeout period)\n        self.info.reload().write(|w| w.set_rload(instruction_timer_value));\n        // Start the watchdog with initial secure counter value\n        self.info.start().write(|w| w.set_strt(secure_counter_value));\n    }\n\n    /// Adds a value to the secure counter.\n    ///\n    /// # Arguments\n    /// * `add` - Value to add to the secure counter\n    pub fn add(&mut self, add: u32) {\n        self.secure_counter = self.secure_counter.wrapping_add(add);\n        self.info.add().write(|w| w.set_ad(add));\n    }\n\n    // Subtracts a value from the secure counter.\n    ///\n    /// # Arguments\n    /// * `sub` - Value to subtract from the secure counter\n    pub fn sub(&mut self, sub: u32) {\n        self.secure_counter = self.secure_counter.wrapping_sub(sub);\n        self.info.sub().write(|w| w.set_sb(sub));\n    }\n\n    /// Checks the secure counter value and restarts the watchdog.\n    ///\n    /// This stops the watchdog, verifies the secure counter matches the expected\n    /// value, and restarts with the same instruction timer reload value.\n    ///\n    /// # Arguments\n    /// * `check` - Expected secure counter value\n    ///\n    /// # Note\n    /// If the counter doesn't match, a miscompare fault may be triggered\n    /// depending on configuration.\n    pub fn check(&mut self, check: u32) {\n        self.secure_counter = check;\n        self.info.stop().write(|w| w.set_stp(self.secure_counter));\n        let reload = self.info.reload().read().rload();\n        self.info.reload().write(|w| w.set_rload(reload));\n        self.info.start().write(|w| w.set_strt(self.secure_counter));\n    }\n\n    /// Stops the watchdog timer.\n    ///\n    /// # Note\n    /// This is a private method. The watchdog is stopped by writing the\n    /// current secure counter value to the STOP register.\n    fn stop(&mut self) {\n        self.info.stop().write(|w| w.set_stp(self.secure_counter));\n    }\n\n    /// Reads the current instruction timer value.\n    ///\n    /// # Returns\n    /// Current countdown value of the instruction timer.\n    pub fn get_instruction_timer(&self) -> u32 {\n        self.info.instruction_timer().read().instim()\n    }\n\n    // Gets the current secure counter value.\n    ///\n    /// # Returns\n    /// The software-tracked secure counter value.\n    pub fn get_secure_counter(&self) -> u32 {\n        self.secure_counter\n    }\n\n    /// Updates the instruction timer reload value.\n    ///\n    /// # Arguments\n    /// * `instruction_timer_value` - New timeout period in clock cycles\n    pub fn update_instruction_timer(&mut self, instruction_timer_value: u32) {\n        self.info.reload().write(|w| w.set_rload(instruction_timer_value));\n    }\n\n    /// Sets a persistent value in the CDOG peripheral.\n    ///\n    /// This value is stored in the 32 bits PERSISTENT register and persist through resets other than a Power-On Reset (POR).\n    ///\n    /// # Arguments\n    /// * `value` - The 32-bit value to store in the persistent register\n    pub fn set_persistent_value(&mut self, value: u32) {\n        self.info.persistent().write(|w| w.set_persis(value));\n    }\n\n    /// Gets the persistent value from the CDOG peripheral.\n    ///\n    /// # Returns\n    /// The 32-bit value stored in the persistent register\n    pub fn get_persistent_value(&self) -> u32 {\n        self.info.persistent().read().persis()\n    }\n}\n\n/// CDOG0 interrupt handler.\n///\n/// This handler is called when any cdog interrupt fires.\n/// When reset happens, the interrupt handler will never be reached.\npub struct InterruptHandler;\n\nimpl Handler<typelevel::CDOG0> for InterruptHandler {\n    unsafe fn on_interrupt() {\n        crate::perf_counters::incr_interrupt_cdog0();\n        let cdog0 = pac::CDOG0;\n\n        // Print all flags at once using the Debug implementation\n        #[cfg(feature = \"defmt\")]\n        defmt::trace!(\"CDOG0 flags {}\", cdog0.flags().read());\n\n        // Stop the cdog\n        cdog0.stop().write(|w| w.set_stp(0));\n\n        // Clear all flags by writing 0\n        cdog0.flags().write(|w| w.0 = 0);\n    }\n}\n"
  },
  {
    "path": "embassy-mcxa/src/chips/mcxa2xx.rs",
    "content": "//! Module for MCXA2xx family\n\npub use inner_periph::{Peripherals, peripherals};\n\nuse crate::interrupt::InterruptExt;\n\n// NOTE: macro generates missing safety docs and unsafe calls in unsafe blocks,\n// allow for now, and put in a module so we can apply the rule to that scope.\n#[allow(clippy::missing_safety_doc, unsafe_op_in_unsafe_fn)]\nmod inner_periph {\n    #[rustfmt::skip]\n    embassy_hal_internal::peripherals!(\n        ADC0,\n        ADC1,\n\n        AOI0,\n        AOI1,\n\n        CAN0,\n\n        CDOG0,\n        CDOG1,\n\n        // CLKOUT is not specifically a peripheral (it's part of SYSCON),\n        // but we still want it to be a singleton.\n        CLKOUT,\n\n        CMC,\n        CMP0,\n        CMP1,\n        CRC0,\n\n        CTIMER0,\n\n        CTIMER0_CH0,\n        CTIMER0_CH1,\n        CTIMER0_CH2,\n        CTIMER0_CH3,\n\n        CTIMER1,\n\n        CTIMER1_CH0,\n        CTIMER1_CH1,\n        CTIMER1_CH2,\n        CTIMER1_CH3,\n\n        CTIMER2,\n\n        CTIMER2_CH0,\n        CTIMER2_CH1,\n        CTIMER2_CH2,\n        CTIMER2_CH3,\n\n        CTIMER3,\n\n        CTIMER3_CH0,\n        CTIMER3_CH1,\n        CTIMER3_CH2,\n        CTIMER3_CH3,\n\n        CTIMER4,\n\n        CTIMER4_CH0,\n        CTIMER4_CH1,\n        CTIMER4_CH2,\n        CTIMER4_CH3,\n\n        DBGMAILBOX,\n        DMA0,\n        DMA_CH0,\n        DMA_CH1,\n        DMA_CH2,\n        DMA_CH3,\n        DMA_CH4,\n        DMA_CH5,\n        DMA_CH6,\n        DMA_CH7,\n        EDMA0_TCD0,\n        EIM0,\n        EQDC0,\n        EQDC1,\n        ERM0,\n        FLEXIO0,\n        FLEXPWM0,\n        FLEXPWM1,\n        FMC0,\n        FMU0,\n        FREQME0,\n        GLIKEY0,\n\n        GPIO0,\n        GPIO1,\n        GPIO2,\n        GPIO3,\n        GPIO4,\n\n        I3C0,\n        INPUTMUX0,\n\n        LPI2C0,\n        LPI2C1,\n        LPI2C2,\n        LPI2C3,\n\n        LPSPI0,\n        LPSPI1,\n\n        LPTMR0,\n\n        LPUART0,\n        LPUART1,\n        LPUART2,\n        LPUART3,\n        LPUART4,\n\n        MAU0,\n        MBC0,\n        MRCC0,\n        OPAMP0,\n        OSTIMER0,\n\n        // Normally SWDIO!\n        #[cfg(feature = \"swd-as-gpio\")]\n        P0_0,\n        // Normally SWCLK!\n        #[cfg(feature = \"swd-as-gpio\")]\n        P0_1,\n        // Normally SWO!\n        #[cfg(feature = \"swd-swo-as-gpio\")]\n        P0_2,\n        // Normally JTAG TDI!\n        #[cfg(feature = \"jtag-extras-as-gpio\")]\n        P0_3,\n        P0_4,\n        P0_5,\n        // Normally JTAG ISPMODE_N!\n        #[cfg(feature = \"jtag-extras-as-gpio\")]\n        P0_6,\n        P0_7,\n        P0_8,\n        P0_9,\n        P0_10,\n        P0_11,\n        P0_12,\n        P0_13,\n        P0_14,\n        P0_15,\n        P0_16,\n        P0_17,\n        P0_18,\n        P0_19,\n        P0_20,\n        P0_21,\n        P0_22,\n        P0_23,\n        P0_24,\n        P0_25,\n        P0_26,\n        P0_27,\n        P0_28,\n        P0_29,\n        P0_30,\n        P0_31,\n\n        P1_0,\n        P1_1,\n        P1_2,\n        P1_3,\n        P1_4,\n        P1_5,\n        P1_6,\n        P1_7,\n        P1_8,\n        P1_9,\n        P1_10,\n        P1_11,\n        P1_12,\n        P1_13,\n        P1_14,\n        P1_15,\n        P1_16,\n        P1_17,\n        P1_18,\n        P1_19,\n        P1_20,\n        P1_21,\n        P1_22,\n        P1_23,\n        P1_24,\n        P1_25,\n        P1_26,\n        P1_27,\n        P1_28,\n        #[cfg(feature = \"dangerous-reset-as-gpio\")]\n        P1_29,\n        #[cfg(feature = \"sosc-as-gpio\")]\n        P1_30,\n        #[cfg(feature = \"sosc-as-gpio\")]\n        P1_31,\n\n        P2_0,\n        P2_1,\n        P2_2,\n        P2_3,\n        P2_4,\n        P2_5,\n        P2_6,\n        P2_7,\n        P2_8,\n        P2_9,\n        P2_10,\n        P2_11,\n        P2_12,\n        P2_13,\n        P2_14,\n        P2_15,\n        P2_16,\n        P2_17,\n        P2_18,\n        P2_19,\n        P2_20,\n        P2_21,\n        P2_22,\n        P2_23,\n        P2_24,\n        P2_25,\n        P2_26,\n        P2_27,\n        P2_28,\n        P2_29,\n        P2_30,\n        P2_31,\n\n        P3_0,\n        P3_1,\n        P3_2,\n        P3_3,\n        P3_4,\n        P3_5,\n        P3_6,\n        P3_7,\n        P3_8,\n        P3_9,\n        P3_10,\n        P3_11,\n        P3_12,\n        P3_13,\n        P3_14,\n        P3_15,\n        P3_16,\n        P3_17,\n        P3_18,\n        P3_19,\n        P3_20,\n        P3_21,\n        P3_22,\n        P3_23,\n        P3_24,\n        P3_25,\n        P3_26,\n        P3_27,\n        P3_28,\n        P3_29,\n        P3_30,\n        P3_31,\n\n        P4_0,\n        P4_1,\n        P4_2,\n        P4_3,\n        P4_4,\n        P4_5,\n        P4_6,\n        P4_7,\n        P4_8,\n        P4_9,\n        P4_10,\n        P4_11,\n        P4_12,\n        P4_13,\n        P4_14,\n        P4_15,\n        P4_16,\n        P4_17,\n        P4_18,\n        P4_19,\n        P4_20,\n        P4_21,\n        P4_22,\n        P4_23,\n        P4_24,\n        P4_25,\n        P4_26,\n        P4_27,\n        P4_28,\n        P4_29,\n        P4_30,\n        P4_31,\n\n        P5_0,\n        P5_1,\n        P5_2,\n        P5_3,\n        P5_4,\n        P5_5,\n        P5_6,\n        P5_7,\n        P5_8,\n        P5_9,\n        P5_10,\n        P5_11,\n        P5_12,\n        P5_13,\n        P5_14,\n        P5_15,\n        P5_16,\n        P5_17,\n        P5_18,\n        P5_19,\n        P5_20,\n        P5_21,\n        P5_22,\n        P5_23,\n        P5_24,\n        P5_25,\n        P5_26,\n        P5_27,\n        P5_28,\n        P5_29,\n        P5_30,\n        P5_31,\n\n        PKC0,\n\n        PORT0,\n        PORT1,\n        PORT2,\n        PORT3,\n        PORT4,\n\n        RTC0,\n        SAU,\n        SCG0,\n        SCN_SCB,\n        SGI0,\n        SMARTDMA0,\n        SPC0,\n        SYSCON,\n        TDET0,\n        TRNG0,\n        UDF0,\n        USB0,\n        UTICK0,\n        VBAT0,\n        WAKETIMER0,\n        WUU0,\n        WWDT0,\n    );\n}\n\n// NOTE: Macro has missing safety docs and makes unsafe calls in unsafe fns\npub use inner_interrupt::*;\n#[allow(clippy::missing_safety_doc, unsafe_op_in_unsafe_fn)]\nmod inner_interrupt {\n    embassy_hal_internal::interrupt_mod!(\n        ADC0,\n        ADC1,\n        CAN0,\n        CDOG0,\n        CDOG1,\n        CMC,\n        CMP0,\n        CMP1,\n        CTIMER0,\n        CTIMER1,\n        CTIMER2,\n        CTIMER3,\n        CTIMER4,\n        DAC0,\n        DMA_CH0,\n        DMA_CH1,\n        DMA_CH2,\n        DMA_CH3,\n        DMA_CH4,\n        DMA_CH5,\n        DMA_CH6,\n        DMA_CH7,\n        EQDC0_COMPARE,\n        EQDC0_HOME,\n        EQDC0_INDEX,\n        EQDC0_WATCHDOG,\n        EQDC1_COMPARE,\n        EQDC1_HOME,\n        EQDC1_INDEX,\n        EQDC1_WATCHDOG,\n        ERM0_MULTI_BIT,\n        ERM0_SINGLE_BIT,\n        FLEXIO,\n        FLEXPWM0_FAULT,\n        FLEXPWM0_RELOAD_ERROR,\n        FLEXPWM0_SUBMODULE0,\n        FLEXPWM0_SUBMODULE1,\n        FLEXPWM0_SUBMODULE2,\n        FLEXPWM0_SUBMODULE3,\n        FLEXPWM1_FAULT,\n        FLEXPWM1_RELOAD_ERROR,\n        FLEXPWM1_SUBMODULE0,\n        FLEXPWM1_SUBMODULE1,\n        FLEXPWM1_SUBMODULE2,\n        FLEXPWM1_SUBMODULE3,\n        FMU0,\n        FREQME0,\n        GLIKEY0,\n        GPIO0,\n        GPIO1,\n        GPIO2,\n        GPIO3,\n        GPIO4,\n        I3C0,\n        LPI2C0,\n        LPI2C1,\n        LPI2C2,\n        LPI2C3,\n        LPSPI0,\n        LPSPI1,\n        LPTMR0,\n        LPUART0,\n        LPUART1,\n        LPUART2,\n        LPUART3,\n        LPUART4,\n        MAU,\n        MBC0,\n        OS_EVENT,\n        PKC,\n        RTC,\n        RTC_1HZ,\n        SCG0,\n        SGI,\n        SMARTDMA,\n        SPC0,\n        TDET,\n        TRNG0,\n        USB0,\n        UTICK0,\n        WAKETIMER0,\n        WUU0,\n        WWDT0,\n    );\n}\n\n/// Initialize HAL with configuration (mirrors embassy-imxrt style). Minimal: just take peripherals.\n/// Also applies configurable NVIC priority for the OSTIMER OS_EVENT interrupt (no enabling).\npub fn init(cfg: crate::config::Config) -> Peripherals {\n    // Might not need to be mutable if none of the `...-as-gpio` features are active.\n    #[allow(unused_mut)]\n    let mut peripherals = Peripherals::take();\n\n    crate::interrupt::RTC.set_priority(cfg.rtc_interrupt_priority);\n    crate::interrupt::GPIO0.set_priority(cfg.gpio_interrupt_priority);\n    crate::interrupt::GPIO1.set_priority(cfg.gpio_interrupt_priority);\n    crate::interrupt::GPIO2.set_priority(cfg.gpio_interrupt_priority);\n    crate::interrupt::GPIO3.set_priority(cfg.gpio_interrupt_priority);\n    crate::interrupt::GPIO4.set_priority(cfg.gpio_interrupt_priority);\n\n    // Configure clocks\n    crate::clocks::init(cfg.clock_cfg).unwrap();\n\n    // Initialize embassy-time global driver backed by OSTIMER0\n    // NOTE: As early as possible, but MUST be AFTER clocks!\n    crate::ostimer::init(cfg.time_interrupt_priority);\n\n    // Initialize the INPUTMUX peripheral\n    crate::inputmux::init();\n\n    // Enable interrupts\n    unsafe {\n        cortex_m::interrupt::enable();\n    }\n\n    // Initialize DMA controller (clock, reset, configuration)\n    crate::dma::init();\n\n    // Enable GPIO clocks\n    unsafe {\n        _ = crate::clocks::enable_and_reset::<crate::peripherals::PORT0>(&crate::clocks::periph_helpers::NoConfig);\n        _ = crate::clocks::enable_and_reset::<crate::peripherals::GPIO0>(&crate::clocks::periph_helpers::NoConfig);\n\n        _ = crate::clocks::enable_and_reset::<crate::peripherals::PORT1>(&crate::clocks::periph_helpers::NoConfig);\n        _ = crate::clocks::enable_and_reset::<crate::peripherals::GPIO1>(&crate::clocks::periph_helpers::NoConfig);\n\n        _ = crate::clocks::enable_and_reset::<crate::peripherals::PORT2>(&crate::clocks::periph_helpers::NoConfig);\n        _ = crate::clocks::enable_and_reset::<crate::peripherals::GPIO2>(&crate::clocks::periph_helpers::NoConfig);\n\n        _ = crate::clocks::enable_and_reset::<crate::peripherals::PORT3>(&crate::clocks::periph_helpers::NoConfig);\n        _ = crate::clocks::enable_and_reset::<crate::peripherals::GPIO3>(&crate::clocks::periph_helpers::NoConfig);\n\n        _ = crate::clocks::enable_and_reset::<crate::peripherals::PORT4>(&crate::clocks::periph_helpers::NoConfig);\n        _ = crate::clocks::enable_and_reset::<crate::peripherals::GPIO4>(&crate::clocks::periph_helpers::NoConfig);\n    }\n\n    // import may be unused if none of the following features are set\n    #[allow(unused_imports)]\n    use crate::gpio::SealedPin;\n\n    // If we are not using SWD pins for SWD reasons, make them floating inputs\n    #[cfg(feature = \"swd-as-gpio\")]\n    {\n        peripherals.P0_0.set_as_disabled();\n        peripherals.P0_1.set_as_disabled();\n    }\n    #[cfg(feature = \"swd-swo-as-gpio\")]\n    {\n        peripherals.P0_2.set_as_disabled();\n    }\n    #[cfg(feature = \"jtag-extras-as-gpio\")]\n    {\n        peripherals.P0_3.set_as_disabled();\n        peripherals.P0_6.set_as_disabled();\n    }\n    #[cfg(feature = \"dangerous-reset-as-gpio\")]\n    {\n        // DANGER DANGER DANGER\n        peripherals.P1_29.set_as_disabled();\n    }\n\n    peripherals\n}\n\n// Chip specific GPIO impls\nmod gpio_impls {\n    use crate::gpio::{AnyPin, GpioPin, Pull, SealedPin};\n    use crate::impl_pin;\n    use crate::pac::common::{RW, Reg};\n    use crate::pac::gpio::vals::{Pdd, Pid};\n    use crate::pac::port::regs::Pcr;\n    use crate::pac::port::vals::{Dse, Ibe, Mux, Sre};\n\n    #[cfg(feature = \"swd-as-gpio\")]\n    impl_pin!(P0_0, 0, 0, GPIO0);\n    #[cfg(feature = \"swd-as-gpio\")]\n    impl_pin!(P0_1, 0, 1, GPIO0);\n    #[cfg(feature = \"swd-swo-as-gpio\")]\n    impl_pin!(P0_2, 0, 2, GPIO0);\n    #[cfg(feature = \"jtag-extras-as-gpio\")]\n    impl_pin!(P0_3, 0, 3, GPIO0);\n    impl_pin!(P0_4, 0, 4, GPIO0);\n    impl_pin!(P0_5, 0, 5, GPIO0);\n    #[cfg(feature = \"jtag-extras-as-gpio\")]\n    impl_pin!(P0_6, 0, 6, GPIO0);\n    impl_pin!(P0_7, 0, 7, GPIO0);\n    impl_pin!(P0_8, 0, 8, GPIO0);\n    impl_pin!(P0_9, 0, 9, GPIO0);\n    impl_pin!(P0_10, 0, 10, GPIO0);\n    impl_pin!(P0_11, 0, 11, GPIO0);\n    impl_pin!(P0_12, 0, 12, GPIO0);\n    impl_pin!(P0_13, 0, 13, GPIO0);\n    impl_pin!(P0_14, 0, 14, GPIO0);\n    impl_pin!(P0_15, 0, 15, GPIO0);\n    impl_pin!(P0_16, 0, 16, GPIO0);\n    impl_pin!(P0_17, 0, 17, GPIO0);\n    impl_pin!(P0_18, 0, 18, GPIO0);\n    impl_pin!(P0_19, 0, 19, GPIO0);\n    impl_pin!(P0_20, 0, 20, GPIO0);\n    impl_pin!(P0_21, 0, 21, GPIO0);\n    impl_pin!(P0_22, 0, 22, GPIO0);\n    impl_pin!(P0_23, 0, 23, GPIO0);\n    impl_pin!(P0_24, 0, 24, GPIO0);\n    impl_pin!(P0_25, 0, 25, GPIO0);\n    impl_pin!(P0_26, 0, 26, GPIO0);\n    impl_pin!(P0_27, 0, 27, GPIO0);\n    impl_pin!(P0_28, 0, 28, GPIO0);\n    impl_pin!(P0_29, 0, 29, GPIO0);\n    impl_pin!(P0_30, 0, 30, GPIO0);\n    impl_pin!(P0_31, 0, 31, GPIO0);\n\n    impl_pin!(P1_0, 1, 0, GPIO1);\n    impl_pin!(P1_1, 1, 1, GPIO1);\n    impl_pin!(P1_2, 1, 2, GPIO1);\n    impl_pin!(P1_3, 1, 3, GPIO1);\n    impl_pin!(P1_4, 1, 4, GPIO1);\n    impl_pin!(P1_5, 1, 5, GPIO1);\n    impl_pin!(P1_6, 1, 6, GPIO1);\n    impl_pin!(P1_7, 1, 7, GPIO1);\n    impl_pin!(P1_8, 1, 8, GPIO1);\n    impl_pin!(P1_9, 1, 9, GPIO1);\n    impl_pin!(P1_10, 1, 10, GPIO1);\n    impl_pin!(P1_11, 1, 11, GPIO1);\n    impl_pin!(P1_12, 1, 12, GPIO1);\n    impl_pin!(P1_13, 1, 13, GPIO1);\n    impl_pin!(P1_14, 1, 14, GPIO1);\n    impl_pin!(P1_15, 1, 15, GPIO1);\n    impl_pin!(P1_16, 1, 16, GPIO1);\n    impl_pin!(P1_17, 1, 17, GPIO1);\n    impl_pin!(P1_18, 1, 18, GPIO1);\n    impl_pin!(P1_19, 1, 19, GPIO1);\n    impl_pin!(P1_20, 1, 20, GPIO1);\n    impl_pin!(P1_21, 1, 21, GPIO1);\n    impl_pin!(P1_22, 1, 22, GPIO1);\n    impl_pin!(P1_23, 1, 23, GPIO1);\n    impl_pin!(P1_24, 1, 24, GPIO1);\n    impl_pin!(P1_25, 1, 25, GPIO1);\n    impl_pin!(P1_26, 1, 26, GPIO1);\n    impl_pin!(P1_27, 1, 27, GPIO1);\n    impl_pin!(P1_28, 1, 28, GPIO1);\n    #[cfg(feature = \"dangerous-reset-as-gpio\")]\n    impl_pin!(P1_29, 1, 29, GPIO1);\n    #[cfg(feature = \"sosc-as-gpio\")]\n    impl_pin!(P1_30, 1, 30, GPIO1);\n    #[cfg(feature = \"sosc-as-gpio\")]\n    impl_pin!(P1_31, 1, 31, GPIO1);\n\n    impl_pin!(P2_0, 2, 0, GPIO2);\n    impl_pin!(P2_1, 2, 1, GPIO2);\n    impl_pin!(P2_2, 2, 2, GPIO2);\n    impl_pin!(P2_3, 2, 3, GPIO2);\n    impl_pin!(P2_4, 2, 4, GPIO2);\n    impl_pin!(P2_5, 2, 5, GPIO2);\n    impl_pin!(P2_6, 2, 6, GPIO2);\n    impl_pin!(P2_7, 2, 7, GPIO2);\n    impl_pin!(P2_8, 2, 8, GPIO2);\n    impl_pin!(P2_9, 2, 9, GPIO2);\n    impl_pin!(P2_10, 2, 10, GPIO2);\n    impl_pin!(P2_11, 2, 11, GPIO2);\n    impl_pin!(P2_12, 2, 12, GPIO2);\n    impl_pin!(P2_13, 2, 13, GPIO2);\n    impl_pin!(P2_14, 2, 14, GPIO2);\n    impl_pin!(P2_15, 2, 15, GPIO2);\n    impl_pin!(P2_16, 2, 16, GPIO2);\n    impl_pin!(P2_17, 2, 17, GPIO2);\n    impl_pin!(P2_18, 2, 18, GPIO2);\n    impl_pin!(P2_19, 2, 19, GPIO2);\n    impl_pin!(P2_20, 2, 20, GPIO2);\n    impl_pin!(P2_21, 2, 21, GPIO2);\n    impl_pin!(P2_22, 2, 22, GPIO2);\n    impl_pin!(P2_23, 2, 23, GPIO2);\n    impl_pin!(P2_24, 2, 24, GPIO2);\n    impl_pin!(P2_25, 2, 25, GPIO2);\n    impl_pin!(P2_26, 2, 26, GPIO2);\n    // impl_pin!(P2_27, 2, 27, GPIO2);\n    // impl_pin!(P2_28, 2, 28, GPIO2);\n    // impl_pin!(P2_29, 2, 29, GPIO2);\n    // impl_pin!(P2_30, 2, 30, GPIO2);\n    // impl_pin!(P2_31, 2, 31, GPIO2);\n\n    impl_pin!(P3_0, 3, 0, GPIO3);\n    impl_pin!(P3_1, 3, 1, GPIO3);\n    impl_pin!(P3_2, 3, 2, GPIO3);\n    impl_pin!(P3_3, 3, 3, GPIO3);\n    impl_pin!(P3_4, 3, 4, GPIO3);\n    impl_pin!(P3_5, 3, 5, GPIO3);\n    impl_pin!(P3_6, 3, 6, GPIO3);\n    impl_pin!(P3_7, 3, 7, GPIO3);\n    impl_pin!(P3_8, 3, 8, GPIO3);\n    impl_pin!(P3_9, 3, 9, GPIO3);\n    impl_pin!(P3_10, 3, 10, GPIO3);\n    impl_pin!(P3_11, 3, 11, GPIO3);\n    impl_pin!(P3_12, 3, 12, GPIO3);\n    impl_pin!(P3_13, 3, 13, GPIO3);\n    impl_pin!(P3_14, 3, 14, GPIO3);\n    impl_pin!(P3_15, 3, 15, GPIO3);\n    impl_pin!(P3_16, 3, 16, GPIO3);\n    impl_pin!(P3_17, 3, 17, GPIO3);\n    impl_pin!(P3_18, 3, 18, GPIO3);\n    impl_pin!(P3_19, 3, 19, GPIO3);\n    impl_pin!(P3_20, 3, 20, GPIO3);\n    impl_pin!(P3_21, 3, 21, GPIO3);\n    impl_pin!(P3_22, 3, 22, GPIO3);\n    impl_pin!(P3_23, 3, 23, GPIO3);\n    impl_pin!(P3_24, 3, 24, GPIO3);\n    impl_pin!(P3_25, 3, 25, GPIO3);\n    impl_pin!(P3_26, 3, 26, GPIO3);\n    impl_pin!(P3_27, 3, 27, GPIO3);\n    impl_pin!(P3_28, 3, 28, GPIO3);\n    impl_pin!(P3_29, 3, 29, GPIO3);\n    impl_pin!(P3_30, 3, 30, GPIO3);\n    impl_pin!(P3_31, 3, 31, GPIO3);\n\n    impl_pin!(P4_0, 4, 0, GPIO4);\n    impl_pin!(P4_1, 4, 1, GPIO4);\n    impl_pin!(P4_2, 4, 2, GPIO4);\n    impl_pin!(P4_3, 4, 3, GPIO4);\n    impl_pin!(P4_4, 4, 4, GPIO4);\n    impl_pin!(P4_5, 4, 5, GPIO4);\n    impl_pin!(P4_6, 4, 6, GPIO4);\n    impl_pin!(P4_7, 4, 7, GPIO4);\n    // impl_pin!(P4_8, 4, 8, GPIO4);\n    // impl_pin!(P4_9, 4, 9, GPIO4);\n    // impl_pin!(P4_10, 4, 10, GPIO4);\n    // impl_pin!(P4_11, 4, 11, GPIO4);\n    // impl_pin!(P4_12, 4, 12, GPIO4);\n    // impl_pin!(P4_13, 4, 13, GPIO4);\n    // impl_pin!(P4_14, 4, 14, GPIO4);\n    // impl_pin!(P4_15, 4, 15, GPIO4);\n    // impl_pin!(P4_16, 4, 16, GPIO4);\n    // impl_pin!(P4_17, 4, 17, GPIO4);\n    // impl_pin!(P4_18, 4, 18, GPIO4);\n    // impl_pin!(P4_19, 4, 19, GPIO4);\n    // impl_pin!(P4_20, 4, 20, GPIO4);\n    // impl_pin!(P4_21, 4, 21, GPIO4);\n    // impl_pin!(P4_22, 4, 22, GPIO4);\n    // impl_pin!(P4_23, 4, 23, GPIO4);\n    // impl_pin!(P4_24, 4, 24, GPIO4);\n    // impl_pin!(P4_25, 4, 25, GPIO4);\n    // impl_pin!(P4_26, 4, 26, GPIO4);\n    // impl_pin!(P4_27, 4, 27, GPIO4);\n    // impl_pin!(P4_28, 4, 28, GPIO4);\n    // impl_pin!(P4_29, 4, 29, GPIO4);\n    // impl_pin!(P4_30, 4, 30, GPIO4);\n    // impl_pin!(P4_31, 4, 31, GPIO4);\n}\n\n/// This module contains implementations of MRCC APIs, specifically of the [`Gate`] trait,\n/// for various low level peripherals.\npub(crate) mod peripheral_gating {\n    use paste::paste;\n\n    use crate::clocks::Gate;\n    use crate::clocks::periph_helpers::{\n        AdcConfig, CTimerConfig, Clk1MConfig, I3cConfig, Lpi2cConfig, LpspiConfig, LpuartConfig, NoConfig,\n        OsTimerConfig,\n    };\n    use crate::{impl_cc_gate, pac};\n\n    // These peripherals have no additional upstream clocks or configuration required\n    // other than enabling through the MRCC gate. Currently, these peripherals will\n    // ALWAYS return `Ok(0)` when calling [`enable_and_reset()`] and/or\n    // [`SPConfHelper::post_enable_config()`].\n    impl_cc_gate!(PORT0, mrcc_glb_cc1, mrcc_glb_rst1, port0, NoConfig);\n    impl_cc_gate!(PORT1, mrcc_glb_cc1, mrcc_glb_rst1, port1, NoConfig);\n    impl_cc_gate!(PORT2, mrcc_glb_cc1, mrcc_glb_rst1, port2, NoConfig);\n    impl_cc_gate!(PORT3, mrcc_glb_cc1, mrcc_glb_rst1, port3, NoConfig);\n    impl_cc_gate!(PORT4, mrcc_glb_cc1, mrcc_glb_rst1, port4, NoConfig);\n\n    impl_cc_gate!(CRC0, mrcc_glb_cc0, mrcc_glb_rst0, crc0, NoConfig);\n    impl_cc_gate!(INPUTMUX0, mrcc_glb_cc0, mrcc_glb_rst0, inputmux0, NoConfig);\n\n    // These peripherals DO have meaningful configuration, and could fail if the system\n    // clocks do not match their needs.\n    impl_cc_gate!(ADC0, mrcc_glb_cc1, mrcc_glb_rst1, adc0, AdcConfig);\n    impl_cc_gate!(ADC1, mrcc_glb_cc1, mrcc_glb_rst1, adc1, AdcConfig);\n\n    impl_cc_gate!(I3C0, mrcc_glb_cc0, mrcc_glb_rst0, i3c0, I3cConfig);\n    impl_cc_gate!(CTIMER0, mrcc_glb_cc0, mrcc_glb_rst0, ctimer0, CTimerConfig);\n    impl_cc_gate!(CTIMER1, mrcc_glb_cc0, mrcc_glb_rst0, ctimer1, CTimerConfig);\n    impl_cc_gate!(CTIMER2, mrcc_glb_cc0, mrcc_glb_rst0, ctimer2, CTimerConfig);\n    impl_cc_gate!(CTIMER3, mrcc_glb_cc0, mrcc_glb_rst0, ctimer3, CTimerConfig);\n    impl_cc_gate!(CTIMER4, mrcc_glb_cc0, mrcc_glb_rst0, ctimer4, CTimerConfig);\n    impl_cc_gate!(OSTIMER0, mrcc_glb_cc1, mrcc_glb_rst1, ostimer0, OsTimerConfig);\n\n    // TRNG peripheral - uses NoConfig since it has no selectable clock source\n    impl_cc_gate!(TRNG0, mrcc_glb_cc1, mrcc_glb_rst1, trng0, NoConfig);\n\n    // Peripherals that use ACC instead of CC!\n    impl_cc_gate!(LPUART0, mrcc_glb_acc0, mrcc_glb_rst0, lpuart0, LpuartConfig);\n    impl_cc_gate!(LPUART1, mrcc_glb_acc0, mrcc_glb_rst0, lpuart1, LpuartConfig);\n    impl_cc_gate!(LPUART2, mrcc_glb_acc0, mrcc_glb_rst0, lpuart2, LpuartConfig);\n    impl_cc_gate!(LPUART3, mrcc_glb_acc0, mrcc_glb_rst0, lpuart3, LpuartConfig);\n    impl_cc_gate!(LPUART4, mrcc_glb_acc0, mrcc_glb_rst0, lpuart4, LpuartConfig);\n\n    // DMA0 peripheral - uses NoConfig since it has no selectable clock source\n    impl_cc_gate!(DMA0, mrcc_glb_acc0, mrcc_glb_rst0, dma0, NoConfig);\n\n    impl_cc_gate!(GPIO0, mrcc_glb_acc2, mrcc_glb_rst2, gpio0, NoConfig);\n    impl_cc_gate!(GPIO1, mrcc_glb_acc2, mrcc_glb_rst2, gpio1, NoConfig);\n    impl_cc_gate!(GPIO2, mrcc_glb_acc2, mrcc_glb_rst2, gpio2, NoConfig);\n    impl_cc_gate!(GPIO3, mrcc_glb_acc2, mrcc_glb_rst2, gpio3, NoConfig);\n    impl_cc_gate!(GPIO4, mrcc_glb_acc2, mrcc_glb_rst2, gpio4, NoConfig);\n\n    impl_cc_gate!(LPI2C0, mrcc_glb_acc0, mrcc_glb_rst0, lpi2c0, Lpi2cConfig);\n    impl_cc_gate!(LPI2C1, mrcc_glb_acc0, mrcc_glb_rst0, lpi2c1, Lpi2cConfig);\n    impl_cc_gate!(LPI2C2, mrcc_glb_acc1, mrcc_glb_rst1, lpi2c2, Lpi2cConfig);\n    impl_cc_gate!(LPI2C3, mrcc_glb_acc1, mrcc_glb_rst1, lpi2c3, Lpi2cConfig);\n\n    impl_cc_gate!(LPSPI0, mrcc_glb_acc0, mrcc_glb_rst0, lpspi0, LpspiConfig);\n    impl_cc_gate!(LPSPI1, mrcc_glb_acc0, mrcc_glb_rst0, lpspi1, LpspiConfig);\n\n    impl_cc_gate!(WWDT0, mrcc_glb_acc0, wwdt0, Clk1MConfig);\n}\n\npub(crate) mod clock_limits {\n    use crate::chips::ClockLimits;\n\n    // TODO: Different for different CPUs?\n    pub const VDD_CORE_MID_DRIVE_WAIT_STATE_LIMITS: &[(u32, u8)] = &[(22_500_000, 0b0000)];\n    pub const VDD_CORE_MID_DRIVE_MAX_WAIT_STATES: u8 = 0b0001;\n\n    pub const VDD_CORE_OVER_DRIVE_WAIT_STATE_LIMITS: &[(u32, u8)] = &[\n        (40_000_000, 0b0000),\n        (80_000_000, 0b0001),\n        (120_000_000, 0b0010),\n        (160_000_000, 0b0011),\n    ];\n    pub const VDD_CORE_OVER_DRIVE_MAX_WAIT_STATES: u8 = 0b0100;\n\n    impl ClockLimits {\n        pub const MID_DRIVE: Self = Self {\n            fro_hf: 90_000_000,\n            fro_hf_div: 45_000_000,\n            pll1_clk: 48_000_000,\n            main_clk: 90_000_000,\n            cpu_clk: 45_000_000,\n            pll1_clk_div: 48_000_000,\n            // clk_16k: 16_384,\n            // clk_in: 50_000_000,\n            // clk_48m: 48_000_000,\n            // fro_12m: 24_000_000, // what?\n            // fro_12m_div: 24_000_000, // what?\n            // clk_1m: 1_000_000,\n            // system_clk: 45_000_000,\n            // bus_clk: 22_500_000,\n            // slow_clk: 7_500_000,\n        };\n\n        pub const OVER_DRIVE: Self = Self {\n            fro_hf: 180_000_000,\n            fro_hf_div: 180_000_000,\n            pll1_clk: 240_000_000,\n            main_clk: 180_000_000,\n            cpu_clk: 180_000_000,\n            pll1_clk_div: 240_000_000,\n            // clk_16k: 16_384,\n            // clk_in: 50_000_000,\n            // clk_48m: 48_000_000,\n            // fro_12m: 24_000_000, // what?\n            // fro_12m_div: 24_000_000, // what?\n            // clk_1m: 1_000_000,\n            // system_clk: 180_000_000,\n            // bus_clk: 90_000_000,\n            // slow_clk: 36_000_000,\n        };\n    }\n}\n"
  },
  {
    "path": "embassy-mcxa/src/chips/mcxa5xx.rs",
    "content": "//! Module for MCXA5xx family\n\npub use inner_periph::{Peripherals, peripherals};\n\nuse crate::interrupt::InterruptExt;\n\n// NOTE: macro generates missing safety docs and unsafe calls in unsafe blocks,\n// allow for now, and put in a module so we can apply the rule to that scope.\n#[allow(clippy::missing_safety_doc, unsafe_op_in_unsafe_fn)]\nmod inner_periph {\n    #[rustfmt::skip]\n    embassy_hal_internal::peripherals!(\n        ADC0,\n        ADC1,\n\n        // AOI0,\n        // AOI1,\n\n        // CAN0,\n        // CAN1,\n\n        CDOG0,\n        CDOG1,\n\n        // CLKOUT is not specifically a peripheral (it's part of SYSCON),\n        // but we still want it to be a singleton.\n        CLKOUT,\n\n        // CMC,\n        // CMP0,\n        // CMP1,\n        CRC0,\n\n        CTIMER0,\n\n        CTIMER0_CH0,\n        CTIMER0_CH1,\n        CTIMER0_CH2,\n        CTIMER0_CH3,\n\n        CTIMER1,\n\n        CTIMER1_CH0,\n        CTIMER1_CH1,\n        CTIMER1_CH2,\n        CTIMER1_CH3,\n\n        CTIMER2,\n\n        CTIMER2_CH0,\n        CTIMER2_CH1,\n        CTIMER2_CH2,\n        CTIMER2_CH3,\n\n        CTIMER3,\n\n        CTIMER3_CH0,\n        CTIMER3_CH1,\n        CTIMER3_CH2,\n        CTIMER3_CH3,\n\n        CTIMER4,\n\n        CTIMER4_CH0,\n        CTIMER4_CH1,\n        CTIMER4_CH2,\n        CTIMER4_CH3,\n\n        // DBGMAILBOX,\n\n        DMA0,\n        DMA_CH0,\n        DMA_CH1,\n        DMA_CH2,\n        DMA_CH3,\n        DMA_CH4,\n        DMA_CH5,\n        DMA_CH6,\n        DMA_CH7,\n        // Need more work on the DMA driver before we can enable these\n        // DMA_CH8,\n        // DMA_CH9,\n        // DMA_CH10,\n        // DMA_CH11,\n        EDMA0_TCD0,\n        // Need more work on the DMA driver before we can enable this\n        // EDMA0_TCD1,\n\n        // EIM0,\n        // EQDC0,\n        // EQDC1,\n        // ERM0,\n        // FLEXIO0,\n        // FLEXPWM0,\n        // FLEXPWM1,\n        // FMC0,\n        // FMU0,\n        // FREQME0,\n        // GLIKEY0,\n\n        GPIO0,\n        GPIO1,\n        GPIO2,\n        GPIO3,\n        GPIO4,\n        GPIO5,\n\n        I3C0,\n        I3C1,\n        I3C2,\n        I3C3,\n        INPUTMUX0,\n\n        LPI2C0,\n        LPI2C1,\n        LPI2C2,\n        LPI2C3,\n\n        LPSPI0,\n        LPSPI1,\n        LPSPI2,\n        LPSPI3,\n        LPSPI4,\n        LPSPI5,\n\n        // LPTMR0,\n\n        LPUART0,\n        LPUART1,\n        LPUART2,\n        LPUART3,\n        LPUART4,\n        LPUART5,\n\n        // MAU0,\n        // MBC0,\n        // MRCC0,\n        // OPAMP0,\n        OSTIMER0,\n\n        // Normally SWDIO!\n        #[cfg(feature = \"swd-as-gpio\")]\n        P0_0,\n        // Normally SWCLK!\n        #[cfg(feature = \"swd-as-gpio\")]\n        P0_1,\n        // Normally SWO!\n        #[cfg(feature = \"swd-swo-as-gpio\")]\n        P0_2,\n        // Normally JTAG TDI!\n        #[cfg(feature = \"jtag-extras-as-gpio\")]\n        P0_3,\n        P0_4,\n        P0_5,\n        // Normally JTAG ISPMODE_N!\n        #[cfg(feature = \"jtag-extras-as-gpio\")]\n        P0_6,\n        P0_7,\n        P0_8,\n        P0_9,\n        P0_10,\n        P0_11,\n        P0_12,\n        P0_13,\n        P0_14,\n        P0_15,\n        P0_16,\n        P0_17,\n        P0_18,\n        P0_19,\n        P0_20,\n        P0_21,\n        P0_22,\n        P0_23,\n        P0_24,\n        P0_25,\n        P0_26,\n        P0_27,\n\n        P1_0,\n        P1_1,\n        P1_2,\n        P1_3,\n        P1_4,\n        P1_5,\n        P1_6,\n        P1_7,\n        P1_8,\n        P1_9,\n        P1_10,\n        P1_11,\n        P1_12,\n        P1_13,\n        P1_14,\n        P1_15,\n        P1_16,\n        P1_17,\n        P1_18,\n        P1_19,\n        // Normally RESET_B!\n        #[cfg(feature = \"dangerous-reset-as-gpio\")]\n        P1_29,\n        // Normally XTAL48M!\n        #[cfg(feature = \"sosc-as-gpio\")]\n        P1_30,\n        // Normally EXTAL48M!\n        #[cfg(feature = \"sosc-as-gpio\")]\n        P1_31,\n\n        P2_0,\n        P2_1,\n        P2_2,\n        P2_3,\n        P2_4,\n        P2_5,\n        P2_6,\n        P2_7,\n        P2_8,\n        P2_9,\n        P2_10,\n        P2_11,\n        P2_12,\n        P2_13,\n        P2_14,\n        P2_15,\n        P2_16,\n        P2_17,\n        P2_18,\n        P2_19,\n        P2_20,\n        P2_21,\n        P2_22,\n        P2_23,\n        P2_24,\n        P2_25,\n        P2_26,\n        P2_28,\n        P2_29,\n        P2_30,\n        P2_31,\n\n        P3_0,\n        P3_1,\n        P3_2,\n        P3_3,\n        P3_4,\n        P3_5,\n        P3_6,\n        P3_7,\n        P3_8,\n        P3_9,\n        P3_10,\n        P3_11,\n        P3_12,\n        P3_13,\n        P3_14,\n        P3_15,\n        P3_16,\n        P3_17,\n        P3_18,\n        P3_19,\n        P3_20,\n        P3_21,\n        P3_22,\n        P3_23,\n        P3_24,\n        P3_25,\n        P3_26,\n        P3_27,\n        P3_28,\n        P3_29,\n        P3_30,\n        P3_31,\n\n        P4_0,\n        P4_1,\n        P4_2,\n        P4_3,\n        P4_4,\n        P4_5,\n        P4_6,\n        P4_7,\n        P4_8,\n        P4_9,\n        P4_10,\n        P4_11,\n        P4_12,\n        P4_13,\n\n        // Normally EXTAL32K!\n        #[cfg(feature = \"rosc-32k-as-gpio\")]\n        P5_0,\n        // Normally XTAL32K!\n        #[cfg(feature = \"rosc-32k-as-gpio\")]\n        P5_1,\n        P5_2,\n        P5_3,\n        P5_4,\n        P5_5,\n        P5_6,\n        P5_7,\n        P5_8,\n        P5_9,\n\n        // PKC0,\n\n        PORT0,\n        PORT1,\n        PORT2,\n        PORT3,\n        PORT4,\n        PORT5,\n\n        RTC0,\n        // SAU,\n        // SCG0,\n        // SCN_SCB,\n        // SGI0,\n        // SMARTDMA0,\n        // SPC0,\n        // SYSCON,\n        // TDET0,\n        TRNG0,\n        // UDF0,\n        // USB0,\n        // UTICK0,\n        // VBAT0,\n        // WAKETIMER0,\n        // WUU0,\n        WWDT0,\n        WWDT1,\n    );\n}\n\n// NOTE: Macro has missing safety docs and makes unsafe calls in unsafe fns\npub use inner_interrupt::*;\n#[allow(clippy::missing_safety_doc, unsafe_op_in_unsafe_fn)]\nmod inner_interrupt {\n    #[rustfmt::skip]\n    embassy_hal_internal::interrupt_mod!(\n        ADC0,\n        ADC1,\n\n        // CAN0,\n        // CAN1,\n\n        CDOG0,\n        CDOG1,\n\n        // CMC,\n\n        // CMP0,\n        // CMP1,\n        // CMP2,\n\n        CTIMER0,\n        CTIMER1,\n        CTIMER2,\n        CTIMER3,\n        CTIMER4,\n\n        // DAC0,\n\n        DMA_CH0,\n        DMA_CH1,\n        DMA_CH2,\n        DMA_CH3,\n        DMA_CH4,\n        DMA_CH5,\n        DMA_CH6,\n        DMA_CH7,\n        DMA_CH8,\n        DMA_CH9,\n        DMA_CH10,\n        DMA_CH11,\n\n        // EQDC0_COMPARE,\n        // EQDC0_HOME,\n        // EQDC0_INDEX,\n        // EQDC0_WATCHDOG,\n        // EQDC1_COMPARE,\n        // EQDC1_HOME,\n        // EQDC1_INDEX,\n        // EQDC1_WATCHDOG,\n        // ERM0_MULTI_BIT,\n        // ERM0_SINGLE_BIT,\n        // FLEXIO,\n        // FLEXPWM0_FAULT,\n        // FLEXPWM0_RELOAD_ERROR,\n        // FLEXPWM0_SUBMODULE0,\n        // FLEXPWM0_SUBMODULE1,\n        // FLEXPWM0_SUBMODULE2,\n        // FLEXPWM0_SUBMODULE3,\n        // FLEXPWM1_FAULT,\n        // FLEXPWM1_RELOAD_ERROR,\n        // FLEXPWM1_SUBMODULE0,\n        // FLEXPWM1_SUBMODULE1,\n        // FLEXPWM1_SUBMODULE2,\n        // FLEXPWM1_SUBMODULE3,\n        // FMU0,\n        // FREQME0,\n        // GLIKEY0,\n\n        GPIO0,\n        GPIO1,\n        GPIO2,\n        GPIO3,\n        GPIO4,\n        GPIO5,\n\n        I3C0,\n        I3C1,\n        I3C2,\n        I3C3,\n\n        LPI2C0,\n        LPI2C1,\n        LPI2C2,\n        LPI2C3,\n\n        LPSPI0,\n        LPSPI1,\n        LPSPI2,\n        LPSPI3,\n        LPSPI4,\n        LPSPI5,\n\n        // LPTMR0,\n        LPUART0,\n        LPUART1,\n        LPUART2,\n        LPUART3,\n        LPUART4,\n        LPUART5,\n        // MAU,\n        // MBC0,\n        OS_EVENT,\n        // PKC,\n        RTC,\n        // SCG0,\n        // SGI,\n        // SLCD,\n        // SMARTDMA,\n        // SPC0,\n        // TDET,\n        TRNG0,\n        // USB0,\n        // UTICK0,\n        // WAKETIMER0,\n        // WUU0,\n        WWDT0,\n        WWDT1,\n    );\n}\n\n/// Initialize HAL with configuration (mirrors embassy-imxrt style). Minimal: just take peripherals.\n/// Also applies configurable NVIC priority for the OSTIMER OS_EVENT interrupt (no enabling).\npub fn init(cfg: crate::config::Config) -> Peripherals {\n    // Might not need to be mutable if none of the `...-as-gpio` features are active.\n    #[allow(unused_mut)]\n    let mut peripherals = Peripherals::take();\n\n    // crate::interrupt::RTC.set_priority(cfg.rtc_interrupt_priority);\n    crate::interrupt::GPIO0.set_priority(cfg.gpio_interrupt_priority);\n    crate::interrupt::GPIO1.set_priority(cfg.gpio_interrupt_priority);\n    crate::interrupt::GPIO2.set_priority(cfg.gpio_interrupt_priority);\n    crate::interrupt::GPIO3.set_priority(cfg.gpio_interrupt_priority);\n    crate::interrupt::GPIO4.set_priority(cfg.gpio_interrupt_priority);\n    crate::interrupt::GPIO5.set_priority(cfg.gpio_interrupt_priority);\n\n    // Configure clocks\n    crate::clocks::init(cfg.clock_cfg).unwrap();\n\n    // Initialize embassy-time global driver backed by OSTIMER0\n    // NOTE: As early as possible, but MUST be AFTER clocks!\n    crate::ostimer::init(cfg.time_interrupt_priority);\n\n    // Initialize the INPUTMUX peripheral\n    crate::inputmux::init();\n\n    // Enable interrupts\n    unsafe {\n        cortex_m::interrupt::enable();\n    }\n\n    // Initialize DMA controller (clock, reset, configuration)\n    crate::dma::init();\n\n    // Enable GPIO clocks\n    unsafe {\n        _ = crate::clocks::enable_and_reset::<crate::peripherals::PORT0>(&crate::clocks::periph_helpers::NoConfig);\n        _ = crate::clocks::enable_and_reset::<crate::peripherals::GPIO0>(&crate::clocks::periph_helpers::NoConfig);\n\n        _ = crate::clocks::enable_and_reset::<crate::peripherals::PORT1>(&crate::clocks::periph_helpers::NoConfig);\n        _ = crate::clocks::enable_and_reset::<crate::peripherals::GPIO1>(&crate::clocks::periph_helpers::NoConfig);\n\n        _ = crate::clocks::enable_and_reset::<crate::peripherals::PORT2>(&crate::clocks::periph_helpers::NoConfig);\n        _ = crate::clocks::enable_and_reset::<crate::peripherals::GPIO2>(&crate::clocks::periph_helpers::NoConfig);\n\n        _ = crate::clocks::enable_and_reset::<crate::peripherals::PORT3>(&crate::clocks::periph_helpers::NoConfig);\n        _ = crate::clocks::enable_and_reset::<crate::peripherals::GPIO3>(&crate::clocks::periph_helpers::NoConfig);\n\n        _ = crate::clocks::enable_and_reset::<crate::peripherals::PORT4>(&crate::clocks::periph_helpers::NoConfig);\n        _ = crate::clocks::enable_and_reset::<crate::peripherals::GPIO4>(&crate::clocks::periph_helpers::NoConfig);\n\n        _ = crate::clocks::enable_and_reset::<crate::peripherals::PORT5>(&crate::clocks::periph_helpers::NoConfig);\n        _ = crate::clocks::enable_and_reset::<crate::peripherals::GPIO5>(&crate::clocks::periph_helpers::NoConfig);\n    }\n\n    // import may be unused if none of the following features are set\n    #[allow(unused_imports)]\n    use crate::gpio::SealedPin;\n\n    // If we are not using pins for specialized purposes, set them as disabled\n    #[cfg(feature = \"rosc-32k-as-gpio\")]\n    {\n        peripherals.P5_0.set_as_disabled();\n        peripherals.P5_1.set_as_disabled();\n    }\n    #[cfg(feature = \"sosc-as-gpio\")]\n    {\n        peripherals.P1_30.set_as_disabled();\n        peripherals.P1_31.set_as_disabled();\n    }\n    #[cfg(feature = \"swd-as-gpio\")]\n    {\n        peripherals.P0_0.set_as_disabled();\n        peripherals.P0_1.set_as_disabled();\n    }\n    #[cfg(feature = \"swd-swo-as-gpio\")]\n    {\n        peripherals.P0_2.set_as_disabled();\n    }\n    #[cfg(feature = \"jtag-extras-as-gpio\")]\n    {\n        peripherals.P0_3.set_as_disabled();\n        peripherals.P0_6.set_as_disabled();\n    }\n    #[cfg(feature = \"dangerous-reset-as-gpio\")]\n    {\n        // DANGER DANGER DANGER\n        peripherals.P1_29.set_as_disabled();\n    }\n\n    peripherals\n}\n\n// Chip specific GPIO impls\nmod gpio_impls {\n    use crate::gpio::{AnyPin, GpioPin, Pull, SealedPin};\n    use crate::impl_pin;\n    use crate::pac::common::{RW, Reg};\n    use crate::pac::gpio::vals::{Pdd, Pid};\n    use crate::pac::port::regs::Pcr;\n    use crate::pac::port::vals::{Dse, Ibe, Mux, Sre};\n\n    #[cfg(feature = \"swd-as-gpio\")]\n    impl_pin!(P0_0, 0, 0, GPIO0);\n    #[cfg(feature = \"swd-as-gpio\")]\n    impl_pin!(P0_1, 0, 1, GPIO0);\n    #[cfg(feature = \"swd-swo-as-gpio\")]\n    impl_pin!(P0_2, 0, 2, GPIO0);\n    #[cfg(feature = \"jtag-extras-as-gpio\")]\n    impl_pin!(P0_3, 0, 3, GPIO0);\n    impl_pin!(P0_4, 0, 4, GPIO0);\n    impl_pin!(P0_5, 0, 5, GPIO0);\n    #[cfg(feature = \"jtag-extras-as-gpio\")]\n    impl_pin!(P0_6, 0, 6, GPIO0);\n    impl_pin!(P0_7, 0, 7, GPIO0);\n    impl_pin!(P0_8, 0, 8, GPIO0);\n    impl_pin!(P0_9, 0, 9, GPIO0);\n    impl_pin!(P0_10, 0, 10, GPIO0);\n    impl_pin!(P0_11, 0, 11, GPIO0);\n    impl_pin!(P0_12, 0, 12, GPIO0);\n    impl_pin!(P0_13, 0, 13, GPIO0);\n    impl_pin!(P0_14, 0, 14, GPIO0);\n    impl_pin!(P0_15, 0, 15, GPIO0);\n    impl_pin!(P0_16, 0, 16, GPIO0);\n    impl_pin!(P0_17, 0, 17, GPIO0);\n    impl_pin!(P0_18, 0, 18, GPIO0);\n    impl_pin!(P0_19, 0, 19, GPIO0);\n    impl_pin!(P0_20, 0, 20, GPIO0);\n    impl_pin!(P0_21, 0, 21, GPIO0);\n    impl_pin!(P0_22, 0, 22, GPIO0);\n    impl_pin!(P0_23, 0, 23, GPIO0);\n    impl_pin!(P0_24, 0, 24, GPIO0);\n    impl_pin!(P0_25, 0, 25, GPIO0);\n    impl_pin!(P0_26, 0, 26, GPIO0);\n    impl_pin!(P0_27, 0, 27, GPIO0);\n\n    impl_pin!(P1_0, 1, 0, GPIO1);\n    impl_pin!(P1_1, 1, 1, GPIO1);\n    impl_pin!(P1_2, 1, 2, GPIO1);\n    impl_pin!(P1_3, 1, 3, GPIO1);\n    impl_pin!(P1_4, 1, 4, GPIO1);\n    impl_pin!(P1_5, 1, 5, GPIO1);\n    impl_pin!(P1_6, 1, 6, GPIO1);\n    impl_pin!(P1_7, 1, 7, GPIO1);\n    impl_pin!(P1_8, 1, 8, GPIO1);\n    impl_pin!(P1_9, 1, 9, GPIO1);\n    impl_pin!(P1_10, 1, 10, GPIO1);\n    impl_pin!(P1_11, 1, 11, GPIO1);\n    impl_pin!(P1_12, 1, 12, GPIO1);\n    impl_pin!(P1_13, 1, 13, GPIO1);\n    impl_pin!(P1_14, 1, 14, GPIO1);\n    impl_pin!(P1_15, 1, 15, GPIO1);\n    impl_pin!(P1_16, 1, 16, GPIO1);\n    impl_pin!(P1_17, 1, 17, GPIO1);\n    impl_pin!(P1_18, 1, 18, GPIO1);\n    impl_pin!(P1_19, 1, 19, GPIO1);\n    #[cfg(feature = \"dangerous-reset-as-gpio\")]\n    impl_pin!(P1_29, 1, 29, GPIO1);\n    #[cfg(feature = \"sosc-as-gpio\")]\n    impl_pin!(P1_30, 1, 30, GPIO1);\n    #[cfg(feature = \"sosc-as-gpio\")]\n    impl_pin!(P1_31, 1, 31, GPIO1);\n\n    impl_pin!(P2_0, 2, 0, GPIO2);\n    impl_pin!(P2_1, 2, 1, GPIO2);\n    impl_pin!(P2_2, 2, 2, GPIO2);\n    impl_pin!(P2_3, 2, 3, GPIO2);\n    impl_pin!(P2_4, 2, 4, GPIO2);\n    impl_pin!(P2_5, 2, 5, GPIO2);\n    impl_pin!(P2_6, 2, 6, GPIO2);\n    impl_pin!(P2_7, 2, 7, GPIO2);\n    impl_pin!(P2_8, 2, 8, GPIO2);\n    impl_pin!(P2_9, 2, 9, GPIO2);\n    impl_pin!(P2_10, 2, 10, GPIO2);\n    impl_pin!(P2_11, 2, 11, GPIO2);\n    impl_pin!(P2_12, 2, 12, GPIO2);\n    impl_pin!(P2_13, 2, 13, GPIO2);\n    impl_pin!(P2_14, 2, 14, GPIO2);\n    impl_pin!(P2_15, 2, 15, GPIO2);\n    impl_pin!(P2_16, 2, 16, GPIO2);\n    impl_pin!(P2_17, 2, 17, GPIO2);\n    impl_pin!(P2_18, 2, 18, GPIO2);\n    impl_pin!(P2_19, 2, 19, GPIO2);\n    impl_pin!(P2_20, 2, 20, GPIO2);\n    impl_pin!(P2_21, 2, 21, GPIO2);\n    impl_pin!(P2_22, 2, 22, GPIO2);\n    impl_pin!(P2_23, 2, 23, GPIO2);\n    impl_pin!(P2_24, 2, 24, GPIO2);\n    impl_pin!(P2_25, 2, 25, GPIO2);\n    impl_pin!(P2_26, 2, 26, GPIO2);\n    impl_pin!(P2_28, 2, 28, GPIO2);\n    impl_pin!(P2_29, 2, 29, GPIO2);\n    impl_pin!(P2_30, 2, 30, GPIO2);\n    impl_pin!(P2_31, 2, 31, GPIO2);\n\n    impl_pin!(P3_0, 3, 0, GPIO3);\n    impl_pin!(P3_1, 3, 1, GPIO3);\n    impl_pin!(P3_2, 3, 2, GPIO3);\n    impl_pin!(P3_3, 3, 3, GPIO3);\n    impl_pin!(P3_4, 3, 4, GPIO3);\n    impl_pin!(P3_5, 3, 5, GPIO3);\n    impl_pin!(P3_6, 3, 6, GPIO3);\n    impl_pin!(P3_7, 3, 7, GPIO3);\n    impl_pin!(P3_8, 3, 8, GPIO3);\n    impl_pin!(P3_9, 3, 9, GPIO3);\n    impl_pin!(P3_10, 3, 10, GPIO3);\n    impl_pin!(P3_11, 3, 11, GPIO3);\n    impl_pin!(P3_12, 3, 12, GPIO3);\n    impl_pin!(P3_13, 3, 13, GPIO3);\n    impl_pin!(P3_14, 3, 14, GPIO3);\n    impl_pin!(P3_15, 3, 15, GPIO3);\n    impl_pin!(P3_16, 3, 16, GPIO3);\n    impl_pin!(P3_17, 3, 17, GPIO3);\n    impl_pin!(P3_18, 3, 18, GPIO3);\n    impl_pin!(P3_19, 3, 19, GPIO3);\n    impl_pin!(P3_20, 3, 20, GPIO3);\n    impl_pin!(P3_21, 3, 21, GPIO3);\n    impl_pin!(P3_22, 3, 22, GPIO3);\n    impl_pin!(P3_23, 3, 23, GPIO3);\n    impl_pin!(P3_24, 3, 24, GPIO3);\n    impl_pin!(P3_25, 3, 25, GPIO3);\n    impl_pin!(P3_26, 3, 26, GPIO3);\n    impl_pin!(P3_27, 3, 27, GPIO3);\n    impl_pin!(P3_28, 3, 28, GPIO3);\n    impl_pin!(P3_29, 3, 29, GPIO3);\n    impl_pin!(P3_30, 3, 30, GPIO3);\n    impl_pin!(P3_31, 3, 31, GPIO3);\n\n    impl_pin!(P4_0, 4, 0, GPIO4);\n    impl_pin!(P4_1, 4, 1, GPIO4);\n    impl_pin!(P4_2, 4, 2, GPIO4);\n    impl_pin!(P4_3, 4, 3, GPIO4);\n    impl_pin!(P4_4, 4, 4, GPIO4);\n    impl_pin!(P4_5, 4, 5, GPIO4);\n    impl_pin!(P4_6, 4, 6, GPIO4);\n    impl_pin!(P4_7, 4, 7, GPIO4);\n    impl_pin!(P4_8, 4, 8, GPIO4);\n    impl_pin!(P4_9, 4, 9, GPIO4);\n    impl_pin!(P4_10, 4, 10, GPIO4);\n    impl_pin!(P4_11, 4, 11, GPIO4);\n    impl_pin!(P4_12, 4, 12, GPIO4);\n    impl_pin!(P4_13, 4, 13, GPIO4);\n\n    #[cfg(feature = \"rosc-32k-as-gpio\")]\n    impl_pin!(P5_0, 5, 0, GPIO5);\n    #[cfg(feature = \"rosc-32k-as-gpio\")]\n    impl_pin!(P5_1, 5, 1, GPIO5);\n    impl_pin!(P5_2, 5, 2, GPIO5);\n    impl_pin!(P5_3, 5, 3, GPIO5);\n    impl_pin!(P5_4, 5, 4, GPIO5);\n    impl_pin!(P5_5, 5, 5, GPIO5);\n    impl_pin!(P5_6, 5, 6, GPIO5);\n    impl_pin!(P5_7, 5, 7, GPIO5);\n    impl_pin!(P5_8, 5, 8, GPIO5);\n    impl_pin!(P5_9, 5, 9, GPIO5);\n}\n\n/// This module contains implementations of MRCC APIs, specifically of the [`Gate`] trait,\n/// for various low level peripherals.\npub(crate) mod peripheral_gating {\n    use paste::paste;\n\n    use crate::clocks::Gate;\n    use crate::clocks::periph_helpers::{\n        AdcConfig, CTimerConfig, Clk1MConfig, I3cConfig, Lpi2cConfig, LpspiConfig, LpuartConfig, NoConfig,\n        OsTimerConfig,\n    };\n    use crate::{impl_cc_gate, pac};\n\n    // These peripherals have no additional upstream clocks or configuration required\n    // other than enabling through the MRCC gate. Currently, these peripherals will\n    // ALWAYS return `Ok(0)` when calling [`enable_and_reset()`] and/or\n    // [`SPConfHelper::post_enable_config()`].\n    impl_cc_gate!(PORT0, mrcc_glb_cc1, mrcc_glb_rst1, port0, NoConfig);\n    impl_cc_gate!(PORT1, mrcc_glb_cc1, mrcc_glb_rst1, port1, NoConfig);\n    impl_cc_gate!(PORT2, mrcc_glb_cc1, mrcc_glb_rst1, port2, NoConfig);\n    impl_cc_gate!(PORT3, mrcc_glb_cc1, mrcc_glb_rst1, port3, NoConfig);\n    impl_cc_gate!(PORT4, mrcc_glb_cc1, mrcc_glb_rst1, port4, NoConfig);\n    impl_cc_gate!(PORT5, mrcc_glb_cc1, port5, NoConfig);\n\n    impl_cc_gate!(CRC0, mrcc_glb_cc0, mrcc_glb_rst0, crc0, NoConfig);\n    impl_cc_gate!(INPUTMUX0, mrcc_glb_cc0, mrcc_glb_rst0, inputmux0, NoConfig);\n\n    // These peripherals DO have meaningful configuration, and could fail if the system\n    // clocks do not match their needs.\n    impl_cc_gate!(ADC0, mrcc_glb_cc1, mrcc_glb_rst1, adc0, AdcConfig);\n    impl_cc_gate!(ADC1, mrcc_glb_cc1, mrcc_glb_rst1, adc1, AdcConfig);\n\n    impl_cc_gate!(I3C0, mrcc_glb_acc2, mrcc_glb_rst2, i3c0, I3cConfig);\n    impl_cc_gate!(I3C1, mrcc_glb_acc2, mrcc_glb_rst2, i3c1, I3cConfig);\n    impl_cc_gate!(I3C2, mrcc_glb_acc2, mrcc_glb_rst2, i3c2, I3cConfig);\n    impl_cc_gate!(I3C3, mrcc_glb_acc2, mrcc_glb_rst2, i3c3, I3cConfig);\n\n    impl_cc_gate!(CTIMER0, mrcc_glb_acc0, mrcc_glb_rst0, ctimer0, CTimerConfig);\n    impl_cc_gate!(CTIMER1, mrcc_glb_acc0, mrcc_glb_rst0, ctimer1, CTimerConfig);\n    impl_cc_gate!(CTIMER2, mrcc_glb_acc0, mrcc_glb_rst0, ctimer2, CTimerConfig);\n    impl_cc_gate!(CTIMER3, mrcc_glb_acc0, mrcc_glb_rst0, ctimer3, CTimerConfig);\n    impl_cc_gate!(CTIMER4, mrcc_glb_acc0, mrcc_glb_rst0, ctimer4, CTimerConfig);\n    impl_cc_gate!(OSTIMER0, mrcc_glb_cc0, mrcc_glb_rst0, ostimer0, OsTimerConfig);\n\n    // TRNG peripheral - uses NoConfig since it has no selectable clock source\n    impl_cc_gate!(TRNG0, mrcc_glb_acc4, mrcc_glb_rst4, trng0, NoConfig);\n\n    // Peripherals that use ACC instead of CC!\n    impl_cc_gate!(LPUART0, mrcc_glb_acc0, mrcc_glb_rst0, lpuart0, LpuartConfig);\n    impl_cc_gate!(LPUART1, mrcc_glb_acc0, mrcc_glb_rst0, lpuart1, LpuartConfig);\n    impl_cc_gate!(LPUART2, mrcc_glb_acc0, mrcc_glb_rst0, lpuart2, LpuartConfig);\n    impl_cc_gate!(LPUART3, mrcc_glb_acc0, mrcc_glb_rst0, lpuart3, LpuartConfig);\n    impl_cc_gate!(LPUART4, mrcc_glb_acc0, mrcc_glb_rst0, lpuart4, LpuartConfig);\n    impl_cc_gate!(LPUART5, mrcc_glb_acc0, mrcc_glb_rst0, lpuart5, LpuartConfig);\n\n    // DMA0 peripheral - uses NoConfig since it has no selectable clock source\n    impl_cc_gate!(DMA0, mrcc_glb_acc0, mrcc_glb_rst0, dma0, NoConfig);\n\n    impl_cc_gate!(GPIO0, mrcc_glb_acc3, mrcc_glb_rst3, gpio0, NoConfig);\n    impl_cc_gate!(GPIO1, mrcc_glb_acc3, mrcc_glb_rst3, gpio1, NoConfig);\n    impl_cc_gate!(GPIO2, mrcc_glb_acc3, mrcc_glb_rst3, gpio2, NoConfig);\n    impl_cc_gate!(GPIO3, mrcc_glb_acc3, mrcc_glb_rst3, gpio3, NoConfig);\n    impl_cc_gate!(GPIO4, mrcc_glb_acc3, mrcc_glb_rst3, gpio4, NoConfig);\n    impl_cc_gate!(GPIO5, mrcc_glb_cc3, gpio5, NoConfig);\n\n    impl_cc_gate!(LPI2C0, mrcc_glb_acc0, mrcc_glb_rst0, lpi2c0, Lpi2cConfig);\n    impl_cc_gate!(LPI2C1, mrcc_glb_acc0, mrcc_glb_rst0, lpi2c1, Lpi2cConfig);\n    impl_cc_gate!(LPI2C2, mrcc_glb_acc0, mrcc_glb_rst0, lpi2c2, Lpi2cConfig);\n    impl_cc_gate!(LPI2C3, mrcc_glb_acc0, mrcc_glb_rst0, lpi2c3, Lpi2cConfig);\n\n    impl_cc_gate!(LPSPI0, mrcc_glb_acc1, mrcc_glb_rst1, lpspi0, LpspiConfig);\n    impl_cc_gate!(LPSPI1, mrcc_glb_acc1, mrcc_glb_rst1, lpspi1, LpspiConfig);\n    impl_cc_gate!(LPSPI2, mrcc_glb_acc1, mrcc_glb_rst1, lpspi2, LpspiConfig);\n    impl_cc_gate!(LPSPI3, mrcc_glb_acc1, mrcc_glb_rst1, lpspi3, LpspiConfig);\n    impl_cc_gate!(LPSPI4, mrcc_glb_acc1, mrcc_glb_rst1, lpspi4, LpspiConfig);\n    impl_cc_gate!(LPSPI5, mrcc_glb_acc1, mrcc_glb_rst1, lpspi5, LpspiConfig);\n\n    impl_cc_gate!(WWDT0, mrcc_glb_acc0, wwdt0, Clk1MConfig);\n    impl_cc_gate!(WWDT1, mrcc_glb_acc0, wwdt1, Clk1MConfig);\n}\n\npub(crate) mod clock_limits {\n    #![allow(dead_code)]\n\n    use crate::chips::ClockLimits;\n\n    pub const VDD_CORE_MID_DRIVE_WAIT_STATE_LIMITS: &[(u32, u8)] = &[(24_000_000, 0b0000)];\n    // <= 48MHz\n    pub const VDD_CORE_MID_DRIVE_MAX_WAIT_STATES: u8 = 0b0001;\n\n    pub const VDD_CORE_NORMAL_DRIVE_WAIT_STATE_LIMITS: &[(u32, u8)] =\n        &[(30_000_000, 0b0000), (60_000_000, 0b0001), (90_000_000, 0b0010)];\n    // <= 120MHz\n    pub const VDD_CORE_NORMAL_DRIVE_MAX_WAIT_STATES: u8 = 0b0011;\n\n    pub const VDD_CORE_OVER_DRIVE_WAIT_STATE_LIMITS: &[(u32, u8)] = &[\n        (40_000_000, 0b0000),\n        (80_000_000, 0b0001),\n        (120_000_000, 0b0010),\n        (160_000_000, 0b0011),\n        (200_000_000, 0b0100),\n    ];\n    // <= 250MHz\n    pub const VDD_CORE_OVER_DRIVE_MAX_WAIT_STATES: u8 = 0b0101;\n\n    impl ClockLimits {\n        pub const MID_DRIVE: Self = Self {\n            fro_hf: 96_000_000,\n            fro_hf_div: 48_000_000,\n            pll1_clk: 100_000_000,\n            pll1_clk_div: 100_000_000,\n            main_clk: 96_000_000,\n            cpu_clk: 48_000_000,\n            // clk_16k: 16_384,\n            // clk_in: 50_000_000,\n            // clk_48m: 48_000_000,\n            // fro_12m: 12_000_000,\n            // fro_12m_div: 12_000_000,\n            // clk_1m: 1_000_000,\n            // system_clk: cpu_clk,\n            // bus_clk: cpu_clk / 2,\n            // slow_clk: cpu_clk / 6,\n        };\n\n        pub const NORMAL_DRIVE: Self = Self {\n            fro_hf: 192_000_000,\n            fro_hf_div: 192_000_000,\n            pll1_clk: 300_000_000,\n            pll1_clk_div: 150_000_000,\n            main_clk: 120_000_000,\n            cpu_clk: 120_000_000,\n            // clk_16k: 16_384,\n            // clk_in: 50_000_000,\n            // clk_48m: 48_000_000,\n            // fro_12m: 12_000_000,\n            // fro_12m_div: 12_000_000,\n            // clk_1m: 1_000_000,\n            // system_clk: cpu_clk,\n            // bus_clk: cpu_clk / 2,\n            // slow_clk: cpu_clk / 6,\n        };\n\n        pub const OVER_DRIVE: Self = Self {\n            fro_hf: 192_000_000,\n            fro_hf_div: 192_000_000,\n            pll1_clk: 400_000_000,\n            pll1_clk_div: 200_000_000,\n            main_clk: 240_000_000,\n            cpu_clk: 240_000_000,\n            // clk_16k: 16_384,\n            // clk_in: 50_000_000,\n            // clk_48m: 48_000_000,\n            // fro_12m: 12_000_000,\n            // fro_12m_div: 12_000_000,\n            // clk_1m: 1_000_000,\n            // system_clk: cpu_clk,\n            // bus_clk: cpu_clk / 2,\n            // slow_clk: cpu_clk / 6,\n        };\n    }\n}\n"
  },
  {
    "path": "embassy-mcxa/src/chips/mod.rs",
    "content": "#[cfg(feature = \"mcxa2xx\")]\npub mod mcxa2xx;\n\n#[cfg(feature = \"mcxa5xx\")]\npub mod mcxa5xx;\n\n#[cfg(feature = \"mcxa2xx\")]\npub(crate) use mcxa2xx::clock_limits;\n#[cfg(feature = \"mcxa5xx\")]\npub(crate) use mcxa5xx::clock_limits;\n\n// From Table 165 - Max Clock Frequencies (mcxa2xx)\n// From Table 375 - Max. Clock Frequency (mcxa5xx)\n#[allow(dead_code)]\npub(crate) struct ClockLimits {\n    pub(crate) fro_hf: u32,\n    pub(crate) fro_hf_div: u32,\n    pub(crate) pll1_clk: u32,\n    pub(crate) main_clk: u32,\n    pub(crate) cpu_clk: u32,\n    pub(crate) pll1_clk_div: u32,\n    // The following items are LISTED in Table 165, but are not necessary\n    // to check at runtime either because they are physically fixed, the\n    // HAL exposes no way for them to exceed their limits, or they cannot\n    // exceed their limits due to some upstream clock enforcement. They\n    // are included here as documentation.\n    //\n    // clk_16k: u32,        // fixed (16.384kHz), no need to check\n    // clk_in: u32,         // Checked already in configure_sosc method, 50MHz in all modes\n    // clk_48m: u32,        // clk_48m is fixed (to 45mhz actually)\n    // fro_12m: u32,        // We don't allow modifying from 12mhz\n    // fro_12m_div: u32,    // div can never exceed 12mhz\n    // clk_1m: u32,         // fro_12m / 12 can never exceed 12mhz\n    // system_clk: u32,     // cpu_clk == system_clk\n    // bus_clk: u32,        // bus_clk == (cpu_clk / 2), if cpu_clk is good so is bus_clk\n    // slow_clk: u32,       // slow_clk == (cpu_clk / 6), if cpu_clk is good so is slow_clock\n}\n"
  },
  {
    "path": "embassy-mcxa/src/clkout.rs",
    "content": "//! CLKOUT pseudo-peripheral\n//!\n//! CLKOUT is a part of the clock generation subsystem, and can be used\n//! either to generate arbitrary waveforms, or to debug the state of\n//! internal oscillators.\n\nuse core::marker::PhantomData;\n\nuse embassy_hal_internal::Peri;\n\nuse crate::clocks::config::VddLevel;\npub use crate::clocks::periph_helpers::Div4;\nuse crate::clocks::{ClockError, PoweredClock, WakeGuard, with_clocks};\nuse crate::gpio::{AnyPin, SealedPin};\nuse crate::pac::mrcc::vals::{ClkdivHalt, ClkdivReset, ClkdivUnstab, ClkoutClkselMux as Mux};\nuse crate::peripherals::CLKOUT;\n\n/// A peripheral representing the CLKOUT pseudo-peripheral\npub struct ClockOut<'a> {\n    _p: PhantomData<&'a mut CLKOUT>,\n    pin: Peri<'a, AnyPin>,\n    freq: u32,\n    _wg: Option<WakeGuard>,\n}\n\n/// Selected clock source to output\n#[derive(Copy, Clone)]\npub enum ClockOutSel {\n    /// 12MHz Internal Oscillator\n    Fro12M,\n    /// FRO180M/FRO192M Internal Oscillator, via divisor\n    FroHfDiv,\n    /// External Oscillator\n    #[cfg(not(feature = \"sosc-as-gpio\"))]\n    ClkIn,\n    /// 16KHz oscillator\n    #[cfg(feature = \"mcxa2xx\")]\n    Clk16K,\n    /// Either the 16K or 32K oscillator, depending on settings\n    #[cfg(feature = \"mcxa5xx\")]\n    LpOsc,\n    /// Output of PLL1\n    #[cfg(feature = \"mcxa2xx\")]\n    Pll1Clk,\n    /// Output of divided PLL1\n    #[cfg(feature = \"mcxa5xx\")]\n    Pll1ClkDiv,\n    /// Main System CPU clock, divided by 6\n    SlowClk,\n}\n\n/// Configuration for the ClockOut\n#[derive(Copy, Clone)]\npub struct Config {\n    /// Selected Source Clock\n    pub sel: ClockOutSel,\n    /// Selected division level\n    pub div: Div4,\n    /// Selected power level\n    pub level: PoweredClock,\n}\n\nimpl<'a> ClockOut<'a> {\n    /// Create a new ClockOut pin. On success, the clock signal will begin immediately\n    /// on the given pin.\n    ///\n    /// Any external pin will be placed into Disabled state upon Drop.\n    pub fn new(\n        _peri: Peri<'a, CLKOUT>,\n        pin: Peri<'a, impl sealed::ClockOutPin>,\n        cfg: Config,\n    ) -> Result<Self, ClockError> {\n        // There's no MRCC enable bit, so we check the validity of the clocks here\n        let (freq, mux, _wg) = check_sel(cfg.sel, cfg.level, cfg.div.into_divisor())?;\n\n        // All good! Apply requested config, starting with the pin.\n        pin.mux();\n\n        setup_clkout(mux, cfg.div);\n\n        Ok(Self {\n            _p: PhantomData,\n            pin: pin.into(),\n            freq: freq / cfg.div.into_divisor(),\n            _wg,\n        })\n    }\n\n    /// Unsafe constructor that ignores PoweredClock checks and discards WakeGuards\n    ///\n    /// Only intended for debugging low power clock gating, to ensure that clocks start/stop\n    /// appropriately.\n    ///\n    /// ## SAFETY\n    ///\n    /// The caller must not rely on the clock running for correctness if the provided\n    /// clock will be gated in deep sleep mode.\n    pub unsafe fn new_unchecked(\n        _peri: Peri<'a, CLKOUT>,\n        pin: Peri<'a, impl sealed::ClockOutPin>,\n        mut cfg: Config,\n    ) -> Result<Self, ClockError> {\n        // Ignore the users clock selection so it Just Works\n        cfg.level = PoweredClock::NormalEnabledDeepSleepDisabled;\n\n        // There's no MRCC enable bit, so we check the validity of the clocks here\n        let (freq, mux, _wg) = check_sel(cfg.sel, cfg.level, cfg.div.into_divisor())?;\n\n        // All good! Apply requested config, starting with the pin.\n        pin.mux();\n\n        setup_clkout(mux, cfg.div);\n\n        Ok(Self {\n            _p: PhantomData,\n            pin: pin.into(),\n            freq: freq / cfg.div.into_divisor(),\n            // No wake guards here!\n            _wg: None,\n        })\n    }\n\n    /// Frequency of the clkout pin\n    #[inline]\n    pub fn frequency(&self) -> u32 {\n        self.freq\n    }\n}\n\nimpl Drop for ClockOut<'_> {\n    fn drop(&mut self) {\n        disable_clkout();\n        self.pin.set_as_disabled();\n    }\n}\n\n/// Check whether the given clock selection is valid\nfn check_sel(sel: ClockOutSel, level: PoweredClock, divisor: u32) -> Result<(u32, Mux, Option<WakeGuard>), ClockError> {\n    let res = with_clocks(|c| {\n        #[cfg(feature = \"mcxa2xx\")]\n        let (freq, mux, fmax, expected) = {\n            let (freq, mux) = match sel {\n                ClockOutSel::Fro12M => (c.ensure_fro_hf_active(&level)?, Mux::CLKROOT_12M),\n                ClockOutSel::FroHfDiv => (c.ensure_fro_hf_div_active(&level)?, Mux::CLKROOT_FIRC_DIV),\n                #[cfg(not(feature = \"sosc-as-gpio\"))]\n                ClockOutSel::ClkIn => (c.ensure_clk_in_active(&level)?, Mux::CLKROOT_SOSC),\n                ClockOutSel::Clk16K => (c.ensure_clk_16k_vdd_core_active(&level)?, Mux::CLKROOT_16K),\n                ClockOutSel::Pll1Clk => (c.ensure_pll1_clk_active(&level)?, Mux::CLKROOT_SPLL),\n                ClockOutSel::SlowClk => (c.ensure_slow_clk_active(&level)?, Mux::CLKROOT_SLOW),\n            };\n            let expected = freq / divisor;\n            let fmax = match c.active_power {\n                VddLevel::MidDriveMode => 45_000_000,\n                VddLevel::OverDriveMode => 90_000_000,\n            };\n            (freq, mux, fmax, expected)\n        };\n        #[cfg(feature = \"mcxa5xx\")]\n        let (freq, mux, fmax, expected) = {\n            let (freq, mux) = match sel {\n                ClockOutSel::Fro12M => (c.ensure_fro_hf_active(&level)?, Mux::I0_CLKROOT_12M),\n                ClockOutSel::FroHfDiv => (c.ensure_fro_hf_div_active(&level)?, Mux::I1_CLKROOT_FIRC_DIV),\n                #[cfg(not(feature = \"sosc-as-gpio\"))]\n                ClockOutSel::ClkIn => (c.ensure_clk_in_active(&level)?, Mux::I2_CLKROOT_SOSC),\n                // TODO: we need this to be an lp_osc clock\n                ClockOutSel::LpOsc => (c.ensure_clk_32k_vdd_core_active(&level)?, Mux::I3_CLKROOT_LPOSC),\n                ClockOutSel::Pll1ClkDiv => (c.ensure_pll1_clk_div_active(&level)?, Mux::I5_CLKROOT_SPLL_DIV),\n                ClockOutSel::SlowClk => (c.ensure_slow_clk_active(&level)?, Mux::I6_CLKROOT_SLOW),\n            };\n            let expected = freq / divisor;\n            let fmax = match c.active_power {\n                VddLevel::MidDriveMode => 48_000_000,\n                VddLevel::NormalMode => 100_000_000,\n                VddLevel::OverDriveMode => 100_000_000,\n            };\n            (freq, mux, fmax, expected)\n        };\n\n        if expected > fmax {\n            Err(ClockError::BadConfig {\n                clock: \"clkout fclk\",\n                reason: \"exceeds fclk max\",\n            })\n        } else {\n            let wg = WakeGuard::for_power(&level);\n            Ok((freq, mux, wg))\n        }\n    });\n    let Some(res) = res else {\n        return Err(ClockError::NeverInitialized);\n    };\n    res\n}\n\n/// Set up the clkout pin using the given mux and div settings\nfn setup_clkout(mux: Mux, div: Div4) {\n    let mrcc = crate::pac::MRCC0;\n\n    mrcc.mrcc_clkout_clksel().write(|w| w.set_mux(mux));\n\n    // Set up clkdiv\n    mrcc.mrcc_clkout_clkdiv().write(|w| {\n        w.set_halt(ClkdivHalt::OFF);\n        w.set_reset(ClkdivReset::OFF);\n        w.set_div(div.into_bits());\n    });\n    mrcc.mrcc_clkout_clkdiv().write(|w| {\n        w.set_halt(ClkdivHalt::ON);\n        w.set_reset(ClkdivReset::ON);\n        w.set_div(div.into_bits());\n    });\n\n    while mrcc.mrcc_clkout_clkdiv().read().unstab() == ClkdivUnstab::ON {}\n}\n\n/// Stop the\nfn disable_clkout() {\n    // Stop the output by selecting the \"none\" clock\n    //\n    // TODO: restore the pin to hi-z or something?\n    let mrcc = crate::pac::MRCC0;\n    mrcc.mrcc_clkout_clkdiv().write(|w| {\n        w.set_reset(ClkdivReset::OFF);\n        w.set_halt(ClkdivHalt::OFF);\n        w.set_div(0);\n    });\n    mrcc.mrcc_clkout_clksel().write(|w| w.0 = 0b111);\n}\n\nmod sealed {\n    use embassy_hal_internal::PeripheralType;\n\n    use crate::gpio::{GpioPin, Pull, SealedPin};\n\n    /// Sealed marker trait for clockout pins\n    pub trait ClockOutPin: GpioPin + PeripheralType {\n        /// Set the given pin to the correct muxing state\n        fn mux(&self);\n    }\n\n    macro_rules! impl_pin {\n        ($pin:ident, $func:ident) => {\n            impl ClockOutPin for crate::peripherals::$pin {\n                fn mux(&self) {\n                    self.set_function(crate::pac::port::vals::Mux::$func);\n                    self.set_pull(Pull::Disabled);\n\n                    // TODO: we may want to expose these as options to allow the slew rate\n                    // and drive strength for clocks if they are particularly high speed.\n                    //\n                    // self.set_drive_strength(crate::pac::port::pcr::Dse::Dse1);\n                    // self.set_slew_rate(crate::pac::port::pcr::Sre::Sre0);\n                }\n            }\n        };\n    }\n\n    // TODO: 5xx reference manual states that P0_6 and P3_8 are clkout pins (Table 352), but the pinmux\n    // table doesn't list which alt mode corresponds with clkout (Table 340)\n    #[cfg(feature = \"mcxa2xx\")]\n    #[cfg(feature = \"jtag-extras-as-gpio\")]\n    impl_pin!(P0_6, MUX12);\n    #[cfg(feature = \"mcxa2xx\")]\n    impl_pin!(P3_8, MUX12);\n\n    impl_pin!(P3_6, MUX1);\n    impl_pin!(P4_2, MUX1);\n}\n"
  },
  {
    "path": "embassy-mcxa/src/clocks/config.rs",
    "content": "//! Clock Configuration\n//!\n//! This module holds configuration types used for the system clocks. For\n//! configuration of individual peripherals, see [`super::periph_helpers`].\n\nuse nxp_pac::scg::vals::FreqSel;\n\nuse super::PoweredClock;\n\n/// This type represents a divider in the range 1..=256.\n///\n/// At a hardware level, this is an 8-bit register from 0..=255,\n/// which adds one.\n#[derive(Copy, Clone, Debug, PartialEq, Eq)]\npub struct Div8(pub(super) u8);\n\nimpl Div8 {\n    /// Store a \"raw\" divisor value that will divide the source by\n    /// `(n + 1)`, e.g. `Div8::from_raw(0)` will divide the source\n    /// by 1, and `Div8::from_raw(255)` will divide the source by\n    /// 256.\n    pub const fn from_raw(n: u8) -> Self {\n        Self(n)\n    }\n\n    /// Divide by one, or no division\n    pub const fn no_div() -> Self {\n        Self(0)\n    }\n\n    /// Store a specific divisor value that will divide the source\n    /// by `n`. e.g. `Div8::from_divisor(1)` will divide the source\n    /// by 1, and `Div8::from_divisor(256)` will divide the source\n    /// by 256.\n    ///\n    /// Will return `None` if `n` is not in the range `1..=256`.\n    /// Consider [`Self::from_raw`] for an infallible version.\n    pub const fn from_divisor(n: u16) -> Option<Self> {\n        let Some(n) = n.checked_sub(1) else {\n            return None;\n        };\n        if n > (u8::MAX as u16) {\n            return None;\n        }\n        Some(Self(n as u8))\n    }\n\n    /// Convert into \"raw\" bits form\n    #[inline(always)]\n    pub const fn into_bits(self) -> u8 {\n        self.0\n    }\n\n    /// Convert into \"divisor\" form, as a u32 for convenient frequency math\n    #[inline(always)]\n    pub const fn into_divisor(self) -> u32 {\n        self.0 as u32 + 1\n    }\n}\n\n/// ## MCXA2xx\n///\n/// ```text\n///               ┌─────────────────────────────────────────────────────────┐\n///               │                                                         │\n///               │   ┌───────────┐  clk_out   ┌─────────┐                  │\n///    XTAL ──────┼──▷│ System    │───────────▷│         │       clk_in     │\n///               │   │  OSC      │ clkout_byp │   MUX   │──────────────────┼──────▷\n///   EXTAL ──────┼──▷│           │───────────▷│         │                  │\n///               │   └───────────┘            └─────────┘                  │\n///               │                                                         │\n///               │   ┌───────────┐ fro_hf_root  ┌────┐          fro_hf     │\n///               │   │ FRO180    ├───────┬─────▷│ CG │─────────────────────┼──────▷\n///               │   │   or      │       │      ├────┤ clk_45m or clk_48m  │\n///               │   │ FRO192    │       └─────▷│ CG │─────────────────────┼──────▷\n///               │   └───────────┘              └────┘                     │\n///               │   ┌───────────┐ fro_12m_root  ┌────┐         fro_12m    │\n///               │   │ FRO12M    │────────┬─────▷│ CG │────────────────────┼──────▷\n///               │   │           │        │      ├────┤          clk_1m    │\n///               │   │           │        └─────▷│1/12│────────────────────┼──────▷\n///               │   └───────────┘               └────┘                    │\n///               │                                                         │\n///               │                  ┌──────────┐                           │\n///               │                  │000       │                           │\n///               │      clk_in      │          │                           │\n///               │  ───────────────▷│001       │                           │\n///               │      fro_12m     │          │                           │\n///               │  ───────────────▷│010       │                           │\n///               │    fro_hf_root   │          │                           │\n///               │  ───────────────▷│011       │              main_clk     │\n///               │                  │          │───────────────────────────┼──────▷\n/// clk_16k ──────┼─────────────────▷│100       │                           │\n///               │       none       │          │                           │\n///               │  ───────────────▷│101       │                           │\n///               │     pll1_clk     │          │                           │\n///               │  ───────────────▷│110       │                           │\n///               │       none       │          │                           │\n///               │  ───────────────▷│111       │                           │\n///               │                  └──────────┘                           │\n///               │                        ▲                                │\n///               │                        │                                │\n///               │                     SCG SCS                             │\n///               │ SCG-Lite                                                │\n///               └─────────────────────────────────────────────────────────┘\n///\n///\n///                      clk_in      ┌─────┐\n///                  ───────────────▷│00   │\n///                      clk_45m     │     │\n///                  ───────────────▷│01   │      ┌───────────┐   pll1_clk\n///                       none       │     │─────▷│   SPLL    │───────────────▷\n///                  ───────────────▷│10   │      └───────────┘\n///                      fro_12m     │     │\n///                  ───────────────▷│11   │\n///                                  └─────┘\n/// ```\n#[non_exhaustive]\npub struct ClocksConfig {\n    /// Power states of VDD Core\n    pub vdd_power: VddPowerConfig,\n    /// Clocks that are used to drive the main clock, including the AHB and CPU core\n    pub main_clock: MainClockConfig,\n    /// FIRC\n    ///\n    /// * On MCXA2xx: FRO180, 45/60/90/180M clock source\n    /// * On MCXA5xx: FRO192, 48/64/96/196M clock source\n    pub firc: Option<FircConfig>,\n    /// SIRC, FRO12M, clk_12m clock source\n    pub sirc: SircConfig,\n    /// FRO16K clock source\n    pub fro16k: Option<Fro16KConfig>,\n    /// OSC32K clock source\n    #[cfg(all(feature = \"mcxa5xx\", feature = \"unstable-osc32k\", not(feature = \"rosc-32k-as-gpio\")))]\n    pub osc32k: Option<Osc32KConfig>,\n    /// SOSC, clk_in clock source\n    ///\n    /// NOTE: Requires `sosc-as-gpio` feature disabled, which also disables GPIO access to P1_30 and P1_31\n    #[cfg(not(feature = \"sosc-as-gpio\"))]\n    pub sosc: Option<SoscConfig>,\n    /// SPLL\n    pub spll: Option<SpllConfig>,\n}\n\n// Power (which is not a clock)\n\n/// Selected VDD Power Mode\n#[derive(Copy, Clone, PartialEq, Debug, Default)]\n#[non_exhaustive]\npub enum VddLevel {\n    /// Standard \"mid drive\" \"MD\" power, 1.0v VDD Core\n    #[default]\n    MidDriveMode,\n\n    /// \"Normal\" voltage, 1.1v VDD Core\n    #[cfg(feature = \"mcxa5xx\")]\n    NormalMode,\n\n    /// Overdrive \"OD\" power, 1.2v VDD Core\n    OverDriveMode,\n}\n\n#[derive(Copy, Clone, PartialEq)]\n#[non_exhaustive]\npub enum VddDriveStrength {\n    /// Low drive\n    Low { enable_bandgap: bool },\n\n    /// Normal drive\n    Normal,\n}\n\n#[derive(Copy, Clone)]\n#[non_exhaustive]\npub struct VddModeConfig {\n    /// VDD_CORE/LDO_CORE voltage level\n    pub level: VddLevel,\n    /// VDD_CORE/LDO_CORE drive strength\n    pub drive: VddDriveStrength,\n}\n\n/// Settings for gating power to on-chip flash\n///\n/// Applies to both \"light\" WFE sleep, as well as Deep Sleep. Requires that\n///\n/// ## FlashDoze\n///\n/// Disables flash memory accesses and places flash memory in Low-Power state whenever the core clock\n/// is gated (CKMODE > 0) because of execution of WFI, WFE, or SLEEPONEXIT. Other bus masters that\n/// attempt to access the flash memory stalls until the core is no longer sleeping.\n///\n/// # FlashWake\n///\n/// Specifies that when this field becomes 1, an attempt to read the flash memory when it is in Low-Power\n/// state because of FLASHCR\\[FLASHDIS\\] or FLASHCR\\[FLASHDOZE\\], causes the flash memory to exit\n/// Low-Power state for the duration of the flash memory access.\n#[derive(Copy, Clone, Default)]\n#[non_exhaustive]\npub enum FlashSleep {\n    /// Don't ever set the flash to sleep\n    #[default]\n    Never,\n    /// Set FlashDoze\n    ///\n    /// This setting is only effective if [CoreSleep] has been configured with at least\n    /// the `WfeGated` option or deeper.\n    FlashDoze,\n    /// Set FlashDoze + FlashWake\n    ///\n    /// This setting is only effective if [CoreSleep] has been configured with at least\n    /// the `WfeGated` option or deeper.\n    //\n    // TODO: This *might* be required for DMA out of flash to actually work when\n    // the core is sleeping, otherwise DMA will stall? Needs to be confirmed.\n    FlashDozeWithFlashWake,\n}\n\n/// Maximum sleep depth for the CPU core\n#[derive(Copy, Clone, Default, Debug)]\n#[non_exhaustive]\npub enum CoreSleep {\n    /// System will sleep using WFE when idle, but the CPU clock domain will not ever\n    /// be gated. This mode uses the most power, but allows for debugging to\n    /// continue uninterrupted.\n    ///\n    /// With this setting, the system never leaves the \"Active\" configuration mode.\n    #[default]\n    WfeUngated,\n    /// The system will sleep using WFE when idle, and the CPU clock domain will be\n    /// be gated. If configured with [FlashSleep], the internal flash may be gated\n    /// as well.\n    ///\n    /// ## WARNING\n    ///\n    /// Enabling this mode has potential danger to soft-lock the system!\n    ///\n    /// * This mode WILL detach the debugging/RTT/defmt session if active upon first sleep.\n    /// * This mode WILL also require ISP mode recovery in order to re-flash if the core becomes\n    ///   \"stuck\" in sleep.\n    WfeGated,\n    /// The system will go to deep sleep when idle, and the CPU clock domain will be\n    /// be gated. If configured with [FlashSleep], the internal flash may be gated\n    /// as well.\n    ///\n    /// This will also move the system into the \"low power\" state, which will disable any\n    /// clocks not configured as `PoweredClock::AlwaysActive\".\n    ///\n    /// ## TODO\n    ///\n    /// For now, this REQUIRES calling unsafe `okay_but_actually_enable_deep_sleep()`\n    /// otherwise we'd ALWAYS go to deep sleep on every WFE. We need to implement a\n    /// custom executor that does proper go-to-deepsleep and come-back-from-deepsleep\n    /// before un-chickening this. If the method isn't called, we just set to `WfeGated`\n    /// instead.\n    ///\n    /// ## WARNING\n    ///\n    /// Enabling this mode has potential danger to soft-lock the system!\n    ///\n    /// * This mode WILL detach the debugging/RTT/defmt session if active upon first sleep.\n    /// * This mode WILL also require ISP mode recovery in order to re-flash if the core becomes\n    ///   \"stuck\" in sleep.\n    DeepSleep,\n}\n\n/// Power control options for the VDD domain, including the CPU and flash memory\n#[derive(Copy, Clone)]\n#[non_exhaustive]\npub struct VddPowerConfig {\n    /// Active power mode, used when not in Deep Sleep\n    pub active_mode: VddModeConfig,\n    /// Low power mode, used when in Deep Sleep\n    pub low_power_mode: VddModeConfig,\n    /// CPU core clock gating settings\n    pub core_sleep: CoreSleep,\n    /// Internal flash clock gating settings\n    pub flash_sleep: FlashSleep,\n}\n\n// Main Clock\n\n/// Main clock source\n#[derive(Copy, Clone)]\npub enum MainClockSource {\n    /// Clock derived from `clk_in`, via the external oscillator (8-50MHz)\n    ///\n    /// NOTE: Requires `sosc-as-gpio` feature disabled, which also disables GPIO access to P1_30 and P1_31\n    #[cfg(not(feature = \"sosc-as-gpio\"))]\n    SoscClkIn,\n    /// Clock derived from `fro_12m`, via the internal 12MHz oscillator (12MHz)\n    SircFro12M,\n    /// Clock derived from `fro_hf_root`, via the internal 45/60/90/180M clock source (45-180MHz)\n    FircHfRoot,\n    /// Clock derived from `clk_16k` (vdd core)\n    #[cfg(feature = \"mcxa2xx\")]\n    RoscFro16K,\n    /// Clock derived from `clk_32k` (vdd core)\n    #[cfg(all(feature = \"mcxa5xx\", not(feature = \"rosc-32k-as-gpio\")))]\n    RoscOsc32K,\n    /// Clock derived from `pll1_clk`, via the internal PLL\n    SPll1,\n}\n\n#[derive(Copy, Clone)]\npub struct MainClockConfig {\n    /// Selected clock source\n    pub source: MainClockSource,\n    /// Power state of the main clock\n    pub power: PoweredClock,\n    /// AHB Clock Divider\n    pub ahb_clk_div: Div8,\n}\n\n// SOSC\n\n/// The mode of the external reference clock\n#[derive(Copy, Clone)]\npub enum SoscMode {\n    /// Passive crystal oscillators\n    CrystalOscillator,\n    /// Active external reference clock\n    ActiveClock,\n}\n\n/// SOSC/clk_in configuration\n#[derive(Copy, Clone)]\npub struct SoscConfig {\n    /// Mode of the external reference clock\n    pub mode: SoscMode,\n    /// Specific frequency of the external reference clock\n    pub frequency: u32,\n    /// Power state of the external reference clock\n    pub power: PoweredClock,\n}\n\n// SPLL\n\n/// PLL1/SPLL configuration\npub struct SpllConfig {\n    /// Input clock source for the PLL1/SPLL\n    pub source: SpllSource,\n    /// Mode of operation for the PLL1/SPLL\n    pub mode: SpllMode,\n    /// Power state of the SPLL\n    pub power: PoweredClock,\n    /// Is the \"pll1_clk_div\" clock enabled?\n    pub pll1_clk_div: Option<Div8>,\n}\n\n/// Input clock source for the PLL1/SPLL\npub enum SpllSource {\n    /// External Oscillator (8-50MHz)\n    #[cfg(not(feature = \"sosc-as-gpio\"))]\n    Sosc,\n    /// Fast Internal Oscillator\n    ///\n    /// * MCXA2xx NOTE: Figure 69 says \"firc_45mhz\"/\"clk_45m\", not \"fro_hf_gated\",\n    ///   so this is always 45MHz.\n    /// * MCXA5xx NOTE: Figure 116 says \"clk_48m\", so this is always 48MHz.\n    Firc,\n    /// S Internal Oscillator (12M)\n    Sirc,\n    // TODO: the reference manual hints that ROSC is possible,\n    // however the minimum input frequency is 32K, but ROSC is 16K.\n    // Some diagrams show this option, and some diagrams omit it.\n    // SVD shows it as \"reserved\".\n    //\n    // TODO(AJM): Re-enable for MCXA5xx\n    //\n    // /// Realtime Internal Oscillator (16K Osc)\n    // Rosc,\n}\n\n/// Mode of operation for the SPLL/PLL1\n///\n/// NOTE: Currently, only \"Mode 1\" normal operational modes are implemented,\n/// as described in the Reference Manual.\n#[non_exhaustive]\npub enum SpllMode {\n    /// Mode 1a does not use the Pre/Post dividers.\n    ///\n    /// `Fout = m_mult x SpllSource`\n    ///\n    /// Both of the following constraints must be met:\n    ///\n    /// * Fout: 275MHz to 550MHz\n    /// * Fout: 4.3MHz to 2x Max CPU Frequency\n    Mode1a {\n        /// PLL Multiplier. Must be in the range 1..=65535.\n        m_mult: u16,\n    },\n\n    /// Mode 1b does not use the Pre-divider.\n    ///\n    /// * `if !bypass_p2_div: Fout = (M / (2 x P)) x Fin`\n    /// * `if  bypass_p2_div: Fout = (M /    P   ) x Fin`\n    ///\n    /// Both of the following constraints must be met:\n    ///\n    /// * Fcco: 275MHz to 550MHz\n    ///   * `Fcco = m_mult x SpllSource`\n    /// * Fout: 4.3MHz to 2x Max CPU Frequency\n    Mode1b {\n        /// PLL Multiplier. `m_mult` must be in the range 1..=65535.\n        m_mult: u16,\n        /// Post Divider. `p_div` must be in the range 1..=31.\n        p_div: u8,\n        /// Bonus post divider\n        bypass_p2_div: bool,\n    },\n\n    /// Mode 1c does use the Pre-divider, but does not use the Post-divider\n    ///\n    /// `Fout = (M / N) x Fin`\n    ///\n    /// Both of the following constraints must be met:\n    ///\n    /// * Fout: 275MHz to 550MHz\n    /// * Fout: 4.3MHz to 2x Max CPU Frequency\n    Mode1c {\n        /// PLL Multiplier. `m_mult` must be in the range 1..=65535.\n        m_mult: u16,\n        /// Pre Divider. `n_div` must be in the range 1..=255.\n        n_div: u8,\n    },\n\n    /// Mode 1b uses both the Pre and Post dividers.\n    ///\n    /// * `if !bypass_p2_div: Fout = (M / (N x 2 x P)) x Fin`\n    /// * `if  bypass_p2_div: Fout = (M / (  N x P  )) x Fin`\n    ///\n    /// Both of the following constraints must be met:\n    ///\n    /// * Fcco: 275MHz to 550MHz\n    ///   * `Fcco = (m_mult x SpllSource) / (n_div x p_div (x 2))`\n    /// * Fout: 4.3MHz to 2x Max CPU Frequency\n    Mode1d {\n        /// PLL Multiplier. `m_mult` must be in the range 1..=65535.\n        m_mult: u16,\n        /// Pre Divider. `n_div` must be in the range 1..=255.\n        n_div: u8,\n        /// Post Divider. `p_div` must be in the range 1..=31.\n        p_div: u8,\n        /// Bonus post divider\n        bypass_p2_div: bool,\n    },\n}\n\n// FIRC/FRO180M\n\n/// ```text\n/// ┌───────────┐ fro_hf_root  ┌────┐   fro_hf\n/// │ FRO180M   ├───────┬─────▷│GATE│──────────▷\n/// │   or      │       │      ├────┤  clk_45m/clk_48m\n/// │ FRO192M   │       └─────▷│GATE│──────────▷\n/// └───────────┘              └────┘\n/// ```\n#[non_exhaustive]\npub struct FircConfig {\n    /// Selected clock frequency\n    pub frequency: FircFreqSel,\n    /// Selected power state of the clock\n    pub power: PoweredClock,\n    /// Is the \"fro_hf\" gated clock enabled?\n    pub fro_hf_enabled: bool,\n    /// Is the \"clk_45m\"/\"clk_48m\" gated clock enabled?\n    pub clk_hf_fundamental_enabled: bool,\n    /// Is the \"fro_hf_div\" clock enabled? Requires `fro_hf`!\n    pub fro_hf_div: Option<Div8>,\n}\n\n/// Selected FIRC frequency\n#[cfg(feature = \"mcxa2xx\")]\n#[derive(Debug, PartialEq)]\npub enum FircFreqSel {\n    /// 45MHz Output\n    Mhz45,\n    /// 60MHz Output\n    Mhz60,\n    /// 90MHz Output\n    Mhz90,\n    /// 180MHz Output\n    Mhz180,\n}\n\n/// Selected FIRC frequency\n#[cfg(feature = \"mcxa5xx\")]\n#[derive(Debug, PartialEq)]\npub enum FircFreqSel {\n    /// 48MHz Output\n    Mhz48,\n    /// 64MHz Output\n    Mhz64,\n    /// 96MHz Output\n    Mhz96,\n    /// 192MHz Output\n    Mhz192,\n}\n\nimpl FircFreqSel {\n    #[cfg(feature = \"mcxa2xx\")]\n    pub(crate) fn to_freq_and_sel(&self) -> (u32, FreqSel) {\n        // NOTE: the SVD currently has the wrong(?) values for these:\n        // 45 -> 48\n        // 60 -> 64\n        // 90 -> 96\n        // 180 -> 192\n        //\n        // Probably correct-ish, but for a different trim value?\n        match self {\n            FircFreqSel::Mhz45 => {\n                // We are default, there's nothing to do here.\n                (45_000_000, FreqSel::FIRC_48MHZ_192S)\n            }\n            FircFreqSel::Mhz60 => (60_000_000, FreqSel::FIRC_64MHZ),\n            FircFreqSel::Mhz90 => (90_000_000, FreqSel::FIRC_96MHZ),\n            FircFreqSel::Mhz180 => (180_000_000, FreqSel::FIRC_192MHZ),\n        }\n    }\n\n    #[cfg(feature = \"mcxa5xx\")]\n    pub(crate) fn to_freq_and_sel(&self) -> (u32, FreqSel) {\n        match self {\n            FircFreqSel::Mhz48 => {\n                // We are default, there's nothing to do here.\n                (48_000_000, FreqSel::FIRC_48MHZ_192S)\n            }\n            FircFreqSel::Mhz64 => (64_000_000, FreqSel::FIRC_64MHZ),\n            FircFreqSel::Mhz96 => (96_000_000, FreqSel::FIRC_96MHZ),\n            FircFreqSel::Mhz192 => (192_000_000, FreqSel::FIRC_192MHZ),\n        }\n    }\n}\n\n// SIRC/FRO12M\n\n/// ```text\n/// ┌───────────┐ fro_12m_root  ┌────┐ fro_12m\n/// │ FRO12M    │────────┬─────▷│ CG │──────────▷\n/// │           │        │      ├────┤  clk_1m\n/// │           │        └─────▷│1/12│──────────▷\n/// └───────────┘               └────┘\n/// ```\n#[non_exhaustive]\npub struct SircConfig {\n    pub power: PoweredClock,\n    // peripheral output, aka sirc_12mhz\n    pub fro_12m_enabled: bool,\n    /// Is the \"fro_lf_div\" clock enabled? Requires `fro_12m`!\n    pub fro_lf_div: Option<Div8>,\n}\n\n/// FRO16K Configuration items\n#[non_exhaustive]\npub struct Fro16KConfig {\n    /// is `clk_16k[0]` active?\n    pub vsys_domain_active: bool,\n    /// is `clk_16k[1]` active?\n    pub vdd_core_domain_active: bool,\n    /// is `clk_16k[2]` active?\n    #[cfg(feature = \"mcxa5xx\")]\n    pub vbat_domain_active: bool,\n}\n\nimpl Default for Fro16KConfig {\n    fn default() -> Self {\n        Self {\n            vsys_domain_active: true,\n            vdd_core_domain_active: true,\n            #[cfg(feature = \"mcxa5xx\")]\n            vbat_domain_active: true,\n        }\n    }\n}\n\n/// OSC32K Operational Mode\n#[cfg(all(feature = \"mcxa5xx\", not(feature = \"rosc-32k-as-gpio\")))]\npub enum Osc32KMode {\n    ///  low power switched oscillator mode\n    LowPower {\n        /// 32K Oscillator internal transconductance gain current\n        coarse_amp_gain: Osc32KCoarseGain,\n        /// Enable if Vbat exceeds 3.0v\n        vbat_exceeds_3v0: bool,\n    },\n    /// high performance transconductance oscillator mode\n    HighPower {\n        /// 32K Oscillator internal transconductance gain current\n        coarse_amp_gain: Osc32KCoarseGain,\n        /// Configurable capacitance for XTAL pad\n        xtal_cap_sel: Osc32KCapSel,\n        /// Configurable capacitance for EXTAL pad\n        extal_cap_sel: Osc32KCapSel,\n    },\n}\n\n/// Coarse Gain Amplification\n///\n/// See datasheet table 4.2.1.4, \"32 kHz oscillation gain setting\"\n#[cfg(all(feature = \"mcxa5xx\", not(feature = \"rosc-32k-as-gpio\")))]\npub enum Osc32KCoarseGain {\n    /// Max ESR 50kOhms, Max Cx 14pF\n    EsrRange0,\n    /// Max ESR 70kOhms, Max Cx 22pF\n    EsrRange1,\n    /// Max ESR 80kOhms, Max Cx 22pF\n    EsrRange2,\n    /// Max ESR 100kOhms, Max Cx 20pF\n    EsrRange3,\n}\n\n#[cfg(all(feature = \"mcxa5xx\", not(feature = \"rosc-32k-as-gpio\")))]\npub enum Osc32KCapSel {\n    // NOTE: 0pF is not supported in non-low-power-modes\n    /// 2pF\n    Cap2PicoF,\n    /// 4pF\n    Cap4PicoF,\n    /// 6pF\n    Cap6PicoF,\n    /// 8pF\n    Cap8PicoF,\n    /// 10pF\n    Cap10PicoF,\n    /// 12pF\n    Cap12PicoF,\n    /// 14pF\n    Cap14PicoF,\n    /// 16pF\n    Cap16PicoF,\n    /// 18pF\n    Cap18PicoF,\n    /// 20pF\n    Cap20PicoF,\n    /// 22pF\n    Cap22PicoF,\n    /// 24pF\n    Cap24PicoF,\n    /// 26pF\n    Cap26PicoF,\n    /// 28pF\n    Cap28PicoF,\n    /// 30pF\n    Cap30PicoF,\n}\n\n/// OSC32K Configuration Items\n#[cfg(all(feature = \"mcxa5xx\", not(feature = \"rosc-32k-as-gpio\")))]\n#[non_exhaustive]\npub struct Osc32KConfig {\n    /// Low/High Power Mode Selection\n    pub mode: Osc32KMode,\n    /// is `clk_32k[0]` active?\n    pub vsys_domain_active: bool,\n    /// is `clk_32k[1]` active?\n    pub vdd_core_domain_active: bool,\n    /// is `clk_32k[2]` active?\n    pub vbat_domain_active: bool,\n}\n\n#[cfg(all(feature = \"mcxa5xx\", not(feature = \"rosc-32k-as-gpio\")))]\nimpl Default for Osc32KConfig {\n    fn default() -> Self {\n        Self {\n            mode: Osc32KMode::LowPower {\n                coarse_amp_gain: Osc32KCoarseGain::EsrRange0,\n                vbat_exceeds_3v0: true,\n            },\n            vsys_domain_active: true,\n            vdd_core_domain_active: true,\n            vbat_domain_active: true,\n        }\n    }\n}\n\nimpl Default for FircConfig {\n    fn default() -> Self {\n        FircConfig {\n            #[cfg(feature = \"mcxa2xx\")]\n            frequency: FircFreqSel::Mhz45,\n            #[cfg(feature = \"mcxa5xx\")]\n            frequency: FircFreqSel::Mhz48,\n            power: PoweredClock::NormalEnabledDeepSleepDisabled,\n            fro_hf_enabled: true,\n            clk_hf_fundamental_enabled: true,\n            fro_hf_div: None,\n        }\n    }\n}\n\nimpl Default for ClocksConfig {\n    fn default() -> Self {\n        Self {\n            vdd_power: VddPowerConfig {\n                active_mode: VddModeConfig {\n                    level: VddLevel::MidDriveMode,\n                    drive: VddDriveStrength::Normal,\n                },\n                low_power_mode: VddModeConfig {\n                    level: VddLevel::MidDriveMode,\n                    drive: VddDriveStrength::Normal,\n                },\n                core_sleep: CoreSleep::WfeUngated,\n                flash_sleep: FlashSleep::Never,\n            },\n            main_clock: MainClockConfig {\n                source: MainClockSource::FircHfRoot,\n                power: PoweredClock::NormalEnabledDeepSleepDisabled,\n                ahb_clk_div: Div8::no_div(),\n            },\n            firc: Some(FircConfig {\n                #[cfg(feature = \"mcxa2xx\")]\n                frequency: FircFreqSel::Mhz45,\n                #[cfg(feature = \"mcxa5xx\")]\n                frequency: FircFreqSel::Mhz48,\n                power: PoweredClock::NormalEnabledDeepSleepDisabled,\n                fro_hf_enabled: true,\n                clk_hf_fundamental_enabled: true,\n                fro_hf_div: None,\n            }),\n            sirc: SircConfig {\n                power: PoweredClock::AlwaysEnabled,\n                fro_12m_enabled: true,\n                fro_lf_div: None,\n            },\n            fro16k: Some(Fro16KConfig {\n                vsys_domain_active: true,\n                vdd_core_domain_active: true,\n                #[cfg(feature = \"mcxa5xx\")]\n                vbat_domain_active: true,\n            }),\n            #[cfg(all(feature = \"mcxa5xx\", feature = \"unstable-osc32k\", not(feature = \"rosc-32k-as-gpio\")))]\n            osc32k: None,\n            #[cfg(not(feature = \"sosc-as-gpio\"))]\n            sosc: None,\n            spll: None,\n        }\n    }\n}\n"
  },
  {
    "path": "embassy-mcxa/src/clocks/gate.rs",
    "content": "//! Clock gate trait and free functions for enabling/disabling peripheral clocks.\n\nuse super::CLOCKS;\nuse super::periph_helpers::{PreEnableParts, SPConfHelper};\nuse super::types::ClockError;\n\n/// Trait describing an AHB clock gate that can be toggled through MRCC.\npub trait Gate {\n    type MrccPeriphConfig: SPConfHelper;\n\n    /// Enable the clock gate.\n    ///\n    /// # SAFETY\n    ///\n    /// The current peripheral must be disabled prior to calling this method\n    unsafe fn enable_clock();\n\n    /// Disable the clock gate.\n    ///\n    /// # SAFETY\n    ///\n    /// There must be no active user of this peripheral when calling this method\n    unsafe fn disable_clock();\n\n    /// Drive the peripheral into reset.\n    ///\n    /// # SAFETY\n    ///\n    /// There must be no active user of this peripheral when calling this method\n    unsafe fn assert_reset();\n\n    /// Drive the peripheral out of reset.\n    ///\n    /// # SAFETY\n    ///\n    /// There must be no active user of this peripheral when calling this method\n    unsafe fn release_reset();\n\n    /// Return whether the clock gate for this peripheral is currently enabled.\n    fn is_clock_enabled() -> bool;\n\n    /// Return whether the peripheral is currently held in reset.\n    fn is_reset_released() -> bool;\n}\n\n/// This is the primary helper method HAL drivers are expected to call when creating\n/// an instance of the peripheral.\n///\n/// This method:\n///\n/// 1. Enables the MRCC clock gate for this peripheral\n/// 2. Calls the `G::MrccPeriphConfig::post_enable_config()` method, returning an error\n///    and re-disabling the peripheral if this fails.\n/// 3. Pulses the MRCC reset line, to reset the peripheral to the default state\n/// 4. Returns the frequency, in Hz that is fed into the peripheral, taking into account\n///    the selected upstream clock, as well as any division specified by `cfg`.\n///\n/// NOTE: if a clock is disabled, sourced from an \"ambient\" clock source, this method\n/// may return `Ok(0)`. In the future, this might be updated to return the correct\n/// \"ambient\" clock, e.g. the AHB/APB frequency.\n///\n/// # SAFETY\n///\n/// This peripheral must not yet be in use prior to calling `enable_and_reset`.\n#[inline]\npub unsafe fn enable_and_reset<G: Gate>(cfg: &G::MrccPeriphConfig) -> Result<PreEnableParts, ClockError> {\n    unsafe {\n        let freq = enable::<G>(cfg)?;\n        pulse_reset::<G>();\n        Ok(freq)\n    }\n}\n\n/// Enable the clock gate for the given peripheral.\n///\n/// Prefer [`enable_and_reset`] unless you are specifically avoiding a pulse of the reset, or need\n/// to control the duration of the pulse more directly.\n///\n/// If an `Err` is returned, the given clock is guaranteed to be disabled.\n///\n/// # SAFETY\n///\n/// This peripheral must not yet be in use prior to calling `enable`.\n#[inline]\npub unsafe fn enable<G: Gate>(cfg: &G::MrccPeriphConfig) -> Result<PreEnableParts, ClockError> {\n    unsafe {\n        // Instead of checking, just disable the clock if it is currently enabled.\n        G::disable_clock();\n\n        let freq = critical_section::with(|cs| {\n            let clocks = CLOCKS.borrow_ref(cs);\n            let clocks = clocks.as_ref().ok_or(ClockError::NeverInitialized)?;\n            cfg.pre_enable_config(clocks)\n        })?;\n\n        G::enable_clock();\n        while !G::is_clock_enabled() {}\n        core::arch::asm!(\"dsb sy; isb sy\", options(nomem, nostack, preserves_flags));\n\n        Ok(freq)\n    }\n}\n\n/// Disable the clock gate for the given peripheral.\n///\n/// # SAFETY\n///\n/// This peripheral must no longer be in use prior to calling `enable`.\n#[allow(dead_code)]\n#[inline]\npub unsafe fn disable<G: Gate>() {\n    unsafe {\n        G::disable_clock();\n    }\n}\n\n/// Check whether a gate is currently enabled.\n#[allow(dead_code)]\n#[inline]\npub fn is_clock_enabled<G: Gate>() -> bool {\n    G::is_clock_enabled()\n}\n\n/// Release a reset line for the given peripheral set.\n///\n/// Prefer [`enable_and_reset`].\n///\n/// # SAFETY\n///\n/// This peripheral must not yet be in use prior to calling `release_reset`.\n#[inline]\npub unsafe fn release_reset<G: Gate>() {\n    unsafe {\n        G::release_reset();\n    }\n}\n\n/// Assert a reset line for the given peripheral set.\n///\n/// Prefer [`enable_and_reset`].\n///\n/// # SAFETY\n///\n/// This peripheral must not yet be in use prior to calling `assert_reset`.\n#[inline]\npub unsafe fn assert_reset<G: Gate>() {\n    unsafe {\n        G::assert_reset();\n    }\n}\n\n/// Check whether the peripheral is held in reset.\n///\n/// # Safety\n///\n/// Must be called with a valid peripheral gate type.\n#[inline]\npub unsafe fn is_reset_released<G: Gate>() -> bool {\n    G::is_reset_released()\n}\n\n/// Pulse a reset line (assert then release) with a short delay.\n///\n/// Prefer [`enable_and_reset`].\n///\n/// # SAFETY\n///\n/// This peripheral must not yet be in use prior to calling `release_reset`.\n#[inline]\npub unsafe fn pulse_reset<G: Gate>() {\n    unsafe {\n        G::assert_reset();\n        cortex_m::asm::nop();\n        cortex_m::asm::nop();\n        G::release_reset();\n    }\n}\n\n//\n// Macros/macro impls\n//\n\n/// This macro is used to implement the [`Gate`] trait for a given peripheral\n/// that is controlled by the MRCC peripheral.\n#[doc(hidden)]\n#[macro_export]\nmacro_rules! impl_cc_gate {\n    ($name:ident, $clk_reg:ident, $field:ident, $config:ty) => {\n        impl Gate for $crate::peripherals::$name {\n            type MrccPeriphConfig = $config;\n\n            paste! {\n                #[inline]\n                unsafe fn enable_clock() {\n                    pac::MRCC0.$clk_reg().modify(|w| w.[<set_ $field>](true));\n                }\n\n                #[inline]\n                unsafe fn disable_clock() {\n                    pac::MRCC0.$clk_reg().modify(|w| w.[<set_ $field>](false));\n                }\n\n                #[inline]\n                unsafe fn release_reset() {}\n\n                #[inline]\n                unsafe fn assert_reset() {}\n            }\n\n            #[inline]\n            fn is_clock_enabled() -> bool {\n                pac::MRCC0.$clk_reg().read().$field()\n            }\n\n            #[inline]\n            fn is_reset_released() -> bool {\n                false\n            }\n        }\n    };\n\n    ($name:ident, $clk_reg:ident, $rst_reg:ident, $field:ident, $config:ty) => {\n        impl Gate for $crate::peripherals::$name {\n            type MrccPeriphConfig = $config;\n\n            paste! {\n                #[inline]\n                unsafe fn enable_clock() {\n                    pac::MRCC0.$clk_reg().modify(|w| w.[<set_ $field>](true));\n                }\n\n                #[inline]\n                unsafe fn disable_clock() {\n                    pac::MRCC0.$clk_reg().modify(|w| w.[<set_ $field>](false));\n                }\n\n                #[inline]\n                unsafe fn release_reset() {\n                    pac::MRCC0.$rst_reg().modify(|w| w.[<set_ $field>](true));\n                    // Wait for reset to set\n                    while !pac::MRCC0.$rst_reg().read().[<$field>]() {}\n                }\n\n                #[inline]\n                unsafe fn assert_reset() {\n                    pac::MRCC0.$rst_reg().modify(|w| w.[<set_ $field>](false));\n                    // Wait for reset to clear\n                    while pac::MRCC0.$rst_reg().read().[<$field>]() {}\n                }\n            }\n\n            #[inline]\n            fn is_clock_enabled() -> bool {\n                pac::MRCC0.$clk_reg().read().$field()\n            }\n\n            #[inline]\n            fn is_reset_released() -> bool {\n                pac::MRCC0.$rst_reg().read().$field()\n            }\n        }\n    };\n}\n"
  },
  {
    "path": "embassy-mcxa/src/clocks/mod.rs",
    "content": "//! # Clock Module\n//!\n//! For the MCX-A, we separate clock and peripheral control into two main stages:\n//!\n//! 1. At startup, e.g. when `embassy_mcxa::init()` is called, we configure the\n//!    core system clocks, including external and internal oscillators. This\n//!    configuration is then largely static for the duration of the program.\n//! 2. When HAL drivers are created, e.g. `Lpuart::new()` is called, the driver\n//!    is responsible for two main things:\n//!     * Ensuring that any required \"upstream\" core system clocks necessary for\n//!       clocking the peripheral is active and configured to a reasonable value\n//!     * Enabling the clock gates for that peripheral, and resetting the peripheral\n//!\n//! From a user perspective, only step 1 is visible. Step 2 is automatically handled\n//! by HAL drivers, using interfaces defined in this module.\n//!\n//! It is also possible to *view* the state of the clock configuration after [`init()`]\n//! has been called, using the [`with_clocks()`] function, which provides a view of the\n//! [`Clocks`] structure.\n//!\n//! ## For HAL driver implementors\n//!\n//! The majority of peripherals in the MCXA chip are fed from either a \"hard-coded\" or\n//! configurable clock source, e.g. selecting the FROM12M or `clk_1m` as a source. This\n//! selection, as well as often any pre-scaler division from that source clock, is made\n//! through MRCC registers.\n//!\n//! Any peripheral that is controlled through the MRCC register can automatically implement\n//! the necessary APIs using the `impl_cc_gate!` macro in this module. You will also need\n//! to define the configuration surface and steps necessary to fully configure that peripheral\n//! from a clocks perspective by:\n//!\n//! 1. Defining a configuration type in the [`periph_helpers`] module that contains any selects\n//!    or divisions available to the HAL driver\n//! 2. Implementing the [`periph_helpers::SPConfHelper`] trait, which should check that the\n//!    necessary input clocks are reasonable\n\nuse core::cell::RefCell;\nuse core::sync::atomic::{AtomicUsize, Ordering};\n\nuse config::ClocksConfig;\nuse critical_section::CriticalSection;\n\nuse crate::pac;\n\npub mod config;\nmod gate;\nmod operator;\npub mod periph_helpers;\nmod sleep;\nmod types;\n\n// Re-exports\npub use config::VddLevel;\npub use gate::{Gate, assert_reset, disable, enable, enable_and_reset, is_reset_released, release_reset};\npub use sleep::deep_sleep_if_possible;\npub use types::{Clock, ClockError, Clocks, PoweredClock, WakeGuard};\n\n//\n// Statics/Consts\n//\n\n/// The state of system core clocks.\n///\n/// Initialized by [`init()`], and then unchanged for the remainder of the program.\npub(super) static CLOCKS: critical_section::Mutex<RefCell<Option<Clocks>>> =\n    critical_section::Mutex::new(RefCell::new(None));\npub(super) static LIVE_HP_TOKENS: AtomicUsize = AtomicUsize::new(0);\n\n//\n// Free functions\n//\n\n/// Initialize the core system clocks with the given [`ClocksConfig`].\n///\n/// This function should be called EXACTLY once at start-up, usually via a\n/// call to [`embassy_mcxa::init()`](crate::init()). Subsequent calls will\n/// return an error.\npub fn init(settings: ClocksConfig) -> Result<(), ClockError> {\n    critical_section::with(|cs| {\n        if CLOCKS.borrow_ref(cs).is_some() {\n            Err(ClockError::AlreadyInitialized)\n        } else {\n            Ok(())\n        }\n    })?;\n\n    let mut clocks = Clocks::default();\n    let mut operator = operator::ClockOperator {\n        clocks: &mut clocks,\n        config: &settings,\n        sirc_forced: false,\n\n        _mrcc0: pac::MRCC0,\n        scg0: pac::SCG0,\n        syscon: pac::SYSCON,\n        vbat0: pac::VBAT0,\n        spc0: pac::SPC0,\n        fmu0: pac::FMU0,\n        cmc: pac::CMC,\n    };\n\n    operator.unlock_mrcc();\n\n    // Before applying any requested clocks, apply the requested VDD_CORE\n    // voltage level\n    operator.configure_voltages()?;\n\n    // Enable SIRC clocks FIRST, in case we need to use SIRC as main_clk for\n    // a short while.\n    operator.configure_sirc_clocks_early()?;\n    operator.configure_firc_clocks()?;\n    operator.configure_fro16k_clocks()?;\n\n    // NOTE: OSC32K must be configured AFTER FRO16K.\n    #[cfg(all(feature = \"mcxa5xx\", feature = \"unstable-osc32k\", not(feature = \"rosc-32k-as-gpio\")))]\n    operator.configure_osc32k_clocks()?;\n\n    #[cfg(not(feature = \"sosc-as-gpio\"))]\n    operator.configure_sosc()?;\n    operator.configure_spll()?;\n\n    // Finally, setup main clock\n    operator.configure_main_clk()?;\n\n    // If we were keeping SIRC enabled, now we can release it.\n    operator.configure_sirc_clocks_late();\n\n    critical_section::with(|cs| {\n        let mut clks = CLOCKS.borrow_ref_mut(cs);\n        assert!(clks.is_none(), \"Clock setup race!\");\n        *clks = Some(clocks);\n    });\n\n    Ok(())\n}\n\n/// Obtain the full clocks structure, calling the given closure in a critical section.\n///\n/// The given closure will be called with read-only access to the state of the system\n/// clocks. This can be used to query and return the state of a given clock.\n///\n/// As the caller's closure will be called in a critical section, care must be taken\n/// not to block or cause any other undue delays while accessing.\n///\n/// Calls to this function will not succeed until after a successful call to `init()`,\n/// and will always return None.\npub fn with_clocks<R: 'static, F: FnOnce(&Clocks) -> R>(f: F) -> Option<R> {\n    critical_section::with(|cs| {\n        let c = CLOCKS.borrow_ref(cs);\n        let c = c.as_ref()?;\n        Some(f(c))\n    })\n}\n\n/// Are there active `WakeGuard`s?\n///\n/// Requires a critical section to ensure this doesn't race between getting the guard\n/// count and performing some action like setting up deep sleep\n#[inline(always)]\npub fn active_wake_guards(_cs: &CriticalSection) -> bool {\n    // Relaxed is okay: we are in a critical section\n    LIVE_HP_TOKENS.load(Ordering::Relaxed) != 0\n}\n"
  },
  {
    "path": "embassy-mcxa/src/clocks/operator.rs",
    "content": "//! `ClockOperator` — init-time clock configuration sequencing.\n//!\n//! This module contains the private `ClockOperator` struct and all of its\n//! `configure_*` methods. It is only used during [`super::init()`].\n\nuse config::{\n    ClocksConfig, CoreSleep, FircConfig, FircFreqSel, Fro16KConfig, MainClockSource, SircConfig, VddDriveStrength,\n    VddLevel,\n};\nuse cortex_m::peripheral::SCB;\nuse nxp_pac::syscon::vals::Unlock;\n\nuse super::config;\nuse super::types::{Clock, ClockError, Clocks, PoweredClock};\nuse crate::chips::{ClockLimits, clock_limits};\nuse crate::pac;\nuse crate::pac::cmc::vals::CkctrlCkmode;\nuse crate::pac::scg::vals::{\n    Erefs, Fircacc, FircaccIe, FirccsrLk, Fircerr, FircerrIe, Fircsten, Range, Scs, SirccsrLk, Sircerr, Sircvld,\n    SosccsrLk, Soscerr, Source, SpllLock, SpllcsrLk, Spllerr, Spllsten, TrimUnlock,\n};\nuse crate::pac::spc::vals::{\n    ActiveCfgBgmode, ActiveCfgCoreldoVddDs, ActiveCfgCoreldoVddLvl, LpCfgBgmode, LpCfgCoreldoVddLvl, Vsm,\n};\nuse crate::pac::syscon::vals::{\n    AhbclkdivUnstab, FrohfdivHalt, FrohfdivReset, FrohfdivUnstab, FrolfdivHalt, FrolfdivReset, FrolfdivUnstab,\n    Pll1clkdivHalt, Pll1clkdivReset, Pll1clkdivUnstab,\n};\n\n/// The ClockOperator is a private helper type that contains the methods used\n/// during system clock initialization.\n///\n/// # SAFETY\n///\n/// Concurrent access to clock-relevant peripheral registers, such as `MRCC`, `SCG`,\n/// `SYSCON`, and `VBAT` should not be allowed for the duration of the [`init()`](super::init) function.\n#[allow(dead_code)]\npub(super) struct ClockOperator<'a> {\n    /// A mutable reference to the current state of system clocks\n    pub(super) clocks: &'a mut Clocks,\n    /// A reference to the requested configuration provided by the caller of [`init()`](super::init)\n    pub(super) config: &'a ClocksConfig,\n\n    /// SIRC is forced-on until we set `main_clk`\n    pub(super) sirc_forced: bool,\n\n    // We hold on to stolen peripherals\n    pub(super) _mrcc0: pac::mrcc::Mrcc,\n    pub(super) scg0: pac::scg::Scg,\n    pub(super) syscon: pac::syscon::Syscon,\n    pub(super) vbat0: pac::vbat::Vbat,\n    pub(super) spc0: pac::spc::Spc,\n    pub(super) fmu0: pac::fmu::Fmu,\n    pub(super) cmc: pac::cmc::Cmc,\n}\n\nimpl ClockOperator<'_> {\n    pub(super) fn unlock_mrcc(&mut self) {\n        // On the MCXA5xx, this is default *locked*, preventing any writes to\n        // MRCC registers re enable/div settings. For now, just leave it unlocked,\n        // we might want to actively unlock/lock in periph helpers in the future.\n        self.syscon.clkunlock().modify(|w| w.set_unlock(Unlock::ENABLE));\n    }\n\n    fn active_limits(&self) -> &'static ClockLimits {\n        match self.config.vdd_power.active_mode.level {\n            VddLevel::MidDriveMode => &ClockLimits::MID_DRIVE,\n            #[cfg(feature = \"mcxa5xx\")]\n            VddLevel::NormalMode => &ClockLimits::NORMAL_DRIVE,\n            VddLevel::OverDriveMode => &ClockLimits::OVER_DRIVE,\n        }\n    }\n\n    fn low_power_limits(&self) -> &'static ClockLimits {\n        match self.config.vdd_power.low_power_mode.level {\n            VddLevel::MidDriveMode => &ClockLimits::MID_DRIVE,\n            #[cfg(feature = \"mcxa5xx\")]\n            VddLevel::NormalMode => &ClockLimits::NORMAL_DRIVE,\n            VddLevel::OverDriveMode => &ClockLimits::OVER_DRIVE,\n        }\n    }\n\n    fn lowest_relevant_limits(&self, for_power: &PoweredClock) -> &'static ClockLimits {\n        // We always enforce that deep sleep has a drive <= active mode.\n        match for_power {\n            PoweredClock::NormalEnabledDeepSleepDisabled => self.active_limits(),\n            PoweredClock::AlwaysEnabled => self.low_power_limits(),\n        }\n    }\n\n    /// Configure the FIRC/FRO180M/FRO192M clock family\n    pub(super) fn configure_firc_clocks(&mut self) -> Result<(), ClockError> {\n        // Three options here:\n        //\n        // * Firc is disabled -> Switch main clock to SIRC and return\n        // * Firc is enabled and !default ->\n        //   * Switch main clock to SIRC\n        //   * Make FIRC changes\n        //   * Switch main clock back to FIRC\n        // * Firc is enabled and default -> nop\n        #[cfg(feature = \"mcxa2xx\")]\n        let default_freq = FircFreqSel::Mhz45;\n        #[cfg(feature = \"mcxa5xx\")]\n        let default_freq = FircFreqSel::Mhz48;\n        let is_default = self.config.firc.as_ref().is_some_and(|c| c.frequency == default_freq);\n\n        // If we are not default, then we need to switch to SIRC\n        if !is_default {\n            // Set SIRC (fro_12m) as the source\n            self.scg0.rccr().modify(|w| w.set_scs(Scs::SIRC));\n\n            // Wait for the change to complete\n            while self.scg0.csr().read().scs() != Scs::SIRC {}\n        }\n\n        // Enable CSR writes\n        self.scg0.firccsr().modify(|w| w.set_lk(FirccsrLk::WRITE_ENABLED));\n\n        // Did the user give us a FIRC config?\n        let Some(firc) = self.config.firc.as_ref() else {\n            // Nope, and we've already switched to fro_12m. Disable FIRC.\n            self.scg0.firccsr().modify(|w| {\n                w.set_fircsten(Fircsten::DISABLED_IN_STOP_MODES);\n                w.set_fircerr_ie(FircerrIe::ERROR_NOT_DETECTED);\n                w.set_firc_fclk_periph_en(false);\n                w.set_firc_sclk_periph_en(false);\n                w.set_fircen(false);\n            });\n\n            self.scg0.firccsr().modify(|w| w.set_lk(FirccsrLk::WRITE_DISABLED));\n            return Ok(());\n        };\n\n        // If we are here, we WANT FIRC. If we are !default, let's disable FIRC before\n        // we mess with it. If we are !default, we have already switched to SIRC instead!\n        if !is_default {\n            // Unlock\n            self.scg0.firccsr().modify(|w| w.set_lk(FirccsrLk::WRITE_ENABLED));\n\n            // Disable FIRC\n            self.scg0.firccsr().modify(|w| {\n                w.set_fircen(false);\n                w.set_fircsten(Fircsten::DISABLED_IN_STOP_MODES);\n                w.set_fircerr_ie(FircerrIe::ERROR_NOT_DETECTED);\n                w.set_fircacc_ie(FircaccIe::FIRCACCNOT);\n                w.set_firc_sclk_periph_en(false);\n                w.set_firc_fclk_periph_en(false);\n            });\n        }\n\n        let limits = self.lowest_relevant_limits(&firc.power);\n\n        // Set frequency (if not the default!), re-enable FIRC, and return the base frequency\n        let (base_freq, sel) = firc.frequency.to_freq_and_sel();\n\n        self.scg0.firccfg().modify(|w| w.set_freq_sel(sel));\n        self.scg0.firccsr().modify(|w| w.set_fircen(true));\n\n        // Wait for FIRC to be enabled, error-free, and accurate\n        let mut firc_ok = false;\n        while !firc_ok {\n            let csr = self.scg0.firccsr().read();\n\n            firc_ok = csr.fircen()\n                && csr.fircacc() == Fircacc::ENABLED_AND_VALID\n                && csr.fircerr() == Fircerr::ERROR_NOT_DETECTED;\n        }\n\n        // Note that the fro_hf_root is active\n        self.clocks.fro_hf_root = Some(Clock {\n            frequency: base_freq,\n            power: firc.power,\n        });\n\n        // Okay! Now we're past that, let's enable all the downstream clocks.\n        let FircConfig {\n            frequency: _,\n            power,\n            fro_hf_enabled,\n            clk_hf_fundamental_enabled,\n            fro_hf_div,\n        } = firc;\n\n        // When is the FRO enabled?\n        let (bg_good, pow_set) = match power {\n            PoweredClock::NormalEnabledDeepSleepDisabled => {\n                // We only need bandgap enabled in active mode\n                (self.clocks.bandgap_active, Fircsten::DISABLED_IN_STOP_MODES)\n            }\n            PoweredClock::AlwaysEnabled => {\n                // We need bandgaps enabled in both active and deep sleep mode\n                let bg_good = self.clocks.bandgap_active && self.clocks.bandgap_lowpower;\n                (bg_good, Fircsten::ENABLED_IN_STOP_MODES)\n            }\n        };\n        if !bg_good {\n            return Err(ClockError::BadConfig {\n                clock: \"fro_hf\",\n                reason: \"bandgap required to be enabled when clock enabled\",\n            });\n        }\n\n        // Do we enable the `fro_hf` output?\n        let fro_hf_set = if *fro_hf_enabled {\n            if base_freq > limits.fro_hf {\n                return Err(ClockError::BadConfig {\n                    clock: \"fro_hf\",\n                    reason: \"exceeds max\",\n                });\n            }\n\n            self.clocks.fro_hf = Some(Clock {\n                frequency: base_freq,\n                power: *power,\n            });\n            true\n        } else {\n            false\n        };\n\n        // Do we enable the `clk_45m`/`clk_48m` output?\n        let clk_fund_set = if *clk_hf_fundamental_enabled {\n            self.clocks.clk_hf_fundamental = Some(Clock {\n                frequency: 45_000_000,\n                power: *power,\n            });\n            true\n        } else {\n            false\n        };\n\n        self.scg0.firccsr().modify(|w| {\n            w.set_fircsten(pow_set);\n            w.set_firc_fclk_periph_en(fro_hf_set);\n            w.set_firc_sclk_periph_en(clk_fund_set);\n        });\n\n        // Last write to CSR, re-lock\n        self.scg0.firccsr().modify(|w| w.set_lk(FirccsrLk::WRITE_DISABLED));\n\n        // Do we enable the `fro_hf_div` output?\n        if let Some(d) = fro_hf_div.as_ref() {\n            // We need `fro_hf` to be enabled\n            if !*fro_hf_enabled {\n                return Err(ClockError::BadConfig {\n                    clock: \"fro_hf_div\",\n                    reason: \"fro_hf not enabled\",\n                });\n            }\n\n            let div_freq = base_freq / d.into_divisor();\n            if div_freq > limits.fro_hf_div {\n                return Err(ClockError::BadConfig {\n                    clock: \"fro_hf_root\",\n                    reason: \"exceeds max frequency\",\n                });\n            }\n\n            // Halt and reset the div; then set our desired div.\n            self.syscon.frohfdiv().write(|w| {\n                w.set_halt(FrohfdivHalt::HALT);\n                w.set_reset(FrohfdivReset::ASSERTED);\n                w.set_div(d.into_bits());\n            });\n            // Then unhalt it, and reset it\n            self.syscon.frohfdiv().write(|w| {\n                w.set_halt(FrohfdivHalt::RUN);\n                w.set_reset(FrohfdivReset::RELEASED);\n                w.set_div(d.into_bits());\n            });\n\n            // Wait for clock to stabilize\n            while self.syscon.frohfdiv().read().unstab() == FrohfdivUnstab::ONGOING {}\n\n            // Store off the clock info\n            self.clocks.fro_hf_div = Some(Clock {\n                frequency: div_freq,\n                power: *power,\n            });\n        }\n\n        Ok(())\n    }\n\n    /// Configure the SIRC/FRO12M clock family\n    pub(super) fn configure_sirc_clocks_early(&mut self) -> Result<(), ClockError> {\n        let SircConfig {\n            power,\n            fro_12m_enabled,\n            fro_lf_div,\n        } = &self.config.sirc;\n        let base_freq = 12_000_000;\n\n        // Allow writes\n        self.scg0.sirccsr().modify(|w| w.set_lk(SirccsrLk::WRITE_ENABLED));\n        self.clocks.fro_12m_root = Some(Clock {\n            frequency: base_freq,\n            power: *power,\n        });\n\n        let deep = match power {\n            PoweredClock::NormalEnabledDeepSleepDisabled => false,\n            PoweredClock::AlwaysEnabled => true,\n        };\n\n        // clk_1m is *before* the fro_12m clock gate\n        self.clocks.clk_1m = Some(Clock {\n            frequency: base_freq / 12,\n            power: *power,\n        });\n\n        // If the user wants fro_12m to be disabled, FOR now, we ignore their\n        // wish to ensure fro_12m is selectable as a main_clk source at least until\n        // we select the CPU clock. We still mark it as not enabled though, to prevent\n        // other peripherals using it, as we will gate if off at `configure_sirc_clocks_late`.\n        if *fro_12m_enabled {\n            self.clocks.fro_12m = Some(Clock {\n                frequency: base_freq,\n                power: *power,\n            });\n        } else {\n            self.sirc_forced = true;\n        };\n\n        // Set sleep/peripheral usage\n        self.scg0.sirccsr().modify(|w| {\n            w.set_sircsten(deep);\n            // Always on, for now at least! Will be resolved in `configure_sirc_clocks_late`\n            w.set_sirc_clk_periph_en(true);\n        });\n\n        while self.scg0.sirccsr().read().sircvld() == Sircvld::DISABLED_OR_NOT_VALID {}\n        if self.scg0.sirccsr().read().sircerr() == Sircerr::ERROR_DETECTED {\n            return Err(ClockError::BadConfig {\n                clock: \"sirc\",\n                reason: \"error set\",\n            });\n        }\n\n        // reset lock\n        self.scg0.sirccsr().modify(|w| w.set_lk(SirccsrLk::WRITE_DISABLED));\n\n        // Do we enable the `fro_lf_div` output?\n        if let Some(d) = fro_lf_div.as_ref() {\n            // We need `fro_lf` to be enabled\n            if !*fro_12m_enabled {\n                return Err(ClockError::BadConfig {\n                    clock: \"fro_lf_div\",\n                    reason: \"fro_12m not enabled\",\n                });\n            }\n\n            // Halt and reset the div; then set our desired div.\n            self.syscon.frolfdiv().write(|w| {\n                w.set_halt(FrolfdivHalt::HALT);\n                w.set_reset(FrolfdivReset::ASSERTED);\n                w.set_div(d.into_bits());\n            });\n            // Then unhalt it, and reset it\n            self.syscon.frolfdiv().modify(|w| {\n                w.set_halt(FrolfdivHalt::RUN);\n                w.set_reset(FrolfdivReset::RELEASED);\n                w.set_div(d.into_bits());\n            });\n\n            // Wait for clock to stabilize\n            while self.syscon.frolfdiv().read().unstab() == FrolfdivUnstab::ONGOING {}\n\n            // Store off the clock info\n            self.clocks.fro_lf_div = Some(Clock {\n                frequency: base_freq / d.into_divisor(),\n                power: *power,\n            });\n        }\n\n        Ok(())\n    }\n\n    pub(super) fn configure_sirc_clocks_late(&mut self) {\n        // If we forced SIRC's fro_12m to be enabled, disable it now.\n        if self.sirc_forced {\n            // Allow writes\n            self.scg0.sirccsr().modify(|w| w.set_lk(SirccsrLk::WRITE_ENABLED));\n\n            // Disable clk_12m\n            self.scg0.sirccsr().modify(|w| w.set_sirc_clk_periph_en(false));\n\n            // reset lock\n            self.scg0.sirccsr().modify(|w| w.set_lk(SirccsrLk::WRITE_DISABLED));\n        }\n    }\n\n    /// Configure the ROSC/FRO16K/clk_16k clock family\n    pub(super) fn configure_fro16k_clocks(&mut self) -> Result<(), ClockError> {\n        // If we have a config: ensure fro16k is enabled. If not: ensure it is disabled.\n        let enable = self.config.fro16k.is_some();\n        self.vbat0.froctla().modify(|w| w.set_fro_en(enable));\n\n        // Lock the control register\n        self.vbat0.frolcka().modify(|w| w.set_lock(true));\n\n        // If we're disabled, we're done!\n        let Some(fro16k) = self.config.fro16k.as_ref() else {\n            return Ok(());\n        };\n\n        // Enabled, now set up.\n        let Fro16KConfig {\n            vsys_domain_active,\n            vdd_core_domain_active,\n            #[cfg(feature = \"mcxa5xx\")]\n            vbat_domain_active,\n        } = fro16k;\n\n        // Enable clock outputs to both VSYS and VDD_CORE domains\n        // Bit 0: clk_16k0 to VSYS domain\n        // Bit 1: clk_16k1 to VDD_CORE/CORE_MAIN domain\n        // Bit 2: clk_16k2 to VBAT domain (5xx only)\n        //\n        // TODO: Define sub-fields for this register with a PAC patch?\n        let mut bits = 0;\n        if *vsys_domain_active {\n            bits |= 0b01;\n            self.clocks.clk_16k_vsys = Some(Clock {\n                frequency: 16_384,\n                power: PoweredClock::AlwaysEnabled,\n            });\n        }\n        if *vdd_core_domain_active {\n            bits |= 0b10;\n            self.clocks.clk_16k_vdd_core = Some(Clock {\n                frequency: 16_384,\n                power: PoweredClock::AlwaysEnabled,\n            });\n        }\n        #[cfg(feature = \"mcxa5xx\")]\n        if *vbat_domain_active {\n            bits |= 0b100;\n            self.clocks.clk_16k_vbat = Some(Clock {\n                frequency: 16_384,\n                power: PoweredClock::AlwaysEnabled,\n            });\n        }\n        self.vbat0.froclke().modify(|w| w.set_clke(bits));\n\n        Ok(())\n    }\n\n    /// Configure the ROSC/OSC32K clock family\n    #[cfg(all(feature = \"mcxa5xx\", feature = \"unstable-osc32k\", not(feature = \"rosc-32k-as-gpio\")))]\n    pub(super) fn configure_osc32k_clocks(&mut self) -> Result<(), ClockError> {\n        use config::{Osc32KCapSel, Osc32KCoarseGain, Osc32KMode};\n        use nxp_pac::vbat::vals::{\n            CoarseAmpGain, ExtalCapSel, InitTrim, ModeEn, StatusaLdoRdy, StatusaOscRdy, SupplyDet, XtalCapSel,\n        };\n\n        // Unlock the control first\n        self.vbat0.ldolcka().modify(|w| w.set_lock(false));\n\n        let Some(cfg) = self.config.osc32k.as_ref() else {\n            // TODO: how to ensure disabled?\n            // ???\n\n            // Re-lock after disabling\n            self.vbat0.ldolcka().modify(|w| w.set_lock(true));\n            return Ok(());\n        };\n\n        // To enable and lock the LDO and bandgap:\n        //\n        // NOTE(AJM): \"The FRO16K must be enabled before enabling the SRAM LDO or the bandgap\"\n        //\n        // 1. Enable the FRO16K.\n        //   * NOTE(AJM): clk_16k is always enabled if enabled at all.\n        //   * TODO(AJM): I'm not sure which domain needs to be active for this requirement.\n        //     It seems reasonable that it would be the vbat domain?\n\n        self.clocks.ensure_clk_16k_vbat_active(&PoweredClock::AlwaysEnabled)?;\n\n        // 2. Write 7h to LDO_RAM Control A (LDOCTLA).\n        self.vbat0.ldoctla().write(|w| {\n            w.set_refresh_en(true);\n            w.set_ldo_en(true);\n            w.set_bg_en(true);\n        });\n\n        // 3. Wait for STATUSA[LDO_RDY] to become 1.\n        while self.vbat0.statusa().read().ldo_rdy() != StatusaLdoRdy::SET {}\n\n        // 4. Write 1h to LDOLCKA[LOCK].\n        self.vbat0.ldolcka().modify(|w| w.set_lock(true));\n\n        match &cfg.mode {\n            Osc32KMode::HighPower {\n                coarse_amp_gain,\n                xtal_cap_sel,\n                extal_cap_sel,\n            } => {\n                // To configure and lock OSC32kHz for normal mode operation:\n                //\n                // 1. Configure OSCCTLA[EXTAL_CAP_SEL], OSCCTLA[XTAL_CAP_SEL] and OSCCTLA[COARSE_AMP_GAIN] as\n                // required based on the external crystal component ESR and CL values, and by the PCB parasitics on the EXTAL32K and\n                // XTAL32K pins. Configure 0h to OSCCTLA[MODE_EN], 1h to OSCCTLA[CAP_SEL_EN], and 1h to OSCCTLA[OSC_EN].\n                //   * NOTE(AJM): You must write 1 to this field and OSCCTLA[OSC_EN] simultaneously.\n                self.vbat0.oscctla().modify(|w| {\n                    w.set_xtal_cap_sel(match xtal_cap_sel {\n                        Osc32KCapSel::Cap2PicoF => XtalCapSel::SEL2,\n                        Osc32KCapSel::Cap4PicoF => XtalCapSel::SEL4,\n                        Osc32KCapSel::Cap6PicoF => XtalCapSel::SEL6,\n                        Osc32KCapSel::Cap8PicoF => XtalCapSel::SEL8,\n                        Osc32KCapSel::Cap10PicoF => XtalCapSel::SEL10,\n                        Osc32KCapSel::Cap12PicoF => XtalCapSel::SEL12,\n                        Osc32KCapSel::Cap14PicoF => XtalCapSel::SEL14,\n                        Osc32KCapSel::Cap16PicoF => XtalCapSel::SEL16,\n                        Osc32KCapSel::Cap18PicoF => XtalCapSel::SEL18,\n                        Osc32KCapSel::Cap20PicoF => XtalCapSel::SEL20,\n                        Osc32KCapSel::Cap22PicoF => XtalCapSel::SEL22,\n                        Osc32KCapSel::Cap24PicoF => XtalCapSel::SEL24,\n                        Osc32KCapSel::Cap26PicoF => XtalCapSel::SEL26,\n                        Osc32KCapSel::Cap28PicoF => XtalCapSel::SEL28,\n                        Osc32KCapSel::Cap30PicoF => XtalCapSel::SEL30,\n                    });\n                    w.set_extal_cap_sel(match extal_cap_sel {\n                        Osc32KCapSel::Cap2PicoF => ExtalCapSel::SEL2,\n                        Osc32KCapSel::Cap4PicoF => ExtalCapSel::SEL4,\n                        Osc32KCapSel::Cap6PicoF => ExtalCapSel::SEL6,\n                        Osc32KCapSel::Cap8PicoF => ExtalCapSel::SEL8,\n                        Osc32KCapSel::Cap10PicoF => ExtalCapSel::SEL10,\n                        Osc32KCapSel::Cap12PicoF => ExtalCapSel::SEL12,\n                        Osc32KCapSel::Cap14PicoF => ExtalCapSel::SEL14,\n                        Osc32KCapSel::Cap16PicoF => ExtalCapSel::SEL16,\n                        Osc32KCapSel::Cap18PicoF => ExtalCapSel::SEL18,\n                        Osc32KCapSel::Cap20PicoF => ExtalCapSel::SEL20,\n                        Osc32KCapSel::Cap22PicoF => ExtalCapSel::SEL22,\n                        Osc32KCapSel::Cap24PicoF => ExtalCapSel::SEL24,\n                        Osc32KCapSel::Cap26PicoF => ExtalCapSel::SEL26,\n                        Osc32KCapSel::Cap28PicoF => ExtalCapSel::SEL28,\n                        Osc32KCapSel::Cap30PicoF => ExtalCapSel::SEL30,\n                    });\n                    w.set_coarse_amp_gain(match coarse_amp_gain {\n                        Osc32KCoarseGain::EsrRange0 => CoarseAmpGain::GAIN05,\n                        Osc32KCoarseGain::EsrRange1 => CoarseAmpGain::GAIN10,\n                        Osc32KCoarseGain::EsrRange2 => CoarseAmpGain::GAIN18,\n                        Osc32KCoarseGain::EsrRange3 => CoarseAmpGain::GAIN33,\n                    });\n                    w.set_mode_en(ModeEn::HP);\n                    w.set_cap_sel_en(true);\n                    w.set_osc_en(true);\n                });\n\n                // 2. Wait for STATUSA[OSC_RDY] to become 1.\n                while self.vbat0.statusa().read().osc_rdy() != StatusaOscRdy::SET {}\n\n                // 3. Write 1h to OSCLCKA[LOCK].\n                self.vbat0.osclcka().modify(|w| w.set_lock(true));\n\n                // 4. Write 0h to OSCCTLA[EXTAL_CAP_SEL] and 0h to OSCCTLA[XTAL_CAP_SEL].\n                self.vbat0.oscctla().modify(|w| {\n                    w.set_xtal_cap_sel(XtalCapSel::SEL0);\n                    w.set_extal_cap_sel(ExtalCapSel::SEL0);\n                });\n\n                // 5. Alter OSCCLKE[CLKE] to clock gate different OSC32K outputs to different peripherals to reduce power consumption.\n                const ENABLED: Option<Clock> = Some(Clock {\n                    frequency: 32_768,\n                    power: PoweredClock::NormalEnabledDeepSleepDisabled,\n                });\n                self.vbat0.oscclke().modify(|w| {\n                    let mut val = 0u8;\n                    if cfg.vsys_domain_active {\n                        val |= 0b001;\n                        self.clocks.clk_32k_vsys = ENABLED;\n                    }\n                    if cfg.vdd_core_domain_active {\n                        val |= 0b010;\n                        self.clocks.clk_32k_vdd_core = ENABLED;\n                    }\n                    if cfg.vbat_domain_active {\n                        val |= 0b100;\n                        self.clocks.clk_32k_vbat = ENABLED;\n                    }\n                    w.set_clke(val);\n                });\n            }\n            Osc32KMode::LowPower {\n                coarse_amp_gain,\n                vbat_exceeds_3v0,\n            } => {\n                // To configure OSC32kHz for low power mode operation:\n                //\n                // 1. Write 3h to OSCCFGA[INIT_TRIM].\n                //   * NOTE(AJM): This is \"1 second\"?\n                self.vbat0.osccfga().modify(|w| w.set_init_trim(InitTrim::SEL3));\n\n                // 2. Configure OSCCTLA[EXTAL_CAP_SEL], OSCCTLA[XTAL_CAP_SEL] and OSCCTLA[COARSE_AMP_GAIN] as\n                // required based on the external crystal component ESR and CL values, and by the PCB parasitics on the EXTAL32K and\n                // XTAL32K pins. Configure 1h to OSCCTLA[MODE_EN], 1h to OSCCTLA[CAP_SEL_EN], and 1h to OSCCTLA[OSC_EN].\n                //   * NOTE(AJM): The configuration EXTAL_CAP_SEL=0000 and CAP_SEL_EN=1 is required in low power\n                //     mode and is not supported in other modes\n                self.vbat0.oscctla().modify(|w| {\n                    // TODO(AJM): Do we need to set these to reasonable values during the \"startup\" phase, and THEN\n                    // restore them to 0? RM is very unclear here.\n                    w.set_xtal_cap_sel(XtalCapSel::SEL0);\n                    w.set_extal_cap_sel(ExtalCapSel::SEL0);\n\n                    w.set_coarse_amp_gain(match coarse_amp_gain {\n                        Osc32KCoarseGain::EsrRange0 => CoarseAmpGain::GAIN05,\n                        Osc32KCoarseGain::EsrRange1 => CoarseAmpGain::GAIN10,\n                        Osc32KCoarseGain::EsrRange2 => CoarseAmpGain::GAIN18,\n                        Osc32KCoarseGain::EsrRange3 => CoarseAmpGain::GAIN33,\n                    });\n\n                    // TODO: This naming is bad\n                    //\n                    // pub enum ModeEn {\n                    //     #[doc = \"Normal mode\"]\n                    //     HP = 0x0,\n                    //     #[doc = \"Startup mode\"]\n                    //     LP = 0x01,\n                    //     _RESERVED_2 = 0x02,\n                    //     #[doc = \"Low power mode\"]\n                    //     SW = 0x03,\n                    // }\n\n                    w.set_mode_en(ModeEn::LP);\n                    w.set_cap_sel_en(true);\n                    w.set_osc_en(true);\n                });\n\n                // 3. Wait for STATUSA[OSC_RDY] to become 1.\n                while self.vbat0.statusa().read().osc_rdy() != StatusaOscRdy::SET {}\n\n                // 4. Write 0h to OSCCFGA[INIT_TRIM].\n                self.vbat0.osccfga().modify(|w| w.set_init_trim(InitTrim::SEL0));\n\n                // 5. Configure 3h to OSCCTLA[MODE_EN], 0h to OSCCTLA[EXTAL_CAP_SEL] and 0h to OSCCTLA[XTAL_CAP_SEL].\n                // Configure OSCCTLA[SUPPLY_DET] as required by application.\n                self.vbat0.oscctla().modify(|w| {\n                    w.set_mode_en(ModeEn::SW);\n                    w.set_xtal_cap_sel(XtalCapSel::SEL0);\n                    w.set_extal_cap_sel(ExtalCapSel::SEL0);\n                    w.set_supply_det(if *vbat_exceeds_3v0 {\n                        SupplyDet::G3VSUPPLY\n                    } else {\n                        SupplyDet::L3VSUPPLY\n                    });\n                });\n\n                // 6. Wait for STATUSA[OSC_RDY] to become 1.\n                while self.vbat0.statusa().read().osc_rdy() != StatusaOscRdy::SET {}\n\n                // 7. Alter OSCCLKE[CLKE] to clock gate different OSC32K outputs to different peripherals to reduce power consumption.\n                const ENABLED: Option<Clock> = Some(Clock {\n                    frequency: 32_768,\n                    power: PoweredClock::AlwaysEnabled,\n                });\n                self.vbat0.oscclke().modify(|w| {\n                    let mut val = 0u8;\n                    if cfg.vsys_domain_active {\n                        val |= 0b001;\n                        self.clocks.clk_32k_vsys = ENABLED;\n                    }\n                    if cfg.vdd_core_domain_active {\n                        val |= 0b010;\n                        self.clocks.clk_32k_vdd_core = ENABLED;\n                    }\n                    if cfg.vbat_domain_active {\n                        val |= 0b100;\n                        self.clocks.clk_32k_vbat = ENABLED;\n                    }\n                    w.set_clke(val);\n                });\n            }\n        }\n\n        Ok(())\n    }\n\n    fn ensure_ldo_active(&mut self, for_clock: &'static str, for_power: &PoweredClock) -> Result<(), ClockError> {\n        let bg_good = match for_power {\n            PoweredClock::NormalEnabledDeepSleepDisabled => self.clocks.bandgap_active,\n            PoweredClock::AlwaysEnabled => self.clocks.bandgap_active && self.clocks.bandgap_lowpower,\n        };\n        if !bg_good {\n            return Err(ClockError::BadConfig {\n                clock: for_clock,\n                reason: \"LDO requires core bandgap enabled\",\n            });\n        }\n\n        // TODO: Config for the LDO? For now, just enable\n        // using the default settings:\n        // LDOBYPASS: 0/not bypassed\n        // VOUT_SEL: 0b100: 1.1v\n        // LDOEN: 0/Disabled\n        let already_enabled = {\n            let ldocsr = self.scg0.ldocsr().read();\n            ldocsr.ldoen() && ldocsr.vout_ok()\n        };\n        if !already_enabled {\n            self.scg0.ldocsr().modify(|w| w.set_ldoen(true));\n            while !self.scg0.ldocsr().read().vout_ok() {}\n        }\n\n        Ok(())\n    }\n\n    /// Configure the SOSC/clk_in oscillator\n    #[cfg(not(feature = \"sosc-as-gpio\"))]\n    pub(super) fn configure_sosc(&mut self) -> Result<(), ClockError> {\n        let Some(parts) = self.config.sosc.as_ref() else {\n            return Ok(());\n        };\n\n        // Enable (and wait for) LDO to be active\n        self.ensure_ldo_active(\"sosc\", &parts.power)?;\n\n        let eref = match parts.mode {\n            config::SoscMode::CrystalOscillator => Erefs::INTERNAL,\n            config::SoscMode::ActiveClock => Erefs::EXTERNAL,\n        };\n        let freq = parts.frequency;\n\n        // TODO: Fix PAC names here\n        //\n        // #[doc = \"0: Frequency range select of 8-16 MHz.\"]\n        // Freq16to20mhz = 0,\n        // #[doc = \"1: Frequency range select of 16-25 MHz.\"]\n        // LowFreq = 1,\n        // #[doc = \"2: Frequency range select of 25-40 MHz.\"]\n        // MediumFreq = 2,\n        // #[doc = \"3: Frequency range select of 40-50 MHz.\"]\n        // HighFreq = 3,\n        let range = match freq {\n            0..8_000_000 => {\n                return Err(ClockError::BadConfig {\n                    clock: \"clk_in\",\n                    reason: \"freq too low\",\n                });\n            }\n            8_000_000..16_000_000 => Range::FREQ_16TO20MHZ,\n            16_000_000..25_000_000 => Range::LOW_FREQ,\n            25_000_000..40_000_000 => Range::MEDIUM_FREQ,\n            40_000_000..50_000_001 => Range::HIGH_FREQ,\n            50_000_001.. => {\n                return Err(ClockError::BadConfig {\n                    clock: \"clk_in\",\n                    reason: \"freq too high\",\n                });\n            }\n        };\n\n        // Set source/erefs and range\n        self.scg0.sosccfg().modify(|w| {\n            w.set_erefs(eref);\n            w.set_range(range);\n        });\n\n        // Disable lock\n        self.scg0.sosccsr().modify(|w| w.set_lk(SosccsrLk::WRITE_ENABLED));\n\n        // TODO: We could enable the SOSC clock monitor. There are some things to\n        // figure out first:\n        //\n        // * This requires SIRC to be enabled, not sure which branch. Maybe fro12m_root?\n        // * If SOSC needs to work in deep sleep, AND the monitor is enabled:\n        //   * SIRC also need needs to be low power\n        // * We need to decide if we need an interrupt or a reset if the monitor trips\n        let (bg_good, soscsten) = match parts.power {\n            PoweredClock::NormalEnabledDeepSleepDisabled => (self.clocks.bandgap_active, false),\n            PoweredClock::AlwaysEnabled => (self.clocks.bandgap_active && self.clocks.bandgap_lowpower, true),\n        };\n\n        if !bg_good {\n            return Err(ClockError::BadConfig {\n                clock: \"sosc\",\n                reason: \"bandgap required\",\n            });\n        }\n\n        // Apply remaining config\n        self.scg0.sosccsr().modify(|w| {\n            // For now, just disable the monitor. See above.\n            w.set_sosccm(false);\n\n            // Set deep sleep mode if needed\n            w.set_soscsten(soscsten);\n\n            // Enable SOSC\n            w.set_soscen(true)\n        });\n\n        // Wait for SOSC to be valid, check for errors\n        while !self.scg0.sosccsr().read().soscvld() {}\n        if self.scg0.sosccsr().read().soscerr() == Soscerr::ENABLED_AND_ERROR {\n            return Err(ClockError::BadConfig {\n                clock: \"clk_in\",\n                reason: \"soscerr is set\",\n            });\n        }\n\n        // Re-lock the sosc\n        self.scg0.sosccsr().modify(|w| w.set_lk(SosccsrLk::WRITE_DISABLED));\n\n        self.clocks.clk_in = Some(Clock {\n            frequency: freq,\n            power: parts.power,\n        });\n\n        Ok(())\n    }\n\n    pub(super) fn configure_spll(&mut self) -> Result<(), ClockError> {\n        // # Vocab\n        //\n        // | Name   | Meaning                                                     |\n        // | :---   | :---                                                        |\n        // | Fin    | Frequency of clkin                                          |\n        // | clkout | Output clock of the PLL                                     |\n        // | Fout   | Frequency of clkout (depends on mode)                       |\n        // | clkref | PLL Reference clock, the input clock to the PFD             |\n        // | Fref   | Frequency of clkref, Fref = Fin / N                         |\n        // | Fcco   | Frequency of the output clock of the CCO, Fcco = M * Fref   |\n        // | N      | Predivider value                                            |\n        // | M      | Feedback divider value                                      |\n        // | P      | Postdivider value                                           |\n        // | Tpon   | PLL start-up time                                           |\n\n        // No PLL? Nothing to do!\n        let Some(cfg) = self.config.spll.as_ref() else {\n            return Ok(());\n        };\n\n        // Ensure the LDO is active\n        self.ensure_ldo_active(\"spll\", &cfg.power)?;\n\n        // match on the source, ensure it is active already\n        let res = match cfg.source {\n            #[cfg(not(feature = \"sosc-as-gpio\"))]\n            config::SpllSource::Sosc => self\n                .clocks\n                .clk_in\n                .as_ref()\n                .map(|c| (c, Source::SOSC))\n                .ok_or(\"sosc not active\"),\n            config::SpllSource::Firc => self\n                .clocks\n                .clk_hf_fundamental\n                .as_ref()\n                .map(|c| (c, Source::FIRC))\n                .ok_or(\"firc not active\"),\n            config::SpllSource::Sirc => self\n                .clocks\n                .fro_12m\n                .as_ref()\n                .map(|c| (c, Source::SIRC))\n                .ok_or(\"sirc not active\"),\n        };\n        // This checks if active\n        let (clk, variant) = res.map_err(|s| ClockError::BadConfig {\n            clock: \"spll\",\n            reason: s,\n        })?;\n        // This checks the correct power reqs\n        if !clk.power.meets_requirement_of(&cfg.power) {\n            return Err(ClockError::BadConfig {\n                clock: \"spll\",\n                reason: \"needs low power source\",\n            });\n        }\n\n        // Bandwidth calc\n        //\n        // > In normal applications, you must calculate the bandwidth manually by using the feedback divider M (ranging from 1 to (2^16)-1),\n        // > Equation 1, and Equation 2. The PLL is automatically stable in such case. In normal applications, SPLLCTRL[BANDDIRECT] must\n        // > be 0; in this case, the bandwidth changes as a function of M.\n        if clk.frequency == 0 {\n            return Err(ClockError::BadConfig {\n                clock: \"spll\",\n                reason: \"internal error\",\n            });\n        }\n\n        // These are calculated differently depending on the mode.\n        let f_in = clk.frequency;\n        let bp_pre: bool;\n        let bp_post: bool;\n        let bp_post2: bool;\n        let m: u16;\n        let p: Option<u8>;\n        let n: Option<u8>;\n\n        // Calculate both Fout and Fcco so we can ensure they don't overflow\n        // and are in range\n        let fout: Option<u32>;\n        let fcco: Option<u32>;\n\n        let m_check = |m: u16| {\n            if !(1..=u16::MAX).contains(&m) {\n                Err(ClockError::BadConfig {\n                    clock: \"spll\",\n                    reason: \"m_mult out of range\",\n                })\n            } else {\n                Ok(m)\n            }\n        };\n        let p_check = |p: u8| {\n            if !(1..=31).contains(&p) {\n                Err(ClockError::BadConfig {\n                    clock: \"spll\",\n                    reason: \"p_div out of range\",\n                })\n            } else {\n                Ok(p)\n            }\n        };\n        let n_check = |n: u8| {\n            if !(1..=u8::MAX).contains(&n) {\n                Err(ClockError::BadConfig {\n                    clock: \"spll\",\n                    reason: \"n_div out of range\",\n                })\n            } else {\n                Ok(n)\n            }\n        };\n\n        match cfg.mode {\n            // Fout = M x Fin\n            config::SpllMode::Mode1a { m_mult } => {\n                bp_pre = true;\n                bp_post = true;\n                bp_post2 = false;\n                m = m_check(m_mult)?;\n                p = None;\n                n = None;\n                fcco = f_in.checked_mul(m_mult as u32);\n                fout = fcco;\n            }\n            // if !bypass_p2_div: Fout = (M / (2 x P)) x Fin\n            // if  bypass_p2_div: Fout = (M /    P   ) x Fin\n            config::SpllMode::Mode1b {\n                m_mult,\n                p_div,\n                bypass_p2_div,\n            } => {\n                bp_pre = true;\n                bp_post = false;\n                bp_post2 = bypass_p2_div;\n                m = m_check(m_mult)?;\n                p = Some(p_check(p_div)?);\n                n = None;\n                let mut div = p_div as u32;\n                if !bypass_p2_div {\n                    div *= 2;\n                }\n                fcco = f_in.checked_mul(m_mult as u32);\n                fout = (f_in / div).checked_mul(m_mult as u32);\n            }\n            // Fout = (M / N) x Fin\n            config::SpllMode::Mode1c { m_mult, n_div } => {\n                bp_pre = false;\n                bp_post = true;\n                bp_post2 = false;\n                m = m_check(m_mult)?;\n                p = None;\n                n = Some(n_check(n_div)?);\n                fcco = (f_in / (n_div as u32)).checked_mul(m_mult as u32);\n                fout = fcco;\n            }\n            // if !bypass_p2_div: Fout = (M / (N x 2 x P)) x Fin\n            // if  bypass_p2_div: Fout = (M / (  N x P  )) x Fin\n            config::SpllMode::Mode1d {\n                m_mult,\n                n_div,\n                p_div,\n                bypass_p2_div,\n            } => {\n                bp_pre = false;\n                bp_post = false;\n                bp_post2 = bypass_p2_div;\n                m = m_check(m_mult)?;\n                p = Some(p_check(p_div)?);\n                n = Some(n_check(n_div)?);\n                // This can't overflow: u8 x u8 (x 2) always fits in u32\n                let mut div = (p_div as u32) * (n_div as u32);\n                if !bypass_p2_div {\n                    div *= 2;\n                }\n                fcco = (f_in / (n_div as u32)).checked_mul(m_mult as u32);\n                fout = (f_in / div).checked_mul(m_mult as u32);\n            }\n        };\n\n        // Dump all the PLL calcs if needed for debugging\n        #[cfg(feature = \"defmt\")]\n        {\n            defmt::debug!(\"f_in: {:?}\", f_in);\n            defmt::debug!(\"bp_pre: {:?}\", bp_pre);\n            defmt::debug!(\"bp_post: {:?}\", bp_post);\n            defmt::debug!(\"bp_post2: {:?}\", bp_post2);\n            defmt::debug!(\"m: {:?}\", m);\n            defmt::debug!(\"p: {:?}\", p);\n            defmt::debug!(\"n: {:?}\", n);\n            defmt::debug!(\"fout: {:?}\", fout);\n            defmt::debug!(\"fcco: {:?}\", fcco);\n        }\n\n        // Ensure the Fcco and Fout calcs didn't overflow\n        let fcco = fcco.ok_or(ClockError::BadConfig {\n            clock: \"spll\",\n            reason: \"fcco invalid1\",\n        })?;\n        let fout = fout.ok_or(ClockError::BadConfig {\n            clock: \"spll\",\n            reason: \"fout invalid\",\n        })?;\n\n        // Fcco: 275MHz to 550MHz\n        if !(275_000_000..=550_000_000).contains(&fcco) {\n            return Err(ClockError::BadConfig {\n                clock: \"spll\",\n                reason: \"fcco invalid2\",\n            });\n        }\n\n        let limits = self.lowest_relevant_limits(&cfg.power);\n\n        // Fout: 4.3MHz to 2x Max CPU Frequency\n        let fmax = limits.cpu_clk;\n        let spll_range_bad1 = !(4_300_000..=(2 * fmax)).contains(&fout);\n        let spll_range_bad2 = fout > limits.pll1_clk;\n\n        if spll_range_bad1 || spll_range_bad2 {\n            return Err(ClockError::BadConfig {\n                clock: \"spll\",\n                reason: \"fout invalid\",\n            });\n        }\n\n        // A = floor(m / 4) + 1\n        let selp_a = (m / 4) + 1;\n        // SELP = A  if A <  31\n        //      = 31 if A >= 31\n        let selp = selp_a.min(31);\n\n        // A = 1                    if        M >= 8000\n        //   = floor(8000 / M)      if 8000 > M >= 122\n        //   = 2 x floor(M / 4) / 3 if 122  > M >= 1\n        let seli_a = if m >= 8000 {\n            1\n        } else if m >= 122 {\n            8000 / m\n        } else {\n            (2 * (m / 4)) / 3\n        };\n        // SELI = A  if A <  63\n        //      = 63 if A >= 63\n        let seli = seli_a.min(63);\n        // SELR must be 0.\n        let selr = 0;\n\n        self.scg0.spllctrl().modify(|w| {\n            w.set_source(variant);\n            w.set_selp(selp as u8);\n            w.set_seli(seli as u8);\n            w.set_selr(selr);\n        });\n\n        if let Some(n) = n {\n            self.scg0.spllndiv().modify(|w| w.set_ndiv(n));\n        }\n        if let Some(p) = p {\n            self.scg0.spllpdiv().modify(|w| w.set_pdiv(p));\n        }\n        self.scg0.spllmdiv().modify(|w| w.set_mdiv(m));\n\n        self.scg0.spllctrl().modify(|w| {\n            w.set_bypassprediv(bp_pre);\n            w.set_bypasspostdiv(bp_post);\n            w.set_bypasspostdiv2(bp_post2);\n\n            // TODO: support FRM?\n            w.set_frm(false);\n        });\n\n        // Unlock\n        self.scg0.spllcsr().modify(|w| w.set_lk(SpllcsrLk::WRITE_ENABLED));\n\n        // TODO: Support clock monitors?\n        // self.scg0.spllcsr().modify(|w| w.spllcm().?);\n\n        self.scg0.trim_lock().write(|w| {\n            w.set_trim_lock_key(0x5a5a);\n            w.set_trim_unlock(TrimUnlock::NOT_LOCKED)\n        });\n\n        // SPLLLOCK_CNFG: The lock time programmed in this register must be\n        // equal to meet the PLL 500μs lock time plus the 300 refclk count startup.\n        //\n        // LOCK_TIME = 500μs/T ref + 300, F ref = F in /N (input frequency divided by pre-divider ratio).\n        //\n        // 500us is 1/2000th of a second, therefore Fref / 2000 is the number of cycles in 500us.\n        let f_ref = if let Some(n) = n { f_in / (n as u32) } else { f_in };\n        let lock_time = f_ref.div_ceil(2000) + 300;\n        self.scg0.splllock_cnfg().write(|w| w.set_lock_time(lock_time));\n\n        // TODO: Support Spread spectrum?\n\n        let (bg_good, spllsten) = match cfg.power {\n            PoweredClock::NormalEnabledDeepSleepDisabled => (self.clocks.bandgap_active, Spllsten::DISABLED_IN_STOP),\n            PoweredClock::AlwaysEnabled => (\n                self.clocks.bandgap_active && self.clocks.bandgap_lowpower,\n                Spllsten::ENABLED_IN_STOP,\n            ),\n        };\n        if !bg_good {\n            return Err(ClockError::BadConfig {\n                clock: \"spll\",\n                reason: \"bandgap required when active\",\n            });\n        }\n\n        self.scg0.spllcsr().modify(|w| {\n            w.set_spllclken(true);\n            w.set_spllpwren(true);\n            w.set_spllsten(spllsten);\n        });\n\n        // Wait for SPLL to set up\n        loop {\n            let csr = self.scg0.spllcsr().read();\n            if csr.spll_lock() == SpllLock::ENABLED_AND_VALID {\n                if csr.spllerr() == Spllerr::ENABLED_AND_ERROR {\n                    return Err(ClockError::BadConfig {\n                        clock: \"spll\",\n                        reason: \"spllerr is set\",\n                    });\n                }\n                break;\n            }\n        }\n\n        // Re-lock SPLL CSR\n        self.scg0.spllcsr().modify(|w| w.set_lk(SpllcsrLk::WRITE_DISABLED));\n\n        // Store clock state\n        self.clocks.pll1_clk = Some(Clock {\n            frequency: fout,\n            power: cfg.power,\n        });\n\n        // Do we enable the `pll1_clk_div` output?\n        if let Some(d) = cfg.pll1_clk_div.as_ref() {\n            let exp_freq = fout / d.into_divisor();\n            if exp_freq > limits.pll1_clk_div {\n                return Err(ClockError::BadConfig {\n                    clock: \"pll1_clk_div\",\n                    reason: \"exceeds max frequency\",\n                });\n            }\n\n            // Halt and reset the div; then set our desired div.\n            self.syscon.pll1clkdiv().write(|w| {\n                w.set_halt(Pll1clkdivHalt::HALT);\n                w.set_reset(Pll1clkdivReset::ASSERTED);\n                w.set_div(d.into_bits());\n            });\n            // Then unhalt it, and reset it\n            self.syscon.pll1clkdiv().write(|w| {\n                w.set_halt(Pll1clkdivHalt::RUN);\n                w.set_reset(Pll1clkdivReset::RELEASED);\n            });\n\n            // Wait for clock to stabilize\n            while self.syscon.pll1clkdiv().read().unstab() == Pll1clkdivUnstab::ONGOING {}\n\n            // Store off the clock info\n            self.clocks.pll1_clk_div = Some(Clock {\n                frequency: exp_freq,\n                power: cfg.power,\n            });\n        }\n\n        Ok(())\n    }\n\n    pub(super) fn configure_main_clk(&mut self) -> Result<(), ClockError> {\n        let (var, name, clk) = match self.config.main_clock.source {\n            #[cfg(not(feature = \"sosc-as-gpio\"))]\n            MainClockSource::SoscClkIn => (Scs::SOSC, \"clk_in\", self.clocks.clk_in.as_ref()),\n            MainClockSource::SircFro12M => (Scs::SIRC, \"fro_12m\", self.clocks.fro_12m.as_ref()),\n            MainClockSource::FircHfRoot => (Scs::FIRC, \"fro_hf_root\", self.clocks.fro_hf_root.as_ref()),\n            #[cfg(feature = \"mcxa2xx\")]\n            MainClockSource::RoscFro16K => (Scs::ROSC, \"fro16k\", self.clocks.clk_16k_vdd_core.as_ref()),\n            #[cfg(all(feature = \"mcxa5xx\", not(feature = \"rosc-32k-as-gpio\")))]\n            MainClockSource::RoscOsc32K => (Scs::ROSC, \"osc32k\", self.clocks.clk_32k_vdd_core.as_ref()),\n            MainClockSource::SPll1 => (Scs::SPLL, \"pll1_clk\", self.clocks.pll1_clk.as_ref()),\n        };\n        let Some(main_clk_src) = clk else {\n            return Err(ClockError::BadConfig {\n                clock: name,\n                reason: \"Needed for main_clock but not enabled\",\n            });\n        };\n\n        if !main_clk_src.power.meets_requirement_of(&self.config.main_clock.power) {\n            return Err(ClockError::BadConfig {\n                clock: name,\n                reason: \"Needed for main_clock but not low power\",\n            });\n        }\n\n        let lowest_limits = self.lowest_relevant_limits(&self.config.main_clock.power);\n        let active_limits = self.active_limits();\n\n        let (levels, wsmax) = match self.config.vdd_power.active_mode.level {\n            VddLevel::MidDriveMode => (\n                clock_limits::VDD_CORE_MID_DRIVE_WAIT_STATE_LIMITS,\n                clock_limits::VDD_CORE_MID_DRIVE_MAX_WAIT_STATES,\n            ),\n            #[cfg(feature = \"mcxa5xx\")]\n            VddLevel::NormalMode => (\n                clock_limits::VDD_CORE_NORMAL_DRIVE_WAIT_STATE_LIMITS,\n                clock_limits::VDD_CORE_NORMAL_DRIVE_MAX_WAIT_STATES,\n            ),\n            VddLevel::OverDriveMode => (\n                clock_limits::VDD_CORE_OVER_DRIVE_WAIT_STATE_LIMITS,\n                clock_limits::VDD_CORE_OVER_DRIVE_MAX_WAIT_STATES,\n            ),\n        };\n\n        // Is the main_clk source in range for main_clk?\n        if main_clk_src.frequency > lowest_limits.main_clk {\n            return Err(ClockError::BadConfig {\n                clock: name,\n                reason: \"Exceeds main_clock frequency\",\n            });\n        }\n\n        // Calculate expected CPU frequency based on main_clk and AHB div\n        let ahb_div = self.config.main_clock.ahb_clk_div;\n        let cpu_freq = main_clk_src.frequency / ahb_div.into_divisor();\n\n        // Is the expected CPU frequency in range for cpu_clk? Note: the CPU\n        // is never running in deep sleep, so we directly use the active limits here\n        if cpu_freq > active_limits.cpu_clk {\n            return Err(ClockError::BadConfig {\n                clock: name,\n                reason: \"Exceeds ahb max frequency\",\n            });\n        }\n\n        // BEFORE we switch, update the flash wait states to the appropriate levels\n        //\n        // NOTE: \"cpu_clk\" is the same as \"system_clk\". Table 22 is not clear exactly\n        // WHICH source clock the limits apply to, but system/ahb/cpu is a fair bet.\n        //\n        // TODO: This calculation doesn't consider low power mode yet!\n        let wait_states = levels\n            .iter()\n            .find(|(fmax, _ws)| cpu_freq <= *fmax)\n            .map(|t| t.1)\n            .unwrap_or(wsmax);\n        self.fmu0.fctrl().modify(|w| w.set_rwsc(wait_states));\n\n        // TODO: (Double) check if clock is actually valid before switching?\n        // Are we already on the right clock?\n        let now = self.scg0.csr().read().scs();\n        if now != var {\n            // Set RCCR\n            self.scg0.rccr().modify(|w| w.set_scs(var));\n\n            // Wait for match\n            while self.scg0.csr().read().scs() != var {}\n        }\n\n        // The main_clk is now set to the selected input clock\n        self.clocks.main_clk = Some(main_clk_src.clone());\n\n        // Update AHB clock division, if necessary\n        if ahb_div.into_bits() != 0 {\n            // AHB has no halt/reset fields - it's different to other DIV8s!\n            self.syscon.ahbclkdiv().modify(|w| w.set_div(ahb_div.into_bits()));\n            // Wait for clock to stabilize\n            while self.syscon.ahbclkdiv().read().unstab() == AhbclkdivUnstab::ONGOING {}\n        }\n\n        // Store off the clock info\n        self.clocks.cpu_system_clk = Some(Clock {\n            frequency: cpu_freq,\n            power: main_clk_src.power,\n        });\n\n        Ok(())\n    }\n\n    pub(super) fn configure_voltages(&mut self) -> Result<(), ClockError> {\n        // Determine if we need to change the active mode voltage levels\n        let to_change = match self.config.vdd_power.active_mode.level {\n            VddLevel::MidDriveMode => {\n                // This is the default mode, I don't believe we need to do anything.\n                //\n                // \"The LVDE and HVDE fields reset only with a POR.\n                // All other fields reset only with a system reset.\"\n                None\n            }\n            #[cfg(feature = \"mcxa5xx\")]\n            VddLevel::NormalMode => {\n                // TODO: fix PAC fields, this is SRAM1V1\n                Some((ActiveCfgCoreldoVddLvl::NORMAL, Vsm::_RESERVED_2))\n            }\n            VddLevel::OverDriveMode => Some((ActiveCfgCoreldoVddLvl::OVER, Vsm::SRAM1V2)),\n        };\n\n        if let Some((vdd, vsm)) = to_change {\n            // You can change the core VDD levels for the LDO_CORE low power regulator only\n            // when CORELDO_VDD_DS=1.\n            //\n            // When switching CORELDO_VDD_DS from low to normal drive strength, ensure the LDO_CORE high\n            // VDD LVL setting is set to the same level that was set prior to switching to the LDO_CORE drive strength\n            // (CORELDO_VDD_DS). Otherwise, if the LVDs are enabled, an unexpected LVD can occur.\n            //\n            // Ensure drive strength is normal (BEFORE shifting level)\n            self.spc0\n                .active_cfg()\n                .modify(|w| w.set_coreldo_vdd_ds(ActiveCfgCoreldoVddDs::NORMAL));\n\n            // ## DS 26.3.2:\n            //\n            // When increasing voltage and frequency in Active mode, you must perform the following steps:\n            //\n            // 1. Increase voltage to a new level (ACTIVE_CFG[CORELDO_VDD_LVL]).\n            self.spc0.active_cfg().modify(|w| w.set_coreldo_vdd_lvl(vdd));\n\n            // 2. Wait for voltage change to complete (SC[BUSY] = 0).\n            while self.spc0.sc().read().busy() {}\n\n            // 3. Configure flash memory to support higher voltage level and frequency (FMU_FCTRL[RWSC].\n            //\n            // NOTE: This step skipped - we will update RWSC when we later apply main cpu clock\n            // frequency changes.\n\n            // 4. Configure SRAM to support higher voltage levels (SRAMCTL[VSM]).\n            self.spc0.sramctl().modify(|w| w.set_vsm(vsm));\n\n            // 5. Request SRAM voltage update (write 1 to SRAMCTL[REQ]).\n            self.spc0.sramctl().modify(|w| w.set_req(true));\n\n            // 6. Wait for SRAM voltage change to complete (SRAMCTL[ACK] = 1).\n            while !self.spc0.sramctl().read().ack() {}\n\n            // 7. Clear request for SRAM voltage change (write 0 to SRAMCTL[REQ]).\n            self.spc0.sramctl().modify(|w| w.set_req(false));\n\n            // 8. Increase frequency to a new level (for example, SCG_RCCR).\n            //\n            // NOTE: This step skipped - we will update RCCR when we later apply main cpu clock\n            // frequency changes.\n\n            // 9. You can continue execution.\n            // :)\n        }\n\n        // If the CORELDO_VDD_DS fields are set to the same value in both the ACTIVE_CFG and LP_CFG registers,\n        // the CORELDO_VDD_LVL's in the ACTIVE_CFG and LP_CFG register must be set to the same voltage\n        // level settings.\n        //\n        // TODO(AJM): I don't really understand this! Enforce it literally for now I guess.\n        const BAD_ASCENDING: Result<(), ClockError> = Err(ClockError::BadConfig {\n            clock: \"vdd_power\",\n            reason: \"Deep sleep can't have higher level than active mode\",\n        });\n        let ds_match = self.config.vdd_power.active_mode.drive == self.config.vdd_power.low_power_mode.drive;\n        let (vdd_match, lpwkup) = match (\n            self.config.vdd_power.active_mode.level,\n            self.config.vdd_power.low_power_mode.level,\n        ) {\n            //\n            // Correct \"descending\" options\n            //\n            // When voltage levels are not the same between ACTIVE mode and Low Power mode, you must write a\n            // nonzero value to SPC->LPWKUP_DELAY.\n            //\n            // This SHOULD be covered by table 165. LPWKUP Delay, but it doesn't actually have\n            // a value for the 1.0v-1.2v transition we need. For now, the C SDK always uses 0x5B.\n            #[cfg(feature = \"mcxa5xx\")]\n            (VddLevel::OverDriveMode, VddLevel::NormalMode) => (false, 0x005b),\n            (VddLevel::OverDriveMode, VddLevel::MidDriveMode) => (false, 0x005b),\n            #[cfg(feature = \"mcxa5xx\")]\n            (VddLevel::NormalMode, VddLevel::MidDriveMode) => (false, 0x005b),\n\n            //\n            // Incorrect \"ascending\" options\n            //\n            // For now, enforce that active is always >= voltage to low power. I don't know if this\n            // is required, but there's probably also no reason to support it?\n            #[cfg(feature = \"mcxa5xx\")]\n            (VddLevel::MidDriveMode, VddLevel::NormalMode) => return BAD_ASCENDING,\n            (VddLevel::MidDriveMode, VddLevel::OverDriveMode) => return BAD_ASCENDING,\n            #[cfg(feature = \"mcxa5xx\")]\n            (VddLevel::NormalMode, VddLevel::OverDriveMode) => return BAD_ASCENDING,\n\n            // Correct \"matching\" options\n            (VddLevel::MidDriveMode, VddLevel::MidDriveMode) => (true, 0x0000),\n            #[cfg(feature = \"mcxa5xx\")]\n            (VddLevel::NormalMode, VddLevel::NormalMode) => (true, 0x0000),\n            (VddLevel::OverDriveMode, VddLevel::OverDriveMode) => (true, 0x0000),\n        };\n        self.spc0.lpwkup_delay().write(|w| w.set_lpwkup_delay(lpwkup));\n\n        if ds_match && !vdd_match {\n            return Err(ClockError::BadConfig {\n                clock: \"vdd_power\",\n                reason: \"DS matches but LVL mismatches!\",\n            });\n        }\n\n        // You can change the core VDD levels for the LDO_CORE low power regulator only when\n        // ACTIVE_CFG[CORELDO_VDD_DS] = 1. So, before entering any of the low-power states (DSLEEP,\n        // PDOWN, DPDOWN) with LDO_CORE low power regulator selected (LP_CFG[CORELDO_VDD_DS] = 0),\n        // you must use CORELDO_VDD_LVL to select the correct regulation level during ACTIVE run mode.\n        //\n        // NOTE(AJM): We've set drive strength to \"normal\" above, and do not (potentially) set it to\n        // \"low\" until later below.\n\n        // NOTE(AJM): The reference manual doesn't have any similar configuration requirements\n        // for low power mode. We'll just configure it, I guess?\n        //\n        // NOTE(AJM): \"LP_CFG: This register resets only after a POR or LVD event.\"\n        let (ds, bgap) = match self.config.vdd_power.low_power_mode.drive {\n            VddDriveStrength::Low { enable_bandgap } => {\n                // If the bandgap is enabled, also enable the high/low voltage\n                // detectors. if it is disabled, these must also be disabled.\n                self.spc0.lp_cfg().modify(|w| {\n                    w.set_sys_hvde(enable_bandgap);\n                    w.set_sys_lvde(enable_bandgap);\n                    w.set_core_lvde(enable_bandgap);\n                });\n\n                (pac::spc::vals::LpCfgCoreldoVddDs::LOW, enable_bandgap)\n            }\n            VddDriveStrength::Normal => {\n                // \"If you specify normal drive strength, you must write a value to LP[BGMODE] that enables the bandgap.\"\n                (pac::spc::vals::LpCfgCoreldoVddDs::NORMAL, true)\n            }\n        };\n        let lvl = match self.config.vdd_power.low_power_mode.level {\n            VddLevel::MidDriveMode => LpCfgCoreldoVddLvl::MID,\n            #[cfg(feature = \"mcxa5xx\")]\n            VddLevel::NormalMode => LpCfgCoreldoVddLvl::NORMAL,\n            VddLevel::OverDriveMode => LpCfgCoreldoVddLvl::OVER,\n        };\n        self.spc0.lp_cfg().modify(|w| w.set_coreldo_vdd_ds(ds));\n\n        // If we're enabling the bandgap, ensure we do it BEFORE changing the VDD level\n        // If we're disabling the bandgap, ensure we do it AFTER changing the VDD level\n        if bgap {\n            self.spc0.lp_cfg().modify(|w| w.set_bgmode(LpCfgBgmode::BGMODE01));\n            self.spc0.lp_cfg().modify(|w| w.set_coreldo_vdd_lvl(lvl));\n        } else {\n            self.spc0.lp_cfg().modify(|w| w.set_coreldo_vdd_lvl(lvl));\n            self.spc0.lp_cfg().modify(|w| w.set_bgmode(LpCfgBgmode::BGMODE0));\n        }\n        self.clocks.bandgap_lowpower = bgap;\n\n        // Updating CORELDO_VDD_LVL sets the SC[BUSY] flag. That flag remains set for at least the total time\n        // delay that Active Voltage Trim Delay (ACTIVE_VDELAY) specifies.\n        //\n        // Before changing CORELDO_VDD_LVL, you must wait until the SC[BUSY] flag clears before entering the\n        // selected low-power sleep\n        //\n        // NOTE(AJM): Let's just proactively wait now so we don't have to worry about it on subsequent sleeps\n        while self.spc0.sc().read().busy() {}\n\n        // NOTE(AJM): I don't really know if this is valid! I'm guessing in most cases you would want to\n        // use the low drive strength for lp mode, and high drive strength for active mode?\n        match self.config.vdd_power.active_mode.drive {\n            VddDriveStrength::Low { enable_bandgap } => {\n                // If the bandgap is enabled, also enable the high/low voltage\n                // detectors. if it is disabled, these must also be disabled.\n                self.spc0.active_cfg().modify(|w| {\n                    w.set_sys_hvde(enable_bandgap);\n                    w.set_sys_lvde(enable_bandgap);\n                    w.set_core_lvde(enable_bandgap);\n                });\n\n                // optionally disable bandgap AFTER setting vdd strength to low\n                self.spc0\n                    .active_cfg()\n                    .modify(|w| w.set_coreldo_vdd_ds(ActiveCfgCoreldoVddDs::LOW));\n                self.spc0.active_cfg().modify(|w| {\n                    if enable_bandgap {\n                        w.set_bgmode(ActiveCfgBgmode::BGMODE01)\n                    } else {\n                        w.set_bgmode(ActiveCfgBgmode::BGMODE0)\n                    }\n                });\n\n                self.clocks.bandgap_active = enable_bandgap;\n            }\n            VddDriveStrength::Normal => {\n                // Already set to normal above\n                self.clocks.bandgap_active = true;\n            }\n        }\n\n        // NOTE: calling `` still marks the core peripherals as taken. See\n        // https://github.com/embassy-rs/embassy/issues/5563 for discussion. Since this\n        // is a ZST, transmuting from `()` is reasonable.\n        let mut scb: SCB = unsafe { core::mem::transmute(()) };\n\n        // Apply sleep settings\n        match self.config.vdd_power.core_sleep {\n            CoreSleep::WfeUngated => {\n                // Do not gate\n                self.cmc.ckctrl().modify(|w| w.set_ckmode(CkctrlCkmode::CKMODE0000));\n\n                // Debug is enabled when core sleeps\n                self.cmc.dbgctl().modify(|w| w.set_sod(false));\n\n                // Don't allow the core to be gated to avoid killing the debugging session\n                scb.clear_sleepdeep();\n            }\n            CoreSleep::WfeGated => {\n                // Allow automatic gating of the core when in LIGHT sleep\n                self.cmc.ckctrl().modify(|w| w.set_ckmode(CkctrlCkmode::CKMODE0001));\n\n                // Debug is disabled when core sleeps\n                self.cmc.dbgctl().modify(|w| w.set_sod(true));\n\n                // Allow the core to be gated - this WILL kill the debugging session!\n                scb.set_sleepdeep();\n            }\n            CoreSleep::DeepSleep => {\n                // We can only support deep sleep with a custom executor which properly\n                // handles going to sleep and returning\n                #[cfg(all(not(feature = \"executor-platform\"), feature = \"defmt\"))]\n                defmt::warn!(\"deep sleep enabled without custom executor\");\n\n                // For now, just enable light sleep. The executor will set deep sleep when\n                // appropriate\n                self.cmc.ckctrl().modify(|w| w.set_ckmode(CkctrlCkmode::CKMODE0001));\n\n                // Debug is disabled when core sleeps\n                self.cmc.dbgctl().modify(|w| w.set_sod(true));\n\n                // Allow the core to be gated - this WILL kill the debugging session!\n                scb.set_sleepdeep();\n\n                // Enable sevonpend, to allow us to wake from WFE sleep with interrupts disabled\n                unsafe {\n                    // TODO: wait for https://github.com/rust-embedded/cortex-m/commit/1be630fdd06990bd14251eabe4cca9307bde549d\n                    // to be released, until then, manual version of SCB.set_sevonpend();\n                    scb.scr.modify(|w| w | (1 << 4));\n                }\n            }\n        }\n        self.clocks.core_sleep = self.config.vdd_power.core_sleep;\n\n        // Allow automatic gating of the flash memory\n        let (wake, doze) = match self.config.vdd_power.flash_sleep {\n            config::FlashSleep::Never => (false, false),\n            config::FlashSleep::FlashDoze => (false, true),\n            config::FlashSleep::FlashDozeWithFlashWake => (true, true),\n        };\n\n        self.cmc.flashcr().modify(|w| {\n            w.set_flashdoze(doze);\n            w.set_flashwake(wake);\n        });\n\n        // At init, disable all analog peripherals. These can be re-enabled\n        // if necessary for HAL drivers.\n        self.spc0.active_cfg1().write(|w| w.0 = 0);\n        self.spc0.lp_cfg1().write(|w| w.0 = 0);\n\n        // Update status\n        self.clocks.active_power = self.config.vdd_power.active_mode.level;\n        self.clocks.lp_power = self.config.vdd_power.low_power_mode.level;\n\n        Ok(())\n    }\n}\n"
  },
  {
    "path": "embassy-mcxa/src/clocks/periph_helpers/mcxa2xx.rs",
    "content": "//! MCXA2xx only peripheral clocks helpers.\n"
  },
  {
    "path": "embassy-mcxa/src/clocks/periph_helpers/mcxa5xx.rs",
    "content": "//! MCXA5xx only peripheral clocks helpers.\n"
  },
  {
    "path": "embassy-mcxa/src/clocks/periph_helpers/mod.rs",
    "content": "//! Peripheral Helpers\n//!\n//! The purpose of this module is to define the per-peripheral special handling\n//! required from a clocking perspective. Different peripherals have different\n//! selectable source clocks, and some peripherals have additional pre-dividers\n//! that can be used.\n//!\n//! See the docs of [`SPConfHelper`] for more details.\n\nuse super::{ClockError, Clocks, PoweredClock, WakeGuard};\nuse crate::clocks::VddLevel;\nuse crate::pac::mrcc::vals::{\n    AdcClkselMux, ClkdivHalt, ClkdivReset, ClkdivUnstab, CtimerClkselMux, FclkClkselMux, Lpi2cClkselMux,\n    LpspiClkselMux, LpuartClkselMux, OstimerClkselMux,\n};\n\n#[cfg(feature = \"mcxa2xx\")]\nmod mcxa2xx;\n\n#[allow(unused_imports)]\n#[cfg(feature = \"mcxa2xx\")]\npub use mcxa2xx::*;\n\n#[cfg(feature = \"mcxa5xx\")]\nmod mcxa5xx;\n\n#[allow(unused_imports)]\n#[cfg(feature = \"mcxa5xx\")]\npub use mcxa5xx::*;\n\n#[must_use]\npub struct PreEnableParts {\n    /// The frequency fed into the peripheral, taking into account the selected\n    /// source clock, as well as any pre-divisors.\n    pub freq: u32,\n    /// The wake guard, if necessary for the selected clock source\n    pub wake_guard: Option<WakeGuard>,\n}\n\nimpl PreEnableParts {\n    pub fn empty() -> Self {\n        Self {\n            freq: 0,\n            wake_guard: None,\n        }\n    }\n}\n\n/// Sealed Peripheral Configuration Helper\n///\n/// NOTE: the name \"sealed\" doesn't *totally* make sense because its not sealed yet in the\n/// embassy-mcxa project, but it derives from embassy-imxrt where it is. We should\n/// fix the name, or actually do the sealing of peripherals.\n///\n/// This trait serves to act as a per-peripheral customization for clocking behavior.\n///\n/// This trait should be implemented on a configuration type for a given peripheral, and\n/// provide the methods that will be called by the higher level operations like\n/// `embassy_mcxa::clocks::enable_and_reset()`.\npub trait SPConfHelper {\n    /// This method is called AFTER a given MRCC peripheral has been disabled, and BEFORE\n    /// the peripheral is to be enabled.\n    ///\n    /// This function SHOULD NOT make any changes to the system clock configuration, even\n    /// unsafely, as this should remain static for the duration of the program.\n    ///\n    /// This function should check that any relevant upstream clocks are enabled, are in a\n    /// reasonable power state, and that the requested configuration can be made. If any of\n    /// these checks fail, an `Err(ClockError)` should be returned, likely `ClockError::BadConfig`.\n    ///\n    /// This function WILL be called in a critical section, care should be taken not to delay\n    /// for an unreasonable amount of time.\n    ///\n    /// On success, this function MUST return an `Ok(parts)`.\n    fn pre_enable_config(&self, clocks: &Clocks) -> Result<PreEnableParts, ClockError>;\n}\n\n/// Copy and paste macro that:\n///\n/// * Sets the clocksel mux to `$selvar`\n/// * Resets and halts the div, and applies the calculated div4 bits\n/// * Releases reset + halt\n/// * Waits for the div to stabilize\n/// * Returns `Ok($freq / $conf.div.into_divisor())`\n///\n/// Assumes:\n///\n/// * self is a configuration struct that has fields called:\n///   * `div`, which is a `Div4`\n///   * `power`, which is a `PoweredClock`\n///\n/// usage:\n///\n/// ```rust,ignore\n/// apply_div4!(self, clksel, clkdiv, variant, freq)\n/// ```\n///\n/// In the future if we make all the clksel+clkdiv pairs into commonly derivedFrom\n/// registers, or if we put some kind of simple trait around those regs, we could\n/// do this with something other than a macro, but for now, this is harm-reduction\n/// to avoid incorrect copy + paste\n#[doc(hidden)]\n#[macro_export]\nmacro_rules! apply_div4 {\n    ($conf:ident, $selreg:ident, $divreg:ident, $selvar:ident, $freq:ident) => {{\n        // set clksel\n        $selreg.modify(|w| w.set_mux($selvar));\n\n        // Set up clkdiv\n        $divreg.modify(|w| {\n            w.set_div($conf.div.into_bits());\n            w.set_halt(ClkdivHalt::OFF);\n            w.set_reset(ClkdivReset::OFF);\n        });\n        $divreg.modify(|w| {\n            w.set_halt(ClkdivHalt::ON);\n            w.set_reset(ClkdivReset::ON);\n        });\n\n        while $divreg.read().unstab() == ClkdivUnstab::OFF {}\n\n        Ok(PreEnableParts {\n            freq: $freq / $conf.div.into_divisor(),\n            wake_guard: WakeGuard::for_power(&$conf.power),\n        })\n    }};\n}\n\n// config types\n\n/// This type represents a divider in the range 1..=16.\n///\n/// At a hardware level, this is an 8-bit register from 0..=15,\n/// which adds one.\n///\n/// While the *clock* domain seems to use 8-bit dividers, the *peripheral* domain\n/// seems to use 4 bit dividers!\n#[derive(Copy, Clone, Debug, PartialEq, Eq)]\npub struct Div4(pub(super) u8);\n\nimpl Div4 {\n    /// Divide by one, or no division\n    pub const fn no_div() -> Self {\n        Self(0)\n    }\n\n    /// Store a \"raw\" divisor value that will divide the source by\n    /// `(n + 1)`, e.g. `Div4::from_raw(0)` will divide the source\n    /// by 1, and `Div4::from_raw(15)` will divide the source by\n    /// 16.\n    pub const fn from_raw(n: u8) -> Option<Self> {\n        if n > 0b1111 { None } else { Some(Self(n)) }\n    }\n\n    /// Store a specific divisor value that will divide the source\n    /// by `n`. e.g. `Div4::from_divisor(1)` will divide the source\n    /// by 1, and `Div4::from_divisor(16)` will divide the source\n    /// by 16.\n    ///\n    /// Will return `None` if `n` is not in the range `1..=16`.\n    /// Consider [`Self::from_raw`] for an infallible version.\n    pub const fn from_divisor(n: u8) -> Option<Self> {\n        let Some(n) = n.checked_sub(1) else {\n            return None;\n        };\n        if n > 0b1111 {\n            return None;\n        }\n        Some(Self(n))\n    }\n\n    /// Convert into \"raw\" bits form\n    #[inline(always)]\n    pub const fn into_bits(self) -> u8 {\n        self.0\n    }\n\n    /// Convert into \"divisor\" form, as a u32 for convenient frequency math\n    #[inline(always)]\n    pub const fn into_divisor(self) -> u32 {\n        self.0 as u32 + 1\n    }\n}\n\n/// A basic type that always returns an error when `post_enable_config` is called.\n///\n/// Should only be used as a placeholder.\npub struct UnimplementedConfig;\n\nimpl SPConfHelper for UnimplementedConfig {\n    fn pre_enable_config(&self, _clocks: &Clocks) -> Result<PreEnableParts, ClockError> {\n        Err(ClockError::UnimplementedConfig)\n    }\n}\n\n/// A basic type that always returns `Ok` when `PreEnableParts` is called.\n///\n/// This should only be used for peripherals that are \"ambiently\" clocked, like `PORTn`\n/// peripherals, which have no selectable/configurable source clock.\npub struct NoConfig;\nimpl SPConfHelper for NoConfig {\n    fn pre_enable_config(&self, _clocks: &Clocks) -> Result<PreEnableParts, ClockError> {\n        Ok(PreEnableParts::empty())\n    }\n}\n\n/// A basic type that always returns `Ok` when `PreEnableParts` is called.\n///\n/// This should only be used for peripherals that are clocked only by\n/// the CLK1M clock and have no other selectable/configurable source\n/// clock.\npub struct Clk1MConfig;\nimpl SPConfHelper for Clk1MConfig {\n    fn pre_enable_config(&self, _clocks: &Clocks) -> Result<PreEnableParts, ClockError> {\n        Ok(PreEnableParts {\n            freq: 1_000_000,\n            wake_guard: None,\n        })\n    }\n}\n\n//\n// Adc\n//\n\n/// Selectable clocks for the ADC peripheral\n#[derive(Copy, Clone, Debug, PartialEq, Eq)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub enum AdcClockSel {\n    /// Divided `fro_lf`/`clk_12m`/FRO12M source\n    FroLfDiv,\n    /// Gated `fro_hf`/`FRO180M` source\n    FroHf,\n    /// External Clock Source\n    #[cfg(not(feature = \"sosc-as-gpio\"))]\n    ClkIn,\n    // /// USB PLL Clk\n    // #[cfg(feature = \"mcxa5xx\")]\n    // UsbPllClk,\n    /// 1MHz clock sourced by a divided `fro_lf`/`clk_12m`\n    Clk1M,\n    /// Internal PLL output, with configurable divisor\n    Pll1ClkDiv,\n    /// No clock/disabled\n    None,\n}\n\n/// Top level configuration for the ADC peripheral\npub struct AdcConfig {\n    /// Power state required for this peripheral\n    pub power: PoweredClock,\n    /// Selected clock-source for this peripheral\n    pub source: AdcClockSel,\n    /// Pre-divisor, applied to the upstream clock output\n    pub div: Div4,\n}\n\nimpl SPConfHelper for AdcConfig {\n    fn pre_enable_config(&self, clocks: &Clocks) -> Result<PreEnableParts, ClockError> {\n        let mrcc0 = crate::pac::MRCC0;\n\n        let (freq, variant) = match self.source {\n            AdcClockSel::FroLfDiv => {\n                let freq = clocks.ensure_fro_lf_div_active(&self.power)?;\n\n                // TODO: fix PAC names for consistency\n                #[cfg(feature = \"mcxa2xx\")]\n                let mux = AdcClkselMux::CLKROOT_FUNC_0;\n                #[cfg(feature = \"mcxa5xx\")]\n                let mux = AdcClkselMux::I0_CLKROOT_SIRC_DIV;\n\n                (freq, mux)\n            }\n            AdcClockSel::FroHf => {\n                let freq = clocks.ensure_fro_hf_active(&self.power)?;\n\n                // TODO: fix PAC names for consistency\n                #[cfg(feature = \"mcxa2xx\")]\n                let mux = AdcClkselMux::CLKROOT_FUNC_1;\n                #[cfg(feature = \"mcxa5xx\")]\n                let mux = AdcClkselMux::I1_CLKROOT_FIRC_GATED;\n\n                (freq, mux)\n            }\n            #[cfg(not(feature = \"sosc-as-gpio\"))]\n            AdcClockSel::ClkIn => {\n                let freq = clocks.ensure_clk_in_active(&self.power)?;\n\n                // TODO: fix PAC names for consistency\n                #[cfg(feature = \"mcxa2xx\")]\n                let mux = AdcClkselMux::CLKROOT_FUNC_3;\n                #[cfg(feature = \"mcxa5xx\")]\n                let mux = AdcClkselMux::I3_CLKROOT_SOSC;\n\n                (freq, mux)\n            }\n            // #[cfg(feature = \"mcxa5xx\")]\n            // AdcClockSel::UsbPllClk => {\n            //     let freq = clocks.ensure_usb_pll_clk_active(&self.power)?;\n            //     let mux = AdcClkselMux::I4_CLKROOT_USBPFD;\n            //     (freq, mux)\n            // }\n            AdcClockSel::Clk1M => {\n                let freq = clocks.ensure_clk_1m_active(&self.power)?;\n\n                // TODO: fix PAC names for consistency\n                #[cfg(feature = \"mcxa2xx\")]\n                let mux = AdcClkselMux::CLKROOT_FUNC_5;\n                #[cfg(feature = \"mcxa5xx\")]\n                let mux = AdcClkselMux::I5_CLKROOT_1M;\n\n                (freq, mux)\n            }\n            AdcClockSel::Pll1ClkDiv => {\n                let freq = clocks.ensure_pll1_clk_div_active(&self.power)?;\n\n                // TODO: fix PAC names for consistency\n                #[cfg(feature = \"mcxa2xx\")]\n                let mux = AdcClkselMux::CLKROOT_FUNC_6;\n                #[cfg(feature = \"mcxa5xx\")]\n                let mux = AdcClkselMux::I6_CLKROOT_SPLL_DIV;\n\n                (freq, mux)\n            }\n            AdcClockSel::None => {\n                mrcc0.mrcc_adc_clksel().write(|w| {\n                    // no ClkrootFunc7, just write manually for now\n                    w.set_mux(AdcClkselMux::_RESERVED_7)\n                });\n                mrcc0.mrcc_adc_clkdiv().modify(|w| {\n                    w.set_reset(ClkdivReset::ON);\n                    w.set_halt(ClkdivHalt::ON);\n                });\n                return Ok(PreEnableParts::empty());\n            }\n        };\n        let clksel = mrcc0.mrcc_adc_clksel();\n        let clkdiv = mrcc0.mrcc_adc_clkdiv();\n\n        // Check clock speed is reasonable\n        let div = self.div.into_divisor();\n        let expected = freq / div;\n        // 22.3.2 peripheral clock max functional clock limits\n        let power = match self.power {\n            PoweredClock::NormalEnabledDeepSleepDisabled => clocks.active_power,\n            PoweredClock::AlwaysEnabled => clocks.lp_power,\n        };\n\n        #[cfg(feature = \"mcxa2xx\")]\n        let fmax = match power {\n            VddLevel::MidDriveMode => 24_000_000,\n            VddLevel::OverDriveMode => 64_000_000,\n        };\n\n        #[cfg(feature = \"mcxa5xx\")]\n        let fmax = match power {\n            VddLevel::MidDriveMode => 24_000_000,\n            VddLevel::NormalMode | VddLevel::OverDriveMode => 64_000_000,\n        };\n\n        if expected > fmax {\n            return Err(ClockError::BadConfig {\n                clock: \"adc fclk\",\n                reason: \"exceeds max rating\",\n            });\n        }\n\n        apply_div4!(self, clksel, clkdiv, variant, freq)\n    }\n}\n\n//\n// OSTimer\n//\n\n/// Selectable clocks for the OSTimer peripheral\n#[derive(Copy, Clone, Debug, PartialEq, Eq)]\npub enum OstimerClockSel {\n    /// 16k clock, sourced from FRO16K (Vdd Core)\n    Clk16kVddCore,\n    /// 1 MHz Clock sourced from FRO12M\n    Clk1M,\n    /// Disabled\n    None,\n}\n\n/// Top level configuration for the `OSTimer` peripheral\npub struct OsTimerConfig {\n    /// Power state required for this peripheral\n    pub power: PoweredClock,\n    /// Selected clock source for this peripheral\n    pub source: OstimerClockSel,\n}\n\nimpl SPConfHelper for OsTimerConfig {\n    fn pre_enable_config(&self, clocks: &Clocks) -> Result<PreEnableParts, ClockError> {\n        let mrcc0 = crate::pac::MRCC0;\n        // NOTE: complies with 22.3.2 peripheral clock max functional clock limits\n        // which is 1MHz, and we can only select 1mhz/16khz.\n        Ok(match self.source {\n            OstimerClockSel::Clk16kVddCore => {\n                // TODO: fix PAC names for consistency\n                #[cfg(feature = \"mcxa2xx\")]\n                let mux = OstimerClkselMux::CLKROOT_16K;\n                #[cfg(feature = \"mcxa5xx\")]\n                let mux = OstimerClkselMux::I0_CLKROOT_16K;\n\n                let freq = clocks.ensure_clk_16k_vdd_core_active(&self.power)?;\n                mrcc0.mrcc_ostimer0_clksel().write(|w| w.set_mux(mux));\n                PreEnableParts {\n                    freq,\n                    wake_guard: WakeGuard::for_power(&self.power),\n                }\n            }\n            OstimerClockSel::Clk1M => {\n                let freq = clocks.ensure_clk_1m_active(&self.power)?;\n                // TODO: fix PAC names for consistency\n                #[cfg(feature = \"mcxa2xx\")]\n                let mux = OstimerClkselMux::CLKROOT_1M;\n                #[cfg(feature = \"mcxa5xx\")]\n                let mux = OstimerClkselMux::I2_CLKROOT_1M;\n\n                mrcc0.mrcc_ostimer0_clksel().write(|w| w.set_mux(mux));\n                PreEnableParts {\n                    freq,\n                    wake_guard: WakeGuard::for_power(&self.power),\n                }\n            }\n            OstimerClockSel::None => {\n                mrcc0\n                    .mrcc_ostimer0_clksel()\n                    .write(|w| w.set_mux(OstimerClkselMux::_RESERVED_3));\n                PreEnableParts::empty()\n            }\n        })\n    }\n}\n\n//\n// LPSPI\n//\n\n/// Selectable clocks for `Lpspi` peripherals\n#[derive(Debug, Clone, Copy)]\npub enum LpspiClockSel {\n    /// FRO12M/FRO_LF/SIRC clock source, passed through divider\n    /// \"fro_lf_div\"\n    FroLfDiv,\n    /// FRO180M/FRO192M/FRO_HF/FIRC clock source, passed through divider\n    /// \"fro_hf_div\"\n    FroHfDiv,\n    /// SOSC/XTAL/EXTAL clock source\n    #[cfg(not(feature = \"sosc-as-gpio\"))]\n    ClkIn,\n    /// clk_1m/FRO_LF divided by 12\n    Clk1M,\n    /// Output of PLL1, passed through clock divider,\n    /// \"pll1_clk_div\", maybe \"pll1_lf_div\"?\n    Pll1ClkDiv,\n    /// Disabled\n    None,\n}\n\n/// Which instance of the `Lpspi` is this?\n///\n/// Should not be directly selectable by end-users.\n#[derive(Copy, Clone, Debug, PartialEq, Eq)]\npub enum LpspiInstance {\n    /// Instance 0\n    Lpspi0,\n    /// Instance 1\n    Lpspi1,\n    /// Instance 2\n    #[cfg(feature = \"mcxa5xx\")]\n    Lpspi2,\n    /// Instance 3\n    #[cfg(feature = \"mcxa5xx\")]\n    Lpspi3,\n    /// Instance 4\n    #[cfg(feature = \"mcxa5xx\")]\n    Lpspi4,\n    /// Instance 5\n    #[cfg(feature = \"mcxa5xx\")]\n    Lpspi5,\n}\n\n/// Top level configuration for `Lpspi` instances.\npub struct LpspiConfig {\n    /// Power state required for this peripheral\n    pub power: PoweredClock,\n    /// Clock source\n    pub source: LpspiClockSel,\n    /// Clock divisor\n    pub div: Div4,\n    /// Which instance is this?\n    // NOTE: should not be user settable\n    pub(crate) instance: LpspiInstance,\n}\n\nimpl SPConfHelper for LpspiConfig {\n    fn pre_enable_config(&self, clocks: &Clocks) -> Result<PreEnableParts, ClockError> {\n        // check that source is suitable\n        let mrcc0 = crate::pac::MRCC0;\n\n        let (clkdiv, clksel) = match self.instance {\n            LpspiInstance::Lpspi0 => (mrcc0.mrcc_lpspi0_clkdiv(), mrcc0.mrcc_lpspi0_clksel()),\n            LpspiInstance::Lpspi1 => (mrcc0.mrcc_lpspi1_clkdiv(), mrcc0.mrcc_lpspi1_clksel()),\n            #[cfg(feature = \"mcxa5xx\")]\n            LpspiInstance::Lpspi2 => (mrcc0.mrcc_lpspi2_clkdiv(), mrcc0.mrcc_lpspi2_clksel()),\n            #[cfg(feature = \"mcxa5xx\")]\n            LpspiInstance::Lpspi3 => (mrcc0.mrcc_lpspi3_clkdiv(), mrcc0.mrcc_lpspi3_clksel()),\n            #[cfg(feature = \"mcxa5xx\")]\n            LpspiInstance::Lpspi4 => (mrcc0.mrcc_lpspi4_clkdiv(), mrcc0.mrcc_lpspi4_clksel()),\n            #[cfg(feature = \"mcxa5xx\")]\n            LpspiInstance::Lpspi5 => (mrcc0.mrcc_lpspi5_clkdiv(), mrcc0.mrcc_lpspi5_clksel()),\n        };\n\n        let (freq, variant) = match self.source {\n            LpspiClockSel::FroLfDiv => {\n                let freq = clocks.ensure_fro_lf_div_active(&self.power)?;\n\n                // TODO: fix PAC names for consistency\n                #[cfg(feature = \"mcxa2xx\")]\n                let mux = LpspiClkselMux::CLKROOT_FUNC_0;\n                #[cfg(feature = \"mcxa5xx\")]\n                let mux = LpspiClkselMux::I0_CLKROOT_FUNC_0;\n\n                (freq, mux)\n            }\n            LpspiClockSel::FroHfDiv => {\n                let freq = clocks.ensure_fro_hf_div_active(&self.power)?;\n\n                // TODO: fix PAC names for consistency\n                #[cfg(feature = \"mcxa2xx\")]\n                let mux = LpspiClkselMux::CLKROOT_FUNC_2;\n                #[cfg(feature = \"mcxa5xx\")]\n                let mux = LpspiClkselMux::I2_CLKROOT_FUNC_2;\n\n                (freq, mux)\n            }\n            #[cfg(not(feature = \"sosc-as-gpio\"))]\n            LpspiClockSel::ClkIn => {\n                let freq = clocks.ensure_clk_in_active(&self.power)?;\n\n                // TODO: fix PAC names for consistency\n                #[cfg(feature = \"mcxa2xx\")]\n                let mux = LpspiClkselMux::CLKROOT_FUNC_3;\n                #[cfg(feature = \"mcxa5xx\")]\n                let mux = LpspiClkselMux::I3_CLKROOT_FUNC_3;\n\n                (freq, mux)\n            }\n            LpspiClockSel::Clk1M => {\n                let freq = clocks.ensure_clk_1m_active(&self.power)?;\n\n                // TODO: fix PAC names for consistency\n                #[cfg(feature = \"mcxa2xx\")]\n                let mux = LpspiClkselMux::CLKROOT_FUNC_5;\n                #[cfg(feature = \"mcxa5xx\")]\n                let mux = LpspiClkselMux::I5_CLKROOT_FUNC_5;\n\n                (freq, mux)\n            }\n            LpspiClockSel::Pll1ClkDiv => {\n                let freq = clocks.ensure_pll1_clk_div_active(&self.power)?;\n\n                // TODO: fix PAC names for consistency\n                #[cfg(feature = \"mcxa2xx\")]\n                let mux = LpspiClkselMux::CLKROOT_FUNC_6;\n                #[cfg(feature = \"mcxa5xx\")]\n                let mux = LpspiClkselMux::I6_CLKROOT_FUNC_6;\n\n                (freq, mux)\n            }\n            LpspiClockSel::None => {\n                // no ClkrootFunc7, just write manually for now\n                clksel.write(|w| w.0 = 0b111);\n                clkdiv.modify(|w| {\n                    w.set_reset(ClkdivReset::OFF);\n                    w.set_halt(ClkdivHalt::OFF);\n                });\n                return Ok(PreEnableParts::empty());\n            }\n        };\n\n        let div = self.div.into_divisor();\n        let expected = freq / div;\n\n        // 21.3.2 peripheral clock max functional clock limits\n        let power = match self.power {\n            PoweredClock::NormalEnabledDeepSleepDisabled => clocks.active_power,\n            PoweredClock::AlwaysEnabled => clocks.lp_power,\n        };\n\n        #[cfg(feature = \"mcxa2xx\")]\n        let fmax = match power {\n            VddLevel::MidDriveMode => 25_000_000,\n            VddLevel::OverDriveMode => 60_000_000,\n        };\n\n        #[cfg(feature = \"mcxa5xx\")]\n        let fmax = match power {\n            VddLevel::MidDriveMode => 50_000_000,\n            VddLevel::NormalMode => 150_000_000,\n            VddLevel::OverDriveMode => 200_000_000,\n        };\n\n        if expected > fmax {\n            return Err(ClockError::BadConfig {\n                clock: \"lpspi fclk\",\n                reason: \"exceeds max rating\",\n            });\n        }\n\n        apply_div4!(self, clksel, clkdiv, variant, freq)\n    }\n}\n\n//\n// I3C\n//\n\n/// Selectable clocks for `I3c` peripherals\n#[derive(Debug, Clone, Copy)]\npub enum I3cClockSel {\n    /// FRO12M/FRO_LF/SIRC clock source, passed through divider\n    /// \"fro_lf_div\"\n    FroLfDiv,\n    /// FRO180M/FRO_HF/FIRC clock source, passed through divider\n    /// \"fro_hf_div\"\n    FroHfDiv,\n    /// SOSC/XTAL/EXTAL clock source\n    #[cfg(not(feature = \"sosc-as-gpio\"))]\n    ClkIn,\n    /// clk_1m/FRO_LF divided by 12\n    Clk1M,\n    /// Internal PLL output, with configurable divisor\n    #[cfg(feature = \"mcxa5xx\")]\n    Pll1ClkDiv,\n    /// Disabled\n    None,\n}\n\n/// Top level configuration for `I3c` instances.\npub struct I3cConfig {\n    /// Power state required for this peripheral\n    pub power: PoweredClock,\n    /// Clock source\n    pub source: I3cClockSel,\n    /// Clock divisor\n    pub div: Div4,\n}\n\nimpl SPConfHelper for I3cConfig {\n    fn pre_enable_config(&self, clocks: &Clocks) -> Result<PreEnableParts, ClockError> {\n        #[cfg(feature = \"mcxa2xx\")]\n        // Always 25MHz maximum frequency.\n        const I3C_FCLK_MAX: u32 = 25_000_000;\n\n        #[cfg(feature = \"mcxa5xx\")]\n        // Always 100MHz maximum frequency.\n        const I3C_FCLK_MAX: u32 = 100_000_000;\n\n        // check that source is suitable\n        let mrcc0 = crate::pac::MRCC0;\n\n        let (clkdiv, clksel) = (mrcc0.mrcc_i3c0_fclk_clkdiv(), mrcc0.mrcc_i3c0_fclk_clksel());\n\n        let (freq, variant) = match self.source {\n            I3cClockSel::FroLfDiv => {\n                let freq = clocks.ensure_fro_lf_div_active(&self.power)?;\n\n                // TODO: fix PAC names for consistency\n                #[cfg(feature = \"mcxa2xx\")]\n                let mux = FclkClkselMux::CLKROOT_FUNC_0;\n                #[cfg(feature = \"mcxa5xx\")]\n                let mux = FclkClkselMux::I0_CLKROOT_FUNC_0;\n\n                (freq, mux)\n            }\n            I3cClockSel::FroHfDiv => {\n                let freq = clocks.ensure_fro_hf_div_active(&self.power)?;\n\n                // TODO: fix PAC names for consistency\n                #[cfg(feature = \"mcxa2xx\")]\n                let mux = FclkClkselMux::CLKROOT_FUNC_2;\n                #[cfg(feature = \"mcxa5xx\")]\n                let mux = FclkClkselMux::I2_CLKROOT_FUNC_2;\n\n                (freq, mux)\n            }\n            #[cfg(not(feature = \"sosc-as-gpio\"))]\n            I3cClockSel::ClkIn => {\n                let freq = clocks.ensure_clk_in_active(&self.power)?;\n\n                // TODO: fix PAC names for consistency\n                #[cfg(feature = \"mcxa2xx\")]\n                let mux = FclkClkselMux::CLKROOT_FUNC_3;\n                #[cfg(feature = \"mcxa5xx\")]\n                let mux = FclkClkselMux::I3_CLKROOT_FUNC_3;\n\n                (freq, mux)\n            }\n            I3cClockSel::Clk1M => {\n                let freq = clocks.ensure_clk_1m_active(&self.power)?;\n\n                // TODO: fix PAC names for consistency\n                #[cfg(feature = \"mcxa2xx\")]\n                let mux = FclkClkselMux::CLKROOT_FUNC_5;\n                #[cfg(feature = \"mcxa5xx\")]\n                let mux = FclkClkselMux::I5_CLKROOT_FUNC_5;\n\n                (freq, mux)\n            }\n            #[cfg(feature = \"mcxa5xx\")]\n            I3cClockSel::Pll1ClkDiv => {\n                let freq = clocks.ensure_pll1_clk_div_active(&self.power)?;\n\n                // TODO: fix PAC names for consistency\n                #[cfg(feature = \"mcxa2xx\")]\n                let mux = FclkClkselMux::CLKROOT_FUNC_6;\n                #[cfg(feature = \"mcxa5xx\")]\n                let mux = FclkClkselMux::I6_CLKROOT_FUNC_6;\n\n                (freq, mux)\n            }\n            I3cClockSel::None => {\n                // no ClkrootFunc7, just write manually for now\n                clksel.write(|w| w.0 = 0b111);\n                clkdiv.modify(|w| {\n                    w.set_reset(ClkdivReset::OFF);\n                    w.set_halt(ClkdivHalt::OFF);\n                });\n                return Ok(PreEnableParts::empty());\n            }\n        };\n\n        if freq > I3C_FCLK_MAX {\n            return Err(ClockError::BadConfig {\n                clock: \"i3c fclk\",\n                reason: \"exceeds max rating\",\n            });\n        }\n\n        apply_div4!(self, clksel, clkdiv, variant, freq)\n    }\n}\n\n//\n// LPI2c\n//\n\n/// Selectable clocks for `Lpi2c` peripherals\n#[derive(Debug, Clone, Copy)]\npub enum Lpi2cClockSel {\n    /// FRO12M/FRO_LF/SIRC clock source, passed through divider\n    /// \"fro_lf_div\"\n    FroLfDiv,\n    /// FRO180M/FRO192M/FRO_HF/FIRC clock source, passed through divider\n    /// \"fro_hf_div\"\n    FroHfDiv,\n    /// SOSC/XTAL/EXTAL clock source\n    #[cfg(feature = \"mcxa2xx\")]\n    #[cfg(not(feature = \"sosc-as-gpio\"))]\n    ClkIn,\n    /// clk_1m/FRO_LF divided by 12\n    Clk1M,\n    /// Output of PLL1, passed through clock divider,\n    /// \"pll1_clk_div\", maybe \"pll1_lf_div\"?\n    #[cfg(feature = \"mcxa2xx\")]\n    Pll1ClkDiv,\n    /// Disabled\n    None,\n}\n\n/// Which instance of the `Lpi2c` is this?\n///\n/// Should not be directly selectable by end-users.\n#[derive(Copy, Clone, Debug, PartialEq, Eq)]\npub enum Lpi2cInstance {\n    /// Instance 0\n    Lpi2c0,\n    /// Instance 1\n    Lpi2c1,\n    /// Instance 2\n    Lpi2c2,\n    /// Instance 3\n    Lpi2c3,\n}\n\n/// Top level configuration for `Lpi2c` instances.\npub struct Lpi2cConfig {\n    /// Power state required for this peripheral\n    pub power: PoweredClock,\n    /// Clock source\n    pub source: Lpi2cClockSel,\n    /// Clock divisor\n    pub div: Div4,\n    /// Which instance is this?\n    // NOTE: should not be user settable\n    pub(crate) instance: Lpi2cInstance,\n}\n\nimpl SPConfHelper for Lpi2cConfig {\n    fn pre_enable_config(&self, clocks: &Clocks) -> Result<PreEnableParts, ClockError> {\n        // check that source is suitable\n        let mrcc0 = crate::pac::MRCC0;\n\n        let (clkdiv, clksel) = match self.instance {\n            Lpi2cInstance::Lpi2c0 => (mrcc0.mrcc_lpi2c0_clkdiv(), mrcc0.mrcc_lpi2c0_clksel()),\n            Lpi2cInstance::Lpi2c1 => (mrcc0.mrcc_lpi2c1_clkdiv(), mrcc0.mrcc_lpi2c1_clksel()),\n            Lpi2cInstance::Lpi2c2 => (mrcc0.mrcc_lpi2c2_clkdiv(), mrcc0.mrcc_lpi2c2_clksel()),\n            Lpi2cInstance::Lpi2c3 => (mrcc0.mrcc_lpi2c3_clkdiv(), mrcc0.mrcc_lpi2c3_clksel()),\n        };\n\n        let (freq, variant) = match self.source {\n            Lpi2cClockSel::FroLfDiv => {\n                let freq = clocks.ensure_fro_lf_div_active(&self.power)?;\n\n                // TODO: fix PAC names for consistency\n                #[cfg(feature = \"mcxa2xx\")]\n                let mux = Lpi2cClkselMux::CLKROOT_FUNC_0;\n                #[cfg(feature = \"mcxa5xx\")]\n                let mux = Lpi2cClkselMux::I0_CLKROOT_FUNC_0;\n\n                (freq, mux)\n            }\n            Lpi2cClockSel::FroHfDiv => {\n                let freq = clocks.ensure_fro_hf_div_active(&self.power)?;\n\n                // TODO: fix PAC names for consistency\n                #[cfg(feature = \"mcxa2xx\")]\n                let mux = Lpi2cClkselMux::CLKROOT_FUNC_2;\n                #[cfg(feature = \"mcxa5xx\")]\n                let mux = Lpi2cClkselMux::I2_CLKROOT_FUNC_2;\n\n                (freq, mux)\n            }\n            #[cfg(feature = \"mcxa2xx\")]\n            #[cfg(not(feature = \"sosc-as-gpio\"))]\n            Lpi2cClockSel::ClkIn => {\n                let freq = clocks.ensure_clk_in_active(&self.power)?;\n                (freq, Lpi2cClkselMux::CLKROOT_FUNC_3)\n            }\n            Lpi2cClockSel::Clk1M => {\n                let freq = clocks.ensure_clk_1m_active(&self.power)?;\n\n                // TODO: fix PAC names for consistency\n                #[cfg(feature = \"mcxa2xx\")]\n                let mux = Lpi2cClkselMux::CLKROOT_FUNC_5;\n                #[cfg(feature = \"mcxa5xx\")]\n                let mux = Lpi2cClkselMux::I5_CLKROOT_FUNC_5;\n\n                (freq, mux)\n            }\n            #[cfg(feature = \"mcxa2xx\")]\n            Lpi2cClockSel::Pll1ClkDiv => {\n                let freq = clocks.ensure_pll1_clk_div_active(&self.power)?;\n                (freq, Lpi2cClkselMux::CLKROOT_FUNC_6)\n            }\n            Lpi2cClockSel::None => {\n                // no ClkrootFunc7, just write manually for now\n                clksel.write(|w| w.0 = 0b111);\n                clkdiv.modify(|w| {\n                    w.set_reset(ClkdivReset::OFF);\n                    w.set_halt(ClkdivHalt::OFF);\n                });\n                return Ok(PreEnableParts::empty());\n            }\n        };\n        let div = self.div.into_divisor();\n        let expected = freq / div;\n        // 22.3.2 peripheral clock max functional clock limits\n        let power = match self.power {\n            PoweredClock::NormalEnabledDeepSleepDisabled => clocks.active_power,\n            PoweredClock::AlwaysEnabled => clocks.lp_power,\n        };\n\n        #[cfg(feature = \"mcxa2xx\")]\n        let fmax = match power {\n            VddLevel::MidDriveMode => 25_000_000,\n            VddLevel::OverDriveMode => 60_000_000,\n        };\n\n        #[cfg(feature = \"mcxa5xx\")]\n        let fmax = match power {\n            VddLevel::MidDriveMode => 25_000_000,\n            VddLevel::NormalMode | VddLevel::OverDriveMode => 64_000_000,\n        };\n\n        if expected > fmax {\n            return Err(ClockError::BadConfig {\n                clock: \"lpi2c fclk\",\n                reason: \"exceeds max rating\",\n            });\n        }\n\n        apply_div4!(self, clksel, clkdiv, variant, freq)\n    }\n}\n\n//\n// LPUart\n//\n\n/// Selectable clocks for Lpuart peripherals\n#[derive(Debug, Clone, Copy)]\npub enum LpuartClockSel {\n    /// FRO12M/FRO_LF/SIRC clock source, passed through divider\n    /// \"fro_lf_div\"\n    FroLfDiv,\n    /// FRO180M/FRO192M/FRO_HF/FIRC clock source, passed through divider\n    /// \"fro_hf_div\"\n    FroHfDiv,\n    /// SOSC/XTAL/EXTAL clock source\n    #[cfg(not(feature = \"sosc-as-gpio\"))]\n    ClkIn,\n    /// FRO16K/clk_16k source\n    #[cfg(feature = \"mcxa2xx\")]\n    Clk16K,\n    /// clk_1m/FRO_LF divided by 12\n    Clk1M,\n    /// Output of PLL1, passed through clock divider,\n    /// \"pll1_clk_div\", maybe \"pll1_lf_div\"?\n    Pll1ClkDiv,\n    /// Disabled\n    None,\n}\n\n/// Which instance of the Lpuart is this?\n///\n/// Should not be directly selectable by end-users.\n#[derive(Copy, Clone, Debug, PartialEq, Eq)]\npub enum LpuartInstance {\n    /// Instance 0\n    Lpuart0,\n    /// Instance 1\n    Lpuart1,\n    /// Instance 2\n    Lpuart2,\n    /// Instance 3\n    Lpuart3,\n    /// Instance 4\n    Lpuart4,\n    /// Instance 5\n    Lpuart5,\n}\n\n/// Top level configuration for `Lpuart` instances.\npub struct LpuartConfig {\n    /// Power state required for this peripheral\n    pub power: PoweredClock,\n    /// Clock source\n    pub source: LpuartClockSel,\n    /// Clock divisor\n    pub div: Div4,\n    /// Which instance is this?\n    // NOTE: should not be user settable\n    pub(crate) instance: LpuartInstance,\n}\n\nimpl SPConfHelper for LpuartConfig {\n    fn pre_enable_config(&self, clocks: &Clocks) -> Result<PreEnableParts, ClockError> {\n        // check that source is suitable\n        let mrcc0 = crate::pac::MRCC0;\n\n        let (clkdiv, clksel) = match self.instance {\n            LpuartInstance::Lpuart0 => (mrcc0.mrcc_lpuart0_clkdiv(), mrcc0.mrcc_lpuart0_clksel()),\n            LpuartInstance::Lpuart1 => (mrcc0.mrcc_lpuart1_clkdiv(), mrcc0.mrcc_lpuart1_clksel()),\n            LpuartInstance::Lpuart2 => (mrcc0.mrcc_lpuart2_clkdiv(), mrcc0.mrcc_lpuart2_clksel()),\n            LpuartInstance::Lpuart3 => (mrcc0.mrcc_lpuart3_clkdiv(), mrcc0.mrcc_lpuart3_clksel()),\n            LpuartInstance::Lpuart4 => (mrcc0.mrcc_lpuart4_clkdiv(), mrcc0.mrcc_lpuart4_clksel()),\n            LpuartInstance::Lpuart5 => (mrcc0.mrcc_lpuart5_clkdiv(), mrcc0.mrcc_lpuart5_clksel()),\n        };\n\n        let (freq, variant) = match self.source {\n            LpuartClockSel::FroLfDiv => {\n                let freq = clocks.ensure_fro_lf_div_active(&self.power)?;\n\n                // TODO: fix PAC names for consistency\n                #[cfg(feature = \"mcxa2xx\")]\n                let mux = LpuartClkselMux::CLKROOT_FUNC_0;\n                #[cfg(feature = \"mcxa5xx\")]\n                let mux = LpuartClkselMux::I0_CLKROOT_SIRC_DIV;\n\n                (freq, mux)\n            }\n            LpuartClockSel::FroHfDiv => {\n                let freq = clocks.ensure_fro_hf_div_active(&self.power)?;\n\n                // TODO: fix PAC names for consistency\n                #[cfg(feature = \"mcxa2xx\")]\n                let mux = LpuartClkselMux::CLKROOT_FUNC_2;\n                #[cfg(feature = \"mcxa5xx\")]\n                let mux = LpuartClkselMux::I2_CLKROOT_FIRC_DIV;\n\n                (freq, mux)\n            }\n            #[cfg(not(feature = \"sosc-as-gpio\"))]\n            LpuartClockSel::ClkIn => {\n                let freq = clocks.ensure_clk_in_active(&self.power)?;\n\n                // TODO: fix PAC names for consistency\n                #[cfg(feature = \"mcxa2xx\")]\n                let mux = LpuartClkselMux::CLKROOT_FUNC_3;\n                #[cfg(feature = \"mcxa5xx\")]\n                let mux = LpuartClkselMux::I3_CLKROOT_SOSC;\n\n                (freq, mux)\n            }\n            #[cfg(feature = \"mcxa2xx\")]\n            LpuartClockSel::Clk16K => {\n                let freq = clocks.ensure_clk_16k_vdd_core_active(&self.power)?;\n\n                // TODO: fix PAC names for consistency\n                #[cfg(feature = \"mcxa2xx\")]\n                let mux = LpuartClkselMux::CLKROOT_FUNC_4;\n                // #[cfg(feature = \"mcxa5xx\")]\n                // let mux = LpuartClkselMux::I4_CLKROOT_LPOSC;\n\n                (freq, mux)\n            }\n            LpuartClockSel::Clk1M => {\n                let freq = clocks.ensure_clk_1m_active(&self.power)?;\n\n                // TODO: fix PAC names for consistency\n                #[cfg(feature = \"mcxa2xx\")]\n                let mux = LpuartClkselMux::CLKROOT_FUNC_5;\n                #[cfg(feature = \"mcxa5xx\")]\n                let mux = LpuartClkselMux::I5_CLKROOT_1M;\n\n                (freq, mux)\n            }\n            LpuartClockSel::Pll1ClkDiv => {\n                let freq = clocks.ensure_pll1_clk_div_active(&self.power)?;\n\n                // TODO: fix PAC names for consistency\n                #[cfg(feature = \"mcxa2xx\")]\n                let mux = LpuartClkselMux::CLKROOT_FUNC_6;\n                #[cfg(feature = \"mcxa5xx\")]\n                let mux = LpuartClkselMux::I6_CLKROOT_SPLL_DIV;\n\n                (freq, mux)\n            }\n            LpuartClockSel::None => {\n                // no ClkrootFunc7, just write manually for now\n                clksel.write(|w| w.set_mux(LpuartClkselMux::_RESERVED_7));\n                clkdiv.modify(|w| {\n                    w.set_reset(ClkdivReset::ON);\n                    w.set_halt(ClkdivHalt::ON);\n                });\n                return Ok(PreEnableParts::empty());\n            }\n        };\n\n        // Check clock speed is reasonable\n        let div = self.div.into_divisor();\n        let expected = freq / div;\n        // 22.3.2 peripheral clock max functional clock limits\n        let power = match self.power {\n            PoweredClock::NormalEnabledDeepSleepDisabled => clocks.active_power,\n            PoweredClock::AlwaysEnabled => clocks.lp_power,\n        };\n        #[cfg(feature = \"mcxa2xx\")]\n        let fmax = match power {\n            VddLevel::MidDriveMode => 45_000_000,\n            VddLevel::OverDriveMode => 180_000_000,\n        };\n        #[cfg(feature = \"mcxa5xx\")]\n        let fmax = match power {\n            VddLevel::MidDriveMode => 50_000_000,\n            VddLevel::NormalMode => 150_000_000,\n            VddLevel::OverDriveMode => 200_000_000,\n        };\n        if expected > fmax {\n            return Err(ClockError::BadConfig {\n                clock: \"lpuart fclk\",\n                reason: \"exceeds max rating\",\n            });\n        }\n\n        // set clksel\n        apply_div4!(self, clksel, clkdiv, variant, freq)\n    }\n}\n\n//\n// CTimer\n//\n\n/// Selectable clocks for `CTimer` peripherals\n#[derive(Debug, Clone, Copy)]\npub enum CTimerClockSel {\n    /// FRO12M/FRO_LF/SIRC clock source, passed through divider\n    /// \"fro_lf_div\"\n    FroLfDiv,\n    /// FRO180M/FRO192M/FRO_HF/FIRC clock source, passed through divider\n    /// \"fro_hf_div\"\n    FroHfDiv,\n    /// SOSC/XTAL/EXTAL clock source\n    #[cfg(not(feature = \"sosc-as-gpio\"))]\n    ClkIn,\n    /// FRO16K/clk_16k source\n    #[cfg(feature = \"mcxa2xx\")]\n    Clk16K,\n    /// clk_1m/FRO_LF divided by 12\n    Clk1M,\n    /// Internal PLL output, with configurable divisor\n    Pll1ClkDiv,\n    /// Disabled\n    None,\n}\n\n/// Which instance of the `CTimer` is this?\n///\n/// Should not be directly selectable by end-users.\n#[derive(Copy, Clone, Debug, PartialEq, Eq)]\npub enum CTimerInstance {\n    /// Instance 0\n    CTimer0,\n    /// Instance 1\n    CTimer1,\n    /// Instance 2\n    CTimer2,\n    /// Instance 3\n    CTimer3,\n    /// Instance 4\n    CTimer4,\n}\n\n/// Top level configuration for `CTimer` instances.\npub struct CTimerConfig {\n    /// Power state required for this peripheral\n    pub power: PoweredClock,\n    /// Clock source\n    pub source: CTimerClockSel,\n    /// Clock divisor\n    pub div: Div4,\n    /// Which instance is this?\n    // NOTE: should not be user settable\n    pub(crate) instance: CTimerInstance,\n}\n\nimpl SPConfHelper for CTimerConfig {\n    fn pre_enable_config(&self, clocks: &Clocks) -> Result<PreEnableParts, ClockError> {\n        // check that source is suitable\n        let mrcc0 = crate::pac::MRCC0;\n\n        let (clkdiv, clksel) = match self.instance {\n            CTimerInstance::CTimer0 => (mrcc0.mrcc_ctimer0_clkdiv(), mrcc0.mrcc_ctimer0_clksel()),\n            CTimerInstance::CTimer1 => (mrcc0.mrcc_ctimer1_clkdiv(), mrcc0.mrcc_ctimer1_clksel()),\n            CTimerInstance::CTimer2 => (mrcc0.mrcc_ctimer2_clkdiv(), mrcc0.mrcc_ctimer2_clksel()),\n            CTimerInstance::CTimer3 => (mrcc0.mrcc_ctimer3_clkdiv(), mrcc0.mrcc_ctimer3_clksel()),\n            CTimerInstance::CTimer4 => (mrcc0.mrcc_ctimer4_clkdiv(), mrcc0.mrcc_ctimer4_clksel()),\n        };\n\n        let (freq, variant) = match self.source {\n            CTimerClockSel::FroLfDiv => {\n                let freq = clocks.ensure_fro_lf_div_active(&self.power)?;\n\n                // TODO: fix PAC names for consistency\n                #[cfg(feature = \"mcxa2xx\")]\n                let mux = CtimerClkselMux::CLKROOT_FUNC_0;\n                #[cfg(feature = \"mcxa5xx\")]\n                let mux = CtimerClkselMux::I0_CLKROOT_SIRC_DIV;\n\n                (freq, mux)\n            }\n            CTimerClockSel::FroHfDiv => {\n                let freq = clocks.ensure_fro_hf_div_active(&self.power)?;\n\n                // TODO: fix PAC names for consistency\n                #[cfg(feature = \"mcxa2xx\")]\n                let mux = CtimerClkselMux::CLKROOT_FUNC_1;\n                #[cfg(feature = \"mcxa5xx\")]\n                let mux = CtimerClkselMux::I1_CLKROOT_FIRC_GATED;\n\n                (freq, mux)\n            }\n            #[cfg(not(feature = \"sosc-as-gpio\"))]\n            CTimerClockSel::ClkIn => {\n                let freq = clocks.ensure_clk_in_active(&self.power)?;\n\n                // TODO: fix PAC names for consistency\n                #[cfg(feature = \"mcxa2xx\")]\n                let mux = CtimerClkselMux::CLKROOT_FUNC_3;\n                #[cfg(feature = \"mcxa5xx\")]\n                let mux = CtimerClkselMux::I3_CLKROOT_SOSC;\n\n                (freq, mux)\n            }\n            #[cfg(feature = \"mcxa2xx\")]\n            CTimerClockSel::Clk16K => {\n                let freq = clocks.ensure_clk_16k_vdd_core_active(&self.power)?;\n\n                // TODO: fix PAC names for consistency\n                #[cfg(feature = \"mcxa2xx\")]\n                let mux = CtimerClkselMux::CLKROOT_FUNC_4;\n                // TODO: MCXA5xx uses \"LPOSC\", which can either be clk_16k or clk_32k.\n                // We do not support this yet.\n                // #[cfg(feature = \"mcxa5xx\")]\n                // let mux = CtimerClkselMux::I4_CLKROOT_LPOSC;\n\n                (freq, mux)\n            }\n            CTimerClockSel::Clk1M => {\n                let freq = clocks.ensure_clk_1m_active(&self.power)?;\n\n                // TODO: fix PAC names for consistency\n                #[cfg(feature = \"mcxa2xx\")]\n                let mux = CtimerClkselMux::CLKROOT_FUNC_5;\n                #[cfg(feature = \"mcxa5xx\")]\n                let mux = CtimerClkselMux::I5_CLKROOT_1M;\n\n                (freq, mux)\n            }\n            CTimerClockSel::Pll1ClkDiv => {\n                let freq = clocks.ensure_pll1_clk_div_active(&self.power)?;\n\n                // TODO: fix PAC names for consistency\n                #[cfg(feature = \"mcxa2xx\")]\n                let mux = CtimerClkselMux::CLKROOT_FUNC_6;\n                #[cfg(feature = \"mcxa5xx\")]\n                let mux = CtimerClkselMux::I6_CLKROOT_SPLL_DIV;\n\n                (freq, mux)\n            }\n            CTimerClockSel::None => {\n                // no ClkrootFunc7, just write manually for now\n                clksel.write(|w| w.set_mux(CtimerClkselMux::_RESERVED_7));\n                clkdiv.modify(|w| {\n                    w.set_reset(ClkdivReset::ON);\n                    w.set_halt(ClkdivHalt::ON)\n                });\n                return Ok(PreEnableParts::empty());\n            }\n        };\n\n        let div = self.div.into_divisor();\n        let expected = freq / div;\n\n        // 22.3.2 peripheral clock max functional clock limits\n        let power = match self.power {\n            PoweredClock::NormalEnabledDeepSleepDisabled => clocks.active_power,\n            PoweredClock::AlwaysEnabled => clocks.lp_power,\n        };\n        #[cfg(feature = \"mcxa2xx\")]\n        let fmax = match power {\n            VddLevel::MidDriveMode => 25_000_000,\n            VddLevel::OverDriveMode => 60_000_000,\n        };\n        #[cfg(feature = \"mcxa5xx\")]\n        let fmax = match power {\n            VddLevel::MidDriveMode => 50_000_000,\n            VddLevel::NormalMode => 150_000_000,\n            VddLevel::OverDriveMode => 200_000_000,\n        };\n\n        if expected > fmax {\n            return Err(ClockError::BadConfig {\n                clock: \"ctimer fclk\",\n                reason: \"exceeds max rating\",\n            });\n        }\n\n        apply_div4!(self, clksel, clkdiv, variant, freq)\n    }\n}\n"
  },
  {
    "path": "embassy-mcxa/src/clocks/sleep.rs",
    "content": "//! Deep sleep entry, exit, and clock recovery.\n\nuse core::cell::Ref;\nuse core::ops::Deref;\n\nuse critical_section::CriticalSection;\n\nuse super::CLOCKS;\nuse super::types::{Clocks, PoweredClock};\nuse crate::pac;\nuse crate::pac::cmc::vals::CkctrlCkmode;\n#[cfg(feature = \"mcxa2xx\")]\nuse crate::pac::scg::vals::Fircvld;\nuse crate::pac::scg::vals::{Sircvld, SpllLock};\n\n/// Attempt to go to deep sleep if possible.\n///\n/// If we successfully went and returned from deep sleep, this function returns a `true`.\n/// If we were unsuccessful due to active `WaitGuard`s, this function returns a `false`.\n///\n/// ## SAFETY\n///\n/// Care must be taken that we have ensured that the system is ready to go to deep\n/// sleep, otherwise HAL peripherals may misbehave. `crate::clocks::init()` must\n/// have been called and returned successfully, with a `CoreSleep` configuration\n/// set to DeepSleep (or lower).\npub unsafe fn deep_sleep_if_possible(cs: &CriticalSection) -> bool {\n    let inhibit = crate::clocks::active_wake_guards(cs);\n    if inhibit {\n        return false;\n    }\n\n    unsafe {\n        // Yep, it's time to go to deep sleep. WHILE STILL IN the CS, get ready\n        setup_deep_sleep();\n\n        // Here we go!\n        //\n        // It is okay to WFE with interrupts disabled: we have enabled SEVONPEND\n        cortex_m::asm::dsb();\n        cortex_m::asm::wfe();\n\n        // Wakey wakey, eggs and bakey\n        recover_deep_sleep(cs);\n    }\n\n    true\n}\n\n/// Prepare the system for deep sleep\n///\n/// ## SAFETY\n///\n/// Care must be taken that we have ensured that the system is ready to go to deep\n/// sleep, otherwise HAL peripherals may misbehave. `crate::clocks::init()` must\n/// have been called and returned successfully, with a `CoreSleep` configuration\n/// set to DeepSleep (or lower).\nunsafe fn setup_deep_sleep() {\n    let cmc = nxp_pac::CMC;\n    let spc = nxp_pac::SPC0;\n\n    // Isolate/unpower external voltage domains\n    spc.evd_cfg().write(|w| w.0 = 0);\n\n    // To configure for Deep Sleep Low-Power mode entry:\n    //\n    // Write Fh to Clock Control (CKCTRL)\n    cmc.ckctrl().modify(|w| w.set_ckmode(CkctrlCkmode::CKMODE1111));\n    // Write 1h to Power Mode Protection (PMPROT)\n    cmc.pmprot().write(|w| w.0 = 1);\n    // Write 1h to Global Power Mode Control (GPMCTRL)\n    cmc.gpmctrl().modify(|w| w.set_lpmode(0b0001));\n    // Redundant?\n    // cmc.pmctrlmain().modify(|w| w.set_lpmode(PmctrlmainLpmode::LPMODE0001));\n\n    // From the C SDK:\n    //\n    // Before executing WFI instruction read back the last register to\n    // ensure all registers writes have completed.\n    let _ = cmc.gpmctrl().read();\n}\n\n/// Start back up after deep sleep returns\n///\n/// ## SAFETY\n///\n/// Care must be taken that we have ensured that the system is ready to go to deep\n/// sleep, otherwise HAL peripherals may misbehave. `crate::clocks::init()` must\n/// have been called and returned successfully, with a `CoreSleep` configuration\n/// set to DeepSleep (or lower).\nunsafe fn recover_deep_sleep(cs: &CriticalSection) {\n    let cmc = nxp_pac::CMC;\n\n    // Restart any necessary clocks\n    unsafe {\n        restart_active_only_clocks(cs);\n    }\n\n    // Re-raise the sleep level to WFE sleep in the off chance that the\n    // user decides to call `wfe` on their own accord, and to avoid having\n    // to re-set if we chill in WFE sleep mostly\n    cmc.ckctrl().modify(|w| w.set_ckmode(CkctrlCkmode::CKMODE0001));\n}\n\n/// Perform any actions necessary to re-initialize clocks after returning to active\n/// mode after a low power (e.g. deep sleep, power-off) state.\n///\n/// ## Safety\n///\n/// This should only be called in a critical section, immediately after waking up.\nunsafe fn restart_active_only_clocks(_cs: &CriticalSection) {\n    let bref: Ref<'_, Option<Clocks>> = CLOCKS.borrow_ref(*_cs);\n    let dref: &Option<Clocks> = bref.deref();\n    let Some(clocks) = dref else {\n        return;\n    };\n    let scg = pac::SCG0;\n\n    // TODO: Restart clock monitors if necessary? Needs to be re-enabled\n    // AFTER FRO12M has been started, and probably after clocks are\n    // valid again.\n    //\n    // TODO: Timeout? Check error fields (at least for SPLL)? Clear\n    // or reset any status bits?\n\n    // Ensure FRO12M is up and running\n    if let Some(fro12m) = clocks.fro_12m_root.as_ref()\n        && !matches!(fro12m.power, PoweredClock::AlwaysEnabled)\n    {\n        while scg.sirccsr().read().sircvld() != Sircvld::ENABLED_AND_VALID {}\n    }\n\n    // Ensure FRO45M is up and running\n    #[cfg(feature = \"mcxa2xx\")]\n    if let Some(frohf) = clocks.fro_hf_root.as_ref()\n        && !matches!(frohf.power, PoweredClock::AlwaysEnabled)\n    {\n        while scg.firccsr().read().fircvld() != Fircvld::ENABLED_AND_VALID {}\n    }\n\n    // Ensure SOSC is up and running\n    #[cfg(not(feature = \"sosc-as-gpio\"))]\n    if let Some(clk_in) = clocks.clk_in.as_ref()\n        && !matches!(clk_in.power, PoweredClock::AlwaysEnabled)\n    {\n        while !scg.sosccsr().read().soscvld() {}\n    }\n\n    // Ensure SPLL is up and running\n    if let Some(spll) = clocks.pll1_clk.as_ref()\n        && !matches!(spll.power, PoweredClock::AlwaysEnabled)\n    {\n        while scg.spllcsr().read().spll_lock() != SpllLock::ENABLED_AND_VALID {}\n    }\n}\n"
  },
  {
    "path": "embassy-mcxa/src/clocks/types.rs",
    "content": "//! Clock system types\n\nuse core::sync::atomic::Ordering;\n\nuse super::LIVE_HP_TOKENS;\nuse crate::clocks::config::{CoreSleep, VddLevel};\n\n/// A guard that will inhibit the device from entering deep sleep while\n/// it exists.\n#[must_use = \"Wake Guard must be kept in order to prevent deep sleep\"]\npub struct WakeGuard {\n    _x: (),\n}\n\nimpl WakeGuard {\n    /// Create a new wake guard, that increments the \"live high power token\" counts.\n    ///\n    /// This is typically used by HAL drivers (when a peripheral is clocked from an\n    /// active-mode-only source) to inhibit sleep, OR by application code to prevent\n    /// deep sleep as well.\n    pub fn new() -> Self {\n        _ = LIVE_HP_TOKENS.fetch_add(1, Ordering::AcqRel);\n        Self { _x: () }\n    }\n\n    /// Helper method to potentially create a guard if necessary for a clock.\n    pub fn for_power(level: &PoweredClock) -> Option<Self> {\n        match level {\n            PoweredClock::NormalEnabledDeepSleepDisabled => Some(Self::new()),\n            PoweredClock::AlwaysEnabled => None,\n        }\n    }\n}\n\nimpl Clone for WakeGuard {\n    fn clone(&self) -> Self {\n        // NOTE: Call load-bearing-new to clone, DO NOT just use the derive to\n        // copy the ZST!\n        Self::new()\n    }\n}\n\nimpl Default for WakeGuard {\n    fn default() -> Self {\n        Self::new()\n    }\n}\n\nimpl Drop for WakeGuard {\n    fn drop(&mut self) {\n        let old = LIVE_HP_TOKENS.fetch_sub(1, Ordering::AcqRel);\n        // Ensure we didn't just underflow.\n        assert!(old != 0);\n    }\n}\n\n/// The `Clocks` structure contains the initialized state of the core system clocks\n///\n/// These values are configured by providing\n/// [ClocksConfig](crate::clocks::config::ClocksConfig) to the\n/// [`init()`](super::init) function at boot time.\n#[derive(Default, Debug, Clone)]\n#[non_exhaustive]\npub struct Clocks {\n    /// Active power config\n    pub active_power: VddLevel,\n\n    /// Low-power power config\n    pub lp_power: VddLevel,\n\n    /// Is the bandgap enabled in active mode?\n    pub bandgap_active: bool,\n\n    /// Is the bandgap enabled in deep sleep mode?\n    pub bandgap_lowpower: bool,\n\n    /// Lowest sleep level\n    pub core_sleep: CoreSleep,\n\n    /// The `clk_in` is a clock provided by an external oscillator\n    /// AKA SOSC\n    #[cfg(not(feature = \"sosc-as-gpio\"))]\n    pub clk_in: Option<Clock>,\n\n    // FRO180M/FRO192M stuff\n    //\n    /// `fro_hf_root` is the direct output of the `FRO180M`/`FRO192M` internal oscillator\n    ///\n    /// It is used to feed downstream clocks, such as `fro_hf`, `clk_45m`/`clk_48m`,\n    /// and `fro_hf_div`.\n    pub fro_hf_root: Option<Clock>,\n\n    /// `fro_hf` is the same frequency as `fro_hf_root`, but behind a gate.\n    pub fro_hf: Option<Clock>,\n\n    /// `clk_45m` (2xx) or `clk_48` (5xx) is a 45MHz/48MHz clock, sourced from `fro_hf`.\n    pub clk_hf_fundamental: Option<Clock>,\n\n    /// `fro_hf_div` is a configurable frequency clock, sourced from `fro_hf`.\n    pub fro_hf_div: Option<Clock>,\n\n    //\n    // End FRO180M/FRO192M\n\n    // FRO12M stuff\n    //\n    /// `fro_12m_root` is the direct output of the `FRO12M` internal oscillator\n    ///\n    /// It is used to feed downstream clocks, such as `fro_12m`, `clk_1m`,\n    /// `and `fro_lf_div`.\n    pub fro_12m_root: Option<Clock>,\n\n    /// `fro_12m` is the same frequency as `fro_12m_root`, but behind a gate.\n    pub fro_12m: Option<Clock>,\n\n    /// `clk_1m` is a 1MHz clock, sourced from `fro_12m`\n    pub clk_1m: Option<Clock>,\n\n    /// `fro_lf_div` is a configurable frequency clock, sourced from `fro_12m`\n    pub fro_lf_div: Option<Clock>,\n    //\n    // End FRO12M stuff\n    /// `clk_16k_vsys` is one of two/three outputs of the `FRO16K` internal oscillator.\n    ///\n    /// Also referred to as `clk_16k[0]` in the datasheet, it feeds peripherals in\n    /// the system domain, such as the CMP and RTC.\n    pub clk_16k_vsys: Option<Clock>,\n\n    /// `clk_16k_vdd_core` is one of two/three outputs of the `FRO16K` internal oscillator.\n    ///\n    /// Also referred to as `clk_16k[1]` in the datasheet, it feeds peripherals in\n    /// the VDD Core domain, such as the OSTimer or LPUarts.\n    pub clk_16k_vdd_core: Option<Clock>,\n\n    /// `clk_16k_vbat` is one of three outputs of the `FRO16K` internal oscillator.\n    ///\n    /// Also referred to as `clk_16k[2]` in the datasheet.\n    #[cfg(feature = \"mcxa5xx\")]\n    pub clk_16k_vbat: Option<Clock>,\n\n    /// `clk_32k_vsys` is one of two/three outputs of the `FRO16K` internal oscillator.\n    ///\n    /// Also referred to as `clk_32k[0]` in the datasheet, it feeds peripherals in\n    /// the system domain, such as the CMP and RTC.\n    #[cfg(all(feature = \"mcxa5xx\", not(feature = \"rosc-32k-as-gpio\")))]\n    pub clk_32k_vsys: Option<Clock>,\n\n    /// `clk_32k_vdd_core` is one of two/three outputs of the `FRO16K` internal oscillator.\n    ///\n    /// Also referred to as `clk_32k[1]` in the datasheet, it feeds peripherals in\n    /// the VDD Core domain, such as the OSTimer or LPUarts.\n    #[cfg(all(feature = \"mcxa5xx\", not(feature = \"rosc-32k-as-gpio\")))]\n    pub clk_32k_vdd_core: Option<Clock>,\n\n    /// `clk_32k_vbat` is one of three outputs of the `FRO16K` internal oscillator.\n    ///\n    /// Also referred to as `clk_32k[2]` in the datasheet.\n    #[cfg(all(feature = \"mcxa5xx\", not(feature = \"rosc-32k-as-gpio\")))]\n    pub clk_32k_vbat: Option<Clock>,\n\n    /// `main_clk` is the main clock, upstream of the cpu/system clock.\n    pub main_clk: Option<Clock>,\n\n    /// `CPU_CLK` or `SYSTEM_CLK` is the output of `main_clk`, run through the `AHBCLKDIV`,\n    /// used for the CPU, AHB, APB, IPS bus, and some high speed peripherals.\n    pub cpu_system_clk: Option<Clock>,\n\n    /// `pll1_clk` is the output of the main system PLL, `pll1`.\n    pub pll1_clk: Option<Clock>,\n\n    /// `pll1_clk_div` is a configurable frequency clock, sourced from `pll1_clk`\n    pub pll1_clk_div: Option<Clock>,\n}\n\n/// `ClockError` is the main error returned when configuring or checking clock state\n#[derive(Debug, Copy, Clone, Eq, PartialEq)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\n#[non_exhaustive]\npub enum ClockError {\n    /// The system clocks were never initialized by calling [`init()`](super::init)\n    NeverInitialized,\n    /// The [`init()`](super::init) function was called more than once\n    AlreadyInitialized,\n    /// The requested configuration was not possible to fulfill, as the system clocks\n    /// were not configured in a compatible way\n    BadConfig { clock: &'static str, reason: &'static str },\n    /// The requested configuration was not possible to fulfill, as the required system\n    /// clocks have not yet been implemented.\n    NotImplemented { clock: &'static str },\n    /// The requested peripheral could not be configured, as the steps necessary to\n    /// enable it have not yet been implemented.\n    UnimplementedConfig,\n}\n\n/// Information regarding a system clock\n#[derive(Debug, Clone)]\npub struct Clock {\n    /// The frequency, in Hz, of the given clock\n    pub frequency: u32,\n    /// The power state of the clock, e.g. whether it is active in deep sleep mode\n    /// or not.\n    pub power: PoweredClock,\n}\n\n/// The power state of a given clock.\n///\n/// On the MCX-A, when Deep-Sleep is entered, any clock not configured for Deep Sleep\n/// mode will be stopped. This means that any downstream usage, e.g. by peripherals,\n/// will also stop.\n///\n/// In the future, we will provide an API for entering Deep Sleep, and if there are\n/// any peripherals that are NOT using an `AlwaysEnabled` clock active, entry into\n/// Deep Sleep will be prevented, in order to avoid misbehaving peripherals.\n#[derive(Debug, Clone, Copy, PartialEq, Eq)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub enum PoweredClock {\n    /// The given clock will NOT continue running in Deep Sleep mode\n    NormalEnabledDeepSleepDisabled,\n    /// The given clock WILL continue running in Deep Sleep mode\n    AlwaysEnabled,\n}\n\n/// The [`Clocks`] type's methods generally take the form of \"ensure X clock is active\".\n///\n/// These methods are intended to be used by HAL peripheral implementors to ensure that their\n/// selected clocks are active at a suitable level at time of construction. These methods\n/// return the frequency of the requested clock, in Hertz, or a [`ClockError`].\nimpl Clocks {\n    fn ensure_clock_active(\n        &self,\n        clock: &Option<Clock>,\n        name: &'static str,\n        at_level: &PoweredClock,\n    ) -> Result<u32, ClockError> {\n        let Some(clk) = clock.as_ref() else {\n            return Err(ClockError::BadConfig {\n                clock: name,\n                reason: \"required but not active\",\n            });\n        };\n        if !clk.power.meets_requirement_of(at_level) {\n            return Err(ClockError::BadConfig {\n                clock: name,\n                reason: \"not low power active\",\n            });\n        }\n        Ok(clk.frequency)\n    }\n\n    /// Ensure the `fro_lf_div` clock is active and valid at the given power state.\n    #[inline]\n    pub fn ensure_fro_lf_div_active(&self, at_level: &PoweredClock) -> Result<u32, ClockError> {\n        self.ensure_clock_active(&self.fro_lf_div, \"fro_lf_div\", at_level)\n    }\n\n    /// Ensure the `fro_hf` clock is active and valid at the given power state.\n    #[inline]\n    pub fn ensure_fro_hf_active(&self, at_level: &PoweredClock) -> Result<u32, ClockError> {\n        self.ensure_clock_active(&self.fro_hf, \"fro_hf\", at_level)\n    }\n\n    /// Ensure the `fro_hf_div` clock is active and valid at the given power state.\n    #[inline]\n    pub fn ensure_fro_hf_div_active(&self, at_level: &PoweredClock) -> Result<u32, ClockError> {\n        self.ensure_clock_active(&self.fro_hf_div, \"fro_hf_div\", at_level)\n    }\n\n    /// Ensure the `clk_in` clock is active and valid at the given power state.\n    #[cfg(not(feature = \"sosc-as-gpio\"))]\n    #[inline]\n    pub fn ensure_clk_in_active(&self, at_level: &PoweredClock) -> Result<u32, ClockError> {\n        self.ensure_clock_active(&self.clk_in, \"clk_in\", at_level)\n    }\n\n    /// Ensure the `clk_16k_vsys` clock is active and valid at the given power state.\n    pub fn ensure_clk_16k_vsys_active(&self, _at_level: &PoweredClock) -> Result<u32, ClockError> {\n        // NOTE: clk_16k is always active in low power mode\n        Ok(self\n            .clk_16k_vsys\n            .as_ref()\n            .ok_or(ClockError::BadConfig {\n                clock: \"clk_16k_vsys\",\n                reason: \"required but not active\",\n            })?\n            .frequency)\n    }\n\n    /// Ensure the `clk_16k_vdd_core` clock is active and valid at the given power state.\n    pub fn ensure_clk_16k_vdd_core_active(&self, _at_level: &PoweredClock) -> Result<u32, ClockError> {\n        // NOTE: clk_16k is always active in low power mode\n        Ok(self\n            .clk_16k_vdd_core\n            .as_ref()\n            .ok_or(ClockError::BadConfig {\n                clock: \"clk_16k_vdd_core\",\n                reason: \"required but not active\",\n            })?\n            .frequency)\n    }\n\n    /// Ensure the `clk_16k_vbat` clock is active and valid at the given power state.\n    #[cfg(feature = \"mcxa5xx\")]\n    pub fn ensure_clk_16k_vbat_active(&self, _at_level: &PoweredClock) -> Result<u32, ClockError> {\n        // NOTE: clk_16k is always active in low power mode\n        Ok(self\n            .clk_16k_vbat\n            .as_ref()\n            .ok_or(ClockError::BadConfig {\n                clock: \"clk_16k_vbat\",\n                reason: \"required but not active\",\n            })?\n            .frequency)\n    }\n\n    /// Ensure the `clk_32k_vsys` clock is active and valid at the given power state.\n    #[cfg(all(feature = \"mcxa5xx\", not(feature = \"rosc-32k-as-gpio\")))]\n    #[inline]\n    pub fn ensure_clk_32k_vsys_active(&self, at_level: &PoweredClock) -> Result<u32, ClockError> {\n        self.ensure_clock_active(&self.clk_32k_vsys, \"clk_32k_vsys\", at_level)\n    }\n\n    /// Ensure the `clk_32k_vdd_core` clock is active and valid at the given power state.\n    #[cfg(all(feature = \"mcxa5xx\", not(feature = \"rosc-32k-as-gpio\")))]\n    #[inline]\n    pub fn ensure_clk_32k_vdd_core_active(&self, at_level: &PoweredClock) -> Result<u32, ClockError> {\n        self.ensure_clock_active(&self.clk_32k_vdd_core, \"clk_32k_vdd_core\", at_level)\n    }\n\n    /// Ensure the `clk_32k_vbat` clock is active and valid at the given power state.\n    #[cfg(all(feature = \"mcxa5xx\", not(feature = \"rosc-32k-as-gpio\")))]\n    #[inline]\n    pub fn ensure_clk_32k_vbat_active(&self, at_level: &PoweredClock) -> Result<u32, ClockError> {\n        self.ensure_clock_active(&self.clk_32k_vbat, \"clk_32k_vbat\", at_level)\n    }\n\n    /// Ensure the `clk_1m` clock is active and valid at the given power state.\n    #[inline]\n    pub fn ensure_clk_1m_active(&self, at_level: &PoweredClock) -> Result<u32, ClockError> {\n        self.ensure_clock_active(&self.clk_1m, \"clk_1m\", at_level)\n    }\n\n    /// Ensure the `pll1_clk` clock is active and valid at the given power state.\n    #[inline]\n    pub fn ensure_pll1_clk_active(&self, at_level: &PoweredClock) -> Result<u32, ClockError> {\n        self.ensure_clock_active(&self.pll1_clk, \"pll1_clk\", at_level)\n    }\n\n    /// Ensure the `pll1_clk_div` clock is active and valid at the given power state.\n    #[inline]\n    pub fn ensure_pll1_clk_div_active(&self, at_level: &PoweredClock) -> Result<u32, ClockError> {\n        self.ensure_clock_active(&self.pll1_clk_div, \"pll1_clk_div\", at_level)\n    }\n\n    /// Ensure the `CPU_CLK` or `SYSTEM_CLK` is active\n    pub fn ensure_cpu_system_clk_active(&self, at_level: &PoweredClock) -> Result<u32, ClockError> {\n        let Some(clk) = self.cpu_system_clk.as_ref() else {\n            return Err(ClockError::BadConfig {\n                clock: \"cpu_system_clk\",\n                reason: \"required but not active\",\n            });\n        };\n\n        // Can the main_clk ever be active in deep sleep? I think it is gated?\n        match at_level {\n            PoweredClock::NormalEnabledDeepSleepDisabled => {}\n            PoweredClock::AlwaysEnabled => {\n                return Err(ClockError::BadConfig {\n                    clock: \"main_clk\",\n                    reason: \"not low power active\",\n                });\n            }\n        }\n\n        Ok(clk.frequency)\n    }\n\n    pub fn ensure_slow_clk_active(&self, at_level: &PoweredClock) -> Result<u32, ClockError> {\n        let freq = self.ensure_cpu_system_clk_active(at_level)?;\n\n        Ok(freq / 6)\n    }\n}\n\nimpl PoweredClock {\n    /// Does THIS clock meet the power requirements of the OTHER clock?\n    pub fn meets_requirement_of(&self, other: &Self) -> bool {\n        match (self, other) {\n            (PoweredClock::NormalEnabledDeepSleepDisabled, PoweredClock::AlwaysEnabled) => false,\n            (PoweredClock::NormalEnabledDeepSleepDisabled, PoweredClock::NormalEnabledDeepSleepDisabled) => true,\n            (PoweredClock::AlwaysEnabled, PoweredClock::NormalEnabledDeepSleepDisabled) => true,\n            (PoweredClock::AlwaysEnabled, PoweredClock::AlwaysEnabled) => true,\n        }\n    }\n}\n"
  },
  {
    "path": "embassy-mcxa/src/config.rs",
    "content": "// HAL configuration (minimal), mirroring embassy-imxrt style\n\nuse crate::clocks::config::ClocksConfig;\nuse crate::interrupt::Priority;\n\n#[non_exhaustive]\npub struct Config {\n    pub time_interrupt_priority: Priority,\n    pub rtc_interrupt_priority: Priority,\n    pub adc_interrupt_priority: Priority,\n    pub gpio_interrupt_priority: Priority,\n    pub wwdt_interrupt_priority: Priority,\n    pub cdog_interrupt_priority: Priority,\n    pub clock_cfg: ClocksConfig,\n}\n\nimpl Default for Config {\n    fn default() -> Self {\n        Self {\n            time_interrupt_priority: Priority::from(0),\n            rtc_interrupt_priority: Priority::from(0),\n            adc_interrupt_priority: Priority::from(0),\n            gpio_interrupt_priority: Priority::from(0),\n            wwdt_interrupt_priority: Priority::from(0),\n            cdog_interrupt_priority: Priority::from(0),\n            clock_cfg: ClocksConfig::default(),\n        }\n    }\n}\n"
  },
  {
    "path": "embassy-mcxa/src/crc.rs",
    "content": "//! Cyclic Redundancy Check (CRC)\n\nuse core::marker::PhantomData;\n\nuse embassy_hal_internal::{Peri, PeripheralType};\nuse paste::paste;\n\nuse crate::clocks::periph_helpers::NoConfig;\nuse crate::clocks::{Gate, enable_and_reset};\nuse crate::pac;\nuse crate::pac::crc::vals::{Fxor, Tcrc, Tot, Totr, Was};\n\n/// CRC driver.\npub struct Crc<'d, M> {\n    info: &'static Info,\n    _phantom: PhantomData<&'d mut M>,\n}\n\nimpl<'d, M: Mode> Crc<'d, M> {\n    fn new_inner<T: Instance>(_peri: Peri<'d, T>) -> Self {\n        // NoConfig? No WakeGuard!\n        _ = unsafe { enable_and_reset::<T>(&NoConfig) };\n\n        Crc {\n            info: T::info(),\n            _phantom: PhantomData,\n        }\n    }\n\n    // Configure the underlying peripheral according to the reference manual.\n    fn configure(&mut self, config: Config, width: Tcrc) {\n        self.info.regs().ctrl().modify(|w| {\n            w.set_fxor(config.complement_out.into());\n            w.set_totr(config.reflect_out.into());\n            w.set_tot(config.reflect_in.into());\n            w.set_was(Was::DATA);\n            w.set_tcrc(width);\n        });\n\n        self.info.regs().gpoly32().write(|w| *w = config.polynomial);\n        self.info.regs().ctrl().modify(|w| w.set_was(Was::SEED));\n        self.info.regs().data32().write(|w| *w = config.seed);\n        self.info.regs().ctrl().modify(|w| w.set_was(Was::DATA));\n    }\n\n    /// Read the computed CRC value\n    fn finalize_inner<W: Word>(self) -> W {\n        // Reference manual states:\n        //\n        // \"After writing all the data, you must wait for at least two\n        // clock cycles to read the data from CRC Data (DATA)\n        // register.\"\n        cortex_m::asm::delay(2);\n        W::read(self.info.regs())\n    }\n\n    fn feed_word<W: Word>(&mut self, word: W) {\n        W::write(self.info.regs(), word);\n    }\n\n    /// Feeds a slice of `Word`s into the CRC peripheral. Returns the computed\n    /// checksum.\n    ///\n    /// The input is strided efficiently into as many `u32`s as possible,\n    /// falling back to smaller writes for the remainder.\n    fn feed_inner<W: Word>(&mut self, data: &[W]) {\n        let (prefix, aligned, suffix) = unsafe { data.align_to::<u32>() };\n\n        for w in prefix {\n            self.feed_word(*w);\n        }\n\n        for w in aligned {\n            self.feed_word(*w);\n        }\n\n        for w in suffix {\n            self.feed_word(*w);\n        }\n    }\n}\n\nimpl<'d> Crc<'d, Crc16> {\n    /// Instantiates a new CRC peripheral driver in 16-bit mode\n    pub fn new_crc16<T: Instance>(peri: Peri<'d, T>, config: Config) -> Self {\n        let mut inst = Self::new_inner(peri);\n        inst.configure(config, Tcrc::B16);\n        inst\n    }\n\n    /// Instantiates a new CRC peripheral driver for the given `Algorithm16`.\n    pub fn new_algorithm16<T: Instance>(peri: Peri<'d, T>, algorithm: Algorithm16) -> Self {\n        Self::new_crc16(peri, algorithm.into_config())\n    }\n\n    /// Instantiates a new CRC peripheral for the `A` algorithm.\n    pub fn new_a<T: Instance>(peri: Peri<'d, T>) -> Self {\n        Self::new_algorithm16(peri, Algorithm16::A)\n    }\n\n    /// Instantiates a new CRC peripheral for the `AugCcitt` algorithm.\n    pub fn new_aug_ccitt<T: Instance>(peri: Peri<'d, T>) -> Self {\n        Self::new_algorithm16(peri, Algorithm16::AugCcitt)\n    }\n\n    /// Instantiates a new CRC peripheral for the `Arc` algorithm.\n    pub fn new_arc<T: Instance>(peri: Peri<'d, T>) -> Self {\n        Self::new_algorithm16(peri, Algorithm16::Arc)\n    }\n\n    /// Instantiates a new CRC peripheral for the `Buypass` algorithm.\n    pub fn new_buypass<T: Instance>(peri: Peri<'d, T>) -> Self {\n        Self::new_algorithm16(peri, Algorithm16::Buypass)\n    }\n\n    /// Instantiates a new CRC peripheral for the `CcittFalse` algorithm.\n    pub fn new_ccitt_false<T: Instance>(peri: Peri<'d, T>) -> Self {\n        Self::new_algorithm16(peri, Algorithm16::CcittFalse)\n    }\n\n    /// Instantiates a new CRC peripheral for the `CcittZero` algorithm.\n    pub fn new_ccitt_zero<T: Instance>(peri: Peri<'d, T>) -> Self {\n        Self::new_algorithm16(peri, Algorithm16::CcittZero)\n    }\n\n    /// Instantiates a new CRC peripheral for the `Cdma2000` algorithm.\n    pub fn new_cdma_2000<T: Instance>(peri: Peri<'d, T>) -> Self {\n        Self::new_algorithm16(peri, Algorithm16::Cdma2000)\n    }\n\n    /// Instantiates a new CRC peripheral for the `Dds110` algorithm.\n    pub fn new_dds_110<T: Instance>(peri: Peri<'d, T>) -> Self {\n        Self::new_algorithm16(peri, Algorithm16::Dds110)\n    }\n\n    /// Instantiates a new CRC peripheral for the `DectX` algorithm.\n    pub fn new_dect_x<T: Instance>(peri: Peri<'d, T>) -> Self {\n        Self::new_algorithm16(peri, Algorithm16::DectX)\n    }\n\n    /// Instantiates a new CRC peripheral for the `Dnp` algorithm.\n    pub fn new_dnp<T: Instance>(peri: Peri<'d, T>) -> Self {\n        Self::new_algorithm16(peri, Algorithm16::Dnp)\n    }\n\n    /// Instantiates a new CRC peripheral for the `En13757` algorithm.\n    pub fn new_en13757<T: Instance>(peri: Peri<'d, T>) -> Self {\n        Self::new_algorithm16(peri, Algorithm16::En13757)\n    }\n\n    /// Instantiates a new CRC peripheral for the `Genibus` algorithm.\n    pub fn new_genibus<T: Instance>(peri: Peri<'d, T>) -> Self {\n        Self::new_algorithm16(peri, Algorithm16::Genibus)\n    }\n\n    /// Instantiates a new CRC peripheral for the `Kermit` algorithm.\n    pub fn new_kermit<T: Instance>(peri: Peri<'d, T>) -> Self {\n        Self::new_algorithm16(peri, Algorithm16::Kermit)\n    }\n\n    /// Instantiates a new CRC peripheral for the `Maxim` algorithm.\n    pub fn new_maxim<T: Instance>(peri: Peri<'d, T>) -> Self {\n        Self::new_algorithm16(peri, Algorithm16::Maxim)\n    }\n\n    /// Instantiates a new CRC peripheral for the `Mcrf4xx` algorithm.\n    pub fn new_mcrf4xx<T: Instance>(peri: Peri<'d, T>) -> Self {\n        Self::new_algorithm16(peri, Algorithm16::Mcrf4xx)\n    }\n\n    /// Instantiates a new CRC peripheral for the `Modbus` algorithm.\n    pub fn new_modbus<T: Instance>(peri: Peri<'d, T>) -> Self {\n        Self::new_algorithm16(peri, Algorithm16::Modbus)\n    }\n\n    /// Instantiates a new CRC peripheral for the `Riello` algorithm.\n    pub fn new_riello<T: Instance>(peri: Peri<'d, T>) -> Self {\n        Self::new_algorithm16(peri, Algorithm16::Riello)\n    }\n\n    /// Instantiates a new CRC peripheral for the `T10Dif` algorithm.\n    pub fn new_t10_dif<T: Instance>(peri: Peri<'d, T>) -> Self {\n        Self::new_algorithm16(peri, Algorithm16::T10Dif)\n    }\n\n    /// Instantiates a new CRC peripheral for the `Teledisk` algorithm.\n    pub fn new_teledisk<T: Instance>(peri: Peri<'d, T>) -> Self {\n        Self::new_algorithm16(peri, Algorithm16::Teledisk)\n    }\n\n    /// Instantiates a new CRC peripheral for the `Tms37157` algorithm.\n    pub fn new_tms_37157<T: Instance>(peri: Peri<'d, T>) -> Self {\n        Self::new_algorithm16(peri, Algorithm16::Tms37157)\n    }\n\n    /// Instantiates a new CRC peripheral for the `Usb` algorithm.\n    pub fn new_usb<T: Instance>(peri: Peri<'d, T>) -> Self {\n        Self::new_algorithm16(peri, Algorithm16::Usb)\n    }\n\n    /// Instantiates a new CRC peripheral for the `X25` algorithm.\n    pub fn new_x25<T: Instance>(peri: Peri<'d, T>) -> Self {\n        Self::new_algorithm16(peri, Algorithm16::X25)\n    }\n\n    /// Instantiates a new CRC peripheral for the `Xmodem` algorithm.\n    pub fn new_xmodem<T: Instance>(peri: Peri<'d, T>) -> Self {\n        Self::new_algorithm16(peri, Algorithm16::Xmodem)\n    }\n\n    /// Feeds a slice of `Word`s into the CRC peripheral.\n    ///\n    /// The input is strided efficiently into as many `u32`s as possible,\n    /// falling back to smaller writes for the remainder.\n    pub fn feed<W: Word>(&mut self, data: &[W]) {\n        self.feed_inner(data);\n    }\n\n    /// Finalizes the CRC calculation and reads the resulting CRC from the\n    /// hardware consuming `self`.\n    pub fn finalize(self) -> u16 {\n        self.finalize_inner()\n    }\n}\n\nimpl<'d> Crc<'d, Crc32> {\n    /// Instantiates a new CRC peripheral driver in 32-bit mode\n    pub fn new_crc32<T: Instance>(peri: Peri<'d, T>, config: Config) -> Self {\n        let mut inst = Self::new_inner(peri);\n        inst.configure(config, Tcrc::B32);\n        inst\n    }\n\n    /// Instantiates a new CRC peripheral driver for the given `Algorithm32`.\n    pub fn new_algorithm32<T: Instance>(peri: Peri<'d, T>, algorithm: Algorithm32) -> Self {\n        Self::new_crc32(peri, algorithm.into_config())\n    }\n\n    /// Instantiates a new CRC peripheral for the `Bzip2` algorithm.\n    pub fn new_bzip2<T: Instance>(peri: Peri<'d, T>) -> Self {\n        Self::new_algorithm32(peri, Algorithm32::Bzip2)\n    }\n\n    /// Instantiates a new CRC peripheral for the `C` algorithm.\n    pub fn new_c<T: Instance>(peri: Peri<'d, T>) -> Self {\n        Self::new_algorithm32(peri, Algorithm32::C)\n    }\n\n    /// Instantiates a new CRC peripheral for the `D` algorithm.\n    pub fn new_d<T: Instance>(peri: Peri<'d, T>) -> Self {\n        Self::new_algorithm32(peri, Algorithm32::D)\n    }\n\n    /// Instantiates a new CRC peripheral for the `IsoHdlc` algorithm.\n    pub fn new_iso_hdlc<T: Instance>(peri: Peri<'d, T>) -> Self {\n        Self::new_algorithm32(peri, Algorithm32::IsoHdlc)\n    }\n\n    /// Instantiates a new CRC peripheral for the `JamCrc` algorithm.\n    pub fn new_jam_crc<T: Instance>(peri: Peri<'d, T>) -> Self {\n        Self::new_algorithm32(peri, Algorithm32::JamCrc)\n    }\n\n    /// Instantiates a new CRC peripheral for the `Mpeg2` algorithm.\n    pub fn new_mpeg2<T: Instance>(peri: Peri<'d, T>) -> Self {\n        Self::new_algorithm32(peri, Algorithm32::Mpeg2)\n    }\n\n    /// Instantiates a new CRC peripheral for the `Posix` algorithm.\n    pub fn new_posix<T: Instance>(peri: Peri<'d, T>) -> Self {\n        Self::new_algorithm32(peri, Algorithm32::Posix)\n    }\n\n    /// Instantiates a new CRC peripheral for the `Q` algorithm.\n    pub fn new_q<T: Instance>(peri: Peri<'d, T>) -> Self {\n        Self::new_algorithm32(peri, Algorithm32::Q)\n    }\n\n    /// Instantiates a new CRC peripheral for the `Xfer` algorithm.\n    pub fn new_xfer<T: Instance>(peri: Peri<'d, T>) -> Self {\n        Self::new_algorithm32(peri, Algorithm32::Xfer)\n    }\n\n    /// Feeds a slice of `Word`s into the CRC peripheral.\n    ///\n    /// The input is strided efficiently into as many `u32`s as possible,\n    /// falling back to smaller writes for the remainder.\n    pub fn feed<W: Word>(&mut self, data: &[W]) {\n        self.feed_inner(data);\n    }\n\n    /// Finalizes the CRC calculation and reads the resulting CRC from the\n    /// hardware consuming `self`.\n    pub fn finalize(self) -> u32 {\n        self.finalize_inner()\n    }\n}\n\nmod sealed {\n    pub trait SealedMode {}\n\n    pub trait SealedWord: Copy {\n        fn write(regs: crate::pac::crc::Crc, word: Self);\n        fn read(regs: crate::pac::crc::Crc) -> Self;\n    }\n}\n\n/// Mode of operation: 32 or 16-bit CRC.\n#[allow(private_bounds)]\npub trait Mode: sealed::SealedMode {}\n\n/// 16-bit CRC.\npub struct Crc16;\nimpl sealed::SealedMode for Crc16 {}\nimpl Mode for Crc16 {}\n\n/// 32-bit CRC.\npub struct Crc32;\nimpl sealed::SealedMode for Crc32 {}\nimpl Mode for Crc32 {}\n\n/// Word size for the CRC.\n#[allow(private_bounds)]\npub trait Word: sealed::SealedWord {}\n\nmacro_rules! impl_word {\n    ($t:ty, $width:literal, $write:expr, $read:expr) => {\n        impl sealed::SealedWord for $t {\n            #[inline]\n            fn write(regs: crate::pac::crc::Crc, word: Self) {\n                $write(regs, word)\n            }\n\n            #[inline]\n            fn read(regs: crate::pac::crc::Crc) -> Self {\n                $read(regs)\n            }\n        }\n\n        impl Word for $t {}\n    };\n}\n\nimpl_word!(u8, 8, write_u8, read_u8);\nimpl_word!(u16, 16, write_u16, read_u16);\nimpl_word!(u32, 32, write_u32, read_u32);\n\nfn write_u8(regs: crate::pac::crc::Crc, word: u8) {\n    regs.data8().write(|w| *w = word);\n}\n\nfn read_u8(regs: crate::pac::crc::Crc) -> u8 {\n    regs.data8().read()\n}\n\nfn write_u16(regs: crate::pac::crc::Crc, word: u16) {\n    regs.data16().write(|w| *w = word);\n}\n\nfn read_u16(regs: crate::pac::crc::Crc) -> u16 {\n    let ctrl = regs.ctrl().read();\n\n    // if transposition is enabled, result sits in the upper 16 bits\n    if matches!(ctrl.totr(), Totr::BYTS_TRNPS | Totr::BYTS_BTS_TRNPS) {\n        (regs.data32().read() >> 16) as u16\n    } else {\n        regs.data16().read()\n    }\n}\n\nfn write_u32(regs: crate::pac::crc::Crc, word: u32) {\n    regs.data32().write(|w| *w = word);\n}\n\nfn read_u32(regs: crate::pac::crc::Crc) -> u32 {\n    regs.data32().read()\n}\n\n/// CRC configuration.\n#[derive(Copy, Clone, Debug)]\n#[non_exhaustive]\npub struct Config {\n    /// The CRC polynomial to be used.\n    pub polynomial: u32,\n\n    /// Reflect bit order of input?\n    pub reflect_in: Reflect,\n\n    /// Reflect CRC bit order?\n    pub reflect_out: Reflect,\n\n    /// 1's complement CRC?\n    pub complement_out: Complement,\n\n    /// CRC Seed\n    pub seed: u32,\n}\n\nimpl Config {\n    /// Create a new CRC config.\n    #[must_use]\n    pub fn new(\n        polynomial: u32,\n        reflect_in: Reflect,\n        reflect_out: Reflect,\n        complement_out: Complement,\n        seed: u32,\n    ) -> Self {\n        Config {\n            polynomial,\n            reflect_in,\n            reflect_out,\n            complement_out,\n            seed,\n        }\n    }\n}\n\nimpl Default for Config {\n    fn default() -> Self {\n        Self {\n            polynomial: 0,\n            reflect_in: Reflect::No,\n            reflect_out: Reflect::No,\n            complement_out: Complement::No,\n            seed: 0xffff_ffff,\n        }\n    }\n}\n\n/// Supported standard CRC16 algorithms.\n#[derive(Copy, Clone, Debug)]\npub enum Algorithm16 {\n    A,\n    Arc,\n    AugCcitt,\n    Buypass,\n    CcittFalse,\n    CcittZero,\n    Cdma2000,\n    Dds110,\n    DectX,\n    Dnp,\n    En13757,\n    Genibus,\n    Kermit,\n    Maxim,\n    Mcrf4xx,\n    Modbus,\n    Riello,\n    T10Dif,\n    Teledisk,\n    Tms37157,\n    Usb,\n    X25,\n    Xmodem,\n}\n\nimpl Algorithm16 {\n    fn into_config(self) -> Config {\n        match self {\n            Algorithm16::A => Config {\n                polynomial: 0x1021,\n                reflect_in: Reflect::Yes,\n                reflect_out: Reflect::Yes,\n                complement_out: Complement::No,\n                seed: 0xc6c6,\n            },\n            Algorithm16::Arc => Config {\n                polynomial: 0x8005,\n                reflect_in: Reflect::Yes,\n                reflect_out: Reflect::Yes,\n                complement_out: Complement::No,\n                seed: 0,\n            },\n            Algorithm16::AugCcitt => Config {\n                polynomial: 0x1021,\n                reflect_in: Reflect::No,\n                reflect_out: Reflect::No,\n                complement_out: Complement::No,\n                seed: 0x1d0f,\n            },\n            Algorithm16::Buypass => Config {\n                polynomial: 0x8005,\n                reflect_in: Reflect::No,\n                reflect_out: Reflect::No,\n                complement_out: Complement::No,\n                seed: 0,\n            },\n            Algorithm16::CcittFalse => Config {\n                polynomial: 0x1021,\n                reflect_in: Reflect::No,\n                reflect_out: Reflect::No,\n                complement_out: Complement::No,\n                seed: 0xffff,\n            },\n            Algorithm16::CcittZero => Config {\n                polynomial: 0x1021,\n                reflect_in: Reflect::No,\n                reflect_out: Reflect::No,\n                complement_out: Complement::No,\n                seed: 0,\n            },\n            Algorithm16::Cdma2000 => Config {\n                polynomial: 0xc867,\n                reflect_in: Reflect::No,\n                reflect_out: Reflect::No,\n                complement_out: Complement::No,\n                seed: 0xffff,\n            },\n            Algorithm16::Dds110 => Config {\n                polynomial: 0x8005,\n                reflect_in: Reflect::No,\n                reflect_out: Reflect::No,\n                complement_out: Complement::No,\n                seed: 0x800d,\n            },\n            Algorithm16::DectX => Config {\n                polynomial: 0x0589,\n                reflect_in: Reflect::No,\n                reflect_out: Reflect::No,\n                complement_out: Complement::No,\n                seed: 0,\n            },\n            Algorithm16::Dnp => Config {\n                polynomial: 0x3d65,\n                reflect_in: Reflect::Yes,\n                reflect_out: Reflect::Yes,\n                complement_out: Complement::Yes,\n                seed: 0,\n            },\n            Algorithm16::En13757 => Config {\n                polynomial: 0x3d65,\n                reflect_in: Reflect::No,\n                reflect_out: Reflect::No,\n                complement_out: Complement::Yes,\n                seed: 0,\n            },\n            Algorithm16::Genibus => Config {\n                polynomial: 0x1021,\n                reflect_in: Reflect::No,\n                reflect_out: Reflect::No,\n                complement_out: Complement::Yes,\n                seed: 0xffff,\n            },\n            Algorithm16::Kermit => Config {\n                polynomial: 0x1021,\n                reflect_in: Reflect::Yes,\n                reflect_out: Reflect::Yes,\n                complement_out: Complement::No,\n                seed: 0,\n            },\n            Algorithm16::Maxim => Config {\n                polynomial: 0x8005,\n                reflect_in: Reflect::Yes,\n                reflect_out: Reflect::Yes,\n                complement_out: Complement::Yes,\n                seed: 0,\n            },\n            Algorithm16::Mcrf4xx => Config {\n                polynomial: 0x1021,\n                reflect_in: Reflect::Yes,\n                reflect_out: Reflect::Yes,\n                complement_out: Complement::No,\n                seed: 0xffff,\n            },\n            Algorithm16::Modbus => Config {\n                polynomial: 0x8005,\n                reflect_in: Reflect::Yes,\n                reflect_out: Reflect::Yes,\n                complement_out: Complement::No,\n                seed: 0xffff,\n            },\n            Algorithm16::Riello => Config {\n                polynomial: 0x1021,\n                reflect_in: Reflect::Yes,\n                reflect_out: Reflect::Yes,\n                complement_out: Complement::No,\n                seed: 0xb2aa,\n            },\n            Algorithm16::T10Dif => Config {\n                polynomial: 0x8bb7,\n                reflect_in: Reflect::No,\n                reflect_out: Reflect::No,\n                complement_out: Complement::No,\n                seed: 0,\n            },\n            Algorithm16::Teledisk => Config {\n                polynomial: 0xa097,\n                reflect_in: Reflect::No,\n                reflect_out: Reflect::No,\n                complement_out: Complement::No,\n                seed: 0,\n            },\n            Algorithm16::Tms37157 => Config {\n                polynomial: 0x1021,\n                reflect_in: Reflect::Yes,\n                reflect_out: Reflect::Yes,\n                complement_out: Complement::No,\n                seed: 0x89ec,\n            },\n            Algorithm16::Usb => Config {\n                polynomial: 0x8005,\n                reflect_in: Reflect::Yes,\n                reflect_out: Reflect::Yes,\n                complement_out: Complement::No,\n                seed: 0xffff,\n            },\n            Algorithm16::X25 => Config {\n                polynomial: 0x1021,\n                reflect_in: Reflect::Yes,\n                reflect_out: Reflect::Yes,\n                complement_out: Complement::Yes,\n                seed: 0xffff,\n            },\n            Algorithm16::Xmodem => Config {\n                polynomial: 0x1021,\n                reflect_in: Reflect::No,\n                reflect_out: Reflect::No,\n                complement_out: Complement::No,\n                seed: 0,\n            },\n        }\n    }\n}\n\n/// Supported standard CRC32 algorithms.\n#[derive(Copy, Clone, Debug)]\npub enum Algorithm32 {\n    Bzip2,\n    C,\n    D,\n    IsoHdlc,\n    JamCrc,\n    Mpeg2,\n    Posix,\n    Q,\n    Xfer,\n}\n\nimpl Algorithm32 {\n    fn into_config(self) -> Config {\n        match self {\n            Algorithm32::Bzip2 => Config {\n                polynomial: 0x04c1_1db7,\n                reflect_in: Reflect::No,\n                reflect_out: Reflect::No,\n                complement_out: Complement::Yes,\n                seed: 0xffff_ffff,\n            },\n            Algorithm32::C => Config {\n                polynomial: 0x1edc_6f41,\n                reflect_in: Reflect::Yes,\n                reflect_out: Reflect::Yes,\n                complement_out: Complement::Yes,\n                seed: 0xffff_ffff,\n            },\n            Algorithm32::D => Config {\n                polynomial: 0xa833_982b,\n                reflect_in: Reflect::Yes,\n                reflect_out: Reflect::Yes,\n                complement_out: Complement::Yes,\n                seed: 0xffff_ffff,\n            },\n            Algorithm32::IsoHdlc => Config {\n                polynomial: 0x04c1_1db7,\n                reflect_in: Reflect::Yes,\n                reflect_out: Reflect::Yes,\n                complement_out: Complement::Yes,\n                seed: 0xffff_ffff,\n            },\n            Algorithm32::JamCrc => Config {\n                polynomial: 0x04c1_1db7,\n                reflect_in: Reflect::Yes,\n                reflect_out: Reflect::Yes,\n                complement_out: Complement::No,\n                seed: 0xffff_ffff,\n            },\n            Algorithm32::Mpeg2 => Config {\n                polynomial: 0x04c1_1db7,\n                reflect_in: Reflect::No,\n                reflect_out: Reflect::No,\n                complement_out: Complement::No,\n                seed: 0xffff_ffff,\n            },\n            Algorithm32::Posix => Config {\n                polynomial: 0x04c1_1db7,\n                reflect_in: Reflect::No,\n                reflect_out: Reflect::No,\n                complement_out: Complement::Yes,\n                seed: 0,\n            },\n            Algorithm32::Q => Config {\n                polynomial: 0x8141_41ab,\n                reflect_in: Reflect::No,\n                reflect_out: Reflect::No,\n                complement_out: Complement::No,\n                seed: 0,\n            },\n            Algorithm32::Xfer => Config {\n                polynomial: 0x0000_00af,\n                reflect_in: Reflect::No,\n                reflect_out: Reflect::No,\n                complement_out: Complement::No,\n                seed: 0,\n            },\n        }\n    }\n}\n\n/// Reflect bit order.\n#[derive(Copy, Clone, Debug)]\npub enum Reflect {\n    No,\n    Yes,\n}\n\nimpl From<Reflect> for Tot {\n    fn from(value: Reflect) -> Tot {\n        match value {\n            Reflect::No => Tot::BYTS_TRNPS,\n            Reflect::Yes => Tot::BYTS_BTS_TRNPS,\n        }\n    }\n}\n\nimpl From<Reflect> for Totr {\n    fn from(value: Reflect) -> Totr {\n        match value {\n            Reflect::No => Totr::NOTRNPS,\n            Reflect::Yes => Totr::BYTS_BTS_TRNPS,\n        }\n    }\n}\n\n/// 1's complement output.\n#[derive(Copy, Clone, Debug)]\npub enum Complement {\n    No,\n    Yes,\n}\n\nimpl From<Complement> for Fxor {\n    fn from(value: Complement) -> Fxor {\n        match value {\n            Complement::No => Fxor::NOXOR,\n            Complement::Yes => Fxor::INVERT,\n        }\n    }\n}\n\ntrait SealedInstance: Gate<MrccPeriphConfig = NoConfig> {\n    fn info() -> &'static Info;\n}\n\n/// CRC Instance\n#[allow(private_bounds)]\npub trait Instance: SealedInstance + PeripheralType + 'static + Send {}\n\nstruct Info {\n    regs: pac::crc::Crc,\n}\n\nimpl Info {\n    #[inline(always)]\n    fn regs(&self) -> pac::crc::Crc {\n        self.regs\n    }\n}\n\nunsafe impl Sync for Info {}\n\nmacro_rules! impl_instance {\n    ($($n:literal),*) => {\n        $(\n            paste!{\n                impl SealedInstance for crate::peripherals::[<CRC $n>] {\n                    fn info() -> &'static Info {\n                        static INFO: Info = Info {\n                            regs: pac::[<CRC $n>],\n                        };\n                        &INFO\n                    }\n                }\n\n                impl Instance for crate::peripherals::[<CRC $n>] {}\n            }\n        )*\n    };\n}\n\nimpl_instance!(0);\n"
  },
  {
    "path": "embassy-mcxa/src/ctimer/capture.rs",
    "content": "//! CTimer-based Capture driver\n\nuse core::fmt;\nuse core::marker::PhantomData;\nuse core::ops::{Add, Sub};\nuse core::sync::atomic::Ordering;\n\nuse embassy_hal_internal::Peri;\nuse nxp_pac::ctimer::vals::{Capfe, Capi, Capre};\n\nuse super::{AnyChannel, CTimer, CTimerChannel, Channel, Info, InputPin, Instance};\nuse crate::clocks::WakeGuard;\nuse crate::gpio::{AnyPin, SealedPin};\nuse crate::inputmux::{SealedValidInputMuxConfig, ValidInputMuxConfig};\nuse crate::interrupt;\nuse crate::interrupt::typelevel::Interrupt;\n\n/// Capture error.\n#[derive(Debug)]\n#[non_exhaustive]\npub enum CaptureError {\n    /// Other\n    Other,\n}\n\n/// Capture configuration\n#[derive(Debug, Copy, Clone, Default)]\n#[non_exhaustive]\npub struct Config {\n    /// Edge capture\n    pub edge: Edge,\n}\n\n/// Capture configuration\n#[derive(Debug, Copy, Clone, Default)]\npub enum Edge {\n    /// Rising edge\n    RisingEdge,\n    /// Falling edge\n    FallingEdge,\n    /// Both edges\n    #[default]\n    Both,\n}\n\n/// Timestamp capture\n///\n/// Timestamp value in ticks.\n#[derive(Debug, Copy, Clone)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub struct Timestamp(u32);\n\nimpl Timestamp {\n    #[inline]\n    fn ticks(self) -> u32 {\n        self.0\n    }\n\n    #[inline]\n    fn with_ticks(self, ticks: u32) -> Self {\n        Self(ticks)\n    }\n}\n\n#[derive(Copy, Clone, Eq, PartialEq, PartialOrd, Ord, Default)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub struct TicksDiff(pub i32);\n\nimpl TicksDiff {\n    #[inline]\n    pub fn to_period(self, tick_hz: u32) -> f32 {\n        assert!(tick_hz != 0);\n        self.0 as f32 / tick_hz as f32\n    }\n\n    #[inline]\n    pub fn to_frequency(self, tick_hz: u32) -> f32 {\n        assert!(self.0 != 0);\n        tick_hz as f32 / self.0 as f32\n    }\n\n    #[inline]\n    pub fn abs(self) -> TicksDiff {\n        TicksDiff(self.0.abs())\n    }\n}\n\nimpl fmt::Debug for TicksDiff {\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        write!(f, \"{} ticks\", self.0)\n    }\n}\n\nimpl Add for Timestamp {\n    type Output = TicksDiff;\n\n    #[inline]\n    fn add(self, rhs: Self) -> Self::Output {\n        let lhs = self.ticks() as i32;\n        let rhs = rhs.ticks() as i32;\n        let raw = lhs.wrapping_add(rhs);\n        TicksDiff(raw)\n    }\n}\n\nimpl Add<u32> for Timestamp {\n    type Output = Timestamp;\n\n    #[inline]\n    fn add(self, rhs: u32) -> Self::Output {\n        self.with_ticks(self.ticks().wrapping_add(rhs))\n    }\n}\n\nimpl Add<TicksDiff> for Timestamp {\n    type Output = Timestamp;\n\n    #[inline]\n    fn add(self, rhs: TicksDiff) -> Self::Output {\n        let t = self.ticks().wrapping_add(rhs.0 as u32);\n        self.with_ticks(t)\n    }\n}\n\nimpl Sub for Timestamp {\n    type Output = TicksDiff;\n\n    #[inline]\n    fn sub(self, rhs: Self) -> Self::Output {\n        let lhs = self.ticks() as i32;\n        let rhs = rhs.ticks() as i32;\n        let raw = lhs.wrapping_sub(rhs);\n        TicksDiff(raw)\n    }\n}\n\nimpl Sub<u32> for Timestamp {\n    type Output = Timestamp;\n\n    #[inline]\n    fn sub(self, rhs: u32) -> Self::Output {\n        self.with_ticks(self.ticks().wrapping_sub(rhs))\n    }\n}\n\nimpl Sub<TicksDiff> for Timestamp {\n    type Output = Timestamp;\n\n    #[inline]\n    fn sub(self, rhs: TicksDiff) -> Self::Output {\n        // Subtracting a signed diff == adding the negated diff\n        let t = self.ticks().wrapping_sub(rhs.0 as u32);\n        self.with_ticks(t)\n    }\n}\n\n/// Capture driver\npub struct Capture<'d> {\n    info: &'static Info,\n    ch: Peri<'d, AnyChannel>,\n    pin: Peri<'d, AnyPin>,\n    source_freq: u32,\n    _wg: Option<WakeGuard>,\n}\n\nimpl<'d> Capture<'d> {\n    /// Create Capture driver\n    ///\n    /// Upon `Drop`, the external `pin` will be placed into `Disabled`\n    /// state.\n    pub fn new_with_input_pin<T: Instance, CH: CTimerChannel<T>, PIN: InputPin>(\n        ctimer: CTimer<'d>,\n        ch: Peri<'d, CH>,\n        pin: Peri<'d, PIN>,\n        _irq: impl crate::interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd,\n        config: Config,\n    ) -> Result<Self, CaptureError>\n    where\n        (T, CH, PIN): ValidInputMuxConfig,\n    {\n        pin.mux();\n        <(T, CH, PIN) as SealedValidInputMuxConfig>::mux();\n\n        let mut inst = Self {\n            info: T::info(),\n            ch: ch.into(),\n            pin: pin.into(),\n            source_freq: ctimer._freq,\n            _wg: ctimer._wg.clone(),\n        };\n\n        inst.set_configuration(&config)?;\n\n        T::Interrupt::unpend();\n\n        // Safety: `_irq` ensures an Interrupt Handler exists.\n        unsafe { T::Interrupt::enable() };\n\n        // Enable CTimer\n        inst.info.regs().tcr().modify(|w| w.set_cen(true));\n\n        Ok(inst)\n    }\n\n    fn set_configuration(&mut self, config: &Config) -> Result<(), CaptureError> {\n        self.info.regs().ccr().modify(|w| {\n            match self.ch.number() {\n                Channel::Zero => match config.edge {\n                    Edge::Both => {\n                        w.set_cap0re(Capre::CAPRE1);\n                        w.set_cap0fe(Capfe::CAPFE1);\n                    }\n                    Edge::RisingEdge => {\n                        w.set_cap0re(Capre::CAPRE1);\n                    }\n                    Edge::FallingEdge => {\n                        w.set_cap0fe(Capfe::CAPFE1);\n                    }\n                },\n                Channel::One => match config.edge {\n                    Edge::Both => {\n                        w.set_cap1re(Capre::CAPRE1);\n                        w.set_cap1fe(Capfe::CAPFE1);\n                    }\n                    Edge::RisingEdge => {\n                        w.set_cap1re(Capre::CAPRE1);\n                    }\n                    Edge::FallingEdge => {\n                        w.set_cap1fe(Capfe::CAPFE1);\n                    }\n                },\n                Channel::Two => match config.edge {\n                    Edge::Both => {\n                        w.set_cap2re(Capre::CAPRE1);\n                        w.set_cap2fe(Capfe::CAPFE1);\n                    }\n                    Edge::RisingEdge => {\n                        w.set_cap2re(Capre::CAPRE1);\n                    }\n                    Edge::FallingEdge => {\n                        w.set_cap2fe(Capfe::CAPFE1);\n                    }\n                },\n                Channel::Three => match config.edge {\n                    Edge::Both => {\n                        w.set_cap3re(Capre::CAPRE1);\n                        w.set_cap3fe(Capfe::CAPFE1);\n                    }\n                    Edge::RisingEdge => {\n                        w.set_cap3re(Capre::CAPRE1);\n                    }\n                    Edge::FallingEdge => {\n                        w.set_cap3fe(Capfe::CAPFE1);\n                    }\n                },\n            };\n        });\n\n        Ok(())\n    }\n\n    pub fn frequency(&self) -> u32 {\n        self.source_freq\n    }\n\n    pub async fn capture(&mut self) -> Result<Timestamp, CaptureError> {\n        self.info\n            .wait_cell()\n            .wait_for(|| {\n                self.info.regs().ccr().modify(|w| match self.ch.number() {\n                    Channel::Zero => {\n                        w.set_cap0i(Capi::CAPI1);\n                    }\n                    Channel::One => {\n                        w.set_cap1i(Capi::CAPI1);\n                    }\n                    Channel::Two => {\n                        w.set_cap2i(Capi::CAPI1);\n                    }\n                    Channel::Three => {\n                        w.set_cap3i(Capi::CAPI1);\n                    }\n                });\n\n                let n: usize = self.ch.number().into();\n                let mask = 1 << n;\n                (self.info.irq_flags().fetch_and(!mask, Ordering::AcqRel)) != 0\n            })\n            .await\n            .map_err(|_| CaptureError::Other)?;\n\n        let timestamp = self.info.regs().cr(self.ch.number().into()).read().cap();\n        Ok(Timestamp(timestamp))\n    }\n}\n\nimpl<'d> Drop for Capture<'d> {\n    fn drop(&mut self) {\n        self.pin.set_as_disabled();\n    }\n}\n\n/// CTimer interrupt handler.\npub struct InterruptHandler<T: Instance> {\n    _phantom: PhantomData<T>,\n}\n\nimpl<T: Instance> interrupt::typelevel::Handler<T::Interrupt> for InterruptHandler<T> {\n    unsafe fn on_interrupt() {\n        T::PERF_INT_INCR();\n        // Clear interrupt status\n        let ir = T::info().regs().ir().read();\n        T::info().regs().ir().write(|w| w.0 = ir.0);\n\n        let mut mask = 0;\n        T::info().regs().ccr().modify(|w| {\n            if ir.cr0int() {\n                w.set_cap0i(Capi::CAPI0);\n                mask |= 1 << 0;\n            }\n\n            if ir.cr1int() {\n                w.set_cap1i(Capi::CAPI0);\n                mask |= 1 << 1;\n            }\n\n            if ir.cr2int() {\n                w.set_cap2i(Capi::CAPI0);\n                mask |= 1 << 2;\n            }\n\n            if ir.cr3int() {\n                w.set_cap3i(Capi::CAPI0);\n                mask |= 1 << 3;\n            }\n        });\n        T::info().irq_flags().fetch_or(mask, Ordering::Release);\n\n        T::PERF_INT_WAKE_INCR();\n        T::info().wait_cell().wake();\n    }\n}\n\nimpl<'d> embassy_embedded_hal::SetConfig for Capture<'d> {\n    type Config = Config;\n    type ConfigError = CaptureError;\n\n    fn set_config(&mut self, config: &Self::Config) -> Result<(), Self::ConfigError> {\n        self.set_configuration(config)\n    }\n}\n"
  },
  {
    "path": "embassy-mcxa/src/ctimer/mod.rs",
    "content": "//! CTimer driver.\n\nuse core::marker::PhantomData;\nuse core::sync::atomic::AtomicU8;\n\nuse embassy_hal_internal::{Peri, PeripheralType};\nuse maitake_sync::WaitCell;\nuse paste::paste;\n\nuse crate::clocks::periph_helpers::{CTimerClockSel, CTimerConfig, Div4};\nuse crate::clocks::{ClockError, Gate, PoweredClock, WakeGuard, enable_and_reset};\nuse crate::gpio::{GpioPin, SealedPin};\nuse crate::{interrupt, pac};\n\npub mod capture;\npub mod pwm;\n\n/// CTimer channel\n#[derive(Debug, Copy, Clone, PartialEq, Eq)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub enum Channel {\n    /// Channel 0\n    Zero,\n    /// Channel 1\n    One,\n    /// Channel 2\n    Two,\n    /// Channel 3\n    Three,\n}\n\nimpl From<Channel> for usize {\n    fn from(value: Channel) -> usize {\n        match value {\n            Channel::Zero => 0,\n            Channel::One => 1,\n            Channel::Two => 2,\n            Channel::Three => 3,\n        }\n    }\n}\n\n/// Error information type\n#[derive(Debug, Copy, Clone, PartialEq, Eq)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\n#[non_exhaustive]\npub enum Error {\n    /// Clock configuration error.\n    ClockSetup(ClockError),\n\n    /// Other internal errors or unexpected state.\n    Other,\n}\n\n/// CTimer configuration\n#[derive(Debug, Copy, Clone)]\n#[non_exhaustive]\npub struct Config {\n    /// Powered clock configuration\n    pub power: PoweredClock,\n    /// CTimer clock source\n    pub source: CTimerClockSel,\n    /// CTimer pre-divider\n    pub div: Div4,\n}\n\nimpl Default for Config {\n    fn default() -> Self {\n        Self {\n            power: PoweredClock::NormalEnabledDeepSleepDisabled,\n            source: CTimerClockSel::FroLfDiv,\n            div: const { Div4::no_div() },\n        }\n    }\n}\n\n/// CTimer core driver.\n#[derive(Clone)]\npub struct CTimer<'d> {\n    _freq: u32,\n    _wg: Option<WakeGuard>,\n    _phantom: PhantomData<&'d mut ()>,\n}\n\nimpl<'d> CTimer<'d> {\n    /// Create a new instance of the CTimer core cdriver.\n    pub fn new<T: Instance>(_peri: Peri<'d, T>, config: Config) -> Result<Self, Error> {\n        // Enable clocks\n        let conf = CTimerConfig {\n            power: config.power,\n            source: config.source,\n            div: config.div,\n            instance: T::CLOCK_INSTANCE,\n        };\n\n        let parts = unsafe { enable_and_reset::<T>(&conf).map_err(Error::ClockSetup)? };\n\n        let inst = Self {\n            _freq: parts.freq,\n            _wg: parts.wake_guard,\n            _phantom: PhantomData,\n        };\n\n        Ok(inst)\n    }\n}\n\nstruct Info {\n    regs: pac::ctimer::Ctimer,\n    wait_cell: WaitCell,\n    irq_flags: AtomicU8,\n}\n\nimpl Info {\n    #[inline(always)]\n    fn regs(&self) -> pac::ctimer::Ctimer {\n        self.regs\n    }\n\n    #[inline(always)]\n    fn wait_cell(&self) -> &WaitCell {\n        &self.wait_cell\n    }\n\n    #[inline(always)]\n    fn irq_flags(&self) -> &AtomicU8 {\n        &self.irq_flags\n    }\n}\n\nunsafe impl Sync for Info {}\n\ntrait SealedInstance: Gate<MrccPeriphConfig = CTimerConfig> {\n    fn info() -> &'static Info;\n\n    /// Clock instance\n    const CLOCK_INSTANCE: crate::clocks::periph_helpers::CTimerInstance;\n    const PERF_INT_INCR: fn();\n    const PERF_INT_WAKE_INCR: fn();\n}\n\n/// CTimer Instance\n#[allow(private_bounds)]\npub trait Instance: SealedInstance + PeripheralType + 'static + Send {\n    /// Interrupt for this I2C instance.\n    type Interrupt: interrupt::typelevel::Interrupt;\n}\n\nmacro_rules! impl_instance {\n    ($peri:ident, $clock:ident, $perf:ident) => {\n        impl SealedInstance for crate::peripherals::$peri {\n            fn info() -> &'static Info {\n                static INFO: Info = Info {\n                    regs: pac::$peri,\n                    wait_cell: WaitCell::new(),\n                    irq_flags: const { AtomicU8::new(0) },\n                };\n                &INFO\n            }\n\n            const CLOCK_INSTANCE: crate::clocks::periph_helpers::CTimerInstance =\n                crate::clocks::periph_helpers::CTimerInstance::$clock;\n            paste! {\n                const PERF_INT_INCR: fn() = crate::perf_counters::[<incr_interrupt_ $perf>];\n                const PERF_INT_WAKE_INCR: fn() = crate::perf_counters::[<incr_interrupt_ $perf _wake>];\n            }\n        }\n\n        impl Instance for crate::peripherals::$peri {\n            type Interrupt = crate::interrupt::typelevel::$peri;\n        }\n    };\n}\n\nimpl_instance!(CTIMER0, CTimer0, ctimer0);\nimpl_instance!(CTIMER1, CTimer1, ctimer1);\nimpl_instance!(CTIMER2, CTimer2, ctimer2);\nimpl_instance!(CTIMER3, CTimer3, ctimer3);\nimpl_instance!(CTIMER4, CTimer4, ctimer4);\n\ntrait SealedCTimerChannel<T: Instance> {\n    fn number(&self) -> Channel;\n}\n\n/// CTimer channel\n#[allow(private_bounds)]\npub trait CTimerChannel<T: Instance>:\n    SealedCTimerChannel<T> + PeripheralType + Into<AnyChannel> + 'static + Send\n{\n}\n\nmacro_rules! impl_channel {\n    ($ch:ident, $peri:ident, $n:ident) => {\n        impl SealedCTimerChannel<crate::peripherals::$peri> for crate::peripherals::$ch {\n            #[inline(always)]\n            fn number(&self) -> Channel {\n                Channel::$n\n            }\n        }\n\n        impl CTimerChannel<crate::peripherals::$peri> for crate::peripherals::$ch {}\n\n        impl From<crate::peripherals::$ch> for AnyChannel {\n            fn from(value: crate::peripherals::$ch) -> Self {\n                Self {\n                    number: value.number(),\n                }\n            }\n        }\n    };\n}\n\nimpl_channel!(CTIMER0_CH0, CTIMER0, Zero);\nimpl_channel!(CTIMER0_CH1, CTIMER0, One);\nimpl_channel!(CTIMER0_CH2, CTIMER0, Two);\nimpl_channel!(CTIMER0_CH3, CTIMER0, Three);\n\nimpl_channel!(CTIMER1_CH0, CTIMER1, Zero);\nimpl_channel!(CTIMER1_CH1, CTIMER1, One);\nimpl_channel!(CTIMER1_CH2, CTIMER1, Two);\nimpl_channel!(CTIMER1_CH3, CTIMER1, Three);\n\nimpl_channel!(CTIMER2_CH0, CTIMER2, Zero);\nimpl_channel!(CTIMER2_CH1, CTIMER2, One);\nimpl_channel!(CTIMER2_CH2, CTIMER2, Two);\nimpl_channel!(CTIMER2_CH3, CTIMER2, Three);\n\nimpl_channel!(CTIMER3_CH0, CTIMER3, Zero);\nimpl_channel!(CTIMER3_CH1, CTIMER3, One);\nimpl_channel!(CTIMER3_CH2, CTIMER3, Two);\nimpl_channel!(CTIMER3_CH3, CTIMER3, Three);\n\nimpl_channel!(CTIMER4_CH0, CTIMER4, Zero);\nimpl_channel!(CTIMER4_CH1, CTIMER4, One);\nimpl_channel!(CTIMER4_CH2, CTIMER4, Two);\nimpl_channel!(CTIMER4_CH3, CTIMER4, Three);\n\n/// Type-erase CTIMER channel\npub struct AnyChannel {\n    number: Channel,\n}\n\nimpl AnyChannel {\n    fn number(&self) -> Channel {\n        self.number\n    }\n}\n\nembassy_hal_internal::impl_peripheral!(AnyChannel);\n\n/// Seal a trait\ntrait SealedInputPin {}\n\n/// Seal a trait\ntrait SealedOutputPin<T: Instance> {}\n\n/// CTimer input pin.\n#[allow(private_bounds)]\npub trait InputPin: GpioPin + SealedInputPin + PeripheralType {\n    fn mux(&self);\n}\n\n/// CTimer output pin.\n#[allow(private_bounds)]\npub trait OutputPin<T: Instance>: GpioPin + SealedOutputPin<T> + PeripheralType {\n    fn mux(&self);\n}\n\nmacro_rules! impl_input_pin {\n    ($pin:ident, $fn:ident) => {\n        impl SealedInputPin for crate::peripherals::$pin {}\n\n        impl InputPin for crate::peripherals::$pin {\n            #[inline(always)]\n            fn mux(&self) {\n                self.set_pull(crate::gpio::Pull::Disabled);\n                self.set_slew_rate(crate::gpio::SlewRate::Fast.into());\n                self.set_drive_strength(crate::gpio::DriveStrength::Double.into());\n                self.set_function(crate::pac::port::vals::Mux::$fn);\n                self.set_enable_input_buffer(true);\n            }\n        }\n    };\n}\n\nmacro_rules! impl_output_pin {\n    ($pin:ident, $peri:ident, $fn:ident) => {\n        impl SealedOutputPin<crate::peripherals::$peri> for crate::peripherals::$pin {}\n\n        impl OutputPin<crate::peripherals::$peri> for crate::peripherals::$pin {\n            #[inline(always)]\n            fn mux(&self) {\n                self.set_pull(crate::gpio::Pull::Disabled);\n                self.set_slew_rate(crate::gpio::SlewRate::Fast.into());\n                self.set_drive_strength(crate::gpio::DriveStrength::Normal.into());\n                self.set_function(crate::pac::port::vals::Mux::$fn);\n                self.set_enable_input_buffer(false);\n            }\n        }\n    };\n}\n\n#[cfg(feature = \"mcxa2xx\")]\nmod mcxa2xx {\n    use super::*;\n\n    // Input pins\n    #[cfg(feature = \"swd-as-gpio\")]\n    impl_input_pin!(P0_0, MUX4);\n    #[cfg(feature = \"swd-as-gpio\")]\n    impl_input_pin!(P0_1, MUX4);\n    #[cfg(feature = \"jtag-extras-as-gpio\")]\n    impl_input_pin!(P0_6, MUX4);\n\n    impl_input_pin!(P0_20, MUX4);\n    impl_input_pin!(P0_21, MUX4);\n    impl_input_pin!(P0_22, MUX4);\n    impl_input_pin!(P0_23, MUX4);\n\n    impl_input_pin!(P1_0, MUX4);\n    impl_input_pin!(P1_1, MUX4);\n    impl_input_pin!(P1_2, MUX5);\n    impl_input_pin!(P1_3, MUX5);\n    impl_input_pin!(P1_6, MUX4);\n    impl_input_pin!(P1_7, MUX4);\n    impl_input_pin!(P1_8, MUX4);\n    impl_input_pin!(P1_9, MUX4);\n    impl_input_pin!(P1_14, MUX4);\n    impl_input_pin!(P1_15, MUX4);\n\n    #[cfg(feature = \"sosc-as-gpio\")]\n    impl_input_pin!(P1_30, MUX4);\n    #[cfg(feature = \"sosc-as-gpio\")]\n    impl_input_pin!(P1_31, MUX4);\n\n    impl_input_pin!(P2_0, MUX4);\n    impl_input_pin!(P2_1, MUX4);\n    impl_input_pin!(P2_2, MUX4);\n    impl_input_pin!(P2_3, MUX4);\n    impl_input_pin!(P2_4, MUX4);\n    impl_input_pin!(P2_5, MUX4);\n    impl_input_pin!(P2_6, MUX4);\n    impl_input_pin!(P2_7, MUX4);\n\n    impl_input_pin!(P3_0, MUX4);\n    impl_input_pin!(P3_1, MUX4);\n    impl_input_pin!(P3_8, MUX4);\n    impl_input_pin!(P3_9, MUX4);\n    impl_input_pin!(P3_14, MUX4);\n    impl_input_pin!(P3_15, MUX4);\n    impl_input_pin!(P3_16, MUX4);\n    impl_input_pin!(P3_17, MUX4);\n    impl_input_pin!(P3_22, MUX4);\n    impl_input_pin!(P3_27, MUX4);\n    impl_input_pin!(P3_28, MUX4);\n    impl_input_pin!(P3_29, MUX4);\n\n    impl_input_pin!(P4_6, MUX4);\n    impl_input_pin!(P4_7, MUX4);\n\n    // Output pins\n    #[cfg(feature = \"swd-swo-as-gpio\")]\n    impl_output_pin!(P0_2, CTIMER0, MUX4);\n    #[cfg(feature = \"jtag-extras-as-gpio\")]\n    impl_output_pin!(P0_3, CTIMER0, MUX4);\n    impl_output_pin!(P0_16, CTIMER0, MUX4);\n    impl_output_pin!(P0_17, CTIMER0, MUX4);\n    impl_output_pin!(P0_18, CTIMER0, MUX4);\n    impl_output_pin!(P0_19, CTIMER0, MUX4);\n    impl_output_pin!(P0_22, CTIMER0, MUX5);\n    impl_output_pin!(P0_23, CTIMER0, MUX5);\n\n    impl_output_pin!(P1_0, CTIMER0, MUX5);\n    impl_output_pin!(P1_1, CTIMER0, MUX5);\n    impl_output_pin!(P1_2, CTIMER1, MUX4);\n    impl_output_pin!(P1_3, CTIMER1, MUX4);\n    impl_output_pin!(P1_4, CTIMER1, MUX4);\n    impl_output_pin!(P1_5, CTIMER1, MUX4);\n    impl_output_pin!(P1_6, CTIMER4, MUX5);\n    impl_output_pin!(P1_7, CTIMER4, MUX5);\n    impl_output_pin!(P1_8, CTIMER0, MUX5);\n    impl_output_pin!(P1_9, CTIMER0, MUX5);\n    impl_output_pin!(P1_10, CTIMER2, MUX4);\n    impl_output_pin!(P1_11, CTIMER2, MUX4);\n    impl_output_pin!(P1_12, CTIMER2, MUX4);\n    impl_output_pin!(P1_13, CTIMER2, MUX4);\n    impl_output_pin!(P1_14, CTIMER3, MUX5);\n    impl_output_pin!(P1_15, CTIMER3, MUX5);\n\n    impl_output_pin!(P2_0, CTIMER2, MUX5);\n    impl_output_pin!(P2_1, CTIMER2, MUX5);\n    impl_output_pin!(P2_2, CTIMER2, MUX5);\n    impl_output_pin!(P2_3, CTIMER2, MUX5);\n    impl_output_pin!(P2_4, CTIMER1, MUX5);\n    impl_output_pin!(P2_5, CTIMER1, MUX5);\n    impl_output_pin!(P2_6, CTIMER1, MUX5);\n    impl_output_pin!(P2_7, CTIMER1, MUX5);\n    impl_output_pin!(P2_10, CTIMER3, MUX4);\n    impl_output_pin!(P2_11, CTIMER3, MUX4);\n    impl_output_pin!(P2_12, CTIMER4, MUX4);\n    impl_output_pin!(P2_12, CTIMER0, MUX5);\n    impl_output_pin!(P2_13, CTIMER4, MUX4);\n    impl_output_pin!(P2_13, CTIMER0, MUX5);\n    impl_output_pin!(P2_15, CTIMER4, MUX5);\n    impl_output_pin!(P2_15, CTIMER0, MUX5);\n    impl_output_pin!(P2_16, CTIMER3, MUX5);\n    impl_output_pin!(P2_16, CTIMER0, MUX5);\n    impl_output_pin!(P2_17, CTIMER3, MUX5);\n    impl_output_pin!(P2_17, CTIMER0, MUX5);\n    impl_output_pin!(P2_19, CTIMER3, MUX4);\n    impl_output_pin!(P2_20, CTIMER2, MUX4);\n    impl_output_pin!(P2_21, CTIMER2, MUX4);\n    impl_output_pin!(P2_23, CTIMER2, MUX4);\n\n    impl_output_pin!(P3_2, CTIMER4, MUX4);\n    impl_output_pin!(P3_6, CTIMER4, MUX4);\n    impl_output_pin!(P3_7, CTIMER4, MUX4);\n    impl_output_pin!(P3_10, CTIMER1, MUX4);\n    impl_output_pin!(P3_11, CTIMER1, MUX4);\n    impl_output_pin!(P3_12, CTIMER1, MUX4);\n    impl_output_pin!(P3_13, CTIMER1, MUX4);\n    impl_output_pin!(P3_18, CTIMER2, MUX4);\n    impl_output_pin!(P3_19, CTIMER2, MUX4);\n    impl_output_pin!(P3_20, CTIMER2, MUX4);\n    impl_output_pin!(P3_21, CTIMER2, MUX4);\n    impl_output_pin!(P3_27, CTIMER3, MUX5);\n    impl_output_pin!(P3_28, CTIMER3, MUX5);\n    #[cfg(feature = \"dangerous-reset-as-gpio\")]\n    impl_output_pin!(P3_29, CTIMER3, MUX5);\n    #[cfg(feature = \"sosc-as-gpio\")]\n    impl_output_pin!(P3_30, CTIMER0, MUX4);\n    #[cfg(feature = \"sosc-as-gpio\")]\n    impl_output_pin!(P3_31, CTIMER0, MUX4);\n\n    impl_output_pin!(P4_2, CTIMER4, MUX4);\n    impl_output_pin!(P4_3, CTIMER4, MUX4);\n    impl_output_pin!(P4_4, CTIMER4, MUX4);\n    impl_output_pin!(P4_5, CTIMER4, MUX4);\n}\n\n#[cfg(feature = \"mcxa5xx\")]\nmod mcxa5xx {\n    use super::*;\n\n    // Input pins\n    #[cfg(feature = \"swd-as-gpio\")]\n    impl_input_pin!(P0_0, MUX4);\n    #[cfg(feature = \"swd-as-gpio\")]\n    impl_input_pin!(P0_1, MUX4);\n    #[cfg(feature = \"jtag-extras-as-gpio\")]\n    impl_input_pin!(P0_6, MUX4);\n    impl_input_pin!(P0_7, MUX4);\n    impl_input_pin!(P0_14, MUX4);\n    impl_input_pin!(P0_15, MUX4);\n    impl_input_pin!(P0_20, MUX4);\n    impl_input_pin!(P0_21, MUX4);\n    impl_input_pin!(P0_22, MUX4);\n    impl_input_pin!(P0_23, MUX4);\n\n    impl_input_pin!(P1_0, MUX4);\n    impl_input_pin!(P1_1, MUX4);\n    impl_input_pin!(P1_2, MUX5);\n    impl_input_pin!(P1_3, MUX5);\n    impl_input_pin!(P1_6, MUX4);\n    impl_input_pin!(P1_7, MUX4);\n    impl_input_pin!(P1_8, MUX4);\n    impl_input_pin!(P1_9, MUX4);\n    impl_input_pin!(P1_16, MUX4);\n    impl_input_pin!(P1_17, MUX4);\n    #[cfg(feature = \"sosc-as-gpio\")]\n    impl_input_pin!(P1_30, MUX4);\n    #[cfg(feature = \"sosc-as-gpio\")]\n    impl_input_pin!(P1_31, MUX4);\n\n    impl_input_pin!(P2_0, MUX4);\n    impl_input_pin!(P2_1, MUX4);\n    impl_input_pin!(P2_2, MUX4);\n    impl_input_pin!(P2_3, MUX4);\n    impl_input_pin!(P2_4, MUX4);\n    impl_input_pin!(P2_5, MUX4);\n    impl_input_pin!(P2_6, MUX4);\n    impl_input_pin!(P2_7, MUX4);\n    impl_input_pin!(P2_24, MUX4);\n    impl_input_pin!(P2_25, MUX4);\n    impl_input_pin!(P2_26, MUX4);\n\n    impl_input_pin!(P3_0, MUX4);\n    impl_input_pin!(P3_1, MUX4);\n    impl_input_pin!(P3_4, MUX4);\n    impl_input_pin!(P3_5, MUX4);\n    impl_input_pin!(P3_8, MUX4);\n    impl_input_pin!(P3_9, MUX4);\n    impl_input_pin!(P3_14, MUX4);\n    impl_input_pin!(P3_15, MUX4);\n    impl_input_pin!(P3_16, MUX4);\n    impl_input_pin!(P3_17, MUX4);\n    impl_input_pin!(P3_22, MUX4);\n    impl_input_pin!(P3_23, MUX4);\n    impl_input_pin!(P3_24, MUX4);\n    impl_input_pin!(P3_25, MUX4);\n    impl_input_pin!(P3_26, MUX4);\n    impl_input_pin!(P3_27, MUX4);\n    impl_input_pin!(P3_28, MUX4);\n    impl_input_pin!(P3_29, MUX4);\n\n    impl_input_pin!(P4_6, MUX4);\n    impl_input_pin!(P4_7, MUX4);\n\n    // Output pins\n    #[cfg(feature = \"swd-swo-as-gpio\")]\n    impl_output_pin!(P0_2, CTIMER0, MUX4);\n    #[cfg(feature = \"jtag-extras-as-gpio\")]\n    impl_output_pin!(P0_3, CTIMER0, MUX4);\n    impl_output_pin!(P0_4, CTIMER0, MUX4);\n    impl_output_pin!(P0_5, CTIMER0, MUX4);\n    impl_output_pin!(P0_12, CTIMER0, MUX4);\n    impl_output_pin!(P0_13, CTIMER0, MUX4);\n    impl_output_pin!(P0_16, CTIMER0, MUX4);\n    impl_output_pin!(P0_17, CTIMER0, MUX4);\n    impl_output_pin!(P0_18, CTIMER0, MUX4);\n    impl_output_pin!(P0_19, CTIMER0, MUX4);\n    impl_output_pin!(P0_22, CTIMER0, MUX5);\n    impl_output_pin!(P0_23, CTIMER0, MUX5);\n    impl_output_pin!(P0_24, CTIMER0, MUX4);\n    impl_output_pin!(P0_25, CTIMER0, MUX4);\n    impl_output_pin!(P0_26, CTIMER0, MUX4);\n    impl_output_pin!(P0_27, CTIMER0, MUX4);\n\n    impl_output_pin!(P1_0, CTIMER0, MUX5);\n    impl_output_pin!(P1_1, CTIMER0, MUX5);\n    impl_output_pin!(P1_2, CTIMER1, MUX4);\n    impl_output_pin!(P1_3, CTIMER1, MUX4);\n    impl_output_pin!(P1_4, CTIMER1, MUX4);\n    impl_output_pin!(P1_5, CTIMER1, MUX4);\n    impl_output_pin!(P1_6, CTIMER4, MUX5);\n    impl_output_pin!(P1_7, CTIMER4, MUX5);\n    impl_output_pin!(P1_8, CTIMER0, MUX5);\n    impl_output_pin!(P1_9, CTIMER0, MUX5);\n    impl_output_pin!(P1_10, CTIMER2, MUX4);\n    impl_output_pin!(P1_11, CTIMER2, MUX4);\n    impl_output_pin!(P1_12, CTIMER2, MUX4);\n    impl_output_pin!(P1_13, CTIMER2, MUX4);\n    impl_output_pin!(P1_14, CTIMER3, MUX5);\n    impl_output_pin!(P1_15, CTIMER3, MUX5);\n    impl_output_pin!(P1_18, CTIMER3, MUX4);\n    impl_output_pin!(P1_19, CTIMER3, MUX4);\n\n    impl_output_pin!(P2_0, CTIMER2, MUX5);\n    impl_output_pin!(P2_1, CTIMER2, MUX5);\n    impl_output_pin!(P2_2, CTIMER2, MUX5);\n    impl_output_pin!(P2_3, CTIMER2, MUX5);\n    impl_output_pin!(P2_4, CTIMER1, MUX5);\n    impl_output_pin!(P2_5, CTIMER1, MUX5);\n    impl_output_pin!(P2_6, CTIMER1, MUX5);\n    impl_output_pin!(P2_7, CTIMER1, MUX5);\n    impl_output_pin!(P2_8, CTIMER3, MUX4);\n    impl_output_pin!(P2_9, CTIMER3, MUX4);\n    impl_output_pin!(P2_10, CTIMER3, MUX4);\n    impl_output_pin!(P2_11, CTIMER3, MUX4);\n    impl_output_pin!(P2_12, CTIMER0, MUX5);\n    impl_output_pin!(P2_12, CTIMER4, MUX4);\n    impl_output_pin!(P2_13, CTIMER0, MUX5);\n    impl_output_pin!(P2_13, CTIMER4, MUX4);\n    impl_output_pin!(P2_14, CTIMER4, MUX4);\n    impl_output_pin!(P2_15, CTIMER0, MUX5);\n    impl_output_pin!(P2_15, CTIMER4, MUX4);\n    impl_output_pin!(P2_16, CTIMER0, MUX5);\n    impl_output_pin!(P2_16, CTIMER3, MUX4);\n    impl_output_pin!(P2_17, CTIMER0, MUX5);\n    impl_output_pin!(P2_17, CTIMER3, MUX4);\n    impl_output_pin!(P2_18, CTIMER3, MUX4);\n    impl_output_pin!(P2_19, CTIMER3, MUX4);\n    impl_output_pin!(P2_20, CTIMER2, MUX4);\n    impl_output_pin!(P2_21, CTIMER2, MUX4);\n    impl_output_pin!(P2_22, CTIMER2, MUX4);\n    impl_output_pin!(P2_23, CTIMER2, MUX4);\n\n    impl_output_pin!(P3_2, CTIMER4, MUX4);\n    impl_output_pin!(P3_3, CTIMER4, MUX4);\n    impl_output_pin!(P3_6, CTIMER4, MUX4);\n    impl_output_pin!(P3_7, CTIMER4, MUX4);\n    impl_output_pin!(P3_10, CTIMER1, MUX4);\n    impl_output_pin!(P3_11, CTIMER1, MUX4);\n    impl_output_pin!(P3_12, CTIMER1, MUX4);\n    impl_output_pin!(P3_13, CTIMER1, MUX4);\n    impl_output_pin!(P3_18, CTIMER2, MUX4);\n    impl_output_pin!(P3_19, CTIMER2, MUX4);\n    impl_output_pin!(P3_20, CTIMER2, MUX4);\n    impl_output_pin!(P3_21, CTIMER2, MUX4);\n    impl_output_pin!(P3_27, CTIMER3, MUX5);\n    impl_output_pin!(P3_28, CTIMER3, MUX5);\n    #[cfg(feature = \"dangerous-reset-as-gpio\")]\n    impl_output_pin!(P3_29, CTIMER3, MUX5);\n    #[cfg(feature = \"sosc-as-gpio\")]\n    impl_output_pin!(P3_30, CTIMER0, MUX4);\n    #[cfg(feature = \"sosc-as-gpio\")]\n    impl_output_pin!(P3_31, CTIMER0, MUX4);\n\n    impl_output_pin!(P4_2, CTIMER4, MUX4);\n    impl_output_pin!(P4_3, CTIMER4, MUX4);\n    impl_output_pin!(P4_4, CTIMER4, MUX4);\n    impl_output_pin!(P4_5, CTIMER4, MUX4);\n}\n"
  },
  {
    "path": "embassy-mcxa/src/ctimer/pwm.rs",
    "content": "//! CTimer-based PWM driver\n\nuse embassy_hal_internal::Peri;\npub use embedded_hal_1::pwm::SetDutyCycle;\nuse embedded_hal_1::pwm::{Error, ErrorKind, ErrorType};\n\nuse super::{AnyChannel, CTimer, CTimerChannel, Channel, Info, Instance, OutputPin};\nuse crate::gpio::{AnyPin, SealedPin};\nuse crate::pac::ctimer::vals::{Mri, Mrr, Mrrl, Mrs, Pwmen};\n\n/// PWM error.\n#[derive(Debug)]\npub enum PwmError {\n    /// Invalid Duty Cycle.\n    InvalidDutyCycle,\n    /// Invalid Channel Number.\n    InvalidChannel,\n    /// Channel mismatch\n    ChannelMismatch,\n}\n\nimpl Error for PwmError {\n    fn kind(&self) -> ErrorKind {\n        match self {\n            PwmError::InvalidDutyCycle => ErrorKind::Other,\n            PwmError::InvalidChannel => ErrorKind::Other,\n            PwmError::ChannelMismatch => ErrorKind::Other,\n        }\n    }\n}\n\n/// Pwm Configuration\n#[derive(Debug, Copy, Clone)]\n#[non_exhaustive]\npub struct Config {\n    /// The point at which the counter wraps around.\n    ///\n    /// This value represents the maximum possible period.\n    pub freq: u16,\n\n    /// Duty cycle.\n    pub duty_cycle: u16,\n}\n\nimpl Default for Config {\n    fn default() -> Self {\n        Self {\n            freq: 20_000,\n            duty_cycle: 0,\n        }\n    }\n}\n\n/// Representation of a single PWM channel.\n///\n/// This PWM representation can change duty cycle, but not frequency\n/// of the PWM.\npub struct Pwm<'d> {\n    info: &'static Info,\n    duty_ch: Peri<'d, AnyChannel>,\n    pin: Peri<'d, AnyPin>,\n    source_freq: u32,\n    pwm_freq: u16,\n    max_period: u16,\n}\n\nimpl<'d> Pwm<'d> {\n    fn enable(&mut self) {\n        self.info.regs().tcr().modify(|w| w.set_cen(true));\n    }\n\n    fn disable(&mut self) {\n        self.info.regs().tcr().modify(|w| w.set_cen(false));\n    }\n\n    fn set_pwm_mode(&self) {\n        self.info.regs().pwmc().modify(|w| match self.duty_ch.number() {\n            Channel::Zero => {\n                w.set_pwmen0(Pwmen::PWM);\n            }\n            Channel::One => {\n                w.set_pwmen1(Pwmen::PWM);\n            }\n            Channel::Two => {\n                w.set_pwmen2(Pwmen::PWM);\n            }\n            Channel::Three => {\n                w.set_pwmen3(Pwmen::PWM);\n            }\n        });\n    }\n\n    fn clear_status(&self) {\n        self.info.regs().mcr().modify(|w| {\n            // Clear stop, reset, and interrupt bits for the PWM channel\n            match self.duty_ch.number() {\n                Channel::Zero => {\n                    w.set_mr0i(Mri::MRI0);\n                    w.set_mr0r(Mrr::MRR0);\n                    w.set_mr0s(Mrs::MRS0);\n                }\n                Channel::One => {\n                    w.set_mr1i(Mri::MRI0);\n                    w.set_mr1r(Mrr::MRR0);\n                    w.set_mr1s(Mrs::MRS0);\n                }\n                Channel::Two => {\n                    w.set_mr2i(Mri::MRI0);\n                    w.set_mr2r(Mrr::MRR0);\n                    w.set_mr2s(Mrs::MRS0);\n                }\n                Channel::Three => {\n                    w.set_mr3i(Mri::MRI0);\n                    w.set_mr3r(Mrr::MRR0);\n                    w.set_mr3s(Mrs::MRS0);\n                }\n            }\n\n            match self.duty_ch.number() {\n                Channel::Zero => {\n                    w.set_mr0rl(Mrrl::MRRL1);\n                }\n                Channel::One => {\n                    w.set_mr1rl(Mrrl::MRRL1);\n                }\n                Channel::Two => {\n                    w.set_mr2rl(Mrrl::MRRL1);\n                }\n                Channel::Three => {\n                    w.set_mr3rl(Mrrl::MRRL1);\n                }\n            }\n        });\n    }\n\n    fn configure_duty_cycle(&self, duty_cycle: u32) {\n        self.info\n            .regs()\n            .mr(self.duty_ch.number().into())\n            .write(|w| w.set_match_(duty_cycle));\n        self.info\n            .regs()\n            .msr(self.duty_ch.number().into())\n            .write(|w| w.set_match_shadow(duty_cycle));\n    }\n}\n\n/// Single channel PWM driver\n///\n/// A single channel is used for Duty Cycle and a single channel is\n/// used for PWM period match.\npub struct SinglePwm<'d> {\n    pwm: Pwm<'d>,\n    period_ch: Peri<'d, AnyChannel>,\n}\n\nimpl<'d> SinglePwm<'d> {\n    /// Create Pwm driver with a single pin as output.\n    ///\n    /// Upon `Drop`, the external `pin` will be placed into `Disabled`\n    /// state.\n    pub fn new<T: Instance, DUTY: CTimerChannel<T>, PIN: OutputPin<T>>(\n        ctimer: CTimer<'d>,\n        duty_ch: Peri<'d, DUTY>,\n        period_ch: Peri<'d, impl CTimerChannel<T>>,\n        pin: Peri<'d, PIN>,\n        config: Config,\n    ) -> Result<Self, PwmError>\n    where\n        (T, DUTY, PIN): ValidMatchConfig,\n    {\n        pin.mux();\n\n        let mut inst = Self {\n            pwm: Pwm {\n                info: T::info(),\n                duty_ch: duty_ch.into(),\n                source_freq: ctimer._freq,\n                pwm_freq: config.freq,\n                pin: pin.into(),\n                max_period: 0,\n            },\n            period_ch: period_ch.into(),\n        };\n\n        inst.set_configuration(&config)?;\n\n        Ok(inst)\n    }\n\n    /// Degrade `self` into the underlying PWM representation.\n    ///\n    /// Upon calling this method, changing frequency will be disallowed.\n    pub fn degrade(self) -> Pwm<'d> {\n        self.pwm\n    }\n\n    fn set_configuration(&mut self, config: &Config) -> Result<(), PwmError> {\n        self.pwm.disable();\n        self.pwm.set_pwm_mode();\n        self.pwm.clear_status();\n\n        self.pwm.info.regs().mcr().modify(|w| match self.period_ch.number() {\n            Channel::Zero => {\n                w.set_mr0r(Mrr::MRR1);\n            }\n            Channel::One => {\n                w.set_mr1r(Mrr::MRR1);\n            }\n            Channel::Two => {\n                w.set_mr2r(Mrr::MRR1);\n            }\n            Channel::Three => {\n                w.set_mr3r(Mrr::MRR1);\n            }\n        });\n\n        // Configure PWM period\n        let period = self.pwm.source_freq / u32::from(self.pwm.pwm_freq) - 1;\n        self.pwm.max_period = period as u16;\n        self.pwm\n            .info\n            .regs()\n            .mr(self.period_ch.number().into())\n            .write(|w| w.set_match_(period));\n\n        // Configure PWM duty cycle\n        let duty_cycle = ((period + 1) * (100 - u32::from(config.duty_cycle))) / 100;\n        self.pwm.configure_duty_cycle(duty_cycle);\n\n        // Start CTimer\n        self.pwm.enable();\n\n        Ok(())\n    }\n}\n\n/// Dual channel PWM driver.\n///\n/// A single period match channel is shared for two independent PWM\n/// outputs. That is, both PWM output channels run on the same\n/// frequency, with optionally different duty cycles.\npub struct DualPwm<'d> {\n    pub pwm0: Pwm<'d>,\n    pub pwm1: Pwm<'d>,\n    period_ch: Peri<'d, AnyChannel>,\n}\n\nimpl<'d> DualPwm<'d> {\n    /// Create Pwm driver with a two pins for two PWM outputs.\n    ///\n    /// Upon `Drop`, all external pins will be placed into `Disabled`\n    /// state.\n    pub fn new<T: Instance, DUTY0: CTimerChannel<T>, DUTY1: CTimerChannel<T>, PIN0: OutputPin<T>, PIN1: OutputPin<T>>(\n        ctimer: CTimer<'d>,\n        duty_ch0: Peri<'d, DUTY0>,\n        duty_ch1: Peri<'d, DUTY1>,\n        period_ch: Peri<'d, impl CTimerChannel<T>>,\n        pin0: Peri<'d, PIN0>,\n        pin1: Peri<'d, PIN1>,\n        config: Config,\n    ) -> Result<Self, PwmError>\n    where\n        (T, DUTY0, PIN0): ValidMatchConfig,\n        (T, DUTY1, PIN1): ValidMatchConfig,\n    {\n        pin0.mux();\n        pin1.mux();\n\n        let mut inst = Self {\n            pwm0: Pwm {\n                info: T::info(),\n                duty_ch: duty_ch0.into(),\n                source_freq: ctimer._freq,\n                pwm_freq: config.freq,\n                pin: pin0.into(),\n                max_period: 0,\n            },\n            pwm1: Pwm {\n                info: T::info(),\n                duty_ch: duty_ch1.into(),\n                source_freq: ctimer._freq,\n                pwm_freq: config.freq,\n                pin: pin1.into(),\n                max_period: 0,\n            },\n            period_ch: period_ch.into(),\n        };\n\n        inst.set_configuration(&config)?;\n\n        Ok(inst)\n    }\n\n    /// Split `self` into its underlying channels.\n    ///\n    /// Upon calling this method, changing PWM frequency will be\n    /// disallowed. Only duty cycles can be changed.\n    pub fn split(self) -> (Pwm<'d>, Pwm<'d>) {\n        (self.pwm0, self.pwm1)\n    }\n\n    fn set_configuration(&mut self, config: &Config) -> Result<(), PwmError> {\n        self.pwm0.disable();\n\n        self.pwm0.set_pwm_mode();\n        self.pwm1.set_pwm_mode();\n\n        self.pwm0.clear_status();\n        self.pwm1.clear_status();\n\n        self.pwm0.info.regs().mcr().modify(|w| match self.period_ch.number() {\n            Channel::Zero => {\n                w.set_mr0r(Mrr::MRR1);\n            }\n            Channel::One => {\n                w.set_mr1r(Mrr::MRR1);\n            }\n            Channel::Two => {\n                w.set_mr2r(Mrr::MRR1);\n            }\n            Channel::Three => {\n                w.set_mr3r(Mrr::MRR1);\n            }\n        });\n\n        // Configure PWM period\n        let period = self.pwm0.source_freq / u32::from(self.pwm0.pwm_freq) - 1;\n\n        self.pwm0.max_period = period as u16;\n        self.pwm1.max_period = period as u16;\n\n        self.pwm0\n            .info\n            .regs()\n            .mr(self.period_ch.number().into())\n            .write(|w| w.set_match_(period));\n\n        // Configure PWM duty cycle\n        let duty_cycle = ((period + 1) * (100 - u32::from(config.duty_cycle))) / 100;\n        self.pwm0.configure_duty_cycle(duty_cycle);\n        self.pwm1.configure_duty_cycle(duty_cycle);\n\n        // Start CTimer\n        self.pwm0.enable();\n\n        Ok(())\n    }\n}\n\n/// Triple channel PWM driver.\n///\n/// A single period match channel is shared for three independent PWM\n/// outputs. That is, all three PWM output channels run on the same\n/// frequency, with optionally different duty cycles.\npub struct TriplePwm<'d> {\n    pub pwm0: Pwm<'d>,\n    pub pwm1: Pwm<'d>,\n    pub pwm2: Pwm<'d>,\n    period_ch: Peri<'d, AnyChannel>,\n}\n\nimpl<'d> TriplePwm<'d> {\n    /// Create Pwm driver using three pins for three PWM outputs.\n    ///\n    /// Upon `Drop`, all external pins will be placed into `Disabled`\n    /// state.\n    pub fn new<\n        T: Instance,\n        DUTY0: CTimerChannel<T>,\n        DUTY1: CTimerChannel<T>,\n        DUTY2: CTimerChannel<T>,\n        PIN0: OutputPin<T>,\n        PIN1: OutputPin<T>,\n        PIN2: OutputPin<T>,\n    >(\n        ctimer: CTimer<'d>,\n        duty_ch0: Peri<'d, DUTY0>,\n        duty_ch1: Peri<'d, DUTY1>,\n        duty_ch2: Peri<'d, DUTY2>,\n        period_ch: Peri<'d, impl CTimerChannel<T>>,\n        pin0: Peri<'d, PIN0>,\n        pin1: Peri<'d, PIN1>,\n        pin2: Peri<'d, PIN2>,\n        config: Config,\n    ) -> Result<Self, PwmError>\n    where\n        (T, DUTY0, PIN0): ValidMatchConfig,\n        (T, DUTY1, PIN1): ValidMatchConfig,\n        (T, DUTY2, PIN2): ValidMatchConfig,\n    {\n        pin0.mux();\n        pin1.mux();\n        pin2.mux();\n\n        let mut inst = Self {\n            pwm0: Pwm {\n                info: T::info(),\n                duty_ch: duty_ch0.into(),\n                source_freq: ctimer._freq,\n                pwm_freq: config.freq,\n                pin: pin0.into(),\n                max_period: 0,\n            },\n            pwm1: Pwm {\n                info: T::info(),\n                duty_ch: duty_ch1.into(),\n                source_freq: ctimer._freq,\n                pwm_freq: config.freq,\n                pin: pin1.into(),\n                max_period: 0,\n            },\n            pwm2: Pwm {\n                info: T::info(),\n                duty_ch: duty_ch2.into(),\n                source_freq: ctimer._freq,\n                pwm_freq: config.freq,\n                pin: pin2.into(),\n                max_period: 0,\n            },\n            period_ch: period_ch.into(),\n        };\n\n        inst.set_configuration(&config)?;\n\n        Ok(inst)\n    }\n\n    /// Split `self` into its underlying channels.\n    ///\n    /// Upon calling this method, changing PWM frequency will be\n    /// disallowed. Only duty cycles can be changed.\n    pub fn split(self) -> (Pwm<'d>, Pwm<'d>, Pwm<'d>) {\n        (self.pwm0, self.pwm1, self.pwm2)\n    }\n\n    fn set_configuration(&mut self, config: &Config) -> Result<(), PwmError> {\n        self.pwm0.disable();\n\n        self.pwm0.set_pwm_mode();\n        self.pwm1.set_pwm_mode();\n        self.pwm2.set_pwm_mode();\n\n        self.pwm0.clear_status();\n        self.pwm1.clear_status();\n        self.pwm2.clear_status();\n\n        self.pwm0.info.regs().mcr().modify(|w| match self.period_ch.number() {\n            Channel::Zero => {\n                w.set_mr0r(Mrr::MRR1);\n            }\n            Channel::One => {\n                w.set_mr1r(Mrr::MRR1);\n            }\n            Channel::Two => {\n                w.set_mr2r(Mrr::MRR1);\n            }\n            Channel::Three => {\n                w.set_mr3r(Mrr::MRR1);\n            }\n        });\n\n        // Configure PWM period\n        let period = self.pwm0.source_freq / u32::from(self.pwm0.pwm_freq) - 1;\n\n        self.pwm0.max_period = period as u16;\n        self.pwm1.max_period = period as u16;\n        self.pwm2.max_period = period as u16;\n\n        self.pwm0\n            .info\n            .regs()\n            .mr(self.period_ch.number().into())\n            .write(|w| w.set_match_(period));\n\n        // Configure PWM duty cycle\n        let duty_cycle = ((period + 1) * (100 - u32::from(config.duty_cycle))) / 100;\n        self.pwm0.configure_duty_cycle(duty_cycle);\n        self.pwm1.configure_duty_cycle(duty_cycle);\n        self.pwm2.configure_duty_cycle(duty_cycle);\n\n        // Start CTimer\n        self.pwm0.enable();\n\n        Ok(())\n    }\n}\n\nimpl<'d> Drop for Pwm<'d> {\n    fn drop(&mut self) {\n        self.disable();\n        self.pin.set_as_disabled();\n    }\n}\n\nimpl<'d> ErrorType for SinglePwm<'d> {\n    type Error = PwmError;\n}\n\nimpl<'d> SetDutyCycle for SinglePwm<'d> {\n    fn max_duty_cycle(&self) -> u16 {\n        self.pwm.max_period\n    }\n\n    fn set_duty_cycle(&mut self, duty: u16) -> Result<(), Self::Error> {\n        self.pwm.set_duty_cycle(duty)\n    }\n}\n\nimpl<'d> ErrorType for Pwm<'d> {\n    type Error = PwmError;\n}\n\nimpl<'d> SetDutyCycle for Pwm<'d> {\n    fn max_duty_cycle(&self) -> u16 {\n        self.max_period\n    }\n\n    fn set_duty_cycle(&mut self, duty: u16) -> Result<(), Self::Error> {\n        let max_duty = self.max_duty_cycle();\n\n        if duty > max_duty {\n            return Err(PwmError::InvalidDutyCycle);\n        }\n\n        self.info\n            .regs()\n            .msr(self.duty_ch.number().into())\n            .write(|w| w.set_match_shadow(u32::from(duty)));\n\n        Ok(())\n    }\n}\n\ntrait SealedValidMatchConfig {}\n\n/// Valid match channel + pin configuration marker trait\n#[allow(private_bounds)]\npub trait ValidMatchConfig: SealedValidMatchConfig {}\n\nmacro_rules! impl_valid_match {\n    ($peri:ident, $ch:ident, $pin:ident, $n:literal) => {\n        impl SealedValidMatchConfig\n            for (\n                crate::peripherals::$peri,\n                crate::peripherals::$ch,\n                crate::peripherals::$pin,\n            )\n        {\n        }\n\n        impl ValidMatchConfig\n            for (\n                crate::peripherals::$peri,\n                crate::peripherals::$ch,\n                crate::peripherals::$pin,\n            )\n        {\n        }\n    };\n}\n\n#[cfg(feature = \"mcxa2xx\")]\nmod mcxa2xx {\n    use super::*;\n\n    // CTIMER0 match channels\n    #[cfg(feature = \"swd-swo-as-gpio\")]\n    impl_valid_match!(CTIMER0, CTIMER0_CH0, P0_2, 0);\n    #[cfg(feature = \"jtag-extras-as-gpio\")]\n    impl_valid_match!(CTIMER0, CTIMER0_CH1, P0_3, 1);\n\n    impl_valid_match!(CTIMER0, CTIMER0_CH0, P0_16, 0);\n    impl_valid_match!(CTIMER0, CTIMER0_CH1, P0_17, 1);\n    impl_valid_match!(CTIMER0, CTIMER0_CH2, P0_18, 2);\n    impl_valid_match!(CTIMER0, CTIMER0_CH3, P0_19, 3);\n\n    impl_valid_match!(CTIMER0, CTIMER0_CH0, P0_22, 0);\n    impl_valid_match!(CTIMER0, CTIMER0_CH1, P0_23, 1);\n    impl_valid_match!(CTIMER0, CTIMER0_CH2, P1_0, 2);\n    impl_valid_match!(CTIMER0, CTIMER0_CH3, P1_1, 3);\n\n    #[cfg(feature = \"sosc-as-gpio\")]\n    impl_valid_match!(CTIMER0, CTIMER0_CH2, P3_30, 2);\n    #[cfg(feature = \"sosc-as-gpio\")]\n    impl_valid_match!(CTIMER0, CTIMER0_CH3, P3_31, 3);\n\n    // CTIMER1 match channels\n    impl_valid_match!(CTIMER1, CTIMER1_CH0, P1_2, 0);\n    impl_valid_match!(CTIMER1, CTIMER1_CH1, P1_3, 1);\n    impl_valid_match!(CTIMER1, CTIMER1_CH2, P1_4, 2);\n    impl_valid_match!(CTIMER1, CTIMER1_CH3, P1_5, 3);\n\n    impl_valid_match!(CTIMER1, CTIMER1_CH0, P2_4, 0);\n    impl_valid_match!(CTIMER1, CTIMER1_CH1, P2_5, 1);\n    impl_valid_match!(CTIMER1, CTIMER1_CH2, P2_6, 2);\n    impl_valid_match!(CTIMER1, CTIMER1_CH3, P2_7, 3);\n\n    impl_valid_match!(CTIMER1, CTIMER1_CH0, P3_10, 0);\n    impl_valid_match!(CTIMER1, CTIMER1_CH1, P3_11, 1);\n    impl_valid_match!(CTIMER1, CTIMER1_CH2, P3_12, 2);\n    impl_valid_match!(CTIMER1, CTIMER1_CH3, P3_13, 3);\n\n    // CTIMER2 match channels\n    impl_valid_match!(CTIMER2, CTIMER2_CH0, P1_10, 0);\n    impl_valid_match!(CTIMER2, CTIMER2_CH1, P1_11, 1);\n    impl_valid_match!(CTIMER2, CTIMER2_CH2, P1_12, 2);\n    impl_valid_match!(CTIMER2, CTIMER2_CH3, P1_13, 3);\n\n    impl_valid_match!(CTIMER2, CTIMER2_CH0, P2_0, 0);\n    impl_valid_match!(CTIMER2, CTIMER2_CH1, P2_1, 1);\n    impl_valid_match!(CTIMER2, CTIMER2_CH2, P2_2, 2);\n    impl_valid_match!(CTIMER2, CTIMER2_CH3, P2_3, 3);\n\n    impl_valid_match!(CTIMER2, CTIMER2_CH0, P2_20, 0);\n    impl_valid_match!(CTIMER2, CTIMER2_CH1, P2_21, 1);\n    impl_valid_match!(CTIMER2, CTIMER2_CH3, P2_23, 3);\n\n    impl_valid_match!(CTIMER2, CTIMER2_CH0, P3_18, 0);\n    impl_valid_match!(CTIMER2, CTIMER2_CH1, P3_19, 1);\n    impl_valid_match!(CTIMER2, CTIMER2_CH2, P3_20, 2);\n    impl_valid_match!(CTIMER2, CTIMER2_CH3, P3_21, 3);\n\n    // CTIMER3 match channels\n    impl_valid_match!(CTIMER3, CTIMER3_CH0, P1_14, 0);\n    impl_valid_match!(CTIMER3, CTIMER3_CH1, P1_15, 1);\n    impl_valid_match!(CTIMER3, CTIMER3_CH2, P2_10, 2);\n    impl_valid_match!(CTIMER3, CTIMER3_CH3, P2_11, 3);\n\n    impl_valid_match!(CTIMER3, CTIMER3_CH0, P2_16, 0);\n    impl_valid_match!(CTIMER3, CTIMER3_CH1, P2_17, 1);\n    impl_valid_match!(CTIMER3, CTIMER3_CH2, P2_19, 3);\n\n    impl_valid_match!(CTIMER3, CTIMER3_CH0, P3_27, 1);\n    impl_valid_match!(CTIMER3, CTIMER3_CH2, P3_28, 2);\n    #[cfg(feature = \"dangerous-reset-as-gpio\")]\n    impl_valid_match!(CTIMER3, CTIMER3_CH3, P3_29, 3);\n\n    // CTIMER4 match channels\n    impl_valid_match!(CTIMER4, CTIMER4_CH0, P1_6, 0);\n    impl_valid_match!(CTIMER4, CTIMER4_CH1, P1_7, 1);\n\n    impl_valid_match!(CTIMER4, CTIMER4_CH0, P2_12, 0);\n    impl_valid_match!(CTIMER4, CTIMER4_CH1, P2_13, 1);\n    impl_valid_match!(CTIMER4, CTIMER4_CH3, P2_15, 3);\n\n    impl_valid_match!(CTIMER4, CTIMER4_CH0, P3_2, 0);\n    impl_valid_match!(CTIMER4, CTIMER4_CH2, P3_6, 2);\n    impl_valid_match!(CTIMER4, CTIMER4_CH3, P3_7, 3);\n\n    impl_valid_match!(CTIMER4, CTIMER4_CH0, P4_2, 0);\n    impl_valid_match!(CTIMER4, CTIMER4_CH1, P4_3, 1);\n    impl_valid_match!(CTIMER4, CTIMER4_CH2, P4_4, 2);\n    impl_valid_match!(CTIMER4, CTIMER4_CH3, P4_5, 3);\n}\n\n#[cfg(feature = \"mcxa5xx\")]\nmod mcxa5xx {\n    use super::*;\n\n    // CTIMER0 match channels\n    impl_valid_match!(CTIMER0, CTIMER0_CH0, P0_16, 0);\n    #[cfg(feature = \"swd-swo-as-gpio\")]\n    impl_valid_match!(CTIMER0, CTIMER0_CH0, P0_2, 0);\n    impl_valid_match!(CTIMER0, CTIMER0_CH0, P0_22, 0);\n    impl_valid_match!(CTIMER0, CTIMER0_CH0, P0_24, 0);\n    impl_valid_match!(CTIMER0, CTIMER0_CH0, P2_12, 0);\n    impl_valid_match!(CTIMER0, CTIMER0_CH1, P0_17, 1);\n    impl_valid_match!(CTIMER0, CTIMER0_CH1, P0_23, 1);\n    impl_valid_match!(CTIMER0, CTIMER0_CH1, P0_25, 1);\n    #[cfg(feature = \"jtag-extras-as-gpio\")]\n    impl_valid_match!(CTIMER0, CTIMER0_CH1, P0_3, 1);\n    impl_valid_match!(CTIMER0, CTIMER0_CH1, P2_13, 1);\n    impl_valid_match!(CTIMER0, CTIMER0_CH2, P0_12, 2);\n    impl_valid_match!(CTIMER0, CTIMER0_CH2, P0_18, 2);\n    impl_valid_match!(CTIMER0, CTIMER0_CH2, P0_26, 2);\n    impl_valid_match!(CTIMER0, CTIMER0_CH2, P0_4, 2);\n    impl_valid_match!(CTIMER0, CTIMER0_CH2, P1_0, 2);\n    impl_valid_match!(CTIMER0, CTIMER0_CH2, P1_8, 2);\n    impl_valid_match!(CTIMER0, CTIMER0_CH2, P2_15, 2);\n    impl_valid_match!(CTIMER0, CTIMER0_CH2, P2_16, 2);\n    #[cfg(feature = \"sosc-as-gpio\")]\n    impl_valid_match!(CTIMER0, CTIMER0_CH2, P3_30, 2);\n    impl_valid_match!(CTIMER0, CTIMER0_CH3, P0_13, 3);\n    impl_valid_match!(CTIMER0, CTIMER0_CH3, P0_19, 3);\n    impl_valid_match!(CTIMER0, CTIMER0_CH3, P0_27, 3);\n    impl_valid_match!(CTIMER0, CTIMER0_CH3, P0_5, 3);\n    impl_valid_match!(CTIMER0, CTIMER0_CH3, P1_1, 3);\n    impl_valid_match!(CTIMER0, CTIMER0_CH3, P1_9, 3);\n    impl_valid_match!(CTIMER0, CTIMER0_CH3, P2_17, 3);\n    #[cfg(feature = \"sosc-as-gpio\")]\n    impl_valid_match!(CTIMER0, CTIMER0_CH3, P3_31, 3);\n\n    // CTIMER1 match channels\n    impl_valid_match!(CTIMER1, CTIMER1_CH0, P1_2, 0);\n    impl_valid_match!(CTIMER1, CTIMER1_CH1, P1_3, 1);\n    impl_valid_match!(CTIMER1, CTIMER1_CH2, P1_4, 2);\n    impl_valid_match!(CTIMER1, CTIMER1_CH3, P1_5, 3);\n    impl_valid_match!(CTIMER1, CTIMER1_CH0, P2_4, 0);\n    impl_valid_match!(CTIMER1, CTIMER1_CH1, P2_5, 1);\n    impl_valid_match!(CTIMER1, CTIMER1_CH2, P2_6, 2);\n    impl_valid_match!(CTIMER1, CTIMER1_CH0, P3_10, 0);\n    impl_valid_match!(CTIMER1, CTIMER1_CH1, P3_11, 1);\n    impl_valid_match!(CTIMER1, CTIMER1_CH2, P3_12, 2);\n    impl_valid_match!(CTIMER1, CTIMER1_CH3, P2_7, 3);\n    impl_valid_match!(CTIMER1, CTIMER1_CH3, P3_13, 3);\n\n    // CTIMER2 match channels\n    impl_valid_match!(CTIMER2, CTIMER2_CH0, P1_10, 0);\n    impl_valid_match!(CTIMER2, CTIMER2_CH0, P2_0, 0);\n    impl_valid_match!(CTIMER2, CTIMER2_CH0, P2_20, 0);\n    impl_valid_match!(CTIMER2, CTIMER2_CH0, P3_18, 0);\n    impl_valid_match!(CTIMER2, CTIMER2_CH1, P1_11, 1);\n    impl_valid_match!(CTIMER2, CTIMER2_CH1, P2_1, 1);\n    impl_valid_match!(CTIMER2, CTIMER2_CH1, P2_21, 1);\n    impl_valid_match!(CTIMER2, CTIMER2_CH1, P3_19, 1);\n    impl_valid_match!(CTIMER2, CTIMER2_CH2, P1_12, 2);\n    impl_valid_match!(CTIMER2, CTIMER2_CH2, P2_2, 2);\n    impl_valid_match!(CTIMER2, CTIMER2_CH2, P2_22, 2);\n    impl_valid_match!(CTIMER2, CTIMER2_CH2, P3_20, 2);\n    impl_valid_match!(CTIMER2, CTIMER2_CH3, P1_13, 3);\n    impl_valid_match!(CTIMER2, CTIMER2_CH3, P2_23, 3);\n    impl_valid_match!(CTIMER2, CTIMER2_CH3, P2_3, 3);\n    impl_valid_match!(CTIMER2, CTIMER2_CH3, P3_21, 3);\n\n    // CTIMER3 match channels\n    impl_valid_match!(CTIMER3, CTIMER3_CH0, P1_14, 0);\n    impl_valid_match!(CTIMER3, CTIMER3_CH0, P1_18, 0);\n    impl_valid_match!(CTIMER3, CTIMER3_CH0, P2_16, 0);\n    impl_valid_match!(CTIMER3, CTIMER3_CH0, P2_8, 0);\n    impl_valid_match!(CTIMER3, CTIMER3_CH1, P1_15, 1);\n    impl_valid_match!(CTIMER3, CTIMER3_CH1, P1_19, 1);\n    impl_valid_match!(CTIMER3, CTIMER3_CH1, P2_17, 1);\n    impl_valid_match!(CTIMER3, CTIMER3_CH1, P2_9, 1);\n    impl_valid_match!(CTIMER3, CTIMER3_CH1, P3_27, 1);\n    impl_valid_match!(CTIMER3, CTIMER3_CH2, P2_10, 2);\n    impl_valid_match!(CTIMER3, CTIMER3_CH2, P2_18, 2);\n    impl_valid_match!(CTIMER3, CTIMER3_CH2, P3_28, 2);\n    impl_valid_match!(CTIMER3, CTIMER3_CH3, P2_11, 3);\n    impl_valid_match!(CTIMER3, CTIMER3_CH3, P2_19, 3);\n    #[cfg(feature = \"dangerous-reset-as-gpio\")]\n    impl_valid_match!(CTIMER3, CTIMER3_CH3, P3_29, 3);\n\n    // CTIMER4 match channels\n    impl_valid_match!(CTIMER4, CTIMER4_CH0, P1_6, 0);\n    impl_valid_match!(CTIMER4, CTIMER4_CH0, P2_12, 0);\n    impl_valid_match!(CTIMER4, CTIMER4_CH0, P3_2, 0);\n    impl_valid_match!(CTIMER4, CTIMER4_CH0, P4_2, 0);\n    impl_valid_match!(CTIMER4, CTIMER4_CH1, P1_7, 1);\n    impl_valid_match!(CTIMER4, CTIMER4_CH1, P2_13, 1);\n    impl_valid_match!(CTIMER4, CTIMER4_CH1, P3_3, 1);\n    impl_valid_match!(CTIMER4, CTIMER4_CH1, P4_3, 1);\n    impl_valid_match!(CTIMER4, CTIMER4_CH2, P2_14, 2);\n    impl_valid_match!(CTIMER4, CTIMER4_CH2, P3_6, 2);\n    impl_valid_match!(CTIMER4, CTIMER4_CH2, P4_4, 2);\n    impl_valid_match!(CTIMER4, CTIMER4_CH3, P2_15, 3);\n    impl_valid_match!(CTIMER4, CTIMER4_CH3, P3_7, 3);\n    impl_valid_match!(CTIMER4, CTIMER4_CH3, P4_5, 3);\n}\n"
  },
  {
    "path": "embassy-mcxa/src/dma.rs",
    "content": "//! DMA driver.\n//!\n//! This module provides a typed channel abstraction over the EDMA_0_TCD0 array\n//! and helpers for configuring the channel MUX. The driver supports\n//! higher-level async transfer APIs.\n//!\n//! # Architecture\n//!\n//! The MCXA276 has 8 DMA channels (0-7), each with its own interrupt vector.\n//! Each channel has a Transfer Control Descriptor (TCD) that defines the\n//! transfer parameters.\n//!\n//! # Choosing the Right API\n//!\n//! This module provides several API levels to match different use cases:\n//!\n//! ## High-Level Async API (Recommended for Most Users)\n//!\n//! Use the async methods when you want simple, safe DMA transfers:\n//!\n//! | Method | Description |\n//! |--------|-------------|\n//! | [`DmaChannel::mem_to_mem()`] | Memory-to-memory copy |\n//! | [`DmaChannel::memset()`] | Fill memory with a pattern |\n//! | [`DmaChannel::write_to_peripheral()`] | Memory-to-peripheral (TX) |\n//! | [`DmaChannel::read_from_peripheral()`] | Peripheral-to-memory (RX) |\n//!\n//! These return a [`Transfer`] future that can be `.await`ed:\n//!\n//! ```rust,no_run\n//! #![no_std]\n//! #![no_main]\n//!\n//! # extern crate panic_halt;\n//! # extern crate embassy_mcxa;\n//! # extern crate embassy_executor;\n//! # use panic_halt as _;\n//! use embassy_executor::Spawner;\n//! use embassy_mcxa::clocks::config::Div8;\n//! use embassy_mcxa::config::Config;\n//! use embassy_mcxa::dma::{DmaChannel, TransferOptions};\n//!\n//! #[embassy_executor::main]\n//! async fn main(_spawner: Spawner) {\n//!     let mut config = Config::default();\n//!     config.clock_cfg.sirc.fro_12m_enabled = true;\n//!     config.clock_cfg.sirc.fro_lf_div = Div8::from_divisor(1);\n//!\n//!     let p = embassy_mcxa::init(config);\n//!\n//!     let src = [0xa5u8; 4];\n//!     let mut dst = [0u8; 4];\n//!\n//!     let mut ch0 = DmaChannel::new(p.DMA_CH0);\n//!     let options = TransferOptions::COMPLETE_INTERRUPT;\n//!     let transfer = ch0.mem_to_mem(&src, &mut dst, options).unwrap();\n//!     transfer.await;\n//! }\n//! ```\n//!\n//! ## Scatter-Gather Builder (For Chained Transfers)\n//!\n//! Use [`ScatterGatherBuilder`] for complex multi-segment transfers:\n//!\n//! ```rust,no_run\n//! #![no_std]\n//! #![no_main]\n//!\n//! # extern crate panic_halt;\n//! # extern crate embassy_mcxa;\n//! # extern crate embassy_executor;\n//! # use panic_halt as _;\n//! use embassy_executor::Spawner;\n//! use embassy_mcxa::clocks::config::Div8;\n//! use embassy_mcxa::config::Config;\n//! use embassy_mcxa::dma::{DmaChannel, ScatterGatherBuilder};\n//!\n//! #[embassy_executor::main]\n//! async fn main(_spawner: Spawner) {\n//!     let mut config = Config::default();\n//!     config.clock_cfg.sirc.fro_12m_enabled = true;\n//!     config.clock_cfg.sirc.fro_lf_div = Div8::from_divisor(1);\n//!\n//!     let p = embassy_mcxa::init(config);\n//!\n//!     let src1 = [0x55u8; 4];\n//!     let src2 = [0xaau8; 4];\n//!     let src3 = [0x5au8; 4];\n//!     let mut dst1 = [0u8; 4];\n//!     let mut dst2 = [0u8; 4];\n//!     let mut dst3 = [0u8; 4];\n//!\n//!     let mut ch0 = DmaChannel::new(p.DMA_CH0);\n//!     let mut builder = ScatterGatherBuilder::<u8>::new();\n//!\n//!     builder.add_transfer(&src1, &mut dst1);\n//!     builder.add_transfer(&src2, &mut dst2);\n//!     builder.add_transfer(&src3, &mut dst3);\n//!\n//!     let transfer = builder.build(ch0).unwrap();\n//!     transfer.blocking_wait();\n//! }\n//! ```\n\n#![allow(dead_code)]\n\nuse core::future::Future;\nuse core::marker::PhantomData;\nuse core::pin::Pin;\nuse core::ptr::NonNull;\nuse core::sync::atomic::{AtomicPtr, AtomicUsize, Ordering, fence};\nuse core::task::{Context, Poll};\n\nuse embassy_hal_internal::{Peri, PeripheralType};\nuse embassy_sync::waitqueue::AtomicWaker;\n\nuse crate::clocks::enable_and_reset;\nuse crate::clocks::periph_helpers::NoConfig;\nuse crate::dma::sealed::SealedChannel;\nuse crate::pac::dma::vals::Halt;\nuse crate::pac::edma_0_tcd::regs::{TcdAttr, TcdBiterElinkno, TcdCiterElinkno, TcdCsr};\nuse crate::pac::edma_0_tcd::vals::{\n    Bwc, Dpa, Dreq, Ecp, Esg, Size, Start, TcdNbytesMloffnoDmloe, TcdNbytesMloffnoSmloe,\n};\nuse crate::pac::{self, Interrupt};\nuse crate::peripherals::DMA0;\n\n/// Initialize DMA controller (clock enabled, reset released, controller configured).\n///\n/// This function is intended to be called ONCE during HAL initialization (`hal::init()`).\n///\n/// The function enables the DMA0 clock, releases reset, and configures the controller\n/// for normal operation with round-robin arbitration.\npub(crate) fn init() {\n    // Enable DMA0 clock and release reset\n    let _ = unsafe { enable_and_reset::<DMA0>(&NoConfig) };\n\n    // Configure DMA controller\n    pac::DMA0.mp_csr().modify(|w| {\n        w.set_edbg(true);\n        w.set_erca(true);\n        w.set_halt(Halt::NORMAL_OPERATION);\n        w.set_gclc(true);\n        w.set_gmrc(true);\n    });\n\n    // REVISIT: This needs to be improved.\n    //\n    // Enable all DMA request lines for non-secure access.\n    #[cfg(feature = \"mcxa5xx\")]\n    {\n        let ahbsc = crate::pac::AHBSC;\n        ahbsc.sec_gp_reg0().write(|w| w.0 = 0xffff_ffff);\n        ahbsc.sec_gp_reg1().write(|w| w.0 = 0xffff_ffff);\n        ahbsc.sec_gp_reg2().write(|w| w.0 = 0xffff_ffff);\n        ahbsc.sec_gp_reg3().write(|w| w.0 = 0xffff_ffff);\n        ahbsc.sec_gp_reg4().write(|w| w.0 = 0xffff_ffff);\n        ahbsc.sec_gp_reg5().write(|w| w.0 = 0xffff_ffff);\n        ahbsc.sec_gp_reg6().write(|w| w.0 = 0xffff_ffff);\n        ahbsc.sec_gp_reg7().write(|w| w.0 = 0xffff_ffff);\n        ahbsc.sec_gp_reg8().write(|w| w.0 = 0xffff_ffff);\n        ahbsc.sec_gp_reg9().write(|w| w.0 = 0xffff_ffff);\n    }\n}\n\n/// DMA transfer priority.\n#[derive(Debug, Copy, Clone, PartialEq, Eq, Default)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\n#[repr(u8)]\npub enum Priority {\n    /// Highest priority.\n    P0 = 0,\n    P1 = 1,\n    P2 = 2,\n    P3 = 3,\n    P4 = 4,\n    P5 = 5,\n    P6 = 6,\n    /// Lowest priority.\n    #[default]\n    P7 = 7,\n}\n\nimpl Priority {\n    pub const LOWEST: Priority = Priority::P7;\n    pub const HIGHEST: Priority = Priority::P0;\n}\n\n/// DMA transfer data width.\n#[derive(Debug, Copy, Clone, PartialEq, Eq, Default)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub enum WordSize {\n    /// 8-bit (1 byte) transfers.\n    OneByte,\n    /// 16-bit (2 byte) transfers.\n    TwoBytes,\n    /// 32-bit (4 byte) transfers.\n    #[default]\n    FourBytes,\n}\n\nimpl WordSize {\n    /// Largest [WordSize] supported by this HAL driver.\n    pub const LARGEST: WordSize = WordSize::FourBytes;\n\n    /// Size in bytes.\n    pub const fn bytes(self) -> usize {\n        match self {\n            WordSize::OneByte => 1,\n            WordSize::TwoBytes => 2,\n            WordSize::FourBytes => 4,\n        }\n    }\n\n    /// Convert to hardware SSIZE/DSIZE field value.\n    pub const fn to_hw_size(self) -> Size {\n        match self {\n            WordSize::OneByte => Size::EIGHT_BIT,\n            WordSize::TwoBytes => Size::SIXTEEN_BIT,\n            WordSize::FourBytes => Size::THIRTYTWO_BIT,\n        }\n    }\n\n    /// Create from byte width (1, 2, or 4).\n    pub const fn from_bytes(bytes: u8) -> Option<Self> {\n        match bytes {\n            1 => Some(WordSize::OneByte),\n            2 => Some(WordSize::TwoBytes),\n            4 => Some(WordSize::FourBytes),\n            _ => None,\n        }\n    }\n}\n\n/// Trait for word-sizes that are supported.\npub trait Word: Copy + 'static {\n    /// The word size for this type.\n    fn size() -> WordSize;\n}\n\nimpl Word for u8 {\n    fn size() -> WordSize {\n        WordSize::OneByte\n    }\n}\n\nimpl Word for u16 {\n    fn size() -> WordSize {\n        WordSize::TwoBytes\n    }\n}\n\nimpl Word for u32 {\n    fn size() -> WordSize {\n        WordSize::FourBytes\n    }\n}\n\n/// DMA transfer options.\n#[derive(Debug, Copy, Clone, PartialEq, Eq)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\n#[non_exhaustive]\npub struct TransferOptions {\n    /// Enable interrupt on half transfer complete.\n    pub half_transfer_interrupt: bool,\n    /// Enable interrupt on transfer complete.\n    pub complete_transfer_interrupt: bool,\n    /// Transfer priority\n    pub priority: Priority,\n}\n\nimpl TransferOptions {\n    /// Short-hand to specify that no options should be configured.\n    pub const NO_INTERRUPTS: Self = Self {\n        half_transfer_interrupt: false,\n        complete_transfer_interrupt: false,\n        priority: Priority::LOWEST,\n    };\n\n    /// Short-hand to specify that only the complete transfer interrupt should be triggered.\n    pub const COMPLETE_INTERRUPT: Self = Self {\n        half_transfer_interrupt: false,\n        complete_transfer_interrupt: true,\n        priority: Priority::LOWEST,\n    };\n}\n\n/// General DMA error types.\n#[derive(Debug, Copy, Clone, PartialEq, Eq)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub enum Error {\n    /// The DMA controller reported a bus error.\n    BusError,\n    /// The transfer was aborted.\n    Aborted,\n    /// Configuration error (e.g., invalid parameters).\n    Configuration,\n    /// Buffer overrun (for ring buffers).\n    Overrun,\n}\n\n/// An error that can occur if the parameters passed were invalid.\n#[derive(Debug, Copy, Clone, PartialEq, Eq)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub struct InvalidParameters;\n\n/// Maximum bytes per DMA transfer (eDMA4 CITER/BITER are 15-bit fields).\n///\n/// This is a hardware limitation of the eDMA4 controller. Transfers larger\n/// than this must be split into multiple DMA operations.\npub const DMA_MAX_TRANSFER_SIZE: usize = 0x7FFF;\n\n/// DMA request sources\n///\n/// (from MCXA266 reference manual PDF attachment \"DMA_Configuration.xml\")\n#[derive(Clone, Copy, Debug)]\n#[repr(u8)]\n#[allow(dead_code)]\n#[cfg(feature = \"mcxa2xx\")]\npub(crate) enum DmaRequest {\n    WUU0WakeUpEvent = 1,\n    CAN0 = 2,\n    LPI2C2Rx = 3,\n    LPI2C2Tx = 4,\n    LPI2C3Rx = 5,\n    LPI2C3Tx = 6,\n    I3C0Rx = 7,\n    I3C0Tx = 8,\n    LPI2C0Rx = 11,\n    LPI2C0Tx = 12,\n    LPI2C1Rx = 13,\n    LPI2C1Tx = 14,\n    LPSPI0Rx = 15,\n    LPSPI0Tx = 16,\n    LPSPI1Rx = 17,\n    LPSPI1Tx = 18,\n    LPUART0Rx = 21,\n    LPUART0Tx = 22,\n    LPUART1Rx = 23,\n    LPUART1Tx = 24,\n    LPUART2Rx = 25,\n    LPUART2Tx = 26,\n    LPUART3Rx = 27,\n    LPUART3Tx = 28,\n    LPUART4Rx = 29,\n    LPUART4Tx = 30,\n    Ctimer0M0 = 31,\n    Ctimer0M1 = 32,\n    Ctimer1M0 = 33,\n    Ctimer1M1 = 34,\n    Ctimer2M0 = 35,\n    Ctimer2M1 = 36,\n    Ctimer3M0 = 37,\n    Ctimer3M1 = 38,\n    Ctimer4M0 = 39,\n    Ctimer4M1 = 40,\n    FlexPWM0Capt0 = 41,\n    FlexPWM0Capt1 = 42,\n    FlexPWM0Capt2 = 43,\n    FlexPWM0Capt3 = 44,\n    FlexPWM0Val0 = 45,\n    FlexPWM0Val1 = 46,\n    FlexPWM0Val2 = 47,\n    FlexPWM0Val3 = 48,\n    LPTMR0CounterMatchEvent = 49,\n    ADC0FifoRequest = 51,\n    ADC1FifoRequest = 52,\n    CMP0 = 53,\n    CMP1 = 54,\n    CMP2 = 55,\n    DAC0FifoRequest = 56,\n    GPIO0PinEvent0 = 60,\n    GPIO1PinEvent0 = 61,\n    GPIO2PinEvent0 = 62,\n    GPIO3PinEvent0 = 63,\n    GPIO4PinEvent0 = 64,\n    QDC0 = 65,\n    QDC1 = 66,\n    FlexIO0SR0 = 71,\n    FlexIO0SR1 = 72,\n    FlexIO0SR2 = 73,\n    FlexIO0SR3 = 74,\n    FlexPWM1ReqCapt0 = 79,\n    FlexPWM1ReqCapt1 = 80,\n    FlexPWM1ReqCapt2 = 81,\n    FlexPWM1ReqCapt3 = 82,\n    FlexPWM1ReqVal0 = 83,\n    FlexPWM1ReqVal1 = 84,\n    FlexPWM1ReqVal2 = 85,\n    FlexPWM1ReqVal3 = 86,\n    CAN1 = 87,\n    LPUART5Rx = 102,\n    LPUART5Tx = 103,\n    MAU0MAU = 115,\n    SGI0ReqIdat = 119,\n    SGI0ReqOdat = 120,\n    ADC2FifoRequest = 123,\n    ADC3FifoRequest = 124,\n}\n\n/// DMA request sources\n///\n/// (from MCXA577 reference manual PDF attachment \"DMA_Configuration.xml\")\n#[derive(Clone, Copy, Debug)]\n#[repr(u8)]\n#[allow(dead_code)]\n#[cfg(feature = \"mcxa5xx\")]\npub(crate) enum DmaRequest {\n    WUU0WakeUpEvent = 1,\n    CAN0 = 2,\n    LPI2C2Rx = 3,\n    LPI2C2Tx = 4,\n    LPI2C3Rx = 5,\n    LPI2C3Tx = 6,\n    I3C0Rx = 7,\n    I3C0Tx = 8,\n    I3C1Rx = 9,\n    I3C1Tx = 10,\n    LPI2C0Rx = 11,\n    LPI2C0Tx = 12,\n    LPI2C1Rx = 13,\n    LPI2C1Tx = 14,\n    LPSPI0Rx = 15,\n    LPSPI0Tx = 16,\n    LPSPI1Rx = 17,\n    LPSPI1Tx = 18,\n    LPSPI2Rx = 19,\n    LPSPI2Tx = 20,\n    LPUART0Rx = 21,\n    LPUART0Tx = 22,\n    LPUART1Rx = 23,\n    LPUART1Tx = 24,\n    LPUART2Rx = 25,\n    LPUART2Tx = 26,\n    LPUART3Rx = 27,\n    LPUART3Tx = 28,\n    LPUART4Rx = 29,\n    LPUART4Tx = 30,\n    Ctimer0M0 = 31,\n    Ctimer0M1 = 32,\n    Ctimer1M0 = 33,\n    Ctimer1M1 = 34,\n    Ctimer2M0 = 35,\n    Ctimer2M1 = 36,\n    Ctimer3M0 = 37,\n    Ctimer3M1 = 38,\n    Ctimer4M0 = 39,\n    Ctimer4M1 = 40,\n    LPTMR0CounterMatchEvent = 49,\n    ADC0FifoRequest = 51,\n    ADC1FifoRequest = 52,\n    CMP0 = 53,\n    DAC0FifoRequest = 56,\n    DAC1FifoRequest = 57,\n    GPIO5PinEvent0 = 59,\n    GPIO0PinEvent0 = 60,\n    GPIO1PinEvent0 = 61,\n    GPIO2PinEvent0 = 62,\n    GPIO3PinEvent0 = 63,\n    GPIO4PinEvent0 = 64,\n    TsiEndOfScan = 69,\n    TsiOutOfRange = 70,\n    FlexIO0SR0 = 71,\n    FlexIO0SR1 = 72,\n    FlexIO0SR2 = 73,\n    FlexIO0SR3 = 74,\n    CAN1 = 87,\n    EspiCh0 = 92,\n    EspiCh1 = 93,\n    LPI2C4Rx = 94,\n    LPI2C4Tx = 95,\n    LPSPI3Rx = 96,\n    LPSPI3Tx = 97,\n    LPSPI4Rx = 98,\n    LPSPI4Tx = 99,\n    LPSPI5Rx = 100,\n    LPSPI5Tx = 101,\n    LPUART5Rx = 102,\n    LPUART5Tx = 103,\n    I3C2Rx = 106,\n    I3C2Tx = 107,\n    I3C3Rx = 108,\n    I3C3Tx = 109,\n    FlexSPI0Rx = 110,\n    FlexSPI0Tx = 111,\n    ITRCTmprOut0 = 117,\n    SGI0ReqIdat = 119,\n    SGI0ReqOdat = 120,\n    Gpio0PinEvent1 = 132,\n    Gpio1PinEvent1 = 133,\n    Gpio2PinEvent1 = 134,\n    Gpio3PinEvent1 = 135,\n    Gpio4PinEvent1 = 136,\n    Gpio5PinEvent1 = 137,\n}\n\nimpl DmaRequest {\n    /// Convert enumerated value into a raw integer\n    pub const fn number(self) -> u8 {\n        self as u8\n    }\n\n    /// Convert a raw integer into an enumerated value\n    ///\n    /// ## SAFETY\n    ///\n    /// The given number MUST be one of the defined variant, e.g. a number\n    /// derived from [`Self::number()`], otherwise it is immediate undefined behavior.\n    pub unsafe fn from_number_unchecked(num: u8) -> Self {\n        unsafe { core::mem::transmute(num) }\n    }\n}\n\nmod sealed {\n    /// Sealed trait for DMA channels.\n    pub trait SealedChannel {\n        /// Zero-based channel index into the TCD array.\n        fn index(&self) -> usize;\n\n        /// Interrupt vector for this channel.\n        fn interrupt(&self) -> crate::interrupt::Interrupt;\n    }\n}\n\n/// Marker trait implemented by HAL peripheral tokens that map to a DMA0\n/// channel backed by one EDMA_0_TCD0 TCD slot.\n#[allow(private_bounds)]\npub trait Channel: sealed::SealedChannel + PeripheralType + Into<AnyChannel> + 'static {}\n\n/// Type-erased DMA channel peripheral.\n///\n/// This allows storing DMA channels in a uniform way regardless of their\n/// concrete type, useful for async transfer futures and runtime channel selection.\n///\n/// ```rust,no_run\n/// # #![no_std]\n/// # #![no_main]\n/// #\n/// # extern crate panic_halt;\n/// # extern crate embassy_mcxa;\n/// # extern crate embassy_executor;\n/// # use panic_halt as _;\n/// # use embassy_executor::Spawner;\n/// # use embassy_hal_internal::Peri;\n/// # use embassy_mcxa::clocks::config::Div8;\n/// # use embassy_mcxa::config::Config;\n/// # use embassy_mcxa::dma::{DmaChannel, AnyChannel};\n/// #\n/// # #[embassy_executor::main]\n/// # async fn main(_spawner: Spawner) {\n/// #     let mut config = Config::default();\n/// #     config.clock_cfg.sirc.fro_12m_enabled = true;\n/// #     config.clock_cfg.sirc.fro_lf_div = Div8::from_divisor(1);\n/// #\n/// #     let p = embassy_mcxa::init(config);\n///     let anychannel: Peri<'static, AnyChannel> = p.DMA_CH0.into();\n///     DmaChannel::new(anychannel);\n/// # }\n/// ```\n#[derive(Debug, Clone, Copy)]\npub struct AnyChannel {\n    index: usize,\n    interrupt: Interrupt,\n}\n\nimpl PeripheralType for AnyChannel {}\nimpl sealed::SealedChannel for AnyChannel {\n    fn index(&self) -> usize {\n        self.index\n    }\n\n    fn interrupt(&self) -> Interrupt {\n        self.interrupt\n    }\n}\n\nimpl Channel for AnyChannel {}\n\n/// Macro to implement Channel trait for a peripheral.\nmacro_rules! impl_channel {\n    ($peri:ident, $index:expr, $irq:ident) => {\n        impl sealed::SealedChannel for crate::peripherals::$peri {\n            fn index(&self) -> usize {\n                $index\n            }\n\n            fn interrupt(&self) -> Interrupt {\n                Interrupt::$irq\n            }\n        }\n        impl Channel for crate::peripherals::$peri {}\n\n        impl From<crate::peripherals::$peri> for AnyChannel {\n            fn from(_: crate::peripherals::$peri) -> Self {\n                AnyChannel {\n                    index: $index,\n                    interrupt: Interrupt::$irq,\n                }\n            }\n        }\n    };\n}\n\nimpl_channel!(DMA_CH0, 0, DMA_CH0);\nimpl_channel!(DMA_CH1, 1, DMA_CH1);\nimpl_channel!(DMA_CH2, 2, DMA_CH2);\nimpl_channel!(DMA_CH3, 3, DMA_CH3);\nimpl_channel!(DMA_CH4, 4, DMA_CH4);\nimpl_channel!(DMA_CH5, 5, DMA_CH5);\nimpl_channel!(DMA_CH6, 6, DMA_CH6);\nimpl_channel!(DMA_CH7, 7, DMA_CH7);\n\n/// Parameters used to configure a 'typical' DMA transfer in [DmaChannel::setup_typical].\nstruct DmaTransferParameters<WSRC: Word, WDST: Word> {\n    /// Number of words (with size WDST) that should be transferred to the destination.\n    dst_count: usize,\n    /// Source pointer. If incrementing, the backing memory region should be at least as large as `count`.\n    src_ptr: *const WSRC,\n    /// Destination pointer. If incrementing, the backing memory region should be at least as large as `count`.\n    dst_ptr: *mut WDST,\n    /// Whether the source pointer should be incremented.\n    src_incr: bool,\n    /// Whether the destination pointer should be incremented.\n    dst_incr: bool,\n    /// Perform circular DMA.\n    ///\n    /// After each loop, will reset the current pointer to the starting addresses, both for src and dest.\n    circular: bool,\n    /// The transfer will be requested and managed by software.\n    ///\n    /// This implies that the transfer is completed in a single major loop iteration.\n    /// The transfer is also started from software, instead of using target DMA peripheral request signal.\n    software: bool,\n    /// Public facing transfer options that might be relevant.\n    options: TransferOptions,\n}\n\n/// DMA channel driver.\npub struct DmaChannel<'a> {\n    channel: Peri<'a, AnyChannel>,\n}\n\nimpl<'a> DmaChannel<'a> {\n    /// Wrap a DMA channel token (takes ownership of the Peri wrapper).\n    ///\n    /// Note: DMA is initialized during `hal::init()` via `dma::init()`.\n    #[inline]\n    pub fn new<C: Channel>(channel: embassy_hal_internal::Peri<'a, C>) -> Self {\n        unsafe {\n            cortex_m::peripheral::NVIC::unmask(channel.interrupt());\n        }\n        Self {\n            channel: channel.into(),\n        }\n    }\n}\n\nimpl DmaChannel<'_> {\n    /// Reborrow the DmaChannel with a shorter lifetime.\n    pub fn reborrow(&mut self) -> DmaChannel<'_> {\n        DmaChannel {\n            channel: self.channel.reborrow(),\n        }\n    }\n\n    /// Channel index in the EDMA_0_TCD0 array.\n    #[inline]\n    pub(crate) fn index(&self) -> usize {\n        self.channel.index()\n    }\n\n    /// Return a reference to the underlying TCD register block.\n    #[inline]\n    pub(crate) fn tcd(&self) -> pac::edma_0_tcd::Tcd {\n        // Safety: MCXA276 has a single eDMA instance\n        pac::EDMA_0_TCD0.tcd(self.channel.index())\n    }\n\n    /// set a manual callback to be called AFTER the DMA interrupt is processed. Will be called in the DMA interrupt\n    /// context.\n    ///\n    /// SAFETY: This must only be called on an owned DmaChannel, as there is only a single\n    /// callback slot, and calling this will invalidate any previously set callbacks.\n    pub(crate) unsafe fn set_callback(&mut self, f: fn()) {\n        // See https://doc.rust-lang.org/std/primitive.fn.html#casting-to-and-from-integers\n        let cb = f as *mut ();\n        CALLBACKS[self.index()].store(cb, Ordering::Release);\n    }\n\n    /// Unset the callback, causing no method to be called after DMA completion.\n    ///\n    /// SAFETY: This must only be called on an owned DmaChannel, as there is only a single\n    /// callback slot, and calling this will invalidate any previously set callbacks.\n    pub(crate) unsafe fn clear_callback(&mut self) {\n        CALLBACKS[self.index()].store(core::ptr::null_mut(), Ordering::Release);\n    }\n\n    /// Access TCD DADDR field\n    pub(crate) fn daddr(&self) -> u32 {\n        self.tcd().tcd_daddr().read().daddr()\n    }\n\n    fn clear_tcd(t: &pac::edma_0_tcd::Tcd) {\n        // Full TCD reset following NXP SDK pattern (EDMA_TcdResetExt).\n        // Reset ALL TCD registers to 0 to clear any stale configuration from\n        // previous transfers. This is critical when reusing a channel.\n        t.tcd_saddr().write(|w| w.set_saddr(0));\n        t.tcd_soff().write(|w| w.set_soff(0));\n        t.tcd_attr().write(|w| *w = TcdAttr(0));\n        t.tcd_nbytes_mloffno().write(|w| w.set_nbytes(0));\n        t.tcd_daddr().write(|w| w.set_daddr(0));\n        t.tcd_doff().write(|w| w.set_doff(0));\n        t.tcd_citer_elinkno().write(|w| *w = TcdCiterElinkno(0));\n        t.tcd_dlast_sga().write(|w| w.set_dlast_sga(0));\n        t.tcd_csr().write(|w| *w = TcdCsr(0)); // Clear CSR completly\n        t.tcd_biter_elinkno().write(|w| *w = TcdBiterElinkno(0));\n    }\n\n    #[inline]\n    fn set_major_loop_ct_elinkno(t: &pac::edma_0_tcd::Tcd, count: u16) {\n        t.tcd_biter_elinkno().write(|w| w.set_biter(count));\n        t.tcd_citer_elinkno().write(|w| w.set_citer(count));\n    }\n\n    #[inline]\n    fn set_major_loop_nbytes_without_minor(t: &pac::edma_0_tcd::Tcd, count: u32) {\n        t.tcd_nbytes_mloffno().write(|w| {\n            w.set_smloe(TcdNbytesMloffnoSmloe::OFFSET_NOT_APPLIED);\n            w.set_dmloe(TcdNbytesMloffnoDmloe::OFFSET_NOT_APPLIED);\n            w.set_nbytes(count)\n        });\n    }\n\n    #[inline]\n    fn set_no_final_adjustments(t: &pac::edma_0_tcd::Tcd) {\n        // No source/dest adjustment after major loop\n        t.tcd_slast_sda().write(|w| w.set_slast_sda(0));\n        t.tcd_dlast_sga().write(|w| w.set_dlast_sga(0));\n    }\n\n    #[inline]\n    fn set_source_ptr<T>(t: &pac::edma_0_tcd::Tcd, p: *const T) {\n        t.tcd_saddr().write(|w| w.set_saddr(p as u32));\n    }\n\n    #[inline]\n    fn set_source_increment(t: &pac::edma_0_tcd::Tcd, sz: WordSize) {\n        t.tcd_soff().write(|w| w.set_soff(sz.bytes() as u16));\n    }\n\n    #[inline]\n    fn set_source_fixed(t: &pac::edma_0_tcd::Tcd) {\n        t.tcd_soff().write(|w| w.set_soff(0));\n    }\n\n    #[inline]\n    fn set_dest_ptr<T>(t: &pac::edma_0_tcd::Tcd, p: *mut T) {\n        t.tcd_daddr().write(|w| w.set_daddr(p as u32));\n    }\n\n    #[inline]\n    fn set_dest_increment(t: &pac::edma_0_tcd::Tcd, sz: WordSize) {\n        t.tcd_doff().write(|w| w.set_doff(sz.bytes() as u16));\n    }\n\n    #[inline]\n    fn set_dest_fixed(t: &pac::edma_0_tcd::Tcd) {\n        t.tcd_doff().write(|w| w.set_doff(0));\n    }\n\n    #[inline]\n    fn set_fixed_priority(t: &pac::edma_0_tcd::Tcd, p: Priority) {\n        t.ch_pri().write(|w| {\n            w.set_dpa(Dpa::SUSPEND);\n            w.set_ecp(Ecp::SUSPEND);\n            w.set_apl(p as u8);\n        });\n    }\n\n    #[inline]\n    fn reset_channel_state(t: &pac::edma_0_tcd::Tcd) {\n        // CSR: Resets to all zeroes (disabled), \"done\" is cleared by writing 1\n        t.ch_csr().write(|w| w.set_done(true));\n        // ES: Resets to all zeroes (disabled), \"err\" is cleared by writing 1\n        t.ch_es().write(|w| w.set_err(true));\n        // INT: Resets to all zeroes (disabled), \"int\" is cleared by writing 1\n        t.ch_int().write(|w| w.set_int(true));\n    }\n\n    /// Start an async transfer.\n    ///\n    /// The channel must already be configured. This enables the channel\n    /// request and returns a `Transfer` future that resolves when the\n    /// DMA transfer completes.\n    ///\n    /// # Safety\n    ///\n    /// The caller must ensure the DMA channel has been properly configured\n    /// and that source/destination buffers remain valid for the duration\n    /// of the transfer.\n    #[allow(unused)]\n    pub(crate) unsafe fn start_transfer(&mut self) -> Transfer<'_> {\n        // Clear any previous DONE/INT flags\n        let t = self.tcd();\n        t.ch_csr().modify(|w| w.set_done(true));\n        t.ch_int().write(|w| w.set_int(true));\n\n        // Enable the channel request\n        t.ch_csr().modify(|w| {\n            w.set_erq(true);\n            w.set_earq(true);\n        });\n\n        Transfer::new(self.reborrow())\n    }\n\n    /// Setup a typical DMA transfer.\n    ///\n    /// A minor loop iteration transfers to a single word before yielding to arbitrage.\n    /// A major loop iteration transfers the entire buffer.\n    ///\n    /// # Safety\n    ///\n    /// Requires that the source/destination buffers remain valid for the duration\n    /// of the transfer.\n    unsafe fn setup_transfers<WSRC: Word, WDST: Word>(&self, params: DmaTransferParameters<WSRC, WDST>) {\n        let byte_count = (params.dst_count as usize * WDST::size().bytes()) as u32;\n\n        let t = self.tcd();\n\n        // Reset channel state - clear DONE, disable requests, clear errors\n        Self::reset_channel_state(&t);\n\n        // Memory & compiler barrier to ensure channel state is fully reset before touching TCD\n        fence(Ordering::Release);\n\n        Self::clear_tcd(&t);\n\n        Self::set_source_ptr(&t, params.src_ptr);\n        if params.src_incr {\n            Self::set_source_increment(&t, WSRC::size());\n        } else {\n            Self::set_source_fixed(&t);\n        }\n\n        Self::set_dest_ptr(&t, params.dst_ptr);\n        if params.dst_incr {\n            Self::set_dest_increment(&t, WDST::size());\n        } else {\n            Self::set_dest_fixed(&t);\n        }\n\n        // Transfer attributes (size)\n        t.tcd_attr().write(|w| {\n            w.set_ssize(WSRC::size().to_hw_size());\n            w.set_dsize(WDST::size().to_hw_size());\n        });\n\n        let (minor, major) = if params.software {\n            // When called from software, transfer all bytes in a single major loop iteration.\n            (byte_count, 1)\n        } else {\n            // When driven by hardware, transfer a DST word per major loop iteration.\n            // When the DMA channel requests (ERQ) is driven by a peripheral, this is desirable.\n            (WDST::size().bytes() as u32, params.dst_count as u16)\n        };\n\n        // Set the amount of bytes to be transferred per major loop iteration without minor loop offsets.\n        Self::set_major_loop_nbytes_without_minor(&t, minor);\n        Self::set_major_loop_ct_elinkno(&t, major);\n\n        // Configure channel to be interruptable, to interrupt, with a set priority.\n        Self::set_fixed_priority(&t, params.options.priority);\n\n        if params.circular {\n            let byte_diff = -(byte_count as i32); // Decrement the address pointers (if incrementing & not fixed).\n            let byte_diff_reg = byte_diff as u32; // Cast as u32 so that it can be stored in the register.\n\n            t.tcd_slast_sda()\n                .write(|w| w.set_slast_sda(if params.src_incr { byte_diff_reg } else { 0 }));\n            t.tcd_dlast_sga()\n                .write(|w| w.set_dlast_sga(if params.dst_incr { byte_diff_reg } else { 0 }));\n        } else {\n            Self::set_no_final_adjustments(&t);\n        }\n\n        // Memory & compiler barrier before setting START\n        fence(Ordering::Release);\n\n        // Control/status: interrupt on major complete, start\n        // Write this last after all other TCD registers are configured\n        t.tcd_csr().write(|w| {\n            w.set_intmajor(params.options.complete_transfer_interrupt);\n            w.set_inthalf(params.options.half_transfer_interrupt);\n            w.set_start(if params.software {\n                Start::CHANNEL_STARTED\n            } else {\n                Start::CHANNEL_NOT_STARTED\n            });\n            w.set_esg(Esg::NORMAL_FORMAT);\n            w.set_majorelink(false);\n            w.set_eeop(false);\n            w.set_esda(false);\n            w.set_bwc(Bwc::NO_STALL);\n\n            w.set_dreq(if params.circular {\n                Dreq::CHANNEL_NOT_AFFECTED // Don't clear ERQ on complete (circular)\n            } else {\n                Dreq::ERQ_FIELD_CLEAR // Auto-disable request after major loop\n            });\n        });\n\n        // Memory & compiler barrier after setting START\n        fence(Ordering::Release);\n    }\n\n    // ========================================================================\n    // Type-Safe Transfer Methods (Embassy-style API)\n    // ========================================================================\n\n    /// Perform a memory-to-memory DMA transfer (simplified API).\n    ///\n    /// This is a type-safe wrapper that uses the `Word` trait to determine\n    /// the correct transfer width automatically. Uses the global eDMA TCD\n    /// register accessor internally.\n    ///\n    /// # Arguments\n    ///\n    /// * `src` - Source buffer\n    /// * `dst` - Destination buffer (must be at least as large as src)\n    /// * `options` - Transfer configuration options\n    ///\n    /// # Safety\n    ///\n    /// The source and destination buffers must remain valid for the\n    /// duration of the transfer.\n    pub fn mem_to_mem<W: Word>(\n        &mut self,\n        src: &[W],\n        dst: &mut [W],\n        options: TransferOptions,\n    ) -> Result<Transfer<'_>, InvalidParameters> {\n        if src.is_empty() || src.len() > dst.len() || src.len() > DMA_MAX_TRANSFER_SIZE {\n            return Err(InvalidParameters);\n        }\n\n        unsafe {\n            self.setup_transfers(DmaTransferParameters {\n                src_ptr: src.as_ptr(),\n                dst_ptr: dst.as_mut_ptr(),\n                dst_count: dst.len(),\n                src_incr: true,\n                dst_incr: true,\n                circular: false,\n                software: true,\n                options,\n            });\n        }\n\n        Ok(Transfer::new(self.reborrow()))\n    }\n\n    /// Fill a memory buffer with a pattern value (memset).\n    ///\n    /// This performs a DMA transfer where the source address remains fixed\n    /// (pattern value) while the destination address increments through the buffer.\n    /// It's useful for quickly filling large memory regions with a constant value.\n    ///\n    /// # Arguments\n    ///\n    /// * `pattern` - Reference to the pattern value (will be read repeatedly)\n    /// * `dst` - Destination buffer to fill\n    /// * `options` - Transfer configuration options\n    ///\n    /// # Example\n    ///\n    /// ```rust,no_run\n    /// # #![no_std]\n    /// # #![no_main]\n    ///\n    /// # extern crate panic_halt;\n    /// # extern crate embassy_mcxa;\n    /// # extern crate embassy_executor;\n    /// # use panic_halt as _;\n    /// # use embassy_executor::Spawner;\n    /// # use embassy_mcxa::clocks::config::Div8;\n    /// # use embassy_mcxa::config::Config;\n    /// # use embassy_mcxa::dma::{DmaChannel, TransferOptions};\n    ///\n    /// # #[embassy_executor::main]\n    /// # async fn main(_spawner: Spawner) {\n    ///     let mut config = Config::default();\n    ///     config.clock_cfg.sirc.fro_12m_enabled = true;\n    ///     config.clock_cfg.sirc.fro_lf_div = Div8::from_divisor(1);\n    ///\n    ///     let p = embassy_mcxa::init(config);\n    ///\n    ///     let mut ch0 = DmaChannel::new(p.DMA_CH0);\n    ///     let pattern = 0xDEADBEEF_u32;\n    ///     let mut buffer = [0u32; 256];\n    ///\n    ///     unsafe {\n    ///         ch0.memset(&pattern, &mut buffer, TransferOptions::COMPLETE_INTERRUPT).unwrap().await;\n    ///     }\n    /// # }\n    /// ```\n    pub fn memset<W: Word>(\n        &mut self,\n        pattern: &W,\n        dst: &mut [W],\n        options: TransferOptions,\n    ) -> Result<Transfer<'_>, InvalidParameters> {\n        if dst.is_empty() || dst.len() > DMA_MAX_TRANSFER_SIZE {\n            return Err(InvalidParameters);\n        }\n\n        unsafe {\n            self.setup_transfers(DmaTransferParameters {\n                src_ptr: pattern,\n                dst_ptr: dst.as_mut_ptr(),\n                dst_count: dst.len(),\n                src_incr: false,\n                dst_incr: true,\n                circular: false,\n                software: true,\n                options,\n            });\n        }\n\n        Ok(Transfer::new(self.reborrow()))\n    }\n\n    /// Write data from memory to a peripheral register.\n    ///\n    /// The destination address remains fixed (peripheral register) while\n    /// the source address increments through the buffer.\n    ///\n    /// # Arguments\n    ///\n    /// * `buf` - Source buffer to write from\n    /// * `peri_addr` - Peripheral register address\n    /// * `options` - Transfer configuration options\n    ///\n    /// # Safety\n    ///\n    /// - The buffer must remain valid for the duration of the transfer.\n    /// - The peripheral address must be valid for writes.\n    pub unsafe fn write_to_peripheral<W: Word>(\n        &mut self,\n        buf: &[W],\n        peri_addr: *mut W,\n        options: TransferOptions,\n    ) -> Result<Transfer<'_>, InvalidParameters> {\n        unsafe { self.setup_write_to_peripheral(buf, peri_addr, false, options)? };\n        Ok(Transfer::new(self.reborrow()))\n    }\n\n    /// Read data from a peripheral register to memory.\n    ///\n    /// The source address remains fixed (peripheral register) while\n    /// the destination address increments through the buffer.\n    ///\n    /// # Arguments\n    ///\n    /// * `peri_addr` - Peripheral register address\n    /// * `buf` - Destination buffer to read into\n    /// * `options` - Transfer configuration options\n    ///\n    /// # Safety\n    ///\n    /// - The buffer must remain valid for the duration of the transfer.\n    /// - The peripheral address must be valid for reads.\n    pub unsafe fn read_from_peripheral<W: Word>(\n        &mut self,\n        peri_addr: *const W,\n        buf: &mut [W],\n        options: TransferOptions,\n    ) -> Result<Transfer<'_>, InvalidParameters> {\n        unsafe { self.setup_read_from_peripheral(peri_addr, buf, false, options)? };\n        Ok(Transfer::new(self.reborrow()))\n    }\n\n    /// Configure a memory-to-peripheral DMA transfer without starting it.\n    ///\n    /// This configures the TCD for a memory-to-peripheral transfer but does NOT\n    /// return a Transfer object. The caller is responsible for:\n    /// 1. Enabling the peripheral's DMA request\n    /// 2. Calling `enable_request()` to start the transfer\n    /// 3. Polling `is_done()` or using interrupts to detect completion\n    /// 4. Calling `disable_request()`, `clear_done()`, `clear_interrupt()` for cleanup\n    ///\n    /// Use this when you need manual control over the DMA lifecycle (e.g., in\n    /// peripheral drivers that have their own completion polling).\n    ///\n    /// # Arguments\n    ///\n    /// * `peri_addr` - Peripheral register address\n    /// * `enable_interrupt` - Whether to enable interrupt on completion\n    ///\n    /// # Safety\n    ///\n    /// - The buffer must remain valid for the duration of the transfer.\n    /// - The peripheral address must be valid for writes.\n    pub(crate) unsafe fn setup_write_zeros_to_peripheral<W: Word>(\n        &self,\n        count: usize,\n        peri_addr: *mut W,\n        options: TransferOptions,\n    ) -> Result<(), InvalidParameters> {\n        if count > DMA_MAX_TRANSFER_SIZE {\n            return Err(InvalidParameters);\n        }\n\n        #[repr(C, align(4))]\n        struct Zero([u8; WordSize::LARGEST.bytes()]);\n\n        // Static mut so that this is allocated in RAM.\n        static mut DUMMY: Zero = Zero([0u8; WordSize::LARGEST.bytes()]);\n\n        unsafe {\n            self.setup_transfers(DmaTransferParameters {\n                // Unsafe: cast to W is safe as DUMMY is aligned and guaranteed to be as least as large.\n                src_ptr: core::ptr::addr_of_mut!(DUMMY) as *const W,\n                dst_ptr: peri_addr,\n                dst_count: count,\n                src_incr: false,\n                dst_incr: false,\n                circular: false,\n                software: false,\n                options,\n            });\n        }\n\n        Ok(())\n    }\n\n    /// Produce the number of bytes transferred at the time of calling\n    /// this function.\n    pub fn transferred_bytes(&self) -> usize {\n        critical_section::with(|_| {\n            let t = self.tcd();\n            let biter = t.tcd_biter_elinkno().read().biter() as usize;\n            let citer = t.tcd_citer_elinkno().read().citer() as usize;\n            let minor = t.tcd_nbytes_mloffno().read().nbytes() as usize;\n            (biter - citer) * minor\n        })\n    }\n\n    /// Configure a memory-to-peripheral DMA transfer without starting it.\n    ///\n    /// This configures the TCD for a memory-to-peripheral transfer but does NOT\n    /// return a Transfer object. The caller is responsible for:\n    /// 1. Enabling the peripheral's DMA request\n    /// 2. Calling `enable_request()` to start the transfer\n    /// 3. Polling `is_done()` or using interrupts to detect completion\n    /// 4. Calling `disable_request()`, `clear_done()`, `clear_interrupt()` for cleanup\n    ///\n    /// Use this when you need manual control over the DMA lifecycle (e.g., in\n    /// peripheral drivers that have their own completion polling).\n    ///\n    /// # Arguments\n    ///\n    /// * `buf` - Source buffer to write from\n    /// * `peri_addr` - Peripheral register address\n    /// * `software` - Use software start for the transfer; otherwise use hardware ERQ to drive the transfer.\n    ///                Should be `false` unless your peripheral does not support hardware ERQ.\n    /// * `enable_interrupt` - Whether to enable interrupt on completion\n    ///\n    /// # Safety\n    ///\n    /// - The buffer must remain valid for the duration of the transfer.\n    /// - The peripheral address must be valid for writes.\n    pub(crate) unsafe fn setup_write_to_peripheral<WSRC: Word, WDST: Word>(\n        &self,\n        buf: &[WSRC],\n        peri_addr: *mut WDST,\n        software: bool,\n        options: TransferOptions,\n    ) -> Result<(), InvalidParameters> {\n        if buf.is_empty() || buf.len() > DMA_MAX_TRANSFER_SIZE {\n            return Err(InvalidParameters);\n        }\n\n        unsafe {\n            self.setup_transfers(DmaTransferParameters {\n                src_ptr: buf.as_ptr(),\n                dst_ptr: peri_addr,\n                dst_count: buf.len() * WSRC::size().bytes() / WDST::size().bytes(),\n                src_incr: true,\n                dst_incr: false,\n                circular: false,\n                software,\n                options,\n            });\n        }\n\n        Ok(())\n    }\n\n    /// Configure a peripheral-to-memory DMA transfer without starting it.\n    ///\n    /// This configures the TCD for a peripheral-to-memory transfer but does NOT\n    /// return a Transfer object. The caller is responsible for:\n    /// 1. Enabling the peripheral's DMA request\n    /// 2. Calling `enable_request()` to start the transfer\n    /// 3. Polling `is_done()` or using interrupts to detect completion\n    /// 4. Calling `disable_request()`, `clear_done()`, `clear_interrupt()` for cleanup\n    ///\n    /// Use this when you need manual control over the DMA lifecycle (e.g., in\n    /// peripheral drivers that have their own completion polling).\n    ///\n    /// # Arguments\n    ///\n    /// * `peri_addr` - Peripheral register address\n    /// * `buf` - Destination buffer to read into\n    /// * `software` - Use software start for the transfer; otherwise use hardware ERQ to drive the transfer.\n    ///                Should be `false` unless your peripheral does not support hardware ERQ.\n    /// * `enable_interrupt` - Whether to enable interrupt on completion\n    ///\n    /// # Safety\n    ///\n    /// - The buffer must remain valid for the duration of the transfer.\n    /// - The peripheral address must be valid for reads.\n    pub(crate) unsafe fn setup_read_from_peripheral<WSRC: Word, WDST: Word>(\n        &self,\n        peri_addr: *const WSRC,\n        buf: &mut [WDST],\n        software: bool,\n        options: TransferOptions,\n    ) -> Result<(), InvalidParameters> {\n        if buf.is_empty() || buf.len() > DMA_MAX_TRANSFER_SIZE {\n            return Err(InvalidParameters);\n        }\n\n        unsafe {\n            self.setup_transfers(DmaTransferParameters {\n                src_ptr: peri_addr,\n                dst_ptr: buf.as_mut_ptr(),\n                dst_count: buf.len(),\n                src_incr: false,\n                dst_incr: true,\n                circular: false,\n                software,\n                options,\n            });\n        }\n\n        Ok(())\n    }\n\n    /// Configure the integrated channel MUX to use the given typed\n    /// DMA request source (e.g., [`Lpuart2TxRequest`] or [`Lpuart2RxRequest`]).\n    ///\n    /// This is the type-safe version that uses marker types to ensure\n    /// compile-time verification of request source validity.\n    ///\n    /// # Safety\n    ///\n    /// The channel must be properly configured before enabling requests.\n    /// The caller must ensure the DMA request source matches the peripheral\n    /// that will drive this channel.\n    ///\n    /// # Note\n    ///\n    /// The NXP SDK requires a two-step write sequence: first clear\n    /// the mux to 0, then set the actual source. This is a hardware\n    /// requirement on eDMA4 for the mux to properly latch.\n    ///\n    /// # Example\n    ///\n    /// ```rust,ignore\n    /// use embassy_mcxa::dma::{DmaChannel, Lpuart2RxRequest};\n    ///\n    /// unsafe {\n    ///     channel.set_request_source(Lpuart2RxRequest::REQUEST_NUMBER);\n    /// }\n    /// ```\n    #[inline]\n    pub(crate) unsafe fn set_request_source(&self, source: DmaRequest) {\n        // Two-step write per NXP SDK: clear to 0, then set actual source.\n        self.tcd().ch_mux().write(|w| w.set_src(0));\n        cortex_m::asm::dsb(); // Ensure the clear completes before setting new source\n        self.tcd().ch_mux().write(|w| w.set_src(source.number()));\n    }\n\n    /// Enable hardware requests for this channel (ERQ=1).\n    ///\n    /// # Safety\n    ///\n    /// The channel must be properly configured before enabling requests.\n    pub(crate) unsafe fn enable_request(&self) {\n        let t = self.tcd();\n        t.ch_csr().modify(|w| {\n            w.set_erq(true);\n            w.set_earq(true);\n        });\n    }\n\n    /// Disable hardware requests for this channel (ERQ=0).\n    ///\n    /// # Safety\n    ///\n    /// Disabling requests on an active transfer may leave the transfer incomplete.\n    pub(crate) unsafe fn disable_request(&self) {\n        let t = self.tcd();\n        t.ch_csr().modify(|w| {\n            w.set_erq(false);\n            w.set_earq(false);\n        });\n    }\n\n    /// Return true if the channel's DONE flag is set.\n    pub(crate) fn is_done(&self) -> bool {\n        let t = self.tcd();\n        t.ch_csr().read().done()\n    }\n\n    /// Clear the DONE flag for this channel.\n    ///\n    /// Uses modify to preserve other bits (especially ERQ) unlike write\n    /// which would clear ERQ and halt an active transfer.\n    ///\n    /// # Safety\n    ///\n    /// Clearing DONE while a transfer is in progress may cause undefined behavior.\n    pub(crate) unsafe fn clear_done(&self) {\n        let t = self.tcd();\n        t.ch_csr().modify(|w| w.set_done(true));\n    }\n\n    /// Clear the channel interrupt flag (CH_INT.INT).\n    ///\n    /// # Safety\n    ///\n    /// Must be called from the correct interrupt context or with interrupts disabled.\n    pub(crate) unsafe fn clear_interrupt(&self) {\n        let t = self.tcd();\n        t.ch_int().write(|w| w.set_int(true));\n    }\n\n    /// Trigger a software start for this channel.\n    ///\n    /// # Safety\n    ///\n    /// The channel must be properly configured with a valid TCD before triggering.\n    #[allow(unused)]\n    pub(crate) unsafe fn trigger_start(&self) {\n        let t = self.tcd();\n        t.tcd_csr().modify(|w| w.set_start(Start::CHANNEL_STARTED));\n    }\n\n    /// Get the waker for this channel\n    pub(crate) fn waker(&self) -> &'static AtomicWaker {\n        &STATES[self.channel.index()].waker\n    }\n\n    /// Enable the interrupt for this channel in the NVIC.\n    pub(crate) fn enable_interrupt(&self) {\n        unsafe {\n            cortex_m::peripheral::NVIC::unmask(self.channel.interrupt());\n        }\n    }\n\n    /// Enable Major Loop Linking.\n    ///\n    /// When the major loop completes, the hardware will trigger a service request\n    /// on `link_ch`.\n    ///\n    /// # Arguments\n    ///\n    /// * `link_ch` - Target channel index (0-7) to link to\n    ///\n    /// # Safety\n    ///\n    /// The channel must be properly configured before setting up linking.\n    #[allow(unused)]\n    pub(crate) unsafe fn set_major_link(&self, link_ch: usize) {\n        let t = self.tcd();\n        t.tcd_csr().modify(|w| {\n            w.set_majorelink(true);\n            w.set_majorlinkch(link_ch as u8)\n        });\n    }\n\n    /// Disable Major Loop Linking.\n    ///\n    /// Removes any major loop channel linking previously configured.\n    ///\n    /// # Safety\n    ///\n    /// The caller must ensure this doesn't disrupt an active transfer that\n    /// depends on the linking.\n    #[allow(unused)]\n    pub(crate) unsafe fn clear_major_link(&self) {\n        let t = self.tcd();\n        t.tcd_csr().modify(|w| w.set_majorelink(false));\n    }\n\n    /// Enable Minor Loop Linking.\n    ///\n    /// After each minor loop, the hardware will trigger a service request\n    /// on `link_ch`.\n    ///\n    /// # Arguments\n    ///\n    /// * `link_ch` - Target channel index (0-7) to link to\n    ///\n    /// # Note\n    ///\n    /// This rewrites CITER and BITER registers to the ELINKYES format.\n    /// It preserves the current loop count.\n    ///\n    /// # Safety\n    ///\n    /// The channel must be properly configured before setting up linking.\n    #[allow(unused)]\n    pub(crate) unsafe fn set_minor_link(&self, link_ch: usize) {\n        let t = self.tcd();\n\n        // Read current CITER (assuming ELINKNO format initially)\n        let current_citer = t.tcd_citer_elinkno().read().citer();\n        let current_biter = t.tcd_biter_elinkno().read().biter();\n\n        // Write back using ELINKYES format\n        t.tcd_citer_elinkyes().write(|w| {\n            w.set_citer(current_citer);\n            w.set_elink(true);\n            w.set_linkch(link_ch as u8);\n        });\n\n        t.tcd_biter_elinkyes().write(|w| {\n            w.set_biter(current_biter);\n            w.set_elink(true);\n            w.set_linkch(link_ch as u8);\n        });\n    }\n\n    /// Disable Minor Loop Linking.\n    ///\n    /// Removes any minor loop channel linking previously configured.\n    /// This rewrites CITER and BITER registers to the ELINKNO format,\n    /// preserving the current loop count.\n    ///\n    /// # Safety\n    ///\n    /// The caller must ensure this doesn't disrupt an active transfer that\n    /// depends on the linking.\n    #[allow(unused)]\n    pub(crate) unsafe fn clear_minor_link(&self) {\n        let t = self.tcd();\n\n        // Read current CITER (could be in either format, but we only need the count)\n        // Note: In ELINKYES format, citer is 9 bits; in ELINKNO, it's 15 bits.\n        // We read from ELINKNO which will give us the combined value.\n        let current_citer = t.tcd_citer_elinkno().read().citer();\n        let current_biter = t.tcd_biter_elinkno().read().biter();\n\n        // Write back using ELINKNO format (disabling link)\n        t.tcd_citer_elinkno().write(|w| {\n            w.set_citer(current_citer);\n            w.set_elink(false);\n        });\n\n        t.tcd_biter_elinkno().write(|w| {\n            w.set_biter(current_biter);\n            w.set_elink(false);\n        });\n    }\n\n    /// Load a TCD from memory into the hardware channel registers.\n    ///\n    /// This is useful for scatter/gather and ping-pong transfers where\n    /// TCDs are prepared in RAM and then loaded into the hardware.\n    ///\n    /// # Safety\n    ///\n    /// - The TCD must be properly initialized.\n    /// - The caller must ensure no concurrent access to the same channel.\n    unsafe fn load_tcd(&self, tcd: &Tcd) {\n        let t = self.tcd();\n        t.tcd_saddr().write(|w| w.set_saddr(tcd.saddr));\n        t.tcd_soff().write(|w| w.set_soff(tcd.soff as u16));\n        t.tcd_attr().write(|w| w.0 = tcd.attr);\n        t.tcd_nbytes_mloffno().write(|w| w.set_nbytes(tcd.nbytes));\n        t.tcd_slast_sda().write(|w| w.set_slast_sda(tcd.slast as u32));\n        t.tcd_daddr().write(|w| w.set_daddr(tcd.daddr));\n        t.tcd_doff().write(|w| w.set_doff(tcd.doff as u16));\n        t.tcd_citer_elinkno().write(|w| w.set_citer(tcd.citer));\n        t.tcd_dlast_sga().write(|w| w.set_dlast_sga(tcd.dlast_sga as u32));\n        t.tcd_csr().write(|w| w.0 = tcd.csr);\n        t.tcd_biter_elinkno().write(|w| w.set_biter(tcd.biter));\n    }\n}\n\n/// In-memory representation of a Transfer Control Descriptor (TCD).\n///\n/// This matches the hardware layout (32 bytes).\n#[repr(C, align(32))]\n#[derive(Clone, Copy, Debug, Default)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\nstruct Tcd {\n    pub saddr: u32,\n    pub soff: i16,\n    pub attr: u16,\n    pub nbytes: u32,\n    pub slast: i32,\n    pub daddr: u32,\n    pub doff: i16,\n    pub citer: u16,\n    pub dlast_sga: i32,\n    pub csr: u16,\n    pub biter: u16,\n}\n\nstruct State {\n    /// Waker for transfer complete interrupt\n    waker: AtomicWaker,\n    /// Waker for half-transfer interrupt\n    half_waker: AtomicWaker,\n}\n\nimpl State {\n    const fn new() -> Self {\n        Self {\n            waker: AtomicWaker::new(),\n            half_waker: AtomicWaker::new(),\n        }\n    }\n}\n\nstatic STATES: [State; 8] = [\n    State::new(),\n    State::new(),\n    State::new(),\n    State::new(),\n    State::new(),\n    State::new(),\n    State::new(),\n    State::new(),\n];\n\npub(crate) fn waker(idx: usize) -> &'static AtomicWaker {\n    &STATES[idx].waker\n}\n\npub(crate) fn half_waker(idx: usize) -> &'static AtomicWaker {\n    &STATES[idx].half_waker\n}\n\n// ============================================================================\n// Async Transfer Future\n// ============================================================================\n\n/// An in-progress DMA transfer.\n///\n/// This type implements `Future` and can be `.await`ed to wait for the\n/// transfer to complete. Dropping the transfer will abort it.\n#[must_use = \"futures do nothing unless you `.await` or poll them\"]\npub struct Transfer<'a> {\n    channel: DmaChannel<'a>,\n}\n\nimpl<'a> Transfer<'a> {\n    /// Create a new transfer for the given channel.\n    ///\n    /// The caller must have already configured and started the DMA channel.\n    pub(crate) fn new(channel: DmaChannel<'a>) -> Self {\n        Self { channel }\n    }\n\n    /// Check if the transfer is still running.\n    pub fn is_running(&self) -> bool {\n        !self.channel.is_done()\n    }\n\n    /// Get the remaining transfer count.\n    pub fn remaining(&self) -> u16 {\n        let t = self.channel.tcd();\n        t.tcd_citer_elinkno().read().citer()\n    }\n\n    /// Block until the transfer completes.\n    pub fn blocking_wait(self) {\n        while self.is_running() {\n            core::hint::spin_loop();\n        }\n\n        // Ensure all DMA writes are visible\n        fence(Ordering::Release);\n\n        // Don't run drop (which would abort)\n        core::mem::forget(self);\n    }\n\n    /// Wait for the half-transfer interrupt asynchronously.\n    ///\n    /// This is useful for double-buffering scenarios where you want to process\n    /// the first half of the buffer while the second half is being filled.\n    ///\n    /// Returns `true` if the half-transfer occurred, `false` if the transfer\n    /// completed before the half-transfer interrupt.\n    ///\n    /// # Note\n    ///\n    /// The transfer must be configured with `TransferOptions::half_transfer_interrupt = true`\n    /// for this method to work correctly.\n    pub async fn wait_half(&mut self) -> Result<bool, TransferErrors> {\n        use core::future::poll_fn;\n\n        poll_fn(|cx| {\n            let state = &STATES[self.channel.index()];\n\n            // Register the half-transfer waker\n            state.half_waker.register(cx.waker());\n\n            // Check if there's an error\n            let t = self.channel.tcd();\n            let es = t.ch_es().read();\n            if es.err() {\n                // Currently, all error fields are in the lowest 8 bits, as-casting truncates\n                let errs = es.0 as u8;\n                return Poll::Ready(Err(TransferErrors(errs)));\n            }\n\n            // Check if we're past the half-way point\n            let biter = t.tcd_biter_elinkno().read().biter();\n            let citer = t.tcd_citer_elinkno().read().citer();\n            let half_point = biter / 2;\n\n            if self.channel.is_done() {\n                // Transfer completed before half-transfer\n                Poll::Ready(Ok(false))\n            } else if citer <= half_point {\n                // We're past the half-way point\n                fence(Ordering::SeqCst);\n                Poll::Ready(Ok(true))\n            } else {\n                Poll::Pending\n            }\n        })\n        .await\n    }\n\n    /// Abort the transfer.\n    fn abort(&mut self) {\n        let t = self.channel.tcd();\n\n        // Disable channel requests\n        t.ch_csr().modify(|w| {\n            w.set_erq(false);\n            w.set_earq(false);\n        });\n\n        // Clear any pending interrupt\n        t.ch_int().write(|w| w.set_int(true));\n\n        // Clear DONE flag\n        t.ch_csr().modify(|w| w.set_done(true));\n\n        fence(Ordering::SeqCst);\n    }\n}\n\n/// A collection of [TransferError] returned by any transfer.\n///\n/// Each error variant can be queried separately, or all errors can be iterated by using [TransferErrors::into_iter].\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\n#[derive(Copy, Clone, Debug)]\npub struct TransferErrors(u8);\n\n/// Iterator to extract all [TransferError]s using [TransferErrors::into_iter].\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\n#[derive(Copy, Clone, Debug)]\npub struct TransferErrorIter(u8);\n\nimpl TransferErrors {\n    const MAP: &[(u8, TransferError)] = &[\n        (1 << 0, TransferError::DestinationBus),\n        (1 << 1, TransferError::SourceBus),\n        (1 << 2, TransferError::ScatterGatherConfiguration),\n        (1 << 3, TransferError::NbytesCiterConfiguration),\n        (1 << 4, TransferError::DestinationOffset),\n        (1 << 5, TransferError::DestinationAddress),\n        (1 << 6, TransferError::SourceOffset),\n        (1 << 7, TransferError::SourceAddress),\n    ];\n\n    /// Destination Bus Error\n    #[inline]\n    pub fn has_destination_bus_err(&self) -> bool {\n        (self.0 & (1 << 0)) != 0\n    }\n\n    /// Source Bus Error\n    #[inline]\n    pub fn has_source_bus_err(&self) -> bool {\n        (self.0 & (1 << 1)) != 0\n    }\n\n    /// Indicates that `TCDn_DLAST_SGA` is not on a 32-byte boundary. This field is\n    /// checked at the beginning of a scatter/gather operation after major loop completion\n    /// if `TCDn_CSR[ESG]` is enabled.\n    #[inline]\n    pub fn has_scatter_gather_configuration_err(&self) -> bool {\n        (self.0 & (1 << 2)) != 0\n    }\n\n    /// This error indicates that one of the following has occurred:\n    ///\n    /// * `TCDn_NBYTES` is not a multiple of `TCDn_ATTR[SSIZE]` and `TCDn_ATTR[DSIZE]`\n    /// * `TCDn_CITER[CITER]` is equal to zero\n    /// * `TCDn_CITER[ELINK]` is not equal to `TCDn_BITER[ELINK]`\n    #[inline]\n    pub fn has_nbytes_citer_configuration_err(&self) -> bool {\n        (self.0 & (1 << 3)) != 0\n    }\n\n    /// `TCDn_DOFF` is inconsistent with `TCDn_ATTR[DSIZE]`.\n    #[inline]\n    pub fn has_destination_offset_err(&self) -> bool {\n        (self.0 & (1 << 4)) != 0\n    }\n\n    /// `TCDn_DADDR` is inconsistent with `TCDn_ATTR[DSIZE]`.\n    #[inline]\n    pub fn has_destination_address_err(&self) -> bool {\n        (self.0 & (1 << 5)) != 0\n    }\n\n    /// `TCDn_SOFF` is inconsistent with `TCDn_ATTR[SSIZE]`.\n    #[inline]\n    pub fn has_source_offset_err(&self) -> bool {\n        (self.0 & (1 << 6)) != 0\n    }\n\n    /// `TCDn_SADDR` is inconsistent with `TCDn_ATTR[SSIZE]`\n    #[inline]\n    pub fn has_source_address_err(&self) -> bool {\n        (self.0 & (1 << 7)) != 0\n    }\n}\n\nimpl IntoIterator for TransferErrors {\n    type Item = TransferError;\n\n    type IntoIter = TransferErrorIter;\n\n    fn into_iter(self) -> Self::IntoIter {\n        TransferErrorIter(self.0)\n    }\n}\n\nimpl Iterator for TransferErrorIter {\n    type Item = TransferError;\n\n    fn next(&mut self) -> Option<Self::Item> {\n        if self.0 == 0 {\n            return None;\n        }\n\n        for (mask, var) in TransferErrors::MAP {\n            // If the bit is set...\n            if self.0 & mask != 0 {\n                // clear the bit\n                self.0 &= !mask;\n                // and return the answer\n                return Some(*var);\n            }\n        }\n\n        // Shouldn't happen, but oh well.\n        None\n    }\n}\n\n/// An error that can be returned as the result of a failed transfer.\n#[derive(Copy, Clone, Debug)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub enum TransferError {\n    /// `TCDn_SADDR` is inconsistent with `TCDn_ATTR[SSIZE]`\n    SourceAddress,\n    /// `TCDn_SOFF` is inconsistent with `TCDn_ATTR[SSIZE]`.\n    SourceOffset,\n    /// `TCDn_DADDR` is inconsistent with `TCDn_ATTR[DSIZE]`.\n    DestinationAddress,\n    /// `TCDn_DOFF` is inconsistent with `TCDn_ATTR[DSIZE]`.\n    DestinationOffset,\n    /// This error indicates that one of the following has occurred:\n    ///\n    /// * `TCDn_NBYTES` is not a multiple of `TCDn_ATTR[SSIZE]` and `TCDn_ATTR[DSIZE]`\n    /// * `TCDn_CITER[CITER]` is equal to zero\n    /// * `TCDn_CITER[ELINK]` is not equal to `TCDn_BITER[ELINK]`\n    NbytesCiterConfiguration,\n    /// Indicates that `TCDn_DLAST_SGA` is not on a 32-byte boundary. This field is\n    /// checked at the beginning of a scatter/gather operation after major loop completion\n    /// if `TCDn_CSR[ESG]` is enabled.\n    ScatterGatherConfiguration,\n    /// Source Bus Error\n    SourceBus,\n    /// Destination Bus Error\n    DestinationBus,\n}\n\nimpl<'a> Unpin for Transfer<'a> {}\n\nimpl<'a> Future for Transfer<'a> {\n    type Output = Result<(), TransferErrors>;\n\n    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {\n        let state = &STATES[self.channel.index()];\n        state.waker.register(cx.waker());\n\n        if self.channel.is_done() {\n            // Ensure all DMA writes are visible before returning\n            fence(Ordering::Acquire);\n\n            let es = self.channel.tcd().ch_es().read();\n            if es.err() {\n                // Currently, all error fields are in the lowest 8 bits, as-casting truncates\n                let errs = es.0 as u8;\n                Poll::Ready(Err(TransferErrors(errs)))\n            } else {\n                Poll::Ready(Ok(()))\n            }\n        } else {\n            Poll::Pending\n        }\n    }\n}\n\nimpl<'a> Drop for Transfer<'a> {\n    fn drop(&mut self) {\n        // Only abort if the transfer is still running\n        // If already complete, no need to abort\n        if self.is_running() {\n            self.abort();\n\n            // Wait for abort to complete\n            while self.is_running() {\n                core::hint::spin_loop();\n            }\n        }\n\n        fence(Ordering::Release);\n    }\n}\n\n/// A ring buffer for continuous DMA reception.\n///\n/// Can only be constructed by drivers in this HAL, and not from the application.\n///\n/// This structure manages a circular DMA transfer, allowing continuous\n/// reception of data without losing bytes between reads. It uses both\n/// half-transfer and complete-transfer interrupts to track available data.\npub struct RingBuffer<'channel, 'buf, W: Word> {\n    /// Reference to the DmaChannel for the duration of the DMA transfer.\n    ///\n    /// When this RingBuffer is dropped, the DmaChannel becomes usable again.\n    channel: DmaChannel<'channel>,\n    /// Buffer pointer. We use NonNull instead of &mut because DMA acts like\n    /// a separate thread writing to this buffer, and &mut claims exclusive\n    /// access which the compiler could optimize incorrectly.\n    buf: NonNull<[W]>,\n    /// Buffer length cached for convenience\n    buf_len: usize,\n    /// Read position in the buffer (consumer side)\n    read_pos: AtomicUsize,\n    /// Phantom data to tie the lifetime to the original buffer\n    _lt: PhantomData<&'buf mut [W]>,\n}\n\nimpl<'channel, 'buf, W: Word> RingBuffer<'channel, 'buf, W> {\n    /// Create a new ring buffer for the given channel and buffer.\n    ///\n    /// # Safety\n    ///\n    /// The caller must ensure:\n    /// - The DMA channel has been configured for circular transfer\n    /// - The buffer remains valid for the lifetime of the ring buffer\n    /// - Only one RingBuffer exists per DMA channel at a time\n    pub(crate) unsafe fn new(channel: DmaChannel<'channel>, buf: &'buf mut [W]) -> Self {\n        let buf_len = buf.len();\n        Self {\n            channel,\n            buf: NonNull::from(buf),\n            buf_len,\n            read_pos: AtomicUsize::new(0),\n            _lt: PhantomData,\n        }\n    }\n\n    /// Get a slice reference to the buffer.\n    ///\n    /// # Safety\n    ///\n    /// The caller must ensure that DMA is not actively writing to the\n    /// portion of the buffer being accessed, or that the access is\n    /// appropriately synchronized.\n    #[inline]\n    unsafe fn buf_slice(&self) -> &[W] {\n        unsafe { self.buf.as_ref() }\n    }\n\n    /// Get the current DMA write position in the buffer.\n    ///\n    /// This reads the current destination address from the DMA controller\n    /// and calculates the buffer offset.\n    fn dma_write_pos(&self) -> usize {\n        let t = self.channel.tcd();\n        let daddr = t.tcd_daddr().read().daddr() as usize;\n        let buf_start = self.buf.as_ptr() as *const W as usize;\n\n        // Calculate offset from buffer start\n        let offset = daddr.wrapping_sub(buf_start) / core::mem::size_of::<W>();\n\n        // Ensure we're within bounds (DMA wraps around)\n        offset % self.buf_len\n    }\n\n    /// Returns the number of bytes available to read.\n    pub fn available(&self) -> usize {\n        let write_pos = self.dma_write_pos();\n        let read_pos = self.read_pos.load(Ordering::Acquire);\n\n        if write_pos >= read_pos {\n            write_pos - read_pos\n        } else {\n            self.buf_len - read_pos + write_pos\n        }\n    }\n\n    /// Check if the buffer has overrun (data was lost).\n    ///\n    /// This happens when DMA writes faster than the application reads.\n    pub fn is_overrun(&self) -> bool {\n        // In a true overrun, the DMA would have wrapped around and caught up\n        // to our read position. We can detect this by checking if available()\n        // equals the full buffer size (minus 1 to distinguish from empty).\n        self.available() >= self.buf_len - 1\n    }\n\n    /// Read data from the ring buffer into the provided slice.\n    ///\n    /// Returns the number of elements read, which may be less than\n    /// `dst.len()` if not enough data is available.\n    ///\n    /// This method does not block; use `read_async()` for async waiting.\n    pub fn read_immediate(&self, dst: &mut [W]) -> usize {\n        let write_pos = self.dma_write_pos();\n        let read_pos = self.read_pos.load(Ordering::Acquire);\n\n        // Calculate available bytes\n        let available = if write_pos >= read_pos {\n            write_pos - read_pos\n        } else {\n            self.buf_len - read_pos + write_pos\n        };\n\n        let to_read = dst.len().min(available);\n        if to_read == 0 {\n            return 0;\n        }\n\n        // Safety: We only read from portions of the buffer that DMA has\n        // already written to (between read_pos and write_pos).\n        let buf = unsafe { self.buf_slice() };\n\n        // Read data, handling wrap-around\n        let first_chunk = (self.buf_len - read_pos).min(to_read);\n        dst[..first_chunk].copy_from_slice(&buf[read_pos..read_pos + first_chunk]);\n\n        if to_read > first_chunk {\n            let second_chunk = to_read - first_chunk;\n            dst[first_chunk..to_read].copy_from_slice(&buf[..second_chunk]);\n        }\n\n        // Update read position\n        let new_read_pos = (read_pos + to_read) % self.buf_len;\n        self.read_pos.store(new_read_pos, Ordering::Release);\n\n        to_read\n    }\n\n    /// Read data from the ring buffer asynchronously.\n    ///\n    /// This waits until at least one byte is available, then reads as much\n    /// as possible into the destination buffer.\n    ///\n    /// Returns the number of elements read.\n    pub async fn read(&self, dst: &mut [W]) -> Result<usize, Error> {\n        use core::future::poll_fn;\n\n        if dst.is_empty() {\n            return Ok(0);\n        }\n\n        poll_fn(|cx| {\n            // Check for overrun\n            if self.is_overrun() {\n                return Poll::Ready(Err(Error::Overrun));\n            }\n\n            // Try to read immediately\n            let n = self.read_immediate(dst);\n            if n > 0 {\n                return Poll::Ready(Ok(n));\n            }\n\n            // Register wakers for both half and complete interrupts\n            let state = &STATES[self.channel.index()];\n            state.waker.register(cx.waker());\n            state.half_waker.register(cx.waker());\n\n            // Check again after registering waker (avoid race)\n            let n = self.read_immediate(dst);\n            if n > 0 {\n                return Poll::Ready(Ok(n));\n            }\n\n            Poll::Pending\n        })\n        .await\n    }\n\n    /// Clear the ring buffer, discarding all unread data.\n    pub fn clear(&self) {\n        let write_pos = self.dma_write_pos();\n        self.read_pos.store(write_pos, Ordering::Release);\n    }\n\n    /// Stop the DMA transfer and consume the ring buffer.\n    ///\n    /// Returns any remaining unread data count.\n    pub fn stop(mut self) -> usize {\n        let res = self.teardown();\n        drop(self);\n        res\n    }\n\n    /// Enable the DMA channel request.\n    ///\n    /// Call this to start continuous reception.\n    /// This is separated from setup to allow for any additional configuration\n    /// before starting the transfer.\n    ///\n    /// ## SAFETY\n    ///\n    /// The Dma Channel must have been setup with proper manual configuration prior to\n    /// calling `enable_dma_request`. See safety requirements of the configuration methods\n    /// for more details.\n    pub(crate) unsafe fn enable_dma_request(&self) {\n        unsafe {\n            self.channel.enable_request();\n        }\n    }\n\n    /// Stop the DMA transfer. Intended to be called by `stop()` or `Drop`.\n    fn teardown(&mut self) -> usize {\n        let available = self.available();\n\n        // Disable the channel\n        let t = self.channel.tcd();\n        t.ch_csr().modify(|w| {\n            w.set_erq(false);\n            w.set_earq(false)\n        });\n\n        // Clear flags\n        t.ch_int().write(|w| w.set_int(true));\n        t.ch_csr().modify(|w| w.set_done(true));\n\n        fence(Ordering::Release);\n\n        available\n    }\n}\n\nimpl<W: Word> Drop for RingBuffer<'_, '_, W> {\n    fn drop(&mut self) {\n        self.teardown();\n    }\n}\n\nimpl<'a> DmaChannel<'a> {\n    /// Set up a circular DMA transfer for continuous peripheral-to-memory reception.\n    ///\n    /// This configures the DMA channel for circular operation with both half-transfer\n    /// and complete-transfer interrupts enabled. The transfer runs continuously until\n    /// stopped via [`RingBuffer::stop()`].\n    ///\n    /// # Arguments\n    ///\n    /// * `peri_addr` - Peripheral register address to read from\n    /// * `buf` - Destination buffer (should be power-of-2 size for best efficiency)\n    ///\n    /// # Returns\n    ///\n    /// A [`RingBuffer`] that can be used to read received data.\n    ///\n    /// # Safety\n    ///\n    /// - The peripheral address must be valid for reads.\n    /// - The peripheral's DMA request must be configured to trigger this channel.\n    pub(crate) unsafe fn setup_circular_read<'buf, W: Word>(\n        &mut self,\n        peri_addr: *const W,\n        buf: &'buf mut [W],\n    ) -> Result<RingBuffer<'_, 'buf, W>, InvalidParameters> {\n        if buf.is_empty() || buf.len() > DMA_MAX_TRANSFER_SIZE {\n            return Err(InvalidParameters);\n        }\n\n        unsafe {\n            self.setup_transfers(DmaTransferParameters {\n                src_ptr: peri_addr,\n                dst_ptr: buf.as_mut_ptr(),\n                dst_count: buf.len(),\n                src_incr: false,\n                dst_incr: true,\n                circular: true,\n                software: true,\n                options: TransferOptions {\n                    half_transfer_interrupt: true,\n                    complete_transfer_interrupt: true,\n                    priority: Priority::default(),\n                },\n            });\n        }\n\n        // Enable NVIC interrupt for this channel so async wakeups work\n        self.enable_interrupt();\n\n        Ok(unsafe { RingBuffer::new(self.reborrow(), buf) })\n    }\n}\n\n/// Maximum number of TCDs in a scatter-gather chain.\npub(crate) const MAX_SCATTER_GATHER_TCDS: usize = 16;\n\n/// A builder for constructing scatter-gather DMA transfer chains.\n///\n/// This provides a type-safe way to build TCD chains for scatter-gather\n/// transfers without manual TCD manipulation.\n///\n/// # Example\n///\n/// ```rust,no_run\n/// # #![no_std]\n/// # #![no_main]\n/// #\n/// # extern crate panic_halt;\n/// # extern crate embassy_mcxa;\n/// # extern crate embassy_executor;\n/// # use panic_halt as _;\n/// # use embassy_executor::Spawner;\n/// # use embassy_hal_internal::Peri;\n/// # use embassy_mcxa::clocks::config::Div8;\n/// # use embassy_mcxa::config::Config;\n/// # use embassy_mcxa::dma::{DmaChannel, ScatterGatherBuilder};\n/// #\n/// # #[embassy_executor::main]\n/// # async fn main(_spawner: Spawner) {\n/// #     let mut config = Config::default();\n/// #     config.clock_cfg.sirc.fro_12m_enabled = true;\n/// #     config.clock_cfg.sirc.fro_lf_div = Div8::from_divisor(1);\n/// #\n/// #     let p = embassy_mcxa::init(config);\n///     let src1 = [0x00u32; 128];\n///     let src2 = [0xffu32; 128];\n///     let src3 = [0x55u32; 128];\n///\n///     let mut dst1 = [0x00u32; 128];\n///     let mut dst2 = [0x00u32; 128];\n///     let mut dst3 = [0x00u32; 128];\n///\n///     let mut ch0 = DmaChannel::new(p.DMA_CH0);\n///     let mut builder = ScatterGatherBuilder::<u32>::new();\n///\n///     // Add transfer segments\n///     builder.add_transfer(&src1, &mut dst1);\n///     builder.add_transfer(&src2, &mut dst2);\n///     builder.add_transfer(&src3, &mut dst3);\n///\n///     // Build and execute\n///     let transfer = unsafe { builder.build(ch0).unwrap() };\n///     transfer.await;\n/// # }\n/// ```\npub struct ScatterGatherBuilder<'a, W: Word> {\n    /// TCD pool (must be 32-byte aligned)\n    tcds: [Tcd; MAX_SCATTER_GATHER_TCDS],\n    /// Number of TCDs configured\n    count: usize,\n    /// Phantom marker for word type\n    _phantom: core::marker::PhantomData<W>,\n\n    _plt: core::marker::PhantomData<&'a mut W>,\n}\n\nimpl<'a, W: Word> ScatterGatherBuilder<'a, W> {\n    /// Create a new scatter-gather builder.\n    pub fn new() -> Self {\n        ScatterGatherBuilder {\n            tcds: [Tcd::default(); MAX_SCATTER_GATHER_TCDS],\n            count: 0,\n            _phantom: core::marker::PhantomData,\n            _plt: core::marker::PhantomData,\n        }\n    }\n\n    /// Add a memory-to-memory transfer segment to the chain.\n    ///\n    /// # Arguments\n    ///\n    /// * `src` - Source buffer for this segment\n    /// * `dst` - Destination buffer for this segment\n    ///\n    /// # Panics\n    ///\n    /// Panics if the maximum number of segments (16) is exceeded.\n    pub fn add_transfer<'b: 'a>(&mut self, src: &'b [W], dst: &'b mut [W]) -> &mut Self {\n        assert!(self.count < MAX_SCATTER_GATHER_TCDS, \"Too many scatter-gather segments\");\n        assert!(!src.is_empty());\n        assert!(dst.len() >= src.len());\n\n        let size = W::size();\n        let byte_size = size.bytes();\n        let hw_size = size.to_hw_size();\n        let nbytes = (src.len() * byte_size) as u32;\n\n        // Build the TCD for this segment\n        self.tcds[self.count] = Tcd {\n            saddr: src.as_ptr() as u32,\n            soff: byte_size as i16,\n            attr: ((hw_size as u16) << 8) | (hw_size as u16), // SSIZE | DSIZE\n            nbytes,\n            slast: 0,\n            daddr: dst.as_mut_ptr() as u32,\n            doff: byte_size as i16,\n            citer: 1,\n            dlast_sga: 0, // Will be filled in by build()\n            csr: 0x0002,  // INTMAJOR only (ESG will be set for non-last TCDs)\n            biter: 1,\n        };\n\n        self.count += 1;\n        self\n    }\n\n    /// Get the number of transfer segments added.\n    pub fn segment_count(&self) -> usize {\n        self.count\n    }\n\n    /// Build the scatter-gather chain and start the transfer.\n    ///\n    /// # Arguments\n    ///\n    /// * `channel` - The DMA channel to use for the transfer\n    ///\n    /// # Returns\n    ///\n    /// A `Transfer` future that completes when the entire chain has executed.\n    pub fn build(&mut self, channel: DmaChannel<'a>) -> Result<Transfer<'a>, Error> {\n        if self.count == 0 {\n            return Err(Error::Configuration);\n        }\n\n        // Link TCDs together\n        //\n        // CSR bit definitions:\n        // - START = bit 0 = 0x0001 (triggers transfer when set)\n        // - INTMAJOR = bit 1 = 0x0002 (interrupt on major loop complete)\n        // - ESG = bit 4 = 0x0010 (enable scatter-gather, loads next TCD on complete)\n        //\n        // When hardware loads a TCD via scatter-gather (ESG), it copies the TCD's\n        // CSR directly into the hardware register. If START is not set in that CSR,\n        // the hardware will NOT auto-execute the loaded TCD.\n        //\n        // Strategy:\n        // - First TCD: ESG | INTMAJOR (no START - we add it manually after loading)\n        // - Middle TCDs: ESG | INTMAJOR | START (auto-execute when loaded via S/G)\n        // - Last TCD: INTMAJOR | START (auto-execute, no further linking)\n        for i in 0..self.count {\n            let is_first = i == 0;\n            let is_last = i == self.count - 1;\n\n            if is_first {\n                if is_last {\n                    // Only one TCD - no ESG, no START (we add START manually)\n                    self.tcds[i].dlast_sga = 0;\n                    self.tcds[i].csr = 0x0002; // INTMAJOR only\n                } else {\n                    // First of multiple - ESG to link, no START (we add START manually)\n                    self.tcds[i].dlast_sga = &self.tcds[i + 1] as *const Tcd as i32;\n                    self.tcds[i].csr = 0x0012; // ESG | INTMAJOR\n                }\n            } else if is_last {\n                // Last TCD (not first) - no ESG, but START so it auto-executes\n                self.tcds[i].dlast_sga = 0;\n                self.tcds[i].csr = 0x0003; // INTMAJOR | START\n            } else {\n                // Middle TCD - ESG to link, and START so it auto-executes\n                self.tcds[i].dlast_sga = &self.tcds[i + 1] as *const Tcd as i32;\n                self.tcds[i].csr = 0x0013; // ESG | INTMAJOR | START\n            }\n        }\n\n        let t = channel.tcd();\n\n        // Reset channel state - clear DONE, disable requests, clear errors\n        // This ensures the channel is in a clean state before loading the TCD\n        DmaChannel::reset_channel_state(&t);\n\n        // Memory barrier to ensure channel state is reset before loading TCD\n        cortex_m::asm::dsb();\n\n        // Load first TCD into hardware\n        unsafe {\n            channel.load_tcd(&self.tcds[0]);\n        }\n\n        // Memory barrier before setting START\n        cortex_m::asm::dsb();\n\n        // Start the transfer\n        t.tcd_csr().modify(|w| w.set_start(Start::CHANNEL_STARTED));\n\n        Ok(Transfer::new(channel))\n    }\n\n    /// Reset the builder for reuse.\n    pub fn clear(&mut self) {\n        self.count = 0;\n    }\n}\n\nimpl<W: Word> Default for ScatterGatherBuilder<'_, W> {\n    fn default() -> Self {\n        Self::new()\n    }\n}\n\n/// A completed scatter-gather transfer result.\n///\n/// This type is returned after a scatter-gather transfer completes,\n/// providing access to any error information.\n#[derive(Debug, Clone, Copy, PartialEq, Eq)]\npub struct ScatterGatherResult {\n    /// Number of segments successfully transferred\n    pub segments_completed: usize,\n    /// Error if any occurred\n    pub error: Option<Error>,\n}\n\n/// Interrupt handler helper.\n///\n/// Call this from your interrupt handler to clear the interrupt flag and wake the waker.\n/// This handles both half-transfer and complete-transfer interrupts.\n///\n/// # Safety\n/// Must be called from the correct DMA channel interrupt context.\nunsafe fn on_interrupt(ch_index: usize) {\n    crate::perf_counters::incr_interrupt_edma0();\n    let edma = &pac::EDMA_0_TCD0;\n    let t = edma.tcd(ch_index);\n\n    // Read TCD CSR to determine interrupt source\n    let csr = t.tcd_csr().read();\n\n    // Check if this is a half-transfer interrupt\n    // INTHALF is set and we're at or past the half-way point\n    if csr.inthalf() {\n        let biter = t.tcd_biter_elinkno().read().biter();\n        let citer = t.tcd_citer_elinkno().read().citer();\n        let half_point = biter / 2;\n\n        if citer <= half_point && citer > 0 {\n            // Half-transfer interrupt - wake half_waker\n            crate::perf_counters::incr_interrupt_edma0_wake();\n            half_waker(ch_index).wake();\n        }\n    }\n\n    // Clear INT flag\n    t.ch_int().write(|w| w.set_int(true));\n\n    // If DONE is set, this is a complete-transfer interrupt\n    // Only wake the full-transfer waker when the transfer is actually complete\n    if t.ch_csr().read().done() {\n        crate::perf_counters::incr_interrupt_edma0_wake();\n        waker(ch_index).wake();\n    }\n}\n\n/// Macro to generate DMA channel interrupt handlers.\nmacro_rules! impl_dma_interrupt_handler {\n    ($irq:ident, $ch:expr) => {\n        #[interrupt]\n        fn $irq() {\n            // SAFETY: The correct $ch is called as generated, We check that\n            // the given callback is non-null before calling.\n            unsafe {\n                on_interrupt($ch);\n\n                // See https://doc.rust-lang.org/std/primitive.fn.html#casting-to-and-from-integers\n                let cb: *mut () = CALLBACKS[$ch].load(Ordering::Acquire);\n                if !cb.is_null() {\n                    let cb: fn() = core::mem::transmute(cb);\n                    (cb)();\n                }\n            }\n        }\n    };\n}\n\nuse crate::pac::interrupt;\n\nimpl_dma_interrupt_handler!(DMA_CH0, 0);\nimpl_dma_interrupt_handler!(DMA_CH1, 1);\nimpl_dma_interrupt_handler!(DMA_CH2, 2);\nimpl_dma_interrupt_handler!(DMA_CH3, 3);\nimpl_dma_interrupt_handler!(DMA_CH4, 4);\nimpl_dma_interrupt_handler!(DMA_CH5, 5);\nimpl_dma_interrupt_handler!(DMA_CH6, 6);\nimpl_dma_interrupt_handler!(DMA_CH7, 7);\n\n// TODO(AJM): This is a gross, gross hack. This implements optional callbacks\n// for DMA completion interrupts. This should go away once we switch to\n// \"in-band\" DMA interrupt binding with `bind_interrupts!`.\nstatic CALLBACKS: [AtomicPtr<()>; 8] = [const { AtomicPtr::new(core::ptr::null_mut()) }; 8];\n"
  },
  {
    "path": "embassy-mcxa/src/executor.rs",
    "content": "use core::marker::PhantomData;\nuse core::sync::atomic::{AtomicBool, AtomicU8, AtomicU32, Ordering};\n\nuse embassy_executor::{Spawner, raw};\nuse embassy_hal_internal::Peri;\n\nuse crate::clocks::config::CoreSleep;\nuse crate::gpio::{DriveStrength, GpioPin, Level, Output, SlewRate};\nuse crate::pac::gpio::vals::{Ptco, Ptso};\n\nstatic TASKS_PENDING: AtomicBool = AtomicBool::new(false);\nstatic EXECUTOR_ONCE: AtomicU8 = AtomicU8::new(0);\n\n/// Information stored in format:\n///\n/// ```\n/// 0bAxxx_xxxx_xxxx_xxxx_OOOO_OOOO_IIII_IIII\n/// ```\n///\n/// Where:\n///\n/// * A: 1 bit, \"is active\", so we can differentiate between \"never set\"\n///   and \"use port 0 pin 0\"\n/// * O: 8 bits, \"pOrt number\"\n/// * I: 8 bits, \"pIn number\"\n///\n/// Initially set to `0`, which makes \"set lo\" and \"set hi\" a no-op\nstatic DEBUG_GPIO: AtomicU32 = AtomicU32::new(0);\n\nconst EXECUTOR_UNINIT: u8 = 0;\nconst EXECUTOR_TAKEN: u8 = 1;\n\n// Use a sentinel value for context to denote the thread pender context\nconst THREAD_PENDER: usize = usize::MAX;\n\npub struct Executor {\n    inner: raw::Executor,\n    not_send: PhantomData<*mut ()>,\n}\n\n/// TODO: Taken from embassy-stm32, verify this is necessary or what we want\n#[unsafe(export_name = \"__pender\")]\nfn __pender(context: *mut ()) {\n    // Safety: `context` is either `usize::MAX` created by `Executor::run`, or a valid interrupt\n    // request number given to `InterruptExecutor::start`.\n\n    let context = context as usize;\n\n    // Try to make Rust optimize the branching away if we only use thread mode.\n    if context == THREAD_PENDER {\n        TASKS_PENDING.store(true, Ordering::Release);\n        cortex_m::asm::sev();\n    }\n}\n\nimpl Executor {\n    // Note: We don't really want a Default impl for this singleton.\n    #[allow(clippy::new_without_default)]\n    pub fn new() -> Self {\n        let res = EXECUTOR_ONCE.compare_exchange(EXECUTOR_UNINIT, EXECUTOR_TAKEN, Ordering::AcqRel, Ordering::Relaxed);\n\n        if res.is_err() {\n            panic!(\"Can only take the executor once\");\n        }\n\n        Self {\n            inner: raw::Executor::new(THREAD_PENDER as *mut ()),\n            not_send: PhantomData,\n        }\n    }\n\n    /// Run the executor.\n    ///\n    /// The `init` closure is called with a [`Spawner`] that spawns tasks on\n    /// this executor. Use it to spawn the initial task(s). After `init` returns,\n    /// the executor starts running the tasks.\n    ///\n    /// To spawn more tasks later, you may keep copies of the [`Spawner`] (it is `Copy`),\n    /// for example by passing it as an argument to the initial tasks.\n    ///\n    /// This function requires `&'static mut self`. This means you have to store the\n    /// Executor instance in a place where it'll live forever and grants you mutable\n    /// access. There's a few ways to do this:\n    ///\n    /// - a [StaticCell](https://docs.rs/static_cell/latest/static_cell/) (safe)\n    /// - a `static mut` (unsafe)\n    /// - a local variable in a function you know never returns (like `fn main() -> !`), upgrading its lifetime with `transmute`. (unsafe)\n    ///\n    /// This function never returns.\n    pub fn run(&'static mut self, init: impl FnOnce(Spawner)) -> ! {\n        // We can only create the executor once\n        init(self.inner.spawner());\n\n        // Until we've performed HAL init, just do WFE sleep\n        let power_depth = loop {\n            unsafe {\n                self.inner.poll();\n\n                if go_around() {\n                    continue;\n                }\n\n                let sleep = crate::clocks::with_clocks(|c| c.core_sleep);\n                if let Some(s) = sleep {\n                    break s;\n                }\n                debug_lo();\n                do_wfe();\n                debug_hi();\n                crate::perf_counters::incr_wfe_sleeps();\n            }\n        };\n\n        match power_depth {\n            // For Wfe sleep, our sleep target is constant. This means that\n            // we don't need to do anything fancy here, just do a normal WFE\n            // loop, since clock init already set our sleep mode parameters\n            CoreSleep::WfeUngated | CoreSleep::WfeGated => loop {\n                unsafe {\n                    self.inner.poll();\n\n                    if go_around() {\n                        continue;\n                    }\n\n                    debug_lo();\n                    do_wfe();\n                    debug_hi();\n                    crate::perf_counters::incr_wfe_sleeps();\n                }\n            },\n            CoreSleep::DeepSleep => loop {\n                unsafe {\n                    // For deep sleep, we need to be a bit more clever. First, poll any\n                    // pending tasks\n                    self.inner.poll();\n\n                    if go_around() {\n                        continue;\n                    }\n\n                    // Next, we need to check if any high-power peripherals exist that should\n                    // inhibit us from entering deep sleep. Take a critical section to check.\n                    //\n                    // We STAY in the CS for the deep sleep to ensure that we handle wake-up\n                    // completely BEFORE yielding control flow back to interrupts.\n                    debug_lo();\n                    let do_wfe_sleep = critical_section::with(|cs| {\n                        let did_deep_sleep = crate::clocks::deep_sleep_if_possible(&cs);\n                        if did_deep_sleep {\n                            debug_hi();\n                        }\n                        !did_deep_sleep\n                    });\n\n                    // Did we succeed at deep sleeping?\n                    if do_wfe_sleep {\n                        // Nope, WFE. We don't need a critical section here because we don't\n                        // need to wait for clocks to resume before we service interrupts.\n                        do_wfe();\n                        debug_hi();\n                        crate::perf_counters::incr_wfe_sleeps();\n                    } else {\n                        // Yep!\n                        crate::perf_counters::incr_deep_sleeps();\n                    }\n                }\n            },\n        }\n    }\n}\n\n/// Every time we WFE, we want to do DSB; WFE.\n#[inline(always)]\nunsafe fn do_wfe() {\n    cortex_m::asm::dsb();\n    cortex_m::asm::wfe();\n}\n\n/// Function to go around to poll again, and clear a pending sev if we\n/// know we will immediately wake anyway.\nfn go_around() -> bool {\n    let sev_pending = TASKS_PENDING.swap(false, Ordering::AcqRel);\n    if sev_pending {\n        // We know __pender has sent a sev, ack it with a wfe, which we\n        // know will immediately return control flow to us.\n        cortex_m::asm::wfe();\n    }\n    sev_pending\n}\n\n/// Dedicate a pin to be used for introspecting the state of the custom executor.\n///\n/// This pin will be driven low prior to entering WFE (light or deep sleep),\n/// and be raised once control flow is returned to the executor.\n///\n/// For deep sleep, the pin will be raised *before* exiting the critical section,\n/// meaning that the level will go high before servicing any interrupts. For light\n/// sleep, no critical section is taken prior to deep sleep, so the pin will go\n/// high once the executor resumes, likely after processing any pending interrupts.\n///\n/// This exact behavior is not considered semver stable, and may change at any time.\n///\n/// This function consumes the given pin, overwriting any previous pin set as the executor\n/// debug pin. If this function is called multiple times, the previously assigned pin(s)\n/// will not be disabled, and will remain at their last updated state.\npub fn set_executor_debug_gpio(pin: Peri<'static, impl GpioPin>) {\n    let pin_num = pin.pin();\n    let port_num = pin.port();\n    // Setup GPIO as output, initially high\n    let output = Output::new(pin, Level::High, DriveStrength::Normal, SlewRate::Slow);\n\n    let number = 0x8000_0000 | ((port_num as u32) << 8) | (pin_num as u32);\n    core::mem::forget(output);\n    DEBUG_GPIO.store(number, Ordering::Relaxed);\n}\n\n/// Set low if we have a debug gpio set, using raw pac methods\nfn debug_lo() {\n    let num = DEBUG_GPIO.load(Ordering::Relaxed);\n    if num == 0 {\n        return;\n    }\n    let port_num = (num >> 8) as u8;\n    let pin_num = (num & 0xFF) as usize;\n    match port_num {\n        0 => crate::pac::GPIO0.pcor(),\n        1 => crate::pac::GPIO1.pcor(),\n        2 => crate::pac::GPIO2.pcor(),\n        3 => crate::pac::GPIO3.pcor(),\n        4 => crate::pac::GPIO4.pcor(),\n        _ => return,\n    }\n    .write(|w| w.set_ptco(pin_num, Ptco::PTCO1));\n}\n\n/// Set high if we have a debug gpio set, using raw pac methods\nfn debug_hi() {\n    let num = DEBUG_GPIO.load(Ordering::Relaxed);\n    if num == 0 {\n        return;\n    }\n    let port_num = (num >> 8) as u8;\n    let pin_num = (num & 0xFF) as usize;\n    match port_num {\n        0 => crate::pac::GPIO0.psor(),\n        1 => crate::pac::GPIO1.psor(),\n        2 => crate::pac::GPIO2.psor(),\n        3 => crate::pac::GPIO3.psor(),\n        4 => crate::pac::GPIO4.psor(),\n        _ => return,\n    }\n    .write(|w| w.set_ptso(pin_num, Ptso::PTSO1));\n}\n"
  },
  {
    "path": "embassy-mcxa/src/flash.rs",
    "content": "//! Flash driver for MCXA276 using ROM API.\n//!\n//! This module provides safe access to the MCXA276's internal flash memory through\n//! the ROM-resident flash driver API. The ROM API lives at a fixed address and exposes\n//! flash operations (init, erase, program, verify, read) via a function pointer table.\n//!\n//! # Flash Geometry (MCXA276)\n//!\n//! - Base address: `0x0000_0000`\n//! - Total size: 1 MB (`0x10_0000`)\n//! - Sector size: 8 KB (`0x2000`) — erase granularity\n//! - Page size: 128 bytes — program granularity\n//! - Phrase size: 16 bytes — minimum program unit\n//!\n//! # Safety\n//!\n//! All flash-modifying operations (erase, program) run inside a critical section\n//! to prevent interrupts from executing code in flash while it is being modified.\n//! After each modifying operation, speculation buffers and the LPCAC are cleared\n//! to ensure subsequent reads return fresh data.\n//!\n//! # Example\n//!\n//! ```rust,no_run\n//! #![no_std]\n//! #![no_main]\n//!\n//! # use panic_halt as _;\n//! use embassy_executor::Spawner;\n//! use embassy_mcxa::clocks::config::Div8;\n//! use embassy_mcxa::config::Config;\n//! use embassy_mcxa::flash::Flash;\n//! use embedded_storage::nor_flash::{NorFlash, ReadNorFlash};\n//!\n//! #[embassy_executor::main]\n//! async fn main(_spawner: Spawner) {\n//!     let mut config = Config::default();\n//!     config.clock_cfg.sirc.fro_lf_div = Div8::from_divisor(1);\n//!\n//!     let p = embassy_mcxa::init(config);\n//!\n//!     let mut flash = Flash::new().unwrap();\n//!\n//!     // Erase a sector at offset 0xFE000 (near the end of 1 MB flash)\n//!     flash.erase(0xFE000, 0x10_0000).unwrap();\n//!\n//!     // Program 128 bytes at that offset (must be a multiple of 16-byte phrase size)\n//!     let data = [0xABu8; 128];\n//!     flash.write(0xFE000, &data).unwrap();\n//!\n//!     // Read back\n//!     let mut buf = [0u8; 128];\n//!     flash.read(0xFE000, &mut buf).unwrap();\n//! }\n//!\n//! ```\n\nuse core::slice;\n\nuse embedded_storage::nor_flash::{ErrorType, NorFlash, NorFlashError, NorFlashErrorKind, ReadNorFlash};\n\nuse crate::pac;\nuse crate::pac::syscon::vals::{ClrLpcac, DisDataSpec, DisFlashSpec, DisLpcac, DisMbeccErrData, DisMbeccErrInst};\n\n// ---------------------------------------------------------------------------\n// Flash geometry constants\n// ---------------------------------------------------------------------------\n\n/// Base address of the internal program flash.\npub const FLASH_BASE: u32 = 0x0000_0000;\n\n/// Total size of the internal program flash in bytes (1 MB).\npub const FLASH_SIZE: usize = 0x10_0000;\n\n/// Sector size in bytes (8 KB) — erase granularity.\npub const SECTOR_SIZE: usize = 0x2000;\n\n/// Page size in bytes (128) — program-page granularity.\npub const PAGE_SIZE: usize = 128;\n\n/// Phrase size in bytes (16) — minimum program unit.\npub const PHRASE_SIZE: usize = 16;\n\n// ---------------------------------------------------------------------------\n// ROM API constants\n// ---------------------------------------------------------------------------\n\n/// Base address of the ROM API bootloader tree for MCXA276.\nconst ROM_API_BASE: u32 = 0x0300_5FE0;\n\n/// Flash erase key: `FOUR_CHAR_CODE('l','f','e','k')` in little-endian.\nconst FLASH_ERASE_KEY: u32 = 0x6B65_666C;\n\n// ---------------------------------------------------------------------------\n// ROM API C-ABI structures\n// ---------------------------------------------------------------------------\n\n/// Flash FFR (Factory Failure Records) configuration, populated by `flash_init`.\n#[repr(C)]\n#[derive(Debug, Default, Copy, Clone)]\nstruct FlashFfrConfig {\n    ffr_block_base: u32,\n    ffr_total_size: u32,\n    ffr_page_size: u32,\n    sector_size: u32,\n    cfpa_page_version: u32,\n    cfpa_page_offset: u32,\n}\n\n/// Flash driver configuration, populated by `flash_init`.\n#[repr(C)]\n#[derive(Debug, Default, Copy, Clone)]\nstruct FlashConfig {\n    pflash_block_base: u32,\n    pflash_total_size: u32,\n    pflash_block_count: u32,\n    pflash_page_size: u32,\n    pflash_sector_size: u32,\n    ffr_config: FlashFfrConfig,\n}\n\n// Type aliases for ROM API function pointer signatures (C ABI).\n// Only `flash_init` takes `*mut FlashConfig`; all other calls use `*const FlashConfig`.\ntype FnFlashInit = unsafe extern \"C\" fn(config: *mut FlashConfig) -> i32;\ntype FnFlashEraseSector = unsafe extern \"C\" fn(config: *const FlashConfig, start: u32, len: u32, key: u32) -> i32;\ntype FnFlashProgramPhrase =\n    unsafe extern \"C\" fn(config: *const FlashConfig, start: u32, src: *const u8, len: u32) -> i32;\ntype FnFlashProgramPage = unsafe extern \"C\" fn(config: *const FlashConfig, start: u32, src: *const u8, len: u32) -> i32;\ntype FnFlashVerifyProgram = unsafe extern \"C\" fn(\n    config: *const FlashConfig,\n    start: u32,\n    len: u32,\n    expected: *const u8,\n    failed_addr: *mut u32,\n    failed_data: *mut u32,\n) -> i32;\ntype FnFlashVerifyErasePhrase = unsafe extern \"C\" fn(config: *const FlashConfig, start: u32, len: u32) -> i32;\ntype FnFlashVerifyErasePage = unsafe extern \"C\" fn(config: *const FlashConfig, start: u32, len: u32) -> i32;\ntype FnFlashVerifyEraseSector = unsafe extern \"C\" fn(config: *const FlashConfig, start: u32, len: u32) -> i32;\ntype FnFlashGetProperty = unsafe extern \"C\" fn(config: *const FlashConfig, property: u32, value: *mut u32) -> i32;\ntype FnFlashRead = unsafe extern \"C\" fn(config: *const FlashConfig, start: u32, dest: *mut u8, len: u32) -> i32;\n\n/// ROM API flash driver interface vtable.\n///\n/// **Layout note**: On MCXA276, `FSL_FEATURE_ROMAPI_IFR == 0`, so the three\n/// IFR function pointers are *omitted*. `flash_read` and `version` follow\n/// immediately after `flash_get_property`.\n#[repr(C)]\nstruct FlashDriverInterface {\n    flash_init: FnFlashInit,\n    flash_erase_sector: FnFlashEraseSector,\n    flash_program_phrase: FnFlashProgramPhrase,\n    flash_program_page: FnFlashProgramPage,\n    flash_verify_program: FnFlashVerifyProgram,\n    flash_verify_erase_phrase: FnFlashVerifyErasePhrase,\n    flash_verify_erase_page: FnFlashVerifyErasePage,\n    flash_verify_erase_sector: FnFlashVerifyEraseSector,\n    flash_get_property: FnFlashGetProperty,\n    // IFR functions omitted (FSL_FEATURE_ROMAPI_IFR == 0 on MCXA276)\n    flash_read: FnFlashRead,\n    version: u32,\n}\n\n/// Root of the ROM bootloader API tree.\n#[repr(C)]\nstruct BootloaderTree {\n    run_bootloader: unsafe extern \"C\" fn(arg: *mut core::ffi::c_void),\n    flash_driver: *const FlashDriverInterface,\n    jump: unsafe extern \"C\" fn(arg: *mut core::ffi::c_void),\n}\n\n/// Returns a reference to the ROM API flash driver interface.\n#[inline(always)]\nfn flash_api() -> &'static FlashDriverInterface {\n    unsafe {\n        let tree = &*(ROM_API_BASE as *const BootloaderTree);\n        &*tree.flash_driver\n    }\n}\n\n// ---------------------------------------------------------------------------\n// Error types\n// ---------------------------------------------------------------------------\n\n/// Errors that can occur during flash operations.\n#[derive(Debug, Copy, Clone, PartialEq, Eq)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\n#[non_exhaustive]\npub enum Error {\n    /// Address or range is outside the flash region.\n    OutOfBounds,\n    /// Address or length is not properly aligned.\n    Unaligned,\n    /// The ROM API returned a non-zero status code.\n    RomApi(i32),\n}\n\n/// Flash property identifiers (ROM API `flash_get_property`).\n#[repr(u32)]\n#[derive(Debug, Copy, Clone, PartialEq, Eq)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub enum FlashProperty {\n    /// Pflash sector size property.\n    PflashSectorSize = 0x00,\n    /// Pflash total size property.\n    PflashTotalSize = 0x01,\n    /// Pflash block size property.\n    PflashBlockSize = 0x02,\n    /// Pflash block count property.\n    PflashBlockCount = 0x03,\n    /// Pflash block base address property.\n    PflashBlockBaseAddr = 0x04,\n    /// Pflash page size property.\n    PflashPageSize = 0x30,\n    /// Pflash system frequency property.\n    PflashSystemFreq = 0x31,\n    /// FFR sector size property.\n    FfrSectorSize = 0x40,\n    /// FFR total size property.\n    FfrTotalSize = 0x41,\n    /// FFR block base address property.\n    FfrBlockBaseAddr = 0x42,\n    /// FFR page size property.\n    FfrPageSize = 0x43,\n}\n\nimpl NorFlashError for Error {\n    fn kind(&self) -> NorFlashErrorKind {\n        match self {\n            Self::OutOfBounds => NorFlashErrorKind::OutOfBounds,\n            Self::Unaligned => NorFlashErrorKind::NotAligned,\n            Self::RomApi(_) => NorFlashErrorKind::Other,\n        }\n    }\n}\n\n/// Convert a ROM API status code to a `Result`.\n#[inline]\nfn check_status(status: i32) -> Result<(), Error> {\n    if status == 0 {\n        Ok(())\n    } else {\n        Err(Error::RomApi(status))\n    }\n}\n\n// ---------------------------------------------------------------------------\n// Cache clearing helpers (must be called after erase/program)\n// ---------------------------------------------------------------------------\n\n/// Clear flash and data speculation buffers by toggling the disable bits\n/// in SYSCON->NVM_CTRL, matching the C `speculation_buffer_clear()`.\n#[inline]\nfn speculation_buffer_clear() {\n    let nvm = pac::SYSCON.nvm_ctrl();\n    let val = nvm.read();\n\n    // Only proceed if MBECC error reporting is enabled for both inst and data\n    if val.dis_mbecc_err_inst() == DisMbeccErrInst::ENABLE && val.dis_mbecc_err_data() == DisMbeccErrData::ENABLE {\n        // Toggle flash speculation disable\n        if val.dis_flash_spec() == DisFlashSpec::ENABLE {\n            nvm.modify(|w| w.set_dis_flash_spec(DisFlashSpec::DISABLE));\n            nvm.modify(|w| w.set_dis_flash_spec(DisFlashSpec::ENABLE));\n        }\n        // Toggle data speculation disable\n        if nvm.read().dis_data_spec() == DisDataSpec::ENABLE {\n            nvm.modify(|w| w.set_dis_data_spec(DisDataSpec::DISABLE));\n            nvm.modify(|w| w.set_dis_data_spec(DisDataSpec::ENABLE));\n        }\n    }\n}\n\n/// Clear LPCAC by setting the CLR_LPCAC bit in SYSCON->LPCAC_CTRL,\n/// matching the C `lpcac_clear()`.\n#[inline]\nfn lpcac_clear() {\n    let lpcac = pac::SYSCON.lpcac_ctrl();\n    if lpcac.read().dis_lpcac() == DisLpcac::ENABLE {\n        lpcac.modify(|w| w.set_clr_lpcac(ClrLpcac::DISABLE));\n    }\n}\n\n/// Combined cache clearing: speculation buffers + LPCAC.\n#[inline]\nfn clear_caches() {\n    speculation_buffer_clear();\n    lpcac_clear();\n}\n\n// ---------------------------------------------------------------------------\n// Flash driver\n// ---------------------------------------------------------------------------\n\n/// Flash driver providing safe access to the MCXA276 internal flash via ROM API.\n///\n/// The driver holds an internal `FlashConfig` that is initialised by the ROM\n/// API's `flash_init` function during construction. All subsequent operations\n/// pass this config to the ROM API.\npub struct Flash {\n    config: FlashConfig,\n}\n\nimpl Flash {\n    /// Create and initialise a new flash driver.\n    ///\n    /// This calls the ROM API `flash_init` to populate the internal flash\n    /// configuration. Returns an error if the ROM API reports failure.\n    pub fn new() -> Result<Self, Error> {\n        let mut config = FlashConfig::default();\n        let status = unsafe { (flash_api().flash_init)(&mut config) };\n        check_status(status)?;\n        Ok(Self { config })\n    }\n\n    /// Erase flash sectors encompassing the given absolute address range.\n    ///\n    /// - `address`: absolute start address (must be sector-aligned).\n    /// - `len`: number of bytes to erase (must be a multiple of sector size).\n    ///\n    /// Runs inside a critical section and clears caches afterwards.\n    pub fn blocking_erase(&mut self, address: u32, len: u32) -> Result<(), Error> {\n        let status = cortex_m::interrupt::free(|_| unsafe {\n            (flash_api().flash_erase_sector)(&self.config, address, len, FLASH_ERASE_KEY)\n        });\n        clear_caches();\n        check_status(status)\n    }\n\n    /// Program a phrase (16 bytes) of data at the given absolute address.\n    ///\n    /// - `address`: absolute start address (must be phrase-aligned).\n    /// - `data`: source buffer whose length must be a multiple of `PHRASE_SIZE`.\n    ///\n    /// Runs inside a critical section and clears caches afterwards.\n    pub fn blocking_program_phrase(&mut self, address: u32, data: &[u8]) -> Result<(), Error> {\n        let status = cortex_m::interrupt::free(|_| unsafe {\n            (flash_api().flash_program_phrase)(&self.config, address, data.as_ptr(), data.len() as u32)\n        });\n        clear_caches();\n        check_status(status)\n    }\n\n    /// Program a page of data at the given absolute address.\n    ///\n    /// - `address`: absolute start address (must be page-aligned).\n    /// - `data`: source buffer whose length must be a multiple of `PAGE_SIZE`.\n    ///\n    /// Runs inside a critical section and clears caches afterwards.\n    pub fn blocking_program(&mut self, address: u32, data: &[u8]) -> Result<(), Error> {\n        let status = cortex_m::interrupt::free(|_| unsafe {\n            (flash_api().flash_program_page)(&self.config, address, data.as_ptr(), data.len() as u32)\n        });\n        clear_caches();\n        check_status(status)\n    }\n\n    /// Verify that the programmed data at `address` matches `expected`.\n    ///\n    /// On mismatch, returns `Error::RomApi` with the ROM status code.\n    pub fn verify_program(&mut self, address: u32, expected: &[u8]) -> Result<(), Error> {\n        let mut failed_address: u32 = 0;\n        let mut failed_data: u32 = 0;\n        let status = unsafe {\n            (flash_api().flash_verify_program)(\n                &self.config,\n                address,\n                expected.len() as u32,\n                expected.as_ptr(),\n                &mut failed_address,\n                &mut failed_data,\n            )\n        };\n        check_status(status)\n    }\n\n    /// Verify that the sector(s) starting at `address` are erased.\n    pub fn verify_erase_sector(&mut self, address: u32, len: u32) -> Result<(), Error> {\n        let status = unsafe { (flash_api().flash_verify_erase_sector)(&self.config, address, len) };\n        check_status(status)\n    }\n\n    /// Read flash data using the ROM API.\n    ///\n    /// - `address`: absolute start address.\n    /// - `dest`: destination buffer.\n    pub fn blocking_read_rom(&mut self, address: u32, dest: &mut [u8]) -> Result<(), Error> {\n        let status = unsafe { (flash_api().flash_read)(&self.config, address, dest.as_mut_ptr(), dest.len() as u32) };\n        check_status(status)\n    }\n\n    /// Read flash data by direct memory-mapped access (no ROM API call).\n    ///\n    /// - `offset`: byte offset from flash base (`0x0000_0000`).\n    /// - `dest`: destination buffer.\n    pub fn blocking_read(&self, offset: u32, dest: &mut [u8]) -> Result<(), Error> {\n        if offset as usize + dest.len() > FLASH_SIZE {\n            return Err(Error::OutOfBounds);\n        }\n        let src = unsafe { slice::from_raw_parts((FLASH_BASE + offset) as *const u8, dest.len()) };\n        dest.copy_from_slice(src);\n        Ok(())\n    }\n\n    /// Return the ROM API version.\n    pub fn rom_api_version(&self) -> u32 {\n        flash_api().version\n    }\n\n    /// Get a ROM API flash property value.\n    pub fn get_property(&mut self, property: FlashProperty) -> Result<u32, Error> {\n        let mut value: u32 = 0;\n        let status = unsafe { (flash_api().flash_get_property)(&self.config, property as u32, &mut value) };\n        check_status(status)?;\n        Ok(value)\n    }\n}\n\n// ---------------------------------------------------------------------------\n// embedded-storage trait implementations\n// ---------------------------------------------------------------------------\n\nimpl ErrorType for Flash {\n    type Error = Error;\n}\n\nimpl ReadNorFlash for Flash {\n    const READ_SIZE: usize = 1;\n\n    fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Self::Error> {\n        self.blocking_read(offset, bytes)\n    }\n\n    fn capacity(&self) -> usize {\n        FLASH_SIZE\n    }\n}\n\nimpl NorFlash for Flash {\n    const WRITE_SIZE: usize = PHRASE_SIZE;\n    const ERASE_SIZE: usize = SECTOR_SIZE;\n\n    fn erase(&mut self, from: u32, to: u32) -> Result<(), Self::Error> {\n        if to < from || to as usize > FLASH_SIZE {\n            return Err(Error::OutOfBounds);\n        }\n        if !(from as usize).is_multiple_of(Self::ERASE_SIZE) || !(to as usize).is_multiple_of(Self::ERASE_SIZE) {\n            return Err(Error::Unaligned);\n        }\n\n        // Erase one sector at a time\n        for sector_addr in (from..to).step_by(Self::ERASE_SIZE) {\n            self.blocking_erase(FLASH_BASE + sector_addr, SECTOR_SIZE as u32)?;\n        }\n        Ok(())\n    }\n\n    fn write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Self::Error> {\n        if offset as usize + bytes.len() > FLASH_SIZE {\n            return Err(Error::OutOfBounds);\n        }\n        if !(offset as usize).is_multiple_of(Self::WRITE_SIZE) || !bytes.len().is_multiple_of(Self::WRITE_SIZE) {\n            return Err(Error::Unaligned);\n        }\n\n        // Program one phrase at a time (16 bytes — the smallest write unit)\n        for (i, chunk) in bytes.chunks(PHRASE_SIZE).enumerate() {\n            let addr = FLASH_BASE + offset + (i * PHRASE_SIZE) as u32;\n            self.blocking_program_phrase(addr, chunk)?;\n        }\n        Ok(())\n    }\n}\n"
  },
  {
    "path": "embassy-mcxa/src/gpio.rs",
    "content": "//! GPIO driver built around a type-erased `Flex` pin, similar to other Embassy HALs.\n//! The exported `Output`/`Input` drivers own a `Flex` so they no longer depend on the\n//! concrete pin type.\n\nuse core::convert::Infallible;\nuse core::future::Future;\nuse core::marker::PhantomData;\nuse core::pin::pin;\n\nuse embassy_hal_internal::{Peri, PeripheralType};\nuse maitake_sync::WaitMap;\nuse paste::paste;\n\nuse crate::interrupt::typelevel::{Handler, Interrupt};\nuse crate::pac::common::{RW, Reg};\nuse crate::pac::gpio::vals::{Irqc, Isf, Pdd, Pid, Ptco, Ptso};\nuse crate::pac::port::regs::Pcr;\nuse crate::pac::port::vals::{Dse, Ibe, Inv, Mux, Ode, Pe, Ps, Sre};\n\nstruct BitIter(u32);\n\nimpl Iterator for BitIter {\n    type Item = usize;\n\n    fn next(&mut self) -> Option<Self::Item> {\n        match self.0.trailing_zeros() {\n            32 => None,\n            b => {\n                self.0 &= !(1 << b);\n                Some(b as usize)\n            }\n        }\n    }\n}\n\n#[cfg(feature = \"mcxa2xx\")]\nconst PORT_COUNT: usize = 5;\n#[cfg(feature = \"mcxa5xx\")]\nconst PORT_COUNT: usize = 6;\n\nstatic PORT_WAIT_MAPS: [WaitMap<usize, ()>; PORT_COUNT] = [const { WaitMap::new() }; PORT_COUNT];\n\n#[allow(private_bounds)]\npub trait Instance: SealedInstance + PeripheralType {\n    type Interrupt: Interrupt;\n}\n\nstruct Info {\n    pub port_index: usize,\n    pub gpio: crate::pac::gpio::Gpio,\n}\n\npub trait PeriGpioExt<'d, T: HasGpioInstance> {\n    /// Type erase the pin while also binding and Irq.\n    ///\n    /// This means the [`AnyPin`] can be used to constuct an async [`Input`] with [`Flex::async_from_anypin`]\n    /// and an async [`Flex`] with [`Flex::async_from_anypin`].\n    fn degrade_async(\n        self,\n        _irq: impl crate::interrupt::typelevel::Binding<<T::Instance as Instance>::Interrupt, InterruptHandler<T::Instance>>,\n    ) -> Peri<'d, AnyPin>;\n}\n\nimpl<'d, T: HasGpioInstance> PeriGpioExt<'d, T> for Peri<'d, T> {\n    /// Type erase the pin while also binding and Irq.\n    ///\n    /// This means the [`AnyPin`] can be used to constuct an async [`Input`] with [`Flex::async_from_anypin`]\n    /// and an async [`Flex`] with [`Flex::async_from_anypin`].\n    fn degrade_async(\n        self,\n        _irq: impl crate::interrupt::typelevel::Binding<<T::Instance as Instance>::Interrupt, InterruptHandler<T::Instance>>,\n    ) -> Peri<'d, AnyPin> {\n        HasGpioInstance::degrade_async(self, _irq)\n    }\n}\n\npub trait HasGpioInstance: GpioPin {\n    type Instance: Instance;\n\n    /// Type erase the pin while also binding and Irq.\n    ///\n    /// This means the [`AnyPin`] can be used to constuct an async [`Input`] with [`Flex::async_from_anypin`]\n    /// and an async [`Flex`] with [`Flex::async_from_anypin`].\n    fn degrade_async<'p>(\n        this: Peri<'p, Self>,\n        _irq: impl crate::interrupt::typelevel::Binding<\n            <Self::Instance as Instance>::Interrupt,\n            InterruptHandler<Self::Instance>,\n        >,\n    ) -> Peri<'p, AnyPin>;\n}\n\ntrait SealedInstance {\n    fn info() -> &'static Info;\n    const PERF_INT_INCR: fn();\n}\n\nmacro_rules! impl_instance {\n    ($($n:expr),*) => {\n        $(\n            paste!{\n                impl SealedInstance for crate::peripherals::[<GPIO $n>] {\n                    fn info() -> &'static Info {\n                        static INFO: Info =  Info {\n                            gpio: crate::pac::[<GPIO $n>],\n                            port_index: $n,\n                        };\n                        &INFO\n                    }\n                const PERF_INT_INCR: fn() = crate::perf_counters::[<incr_interrupt_gpio $n _wake>];\n                }\n\n                impl Instance for crate::peripherals::[<GPIO $n>] {\n                    type Interrupt = crate::interrupt::typelevel::[<GPIO $n>];\n                }\n            }\n        )*\n    };\n}\n\nimpl_instance!(0, 1, 2, 3, 4);\n#[cfg(feature = \"mcxa5xx\")]\nimpl_instance!(5);\n\npub struct InterruptHandler<T: Instance> {\n    _phantom: PhantomData<T>,\n}\n\nimpl<T: Instance> Handler<T::Interrupt> for InterruptHandler<T> {\n    unsafe fn on_interrupt() {\n        let info = T::info();\n        let isfr = info.gpio.isfr(0);\n\n        for pin in BitIter(isfr.read().0) {\n            // Clear all pending interrupts\n            isfr.write(|w| w.0 = 1 << pin);\n            info.gpio.icr(pin).modify(|w| w.set_irqc(Irqc::IRQC0)); // Disable interrupt\n\n            // Wake the corresponding port waker\n            if let Some(w) = PORT_WAIT_MAPS.get(info.port_index) {\n                T::PERF_INT_INCR();\n                w.wake(&pin, ());\n            }\n        }\n    }\n}\n\n/// Open-drain for GPIO pins.\n#[derive(Copy, Clone, Eq, PartialEq, Debug)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub enum OpenDrain {\n    /// Output is push-pull (not open-drain)\n    No,\n    /// Output is open-drain\n    Yes,\n}\n\nimpl From<OpenDrain> for Ode {\n    fn from(open_drain: OpenDrain) -> Self {\n        match open_drain {\n            OpenDrain::No => Ode::ODE0,\n            OpenDrain::Yes => Ode::ODE1,\n        }\n    }\n}\n\n/// Logical level for GPIO pins.\n#[derive(Copy, Clone, Eq, PartialEq, Debug)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub enum Level {\n    Low,\n    High,\n}\n\nimpl From<bool> for Level {\n    fn from(val: bool) -> Self {\n        match val {\n            true => Self::High,\n            false => Self::Low,\n        }\n    }\n}\n\n#[derive(Copy, Clone, Eq, PartialEq, Debug)]\npub enum Pull {\n    Disabled,\n    Up,\n    Down,\n}\n\nimpl From<Pull> for (Pe, Ps) {\n    fn from(pull: Pull) -> Self {\n        match pull {\n            Pull::Disabled => (Pe::PE0, Ps::PS0),\n            Pull::Up => (Pe::PE1, Ps::PS1),\n            Pull::Down => (Pe::PE1, Ps::PS0),\n        }\n    }\n}\n\n#[derive(Copy, Clone, Eq, PartialEq, Debug)]\npub enum SlewRate {\n    Fast,\n    Slow,\n}\n\nimpl From<SlewRate> for Sre {\n    fn from(slew_rate: SlewRate) -> Self {\n        match slew_rate {\n            SlewRate::Fast => Sre::SRE0,\n            SlewRate::Slow => Sre::SRE1,\n        }\n    }\n}\n\n#[derive(Copy, Clone, Eq, PartialEq, Debug)]\npub enum DriveStrength {\n    Normal,\n    Double,\n}\n\nimpl From<DriveStrength> for Dse {\n    fn from(strength: DriveStrength) -> Self {\n        match strength {\n            DriveStrength::Normal => Dse::DSE0,\n            DriveStrength::Double => Dse::DSE1,\n        }\n    }\n}\n\n#[derive(Copy, Clone, Eq, PartialEq, Debug)]\npub enum Inverter {\n    Disabled,\n    Enabled,\n}\n\nimpl From<Inverter> for Inv {\n    fn from(strength: Inverter) -> Self {\n        match strength {\n            Inverter::Disabled => Inv::INV0,\n            Inverter::Enabled => Inv::INV1,\n        }\n    }\n}\n\npub type Gpio = crate::peripherals::GPIO0;\n\n/// Type-erased representation of a GPIO pin.\npub struct AnyPin {\n    port: u8,\n    pin: u8,\n    gpio: crate::pac::gpio::Gpio,\n    port_reg: crate::pac::port::Port,\n    pcr_reg: Reg<Pcr, RW>,\n    irq_bound: bool,\n}\n\nimpl AnyPin {\n    /// Create an `AnyPin` from raw components.\n    pub(crate) fn new(\n        port: u8,\n        pin: u8,\n        gpio: crate::pac::gpio::Gpio,\n        port_reg: crate::pac::port::Port,\n        pcr_reg: Reg<Pcr, RW>,\n        irq_bound: bool,\n    ) -> Self {\n        Self {\n            port,\n            pin,\n            gpio,\n            port_reg,\n            pcr_reg,\n            irq_bound,\n        }\n    }\n\n    #[inline(always)]\n    fn gpio(&self) -> crate::pac::gpio::Gpio {\n        self.gpio\n    }\n\n    #[inline(always)]\n    pub fn port_index(&self) -> u8 {\n        self.port\n    }\n\n    #[inline(always)]\n    pub fn pin_index(&self) -> u8 {\n        self.pin\n    }\n\n    #[inline(always)]\n    fn port_reg(&self) -> crate::pac::port::Port {\n        self.port_reg\n    }\n\n    #[inline(always)]\n    fn pcr_reg(&self) -> Reg<Pcr, RW> {\n        self.pcr_reg\n    }\n\n    #[inline(always)]\n    fn irq_bound(&self) -> bool {\n        self.irq_bound\n    }\n}\n\nembassy_hal_internal::impl_peripheral!(AnyPin);\n\npub(crate) trait SealedPin {\n    fn port(&self) -> u8;\n\n    fn pin(&self) -> u8;\n\n    fn gpio(&self) -> crate::pac::gpio::Gpio;\n\n    fn port_reg(&self) -> crate::pac::port::Port;\n\n    fn pcr_reg(&self) -> Reg<Pcr, RW>;\n\n    fn set_function(&self, function: Mux);\n\n    fn set_pull(&self, pull: Pull);\n\n    fn set_drive_strength(&self, strength: Dse);\n\n    fn set_slew_rate(&self, slew_rate: Sre);\n\n    fn set_enable_input_buffer(&self, buffer_enabled: bool);\n\n    fn set_as_disabled(&self);\n}\n\n/// GPIO pin trait.\n#[allow(private_bounds)]\npub trait GpioPin: SealedPin + Sized + PeripheralType + Into<AnyPin> + 'static {\n    /// Type-erase the pin.\n    fn degrade(self) -> AnyPin {\n        // SAFETY: This is only called within the GpioPin trait, which is only\n        // implemented within this module on valid pin peripherals and thus\n        // has been verified to be correct.\n        AnyPin::new(\n            self.port(),\n            self.pin(),\n            self.gpio(),\n            self.port_reg(),\n            self.pcr_reg(),\n            false,\n        )\n    }\n}\n\nimpl SealedPin for AnyPin {\n    #[inline(always)]\n    fn pin(&self) -> u8 {\n        self.pin_index()\n    }\n\n    #[inline(always)]\n    fn port(&self) -> u8 {\n        self.port_index()\n    }\n\n    #[inline(always)]\n    fn gpio(&self) -> crate::pac::gpio::Gpio {\n        self.gpio()\n    }\n\n    #[inline(always)]\n    fn port_reg(&self) -> crate::pac::port::Port {\n        self.port_reg()\n    }\n\n    #[inline(always)]\n    fn pcr_reg(&self) -> Reg<Pcr, RW> {\n        self.pcr_reg()\n    }\n\n    #[inline(always)]\n    fn set_function(&self, function: Mux) {\n        self.pcr_reg().modify(|w| w.set_mux(function));\n    }\n\n    #[inline(always)]\n    fn set_pull(&self, pull: Pull) {\n        let (pull_enable, pull_select) = pull.into();\n        self.pcr_reg().modify(|w| {\n            w.set_pe(pull_enable);\n            w.set_ps(pull_select)\n        });\n    }\n\n    #[inline(always)]\n    fn set_drive_strength(&self, strength: Dse) {\n        self.pcr_reg().modify(|w| w.set_dse(strength));\n    }\n\n    #[inline(always)]\n    fn set_slew_rate(&self, slew_rate: Sre) {\n        self.pcr_reg().modify(|w| w.set_sre(slew_rate));\n    }\n\n    #[inline(always)]\n    fn set_enable_input_buffer(&self, buffer_enabled: bool) {\n        self.pcr_reg()\n            .modify(|w| w.set_ibe(if buffer_enabled { Ibe::IBE1 } else { Ibe::IBE0 }));\n    }\n\n    #[inline(always)]\n    fn set_as_disabled(&self) {\n        // Set GPIO direction as input\n        self.gpio().pddr().modify(|w| w.set_pdd(self.pin() as usize, Pdd::PDD0));\n        // Set input buffer as disabled\n        self.set_enable_input_buffer(false);\n        // Set mode as GPIO (vs other potential functions)\n        self.set_function(Mux::MUX0);\n        // Set pin as disabled\n        self.gpio().pidr().modify(|w| w.set_pid(self.pin() as usize, Pid::PID1));\n    }\n}\n\nimpl GpioPin for AnyPin {}\n\n#[doc(hidden)]\n#[macro_export]\nmacro_rules! impl_pin {\n    ($peri:ident, $port:expr, $pin:expr, $block:ident) => {\n        ::paste::paste! {\n            impl SealedPin for $crate::peripherals::$peri {\n                #[inline(always)]\n                fn port(&self) -> u8 {\n                    $port\n                }\n\n                #[inline(always)]\n                fn pin(&self) -> u8 {\n                    $pin\n                }\n\n                #[inline(always)]\n                fn gpio(&self) -> crate::pac::gpio::Gpio {\n                    crate::pac::$block\n                }\n\n                #[inline(always)]\n                fn port_reg(&self) -> crate::pac::port::Port {\n                    crate::pac::[<PORT $port>]\n                }\n\n                #[inline(always)]\n                fn pcr_reg(&self) -> Reg<Pcr, RW> {\n                    self.port_reg().pcr($pin)\n                }\n\n                #[inline(always)]\n                fn set_function(&self, function: Mux) {\n                    self.pcr_reg().modify(|w| w.set_mux(function));\n                }\n\n                #[inline(always)]\n                fn set_pull(&self, pull: Pull) {\n                    let (pull_enable, pull_select) = pull.into();\n                    self.pcr_reg().modify(|w| {\n                        w.set_pe(pull_enable);\n                        w.set_ps(pull_select);\n                    });\n                }\n\n                #[inline(always)]\n                fn set_drive_strength(&self, strength: Dse) {\n                    self.pcr_reg().modify(|w| w.set_dse(strength));\n                }\n\n                #[inline(always)]\n                fn set_slew_rate(&self, slew_rate: Sre) {\n                    self.pcr_reg().modify(|w| w.set_sre(slew_rate));\n                }\n\n                #[inline(always)]\n                fn set_enable_input_buffer(&self, buffer_enabled: bool) {\n                    self.pcr_reg().modify(|w| w.set_ibe(if buffer_enabled { Ibe::IBE1 } else { Ibe::IBE0 }));\n                }\n\n                #[inline(always)]\n                fn set_as_disabled(&self) {\n                    // Set GPIO direction as input\n                    self.gpio().pddr().modify(|w| w.set_pdd(self.pin() as usize, Pdd::PDD0));\n                    // Set input buffer as disabled\n                    self.set_enable_input_buffer(false);\n                    // Set mode as GPIO (vs other potential functions)\n                    self.set_function(Mux::MUX0);\n                    // Set pin as disabled\n                    self.gpio().pidr().modify(|w| w.set_pid(self.pin() as usize, Pid::PID1));\n                }\n            }\n\n            impl GpioPin for crate::peripherals::$peri {}\n\n            impl From<crate::peripherals::$peri> for AnyPin {\n                fn from(value: crate::peripherals::$peri) -> Self {\n                    value.degrade()\n                }\n            }\n\n            impl crate::peripherals::$peri {\n                /// Convenience helper to obtain a type-erased handle to this pin.\n                pub fn degrade(&self) -> AnyPin {\n                    AnyPin::new(\n                        self.port(),\n                        self.pin(),\n                        self.gpio(),\n                        self.port_reg(),\n                        self.pcr_reg(),\n                        false,\n                    )\n                }\n            }\n\n            impl crate::gpio::HasGpioInstance for crate::peripherals::$peri {\n                type Instance = crate::peripherals::$block;\n                fn degrade_async<'p>(\n                    this: embassy_hal_internal::Peri<'p, Self>,\n                    _irq: impl crate::interrupt::typelevel::Binding<\n                        <Self::Instance as crate::gpio::Instance>::Interrupt,\n                        crate::gpio::InterruptHandler<Self::Instance>,\n                    >,\n                ) -> embassy_hal_internal::Peri<'p, AnyPin> {\n                    use crate::interrupt::typelevel::Interrupt;\n                    unsafe {\n                        <<Self as crate::gpio::HasGpioInstance>::Instance as crate::gpio::Instance>::Interrupt::enable();\n                    }\n                    unsafe {\n                        embassy_hal_internal::Peri::new_unchecked(AnyPin::new(\n                            this.port(),\n                            this.pin(),\n                            this.gpio(),\n                            this.port_reg(),\n                            this.pcr_reg(),\n                            true,\n                        ))\n                    }\n                }\n            }\n        }\n    };\n}\n\nmod sealed {\n    pub trait Sealed {}\n}\npub trait Mode: sealed::Sealed {}\n\npub struct Async {}\nimpl sealed::Sealed for Async {}\nimpl Mode for Async {}\n\npub struct Blocking {}\nimpl sealed::Sealed for Blocking {}\nimpl Mode for Blocking {}\n\n/// A flexible pin that can be configured as input or output.\npub struct Flex<'d, M: Mode = Blocking> {\n    pin: Peri<'d, AnyPin>,\n    _phantom: PhantomData<&'d mut M>,\n}\n\nimpl<'d> Flex<'d> {\n    /// Wrap the pin in a `Flex`.\n    ///\n    /// The pin remains unmodified. The initial output level is unspecified, but\n    /// can be changed before the pin is put into output mode.\n    pub fn new(pin: Peri<'d, impl GpioPin>) -> Self {\n        pin.set_function(Mux::MUX0);\n        Self {\n            pin: pin.into(),\n            _phantom: PhantomData,\n        }\n    }\n}\n\nimpl<'d, M: Mode> Flex<'d, M> {\n    #[inline]\n    fn gpio(&self) -> crate::pac::gpio::Gpio {\n        self.pin.gpio()\n    }\n\n    /// Put the pin into input mode.\n    pub fn set_as_input(&mut self) {\n        self.set_enable_input_buffer(true);\n        self.gpio()\n            .pddr()\n            .modify(|w| w.set_pdd(self.pin.pin_index() as usize, Pdd::PDD0));\n    }\n\n    /// Put the pin into output mode.\n    pub fn set_as_output(&mut self) {\n        self.set_pull(Pull::Disabled);\n        self.gpio()\n            .pddr()\n            .modify(|w| w.set_pdd(self.pin.pin_index() as usize, Pdd::PDD1));\n    }\n\n    /// Set output level to High.\n    #[inline]\n    pub fn set_high(&mut self) {\n        self.gpio()\n            .psor()\n            .write(|w| w.set_ptso(self.pin.pin_index() as usize, Ptso::PTSO1));\n    }\n\n    /// Set output level to Low.\n    #[inline]\n    pub fn set_low(&mut self) {\n        self.gpio()\n            .pcor()\n            .write(|w| w.set_ptco(self.pin.pin_index() as usize, Ptco::PTCO1));\n    }\n\n    /// Set output level to the given `Level`.\n    #[inline]\n    pub fn set_level(&mut self, level: Level) {\n        match level {\n            Level::High => self.set_high(),\n            Level::Low => self.set_low(),\n        }\n    }\n\n    /// Toggle output level.\n    #[inline]\n    pub fn toggle(&mut self) {\n        self.gpio()\n            .ptor()\n            .write(|w| w.set_ptto(self.pin.pin_index() as usize, true));\n    }\n\n    /// Get whether the pin input level is high.\n    #[inline]\n    pub fn is_high(&self) -> bool {\n        self.gpio().pdir().read().pdi(self.pin.pin_index() as usize)\n    }\n\n    /// Get whether the pin input level is low.\n    #[inline]\n    pub fn is_low(&self) -> bool {\n        !self.is_high()\n    }\n\n    /// Is the output pin set as high?\n    #[inline]\n    pub fn is_set_high(&self) -> bool {\n        #[cfg(feature = \"mcxa2xx\")]\n        let set = self.gpio().pdor().read().pdo(self.pin.pin_index() as usize);\n        #[cfg(feature = \"mcxa5xx\")]\n        let set = (self.gpio().pdor().read().0 & (1 << self.pin.pin_index())) != 0;\n        set\n    }\n\n    /// Is the output pin set as low?\n    #[inline]\n    pub fn is_set_low(&self) -> bool {\n        !self.is_set_high()\n    }\n\n    /// Configure open-drain output.\n    #[inline]\n    pub fn set_open_drain(&mut self, open_drain: OpenDrain) {\n        self.pin.pcr_reg().modify(|w| w.set_ode(open_drain.into()));\n    }\n\n    /// Configure the input logic inversion of this pin.\n    #[inline]\n    pub fn set_input_inversion(&mut self, invert: Inverter) {\n        self.pin.pcr_reg().modify(|w| w.set_inv(invert.into()));\n    }\n\n    /// Configure the pin pull up/down level.\n    pub fn set_pull(&mut self, pull_select: Pull) {\n        self.pin.set_pull(pull_select);\n    }\n\n    /// Configure the pin drive strength.\n    pub fn set_drive_strength(&mut self, strength: DriveStrength) {\n        self.pin.set_drive_strength(strength.into());\n    }\n\n    /// Configure the pin slew rate.\n    pub fn set_slew_rate(&mut self, slew_rate: SlewRate) {\n        self.pin.set_slew_rate(slew_rate.into());\n    }\n\n    /// Enable input buffer for the pin.\n    pub fn set_enable_input_buffer(&mut self, buffer_enabled: bool) {\n        self.pin.set_enable_input_buffer(buffer_enabled);\n    }\n\n    /// Get pin level.\n    pub fn get_level(&self) -> Level {\n        self.is_high().into()\n    }\n}\n\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub struct NoIrqBound;\n\n/// Async methods\nimpl<'d> Flex<'d, Async> {\n    /// Wrap the pin in Flex with Async support.\n    ///\n    /// This enables the use of async functions like: [`Flex::wait_for_high`] and [`Flex::wait_for_falling_edge`].\n    pub fn new_async<P>(\n        pin: Peri<'d, P>,\n        _irq: impl crate::interrupt::typelevel::Binding<<P::Instance as Instance>::Interrupt, InterruptHandler<P::Instance>>,\n    ) -> Self\n    where\n        P: GpioPin + HasGpioInstance,\n    {\n        pin.set_function(Mux::MUX0);\n        unsafe {\n            <P::Instance as Instance>::Interrupt::enable();\n        }\n        Self {\n            pin: pin.into(),\n            _phantom: PhantomData,\n        }\n    }\n\n    /// Wrap an [`AnyPin`] in Flex with Async support.\n    ///\n    /// This enables the use of async functions like: [`Input::wait_for_high`] and [`Input::wait_for_falling_edge`].\n    /// In order to use an [`AnyPin`] with this function it needs to be constructed by\n    /// calling [`PeriGpioExt::degrade_async`] on the pin to bind the Irq.\n    /// If an [`AnyPin`] is provided that was not constucted with [`PeriGpioExt::degrade_async`],\n    /// it will return the error: [`NoIrqBound`].\n    pub fn async_from_anypin(pin: Peri<'d, AnyPin>) -> Result<Self, NoIrqBound> {\n        pin.set_function(Mux::MUX0);\n        if pin.irq_bound() {\n            Ok(Self {\n                pin: pin.into(),\n                _phantom: PhantomData,\n            })\n        } else {\n            Err(NoIrqBound)\n        }\n    }\n\n    /// Helper function that waits for a given interrupt trigger\n    async fn wait_for_inner(&mut self, level: crate::pac::gpio::vals::Irqc) {\n        // First, ensure that we have a waker that is ready for this port+pin\n        let w = PORT_WAIT_MAPS[usize::from(self.pin.port)].wait(self.pin.pin.into());\n        let mut w = pin!(w);\n        // Wait for the subscription to occur, which requires polling at least once\n        //\n        // This function returns a result, but can only be an Err if:\n        //\n        // * We call `.close()` on a WaitMap, which we never do\n        // * We have a duplicate key, which can't happen because `wait_for_*` methods\n        //   take an &mut ref of their unique port+pin combo\n        //\n        // So we wait for it to complete, but ignore the result.\n        _ = w.as_mut().subscribe().await;\n\n        // Now that our waker is in the map, we can enable the appropriate interrupt\n        //\n        // Clear any existing pending interrupt on this pin\n        self.pin.gpio().isfr(0).write(|w| w.0 = 1 << self.pin.pin());\n        self.pin\n            .gpio()\n            .icr(self.pin.pin().into())\n            .write(|w| w.set_isf(Isf::ISF1));\n\n        // Pin interrupt configuration\n        self.pin.gpio().icr(self.pin.pin().into()).modify(|w| w.set_irqc(level));\n\n        // Finally, we can await the matching call to `.wake()` from the interrupt.\n        //\n        // Again, technically, this could return a result, but for the same reasons\n        // as above, this can't be an error in our case, so just wait for it to complete\n        _ = w.await;\n    }\n\n    /// Wait until the pin is high. If it is already high, return immediately.\n    #[inline]\n    pub fn wait_for_high(&mut self) -> impl Future<Output = ()> + use<'_, 'd> {\n        self.wait_for_inner(Irqc::IRQC12)\n    }\n\n    /// Wait until the pin is low. If it is already low, return immediately.\n    #[inline]\n    pub fn wait_for_low(&mut self) -> impl Future<Output = ()> + use<'_, 'd> {\n        self.wait_for_inner(Irqc::IRQC8)\n    }\n\n    /// Wait for the pin to undergo a transition from low to high.\n    #[inline]\n    pub fn wait_for_rising_edge(&mut self) -> impl Future<Output = ()> + use<'_, 'd> {\n        self.wait_for_inner(Irqc::IRQC9)\n    }\n\n    /// Wait for the pin to undergo a transition from high to low.\n    #[inline]\n    pub fn wait_for_falling_edge(&mut self) -> impl Future<Output = ()> + use<'_, 'd> {\n        self.wait_for_inner(Irqc::IRQC10)\n    }\n\n    /// Wait for the pin to undergo any transition, i.e low to high OR high to low.\n    #[inline]\n    pub fn wait_for_any_edge(&mut self) -> impl Future<Output = ()> + use<'_, 'd> {\n        self.wait_for_inner(Irqc::IRQC11)\n    }\n}\n\nimpl<'d, M: Mode> Drop for Flex<'d, M> {\n    #[inline]\n    fn drop(&mut self) {\n        self.pin.set_as_disabled();\n    }\n}\n\n/// GPIO output driver that owns a `Flex` pin.\npub struct Output<'d> {\n    flex: Flex<'d>,\n}\n\nimpl<'d> Output<'d> {\n    /// Create a GPIO output driver for a [GpioPin] with the provided [Level].\n    pub fn new(pin: Peri<'d, impl GpioPin>, initial: Level, strength: DriveStrength, slew_rate: SlewRate) -> Self {\n        let mut flex = Flex::new(pin);\n        flex.set_level(initial);\n        flex.set_as_output();\n        flex.set_drive_strength(strength);\n        flex.set_slew_rate(slew_rate);\n        flex.set_open_drain(OpenDrain::No);\n        Self { flex }\n    }\n\n    /// Set the output as high.\n    #[inline]\n    pub fn set_high(&mut self) {\n        self.flex.set_high();\n    }\n\n    /// Set the output as low.\n    #[inline]\n    pub fn set_low(&mut self) {\n        self.flex.set_low();\n    }\n\n    /// Set the output level.\n    #[inline]\n    pub fn set_level(&mut self, level: Level) {\n        self.flex.set_level(level);\n    }\n\n    /// Toggle the output level.\n    #[inline]\n    pub fn toggle(&mut self) {\n        self.flex.toggle();\n    }\n\n    /// Is the output pin set as high?\n    #[inline]\n    pub fn is_set_high(&self) -> bool {\n        self.flex.is_set_high()\n    }\n\n    /// Is the output pin set as low?\n    #[inline]\n    pub fn is_set_low(&self) -> bool {\n        self.flex.is_set_low()\n    }\n\n    /// Expose the inner `Flex` if callers need to reconfigure the pin.\n    #[inline]\n    pub fn into_flex(self) -> Flex<'d> {\n        self.flex\n    }\n\n    /// Convert this output pin into an open-drain output pin.\n    #[inline]\n    pub fn into_open_drain(mut self) -> OutputOpenDrain<'d> {\n        self.flex.set_open_drain(OpenDrain::Yes);\n        OutputOpenDrain { flex: self.flex }\n    }\n}\n\n/// GPIO output open-drain driver that owns a `Flex` pin.\npub struct OutputOpenDrain<'d> {\n    flex: Flex<'d>,\n}\n\nimpl<'d> OutputOpenDrain<'d> {\n    /// Create a GPIO output open-drain driver for a [GpioPin] with the provided [Level].\n    pub fn new(pin: Peri<'d, impl GpioPin>, initial: Level, strength: DriveStrength, slew_rate: SlewRate) -> Self {\n        let mut flex = Flex::new(pin);\n        flex.set_level(initial);\n        flex.set_as_output();\n        flex.set_drive_strength(strength);\n        flex.set_slew_rate(slew_rate);\n        flex.set_enable_input_buffer(true);\n        flex.set_open_drain(OpenDrain::Yes);\n        Self { flex }\n    }\n\n    /// Get whether the pin level is high.\n    #[inline]\n    pub fn is_high(&self) -> bool {\n        self.flex.is_high()\n    }\n\n    /// Get whether the pin level is low.\n    #[inline]\n    pub fn is_low(&self) -> bool {\n        self.flex.is_low()\n    }\n\n    /// Set the output as high (open-drain high is just letting go of the line).\n    #[inline]\n    pub fn set_high(&mut self) {\n        self.flex.set_high();\n    }\n\n    /// Set the output as low (open-drain low is driving the line low).\n    #[inline]\n    pub fn set_low(&mut self) {\n        self.flex.set_low();\n    }\n\n    /// Set the output level.\n    #[inline]\n    pub fn set_level(&mut self, level: Level) {\n        self.flex.set_level(level);\n    }\n\n    /// Get the pin level.\n    pub fn get_level(&self) -> Level {\n        self.flex.get_level()\n    }\n\n    /// Toggle the output level.\n    #[inline]\n    pub fn toggle(&mut self) {\n        if self.flex.is_set_low() {\n            self.set_high();\n        } else {\n            self.set_low();\n        }\n    }\n\n    /// Configure the input logic inversion of this pin.\n    #[inline]\n    pub fn set_inversion(&mut self, invert: Inverter) {\n        self.flex.set_input_inversion(invert)\n    }\n\n    /// Expose the inner `Flex` if callers need to reconfigure the pin.\n    #[inline]\n    pub fn into_flex(self) -> Flex<'d> {\n        self.flex\n    }\n\n    /// Convert this output pin into an push-pull output pin.\n    #[inline]\n    pub fn into_push_pull(mut self) -> Output<'d> {\n        self.flex.set_open_drain(OpenDrain::No);\n        Output { flex: self.flex }\n    }\n}\n\n/// GPIO input driver that owns a `Flex` pin.\npub struct Input<'d, M: Mode = Blocking> {\n    flex: Flex<'d, M>,\n}\n\nimpl<'d> Input<'d> {\n    /// Create a GPIO input driver for a [GpioPin].\n    ///\n    pub fn new(pin: Peri<'d, impl GpioPin>, pull_select: Pull) -> Self {\n        let mut flex = Flex::new(pin);\n        flex.set_as_input();\n        flex.set_pull(pull_select);\n        Self { flex }\n    }\n}\n\nimpl<'d, M: Mode> Input<'d, M> {\n    /// Get whether the pin input level is high.\n    #[inline]\n    pub fn is_high(&self) -> bool {\n        self.flex.is_high()\n    }\n\n    /// Get whether the pin input level is low.\n    #[inline]\n    pub fn is_low(&self) -> bool {\n        self.flex.is_low()\n    }\n\n    /// Expose the inner `Flex` if callers need to reconfigure the pin.\n    ///\n    /// Since Drive Strength and Slew Rate are not set when creating the Input\n    /// pin, they need to be set when converting\n    #[inline]\n    pub fn into_flex(mut self, strength: DriveStrength, slew_rate: SlewRate) -> Flex<'d, M> {\n        self.flex.set_drive_strength(strength);\n        self.flex.set_slew_rate(slew_rate);\n        self.flex\n    }\n\n    /// Configure the input logic inversion of this pin.\n    #[inline]\n    pub fn set_inversion(&mut self, invert: Inverter) {\n        self.flex.set_input_inversion(invert)\n    }\n\n    /// Get the pin level.\n    pub fn get_level(&self) -> Level {\n        self.flex.get_level()\n    }\n}\n\n/// Async methods\nimpl<'d> Input<'d, Async> {\n    /// Create a GPIO input driver for a [GpioPin] with async support.\n    ///\n    /// This enables the use of async functions like: [`Input::wait_for_high`] and [`Input::wait_for_falling_edge`].\n    pub fn new_async<P>(\n        pin: Peri<'d, P>,\n        irq: impl crate::interrupt::typelevel::Binding<<P::Instance as Instance>::Interrupt, InterruptHandler<P::Instance>>\n        + 'd,\n        pull_select: Pull,\n    ) -> Self\n    where\n        P: GpioPin + HasGpioInstance,\n    {\n        let mut flex = Flex::new_async(pin, irq);\n        flex.set_as_input();\n        flex.set_pull(pull_select);\n        Self { flex }\n    }\n\n    /// Create a GPIO input driver for a [GpioPin] with async support from an [`AnyPin`].\n    ///\n    /// This enables the use of async functions like: [`Input::wait_for_high`] and [`Input::wait_for_falling_edge`].\n    /// In order to use an [`AnyPin`] with this function it needs to be constructed by\n    /// calling [`PeriGpioExt::degrade_async`] on the pin to bind the Irq.\n    /// If an [`AnyPin`] is provided that was not constucted with [`PeriGpioExt::degrade_async`],\n    /// it will return the error: [`NoIrqBound`].\n    pub fn async_from_anypin(pin: Peri<'d, AnyPin>, pull_select: Pull) -> Result<Self, NoIrqBound> {\n        let mut flex = Flex::async_from_anypin(pin)?;\n        flex.set_as_input();\n        flex.set_pull(pull_select);\n        Ok(Self { flex })\n    }\n\n    /// Wait until the pin is high. If it is already high, return immediately.\n    #[inline]\n    pub fn wait_for_high(&mut self) -> impl Future<Output = ()> + use<'_, 'd> {\n        self.flex.wait_for_high()\n    }\n\n    /// Wait until the pin is low. If it is already low, return immediately.\n    #[inline]\n    pub fn wait_for_low(&mut self) -> impl Future<Output = ()> + use<'_, 'd> {\n        self.flex.wait_for_low()\n    }\n\n    /// Wait for the pin to undergo a transition from low to high.\n    #[inline]\n    pub fn wait_for_rising_edge(&mut self) -> impl Future<Output = ()> + use<'_, 'd> {\n        self.flex.wait_for_rising_edge()\n    }\n\n    /// Wait for the pin to undergo a transition from high to low.\n    #[inline]\n    pub fn wait_for_falling_edge(&mut self) -> impl Future<Output = ()> + use<'_, 'd> {\n        self.flex.wait_for_falling_edge()\n    }\n\n    /// Wait for the pin to undergo any transition, i.e low to high OR high to low.\n    #[inline]\n    pub fn wait_for_any_edge(&mut self) -> impl Future<Output = ()> + use<'_, 'd> {\n        self.flex.wait_for_any_edge()\n    }\n}\n\nimpl embedded_hal_async::digital::Wait for Input<'_, Async> {\n    async fn wait_for_high(&mut self) -> Result<(), Self::Error> {\n        self.wait_for_high().await;\n        Ok(())\n    }\n\n    async fn wait_for_low(&mut self) -> Result<(), Self::Error> {\n        self.wait_for_low().await;\n        Ok(())\n    }\n\n    async fn wait_for_rising_edge(&mut self) -> Result<(), Self::Error> {\n        self.wait_for_rising_edge().await;\n        Ok(())\n    }\n\n    async fn wait_for_falling_edge(&mut self) -> Result<(), Self::Error> {\n        self.wait_for_falling_edge().await;\n        Ok(())\n    }\n\n    async fn wait_for_any_edge(&mut self) -> Result<(), Self::Error> {\n        self.wait_for_any_edge().await;\n        Ok(())\n    }\n}\n\nimpl embedded_hal_async::digital::Wait for Flex<'_, Async> {\n    async fn wait_for_high(&mut self) -> Result<(), Self::Error> {\n        self.wait_for_high().await;\n        Ok(())\n    }\n\n    async fn wait_for_low(&mut self) -> Result<(), Self::Error> {\n        self.wait_for_low().await;\n        Ok(())\n    }\n\n    async fn wait_for_rising_edge(&mut self) -> Result<(), Self::Error> {\n        self.wait_for_rising_edge().await;\n        Ok(())\n    }\n\n    async fn wait_for_falling_edge(&mut self) -> Result<(), Self::Error> {\n        self.wait_for_falling_edge().await;\n        Ok(())\n    }\n\n    async fn wait_for_any_edge(&mut self) -> Result<(), Self::Error> {\n        self.wait_for_any_edge().await;\n        Ok(())\n    }\n}\n\n// Both embedded_hal 0.2 and 1.0 must be supported by embassy HALs.\nimpl<M: Mode> embedded_hal_02::digital::v2::InputPin for Flex<'_, M> {\n    // GPIO operations on this block cannot fail, therefor we set the error type\n    // to Infallible to guarantee that we can only produce Ok variants.\n    type Error = Infallible;\n\n    #[inline]\n    fn is_high(&self) -> Result<bool, Self::Error> {\n        Ok(self.is_high())\n    }\n\n    #[inline]\n    fn is_low(&self) -> Result<bool, Self::Error> {\n        Ok(self.is_low())\n    }\n}\n\nimpl<M: Mode> embedded_hal_02::digital::v2::InputPin for Input<'_, M> {\n    type Error = Infallible;\n\n    #[inline]\n    fn is_high(&self) -> Result<bool, Self::Error> {\n        Ok(self.is_high())\n    }\n\n    #[inline]\n    fn is_low(&self) -> Result<bool, Self::Error> {\n        Ok(self.is_low())\n    }\n}\n\nimpl embedded_hal_02::digital::v2::OutputPin for Output<'_> {\n    type Error = Infallible;\n\n    #[inline]\n    fn set_high(&mut self) -> Result<(), Self::Error> {\n        self.set_high();\n        Ok(())\n    }\n\n    #[inline]\n    fn set_low(&mut self) -> Result<(), Self::Error> {\n        self.set_low();\n        Ok(())\n    }\n}\n\nimpl<M: Mode> embedded_hal_02::digital::v2::OutputPin for Flex<'_, M> {\n    type Error = Infallible;\n\n    #[inline]\n    fn set_high(&mut self) -> Result<(), Self::Error> {\n        self.set_high();\n        Ok(())\n    }\n\n    #[inline]\n    fn set_low(&mut self) -> Result<(), Self::Error> {\n        self.set_low();\n        Ok(())\n    }\n}\n\nimpl<M: Mode> embedded_hal_02::digital::v2::StatefulOutputPin for Flex<'_, M> {\n    #[inline]\n    fn is_set_high(&self) -> Result<bool, Self::Error> {\n        Ok(self.is_set_high())\n    }\n\n    #[inline]\n    fn is_set_low(&self) -> Result<bool, Self::Error> {\n        Ok(self.is_set_low())\n    }\n}\n\nimpl<M: Mode> embedded_hal_02::digital::v2::ToggleableOutputPin for Flex<'_, M> {\n    type Error = Infallible;\n\n    #[inline]\n    fn toggle(&mut self) -> Result<(), Self::Error> {\n        self.toggle();\n        Ok(())\n    }\n}\n\nimpl<M: Mode> embedded_hal_1::digital::ErrorType for Flex<'_, M> {\n    type Error = Infallible;\n}\n\nimpl<M: Mode> embedded_hal_1::digital::ErrorType for Input<'_, M> {\n    type Error = Infallible;\n}\n\nimpl embedded_hal_1::digital::ErrorType for Output<'_> {\n    type Error = Infallible;\n}\n\nimpl<M: Mode> embedded_hal_1::digital::InputPin for Input<'_, M> {\n    #[inline]\n    fn is_high(&mut self) -> Result<bool, Self::Error> {\n        Ok((*self).is_high())\n    }\n\n    #[inline]\n    fn is_low(&mut self) -> Result<bool, Self::Error> {\n        Ok((*self).is_low())\n    }\n}\n\nimpl embedded_hal_1::digital::OutputPin for Output<'_> {\n    #[inline]\n    fn set_high(&mut self) -> Result<(), Self::Error> {\n        self.set_high();\n        Ok(())\n    }\n\n    #[inline]\n    fn set_low(&mut self) -> Result<(), Self::Error> {\n        self.set_low();\n        Ok(())\n    }\n}\n\nimpl<M: Mode> embedded_hal_1::digital::OutputPin for Flex<'_, M> {\n    #[inline]\n    fn set_high(&mut self) -> Result<(), Self::Error> {\n        self.set_high();\n        Ok(())\n    }\n\n    #[inline]\n    fn set_low(&mut self) -> Result<(), Self::Error> {\n        self.set_low();\n        Ok(())\n    }\n}\n\nimpl<M: Mode> embedded_hal_1::digital::StatefulOutputPin for Flex<'_, M> {\n    #[inline]\n    fn is_set_high(&mut self) -> Result<bool, Self::Error> {\n        Ok((*self).is_set_high())\n    }\n\n    #[inline]\n    fn is_set_low(&mut self) -> Result<bool, Self::Error> {\n        Ok((*self).is_set_low())\n    }\n}\n"
  },
  {
    "path": "embassy-mcxa/src/i2c/controller.rs",
    "content": "//! # LPI2C Controller Driver\n//!\n//! This module provides a driver for the Low-Power Inter-Integrated\n//! Circuit (LPI2C) controller, supporting blocking,\n//! interrupt-only async, and DMA async modes of operation.\n//!\n//! The driver support all transfer speeds except for Fast Mode+.\n//!\n//! ## Features\n//!\n//! - **Blocking and Asynchronous Modes**: Supports both blocking and\n//! async APIs for flexibility in different runtime environments.\n//! - **DMA Support**: Enables high-performance data transfers using\n//! DMA.\n//! - **Configurable Bus Speeds**: Supports standard (100 kHz), fast\n//! (400 kHz), and fast-plus (1 MHz) modes. Ultra-fast (3.4 MHz) mode\n//! is not yet implemented.\n//! - **Error Handling**: Comprehensive error reporting, including\n//! FIFO errors, arbitration loss, and address NACK conditions.\n//! - **Embedded HAL Compatibility**: Implements traits from\n//! `embedded-hal` and `embedded-hal-async` for interoperability with\n//! other libraries.\n//!\n//! ### Error Types\n//!\n//! - `SetupError`: Errors related to hardware initialization, such as\n//! clock configuration issues.\n//! - `IOError`: Errors during I2C operations, including FIFO errors,\n//! arbitration loss, and invalid buffer lengths.\n//!\n//! ## Example\n//!\n//! ```rust,no_run\n//! #![no_std]\n//! #![no_main]\n//!\n//! # extern crate panic_halt;\n//! # extern crate embassy_mcxa;\n//! # extern crate embassy_executor;\n//! # use panic_halt as _;\n//! use embassy_executor::Spawner;\n//! use embassy_mcxa::clocks::config::Div8;\n//! use embassy_mcxa::config::Config;\n//! use embassy_mcxa::i2c::controller::{self, I2c, Speed};\n//!\n//! #[embassy_executor::main]\n//! async fn main(_spawner: Spawner) {\n//!     let mut config = Config::default();\n//!     config.clock_cfg.sirc.fro_lf_div = Div8::from_divisor(1);\n//!\n//!     let p = embassy_mcxa::init(config);\n//!\n//!     let mut i2c = I2c::new_blocking(p.LPI2C2, p.P1_9, p.P1_8, Default::default()).unwrap();\n//!\n//!     // Write data\n//!     i2c.blocking_write(0x50, &[0x01, 0x02, 0x03]).unwrap();\n//!\n//!     // Read data\n//!     let mut buffer = [0u8; 3];\n//!     i2c.blocking_read(0x50, &mut buffer).unwrap();\n//! }\n//! ```\n\nuse core::future::Future;\nuse core::marker::PhantomData;\n\nuse embassy_hal_internal::Peri;\nuse embassy_hal_internal::drop::OnDrop;\n\nuse super::{Async, AsyncMode, Blocking, Dma, Info, Instance, Mode, SclPin, SdaPin};\nuse crate::clocks::periph_helpers::{Div4, Lpi2cClockSel, Lpi2cConfig};\nuse crate::clocks::{ClockError, PoweredClock, WakeGuard, enable_and_reset};\nuse crate::dma::{Channel, DMA_MAX_TRANSFER_SIZE, DmaChannel, TransferOptions};\nuse crate::gpio::{AnyPin, SealedPin};\nuse crate::interrupt;\nuse crate::interrupt::typelevel::Interrupt;\nuse crate::pac::lpi2c::regs::Msr;\nuse crate::pac::lpi2c::vals::{Alf, Cmd, Dmf, Dozen, Epf, McrRrf, McrRtf, MsrFef, MsrSdf, Ndf, Pltf, Stf};\n\n/// Errors exclusive to HW initialization\n#[derive(Debug, Copy, Clone, PartialEq, Eq)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\n#[non_exhaustive]\npub enum SetupError {\n    /// Clock configuration error.\n    ClockSetup(ClockError),\n    /// Other internal errors or unexpected state.\n    Other,\n}\n\n/// I/O Errors\n#[derive(Debug, Copy, Clone, PartialEq, Eq)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\n#[non_exhaustive]\npub enum IOError {\n    /// FIFO Error, the command in the FIFO queue expected the controller to be in a STARTed state, but it was not.\n    ///\n    /// Even though a START could have been issued earlier, the controller might now be in a different state.\n    /// For example, a NAK condition was detected and the controller automatically issued a STOP.\n    FifoError,\n    /// Reading for I2C failed.\n    ReadFail,\n    /// Writing to I2C failed.\n    WriteFail,\n    /// I2C address NAK condition.\n    AddressNack,\n    /// Bus level arbitration loss.\n    ArbitrationLoss,\n    /// Address out of range.\n    AddressOutOfRange(u8),\n    /// Invalid write buffer length.\n    InvalidWriteBufferLength,\n    /// Invalid read buffer length.\n    InvalidReadBufferLength,\n    /// Other internal errors or unexpected state.\n    Other,\n}\n\nimpl From<crate::dma::InvalidParameters> for IOError {\n    fn from(_value: crate::dma::InvalidParameters) -> Self {\n        IOError::Other\n    }\n}\n\n/// I2C interrupt handler.\npub struct InterruptHandler<T: Instance> {\n    _phantom: PhantomData<T>,\n}\n\nimpl<T: Instance> interrupt::typelevel::Handler<T::Interrupt> for InterruptHandler<T> {\n    unsafe fn on_interrupt() {\n        T::PERF_INT_INCR();\n        if T::info().regs().mier().read().0 != 0 {\n            T::info().regs().mier().write(|w| {\n                w.set_tdie(false);\n                w.set_rdie(false);\n                w.set_epie(false);\n                w.set_sdie(false);\n                w.set_ndie(false);\n                w.set_alie(false);\n                w.set_feie(false);\n                w.set_pltie(false);\n                w.set_dmie(false);\n                w.set_stie(false);\n            });\n\n            T::PERF_INT_WAKE_INCR();\n            T::info().wait_cell().wake();\n        }\n    }\n}\n\n/// Bus speed (nominal SCL, no clock stretching)\n#[derive(Clone, Copy, Default, PartialEq)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub enum Speed {\n    #[default]\n    /// 100 kbit/sec\n    Standard,\n    /// 400 kbit/sec\n    Fast,\n    /// 1 Mbit/sec\n    FastPlus,\n    /// 3.4 Mbit/sec\n    UltraFast,\n}\n\nimpl From<Speed> for u32 {\n    fn from(val: Speed) -> Self {\n        match val {\n            Speed::Standard => 100_000,\n            Speed::Fast => 400_000,\n            Speed::FastPlus => 1_000_000,\n            Speed::UltraFast => 3_400_000,\n        }\n    }\n}\n\nimpl From<Speed> for (u8, u8, u8, u8) {\n    fn from(value: Speed) -> (u8, u8, u8, u8) {\n        match value {\n            Speed::Standard => (0x3d, 0x37, 0x3b, 0x1d),\n            Speed::Fast => (0x0e, 0x0c, 0x0d, 0x06),\n            Speed::FastPlus => (0x04, 0x03, 0x03, 0x02),\n\n            // UltraFast is \"special\". Leaving it unimplemented until\n            // the driver and the clock API is further stabilized.\n            Speed::UltraFast => todo!(),\n        }\n    }\n}\n\n#[derive(Debug, Clone, Copy, PartialEq, Eq)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\nenum SendStop {\n    No,\n    Yes,\n}\n\n/// I2C controller configuration\n#[derive(Clone, Copy, Default)]\n#[non_exhaustive]\npub struct Config {\n    /// Bus speed\n    pub speed: Speed,\n\n    /// Clock configuration\n    pub clock_config: ClockConfig,\n}\n\n/// I2C controller clock configuration\n#[derive(Clone, Copy)]\n#[non_exhaustive]\npub struct ClockConfig {\n    /// Powered clock configuration\n    pub power: PoweredClock,\n    /// LPI2C clock source\n    pub source: Lpi2cClockSel,\n    /// LPI2C pre-divider\n    pub div: Div4,\n}\n\nimpl Default for ClockConfig {\n    fn default() -> Self {\n        Self {\n            power: PoweredClock::NormalEnabledDeepSleepDisabled,\n            source: Lpi2cClockSel::FroLfDiv,\n            div: const { Div4::no_div() },\n        }\n    }\n}\n\n/// I2C Controller Driver.\npub struct I2c<'d, M: Mode> {\n    info: &'static Info,\n    _scl: Peri<'d, AnyPin>,\n    _sda: Peri<'d, AnyPin>,\n    mode: M,\n    is_hs: bool,\n    _wg: Option<WakeGuard>,\n}\n\nimpl<'d> I2c<'d, Blocking> {\n    /// Creates a new blocking instance of the I2C Controller bus driver.\n    ///\n    /// This method initializes the I2C controller in blocking mode, allowing\n    /// synchronous read and write operations.  The I2C bus is configured based\n    /// on the provided `Config` structure, which specifies parameters such as\n    /// bus speed and clock settings.\n    ///\n    /// # Arguments\n    ///\n    /// - `peri`: The peripheral instance representing the I2C controller hardware.\n    /// - `scl`: The pin to be used for the I2C clock line (SCL).\n    /// - `sda`: The pin to be used for the I2C data line (SDA).\n    /// - `config`: A `Config` structure specifying the desired I2C configuration, including bus speed and clock settings.\n    ///\n    /// # Returns\n    ///\n    /// - `Ok(Self)`: A new instance of the I2C driver in blocking mode if initialization is successful.\n    /// - `Err(SetupError)`: An error if the initialization fails, such as due to invalid clock configuration.\n    ///\n    /// # Behavior\n    ///\n    /// - The I2C controller is configured and enabled based on the provided `Config`.\n    /// - Any external pins used for SCL and SDA will be placed into a disabled state when the driver instance is dropped.\n    ///\n    /// # Errors\n    ///\n    /// - `SetupError::ClockSetup`: If there is an issue with the clock configuration.\n    /// - `SetupError::Other`: For other unexpected initialization errors.\n    pub fn new_blocking<T: Instance>(\n        peri: Peri<'d, T>,\n        scl: Peri<'d, impl SclPin<T>>,\n        sda: Peri<'d, impl SdaPin<T>>,\n        config: Config,\n    ) -> Result<Self, SetupError> {\n        Self::new_inner(peri, scl, sda, config, Blocking)\n    }\n}\n\nimpl<'d, M: Mode> I2c<'d, M> {\n    fn new_inner<T: Instance>(\n        _peri: Peri<'d, T>,\n        scl: Peri<'d, impl SclPin<T>>,\n        sda: Peri<'d, impl SdaPin<T>>,\n        config: Config,\n        mode: M,\n    ) -> Result<Self, SetupError> {\n        let ClockConfig { power, source, div } = config.clock_config;\n\n        // Enable clocks\n        let conf = Lpi2cConfig {\n            power,\n            source,\n            div,\n            instance: T::CLOCK_INSTANCE,\n        };\n\n        let parts = unsafe { enable_and_reset::<T>(&conf).map_err(SetupError::ClockSetup)? };\n\n        scl.mux();\n        sda.mux();\n\n        let _scl = scl.into();\n        let _sda = sda.into();\n\n        let inst = Self {\n            info: T::info(),\n            _scl,\n            _sda,\n            mode,\n            is_hs: config.speed == Speed::UltraFast,\n            _wg: parts.wake_guard,\n        };\n\n        inst.set_configuration(&config);\n\n        Ok(inst)\n    }\n\n    fn set_configuration(&self, config: &Config) {\n        // Disable the controller.\n        critical_section::with(|_| self.info.regs().mcr().modify(|w| w.set_men(false)));\n\n        // Soft-reset the controller, read and write FIFOs.\n        self.reset_fifos();\n        critical_section::with(|_| {\n            self.info.regs().mcr().modify(|w| w.set_rst(true));\n            // According to Reference Manual section 40.7.1.4, \"There\n            // is no minimum delay required before clearing the\n            // software reset\", therefore we clear it immediately.\n            self.info.regs().mcr().modify(|w| w.set_rst(false));\n\n            self.info.regs().mcr().modify(|w| {\n                w.set_dozen(Dozen::ENABLED);\n                w.set_dbgen(false);\n            });\n        });\n\n        let (clklo, clkhi, sethold, datavd) = config.speed.into();\n\n        critical_section::with(|_| {\n            self.info.regs().mccr0().modify(|w| {\n                w.set_clklo(clklo);\n                w.set_clkhi(clkhi);\n                w.set_sethold(sethold);\n                w.set_datavd(datavd);\n            })\n        });\n\n        // Enable the controller.\n        critical_section::with(|_| self.info.regs().mcr().modify(|w| w.set_men(true)));\n\n        // Clear all flags\n        self.info.regs().msr().write(|w| {\n            w.set_epf(Epf::INT_YES);\n            w.set_sdf(MsrSdf::INT_YES);\n            w.set_ndf(Ndf::INT_YES);\n            w.set_alf(Alf::INT_YES);\n            w.set_fef(MsrFef::INT_YES);\n            w.set_pltf(Pltf::INT_YES);\n            w.set_dmf(Dmf::INT_YES);\n            w.set_stf(Stf::INT_YES);\n        });\n    }\n\n    fn remediation(&self) {\n        #[cfg(feature = \"defmt\")]\n        defmt::trace!(\"Future dropped, issuing stop\",);\n\n        // if the FIFO is not empty, drop its contents.\n        if !self.is_tx_fifo_empty_or_error() {\n            self.reset_fifos();\n        }\n\n        // send a stop command\n        let _ = self.stop();\n    }\n\n    /// Resets both TX and RX FIFOs dropping their contents.\n    fn reset_fifos(&self) {\n        critical_section::with(|_| {\n            self.info.regs().mcr().modify(|w| {\n                w.set_rtf(McrRtf::RESET);\n                w.set_rrf(McrRrf::RESET);\n            });\n        });\n    }\n\n    /// Checks whether the TX FIFO is full\n    fn is_tx_fifo_full(&self) -> bool {\n        let txfifo_size = 1 << self.info.regs().param().read().mtxfifo();\n        self.info.regs().mfsr().read().txcount() == txfifo_size\n    }\n\n    /// Checks whether the TX FIFO is empty\n    fn is_tx_fifo_empty(&self) -> bool {\n        self.info.regs().mfsr().read().txcount() == 0\n    }\n\n    /// Checks whether the TX FIFO or if there is an error condition active.\n    fn is_tx_fifo_empty_or_error(&self) -> bool {\n        self.is_tx_fifo_empty() || self.status().is_err()\n    }\n\n    /// Checks whether the RX FIFO is empty.\n    fn is_rx_fifo_empty(&self) -> bool {\n        self.info.regs().mfsr().read().rxcount() == 0\n    }\n\n    /// Parses the controller status producing an\n    /// appropriate `Result<(), Error>` variant.\n    fn parse_status(&self, msr: &Msr) -> Result<(), IOError> {\n        if msr.ndf() == Ndf::INT_YES {\n            Err(IOError::AddressNack)\n        } else if msr.alf() == Alf::INT_YES {\n            Err(IOError::ArbitrationLoss)\n        } else if msr.fef() == MsrFef::INT_YES {\n            Err(IOError::FifoError)\n        } else {\n            Ok(())\n        }\n    }\n\n    /// Reads, parses and clears the controller status producing an\n    /// appropriate `Result<(), Error>` variant.\n    ///\n    /// Will also send a STOP command if the tx_fifo is empty.\n    fn status_and_act(&self) -> Result<(), IOError> {\n        let msr = self.info.regs().msr().read();\n        self.info.regs().msr().write(|w| *w = msr);\n\n        let status = self.parse_status(&msr);\n\n        if let Err(IOError::AddressNack) = status {\n            // According to the Reference Manual, section 40.7.1.5\n            // Controller Status (MSR), the controller will\n            // automatically send a STOP condition if\n            // `MCFGR1[AUTOSTOP]` is enabled or if the transmit FIFO\n            // is *not* empty.\n            //\n            // If neither of those conditions is true, we will send a\n            // STOP ourselves.\n            if !self.info.regs().mcfgr1().read().autostop() && self.is_tx_fifo_empty() {\n                self.remediation();\n            }\n        }\n\n        status\n    }\n\n    /// Reads and parses the controller status producing an\n    /// appropriate `Result<(), Error>` variant.\n    fn status(&self) -> Result<(), IOError> {\n        self.parse_status(&self.info.regs().msr().read())\n    }\n\n    /// Inserts the given command into the outgoing FIFO.\n    ///\n    /// Caller must ensure there is space in the FIFO for the new\n    /// command.\n    fn send_cmd(&self, cmd: Cmd, data: u8) {\n        #[cfg(feature = \"defmt\")]\n        defmt::trace!(\n            \"Sending cmd '{}' ({}) with data '{:08x}' MSR: {:08x}\",\n            cmd,\n            cmd as u8,\n            data,\n            self.info.regs().msr().read()\n        );\n\n        self.info.regs().mtdr().write(|w| {\n            w.set_data(data);\n            w.set_cmd(cmd);\n        });\n    }\n\n    /// Prepares an appropriate Start condition on bus by issuing a\n    /// `Start` command together with the device address and R/w bit.\n    ///\n    /// Blocks waiting for space in the FIFO to become available, then\n    /// sends the command and blocks waiting for the FIFO to become\n    /// empty ensuring the command was sent.\n    fn start(&self, address: u8, read: bool) -> Result<(), IOError> {\n        if address >= 0x80 {\n            return Err(IOError::AddressOutOfRange(address));\n        }\n\n        // Wait until we have space in the TxFIFO\n        while self.is_tx_fifo_full() {}\n\n        let addr_rw = address << 1 | if read { 1 } else { 0 };\n        self.send_cmd(if self.is_hs { Cmd::START_HS } else { Cmd::START }, addr_rw);\n\n        // Wait for TxFIFO to be drained\n        while !self.is_tx_fifo_empty_or_error() {}\n\n        // Check controller status\n        self.status_and_act()\n    }\n\n    /// Prepares a Stop condition on the bus.\n    ///\n    /// Analogous to `start`, this blocks waiting for space in the\n    /// FIFO to become available, then sends the command and blocks\n    /// waiting for the FIFO to become empty ensuring the command was\n    /// sent.\n    fn stop(&self) -> Result<(), IOError> {\n        // Wait until we have space in the TxFIFO\n        while self.is_tx_fifo_full() {}\n\n        self.send_cmd(Cmd::STOP, 0);\n\n        // Wait for TxFIFO to be drained\n        while !self.is_tx_fifo_empty_or_error() {}\n\n        self.status_and_act()\n    }\n\n    fn blocking_read_internal(&self, address: u8, read: &mut [u8], send_stop: SendStop) -> Result<(), IOError> {\n        if read.is_empty() {\n            return Err(IOError::InvalidReadBufferLength);\n        }\n\n        for chunk in read.chunks_mut(256) {\n            self.start(address, true)?;\n\n            // Wait until we have space in the TxFIFO\n            while self.is_tx_fifo_full() {}\n\n            self.send_cmd(Cmd::RECEIVE, (chunk.len() - 1) as u8);\n\n            for byte in chunk.iter_mut() {\n                // Wait until there's data in the RxFIFO\n                while self.is_rx_fifo_empty() {}\n\n                *byte = self.info.regs().mrdr().read().data();\n            }\n        }\n\n        if send_stop == SendStop::Yes {\n            self.stop()?;\n        }\n\n        Ok(())\n    }\n\n    fn blocking_write_internal(&self, address: u8, write: &[u8], send_stop: SendStop) -> Result<(), IOError> {\n        self.start(address, false)?;\n\n        // Usually, embassy HALs error out with an empty write,\n        // however empty writes are useful for writing I2C scanning\n        // logic through write probing. That is, we send a start with\n        // R/w bit cleared, but instead of writing any data, just send\n        // the stop onto the bus. This has the effect of checking if\n        // the resulting address got an ACK but causing no\n        // side-effects to the device on the other end.\n        //\n        // Because of this, we are not going to error out in case of\n        // empty writes.\n        if write.is_empty() {\n            #[cfg(feature = \"defmt\")]\n            defmt::trace!(\"Empty write, write probing?\");\n            if send_stop == SendStop::Yes {\n                self.stop()?;\n            }\n            return Ok(());\n        }\n\n        for byte in write {\n            // Wait until we have space in the TxFIFO\n            while self.is_tx_fifo_full() {}\n\n            self.send_cmd(Cmd::TRANSMIT, *byte);\n        }\n\n        if send_stop == SendStop::Yes {\n            self.stop()?;\n        }\n\n        Ok(())\n    }\n\n    // Public API: Blocking\n\n    /// Reads data from the specified I2C address into the provided buffer.\n    ///\n    /// This method blocks the caller until the operation is complete.\n    ///\n    /// # Arguments\n    ///\n    /// - `address`: The 7-bit I2C address of the target device.\n    /// - `read`: A mutable buffer to store the data read from the device.\n    ///\n    /// # Returns\n    ///\n    /// - `Ok(())` if the read operation is successful.\n    /// - `Err(IOError)` if an error occurs during the operation, such as an address NACK or FIFO error.\n    ///\n    /// # Errors\n    ///\n    /// - `IOError::AddressNack`: If the device does not acknowledge the address.\n    /// - `IOError::FifoError`: If there is an issue with the FIFO queue.\n    /// - Other variants of `IOError` for specific I2C errors.\n    ///\n    /// # Notes\n    ///\n    /// The driver will attempt to fill the buffer with data. If the\n    /// buffer length exceeds the maximum transfer size of the\n    /// controller, the read operation will be performed in multiple\n    /// chunks. This will be transparent to the caller.\n    pub fn blocking_read(&mut self, address: u8, read: &mut [u8]) -> Result<(), IOError> {\n        self.blocking_read_internal(address, read, SendStop::Yes)\n    }\n\n    /// Writes data to the specified I2C address from the provided buffer.\n    ///\n    /// This method blocks the caller until the operation is complete.\n    ///\n    /// # Arguments\n    ///\n    /// - `address`: The 7-bit I2C address of the target device.\n    /// - `write`: A buffer containing the data to be written to the device.\n    ///\n    /// # Returns\n    ///\n    /// - `Ok(())` if the write operation is successful.\n    /// - `Err(IOError)` if an error occurs during the operation, such as an address NACK or FIFO error.\n    ///\n    /// # Errors\n    ///\n    /// - `IOError::AddressNack`: If the device does not acknowledge the address.\n    /// - `IOError::FifoError`: If there is an issue with the FIFO queue.\n    /// - Other variants of `IOError` for specific I2C errors.\n    pub fn blocking_write(&mut self, address: u8, write: &[u8]) -> Result<(), IOError> {\n        self.blocking_write_internal(address, write, SendStop::Yes)\n    }\n\n    /// Performs a combined write and read operation on the specified I2C\n    /// address.\n    ///\n    /// This method first writes data to the device, then reads data from the\n    /// device into the provided buffer.  The caller is blocked until the\n    /// operation is complete.\n    ///\n    /// # Arguments\n    ///\n    /// - `address`: The 7-bit I2C address of the target device.\n    /// - `write`: A buffer containing the data to be written to the device.\n    /// - `read`: A mutable buffer to store the data read from the device.\n    ///\n    /// # Returns\n    ///\n    /// - `Ok(())` if the write-read operation is successful.\n    /// - `Err(IOError)` if an error occurs during the operation, such as an address NACK or FIFO error.\n    ///\n    /// # Errors\n    ///\n    /// - `IOError::AddressNack`: If the device does not acknowledge the address.\n    /// - `IOError::FifoError`: If there is an issue with the FIFO queue.\n    /// - Other variants of `IOError` for specific I2C errors.\n    pub fn blocking_write_read(&mut self, address: u8, write: &[u8], read: &mut [u8]) -> Result<(), IOError> {\n        self.blocking_write_internal(address, write, SendStop::No)?;\n        self.blocking_read_internal(address, read, SendStop::Yes)\n    }\n}\n\n#[allow(private_bounds)]\nimpl<'d, M: AsyncMode> I2c<'d, M>\nwhere\n    Self: AsyncEngine,\n{\n    fn enable_rx_ints(&self) {\n        self.info.regs().mier().write(|w| {\n            w.set_rdie(true);\n            w.set_ndie(true);\n            w.set_alie(true);\n            w.set_feie(true);\n            w.set_pltie(true);\n        });\n    }\n\n    fn enable_tx_ints(&self) {\n        self.info.regs().mier().write(|w| {\n            w.set_tdie(true);\n            w.set_ndie(true);\n            w.set_alie(true);\n            w.set_feie(true);\n            w.set_pltie(true);\n        });\n    }\n\n    /// Schedule sending a START command and await it being pulled from the FIFO.\n    ///\n    /// Does not indicate that the command was responded to.\n    async fn async_start(&self, address: u8, read: bool) -> Result<(), IOError> {\n        if address >= 0x80 {\n            return Err(IOError::AddressOutOfRange(address));\n        }\n\n        // send the start command\n        let addr_rw = address << 1 | if read { 1 } else { 0 };\n        self.send_cmd(if self.is_hs { Cmd::START_HS } else { Cmd::START }, addr_rw);\n\n        self.info\n            .wait_cell()\n            .wait_for(|| {\n                // enable interrupts\n                self.enable_tx_ints();\n                // if the command FIFO is empty, we're done sending start\n                self.is_tx_fifo_empty_or_error()\n            })\n            .await\n            .map_err(|_| IOError::Other)?;\n\n        // Note: the START + ACK/NACK have not necessarily been finished here.\n        // thus this might return Ok(()), but might at a later state result in NAK or FifoError.\n        self.status_and_act()\n    }\n\n    async fn async_stop(&self) -> Result<(), IOError> {\n        // send the stop command\n        self.send_cmd(Cmd::STOP, 0);\n\n        self.info\n            .wait_cell()\n            .wait_for(|| {\n                // enable interrupts\n                self.enable_tx_ints();\n                // if the command FIFO is empty, we're done sending stop\n                self.is_tx_fifo_empty_or_error()\n            })\n            .await\n            .map_err(|_| IOError::Other)?;\n\n        self.status_and_act()\n    }\n\n    // Public API: Async\n\n    /// Reads data from the specified I2C address into the provided buffer asynchronously.\n    ///\n    /// This method performs the read operation without blocking the caller,\n    /// returning a `Future` that resolves when the operation is complete.\n    ///\n    /// # Arguments\n    ///\n    /// - `address`: The 7-bit I2C address of the target device.\n    /// - `read`: A mutable buffer to store the data read from the device.\n    ///\n    /// # Returns\n    ///\n    /// - A `Future` that resolves to `Ok(())` if the read operation is successful.\n    /// - Resolves to `Err(IOError)` if an error occurs during the operation, such as an address NACK or FIFO error.\n    ///\n    /// # Errors\n    ///\n    /// - `IOError::AddressNack`: If the device does not acknowledge the address.\n    /// - `IOError::FifoError`: If there is an issue with the FIFO queue.\n    /// - Other variants of `IOError` for specific I2C errors.\n    pub fn async_read<'a>(\n        &'a mut self,\n        address: u8,\n        read: &'a mut [u8],\n    ) -> impl Future<Output = Result<(), IOError>> + 'a {\n        <Self as AsyncEngine>::async_read_internal(self, address, read, SendStop::Yes)\n    }\n\n    /// Writes data to the specified I2C address from the provided buffer asynchronously.\n    ///\n    /// This method performs the write operation without blocking the caller, returning a `Future` that resolves when the operation is complete.\n    ///\n    /// # Arguments\n    ///\n    /// - `address`: The 7-bit I2C address of the target device.\n    /// - `write`: A buffer containing the data to be written to the device.\n    ///\n    /// # Returns\n    ///\n    /// - A `Future` that resolves to `Ok(())` if the write operation is successful.\n    /// - Resolves to `Err(IOError)` if an error occurs during the operation, such as an address NACK or FIFO error.\n    ///\n    /// # Errors\n    ///\n    /// - `IOError::AddressNack`: If the device does not acknowledge the address.\n    /// - `IOError::FifoError`: If there is an issue with the FIFO queue.\n    /// - Other variants of `IOError` for specific I2C errors.\n    pub fn async_write<'a>(\n        &'a mut self,\n        address: u8,\n        write: &'a [u8],\n    ) -> impl Future<Output = Result<(), IOError>> + 'a {\n        <Self as AsyncEngine>::async_write_internal(self, address, write, SendStop::Yes)\n    }\n\n    /// Performs a combined write and read operation on the specified I2C\n    /// address asynchronously.\n    ///\n    /// This method first writes data to the device, then reads data from the\n    /// device into the provided buffer. The operation is performed without\n    /// blocking the caller.\n    ///\n    /// # Arguments\n    ///\n    /// - `address`: The 7-bit I2C address of the target device.\n    /// - `write`: A buffer containing the data to be written to the device.\n    /// - `read`: A mutable buffer to store the data read from the device.\n    ///\n    /// # Returns\n    ///\n    /// - `Ok(())` if the write-read operation is successful.\n    /// - `Err(IOError)` if an error occurs during the operation, such as an address NACK or FIFO error.\n    ///\n    /// # Errors\n    ///\n    /// - `IOError::AddressNack`: If the device does not acknowledge the address.\n    /// - `IOError::FifoError`: If there is an issue with the FIFO queue.\n    /// - Other variants of `IOError` for specific I2C errors.\n    pub async fn async_write_read<'a>(\n        &'a mut self,\n        address: u8,\n        write: &'a [u8],\n        read: &'a mut [u8],\n    ) -> Result<(), IOError> {\n        <Self as AsyncEngine>::async_write_internal(self, address, write, SendStop::No).await?;\n        <Self as AsyncEngine>::async_read_internal(self, address, read, SendStop::Yes).await\n    }\n}\n\ntrait AsyncEngine {\n    fn async_read_internal<'a>(\n        &'a mut self,\n        address: u8,\n        read: &'a mut [u8],\n        send_stop: SendStop,\n    ) -> impl Future<Output = Result<(), IOError>> + 'a;\n\n    fn async_write_internal<'a>(\n        &'a mut self,\n        address: u8,\n        write: &'a [u8],\n        send_stop: SendStop,\n    ) -> impl Future<Output = Result<(), IOError>> + 'a;\n}\n\nimpl<'d> I2c<'d, Async> {\n    /// Creates a new interrupt-only asynchronous instance of the I2C Controller\n    /// bus driver.\n    ///\n    /// This method initializes the I2C controller in asynchronous mode,\n    /// enabling non-blocking operations using futures.  The I2C bus is\n    /// configured based on the provided `Config` structure, which specifies\n    /// parameters such as bus speed and clock settings.\n    ///\n    /// # Arguments\n    ///\n    /// - `peri`: The peripheral instance representing the I2C controller hardware.\n    /// - `scl`: The pin to be used for the I2C clock line (SCL).\n    /// - `sda`: The pin to be used for the I2C data line (SDA).\n    /// - `_irq`: The interrupt binding for the I2C controller, ensuring that an interrupt handler is registered.\n    /// - `config`: A `Config` structure specifying the desired I2C configuration, including bus speed and clock settings.\n    ///\n    /// # Returns\n    ///\n    /// - `Ok(Self)`: A new instance of the I2C driver in asynchronous mode if initialization is successful.\n    /// - `Err(SetupError)`: An error if the initialization fails, such as due to invalid clock configuration.\n    ///\n    /// # Behavior\n    ///\n    /// - The I2C controller is configured and enabled based on the provided `Config`.\n    /// - The interrupt for the I2C controller is enabled to support asynchronous operations.\n    /// - Any external pins used for SCL and SDA will be placed into a disabled state when the driver instance is dropped.\n    ///\n    /// # Errors\n    ///\n    /// - `SetupError::ClockSetup`: If there is an issue with the clock configuration.\n    /// - `SetupError::Other`: For other unexpected initialization errors.\n    pub fn new_async<T: Instance>(\n        peri: Peri<'d, T>,\n        scl: Peri<'d, impl SclPin<T>>,\n        sda: Peri<'d, impl SdaPin<T>>,\n        _irq: impl crate::interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd,\n        config: Config,\n    ) -> Result<Self, SetupError> {\n        T::Interrupt::unpend();\n\n        // Safety: `_irq` ensures an Interrupt Handler exists.\n        unsafe { T::Interrupt::enable() };\n\n        Self::new_inner(peri, scl, sda, config, Async)\n    }\n}\n\nimpl<'d> AsyncEngine for I2c<'d, Async> {\n    async fn async_read_internal(&mut self, address: u8, read: &mut [u8], send_stop: SendStop) -> Result<(), IOError> {\n        if read.is_empty() {\n            return Err(IOError::InvalidReadBufferLength);\n        }\n\n        // perform corrective action if the future is dropped\n        let on_drop = OnDrop::new(|| self.remediation());\n\n        for chunk in read.chunks_mut(256) {\n            self.async_start(address, true).await?;\n\n            // send receive command\n            self.send_cmd(Cmd::RECEIVE, (chunk.len() - 1) as u8);\n\n            self.info\n                .wait_cell()\n                .wait_for(|| {\n                    // enable interrupts\n                    self.enable_tx_ints();\n                    // if the command FIFO is empty, we're done sending start\n                    self.is_tx_fifo_empty_or_error()\n                })\n                .await\n                .map_err(|_| IOError::Other)?;\n\n            for byte in chunk.iter_mut() {\n                self.info\n                    .wait_cell()\n                    .wait_for(|| {\n                        // enable interrupts\n                        self.enable_rx_ints();\n                        // if the rx FIFO is not empty, we need to read a byte\n                        !self.is_rx_fifo_empty()\n                    })\n                    .await\n                    .map_err(|_| IOError::ReadFail)?;\n\n                *byte = self.info.regs().mrdr().read().data();\n            }\n        }\n\n        if send_stop == SendStop::Yes {\n            self.async_stop().await?;\n        }\n\n        // defuse it if the future is not dropped\n        on_drop.defuse();\n\n        Ok(())\n    }\n\n    async fn async_write_internal(&mut self, address: u8, write: &[u8], send_stop: SendStop) -> Result<(), IOError> {\n        self.async_start(address, false).await?;\n\n        // perform corrective action if the future is dropped\n        let on_drop = OnDrop::new(|| self.remediation());\n\n        // Usually, embassy HALs error out with an empty write,\n        // however empty writes are useful for writing I2C scanning\n        // logic through write probing. That is, we send a start with\n        // R/w bit cleared, but instead of writing any data, just send\n        // the stop onto the bus. This has the effect of checking if\n        // the resulting address got an ACK but causing no\n        // side-effects to the device on the other end.\n        //\n        // Because of this, we are not going to error out in case of\n        // empty writes.\n        if write.is_empty() {\n            #[cfg(feature = \"defmt\")]\n            defmt::trace!(\"Empty write, write probing?\");\n            if send_stop == SendStop::Yes {\n                self.async_stop().await?;\n            }\n            return Ok(());\n        }\n\n        for byte in write {\n            // initiate transmit\n            self.send_cmd(Cmd::TRANSMIT, *byte);\n\n            self.info\n                .wait_cell()\n                .wait_for(|| {\n                    // enable interrupts\n                    self.enable_tx_ints();\n                    // if the tx FIFO is empty, we're done transmiting\n                    self.is_tx_fifo_empty_or_error()\n                })\n                .await\n                .map_err(|_| IOError::WriteFail)?;\n\n            self.status_and_act()?;\n        }\n\n        if send_stop == SendStop::Yes {\n            self.async_stop().await?;\n        }\n\n        // defuse it if the future is not dropped\n        on_drop.defuse();\n\n        Ok(())\n    }\n}\n\nimpl<'d> I2c<'d, Dma<'d>> {\n    /// Creates a new asynchronous instance of the I2C Controller bus driver with DMA support.\n    ///\n    /// This method initializes the I2C controller in asynchronous mode with\n    /// Direct Memory Access (DMA) support, enabling efficient non-blocking\n    /// operations for large data transfers.  The I2C bus is configured based on\n    /// the provided `Config` structure, which specifies parameters such as bus\n    /// speed and clock settings.\n    ///\n    /// # Arguments\n    ///\n    /// - `peri`: The peripheral instance representing the I2C controller hardware.\n    /// - `scl`: The pin to be used for the I2C clock line (SCL).\n    /// - `sda`: The pin to be used for the I2C data line (SDA).\n    /// - `tx_dma`: The DMA channel to be used for transmitting data.\n    /// - `rx_dma`: The DMA channel to be used for receiving data.\n    /// - `_irq`: The interrupt binding for the I2C controller, ensuring that an interrupt handler is registered.\n    /// - `config`: A `Config` structure specifying the desired I2C configuration, including bus speed and clock settings.\n    ///\n    /// # Returns\n    ///\n    /// - `Ok(Self)`: A new instance of the I2C driver in asynchronous mode with DMA support if initialization is successful.\n    /// - `Err(SetupError)`: An error if the initialization fails, such as due to invalid clock configuration.\n    ///\n    /// # Behavior\n    ///\n    /// - The I2C controller is configured and enabled based on the provided `Config`.\n    /// - The interrupt for the I2C controller is enabled to support asynchronous operations.\n    /// - The specified DMA channels are initialized and their interrupts are enabled.\n    /// - Any external pins used for SCL and SDA will be placed into a disabled state when the driver instance is dropped.\n    ///\n    /// # Errors\n    ///\n    /// - `SetupError::ClockSetup`: If there is an issue with the clock configuration.\n    /// - `SetupError::Other`: For other unexpected initialization errors.\n    pub fn new_async_with_dma<T: Instance>(\n        peri: Peri<'d, T>,\n        scl: Peri<'d, impl SclPin<T>>,\n        sda: Peri<'d, impl SdaPin<T>>,\n        tx_dma: Peri<'d, impl Channel>,\n        rx_dma: Peri<'d, impl Channel>,\n        _irq: impl crate::interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd,\n        config: Config,\n    ) -> Result<Self, SetupError> {\n        T::Interrupt::unpend();\n\n        // Safety: `_irq` ensures an Interrupt Handler exists.\n        unsafe { T::Interrupt::enable() };\n\n        // enable this channel's interrupt\n        let tx_dma = DmaChannel::new(tx_dma);\n        let rx_dma = DmaChannel::new(rx_dma);\n\n        tx_dma.enable_interrupt();\n        rx_dma.enable_interrupt();\n\n        Self::new_inner(\n            peri,\n            scl,\n            sda,\n            config,\n            Dma {\n                tx_dma,\n                rx_dma,\n                tx_request: T::TX_DMA_REQUEST,\n                rx_request: T::RX_DMA_REQUEST,\n            },\n        )\n    }\n}\n\nimpl<'d> AsyncEngine for I2c<'d, Dma<'d>> {\n    async fn async_read_internal(&mut self, address: u8, read: &mut [u8], send_stop: SendStop) -> Result<(), IOError> {\n        if read.is_empty() {\n            return Err(IOError::InvalidReadBufferLength);\n        }\n\n        // perform corrective action if the future is dropped\n        let on_drop = OnDrop::new(|| {\n            self.remediation();\n            self.info.regs().mder().modify(|w| w.set_rdde(false));\n        });\n\n        for chunk in read.chunks_mut(256) {\n            self.async_start(address, true).await?;\n\n            // send receive command\n            self.send_cmd(Cmd::RECEIVE, (chunk.len() - 1) as u8);\n\n            let peri_addr = self.info.regs().mrdr().as_ptr() as *const u8;\n\n            // _rx_dma is guaranteed to be Some\n            unsafe {\n                // Clean up channel state\n                self.mode.rx_dma.disable_request();\n                self.mode.rx_dma.clear_done();\n                self.mode.rx_dma.clear_interrupt();\n\n                // Set DMA request source from instance type (type-safe)\n                self.mode.rx_dma.set_request_source(self.mode.rx_request);\n\n                // Configure TCD for peripheral-to-memory transfer\n                self.mode.rx_dma.setup_read_from_peripheral(\n                    peri_addr,\n                    chunk,\n                    false,\n                    TransferOptions::COMPLETE_INTERRUPT,\n                )?;\n\n                // Enable I2C RX DMA request\n                self.info.regs().mder().modify(|w| w.set_rdde(true));\n\n                // Enable DMA channel request\n                self.mode.rx_dma.enable_request();\n            }\n\n            // Wait for completion asynchronously\n            core::future::poll_fn(|cx| {\n                self.mode.rx_dma.waker().register(cx.waker());\n                if self.mode.rx_dma.is_done() {\n                    core::task::Poll::Ready(())\n                } else {\n                    core::task::Poll::Pending\n                }\n            })\n            .await;\n\n            // Ensure DMA writes are visible to CPU\n            cortex_m::asm::dsb();\n            // Cleanup\n            self.info.regs().mder().modify(|w| w.set_rdde(false));\n            unsafe {\n                self.mode.rx_dma.disable_request();\n                self.mode.rx_dma.clear_done();\n            }\n        }\n\n        if send_stop == SendStop::Yes {\n            self.async_stop().await?;\n        }\n\n        // defuse it if the future is not dropped\n        on_drop.defuse();\n\n        Ok(())\n    }\n\n    async fn async_write_internal(&mut self, address: u8, write: &[u8], send_stop: SendStop) -> Result<(), IOError> {\n        self.async_start(address, false).await?;\n\n        // Usually, embassy HALs error out with an empty write,\n        // however empty writes are useful for writing I2C scanning\n        // logic through write probing. That is, we send a start with\n        // R/w bit cleared, but instead of writing any data, just send\n        // the stop onto the bus. This has the effect of checking if\n        // the resulting address got an ACK but causing no\n        // side-effects to the device on the other end.\n        //\n        // Because of this, we are not going to error out in case of\n        // empty writes.\n        if write.is_empty() {\n            #[cfg(feature = \"defmt\")]\n            defmt::trace!(\"Empty write, write probing?\");\n            if send_stop == SendStop::Yes {\n                self.async_stop().await?;\n            }\n            return Ok(());\n        }\n\n        // perform corrective action if the future is dropped\n        let on_drop = OnDrop::new(|| {\n            self.remediation();\n            self.info.regs().mder().modify(|w| w.set_tdde(false));\n        });\n\n        for chunk in write.chunks(DMA_MAX_TRANSFER_SIZE) {\n            let peri_addr = self.info.regs().mtdr().as_ptr() as *mut u8;\n\n            unsafe {\n                // Clean up channel state\n                self.mode.tx_dma.disable_request();\n                self.mode.tx_dma.clear_done();\n                self.mode.tx_dma.clear_interrupt();\n\n                // Set DMA request source from instance type (type-safe)\n                self.mode.tx_dma.set_request_source(self.mode.tx_request);\n\n                // Configure TCD for memory-to-peripheral transfer\n                self.mode.tx_dma.setup_write_to_peripheral(\n                    chunk,\n                    peri_addr,\n                    false,\n                    TransferOptions::COMPLETE_INTERRUPT,\n                )?;\n\n                // Enable I2C TX DMA request\n                self.info.regs().mder().modify(|w| w.set_tdde(true));\n\n                // Enable DMA channel request\n                self.mode.tx_dma.enable_request();\n            }\n\n            // Wait for completion asynchronously\n            core::future::poll_fn(|cx| {\n                self.mode.tx_dma.waker().register(cx.waker());\n                if self.mode.tx_dma.is_done() {\n                    core::task::Poll::Ready(())\n                } else {\n                    core::task::Poll::Pending\n                }\n            })\n            .await;\n\n            // Ensure DMA writes are visible to CPU\n            cortex_m::asm::dsb();\n            // Cleanup\n            self.info.regs().mder().modify(|w| w.set_tdde(false));\n            unsafe {\n                self.mode.tx_dma.disable_request();\n                self.mode.tx_dma.clear_done();\n            }\n        }\n\n        if send_stop == SendStop::Yes {\n            self.async_stop().await?;\n        }\n\n        // defuse it if the future is not dropped\n        on_drop.defuse();\n\n        Ok(())\n    }\n}\n\nimpl<'d, M: Mode> Drop for I2c<'d, M> {\n    fn drop(&mut self) {\n        self._scl.set_as_disabled();\n        self._sda.set_as_disabled();\n    }\n}\n\nimpl<'d, M: Mode> embedded_hal_02::blocking::i2c::Read for I2c<'d, M> {\n    type Error = IOError;\n\n    fn read(&mut self, address: u8, buffer: &mut [u8]) -> Result<(), Self::Error> {\n        self.blocking_read(address, buffer)\n    }\n}\n\nimpl<'d, M: Mode> embedded_hal_02::blocking::i2c::Write for I2c<'d, M> {\n    type Error = IOError;\n\n    fn write(&mut self, address: u8, bytes: &[u8]) -> Result<(), Self::Error> {\n        self.blocking_write(address, bytes)\n    }\n}\n\nimpl<'d, M: Mode> embedded_hal_02::blocking::i2c::WriteRead for I2c<'d, M> {\n    type Error = IOError;\n\n    fn write_read(&mut self, address: u8, bytes: &[u8], buffer: &mut [u8]) -> Result<(), Self::Error> {\n        self.blocking_write_read(address, bytes, buffer)\n    }\n}\n\nimpl<'d, M: Mode> embedded_hal_02::blocking::i2c::Transactional for I2c<'d, M> {\n    type Error = IOError;\n\n    fn exec(\n        &mut self,\n        address: u8,\n        operations: &mut [embedded_hal_02::blocking::i2c::Operation<'_>],\n    ) -> Result<(), Self::Error> {\n        if let Some((last, rest)) = operations.split_last_mut() {\n            for op in rest {\n                match op {\n                    embedded_hal_02::blocking::i2c::Operation::Read(buf) => {\n                        self.blocking_read_internal(address, buf, SendStop::No)?\n                    }\n                    embedded_hal_02::blocking::i2c::Operation::Write(buf) => {\n                        self.blocking_write_internal(address, buf, SendStop::No)?\n                    }\n                }\n            }\n\n            match last {\n                embedded_hal_02::blocking::i2c::Operation::Read(buf) => {\n                    self.blocking_read_internal(address, buf, SendStop::Yes)\n                }\n                embedded_hal_02::blocking::i2c::Operation::Write(buf) => {\n                    self.blocking_write_internal(address, buf, SendStop::Yes)\n                }\n            }\n        } else {\n            Ok(())\n        }\n    }\n}\n\nimpl embedded_hal_1::i2c::Error for IOError {\n    fn kind(&self) -> embedded_hal_1::i2c::ErrorKind {\n        match *self {\n            Self::ArbitrationLoss => embedded_hal_1::i2c::ErrorKind::ArbitrationLoss,\n            Self::AddressNack => {\n                embedded_hal_1::i2c::ErrorKind::NoAcknowledge(embedded_hal_1::i2c::NoAcknowledgeSource::Address)\n            }\n            _ => embedded_hal_1::i2c::ErrorKind::Other,\n        }\n    }\n}\n\nimpl<'d, M: Mode> embedded_hal_1::i2c::ErrorType for I2c<'d, M> {\n    type Error = IOError;\n}\n\nimpl<'d, M: Mode> embedded_hal_1::i2c::I2c for I2c<'d, M> {\n    fn transaction(\n        &mut self,\n        address: u8,\n        operations: &mut [embedded_hal_1::i2c::Operation<'_>],\n    ) -> Result<(), Self::Error> {\n        if let Some((last, rest)) = operations.split_last_mut() {\n            for op in rest {\n                match op {\n                    embedded_hal_1::i2c::Operation::Read(buf) => {\n                        self.blocking_read_internal(address, buf, SendStop::No)?\n                    }\n                    embedded_hal_1::i2c::Operation::Write(buf) => {\n                        self.blocking_write_internal(address, buf, SendStop::No)?\n                    }\n                }\n            }\n\n            match last {\n                embedded_hal_1::i2c::Operation::Read(buf) => self.blocking_read_internal(address, buf, SendStop::Yes),\n                embedded_hal_1::i2c::Operation::Write(buf) => self.blocking_write_internal(address, buf, SendStop::Yes),\n            }\n        } else {\n            Ok(())\n        }\n    }\n}\n\nimpl<'d, M: AsyncMode> embedded_hal_async::i2c::I2c for I2c<'d, M>\nwhere\n    I2c<'d, M>: AsyncEngine,\n{\n    async fn transaction(\n        &mut self,\n        address: u8,\n        operations: &mut [embedded_hal_async::i2c::Operation<'_>],\n    ) -> Result<(), Self::Error> {\n        if let Some((last, rest)) = operations.split_last_mut() {\n            for op in rest {\n                match op {\n                    embedded_hal_async::i2c::Operation::Read(buf) => {\n                        <Self as AsyncEngine>::async_read_internal(self, address, buf, SendStop::No).await?\n                    }\n                    embedded_hal_async::i2c::Operation::Write(buf) => {\n                        <Self as AsyncEngine>::async_write_internal(self, address, buf, SendStop::No).await?\n                    }\n                }\n            }\n\n            match last {\n                embedded_hal_async::i2c::Operation::Read(buf) => {\n                    <Self as AsyncEngine>::async_read_internal(self, address, buf, SendStop::Yes).await\n                }\n                embedded_hal_async::i2c::Operation::Write(buf) => {\n                    <Self as AsyncEngine>::async_write_internal(self, address, buf, SendStop::Yes).await\n                }\n            }\n        } else {\n            Ok(())\n        }\n    }\n}\n\nimpl<'d, M: Mode> embassy_embedded_hal::SetConfig for I2c<'d, M> {\n    type Config = Config;\n    type ConfigError = SetupError;\n\n    fn set_config(&mut self, config: &Self::Config) -> Result<(), SetupError> {\n        self.set_configuration(config);\n        Ok(())\n    }\n}\n"
  },
  {
    "path": "embassy-mcxa/src/i2c/mod.rs",
    "content": "//! I2C Support\n\nuse embassy_hal_internal::PeripheralType;\nuse maitake_sync::WaitCell;\nuse paste::paste;\n\nuse crate::clocks::Gate;\nuse crate::clocks::periph_helpers::Lpi2cConfig;\nuse crate::dma::{DmaChannel, DmaRequest};\nuse crate::gpio::{GpioPin, SealedPin};\nuse crate::{interrupt, pac};\n\npub mod controller;\npub mod target;\n\nmod sealed {\n    /// Seal a trait\n    pub trait Sealed {}\n}\n\ntrait SealedInstance: Gate<MrccPeriphConfig = Lpi2cConfig> {\n    fn info() -> &'static Info;\n\n    /// Clock instance\n    const CLOCK_INSTANCE: crate::clocks::periph_helpers::Lpi2cInstance;\n    const PERF_INT_INCR: fn();\n    const PERF_INT_WAKE_INCR: fn();\n    const TX_DMA_REQUEST: DmaRequest;\n    const RX_DMA_REQUEST: DmaRequest;\n}\n\n/// I2C Instance\n#[allow(private_bounds)]\npub trait Instance: SealedInstance + PeripheralType + 'static + Send {\n    /// Interrupt for this I2C instance.\n    type Interrupt: interrupt::typelevel::Interrupt;\n}\n\nstruct Info {\n    regs: pac::lpi2c::Lpi2c,\n    wait_cell: WaitCell,\n}\n\nimpl Info {\n    #[inline(always)]\n    fn regs(&self) -> pac::lpi2c::Lpi2c {\n        self.regs\n    }\n\n    #[inline(always)]\n    fn wait_cell(&self) -> &WaitCell {\n        &self.wait_cell\n    }\n}\n\nunsafe impl Sync for Info {}\n\nmacro_rules! impl_instance {\n    ($($n:literal),*) => {\n        $(\n            paste!{\n                impl SealedInstance for crate::peripherals::[<LPI2C $n>] {\n                    fn info() -> &'static Info {\n                        static INFO: Info = Info {\n                            regs: pac::[<LPI2C $n>],\n                            wait_cell: WaitCell::new(),\n                        };\n                        &INFO\n                    }\n\n                    const CLOCK_INSTANCE: crate::clocks::periph_helpers::Lpi2cInstance\n                        = crate::clocks::periph_helpers::Lpi2cInstance::[<Lpi2c $n>];\n                    const PERF_INT_INCR: fn() = crate::perf_counters::[<incr_interrupt_i2c $n>];\n                    const PERF_INT_WAKE_INCR: fn() = crate::perf_counters::[<incr_interrupt_i2c $n _wake>];\n                    const TX_DMA_REQUEST: DmaRequest = DmaRequest::[<LPI2C $n Tx>];\n                    const RX_DMA_REQUEST: DmaRequest = DmaRequest::[<LPI2C $n Rx>];\n                }\n\n                impl Instance for crate::peripherals::[<LPI2C $n>] {\n                    type Interrupt = crate::interrupt::typelevel::[<LPI2C $n>];\n                }\n            }\n        )*\n    };\n}\n\nimpl_instance!(0, 1, 2, 3);\n\n/// SCL pin trait.\npub trait SclPin<Instance>: GpioPin + sealed::Sealed + PeripheralType {\n    fn mux(&self);\n}\n\n/// SDA pin trait.\npub trait SdaPin<Instance>: GpioPin + sealed::Sealed + PeripheralType {\n    fn mux(&self);\n}\n\n/// Driver mode.\n#[allow(private_bounds)]\npub trait Mode: sealed::Sealed {}\n\n/// Async driver mode.\n#[allow(private_bounds)]\npub trait AsyncMode: sealed::Sealed + Mode {}\n\n/// Blocking mode.\npub struct Blocking;\nimpl sealed::Sealed for Blocking {}\nimpl Mode for Blocking {}\n\n/// Async mode.\npub struct Async;\nimpl sealed::Sealed for Async {}\nimpl Mode for Async {}\nimpl AsyncMode for Async {}\n\n/// DMA mode.\npub struct Dma<'d> {\n    tx_dma: DmaChannel<'d>,\n    rx_dma: DmaChannel<'d>,\n    rx_request: DmaRequest,\n    tx_request: DmaRequest,\n}\nimpl sealed::Sealed for Dma<'_> {}\nimpl Mode for Dma<'_> {}\nimpl AsyncMode for Dma<'_> {}\n\nmacro_rules! impl_pin {\n    ($pin:ident, $peri:ident, $fn:ident, $trait:ident) => {\n        impl sealed::Sealed for crate::peripherals::$pin {}\n\n        impl $trait<crate::peripherals::$peri> for crate::peripherals::$pin {\n            fn mux(&self) {\n                self.set_pull(crate::gpio::Pull::Disabled);\n                self.set_slew_rate(crate::gpio::SlewRate::Fast.into());\n                self.set_drive_strength(crate::gpio::DriveStrength::Double.into());\n                self.set_function(crate::pac::port::vals::Mux::$fn);\n                self.set_enable_input_buffer(true);\n            }\n        }\n    };\n}\n\n#[cfg(feature = \"mcxa2xx\")]\nmod mcxa2xx {\n    use super::*;\n\n    impl_pin!(P0_16, LPI2C0, MUX2, SdaPin);\n    impl_pin!(P0_17, LPI2C0, MUX2, SclPin);\n    impl_pin!(P0_18, LPI2C0, MUX2, SclPin);\n    impl_pin!(P0_19, LPI2C0, MUX2, SdaPin);\n    impl_pin!(P1_0, LPI2C1, MUX3, SdaPin);\n    impl_pin!(P1_1, LPI2C1, MUX3, SclPin);\n    impl_pin!(P1_2, LPI2C1, MUX3, SdaPin);\n    impl_pin!(P1_3, LPI2C1, MUX3, SclPin);\n    impl_pin!(P1_8, LPI2C2, MUX3, SdaPin);\n    impl_pin!(P1_9, LPI2C2, MUX3, SclPin);\n    impl_pin!(P1_10, LPI2C2, MUX3, SdaPin);\n    impl_pin!(P1_11, LPI2C2, MUX3, SclPin);\n    impl_pin!(P1_12, LPI2C1, MUX2, SdaPin);\n    impl_pin!(P1_13, LPI2C1, MUX2, SclPin);\n    impl_pin!(P1_14, LPI2C1, MUX2, SclPin);\n    impl_pin!(P1_15, LPI2C1, MUX2, SdaPin);\n\n    #[cfg(feature = \"sosc-as-gpio\")]\n    impl_pin!(P1_30, LPI2C0, MUX3, SdaPin);\n    #[cfg(feature = \"sosc-as-gpio\")]\n    impl_pin!(P1_31, LPI2C0, MUX3, SclPin);\n\n    impl_pin!(P3_27, LPI2C3, MUX2, SclPin);\n    impl_pin!(P3_28, LPI2C3, MUX2, SdaPin);\n    // impl_pin!(P3_29, LPI2C3, MUX2, HreqPin); What is this HREQ pin?\n    impl_pin!(P3_30, LPI2C3, MUX2, SclPin);\n    impl_pin!(P3_31, LPI2C3, MUX2, SdaPin);\n    impl_pin!(P4_2, LPI2C2, MUX2, SdaPin);\n    impl_pin!(P4_3, LPI2C0, MUX2, SclPin);\n    impl_pin!(P4_4, LPI2C2, MUX2, SdaPin);\n    impl_pin!(P4_5, LPI2C0, MUX2, SclPin);\n    // impl_pin!(P4_6, LPI2C0, MUX2, HreqPin); What is this HREQ pin?\n}\n\n#[cfg(feature = \"mcxa5xx\")]\nmod mcxa5xx {\n    use super::*;\n\n    impl_pin!(P0_16, LPI2C0, MUX2, SdaPin);\n    impl_pin!(P0_17, LPI2C0, MUX2, SclPin);\n    impl_pin!(P0_18, LPI2C0, MUX2, SclPin);\n    impl_pin!(P0_19, LPI2C0, MUX2, SdaPin);\n    #[cfg(feature = \"sosc-as-gpio\")]\n    impl_pin!(P1_30, LPI2C0, MUX3, SdaPin);\n    #[cfg(feature = \"sosc-as-gpio\")]\n    impl_pin!(P1_31, LPI2C0, MUX3, SclPin);\n\n    impl_pin!(P1_0, LPI2C1, MUX3, SdaPin);\n    impl_pin!(P1_1, LPI2C1, MUX3, SclPin);\n    impl_pin!(P1_12, LPI2C1, MUX2, SdaPin);\n    impl_pin!(P1_13, LPI2C1, MUX2, SclPin);\n    impl_pin!(P1_14, LPI2C1, MUX2, SclPin);\n    impl_pin!(P1_15, LPI2C1, MUX2, SdaPin);\n\n    impl_pin!(P1_8, LPI2C2, MUX3, SdaPin);\n    impl_pin!(P1_9, LPI2C2, MUX3, SclPin);\n    impl_pin!(P4_2, LPI2C2, MUX2, SdaPin);\n    impl_pin!(P4_3, LPI2C2, MUX2, SclPin);\n    impl_pin!(P4_4, LPI2C2, MUX2, SdaPin);\n    impl_pin!(P4_5, LPI2C2, MUX2, SclPin);\n\n    impl_pin!(P3_20, LPI2C3, MUX2, SdaPin);\n    impl_pin!(P3_21, LPI2C3, MUX2, SclPin);\n    impl_pin!(P3_27, LPI2C3, MUX2, SclPin);\n    impl_pin!(P3_28, LPI2C3, MUX2, SdaPin);\n    impl_pin!(P3_30, LPI2C3, MUX2, SclPin);\n    impl_pin!(P3_31, LPI2C3, MUX2, SdaPin);\n}\n"
  },
  {
    "path": "embassy-mcxa/src/i2c/target.rs",
    "content": "//! LPI2C Target Driver\n//!\n//! This module provides an implementation of an I2C target (slave)\n//! driver. It supports both blocking and asynchronous modes of\n//! operation, as well as DMA-based transfers. The driver allows the\n//! target device to respond to requests from an I2C controller\n//! (master), including reading and writing data, handling general\n//! calls, and responding to SMBus alerts.\n//!\n//! ## Example\n//!\n//! ```rust,no_run\n//! #![no_std]\n//! #![no_main]\n//!\n//! # extern crate panic_halt;\n//! # extern crate embassy_mcxa;\n//! # extern crate embassy_executor;\n//! # use panic_halt as _;\n//! use embassy_executor::Spawner;\n//! use embassy_mcxa::clocks::config::Div8;\n//! use embassy_mcxa::config::Config;\n//! use embassy_mcxa::i2c::target;\n//!\n//! #[embassy_executor::main]\n//! async fn main(_spawner: Spawner) {\n//!     let mut config = Config::default();\n//!     config.clock_cfg.sirc.fro_lf_div = Div8::from_divisor(1);\n//!\n//!     let p = embassy_mcxa::init(config);\n//!\n//!     let mut config = target::Config::default();\n//!     config.address = target::Address::Dual(0x2a, 0x31);\n//!     let mut i2c = target::I2c::new_blocking(p.LPI2C3, p.P3_27, p.P3_28, config).unwrap();\n//!     let mut buf = [0u8; 32];\n//!\n//!     loop {\n//!         let request = i2c.blocking_listen().unwrap();\n//!         match request {\n//!             target::Request::Read(addr) => {\n//!                 // Controller wants to read from us at `addr`\n//!                 buf.fill(0x55);\n//!                 let _count = i2c.blocking_respond_to_read(&buf).unwrap();\n//!             }\n//!             target::Request::Write(_addr) => {\n//!                 // Controller wants to write to us at `addr`\n//!                 let _count = i2c.blocking_respond_to_write(&mut buf).unwrap();\n//!             }\n//!             target::Request::Stop(_addr) => {\n//!                 // Controller issued a STOP condition for `addr`\n//!             }\n//!             target::Request::GeneralCall => {\n//!                 // Controller issued a General Call\n//!             }\n//!             target::Request::SmbusAlert => {\n//!                 // Controller issued an SMBus Alert\n//!             }\n//!             _ => {}\n//!         }\n//!     }\n//! }\n//! ```\n\nuse core::marker::PhantomData;\nuse core::ops::Range;\nuse core::sync::atomic::{Ordering, fence};\n\nuse embassy_hal_internal::Peri;\nuse embassy_hal_internal::drop::OnDrop;\n\nuse super::{Async, AsyncMode, Blocking, Dma, Info, Instance, Mode, SclPin, SdaPin};\npub use crate::clocks::PoweredClock;\npub use crate::clocks::periph_helpers::{Div4, Lpi2cClockSel, Lpi2cConfig};\nuse crate::clocks::{ClockError, WakeGuard, enable_and_reset};\nuse crate::dma::{Channel, DMA_MAX_TRANSFER_SIZE, DmaChannel, TransferOptions};\nuse crate::gpio::{AnyPin, SealedPin};\nuse crate::interrupt;\nuse crate::interrupt::typelevel::Interrupt;\nuse crate::pac::lpi2c::vals::{Addrcfg, Filtdz, ScrRrf, ScrRtf};\n\n/// Errors exclusive to hardware Initialization\n#[derive(Debug, Copy, Clone, PartialEq, Eq)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\n#[non_exhaustive]\npub enum SetupError {\n    /// Clock configuration error.\n    ClockSetup(ClockError),\n    /// Invalid Address\n    InvalidAddress,\n    /// Other internal errors or unexpected state.\n    Other,\n}\n\n/// Errors exclusive to I/O\n#[derive(Debug, Copy, Clone, PartialEq, Eq)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\n#[non_exhaustive]\npub enum IOError {\n    /// Busy Busy\n    BusBusy,\n    /// Target Busy\n    TargetBusy,\n    /// FIFO Error\n    FifoError,\n    /// Bit Error\n    BitError,\n    /// Other internal errors or unexpected state.\n    Other,\n}\n\nimpl From<crate::dma::InvalidParameters> for IOError {\n    fn from(_value: crate::dma::InvalidParameters) -> Self {\n        IOError::Other\n    }\n}\n\n/// I2C interrupt handler.\npub struct InterruptHandler<T: Instance> {\n    _phantom: PhantomData<T>,\n}\n\nimpl<T: Instance> interrupt::typelevel::Handler<T::Interrupt> for InterruptHandler<T> {\n    unsafe fn on_interrupt() {\n        T::PERF_INT_INCR();\n        if T::info().regs().sier().read().0 != 0 {\n            T::info().regs().sier().write(|w| {\n                w.set_tdie(false);\n                w.set_rdie(false);\n                w.set_avie(false);\n                w.set_taie(false);\n                w.set_rsie(false);\n                w.set_sdie(false);\n                w.set_beie(false);\n                w.set_feie(false);\n                w.set_am0ie(false);\n                w.set_am1ie(false);\n                w.set_gcie(false);\n                w.set_sarie(false);\n            });\n\n            T::PERF_INT_WAKE_INCR();\n            T::info().wait_cell().wake();\n        }\n    }\n}\n\n/// I2C target addresses.\n#[derive(Clone)]\npub enum Address {\n    /// Single address\n    Single(u16),\n    /// Two addresses\n    Dual(u16, u16),\n    /// Range of addresses\n    Range(Range<u16>),\n}\n\nimpl Default for Address {\n    fn default() -> Self {\n        Self::Single(0x2a)\n    }\n}\n\n/// Enable or disable feature\n#[derive(Clone, Default)]\npub enum Status {\n    #[default]\n    Disabled,\n    Enabled,\n}\n\nimpl From<Status> for bool {\n    fn from(value: Status) -> Self {\n        match value {\n            Status::Disabled => false,\n            Status::Enabled => true,\n        }\n    }\n}\n\n/// I2C target configuration\n#[derive(Clone, Default)]\n#[non_exhaustive]\npub struct Config {\n    /// Addresses to respond to\n    pub address: Address,\n\n    /// Enable SMBus alert\n    pub smbus_alert: Status,\n\n    /// Enable general call support\n    pub general_call: Status,\n\n    /// Clock configuration\n    pub clock_config: ClockConfig,\n}\n\n/// I2C target clock configuration\n#[derive(Clone)]\n#[non_exhaustive]\npub struct ClockConfig {\n    /// Powered clock configuration\n    pub power: PoweredClock,\n    /// LPI2C clock source\n    pub source: Lpi2cClockSel,\n    /// LPI2C pre-divider\n    pub div: Div4,\n}\n\nimpl Default for ClockConfig {\n    fn default() -> Self {\n        Self {\n            power: PoweredClock::NormalEnabledDeepSleepDisabled,\n            source: Lpi2cClockSel::FroLfDiv,\n            div: const { Div4::no_div() },\n        }\n    }\n}\n\n/// I2C target events\n#[derive(Clone, Debug)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\n#[non_exhaustive]\npub enum Request {\n    /// Controller wants to write data to this Target\n    Write(u16),\n    /// Controller wants to read data from this Target\n    Read(u16),\n    /// Controller issued Stop condition for this Target\n    Stop(u16),\n    /// Controller issued a General Call\n    GeneralCall,\n    /// Controller issued SMBUS Alert\n    SmbusAlert,\n}\n\n/// I2C target events\n#[derive(Clone)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\n#[non_exhaustive]\npub enum Event {\n    SmbusAlert,\n    GeneralCall,\n    Address0Match(u16),\n    Address1Match(u16),\n    Stop(u16),\n    RepeatedStart(u16),\n    TransmitAck,\n    AddressValid(u16),\n    ReceiveData,\n    TransmitData,\n}\n\n/// I2C Target Driver.\npub struct I2c<'d, M: Mode> {\n    info: &'static Info,\n    _scl: Peri<'d, AnyPin>,\n    _sda: Peri<'d, AnyPin>,\n    smbus_alert: Status,\n    general_call: Status,\n    mode: M,\n    _wg: Option<WakeGuard>,\n}\n\nimpl<'d, M: Mode> I2c<'d, M> {\n    fn new_inner<T: Instance>(\n        _peri: Peri<'d, T>,\n        scl: Peri<'d, impl SclPin<T>>,\n        sda: Peri<'d, impl SdaPin<T>>,\n        config: Config,\n        mode: M,\n    ) -> Result<Self, SetupError> {\n        let ClockConfig { power, source, div } = config.clock_config;\n\n        // Enable clocks\n        let conf = Lpi2cConfig {\n            power,\n            source,\n            div,\n            instance: T::CLOCK_INSTANCE,\n        };\n\n        let parts = unsafe { enable_and_reset::<T>(&conf).map_err(SetupError::ClockSetup)? };\n\n        scl.mux();\n        sda.mux();\n\n        let _scl = scl.into();\n        let _sda = sda.into();\n\n        let inst = Self {\n            info: T::info(),\n            _scl,\n            _sda,\n            smbus_alert: config.smbus_alert.clone(),\n            general_call: config.general_call.clone(),\n            mode,\n            _wg: parts.wake_guard,\n        };\n\n        inst.set_configuration(&config)?;\n\n        Ok(inst)\n    }\n\n    fn set_configuration(&self, config: &Config) -> Result<(), SetupError> {\n        critical_section::with(|_| {\n            // Disable the target.\n            self.info.regs().scr().modify(|w| w.set_sen(false));\n\n            // Soft-reset the target, read and write FIFOs.\n            self.reset_fifos();\n            self.info.regs().scr().modify(|w| w.set_rst(true));\n            // According to Reference Manual section 40.7.1.4, \"There\n            // is no minimum delay required before clearing the\n            // software reset\", therefore we clear it immediately.\n            self.info.regs().scr().modify(|w| w.set_rst(false));\n\n            self.info.regs().scr().modify(|w| {\n                w.set_filtdz(Filtdz::FILTER_DISABLED);\n                w.set_filten(false);\n            });\n\n            self.info.regs().scfgr1().modify(|w| {\n                w.set_rxstall(true);\n                w.set_txdstall(true);\n            });\n\n            // Configure address matching\n            match config.address {\n                Address::Single(addr) => {\n                    self.info.regs().samr().write(|w| w.set_addr0(addr));\n                    self.info.regs().scfgr1().modify(|w| {\n                        w.set_addrcfg(if (0x00..=0x7f).contains(&addr) {\n                            Addrcfg::ADDRESS_MATCH0_7_BIT\n                        } else {\n                            Addrcfg::ADDRESS_MATCH0_10_BIT\n                        })\n                    });\n                }\n\n                Address::Dual(addr0, addr1) => {\n                    // Either both a 7-bit or both are 10-bit\n                    if ((0x00..=0x7f).contains(&addr0) ^ (0x00..=0x7f).contains(&addr1))\n                        || ((0x80..=0x3ff).contains(&addr0) ^ (0x80..=0x3ff).contains(&addr1))\n                    {\n                        return Err(SetupError::InvalidAddress);\n                    }\n\n                    self.info.regs().samr().write(|w| {\n                        w.set_addr0(addr0);\n                        w.set_addr1(addr1);\n                    });\n                    self.info.regs().scfgr1().modify(|w| {\n                        w.set_addrcfg(if (0x00..=0x7f).contains(&addr0) {\n                            Addrcfg::ADDRESS_MATCH0_7_BIT_OR_ADDRESS_MATCH1_7_BIT\n                        } else {\n                            Addrcfg::ADDRESS_MATCH0_10_BIT_OR_ADDRESS_MATCH1_10_BIT\n                        })\n                    });\n                }\n\n                Address::Range(Range { start, end }) => {\n                    if ((0x00..=0x7f).contains(&start) ^ (0x00..=0x7f).contains(&end))\n                        || ((0x80..=0x3ff).contains(&start) ^ (0x80..=0x3ff).contains(&end))\n                    {\n                        return Err(SetupError::InvalidAddress);\n                    }\n\n                    self.info.regs().samr().write(|w| {\n                        w.set_addr0(start);\n                        w.set_addr1(end - 1);\n                    });\n                    self.info.regs().scfgr1().modify(|w| {\n                        w.set_addrcfg(if (0x00..=0x7f).contains(&start) {\n                            Addrcfg::FROM_ADDRESS_MATCH0_7_BIT_TO_ADDRESS_MATCH1_7_BIT\n                        } else {\n                            Addrcfg::FROM_ADDRESS_MATCH0_10_BIT_TO_ADDRESS_MATCH1_10_BIT\n                        })\n                    });\n                }\n            }\n\n            // Enable the target.\n            self.info.regs().scr().modify(|w| w.set_sen(true));\n\n            // Clear all flags\n            self.info.regs().ssr().write(|w| {\n                w.set_rsf(true);\n                w.set_sdf(true);\n                w.set_bef(true);\n                w.set_fef(true);\n            });\n\n            Ok(())\n        })\n    }\n\n    /// Resets both TX and RX FIFOs dropping their contents.\n    fn reset_fifos(&self) {\n        // The critical section is needed to prevent an interrupt from\n        // modifying SCR while we're in the middle of our\n        // read-modify-write operation.\n        critical_section::with(|_| {\n            self.info.regs().scr().modify(|w| {\n                w.set_rtf(ScrRtf::NOW_EMPTY);\n                w.set_rrf(ScrRrf::NOW_EMPTY);\n            });\n        });\n    }\n\n    fn clear_status(&self) {\n        self.info.regs().ssr().write(|w| {\n            w.set_rsf(true);\n            w.set_sdf(true);\n            w.set_bef(true);\n            w.set_fef(true);\n        });\n    }\n\n    /// Reads and parses the target status producing an\n    /// appropriate `Result<(), Error>` variant.\n    fn status(&self) -> Result<Event, IOError> {\n        let ssr = self.info.regs().ssr().read();\n        self.clear_status();\n\n        if ssr.avf() {\n            let sasr = self.info.regs().sasr().read();\n            let addr = sasr.raddr();\n            Ok(Event::AddressValid(addr))\n        } else if ssr.taf() {\n            Ok(Event::TransmitAck)\n        } else if ssr.rsf() {\n            let sasr = self.info.regs().sasr().read();\n            let addr = sasr.raddr();\n            Ok(Event::RepeatedStart(addr))\n        } else if ssr.sdf() {\n            let sasr = self.info.regs().sasr().read();\n            let addr = sasr.raddr();\n            Ok(Event::Stop(addr))\n        } else if ssr.bef() {\n            Err(IOError::BitError)\n        } else if ssr.fef() {\n            Err(IOError::FifoError)\n        } else if ssr.gcf() {\n            Ok(Event::GeneralCall)\n        } else if ssr.sarf() {\n            Ok(Event::SmbusAlert)\n        } else {\n            Err(IOError::Other)\n        }\n    }\n\n    // Public API: Blocking\n\n    /// Block waiting for new events.\n    ///\n    /// This function blocks the caller until a new I2C event is received. It returns the\n    /// type of request made by the I2C controller.\n    ///\n    /// # Returns\n    ///\n    /// - `Ok(Request)` on success.\n    /// - `Err(IOError)` if an error occurs.\n    pub fn blocking_listen(&mut self) -> Result<Request, IOError> {\n        self.clear_status();\n\n        // Wait for Address Valid\n        loop {\n            let ssr = self.info.regs().ssr().read();\n            let avr = ssr.avf();\n            let sarf = ssr.sarf();\n            let gcf = ssr.gcf();\n            let sdf = ssr.sdf();\n\n            if avr || sarf || gcf || sdf {\n                break;\n            }\n        }\n\n        let event = self.status()?;\n\n        match event {\n            Event::SmbusAlert => Ok(Request::SmbusAlert),\n            Event::GeneralCall => Ok(Request::GeneralCall),\n            Event::Stop(addr) => Ok(Request::Stop(addr >> 1)),\n            Event::RepeatedStart(addr) | Event::AddressValid(addr) => {\n                if addr & 1 != 0 {\n                    Ok(Request::Read(addr >> 1))\n                } else {\n                    Ok(Request::Write(addr >> 1))\n                }\n            }\n            _ => Err(IOError::Other),\n        }\n    }\n\n    /// Transmit data to the I2C controller.\n    ///\n    /// This function sends the contents of the provided buffer to the I2C controller. It\n    /// blocks until the data is transmitted or an error occurs.\n    ///\n    /// # Parameters\n    ///\n    /// - `buf`: The buffer containing the data to transmit.\n    ///\n    /// # Returns\n    ///\n    /// - `Ok(usize)` with the number of bytes transmitted.\n    /// - `Err(IOError)` if an error occurs.\n    pub fn blocking_respond_to_read(&mut self, buf: &[u8]) -> Result<usize, IOError> {\n        let mut count = 0;\n\n        self.clear_status();\n\n        for byte in buf.iter() {\n            // Wait until we can send data\n            let ssr = loop {\n                let ssr = self.info.regs().ssr().read();\n                let tdf = ssr.tdf();\n                let sdf = ssr.sdf();\n                let rsf = ssr.rsf();\n\n                if tdf || sdf || rsf {\n                    break ssr;\n                }\n            };\n\n            // If we see a STOP or REPEATED START, break out\n            if ssr.sdf() || ssr.rsf() {\n                #[cfg(feature = \"defmt\")]\n                defmt::trace!(\"Early stop of Target Send routine. STOP or Repeated-start received\");\n                break;\n            } else {\n                self.info.regs().stdr().write(|w| w.set_data(*byte));\n                count += 1;\n            }\n        }\n\n        Ok(count)\n    }\n\n    /// Receive data from the I2C controller.\n    ///\n    /// This function receives data from the I2C controller into the provided buffer. It\n    /// blocks until the buffer is filled or an error occurs.\n    ///\n    /// # Parameters\n    ///\n    /// - `buf`: The buffer to store the received data.\n    ///\n    /// # Returns\n    ///\n    /// - `Ok(usize)` with the number of bytes received.\n    /// - `Err(IOError)` if an error occurs.\n    pub fn blocking_respond_to_write(&mut self, buf: &mut [u8]) -> Result<usize, IOError> {\n        let mut count = 0;\n\n        self.clear_status();\n\n        for byte in buf.iter_mut() {\n            // Wait until we have data to read\n            let ssr = loop {\n                let ssr = self.info.regs().ssr().read();\n                let rdf = ssr.rdf();\n                let sdf = ssr.sdf();\n                let rsf = ssr.rsf();\n\n                if rdf || sdf || rsf {\n                    break ssr;\n                }\n            };\n\n            // If we see a STOP or REPEATED START, break out\n            if ssr.sdf() || ssr.rsf() {\n                #[cfg(feature = \"defmt\")]\n                defmt::trace!(\"Early stop of Target Receive routine. STOP or Repeated-start received\");\n                break;\n            } else {\n                *byte = self.info.regs().srdr().read().data();\n                count += 1;\n            }\n        }\n\n        Ok(count)\n    }\n}\n\nimpl<'d> I2c<'d, Blocking> {\n    /// Create a new blocking instance of the I2C Target bus driver.\n    ///\n    /// This function initializes the I2C target driver in blocking mode. It configures the\n    /// I2C peripheral, sets up the clock, and prepares the pins for operation. Any external\n    /// pin will be placed into the Disabled state upon `Drop`.\n    ///\n    /// # Parameters\n    ///\n    /// - `peri`: The I2C peripheral instance.\n    /// - `scl`: The SCL pin.\n    /// - `sda`: The SDA pin.\n    /// - `config`: The configuration for the I2C target.\n    ///\n    /// # Returns\n    ///\n    /// - `Ok(Self)` on success.\n    /// - `Err(SetupError)` if initialization fails.\n    pub fn new_blocking<T: Instance>(\n        peri: Peri<'d, T>,\n        scl: Peri<'d, impl SclPin<T>>,\n        sda: Peri<'d, impl SdaPin<T>>,\n        config: Config,\n    ) -> Result<Self, SetupError> {\n        Self::new_inner(peri, scl, sda, config, Blocking)\n    }\n}\n\nimpl<'d> I2c<'d, Async> {\n    /// Create a new asynchronous instance of the I2C Target bus driver.\n    ///\n    /// This function initializes the I2C target driver in asynchronous mode. It configures the\n    /// I2C peripheral, sets up the clock, and prepares the pins for operation. Any external\n    /// pin will be placed into the Disabled state upon `Drop`.\n    ///\n    /// # Parameters\n    ///\n    /// - `peri`: The I2C peripheral instance.\n    /// - `scl`: The SCL pin.\n    /// - `sda`: The SDA pin.\n    /// - `_irq`: The interrupt binding for the I2C peripheral.\n    /// - `config`: The configuration for the I2C target.\n    ///\n    /// # Returns\n    ///\n    /// - `Ok(Self)` on success.\n    /// - `Err(SetupError)` if initialization fails.\n    pub fn new_async<T: Instance>(\n        peri: Peri<'d, T>,\n        scl: Peri<'d, impl SclPin<T>>,\n        sda: Peri<'d, impl SdaPin<T>>,\n        _irq: impl crate::interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd,\n        config: Config,\n    ) -> Result<Self, SetupError> {\n        T::Interrupt::unpend();\n\n        // Safety: `_irq` ensures an Interrupt Handler exists.\n        unsafe { T::Interrupt::enable() };\n\n        Self::new_inner(peri, scl, sda, config, Async)\n    }\n}\n\nimpl<'d> I2c<'d, Dma<'d>> {\n    /// Create a new asynchronous instance of the I2C Target bus driver with DMA support.\n    ///\n    /// This function initializes the I2C target driver in asynchronous mode with DMA support.\n    /// It configures the I2C peripheral, sets up the clock, and prepares the pins for operation.\n    /// Any external pin will be placed into the Disabled state upon `Drop`, and the DMA channels\n    /// are also disabled.\n    ///\n    /// # Parameters\n    ///\n    /// - `peri`: The I2C peripheral instance.\n    /// - `scl`: The SCL pin.\n    /// - `sda`: The SDA pin.\n    /// - `tx_dma`: The DMA channel for transmitting data.\n    /// - `rx_dma`: The DMA channel for receiving data.\n    /// - `_irq`: The interrupt binding for the I2C peripheral.\n    /// - `config`: The configuration for the I2C target.\n    ///\n    /// # Returns\n    ///\n    /// - `Ok(Self)` on success.\n    /// - `Err(SetupError)` if initialization fails.\n    pub fn new_async_with_dma<T: Instance>(\n        peri: Peri<'d, T>,\n        scl: Peri<'d, impl SclPin<T>>,\n        sda: Peri<'d, impl SdaPin<T>>,\n        tx_dma: Peri<'d, impl Channel>,\n        rx_dma: Peri<'d, impl Channel>,\n        _irq: impl crate::interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd,\n        config: Config,\n    ) -> Result<Self, SetupError> {\n        T::Interrupt::unpend();\n\n        // Safety: `_irq` ensures an Interrupt Handler exists.\n        unsafe { T::Interrupt::enable() };\n\n        // enable this channel's interrupt\n        let tx_dma = DmaChannel::new(tx_dma);\n        let rx_dma = DmaChannel::new(rx_dma);\n\n        tx_dma.enable_interrupt();\n        rx_dma.enable_interrupt();\n\n        Self::new_inner(\n            peri,\n            scl,\n            sda,\n            config,\n            Dma {\n                tx_dma,\n                rx_dma,\n                tx_request: T::TX_DMA_REQUEST,\n                rx_request: T::RX_DMA_REQUEST,\n            },\n        )\n    }\n\n    async fn read_dma_chunk(&mut self, data: &mut [u8]) -> Result<usize, IOError> {\n        let peri_addr = self.info.regs().srdr().as_ptr() as *const u8;\n\n        self.clear_status();\n\n        unsafe {\n            // Clean up channel state\n            self.mode.rx_dma.disable_request();\n            self.mode.rx_dma.clear_done();\n            self.mode.rx_dma.clear_interrupt();\n\n            // Set DMA request source from instance type (type-safe)\n            self.mode.rx_dma.set_request_source(self.mode.rx_request);\n\n            // Configure TCD for memory-to-peripheral transfer\n            self.mode\n                .rx_dma\n                .setup_read_from_peripheral(peri_addr, data, false, TransferOptions::COMPLETE_INTERRUPT)?;\n\n            // Enable I2C RX DMA request\n            self.info.regs().sder().modify(|w| w.set_rdde(true));\n\n            // Enable DMA channel request\n            self.mode.rx_dma.enable_request();\n        }\n\n        // Wait until STOP or REPEATED START\n        self.info\n            .wait_cell()\n            .wait_for(|| {\n                self.info.regs().sier().write(|w| {\n                    w.set_feie(true);\n                    w.set_beie(true);\n                    w.set_sdie(true);\n                    w.set_rsie(true);\n                });\n                let ssr = self.info.regs().ssr().read();\n                ssr.fef() || ssr.bef() || ssr.sdf() || ssr.rsf()\n            })\n            .await\n            .map_err(|_| IOError::Other)?;\n\n        // Cleanup\n        self.info.regs().sder().modify(|w| w.set_rdde(false));\n        unsafe {\n            self.mode.rx_dma.disable_request();\n            self.mode.rx_dma.clear_done();\n        }\n\n        // Ensure all writes by DMA are visible to the CPU\n        // TODO: ensure this is done internal to the DMA methods so individual drivers\n        // don't need to handle this?\n        fence(Ordering::Acquire);\n\n        let ssr = self.info.regs().ssr().read();\n\n        if ssr.fef() {\n            Err(IOError::FifoError)\n        } else if ssr.bef() {\n            Err(IOError::BitError)\n        } else if ssr.sdf() || ssr.rsf() {\n            Ok(self.mode.rx_dma.transferred_bytes())\n        } else {\n            Err(IOError::Other)\n        }\n    }\n\n    async fn write_dma_chunk(&mut self, data: &[u8]) -> Result<usize, IOError> {\n        let peri_addr = self.info.regs().stdr().as_ptr() as *mut u8;\n\n        self.clear_status();\n\n        unsafe {\n            // Clean up channel state\n            self.mode.tx_dma.disable_request();\n            self.mode.tx_dma.clear_done();\n            self.mode.tx_dma.clear_interrupt();\n\n            // Set DMA request source from instance type (type-safe)\n            self.mode.tx_dma.set_request_source(self.mode.tx_request);\n\n            // Configure TCD for memory-to-peripheral transfer\n            self.mode\n                .tx_dma\n                .setup_write_to_peripheral(data, peri_addr, false, TransferOptions::NO_INTERRUPTS)?;\n\n            // Ensure all writes by DMA are visible to the CPU\n            // TODO: ensure this is done internal to the DMA methods so individual drivers\n            // don't need to handle this?\n            fence(Ordering::Release);\n\n            // Enable I2C TX DMA request\n            self.info.regs().sder().modify(|w| w.set_tdde(true));\n\n            // Enable DMA channel request\n            self.mode.tx_dma.enable_request();\n        }\n\n        // Wait until STOP or REPEATED START\n        self.info\n            .wait_cell()\n            .wait_for(|| {\n                self.info.regs().sier().write(|w| {\n                    w.set_feie(true);\n                    w.set_beie(true);\n                    w.set_sdie(true);\n                    w.set_rsie(true);\n                });\n                let ssr = self.info.regs().ssr().read();\n                ssr.fef() || ssr.bef() || ssr.sdf() || ssr.rsf()\n            })\n            .await\n            .map_err(|_| IOError::Other)?;\n\n        // Cleanup\n        self.info.regs().sder().modify(|w| w.set_tdde(false));\n        unsafe {\n            self.mode.tx_dma.disable_request();\n            self.mode.tx_dma.clear_done();\n        }\n\n        let ssr = self.info.regs().ssr().read();\n\n        if ssr.fef() {\n            Err(IOError::FifoError)\n        } else if ssr.bef() {\n            Err(IOError::BitError)\n        } else if ssr.sdf() || ssr.rsf() {\n            Ok(self.mode.tx_dma.transferred_bytes())\n        } else {\n            Err(IOError::Other)\n        }\n    }\n}\n\n#[allow(private_bounds)]\nimpl<'d, M: AsyncMode> I2c<'d, M>\nwhere\n    Self: AsyncEngine,\n{\n    fn enable_ints(&self) {\n        self.info.regs().sier().write(|w| {\n            w.set_sarie(self.smbus_alert.clone().into());\n            w.set_gcie(self.general_call.clone().into());\n            w.set_am1ie(true);\n            w.set_am0ie(true);\n            w.set_feie(true);\n            w.set_beie(true);\n            w.set_sdie(true);\n            w.set_rsie(true);\n            w.set_taie(true);\n            w.set_avie(true);\n            w.set_rdie(true);\n            w.set_tdie(true);\n        });\n    }\n\n    // Public API: Async\n\n    /// Asynchronously wait for new events.\n    ///\n    /// This function waits asynchronously for a new I2C event and returns the type of\n    /// request made by the I2C controller.\n    ///\n    /// # Returns\n    ///\n    /// - `Ok(Request)` on success.\n    /// - `Err(IOError)` if an error occurs.\n    pub async fn async_listen(&mut self) -> Result<Request, IOError> {\n        self.clear_status();\n\n        self.info\n            .wait_cell()\n            .wait_for(|| {\n                self.enable_ints();\n                let status = self.info.regs().ssr().read();\n                status.avf() || status.sarf() || status.gcf() || status.sdf()\n            })\n            .await\n            .map_err(|_| IOError::Other)?;\n\n        let event = self.status()?;\n\n        match event {\n            Event::SmbusAlert => Ok(Request::SmbusAlert),\n            Event::GeneralCall => Ok(Request::GeneralCall),\n            Event::Stop(addr) => Ok(Request::Stop(addr >> 1)),\n            Event::RepeatedStart(addr) | Event::AddressValid(addr) => {\n                if addr & 1 != 0 {\n                    Ok(Request::Read(addr >> 1))\n                } else {\n                    Ok(Request::Write(addr >> 1))\n                }\n            }\n            _ => Err(IOError::Other),\n        }\n    }\n\n    /// Asynchronously transmit data to the I2C controller.\n    ///\n    /// This function sends the contents of the provided buffer to the I2C controller\n    /// asynchronously.\n    ///\n    /// # Parameters\n    ///\n    /// - `buf`: The buffer containing the data to transmit.\n    ///\n    /// # Returns\n    ///\n    /// - `Ok(usize)` with the number of bytes transmitted.\n    /// - `Err(IOError)` if an error occurs.\n    pub fn async_respond_to_read<'a>(&'a mut self, buf: &'a [u8]) -> impl Future<Output = Result<usize, IOError>> + 'a {\n        <Self as AsyncEngine>::async_respond_to_read_internal(self, buf)\n    }\n\n    /// Asynchronously receive data from the I2C controller.\n    ///\n    /// This function receives data from the I2C controller into the provided buffer\n    /// asynchronously.\n    ///\n    /// # Parameters\n    ///\n    /// - `buf`: The buffer to store the received data.\n    ///\n    /// # Returns\n    ///\n    /// - `Ok(usize)` with the number of bytes received.\n    /// - `Err(IOError)` if an error occurs.\n    pub fn async_respond_to_write<'a>(\n        &'a mut self,\n        buf: &'a mut [u8],\n    ) -> impl Future<Output = Result<usize, IOError>> + 'a {\n        <Self as AsyncEngine>::async_respond_to_write_internal(self, buf)\n    }\n}\n\ntrait AsyncEngine {\n    fn async_respond_to_read_internal<'a>(\n        &'a mut self,\n        buf: &'a [u8],\n    ) -> impl Future<Output = Result<usize, IOError>> + 'a;\n\n    fn async_respond_to_write_internal<'a>(\n        &'a mut self,\n        buf: &'a mut [u8],\n    ) -> impl Future<Output = Result<usize, IOError>> + 'a;\n}\n\nimpl<'d> AsyncEngine for I2c<'d, Async> {\n    async fn async_respond_to_read_internal(&mut self, buf: &[u8]) -> Result<usize, IOError> {\n        let mut count = 0;\n\n        self.clear_status();\n\n        for byte in buf.iter() {\n            // Wait until we can send data\n            self.info\n                .wait_cell()\n                .wait_for(|| {\n                    self.enable_ints();\n                    let ssr = self.info.regs().ssr().read();\n                    ssr.tdf() || ssr.sdf() || ssr.rsf()\n                })\n                .await\n                .map_err(|_| IOError::Other)?;\n\n            // If we see a STOP or REPEATED START, break out\n            let ssr = self.info.regs().ssr().read();\n            if ssr.sdf() || ssr.rsf() {\n                #[cfg(feature = \"defmt\")]\n                defmt::trace!(\"Early stop of Target Send routine. STOP or Repeated-start received\");\n                self.reset_fifos();\n                break;\n            } else {\n                self.info.regs().stdr().write(|w| w.set_data(*byte));\n                count += 1;\n            }\n        }\n\n        Ok(count)\n    }\n\n    async fn async_respond_to_write_internal(&mut self, buf: &mut [u8]) -> Result<usize, IOError> {\n        let mut count = 0;\n\n        self.clear_status();\n\n        for byte in buf.iter_mut() {\n            self.info\n                .wait_cell()\n                .wait_for(|| {\n                    self.enable_ints();\n                    let ssr = self.info.regs().ssr().read();\n                    ssr.rdf() || ssr.sdf() || ssr.rsf()\n                })\n                .await\n                .map_err(|_| IOError::Other)?;\n\n            // If we see a STOP or REPEATED START, break out\n            let ssr = self.info.regs().ssr().read();\n            if ssr.sdf() || ssr.rsf() {\n                #[cfg(feature = \"defmt\")]\n                defmt::trace!(\"Early stop of Target Receive routine. STOP or Repeated-start received\");\n                self.reset_fifos();\n                break;\n            } else {\n                *byte = self.info.regs().srdr().read().data();\n                count += 1;\n            }\n        }\n\n        Ok(count)\n    }\n}\n\nimpl<'d> AsyncEngine for I2c<'d, Dma<'d>> {\n    async fn async_respond_to_read_internal(&mut self, buf: &[u8]) -> Result<usize, IOError> {\n        let mut count = 0;\n\n        self.clear_status();\n\n        // perform corrective action if the future is dropped\n        let on_drop = OnDrop::new(|| {\n            self.info.regs().sder().modify(|w| w.set_tdde(false));\n        });\n\n        for chunk in buf.chunks(DMA_MAX_TRANSFER_SIZE) {\n            count += self.write_dma_chunk(chunk).await?;\n        }\n\n        // defuse it if the future is not dropped\n        on_drop.defuse();\n\n        Ok(count)\n    }\n\n    async fn async_respond_to_write_internal<'a>(&'a mut self, buf: &'a mut [u8]) -> Result<usize, IOError> {\n        let mut count = 0;\n\n        self.clear_status();\n\n        // perform corrective action if the future is dropped\n        let on_drop = OnDrop::new(|| {\n            self.info.regs().sder().modify(|w| w.set_rdde(false));\n        });\n\n        for chunk in buf.chunks_mut(DMA_MAX_TRANSFER_SIZE) {\n            count += self.read_dma_chunk(chunk).await?;\n        }\n\n        // defuse it if the future is not dropped\n        on_drop.defuse();\n\n        Ok(count)\n    }\n}\n\nimpl<'d, M: Mode> Drop for I2c<'d, M> {\n    fn drop(&mut self) {\n        self._scl.set_as_disabled();\n        self._sda.set_as_disabled();\n    }\n}\n"
  },
  {
    "path": "embassy-mcxa/src/i3c/controller.rs",
    "content": "//! I3C Controller driver.\n\nuse embassy_hal_internal::Peri;\nuse embassy_hal_internal::drop::OnDrop;\nuse nxp_pac::i3c::vals::{MdmactrlDmafb, MdmactrlDmatb};\n\nuse super::{Async, AsyncMode, Blocking, Dma, Info, Instance, InterruptHandler, Mode, SclPin, SdaPin};\nuse crate::clocks::periph_helpers::{Div4, I3cClockSel, I3cConfig};\nuse crate::clocks::{ClockError, PoweredClock, WakeGuard, enable_and_reset};\nuse crate::dma::{Channel, DMA_MAX_TRANSFER_SIZE, DmaChannel, TransferOptions};\nuse crate::gpio::{AnyPin, SealedPin};\npub use crate::i2c::controller::Speed;\nuse crate::interrupt::typelevel;\nuse crate::interrupt::typelevel::Interrupt;\nuse crate::pac::i3c::vals::{\n    Disto, Hkeep, Ibiresp, MctrlDir as I3cDir, MdatactrlRxtrig, MdatactrlTxtrig, Mstena, Request, State, Type,\n};\n\nconst MAX_CHUNK_SIZE: usize = 255;\n\n/// Setup Errors\n#[derive(Debug, Copy, Clone, PartialEq, Eq)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\n#[non_exhaustive]\npub enum SetupError {\n    /// Clock configuration error.\n    ClockSetup(ClockError),\n    /// User provided an invalid configuration\n    InvalidConfiguration,\n    /// Other internal errors or unexpected state.\n    Other,\n}\n\n/// I/O Errors\n#[derive(Debug, Copy, Clone, PartialEq, Eq)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\n#[non_exhaustive]\npub enum IOError {\n    /// Underrun error\n    Underrun,\n    /// Not Acknowledge error\n    Nack,\n    /// Write abort error\n    WriteAbort,\n    /// Terminate error\n    Terminate,\n    /// High data rate parity flag\n    HighDataRateParity,\n    /// High data rate CRC error\n    HighDataRateCrc,\n    /// Overread error\n    Overread,\n    /// Overwrite error\n    Overwrite,\n    /// Message error\n    Message,\n    /// Invalid request error\n    InvalidRequest,\n    /// Timeout error\n    Timeout,\n    /// Address out of range.\n    AddressOutOfRange(u8),\n    /// Invalid write buffer length.\n    InvalidWriteBufferLength,\n    /// Invalid read buffer length.\n    InvalidReadBufferLength,\n    /// Other internal errors or unexpected state.\n    Other,\n}\n\nimpl From<crate::dma::InvalidParameters> for IOError {\n    fn from(_value: crate::dma::InvalidParameters) -> Self {\n        Self::Other\n    }\n}\n\n#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\nenum SendStop {\n    #[default]\n    No,\n    Yes,\n}\n\n#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\n#[allow(dead_code)]\npub enum BusType {\n    /// I3C SDR\n    #[default]\n    I3cSdr,\n    /// Legacy I2C\n    I2c,\n    /// I3C DDR\n    I3cDdr,\n}\n\nimpl From<BusType> for Type {\n    fn from(value: BusType) -> Self {\n        match value {\n            BusType::I3cSdr => Self::I3C,\n            BusType::I2c => Self::I2C,\n            BusType::I3cDdr => Self::DDR,\n        }\n    }\n}\n\n#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\nenum Dir {\n    #[default]\n    Write,\n    Read,\n}\n\nimpl From<Dir> for I3cDir {\n    fn from(value: Dir) -> Self {\n        match value {\n            Dir::Write => Self::DIRWRITE,\n            Dir::Read => Self::DIRREAD,\n        }\n    }\n}\n\n/// I3C controller configuration\n#[non_exhaustive]\npub struct Config {\n    /// I3C push-pull bus frequency in Hz.\n    pub push_pull_freq: u32,\n\n    /// I3C open-drain frequency in Hz.\n    pub open_drain_freq: u32,\n\n    /// I2C bus speed\n    pub i2c_speed: Speed,\n\n    /// Clock configuration\n    pub clock_config: ClockConfig,\n}\n\nimpl Default for Config {\n    fn default() -> Self {\n        Self {\n            push_pull_freq: 1_500_000,\n            open_drain_freq: 750_000,\n            i2c_speed: Speed::Fast,\n            clock_config: ClockConfig::default(),\n        }\n    }\n}\n\n/// I3C controller clock configuration\n#[derive(Clone)]\n#[non_exhaustive]\npub struct ClockConfig {\n    /// Powered clock configuration\n    pub power: PoweredClock,\n    /// I3C clock source\n    pub source: I3cClockSel,\n    /// I3C pre-divider\n    pub div: Div4,\n}\n\nimpl Default for ClockConfig {\n    fn default() -> Self {\n        Self {\n            power: PoweredClock::NormalEnabledDeepSleepDisabled,\n            source: I3cClockSel::FroLfDiv,\n            div: const { Div4::no_div() },\n        }\n    }\n}\n\nfn calculate_error(cur_freq: u32, desired_freq: u32) -> u32 {\n    let delta = cur_freq.abs_diff(desired_freq);\n    delta * 100 / desired_freq\n}\n\n/// I3C controller driver.\npub struct I3c<'d, M: Mode> {\n    info: &'static Info,\n    _scl: Peri<'d, AnyPin>,\n    _sda: Peri<'d, AnyPin>,\n    mode: M,\n    fclk: u32,\n    _wg: Option<WakeGuard>,\n}\n\nimpl<'d, M: Mode> I3c<'d, M> {\n    fn new_inner<T: Instance>(\n        _peri: Peri<'d, T>,\n        scl: Peri<'d, impl SclPin<T>>,\n        sda: Peri<'d, impl SdaPin<T>>,\n        config: Config,\n        mode: M,\n    ) -> Result<Self, SetupError> {\n        let ClockConfig { power, source, div } = config.clock_config;\n\n        // Enable clocks\n        let conf = I3cConfig { power, source, div };\n\n        let parts = unsafe { enable_and_reset::<T>(&conf).map_err(SetupError::ClockSetup)? };\n\n        scl.mux();\n        sda.mux();\n\n        let _scl = scl.into();\n        let _sda = sda.into();\n\n        let inst = Self {\n            info: T::info(),\n            _scl,\n            _sda,\n            mode,\n            fclk: parts.freq,\n            _wg: parts.wake_guard,\n        };\n\n        inst.set_configuration(&config)?;\n\n        Ok(inst)\n    }\n\n    fn set_configuration(&self, config: &Config) -> Result<(), SetupError> {\n        self.clear_flags();\n\n        self.info.regs().mdatactrl().modify(|w| {\n            w.set_flushtb(true);\n            w.set_flushfb(true);\n            w.set_unlock(true);\n            w.set_txtrig(MdatactrlTxtrig::FULL_OR_LESS);\n            w.set_rxtrig(MdatactrlRxtrig::NOT_EMPTY);\n        });\n\n        let (ppbaud, odbaud, i2cbaud) = self.calculate_baud_rate_params(config)?;\n\n        self.info.regs().mconfig().write(|w| {\n            w.set_ppbaud(ppbaud as u8);\n            w.set_odbaud(odbaud as u8);\n            w.set_i2cbaud(i2cbaud as u8);\n            w.set_mstena(Mstena::MASTER_ON);\n            w.set_disto(Disto::ENABLE);\n            w.set_hkeep(Hkeep::NONE);\n            w.set_odstop(false);\n            w.set_odhpp(true);\n        });\n\n        Ok(())\n    }\n\n    // REVISIT: not very readable\n    fn calculate_baud_rate_params(&self, config: &Config) -> Result<(u32, u32, u32), SetupError> {\n        const NSEC_PER_SEC: u32 = 1_000_000_000;\n\n        let fclk = self.fclk;\n\n        let target_pp_hz = config.push_pull_freq;\n\n        if target_pp_hz == 0 {\n            return Err(SetupError::InvalidConfiguration);\n        }\n\n        let max_pp_hz = target_pp_hz + target_pp_hz / 10;\n\n        let target_od_hz = config.open_drain_freq;\n        let max_od_hz = target_od_hz + target_od_hz / 10;\n\n        let target_i2c_hz = <Speed as Into<u32>>::into(config.i2c_speed);\n\n        /* -------------------------------------------------------------\n         * 1) Push‑Pull baud (PPBAUD)\n         *    Generated from fclk / 2\n         * ------------------------------------------------------------- */\n\n        let mut pp_src_hz = fclk / 2;\n\n        let mut pp_div = (pp_src_hz / target_pp_hz).max(1);\n        if pp_src_hz / pp_div > max_pp_hz {\n            pp_div += 1;\n        }\n\n        let pp_baud = pp_div - 1;\n        pp_src_hz /= pp_div;\n\n        let pp_low_ns = NSEC_PER_SEC / (2 * pp_src_hz);\n\n        /* -------------------------------------------------------------\n         * 2) Open‑Drain baud (ODBAUD)\n         *    Depends on ODHPP mode\n         * ------------------------------------------------------------- */\n\n        let odhpp_enabled = self.info.regs().mconfig().read().odhpp();\n\n        let (od_baud, _od_src_hz) = if odhpp_enabled {\n            // OD rate derived from 2×PP clock\n            let mut div = ((2 * pp_src_hz) / target_od_hz).max(2);\n            if (2 * pp_src_hz) / div > max_od_hz {\n                div += 1;\n            }\n\n            (div - 2, (2 * pp_src_hz) / div)\n        } else {\n            // OD rate derived directly\n            let mut div = (pp_src_hz / target_od_hz).max(1);\n            if pp_src_hz / div > max_od_hz {\n                div += 1;\n            }\n\n            (div - 1, pp_src_hz / div)\n        };\n\n        let od_low_ns = (od_baud + 1) * pp_low_ns;\n\n        /* -------------------------------------------------------------\n         * 3) I²C baud selection\n         *    Choose even/odd divider with lowest error\n         * ------------------------------------------------------------- */\n\n        let even_div = ((fclk / target_i2c_hz) / (2 * (pp_baud + 1) * (od_baud + 1))).max(1);\n        let even_rate = NSEC_PER_SEC / (2 * even_div * od_low_ns);\n        let even_error = calculate_error(even_rate, target_i2c_hz);\n\n        let odd_div = (((fclk / target_i2c_hz) / ((pp_baud + 1) * (od_baud + 1) - 1)) / 2).max(1);\n        let odd_rate = NSEC_PER_SEC / ((2 * odd_div + 1) * od_low_ns);\n        let odd_error = calculate_error(odd_rate, target_i2c_hz);\n\n        let i2c_baud = if even_error < 10 || odd_error < 10 {\n            if even_error < odd_error {\n                (even_div - 1) * 2\n            } else {\n                (odd_div - 1) * 2 + 1\n            }\n        } else if pp_src_hz / even_div < target_i2c_hz {\n            (even_div - 1) * 2\n        } else {\n            even_div * 2\n        };\n\n        Ok((pp_baud, od_baud, i2c_baud))\n    }\n\n    fn blocking_remediation(&self, bus_type: BusType) {\n        // if the FIFO is not empty, drop its contents.\n        if self.info.regs().mdatactrl().read().txcount() != 0 {\n            self.info.regs().mdatactrl().modify(|w| {\n                w.set_flushtb(true);\n                w.set_flushfb(true);\n            });\n        }\n\n        // send a stop command\n        let _ = self.blocking_stop(bus_type);\n    }\n\n    fn clear_flags(&self) {\n        self.info.regs().mstatus().write(|w| {\n            w.set_slvstart(true);\n            w.set_mctrldone(true);\n            w.set_complete(true);\n            w.set_ibiwon(true);\n            w.set_nowmaster(true);\n        });\n    }\n\n    fn blocking_wait_for_ctrldone(&self) {\n        while !self.info.regs().mstatus().read().mctrldone() {}\n    }\n\n    fn blocking_wait_for_complete(&self) {\n        while !self.info.regs().mstatus().read().complete() {}\n    }\n\n    fn blocking_wait_for_tx_fifo(&self) {\n        while self.info.regs().mdatactrl().read().txfull() {}\n    }\n\n    fn blocking_wait_for_rx_fifo(&self) {\n        while self.info.regs().mdatactrl().read().rxempty() {}\n    }\n\n    fn status(&self) -> Result<(), IOError> {\n        if self.info.regs().mstatus().read().errwarn() {\n            let merrwarn = self.info.regs().merrwarn().read();\n\n            if merrwarn.urun() {\n                Err(IOError::Underrun)\n            } else if merrwarn.nack() {\n                Err(IOError::Nack)\n            } else if merrwarn.wrabt() {\n                Err(IOError::WriteAbort)\n            } else if merrwarn.term() {\n                Err(IOError::Terminate)\n            } else if merrwarn.hpar() {\n                Err(IOError::HighDataRateParity)\n            } else if merrwarn.hcrc() {\n                Err(IOError::HighDataRateCrc)\n            } else if merrwarn.oread() {\n                Err(IOError::Overread)\n            } else if merrwarn.owrite() {\n                Err(IOError::Overwrite)\n            } else if merrwarn.msgerr() {\n                Err(IOError::Message)\n            } else if merrwarn.invreq() {\n                Err(IOError::InvalidRequest)\n            } else if merrwarn.timeout() {\n                Err(IOError::Timeout)\n            } else {\n                // should never happen\n                Err(IOError::Other)\n            }\n        } else {\n            Ok(())\n        }\n    }\n\n    /// Prepares an appropriate Start condition on bus by issuing a\n    /// `Start` request together with the device address, bus type\n    /// (i3c sdr, i3c ddr, or i2c), and R/w bit.\n    fn blocking_start(&self, address: u8, bus_type: BusType, dir: Dir, len: u8) -> Result<(), IOError> {\n        self.clear_flags();\n\n        self.info.regs().mctrl().write(|w| {\n            w.set_addr(address);\n            w.set_rdterm(len);\n            w.set_type_(bus_type.into());\n            w.set_request(Request::EMITSTARTADDR);\n            w.set_dir(dir.into());\n            w.set_ibiresp(Ibiresp::ACK);\n        });\n\n        self.blocking_wait_for_ctrldone();\n        self.status()\n    }\n\n    /// Prepares a Stop condition on the bus.\n    ///\n    /// Analogous to `start`, this blocks waiting for space in the\n    /// FIFO to become available, then sends the command and blocks\n    /// waiting for the FIFO to become empty ensuring the command was\n    /// sent.\n    fn blocking_stop(&self, bus_type: BusType) -> Result<(), IOError> {\n        if self.info.regs().mstatus().read().state() != State::NORMACT {\n            Err(IOError::InvalidRequest)\n        } else {\n            // NOTE: Section 41.3.2.1 states that \"when sending STOP\n            // in I2C mode, MCONFIG[ODSTOP] and MCTRL[TYPE] must be\n            // 1\".\n            self.info\n                .regs()\n                .mconfig()\n                .modify(|w| w.set_odstop(bus_type == BusType::I2c));\n            self.info.regs().mctrl().write(|w| {\n                w.set_request(Request::EMITSTOP);\n                w.set_type_(bus_type.into())\n            });\n            self.blocking_wait_for_ctrldone();\n            self.status()\n        }\n    }\n\n    fn blocking_read_internal(\n        &self,\n        address: u8,\n        read: &mut [u8],\n        bus_type: BusType,\n        send_stop: SendStop,\n    ) -> Result<(), IOError> {\n        if read.is_empty() {\n            return Err(IOError::InvalidReadBufferLength);\n        }\n\n        for chunk in read.chunks_mut(MAX_CHUNK_SIZE) {\n            if let Err(e) = self.blocking_start(address, bus_type, Dir::Read, chunk.len() as u8) {\n                self.blocking_remediation(bus_type);\n                return Err(e);\n            };\n\n            for byte in chunk.iter_mut() {\n                self.blocking_wait_for_rx_fifo();\n                *byte = self.info.regs().mrdatab().read().value();\n            }\n        }\n\n        if send_stop == SendStop::Yes {\n            self.blocking_stop(bus_type)?;\n        }\n\n        Ok(())\n    }\n\n    fn blocking_write_internal(\n        &self,\n        address: u8,\n        write: &[u8],\n        bus_type: BusType,\n        send_stop: SendStop,\n    ) -> Result<(), IOError> {\n        if let Err(e) = self.blocking_start(address, bus_type, Dir::Write, 0) {\n            self.blocking_remediation(bus_type);\n            return Err(e);\n        };\n\n        // Usually, embassy HALs error out with an empty write,\n        // however empty writes are useful for writing I2C scanning\n        // logic through write probing. That is, we send a start with\n        // R/w bit cleared, but instead of writing any data, just send\n        // the stop onto the bus. This has the effect of checking if\n        // the resulting address got an ACK but causing no\n        // side-effects to the device on the other end.\n        //\n        // Because of this, we are not going to error out in case of\n        // empty writes.\n        if write.is_empty() {\n            #[cfg(feature = \"defmt\")]\n            defmt::trace!(\"Empty write, write probing?\");\n            if send_stop == SendStop::Yes {\n                self.blocking_stop(bus_type)?;\n            }\n            return Ok(());\n        }\n\n        let Some((last, rest)) = write.split_last() else {\n            return Err(IOError::InvalidWriteBufferLength);\n        };\n\n        for byte in rest {\n            // Wait until we have space in the TX FIFO.\n            self.blocking_wait_for_tx_fifo();\n            self.info.regs().mwdatab().write(|w| w.set_value(*byte));\n        }\n\n        self.blocking_wait_for_tx_fifo();\n        self.info.regs().mwdatabe().write(|w| w.set_value(*last));\n        self.blocking_wait_for_complete();\n\n        if send_stop == SendStop::Yes {\n            self.blocking_stop(bus_type)?;\n        }\n\n        Ok(())\n    }\n\n    // Public API: Blocking\n\n    /// Read from address into buffer blocking caller until done.\n    pub fn blocking_read(&mut self, address: u8, read: &mut [u8], bus_type: BusType) -> Result<(), IOError> {\n        self.blocking_read_internal(address, read, bus_type, SendStop::Yes)\n    }\n\n    /// Write to address from buffer blocking caller until done.\n    pub fn blocking_write(&mut self, address: u8, write: &[u8], bus_type: BusType) -> Result<(), IOError> {\n        self.blocking_write_internal(address, write, bus_type, SendStop::Yes)\n    }\n\n    /// Write to address from bytes and read from address into buffer blocking caller until done.\n    pub fn blocking_write_read(\n        &mut self,\n        address: u8,\n        write: &[u8],\n        read: &mut [u8],\n        bus_type: BusType,\n    ) -> Result<(), IOError> {\n        self.blocking_write_internal(address, write, bus_type, SendStop::No)?;\n        self.blocking_read_internal(address, read, bus_type, SendStop::Yes)\n    }\n}\n\nimpl<'d> I3c<'d, Blocking> {\n    /// Create a new blocking instance of the I3C controller bus driver.\n    ///\n    /// Any external pin will be placed into Disabled state upon Drop.\n    pub fn new_blocking<T: Instance>(\n        peri: Peri<'d, T>,\n        scl: Peri<'d, impl SclPin<T>>,\n        sda: Peri<'d, impl SdaPin<T>>,\n        config: Config,\n    ) -> Result<Self, SetupError> {\n        Self::new_inner(peri, scl, sda, config, Blocking)\n    }\n}\n\ntrait AsyncEngine {\n    fn async_read_internal<'a>(\n        &'a self,\n        address: u8,\n        read: &'a mut [u8],\n        bus_type: BusType,\n        send_stop: SendStop,\n    ) -> impl Future<Output = Result<(), IOError>> + 'a;\n\n    fn async_write_internal<'a>(\n        &'a self,\n        address: u8,\n        write: &'a [u8],\n        bus_type: BusType,\n        send_stop: SendStop,\n    ) -> impl Future<Output = Result<(), IOError>> + 'a;\n}\n\nimpl<'d> I3c<'d, Async> {\n    /// Create a new asynchronous instance of the I3C controller bus driver.\n    ///\n    /// Any external pin will be placed into Disabled state upon Drop.\n    pub fn new_async<T: Instance>(\n        peri: Peri<'d, T>,\n        scl: Peri<'d, impl SclPin<T>>,\n        sda: Peri<'d, impl SdaPin<T>>,\n        _irq: impl typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd,\n        config: Config,\n    ) -> Result<Self, SetupError> {\n        let inst = Self::new_inner(peri, scl, sda, config, Async);\n\n        T::Interrupt::unpend();\n        unsafe {\n            T::Interrupt::enable();\n        }\n\n        inst\n    }\n}\n\nimpl<'d> AsyncEngine for I3c<'d, Async> {\n    async fn async_read_internal(\n        &self,\n        address: u8,\n        read: &mut [u8],\n        bus_type: BusType,\n        send_stop: SendStop,\n    ) -> Result<(), IOError> {\n        if read.is_empty() {\n            return Err(IOError::InvalidReadBufferLength);\n        }\n\n        // perform corrective action if the future is dropped\n        let on_drop = OnDrop::new(|| {\n            self.blocking_remediation(bus_type);\n        });\n\n        for chunk in read.chunks_mut(MAX_CHUNK_SIZE) {\n            self.async_start(address, bus_type, Dir::Read, chunk.len() as u8)\n                .await?;\n\n            for byte in chunk.iter_mut() {\n                self.async_wait_for_rx_fifo().await?;\n                *byte = self.info.regs().mrdatab().read().value();\n            }\n        }\n\n        if send_stop == SendStop::Yes {\n            self.async_stop(bus_type).await?;\n        }\n\n        // defuse it if the future is not dropped\n        on_drop.defuse();\n\n        Ok(())\n    }\n\n    async fn async_write_internal(\n        &self,\n        address: u8,\n        write: &[u8],\n        bus_type: BusType,\n        send_stop: SendStop,\n    ) -> Result<(), IOError> {\n        // perform corrective action if the future is dropped\n        let on_drop = OnDrop::new(|| {\n            self.blocking_remediation(bus_type);\n        });\n\n        self.async_start(address, bus_type, Dir::Write, 0).await?;\n\n        // Usually, embassy HALs error out with an empty write,\n        // however empty writes are useful for writing I2C scanning\n        // logic through write probing. That is, we send a start with\n        // R/w bit cleared, but instead of writing any data, just send\n        // the stop onto the bus. This has the effect of checking if\n        // the resulting address got an ACK but causing no\n        // side-effects to the device on the other end.\n        //\n        // Because of this, we are not going to error out in case of\n        // empty writes.\n        if write.is_empty() {\n            #[cfg(feature = \"defmt\")]\n            defmt::trace!(\"Empty write, write probing?\");\n            if send_stop == SendStop::Yes {\n                self.async_stop(bus_type).await?;\n            }\n            return Ok(());\n        }\n\n        let Some((last, rest)) = write.split_last() else {\n            return Err(IOError::InvalidWriteBufferLength);\n        };\n\n        for byte in rest {\n            self.async_wait_for_tx_fifo().await?;\n            self.info.regs().mwdatab().write(|w| w.set_value(*byte));\n        }\n\n        // Wait until we have space in the TX FIFO.\n        self.async_wait_for_tx_fifo().await?;\n        self.info.regs().mwdatabe().write(|w| w.set_value(*last));\n\n        // Wait for complete\n        self.async_wait_for_complete().await?;\n\n        if send_stop == SendStop::Yes {\n            self.async_stop(bus_type).await?;\n        }\n\n        // defuse it if the future is not dropped\n        on_drop.defuse();\n\n        Ok(())\n    }\n}\n\nimpl<'d> I3c<'d, Dma<'d>> {\n    /// Create a new async instance of the I3C Controller bus driver\n    /// with DMA support.\n    ///\n    /// Any external pin will be placed into Disabled state upon Drop,\n    /// additionally, the DMA channel is disabled.\n    pub fn new_async_with_dma<T: Instance>(\n        peri: Peri<'d, T>,\n        scl: Peri<'d, impl SclPin<T>>,\n        sda: Peri<'d, impl SdaPin<T>>,\n        tx_dma: Peri<'d, impl Channel>,\n        rx_dma: Peri<'d, impl Channel>,\n        _irq: impl typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd,\n        config: Config,\n    ) -> Result<Self, SetupError> {\n        T::Interrupt::unpend();\n\n        // Safety: `_irq` ensures an Interrupt Handler exists.\n        unsafe { T::Interrupt::enable() };\n\n        // enable this channel's interrupt\n        let tx_dma = DmaChannel::new(tx_dma);\n        let rx_dma = DmaChannel::new(rx_dma);\n\n        tx_dma.enable_interrupt();\n        rx_dma.enable_interrupt();\n\n        Self::new_inner(\n            peri,\n            scl,\n            sda,\n            config,\n            Dma {\n                tx_dma,\n                rx_dma,\n                tx_request: T::TX_DMA_REQUEST,\n                rx_request: T::RX_DMA_REQUEST,\n            },\n        )\n    }\n}\n\nimpl<'d> AsyncEngine for I3c<'d, Dma<'d>> {\n    async fn async_read_internal(\n        &self,\n        address: u8,\n        read: &mut [u8],\n        bus_type: BusType,\n        send_stop: SendStop,\n    ) -> Result<(), IOError> {\n        if read.is_empty() {\n            return Err(IOError::InvalidReadBufferLength);\n        }\n\n        // perform corrective action if the future is dropped\n        let on_drop = OnDrop::new(|| {\n            self.blocking_remediation(bus_type);\n            self.info\n                .regs()\n                .mdmactrl()\n                .modify(|w| w.set_dmafb(MdmactrlDmafb::NOT_USED));\n        });\n\n        for chunk in read.chunks_mut(MAX_CHUNK_SIZE) {\n            self.async_start(address, bus_type, Dir::Read, chunk.len() as u8)\n                .await?;\n\n            let peri_addr = self.info.regs().mrdatab().as_ptr() as *const u8;\n\n            unsafe {\n                // Clean up channel state\n                self.mode.rx_dma.disable_request();\n                self.mode.rx_dma.clear_done();\n                self.mode.rx_dma.clear_interrupt();\n\n                // Set DMA request source from instance type (type-safe)\n                self.mode.rx_dma.set_request_source(self.mode.rx_request);\n\n                // Configure TCD for peripheral-to-memory transfer\n                self.mode.rx_dma.setup_read_from_peripheral(\n                    peri_addr,\n                    chunk,\n                    false,\n                    TransferOptions::COMPLETE_INTERRUPT,\n                )?;\n\n                // Enable I3C RX DMA request\n                self.info\n                    .regs()\n                    .mdmactrl()\n                    .modify(|w| w.set_dmafb(MdmactrlDmafb::ENABLE));\n\n                // Enable DMA channel request\n                self.mode.rx_dma.enable_request();\n            }\n\n            // Wait for completion asynchronously\n            core::future::poll_fn(|cx| {\n                self.mode.rx_dma.waker().register(cx.waker());\n                if self.mode.rx_dma.is_done() {\n                    core::task::Poll::Ready(())\n                } else {\n                    core::task::Poll::Pending\n                }\n            })\n            .await;\n\n            // Ensure DMA writes are visible to CPU\n            cortex_m::asm::dsb();\n            // Cleanup\n            self.info\n                .regs()\n                .mdmactrl()\n                .modify(|w| w.set_dmafb(MdmactrlDmafb::NOT_USED));\n            unsafe {\n                self.mode.rx_dma.disable_request();\n                self.mode.rx_dma.clear_done();\n            }\n        }\n\n        if send_stop == SendStop::Yes {\n            self.async_stop(bus_type).await?;\n        }\n\n        // defuse it if the future is not dropped\n        on_drop.defuse();\n\n        Ok(())\n    }\n\n    async fn async_write_internal(\n        &self,\n        address: u8,\n        write: &[u8],\n        bus_type: BusType,\n        send_stop: SendStop,\n    ) -> Result<(), IOError> {\n        // perform corrective action if the future is dropped\n        let on_drop = OnDrop::new(|| {\n            self.blocking_remediation(bus_type);\n            self.info\n                .regs()\n                .mdmactrl()\n                .modify(|w| w.set_dmatb(MdmactrlDmatb::NOT_USED));\n        });\n\n        self.async_start(address, bus_type, Dir::Write, 0).await?;\n\n        // Usually, embassy HALs error out with an empty write,\n        // however empty writes are useful for writing I2C scanning\n        // logic through write probing. That is, we send a start with\n        // R/w bit cleared, but instead of writing any data, just send\n        // the stop onto the bus. This has the effect of checking if\n        // the resulting address got an ACK but causing no\n        // side-effects to the device on the other end.\n        //\n        // Because of this, we are not going to error out in case of\n        // empty writes.\n        if write.is_empty() {\n            #[cfg(feature = \"defmt\")]\n            defmt::trace!(\"Empty write, write probing?\");\n            if send_stop == SendStop::Yes {\n                self.async_stop(bus_type).await?;\n            }\n            return Ok(());\n        }\n\n        let Some((last, rest)) = write.split_last() else {\n            return Err(IOError::InvalidWriteBufferLength);\n        };\n\n        for chunk in rest.chunks(DMA_MAX_TRANSFER_SIZE) {\n            let peri_addr = self.info.regs().mwdatab().as_ptr() as *mut u8;\n\n            unsafe {\n                // Clean up channel state\n                self.mode.tx_dma.disable_request();\n                self.mode.tx_dma.clear_done();\n                self.mode.tx_dma.clear_interrupt();\n\n                // Set DMA request source from instance type (type-safe)\n                self.mode.tx_dma.set_request_source(self.mode.tx_request);\n\n                // Configure TCD for memory-to-peripheral transfer\n                self.mode.tx_dma.setup_write_to_peripheral(\n                    chunk,\n                    peri_addr,\n                    false,\n                    TransferOptions::COMPLETE_INTERRUPT,\n                )?;\n\n                // Enable I3C TX DMA request\n                self.info\n                    .regs()\n                    .mdmactrl()\n                    .modify(|w| w.set_dmatb(MdmactrlDmatb::ENABLE));\n\n                // Enable DMA channel request\n                self.mode.tx_dma.enable_request();\n            }\n\n            // Wait for completion asynchronously\n            core::future::poll_fn(|cx| {\n                self.mode.tx_dma.waker().register(cx.waker());\n                if self.mode.tx_dma.is_done() {\n                    core::task::Poll::Ready(())\n                } else {\n                    core::task::Poll::Pending\n                }\n            })\n            .await;\n\n            // Ensure DMA writes are visible to CPU\n            cortex_m::asm::dsb();\n            // Cleanup\n            self.info\n                .regs()\n                .mdmactrl()\n                .modify(|w| w.set_dmatb(MdmactrlDmatb::NOT_USED));\n            unsafe {\n                self.mode.tx_dma.disable_request();\n                self.mode.tx_dma.clear_done();\n            }\n        }\n\n        // Wait until we have space in the TX FIFO.\n        self.async_wait_for_tx_fifo().await?;\n        self.info.regs().mwdatabe().write(|w| w.set_value(*last));\n\n        // Wait for complete\n        self.async_wait_for_complete().await?;\n\n        if send_stop == SendStop::Yes {\n            self.async_stop(bus_type).await?;\n        }\n\n        // defuse it if the future is not dropped\n        on_drop.defuse();\n\n        Ok(())\n    }\n}\n\n#[allow(private_bounds)]\nimpl<'d, M: AsyncMode> I3c<'d, M>\nwhere\n    Self: AsyncEngine,\n{\n    async fn async_wait_for_ctrldone(&self) -> Result<(), IOError> {\n        self.info\n            .wait_cell()\n            .wait_for(|| {\n                // enable control done interrupt\n                self.info.regs().mintset().write(|w| {\n                    w.set_mctrldone(true);\n                    w.set_errwarn(true);\n                });\n                // check control done status\n                self.info.regs().mstatus().read().mctrldone() || self.info.regs().mstatus().read().errwarn()\n            })\n            .await\n            .map_err(|_| IOError::Other)\n    }\n\n    async fn async_wait_for_complete(&self) -> Result<(), IOError> {\n        self.info\n            .wait_cell()\n            .wait_for(|| {\n                // enable control done interrupt\n                self.info.regs().mintset().write(|w| {\n                    w.set_complete(true);\n                    w.set_errwarn(true);\n                });\n                // check control done status\n                self.info.regs().mstatus().read().complete() || self.info.regs().mstatus().read().errwarn()\n            })\n            .await\n            .map_err(|_| IOError::Other)\n    }\n\n    async fn async_wait_for_tx_fifo(&self) -> Result<(), IOError> {\n        // Wait until we have space in the TX FIFO.\n        self.info\n            .wait_cell()\n            .wait_for(|| {\n                // enable TXNOTFULL interrupt\n                self.info.regs().mintset().write(|w| {\n                    w.set_txnotfull(true);\n                    w.set_errwarn(true);\n                });\n                // if the TX FIFO isn't full, we can write bytes\n                self.info.regs().mstatus().read().txnotfull() || self.info.regs().mstatus().read().errwarn()\n            })\n            .await\n            .map_err(|_| IOError::Overwrite)\n    }\n\n    async fn async_wait_for_rx_fifo(&self) -> Result<(), IOError> {\n        self.info\n            .wait_cell()\n            .wait_for(|| {\n                // enable RXPEND interrupt\n                self.info.regs().mintset().write(|w| {\n                    w.set_rxpend(true);\n                    w.set_errwarn(true);\n                });\n                // if the rx FIFO is pending, we need to read bytes\n                self.info.regs().mstatus().read().rxpend() || self.info.regs().mstatus().read().errwarn()\n            })\n            .await\n            .map_err(|_| IOError::Overread)\n    }\n\n    /// Prepares an appropriate Start condition on bus by issuing a\n    /// `Start` request together with the device address, bus type\n    /// (i3c sdr, i3c ddr, or i2c), and R/w bit.\n    async fn async_start(&self, address: u8, bus_type: BusType, dir: Dir, len: u8) -> Result<(), IOError> {\n        self.clear_flags();\n\n        self.info.regs().mctrl().write(|w| {\n            w.set_addr(address);\n            w.set_rdterm(len);\n            w.set_type_(bus_type.into());\n            w.set_request(Request::EMITSTARTADDR);\n            w.set_dir(dir.into());\n            w.set_ibiresp(Ibiresp::ACK);\n        });\n\n        self.async_wait_for_ctrldone().await?;\n        self.status()\n    }\n\n    /// Prepares a Stop condition on the bus.\n    ///\n    /// Analogous to `start`, this blocks waiting for space in the\n    /// FIFO to become available, then sends the command and blocks\n    /// waiting for the FIFO to become empty ensuring the command was\n    /// sent.\n    async fn async_stop(&self, bus_type: BusType) -> Result<(), IOError> {\n        if self.info.regs().mstatus().read().state() != State::NORMACT {\n            Err(IOError::InvalidRequest)\n        } else {\n            // NOTE: Section 41.3.2.1 states that \"when sending STOP\n            // in I2C mode, MCONFIG[ODSTOP] and MCTRL[TYPE] must be\n            // 1\".\n            self.info\n                .regs()\n                .mconfig()\n                .modify(|w| w.set_odstop(bus_type == BusType::I2c));\n\n            self.info.regs().mctrl().write(|w| {\n                w.set_request(Request::EMITSTOP);\n                w.set_type_(bus_type.into());\n            });\n            self.async_wait_for_ctrldone().await?;\n            self.status()\n        }\n    }\n\n    // Public API: Async\n\n    /// Read from address into buffer asynchronously.\n    pub fn async_read<'a>(\n        &'a mut self,\n        address: u8,\n        read: &'a mut [u8],\n        bus_type: BusType,\n    ) -> impl Future<Output = Result<(), IOError>> + 'a {\n        <Self as AsyncEngine>::async_read_internal(self, address, read, bus_type, SendStop::Yes)\n    }\n\n    /// Write to address from buffer asynchronously.\n    pub fn async_write<'a>(\n        &'a mut self,\n        address: u8,\n        write: &'a [u8],\n        bus_type: BusType,\n    ) -> impl Future<Output = Result<(), IOError>> + 'a {\n        <Self as AsyncEngine>::async_write_internal(self, address, write, bus_type, SendStop::Yes)\n    }\n\n    /// Write to address from bytes and then read from address into buffer asynchronously.\n    pub async fn async_write_read<'a>(\n        &'a mut self,\n        address: u8,\n        write: &'a [u8],\n        read: &'a mut [u8],\n        bus_type: BusType,\n    ) -> Result<(), IOError> {\n        <Self as AsyncEngine>::async_write_internal(self, address, write, bus_type, SendStop::No).await?;\n        <Self as AsyncEngine>::async_read_internal(self, address, read, bus_type, SendStop::Yes).await\n    }\n}\n\nimpl<'d, M: Mode> Drop for I3c<'d, M> {\n    fn drop(&mut self) {\n        self._scl.set_as_disabled();\n        self._sda.set_as_disabled();\n    }\n}\n\nimpl<'d, M: Mode> embedded_hal_02::blocking::i2c::Read for I3c<'d, M> {\n    type Error = IOError;\n\n    fn read(&mut self, address: u8, buffer: &mut [u8]) -> Result<(), Self::Error> {\n        self.blocking_read(address, buffer, BusType::I2c)\n    }\n}\n\nimpl<'d, M: Mode> embedded_hal_02::blocking::i2c::Write for I3c<'d, M> {\n    type Error = IOError;\n\n    fn write(&mut self, address: u8, bytes: &[u8]) -> Result<(), Self::Error> {\n        self.blocking_write(address, bytes, BusType::I2c)\n    }\n}\n\nimpl<'d, M: Mode> embedded_hal_02::blocking::i2c::WriteRead for I3c<'d, M> {\n    type Error = IOError;\n\n    fn write_read(&mut self, address: u8, bytes: &[u8], buffer: &mut [u8]) -> Result<(), Self::Error> {\n        self.blocking_write_read(address, bytes, buffer, BusType::I2c)\n    }\n}\n\nimpl<'d, M: Mode> embedded_hal_02::blocking::i2c::Transactional for I3c<'d, M> {\n    type Error = IOError;\n\n    fn exec(\n        &mut self,\n        address: u8,\n        operations: &mut [embedded_hal_02::blocking::i2c::Operation<'_>],\n    ) -> Result<(), Self::Error> {\n        let Some((last, rest)) = operations.split_last_mut() else {\n            return Ok(());\n        };\n\n        for op in rest {\n            match op {\n                embedded_hal_02::blocking::i2c::Operation::Read(buf) => {\n                    self.blocking_read_internal(address, buf, BusType::I2c, SendStop::No)?\n                }\n                embedded_hal_02::blocking::i2c::Operation::Write(buf) => {\n                    self.blocking_write_internal(address, buf, BusType::I2c, SendStop::No)?\n                }\n            }\n        }\n\n        match last {\n            embedded_hal_02::blocking::i2c::Operation::Read(buf) => {\n                self.blocking_read_internal(address, buf, BusType::I2c, SendStop::Yes)\n            }\n            embedded_hal_02::blocking::i2c::Operation::Write(buf) => {\n                self.blocking_write_internal(address, buf, BusType::I2c, SendStop::Yes)\n            }\n        }\n    }\n}\n\nimpl embedded_hal_1::i2c::Error for IOError {\n    fn kind(&self) -> embedded_hal_1::i2c::ErrorKind {\n        match *self {\n            Self::Nack => {\n                embedded_hal_1::i2c::ErrorKind::NoAcknowledge(embedded_hal_1::i2c::NoAcknowledgeSource::Unknown)\n            }\n            _ => embedded_hal_1::i2c::ErrorKind::Other,\n        }\n    }\n}\n\nimpl<'d, M: Mode> embedded_hal_1::i2c::ErrorType for I3c<'d, M> {\n    type Error = IOError;\n}\n\nimpl<'d, M: Mode> embedded_hal_1::i2c::I2c for I3c<'d, M> {\n    fn transaction(\n        &mut self,\n        address: u8,\n        operations: &mut [embedded_hal_1::i2c::Operation<'_>],\n    ) -> Result<(), Self::Error> {\n        let Some((last, rest)) = operations.split_last_mut() else {\n            return Ok(());\n        };\n\n        for op in rest {\n            match op {\n                embedded_hal_1::i2c::Operation::Read(buf) => {\n                    self.blocking_read_internal(address, buf, BusType::I2c, SendStop::No)?\n                }\n                embedded_hal_1::i2c::Operation::Write(buf) => {\n                    self.blocking_write_internal(address, buf, BusType::I2c, SendStop::No)?\n                }\n            }\n        }\n\n        match last {\n            embedded_hal_1::i2c::Operation::Read(buf) => {\n                self.blocking_read_internal(address, buf, BusType::I2c, SendStop::Yes)\n            }\n            embedded_hal_1::i2c::Operation::Write(buf) => {\n                self.blocking_write_internal(address, buf, BusType::I2c, SendStop::Yes)\n            }\n        }\n    }\n}\n\nimpl<'d, M: AsyncMode> embedded_hal_async::i2c::I2c for I3c<'d, M>\nwhere\n    I3c<'d, M>: AsyncEngine,\n{\n    async fn transaction(\n        &mut self,\n        address: u8,\n        operations: &mut [embedded_hal_async::i2c::Operation<'_>],\n    ) -> Result<(), Self::Error> {\n        let Some((last, rest)) = operations.split_last_mut() else {\n            return Ok(());\n        };\n\n        for op in rest {\n            match op {\n                embedded_hal_async::i2c::Operation::Read(buf) => {\n                    <Self as AsyncEngine>::async_read_internal(self, address, buf, BusType::I2c, SendStop::No).await?\n                }\n                embedded_hal_async::i2c::Operation::Write(buf) => {\n                    <Self as AsyncEngine>::async_write_internal(self, address, buf, BusType::I2c, SendStop::No).await?\n                }\n            }\n        }\n\n        match last {\n            embedded_hal_async::i2c::Operation::Read(buf) => {\n                <Self as AsyncEngine>::async_read_internal(self, address, buf, BusType::I2c, SendStop::Yes).await\n            }\n            embedded_hal_async::i2c::Operation::Write(buf) => {\n                <Self as AsyncEngine>::async_write_internal(self, address, buf, BusType::I2c, SendStop::Yes).await\n            }\n        }\n    }\n}\n\nimpl<'d, M: Mode> embassy_embedded_hal::SetConfig for I3c<'d, M> {\n    type Config = Config;\n    type ConfigError = SetupError;\n\n    fn set_config(&mut self, config: &Self::Config) -> Result<(), Self::ConfigError> {\n        self.set_configuration(config)\n    }\n}\n"
  },
  {
    "path": "embassy-mcxa/src/i3c/mod.rs",
    "content": "//! I3C Support\n\nuse core::marker::PhantomData;\n\nuse embassy_hal_internal::PeripheralType;\nuse maitake_sync::WaitCell;\nuse paste::paste;\n\nuse crate::clocks::Gate;\nuse crate::clocks::periph_helpers::I3cConfig;\nuse crate::dma::{DmaChannel, DmaRequest};\nuse crate::gpio::{GpioPin, SealedPin};\nuse crate::{interrupt, pac};\n\npub mod controller;\n\n/// I3C interrupt handler.\npub struct InterruptHandler<T: Instance> {\n    _phantom: PhantomData<T>,\n}\n\nimpl<T: Instance> interrupt::typelevel::Handler<T::Interrupt> for InterruptHandler<T> {\n    unsafe fn on_interrupt() {\n        let status = T::info().regs().mintmasked().read();\n        T::PERF_INT_INCR();\n\n        if status.nowmaster()\n            || status.complete()\n            || status.mctrldone()\n            || status.slvstart()\n            || status.errwarn()\n            || status.rxpend()\n            || status.txnotfull()\n        {\n            T::info().regs().mintclr().write(|w| {\n                w.set_nowmaster(true);\n                w.set_complete(true);\n                w.set_mctrldone(true);\n                w.set_slvstart(true);\n                w.set_errwarn(true);\n                w.set_rxpend(true);\n                w.set_txnotfull(true);\n            });\n\n            T::PERF_INT_WAKE_INCR();\n            T::info().wait_cell().wake();\n        }\n    }\n}\n\nmod sealed {\n    /// Seal a trait\n    pub trait Sealed {}\n}\n\ntrait SealedInstance: Gate<MrccPeriphConfig = I3cConfig> {\n    fn info() -> &'static Info;\n\n    const PERF_INT_INCR: fn();\n    const PERF_INT_WAKE_INCR: fn();\n    const TX_DMA_REQUEST: DmaRequest;\n    const RX_DMA_REQUEST: DmaRequest;\n}\n\n/// I3C Instance\n#[allow(private_bounds)]\npub trait Instance: SealedInstance + PeripheralType + 'static + Send {\n    /// Interrupt for this I3C instance.\n    type Interrupt: interrupt::typelevel::Interrupt;\n}\n\nstruct Info {\n    regs: pac::i3c::I3c,\n    wait_cell: WaitCell,\n}\n\nunsafe impl Sync for Info {}\n\nimpl Info {\n    #[inline(always)]\n    fn regs(&self) -> pac::i3c::I3c {\n        self.regs\n    }\n\n    #[inline(always)]\n    fn wait_cell(&self) -> &WaitCell {\n        &self.wait_cell\n    }\n}\n\nmacro_rules! impl_instance {\n    ($($n:literal);*) => {\n        $(\n            paste! {\n                impl SealedInstance for crate::peripherals::[<I3C $n>] {\n                    fn info() -> &'static Info {\n                        static INFO: Info = Info {\n                            regs: pac::[<I3C $n>],\n                            wait_cell: WaitCell::new(),\n                        };\n                        &INFO\n                    }\n\n                    const TX_DMA_REQUEST: DmaRequest = DmaRequest::[<I3C $n Tx>];\n                    const RX_DMA_REQUEST: DmaRequest = DmaRequest::[<I3C $n Rx>];\n                    const PERF_INT_INCR: fn() = crate::perf_counters::[<incr_interrupt_i3c $n>];\n                    const PERF_INT_WAKE_INCR: fn() = crate::perf_counters::[<incr_interrupt_i3c $n _wake>];\n                }\n\n                impl Instance for crate::peripherals::[<I3C $n>] {\n                    type Interrupt = crate::interrupt::typelevel::[<I3C $n>];\n                }\n            }\n        )*\n    };\n}\n\nimpl_instance!(0);\n\n#[cfg(feature = \"mcxa5xx\")]\nimpl_instance!(1; 2; 3);\n\n/// SCL pin trait.\npub trait SclPin<T: Instance>: GpioPin + sealed::Sealed + PeripheralType {\n    fn mux(&self);\n}\n\n/// SDA pin trait.\npub trait SdaPin<T: Instance>: GpioPin + sealed::Sealed + PeripheralType {\n    fn mux(&self);\n}\n\n/// Driver mode.\n#[allow(private_bounds)]\npub trait Mode: sealed::Sealed {}\n\n/// Async driver mode.\n#[allow(private_bounds)]\npub trait AsyncMode: sealed::Sealed + Mode {}\n\n/// Blocking mode.\npub struct Blocking;\nimpl sealed::Sealed for Blocking {}\nimpl Mode for Blocking {}\n\n/// Async mode.\npub struct Async;\nimpl sealed::Sealed for Async {}\nimpl Mode for Async {}\nimpl AsyncMode for Async {}\n\n/// DMA mode.\npub struct Dma<'d> {\n    tx_dma: DmaChannel<'d>,\n    tx_request: DmaRequest,\n\n    rx_dma: DmaChannel<'d>,\n    rx_request: DmaRequest,\n}\nimpl sealed::Sealed for Dma<'_> {}\nimpl Mode for Dma<'_> {}\nimpl AsyncMode for Dma<'_> {}\n\nmacro_rules! impl_pin {\n    ($pin:ident, $peri:ident, $fn:ident, $trait:ident) => {\n        paste! {\n            impl sealed::Sealed for crate::peripherals::$pin {}\n\n            impl $trait<crate::peripherals::$peri> for crate::peripherals::$pin {\n                fn mux(&self) {\n                    self.set_pull(crate::gpio::Pull::Disabled);\n                    self.set_slew_rate(crate::gpio::SlewRate::Fast.into());\n                    self.set_drive_strength(crate::gpio::DriveStrength::Double.into());\n                    self.set_function(crate::pac::port::vals::Mux::$fn);\n                    self.set_enable_input_buffer(true);\n                }\n            }\n        }\n    };\n}\n\n#[cfg(feature = \"mcxa2xx\")]\nmod mcxa2xx_pins {\n    use super::*;\n    // impl_pin!(P0_2, I3C0, MUX10, PurPin); REVISIT: what is this for?\n    impl_pin!(P0_17, I3C0, MUX10, SclPin);\n    impl_pin!(P0_18, I3C0, MUX10, SdaPin);\n    impl_pin!(P1_8, I3C0, MUX10, SdaPin);\n    impl_pin!(P1_9, I3C0, MUX10, SclPin);\n    // impl_pin!(P1_11, I3C0, MUX10, PurPin); REVISIT: what is this for?\n    #[cfg(feature = \"sosc-as-gpio\")]\n    impl_pin!(P1_30, I3C0, MUX10, SdaPin);\n    #[cfg(feature = \"sosc-as-gpio\")]\n    impl_pin!(P1_31, I3C0, MUX10, SclPin);\n}\n\n#[cfg(feature = \"mcxa5xx\")]\nmod mcxa5xx_pins {\n    use super::*;\n\n    // impl_pin!(P0_2, I3C0, MUX10, PurPin); REVISIT: what is this for?\n    impl_pin!(P0_16, I3C0, MUX10, SdaPin);\n    impl_pin!(P0_17, I3C0, MUX10, SclPin);\n    impl_pin!(P0_20, I3C0, MUX10, SdaPin);\n    impl_pin!(P0_21, I3C0, MUX10, SclPin);\n    // impl_pin!(P0_22, I3C0, MUX10, PurPin); REVISIT: what is this for?\n\n    impl_pin!(P1_5, I3C1, MUX10, SdaPin);\n    impl_pin!(P1_6, I3C1, MUX10, SdaPin);\n    impl_pin!(P1_7, I3C1, MUX10, SdaPin);\n    impl_pin!(P1_8, I3C1, MUX10, SdaPin);\n    impl_pin!(P1_9, I3C1, MUX10, SclPin);\n    impl_pin!(P1_14, I3C1, MUX10, SdaPin);\n    // impl_pin!(P1_15, I3C1, MUX10, PurPin); REVISIT: what is this for?\n    impl_pin!(P1_16, I3C1, MUX10, SdaPin);\n    impl_pin!(P1_17, I3C1, MUX10, SclPin);\n    impl_pin!(P1_18, I3C1, MUX10, SdaPin);\n    impl_pin!(P1_19, I3C1, MUX10, SdaPin);\n    #[cfg(feature = \"sosc-as-gpio\")]\n    impl_pin!(P1_30, I3C2, MUX10, SdaPin);\n    #[cfg(feature = \"sosc-as-gpio\")]\n    impl_pin!(P1_31, I3C2, MUX10, SclPin);\n\n    // impl_pin!(P4_0, I3C2, MUX10, PurPin); REVISIT: what is this for?\n    // impl_pin!(P4_1, I3C3, MUX10, PurPin); REVISIT: what is this for?\n    // impl_pin!(P4_12, I3C2, MUX10, PurPin); REVISIT: what is this for?\n    // impl_pin!(P4_13, I3C3, MUX10, PurPin); REVISIT: what is this for?\n    impl_pin!(P4_2, I3C3, MUX10, SdaPin);\n    impl_pin!(P4_3, I3C2, MUX10, SclPin);\n    impl_pin!(P4_4, I3C2, MUX10, SdaPin);\n    impl_pin!(P4_5, I3C3, MUX10, SclPin);\n    impl_pin!(P4_6, I3C3, MUX10, SclPin);\n    impl_pin!(P4_7, I3C3, MUX10, SdaPin);\n}\n"
  },
  {
    "path": "embassy-mcxa/src/inputmux.rs",
    "content": "//! Input Mux driver.\n\nuse paste::paste;\n\nuse crate::clocks::periph_helpers::NoConfig;\nuse crate::clocks::{disable, enable, enable_and_reset};\nuse crate::pac;\nuse crate::peripherals::INPUTMUX0;\n\npub(crate) trait SealedValidInputMuxConfig {\n    fn mux() {}\n}\n\n/// Marker trait for valid Input mux configurations.\n#[allow(private_bounds)]\npub trait ValidInputMuxConfig: SealedValidInputMuxConfig {}\n\npub(crate) fn init() {\n    unsafe {\n        // Enable the peripheral an deassert reset.\n        let _ = enable_and_reset::<INPUTMUX0>(&NoConfig);\n\n        // INPUTMUX only needs to have its clocks ungated when\n        // accessing any of the memory mapped registers. Therefore,\n        // it's safe to disable clocks here.\n        disable::<INPUTMUX0>();\n    }\n}\n\nmacro_rules! impl_input_mux {\n    (ctimer, $value:literal, $pin:ident) => {\n        impl_input_mux!(ctimer, 0, $value, $pin);\n        impl_input_mux!(ctimer, 1, $value, $pin);\n        impl_input_mux!(ctimer, 2, $value, $pin);\n        impl_input_mux!(ctimer, 3, $value, $pin);\n        impl_input_mux!(ctimer, 4, $value, $pin);\n    };\n\n    (ctimer, $inst:literal, $value:literal, $pin:ident) => {\n        paste! {\n            impl_input_mux!([<CTIMER $inst>], [<CTIMER $inst _CH0>], $pin, [<ctimer $inst cap>], 0, $value);\n            impl_input_mux!([<CTIMER $inst>], [<CTIMER $inst _CH1>], $pin, [<ctimer $inst cap>], 1, $value);\n            impl_input_mux!([<CTIMER $inst>], [<CTIMER $inst _CH2>], $pin, [<ctimer $inst cap>], 2, $value);\n            impl_input_mux!([<CTIMER $inst>], [<CTIMER $inst _CH3>], $pin, [<ctimer $inst cap>], 3, $value);\n        }\n    };\n\n    ($peri:ident, $ch:ident, $pin:ident, $reg:ident, $n:literal, $value:literal) => {\n        paste! {\n            impl SealedValidInputMuxConfig\n                for (\n                    crate::peripherals::$peri,\n                    crate::peripherals::$ch,\n                    crate::peripherals::$pin,\n                )\n            {\n                #[inline(always)]\n                fn mux() {\n                    let _ = unsafe { enable::<INPUTMUX0>(&NoConfig) };\n                    pac::INPUTMUX0.[<$reg>]($n).write(|w| w.set_inp([<$value>].into()));\n                    unsafe { disable::<INPUTMUX0>() };\n\n                }\n            }\n\n            impl ValidInputMuxConfig\n                for (\n                    crate::peripherals::$peri,\n                    crate::peripherals::$ch,\n                    crate::peripherals::$pin,\n                )\n            {\n            }\n        }\n    };\n}\n\n#[cfg(feature = \"swd-as-gpio\")]\nimpl_input_mux!(ctimer, 2, P0_1);\n#[cfg(feature = \"jtag-extras-as-gpio\")]\nimpl_input_mux!(ctimer, 3, P0_6);\n\nimpl_input_mux!(ctimer, 1, P0_20);\nimpl_input_mux!(ctimer, 2, P0_21);\nimpl_input_mux!(ctimer, 3, P0_22);\nimpl_input_mux!(ctimer, 4, P0_23);\n\nimpl_input_mux!(ctimer, 5, P1_0);\nimpl_input_mux!(ctimer, 6, P1_1);\nimpl_input_mux!(ctimer, 1, P1_2);\nimpl_input_mux!(ctimer, 2, P1_3);\nimpl_input_mux!(ctimer, 7, P1_6);\nimpl_input_mux!(ctimer, 8, P1_7);\nimpl_input_mux!(ctimer, 9, P1_8);\nimpl_input_mux!(ctimer, 10, P1_9);\nimpl_input_mux!(ctimer, 11, P1_14);\nimpl_input_mux!(ctimer, 12, P1_15);\n\n#[cfg(feature = \"sosc-as-gpio\")]\nimpl_input_mux!(ctimer, 17, P1_30);\n#[cfg(feature = \"sosc-as-gpio\")]\nimpl_input_mux!(ctimer, 18, P1_31);\n\nimpl_input_mux!(ctimer, 17, P2_0);\nimpl_input_mux!(ctimer, 18, P2_1);\nimpl_input_mux!(ctimer, 13, P2_2);\nimpl_input_mux!(ctimer, 14, P2_3);\nimpl_input_mux!(ctimer, 15, P2_4);\nimpl_input_mux!(ctimer, 16, P2_5);\nimpl_input_mux!(ctimer, 19, P2_6);\nimpl_input_mux!(ctimer, 20, P2_7);\n\nimpl_input_mux!(ctimer, 17, P3_0);\nimpl_input_mux!(ctimer, 18, P3_1);\nimpl_input_mux!(ctimer, 5, P3_8);\nimpl_input_mux!(ctimer, 6, P3_9);\nimpl_input_mux!(ctimer, 7, P3_14);\nimpl_input_mux!(ctimer, 8, P3_15);\nimpl_input_mux!(ctimer, 9, P3_16);\nimpl_input_mux!(ctimer, 10, P3_17);\nimpl_input_mux!(ctimer, 11, P3_22);\n#[cfg(feature = \"mcxa2xx\")]\nimpl_input_mux!(ctimer, 14, P3_27);\n#[cfg(feature = \"mcxa2xx\")]\nimpl_input_mux!(ctimer, 13, P3_28);\n#[cfg(all(feature = \"dangerous-reset-as-gpio\", feature = \"mcxa2xx\"))]\nimpl_input_mux!(ctimer, 4, P3_29);\n\nimpl_input_mux!(ctimer, 7, P4_6);\nimpl_input_mux!(ctimer, 8, P4_7);\n"
  },
  {
    "path": "embassy-mcxa/src/lib.rs",
    "content": "#![no_std]\n#![allow(async_fn_in_trait)]\n#![doc = include_str!(\"../README.md\")]\n// Clippy Exceptions\n//\n// Allow functions with too many args - we have a lot of HAL constructors like this for now\n#![allow(clippy::too_many_arguments)]\n\n/// Module for MCXA2xx-specific HAL drivers\n///\n/// NOTE: *for now*, some items are here because we haven't validated them on the MCXA5xx yet.\n/// This note will be removed when the two reach parity.\n#[cfg(feature = \"mcxa2xx\")]\n#[path = \".\"]\nmod mcxa2xx_exclusive {\n    pub mod flash;\n\n    pub use crate::chips::mcxa2xx::{Peripherals, init, interrupt, peripherals};\n}\n\n/// Module for MCXA5xx-specific HAL drivers\n#[cfg(feature = \"mcxa5xx\")]\n#[path = \".\"]\nmod mcxa5xx_exclusive {\n    pub use crate::chips::mcxa5xx::{Peripherals, init, interrupt, peripherals};\n}\n\n/// Module for HAL drivers supported by all chips\n#[path = \".\"]\nmod all_chips {\n    pub mod adc;\n    pub mod cdog;\n    pub mod clkout;\n    pub mod clocks;\n    pub mod config;\n    pub mod crc;\n    pub mod ctimer;\n    pub mod dma;\n    #[cfg(feature = \"executor-platform\")]\n    pub mod executor;\n    pub mod gpio;\n    pub mod i2c;\n    pub mod i3c;\n    pub mod inputmux;\n    pub mod lpuart;\n    pub mod ostimer;\n    pub mod perf_counters;\n    pub mod reset_reason;\n    pub mod rtc;\n    pub mod spi;\n    pub mod trng;\n    pub mod wwdt;\n}\n\n#[allow(unused_imports)]\npub use all_chips::*;\n#[cfg(feature = \"mcxa2xx\")]\npub use mcxa2xx_exclusive::*;\n#[cfg(feature = \"mcxa5xx\")]\npub use mcxa5xx_exclusive::*;\n\npub(crate) mod chips;\n\n// Re-export interrupt traits and types\n// Re-export Peri and PeripheralType to allow applications to express Peri types and requirements.\npub use embassy_hal_internal::{Peri, PeripheralType};\n#[cfg(feature = \"unstable-pac\")]\npub use nxp_pac as pac;\n#[cfg(not(feature = \"unstable-pac\"))]\npub(crate) use nxp_pac as pac;\n\nconst HALS_SELECTED: usize = const { cfg!(feature = \"mcxa2xx\") as usize + cfg!(feature = \"mcxa5xx\") as usize };\n\n/// Ensure exactly one chip feature is set.\n#[doc(hidden)]\npub const _SINGLE_HAL_CHECK: bool = const {\n    assert!(HALS_SELECTED == 1, \"Select exactly one chip feature!\");\n    HALS_SELECTED == 1\n};\n\n/// Macro to bind interrupts to handlers, similar to embassy-imxrt.\n///\n/// Example:\n/// - Bind OS_EVENT to the OSTIMER time-driver handler\n///   bind_interrupts!(struct Irqs { OS_EVENT => crate::ostimer::time_driver::OsEventHandler; });\n#[macro_export]\nmacro_rules! bind_interrupts {\n    ($(#[$attr:meta])* $vis:vis struct $name:ident {\n        $(\n            $(#[cfg($cond_irq:meta)])?\n            $irq:ident => $(\n                $(#[cfg($cond_handler:meta)])?\n                $handler:ty\n            ),*;\n        )*\n    }) => {\n        #[derive(Copy, Clone)]\n        $(#[$attr])*\n        $vis struct $name;\n\n        $(\n            #[allow(non_snake_case)]\n            #[unsafe(no_mangle)]\n            $(#[cfg($cond_irq)])?\n            unsafe extern \"C\" fn $irq() {\n                unsafe {\n                    $(\n                        $(#[cfg($cond_handler)])?\n                        <$handler as $crate::interrupt::typelevel::Handler<$crate::interrupt::typelevel::$irq>>::on_interrupt();\n                    )*\n                }\n            }\n\n            $(#[cfg($cond_irq)])?\n            $crate::bind_interrupts!(@inner\n                $(\n                    $(#[cfg($cond_handler)])?\n                    unsafe impl $crate::interrupt::typelevel::Binding<$crate::interrupt::typelevel::$irq, $handler> for $name {}\n                )*\n            );\n        )*\n    };\n    (@inner $($t:tt)*) => {\n        $($t)*\n    }\n}\n"
  },
  {
    "path": "embassy-mcxa/src/lpuart/bbq.rs",
    "content": "//! Buffered Lpuart driver powered by `bbqueue`\n\n#![deny(clippy::undocumented_unsafe_blocks)]\n\nuse core::marker::PhantomData;\nuse core::ptr::NonNull;\nuse core::sync::atomic::{AtomicU8, AtomicU32, Ordering, fence};\n\nuse bbqueue::BBQueue;\nuse bbqueue::prod_cons::stream::{StreamGrantR, StreamGrantW};\nuse bbqueue::traits::coordination::cas::AtomicCoord;\nuse bbqueue::traits::notifier::maitake::MaiNotSpsc;\nuse bbqueue::traits::storage::Storage;\nuse embassy_hal_internal::Peri;\nuse grounded::uninit::GroundedCell;\nuse maitake_sync::WaitCell;\nuse nxp_pac::lpuart::vals::Tc;\nuse paste::paste;\n\nuse super::{DataBits, IdleConfig, Info, MsbFirst, Parity, RxPin, StopBits, TxPin, TxPins};\nuse crate::clocks::periph_helpers::{Div4, LpuartClockSel};\nuse crate::clocks::{PoweredClock, WakeGuard};\nuse crate::dma::{DMA_MAX_TRANSFER_SIZE, DmaChannel, DmaRequest, InvalidParameters, TransferOptions};\nuse crate::gpio::{AnyPin, HasGpioInstance, PeriGpioExt};\nuse crate::interrupt::typelevel::{Binding, Handler, Interrupt};\nuse crate::lpuart::{Instance, RxPins};\n\n/// Error Type\n#[derive(Debug, PartialEq)]\n#[non_exhaustive]\npub enum BbqError {\n    /// Errors from LPUart setup\n    Basic(super::Error),\n    /// Could not initialize a new instance as the current instance is already in use\n    Busy,\n    /// Attempted to create an Rx half with Tx parts, or a Tx half with Rx parts\n    WrongParts,\n    /// Requested an [`RxMode::MaxFrame`] too large for the provided buffer\n    MaxFrameTooLarge,\n}\n\nimpl core::fmt::Display for BbqError {\n    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {\n        <Self as core::fmt::Debug>::fmt(self, f)\n    }\n}\n\nimpl core::error::Error for BbqError {}\n\n/// RX Reception mode\n#[derive(Debug, Clone, Copy, Default)]\n#[non_exhaustive]\npub enum BbqRxMode {\n    /// Default mode, attempts to utilize the ring buffer as maximally as possible.\n    ///\n    /// In this mode, the interrupt will use whatever space is available, up to 1/4\n    /// the total ring buffer size, or the max DMA transfer size, whichever is smaller.\n    /// however this may mean that if we are at the \"end\" of the ring buffer,\n    /// some transfers may be smaller, meaning we need to \"reload\" the interrupt\n    /// more often.\n    ///\n    /// At slower UART rates (like 115_200), this is probably acceptable, as we have\n    /// roughly 347us to service the \"end of transfer\" interrupt and reload the next\n    /// DMA transfer. However at higher speeds (like 4_000_000), this time shrinks to\n    /// 10us, meaning that critical sections (including defmt logging) may cause us to\n    /// lose data.\n    ///\n    /// If you know your maximum frame/burst size, you can instead use [`RxMode::MaxFrame`],\n    /// which will never allow \"short\" grants, with the trade off that we may reduce the\n    /// total usable capacity temporarily if we need to wrap around the ring buffer early.\n    #[default]\n    Efficiency,\n\n    /// Max Frame mode, ensures that dma transfers always have exactly `size` bytes available\n    ///\n    /// In this mode, we will always make DMA transfers of the given size. This is intended for\n    /// cases where we are receving bursts of data <= `size`, ideally with a short gap between\n    /// bursts. This means that we will receive an IDLE interrupt, and switch over receiving grants\n    /// in the quiet period, avoiding potentially latency-sensitive DMA transfer updates while\n    /// data is still being transferred. This is especially useful at higher baudrates.\n    ///\n    /// The tradeoff here is that we can temporarily \"waste\" up to `(size - 1)` bytes if we\n    /// are forced to wrap-around the ring buffer early. For example if there is only 1023 bytes\n    /// in the ring buffer before it wraps around, and `size = 1024`, we will be forced to wrap\n    /// around the ring early, skipping that capacity. In some cases, where the required 1024\n    /// bytes are not available at the beginning of the ring buffer either, we will not begin\n    /// a transfer at all, potentially losing data if capacity is not freed up before the next\n    /// transfer starts (each time the ring buffer is drained, we will automatically re-start\n    /// receiving if enough capacity is made available).\n    ///\n    /// `size` must be <= (capacity / 4).\n    MaxFrame { size: usize },\n}\n\n/// Lpuart config\n#[derive(Debug, Clone, Copy)]\n#[non_exhaustive]\npub struct BbqConfig {\n    /// Power state required for this peripheral\n    pub power: PoweredClock,\n    /// Clock source\n    pub source: LpuartClockSel,\n    /// Clock divisor\n    pub div: Div4,\n    /// Baud rate in bits per second\n    pub baudrate_bps: u32,\n    /// Parity configuration\n    pub parity_mode: Option<Parity>,\n    /// Number of data bits\n    pub data_bits_count: DataBits,\n    /// MSB First or LSB First configuration\n    pub msb_first: MsbFirst,\n    /// Number of stop bits\n    pub stop_bits_count: StopBits,\n    /// RX IDLE configuration\n    pub rx_idle_config: IdleConfig,\n}\n\nimpl Default for BbqConfig {\n    fn default() -> Self {\n        Self {\n            baudrate_bps: 115_200u32,\n            parity_mode: None,\n            data_bits_count: DataBits::DATA8,\n            msb_first: MsbFirst::LSB_FIRST,\n            stop_bits_count: StopBits::ONE,\n            rx_idle_config: IdleConfig::IDLE_1,\n            power: PoweredClock::AlwaysEnabled,\n            source: LpuartClockSel::FroLfDiv,\n            div: Div4::no_div(),\n        }\n    }\n}\n\nimpl From<BbqConfig> for super::Config {\n    fn from(value: BbqConfig) -> Self {\n        let mut cfg = super::Config::default();\n        let BbqConfig {\n            power,\n            source,\n            div,\n            baudrate_bps,\n            parity_mode,\n            data_bits_count,\n            msb_first,\n            stop_bits_count,\n            rx_idle_config,\n        } = value;\n\n        // User selectable\n        cfg.power = power;\n        cfg.source = source;\n        cfg.div = div;\n        cfg.baudrate_bps = baudrate_bps;\n        cfg.parity_mode = parity_mode;\n        cfg.data_bits_count = data_bits_count;\n        cfg.msb_first = msb_first;\n        cfg.stop_bits_count = stop_bits_count;\n        cfg.rx_idle_config = rx_idle_config;\n\n        // Manually set\n        cfg.tx_fifo_watermark = 0;\n        cfg.rx_fifo_watermark = 0;\n        cfg.swap_txd_rxd = false;\n\n        cfg\n    }\n}\n\n/// A `bbqueue` powered buffered Lpuart\npub struct LpuartBbq {\n    // TODO: Don't just make these pub, we don't *really* handle dropping/recreation\n    // of separate parts at the moment.\n    /// The TX half of the LPUART\n    tx: LpuartBbqTx,\n    /// The RX half of the LPUART\n    rx: LpuartBbqRx,\n}\n\n#[derive(Copy, Clone)]\nstruct BbqVtable {\n    lpuart_init: fn(bool, bool, bool, bool, super::Config) -> Result<Option<WakeGuard>, super::Error>,\n    int_pend: fn(),\n    int_unpend: fn(),\n    int_disable: fn(),\n    dma_rx_cb: fn(),\n    int_enable: unsafe fn(),\n}\n\nimpl BbqVtable {\n    fn for_lpuart<T: BbqInstance>() -> Self {\n        Self {\n            int_pend: T::Interrupt::pend,\n            int_unpend: T::Interrupt::unpend,\n            int_disable: T::Interrupt::disable,\n            int_enable: T::Interrupt::enable,\n            dma_rx_cb: T::dma_rx_complete_cb,\n            lpuart_init: super::Lpuart::<'static, super::Blocking>::init::<T>,\n        }\n    }\n}\n\n#[derive(PartialEq, Copy, Clone)]\nenum WhichHalf {\n    Rx,\n    Tx,\n}\n\npub struct BbqHalfParts {\n    // resources\n    buffer: &'static mut [u8],\n    dma_ch: DmaChannel<'static>,\n    pin: Peri<'static, AnyPin>,\n\n    // type erasure\n    which: WhichHalf,\n    dma_req: u8,\n    mux: crate::pac::port::vals::Mux,\n    info: &'static Info,\n    state: &'static BbqState,\n    vtable: BbqVtable,\n}\n\npub struct BbqParts {\n    // resources\n    tx_buffer: &'static mut [u8],\n    tx_dma_ch: DmaChannel<'static>,\n    tx_pin: Peri<'static, AnyPin>,\n    rx_buffer: &'static mut [u8],\n    rx_dma_ch: DmaChannel<'static>,\n    rx_pin: Peri<'static, AnyPin>,\n\n    // type erasure\n    tx_dma_req: u8,\n    tx_mux: crate::pac::port::vals::Mux,\n    rx_dma_req: u8,\n    rx_mux: crate::pac::port::vals::Mux,\n    info: &'static Info,\n    state: &'static BbqState,\n    vtable: BbqVtable,\n}\n\nimpl BbqParts {\n    pub fn new<T: BbqInstance, Tx: TxPin<T>, Rx: RxPin<T>>(\n        _inner: Peri<'static, T>,\n        _irq: impl Binding<T::Interrupt, BbqInterruptHandler<T>> + 'static,\n        tx_pin: Peri<'static, Tx>,\n        tx_buffer: &'static mut [u8],\n        tx_dma_ch: impl Into<DmaChannel<'static>>,\n        rx_pin: Peri<'static, Rx>,\n        rx_buffer: &'static mut [u8],\n        rx_dma_ch: impl Into<DmaChannel<'static>>,\n    ) -> Result<Self, BbqError> {\n        Ok(Self {\n            tx_buffer,\n            tx_dma_ch: tx_dma_ch.into(),\n            tx_pin: tx_pin.into(),\n            rx_buffer,\n            rx_dma_ch: rx_dma_ch.into(),\n            rx_pin: rx_pin.into(),\n            tx_dma_req: T::TX_DMA_REQUEST.number(),\n            tx_mux: Tx::MUX,\n            rx_dma_req: T::RX_DMA_REQUEST.number(),\n            rx_mux: Rx::MUX,\n            info: T::info(),\n            state: T::bbq_state(),\n            vtable: BbqVtable::for_lpuart::<T>(),\n        })\n    }\n\n    /// Access a borrow of the contained RX pin\n    pub fn rx_pin(&mut self) -> Peri<'_, AnyPin> {\n        self.rx_pin.reborrow()\n    }\n\n    /// Access a borrow of the contained TX pin\n    pub fn tx_pin(&mut self) -> Peri<'_, AnyPin> {\n        self.tx_pin.reborrow()\n    }\n\n    /// Access a borrow of both the RX and TX pin (in that order)\n    pub fn pins(&mut self) -> (Peri<'_, AnyPin>, Peri<'_, AnyPin>) {\n        let Self { tx_pin, rx_pin, .. } = self;\n        (rx_pin.reborrow(), tx_pin.reborrow())\n    }\n}\n\nimpl BbqHalfParts {\n    pub fn pin(&mut self) -> Peri<'_, AnyPin> {\n        self.pin.reborrow()\n    }\n\n    pub fn new_tx_half<T: BbqInstance, P: TxPin<T>>(\n        _inner: Peri<'static, T>,\n        _irq: impl Binding<T::Interrupt, BbqInterruptHandler<T>> + 'static,\n        tx_pin: Peri<'static, P>,\n        buffer: &'static mut [u8],\n        dma_ch: impl Into<DmaChannel<'static>>,\n    ) -> Self {\n        Self {\n            buffer,\n            dma_ch: dma_ch.into(),\n            pin: tx_pin.into(),\n            mux: P::MUX,\n            info: T::info(),\n            state: T::bbq_state(),\n            dma_req: T::TX_DMA_REQUEST.number(),\n            vtable: BbqVtable::for_lpuart::<T>(),\n            which: WhichHalf::Tx,\n        }\n    }\n\n    pub fn new_rx_half<T: BbqInstance, P: RxPin<T>>(\n        _inner: Peri<'static, T>,\n        _irq: impl Binding<T::Interrupt, BbqInterruptHandler<T>> + 'static,\n        tx_pin: Peri<'static, P>,\n        buffer: &'static mut [u8],\n        dma_ch: impl Into<DmaChannel<'static>>,\n    ) -> Self {\n        Self {\n            buffer,\n            dma_ch: dma_ch.into(),\n            pin: tx_pin.into(),\n            mux: P::MUX,\n            info: T::info(),\n            state: T::bbq_state(),\n            dma_req: T::RX_DMA_REQUEST.number(),\n            vtable: BbqVtable::for_lpuart::<T>(),\n            which: WhichHalf::Rx,\n        }\n    }\n\n    /// Setup Rx half while binding GPIO to the gpio pin.\n    /// This allows later use of async functions on the pin.\n    pub fn new_rx_half_async<T: BbqInstance, P: RxPin<T> + HasGpioInstance>(\n        _inner: Peri<'static, T>,\n        irq: impl Binding<T::Interrupt, BbqInterruptHandler<T>>\n        + Binding<<P::Instance as crate::gpio::Instance>::Interrupt, crate::gpio::InterruptHandler<P::Instance>>\n        + 'static,\n        tx_pin: Peri<'static, P>,\n        buffer: &'static mut [u8],\n        dma_ch: impl Into<DmaChannel<'static>>,\n    ) -> Self {\n        Self {\n            buffer,\n            dma_ch: dma_ch.into(),\n            pin: tx_pin.degrade_async(irq),\n            mux: P::MUX,\n            info: T::info(),\n            state: T::bbq_state(),\n            dma_req: T::RX_DMA_REQUEST.number(),\n            vtable: BbqVtable::for_lpuart::<T>(),\n            which: WhichHalf::Rx,\n        }\n    }\n}\n\nimpl LpuartBbq {\n    /// Create a new LpuartBbq with both transmit and receive halves\n    pub fn new(parts: BbqParts, config: BbqConfig, mode: BbqRxMode) -> Result<Self, BbqError> {\n        // Get state for this instance, and try to move from the \"uninit\" to \"initing\" state\n        parts.state.uninit_to_initing()?;\n\n        // Set as TX/RX pin mode\n        any_as_tx(&parts.tx_pin, parts.tx_mux);\n        any_as_rx(&parts.rx_pin, parts.rx_mux);\n\n        // Configure UART peripheral\n        // TODO make this a specific Bbq mode instead of using blocking\n        // TODO support CTS/RTS pins?\n\n        let _wg = (parts.vtable.lpuart_init)(true, true, false, false, config.into()).map_err(BbqError::Basic)?;\n\n        // Setup the TX state\n        //\n        // SAFETY: We have ensured we are in the INITING state, and interrupts are not yet active.\n        unsafe {\n            LpuartBbqTx::initialize_tx_state(parts.state, parts.tx_dma_ch, parts.tx_buffer, parts.tx_dma_req);\n        }\n\n        // Setup the RX state\n        let len = parts.rx_buffer.len();\n        // SAFETY: We have ensured we are in the INITING state, and the interrupt is not yet active.\n        unsafe {\n            LpuartBbqRx::initialize_rx_state(\n                parts.state,\n                parts.info,\n                parts.rx_dma_ch,\n                parts.vtable.dma_rx_cb,\n                parts.rx_buffer,\n                parts.rx_dma_req,\n            );\n        }\n\n        // Update our state to \"initialized\", and that we have the TXDMA + RXDMA channels present\n        // Okay to just store: we have exclusive access\n        let max_size = (len / 4).min(DMA_MAX_TRANSFER_SIZE);\n        let rx_mode_bits = match mode {\n            BbqRxMode::Efficiency => (max_size as u32) << 16,\n            BbqRxMode::MaxFrame { size } => {\n                if size > max_size {\n                    return Err(BbqError::MaxFrameTooLarge);\n                }\n                let size = (size as u32) << 16;\n                size | STATE_RXDMA_MODE_MAXFRAME\n            }\n        };\n        let new_state = STATE_INITED | STATE_TXDMA_PRESENT | STATE_RXDMA_PRESENT | rx_mode_bits;\n        parts.state.state.store(new_state, Ordering::Release);\n\n        // SAFETY: We have ensured that our ISR is present via the IRQ token, and we have\n        // initialized the shared state machine sufficiently that it can execute correctly\n        // when triggered.\n        unsafe {\n            // Clear any stale interrupt flags\n            (parts.vtable.int_unpend)();\n            // Enable the LPUART interrupt\n            (parts.vtable.int_enable)();\n            // Immediately pend the interrupt, this will \"load\" the DMA transfer as the\n            // ISR will notice that there is no active grant. This means that we start\n            // receiving immediately without additional user interaction.\n            (parts.vtable.int_pend)();\n        }\n\n        Ok(Self {\n            tx: LpuartBbqTx {\n                state: parts.state,\n                info: parts.info,\n                vtable: parts.vtable,\n                mux: parts.tx_mux,\n                _tx_pins: TxPins {\n                    tx_pin: parts.tx_pin,\n                    cts_pin: None,\n                },\n                _wg: _wg.clone(),\n            },\n            rx: LpuartBbqRx {\n                state: parts.state,\n                info: parts.info,\n                vtable: parts.vtable,\n                mux: parts.rx_mux,\n                _rx_pins: RxPins {\n                    rx_pin: parts.rx_pin,\n                    rts_pin: None,\n                },\n                _wg,\n            },\n        })\n    }\n\n    /// Write some data to the buffer. See [`LpuartBbqTx::write`] for more information\n    pub fn write(&mut self, buf: &[u8]) -> impl Future<Output = Result<usize, BbqError>> {\n        self.tx.write(buf)\n    }\n\n    /// Read some data from the buffer. See [`LpuartBbqRx::read`] for more information\n    pub fn read(&mut self, buf: &mut [u8]) -> impl Future<Output = Result<usize, BbqError>> {\n        self.rx.read(buf)\n    }\n\n    /// Wait for all bytes in the outgoing buffer to be flushed asynchronously.\n    ///\n    /// See [`LpuartBbqTx::flush`] for more information\n    pub fn flush(&mut self) -> impl Future<Output = ()> {\n        self.tx.flush()\n    }\n\n    /// Busy wait until all transmitting has completed\n    ///\n    /// See [`LpuartBbqTx::blocking_flush`] for more information\n    pub fn blocking_flush(&mut self) {\n        self.tx.blocking_flush();\n    }\n\n    /// Borrow split parts.\n    pub fn split_ref(&mut self) -> (&mut LpuartBbqRx, &mut LpuartBbqTx) {\n        let Self { tx, rx } = self;\n        (rx, tx)\n    }\n\n    /// Teardown the LpuartBbq, retrieving the original parts\n    pub fn teardown(self) -> BbqParts {\n        let Self { tx, rx } = self;\n        let tx_parts = tx.teardown();\n        let rx_parts = rx.teardown();\n        BbqParts {\n            tx_buffer: tx_parts.buffer,\n            tx_dma_ch: tx_parts.dma_ch,\n            tx_pin: tx_parts.pin,\n            rx_buffer: rx_parts.buffer,\n            rx_dma_ch: rx_parts.dma_ch,\n            rx_pin: rx_parts.pin,\n            tx_dma_req: tx_parts.dma_req,\n            tx_mux: tx_parts.mux,\n            rx_dma_req: rx_parts.dma_req,\n            rx_mux: rx_parts.mux,\n            info: tx_parts.info,\n            state: tx_parts.state,\n            vtable: tx_parts.vtable,\n        }\n    }\n}\n\n/// A `bbqueue` powered Lpuart TX Half\npub struct LpuartBbqTx {\n    state: &'static BbqState,\n    info: &'static Info,\n    vtable: BbqVtable,\n    mux: crate::pac::port::vals::Mux,\n    _tx_pins: TxPins<'static>,\n    _wg: Option<WakeGuard>,\n}\n\nimpl LpuartBbqTx {\n    /// ## SAFETY\n    ///\n    /// This function must only be called in the \"INITING\" state, and BEFORE\n    /// enabling interrupts, meaning we have exclusive access to the TX components\n    /// of the given BbqState.\n    unsafe fn initialize_tx_state(\n        state: &'static BbqState,\n        dma: DmaChannel<'static>,\n        tx_buffer: &'static mut [u8],\n        request_num: u8,\n    ) {\n        // Enable the DMA interrupt to handle \"transfer complete\" interrupts\n        dma.enable_interrupt();\n\n        // Setup the TX bbqueue instance, store the DMA channel and bbqueue in the\n        // BbqState storage location.\n        //\n        // TODO: We could probably be more clever and setup the DMA transfer request\n        // number ONCE in init, then just do a minimal-reload. This would allow us to\n        // avoid storing the txdma_num, and save some effort in the ISR.\n        let cont = Container::from(tx_buffer);\n\n        // SAFETY: We have exclusive access to the shared TX components, and the interrupt\n        // is not yet enabled. We move ownership of these resources to the shared area.\n        unsafe {\n            state.tx_queue.get().write(BBQueue::new_with_storage(cont));\n            state.txdma.get().write(dma);\n            state.txdma_num.store(request_num, Ordering::Release);\n        }\n    }\n\n    /// Create a new LpuartBbq with only the transmit half\n    ///\n    /// NOTE: Dropping the `LpuartBbqTx` will *permanently* leak the TX buffer, DMA channel, and tx pin.\n    /// Call [LpuartBbqTx::teardown] to reclaim these resources.\n    pub fn new(parts: BbqHalfParts, config: BbqConfig) -> Result<Self, BbqError> {\n        // Are these the right parts?\n        if parts.which != WhichHalf::Tx {\n            return Err(BbqError::WrongParts);\n        }\n\n        // Get state for this instance, and try to move from the \"uninit\" to \"initing\" state\n        parts.state.uninit_to_initing()?;\n\n        // Set as TX pin mode\n        any_as_tx(&parts.pin, parts.mux);\n\n        // Configure UART peripheral\n        // TODO make this a specific Bbq mode instead of using blocking\n        // TODO support CTS pin?\n        let _wg = (parts.vtable.lpuart_init)(true, false, false, false, config.into()).map_err(BbqError::Basic)?;\n\n        // Setup the TX Half state\n        //\n        // SAFETY: We have ensured we are in the INITING state, and the interrupt is not yet active.\n        unsafe {\n            Self::initialize_tx_state(parts.state, parts.dma_ch, parts.buffer, parts.dma_req);\n        }\n\n        // Update our state to \"initialized\", and that we have the TXDMA channel present\n        // Okay to just store: we have exclusive access\n        let new_state = STATE_INITED | STATE_TXDMA_PRESENT;\n        parts.state.state.store(new_state, Ordering::Release);\n\n        // SAFETY: We have properly initialized the shared storage, and ensured that\n        // our ISR is installed with the Irq token.\n        unsafe {\n            // Clear any stale interrupt flags\n            (parts.vtable.int_unpend)();\n            // Enable the LPUART interrupt\n            (parts.vtable.int_enable)();\n            // NOTE: Unlike RX, we don't begin transmitting immediately, we move\n            // from Idle -> Transmitting the first time the user calls write.\n        }\n\n        Ok(Self {\n            state: parts.state,\n            info: parts.info,\n            vtable: parts.vtable,\n            _tx_pins: TxPins {\n                tx_pin: parts.pin,\n                cts_pin: None,\n            },\n            _wg,\n            mux: parts.mux,\n        })\n    }\n\n    /// Write some data to the outgoing transmit buffer\n    ///\n    /// This method waits until some data is able to be written to the internal buffer,\n    /// and returns the number of bytes from `buf` consumed.\n    ///\n    /// This does NOT guarantee all bytes of `buf` have been buffered, only the amount returned.\n    ///\n    /// This does NOT guarantee the bytes have been written to the wire. See [`Self::flush()`].\n    pub async fn write(&mut self, buf: &[u8]) -> Result<usize, BbqError> {\n        // TODO: we could have a version of this that gives the user the grant directly\n        // to reduce the effort of copying.\n\n        // SAFETY: The existence of a LpuartBbqTx ensures that the `tx_queue` has been\n        // initialized. The tx_queue is safe to access in a shared manner after initialization.\n        let tx_queue = unsafe { &*self.state.tx_queue.get() };\n\n        let prod = tx_queue.stream_producer();\n        let mut wgr = prod.wait_grant_max_remaining(buf.len()).await;\n        let to_copy = buf.len().min(wgr.len());\n        wgr[..to_copy].copy_from_slice(&buf[..to_copy]);\n        wgr.commit(to_copy);\n        (self.vtable.int_pend)();\n\n        Ok(to_copy)\n    }\n\n    /// Wait for all bytes in the outgoing buffer to be flushed asynchronously.\n    ///\n    /// When this method completes, the outgoing buffer is empty.\n    pub async fn flush(&mut self) {\n        // Discard the result on wait_for as we never close the waiter.\n        let _ = self\n            .state\n            .tx_flushed\n            .wait_for(|| {\n                // We are idle when there is no TXGR active\n                (self.state.state.load(Ordering::Acquire) & STATE_TXGR_ACTIVE) == 0\n            })\n            .await;\n    }\n\n    /// Busy wait until all transmitting has completed\n    ///\n    /// When this method completes, the outgoing buffer is empty.\n    pub fn blocking_flush(&mut self) {\n        while (self.state.state.load(Ordering::Acquire) & STATE_TXGR_ACTIVE) != 0 {}\n    }\n\n    /// Teardown the Tx handle, reclaiming the parts.\n    pub fn teardown(self) -> BbqHalfParts {\n        // First, disable relevant interrupts\n        let state = critical_section::with(|_cs| {\n            self.info.regs.ctrl().modify(|w| w.set_tcie(false));\n            // Clear the TXDMA present bit to prevent the ISR from touching anything.\n            // Relaxed is okay here because CS::with has Acq/Rel semantics on entry and exit\n            self.state.state.fetch_and(!STATE_TXDMA_PRESENT, Ordering::Relaxed)\n        });\n\n        // If there is an active grant, the TX DMA may be active. Stop it and release the grant\n        if (state & STATE_TXGR_ACTIVE) != 0 {\n            // SAFETY: We have unset TXDMA_PRESENT and disabled TCIE: we now have exclusive\n            // access to the shared tx resources.\n            unsafe {\n                // Take DMA channel by mut ref\n                let txdma = &mut *self.state.txdma.get();\n\n                // Stop the DMA\n                self.info.regs().baud().modify(|w| w.set_tdmae(false));\n                txdma.disable_request();\n                txdma.clear_done();\n                fence(Ordering::Acquire);\n\n                // Then take the grant by ownership, and drop it, which releases the grant\n                _ = self.state.txgr.get().read();\n            }\n            self.state.state.fetch_and(!STATE_TXGR_ACTIVE, Ordering::AcqRel);\n        }\n\n        // Get a reference to the tx_queue to retrieve the Container\n        //\n        // SAFETY: We have unset TXDMA_PRESENT and disabled TCIE: we now have exclusive\n        // access to the shared tx resources.\n        let (ptr, len) = unsafe {\n            let tx_queue = &*self.state.tx_queue.get();\n            tx_queue.storage().ptr_len()\n        };\n\n        // Now, drop the queue in place. This is sound because as the LpuartBbqTx, we have exclusive\n        // access to the \"producer\" half, and by disabling the interrupt and notching out the state\n        // bits, we know the ISR will no longer touch the consumer part.\n        //\n        // Also, take the DmaChannel by ownership this time.\n        //\n        // SAFETY: We have unset TXDMA_PRESENT and disabled TCIE: we now have exclusive\n        // access to the shared tx resources.\n        let tx_dma = unsafe {\n            core::ptr::drop_in_place(self.state.tx_queue.get());\n            // Defensive coding: purge the tx_queue just in case. This doesn't zero the\n            // whole buffer, only the tracking pointers.\n            core::ptr::write_bytes(self.state.tx_queue.get(), 0, 1);\n            let mut dma = self.state.txdma.get().read();\n            dma.clear_callback();\n            dma\n        };\n\n        // Re-magic the mut slice from the storage we have now reclaimed by dropping the\n        // tx_queue.\n        //\n        // SAFETY: We have unset TXDMA_PRESENT and disabled TCIE: we now have exclusive\n        // access to the shared tx resources.\n        let tx_buffer = unsafe { core::slice::from_raw_parts_mut(ptr.as_ptr(), len) };\n\n        // Now, if this was the last part of the lpuart, we are responsible for peripheral\n        // cleanup.\n        if (state & !(STATE_TXGR_ACTIVE | STATE_TXDMA_PRESENT)) == STATE_INITED {\n            (self.vtable.int_disable)();\n            super::disable_peripheral(self.info);\n            self.state.state.store(STATE_UNINIT, Ordering::Relaxed);\n        }\n\n        let (tx_pin, _) = self._tx_pins.take();\n\n        BbqHalfParts {\n            buffer: tx_buffer,\n            dma_ch: tx_dma,\n            pin: tx_pin,\n            dma_req: self.state.txdma_num.load(Ordering::Relaxed),\n            mux: self.mux,\n            info: self.info,\n            state: self.state,\n            vtable: self.vtable,\n            which: WhichHalf::Tx,\n        }\n    }\n}\n\nuse embedded_io_async::ErrorType;\nimpl embedded_io_async::Error for BbqError {\n    fn kind(&self) -> embedded_io::ErrorKind {\n        match self {\n            BbqError::Basic(error) => error.kind(),\n            BbqError::Busy => embedded_io::ErrorKind::Other,\n            BbqError::WrongParts => embedded_io::ErrorKind::Other,\n            BbqError::MaxFrameTooLarge => embedded_io::ErrorKind::OutOfMemory,\n        }\n    }\n}\nimpl ErrorType for LpuartBbqTx {\n    type Error = BbqError;\n}\n\nimpl embedded_io_async::Write for LpuartBbqTx {\n    fn write(&mut self, buf: &[u8]) -> impl Future<Output = Result<usize, Self::Error>> {\n        self.write(buf)\n    }\n\n    async fn flush(&mut self) -> Result<(), Self::Error> {\n        self.flush().await;\n        Ok(())\n    }\n}\n\npub struct LpuartBbqRx {\n    state: &'static BbqState,\n    info: &'static Info,\n    vtable: BbqVtable,\n    mux: crate::pac::port::vals::Mux,\n    _rx_pins: RxPins<'static>,\n    _wg: Option<WakeGuard>,\n}\n\nimpl LpuartBbqRx {\n    /// ## SAFETY\n    ///\n    /// This function must only be called in the \"INITING\" state, and BEFORE\n    /// enabling interrupts, meaning we have exclusive access to the RX components\n    /// of the given BbqState.\n    unsafe fn initialize_rx_state(\n        state: &'static BbqState,\n        _info: &'static Info,\n        mut dma: DmaChannel<'static>,\n        rx_callback: fn(),\n        rx_buffer: &'static mut [u8],\n        request_num: u8,\n    ) {\n        // Set the callback to our completion handler, so our LPUART interrupt gets called to\n        // complete the transfer and reload\n        //\n        // TODO: Right now we only do this on RX, we might want to also handle this on TX as well\n        // so we have more time to reload, but for now we'll naturally get the \"transfer complete\"\n        // interrupt when the TX fifo empties, and we are less latency sensitive on TX than RX.\n        //\n        // SAFETY: We have exclusive ownership of the DmaChannel, and are able to overwrite the\n        // existing callback, if any.\n        unsafe {\n            dma.set_callback(rx_callback);\n        }\n\n        // Enable the DMA interrupt to handle \"transfer complete\" interrupts\n        dma.enable_interrupt();\n\n        // Setup the RX bbqueue instance, store the DMA channel and bbqueue in the\n        // BbqState storage location.\n        //\n        // TODO: We could probably be more clever and setup the DMA transfer request\n        // number ONCE in init, then just do a minimal-reload. This would allow us to\n        // avoid storing the rxdma_num, and save some effort in the ISR.\n        let cont = Container::from(rx_buffer);\n\n        // SAFETY: We have exclusive access to the shared RX components, and the interrupt\n        // is not yet enabled. We move ownership of these resources to the shared area.\n        unsafe {\n            state.rx_queue.get().write(BBQueue::new_with_storage(cont));\n            state.rxdma.get().write(dma);\n            state.rxdma_num.store(request_num, Ordering::Release);\n        }\n\n        // TODO: Do we actually want these interrupts enabled? We probably do, so we can\n        // clear the errors, but I'm not sure if any of these actually stall the receive.\n        //\n        // That being said, I've observed the RX line being floating (e.g. if the sender\n        // is in reset or disconnected) causing ~infinite \"framing errors\", which causes\n        // an interrupt storm since we don't *disable* the interrupt. We probably need to\n        // think about how/if we handle these kinds of errors.\n        //\n        // info.regs().ctrl().modify(|w| {\n        //     // overrun\n        //     w.set_orie(true);\n        //     // noise\n        //     w.set_neie(true);\n        //     // framing\n        //     w.set_feie(true);\n        // });\n    }\n\n    /// Create a new LpuartBbq with only the receive half\n    ///\n    /// NOTE: Dropping the `LpuartBbqRx` will *permanently* leak the TX buffer, DMA channel, and tx pin.\n    /// Call [LpuartBbqTx::teardown] to reclaim these resources.\n    pub fn new(parts: BbqHalfParts, config: BbqConfig, mode: BbqRxMode) -> Result<Self, BbqError> {\n        // Are these the right parts?\n        if parts.which != WhichHalf::Rx {\n            return Err(BbqError::WrongParts);\n        }\n\n        // Get state for this instance, and try to move from the \"uninit\" to \"initing\" state\n        parts.state.uninit_to_initing()?;\n\n        // Set RX pin mode\n        any_as_rx(&parts.pin, parts.mux);\n\n        // Configure UART peripheral\n        // TODO make this a specific Bbq mode instead of using blocking\n        // TODO support RTS pin?\n        let _wg = (parts.vtable.lpuart_init)(false, true, false, false, config.into()).map_err(BbqError::Basic)?;\n\n        // Setup the RX half state\n        let len = parts.buffer.len();\n\n        // SAFETY: We have ensured that we are in the INITING state, and the interrupt is not yet active.\n        unsafe {\n            Self::initialize_rx_state(\n                parts.state,\n                parts.info,\n                parts.dma_ch,\n                parts.vtable.dma_rx_cb,\n                parts.buffer,\n                parts.dma_req,\n            );\n        }\n\n        // Update our state to \"initialized\", and that we have the RXDMA channel present\n        // Okay to just store: we have exclusive access\n        let max_size = (len / 4).min(DMA_MAX_TRANSFER_SIZE);\n        let rx_mode_bits = match mode {\n            BbqRxMode::Efficiency => (max_size as u32) << 16,\n            BbqRxMode::MaxFrame { size } => {\n                if size > max_size {\n                    return Err(BbqError::MaxFrameTooLarge);\n                }\n                let size = (size as u32) << 16;\n                size | STATE_RXDMA_MODE_MAXFRAME\n            }\n        };\n        let new_state = STATE_INITED | STATE_RXDMA_PRESENT | rx_mode_bits;\n        parts.state.state.store(new_state, Ordering::Release);\n\n        // SAFETY: We have ensured that our ISR is present via the IRQ token, and we have\n        // initialized the shared state machine sufficiently that it can execute correctly\n        // when triggered.\n        unsafe {\n            // Clear any stale interrupt flags\n            (parts.vtable.int_unpend)();\n            // Enable the LPUART interrupt\n            (parts.vtable.int_enable)();\n            // Immediately pend the interrupt, this will \"load\" the DMA transfer as the\n            // ISR will notice that there is no active grant. This means that we start\n            // receiving immediately without additional user interaction.\n            (parts.vtable.int_pend)();\n        }\n\n        Ok(Self {\n            state: parts.state,\n            info: parts.info,\n            vtable: parts.vtable,\n            mux: parts.mux,\n            _rx_pins: RxPins {\n                rx_pin: parts.pin,\n                rts_pin: None,\n            },\n            _wg,\n        })\n    }\n\n    /// Read some data from the incoming receive buffer\n    ///\n    /// This method waits until some data is able to be read from the internal buffer,\n    /// and returns the number of bytes from `buf` written.\n    ///\n    /// This does NOT guarantee all bytes of `buf` have been written, only the amount returned.\n    ///\n    /// When receiving, this method must be called somewhat regularly to ensure that the incoming\n    /// buffer does not become over full.\n    ///\n    /// In this case, data will be discarded until this read method is called and capacity is made\n    /// available.\n    pub async fn read(&mut self, buf: &mut [u8]) -> Result<usize, BbqError> {\n        // TODO: we could have a version of this that gives the user the grant directly\n        // to reduce the effort of copying.\n\n        // SAFETY: The existence of a LpuartBbqRx ensures that the `rx_queue` has been\n        // initialized. The rx_queue is safe to access in a shared manner after initialization.\n        let queue = unsafe { &*self.state.rx_queue.get() };\n        let cons = queue.stream_consumer();\n        let rgr = cons.wait_read().await;\n        let to_copy = buf.len().min(rgr.len());\n        buf[..to_copy].copy_from_slice(&rgr[..to_copy]);\n        rgr.release(to_copy);\n\n        // If NO rx_dma is active, that means we stalled, so pend the interrupt to\n        // restart it now that we've freed space.\n        if (self.state.state.load(Ordering::Acquire) & STATE_RXGR_ACTIVE) == 0 {\n            (self.vtable.int_pend)();\n        }\n\n        Ok(to_copy)\n    }\n\n    /// Teardown the Rx handle, reclaiming the DMA channel, receive buffer, and Rx pin.\n    pub fn teardown(self) -> BbqHalfParts {\n        // First, mark the RXDMA as not present to halt the ISR from processing the state\n        // machine\n        let rx_state_bits = STATE_RXDMA_PRESENT\n            | STATE_RXGR_ACTIVE\n            | STATE_RXDMA_COMPLETE\n            | STATE_RXDMA_MODE_MAXFRAME\n            | STATE_RXGR_LEN_MASK;\n        let state = self.state.state.fetch_and(!rx_state_bits, Ordering::AcqRel);\n\n        // Then, disable receive-relevant interrupts\n        critical_section::with(|_cs| {\n            self.info.regs.ctrl().modify(|w| {\n                w.set_ilie(false);\n                w.set_neie(false);\n                w.set_feie(false);\n                w.set_orie(false);\n            });\n        });\n\n        // If there is an active grant, the RX DMA may be active. Stop it and release the grant\n        if (state & STATE_RXGR_ACTIVE) != 0 {\n            // SAFETY: We have unset RXDMA_PRESENT and disabled all RX interrupts: we now have exclusive\n            // access to the shared rx resources.\n            unsafe {\n                // Take DMA channel by mut ref\n                let rxdma = &mut *self.state.rxdma.get();\n\n                // Stop the DMA\n                self.info.regs().baud().modify(|w| w.set_rdmae(false));\n                rxdma.disable_request();\n                rxdma.clear_done();\n                fence(Ordering::Acquire);\n\n                // Then take the grant by ownership, and drop it, which releases the grant\n                _ = self.state.rxgr.get().read();\n            }\n        }\n\n        // Get a reference to the rx_queue to retrieve the Container\n        //\n        // SAFETY: We have unset RXDMA_PRESENT and disabled all RX interrupts: we now have exclusive\n        // access to the shared rx resources.\n        let (ptr, len) = unsafe {\n            let rx_queue = &*self.state.rx_queue.get();\n            rx_queue.storage().ptr_len()\n        };\n\n        // Now, drop the queue in place. This is sound because as the LpuartBbqRx, we have exclusive\n        // access to the \"consumer\" half, and by disabling the interrupt and notching out the state\n        // bits, we know the ISR will no longer touch the producer part.\n        //\n        // Also, take the DmaChannel by ownership this time.\n        //\n        // SAFETY: We have unset RXDMA_PRESENT and disabled all RX interrupts: we now have exclusive\n        // access to the shared rx resources.\n        let rx_dma = unsafe {\n            core::ptr::drop_in_place(self.state.rx_queue.get());\n            // Defensive coding: purge the rx_queue just in case. This doesn't zero the\n            // whole buffer, only the tracking pointers.\n            core::ptr::write_bytes(self.state.rx_queue.get(), 0, 1);\n            let mut dma = self.state.rxdma.get().read();\n            dma.clear_callback();\n            dma\n        };\n\n        // Re-magic the mut slice from the storage we have now reclaimed by dropping the\n        // rx_queue.\n        //\n        // SAFETY: We have unset RXDMA_PRESENT and disabled all RX interrupts: we now have exclusive\n        // access to the shared rx resources.\n        let rx_buffer = unsafe { core::slice::from_raw_parts_mut(ptr.as_ptr(), len) };\n\n        // Now, if this was the last part of the lpuart, we are responsible for peripheral\n        // cleanup.\n        if (state & !rx_state_bits) == STATE_INITED {\n            super::disable_peripheral(self.info);\n            self.state.state.store(STATE_UNINIT, Ordering::Relaxed);\n        }\n\n        let (rx_pin, _) = self._rx_pins.take();\n        BbqHalfParts {\n            buffer: rx_buffer,\n            dma_ch: rx_dma,\n            pin: rx_pin,\n            dma_req: self.state.rxdma_num.load(Ordering::Relaxed),\n            mux: self.mux,\n            info: self.info,\n            state: self.state,\n            vtable: self.vtable,\n            which: WhichHalf::Rx,\n        }\n    }\n}\n\nimpl embedded_io_async::ErrorType for LpuartBbqRx {\n    type Error = BbqError;\n}\n\nimpl embedded_io_async::Read for LpuartBbqRx {\n    fn read(&mut self, buf: &mut [u8]) -> impl Future<Output = Result<usize, Self::Error>> {\n        self.read(buf)\n    }\n}\n\n// A wrapper type representing a `&'static mut [u8]` buffer\nstruct Container {\n    ptr: NonNull<u8>,\n    len: usize,\n}\n\nimpl Storage for Container {\n    /// SAFETY: The length and ptr destination of the Container are never changed.\n    unsafe fn ptr_len(&self) -> (NonNull<u8>, usize) {\n        (self.ptr, self.len)\n    }\n}\n\nimpl From<&'static mut [u8]> for Container {\n    fn from(value: &'static mut [u8]) -> Self {\n        Self {\n            len: value.len(),\n            // SAFETY: The input slice is guaranteed to contain a non-null value\n            ptr: unsafe { NonNull::new_unchecked(value.as_mut_ptr()) },\n        }\n    }\n}\n\n/// interrupt handler.\npub struct BbqInterruptHandler<T: Instance> {\n    _phantom: PhantomData<T>,\n}\n\nconst STATE_UNINIT: u32 = 0b0000_0000_0000_0000_0000_0000_0000_0000;\nconst STATE_INITING: u32 = 0b0000_0000_0000_0000_0000_0000_0000_0001;\nconst STATE_INITED: u32 = 0b0000_0000_0000_0000_0000_0000_0000_0011;\nconst STATE_RXGR_ACTIVE: u32 = 0b0000_0000_0000_0000_0000_0000_0000_0100;\nconst STATE_TXGR_ACTIVE: u32 = 0b0000_0000_0000_0000_0000_0000_0000_1000;\nconst STATE_RXDMA_PRESENT: u32 = 0b0000_0000_0000_0000_0000_0000_0001_0000;\nconst STATE_TXDMA_PRESENT: u32 = 0b0000_0000_0000_0000_0000_0000_0010_0000;\nconst STATE_RXDMA_COMPLETE: u32 = 0b0000_0000_0000_0000_0000_0000_0100_0000;\nconst STATE_RXDMA_MODE_MAXFRAME: u32 = 0b0000_0000_0000_0000_0000_0000_1000_0000;\nconst STATE_RXGR_LEN_MASK: u32 = 0b1111_1111_1111_1111_0000_0000_0000_0000;\n\nstruct BbqState {\n    /// 0bGGGG_GGGG_GGGG_GGGG_xxxx_xxxx_MDTR_PCAI\n    ///                                        ^^--> 0b00: uninit, 0b01: initing, 0b11 init'd.\n    ///                                       ^----> 0b0: No Rx grant, 0b1: Rx grant active\n    ///                                      ^-----> 0b0: No Tx grant, 0b1: Tx grant active\n    ///                                    ^-------> 0b0: No Rx DMA present, 0b1: Rx DMA present\n    ///                                   ^--------> 0b0: No Tx DMA present, 0b1: Tx DMA present\n    ///                                  ^---------> 0b0: Rx DMA not complete, 0b1: Rx DMA complete\n    ///                                 ^----------> 0b0: RxMode \"Efficiency\", 0b1: RxMode \"Max Frame\"\n    ///   ^^^^_^^^^_^^^^_^^^^----------------------> 16-bit: RX Grant size\n    state: AtomicU32,\n\n    /// The \"outgoing\" bbqueue buffer\n    ///\n    /// Only valid when state is STATE_INITED + STATE_TXDMA_PRESENT.\n    tx_queue: GroundedCell<BBQueue<Container, AtomicCoord, MaiNotSpsc>>,\n    /// The \"outgoing\" transmit grant, which DMA will read from.\n    ///\n    /// Only valid when state is STATE_INITED + STATE_TXDMA_PRESENT + STATE_TXGR_ACTIVE.\n    txgr: GroundedCell<StreamGrantR<&'static BBQueue<Container, AtomicCoord, MaiNotSpsc>>>,\n    /// The \"outgoing\" DMA channel.\n    ///\n    /// Only valid when state is STATE_INITED + STATE_TXDMA_PRESENT.\n    txdma: GroundedCell<DmaChannel<'static>>,\n    /// The \"outgoing\" DMA request number.\n    ///\n    /// Only valid when state is STATE_INITED + STATE_TXDMA_PRESENT.\n    txdma_num: AtomicU8,\n\n    /// The \"incoming\" bbqueue buffer\n    ///\n    /// Only valid when state is STATE_INITED + STATE_RXDMA_PRESENT.\n    rx_queue: GroundedCell<BBQueue<Container, AtomicCoord, MaiNotSpsc>>,\n    /// The \"incoming\" receive grant, which DMA will write to.\n    ///\n    /// Only valid when state is STATE_INITED + STATE_RXDMA_PRESENT + STATE_RXGR_ACTIVE.\n    rxgr: GroundedCell<StreamGrantW<&'static BBQueue<Container, AtomicCoord, MaiNotSpsc>>>,\n    /// The \"incoming\" DMA channel.\n    ///\n    /// Only valid when state is STATE_INITED + STATE_RXDMA_PRESENT.\n    rxdma: GroundedCell<DmaChannel<'static>>,\n    /// The \"incoming\" DMA request number.\n    ///\n    /// Only valid when state is STATE_INITED + STATE_RXDMA_PRESENT.\n    rxdma_num: AtomicU8,\n\n    /// Waiter for the outgoing buffer to be flushed\n    tx_flushed: WaitCell,\n}\n\nimpl BbqState {\n    const fn new() -> Self {\n        Self {\n            state: AtomicU32::new(0),\n            tx_queue: GroundedCell::uninit(),\n            rx_queue: GroundedCell::uninit(),\n            rxgr: GroundedCell::uninit(),\n            txgr: GroundedCell::uninit(),\n            txdma: GroundedCell::uninit(),\n            txdma_num: AtomicU8::new(0),\n            rxdma: GroundedCell::uninit(),\n            rxdma_num: AtomicU8::new(0),\n            tx_flushed: WaitCell::new(),\n        }\n    }\n\n    /// Attempt to move from the \"uninit\" state to the \"initing\" state. Returns an\n    /// error if we are not in the \"uninit\" state.\n    fn uninit_to_initing(&'static self) -> Result<(), BbqError> {\n        self.state\n            .compare_exchange(STATE_UNINIT, STATE_INITING, Ordering::AcqRel, Ordering::Acquire)\n            .map(drop)\n            .map_err(|_| BbqError::Busy)\n    }\n\n    /// Complete an active TX DMA transfer. Called from ISR context.\n    ///\n    /// After calling, the transmit half of the driver will be in the idle state.\n    ///\n    /// ## SAFETY\n    ///\n    /// * The HAL driver must be initialized\n    /// * The TXDMA must be present\n    /// * A write grant must be active\n    /// * We must be in ISR context\n    unsafe fn finalize_write(&'static self, info: &'static Info) {\n        // SAFETY: With the function-level safety requirements met, we are free to modify\n        // shared tx state.\n        unsafe {\n            // Load the active TX grant, taking it \"by ownership\"\n            let txgr = self.txgr.get().read();\n            // Get the TX DMA, taking it by &mut ref\n            let txdma = &mut *self.txdma.get();\n\n            // Stop the DMA\n            info.regs().baud().modify(|w| w.set_tdmae(false));\n            txdma.disable_request();\n            txdma.clear_done();\n            // TODO: Some other way of ensuring the DMA is completely stopped?\n            fence(Ordering::Acquire);\n\n            // The max transfer length was the lesser of capacity / 4 or the max DMA transfer size\n            // in a single transaction. This is because the `read()` used to create this grant may\n            // be larger, if more bytes were available.\n            let max_len = (&*self.tx_queue.get()).capacity() / 4;\n            let xfer = txgr.len().min(max_len).min(DMA_MAX_TRANSFER_SIZE);\n\n            // Release the number of transferred bytes, making them available to the user to reuse,\n            // and waking the write waiter if there is one present (e.g. if we were previously full).\n            txgr.release(xfer);\n        }\n        // Mark the TXGR as inactive, signifying \"idle\"\n        self.state.fetch_and(!STATE_TXGR_ACTIVE, Ordering::AcqRel);\n    }\n\n    /// Complete an active RX DMA transfer. Called from ISR context.\n    ///\n    /// After calling, the receive half of the driver will be in the idle state.\n    ///\n    /// ## SAFETY\n    ///\n    /// * The HAL driver must be initialized\n    /// * The RXDMA must be present\n    /// * A read grant must be active\n    /// * We must be in ISR context\n    unsafe fn finalize_read(&'static self, info: &'static Info) {\n        // SAFETY: With the function-level safety requirements met, we are free to modify\n        // shared rx state.\n        unsafe {\n            // Load the active RX grant, taking it by ownership\n            let rxgr = self.rxgr.get().read();\n            // Get the RX DMA, taking it by &mut ref\n            let rxdma = &mut *self.rxdma.get();\n\n            // Stop the active DMA.\n            // The DMA may NOT be done yet if this was an idle interrupt\n            info.regs().baud().modify(|w| w.set_rdmae(false));\n            rxdma.disable_request();\n            rxdma.clear_done();\n\n            // Fence to ensure all DMA written bytes are complete, and we can see\n            // any writes to the DADDR\n            fence(Ordering::AcqRel);\n\n            // Calculate the number of bytes written using the current write address of the\n            // DMA channel, minus our starting address.\n            let daddr = rxdma.daddr() as usize;\n            let sstrt = rxgr.as_ptr() as usize;\n            let ttl = daddr.wrapping_sub(sstrt).min(rxgr.len());\n\n            // Commit these bytes, making them visible to the user, and waking any pending\n            // waiters if any (e.g. if we were previously empty)\n            rxgr.commit(ttl);\n        }\n        // Mark the RXGR inactive, signifying idle\n        self.state.fetch_and(!STATE_RXGR_ACTIVE, Ordering::AcqRel);\n    }\n\n    /// Attempt to start an active write transfer. Called from ISR context.\n    ///\n    /// Returns true if a transfer was started, and returns false if no transfer\n    /// was started (e.g. the outgoing buffer is completely drained).\n    ///\n    /// ## SAFETY\n    ///\n    /// * The HAL driver must be initialized\n    /// * The TXDMA must be present\n    /// * A write grant must NOT be active\n    /// * We must be in ISR context\n    unsafe fn start_write_transfer(&'static self, info: &'static Info) -> bool {\n        // Get the tx queue, by & ref\n        //\n        // SAFETY: TXDMA_PRESENT bit being enabled means the tx_queue has been initialized.\n        // The tx_queue is safe to access in a shared manner after initialization.\n        let tx_queue = unsafe { &*self.tx_queue.get() };\n        let Ok(rgr) = tx_queue.stream_consumer().read() else {\n            // Nothing to do!\n            return false;\n        };\n\n        // SAFETY: With the function-level safety requirements met, we are free to modify\n        // shared tx state.\n        unsafe {\n            // Take the TXDMA by &mut ref\n            let txdma = &mut *self.txdma.get();\n\n            // Initialize the transfer from the bbqueue grant to DMA\n            //\n            // TODO: Most of this setup is redundant/repeated, we could save some effort\n            // since most DMA transfer parameters are the same.\n            let source = DmaRequest::from_number_unchecked(self.txdma_num.load(Ordering::Relaxed));\n            txdma.disable_request();\n            txdma.clear_done();\n            txdma.clear_interrupt();\n            txdma.set_request_source(source);\n\n            let peri_addr = info.regs().data().as_ptr().cast::<u8>();\n\n            // NOTE: we limit the max transfer size to 1/4 the capacity for latency reasons,\n            // so we can make buffer space available for further writing by the application\n            // as soon as possible, as the buffer space is not made available until after\n            // the transfer completes.\n            let max_len = (&*self.tx_queue.get()).capacity() / 4;\n            let len = rgr.len().min(max_len).min(DMA_MAX_TRANSFER_SIZE);\n            if let Err(InvalidParameters) =\n                txdma.setup_write_to_peripheral(&rgr[..len], peri_addr, false, TransferOptions::COMPLETE_INTERRUPT)\n            {\n                return false;\n            }\n\n            // Enable the DMA transfer\n            info.regs().baud().modify(|w| w.set_tdmae(true));\n            txdma.enable_request();\n\n            // Store (by ownership) the outgoing read grant to the bbqueue state\n            self.txgr.get().write(rgr);\n\n            // Mark the TXGR as active, signifying the \"transmitting\" state\n            self.state.fetch_or(STATE_TXGR_ACTIVE, Ordering::AcqRel);\n        }\n\n        // wait until the system is not reporting TC complete, to ensure we don't\n        // immediately retrigger an interrupt.\n        //\n        // TODO: I'm not sure this actually ever happens, this is a defensive check\n        while info.regs.stat().read().tc() == Tc::COMPLETE {}\n\n        true\n    }\n\n    /// Attempt to start an active read transfer. Called from ISR context.\n    ///\n    /// Returns true if a transfer was started, and returns false if no transfer\n    /// was started (e.g. the incoming buffer is completely full).\n    ///\n    /// ## SAFETY\n    ///\n    /// * The HAL driver must be initialized\n    /// * The RXDMA must be present\n    /// * A write grant must NOT be active\n    /// * We must be in ISR context\n    unsafe fn start_read_transfer(&'static self, info: &'static Info) -> bool {\n        // SAFETY: RXDMA_PRESENT bit being enabled means the rx_queue has been initialized.\n        // The rx_queue is safe to access in a shared manner after initialization.\n        let rx_queue = unsafe { &*self.rx_queue.get() };\n\n        // Determine the size and kind of grant to request\n        let state = self.state.load(Ordering::Relaxed);\n        let len = (state >> 16) as usize;\n        let is_max_frame = (state & STATE_RXDMA_MODE_MAXFRAME) != 0;\n        let prod = rx_queue.stream_producer();\n\n        let grant_res = if is_max_frame {\n            prod.grant_exact(len)\n        } else {\n            prod.grant_max_remaining(len)\n        };\n\n        let Ok(mut wgr) = grant_res else {\n            // If we can't get a grant, that's a problem. Return false to note we didn't\n            // start one, and hope the user frees space soon. See the `read` method for\n            // how read transfers are restarted in this case.\n            return false;\n        };\n\n        // SAFETY: With the function-level safety requirements met, we are free to modify\n        // shared rx state.\n        unsafe {\n            // Initialize the transfer from the DMA to the bbqueue grant\n            //\n            // TODO: Most of this setup is redundant/repeated, we could save some effort\n            // since most DMA transfer parameters are the same.\n            let rxdma = &mut *self.rxdma.get();\n            let source = DmaRequest::from_number_unchecked(self.rxdma_num.load(Ordering::Relaxed));\n            rxdma.disable_request();\n            rxdma.clear_done();\n            rxdma.clear_interrupt();\n            rxdma.set_request_source(source);\n\n            let peri_addr = info.regs().data().as_ptr().cast::<u8>();\n            if let Err(InvalidParameters) =\n                rxdma.setup_read_from_peripheral(peri_addr, &mut wgr, false, TransferOptions::COMPLETE_INTERRUPT)\n            {\n                return false;\n            }\n\n            // Enable the DMA transfer\n            info.regs().baud().modify(|w| w.set_rdmae(true));\n            rxdma.enable_request();\n\n            // Store (by ownership) the incoming write grant to the bbqueue state\n            self.rxgr.get().write(wgr);\n\n            // Mark the RXGR as active, signifying the \"receiving\" state\n            self.state.fetch_or(STATE_RXGR_ACTIVE, Ordering::AcqRel);\n        }\n\n        true\n    }\n}\n\n#[allow(private_bounds, private_interfaces)]\npub trait BbqInstance: Instance {\n    /// The BBQ specific state storage\n    fn bbq_state() -> &'static BbqState;\n    /// A callback for the DMA handler to call that marks RXDMA as complete and\n    /// pends the LPUART interrupt for further processing.\n    fn dma_rx_complete_cb();\n}\n\nmacro_rules! impl_instance {\n    ($($n:expr);* $(;)?) => {\n        $(\n            paste!{\n                #[allow(private_interfaces)]\n                impl BbqInstance for crate::peripherals::[<LPUART $n>] {\n                    fn bbq_state() -> &'static BbqState {\n                        static STATE: BbqState = BbqState::new();\n                        &STATE\n                    }\n\n                    fn dma_rx_complete_cb() {\n                        let state = Self::bbq_state();\n                        // Mark the DMA as complete\n                        state.state.fetch_or(STATE_RXDMA_COMPLETE, Ordering::AcqRel);\n                        // Pend the UART interrupt to handle the switchover\n                        Self::Interrupt::pend();\n                    }\n                }\n            }\n        )*\n    };\n}\n\nimpl_instance!(0; 1; 2; 3; 4);\n\n#[cfg(feature = \"mcxa5xx\")]\nimpl_instance!(5);\n\n// Basically the on_interrupt handler, but as a free function so it doesn't get\n// monomorphized.\n//\n// SAFETY: Should only be called by the `on_interrupt` function in ISR context, with\n// the shared BbqState properly initialized in the INITED state\nunsafe fn handler(info: &'static Info, state: &'static BbqState) {\n    let regs = info.regs();\n    let ctrl = regs.ctrl().read();\n    let stat = regs.stat().read();\n\n    // Just clear any errors - TODO, signal these to the consumer?\n    // For now, we just clear + discard errors if they occur.\n    let or = stat.or();\n    let pf = stat.pf();\n    let fe = stat.fe();\n    let nf = stat.nf();\n    let idle = stat.idle();\n    regs.stat().modify(|w| {\n        w.set_or(or);\n        w.set_pf(pf);\n        w.set_fe(fe);\n        w.set_nf(nf);\n        w.set_idle(idle);\n    });\n\n    //\n    // RX state machine\n    //\n\n    // Check DMA complete or idle interrupt occurred - we need to stop\n    // the current RX transfer in either case.\n    let pre_clear = state.state.fetch_and(!STATE_RXDMA_COMPLETE, Ordering::AcqRel);\n\n    // SAFETY NOTE: The RXDMA_PRESENT bit is used to mediate whether the interrupt should\n    // act the shared RX data. This is used by functions like `teardown` to disable interrupt\n    // access to shared data when tearing down.\n    let rx_present = (pre_clear & STATE_RXDMA_PRESENT) != 0;\n    if rx_present {\n        let rx_active = (pre_clear & STATE_RXGR_ACTIVE) != 0;\n        let dma_complete = (pre_clear & STATE_RXDMA_COMPLETE) != 0;\n        if rx_active && (idle || dma_complete) {\n            // State change, move from Receiving -> Idle\n            //\n            // SAFETY: The HAL driver is initialized, we checked that RXDMA_PRESENT is set, we\n            // checked that RXGR_ACTIVE is set, we are in ISR context\n            unsafe {\n                state.finalize_read(info);\n            }\n        }\n\n        // If we are now idle, attempt to \"reload\" the transfer and being receiving again ASAP.\n        // Only do this if RXDMA is present. We re-load from state to ensure we see when\n        // `finalize_read` just cleared the bit.\n        let rx_idle = (state.state.load(Ordering::Acquire) & STATE_RXGR_ACTIVE) == 0;\n        if rx_idle {\n            // Either Idle -> Receiving or Idle -> Idle\n            //\n            // SAFETY: The HAL driver is initialized, we checked that RXDMA_PRESENT is set, we\n            // checked there isn't a write grant active, and we are in ISR context.\n            unsafe {\n                let started = state.start_read_transfer(info);\n                // Enable ILIE if we started a transfer, otherwise (keep) disabled.\n                // ILIE - Idle Line Interrupt Enable\n                regs.ctrl().modify(|w| w.set_ilie(started));\n            }\n        }\n    }\n\n    //\n    // TX state machine\n    //\n\n    // SAFETY NOTE: The TXDMA_PRESENT bit is used to mediate whether the interrupt should\n    // act the shared TX data. This is used by functions like `teardown` to disable interrupt\n    // access to shared data when tearing down.\n    let tx_state = state.state.load(Ordering::Acquire);\n    let tx_present = (tx_state & STATE_TXDMA_PRESENT) != 0;\n    if tx_present {\n        // Handle TX data - TCIE is only enabled if we are transmitting, and we only\n        // check that the outgoing transfer is complete. In the future, we might\n        // try to do this a bit earlier if the DMA completes but we haven't yet\n        // drained the TX fifo yet.\n        let txie_set = ctrl.tcie();\n        let tc_complete = regs.stat().read().tc() == Tc::COMPLETE;\n        let txgr_present = (tx_state & STATE_TXGR_ACTIVE) != 0;\n\n        let tx_did_finish = txie_set && tc_complete && txgr_present;\n        if tx_did_finish {\n            // State change, move from Transmitting -> Idle\n            //\n            // SAFETY: The driver has been initialized, we've checked TXDMA_PRESENT is set,\n            // we've checked TXGR_ACTIVE is set, we are in ISR context.\n            unsafe {\n                state.finalize_write(info);\n            }\n        }\n\n        // If we are now idle, attempt to \"reload\" the transfer and begin transmitting again.\n        // Only do this if TXDMA is present.\n        let tx_idle = (state.state.load(Ordering::Acquire) & STATE_TXGR_ACTIVE) == 0;\n        if tx_idle {\n            // Either Idle -> Transmitting or Idle -> Idle\n            //\n            // SAFETY: The driver has been initialized, we've checked TXDMA_PRESENT is set,\n            // we've checked TXGR_ACTIVE is NOT set, we are in ISR context.\n            unsafe {\n                let started = state.start_write_transfer(info);\n                // Enable tcie if we started a transfer, otherwise (keep) disabled.\n                // TCIE - Transfer Complete Interrupt Enable\n                regs.ctrl().modify(|w| w.set_tcie(started));\n\n                // Did we go from \"transmitting\" to \"idle\" in this ISR? If so, wake any \"flush\" waiters.\n                if tx_did_finish && !started {\n                    state.tx_flushed.wake();\n                }\n            }\n        }\n    }\n}\n\nimpl<T: BbqInstance> Handler<T::Interrupt> for BbqInterruptHandler<T> {\n    unsafe fn on_interrupt() {\n        T::PERF_INT_INCR();\n        let info = T::info();\n        let state = T::bbq_state();\n\n        // SAFETY: Interrupts are only enabled when state is valid, we are calling the handler\n        // from ISR context.\n        unsafe {\n            handler(info, state);\n        }\n    }\n}\n\nuse crate::gpio::SealedPin;\n\nfn any_as_tx(pin: &Peri<'_, AnyPin>, mux: crate::pac::port::vals::Mux) {\n    pin.set_pull(crate::gpio::Pull::Disabled);\n    pin.set_slew_rate(crate::gpio::SlewRate::Fast.into());\n    pin.set_drive_strength(crate::gpio::DriveStrength::Normal.into());\n    pin.set_function(mux);\n    pin.set_enable_input_buffer(false);\n}\n\nfn any_as_rx(pin: &Peri<'_, AnyPin>, mux: crate::pac::port::vals::Mux) {\n    pin.set_pull(crate::gpio::Pull::Disabled);\n    pin.set_function(mux);\n    pin.set_enable_input_buffer(true);\n}\n"
  },
  {
    "path": "embassy-mcxa/src/lpuart/blocking.rs",
    "content": "use embassy_hal_internal::Peri;\n\nuse super::*;\nuse crate::pac::lpuart::vals::{Tc, Tdre};\n\n/// Blocking mode.\npub struct Blocking;\nimpl sealed::Sealed for Blocking {}\nimpl Mode for Blocking {}\n\nimpl<'a> Lpuart<'a, Blocking> {\n    /// Create a new blocking LPUART instance with RX/TX pins.\n    ///\n    /// Any external pin will be placed into Disabled state upon Drop.\n    pub fn new_blocking<T: Instance>(\n        _inner: Peri<'a, T>,\n        tx_pin: Peri<'a, impl TxPin<T>>,\n        rx_pin: Peri<'a, impl RxPin<T>>,\n        config: Config,\n    ) -> Result<Self, Error> {\n        // Configure the pins for LPUART usage\n        tx_pin.as_tx();\n        rx_pin.as_rx();\n\n        let wg = Self::init::<T>(true, true, false, false, config)?;\n        Ok(Self {\n            tx: LpuartTx::new_inner::<T>(tx_pin.into(), None, Blocking, wg.clone()),\n            rx: LpuartRx::new_inner::<T>(rx_pin.into(), None, Blocking, wg),\n        })\n    }\n\n    /// Create a new blocking LPUART instance with RX, TX and RTS/CTS flow control pins.\n    ///\n    /// Any external pin will be placed into Disabled state upon Drop.\n    pub fn new_blocking_with_rtscts<T: Instance>(\n        _inner: Peri<'a, T>,\n        tx_pin: Peri<'a, impl TxPin<T>>,\n        rx_pin: Peri<'a, impl RxPin<T>>,\n        cts_pin: Peri<'a, impl CtsPin<T>>,\n        rts_pin: Peri<'a, impl RtsPin<T>>,\n        config: Config,\n    ) -> Result<Self, Error> {\n        // Configure the pins for LPUART usage\n        rx_pin.as_rx();\n        tx_pin.as_tx();\n        rts_pin.as_rts();\n        cts_pin.as_cts();\n\n        let wg = Self::init::<T>(true, true, true, true, config)?;\n        Ok(Self {\n            rx: LpuartRx::new_inner::<T>(rx_pin.into(), Some(rts_pin.into()), Blocking, wg.clone()),\n            tx: LpuartTx::new_inner::<T>(tx_pin.into(), Some(cts_pin.into()), Blocking, wg),\n        })\n    }\n\n    /// Read data from LPUART RX blocking execution until the buffer is filled\n    pub fn blocking_read(&mut self, buf: &mut [u8]) -> Result<(), Error> {\n        self.rx.blocking_read(buf)\n    }\n\n    /// Read data from LPUART RX without blocking\n    pub fn read(&mut self, buf: &mut [u8]) -> Result<(), Error> {\n        self.rx.read(buf)\n    }\n\n    /// Write data to LPUART TX blocking execution until all data is sent\n    pub fn blocking_write(&mut self, buf: &[u8]) -> Result<(), Error> {\n        self.tx.blocking_write(buf)\n    }\n\n    pub fn write_byte(&mut self, byte: u8) -> Result<(), Error> {\n        self.tx.write_byte(byte)\n    }\n\n    pub fn read_byte_blocking(&mut self) -> u8 {\n        loop {\n            if let Ok(b) = self.rx.read_byte() {\n                return b;\n            }\n        }\n    }\n\n    pub fn write_str_blocking(&mut self, buf: &str) {\n        self.tx.write_str_blocking(buf);\n    }\n\n    /// Write data to LPUART TX without blocking\n    pub fn write(&mut self, buf: &[u8]) -> Result<(), Error> {\n        self.tx.write(buf)\n    }\n\n    /// Flush LPUART TX blocking execution until all data has been transmitted\n    pub fn blocking_flush(&mut self) -> Result<(), Error> {\n        self.tx.blocking_flush()\n    }\n\n    /// Flush LPUART TX without blocking\n    pub fn flush(&mut self) -> Result<(), Error> {\n        self.tx.flush()\n    }\n}\n\nimpl<'a> LpuartTx<'a, Blocking> {\n    /// Create a new blocking LPUART transmitter instance.\n    ///\n    /// Any external pin will be placed into Disabled state upon Drop.\n    pub fn new_blocking<T: Instance>(\n        _inner: Peri<'a, T>,\n        tx_pin: Peri<'a, impl TxPin<T>>,\n        config: Config,\n    ) -> Result<Self, Error> {\n        // Configure the pins for LPUART usage\n        tx_pin.as_tx();\n\n        let wg = Lpuart::<Blocking>::init::<T>(true, false, false, false, config)?;\n        Ok(Self::new_inner::<T>(tx_pin.into(), None, Blocking, wg))\n    }\n\n    /// Create a new blocking LPUART transmitter instance with CTS flow control.\n    ///\n    /// Any external pin will be placed into Disabled state upon Drop.\n    pub fn new_blocking_with_cts<T: Instance>(\n        _inner: Peri<'a, T>,\n        tx_pin: Peri<'a, impl TxPin<T>>,\n        cts_pin: Peri<'a, impl CtsPin<T>>,\n        config: Config,\n    ) -> Result<Self, Error> {\n        tx_pin.as_tx();\n        cts_pin.as_cts();\n\n        let wg = Lpuart::<Blocking>::init::<T>(true, false, true, false, config)?;\n        Ok(Self::new_inner::<T>(tx_pin.into(), Some(cts_pin.into()), Blocking, wg))\n    }\n\n    fn write_byte_internal(&mut self, byte: u8) -> Result<(), Error> {\n        self.info.regs().data().modify(|w| w.0 = u32::from(byte));\n\n        Ok(())\n    }\n\n    fn blocking_write_byte(&mut self, byte: u8) -> Result<(), Error> {\n        while self.info.regs().stat().read().tdre() == Tdre::TXDATA {}\n        self.write_byte_internal(byte)\n    }\n\n    fn write_byte(&mut self, byte: u8) -> Result<(), Error> {\n        if self.info.regs().stat().read().tdre() == Tdre::TXDATA {\n            Err(Error::TxFifoFull)\n        } else {\n            self.write_byte_internal(byte)\n        }\n    }\n\n    /// Write data to LPUART TX blocking execution until all data is sent.\n    pub fn blocking_write(&mut self, buf: &[u8]) -> Result<(), Error> {\n        for x in buf {\n            self.blocking_write_byte(*x)?;\n        }\n\n        Ok(())\n    }\n\n    pub fn write_str_blocking(&mut self, buf: &str) {\n        let _ = self.blocking_write(buf.as_bytes());\n    }\n\n    /// Write data to LPUART TX without blocking.\n    pub fn write(&mut self, buf: &[u8]) -> Result<(), Error> {\n        for x in buf {\n            self.write_byte(*x)?;\n        }\n\n        Ok(())\n    }\n\n    /// Flush LPUART TX blocking execution until all data has been transmitted.\n    pub fn blocking_flush(&mut self) -> Result<(), Error> {\n        while self.info.regs().water().read().txcount() != 0 {\n            // Wait for TX FIFO to drain\n        }\n\n        // Wait for last character to shift out\n        while self.info.regs().stat().read().tc() == Tc::ACTIVE {\n            // Wait for transmission to complete\n        }\n\n        Ok(())\n    }\n\n    /// Flush LPUART TX.\n    pub fn flush(&mut self) -> Result<(), Error> {\n        // Check if TX FIFO is empty\n        if self.info.regs().water().read().txcount() != 0 {\n            return Err(Error::TxBusy);\n        }\n\n        // Check if transmission is complete\n        if self.info.regs().stat().read().tc() == Tc::ACTIVE {\n            return Err(Error::TxBusy);\n        }\n\n        Ok(())\n    }\n}\n\nimpl<'a> LpuartRx<'a, Blocking> {\n    /// Create a new blocking LPUART Receiver instance.\n    ///\n    /// Any external pin will be placed into Disabled state upon Drop.\n    pub fn new_blocking<T: Instance>(\n        _inner: Peri<'a, T>,\n        rx_pin: Peri<'a, impl RxPin<T>>,\n        config: Config,\n    ) -> Result<Self, Error> {\n        rx_pin.as_rx();\n\n        let wg = Lpuart::<Blocking>::init::<T>(false, true, false, false, config)?;\n        Ok(Self::new_inner::<T>(rx_pin.into(), None, Blocking, wg))\n    }\n\n    /// Create a new blocking LPUART Receiver instance with RTS flow control.\n    ///\n    /// Any external pin will be placed into Disabled state upon Drop.\n    pub fn new_blocking_with_rts<T: Instance>(\n        _inner: Peri<'a, T>,\n        rx_pin: Peri<'a, impl RxPin<T>>,\n        rts_pin: Peri<'a, impl RtsPin<T>>,\n        config: Config,\n    ) -> Result<Self, Error> {\n        rx_pin.as_rx();\n        rts_pin.as_rts();\n\n        let wg = Lpuart::<Blocking>::init::<T>(false, true, false, true, config)?;\n        Ok(Self::new_inner::<T>(rx_pin.into(), Some(rts_pin.into()), Blocking, wg))\n    }\n\n    fn read_byte_internal(&mut self) -> Result<u8, Error> {\n        Ok((self.info.regs().data().read().0 & 0xFF) as u8)\n    }\n\n    fn read_byte(&mut self) -> Result<u8, Error> {\n        check_and_clear_rx_errors(self.info)?;\n\n        if !has_rx_data_pending(self.info) {\n            return Err(Error::RxFifoEmpty);\n        }\n\n        self.read_byte_internal()\n    }\n\n    fn blocking_read_byte(&mut self) -> Result<u8, Error> {\n        loop {\n            if has_rx_data_pending(self.info) {\n                return self.read_byte_internal();\n            }\n\n            check_and_clear_rx_errors(self.info)?;\n        }\n    }\n\n    /// Read data from LPUART RX without blocking.\n    pub fn read(&mut self, buf: &mut [u8]) -> Result<(), Error> {\n        for byte in buf.iter_mut() {\n            *byte = self.read_byte()?;\n        }\n        Ok(())\n    }\n\n    /// Read data from LPUART RX blocking execution until the buffer is filled.\n    pub fn blocking_read(&mut self, buf: &mut [u8]) -> Result<(), Error> {\n        for byte in buf.iter_mut() {\n            *byte = self.blocking_read_byte()?;\n        }\n        Ok(())\n    }\n}\n\nimpl embedded_hal_02::serial::Read<u8> for LpuartRx<'_, Blocking> {\n    type Error = Error;\n\n    fn read(&mut self) -> core::result::Result<u8, nb::Error<Self::Error>> {\n        let mut buf = [0; 1];\n        match self.read(&mut buf) {\n            Ok(_) => Ok(buf[0]),\n            Err(Error::RxFifoEmpty) => Err(nb::Error::WouldBlock),\n            Err(e) => Err(nb::Error::Other(e)),\n        }\n    }\n}\n\nimpl embedded_hal_02::serial::Write<u8> for LpuartTx<'_, Blocking> {\n    type Error = Error;\n\n    fn write(&mut self, word: u8) -> core::result::Result<(), nb::Error<Self::Error>> {\n        match self.write(&[word]) {\n            Ok(_) => Ok(()),\n            Err(Error::TxFifoFull) => Err(nb::Error::WouldBlock),\n            Err(e) => Err(nb::Error::Other(e)),\n        }\n    }\n\n    fn flush(&mut self) -> core::result::Result<(), nb::Error<Self::Error>> {\n        match self.flush() {\n            Ok(_) => Ok(()),\n            Err(Error::TxBusy) => Err(nb::Error::WouldBlock),\n            Err(e) => Err(nb::Error::Other(e)),\n        }\n    }\n}\n\nimpl embedded_hal_02::blocking::serial::Write<u8> for LpuartTx<'_, Blocking> {\n    type Error = Error;\n\n    fn bwrite_all(&mut self, buffer: &[u8]) -> core::result::Result<(), Self::Error> {\n        self.blocking_write(buffer)\n    }\n\n    fn bflush(&mut self) -> core::result::Result<(), Self::Error> {\n        self.blocking_flush()\n    }\n}\n\nimpl embedded_hal_02::serial::Read<u8> for Lpuart<'_, Blocking> {\n    type Error = Error;\n\n    fn read(&mut self) -> core::result::Result<u8, nb::Error<Self::Error>> {\n        embedded_hal_02::serial::Read::read(&mut self.rx)\n    }\n}\n\nimpl embedded_hal_02::serial::Write<u8> for Lpuart<'_, Blocking> {\n    type Error = Error;\n\n    fn write(&mut self, word: u8) -> core::result::Result<(), nb::Error<Self::Error>> {\n        embedded_hal_02::serial::Write::write(&mut self.tx, word)\n    }\n\n    fn flush(&mut self) -> core::result::Result<(), nb::Error<Self::Error>> {\n        embedded_hal_02::serial::Write::flush(&mut self.tx)\n    }\n}\n\nimpl embedded_hal_02::blocking::serial::Write<u8> for Lpuart<'_, Blocking> {\n    type Error = Error;\n\n    fn bwrite_all(&mut self, buffer: &[u8]) -> core::result::Result<(), Self::Error> {\n        self.blocking_write(buffer)\n    }\n\n    fn bflush(&mut self) -> core::result::Result<(), Self::Error> {\n        self.blocking_flush()\n    }\n}\n\nimpl embedded_hal_nb::serial::Read for LpuartRx<'_, Blocking> {\n    fn read(&mut self) -> nb::Result<u8, Self::Error> {\n        let mut buf = [0; 1];\n        match self.read(&mut buf) {\n            Ok(_) => Ok(buf[0]),\n            Err(Error::RxFifoEmpty) => Err(nb::Error::WouldBlock),\n            Err(e) => Err(nb::Error::Other(e)),\n        }\n    }\n}\n\nimpl embedded_hal_nb::serial::Write for LpuartTx<'_, Blocking> {\n    fn write(&mut self, word: u8) -> nb::Result<(), Self::Error> {\n        match self.write(&[word]) {\n            Ok(_) => Ok(()),\n            Err(Error::TxFifoFull) => Err(nb::Error::WouldBlock),\n            Err(e) => Err(nb::Error::Other(e)),\n        }\n    }\n\n    fn flush(&mut self) -> nb::Result<(), Self::Error> {\n        match self.flush() {\n            Ok(_) => Ok(()),\n            Err(Error::TxBusy) => Err(nb::Error::WouldBlock),\n            Err(e) => Err(nb::Error::Other(e)),\n        }\n    }\n}\n\nimpl core::fmt::Write for LpuartTx<'_, Blocking> {\n    fn write_str(&mut self, s: &str) -> core::fmt::Result {\n        self.blocking_write(s.as_bytes()).map_err(|_| core::fmt::Error)\n    }\n}\n\nimpl embedded_hal_nb::serial::Read for Lpuart<'_, Blocking> {\n    fn read(&mut self) -> nb::Result<u8, Self::Error> {\n        embedded_hal_nb::serial::Read::read(&mut self.rx)\n    }\n}\n\nimpl embedded_hal_nb::serial::Write for Lpuart<'_, Blocking> {\n    fn write(&mut self, char: u8) -> nb::Result<(), Self::Error> {\n        embedded_hal_nb::serial::Write::write(&mut self.tx, char)\n    }\n\n    fn flush(&mut self) -> nb::Result<(), Self::Error> {\n        embedded_hal_nb::serial::Write::flush(&mut self.tx)\n    }\n}\n\nimpl embedded_io::Read for LpuartRx<'_, Blocking> {\n    fn read(&mut self, buf: &mut [u8]) -> core::result::Result<usize, Self::Error> {\n        self.blocking_read(buf).map(|_| buf.len())\n    }\n}\n\nimpl embedded_io::Write for LpuartTx<'_, Blocking> {\n    fn write(&mut self, buf: &[u8]) -> core::result::Result<usize, Self::Error> {\n        self.blocking_write(buf).map(|_| buf.len())\n    }\n\n    fn flush(&mut self) -> core::result::Result<(), Self::Error> {\n        self.blocking_flush()\n    }\n}\n\nimpl embedded_io::Read for Lpuart<'_, Blocking> {\n    fn read(&mut self, buf: &mut [u8]) -> core::result::Result<usize, Self::Error> {\n        embedded_io::Read::read(&mut self.rx, buf)\n    }\n}\n\nimpl embedded_io::Write for Lpuart<'_, Blocking> {\n    fn write(&mut self, buf: &[u8]) -> core::result::Result<usize, Self::Error> {\n        embedded_io::Write::write(&mut self.tx, buf)\n    }\n\n    fn flush(&mut self) -> core::result::Result<(), Self::Error> {\n        embedded_io::Write::flush(&mut self.tx)\n    }\n}\n"
  },
  {
    "path": "embassy-mcxa/src/lpuart/buffered.rs",
    "content": "use core::marker::PhantomData;\n\nuse embassy_hal_internal::Peri;\n\nuse super::*;\nuse crate::clocks::WakeGuard;\nuse crate::interrupt::typelevel::Interrupt;\nuse crate::interrupt::{self};\n\n/// Async buffered mode where the bytes are pumped by the interrupt handler.\npub struct Buffered;\nimpl sealed::Sealed for Buffered {}\nimpl Mode for Buffered {}\n\nimpl<'a> Lpuart<'a, Buffered> {\n    /// Common initialization logic\n    fn init_buffered<T: Instance>(\n        tx_buffer: Option<&'a mut [u8]>,\n        rx_buffer: Option<&'a mut [u8]>,\n        config: Config,\n        enable_tx: bool,\n        enable_rx: bool,\n        enable_rts: bool,\n        enable_cts: bool,\n    ) -> Result<Option<WakeGuard>, Error> {\n        // Initialize buffers\n        if let Some(tx_buffer) = tx_buffer {\n            if tx_buffer.is_empty() {\n                return Err(Error::InvalidArgument);\n            }\n            unsafe { T::state().tx_buf.init(tx_buffer.as_mut_ptr(), tx_buffer.len()) };\n        }\n\n        if let Some(rx_buffer) = rx_buffer {\n            if rx_buffer.is_empty() {\n                return Err(Error::InvalidArgument);\n            }\n            unsafe { T::state().rx_buf.init(rx_buffer.as_mut_ptr(), rx_buffer.len()) };\n        }\n\n        // Enable clocks and initialize hardware\n        let conf = LpuartConfig {\n            power: config.power,\n            source: config.source,\n            div: config.div,\n            instance: T::CLOCK_INSTANCE,\n        };\n        let parts = unsafe { enable_and_reset::<T>(&conf).map_err(Error::ClockSetup)? };\n\n        Self::init::<T>(enable_tx, enable_rx, enable_cts, enable_rts, config)?;\n\n        // Enable interrupts for buffered operation\n        cortex_m::interrupt::free(|_| {\n            T::info().regs().ctrl().modify(|w| {\n                w.set_rie(true); // RX interrupt\n                w.set_orie(true); // Overrun interrupt\n                w.set_peie(true); // Parity error interrupt\n                w.set_feie(true); // Framing error interrupt\n                w.set_neie(true); // Noise error interrupt\n            });\n        });\n\n        // Enable interrupt\n        T::Interrupt::unpend();\n        unsafe {\n            T::Interrupt::enable();\n        }\n\n        Ok(parts.wake_guard)\n    }\n\n    /// Helper for full-duplex initialization\n    fn new_inner_buffered<T: Instance>(\n        tx_pin: Peri<'a, AnyPin>,\n        rx_pin: Peri<'a, AnyPin>,\n        rts_pin: Option<Peri<'a, AnyPin>>,\n        cts_pin: Option<Peri<'a, AnyPin>>,\n        tx_buffer: &'a mut [u8],\n        rx_buffer: &'a mut [u8],\n        config: Config,\n    ) -> Result<Self, Error> {\n        let wg = Self::init_buffered::<T>(\n            Some(tx_buffer),\n            Some(rx_buffer),\n            config,\n            true,\n            true,\n            rts_pin.is_some(),\n            cts_pin.is_some(),\n        )?;\n\n        Ok(Self {\n            tx: LpuartTx::new_inner::<T>(tx_pin, cts_pin, Buffered, wg.clone()),\n            rx: LpuartRx::new_inner::<T>(rx_pin, rts_pin, Buffered, wg),\n        })\n    }\n\n    /// Create a new full duplex buffered LPUART.\n    ///\n    /// Any external pin will be placed into Disabled state upon Drop.\n    ///\n    /// ## SAFETY\n    ///\n    /// You must NOT call `core::mem::forget` on `Lpuart` if `rx_buffer` or `tx_buffer` are not\n    /// `'static`. This will cause memory corruption.\n    pub fn new_buffered<T: Instance>(\n        _inner: Peri<'a, T>,\n        tx_pin: Peri<'a, impl TxPin<T>>,\n        rx_pin: Peri<'a, impl RxPin<T>>,\n        _irq: impl interrupt::typelevel::Binding<T::Interrupt, BufferedInterruptHandler<T>> + 'a,\n        tx_buffer: &'a mut [u8],\n        rx_buffer: &'a mut [u8],\n        config: Config,\n    ) -> Result<Self, Error> {\n        tx_pin.as_tx();\n        rx_pin.as_rx();\n\n        Self::new_inner_buffered::<T>(tx_pin.into(), rx_pin.into(), None, None, tx_buffer, rx_buffer, config)\n    }\n\n    /// Create a new buffered LPUART instance with RTS/CTS flow control.\n    ///\n    /// Any external pin will be placed into Disabled state upon Drop.\n    ///\n    /// ## SAFETY\n    ///\n    /// You must NOT call `core::mem::forget` on `Lpuart` if `rx_buffer` or `tx_buffer` are not\n    /// `'static`. This will cause memory corruption.\n    pub fn new_buffered_with_rtscts<T: Instance>(\n        _inner: Peri<'a, T>,\n        tx_pin: Peri<'a, impl TxPin<T>>,\n        rx_pin: Peri<'a, impl RxPin<T>>,\n        rts_pin: Peri<'a, impl RtsPin<T>>,\n        cts_pin: Peri<'a, impl CtsPin<T>>,\n        _irq: impl interrupt::typelevel::Binding<T::Interrupt, BufferedInterruptHandler<T>> + 'a,\n        tx_buffer: &'a mut [u8],\n        rx_buffer: &'a mut [u8],\n        config: Config,\n    ) -> Result<Self, Error> {\n        tx_pin.as_tx();\n        rx_pin.as_rx();\n        rts_pin.as_rts();\n        cts_pin.as_cts();\n\n        Self::new_inner_buffered::<T>(\n            tx_pin.into(),\n            rx_pin.into(),\n            Some(rts_pin.into()),\n            Some(cts_pin.into()),\n            tx_buffer,\n            rx_buffer,\n            config,\n        )\n    }\n\n    /// Create a new buffered LPUART with only RTS flow control (RX flow control).\n    ///\n    /// Any external pin will be placed into Disabled state upon Drop.\n    ///\n    /// ## SAFETY\n    ///\n    /// You must NOT call `core::mem::forget` on `Lpuart` if `rx_buffer` or `tx_buffer` are not\n    /// `'static`. This will cause memory corruption.\n    pub fn new_buffered_with_rts<T: Instance>(\n        _inner: Peri<'a, T>,\n        tx_pin: Peri<'a, impl TxPin<T>>,\n        rx_pin: Peri<'a, impl RxPin<T>>,\n        rts_pin: Peri<'a, impl RtsPin<T>>,\n        _irq: impl interrupt::typelevel::Binding<T::Interrupt, BufferedInterruptHandler<T>> + 'a,\n        tx_buffer: &'a mut [u8],\n        rx_buffer: &'a mut [u8],\n        config: Config,\n    ) -> Result<Self, Error> {\n        tx_pin.as_tx();\n        rx_pin.as_rx();\n        rts_pin.as_rts();\n\n        Self::new_inner_buffered::<T>(\n            tx_pin.into(),\n            rx_pin.into(),\n            Some(rts_pin.into()),\n            None,\n            tx_buffer,\n            rx_buffer,\n            config,\n        )\n    }\n\n    /// Create a new buffered LPUART with only CTS flow control (TX flow control).\n    ///\n    /// Any external pin will be placed into Disabled state upon Drop.\n    ///\n    /// ## SAFETY\n    ///\n    /// You must NOT call `core::mem::forget` on `Lpuart` if `rx_buffer` or `tx_buffer` are not\n    /// `'static`. This will cause memory corruption.\n    pub fn new_buffered_with_cts<T: Instance>(\n        _inner: Peri<'a, T>,\n        tx_pin: Peri<'a, impl TxPin<T>>,\n        rx_pin: Peri<'a, impl RxPin<T>>,\n        cts_pin: Peri<'a, impl CtsPin<T>>,\n        _irq: impl interrupt::typelevel::Binding<T::Interrupt, BufferedInterruptHandler<T>> + 'a,\n        tx_buffer: &'a mut [u8],\n        rx_buffer: &'a mut [u8],\n        config: Config,\n    ) -> Result<Self, Error> {\n        tx_pin.as_tx();\n        rx_pin.as_rx();\n        cts_pin.as_cts();\n\n        Self::new_inner_buffered::<T>(\n            tx_pin.into(),\n            rx_pin.into(),\n            None,\n            Some(cts_pin.into()),\n            tx_buffer,\n            rx_buffer,\n            config,\n        )\n    }\n}\n\nimpl<'a> LpuartTx<'a, Buffered> {\n    /// Helper for TX-only initialization\n    fn new_inner_buffered<T: Instance>(\n        tx_pin: Peri<'a, AnyPin>,\n        cts_pin: Option<Peri<'a, AnyPin>>,\n        tx_buffer: &'a mut [u8],\n        config: Config,\n    ) -> Result<Self, Error> {\n        let wg = Lpuart::<'a, Buffered>::init_buffered::<T>(\n            Some(tx_buffer),\n            None,\n            config,\n            true,\n            false,\n            false,\n            cts_pin.is_some(),\n        )?;\n\n        Ok(Self::new_inner::<T>(tx_pin, cts_pin, Buffered, wg))\n    }\n\n    /// Create a new TX-only LPUART.\n    ///\n    /// Any external pin will be placed into Disabled state upon Drop.\n    ///\n    /// ## SAFETY\n    ///\n    /// You must NOT call `core::mem::forget` on `LpuartTx` if `tx_buffer` is not `'static`.\n    /// This will potentially send \"garbage\" data via the UART.\n    pub fn new<T: Instance>(\n        _inner: Peri<'a, T>,\n        tx_pin: Peri<'a, impl TxPin<T>>,\n        _irq: impl interrupt::typelevel::Binding<T::Interrupt, BufferedInterruptHandler<T>> + 'a,\n        tx_buffer: &'a mut [u8],\n        config: Config,\n    ) -> Result<Self, Error> {\n        tx_pin.as_tx();\n\n        let res = Self::new_inner_buffered::<T>(tx_pin.into(), None, tx_buffer, config)?;\n\n        Ok(res)\n    }\n\n    /// Create a new TX-only buffered LPUART with CTS flow control.\n    ///\n    /// Any external pin will be placed into Disabled state upon Drop.\n    ///\n    /// ## SAFETY\n    ///\n    /// You must NOT call `core::mem::forget` on `LpuartTx` if `tx_buffer` is not `'static`.\n    /// This will potentially send \"garbage\" data via the UART.\n    pub fn new_with_cts<T: Instance>(\n        _inner: Peri<'a, T>,\n        tx_pin: Peri<'a, impl TxPin<T>>,\n        cts_pin: Peri<'a, impl CtsPin<T>>,\n        _irq: impl interrupt::typelevel::Binding<T::Interrupt, BufferedInterruptHandler<T>> + 'a,\n        tx_buffer: &'a mut [u8],\n        config: Config,\n    ) -> Result<Self, Error> {\n        tx_pin.as_tx();\n        cts_pin.as_cts();\n\n        let res = Self::new_inner_buffered::<T>(tx_pin.into(), Some(cts_pin.into()), tx_buffer, config)?;\n\n        // Enable interrupt\n        T::Interrupt::unpend();\n        unsafe {\n            T::Interrupt::enable();\n        }\n\n        Ok(res)\n    }\n\n    /// Write data asynchronously\n    pub async fn write(&mut self, buf: &[u8]) -> Result<usize, Error> {\n        if buf.is_empty() {\n            return Ok(0);\n        }\n        // Wait for space in the buffer\n        self.state.tx_waker.wait_for(|| !self.state.tx_buf.is_full()).await?;\n\n        // We now know there is space, so do a normal try_write\n        Ok(self.try_write(buf))\n    }\n\n    /// Flush the TX buffer and wait for transmission to complete\n    pub async fn flush(&mut self) -> Result<(), Error> {\n        // Wait for TX buffer to empty and transmission to complete\n        Ok(self\n            .state\n            .tx_waker\n            .wait_for(|| {\n                let tx_empty = self.state.tx_buf.is_empty();\n                let fifo_empty = self.info.regs().water().read().txcount() == 0;\n                let tc_complete = self.info.regs().stat().read().tc() == Tc::COMPLETE;\n                tx_empty && fifo_empty && tc_complete\n            })\n            .await?)\n    }\n\n    /// Try to write without blocking\n    ///\n    /// May return 0 if the provided buf is zero, or there are no bytes available\n    pub fn try_write(&mut self, buf: &[u8]) -> usize {\n        if buf.is_empty() {\n            return 0;\n        }\n\n        // SAFETY: Ring buffer is initialized on `new`, there can only be one `LpuartTx`\n        // live at once, exclusive access is guaranteed by `&mut self`.\n        let mut writer = unsafe { self.state.tx_buf.writer() };\n        let starting_len = buf.len();\n        let mut window = buf;\n\n        // This will usually run once, and at most twice, if we are in a wrap-around\n        // condition.\n        loop {\n            // Get destination\n            let dst = writer.push_slice();\n            // Copy what is possible\n            let to_copy = dst.len().min(window.len());\n            let (now, later) = window.split_at(to_copy);\n            dst[..to_copy].copy_from_slice(now);\n\n            // Update the \"to send\" window to only contain the remaining unsent bytes\n            window = later;\n            // Update the ring buffer with the pushed bytes\n            writer.push_done(to_copy);\n\n            // If the copy is complete, or the buffer is full, we are done copying\n            if to_copy == 0 || window.is_empty() {\n                break;\n            }\n        }\n\n        let written = starting_len - window.len();\n\n        if written > 0 {\n            // Enable TX interrupt to start transmission\n            cortex_m::interrupt::free(|_| {\n                self.info.regs().ctrl().modify(|w| w.set_tie(true));\n            });\n        }\n\n        written\n    }\n}\n\nimpl<'a> LpuartRx<'a, Buffered> {\n    /// Helper for RX-only initialization\n    fn new_inner_buffered<T: Instance>(\n        rx_pin: Peri<'a, AnyPin>,\n        rts_pin: Option<Peri<'a, AnyPin>>,\n        rx_buffer: &'a mut [u8],\n        config: Config,\n    ) -> Result<Self, Error> {\n        let wg = Lpuart::<'a, Buffered>::init_buffered::<T>(\n            None,\n            Some(rx_buffer),\n            config,\n            false,\n            true,\n            rts_pin.is_some(),\n            false,\n        )?;\n\n        Ok(Self::new_inner::<T>(rx_pin, rts_pin, Buffered, wg))\n    }\n\n    /// Create a new RX-only buffered LPUART.\n    ///\n    /// Any external pin will be placed into Disabled state upon Drop.\n    ///\n    /// ## SAFETY\n    ///\n    /// You must NOT call `core::mem::forget` on `LpuartRx` if `rx_buffer` is not `'static`.\n    /// This will cause memory corruption.\n    pub fn new<T: Instance>(\n        _inner: Peri<'a, T>,\n        rx_pin: Peri<'a, impl RxPin<T>>,\n        _irq: impl interrupt::typelevel::Binding<T::Interrupt, BufferedInterruptHandler<T>> + 'a,\n        rx_buffer: &'a mut [u8],\n        config: Config,\n    ) -> Result<Self, Error> {\n        rx_pin.as_rx();\n\n        let res = Self::new_inner_buffered::<T>(rx_pin.into(), None, rx_buffer, config)?;\n\n        // Enable interrupt\n        T::Interrupt::unpend();\n        unsafe {\n            T::Interrupt::enable();\n        }\n\n        Ok(res)\n    }\n\n    /// Create a new RX-only buffered LPUART with RTS flow control.\n    ///\n    /// Any external pin will be placed into Disabled state upon Drop.\n    ///\n    /// ## SAFETY\n    ///\n    /// You must NOT call `core::mem::forget` on `LpuartRx` if `rx_buffer` is not `'static`.\n    /// This will cause memory corruption.\n    pub fn new_with_rts<T: Instance>(\n        _inner: Peri<'a, T>,\n        rx_pin: Peri<'a, impl RxPin<T>>,\n        rts_pin: Peri<'a, impl RtsPin<T>>,\n        _irq: impl interrupt::typelevel::Binding<T::Interrupt, BufferedInterruptHandler<T>> + 'a,\n        rx_buffer: &'a mut [u8],\n        config: Config,\n    ) -> Result<Self, Error> {\n        rx_pin.as_rx();\n        rts_pin.as_rts();\n\n        let res = Self::new_inner_buffered::<T>(rx_pin.into(), Some(rts_pin.into()), rx_buffer, config)?;\n\n        // Enable interrupt\n        T::Interrupt::unpend();\n        unsafe {\n            T::Interrupt::enable();\n        }\n\n        Ok(res)\n    }\n\n    /// Read data asynchronously\n    pub async fn read(&mut self, buf: &mut [u8]) -> Result<usize, Error> {\n        if buf.is_empty() {\n            return Ok(0);\n        }\n\n        // Wait for some data to be available\n        self.state.rx_waker.wait_for(|| !self.state.rx_buf.is_empty()).await?;\n\n        // Now do a normal try_read, we know it will return a non-zero amount\n        Ok(self.try_read(buf))\n    }\n\n    /// Try to read without blocking\n    ///\n    /// May return zero bytes if none are available, or the provided buffer is\n    /// of zero length.\n    pub fn try_read(&mut self, buf: &mut [u8]) -> usize {\n        if buf.is_empty() {\n            return 0;\n        }\n\n        // SAFETY: Ring buffer is initialized on `new`, there can only be one `LpuartRx`\n        // live at once, exclusive access is guaranteed by `&mut self`.\n        let mut reader = unsafe { self.state.rx_buf.reader() };\n        let starting_len = buf.len();\n        let mut window = buf;\n\n        // This will usually run once, and at most twice, if we are in a wrap-around\n        // condition.\n        loop {\n            // Get source amount\n            let src = reader.pop_slice();\n            // Determine the amount to copy, do so\n            let to_copy = src.len().min(window.len());\n            let (now, later) = window.split_at_mut(to_copy);\n            now.copy_from_slice(&src[..to_copy]);\n            // Tell the ring buffer the space is now free\n            reader.pop_done(to_copy);\n            // The \"to recv\" window is the amount we didn't just fill\n            window = later;\n\n            // If we copied nothing or there are no bytes left to be copied,\n            // then we are done\n            if to_copy == 0 || window.is_empty() {\n                break;\n            }\n        }\n\n        starting_len - window.len()\n    }\n}\n\n/// Buffered UART interrupt handler.\npub struct BufferedInterruptHandler<T: Instance> {\n    _phantom: PhantomData<T>,\n}\n\nimpl<T: Instance> crate::interrupt::typelevel::Handler<T::Interrupt> for BufferedInterruptHandler<T> {\n    unsafe fn on_interrupt() {\n        T::PERF_INT_INCR();\n\n        let regs = T::info().regs();\n        let state = T::state();\n\n        let ctrl = regs.ctrl().read();\n        let stat = regs.stat().read();\n        let param = regs.param().read();\n        let has_rx_fifo = param.rxfifo() > 0;\n        let has_tx_fifo = param.txfifo() > 0;\n\n        // Handle overrun error\n        if stat.or() {\n            regs.stat().write(|w| w.set_or(true));\n            T::PERF_INT_WAKE_INCR();\n            state.rx_waker.wake();\n        }\n\n        // Clear other error flags\n        if stat.pf() {\n            regs.stat().write(|w| w.set_pf(true));\n        }\n        if stat.fe() {\n            regs.stat().write(|w| w.set_fe(true));\n        }\n        if stat.nf() {\n            regs.stat().write(|w| w.set_nf(true));\n        }\n\n        // Handle RX data\n        if ctrl.rie() && (has_rx_data_pending(T::info()) || stat.idle()) {\n            let mut pushed_any = false;\n            let mut writer = unsafe { state.rx_buf.writer() };\n\n            if has_rx_fifo {\n                // Read from FIFO as long as there is data available and\n                // somewhere to put it\n                while regs.water().read().rxcount() > 0 && !state.rx_buf.is_full() {\n                    let byte = regs.data().read().0 as u8;\n                    writer.push_one(byte);\n                    pushed_any = true;\n                }\n            } else {\n                // Read single byte if possible\n                if regs.stat().read().rdrf() && !state.rx_buf.is_full() {\n                    let byte = (regs.data().read().0 & 0xFF) as u8;\n                    writer.push_one(byte);\n                    pushed_any = true;\n                }\n            }\n\n            if pushed_any {\n                T::PERF_INT_WAKE_INCR();\n                state.rx_waker.wake();\n            }\n\n            // Clear idle flag if set\n            if stat.idle() {\n                regs.stat().write(|w| w.set_idle(true));\n            }\n        }\n\n        // Handle TX data\n        if ctrl.tie() {\n            let mut sent_any = false;\n            let mut reader = unsafe { state.tx_buf.reader() };\n            let to_pop = if has_tx_fifo {\n                // tx fifo size is 2^param.txfifo, we want to pop enough to fill\n                // the fifo, minus whatever is in there now.\n                (1 << param.txfifo()) - regs.water().read().txcount()\n            } else if regs.stat().read().tdre() != Tdre::TXDATA {\n                1\n            } else {\n                0\n            };\n\n            // Send data while TX buffer is ready and we have data\n            for _ in 0..to_pop {\n                if let Some(byte) = reader.pop_one() {\n                    regs.data().write(|w| w.0 = u32::from(byte));\n                    sent_any = true;\n                } else {\n                    // No more data to send\n                    break;\n                }\n            }\n\n            if sent_any {\n                T::PERF_INT_WAKE_INCR();\n                state.tx_waker.wake();\n            }\n\n            // If buffer is empty, switch to TC interrupt or disable\n            if state.tx_buf.is_empty() {\n                cortex_m::interrupt::free(|_| {\n                    regs.ctrl().modify(|w| {\n                        w.set_tie(false);\n                        w.set_tcie(true);\n                    });\n                });\n            }\n        }\n\n        // Handle transmission complete\n        if ctrl.tcie() && regs.stat().read().tc() == Tc::COMPLETE {\n            T::PERF_INT_WAKE_INCR();\n            state.tx_waker.wake();\n\n            // Disable TC interrupt\n            cortex_m::interrupt::free(|_| {\n                regs.ctrl().modify(|w| w.set_tcie(false));\n            });\n        }\n    }\n}\n\nimpl embedded_io_async::Write for LpuartTx<'_, Buffered> {\n    async fn write(&mut self, buf: &[u8]) -> core::result::Result<usize, Self::Error> {\n        self.write(buf).await\n    }\n\n    async fn flush(&mut self) -> core::result::Result<(), Self::Error> {\n        self.flush().await\n    }\n}\n\nimpl embedded_io_async::Read for LpuartRx<'_, Buffered> {\n    async fn read(&mut self, buf: &mut [u8]) -> core::result::Result<usize, Self::Error> {\n        self.read(buf).await\n    }\n}\n\nimpl embedded_io_async::Write for Lpuart<'_, Buffered> {\n    async fn write(&mut self, buf: &[u8]) -> core::result::Result<usize, Self::Error> {\n        self.tx.write(buf).await\n    }\n\n    async fn flush(&mut self) -> core::result::Result<(), Self::Error> {\n        self.tx.flush().await\n    }\n}\n\nimpl embedded_io_async::Read for Lpuart<'_, Buffered> {\n    async fn read(&mut self, buf: &mut [u8]) -> core::result::Result<usize, Self::Error> {\n        self.rx.read(buf).await\n    }\n}\n"
  },
  {
    "path": "embassy-mcxa/src/lpuart/dma.rs",
    "content": "use core::future::Future;\n\nuse embassy_hal_internal::Peri;\n\nuse super::*;\nuse crate::dma::{\n    Channel, DMA_MAX_TRANSFER_SIZE, DmaChannel, DmaRequest, InvalidParameters, RingBuffer, TransferOptions,\n};\nuse crate::gpio::AnyPin;\nuse crate::pac::lpuart::vals::{Tc, Tdre};\n\n/// DMA mode.\npub struct Dma<'d> {\n    dma: DmaChannel<'d>,\n    request: DmaRequest,\n}\nimpl sealed::Sealed for Dma<'_> {}\nimpl Mode for Dma<'_> {}\n\n/// Lpuart RX driver with ring-buffered DMA support.\npub struct RingBufferedLpuartRx<'peri, 'ring> {\n    ring: RingBuffer<'peri, 'ring, u8>,\n}\n\nstruct TxDmaGuard<'a> {\n    dma: DmaChannel<'a>,\n    info: &'static Info,\n}\n\nimpl<'a> TxDmaGuard<'a> {\n    fn new(dma: DmaChannel<'a>, info: &'static Info) -> Self {\n        Self { dma, info }\n    }\n\n    /// Complete the transfer normally (don't abort on drop).\n    fn complete(self) {\n        // Cleanup\n        self.info.regs().baud().modify(|w| w.set_tdmae(false));\n        unsafe {\n            self.dma.disable_request();\n            self.dma.clear_done();\n        }\n        // Don't run drop since we've cleaned up\n        core::mem::forget(self);\n    }\n}\n\nimpl Drop for TxDmaGuard<'_> {\n    fn drop(&mut self) {\n        // Abort the DMA transfer if still running\n        unsafe {\n            self.dma.disable_request();\n            self.dma.clear_done();\n            self.dma.clear_interrupt();\n        }\n        // Disable UART TX DMA request\n        self.info.regs().baud().modify(|w| w.set_tdmae(false));\n    }\n}\n\n/// Guard struct for RX DMA transfers.\nstruct RxDmaGuard<'a> {\n    dma: DmaChannel<'a>,\n    info: &'static Info,\n}\n\nimpl<'a> RxDmaGuard<'a> {\n    fn new(dma: DmaChannel<'a>, info: &'static Info) -> Self {\n        Self { dma, info }\n    }\n\n    /// Complete the transfer normally (don't abort on drop).\n    fn complete(self) {\n        // Ensure DMA writes are visible to CPU\n        cortex_m::asm::dsb();\n        // Cleanup\n        self.info.regs().baud().modify(|w| w.set_rdmae(false));\n        unsafe {\n            self.dma.disable_request();\n            self.dma.clear_done();\n        }\n        // Don't run drop since we've cleaned up\n        core::mem::forget(self);\n    }\n}\n\nimpl Drop for RxDmaGuard<'_> {\n    fn drop(&mut self) {\n        // Abort the DMA transfer if still running\n        unsafe {\n            self.dma.disable_request();\n            self.dma.clear_done();\n            self.dma.clear_interrupt();\n        }\n        // Disable UART RX DMA request\n        self.info.regs().baud().modify(|w| w.set_rdmae(false));\n    }\n}\n\nimpl<'a> LpuartTx<'a, Dma<'a>> {\n    /// Create a new LPUART TX driver with DMA support.\n    ///\n    /// Any external pin will be placed into Disabled state upon Drop.\n    pub fn new_async_with_dma<T: Instance>(\n        _inner: Peri<'a, T>,\n        tx_pin: Peri<'a, impl TxPin<T>>,\n        tx_dma_ch: Peri<'a, impl Channel>,\n        config: Config,\n    ) -> Result<Self, Error> {\n        tx_pin.as_tx();\n        let tx_pin: Peri<'a, AnyPin> = tx_pin.into();\n\n        // Initialize LPUART with TX enabled, RX disabled, no flow control\n        let wg = Lpuart::<Dma<'_>>::init::<T>(true, false, false, false, config)?;\n\n        // Enable interrupt\n        let dma = DmaChannel::new(tx_dma_ch);\n        dma.enable_interrupt();\n\n        Ok(Self::new_inner::<T>(\n            tx_pin,\n            None,\n            Dma {\n                dma,\n                request: T::TX_DMA_REQUEST,\n            },\n            wg,\n        ))\n    }\n\n    /// Write data using DMA.\n    ///\n    /// This configures the DMA channel for a memory-to-peripheral transfer\n    /// and waits for completion asynchronously. Large buffers are automatically\n    /// split into chunks that fit within the DMA transfer limit.\n    ///\n    /// The DMA request source is automatically derived from the LPUART instance type.\n    ///\n    /// # Safety\n    ///\n    /// If the returned future is dropped before completion (e.g., due to a timeout),\n    /// the DMA transfer is automatically aborted to prevent use-after-free.\n    ///\n    /// # Arguments\n    /// * `buf` - Data buffer to transmit\n    pub async fn write(&mut self, buf: &[u8]) -> Result<usize, Error> {\n        if buf.is_empty() {\n            return Ok(0);\n        }\n\n        let mut total = 0;\n        for chunk in buf.chunks(DMA_MAX_TRANSFER_SIZE) {\n            total += self.write_dma_inner(chunk).await?;\n        }\n\n        Ok(total)\n    }\n\n    /// Internal helper to write a single chunk (max 0x7FFF bytes) using DMA.\n    async fn write_dma_inner(&mut self, buf: &[u8]) -> Result<usize, Error> {\n        let len = buf.len();\n        let peri_addr = self.info.regs().data().as_ptr() as *mut u8;\n\n        let dma = &mut self.mode.dma;\n\n        unsafe {\n            // Clean up channel state\n            dma.disable_request();\n            dma.clear_done();\n            dma.clear_interrupt();\n\n            // Set DMA request source from instance type (type-safe)\n            dma.set_request_source(self.mode.request);\n\n            // Configure TCD for memory-to-peripheral transfer\n            dma.setup_write_to_peripheral(buf, peri_addr, false, TransferOptions::COMPLETE_INTERRUPT)?;\n\n            // Enable UART TX DMA request\n            self.info.regs().baud().modify(|w| w.set_tdmae(true));\n\n            // Enable DMA channel request\n            dma.enable_request();\n        }\n\n        // Create guard that will abort DMA if this future is dropped\n        let guard = TxDmaGuard::new(dma.reborrow(), self.info);\n\n        // Wait for completion asynchronously\n        core::future::poll_fn(|cx| {\n            guard.dma.waker().register(cx.waker());\n            if guard.dma.is_done() {\n                core::task::Poll::Ready(())\n            } else {\n                core::task::Poll::Pending\n            }\n        })\n        .await;\n\n        // Transfer completed successfully - clean up without aborting\n        guard.complete();\n\n        Ok(len)\n    }\n\n    /// Blocking write (fallback when DMA is not needed)\n    pub fn blocking_write(&mut self, buf: &[u8]) -> Result<(), Error> {\n        for &byte in buf {\n            while self.info.regs().stat().read().tdre() == Tdre::TXDATA {}\n            self.info.regs().data().write(|w| w.0 = byte as u32);\n        }\n        Ok(())\n    }\n\n    /// Flush TX blocking\n    pub fn blocking_flush(&mut self) -> Result<(), Error> {\n        while self.info.regs().water().read().txcount() != 0 {}\n        while self.info.regs().stat().read().tc() == Tc::ACTIVE {}\n        Ok(())\n    }\n}\n\nimpl<'a> LpuartRx<'a, Dma<'a>> {\n    /// Create a new LPUART RX driver with DMA support.\n    ///\n    /// Any external pin will be placed into Disabled state upon Drop.\n    pub fn new_async_with_dma<T: Instance>(\n        _inner: Peri<'a, T>,\n        rx_pin: Peri<'a, impl RxPin<T>>,\n        rx_dma_ch: Peri<'a, impl Channel>,\n        config: Config,\n    ) -> Result<Self, Error> {\n        rx_pin.as_rx();\n        let rx_pin: Peri<'a, AnyPin> = rx_pin.into();\n\n        // Initialize LPUART with TX disabled, RX enabled, no flow control\n        let _wg = Lpuart::<Dma<'_>>::init::<T>(false, true, false, false, config)?;\n\n        // Enable dma interrupt\n        let dma = DmaChannel::new(rx_dma_ch);\n        dma.enable_interrupt();\n\n        Ok(Self::new_inner::<T>(\n            rx_pin,\n            None,\n            Dma {\n                dma,\n                request: T::RX_DMA_REQUEST,\n            },\n            _wg,\n        ))\n    }\n\n    /// Read data using DMA.\n    ///\n    /// This configures the DMA channel for a peripheral-to-memory transfer\n    /// and waits for completion asynchronously. Large buffers are automatically\n    /// split into chunks that fit within the DMA transfer limit.\n    ///\n    /// The DMA request source is automatically derived from the LPUART instance type.\n    ///\n    /// # Safety\n    ///\n    /// If the returned future is dropped before completion (e.g., due to a timeout),\n    /// the DMA transfer is automatically aborted to prevent use-after-free.\n    ///\n    /// # Arguments\n    /// * `buf` - Buffer to receive data into\n    pub async fn read(&mut self, buf: &mut [u8]) -> Result<usize, Error> {\n        if buf.is_empty() {\n            return Ok(0);\n        }\n\n        let mut total = 0;\n        for chunk in buf.chunks_mut(DMA_MAX_TRANSFER_SIZE) {\n            total += self.read_dma_inner(chunk).await?;\n        }\n\n        Ok(total)\n    }\n\n    /// Internal helper to read a single chunk (max 0x7FFF bytes) using DMA.\n    async fn read_dma_inner(&mut self, buf: &mut [u8]) -> Result<usize, Error> {\n        // First check if there are any RX errors\n        check_and_clear_rx_errors(self.info)?;\n\n        // We already check in the public function that the length is\n        // 0 < buf.len <= DMA_MAX_TRANSFER_SIZE\n        let len = buf.len();\n        let peri_addr = self.info.regs().data().as_ptr() as *const u8;\n        let rx_dma = &mut self.mode.dma;\n\n        unsafe {\n            // Clean up channel state\n            rx_dma.disable_request();\n            rx_dma.clear_done();\n            rx_dma.clear_interrupt();\n\n            // Set DMA request source from instance type (type-safe)\n            rx_dma.set_request_source(self.mode.request);\n\n            // Configure TCD for peripheral-to-memory transfer\n            rx_dma.setup_read_from_peripheral(peri_addr, buf, false, TransferOptions::COMPLETE_INTERRUPT)?;\n\n            // Enable UART RX DMA request\n            self.info.regs().baud().modify(|w| w.set_rdmae(true));\n\n            // Enable DMA channel request\n            rx_dma.enable_request();\n        }\n\n        // Create guard that will abort DMA if this future is dropped\n        let guard = RxDmaGuard::new(rx_dma.reborrow(), self.info);\n\n        // Wait for completion asynchronously\n        core::future::poll_fn(|cx| {\n            guard.dma.waker().register(cx.waker());\n            if guard.dma.is_done() {\n                core::task::Poll::Ready(())\n            } else {\n                core::task::Poll::Pending\n            }\n        })\n        .await;\n\n        // Transfer completed successfully - clean up without aborting\n        guard.complete();\n\n        Ok(len)\n    }\n\n    /// Blocking read (fallback when DMA is not needed)\n    pub fn blocking_read(&mut self, buf: &mut [u8]) -> Result<(), Error> {\n        for byte in buf.iter_mut() {\n            loop {\n                if has_rx_data_pending(self.info) {\n                    *byte = (self.info.regs().data().read().0 & 0xFF) as u8;\n                    break;\n                }\n                check_and_clear_rx_errors(self.info)?;\n            }\n        }\n        Ok(())\n    }\n\n    pub fn into_ring_dma_rx<'buf: 'a>(\n        &mut self,\n        buf: &'buf mut [u8],\n    ) -> Result<RingBufferedLpuartRx<'_, 'buf>, InvalidParameters> {\n        let ring = self.setup_ring_buffer(buf)?;\n        unsafe { ring.enable_dma_request() };\n        Ok(RingBufferedLpuartRx { ring })\n    }\n\n    /// Set up a ring buffer for continuous DMA reception.\n    ///\n    /// This configures the DMA channel for circular operation, enabling continuous\n    /// reception of data without gaps. The DMA will continuously write received\n    /// bytes into the buffer, wrapping around when it reaches the end.\n    ///\n    /// This method encapsulates all the low-level setup:\n    /// - Configures the DMA request source for this LPUART instance\n    /// - Enables the RX DMA request in the LPUART peripheral\n    /// - Sets up the circular DMA transfer\n    /// - Enables the NVIC interrupt for async wakeups\n    ///\n    /// # Arguments\n    ///\n    /// * `buf` - Destination buffer for received data (power-of-2 size is ideal for efficiency)\n    ///\n    /// # Returns\n    ///\n    /// A [`RingBuffer`] that can be used to asynchronously read received data.\n    ///\n    /// # Example\n    ///\n    /// ```rust,no_run\n    /// # #![no_std]\n    /// # #![no_main]\n    /// #\n    /// # extern crate panic_halt;\n    /// # extern crate embassy_mcxa;\n    /// # extern crate embassy_executor;\n    /// # use panic_halt as _;\n    /// # use embassy_executor::Spawner;\n    /// # use embassy_mcxa::clocks::config::Div8;\n    /// # use embassy_mcxa::config::Config;\n    /// # use embassy_mcxa::lpuart;\n    /// # use static_cell::ConstStaticCell;\n    /// #\n    /// static RX_RING_BUFFER: ConstStaticCell<[u8; 64]> = ConstStaticCell::new([0; 64]);\n    /// # #[embassy_executor::main]\n    /// # async fn main(_spawner: Spawner) {\n    /// #     let mut cfg = Config::default();\n    /// #     cfg.clock_cfg.sirc.fro_12m_enabled = true;\n    /// #     cfg.clock_cfg.sirc.fro_lf_div = Some(Div8::no_div());\n    /// #\n    /// #     let p = embassy_mcxa::init(cfg);\n    /// #\n    /// #     // Create UART configuration\n    /// #     let config = lpuart::Config {\n    /// #     baudrate_bps: 115_200,\n    /// #     ..Default::default()\n    /// #     };\n    ///\n    ///      // Create UART instance with DMA channels\n    ///      let mut lpuart = lpuart::Lpuart::new_async_with_dma(\n    ///          p.LPUART2, // Instance\n    ///          p.P2_2,    // TX pin\n    ///          p.P2_3,    // RX pin\n    ///          p.DMA_CH0, // TX DMA channel\n    ///          p.DMA_CH1, // RX DMA channel\n    ///          config,\n    ///      )\n    ///      .unwrap();\n    ///\n    ///      let (_, mut rx) = lpuart.split();\n    ///\n    ///      let buf = RX_RING_BUFFER.take();\n    ///      // Set up the ring buffer with circular DMA\n    ///      let mut ring_buf = rx.into_ring_dma_rx(buf).unwrap();\n    ///      let mut read_buf = [0u8; 16];\n    ///      let n = ring_buf.read(&mut read_buf).await.unwrap();\n    /// # }\n    /// ```\n    fn setup_ring_buffer<'buf: 'a>(\n        &mut self,\n        buf: &'buf mut [u8],\n    ) -> Result<RingBuffer<'_, 'buf, u8>, InvalidParameters> {\n        // Get the peripheral data register address\n        let peri_addr = self.info.regs().data().as_ptr() as *const u8;\n\n        // Configure DMA request source for this LPUART instance (type-safe)\n        unsafe {\n            self.mode.dma.set_request_source(self.mode.request);\n        }\n\n        // Enable RX DMA request in the LPUART peripheral\n        self.info.regs().baud().modify(|w| w.set_rdmae(true));\n\n        // Set up circular DMA transfer (this also enables NVIC interrupt)\n        unsafe { self.mode.dma.setup_circular_read(peri_addr, buf) }\n    }\n}\n\nimpl<'peri, 'buf> RingBufferedLpuartRx<'peri, 'buf> {\n    /// Read from the ring buffer\n    pub fn read<'d>(\n        &mut self,\n        dst: &'d mut [u8],\n    ) -> impl Future<Output = core::result::Result<usize, crate::dma::Error>> + use<'_, 'buf, 'd> {\n        self.ring.read(dst)\n    }\n\n    /// Clear the current contents of the ring buffer\n    pub fn clear(&mut self) {\n        self.ring.clear();\n    }\n}\n\nimpl<'a> Lpuart<'a, Dma<'a>> {\n    /// Create a new LPUART driver with DMA support for both TX and RX.\n    ///\n    /// Any external pin will be placed into Disabled state upon Drop.\n    pub fn new_async_with_dma<T: Instance>(\n        _inner: Peri<'a, T>,\n        tx_pin: Peri<'a, impl TxPin<T>>,\n        rx_pin: Peri<'a, impl RxPin<T>>,\n        tx_dma_ch: Peri<'a, impl Channel>,\n        rx_dma_ch: Peri<'a, impl Channel>,\n        config: Config,\n    ) -> Result<Self, Error> {\n        tx_pin.as_tx();\n        rx_pin.as_rx();\n\n        let tx_pin: Peri<'a, AnyPin> = tx_pin.into();\n        let rx_pin: Peri<'a, AnyPin> = rx_pin.into();\n\n        // Initialize LPUART with both TX and RX enabled, no flow control\n        let wg = Lpuart::<Dma<'a>>::init::<T>(true, true, false, false, config)?;\n\n        // Enable DMA interrupts\n        let tx_dma = DmaChannel::new(tx_dma_ch);\n        let rx_dma = DmaChannel::new(rx_dma_ch);\n        tx_dma.enable_interrupt();\n        rx_dma.enable_interrupt();\n\n        Ok(Self {\n            tx: LpuartTx::new_inner::<T>(\n                tx_pin,\n                None,\n                Dma {\n                    dma: tx_dma,\n                    request: T::TX_DMA_REQUEST,\n                },\n                wg.clone(),\n            ),\n            rx: LpuartRx::new_inner::<T>(\n                rx_pin,\n                None,\n                Dma {\n                    dma: rx_dma,\n                    request: T::RX_DMA_REQUEST,\n                },\n                wg,\n            ),\n        })\n    }\n\n    /// Write data using DMA\n    pub async fn write(&mut self, buf: &[u8]) -> Result<usize, Error> {\n        self.tx.write(buf).await\n    }\n\n    /// Read data using DMA\n    pub async fn read(&mut self, buf: &mut [u8]) -> Result<usize, Error> {\n        self.rx.read(buf).await\n    }\n}\n\nimpl<'a> embedded_io_async::Read for LpuartRx<'a, Dma<'a>> {\n    async fn read(&mut self, buf: &mut [u8]) -> core::result::Result<usize, Self::Error> {\n        self.read(buf).await\n    }\n}\n\nimpl<'a> embedded_io_async::Write for LpuartTx<'a, Dma<'a>> {\n    async fn write(&mut self, buf: &[u8]) -> core::result::Result<usize, Self::Error> {\n        self.write(buf).await\n    }\n\n    async fn flush(&mut self) -> core::result::Result<(), Self::Error> {\n        // No-op, when DMA transfer is completed, it is also flushed.\n        Ok(())\n    }\n}\n\nimpl<'a> embedded_io_async::Read for Lpuart<'a, Dma<'a>> {\n    async fn read(&mut self, buf: &mut [u8]) -> core::result::Result<usize, Self::Error> {\n        self.rx.read(buf).await\n    }\n}\n\nimpl<'a> embedded_io_async::Write for Lpuart<'a, Dma<'a>> {\n    async fn write(&mut self, buf: &[u8]) -> core::result::Result<usize, Self::Error> {\n        self.tx.write(buf).await\n    }\n\n    async fn flush(&mut self) -> core::result::Result<(), Self::Error> {\n        // No-op, when DMA transfer is completed, it is also flushed.\n        self.tx.flush().await\n    }\n}\n"
  },
  {
    "path": "embassy-mcxa/src/lpuart/mod.rs",
    "content": "use core::marker::PhantomData;\nuse core::sync::atomic::{AtomicU8, Ordering};\n\nuse embassy_hal_internal::atomic_ring_buffer::RingBuffer;\nuse embassy_hal_internal::{Peri, PeripheralType};\nuse maitake_sync::WaitCell;\nuse nxp_pac::lpuart::vals::Dozeen;\nuse paste::paste;\n\nuse crate::clocks::periph_helpers::{Div4, LpuartClockSel, LpuartConfig};\nuse crate::clocks::{ClockError, Gate, PoweredClock, WakeGuard, enable_and_reset};\nuse crate::dma::DmaRequest;\nuse crate::gpio::{AnyPin, SealedPin};\nuse crate::interrupt::typelevel::Interrupt;\nuse crate::pac::lpuart::vals::{\n    Idlecfg as IdleConfig, Ilt as IdleType, M as DataBits, Msbf as MsbFirst, Pt as Parity, Rst, Rxflush,\n    Sbns as StopBits, Swap, Tc, Tdre, Txctsc as TxCtsConfig, Txctssrc as TxCtsSource, Txflush,\n};\nuse crate::{interrupt, pac};\n\nmod bbq;\nmod blocking;\nmod buffered;\nmod dma;\n\npub use bbq::{\n    BbqConfig, BbqError, BbqHalfParts, BbqInterruptHandler, BbqParts, BbqRxMode, LpuartBbq, LpuartBbqRx, LpuartBbqTx,\n};\npub use blocking::Blocking;\npub use buffered::{Buffered, BufferedInterruptHandler};\npub use dma::{Dma, RingBufferedLpuartRx};\n\nmod sealed {\n    pub trait Sealed {}\n}\n\nstruct State {\n    tx_waker: WaitCell,\n    tx_buf: RingBuffer,\n    rx_waker: WaitCell,\n    rx_buf: RingBuffer,\n    tx_rx_refmask: TxRxRefMask,\n}\n\n/// Value corresponding to either the Tx or the Rx part of the Uart being active.\n#[derive(Clone, Copy)]\n#[repr(u8)]\nenum TxRxRef {\n    Rx = 0b01,\n    Tx = 0b10,\n}\n\n/// Mask that stores whether a Tx and/or Rx part of the Uart is active.\n///\n/// Used in constructors and Drop to manage the peripheral lifetime.\nstruct TxRxRefMask(AtomicU8);\n\nimpl TxRxRefMask {\n    pub const fn new() -> Self {\n        Self(AtomicU8::new(0))\n    }\n\n    /// Atomically signal that either the Tx or Rx has been dropped.\n    ///\n    /// Returns `true` if after this call all parts are inactive.\n    pub fn set_inactive_fetch_last(&self, value: TxRxRef) -> bool {\n        let value = value as u8;\n        self.0.fetch_and(!value, Ordering::AcqRel) & !value == 0\n    }\n\n    /// Atomically signal that either the Tx or Rx has been created.\n    pub fn set_active(&self, value: TxRxRef) {\n        let value = value as u8;\n        self.0.fetch_or(value, Ordering::AcqRel);\n    }\n\n    /// Atomically determine if either channels have been created, but not dropped. Clears the state.\n    ///\n    /// Should only be relevant when any of the parts have been leaked using [core::mem::forget].\n    pub fn fetch_any_alive_reset(&self) -> bool {\n        self.0.swap(0, Ordering::AcqRel) != 0\n    }\n}\n\nimpl Default for State {\n    fn default() -> Self {\n        Self::new()\n    }\n}\n\nimpl State {\n    /// Create a new state instance\n    pub const fn new() -> Self {\n        Self {\n            tx_waker: WaitCell::new(),\n            tx_buf: RingBuffer::new(),\n            rx_waker: WaitCell::new(),\n            rx_buf: RingBuffer::new(),\n            tx_rx_refmask: TxRxRefMask::new(),\n        }\n    }\n}\n\nstruct Info {\n    regs: crate::pac::lpuart::Lpuart,\n    int_disable: fn(),\n    mrcc_disable: unsafe fn(),\n}\n\nunsafe impl Sync for Info {}\n\nimpl Info {\n    #[inline(always)]\n    fn regs(&self) -> crate::pac::lpuart::Lpuart {\n        self.regs\n    }\n}\n\ntrait SealedInstance: Gate<MrccPeriphConfig = LpuartConfig> {\n    fn info() -> &'static Info;\n    fn state() -> &'static State;\n\n    const CLOCK_INSTANCE: crate::clocks::periph_helpers::LpuartInstance;\n    const TX_DMA_REQUEST: DmaRequest;\n    const RX_DMA_REQUEST: DmaRequest;\n    const PERF_INT_INCR: fn();\n    const PERF_INT_WAKE_INCR: fn();\n}\n\n/// Trait for LPUART peripheral instances\n#[allow(private_bounds)]\npub trait Instance: SealedInstance + PeripheralType + 'static + Send {\n    type Interrupt: interrupt::typelevel::Interrupt;\n}\n\nmacro_rules! impl_instance {\n    ($($n:expr);* $(;)?) => {\n        $(\n            paste!{\n                impl SealedInstance for crate::peripherals::[<LPUART $n>] {\n                    fn info() -> &'static Info {\n                        static INFO: Info = Info {\n                            regs: pac::[<LPUART $n>],\n                            int_disable: crate::interrupt::typelevel::[<LPUART $n>]::disable,\n                            mrcc_disable: crate::clocks::disable::<crate::peripherals::[<LPUART $n>]>,\n                        };\n                        &INFO\n                    }\n\n                    fn state() -> &'static State {\n                        static STATE: State = State::new();\n                        &STATE\n                    }\n\n                    const CLOCK_INSTANCE: crate::clocks::periph_helpers::LpuartInstance\n                        = crate::clocks::periph_helpers::LpuartInstance::[<Lpuart $n>];\n                    const TX_DMA_REQUEST: DmaRequest = DmaRequest::[<LPUART $n Tx>];\n                    const RX_DMA_REQUEST: DmaRequest = DmaRequest::[<LPUART $n Rx>];\n                    const PERF_INT_INCR: fn() = crate::perf_counters::[<incr_interrupt_lpuart $n>];\n                    const PERF_INT_WAKE_INCR: fn() = crate::perf_counters::[<incr_interrupt_lpuart $n _wake>];\n                }\n\n                impl Instance for crate::peripherals::[<LPUART $n>] {\n                    type Interrupt = crate::interrupt::typelevel::[<LPUART $n>];\n\n                }\n            }\n        )*\n    };\n}\n\n// DMA request sources are now type-safe via associated types.\n// The request source numbers are defined in src/dma.rs:\n// LPUART0: RX=21, TX=22 -> Lpuart0RxRequest, Lpuart0TxRequest\n// LPUART1: RX=23, TX=24 -> Lpuart1RxRequest, Lpuart1TxRequest\n// LPUART2: RX=25, TX=26 -> Lpuart2RxRequest, Lpuart2TxRequest\n// LPUART3: RX=27, TX=28 -> Lpuart3RxRequest, Lpuart3TxRequest\n// LPUART4: RX=29, TX=30 -> Lpuart4RxRequest, Lpuart4TxRequest\n// LPUART5: RX=31, TX=32 -> Lpuart5RxRequest, Lpuart5TxRequest\nimpl_instance!(0; 1; 2; 3; 4);\n#[cfg(feature = \"mcxa5xx\")]\nimpl_instance!(5);\n\n/// Perform software reset on the LPUART peripheral\nfn perform_software_reset(info: &'static Info) {\n    // Software reset - set and clear RST bit (Global register)\n    info.regs().global().write(|w| w.set_rst(Rst::RESET));\n    info.regs().global().write(|w| w.set_rst(Rst::NO_EFFECT));\n}\n\n/// Disable both transmitter and receiver\nfn disable_transceiver(info: &'static Info) {\n    info.regs().ctrl().modify(|w| {\n        w.set_te(false);\n        w.set_re(false);\n    });\n}\n\n/// Calculate and configure baudrate settings\nfn configure_baudrate(info: &'static Info, baudrate_bps: u32, clock_freq: u32) -> Result<(), Error> {\n    let (osr, sbr) = calculate_baudrate(baudrate_bps, clock_freq)?;\n\n    // Configure BAUD register\n    info.regs().baud().modify(|w| {\n        // Clear and set OSR\n        w.set_osr(osr - 1);\n        // Clear and set SBR\n        w.set_sbr(sbr);\n        // Set BOTHEDGE if OSR is between 4 and 7\n        w.set_bothedge(osr > 3 && osr < 8);\n    });\n\n    Ok(())\n}\n\n/// Configure frame format (stop bits, data bits)\nfn configure_frame_format(info: &'static Info, config: &Config) {\n    // Configure stop bits\n    info.regs().baud().modify(|w| w.set_sbns(config.stop_bits_count));\n\n    // Clear M10 for now (10-bit mode)\n    info.regs().baud().modify(|w| w.set_m10(false));\n}\n\n/// Configure control settings (parity, data bits, idle config, pin swap)\nfn configure_control_settings(info: &'static Info, config: &Config) {\n    info.regs().ctrl().modify(|w| {\n        // Parity configuration\n        if let Some(parity) = config.parity_mode {\n            w.set_pe(true);\n            w.set_pt(parity);\n        } else {\n            w.set_pe(false);\n        };\n\n        // Allow the lpuart to wake from deep sleep if configured to\n        // work in deep sleep mode.\n        //\n        // NOTE: this is the default state, and setting this to `Dozeen::DISABLED`\n        // seems to actively *stop* the uart, regardless of whether automatic clock\n        // gating is used, or if the device never goes to deep sleep at all (e.g.\n        // in WfeUngated configuration). For now, let's not touch this unless we\n        // actually need to, e.g. *forcing* the lpuart to sleep!\n        w.set_dozeen(Dozeen::ENABLED);\n\n        // Data bits configuration\n        match config.data_bits_count {\n            DataBits::DATA8 => {\n                if config.parity_mode.is_some() {\n                    w.set_m(DataBits::DATA9); // 8 data + 1 parity = 9 bits\n                } else {\n                    w.set_m(DataBits::DATA8); // 8 data bits only\n                }\n            }\n            DataBits::DATA9 => w.set_m(DataBits::DATA9),\n        };\n\n        // Idle configuration\n        w.set_idlecfg(config.rx_idle_config);\n        w.set_ilt(config.rx_idle_type);\n\n        // Swap TXD/RXD if configured\n        if config.swap_txd_rxd {\n            w.set_swap(Swap::SWAP);\n        } else {\n            w.set_swap(Swap::STANDARD);\n        }\n    });\n}\n\n/// Configure FIFO settings and watermarks\nfn configure_fifo(info: &'static Info, config: &Config) {\n    // Configure WATER register for FIFO watermarks\n    info.regs().water().write(|w| {\n        w.set_rxwater(config.rx_fifo_watermark);\n        w.set_txwater(config.tx_fifo_watermark);\n    });\n\n    // Enable TX/RX FIFOs\n    info.regs().fifo().modify(|w| {\n        w.set_txfe(true);\n        w.set_rxfe(true);\n    });\n\n    // Flush FIFOs\n    info.regs().fifo().modify(|w| {\n        w.set_txflush(Txflush::TXFIFO_RST);\n        w.set_rxflush(Rxflush::RXFIFO_RST);\n    });\n}\n\n/// Clear all status flags\nfn clear_all_status_flags(info: &'static Info) {\n    info.regs().stat().modify(|_w| {\n        // Write back all values, clearing the W1C fields implicitly.\n    });\n}\n\n/// Configure hardware flow control if enabled\nfn configure_flow_control(info: &'static Info, enable_tx_cts: bool, enable_rx_rts: bool, config: &Config) {\n    if enable_rx_rts || enable_tx_cts {\n        info.regs().modir().modify(|w| {\n            w.set_txctsc(config.tx_cts_config);\n            w.set_txctssrc(config.tx_cts_source);\n            w.set_rxrtse(enable_rx_rts);\n            w.set_txctse(enable_tx_cts);\n        });\n    }\n}\n\n/// Configure bit order (MSB first or LSB first)\nfn configure_bit_order(info: &'static Info, msb_first: MsbFirst) {\n    info.regs().stat().modify(|w| w.set_msbf(msb_first));\n}\n\n/// Enable transmitter and/or receiver based on configuration\nfn enable_transceiver(info: &'static Info, enable_tx: bool, enable_rx: bool) {\n    info.regs().ctrl().modify(|w| {\n        if enable_tx {\n            w.set_te(true);\n        }\n        if enable_rx {\n            w.set_re(true);\n        }\n    });\n}\n\nfn calculate_baudrate(baudrate: u32, src_clock_hz: u32) -> Result<(u8, u16), Error> {\n    let mut baud_diff = baudrate;\n    let mut osr = 0u8;\n    let mut sbr = 0u16;\n\n    // Try OSR values from 4 to 32\n    for osr_temp in 4u8..=32u8 {\n        // Calculate SBR: (srcClock_Hz * 2 / (baudRate * osr) + 1) / 2\n        let sbr_calc = ((src_clock_hz * 2) / (baudrate * osr_temp as u32)).div_ceil(2);\n\n        let sbr_temp = if sbr_calc == 0 {\n            1\n        } else if sbr_calc > 0x1FFF {\n            0x1FFF\n        } else {\n            sbr_calc as u16\n        };\n\n        // Calculate actual baud rate\n        let calculated_baud = src_clock_hz / (osr_temp as u32 * sbr_temp as u32);\n\n        let temp_diff = calculated_baud.abs_diff(baudrate);\n\n        if temp_diff <= baud_diff {\n            baud_diff = temp_diff;\n            osr = osr_temp;\n            sbr = sbr_temp;\n        }\n    }\n\n    // Check if baud rate difference is within 3%\n    if baud_diff > (baudrate / 100) * 3 {\n        return Err(Error::UnsupportedBaudrate);\n    }\n\n    Ok((osr, sbr))\n}\n\n/// Wait for all transmit operations to complete\nfn wait_for_tx_complete(info: &'static Info) {\n    // Wait for TX FIFO to empty\n    while info.regs().water().read().txcount() != 0 {\n        // Wait for TX FIFO to drain\n    }\n\n    // Wait for last character to shift out (TC = Transmission Complete)\n    while info.regs().stat().read().tc() == Tc::ACTIVE {\n        // Wait for transmission to complete\n    }\n}\n\nfn check_and_clear_rx_errors(info: &'static Info) -> Result<(), Error> {\n    let stat = info.regs().stat().read();\n\n    // Check for overrun first - other error flags are prevented when OR is set\n    let or_set = stat.or();\n    let pf_set = stat.pf();\n    let fe_set = stat.fe();\n    let nf_set = stat.nf();\n\n    // Clear all errors before returning\n    info.regs().stat().write(|w| {\n        w.set_or(or_set);\n        w.set_pf(pf_set);\n        w.set_fe(fe_set);\n        w.set_nf(nf_set);\n    });\n\n    // Return error source\n    if or_set {\n        Err(Error::Overrun)\n    } else if pf_set {\n        Err(Error::Parity)\n    } else if fe_set {\n        Err(Error::Framing)\n    } else if nf_set {\n        Err(Error::Noise)\n    } else {\n        Ok(())\n    }\n}\n\nfn has_rx_data_pending(info: &'static Info) -> bool {\n    if info.regs().param().read().rxfifo() > 0 {\n        // FIFO is available - check RXCOUNT in WATER register\n        info.regs().water().read().rxcount() > 0\n    } else {\n        // No FIFO - check RDRF flag in STAT register\n        info.regs().stat().read().rdrf()\n    }\n}\n\nimpl<T: SealedPin> sealed::Sealed for T {}\n\npub trait TxPin<T: Instance>: Into<AnyPin> + sealed::Sealed + PeripheralType {\n    const MUX: crate::pac::port::vals::Mux;\n    /// convert the pin to appropriate function for Lpuart Tx  usage\n    fn as_tx(&self);\n}\n\npub trait RxPin<T: Instance>: Into<AnyPin> + sealed::Sealed + PeripheralType {\n    const MUX: crate::pac::port::vals::Mux;\n    /// convert the pin to appropriate function for Lpuart Rx  usage\n    fn as_rx(&self);\n}\n\npub trait CtsPin<T: Instance>: Into<AnyPin> + sealed::Sealed + PeripheralType {\n    const MUX: crate::pac::port::vals::Mux;\n    /// convert the pin to appropriate function for Lpuart Cts usage\n    fn as_cts(&self);\n}\n\npub trait RtsPin<T: Instance>: Into<AnyPin> + sealed::Sealed + PeripheralType {\n    const MUX: crate::pac::port::vals::Mux;\n    /// convert the pin to appropriate function for Lpuart Rts usage\n    fn as_rts(&self);\n}\n\nmacro_rules! impl_tx_pin {\n    ($inst:ident, $pin:ident, $alt:ident) => {\n        impl TxPin<crate::peripherals::$inst> for crate::peripherals::$pin {\n            const MUX: crate::pac::port::vals::Mux = crate::pac::port::vals::Mux::$alt;\n            fn as_tx(&self) {\n                self.set_pull(crate::gpio::Pull::Disabled);\n                self.set_slew_rate(crate::gpio::SlewRate::Fast.into());\n                self.set_drive_strength(crate::gpio::DriveStrength::Normal.into());\n                self.set_function(<Self as TxPin<crate::peripherals::$inst>>::MUX);\n                self.set_enable_input_buffer(false);\n            }\n        }\n    };\n}\n\nmacro_rules! impl_rx_pin {\n    ($inst:ident, $pin:ident, $alt:ident) => {\n        impl RxPin<crate::peripherals::$inst> for crate::peripherals::$pin {\n            const MUX: crate::pac::port::vals::Mux = crate::pac::port::vals::Mux::$alt;\n            fn as_rx(&self) {\n                self.set_pull(crate::gpio::Pull::Disabled);\n                self.set_function(<Self as RxPin<crate::peripherals::$inst>>::MUX);\n                self.set_enable_input_buffer(true);\n            }\n        }\n    };\n}\n\nmacro_rules! impl_cts_pin {\n    ($inst:ident, $pin:ident, $alt:ident) => {\n        impl CtsPin<crate::peripherals::$inst> for crate::peripherals::$pin {\n            const MUX: crate::pac::port::vals::Mux = crate::pac::port::vals::Mux::$alt;\n            fn as_cts(&self) {\n                self.set_pull(crate::gpio::Pull::Disabled);\n                self.set_function(<Self as CtsPin<crate::peripherals::$inst>>::MUX);\n                self.set_enable_input_buffer(true);\n            }\n        }\n    };\n}\n\nmacro_rules! impl_rts_pin {\n    ($inst:ident, $pin:ident, $alt:ident) => {\n        impl RtsPin<crate::peripherals::$inst> for crate::peripherals::$pin {\n            const MUX: crate::pac::port::vals::Mux = crate::pac::port::vals::Mux::$alt;\n            fn as_rts(&self) {\n                self.set_pull(crate::gpio::Pull::Disabled);\n                self.set_slew_rate(crate::gpio::SlewRate::Fast.into());\n                self.set_drive_strength(crate::gpio::DriveStrength::Normal.into());\n                self.set_function(<Self as RtsPin<crate::peripherals::$inst>>::MUX);\n                self.set_enable_input_buffer(false);\n            }\n        }\n    };\n}\n\n// LPUART 0\n#[cfg(feature = \"jtag-extras-as-gpio\")]\nimpl_tx_pin!(LPUART0, P0_3, MUX2);\nimpl_tx_pin!(LPUART0, P0_21, MUX3);\nimpl_tx_pin!(LPUART0, P2_1, MUX2);\n#[cfg(feature = \"mcxa5xx\")]\nimpl_tx_pin!(LPUART0, P4_9, MUX2);\n\n#[cfg(feature = \"swd-swo-as-gpio\")]\nimpl_rx_pin!(LPUART0, P0_2, MUX2);\nimpl_rx_pin!(LPUART0, P0_20, MUX3);\nimpl_rx_pin!(LPUART0, P2_0, MUX2);\n#[cfg(feature = \"mcxa5xx\")]\nimpl_rx_pin!(LPUART0, P4_8, MUX2);\n\n#[cfg(feature = \"swd-as-gpio\")]\nimpl_cts_pin!(LPUART0, P0_1, MUX2);\nimpl_cts_pin!(LPUART0, P0_23, MUX3);\nimpl_cts_pin!(LPUART0, P2_3, MUX2);\n#[cfg(feature = \"mcxa5xx\")]\nimpl_cts_pin!(LPUART0, P4_11, MUX2);\n\n#[cfg(feature = \"swd-as-gpio\")]\nimpl_rts_pin!(LPUART0, P0_0, MUX2);\nimpl_rts_pin!(LPUART0, P0_22, MUX3);\nimpl_rts_pin!(LPUART0, P2_2, MUX2);\n#[cfg(feature = \"mcxa5xx\")]\nimpl_rts_pin!(LPUART0, P4_10, MUX2);\n\n// LPUART 1\nimpl_tx_pin!(LPUART1, P1_9, MUX2);\nimpl_tx_pin!(LPUART1, P2_13, MUX3);\nimpl_tx_pin!(LPUART1, P3_9, MUX3);\nimpl_tx_pin!(LPUART1, P3_21, MUX3);\n\nimpl_rx_pin!(LPUART1, P1_8, MUX2);\nimpl_rx_pin!(LPUART1, P2_12, MUX3);\nimpl_rx_pin!(LPUART1, P3_8, MUX3);\nimpl_rx_pin!(LPUART1, P3_20, MUX3);\n\nimpl_cts_pin!(LPUART1, P1_11, MUX2);\nimpl_cts_pin!(LPUART1, P2_17, MUX3);\nimpl_cts_pin!(LPUART1, P3_11, MUX3);\nimpl_cts_pin!(LPUART1, P3_23, MUX3);\n\nimpl_rts_pin!(LPUART1, P1_10, MUX2);\nimpl_rts_pin!(LPUART1, P2_15, MUX3);\nimpl_rts_pin!(LPUART1, P2_16, MUX3);\nimpl_rts_pin!(LPUART1, P3_10, MUX3);\n#[cfg(feature = \"mcxa5xx\")]\nimpl_rts_pin!(LPUART1, P3_22, MUX3);\n\n// LPUART 2\nimpl_tx_pin!(LPUART2, P1_5, MUX3);\nimpl_tx_pin!(LPUART2, P1_13, MUX3);\nimpl_tx_pin!(LPUART2, P2_2, MUX3);\nimpl_tx_pin!(LPUART2, P2_10, MUX3);\nimpl_tx_pin!(LPUART2, P3_15, MUX2);\n\nimpl_rx_pin!(LPUART2, P1_4, MUX3);\nimpl_rx_pin!(LPUART2, P1_12, MUX3);\nimpl_rx_pin!(LPUART2, P2_3, MUX3);\nimpl_rx_pin!(LPUART2, P2_11, MUX3);\nimpl_rx_pin!(LPUART2, P3_14, MUX2);\n\nimpl_cts_pin!(LPUART2, P1_7, MUX3);\nimpl_cts_pin!(LPUART2, P1_15, MUX3);\nimpl_cts_pin!(LPUART2, P2_4, MUX3);\nimpl_cts_pin!(LPUART2, P3_13, MUX2);\n\nimpl_rts_pin!(LPUART2, P1_6, MUX3);\nimpl_rts_pin!(LPUART2, P1_14, MUX3);\nimpl_rts_pin!(LPUART2, P2_5, MUX3);\nimpl_rts_pin!(LPUART2, P3_12, MUX2);\n\n// LPUART 3\nimpl_tx_pin!(LPUART3, P3_1, MUX3);\nimpl_tx_pin!(LPUART3, P3_12, MUX3);\nimpl_tx_pin!(LPUART3, P4_5, MUX3);\n\nimpl_rx_pin!(LPUART3, P3_0, MUX3);\nimpl_rx_pin!(LPUART3, P3_13, MUX3);\nimpl_rx_pin!(LPUART3, P4_2, MUX3);\n\nimpl_cts_pin!(LPUART3, P3_7, MUX3);\nimpl_cts_pin!(LPUART3, P3_14, MUX3);\nimpl_cts_pin!(LPUART3, P4_6, MUX3);\n\nimpl_rts_pin!(LPUART3, P3_6, MUX3);\nimpl_rts_pin!(LPUART3, P3_15, MUX3);\nimpl_rts_pin!(LPUART3, P4_7, MUX3);\n\n// LPUART 4\nimpl_tx_pin!(LPUART4, P2_7, MUX3);\nimpl_tx_pin!(LPUART4, P3_19, MUX2);\nimpl_tx_pin!(LPUART4, P3_27, MUX3);\nimpl_tx_pin!(LPUART4, P4_3, MUX3);\n\nimpl_rx_pin!(LPUART4, P2_6, MUX3);\nimpl_rx_pin!(LPUART4, P3_18, MUX2);\n#[cfg(feature = \"mcxa2xx\")]\nimpl_rx_pin!(LPUART4, P3_28, MUX3);\nimpl_rx_pin!(LPUART4, P4_4, MUX3);\n\nimpl_cts_pin!(LPUART4, P2_0, MUX3);\nimpl_cts_pin!(LPUART4, P3_17, MUX2);\n#[cfg(feature = \"mcxa2xx\")]\nimpl_cts_pin!(LPUART4, P3_31, MUX3);\n\nimpl_rts_pin!(LPUART4, P2_1, MUX3);\nimpl_rts_pin!(LPUART4, P3_16, MUX2);\n#[cfg(feature = \"mcxa2xx\")]\nimpl_rts_pin!(LPUART4, P3_30, MUX3);\n\n// LPUART 5\n#[cfg(feature = \"mcxa5xx\")]\nimpl_tx_pin!(LPUART5, P0_25, MUX8);\n#[cfg(feature = \"mcxa5xx\")]\nimpl_tx_pin!(LPUART5, P1_10, MUX8);\n#[cfg(feature = \"mcxa5xx\")]\nimpl_tx_pin!(LPUART5, P1_17, MUX8);\n#[cfg(feature = \"mcxa5xx\")]\nimpl_tx_pin!(LPUART5, P3_10, MUX8);\n\n#[cfg(feature = \"mcxa5xx\")]\nimpl_rx_pin!(LPUART5, P0_24, MUX8);\n#[cfg(feature = \"mcxa5xx\")]\nimpl_rx_pin!(LPUART5, P1_11, MUX8);\n#[cfg(feature = \"mcxa5xx\")]\nimpl_rx_pin!(LPUART5, P1_16, MUX8);\n#[cfg(feature = \"mcxa5xx\")]\nimpl_rx_pin!(LPUART5, P3_11, MUX8);\n\n#[cfg(feature = \"mcxa5xx\")]\nimpl_cts_pin!(LPUART5, P0_27, MUX8);\n#[cfg(feature = \"mcxa5xx\")]\nimpl_cts_pin!(LPUART5, P1_12, MUX8);\n#[cfg(feature = \"mcxa5xx\")]\nimpl_cts_pin!(LPUART5, P1_19, MUX8);\n#[cfg(feature = \"mcxa5xx\")]\nimpl_cts_pin!(LPUART5, P3_8, MUX8);\n\n#[cfg(feature = \"mcxa5xx\")]\nimpl_rts_pin!(LPUART5, P0_26, MUX8);\n#[cfg(feature = \"mcxa5xx\")]\nimpl_rts_pin!(LPUART5, P1_13, MUX8);\n#[cfg(feature = \"mcxa5xx\")]\nimpl_rts_pin!(LPUART5, P1_18, MUX8);\n#[cfg(feature = \"mcxa5xx\")]\nimpl_rts_pin!(LPUART5, P3_9, MUX8);\n\n/// LPUART error types\n#[derive(Debug, Copy, Clone, Eq, PartialEq)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub enum Error {\n    /// Read error\n    Read,\n    /// Buffer overflow\n    Overrun,\n    /// Noise error\n    Noise,\n    /// Framing error\n    Framing,\n    /// Parity error\n    Parity,\n    /// Failure\n    Fail,\n    /// Invalid argument\n    InvalidArgument,\n    /// Lpuart baud rate cannot be supported with the given clock\n    UnsupportedBaudrate,\n    /// RX FIFO Empty\n    RxFifoEmpty,\n    /// TX FIFO Full\n    TxFifoFull,\n    /// TX Busy\n    TxBusy,\n    /// Clock Error\n    ClockSetup(ClockError),\n    /// Other internal errors or unexpected state.\n    Other,\n}\n\nimpl From<crate::dma::InvalidParameters> for Error {\n    fn from(_value: crate::dma::InvalidParameters) -> Self {\n        Error::Other\n    }\n}\n\nimpl From<maitake_sync::Closed> for Error {\n    fn from(_value: maitake_sync::Closed) -> Self {\n        Error::Other\n    }\n}\n\nimpl core::fmt::Display for Error {\n    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {\n        match self {\n            Error::Read => write!(f, \"Read error\"),\n            Error::Overrun => write!(f, \"Buffer overflow\"),\n            Error::Noise => write!(f, \"Noise error\"),\n            Error::Framing => write!(f, \"Framing error\"),\n            Error::Parity => write!(f, \"Parity error\"),\n            Error::Fail => write!(f, \"Failure\"),\n            Error::InvalidArgument => write!(f, \"Invalid argument\"),\n            Error::UnsupportedBaudrate => write!(f, \"Unsupported baud rate\"),\n            Error::RxFifoEmpty => write!(f, \"RX FIFO empty\"),\n            Error::TxFifoFull => write!(f, \"TX FIFO full\"),\n            Error::TxBusy => write!(f, \"TX busy\"),\n            Error::ClockSetup(e) => write!(f, \"Clock setup error: {:?}\", e),\n            Error::Other => write!(f, \"Other error\"),\n        }\n    }\n}\n\nimpl core::error::Error for Error {}\n\n/// Lpuart config\n#[derive(Debug, Clone, Copy)]\npub struct Config {\n    /// Power state required for this peripheral\n    pub power: PoweredClock,\n    /// Clock source\n    pub source: LpuartClockSel,\n    /// Clock divisor\n    pub div: Div4,\n    /// Baud rate in bits per second\n    pub baudrate_bps: u32,\n    /// Parity configuration\n    pub parity_mode: Option<Parity>,\n    /// Number of data bits\n    pub data_bits_count: DataBits,\n    /// MSB First or LSB First configuration\n    pub msb_first: MsbFirst,\n    /// Number of stop bits\n    pub stop_bits_count: StopBits,\n    /// TX FIFO watermark\n    pub tx_fifo_watermark: u8,\n    /// RX FIFO watermark\n    pub rx_fifo_watermark: u8,\n    /// TX CTS source\n    pub tx_cts_source: TxCtsSource,\n    /// TX CTS configure\n    pub tx_cts_config: TxCtsConfig,\n    /// RX IDLE type\n    pub rx_idle_type: IdleType,\n    /// RX IDLE configuration\n    pub rx_idle_config: IdleConfig,\n    /// Swap TXD and RXD pins\n    pub swap_txd_rxd: bool,\n}\n\nimpl Default for Config {\n    fn default() -> Self {\n        Self {\n            baudrate_bps: 115_200u32,\n            parity_mode: None,\n            data_bits_count: DataBits::DATA8,\n            msb_first: MsbFirst::LSB_FIRST,\n            stop_bits_count: StopBits::ONE,\n            tx_fifo_watermark: 0,\n            rx_fifo_watermark: 1,\n            tx_cts_source: TxCtsSource::CTS,\n            tx_cts_config: TxCtsConfig::START,\n            rx_idle_type: IdleType::FROM_START,\n            rx_idle_config: IdleConfig::IDLE_1,\n            swap_txd_rxd: false,\n            power: PoweredClock::NormalEnabledDeepSleepDisabled,\n            source: LpuartClockSel::FroLfDiv,\n            div: Div4::no_div(),\n        }\n    }\n}\n\n/// Driver mode.\n#[allow(private_bounds)]\npub trait Mode: sealed::Sealed {}\n\n/// Lpuart driver.\npub struct Lpuart<'a, M: Mode> {\n    tx: LpuartTx<'a, M>,\n    rx: LpuartRx<'a, M>,\n}\n\nstruct TxPins<'a> {\n    tx_pin: Peri<'a, AnyPin>,\n    cts_pin: Option<Peri<'a, AnyPin>>,\n}\n\nimpl<'a> TxPins<'a> {\n    fn take(self) -> (Peri<'a, AnyPin>, Option<Peri<'a, AnyPin>>) {\n        unsafe {\n            let tx_pin = self.tx_pin.clone_unchecked();\n            let cts_pin = self.cts_pin.as_ref().map(|p| p.clone_unchecked());\n            core::mem::forget(self);\n            (tx_pin, cts_pin)\n        }\n    }\n}\n\nstruct RxPins<'a> {\n    rx_pin: Peri<'a, AnyPin>,\n    rts_pin: Option<Peri<'a, AnyPin>>,\n}\n\nimpl<'a> RxPins<'a> {\n    fn take(self) -> (Peri<'a, AnyPin>, Option<Peri<'a, AnyPin>>) {\n        unsafe {\n            let rx_pin = self.rx_pin.clone_unchecked();\n            let rts_pin = self.rts_pin.as_ref().map(|p| p.clone_unchecked());\n            core::mem::forget(self);\n            (rx_pin, rts_pin)\n        }\n    }\n}\n\nimpl Drop for TxPins<'_> {\n    fn drop(&mut self) {\n        self.tx_pin.set_as_disabled();\n        if let Some(cts_pin) = &self.cts_pin {\n            cts_pin.set_as_disabled();\n        }\n    }\n}\n\nimpl Drop for RxPins<'_> {\n    fn drop(&mut self) {\n        self.rx_pin.set_as_disabled();\n        if let Some(rts_pin) = &self.rts_pin {\n            rts_pin.set_as_disabled();\n        }\n    }\n}\n\n/// Lpuart TX driver.\npub struct LpuartTx<'a, M: Mode> {\n    info: &'static Info,\n    state: &'static State,\n    mode: M,\n    _tx_pins: TxPins<'a>,\n    _wg: Option<WakeGuard>,\n    _phantom: PhantomData<&'a ()>,\n}\n\n/// Lpuart Rx driver.\npub struct LpuartRx<'a, M: Mode> {\n    info: &'static Info,\n    state: &'static State,\n    mode: M,\n    _rx_pins: RxPins<'a>,\n    _wg: Option<WakeGuard>,\n    _phantom: PhantomData<&'a ()>,\n}\n\nfn disable_peripheral(info: &'static Info) {\n    // Clear all status flags\n    clear_all_status_flags(info);\n\n    // Disable interrupts at the NVIC level\n    (info.int_disable)();\n\n    // Disable the module - clear all CTRL register bits\n    info.regs().ctrl().write(|w| w.0 = 0);\n\n    // Disable at the MRCC level\n    unsafe {\n        (info.mrcc_disable)();\n    }\n}\n\nimpl<M: Mode> Drop for LpuartTx<'_, M> {\n    fn drop(&mut self) {\n        // Wait for TX operations to complete. We cannot load more items\n        // into the fifo as we have exclusive access to the LpuartTx\n        wait_for_tx_complete(self.info);\n\n        // Disable transmit interrupts to prevent usage of the buffer space\n        cortex_m::interrupt::free(|_| {\n            self.info.regs().ctrl().modify(|w| w.set_tie(false));\n        });\n\n        // De-init the tx buffer, as once '_ ends we no longer can guarantee\n        // our usage of the buffer is sound.\n        unsafe {\n            self.state.tx_buf.deinit();\n        }\n\n        if self.state.tx_rx_refmask.set_inactive_fetch_last(TxRxRef::Tx) {\n            disable_peripheral(self.info);\n        }\n    }\n}\n\nimpl<M: Mode> Drop for LpuartRx<'_, M> {\n    fn drop(&mut self) {\n        // Disable receive interrupts to prevent future usage of the buffer space\n        cortex_m::interrupt::free(|_| {\n            self.info.regs().ctrl().modify(|w| {\n                w.set_rie(false); // RX interrupt\n                w.set_orie(false); // Overrun interrupt\n                w.set_peie(false); // Parity error interrupt\n                w.set_feie(false); // Framing error interrupt\n                w.set_neie(false); // Noise error interrupt\n            });\n        });\n\n        // De-init the rx buffer, as once '_ ends we no longer can guarantee\n        // our usage of the buffer is sound.\n        unsafe {\n            self.state.rx_buf.deinit();\n        }\n\n        if self.state.tx_rx_refmask.set_inactive_fetch_last(TxRxRef::Rx) {\n            disable_peripheral(self.info);\n        }\n    }\n}\n\nimpl<'a, M: Mode> Lpuart<'a, M> {\n    fn init<T: Instance>(\n        enable_tx: bool,\n        enable_rx: bool,\n        enable_tx_cts: bool,\n        enable_rx_rts: bool,\n        config: Config,\n    ) -> Result<Option<WakeGuard>, Error> {\n        // Check if the peripheral was leaked using [core::mem::forget], and clean up the peripheral.\n        if T::state().tx_rx_refmask.fetch_any_alive_reset() {\n            disable_peripheral(T::info());\n        }\n\n        // Enable clocks\n        let conf = LpuartConfig {\n            power: config.power,\n            source: config.source,\n            div: config.div,\n            instance: T::CLOCK_INSTANCE,\n        };\n        let parts = unsafe { enable_and_reset::<T>(&conf).map_err(Error::ClockSetup)? };\n\n        // Perform initialization sequence\n        perform_software_reset(T::info());\n        disable_transceiver(T::info());\n        configure_baudrate(T::info(), config.baudrate_bps, parts.freq)?;\n        configure_frame_format(T::info(), &config);\n        configure_control_settings(T::info(), &config);\n        configure_fifo(T::info(), &config);\n        clear_all_status_flags(T::info());\n        configure_flow_control(T::info(), enable_tx_cts, enable_rx_rts, &config);\n        configure_bit_order(T::info(), config.msb_first);\n        enable_transceiver(T::info(), enable_tx, enable_rx);\n\n        Ok(parts.wake_guard)\n    }\n\n    /// Split the Lpuart into a transmitter and receiver\n    pub fn split(self) -> (LpuartTx<'a, M>, LpuartRx<'a, M>) {\n        (self.tx, self.rx)\n    }\n\n    /// Split the Lpuart into a transmitter and receiver by mutable reference\n    pub fn split_ref(&mut self) -> (&mut LpuartTx<'a, M>, &mut LpuartRx<'a, M>) {\n        (&mut self.tx, &mut self.rx)\n    }\n}\n\nimpl<'a, M: Mode> LpuartTx<'a, M> {\n    fn new_inner<T: Instance>(\n        tx_pin: Peri<'a, AnyPin>,\n        cts_pin: Option<Peri<'a, AnyPin>>,\n        mode: M,\n        wg: Option<WakeGuard>,\n    ) -> Self {\n        T::state().tx_rx_refmask.set_active(TxRxRef::Tx);\n\n        Self {\n            info: T::info(),\n            state: T::state(),\n            mode,\n            _tx_pins: TxPins { tx_pin, cts_pin },\n            _wg: wg,\n            _phantom: PhantomData,\n        }\n    }\n}\n\nimpl<'a, M: Mode> LpuartRx<'a, M> {\n    fn new_inner<T: Instance>(\n        rx_pin: Peri<'a, AnyPin>,\n        rts_pin: Option<Peri<'a, AnyPin>>,\n        mode: M,\n        _wg: Option<WakeGuard>,\n    ) -> Self {\n        T::state().tx_rx_refmask.set_active(TxRxRef::Rx);\n\n        Self {\n            info: T::info(),\n            state: T::state(),\n            mode,\n            _rx_pins: RxPins { rx_pin, rts_pin },\n            _wg,\n            _phantom: PhantomData,\n        }\n    }\n}\n\nimpl embedded_hal_nb::serial::Error for Error {\n    fn kind(&self) -> embedded_hal_nb::serial::ErrorKind {\n        match *self {\n            Self::Framing => embedded_hal_nb::serial::ErrorKind::FrameFormat,\n            Self::Overrun => embedded_hal_nb::serial::ErrorKind::Overrun,\n            Self::Parity => embedded_hal_nb::serial::ErrorKind::Parity,\n            Self::Noise => embedded_hal_nb::serial::ErrorKind::Noise,\n            _ => embedded_hal_nb::serial::ErrorKind::Other,\n        }\n    }\n}\n\nimpl<M: Mode> embedded_hal_nb::serial::ErrorType for LpuartRx<'_, M> {\n    type Error = Error;\n}\n\nimpl<M: Mode> embedded_hal_nb::serial::ErrorType for LpuartTx<'_, M> {\n    type Error = Error;\n}\n\nimpl<M: Mode> embedded_hal_nb::serial::ErrorType for Lpuart<'_, M> {\n    type Error = Error;\n}\n\nimpl embedded_io::Error for Error {\n    fn kind(&self) -> embedded_io::ErrorKind {\n        embedded_io::ErrorKind::Other\n    }\n}\n\nimpl<M: Mode> embedded_io::ErrorType for LpuartRx<'_, M> {\n    type Error = Error;\n}\n\nimpl<M: Mode> embedded_io::ErrorType for LpuartTx<'_, M> {\n    type Error = Error;\n}\n\nimpl<M: Mode> embedded_io::ErrorType for Lpuart<'_, M> {\n    type Error = Error;\n}\n"
  },
  {
    "path": "embassy-mcxa/src/ostimer.rs",
    "content": "//! Time Driver.\nuse core::cell::{Cell, RefCell};\nuse core::sync::atomic::{AtomicBool, Ordering};\n\nuse critical_section::CriticalSection;\nuse embassy_sync::blocking_mutex::Mutex;\nuse embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;\nuse embassy_time_driver::Driver;\nuse embassy_time_queue_utils::Queue;\n\nuse crate::clocks::periph_helpers::{OsTimerConfig, OstimerClockSel};\nuse crate::clocks::{PoweredClock, enable_and_reset};\nuse crate::interrupt;\nuse crate::interrupt::InterruptExt;\nuse crate::pac::OSTIMER0;\nuse crate::peripherals::OSTIMER0;\n\nstruct AlarmState {\n    timestamp: Cell<u64>,\n}\n\nunsafe impl Send for AlarmState {}\n\nimpl AlarmState {\n    const fn new() -> Self {\n        Self {\n            timestamp: Cell::new(u64::MAX),\n        }\n    }\n}\n\n/// Convert gray to decimal\n///\n/// Os Event provides a 64-bit timestamp gray-encoded. All we have to\n/// do here is read both 32-bit halves of the register and convert\n/// from gray to regular binary.\nfn gray_to_dec(gray: u64) -> u64 {\n    let mut dec = gray;\n\n    dec ^= dec >> 1;\n    dec ^= dec >> 2;\n    dec ^= dec >> 4;\n    dec ^= dec >> 8;\n    dec ^= dec >> 16;\n    dec ^= dec >> 32;\n\n    dec\n}\n\n/// Convert decimal to gray\n///\n/// Before writing match value to the target register, we must convert\n/// it back into gray code.\nfn dec_to_gray(dec: u64) -> u64 {\n    let gray = dec;\n    gray ^ (gray >> 1)\n}\n\nembassy_time_driver::time_driver_impl!(static DRIVER: OsTimer = OsTimer {\n    alarms:  Mutex::const_new(CriticalSectionRawMutex::new(), AlarmState::new()),\n    queue: Mutex::new(RefCell::new(Queue::new())),\n});\n\nstruct OsTimer {\n    /// Timestamp at which to fire alarm. u64::MAX if no alarm is scheduled.\n    alarms: Mutex<CriticalSectionRawMutex, AlarmState>,\n    queue: Mutex<CriticalSectionRawMutex, RefCell<Queue>>,\n}\n\nimpl OsTimer {\n    fn init(&'static self, irq_prio: crate::interrupt::Priority) {\n        // init alarms\n        critical_section::with(|cs| {\n            let alarm = DRIVER.alarms.borrow(cs);\n            alarm.timestamp.set(u64::MAX);\n        });\n\n        let parts = unsafe {\n            enable_and_reset::<OSTIMER0>(&OsTimerConfig {\n                power: PoweredClock::AlwaysEnabled,\n                source: OstimerClockSel::Clk1M,\n            })\n            .expect(\"Enabling OsTimer clock should not fail\")\n        };\n\n        // Currently does nothing as Clk1M is always enabled anyway, this is here\n        // to make sure that doesn't change in a refactoring.\n        core::mem::forget(parts.wake_guard);\n\n        interrupt::OS_EVENT.disable();\n\n        // Make sure interrupt is masked\n        OSTIMER0.osevent_ctrl().modify(|w| w.set_ostimer_intena(false));\n\n        // Default to the end of time\n        OSTIMER0.match_l().write(|w| w.set_match_value(u32::MAX));\n        OSTIMER0.match_h().write(|w| w.set_match_value(u16::MAX));\n\n        interrupt::OS_EVENT.unpend();\n        interrupt::OS_EVENT.set_priority(irq_prio);\n        unsafe { interrupt::OS_EVENT.enable() };\n    }\n\n    /// Set an alarm for timestamp, returning false if the time has already expired.\n    fn set_alarm(&self, cs: CriticalSection, timestamp: u64) -> bool {\n        let alarm = self.alarms.borrow(cs);\n        alarm.timestamp.set(timestamp);\n\n        // Wait until we're allowed to write to MATCH_L/MATCH_H registers\n        while OSTIMER0.osevent_ctrl().read().match_wr_rdy() {}\n\n        let gray_timestamp = dec_to_gray(timestamp);\n\n        OSTIMER0.match_l().write(|w| w.set_match_value(gray_timestamp as u32));\n        OSTIMER0\n            .match_h()\n            .write(|w| w.set_match_value((gray_timestamp >> 32) as u16));\n\n        // Check if the timestamp has already expired, which could mean the just set match value would never match.\n        let t = self.now();\n        if timestamp <= t {\n            OSTIMER0.osevent_ctrl().modify(|w| w.set_ostimer_intena(false));\n            alarm.timestamp.set(u64::MAX);\n            return false;\n        }\n\n        // Enable interrupt. If the timestamp already matched, this would immediately pend the interrupt.\n        OSTIMER0.osevent_ctrl().modify(|w| w.set_ostimer_intena(true));\n        true\n    }\n\n    fn trigger_alarm(&self, cs: CriticalSection) {\n        let mut next = self.queue.borrow(cs).borrow_mut().next_expiration(self.now());\n        while !self.set_alarm(cs, next) {\n            next = self.queue.borrow(cs).borrow_mut().next_expiration(self.now());\n        }\n    }\n\n    fn on_interrupt(&self) {\n        crate::perf_counters::incr_interrupt_ostimer();\n        critical_section::with(|cs| {\n            if OSTIMER0.osevent_ctrl().read().ostimer_intrflag() {\n                OSTIMER0.osevent_ctrl().modify(|w| {\n                    w.set_ostimer_intena(false);\n                    w.set_ostimer_intrflag(true)\n                });\n                crate::perf_counters::incr_interrupt_ostimer_alarm();\n                self.trigger_alarm(cs);\n            }\n        });\n    }\n}\n\nstatic INIT: AtomicBool = AtomicBool::new(false);\n\nimpl Driver for OsTimer {\n    fn now(&self) -> u64 {\n        // Don't try to read the timer before the OsTimer is actually enabled.\n        // This leads to faults on the MCX-A.\n        if !INIT.load(Ordering::Relaxed) {\n            return 0;\n        }\n\n        let mut t = OSTIMER0.evtimerh().read().0 as u64;\n        t <<= 32;\n        t |= OSTIMER0.evtimerl().read().evtimer_count_value() as u64;\n        gray_to_dec(t)\n    }\n\n    fn schedule_wake(&self, at: u64, waker: &core::task::Waker) {\n        critical_section::with(|cs| {\n            let mut queue = self.queue.borrow(cs).borrow_mut();\n\n            if queue.schedule_wake(at, waker) {\n                let mut next = queue.next_expiration(self.now());\n                while !self.set_alarm(cs, next) {\n                    next = queue.next_expiration(self.now());\n                }\n            }\n        })\n    }\n}\n\n#[allow(non_snake_case)]\n#[interrupt]\nfn OS_EVENT() {\n    DRIVER.on_interrupt()\n}\n\npub(crate) fn init(irq_prio: crate::interrupt::Priority) {\n    DRIVER.init(irq_prio);\n    INIT.store(true, Ordering::Relaxed);\n}\n"
  },
  {
    "path": "embassy-mcxa/src/perf_counters.rs",
    "content": "//! # Performance counters\n//!\n//! This module contains simple performance counters, intended to aid debugging\n//! for metrics like \"number of interrupts served\" per peripheral, etc.\n//!\n//! When the `perf` feature is active, then the performance counters are functional.\n//! When the `perf` feature is NOT active, the \"increment\" and \"clear\" interfaces are\n//! still available, but act as a no-op.\n\n#[cfg_attr(not(feature = \"perf\"), allow(unused_imports))]\nuse core::sync::atomic::{AtomicU32, Ordering};\n\nuse paste::paste;\nmacro_rules! define_counters {\n    ($( $(#[$attr:meta])* $name:ident),*) => {\n        #[cfg_attr(not(feature = \"perf\"), allow(dead_code))]\n        static PERF_COUNTERS: Counters = Counters::new();\n\n        impl Counters {\n            const fn new() -> Self {\n                Self {\n                    $(\n                        $(#[$attr])*\n                        #[cfg(feature = \"perf\")]\n                        $name: AtomicU32::new(0),\n                    )*\n                }\n            }\n        }\n\n        paste! {\n            /// Reset all perf counters to zero\n            #[cfg(feature = \"perf\")]\n            pub fn clear_all() {\n                $(\n                    [<clear_ $name>]();\n                )*\n            }\n\n            /// Get a snapshot report of all perf counters\n            #[cfg(feature = \"perf\")]\n            pub fn get_report() -> Report {\n                Report {\n                    $(\n                        $name: ([<get_ $name>])(),\n                    )*\n                }\n            }\n\n            /// Get a snapshot report of all perf counters, and also reset all counters to zero\n            #[cfg(feature = \"perf\")]\n            pub fn get_report_and_clear() -> Report {\n                Report {\n                    $(\n                        $name: ([<get_and_clear_ $name>])(),\n                    )*\n                }\n            }\n\n\n            $(\n                /// Increment perf counter by 1\n                $(#[$attr])*\n                #[inline(always)]\n                pub fn [<incr_ $name>]() {\n                    #[cfg(feature = \"perf\")]\n                    PERF_COUNTERS.$name.fetch_add(1, Ordering::Relaxed);\n                }\n\n                /// Reset perf counter to zero\n                $(#[$attr])*\n                #[inline(always)]\n                pub fn [<clear_ $name>]() {\n                    #[cfg(feature = \"perf\")]\n                    PERF_COUNTERS.$name.store(0, Ordering::Relaxed);\n                }\n\n                /// Get current perf counter snapshot\n                ///\n                /// If the `perf` feature is not enabled, this always returns zero\n                $(#[$attr])*\n                #[inline(always)]\n                pub fn [<get_ $name>]() -> u32 {\n                    #[cfg(feature = \"perf\")]\n                    let ret = PERF_COUNTERS.$name.load(Ordering::Relaxed);\n                    #[cfg(not(feature = \"perf\"))]\n                    let ret = 0;\n                    ret\n                }\n\n                /// Get current perf counter snapshot and reset the perf counter to zero\n                ///\n                /// If the `perf` feature is not enabled, this always returns zero\n                $(#[$attr])*\n                #[inline(always)]\n                pub fn [<get_and_clear_ $name>]() -> u32 {\n                    #[cfg(feature = \"perf\")]\n                    let ret = PERF_COUNTERS.$name.swap(0, Ordering::Relaxed);\n                    #[cfg(not(feature = \"perf\"))]\n                    let ret = 0;\n                    ret\n                }\n            )*\n\n        }\n\n        /// A snapshot report of all perf counters\n        #[cfg(feature = \"perf\")]\n        #[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\n        pub struct Report {\n            $(\n                $(#[$attr])*\n                pub $name: u32,\n            )*\n        }\n\n        struct Counters {\n            $(\n                $(#[$attr])*\n                #[cfg(feature = \"perf\")]\n                $name: AtomicU32,\n            )*\n        }\n    };\n}\n\n// TODO: In the future, we may want to have more granular groupings of counters, behind\n// features like `perf-interrupt`, `perf-interrupt-wake`, `perf-sleep`, etc. In that case,\n// we might want a macro like the following, that enables a perf counter for ANY of the\n// given features:\n//\n// ```rust\n// define_counters! {\n//   ([\"perf-interrupt\", \"perf-adc\"], interrupt_adc0),\n// };\n//\n// We can implement this later if we decide that \"all of the perf counters\" takes up too\n// much static RAM space.\ndefine_counters!(\n    deep_sleeps,\n    interrupt_adc0,\n    interrupt_adc1,\n    interrupt_adc2,\n    interrupt_adc3,\n    interrupt_cdog0,\n    interrupt_ctimer0,\n    interrupt_ctimer0_wake,\n    interrupt_ctimer1,\n    interrupt_ctimer1_wake,\n    interrupt_ctimer2,\n    interrupt_ctimer2_wake,\n    interrupt_ctimer3,\n    interrupt_ctimer3_wake,\n    interrupt_ctimer4,\n    interrupt_ctimer4_wake,\n    interrupt_edma0,\n    interrupt_edma0_wake,\n    interrupt_gpio0,\n    interrupt_gpio0_wake,\n    interrupt_gpio1,\n    interrupt_gpio1_wake,\n    interrupt_gpio2,\n    interrupt_gpio2_wake,\n    interrupt_gpio3,\n    interrupt_gpio3_wake,\n    interrupt_gpio4,\n    interrupt_gpio4_wake,\n    interrupt_gpio5,\n    interrupt_gpio5_wake,\n    interrupt_i2c0,\n    interrupt_i2c0_wake,\n    interrupt_i2c1,\n    interrupt_i2c1_wake,\n    interrupt_i2c2,\n    interrupt_i2c2_wake,\n    interrupt_i2c3,\n    interrupt_i2c3_wake,\n    interrupt_i3c0,\n    interrupt_i3c0_wake,\n    #[cfg(feature = \"mcxa5xx\")]\n    interrupt_i3c1,\n    #[cfg(feature = \"mcxa5xx\")]\n    interrupt_i3c1_wake,\n    #[cfg(feature = \"mcxa5xx\")]\n    interrupt_i3c2,\n    #[cfg(feature = \"mcxa5xx\")]\n    interrupt_i3c2_wake,\n    #[cfg(feature = \"mcxa5xx\")]\n    interrupt_i3c3,\n    #[cfg(feature = \"mcxa5xx\")]\n    interrupt_i3c3_wake,\n    interrupt_lpuart0,\n    interrupt_lpuart0_wake,\n    interrupt_lpuart1,\n    interrupt_lpuart1_wake,\n    interrupt_lpuart2,\n    interrupt_lpuart2_wake,\n    interrupt_lpuart3,\n    interrupt_lpuart3_wake,\n    interrupt_lpuart4,\n    interrupt_lpuart4_wake,\n    interrupt_lpuart5,\n    interrupt_lpuart5_wake,\n    interrupt_ostimer,\n    interrupt_ostimer_alarm,\n    interrupt_rtc0,\n    interrupt_rtc0_wake,\n    interrupt_spi0,\n    interrupt_spi0_wake,\n    interrupt_spi1,\n    interrupt_spi1_wake,\n    #[cfg(feature = \"mcxa5xx\")]\n    interrupt_spi2,\n    #[cfg(feature = \"mcxa5xx\")]\n    interrupt_spi2_wake,\n    #[cfg(feature = \"mcxa5xx\")]\n    interrupt_spi3,\n    #[cfg(feature = \"mcxa5xx\")]\n    interrupt_spi3_wake,\n    #[cfg(feature = \"mcxa5xx\")]\n    interrupt_spi4,\n    #[cfg(feature = \"mcxa5xx\")]\n    interrupt_spi4_wake,\n    #[cfg(feature = \"mcxa5xx\")]\n    interrupt_spi5,\n    #[cfg(feature = \"mcxa5xx\")]\n    interrupt_spi5_wake,\n    interrupt_trng0,\n    interrupt_trng0_wake,\n    interrupt_wwdt,\n    wfe_sleeps\n);\n"
  },
  {
    "path": "embassy-mcxa/src/reset_reason.rs",
    "content": "//! Reset reason\n//!\n//! MCXA families keep the most recent reset reason in the SRS\n//! register of the CMC block. This lets users understand why the MCU\n//! has reset and take appropriate corrective actions if required.\n//!\n//! The reset reason bits are cached for the during of this boot,\n//! allowing the user to query the reset reason as many times as\n//! necessary.\n\nuse core::sync::atomic::{AtomicU32, Ordering};\n\nstatic RESET_REASON: AtomicU32 = AtomicU32::new(0);\n\n/// Reads the most recent reset reason from the Core Mode Controller\n/// (CMC).\npub fn reset_reason() -> ResetReasonRaw {\n    let regs = crate::pac::CMC;\n\n    let reason = critical_section::with(|_| {\n        let mut r = RESET_REASON.load(Ordering::Relaxed);\n\n        if r == 0 {\n            // Read status\n            r = regs.srs().read().0;\n\n            // Clear status\n            regs.ssrs().modify(|w| w.0 = r);\n\n            RESET_REASON.store(r, Ordering::Relaxed);\n        }\n\n        r\n    });\n\n    ResetReasonRaw(reason)\n}\n\n/// Raw reset reason bits. Can be queried or all reasons can be iterated over\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\n#[derive(Copy, Clone, Debug)]\npub struct ResetReasonRaw(u32);\n\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\n#[derive(Copy, Clone, Debug)]\npub struct ResetReasonRawIter(u32);\n\nimpl IntoIterator for ResetReasonRaw {\n    type Item = ResetReason;\n\n    type IntoIter = ResetReasonRawIter;\n\n    /// Convert to an iterator of contained reset reasons\n    fn into_iter(self) -> Self::IntoIter {\n        ResetReasonRawIter(self.0)\n    }\n}\n\nimpl ResetReasonRaw {\n    const MAP: &[(u32, ResetReason)] = &[\n        (1 << 0, ResetReason::WakeUp),\n        (1 << 1, ResetReason::Por),\n        (1 << 2, ResetReason::VoltageDetect),\n        (1 << 4, ResetReason::Warm),\n        (1 << 5, ResetReason::Fatal),\n        (1 << 8, ResetReason::Pin),\n        (1 << 9, ResetReason::Dap),\n        (1 << 10, ResetReason::ResetAckTimeout),\n        (1 << 11, ResetReason::LowPowerAckTimeout),\n        (1 << 12, ResetReason::SystemClockGeneration),\n        (1 << 13, ResetReason::Wwdt0),\n        (1 << 14, ResetReason::Software),\n        (1 << 15, ResetReason::Lockup),\n        #[cfg(feature = \"mcxa5xx\")]\n        (1 << 25, ResetReason::Wwdt1),\n        (1 << 26, ResetReason::Cdog0),\n        (1 << 27, ResetReason::Cdog1),\n        (1 << 28, ResetReason::Jtag),\n        #[cfg(feature = \"mcxa5xx\")]\n        (1 << 30, ResetReason::SecurityViolation),\n    ];\n\n    /// Wake up\n    #[inline]\n    pub fn is_wakeup(&self) -> bool {\n        (self.0 & (1 << 0)) != 0\n    }\n\n    /// Power-on Reset\n    #[inline]\n    pub fn is_por(&self) -> bool {\n        (self.0 & (1 << 1)) != 0\n    }\n\n    /// Voltage detect\n    #[inline]\n    pub fn is_voltage_detect(&self) -> bool {\n        (self.0 & (1 << 2)) != 0\n    }\n\n    /// Warm\n    #[inline]\n    pub fn is_warm(&self) -> bool {\n        (self.0 & (1 << 4)) != 0\n    }\n\n    /// Fatal\n    #[inline]\n    pub fn is_fatal(&self) -> bool {\n        (self.0 & (1 << 5)) != 0\n    }\n\n    /// Pin\n    #[inline]\n    pub fn is_pin(&self) -> bool {\n        (self.0 & (1 << 8)) != 0\n    }\n\n    /// DAP\n    #[inline]\n    pub fn is_dap(&self) -> bool {\n        (self.0 & (1 << 9)) != 0\n    }\n\n    /// Reset ack timeout\n    #[inline]\n    pub fn is_reset_ack_timeout(&self) -> bool {\n        (self.0 & (1 << 10)) != 0\n    }\n\n    /// Low power ack timeout\n    #[inline]\n    pub fn is_low_power_ack_timeout(&self) -> bool {\n        (self.0 & (1 << 11)) != 0\n    }\n\n    /// System clock generation\n    #[inline]\n    pub fn is_system_clock_generation(&self) -> bool {\n        (self.0 & (1 << 12)) != 0\n    }\n\n    /// Watchdog 0\n    #[inline]\n    pub fn is_watchdog0(&self) -> bool {\n        (self.0 & (1 << 13)) != 0\n    }\n\n    /// Software\n    pub fn is_software(&self) -> bool {\n        (self.0 & (1 << 14)) != 0\n    }\n\n    /// Lockup\n    pub fn is_lockup(&self) -> bool {\n        (self.0 & (1 << 15)) != 0\n    }\n\n    /// Watchdog 1\n    #[inline]\n    #[cfg(feature = \"mcxa5xx\")]\n    pub fn is_watchdog1(&self) -> bool {\n        (self.0 & (1 << 25)) != 0\n    }\n\n    /// Code watchdog 0\n    pub fn is_code_watchdog0(&self) -> bool {\n        (self.0 & (1 << 26)) != 0\n    }\n\n    /// Code watchdog 1\n    pub fn is_code_watchdog1(&self) -> bool {\n        (self.0 & (1 << 27)) != 0\n    }\n\n    /// JTAG\n    pub fn is_jtag(&self) -> bool {\n        (self.0 & (1 << 28)) != 0\n    }\n\n    /// Security Violation\n    #[cfg(feature = \"mcxa5xx\")]\n    pub fn is_security_violation(&self) -> bool {\n        (self.0 & (1 << 30)) != 0\n    }\n}\n\nimpl Iterator for ResetReasonRawIter {\n    type Item = ResetReason;\n\n    fn next(&mut self) -> Option<Self::Item> {\n        if self.0 == 0 {\n            return None;\n        }\n\n        for (mask, var) in ResetReasonRaw::MAP {\n            // If the bit is set...\n            if self.0 & mask != 0 {\n                // clear the bit\n                self.0 &= !mask;\n                // and return the answer\n                return Some(*var);\n            }\n        }\n\n        // Shouldn't happen, but oh well.\n        None\n    }\n}\n\n/// Indicates the type and source of the most recent reset.\n#[derive(Clone, Copy, Debug)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\n#[non_exhaustive]\npub enum ResetReason {\n    /// Tamper reset.\n    Tamper,\n\n    /// JTAG System Reset request.\n    Jtag,\n\n    /// Code Watchdog 0 reset.\n    Cdog0,\n\n    /// Code Watchdog 1 reset.\n    Cdog1,\n\n    /// Lockup reset.\n    Lockup,\n\n    /// Software reset.\n    Software,\n\n    /// Windowed Watchdog 0 reset.\n    Wwdt0,\n\n    /// Windowed Watchdog 1 reset.\n    #[cfg(feature = \"mcxa5xx\")]\n    Wwdt1,\n\n    /// Security Violation reset.\n    #[cfg(feature = \"mcxa5xx\")]\n    SecurityViolation,\n\n    /// System clock generation reset.\n    SystemClockGeneration,\n\n    /// Low Power Acknowledge Timeout reset.\n    LowPowerAckTimeout,\n\n    /// Reset Timeout.\n    ResetAckTimeout,\n\n    /// Debug Access Port reset.\n    Dap,\n\n    /// External assertion of RESET_b pin.\n    Pin,\n\n    /// Fatal reset.\n    Fatal,\n\n    /// Warm reset.\n    Warm,\n\n    /// Voltage detect reset.\n    VoltageDetect,\n\n    /// Power-on reset.\n    Por,\n\n    /// Wake-up reset.\n    WakeUp,\n}\n"
  },
  {
    "path": "embassy-mcxa/src/rtc/mcxa2xx.rs",
    "content": "//! RTC DateTime driver.\nuse core::convert::Infallible;\nuse core::marker::PhantomData;\n\nuse embassy_embedded_hal::SetConfig;\nuse embassy_hal_internal::{Peri, PeripheralType};\nuse maitake_sync::WaitCell;\n\nuse crate::clocks::{WakeGuard, with_clocks};\nuse crate::interrupt::typelevel::{Handler, Interrupt};\nuse crate::pac;\nuse crate::pac::rtc::vals::{Swr, Tcr, Um};\n\n/// RTC interrupt handler.\npub struct InterruptHandler<I: Instance> {\n    _phantom: PhantomData<I>,\n}\n\ntrait SealedInstance {\n    fn info() -> &'static Info;\n\n    const PERF_INT_INCR: fn();\n    const PERF_INT_WAKE_INCR: fn();\n}\n\n/// Trait for RTC peripheral instances\n#[allow(private_bounds)]\npub trait Instance: SealedInstance + PeripheralType + 'static + Send {\n    type Interrupt: Interrupt;\n}\n\nstruct Info {\n    regs: pac::rtc::Rtc,\n    wait_cell: WaitCell,\n}\n\nimpl Info {\n    #[inline(always)]\n    fn regs(&self) -> pac::rtc::Rtc {\n        self.regs\n    }\n\n    #[inline(always)]\n    fn wait_cell(&self) -> &WaitCell {\n        &self.wait_cell\n    }\n}\n\nunsafe impl Sync for Info {}\n\nimpl SealedInstance for crate::peripherals::RTC0 {\n    #[inline(always)]\n    fn info() -> &'static Info {\n        static INFO: Info = Info {\n            regs: pac::RTC0,\n            wait_cell: WaitCell::new(),\n        };\n        &INFO\n    }\n\n    const PERF_INT_INCR: fn() = crate::perf_counters::incr_interrupt_rtc0;\n    const PERF_INT_WAKE_INCR: fn() = crate::perf_counters::incr_interrupt_rtc0_wake;\n}\n\nimpl Instance for crate::peripherals::RTC0 {\n    type Interrupt = crate::interrupt::typelevel::RTC;\n}\n\n/// Number of days in a standard year\nconst DAYS_IN_A_YEAR: u32 = 365;\n/// Number of seconds in a day\nconst SECONDS_IN_A_DAY: u32 = 86400;\n/// Number of seconds in an hour\nconst SECONDS_IN_A_HOUR: u32 = 3600;\n/// Number of seconds in a minute\nconst SECONDS_IN_A_MINUTE: u32 = 60;\n/// Unix epoch start year\nconst YEAR_RANGE_START: u16 = 1970;\n\n/// Date and time structure for RTC operations\n#[derive(Debug, Clone, Copy)]\npub struct DateTime {\n    pub year: u16,\n    pub month: u8,\n    pub day: u8,\n    pub hour: u8,\n    pub minute: u8,\n    pub second: u8,\n}\n\n#[derive(Copy, Clone)]\npub struct Config {\n    #[allow(dead_code)]\n    wakeup_select: bool,\n    update_mode: Um,\n    #[allow(dead_code)]\n    supervisor_access: bool,\n    compensation_interval: u8,\n    compensation_time: Tcr,\n}\n\nimpl Default for Config {\n    fn default() -> Self {\n        Self {\n            wakeup_select: false,\n            update_mode: Um::UM_0,\n            supervisor_access: false,\n            compensation_interval: 0,\n            compensation_time: Tcr::TCR_0,\n        }\n    }\n}\n\n/// RTC interrupt enable flags\n#[derive(Copy, Clone)]\npub struct RtcInterruptEnable;\n\nimpl RtcInterruptEnable {\n    pub const RTC_TIME_INVALID_INTERRUPT_ENABLE: u32 = 1 << 0;\n    pub const RTC_TIME_OVERFLOW_INTERRUPT_ENABLE: u32 = 1 << 1;\n    pub const RTC_ALARM_INTERRUPT_ENABLE: u32 = 1 << 2;\n    pub const RTC_SECONDS_INTERRUPT_ENABLE: u32 = 1 << 4;\n}\n\n/// Converts a DateTime structure to Unix timestamp (seconds since 1970-01-01)\n///\n/// # Arguments\n///\n/// * `datetime` - The date and time to convert\n///\n/// # Returns\n///\n/// Unix timestamp as u32\n///\n/// # Note\n///\n/// This function handles leap years correctly.\npub fn convert_datetime_to_seconds(datetime: &DateTime) -> u32 {\n    let month_days: [u16; 13] = [0, 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334];\n\n    let mut seconds = (datetime.year as u32 - 1970) * DAYS_IN_A_YEAR;\n    seconds += (datetime.year as u32 / 4) - (1970 / 4);\n    seconds += month_days[datetime.month as usize] as u32;\n    seconds += datetime.day as u32 - 1;\n\n    if (datetime.year & 3 == 0) && (datetime.month <= 2) {\n        seconds -= 1;\n    }\n\n    seconds = seconds * SECONDS_IN_A_DAY\n        + (datetime.hour as u32 * SECONDS_IN_A_HOUR)\n        + (datetime.minute as u32 * SECONDS_IN_A_MINUTE)\n        + datetime.second as u32;\n\n    seconds\n}\n\n/// Converts Unix timestamp to DateTime structure\n///\n/// # Arguments\n///\n/// * `seconds` - Unix timestamp (seconds since 1970-01-01)\n///\n/// # Returns\n///\n/// DateTime structure with the converted date and time\n///\n/// # Note\n///\n/// This function handles leap years correctly.\npub fn convert_seconds_to_datetime(seconds: u32) -> DateTime {\n    let mut seconds_remaining = seconds;\n    let mut days = seconds_remaining / SECONDS_IN_A_DAY + 1;\n    seconds_remaining %= SECONDS_IN_A_DAY;\n\n    let hour = (seconds_remaining / SECONDS_IN_A_HOUR) as u8;\n    seconds_remaining %= SECONDS_IN_A_HOUR;\n    let minute = (seconds_remaining / SECONDS_IN_A_MINUTE) as u8;\n    let second = (seconds_remaining % SECONDS_IN_A_MINUTE) as u8;\n\n    let mut year = YEAR_RANGE_START;\n    let mut days_in_year = DAYS_IN_A_YEAR;\n\n    while days > days_in_year {\n        days -= days_in_year;\n        year += 1;\n\n        days_in_year = if year.is_multiple_of(4) {\n            DAYS_IN_A_YEAR + 1\n        } else {\n            DAYS_IN_A_YEAR\n        };\n    }\n\n    let days_per_month = [\n        31,\n        if (year.is_multiple_of(4) && !year.is_multiple_of(100)) || year.is_multiple_of(400) {\n            29\n        } else {\n            28\n        },\n        31,\n        30,\n        31,\n        30,\n        31,\n        31,\n        30,\n        31,\n        30,\n        31,\n    ];\n\n    let mut month = 1;\n    for (m, month_days) in days_per_month.iter().enumerate() {\n        let m = m + 1;\n        if days <= *month_days as u32 {\n            month = m;\n            break;\n        } else {\n            days -= *month_days as u32;\n        }\n    }\n\n    let day = days as u8;\n\n    DateTime {\n        year,\n        month: month as u8,\n        day,\n        hour,\n        minute,\n        second,\n    }\n}\n\n/// Minimal RTC handle for a specific instance I (store the zero-sized token like embassy)\npub struct Rtc<'a> {\n    _inst: core::marker::PhantomData<&'a mut ()>,\n    info: &'static Info,\n    _wg: Option<WakeGuard>,\n}\n\nimpl<'a> Rtc<'a> {\n    /// Create a new instance of the real time clock.\n    pub fn new<T: Instance>(\n        _inst: Peri<'a, T>,\n        _irq: impl crate::interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'a,\n        config: Config,\n    ) -> Self {\n        let info = T::info();\n\n        // The RTC is NOT gated by the MRCC, but we DO need to make sure the 16k clock\n        // on the vsys domain is active\n        let clocks = with_clocks(|c| c.clk_16k_vsys.clone());\n        let clk = match clocks {\n            None => panic!(\"Clocks have not been initialized\"),\n            Some(None) => panic!(\"Clocks initialized, but clk_16k_vsys not active\"),\n            Some(Some(clk)) => clk,\n        };\n\n        let mut inst = Self {\n            info,\n            _inst: PhantomData,\n            _wg: WakeGuard::for_power(&clk.power),\n        };\n\n        inst.set_configuration(&config);\n\n        // Enable RTC interrupt\n        T::Interrupt::unpend();\n        unsafe { T::Interrupt::enable() };\n\n        Self {\n            _inst: core::marker::PhantomData,\n            info,\n            _wg: WakeGuard::for_power(&clk.power),\n        }\n    }\n\n    fn set_configuration(&mut self, config: &Config) {\n        self.info.regs().cr().modify(|w| w.set_swr(Swr::SWR_1));\n        self.info.regs().cr().modify(|w| w.set_swr(Swr::SWR_0));\n        self.info.regs().tsr().write(|w| w.0 = 1);\n\n        self.info.regs().cr().modify(|w| w.set_um(config.update_mode));\n\n        self.info.regs().tcr().modify(|w| {\n            w.set_cir(config.compensation_interval);\n            w.set_tcr(config.compensation_time);\n        });\n    }\n\n    /// Set the current date and time\n    ///\n    /// # Arguments\n    ///\n    /// * `datetime` - The date and time to set\n    ///\n    /// # Note\n    ///\n    /// The datetime is converted to Unix timestamp and written to the time seconds register.\n    pub fn set_datetime(&self, datetime: DateTime) {\n        let seconds = convert_datetime_to_seconds(&datetime);\n        self.info.regs().tsr().write(|w| w.0 = seconds);\n    }\n\n    /// Get the current date and time\n    ///\n    /// # Returns\n    ///\n    /// Current date and time as DateTime\n    ///\n    /// # Note\n    ///\n    /// Reads the current Unix timestamp from the time seconds register and converts it.\n    pub fn get_datetime(&self) -> DateTime {\n        let seconds = self.info.regs().tsr().read().0;\n        convert_seconds_to_datetime(seconds)\n    }\n\n    /// Set the alarm date and time\n    ///\n    /// # Arguments\n    ///\n    /// * `alarm` - The date and time when the alarm should trigger\n    ///\n    /// # Note\n    ///\n    /// This function:\n    /// - Clears any existing alarm by writing 0 to the alarm register\n    /// - Waits for the clear operation to complete\n    /// - Sets the new alarm time\n    /// - Waits for the write operation to complete\n    /// - Uses timeouts to prevent infinite loops\n    /// - Enables the alarm interrupt after setting\n    pub fn set_alarm(&self, alarm: DateTime) {\n        let seconds = convert_datetime_to_seconds(&alarm);\n\n        self.info.regs().tar().write(|w| w.0 = 0);\n        let mut timeout = 10000;\n        while self.info.regs().tar().read().0 != 0 && timeout > 0 {\n            timeout -= 1;\n        }\n\n        self.info.regs().tar().write(|w| w.0 = seconds);\n\n        let mut timeout = 10000;\n        while self.info.regs().tar().read().0 != seconds && timeout > 0 {\n            timeout -= 1;\n        }\n\n        self.set_interrupt(RtcInterruptEnable::RTC_ALARM_INTERRUPT_ENABLE);\n    }\n\n    /// Get the current alarm date and time\n    ///\n    /// # Returns\n    ///\n    /// Alarm date and time as DateTime\n    ///\n    /// # Note\n    ///\n    /// Reads the alarm timestamp from the time alarm register and converts it.\n    pub fn get_alarm(&self) -> DateTime {\n        let alarm_seconds = self.info.regs().tar().read().0;\n        convert_seconds_to_datetime(alarm_seconds)\n    }\n\n    /// Start the RTC time counter\n    ///\n    /// # Note\n    ///\n    /// Sets the Time Counter Enable (TCE) bit in the status register.\n    pub fn start(&self) {\n        self.info.regs().sr().modify(|w| w.set_tce(true));\n    }\n\n    /// Stop the RTC time counter\n    ///\n    /// # Note\n    ///\n    /// Clears the Time Counter Enable (TCE) bit in the status register.\n    pub fn stop(&self) {\n        self.info.regs().sr().modify(|w| w.set_tce(false));\n    }\n\n    /// Enable specific RTC interrupts\n    ///\n    /// # Arguments\n    ///\n    /// * `mask` - Bitmask of interrupts to enable (use RtcInterruptEnable constants)\n    ///\n    /// # Note\n    ///\n    /// This function enables the specified interrupt types and resets the alarm occurred flag.\n    /// Available interrupts:\n    /// - Time Invalid Interrupt\n    /// - Time Overflow Interrupt\n    /// - Alarm Interrupt\n    /// - Seconds Interrupt\n    pub fn set_interrupt(&self, mask: u32) {\n        if (RtcInterruptEnable::RTC_TIME_INVALID_INTERRUPT_ENABLE & mask) != 0 {\n            self.info.regs().ier().modify(|w| w.set_tiie(true));\n        }\n        if (RtcInterruptEnable::RTC_TIME_OVERFLOW_INTERRUPT_ENABLE & mask) != 0 {\n            self.info.regs().ier().modify(|w| w.set_toie(true));\n        }\n        if (RtcInterruptEnable::RTC_ALARM_INTERRUPT_ENABLE & mask) != 0 {\n            self.info.regs().ier().modify(|w| w.set_taie(true));\n        }\n        if (RtcInterruptEnable::RTC_SECONDS_INTERRUPT_ENABLE & mask) != 0 {\n            self.info.regs().ier().modify(|w| w.set_tsie(true));\n        }\n    }\n\n    /// Disable specific RTC interrupts\n    ///\n    /// # Arguments\n    ///\n    /// * `mask` - Bitmask of interrupts to disable (use RtcInterruptEnable constants)\n    ///\n    /// # Note\n    ///\n    /// This function disables the specified interrupt types.\n    pub fn disable_interrupt(&self, mask: u32) {\n        if (RtcInterruptEnable::RTC_TIME_INVALID_INTERRUPT_ENABLE & mask) != 0 {\n            self.info.regs().ier().modify(|w| w.set_tiie(false));\n        }\n        if (RtcInterruptEnable::RTC_TIME_OVERFLOW_INTERRUPT_ENABLE & mask) != 0 {\n            self.info.regs().ier().modify(|w| w.set_toie(false));\n        }\n        if (RtcInterruptEnable::RTC_ALARM_INTERRUPT_ENABLE & mask) != 0 {\n            self.info.regs().ier().modify(|w| w.set_taie(false));\n        }\n        if (RtcInterruptEnable::RTC_SECONDS_INTERRUPT_ENABLE & mask) != 0 {\n            self.info.regs().ier().modify(|w| w.set_tsie(false));\n        }\n    }\n\n    /// Clear the alarm interrupt flag\n    ///\n    /// # Note\n    ///\n    /// This function clears the Time Alarm Interrupt Enable bit.\n    pub fn clear_alarm_flag(&self) {\n        self.info.regs().ier().modify(|w| w.set_taie(false));\n    }\n\n    /// Wait for an RTC alarm to trigger.\n    ///\n    /// # Arguments\n    ///\n    /// * `alarm` - The date and time when the alarm should trigger\n    ///\n    /// This function will wait until the RTC alarm is triggered.\n    /// If no alarm is scheduled, it will wait indefinitely until one is scheduled and triggered.\n    pub async fn wait_for_alarm(&mut self, alarm: DateTime) {\n        let wait = self.info.wait_cell().subscribe().await;\n\n        self.set_alarm(alarm);\n        self.start();\n\n        // REVISIT: propagate error?\n        let _ = wait.await;\n\n        // Clear the interrupt and disable the alarm after waking up\n        self.disable_interrupt(RtcInterruptEnable::RTC_ALARM_INTERRUPT_ENABLE);\n    }\n}\n\n/// RTC interrupt handler\n///\n/// This struct implements the interrupt handler for RTC events.\nimpl<T: Instance> Handler<T::Interrupt> for InterruptHandler<T> {\n    unsafe fn on_interrupt() {\n        T::PERF_INT_INCR();\n        let rtc = pac::RTC0;\n        // Check if this is actually a time alarm interrupt\n        let sr = rtc.sr().read();\n        if sr.taf() {\n            rtc.ier().modify(|w| w.set_taie(false));\n            T::PERF_INT_WAKE_INCR();\n            T::info().wait_cell().wake();\n        }\n    }\n}\n\nimpl<'a> SetConfig for Rtc<'a> {\n    type Config = Config;\n    type ConfigError = Infallible;\n\n    fn set_config(&mut self, config: &Self::Config) -> Result<(), Self::ConfigError> {\n        self.set_configuration(config);\n        Ok(())\n    }\n}\n"
  },
  {
    "path": "embassy-mcxa/src/rtc/mcxa5xx.rs",
    "content": "//! RTC DateTime driver.\nuse core::marker::PhantomData;\n\nuse embassy_embedded_hal::SetConfig;\nuse embassy_hal_internal::{Peri, PeripheralType};\nuse maitake_sync::WaitCell;\n\nuse crate::clocks::{WakeGuard, with_clocks};\nuse crate::interrupt::typelevel::{Handler, Interrupt};\nuse crate::pac;\nuse crate::pac::rtc::vals::Swr;\n\n/// RTC interrupt handler.\npub struct InterruptHandler<I: Instance> {\n    _phantom: PhantomData<I>,\n}\n\ntrait SealedInstance {\n    fn info() -> &'static Info;\n\n    const PERF_INT_INCR: fn();\n    const PERF_INT_WAKE_INCR: fn();\n}\n\n/// Trait for RTC peripheral instances\n#[allow(private_bounds)]\npub trait Instance: SealedInstance + PeripheralType + 'static + Send {\n    type Interrupt: Interrupt;\n}\n\nstruct Info {\n    regs: pac::rtc::Rtc,\n    wait_cell: WaitCell,\n}\n\nimpl Info {\n    #[inline(always)]\n    fn regs(&self) -> pac::rtc::Rtc {\n        self.regs\n    }\n\n    #[inline(always)]\n    fn wait_cell(&self) -> &WaitCell {\n        &self.wait_cell\n    }\n}\n\nunsafe impl Sync for Info {}\n\nimpl SealedInstance for crate::peripherals::RTC0 {\n    #[inline(always)]\n    fn info() -> &'static Info {\n        static INFO: Info = Info {\n            regs: pac::RTC0,\n            wait_cell: WaitCell::new(),\n        };\n        &INFO\n    }\n\n    const PERF_INT_INCR: fn() = crate::perf_counters::incr_interrupt_rtc0;\n    const PERF_INT_WAKE_INCR: fn() = crate::perf_counters::incr_interrupt_rtc0_wake;\n}\n\nimpl Instance for crate::peripherals::RTC0 {\n    type Interrupt = crate::interrupt::typelevel::RTC;\n}\n\n/// Month\n#[derive(Copy, Clone, Debug, PartialEq, PartialOrd)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\n#[repr(u8)]\npub enum Month {\n    /// January\n    January = 1,\n    /// February\n    February = 2,\n    /// March\n    March = 3,\n    /// April\n    April = 4,\n    /// May\n    May = 5,\n    /// June\n    June = 6,\n    /// July\n    July = 7,\n    /// August\n    August = 8,\n    /// September\n    September = 9,\n    /// October\n    October = 10,\n    /// November\n    November = 11,\n    /// December\n    December = 12,\n}\n\nimpl From<u8> for Month {\n    fn from(value: u8) -> Self {\n        match value {\n            1 => Self::January,\n            2 => Self::February,\n            3 => Self::March,\n            4 => Self::April,\n            5 => Self::May,\n            6 => Self::June,\n            7 => Self::July,\n            8 => Self::August,\n            9 => Self::September,\n            10 => Self::October,\n            11 => Self::November,\n            12 => Self::December,\n            _ => unreachable!(),\n        }\n    }\n}\n\nimpl From<Month> for u8 {\n    fn from(value: Month) -> Self {\n        value as u8\n    }\n}\n\n/// Day of the week\n#[derive(Copy, Clone, Debug, PartialEq, PartialOrd)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\n#[repr(u8)]\npub enum Weekday {\n    /// Sunday\n    Sunday = 0,\n    /// Monday\n    Monday = 1,\n    /// Tuesday\n    Tuesday = 2,\n    /// Wednesday\n    Wednesday = 3,\n    /// Thursday\n    Thursday = 4,\n    /// Friday\n    Friday = 5,\n    /// Saturday\n    Saturday = 6,\n}\n\nimpl From<u8> for Weekday {\n    fn from(value: u8) -> Self {\n        match value {\n            0 => Self::Sunday,\n            1 => Self::Monday,\n            2 => Self::Tuesday,\n            3 => Self::Wednesday,\n            4 => Self::Thursday,\n            5 => Self::Friday,\n            6 => Self::Saturday,\n            _ => unreachable!(),\n        }\n    }\n}\n\nimpl From<Weekday> for u8 {\n    fn from(value: Weekday) -> Self {\n        value as u8\n    }\n}\n\n/// Date and time structure for RTC operations\n#[derive(Debug, Clone, Copy)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub struct DateTime {\n    /// Year\n    pub year: i16,\n    /// Month\n    pub month: Month,\n    /// Day\n    pub day: u8,\n    /// Day of the week\n    pub dow: Weekday,\n    /// Hour\n    pub hour: u8,\n    /// Minute\n    pub minute: u8,\n    /// Second\n    pub second: u8,\n}\n\n#[derive(Copy, Clone, Default, PartialEq, PartialOrd)]\n#[repr(u8)]\npub enum Clkout {\n    /// No output clock\n    #[default]\n    None = 0,\n    /// Fine 1Hz clock with both precise edges\n    Fine = 1,\n    /// 32768Hz or 16384Hz output\n    Main = 2,\n    /// Coarse 1Hz clock with both precise edges\n    Coarse = 3,\n}\n\nimpl From<Clkout> for u8 {\n    fn from(value: Clkout) -> Self {\n        value as u8\n    }\n}\n\n#[derive(Copy, Clone, Default, PartialEq, PartialOrd)]\npub enum ClkSel {\n    /// 16384Hz\n    #[default]\n    Clk16384,\n    /// 32768Hz\n    Clk32768,\n}\n\nimpl From<ClkSel> for bool {\n    fn from(value: ClkSel) -> Self {\n        match value {\n            ClkSel::Clk16384 => false,\n            ClkSel::Clk32768 => true,\n        }\n    }\n}\n\n#[derive(Copy, Clone, Default, PartialEq, PartialOrd)]\npub enum EnableDaylightSavings {\n    /// No\n    #[default]\n    No,\n    /// Yes\n    Yes,\n}\n\nimpl From<EnableDaylightSavings> for bool {\n    fn from(value: EnableDaylightSavings) -> Self {\n        match value {\n            EnableDaylightSavings::No => false,\n            EnableDaylightSavings::Yes => true,\n        }\n    }\n}\n\n#[derive(Copy, Clone, Default, PartialEq, PartialOrd)]\npub enum Compensation {\n    /// No compensation\n    #[default]\n    None,\n    /// Coarse compensation\n    Coarse {\n        /// Duration in seconds over which the correction is applied.\n        interval: u8,\n        /// Correction value in terms of number of clock\n        /// cycles of the RTC oscillator clock.\n        correction: i8,\n    },\n    /// Fine compensation\n    Fine {\n        /// Integral correction value in terms of number of clock\n        /// cycles of the RTC oscillator clock.\n        integral: i8,\n        /// Fractional correction value in terms of number of clock\n        /// cycles of the fixed IRC clock\n        fractional: u8,\n    },\n}\n\n#[derive(Copy, Clone, Default, PartialEq, PartialOrd)]\n#[repr(u8)]\npub enum AlarmMatch {\n    /// Alarm matches second, minute, and hour.\n    #[default]\n    Hour = 0,\n    /// Alarm matches second, minute, hour, and day.\n    Day = 1,\n    /// Alarm matches second, minute, hour, day, and month.\n    Month = 2,\n    /// Alarm matches second, minute, hour, day, month, and year.\n    Year = 3,\n}\n\nimpl From<AlarmMatch> for u8 {\n    fn from(value: AlarmMatch) -> Self {\n        value as u8\n    }\n}\n\n#[derive(Copy, Clone, Default)]\npub struct Config {\n    /// Clkout selection\n    clkout: Clkout,\n    /// RTC Clock select\n    clksel: ClkSel,\n    /// Daylight savings select\n    daylight_savings: EnableDaylightSavings,\n    /// Alarm match. Selects which time and calendar counters are used\n    /// for matching and will generate and alarm.\n    alarm_match: AlarmMatch,\n    /// Compensation method\n    compensation: Compensation,\n}\n\n/// Errors exclusive to HW initialization\n#[derive(Debug, Copy, Clone, PartialEq, Eq)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\n#[non_exhaustive]\npub enum SetupError {\n    /// Clock configuration error.\n    ClockSetup,\n}\n\n/// Errors exclusive for datetime.\n#[derive(Debug, Copy, Clone, PartialEq, Eq)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\n#[non_exhaustive]\npub enum RtcError {\n    /// Invalid datetime\n    InvalidDateTime,\n    /// Invalid DST year\n    InvalidDstYear,\n    /// Other error\n    Other,\n}\n\n/// RTC driver.\npub struct Rtc<'a> {\n    _inst: core::marker::PhantomData<&'a mut ()>,\n    info: &'static Info,\n    _wg: Option<WakeGuard>,\n}\n\nimpl<'a> Rtc<'a> {\n    const BASE_YEAR: i16 = 2112;\n\n    /// Create a new instance of the real time clock.\n    pub fn new<T: Instance>(\n        _inst: Peri<'a, T>,\n        _irq: impl crate::interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'a,\n        config: Config,\n    ) -> Result<Self, SetupError> {\n        let info = T::info();\n\n        // The RTC is NOT gated by the MRCC, but we DO need to make\n        // sure either the 16k clock or the 32k clock is active.\n        let clocks = if config.clksel == ClkSel::Clk16384 {\n            with_clocks(|c| c.clk_16k_vsys.clone())\n        } else {\n            with_clocks(|c| c.clk_32k_vsys.clone())\n        };\n\n        let clk = clocks.flatten().ok_or(SetupError::ClockSetup)?;\n\n        let mut inst = Self {\n            info,\n            _inst: PhantomData,\n            _wg: WakeGuard::for_power(&clk.power),\n        };\n\n        inst.set_configuration(&config)?;\n\n        // Enable RTC interrupt\n        T::Interrupt::unpend();\n        unsafe { T::Interrupt::enable() };\n\n        Ok(inst)\n    }\n\n    fn set_configuration(&mut self, config: &Config) -> Result<(), SetupError> {\n        self.disable_write_protect();\n\n        self.info.regs().ctrl().modify(|w| w.set_swr(Swr::ASSERTED));\n        self.info.regs().ctrl().modify(|w| w.set_swr(Swr::CLEARED));\n\n        self.info.regs().ctrl().modify(|w| {\n            w.set_clkout(config.clkout.into());\n            w.set_clko_dis(config.clkout == Clkout::None);\n            w.set_clk_sel(config.clksel.into());\n            w.set_dst_en(config.daylight_savings.into());\n            w.set_alm_match(config.alarm_match.into());\n\n            match config.compensation {\n                Compensation::None => {\n                    w.set_comp_en(false);\n                    w.set_fineen(false);\n                }\n\n                Compensation::Coarse { .. } => {\n                    w.set_comp_en(true);\n                    w.set_fineen(false);\n                }\n\n                Compensation::Fine { .. } => {\n                    w.set_comp_en(false);\n                    w.set_fineen(true);\n                }\n            }\n        });\n\n        self.info.regs().compen().write(|w| match config.compensation {\n            Compensation::None => {}\n\n            Compensation::Coarse { interval, correction } => {\n                w.set_compen_val((interval as u16) << 8 | correction as u16);\n            }\n\n            Compensation::Fine { integral, fractional } => {\n                w.set_compen_val((integral as u16) << 8 | fractional as u16);\n            }\n        });\n\n        self.enable_write_protect();\n\n        Ok(())\n    }\n\n    fn disable_write_protect(&mut self) {\n        self.info.regs().status8().write_value(0b00);\n        self.info.regs().status8().write_value(0b01);\n        self.info.regs().status8().write_value(0b11);\n        self.info.regs().status8().write_value(0b10);\n    }\n\n    fn enable_write_protect(&mut self) {\n        self.info.regs().status8().write_value(0b10);\n    }\n\n    fn is_valid_datetime(&self, t: DateTime) -> Result<(), RtcError> {\n        if (t.year < (Self::BASE_YEAR - 128) || t.year > (Self::BASE_YEAR + 127))\n            || t.day > 31\n            || t.hour > 23\n            || t.minute > 59\n            || t.second > 59\n        {\n            Err(RtcError::InvalidDateTime)\n        } else {\n            Ok(())\n        }\n    }\n\n    fn is_valid_dst_year(&self, t: DateTime) -> Result<(), RtcError> {\n        let now = self.now()?;\n\n        if now.year != t.year {\n            Err(RtcError::InvalidDstYear)\n        } else {\n            Ok(())\n        }\n    }\n\n    /// Return the current datetime.\n    ///\n    /// Will block until we can access Datetime registers.\n    pub fn now(&self) -> Result<DateTime, RtcError> {\n        let ym = self.info.regs().yearmon().read();\n        let d = self.info.regs().days().read();\n        let hm = self.info.regs().hourmin().read();\n        let second = self.info.regs().seconds().read().sec_cnt();\n\n        let year = (i16::from(ym.yrofst() as i8) + Self::BASE_YEAR).into();\n        let month = ym.mon_cnt().into();\n        let dow = d.dow().into();\n        let day = d.day_cnt();\n        let hour = hm.hour_cnt();\n        let minute = hm.min_cnt();\n\n        Ok(DateTime {\n            year,\n            month,\n            day,\n            dow,\n            hour,\n            minute,\n            second,\n        })\n    }\n\n    /// Set the datetime to a new value.\n    ///\n    /// # Errors\n    ///\n    /// Will return `RtcError::InvalidDateTime` if the datetime is not a valid range.\n    pub fn set_datetime(&mut self, t: DateTime) -> Result<(), RtcError> {\n        self.is_valid_datetime(t)?;\n\n        let year = (t.year - Self::BASE_YEAR) as u8;\n        let month = t.month.into();\n        let dow = t.dow.into();\n        let day = t.day;\n        let hour = t.hour;\n        let minute = t.minute;\n        let second = t.second;\n\n        self.disable_write_protect();\n\n        self.info.regs().yearmon().write(|w| {\n            w.set_yrofst(year);\n            w.set_mon_cnt(month);\n        });\n\n        self.info.regs().days().write(|w| {\n            w.set_dow(dow);\n            w.set_day_cnt(day);\n        });\n\n        self.info.regs().hourmin().write(|w| {\n            w.set_hour_cnt(hour);\n            w.set_min_cnt(minute);\n        });\n\n        self.info.regs().seconds().write(|w| w.set_sec_cnt(second));\n\n        self.enable_write_protect();\n\n        Ok(())\n    }\n\n    /// Set the Daylight Savings start and end time.\n    ///\n    /// Note: only day, month, and hour are accounted for. The\n    /// underlying HW is incapable of enabling DST for a future year.\n    ///\n    /// # Errors\n    ///\n    /// `RtcError::InvalidDateTime` if the either datetime is not a valid range.\n    /// `RtcError::InvalidDstYear` if the either datetime contains a future or past year.\n    pub fn set_dst(&mut self, start: DateTime, end: DateTime) -> Result<(), RtcError> {\n        self.is_valid_datetime(start)?;\n        self.is_valid_datetime(end)?;\n        self.is_valid_dst_year(start)?;\n        self.is_valid_dst_year(end)?;\n\n        self.disable_write_protect();\n\n        self.info.regs().dst_hour().write(|w| {\n            w.set_dst_start_hour(start.hour - 1);\n            w.set_dst_end_hour(end.hour - 1);\n        });\n\n        self.info.regs().dst_month().write(|w| {\n            w.set_dst_start_month(start.month.into());\n            w.set_dst_end_month(end.month.into());\n        });\n\n        self.info.regs().dst_day().write(|w| {\n            w.set_dst_start_day(start.day);\n            w.set_dst_end_day(end.day);\n        });\n\n        self.enable_write_protect();\n\n        Ok(())\n    }\n\n    /// Set alarm to `t` and wait for the RTC alarm interrup to trigger.\n    ///\n    /// # Errors\n    ///\n    /// Will return `RtcError::InvalidDateTime` if the datetime is not a valid range.\n    pub async fn wait_for_alarm(&mut self, t: DateTime) -> Result<(), RtcError> {\n        self.is_valid_datetime(t)?;\n\n        let year = (t.year - Self::BASE_YEAR) as u8;\n        let month = t.month.into();\n        let day = t.day;\n        let hour = t.hour;\n        let minute = t.minute;\n        let second = t.second;\n\n        self.disable_write_protect();\n\n        self.info.regs().alm_yearmon().write(|w| {\n            w.set_alm_year(year);\n            w.set_alm_mon(month);\n        });\n\n        self.info.regs().alm_days().write(|w| {\n            w.set_alm_day(day);\n        });\n\n        self.info.regs().alm_hourmin().write(|w| {\n            w.set_alm_hour(hour);\n            w.set_alm_min(minute);\n        });\n\n        self.info.regs().alm_seconds().write(|w| w.set_alm_sec(second));\n\n        self.info\n            .wait_cell()\n            .wait_for(|| {\n                self.info.regs().ier().modify(|w| w.set_alm_ie(true));\n                self.info.regs().isr().read().alm_is()\n            })\n            .await\n            .map_err(|_| RtcError::Other)?;\n\n        self.info.regs().isr().write(|w| w.set_alm_is(true));\n\n        self.enable_write_protect();\n\n        Ok(())\n    }\n}\n\n/// RTC interrupt handler\n///\n/// This struct implements the interrupt handler for RTC events.\nimpl<T: Instance> Handler<T::Interrupt> for InterruptHandler<T> {\n    unsafe fn on_interrupt() {\n        T::PERF_INT_INCR();\n\n        // Check if this is actually a time alarm interrupt\n        let status = T::info().regs().isr().read();\n\n        if status.alm_is() {\n            T::info().regs().ier().modify(|w| w.set_alm_ie(false));\n            T::PERF_INT_WAKE_INCR();\n            T::info().wait_cell().wake();\n        }\n    }\n}\n\nimpl<'a> SetConfig for Rtc<'a> {\n    type Config = Config;\n    type ConfigError = SetupError;\n\n    fn set_config(&mut self, config: &Self::Config) -> Result<(), Self::ConfigError> {\n        self.set_configuration(config)\n    }\n}\n"
  },
  {
    "path": "embassy-mcxa/src/rtc/mod.rs",
    "content": "//! RTC DateTime driver.\n\n#[cfg(feature = \"mcxa2xx\")]\nmod mcxa2xx;\n\n#[cfg(feature = \"mcxa2xx\")]\npub use mcxa2xx::{Config, DateTime, InterruptHandler, Rtc};\n\n#[cfg(feature = \"mcxa5xx\")]\nmod mcxa5xx;\n\n#[cfg(feature = \"mcxa5xx\")]\npub use mcxa5xx::{Compensation, Config, DateTime, InterruptHandler, Month, Rtc, Weekday};\n"
  },
  {
    "path": "embassy-mcxa/src/spi/controller.rs",
    "content": "//! LPSPI Controller Driver.\n\nuse core::marker::PhantomData;\nuse core::sync::atomic::{Ordering, fence};\n\nuse embassy_embedded_hal::SetConfig;\nuse embassy_futures::join::join;\nuse embassy_hal_internal::Peri;\nuse embassy_hal_internal::drop::OnDrop;\npub use embedded_hal_1::spi::{MODE_0, MODE_1, MODE_2, MODE_3, Mode, Phase, Polarity};\nuse nxp_pac::lpspi::vals::{Cpha, Cpol, Lsbf, Master, Mbf, Outcfg, Pcspol, Pincfg, Prescale, Rrf, Rtf, Rxmsk, Txmsk};\n\nuse super::{Async, AsyncMode, Blocking, Dma, Info, Instance, MisoPin, Mode as IoMode, MosiPin, SckPin};\nuse crate::clocks::periph_helpers::{Div4, LpspiClockSel, LpspiConfig};\nuse crate::clocks::{ClockError, PoweredClock, WakeGuard, enable_and_reset};\nuse crate::dma::{Channel, DMA_MAX_TRANSFER_SIZE, DmaChannel, TransferOptions};\nuse crate::gpio::AnyPin;\nuse crate::interrupt;\nuse crate::interrupt::typelevel::Interrupt;\n\n// LPSPI has a 4-word FIFO.\nconst LPSPI_FIFO_SIZE: u8 = 4;\n\n/// Errors exclusive to HW initialization\n#[derive(Debug, Copy, Clone, PartialEq, Eq)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\n#[non_exhaustive]\npub enum SetupError {\n    /// Clock configuration error.\n    ClockSetup(ClockError),\n    /// Other internal errors or unexpected state.\n    Other,\n}\n\n/// I/O Errors\n#[derive(Debug, Copy, Clone, PartialEq, Eq)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\n#[non_exhaustive]\npub enum IoError {\n    /// Receive error.\n    ///\n    /// Indicates FIFO overflow condition has happened.\n    ReceiveError,\n    /// Transmit error.\n    ///\n    /// Indicated FIFO underrun condition has happened.\n    TransmitError,\n    /// Other internal errors or unexpected state.\n    Other,\n}\n\nimpl From<crate::dma::InvalidParameters> for IoError {\n    fn from(_value: crate::dma::InvalidParameters) -> Self {\n        IoError::Other\n    }\n}\n\n/// SPI interrupt handler.\npub struct InterruptHandler<T: Instance> {\n    _phantom: PhantomData<T>,\n}\n\nimpl<T: Instance> interrupt::typelevel::Handler<T::Interrupt> for InterruptHandler<T> {\n    unsafe fn on_interrupt() {\n        T::PERF_INT_INCR();\n        let r = T::info().regs().ier().read().0;\n        if r != 0 {\n            T::info().regs().ier().write(|w| w.0 = 0);\n            T::PERF_INT_WAKE_INCR();\n            T::info().wait_cell().wake();\n        }\n    }\n}\n\n/// SPI target clock configuration\n#[derive(Clone)]\n#[non_exhaustive]\npub struct ClockConfig {\n    /// Powered clock configuration\n    pub power: PoweredClock,\n    /// LPSPI clock source\n    pub source: LpspiClockSel,\n    /// LPSPI pre-divider\n    pub div: Div4,\n}\n\nimpl Default for ClockConfig {\n    fn default() -> Self {\n        Self {\n            power: PoweredClock::NormalEnabledDeepSleepDisabled,\n            source: LpspiClockSel::FroLfDiv,\n            div: const { Div4::no_div() },\n        }\n    }\n}\n\n/// SPI bit order\n#[derive(Default)]\npub enum BitOrder {\n    /// Most-significant bit first\n    #[default]\n    MsbFirst,\n    /// Least-significant bit first\n    LsbFirst,\n}\n\n/// SPI controller configuration\n#[non_exhaustive]\npub struct Config {\n    /// Frequency in Hertz.\n    pub frequency: u32,\n    /// SPI operating mode.\n    pub mode: Mode,\n    /// Bit order\n    pub bit_order: BitOrder,\n    /// Clock configuration\n    pub clock_config: ClockConfig,\n}\n\nimpl Default for Config {\n    fn default() -> Self {\n        Self {\n            frequency: 1_000_000,\n            mode: MODE_0,\n            bit_order: Default::default(),\n            clock_config: Default::default(),\n        }\n    }\n}\n\n/// Spi driver.\npub struct Spi<'d, M: IoMode> {\n    info: &'static Info,\n    _sck: Peri<'d, AnyPin>,\n    _miso: Option<Peri<'d, AnyPin>>,\n    _mosi: Option<Peri<'d, AnyPin>>,\n    _freq: u32,\n    mode: M,\n    _wg: Option<WakeGuard>,\n    _phantom: PhantomData<&'d M>,\n}\n\nimpl<'d, M: IoMode> Spi<'d, M> {\n    fn new_inner<T: Instance>(\n        _peri: Peri<'d, T>,\n        _sck: Peri<'d, AnyPin>,\n        _mosi: Option<Peri<'d, AnyPin>>,\n        _miso: Option<Peri<'d, AnyPin>>,\n        config: Config,\n        mode: M,\n    ) -> Result<Self, SetupError> {\n        let ClockConfig { power, source, div } = config.clock_config;\n\n        // Enable clocks\n        let conf = LpspiConfig {\n            power,\n            source,\n            div,\n            instance: T::CLOCK_INSTANCE,\n        };\n\n        let parts = unsafe { enable_and_reset::<T>(&conf).map_err(SetupError::ClockSetup)? };\n\n        let mut inst = Self {\n            info: T::info(),\n            _sck,\n            _mosi,\n            _miso,\n            mode,\n            _freq: parts.freq,\n            _wg: parts.wake_guard,\n            _phantom: PhantomData,\n        };\n\n        inst.set_configuration(&config)?;\n\n        Ok(inst)\n    }\n\n    fn set_configuration(&mut self, config: &Config) -> Result<(), SetupError> {\n        let (prescaler, div) = compute_baud_params(self._freq, config.frequency);\n\n        self.info.regs().cr().write(|w| {\n            w.set_men(false);\n            w.set_rst(true);\n            w.set_rtf(Rtf::TXFIFO_RST);\n            w.set_rrf(Rrf::RXFIFO_RST);\n        });\n\n        self.info.regs().cr().modify(|w| w.set_rst(false));\n\n        self.info.regs().cfgr1().write(|w| {\n            w.set_master(Master::MASTER_MODE);\n            w.set_pincfg(Pincfg::SIN_IN_SOUT_OUT);\n            w.set_pcspol(Pcspol::DISCARDED);\n        });\n\n        self.info.regs().ccr().write(|w| {\n            w.set_sckdiv(div);\n            w.set_dbt(div);\n            w.set_pcssck(div);\n            w.set_sckpcs(div);\n        });\n\n        self.info.regs().fcr().write(|w| {\n            w.set_txwater(0);\n            w.set_rxwater(0);\n        });\n\n        self.info.regs().tcr().write(|w| {\n            // Assuming byte transfers\n            w.set_framesz(7);\n\n            w.set_cpol(match config.mode.polarity {\n                Polarity::IdleLow => Cpol::INACTIVE_LOW,\n                Polarity::IdleHigh => Cpol::INACTIVE_HIGH,\n            });\n\n            w.set_cpha(match config.mode.phase {\n                Phase::CaptureOnFirstTransition => Cpha::CAPTURED,\n                Phase::CaptureOnSecondTransition => Cpha::CHANGED,\n            });\n\n            w.set_lsbf(match config.bit_order {\n                BitOrder::MsbFirst => Lsbf::MSB_FIRST,\n                BitOrder::LsbFirst => Lsbf::LSB_FIRST,\n            });\n\n            w.set_prescale(prescaler);\n        });\n\n        self.info.regs().cr().modify(|w| w.set_men(true));\n\n        Ok(())\n    }\n}\n\nimpl<'d, M: IoMode> Spi<'d, M> {\n    fn check_status(&mut self) -> Result<(), IoError> {\n        let status = self.info.regs().sr().read();\n\n        if status.ref_() {\n            // Empty the RX FIFO.\n            self.info.regs().cr().modify(|w| w.set_rrf(Rrf::RXFIFO_RST));\n            self.info.regs().sr().write(|w| w.set_ref_(true));\n            Err(IoError::ReceiveError)\n        } else if status.tef() {\n            self.info.regs().sr().write(|w| w.set_tef(true));\n            Err(IoError::TransmitError)\n        } else {\n            Ok(())\n        }\n    }\n\n    /// Read data from Spi blocking execution until done.\n    pub fn blocking_read(&mut self, data: &mut [u8]) -> Result<(), IoError> {\n        if data.is_empty() {\n            return Ok(());\n        }\n\n        self.info.regs().tcr().modify(|w| {\n            w.set_txmsk(Txmsk::MASK);\n            w.set_rxmsk(Rxmsk::NORMAL);\n        });\n\n        for word in data {\n            // Wait until we have data in the RxFIFO.\n            while self.info.regs().fsr().read().rxcount() == 0 {}\n            self.check_status()?;\n            *word = self.info.regs().rdr().read().data() as u8;\n        }\n\n        Ok(())\n    }\n\n    /// Write data to Spi blocking execution until done.\n    pub fn blocking_write(&mut self, data: &[u8]) -> Result<(), IoError> {\n        if data.is_empty() {\n            return Ok(());\n        }\n\n        self.info.regs().tcr().modify(|w| {\n            w.set_txmsk(Txmsk::NORMAL);\n            w.set_rxmsk(Rxmsk::MASK);\n        });\n\n        let fifo_size = LPSPI_FIFO_SIZE;\n\n        for word in data {\n            // Wait until we have at least one byte space in the TxFIFO.\n            while self.info.regs().fsr().read().txcount() - fifo_size == 0 {}\n            self.check_status()?;\n            self.info.regs().tdr().write(|w| w.set_data(*word as u32));\n        }\n\n        self.blocking_flush()\n    }\n\n    /// Transfer data to SPI blocking execution until done.\n    pub fn blocking_transfer(&mut self, read: &mut [u8], write: &[u8]) -> Result<(), IoError> {\n        if read.is_empty() && write.is_empty() {\n            return Ok(());\n        }\n\n        self.info.regs().tcr().modify(|w| {\n            w.set_txmsk(Txmsk::NORMAL);\n            w.set_rxmsk(Rxmsk::NORMAL);\n        });\n\n        let fifo_size = LPSPI_FIFO_SIZE;\n\n        for (wb, rb) in write.iter().zip(read.iter_mut()) {\n            // Wait until we have at least one byte space in the TxFIFO.\n            while self.info.regs().fsr().read().txcount() - fifo_size == 0 {}\n            self.check_status()?;\n            self.info.regs().tdr().write(|w| w.set_data(*wb as u32));\n\n            // Wait until we have data in the RxFIFO.\n            while self.info.regs().fsr().read().rxcount() == 0 {}\n            self.check_status()?;\n            *rb = self.info.regs().rdr().read().data() as u8;\n        }\n\n        self.blocking_flush()\n    }\n\n    /// Transfer data in place to SPI blocking execution until done.\n    pub fn blocking_transfer_in_place(&mut self, data: &mut [u8]) -> Result<(), IoError> {\n        if data.is_empty() {\n            return Ok(());\n        }\n\n        self.info.regs().tcr().modify(|w| {\n            w.set_txmsk(Txmsk::NORMAL);\n            w.set_rxmsk(Rxmsk::NORMAL);\n        });\n\n        let fifo_size = LPSPI_FIFO_SIZE;\n\n        for word in data {\n            // Wait until we have at least one byte space in the TxFIFO.\n            while self.info.regs().fsr().read().txcount() - fifo_size == 0 {}\n            self.check_status()?;\n            self.info.regs().tdr().write(|w| w.set_data(*word as u32));\n\n            // Wait until we have data in the RxFIFO.\n            while self.info.regs().fsr().read().rxcount() == 0 {}\n            self.check_status()?;\n            *word = self.info.regs().rdr().read().data() as u8;\n        }\n\n        self.blocking_flush()\n    }\n\n    /// Block execution until Spi is done.\n    pub fn blocking_flush(&mut self) -> Result<(), IoError> {\n        while self.info.regs().sr().read().mbf() == Mbf::BUSY {}\n        self.check_status()\n    }\n}\n\nimpl<'d> Spi<'d, Blocking> {\n    /// Create a SPI driver in blocking mode.\n    pub fn new_blocking<T: Instance>(\n        _peri: Peri<'d, T>,\n        sck: Peri<'d, impl SckPin<T> + 'd>,\n        mosi: Peri<'d, impl MosiPin<T> + 'd>,\n        miso: Peri<'d, impl MisoPin<T> + 'd>,\n        config: Config,\n    ) -> Result<Self, SetupError> {\n        sck.mux();\n        mosi.mux();\n        miso.mux();\n\n        let sck = sck.into();\n        let mosi = mosi.into();\n        let miso = miso.into();\n\n        Self::new_inner(_peri, sck, Some(mosi), Some(miso), config, Blocking)\n    }\n\n    /// Create a TX-only SPI driver in blocking mode.\n    pub fn new_blocking_txonly<T: Instance>(\n        _peri: Peri<'d, T>,\n        sck: Peri<'d, impl SckPin<T> + 'd>,\n        mosi: Peri<'d, impl MosiPin<T> + 'd>,\n        config: Config,\n    ) -> Result<Self, SetupError> {\n        sck.mux();\n        mosi.mux();\n\n        let sck = sck.into();\n        let mosi = mosi.into();\n\n        Self::new_inner(_peri, sck, Some(mosi), None, config, Blocking)\n    }\n\n    /// Create an RX-only SPI driver in blocking mode.\n    pub fn new_blocking_rxonly<T: Instance>(\n        _peri: Peri<'d, T>,\n        sck: Peri<'d, impl SckPin<T> + 'd>,\n        miso: Peri<'d, impl MisoPin<T> + 'd>,\n        config: Config,\n    ) -> Result<Self, SetupError> {\n        sck.mux();\n        miso.mux();\n\n        let sck = sck.into();\n        let miso = miso.into();\n\n        Self::new_inner(_peri, sck, None, Some(miso), config, Blocking)\n    }\n}\n\nimpl<'d> Spi<'d, Async> {\n    /// Create a SPI driver in async mode.\n    pub fn new_async<T: Instance>(\n        _peri: Peri<'d, T>,\n        sck: Peri<'d, impl SckPin<T> + 'd>,\n        mosi: Peri<'d, impl MosiPin<T> + 'd>,\n        miso: Peri<'d, impl MisoPin<T> + 'd>,\n        _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd,\n        config: Config,\n    ) -> Result<Self, SetupError> {\n        sck.mux();\n        mosi.mux();\n        miso.mux();\n\n        let sck = sck.into();\n        let mosi = mosi.into();\n        let miso = miso.into();\n\n        T::Interrupt::unpend();\n        unsafe { T::Interrupt::enable() };\n\n        Self::new_inner(_peri, sck, Some(mosi), Some(miso), config, Async)\n    }\n\n    /// Create a TX-only SPI driver in async mode.\n    pub fn new_async_txonly<T: Instance>(\n        _peri: Peri<'d, T>,\n        sck: Peri<'d, impl SckPin<T> + 'd>,\n        mosi: Peri<'d, impl MosiPin<T> + 'd>,\n        _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd,\n        config: Config,\n    ) -> Result<Self, SetupError> {\n        sck.mux();\n        mosi.mux();\n\n        let sck = sck.into();\n        let mosi = mosi.into();\n\n        T::Interrupt::unpend();\n        unsafe { T::Interrupt::enable() };\n\n        Self::new_inner(_peri, sck, Some(mosi), None, config, Async)\n    }\n\n    /// Create an RX-only SPI driver in async mode.\n    pub fn new_async_rxonly<T: Instance>(\n        _peri: Peri<'d, T>,\n        sck: Peri<'d, impl SckPin<T> + 'd>,\n        miso: Peri<'d, impl MisoPin<T> + 'd>,\n        _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd,\n        config: Config,\n    ) -> Result<Self, SetupError> {\n        sck.mux();\n        miso.mux();\n\n        let sck = sck.into();\n        let miso = miso.into();\n\n        T::Interrupt::unpend();\n        unsafe { T::Interrupt::enable() };\n\n        Self::new_inner(_peri, sck, None, Some(miso), config, Async)\n    }\n}\n\nimpl<'d> Spi<'d, Dma<'d>> {\n    /// Create a SPI driver in async mode.\n    pub fn new_async_with_dma<T: Instance>(\n        _peri: Peri<'d, T>,\n        sck: Peri<'d, impl SckPin<T> + 'd>,\n        mosi: Peri<'d, impl MosiPin<T> + 'd>,\n        miso: Peri<'d, impl MisoPin<T> + 'd>,\n        tx_dma: Peri<'d, impl Channel>,\n        rx_dma: Peri<'d, impl Channel>,\n        _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd,\n        config: Config,\n    ) -> Result<Self, SetupError> {\n        sck.mux();\n        mosi.mux();\n        miso.mux();\n\n        let sck = sck.into();\n        let mosi = mosi.into();\n        let miso = miso.into();\n\n        let tx_dma = DmaChannel::new(tx_dma);\n        let rx_dma = DmaChannel::new(rx_dma);\n\n        // enable this channel's interrupt\n        tx_dma.enable_interrupt();\n        rx_dma.enable_interrupt();\n\n        T::Interrupt::unpend();\n        unsafe { T::Interrupt::enable() };\n\n        Self::new_inner(\n            _peri,\n            sck,\n            Some(mosi),\n            Some(miso),\n            config,\n            Dma {\n                tx_dma,\n                rx_dma,\n                tx_request: T::TX_DMA_REQUEST,\n                rx_request: T::RX_DMA_REQUEST,\n            },\n        )\n    }\n\n    async fn read_dma_chunk(&mut self, data: &mut [u8]) -> Result<(), IoError> {\n        let rx_peri_addr = self.info.regs().rdr().as_ptr() as *mut u8;\n        let tx_peri_addr = self.info.regs().tdr().as_ptr() as *mut u8;\n\n        unsafe {\n            // Clean up channel state\n            self.mode.rx_dma.disable_request();\n            self.mode.rx_dma.clear_done();\n            self.mode.rx_dma.clear_interrupt();\n\n            self.mode.tx_dma.disable_request();\n            self.mode.tx_dma.clear_done();\n            self.mode.tx_dma.clear_interrupt();\n\n            // Set DMA request source from instance type\n            self.mode.rx_dma.set_request_source(self.mode.rx_request);\n            self.mode.tx_dma.set_request_source(self.mode.tx_request);\n\n            self.mode.tx_dma.setup_write_zeros_to_peripheral(\n                data.len(),\n                tx_peri_addr,\n                TransferOptions::NO_INTERRUPTS,\n            )?;\n\n            self.mode.rx_dma.setup_read_from_peripheral(\n                rx_peri_addr,\n                data,\n                false,\n                TransferOptions::COMPLETE_INTERRUPT,\n            )?;\n\n            // Enable SPI DMA request\n            self.info.regs().der().modify(|w| {\n                w.set_rdde(true);\n                w.set_tdde(true);\n            });\n\n            // Enable DMA channel request\n            self.mode.rx_dma.enable_request();\n            self.mode.tx_dma.enable_request();\n        }\n\n        // Wait for completion asynchronously\n        core::future::poll_fn(|cx| {\n            self.mode.rx_dma.waker().register(cx.waker());\n            if self.mode.rx_dma.is_done() {\n                core::task::Poll::Ready(())\n            } else {\n                core::task::Poll::Pending\n            }\n        })\n        .await;\n\n        // Cleanup\n        self.info.regs().der().modify(|w| {\n            w.set_rdde(false);\n            w.set_tdde(false);\n        });\n        unsafe {\n            self.mode.rx_dma.disable_request();\n            self.mode.rx_dma.clear_done();\n\n            self.mode.tx_dma.disable_request();\n            self.mode.tx_dma.clear_done();\n        }\n\n        // Ensure all writes by DMA are visible to the CPU\n        // TODO: ensure this is done internal to the DMA methods so individual drivers\n        // don't need to handle this?\n        fence(Ordering::Acquire);\n\n        Ok(())\n    }\n\n    async fn write_dma_chunk(&mut self, data: &[u8]) -> Result<(), IoError> {\n        let peri_addr = self.info.regs().tdr().as_ptr() as *mut u8;\n\n        unsafe {\n            // Clean up channel state\n            self.mode.tx_dma.disable_request();\n            self.mode.tx_dma.clear_done();\n            self.mode.tx_dma.clear_interrupt();\n\n            // Set DMA request source from instance type (type-safe)\n            self.mode.tx_dma.set_request_source(self.mode.tx_request);\n\n            // Configure TCD for memory-to-peripheral transfer\n            self.mode\n                .tx_dma\n                .setup_write_to_peripheral(data, peri_addr, false, TransferOptions::COMPLETE_INTERRUPT)?;\n\n            // Ensure all writes by CPU are visible to the DMA\n            // TODO: ensure this is done internal to the DMA methods so individual drivers\n            // don't need to handle this?\n            fence(Ordering::Release);\n\n            // Enable SPI TX DMA request\n            self.info.regs().der().modify(|w| w.set_tdde(true));\n\n            // Enable DMA channel request\n            self.mode.tx_dma.enable_request();\n        }\n\n        // Wait for completion asynchronously\n        core::future::poll_fn(|cx| {\n            self.mode.tx_dma.waker().register(cx.waker());\n            if self.mode.tx_dma.is_done() {\n                core::task::Poll::Ready(())\n            } else {\n                core::task::Poll::Pending\n            }\n        })\n        .await;\n\n        // Cleanup\n        self.info.regs().der().modify(|w| w.set_tdde(false));\n        unsafe {\n            self.mode.tx_dma.disable_request();\n            self.mode.tx_dma.clear_done();\n        }\n\n        Ok(())\n    }\n\n    async fn transfer_dma_chunk(&mut self, read: &mut [u8], write: &[u8]) -> Result<(), IoError> {\n        let rx_peri_addr = self.info.regs().rdr().as_ptr() as *mut u8;\n        let tx_peri_addr = self.info.regs().tdr().as_ptr() as *mut u8;\n\n        unsafe {\n            // Clean up channel state\n            self.mode.rx_dma.disable_request();\n            self.mode.rx_dma.clear_done();\n            self.mode.rx_dma.clear_interrupt();\n\n            self.mode.tx_dma.disable_request();\n            self.mode.tx_dma.clear_done();\n            self.mode.tx_dma.clear_interrupt();\n\n            // Set DMA request source from instance type\n            self.mode.rx_dma.set_request_source(self.mode.rx_request);\n            self.mode.tx_dma.set_request_source(self.mode.tx_request);\n\n            self.mode.tx_dma.setup_write_to_peripheral(\n                write,\n                tx_peri_addr,\n                false,\n                TransferOptions::COMPLETE_INTERRUPT,\n            )?;\n\n            self.mode.rx_dma.setup_read_from_peripheral(\n                rx_peri_addr,\n                read,\n                false,\n                TransferOptions::COMPLETE_INTERRUPT,\n            )?;\n\n            // Ensure all writes by CPU are visible to the DMA\n            // TODO: ensure this is done internal to the DMA methods so individual drivers\n            // don't need to handle this?\n            fence(Ordering::Release);\n\n            // Enable SPI DMA request\n            self.info.regs().der().modify(|w| {\n                w.set_rdde(true);\n                w.set_tdde(true);\n            });\n\n            // Enable DMA channel request\n            self.mode.rx_dma.enable_request();\n            self.mode.tx_dma.enable_request();\n        }\n\n        // Wait for completion asynchronously\n        let tx_transfer = async {\n            core::future::poll_fn(|cx| {\n                self.mode.tx_dma.waker().register(cx.waker());\n\n                if self.mode.tx_dma.is_done() {\n                    core::task::Poll::Ready(())\n                } else {\n                    core::task::Poll::Pending\n                }\n            })\n            .await;\n\n            if read.len() > write.len() {\n                let write_bytes_len = read.len() - write.len();\n\n                unsafe {\n                    self.mode.tx_dma.disable_request();\n                    self.mode.tx_dma.clear_done();\n                    self.mode.tx_dma.clear_interrupt();\n\n                    self.mode.tx_dma.setup_write_zeros_to_peripheral(\n                        write_bytes_len,\n                        tx_peri_addr,\n                        TransferOptions::COMPLETE_INTERRUPT,\n                    )?;\n\n                    self.mode.tx_dma.enable_request();\n                }\n\n                core::future::poll_fn(|cx| {\n                    self.mode.tx_dma.waker().register(cx.waker());\n\n                    if self.mode.tx_dma.is_done() {\n                        core::task::Poll::Ready(())\n                    } else {\n                        core::task::Poll::Pending\n                    }\n                })\n                .await\n            }\n\n            Ok::<(), IoError>(())\n        };\n\n        let rx_transfer = core::future::poll_fn(|cx| {\n            self.mode.rx_dma.waker().register(cx.waker());\n            if self.mode.rx_dma.is_done() {\n                core::task::Poll::Ready(())\n            } else {\n                core::task::Poll::Pending\n            }\n        });\n\n        let (tx_res, ()) = join(tx_transfer, rx_transfer).await;\n        tx_res?;\n\n        // Cleanup\n        self.info.regs().der().modify(|w| {\n            w.set_rdde(false);\n            w.set_tdde(false);\n        });\n        unsafe {\n            self.mode.rx_dma.disable_request();\n            self.mode.rx_dma.clear_done();\n\n            self.mode.tx_dma.disable_request();\n            self.mode.tx_dma.clear_done();\n        }\n\n        // if write > read we should clear any overflow of the FIFO SPI buffer\n        if write.len() > read.len() {\n            while self.info.regs().fsr().read().rxcount() == 0 {}\n            self.check_status()?;\n            let _ = self.info.regs().rdr().read().data() as u8;\n        }\n\n        // Ensure all writes by DMA are visible to the CPU\n        // TODO: ensure this is done internal to the DMA methods so individual drivers\n        // don't need to handle this?\n        fence(Ordering::Acquire);\n\n        Ok(())\n    }\n\n    async fn transfer_in_place_dma_chunk(&mut self, data: &mut [u8]) -> Result<(), IoError> {\n        let rx_peri_addr = self.info.regs().rdr().as_ptr() as *mut u8;\n        let tx_peri_addr = self.info.regs().tdr().as_ptr() as *mut u8;\n\n        unsafe {\n            // Clean up channel state\n            self.mode.rx_dma.disable_request();\n            self.mode.rx_dma.clear_done();\n            self.mode.rx_dma.clear_interrupt();\n\n            self.mode.tx_dma.disable_request();\n            self.mode.tx_dma.clear_done();\n            self.mode.tx_dma.clear_interrupt();\n\n            // Set DMA request source from instance type\n            self.mode.rx_dma.set_request_source(self.mode.rx_request);\n            self.mode.tx_dma.set_request_source(self.mode.tx_request);\n\n            self.mode.tx_dma.setup_write_to_peripheral(\n                data,\n                tx_peri_addr,\n                false,\n                TransferOptions::COMPLETE_INTERRUPT,\n            )?;\n            self.mode.rx_dma.setup_read_from_peripheral(\n                rx_peri_addr,\n                data,\n                false,\n                TransferOptions::COMPLETE_INTERRUPT,\n            )?;\n\n            // Ensure all writes by CPU are visible to the DMA\n            // TODO: ensure this is done internal to the DMA methods so individual drivers\n            // don't need to handle this?\n            fence(Ordering::Release);\n\n            // Enable SPI DMA request\n            self.info.regs().der().modify(|w| {\n                w.set_rdde(true);\n                w.set_tdde(true);\n            });\n\n            // Enable DMA channel request\n            self.mode.rx_dma.enable_request();\n            self.mode.tx_dma.enable_request();\n        }\n\n        // Wait for completion asynchronously\n        let tx_transfer = core::future::poll_fn(|cx| {\n            self.mode.tx_dma.waker().register(cx.waker());\n            if self.mode.tx_dma.is_done() {\n                core::task::Poll::Ready(())\n            } else {\n                core::task::Poll::Pending\n            }\n        });\n\n        let rx_transfer = core::future::poll_fn(|cx| {\n            self.mode.rx_dma.waker().register(cx.waker());\n            if self.mode.rx_dma.is_done() {\n                core::task::Poll::Ready(())\n            } else {\n                core::task::Poll::Pending\n            }\n        });\n\n        join(tx_transfer, rx_transfer).await;\n\n        // Cleanup\n        self.info.regs().der().modify(|w| {\n            w.set_rdde(false);\n            w.set_tdde(false);\n        });\n        unsafe {\n            self.mode.rx_dma.disable_request();\n            self.mode.rx_dma.clear_done();\n\n            self.mode.tx_dma.disable_request();\n            self.mode.tx_dma.clear_done();\n        }\n\n        // Ensure all writes by DMA are visible to the CPU\n        // TODO: ensure this is done internal to the DMA methods so individual drivers\n        // don't need to handle this?\n        fence(Ordering::Acquire);\n\n        Ok(())\n    }\n}\n\ntrait AsyncEngine {\n    async fn async_read_internal(&mut self, data: &mut [u8]) -> Result<(), IoError>;\n    async fn async_write_internal(&mut self, data: &[u8]) -> Result<(), IoError>;\n    async fn async_transfer_internal(&mut self, read: &mut [u8], write: &[u8]) -> Result<(), IoError>;\n    async fn async_transfer_in_place_internal(&mut self, data: &mut [u8]) -> Result<(), IoError>;\n}\n\n#[allow(private_bounds)]\nimpl<'d, M: AsyncMode> Spi<'d, M>\nwhere\n    Self: AsyncEngine,\n{\n    /// Read data from Spi async execution until done.\n    pub fn async_read(&mut self, data: &mut [u8]) -> impl Future<Output = Result<(), IoError>> {\n        <Self as AsyncEngine>::async_read_internal(self, data)\n    }\n\n    /// Write data to Spi async execution until done.\n    pub fn async_write(&mut self, data: &[u8]) -> impl Future<Output = Result<(), IoError>> {\n        <Self as AsyncEngine>::async_write_internal(self, data)\n    }\n\n    /// Transfer data to SPI async execution until done.\n    pub fn async_transfer(&mut self, read: &mut [u8], write: &[u8]) -> impl Future<Output = Result<(), IoError>> {\n        <Self as AsyncEngine>::async_transfer_internal(self, read, write)\n    }\n\n    /// Transfer data in place to SPI async execution until done.\n    pub fn async_transfer_in_place(&mut self, data: &mut [u8]) -> impl Future<Output = Result<(), IoError>> {\n        <Self as AsyncEngine>::async_transfer_in_place_internal(self, data)\n    }\n\n    /// Async flush.\n    pub async fn async_flush(&mut self) -> Result<(), IoError> {\n        self.info\n            .wait_cell()\n            .wait_for(|| {\n                self.info.regs().ier().write(|w| w.set_tcie(true));\n                self.info.regs().sr().read().tcf()\n            })\n            .await\n            .map_err(|_| IoError::Other)\n    }\n}\n\nimpl<'d> AsyncEngine for Spi<'d, Async> {\n    async fn async_read_internal(&mut self, data: &mut [u8]) -> Result<(), IoError> {\n        if data.is_empty() {\n            return Ok(());\n        }\n\n        self.info.regs().tcr().modify(|w| {\n            w.set_txmsk(Txmsk::MASK);\n            w.set_rxmsk(Rxmsk::NORMAL);\n        });\n\n        for word in data {\n            // Wait until we have data in the RxFIFO.\n            self.info\n                .wait_cell()\n                .wait_for(|| {\n                    self.info.regs().ier().modify(|w| {\n                        w.set_rdie(true);\n                        w.set_reie(true);\n                    });\n                    self.info.regs().fsr().read().rxcount() > 0 || self.info.regs().sr().read().ref_()\n                })\n                .await\n                .map_err(|_| IoError::Other)?;\n\n            self.check_status()?;\n\n            // dummy data\n            self.info.regs().tdr().write(|w| w.set_data(0));\n            *word = self.info.regs().rdr().read().data() as u8;\n        }\n\n        Ok(())\n    }\n\n    async fn async_write_internal(&mut self, data: &[u8]) -> Result<(), IoError> {\n        if data.is_empty() {\n            return Ok(());\n        }\n\n        self.info.regs().tcr().modify(|w| {\n            w.set_txmsk(Txmsk::NORMAL);\n            w.set_rxmsk(Rxmsk::MASK);\n        });\n\n        for word in data {\n            // Wait until we have at least one byte space in the TxFIFO.\n            self.info\n                .wait_cell()\n                .wait_for(|| {\n                    self.info.regs().ier().modify(|w| {\n                        w.set_tdie(true);\n                        w.set_teie(true);\n                    });\n                    self.info.regs().fsr().read().txcount() < LPSPI_FIFO_SIZE || self.info.regs().sr().read().tef()\n                })\n                .await\n                .map_err(|_| IoError::Other)?;\n\n            self.check_status()?;\n\n            self.info.regs().tdr().write(|w| w.set_data(*word as u32));\n        }\n\n        self.async_flush().await\n    }\n\n    async fn async_transfer_internal(&mut self, read: &mut [u8], write: &[u8]) -> Result<(), IoError> {\n        if read.is_empty() && write.is_empty() {\n            return Ok(());\n        }\n\n        self.info.regs().tcr().modify(|w| {\n            w.set_txmsk(Txmsk::NORMAL);\n            w.set_rxmsk(Rxmsk::NORMAL);\n        });\n\n        // Zip will terminate whenever the first of write or read are exhausted\n        for (wb, rb) in write.iter().zip(read.iter_mut()) {\n            // Wait until we have at least one byte space in the TxFIFO.\n            self.info\n                .wait_cell()\n                .wait_for(|| {\n                    self.info.regs().ier().modify(|w| {\n                        w.set_tdie(true);\n                        w.set_teie(true);\n                    });\n                    self.info.regs().fsr().read().txcount() < LPSPI_FIFO_SIZE || self.info.regs().sr().read().tef()\n                })\n                .await\n                .map_err(|_| IoError::Other)?;\n            self.check_status()?;\n            self.info.regs().tdr().write(|w| w.set_data(*wb as u32));\n\n            // Wait until we have data in the RxFIFO.\n            self.info\n                .wait_cell()\n                .wait_for(|| {\n                    self.info.regs().ier().modify(|w| {\n                        w.set_rdie(true);\n                        w.set_reie(true);\n                    });\n                    self.info.regs().fsr().read().rxcount() > 0 || self.info.regs().sr().read().ref_()\n                })\n                .await\n                .map_err(|_| IoError::Other)?;\n            self.check_status()?;\n            *rb = self.info.regs().rdr().read().data() as u8;\n        }\n\n        self.async_flush().await\n    }\n\n    async fn async_transfer_in_place_internal(&mut self, data: &mut [u8]) -> Result<(), IoError> {\n        if data.is_empty() {\n            return Ok(());\n        }\n\n        self.info.regs().tcr().modify(|w| {\n            w.set_txmsk(Txmsk::NORMAL);\n            w.set_rxmsk(Rxmsk::NORMAL);\n        });\n\n        for word in data {\n            // Wait until we have at least one byte space in the TxFIFO.\n            self.info\n                .wait_cell()\n                .wait_for(|| {\n                    self.info.regs().ier().modify(|w| w.set_tdie(true));\n                    self.info.regs().fsr().read().txcount() < LPSPI_FIFO_SIZE\n                })\n                .await\n                .map_err(|_| IoError::Other)?;\n            self.info.regs().tdr().write(|w| w.set_data(*word as u32));\n\n            // Wait until we have data in the RxFIFO.\n            self.info\n                .wait_cell()\n                .wait_for(|| {\n                    self.info.regs().ier().modify(|w| w.set_rdie(true));\n                    self.info.regs().fsr().read().rxcount() > 0\n                })\n                .await\n                .map_err(|_| IoError::Other)?;\n            *word = self.info.regs().rdr().read().data() as u8;\n        }\n\n        self.async_flush().await\n    }\n}\n\nimpl<'d> AsyncEngine for Spi<'d, Dma<'d>> {\n    async fn async_read_internal(&mut self, data: &mut [u8]) -> Result<(), IoError> {\n        if data.is_empty() {\n            return Ok(());\n        }\n\n        self.info.regs().tcr().modify(|w| {\n            w.set_txmsk(Txmsk::NORMAL);\n            w.set_rxmsk(Rxmsk::NORMAL);\n        });\n\n        self.info.regs().cfgr1().modify(|w| w.set_outcfg(Outcfg::TRISTATED));\n\n        let _on_drop = OnDrop::new(|| {\n            self.info.regs().der().modify(|w| w.set_rdde(false));\n            self.info.regs().cfgr1().modify(|w| w.set_outcfg(Outcfg::TRISTATED));\n        });\n\n        for chunk in data.chunks_mut(DMA_MAX_TRANSFER_SIZE) {\n            self.read_dma_chunk(chunk).await?;\n        }\n\n        Ok(())\n    }\n\n    async fn async_write_internal(&mut self, data: &[u8]) -> Result<(), IoError> {\n        if data.is_empty() {\n            return Ok(());\n        }\n\n        self.info.regs().tcr().modify(|w| {\n            w.set_txmsk(Txmsk::NORMAL);\n            w.set_rxmsk(Rxmsk::MASK);\n        });\n\n        let on_drop = OnDrop::new(|| {\n            self.info.regs().der().modify(|w| w.set_tdde(false));\n        });\n\n        for chunk in data.chunks(DMA_MAX_TRANSFER_SIZE) {\n            self.write_dma_chunk(chunk).await?;\n        }\n\n        on_drop.defuse();\n\n        self.async_flush().await\n    }\n\n    async fn async_transfer_internal(&mut self, read: &mut [u8], write: &[u8]) -> Result<(), IoError> {\n        if read.is_empty() && write.is_empty() {\n            return Ok(());\n        }\n\n        self.info.regs().tcr().modify(|w| {\n            w.set_txmsk(Txmsk::NORMAL);\n            w.set_rxmsk(Rxmsk::NORMAL);\n        });\n\n        let on_drop = OnDrop::new(|| {\n            self.info.regs().der().modify(|w| w.set_tdde(false));\n        });\n\n        for (read_chunk, write_chunk) in read\n            .chunks_mut(DMA_MAX_TRANSFER_SIZE)\n            .zip(write.chunks(DMA_MAX_TRANSFER_SIZE))\n        {\n            self.transfer_dma_chunk(read_chunk, write_chunk).await?;\n        }\n\n        on_drop.defuse();\n\n        self.async_flush().await\n    }\n\n    async fn async_transfer_in_place_internal(&mut self, data: &mut [u8]) -> Result<(), IoError> {\n        if data.is_empty() {\n            return Ok(());\n        }\n\n        self.info.regs().tcr().modify(|w| {\n            w.set_txmsk(Txmsk::NORMAL);\n            w.set_rxmsk(Rxmsk::NORMAL);\n        });\n\n        for chunk in data.chunks_mut(DMA_MAX_TRANSFER_SIZE) {\n            self.transfer_in_place_dma_chunk(chunk).await?;\n        }\n\n        self.async_flush().await\n    }\n}\n\n/// Compute prescaler and SCKDIV for the desired baud rate.\n/// Returns (prescaler, sckdiv) where:\n/// - prescaler is a Prescaler enum value\n/// - sckdiv is 0-255\n///\n/// Baud = src_hz / (prescaler.divisor() * (SCKDIV + 2))\npub(super) fn compute_baud_params(src_hz: u32, baud_hz: u32) -> (Prescale, u8) {\n    if baud_hz == 0 {\n        return (Prescale::DIVIDEBY1, 0);\n    }\n\n    let prescalers = [\n        Prescale::DIVIDEBY1,\n        Prescale::DIVIDEBY2,\n        Prescale::DIVIDEBY4,\n        Prescale::DIVIDEBY8,\n        Prescale::DIVIDEBY16,\n        Prescale::DIVIDEBY32,\n        Prescale::DIVIDEBY64,\n        Prescale::DIVIDEBY128,\n    ];\n\n    let (prescaler, div, _) = prescalers.iter().fold(\n        (Prescale::DIVIDEBY1, 0u8, u32::MAX),\n        |(best_pre, best_div, best_err), &prescaler| {\n            let divisor: u32 = 1 << (prescaler as u8);\n            let denom = divisor.saturating_mul(baud_hz);\n            let sckdiv_calc = (src_hz + denom / 2) / denom;\n            if sckdiv_calc < 2 {\n                return (best_pre, best_div, best_err);\n            }\n            let sckdiv = (sckdiv_calc - 2).min(255) as u8;\n            let actual_baud = src_hz / (divisor * (sckdiv as u32 + 2));\n            let error = actual_baud.abs_diff(baud_hz);\n            if error < best_err {\n                (prescaler, sckdiv, error)\n            } else {\n                (best_pre, best_div, best_err)\n            }\n        },\n    );\n\n    (prescaler, div)\n}\n\nimpl<'d, M: IoMode> embedded_hal_02::blocking::spi::Transfer<u8> for Spi<'d, M> {\n    type Error = IoError;\n\n    fn transfer<'w>(&mut self, words: &'w mut [u8]) -> Result<&'w [u8], Self::Error> {\n        self.blocking_transfer_in_place(words)?;\n        Ok(words)\n    }\n}\n\nimpl<'d, M: IoMode> embedded_hal_02::blocking::spi::Write<u8> for Spi<'d, M> {\n    type Error = IoError;\n\n    fn write(&mut self, words: &[u8]) -> Result<(), Self::Error> {\n        self.blocking_write(words)\n    }\n}\n\nimpl embedded_hal_1::spi::Error for IoError {\n    fn kind(&self) -> embedded_hal_1::spi::ErrorKind {\n        match *self {\n            IoError::Other => embedded_hal_1::spi::ErrorKind::Other,\n            IoError::ReceiveError => embedded_hal_1::spi::ErrorKind::Overrun,\n            IoError::TransmitError => embedded_hal_1::spi::ErrorKind::Other,\n        }\n    }\n}\n\nimpl<'d, M: IoMode> embedded_hal_1::spi::ErrorType for Spi<'d, M> {\n    type Error = IoError;\n}\n\nimpl<'d, M: IoMode> embedded_hal_1::spi::SpiBus<u8> for Spi<'d, M> {\n    fn flush(&mut self) -> Result<(), Self::Error> {\n        self.blocking_flush()\n    }\n\n    fn read(&mut self, words: &mut [u8]) -> Result<(), Self::Error> {\n        self.blocking_read(words)\n    }\n\n    fn write(&mut self, words: &[u8]) -> Result<(), Self::Error> {\n        self.blocking_write(words)\n    }\n\n    fn transfer(&mut self, read: &mut [u8], write: &[u8]) -> Result<(), Self::Error> {\n        self.blocking_transfer(read, write)\n    }\n\n    fn transfer_in_place(&mut self, words: &mut [u8]) -> Result<(), Self::Error> {\n        self.blocking_transfer_in_place(words)\n    }\n}\n\nimpl<'d, M: AsyncMode> embedded_hal_async::spi::SpiBus<u8> for Spi<'d, M>\nwhere\n    Spi<'d, M>: AsyncEngine,\n{\n    async fn flush(&mut self) -> Result<(), Self::Error> {\n        self.async_flush().await\n    }\n\n    async fn write(&mut self, words: &[u8]) -> Result<(), Self::Error> {\n        self.async_write(words).await\n    }\n\n    async fn read(&mut self, words: &mut [u8]) -> Result<(), Self::Error> {\n        self.async_read(words).await\n    }\n\n    async fn transfer(&mut self, read: &mut [u8], write: &[u8]) -> Result<(), Self::Error> {\n        self.async_transfer(read, write).await\n    }\n\n    async fn transfer_in_place(&mut self, words: &mut [u8]) -> Result<(), Self::Error> {\n        self.async_transfer_in_place(words).await\n    }\n}\n\nimpl<'d, M: IoMode> SetConfig for Spi<'d, M> {\n    type Config = Config;\n    type ConfigError = SetupError;\n\n    fn set_config(&mut self, config: &Self::Config) -> Result<(), Self::ConfigError> {\n        self.set_configuration(config)\n    }\n}\n"
  },
  {
    "path": "embassy-mcxa/src/spi/mod.rs",
    "content": "//! LPSPI driver\nuse embassy_hal_internal::PeripheralType;\nuse maitake_sync::WaitCell;\nuse paste::paste;\n\nuse crate::clocks::Gate;\nuse crate::clocks::periph_helpers::LpspiConfig;\nuse crate::dma::{DmaChannel, DmaRequest};\nuse crate::gpio::{GpioPin, SealedPin};\nuse crate::{interrupt, pac};\n\npub mod controller;\nmod sealed {\n    /// Seal a trait\n    pub trait Sealed {}\n}\n\ntrait SealedInstance: Gate<MrccPeriphConfig = LpspiConfig> {\n    fn info() -> &'static Info;\n\n    /// Clock instance\n    const CLOCK_INSTANCE: crate::clocks::periph_helpers::LpspiInstance;\n    const PERF_INT_INCR: fn();\n    const PERF_INT_WAKE_INCR: fn();\n    const TX_DMA_REQUEST: DmaRequest;\n    const RX_DMA_REQUEST: DmaRequest;\n}\n\n/// SPI Instance\n#[allow(private_bounds)]\npub trait Instance: SealedInstance + PeripheralType + 'static + Send {\n    /// Interrupt for this SPI instance.\n    type Interrupt: interrupt::typelevel::Interrupt;\n}\n\nstruct Info {\n    regs: pac::lpspi::Lpspi,\n    wait_cell: WaitCell,\n}\n\nimpl Info {\n    #[inline(always)]\n    fn regs(&self) -> pac::lpspi::Lpspi {\n        self.regs\n    }\n\n    #[inline(always)]\n    fn wait_cell(&self) -> &WaitCell {\n        &self.wait_cell\n    }\n}\n\nunsafe impl Sync for Info {}\n\nmacro_rules! impl_instance {\n    ($($n:expr),*) => {\n        $(\n            paste!{\n                impl SealedInstance for crate::peripherals::[<LPSPI $n>] {\n                    fn info() -> &'static Info {\n                        static INFO: Info = Info {\n                            regs: pac::[<LPSPI $n>],\n                            wait_cell: WaitCell::new(),\n                        };\n                        &INFO\n                    }\n\n                    const CLOCK_INSTANCE: crate::clocks::periph_helpers::LpspiInstance\n                        = crate::clocks::periph_helpers::LpspiInstance::[<Lpspi $n>];\n                    const PERF_INT_INCR: fn() = crate::perf_counters::[<incr_interrupt_spi $n>];\n                    const PERF_INT_WAKE_INCR: fn() = crate::perf_counters::[<incr_interrupt_spi $n _wake>];\n                    const TX_DMA_REQUEST: DmaRequest = DmaRequest::[<LPSPI $n Tx>];\n                    const RX_DMA_REQUEST: DmaRequest = DmaRequest::[<LPSPI $n Rx>];\n                }\n\n                impl Instance for crate::peripherals::[<LPSPI $n>] {\n                    type Interrupt = crate::interrupt::typelevel::[<LPSPI $n>];\n                }\n            }\n        )*\n    };\n}\n\nimpl_instance!(0, 1);\n\n#[cfg(feature = \"mcxa5xx\")]\nimpl_instance!(2, 3, 4, 5);\n\n/// MOSI pin trait.\npub trait MosiPin<Instance>: GpioPin + sealed::Sealed + PeripheralType {\n    fn mux(&self);\n}\n\n/// MISO pin trait.\npub trait MisoPin<Instance>: GpioPin + sealed::Sealed + PeripheralType {\n    fn mux(&self);\n}\n\n/// SCK pin trait.\npub trait SckPin<Instance>: GpioPin + sealed::Sealed + PeripheralType {\n    fn mux(&self);\n}\n\n/// Driver mode.\n#[allow(private_bounds)]\npub trait Mode: sealed::Sealed {}\n\n/// Async driver mode.\n#[allow(private_bounds)]\npub trait AsyncMode: sealed::Sealed + Mode {}\n\n/// Blocking mode.\npub struct Blocking;\nimpl sealed::Sealed for Blocking {}\nimpl Mode for Blocking {}\n\n/// Async mode.\npub struct Async;\nimpl sealed::Sealed for Async {}\nimpl Mode for Async {}\nimpl AsyncMode for Async {}\n\n/// DMA mode.\npub struct Dma<'d> {\n    tx_dma: DmaChannel<'d>,\n    rx_dma: DmaChannel<'d>,\n    rx_request: DmaRequest,\n    tx_request: DmaRequest,\n}\nimpl sealed::Sealed for Dma<'_> {}\nimpl Mode for Dma<'_> {}\nimpl AsyncMode for Dma<'_> {}\n\nmacro_rules! impl_pin {\n    ($pin:ident, $peri:ident, $fn:ident, $trait:ident) => {\n        impl sealed::Sealed for crate::peripherals::$pin {}\n\n        impl $trait<crate::peripherals::$peri> for crate::peripherals::$pin {\n            fn mux(&self) {\n                self.set_pull(crate::gpio::Pull::Disabled);\n                self.set_slew_rate(crate::gpio::SlewRate::Fast.into());\n                self.set_drive_strength(crate::gpio::DriveStrength::Double.into());\n                self.set_function(crate::pac::port::vals::Mux::$fn);\n                self.set_enable_input_buffer(true);\n            }\n        }\n    };\n}\n\n#[cfg(feature = \"swd-as-gpio\")]\nimpl_pin!(P0_1, LPSPI0, MUX3, MisoPin);\n#[cfg(feature = \"swd-swo-as-gpio\")]\nimpl_pin!(P0_2, LPSPI0, MUX3, SckPin);\n#[cfg(feature = \"jtag-extras-as-gpio\")]\nimpl_pin!(P0_3, LPSPI0, MUX3, MosiPin);\n\nimpl_pin!(P1_0, LPSPI0, MUX2, MosiPin);\nimpl_pin!(P1_1, LPSPI0, MUX2, SckPin);\nimpl_pin!(P1_2, LPSPI0, MUX2, MisoPin);\n\nimpl_pin!(P2_12, LPSPI1, MUX2, SckPin);\nimpl_pin!(P2_13, LPSPI1, MUX2, MosiPin);\nimpl_pin!(P2_15, LPSPI1, MUX2, MisoPin);\nimpl_pin!(P2_16, LPSPI1, MUX2, MisoPin);\n\nimpl_pin!(P3_8, LPSPI1, MUX2, MosiPin);\nimpl_pin!(P3_9, LPSPI1, MUX2, MisoPin);\nimpl_pin!(P3_10, LPSPI1, MUX2, SckPin);\n\n#[cfg(feature = \"mcxa5xx\")]\nmod mcxa5xx {\n    use super::*;\n\n    impl_pin!(P0_20, LPSPI4, MUX8, MisoPin);\n    impl_pin!(P0_21, LPSPI4, MUX8, SckPin);\n    impl_pin!(P0_22, LPSPI4, MUX8, MosiPin);\n\n    impl_pin!(P1_12, LPSPI5, MUX5, MisoPin);\n    impl_pin!(P1_13, LPSPI5, MUX5, SckPin);\n    impl_pin!(P1_14, LPSPI5, MUX8, MosiPin);\n\n    impl_pin!(P2_0, LPSPI2, MUX8, MisoPin);\n    impl_pin!(P2_1, LPSPI2, MUX8, SckPin);\n    impl_pin!(P2_2, LPSPI2, MUX8, MosiPin);\n\n    impl_pin!(P3_4, LPSPI4, MUX3, MosiPin);\n    impl_pin!(P3_3, LPSPI4, MUX3, SckPin);\n    impl_pin!(P3_2, LPSPI4, MUX3, MisoPin);\n\n    impl_pin!(P4_3, LPSPI2, MUX8, SckPin);\n    impl_pin!(P4_4, LPSPI2, MUX8, MisoPin);\n    impl_pin!(P4_5, LPSPI2, MUX8, MosiPin);\n    impl_pin!(P4_8, LPSPI5, MUX8, MosiPin);\n    impl_pin!(P4_9, LPSPI5, MUX8, MisoPin);\n    impl_pin!(P4_10, LPSPI5, MUX8, SckPin);\n}\n"
  },
  {
    "path": "embassy-mcxa/src/trng.rs",
    "content": "//! True Random Number Generator\n\nuse core::marker::PhantomData;\nuse core::sync::atomic::{AtomicU32, Ordering};\n\nuse embassy_hal_internal::{Peri, PeripheralType};\nuse maitake_sync::WaitCell;\nuse paste::paste;\n\nuse crate::clocks::periph_helpers::NoConfig;\nuse crate::clocks::{Gate, enable_and_reset};\nuse crate::interrupt::typelevel;\nuse crate::interrupt::typelevel::{Handler, Interrupt};\nuse crate::pac;\nuse crate::pac::trng::regs::IntStatus;\nuse crate::pac::trng::vals::{IntStatusEntVal, TrngEntCtl};\n\nconst BLOCK_SIZE: usize = 8;\n\n#[allow(private_bounds)]\npub trait Mode: sealed::SealedMode {}\n\nmod sealed {\n    pub trait SealedMode {}\n}\n\n/// Blocking driver mode.\npub struct Blocking;\nimpl sealed::SealedMode for Blocking {}\nimpl Mode for Blocking {}\n\n/// Async driver mode.\npub struct Async;\nimpl sealed::SealedMode for Async {}\nimpl Mode for Async {}\n\n/// TRNG Driver\npub struct Trng<'d, M: Mode> {\n    info: &'static Info,\n    _phantom: PhantomData<&'d mut M>,\n}\n\nimpl<'d, M: Mode> Trng<'d, M> {\n    fn new_inner<T: Instance>(_peri: Peri<'d, T>, config: Config) -> Self {\n        // No clock: No WakeGuard!\n        _ = unsafe { enable_and_reset::<T>(&NoConfig) };\n\n        let mut inst = Self {\n            info: T::info(),\n            _phantom: PhantomData,\n        };\n\n        inst.configure(config);\n        inst\n    }\n\n    fn configure(&mut self, config: Config) {\n        self.info.regs().mctl().modify(|w| {\n            w.set_rst_def(true);\n            w.set_prgm(true);\n            w.set_err(true)\n        });\n\n        self.info.regs().scml().write(|w| {\n            w.set_mono_max(config.monobit_limit_max);\n            w.set_mono_rng(config.monobit_limit_range);\n        });\n\n        self.info.regs().scr1l().write(|w| {\n            w.set_run1_max(config.run_length1_limit_max);\n            w.set_run1_rng(config.run_length1_limit_range);\n        });\n\n        self.info.regs().scr2l().write(|w| {\n            w.set_run2_max(config.run_length2_limit_max);\n            w.set_run2_rng(config.run_length2_limit_range);\n        });\n\n        self.info.regs().scr3l().write(|w| {\n            w.set_run3_max(config.run_length3_limit_max);\n            w.set_run3_rng(config.run_length3_limit_range);\n        });\n\n        self.info\n            .regs()\n            .frqmax()\n            .write(|w| w.set_frq_max(config.freq_counter_max));\n\n        self.info\n            .regs()\n            .frqmin()\n            .write(|w| w.set_frq_min(config.freq_counter_min));\n\n        self.info.regs().scmisc().write(|w| {\n            w.set_lrun_max(config.long_run_limit_max);\n            w.set_rty_ct(config.retry_count);\n        });\n\n        self.info.regs().sdctl().write(|w| {\n            w.set_samp_size(config.sample_size);\n            w.set_ent_dly(config.entropy_delay);\n        });\n\n        self.info\n            .regs()\n            .osc2_ctl()\n            .modify(|w| w.set_trng_ent_ctl(config.osc_mode.into()));\n\n        self.info.regs().mctl().modify(|w| w.set_prgm(false));\n\n        let _ = self.info.regs().ent(7).read();\n\n        self.start();\n    }\n\n    fn start(&mut self) {\n        #[cfg(feature = \"mcxa2xx\")]\n        self.info.regs().mctl().modify(|w| w.set_trng_acc(true));\n    }\n\n    fn stop(&mut self) {\n        #[cfg(feature = \"mcxa2xx\")]\n        self.info.regs().mctl().modify(|w| w.set_trng_acc(false));\n    }\n\n    fn blocking_wait_for_generation(&mut self) {\n        while !self.info.regs().mctl().read().ent_val() {\n            if self.info.regs().mctl().read().err() {\n                self.info.regs().mctl().modify(|w| w.set_err(true));\n            }\n        }\n    }\n\n    fn fill_chunk(&mut self, chunk: &mut [u8]) {\n        let mut entropy = [0u32; 8];\n\n        for (i, item) in entropy.iter_mut().enumerate() {\n            *item = self.info.regs().ent(i).read().ent();\n        }\n\n        let entropy: [u8; 32] = unsafe { core::mem::transmute(entropy) };\n\n        chunk.copy_from_slice(&entropy[..chunk.len()]);\n    }\n\n    // Blocking API\n\n    /// Fill the buffer with random bytes, blocking version.\n    pub fn blocking_fill_bytes(&mut self, buf: &mut [u8]) {\n        if buf.is_empty() {\n            return; // nothing to fill\n        }\n\n        for chunk in buf.chunks_mut(32) {\n            self.blocking_wait_for_generation();\n            self.fill_chunk(chunk);\n        }\n    }\n\n    /// Return a random u32, blocking version.\n    pub fn blocking_next_u32(&mut self) -> u32 {\n        self.blocking_wait_for_generation();\n        // New random bytes are generated only after reading ENT7\n        self.info.regs().ent(7).read().ent()\n    }\n\n    /// Return a random u64, blocking version.\n    pub fn blocking_next_u64(&mut self) -> u64 {\n        self.blocking_wait_for_generation();\n\n        let mut result = u64::from(self.info.regs().ent(6).read().ent()) << 32;\n        // New random bytes are generated only after reading ENT7\n        result |= u64::from(self.info.regs().ent(7).read().ent());\n        result\n    }\n\n    /// Return the full block of `[u32; 8]` generated by the hardware,\n    /// blocking version.\n    pub fn blocking_next_block(&mut self, block: &mut [u32; BLOCK_SIZE]) {\n        self.blocking_wait_for_generation();\n        for (reg, result) in (0..8).map(|i| self.info.regs().ent(i)).zip(block.iter_mut()) {\n            *result = reg.read().ent();\n        }\n    }\n}\n\nimpl<'d> Trng<'d, Blocking> {\n    /// Instantiates a new TRNG peripheral driver with 128 samples of entropy.\n    pub fn new_blocking_128<T: Instance>(_peri: Peri<'d, T>) -> Self {\n        Self::new_inner(\n            _peri,\n            Config {\n                sample_size: 128,\n                retry_count: 1,\n                long_run_limit_max: 29,\n                monobit_limit_max: 94,\n                monobit_limit_range: 61,\n                run_length1_limit_max: 39,\n                run_length1_limit_range: 39,\n                run_length2_limit_max: 24,\n                run_length2_limit_range: 25,\n                run_length3_limit_max: 17,\n                run_length3_limit_range: 18,\n                ..Default::default()\n            },\n        )\n    }\n\n    /// Instantiates a new TRNG peripheral driver with  256 samples of entropy.\n    pub fn new_blocking_256<T: Instance>(_peri: Peri<'d, T>) -> Self {\n        Self::new_inner(\n            _peri,\n            Config {\n                sample_size: 256,\n                retry_count: 1,\n                long_run_limit_max: 31,\n                monobit_limit_max: 171,\n                monobit_limit_range: 86,\n                run_length1_limit_max: 63,\n                run_length1_limit_range: 56,\n                run_length2_limit_max: 38,\n                run_length2_limit_range: 38,\n                run_length3_limit_max: 25,\n                run_length3_limit_range: 26,\n                ..Default::default()\n            },\n        )\n    }\n\n    /// Instantiates a new TRNG peripheral driver with 512 samples of entropy.\n    pub fn new_blocking_512<T: Instance>(_peri: Peri<'d, T>) -> Self {\n        Self::new_inner(_peri, Default::default())\n    }\n\n    /// Instantiates a new TRNG peripheral driver.\n    ///\n    /// NOTE: this constructor makes no attempt at validating the\n    /// parameters. If you get this wrong, the security guarantees of\n    /// the TRNG with regards to entropy may be violated\n    pub fn new_blocking_with_custom_config<T: Instance>(_peri: Peri<'d, T>, config: Config) -> Self {\n        Self::new_inner(_peri, config)\n    }\n}\n\nimpl<'d> Trng<'d, Async> {\n    /// Instantiates a new TRNG peripheral driver with 128 samples of entropy.\n    pub fn new_128<T: Instance>(\n        _peri: Peri<'d, T>,\n        _irq: impl crate::interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd,\n    ) -> Self {\n        let inst = Self::new_inner(\n            _peri,\n            Config {\n                sample_size: 128,\n                retry_count: 1,\n                long_run_limit_max: 29,\n                monobit_limit_max: 94,\n                monobit_limit_range: 61,\n                run_length1_limit_max: 39,\n                run_length1_limit_range: 39,\n                run_length2_limit_max: 24,\n                run_length2_limit_range: 25,\n                run_length3_limit_max: 17,\n                run_length3_limit_range: 18,\n                ..Default::default()\n            },\n        );\n\n        T::Interrupt::unpend();\n        INT_STAT.store(0, Ordering::Release);\n        unsafe {\n            T::Interrupt::enable();\n        }\n        inst\n    }\n\n    /// Instantiates a new TRNG peripheral driver with 256 samples of entropy.\n    pub fn new_256<T: Instance>(\n        _peri: Peri<'d, T>,\n        _irq: impl crate::interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd,\n    ) -> Self {\n        let inst = Self::new_inner(\n            _peri,\n            Config {\n                sample_size: 256,\n                retry_count: 1,\n                long_run_limit_max: 31,\n                monobit_limit_max: 171,\n                monobit_limit_range: 86,\n                run_length1_limit_max: 63,\n                run_length1_limit_range: 56,\n                run_length2_limit_max: 38,\n                run_length2_limit_range: 38,\n                run_length3_limit_max: 25,\n                run_length3_limit_range: 26,\n                ..Default::default()\n            },\n        );\n\n        T::Interrupt::unpend();\n        INT_STAT.store(0, Ordering::Release);\n        unsafe {\n            T::Interrupt::enable();\n        }\n        inst\n    }\n\n    /// Instantiates a new TRNG peripheral driver with 512 samples of entropy.\n    pub fn new_512<T: Instance>(\n        _peri: Peri<'d, T>,\n        _irq: impl crate::interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd,\n    ) -> Self {\n        let inst = Self::new_inner(_peri, Default::default());\n\n        T::Interrupt::unpend();\n        INT_STAT.store(0, Ordering::Release);\n        unsafe {\n            T::Interrupt::enable();\n        }\n        inst\n    }\n\n    /// Instantiates a new TRNG peripheral driver.\n    ///\n    /// NOTE: this constructor makes no attempt at validating the\n    /// parameters. If you get this wrong, the security guarantees of\n    /// the TRNG with regards to entropy may be violated\n    pub fn new_with_custom_config<T: Instance>(\n        _peri: Peri<'d, T>,\n        _irq: impl crate::interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd,\n        config: Config,\n    ) -> Self {\n        let inst = Self::new_inner(_peri, config);\n\n        T::Interrupt::unpend();\n        INT_STAT.store(0, Ordering::Release);\n        unsafe {\n            T::Interrupt::enable();\n        }\n        inst\n    }\n\n    fn enable_ints(&mut self) {\n        self.info.regs().int_mask().write(|w| {\n            w.set_hw_err(true);\n            w.set_ent_val(true);\n            w.set_frq_ct_fail(true);\n            w.set_intg_flt(true);\n        });\n    }\n\n    async fn wait_for_generation(&mut self) -> Result<(), Error> {\n        self.info\n            .wait_cell()\n            .wait_for_value(|| {\n                self.enable_ints();\n                let status = INT_STAT.swap(0, Ordering::AcqRel);\n                if status == 0 {\n                    return None;\n                }\n\n                let status = IntStatus(status);\n\n                if status.ent_val() == IntStatusEntVal::ENT_VAL_VALID {\n                    Some(Ok(()))\n                } else if status.frq_ct_fail() {\n                    Some(Err(Error::FrequencyCountFail))\n                } else if status.hw_err() {\n                    Some(Err(Error::HardwareFail))\n                } else if status.intg_flt() {\n                    Some(Err(Error::IntegrityError))\n                } else {\n                    Some(Err(Error::ErrorStatus))\n                }\n            })\n            .await\n            .map_err(|_| Error::ErrorStatus)\n            .flatten()\n    }\n\n    // Async API\n\n    /// Fill the buffer with random bytes, async version.\n    pub async fn async_fill_bytes(&mut self, buf: &mut [u8]) -> Result<(), Error> {\n        if buf.is_empty() {\n            return Ok(()); // nothing to fill\n        }\n\n        for chunk in buf.chunks_mut(32) {\n            self.wait_for_generation().await?;\n            self.fill_chunk(chunk);\n        }\n\n        Ok(())\n    }\n\n    /// Return a random u32, async version.\n    pub async fn async_next_u32(&mut self) -> Result<u32, Error> {\n        self.wait_for_generation().await?;\n        // New random bytes are generated only after reading ENT7\n        Ok(self.info.regs().ent(7).read().ent())\n    }\n\n    /// Return a random u64, async version.\n    pub async fn async_next_u64(&mut self) -> Result<u64, Error> {\n        self.wait_for_generation().await?;\n\n        let mut result = u64::from(self.info.regs().ent(6).read().ent()) << 32;\n        // New random bytes are generated only after reading ENT7\n        result |= u64::from(self.info.regs().ent(7).read().ent());\n\n        Ok(result)\n    }\n\n    /// Return the full block of `[u32; 8]` generated by the hardware,\n    /// async version.\n    pub async fn async_next_block(&mut self, block: &mut [u32; BLOCK_SIZE]) -> Result<(), Error> {\n        self.wait_for_generation().await?;\n\n        for (reg, result) in (0..8).map(|i| self.info.regs().ent(i)).zip(block.iter_mut()) {\n            *result = reg.read().ent();\n        }\n\n        Ok(())\n    }\n}\n\nimpl<M: Mode> Drop for Trng<'_, M> {\n    fn drop(&mut self) {\n        // wait until allowed to stop\n        while !self.info.regs().mctl().read().tstop_ok() {}\n        // stop\n        self.stop();\n        // reset the TRNG\n        self.info.regs().mctl().write(|w| w.set_rst_def(true));\n    }\n}\n\n/// Trng errors\n#[derive(Clone, Copy, Debug)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\n#[non_exhaustive]\npub enum Error {\n    /// Integrity error.\n    IntegrityError,\n\n    /// Frequency counter fail\n    FrequencyCountFail,\n\n    /// Error status\n    ErrorStatus,\n\n    /// Buffer argument is invalid\n    InvalidBuffer,\n\n    /// Hardware fail\n    HardwareFail,\n}\n\nstatic INT_STAT: AtomicU32 = AtomicU32::new(0);\n\n/// TRNG interrupt handler.\npub struct InterruptHandler<T: Instance> {\n    _phantom: PhantomData<T>,\n}\n\nimpl<T: Instance> Handler<T::Interrupt> for InterruptHandler<T> {\n    unsafe fn on_interrupt() {\n        T::PERF_INT_INCR();\n        let int_status = T::info().regs().int_status().read().0;\n        INT_STAT.fetch_or(int_status, Ordering::AcqRel);\n        if int_status != 0 {\n            T::info().regs().int_ctrl().write(|w| {\n                w.set_hw_err(false);\n                w.set_ent_val(false);\n                w.set_frq_ct_fail(false);\n                w.set_intg_flt(false);\n            });\n            T::PERF_INT_WAKE_INCR();\n            T::info().wait_cell().wake();\n        }\n    }\n}\n\n/// True random number generator configuration parameters.\n#[derive(Clone, Copy, Debug)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\n#[non_exhaustive]\npub struct Config {\n    /// Total number of Entropy samples that will be taken during\n    /// Entropy generation.\n    pub sample_size: u16,\n\n    /// Length (in system clocks) of each Entropy sample taken.\n    pub entropy_delay: u16,\n\n    /// Frequency Counter Maximum Limit\n    pub freq_counter_max: u32,\n\n    /// Frequency Counter Minimum Limit\n    pub freq_counter_min: u32,\n\n    /// Statistical check monobit max limit\n    pub monobit_limit_max: u16,\n\n    /// Statistical check monobit range\n    pub monobit_limit_range: u16,\n\n    /// Statistical check run length 1 limit max\n    pub run_length1_limit_max: u16,\n\n    /// Statistical check run length 1 limit range\n    pub run_length1_limit_range: u16,\n\n    /// Statistical check run length 2 limit max\n    pub run_length2_limit_max: u16,\n\n    /// Statistical check run length 2 limit range\n    pub run_length2_limit_range: u16,\n\n    /// Statistical check run length 3 limit max\n    pub run_length3_limit_max: u16,\n\n    /// Statistical check run length 3 limit range\n    pub run_length3_limit_range: u16,\n\n    /// Retry count\n    pub retry_count: u8,\n\n    /// Long run limit max\n    pub long_run_limit_max: u8,\n\n    /// Sparse bit limit\n    pub sparse_bit_limit: u16,\n\n    /// Oscillator mode\n    pub osc_mode: OscMode,\n}\n\nimpl Default for Config {\n    fn default() -> Self {\n        Self {\n            sample_size: 512,\n            entropy_delay: 32_000,\n            freq_counter_max: 75_000,\n            freq_counter_min: 30_000,\n            monobit_limit_max: 317,\n            monobit_limit_range: 122,\n            run_length1_limit_max: 107,\n            run_length1_limit_range: 80,\n            run_length2_limit_max: 62,\n            run_length2_limit_range: 55,\n            run_length3_limit_max: 39,\n            run_length3_limit_range: 39,\n            retry_count: 1,\n            long_run_limit_max: 32,\n            sparse_bit_limit: 0,\n            osc_mode: OscMode::DualOscs,\n        }\n    }\n}\n\n/// Sample size.\n#[derive(Clone, Copy, Debug)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\n#[non_exhaustive]\npub enum SampleSize {\n    /// 128 bits\n    _128,\n\n    /// 256 bits\n    _256,\n\n    /// 512 bits\n    _512,\n}\n\n/// Oscillator mode.\n#[derive(Clone, Copy, Debug)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\n#[non_exhaustive]\npub enum OscMode {\n    /// Single oscillator using OSC1.\n    SingleOsc1,\n\n    /// Dual oscillator.\n    DualOscs,\n\n    /// Single oscillator using OSC2.\n    SingleOsc2,\n}\n\nimpl From<OscMode> for TrngEntCtl {\n    fn from(value: OscMode) -> Self {\n        match value {\n            OscMode::SingleOsc1 => Self::TRNG_ENT_CTL_SINGLE_OSC1,\n            OscMode::DualOscs => Self::TRNG_ENT_CTL_DUAL_OSCS,\n            OscMode::SingleOsc2 => Self::TRNG_ENT_CTL_SINGLE_OSC2,\n        }\n    }\n}\n\nimpl<'d, M: Mode> rand_core_06::RngCore for Trng<'d, M> {\n    fn next_u32(&mut self) -> u32 {\n        self.blocking_next_u32()\n    }\n\n    fn next_u64(&mut self) -> u64 {\n        self.blocking_next_u64()\n    }\n\n    fn fill_bytes(&mut self, dest: &mut [u8]) {\n        self.blocking_fill_bytes(dest);\n    }\n\n    fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), rand_core_06::Error> {\n        self.blocking_fill_bytes(dest);\n        Ok(())\n    }\n}\n\nimpl<'d, M: Mode> rand_core_06::CryptoRng for Trng<'d, M> {}\n\nimpl<'d, M: Mode> rand_core_09::RngCore for Trng<'d, M> {\n    fn next_u32(&mut self) -> u32 {\n        self.blocking_next_u32()\n    }\n\n    fn next_u64(&mut self) -> u64 {\n        self.blocking_next_u64()\n    }\n\n    fn fill_bytes(&mut self, dest: &mut [u8]) {\n        self.blocking_fill_bytes(dest);\n    }\n}\n\nimpl<'d, M: Mode> rand_core_09::CryptoRng for Trng<'d, M> {}\n\nimpl<'d, M: Mode> rand_core_06::block::BlockRngCore for Trng<'d, M> {\n    type Item = u32;\n    type Results = [Self::Item; BLOCK_SIZE];\n\n    fn generate(&mut self, results: &mut Self::Results) {\n        self.blocking_next_block(results);\n    }\n}\n\nimpl<'d, M: Mode> rand_core_09::block::BlockRngCore for Trng<'d, M> {\n    type Item = u32;\n    type Results = [Self::Item; BLOCK_SIZE];\n\n    fn generate(&mut self, results: &mut Self::Results) {\n        self.blocking_next_block(results);\n    }\n}\n\nimpl<'d, M: Mode> rand_core_09::block::CryptoBlockRng for Trng<'d, M> {}\n\ntrait SealedInstance: Gate<MrccPeriphConfig = NoConfig> {\n    fn info() -> &'static Info;\n\n    const PERF_INT_INCR: fn();\n    const PERF_INT_WAKE_INCR: fn();\n}\n\n/// CRC Instance\n#[allow(private_bounds)]\npub trait Instance: SealedInstance + PeripheralType + 'static + Send {\n    /// Interrupt for this TRNG instance.\n    type Interrupt: typelevel::Interrupt;\n}\n\nstruct Info {\n    regs: pac::trng::Trng,\n    wait_cell: WaitCell,\n}\n\nimpl Info {\n    #[inline(always)]\n    fn regs(&self) -> pac::trng::Trng {\n        self.regs\n    }\n\n    #[inline(always)]\n    fn wait_cell(&self) -> &WaitCell {\n        &self.wait_cell\n    }\n}\n\nunsafe impl Sync for Info {}\n\nmacro_rules! impl_instance {\n    ($($n:literal),*) => {\n        $(\n            paste!{\n                impl SealedInstance for crate::peripherals::[<TRNG $n>] {\n                    fn info() -> &'static Info {\n                        static INFO: Info = Info {\n                            regs: pac::[<TRNG $n>],\n                            wait_cell: WaitCell::new(),\n                        };\n                        &INFO\n                    }\n\n                    const PERF_INT_INCR: fn() = crate::perf_counters::[<incr_interrupt_trng $n>];\n                    const PERF_INT_WAKE_INCR: fn() = crate::perf_counters::[<incr_interrupt_trng $n _wake>];\n                }\n\n                impl Instance for crate::peripherals::[<TRNG $n>] {\n                    type Interrupt = crate::interrupt::typelevel::[<TRNG $n>];\n                }\n            }\n        )*\n    };\n}\n\nimpl_instance!(0);\n"
  },
  {
    "path": "embassy-mcxa/src/wwdt.rs",
    "content": "//! Windowed Watchdog Timer (WWDT) driver for MCXA microcontrollers.\n//!\n//! The WWDT is a hardware timer that can reset the system or generate an interrupt if the software fails to\n//! periodically \"feed\" the watchdog within a specified time window. This helps detect\n//! and recover from software failures or system hangs.\n//!\n//! The FRO12M provides a 1 MHz clock (clk_1m) used as WWDT0 independant clock source. This clock is / 4 by an internal fixed divider.\n\nuse core::marker::PhantomData;\n\nuse embassy_hal_internal::{Peri, PeripheralType};\nuse embassy_time::Duration;\nuse paste::paste;\n\nuse crate::clocks::periph_helpers::Clk1MConfig;\nuse crate::clocks::{ClockError, Gate, WakeGuard, enable_and_reset};\nuse crate::interrupt::typelevel;\nuse crate::interrupt::typelevel::{Handler, Interrupt};\nuse crate::pac;\nuse crate::pac::wwdt::vals::{Wden, Wdprotect, Wdreset};\n\n/// WWDT0 Error types\n#[derive(Debug, Clone, Copy, PartialEq, Eq)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub enum Error {\n    /// Clock configuration error.\n    ClockSetup(ClockError),\n    TimeoutTooSmall,\n    TimeoutTooLarge,\n    WarningTooLarge,\n}\n\n/// WWDT configuration\n#[derive(Debug, Clone, Copy, PartialEq, Eq)]\npub struct Config {\n    /// The timeout period after which the watchdog will trigger\n    pub timeout: Duration,\n    pub warning: Option<Duration>,\n}\n\nimpl Default for Config {\n    fn default() -> Self {\n        Self {\n            timeout: Duration::from_secs(1),\n            warning: None,\n        }\n    }\n}\n\n/// Watchdog peripheral\npub struct Watchdog<'d> {\n    info: &'static Info,\n    _phantom: PhantomData<&'d mut ()>,\n    _wg: Option<WakeGuard>,\n}\n\nimpl<'d> Watchdog<'d> {\n    /// Create a new WWDT instance.\n    ///\n    /// Configure the WWDT, enables the interrupt, set the timeout and or warning value.\n    ///\n    /// # Arguments\n    ///\n    /// * `_peri` - The WWDT peripheral instance\n    /// * `_irq` - Interrupt binding for WWDT0\n    /// * `config - WWDT config with timeout and optional warning value\n    pub fn new<T: Instance>(\n        _peri: Peri<'d, T>,\n        _irq: impl crate::interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd,\n        config: Config,\n    ) -> Result<Self, Error> {\n        let parts = unsafe { enable_and_reset::<T>(&Clk1MConfig).map_err(Error::ClockSetup)? };\n\n        let watchdog = Self {\n            info: T::info(),\n            _phantom: PhantomData,\n            _wg: parts.wake_guard,\n        };\n\n        let frequency = parts.freq / 4;\n        let timeout_cycles = (frequency as u64 * config.timeout.as_micros()) / 1_000_000;\n\n        // Ensure the value fits in u32 and is within valid range\n        //\n        // Writing a value below FFh causes 00_00FFh to load into the\n        // register. Therefore, the minimum timeout interval is TWDCLK\n        // X 256 X 4.\n        if timeout_cycles > 0xFFFFFF {\n            return Err(Error::TimeoutTooLarge);\n        }\n\n        if timeout_cycles <= 0xFF {\n            return Err(Error::TimeoutTooSmall);\n        }\n\n        watchdog.set_timeout_value(timeout_cycles as u32);\n\n        // Windows value is set to max at reset for no effect.\n\n        if let Some(warning_value) = config.warning {\n            let warning_cycles = (frequency as u64 * warning_value.as_micros()) / 1_000_000;\n            if warning_cycles > 0x3FF {\n                return Err(Error::WarningTooLarge);\n            }\n\n            watchdog.set_warning_value(warning_cycles as u16);\n            watchdog.enable_interrupt();\n        } else {\n            watchdog.enable_reset();\n        }\n\n        watchdog.lock_oscillator();\n\n        T::Interrupt::unpend();\n\n        // Safety: `_irq` ensures an Interrupt Handler exists.\n        unsafe {\n            T::Interrupt::enable();\n        }\n\n        Ok(watchdog)\n    }\n\n    /// Start the watchdog timer with the specified timeout period.\n    pub fn start(&mut self) {\n        self.enable();\n        self.feed();\n\n        // Set the WDPROTECT bit to false after the Feed Sequence (0xAA, 0x55)\n        self.set_flexible_mode();\n    }\n\n    /// Feed the watchdog to prevent timeout.\n    ///\n    /// This must be called periodically before the timeout period expires to prevent\n    /// the watchdog from triggering a reset or interrupt.\n    pub fn feed(&self) {\n        critical_section::with(|_cs| {\n            self.info.regs().feed().write(|w| w.set_feed(0xAA));\n            self.info.regs().feed().write(|w| w.set_feed(0x55));\n        });\n    }\n\n    /// Enable the watchdog timer.\n    /// Function is blocking until the watchdog is actually started.\n    fn enable(&self) {\n        self.info.regs().mod_().modify(|w| w.set_wden(Wden::RUN));\n        while self.info.regs().tc().read().count() == 0xFF {}\n    }\n\n    /// Set the watchdog protection mode to flexible.\n    fn set_flexible_mode(&self) {\n        self.info.regs().mod_().modify(|w| w.set_wdprotect(Wdprotect::FLEXIBLE));\n    }\n\n    /// Enable interrupt mode.\n    fn enable_interrupt(&self) {\n        self.info.regs().mod_().modify(|w| w.set_wdreset(Wdreset::INTERRUPT));\n    }\n\n    /// Enable reset mode.\n    fn enable_reset(&self) {\n        self.info.regs().mod_().modify(|w| w.set_wdreset(Wdreset::RESET));\n    }\n\n    /// Set the timeout value in clock cycles.\n    ///\n    /// # Arguments\n    ///\n    /// * `timeout` - Number of clock cycles before timeout.\n    fn set_timeout_value(&self, timeout: u32) {\n        self.info.regs().tc().write(|w| w.set_count(timeout));\n    }\n\n    /// Set the warning interrupt value in clock cycles.\n    ///\n    /// # Arguments\n    ///\n    /// * `warning` - Number of clock cycles before warning interrupt.\n    fn set_warning_value(&self, warning: u16) {\n        self.info.regs().warnint().write(|w| w.set_warnint(warning));\n    }\n\n    /// Lock the oscillator to prevent disabling or powering down the watchdog oscillator.\n    fn lock_oscillator(&self) {\n        self.info.regs().mod_().modify(|w| w.set_lock(true));\n    }\n}\n\n/// WWDT interrupt handler.\n///\n/// This handler is called when the watchdog warning interrupt fires.\n/// When reset happens, the interrupt handler will never be reached.\npub struct InterruptHandler<T: Instance> {\n    _phantom: PhantomData<T>,\n}\n\nimpl<T: Instance> Handler<T::Interrupt> for InterruptHandler<T> {\n    unsafe fn on_interrupt() {\n        crate::perf_counters::incr_interrupt_wwdt();\n        if T::info().regs().mod_().read().wdtof() {\n            #[cfg(feature = \"defmt\")]\n            defmt::trace!(\"WWDT0: Timeout occurred\");\n\n            T::info().regs().mod_().modify(|w| w.set_wdtof(true));\n        }\n\n        if T::info().regs().mod_().read().wdint() {\n            #[cfg(feature = \"defmt\")]\n            defmt::trace!(\"T::INFO().REGS()0: Warning interrupt\");\n\n            T::info().regs().mod_().modify(|w| w.set_wdint(true));\n        }\n    }\n}\n\ntrait SealedInstance: Gate<MrccPeriphConfig = Clk1MConfig> {\n    fn info() -> &'static Info;\n}\n\n/// WWDT Instance\n#[allow(private_bounds)]\npub trait Instance: SealedInstance + PeripheralType + 'static + Send {\n    /// Interrupt for this WWDT instance.\n    type Interrupt: typelevel::Interrupt;\n}\n\nstruct Info {\n    regs: pac::wwdt::Wwdt,\n}\n\nimpl Info {\n    #[inline(always)]\n    fn regs(&self) -> pac::wwdt::Wwdt {\n        self.regs\n    }\n}\n\nunsafe impl Sync for Info {}\n\nmacro_rules! impl_instance {\n    ($($n:literal);*) => {\n        $(\n            paste!{\n                impl SealedInstance for crate::peripherals::[<WWDT $n>] {\n                    fn info() -> &'static Info {\n                        static INFO: Info = Info {\n                            regs: pac::[<WWDT $n>],\n                        };\n                        &INFO\n                    }\n                }\n\n                impl Instance for crate::peripherals::[<WWDT $n>] {\n                    type Interrupt = crate::interrupt::typelevel::[<WWDT $n>];\n                }\n            }\n        )*\n    };\n}\n\nimpl_instance!(0);\n\n#[cfg(feature = \"mcxa5xx\")]\nimpl_instance!(1);\n"
  },
  {
    "path": "embassy-microchip/CHANGELOG.md",
    "content": "# Changelog\n\nAll notable changes to this project will be documented in this file.\n\nThe format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),\nand this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).\n\n<!-- next-header -->\n## Unreleased - ReleaseDate\n\n- Add UART driver.\n- First commit.\n"
  },
  {
    "path": "embassy-microchip/Cargo.toml",
    "content": "[package]\nname = \"embassy-microchip\"\nversion = \"0.1.0\"\nedition = \"2024\"\nlicense = \"MIT OR Apache-2.0\"\ndescription = \"Embassy Hardware Abstraction Layer (HAL) for the MEC and CEC family of microcontrollers\"\nkeywords = [\"embedded\", \"async\", \"microchip\", \"mec\", \"cec\", \"embedded-hal\"]\ncategories = [\"embedded\", \"hardware-support\", \"no-std\", \"asynchronous\"]\nrepository = \"https://github.com/embassy-rs/embassy\"\ndocumentation = \"https://docs.embassy.dev/embassy-microchip\"\n\n# TODO: Remove to publish initial version\npublish = false\n\n[package.metadata.embassy]\nbuild = [\n    {target = \"thumbv7em-none-eabihf\", features = [\"defmt\", \"mec1721n_b0_lj\", \"rt\", \"unstable-pac\"]},\n    {target = \"thumbv7em-none-eabihf\", features = [\"defmt\", \"mec1721n_b0_sz\", \"rt\", \"unstable-pac\"]},\n    {target = \"thumbv7em-none-eabihf\", features = [\"defmt\", \"mec1723n_b0_lj\", \"rt\", \"unstable-pac\"]},\n    {target = \"thumbv7em-none-eabihf\", features = [\"defmt\", \"mec1723n_b0_sz\", \"rt\", \"unstable-pac\"]},\n    {target = \"thumbv7em-none-eabihf\", features = [\"defmt\", \"mec1723n_f0_sz\", \"rt\", \"unstable-pac\"]},\n    {target = \"thumbv7em-none-eabihf\", features = [\"defmt\", \"mec1723n_p0_9y\", \"rt\", \"unstable-pac\"]},\n    {target = \"thumbv7em-none-eabihf\", features = [\"defmt\", \"mec1724n_b0_lj\", \"rt\", \"unstable-pac\"]},\n    {target = \"thumbv7em-none-eabihf\", features = [\"defmt\", \"mec1724n_b0_sz\", \"rt\", \"unstable-pac\"]},\n    {target = \"thumbv7em-none-eabihf\", features = [\"defmt\", \"mec1725n_b0_lj\", \"rt\", \"unstable-pac\"]},\n    {target = \"thumbv7em-none-eabihf\", features = [\"defmt\", \"mec1727n_b0_sz\", \"rt\", \"unstable-pac\"]},\n]\n\n[package.metadata.embassy_docs]\nsrc_base = \"https://github.com/embassy-rs/embassy/blob/embassy-microchip-v$VERSION/embassy-microchip/src/\"\nsrc_base_git = \"https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-microchip/src/\"\n\nfeatures = [\"mec1723n_b0_sz\", \"defmt\", \"unstable-pac\"]\nflavors = [\n    { regex_feature = \".*\", target = \"thumbv7em-none-eabihf\" }\n]\n\n[package.metadata.docs.rs]\nfeatures = [\"mec1723n_b0_sz\", \"defmt\", \"unstable-pac\"]\nrustdoc-args = [\"--cfg\", \"docsrs\"]\n\n[features]\ndefault = [\"rt\"]\n\n## Cortex-M runtime (enabled by default)\nrt = [\"mec17xx-pac/rt\"]\n\n## Enable defmt\ndefmt = [\"dep:defmt\", \"embassy-hal-internal/defmt\", \"embassy-sync/defmt\", \"mec17xx-pac/defmt\"]\n\n## Reexport the PAC for the currently enabled chip at `embassy_imxrt::pac` (unstable)\nunstable-pac = []\n\n# Features starting with `_` are for internal use only. They're not intended\n# to be enabled by other crates, and are not covered by semver guarantees.\n\n#! ### Chip selection features\n## MEC1721N_B0_LJ\nmec1721n_b0_lj = [\"mec17xx-pac/mec1721n_b0_sz\"]\n\n## MEC1721N_B0_SZ\nmec1721n_b0_sz = [\"mec17xx-pac/mec1721n_b0_sz\"]\n\n## MEC1723N_B0_LJ\nmec1723n_b0_lj = [\"mec17xx-pac/mec1723n_b0_lj\"]\n\n## MEC1723N_B0_SZ\nmec1723n_b0_sz = [\"mec17xx-pac/mec1723n_b0_sz\"]\n\n## MEC1723N_F0_SZ\nmec1723n_f0_sz = [\"mec17xx-pac/mec1723n_f0_sz\"]\n\n## MEC1723N_P0_9Y\nmec1723n_p0_9y = [\"mec17xx-pac/mec1723n_p0_9y\"]\n\n## MEC1724N_B0_LJ\nmec1724n_b0_lj = [\"mec17xx-pac/mec1724n_b0_lj\"]\n\n## MEC1723N_B0_SZ\nmec1724n_b0_sz = [\"mec17xx-pac/mec1724n_b0_sz\"]\n\n## MEC1725\nmec1725n_b0_lj = [\"mec17xx-pac/mec1725n_b0_lj\"]\n\n## MEC1727N_B0_SZ\nmec1727n_b0_sz = [\"mec17xx-pac/mec1727n_b0_sz\"]\n\n[dependencies]\ncortex-m = { version = \"0.7\", features = [\"critical-section-single-core\"] }\ncortex-m-rt = { version = \"0.7\", features = [\"device\"] }\ncritical-section = \"1.2.0\"\ndefmt = { version = \"1.0\", optional = true }\ndocument-features = \"0.2.11\"\nembassy-embedded-hal = { version = \"0.6.0\", path = \"../embassy-embedded-hal\" }\nembassy-futures = { version = \"0.1.2\", path = \"../embassy-futures\" }\nembassy-hal-internal = { version = \"0.5.0\", path = \"../embassy-hal-internal\", features = [\"cortex-m\", \"prio-bits-3\"] }\nembassy-sync = { version = \"0.8.0\", path = \"../embassy-sync\" }\nembassy-time = { version = \"0.5.1\", path = \"../embassy-time\" }\nembassy-time-driver = { version = \"0.2.2\", path = \"../embassy-time-driver\", features = [\"tick-hz-48_000_000\"] }\nembassy-time-queue-utils = { version = \"0.3.0\", path = \"../embassy-time-queue-utils\" }\nembedded-hal-02 = { package = \"embedded-hal\", version = \"0.2.6\", features = [\"unproven\"] }\nembedded-hal-1 = { package = \"embedded-hal\", version = \"1.0\" }\nembedded-hal-async = { version = \"1.0\" }\nembedded-hal-nb = { version = \"1.0\" }\nembedded-io = \"0.7\"\nembedded-io-async = { version = \"0.7.0\" }\nfixed = \"1.29.0\"\nlog = { version = \"0.4.17\", optional = true }\nnb = \"1.0.0\"\n\n# PAC\nmec17xx-pac = { version = \"0.1.2\", default-features = false, features = [\"rt\"] }\n"
  },
  {
    "path": "embassy-microchip/README.md",
    "content": "# Embassy Microchip HAL\n\nHALs implement safe, idiomatic Rust APIs to use the hardware capabilities, so\nraw register manipulation is not needed.\n\nThe Embassy Microchip HAL targets the Microchip MEC and CEC Family of MCUs. The\nHAL implements both blocking and async APIs for many peripherals. The benefit of\nusing the async APIs is that the HAL takes care of waiting for peripherals to\ncomplete operations in low power mode and handling of interrupts, so that\napplications can focus on business logic.\n\nNOTE: The Embassy HALs can be used both for non-async and async operations. For\nasync, you can choose which runtime you want to use.\n\nFor a complete list of available peripherals and features, see the\n[embassy-microchip documentation](https://docs.embassy.dev/embassy-microchip).\n"
  },
  {
    "path": "embassy-microchip/src/fmt.rs",
    "content": "#![macro_use]\n#![allow(unused)]\n\nuse core::fmt::{Debug, Display, LowerHex};\n\n#[collapse_debuginfo(yes)]\nmacro_rules! assert {\n    ($($x:tt)*) => {\n        {\n            #[cfg(not(feature = \"defmt\"))]\n            ::core::assert!($($x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::assert!($($x)*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! assert_eq {\n    ($($x:tt)*) => {\n        {\n            #[cfg(not(feature = \"defmt\"))]\n            ::core::assert_eq!($($x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::assert_eq!($($x)*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! assert_ne {\n    ($($x:tt)*) => {\n        {\n            #[cfg(not(feature = \"defmt\"))]\n            ::core::assert_ne!($($x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::assert_ne!($($x)*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! debug_assert {\n    ($($x:tt)*) => {\n        {\n            #[cfg(not(feature = \"defmt\"))]\n            ::core::debug_assert!($($x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::debug_assert!($($x)*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! debug_assert_eq {\n    ($($x:tt)*) => {\n        {\n            #[cfg(not(feature = \"defmt\"))]\n            ::core::debug_assert_eq!($($x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::debug_assert_eq!($($x)*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! debug_assert_ne {\n    ($($x:tt)*) => {\n        {\n            #[cfg(not(feature = \"defmt\"))]\n            ::core::debug_assert_ne!($($x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::debug_assert_ne!($($x)*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! todo {\n    ($($x:tt)*) => {\n        {\n            #[cfg(not(feature = \"defmt\"))]\n            ::core::todo!($($x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::todo!($($x)*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! unreachable {\n    ($($x:tt)*) => {\n        {\n            #[cfg(not(feature = \"defmt\"))]\n            ::core::unreachable!($($x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::unreachable!($($x)*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! panic {\n    ($($x:tt)*) => {\n        {\n            #[cfg(not(feature = \"defmt\"))]\n            ::core::panic!($($x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::panic!($($x)*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! trace {\n    ($s:literal $(, $x:expr)* $(,)?) => {\n        {\n            #[cfg(feature = \"defmt\")]\n            ::defmt::trace!($s $(, $x)*);\n            #[cfg(not(feature = \"defmt\"))]\n            let _ = ($( & $x ),*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! debug {\n    ($s:literal $(, $x:expr)* $(,)?) => {\n        {\n            #[cfg(feature = \"defmt\")]\n            ::defmt::debug!($s $(, $x)*);\n            #[cfg(not(feature = \"defmt\"))]\n            let _ = ($( & $x ),*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! info {\n    ($s:literal $(, $x:expr)* $(,)?) => {\n        {\n            #[cfg(feature = \"defmt\")]\n            ::defmt::info!($s $(, $x)*);\n            #[cfg(not(feature = \"defmt\"))]\n            let _ = ($( & $x ),*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! warn {\n    ($s:literal $(, $x:expr)* $(,)?) => {\n        {\n            #[cfg(feature = \"defmt\")]\n            ::defmt::warn!($s $(, $x)*);\n            #[cfg(not(feature = \"defmt\"))]\n            let _ = ($( & $x ),*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! error {\n    ($s:literal $(, $x:expr)* $(,)?) => {\n        {\n            #[cfg(feature = \"defmt\")]\n            ::defmt::error!($s $(, $x)*);\n            #[cfg(not(feature = \"defmt\"))]\n            let _ = ($( & $x ),*);\n        }\n    };\n}\n\n#[cfg(feature = \"defmt\")]\n#[collapse_debuginfo(yes)]\nmacro_rules! unwrap {\n    ($($x:tt)*) => {\n        ::defmt::unwrap!($($x)*)\n    };\n}\n\n#[cfg(not(feature = \"defmt\"))]\n#[collapse_debuginfo(yes)]\nmacro_rules! unwrap {\n    ($arg:expr) => {\n        match $crate::fmt::Try::into_result($arg) {\n            ::core::result::Result::Ok(t) => t,\n            ::core::result::Result::Err(e) => {\n                ::core::panic!(\"unwrap of `{}` failed: {:?}\", ::core::stringify!($arg), e);\n            }\n        }\n    };\n    ($arg:expr, $($msg:expr),+ $(,)? ) => {\n        match $crate::fmt::Try::into_result($arg) {\n            ::core::result::Result::Ok(t) => t,\n            ::core::result::Result::Err(e) => {\n                ::core::panic!(\"unwrap of `{}` failed: {}: {:?}\", ::core::stringify!($arg), ::core::format_args!($($msg,)*), e);\n            }\n        }\n    }\n}\n\n#[derive(Debug, Copy, Clone, Eq, PartialEq)]\npub struct NoneError;\n\npub trait Try {\n    type Ok;\n    type Error;\n    fn into_result(self) -> Result<Self::Ok, Self::Error>;\n}\n\nimpl<T> Try for Option<T> {\n    type Ok = T;\n    type Error = NoneError;\n\n    #[inline]\n    fn into_result(self) -> Result<T, NoneError> {\n        self.ok_or(NoneError)\n    }\n}\n\nimpl<T, E> Try for Result<T, E> {\n    type Ok = T;\n    type Error = E;\n\n    #[inline]\n    fn into_result(self) -> Self {\n        self\n    }\n}\n\npub(crate) struct Bytes<'a>(pub &'a [u8]);\n\nimpl Debug for Bytes<'_> {\n    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {\n        write!(f, \"{:#02x?}\", self.0)\n    }\n}\n\nimpl Display for Bytes<'_> {\n    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {\n        write!(f, \"{:#02x?}\", self.0)\n    }\n}\n\nimpl LowerHex for Bytes<'_> {\n    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {\n        write!(f, \"{:#02x?}\", self.0)\n    }\n}\n\n#[cfg(feature = \"defmt\")]\nimpl defmt::Format for Bytes<'_> {\n    fn format(&self, fmt: defmt::Formatter) {\n        defmt::write!(fmt, \"{:02x}\", self.0)\n    }\n}\n"
  },
  {
    "path": "embassy-microchip/src/gpio.rs",
    "content": "//! GPIO driver.\n\n#![macro_use]\nuse core::future::Future;\nuse core::pin::Pin as FuturePin;\nuse core::task::{Context, Poll};\n\nuse cortex_m::interrupt::InterruptNumber;\nuse embassy_hal_internal::{Peri, PeripheralType, impl_peripheral};\nuse embassy_sync::waitqueue::AtomicWaker;\n\nuse crate::interrupt::InterruptExt;\nuse crate::pac::common::{R, RW, Reg};\nuse crate::pac::gpio::regs::{Ctrl1, Ctrl2};\nuse crate::{interrupt, pac, peripherals};\n\n// Each interrupt aggregator block holds a maximum of 31 GPIOs. Some combine a\n// considerably smaller number of pins, but we're still allocating 31\n// `AtomicWaker`s for those.\npub(crate) const PIN_COUNT: usize = 31;\n\nstatic GIRQ08_WAKERS: [AtomicWaker; PIN_COUNT] = [const { AtomicWaker::new() }; PIN_COUNT];\nstatic GIRQ09_WAKERS: [AtomicWaker; PIN_COUNT] = [const { AtomicWaker::new() }; PIN_COUNT];\nstatic GIRQ10_WAKERS: [AtomicWaker; PIN_COUNT] = [const { AtomicWaker::new() }; PIN_COUNT];\nstatic GIRQ11_WAKERS: [AtomicWaker; PIN_COUNT] = [const { AtomicWaker::new() }; PIN_COUNT];\nstatic GIRQ12_WAKERS: [AtomicWaker; PIN_COUNT] = [const { AtomicWaker::new() }; PIN_COUNT];\nstatic GIRQ26_WAKERS: [AtomicWaker; PIN_COUNT] = [const { AtomicWaker::new() }; PIN_COUNT];\n\n/// Represents a digital input or output level.\n#[derive(Debug, Eq, PartialEq, Clone, Copy)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub enum Level {\n    /// Logical low.\n    Low,\n\n    /// Logical high.\n    High,\n}\n\nimpl From<bool> for Level {\n    fn from(val: bool) -> Self {\n        match val {\n            true => Self::High,\n            false => Self::Low,\n        }\n    }\n}\n\nimpl From<Level> for bool {\n    fn from(level: Level) -> bool {\n        match level {\n            Level::High => true,\n            Level::Low => false,\n        }\n    }\n}\n\n/// Represents a pull setting for an input.\n#[derive(Debug, Clone, Copy, Eq, PartialEq)]\npub enum Pull {\n    /// No pull.\n    None,\n\n    /// Internal pull-up resistor.\n    Up,\n\n    /// Internal pull-down resistor.\n    Down,\n\n    /// Repeater mode. Pin is kept at previous voltage level when no\n    /// active driver is present on the pin.\n    Repeater,\n}\n\nimpl From<Pull> for crate::pac::Pull {\n    fn from(val: Pull) -> Self {\n        match val {\n            Pull::None => Self::NONE,\n            Pull::Up => Self::UP,\n            Pull::Down => Self::DOWN,\n            Pull::Repeater => Self::REPEATER,\n        }\n    }\n}\n\n/// Drive strenght of an output.\n#[derive(Debug, Eq, PartialEq)]\npub enum Drive {\n    /// 2mA for PIO-12, 4mA for PIO-24,\n    Weakest,\n\n    /// 4mA for PIO-12, 8mA for PIO-24,\n    Weak,\n\n    /// 8mA for PIO-12, 16mA for PIO-24,\n    Strong,\n\n    /// 12mA for PIO-12, 24mA for PIO-24,\n    Strongest,\n}\n\nimpl From<Drive> for crate::pac::Strength {\n    fn from(val: Drive) -> Self {\n        match val {\n            Drive::Weakest => Self::LOWEST,\n            Drive::Weak => Self::LOW,\n            Drive::Strong => Self::MEDIUM,\n            Drive::Strongest => Self::FULL,\n        }\n    }\n}\n\n/// Slow rate of an output.\n#[derive(Debug, Eq, PartialEq)]\npub enum SlewRate {\n    /// Slow (half-frequency) slew rate.\n    Slow,\n\n    /// Fast slew rate.\n    Fast,\n}\n\nimpl From<SlewRate> for crate::pac::SlewCtrl {\n    fn from(val: SlewRate) -> Self {\n        match val {\n            SlewRate::Slow => Self::SLOW,\n            SlewRate::Fast => Self::FAST,\n        }\n    }\n}\n\n/// GPIO input driber.\npub struct Input<'d> {\n    pin: Flex<'d>,\n}\n\nimpl<'d> Input<'d> {\n    /// Create a GPIO input driver for a [Pin] with the provided [Pull] configuration.\n    #[inline]\n    pub fn new(pin: Peri<'d, impl Pin>, pull: Pull) -> Self {\n        let mut pin = Flex::new(pin);\n        pin.set_as_input();\n        pin.set_pull(pull);\n        Self { pin }\n    }\n\n    /// Get wheter the pin input level is high.\n    #[inline]\n    pub fn is_high(&self) -> bool {\n        self.pin.is_high()\n    }\n\n    /// Get whether the pin input level is low.\n    #[inline]\n    pub fn is_low(&self) -> bool {\n        self.pin.is_low()\n    }\n\n    /// Returns the current pin level\n    #[inline]\n    pub fn get_level(&self) -> Level {\n        self.pin.get_level()\n    }\n\n    /// Wait until the pin is high. If it is already high, return immediately.\n    #[inline]\n    pub async fn wait_for_high(&mut self) {\n        self.pin.wait_for_high().await;\n    }\n\n    /// Wait until the pin is low. If it is already low, return immediately.\n    #[inline]\n    pub async fn wait_for_low(&mut self) {\n        self.pin.wait_for_low().await;\n    }\n\n    /// Wait for the pin to undergo a transition from low to high.\n    #[inline]\n    pub async fn wait_for_rising_edge(&mut self) {\n        self.pin.wait_for_rising_edge().await;\n    }\n\n    /// Wait for the pin to undergo a transition from high to low.\n    #[inline]\n    pub async fn wait_for_falling_edge(&mut self) {\n        self.pin.wait_for_falling_edge().await;\n    }\n\n    /// Wait for the pin to undergo any transition, i.e low to high OR high to low.\n    #[inline]\n    pub async fn wait_for_any_edge(&mut self) {\n        self.pin.wait_for_any_edge().await;\n    }\n}\n\n/// Interrupt trigger levels.\n#[derive(Debug, Eq, PartialEq, Copy, Clone)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub enum InterruptTrigger {\n    /// Trigger on pin low.\n    LevelLow,\n\n    /// Trigger on pin high.\n    LevelHigh,\n\n    /// Trigger on high to low transition.\n    EdgeLow,\n\n    /// Trigger on low to high transition.\n    EdgeHigh,\n\n    /// Trigger on any transition.\n    AnyEdge,\n}\n\npub unsafe fn init() {\n    // GPIO interrupts must go through the Interrupt Aggregator.\n    let blk_en = 1 << (8 + interrupt::GIRQ08.number())\n        | 1 << (8 + interrupt::GIRQ09.number())\n        | 1 << (8 + interrupt::GIRQ10.number())\n        | 1 << (8 + interrupt::GIRQ11.number())\n        | 1 << (8 + interrupt::GIRQ12.number())\n        | 1 << (8 + interrupt::GIRQ26.number());\n    crate::pac::ECIA.blk_en_set().write(|w| w.set_vtor_en_set(blk_en));\n\n    interrupt::GIRQ08.disable();\n    interrupt::GIRQ09.disable();\n    interrupt::GIRQ10.disable();\n    interrupt::GIRQ11.disable();\n    interrupt::GIRQ12.disable();\n    interrupt::GIRQ26.disable();\n\n    interrupt::GIRQ08.set_priority(interrupt::Priority::P7);\n    interrupt::GIRQ09.set_priority(interrupt::Priority::P7);\n    interrupt::GIRQ10.set_priority(interrupt::Priority::P7);\n    interrupt::GIRQ11.set_priority(interrupt::Priority::P7);\n    interrupt::GIRQ12.set_priority(interrupt::Priority::P7);\n    interrupt::GIRQ26.set_priority(interrupt::Priority::P7);\n\n    interrupt::GIRQ08.unpend();\n    interrupt::GIRQ09.unpend();\n    interrupt::GIRQ10.unpend();\n    interrupt::GIRQ11.unpend();\n    interrupt::GIRQ12.unpend();\n    interrupt::GIRQ26.unpend();\n\n    unsafe {\n        interrupt::GIRQ08.enable();\n        interrupt::GIRQ09.enable();\n        interrupt::GIRQ10.enable();\n        interrupt::GIRQ11.enable();\n        interrupt::GIRQ12.enable();\n        interrupt::GIRQ26.enable();\n    }\n}\n\n#[cfg(feature = \"rt\")]\n#[interrupt]\nfn GIRQ08() {\n    let regs = InterruptRegs {\n        result: crate::pac::ECIA.result8(),\n        clr: crate::pac::ECIA.en_clr8(),\n    };\n    irq_handler(regs, &GIRQ08_WAKERS);\n}\n\n#[cfg(feature = \"rt\")]\n#[interrupt]\nfn GIRQ09() {\n    let regs = InterruptRegs {\n        result: crate::pac::ECIA.result9(),\n        clr: crate::pac::ECIA.en_clr9(),\n    };\n    irq_handler(regs, &GIRQ09_WAKERS);\n}\n\n#[cfg(feature = \"rt\")]\n#[interrupt]\nfn GIRQ10() {\n    let regs = InterruptRegs {\n        result: crate::pac::ECIA.result10(),\n        clr: crate::pac::ECIA.en_clr10(),\n    };\n    irq_handler(regs, &GIRQ10_WAKERS);\n}\n\n#[cfg(feature = \"rt\")]\n#[interrupt]\nfn GIRQ11() {\n    let regs = InterruptRegs {\n        result: crate::pac::ECIA.result11(),\n        clr: crate::pac::ECIA.en_clr11(),\n    };\n    irq_handler(regs, &GIRQ11_WAKERS);\n}\n\n#[cfg(feature = \"rt\")]\n#[interrupt]\nfn GIRQ12() {\n    let regs = InterruptRegs {\n        result: crate::pac::ECIA.result12(),\n        clr: crate::pac::ECIA.en_clr12(),\n    };\n    irq_handler(regs, &GIRQ12_WAKERS);\n}\n\n#[cfg(feature = \"rt\")]\n#[interrupt]\nfn GIRQ26() {\n    let regs = InterruptRegs {\n        result: crate::pac::ECIA.result26(),\n        clr: crate::pac::ECIA.en_clr26(),\n    };\n    irq_handler(regs, &GIRQ26_WAKERS);\n}\n\n#[cfg(feature = \"rt\")]\n#[inline]\nfn irq_handler(regs: InterruptRegs, wakers: &[AtomicWaker; PIN_COUNT]) {\n    let result = regs.result.read();\n\n    for bit in 0..PIN_COUNT {\n        if result & (1 << bit) != 0 {\n            // mask event\n            regs.clr.write_value(1 << bit);\n            wakers[bit].wake();\n        }\n    }\n}\n\n#[must_use = \"futures do nothing unless you `.await` or poll them\"]\nstruct InputFuture<'d> {\n    pin: Peri<'d, AnyPin>,\n    trigger: InterruptTrigger,\n}\n\nimpl<'d> InputFuture<'d> {\n    fn new(pin: Peri<'d, AnyPin>, trigger: InterruptTrigger) -> Self {\n        pin.enable_interrupts(trigger);\n        Self { pin, trigger }\n    }\n}\n\nimpl<'d> Unpin for InputFuture<'d> {}\nimpl<'d> Future for InputFuture<'d> {\n    type Output = ();\n\n    fn poll(self: FuturePin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {\n        let waker = self.pin.waker();\n        waker.register(cx.waker());\n\n        // If user requested a level change, then we must check the\n        // current level of the pin. If it matches the requested\n        // level, we're done. Just produce a `Poll::Ready`. If,\n        // however, an edge change was requested, we must wait for the\n        // actual interrupt.\n        let level_change = match self.trigger {\n            InterruptTrigger::LevelLow => !self.pin.regs().ctrl1.read().inp(),\n            InterruptTrigger::LevelHigh => self.pin.regs().ctrl1.read().inp(),\n            _ => false,\n        };\n\n        // IRQ handler will mask the interrupt if the event has occurred. If the\n        // bit for $this event is cleared, that means we triggered an interrupt\n        // and an return control to the application.\n        let set = self.pin.regs().set.read();\n\n        if level_change || (set & (1 << self.pin.irq_bit()) == 0) {\n            Poll::Ready(())\n        } else {\n            // unmask event so other events can be sampled\n            self.pin.regs().set.write_value(1 << self.pin.irq_bit());\n            Poll::Pending\n        }\n    }\n}\n\nimpl<'d> Drop for InputFuture<'d> {\n    fn drop(&mut self) {\n        self.pin.disable_interrupts();\n    }\n}\n\n/// GPIO output driver.\npub struct Output<'d> {\n    pin: Flex<'d>,\n}\n\nimpl<'d> Output<'d> {\n    /// Create GPIO output driver for a [Pin] with the provided [Level].\n    #[inline]\n    pub fn new(pin: Peri<'d, impl Pin>, initial_output: Level) -> Self {\n        let mut pin = Flex::new(pin);\n\n        match initial_output {\n            Level::High => pin.set_high(),\n            Level::Low => pin.set_low(),\n        }\n\n        pin.set_as_output();\n\n        Self { pin }\n    }\n\n    /// Set the pin's drive strength.\n    #[inline]\n    pub fn set_drive_strength(&mut self, strength: Drive) {\n        self.pin.set_drive_strength(strength)\n    }\n\n    /// Set the pin's slew rate.\n    #[inline]\n    pub fn set_slew_rate(&mut self, slew_rate: SlewRate) {\n        self.pin.set_slew_rate(slew_rate)\n    }\n\n    /// Set the outpt as high.\n    #[inline]\n    pub fn set_high(&mut self) {\n        self.pin.set_high()\n    }\n\n    /// Set the output as low.\n    #[inline]\n    pub fn set_low(&mut self) {\n        self.pin.set_low()\n    }\n\n    /// Set the output level.\n    #[inline]\n    pub fn set_level(&mut self, level: Level) {\n        self.pin.set_level(level)\n    }\n\n    /// Is the outpt pin set as high?\n    #[inline]\n    pub fn is_set_high(&self) -> bool {\n        self.pin.is_set_high()\n    }\n\n    /// Is the output pin set as low?\n    #[inline]\n    pub fn is_set_low(&self) -> bool {\n        self.pin.is_set_low()\n    }\n\n    /// What level output is set to\n    #[inline]\n    pub fn get_output_level(&self) -> Level {\n        self.pin.get_output_level()\n    }\n\n    /// Toggle pin output\n    #[inline]\n    pub fn toggle(&mut self) {\n        self.pin.toggle()\n    }\n}\n\n/// GPIO output open-drain.\npub struct OutputOpenDrain<'d> {\n    pin: Flex<'d>,\n}\n\nimpl<'d> OutputOpenDrain<'d> {\n    /// Create GPIO output driver for a [Pin] in open drain mode with the\n    /// provided [Level].\n    #[inline]\n    pub fn new(pin: Peri<'d, impl Pin>, initial_output: Level) -> Self {\n        let mut pin = Flex::new(pin);\n\n        pin.set_as_output_open_drain();\n        pin.set_level(initial_output);\n\n        Self { pin }\n    }\n\n    /// Set the pin's pull-up.\n    #[inline]\n    pub fn set_pullup(&mut self, enable: bool) {\n        if enable {\n            self.pin.set_pull(Pull::Up)\n        } else {\n            self.pin.set_pull(Pull::None)\n        }\n    }\n\n    /// Set the pin's drive strength.\n    #[inline]\n    pub fn set_drive_strength(&mut self, strength: Drive) {\n        self.pin.set_drive_strength(strength)\n    }\n\n    /// Set the pin's slew rate.\n    #[inline]\n    pub fn set_slew_rate(&mut self, slew_rate: SlewRate) {\n        self.pin.set_slew_rate(slew_rate)\n    }\n\n    /// Set the output as high.\n    #[inline]\n    pub fn set_high(&mut self) {\n        self.pin.set_high()\n    }\n\n    /// Set the output as low.\n    #[inline]\n    pub fn set_low(&mut self) {\n        self.pin.set_low()\n    }\n\n    /// Set the output level.\n    #[inline]\n    pub fn set_level(&mut self, level: Level) {\n        self.pin.set_level(level)\n    }\n\n    /// Is the output level high?\n    #[inline]\n    pub fn is_set_high(&self) -> bool {\n        !self.is_set_low()\n    }\n\n    /// Is the output level low?\n    #[inline]\n    pub fn is_set_low(&self) -> bool {\n        self.pin.is_set_as_output()\n    }\n\n    /// What level output is set to\n    #[inline]\n    pub fn get_output_level(&self) -> Level {\n        self.is_set_high().into()\n    }\n\n    /// Toggle pin output\n    #[inline]\n    pub fn toggle(&mut self) {\n        self.pin.toggle()\n    }\n\n    /// Get whether the pin input level is high.\n    #[inline]\n    pub fn is_high(&self) -> bool {\n        self.pin.is_high()\n    }\n\n    /// Get whether the pin input level is low.\n    #[inline]\n    pub fn is_low(&self) -> bool {\n        self.pin.is_low()\n    }\n\n    /// Returns current pin level\n    #[inline]\n    pub fn get_level(&self) -> Level {\n        self.is_high().into()\n    }\n\n    /// Wait until the pin is high. If it is already high, return immediately.\n    #[inline]\n    pub async fn wait_for_high(&mut self) {\n        self.pin.wait_for_high().await;\n    }\n\n    /// Wait until the pin is low. If it is already low, return immediately.\n    #[inline]\n    pub async fn wait_for_low(&mut self) {\n        self.pin.wait_for_low().await;\n    }\n\n    /// Wait for the pin to undergo a transition from low to high.\n    #[inline]\n    pub async fn wait_for_rising_edge(&mut self) {\n        self.pin.wait_for_rising_edge().await;\n    }\n\n    /// Wait for the pin to undergo a transition from high to low.\n    #[inline]\n    pub async fn wait_for_falling_edge(&mut self) {\n        self.pin.wait_for_falling_edge().await;\n    }\n\n    /// Wait for the pin to undergo any transition, i.e low to high OR high to\n    /// low.\n    #[inline]\n    pub async fn wait_for_any_edge(&mut self) {\n        self.pin.wait_for_any_edge().await;\n    }\n}\n\n/// GPIO flexible pin.\n///\n/// This pin can be either an input or output pin. The output level register bit\n/// will remain set while not in output mode, so the pin's level will be\n/// 'remembered' when it is not in output mode.\npub struct Flex<'d> {\n    pin: Peri<'d, AnyPin>,\n}\n\nimpl<'d> Flex<'d> {\n    /// Wrap the pin in a `Flex`.\n    ///\n    /// The pin remains disconnected. The initial output level is unspecified,\n    /// but can be changed before the pin is put into output mode.\n    #[inline]\n    pub fn new(pin: Peri<'d, impl Pin>) -> Self {\n        critical_section::with(|_| {\n            pin.regs().ctrl1.modify(|w| {\n                w.set_mux_ctrl(crate::pac::Function::GPIO);\n                w.set_out_sel(crate::pac::Sel::PIN);\n            })\n        });\n\n        let pin = pin.into();\n        pin.disable_interrupts();\n\n        Self { pin }\n    }\n\n    /// Set the pin's pull.\n    #[inline]\n    pub fn set_pull(&mut self, pull: Pull) {\n        critical_section::with(|_| {\n            self.pin.regs().ctrl1.modify(|w| w.set_pu_pd(pull.into()));\n        });\n    }\n\n    /// Set the pin's drive stength\n    #[inline]\n    pub fn set_drive_strength(&mut self, strength: Drive) {\n        critical_section::with(|_| {\n            self.pin.regs().ctrl2.modify(|w| w.set_driv_stren(strength.into()));\n        });\n    }\n\n    /// Set the pin's slew rate.\n    #[inline]\n    pub fn set_slew_rate(&mut self, slew_rate: SlewRate) {\n        critical_section::with(|_| {\n            self.pin.regs().ctrl2.modify(|w| w.set_slew_ctrl(slew_rate.into()));\n        });\n    }\n\n    /// Put the pin into input mode.\n    ///\n    /// The pull setting is left unchanged.\n    #[inline]\n    pub fn set_as_input(&mut self) {\n        critical_section::with(|_| {\n            self.pin.regs().ctrl1.modify(|w| {\n                w.set_dir(crate::pac::Dir::INPUT);\n                w.set_inp_dis(false);\n            })\n        });\n    }\n\n    /// Put the pin into output mode.\n    ///\n    /// The pin level will be whatever was set before (or low by default). If you want it to begin\n    /// at a specific level, call `set_high`/`set_low` on the pin first.\n    #[inline]\n    pub fn set_as_output(&mut self) {\n        critical_section::with(|_| self.pin.regs().ctrl1.modify(|w| w.set_dir(crate::pac::Dir::OUTPUT)));\n    }\n\n    /// Put the pin into output open drain mode.\n    ///\n    /// The pin level will be whatever was set before (or low by\n    /// default). If you want it to begin at a specific level, call\n    /// `set_high`/`set_low` on the pin first.\n    #[inline]\n    pub fn set_as_output_open_drain(&mut self) {\n        critical_section::with(|_| {\n            self.pin.regs().ctrl1.modify(|w| {\n                w.set_dir(crate::pac::Dir::OUTPUT);\n                w.set_out_buff_type(crate::pac::BufferType::OPEN_DRAIN);\n            })\n        });\n    }\n\n    /// Set as output pin.\n    #[inline]\n    fn is_set_as_output(&self) -> bool {\n        self.pin.regs().ctrl1.read().dir() == crate::pac::Dir::OUTPUT\n    }\n\n    /// Get whether the pin input level is high.\n    #[inline]\n    pub fn is_high(&self) -> bool {\n        self.pin.regs().ctrl1.read().inp()\n    }\n\n    /// Get whether the pin input level is low.\n    #[inline]\n    pub fn is_low(&self) -> bool {\n        !self.is_high()\n    }\n\n    /// Returns current pin level\n    #[inline]\n    pub fn get_level(&self) -> Level {\n        self.is_high().into()\n    }\n\n    /// Set the output as high.\n    #[inline]\n    pub fn set_high(&mut self) {\n        critical_section::with(|_| {\n            self.pin.regs().ctrl1.modify(|w| w.set_alt_data(true));\n        });\n    }\n\n    /// Set the output as low.\n    #[inline]\n    pub fn set_low(&mut self) {\n        critical_section::with(|_| {\n            self.pin.regs().ctrl1.modify(|w| w.set_alt_data(false));\n        });\n    }\n\n    /// Set the output level.\n    #[inline]\n    pub fn set_level(&mut self, level: Level) {\n        match level {\n            Level::Low => self.set_low(),\n            Level::High => self.set_high(),\n        }\n    }\n\n    /// Is the output level high?\n    #[inline]\n    pub fn is_set_high(&self) -> bool {\n        self.pin.regs().ctrl1.read().alt_data()\n    }\n\n    /// Is the output level low?\n    #[inline]\n    pub fn is_set_low(&self) -> bool {\n        !self.is_set_high()\n    }\n\n    /// What level output is set to\n    #[inline]\n    pub fn get_output_level(&self) -> Level {\n        self.is_set_high().into()\n    }\n\n    /// Toggle the output pin\n    #[inline]\n    pub fn toggle(&mut self) {\n        critical_section::with(|_| {\n            let val = self.pin.regs().ctrl1.read().alt_data();\n            self.pin.regs().ctrl1.modify(|w| w.set_alt_data(!val));\n        });\n    }\n\n    /// Wait until the pin is high. If it is already high, return immediately.\n    #[inline]\n    pub async fn wait_for_high(&mut self) {\n        InputFuture::new(self.pin.reborrow(), InterruptTrigger::LevelHigh).await;\n    }\n\n    /// Wait until the pin is low. If it is already low, return immediately.\n    #[inline]\n    pub async fn wait_for_low(&mut self) {\n        InputFuture::new(self.pin.reborrow(), InterruptTrigger::LevelLow).await;\n    }\n\n    /// Wait for the pin to undergo a transition from low to high.\n    #[inline]\n    pub async fn wait_for_rising_edge(&mut self) {\n        InputFuture::new(self.pin.reborrow(), InterruptTrigger::EdgeHigh).await;\n    }\n\n    /// Wait for the pin to undergo a transition from high to low.\n    #[inline]\n    pub async fn wait_for_falling_edge(&mut self) {\n        InputFuture::new(self.pin.reborrow(), InterruptTrigger::EdgeLow).await;\n    }\n\n    /// Wait for the pin to undergo any transition, i.e low to high OR high to low.\n    #[inline]\n    pub async fn wait_for_any_edge(&mut self) {\n        InputFuture::new(self.pin.reborrow(), InterruptTrigger::AnyEdge).await;\n    }\n}\n\nimpl<'d> Drop for Flex<'d> {\n    #[inline]\n    fn drop(&mut self) {\n        todo!()\n    }\n}\n\npub(crate) trait SealedPin: Sized {\n    fn _pin(&self) -> u8;\n    fn _port(&self) -> u8;\n    fn regs(&self) -> Registers;\n    fn irq_id(&self) -> usize;\n    fn irq_bit(&self) -> usize;\n\n    fn waker(&self) -> &AtomicWaker {\n        match self.irq_id() {\n            0 => &GIRQ08_WAKERS[self.irq_bit()],\n            1 => &GIRQ09_WAKERS[self.irq_bit()],\n            2 => &GIRQ10_WAKERS[self.irq_bit()],\n            3 => &GIRQ11_WAKERS[self.irq_bit()],\n            4 => &GIRQ12_WAKERS[self.irq_bit()],\n            17 => &GIRQ26_WAKERS[self.irq_bit()],\n            _ => unreachable!(),\n        }\n    }\n}\n\n#[derive(Clone, Copy)]\nstruct InterruptRegs {\n    result: Reg<u32, R>,\n    clr: Reg<u32, RW>,\n}\n\n#[derive(Clone, Copy)]\npub(crate) struct Registers {\n    pub(crate) ctrl1: Reg<Ctrl1, RW>,\n    pub(crate) ctrl2: Reg<Ctrl2, RW>,\n    pub(crate) src: Reg<u32, RW>,\n    pub(crate) set: Reg<u32, RW>,\n    pub(crate) clr: Reg<u32, RW>,\n}\n\n/// Interface for a [Pin] that can be configured by an [Input] or [Output]\n/// driver, or converted to an [AnyPin].\n#[allow(private_bounds)]\npub trait Pin: PeripheralType + Into<AnyPin> + SealedPin + Sized + 'static {\n    /// Returns the pin number within a port\n    #[inline]\n    fn pin(&self) -> u8 {\n        self._pin()\n    }\n\n    #[inline]\n    fn port(&self) -> u8 {\n        self._port()\n    }\n}\n\n/// Type-erased GPIO pin\npub struct AnyPin {\n    pin: u8,\n    port: u8,\n    regs: Registers,\n    irq_id: usize,\n    irq_bit: usize,\n}\n\nimpl AnyPin {\n    fn enable_interrupts(&self, trigger: InterruptTrigger) {\n        critical_section::with(|_| {\n            self.regs().clr.write_value(1 << self.irq_bit);\n\n            self.regs().ctrl1.modify(|w| match trigger {\n                InterruptTrigger::LevelLow => {\n                    w.set_edge_en(false);\n                    w.set_intr_det(0);\n                }\n                InterruptTrigger::LevelHigh => {\n                    w.set_edge_en(false);\n                    w.set_intr_det(1);\n                }\n                InterruptTrigger::EdgeLow => {\n                    w.set_edge_en(true);\n                    w.set_intr_det(6);\n                }\n                InterruptTrigger::EdgeHigh => {\n                    w.set_edge_en(true);\n                    w.set_intr_det(5);\n                }\n                InterruptTrigger::AnyEdge => {\n                    w.set_edge_en(true);\n                    w.set_intr_det(7);\n                }\n            });\n\n            self.regs().src.write_value(1 << self.irq_bit);\n            self.regs().set.write_value(1 << self.irq_bit);\n        });\n    }\n\n    fn disable_interrupts(&self) {\n        critical_section::with(|_| {\n            self.regs().ctrl1.modify(|w| {\n                w.set_edge_en(false);\n                w.set_intr_det(4);\n            });\n\n            self.regs().clr.write_value(1 << self.irq_bit);\n        });\n    }\n}\n\nimpl_peripheral!(AnyPin);\n\nimpl Pin for AnyPin {}\nimpl SealedPin for AnyPin {\n    #[inline]\n    fn _pin(&self) -> u8 {\n        self.pin\n    }\n\n    #[inline]\n    fn _port(&self) -> u8 {\n        self.port\n    }\n\n    #[inline]\n    fn regs(&self) -> Registers {\n        self.regs\n    }\n\n    #[inline]\n    fn irq_id(&self) -> usize {\n        self.irq_id\n    }\n\n    #[inline]\n    fn irq_bit(&self) -> usize {\n        self.irq_bit\n    }\n}\n\nmacro_rules! impl_pin {\n    ($name:ident, $port:expr, $pin:expr, $irq_id:expr, $irq_bit:expr, $src:ident, $set:ident, $result:ident, $clr:ident) => {\n        impl Pin for peripherals::$name {}\n        impl SealedPin for peripherals::$name {\n            #[inline]\n            fn _pin(&self) -> u8 {\n                $pin\n            }\n\n            #[inline]\n            fn _port(&self) -> u8 {\n                $port\n            }\n\n            #[inline]\n            fn irq_id(&self) -> usize {\n                $irq_id\n            }\n\n            #[inline]\n            fn irq_bit(&self) -> usize {\n                $irq_bit\n            }\n\n            #[inline]\n            fn regs(&self) -> Registers {\n                let ptr = pac::GPIO.as_ptr();\n                let ctrl1 =\n                    unsafe { Reg::from_ptr(ptr.byte_add((($port / 10) << 8) + (($port % 10) << 5) + ($pin * 4)) as _) };\n                let ctrl2 = unsafe {\n                    Reg::from_ptr(ptr.byte_add(0x500 + (($port / 10) << 8) + (($port % 10) << 5) + ($pin * 4)) as _)\n                };\n\n                Registers {\n                    ctrl1,\n                    ctrl2,\n                    src: crate::pac::ECIA.$src(),\n                    set: crate::pac::ECIA.$set(),\n                    clr: crate::pac::ECIA.$clr(),\n                }\n            }\n        }\n\n        impl From<peripherals::$name> for AnyPin {\n            fn from(val: peripherals::$name) -> Self {\n                Self {\n                    pin: val._pin(),\n                    port: val._port(),\n                    regs: val.regs(),\n                    irq_bit: val.irq_bit(),\n                    irq_id: val.irq_id(),\n                }\n            }\n        }\n    };\n}\n\nimpl_pin!(GPIO0, 0, 0, 3, 0, src11, en_set11, result11, en_clr11);\nimpl_pin!(GPIO1, 0, 1, 3, 1, src11, en_set11, result11, en_clr11);\nimpl_pin!(GPIO2, 0, 2, 3, 2, src11, en_set11, result11, en_clr11);\nimpl_pin!(GPIO3, 0, 3, 3, 3, src11, en_set11, result11, en_clr11);\nimpl_pin!(GPIO4, 0, 4, 3, 4, src11, en_set11, result11, en_clr11);\nimpl_pin!(GPIO5, 0, 5, 3, 5, src11, en_set11, result11, en_clr11);\nimpl_pin!(GPIO6, 0, 6, 3, 6, src11, en_set11, result11, en_clr11);\nimpl_pin!(GPIO7, 0, 7, 3, 7, src11, en_set11, result11, en_clr11);\n\nimpl_pin!(GPIO10, 1, 0, 3, 8, src11, en_set11, result11, en_clr11);\nimpl_pin!(GPIO11, 1, 1, 3, 9, src11, en_set11, result11, en_clr11);\nimpl_pin!(GPIO12, 1, 2, 3, 10, src11, en_set11, result11, en_clr11);\nimpl_pin!(GPIO13, 1, 3, 3, 11, src11, en_set11, result11, en_clr11);\nimpl_pin!(GPIO14, 1, 4, 3, 12, src11, en_set11, result11, en_clr11);\nimpl_pin!(GPIO15, 1, 5, 3, 13, src11, en_set11, result11, en_clr11);\nimpl_pin!(GPIO16, 1, 6, 3, 14, src11, en_set11, result11, en_clr11);\nimpl_pin!(GPIO17, 1, 7, 3, 15, src11, en_set11, result11, en_clr11);\n\nimpl_pin!(GPIO20, 2, 0, 3, 16, src11, en_set11, result11, en_clr11);\nimpl_pin!(GPIO21, 2, 1, 3, 17, src11, en_set11, result11, en_clr11);\nimpl_pin!(GPIO22, 2, 2, 3, 18, src11, en_set11, result11, en_clr11);\nimpl_pin!(GPIO23, 2, 3, 3, 19, src11, en_set11, result11, en_clr11);\nimpl_pin!(GPIO24, 2, 4, 3, 20, src11, en_set11, result11, en_clr11);\nimpl_pin!(GPIO25, 2, 5, 3, 21, src11, en_set11, result11, en_clr11);\nimpl_pin!(GPIO26, 2, 6, 3, 22, src11, en_set11, result11, en_clr11);\nimpl_pin!(GPIO27, 2, 7, 3, 23, src11, en_set11, result11, en_clr11);\n\nimpl_pin!(GPIO30, 3, 0, 3, 24, src11, en_set11, result11, en_clr11);\nimpl_pin!(GPIO31, 3, 1, 3, 25, src11, en_set11, result11, en_clr11);\nimpl_pin!(GPIO32, 3, 2, 3, 26, src11, en_set11, result11, en_clr11);\nimpl_pin!(GPIO33, 3, 3, 3, 27, src11, en_set11, result11, en_clr11);\nimpl_pin!(GPIO34, 3, 4, 3, 28, src11, en_set11, result11, en_clr11);\nimpl_pin!(GPIO35, 3, 5, 3, 29, src11, en_set11, result11, en_clr11);\nimpl_pin!(GPIO36, 3, 6, 3, 30, src11, en_set11, result11, en_clr11);\n\nimpl_pin!(GPIO40, 4, 0, 2, 0, src10, en_set10, result10, en_clr10);\nimpl_pin!(GPIO41, 4, 1, 2, 1, src10, en_set10, result10, en_clr10);\nimpl_pin!(GPIO42, 4, 2, 2, 2, src10, en_set10, result10, en_clr10);\nimpl_pin!(GPIO43, 4, 3, 2, 3, src10, en_set10, result10, en_clr10);\nimpl_pin!(GPIO44, 4, 4, 2, 4, src10, en_set10, result10, en_clr10);\nimpl_pin!(GPIO45, 4, 5, 2, 5, src10, en_set10, result10, en_clr10);\nimpl_pin!(GPIO46, 4, 6, 2, 6, src10, en_set10, result10, en_clr10);\nimpl_pin!(GPIO47, 4, 7, 2, 7, src10, en_set10, result10, en_clr10);\n\nimpl_pin!(GPIO50, 5, 0, 2, 8, src10, en_set10, result10, en_clr10);\nimpl_pin!(GPIO51, 5, 1, 2, 9, src10, en_set10, result10, en_clr10);\nimpl_pin!(GPIO52, 5, 2, 2, 10, src10, en_set10, result10, en_clr10);\nimpl_pin!(GPIO53, 5, 3, 2, 11, src10, en_set10, result10, en_clr10);\nimpl_pin!(GPIO54, 5, 4, 2, 12, src10, en_set10, result10, en_clr10);\nimpl_pin!(GPIO55, 5, 5, 2, 13, src10, en_set10, result10, en_clr10);\nimpl_pin!(GPIO56, 5, 6, 2, 14, src10, en_set10, result10, en_clr10);\nimpl_pin!(GPIO57, 5, 7, 2, 15, src10, en_set10, result10, en_clr10);\n\nimpl_pin!(GPIO60, 6, 0, 2, 16, src10, en_set10, result10, en_clr10);\nimpl_pin!(GPIO61, 6, 1, 2, 17, src10, en_set10, result10, en_clr10);\nimpl_pin!(GPIO62, 6, 2, 2, 18, src10, en_set10, result10, en_clr10);\nimpl_pin!(GPIO63, 6, 3, 2, 19, src10, en_set10, result10, en_clr10);\nimpl_pin!(GPIO64, 6, 4, 2, 20, src10, en_set10, result10, en_clr10);\nimpl_pin!(GPIO65, 6, 5, 2, 21, src10, en_set10, result10, en_clr10);\nimpl_pin!(GPIO66, 6, 6, 2, 22, src10, en_set10, result10, en_clr10);\nimpl_pin!(GPIO67, 6, 7, 2, 23, src10, en_set10, result10, en_clr10);\n\nimpl_pin!(GPIO70, 7, 0, 2, 24, src10, en_set10, result10, en_clr10);\nimpl_pin!(GPIO71, 7, 1, 2, 25, src10, en_set10, result10, en_clr10);\nimpl_pin!(GPIO72, 7, 2, 2, 26, src10, en_set10, result10, en_clr10);\nimpl_pin!(GPIO73, 7, 3, 2, 27, src10, en_set10, result10, en_clr10);\nimpl_pin!(GPIO74, 7, 4, 2, 28, src10, en_set10, result10, en_clr10);\nimpl_pin!(GPIO75, 7, 5, 2, 29, src10, en_set10, result10, en_clr10);\nimpl_pin!(GPIO76, 7, 6, 2, 30, src10, en_set10, result10, en_clr10);\n\nimpl_pin!(GPIO100, 10, 0, 1, 0, src9, en_set9, result9, en_clr9);\nimpl_pin!(GPIO101, 10, 1, 1, 1, src9, en_set9, result9, en_clr9);\nimpl_pin!(GPIO102, 10, 2, 1, 2, src9, en_set9, result9, en_clr9);\nimpl_pin!(GPIO103, 10, 3, 1, 3, src9, en_set9, result9, en_clr9);\nimpl_pin!(GPIO104, 10, 4, 1, 4, src9, en_set9, result9, en_clr9);\nimpl_pin!(GPIO105, 10, 5, 1, 5, src9, en_set9, result9, en_clr9);\nimpl_pin!(GPIO106, 10, 6, 1, 6, src9, en_set9, result9, en_clr9);\nimpl_pin!(GPIO107, 10, 7, 1, 7, src9, en_set9, result9, en_clr9);\n\nimpl_pin!(GPIO112, 11, 2, 1, 10, src9, en_set9, result9, en_clr9);\nimpl_pin!(GPIO113, 11, 3, 1, 11, src9, en_set9, result9, en_clr9);\nimpl_pin!(GPIO114, 11, 4, 1, 12, src9, en_set9, result9, en_clr9);\nimpl_pin!(GPIO115, 11, 5, 1, 13, src9, en_set9, result9, en_clr9);\nimpl_pin!(GPIO116, 11, 6, 1, 14, src9, en_set9, result9, en_clr9);\nimpl_pin!(GPIO117, 11, 7, 1, 15, src9, en_set9, result9, en_clr9);\n\nimpl_pin!(GPIO120, 12, 0, 1, 16, src9, en_set9, result9, en_clr9);\nimpl_pin!(GPIO121, 12, 1, 1, 17, src9, en_set9, result9, en_clr9);\nimpl_pin!(GPIO122, 12, 2, 1, 18, src9, en_set9, result9, en_clr9);\nimpl_pin!(GPIO123, 12, 3, 1, 19, src9, en_set9, result9, en_clr9);\nimpl_pin!(GPIO124, 12, 4, 1, 20, src9, en_set9, result9, en_clr9);\nimpl_pin!(GPIO125, 12, 5, 1, 21, src9, en_set9, result9, en_clr9);\nimpl_pin!(GPIO126, 12, 6, 1, 22, src9, en_set9, result9, en_clr9);\nimpl_pin!(GPIO127, 12, 7, 1, 23, src9, en_set9, result9, en_clr9);\n\nimpl_pin!(GPIO130, 13, 0, 1, 24, src9, en_set9, result9, en_clr9);\nimpl_pin!(GPIO131, 13, 1, 1, 25, src9, en_set9, result9, en_clr9);\nimpl_pin!(GPIO132, 13, 2, 1, 26, src9, en_set9, result9, en_clr9);\nimpl_pin!(GPIO133, 13, 3, 1, 27, src9, en_set9, result9, en_clr9);\nimpl_pin!(GPIO134, 13, 4, 1, 28, src9, en_set9, result9, en_clr9);\nimpl_pin!(GPIO135, 13, 5, 1, 29, src9, en_set9, result9, en_clr9);\n\nimpl_pin!(GPIO140, 14, 0, 0, 0, src8, en_set8, result8, en_clr8);\nimpl_pin!(GPIO141, 14, 1, 0, 1, src8, en_set8, result8, en_clr8);\nimpl_pin!(GPIO142, 14, 2, 0, 2, src8, en_set8, result8, en_clr8);\nimpl_pin!(GPIO143, 14, 3, 0, 3, src8, en_set8, result8, en_clr8);\nimpl_pin!(GPIO144, 14, 4, 0, 4, src8, en_set8, result8, en_clr8);\nimpl_pin!(GPIO145, 14, 5, 0, 5, src8, en_set8, result8, en_clr8);\nimpl_pin!(GPIO146, 14, 6, 0, 6, src8, en_set8, result8, en_clr8);\nimpl_pin!(GPIO147, 14, 7, 0, 7, src8, en_set8, result8, en_clr8);\n\nimpl_pin!(GPIO150, 15, 0, 0, 8, src8, en_set8, result8, en_clr8);\nimpl_pin!(GPIO151, 15, 1, 0, 9, src8, en_set8, result8, en_clr8);\nimpl_pin!(GPIO152, 15, 2, 0, 10, src8, en_set8, result8, en_clr8);\nimpl_pin!(GPIO153, 15, 3, 0, 11, src8, en_set8, result8, en_clr8);\nimpl_pin!(GPIO154, 15, 4, 0, 12, src8, en_set8, result8, en_clr8);\nimpl_pin!(GPIO155, 15, 5, 0, 13, src8, en_set8, result8, en_clr8);\nimpl_pin!(GPIO156, 15, 6, 0, 14, src8, en_set8, result8, en_clr8);\nimpl_pin!(GPIO157, 15, 7, 0, 15, src8, en_set8, result8, en_clr8);\n\nimpl_pin!(GPIO160, 16, 0, 0, 16, src8, en_set8, result8, en_clr8);\nimpl_pin!(GPIO161, 16, 1, 0, 17, src8, en_set8, result8, en_clr8);\nimpl_pin!(GPIO162, 16, 2, 0, 18, src8, en_set8, result8, en_clr8);\nimpl_pin!(GPIO165, 16, 5, 0, 21, src8, en_set8, result8, en_clr8);\nimpl_pin!(GPIO166, 16, 6, 0, 22, src8, en_set8, result8, en_clr8);\n\nimpl_pin!(GPIO170, 17, 0, 0, 24, src8, en_set8, result8, en_clr8);\nimpl_pin!(GPIO171, 17, 1, 0, 25, src8, en_set8, result8, en_clr8);\nimpl_pin!(GPIO172, 17, 2, 0, 26, src8, en_set8, result8, en_clr8);\nimpl_pin!(GPIO173, 17, 3, 0, 27, src8, en_set8, result8, en_clr8);\nimpl_pin!(GPIO174, 17, 4, 0, 28, src8, en_set8, result8, en_clr8);\nimpl_pin!(GPIO175, 17, 5, 0, 29, src8, en_set8, result8, en_clr8);\n\nimpl_pin!(GPIO200, 20, 0, 4, 0, src12, en_set12, result12, en_clr12);\nimpl_pin!(GPIO201, 20, 1, 4, 1, src12, en_set12, result12, en_clr12);\nimpl_pin!(GPIO202, 20, 2, 4, 2, src12, en_set12, result12, en_clr12);\nimpl_pin!(GPIO203, 20, 3, 4, 3, src12, en_set12, result12, en_clr12);\nimpl_pin!(GPIO204, 20, 4, 4, 4, src12, en_set12, result12, en_clr12);\nimpl_pin!(GPIO205, 20, 5, 4, 5, src12, en_set12, result12, en_clr12);\nimpl_pin!(GPIO206, 20, 6, 4, 6, src12, en_set12, result12, en_clr12);\nimpl_pin!(GPIO207, 20, 7, 4, 7, src12, en_set12, result12, en_clr12);\n\nimpl_pin!(GPIO210, 21, 0, 4, 8, src12, en_set12, result12, en_clr12);\nimpl_pin!(GPIO211, 21, 1, 4, 9, src12, en_set12, result12, en_clr12);\nimpl_pin!(GPIO212, 21, 2, 4, 10, src12, en_set12, result12, en_clr12);\nimpl_pin!(GPIO213, 21, 3, 4, 11, src12, en_set12, result12, en_clr12);\nimpl_pin!(GPIO214, 21, 4, 4, 12, src12, en_set12, result12, en_clr12);\nimpl_pin!(GPIO215, 21, 5, 4, 13, src12, en_set12, result12, en_clr12);\nimpl_pin!(GPIO216, 21, 6, 4, 14, src12, en_set12, result12, en_clr12);\nimpl_pin!(GPIO217, 21, 7, 4, 15, src12, en_set12, result12, en_clr12);\n\nimpl_pin!(GPIO221, 22, 1, 4, 17, src12, en_set12, result12, en_clr12);\nimpl_pin!(GPIO222, 22, 2, 4, 18, src12, en_set12, result12, en_clr12);\nimpl_pin!(GPIO223, 22, 3, 4, 19, src12, en_set12, result12, en_clr12);\nimpl_pin!(GPIO224, 22, 4, 4, 20, src12, en_set12, result12, en_clr12);\nimpl_pin!(GPIO225, 22, 5, 4, 21, src12, en_set12, result12, en_clr12);\nimpl_pin!(GPIO226, 22, 6, 4, 22, src12, en_set12, result12, en_clr12);\nimpl_pin!(GPIO227, 22, 7, 4, 23, src12, en_set12, result12, en_clr12);\n\nimpl_pin!(GPIO230, 23, 0, 4, 23, src11, en_set11, result11, en_clr11);\nimpl_pin!(GPIO231, 23, 0, 4, 23, src11, en_set11, result11, en_clr11);\n\nimpl_pin!(GPIO240, 24, 0, 17, 0, src26, en_set26, result26, en_clr26);\nimpl_pin!(GPIO241, 24, 1, 17, 1, src26, en_set26, result26, en_clr26);\nimpl_pin!(GPIO242, 24, 2, 17, 2, src26, en_set26, result26, en_clr26);\nimpl_pin!(GPIO243, 24, 3, 17, 3, src26, en_set26, result26, en_clr26);\nimpl_pin!(GPIO244, 24, 4, 17, 4, src26, en_set26, result26, en_clr26);\nimpl_pin!(GPIO245, 24, 5, 17, 5, src26, en_set26, result26, en_clr26);\nimpl_pin!(GPIO246, 24, 6, 17, 6, src26, en_set26, result26, en_clr26);\nimpl_pin!(GPIO247, 24, 7, 17, 7, src26, en_set26, result26, en_clr26);\n\nimpl_pin!(GPIO254, 25, 4, 17, 12, src26, en_set26, result26, en_clr26);\nimpl_pin!(GPIO255, 25, 5, 17, 13, src26, en_set26, result26, en_clr26);\n"
  },
  {
    "path": "embassy-microchip/src/i2c.rs",
    "content": "//! I2C driver.\n\nuse core::future;\nuse core::marker::PhantomData;\nuse core::task::Poll;\n\nuse embassy_hal_internal::{Peri, PeripheralType};\nuse embassy_sync::waitqueue::AtomicWaker;\nuse pac::smb0;\n\nuse crate::gpio::{AnyPin, SealedPin};\nuse crate::interrupt::typelevel::{Binding, Interrupt};\nuse crate::{interrupt, pac, peripherals};\n\n/// I2c error abort reason\n#[derive(Debug, PartialEq, Eq, Clone, Copy)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub enum AbortReason {\n    /// A bus operation was not acknowledged.\n    NoAcknowledge,\n\n    /// Lost arbitration\n    ArbitrationLoss,\n\n    /// Other reason\n    Other,\n}\n\n/// I2C error bus timeout reason\n#[derive(Debug, PartialEq, Eq, Clone, Copy)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub enum BusTimeoutReason {\n    /// Clock high, data high timeout\n    ClockHighDataHigh,\n\n    /// Clock high, data low timeout\n    ClockHighDataLow,\n\n    /// Slave cumulative timeout\n    SlaveCumulativeTimeout,\n\n    /// Master cumulative timeout\n    MasterCumulativeTimeout,\n\n    /// Device timeout\n    DeviceTimeout,\n}\n\n/// I2C error\n#[derive(Debug, PartialEq, Eq, Clone, Copy)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub enum Error {\n    /// I2C abort with error\n    Abort(AbortReason),\n\n    /// Bus timeout\n    BusTimeout(BusTimeoutReason),\n\n    /// User passed in a read buffer with 0 length\n    InvalidReadBufferLength,\n\n    /// User passed in a write buffer with 0 length\n    InvalidWriteBufferLength,\n\n    /// Target I2C address is out of range\n    AddressOutOfRange(u8),\n\n    /// Target I2C address is reserved\n    AddressReserved(u8),\n}\n\n/// I2C config error\n#[derive(Debug, PartialEq, Eq, Clone, Copy)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub enum ConfigError {\n    /// Max I2C frequency is 1MHz\n    FrequencyTooHigh,\n\n    /// The baud rate clock is too slow to support the requested\n    /// frequency\n    ClockTooSlow,\n\n    /// The baud rate clock is too fast to support the requested\n    /// frequency\n    ClockTooFast,\n}\n\n/// I2C bus speeds\n#[derive(Debug, PartialEq, Eq, Clone, Copy)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub enum BusSpeed {\n    /// 100kHz\n    Standard,\n\n    /// 400kHz,\n    Fast,\n\n    /// 1MHz,\n    FastPlus,\n}\n\n/// I2C config\n#[non_exhaustive]\n#[derive(Copy, Clone)]\npub struct Config {\n    /// Bus speed.\n    pub speed: BusSpeed,\n\n    /// Own address 1 in 7-bit format.\n    ///\n    /// The internal master should not attempt transactions to a slave\n    /// with the same address as `addr1`. This represents an illegal\n    /// operation.\n    ///\n    /// Passing `None` results in the value 0 being written to the Own\n    /// Address register and General Call Match to be disabled.\n    pub addr1: Option<u8>,\n\n    /// Own address 2 in 7-bit format.\n    ///\n    /// The internal master should not attempt transactions to a slave\n    /// with the same address as `addr2`. This represents an illegal\n    /// operation.\n    ///\n    /// Passing `None` results in the value 0 being written to the Own\n    /// Address register and General Call Match to be disabled.\n    pub addr2: Option<u8>,\n}\n\nimpl Default for Config {\n    fn default() -> Self {\n        Self {\n            speed: BusSpeed::Standard,\n            addr1: None,\n            addr2: None,\n        }\n    }\n}\n\nmacro_rules! write_register_with_delay {\n    ($reg_path:expr, |$w:ident| $body:block) => {{\n        $reg_path.write(|$w| $body);\n        // TODO - empirically determined delay\n        cortex_m::asm::delay(20_000);\n    }};\n}\n\n/// I2C driver.\npub struct I2c<'d, T: Instance, M: Mode> {\n    _peri: Peri<'d, T>,\n    _sda: Peri<'d, AnyPin>,\n    _scl: Peri<'d, AnyPin>,\n    config: Config,\n    port: u8,\n    phantom: PhantomData<M>,\n}\n\nimpl<'d, T: Instance> I2c<'d, T, Blocking> {\n    /// Create a new driver instance in blocking mode.\n    pub fn new_blocking<SCL: SclPin, SDA: SdaPin>(\n        _peri: Peri<'d, T>,\n        _scl: Peri<'d, SCL>,\n        _sda: Peri<'d, SDA>,\n        config: Config,\n    ) -> Self\n    where\n        (T, SCL, SDA): ValidI2cConfig,\n    {\n        _scl.setup();\n        _sda.setup();\n\n        let port = <(T, SCL, SDA) as SealedValidI2cConfig>::port();\n        Self::new_inner(_peri, _scl.into(), _sda.into(), config, port)\n    }\n}\n\nimpl<'d, T: Instance> I2c<'d, T, Async> {\n    /// Create a new driver instance in async mode.\n    pub fn new_async<SCL: SclPin, SDA: SdaPin>(\n        _peri: Peri<'d, T>,\n        _scl: Peri<'d, SCL>,\n        _sda: Peri<'d, SDA>,\n        _irq: impl Binding<T::Interrupt, InterruptHandler<T>>,\n        config: Config,\n    ) -> Self\n    where\n        (T, SCL, SDA): ValidI2cConfig,\n    {\n        // mask all interrupts\n        pac::ECIA.src13().write_value(1 << T::irq_bit());\n        pac::ECIA.en_clr13().write_value(1 << T::irq_bit());\n\n        _scl.setup();\n        _sda.setup();\n\n        let port = <(T, SCL, SDA) as SealedValidI2cConfig>::port();\n        let i2c = Self::new_inner(_peri, _scl.into(), _sda.into(), config, port);\n\n        // unmask interrupt\n        pac::ECIA.en_set13().write_value(1 << T::irq_bit());\n\n        T::Interrupt::unpend();\n        unsafe { T::Interrupt::enable() };\n\n        i2c\n    }\n\n    /// Calls `f` to check if we are ready or not.\n    /// If not, `g` is called once(to eg enable the required interrupts).\n    /// The waker will always be registered prior to calling `f`.\n    async fn wait_on<F, U, G>(&mut self, mut f: F, mut g: G) -> U\n    where\n        F: FnMut(&mut Self) -> Poll<U>,\n        G: FnMut(&mut Self),\n    {\n        future::poll_fn(|cx| {\n            // Register prior to checking the condition\n            T::waker().register(cx.waker());\n            let r = f(self);\n\n            if r.is_pending() {\n                g(self);\n            }\n            r\n        })\n        .await\n    }\n\n    async fn wait_for_bus_free_async(&mut self) -> Result<(), Error> {\n        self.wait_on(\n            |_| {\n                let compl = T::regs().compl().read();\n                let sts = T::regs().rsts().read();\n\n                if sts.nbb() {\n                    T::regs().compl().write(|w| w.set_idle(true));\n                    Poll::Ready(Ok(()))\n                } else if compl.lab() {\n                    T::regs().compl().write(|w| w.set_lab(true));\n                    Poll::Ready(Err(Error::Abort(AbortReason::ArbitrationLoss)))\n                } else if compl.ber() && compl.dto() {\n                    T::regs().compl().write(|w| w.set_dto(true));\n                    Poll::Ready(Err(Error::BusTimeout(BusTimeoutReason::DeviceTimeout)))\n                } else if compl.ber() && compl.mcto() {\n                    T::regs().compl().write(|w| w.set_mcto(true));\n                    Poll::Ready(Err(Error::BusTimeout(BusTimeoutReason::MasterCumulativeTimeout)))\n                } else if compl.ber() && compl.chdl() {\n                    T::regs().compl().write(|w| w.set_chdl(true));\n                    Poll::Ready(Err(Error::BusTimeout(BusTimeoutReason::ClockHighDataLow)))\n                } else if compl.ber() && compl.chdh() {\n                    T::regs().compl().write(|w| w.set_chdh(true));\n                    Poll::Ready(Err(Error::BusTimeout(BusTimeoutReason::ClockHighDataHigh)))\n                } else if compl.ber() {\n                    T::regs().compl().write(|w| w.set_ber(true));\n                    Poll::Ready(Err(Error::Abort(AbortReason::Other)))\n                } else {\n                    Poll::Pending\n                }\n            },\n            |_| {\n                T::regs().cfg().modify(|w| w.set_enidi(true));\n            },\n        )\n        .await\n    }\n\n    async fn wait_for_completion_async(&mut self) -> Result<(), Error> {\n        self.wait_on(\n            |_| {\n                let compl = T::regs().compl().read();\n                let sts = T::regs().rsts().read();\n\n                if sts.pin() {\n                    Poll::Pending\n                } else if !sts.lrb_ad0() {\n                    Poll::Ready(Ok(()))\n                } else if compl.lab() {\n                    T::regs().compl().write(|w| w.set_lab(true));\n                    Poll::Ready(Err(Error::Abort(AbortReason::ArbitrationLoss)))\n                } else if compl.ber() && compl.dto() {\n                    T::regs().compl().write(|w| w.set_dto(true));\n                    Poll::Ready(Err(Error::BusTimeout(BusTimeoutReason::DeviceTimeout)))\n                } else if compl.ber() && compl.mcto() {\n                    T::regs().compl().write(|w| w.set_mcto(true));\n                    Poll::Ready(Err(Error::BusTimeout(BusTimeoutReason::MasterCumulativeTimeout)))\n                } else if compl.ber() && compl.chdl() {\n                    T::regs().compl().write(|w| w.set_chdl(true));\n                    Poll::Ready(Err(Error::BusTimeout(BusTimeoutReason::ClockHighDataLow)))\n                } else if compl.ber() && compl.chdh() {\n                    T::regs().compl().write(|w| w.set_chdh(true));\n                    Poll::Ready(Err(Error::BusTimeout(BusTimeoutReason::ClockHighDataHigh)))\n                } else if compl.ber() {\n                    T::regs().compl().write(|w| w.set_ber(true));\n                    Poll::Ready(Err(Error::Abort(AbortReason::Other)))\n                } else {\n                    Poll::Pending\n                }\n            },\n            |_| {\n                T::regs().cfg().modify(|w| {\n                    w.set_enidi(true);\n                    w.set_enmi(true);\n                    w.set_ensi(true);\n                });\n            },\n        )\n        .await\n    }\n\n    async fn start_async(&mut self, address: u8, rw: bool, repeated: bool) -> Result<(), Error> {\n        if address >= 0x80 {\n            return Err(Error::AddressOutOfRange(address));\n        }\n\n        let r = T::regs();\n        let own_addr = r.own_addr().read();\n        let (addr1, addr2) = (own_addr.addr1(), own_addr.addr2());\n\n        if address == addr1 || address == addr2 {\n            return Err(Error::AddressReserved(address));\n        }\n\n        if repeated {\n            write_register_with_delay!(T::regs().wctrl(), |w| {\n                w.set_eso(true);\n                w.set_sta(true);\n                w.set_ack(true);\n            });\n            T::regs().i2cdata().write_value(address << 1 | u8::from(rw));\n        } else {\n            if rw {\n                T::regs().i2cdata().write_value(address << 1 | 1);\n                self.wait_for_bus_free_async().await?;\n            } else {\n                self.wait_for_bus_free_async().await?;\n                T::regs().i2cdata().write_value(address << 1 | 0);\n            }\n\n            write_register_with_delay!(T::regs().wctrl(), |w| {\n                w.set_pin(true);\n                w.set_eso(true);\n                w.set_sta(true);\n                w.set_ack(true);\n            });\n\n            self.wait_for_completion_async().await?;\n        }\n\n        Ok(())\n    }\n\n    async fn read_byte_async(&mut self) -> Result<u8, Error> {\n        self.wait_for_completion_async().await?;\n        let byte = T::regs().i2cdata().read();\n        Ok(byte)\n    }\n\n    async fn write_byte_async(&mut self, byte: u8) -> Result<(), Error> {\n        self.wait_for_completion_async().await?;\n        T::regs().i2cdata().write_value(byte);\n        Ok(())\n    }\n\n    async fn read_async_internal(\n        &mut self,\n        address: u8,\n        read: &mut [u8],\n        repeated: bool,\n        send_stop: bool,\n    ) -> Result<(), Error> {\n        if read.is_empty() {\n            return Err(Error::InvalidReadBufferLength);\n        }\n\n        self.start_async(address, true, repeated).await?;\n\n        // First byte in the FIFO is the slave address. Ignore it.\n        let _ = self.read_byte_async().await?;\n\n        let last = read.len() - 1;\n        for (i, byte) in read.iter_mut().enumerate() {\n            if i == last {\n                write_register_with_delay!(T::regs().wctrl(), |w| {\n                    w.set_eso(true);\n                });\n            }\n\n            let b = self.read_byte_async().await?;\n            *byte = b;\n        }\n\n        if send_stop {\n            Self::stop();\n        }\n\n        Ok(())\n    }\n\n    async fn write_async_internal(&mut self, address: u8, write: &[u8], send_stop: bool) -> Result<(), Error> {\n        if write.is_empty() {\n            return Err(Error::InvalidWriteBufferLength);\n        }\n\n        self.start_async(address, false, false).await?;\n\n        for byte in write.iter() {\n            self.write_byte_async(*byte).await?;\n        }\n\n        if send_stop {\n            Self::stop();\n        }\n\n        Ok(())\n    }\n\n    /// Read from address into read asynchronously.\n    pub async fn read_async(&mut self, address: u8, read: &mut [u8]) -> Result<(), Error> {\n        self.read_async_internal(address, read, false, true).await\n    }\n\n    /// Write to address from write asynchronously.\n    pub async fn write_async(&mut self, address: u8, write: &[u8]) -> Result<(), Error> {\n        self.write_async_internal(address, write, true).await\n    }\n\n    /// Write to address from write and read from address into read asynchronously.\n    pub async fn write_read_async(&mut self, address: u8, write: &[u8], read: &mut [u8]) -> Result<(), Error> {\n        self.write_async_internal(address, write, false).await?;\n        self.read_async_internal(address, read, true, true).await\n    }\n}\n\n/// Interrupt handler.\npub struct InterruptHandler<T: Instance> {\n    _phantom: PhantomData<T>,\n}\n\nimpl<T: Instance> interrupt::typelevel::Handler<T::Interrupt> for InterruptHandler<T> {\n    unsafe fn on_interrupt() {\n        pac::ECIA.src13().write_value(1 << T::irq_bit());\n\n        T::regs().cfg().modify(|w| {\n            w.set_enmi(false);\n            w.set_enidi(false);\n            w.set_en_aas(false);\n        });\n\n        T::waker().wake();\n    }\n}\n\nimpl<'d, T: Instance + 'd, M: Mode> I2c<'d, T, M> {\n    fn new_inner(_peri: Peri<'d, T>, _scl: Peri<'d, AnyPin>, _sda: Peri<'d, AnyPin>, config: Config, port: u8) -> Self {\n        let mut i2c = Self {\n            _peri,\n            _scl,\n            _sda,\n            config,\n            port,\n            phantom: PhantomData,\n        };\n        i2c.reset_reconfigure();\n        i2c\n    }\n\n    fn reset_reconfigure(&mut self) {\n        let r = T::regs();\n\n        let addr1 = self.config.addr1.unwrap_or(0);\n        let addr2 = self.config.addr2.unwrap_or(0);\n\n        critical_section::with(|_| {\n            // Reset the controller first.\n            T::reset();\n\n            r.cfg().write(|w| {\n                w.set_flush_sxbuf(true);\n                w.set_flush_srbuf(true);\n                w.set_flush_mxbuf(true);\n                w.set_flush_mrbuf(true);\n            });\n\n            r.wctrl().write(|w| w.set_pin(true));\n            r.own_addr().write(|w| {\n                w.set_addr1(addr1);\n                w.set_addr2(addr2);\n            });\n\n            r.cfg().write(|w| {\n                if addr1 == 0 || addr2 == 0 {\n                    w.set_gc_dis(false);\n                }\n                w.set_port_sel(self.port);\n                w.set_fen(true);\n            });\n\n            match self.config.speed {\n                BusSpeed::Standard => {\n                    r.busclk().write(|w| {\n                        w.set_low_per(0x4f);\n                        w.set_high_per(0x4f);\n                    });\n                    r.datatm().write(|w| {\n                        w.set_data_hold(0x06);\n                        w.set_restart_setup(0x50);\n                        w.set_stop_setup(0x4d);\n                        w.set_first_start_hold(0x0c);\n                    });\n\n                    r.rshtm().write(|w| w.set_rshtm(0x4d));\n                    r.idlsc().write(|w| {\n                        w.set_fair_bus_idl_min(0x01ed);\n                        w.set_fair_idl_dly(0x01fc);\n                    });\n                    r.tmoutsc().write(|w| {\n                        w.set_clk_high_tim_out(0xc7);\n                        w.set_slv_cum_tim_out(0xc2);\n                        w.set_mast_cum_tim_out(0x9c);\n                        w.set_bus_idle_min(0x4b);\n                    });\n                }\n                BusSpeed::Fast => {\n                    r.busclk().write(|w| {\n                        w.set_low_per(0x17);\n                        w.set_high_per(0x0f);\n                    });\n                    r.datatm().write(|w| {\n                        w.set_data_hold(0x06);\n                        w.set_restart_setup(0x0a);\n                        w.set_stop_setup(0x0a);\n                        w.set_first_start_hold(0x04);\n                    });\n\n                    r.rshtm().write(|w| w.set_rshtm(0x0a));\n                    r.idlsc().write(|w| {\n                        w.set_fair_bus_idl_min(0x0050);\n                        w.set_fair_idl_dly(0x0100);\n                    });\n                    r.tmoutsc().write(|w| {\n                        w.set_clk_high_tim_out(0xc7);\n                        w.set_slv_cum_tim_out(0xc2);\n                        w.set_mast_cum_tim_out(0x9c);\n                        w.set_bus_idle_min(0x15);\n                    });\n                }\n                BusSpeed::FastPlus => {\n                    r.busclk().write(|w| {\n                        w.set_low_per(0x09);\n                        w.set_high_per(0x05);\n                    });\n                    r.datatm().write(|w| {\n                        w.set_data_hold(0x01);\n                        w.set_restart_setup(0x06);\n                        w.set_stop_setup(0x06);\n                        w.set_first_start_hold(0x04);\n                    });\n\n                    r.rshtm().write(|w| w.set_rshtm(0x06));\n                    r.idlsc().write(|w| {\n                        w.set_fair_bus_idl_min(0x0050);\n                        w.set_fair_idl_dly(0x0100);\n                    });\n                    r.tmoutsc().write(|w| {\n                        w.set_clk_high_tim_out(0xc7);\n                        w.set_slv_cum_tim_out(0xc2);\n                        w.set_mast_cum_tim_out(0x9c);\n                        w.set_bus_idle_min(0x08);\n                    });\n                }\n            }\n\n            r.wctrl().write(|w| {\n                w.set_pin(true);\n                w.set_eso(true);\n                w.set_ack(true);\n            });\n\n            r.cfg().modify(|w| w.set_en(true));\n        });\n\n        while !r.rsts().read().nbb() {}\n\n        // 6. Delay.\n        //\n        // Documentation states: Wait a time equal to the longest i2c\n        // message to synchronize the NBB bit in multi-master systems\n        // only.\n        //\n        // We're assuming that we're not in a multi-master scenario,\n        // therefore skipping the delay.\n    }\n\n    fn wait_for_bus_free() {\n        while !T::regs().rsts().read().nbb() {}\n    }\n\n    fn start(address: u8, rw: bool, repeated: bool) -> Result<(), Error> {\n        if address >= 0x80 {\n            return Err(Error::AddressOutOfRange(address));\n        }\n\n        let r = T::regs();\n        let own_addr = r.own_addr().read();\n        let (addr1, addr2) = (own_addr.addr1(), own_addr.addr2());\n\n        if address == addr1 || address == addr2 {\n            return Err(Error::AddressReserved(address));\n        }\n\n        if repeated {\n            write_register_with_delay!(T::regs().wctrl(), |w| {\n                w.set_eso(true);\n                w.set_sta(true);\n                w.set_ack(true);\n            });\n            T::regs().i2cdata().write_value(address << 1 | u8::from(rw));\n        } else {\n            if rw {\n                T::regs().i2cdata().write_value(address << 1 | 1);\n                Self::wait_for_bus_free();\n            } else {\n                Self::wait_for_bus_free();\n                T::regs().i2cdata().write_value(address << 1 | 0);\n            }\n\n            write_register_with_delay!(T::regs().wctrl(), |w| {\n                w.set_pin(true);\n                w.set_eso(true);\n                w.set_sta(true);\n                w.set_ack(true);\n            });\n        }\n\n        Ok(())\n    }\n\n    fn stop() {\n        write_register_with_delay!(T::regs().wctrl(), |w| {\n            w.set_pin(true);\n            w.set_eso(true);\n            w.set_sto(true);\n            w.set_sta(false);\n            w.set_ack(true);\n        });\n    }\n\n    fn check_status() -> Result<(), Error> {\n        while T::regs().rsts().read().pin() {}\n\n        let status = T::regs().rsts().read();\n\n        if status.lrb_ad0() {\n            Self::stop();\n            Err(Error::Abort(AbortReason::NoAcknowledge))\n        } else if status.lab() {\n            Err(Error::Abort(AbortReason::ArbitrationLoss))\n        } else if status.ber() {\n            let completion = T::regs().compl().read();\n\n            if completion.dto() {\n                Err(Error::BusTimeout(BusTimeoutReason::DeviceTimeout))\n            } else if completion.mcto() {\n                Err(Error::BusTimeout(BusTimeoutReason::MasterCumulativeTimeout))\n            } else if completion.chdl() {\n                Err(Error::BusTimeout(BusTimeoutReason::ClockHighDataLow))\n            } else if completion.chdh() {\n                Err(Error::BusTimeout(BusTimeoutReason::ClockHighDataHigh))\n            } else {\n                Err(Error::Abort(AbortReason::Other))\n            }\n        } else {\n            Ok(())\n        }\n    }\n\n    fn read_byte(last: bool) -> Result<u8, Error> {\n        if !last {\n            Self::check_status()?;\n        } else {\n            write_register_with_delay!(T::regs().wctrl(), |w| {\n                w.set_eso(true);\n            });\n        }\n\n        let byte = T::regs().i2cdata().read();\n\n        if last {\n            Self::check_status()?;\n        }\n\n        Ok(byte)\n    }\n\n    fn write_byte(byte: u8) -> Result<(), Error> {\n        Self::check_status()?;\n        T::regs().i2cdata().write_value(byte);\n        Ok(())\n    }\n\n    fn read_blocking_internal(\n        &mut self,\n        address: u8,\n        read: &mut [u8],\n        repeated: bool,\n        send_stop: bool,\n    ) -> Result<(), Error> {\n        if read.is_empty() {\n            return Err(Error::InvalidReadBufferLength);\n        }\n\n        Self::start(address, true, repeated)?;\n\n        // First byte in the FIFO is the slave address. Ignore it.\n        let _ = Self::read_byte(false)?;\n\n        let last = read.len() - 1;\n        for (i, byte) in read.iter_mut().enumerate() {\n            *byte = Self::read_byte(i == last)?;\n        }\n\n        if send_stop {\n            Self::stop();\n        }\n\n        Ok(())\n    }\n\n    fn write_blocking_internal(&mut self, address: u8, write: &[u8], send_stop: bool) -> Result<(), Error> {\n        if write.is_empty() {\n            return Err(Error::InvalidWriteBufferLength);\n        }\n\n        Self::start(address, false, false)?;\n\n        for byte in write.iter() {\n            Self::write_byte(*byte)?;\n        }\n\n        if send_stop {\n            Self::stop();\n        }\n\n        Ok(())\n    }\n\n    /// Read from address into read blocking caller until done.\n    pub fn blocking_read(&mut self, address: u8, read: &mut [u8]) -> Result<(), Error> {\n        // TODO - empirically determined delay\n        cortex_m::asm::delay(20_000);\n        let retval = self.read_blocking_internal(address, read, false, true);\n        retval\n    }\n\n    /// Write to address from write blocking caller until done.\n    pub fn blocking_write(&mut self, address: u8, write: &[u8]) -> Result<(), Error> {\n        // TODO - empirically determined delay\n        cortex_m::asm::delay(20_000);\n        let retval = self.write_blocking_internal(address, write, true);\n        retval\n    }\n\n    /// Write to address from write and read from address into read blocking caller until done.\n    pub fn blocking_write_read(&mut self, address: u8, write: &[u8], read: &mut [u8]) -> Result<(), Error> {\n        // TODO - empirically determined delay\n        cortex_m::asm::delay(20_000);\n        self.write_blocking_internal(address, write, false)?;\n        // TODO - empirically determined delay\n        cortex_m::asm::delay(20_000);\n        self.read_blocking_internal(address, read, true, true)\n    }\n}\n\nimpl<'d, T: Instance, M: Mode> embedded_hal_02::blocking::i2c::Read for I2c<'d, T, M> {\n    type Error = Error;\n\n    fn read(&mut self, address: u8, buffer: &mut [u8]) -> Result<(), Self::Error> {\n        self.blocking_read(address, buffer)\n    }\n}\n\nimpl<'d, T: Instance, M: Mode> embedded_hal_02::blocking::i2c::Write for I2c<'d, T, M> {\n    type Error = Error;\n\n    fn write(&mut self, address: u8, bytes: &[u8]) -> Result<(), Self::Error> {\n        self.blocking_write(address, bytes)\n    }\n}\n\nimpl<'d, T: Instance, M: Mode> embedded_hal_02::blocking::i2c::WriteRead for I2c<'d, T, M> {\n    type Error = Error;\n\n    fn write_read(&mut self, address: u8, bytes: &[u8], buffer: &mut [u8]) -> Result<(), Self::Error> {\n        self.blocking_write_read(address, bytes, buffer)\n    }\n}\n\nimpl<'d, T: Instance, M: Mode> embedded_hal_02::blocking::i2c::Transactional for I2c<'d, T, M> {\n    type Error = Error;\n\n    fn exec(\n        &mut self,\n        address: u8,\n        operations: &mut [embedded_hal_02::blocking::i2c::Operation<'_>],\n    ) -> Result<(), Self::Error> {\n        for i in 0..operations.len() {\n            let last = i == operations.len() - 1;\n            match &mut operations[i] {\n                embedded_hal_02::blocking::i2c::Operation::Read(buf) => {\n                    self.read_blocking_internal(address, buf, false, last)?\n                }\n                embedded_hal_02::blocking::i2c::Operation::Write(buf) => {\n                    self.write_blocking_internal(address, buf, last)?\n                }\n            }\n        }\n        Ok(())\n    }\n}\n\nimpl embedded_hal_1::i2c::Error for Error {\n    fn kind(&self) -> embedded_hal_1::i2c::ErrorKind {\n        match *self {\n            Self::Abort(AbortReason::ArbitrationLoss) => embedded_hal_1::i2c::ErrorKind::ArbitrationLoss,\n            Self::Abort(AbortReason::NoAcknowledge) => {\n                embedded_hal_1::i2c::ErrorKind::NoAcknowledge(embedded_hal_1::i2c::NoAcknowledgeSource::Address)\n            }\n            Self::Abort(AbortReason::Other) => embedded_hal_1::i2c::ErrorKind::Other,\n            Self::BusTimeout(_) => embedded_hal_1::i2c::ErrorKind::Bus,\n            Self::InvalidReadBufferLength => embedded_hal_1::i2c::ErrorKind::Other,\n            Self::InvalidWriteBufferLength => embedded_hal_1::i2c::ErrorKind::Other,\n            Self::AddressOutOfRange(_) => embedded_hal_1::i2c::ErrorKind::Other,\n            Self::AddressReserved(_) => embedded_hal_1::i2c::ErrorKind::Other,\n        }\n    }\n}\n\nimpl<'d, T: Instance, M: Mode> embedded_hal_1::i2c::ErrorType for I2c<'d, T, M> {\n    type Error = Error;\n}\n\nimpl<'d, T: Instance, M: Mode> embedded_hal_1::i2c::I2c for I2c<'d, T, M> {\n    fn read(&mut self, address: u8, read: &mut [u8]) -> Result<(), Self::Error> {\n        self.blocking_read(address, read)\n    }\n\n    fn write(&mut self, address: u8, write: &[u8]) -> Result<(), Self::Error> {\n        self.blocking_write(address, write)\n    }\n\n    fn write_read(&mut self, address: u8, write: &[u8], read: &mut [u8]) -> Result<(), Self::Error> {\n        self.blocking_write_read(address, write, read)\n    }\n\n    fn transaction(\n        &mut self,\n        address: u8,\n        operations: &mut [embedded_hal_1::i2c::Operation<'_>],\n    ) -> Result<(), Self::Error> {\n        for i in 0..operations.len() {\n            let last = i == operations.len() - 1;\n            match &mut operations[i] {\n                embedded_hal_1::i2c::Operation::Read(buf) => self.write_blocking_internal(address, buf, last)?,\n                embedded_hal_1::i2c::Operation::Write(buf) => self.write_blocking_internal(address, buf, last)?,\n            }\n        }\n        Ok(())\n    }\n}\n\nimpl<'d, A, T> embedded_hal_async::i2c::I2c<A> for I2c<'d, T, Async>\nwhere\n    A: embedded_hal_async::i2c::AddressMode + Into<u8> + 'static,\n    T: Instance + 'd,\n{\n    async fn read(&mut self, address: A, read: &mut [u8]) -> Result<(), Self::Error> {\n        self.read_async(address.into(), read).await\n    }\n\n    async fn write(&mut self, address: A, write: &[u8]) -> Result<(), Self::Error> {\n        self.write_async(address.into(), write).await\n    }\n\n    async fn write_read(&mut self, address: A, write: &[u8], read: &mut [u8]) -> Result<(), Self::Error> {\n        self.write_read_async(address.into(), write, read).await\n    }\n\n    async fn transaction(\n        &mut self,\n        address: A,\n        operations: &mut [embedded_hal_1::i2c::Operation<'_>],\n    ) -> Result<(), Self::Error> {\n        use embedded_hal_1::i2c::Operation;\n\n        let addr: u8 = address.into();\n        let mut iterator = operations.iter_mut();\n\n        while let Some(op) = iterator.next() {\n            let last = iterator.len() == 0;\n\n            match op {\n                Operation::Read(buffer) => {\n                    self.read_async_internal(addr, buffer, false, last).await?;\n                }\n                Operation::Write(buffer) => {\n                    self.write_async_internal(addr, buffer, last).await?;\n                }\n            }\n        }\n        Ok(())\n    }\n}\n\ntrait SealedInstance {\n    fn regs() -> smb0::Smb0;\n    fn waker() -> &'static AtomicWaker;\n    fn irq_bit() -> usize;\n    fn reset();\n}\n\ntrait SealedMode {}\n\n/// Drivermode.\n#[allow(private_bounds)]\npub trait Mode: SealedMode {}\n\nmacro_rules! impl_mode {\n    ($mode:ident) => {\n        impl SealedMode for $mode {}\n        impl Mode for $mode {}\n    };\n}\n\n/// Blocking mode.\npub struct Blocking;\n\n/// Async mode.\npub struct Async;\n\nimpl_mode!(Blocking);\nimpl_mode!(Async);\n\n/// I2C instance.\n#[allow(private_bounds)]\npub trait Instance: SealedInstance + PeripheralType + 'static + Send {\n    /// Interrupt for this peripheral.\n    type Interrupt: interrupt::typelevel::Interrupt;\n}\n\n// Ideally, these should be part of a dedicated PCR driver\nconst LOCK: u32 = 0xa638_2d4d;\nconst UNLOCK: u32 = 0xa638_2d4c;\n\nmacro_rules! impl_instance {\n    ($peri:ident, $bit:expr, $irq:ident, $reset:expr) => {\n        impl SealedInstance for peripherals::$peri {\n            #[inline(always)]\n            fn regs() -> smb0::Smb0 {\n                pac::$peri\n            }\n\n            #[inline(always)]\n            fn waker() -> &'static AtomicWaker {\n                static WAKER: AtomicWaker = AtomicWaker::new();\n                &WAKER\n            }\n\n            #[inline(always)]\n            fn irq_bit() -> usize {\n                $bit\n            }\n\n            #[inline(always)]\n            fn reset() {\n                fn internal_reset(f: impl FnOnce(pac::pcr::Pcr)) {\n                    pac::PCR.lock_reg().write(|w| w.set_pcr_rst_en_lock(UNLOCK));\n                    f(pac::PCR);\n                    pac::PCR.lock_reg().write(|w| w.set_pcr_rst_en_lock(LOCK));\n                }\n\n                internal_reset($reset);\n            }\n        }\n\n        impl Instance for peripherals::$peri {\n            type Interrupt = crate::interrupt::typelevel::$irq;\n        }\n    };\n}\n\nimpl_instance!(SMB0, 0, I2CSMB0, |pcr| pcr\n    .rst_en_1()\n    .write(|w| w.set_smb0_rst_en(true)));\nimpl_instance!(SMB1, 1, I2CSMB1, |pcr| pcr\n    .rst_en_3()\n    .write(|w| w.set_smb1_rst_en(true)));\nimpl_instance!(SMB2, 2, I2CSMB2, |pcr| pcr\n    .rst_en_3()\n    .write(|w| w.set_smb2_rst_en(true)));\nimpl_instance!(SMB3, 3, I2CSMB3, |pcr| pcr\n    .rst_en_3()\n    .write(|w| w.set_smb3_rst_en(true)));\nimpl_instance!(SMB4, 4, I2CSMB4, |pcr| pcr\n    .rst_en_3()\n    .write(|w| w.set_smb_4_rst_en(true)));\n\n/// SDA pin.\npub trait SdaPin: crate::gpio::Pin {\n    fn setup(&self);\n}\n\n/// SCL pin.\npub trait SclPin: crate::gpio::Pin {\n    fn setup(&self);\n}\n\nmacro_rules! impl_pin {\n    ($function:ident, $($pin:ident, $mux:ident),*) => {\n\t$(\n            impl $function for peripherals::$pin {\n\t\t#[inline(always)]\n\t\tfn setup(&self) {\n\t\t    critical_section::with(|_| {\n\t\t\tself.regs().ctrl1.modify(|w| {\n\t\t\t    w.set_out_buff_type(crate::pac::BufferType::OPEN_DRAIN);\n                            w.set_inp_dis(false);\n\t\t\t    w.set_alt_data(true);\n\t\t\t    w.set_mux_ctrl(crate::pac::Function::$mux);\n\t\t\t})\n\t\t    });\n\t\t}\n\t    }\n\t)*\n    };\n}\n\n#[rustfmt::skip]\nimpl_pin!(\n    SdaPin,\n    GPIO0, F3,\n    GPIO3, F1,\n    GPIO5, F1,\n    GPIO7, F1,\n    GPIO12, F1,\n    GPIO26, F3,\n    GPIO30, F2,\n    GPIO66, F2,\n    GPIO70, F2,\n    GPIO72, F2,\n    GPIO130, F1,\n    GPIO132, F1,\n    GPIO141, F1,\n    GPIO143, F1,\n    GPIO145, F1,\n    GPIO147, F1,\n    GPIO152, F3,\n    GPIO154, F1,\n    GPIO231, F1\n);\n\n#[rustfmt::skip]\nimpl_pin!(\n    SclPin,\n    GPIO4, F1,\n    GPIO6, F1,\n    GPIO10, F1,\n    GPIO13, F1,\n    GPIO24, F3,\n    GPIO27, F3,\n    GPIO62, F2,\n    GPIO65, F2,\n    GPIO71, F2,\n    GPIO73, F2,\n    GPIO107, F4,\n    GPIO131, F1,\n    GPIO140, F1,\n    GPIO142, F1,\n    GPIO144, F4,\n    GPIO146, F1,\n    GPIO150, F1,\n    GPIO155, F1,\n    GPIO230, F1\n);\n\ntrait SealedValidI2cConfig {\n    fn port() -> u8;\n}\n\n/// A marker trait implemented for valid configurations\n#[allow(private_bounds)]\npub trait ValidI2cConfig: SealedValidI2cConfig {}\n\nmacro_rules! impl_config {\n    ($scl:ident, $sda:ident, $port:expr) => {\n        impl SealedValidI2cConfig for (peripherals::SMB0, peripherals::$scl, peripherals::$sda) {\n            #[inline(always)]\n            fn port() -> u8 {\n                $port\n            }\n        }\n        impl SealedValidI2cConfig for (peripherals::SMB1, peripherals::$scl, peripherals::$sda) {\n            #[inline(always)]\n            fn port() -> u8 {\n                $port\n            }\n        }\n        impl SealedValidI2cConfig for (peripherals::SMB2, peripherals::$scl, peripherals::$sda) {\n            #[inline(always)]\n            fn port() -> u8 {\n                $port\n            }\n        }\n        impl SealedValidI2cConfig for (peripherals::SMB3, peripherals::$scl, peripherals::$sda) {\n            #[inline(always)]\n            fn port() -> u8 {\n                $port\n            }\n        }\n        impl SealedValidI2cConfig for (peripherals::SMB4, peripherals::$scl, peripherals::$sda) {\n            #[inline(always)]\n            fn port() -> u8 {\n                $port\n            }\n        }\n\n        impl ValidI2cConfig for (peripherals::SMB0, peripherals::$scl, peripherals::$sda) {}\n        impl ValidI2cConfig for (peripherals::SMB1, peripherals::$scl, peripherals::$sda) {}\n        impl ValidI2cConfig for (peripherals::SMB2, peripherals::$scl, peripherals::$sda) {}\n        impl ValidI2cConfig for (peripherals::SMB3, peripherals::$scl, peripherals::$sda) {}\n        impl ValidI2cConfig for (peripherals::SMB4, peripherals::$scl, peripherals::$sda) {}\n    };\n}\n\n// I2C00\nimpl_config!(GPIO4, GPIO3, 0);\n\n// I2C01\nimpl_config!(GPIO73, GPIO72, 1);\nimpl_config!(GPIO131, GPIO130, 1);\n\n// I2C02\nimpl_config!(GPIO155, GPIO154, 2);\n\n// I2C03\nimpl_config!(GPIO10, GPIO7, 3);\n\n// I2C04\nimpl_config!(GPIO144, GPIO143, 4);\n\n// I2C05\nimpl_config!(GPIO142, GPIO141, 5);\n\n// I2C06\nimpl_config!(GPIO140, GPIO132, 6);\n\n// I2C07\nimpl_config!(GPIO13, GPIO12, 7);\nimpl_config!(GPIO24, GPIO152, 7);\n\n// I2C08\nimpl_config!(GPIO230, GPIO231, 8);\n\n// I2C09\nimpl_config!(GPIO146, GPIO145, 9);\n\n// I2C10\nimpl_config!(GPIO107, GPIO30, 10);\n\n// I2C11\nimpl_config!(GPIO62, GPIO0, 11);\nimpl_config!(GPIO6, GPIO5, 11);\n\n// I2C12\nimpl_config!(GPIO27, GPIO26, 12);\n\n// I2C13\nimpl_config!(GPIO65, GPIO66, 13);\n\n// I2C14\nimpl_config!(GPIO71, GPIO70, 14);\n\n// I2C15\nimpl_config!(GPIO150, GPIO147, 15);\n"
  },
  {
    "path": "embassy-microchip/src/lib.rs",
    "content": "#![no_std]\n#![allow(async_fn_in_trait)]\n#![doc = include_str!(\"../README.md\")]\n\n//! ## Feature flags\n#![doc = document_features::document_features!(feature_label = r#\"<span class=\"stab portability\"><code>{feature}</code></span>\"#)]\n\n#[cfg(not(any(\n    feature = \"mec1721n_b0_lj\",\n    feature = \"mec1721n_b0_sz\",\n    feature = \"mec1723n_b0_lj\",\n    feature = \"mec1723n_b0_sz\",\n    feature = \"mec1723n_f0_sz\",\n    feature = \"mec1723n_p0_9y\",\n    feature = \"mec1724n_b0_lj\",\n    feature = \"mec1724n_b0_sz\",\n    feature = \"mec1725n_b0_lj\",\n    feature = \"mec1727n_b0_sz\",\n)))]\ncompile_error!(\n    \"No chip feature activated. You must activate exactcly one of the following features:\n    mec1721n_b0_lj,\n    mec1721n_b0_sz,\n    mec1723n_b0_lj,\n    mec1723n_b0_sz,\n    mec1723n_f0_sz,\n    mec1723n_p0_9y,\n    mec1724n_b0_lj,\n    mec1724n_b0_sz,\n    mec1725n_b0_lj,\n    mec1727n_b0_sz,\n    \"\n);\n\n// This mod MUST go first, so that the others see its macros.\npub(crate) mod fmt;\n\npub mod gpio;\npub mod i2c;\npub mod pwm;\npub mod tach;\npub mod time_driver;\npub mod uart;\n\n// Reexports\npub use embassy_hal_internal::{Peri, PeripheralType};\n#[cfg(feature = \"unstable-pac\")]\npub use mec17xx_pac as pac;\n#[cfg(not(feature = \"unstable-pac\"))]\npub(crate) use mec17xx_pac as pac;\n\n#[cfg(feature = \"rt\")]\npub use crate::pac::NVIC_PRIO_BITS;\n\nembassy_hal_internal::interrupt_mod!(\n    ADC_RPT,\n    ADC_SNGL,\n    AEC0_IBF,\n    AEC0_OBE,\n    AEC1_IBF,\n    AEC1_OBE,\n    AEC2_IBF,\n    AEC2_OBE,\n    AEC3_IBF,\n    AEC3_OBE,\n    AEC4_IBF,\n    AEC4_OBE,\n    APM1_CTL,\n    APM1_EN,\n    APM1_STS,\n    ASIF,\n    BCM_BUSY_CLR_0,\n    BCM_ERR_0,\n    CCT,\n    CCT_CAP0,\n    CCT_CAP1,\n    CCT_CAP2,\n    CCT_CAP3,\n    CCT_CAP4,\n    CCT_CAP5,\n    CCT_CMP0,\n    CCT_CMP1,\n    CNTR_TMR0,\n    CNTR_TMR1,\n    CNTR_TMR2,\n    CNTR_TMR3,\n    DMA_CH00,\n    DMA_CH01,\n    DMA_CH02,\n    DMA_CH03,\n    DMA_CH04,\n    DMA_CH05,\n    DMA_CH06,\n    DMA_CH07,\n    DMA_CH08,\n    DMA_CH09,\n    DMA_CH10,\n    DMA_CH11,\n    DMA_CH12,\n    DMA_CH13,\n    DMA_CH14,\n    DMA_CH15,\n    EMI0,\n    EMI1,\n    EMI2,\n    ESPI_RESET,\n    ESPI_VWIRE,\n    GIRQ08,\n    GIRQ09,\n    GIRQ10,\n    GIRQ11,\n    GIRQ12,\n    GIRQ13,\n    GIRQ14,\n    GIRQ15,\n    GIRQ17,\n    GIRQ18,\n    GIRQ19,\n    GIRQ20,\n    GIRQ21,\n    GIRQ23,\n    GIRQ24,\n    GIRQ25,\n    GIRQ26,\n    HTMR0,\n    HTMR1,\n    I2CSMB0,\n    I2CSMB1,\n    I2CSMB2,\n    I2CSMB3,\n    I2CSMB4,\n    INTR_BM1,\n    INTR_BM2,\n    INTR_FLASH,\n    INTR_LTR,\n    INTR_OOB_DOWN,\n    INTR_OOB_UP,\n    INTR_PC,\n    KBC_IBF,\n    KBC_OBE,\n    KEYSCAN,\n    LED0,\n    LED1,\n    LED2,\n    LED3,\n    MBOX,\n    P80CAP0,\n    PECI,\n    PHOT,\n    POWERGUARD_0,\n    POWERGUARD_1,\n    PS2_0A_WAKE,\n    PS2_0B_WAKE,\n    PS2_0_ACT,\n    QMSPI,\n    RC_ID0,\n    RC_ID1,\n    RC_ID2,\n    RPM2PWM_0_SPIN,\n    RPM2PWM_0_STALL,\n    RPM2PWM_1_SPIN,\n    RPM2PWM_1_STALL,\n    RTC,\n    RTC_ALARM,\n    RTMR,\n    RX0,\n    RX1,\n    SAF_DONE,\n    SAF_ERR,\n    SPISLV,\n    SYSPWR,\n    TACH0,\n    TACH1,\n    TACH2,\n    TACH3,\n    TIMER16_0,\n    TIMER16_1,\n    TIMER16_2,\n    TIMER16_3,\n    TIMER32_0,\n    TIMER32_1,\n    TX0,\n    TX1,\n    UART0,\n    UART1,\n    VCI_IN0,\n    VCI_IN1,\n    VCI_IN2,\n    VCI_IN3,\n    VCI_OVRD_IN,\n    WDT,\n    WK,\n    WKSEC,\n    WKSUB,\n    WKSUBSEC,\n);\n\n/// Macro to bind interrupts to handlers.\n///\n/// This defines the right interrupt handlers, and creates a unit struct (like `struct Irqs;`)\n/// and implements the right [`Binding`]s for it. You can pass this struct to drivers to\n/// prove at compile-time that the right interrupts have been bound.\n///\n/// Example of how to bind one interrupt:\n///\n/// ```rust,ignore\n/// use embassy_microchip::{bind_interrupts, usb, peripherals};\n///\n/// bind_interrupts!(struct Irqs {\n///     USBCTRL_IRQ => usb::InterruptHandler<peripherals::USB>;\n/// });\n/// ```\n///\n// developer note: this macro can't be in `embassy-hal-internal` due to the use of `$crate`.\n#[macro_export]\nmacro_rules! bind_interrupts {\n    ($vis:vis struct $name:ident {\n        $(\n            $(#[cfg($cond_irq:meta)])?\n            $irq:ident => $(\n                $(#[cfg($cond_handler:meta)])?\n                $handler:ty\n            ),*;\n        )*\n    }) => {\n        #[derive(Copy, Clone)]\n        $vis struct $name;\n\n        $(\n            #[allow(non_snake_case)]\n            #[unsafe(no_mangle)]\n            $(#[cfg($cond_irq)])?\n            unsafe extern \"C\" fn $irq() {\n                $(\n                    $(#[cfg($cond_handler)])?\n                    <$handler as $crate::interrupt::typelevel::Handler<$crate::interrupt::typelevel::$irq>>::on_interrupt();\n\n                )*\n            }\n\n            $(#[cfg($cond_irq)])?\n            $crate::bind_interrupts!(@inner\n                $(\n                    $(#[cfg($cond_handler)])?\n                    unsafe impl $crate::interrupt::typelevel::Binding<$crate::interrupt::typelevel::$irq, $handler> for $name {}\n                )*\n            );\n        )*\n    };\n    (@inner $($t:tt)*) => {\n        $($t)*\n    }\n}\n\nembassy_hal_internal::peripherals! {\n    // Port 0\n    GPIO0,\n    GPIO1,\n    GPIO2,\n    GPIO3,\n    GPIO4,\n    GPIO5,\n    GPIO6,\n    GPIO7,\n\n    // Port 1\n    GPIO10,\n    GPIO11,\n    GPIO12,\n    GPIO13,\n    GPIO14,\n    GPIO15,\n    GPIO16,\n    GPIO17,\n\n    // Port 2\n    GPIO20,\n    GPIO21,\n    GPIO22,\n    GPIO23,\n    GPIO24,\n    GPIO25,\n    GPIO26,\n    GPIO27,\n\n    // Port 3\n    GPIO30,\n    GPIO31,\n    GPIO32,\n    GPIO33,\n    GPIO34,\n    GPIO35,\n    GPIO36,\n\n    // Port 4\n    GPIO40,\n    GPIO41,\n    GPIO42,\n    GPIO43,\n    GPIO44,\n    GPIO45,\n    GPIO46,\n    GPIO47,\n\n    // Port 5\n    GPIO50,\n    GPIO51,\n    GPIO52,\n    GPIO53,\n    GPIO54,\n    GPIO55,\n    GPIO56,\n    GPIO57,\n\n    // Port 6\n    GPIO60,\n    GPIO61,\n    GPIO62,\n    GPIO63,\n    GPIO64,\n    GPIO65,\n    GPIO66,\n    GPIO67,\n\n    // Port 7\n    GPIO70,\n    GPIO71,\n    GPIO72,\n    GPIO73,\n    GPIO74,\n    GPIO75,\n    GPIO76,\n\n    // Ports 8 and 9 don't exist\n\n    // Port 10\n    GPIO100,\n    GPIO101,\n    GPIO102,\n    GPIO103,\n    GPIO104,\n    GPIO105,\n    GPIO106,\n    GPIO107,\n\n    // Port 11\n    GPIO112,\n    GPIO113,\n    GPIO114,\n    GPIO115,\n    GPIO116,\n    GPIO117,\n\n    // Port 12\n    GPIO120,\n    GPIO121,\n    GPIO122,\n    GPIO123,\n    GPIO124,\n    GPIO125,\n    GPIO126,\n    GPIO127,\n\n    // Port 13\n    GPIO130,\n    GPIO131,\n    GPIO132,\n    GPIO133,\n    GPIO134,\n    GPIO135,\n\n    // Port 14\n    GPIO140,\n    GPIO141,\n    GPIO142,\n    GPIO143,\n    GPIO144,\n    GPIO145,\n    GPIO146,\n    GPIO147,\n\n    // Port 15\n    GPIO150,\n    GPIO151,\n    GPIO152,\n    GPIO153,\n    GPIO154,\n    GPIO155,\n    GPIO156,\n    GPIO157,\n\n    // Port 16\n    GPIO160,\n    GPIO161,\n    GPIO162,\n    GPIO165,\n    GPIO166,\n    GPIO167,\n\n    // Port 17\n    GPIO170,\n    GPIO171,\n    GPIO172,\n    GPIO173,\n    GPIO174,\n    GPIO175,\n\n    // Ports 18 and 19 don't exist\n\n    // Port 20\n    GPIO200,\n    GPIO201,\n    GPIO202,\n    GPIO203,\n    GPIO204,\n    GPIO205,\n    GPIO206,\n    GPIO207,\n\n    // Port 21\n    GPIO210,\n    GPIO211,\n    GPIO212,\n    GPIO213,\n    GPIO214,\n    GPIO215,\n    GPIO216,\n    GPIO217,\n\n    // Port 22\n    GPIO221,\n    GPIO222,\n    GPIO223,\n    GPIO224,\n    GPIO225,\n    GPIO226,\n    GPIO227,\n\n    // Port 23\n    GPIO230,\n    GPIO231,\n\n    // Port 24\n    GPIO240,\n    GPIO241,\n    GPIO242,\n    GPIO243,\n    GPIO244,\n    GPIO245,\n    GPIO246,\n    GPIO247,\n\n    // Port 25\n    // GPIO253, no interrupt\n    GPIO254,\n    GPIO255,\n    // GPIO256, no interrupt\n    // GPIO257, no interrupt\n\n    // Port 26\n    // GPIO260, no interrupt\n\n    ESPI,\n\n    PWM0,\n    PWM1,\n    PWM2,\n    PWM3,\n    PWM4,\n    PWM5,\n    PWM6,\n    PWM7,\n    PWM8,\n    PWM9,\n    PWM10,\n    PWM11,\n\n    SMB0,\n    SMB1,\n    SMB2,\n    SMB3,\n    SMB4,\n    TACH0,\n    TACH1,\n    TACH2,\n    TACH3,\n    UART0,\n    UART1,\n}\n\n/// HAL configuration for Microchip.\npub mod config {\n    /// HAL configuration passed when initializing.\n    #[non_exhaustive]\n    pub struct Config {}\n\n    impl Default for Config {\n        fn default() -> Self {\n            Self {}\n        }\n    }\n\n    impl Config {\n        /// Create a new configuration\n        pub fn new() -> Self {\n            Self {}\n        }\n    }\n}\n\n/// Initialize the `embassy-microchip` HAL with the provided configuration.\n///\n/// This returns the peripheral singletons that can be used for creating drivers.\n///\n/// This should only be called once at startup, otherwise it panis.\npub fn init(_config: config::Config) -> Peripherals {\n    let peripherals = Peripherals::take();\n\n    unsafe {\n        // ROM code leaves interrupts globally disabled, d'oh.\n        cortex_m::interrupt::enable();\n        gpio::init();\n    }\n\n    time_driver::init();\n\n    peripherals\n}\n"
  },
  {
    "path": "embassy-microchip/src/pwm.rs",
    "content": "//! Pulse Width Modulation (PWM)\n\nuse embassy_hal_internal::{Peri, PeripheralType};\npub use embedded_hal_1::pwm::SetDutyCycle;\nuse embedded_hal_1::pwm::{Error, ErrorKind, ErrorType};\n\nuse crate::gpio::{AnyPin, Pin, SealedPin};\nuse crate::pac::pwm0::Pwm0;\nuse crate::{pac, peripherals};\n\n#[allow(non_camel_case_types)]\n#[derive(Copy, Clone, PartialEq)]\n/// Clock selection\npub enum Clock {\n    _48MHz,\n    _100kHz,\n}\n\nimpl From<Clock> for bool {\n    fn from(clk: Clock) -> Self {\n        match clk {\n            Clock::_48MHz => true,\n            Clock::_100kHz => false,\n        }\n    }\n}\n\n/// The configuration of a PWM slice.\n#[non_exhaustive]\n#[derive(Clone, Copy)]\npub struct Config {\n    /// Inverts the PWM output signal.\n    pub invert: bool,\n\n    /// Enables the PWM slice, allowing it to generate an output.\n    pub enable: bool,\n\n    /// PWM target frequency\n    pub frequency: u32,\n\n    /// PWM target duty cycle\n    pub duty_cycle: u16,\n}\n\nimpl Default for Config {\n    fn default() -> Self {\n        Self {\n            invert: false,\n            enable: true, // differs from reset\n            frequency: 100_000,\n            duty_cycle: 32767,\n        }\n    }\n}\n\n/// PWM error.\n#[derive(Debug)]\npub enum PwmError {\n    /// Invalid Duty Cycle.\n    InvalidDutyCycle,\n}\n\nimpl Error for PwmError {\n    fn kind(&self) -> ErrorKind {\n        match self {\n            PwmError::InvalidDutyCycle => ErrorKind::Other,\n        }\n    }\n}\n\n/// PWM driver.\npub struct Pwm<'d, T: Instance> {\n    _peri: Peri<'d, T>,\n    _pin: Peri<'d, AnyPin>,\n}\n\nimpl<'d, T: Instance> ErrorType for Pwm<'d, T> {\n    type Error = PwmError;\n}\n\nimpl<'d, T: Instance> Pwm<'d, T> {\n    const HIGHEST_DIVIDER: u32 = 15;\n    const HIGHEST_ON_OFF: u32 = 65_535;\n    const HIGH_CLK_FREQ: u32 = 48_000_000;\n    const LOW_CLK_FREQ: u32 = 100_000;\n    const MIN_HIGH_CLK_FREQ: u32 = Self::HIGH_CLK_FREQ / (2 * (Self::HIGHEST_ON_OFF + 1) * (Self::HIGHEST_DIVIDER + 1));\n\n    pub fn new(peri: Peri<'d, T>, pin: Peri<'d, impl PwmPin<T>>, config: Config) -> Self {\n        Self::new_inner(peri, pin, config)\n    }\n\n    fn new_inner(_peri: Peri<'d, T>, _pin: Peri<'d, impl PwmPin<T>>, config: Config) -> Self {\n        _pin.setup();\n        Self::configure(config);\n        Self {\n            _peri,\n            _pin: _pin.into(),\n        }\n    }\n\n    fn configure(config: Config) {\n        let (div, on, off, clk) = Self::compute_parameters(config.frequency, config.duty_cycle);\n\n        T::regs().cfg().write(|w| {\n            w.set_inv(config.invert);\n            w.set_clk_sel(clk.into());\n            w.set_clk_pre_div(div);\n            w.set_pwm_en(config.enable);\n        });\n\n        T::regs().cnt_on().write_value(on);\n        T::regs().cnt_off().write_value(off);\n    }\n\n    fn compute_parameters(target_freq: u32, target_duty_cycle: u16) -> (u8, u32, u32, Clock) {\n        let high = target_freq > Self::MIN_HIGH_CLK_FREQ;\n\n        // We assume that if target_freq is not greater than high\n        // clock minimum frequency, then it must fit within the low\n        // clock, even if with high error.\n\n        let (clk, freq) = if high {\n            (Clock::_48MHz, Self::HIGH_CLK_FREQ)\n        } else {\n            (Clock::_100kHz, Self::LOW_CLK_FREQ)\n        };\n\n        let (div, _) = (1..=16).fold((0, u32::MAX), |(best_div, best_error), d| {\n            let candidate = freq / d;\n            let error = target_freq.abs_diff(candidate);\n\n            if error < best_error {\n                (candidate, error)\n            } else {\n                (best_div, best_error)\n            }\n        });\n\n        let (on, off) = Self::compute_on_off(freq, target_freq, target_duty_cycle);\n        (div as u8, on, off, clk)\n    }\n\n    fn compute_on_off(freq: u32, target_freq: u32, target_duty_cycle: u16) -> (u32, u32) {\n        let total = freq * 10 / target_freq;\n        let on = ((total * u32::from(target_duty_cycle)) / 100_000) - 1;\n        let off = total - on - 2;\n\n        (on, off)\n    }\n}\n\nimpl<'d, T: Instance> SetDutyCycle for Pwm<'d, T> {\n    fn max_duty_cycle(&self) -> u16 {\n        u16::MAX\n    }\n\n    fn set_duty_cycle(&mut self, duty: u16) -> Result<(), Self::Error> {\n        let max_duty = self.max_duty_cycle();\n\n        if duty > max_duty {\n            return Err(PwmError::InvalidDutyCycle);\n        }\n\n        let off = u32::from(duty) * u32::from(u16::MAX) / u32::from(max_duty);\n        let on = u32::from(u16::MAX) - off;\n\n        T::regs().cnt_on().write_value(on.into());\n        T::regs().cnt_off().write_value(off.into());\n\n        Ok(())\n    }\n}\n\ntrait SealedInstance {\n    fn regs() -> Pwm0;\n}\n\n/// PWM Instance trait\n#[allow(private_bounds)]\npub trait Instance: SealedInstance + PeripheralType + 'static + Send {}\n\nmacro_rules! impl_instance {\n    ($($peri:ident),*) => {\n\t$(\n\t    impl SealedInstance for peripherals::$peri {\n\t\t#[inline(always)]\n\t\tfn regs() -> Pwm0 {\n\t\t    pac::$peri\n\t\t}\n\t    }\n\n\t    impl Instance for peripherals::$peri {}\n\t)*\n    }\n}\n\nimpl_instance!(PWM0, PWM1, PWM2, PWM3, PWM4, PWM5, PWM6, PWM7, PWM8, PWM9, PWM10, PWM11);\n\npub trait PwmPin<T: Instance>: Pin + PeripheralType {\n    fn setup(&self);\n}\n\nmacro_rules! impl_pin {\n    ($peri:ident, $($pin:ident),*) => {\n\t$(\n\t    impl PwmPin<peripherals::$peri> for peripherals::$pin {\n                #[inline(always)]\n                fn setup(&self) {\n                    critical_section::with(|_| {\n                        self.regs().ctrl1.modify(|w| {\n                            w.set_mux_ctrl(pac::Function::F1);\n                        })\n                    });\n                }\n            }\n\t)*\n    }\n}\n\nimpl_pin!(PWM0, GPIO53, GPIO241);\nimpl_pin!(PWM1, GPIO54);\nimpl_pin!(PWM2, GPIO55, GPIO45);\nimpl_pin!(PWM3, GPIO56);\nimpl_pin!(PWM4, GPIO11, GPIO1);\nimpl_pin!(PWM5, GPIO2);\nimpl_pin!(PWM6, GPIO14, GPIO63);\nimpl_pin!(PWM7, GPIO15);\nimpl_pin!(PWM8, GPIO35, GPIO175);\nimpl_pin!(PWM9, GPIO133);\nimpl_pin!(PWM10, GPIO134);\nimpl_pin!(PWM11, GPIO160, GPIO222);\n"
  },
  {
    "path": "embassy-microchip/src/tach.rs",
    "content": "//! TACH driver.\n\nuse core::future::poll_fn;\nuse core::marker::PhantomData;\nuse core::task::Poll;\n\nuse embassy_hal_internal::{Peri, PeripheralType};\nuse embassy_sync::waitqueue::AtomicWaker;\n\nuse crate::gpio::{AnyPin, Pin as GpioPin};\nuse crate::interrupt::typelevel::{Binding, Interrupt};\nuse crate::pac::tach0::Tach0;\nuse crate::{interrupt, pac, peripherals};\n\n// The minimum and maximum RPMs the TACH peripheral can reliably measure according to datasheet\nconst MIN_RPM: u32 = 100;\nconst MAX_RPM: u32 = 30_000;\n\n// The fixed clock to TACH\nconst TACH_CLK: u32 = 100_000;\n\n// Seconds per minute\nconst SEC_PER_MIN: u32 = 60;\n\n/// Tach interrupt handler.\npub struct InterruptHandler<T: Instance> {\n    _phantom: PhantomData<T>,\n}\n\nimpl<T: Instance> interrupt::typelevel::Handler<T::Interrupt> for InterruptHandler<T> {\n    unsafe fn on_interrupt() {\n        // Disable the interrupt here to prevent storm, so we can read the count ready bit and clear it later\n        T::regs().ctrl().modify(|w| w.set_cnt_rdy_int_en(false));\n        T::waker().wake();\n    }\n}\n\n/// TACH error.\n#[derive(Clone, Copy, Debug)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub enum Error {\n    /// The calculated RPM is below the minimum RPM the tach peripheral can reliably measure (100 RPM),\n    /// thus it is likely inaccurate.\n    ///\n    /// It is very likely the fan is stopped, however this driver does not assume that.\n    RpmLow(u32),\n\n    /// The calculated RPM is above the maximum RPM the tach peripheral can reliably measure (30,000 RPM),\n    /// thus it is likely inaccurate.\n    RpmHigh(u32),\n\n    /// Calculating RPM would result in a division by zero.\n    ///\n    /// This would indicate the selected number of [`Edges`] occurred in less than one tach clock period (10 µs),\n    /// thus the tach counter is zero.\n    DivideByZero,\n}\n\n/// The number of TACH input edges for which the number of 100 kHz pulses will be counted.\n#[derive(Clone, Copy, Debug)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub enum Edges {\n    /// Two edges.\n    _2,\n    /// Three edges.\n    _3,\n    /// Five edges.\n    _5,\n    /// Nine edges.\n    _9,\n}\n\nimpl From<Edges> for u8 {\n    fn from(edges: Edges) -> Self {\n        match edges {\n            Edges::_2 => 0b00,\n            Edges::_3 => 0b01,\n            Edges::_5 => 0b10,\n            Edges::_9 => 0b11,\n        }\n    }\n}\n\n/// TACH peripheral config.\npub struct Config {\n    /// Remove high frequency glitches from TACH input\n    /// (e.g. pulses less than two 100 KHz periods wide will be filtered).\n    ///\n    /// It is recommended to always enable this.\n    pub filter_en: bool,\n\n    /// The number of TACH input edges for which the number of 100 kHz pulses will be counted.\n    ///\n    /// This should match the number of edges per revolution for specific fan model.\n    ///\n    /// For typical fans which produce a 50% duty cycle square wave,\n    /// five edges (two periods) represents one revolution.\n    ///\n    /// See figure 28-2 in datasheet.\n    pub edges: Edges,\n}\n\nimpl Default for Config {\n    fn default() -> Self {\n        Self {\n            filter_en: true,\n            edges: Edges::_5,\n        }\n    }\n}\n\n/// TACH driver.\npub struct Tach<'d, T: Instance> {\n    _peri: Peri<'d, T>,\n    _pin: Peri<'d, AnyPin>,\n}\n\nimpl<'d, T: Instance> Tach<'d, T> {\n    /// Create a new TACH driver instance.\n    pub fn new(\n        _peri: Peri<'d, T>,\n        _pin: Peri<'d, impl TachPin<T>>,\n        _irq: impl Binding<T::Interrupt, InterruptHandler<T>>,\n        config: Config,\n    ) -> Self {\n        // Sets given pin to TACH mode (alternate function 1)\n        // REVISIT: make an API for this\n        critical_section::with(|_| {\n            _pin.regs().ctrl1.modify(|w| {\n                w.set_mux_ctrl(pac::Function::F1);\n            })\n        });\n\n        // Set tach to mode 1 with edges and filtering set by config, then enable.\n        //\n        // Mode 0 does not seem very useful for a high level driver like this,\n        // so users are free to use the PAC if they need it.\n        T::regs().ctrl().write(|w| {\n            w.set_en(true);\n            w.set_filt_en(config.filter_en);\n            w.set_rd_mod_sel(true);\n            w.set_edges(config.edges.into());\n        });\n\n        T::Interrupt::unpend();\n        // SAFETY: We have sole control of TACH interrupts so this is safe to do.\n        unsafe { T::Interrupt::enable() };\n\n        Self {\n            _peri,\n            _pin: _pin.into(),\n        }\n    }\n\n    fn calculate_rpm(&self) -> Result<u16, Error> {\n        let count = T::regs().ctrl().read().cntr();\n\n        if count == 0 {\n            // Somehow we saw all edges before a single 100 kHz pulse?\n            Err(Error::DivideByZero)\n        } else {\n            // This calculation assumes the user correctly set the number of edges per one revolution\n            let rpm = (SEC_PER_MIN * TACH_CLK) / count as u32;\n\n            // The datasheet specifies the peripheral can reliably measure 100-30,000 RPM\n            // If we calculate a value outside that range, still return it, but wrap in an error to let user know\n            match rpm {\n                ..MIN_RPM => Err(Error::RpmLow(rpm)),\n                MIN_RPM..=MAX_RPM => Ok(rpm as u16),\n                _ => Err(Error::RpmHigh(rpm)),\n            }\n        }\n    }\n\n    /// Return the measured fan speed in revolutions per minute (RPM).\n    ///\n    /// # Errors\n    ///\n    /// If calculated RPM is below 100 RPM, [`Error::RpmLow`] is returned. In this case,\n    /// the fan is likely stopped.\n    ///\n    /// If calculated RPM is above 30,000 RPM, [`Error::RpmHigh`] is returned. In this case,\n    /// the fan is rotating too quickly and the calculated RPM is likely inaccurate.\n    ///\n    /// If selected number of [`Edges`] occurred in less than one TACH clock period (10 µs),\n    /// [`Error::DivideByZero`] is returned as calculating RPM is impossible.\n    pub async fn rpm(&mut self) -> Result<u16, Error> {\n        poll_fn(|cx| {\n            T::waker().register(cx.waker());\n\n            if T::regs().sts().read().cnt_rdy_sts() {\n                // Count has been latched, so clear ready bit and calculate RPM from count\n                T::regs().sts().modify(|w| w.set_cnt_rdy_sts(true));\n                Poll::Ready(self.calculate_rpm())\n            } else {\n                // Count still not latched, so enable interrupt and wait\n                T::regs().ctrl().modify(|w| w.set_cnt_rdy_int_en(true));\n                Poll::Pending\n            }\n        })\n        .await\n    }\n}\n\nimpl<'d, T: Instance> Drop for Tach<'d, T> {\n    fn drop(&mut self) {\n        T::regs().ctrl().write(|w| w.set_en(false));\n        T::Interrupt::disable();\n    }\n}\n\ntrait SealedInstance {\n    fn regs() -> Tach0;\n    fn waker() -> &'static AtomicWaker;\n}\n\n/// TACH Instance trait.\n#[allow(private_bounds)]\npub trait Instance: SealedInstance + PeripheralType + 'static + Send {\n    type Interrupt: interrupt::typelevel::Interrupt;\n}\n\nmacro_rules! impl_instance {\n    ($peri:ident) => {\n        impl SealedInstance for peripherals::$peri {\n            #[inline(always)]\n            fn regs() -> Tach0 {\n                pac::$peri\n            }\n\n            #[inline(always)]\n            fn waker() -> &'static AtomicWaker {\n                static WAKER: AtomicWaker = AtomicWaker::new();\n                &WAKER\n            }\n        }\n\n        impl Instance for peripherals::$peri {\n            type Interrupt = crate::interrupt::typelevel::$peri;\n        }\n    };\n}\n\nimpl_instance!(TACH0);\nimpl_instance!(TACH1);\nimpl_instance!(TACH2);\nimpl_instance!(TACH3);\n\n/// A GPIO pin that can be configured as a TACH pin.\npub trait TachPin<T: Instance>: GpioPin + PeripheralType {}\n\nmacro_rules! impl_pin {\n    ($peri:ident, $($pin:ident),*) => {\n        $(\n            impl TachPin<peripherals::$peri> for peripherals::$pin {}\n        )*\n    }\n}\n\nimpl_pin!(TACH0, GPIO50);\nimpl_pin!(TACH1, GPIO51);\nimpl_pin!(TACH2, GPIO52);\nimpl_pin!(TACH3, GPIO33);\n"
  },
  {
    "path": "embassy-microchip/src/time_driver.rs",
    "content": "//! Time driver.\nuse core::cell::{Cell, RefCell};\nuse core::sync::atomic::{AtomicU32, Ordering, compiler_fence};\n\nuse critical_section::CriticalSection;\nuse embassy_hal_internal::interrupt::InterruptExt;\nuse embassy_sync::blocking_mutex::CriticalSectionMutex as Mutex;\nuse embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;\nuse embassy_time_driver::Driver;\nuse embassy_time_queue_utils::Queue;\n\nuse crate::interrupt;\nuse crate::pac::{CCT, ECIA};\n\n/// Calculate the timestamp from the period count and the tick count.\n///\n/// The Input Capture and Compare Timer is a 32-bit free running timer\n/// running at the main clock frequency.\n///\n/// We define a period to be 2^31 ticks, such that each overflow is 2\n/// periods.\n///\n/// Toe get `now()`, `period` is read first, then `counter` is\n/// read. If the counter value matches the expected range for the\n/// `period` parity, we're done. If it doesn't, this mean that a new\n/// period start has raced us between reading `period` and `counter`,\n/// so we assume the `counter` value corresponds to the next period.\n///\n/// `period` is a 32-bit integer, it overlows on 2^32 * 2^31 /\n/// 48_000_000 seconds of uptime, which is about 6093 years.\nfn calc_now(period: u32, counter: u32) -> u64 {\n    ((period as u64) << 31) + ((counter ^ ((period & 1) << 31)) as u64)\n}\n\nstruct AlarmState {\n    timestamp: Cell<u64>,\n}\n\nunsafe impl Send for AlarmState {}\n\nimpl AlarmState {\n    const fn new() -> Self {\n        Self {\n            timestamp: Cell::new(u64::MAX),\n        }\n    }\n}\n\npub(crate) struct CctDriver {\n    /// Number of 2^31 periods elapsed since boot.\n    period: AtomicU32,\n\n    /// Timestamp at which to fire alarm. `u64::MAX` if no alarm is\n    /// scheduled.\n    alarm: Mutex<AlarmState>,\n    queue: Mutex<RefCell<Queue>>,\n}\n\nembassy_time_driver::time_driver_impl!(static DRIVER: CctDriver = CctDriver {\n    period: AtomicU32::new(0),\n    alarm: Mutex::const_new(CriticalSectionRawMutex::new(), AlarmState::new()),\n    queue: Mutex::new(RefCell::new(Queue::new())),\n});\n\nimpl CctDriver {\n    fn init(&'static self) {\n        interrupt::CCT.disable();\n        interrupt::CCT_CMP0.disable();\n        interrupt::CCT_CMP1.disable();\n\n        interrupt::CCT.set_priority(interrupt::Priority::P0);\n        interrupt::CCT_CMP0.set_priority(interrupt::Priority::P0);\n        interrupt::CCT_CMP1.set_priority(interrupt::Priority::P0);\n\n        ECIA.src18().write_value(1 << 20 | 1 << 27 | 1 << 28);\n        ECIA.en_set18().write_value(1 << 20 | 1 << 27 | 1 << 28);\n\n        // Reset timer\n        CCT.ctrl().write(|w| w.set_free_rst(true));\n\n        // Wait until reset is completed\n        while CCT.ctrl().read().free_rst() {}\n\n        // Mid\n        CCT.comp0().write(|w| w.set_comp_0(0x8000_0000));\n\n        CCT.ctrl().write(|w| {\n            w.set_act(true);\n            w.set_free_en(true);\n            w.set_cmp_en0(true);\n        });\n\n        interrupt::CCT.unpend();\n        interrupt::CCT_CMP0.unpend();\n        interrupt::CCT_CMP1.unpend();\n        unsafe {\n            interrupt::CCT.enable();\n            interrupt::CCT_CMP0.enable();\n            interrupt::CCT_CMP1.enable();\n        }\n    }\n\n    fn next_period(&self) {\n        let period = self.period.load(Ordering::Relaxed) + 1;\n        self.period.store(period, Ordering::Relaxed);\n        let t = (period as u64) << 31;\n\n        critical_section::with(move |cs| {\n            let alarm = self.alarm.borrow(cs);\n            let at = alarm.timestamp.get();\n\n            if at < t + 0xc000_0000 {\n                CCT.ctrl().modify(|w| w.set_cmp_en1(true));\n            }\n        });\n    }\n\n    fn set_alarm(&self, cs: CriticalSection, timestamp: u64) -> bool {\n        self.alarm.borrow(cs).timestamp.set(timestamp);\n\n        let t = self.now();\n        if timestamp <= t {\n            // If timestamp has passed, alarm will not fire.\n            // Disarm it and return `false`.\n            CCT.ctrl().modify(|w| w.set_cmp_en1(false));\n            self.alarm.borrow(cs).timestamp.set(u64::MAX);\n            return false;\n        }\n\n        CCT.comp1().write(|w| w.set_comp_1(timestamp as u32));\n\n        let diff = timestamp - t;\n        CCT.ctrl().modify(|w| w.set_cmp_en1(diff < 0xc000_0000));\n\n        let t = self.now();\n        if timestamp <= t {\n            CCT.ctrl().modify(|w| w.set_cmp_en1(true));\n            // If timestamp has passed, alarm will not fire.\n            // Disarm it and return `false`.\n            CCT.ctrl().modify(|w| w.set_cmp_en1(false));\n            self.alarm.borrow(cs).timestamp.set(u64::MAX);\n            return false;\n        }\n\n        true\n    }\n\n    fn trigger_alarm(&self, cs: CriticalSection) {\n        let mut next = self.queue.borrow(cs).borrow_mut().next_expiration(self.now());\n        while !self.set_alarm(cs, next) {\n            next = self.queue.borrow(cs).borrow_mut().next_expiration(self.now());\n        }\n    }\n}\n\nimpl Driver for CctDriver {\n    fn now(&self) -> u64 {\n        let period = self.period.load(Ordering::Relaxed);\n        compiler_fence(Ordering::Acquire);\n        let counter = CCT.free_run().read().tmr();\n        calc_now(period, counter)\n    }\n\n    fn schedule_wake(&self, at: u64, waker: &core::task::Waker) {\n        critical_section::with(|cs| {\n            let mut queue = self.queue.borrow(cs).borrow_mut();\n\n            if queue.schedule_wake(at, waker) {\n                let mut next = queue.next_expiration(self.now());\n                while !self.set_alarm(cs, next) {\n                    next = queue.next_expiration(self.now());\n                }\n            }\n        })\n    }\n}\n\n#[cfg(feature = \"rt\")]\n#[interrupt]\nfn CCT() {\n    ECIA.src18().write_value(1 << 20);\n    DRIVER.next_period();\n}\n\n#[cfg(feature = \"rt\")]\n#[interrupt]\nfn CCT_CMP0() {\n    ECIA.src18().write_value(1 << 27);\n    DRIVER.next_period();\n}\n\n#[cfg(feature = \"rt\")]\n#[interrupt]\nfn CCT_CMP1() {\n    ECIA.src18().write_value(1 << 28);\n    critical_section::with(|cs| DRIVER.trigger_alarm(cs));\n}\n\npub(crate) fn init() {\n    DRIVER.init()\n}\n"
  },
  {
    "path": "embassy-microchip/src/uart.rs",
    "content": "//! UART driver.\n\nuse core::future::poll_fn;\nuse core::marker::PhantomData;\nuse core::sync::atomic::{AtomicU8, Ordering};\nuse core::task::Poll;\n\nuse embassy_hal_internal::{Peri, PeripheralType};\nuse embassy_sync::waitqueue::AtomicWaker;\nuse mec17xx_pac::uart0::regs::DataLsr;\n\nuse crate::gpio::{AnyPin, Pin, SealedPin};\nuse crate::interrupt::typelevel::{Binding, Interrupt};\nuse crate::pac::uart0::Uart0;\nuse crate::{interrupt, pac, peripherals};\n\n// The datasheet does not appear to specify this, but the 16550 has a 16-byte FIFO\n// and empirical testing confirms this is also the case for the MEC UART.\n//\n// This is useful to know since UART does not support DMA, allowing us to interrupt less frequently.\nconst FIFO_SZ: usize = 16;\n\n// The HW gives us a convenient scratch register which we use to hold interrupt flags\nmod int_flag {\n    pub(crate) const RX_AVAILABLE: u8 = 1 << 0;\n    pub(crate) const TIMEOUT: u8 = 1 << 1;\n    pub(crate) const TX_EMPTY: u8 = 1 << 2;\n}\n\n/// UART interrupt handler.\npub struct InterruptHandler<T: Instance> {\n    _phantom: PhantomData<T>,\n}\n\nimpl<T: Instance> interrupt::typelevel::Handler<T::Interrupt> for InterruptHandler<T> {\n    unsafe fn on_interrupt() {\n        let intid = InterruptType::try_from(T::info().reg.data().int_id().read().intid());\n        match intid {\n            // In case of RxAvailable, We need a way to know that the FIFO level is triggered in task,\n            // but also need to disable the interrupt to clear it (which clears it in intid).\n            //\n            // In case of LineStatus, We don't use a separate flag from RxAvailable because\n            // the RX task will keep reading bytes until it hits the byte that produced the error\n            Ok(InterruptType::LineStatus) | Ok(InterruptType::RxAvailable) => {\n                T::info().reg.data().scr().modify(|w| *w |= int_flag::RX_AVAILABLE);\n                T::info().reg.data().ien().modify(|w| {\n                    w.set_elsi(false);\n                    w.set_erdai(false);\n                });\n                T::info().rx_waker.wake();\n            }\n\n            // There does not appear to be a way of disabling this interrupt, and it is always\n            // enabled if RX available interrupt is enabled. The datasheet shows this as equal\n            // priority to RxAvailable, but it seems to have higher priority because even if the\n            // FIFO trigger level is reached, we will always see this interrupt ID until a byte\n            // is read from FIFO.\n            //\n            // This is annoying because we want to be able to batch read to the set RX FIFO\n            // level trigger, but if our input source is slow (such as person typing on keyboard),\n            // this will almost always trigger after the first byte is received (since we want\n            // to wait until the FIFO trigger is reached until reading bytes from FIFO).\n            //\n            // Alas we have to deal with this, which the RX task will do by falling back into\n            // interrupting for every byte.\n            Ok(InterruptType::CharacterTimeout) => {\n                T::info().reg.data().scr().modify(|w| *w |= int_flag::TIMEOUT);\n                T::info().reg.data().ien().modify(|w| {\n                    w.set_elsi(false);\n                    w.set_erdai(false);\n                });\n                T::info().rx_waker.wake();\n            }\n\n            // Note: We mark TX empty flag because although we could check LSR for tx empty\n            // in the TX task, doing so will clear error bits in LSR.\n            //\n            // This is important because there is a potential race where even though Line Status\n            // interrupt is higher priority, we disable it above so the interrupt could trigger\n            // again, waking the TX task, which might go before the RX task, and it would end up\n            // accidentally clearing LSR error bits before RX task can see them.\n            Ok(InterruptType::TxEmpty) => {\n                T::info().reg.data().scr().modify(|w| *w |= int_flag::TX_EMPTY);\n                T::info().reg.data().ien().modify(|w| w.set_ethrei(false));\n                T::info().tx_waker.wake();\n            }\n\n            // Unknown/unsupported interrupt type, so ignore\n            _ => (),\n        }\n    }\n}\n\nenum InterruptType {\n    LineStatus,\n    RxAvailable,\n    CharacterTimeout,\n    TxEmpty,\n    // MODEM is not currently supported\n    _Modem,\n}\n\nimpl TryFrom<u8> for InterruptType {\n    type Error = ();\n    fn try_from(value: u8) -> Result<Self, Self::Error> {\n        // Note: This always assumes we are in FIFO mode (the driver does not support non-FIFO mode)\n        Ok(match value {\n            0b011 => Self::LineStatus,\n            0b010 => Self::RxAvailable,\n            0b110 => Self::CharacterTimeout,\n            0b001 => Self::TxEmpty,\n            0b000 => Self::_Modem,\n            _ => Err(())?,\n        })\n    }\n}\n\nenum RxFifoTrigger {\n    _1,\n    _4,\n    _8,\n    _14,\n}\n\nimpl From<RxFifoTrigger> for u8 {\n    // Note: These are the bit representations in the register\n    fn from(trigger: RxFifoTrigger) -> Self {\n        match trigger {\n            RxFifoTrigger::_1 => 0b00,\n            RxFifoTrigger::_4 => 0b01,\n            RxFifoTrigger::_8 => 0b10,\n            RxFifoTrigger::_14 => 0b11,\n        }\n    }\n}\n\n/// Data word length.\n#[derive(Debug, Copy, Clone, Eq, PartialEq)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub enum WordLen {\n    /// 5 bits.\n    _5,\n    /// 6 bits.\n    _6,\n    /// 7 bits.\n    _7,\n    /// 8 bits.\n    _8,\n}\n\nimpl WordLen {\n    fn as_bits(&self) -> u8 {\n        match self {\n            Self::_5 => 0b00,\n            Self::_6 => 0b01,\n            Self::_7 => 0b10,\n            Self::_8 => 0b11,\n        }\n    }\n}\n\n/// Parity selection.\n#[derive(Debug, Copy, Clone, Eq, PartialEq)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub enum Parity {\n    /// No parity bit.\n    None,\n    /// Odd parity.\n    Odd,\n    /// Even parity.\n    Even,\n    /// Mark parity.\n    Mark,\n    /// Space parity.\n    Space,\n}\n\nimpl Parity {\n    fn as_par_sel(&self) -> bool {\n        matches!(*self, Self::Even | Self::Space)\n    }\n\n    fn as_stick_par(&self) -> bool {\n        matches!(*self, Self::Mark | Self::Space)\n    }\n}\n\n/// Baud clock source.\n#[derive(Debug, Copy, Clone, Eq, PartialEq)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub enum ClkSrc {\n    /// External clock (in Hz).\n    External(u32),\n    /// Internal 1.8432 MHz clock.\n    _1_8432MHz,\n    /// Internal 48 MHz clock.\n    _48MHz,\n}\n\nimpl ClkSrc {\n    fn as_bits(&self) -> bool {\n        matches!(self, ClkSrc::External(_))\n    }\n\n    fn as_baud_clk_sel_bits(&self) -> u8 {\n        // If using external clock source this is just a \"don't care\" bit\n        (*self == Self::_48MHz) as u8\n    }\n}\n\nimpl From<ClkSrc> for u32 {\n    fn from(clk_src: ClkSrc) -> Self {\n        match clk_src {\n            ClkSrc::External(f) => f,\n            ClkSrc::_1_8432MHz => 1_843_200,\n            ClkSrc::_48MHz => 48_000_000,\n        }\n    }\n}\n\n/// UART configuration.\n#[derive(Debug, Copy, Clone, Eq, PartialEq)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub struct Config {\n    /// The clock source which the baud clock is derived from.\n    ///\n    /// This will affect which baud rates can be accurately represented.\n    pub clk_src: ClkSrc,\n    /// Baudrate in bits per second (BPS).\n    pub baudrate: u32,\n    /// Data word length.\n    ///\n    /// Note: If multi stop bits is enabled, the number of stop bits corresponds to chosen word length.\n    pub word_len: WordLen,\n    /// Enable multiple stop bits.\n    ///\n    /// If enabled, the number of stop bits will be dependent on chosen word length.\n    ///\n    /// If disabled, the number of stop bits will always be 1.\n    pub use_multi_stop: bool,\n    /// Parity selection.\n    pub parity: Parity,\n    /// Enable inverted polarity on RX and TX pins.\n    pub invert_polarity: bool,\n}\n\nimpl Default for Config {\n    fn default() -> Self {\n        Self {\n            clk_src: ClkSrc::_1_8432MHz,\n            baudrate: 115_200,\n            word_len: WordLen::_8,\n            use_multi_stop: false,\n            parity: Parity::None,\n            invert_polarity: false,\n        }\n    }\n}\n\n/// UART error.\n#[derive(Debug, Copy, Clone, Eq, PartialEq)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub enum Error {\n    /// A baud rate was supplied which can not be represenetd based on chosen clock source.\n    InvalidBaud,\n    /// RX FIFO overrun occurred.\n    Overrun,\n    /// RX parity error occurred.\n    Parity,\n    /// RX frame error occurred.\n    Frame,\n    /// RX break interrupt occurred.\n    Break,\n}\n\n/// UART RX error.\n///\n/// Contains the [`Error`] along with number of valid bytes read before error occurred.\n#[derive(Debug, Clone, Copy)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub struct RxError {\n    /// Bytes read before error occurred.\n    pub bytes_read: usize,\n    /// The actual error encountered.\n    pub err: Error,\n}\n\nimpl core::fmt::Display for RxError {\n    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {\n        match self.err {\n            Error::InvalidBaud => write!(\n                f,\n                \"A baud rate was supplied which can not be represenetd based on chosen clock source.\"\n            ),\n            Error::Overrun => write!(f, \"RX FIFO overrun occurred.\"),\n            Error::Parity => write!(f, \"RX parity error occurred.\"),\n            Error::Frame => write!(f, \"RX frame error occurred.\"),\n            Error::Break => write!(f, \"RX break interrupt occurred.\"),\n        }\n    }\n}\n\nimpl core::error::Error for RxError {}\n\nfn init<'d, T: Instance>(\n    _rx_pin: Option<&Peri<'d, AnyPin>>,\n    _tx_pin: Option<&Peri<'d, AnyPin>>,\n    config: Config,\n) -> Result<(), Error> {\n    // Ensure baudrate is nonzero\n    let divisor = u32::from(config.clk_src)\n        .checked_div(16 * config.baudrate)\n        .ok_or(Error::InvalidBaud)? as u16;\n\n    // Divisor can be 15 bits max (the 16th bit is used to select clock source)\n    if divisor > 0x7FFF {\n        return Err(Error::InvalidBaud);\n    };\n\n    // Configure pins\n    critical_section::with(|_| {\n        if let Some(rx) = _rx_pin {\n            rx.regs().ctrl1.modify(|w| {\n                w.set_mux_ctrl(pac::Function::F1);\n                w.set_dir(pac::Dir::INPUT);\n                w.set_inp_dis(false);\n                w.set_pu_pd(pac::Pull::NONE);\n            })\n        }\n        if let Some(tx) = _tx_pin {\n            tx.regs().ctrl1.modify(|w| {\n                w.set_mux_ctrl(pac::Function::F1);\n                w.set_dir(pac::Dir::OUTPUT);\n                w.set_inp_dis(true);\n                w.set_pu_pd(pac::Pull::NONE);\n            })\n        }\n    });\n\n    // Set config\n    T::info().reg.data().cfg_sel().write(|w| {\n        w.set_clk_src(config.clk_src.as_bits());\n        w.set_polar(config.invert_polarity);\n    });\n\n    // Set line control with latched DLAB since we modify baud registers next\n    T::info().reg.data().lcr().write(|w| {\n        w.set_word_len(config.word_len.as_bits());\n        w.set_stop_bits(config.use_multi_stop);\n        w.set_en_par(config.parity != Parity::None);\n        if config.parity != Parity::None {\n            w.set_par_sel(config.parity.as_par_sel());\n            w.set_stick_par(config.parity.as_stick_par());\n        }\n        w.set_dlab(true);\n    });\n\n    // Set baud rate divisor MSB\n    // Note: bit 7 determines the baud clock source,\n    // but the PAC doesn't appear to have a type with fields for this reg\n    T::info()\n        .reg\n        .dlab()\n        .baudrt_msb()\n        .write(|w| *w = (config.clk_src.as_baud_clk_sel_bits() << 7) | (divisor >> 8) as u8);\n    // Set baud rate divisor LSB\n    T::info().reg.dlab().baudrt_lsb().write(|w| *w = divisor as u8);\n\n    // Unlatch DLAB, remaining unlatched for rest of instance's lifetime\n    T::info().reg.dlab().lcr().modify(|w| w.set_dlab(false));\n\n    // Enable and clear FIFO\n    T::info().reg.data().fifo_cr().write(|w| {\n        w.set_exrf(true);\n        w.set_clr_recv_fifo(true);\n        w.set_clr_xmit_fifo(true);\n    });\n\n    // Ensure scratch is cleared which we use for interrupt flags\n    T::info().reg.data().scr().write(|w| *w = 0);\n\n    // Enable UART\n    T::info().reg.data().activate().write(|w| *w = 1);\n\n    Ok(())\n}\n\nfn interrupt_en<T: Instance>() {\n    T::info().reg.data().mcr().modify(|w| w.set_out2(true));\n\n    // Unmask interrupt\n    pac::ECIA.en_set15().write_value(1 << T::irq_bit());\n    T::Interrupt::unpend();\n    // SAFETY: We have sole control of UART interrupts so this is safe to do\n    unsafe { T::Interrupt::enable() };\n}\n\nfn drop_rx_tx(info: &'static Info) {\n    // Only disable UART once both UartRx and UartTx have been dropped\n    if info.rx_tx_refcount.fetch_sub(1, Ordering::AcqRel) == 1 {\n        info.reg.data().mcr().modify(|w| w.set_out2(false));\n        info.reg.data().activate().write(|w| *w = 0);\n    }\n}\n\n/// UART driver.\npub struct Uart<'d, M: Mode> {\n    rx: UartRx<'d, M>,\n    tx: UartTx<'d, M>,\n}\n\nimpl<'d, M: Mode> Uart<'d, M> {\n    fn new_inner<T: Instance>(\n        _rx_pin: Peri<'d, impl RxPin<T>>,\n        _tx_pin: Peri<'d, impl TxPin<T>>,\n        config: Config,\n    ) -> Result<Self, Error> {\n        let rx_pin = _rx_pin.into();\n        let tx_pin = _tx_pin.into();\n        init::<T>(Some(&rx_pin), Some(&tx_pin), config)?;\n        let rx = UartRx::new_inner::<T>(rx_pin);\n        let tx = UartTx::new_inner::<T>(tx_pin);\n        Ok(Self { rx, tx })\n    }\n\n    /// Reads bytes from RX FIFO until buffer is full, blocking if empty.\n    ///\n    /// # Errors\n    ///\n    /// Returns [`RxError`] if error occurred during read.\n    pub fn blocking_read(&mut self, buf: &mut [u8]) -> Result<(), RxError> {\n        self.rx.blocking_read(buf)\n    }\n\n    /// Writes bytes to TX FIFO, blocking if full.\n    pub fn blocking_write(&mut self, bytes: &[u8]) {\n        self.tx.blocking_write(bytes)\n    }\n\n    /// Blocks until both the transmit holding and shift registers are empty.\n    pub fn blocking_flush(&mut self) {\n        self.tx.blocking_flush()\n    }\n\n    /// Splits the UART driver into separate [`UartRx`] and [`UartTx`] drivers.\n    ///\n    /// Helpful for sharing the UART among receiver/transmitter tasks.\n    pub fn split(self) -> (UartRx<'d, M>, UartTx<'d, M>) {\n        (self.rx, self.tx)\n    }\n\n    /// Splits the UART driver into separate [`UartRx`] and [`UartTx`] drivers by mutable reference.\n    ///\n    /// Helpful for sharing the UART among receiver/transmitter tasks without destroying the original [`Uart`] instance.\n    pub fn split_ref(&mut self) -> (&mut UartRx<'d, M>, &mut UartTx<'d, M>) {\n        (&mut self.rx, &mut self.tx)\n    }\n}\n\nimpl<'d> Uart<'d, Blocking> {\n    /// Create a new blocking UART driver instance with given configuration.\n    ///\n    /// # Errors\n    ///\n    /// Returns [`Error::InvalidBaud`] if the supplied baud rate can not be represented by\n    /// given clock source.\n    pub fn new_blocking<T: Instance>(\n        _peri: Peri<'d, T>,\n        _rx_pin: Peri<'d, impl RxPin<T>>,\n        _tx_pin: Peri<'d, impl TxPin<T>>,\n        config: Config,\n    ) -> Result<Self, Error> {\n        Self::new_inner(_rx_pin, _tx_pin, config)\n    }\n}\n\nimpl<'d> Uart<'d, Async> {\n    /// Create a new async UART driver instance with given configuration.\n    ///\n    /// # Errors\n    ///\n    /// Returns [`Error::InvalidBaud`] if the supplied baud rate can not be represented by\n    /// given clock source.\n    pub fn new_async<T: Instance>(\n        _peri: Peri<'d, T>,\n        _rx_pin: Peri<'d, impl RxPin<T>>,\n        _tx_pin: Peri<'d, impl TxPin<T>>,\n        _irq: impl Binding<T::Interrupt, InterruptHandler<T>>,\n        config: Config,\n    ) -> Result<Self, Error> {\n        let uart = Self::new_inner(_rx_pin, _tx_pin, config)?;\n        interrupt_en::<T>();\n        Ok(uart)\n    }\n\n    /// Reads bytes from RX FIFO until buffer is full.\n    ///\n    /// # Errors\n    ///\n    /// Returns [`RxError`] if error occurred during read.\n    pub fn read(&mut self, buf: &mut [u8]) -> impl Future<Output = Result<(), RxError>> {\n        self.rx.read(buf)\n    }\n\n    /// Writes bytes to TX FIFO.\n    pub fn write(&mut self, bytes: &[u8]) -> impl Future<Output = ()> {\n        self.tx.write(bytes)\n    }\n\n    /// Waits until both the transmit holding and shift registers are empty.\n    pub fn flush(&mut self) -> impl Future<Output = ()> {\n        self.tx.flush()\n    }\n}\n\n/// RX-only UART driver.\npub struct UartRx<'d, M: Mode> {\n    info: &'static Info,\n    _rx_pin: Peri<'d, AnyPin>,\n    _phantom: PhantomData<&'d M>,\n}\n\nimpl<'d, M: Mode> UartRx<'d, M> {\n    fn new_inner<T: Instance>(_rx_pin: Peri<'d, AnyPin>) -> Self {\n        T::info().rx_tx_refcount.fetch_add(1, Ordering::AcqRel);\n\n        Self {\n            info: T::info(),\n            _rx_pin,\n            _phantom: PhantomData,\n        }\n    }\n\n    fn read_inner(&mut self, lsr: DataLsr) -> Result<u8, Error> {\n        // An overrun error is not associated with particular character like others\n        // We just bail out early, and it might be hard to recover correctly from this\n        //\n        // The onus will likely need to be on caller to handle an overrun how they see fit\n        if lsr.overrun() {\n            return Err(Error::Overrun);\n        }\n\n        // Read byte first (because even if the byte produces an error we still want to drain it)\n        let byte = self.info.reg.data().rx_dat().read();\n\n        // Note: We don't make use of bit 7 (FIFO_ERROR) because it just tells us if one of the\n        // below errors is somewhere in the FIFO, but we are checking the specific byte at top of\n        // the FIFO for an error\n\n        // And only return it if there is no error\n        if lsr.pe() {\n            Err(Error::Parity)\n        } else if lsr.frame_err() {\n            Err(Error::Frame)\n        } else if lsr.brk_intr() {\n            Err(Error::Break)\n        } else {\n            Ok(byte)\n        }\n    }\n\n    fn nb_read_byte(&mut self) -> Result<u8, Error> {\n        let lsr = self.info.reg.data().lsr().read();\n        self.read_inner(lsr)\n    }\n\n    fn blocking_read_byte(&mut self) -> Result<u8, Error> {\n        // LSR clears error bits on read so want to make sure only read it once after data ready\n        let lsr = loop {\n            let lsr = self.info.reg.data().lsr().read();\n            if lsr.data_ready() {\n                break lsr;\n            }\n        };\n\n        self.read_inner(lsr)\n    }\n\n    /// Reads bytes from RX FIFO until buffer is full, blocking if empty.\n    ///\n    /// # Errors\n    ///\n    /// Returns [`RxError`] if error occurred during read.\n    pub fn blocking_read(&mut self, buf: &mut [u8]) -> Result<(), RxError> {\n        // If we encounter an error, return the number of valid bytes read up until error occurred\n        for (bytes_read, byte) in buf.iter_mut().enumerate() {\n            *byte = self.blocking_read_byte().map_err(|err| RxError { bytes_read, err })?;\n        }\n\n        Ok(())\n    }\n}\n\nimpl<'d> UartRx<'d, Blocking> {\n    /// Create a new blocking RX-only UART driver instance with given configuration.\n    ///\n    /// # Errors\n    ///\n    /// Returns [`Error::InvalidBaud`] if the supplied baud rate can not be represented by\n    /// given clock source.\n    pub fn new_blocking<T: Instance>(\n        _peri: Peri<'d, T>,\n        _rx_pin: Peri<'d, impl RxPin<T>>,\n        config: Config,\n    ) -> Result<Self, Error> {\n        let rx_pin = _rx_pin.into();\n        init::<T>(Some(&rx_pin), None, config)?;\n        Ok(Self::new_inner::<T>(rx_pin))\n    }\n}\n\nimpl<'d> UartRx<'d, Async> {\n    async fn wait_rx_ready(&mut self) -> bool {\n        poll_fn(|cx| {\n            self.info.rx_waker.register(cx.waker());\n            let int_flags = self.info.reg.data().scr().read();\n\n            if int_flags & int_flag::TIMEOUT != 0 {\n                critical_section::with(|_| self.info.reg.data().scr().modify(|w| *w &= !int_flag::TIMEOUT));\n                // Indicates a byte is ready to read, but we didn't trigger the FIFO level before timeout\n                Poll::Ready(false)\n            } else if int_flags & int_flag::RX_AVAILABLE != 0 {\n                critical_section::with(|_| self.info.reg.data().scr().modify(|w| *w &= !int_flag::RX_AVAILABLE));\n                Poll::Ready(true)\n            } else {\n                critical_section::with(|_| {\n                    self.info.reg.data().ien().modify(|w| {\n                        w.set_elsi(true);\n                        w.set_erdai(true);\n                    })\n                });\n                Poll::Pending\n            }\n        })\n        .await\n    }\n\n    fn set_fifo_trigger(&mut self, trigger: RxFifoTrigger) {\n        self.info.reg.data().fifo_cr().write(|w| {\n            // Must always set EXRF when setting other bits in the reg, even if previously set\n            w.set_exrf(true);\n            w.set_recv_fifo_trig_lvl(trigger.into());\n        });\n    }\n\n    async fn read_byte(&mut self) -> Result<u8, Error> {\n        // LSR clears error bits on read so want to make sure only read it once when data ready then check bits\n        let lsr = {\n            let lsr = self.info.reg.data().lsr().read();\n            if !lsr.data_ready() {\n                let _ = self.wait_rx_ready().await;\n                self.info.reg.data().lsr().read()\n            } else {\n                lsr\n            }\n        };\n\n        self.read_inner(lsr)\n    }\n\n    async fn read_chunk(&mut self, chunk: &mut [u8], bytes_read_start: usize) -> Result<(), RxError> {\n        self.set_fifo_trigger(RxFifoTrigger::_1);\n        for (bytes_read, byte) in chunk.iter_mut().enumerate() {\n            *byte = self.read_byte().await.map_err(|err| RxError {\n                bytes_read: bytes_read_start + bytes_read,\n                err,\n            })?;\n        }\n        Ok(())\n    }\n\n    async fn read_chunk_batched(&mut self, chunk: &mut [u8], bytes_read_start: usize) -> Result<(), RxError> {\n        // If our FIFO level was reached without timeout, we can read all the bytes in one go,\n        // but still need to check each byte for an error\n        if self.wait_rx_ready().await {\n            for (bytes_read, byte) in chunk.iter_mut().enumerate() {\n                *byte = self.nb_read_byte().map_err(|err| RxError {\n                    bytes_read: bytes_read_start + bytes_read,\n                    err,\n                })?;\n            }\n\n        // However, if a timeout occured, our assumptions about the number of bytes in the FIFO\n        // no longer holds (since we have to read a byte to clear the timeout interrupt), meaning\n        // we have no choice but to unfortunately fall back on byte-by-byte interrupts\n        } else {\n            self.read_chunk(chunk, bytes_read_start).await?;\n        }\n\n        Ok(())\n    }\n\n    async fn read_chunks<const N: usize>(\n        &mut self,\n        chunks: &mut [[u8; N]],\n        trigger: RxFifoTrigger,\n        bytes_read_start: usize,\n    ) -> Result<(), RxError> {\n        self.set_fifo_trigger(trigger);\n        for (i, chunk) in chunks.iter_mut().enumerate() {\n            self.read_chunk_batched(chunk, bytes_read_start + (N * i)).await?;\n        }\n        Ok(())\n    }\n\n    /// Create a new async RX-only UART driver instance with given configuration.\n    ///\n    /// # Errors\n    ///\n    /// Returns [`Error::InvalidBaud`] if the supplied baud rate can not be represented by\n    /// given clock source.\n    pub fn new_async<T: Instance>(\n        _peri: Peri<'d, T>,\n        _rx_pin: Peri<'d, impl RxPin<T>>,\n        _irq: impl Binding<T::Interrupt, InterruptHandler<T>>,\n        config: Config,\n    ) -> Result<Self, Error> {\n        let rx_pin = _rx_pin.into();\n        init::<T>(Some(&rx_pin), None, config)?;\n        interrupt_en::<T>();\n        Ok(Self::new_inner::<T>(rx_pin))\n    }\n\n    /// Reads bytes from RX FIFO until buffer is full.\n    ///\n    /// # Errors\n    ///\n    /// Returns [`RxError`] if error occurred during read.\n    pub async fn read(&mut self, buf: &mut [u8]) -> Result<(), RxError> {\n        // The idea here is that the HW provides us 4 FIFO level triggers (14, 8, 4, 1),\n        // so to minimize the number of interrupts, we split the buffer up greedily into chunks\n        // of the highest trigger level we can, and then know when the interrupt is triggered,\n        // we can read that number of bytes in one shot.\n        const TRIG_14: usize = 14;\n        const TRIG_8: usize = 8;\n        const TRIG_4: usize = 4;\n\n        let (c14, rem) = buf.as_chunks_mut::<TRIG_14>();\n        let (c8, rem) = rem.as_chunks_mut::<TRIG_8>();\n        let (c4, c1) = rem.as_chunks_mut::<TRIG_4>();\n\n        // We keep track of total number of valid bytes read, so in case of error,\n        // we can return this number to the caller\n        let mut bytes_read = 0;\n\n        self.read_chunks(c14, RxFifoTrigger::_14, bytes_read).await?;\n        bytes_read += c14.len() * TRIG_14;\n\n        self.read_chunks(c8, RxFifoTrigger::_8, bytes_read).await?;\n        bytes_read += c8.len() * TRIG_8;\n\n        self.read_chunks(c4, RxFifoTrigger::_4, bytes_read).await?;\n        bytes_read += c4.len() * TRIG_4;\n\n        // The last chunk is smaller than 4, so we have no choice but to interrupt for each byte\n        self.read_chunk(c1, bytes_read).await?;\n\n        Ok(())\n    }\n}\n\nimpl<'d, M: Mode> Drop for UartRx<'d, M> {\n    fn drop(&mut self) {\n        // Revisit: Add API for disabling pins\n        drop_rx_tx(self.info);\n    }\n}\n\n/// TX-only UART driver.\npub struct UartTx<'d, M: Mode> {\n    info: &'static Info,\n    _tx_pin: Peri<'d, AnyPin>,\n    _phantom: PhantomData<&'d M>,\n}\n\nimpl<'d, M: Mode> UartTx<'d, M> {\n    fn new_inner<T: Instance>(_tx_pin: Peri<'d, AnyPin>) -> Self {\n        T::info().rx_tx_refcount.fetch_add(1, Ordering::AcqRel);\n\n        Self {\n            info: T::info(),\n            _tx_pin,\n            _phantom: PhantomData,\n        }\n    }\n\n    fn write_chunk(&mut self, chunk: &[u8]) {\n        for byte in chunk {\n            self.info.reg.data().tx_dat().write(|w| *w = *byte);\n        }\n    }\n\n    /// Writes bytes to TX FIFO, blocking if full.\n    pub fn blocking_write(&mut self, buf: &[u8]) {\n        for chunk in buf.chunks(FIFO_SZ) {\n            while !self.info.reg.data().lsr().read().trans_empty() {}\n            self.write_chunk(chunk);\n        }\n    }\n\n    /// Blocks until both the transmit holding and shift registers are empty.\n    pub fn blocking_flush(&mut self) {\n        // Note: The register for some reason is named \"Transmit Error\" but it really\n        // reflects the status of both the transmit and shift registers aka \"Busy\" status\n        while !self.info.reg.data().lsr().read().trans_err() {}\n    }\n}\n\nimpl<'d> UartTx<'d, Blocking> {\n    /// Create a new blocking TX-only UART driver instance with given configuration.\n    ///\n    /// # Errors\n    ///\n    /// Returns [`Error::InvalidBaud`] if the supplied baud rate can not be represented by\n    /// given clock source.\n    pub fn new_blocking<T: Instance>(\n        _peri: Peri<'d, T>,\n        _tx_pin: Peri<'d, impl TxPin<T>>,\n        config: Config,\n    ) -> Result<Self, Error> {\n        let tx_pin = _tx_pin.into();\n        init::<T>(None, Some(&tx_pin), config)?;\n        Ok(Self::new_inner::<T>(tx_pin))\n    }\n}\n\nimpl<'d> UartTx<'d, Async> {\n    async fn wait_tx_empty(&mut self) {\n        poll_fn(|cx| {\n            self.info.tx_waker.register(cx.waker());\n            if self.info.reg.data().scr().read() & int_flag::TX_EMPTY != 0 {\n                critical_section::with(|_| self.info.reg.data().scr().modify(|w| *w &= !int_flag::TX_EMPTY));\n                Poll::Ready(())\n            } else {\n                critical_section::with(|_| self.info.reg.data().ien().modify(|w| w.set_ethrei(true)));\n                Poll::Pending\n            }\n        })\n        .await\n    }\n\n    /// Create a new async TX-only UART driver instance with given configuration.\n    ///\n    /// # Errors\n    ///\n    /// Returns [`Error::InvalidBaud`] if the supplied baud rate can not be represented by\n    /// given clock source.\n    pub fn new_async<T: Instance>(\n        _peri: Peri<'d, T>,\n        _tx_pin: Peri<'d, impl TxPin<T>>,\n        _irq: impl Binding<T::Interrupt, InterruptHandler<T>>,\n        config: Config,\n    ) -> Result<Self, Error> {\n        let tx_pin = _tx_pin.into();\n        init::<T>(None, Some(&tx_pin), config)?;\n        interrupt_en::<T>();\n        Ok(Self::new_inner::<T>(tx_pin))\n    }\n\n    /// Writes bytes to TX FIFO.\n    pub async fn write(&mut self, buf: &[u8]) {\n        for chunk in buf.chunks(FIFO_SZ) {\n            self.wait_tx_empty().await;\n            self.write_chunk(chunk);\n        }\n    }\n\n    /// Waits until both the transmit holding and shift registers are empty.\n    pub async fn flush(&mut self) {\n        // We can wait for an interrupt to know when the TX FIFO is empty,\n        // but there does not appear to be an interrupt for when the TX shift reg is empty,\n        // so have to block\n        self.wait_tx_empty().await;\n        self.blocking_flush();\n    }\n}\n\nimpl<'d, M: Mode> Drop for UartTx<'d, M> {\n    fn drop(&mut self) {\n        // Revisit: Add API for disabling pins\n        drop_rx_tx(self.info);\n    }\n}\n\nstruct Info {\n    reg: Uart0,\n    rx_tx_refcount: AtomicU8,\n    rx_waker: AtomicWaker,\n    tx_waker: AtomicWaker,\n}\n\ntrait SealedMode {}\n\n/// Blocking mode.\npub struct Blocking;\nimpl SealedMode for Blocking {}\nimpl Mode for Blocking {}\n\n/// Async mode.\npub struct Async;\nimpl SealedMode for Async {}\nimpl Mode for Async {}\n\n/// Driver mode.\n#[allow(private_bounds)]\npub trait Mode: SealedMode {}\n\ntrait SealedInstance {\n    fn info() -> &'static Info;\n    fn irq_bit() -> usize;\n}\n\n/// UART instance trait.\n#[allow(private_bounds)]\npub trait Instance: SealedInstance + PeripheralType + 'static + Send {\n    type Interrupt: interrupt::typelevel::Interrupt;\n}\n\nmacro_rules! impl_instance {\n    ($peri:ident, $bit:expr) => {\n        impl SealedInstance for peripherals::$peri {\n            #[inline(always)]\n            fn info() -> &'static Info {\n                static INFO: Info = Info {\n                    reg: pac::$peri,\n                    rx_tx_refcount: AtomicU8::new(0),\n                    rx_waker: AtomicWaker::new(),\n                    tx_waker: AtomicWaker::new(),\n                };\n                &INFO\n            }\n\n            #[inline(always)]\n            fn irq_bit() -> usize {\n                $bit\n            }\n        }\n\n        impl Instance for peripherals::$peri {\n            type Interrupt = crate::interrupt::typelevel::$peri;\n        }\n    };\n}\n\nimpl_instance!(UART0, 0);\nimpl_instance!(UART1, 1);\n\n/// A pin that can be configured as a UART RX pin.\npub trait RxPin<T: Instance>: Pin + PeripheralType {}\n\n/// A pin that can be configured as a UART TX pin.\npub trait TxPin<T: Instance>: Pin + PeripheralType {}\n\nmacro_rules! impl_pin {\n    ($function:ident, $peri:ident, $($pin:ident),*) => {\n        $(\n            impl $function<peripherals::$peri> for peripherals::$pin {}\n        )*\n    }\n}\n\nimpl_pin!(RxPin, UART0, GPIO105);\nimpl_pin!(TxPin, UART0, GPIO104);\nimpl_pin!(RxPin, UART1, GPIO171, GPIO255);\nimpl_pin!(TxPin, UART1, GPIO170);\n\nimpl<'d, M: Mode> embedded_hal_02::blocking::serial::Write<u8> for Uart<'d, M> {\n    type Error = core::convert::Infallible;\n\n    fn bwrite_all(&mut self, buffer: &[u8]) -> Result<(), Self::Error> {\n        self.blocking_write(buffer);\n        Ok(())\n    }\n\n    fn bflush(&mut self) -> Result<(), Self::Error> {\n        self.blocking_flush();\n        Ok(())\n    }\n}\n\nimpl<'d, M: Mode> embedded_hal_02::blocking::serial::Write<u8> for UartTx<'d, M> {\n    type Error = core::convert::Infallible;\n\n    fn bwrite_all(&mut self, buffer: &[u8]) -> Result<(), Self::Error> {\n        self.blocking_write(buffer);\n        Ok(())\n    }\n\n    fn bflush(&mut self) -> Result<(), Self::Error> {\n        self.blocking_flush();\n        Ok(())\n    }\n}\n\nimpl embedded_io::Error for RxError {\n    fn kind(&self) -> embedded_io::ErrorKind {\n        // Overrun error may need to be handled differently than other errors,\n        // but there isn't a perfect `ErrorKind` match for it\n        if self.err == Error::Overrun {\n            embedded_io::ErrorKind::Other\n        } else {\n            embedded_io::ErrorKind::InvalidData\n        }\n    }\n}\n\nimpl<'d, M: Mode> embedded_io::ErrorType for Uart<'d, M> {\n    type Error = RxError;\n}\n\nimpl<'d, M: Mode> embedded_io::Read for Uart<'d, M> {\n    fn read(&mut self, buf: &mut [u8]) -> Result<usize, Self::Error> {\n        self.blocking_read(buf).map(|_| buf.len())\n    }\n}\n\nimpl<'d> embedded_io_async::Read for Uart<'d, Async> {\n    async fn read(&mut self, buf: &mut [u8]) -> Result<usize, Self::Error> {\n        self.read(buf).await.map(|_| buf.len())\n    }\n}\n\nimpl<'d, M: Mode> embedded_io::Write for Uart<'d, M> {\n    fn write(&mut self, buf: &[u8]) -> Result<usize, Self::Error> {\n        self.blocking_write(buf);\n        Ok(buf.len())\n    }\n\n    fn flush(&mut self) -> Result<(), Self::Error> {\n        self.blocking_flush();\n        Ok(())\n    }\n}\n\nimpl<'d> embedded_io_async::Write for Uart<'d, Async> {\n    async fn write(&mut self, buf: &[u8]) -> Result<usize, Self::Error> {\n        self.write(buf).await;\n        Ok(buf.len())\n    }\n\n    async fn flush(&mut self) -> Result<(), Self::Error> {\n        self.flush().await;\n        Ok(())\n    }\n}\n\nimpl<'d, M: Mode> embedded_io::ErrorType for UartTx<'d, M> {\n    type Error = core::convert::Infallible;\n}\n\nimpl<'d, M: Mode> embedded_io::Write for UartTx<'d, M> {\n    fn write(&mut self, buf: &[u8]) -> Result<usize, Self::Error> {\n        self.blocking_write(buf);\n        Ok(buf.len())\n    }\n\n    fn flush(&mut self) -> Result<(), Self::Error> {\n        self.blocking_flush();\n        Ok(())\n    }\n}\n\nimpl<'d> embedded_io_async::Write for UartTx<'d, Async> {\n    async fn write(&mut self, buf: &[u8]) -> Result<usize, Self::Error> {\n        self.write(buf).await;\n        Ok(buf.len())\n    }\n\n    async fn flush(&mut self) -> Result<(), Self::Error> {\n        self.flush().await;\n        Ok(())\n    }\n}\n\nimpl<'d, M: Mode> embedded_io::ErrorType for UartRx<'d, M> {\n    type Error = RxError;\n}\n\nimpl<'d, M: Mode> embedded_io::Read for UartRx<'d, M> {\n    fn read(&mut self, buf: &mut [u8]) -> Result<usize, Self::Error> {\n        self.blocking_read(buf).map(|_| buf.len())\n    }\n}\n\nimpl<'d> embedded_io_async::Read for UartRx<'d, Async> {\n    async fn read(&mut self, buf: &mut [u8]) -> Result<usize, Self::Error> {\n        self.read(buf).await.map(|_| buf.len())\n    }\n}\n"
  },
  {
    "path": "embassy-mspm0/CHANGELOG.md",
    "content": "# Changelog for embassy-mspm0\n\nAll notable changes to this project will be documented in this file.\n\nThe format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),\nand this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).\n\n<!-- next-header -->\n## Unreleased - ReleaseDate\n\n- feat: Add I2C Controller (blocking & async) + examples for mspm0l1306, mspm0g3507 (tested MCUs) (#4435)\n- fix gpio interrupt not being set for mspm0l110x\n- feat: Add window watchdog implementation based on WWDT0, WWDT1 peripherals (#4574)\n- feat: Add MSPM0C1105/C1106 support\n- feat: Add adc implementation (#4646)\n- fix: gpio OutputOpenDrain config (#4735)\n- fix: add MSPM0C1106 to build test matrix\n- feat: add MSPM0H3216 support\n- feat: Add i2c target implementation (#4605)\n- fix: group irq handlers must check for NO_INTR (#4785)\n- feat: Add read_reset_cause function\n- feat: Add module Mathacl & example for mspm0g3507 (#4897)\n- feat: Add MSPM0G5187 support\n- feat: add CPU accelerated division function (#4966)\n- feat: Add trng implementation (#5172)\n- fix: feature guard pins used for NRST and SWD (#5257)\n"
  },
  {
    "path": "embassy-mspm0/Cargo.toml",
    "content": "[package]\nname = \"embassy-mspm0\"\nversion = \"0.1.0\"\nedition = \"2024\"\nlicense = \"MIT OR Apache-2.0\"\ndescription = \"Embassy Hardware Abstraction Layer (HAL) for Texas Instruments MSPM0 series microcontrollers\"\nkeywords = [\"embedded\", \"async\", \"mspm0\", \"hal\", \"embedded-hal\"]\ncategories = [\"embedded\", \"hardware-support\", \"no-std\", \"asynchronous\"]\nrepository = \"https://github.com/embassy-rs/embassy\"\ndocumentation = \"https://docs.embassy.dev/embassy-mspm0\"\npublish = false\n\n[package.metadata.embassy]\nbuild = [\n    {target = \"thumbv6m-none-eabi\", features = [\"defmt\", \"mspm0c1104dgs20\", \"time-driver-any\"]},\n    {target = \"thumbv6m-none-eabi\", features = [\"defmt\", \"mspm0c1106rgz\", \"time-driver-any\"]},\n    {target = \"thumbv6m-none-eabi\", features = [\"defmt\", \"mspm0g1107ycj\", \"time-driver-any\"]},\n    {target = \"thumbv6m-none-eabi\", features = [\"defmt\", \"mspm0g1505pt\", \"time-driver-any\"]},\n    {target = \"thumbv6m-none-eabi\", features = [\"defmt\", \"mspm0g1519rhb\", \"time-driver-any\"]},\n    {target = \"thumbv6m-none-eabi\", features = [\"defmt\", \"mspm0g3105rhb\", \"time-driver-any\"]},\n    {target = \"thumbv6m-none-eabi\", features = [\"defmt\", \"mspm0g3507pm\", \"time-driver-any\"]},\n    {target = \"thumbv6m-none-eabi\", features = [\"defmt\", \"mspm0g3519pz\", \"time-driver-any\"]},\n    {target = \"thumbv6m-none-eabi\", features = [\"defmt\", \"mspm0g5187pm\", \"time-driver-any\"]},\n    {target = \"thumbv6m-none-eabi\", features = [\"defmt\", \"mspm0h3216pt\", \"time-driver-any\"]},\n    {target = \"thumbv6m-none-eabi\", features = [\"defmt\", \"mspm0l1306rhb\", \"time-driver-any\"]},\n    {target = \"thumbv6m-none-eabi\", features = [\"defmt\", \"mspm0l2228pn\", \"time-driver-any\"]},\n    {target = \"thumbv6m-none-eabi\", features = [\"defmt\", \"mspm0l1345dgs28\", \"time-driver-any\"]},\n    {target = \"thumbv6m-none-eabi\", features = [\"defmt\", \"mspm0l1106dgs28\", \"time-driver-any\"]},\n    {target = \"thumbv6m-none-eabi\", features = [\"defmt\", \"mspm0l1228pt\", \"time-driver-any\"]},\n]\n\n[package.metadata.embassy_docs]\nsrc_base = \"https://github.com/embassy-rs/embassy/blob/embassy-mspm0-v$VERSION/embassy-mspm0/src/\"\nsrc_base_git = \"https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-mspm0/src/\"\n\nfeatures = [\"defmt\", \"unstable-pac\", \"time-driver-any\"]\nflavors = [\n    { regex_feature = \"mspm0c.*\", target = \"thumbv6m-none-eabi\" },\n    { regex_feature = \"mspm0l.*\", target = \"thumbv6m-none-eabi\" },\n    { regex_feature = \"mspm0g.*\", target = \"thumbv6m-none-eabi\" },\n    { regex_feature = \"mspm0h.*\", target = \"thumbv6m-none-eabi\" },\n]\n\n[package.metadata.docs.rs]\nfeatures = [\"defmt\", \"unstable-pac\", \"time-driver-any\", \"time\", \"mspm0g3507\"]\nrustdoc-args = [\"--cfg\", \"docsrs\"]\n\n[dependencies]\nembassy-sync = { version = \"0.8.0\", path = \"../embassy-sync\" }\n# TODO: Support other tick rates\nembassy-time-driver = { version = \"0.2.2\", path = \"../embassy-time-driver\", optional = true, features = [\"tick-hz-32_768\"] }\nembassy-time-queue-utils = { version = \"0.3.0\", path = \"../embassy-time-queue-utils\", optional = true }\nembassy-futures = { version = \"0.1.2\", path = \"../embassy-futures\" }\nembassy-hal-internal = { version = \"0.5.0\", path = \"../embassy-hal-internal\", features = [\"cortex-m\", \"prio-bits-2\"] }\nembassy-embedded-hal = { version = \"0.6.0\", path = \"../embassy-embedded-hal\", default-features = false }\n\nembedded-hal-02 = { package = \"embedded-hal\", version = \"0.2.6\", features = [\"unproven\"] }\nembedded-hal = { version = \"1.0\" }\nembedded-hal-nb = { version = \"1.0\" }\nembedded-hal-async = { version = \"1.0\" }\nembedded-io = { version = \"0.7.1\" }\nembedded-io-async = { version = \"0.7.0\" }\n\ndefmt = { version = \"1.0.1\", optional = true }\nfixed = \"1.29\"\nlog = { version = \"0.4.14\", optional = true }\ncortex-m-rt = \">=0.6.15,<0.8\"\ncortex-m = \"0.7.6\"\ncritical-section = \"1.2.0\"\nmicromath = \"2.0.0\"\n\n# mspm0-metapac = { version = \"\" }\nmspm0-metapac = { git = \"https://github.com/mspm0-rs/mspm0-data-generated/\", tag = \"mspm0-data-d9f54366c65cec47957065f42fe04542b852a8cd\" }\nrand_core = \"0.9\"\n\n[build-dependencies]\nproc-macro2 = \"1.0.94\"\nquote = \"1.0.40\"\ncfg_aliases = \"0.2.1\"\n\n# mspm0-metapac = { version = \"\", default-features = false, features = [\"metadata\"] }\nmspm0-metapac = { git = \"https://github.com/mspm0-rs/mspm0-data-generated/\", tag = \"mspm0-data-d9f54366c65cec47957065f42fe04542b852a8cd\", default-features = false, features = [\"metadata\"] }\n\n[features]\ndefault = [\"rt\"]\n\n## Enable `mspm0-metapac`'s `rt` feature\nrt = [\"mspm0-metapac/rt\"]\n\n## Use [`defmt`](https://docs.rs/defmt/latest/defmt/) for logging\ndefmt = [\n    \"dep:defmt\",\n    \"embassy-sync/defmt\",\n    \"embassy-embedded-hal/defmt\",\n    \"embassy-hal-internal/defmt\",\n]\n\n## Use log for logging\nlog = [\"dep:log\"]\n\n## Re-export mspm0-metapac at `mspm0::pac`.\n## This is unstable because semver-minor (non-breaking) releases of embassy-mspm0 may major-bump (breaking) the mspm0-metapac version.\n## If this is an issue for you, you're encouraged to directly depend on a fixed version of the PAC.\n## There are no plans to make this stable.\nunstable-pac = []\n\n## Allow using the NRST pin as a regular GPIO pin.\n## This is not applicable to all chips. In particular MSPM0C1103/4 share PA1 with NRST.\nnrst-pin-as-gpio = []\n\n## Allow using the SWD pins as regular GPIO pins.\nswd-pins-as-gpio = []\n\n#! ## Time\n\n# Features starting with `_` are for internal use only. They're not intended\n# to be enabled by other crates, and are not covered by semver guarantees.\n_time-driver = [\"dep:embassy-time-driver\", \"dep:embassy-time-queue-utils\"]\n\n# Use any time driver\ntime-driver-any = [\"_time-driver\"]\n## Use TIMG0 as time driver\ntime-driver-timg0 = [\"_time-driver\"]\n## Use TIMG1 as time driver\ntime-driver-timg1 = [\"_time-driver\"]\n## Use TIMG2 as time driver\ntime-driver-timg2 = [\"_time-driver\"]\n## Use TIMG3 as time driver\ntime-driver-timg3 = [\"_time-driver\"]\n## Use TIMG4 as time driver\ntime-driver-timg4 = [\"_time-driver\"]\n## Use TIMG5 as time driver\ntime-driver-timg5 = [\"_time-driver\"]\n## Use TIMG6 as time driver\ntime-driver-timg6 = [\"_time-driver\"]\n## Use TIMG7 as time driver\ntime-driver-timg7 = [\"_time-driver\"]\n## Use TIMG8 as time driver\ntime-driver-timg8 = [\"_time-driver\"]\n## Use TIMG9 as time driver\ntime-driver-timg9 = [\"_time-driver\"]\n## Use TIMG10 as time driver\ntime-driver-timg10 = [\"_time-driver\"]\n## Use TIMG11 as time driver\ntime-driver-timg11 = [\"_time-driver\"]\n## Use TIMG12 as time driver\ntime-driver-timg12 = [\"_time-driver\"]\n## Use TIMG13 as time driver\ntime-driver-timg13 = [\"_time-driver\"]\n## Use TIMG14 as time driver\ntime-driver-timg14 = [\"_time-driver\"]\n## Use TIMA0 as time driver\ntime-driver-tima0 = [\"_time-driver\"]\n## Use TIMA1 as time driver\ntime-driver-tima1 = [\"_time-driver\"]\n\n#! ## Chip-selection features\n#! Select your chip by specifying the model as a feature, e.g. `mspm0g3507pm`.\n#! Check the `Cargo.toml` for the latest list of supported chips.\n#!\n#! **Important:** Do not forget to adapt the target chip in your toolchain,\n#! e.g. in `.cargo/config.toml`.\nmspm0c1103dgs20 = [\"mspm0-metapac/mspm0c1103dgs20\"]\nmspm0c1103ruk = [\"mspm0-metapac/mspm0c1103ruk\"]\nmspm0c1103dyy = [\"mspm0-metapac/mspm0c1103dyy\"]\nmspm0c1103dsg = [\"mspm0-metapac/mspm0c1103dsg\"]\nmspm0c1104dgs20 = [\"mspm0-metapac/mspm0c1104dgs20\"]\nmspm0c1104ruk = [\"mspm0-metapac/mspm0c1104ruk\"]\nmspm0c1104dyy = [\"mspm0-metapac/mspm0c1104dyy\"]\nmspm0c1104dsg = [\"mspm0-metapac/mspm0c1104dsg\"]\nmspm0c1104ycj = [\"mspm0-metapac/mspm0c1104ycj\"]\nmspm0c1105pt = [\"mspm0-metapac/mspm0c1105pt\"]\nmspm0c1105rgz = [\"mspm0-metapac/mspm0c1105rgz\"]\nmspm0c1105rhb = [\"mspm0-metapac/mspm0c1105rhb\"]\nmspm0c1105dgs32 = [\"mspm0-metapac/mspm0c1105dgs32\"]\nmspm0c1105dgs28 = [\"mspm0-metapac/mspm0c1105dgs28\"]\nmspm0c1105rge = [\"mspm0-metapac/mspm0c1105rge\"]\nmspm0c1105dgs20 = [\"mspm0-metapac/mspm0c1105dgs20\"]\nmspm0c1105ruk = [\"mspm0-metapac/mspm0c1105ruk\"]\nmspm0c1105zcm = [\"mspm0-metapac/mspm0c1105zcm\"]\nmspm0c1106pt = [\"mspm0-metapac/mspm0c1106pt\"]\nmspm0c1106rgz = [\"mspm0-metapac/mspm0c1106rgz\"]\nmspm0c1106rhb = [\"mspm0-metapac/mspm0c1106rhb\"]\nmspm0c1106dgs32 = [\"mspm0-metapac/mspm0c1106dgs32\"]\nmspm0c1106dgs28 = [\"mspm0-metapac/mspm0c1106dgs28\"]\nmspm0c1106rge = [\"mspm0-metapac/mspm0c1106rge\"]\nmspm0c1106dgs20 = [\"mspm0-metapac/mspm0c1106dgs20\"]\nmspm0c1106ruk = [\"mspm0-metapac/mspm0c1106ruk\"]\nmspm0c1106zcm = [\"mspm0-metapac/mspm0c1106zcm\"]\nmspm0g1105rge = [\"mspm0-metapac/mspm0g1105rge\"]\nmspm0g1105dgs28 = [\"mspm0-metapac/mspm0g1105dgs28\"]\nmspm0g1105rhb = [\"mspm0-metapac/mspm0g1105rhb\"]\nmspm0g1105rgz = [\"mspm0-metapac/mspm0g1105rgz\"]\nmspm0g1105pt = [\"mspm0-metapac/mspm0g1105pt\"]\nmspm0g1105pm = [\"mspm0-metapac/mspm0g1105pm\"]\nmspm0g1106rge = [\"mspm0-metapac/mspm0g1106rge\"]\nmspm0g1106dgs28 = [\"mspm0-metapac/mspm0g1106dgs28\"]\nmspm0g1106rhb = [\"mspm0-metapac/mspm0g1106rhb\"]\nmspm0g1106rgz = [\"mspm0-metapac/mspm0g1106rgz\"]\nmspm0g1106pt = [\"mspm0-metapac/mspm0g1106pt\"]\nmspm0g1106pm = [\"mspm0-metapac/mspm0g1106pm\"]\nmspm0g1107rge = [\"mspm0-metapac/mspm0g1107rge\"]\nmspm0g1107dgs28 = [\"mspm0-metapac/mspm0g1107dgs28\"]\nmspm0g1107rhb = [\"mspm0-metapac/mspm0g1107rhb\"]\nmspm0g1107rgz = [\"mspm0-metapac/mspm0g1107rgz\"]\nmspm0g1107pt = [\"mspm0-metapac/mspm0g1107pt\"]\nmspm0g1107pm = [\"mspm0-metapac/mspm0g1107pm\"]\nmspm0g1107ycj = [\"mspm0-metapac/mspm0g1107ycj\"]\nmspm0g1505rge = [\"mspm0-metapac/mspm0g1505rge\"]\nmspm0g1505rhb = [\"mspm0-metapac/mspm0g1505rhb\"]\nmspm0g1505rgz = [\"mspm0-metapac/mspm0g1505rgz\"]\nmspm0g1505pt = [\"mspm0-metapac/mspm0g1505pt\"]\nmspm0g1505pm = [\"mspm0-metapac/mspm0g1505pm\"]\nmspm0g1506rge = [\"mspm0-metapac/mspm0g1506rge\"]\nmspm0g1506rhb = [\"mspm0-metapac/mspm0g1506rhb\"]\nmspm0g1506rgz = [\"mspm0-metapac/mspm0g1506rgz\"]\nmspm0g1506pt = [\"mspm0-metapac/mspm0g1506pt\"]\nmspm0g1506pm = [\"mspm0-metapac/mspm0g1506pm\"]\nmspm0g1507rge = [\"mspm0-metapac/mspm0g1507rge\"]\nmspm0g1507rhb = [\"mspm0-metapac/mspm0g1507rhb\"]\nmspm0g1507rgz = [\"mspm0-metapac/mspm0g1507rgz\"]\nmspm0g1507pt = [\"mspm0-metapac/mspm0g1507pt\"]\nmspm0g1507pm = [\"mspm0-metapac/mspm0g1507pm\"]\nmspm0g1507ycj = [\"mspm0-metapac/mspm0g1507ycj\"]\nmspm0g1518rhb = [\"mspm0-metapac/mspm0g1518rhb\"]\nmspm0g1518rgz = [\"mspm0-metapac/mspm0g1518rgz\"]\nmspm0g1518pt = [\"mspm0-metapac/mspm0g1518pt\"]\nmspm0g1518pm = [\"mspm0-metapac/mspm0g1518pm\"]\nmspm0g1518pz = [\"mspm0-metapac/mspm0g1518pz\"]\nmspm0g1518pn = [\"mspm0-metapac/mspm0g1518pn\"]\nmspm0g1518zaw = [\"mspm0-metapac/mspm0g1518zaw\"]\nmspm0g1519rhb = [\"mspm0-metapac/mspm0g1519rhb\"]\nmspm0g1519rgz = [\"mspm0-metapac/mspm0g1519rgz\"]\nmspm0g1519pt = [\"mspm0-metapac/mspm0g1519pt\"]\nmspm0g1519pm = [\"mspm0-metapac/mspm0g1519pm\"]\nmspm0g1519pz = [\"mspm0-metapac/mspm0g1519pz\"]\nmspm0g1519pn = [\"mspm0-metapac/mspm0g1519pn\"]\nmspm0g3105dgs20 = [\"mspm0-metapac/mspm0g3105dgs20\"]\nmspm0g3105dgs28 = [\"mspm0-metapac/mspm0g3105dgs28\"]\nmspm0g3105rhb = [\"mspm0-metapac/mspm0g3105rhb\"]\nmspm0g3106dgs20 = [\"mspm0-metapac/mspm0g3106dgs20\"]\nmspm0g3106dgs28 = [\"mspm0-metapac/mspm0g3106dgs28\"]\nmspm0g3106rhb = [\"mspm0-metapac/mspm0g3106rhb\"]\nmspm0g3107dgs20 = [\"mspm0-metapac/mspm0g3107dgs20\"]\nmspm0g3107dgs28 = [\"mspm0-metapac/mspm0g3107dgs28\"]\nmspm0g3107rhb = [\"mspm0-metapac/mspm0g3107rhb\"]\nmspm0g3505dgs28 = [\"mspm0-metapac/mspm0g3505dgs28\"]\nmspm0g3505rhb = [\"mspm0-metapac/mspm0g3505rhb\"]\nmspm0g3505rgz = [\"mspm0-metapac/mspm0g3505rgz\"]\nmspm0g3505pt = [\"mspm0-metapac/mspm0g3505pt\"]\nmspm0g3505pm = [\"mspm0-metapac/mspm0g3505pm\"]\nmspm0g3506dgs28 = [\"mspm0-metapac/mspm0g3506dgs28\"]\nmspm0g3506rhb = [\"mspm0-metapac/mspm0g3506rhb\"]\nmspm0g3506rgz = [\"mspm0-metapac/mspm0g3506rgz\"]\nmspm0g3506pt = [\"mspm0-metapac/mspm0g3506pt\"]\nmspm0g3506pm = [\"mspm0-metapac/mspm0g3506pm\"]\nmspm0g3507dgs28 = [\"mspm0-metapac/mspm0g3507dgs28\"]\nmspm0g3507rhb = [\"mspm0-metapac/mspm0g3507rhb\"]\nmspm0g3507rgz = [\"mspm0-metapac/mspm0g3507rgz\"]\nmspm0g3507pt = [\"mspm0-metapac/mspm0g3507pt\"]\nmspm0g3507pm = [\"mspm0-metapac/mspm0g3507pm\"]\nmspm0g3518rhb = [\"mspm0-metapac/mspm0g3518rhb\"]\nmspm0g3518rgz = [\"mspm0-metapac/mspm0g3518rgz\"]\nmspm0g3518pt = [\"mspm0-metapac/mspm0g3518pt\"]\nmspm0g3518pm = [\"mspm0-metapac/mspm0g3518pm\"]\nmspm0g3518pz = [\"mspm0-metapac/mspm0g3518pz\"]\nmspm0g3518pn = [\"mspm0-metapac/mspm0g3518pn\"]\nmspm0g3519rhb = [\"mspm0-metapac/mspm0g3519rhb\"]\nmspm0g3519rgz = [\"mspm0-metapac/mspm0g3519rgz\"]\nmspm0g3519pt = [\"mspm0-metapac/mspm0g3519pt\"]\nmspm0g3519pm = [\"mspm0-metapac/mspm0g3519pm\"]\nmspm0g3519pz = [\"mspm0-metapac/mspm0g3519pz\"]\nmspm0g3519pn = [\"mspm0-metapac/mspm0g3519pn\"]\nmspm0g3519zaw = [\"mspm0-metapac/mspm0g3519zaw\"]\nmspm0g5187rhb = [\"mspm0-metapac/mspm0g5187rhb\"]\nmspm0g5187rgz = [\"mspm0-metapac/mspm0g5187rgz\"]\nmspm0g5187pt = [\"mspm0-metapac/mspm0g5187pt\"]\nmspm0g5187pm = [\"mspm0-metapac/mspm0g5187pm\"]\nmspm0g5187ruy = [\"mspm0-metapac/mspm0g5187ruy\"]\nmspm0g5187ycj = [\"mspm0-metapac/mspm0g5187ycj\"]\nmspm0g5187rge = [\"mspm0-metapac/mspm0g5187rge\"]\nmspm0h3216pt = [\"mspm0-metapac/mspm0h3216pt\"]\nmspm0l1105dgs20 = [\"mspm0-metapac/mspm0l1105dgs20\"]\nmspm0l1105dgs28 = [\"mspm0-metapac/mspm0l1105dgs28\"]\nmspm0l1105rge = [\"mspm0-metapac/mspm0l1105rge\"]\nmspm0l1105rtr = [\"mspm0-metapac/mspm0l1105rtr\"]\nmspm0l1105dyy = [\"mspm0-metapac/mspm0l1105dyy\"]\nmspm0l1106rhb = [\"mspm0-metapac/mspm0l1106rhb\"]\nmspm0l1106dgs20 = [\"mspm0-metapac/mspm0l1106dgs20\"]\nmspm0l1106dgs28 = [\"mspm0-metapac/mspm0l1106dgs28\"]\nmspm0l1106rge = [\"mspm0-metapac/mspm0l1106rge\"]\nmspm0l1106rtr = [\"mspm0-metapac/mspm0l1106rtr\"]\nmspm0l1106dyy = [\"mspm0-metapac/mspm0l1106dyy\"]\nmspm0l1227rhb = [\"mspm0-metapac/mspm0l1227rhb\"]\nmspm0l1227pn = [\"mspm0-metapac/mspm0l1227pn\"]\nmspm0l1227rgz = [\"mspm0-metapac/mspm0l1227rgz\"]\nmspm0l1227pt = [\"mspm0-metapac/mspm0l1227pt\"]\nmspm0l1227pm = [\"mspm0-metapac/mspm0l1227pm\"]\nmspm0l1227rge = [\"mspm0-metapac/mspm0l1227rge\"]\nmspm0l1228rhb = [\"mspm0-metapac/mspm0l1228rhb\"]\nmspm0l1228pn = [\"mspm0-metapac/mspm0l1228pn\"]\nmspm0l1228rgz = [\"mspm0-metapac/mspm0l1228rgz\"]\nmspm0l1228pt = [\"mspm0-metapac/mspm0l1228pt\"]\nmspm0l1228pm = [\"mspm0-metapac/mspm0l1228pm\"]\nmspm0l1228rge = [\"mspm0-metapac/mspm0l1228rge\"]\nmspm0l1303rge = [\"mspm0-metapac/mspm0l1303rge\"]\nmspm0l1304rhb = [\"mspm0-metapac/mspm0l1304rhb\"]\nmspm0l1304dgs20 = [\"mspm0-metapac/mspm0l1304dgs20\"]\nmspm0l1304dgs28 = [\"mspm0-metapac/mspm0l1304dgs28\"]\nmspm0l1304rge = [\"mspm0-metapac/mspm0l1304rge\"]\nmspm0l1304rtr = [\"mspm0-metapac/mspm0l1304rtr\"]\nmspm0l1304dyy = [\"mspm0-metapac/mspm0l1304dyy\"]\nmspm0l1305dgs20 = [\"mspm0-metapac/mspm0l1305dgs20\"]\nmspm0l1305dgs28 = [\"mspm0-metapac/mspm0l1305dgs28\"]\nmspm0l1305rge = [\"mspm0-metapac/mspm0l1305rge\"]\nmspm0l1305rtr = [\"mspm0-metapac/mspm0l1305rtr\"]\nmspm0l1305dyy = [\"mspm0-metapac/mspm0l1305dyy\"]\nmspm0l1306rhb = [\"mspm0-metapac/mspm0l1306rhb\"]\nmspm0l1306dgs20 = [\"mspm0-metapac/mspm0l1306dgs20\"]\nmspm0l1306dgs28 = [\"mspm0-metapac/mspm0l1306dgs28\"]\nmspm0l1306rge = [\"mspm0-metapac/mspm0l1306rge\"]\nmspm0l1306dyy = [\"mspm0-metapac/mspm0l1306dyy\"]\nmspm0l1343dgs20 = [\"mspm0-metapac/mspm0l1343dgs20\"]\nmspm0l1344dgs20 = [\"mspm0-metapac/mspm0l1344dgs20\"]\nmspm0l1345dgs28 = [\"mspm0-metapac/mspm0l1345dgs28\"]\nmspm0l1346dgs28 = [\"mspm0-metapac/mspm0l1346dgs28\"]\nmspm0l2227rgz = [\"mspm0-metapac/mspm0l2227rgz\"]\nmspm0l2227pt = [\"mspm0-metapac/mspm0l2227pt\"]\nmspm0l2227pm = [\"mspm0-metapac/mspm0l2227pm\"]\nmspm0l2227pn = [\"mspm0-metapac/mspm0l2227pn\"]\nmspm0l2228rgz = [\"mspm0-metapac/mspm0l2228rgz\"]\nmspm0l2228pt = [\"mspm0-metapac/mspm0l2228pt\"]\nmspm0l2228pm = [\"mspm0-metapac/mspm0l2228pm\"]\nmspm0l2228pn = [\"mspm0-metapac/mspm0l2228pn\"]\nmsps003f3pw20 = [\"mspm0-metapac/msps003f3pw20\"]\nmsps003f4pw20 = [\"mspm0-metapac/msps003f4pw20\"]\n"
  },
  {
    "path": "embassy-mspm0/README.md",
    "content": "# Embassy MSPM0 HAL\n\nThe embassy-mspm0 HAL aims to provide a safe, idiomatic hardware abstraction layer for all MSPM0 and MSPS003 chips.\n\n* [Documentation](https://docs.embassy.dev/embassy-mspm0/) (**Important:** use docs.embassy.dev rather than docs.rs to see the specific docs for the chip you’re using!)\n* [Source](https://github.com/embassy-rs/embassy/tree/main/embassy-mspm0)\n* [Examples](https://github.com/embassy-rs/embassy/tree/main/examples)\n\n## Embedded-hal\n\nThe `embassy-mspm0` HAL implements the traits from [embedded-hal](https://crates.io/crates/embedded-hal) (1.0) and [embedded-hal-async](https://crates.io/crates/embedded-hal-async), as well as [embedded-io](https://crates.io/crates/embedded-io) and [embedded-io-async](https://crates.io/crates/embedded-io-async).\n\n## A note on feature flag names\n\nFeature flag names for chips do not include temperature rating or distribution format.\n\nUsually chapter 10 of your device's datasheet will explain the device nomenclature and how to decode it. Feature names in embassy-mspm0 only use the following from device nomenclature:\n- MCU platform\n- Product family\n- Device subfamily\n- Flash memory\n- Package type\n\nThis means for a part such as `MSPM0G3507SPMR`, the feature name is `mspm0g3507pm`. This also means that `MSPM0G3507QPMRQ1` uses the feature `mspm0g3507pm`, since the Q1 parts are just qualified variants of the base G3507 with a PM (QFP-64) package.\n\n## Interoperability\n\nThis crate can run on any executor.\n"
  },
  {
    "path": "embassy-mspm0/build.rs",
    "content": "use std::cmp::Ordering;\nuse std::collections::HashMap;\nuse std::fmt::Write;\nuse std::io::Write as _;\nuse std::path::{Path, PathBuf};\nuse std::process::Command;\nuse std::sync::LazyLock;\nuse std::{env, fs};\n\nuse common::CfgSet;\nuse mspm0_metapac::metadata::{ALL_CHIPS, METADATA};\nuse proc_macro2::{Ident, Literal, Span, TokenStream};\nuse quote::{format_ident, quote};\n\n#[path = \"./build_common.rs\"]\nmod common;\n\nfn main() {\n    let mut cfgs = common::CfgSet::new();\n    common::set_target_cfgs(&mut cfgs);\n\n    generate_code(&mut cfgs);\n    select_gpio_features(&mut cfgs);\n    interrupt_group_linker_magic();\n}\n\nfn generate_code(cfgs: &mut CfgSet) {\n    #[cfg(any(feature = \"rt\"))]\n    println!(\n        \"cargo:rustc-link-search={}\",\n        PathBuf::from(env::var_os(\"OUT_DIR\").unwrap()).display(),\n    );\n\n    cfgs.declare_all(&[\"gpio_pb\", \"gpio_pc\", \"int_group1\", \"unicomm\"]);\n\n    let chip_name = match env::vars()\n        .map(|(a, _)| a)\n        .filter(|x| x.starts_with(\"CARGO_FEATURE_MSPM0\") || x.starts_with(\"CARGO_FEATURE_MSPS\"))\n        .get_one()\n    {\n        Ok(x) => x,\n        Err(GetOneError::None) => panic!(\"No mspm0xx/mspsxx Cargo feature enabled\"),\n        Err(GetOneError::Multiple) => panic!(\"Multiple mspm0xx/mspsxx Cargo features enabled\"),\n    }\n    .strip_prefix(\"CARGO_FEATURE_\")\n    .unwrap()\n    .to_ascii_lowercase()\n    .replace('_', \"-\");\n\n    eprintln!(\"chip: {chip_name}\");\n\n    cfgs.enable_all(&get_chip_cfgs(&chip_name));\n    for chip in ALL_CHIPS {\n        cfgs.declare_all(&get_chip_cfgs(&chip));\n    }\n\n    let mut singletons = get_singletons(cfgs);\n\n    time_driver(&mut singletons, cfgs);\n    pin_features(&mut singletons);\n\n    let mut g = TokenStream::new();\n\n    g.extend(generate_singletons(&singletons));\n    g.extend(generate_pincm_mapping());\n    g.extend(generate_pin());\n    g.extend(generate_timers());\n    g.extend(generate_interrupts());\n    g.extend(generate_peripheral_instances());\n    g.extend(generate_pin_trait_impls());\n    g.extend(generate_groups());\n    g.extend(generate_dma_channel_count());\n    g.extend(generate_adc_constants(cfgs));\n\n    let out_dir = &PathBuf::from(env::var_os(\"OUT_DIR\").unwrap());\n    let out_file = out_dir.join(\"_generated.rs\").to_string_lossy().to_string();\n    fs::write(&out_file, g.to_string()).unwrap();\n    rustfmt(&out_file);\n}\n\nfn get_chip_cfgs(chip_name: &str) -> Vec<String> {\n    let mut cfgs = Vec::new();\n\n    // GPIO on C110x is special as it does not belong to an interrupt group.\n    if chip_name.starts_with(\"mspm0c1103\") || chip_name.starts_with(\"mspm0c1104\") || chip_name.starts_with(\"msps003f\") {\n        cfgs.push(\"mspm0c110x\".to_string());\n    }\n\n    if chip_name.starts_with(\"mspm0c1105\") || chip_name.starts_with(\"mspm0c1106\") {\n        cfgs.push(\"mspm0c1105_c1106\".to_string());\n    }\n\n    // Family ranges (temporary until int groups are generated)\n    //\n    // TODO: Remove this once int group stuff is generated.\n    if chip_name.starts_with(\"mspm0g110\") {\n        cfgs.push(\"mspm0g110x\".to_string());\n    }\n\n    if chip_name.starts_with(\"mspm0g150\") {\n        cfgs.push(\"mspm0g150x\".to_string());\n    }\n\n    if chip_name.starts_with(\"mspm0g151\") {\n        cfgs.push(\"mspm0g151x\".to_string());\n    }\n\n    if chip_name.starts_with(\"mspm0g310\") {\n        cfgs.push(\"mspm0g310x\".to_string());\n    }\n\n    if chip_name.starts_with(\"mspm0g350\") {\n        cfgs.push(\"mspm0g350x\".to_string());\n    }\n\n    if chip_name.starts_with(\"mspm0g351\") {\n        cfgs.push(\"mspm0g351x\".to_string());\n    }\n\n    if chip_name.starts_with(\"mspm0g518\") {\n        cfgs.push(\"mspm0g518x\".to_string());\n    }\n\n    if chip_name.starts_with(\"mspm0h321\") {\n        cfgs.push(\"mspm0h321x\".to_string());\n    }\n\n    if chip_name.starts_with(\"mspm0l110\") {\n        cfgs.push(\"mspm0l110x\".to_string());\n    }\n\n    if chip_name.starts_with(\"mspm0l122\") {\n        cfgs.push(\"mspm0l122x\".to_string());\n    }\n\n    if chip_name.starts_with(\"mspm0l130\") {\n        cfgs.push(\"mspm0l130x\".to_string());\n    }\n\n    if chip_name.starts_with(\"mspm0l134\") {\n        cfgs.push(\"mspm0l134x\".to_string());\n    }\n\n    if chip_name.starts_with(\"mspm0l222\") {\n        cfgs.push(\"mspm0l222x\".to_string());\n    }\n\n    cfgs\n}\n\n/// Interrupt groups use a weakly linked symbols and #[linkage = \"extern_weak\"] is nightly we need to\n/// do some linker magic to create weak linkage.\nfn interrupt_group_linker_magic() {\n    let mut file = String::new();\n\n    for group in METADATA.interrupt_groups {\n        for interrupt in group.interrupts.iter() {\n            let name = interrupt.name;\n\n            writeln!(&mut file, \"PROVIDE({name} = DefaultHandler);\").unwrap();\n        }\n    }\n\n    let out_dir = &PathBuf::from(env::var_os(\"OUT_DIR\").unwrap());\n    let out_file = out_dir.join(\"interrupt_group.x\");\n    fs::write(&out_file, file).unwrap();\n}\n\nfn generate_groups() -> TokenStream {\n    let group_vectors = METADATA.interrupt_groups.iter().map(|group| {\n        let vectors = group.interrupts.iter().map(|interrupt| {\n            let fn_name = Ident::new(interrupt.name, Span::call_site());\n\n            quote! {\n                pub(crate) fn #fn_name();\n            }\n        });\n\n        quote! { #(#vectors)* }\n    });\n\n    let groups = METADATA.interrupt_groups.iter().map(|group| {\n        let interrupt_group_name = Ident::new(group.name, Span::call_site());\n        let group_enum = Ident::new(&format!(\"Group{}\", &group.name[5..]), Span::call_site());\n        let group_number = Literal::u32_unsuffixed(group.number);\n\n        let matches = group.interrupts.iter().map(|interrupt| {\n            let variant = Ident::new(&interrupt.name, Span::call_site());\n\n            quote! {\n                #group_enum::#variant => unsafe { group_vectors::#variant() },\n            }\n        });\n\n        quote! {\n            #[cfg(feature = \"rt\")]\n            #[crate::pac::interrupt]\n            fn #interrupt_group_name() {\n                use crate::pac::#group_enum;\n\n                let group = crate::pac::CPUSS.int_group(#group_number);\n                let stat = group.iidx().read().stat();\n\n                // check for spurious interrupts\n                if stat == crate::pac::cpuss::vals::Iidx::NO_INTR {\n                    return;\n                }\n\n                // MUST subtract by 1 because NO_INTR offsets IIDX values.\n                let iidx = stat.to_bits() - 1;\n\n                let Ok(group) = #group_enum::try_from(iidx as u8) else {\n                    return;\n                };\n\n                match group {\n                    #(#matches)*\n                }\n            }\n        }\n    });\n\n    quote! {\n        #(#groups)*\n\n        #[cfg(feature = \"rt\")]\n        mod group_vectors {\n            unsafe extern \"Rust\" {\n                #(#group_vectors)*\n            }\n        }\n    }\n}\n\nfn generate_dma_channel_count() -> TokenStream {\n    let count = METADATA.dma_channels.len();\n\n    quote! { pub const DMA_CHANNELS: usize = #count; }\n}\n\nfn generate_adc_constants(cfgs: &mut CfgSet) -> TokenStream {\n    let vrsel = METADATA.adc_vrsel;\n    let memctl = METADATA.adc_memctl;\n\n    cfgs.declare(\"adc_neg_vref\");\n    match vrsel {\n        3 => (),\n        5 => cfgs.enable(\"adc_neg_vref\"),\n        _ => panic!(\"Unsupported ADC VRSEL value: {vrsel}\"),\n    }\n    quote! {\n        pub const ADC_VRSEL: u8 = #vrsel;\n        pub const ADC_MEMCTL: u8 = #memctl;\n    }\n}\n\n#[derive(Debug, Clone)]\nstruct Singleton {\n    name: String,\n\n    /// `#[cfg]` guard which enables this singleton instance to be obtained.\n    cfg: Option<TokenStream>,\n}\n\nimpl PartialEq for Singleton {\n    fn eq(&self, other: &Self) -> bool {\n        self.name == other.name\n    }\n}\n\nimpl Eq for Singleton {}\n\nimpl PartialOrd for Singleton {\n    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {\n        Some(self.cmp(other))\n    }\n}\n\nimpl Ord for Singleton {\n    fn cmp(&self, other: &Self) -> Ordering {\n        self.name.cmp(&other.name)\n    }\n}\n\nfn get_singletons(cfgs: &mut common::CfgSet) -> Vec<Singleton> {\n    let mut singletons = Vec::<Singleton>::new();\n\n    for peripheral in METADATA.peripherals {\n        // Some peripherals do not generate a singleton, but generate a singleton for each pin.\n        let skip_peripheral_singleton = match peripheral.kind {\n            \"gpio\" => {\n                // Also enable ports that are present.\n                match peripheral.name {\n                    \"GPIOB\" => cfgs.enable(\"gpio_pb\"),\n                    \"GPIOC\" => cfgs.enable(\"gpio_pc\"),\n                    _ => (),\n                }\n\n                true\n            }\n\n            // Each channel gets a singleton, handled separately.\n            \"dma\" => true,\n\n            // These peripherals do not exist as singletons, and have no signals but are managed\n            // by the HAL.\n            \"iomux\" | \"cpuss\" => true,\n\n            // Unicomm instances get their own singletons, but we need to enable a cfg for unicomm drivers.\n            \"unicomm\" => {\n                cfgs.enable(\"unicomm\");\n                false\n            }\n\n            // TODO: Remove after TIMB is fixed\n            \"tim\" if peripheral.name.starts_with(\"TIMB\") => true,\n\n            _ => false,\n        };\n\n        if !skip_peripheral_singleton {\n            singletons.push(Singleton {\n                name: peripheral.name.to_string(),\n                cfg: None,\n            });\n        }\n\n        // Generate each GPIO pin singleton\n        if peripheral.name.starts_with(\"GPIO\") {\n            for pin in peripheral.pins {\n                let singleton = make_valid_identifier(&pin.signal);\n                singletons.push(singleton);\n            }\n        }\n    }\n\n    // DMA channels get their own singletons\n    for dma_channel in METADATA.dma_channels.iter() {\n        singletons.push(Singleton {\n            name: format!(\"DMA_CH{}\", dma_channel.number),\n            cfg: None,\n        });\n    }\n\n    singletons.sort_by(|a, b| a.name.cmp(&b.name));\n    singletons\n}\n\nfn make_valid_identifier(s: &str) -> Singleton {\n    let name = s.replace('+', \"_P\").replace(\"-\", \"_N\");\n\n    Singleton { name, cfg: None }\n}\n\nfn generate_pincm_mapping() -> TokenStream {\n    let pincms = METADATA.pins.iter().map(|mapping| {\n        let port_letter = mapping.pin.strip_prefix(\"P\").unwrap();\n        let port_base = (port_letter.chars().next().unwrap() as u8 - b'A') * 32;\n        // This assumes all ports are single letter length.\n        // This is fine unless TI releases a part with 833+ GPIO pins.\n        let pin_number = mapping.pin[2..].parse::<u8>().unwrap();\n\n        let num = port_base + pin_number;\n\n        // But subtract 1 since pincm indices start from 0, not 1.\n        let pincm = Literal::u8_unsuffixed(mapping.pincm - 1);\n        quote! {\n            #num => #pincm\n        }\n    });\n\n    quote! {\n        #[doc = \"Get the mapping from GPIO pin port to IOMUX PINCM index. This is required since the mapping from IO to PINCM index is not consistent across parts.\"]\n        pub(crate) fn gpio_pincm(pin_port: u8) -> u8 {\n            match pin_port {\n                #(#pincms),*,\n                _ => unreachable!(),\n            }\n        }\n    }\n}\n\nfn generate_pin() -> TokenStream {\n    let pin_impls = METADATA.pins.iter().map(|pin| {\n        let name = Ident::new(&pin.pin, Span::call_site());\n        let port_letter = pin.pin.strip_prefix(\"P\").unwrap();\n        let port_letter = port_letter.chars().next().unwrap();\n        let pin_number = Literal::u8_unsuffixed(pin.pin[2..].parse::<u8>().unwrap());\n\n        let port = Ident::new(&format!(\"Port{}\", port_letter), Span::call_site());\n\n        // TODO: Feature gate pins that can be used as NRST\n\n        quote! {\n            impl_pin!(#name, crate::gpio::Port::#port, #pin_number);\n        }\n    });\n\n    quote! {\n        #(#pin_impls)*\n    }\n}\n\nfn time_driver(singletons: &mut Vec<Singleton>, cfgs: &mut CfgSet) {\n    // Timer features\n    for (timer, _) in TIMERS.iter() {\n        let name = timer.to_lowercase();\n        cfgs.declare(&format!(\"time_driver_{}\", name));\n    }\n\n    let time_driver = match env::vars()\n        .map(|(a, _)| a)\n        .filter(|x| x.starts_with(\"CARGO_FEATURE_TIME_DRIVER_\"))\n        .get_one()\n    {\n        Ok(x) => Some(\n            x.strip_prefix(\"CARGO_FEATURE_TIME_DRIVER_\")\n                .unwrap()\n                .to_ascii_lowercase(),\n        ),\n        Err(GetOneError::None) => None,\n        Err(GetOneError::Multiple) => panic!(\"Multiple time-driver-xxx Cargo features enabled\"),\n    };\n\n    // Verify the selected timer is available\n    let selected_timer = match time_driver.as_ref().map(|x| x.as_ref()) {\n        None => \"\",\n        // TODO: Fix TIMB0\n        // Some(\"timb0\") => \"TIMB0\",\n        Some(\"timg0\") => \"TIMG0\",\n        Some(\"timg1\") => \"TIMG1\",\n        Some(\"timg2\") => \"TIMG2\",\n        Some(\"timg3\") => \"TIMG3\",\n        Some(\"timg4\") => \"TIMG4\",\n        Some(\"timg5\") => \"TIMG5\",\n        Some(\"timg6\") => \"TIMG6\",\n        Some(\"timg7\") => \"TIMG7\",\n        Some(\"timg8\") => \"TIMG8\",\n        Some(\"timg9\") => \"TIMG9\",\n        Some(\"timg10\") => \"TIMG10\",\n        Some(\"timg11\") => \"TIMG11\",\n        Some(\"timg14\") => \"TIMG14\",\n        Some(\"tima0\") => \"TIMA0\",\n        Some(\"tima1\") => \"TIMA1\",\n        Some(\"any\") => {\n            // Order of timer candidates:\n            // 1. Basic timers\n            // 2. 16-bit, 2 channel\n            // 3. 16-bit, 2 channel with shadow registers\n            // 4. 16-bit, 4 channel\n            // 5. 16-bit with QEI\n            // 6. Advanced timers\n            //\n            // TODO: 32-bit timers are not considered yet\n            [\n                // basic timers. No PWM pins\n                // \"TIMB0\", // 16-bit, 2 channel\n                \"TIMG0\", \"TIMG1\", \"TIMG2\", \"TIMG3\", // 16-bit, 2 channel with shadow registers\n                \"TIMG4\", \"TIMG5\", \"TIMG6\", \"TIMG7\",  // 16-bit, 4 channel\n                \"TIMG14\", // 16-bit with QEI\n                \"TIMG8\", \"TIMG9\", \"TIMG10\", \"TIMG11\", // Advanced timers\n                \"TIMA0\", \"TIMA1\",\n            ]\n            .iter()\n            .find(|tim| singletons.iter().any(|s| s.name == **tim))\n            .expect(\"Could not find any timer\")\n        }\n        _ => panic!(\"unknown time_driver {:?}\", time_driver),\n    };\n\n    if !selected_timer.is_empty() {\n        cfgs.enable(format!(\"time_driver_{}\", selected_timer.to_lowercase()));\n    }\n\n    // Apply cfgs to each timer and it's pins\n    for singleton in singletons.iter_mut() {\n        if singleton.name.starts_with(\"TIM\") {\n            // Remove suffixes for pin singletons.\n            let name = if singleton.name.contains(\"_CCP\") {\n                singleton.name.split_once(\"_CCP\").unwrap().0\n            } else if singleton.name.contains(\"_FAULT\") {\n                singleton.name.split_once(\"_FAULT\").unwrap().0\n            } else if singleton.name.contains(\"_IDX\") {\n                singleton.name.split_once(\"_IDX\").unwrap().0\n            } else {\n                &singleton.name\n            };\n\n            let feature = format!(\"time-driver-{}\", name.to_lowercase());\n\n            if singleton.name.contains(selected_timer) {\n                singleton.cfg = Some(quote! { #[cfg(not(all(feature = \"time-driver-any\", feature = #feature)))] });\n            } else {\n                singleton.cfg = Some(quote! { #[cfg(not(feature = #feature))] });\n            }\n        }\n    }\n}\n\nfn pin_features(singletons: &mut Vec<Singleton>) {\n    let sysctl = METADATA\n        .peripherals\n        .iter()\n        .find(|p| p.name == \"SYSCTL\")\n        .expect(\"no SYSCTL peripheral\");\n\n    // Some packages make NRST share a physical pin with a GPIO.\n    if let Some(pin) = sysctl.pins.iter().find(|p| p.signal == \"NRST\" && p.pin != \"NRST\") {\n        let pin = singletons\n            .iter_mut()\n            .find(|s| s.name == pin.pin)\n            .expect(\"Could not find NRST pin to cfg gate\");\n\n        pin.cfg = Some(quote! { #[cfg(feature = \"nrst-pin-as-gpio\")] });\n    }\n\n    let debugss = METADATA\n        .peripherals\n        .iter()\n        .find(|p| p.name == \"DEBUGSS\")\n        .expect(\"Could not find DEBUGSS peripheral\");\n\n    for pin in debugss.pins.iter() {\n        let pin = singletons\n            .iter_mut()\n            .find(|s| s.name == pin.pin)\n            .expect(\"Could not find SWD pin to cfg gate\");\n\n        pin.cfg = Some(quote! { #[cfg(feature = \"swd-pins-as-gpio\")] });\n    }\n}\n\nfn generate_singletons(singletons: &[Singleton]) -> TokenStream {\n    let singletons_peripherals_struct = singletons\n        .iter()\n        .map(|s| {\n            let cfg = s.cfg.clone().unwrap_or_default();\n\n            let ident = format_ident!(\"{}\", s.name);\n\n            quote! {\n                #cfg\n                #ident\n            }\n        })\n        .collect::<Vec<_>>();\n\n    let singletons_peripherals_def = singletons\n        .iter()\n        .map(|s| {\n            let ident = format_ident!(\"{}\", s.name);\n\n            quote! {\n                #ident\n            }\n        })\n        .collect::<Vec<_>>();\n\n    quote! {\n        embassy_hal_internal::peripherals_definition!(#(#singletons_peripherals_def),*);\n        embassy_hal_internal::peripherals_struct!(#(#singletons_peripherals_struct),*);\n    }\n}\n\nfn generate_timers() -> TokenStream {\n    // Generate timers\n    let timer_impls = METADATA\n        .peripherals\n        .iter()\n        .filter(|p| p.name.starts_with(\"TIM\"))\n        // TODO: Fix TIMB when used at time driver.\n        .filter(|p| !p.name.starts_with(\"TIMB\"))\n        .map(|peripheral| {\n            let name = Ident::new(&peripheral.name, Span::call_site());\n            let timers = &*TIMERS;\n\n            let timer = timers.get(peripheral.name).expect(\"Timer does not exist\");\n            assert!(timer.bits == 16 || timer.bits == 32);\n            let bits = if timer.bits == 16 {\n                quote! { Bits16 }\n            } else {\n                quote! { Bits32 }\n            };\n\n            quote! {\n                impl_timer!(#name, #bits);\n            }\n        });\n\n    quote! {\n        #(#timer_impls)*\n    }\n}\n\nfn generate_interrupts() -> TokenStream {\n    // Generate interrupt module\n    let interrupts: Vec<Ident> = METADATA\n        .interrupts\n        .iter()\n        .map(|interrupt| Ident::new(interrupt.name, Span::call_site()))\n        .collect();\n\n    let group_interrupt_enables = METADATA\n        .interrupts\n        .iter()\n        .filter(|interrupt| interrupt.name.contains(\"GROUP\"))\n        .map(|interrupt| {\n            let name = Ident::new(interrupt.name, Span::call_site());\n\n            quote! {\n                crate::interrupt::typelevel::#name::enable();\n            }\n        });\n\n    // Generate interrupt enables for groups\n    quote! {\n        embassy_hal_internal::interrupt_mod! {\n            #(#interrupts),*\n        }\n\n        pub fn enable_group_interrupts(_cs: critical_section::CriticalSection) {\n            use crate::interrupt::typelevel::Interrupt;\n\n            // This is empty for C1105/6\n            #[allow(unused_unsafe)]\n            unsafe {\n                #(#group_interrupt_enables)*\n            }\n        }\n    }\n}\n\nfn generate_peripheral_instances() -> TokenStream {\n    let mut impls = Vec::<TokenStream>::new();\n\n    for peripheral in METADATA.peripherals {\n        let peri = format_ident!(\"{}\", peripheral.name);\n        let fifo_size = peripheral.sys_fentries;\n\n        let tokens = match peripheral.kind {\n            \"uart\" => Some(quote! { impl_uart_instance!(#peri); }),\n            \"i2c\" => Some(quote! { impl_i2c_instance!(#peri, #fifo_size); }),\n            \"wwdt\" => Some(quote! { impl_wwdt_instance!(#peri); }),\n            \"adc\" => Some(quote! { impl_adc_instance!(#peri); }),\n            \"mathacl\" => Some(quote! { impl_mathacl_instance!(#peri); }),\n            _ => None,\n        };\n\n        if let Some(tokens) = tokens {\n            impls.push(tokens);\n        }\n    }\n\n    // DMA channels\n    for dma_channel in METADATA.dma_channels.iter() {\n        let peri = format_ident!(\"DMA_CH{}\", dma_channel.number);\n        let num = dma_channel.number;\n\n        if dma_channel.full {\n            impls.push(quote! { impl_full_dma_channel!(#peri, #num); });\n        } else {\n            impls.push(quote! { impl_dma_channel!(#peri, #num); });\n        }\n    }\n\n    quote! {\n        #(#impls)*\n    }\n}\n\nfn generate_pin_trait_impls() -> TokenStream {\n    let mut impls = Vec::<TokenStream>::new();\n\n    for peripheral in METADATA.peripherals {\n        for pin in peripheral.pins {\n            let key = (peripheral.kind, pin.signal);\n\n            let pin_name = format_ident!(\"{}\", pin.pin);\n            let peri = format_ident!(\"{}\", peripheral.name);\n            let pf = pin.pf;\n\n            // Will be filled in when uart implementation is finished\n            let _ = pin_name;\n            let _ = peri;\n            let _ = pf;\n\n            let tokens = match key {\n                (\"uart\", \"TX\") => Some(quote! { impl_uart_tx_pin!(#peri, #pin_name, #pf); }),\n                (\"uart\", \"RX\") => Some(quote! { impl_uart_rx_pin!(#peri, #pin_name, #pf); }),\n                (\"uart\", \"CTS\") => Some(quote! { impl_uart_cts_pin!(#peri, #pin_name, #pf); }),\n                (\"uart\", \"RTS\") => Some(quote! { impl_uart_rts_pin!(#peri, #pin_name, #pf); }),\n                (\"i2c\", \"SDA\") => Some(quote! { impl_i2c_sda_pin!(#peri, #pin_name, #pf); }),\n                (\"i2c\", \"SCL\") => Some(quote! { impl_i2c_scl_pin!(#peri, #pin_name, #pf); }),\n                (\"adc\", s) => {\n                    let signal = s.parse::<u8>().unwrap();\n                    Some(quote! { impl_adc_pin!(#peri, #pin_name, #signal); })\n                }\n\n                _ => None,\n            };\n\n            if let Some(tokens) = tokens {\n                impls.push(tokens);\n            }\n        }\n    }\n\n    quote! {\n        #(#impls)*\n    }\n}\n\nfn select_gpio_features(cfgs: &mut CfgSet) {\n    cfgs.declare_all(&[\n        \"gpioa_interrupt\",\n        \"gpioa_group\",\n        \"gpiob_interrupt\",\n        \"gpiob_group\",\n        \"gpioc_group\",\n    ]);\n\n    for interrupt in METADATA.interrupts.iter() {\n        match interrupt.name {\n            \"GPIOA\" => cfgs.enable(\"gpioa_interrupt\"),\n            \"GPIOB\" => cfgs.enable(\"gpiob_interrupt\"),\n            _ => (),\n        }\n    }\n\n    for group in METADATA.interrupt_groups.iter() {\n        for interrupt in group.interrupts {\n            match interrupt.name {\n                \"GPIOA\" => cfgs.enable(\"gpioa_group\"),\n                \"GPIOB\" => cfgs.enable(\"gpiob_group\"),\n                \"GPIOC\" => cfgs.enable(\"gpioc_group\"),\n                _ => (),\n            }\n        }\n    }\n}\n\n/// rustfmt a given path.\n/// Failures are logged to stderr and ignored.\nfn rustfmt(path: impl AsRef<Path>) {\n    let path = path.as_ref();\n    match Command::new(\"rustfmt\").args([path]).output() {\n        Err(e) => {\n            eprintln!(\"failed to exec rustfmt {:?}: {:?}\", path, e);\n        }\n        Ok(out) => {\n            if !out.status.success() {\n                eprintln!(\"rustfmt {:?} failed:\", path);\n                eprintln!(\"=== STDOUT:\");\n                std::io::stderr().write_all(&out.stdout).unwrap();\n                eprintln!(\"=== STDERR:\");\n                std::io::stderr().write_all(&out.stderr).unwrap();\n            }\n        }\n    }\n}\n\n#[allow(dead_code)]\nstruct TimerDesc {\n    bits: u8,\n    /// Is there an 8-bit prescaler\n    prescaler: bool,\n    /// Is there a repeat counter\n    repeat_counter: bool,\n    ccp_channels_internal: u8,\n    ccp_channels_external: u8,\n    external_pwm_channels: u8,\n    phase_load: bool,\n    shadow_load: bool,\n    shadow_ccs: bool,\n    deadband: bool,\n    fault_handler: bool,\n    qei_hall: bool,\n}\n\n/// Description of all timer instances.\nconst TIMERS: LazyLock<HashMap<String, TimerDesc>> = LazyLock::new(|| {\n    let mut map = HashMap::new();\n    map.insert(\n        \"TIMB0\".into(),\n        TimerDesc {\n            bits: 16,\n            prescaler: true,\n            repeat_counter: false,\n            ccp_channels_internal: 2,\n            ccp_channels_external: 2,\n            external_pwm_channels: 0,\n            phase_load: false,\n            shadow_load: false,\n            shadow_ccs: false,\n            deadband: false,\n            fault_handler: false,\n            qei_hall: false,\n        },\n    );\n\n    map.insert(\n        \"TIMG0\".into(),\n        TimerDesc {\n            bits: 16,\n            prescaler: true,\n            repeat_counter: false,\n            ccp_channels_internal: 2,\n            ccp_channels_external: 2,\n            external_pwm_channels: 2,\n            phase_load: false,\n            shadow_load: false,\n            shadow_ccs: false,\n            deadband: false,\n            fault_handler: false,\n            qei_hall: false,\n        },\n    );\n\n    map.insert(\n        \"TIMG1\".into(),\n        TimerDesc {\n            bits: 16,\n            prescaler: true,\n            repeat_counter: false,\n            ccp_channels_internal: 2,\n            ccp_channels_external: 2,\n            external_pwm_channels: 2,\n            phase_load: false,\n            shadow_load: false,\n            shadow_ccs: false,\n            deadband: false,\n            fault_handler: false,\n            qei_hall: false,\n        },\n    );\n\n    map.insert(\n        \"TIMG2\".into(),\n        TimerDesc {\n            bits: 16,\n            prescaler: true,\n            repeat_counter: false,\n            ccp_channels_internal: 2,\n            ccp_channels_external: 2,\n            external_pwm_channels: 2,\n            phase_load: false,\n            shadow_load: false,\n            shadow_ccs: false,\n            deadband: false,\n            fault_handler: false,\n            qei_hall: false,\n        },\n    );\n\n    map.insert(\n        \"TIMG3\".into(),\n        TimerDesc {\n            bits: 16,\n            prescaler: true,\n            repeat_counter: false,\n            ccp_channels_internal: 2,\n            ccp_channels_external: 2,\n            external_pwm_channels: 2,\n            phase_load: false,\n            shadow_load: false,\n            shadow_ccs: false,\n            deadband: false,\n            fault_handler: false,\n            qei_hall: false,\n        },\n    );\n\n    map.insert(\n        \"TIMG4\".into(),\n        TimerDesc {\n            bits: 16,\n            prescaler: true,\n            repeat_counter: false,\n            ccp_channels_internal: 2,\n            ccp_channels_external: 2,\n            external_pwm_channels: 2,\n            phase_load: false,\n            shadow_load: true,\n            shadow_ccs: true,\n            deadband: false,\n            fault_handler: false,\n            qei_hall: false,\n        },\n    );\n\n    map.insert(\n        \"TIMG5\".into(),\n        TimerDesc {\n            bits: 16,\n            prescaler: true,\n            repeat_counter: false,\n            ccp_channels_internal: 2,\n            ccp_channels_external: 2,\n            external_pwm_channels: 2,\n            phase_load: false,\n            shadow_load: true,\n            shadow_ccs: true,\n            deadband: false,\n            fault_handler: false,\n            qei_hall: false,\n        },\n    );\n\n    map.insert(\n        \"TIMG6\".into(),\n        TimerDesc {\n            bits: 16,\n            prescaler: true,\n            repeat_counter: false,\n            ccp_channels_internal: 2,\n            ccp_channels_external: 2,\n            external_pwm_channels: 2,\n            phase_load: false,\n            shadow_load: true,\n            shadow_ccs: true,\n            deadband: false,\n            fault_handler: false,\n            qei_hall: false,\n        },\n    );\n\n    map.insert(\n        \"TIMG7\".into(),\n        TimerDesc {\n            bits: 16,\n            prescaler: true,\n            repeat_counter: false,\n            ccp_channels_internal: 2,\n            ccp_channels_external: 2,\n            external_pwm_channels: 2,\n            phase_load: false,\n            shadow_load: true,\n            shadow_ccs: true,\n            deadband: false,\n            fault_handler: false,\n            qei_hall: false,\n        },\n    );\n\n    map.insert(\n        \"TIMG8\".into(),\n        TimerDesc {\n            bits: 16,\n            prescaler: true,\n            repeat_counter: false,\n            ccp_channels_internal: 2,\n            ccp_channels_external: 2,\n            external_pwm_channels: 2,\n            phase_load: false,\n            shadow_load: false,\n            shadow_ccs: false,\n            deadband: false,\n            fault_handler: false,\n            qei_hall: true,\n        },\n    );\n\n    map.insert(\n        \"TIMG9\".into(),\n        TimerDesc {\n            bits: 16,\n            prescaler: true,\n            repeat_counter: false,\n            ccp_channels_internal: 2,\n            ccp_channels_external: 2,\n            external_pwm_channels: 2,\n            phase_load: false,\n            shadow_load: false,\n            shadow_ccs: false,\n            deadband: false,\n            fault_handler: false,\n            qei_hall: true,\n        },\n    );\n\n    map.insert(\n        \"TIMG10\".into(),\n        TimerDesc {\n            bits: 16,\n            prescaler: true,\n            repeat_counter: false,\n            ccp_channels_internal: 2,\n            ccp_channels_external: 2,\n            external_pwm_channels: 2,\n            phase_load: false,\n            shadow_load: false,\n            shadow_ccs: false,\n            deadband: false,\n            fault_handler: false,\n            qei_hall: true,\n        },\n    );\n\n    map.insert(\n        \"TIMG11\".into(),\n        TimerDesc {\n            bits: 16,\n            prescaler: true,\n            repeat_counter: false,\n            ccp_channels_internal: 2,\n            ccp_channels_external: 2,\n            external_pwm_channels: 2,\n            phase_load: false,\n            shadow_load: false,\n            shadow_ccs: false,\n            deadband: false,\n            fault_handler: false,\n            qei_hall: true,\n        },\n    );\n\n    map.insert(\n        \"TIMG12\".into(),\n        TimerDesc {\n            bits: 32,\n            prescaler: false,\n            repeat_counter: false,\n            ccp_channels_internal: 2,\n            ccp_channels_external: 2,\n            external_pwm_channels: 2,\n            phase_load: false,\n            shadow_load: false,\n            shadow_ccs: true,\n            deadband: false,\n            fault_handler: false,\n            qei_hall: false,\n        },\n    );\n\n    map.insert(\n        \"TIMG13\".into(),\n        TimerDesc {\n            bits: 32,\n            prescaler: false,\n            repeat_counter: false,\n            ccp_channels_internal: 2,\n            ccp_channels_external: 2,\n            external_pwm_channels: 2,\n            phase_load: false,\n            shadow_load: false,\n            shadow_ccs: true,\n            deadband: false,\n            fault_handler: false,\n            qei_hall: false,\n        },\n    );\n\n    map.insert(\n        \"TIMG14\".into(),\n        TimerDesc {\n            bits: 16,\n            prescaler: true,\n            repeat_counter: false,\n            ccp_channels_internal: 4,\n            ccp_channels_external: 4,\n            external_pwm_channels: 4,\n            phase_load: false,\n            shadow_load: false,\n            shadow_ccs: false,\n            deadband: false,\n            fault_handler: false,\n            qei_hall: false,\n        },\n    );\n\n    map.insert(\n        \"TIMA0\".into(),\n        TimerDesc {\n            bits: 16,\n            prescaler: true,\n            repeat_counter: true,\n            ccp_channels_internal: 4,\n            ccp_channels_external: 2,\n            external_pwm_channels: 8,\n            phase_load: true,\n            shadow_load: true,\n            shadow_ccs: true,\n            deadband: true,\n            fault_handler: true,\n            qei_hall: false,\n        },\n    );\n\n    map.insert(\n        \"TIMA1\".into(),\n        TimerDesc {\n            bits: 16,\n            prescaler: true,\n            repeat_counter: true,\n            ccp_channels_internal: 2,\n            ccp_channels_external: 2,\n            external_pwm_channels: 4,\n            phase_load: true,\n            shadow_load: true,\n            shadow_ccs: true,\n            deadband: true,\n            fault_handler: true,\n            qei_hall: false,\n        },\n    );\n\n    map\n});\n\nenum GetOneError {\n    None,\n    Multiple,\n}\n\ntrait IteratorExt: Iterator {\n    fn get_one(self) -> Result<Self::Item, GetOneError>;\n}\n\nimpl<T: Iterator> IteratorExt for T {\n    fn get_one(mut self) -> Result<Self::Item, GetOneError> {\n        match self.next() {\n            None => Err(GetOneError::None),\n            Some(res) => match self.next() {\n                Some(_) => Err(GetOneError::Multiple),\n                None => Ok(res),\n            },\n        }\n    }\n}\n"
  },
  {
    "path": "embassy-mspm0/build_common.rs",
    "content": "// NOTE: this file is copy-pasted between several Embassy crates, because there is no\n// straightforward way to share this code:\n// - it cannot be placed into the root of the repo and linked from each build.rs using `#[path =\n// \"../build_common.rs\"]`, because `cargo publish` requires that all files published with a crate\n// reside in the crate's directory,\n// - it cannot be symlinked from `embassy-xxx/build_common.rs` to `../build_common.rs`, because\n// symlinks don't work on Windows.\n\nuse std::collections::HashSet;\nuse std::env;\n\n/// Helper for emitting cargo instruction for enabling configs (`cargo:rustc-cfg=X`) and declaring\n/// them (`cargo:rust-check-cfg=cfg(X)`).\n#[derive(Debug)]\npub struct CfgSet {\n    enabled: HashSet<String>,\n    declared: HashSet<String>,\n}\n\nimpl CfgSet {\n    pub fn new() -> Self {\n        Self {\n            enabled: HashSet::new(),\n            declared: HashSet::new(),\n        }\n    }\n\n    /// Enable a config, which can then be used in `#[cfg(...)]` for conditional compilation.\n    ///\n    /// All configs that can potentially be enabled should be unconditionally declared using\n    /// [`Self::declare()`].\n    pub fn enable(&mut self, cfg: impl AsRef<str>) {\n        if self.enabled.insert(cfg.as_ref().to_owned()) {\n            println!(\"cargo:rustc-cfg={}\", cfg.as_ref());\n        }\n    }\n\n    pub fn enable_all(&mut self, cfgs: &[impl AsRef<str>]) {\n        for cfg in cfgs.iter() {\n            self.enable(cfg.as_ref());\n        }\n    }\n\n    /// Declare a valid config for conditional compilation, without enabling it.\n    ///\n    /// This enables rustc to check that the configs in `#[cfg(...)]` attributes are valid.\n    pub fn declare(&mut self, cfg: impl AsRef<str>) {\n        if self.declared.insert(cfg.as_ref().to_owned()) {\n            println!(\"cargo:rustc-check-cfg=cfg({})\", cfg.as_ref());\n        }\n    }\n\n    pub fn declare_all(&mut self, cfgs: &[impl AsRef<str>]) {\n        for cfg in cfgs.iter() {\n            self.declare(cfg.as_ref());\n        }\n    }\n\n    pub fn set(&mut self, cfg: impl Into<String>, enable: bool) {\n        let cfg = cfg.into();\n        if enable {\n            self.enable(cfg.clone());\n        }\n        self.declare(cfg);\n    }\n}\n\n/// Sets configs that describe the target platform.\npub fn set_target_cfgs(cfgs: &mut CfgSet) {\n    let target = env::var(\"TARGET\").unwrap();\n\n    if target.starts_with(\"thumbv6m-\") {\n        cfgs.enable_all(&[\"cortex_m\", \"armv6m\"]);\n    } else if target.starts_with(\"thumbv7m-\") {\n        cfgs.enable_all(&[\"cortex_m\", \"armv7m\"]);\n    } else if target.starts_with(\"thumbv7em-\") {\n        cfgs.enable_all(&[\"cortex_m\", \"armv7m\", \"armv7em\"]);\n    } else if target.starts_with(\"thumbv8m.base\") {\n        cfgs.enable_all(&[\"cortex_m\", \"armv8m\", \"armv8m_base\"]);\n    } else if target.starts_with(\"thumbv8m.main\") {\n        cfgs.enable_all(&[\"cortex_m\", \"armv8m\", \"armv8m_main\"]);\n    }\n    cfgs.declare_all(&[\n        \"cortex_m\",\n        \"armv6m\",\n        \"armv7m\",\n        \"armv7em\",\n        \"armv8m\",\n        \"armv8m_base\",\n        \"armv8m_main\",\n    ]);\n\n    cfgs.set(\"has_fpu\", target.ends_with(\"-eabihf\"));\n}\n"
  },
  {
    "path": "embassy-mspm0/src/adc.rs",
    "content": "#![macro_use]\n\nuse core::future::poll_fn;\nuse core::marker::PhantomData;\nuse core::task::Poll;\n\nuse embassy_hal_internal::{PeripheralType, impl_peripheral};\nuse embassy_sync::waitqueue::AtomicWaker;\n\nuse crate::interrupt::{Interrupt, InterruptExt};\nuse crate::mode::{Async, Blocking, Mode};\nuse crate::pac::adc::{Adc as Regs, vals};\nuse crate::{Peri, interrupt};\n\n/// Interrupt handler.\npub struct InterruptHandler<T: Instance> {\n    _phantom: PhantomData<T>,\n}\n\nimpl<T: Instance> interrupt::typelevel::Handler<T::Interrupt> for InterruptHandler<T> {\n    unsafe fn on_interrupt() {\n        // Mis is cleared upon reading iidx\n        let iidx = T::info().regs.cpu_int(0).iidx().read().stat();\n        // TODO: Running in sequence mode, we get an interrupt per finished result. It would be\n        // nice to wake up only after all results are finished.\n        if vals::CpuIntIidxStat::MEMRESIFG0 <= iidx && iidx <= vals::CpuIntIidxStat::MEMRESIFG23 {\n            T::state().waker.wake();\n        }\n    }\n}\n\n// Constants from the metapac crate\nconst ADC_VRSEL: u8 = crate::_generated::ADC_VRSEL;\nconst ADC_MEMCTL: u8 = crate::_generated::ADC_MEMCTL;\n\n#[derive(Clone, Copy, PartialEq, Eq, Debug)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\n/// Conversion resolution of the ADC results.\npub enum Resolution {\n    /// 12-bits resolution\n    BIT12,\n\n    /// 10-bits resolution\n    BIT10,\n\n    /// 8-bits resolution\n    BIT8,\n}\n\nimpl Resolution {\n    /// Number of bits of the resolution.\n    pub fn bits(&self) -> u8 {\n        match self {\n            Resolution::BIT12 => 12,\n            Resolution::BIT10 => 10,\n            Resolution::BIT8 => 8,\n        }\n    }\n}\n\n#[derive(Clone, Copy, PartialEq, Eq, Debug)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\n/// Reference voltage (Vref) selection for the ADC channels.\npub enum Vrsel {\n    /// VDDA reference\n    VddaVssa = 0,\n\n    /// External reference from pin\n    ExtrefVrefm = 1,\n\n    /// Internal reference\n    IntrefVssa = 2,\n\n    /// VDDA and VREFM connected to VREF+ and VREF- of ADC\n    #[cfg(adc_neg_vref)]\n    VddaVrefm = 3,\n\n    /// INTREF and VREFM connected to VREF+ and VREF- of ADC\n    #[cfg(adc_neg_vref)]\n    IntrefVrefm = 4,\n}\n\n/// ADC configuration.\n#[derive(Copy, Clone)]\n#[non_exhaustive]\npub struct Config {\n    /// Resolution of the ADC conversion. The number of bits used to represent an ADC measurement.\n    pub resolution: Resolution,\n    /// ADC voltage reference selection.\n    ///\n    /// This value is used when reading a single channel. When reading a sequence\n    /// the vr_select is provided per channel.\n    pub vr_select: Vrsel,\n    /// The sample time in number of ADC sample clock cycles.\n    pub sample_time: u16,\n}\n\nimpl Default for Config {\n    fn default() -> Self {\n        Self {\n            resolution: Resolution::BIT12,\n            vr_select: Vrsel::VddaVssa,\n            sample_time: 50,\n        }\n    }\n}\n\n/// ADC (Analog to Digial Converter) Driver.\npub struct Adc<'d, T: Instance, M: Mode> {\n    #[allow(unused)]\n    adc: crate::Peri<'d, T>,\n    info: &'static Info,\n    state: &'static State,\n    config: Config,\n    _phantom: PhantomData<M>,\n}\n\nimpl<'d, T: Instance> Adc<'d, T, Blocking> {\n    /// A new blocking ADC driver instance.\n    pub fn new_blocking(peri: Peri<'d, T>, config: Config) -> Self {\n        let mut this = Self {\n            adc: peri,\n            info: T::info(),\n            state: T::state(),\n            config,\n            _phantom: PhantomData,\n        };\n        this.setup();\n        this\n    }\n}\n\nimpl<'d, T: Instance> Adc<'d, T, Async> {\n    /// A new asynchronous ADC driver instance.\n    pub fn new_async(\n        peri: Peri<'d, T>,\n        config: Config,\n        _irqs: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd,\n    ) -> Self {\n        let mut this = Self {\n            adc: peri,\n            info: T::info(),\n            state: T::state(),\n            config,\n            _phantom: PhantomData,\n        };\n        this.setup();\n        unsafe {\n            this.info.interrupt.enable();\n        }\n        this\n    }\n}\n\nimpl<'d, T: Instance, M: Mode> Adc<'d, T, M> {\n    const SINGLE_CHANNEL: u8 = 0;\n\n    fn setup(&mut self) {\n        let config = &self.config;\n        assert!(\n            (config.vr_select as u8) < ADC_VRSEL,\n            \"Reference voltage selection out of bounds\"\n        );\n        // Reset adc\n        self.info.regs.gprcm(0).rstctl().write(|reg| {\n            reg.set_resetstkyclr(true);\n            reg.set_resetassert(true);\n            reg.set_key(vals::ResetKey::KEY);\n        });\n\n        // Power up adc\n        self.info.regs.gprcm(0).pwren().modify(|reg| {\n            reg.set_enable(true);\n            reg.set_key(vals::PwrenKey::KEY);\n        });\n\n        // Wait for cycles similar to TI power setup\n        cortex_m::asm::delay(16);\n\n        // Set clock config\n        self.info.regs.gprcm(0).clkcfg().modify(|reg| {\n            reg.set_key(vals::ClkcfgKey::KEY);\n            reg.set_sampclk(vals::Sampclk::SYSOSC);\n        });\n        self.info.regs.ctl0().modify(|reg| {\n            reg.set_sclkdiv(vals::Sclkdiv::DIV_BY_4);\n        });\n        self.info.regs.clkfreq().modify(|reg| {\n            reg.set_frange(vals::Frange::RANGE24TO32);\n        });\n\n        // Init single conversion with software trigger and auto sampling\n        //\n        // We use sequence to support sequence operation in the future, but only set up a single\n        // channel\n        self.info.regs.ctl1().modify(|reg| {\n            reg.set_conseq(vals::Conseq::SEQUENCE);\n            reg.set_sampmode(vals::Sampmode::AUTO);\n            reg.set_trigsrc(vals::Trigsrc::SOFTWARE);\n        });\n        let res = match config.resolution {\n            Resolution::BIT12 => vals::Res::BIT_12,\n            Resolution::BIT10 => vals::Res::BIT_10,\n            Resolution::BIT8 => vals::Res::BIT_8,\n        };\n        self.info.regs.ctl2().modify(|reg| {\n            // Startadd detemines the channel used in single mode.\n            reg.set_startadd(Self::SINGLE_CHANNEL);\n            reg.set_endadd(Self::SINGLE_CHANNEL);\n            reg.set_res(res);\n            reg.set_df(false);\n        });\n\n        // Set the sample time used by all channels for now\n        self.info.regs.scomp0().modify(|reg| {\n            reg.set_val(config.sample_time);\n        });\n    }\n\n    fn setup_blocking_channel(&mut self, channel: &Peri<'d, impl AdcChannel<T>>) {\n        channel.setup();\n\n        // CTL0.ENC must be 0 to write the MEMCTL register\n        while self.info.regs.ctl0().read().enc() {\n            // Wait until the ADC is not in conversion mode\n        }\n\n        // Conversion mem config\n        let vrsel = vals::Vrsel::from_bits(self.config.vr_select as u8);\n        self.info.regs.memctl(Self::SINGLE_CHANNEL as usize).modify(|reg| {\n            reg.set_chansel(channel.channel());\n            reg.set_vrsel(vrsel);\n            reg.set_stime(vals::Stime::SEL_SCOMP0);\n            reg.set_avgen(false);\n            reg.set_bcsen(false);\n            reg.set_trig(vals::Trig::AUTO_NEXT);\n            reg.set_wincomp(false);\n        });\n        self.info.regs.ctl2().modify(|reg| {\n            // Set end address to the number of used channels\n            reg.set_endadd(Self::SINGLE_CHANNEL);\n        });\n    }\n\n    fn enable_conversion(&mut self) {\n        // Enable conversion\n        self.info.regs.ctl0().modify(|reg| {\n            reg.set_enc(true);\n        });\n    }\n\n    fn start_conversion(&mut self) {\n        // Start conversion\n        self.info.regs.ctl1().modify(|reg| {\n            reg.set_sc(vals::Sc::START);\n        });\n    }\n\n    fn conversion_result(&mut self, channel_id: usize) -> u16 {\n        // Read result\n        self.info.regs.memres(channel_id).read().data()\n    }\n\n    /// Read one ADC channel in blocking mode using the config provided at initialization.\n    pub fn blocking_read(&mut self, channel: &Peri<'d, impl AdcChannel<T>>) -> u16 {\n        self.setup_blocking_channel(channel);\n        self.enable_conversion();\n        self.start_conversion();\n\n        while self.info.regs.ctl0().read().enc() {}\n\n        self.conversion_result(Self::SINGLE_CHANNEL as usize)\n    }\n}\n\nimpl<'d, T: Instance> Adc<'d, T, Async> {\n    /// Maximum length allowed for [`Self::read_sequence`].\n    pub const MAX_SEQUENCE_LEN: usize = ADC_MEMCTL as usize;\n\n    async fn wait_for_conversion(&self) {\n        let info = self.info;\n        let state = self.state;\n        poll_fn(move |cx| {\n            state.waker.register(cx.waker());\n\n            if !info.regs.ctl0().read().enc() {\n                Poll::Ready(())\n            } else {\n                Poll::Pending\n            }\n        })\n        .await;\n    }\n\n    fn setup_async_channel(&self, id: usize, channel: &Peri<'d, impl AdcChannel<T>>, vrsel: Vrsel) {\n        let vrsel = vals::Vrsel::from_bits(vrsel as u8);\n        // Conversion mem config\n        self.info.regs.memctl(id).modify(|reg| {\n            reg.set_chansel(channel.channel());\n            reg.set_vrsel(vrsel);\n            reg.set_stime(vals::Stime::SEL_SCOMP0);\n            reg.set_avgen(false);\n            reg.set_bcsen(false);\n            reg.set_trig(vals::Trig::AUTO_NEXT);\n            reg.set_wincomp(false);\n        });\n\n        // Clear interrupt status\n        self.info.regs.cpu_int(0).iclr().write(|reg| {\n            reg.set_memresifg(id, true);\n        });\n        // Enable interrupt\n        self.info.regs.cpu_int(0).imask().modify(|reg| {\n            reg.set_memresifg(id, true);\n        });\n    }\n\n    /// Read one ADC channel asynchronously using the config provided at initialization.\n    pub async fn read_channel(&mut self, channel: &Peri<'d, impl AdcChannel<T>>) -> u16 {\n        channel.setup();\n\n        // CTL0.ENC must be 0 to write the MEMCTL register\n        self.wait_for_conversion().await;\n\n        self.info.regs.ctl2().modify(|reg| {\n            // Set end address to the number of used channels\n            reg.set_endadd(Self::SINGLE_CHANNEL);\n        });\n\n        self.setup_async_channel(Self::SINGLE_CHANNEL as usize, channel, self.config.vr_select);\n\n        self.enable_conversion();\n        self.start_conversion();\n        self.wait_for_conversion().await;\n\n        self.conversion_result(Self::SINGLE_CHANNEL as usize)\n    }\n\n    /// Read one or multiple ADC channels using the Vrsel provided per channel.\n    ///\n    /// `sequence` iterator and `readings` must have the same length.\n    ///\n    /// Example\n    /// ```rust,ignore\n    /// use embassy_mspm0::adc::{Adc, AdcChannel, Vrsel};\n    ///\n    /// let mut adc = Adc::new_async(p.ADC0, adc_config, Irqs);\n    /// let sequence = [(&p.PA22.into(), Vrsel::VddaVssa), (&p.PA20.into(), Vrsel::VddaVssa)];\n    /// let mut readings = [0u16; 2];\n    ///\n    /// adc.read_sequence(sequence.into_iter(), &mut readings).await;\n    /// defmt::info!(\"Measurements: {}\", readings);\n    /// ```\n    pub async fn read_sequence<'a>(\n        &mut self,\n        sequence: impl ExactSizeIterator<Item = (&'a Peri<'d, AnyAdcChannel<T>>, Vrsel)>,\n        readings: &mut [u16],\n    ) where\n        'd: 'a,\n    {\n        assert!(sequence.len() != 0, \"Asynchronous read sequence cannot be empty\");\n        assert!(\n            sequence.len() == readings.len(),\n            \"Sequence length must be equal to readings length\"\n        );\n        assert!(\n            sequence.len() <= Self::MAX_SEQUENCE_LEN,\n            \"Asynchronous read sequence cannot be more than {} in length\",\n            Self::MAX_SEQUENCE_LEN\n        );\n\n        // CTL0.ENC must be 0 to write the MEMCTL register\n        self.wait_for_conversion().await;\n\n        self.info.regs.ctl2().modify(|reg| {\n            // Set end address to the number of used channels\n            reg.set_endadd((sequence.len() - 1) as u8);\n        });\n\n        for (i, (channel, vrsel)) in sequence.enumerate() {\n            self.setup_async_channel(i, channel, vrsel);\n        }\n        self.enable_conversion();\n        self.start_conversion();\n        self.wait_for_conversion().await;\n\n        for (i, r) in readings.iter_mut().enumerate() {\n            *r = self.conversion_result(i);\n        }\n    }\n}\n\n/// Peripheral instance trait.\n#[allow(private_bounds)]\npub trait Instance: PeripheralType + SealedInstance {\n    type Interrupt: crate::interrupt::typelevel::Interrupt;\n}\n\n/// Peripheral state.\npub(crate) struct State {\n    waker: AtomicWaker,\n}\n\nimpl State {\n    pub const fn new() -> Self {\n        Self {\n            waker: AtomicWaker::new(),\n        }\n    }\n}\n\n/// Peripheral information.\npub(crate) struct Info {\n    pub(crate) regs: Regs,\n    pub(crate) interrupt: Interrupt,\n}\n\n/// Peripheral instance trait.\npub(crate) trait SealedInstance {\n    fn info() -> &'static Info;\n    fn state() -> &'static State;\n}\n\nmacro_rules! impl_adc_instance {\n    ($instance: ident) => {\n        impl crate::adc::SealedInstance for crate::peripherals::$instance {\n            fn info() -> &'static crate::adc::Info {\n                use crate::adc::Info;\n                use crate::interrupt::typelevel::Interrupt;\n\n                static INFO: Info = Info {\n                    regs: crate::pac::$instance,\n                    interrupt: crate::interrupt::typelevel::$instance::IRQ,\n                };\n                &INFO\n            }\n\n            fn state() -> &'static crate::adc::State {\n                use crate::adc::State;\n\n                static STATE: State = State::new();\n                &STATE\n            }\n        }\n\n        impl crate::adc::Instance for crate::peripherals::$instance {\n            type Interrupt = crate::interrupt::typelevel::$instance;\n        }\n    };\n}\n\n/// A type-erased channel for a given ADC instance.\n///\n/// This is useful in scenarios where you need the ADC channels to have the same type, such as\n/// storing them in an array.\npub struct AnyAdcChannel<T> {\n    pub(crate) channel: u8,\n    pub(crate) _phantom: PhantomData<T>,\n}\n\nimpl_peripheral!(AnyAdcChannel<T: Instance>);\nimpl<T: Instance> AdcChannel<T> for AnyAdcChannel<T> {}\nimpl<T: Instance> SealedAdcChannel<T> for AnyAdcChannel<T> {\n    fn channel(&self) -> u8 {\n        self.channel\n    }\n}\n\nimpl<T> AnyAdcChannel<T> {\n    #[allow(unused)]\n    pub(crate) fn get_hw_channel(&self) -> u8 {\n        self.channel\n    }\n}\n\n/// ADC channel.\n#[allow(private_bounds)]\npub trait AdcChannel<T>: PeripheralType + Into<AnyAdcChannel<T>> + SealedAdcChannel<T> + Sized {}\n\npub(crate) trait SealedAdcChannel<T> {\n    fn setup(&self) {}\n\n    fn channel(&self) -> u8;\n}\n\nmacro_rules! impl_adc_pin {\n    ($inst: ident, $pin: ident, $ch: expr) => {\n        impl crate::adc::AdcChannel<peripherals::$inst> for crate::peripherals::$pin {}\n        impl crate::adc::SealedAdcChannel<peripherals::$inst> for crate::peripherals::$pin {\n            fn setup(&self) {\n                crate::gpio::SealedPin::set_as_analog(self);\n            }\n\n            fn channel(&self) -> u8 {\n                $ch\n            }\n        }\n\n        impl From<crate::peripherals::$pin> for crate::adc::AnyAdcChannel<peripherals::$inst> {\n            fn from(val: crate::peripherals::$pin) -> Self {\n                crate::adc::SealedAdcChannel::<peripherals::$inst>::setup(&val);\n\n                Self {\n                    channel: crate::adc::SealedAdcChannel::<peripherals::$inst>::channel(&val),\n                    _phantom: core::marker::PhantomData,\n                }\n            }\n        }\n    };\n}\n"
  },
  {
    "path": "embassy-mspm0/src/dma.rs",
    "content": "//! Direct Memory Access (DMA)\n\n#![macro_use]\n\nuse core::future::Future;\nuse core::mem;\nuse core::pin::Pin;\nuse core::sync::atomic::{Ordering, compiler_fence};\nuse core::task::{Context, Poll};\n\nuse critical_section::CriticalSection;\nuse embassy_hal_internal::interrupt::InterruptExt;\nuse embassy_hal_internal::{PeripheralType, impl_peripheral};\nuse embassy_sync::waitqueue::AtomicWaker;\nuse mspm0_metapac::common::{RW, Reg};\nuse mspm0_metapac::dma::regs;\nuse mspm0_metapac::dma::vals::{self, Autoen, Em, Incr, Preirq, Wdth};\n\nuse crate::{Peri, interrupt, pac};\n\n/// The burst size of a DMA transfer.\n#[derive(Debug, Clone, Copy, PartialEq, Eq)]\npub enum BurstSize {\n    /// The whole block transfer is completed in one transfer without interruption.\n    Complete,\n\n    /// The burst size is 8, after 9 transfers the block transfer is interrupted and the priority\n    /// is reevaluated.\n    _8,\n\n    /// The burst size is 16, after 17 transfers the block transfer is interrupted and the priority\n    /// is reevaluated.\n    _16,\n\n    /// The burst size is 32, after 32 transfers the block transfer is interrupted and the priority\n    /// is reevaluated.\n    _32,\n}\n\n/// DMA channel.\n#[allow(private_bounds)]\npub trait Channel: Into<AnyChannel> + PeripheralType {}\n\n/// Full DMA channel.\n#[allow(private_bounds)]\npub trait FullChannel: Channel + Into<AnyFullChannel> {}\n\n/// Type-erased DMA channel.\npub struct AnyChannel {\n    pub(crate) id: u8,\n}\nimpl_peripheral!(AnyChannel);\n\nimpl SealedChannel for AnyChannel {\n    fn id(&self) -> u8 {\n        self.id\n    }\n}\nimpl Channel for AnyChannel {}\n\n/// Type-erased full DMA channel.\npub struct AnyFullChannel {\n    pub(crate) id: u8,\n}\nimpl_peripheral!(AnyFullChannel);\n\nimpl SealedChannel for AnyFullChannel {\n    fn id(&self) -> u8 {\n        self.id\n    }\n}\nimpl Channel for AnyFullChannel {}\nimpl FullChannel for AnyFullChannel {}\n\nimpl From<AnyFullChannel> for AnyChannel {\n    fn from(value: AnyFullChannel) -> Self {\n        Self { id: value.id }\n    }\n}\n\n#[allow(private_bounds)]\npub trait Word: SealedWord {\n    /// Size in bytes for the width.\n    fn size() -> isize;\n}\n\nimpl SealedWord for u8 {\n    fn width() -> vals::Wdth {\n        vals::Wdth::BYTE\n    }\n}\nimpl Word for u8 {\n    fn size() -> isize {\n        1\n    }\n}\n\nimpl SealedWord for u16 {\n    fn width() -> vals::Wdth {\n        vals::Wdth::HALF\n    }\n}\nimpl Word for u16 {\n    fn size() -> isize {\n        2\n    }\n}\n\nimpl SealedWord for u32 {\n    fn width() -> vals::Wdth {\n        vals::Wdth::WORD\n    }\n}\nimpl Word for u32 {\n    fn size() -> isize {\n        4\n    }\n}\n\nimpl SealedWord for u64 {\n    fn width() -> vals::Wdth {\n        vals::Wdth::LONG\n    }\n}\nimpl Word for u64 {\n    fn size() -> isize {\n        8\n    }\n}\n\n// TODO: u128 (LONGLONG) support. G350x does support it, but other parts do not such as C110x. More metadata is\n// needed to properly enable this.\n// impl SealedWord for u128 {\n//     fn width() -> vals::Wdth {\n//         vals::Wdth::LONGLONG\n//     }\n// }\n// impl Word for u128 {\n//     fn size() -> isize {\n//         16\n//     }\n// }\n\n#[derive(Debug, Copy, Clone, PartialEq, Eq)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub enum Error {\n    /// The DMA transfer is too large.\n    ///\n    /// The hardware limits the DMA to 16384 transfers per channel at a time. This means that transferring\n    /// 16384 `u8` and 16384 `u64` are equivalent, since the DMA must copy 16384 values.\n    TooManyTransfers,\n}\n\n/// DMA transfer mode for basic channels.\n#[derive(Debug, Copy, Clone, PartialEq, Eq)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub enum TransferMode {\n    /// Each DMA trigger will transfer a single value.\n    Single,\n\n    /// Each DMA trigger will transfer the complete block with one trigger.\n    Block,\n}\n\n/// DMA transfer options.\n#[derive(Debug, Copy, Clone, PartialEq, Eq)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\n#[non_exhaustive]\npub struct TransferOptions {\n    /// DMA transfer mode.\n    pub mode: TransferMode,\n    // TODO: Read and write stride.\n}\n\nimpl Default for TransferOptions {\n    fn default() -> Self {\n        Self {\n            mode: TransferMode::Single,\n        }\n    }\n}\n\n/// DMA transfer.\n#[must_use = \"futures do nothing unless you `.await` or poll them\"]\npub struct Transfer<'a> {\n    channel: Peri<'a, AnyChannel>,\n}\n\nimpl<'a> Transfer<'a> {\n    /// Software trigger source.\n    ///\n    /// Using this trigger source means that a transfer will start immediately rather than waiting for\n    /// a hardware event. This can be useful if you want to do a DMA accelerated memcpy.\n    pub const SOFTWARE_TRIGGER: u8 = 0;\n\n    /// Create a new read DMA transfer.\n    pub unsafe fn new_read<SW: Word, DW: Word>(\n        channel: Peri<'a, impl Channel>,\n        trigger_source: u8,\n        src: *mut SW,\n        dst: &'a mut [DW],\n        options: TransferOptions,\n    ) -> Result<Self, Error> {\n        Self::new_read_raw(channel, trigger_source, src, dst, options)\n    }\n\n    /// Create a new read DMA transfer, using raw pointers.\n    pub unsafe fn new_read_raw<SW: Word, DW: Word>(\n        channel: Peri<'a, impl Channel>,\n        trigger_source: u8,\n        src: *mut SW,\n        dst: *mut [DW],\n        options: TransferOptions,\n    ) -> Result<Self, Error> {\n        verify_transfer::<DW>(dst)?;\n\n        let channel = channel.into();\n        channel.configure(\n            trigger_source,\n            src.cast(),\n            SW::width(),\n            dst.cast(),\n            DW::width(),\n            dst.len() as u16,\n            false,\n            true,\n            options,\n        );\n        channel.start();\n\n        Ok(Self { channel })\n    }\n\n    /// Create a new write DMA transfer.\n    pub unsafe fn new_write<SW: Word, DW: Word>(\n        channel: Peri<'a, impl Channel>,\n        trigger_source: u8,\n        src: &'a [SW],\n        dst: *mut DW,\n        options: TransferOptions,\n    ) -> Result<Self, Error> {\n        Self::new_write_raw(channel, trigger_source, src, dst, options)\n    }\n\n    /// Create a new write DMA transfer, using raw pointers.\n    pub unsafe fn new_write_raw<SW: Word, DW: Word>(\n        channel: Peri<'a, impl Channel>,\n        trigger_source: u8,\n        src: *const [SW],\n        dst: *mut DW,\n        options: TransferOptions,\n    ) -> Result<Self, Error> {\n        verify_transfer::<SW>(src)?;\n\n        let channel = channel.into();\n        channel.configure(\n            trigger_source,\n            src.cast(),\n            SW::width(),\n            dst.cast(),\n            DW::width(),\n            src.len() as u16,\n            true,\n            false,\n            options,\n        );\n        channel.start();\n\n        Ok(Self { channel })\n    }\n\n    // TODO: Copy between slices.\n\n    /// Request the transfer to resume.\n    pub fn resume(&mut self) {\n        self.channel.resume();\n    }\n\n    /// Request the transfer to pause, keeping the existing configuration for this channel.\n    /// To restart the transfer, call [`start`](Self::start) again.\n    ///\n    /// This doesn't immediately stop the transfer, you have to wait until [`is_running`](Self::is_running) returns false.\n    pub fn request_pause(&mut self) {\n        self.channel.request_pause();\n    }\n\n    /// Return whether this transfer is still running.\n    ///\n    /// If this returns [`false`], it can be because either the transfer finished, or\n    /// it was requested to stop early with [`request_stop`].\n    pub fn is_running(&mut self) -> bool {\n        self.channel.is_running()\n    }\n\n    /// Blocking wait until the transfer finishes.\n    pub fn blocking_wait(mut self) {\n        // \"Subsequent reads and writes cannot be moved ahead of preceding reads.\"\n        compiler_fence(Ordering::SeqCst);\n\n        while self.is_running() {}\n\n        // \"Subsequent reads and writes cannot be moved ahead of preceding reads.\"\n        compiler_fence(Ordering::SeqCst);\n\n        // Prevent drop from being called since we ran to completion (drop will try to pause).\n        mem::forget(self);\n    }\n}\n\nimpl<'a> Unpin for Transfer<'a> {}\nimpl<'a> Future for Transfer<'a> {\n    type Output = ();\n\n    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {\n        let state: &ChannelState = &STATE[self.channel.id as usize];\n\n        state.waker.register(cx.waker());\n\n        // \"Subsequent reads and writes cannot be moved ahead of preceding reads.\"\n        compiler_fence(Ordering::SeqCst);\n\n        if self.channel.is_running() {\n            Poll::Pending\n        } else {\n            Poll::Ready(())\n        }\n    }\n}\n\nimpl<'a> Drop for Transfer<'a> {\n    fn drop(&mut self) {\n        self.channel.request_pause();\n        while self.is_running() {}\n\n        // \"Subsequent reads and writes cannot be moved ahead of preceding reads.\"\n        compiler_fence(Ordering::SeqCst);\n    }\n}\n\n// impl details\n\nfn verify_transfer<W: Word>(ptr: *const [W]) -> Result<(), Error> {\n    if ptr.len() > (u16::MAX as usize) {\n        return Err(Error::TooManyTransfers);\n    }\n\n    // TODO: Stride checks\n\n    Ok(())\n}\n\nfn convert_burst_size(value: BurstSize) -> vals::Burstsz {\n    match value {\n        BurstSize::Complete => vals::Burstsz::INFINITI,\n        BurstSize::_8 => vals::Burstsz::BURST_8,\n        BurstSize::_16 => vals::Burstsz::BURST_16,\n        BurstSize::_32 => vals::Burstsz::BURST_32,\n    }\n}\n\nfn convert_mode(mode: TransferMode) -> vals::Tm {\n    match mode {\n        TransferMode::Single => vals::Tm::SINGLE,\n        TransferMode::Block => vals::Tm::BLOCK,\n    }\n}\n\nconst CHANNEL_COUNT: usize = crate::_generated::DMA_CHANNELS;\nstatic STATE: [ChannelState; CHANNEL_COUNT] = [const { ChannelState::new() }; CHANNEL_COUNT];\n\nstruct ChannelState {\n    waker: AtomicWaker,\n}\n\nimpl ChannelState {\n    const fn new() -> Self {\n        Self {\n            waker: AtomicWaker::new(),\n        }\n    }\n}\n\n/// SAFETY: Must only be called once.\n///\n/// Changing the burst size mid transfer may have some odd behavior.\npub(crate) unsafe fn init(_cs: CriticalSection, burst_size: BurstSize, round_robin: bool) {\n    pac::DMA.prio().modify(|prio| {\n        prio.set_burstsz(convert_burst_size(burst_size));\n        prio.set_roundrobin(round_robin);\n    });\n    pac::DMA.int_event(0).imask().modify(|w| {\n        w.set_dataerr(true);\n        w.set_addrerr(true);\n    });\n\n    interrupt::DMA.enable();\n}\n\npub(crate) trait SealedWord {\n    fn width() -> vals::Wdth;\n}\n\npub(crate) trait SealedChannel {\n    fn id(&self) -> u8;\n\n    #[inline]\n    fn tctl(&self) -> Reg<regs::Tctl, RW> {\n        pac::DMA.trig(self.id() as usize).tctl()\n    }\n\n    #[inline]\n    fn ctl(&self) -> Reg<regs::Ctl, RW> {\n        pac::DMA.chan(self.id() as usize).ctl()\n    }\n\n    #[inline]\n    fn sa(&self) -> Reg<u32, RW> {\n        pac::DMA.chan(self.id() as usize).sa()\n    }\n\n    #[inline]\n    fn da(&self) -> Reg<u32, RW> {\n        pac::DMA.chan(self.id() as usize).da()\n    }\n\n    #[inline]\n    fn sz(&self) -> Reg<regs::Sz, RW> {\n        pac::DMA.chan(self.id() as usize).sz()\n    }\n\n    #[inline]\n    fn mask_interrupt(&self, enable: bool) {\n        // Enabling interrupts is an RMW operation.\n        critical_section::with(|_cs| {\n            pac::DMA.int_event(0).imask().modify(|w| {\n                w.set_ch(self.id() as usize, enable);\n            });\n        })\n    }\n\n    /// # Safety\n    ///\n    /// - `src` must be valid for the lifetime of the transfer.\n    /// - `dst` must be valid for the lifetime of the transfer.\n    unsafe fn configure(\n        &self,\n        trigger_sel: u8,\n        src: *const u32,\n        src_wdth: Wdth,\n        dst: *const u32,\n        dst_wdth: Wdth,\n        transfer_count: u16,\n        increment_src: bool,\n        increment_dst: bool,\n        options: TransferOptions,\n    ) {\n        // \"Subsequent reads and writes cannot be moved ahead of preceding reads.\"\n        compiler_fence(Ordering::SeqCst);\n\n        self.ctl().modify(|w| {\n            // SLAU 5.2.5:\n            // \"The DMATSEL bits should be modified only when the DMACTLx.DMAEN bit is\n            //  0; otherwise, unpredictable DMA triggers can occur.\"\n            //\n            // We also want to stop any transfers before setup.\n            w.set_en(false);\n            w.set_req(false);\n\n            // Not every part supports auto enable, so force its value to 0.\n            w.set_autoen(Autoen::NONE);\n            w.set_preirq(Preirq::PREIRQ_DISABLE);\n            w.set_srcwdth(src_wdth);\n            w.set_dstwdth(dst_wdth);\n            w.set_srcincr(if increment_src {\n                Incr::INCREMENT\n            } else {\n                Incr::UNCHANGED\n            });\n            w.set_dstincr(if increment_dst {\n                Incr::INCREMENT\n            } else {\n                Incr::UNCHANGED\n            });\n\n            w.set_em(Em::NORMAL);\n            // Single and block will clear the enable bit when the transfers finish.\n            w.set_tm(convert_mode(options.mode));\n        });\n\n        self.tctl().write(|w| {\n            w.set_tsel(trigger_sel);\n            // Basic channels do not implement cross triggering.\n            w.set_tint(vals::Tint::EXTERNAL);\n        });\n\n        self.sz().write(|w| {\n            w.set_size(transfer_count);\n        });\n\n        self.sa().write_value(src as u32);\n        self.da().write_value(dst as u32);\n\n        // Enable the channel.\n        self.ctl().modify(|w| {\n            // FIXME: Why did putting set_req later fix some transfers\n            w.set_en(true);\n            w.set_req(true);\n        });\n    }\n\n    fn start(&self) {\n        self.mask_interrupt(true);\n\n        // \"Subsequent reads and writes cannot be moved ahead of preceding reads.\"\n        compiler_fence(Ordering::SeqCst);\n\n        // Request the DMA transfer to start.\n        self.ctl().modify(|w| {\n            w.set_req(true);\n        });\n    }\n\n    fn resume(&self) {\n        self.mask_interrupt(true);\n\n        // \"Subsequent reads and writes cannot be moved ahead of preceding reads.\"\n        compiler_fence(Ordering::SeqCst);\n\n        self.ctl().modify(|w| {\n            // w.set_en(true);\n            w.set_req(true);\n        });\n    }\n\n    fn request_pause(&self) {\n        // \"Subsequent reads and writes cannot be moved ahead of preceding reads.\"\n        compiler_fence(Ordering::SeqCst);\n\n        // Stop the transfer.\n        //\n        // SLAU846 5.2.6:\n        // \"A DMA block transfer in progress can be stopped by clearing the DMAEN bit\"\n        self.ctl().modify(|w| {\n            // w.set_en(false);\n            w.set_req(false);\n        });\n    }\n\n    fn is_running(&self) -> bool {\n        // \"Subsequent reads and writes cannot be moved ahead of preceding reads.\"\n        compiler_fence(Ordering::SeqCst);\n\n        let ctl = self.ctl().read();\n\n        // Is the transfer requested?\n        ctl.req()\n            // Is the channel enabled?\n            && ctl.en()\n    }\n}\n\nmacro_rules! impl_dma_channel {\n    ($instance: ident, $num: expr) => {\n        impl crate::dma::SealedChannel for crate::peripherals::$instance {\n            fn id(&self) -> u8 {\n                $num\n            }\n        }\n\n        impl From<crate::peripherals::$instance> for crate::dma::AnyChannel {\n            fn from(value: crate::peripherals::$instance) -> Self {\n                use crate::dma::SealedChannel;\n\n                Self { id: value.id() }\n            }\n        }\n\n        impl crate::dma::Channel for crate::peripherals::$instance {}\n    };\n}\n\n// C1104 has no full DMA channels.\n#[allow(unused_macros)]\nmacro_rules! impl_full_dma_channel {\n    ($instance: ident, $num: expr) => {\n        impl_dma_channel!($instance, $num);\n\n        impl From<crate::peripherals::$instance> for crate::dma::AnyFullChannel {\n            fn from(value: crate::peripherals::$instance) -> Self {\n                use crate::dma::SealedChannel;\n\n                Self { id: value.id() }\n            }\n        }\n\n        impl crate::dma::FullChannel for crate::peripherals::$instance {}\n    };\n}\n\n#[cfg(feature = \"rt\")]\n#[interrupt]\nfn DMA() {\n    use crate::BitIter;\n\n    let events = pac::DMA.int_event(0);\n    let mis = events.mis().read();\n\n    // TODO: Handle DATAERR and ADDRERR? However we do not know which channel causes an error.\n    if mis.dataerr() {\n        panic!(\"DMA data error\");\n    } else if mis.addrerr() {\n        panic!(\"DMA address error\")\n    }\n\n    // Ignore preirq interrupts (values greater than 16).\n    for i in BitIter(mis.0 & 0x0000_FFFF) {\n        if let Some(state) = STATE.get(i as usize) {\n            state.waker.wake();\n\n            // Notify the future that the counter size hit zero\n            events.imask().modify(|w| {\n                w.set_ch(i as usize, false);\n            });\n        }\n    }\n}\n"
  },
  {
    "path": "embassy-mspm0/src/fmt.rs",
    "content": "#![macro_use]\n#![allow(unused)]\n\nuse core::fmt::{Debug, Display, LowerHex};\n\n#[cfg(all(feature = \"defmt\", feature = \"log\"))]\ncompile_error!(\"You may not enable both `defmt` and `log` features.\");\n\n#[collapse_debuginfo(yes)]\nmacro_rules! assert {\n    ($($x:tt)*) => {\n        {\n            #[cfg(not(feature = \"defmt\"))]\n            ::core::assert!($($x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::assert!($($x)*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! assert_eq {\n    ($($x:tt)*) => {\n        {\n            #[cfg(not(feature = \"defmt\"))]\n            ::core::assert_eq!($($x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::assert_eq!($($x)*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! assert_ne {\n    ($($x:tt)*) => {\n        {\n            #[cfg(not(feature = \"defmt\"))]\n            ::core::assert_ne!($($x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::assert_ne!($($x)*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! debug_assert {\n    ($($x:tt)*) => {\n        {\n            #[cfg(not(feature = \"defmt\"))]\n            ::core::debug_assert!($($x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::debug_assert!($($x)*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! debug_assert_eq {\n    ($($x:tt)*) => {\n        {\n            #[cfg(not(feature = \"defmt\"))]\n            ::core::debug_assert_eq!($($x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::debug_assert_eq!($($x)*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! debug_assert_ne {\n    ($($x:tt)*) => {\n        {\n            #[cfg(not(feature = \"defmt\"))]\n            ::core::debug_assert_ne!($($x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::debug_assert_ne!($($x)*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! todo {\n    ($($x:tt)*) => {\n        {\n            #[cfg(not(feature = \"defmt\"))]\n            ::core::todo!($($x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::todo!($($x)*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! unreachable {\n    ($($x:tt)*) => {\n        {\n            #[cfg(not(feature = \"defmt\"))]\n            ::core::unreachable!($($x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::unreachable!($($x)*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! panic {\n    ($($x:tt)*) => {\n        {\n            #[cfg(not(feature = \"defmt\"))]\n            ::core::panic!($($x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::panic!($($x)*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! trace {\n    ($s:literal $(, $x:expr)* $(,)?) => {\n        {\n            #[cfg(feature = \"log\")]\n            ::log::trace!($s $(, $x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::trace!($s $(, $x)*);\n            #[cfg(not(any(feature = \"log\", feature=\"defmt\")))]\n            let _ = ($( & $x ),*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! debug {\n    ($s:literal $(, $x:expr)* $(,)?) => {\n        {\n            #[cfg(feature = \"log\")]\n            ::log::debug!($s $(, $x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::debug!($s $(, $x)*);\n            #[cfg(not(any(feature = \"log\", feature=\"defmt\")))]\n            let _ = ($( & $x ),*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! info {\n    ($s:literal $(, $x:expr)* $(,)?) => {\n        {\n            #[cfg(feature = \"log\")]\n            ::log::info!($s $(, $x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::info!($s $(, $x)*);\n            #[cfg(not(any(feature = \"log\", feature=\"defmt\")))]\n            let _ = ($( & $x ),*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! warn {\n    ($s:literal $(, $x:expr)* $(,)?) => {\n        {\n            #[cfg(feature = \"log\")]\n            ::log::warn!($s $(, $x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::warn!($s $(, $x)*);\n            #[cfg(not(any(feature = \"log\", feature=\"defmt\")))]\n            let _ = ($( & $x ),*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! error {\n    ($s:literal $(, $x:expr)* $(,)?) => {\n        {\n            #[cfg(feature = \"log\")]\n            ::log::error!($s $(, $x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::error!($s $(, $x)*);\n            #[cfg(not(any(feature = \"log\", feature=\"defmt\")))]\n            let _ = ($( & $x ),*);\n        }\n    };\n}\n\n#[cfg(feature = \"defmt\")]\n#[collapse_debuginfo(yes)]\nmacro_rules! unwrap {\n    ($($x:tt)*) => {\n        ::defmt::unwrap!($($x)*)\n    };\n}\n\n#[cfg(not(feature = \"defmt\"))]\n#[collapse_debuginfo(yes)]\nmacro_rules! unwrap {\n    ($arg:expr) => {\n        match $crate::fmt::Try::into_result($arg) {\n            ::core::result::Result::Ok(t) => t,\n            ::core::result::Result::Err(e) => {\n                ::core::panic!(\"unwrap of `{}` failed: {:?}\", ::core::stringify!($arg), e);\n            }\n        }\n    };\n    ($arg:expr, $($msg:expr),+ $(,)? ) => {\n        match $crate::fmt::Try::into_result($arg) {\n            ::core::result::Result::Ok(t) => t,\n            ::core::result::Result::Err(e) => {\n                ::core::panic!(\"unwrap of `{}` failed: {}: {:?}\", ::core::stringify!($arg), ::core::format_args!($($msg,)*), e);\n            }\n        }\n    }\n}\n\n#[derive(Debug, Copy, Clone, Eq, PartialEq)]\npub struct NoneError;\n\npub trait Try {\n    type Ok;\n    type Error;\n    fn into_result(self) -> Result<Self::Ok, Self::Error>;\n}\n\nimpl<T> Try for Option<T> {\n    type Ok = T;\n    type Error = NoneError;\n\n    #[inline]\n    fn into_result(self) -> Result<T, NoneError> {\n        self.ok_or(NoneError)\n    }\n}\n\nimpl<T, E> Try for Result<T, E> {\n    type Ok = T;\n    type Error = E;\n\n    #[inline]\n    fn into_result(self) -> Self {\n        self\n    }\n}\n\npub(crate) struct Bytes<'a>(pub &'a [u8]);\n\nimpl<'a> Debug for Bytes<'a> {\n    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {\n        write!(f, \"{:#02x?}\", self.0)\n    }\n}\n\nimpl<'a> Display for Bytes<'a> {\n    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {\n        write!(f, \"{:#02x?}\", self.0)\n    }\n}\n\nimpl<'a> LowerHex for Bytes<'a> {\n    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {\n        write!(f, \"{:#02x?}\", self.0)\n    }\n}\n\n#[cfg(feature = \"defmt\")]\nimpl<'a> defmt::Format for Bytes<'a> {\n    fn format(&self, fmt: defmt::Formatter) {\n        defmt::write!(fmt, \"{:02x}\", self.0)\n    }\n}\n"
  },
  {
    "path": "embassy-mspm0/src/gpio.rs",
    "content": "#![macro_use]\n\nuse core::convert::Infallible;\nuse core::future::Future;\nuse core::pin::Pin as FuturePin;\nuse core::task::{Context, Poll};\n\nuse embassy_hal_internal::{Peri, PeripheralType, impl_peripheral};\nuse embassy_sync::waitqueue::AtomicWaker;\n\nuse crate::pac::gpio::vals::*;\nuse crate::pac::gpio::{self};\n#[cfg(all(feature = \"rt\", any(gpioa_interrupt, gpiob_interrupt)))]\nuse crate::pac::interrupt;\nuse crate::pac::{self};\n\n/// Represents a digital input or output level.\n#[derive(Debug, Eq, PartialEq, Clone, Copy)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub enum Level {\n    /// Logical low.\n    Low,\n    /// Logical high.\n    High,\n}\n\nimpl From<bool> for Level {\n    fn from(val: bool) -> Self {\n        match val {\n            true => Self::High,\n            false => Self::Low,\n        }\n    }\n}\n\nimpl From<Level> for bool {\n    fn from(level: Level) -> bool {\n        match level {\n            Level::Low => false,\n            Level::High => true,\n        }\n    }\n}\n\n/// Represents a pull setting for an input.\n#[derive(Debug, Clone, Copy, Eq, PartialEq)]\npub enum Pull {\n    /// No pull.\n    None,\n    /// Internal pull-up resistor.\n    Up,\n    /// Internal pull-down resistor.\n    Down,\n}\n\n/// A GPIO bank with up to 32 pins.\n#[derive(Debug, Clone, Copy, Eq, PartialEq)]\npub enum Port {\n    /// Port A.\n    PortA = 0,\n\n    /// Port B.\n    #[cfg(gpio_pb)]\n    PortB = 1,\n\n    /// Port C.\n    #[cfg(gpio_pc)]\n    PortC = 2,\n}\n\n/// GPIO flexible pin.\n///\n/// This pin can either be a disconnected, input, or output pin, or both. The level register bit will remain\n/// set while not in output mode, so the pin's level will be 'remembered' when it is not in output\n/// mode.\npub struct Flex<'d> {\n    pin: Peri<'d, AnyPin>,\n}\n\nimpl<'d> Flex<'d> {\n    /// Wrap the pin in a `Flex`.\n    ///\n    /// The pin remains disconnected. The initial output level is unspecified, but can be changed\n    /// before the pin is put into output mode.\n    #[inline]\n    pub fn new(pin: Peri<'d, impl Pin>) -> Self {\n        // Pin will be in disconnected state.\n        Self { pin: pin.into() }\n    }\n\n    /// Set the pin's pull.\n    #[inline]\n    pub fn set_pull(&mut self, pull: Pull) {\n        let pincm = pac::IOMUX.pincm(self.pin.pin_cm() as usize);\n\n        pincm.modify(|w| {\n            w.set_pipd(matches!(pull, Pull::Down));\n            w.set_pipu(matches!(pull, Pull::Up));\n        });\n    }\n\n    /// Put the pin into input mode.\n    ///\n    /// The pull setting is left unchanged.\n    #[inline]\n    pub fn set_as_input(&mut self) {\n        let pincm = pac::IOMUX.pincm(self.pin.pin_cm() as usize);\n\n        pincm.modify(|w| {\n            w.set_pf(GPIO_PF);\n            w.set_hiz1(false);\n            w.set_pc(true);\n            w.set_inena(true);\n        });\n\n        self.pin.block().doeclr31_0().write(|w| {\n            w.set_dio(self.pin.bit_index(), true);\n        });\n    }\n\n    /// Put the pin into output mode.\n    ///\n    /// The pin level will be whatever was set before (or low by default). If you want it to begin\n    /// at a specific level, call `set_high`/`set_low` on the pin first.\n    #[inline]\n    pub fn set_as_output(&mut self) {\n        let pincm = pac::IOMUX.pincm(self.pin.pin_cm() as usize);\n\n        pincm.modify(|w| {\n            w.set_pf(GPIO_PF);\n            w.set_hiz1(false);\n            w.set_pc(true);\n            w.set_inena(false);\n        });\n\n        self.pin.block().doeset31_0().write(|w| {\n            w.set_dio(self.pin.bit_index(), true);\n        });\n    }\n\n    /// Put the pin into input + open-drain output mode.\n    ///\n    /// The hardware will drive the line low if you set it to low, and will leave it floating if you set\n    /// it to high, in which case you can read the input to figure out whether another device\n    /// is driving the line low.\n    ///\n    /// The pin level will be whatever was set before (or low by default). If you want it to begin\n    /// at a specific level, call `set_high`/`set_low` on the pin first.\n    ///\n    /// The internal weak pull-up and pull-down resistors will be disabled.\n    #[inline]\n    pub fn set_as_input_output(&mut self) {\n        let pincm = pac::IOMUX.pincm(self.pin.pin_cm() as usize);\n\n        pincm.modify(|w| {\n            w.set_pf(GPIO_PF);\n            w.set_hiz1(true);\n            w.set_pc(true);\n            w.set_inena(true);\n        });\n\n        // Enable output driver (DOE) - required for open-drain to drive low\n        self.pin.block().doeset31_0().write(|w| {\n            w.set_dio(self.pin.bit_index(), true);\n        });\n\n        self.set_pull(Pull::None);\n    }\n\n    /// Set the pin as \"disconnected\", ie doing nothing and consuming the lowest\n    /// amount of power possible.\n    ///\n    /// This is currently the same as [`Self::set_as_analog()`] but is semantically different\n    /// really. Drivers should `set_as_disconnected()` pins when dropped.\n    ///\n    /// Note that this also disables the internal weak pull-up and pull-down resistors.\n    #[inline]\n    pub fn set_as_disconnected(&mut self) {\n        let pincm = pac::IOMUX.pincm(self.pin.pin_cm() as usize);\n\n        pincm.modify(|w| {\n            w.set_pf(DISCONNECT_PF);\n            w.set_hiz1(false);\n            w.set_pc(false);\n            w.set_inena(false);\n        });\n\n        self.set_pull(Pull::None);\n        self.set_inversion(false);\n    }\n\n    /// Configure the logic inversion of this pin.\n    ///\n    /// Logic inversion applies to both the input and output path of this pin.\n    #[inline]\n    pub fn set_inversion(&mut self, invert: bool) {\n        let pincm = pac::IOMUX.pincm(self.pin.pin_cm() as usize);\n\n        pincm.modify(|w| {\n            w.set_inv(invert);\n        });\n    }\n\n    // TODO: drive strength, hysteresis, wakeup enable, wakeup compare\n\n    /// Put the pin into the PF mode, unchecked.\n    ///\n    /// This puts the pin into the PF mode, with the request number. This is completely unchecked,\n    /// it can attach the pin to literally any peripheral, so use with care. In addition the pin\n    /// peripheral is connected in the iomux.\n    ///\n    /// The peripheral attached to the pin depends on the part in use. Consult the datasheet\n    /// or technical reference manual for additional details.\n    #[inline]\n    pub fn set_pf_unchecked(&mut self, pf: u8) {\n        // Per SLAU893 and SLAU846B, PF is only 6 bits\n        assert_eq!(pf & 0xC0, 0, \"PF is out of range\");\n\n        let pincm = pac::IOMUX.pincm(self.pin.pin_cm() as usize);\n\n        pincm.modify(|w| {\n            w.set_pf(pf);\n            // If the PF is manually set, connect the pin\n            w.set_pc(true);\n        });\n    }\n\n    /// Get whether the pin input level is high.\n    #[inline]\n    pub fn is_high(&self) -> bool {\n        self.pin.block().din31_0().read().dio(self.pin.bit_index())\n    }\n\n    /// Get whether the pin input level is low.\n    #[inline]\n    pub fn is_low(&self) -> bool {\n        !self.is_high()\n    }\n\n    /// Returns current pin level\n    #[inline]\n    pub fn get_level(&self) -> Level {\n        self.is_high().into()\n    }\n\n    /// Set the output as high.\n    #[inline]\n    pub fn set_high(&mut self) {\n        self.pin.block().doutset31_0().write(|w| {\n            w.set_dio(self.pin.bit_index() as usize, true);\n        });\n    }\n\n    /// Set the output as low.\n    #[inline]\n    pub fn set_low(&mut self) {\n        self.pin.block().doutclr31_0().write(|w| {\n            w.set_dio(self.pin.bit_index(), true);\n        });\n    }\n\n    /// Toggle pin output\n    #[inline]\n    pub fn toggle(&mut self) {\n        self.pin.block().douttgl31_0().write(|w| {\n            w.set_dio(self.pin.bit_index(), true);\n        })\n    }\n\n    /// Set the output level.\n    #[inline]\n    pub fn set_level(&mut self, level: Level) {\n        match level {\n            Level::Low => self.set_low(),\n            Level::High => self.set_high(),\n        }\n    }\n\n    /// Get the current pin output level.\n    #[inline]\n    pub fn get_output_level(&self) -> Level {\n        self.is_set_high().into()\n    }\n\n    /// Is the output level high?\n    #[inline]\n    pub fn is_set_high(&self) -> bool {\n        self.pin.block().dout31_0().read().dio(self.pin.bit_index())\n    }\n\n    /// Is the output level low?\n    #[inline]\n    pub fn is_set_low(&self) -> bool {\n        !self.is_set_high()\n    }\n\n    /// Wait until the pin is high. If it is already high, return immediately.\n    #[inline]\n    pub async fn wait_for_high(&mut self) {\n        if self.is_high() {\n            return;\n        }\n\n        self.wait_for_rising_edge().await\n    }\n\n    /// Wait until the pin is low. If it is already low, return immediately.\n    #[inline]\n    pub async fn wait_for_low(&mut self) {\n        if self.is_low() {\n            return;\n        }\n\n        self.wait_for_falling_edge().await\n    }\n\n    /// Wait for the pin to undergo a transition from low to high.\n    #[inline]\n    pub async fn wait_for_rising_edge(&mut self) {\n        InputFuture::new(self.pin.reborrow(), Polarity::RISE).await\n    }\n\n    /// Wait for the pin to undergo a transition from high to low.\n    #[inline]\n    pub async fn wait_for_falling_edge(&mut self) {\n        InputFuture::new(self.pin.reborrow(), Polarity::FALL).await\n    }\n\n    /// Wait for the pin to undergo any transition, i.e low to high OR high to low.\n    #[inline]\n    pub async fn wait_for_any_edge(&mut self) {\n        InputFuture::new(self.pin.reborrow(), Polarity::RISE_FALL).await\n    }\n}\n\nimpl<'d> Drop for Flex<'d> {\n    #[inline]\n    fn drop(&mut self) {\n        self.set_as_disconnected();\n    }\n}\n\n/// GPIO input driver.\npub struct Input<'d> {\n    pin: Flex<'d>,\n}\n\nimpl<'d> Input<'d> {\n    /// Create GPIO input driver for a [Pin] with the provided [Pull] configuration.\n    #[inline]\n    pub fn new(pin: Peri<'d, impl Pin>, pull: Pull) -> Self {\n        let mut pin = Flex::new(pin);\n        pin.set_as_input();\n        pin.set_pull(pull);\n        Self { pin }\n    }\n\n    /// Get whether the pin input level is high.\n    #[inline]\n    pub fn is_high(&self) -> bool {\n        self.pin.is_high()\n    }\n\n    /// Get whether the pin input level is low.\n    #[inline]\n    pub fn is_low(&self) -> bool {\n        self.pin.is_low()\n    }\n\n    /// Get the current pin input level.\n    #[inline]\n    pub fn get_level(&self) -> Level {\n        self.pin.get_level()\n    }\n\n    /// Configure the logic inversion of this pin.\n    ///\n    /// Logic inversion applies to the input path of this pin.\n    #[inline]\n    pub fn set_inversion(&mut self, invert: bool) {\n        self.pin.set_inversion(invert)\n    }\n\n    /// Wait until the pin is high. If it is already high, return immediately.\n    #[inline]\n    pub async fn wait_for_high(&mut self) {\n        self.pin.wait_for_high().await\n    }\n\n    /// Wait until the pin is low. If it is already low, return immediately.\n    #[inline]\n    pub async fn wait_for_low(&mut self) {\n        self.pin.wait_for_low().await\n    }\n\n    /// Wait for the pin to undergo a transition from low to high.\n    #[inline]\n    pub async fn wait_for_rising_edge(&mut self) {\n        self.pin.wait_for_rising_edge().await\n    }\n\n    /// Wait for the pin to undergo a transition from high to low.\n    #[inline]\n    pub async fn wait_for_falling_edge(&mut self) {\n        self.pin.wait_for_falling_edge().await\n    }\n\n    /// Wait for the pin to undergo any transition, i.e low to high OR high to low.\n    #[inline]\n    pub async fn wait_for_any_edge(&mut self) {\n        self.pin.wait_for_any_edge().await\n    }\n}\n\n/// GPIO output driver.\n///\n/// Note that pins will **return to their floating state** when `Output` is dropped.\n/// If pins should retain their state indefinitely, either keep ownership of the\n/// `Output`, or pass it to [`core::mem::forget`].\npub struct Output<'d> {\n    pin: Flex<'d>,\n}\n\nimpl<'d> Output<'d> {\n    /// Create GPIO output driver for a [Pin] with the provided [Level] configuration.\n    #[inline]\n    pub fn new(pin: Peri<'d, impl Pin>, initial_output: Level) -> Self {\n        let mut pin = Flex::new(pin);\n        pin.set_as_output();\n        pin.set_level(initial_output);\n        Self { pin }\n    }\n\n    /// Set the output as high.\n    #[inline]\n    pub fn set_high(&mut self) {\n        self.pin.set_high();\n    }\n\n    /// Set the output as low.\n    #[inline]\n    pub fn set_low(&mut self) {\n        self.pin.set_low();\n    }\n\n    /// Set the output level.\n    #[inline]\n    pub fn set_level(&mut self, level: Level) {\n        self.pin.set_level(level)\n    }\n\n    /// Is the output pin set as high?\n    #[inline]\n    pub fn is_set_high(&self) -> bool {\n        self.pin.is_set_high()\n    }\n\n    /// Is the output pin set as low?\n    #[inline]\n    pub fn is_set_low(&self) -> bool {\n        self.pin.is_set_low()\n    }\n\n    /// What level output is set to\n    #[inline]\n    pub fn get_output_level(&self) -> Level {\n        self.pin.get_output_level()\n    }\n\n    /// Toggle pin output\n    #[inline]\n    pub fn toggle(&mut self) {\n        self.pin.toggle();\n    }\n\n    /// Configure the logic inversion of this pin.\n    ///\n    /// Logic inversion applies to the input path of this pin.\n    #[inline]\n    pub fn set_inversion(&mut self, invert: bool) {\n        self.pin.set_inversion(invert)\n    }\n}\n\n/// GPIO output open-drain driver.\n///\n/// Note that pins will **return to their floating state** when `OutputOpenDrain` is dropped.\n/// If pins should retain their state indefinitely, either keep ownership of the\n/// `OutputOpenDrain`, or pass it to [`core::mem::forget`].\npub struct OutputOpenDrain<'d> {\n    pin: Flex<'d>,\n}\n\nimpl<'d> OutputOpenDrain<'d> {\n    /// Create a new GPIO open drain output driver for a [Pin] with the provided [Level].\n    #[inline]\n    pub fn new(pin: Peri<'d, impl Pin>, initial_output: Level) -> Self {\n        let mut pin = Flex::new(pin);\n        pin.set_level(initial_output);\n        pin.set_as_input_output();\n        Self { pin }\n    }\n\n    /// Get whether the pin input level is high.\n    #[inline]\n    pub fn is_high(&self) -> bool {\n        !self.pin.is_low()\n    }\n\n    /// Get whether the pin input level is low.\n    #[inline]\n    pub fn is_low(&self) -> bool {\n        self.pin.is_low()\n    }\n\n    /// Get the current pin input level.\n    #[inline]\n    pub fn get_level(&self) -> Level {\n        self.pin.get_level()\n    }\n\n    /// Set the output as high.\n    #[inline]\n    pub fn set_high(&mut self) {\n        self.pin.set_high();\n    }\n\n    /// Set the output as low.\n    #[inline]\n    pub fn set_low(&mut self) {\n        self.pin.set_low();\n    }\n\n    /// Set the output level.\n    #[inline]\n    pub fn set_level(&mut self, level: Level) {\n        self.pin.set_level(level);\n    }\n\n    /// Get whether the output level is set to high.\n    #[inline]\n    pub fn is_set_high(&self) -> bool {\n        self.pin.is_set_high()\n    }\n\n    /// Get whether the output level is set to low.\n    #[inline]\n    pub fn is_set_low(&self) -> bool {\n        self.pin.is_set_low()\n    }\n\n    /// Get the current output level.\n    #[inline]\n    pub fn get_output_level(&self) -> Level {\n        self.pin.get_output_level()\n    }\n\n    /// Toggle pin output\n    #[inline]\n    pub fn toggle(&mut self) {\n        self.pin.toggle()\n    }\n\n    /// Configure the logic inversion of this pin.\n    ///\n    /// Logic inversion applies to the input path of this pin.\n    #[inline]\n    pub fn set_inversion(&mut self, invert: bool) {\n        self.pin.set_inversion(invert)\n    }\n\n    /// Wait until the pin is high. If it is already high, return immediately.\n    #[inline]\n    pub async fn wait_for_high(&mut self) {\n        self.pin.wait_for_high().await\n    }\n\n    /// Wait until the pin is low. If it is already low, return immediately.\n    #[inline]\n    pub async fn wait_for_low(&mut self) {\n        self.pin.wait_for_low().await\n    }\n\n    /// Wait for the pin to undergo a transition from low to high.\n    #[inline]\n    pub async fn wait_for_rising_edge(&mut self) {\n        self.pin.wait_for_rising_edge().await\n    }\n\n    /// Wait for the pin to undergo a transition from high to low.\n    #[inline]\n    pub async fn wait_for_falling_edge(&mut self) {\n        self.pin.wait_for_falling_edge().await\n    }\n\n    /// Wait for the pin to undergo any transition, i.e low to high OR high to low.\n    #[inline]\n    pub async fn wait_for_any_edge(&mut self) {\n        self.pin.wait_for_any_edge().await\n    }\n}\n\n/// Type-erased GPIO pin\npub struct AnyPin {\n    pub(crate) pin_port: u8,\n}\n\nimpl AnyPin {\n    /// Create an [AnyPin] for a specific pin.\n    ///\n    /// # Safety\n    /// - `pin_port` should not in use by another driver.\n    #[inline]\n    pub unsafe fn steal(pin_port: u8) -> Peri<'static, Self> {\n        Peri::new_unchecked(Self { pin_port })\n    }\n}\n\nimpl_peripheral!(AnyPin);\n\nimpl Pin for AnyPin {}\nimpl SealedPin for AnyPin {\n    #[inline]\n    fn pin_port(&self) -> u8 {\n        self.pin_port\n    }\n}\n\n/// Interface for a Pin that can be configured by an [Input] or [Output] driver, or converted to an [AnyPin].\n#[allow(private_bounds)]\npub trait Pin: PeripheralType + Into<AnyPin> + SealedPin + Sized + 'static {\n    /// The index of this pin in PINCM (pin control management) registers.\n    #[inline]\n    fn pin_cm(&self) -> u8 {\n        self._pin_cm()\n    }\n}\n\nimpl<'d> embedded_hal::digital::ErrorType for Flex<'d> {\n    type Error = Infallible;\n}\n\nimpl<'d> embedded_hal::digital::InputPin for Flex<'d> {\n    #[inline]\n    fn is_high(&mut self) -> Result<bool, Self::Error> {\n        Ok((*self).is_high())\n    }\n\n    #[inline]\n    fn is_low(&mut self) -> Result<bool, Self::Error> {\n        Ok((*self).is_low())\n    }\n}\n\nimpl<'d> embedded_hal::digital::OutputPin for Flex<'d> {\n    #[inline]\n    fn set_low(&mut self) -> Result<(), Self::Error> {\n        Ok(self.set_low())\n    }\n\n    #[inline]\n    fn set_high(&mut self) -> Result<(), Self::Error> {\n        Ok(self.set_high())\n    }\n}\n\nimpl<'d> embedded_hal::digital::StatefulOutputPin for Flex<'d> {\n    #[inline]\n    fn is_set_high(&mut self) -> Result<bool, Self::Error> {\n        Ok((*self).is_set_high())\n    }\n\n    #[inline]\n    fn is_set_low(&mut self) -> Result<bool, Self::Error> {\n        Ok((*self).is_set_low())\n    }\n}\n\nimpl<'d> embedded_hal_async::digital::Wait for Flex<'d> {\n    async fn wait_for_high(&mut self) -> Result<(), Self::Error> {\n        self.wait_for_high().await;\n        Ok(())\n    }\n\n    async fn wait_for_low(&mut self) -> Result<(), Self::Error> {\n        self.wait_for_low().await;\n        Ok(())\n    }\n\n    async fn wait_for_rising_edge(&mut self) -> Result<(), Self::Error> {\n        self.wait_for_rising_edge().await;\n        Ok(())\n    }\n\n    async fn wait_for_falling_edge(&mut self) -> Result<(), Self::Error> {\n        self.wait_for_falling_edge().await;\n        Ok(())\n    }\n\n    async fn wait_for_any_edge(&mut self) -> Result<(), Self::Error> {\n        self.wait_for_any_edge().await;\n        Ok(())\n    }\n}\n\nimpl<'d> embedded_hal::digital::ErrorType for Input<'d> {\n    type Error = Infallible;\n}\n\nimpl<'d> embedded_hal::digital::InputPin for Input<'d> {\n    #[inline]\n    fn is_high(&mut self) -> Result<bool, Self::Error> {\n        Ok((*self).is_high())\n    }\n\n    #[inline]\n    fn is_low(&mut self) -> Result<bool, Self::Error> {\n        Ok((*self).is_low())\n    }\n}\n\nimpl<'d> embedded_hal_async::digital::Wait for Input<'d> {\n    async fn wait_for_high(&mut self) -> Result<(), Self::Error> {\n        self.wait_for_high().await;\n        Ok(())\n    }\n\n    async fn wait_for_low(&mut self) -> Result<(), Self::Error> {\n        self.wait_for_low().await;\n        Ok(())\n    }\n\n    async fn wait_for_rising_edge(&mut self) -> Result<(), Self::Error> {\n        self.wait_for_rising_edge().await;\n        Ok(())\n    }\n\n    async fn wait_for_falling_edge(&mut self) -> Result<(), Self::Error> {\n        self.wait_for_falling_edge().await;\n        Ok(())\n    }\n\n    async fn wait_for_any_edge(&mut self) -> Result<(), Self::Error> {\n        self.wait_for_any_edge().await;\n        Ok(())\n    }\n}\n\nimpl<'d> embedded_hal::digital::ErrorType for Output<'d> {\n    type Error = Infallible;\n}\n\nimpl<'d> embedded_hal::digital::OutputPin for Output<'d> {\n    #[inline]\n    fn set_low(&mut self) -> Result<(), Self::Error> {\n        Ok(self.set_low())\n    }\n\n    #[inline]\n    fn set_high(&mut self) -> Result<(), Self::Error> {\n        Ok(self.set_high())\n    }\n}\n\nimpl<'d> embedded_hal::digital::StatefulOutputPin for Output<'d> {\n    #[inline]\n    fn is_set_high(&mut self) -> Result<bool, Self::Error> {\n        Ok((*self).is_set_high())\n    }\n\n    #[inline]\n    fn is_set_low(&mut self) -> Result<bool, Self::Error> {\n        Ok((*self).is_set_low())\n    }\n}\n\nimpl<'d> embedded_hal::digital::ErrorType for OutputOpenDrain<'d> {\n    type Error = Infallible;\n}\n\nimpl<'d> embedded_hal::digital::InputPin for OutputOpenDrain<'d> {\n    #[inline]\n    fn is_high(&mut self) -> Result<bool, Self::Error> {\n        Ok((*self).is_high())\n    }\n\n    #[inline]\n    fn is_low(&mut self) -> Result<bool, Self::Error> {\n        Ok((*self).is_low())\n    }\n}\n\nimpl<'d> embedded_hal::digital::OutputPin for OutputOpenDrain<'d> {\n    #[inline]\n    fn set_low(&mut self) -> Result<(), Self::Error> {\n        Ok(self.set_low())\n    }\n\n    #[inline]\n    fn set_high(&mut self) -> Result<(), Self::Error> {\n        Ok(self.set_high())\n    }\n}\n\nimpl<'d> embedded_hal::digital::StatefulOutputPin for OutputOpenDrain<'d> {\n    #[inline]\n    fn is_set_high(&mut self) -> Result<bool, Self::Error> {\n        Ok((*self).is_set_high())\n    }\n\n    #[inline]\n    fn is_set_low(&mut self) -> Result<bool, Self::Error> {\n        Ok((*self).is_set_low())\n    }\n}\n\nimpl<'d> embedded_hal_async::digital::Wait for OutputOpenDrain<'d> {\n    async fn wait_for_high(&mut self) -> Result<(), Self::Error> {\n        self.wait_for_high().await;\n        Ok(())\n    }\n\n    async fn wait_for_low(&mut self) -> Result<(), Self::Error> {\n        self.wait_for_low().await;\n        Ok(())\n    }\n\n    async fn wait_for_rising_edge(&mut self) -> Result<(), Self::Error> {\n        self.wait_for_rising_edge().await;\n        Ok(())\n    }\n\n    async fn wait_for_falling_edge(&mut self) -> Result<(), Self::Error> {\n        self.wait_for_falling_edge().await;\n        Ok(())\n    }\n\n    async fn wait_for_any_edge(&mut self) -> Result<(), Self::Error> {\n        self.wait_for_any_edge().await;\n        Ok(())\n    }\n}\n\n#[cfg_attr(mspm0g518x, allow(dead_code))]\n#[derive(Copy, Clone)]\npub struct PfType {\n    pull: Pull,\n    input: bool,\n    invert: bool,\n}\n\nimpl PfType {\n    pub const fn input(pull: Pull, invert: bool) -> Self {\n        Self {\n            pull,\n            input: true,\n            invert,\n        }\n    }\n\n    pub const fn output(pull: Pull, invert: bool) -> Self {\n        Self {\n            pull,\n            input: false,\n            invert,\n        }\n    }\n}\n\n/// The pin function to disconnect peripherals from the pin.\n///\n/// This is also the pin function used to connect to analog peripherals, such as an ADC.\nconst DISCONNECT_PF: u8 = 0;\n\n/// The pin function for the GPIO peripheral.\n///\n/// This is fixed to `1` for every part.\nconst GPIO_PF: u8 = 1;\n\nmacro_rules! impl_pin {\n    ($name: ident, $port: expr, $pin_num: expr) => {\n        impl crate::gpio::Pin for crate::peripherals::$name {}\n        impl crate::gpio::SealedPin for crate::peripherals::$name {\n            #[inline]\n            fn pin_port(&self) -> u8 {\n                ($port as u8) * 32 + $pin_num\n            }\n        }\n\n        impl From<crate::peripherals::$name> for crate::gpio::AnyPin {\n            fn from(val: crate::peripherals::$name) -> Self {\n                Self {\n                    pin_port: crate::gpio::SealedPin::pin_port(&val),\n                }\n            }\n        }\n    };\n}\n\n// TODO: Possible micro-op for C110X, not every pin is instantiated even on the 20 pin parts.\n//       This would mean cfg guarding to just cfg guarding every pin instance.\nstatic PORTA_WAKERS: [AtomicWaker; 32] = [const { AtomicWaker::new() }; 32];\n#[cfg(gpio_pb)]\nstatic PORTB_WAKERS: [AtomicWaker; 32] = [const { AtomicWaker::new() }; 32];\n#[cfg(gpio_pc)]\nstatic PORTC_WAKERS: [AtomicWaker; 32] = [const { AtomicWaker::new() }; 32];\n\npub(crate) trait SealedPin {\n    fn pin_port(&self) -> u8;\n\n    fn port(&self) -> Port {\n        match self.pin_port() / 32 {\n            0 => Port::PortA,\n            #[cfg(gpio_pb)]\n            1 => Port::PortB,\n            #[cfg(gpio_pc)]\n            2 => Port::PortC,\n            _ => unreachable!(),\n        }\n    }\n\n    fn waker(&self) -> &AtomicWaker {\n        match self.port() {\n            Port::PortA => &PORTA_WAKERS[self.bit_index()],\n            #[cfg(gpio_pb)]\n            Port::PortB => &PORTB_WAKERS[self.bit_index()],\n            #[cfg(gpio_pc)]\n            Port::PortC => &PORTC_WAKERS[self.bit_index()],\n        }\n    }\n\n    fn _pin_cm(&self) -> u8 {\n        // Some parts like the MSPM0L222x have pincm mappings all over the place.\n        crate::gpio_pincm(self.pin_port())\n    }\n\n    fn bit_index(&self) -> usize {\n        (self.pin_port() % 32) as usize\n    }\n\n    #[inline]\n    fn set_as_analog(&self) {\n        let pincm = pac::IOMUX.pincm(self._pin_cm() as usize);\n\n        pincm.modify(|w| {\n            w.set_pf(DISCONNECT_PF);\n            w.set_pipu(false);\n            w.set_pipd(false);\n        });\n    }\n\n    #[cfg_attr(mspm0g518x, allow(dead_code))]\n    fn update_pf(&self, ty: PfType) {\n        let pincm = pac::IOMUX.pincm(self._pin_cm() as usize);\n        let pf = pincm.read().pf();\n\n        set_pf(self._pin_cm() as usize, pf, ty);\n    }\n\n    #[cfg_attr(mspm0g518x, allow(dead_code))]\n    fn set_as_pf(&self, pf: u8, ty: PfType) {\n        set_pf(self._pin_cm() as usize, pf, ty)\n    }\n\n    /// Set the pin as \"disconnected\", ie doing nothing and consuming the lowest\n    /// amount of power possible.\n    ///\n    /// This is currently the same as [`Self::set_as_analog()`] but is semantically different\n    /// really. Drivers should `set_as_disconnected()` pins when dropped.\n    ///\n    /// Note that this also disables the internal weak pull-up and pull-down resistors.\n    #[inline]\n    #[cfg_attr(mspm0g518x, allow(dead_code))]\n    fn set_as_disconnected(&self) {\n        self.set_as_analog();\n    }\n\n    #[inline]\n    fn block(&self) -> gpio::Gpio {\n        match self.pin_port() / 32 {\n            0 => pac::GPIOA,\n            #[cfg(gpio_pb)]\n            1 => pac::GPIOB,\n            #[cfg(gpio_pc)]\n            2 => pac::GPIOC,\n            _ => unreachable!(),\n        }\n    }\n}\n\n#[inline(never)]\nfn set_pf(pincm: usize, pf: u8, ty: PfType) {\n    pac::IOMUX.pincm(pincm).modify(|w| {\n        w.set_pf(pf);\n        w.set_pc(true);\n        w.set_pipu(ty.pull == Pull::Up);\n        w.set_pipd(ty.pull == Pull::Down);\n        w.set_inena(ty.input);\n        w.set_inv(ty.invert);\n    });\n}\n\n#[must_use = \"futures do nothing unless you `.await` or poll them\"]\nstruct InputFuture<'d> {\n    pin: Peri<'d, AnyPin>,\n}\n\nimpl<'d> InputFuture<'d> {\n    fn new(pin: Peri<'d, AnyPin>, polarity: Polarity) -> Self {\n        let block = pin.block();\n\n        // Before clearing any previous edge events, we must disable events.\n        //\n        // If we don't do this, it is possible that after we clear the interrupt, the current event\n        // the hardware is listening for may not be the same event we will configure. This may result\n        // in RIS being set. Then when interrupts are unmasked and RIS is set, we may get the wrong event\n        // causing an interrupt.\n        //\n        // Selecting which polarity events happen is a RMW operation.\n        critical_section::with(|_cs| {\n            if pin.bit_index() >= 16 {\n                block.polarity31_16().modify(|w| {\n                    w.set_dio(pin.bit_index() - 16, Polarity::DISABLE);\n                });\n            } else {\n                block.polarity15_0().modify(|w| {\n                    w.set_dio(pin.bit_index(), Polarity::DISABLE);\n                });\n            };\n        });\n\n        // First clear the bit for this event. Otherwise previous edge events may be recorded.\n        block.cpu_int().iclr().write(|w| {\n            w.set_dio(pin.bit_index(), true);\n        });\n\n        // Selecting which polarity events happen is a RMW operation.\n        critical_section::with(|_cs| {\n            // Tell the hardware which pin event we want to receive.\n            if pin.bit_index() >= 16 {\n                block.polarity31_16().modify(|w| {\n                    w.set_dio(pin.bit_index() - 16, polarity);\n                });\n            } else {\n                block.polarity15_0().modify(|w| {\n                    w.set_dio(pin.bit_index(), polarity);\n                });\n            };\n        });\n\n        Self { pin }\n    }\n}\n\nimpl<'d> Future for InputFuture<'d> {\n    type Output = ();\n\n    fn poll(self: FuturePin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {\n        // We need to register/re-register the waker for each poll because any\n        // calls to wake will deregister the waker.\n        let waker = self.pin.waker();\n        waker.register(cx.waker());\n\n        // The interrupt handler will mask the interrupt if the event has occurred.\n        if self.pin.block().cpu_int().ris().read().dio(self.pin.bit_index()) {\n            return Poll::Ready(());\n        }\n\n        // Unmasking the interrupt is a RMW operation.\n        //\n        // Guard with a critical section in case two different threads try to unmask at the same time.\n        critical_section::with(|_cs| {\n            self.pin.block().cpu_int().imask().modify(|w| {\n                w.set_dio(self.pin.bit_index(), true);\n            });\n        });\n\n        Poll::Pending\n    }\n}\n\npub(crate) fn init(gpio: gpio::Gpio) {\n    gpio.gprcm().rstctl().write(|w| {\n        w.set_resetstkyclr(true);\n        w.set_resetassert(true);\n        w.set_key(ResetKey::KEY);\n    });\n\n    gpio.gprcm().pwren().write(|w| {\n        w.set_enable(true);\n        w.set_key(PwrenKey::KEY);\n    });\n\n    gpio.evt_mode().modify(|w| {\n        // The CPU will clear it's own interrupts\n        w.set_cpu_cfg(EvtCfg::SOFTWARE);\n    });\n}\n\n#[cfg(feature = \"rt\")]\nfn irq_handler(gpio: gpio::Gpio, wakers: &[AtomicWaker; 32]) {\n    use crate::BitIter;\n    // Only consider pins which have interrupts unmasked.\n\n    let bits = gpio.cpu_int().mis().read().0;\n\n    for i in BitIter(bits) {\n        wakers[i as usize].wake();\n\n        // Notify the future that an edge event has occurred by masking the interrupt for this pin.\n        gpio.cpu_int().imask().modify(|w| {\n            w.set_dio(i as usize, false);\n        });\n    }\n}\n\n#[cfg(all(gpioa_interrupt, gpioa_group))]\ncompile_error!(\"gpioa_interrupt and gpioa_group are mutually exclusive cfgs\");\n#[cfg(all(gpiob_interrupt, gpiob_group))]\ncompile_error!(\"gpiob_interrupt and gpiob_group are mutually exclusive cfgs\");\n\n// C110x and L110x have a dedicated interrupts just for GPIOA.\n//\n// These chips do not have a GROUP1 interrupt.\n#[cfg(all(feature = \"rt\", gpioa_interrupt))]\n#[interrupt]\nfn GPIOA() {\n    irq_handler(pac::GPIOA, &PORTA_WAKERS);\n}\n\n#[cfg(all(feature = \"rt\", gpiob_interrupt))]\n#[interrupt]\nfn GPIOB() {\n    irq_handler(pac::GPIOB, &PORTB_WAKERS);\n}\n\n// These symbols are weakly defined as DefaultHandler and are called by the interrupt group implementation.\n//\n// Defining these as no_mangle is required so that the linker will pick these over the default handler.\n\n#[cfg(all(feature = \"rt\", gpioa_group))]\n#[unsafe(no_mangle)]\n#[allow(non_snake_case)]\nfn GPIOA() {\n    irq_handler(pac::GPIOA, &PORTA_WAKERS);\n}\n\n#[cfg(all(feature = \"rt\", gpiob_group))]\n#[unsafe(no_mangle)]\n#[allow(non_snake_case)]\nfn GPIOB() {\n    irq_handler(pac::GPIOB, &PORTB_WAKERS);\n}\n\n#[cfg(all(feature = \"rt\", gpioc_group))]\n#[allow(non_snake_case)]\n#[unsafe(no_mangle)]\nfn GPIOC() {\n    irq_handler(pac::GPIOC, &PORTC_WAKERS);\n}\n"
  },
  {
    "path": "embassy-mspm0/src/i2c.rs",
    "content": "#![macro_use]\n\nuse core::future;\nuse core::marker::PhantomData;\nuse core::sync::atomic::{AtomicU32, Ordering};\nuse core::task::Poll;\n\nuse embassy_embedded_hal::SetConfig;\nuse embassy_hal_internal::PeripheralType;\nuse embassy_sync::waitqueue::AtomicWaker;\nuse mspm0_metapac::i2c;\n\nuse crate::Peri;\nuse crate::gpio::{AnyPin, PfType, Pull, SealedPin};\nuse crate::interrupt::typelevel::Binding;\nuse crate::interrupt::{Interrupt, InterruptExt};\nuse crate::mode::{Async, Blocking, Mode};\nuse crate::pac::i2c::{I2c as Regs, vals};\nuse crate::pac::{self};\n\n/// The clock source for the I2C.\n#[derive(Clone, Copy, PartialEq, Eq, Debug)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub enum ClockSel {\n    /// Use the bus clock.\n    ///\n    /// Configurable clock.\n    BusClk,\n\n    /// Use the middle frequency clock.\n    ///\n    /// The MCLK runs at 4 MHz.\n    MfClk,\n}\n\n/// The clock divider for the I2C.\n#[derive(Clone, Copy, PartialEq, Eq, Debug)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub enum ClockDiv {\n    // \"Do not divide clock source.\n    DivBy1,\n    // \"Divide clock source by 2.\n    DivBy2,\n    // \"Divide clock source by 3.\n    DivBy3,\n    // \"Divide clock source by 4.\n    DivBy4,\n    // \"Divide clock source by 5.\n    DivBy5,\n    // \"Divide clock source by 6.\n    DivBy6,\n    // \"Divide clock source by 7.\n    DivBy7,\n    // \"Divide clock source by 8.\n    DivBy8,\n}\n\nimpl ClockDiv {\n    pub(crate) fn into(self) -> vals::Ratio {\n        match self {\n            Self::DivBy1 => vals::Ratio::DIV_BY_1,\n            Self::DivBy2 => vals::Ratio::DIV_BY_2,\n            Self::DivBy3 => vals::Ratio::DIV_BY_3,\n            Self::DivBy4 => vals::Ratio::DIV_BY_4,\n            Self::DivBy5 => vals::Ratio::DIV_BY_5,\n            Self::DivBy6 => vals::Ratio::DIV_BY_6,\n            Self::DivBy7 => vals::Ratio::DIV_BY_7,\n            Self::DivBy8 => vals::Ratio::DIV_BY_8,\n        }\n    }\n\n    fn divider(self) -> u32 {\n        match self {\n            Self::DivBy1 => 1,\n            Self::DivBy2 => 2,\n            Self::DivBy3 => 3,\n            Self::DivBy4 => 4,\n            Self::DivBy5 => 5,\n            Self::DivBy6 => 6,\n            Self::DivBy7 => 7,\n            Self::DivBy8 => 8,\n        }\n    }\n}\n\n/// The I2C mode.\n#[derive(Clone, Copy, PartialEq, Eq, Debug)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub enum BusSpeed {\n    /// Standard mode.\n    ///\n    /// The Standard mode runs at 100 kHz.\n    Standard,\n\n    /// Fast mode.\n    ///\n    /// The fast mode runs at 400 kHz.\n    FastMode,\n\n    /// Fast mode plus.\n    ///\n    /// The fast mode plus runs at 1 MHz.\n    FastModePlus,\n\n    /// Custom mode.\n    ///\n    /// The custom mode frequency (in Hz) can be set manually.\n    Custom(u32),\n}\n\nimpl BusSpeed {\n    fn hertz(self) -> u32 {\n        match self {\n            Self::Standard => 100_000,\n            Self::FastMode => 400_000,\n            Self::FastModePlus => 1_000_000,\n            Self::Custom(s) => s,\n        }\n    }\n}\n\n#[non_exhaustive]\n#[derive(Clone, Copy, PartialEq, Eq, Debug)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\n/// Config Error\npub enum ConfigError {\n    /// Invalid clock rate.\n    ///\n    /// The clock rate could not be configured with the given conifguratoin.\n    InvalidClockRate,\n\n    /// Clock source not enabled.\n    ///\n    /// The clock soure is not enabled is SYSCTL.\n    ClockSourceNotEnabled,\n\n    /// Invalid target address.\n    ///\n    /// The target address is not 7-bit.\n    InvalidTargetAddress,\n}\n\n#[non_exhaustive]\n#[derive(Clone, Copy, PartialEq, Eq, Debug)]\n/// Config\npub struct Config {\n    /// I2C clock source.\n    pub(crate) clock_source: ClockSel,\n\n    /// I2C clock divider.\n    pub clock_div: ClockDiv,\n\n    /// If true: invert SDA pin signal values (V<sub>DD</sub> = 0/mark, Gnd = 1/idle).\n    pub invert_sda: bool,\n\n    /// If true: invert SCL pin signal values (V<sub>DD</sub> = 0/mark, Gnd = 1/idle).\n    pub invert_scl: bool,\n\n    /// Set the pull configuration for the SDA pin.\n    pub sda_pull: Pull,\n\n    /// Set the pull configuration for the SCL pin.\n    pub scl_pull: Pull,\n\n    /// Set the pull configuration for the SCL pin.\n    pub bus_speed: BusSpeed,\n}\n\nimpl Default for Config {\n    fn default() -> Self {\n        Self {\n            clock_source: ClockSel::MfClk,\n            clock_div: ClockDiv::DivBy1,\n            invert_sda: false,\n            invert_scl: false,\n            sda_pull: Pull::None,\n            scl_pull: Pull::None,\n            bus_speed: BusSpeed::Standard,\n        }\n    }\n}\n\nimpl Config {\n    pub fn sda_pf(&self) -> PfType {\n        PfType::input(self.sda_pull, self.invert_sda)\n    }\n    pub fn scl_pf(&self) -> PfType {\n        PfType::input(self.scl_pull, self.invert_scl)\n    }\n    fn calculate_timer_period(&self) -> u8 {\n        // Sets the timer period to bring the clock frequency to the selected I2C speed\n        // From the documentation: TPR = (I2C_CLK / (I2C_FREQ * (SCL_LP + SCL_HP))) - 1 where:\n        // - I2C_FREQ is desired I2C frequency (= I2C_BASE_FREQ divided by I2C_DIV)\n        // - TPR is the Timer Period register value (range of 1 to 127)\n        // - SCL_LP is the SCL Low period (fixed at 6)\n        // - SCL_HP is the SCL High period (fixed at 4)\n        // - I2C_CLK is functional clock frequency\n        return ((self.calculate_clock_source() / (self.bus_speed.hertz() * 10u32)) - 1)\n            .try_into()\n            .unwrap();\n    }\n\n    #[cfg(any(mspm0c110x, mspm0c1105_c1106))]\n    pub(crate) fn calculate_clock_source(&self) -> u32 {\n        // Assume that BusClk has default value.\n        // TODO: calculate BusClk more precisely.\n        match self.clock_source {\n            ClockSel::MfClk => 4_000_000 / self.clock_div.divider(),\n            ClockSel::BusClk => 24_000_000 / self.clock_div.divider(),\n        }\n    }\n\n    #[cfg(any(\n        mspm0g110x, mspm0g150x, mspm0g151x, mspm0g310x, mspm0g350x, mspm0g351x, mspm0h321x, mspm0l110x, mspm0l122x,\n        mspm0l130x, mspm0l134x, mspm0l222x\n    ))]\n    pub(crate) fn calculate_clock_source(&self) -> u32 {\n        // Assume that BusClk has default value.\n        // TODO: calculate BusClk more precisely.\n        match self.clock_source {\n            ClockSel::MfClk => 4_000_000 / self.clock_div.divider(),\n            ClockSel::BusClk => 32_000_000 / self.clock_div.divider(),\n        }\n    }\n\n    fn check_clock_i2c(&self) -> bool {\n        // make sure source clock is ~20 faster than i2c clock\n        let clk_ratio = 20;\n\n        let i2c_clk = self.bus_speed.hertz() / self.clock_div.divider();\n        let src_clk = self.calculate_clock_source();\n\n        // check clock rate\n        return src_clk >= i2c_clk * clk_ratio;\n    }\n\n    fn define_clock_source(&mut self) -> bool {\n        // decide which clock source to choose based on i2c clock.\n        // If i2c speed <= 200kHz, use MfClk, otherwise use BusClk\n        if self.bus_speed.hertz() / self.clock_div.divider() > 200_000 {\n            // TODO: check if BUSCLK enabled\n            self.clock_source = ClockSel::BusClk;\n        } else {\n            // is MFCLK enabled\n            if !pac::SYSCTL.mclkcfg().read().usemftick() {\n                return false;\n            }\n            self.clock_source = ClockSel::MfClk;\n        }\n        return true;\n    }\n\n    /// Check the config.\n    ///\n    /// Make sure that configuration is valid and enabled by the system.\n    pub fn check_config(&mut self) -> Result<(), ConfigError> {\n        if !self.define_clock_source() {\n            return Err(ConfigError::ClockSourceNotEnabled);\n        }\n\n        if !self.check_clock_i2c() {\n            return Err(ConfigError::InvalidClockRate);\n        }\n\n        Ok(())\n    }\n}\n\n/// Serial error\n#[derive(Debug, Eq, PartialEq, Copy, Clone)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\n#[non_exhaustive]\npub enum Error {\n    /// Bus error\n    Bus,\n\n    /// Arbitration lost\n    Arbitration,\n\n    /// ACK not received (either to the address or to a data byte)\n    Nack,\n\n    /// Timeout\n    Timeout,\n\n    /// CRC error\n    Crc,\n\n    /// Overrun error\n    Overrun,\n\n    /// Zero-length transfers are not allowed.\n    ZeroLengthTransfer,\n\n    /// Transfer length is over limit.\n    TransferLengthIsOverLimit,\n}\n\nimpl core::fmt::Display for Error {\n    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {\n        let message = match self {\n            Self::Bus => \"Bus Error\",\n            Self::Arbitration => \"Arbitration Lost\",\n            Self::Nack => \"ACK Not Received\",\n            Self::Timeout => \"Request Timed Out\",\n            Self::Crc => \"CRC Mismatch\",\n            Self::Overrun => \"Buffer Overrun\",\n            Self::ZeroLengthTransfer => \"Zero-Length Transfers are not allowed\",\n            Self::TransferLengthIsOverLimit => \"Transfer length is over limit\",\n        };\n\n        write!(f, \"{}\", message)\n    }\n}\n\nimpl core::error::Error for Error {}\n\n/// I2C Driver.\npub struct I2c<'d, M: Mode> {\n    info: &'static Info,\n    state: &'static State,\n    scl: Option<Peri<'d, AnyPin>>,\n    sda: Option<Peri<'d, AnyPin>>,\n    _phantom: PhantomData<M>,\n}\n\nimpl<'d, M: Mode> SetConfig for I2c<'d, M> {\n    type Config = Config;\n    type ConfigError = ConfigError;\n\n    fn set_config(&mut self, config: &Self::Config) -> Result<(), Self::ConfigError> {\n        self.set_config(*config)\n    }\n}\n\nimpl<'d> I2c<'d, Blocking> {\n    pub fn new_blocking<T: Instance>(\n        peri: Peri<'d, T>,\n        scl: Peri<'d, impl SclPin<T>>,\n        sda: Peri<'d, impl SdaPin<T>>,\n        mut config: Config,\n    ) -> Result<Self, ConfigError> {\n        if let Err(err) = config.check_config() {\n            return Err(err);\n        }\n\n        Self::new_inner(peri, scl, sda, config)\n    }\n}\n\nimpl<'d> I2c<'d, Async> {\n    pub fn new_async<T: Instance>(\n        peri: Peri<'d, T>,\n        scl: Peri<'d, impl SclPin<T>>,\n        sda: Peri<'d, impl SdaPin<T>>,\n        _irq: impl Binding<T::Interrupt, InterruptHandler<T>> + 'd,\n        mut config: Config,\n    ) -> Result<Self, ConfigError> {\n        if let Err(err) = config.check_config() {\n            return Err(err);\n        }\n\n        let i2c = Self::new_inner(peri, scl, sda, config);\n\n        T::info().interrupt.unpend();\n        unsafe { T::info().interrupt.enable() };\n\n        i2c\n    }\n}\n\nimpl<'d, M: Mode> I2c<'d, M> {\n    /// Reconfigure the driver\n    pub fn set_config(&mut self, mut config: Config) -> Result<(), ConfigError> {\n        if let Err(err) = config.check_config() {\n            return Err(err);\n        }\n\n        self.info.interrupt.disable();\n\n        if let Some(ref sda) = self.sda {\n            sda.update_pf(config.sda_pf());\n        }\n\n        if let Some(ref scl) = self.scl {\n            scl.update_pf(config.scl_pf());\n        }\n\n        self.init(&config)\n    }\n\n    fn init(&mut self, config: &Config) -> Result<(), ConfigError> {\n        // Init I2C\n        self.info.regs.clksel().write(|w| match config.clock_source {\n            ClockSel::BusClk => {\n                w.set_mfclk_sel(false);\n                w.set_busclk_sel(true);\n            }\n            ClockSel::MfClk => {\n                w.set_mfclk_sel(true);\n                w.set_busclk_sel(false);\n            }\n        });\n        self.info.regs.clkdiv().write(|w| w.set_ratio(config.clock_div.into()));\n\n        // set up glitch filter\n        self.info.regs.gfctl().modify(|w| {\n            w.set_agfen(false);\n            w.set_agfsel(vals::Agfsel::AGLIT_50);\n            w.set_chain(true);\n        });\n\n        // Reset controller transfer, follow TI example\n        self.info.regs.controller(0).cctr().modify(|w| {\n            w.set_burstrun(false);\n            w.set_start(false);\n            w.set_stop(false);\n            w.set_ack(false);\n            w.set_cackoen(false);\n            w.set_rd_on_txempty(false);\n            w.set_cblen(0);\n        });\n\n        self.state\n            .clock\n            .store(config.calculate_clock_source(), Ordering::Relaxed);\n\n        self.info\n            .regs\n            .controller(0)\n            .ctpr()\n            .write(|w| w.set_tpr(config.calculate_timer_period()));\n\n        // Set Tx Fifo threshold, follow TI example\n        self.info\n            .regs\n            .controller(0)\n            .cfifoctl()\n            .write(|w| w.set_txtrig(vals::CfifoctlTxtrig::EMPTY));\n        // Set Rx Fifo threshold, follow TI example\n        self.info\n            .regs\n            .controller(0)\n            .cfifoctl()\n            .write(|w| w.set_rxtrig(vals::CfifoctlRxtrig::LEVEL_1));\n        // Enable controller clock stretching, follow TI example\n\n        self.info.regs.controller(0).ccr().modify(|w| {\n            w.set_clkstretch(true);\n            w.set_active(true);\n        });\n\n        Ok(())\n    }\n\n    fn master_stop(&mut self) {\n        // not the first transaction, delay 1000 cycles\n        cortex_m::asm::delay(1000);\n\n        // Stop transaction\n        self.info.regs.controller(0).cctr().modify(|w| {\n            w.set_cblen(0);\n            w.set_stop(true);\n            w.set_start(false);\n        });\n    }\n\n    fn master_continue(&mut self, length: usize, send_ack_nack: bool, send_stop: bool) -> Result<(), Error> {\n        // delay between ongoing transactions, 1000 cycles\n        cortex_m::asm::delay(1000);\n\n        // Update transaction to length amount of bytes\n        self.info.regs.controller(0).cctr().modify(|w| {\n            w.set_cblen(length as u16);\n            w.set_start(false);\n            w.set_ack(send_ack_nack);\n            w.set_stop(send_stop);\n        });\n\n        Ok(())\n    }\n\n    fn master_read(\n        &mut self,\n        address: u8,\n        length: usize,\n        restart: bool,\n        send_ack_nack: bool,\n        send_stop: bool,\n    ) -> Result<(), Error> {\n        if restart {\n            // not the first transaction, delay 1000 cycles\n            cortex_m::asm::delay(1000);\n        }\n\n        // Set START and prepare to receive bytes into\n        // `buffer`. The START bit can be set even if the bus\n        // is BUSY or I2C is in slave mode.\n        self.info.regs.controller(0).csa().modify(|w| {\n            w.set_taddr(address as u16);\n            w.set_cmode(vals::Mode::MODE7);\n            w.set_dir(vals::Dir::RECEIVE);\n        });\n\n        self.info.regs.controller(0).cctr().modify(|w| {\n            w.set_cblen(length as u16);\n            w.set_burstrun(true);\n            w.set_ack(send_ack_nack);\n            w.set_start(true);\n            w.set_stop(send_stop);\n        });\n\n        Ok(())\n    }\n\n    fn master_write(&mut self, address: u8, length: usize, send_stop: bool) -> Result<(), Error> {\n        // Start transfer of length amount of bytes\n        self.info.regs.controller(0).csa().modify(|w| {\n            w.set_taddr(address as u16);\n            w.set_cmode(vals::Mode::MODE7);\n            w.set_dir(vals::Dir::TRANSMIT);\n        });\n        self.info.regs.controller(0).cctr().modify(|w| {\n            w.set_cblen(length as u16);\n            w.set_burstrun(true);\n            w.set_start(true);\n            w.set_stop(send_stop);\n        });\n\n        Ok(())\n    }\n\n    fn check_error(&self) -> Result<(), Error> {\n        let csr = self.info.regs.controller(0).csr().read();\n        if csr.err() {\n            return Err(Error::Nack);\n        } else if csr.arblst() {\n            return Err(Error::Arbitration);\n        }\n        Ok(())\n    }\n}\n\nimpl<'d> I2c<'d, Blocking> {\n    fn master_blocking_continue(&mut self, length: usize, send_ack_nack: bool, send_stop: bool) -> Result<(), Error> {\n        // Perform transaction\n        self.master_continue(length, send_ack_nack, send_stop)?;\n\n        // Poll until the Controller process all bytes or NACK\n        while self.info.regs.controller(0).csr().read().busy() {}\n\n        Ok(())\n    }\n\n    fn master_blocking_read(\n        &mut self,\n        address: u8,\n        length: usize,\n        restart: bool,\n        send_ack_nack: bool,\n        send_stop: bool,\n    ) -> Result<(), Error> {\n        // unless restart, Wait for the controller to be idle,\n        if !restart {\n            while !self.info.regs.controller(0).csr().read().idle() {}\n        }\n\n        self.master_read(address, length, restart, send_ack_nack, send_stop)?;\n\n        // Poll until the Controller process all bytes or NACK\n        while self.info.regs.controller(0).csr().read().busy() {}\n\n        Ok(())\n    }\n\n    fn master_blocking_write(&mut self, address: u8, length: usize, send_stop: bool) -> Result<(), Error> {\n        // Wait for the controller to be idle\n        while !self.info.regs.controller(0).csr().read().idle() {}\n\n        // Perform writing\n        self.master_write(address, length, send_stop)?;\n\n        // Poll until the Controller writes all bytes or NACK\n        while self.info.regs.controller(0).csr().read().busy() {}\n\n        Ok(())\n    }\n\n    fn read_blocking_internal(\n        &mut self,\n        address: u8,\n        read: &mut [u8],\n        restart: bool,\n        end_w_stop: bool,\n    ) -> Result<(), Error> {\n        if read.is_empty() {\n            return Err(Error::ZeroLengthTransfer);\n        }\n        if read.len() > self.info.fifo_size {\n            return Err(Error::TransferLengthIsOverLimit);\n        }\n\n        let read_len = read.len();\n        let mut bytes_to_read = read_len;\n        for (number, chunk) in read.chunks_mut(self.info.fifo_size).enumerate() {\n            bytes_to_read -= chunk.len();\n            // if the current transaction is the last & end_w_stop, send stop\n            let send_stop = bytes_to_read == 0 && end_w_stop;\n            // if there are still bytes to read, send ACK\n            let send_ack_nack = bytes_to_read != 0;\n\n            if number == 0 {\n                self.master_blocking_read(\n                    address,\n                    chunk.len().min(self.info.fifo_size),\n                    restart,\n                    send_ack_nack,\n                    send_stop,\n                )?\n            } else {\n                self.master_blocking_continue(chunk.len(), send_ack_nack, send_stop)?;\n            }\n\n            // check errors\n            if let Err(err) = self.check_error() {\n                self.master_stop();\n                return Err(err);\n            }\n\n            for byte in chunk {\n                *byte = self.info.regs.controller(0).crxdata().read().value();\n            }\n        }\n        Ok(())\n    }\n\n    fn write_blocking_internal(&mut self, address: u8, write: &[u8], end_w_stop: bool) -> Result<(), Error> {\n        if write.is_empty() {\n            return Err(Error::ZeroLengthTransfer);\n        }\n        if write.len() > self.info.fifo_size {\n            return Err(Error::TransferLengthIsOverLimit);\n        }\n\n        let mut bytes_to_send = write.len();\n        for (number, chunk) in write.chunks(self.info.fifo_size).enumerate() {\n            for byte in chunk {\n                let ctrl0 = self.info.regs.controller(0).ctxdata();\n                ctrl0.write(|w| w.set_value(*byte));\n            }\n\n            // if the current transaction is the last & end_w_stop, send stop\n            bytes_to_send -= chunk.len();\n            let send_stop = end_w_stop && bytes_to_send == 0;\n\n            if number == 0 {\n                self.master_blocking_write(address, chunk.len(), send_stop)?;\n            } else {\n                self.master_blocking_continue(chunk.len(), false, send_stop)?;\n            }\n\n            // check errors\n            if let Err(err) = self.check_error() {\n                self.master_stop();\n                return Err(err);\n            }\n        }\n        Ok(())\n    }\n\n    // =========================\n    //  Blocking public API\n\n    /// Blocking read.\n    pub fn blocking_read(&mut self, address: u8, read: &mut [u8]) -> Result<(), Error> {\n        // wait until bus is free\n        while self.info.regs.controller(0).csr().read().busbsy() {}\n        self.read_blocking_internal(address, read, false, true)\n    }\n\n    /// Blocking write.\n    pub fn blocking_write(&mut self, address: u8, write: &[u8]) -> Result<(), Error> {\n        // wait until bus is free\n        while self.info.regs.controller(0).csr().read().busbsy() {}\n        self.write_blocking_internal(address, write, true)\n    }\n\n    /// Blocking write, restart, read.\n    pub fn blocking_write_read(&mut self, address: u8, write: &[u8], read: &mut [u8]) -> Result<(), Error> {\n        // wait until bus is free\n        while self.info.regs.controller(0).csr().read().busbsy() {}\n        let err = self.write_blocking_internal(address, write, false);\n        if err != Ok(()) {\n            return err;\n        }\n        self.read_blocking_internal(address, read, true, true)\n    }\n}\n\nimpl<'d> I2c<'d, Async> {\n    async fn write_async_internal(&mut self, addr: u8, write: &[u8], end_w_stop: bool) -> Result<(), Error> {\n        let ctrl = self.info.regs.controller(0);\n\n        let mut bytes_to_send = write.len();\n        for (number, chunk) in write.chunks(self.info.fifo_size).enumerate() {\n            self.info.regs.cpu_int(0).imask().modify(|w| {\n                w.set_carblost(true);\n                w.set_cnack(true);\n                w.set_ctxdone(true);\n            });\n\n            for byte in chunk {\n                ctrl.ctxdata().write(|w| w.set_value(*byte));\n            }\n\n            // if the current transaction is the last & end_w_stop, send stop\n            bytes_to_send -= chunk.len();\n            let send_stop = end_w_stop && bytes_to_send == 0;\n\n            if number == 0 {\n                self.master_write(addr, chunk.len(), send_stop)?;\n            } else {\n                self.master_continue(chunk.len(), false, send_stop)?;\n            }\n\n            let res: Result<(), Error> = future::poll_fn(|cx| {\n                use crate::i2c::vals::CpuIntIidxStat;\n                // Register prior to checking the condition\n                self.state.waker.register(cx.waker());\n\n                let result = match self.info.regs.cpu_int(0).iidx().read().stat() {\n                    CpuIntIidxStat::NO_INTR => Poll::Pending,\n                    CpuIntIidxStat::CNACKFG => Poll::Ready(Err(Error::Nack)),\n                    CpuIntIidxStat::CARBLOSTFG => Poll::Ready(Err(Error::Arbitration)),\n                    CpuIntIidxStat::CTXDONEFG => Poll::Ready(Ok(())),\n                    _ => Poll::Pending,\n                };\n\n                if !result.is_pending() {\n                    self.info\n                        .regs\n                        .cpu_int(0)\n                        .imask()\n                        .write_value(i2c::regs::CpuInt::default());\n                }\n                return result;\n            })\n            .await;\n\n            if res.is_err() {\n                self.master_stop();\n                return res;\n            }\n        }\n        Ok(())\n    }\n\n    async fn read_async_internal(\n        &mut self,\n        addr: u8,\n        read: &mut [u8],\n        restart: bool,\n        end_w_stop: bool,\n    ) -> Result<(), Error> {\n        let read_len = read.len();\n\n        let mut bytes_to_read = read_len;\n        for (number, chunk) in read.chunks_mut(self.info.fifo_size).enumerate() {\n            bytes_to_read -= chunk.len();\n            // if the current transaction is the last & end_w_stop, send stop\n            let send_stop = bytes_to_read == 0 && end_w_stop;\n            // if there are still bytes to read, send ACK\n            let send_ack_nack = bytes_to_read != 0;\n\n            self.info.regs.cpu_int(0).imask().modify(|w| {\n                w.set_carblost(true);\n                w.set_cnack(true);\n                w.set_crxdone(true);\n            });\n\n            if number == 0 {\n                self.master_read(addr, chunk.len(), restart, send_ack_nack, send_stop)?\n            } else {\n                self.master_continue(chunk.len(), send_ack_nack, send_stop)?;\n            }\n\n            let res: Result<(), Error> = future::poll_fn(|cx| {\n                use crate::i2c::vals::CpuIntIidxStat;\n                // Register prior to checking the condition\n                self.state.waker.register(cx.waker());\n\n                let result = match self.info.regs.cpu_int(0).iidx().read().stat() {\n                    CpuIntIidxStat::NO_INTR => Poll::Pending,\n                    CpuIntIidxStat::CNACKFG => Poll::Ready(Err(Error::Nack)),\n                    CpuIntIidxStat::CARBLOSTFG => Poll::Ready(Err(Error::Arbitration)),\n                    CpuIntIidxStat::CRXDONEFG => Poll::Ready(Ok(())),\n                    _ => Poll::Pending,\n                };\n\n                if !result.is_pending() {\n                    self.info\n                        .regs\n                        .cpu_int(0)\n                        .imask()\n                        .write_value(i2c::regs::CpuInt::default());\n                }\n                return result;\n            })\n            .await;\n\n            if res.is_err() {\n                self.master_stop();\n                return res;\n            }\n\n            for byte in chunk {\n                *byte = self.info.regs.controller(0).crxdata().read().value();\n            }\n        }\n        Ok(())\n    }\n\n    // =========================\n    //  Async public API\n\n    pub async fn async_write(&mut self, address: u8, write: &[u8]) -> Result<(), Error> {\n        // wait until bus is free\n        while self.info.regs.controller(0).csr().read().busbsy() {}\n        self.write_async_internal(address, write, true).await\n    }\n\n    pub async fn async_read(&mut self, address: u8, read: &mut [u8]) -> Result<(), Error> {\n        // wait until bus is free\n        while self.info.regs.controller(0).csr().read().busbsy() {}\n        self.read_async_internal(address, read, false, true).await\n    }\n\n    pub async fn async_write_read(&mut self, address: u8, write: &[u8], read: &mut [u8]) -> Result<(), Error> {\n        // wait until bus is free\n        while self.info.regs.controller(0).csr().read().busbsy() {}\n\n        let err = self.write_async_internal(address, write, false).await;\n        if err != Ok(()) {\n            return err;\n        }\n        self.read_async_internal(address, read, true, true).await\n    }\n}\n\nimpl<'d> embedded_hal_02::blocking::i2c::Read for I2c<'d, Blocking> {\n    type Error = Error;\n\n    fn read(&mut self, address: u8, buffer: &mut [u8]) -> Result<(), Self::Error> {\n        self.blocking_read(address, buffer)\n    }\n}\n\nimpl<'d> embedded_hal_02::blocking::i2c::Write for I2c<'d, Blocking> {\n    type Error = Error;\n\n    fn write(&mut self, address: u8, bytes: &[u8]) -> Result<(), Self::Error> {\n        self.blocking_write(address, bytes)\n    }\n}\n\nimpl<'d> embedded_hal_02::blocking::i2c::WriteRead for I2c<'d, Blocking> {\n    type Error = Error;\n\n    fn write_read(&mut self, address: u8, bytes: &[u8], buffer: &mut [u8]) -> Result<(), Self::Error> {\n        self.blocking_write_read(address, bytes, buffer)\n    }\n}\n\nimpl<'d> embedded_hal_02::blocking::i2c::Transactional for I2c<'d, Blocking> {\n    type Error = Error;\n\n    fn exec(\n        &mut self,\n        address: u8,\n        operations: &mut [embedded_hal_02::blocking::i2c::Operation<'_>],\n    ) -> Result<(), Self::Error> {\n        // wait until bus is free\n        while self.info.regs.controller(0).csr().read().busbsy() {}\n        for i in 0..operations.len() {\n            match &mut operations[i] {\n                embedded_hal_02::blocking::i2c::Operation::Read(buf) => {\n                    self.read_blocking_internal(address, buf, false, false)?\n                }\n                embedded_hal_02::blocking::i2c::Operation::Write(buf) => {\n                    self.write_blocking_internal(address, buf, false)?\n                }\n            }\n        }\n        self.master_stop();\n        Ok(())\n    }\n}\n\nimpl embedded_hal::i2c::Error for Error {\n    fn kind(&self) -> embedded_hal::i2c::ErrorKind {\n        match *self {\n            Self::Bus => embedded_hal::i2c::ErrorKind::Bus,\n            Self::Arbitration => embedded_hal::i2c::ErrorKind::ArbitrationLoss,\n            Self::Nack => embedded_hal::i2c::ErrorKind::NoAcknowledge(embedded_hal::i2c::NoAcknowledgeSource::Unknown),\n            Self::Timeout => embedded_hal::i2c::ErrorKind::Other,\n            Self::Crc => embedded_hal::i2c::ErrorKind::Other,\n            Self::Overrun => embedded_hal::i2c::ErrorKind::Overrun,\n            Self::ZeroLengthTransfer => embedded_hal::i2c::ErrorKind::Other,\n            Self::TransferLengthIsOverLimit => embedded_hal::i2c::ErrorKind::Other,\n        }\n    }\n}\n\nimpl<'d, M: Mode> embedded_hal::i2c::ErrorType for I2c<'d, M> {\n    type Error = Error;\n}\n\nimpl<'d> embedded_hal::i2c::I2c for I2c<'d, Blocking> {\n    fn read(&mut self, address: u8, read: &mut [u8]) -> Result<(), Self::Error> {\n        self.blocking_read(address, read)\n    }\n\n    fn write(&mut self, address: u8, write: &[u8]) -> Result<(), Self::Error> {\n        self.blocking_write(address, write)\n    }\n\n    fn write_read(&mut self, address: u8, write: &[u8], read: &mut [u8]) -> Result<(), Self::Error> {\n        self.blocking_write_read(address, write, read)\n    }\n\n    fn transaction(\n        &mut self,\n        address: u8,\n        operations: &mut [embedded_hal::i2c::Operation<'_>],\n    ) -> Result<(), Self::Error> {\n        // wait until bus is free\n        while self.info.regs.controller(0).csr().read().busbsy() {}\n        for i in 0..operations.len() {\n            match &mut operations[i] {\n                embedded_hal::i2c::Operation::Read(buf) => self.read_blocking_internal(address, buf, false, false)?,\n                embedded_hal::i2c::Operation::Write(buf) => self.write_blocking_internal(address, buf, false)?,\n            }\n        }\n        self.master_stop();\n        Ok(())\n    }\n}\n\nimpl<'d> embedded_hal_async::i2c::I2c for I2c<'d, Async> {\n    async fn read(&mut self, address: u8, read: &mut [u8]) -> Result<(), Self::Error> {\n        self.async_read(address, read).await\n    }\n\n    async fn write(&mut self, address: u8, write: &[u8]) -> Result<(), Self::Error> {\n        self.async_write(address, write).await\n    }\n\n    async fn write_read(&mut self, address: u8, write: &[u8], read: &mut [u8]) -> Result<(), Self::Error> {\n        self.async_write_read(address, write, read).await\n    }\n\n    async fn transaction(\n        &mut self,\n        address: u8,\n        operations: &mut [embedded_hal::i2c::Operation<'_>],\n    ) -> Result<(), Self::Error> {\n        // wait until bus is free\n        while self.info.regs.controller(0).csr().read().busbsy() {}\n        for i in 0..operations.len() {\n            match &mut operations[i] {\n                embedded_hal::i2c::Operation::Read(buf) => self.read_async_internal(address, buf, false, false).await?,\n                embedded_hal::i2c::Operation::Write(buf) => self.write_async_internal(address, buf, false).await?,\n            }\n        }\n        self.master_stop();\n        Ok(())\n    }\n}\n\n/// Interrupt handler.\npub struct InterruptHandler<T: Instance> {\n    _i2c: PhantomData<T>,\n}\n\nimpl<T: Instance> crate::interrupt::typelevel::Handler<T::Interrupt> for InterruptHandler<T> {\n    // Mask interrupts and wake any task waiting for this interrupt\n    unsafe fn on_interrupt() {\n        T::state().waker.wake();\n    }\n}\n\n/// Peripheral instance trait.\n#[allow(private_bounds)]\npub trait Instance: SealedInstance + PeripheralType {\n    type Interrupt: crate::interrupt::typelevel::Interrupt;\n}\n\n/// I2C `SDA` pin trait\npub trait SdaPin<T: Instance>: crate::gpio::Pin {\n    /// Get the PF number needed to use this pin as `SDA`.\n    fn pf_num(&self) -> u8;\n}\n\n/// I2C `SCL` pin trait\npub trait SclPin<T: Instance>: crate::gpio::Pin {\n    /// Get the PF number needed to use this pin as `SCL`.\n    fn pf_num(&self) -> u8;\n}\n\n// ==== IMPL types ====\n\npub(crate) struct Info {\n    pub(crate) regs: Regs,\n    pub(crate) interrupt: Interrupt,\n    pub fifo_size: usize,\n}\n\npub(crate) struct State {\n    /// The clock rate of the I2C. This might be configured.\n    pub(crate) clock: AtomicU32,\n    pub(crate) waker: AtomicWaker,\n}\n\nimpl<'d, M: Mode> I2c<'d, M> {\n    fn new_inner<T: Instance>(\n        _peri: Peri<'d, T>,\n        scl: Peri<'d, impl SclPin<T>>,\n        sda: Peri<'d, impl SdaPin<T>>,\n        config: Config,\n    ) -> Result<Self, ConfigError> {\n        // Init power for I2C\n        T::info().regs.gprcm(0).rstctl().write(|w| {\n            w.set_resetstkyclr(true);\n            w.set_resetassert(true);\n            w.set_key(vals::ResetKey::KEY);\n        });\n\n        T::info().regs.gprcm(0).pwren().write(|w| {\n            w.set_enable(true);\n            w.set_key(vals::PwrenKey::KEY);\n        });\n\n        // init delay, 16 cycles\n        cortex_m::asm::delay(16);\n\n        // Init GPIO\n        let scl_inner = new_pin!(scl, config.scl_pf());\n        let sda_inner = new_pin!(sda, config.sda_pf());\n\n        if let Some(ref scl) = scl_inner {\n            let pincm = pac::IOMUX.pincm(scl._pin_cm() as usize);\n            pincm.modify(|w| {\n                w.set_hiz1(true);\n            });\n        }\n\n        if let Some(ref sda) = sda_inner {\n            let pincm = pac::IOMUX.pincm(sda._pin_cm() as usize);\n            pincm.modify(|w| {\n                w.set_hiz1(true);\n            });\n        }\n\n        let mut this = Self {\n            info: T::info(),\n            state: T::state(),\n            scl: scl_inner,\n            sda: sda_inner,\n            _phantom: PhantomData,\n        };\n        this.init(&config)?;\n\n        Ok(this)\n    }\n}\n\npub(crate) trait SealedInstance {\n    fn info() -> &'static Info;\n    fn state() -> &'static State;\n}\n\nmacro_rules! impl_i2c_instance {\n    ($instance: ident, $fifo_size: expr) => {\n        impl crate::i2c::SealedInstance for crate::peripherals::$instance {\n            fn info() -> &'static crate::i2c::Info {\n                use crate::i2c::Info;\n                use crate::interrupt::typelevel::Interrupt;\n\n                const INFO: Info = Info {\n                    regs: crate::pac::$instance,\n                    interrupt: crate::interrupt::typelevel::$instance::IRQ,\n                    fifo_size: $fifo_size,\n                };\n                &INFO\n            }\n\n            fn state() -> &'static crate::i2c::State {\n                use crate::i2c::State;\n                use crate::interrupt::typelevel::Interrupt;\n\n                static STATE: State = State {\n                    clock: core::sync::atomic::AtomicU32::new(0),\n                    waker: embassy_sync::waitqueue::AtomicWaker::new(),\n                };\n                &STATE\n            }\n        }\n\n        impl crate::i2c::Instance for crate::peripherals::$instance {\n            type Interrupt = crate::interrupt::typelevel::$instance;\n        }\n    };\n}\n\nmacro_rules! impl_i2c_sda_pin {\n    ($instance: ident, $pin: ident, $pf: expr) => {\n        impl crate::i2c::SdaPin<crate::peripherals::$instance> for crate::peripherals::$pin {\n            fn pf_num(&self) -> u8 {\n                $pf\n            }\n        }\n    };\n}\n\nmacro_rules! impl_i2c_scl_pin {\n    ($instance: ident, $pin: ident, $pf: expr) => {\n        impl crate::i2c::SclPin<crate::peripherals::$instance> for crate::peripherals::$pin {\n            fn pf_num(&self) -> u8 {\n                $pf\n            }\n        }\n    };\n}\n\n#[cfg(test)]\nmod tests {\n    use crate::i2c::{BusSpeed, ClockDiv, ClockSel, Config};\n\n    /// These tests are based on TI's reference caluclation.\n    #[test]\n    fn ti_calculate_timer_period() {\n        let mut config = Config::default();\n        config.clock_div = ClockDiv::DivBy1;\n        config.bus_speed = BusSpeed::FastMode;\n        config.clock_source = ClockSel::BusClk;\n        assert_eq!(config.calculate_timer_period(), 7u8);\n    }\n\n    #[test]\n    fn ti_calculate_timer_period_2() {\n        let mut config = Config::default();\n        config.clock_div = ClockDiv::DivBy2;\n        config.bus_speed = BusSpeed::FastMode;\n        config.clock_source = ClockSel::BusClk;\n        assert_eq!(config.calculate_timer_period(), 3u8);\n    }\n\n    #[test]\n    fn ti_calculate_timer_period_3() {\n        let mut config = Config::default();\n        config.clock_div = ClockDiv::DivBy2;\n        config.bus_speed = BusSpeed::Standard;\n        config.clock_source = ClockSel::BusClk;\n        assert_eq!(config.calculate_timer_period(), 15u8);\n    }\n\n    #[test]\n    fn ti_calculate_timer_period_4() {\n        let mut config = Config::default();\n        config.clock_div = ClockDiv::DivBy2;\n        config.bus_speed = BusSpeed::Custom(100_000);\n        config.clock_source = ClockSel::BusClk;\n        assert_eq!(config.calculate_timer_period(), 15u8);\n    }\n\n    #[test]\n    fn clock_check_fastmodeplus_rate_with_busclk() {\n        let mut config = Config::default();\n        config.clock_source = ClockSel::BusClk;\n        config.bus_speed = BusSpeed::FastModePlus;\n        assert!(config.check_clock_i2c());\n    }\n\n    #[test]\n    fn clock_check_fastmode_rate_with_busclk() {\n        let mut config = Config::default();\n        config.clock_source = ClockSel::BusClk;\n        config.bus_speed = BusSpeed::FastMode;\n        assert!(config.check_clock_i2c());\n    }\n\n    #[test]\n    fn clock_check_fastmodeplus_rate_with_mfclk() {\n        let mut config = Config::default();\n        config.clock_source = ClockSel::MfClk;\n        config.bus_speed = BusSpeed::FastModePlus;\n        assert!(!config.check_clock_i2c());\n    }\n\n    #[test]\n    fn clock_check_fastmode_rate_with_mfclk() {\n        let mut config = Config::default();\n        config.clock_source = ClockSel::MfClk;\n        config.bus_speed = BusSpeed::FastMode;\n        assert!(!config.check_clock_i2c());\n    }\n}\n"
  },
  {
    "path": "embassy-mspm0/src/i2c_target.rs",
    "content": "//! Inter-Integrated-Circuit (I2C) Target\n// The following code is modified from embassy-stm32 and embassy-rp\n// https://github.com/embassy-rs/embassy/tree/main/embassy-stm32\n// https://github.com/embassy-rs/embassy/tree/main/embassy-rp\n\nuse core::future::poll_fn;\nuse core::marker::PhantomData;\nuse core::sync::atomic::Ordering;\nuse core::task::Poll;\n\nuse embassy_embedded_hal::SetConfig;\nuse mspm0_metapac::i2c::vals::CpuIntIidxStat;\n\nuse crate::gpio::{AnyPin, SealedPin};\n// Re-use I2c controller types\nuse crate::i2c::{ClockSel, ConfigError, Info, Instance, InterruptHandler, SclPin, SdaPin, State};\nuse crate::interrupt::InterruptExt;\nuse crate::mode::{Async, Blocking, Mode};\nuse crate::pac::i2c::vals;\nuse crate::pac::{self};\nuse crate::{Peri, i2c, i2c_target, interrupt};\n\n#[non_exhaustive]\n#[derive(Clone, Copy, PartialEq, Eq, Debug)]\n/// Config\npub struct Config {\n    /// 7-bit Target Address\n    pub target_addr: u8,\n\n    /// Control if the target should ack to and report general calls.\n    pub general_call: bool,\n}\n\nimpl Default for Config {\n    fn default() -> Self {\n        Self {\n            target_addr: 0x48,\n            general_call: false,\n        }\n    }\n}\n\n/// I2C error\n#[derive(Debug, PartialEq, Eq, Clone, Copy)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\n#[non_exhaustive]\npub enum Error {\n    /// User passed in a response buffer that was 0 length\n    InvalidResponseBufferLength,\n    /// The response buffer length was too short to contain the message\n    ///\n    /// The length parameter will always be the length of the buffer, and is\n    /// provided as a convenience for matching alongside `Command::Write`.\n    PartialWrite(usize),\n    /// The response buffer length was too short to contain the message\n    ///\n    /// The length parameter will always be the length of the buffer, and is\n    /// provided as a convenience for matching alongside `Command::GeneralCall`.\n    PartialGeneralCall(usize),\n}\n\n/// Received command from the controller.\n#[derive(Debug, Copy, Clone, Eq, PartialEq)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub enum Command {\n    /// General Call Write: Controller sent the General Call address (0x00) followed by data.\n    /// Contains the number of bytes written by the controller.\n    GeneralCall(usize),\n    /// Read: Controller wants to read data from the target.\n    Read,\n    /// Write: Controller sent the target's address followed by data.\n    /// Contains the number of bytes written by the controller.\n    Write(usize),\n    /// Write followed by Read (Repeated Start): Controller wrote data, then issued a repeated\n    /// start and wants to read data. Contains the number of bytes written before the read.\n    WriteRead(usize),\n}\n\n/// Status after responding to a controller read request.\n#[derive(Debug, Copy, Clone, Eq, PartialEq)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub enum ReadStatus {\n    /// Transaction completed successfully. The controller either NACKed the last byte\n    /// or sent a STOP condition.\n    Done,\n    /// Transaction incomplete, controller trying to read more bytes than were provided\n    NeedMoreBytes,\n    /// Transaction complete, but controller stopped reading bytes before we ran out\n    LeftoverBytes(u16),\n}\n\n/// I2C Target driver.\n// Use the same Instance, SclPin, SdaPin traits as the controller\npub struct I2cTarget<'d, M: Mode> {\n    info: &'static Info,\n    state: &'static State,\n    scl: Option<Peri<'d, AnyPin>>,\n    sda: Option<Peri<'d, AnyPin>>,\n    config: i2c::Config,\n    target_config: i2c_target::Config,\n    _phantom: PhantomData<M>,\n}\n\nimpl<'d> SetConfig for I2cTarget<'d, Async> {\n    type Config = (i2c::Config, i2c_target::Config);\n    type ConfigError = ConfigError;\n\n    fn set_config(&mut self, config: &Self::Config) -> Result<(), Self::ConfigError> {\n        self.info.interrupt.disable();\n\n        if let Some(ref sda) = self.sda {\n            sda.update_pf(config.0.sda_pf());\n        }\n\n        if let Some(ref scl) = self.scl {\n            scl.update_pf(config.0.scl_pf());\n        }\n\n        self.config = config.0.clone();\n        self.target_config = config.1.clone();\n\n        self.reset()\n    }\n}\n\nimpl<'d> SetConfig for I2cTarget<'d, Blocking> {\n    type Config = (i2c::Config, i2c_target::Config);\n    type ConfigError = ConfigError;\n\n    fn set_config(&mut self, config: &Self::Config) -> Result<(), Self::ConfigError> {\n        if let Some(ref sda) = self.sda {\n            sda.update_pf(config.0.sda_pf());\n        }\n\n        if let Some(ref scl) = self.scl {\n            scl.update_pf(config.0.scl_pf());\n        }\n\n        self.config = config.0.clone();\n        self.target_config = config.1.clone();\n\n        self.reset()\n    }\n}\n\nimpl<'d> I2cTarget<'d, Async> {\n    /// Create a new asynchronous I2C target driver using interrupts\n    /// The `config` reuses the i2c controller config to setup the clock while `target_config`\n    /// configures i2c target specific parameters.\n    pub fn new<T: Instance>(\n        peri: Peri<'d, T>,\n        scl: Peri<'d, impl SclPin<T>>,\n        sda: Peri<'d, impl SdaPin<T>>,\n        _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd,\n        config: i2c::Config,\n        target_config: i2c_target::Config,\n    ) -> Result<Self, ConfigError> {\n        let mut this = Self::new_inner(\n            peri,\n            new_pin!(scl, config.scl_pf()),\n            new_pin!(sda, config.sda_pf()),\n            config,\n            target_config,\n        );\n        this.reset()?;\n        Ok(this)\n    }\n\n    /// Reset the i2c peripheral. If you cancel a respond_to_read, you may stall the bus.\n    /// You can recover the bus by calling this function, but doing so will almost certainly cause\n    /// an i/o error in the controller.\n    pub fn reset(&mut self) -> Result<(), ConfigError> {\n        self.init()?;\n        unsafe { self.info.interrupt.enable() };\n        Ok(())\n    }\n}\n\nimpl<'d> I2cTarget<'d, Blocking> {\n    /// Create a new blocking I2C target driver.\n    /// The `config` reuses the i2c controller config to setup the clock while `target_config`\n    /// configures i2c target specific parameters.\n    pub fn new_blocking<T: Instance>(\n        peri: Peri<'d, T>,\n        scl: Peri<'d, impl SclPin<T>>,\n        sda: Peri<'d, impl SdaPin<T>>,\n        config: i2c::Config,\n        target_config: i2c_target::Config,\n    ) -> Result<Self, ConfigError> {\n        let mut this = Self::new_inner(\n            peri,\n            new_pin!(scl, config.scl_pf()),\n            new_pin!(sda, config.sda_pf()),\n            config,\n            target_config,\n        );\n        this.reset()?;\n        Ok(this)\n    }\n\n    /// Reset the i2c peripheral. If you cancel a respond_to_read, you may stall the bus.\n    /// You can recover the bus by calling this function, but doing so will almost certainly cause\n    /// an i/o error in the controller.\n    pub fn reset(&mut self) -> Result<(), ConfigError> {\n        self.init()?;\n        Ok(())\n    }\n}\n\nimpl<'d, M: Mode> I2cTarget<'d, M> {\n    fn new_inner<T: Instance>(\n        _peri: Peri<'d, T>,\n        scl: Option<Peri<'d, AnyPin>>,\n        sda: Option<Peri<'d, AnyPin>>,\n        config: i2c::Config,\n        target_config: i2c_target::Config,\n    ) -> Self {\n        if let Some(ref scl) = scl {\n            let pincm = pac::IOMUX.pincm(scl._pin_cm() as usize);\n            pincm.modify(|w| {\n                w.set_hiz1(true);\n            });\n        }\n        if let Some(ref sda) = sda {\n            let pincm = pac::IOMUX.pincm(sda._pin_cm() as usize);\n            pincm.modify(|w| {\n                w.set_hiz1(true);\n            });\n        }\n\n        Self {\n            info: T::info(),\n            state: T::state(),\n            scl,\n            sda,\n            config,\n            target_config,\n            _phantom: PhantomData,\n        }\n    }\n\n    fn init(&mut self) -> Result<(), ConfigError> {\n        let mut config = self.config;\n        let target_config = self.target_config;\n        let regs = self.info.regs;\n\n        config.check_config()?;\n        // Target address must be 7-bit\n        if !(target_config.target_addr < 0x80) {\n            return Err(ConfigError::InvalidTargetAddress);\n        }\n\n        regs.target(0).tctr().modify(|w| {\n            w.set_active(false);\n        });\n\n        // Init power for I2C\n        regs.gprcm(0).rstctl().write(|w| {\n            w.set_resetstkyclr(true);\n            w.set_resetassert(true);\n            w.set_key(vals::ResetKey::KEY);\n        });\n\n        regs.gprcm(0).pwren().write(|w| {\n            w.set_enable(true);\n            w.set_key(vals::PwrenKey::KEY);\n        });\n\n        self.info.interrupt.disable();\n\n        // Init delay from the M0 examples by TI in CCStudio (16 cycles)\n        cortex_m::asm::delay(16);\n\n        // Select and configure the I2C clock using the CLKSEL and CLKDIV registers\n        regs.clksel().write(|w| match config.clock_source {\n            ClockSel::BusClk => {\n                w.set_mfclk_sel(false);\n                w.set_busclk_sel(true);\n            }\n            ClockSel::MfClk => {\n                w.set_mfclk_sel(true);\n                w.set_busclk_sel(false);\n            }\n        });\n        regs.clkdiv().write(|w| w.set_ratio(config.clock_div.into()));\n\n        // Configure at least one target address by writing the 7-bit address to I2Cx.SOAR register. The additional\n        // target address can be enabled and configured by using I2Cx.TOAR2 register.\n        regs.target(0).toar().modify(|w| {\n            w.set_oaren(true);\n            w.set_oar(target_config.target_addr as u16);\n        });\n\n        self.state\n            .clock\n            .store(config.calculate_clock_source(), Ordering::Relaxed);\n\n        regs.target(0).tctr().modify(|w| {\n            w.set_gencall(target_config.general_call);\n            w.set_tclkstretch(true);\n            // Disable target wakeup, follow TI example. (TI note: Workaround for errata I2C_ERR_04.)\n            w.set_twuen(false);\n            w.set_txempty_on_treq(true);\n        });\n\n        // Enable the I2C target mode by setting the ACTIVE bit in I2Cx.TCTR register.\n        regs.target(0).tctr().modify(|w| {\n            w.set_active(true);\n        });\n\n        Ok(())\n    }\n\n    #[inline(always)]\n    fn drain_fifo(&mut self, buffer: &mut [u8], offset: &mut usize) {\n        let regs = self.info.regs;\n\n        for b in &mut buffer[*offset..] {\n            if regs.target(0).tfifosr().read().rxfifocnt() == 0 {\n                break;\n            }\n\n            *b = regs.target(0).trxdata().read().value();\n            *offset += 1;\n        }\n    }\n\n    /// Blocking function to empty the tx fifo\n    ///\n    /// This function can be used to empty the transmit FIFO if data remains after handling a 'read' command (LeftoverBytes).\n    pub fn flush_tx_fifo(&mut self) {\n        self.info.regs.target(0).tfifoctl().modify(|w| {\n            w.set_txflush(true);\n        });\n        while self.info.regs.target(0).tfifosr().read().txfifocnt() as usize != self.info.fifo_size {}\n        self.info.regs.target(0).tfifoctl().modify(|w| {\n            w.set_txflush(false);\n        });\n    }\n}\n\nimpl<'d> I2cTarget<'d, Async> {\n    /// Wait asynchronously for commands from an I2C controller.\n    /// `buffer` is provided in case controller does a 'write', 'write read', or 'general call' and is unused for 'read'.\n    pub async fn listen(&mut self, buffer: &mut [u8]) -> Result<Command, Error> {\n        let regs = self.info.regs;\n\n        let mut len = 0;\n\n        // Set the rx fifo interrupt to avoid a fifo overflow\n        regs.target(0).tfifoctl().modify(|r| {\n            r.set_rxtrig(vals::TfifoctlRxtrig::LEVEL_6);\n        });\n\n        self.wait_on(\n            |me| {\n                // Check if address matches the General Call address (0x00)\n                let is_gencall = regs.target(0).tsr().read().addrmatch() == 0;\n\n                if regs.target(0).tfifosr().read().rxfifocnt() > 0 {\n                    me.drain_fifo(buffer, &mut len);\n                }\n\n                if buffer.len() == len && regs.target(0).tfifosr().read().rxfifocnt() > 0 {\n                    if is_gencall {\n                        return Poll::Ready(Err(Error::PartialGeneralCall(buffer.len())));\n                    } else {\n                        return Poll::Ready(Err(Error::PartialWrite(buffer.len())));\n                    }\n                }\n\n                let iidx = regs.cpu_int(0).iidx().read().stat();\n                trace!(\"ls:{} len:{}\", iidx as u8, len);\n                let result = match iidx {\n                    CpuIntIidxStat::TTXEMPTY => match len {\n                        0 => Poll::Ready(Ok(Command::Read)),\n                        w => Poll::Ready(Ok(Command::WriteRead(w))),\n                    },\n                    CpuIntIidxStat::TSTOPFG => match (is_gencall, len) {\n                        (_, 0) => Poll::Pending,\n                        (true, w) => Poll::Ready(Ok(Command::GeneralCall(w))),\n                        (false, w) => Poll::Ready(Ok(Command::Write(w))),\n                    },\n                    _ => Poll::Pending,\n                };\n                if !result.is_pending() {\n                    regs.cpu_int(0).imask().write(|_| {});\n                }\n                result\n            },\n            |_me| {\n                regs.cpu_int(0).imask().write(|_| {});\n                regs.cpu_int(0).imask().modify(|w| {\n                    w.set_tgencall(true);\n                    w.set_trxfifotrg(true);\n                    w.set_tstop(true);\n                    w.set_ttxempty(true);\n                });\n            },\n        )\n        .await\n    }\n\n    /// Respond to an I2C controller 'read' command, asynchronously.\n    pub async fn respond_to_read(&mut self, buffer: &[u8]) -> Result<ReadStatus, Error> {\n        if buffer.is_empty() {\n            return Err(Error::InvalidResponseBufferLength);\n        }\n\n        let regs = self.info.regs;\n        let fifo_size = self.info.fifo_size;\n        let mut chunks = buffer.chunks(self.info.fifo_size);\n\n        self.wait_on(\n            |_me| {\n                if let Some(chunk) = chunks.next() {\n                    for byte in chunk {\n                        regs.target(0).ttxdata().write(|w| w.set_value(*byte));\n                    }\n\n                    return Poll::Pending;\n                }\n\n                let iidx = regs.cpu_int(0).iidx().read().stat();\n                let fifo_bytes = fifo_size - regs.target(0).tfifosr().read().txfifocnt() as usize;\n                trace!(\"rs:{}, fifo:{}\", iidx as u8, fifo_bytes);\n\n                let result = match iidx {\n                    CpuIntIidxStat::TTXEMPTY => Poll::Ready(Ok(ReadStatus::NeedMoreBytes)),\n                    CpuIntIidxStat::TSTOPFG => match fifo_bytes {\n                        0 => Poll::Ready(Ok(ReadStatus::Done)),\n                        w => Poll::Ready(Ok(ReadStatus::LeftoverBytes(w as u16))),\n                    },\n                    _ => Poll::Pending,\n                };\n                if !result.is_pending() {\n                    regs.cpu_int(0).imask().write(|_| {});\n                }\n                result\n            },\n            |_me| {\n                regs.cpu_int(0).imask().write(|_| {});\n                regs.cpu_int(0).imask().modify(|w| {\n                    w.set_ttxempty(true);\n                    w.set_tstop(true);\n                });\n            },\n        )\n        .await\n    }\n\n    /// Respond to reads with the fill byte until the controller stops asking\n    pub async fn respond_till_stop(&mut self, fill: u8) -> Result<(), Error> {\n        // The buffer size could be increased to reduce interrupt noise but has higher probability\n        // of LeftoverBytes\n        let buff = [fill];\n        loop {\n            match self.respond_to_read(&buff).await {\n                Ok(ReadStatus::NeedMoreBytes) => (),\n                Ok(_) => break Ok(()),\n                Err(e) => break Err(e),\n            }\n        }\n    }\n\n    /// Respond to a controller read, then fill any remaining read bytes with `fill`\n    pub async fn respond_and_fill(&mut self, buffer: &[u8], fill: u8) -> Result<ReadStatus, Error> {\n        let resp_stat = self.respond_to_read(buffer).await?;\n\n        if resp_stat == ReadStatus::NeedMoreBytes {\n            self.respond_till_stop(fill).await?;\n            Ok(ReadStatus::Done)\n        } else {\n            Ok(resp_stat)\n        }\n    }\n\n    /// Calls `f` to check if we are ready or not.\n    /// If not, `g` is called once(to eg enable the required interrupts).\n    /// The waker will always be registered prior to calling `f`.\n    #[inline(always)]\n    async fn wait_on<F, U, G>(&mut self, mut f: F, mut g: G) -> U\n    where\n        F: FnMut(&mut Self) -> Poll<U>,\n        G: FnMut(&mut Self),\n    {\n        poll_fn(|cx| {\n            // Register prior to checking the condition\n            self.state.waker.register(cx.waker());\n            let r = f(self);\n\n            if r.is_pending() {\n                g(self);\n            }\n\n            r\n        })\n        .await\n    }\n}\n\nimpl<'d, M: Mode> Drop for I2cTarget<'d, M> {\n    fn drop(&mut self) {\n        // Ensure peripheral is disabled and pins are reset\n        self.info.regs.target(0).tctr().modify(|w| w.set_active(false));\n\n        self.scl.as_ref().map(|x| x.set_as_disconnected());\n        self.sda.as_ref().map(|x| x.set_as_disconnected());\n    }\n}\n"
  },
  {
    "path": "embassy-mspm0/src/lib.rs",
    "content": "#![no_std]\n#![allow(unsafe_op_in_unsafe_fn)]\n// Doc feature labels can be tested locally by running RUSTDOCFLAGS=\"--cfg=docsrs\" cargo +nightly doc\n#![cfg_attr(docsrs, feature(doc_auto_cfg, doc_cfg_hide), doc(cfg_hide(doc, docsrs)))]\n#![cfg_attr(\n    docsrs,\n    doc = \"<div style='padding:30px;background:#810;color:#fff;text-align:center;'><p>You might want to <a href='https://docs.embassy.dev/embassy-mspm0'>browse the `embassy-mspm0` documentation on the Embassy website</a> instead.</p><p>The documentation here on `docs.rs` is built for a single chip only, while on the Embassy website you can pick your exact chip from the top menu. Available peripherals and their APIs change depending on the chip.</p></div>\\n\\n\"\n)]\n#![doc = include_str!(\"../README.md\")]\n\n// These mods MUST go first, so that the others see the macros.\npub(crate) mod fmt;\nmod macros;\n\npub mod adc;\npub mod dma;\npub mod gpio;\n// TODO: I2C unicomm\n#[cfg(not(unicomm))]\npub mod i2c;\n#[cfg(not(unicomm))]\npub mod i2c_target;\n#[cfg(any(mspm0g150x, mspm0g151x, mspm0g350x, mspm0g351x))]\npub mod mathacl;\npub mod timer;\n#[cfg(any(mspm0g150x, mspm0g151x, mspm0g350x, mspm0g351x, mspm0l122x, mspm0l222x))]\npub mod trng;\n// TODO: UART unicomm\n#[cfg(not(unicomm))]\npub mod uart;\npub mod wwdt;\n\n/// Operating modes for peripherals.\npub mod mode {\n    trait SealedMode {}\n\n    /// Operating mode for a peripheral.\n    #[allow(private_bounds)]\n    pub trait Mode: SealedMode {}\n\n    /// Blocking mode.\n    pub struct Blocking;\n    impl SealedMode for Blocking {}\n    impl Mode for Blocking {}\n\n    /// Async mode.\n    pub struct Async;\n    impl SealedMode for Async {}\n    impl Mode for Async {}\n}\n\n#[cfg(feature = \"_time-driver\")]\nmod time_driver;\n\npub(crate) mod _generated {\n    #![allow(dead_code)]\n    #![allow(unused_imports)]\n    #![allow(non_snake_case)]\n    #![allow(missing_docs)]\n\n    include!(concat!(env!(\"OUT_DIR\"), \"/_generated.rs\"));\n}\n\n// Reexports\npub(crate) use _generated::gpio_pincm;\npub use _generated::{Peripherals, peripherals};\npub use embassy_hal_internal::Peri;\n#[cfg(feature = \"unstable-pac\")]\npub use mspm0_metapac as pac;\n#[cfg(not(feature = \"unstable-pac\"))]\npub(crate) use mspm0_metapac as pac;\n\npub use crate::_generated::interrupt;\n\n/// Macro to bind interrupts to handlers.\n///\n/// This defines the right interrupt handlers, and creates a unit struct (like `struct Irqs;`)\n/// and implements the right [`Binding`]s for it. You can pass this struct to drivers to\n/// prove at compile-time that the right interrupts have been bound.\n///\n/// Example of how to bind one interrupt:\n///\n/// ```rust,ignore\n/// use embassy_nrf::{bind_interrupts, spim, peripherals};\n///\n/// bind_interrupts!(\n///     /// Binds the SPIM3 interrupt.\n///     struct Irqs {\n///         SPIM3 => spim::InterruptHandler<peripherals::SPI3>;\n///     }\n/// );\n/// ```\n///\n/// Example of how to bind multiple interrupts in a single macro invocation:\n///\n/// ```rust,ignore\n/// use embassy_nrf::{bind_interrupts, spim, twim, peripherals};\n///\n/// bind_interrupts!(struct Irqs {\n///     SPIM3 => spim::InterruptHandler<peripherals::SPI3>;\n///     TWISPI0 => twim::InterruptHandler<peripherals::TWISPI0>;\n/// });\n/// ```\n\n// developer note: this macro can't be in `embassy-hal-internal` due to the use of `$crate`.\n#[macro_export]\nmacro_rules! bind_interrupts {\n    ($(#[$attr:meta])* $vis:vis struct $name:ident {\n        $(\n            $(#[cfg($cond_irq:meta)])?\n            $irq:ident => $(\n                $(#[cfg($cond_handler:meta)])?\n                $handler:ty\n            ),*;\n        )*\n    }) => {\n        #[derive(Copy, Clone)]\n        $(#[$attr])*\n        $vis struct $name;\n\n        $(\n            #[allow(non_snake_case)]\n            #[unsafe(no_mangle)]\n            $(#[cfg($cond_irq)])?\n            unsafe extern \"C\" fn $irq() {\n                unsafe {\n                    $(\n                        $(#[cfg($cond_handler)])?\n                        <$handler as $crate::interrupt::typelevel::Handler<$crate::interrupt::typelevel::$irq>>::on_interrupt();\n\n                    )*\n                }\n            }\n\n            $(#[cfg($cond_irq)])?\n            $crate::bind_interrupts!(@inner\n                $(\n                    $(#[cfg($cond_handler)])?\n                    unsafe impl $crate::interrupt::typelevel::Binding<$crate::interrupt::typelevel::$irq, $handler> for $name {}\n                )*\n            );\n        )*\n    };\n    (@inner $($t:tt)*) => {\n        $($t)*\n    }\n}\n\n/// `embassy-mspm0` global configuration.\n#[non_exhaustive]\n#[derive(Clone, Copy)]\npub struct Config {\n    // TODO: OSC configuration.\n    /// The size of DMA block transfer burst.\n    ///\n    /// If this is set to a value\n    pub dma_burst_size: dma::BurstSize,\n\n    /// Whether the DMA channels are used in a fixed priority or a round robin fashion.\n    ///\n    /// If [`false`], the DMA priorities are fixed.\n    ///\n    /// If [`true`], after a channel finishes a transfer it becomes the lowest priority.\n    pub dma_round_robin: bool,\n}\n\nimpl Default for Config {\n    fn default() -> Self {\n        Self {\n            dma_burst_size: dma::BurstSize::Complete,\n            dma_round_robin: false,\n        }\n    }\n}\n\npub fn init(config: Config) -> Peripherals {\n    critical_section::with(|cs| {\n        let peripherals = Peripherals::take_with_cs(cs);\n\n        // TODO: Further clock configuration\n\n        pac::SYSCTL.mclkcfg().modify(|w| {\n            // Enable MFCLK\n            w.set_usemftick(true);\n            // MDIV must be disabled if MFCLK is enabled.\n            w.set_mdiv(0);\n        });\n\n        // Enable MFCLK for peripheral use\n        //\n        // TODO: Optional?\n        pac::SYSCTL.genclken().modify(|w| {\n            w.set_mfpclken(true);\n        });\n\n        pac::SYSCTL.borthreshold().modify(|w| {\n            w.set_level(0);\n        });\n\n        gpio::init(pac::GPIOA);\n        #[cfg(gpio_pb)]\n        gpio::init(pac::GPIOB);\n        #[cfg(gpio_pc)]\n        gpio::init(pac::GPIOC);\n\n        _generated::enable_group_interrupts(cs);\n\n        #[cfg(any(mspm0c110x, mspm0l110x))]\n        unsafe {\n            use crate::_generated::interrupt::typelevel::Interrupt;\n            crate::interrupt::typelevel::GPIOA::enable();\n        }\n\n        // SAFETY: Peripherals::take_with_cs will only be run once or panic.\n        unsafe { dma::init(cs, config.dma_burst_size, config.dma_round_robin) };\n\n        #[cfg(feature = \"_time-driver\")]\n        time_driver::init(cs);\n\n        peripherals\n    })\n}\n\npub(crate) mod sealed {\n    #[allow(dead_code)]\n    pub trait Sealed {}\n}\n\nstruct BitIter(u32);\n\nimpl Iterator for BitIter {\n    type Item = u32;\n\n    fn next(&mut self) -> Option<Self::Item> {\n        match self.0.trailing_zeros() {\n            32 => None,\n            b => {\n                self.0 &= !(1 << b);\n                Some(b)\n            }\n        }\n    }\n}\n\n/// Reset cause values from SYSCTL.RSTCAUSE register.\n/// Based on MSPM0 L-series Technical Reference Manual Table 2-9 and\n/// MSPM0 G-series Technical Reference Manual Table 2-12.\n#[derive(Clone, Copy, PartialEq, Eq, Debug)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub enum ResetCause {\n    /// No reset since last read\n    NoReset,\n    /// VDD < POR- violation, PMU trim parity fault, or SHUTDNSTOREx parity fault\n    PorHwFailure,\n    /// NRST pin reset (>1s)\n    PorExternalNrst,\n    /// Software-triggered POR\n    PorSwTriggered,\n    /// VDD < BOR- violation\n    BorSupplyFailure,\n    /// Wake from SHUTDOWN\n    BorWakeFromShutdown,\n    /// Non-PMU trim parity fault\n    #[cfg(not(any(\n        mspm0c110x,\n        mspm0c1105_c1106,\n        mspm0g110x,\n        mspm0g150x,\n        mspm0g151x,\n        mspm0g310x,\n        mspm0g350x,\n        mspm0g351x\n    )))]\n    BootrstNonPmuParityFault,\n    /// Fatal clock fault\n    BootrstClockFault,\n    /// Software-triggered BOOTRST\n    BootrstSwTriggered,\n    /// NRST pin reset (<1s)\n    BootrstExternalNrst,\n    /// WWDT0 violation\n    BootrstWwdt0Violation,\n    /// WWDT1 violation (G-series only)\n    #[cfg(any(mspm0g110x, mspm0g150x, mspm0g151x, mspm0g310x, mspm0g350x, mspm0g351x, mspm0g518x))]\n    SysrstWwdt1Violation,\n    /// BSL exit (if present)\n    SysrstBslExit,\n    /// BSL entry (if present)\n    SysrstBslEntry,\n    /// Uncorrectable flash ECC error (if present)\n    #[cfg(not(any(mspm0c110x, mspm0c1105_c1106, mspm0g351x, mspm0g151x)))]\n    SysrstFlashEccError,\n    /// CPU lockup violation\n    SysrstCpuLockupViolation,\n    /// Debug-triggered SYSRST\n    SysrstDebugTriggered,\n    /// Software-triggered SYSRST\n    SysrstSwTriggered,\n    /// Debug-triggered CPURST\n    CpurstDebugTriggered,\n    /// Software-triggered CPURST\n    CpurstSwTriggered,\n}\n\n/// Read the reset cause from the SYSCTL.RSTCAUSE register.\n///\n/// This function reads the reset cause register which indicates why the last\n/// system reset occurred. The register is automatically cleared after being read,\n/// so this should be called only once per application startup.\n///\n/// If the reset cause is not recognized, an `Err` containing the raw value is returned.\n#[must_use = \"Reading reset cause will clear it\"]\npub fn read_reset_cause() -> Result<ResetCause, u8> {\n    let cause_raw = pac::SYSCTL.rstcause().read().id();\n\n    use ResetCause::*;\n    use pac::sysctl::vals::Id;\n\n    match cause_raw {\n        Id::NORST => Ok(NoReset),\n        Id::PORHWFAIL => Ok(PorHwFailure),\n        Id::POREXNRST => Ok(PorExternalNrst),\n        Id::PORSW => Ok(PorSwTriggered),\n        Id::BORSUPPLY => Ok(BorSupplyFailure),\n        Id::BORWAKESHUTDN => Ok(BorWakeFromShutdown),\n        #[cfg(not(any(\n            mspm0c110x,\n            mspm0c1105_c1106,\n            mspm0g110x,\n            mspm0g150x,\n            mspm0g151x,\n            mspm0g310x,\n            mspm0g350x,\n            mspm0g351x,\n            mspm0g518x,\n        )))]\n        Id::BOOTNONPMUPARITY => Ok(BootrstNonPmuParityFault),\n        Id::BOOTCLKFAIL => Ok(BootrstClockFault),\n        Id::BOOTSW => Ok(BootrstSwTriggered),\n        Id::BOOTEXNRST => Ok(BootrstExternalNrst),\n        Id::BOOTWWDT0 => Ok(BootrstWwdt0Violation),\n        Id::SYSBSLEXIT => Ok(SysrstBslExit),\n        Id::SYSBSLENTRY => Ok(SysrstBslEntry),\n        #[cfg(any(mspm0g110x, mspm0g150x, mspm0g151x, mspm0g310x, mspm0g350x, mspm0g351x, mspm0g518x))]\n        Id::SYSWWDT1 => Ok(SysrstWwdt1Violation),\n        #[cfg(not(any(mspm0c110x, mspm0c1105_c1106, mspm0g351x, mspm0g151x)))]\n        Id::SYSFLASHECC => Ok(SysrstFlashEccError),\n        Id::SYSCPULOCK => Ok(SysrstCpuLockupViolation),\n        Id::SYSDBG => Ok(SysrstDebugTriggered),\n        Id::SYSSW => Ok(SysrstSwTriggered),\n        Id::CPUDBG => Ok(CpurstDebugTriggered),\n        Id::CPUSW => Ok(CpurstSwTriggered),\n        other => Err(other as u8),\n    }\n}\n"
  },
  {
    "path": "embassy-mspm0/src/macros.rs",
    "content": "#![macro_use]\n\n#[allow(unused)]\nmacro_rules! new_pin {\n    ($name: ident, $pf_type: expr) => {{\n        let pin = $name;\n        pin.set_as_pf(pin.pf_num(), $pf_type);\n        Some(pin.into())\n    }};\n}\n"
  },
  {
    "path": "embassy-mspm0/src/mathacl.rs",
    "content": "//! MATHACL\n//!\n//! This HAL implements mathematical calculations performed by the CPU.\n\n#![macro_use]\n\nuse core::f32::consts::PI;\nuse core::marker::PhantomData;\n\nuse embassy_hal_internal::PeripheralType;\nuse micromath::F32Ext;\n\nuse crate::Peri;\nuse crate::pac::mathacl::{Mathacl as Regs, vals};\n\nconst ERROR_TOLERANCE: f32 = 0.00001;\n\npub enum Precision {\n    High = 31,\n    Medium = 15,\n    Low = 1,\n}\n\n/// Error type for Mathacl operations.\n#[derive(Debug, Eq, PartialEq, Copy, Clone)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\n#[non_exhaustive]\npub enum Error {\n    ValueInWrongRange,\n    DivideByZero,\n    FaultIQTypeFormat,\n    IQTypeError(IQTypeError),\n}\n\npub struct Mathacl<'d> {\n    regs: &'static Regs,\n    _phantom: PhantomData<&'d mut ()>,\n}\n\nimpl<'d> Mathacl<'d> {\n    /// Mathacl initialization.\n    pub fn new<T: Instance>(_instance: Peri<'d, T>) -> Self {\n        // Init power\n        T::regs().gprcm(0).rstctl().write(|w| {\n            w.set_resetstkyclr(vals::Resetstkyclr::CLR);\n            w.set_resetassert(vals::Resetassert::ASSERT);\n            w.set_key(vals::ResetKey::KEY);\n        });\n\n        // Enable power\n        T::regs().gprcm(0).pwren().write(|w| {\n            w.set_enable(true);\n            w.set_key(vals::PwrenKey::KEY);\n        });\n\n        // init delay, 16 cycles\n        cortex_m::asm::delay(16);\n\n        Self {\n            regs: T::regs(),\n            _phantom: PhantomData,\n        }\n    }\n\n    /// Internal helper SINCOS function.\n    fn sincos(&mut self, rad: f32, precision: Precision, sin: bool) -> Result<f32, Error> {\n        if rad > PI || rad < -PI {\n            return Err(Error::ValueInWrongRange);\n        }\n\n        // make division using mathacl\n        let native = self.div_iq(IQType::from_f32(rad, 15, true)?, IQType::from_f32(PI, 15, true)?)?;\n\n        self.regs.ctl().write(|w| {\n            w.set_func(vals::Func::SINCOS);\n            w.set_numiter(precision as u8);\n        });\n\n        // integer part has to be 0 bits\n        self.regs.op1().write(|w| {\n            w.set_data(IQType::from_f32(native, 0, true).unwrap().to_reg());\n        });\n\n        // check if done\n        while self.regs.status().read().busy() == vals::Busy::NOTDONE {}\n\n        match sin {\n            true => Ok(IQType::from_reg(self.regs.res2().read().data(), 0, true)\n                .unwrap()\n                .to_f32()),\n            false => Ok(IQType::from_reg(self.regs.res1().read().data(), 0, true)\n                .unwrap()\n                .to_f32()),\n        }\n    }\n\n    /// Calsulates trigonometric sine operation in the range [-1,1) with a give precision.\n    pub fn sin(&mut self, rad: f32, precision: Precision) -> Result<f32, Error> {\n        self.sincos(rad, precision, true)\n    }\n\n    /// Calsulates trigonometric cosine operation in the range [-1,1) with a give precision.\n    pub fn cos(&mut self, rad: f32, precision: Precision) -> Result<f32, Error> {\n        self.sincos(rad, precision, false)\n    }\n\n    pub fn div_i32(&mut self, dividend: i32, divisor: i32) -> Result<i32, Error> {\n        if divisor == 0 {\n            return Err(Error::DivideByZero);\n        } else if dividend == 0 {\n            return Ok(0);\n        }\n        let signed = true;\n\n        self.regs.ctl().write(|w| {\n            w.set_func(vals::Func::DIV);\n            w.set_optype(signed);\n        });\n\n        self.regs.op2().write(|w| {\n            w.set_data(divisor as u32);\n        });\n\n        self.regs.op1().write(|w| {\n            w.set_data(dividend as u32);\n        });\n\n        // check if done\n        while self.regs.status().read().busy() == vals::Busy::NOTDONE {}\n\n        // read quotient\n        Ok(self.regs.res1().read().data() as i32)\n    }\n\n    pub fn div_u32(&mut self, dividend: u32, divisor: u32) -> Result<u32, Error> {\n        if divisor == 0 {\n            return Err(Error::DivideByZero);\n        } else if dividend == 0 {\n            return Ok(0);\n        }\n        let signed = false;\n\n        self.regs.ctl().write(|w| {\n            w.set_func(vals::Func::DIV);\n            w.set_optype(signed);\n        });\n\n        self.regs.op2().write(|w| {\n            w.set_data(divisor);\n        });\n\n        self.regs.op1().write(|w| {\n            w.set_data(dividend);\n        });\n\n        // check if done\n        while self.regs.status().read().busy() == vals::Busy::NOTDONE {}\n\n        // read quotient\n        Ok(self.regs.res1().read().data())\n    }\n\n    /// Divide function (DIV) computes with a known dividend and divisor.\n    pub fn div_iq(&mut self, dividend: IQType, divisor: IQType) -> Result<f32, Error> {\n        let divisor_value = divisor.to_f32();\n        if -ERROR_TOLERANCE < divisor_value && divisor_value < ERROR_TOLERANCE {\n            return Err(Error::DivideByZero);\n        }\n\n        // check if both numbers have the same number of bits\n        if dividend.f_bits != divisor.f_bits {\n            return Err(Error::FaultIQTypeFormat);\n        }\n\n        // dividen and divisor must have the same signedness\n        if dividend.signed ^ divisor.signed {\n            return Err(Error::FaultIQTypeFormat);\n        }\n\n        self.regs.ctl().write(|w| {\n            w.set_func(vals::Func::DIV);\n            w.set_optype(dividend.signed);\n            w.set_qval(dividend.f_bits.into());\n        });\n\n        self.regs.op2().write(|w| {\n            w.set_data(divisor.to_reg());\n        });\n\n        self.regs.op1().write(|w| {\n            w.set_data(dividend.to_reg());\n        });\n\n        // check if done\n        while self.regs.status().read().busy() == vals::Busy::NOTDONE {}\n\n        // read quotient\n        return Ok(\n            IQType::from_reg(self.regs.res1().read().data(), dividend.i_bits.into(), dividend.signed)\n                .unwrap()\n                .to_f32(),\n        );\n    }\n}\n\npub(crate) trait SealedInstance {\n    fn regs() -> &'static Regs;\n}\n\n/// Mathacl instance trait\n#[allow(private_bounds)]\npub trait Instance: SealedInstance + PeripheralType {}\n\nmacro_rules! impl_mathacl_instance {\n    ($instance: ident) => {\n        impl crate::mathacl::SealedInstance for crate::peripherals::$instance {\n            fn regs() -> &'static crate::pac::mathacl::Mathacl {\n                &crate::pac::$instance\n            }\n        }\n\n        impl crate::mathacl::Instance for crate::peripherals::$instance {}\n    };\n}\n\n/// Error type for Mathacl operations.\n#[derive(Debug, Eq, PartialEq, Copy, Clone)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\n#[non_exhaustive]\npub enum IQTypeError {\n    FaultySignParameter,\n    IntPartIsTrimmed,\n}\n\nimpl From<IQTypeError> for Error {\n    fn from(e: IQTypeError) -> Self {\n        Error::IQTypeError(e)\n    }\n}\n\n#[derive(Debug, PartialEq, Copy, Clone)]\npub struct IQType {\n    i_bits: u8,\n    f_bits: u8,\n    negative: bool,\n    signed: bool,\n    i_data: u32,\n    f_data: u32,\n}\n\n/// IQType implements 32-bit fixed point numbers with configurable integer and fractional parts.\nimpl IQType {\n    pub fn from_reg(data: u32, i_bits: u8, signed: bool) -> Result<Self, IQTypeError> {\n        // check if negative\n        let negative = signed && ((1u32 << 31) & data != 0);\n\n        // total bit count\n        let total_bits = if signed { 31 } else { 32 };\n\n        // number of fractional bits\n        let f_bits = total_bits - i_bits;\n\n        // Compute masks\n        let max_mask = if signed { 0x7FFFFFFF } else { 0xFFFFFFFF };\n        let (i_mask, f_mask) = if i_bits == 0 {\n            (0, max_mask)\n        } else if i_bits == total_bits {\n            (max_mask, 0)\n        } else {\n            ((1u32 << i_bits) - 1, (1u32 << f_bits) - 1)\n        };\n\n        // Compute i_data and f_data\n        let mut i_data = if i_bits == 0 {\n            0\n        } else if i_bits == total_bits {\n            data & i_mask\n        } else {\n            (data >> f_bits) & i_mask\n        };\n        let mut f_data = data & f_mask;\n\n        // if negative, do 2’s compliment\n        if negative {\n            i_data = !i_data & i_mask;\n            f_data = (!f_data & f_mask) + 1;\n        }\n\n        Ok(Self {\n            i_bits,\n            f_bits,\n            negative,\n            signed,\n            i_data,\n            f_data,\n        })\n    }\n\n    pub fn from_f32(data: f32, i_bits: u8, signed: bool) -> Result<Self, IQTypeError> {\n        // check if negative\n        let negative = data < 0.0;\n\n        if !signed && negative {\n            return Err(IQTypeError::FaultySignParameter);\n        }\n\n        // absolute value\n        let abs = if data < 0.0 { -data } else { data };\n\n        // total bit count\n        let total_bits = if signed { 31 } else { 32 };\n\n        // number of fractional bits\n        let f_bits: u8 = total_bits - i_bits;\n\n        let abs_floor = abs.floor();\n        let i_data = abs_floor as u32;\n        let f_data = ((abs - abs_floor) * (1u32 << f_bits) as f32).round() as u32;\n\n        // Handle trimming integer part\n        if i_bits == 0 && i_data > 0 {\n            return Err(IQTypeError::IntPartIsTrimmed);\n        }\n\n        Ok(Self {\n            i_bits,\n            f_bits,\n            negative,\n            signed,\n            i_data,\n            f_data,\n        })\n    }\n\n    pub fn to_f32(&self) -> f32 {\n        let mut value = (self.i_data as f32) + (self.f_data as f32) / (1u32 << self.f_bits as u8) as f32;\n        if self.negative {\n            value = -value;\n        }\n        return value;\n    }\n\n    pub fn to_reg(&self) -> u32 {\n        let mut res: u32 = 0;\n\n        // total bit count\n        let total_bits: u8 = if self.signed { 31 } else { 32 };\n\n        // Compute masks\n        let max_mask = if self.signed { 0x7FFFFFFF } else { 0xFFFFFFFF };\n        let (i_mask, f_mask) = if self.i_bits == 0 {\n            (0, max_mask)\n        } else if self.i_bits == total_bits {\n            (max_mask, 0)\n        } else {\n            ((1u32 << self.i_bits) - 1, (1u32 << self.f_bits) - 1)\n        };\n\n        // calculate result\n        if self.i_bits > 0 {\n            res = self.i_data << self.f_bits & (i_mask << self.f_bits);\n        }\n\n        if self.f_bits > 0 {\n            res = (self.f_data & f_mask) | res;\n        }\n\n        // if negative, do 2’s compliment\n        if self.negative {\n            res = !res + 1;\n        }\n        res\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n\n    #[test]\n    fn mathacl_iqtype_errors() {\n        // integer part trimmed\n        let mut test_float = 1.0;\n        assert_eq!(\n            IQType::from_f32(test_float, 0, true),\n            Err(IQTypeError::IntPartIsTrimmed)\n        );\n        // negative value for unsigned type\n        test_float = -1.0;\n        assert_eq!(\n            IQType::from_f32(test_float, 1, false),\n            Err(IQTypeError::FaultySignParameter)\n        );\n    }\n\n    #[test]\n    fn mathacl_iqtype_f32_to_f32() {\n        assert_eq!(IQType::from_f32(0.0, 15, true).unwrap().to_f32(), 0.0);\n        assert_eq!(IQType::from_f32(0.0, 16, false).unwrap().to_f32(), 0.0);\n\n        assert_eq!(IQType::from_f32(1.5, 16, false).unwrap().to_f32(), 1.5);\n        assert_eq!(IQType::from_f32(1.5, 15, true).unwrap().to_f32(), 1.5);\n        assert_eq!(IQType::from_f32(-1.5, 15, true).unwrap().to_f32(), -1.5);\n    }\n\n    #[test]\n    fn mathacl_iqtype_reg_to_reg() {\n        assert_eq!(IQType::from_reg(0x0, 15, true).unwrap().to_reg(), 0x0);\n        assert_eq!(IQType::from_reg(0x0, 16, false).unwrap().to_reg(), 0x0);\n\n        assert_eq!(IQType::from_reg(0x00018000, 15, true).unwrap().to_reg(), 0x00018000);\n        assert_eq!(IQType::from_reg(0x00018000, 16, false).unwrap().to_reg(), 0x00018000);\n        assert_eq!(IQType::from_reg(0xFFFE5556, 15, true).unwrap().to_reg(), 0xFFFE5556);\n    }\n\n    #[test]\n    fn mathacl_iqtype_f32_to_register() {\n        let mut test_float = 0.0;\n        assert_eq!(IQType::from_f32(test_float, 15, true).unwrap().to_reg(), 0x0);\n        assert_eq!(IQType::from_f32(test_float, 16, false).unwrap().to_reg(), 0x0);\n\n        test_float = 1.5;\n        assert_eq!(IQType::from_f32(test_float, 15, true).unwrap().to_reg(), 0x00018000);\n        assert_eq!(IQType::from_f32(test_float, 16, false).unwrap().to_reg(), 0x00018000);\n\n        test_float = -1.5;\n        assert_eq!(IQType::from_f32(test_float, 15, true).unwrap().to_reg(), 0xFFFE8000);\n\n        test_float = 1.666657;\n        assert_eq!(IQType::from_f32(test_float, 15, true).unwrap().to_reg(), 0x0001AAAA);\n        assert_eq!(IQType::from_f32(test_float, 16, false).unwrap().to_reg(), 0x0001AAAA);\n\n        test_float = -1.666657;\n        assert_eq!(IQType::from_f32(test_float, 15, true).unwrap().to_reg(), 0xFFFE5556);\n    }\n\n    #[test]\n    fn mathacl_iqtype_register_to_signed_f32() {\n        let mut test_u32: u32 = 0x7FFFFFFF;\n\n        let mut result = IQType::from_reg(test_u32, 0, true).unwrap().to_f32();\n        assert!(result < 1.0 + ERROR_TOLERANCE && result > 1.0 - ERROR_TOLERANCE);\n\n        test_u32 = 0x0;\n        result = IQType::from_reg(test_u32, 0, true).unwrap().to_f32();\n        assert!(result < 0.0 + ERROR_TOLERANCE && result > 0.0 - ERROR_TOLERANCE);\n\n        test_u32 = 0x0001AAAA;\n        result = IQType::from_reg(test_u32, 15, true).unwrap().to_f32();\n        assert!(result < 1.666657 + ERROR_TOLERANCE && result > 1.666657 - ERROR_TOLERANCE);\n\n        test_u32 = 0xFFFE5556;\n        result = IQType::from_reg(test_u32, 15, true).unwrap().to_f32();\n        assert!(result < -1.666657 + ERROR_TOLERANCE && result > -1.666657 - ERROR_TOLERANCE);\n    }\n}\n"
  },
  {
    "path": "embassy-mspm0/src/time_driver.rs",
    "content": "use core::cell::{Cell, RefCell};\nuse core::sync::atomic::{AtomicU32, Ordering, compiler_fence};\nuse core::task::Waker;\n\nuse critical_section::{CriticalSection, Mutex};\nuse embassy_time_driver::Driver;\nuse embassy_time_queue_utils::Queue;\nuse mspm0_metapac::interrupt;\nuse mspm0_metapac::tim::Tim;\nuse mspm0_metapac::tim::vals::{Cm, Cvae, CxC, EvtCfg, PwrenKey, Repeat, ResetKey};\n\nuse crate::peripherals;\nuse crate::timer::SealedTimer;\n\n#[cfg(any(time_driver_timg12, time_driver_timg13))]\ncompile_error!(\"TIMG12 and TIMG13 are not supported by the time driver yet\");\n\n// Currently TIMG12 and TIMG13 are excluded because those are 32-bit timers.\n#[cfg(time_driver_timb0)]\ntype T = peripherals::TIMB0;\n#[cfg(time_driver_timg0)]\ntype T = peripherals::TIMG0;\n#[cfg(time_driver_timg1)]\ntype T = peripherals::TIMG1;\n#[cfg(time_driver_timg2)]\ntype T = peripherals::TIMG2;\n#[cfg(time_driver_timg3)]\ntype T = peripherals::TIMG3;\n#[cfg(time_driver_timg4)]\ntype T = peripherals::TIMG4;\n#[cfg(time_driver_timg5)]\ntype T = peripherals::TIMG5;\n#[cfg(time_driver_timg6)]\ntype T = peripherals::TIMG6;\n#[cfg(time_driver_timg7)]\ntype T = peripherals::TIMG7;\n#[cfg(time_driver_timg8)]\ntype T = peripherals::TIMG8;\n#[cfg(time_driver_timg9)]\ntype T = peripherals::TIMG9;\n#[cfg(time_driver_timg10)]\ntype T = peripherals::TIMG10;\n#[cfg(time_driver_timg11)]\ntype T = peripherals::TIMG11;\n#[cfg(time_driver_timg14)]\ntype T = peripherals::TIMG14;\n#[cfg(time_driver_tima0)]\ntype T = peripherals::TIMA0;\n#[cfg(time_driver_tima1)]\ntype T = peripherals::TIMA1;\n\n// TODO: RTC\n\nfn regs() -> Tim {\n    unsafe { Tim::from_ptr(T::regs()) }\n}\n\n/// Clock timekeeping works with something we call \"periods\", which are time intervals\n/// of 2^15 ticks. The Clock counter value is 16 bits, so one \"overflow cycle\" is 2 periods.\nfn calc_now(period: u32, counter: u16) -> u64 {\n    ((period as u64) << 15) + ((counter as u32 ^ ((period & 1) << 15)) as u64)\n}\n\n/// The TIMx driver uses one of the `TIMG` or `TIMA` timer instances to implement a timer with a 32.768 kHz\n/// tick rate. (TODO: Allow setting the tick rate)\n///\n/// This driver defines a period to be 2^15 ticks. 16-bit timers of course count to 2^16 ticks.\n///\n/// To generate a period every 2^15 ticks, the CC0 value is set to 2^15 and the load value set to 2^16.\n/// Incrementing the period on a CCU0 and load results in the a period of 2^15 ticks.\n///\n/// For a specific timestamp, load the lower 16 bits into the CC1 value. When the period where the timestamp\n/// should be enabled is reached, then the CCU1 (CC1 up) interrupt runs to actually wake the timer.\n///\n/// TODO: Compensate for per part variance. This can supposedly be done with the FCC system.\n/// TODO: Allow using 32-bit timers (TIMG12 and TIMG13).\nstruct TimxDriver {\n    /// Number of 2^15 periods elapsed since boot.\n    period: AtomicU32,\n    /// Timestamp at which to fire alarm. u64::MAX if no alarm is scheduled.\n    alarm: Mutex<Cell<u64>>,\n    queue: Mutex<RefCell<Queue>>,\n}\n\nimpl TimxDriver {\n    #[inline(never)]\n    fn init(&'static self, _cs: CriticalSection) {\n        // Clock config\n        // TODO: Configurable tick rate up to 4 MHz (32 kHz for now)\n        let regs = regs();\n\n        // Reset timer\n        regs.gprcm(0).rstctl().write(|w| {\n            w.set_resetassert(true);\n            w.set_key(ResetKey::KEY);\n            w.set_resetstkyclr(true);\n        });\n\n        // Power up timer\n        regs.gprcm(0).pwren().write(|w| {\n            w.set_enable(true);\n            w.set_key(PwrenKey::KEY);\n        });\n\n        // Following the instructions according to SLAU847D 23.2.1: TIMCLK Configuration\n\n        // 1. Select TIMCLK source\n        regs.clksel().modify(|w| {\n            // Use LFCLK for a 32.768kHz tick rate\n            w.set_lfclk_sel(true);\n            // TODO: Allow MFCLK for configurable tick rate up to 4 MHz\n            // w.set_mfclk_sel(ClkSel::ENABLE);\n        });\n\n        // 2. Divide by TIMCLK, we don't need to divide further for the 32kHz tick rate\n        regs.clkdiv().modify(|w| {\n            w.set_ratio(0); // + 1\n        });\n\n        // 3. To be generic across timer instances, we do not use the prescaler.\n        // TODO: mspm0-sdk always sets this, regardless of timer width?\n        regs.commonregs(0).cps().modify(|w| {\n            w.set_pcnt(0);\n        });\n\n        regs.pdbgctl().modify(|w| {\n            w.set_free(true);\n        });\n\n        // 4. Enable the TIMCLK.\n        regs.commonregs(0).cclkctl().modify(|w| {\n            w.set_clken(true);\n        });\n\n        regs.counterregs(0).ctrctl().modify(|w| {\n            // allow counting during debug\n            w.set_repeat(Repeat::REPEAT_3);\n            w.set_cvae(Cvae::ZEROVAL);\n            w.set_cm(Cm::UP);\n\n            // Must explicitly set CZC, CAC and CLC to 0 in order for all the timers to count.\n            //\n            // The reset value of these registers is 0x07, which is a reserved value.\n            //\n            // Looking at a bit representation of the reset value, this appears to be an AND\n            // of 2-input QEI mode and CCCTL_3 ACOND. Given that TIMG14 and TIMA0 have no QEI\n            // and 4 capture and compare channels, this works by accident for those timer units.\n            w.set_czc(CxC::CCTL0);\n            w.set_cac(CxC::CCTL0);\n            w.set_clc(CxC::CCTL0);\n        });\n\n        // Middle\n        regs.counterregs(0).cc(0).write_value(0x7FFF as u32);\n        regs.counterregs(0).load().write_value(u16::MAX as u32);\n\n        // Enable the period interrupts\n        //\n        // This does not appear to ever be set for CPU_INT in the TI SDK and is not technically needed.\n        regs.evt_mode().modify(|w| {\n            w.set_evt_cfg(0, EvtCfg::SOFTWARE);\n        });\n\n        regs.cpu_int(0).imask().modify(|w| {\n            w.set_l(true);\n            w.set_ccu0(true);\n        });\n\n        unsafe { T::enable_interrupt() };\n\n        // Allow the counter to start counting.\n        regs.counterregs(0).ctrctl().modify(|w| {\n            w.set_en(true);\n        });\n    }\n\n    #[inline(never)]\n    fn next_period(&self) {\n        let r = regs();\n\n        // We only modify the period from the timer interrupt, so we know this can't race.\n        let period = self.period.load(Ordering::Relaxed) + 1;\n        self.period.store(period, Ordering::Relaxed);\n        let t = (period as u64) << 15;\n\n        critical_section::with(move |cs| {\n            r.cpu_int(0).imask().modify(move |w| {\n                let alarm = self.alarm.borrow(cs);\n                let at = alarm.get();\n\n                if at < t + 0xC000 {\n                    // just enable it. `set_alarm` has already set the correct CC1 val.\n                    w.set_ccu1(true);\n                }\n            })\n        });\n    }\n\n    #[inline(never)]\n    fn on_interrupt(&self) {\n        let r = regs();\n\n        critical_section::with(|cs| {\n            let mis = r.cpu_int(0).mis().read();\n\n            // Advance to next period if overflowed\n            if mis.l() {\n                self.next_period();\n\n                r.cpu_int(0).iclr().write(|w| {\n                    w.set_l(true);\n                });\n            }\n\n            if mis.ccu0() {\n                self.next_period();\n\n                r.cpu_int(0).iclr().write(|w| {\n                    w.set_ccu0(true);\n                });\n            }\n\n            if mis.ccu1() {\n                r.cpu_int(0).iclr().write(|w| {\n                    w.set_ccu1(true);\n                });\n\n                self.trigger_alarm(cs);\n            }\n        });\n    }\n\n    fn trigger_alarm(&self, cs: CriticalSection) {\n        let mut next = self.queue.borrow(cs).borrow_mut().next_expiration(self.now());\n\n        while !self.set_alarm(cs, next) {\n            next = self.queue.borrow(cs).borrow_mut().next_expiration(self.now());\n        }\n    }\n\n    fn set_alarm(&self, cs: CriticalSection, timestamp: u64) -> bool {\n        let r = regs();\n\n        self.alarm.borrow(cs).set(timestamp);\n\n        let t = self.now();\n\n        if timestamp <= t {\n            // If alarm timestamp has passed the alarm will not fire.\n            // Disarm the alarm and return `false` to indicate that.\n            r.cpu_int(0).imask().modify(|w| w.set_ccu1(false));\n\n            self.alarm.borrow(cs).set(u64::MAX);\n\n            return false;\n        }\n\n        // Write the CC1 value regardless of whether we're going to enable it now or not.\n        // This way, when we enable it later, the right value is already set.\n        //\n        // Cast to u16 and then u32 to clamp to 16-bit timer limits.\n        r.counterregs(0).cc(1).write_value(timestamp as u16 as u32);\n\n        // Enable it if it'll happen soon. Otherwise, `next_period` will enable it.\n        let diff = timestamp - t;\n        r.cpu_int(0).imask().modify(|w| w.set_ccu1(diff < 0xC000));\n\n        // Reevaluate if the alarm timestamp is still in the future\n        let t = self.now();\n        if timestamp <= t {\n            // If alarm timestamp has passed since we set it, we have a race condition and\n            // the alarm may or may not have fired.\n            // Disarm the alarm and return `false` to indicate that.\n            // It is the caller's responsibility to handle this ambiguity.\n            r.cpu_int(0).imask().modify(|w| w.set_ccu1(false));\n\n            self.alarm.borrow(cs).set(u64::MAX);\n\n            return false;\n        }\n\n        // We're confident the alarm will ring in the future.\n        true\n    }\n}\n\nimpl Driver for TimxDriver {\n    fn now(&self) -> u64 {\n        let regs = regs();\n\n        let period = self.period.load(Ordering::Relaxed);\n        // Ensure the compiler does not read the counter before the period.\n        compiler_fence(Ordering::Acquire);\n\n        let counter = regs.counterregs(0).ctr().read() as u16;\n\n        calc_now(period, counter)\n    }\n\n    fn schedule_wake(&self, at: u64, waker: &Waker) {\n        critical_section::with(|cs| {\n            let mut queue = self.queue.borrow(cs).borrow_mut();\n\n            if queue.schedule_wake(at, waker) {\n                let mut next = queue.next_expiration(self.now());\n\n                while !self.set_alarm(cs, next) {\n                    next = queue.next_expiration(self.now());\n                }\n            }\n        });\n    }\n}\n\nembassy_time_driver::time_driver_impl!(static DRIVER: TimxDriver = TimxDriver {\n    period: AtomicU32::new(0),\n    alarm: Mutex::new(Cell::new(u64::MAX)),\n    queue: Mutex::new(RefCell::new(Queue::new()))\n});\n\npub(crate) fn init(cs: CriticalSection) {\n    DRIVER.init(cs);\n}\n\n#[cfg(time_driver_timg0)]\n#[interrupt]\nfn TIMG0() {\n    DRIVER.on_interrupt();\n}\n\n#[cfg(time_driver_timg1)]\n#[interrupt]\nfn TIMG1() {\n    DRIVER.on_interrupt();\n}\n\n#[cfg(time_driver_timg2)]\n#[interrupt]\nfn TIMG2() {\n    DRIVER.on_interrupt();\n}\n\n#[cfg(time_driver_timg3)]\n#[interrupt]\nfn TIMG3() {\n    DRIVER.on_interrupt();\n}\n\n#[cfg(time_driver_timg4)]\n#[interrupt]\nfn TIMG4() {\n    DRIVER.on_interrupt();\n}\n\n#[cfg(time_driver_timg5)]\n#[interrupt]\nfn TIMG5() {\n    DRIVER.on_interrupt();\n}\n\n#[cfg(time_driver_timg6)]\n#[interrupt]\nfn TIMG6() {\n    DRIVER.on_interrupt();\n}\n\n#[cfg(time_driver_timg7)]\n#[interrupt]\nfn TIMG7() {\n    DRIVER.on_interrupt();\n}\n\n#[cfg(time_driver_timg8)]\n#[interrupt]\nfn TIMG8() {\n    DRIVER.on_interrupt();\n}\n\n#[cfg(time_driver_timg9)]\n#[interrupt]\nfn TIMG9() {\n    DRIVER.on_interrupt();\n}\n\n#[cfg(time_driver_timg10)]\n#[interrupt]\nfn TIMG10() {\n    DRIVER.on_interrupt();\n}\n\n#[cfg(time_driver_timg11)]\n#[interrupt]\nfn TIMG11() {\n    DRIVER.on_interrupt();\n}\n\n// TODO: TIMG12 and TIMG13\n\n#[cfg(time_driver_timg14)]\n#[interrupt]\nfn TIMG14() {\n    DRIVER.on_interrupt();\n}\n\n#[cfg(time_driver_tima0)]\n#[interrupt]\nfn TIMA0() {\n    DRIVER.on_interrupt();\n}\n\n#[cfg(time_driver_tima1)]\n#[interrupt]\nfn TIMA1() {\n    DRIVER.on_interrupt();\n}\n"
  },
  {
    "path": "embassy-mspm0/src/timer.rs",
    "content": "#![macro_use]\n\n/// Amount of bits of a timer.\n#[derive(Debug, Clone, Copy, PartialEq, Eq)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub enum TimerBits {\n    /// 16 bits.\n    Bits16,\n    /// 32 bits.\n    Bits32,\n}\n\n#[allow(private_bounds)]\npub trait Timer: SealedTimer + 'static {\n    /// Amount of bits this timer has.\n    const BITS: TimerBits;\n}\n\npub(crate) trait SealedTimer {\n    /// Registers for this timer.\n    ///\n    /// This is a raw pointer to the register block. The actual register block layout varies depending on the\n    /// timer type.\n    fn regs() -> *mut ();\n\n    /// Enable the interrupt corresponding to this timer.\n    unsafe fn enable_interrupt();\n}\n\nmacro_rules! impl_timer {\n    ($name: ident, $bits: ident) => {\n        impl crate::timer::SealedTimer for crate::peripherals::$name {\n            fn regs() -> *mut () {\n                crate::pac::$name.as_ptr()\n            }\n\n            unsafe fn enable_interrupt() {\n                use embassy_hal_internal::interrupt::InterruptExt;\n                crate::interrupt::$name.unpend();\n                crate::interrupt::$name.enable();\n            }\n        }\n\n        impl crate::timer::Timer for crate::peripherals::$name {\n            const BITS: crate::timer::TimerBits = crate::timer::TimerBits::$bits;\n        }\n    };\n}\n"
  },
  {
    "path": "embassy-mspm0/src/trng.rs",
    "content": "//! True Random Number Generator (TRNG) driver.\nuse core::fmt::Display;\nuse core::future::poll_fn;\nuse core::marker::PhantomData;\nuse core::task::Poll;\n\nuse cortex_m::asm;\nuse embassy_hal_internal::Peri;\nuse embassy_sync::waitqueue::AtomicWaker;\nuse mspm0_metapac::trng::regs::Int;\nuse mspm0_metapac::trng::vals::Cmd::*;\nuse mspm0_metapac::trng::vals::{self, PwrenKey, Ratio, RstctlKey};\nuse rand_core::{TryCryptoRng, TryRngCore};\n\nuse crate::peripherals::TRNG;\nuse crate::sealed;\n\nstatic WAKER: AtomicWaker = AtomicWaker::new();\n\n/// Decimation rate marker types. See [`DecimRate`].\npub trait SecurityMarker: sealed::Sealed {\n    type DecimRate: DecimRate;\n}\n\n/// Marker type for cryptographic decimation rate. See [`CryptoDecimRate`].\npub struct Crypto;\n\nimpl sealed::Sealed for Crypto {}\nimpl SecurityMarker for Crypto {\n    type DecimRate = CryptoDecimRate;\n}\n\n/// Marker type for fast decimation rate. See [`FastDecimRate`].\npub struct Fast;\n\nimpl sealed::Sealed for Fast {}\nimpl SecurityMarker for Fast {\n    type DecimRate = FastDecimRate;\n}\n\n/// The decimation rate settings for the TRNG.\n/// Higher decimation rates improve the quality of the random numbers at the cost of speed.\n///\n/// L-series TRM 13.2.2: It is required to use a decimation rate of at least 4 for cryptographic applications.\n///\n/// See [`FastDecimRate`] and [`CryptoDecimRate`] for available options.\npub trait DecimRate: sealed::Sealed + Into<vals::DecimRate> + Copy {}\n\n/// Fast decimation rates for non-cryptographic applications.\n/// See [`DecimRate`].\n#[derive(Clone, Copy, Debug, PartialEq, Eq)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub enum FastDecimRate {\n    Decim1,\n    Decim2,\n    Decim3,\n}\n\nimpl sealed::Sealed for FastDecimRate {}\nimpl Into<vals::DecimRate> for FastDecimRate {\n    fn into(self) -> vals::DecimRate {\n        match self {\n            Self::Decim1 => vals::DecimRate::DECIM_1,\n            Self::Decim2 => vals::DecimRate::DECIM_2,\n            Self::Decim3 => vals::DecimRate::DECIM_3,\n        }\n    }\n}\nimpl DecimRate for FastDecimRate {}\n\n/// Cryptographic decimation rates for cryptographic applications.\n/// See [`DecimRate`].\n#[derive(Clone, Copy, Debug, PartialEq, Eq)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub enum CryptoDecimRate {\n    Decim4,\n    Decim5,\n    Decim6,\n    Decim7,\n    Decim8,\n}\n\nimpl sealed::Sealed for CryptoDecimRate {}\nimpl Into<vals::DecimRate> for CryptoDecimRate {\n    fn into(self) -> vals::DecimRate {\n        match self {\n            Self::Decim4 => vals::DecimRate::DECIM_4,\n            Self::Decim5 => vals::DecimRate::DECIM_5,\n            Self::Decim6 => vals::DecimRate::DECIM_6,\n            Self::Decim7 => vals::DecimRate::DECIM_7,\n            Self::Decim8 => vals::DecimRate::DECIM_8,\n        }\n    }\n}\nimpl DecimRate for CryptoDecimRate {}\n\n/// Represents errors that can arise during initialization with [`Trng::new`] or reading a value from the [`Trng`].\n#[derive(Debug)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub enum Error {\n    /// L-series TRM\n    /// The digital startup health test is run by application software when powering up the TRNG module.\n    /// This built-in self-test verifies that the digital block is functioning properly by running predefined sequences of digital samples through the complete digital block while checking for expected outputs.\n    /// The test sequence includes eight tests (indexed 0-7.)\n    ///\n    /// This failure indicates an irrecoverable fault in the digital block detected during startup.\n    DigitalSelfTestFailed(u8),\n    /// L-series TRM 13.2.4.2:\n    /// The analog startup health test is run by application software when powering up the TRNG module.\n    /// This test verifies that the analog block is functioning properly by capturing 4,096 consecutive analog samples and verifying that the samples pass a health test.\n    ///\n    /// The driver only raises this error after three subsequent failures during initialization.\n    AnalogSelfTestFailed,\n    /// L-series TRM 13.2.4.3.1:\n    /// The repetition count test quickly detects failures that cause the entropy source to remain stuck on a single output value for an extended period of time.\n    /// The repetition count test fails if the entropy source outputs the same bit value for 135 consecutive samples.\n    ///\n    /// <div class=\"warning\"> This error may occur by chance and can be retried up to two additional times after calling <a href=\"struct.Trng.html#method.fail_reset\"><code>Trng::fail_reset</code></a>. </div>\n    RepetitionCountTestFailed,\n    /// L-series TRM 13.2.4.3.2:\n    /// The adaptive proportion test detects failures that cause a disproportionate number of samples to be the same bit value and/or bit pattern over a window of 1024 samples.\n    /// The adaptive proportion test fails if any of the conditions in Table 13-1 are violated.\n    ///\n    /// <div class=\"warning\"> This error may occur by chance and can be retried up to two additional times after calling <a href=\"struct.Trng.html#method.fail_reset\"><code>Trng::fail_reset</code></a>. </div>\n    AdaptiveProportionTestFailed,\n}\n\nimpl Display for Error {\n    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {\n        match self {\n            Self::DigitalSelfTestFailed(n) => write!(f, \"Digital startup self-test (index: {n}) failed\"),\n            Self::AnalogSelfTestFailed => write!(f, \"Analog startup self-test failed\"),\n            Self::RepetitionCountTestFailed => write!(f, \"Repetition count runtime self-test failed\"),\n            Self::AdaptiveProportionTestFailed => write!(f, \"Adaptive proportion runtime self-test failed\"),\n        }\n    }\n}\n\nimpl core::error::Error for Error {}\n\n/// True Random Number Generator (TRNG) Driver for MSPM0 series.\n///\n/// The driver provides blocking random numbers with [`TryRngCore`] methods and asynchronous counterparts in [`Trng::async_read_u32`], [`Trng::async_read_u64`], and [`Trng::async_read_bytes`].\n///\n/// The TRNG can be configured with different decimation rates. See [`DecimRate`], [`FastDecimRate`], and [`CryptoDecimRate`].\n/// The TRNG can be instantiated with [`Trng::new`], [`Trng::new_fast`], or [`Trng::new_secure`].\n///\n/// Usage example:\n/// ```no_run\n/// #![no_std]\n/// #![no_main]\n///\n/// use embassy_executor::Spawner;\n/// use embassy_mspm0::Config;\n/// use embassy_mspm0::trng::Trng;\n/// use rand_core::TryRngCore;\n/// use {defmt_rtt as _, panic_halt as _};\n///\n/// #[embassy_executor::main]\n/// async fn main(_spawner: Spawner) -> ! {\n///     let p = embassy_mspm0::init(Config::default());\n///     let mut trng = Trng::new(p.TRNG).expect(\"Failed to initialize TRNG\");\n///     let mut randomness = [0u8; 16];\n///\n///     loop {\n///         trng.fill_bytes(&mut randomness).unwrap();\n///         assert_ne!(randomness, [0u8; 16]);\n///     }\n/// }\n/// ```\npub struct Trng<'d, L: SecurityMarker> {\n    inner: TrngInner<'d>,\n    _security_level: PhantomData<L>,\n}\n\nimpl<'d> Trng<'d, Crypto> {\n    /// Setup a TRNG driver with the default safe decimation rate (Decim4).\n    #[inline(always)]\n    pub fn new(peripheral: Peri<'d, TRNG>) -> Result<Self, Error> {\n        Self::new_secure(peripheral, CryptoDecimRate::Decim4)\n    }\n}\n\n// Split off new methods to allow type inference when providing the decimation rate.\nimpl<'d> Trng<'d, Fast> {\n    /// Setup a TRNG driver with a specific fast decimation rate.\n    ///\n    /// <div class=\"warning\"> The created TRNG is not suitable for cryptography. Use <a href=\"struct.Trng.html#method.new_secure\"><code>Trng::new_secure</code></a> instead. </div>\n    #[inline(always)]\n    pub fn new_fast(peripheral: Peri<'d, TRNG>, rate: FastDecimRate) -> Result<Self, Error> {\n        Self::new_with_rate(peripheral, rate)\n    }\n}\n\nimpl<'d> Trng<'d, Crypto> {\n    /// Setup a TRNG driver with a specific cryptographic decimation rate.\n    #[inline(always)]\n    pub fn new_secure(peripheral: Peri<'d, TRNG>, rate: CryptoDecimRate) -> Result<Self, Error> {\n        Self::new_with_rate(peripheral, rate)\n    }\n}\n\nimpl<'d, D: SecurityMarker> Trng<'d, D> {\n    #[inline(always)]\n    fn new_with_rate(_peripheral: Peri<'d, TRNG>, rate: D::DecimRate) -> Result<Self, Error> {\n        Ok(Self {\n            inner: TrngInner::new(rate.into())?,\n            _security_level: PhantomData,\n        })\n    }\n\n    /// L-series TRM 13.2.5.2(10): procedure for recovering from a failed read.\n    ///\n    /// This method reinitializes the TRNG which may take some time to complete.\n    #[inline(always)]\n    pub fn fail_reset(&mut self) -> Result<(), Error> {\n        self.inner.fail_reset()\n    }\n\n    /// Asynchronously read a 32-bit random value from the TRNG.\n    ///\n    /// The synchronous counterpart is given by [`TryRngCore::try_next_u32`].\n    ///\n    /// As with the [`synchronous`](TryRngCore) methods, an [`Err`] may be retried up to two times after calling [`Trng::fail_reset`].\n    #[cfg(feature = \"rt\")]\n    #[inline(always)]\n    pub async fn async_read_u32(&mut self) -> Result<u32, Error> {\n        self.inner.async_read_u32().await\n    }\n\n    /// Asynchronously read a 64-bit random value from the TRNG.\n    ///\n    /// The synchronous counterpart is given by [`TryRngCore::try_next_u64`].\n    ///\n    /// As with the [`synchronous`](TryRngCore) methods, an [`Err`] may be retried up to two times after calling [`Trng::fail_reset`].\n    #[cfg(feature = \"rt\")]\n    #[inline(always)]\n    pub async fn async_read_u64(&mut self) -> Result<u64, Error> {\n        self.inner.async_read_u64().await\n    }\n\n    /// Asynchronously fill `dest` with random bytes from the TRNG.\n    ///\n    /// The synchronous counterpart is given by [`TryRngCore::try_fill_bytes`].\n    ///\n    /// As with the [`synchronous`](TryRngCore) methods, an [`Err`] may be retried up to two times after calling [`Trng::fail_reset`].\n    ///\n    /// > **Note**\n    /// When an error condition occurs, the buffer may be partially filled.\n    #[cfg(feature = \"rt\")]\n    #[inline(always)]\n    pub async fn async_read_bytes(&mut self, dest: &mut [u8]) -> Result<(), Error> {\n        self.inner.async_read_bytes(dest).await\n    }\n}\n\n/// Implements the fallible [`TryRngCore`].\n///\n/// If any of the methods give an [`Err`], the operation may be retried up to two times after calling [`Trng::fail_reset`].\nimpl<D: SecurityMarker> TryRngCore for Trng<'_, D> {\n    type Error = Error;\n\n    #[inline(always)]\n    fn try_next_u32(&mut self) -> Result<u32, Error> {\n        self.inner.try_next_u32()\n    }\n\n    #[inline(always)]\n    fn try_next_u64(&mut self) -> Result<u64, Error> {\n        self.inner.try_next_u64()\n    }\n\n    /// > **Note**\n    /// When an error condition occurs, the buffer may be partially filled.\n    #[inline(always)]\n    fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), Error> {\n        self.inner.try_fill_bytes(dest)\n    }\n}\n\nimpl TryCryptoRng for Trng<'_, Crypto> {}\n\n// TODO: Replace this when the MCLK rate can be adjusted.\n#[cfg(any(mspm0c110x, mspm0c1105_c1106))]\nfn get_mclk_frequency() -> u32 {\n    24_000_000\n}\n\n// TODO: Replace this when the MCLK rate can be adjusted.\n#[cfg(any(\n    mspm0g110x, mspm0g150x, mspm0g151x, mspm0g310x, mspm0g350x, mspm0g351x, mspm0h321x, mspm0l110x, mspm0l122x,\n    mspm0l130x, mspm0l134x, mspm0l222x\n))]\nfn get_mclk_frequency() -> u32 {\n    32_000_000\n}\n\n// Inner TRNG driver implementation. Used to reduce monomorphization bloat.\nstruct TrngInner<'d> {\n    decim_rate: vals::DecimRate,\n    _phantom: PhantomData<&'d ()>,\n}\n\nimpl TrngInner<'_> {\n    fn new(decim_rate: vals::DecimRate) -> Result<Self, Error> {\n        let mut trng = TrngInner {\n            decim_rate: decim_rate,\n            _phantom: PhantomData,\n        };\n\n        trng.reset();\n        // L-series TRM 13.2.5.2\n        trng.power_on(); // 1. Power on the TRNG peripheral.\n        asm::delay(16); // Delay 16 cycles for power-on.\n        trng.init()?; // 2-8. Initialize the TRNG.\n        Ok(trng)\n    }\n\n    fn fail_reset(&mut self) -> Result<(), Error> {\n        regs().iclr().write(|w| w.set_irq_health_fail(true));\n        self.set_cmd(PWR_OFF);\n        self.init()\n    }\n\n    fn init(&mut self) -> Result<(), Error> {\n        // L-series TRM 13.2.5.2\n        self.set_div(); // 2. Set the clock divider.\n        regs().imask().write_value(Int::default()); // 3. Disable all interrupts.\n        self.set_cmd(NORM_FUNC); // 4. Set to normal function mode.\n        self.dig_test()?; // 5. Perform digital block start-up self-tests.\n        self.ana_test()?; // 6. Perform analog block start-up self-test.\n        self.clr_rdy(); // 7.a Clear IRQ_CAPTURED_RDY_IRQ.\n        self.set_decim_rate(); // 7.b Set decimation rate.\n        self.set_cmd(NORM_FUNC); // 7.b Set to normal function mode again after changing decimation rate.\n        _ = self.read(); // 8. By 13.2.4.1, must discard first value.\n        Ok(())\n    }\n\n    fn reset(&mut self) {\n        regs().gprcm().rstctl().write(|w| {\n            w.set_key(RstctlKey::KEY);\n            w.set_resetassert(true);\n        });\n    }\n\n    fn power_on(&mut self) {\n        regs().gprcm().pwren().write(|w| {\n            w.set_key(PwrenKey::KEY);\n            w.set_enable(true);\n        });\n    }\n\n    fn power_off(&mut self) {\n        regs().gprcm().pwren().write(|w| w.set_key(PwrenKey::KEY));\n    }\n\n    fn set_div(&mut self) {\n        // L-series TRM 13.2.2: The TRNG is derived from MCLK. Datasheets specify 9.5-20 MHz range.\n        let freq = get_mclk_frequency();\n        let ratio = if freq > 160_000_000 {\n            panic!(\"MCLK frequency {} > 160 MHz is not compatible with the TRNG\", freq)\n        } else if freq >= 80_000_000 {\n            Ratio::DIV_BY_8\n        } else if freq >= 60_000_000 {\n            Ratio::DIV_BY_6\n        } else if freq >= 40_000_000 {\n            Ratio::DIV_BY_4\n        } else if freq >= 20_000_000 {\n            Ratio::DIV_BY_2\n        } else if freq >= 9_500_000 {\n            Ratio::DIV_BY_1\n        } else {\n            panic!(\"MCLK frequency {} < 9.5 MHz is not compatible with the TRNG\", freq)\n        };\n        regs().clkdiv().write(|w| w.set_ratio(ratio));\n    }\n\n    fn set_decim_rate(&mut self) {\n        regs().ctl().modify(|w| w.set_decim_rate(self.decim_rate));\n    }\n\n    fn clr_rdy(&mut self) {\n        regs().iclr().write(|w| w.set_irq_captured_rdy(true));\n    }\n\n    fn set_cmd(&mut self, cmd: vals::Cmd) {\n        regs().iclr().write(|w| w.set_irq_cmd_done(true));\n        regs().ctl().modify(|w| w.set_cmd(cmd));\n        while !regs().ris().read().irq_cmd_done() {}\n    }\n\n    fn dig_test(&mut self) -> Result<(), Error> {\n        self.set_cmd(PWRUP_DIG);\n        let results = regs().test_results().read();\n        for n in 0..8u8 {\n            // 13.2.4.1: Digital tests must pass.\n            if !results.dig_test(n as usize) {\n                return Err(Error::DigitalSelfTestFailed(n));\n            }\n        }\n        Ok(())\n    }\n\n    fn ana_test(&mut self) -> Result<(), Error> {\n        // 13.2.4.2: Analog tests have a small chance to fail, so try up to 3 times.\n        for _ in 0..3 {\n            self.set_cmd(PWRUP_ANA);\n            let results = regs().test_results().read();\n            if !results.ana_test() {\n                self.set_cmd(PWR_OFF);\n            } else {\n                return Ok(());\n            }\n        }\n        Err(Error::AnalogSelfTestFailed)\n    }\n\n    fn poll(&mut self) -> Poll<Result<u32, Error>> {\n        let ris = regs().ris().read();\n        if ris.irq_health_fail() {\n            // 10. The TRNG failed the health check.\n            let stat = regs().stat().read();\n            if stat.adap_fail() {\n                Poll::Ready(Err(Error::AdaptiveProportionTestFailed))\n            } else if stat.rep_fail() {\n                Poll::Ready(Err(Error::RepetitionCountTestFailed))\n            } else {\n                unreachable!(\"Unexpected TRNG health failure type\")\n            }\n        } else if ris.irq_captured_rdy() {\n            // 9. When data is ready, read it.\n            self.clr_rdy();\n            let data = regs().data_capture().read();\n            Poll::Ready(Ok(data))\n        } else {\n            Poll::Pending\n        }\n    }\n\n    fn read(&mut self) -> Result<u32, Error> {\n        loop {\n            if let Poll::Ready(r) = self.poll() {\n                return r;\n            }\n        }\n    }\n\n    #[cfg(feature = \"rt\")]\n    async fn async_read_u32(&mut self) -> Result<u32, Error> {\n        poll_fn(|cx| {\n            WAKER.register(cx.waker());\n            let result = self.poll();\n            if result.is_pending() {\n                // Enable interrupts\n                regs().imask().write(|w| {\n                    w.set_irq_captured_rdy(true);\n                    w.set_irq_health_fail(true);\n                });\n            }\n            result\n        })\n        .await\n    }\n\n    #[cfg(feature = \"rt\")]\n    async fn async_read_u64(&mut self) -> Result<u64, Error> {\n        let v1 = u64::from(self.async_read_u32().await?);\n        let v2 = u64::from(self.async_read_u32().await?);\n        Ok(v2 << 32 | v1)\n    }\n\n    #[cfg(feature = \"rt\")]\n    async fn async_read_bytes(&mut self, dest: &mut [u8]) -> Result<(), Error> {\n        let mut left = dest;\n        while left.len() >= 4 {\n            let (l, r) = left.split_at_mut(4);\n            left = r;\n            let chunk = self.async_read_u32().await?.to_ne_bytes();\n            l.copy_from_slice(&chunk);\n        }\n        if !left.is_empty() {\n            let chunk = self.async_read_u32().await?.to_ne_bytes();\n            left.copy_from_slice(&chunk[..left.len()]);\n        }\n        Ok(())\n    }\n}\n\nimpl Drop for TrngInner<'_> {\n    fn drop(&mut self) {\n        regs().imask().write_value(Int::default()); // Disable all interrupts.\n        self.power_off();\n    }\n}\n\nimpl TryRngCore for TrngInner<'_> {\n    type Error = Error;\n\n    fn try_next_u32(&mut self) -> Result<u32, Error> {\n        self.read()\n    }\n\n    fn try_next_u64(&mut self) -> Result<u64, Error> {\n        let v1 = u64::from(self.try_next_u32()?);\n        let v2 = u64::from(self.try_next_u32()?);\n        Ok(v2 << 32 | v1)\n    }\n\n    /// > **Note**\n    /// When an error condition occurs, the buffer may be partially filled.\n    fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), Error> {\n        let mut left = dest;\n        while left.len() >= 4 {\n            let (l, r) = left.split_at_mut(4);\n            left = r;\n            let chunk = self.try_next_u32()?.to_ne_bytes();\n            l.copy_from_slice(&chunk);\n        }\n        if !left.is_empty() {\n            let chunk = self.try_next_u32()?.to_ne_bytes();\n            left.copy_from_slice(&chunk[..left.len()]);\n        }\n        Ok(())\n    }\n}\n\nfn regs() -> crate::pac::trng::Trng {\n    crate::pac::TRNG\n}\n\n// This symbol is weakly defined as DefaultHandler and is called by the interrupt group implementation.\n// Defining this as no_mangle is required so that the linker will pick this up.\n#[cfg(feature = \"rt\")]\n#[unsafe(no_mangle)]\n#[allow(non_snake_case)]\nfn TRNG() {\n    regs().imask().write_value(Int::default()); // Disable all interrupts.\n    WAKER.wake();\n}\n"
  },
  {
    "path": "embassy-mspm0/src/uart/buffered.rs",
    "content": "use core::future::{Future, poll_fn};\nuse core::marker::PhantomData;\nuse core::slice;\nuse core::sync::atomic::{AtomicU8, Ordering};\nuse core::task::Poll;\n\nuse embassy_embedded_hal::SetConfig;\nuse embassy_hal_internal::atomic_ring_buffer::RingBuffer;\nuse embassy_hal_internal::interrupt::InterruptExt;\nuse embassy_sync::waitqueue::AtomicWaker;\nuse embedded_hal_nb::nb;\n\nuse crate::gpio::{AnyPin, SealedPin};\nuse crate::interrupt::typelevel::Binding;\nuse crate::pac::uart::Uart as Regs;\nuse crate::uart::{Config, ConfigError, CtsPin, Error, Info, Instance, RtsPin, RxPin, State, TxPin};\nuse crate::{Peri, interrupt};\n\n/// Interrupt handler.\npub struct BufferedInterruptHandler<T: Instance> {\n    _uart: PhantomData<T>,\n}\n\nimpl<T: Instance> interrupt::typelevel::Handler<T::Interrupt> for BufferedInterruptHandler<T> {\n    unsafe fn on_interrupt() {\n        on_interrupt(T::info().regs, T::buffered_state())\n    }\n}\n\n/// Bidirectional buffered UART which acts as a combination of [`BufferedUartTx`] and [`BufferedUartRx`].\npub struct BufferedUart<'d> {\n    rx: BufferedUartRx<'d>,\n    tx: BufferedUartTx<'d>,\n}\n\nimpl SetConfig for BufferedUart<'_> {\n    type Config = Config;\n    type ConfigError = ConfigError;\n\n    fn set_config(&mut self, config: &Self::Config) -> Result<(), Self::ConfigError> {\n        self.set_config(config)\n    }\n}\n\nimpl<'d> BufferedUart<'d> {\n    /// Create a new bidirectional buffered UART.\n    pub fn new<T: Instance>(\n        uart: Peri<'d, T>,\n        tx: Peri<'d, impl TxPin<T>>,\n        rx: Peri<'d, impl RxPin<T>>,\n        _irq: impl Binding<T::Interrupt, BufferedInterruptHandler<T>>,\n        tx_buffer: &'d mut [u8],\n        rx_buffer: &'d mut [u8],\n        config: Config,\n    ) -> Result<Self, ConfigError> {\n        Self::new_inner(\n            uart,\n            new_pin!(rx, config.rx_pf()),\n            new_pin!(tx, config.tx_pf()),\n            None,\n            None,\n            tx_buffer,\n            rx_buffer,\n            config,\n        )\n    }\n\n    /// Create a new bidirectional buffered UART with request-to-send and clear-to-send pins\n    pub fn new_with_rtscts<T: Instance>(\n        uart: Peri<'d, T>,\n        tx: Peri<'d, impl TxPin<T>>,\n        rx: Peri<'d, impl RxPin<T>>,\n        rts: Peri<'d, impl RtsPin<T>>,\n        cts: Peri<'d, impl CtsPin<T>>,\n        _irq: impl Binding<T::Interrupt, BufferedInterruptHandler<T>>,\n        tx_buffer: &'d mut [u8],\n        rx_buffer: &'d mut [u8],\n        config: Config,\n    ) -> Result<Self, ConfigError> {\n        Self::new_inner(\n            uart,\n            new_pin!(rx, config.rx_pf()),\n            new_pin!(tx, config.tx_pf()),\n            new_pin!(rts, config.rts_pf()),\n            new_pin!(cts, config.cts_pf()),\n            tx_buffer,\n            rx_buffer,\n            config,\n        )\n    }\n\n    /// Reconfigure the driver\n    pub fn set_config(&mut self, config: &Config) -> Result<(), ConfigError> {\n        self.tx.set_config(config)?;\n        self.rx.set_config(config)\n    }\n\n    /// Set baudrate\n    pub fn set_baudrate(&mut self, baudrate: u32) -> Result<(), ConfigError> {\n        self.rx.set_baudrate(baudrate)\n    }\n\n    /// Write to UART TX buffer, blocking execution until done.\n    pub fn blocking_write(&mut self, buffer: &[u8]) -> Result<usize, Error> {\n        self.tx.blocking_write(buffer)\n    }\n\n    /// Flush UART TX buffer, blocking execution until done.\n    pub fn blocking_flush(&mut self) -> Result<(), Error> {\n        self.tx.blocking_flush()\n    }\n\n    /// Check if UART is busy.\n    pub fn busy(&self) -> bool {\n        self.tx.busy()\n    }\n\n    /// Read from UART RX buffer, blocking execution until done.\n    pub fn blocking_read(&mut self, buffer: &mut [u8]) -> Result<usize, Error> {\n        self.rx.blocking_read(buffer)\n    }\n\n    /// Send break character.\n    pub fn send_break(&mut self) {\n        self.tx.send_break()\n    }\n\n    /// Split into separate RX and TX handles.\n    pub fn split(self) -> (BufferedUartTx<'d>, BufferedUartRx<'d>) {\n        (self.tx, self.rx)\n    }\n\n    /// Split into separate RX and TX handles.\n    pub fn split_ref(&mut self) -> (BufferedUartTx<'_>, BufferedUartRx<'_>) {\n        (\n            BufferedUartTx {\n                info: self.tx.info,\n                state: self.tx.state,\n                tx: self.tx.tx.as_mut().map(Peri::reborrow),\n                cts: self.tx.cts.as_mut().map(Peri::reborrow),\n                reborrowed: true,\n            },\n            BufferedUartRx {\n                info: self.rx.info,\n                state: self.rx.state,\n                rx: self.rx.rx.as_mut().map(Peri::reborrow),\n                rts: self.rx.rts.as_mut().map(Peri::reborrow),\n                reborrowed: true,\n            },\n        )\n    }\n}\n\n/// Rx-only buffered UART.\n///\n/// Can be obtained from [`BufferedUart::split`], or can be constructed independently,\n/// if you do not need the transmitting half of the driver.\npub struct BufferedUartRx<'d> {\n    info: &'static Info,\n    state: &'static BufferedState,\n    rx: Option<Peri<'d, AnyPin>>,\n    rts: Option<Peri<'d, AnyPin>>,\n    reborrowed: bool,\n}\n\nimpl SetConfig for BufferedUartRx<'_> {\n    type Config = Config;\n    type ConfigError = ConfigError;\n\n    fn set_config(&mut self, config: &Self::Config) -> Result<(), Self::ConfigError> {\n        self.set_config(config)\n    }\n}\n\nimpl<'d> BufferedUartRx<'d> {\n    /// Create a new rx-only buffered UART with no hardware flow control.\n    ///\n    /// Useful if you only want Uart Rx. It saves 1 pin.\n    pub fn new<T: Instance>(\n        uart: Peri<'d, T>,\n        rx: Peri<'d, impl RxPin<T>>,\n        _irq: impl Binding<T::Interrupt, BufferedInterruptHandler<T>>,\n        rx_buffer: &'d mut [u8],\n        config: Config,\n    ) -> Result<Self, ConfigError> {\n        Self::new_inner(uart, new_pin!(rx, config.rx_pf()), None, rx_buffer, config)\n    }\n\n    /// Create a new rx-only buffered UART with a request-to-send pin\n    pub fn new_with_rts<T: Instance>(\n        uart: Peri<'d, T>,\n        rx: Peri<'d, impl RxPin<T>>,\n        rts: Peri<'d, impl RtsPin<T>>,\n        _irq: impl Binding<T::Interrupt, BufferedInterruptHandler<T>>,\n        rx_buffer: &'d mut [u8],\n        config: Config,\n    ) -> Result<Self, ConfigError> {\n        Self::new_inner(\n            uart,\n            new_pin!(rx, config.rx_pf()),\n            new_pin!(rts, config.rts_pf()),\n            rx_buffer,\n            config,\n        )\n    }\n\n    /// Reconfigure the driver\n    pub fn set_config(&mut self, config: &Config) -> Result<(), ConfigError> {\n        if let Some(ref rx) = self.rx {\n            rx.update_pf(config.rx_pf());\n        }\n\n        if let Some(ref rts) = self.rts {\n            rts.update_pf(config.rts_pf());\n        }\n\n        super::reconfigure(&self.info, &self.state.state, config)\n    }\n\n    /// Set baudrate\n    pub fn set_baudrate(&mut self, baudrate: u32) -> Result<(), ConfigError> {\n        super::set_baudrate(&self.info, self.state.state.clock.load(Ordering::Relaxed), baudrate)\n    }\n\n    /// Read from UART RX buffer, blocking execution until done.\n    pub fn blocking_read(&mut self, buffer: &mut [u8]) -> Result<usize, Error> {\n        self.blocking_read_inner(buffer)\n    }\n}\n\nimpl Drop for BufferedUartRx<'_> {\n    fn drop(&mut self) {\n        if !self.reborrowed {\n            let state = self.state;\n\n            // SAFETY: RX is being dropped (and is not reborrowed), so the ring buffer must be deinitialized\n            // in order to meet the requirements of init.\n            unsafe {\n                state.rx_buf.deinit();\n            }\n\n            // TX is inactive if the buffer is not available. If this is true, then disable the\n            // interrupt handler since we are running in RX only mode.\n            if state.tx_buf.len() == 0 {\n                self.info.interrupt.disable();\n            }\n\n            self.rx.as_ref().map(|x| x.set_as_disconnected());\n            self.rts.as_ref().map(|x| x.set_as_disconnected());\n        }\n    }\n}\n\n/// Tx-only buffered UART.\n///\n/// Can be obtained from [`BufferedUart::split`], or can be constructed independently,\n/// if you do not need the receiving half of the driver.\npub struct BufferedUartTx<'d> {\n    info: &'static Info,\n    state: &'static BufferedState,\n    tx: Option<Peri<'d, AnyPin>>,\n    cts: Option<Peri<'d, AnyPin>>,\n    reborrowed: bool,\n}\n\nimpl SetConfig for BufferedUartTx<'_> {\n    type Config = Config;\n    type ConfigError = ConfigError;\n\n    fn set_config(&mut self, config: &Self::Config) -> Result<(), Self::ConfigError> {\n        self.set_config(config)\n    }\n}\n\nimpl<'d> BufferedUartTx<'d> {\n    /// Create a new tx-only buffered UART with no hardware flow control.\n    ///\n    /// Useful if you only want Uart Tx. It saves 1 pin.\n    pub fn new<T: Instance>(\n        uart: Peri<'d, T>,\n        tx: Peri<'d, impl TxPin<T>>,\n        _irq: impl Binding<T::Interrupt, BufferedInterruptHandler<T>>,\n        tx_buffer: &'d mut [u8],\n        config: Config,\n    ) -> Result<Self, ConfigError> {\n        Self::new_inner(uart, new_pin!(tx, config.tx_pf()), None, tx_buffer, config)\n    }\n\n    /// Create a new tx-only buffered UART with a clear-to-send pin\n    pub fn new_with_rts<T: Instance>(\n        uart: Peri<'d, T>,\n        tx: Peri<'d, impl TxPin<T>>,\n        cts: Peri<'d, impl CtsPin<T>>,\n        _irq: impl Binding<T::Interrupt, BufferedInterruptHandler<T>>,\n        tx_buffer: &'d mut [u8],\n        config: Config,\n    ) -> Result<Self, ConfigError> {\n        Self::new_inner(\n            uart,\n            new_pin!(tx, config.tx_pf()),\n            new_pin!(cts, config.cts_pf()),\n            tx_buffer,\n            config,\n        )\n    }\n\n    /// Reconfigure the driver\n    pub fn set_config(&mut self, config: &Config) -> Result<(), ConfigError> {\n        if let Some(ref tx) = self.tx {\n            tx.update_pf(config.tx_pf());\n        }\n\n        if let Some(ref cts) = self.cts {\n            cts.update_pf(config.cts_pf());\n        }\n\n        super::reconfigure(self.info, &self.state.state, config)\n    }\n\n    /// Set baudrate\n    pub fn set_baudrate(&self, baudrate: u32) -> Result<(), ConfigError> {\n        super::set_baudrate(&self.info, self.state.state.clock.load(Ordering::Relaxed), baudrate)\n    }\n\n    /// Write to UART TX buffer, blocking execution until done.\n    pub fn blocking_write(&mut self, buffer: &[u8]) -> Result<usize, Error> {\n        self.blocking_write_inner(buffer)\n    }\n\n    /// Flush UART TX buffer, blocking execution until done.\n    pub fn blocking_flush(&mut self) -> Result<(), Error> {\n        let state = self.state;\n\n        loop {\n            if state.tx_buf.is_empty() {\n                return Ok(());\n            }\n        }\n    }\n\n    /// Check if UART is busy.\n    pub fn busy(&self) -> bool {\n        super::busy(self.info.regs)\n    }\n\n    /// Send break character\n    pub fn send_break(&mut self) {\n        let r = self.info.regs;\n\n        r.lcrh().modify(|w| {\n            w.set_brk(true);\n        });\n    }\n}\n\nimpl Drop for BufferedUartTx<'_> {\n    fn drop(&mut self) {\n        if !self.reborrowed {\n            let state = self.state;\n\n            // SAFETY: TX is being dropped (and is not reborrowed), so the ring buffer must be deinitialized\n            // in order to meet the requirements of init.\n            unsafe {\n                state.tx_buf.deinit();\n            }\n\n            // RX is inactive if the buffer is not available. If this is true, then disable the\n            // interrupt handler since we are running in TX only mode.\n            if state.rx_buf.len() == 0 {\n                self.info.interrupt.disable();\n            }\n\n            self.tx.as_ref().map(|x| x.set_as_disconnected());\n            self.cts.as_ref().map(|x| x.set_as_disconnected());\n        }\n    }\n}\n\nimpl embedded_io_async::ErrorType for BufferedUart<'_> {\n    type Error = Error;\n}\n\nimpl embedded_io_async::ErrorType for BufferedUartRx<'_> {\n    type Error = Error;\n}\n\nimpl embedded_io_async::ErrorType for BufferedUartTx<'_> {\n    type Error = Error;\n}\n\nimpl embedded_io_async::Read for BufferedUart<'_> {\n    async fn read(&mut self, buf: &mut [u8]) -> Result<usize, Self::Error> {\n        self.rx.read(buf).await\n    }\n}\n\nimpl embedded_io_async::Read for BufferedUartRx<'_> {\n    async fn read(&mut self, buf: &mut [u8]) -> Result<usize, Self::Error> {\n        self.read_inner(buf).await\n    }\n}\n\nimpl embedded_io_async::ReadReady for BufferedUart<'_> {\n    fn read_ready(&mut self) -> Result<bool, Self::Error> {\n        self.rx.read_ready()\n    }\n}\n\nimpl embedded_io_async::ReadReady for BufferedUartRx<'_> {\n    fn read_ready(&mut self) -> Result<bool, Self::Error> {\n        self.read_ready_inner()\n    }\n}\n\nimpl embedded_io_async::BufRead for BufferedUart<'_> {\n    async fn fill_buf(&mut self) -> Result<&[u8], Self::Error> {\n        self.rx.fill_buf().await\n    }\n\n    fn consume(&mut self, amt: usize) {\n        self.rx.consume(amt);\n    }\n}\n\nimpl embedded_io_async::BufRead for BufferedUartRx<'_> {\n    async fn fill_buf(&mut self) -> Result<&[u8], Self::Error> {\n        self.fill_buf_inner().await\n    }\n\n    fn consume(&mut self, amt: usize) {\n        self.consume_inner(amt);\n    }\n}\n\nimpl embedded_io_async::Write for BufferedUart<'_> {\n    async fn write(&mut self, buf: &[u8]) -> Result<usize, Self::Error> {\n        self.tx.write_inner(buf).await\n    }\n\n    async fn flush(&mut self) -> Result<(), Self::Error> {\n        self.tx.flush_inner().await\n    }\n}\n\nimpl embedded_io_async::Write for BufferedUartTx<'_> {\n    async fn write(&mut self, buf: &[u8]) -> Result<usize, Self::Error> {\n        self.write_inner(buf).await\n    }\n\n    async fn flush(&mut self) -> Result<(), Self::Error> {\n        self.flush_inner().await\n    }\n}\n\nimpl embedded_io::Read for BufferedUart<'_> {\n    fn read(&mut self, buf: &mut [u8]) -> Result<usize, Self::Error> {\n        self.rx.read(buf)\n    }\n}\n\nimpl embedded_io::Read for BufferedUartRx<'_> {\n    fn read(&mut self, buf: &mut [u8]) -> Result<usize, Self::Error> {\n        self.blocking_read_inner(buf)\n    }\n}\n\nimpl embedded_io::Write for BufferedUart<'_> {\n    fn write(&mut self, buf: &[u8]) -> Result<usize, Self::Error> {\n        self.tx.write(buf)\n    }\n\n    fn flush(&mut self) -> Result<(), Self::Error> {\n        self.tx.flush()\n    }\n}\n\nimpl embedded_io::Write for BufferedUartTx<'_> {\n    fn write(&mut self, buf: &[u8]) -> Result<usize, Self::Error> {\n        self.blocking_write_inner(buf)\n    }\n\n    fn flush(&mut self) -> Result<(), Self::Error> {\n        self.blocking_flush()\n    }\n}\n\nimpl embedded_hal_nb::serial::Error for Error {\n    fn kind(&self) -> embedded_hal_nb::serial::ErrorKind {\n        match self {\n            Error::Framing => embedded_hal_nb::serial::ErrorKind::FrameFormat,\n            Error::Noise => embedded_hal_nb::serial::ErrorKind::Noise,\n            Error::Overrun => embedded_hal_nb::serial::ErrorKind::Overrun,\n            Error::Parity => embedded_hal_nb::serial::ErrorKind::Parity,\n            Error::Break => embedded_hal_nb::serial::ErrorKind::Other,\n        }\n    }\n}\n\nimpl embedded_hal_nb::serial::ErrorType for BufferedUart<'_> {\n    type Error = Error;\n}\n\nimpl embedded_hal_nb::serial::ErrorType for BufferedUartRx<'_> {\n    type Error = Error;\n}\n\nimpl embedded_hal_nb::serial::ErrorType for BufferedUartTx<'_> {\n    type Error = Error;\n}\n\nimpl embedded_hal_nb::serial::Read for BufferedUart<'_> {\n    fn read(&mut self) -> nb::Result<u8, Self::Error> {\n        self.rx.read()\n    }\n}\n\nimpl embedded_hal_nb::serial::Read for BufferedUartRx<'_> {\n    fn read(&mut self) -> nb::Result<u8, Self::Error> {\n        if self.info.regs.stat().read().rxfe() {\n            return Err(nb::Error::WouldBlock);\n        }\n\n        super::read_with_error(self.info.regs).map_err(nb::Error::Other)\n    }\n}\n\nimpl embedded_hal_nb::serial::Write for BufferedUart<'_> {\n    fn write(&mut self, word: u8) -> nb::Result<(), Self::Error> {\n        self.tx.write(word)\n    }\n\n    fn flush(&mut self) -> nb::Result<(), Self::Error> {\n        self.tx.flush()\n    }\n}\n\nimpl embedded_hal_nb::serial::Write for BufferedUartTx<'_> {\n    fn write(&mut self, word: u8) -> nb::Result<(), Self::Error> {\n        self.blocking_write(&[word]).map(drop).map_err(nb::Error::Other)\n    }\n\n    fn flush(&mut self) -> nb::Result<(), Self::Error> {\n        self.blocking_flush().map_err(nb::Error::Other)\n    }\n}\n\n// Impl details\n\n/// Buffered UART state.\npub(crate) struct BufferedState {\n    /// non-buffered UART state. This is inline in order to avoid [`BufferedUartRx`]/Tx\n    /// needing to carry around a 2nd static reference and waste another 4 bytes.\n    state: State,\n    rx_waker: AtomicWaker,\n    rx_buf: RingBuffer,\n    tx_waker: AtomicWaker,\n    tx_buf: RingBuffer,\n    rx_error: AtomicU8,\n}\n\n// these must match bits 8..12 in RXDATA, but shifted by 8 to the right\nconst RXE_NOISE: u8 = 16;\nconst RXE_OVERRUN: u8 = 8;\nconst RXE_BREAK: u8 = 4;\nconst RXE_PARITY: u8 = 2;\nconst RXE_FRAMING: u8 = 1;\n\nimpl BufferedState {\n    pub const fn new() -> Self {\n        Self {\n            state: State::new(),\n            rx_waker: AtomicWaker::new(),\n            rx_buf: RingBuffer::new(),\n            tx_waker: AtomicWaker::new(),\n            tx_buf: RingBuffer::new(),\n            rx_error: AtomicU8::new(0),\n        }\n    }\n}\n\nimpl<'d> BufferedUart<'d> {\n    fn new_inner<T: Instance>(\n        _peri: Peri<'d, T>,\n        rx: Option<Peri<'d, AnyPin>>,\n        tx: Option<Peri<'d, AnyPin>>,\n        rts: Option<Peri<'d, AnyPin>>,\n        cts: Option<Peri<'d, AnyPin>>,\n        tx_buffer: &'d mut [u8],\n        rx_buffer: &'d mut [u8],\n        config: Config,\n    ) -> Result<Self, ConfigError> {\n        let info = T::info();\n        let state = T::buffered_state();\n\n        let mut this = Self {\n            tx: BufferedUartTx {\n                info,\n                state,\n                tx,\n                cts,\n                reborrowed: false,\n            },\n            rx: BufferedUartRx {\n                info,\n                state,\n                rx,\n                rts,\n                reborrowed: false,\n            },\n        };\n        this.enable_and_configure(tx_buffer, rx_buffer, &config)?;\n\n        Ok(this)\n    }\n\n    fn enable_and_configure(\n        &mut self,\n        tx_buffer: &'d mut [u8],\n        rx_buffer: &'d mut [u8],\n        config: &Config,\n    ) -> Result<(), ConfigError> {\n        let info = self.rx.info;\n        let state = self.rx.state;\n\n        assert!(!tx_buffer.is_empty());\n        assert!(!rx_buffer.is_empty());\n\n        init_buffers(info, state, Some(tx_buffer), Some(rx_buffer));\n        super::enable(info.regs);\n        super::configure(\n            info,\n            &state.state,\n            config,\n            true,\n            self.rx.rts.is_some(),\n            true,\n            self.tx.cts.is_some(),\n        )?;\n\n        info.regs.cpu_int(0).imask().modify(|w| {\n            w.set_rxint(true);\n        });\n\n        info.interrupt.unpend();\n        unsafe { info.interrupt.enable() };\n\n        Ok(())\n    }\n}\n\nimpl<'d> BufferedUartRx<'d> {\n    fn new_inner<T: Instance>(\n        _peri: Peri<'d, T>,\n        rx: Option<Peri<'d, AnyPin>>,\n        rts: Option<Peri<'d, AnyPin>>,\n        rx_buffer: &'d mut [u8],\n        config: Config,\n    ) -> Result<Self, ConfigError> {\n        let mut this = Self {\n            info: T::info(),\n            state: T::buffered_state(),\n            rx,\n            rts,\n            reborrowed: false,\n        };\n        this.enable_and_configure(rx_buffer, &config)?;\n\n        Ok(this)\n    }\n\n    fn enable_and_configure(&mut self, rx_buffer: &'d mut [u8], config: &Config) -> Result<(), ConfigError> {\n        let info = self.info;\n        let state = self.state;\n\n        init_buffers(info, state, None, Some(rx_buffer));\n        super::enable(info.regs);\n        super::configure(info, &self.state.state, config, true, self.rts.is_some(), false, false)?;\n\n        info.regs.cpu_int(0).imask().modify(|w| {\n            w.set_rxint(true);\n        });\n\n        info.interrupt.unpend();\n        unsafe { info.interrupt.enable() };\n\n        Ok(())\n    }\n\n    async fn read_inner(&self, buf: &mut [u8]) -> Result<usize, Error> {\n        poll_fn(move |cx| {\n            let state = self.state;\n\n            if let Poll::Ready(r) = self.try_read(buf) {\n                return Poll::Ready(r);\n            }\n\n            state.rx_waker.register(cx.waker());\n            Poll::Pending\n        })\n        .await\n    }\n\n    fn blocking_read_inner(&self, buffer: &mut [u8]) -> Result<usize, Error> {\n        loop {\n            match self.try_read(buffer) {\n                Poll::Ready(res) => return res,\n                Poll::Pending => continue,\n            }\n        }\n    }\n\n    fn fill_buf_inner(&self) -> impl Future<Output = Result<&'_ [u8], Error>> {\n        poll_fn(move |cx| {\n            let mut rx_reader = unsafe { self.state.rx_buf.reader() };\n            let (p, n) = rx_reader.pop_buf();\n            let result = if n == 0 {\n                match Self::get_rx_error(self.state) {\n                    None => {\n                        self.state.rx_waker.register(cx.waker());\n                        return Poll::Pending;\n                    }\n                    Some(e) => Err(e),\n                }\n            } else {\n                let buf = unsafe { slice::from_raw_parts(p, n) };\n                Ok(buf)\n            };\n\n            Poll::Ready(result)\n        })\n    }\n\n    fn consume_inner(&self, amt: usize) {\n        let mut rx_reader = unsafe { self.state.rx_buf.reader() };\n        rx_reader.pop_done(amt);\n\n        // (Re-)Enable the interrupt to receive more data in case it was\n        // disabled because the buffer was full or errors were detected.\n        self.info.regs.cpu_int(0).imask().modify(|w| {\n            w.set_rxint(true);\n            w.set_rtout(true);\n        });\n    }\n\n    /// we are ready to read if there is data in the buffer\n    fn read_ready_inner(&self) -> Result<bool, Error> {\n        Ok(!self.state.rx_buf.is_empty())\n    }\n\n    fn try_read(&self, buf: &mut [u8]) -> Poll<Result<usize, Error>> {\n        let state = self.state;\n\n        if buf.is_empty() {\n            return Poll::Ready(Ok(0));\n        }\n\n        let mut rx_reader = unsafe { state.rx_buf.reader() };\n        let n = rx_reader.pop(|data| {\n            let n = data.len().min(buf.len());\n            buf[..n].copy_from_slice(&data[..n]);\n            n\n        });\n\n        let result = if n == 0 {\n            match Self::get_rx_error(state) {\n                None => return Poll::Pending,\n                Some(e) => Err(e),\n            }\n        } else {\n            Ok(n)\n        };\n\n        // (Re-)Enable the interrupt to receive more data in case it was\n        // disabled because the buffer was full or errors were detected.\n        self.info.regs.cpu_int(0).imask().modify(|w| {\n            w.set_rxint(true);\n            w.set_rtout(true);\n        });\n\n        Poll::Ready(result)\n    }\n\n    fn get_rx_error(state: &BufferedState) -> Option<Error> {\n        // Cortex-M0 has does not support atomic swap, so we must do two operations.\n        let errs = critical_section::with(|_cs| {\n            let errs = state.rx_error.load(Ordering::Relaxed);\n            state.rx_error.store(0, Ordering::Relaxed);\n\n            errs\n        });\n\n        if errs & RXE_NOISE != 0 {\n            Some(Error::Noise)\n        } else if errs & RXE_OVERRUN != 0 {\n            Some(Error::Overrun)\n        } else if errs & RXE_BREAK != 0 {\n            Some(Error::Break)\n        } else if errs & RXE_PARITY != 0 {\n            Some(Error::Parity)\n        } else if errs & RXE_FRAMING != 0 {\n            Some(Error::Framing)\n        } else {\n            None\n        }\n    }\n}\n\nimpl<'d> BufferedUartTx<'d> {\n    fn new_inner<T: Instance>(\n        _peri: Peri<'d, T>,\n        tx: Option<Peri<'d, AnyPin>>,\n        cts: Option<Peri<'d, AnyPin>>,\n        tx_buffer: &'d mut [u8],\n        config: Config,\n    ) -> Result<Self, ConfigError> {\n        let mut this = Self {\n            info: T::info(),\n            state: T::buffered_state(),\n            tx,\n            cts,\n            reborrowed: false,\n        };\n\n        this.enable_and_configure(tx_buffer, &config)?;\n\n        Ok(this)\n    }\n\n    async fn write_inner(&self, buf: &[u8]) -> Result<usize, Error> {\n        poll_fn(move |cx| {\n            let state = self.state;\n\n            if buf.is_empty() {\n                return Poll::Ready(Ok(0));\n            }\n\n            let mut tx_writer = unsafe { state.tx_buf.writer() };\n            let n = tx_writer.push(|data| {\n                let n = data.len().min(buf.len());\n                data[..n].copy_from_slice(&buf[..n]);\n                n\n            });\n\n            if n == 0 {\n                state.tx_waker.register(cx.waker());\n                return Poll::Pending;\n            }\n\n            // The TX interrupt only triggers when the there was data in the\n            // FIFO and the number of bytes drops below a threshold. When the\n            // FIFO was empty we have to manually pend the interrupt to shovel\n            // TX data from the buffer into the FIFO.\n            self.info.interrupt.pend();\n            Poll::Ready(Ok(n))\n        })\n        .await\n    }\n\n    fn blocking_write_inner(&self, buffer: &[u8]) -> Result<usize, Error> {\n        let state = self.state;\n\n        loop {\n            let empty = state.tx_buf.is_empty();\n\n            // SAFETY: tx buf must be initialized if BufferedUartTx exists.\n            let mut tx_writer = unsafe { state.tx_buf.writer() };\n            let data = tx_writer.push_slice();\n\n            if !data.is_empty() {\n                let n = data.len().min(buffer.len());\n                data[..n].copy_from_slice(&buffer[..n]);\n                tx_writer.push_done(n);\n\n                if empty {\n                    self.info.interrupt.pend();\n                }\n\n                return Ok(n);\n            }\n        }\n    }\n\n    async fn flush_inner(&self) -> Result<(), Error> {\n        poll_fn(move |cx| {\n            let state = self.state;\n\n            if !state.tx_buf.is_empty() {\n                state.tx_waker.register(cx.waker());\n                return Poll::Pending;\n            }\n\n            Poll::Ready(Ok(()))\n        })\n        .await\n    }\n\n    fn enable_and_configure(&mut self, tx_buffer: &'d mut [u8], config: &Config) -> Result<(), ConfigError> {\n        let info = self.info;\n        let state = self.state;\n\n        init_buffers(info, state, Some(tx_buffer), None);\n        super::enable(info.regs);\n        super::configure(info, &state.state, config, false, false, true, self.cts.is_some())?;\n\n        info.regs.cpu_int(0).imask().modify(|w| {\n            w.set_rxint(true);\n        });\n\n        info.interrupt.unpend();\n        unsafe { info.interrupt.enable() };\n\n        Ok(())\n    }\n}\n\nfn init_buffers<'d>(\n    info: &Info,\n    state: &BufferedState,\n    tx_buffer: Option<&'d mut [u8]>,\n    rx_buffer: Option<&'d mut [u8]>,\n) {\n    if let Some(tx_buffer) = tx_buffer {\n        let len = tx_buffer.len();\n        unsafe { state.tx_buf.init(tx_buffer.as_mut_ptr(), len) };\n    }\n\n    if let Some(rx_buffer) = rx_buffer {\n        let len = rx_buffer.len();\n        unsafe { state.rx_buf.init(rx_buffer.as_mut_ptr(), len) };\n    }\n\n    info.regs.cpu_int(0).imask().modify(|w| {\n        w.set_nerr(true);\n        w.set_frmerr(true);\n        w.set_parerr(true);\n        w.set_brkerr(true);\n        w.set_ovrerr(true);\n    });\n}\n\nfn on_interrupt(r: Regs, state: &'static BufferedState) {\n    let int = r.cpu_int(0).mis().read();\n\n    // Per https://github.com/embassy-rs/embassy/pull/1458, both buffered and unbuffered handlers may be bound.\n    if super::dma_enabled(r) {\n        return;\n    }\n\n    // RX\n    if state.rx_buf.is_available() {\n        // SAFETY: RX must have been initialized if RXE is set.\n        let mut rx_writer = unsafe { state.rx_buf.writer() };\n        let rx_buf = rx_writer.push_slice();\n        let mut n_read = 0;\n        let mut error = false;\n\n        for rx_byte in rx_buf {\n            let stat = r.stat().read();\n\n            if stat.rxfe() {\n                break;\n            }\n\n            let data = r.rxdata().read();\n\n            if (data.0 >> 8) != 0 {\n                // Cortex-M0 does not support atomic fetch_or, must do 2 operations.\n                critical_section::with(|_cs| {\n                    let mut value = state.rx_error.load(Ordering::Relaxed);\n                    value |= (data.0 >> 8) as u8;\n                    state.rx_error.store(value, Ordering::Relaxed);\n                });\n                error = true;\n\n                // only fill the buffer with valid characters. the current character is fine\n                // if the error is an overrun, but if we add it to the buffer we'll report\n                // the overrun one character too late. drop it instead and pretend we were\n                // a bit slower at draining the rx fifo than we actually were.\n                // this is consistent with blocking uart error reporting.\n                break;\n            }\n\n            *rx_byte = data.data();\n            n_read += 1;\n        }\n\n        if n_read > 0 {\n            rx_writer.push_done(n_read);\n            state.rx_waker.wake();\n        } else if error {\n            state.rx_waker.wake();\n        }\n\n        // Disable any further RX interrupts when the buffer becomes full or\n        // errors have occurred. This lets us buffer additional errors in the\n        // fifo without needing more error storage locations, and most applications\n        // will want to do a full reset of their uart state anyway once an error\n        // has happened.\n        if state.rx_buf.is_full() || error {\n            r.cpu_int(0).imask().modify(|w| {\n                w.set_rxint(false);\n                w.set_rtout(false);\n            });\n        }\n    }\n\n    if int.eot() {\n        r.cpu_int(0).imask().modify(|w| {\n            w.set_eot(false);\n        });\n\n        r.cpu_int(0).iclr().write(|w| {\n            w.set_eot(true);\n        });\n\n        state.tx_waker.wake();\n    }\n\n    // TX\n    if state.tx_buf.is_available() {\n        // SAFETY: TX must have been initialized if TXE is set.\n        let mut tx_reader = unsafe { state.tx_buf.reader() };\n        let buf = tx_reader.pop_slice();\n        let mut n_written = 0;\n\n        for tx_byte in buf.iter_mut() {\n            let stat = r.stat().read();\n\n            if stat.txff() {\n                break;\n            }\n\n            r.txdata().write(|w| {\n                w.set_data(*tx_byte);\n            });\n            n_written += 1;\n        }\n\n        if n_written > 0 {\n            // EOT will wake.\n            r.cpu_int(0).imask().modify(|w| {\n                w.set_eot(true);\n            });\n\n            tx_reader.pop_done(n_written);\n        }\n    }\n\n    // Clear TX and error interrupt flags\n    // RX interrupt flags are cleared by writing to ICLR.\n    let mis = r.cpu_int(0).mis().read();\n    r.cpu_int(0).iclr().write(|w| {\n        w.set_nerr(mis.nerr());\n        w.set_frmerr(mis.frmerr());\n        w.set_parerr(mis.parerr());\n        w.set_brkerr(mis.brkerr());\n        w.set_ovrerr(mis.ovrerr());\n    });\n\n    // Errors\n    if mis.nerr() {\n        warn!(\"Noise error\");\n    }\n    if mis.frmerr() {\n        warn!(\"Framing error\");\n    }\n    if mis.parerr() {\n        warn!(\"Parity error\");\n    }\n    if mis.brkerr() {\n        warn!(\"Break error\");\n    }\n    if mis.ovrerr() {\n        warn!(\"Overrun error\");\n    }\n}\n"
  },
  {
    "path": "embassy-mspm0/src/uart/mod.rs",
    "content": "#![macro_use]\n\nmod buffered;\n\nuse core::marker::PhantomData;\nuse core::sync::atomic::{AtomicU32, Ordering, compiler_fence};\n\npub use buffered::*;\nuse embassy_embedded_hal::SetConfig;\nuse embassy_hal_internal::PeripheralType;\n\nuse crate::Peri;\nuse crate::gpio::{AnyPin, PfType, Pull, SealedPin};\nuse crate::interrupt::{Interrupt, InterruptExt};\nuse crate::mode::{Blocking, Mode};\nuse crate::pac::uart::{Uart as Regs, vals};\n\n/// The clock source for the UART.\n#[derive(Clone, Copy, PartialEq, Eq, Debug)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub enum ClockSel {\n    /// Use the low frequency clock.\n    ///\n    /// The LFCLK runs at 32.768 kHz.\n    LfClk,\n\n    /// Use the middle frequency clock.\n    ///\n    /// The MCLK runs at 4 MHz.\n    MfClk,\n    // BusClk,\n    // BusClk depends on the timer's power domain.\n    // This will be implemented later.\n}\n\n#[non_exhaustive]\n#[derive(Clone, Copy, PartialEq, Eq, Debug)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\n/// The order of bits in byte.\npub enum BitOrder {\n    /// The most significant bit is first.\n    MsbFirst,\n\n    /// The least significant bit is first.\n    LsbFirst,\n}\n\n#[derive(Clone, Copy, PartialEq, Eq, Debug)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\n/// Number of data bits\npub enum DataBits {\n    /// 5 Data Bits\n    DataBits5,\n\n    /// 6 Data Bits\n    DataBits6,\n\n    /// 7 Data Bits\n    DataBits7,\n\n    /// 8 Data Bits\n    DataBits8,\n}\n\n#[derive(Clone, Copy, PartialEq, Eq, Debug)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\n/// Parity\npub enum Parity {\n    /// No parity\n    ParityNone,\n\n    /// Even Parity\n    ParityEven,\n\n    /// Odd Parity\n    ParityOdd,\n}\n\n#[derive(Clone, Copy, PartialEq, Eq, Debug)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\n/// Number of stop bits\npub enum StopBits {\n    /// One stop bit\n    Stop1,\n\n    /// Two stop bits\n    Stop2,\n}\n\n#[non_exhaustive]\n#[derive(Clone, Copy, PartialEq, Eq, Debug)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\n/// Config Error\npub enum ConfigError {\n    /// Rx or Tx not enabled\n    RxOrTxNotEnabled,\n\n    /// The baud rate could not be configured with the given clocks.\n    InvalidBaudRate,\n}\n\n#[non_exhaustive]\n#[derive(Clone, Copy, PartialEq, Eq, Debug)]\n/// Config\npub struct Config {\n    /// UART clock source.\n    pub clock_source: ClockSel,\n\n    /// Baud rate\n    pub baudrate: u32,\n\n    /// Number of data bits.\n    pub data_bits: DataBits,\n\n    /// Number of stop bits.\n    pub stop_bits: StopBits,\n\n    /// Parity type.\n    pub parity: Parity,\n\n    /// The order of bits in a transmitted/received byte.\n    pub msb_order: BitOrder,\n\n    /// If true: the `TX` is internally connected to `RX`.\n    pub loop_back_enable: bool,\n\n    // TODO: Pending way to check if uart is extended\n    // /// If true: [manchester coding] is used.\n    // ///\n    // /// [manchester coding]: https://en.wikipedia.org/wiki/Manchester_code\n    // pub manchester: bool,\n\n    // TODO: majority voting\n    /// If true: the built-in FIFO is enabled.\n    pub fifo_enable: bool,\n\n    // TODO: glitch suppression\n    /// If true: invert TX pin signal values (V<sub>DD</sub> = 0/mark, Gnd = 1/idle).\n    pub invert_tx: bool,\n\n    /// If true: invert RX pin signal values (V<sub>DD</sub> = 0/mark, Gnd = 1/idle).\n    pub invert_rx: bool,\n\n    /// If true: invert RTS pin signal values (V<sub>DD</sub> = 0/mark, Gnd = 1/idle).\n    pub invert_rts: bool,\n\n    /// If true: invert CTS pin signal values (V<sub>DD</sub> = 0/mark, Gnd = 1/idle).\n    pub invert_cts: bool,\n\n    /// Set the pull configuration for the TX pin.\n    pub tx_pull: Pull,\n\n    /// Set the pull configuration for the RX pin.\n    pub rx_pull: Pull,\n\n    /// Set the pull configuration for the RTS pin.\n    pub rts_pull: Pull,\n\n    /// Set the pull configuration for the CTS pin.\n    pub cts_pull: Pull,\n}\n\nimpl Default for Config {\n    fn default() -> Self {\n        Self {\n            clock_source: ClockSel::MfClk,\n            baudrate: 115200,\n            data_bits: DataBits::DataBits8,\n            stop_bits: StopBits::Stop1,\n            parity: Parity::ParityNone,\n            // hardware default\n            msb_order: BitOrder::LsbFirst,\n            loop_back_enable: false,\n            // manchester: false,\n            fifo_enable: false,\n            invert_tx: false,\n            invert_rx: false,\n            invert_rts: false,\n            invert_cts: false,\n            tx_pull: Pull::None,\n            rx_pull: Pull::None,\n            rts_pull: Pull::None,\n            cts_pull: Pull::None,\n        }\n    }\n}\n\n/// Bidirectional UART Driver, which acts as a combination of [`UartTx`] and [`UartRx`].\n///\n/// ### Notes on [`embedded_io::Read`]\n///\n/// [`embedded_io::Read`] requires guarantees that the base [`UartRx`] cannot provide.\n///\n/// See [`UartRx`] for more details, and see [`BufferedUart`] and [`RingBufferedUartRx`]\n/// as alternatives that do provide the necessary guarantees for `embedded_io::Read`.\npub struct Uart<'d, M: Mode> {\n    tx: UartTx<'d, M>,\n    rx: UartRx<'d, M>,\n}\n\nimpl<'d, M: Mode> SetConfig for Uart<'d, M> {\n    type Config = Config;\n    type ConfigError = ConfigError;\n\n    fn set_config(&mut self, config: &Self::Config) -> Result<(), Self::ConfigError> {\n        self.set_config(config)\n    }\n}\n\n/// Serial error\n#[derive(Debug, Eq, PartialEq, Copy, Clone)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\n#[non_exhaustive]\npub enum Error {\n    Framing,\n\n    Noise,\n\n    Overrun,\n\n    Parity,\n\n    Break,\n}\n\nimpl core::fmt::Display for Error {\n    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {\n        let message = match self {\n            Self::Framing => \"Framing Error\",\n            Self::Noise => \"Noise Error\",\n            Self::Overrun => \"RX Buffer Overrun\",\n            Self::Parity => \"Parity Check Error\",\n            Self::Break => \"Break Error\",\n        };\n\n        write!(f, \"{}\", message)\n    }\n}\n\nimpl core::error::Error for Error {}\n\nimpl embedded_io::Error for Error {\n    fn kind(&self) -> embedded_io::ErrorKind {\n        embedded_io::ErrorKind::Other\n    }\n}\n\n/// Rx-only UART Driver.\n///\n/// Can be obtained from [`Uart::split`], or can be constructed independently,\n/// if you do not need the transmitting half of the driver.\npub struct UartRx<'d, M: Mode> {\n    info: &'static Info,\n    state: &'static State,\n    rx: Option<Peri<'d, AnyPin>>,\n    rts: Option<Peri<'d, AnyPin>>,\n    _phantom: PhantomData<M>,\n}\n\nimpl<'d, M: Mode> SetConfig for UartRx<'d, M> {\n    type Config = Config;\n    type ConfigError = ConfigError;\n\n    fn set_config(&mut self, config: &Self::Config) -> Result<(), Self::ConfigError> {\n        self.set_config(config)\n    }\n}\n\nimpl<'d> UartRx<'d, Blocking> {\n    /// Create a new rx-only UART with no hardware flow control.\n    ///\n    /// Useful if you only want Uart Rx. It saves 1 pin.\n    pub fn new_blocking<T: Instance>(\n        peri: Peri<'d, T>,\n        rx: Peri<'d, impl RxPin<T>>,\n        config: Config,\n    ) -> Result<Self, ConfigError> {\n        Self::new_inner(peri, new_pin!(rx, config.rx_pf()), None, config)\n    }\n\n    /// Create a new rx-only UART with a request-to-send pin\n    pub fn new_blocking_with_rts<T: Instance>(\n        peri: Peri<'d, T>,\n        rx: Peri<'d, impl RxPin<T>>,\n        rts: Peri<'d, impl RtsPin<T>>,\n        config: Config,\n    ) -> Result<Self, ConfigError> {\n        Self::new_inner(\n            peri,\n            new_pin!(rx, config.rx_pf()),\n            new_pin!(rts, config.rts_pf()),\n            config,\n        )\n    }\n}\n\nimpl<'d, M: Mode> UartRx<'d, M> {\n    /// Perform a blocking read into `buffer`\n    pub fn blocking_read(&mut self, buffer: &mut [u8]) -> Result<(), Error> {\n        let r = self.info.regs;\n\n        for b in buffer {\n            // Wait if nothing has arrived yet.\n            while r.stat().read().rxfe() {}\n\n            // Prevent the compiler from reading from buffer too early\n            compiler_fence(Ordering::Acquire);\n            *b = read_with_error(r)?;\n        }\n\n        Ok(())\n    }\n\n    /// Reconfigure the driver\n    pub fn set_config(&mut self, config: &Config) -> Result<(), ConfigError> {\n        if let Some(ref rx) = self.rx {\n            rx.update_pf(config.rx_pf());\n        }\n\n        if let Some(ref rts) = self.rts {\n            rts.update_pf(config.rts_pf());\n        }\n\n        reconfigure(self.info, self.state, config)\n    }\n\n    /// Set baudrate\n    pub fn set_baudrate(&self, baudrate: u32) -> Result<(), ConfigError> {\n        set_baudrate(&self.info, self.state.clock.load(Ordering::Relaxed), baudrate)\n    }\n}\n\nimpl<'d, M: Mode> Drop for UartRx<'d, M> {\n    fn drop(&mut self) {\n        self.rx.as_ref().map(|x| x.set_as_disconnected());\n        self.rts.as_ref().map(|x| x.set_as_disconnected());\n    }\n}\n\n/// Tx-only UART Driver.\n///\n/// Can be obtained from [`Uart::split`], or can be constructed independently,\n/// if you do not need the receiving half of the driver.\npub struct UartTx<'d, M: Mode> {\n    info: &'static Info,\n    state: &'static State,\n    tx: Option<Peri<'d, AnyPin>>,\n    cts: Option<Peri<'d, AnyPin>>,\n    _phantom: PhantomData<M>,\n}\n\nimpl<'d, M: Mode> SetConfig for UartTx<'d, M> {\n    type Config = Config;\n    type ConfigError = ConfigError;\n\n    fn set_config(&mut self, config: &Self::Config) -> Result<(), Self::ConfigError> {\n        reconfigure(self.info, self.state, config)\n    }\n}\n\nimpl<'d> UartTx<'d, Blocking> {\n    /// Create a new blocking tx-only UART with no hardware flow control.\n    ///\n    /// Useful if you only want Uart Tx. It saves 1 pin.\n    pub fn new_blocking<T: Instance>(\n        peri: Peri<'d, T>,\n        tx: Peri<'d, impl TxPin<T>>,\n        config: Config,\n    ) -> Result<Self, ConfigError> {\n        Self::new_inner(peri, new_pin!(tx, config.tx_pf()), None, config)\n    }\n\n    /// Create a new blocking tx-only UART with a clear-to-send pin\n    pub fn new_blocking_with_cts<T: Instance>(\n        peri: Peri<'d, T>,\n        tx: Peri<'d, impl TxPin<T>>,\n        cts: Peri<'d, impl CtsPin<T>>,\n        config: Config,\n    ) -> Result<Self, ConfigError> {\n        Self::new_inner(\n            peri,\n            new_pin!(tx, config.tx_pf()),\n            new_pin!(cts, config.cts_pf()),\n            config,\n        )\n    }\n}\n\nimpl<'d, M: Mode> UartTx<'d, M> {\n    /// Perform a blocking UART write\n    pub fn blocking_write(&mut self, buffer: &[u8]) -> Result<(), Error> {\n        let r = self.info.regs;\n\n        for &b in buffer {\n            // Wait if there is no space\n            while !r.stat().read().txfe() {}\n\n            // Prevent the compiler from writing to buffer too early\n            compiler_fence(Ordering::Release);\n            r.txdata().write(|w| {\n                w.set_data(b);\n            });\n        }\n\n        Ok(())\n    }\n\n    /// Block until transmission complete\n    pub fn blocking_flush(&mut self) -> Result<(), Error> {\n        let r = self.info.regs;\n\n        // Wait until TX fifo/buffer is empty\n        while r.stat().read().txfe() {}\n        Ok(())\n    }\n\n    /// Send break character\n    pub fn send_break(&self) {\n        let r = self.info.regs;\n\n        r.lcrh().modify(|w| {\n            w.set_brk(true);\n        });\n    }\n\n    /// Check if UART is busy.\n    pub fn busy(&self) -> bool {\n        busy(self.info.regs)\n    }\n\n    /// Reconfigure the driver\n    pub fn set_config(&mut self, config: &Config) -> Result<(), ConfigError> {\n        if let Some(ref tx) = self.tx {\n            tx.update_pf(config.tx_pf());\n        }\n\n        if let Some(ref cts) = self.cts {\n            cts.update_pf(config.cts_pf());\n        }\n\n        reconfigure(self.info, self.state, config)\n    }\n\n    /// Set baudrate\n    pub fn set_baudrate(&self, baudrate: u32) -> Result<(), ConfigError> {\n        set_baudrate(&self.info, self.state.clock.load(Ordering::Relaxed), baudrate)\n    }\n}\n\nimpl<'d, M: Mode> Drop for UartTx<'d, M> {\n    fn drop(&mut self) {\n        self.tx.as_ref().map(|x| x.set_as_disconnected());\n        self.cts.as_ref().map(|x| x.set_as_disconnected());\n    }\n}\n\nimpl<'d> Uart<'d, Blocking> {\n    /// Create a new blocking bidirectional UART.\n    pub fn new_blocking<T: Instance>(\n        peri: Peri<'d, T>,\n        rx: Peri<'d, impl RxPin<T>>,\n        tx: Peri<'d, impl TxPin<T>>,\n        config: Config,\n    ) -> Result<Self, ConfigError> {\n        Self::new_inner(\n            peri,\n            new_pin!(rx, config.rx_pf()),\n            new_pin!(tx, config.tx_pf()),\n            None,\n            None,\n            config,\n        )\n    }\n\n    /// Create a new bidirectional UART with request-to-send and clear-to-send pins\n    pub fn new_blocking_with_rtscts<T: Instance>(\n        peri: Peri<'d, T>,\n        rx: Peri<'d, impl RxPin<T>>,\n        tx: Peri<'d, impl TxPin<T>>,\n        rts: Peri<'d, impl RtsPin<T>>,\n        cts: Peri<'d, impl CtsPin<T>>,\n        config: Config,\n    ) -> Result<Self, ConfigError> {\n        Self::new_inner(\n            peri,\n            new_pin!(rx, config.rx_pf()),\n            new_pin!(tx, config.tx_pf()),\n            new_pin!(rts, config.rts_pf()),\n            new_pin!(cts, config.cts_pf()),\n            config,\n        )\n    }\n}\n\nimpl<'d, M: Mode> Uart<'d, M> {\n    /// Perform a blocking write\n    pub fn blocking_write(&mut self, buffer: &[u8]) -> Result<(), Error> {\n        self.tx.blocking_write(buffer)\n    }\n\n    /// Block until transmission complete\n    pub fn blocking_flush(&mut self) -> Result<(), Error> {\n        self.tx.blocking_flush()\n    }\n\n    /// Check if UART is busy.\n    pub fn busy(&self) -> bool {\n        self.tx.busy()\n    }\n\n    /// Perform a blocking read into `buffer`\n    pub fn blocking_read(&mut self, buffer: &mut [u8]) -> Result<(), Error> {\n        self.rx.blocking_read(buffer)\n    }\n\n    /// Split the Uart into a transmitter and receiver, which is\n    /// particularly useful when having two tasks correlating to\n    /// transmitting and receiving.\n    pub fn split(self) -> (UartTx<'d, M>, UartRx<'d, M>) {\n        (self.tx, self.rx)\n    }\n\n    /// Split the Uart into a transmitter and receiver by mutable reference,\n    /// which is particularly useful when having two tasks correlating to\n    /// transmitting and receiving.\n    pub fn split_ref(&mut self) -> (&mut UartTx<'d, M>, &mut UartRx<'d, M>) {\n        (&mut self.tx, &mut self.rx)\n    }\n\n    /// Reconfigure the driver\n    pub fn set_config(&mut self, config: &Config) -> Result<(), ConfigError> {\n        self.tx.set_config(config)?;\n        self.rx.set_config(config)\n    }\n\n    /// Send break character\n    pub fn send_break(&self) {\n        self.tx.send_break();\n    }\n\n    /// Set baudrate\n    pub fn set_baudrate(&self, baudrate: u32) -> Result<(), ConfigError> {\n        set_baudrate(&self.tx.info, self.tx.state.clock.load(Ordering::Relaxed), baudrate)\n    }\n}\n\n/// Peripheral instance trait.\n#[allow(private_bounds)]\npub trait Instance: SealedInstance + PeripheralType {\n    type Interrupt: crate::interrupt::typelevel::Interrupt;\n}\n\n/// UART `TX` pin trait\npub trait TxPin<T: Instance>: crate::gpio::Pin {\n    /// Get the PF number needed to use this pin as `TX`.\n    fn pf_num(&self) -> u8;\n}\n\n/// UART `RX` pin trait\npub trait RxPin<T: Instance>: crate::gpio::Pin {\n    /// Get the PF number needed to use this pin as `RX`.\n    fn pf_num(&self) -> u8;\n}\n\n/// UART `CTS` pin trait\npub trait CtsPin<T: Instance>: crate::gpio::Pin {\n    /// Get the PF number needed to use this pin as `CTS`.\n    fn pf_num(&self) -> u8;\n}\n\n/// UART `RTS` pin trait\npub trait RtsPin<T: Instance>: crate::gpio::Pin {\n    /// Get the PF number needed to use this pin as `RTS`.\n    fn pf_num(&self) -> u8;\n}\n\n// ==== IMPL types ====\n\npub(crate) struct Info {\n    pub(crate) regs: Regs,\n    pub(crate) interrupt: Interrupt,\n}\n\npub(crate) struct State {\n    /// The clock rate of the UART in Hz.\n    clock: AtomicU32,\n}\n\nimpl State {\n    pub const fn new() -> Self {\n        Self {\n            clock: AtomicU32::new(0),\n        }\n    }\n}\n\nimpl<'d, M: Mode> UartRx<'d, M> {\n    fn new_inner<T: Instance>(\n        _peri: Peri<'d, T>,\n        rx: Option<Peri<'d, AnyPin>>,\n        rts: Option<Peri<'d, AnyPin>>,\n        config: Config,\n    ) -> Result<Self, ConfigError> {\n        let mut this = Self {\n            info: T::info(),\n            state: T::state(),\n            rx,\n            rts,\n            _phantom: PhantomData,\n        };\n        this.enable_and_configure(&config)?;\n\n        Ok(this)\n    }\n\n    fn enable_and_configure(&mut self, config: &Config) -> Result<(), ConfigError> {\n        let info = self.info;\n\n        enable(info.regs);\n        configure(info, self.state, config, true, self.rts.is_some(), false, false)?;\n\n        Ok(())\n    }\n}\n\nimpl<'d, M: Mode> UartTx<'d, M> {\n    fn new_inner<T: Instance>(\n        _peri: Peri<'d, T>,\n        tx: Option<Peri<'d, AnyPin>>,\n        cts: Option<Peri<'d, AnyPin>>,\n        config: Config,\n    ) -> Result<Self, ConfigError> {\n        let mut this = Self {\n            info: T::info(),\n            state: T::state(),\n            tx,\n            cts,\n            _phantom: PhantomData,\n        };\n        this.enable_and_configure(&config)?;\n\n        Ok(this)\n    }\n\n    fn enable_and_configure(&mut self, config: &Config) -> Result<(), ConfigError> {\n        let info = self.info;\n        let state = self.state;\n\n        enable(info.regs);\n        configure(info, state, config, false, false, true, self.cts.is_some())?;\n\n        Ok(())\n    }\n}\n\nimpl<'d, M: Mode> Uart<'d, M> {\n    fn new_inner<T: Instance>(\n        _peri: Peri<'d, T>,\n        rx: Option<Peri<'d, AnyPin>>,\n        tx: Option<Peri<'d, AnyPin>>,\n        rts: Option<Peri<'d, AnyPin>>,\n        cts: Option<Peri<'d, AnyPin>>,\n        config: Config,\n    ) -> Result<Self, ConfigError> {\n        let info = T::info();\n        let state = T::state();\n\n        let mut this = Self {\n            tx: UartTx {\n                info,\n                state,\n                tx,\n                cts,\n                _phantom: PhantomData,\n            },\n            rx: UartRx {\n                info,\n                state,\n                rx,\n                rts,\n                _phantom: PhantomData,\n            },\n        };\n        this.enable_and_configure(&config)?;\n\n        Ok(this)\n    }\n\n    fn enable_and_configure(&mut self, config: &Config) -> Result<(), ConfigError> {\n        let info = self.rx.info;\n        let state = self.rx.state;\n\n        enable(info.regs);\n        configure(\n            info,\n            state,\n            config,\n            true,\n            self.rx.rts.is_some(),\n            true,\n            self.tx.cts.is_some(),\n        )?;\n\n        info.interrupt.unpend();\n        unsafe { info.interrupt.enable() };\n\n        Ok(())\n    }\n}\n\nimpl Config {\n    fn tx_pf(&self) -> PfType {\n        PfType::output(self.tx_pull, self.invert_tx)\n    }\n\n    fn rx_pf(&self) -> PfType {\n        PfType::input(self.rx_pull, self.invert_rx)\n    }\n\n    fn rts_pf(&self) -> PfType {\n        PfType::output(self.rts_pull, self.invert_rts)\n    }\n\n    fn cts_pf(&self) -> PfType {\n        PfType::input(self.rts_pull, self.invert_rts)\n    }\n}\n\nfn enable(regs: Regs) {\n    let gprcm = regs.gprcm(0);\n\n    gprcm.rstctl().write(|w| {\n        w.set_resetstkyclr(true);\n        w.set_resetassert(true);\n        w.set_key(vals::ResetKey::KEY);\n    });\n\n    gprcm.pwren().write(|w| {\n        w.set_enable(true);\n        w.set_key(vals::PwrenKey::KEY);\n    });\n}\n\nfn configure(\n    info: &Info,\n    state: &State,\n    config: &Config,\n    enable_rx: bool,\n    enable_rts: bool,\n    enable_tx: bool,\n    enable_cts: bool,\n) -> Result<(), ConfigError> {\n    let r = info.regs;\n\n    if !enable_rx && !enable_tx {\n        return Err(ConfigError::RxOrTxNotEnabled);\n    }\n\n    // SLAU846B says that clocks should be enabled before disabling the uart.\n    r.clksel().write(|w| match config.clock_source {\n        ClockSel::LfClk => {\n            w.set_lfclk_sel(true);\n            w.set_mfclk_sel(false);\n            w.set_busclk_sel(false);\n        }\n        ClockSel::MfClk => {\n            w.set_mfclk_sel(true);\n            w.set_lfclk_sel(false);\n            w.set_busclk_sel(false);\n        }\n    });\n\n    let clock = match config.clock_source {\n        ClockSel::LfClk => 32768,\n        ClockSel::MfClk => 4_000_000,\n    };\n\n    state.clock.store(clock, Ordering::Relaxed);\n\n    info.regs.ctl0().modify(|w| {\n        w.set_lbe(config.loop_back_enable);\n        // Errata UART_ERR_02, must set RXE to allow use of EOT.\n        w.set_rxe(enable_rx | enable_tx);\n        w.set_txe(enable_tx);\n        // RXD_OUT_EN and TXD_OUT_EN?\n        w.set_menc(false);\n        w.set_mode(vals::Mode::UART);\n        w.set_rtsen(enable_rts);\n        w.set_ctsen(enable_cts);\n        // oversampling is set later\n        w.set_fen(config.fifo_enable);\n        // TODO: config\n        w.set_majvote(false);\n        w.set_msbfirst(matches!(config.msb_order, BitOrder::MsbFirst));\n    });\n\n    info.regs.ifls().modify(|w| {\n        // TODO: Need power domain info for other options.\n        w.set_txiflsel(vals::Iflssel::AT_LEAST_ONE);\n        w.set_rxiflsel(vals::Iflssel::AT_LEAST_ONE);\n    });\n\n    info.regs.lcrh().modify(|w| {\n        let eps = if matches!(config.parity, Parity::ParityEven) {\n            vals::Eps::EVEN\n        } else {\n            vals::Eps::ODD\n        };\n\n        let wlen = match config.data_bits {\n            DataBits::DataBits5 => vals::Wlen::DATABIT5,\n            DataBits::DataBits6 => vals::Wlen::DATABIT6,\n            DataBits::DataBits7 => vals::Wlen::DATABIT7,\n            DataBits::DataBits8 => vals::Wlen::DATABIT8,\n        };\n\n        // Used in LIN mode only\n        w.set_brk(false);\n        w.set_pen(config.parity != Parity::ParityNone);\n        w.set_eps(eps);\n        w.set_stp2(matches!(config.stop_bits, StopBits::Stop2));\n        w.set_wlen(wlen);\n        // appears to only be used in RS-485 mode.\n        w.set_sps(false);\n        // IDLE pattern?\n        w.set_sendidle(false);\n        // ignore extdir_setup and extdir_hold, only used in RS-485 mode.\n    });\n\n    set_baudrate_inner(info.regs, clock, config.baudrate)?;\n\n    r.ctl0().modify(|w| {\n        w.set_enable(true);\n    });\n\n    Ok(())\n}\n\nfn reconfigure(info: &Info, state: &State, config: &Config) -> Result<(), ConfigError> {\n    info.interrupt.disable();\n    let r = info.regs;\n    let ctl0 = r.ctl0().read();\n    configure(info, state, config, ctl0.rxe(), ctl0.rtsen(), ctl0.txe(), ctl0.ctsen())?;\n\n    info.interrupt.unpend();\n    unsafe { info.interrupt.enable() };\n\n    Ok(())\n}\n\n/// Set the baud rate and clock settings.\n///\n/// This should be done relatively late during configuration since some clock settings are invalid depending on mode.\nfn set_baudrate(info: &Info, clock: u32, baudrate: u32) -> Result<(), ConfigError> {\n    let r = info.regs;\n\n    info.interrupt.disable();\n\n    // Programming baud rate requires that the peripheral is disabled\n    critical_section::with(|_cs| {\n        r.ctl0().modify(|w| {\n            w.set_enable(false);\n        });\n    });\n\n    // Wait for end of transmission per suggestion in SLAU 845 section 18.3.28\n    while !r.stat().read().txfe() {}\n\n    set_baudrate_inner(r, clock, baudrate)?;\n\n    critical_section::with(|_cs| {\n        r.ctl0().modify(|w| {\n            w.set_enable(true);\n        });\n    });\n\n    info.interrupt.unpend();\n    unsafe { info.interrupt.enable() };\n\n    Ok(())\n}\n\nfn set_baudrate_inner(regs: Regs, clock: u32, baudrate: u32) -> Result<(), ConfigError> {\n    // Quoting SLAU846 section 18.2.3.4:\n    // \"When IBRD = 0, FBRD is ignored and no data gets transferred by the UART.\"\n    const MIN_IBRD: u16 = 1;\n\n    // FBRD can be 0\n    // FBRD is at most a 6-bit number.\n    const MAX_FBRD: u8 = 2_u8.pow(6);\n\n    const DIVS: [(u8, vals::Clkdiv); 8] = [\n        (1, vals::Clkdiv::DIV_BY_1),\n        (2, vals::Clkdiv::DIV_BY_2),\n        (3, vals::Clkdiv::DIV_BY_3),\n        (4, vals::Clkdiv::DIV_BY_4),\n        (5, vals::Clkdiv::DIV_BY_5),\n        (6, vals::Clkdiv::DIV_BY_6),\n        (7, vals::Clkdiv::DIV_BY_7),\n        (8, vals::Clkdiv::DIV_BY_8),\n    ];\n\n    // Quoting from SLAU 846 section 18.2.3.4:\n    // \"Select oversampling by 3 or 8 to achieve higher speed with UARTclk/8 or UARTclk/3. In this case\n    //  the receiver tolerance to clock deviation is reduced.\"\n    //\n    // \"Select oversampling by 16 to increase the tolerance of the receiver to clock deviations. The\n    //  maximum speed is limited to UARTclk/16.\"\n    //\n    // Based on these requirements, prioritize higher oversampling first to increase tolerance to clock\n    // deviation. If no valid BRD value can be found satisifying the highest sample rate, then reduce\n    // sample rate until valid parameters are found.\n    const OVS: [(u8, vals::Hse); 3] = [(16, vals::Hse::OVS16), (8, vals::Hse::OVS8), (3, vals::Hse::OVS3)];\n\n    // 3x oversampling is not supported with manchester coding, DALI or IrDA.\n    let x3_invalid = {\n        let ctl0 = regs.ctl0().read();\n        let irctl = regs.irctl().read();\n\n        ctl0.menc() || matches!(ctl0.mode(), vals::Mode::DALI) || irctl.iren()\n    };\n    let mut found = None;\n\n    'outer: for &(oversampling, hse_value) in &OVS {\n        if matches!(hse_value, vals::Hse::OVS3) && x3_invalid {\n            continue;\n        }\n\n        // Verify that the selected oversampling does not require a clock faster than what the hardware\n        // is provided.\n        let Some(min_clock) = baudrate.checked_mul(oversampling as u32) else {\n            trace!(\n                \"{}x oversampling would cause overflow for clock: {} Hz\",\n                oversampling, clock\n            );\n            continue;\n        };\n\n        if min_clock > clock {\n            trace!(\"{} oversampling is too high for clock: {} Hz\", oversampling, clock);\n            continue;\n        }\n\n        for &(div, div_value) in &DIVS {\n            trace!(\n                \"Trying div: {}, oversampling {} for {} baud\",\n                div, oversampling, baudrate\n            );\n\n            let Some((ibrd, fbrd)) = calculate_brd(clock, div, baudrate, oversampling) else {\n                trace!(\"Calculating BRD overflowed: trying another divider\");\n                continue;\n            };\n\n            if ibrd < MIN_IBRD || fbrd > MAX_FBRD {\n                trace!(\"BRD was invalid: trying another divider\");\n                continue;\n            }\n\n            found = Some((hse_value, div_value, ibrd, fbrd));\n            break 'outer;\n        }\n    }\n\n    let Some((hse, div, ibrd, fbrd)) = found else {\n        return Err(ConfigError::InvalidBaudRate);\n    };\n\n    regs.clkdiv().write(|w| {\n        w.set_ratio(div);\n    });\n\n    regs.ibrd().write(|w| {\n        w.set_divint(ibrd);\n    });\n\n    regs.fbrd().write(|w| {\n        w.set_divfrac(fbrd);\n    });\n\n    regs.ctl0().modify(|w| {\n        w.set_hse(hse);\n    });\n\n    Ok(())\n}\n\n/// Calculate the integer and fractional parts of the `BRD` value.\n///\n/// Returns [`None`] if calculating this results in overflows.\n///\n/// Values returned are `(ibrd, fbrd)`\nfn calculate_brd(clock: u32, div: u8, baud: u32, oversampling: u8) -> Option<(u16, u8)> {\n    use fixed::types::U26F6;\n\n    // Calculate BRD according to SLAU 846 section 18.2.3.4.\n    //\n    // BRD is a 22-bit value with 16 integer bits and 6 fractional bits.\n    //\n    // uart_clock = clock / div\n    // brd = ibrd.fbrd = uart_clock / (oversampling * baud)\"\n    //\n    // It is tempting to rearrange the equation such that there is only a single division in\n    // order to reduce error. However this is wrong since the denominator ends up being too\n    // small to represent in 6 fraction bits. This means that FBRD would always be 0.\n    //\n    // Calculations are done in a U16F6 format. However the fixed crate has no such representation.\n    // U26F6 is used since it has the same number of fractional bits and we verify at the end that\n    // the integer part did not overflow.\n    let clock = U26F6::from_num(clock);\n    let div = U26F6::from_num(div);\n    let oversampling = U26F6::from_num(oversampling);\n    let baud = U26F6::from_num(baud);\n\n    let uart_clock = clock.checked_div(div)?;\n\n    // oversampling * baud\n    let denom = oversampling.checked_mul(baud)?;\n    // uart_clock / (oversampling * baud)\n    let brd = uart_clock.checked_div(denom)?;\n\n    // Checked is used to determine overflow in the 10 most singificant bits since the\n    // actual representation of BRD is U16F6.\n    let ibrd = brd.checked_to_num::<u16>()?;\n\n    // We need to scale FBRD's representation to an integer.\n    let fbrd_scale = U26F6::from_num(2_u32.checked_pow(U26F6::FRAC_NBITS)?);\n\n    // It is suggested that 0.5 is added to ensure that any fractional parts round up to the next\n    // integer. If it doesn't round up then it'll get discarded which is okay.\n    let half = U26F6::from_num(1) / U26F6::from_num(2);\n    // fbrd = INT(((FRAC(BRD) * 64) + 0.5))\n    let fbrd = brd\n        .frac()\n        .checked_mul(fbrd_scale)?\n        .checked_add(half)?\n        .checked_to_num::<u8>()?;\n\n    Some((ibrd, fbrd))\n}\n\nfn read_with_error(r: Regs) -> Result<u8, Error> {\n    let rx = r.rxdata().read();\n\n    if rx.frmerr() {\n        return Err(Error::Framing);\n    } else if rx.parerr() {\n        return Err(Error::Parity);\n    } else if rx.brkerr() {\n        return Err(Error::Break);\n    } else if rx.ovrerr() {\n        return Err(Error::Overrun);\n    } else if rx.nerr() {\n        return Err(Error::Noise);\n    }\n\n    Ok(rx.data())\n}\n\n/// This function assumes CTL0.ENABLE is set (for errata cases).\nfn busy(r: Regs) -> bool {\n    // Errata UART_ERR_08\n    if cfg!(any(\n        mspm0g151x, mspm0g351x, mspm0l110x, mspm0l130x, mspm0l134x, mspm0c110x,\n    )) {\n        let stat = r.stat().read();\n        // \"Poll TXFIFO status and the CTL0.ENABLE register bit to identify BUSY status.\"\n        !stat.txfe()\n    } else {\n        r.stat().read().busy()\n    }\n}\n\n// TODO: Implement when dma uart is implemented.\nfn dma_enabled(_r: Regs) -> bool {\n    false\n}\n\npub(crate) trait SealedInstance {\n    fn info() -> &'static Info;\n    fn state() -> &'static State;\n    fn buffered_state() -> &'static BufferedState;\n}\n\nmacro_rules! impl_uart_instance {\n    ($instance: ident) => {\n        impl crate::uart::SealedInstance for crate::peripherals::$instance {\n            fn info() -> &'static crate::uart::Info {\n                use crate::interrupt::typelevel::Interrupt;\n                use crate::uart::Info;\n\n                const INFO: Info = Info {\n                    regs: crate::pac::$instance,\n                    interrupt: crate::interrupt::typelevel::$instance::IRQ,\n                };\n                &INFO\n            }\n\n            fn state() -> &'static crate::uart::State {\n                use crate::uart::State;\n\n                static STATE: State = State::new();\n                &STATE\n            }\n\n            fn buffered_state() -> &'static crate::uart::BufferedState {\n                use crate::uart::BufferedState;\n\n                static STATE: BufferedState = BufferedState::new();\n                &STATE\n            }\n        }\n\n        impl crate::uart::Instance for crate::peripherals::$instance {\n            type Interrupt = crate::interrupt::typelevel::$instance;\n        }\n    };\n}\n\nmacro_rules! impl_uart_tx_pin {\n    ($instance: ident, $pin: ident, $pf: expr) => {\n        impl crate::uart::TxPin<crate::peripherals::$instance> for crate::peripherals::$pin {\n            fn pf_num(&self) -> u8 {\n                $pf\n            }\n        }\n    };\n}\n\nmacro_rules! impl_uart_rx_pin {\n    ($instance: ident, $pin: ident, $pf: expr) => {\n        impl crate::uart::RxPin<crate::peripherals::$instance> for crate::peripherals::$pin {\n            fn pf_num(&self) -> u8 {\n                $pf\n            }\n        }\n    };\n}\n\nmacro_rules! impl_uart_cts_pin {\n    ($instance: ident, $pin: ident, $pf: expr) => {\n        impl crate::uart::CtsPin<crate::peripherals::$instance> for crate::peripherals::$pin {\n            fn pf_num(&self) -> u8 {\n                $pf\n            }\n        }\n    };\n}\n\nmacro_rules! impl_uart_rts_pin {\n    ($instance: ident, $pin: ident, $pf: expr) => {\n        impl crate::uart::RtsPin<crate::peripherals::$instance> for crate::peripherals::$pin {\n            fn pf_num(&self) -> u8 {\n                $pf\n            }\n        }\n    };\n}\n\n#[cfg(test)]\nmod tests {\n    use super::calculate_brd;\n\n    /// This is a smoke test based on the example in SLAU 846 section 18.2.3.4.\n    #[test]\n    fn datasheet() {\n        let brd = calculate_brd(40_000_000, 1, 19200, 16);\n\n        assert!(matches!(brd, Some((130, 13))));\n    }\n}\n"
  },
  {
    "path": "embassy-mspm0/src/wwdt.rs",
    "content": "//! Window Watchdog Timer (WWDT) driver.\n//!\n//! This HAL implements a basic window watchdog timer with handles.\n\n#![macro_use]\n\nuse embassy_hal_internal::PeripheralType;\n\nuse crate::Peri;\nuse crate::pac::wwdt::{Wwdt as Regs, vals};\nuse crate::pac::{self};\n\n/// Possible watchdog timeout values.\n#[derive(Clone, Copy, PartialEq, Eq, Debug)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub enum Timeout {\n    USec1950,\n    USec3910,\n    USec5860,\n    USec7810,\n    USec9770,\n    USec11720,\n    USec13670,\n    USec15630,\n    USec23440,\n    USec31250,\n    USec32250,\n    USec39060,\n    USec46880,\n    USec54690,\n    USec62500,\n    USec93750,\n    USec125000,\n    USec156250,\n    USec187500,\n    USec218750,\n    MSec130,\n    MSec250,\n    MSec380,\n    MSec500,\n    MSec630,\n    MSec750,\n    MSec880,\n    Sec1,\n    Sec2,\n    Sec3,\n    Sec4,\n    Sec5,\n    Sec6,\n    Sec7,\n    Sec8,\n    Sec16,\n    Sec24,\n    Sec32,\n    Sec40,\n    Sec48,\n    Sec56,\n    Sec64,\n    Sec128,  // 2.13 min\n    Sec192,  // 3.20 min\n    Sec256,  // 4.27 min\n    Sec320,  // 5.33 min\n    Sec384,  // 6.40 min\n    Sec448,  // 7.47 min\n    Sec512,  // 8.53 min\n    Sec1024, // 17.07 min\n    Sec2048, // 34.13 min\n    Sec3072, // 51.20 min\n    Sec4096, // 68.27 min\n    Sec5120, // 85.33 min\n    Sec6144, // 102.40 min\n    Sec7168, // 119.47 min\n    Sec8192, // 136.53 min\n}\n\nimpl Timeout {\n    fn get_period(self) -> vals::Per {\n        match self {\n            //  period count is 2**25\n            Self::Sec1024\n            | Self::Sec2048\n            | Self::Sec3072\n            | Self::Sec4096\n            | Self::Sec5120\n            | Self::Sec6144\n            | Self::Sec7168\n            | Self::Sec8192 => vals::Per::EN_25,\n            //  period count is 2**21\n            Self::Sec64\n            | Self::Sec128\n            | Self::Sec192\n            | Self::Sec256\n            | Self::Sec320\n            | Self::Sec384\n            | Self::Sec448\n            | Self::Sec512 => vals::Per::EN_21,\n            //  period count is 2**18\n            Self::Sec8 | Self::Sec16 | Self::Sec24 | Self::Sec32 | Self::Sec40 | Self::Sec48 | Self::Sec56 => {\n                vals::Per::EN_18\n            }\n            //  period count is 2**15\n            Self::Sec1 | Self::Sec2 | Self::Sec3 | Self::Sec4 | Self::Sec5 | Self::Sec6 | Self::Sec7 => {\n                vals::Per::EN_15\n            }\n            //  period count is 2**12\n            Self::MSec130\n            | Self::MSec250\n            | Self::MSec380\n            | Self::MSec500\n            | Self::MSec630\n            | Self::MSec750\n            | Self::MSec880 => vals::Per::EN_12,\n            //  period count is 2**10\n            Self::USec31250\n            | Self::USec62500\n            | Self::USec93750\n            | Self::USec125000\n            | Self::USec156250\n            | Self::USec187500\n            | Self::USec218750 => vals::Per::EN_10,\n            //  period count is 2**8\n            Self::USec7810\n            | Self::USec15630\n            | Self::USec23440\n            | Self::USec32250\n            | Self::USec39060\n            | Self::USec46880\n            | Self::USec54690 => vals::Per::EN_8,\n            //  period count is 2**6\n            Self::USec1950 | Self::USec3910 | Self::USec5860 | Self::USec9770 | Self::USec11720 | Self::USec13670 => {\n                vals::Per::EN_6\n            }\n        }\n    }\n\n    fn get_clkdiv(self) -> u8 {\n        match self {\n            //  divide by 1\n            Self::USec1950\n            | Self::USec7810\n            | Self::USec31250\n            | Self::MSec130\n            | Self::Sec1\n            | Self::Sec8\n            | Self::Sec64\n            | Self::Sec1024 => 0u8,\n            //  divide by 2\n            Self::USec3910\n            | Self::USec15630\n            | Self::USec62500\n            | Self::MSec250\n            | Self::Sec2\n            | Self::Sec16\n            | Self::Sec128\n            | Self::Sec2048 => 1u8,\n            //  divide by 3\n            Self::USec5860\n            | Self::USec23440\n            | Self::USec93750\n            | Self::MSec380\n            | Self::Sec3\n            | Self::Sec24\n            | Self::Sec192\n            | Self::Sec3072 => 2u8,\n            //  divide by 4\n            Self::USec32250\n            | Self::USec125000\n            | Self::MSec500\n            | Self::Sec4\n            | Self::Sec32\n            | Self::Sec256\n            | Self::Sec4096 => 3u8,\n            //  divide by 5\n            Self::USec9770\n            | Self::USec39060\n            | Self::USec156250\n            | Self::MSec630\n            | Self::Sec5\n            | Self::Sec40\n            | Self::Sec320\n            | Self::Sec5120 => 4u8,\n            //  divide by 6\n            Self::USec11720\n            | Self::USec46880\n            | Self::USec187500\n            | Self::MSec750\n            | Self::Sec6\n            | Self::Sec48\n            | Self::Sec384\n            | Self::Sec6144 => 5u8,\n            //  divide by 7\n            Self::USec13670\n            | Self::USec54690\n            | Self::USec218750\n            | Self::MSec880\n            | Self::Sec7\n            | Self::Sec56\n            | Self::Sec448\n            | Self::Sec7168 => 6u8,\n            //  divide by 8\n            Self::Sec512 | Self::Sec8192 => 7u8,\n        }\n    }\n}\n\n/// Timeout percentage that is treated as \"too early\" and generates violation.\n#[derive(Clone, Copy, PartialEq, Eq, Debug)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub enum ClosedWindowPercentage {\n    // window period is not used\n    Zero,\n    // 12.5% percents\n    Twelve,\n    // 18.75% percents\n    Eighteen,\n    // 25% percents\n    TwentyFive,\n    // 50% percents\n    Fifty,\n    // 75% percents\n    SeventyFive,\n    // 81.25% percents\n    EightyOne,\n    // 87.5% percents\n    EightySeven,\n}\n\nimpl ClosedWindowPercentage {\n    fn get_native_size(self) -> vals::Window {\n        match self {\n            Self::Zero => vals::Window::SIZE_0,\n            Self::Twelve => vals::Window::SIZE_12,\n            Self::Eighteen => vals::Window::SIZE_18,\n            Self::TwentyFive => vals::Window::SIZE_25,\n            Self::Fifty => vals::Window::SIZE_50,\n            Self::SeventyFive => vals::Window::SIZE_75,\n            Self::EightyOne => vals::Window::SIZE_81,\n            Self::EightySeven => vals::Window::SIZE_87,\n        }\n    }\n}\n\n#[non_exhaustive]\n#[derive(Clone, Copy, PartialEq, Eq, Debug)]\n/// Watchdog Config\npub struct Config {\n    /// Watchdog timeout\n    pub timeout: Timeout,\n\n    /// closed window percentage\n    pub closed_window: ClosedWindowPercentage,\n}\n\nimpl Default for Config {\n    fn default() -> Self {\n        Self {\n            timeout: Timeout::Sec1,\n            closed_window: ClosedWindowPercentage::Zero,\n        }\n    }\n}\n\npub struct Watchdog {\n    regs: &'static Regs,\n}\n\nimpl Watchdog {\n    /// Watchdog initialization.\n    pub fn new<T: Instance>(_instance: Peri<T>, config: Config) -> Self {\n        // Init power for watchdog\n        T::regs().gprcm(0).rstctl().write(|w| {\n            w.set_resetstkyclr(true);\n            w.set_resetassert(true);\n            w.set_key(vals::ResetKey::KEY);\n        });\n\n        // Enable power for watchdog\n        T::regs().gprcm(0).pwren().write(|w| {\n            w.set_enable(true);\n            w.set_key(vals::PwrenKey::KEY);\n        });\n\n        // init delay, 16 cycles\n        cortex_m::asm::delay(16);\n\n        critical_section::with(|_| {\n            // make sure watchdog triggers BOOTRST\n            pac::SYSCTL.systemcfg().modify(|w| {\n                if *T::regs() == pac::WWDT0 {\n                    w.set_wwdtlp0rstdis(false);\n                }\n\n                #[cfg(any(mspm0g110x, mspm0g150x, mspm0g151x, mspm0g310x, mspm0g350x, mspm0g351x))]\n                if *T::regs() == pac::WWDT1 {\n                    w.set_wwdtlp1rstdis(false);\n                }\n            });\n        });\n\n        //init watchdog\n        T::regs().wwdtctl0().write(|w| {\n            w.set_clkdiv(config.timeout.get_clkdiv());\n            w.set_per(config.timeout.get_period());\n            w.set_mode(vals::Mode::WINDOW);\n            w.set_window0(config.closed_window.get_native_size());\n            w.set_window1(vals::Window::SIZE_0);\n            w.set_key(vals::Wwdtctl0Key::KEY);\n        });\n\n        // Set Window0 as active window\n        T::regs().wwdtctl1().write(|w| {\n            w.set_winsel(vals::Winsel::WIN0);\n            w.set_key(vals::Wwdtctl1Key::KEY);\n        });\n\n        Self { regs: T::regs() }\n    }\n\n    /// Pet (reload, refresh) the watchdog.\n    pub fn pet(&mut self) {\n        self.regs.wwdtcntrst().write(|w| {\n            w.set_restart(vals::WwdtcntrstRestart::RESTART);\n        });\n    }\n}\n\npub(crate) trait SealedInstance {\n    fn regs() -> &'static Regs;\n}\n\n/// WWDT instance trait\n#[allow(private_bounds)]\npub trait Instance: SealedInstance + PeripheralType {}\n\nmacro_rules! impl_wwdt_instance {\n    ($instance: ident) => {\n        impl crate::wwdt::SealedInstance for crate::peripherals::$instance {\n            fn regs() -> &'static crate::pac::wwdt::Wwdt {\n                &crate::pac::$instance\n            }\n        }\n\n        impl crate::wwdt::Instance for crate::peripherals::$instance {}\n    };\n}\n"
  },
  {
    "path": "embassy-net/CHANGELOG.md",
    "content": "# Changelog\n\nAll notable changes to this project will be documented in this file.\n\nThe format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),\nand this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).\n\n<!-- next-header -->\n## Unreleased - ReleaseDate\n\n## 0.9.0 - 2026-03-10\n\n- raw: Removed unnecessary Driver type parameter from `RawSocket::new`\n- `{UdpSocket, IcmpSocket}::send_to_with` support writing less than `max_size` into the buffer by returning the number of bytes written from the closure\n- Update embassy-sync 0.8.0\n\n## 0.8.0 - 2026-01-04\n\n- tcp: Add `set_nagle_enabled()` to control TcpSocket nagle algorithm.\n- update to embedded-io 0.7\n- update to embedded-nal 0.9\n\n## 0.7.1 - 2025-08-26\n\nNo unreleased changes yet... Quick, go send a PR!\n\n## 0.7 - 2025-05-06\n\n- don't infinite loop if udp::send methods receive a buffer too large to ever be sent\n- add ICMP sockets and a ping utility\n- configurable rate_limit for the ping utility\n- Feature match udp sockets\n\n## 0.6 - 2025-01-05\n\n- Make `Config` constructors `const`\n- The `std` feature has been removed\n- Updated `embassy-time` to v0.4\n\n## 0.5 - 2024-11-28\n\n- Refactor the API structure, simplifying lifetimes and generics.\n    - Stack is now a thin handle that implements `Copy+Clone`. Instead of passing `&Stack` around, you can now pass `Stack`.\n    - `Stack` and `DnsSocket` no longer need a generic parameter for the device driver.\n    - The `run()` method has been moved to a new `Runner` struct.\n    - Sockets are covariant wrt their lifetime.\n    - An implication of the refactor is now you need only one `StaticCell` instead of two if you need to share the network stack between tasks.\n- Use standard `core::net` IP types instead of custom ones from smoltcp.\n- Update to `smoltcp` v0.12.\n- Add `mdns` Cargo feature.\n- dns: properly handle `AddrType::Either` in `get_host_by_name()`\n- dns: truncate instead of panic if the DHCP server gives us more DNS servers than the configured maximum.\n- stack: add `wait_link_up()`, `wait_link_down()`, `wait_config_down()`.\n- tcp: Add `recv_queue()`, `send_queue()`.\n- tcp: Add `wait_read_ready()`, `wait_write_ready()`.\n- tcp: allow setting timeout through `embedded-nal` client.\n- tcp: fix `flush()` hanging forever if socket is closed with pending data.\n- tcp: fix `flush()` not waiting for ACK of FIN.\n- tcp: implement `ReadReady`, `WriteReady` traits from `embedded-io`.\n- udp, raw: Add `wait_send_ready()`, `wait_recv_ready()`, `flush()`.\n- udp: add `recv_from_with()`, `send_to_with()` methods, allowing for IO with one less copy.\n- udp: send/recv now takes/returns full `UdpMetadata` instead of just the remote `IpEndpoint`.\n- raw: add raw sockets.\n\n\n## 0.4 - 2024-01-11\n\n- Update to `embassy-time` v0.3.\n\n## 0.3 - 2024-01-04\n\n- Added `ReadReady` and `WriteReady` impls on `TcpSocket`.\n- Avoid never resolving `TcpIo::read` when the output buffer is empty.\n- Update to `smoltcp` v0.11.\n- Forward constants from `smoltcp` in DNS query results so changing DNS result size in `smoltcp` properly propagates.\n- Removed the nightly feature.\n\n## 0.2.1 - 2023-10-31\n\n- Re-add impl_trait_projections\n- Fix: Reset DHCP socket when the link up is detected \n\n## 0.2.0 - 2023-10-18\n\n- Re-export `smoltcp::wire::IpEndpoint`\n- Add poll functions on UdpSocket\n- Make dual-stack work in embassy-net\n- Fix multicast support\n- Allow ethernet and 802.15.4 to coexist\n- Add IEEE802.15.4 address to embassy net Stack\n- Use HardwareAddress in Driver\n- Add async versions of smoltcp's `send` and `recv` closure based API\n- add error translation to tcp errors\n- Forward TCP/UDP socket capacity impls\n- allow changing IP config at runtime\n- allow non-'static drivers\n- Remove impl_trait_projections\n- update embedded-io, embedded-nal-async\n- add support for dhcp hostname option\n- Wake stack's task after queueing a DNS query\n\n## 0.1.0 - 2023-06-29\n\n- First release\n"
  },
  {
    "path": "embassy-net/Cargo.toml",
    "content": "[package]\nname = \"embassy-net\"\nversion = \"0.9.0\"\nedition = \"2024\"\nlicense = \"MIT OR Apache-2.0\"\ndescription = \"Async TCP/IP network stack for embedded systems\"\nrepository = \"https://github.com/embassy-rs/embassy\"\ndocumentation = \"https://docs.embassy.dev/embassy-net\"\ncategories = [\n    \"embedded\",\n    \"no-std\",\n    \"asynchronous\",\n    \"network-programming\",\n]\n\n[package.metadata.embassy]\nbuild = [\n    {target = \"thumbv7em-none-eabi\", features = [\"defmt\", \"dns\", \"medium-ethernet\", \"packet-trace\", \"proto-ipv4\", \"tcp\", \"udp\"]},\n    {target = \"thumbv7em-none-eabi\", features = [\"defmt\", \"dns\", \"medium-ethernet\", \"multicast\", \"proto-ipv4\", \"tcp\", \"udp\"]},\n    {target = \"thumbv7em-none-eabi\", features = [\"defmt\", \"dhcpv4\", \"dns\", \"medium-ethernet\", \"tcp\", \"udp\"]},\n    {target = \"thumbv7em-none-eabi\", features = [\"defmt\", \"dhcpv4\", \"dhcpv4-hostname\", \"dns\", \"medium-ethernet\", \"tcp\", \"udp\"]},\n    {target = \"thumbv7em-none-eabi\", features = [\"defmt\", \"dhcpv4\", \"dhcpv4-hostname\", \"dns\", \"medium-ethernet\", \"slaac\", \"tcp\", \"udp\"]},\n    {target = \"thumbv7em-none-eabi\", features = [\"defmt\", \"dns\", \"medium-ethernet\", \"proto-ipv6\", \"tcp\", \"udp\"]},\n    {target = \"thumbv7em-none-eabi\", features = [\"defmt\", \"dns\", \"medium-ethernet\", \"proto-ipv6\", \"slaac\", \"tcp\", \"udp\"]},\n    {target = \"thumbv7em-none-eabi\", features = [\"defmt\", \"dns\", \"medium-ieee802154\", \"proto-ipv6\", \"tcp\", \"udp\"]},\n    {target = \"thumbv7em-none-eabi\", features = [\"defmt\", \"dns\", \"medium-ethernet\", \"medium-ieee802154\", \"proto-ipv6\", \"tcp\", \"udp\"]},\n    {target = \"thumbv7em-none-eabi\", features = [\"defmt\", \"dns\", \"medium-ethernet\", \"proto-ipv4\", \"proto-ipv6\", \"tcp\", \"udp\"]},\n    {target = \"thumbv7em-none-eabi\", features = [\"defmt\", \"dns\", \"medium-ip\", \"proto-ipv4\", \"proto-ipv6\", \"tcp\", \"udp\"]},\n    {target = \"thumbv7em-none-eabi\", features = [\"defmt\", \"dns\", \"medium-ethernet\", \"medium-ip\", \"proto-ipv4\", \"proto-ipv6\", \"tcp\", \"udp\"]},\n    {target = \"thumbv7em-none-eabi\", features = [\"defmt\", \"dns\", \"medium-ethernet\", \"medium-ieee802154\", \"medium-ip\", \"proto-ipv4\", \"proto-ipv6\", \"tcp\", \"udp\"]},\n    # Xtensa builds\n    {group = \"xtensa\", build-std = [\"core\", \"alloc\"],  target = \"xtensa-esp32-none-elf\", features = [\"defmt\", \"tcp\", \"udp\", \"dns\", \"proto-ipv4\", \"medium-ethernet\", \"packet-trace\"]},\n    {group = \"xtensa\", build-std = [\"core\", \"alloc\"],  target = \"xtensa-esp32-none-elf\", features = [\"defmt\", \"tcp\", \"udp\", \"dns\", \"proto-ipv4\", \"multicast\", \"medium-ethernet\"]},\n    {group = \"xtensa\", build-std = [\"core\", \"alloc\"],  target = \"xtensa-esp32-none-elf\", features = [\"defmt\", \"tcp\", \"udp\", \"dns\", \"dhcpv4\", \"medium-ethernet\"]},\n    {group = \"xtensa\", build-std = [\"core\", \"alloc\"],  target = \"xtensa-esp32-none-elf\", features = [\"defmt\", \"tcp\", \"udp\", \"dns\", \"dhcpv4\", \"medium-ethernet\", \"dhcpv4-hostname\"]},\n    {group = \"xtensa\", build-std = [\"core\", \"alloc\"],  target = \"xtensa-esp32-none-elf\", features = [\"defmt\", \"tcp\", \"udp\", \"dns\", \"proto-ipv6\", \"medium-ethernet\"]},\n    {group = \"xtensa\", build-std = [\"core\", \"alloc\"],  target = \"xtensa-esp32-none-elf\", features = [\"defmt\", \"tcp\", \"udp\", \"dns\", \"proto-ipv6\", \"medium-ieee802154\"]},\n    {group = \"xtensa\", build-std = [\"core\", \"alloc\"],  target = \"xtensa-esp32-none-elf\", features = [\"defmt\", \"tcp\", \"udp\", \"dns\", \"proto-ipv6\", \"medium-ethernet\", \"medium-ieee802154\"]},\n    {group = \"xtensa\", build-std = [\"core\", \"alloc\"],  target = \"xtensa-esp32-none-elf\", features = [\"defmt\", \"tcp\", \"udp\", \"dns\", \"proto-ipv6\", \"medium-ethernet\"]},\n    {group = \"xtensa\", build-std = [\"core\", \"alloc\"],  target = \"xtensa-esp32-none-elf\", features = [\"defmt\", \"tcp\", \"udp\", \"dns\", \"proto-ipv4\", \"proto-ipv6\", \"medium-ethernet\"]},\n    {group = \"xtensa\", build-std = [\"core\", \"alloc\"],  target = \"xtensa-esp32-none-elf\", features = [\"defmt\", \"tcp\", \"udp\", \"dns\", \"proto-ipv4\", \"proto-ipv6\", \"medium-ip\"]},\n    {group = \"xtensa\", build-std = [\"core\", \"alloc\"],  target = \"xtensa-esp32-none-elf\", features = [\"defmt\", \"tcp\", \"udp\", \"dns\", \"proto-ipv4\", \"proto-ipv6\", \"medium-ip\", \"medium-ethernet\"]},\n    {group = \"xtensa\", build-std = [\"core\", \"alloc\"],  target = \"xtensa-esp32-none-elf\", features = [\"defmt\", \"tcp\", \"udp\", \"dns\", \"proto-ipv4\", \"proto-ipv6\", \"medium-ip\", \"medium-ethernet\", \"medium-ieee802154\"]},\n]\n\n[package.metadata.embassy_docs]\nsrc_base = \"https://github.com/embassy-rs/embassy/blob/embassy-net-v$VERSION/embassy-net/src/\"\nsrc_base_git = \"https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-net/src/\"\nfeatures = [\"defmt\", \"tcp\", \"udp\", \"raw\", \"dns\", \"icmp\", \"dhcpv4\", \"proto-ipv6\", \"medium-ethernet\", \"medium-ip\", \"medium-ieee802154\", \"multicast\", \"dhcpv4-hostname\"]\ntarget = \"thumbv7em-none-eabi\"\n\n[package.metadata.docs.rs]\nfeatures = [\"defmt\", \"tcp\", \"udp\", \"raw\", \"dns\", \"icmp\", \"dhcpv4\", \"proto-ipv6\", \"medium-ethernet\", \"medium-ip\", \"medium-ieee802154\", \"multicast\", \"dhcpv4-hostname\"]\n\n[features]\n## Enable defmt\ndefmt = [\"dep:defmt\", \"smoltcp/defmt\", \"embassy-net-driver/defmt\", \"embassy-time/defmt\", \"heapless/defmt\", \"defmt?/ip_in_core\"]\n## Enable log\nlog = [\"dep:log\"]\n\n## Trace all raw received and transmitted packets using defmt or log.\npacket-trace = []\n\n#! Many of the following feature flags are re-exports of smoltcp feature flags. See \n#! the [smoltcp feature flag documentation](https://github.com/smoltcp-rs/smoltcp#feature-flags)\n#! for more details\n\n## Enable ICMP support\nicmp = [\"smoltcp/socket-icmp\"]\n## ICMP echo request auto reply\nauto-icmp-echo-reply = [\"smoltcp/auto-icmp-echo-reply\"]\n## Enable UDP support\nudp = [\"smoltcp/socket-udp\"]\n## Enable Raw support\nraw = [\"smoltcp/socket-raw\"]\n## Enable TCP support\ntcp = [\"smoltcp/socket-tcp\"]\n## Enable DNS support\ndns = [\"smoltcp/socket-dns\", \"smoltcp/proto-dns\"]\n## Enable mDNS support\nmdns = [\"dns\", \"smoltcp/socket-mdns\"]\n## Enable DHCPv4 support\ndhcpv4 = [\"proto-ipv4\", \"medium-ethernet\", \"smoltcp/socket-dhcpv4\"]\n## Enable DHCPv4 support with hostname\ndhcpv4-hostname = [\"dhcpv4\"]\n## Enable IPv4 support\nproto-ipv4 = [\"smoltcp/proto-ipv4\"]\n## Enable IPv6 support\nproto-ipv6 = [\"smoltcp/proto-ipv6\"]\n## Enable the Ethernet medium\nmedium-ethernet = [\"smoltcp/medium-ethernet\"]\n## Enable the IP medium\nmedium-ip = [\"smoltcp/medium-ip\"]\n## Enable the IEEE 802.15.4 medium\nmedium-ieee802154 = [\"smoltcp/medium-ieee802154\"]\n## Enable multicast support (for both ipv4 and/or ipv6 if enabled)\nmulticast = [\"smoltcp/multicast\"]\n## Enable stateless address autoconfiguration for ipv6\nslaac = [\"proto-ipv6\", \"multicast\", \"smoltcp/proto-ipv6-slaac\"]\n## Enable smoltcp std feature (necessary if using \"managed\" crate std feature)\nstd = [\"smoltcp/std\"]\n## Enable smoltcp alloc feature (necessary if using \"managed\" crate alloc feature)\nalloc = [\"smoltcp/alloc\"]\n\n[dependencies]\n\ndefmt = { version = \"1.0.1\", optional = true }\nlog = { version = \"0.4.14\", optional = true }\n\nsmoltcp = {  version = \"0.13.0\", default-features = false, features = [\n  \"socket\",\n  \"async\",\n] }\n\nembassy-net-driver = { version = \"0.2.0\", path = \"../embassy-net-driver\" }\nembassy-time = { version = \"0.5.1\", path = \"../embassy-time\" }\nembassy-sync = { version = \"0.8.0\", path = \"../embassy-sync\" }\nembedded-io-async = { version = \"0.7.0\" }\n\nmanaged = { version = \"0.8.0\", default-features = false, features = [ \"map\" ] }\nheapless = { version = \"0.9\", default-features = false }\nembedded-nal-async = \"0.9.0\"\ndocument-features = \"0.2.7\"\n"
  },
  {
    "path": "embassy-net/README.md",
    "content": "# embassy-net\n\n`embassy-net` is a no-std no-alloc async network stack, designed for embedded systems.\n\nIt builds on [`smoltcp`](https://github.com/smoltcp-rs/smoltcp). It provides a higher-level and more opinionated\nAPI. It glues together the components provided by `smoltcp`, handling the low-level details with defaults and\nmemory management designed to work well for embedded systems, aiming for a more \"Just Works\" experience.\n\n## Features\n\n- IPv4, IPv6\n- Ethernet and bare-IP mediums.\n- TCP, UDP, DNS, DHCPv4\n- TCP sockets implement the `embedded-io` async traits.\n- Multicast\n\nSee the [`smoltcp`](https://github.com/smoltcp-rs/smoltcp) README for a detailed list of implemented and\nunimplemented features of the network protocols.\n\n## Hardware support\n\n- [`esp-wifi`](https://github.com/esp-rs/esp-wifi) for WiFi support on bare-metal ESP32 chips. Maintained by Espressif.\n- [`cyw43`](https://github.com/embassy-rs/embassy/tree/main/cyw43) for WiFi on CYW43xx chips, used in the Raspberry Pi Pico W\n- [`embassy-usb`](https://github.com/embassy-rs/embassy/tree/main/embassy-usb) for Ethernet-over-USB (CDC NCM) support.\n- [`embassy-stm32`](https://github.com/embassy-rs/embassy/tree/main/embassy-stm32) for the builtin Ethernet MAC in all STM32 chips (STM32F1, STM32F2, STM32F4, STM32F7, STM32H7, STM32H5).\n- [`embassy-net-wiznet`](https://github.com/embassy-rs/embassy/tree/main/embassy-net-wiznet) for Wiznet SPI Ethernet MAC+PHY chips (W5100S, W5500)\n- [`embassy-net-esp-hosted`](https://github.com/embassy-rs/embassy/tree/main/embassy-net-esp-hosted) for using ESP32 chips with the [`esp-hosted`](https://github.com/espressif/esp-hosted) firmware as WiFi adapters for another non-ESP32 MCU.\n- [`embassy-nrf`](https://github.com/embassy-rs/embassy/tree/main/embassy-nrf) for IEEE 802.15.4 support on nrf chips.\n\n## Examples\n\n- For usage with Embassy HALs and network chip drivers, search [here](https://github.com/embassy-rs/embassy/tree/main/examples) for `eth` or `wifi`.\n- The [`esp-wifi` repo](https://github.com/esp-rs/esp-wifi) has examples for use on bare-metal ESP32 chips.\n- For usage on `std` platforms, see [the `std` examples](https://github.com/embassy-rs/embassy/tree/main/examples/std/src/bin)\n\n## Adding support for new hardware\n\nTo add `embassy-net` support for new hardware (i.e. a new Ethernet or WiFi chip, or\nan Ethernet/WiFi MCU peripheral), you have to implement the [`embassy-net-driver`](https://crates.io/crates/embassy-net-driver)\ntraits.\n\nAlternatively, [`embassy-net-driver-channel`](https://crates.io/crates/embassy-net-driver-channel) provides a higher-level API\nto construct a driver that processes packets in its own background task and communicates with the `embassy-net` task via\npacket queues for RX and TX.\n\nDrivers should depend only on `embassy-net-driver` or `embassy-net-driver-channel`. Never on the main `embassy-net` crate.\nThis allows existing drivers to continue working for newer `embassy-net` major versions, without needing an update, if the driver\ntrait has not had breaking changes.\n\n## Interoperability\n\nThis crate can run on any executor.\n\n[`embassy-time`](https://crates.io/crates/embassy-time) is used for timekeeping and timeouts. You must\nlink an `embassy-time` driver in your project to use this crate.\n"
  },
  {
    "path": "embassy-net/src/dns.rs",
    "content": "//! DNS client compatible with the `embedded-nal-async` traits.\n//!\n//! This exists only for compatibility with crates that use `embedded-nal-async`.\n//! Prefer using [`Stack::dns_query`](crate::Stack::dns_query) directly if you're\n//! not using `embedded-nal-async`.\n\nuse heapless::Vec;\npub use smoltcp::socket::dns::{DnsQuery, Socket};\npub(crate) use smoltcp::socket::dns::{GetQueryResultError, StartQueryError};\npub use smoltcp::wire::{DnsQueryType, IpAddress};\n\nuse crate::Stack;\n\n/// Errors returned by DnsSocket.\n#[derive(Debug, PartialEq, Eq, Clone, Copy)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub enum Error {\n    /// Invalid name\n    InvalidName,\n    /// Name too long\n    NameTooLong,\n    /// Name lookup failed\n    Failed,\n}\n\nimpl From<GetQueryResultError> for Error {\n    fn from(_: GetQueryResultError) -> Self {\n        Self::Failed\n    }\n}\n\nimpl From<StartQueryError> for Error {\n    fn from(e: StartQueryError) -> Self {\n        match e {\n            StartQueryError::NoFreeSlot => Self::Failed,\n            StartQueryError::InvalidName => Self::InvalidName,\n            StartQueryError::NameTooLong => Self::NameTooLong,\n        }\n    }\n}\n\n/// DNS client compatible with the `embedded-nal-async` traits.\n///\n/// This exists only for compatibility with crates that use `embedded-nal-async`.\n/// Prefer using [`Stack::dns_query`](crate::Stack::dns_query) directly if you're\n/// not using `embedded-nal-async`.\npub struct DnsSocket<'a> {\n    stack: Stack<'a>,\n}\n\nimpl<'a> DnsSocket<'a> {\n    /// Create a new DNS socket using the provided stack.\n    ///\n    /// NOTE: If using DHCP, make sure it has reconfigured the stack to ensure the DNS servers are updated.\n    pub fn new(stack: Stack<'a>) -> Self {\n        Self { stack }\n    }\n\n    /// Make a query for a given name and return the corresponding IP addresses.\n    pub async fn query(\n        &self,\n        name: &str,\n        qtype: DnsQueryType,\n    ) -> Result<Vec<IpAddress, { smoltcp::config::DNS_MAX_RESULT_COUNT }>, Error> {\n        self.stack.dns_query(name, qtype).await\n    }\n}\n\nimpl<'a> embedded_nal_async::Dns for DnsSocket<'a> {\n    type Error = Error;\n\n    async fn get_host_by_name(\n        &self,\n        host: &str,\n        addr_type: embedded_nal_async::AddrType,\n    ) -> Result<core::net::IpAddr, Self::Error> {\n        use core::net::IpAddr;\n\n        use embedded_nal_async::AddrType;\n\n        let (qtype, secondary_qtype) = match addr_type {\n            AddrType::IPv4 => (DnsQueryType::A, None),\n            AddrType::IPv6 => (DnsQueryType::Aaaa, None),\n            AddrType::Either => {\n                #[cfg(not(feature = \"proto-ipv6\"))]\n                let v6_first = false;\n                #[cfg(feature = \"proto-ipv6\")]\n                let v6_first = self.stack.config_v6().is_some();\n                match v6_first {\n                    true => (DnsQueryType::Aaaa, Some(DnsQueryType::A)),\n                    false => (DnsQueryType::A, Some(DnsQueryType::Aaaa)),\n                }\n            }\n        };\n        let mut addrs = self.query(host, qtype).await?;\n        if addrs.is_empty() {\n            if let Some(qtype) = secondary_qtype {\n                addrs = self.query(host, qtype).await?\n            }\n        }\n        if let Some(first) = addrs.get(0) {\n            Ok(match first {\n                #[cfg(feature = \"proto-ipv4\")]\n                IpAddress::Ipv4(addr) => IpAddr::V4(*addr),\n                #[cfg(feature = \"proto-ipv6\")]\n                IpAddress::Ipv6(addr) => IpAddr::V6(*addr),\n            })\n        } else {\n            Err(Error::Failed)\n        }\n    }\n\n    async fn get_host_by_address(&self, _addr: core::net::IpAddr, _result: &mut [u8]) -> Result<usize, Self::Error> {\n        todo!()\n    }\n}\n\nfn _assert_covariant<'a, 'b: 'a>(x: DnsSocket<'b>) -> DnsSocket<'a> {\n    x\n}\n"
  },
  {
    "path": "embassy-net/src/driver_util.rs",
    "content": "use core::task::Context;\n\nuse embassy_net_driver::{Capabilities, Checksum, Driver, RxToken, TxToken};\nuse smoltcp::phy::{self, Medium};\nuse smoltcp::time::Instant;\n\npub(crate) struct DriverAdapter<'d, 'c, T>\nwhere\n    T: Driver,\n{\n    // must be Some when actually using this to rx/tx\n    pub cx: Option<&'d mut Context<'c>>,\n    pub inner: &'d mut T,\n    pub medium: Medium,\n}\n\nimpl<'d, 'c, T> phy::Device for DriverAdapter<'d, 'c, T>\nwhere\n    T: Driver,\n{\n    type RxToken<'a>\n        = RxTokenAdapter<T::RxToken<'a>>\n    where\n        Self: 'a;\n    type TxToken<'a>\n        = TxTokenAdapter<T::TxToken<'a>>\n    where\n        Self: 'a;\n\n    fn receive(&mut self, _timestamp: Instant) -> Option<(Self::RxToken<'_>, Self::TxToken<'_>)> {\n        self.inner\n            .receive(unwrap!(self.cx.as_deref_mut()))\n            .map(|(rx, tx)| (RxTokenAdapter(rx), TxTokenAdapter(tx)))\n    }\n\n    /// Construct a transmit token.\n    fn transmit(&mut self, _timestamp: Instant) -> Option<Self::TxToken<'_>> {\n        self.inner.transmit(unwrap!(self.cx.as_deref_mut())).map(TxTokenAdapter)\n    }\n\n    /// Get a description of device capabilities.\n    fn capabilities(&self) -> phy::DeviceCapabilities {\n        fn convert(c: Checksum) -> phy::Checksum {\n            match c {\n                Checksum::Both => phy::Checksum::Both,\n                Checksum::Tx => phy::Checksum::Tx,\n                Checksum::Rx => phy::Checksum::Rx,\n                Checksum::None => phy::Checksum::None,\n            }\n        }\n        let caps: Capabilities = self.inner.capabilities();\n        let mut smolcaps = phy::DeviceCapabilities::default();\n\n        smolcaps.max_transmission_unit = caps.max_transmission_unit;\n        smolcaps.max_burst_size = caps.max_burst_size;\n        smolcaps.medium = self.medium;\n        smolcaps.checksum.ipv4 = convert(caps.checksum.ipv4);\n        smolcaps.checksum.tcp = convert(caps.checksum.tcp);\n        smolcaps.checksum.udp = convert(caps.checksum.udp);\n        #[cfg(feature = \"proto-ipv4\")]\n        {\n            smolcaps.checksum.icmpv4 = convert(caps.checksum.icmpv4);\n        }\n        #[cfg(feature = \"proto-ipv6\")]\n        {\n            smolcaps.checksum.icmpv6 = convert(caps.checksum.icmpv6);\n        }\n\n        smolcaps\n    }\n}\n\npub(crate) struct RxTokenAdapter<T>(T)\nwhere\n    T: RxToken;\n\nimpl<T> phy::RxToken for RxTokenAdapter<T>\nwhere\n    T: RxToken,\n{\n    fn consume<R, F>(self, f: F) -> R\n    where\n        F: FnOnce(&[u8]) -> R,\n    {\n        self.0.consume(|buf| {\n            #[cfg(feature = \"packet-trace\")]\n            trace!(\"embassy device rx: {:02x}\", buf);\n            f(buf)\n        })\n    }\n}\n\npub(crate) struct TxTokenAdapter<T>(T)\nwhere\n    T: TxToken;\n\nimpl<T> phy::TxToken for TxTokenAdapter<T>\nwhere\n    T: TxToken,\n{\n    fn consume<R, F>(self, len: usize, f: F) -> R\n    where\n        F: FnOnce(&mut [u8]) -> R,\n    {\n        self.0.consume(len, |buf| {\n            let r = f(buf);\n            #[cfg(feature = \"packet-trace\")]\n            trace!(\"embassy device tx: {:02x}\", buf);\n            r\n        })\n    }\n}\n"
  },
  {
    "path": "embassy-net/src/fmt.rs",
    "content": "#![macro_use]\n#![allow(unused)]\n\nuse core::fmt::{Debug, Display, LowerHex};\n\n#[cfg(all(feature = \"defmt\", feature = \"log\"))]\ncompile_error!(\"You may not enable both `defmt` and `log` features.\");\n\n#[collapse_debuginfo(yes)]\nmacro_rules! assert {\n    ($($x:tt)*) => {\n        {\n            #[cfg(not(feature = \"defmt\"))]\n            ::core::assert!($($x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::assert!($($x)*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! assert_eq {\n    ($($x:tt)*) => {\n        {\n            #[cfg(not(feature = \"defmt\"))]\n            ::core::assert_eq!($($x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::assert_eq!($($x)*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! assert_ne {\n    ($($x:tt)*) => {\n        {\n            #[cfg(not(feature = \"defmt\"))]\n            ::core::assert_ne!($($x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::assert_ne!($($x)*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! debug_assert {\n    ($($x:tt)*) => {\n        {\n            #[cfg(not(feature = \"defmt\"))]\n            ::core::debug_assert!($($x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::debug_assert!($($x)*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! debug_assert_eq {\n    ($($x:tt)*) => {\n        {\n            #[cfg(not(feature = \"defmt\"))]\n            ::core::debug_assert_eq!($($x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::debug_assert_eq!($($x)*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! debug_assert_ne {\n    ($($x:tt)*) => {\n        {\n            #[cfg(not(feature = \"defmt\"))]\n            ::core::debug_assert_ne!($($x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::debug_assert_ne!($($x)*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! todo {\n    ($($x:tt)*) => {\n        {\n            #[cfg(not(feature = \"defmt\"))]\n            ::core::todo!($($x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::todo!($($x)*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! unreachable {\n    ($($x:tt)*) => {\n        {\n            #[cfg(not(feature = \"defmt\"))]\n            ::core::unreachable!($($x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::unreachable!($($x)*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! panic {\n    ($($x:tt)*) => {\n        {\n            #[cfg(not(feature = \"defmt\"))]\n            ::core::panic!($($x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::panic!($($x)*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! trace {\n    ($s:literal $(, $x:expr)* $(,)?) => {\n        {\n            #[cfg(feature = \"log\")]\n            ::log::trace!($s $(, $x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::trace!($s $(, $x)*);\n            #[cfg(not(any(feature = \"log\", feature=\"defmt\")))]\n            let _ = ($( & $x ),*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! debug {\n    ($s:literal $(, $x:expr)* $(,)?) => {\n        {\n            #[cfg(feature = \"log\")]\n            ::log::debug!($s $(, $x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::debug!($s $(, $x)*);\n            #[cfg(not(any(feature = \"log\", feature=\"defmt\")))]\n            let _ = ($( & $x ),*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! info {\n    ($s:literal $(, $x:expr)* $(,)?) => {\n        {\n            #[cfg(feature = \"log\")]\n            ::log::info!($s $(, $x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::info!($s $(, $x)*);\n            #[cfg(not(any(feature = \"log\", feature=\"defmt\")))]\n            let _ = ($( & $x ),*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! warn {\n    ($s:literal $(, $x:expr)* $(,)?) => {\n        {\n            #[cfg(feature = \"log\")]\n            ::log::warn!($s $(, $x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::warn!($s $(, $x)*);\n            #[cfg(not(any(feature = \"log\", feature=\"defmt\")))]\n            let _ = ($( & $x ),*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! error {\n    ($s:literal $(, $x:expr)* $(,)?) => {\n        {\n            #[cfg(feature = \"log\")]\n            ::log::error!($s $(, $x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::error!($s $(, $x)*);\n            #[cfg(not(any(feature = \"log\", feature=\"defmt\")))]\n            let _ = ($( & $x ),*);\n        }\n    };\n}\n\n#[cfg(feature = \"defmt\")]\n#[collapse_debuginfo(yes)]\nmacro_rules! unwrap {\n    ($($x:tt)*) => {\n        ::defmt::unwrap!($($x)*)\n    };\n}\n\n#[cfg(not(feature = \"defmt\"))]\n#[collapse_debuginfo(yes)]\nmacro_rules! unwrap {\n    ($arg:expr) => {\n        match $crate::fmt::Try::into_result($arg) {\n            ::core::result::Result::Ok(t) => t,\n            ::core::result::Result::Err(e) => {\n                ::core::panic!(\"unwrap of `{}` failed: {:?}\", ::core::stringify!($arg), e);\n            }\n        }\n    };\n    ($arg:expr, $($msg:expr),+ $(,)? ) => {\n        match $crate::fmt::Try::into_result($arg) {\n            ::core::result::Result::Ok(t) => t,\n            ::core::result::Result::Err(e) => {\n                ::core::panic!(\"unwrap of `{}` failed: {}: {:?}\", ::core::stringify!($arg), ::core::format_args!($($msg,)*), e);\n            }\n        }\n    }\n}\n\n#[derive(Debug, Copy, Clone, Eq, PartialEq)]\npub struct NoneError;\n\npub trait Try {\n    type Ok;\n    type Error;\n    fn into_result(self) -> Result<Self::Ok, Self::Error>;\n}\n\nimpl<T> Try for Option<T> {\n    type Ok = T;\n    type Error = NoneError;\n\n    #[inline]\n    fn into_result(self) -> Result<T, NoneError> {\n        self.ok_or(NoneError)\n    }\n}\n\nimpl<T, E> Try for Result<T, E> {\n    type Ok = T;\n    type Error = E;\n\n    #[inline]\n    fn into_result(self) -> Self {\n        self\n    }\n}\n\npub(crate) struct Bytes<'a>(pub &'a [u8]);\n\nimpl<'a> Debug for Bytes<'a> {\n    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {\n        write!(f, \"{:#02x?}\", self.0)\n    }\n}\n\nimpl<'a> Display for Bytes<'a> {\n    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {\n        write!(f, \"{:#02x?}\", self.0)\n    }\n}\n\nimpl<'a> LowerHex for Bytes<'a> {\n    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {\n        write!(f, \"{:#02x?}\", self.0)\n    }\n}\n\n#[cfg(feature = \"defmt\")]\nimpl<'a> defmt::Format for Bytes<'a> {\n    fn format(&self, fmt: defmt::Formatter) {\n        defmt::write!(fmt, \"{:02x}\", self.0)\n    }\n}\n"
  },
  {
    "path": "embassy-net/src/icmp.rs",
    "content": "//! ICMP sockets.\n\nuse core::future::{Future, poll_fn};\nuse core::mem;\nuse core::task::{Context, Poll};\n\nuse smoltcp::iface::{Interface, SocketHandle};\npub use smoltcp::phy::ChecksumCapabilities;\nuse smoltcp::socket::icmp;\npub use smoltcp::socket::icmp::{Endpoint as IcmpEndpoint, PacketMetadata};\nuse smoltcp::wire::IpAddress;\n#[cfg(feature = \"proto-ipv4\")]\npub use smoltcp::wire::{Icmpv4Message, Icmpv4Packet, Icmpv4Repr};\n#[cfg(feature = \"proto-ipv6\")]\npub use smoltcp::wire::{Icmpv6Message, Icmpv6Packet, Icmpv6Repr};\n\nuse crate::Stack;\n\n/// Error returned by [`IcmpSocket::bind`].\n#[derive(PartialEq, Eq, Clone, Copy, Debug)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub enum BindError {\n    /// The socket was already open.\n    InvalidState,\n    /// The endpoint isn't specified\n    InvalidEndpoint,\n    /// No route to host.\n    NoRoute,\n}\n\n/// Error returned by [`IcmpSocket::send_to`].\n#[derive(PartialEq, Eq, Clone, Copy, Debug)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub enum SendError {\n    /// No route to host.\n    NoRoute,\n    /// Socket not bound to an outgoing port.\n    SocketNotBound,\n    /// There is not enough transmit buffer capacity to ever send this packet.\n    PacketTooLarge,\n}\n\n/// Error returned by [`IcmpSocket::recv_from`].\n#[derive(PartialEq, Eq, Clone, Copy, Debug)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub enum RecvError {\n    /// Provided buffer was smaller than the received packet.\n    Truncated,\n}\n\n/// An ICMP socket.\npub struct IcmpSocket<'a> {\n    stack: Stack<'a>,\n    handle: SocketHandle,\n}\n\nimpl<'a> IcmpSocket<'a> {\n    /// Create a new ICMP socket using the provided stack and buffers.\n    pub fn new(\n        stack: Stack<'a>,\n        rx_meta: &'a mut [PacketMetadata],\n        rx_buffer: &'a mut [u8],\n        tx_meta: &'a mut [PacketMetadata],\n        tx_buffer: &'a mut [u8],\n    ) -> Self {\n        let handle = stack.with_mut(|i| {\n            let rx_meta: &'static mut [PacketMetadata] = unsafe { mem::transmute(rx_meta) };\n            let rx_buffer: &'static mut [u8] = unsafe { mem::transmute(rx_buffer) };\n            let tx_meta: &'static mut [PacketMetadata] = unsafe { mem::transmute(tx_meta) };\n            let tx_buffer: &'static mut [u8] = unsafe { mem::transmute(tx_buffer) };\n            i.sockets.add(icmp::Socket::new(\n                icmp::PacketBuffer::new(rx_meta, rx_buffer),\n                icmp::PacketBuffer::new(tx_meta, tx_buffer),\n            ))\n        });\n\n        Self { stack, handle }\n    }\n\n    /// Bind the socket to the given endpoint.\n    pub fn bind<T>(&mut self, endpoint: T) -> Result<(), BindError>\n    where\n        T: Into<IcmpEndpoint>,\n    {\n        let endpoint = endpoint.into();\n\n        if !endpoint.is_specified() {\n            return Err(BindError::InvalidEndpoint);\n        }\n\n        match self.with_mut(|s, _| s.bind(endpoint)) {\n            Ok(()) => Ok(()),\n            Err(icmp::BindError::InvalidState) => Err(BindError::InvalidState),\n            Err(icmp::BindError::Unaddressable) => Err(BindError::NoRoute),\n        }\n    }\n\n    fn with<R>(&self, f: impl FnOnce(&icmp::Socket, &Interface) -> R) -> R {\n        self.stack.with(|i| {\n            let socket = i.sockets.get::<icmp::Socket>(self.handle);\n            f(socket, &i.iface)\n        })\n    }\n\n    fn with_mut<R>(&self, f: impl FnOnce(&mut icmp::Socket, &mut Interface) -> R) -> R {\n        self.stack.with_mut(|i| {\n            let socket = i.sockets.get_mut::<icmp::Socket>(self.handle);\n            let res = f(socket, &mut i.iface);\n            i.waker.wake();\n            res\n        })\n    }\n\n    /// Wait until the socket becomes readable.\n    ///\n    /// A socket is readable when a packet has been received, or when there are queued packets in\n    /// the buffer.\n    pub fn wait_recv_ready(&self) -> impl Future<Output = ()> + '_ {\n        poll_fn(move |cx| self.poll_recv_ready(cx))\n    }\n\n    /// Wait until a datagram can be read.\n    ///\n    /// When no datagram is readable, this method will return `Poll::Pending` and\n    /// register the current task to be notified when a datagram is received.\n    ///\n    /// When a datagram is received, this method will return `Poll::Ready`.\n    pub fn poll_recv_ready(&self, cx: &mut Context<'_>) -> Poll<()> {\n        self.with_mut(|s, _| {\n            if s.can_recv() {\n                Poll::Ready(())\n            } else {\n                // socket buffer is empty wait until at least one byte has arrived\n                s.register_recv_waker(cx.waker());\n                Poll::Pending\n            }\n        })\n    }\n\n    /// Receive a datagram.\n    ///\n    /// This method will wait until a datagram is received.\n    ///\n    /// Returns the number of bytes received and the remote endpoint.\n    pub fn recv_from<'s>(\n        &'s self,\n        buf: &'s mut [u8],\n    ) -> impl Future<Output = Result<(usize, IpAddress), RecvError>> + 's {\n        poll_fn(|cx| self.poll_recv_from(buf, cx))\n    }\n\n    /// Receive a datagram.\n    ///\n    /// When no datagram is available, this method will return `Poll::Pending` and\n    /// register the current task to be notified when a datagram is received.\n    ///\n    /// When a datagram is received, this method will return `Poll::Ready` with the\n    /// number of bytes received and the remote endpoint.\n    pub fn poll_recv_from(&self, buf: &mut [u8], cx: &mut Context<'_>) -> Poll<Result<(usize, IpAddress), RecvError>> {\n        self.with_mut(|s, _| match s.recv_slice(buf) {\n            Ok((n, meta)) => Poll::Ready(Ok((n, meta))),\n            // No data ready\n            Err(icmp::RecvError::Truncated) => Poll::Ready(Err(RecvError::Truncated)),\n            Err(icmp::RecvError::Exhausted) => {\n                s.register_recv_waker(cx.waker());\n                Poll::Pending\n            }\n        })\n    }\n\n    /// Dequeue a packet received from a remote endpoint and calls the provided function with the\n    /// slice of the packet and the remote endpoint address and returns `Poll::Ready` with the\n    /// function's returned value.\n    ///\n    /// **Note**: when the size of the provided buffer is smaller than the size of the payload,\n    /// the packet is dropped and a `RecvError::Truncated` error is returned.\n    pub async fn recv_from_with<F, R>(&self, f: F) -> Result<R, RecvError>\n    where\n        F: FnOnce((&[u8], IpAddress)) -> R,\n    {\n        let mut f = Some(f);\n        poll_fn(move |cx| {\n            self.with_mut(|s, _| match s.recv() {\n                Ok(x) => Poll::Ready(Ok(unwrap!(f.take())(x))),\n                Err(icmp::RecvError::Exhausted) => {\n                    cx.waker().wake_by_ref();\n                    Poll::Pending\n                }\n                Err(icmp::RecvError::Truncated) => Poll::Ready(Err(RecvError::Truncated)),\n            })\n        })\n        .await\n    }\n\n    /// Wait until the socket becomes writable.\n    ///\n    /// A socket becomes writable when there is space in the buffer, from initial memory or after\n    /// dispatching datagrams on a full buffer.\n    pub fn wait_send_ready(&self) -> impl Future<Output = ()> + '_ {\n        poll_fn(|cx| self.poll_send_ready(cx))\n    }\n\n    /// Wait until a datagram can be sent.\n    ///\n    /// When no datagram can be sent (i.e. the buffer is full), this method will return\n    /// `Poll::Pending` and register the current task to be notified when\n    /// space is freed in the buffer after a datagram has been dispatched.\n    ///\n    /// When a datagram can be sent, this method will return `Poll::Ready`.\n    pub fn poll_send_ready(&self, cx: &mut Context<'_>) -> Poll<()> {\n        self.with_mut(|s, _| {\n            if s.can_send() {\n                Poll::Ready(())\n            } else {\n                // socket buffer is full wait until a datagram has been dispatched\n                s.register_send_waker(cx.waker());\n                Poll::Pending\n            }\n        })\n    }\n\n    /// Send a datagram to the specified remote endpoint.\n    ///\n    /// This method will wait until the datagram has been sent.\n    ///\n    /// If the socket's send buffer is too small to fit `buf`, this method will return `Err(SendError::PacketTooLarge)`\n    ///\n    /// When the remote endpoint is not reachable, this method will return `Err(SendError::NoRoute)`\n    pub async fn send_to<T>(&self, buf: &[u8], remote_endpoint: T) -> Result<(), SendError>\n    where\n        T: Into<IpAddress>,\n    {\n        let remote_endpoint: IpAddress = remote_endpoint.into();\n        poll_fn(move |cx| self.poll_send_to(buf, remote_endpoint, cx)).await\n    }\n\n    /// Send a datagram to the specified remote endpoint.\n    ///\n    /// When the datagram has been sent, this method will return `Poll::Ready(Ok())`.\n    ///\n    /// When the socket's send buffer is full, this method will return `Poll::Pending`\n    /// and register the current task to be notified when the buffer has space available.\n    ///\n    /// If the socket's send buffer is too small to fit `buf`, this method will return `Poll::Ready(Err(SendError::PacketTooLarge))`\n    ///\n    /// When the remote endpoint is not reachable, this method will return `Poll::Ready(Err(Error::NoRoute))`.\n    pub fn poll_send_to<T>(&self, buf: &[u8], remote_endpoint: T, cx: &mut Context<'_>) -> Poll<Result<(), SendError>>\n    where\n        T: Into<IpAddress>,\n    {\n        // Don't need to wake waker in `with_mut` if the buffer will never fit the icmp tx_buffer.\n        let send_capacity_too_small = self.with(|s, _| s.payload_send_capacity() < buf.len());\n        if send_capacity_too_small {\n            return Poll::Ready(Err(SendError::PacketTooLarge));\n        }\n\n        self.with_mut(|s, _| match s.send_slice(buf, remote_endpoint.into()) {\n            // Entire datagram has been sent\n            Ok(()) => Poll::Ready(Ok(())),\n            Err(icmp::SendError::BufferFull) => {\n                s.register_send_waker(cx.waker());\n                Poll::Pending\n            }\n            Err(icmp::SendError::Unaddressable) => {\n                // If no sender/outgoing port is specified, there is not really \"no route\"\n                if s.is_open() {\n                    Poll::Ready(Err(SendError::NoRoute))\n                } else {\n                    Poll::Ready(Err(SendError::SocketNotBound))\n                }\n            }\n        })\n    }\n\n    /// Enqueue a packet to be sent to a given remote address with a zero-copy function.\n    ///\n    /// This method will wait until the buffer can fit the requested size before\n    /// passing it to the closure. The closure returns the number of bytes\n    /// written into the buffer.\n    pub async fn send_to_with<T, F, R>(&mut self, max_size: usize, remote_endpoint: T, f: F) -> Result<R, SendError>\n    where\n        T: Into<IpAddress>,\n        F: FnOnce(&mut [u8]) -> (usize, R),\n    {\n        // Don't need to wake waker in `with_mut` if the buffer will never fit the icmp tx_buffer.\n        let send_capacity_too_small = self.with(|s, _| s.payload_send_capacity() < max_size);\n        if send_capacity_too_small {\n            return Err(SendError::PacketTooLarge);\n        }\n\n        let mut f = Some(f);\n        let remote_endpoint = remote_endpoint.into();\n        poll_fn(move |cx| {\n            self.with_mut(|s, _| {\n                let mut ret = None;\n\n                match s.send_with(max_size, remote_endpoint, |buf| {\n                    let (size, r) = unwrap!(f.take())(buf);\n                    ret = Some(r);\n                    size\n                }) {\n                    Ok(_n) => Poll::Ready(Ok(unwrap!(ret))),\n                    Err(icmp::SendError::BufferFull) => {\n                        s.register_send_waker(cx.waker());\n                        Poll::Pending\n                    }\n                    Err(icmp::SendError::Unaddressable) => Poll::Ready(Err(SendError::NoRoute)),\n                }\n            })\n        })\n        .await\n    }\n\n    /// Flush the socket.\n    ///\n    /// This method will wait until the socket is flushed.\n    pub fn flush(&mut self) -> impl Future<Output = ()> + '_ {\n        poll_fn(|cx| {\n            self.with_mut(|s, _| {\n                if s.send_queue() == 0 {\n                    Poll::Ready(())\n                } else {\n                    s.register_send_waker(cx.waker());\n                    Poll::Pending\n                }\n            })\n        })\n    }\n\n    /// Check whether the socket is open.\n    pub fn is_open(&self) -> bool {\n        self.with(|s, _| s.is_open())\n    }\n\n    /// Returns whether the socket is ready to send data, i.e. it has enough buffer space to hold a packet.\n    pub fn may_send(&self) -> bool {\n        self.with(|s, _| s.can_send())\n    }\n\n    /// Returns whether the socket is ready to receive data, i.e. it has received a packet that's now in the buffer.\n    pub fn may_recv(&self) -> bool {\n        self.with(|s, _| s.can_recv())\n    }\n\n    /// Return the maximum number packets the socket can receive.\n    pub fn packet_recv_capacity(&self) -> usize {\n        self.with(|s, _| s.packet_recv_capacity())\n    }\n\n    /// Return the maximum number packets the socket can receive.\n    pub fn packet_send_capacity(&self) -> usize {\n        self.with(|s, _| s.packet_send_capacity())\n    }\n\n    /// Return the maximum number of bytes inside the recv buffer.\n    pub fn payload_recv_capacity(&self) -> usize {\n        self.with(|s, _| s.payload_recv_capacity())\n    }\n\n    /// Return the maximum number of bytes inside the transmit buffer.\n    pub fn payload_send_capacity(&self) -> usize {\n        self.with(|s, _| s.payload_send_capacity())\n    }\n\n    /// Return the time-to-live (IPv4) or hop limit (IPv6) value used in outgoing packets.\n    pub fn hop_limit(&self) -> Option<u8> {\n        self.with(|s, _| s.hop_limit())\n    }\n\n    /// Set the hop limit field in the IP header of sent packets.\n    pub fn set_hop_limit(&mut self, hop_limit: Option<u8>) {\n        self.with_mut(|s, _| s.set_hop_limit(hop_limit))\n    }\n}\n\nimpl Drop for IcmpSocket<'_> {\n    fn drop(&mut self) {\n        self.stack.with_mut(|i| i.sockets.remove(self.handle));\n    }\n}\n\npub mod ping {\n    //! Ping utilities.\n    //!\n    //! This module allows for an easy ICMP Echo message interface used to\n    //! ping devices with an [ICMP Socket](IcmpSocket).\n    //!\n    //! ## Usage\n    //!\n    //! ```\n    //! use core::net::Ipv4Addr;\n    //! use core::str::FromStr;\n    //!\n    //! use embassy_net::icmp::ping::{PingManager, PingParams};\n    //! use embassy_net::icmp::PacketMetadata;\n    //!\n    //! let mut rx_buffer = [0; 256];\n    //! let mut tx_buffer = [0; 256];\n    //! let mut rx_meta = [PacketMetadata::EMPTY];\n    //! let mut tx_meta = [PacketMetadata::EMPTY];\n    //!\n    //! let mut ping_manager = PingManager::new(stack, &mut rx_meta, &mut rx_buffer, &mut tx_meta, &mut tx_buffer);\n    //! let addr = \"192.168.8.1\";\n    //! let mut ping_params = PingParams::new(Ipv4Addr::from_str(addr).unwrap());\n    //! ping_params.set_payload(b\"Hello, router!\");\n    //! match ping_manager.ping(&ping_params).await {\n    //!     Ok(time) => info!(\"Ping time of {}: {}ms\", addr, time.as_millis()),\n    //!     Err(ping_error) => warn!(\"{:?}\", ping_error),\n    //! };\n    //! ```\n\n    use core::net::IpAddr;\n    #[cfg(feature = \"proto-ipv6\")]\n    use core::net::Ipv6Addr;\n\n    use embassy_time::{Duration, Instant, Timer, WithTimeout};\n    #[cfg(feature = \"proto-ipv6\")]\n    use smoltcp::wire::IpAddress;\n    #[cfg(feature = \"proto-ipv6\")]\n    use smoltcp::wire::Ipv6Address;\n\n    use super::*;\n\n    /// Error returned by [`ping()`](PingManager::ping).\n    #[derive(PartialEq, Eq, Clone, Copy, Debug)]\n    #[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\n    pub enum PingError {\n        /// The target did not respond.\n        ///\n        /// The packet was sent but the Reply packet has not been recieved\n        /// in the timeout set by [`set_timeout()`](PingParams::set_timeout).\n        DestinationHostUnreachable,\n        /// The target has not been specified.\n        InvalidTargetAddress,\n        /// The source has not been specified (Ipv6 only).\n        #[cfg(feature = \"proto-ipv6\")]\n        InvalidSourceAddress,\n        /// The socket could not queue the packet in the buffer.\n        SocketSendTimeout,\n        /// Container error for [`icmp::BindError`].\n        SocketBindError(BindError),\n        /// Container error for [`icmp::SendError`].\n        SocketSendError(SendError),\n        /// Container error for [`icmp::RecvError`].\n        SocketRecvError(RecvError),\n    }\n\n    /// Manages ICMP ping operations.\n    ///\n    /// This struct provides functionality to send ICMP echo requests (pings) to a specified target\n    /// and measure the round-trip time for the requests. It supports both IPv4 and IPv6, depending\n    /// on the enabled features.\n    ///\n    /// # Fields\n    ///\n    /// * `stack` - The network stack instance used for managing network operations.\n    /// * `rx_meta` - Metadata buffer for receiving packets.\n    /// * `rx_buffer` - Buffer for receiving packets.\n    /// * `tx_meta` - Metadata buffer for transmitting packets.\n    /// * `tx_buffer` - Buffer for transmitting packets.\n    /// * `ident` - Identifier for the ICMP echo requests.\n    ///\n    /// # Methods\n    ///\n    /// * [`new`](PingManager::new) - Creates a new instance of `PingManager` with the specified stack and buffers.\n    /// * [`ping`](PingManager::ping) - Sends ICMP echo requests to the specified target and returns the average round-trip time.\n    pub struct PingManager<'d> {\n        stack: Stack<'d>,\n        rx_meta: &'d mut [PacketMetadata],\n        rx_buffer: &'d mut [u8],\n        tx_meta: &'d mut [PacketMetadata],\n        tx_buffer: &'d mut [u8],\n        ident: u16,\n    }\n\n    impl<'d> PingManager<'d> {\n        /// Creates a new instance of [`PingManager`] with a [`Stack`] instance\n        /// and the buffers used for RX and TX.\n        ///\n        /// **note**: This does not yet creates the ICMP socket.\n        pub fn new(\n            stack: Stack<'d>,\n            rx_meta: &'d mut [PacketMetadata],\n            rx_buffer: &'d mut [u8],\n            tx_meta: &'d mut [PacketMetadata],\n            tx_buffer: &'d mut [u8],\n        ) -> Self {\n            Self {\n                stack,\n                rx_meta,\n                rx_buffer,\n                tx_meta,\n                tx_buffer,\n                ident: 0,\n            }\n        }\n\n        /// Sends ICMP echo requests to the specified target and returns the average round-trip time.\n        ///\n        /// # Arguments\n        ///\n        /// * `params` - Parameters for configuring the ping operation.\n        ///\n        /// # Returns\n        ///\n        /// * `Ok(Duration)` - The average round-trip time for the ping requests.\n        /// * `Err(PingError)` - An error occurred during the ping operation.\n        pub async fn ping<'a>(&mut self, params: &PingParams<'a>) -> Result<Duration, PingError> {\n            // Input validation\n            if params.target().is_none() {\n                return Err(PingError::InvalidTargetAddress);\n            }\n            #[cfg(feature = \"proto-ipv6\")]\n            if params.target().unwrap().is_ipv6() && params.source().is_none() {\n                return Err(PingError::InvalidSourceAddress);\n            }\n            // Increment the ident (wrapping u16) to respect standards\n            self.ident = self.ident.wrapping_add(1u16);\n            // Used to calculate the average duration\n            let mut total_duration = Duration::default();\n            let mut num_of_durations = 0u16;\n            // Increment the sequence number as per standards\n            for seq_no in 0..params.count() {\n                // Make sure each ping takes at least 1 second to respect standards\n                let rate_limit_start = Instant::now();\n\n                // make a single ping\n                // - shorts out errors\n                // - select the ip version\n                let ping_duration = match params.target.unwrap() {\n                    #[cfg(feature = \"proto-ipv4\")]\n                    IpAddress::Ipv4(_) => self.single_ping_v4(params, seq_no).await?,\n                    #[cfg(feature = \"proto-ipv6\")]\n                    IpAddress::Ipv6(_) => self.single_ping_v6(params, seq_no).await?,\n                };\n\n                // safely add up the durations of each ping\n                if let Some(dur) = total_duration.checked_add(ping_duration) {\n                    total_duration = dur;\n                    num_of_durations += 1;\n                }\n\n                // 1 sec min per ping\n                let rate_limit_end = rate_limit_start.elapsed();\n                if rate_limit_end <= params.rate_limit {\n                    Timer::after(params.rate_limit.checked_sub(rate_limit_end).unwrap()).await;\n                }\n            }\n            // calculate and return the average duration\n            Ok(total_duration.checked_div(num_of_durations as u32).unwrap())\n        }\n\n        #[cfg(feature = \"proto-ipv4\")]\n        fn create_repr_ipv4<'b>(&self, params: &PingParams<'b>, seq_no: u16) -> Icmpv4Repr<'b> {\n            Icmpv4Repr::EchoRequest {\n                ident: self.ident,\n                seq_no,\n                data: params.payload,\n            }\n        }\n\n        #[cfg(feature = \"proto-ipv6\")]\n        fn create_repr_ipv6<'b>(&self, params: &PingParams<'b>, seq_no: u16) -> Icmpv6Repr<'b> {\n            Icmpv6Repr::EchoRequest {\n                ident: self.ident,\n                seq_no,\n                data: params.payload,\n            }\n        }\n\n        #[cfg(feature = \"proto-ipv4\")]\n        async fn single_ping_v4(&mut self, params: &PingParams<'_>, seq_no: u16) -> Result<Duration, PingError> {\n            let ping_repr = self.create_repr_ipv4(params, seq_no);\n\n            // Create the socket and set hop limit and bind it to the endpoint with the ident\n            let mut socket = IcmpSocket::new(self.stack, self.rx_meta, self.rx_buffer, self.tx_meta, self.tx_buffer);\n            socket.set_hop_limit(params.hop_limit);\n            if let Err(e) = socket.bind(IcmpEndpoint::Ident(self.ident)) {\n                return Err(PingError::SocketBindError(e));\n            }\n\n            // Helper func to fill the buffer when sending the ICMP packet\n            fn fill_packet_buffer(buf: &mut [u8], ping_repr: Icmpv4Repr<'_>) -> Instant {\n                let mut icmp_packet = Icmpv4Packet::new_unchecked(buf);\n                ping_repr.emit(&mut icmp_packet, &ChecksumCapabilities::default());\n                Instant::now()\n            }\n\n            // Send with timeout the ICMP packet filling it with the helper function\n            let send_result = socket\n                .send_to_with(ping_repr.buffer_len(), params.target.unwrap(), |buf| {\n                    (buf.len(), fill_packet_buffer(buf, ping_repr))\n                })\n                .with_timeout(Duration::from_millis(100))\n                .await;\n            // Filter and translate potential errors from sending the packet\n            let now = match send_result {\n                Ok(send_result) => match send_result {\n                    Ok(i) => i,\n                    Err(e) => return Err(PingError::SocketSendError(e)),\n                },\n                Err(_) => return Err(PingError::SocketSendTimeout),\n            };\n\n            // Helper function for the recieve helper function to validate the echo reply\n            fn filter_pong(buf: &[u8], seq_no: u16) -> bool {\n                let pong_packet = match Icmpv4Packet::new_checked(buf) {\n                    Ok(pak) => pak,\n                    Err(_) => return false,\n                };\n                pong_packet.echo_seq_no() == seq_no\n            }\n\n            // Helper function to recieve and return the correct echo reply when it finds it\n            async fn recv_pong(socket: &IcmpSocket<'_>, seq_no: u16) -> Result<(), PingError> {\n                while match socket.recv_from_with(|(buf, _)| filter_pong(buf, seq_no)).await {\n                    Ok(b) => !b,\n                    Err(e) => return Err(PingError::SocketRecvError(e)),\n                } {}\n                Ok(())\n            }\n\n            // Calls the recieve helper function with a timeout\n            match recv_pong(&socket, seq_no).with_timeout(params.timeout).await {\n                Ok(res) => res?,\n                Err(_) => return Err(PingError::DestinationHostUnreachable),\n            }\n\n            // Return the round trip duration\n            Ok(now.elapsed())\n        }\n\n        #[cfg(feature = \"proto-ipv6\")]\n        async fn single_ping_v6(&mut self, params: &PingParams<'_>, seq_no: u16) -> Result<Duration, PingError> {\n            let ping_repr = self.create_repr_ipv6(params, seq_no);\n\n            // Create the socket and set hop limit and bind it to the endpoint with the ident\n            let mut socket = IcmpSocket::new(self.stack, self.rx_meta, self.rx_buffer, self.tx_meta, self.tx_buffer);\n            socket.set_hop_limit(params.hop_limit);\n            if let Err(e) = socket.bind(IcmpEndpoint::Ident(self.ident)) {\n                return Err(PingError::SocketBindError(e));\n            }\n\n            // Helper func to fill the buffer when sending the ICMP packet\n            fn fill_packet_buffer(buf: &mut [u8], ping_repr: Icmpv6Repr<'_>, params: &PingParams<'_>) -> Instant {\n                let mut icmp_packet = Icmpv6Packet::new_unchecked(buf);\n                let target = match params.target().unwrap() {\n                    IpAddr::V4(_) => unreachable!(),\n                    IpAddr::V6(addr) => addr,\n                };\n                ping_repr.emit(\n                    &params.source().unwrap(),\n                    &target,\n                    &mut icmp_packet,\n                    &ChecksumCapabilities::default(),\n                );\n                Instant::now()\n            }\n\n            // Send with timeout the ICMP packet filling it with the helper function\n            let send_result = socket\n                .send_to_with(ping_repr.buffer_len(), params.target.unwrap(), |buf| {\n                    (buf.len(), fill_packet_buffer(buf, ping_repr, params))\n                })\n                .with_timeout(Duration::from_millis(100))\n                .await;\n            let now = match send_result {\n                Ok(send_result) => match send_result {\n                    Ok(i) => i,\n                    Err(e) => return Err(PingError::SocketSendError(e)),\n                },\n                Err(_) => return Err(PingError::SocketSendTimeout),\n            };\n\n            // Helper function for the recieve helper function to validate the echo reply\n            fn filter_pong(buf: &[u8], seq_no: u16) -> bool {\n                let pong_packet = match Icmpv6Packet::new_checked(buf) {\n                    Ok(pak) => pak,\n                    Err(_) => return false,\n                };\n                pong_packet.echo_seq_no() == seq_no\n            }\n\n            // Helper function to recieve and return the correct echo reply when it finds it\n            async fn recv_pong(socket: &IcmpSocket<'_>, seq_no: u16) -> Result<(), PingError> {\n                while match socket.recv_from_with(|(buf, _)| filter_pong(buf, seq_no)).await {\n                    Ok(b) => !b,\n                    Err(e) => return Err(PingError::SocketRecvError(e)),\n                } {}\n                Ok(())\n            }\n\n            // Calls the recieve helper function with a timeout\n            match recv_pong(&socket, seq_no).with_timeout(params.timeout).await {\n                Ok(res) => res?,\n                Err(_) => return Err(PingError::DestinationHostUnreachable),\n            }\n\n            // Return the round trip duration\n            Ok(now.elapsed())\n        }\n    }\n\n    /// Parameters for configuring the ping operation.\n    ///\n    /// This struct provides various configuration options for performing ICMP ping operations,\n    /// including the target IP address, payload data, hop limit, number of pings, and timeout duration.\n    ///\n    /// # Fields\n    ///\n    /// * `target` - The target IP address for the ping operation.\n    /// * `source` - The source IP address for the ping operation (IPv6 only).\n    /// * `payload` - The data to be sent in the payload field of the ping.\n    /// * `hop_limit` - The hop limit to be used by the socket.\n    /// * `count` - The number of pings to be sent in one ping operation.\n    /// * `timeout` - The timeout duration before returning a [`PingError::DestinationHostUnreachable`] error.\n    /// * `rate_limit` - The minimum time per echo request.\n    pub struct PingParams<'a> {\n        target: Option<IpAddress>,\n        #[cfg(feature = \"proto-ipv6\")]\n        source: Option<Ipv6Address>,\n        payload: &'a [u8],\n        hop_limit: Option<u8>,\n        count: u16,\n        timeout: Duration,\n        rate_limit: Duration,\n    }\n\n    impl Default for PingParams<'_> {\n        fn default() -> Self {\n            Self {\n                target: None,\n                #[cfg(feature = \"proto-ipv6\")]\n                source: None,\n                payload: b\"embassy-net\",\n                hop_limit: None,\n                count: 4,\n                timeout: Duration::from_secs(4),\n                rate_limit: Duration::from_secs(1),\n            }\n        }\n    }\n\n    impl<'a> PingParams<'a> {\n        /// Creates a new instance of [`PingParams`] with the specified target IP address.\n        pub fn new<T: Into<IpAddr>>(target: T) -> Self {\n            Self {\n                target: Some(PingParams::ip_addr_to_smoltcp(target)),\n                #[cfg(feature = \"proto-ipv6\")]\n                source: None,\n                payload: b\"embassy-net\",\n                hop_limit: None,\n                count: 4,\n                timeout: Duration::from_secs(4),\n                rate_limit: Duration::from_secs(1),\n            }\n        }\n\n        fn ip_addr_to_smoltcp<T: Into<IpAddr>>(ip_addr: T) -> IpAddress {\n            match ip_addr.into() {\n                #[cfg(feature = \"proto-ipv4\")]\n                IpAddr::V4(v4) => IpAddress::Ipv4(v4),\n                #[cfg(not(feature = \"proto-ipv4\"))]\n                IpAddr::V4(_) => unreachable!(),\n                #[cfg(feature = \"proto-ipv6\")]\n                IpAddr::V6(v6) => IpAddress::Ipv6(v6),\n                #[cfg(not(feature = \"proto-ipv6\"))]\n                IpAddr::V6(_) => unreachable!(),\n            }\n        }\n\n        /// Sets the target IP address for the ping.\n        pub fn set_target<T: Into<IpAddr>>(&mut self, target: T) -> &mut Self {\n            self.target = Some(PingParams::ip_addr_to_smoltcp(target));\n            self\n        }\n\n        /// Retrieves the target IP address for the ping.\n        pub fn target(&self) -> Option<IpAddr> {\n            self.target.map(|t| t.into())\n        }\n\n        /// Sets the source IP address for the ping (IPv6 only).\n        #[cfg(feature = \"proto-ipv6\")]\n        pub fn set_source<T: Into<Ipv6Address>>(&mut self, source: T) -> &mut Self {\n            self.source = Some(source.into());\n            self\n        }\n\n        /// Retrieves the source IP address for the ping (IPv6 only).\n        #[cfg(feature = \"proto-ipv6\")]\n        pub fn source(&self) -> Option<Ipv6Addr> {\n            self.source\n        }\n\n        /// Sets the data used in the payload field of the ping with the provided slice.\n        pub fn set_payload(&mut self, payload: &'a [u8]) -> &mut Self {\n            self.payload = payload;\n            self\n        }\n\n        /// Gives a reference to the slice of data that's going to be sent in the payload field\n        /// of the ping.\n        pub fn payload(&self) -> &'a [u8] {\n            self.payload\n        }\n\n        /// Sets the hop limit that will be used by the socket with [`set_hop_limit()`](IcmpSocket::set_hop_limit).\n        ///\n        /// **Note**: A hop limit of [`Some(0)`](Some()) is equivalent to a hop limit of [`None`].\n        pub fn set_hop_limit(&mut self, hop_limit: Option<u8>) -> &mut Self {\n            let mut hop_limit = hop_limit;\n            if hop_limit.is_some_and(|x| x == 0) {\n                hop_limit = None\n            }\n            self.hop_limit = hop_limit;\n            self\n        }\n\n        /// Retrieves the hop limit that will be used by the socket with [`set_hop_limit()`](IcmpSocket::set_hop_limit).\n        pub fn hop_limit(&self) -> Option<u8> {\n            self.hop_limit\n        }\n\n        /// Sets the count used for specifying the number of pings done on one\n        /// [`ping()`](PingManager::ping) call.\n        ///\n        /// **Note**: A count of 0 will be set as 1.\n        pub fn set_count(&mut self, count: u16) -> &mut Self {\n            let mut count = count;\n            if count == 0 {\n                count = 1;\n            }\n            self.count = count;\n            self\n        }\n\n        /// Retrieve the count used for specifying the number of pings done on one\n        /// [`ping()`](PingManager::ping) call.\n        pub fn count(&self) -> u16 {\n            self.count\n        }\n\n        /// Sets the timeout used before returning [`PingError::DestinationHostUnreachable`]\n        /// when waiting for the Echo Reply icmp packet.\n        pub fn set_timeout(&mut self, timeout: Duration) -> &mut Self {\n            self.timeout = timeout;\n            self\n        }\n\n        /// Retrieve the timeout used before returning [`PingError::DestinationHostUnreachable`]\n        /// when waiting for the Echo Reply icmp packet.\n        pub fn timeout(&self) -> Duration {\n            self.timeout\n        }\n\n        /// Sets the `rate_limit`: minimum time per echo request.\n        pub fn set_rate_limit(&mut self, rate_limit: Duration) -> &mut Self {\n            self.rate_limit = rate_limit;\n            self\n        }\n\n        /// Retrieve the rate_limit.\n        pub fn rate_limit(&self) -> Duration {\n            self.rate_limit\n        }\n    }\n}\n"
  },
  {
    "path": "embassy-net/src/lib.rs",
    "content": "#![no_std]\n#![allow(async_fn_in_trait)]\n#![allow(unsafe_op_in_unsafe_fn)]\n#![warn(missing_docs)]\n#![doc = include_str!(\"../README.md\")]\n\n//! ## Feature flags\n#![doc = document_features::document_features!(feature_label = r#\"<span class=\"stab portability\"><code>{feature}</code></span>\"#)]\n\n#[cfg(not(any(feature = \"proto-ipv4\", feature = \"proto-ipv6\")))]\ncompile_error!(\"You must enable at least one of the following features: proto-ipv4, proto-ipv6\");\n\n// This mod MUST go first, so that the others see its macros.\npub(crate) mod fmt;\n\n#[cfg(feature = \"dns\")]\npub mod dns;\nmod driver_util;\n#[cfg(feature = \"icmp\")]\npub mod icmp;\n#[cfg(feature = \"raw\")]\npub mod raw;\n#[cfg(feature = \"tcp\")]\npub mod tcp;\nmod time;\n#[cfg(feature = \"udp\")]\npub mod udp;\n\nuse core::cell::RefCell;\nuse core::future::{Future, poll_fn};\nuse core::mem::MaybeUninit;\nuse core::pin::pin;\nuse core::task::{Context, Poll};\n\npub use embassy_net_driver as driver;\nuse embassy_net_driver::{Driver, LinkState};\nuse embassy_sync::waitqueue::WakerRegistration;\nuse embassy_time::{Instant, Timer};\nuse heapless::Vec;\n#[cfg(feature = \"dns\")]\npub use smoltcp::config::DNS_MAX_SERVER_COUNT;\n#[cfg(feature = \"multicast\")]\npub use smoltcp::iface::MulticastError;\n#[cfg(any(feature = \"dns\", feature = \"dhcpv4\"))]\nuse smoltcp::iface::SocketHandle;\nuse smoltcp::iface::{Interface, SocketSet, SocketStorage};\nuse smoltcp::phy::Medium;\n#[cfg(feature = \"dhcpv4\")]\nuse smoltcp::socket::dhcpv4::{self, RetryConfig};\n#[cfg(feature = \"medium-ethernet\")]\npub use smoltcp::wire::EthernetAddress;\n#[cfg(any(feature = \"medium-ethernet\", feature = \"medium-ieee802154\", feature = \"medium-ip\"))]\npub use smoltcp::wire::HardwareAddress;\n#[cfg(any(feature = \"udp\", feature = \"tcp\"))]\npub use smoltcp::wire::IpListenEndpoint;\n#[cfg(feature = \"medium-ieee802154\")]\npub use smoltcp::wire::{Ieee802154Address, Ieee802154Frame};\npub use smoltcp::wire::{IpAddress, IpCidr, IpEndpoint};\n#[cfg(feature = \"proto-ipv4\")]\npub use smoltcp::wire::{Ipv4Address, Ipv4Cidr};\n#[cfg(feature = \"proto-ipv6\")]\npub use smoltcp::wire::{Ipv6Address, Ipv6Cidr};\n\nuse crate::driver_util::DriverAdapter;\nuse crate::time::{instant_from_smoltcp, instant_to_smoltcp};\n\nconst LOCAL_PORT_MIN: u16 = 1025;\nconst LOCAL_PORT_MAX: u16 = 65535;\n#[cfg(feature = \"dns\")]\nconst MAX_QUERIES: usize = 4;\n#[cfg(feature = \"dhcpv4-hostname\")]\nconst MAX_HOSTNAME_LEN: usize = 32;\n\n/// Memory resources needed for a network stack.\npub struct StackResources<const SOCK: usize> {\n    sockets: MaybeUninit<[SocketStorage<'static>; SOCK]>,\n    inner: MaybeUninit<RefCell<Inner>>,\n    #[cfg(feature = \"dns\")]\n    queries: MaybeUninit<[Option<dns::DnsQuery>; MAX_QUERIES]>,\n    #[cfg(feature = \"dhcpv4-hostname\")]\n    hostname: HostnameResources,\n}\n\n#[cfg(feature = \"dhcpv4-hostname\")]\nstruct HostnameResources {\n    option: MaybeUninit<smoltcp::wire::DhcpOption<'static>>,\n    data: MaybeUninit<[u8; MAX_HOSTNAME_LEN]>,\n}\n\nimpl<const SOCK: usize> StackResources<SOCK> {\n    /// Create a new set of stack resources.\n    pub const fn new() -> Self {\n        Self {\n            sockets: MaybeUninit::uninit(),\n            inner: MaybeUninit::uninit(),\n            #[cfg(feature = \"dns\")]\n            queries: MaybeUninit::uninit(),\n            #[cfg(feature = \"dhcpv4-hostname\")]\n            hostname: HostnameResources {\n                option: MaybeUninit::uninit(),\n                data: MaybeUninit::uninit(),\n            },\n        }\n    }\n}\n\n/// Static IP address configuration.\n#[cfg(feature = \"proto-ipv4\")]\n#[derive(Debug, Clone, PartialEq, Eq)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub struct StaticConfigV4 {\n    /// IP address and subnet mask.\n    pub address: Ipv4Cidr,\n    /// Default gateway.\n    pub gateway: Option<Ipv4Address>,\n    /// DNS servers.\n    pub dns_servers: Vec<Ipv4Address, 3>,\n}\n\n/// Static IPv6 address configuration\n#[cfg(feature = \"proto-ipv6\")]\n#[derive(Debug, Clone, PartialEq, Eq)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub struct StaticConfigV6 {\n    /// IP address and subnet mask.\n    pub address: Ipv6Cidr,\n    /// Default gateway.\n    pub gateway: Option<Ipv6Address>,\n    /// DNS servers.\n    pub dns_servers: Vec<Ipv6Address, 3>,\n}\n\n/// DHCP configuration.\n#[cfg(feature = \"dhcpv4\")]\n#[derive(Debug, Clone, PartialEq, Eq)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\n#[non_exhaustive]\npub struct DhcpConfig {\n    /// Maximum lease duration.\n    ///\n    /// If not set, the lease duration specified by the server will be used.\n    /// If set, the lease duration will be capped at this value.\n    pub max_lease_duration: Option<embassy_time::Duration>,\n    /// Retry configuration.\n    pub retry_config: RetryConfig,\n    /// Ignore NAKs from DHCP servers.\n    ///\n    /// This is not compliant with the DHCP RFCs, since theoretically we must stop using the assigned IP when receiving a NAK. This can increase reliability on broken networks with buggy routers or rogue DHCP servers, however.\n    pub ignore_naks: bool,\n    /// Server port. This is almost always 67. Do not change unless you know what you're doing.\n    pub server_port: u16,\n    /// Client port. This is almost always 68. Do not change unless you know what you're doing.\n    pub client_port: u16,\n    /// Our hostname. This will be sent to the DHCP server as Option 12.\n    #[cfg(feature = \"dhcpv4-hostname\")]\n    pub hostname: Option<heapless::String<MAX_HOSTNAME_LEN>>,\n}\n\n#[cfg(feature = \"dhcpv4\")]\nimpl Default for DhcpConfig {\n    fn default() -> Self {\n        Self {\n            max_lease_duration: Default::default(),\n            retry_config: Default::default(),\n            ignore_naks: Default::default(),\n            server_port: smoltcp::wire::DHCP_SERVER_PORT,\n            client_port: smoltcp::wire::DHCP_CLIENT_PORT,\n            #[cfg(feature = \"dhcpv4-hostname\")]\n            hostname: None,\n        }\n    }\n}\n\n/// Network stack configuration.\n#[derive(Debug, Clone, Default, PartialEq, Eq)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\n#[non_exhaustive]\npub struct Config {\n    /// IPv4 configuration\n    #[cfg(feature = \"proto-ipv4\")]\n    pub ipv4: ConfigV4,\n    /// IPv6 configuration\n    #[cfg(feature = \"proto-ipv6\")]\n    pub ipv6: ConfigV6,\n}\n\nimpl Config {\n    /// IPv4 configuration with static addressing.\n    #[cfg(feature = \"proto-ipv4\")]\n    pub const fn ipv4_static(config: StaticConfigV4) -> Self {\n        Self {\n            ipv4: ConfigV4::Static(config),\n            #[cfg(feature = \"proto-ipv6\")]\n            ipv6: ConfigV6::None,\n        }\n    }\n\n    /// IPv6 configuration with static addressing.\n    #[cfg(feature = \"proto-ipv6\")]\n    pub const fn ipv6_static(config: StaticConfigV6) -> Self {\n        Self {\n            #[cfg(feature = \"proto-ipv4\")]\n            ipv4: ConfigV4::None,\n            ipv6: ConfigV6::Static(config),\n        }\n    }\n\n    /// IPv4 configuration with dynamic addressing.\n    ///\n    /// # Example\n    /// ```rust\n    /// # use embassy_net::Config;\n    /// let _cfg = Config::dhcpv4(Default::default());\n    /// ```\n    #[cfg(feature = \"dhcpv4\")]\n    pub const fn dhcpv4(config: DhcpConfig) -> Self {\n        Self {\n            ipv4: ConfigV4::Dhcp(config),\n            #[cfg(feature = \"proto-ipv6\")]\n            ipv6: ConfigV6::None,\n        }\n    }\n\n    /// Slaac configuration with dynamic addressing.\n    #[cfg(feature = \"slaac\")]\n    pub const fn slaac() -> Self {\n        Self {\n            #[cfg(feature = \"proto-ipv4\")]\n            ipv4: ConfigV4::None,\n            ipv6: ConfigV6::Slaac,\n        }\n    }\n}\n\n/// Network stack IPv4 configuration.\n#[cfg(feature = \"proto-ipv4\")]\n#[derive(Debug, Clone, Default, PartialEq, Eq)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub enum ConfigV4 {\n    /// Do not configure IPv4.\n    #[default]\n    None,\n    /// Use a static IPv4 address configuration.\n    Static(StaticConfigV4),\n    /// Use DHCP to obtain an IP address configuration.\n    #[cfg(feature = \"dhcpv4\")]\n    Dhcp(DhcpConfig),\n}\n\n/// Network stack IPv6 configuration.\n#[cfg(feature = \"proto-ipv6\")]\n#[derive(Debug, Clone, Default, PartialEq, Eq)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub enum ConfigV6 {\n    /// Do not configure IPv6.\n    #[default]\n    None,\n    /// Use a static IPv6 address configuration.\n    Static(StaticConfigV6),\n    /// Use SLAAC for IPv6 address configuration.\n    #[cfg(feature = \"slaac\")]\n    Slaac,\n}\n\n/// Network stack runner.\n///\n/// You must call [`Runner::run()`] in a background task for the network stack to work.\npub struct Runner<'d, D: Driver> {\n    driver: D,\n    stack: Stack<'d>,\n}\n\n/// Network stack handle\n///\n/// Use this to create sockets. It's `Copy`, so you can pass\n/// it by value instead of by reference.\n#[derive(Copy, Clone)]\npub struct Stack<'d> {\n    inner: &'d RefCell<Inner>,\n}\n\npub(crate) struct Inner {\n    pub(crate) sockets: SocketSet<'static>, // Lifetime type-erased.\n    pub(crate) iface: Interface,\n    /// Waker used for triggering polls.\n    pub(crate) waker: WakerRegistration,\n    /// Waker used for waiting for link up or config up.\n    state_waker: WakerRegistration,\n    hardware_address: HardwareAddress,\n    next_local_port: u16,\n    link_up: bool,\n    #[cfg(feature = \"proto-ipv4\")]\n    static_v4: Option<StaticConfigV4>,\n    #[cfg(feature = \"proto-ipv6\")]\n    static_v6: Option<StaticConfigV6>,\n    #[cfg(feature = \"slaac\")]\n    slaac: bool,\n    #[cfg(feature = \"dhcpv4\")]\n    dhcp_socket: Option<SocketHandle>,\n    #[cfg(feature = \"dns\")]\n    dns_socket: SocketHandle,\n    #[cfg(feature = \"dns\")]\n    dns_waker: WakerRegistration,\n    #[cfg(feature = \"dhcpv4-hostname\")]\n    hostname: *mut HostnameResources,\n}\n\nfn _assert_covariant<'a, 'b: 'a>(x: Stack<'b>) -> Stack<'a> {\n    x\n}\n\n/// Create a new network stack.\npub fn new<'d, D: Driver, const SOCK: usize>(\n    mut driver: D,\n    config: Config,\n    resources: &'d mut StackResources<SOCK>,\n    random_seed: u64,\n) -> (Stack<'d>, Runner<'d, D>) {\n    let (hardware_address, medium) = to_smoltcp_hardware_address(driver.hardware_address());\n    let mut iface_cfg = smoltcp::iface::Config::new(hardware_address);\n    iface_cfg.random_seed = random_seed;\n    #[cfg(feature = \"slaac\")]\n    {\n        iface_cfg.slaac = matches!(config.ipv6, ConfigV6::Slaac);\n    }\n\n    #[allow(unused_mut)]\n    let mut iface = Interface::new(\n        iface_cfg,\n        &mut DriverAdapter {\n            inner: &mut driver,\n            cx: None,\n            medium,\n        },\n        instant_to_smoltcp(Instant::now()),\n    );\n\n    unsafe fn transmute_slice<T>(x: &mut [T]) -> &'static mut [T] {\n        core::mem::transmute(x)\n    }\n\n    let sockets = resources.sockets.write([SocketStorage::EMPTY; SOCK]);\n    #[allow(unused_mut)]\n    let mut sockets: SocketSet<'static> = SocketSet::new(unsafe { transmute_slice(sockets) });\n\n    let next_local_port = (random_seed % (LOCAL_PORT_MAX - LOCAL_PORT_MIN) as u64) as u16 + LOCAL_PORT_MIN;\n\n    #[cfg(feature = \"dns\")]\n    let dns_socket = sockets.add(dns::Socket::new(\n        &[],\n        managed::ManagedSlice::Borrowed(unsafe {\n            transmute_slice(resources.queries.write([const { None }; MAX_QUERIES]))\n        }),\n    ));\n\n    let mut inner = Inner {\n        sockets,\n        iface,\n        waker: WakerRegistration::new(),\n        state_waker: WakerRegistration::new(),\n        next_local_port,\n        hardware_address,\n        link_up: false,\n        #[cfg(feature = \"proto-ipv4\")]\n        static_v4: None,\n        #[cfg(feature = \"proto-ipv6\")]\n        static_v6: None,\n        #[cfg(feature = \"slaac\")]\n        slaac: false,\n        #[cfg(feature = \"dhcpv4\")]\n        dhcp_socket: None,\n        #[cfg(feature = \"dns\")]\n        dns_socket,\n        #[cfg(feature = \"dns\")]\n        dns_waker: WakerRegistration::new(),\n        #[cfg(feature = \"dhcpv4-hostname\")]\n        hostname: &mut resources.hostname,\n    };\n\n    #[cfg(feature = \"proto-ipv4\")]\n    inner.set_config_v4(config.ipv4);\n    #[cfg(feature = \"proto-ipv6\")]\n    inner.set_config_v6(config.ipv6);\n    inner.apply_static_config();\n\n    let inner = &*resources.inner.write(RefCell::new(inner));\n    let stack = Stack { inner };\n    (stack, Runner { driver, stack })\n}\n\nfn to_smoltcp_hardware_address(addr: driver::HardwareAddress) -> (HardwareAddress, Medium) {\n    match addr {\n        #[cfg(feature = \"medium-ethernet\")]\n        driver::HardwareAddress::Ethernet(eth) => (HardwareAddress::Ethernet(EthernetAddress(eth)), Medium::Ethernet),\n        #[cfg(feature = \"medium-ieee802154\")]\n        driver::HardwareAddress::Ieee802154(ieee) => (\n            HardwareAddress::Ieee802154(Ieee802154Address::Extended(ieee)),\n            Medium::Ieee802154,\n        ),\n        #[cfg(feature = \"medium-ip\")]\n        driver::HardwareAddress::Ip => (HardwareAddress::Ip, Medium::Ip),\n\n        #[allow(unreachable_patterns)]\n        _ => panic!(\n            \"Unsupported medium {:?}. Make sure to enable the right medium feature in embassy-net's Cargo features.\",\n            addr\n        ),\n    }\n}\n\nimpl<'d> Stack<'d> {\n    fn with<R>(&self, f: impl FnOnce(&Inner) -> R) -> R {\n        f(&self.inner.borrow())\n    }\n\n    fn with_mut<R>(&self, f: impl FnOnce(&mut Inner) -> R) -> R {\n        f(&mut self.inner.borrow_mut())\n    }\n\n    /// Get the hardware address of the network interface.\n    pub fn hardware_address(&self) -> HardwareAddress {\n        self.with(|i| i.hardware_address)\n    }\n\n    /// Check whether the link is up.\n    pub fn is_link_up(&self) -> bool {\n        self.with(|i| i.link_up)\n    }\n\n    /// Check whether the network stack has a valid IP configuration.\n    /// This is true if the network stack has a static IP configuration or if DHCP has completed\n    pub fn is_config_up(&self) -> bool {\n        let v4_up;\n        let v6_up;\n\n        #[cfg(feature = \"proto-ipv4\")]\n        {\n            v4_up = self.config_v4().is_some();\n        }\n        #[cfg(not(feature = \"proto-ipv4\"))]\n        {\n            v4_up = false;\n        }\n\n        #[cfg(feature = \"proto-ipv6\")]\n        {\n            v6_up = self.config_v6().is_some();\n        }\n        #[cfg(not(feature = \"proto-ipv6\"))]\n        {\n            v6_up = false;\n        }\n\n        v4_up || v6_up\n    }\n\n    /// Wait for the network device to obtain a link signal.\n    pub async fn wait_link_up(&self) {\n        self.wait(|| self.is_link_up()).await\n    }\n\n    /// Wait for the network device to lose link signal.\n    pub async fn wait_link_down(&self) {\n        self.wait(|| !self.is_link_up()).await\n    }\n\n    /// Wait for the network stack to obtain a valid IP configuration.\n    ///\n    /// ## Notes:\n    /// - Ensure [`Runner::run`] has been started before using this function.\n    ///\n    /// - This function may never return (e.g. if no configuration is obtained through DHCP).\n    /// The caller is supposed to handle a timeout for this case.\n    ///\n    /// ## Example\n    /// ```ignore\n    /// let config = embassy_net::Config::dhcpv4(Default::default());\n    /// // Init network stack\n    /// // NOTE: DHCP and DNS need one socket slot if enabled. This is why we're\n    /// // provisioning space for 3 sockets here: one for DHCP, one for DNS, and one for your code (e.g. TCP).\n    /// // If you use more sockets you must increase this. If you don't enable DHCP or DNS you can decrease it.\n    /// static RESOURCES: StaticCell<embassy_net::StackResources<3>> = StaticCell::new();\n    /// let (stack, runner) = embassy_net::new(\n    ///    driver,\n    ///    config,\n    ///    RESOURCES.init(embassy_net::StackResources::new()),\n    ///    seed\n    /// );\n    /// // Launch network task that runs `runner.run().await`\n    /// spawner.spawn(net_task(runner).unwrap());\n    /// // Wait for DHCP config\n    /// stack.wait_config_up().await;\n    /// // use the network stack\n    /// // ...\n    /// ```\n    pub async fn wait_config_up(&self) {\n        self.wait(|| self.is_config_up()).await\n    }\n\n    /// Wait for the network stack to lose a valid IP configuration.\n    pub async fn wait_config_down(&self) {\n        self.wait(|| !self.is_config_up()).await\n    }\n\n    fn wait<'a>(&'a self, mut predicate: impl FnMut() -> bool + 'a) -> impl Future<Output = ()> + 'a {\n        poll_fn(move |cx| {\n            if predicate() {\n                Poll::Ready(())\n            } else {\n                // If the config is not up, we register a waker that is woken up\n                // when a config is applied (static, slaac or DHCP).\n                trace!(\"Waiting for config up\");\n\n                self.with_mut(|i| {\n                    i.state_waker.register(cx.waker());\n                });\n\n                Poll::Pending\n            }\n        })\n    }\n\n    /// Get the current IPv4 configuration.\n    ///\n    /// If using DHCP, this will be None if DHCP hasn't been able to\n    /// acquire an IP address, or Some if it has.\n    #[cfg(feature = \"proto-ipv4\")]\n    pub fn config_v4(&self) -> Option<StaticConfigV4> {\n        self.with(|i| i.static_v4.clone())\n    }\n\n    /// Get the current IPv6 configuration.\n    #[cfg(feature = \"proto-ipv6\")]\n    pub fn config_v6(&self) -> Option<StaticConfigV6> {\n        self.with(|i| i.static_v6.clone())\n    }\n\n    /// Set the IPv4 configuration.\n    #[cfg(feature = \"proto-ipv4\")]\n    pub fn set_config_v4(&self, config: ConfigV4) {\n        self.with_mut(|i| {\n            i.set_config_v4(config);\n            i.apply_static_config();\n        })\n    }\n\n    /// Set the IPv6 configuration.\n    #[cfg(feature = \"proto-ipv6\")]\n    pub fn set_config_v6(&self, config: ConfigV6) {\n        self.with_mut(|i| {\n            i.set_config_v6(config);\n            i.apply_static_config();\n        })\n    }\n\n    /// Make a query for a given name and return the corresponding IP addresses.\n    #[cfg(feature = \"dns\")]\n    pub async fn dns_query(\n        &self,\n        name: &str,\n        qtype: dns::DnsQueryType,\n    ) -> Result<Vec<IpAddress, { smoltcp::config::DNS_MAX_RESULT_COUNT }>, dns::Error> {\n        // For A and AAAA queries we try detect whether `name` is just an IP address\n        match qtype {\n            #[cfg(feature = \"proto-ipv4\")]\n            dns::DnsQueryType::A => {\n                if let Ok(ip) = name.parse().map(IpAddress::Ipv4) {\n                    return Ok([ip].into_iter().collect());\n                }\n            }\n            #[cfg(feature = \"proto-ipv6\")]\n            dns::DnsQueryType::Aaaa => {\n                if let Ok(ip) = name.parse().map(IpAddress::Ipv6) {\n                    return Ok([ip].into_iter().collect());\n                }\n            }\n            _ => {}\n        }\n\n        let query = poll_fn(|cx| {\n            self.with_mut(|i| {\n                let socket = i.sockets.get_mut::<dns::Socket>(i.dns_socket);\n                match socket.start_query(i.iface.context(), name, qtype) {\n                    Ok(handle) => {\n                        i.waker.wake();\n                        Poll::Ready(Ok(handle))\n                    }\n                    Err(dns::StartQueryError::NoFreeSlot) => {\n                        i.dns_waker.register(cx.waker());\n                        Poll::Pending\n                    }\n                    Err(e) => Poll::Ready(Err(e)),\n                }\n            })\n        })\n        .await?;\n\n        #[must_use = \"to delay the drop handler invocation to the end of the scope\"]\n        struct OnDrop<F: FnOnce()> {\n            f: core::mem::MaybeUninit<F>,\n        }\n\n        impl<F: FnOnce()> OnDrop<F> {\n            fn new(f: F) -> Self {\n                Self {\n                    f: core::mem::MaybeUninit::new(f),\n                }\n            }\n\n            fn defuse(self) {\n                core::mem::forget(self)\n            }\n        }\n\n        impl<F: FnOnce()> Drop for OnDrop<F> {\n            fn drop(&mut self) {\n                unsafe { self.f.as_ptr().read()() }\n            }\n        }\n\n        let drop = OnDrop::new(|| {\n            self.with_mut(|i| {\n                let socket = i.sockets.get_mut::<dns::Socket>(i.dns_socket);\n                socket.cancel_query(query);\n                i.waker.wake();\n                i.dns_waker.wake();\n            })\n        });\n\n        let res = poll_fn(|cx| {\n            self.with_mut(|i| {\n                let socket = i.sockets.get_mut::<dns::Socket>(i.dns_socket);\n                match socket.get_query_result(query) {\n                    Ok(addrs) => {\n                        i.dns_waker.wake();\n                        Poll::Ready(Ok(addrs))\n                    }\n                    Err(dns::GetQueryResultError::Pending) => {\n                        socket.register_query_waker(query, cx.waker());\n                        Poll::Pending\n                    }\n                    Err(e) => {\n                        i.dns_waker.wake();\n                        Poll::Ready(Err(e.into()))\n                    }\n                }\n            })\n        })\n        .await;\n\n        drop.defuse();\n\n        res\n    }\n}\n\n#[cfg(feature = \"multicast\")]\nimpl<'d> Stack<'d> {\n    /// Join a multicast group.\n    pub fn join_multicast_group(&self, addr: impl Into<IpAddress>) -> Result<(), MulticastError> {\n        self.with_mut(|i| i.iface.join_multicast_group(addr))\n    }\n\n    /// Leave a multicast group.\n    pub fn leave_multicast_group(&self, addr: impl Into<IpAddress>) -> Result<(), MulticastError> {\n        self.with_mut(|i| i.iface.leave_multicast_group(addr))\n    }\n\n    /// Get whether the network stack has joined the given multicast group.\n    pub fn has_multicast_group(&self, addr: impl Into<IpAddress>) -> bool {\n        self.with(|i| i.iface.has_multicast_group(addr))\n    }\n}\n\nimpl Inner {\n    #[cfg(feature = \"slaac\")]\n    fn get_link_local_address(&self) -> IpCidr {\n        let ll_prefix = Ipv6Cidr::new(Ipv6Cidr::LINK_LOCAL_PREFIX.address(), 64);\n        Ipv6Cidr::from_link_prefix(&ll_prefix, self.hardware_address)\n            .unwrap()\n            .into()\n    }\n\n    #[allow(clippy::absurd_extreme_comparisons)]\n    pub fn get_local_port(&mut self) -> u16 {\n        let res = self.next_local_port;\n        self.next_local_port = if res >= LOCAL_PORT_MAX { LOCAL_PORT_MIN } else { res + 1 };\n        res\n    }\n\n    #[cfg(feature = \"proto-ipv4\")]\n    pub fn set_config_v4(&mut self, config: ConfigV4) {\n        // Handle static config.\n        self.static_v4 = match config.clone() {\n            ConfigV4::None => None,\n            #[cfg(feature = \"dhcpv4\")]\n            ConfigV4::Dhcp(_) => None,\n            ConfigV4::Static(c) => Some(c),\n        };\n\n        // Handle DHCP config.\n        #[cfg(feature = \"dhcpv4\")]\n        match config {\n            ConfigV4::Dhcp(c) => {\n                // Create the socket if it doesn't exist.\n                if self.dhcp_socket.is_none() {\n                    let socket = smoltcp::socket::dhcpv4::Socket::new();\n                    let handle = self.sockets.add(socket);\n                    self.dhcp_socket = Some(handle);\n                }\n\n                // Configure it\n                let socket = self.sockets.get_mut::<dhcpv4::Socket>(unwrap!(self.dhcp_socket));\n                socket.set_ignore_naks(c.ignore_naks);\n                socket.set_max_lease_duration(c.max_lease_duration.map(crate::time::duration_to_smoltcp));\n                socket.set_ports(c.server_port, c.client_port);\n                socket.set_retry_config(c.retry_config);\n\n                socket.set_outgoing_options(&[]);\n                #[cfg(feature = \"dhcpv4-hostname\")]\n                if let Some(h) = c.hostname {\n                    // safety:\n                    // - we just did set_outgoing_options([]) so we know the socket is no longer holding a reference.\n                    // - we know this pointer lives for as long as the stack exists, because `new()` borrows\n                    //   the resources for `'d`. Therefore it's OK to pass a reference to this to smoltcp.\n                    let hostname = unsafe { &mut *self.hostname };\n\n                    // create data\n                    let data = hostname.data.write([0; MAX_HOSTNAME_LEN]);\n                    data[..h.len()].copy_from_slice(h.as_bytes());\n                    let data: &[u8] = &data[..h.len()];\n\n                    // set the option.\n                    let option = hostname.option.write(smoltcp::wire::DhcpOption { data, kind: 12 });\n                    socket.set_outgoing_options(core::slice::from_ref(option));\n                }\n\n                socket.reset();\n            }\n            _ => {\n                // Remove DHCP socket if any.\n                if let Some(socket) = self.dhcp_socket {\n                    self.sockets.remove(socket);\n                    self.dhcp_socket = None;\n                }\n            }\n        }\n    }\n\n    #[cfg(feature = \"proto-ipv6\")]\n    pub fn set_config_v6(&mut self, config: ConfigV6) {\n        #[cfg(feature = \"slaac\")]\n        {\n            self.slaac = matches!(config, ConfigV6::Slaac);\n        }\n        self.static_v6 = match config {\n            ConfigV6::None => None,\n            ConfigV6::Static(c) => Some(c),\n            #[cfg(feature = \"slaac\")]\n            ConfigV6::Slaac => None,\n        };\n    }\n\n    fn apply_static_config(&mut self) {\n        let mut addrs = Vec::new();\n        #[cfg(feature = \"dns\")]\n        let mut dns_servers: Vec<_, 6> = Vec::new();\n        #[cfg(feature = \"proto-ipv4\")]\n        let mut gateway_v4 = None;\n        #[cfg(feature = \"proto-ipv6\")]\n        let mut gateway_v6 = None;\n\n        #[cfg(feature = \"proto-ipv4\")]\n        if let Some(config) = &self.static_v4 {\n            debug!(\"IPv4: UP\");\n            debug!(\"   IP address:      {:?}\", config.address);\n            debug!(\"   Default gateway: {:?}\", config.gateway);\n\n            unwrap!(addrs.push(IpCidr::Ipv4(config.address)).ok());\n            gateway_v4 = config.gateway;\n            #[cfg(feature = \"dns\")]\n            for s in &config.dns_servers {\n                debug!(\"   DNS server:      {:?}\", s);\n                unwrap!(dns_servers.push(s.clone().into()).ok());\n            }\n        } else {\n            info!(\"IPv4: DOWN\");\n        }\n\n        #[cfg(feature = \"proto-ipv6\")]\n        if let Some(config) = &self.static_v6 {\n            debug!(\"IPv6: UP\");\n            debug!(\"   IP address:      {:?}\", config.address);\n            debug!(\"   Default gateway: {:?}\", config.gateway);\n\n            unwrap!(addrs.push(IpCidr::Ipv6(config.address)).ok());\n            gateway_v6 = config.gateway;\n            #[cfg(feature = \"dns\")]\n            for s in &config.dns_servers {\n                debug!(\"   DNS server:      {:?}\", s);\n                unwrap!(dns_servers.push(s.clone().into()).ok());\n            }\n        } else {\n            info!(\"IPv6: DOWN\");\n        }\n\n        // Apply addresses\n        self.iface.update_ip_addrs(|a| {\n            *a = addrs;\n        });\n\n        // Add the link local-address\n        #[cfg(feature = \"slaac\")]\n        {\n            let ll_address = self.get_link_local_address();\n            self.iface.update_ip_addrs(|a| {\n                let _ = a.push(ll_address);\n            })\n        }\n\n        // Apply gateways\n        #[cfg(feature = \"proto-ipv4\")]\n        if let Some(gateway) = gateway_v4 {\n            unwrap!(self.iface.routes_mut().add_default_ipv4_route(gateway));\n        } else {\n            self.iface.routes_mut().remove_default_ipv4_route();\n        }\n        #[cfg(feature = \"proto-ipv6\")]\n        if let Some(gateway) = gateway_v6 {\n            unwrap!(self.iface.routes_mut().add_default_ipv6_route(gateway));\n        } else {\n            self.iface.routes_mut().remove_default_ipv6_route();\n        }\n\n        // Apply DNS servers\n        #[cfg(feature = \"dns\")]\n        if !dns_servers.is_empty() {\n            let count = if dns_servers.len() > DNS_MAX_SERVER_COUNT {\n                warn!(\"Number of DNS servers exceeds DNS_MAX_SERVER_COUNT, truncating list.\");\n                DNS_MAX_SERVER_COUNT\n            } else {\n                dns_servers.len()\n            };\n            self.sockets\n                .get_mut::<smoltcp::socket::dns::Socket>(self.dns_socket)\n                .update_servers(&dns_servers[..count]);\n        }\n\n        self.state_waker.wake();\n    }\n\n    fn poll<D: Driver>(&mut self, cx: &mut Context<'_>, driver: &mut D) {\n        self.waker.register(cx.waker());\n\n        let (_hardware_addr, medium) = to_smoltcp_hardware_address(driver.hardware_address());\n\n        #[cfg(any(feature = \"medium-ethernet\", feature = \"medium-ieee802154\"))]\n        {\n            let do_set = match medium {\n                #[cfg(feature = \"medium-ethernet\")]\n                Medium::Ethernet => true,\n                #[cfg(feature = \"medium-ieee802154\")]\n                Medium::Ieee802154 => true,\n                #[allow(unreachable_patterns)]\n                _ => false,\n            };\n            if do_set {\n                self.iface.set_hardware_addr(_hardware_addr);\n            }\n        }\n\n        let timestamp = instant_to_smoltcp(Instant::now());\n        let mut smoldev = DriverAdapter {\n            cx: Some(cx),\n            inner: driver,\n            medium,\n        };\n        self.iface.poll(timestamp, &mut smoldev, &mut self.sockets);\n\n        // Update link up\n        let old_link_up = self.link_up;\n        self.link_up = driver.link_state(cx) == LinkState::Up;\n\n        // Print when changed\n        if old_link_up != self.link_up {\n            info!(\"link_up = {:?}\", self.link_up);\n            self.state_waker.wake();\n        }\n\n        #[allow(unused_mut)]\n        let mut configure = false;\n\n        #[cfg(feature = \"dhcpv4\")]\n        {\n            configure |= if let Some(dhcp_handle) = self.dhcp_socket {\n                let socket = self.sockets.get_mut::<dhcpv4::Socket>(dhcp_handle);\n\n                if self.link_up {\n                    if old_link_up != self.link_up {\n                        socket.reset();\n                    }\n                    match socket.poll() {\n                        None => false,\n                        Some(dhcpv4::Event::Deconfigured) => {\n                            self.static_v4 = None;\n                            true\n                        }\n                        Some(dhcpv4::Event::Configured(config)) => {\n                            self.static_v4 = Some(StaticConfigV4 {\n                                address: config.address,\n                                gateway: config.router,\n                                dns_servers: config.dns_servers,\n                            });\n                            true\n                        }\n                    }\n                } else if old_link_up {\n                    socket.reset();\n                    self.static_v4 = None;\n                    true\n                } else {\n                    false\n                }\n            } else {\n                false\n            }\n        }\n\n        #[cfg(feature = \"slaac\")]\n        if self.slaac && self.iface.slaac_updated_at() == timestamp {\n            let ipv6_address = self.iface.ip_addrs().iter().find_map(|addr| match addr {\n                IpCidr::Ipv6(ip6_address) if !Ipv6Cidr::LINK_LOCAL_PREFIX.contains_addr(&ip6_address.address()) => {\n                    Some(ip6_address)\n                }\n                _ => None,\n            });\n            self.static_v6 = if let Some(address) = ipv6_address {\n                let gateway = self\n                    .iface\n                    .routes()\n                    .get_default_ipv6_route()\n                    .map(|r| match r.via_router {\n                        IpAddress::Ipv6(gateway) => Some(gateway),\n                        #[cfg(feature = \"proto-ipv4\")]\n                        _ => None,\n                    })\n                    .flatten();\n                let config = StaticConfigV6 {\n                    address: *address,\n                    gateway,\n                    dns_servers: Vec::new(), // RDNSS not (yet) supported by smoltcp.\n                };\n                Some(config)\n            } else {\n                None\n            };\n            configure = true;\n        }\n\n        if configure {\n            self.apply_static_config()\n        }\n\n        if let Some(poll_at) = self.iface.poll_at(timestamp, &mut self.sockets) {\n            let t = pin!(Timer::at(instant_from_smoltcp(poll_at)));\n            if t.poll(cx).is_ready() {\n                cx.waker().wake_by_ref();\n            }\n        }\n    }\n}\n\nimpl<'d, D: Driver> Runner<'d, D> {\n    /// Run the network stack.\n    ///\n    /// You must call this in a background task, to process network events.\n    pub async fn run(&mut self) -> ! {\n        poll_fn(|cx| {\n            self.stack.with_mut(|i| i.poll(cx, &mut self.driver));\n            Poll::<()>::Pending\n        })\n        .await;\n        unreachable!()\n    }\n}\n"
  },
  {
    "path": "embassy-net/src/raw.rs",
    "content": "//! Raw sockets.\n\nuse core::future::{Future, poll_fn};\nuse core::mem;\nuse core::task::{Context, Poll};\n\nuse smoltcp::iface::{Interface, SocketHandle};\nuse smoltcp::socket::raw;\npub use smoltcp::socket::raw::PacketMetadata;\npub use smoltcp::wire::{IpProtocol, IpVersion};\n\nuse crate::Stack;\n\n/// Error returned by [`RawSocket::recv`] and [`RawSocket::send`].\n#[derive(PartialEq, Eq, Clone, Copy, Debug)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub enum RecvError {\n    /// Provided buffer was smaller than the received packet.\n    Truncated,\n}\n\n/// An Raw socket.\npub struct RawSocket<'a> {\n    stack: Stack<'a>,\n    handle: SocketHandle,\n}\n\nimpl<'a> RawSocket<'a> {\n    /// Create a new Raw socket using the provided stack and buffers.\n    pub fn new(\n        stack: Stack<'a>,\n        ip_version: Option<IpVersion>,\n        ip_protocol: Option<IpProtocol>,\n        rx_meta: &'a mut [PacketMetadata],\n        rx_buffer: &'a mut [u8],\n        tx_meta: &'a mut [PacketMetadata],\n        tx_buffer: &'a mut [u8],\n    ) -> Self {\n        let handle = stack.with_mut(|i| {\n            let rx_meta: &'static mut [PacketMetadata] = unsafe { mem::transmute(rx_meta) };\n            let rx_buffer: &'static mut [u8] = unsafe { mem::transmute(rx_buffer) };\n            let tx_meta: &'static mut [PacketMetadata] = unsafe { mem::transmute(tx_meta) };\n            let tx_buffer: &'static mut [u8] = unsafe { mem::transmute(tx_buffer) };\n            i.sockets.add(raw::Socket::new(\n                ip_version,\n                ip_protocol,\n                raw::PacketBuffer::new(rx_meta, rx_buffer),\n                raw::PacketBuffer::new(tx_meta, tx_buffer),\n            ))\n        });\n\n        Self { stack, handle }\n    }\n\n    fn with_mut<R>(&self, f: impl FnOnce(&mut raw::Socket, &mut Interface) -> R) -> R {\n        self.stack.with_mut(|i| {\n            let socket = i.sockets.get_mut::<raw::Socket>(self.handle);\n            let res = f(socket, &mut i.iface);\n            i.waker.wake();\n            res\n        })\n    }\n\n    /// Wait until the socket becomes readable.\n    ///\n    /// A socket is readable when a packet has been received, or when there are queued packets in\n    /// the buffer.\n    pub fn wait_recv_ready(&self) -> impl Future<Output = ()> + '_ {\n        poll_fn(move |cx| self.poll_recv_ready(cx))\n    }\n\n    /// Receive a datagram.\n    ///\n    /// This method will wait until a datagram is received.\n    pub async fn recv(&self, buf: &mut [u8]) -> Result<usize, RecvError> {\n        poll_fn(move |cx| self.poll_recv(buf, cx)).await\n    }\n\n    /// Wait until a datagram can be read.\n    ///\n    /// When no datagram is readable, this method will return `Poll::Pending` and\n    /// register the current task to be notified when a datagram is received.\n    ///\n    /// When a datagram is received, this method will return `Poll::Ready`.\n    pub fn poll_recv_ready(&self, cx: &mut Context<'_>) -> Poll<()> {\n        self.with_mut(|s, _| {\n            if s.can_recv() {\n                Poll::Ready(())\n            } else {\n                // socket buffer is empty wait until at least one byte has arrived\n                s.register_recv_waker(cx.waker());\n                Poll::Pending\n            }\n        })\n    }\n\n    /// Receive a datagram.\n    ///\n    /// When no datagram is available, this method will return `Poll::Pending` and\n    /// register the current task to be notified when a datagram is received.\n    pub fn poll_recv(&self, buf: &mut [u8], cx: &mut Context<'_>) -> Poll<Result<usize, RecvError>> {\n        self.with_mut(|s, _| match s.recv_slice(buf) {\n            Ok(n) => Poll::Ready(Ok(n)),\n            // No data ready\n            Err(raw::RecvError::Truncated) => Poll::Ready(Err(RecvError::Truncated)),\n            Err(raw::RecvError::Exhausted) => {\n                s.register_recv_waker(cx.waker());\n                Poll::Pending\n            }\n        })\n    }\n\n    /// Wait until the socket becomes writable.\n    ///\n    /// A socket becomes writable when there is space in the buffer, from initial memory or after\n    /// dispatching datagrams on a full buffer.\n    pub fn wait_send_ready(&self) -> impl Future<Output = ()> + '_ {\n        poll_fn(move |cx| self.poll_send_ready(cx))\n    }\n\n    /// Wait until a datagram can be sent.\n    ///\n    /// When no datagram can be sent (i.e. the buffer is full), this method will return\n    /// `Poll::Pending` and register the current task to be notified when\n    /// space is freed in the buffer after a datagram has been dispatched.\n    ///\n    /// When a datagram can be sent, this method will return `Poll::Ready`.\n    pub fn poll_send_ready(&self, cx: &mut Context<'_>) -> Poll<()> {\n        self.with_mut(|s, _| {\n            if s.can_send() {\n                Poll::Ready(())\n            } else {\n                // socket buffer is full wait until a datagram has been dispatched\n                s.register_send_waker(cx.waker());\n                Poll::Pending\n            }\n        })\n    }\n\n    /// Send a datagram.\n    ///\n    /// This method will wait until the datagram has been sent.`\n    pub fn send<'s>(&'s self, buf: &'s [u8]) -> impl Future<Output = ()> + 's {\n        poll_fn(|cx| self.poll_send(buf, cx))\n    }\n\n    /// Send a datagram.\n    ///\n    /// When the datagram has been sent, this method will return `Poll::Ready(Ok())`.\n    ///\n    /// When the socket's send buffer is full, this method will return `Poll::Pending`\n    /// and register the current task to be notified when the buffer has space available.\n    pub fn poll_send(&self, buf: &[u8], cx: &mut Context<'_>) -> Poll<()> {\n        self.with_mut(|s, _| match s.send_slice(buf) {\n            // Entire datagram has been sent\n            Ok(()) => Poll::Ready(()),\n            Err(raw::SendError::BufferFull) => {\n                s.register_send_waker(cx.waker());\n                Poll::Pending\n            }\n        })\n    }\n\n    /// Flush the socket.\n    ///\n    /// This method will wait until the socket is flushed.\n    pub fn flush(&mut self) -> impl Future<Output = ()> + '_ {\n        poll_fn(|cx| {\n            self.with_mut(|s, _| {\n                if s.send_queue() == 0 {\n                    Poll::Ready(())\n                } else {\n                    s.register_send_waker(cx.waker());\n                    Poll::Pending\n                }\n            })\n        })\n    }\n}\n\nimpl Drop for RawSocket<'_> {\n    fn drop(&mut self) {\n        self.stack.with_mut(|i| i.sockets.remove(self.handle));\n    }\n}\n\nfn _assert_covariant<'a, 'b: 'a>(x: RawSocket<'b>) -> RawSocket<'a> {\n    x\n}\n"
  },
  {
    "path": "embassy-net/src/tcp.rs",
    "content": "//! TCP sockets.\n//!\n//! # Listening\n//!\n//! `embassy-net` does not have a `TcpListener`. Instead, individual `TcpSocket`s can be put into\n//! listening mode by calling [`TcpSocket::accept`].\n//!\n//! Incoming connections when no socket is listening are rejected. To accept many incoming\n//! connections, create many sockets and put them all into listening mode.\n\nuse core::future::{Future, poll_fn};\nuse core::mem;\nuse core::task::{Context, Poll};\n\nuse embassy_time::Duration;\nuse smoltcp::iface::{Interface, SocketHandle};\nuse smoltcp::socket::tcp;\npub use smoltcp::socket::tcp::State;\nuse smoltcp::wire::{IpEndpoint, IpListenEndpoint};\n\nuse crate::Stack;\nuse crate::time::duration_to_smoltcp;\n\n/// Error returned by TcpSocket read/write functions.\n#[derive(PartialEq, Eq, Clone, Copy, Debug)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub enum Error {\n    /// The connection was reset.\n    ///\n    /// This can happen on receiving a RST packet, or on timeout.\n    ConnectionReset,\n}\n\n/// Error returned by [`TcpSocket::connect`].\n#[derive(PartialEq, Eq, Clone, Copy, Debug)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub enum ConnectError {\n    /// The socket is already connected or listening.\n    InvalidState,\n    /// The remote host rejected the connection with a RST packet.\n    ConnectionReset,\n    /// Connect timed out.\n    TimedOut,\n    /// No route to host.\n    NoRoute,\n}\n\n/// Error returned by [`TcpSocket::accept`].\n#[derive(PartialEq, Eq, Clone, Copy, Debug)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub enum AcceptError {\n    /// The socket is already connected or listening.\n    InvalidState,\n    /// Invalid listen port\n    InvalidPort,\n    /// The remote host rejected the connection with a RST packet.\n    ConnectionReset,\n}\n\n/// A TCP socket.\npub struct TcpSocket<'a> {\n    io: TcpIo<'a>,\n}\n\n/// The reader half of a TCP socket.\npub struct TcpReader<'a> {\n    io: TcpIo<'a>,\n}\n\n/// The writer half of a TCP socket.\npub struct TcpWriter<'a> {\n    io: TcpIo<'a>,\n}\n\nimpl<'a> TcpReader<'a> {\n    /// Wait until the socket becomes readable.\n    ///\n    /// A socket becomes readable when the receive half of the full-duplex connection is open\n    /// (see [`may_recv()`](TcpSocket::may_recv)), and there is some pending data in the receive buffer.\n    ///\n    /// This is the equivalent of [read](#method.read), without buffering any data.\n    pub fn wait_read_ready(&self) -> impl Future<Output = ()> + '_ {\n        poll_fn(move |cx| self.io.poll_read_ready(cx))\n    }\n\n    /// Read data from the socket.\n    ///\n    /// Returns how many bytes were read, or an error. If no data is available, it waits\n    /// until there is at least one byte available.\n    ///\n    /// # Note\n    /// A return value of Ok(0) means that we have read all data and the remote\n    /// side has closed our receive half of the socket. The remote can no longer\n    /// send bytes.\n    ///\n    /// The send half of the socket is still open. If you want to reconnect using\n    /// the socket you split this reader off the send half needs to be closed using\n    /// [`abort()`](TcpSocket::abort).\n    pub async fn read(&mut self, buf: &mut [u8]) -> Result<usize, Error> {\n        self.io.read(buf).await\n    }\n\n    /// Call `f` with the largest contiguous slice of octets in the receive buffer,\n    /// and dequeue the amount of elements returned by `f`.\n    ///\n    /// If no data is available, it waits until there is at least one byte available.\n    pub async fn read_with<F, R>(&mut self, f: F) -> Result<R, Error>\n    where\n        F: FnOnce(&mut [u8]) -> (usize, R),\n    {\n        self.io.read_with(f).await\n    }\n\n    /// Return the maximum number of bytes inside the transmit buffer.\n    pub fn recv_capacity(&self) -> usize {\n        self.io.recv_capacity()\n    }\n\n    /// Return the amount of octets queued in the receive buffer. This value can be larger than\n    /// the slice read by the next `recv` or `peek` call because it includes all queued octets,\n    /// and not only the octets that may be returned as a contiguous slice.\n    pub fn recv_queue(&self) -> usize {\n        self.io.recv_queue()\n    }\n}\n\nimpl<'a> TcpWriter<'a> {\n    /// Wait until the socket becomes writable.\n    ///\n    /// A socket becomes writable when the transmit half of the full-duplex connection is open\n    /// (see [`may_send()`](TcpSocket::may_send)), and the transmit buffer is not full.\n    ///\n    /// This is the equivalent of [write](#method.write), without sending any data.\n    pub fn wait_write_ready(&self) -> impl Future<Output = ()> + '_ {\n        poll_fn(move |cx| self.io.poll_write_ready(cx))\n    }\n\n    /// Write data to the socket.\n    ///\n    /// Returns how many bytes were written, or an error. If the socket is not ready to\n    /// accept data, it waits until it is.\n    pub fn write<'s>(&'s mut self, buf: &'s [u8]) -> impl Future<Output = Result<usize, Error>> + 's {\n        self.io.write(buf)\n    }\n\n    /// Flushes the written data to the socket.\n    ///\n    /// This waits until all data has been sent, and ACKed by the remote host. For a connection\n    /// closed with [`abort()`](TcpSocket::abort) it will wait for the TCP RST packet to be sent.\n    pub fn flush(&mut self) -> impl Future<Output = Result<(), Error>> + '_ {\n        self.io.flush()\n    }\n\n    /// Call `f` with the largest contiguous slice of octets in the transmit buffer,\n    /// and enqueue the amount of elements returned by `f`.\n    ///\n    /// If the socket is not ready to accept data, it waits until it is.\n    pub async fn write_with<F, R>(&mut self, f: F) -> Result<R, Error>\n    where\n        F: FnOnce(&mut [u8]) -> (usize, R),\n    {\n        self.io.write_with(f).await\n    }\n\n    /// Return the maximum number of bytes inside the transmit buffer.\n    pub fn send_capacity(&self) -> usize {\n        self.io.send_capacity()\n    }\n\n    /// Return the amount of octets queued in the transmit buffer.\n    pub fn send_queue(&self) -> usize {\n        self.io.send_queue()\n    }\n}\n\nimpl<'a> TcpSocket<'a> {\n    /// Create a new TCP socket on the given stack, with the given buffers.\n    pub fn new(stack: Stack<'a>, rx_buffer: &'a mut [u8], tx_buffer: &'a mut [u8]) -> Self {\n        let handle = stack.with_mut(|i| {\n            let rx_buffer: &'static mut [u8] = unsafe { mem::transmute(rx_buffer) };\n            let tx_buffer: &'static mut [u8] = unsafe { mem::transmute(tx_buffer) };\n            i.sockets.add(tcp::Socket::new(\n                tcp::SocketBuffer::new(rx_buffer),\n                tcp::SocketBuffer::new(tx_buffer),\n            ))\n        });\n\n        Self {\n            io: TcpIo { stack, handle },\n        }\n    }\n\n    /// Return the maximum number of bytes inside the recv buffer.\n    pub fn recv_capacity(&self) -> usize {\n        self.io.recv_capacity()\n    }\n\n    /// Return the maximum number of bytes inside the transmit buffer.\n    pub fn send_capacity(&self) -> usize {\n        self.io.send_capacity()\n    }\n\n    /// Return the amount of octets queued in the transmit buffer.\n    pub fn send_queue(&self) -> usize {\n        self.io.send_queue()\n    }\n\n    /// Return the amount of octets queued in the receive buffer. This value can be larger than\n    /// the slice read by the next `recv` or `peek` call because it includes all queued octets,\n    /// and not only the octets that may be returned as a contiguous slice.\n    pub fn recv_queue(&self) -> usize {\n        self.io.recv_queue()\n    }\n\n    /// Call `f` with the largest contiguous slice of octets in the transmit buffer,\n    /// and enqueue the amount of elements returned by `f`.\n    ///\n    /// If the socket is not ready to accept data, it waits until it is.\n    pub async fn write_with<F, R>(&mut self, f: F) -> Result<R, Error>\n    where\n        F: FnOnce(&mut [u8]) -> (usize, R),\n    {\n        self.io.write_with(f).await\n    }\n\n    /// Call `f` with the largest contiguous slice of octets in the receive buffer,\n    /// and dequeue the amount of elements returned by `f`.\n    ///\n    /// If no data is available, it waits until there is at least one byte available.\n    pub async fn read_with<F, R>(&mut self, f: F) -> Result<R, Error>\n    where\n        F: FnOnce(&mut [u8]) -> (usize, R),\n    {\n        self.io.read_with(f).await\n    }\n\n    /// Split the socket into reader and a writer halves.\n    pub fn split(&mut self) -> (TcpReader<'_>, TcpWriter<'_>) {\n        (TcpReader { io: self.io }, TcpWriter { io: self.io })\n    }\n\n    /// Connect to a remote host.\n    pub async fn connect<T>(&mut self, remote_endpoint: T) -> Result<(), ConnectError>\n    where\n        T: Into<IpEndpoint>,\n    {\n        let local_port = self.io.stack.with_mut(|i| i.get_local_port());\n\n        match {\n            self.io\n                .with_mut(|s, i| s.connect(i.context(), remote_endpoint, local_port))\n        } {\n            Ok(()) => {}\n            Err(tcp::ConnectError::InvalidState) => return Err(ConnectError::InvalidState),\n            Err(tcp::ConnectError::Unaddressable) => return Err(ConnectError::NoRoute),\n        }\n\n        poll_fn(|cx| {\n            self.io.with_mut(|s, _| match s.state() {\n                tcp::State::Closed | tcp::State::TimeWait => Poll::Ready(Err(ConnectError::ConnectionReset)),\n                tcp::State::Listen => unreachable!(),\n                tcp::State::SynSent | tcp::State::SynReceived => {\n                    s.register_send_waker(cx.waker());\n                    Poll::Pending\n                }\n                _ => Poll::Ready(Ok(())),\n            })\n        })\n        .await\n    }\n\n    /// Accept a connection from a remote host.\n    ///\n    /// This function puts the socket in listening mode, and waits until a connection is received.\n    pub async fn accept<T>(&mut self, local_endpoint: T) -> Result<(), AcceptError>\n    where\n        T: Into<IpListenEndpoint>,\n    {\n        match self.io.with_mut(|s, _| s.listen(local_endpoint)) {\n            Ok(()) => {}\n            Err(tcp::ListenError::InvalidState) => return Err(AcceptError::InvalidState),\n            Err(tcp::ListenError::Unaddressable) => return Err(AcceptError::InvalidPort),\n        }\n\n        poll_fn(|cx| {\n            self.io.with_mut(|s, _| match s.state() {\n                tcp::State::Listen | tcp::State::SynSent | tcp::State::SynReceived => {\n                    s.register_send_waker(cx.waker());\n                    Poll::Pending\n                }\n                _ => Poll::Ready(Ok(())),\n            })\n        })\n        .await\n    }\n\n    /// Wait until the socket becomes readable.\n    ///\n    /// A socket becomes readable when the receive half of the full-duplex connection is open\n    /// (see [may_recv](#method.may_recv)), and there is some pending data in the receive buffer.\n    ///\n    /// This is the equivalent of [read](#method.read), without buffering any data.\n    pub fn wait_read_ready(&self) -> impl Future<Output = ()> + '_ {\n        poll_fn(move |cx| self.io.poll_read_ready(cx))\n    }\n\n    /// Read data from the socket.\n    ///\n    /// Returns how many bytes were read, or an error. If no data is available, it waits\n    /// until there is at least one byte available.\n    ///\n    /// A return value of Ok(0) means that the socket was closed and is longer\n    /// able to receive any data.\n    pub fn read<'s>(&'s mut self, buf: &'s mut [u8]) -> impl Future<Output = Result<usize, Error>> + 's {\n        self.io.read(buf)\n    }\n\n    /// Wait until the socket becomes writable.\n    ///\n    /// A socket becomes writable when the transmit half of the full-duplex connection is open\n    /// (see [may_send](#method.may_send)), and the transmit buffer is not full.\n    ///\n    /// This is the equivalent of [write](#method.write), without sending any data.\n    pub fn wait_write_ready(&self) -> impl Future<Output = ()> + '_ {\n        poll_fn(move |cx| self.io.poll_write_ready(cx))\n    }\n\n    /// Write data to the socket.\n    ///\n    /// Returns how many bytes were written, or an error. If the socket is not ready to\n    /// accept data, it waits until it is.\n    pub fn write<'s>(&'s mut self, buf: &'s [u8]) -> impl Future<Output = Result<usize, Error>> + 's {\n        self.io.write(buf)\n    }\n\n    /// Flushes the written data to the socket.\n    ///\n    /// This waits until all data has been sent, and ACKed by the remote host. For a connection\n    /// closed with [`abort()`](TcpSocket::abort) it will wait for the TCP RST packet to be sent.\n    pub fn flush(&mut self) -> impl Future<Output = Result<(), Error>> + '_ {\n        self.io.flush()\n    }\n\n    /// Set the timeout for the socket.\n    ///\n    /// If the timeout is set, the socket will be closed if no data is received for the\n    /// specified duration.\n    ///\n    /// # Note:\n    /// Set a keep alive interval ([`set_keep_alive`] to prevent timeouts when\n    /// the remote could still respond.\n    pub fn set_timeout(&mut self, duration: Option<Duration>) {\n        self.io\n            .with_mut(|s, _| s.set_timeout(duration.map(duration_to_smoltcp)))\n    }\n\n    /// Set the keep-alive interval for the socket.\n    ///\n    /// If the keep-alive interval is set, the socket will send keep-alive packets after\n    /// the specified duration of inactivity.\n    ///\n    /// If not set, the socket will not send keep-alive packets.\n    ///\n    /// By setting a [`timeout`](Self::timeout) larger then the keep alive you\n    /// can detect a remote endpoint that no longer answers.\n    pub fn set_keep_alive(&mut self, interval: Option<Duration>) {\n        self.io\n            .with_mut(|s, _| s.set_keep_alive(interval.map(duration_to_smoltcp)))\n    }\n\n    /// Set the hop limit field in the IP header of sent packets.\n    pub fn set_hop_limit(&mut self, hop_limit: Option<u8>) {\n        self.io.with_mut(|s, _| s.set_hop_limit(hop_limit))\n    }\n\n    /// Enable or disable Nagles's algorithm.\n    ///\n    /// By default, Nagle's algorithm is enabled.\n    /// When enabled, Nagle’s Algorithm prevents sending segments smaller\n    /// than MSS if there is data in flight (sent but not acknowledged).\n    /// In other words, it ensures at most only one segment smaller than\n    /// MSS is in flight at a time.\n    /// It ensures better network utilization by preventing sending many\n    /// very small packets, at the cost of increased latency in some\n    /// situations, particularly when the remote peer has ACK delay enabled.\n    pub fn set_nagle_enabled(&mut self, enabled: bool) {\n        self.io.with_mut(|s, _| s.set_nagle_enabled(enabled))\n    }\n\n    /// Get the local endpoint of the socket.\n    ///\n    /// Returns `None` if the socket is not bound (listening) or not connected.\n    pub fn local_endpoint(&self) -> Option<IpEndpoint> {\n        self.io.with(|s, _| s.local_endpoint())\n    }\n\n    /// Get the remote endpoint of the socket.\n    ///\n    /// Returns `None` if the socket is not connected.\n    pub fn remote_endpoint(&self) -> Option<IpEndpoint> {\n        self.io.with(|s, _| s.remote_endpoint())\n    }\n\n    /// Get the state of the socket.\n    pub fn state(&self) -> State {\n        self.io.with(|s, _| s.state())\n    }\n\n    /// Close the write half of the socket.\n    ///\n    /// This closes only the write half of the socket. The read half side remains open, the\n    /// socket can still receive data.\n    ///\n    /// Data that has been written to the socket and not yet sent (or not yet ACKed) will still\n    /// still sent. The last segment of the pending to send data is sent with the FIN flag set.\n    pub fn close(&mut self) {\n        self.io.with_mut(|s, _| s.close())\n    }\n\n    /// Forcibly close the socket.\n    ///\n    /// This instantly closes both the read and write halves of the socket. Any pending data\n    /// that has not been sent will be lost.\n    ///\n    /// Note that the TCP RST packet is not sent immediately - if the `TcpSocket` is dropped too soon\n    /// the remote host may not know the connection has been closed.\n    /// `abort()` callers should wait for a [`flush()`](TcpSocket::flush) call to complete before\n    /// dropping or reusing the socket.\n    pub fn abort(&mut self) {\n        self.io.with_mut(|s, _| s.abort())\n    }\n\n    /// Return whether the transmit half of the full-duplex connection is open.\n    ///\n    /// This function returns true if it's possible to send data and have it arrive\n    /// to the remote endpoint. However, it does not make any guarantees about the state\n    /// of the transmit buffer, and even if it returns true, [write](#method.write) may\n    /// not be able to enqueue any octets.\n    ///\n    /// In terms of the TCP state machine, the socket must be in the `ESTABLISHED` or\n    /// `CLOSE-WAIT` state.\n    pub fn may_send(&self) -> bool {\n        self.io.with(|s, _| s.may_send())\n    }\n\n    /// Check whether the transmit half of the full-duplex connection is open\n    /// (see [may_send](#method.may_send)), and the transmit buffer is not full.\n    pub fn can_send(&self) -> bool {\n        self.io.with(|s, _| s.can_send())\n    }\n\n    /// return whether the receive half of the full-duplex connection is open.\n    /// This function returns true if it’s possible to receive data from the remote endpoint.\n    /// It will return true while there is data in the receive buffer, and if there isn’t,\n    /// as long as the remote endpoint has not closed the connection.\n    pub fn may_recv(&self) -> bool {\n        self.io.with(|s, _| s.may_recv())\n    }\n\n    /// Get whether the socket is ready to receive data, i.e. whether there is some pending data in the receive buffer.\n    pub fn can_recv(&self) -> bool {\n        self.io.with(|s, _| s.can_recv())\n    }\n}\n\nimpl<'a> Drop for TcpSocket<'a> {\n    fn drop(&mut self) {\n        self.io.stack.with_mut(|i| i.sockets.remove(self.io.handle));\n    }\n}\n\nfn _assert_covariant<'a, 'b: 'a>(x: TcpSocket<'b>) -> TcpSocket<'a> {\n    x\n}\nfn _assert_covariant_reader<'a, 'b: 'a>(x: TcpReader<'b>) -> TcpReader<'a> {\n    x\n}\nfn _assert_covariant_writer<'a, 'b: 'a>(x: TcpWriter<'b>) -> TcpWriter<'a> {\n    x\n}\n\n// =======================\n\n#[derive(Copy, Clone)]\nstruct TcpIo<'a> {\n    stack: Stack<'a>,\n    handle: SocketHandle,\n}\n\nimpl<'d> TcpIo<'d> {\n    fn with<R>(&self, f: impl FnOnce(&tcp::Socket, &Interface) -> R) -> R {\n        self.stack.with(|i| {\n            let socket = i.sockets.get::<tcp::Socket>(self.handle);\n            f(socket, &i.iface)\n        })\n    }\n\n    fn with_mut<R>(&self, f: impl FnOnce(&mut tcp::Socket, &mut Interface) -> R) -> R {\n        self.stack.with_mut(|i| {\n            let socket = i.sockets.get_mut::<tcp::Socket>(self.handle);\n            let res = f(socket, &mut i.iface);\n            i.waker.wake();\n            res\n        })\n    }\n\n    fn poll_read_ready(&self, cx: &mut Context<'_>) -> Poll<()> {\n        self.with_mut(|s, _| {\n            if s.can_recv() {\n                Poll::Ready(())\n            } else {\n                s.register_recv_waker(cx.waker());\n                Poll::Pending\n            }\n        })\n    }\n\n    fn read<'s>(&'s mut self, buf: &'s mut [u8]) -> impl Future<Output = Result<usize, Error>> + 's {\n        poll_fn(|cx| {\n            // CAUTION: smoltcp semantics around EOF are different to what you'd expect\n            // from posix-like IO, so we have to tweak things here.\n            self.with_mut(|s, _| match s.recv_slice(buf) {\n                // Reading into empty buffer\n                Ok(0) if buf.is_empty() => {\n                    // embedded_io_async::Read's contract is to not block if buf is empty. While\n                    // this function is not a direct implementor of the trait method, we still don't\n                    // want our future to never resolve.\n                    Poll::Ready(Ok(0))\n                }\n                // No data ready\n                Ok(0) => {\n                    s.register_recv_waker(cx.waker());\n                    Poll::Pending\n                }\n                // Data ready!\n                Ok(n) => Poll::Ready(Ok(n)),\n                // EOF\n                Err(tcp::RecvError::Finished) => Poll::Ready(Ok(0)),\n                // Connection reset. TODO: this can also be timeouts etc, investigate.\n                Err(tcp::RecvError::InvalidState) => Poll::Ready(Err(Error::ConnectionReset)),\n            })\n        })\n    }\n\n    fn poll_write_ready(&self, cx: &mut Context<'_>) -> Poll<()> {\n        self.with_mut(|s, _| {\n            if s.can_send() {\n                Poll::Ready(())\n            } else {\n                s.register_send_waker(cx.waker());\n                Poll::Pending\n            }\n        })\n    }\n\n    fn write<'s>(&'s mut self, buf: &'s [u8]) -> impl Future<Output = Result<usize, Error>> + 's {\n        poll_fn(|cx| {\n            self.with_mut(|s, _| match s.send_slice(buf) {\n                // Not ready to send (no space in the tx buffer)\n                Ok(0) => {\n                    s.register_send_waker(cx.waker());\n                    Poll::Pending\n                }\n                // Some data sent\n                Ok(n) => Poll::Ready(Ok(n)),\n                // Connection reset. TODO: this can also be timeouts etc, investigate.\n                Err(tcp::SendError::InvalidState) => Poll::Ready(Err(Error::ConnectionReset)),\n            })\n        })\n    }\n\n    async fn write_with<F, R>(&mut self, f: F) -> Result<R, Error>\n    where\n        F: FnOnce(&mut [u8]) -> (usize, R),\n    {\n        let mut f = Some(f);\n        poll_fn(move |cx| {\n            self.with_mut(|s, _| {\n                if !s.can_send() {\n                    if s.may_send() {\n                        // socket buffer is full wait until it has atleast one byte free\n                        s.register_send_waker(cx.waker());\n                        Poll::Pending\n                    } else {\n                        // if we can't transmit because the transmit half of the duplex connection is closed then return an error\n                        Poll::Ready(Err(Error::ConnectionReset))\n                    }\n                } else {\n                    Poll::Ready(match s.send(unwrap!(f.take())) {\n                        // Connection reset. TODO: this can also be timeouts etc, investigate.\n                        Err(tcp::SendError::InvalidState) => Err(Error::ConnectionReset),\n                        Ok(r) => Ok(r),\n                    })\n                }\n            })\n        })\n        .await\n    }\n\n    async fn read_with<F, R>(&mut self, f: F) -> Result<R, Error>\n    where\n        F: FnOnce(&mut [u8]) -> (usize, R),\n    {\n        let mut f = Some(f);\n        poll_fn(move |cx| {\n            self.with_mut(|s, _| {\n                if !s.can_recv() {\n                    if s.may_recv() {\n                        // socket buffer is empty wait until it has atleast one byte has arrived\n                        s.register_recv_waker(cx.waker());\n                        Poll::Pending\n                    } else {\n                        // if we can't receive because the receive half of the duplex connection is closed then return an error\n                        Poll::Ready(Err(Error::ConnectionReset))\n                    }\n                } else {\n                    Poll::Ready(match s.recv(unwrap!(f.take())) {\n                        // Connection reset. TODO: this can also be timeouts etc, investigate.\n                        Err(tcp::RecvError::Finished) | Err(tcp::RecvError::InvalidState) => {\n                            Err(Error::ConnectionReset)\n                        }\n                        Ok(r) => Ok(r),\n                    })\n                }\n            })\n        })\n        .await\n    }\n\n    fn flush(&mut self) -> impl Future<Output = Result<(), Error>> + '_ {\n        poll_fn(|cx| {\n            self.with_mut(|s, _| {\n                let data_pending = (s.send_queue() > 0) && s.state() != tcp::State::Closed;\n                let fin_pending = matches!(\n                    s.state(),\n                    tcp::State::FinWait1 | tcp::State::Closing | tcp::State::LastAck\n                );\n                let rst_pending = s.state() == tcp::State::Closed && s.remote_endpoint().is_some();\n\n                // If there are outstanding send operations, register for wake up and wait\n                // smoltcp issues wake-ups when octets are dequeued from the send buffer\n                if data_pending || fin_pending || rst_pending {\n                    s.register_send_waker(cx.waker());\n                    Poll::Pending\n                // No outstanding sends, socket is flushed\n                } else {\n                    Poll::Ready(Ok(()))\n                }\n            })\n        })\n    }\n\n    fn recv_capacity(&self) -> usize {\n        self.with(|s, _| s.recv_capacity())\n    }\n\n    fn send_capacity(&self) -> usize {\n        self.with(|s, _| s.send_capacity())\n    }\n\n    fn send_queue(&self) -> usize {\n        self.with(|s, _| s.send_queue())\n    }\n\n    fn recv_queue(&self) -> usize {\n        self.with(|s, _| s.recv_queue())\n    }\n}\n\nmod embedded_io_impls {\n    use super::*;\n\n    impl core::fmt::Display for ConnectError {\n        fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {\n            f.write_str(\"ConnectError\")\n        }\n    }\n    impl core::error::Error for ConnectError {}\n\n    impl embedded_io_async::Error for ConnectError {\n        fn kind(&self) -> embedded_io_async::ErrorKind {\n            match self {\n                ConnectError::ConnectionReset => embedded_io_async::ErrorKind::ConnectionReset,\n                ConnectError::TimedOut => embedded_io_async::ErrorKind::TimedOut,\n                ConnectError::NoRoute => embedded_io_async::ErrorKind::NotConnected,\n                ConnectError::InvalidState => embedded_io_async::ErrorKind::Other,\n            }\n        }\n    }\n\n    impl core::fmt::Display for Error {\n        fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {\n            match self {\n                Self::ConnectionReset => f.write_str(\"ConnectionReset\"),\n            }\n        }\n    }\n    impl core::error::Error for Error {}\n\n    impl embedded_io_async::Error for Error {\n        fn kind(&self) -> embedded_io_async::ErrorKind {\n            match self {\n                Error::ConnectionReset => embedded_io_async::ErrorKind::ConnectionReset,\n            }\n        }\n    }\n\n    impl<'d> embedded_io_async::ErrorType for TcpSocket<'d> {\n        type Error = Error;\n    }\n\n    impl<'d> embedded_io_async::Read for TcpSocket<'d> {\n        async fn read(&mut self, buf: &mut [u8]) -> Result<usize, Self::Error> {\n            self.io.read(buf).await\n        }\n    }\n\n    impl<'d> embedded_io_async::ReadReady for TcpSocket<'d> {\n        fn read_ready(&mut self) -> Result<bool, Self::Error> {\n            Ok(self.io.with(|s, _| s.can_recv() || !s.may_recv()))\n        }\n    }\n\n    impl<'d> embedded_io_async::Write for TcpSocket<'d> {\n        async fn write(&mut self, buf: &[u8]) -> Result<usize, Self::Error> {\n            self.io.write(buf).await\n        }\n\n        async fn flush(&mut self) -> Result<(), Self::Error> {\n            self.io.flush().await\n        }\n    }\n\n    impl<'d> embedded_io_async::WriteReady for TcpSocket<'d> {\n        fn write_ready(&mut self) -> Result<bool, Self::Error> {\n            Ok(self.io.with(|s, _| s.can_send()))\n        }\n    }\n\n    impl<'d> embedded_io_async::ErrorType for TcpReader<'d> {\n        type Error = Error;\n    }\n\n    impl<'d> embedded_io_async::Read for TcpReader<'d> {\n        async fn read(&mut self, buf: &mut [u8]) -> Result<usize, Self::Error> {\n            self.io.read(buf).await\n        }\n    }\n\n    impl<'d> embedded_io_async::ReadReady for TcpReader<'d> {\n        fn read_ready(&mut self) -> Result<bool, Self::Error> {\n            Ok(self.io.with(|s, _| s.can_recv() || !s.may_recv()))\n        }\n    }\n\n    impl<'d> embedded_io_async::ErrorType for TcpWriter<'d> {\n        type Error = Error;\n    }\n\n    impl<'d> embedded_io_async::Write for TcpWriter<'d> {\n        async fn write(&mut self, buf: &[u8]) -> Result<usize, Self::Error> {\n            self.io.write(buf).await\n        }\n\n        async fn flush(&mut self) -> Result<(), Self::Error> {\n            self.io.flush().await\n        }\n    }\n\n    impl<'d> embedded_io_async::WriteReady for TcpWriter<'d> {\n        fn write_ready(&mut self) -> Result<bool, Self::Error> {\n            Ok(self.io.with(|s, _| s.can_send()))\n        }\n    }\n}\n\n/// TCP client compatible with `embedded-nal-async` traits.\npub mod client {\n    use core::cell::{Cell, UnsafeCell};\n    use core::mem::MaybeUninit;\n    use core::net::IpAddr;\n    use core::ptr::NonNull;\n\n    use super::*;\n\n    /// TCP client connection pool compatible with `embedded-nal-async` traits.\n    ///\n    /// The pool is capable of managing up to N concurrent connections with tx and rx buffers according to TX_SZ and RX_SZ.\n    pub struct TcpClient<'d, const N: usize, const TX_SZ: usize = 1024, const RX_SZ: usize = 1024> {\n        stack: Stack<'d>,\n        state: &'d TcpClientState<N, TX_SZ, RX_SZ>,\n        socket_timeout: Option<Duration>,\n    }\n\n    impl<'d, const N: usize, const TX_SZ: usize, const RX_SZ: usize> TcpClient<'d, N, TX_SZ, RX_SZ> {\n        /// Create a new `TcpClient`.\n        pub fn new(stack: Stack<'d>, state: &'d TcpClientState<N, TX_SZ, RX_SZ>) -> Self {\n            Self {\n                stack,\n                state,\n                socket_timeout: None,\n            }\n        }\n\n        /// Set the timeout for each socket created by this `TcpClient`.\n        ///\n        /// If the timeout is set, the socket will be closed if no data is received for the\n        /// specified duration.\n        pub fn set_timeout(&mut self, timeout: Option<Duration>) {\n            self.socket_timeout = timeout;\n        }\n    }\n\n    impl<'d, const N: usize, const TX_SZ: usize, const RX_SZ: usize> embedded_nal_async::TcpConnect\n        for TcpClient<'d, N, TX_SZ, RX_SZ>\n    {\n        type Error = Error;\n        type Connection<'m>\n            = TcpConnection<'m, N, TX_SZ, RX_SZ>\n        where\n            Self: 'm;\n\n        async fn connect<'a>(&'a self, remote: core::net::SocketAddr) -> Result<Self::Connection<'a>, Self::Error> {\n            let addr: crate::IpAddress = match remote.ip() {\n                #[cfg(feature = \"proto-ipv4\")]\n                IpAddr::V4(addr) => crate::IpAddress::Ipv4(addr),\n                #[cfg(not(feature = \"proto-ipv4\"))]\n                IpAddr::V4(_) => panic!(\"ipv4 support not enabled\"),\n                #[cfg(feature = \"proto-ipv6\")]\n                IpAddr::V6(addr) => crate::IpAddress::Ipv6(addr),\n                #[cfg(not(feature = \"proto-ipv6\"))]\n                IpAddr::V6(_) => panic!(\"ipv6 support not enabled\"),\n            };\n            let remote_endpoint = (addr, remote.port());\n            let mut socket = TcpConnection::new(self.stack, self.state)?;\n            socket.socket.set_timeout(self.socket_timeout);\n            socket\n                .socket\n                .connect(remote_endpoint)\n                .await\n                .map_err(|_| Error::ConnectionReset)?;\n            Ok(socket)\n        }\n    }\n\n    /// Opened TCP connection in a [`TcpClient`].\n    pub struct TcpConnection<'d, const N: usize, const TX_SZ: usize, const RX_SZ: usize> {\n        socket: TcpSocket<'d>,\n        state: &'d TcpClientState<N, TX_SZ, RX_SZ>,\n        bufs: NonNull<([u8; TX_SZ], [u8; RX_SZ])>,\n    }\n\n    impl<'d, const N: usize, const TX_SZ: usize, const RX_SZ: usize> TcpConnection<'d, N, TX_SZ, RX_SZ> {\n        fn new(stack: Stack<'d>, state: &'d TcpClientState<N, TX_SZ, RX_SZ>) -> Result<Self, Error> {\n            let mut bufs = state.pool.alloc().ok_or(Error::ConnectionReset)?;\n            Ok(Self {\n                socket: unsafe { TcpSocket::new(stack, &mut bufs.as_mut().1, &mut bufs.as_mut().0) },\n                state,\n                bufs,\n            })\n        }\n    }\n\n    impl<'d, const N: usize, const TX_SZ: usize, const RX_SZ: usize> Drop for TcpConnection<'d, N, TX_SZ, RX_SZ> {\n        fn drop(&mut self) {\n            unsafe {\n                self.socket.close();\n                self.state.pool.free(self.bufs);\n            }\n        }\n    }\n\n    impl<'d, const N: usize, const TX_SZ: usize, const RX_SZ: usize> embedded_io_async::ErrorType\n        for TcpConnection<'d, N, TX_SZ, RX_SZ>\n    {\n        type Error = Error;\n    }\n\n    impl<'d, const N: usize, const TX_SZ: usize, const RX_SZ: usize> embedded_io_async::Read\n        for TcpConnection<'d, N, TX_SZ, RX_SZ>\n    {\n        async fn read(&mut self, buf: &mut [u8]) -> Result<usize, Self::Error> {\n            self.socket.read(buf).await\n        }\n    }\n\n    impl<'d, const N: usize, const TX_SZ: usize, const RX_SZ: usize> embedded_io_async::Write\n        for TcpConnection<'d, N, TX_SZ, RX_SZ>\n    {\n        async fn write(&mut self, buf: &[u8]) -> Result<usize, Self::Error> {\n            self.socket.write(buf).await\n        }\n\n        async fn flush(&mut self) -> Result<(), Self::Error> {\n            self.socket.flush().await\n        }\n    }\n\n    /// State for TcpClient\n    pub struct TcpClientState<const N: usize, const TX_SZ: usize, const RX_SZ: usize> {\n        pool: Pool<([u8; TX_SZ], [u8; RX_SZ]), N>,\n    }\n\n    impl<const N: usize, const TX_SZ: usize, const RX_SZ: usize> TcpClientState<N, TX_SZ, RX_SZ> {\n        /// Create a new `TcpClientState`.\n        pub const fn new() -> Self {\n            Self { pool: Pool::new() }\n        }\n    }\n\n    struct Pool<T, const N: usize> {\n        used: [Cell<bool>; N],\n        data: [UnsafeCell<MaybeUninit<T>>; N],\n    }\n\n    impl<T, const N: usize> Pool<T, N> {\n        const VALUE: Cell<bool> = Cell::new(false);\n        const UNINIT: UnsafeCell<MaybeUninit<T>> = UnsafeCell::new(MaybeUninit::uninit());\n\n        const fn new() -> Self {\n            Self {\n                used: [Self::VALUE; N],\n                data: [Self::UNINIT; N],\n            }\n        }\n    }\n\n    impl<T, const N: usize> Pool<T, N> {\n        fn alloc(&self) -> Option<NonNull<T>> {\n            for n in 0..N {\n                // this can't race because Pool is not Sync.\n                if !self.used[n].get() {\n                    self.used[n].set(true);\n                    let p = self.data[n].get() as *mut T;\n                    return Some(unsafe { NonNull::new_unchecked(p) });\n                }\n            }\n            None\n        }\n\n        /// safety: p must be a pointer obtained from self.alloc that hasn't been freed yet.\n        unsafe fn free(&self, p: NonNull<T>) {\n            let origin = self.data.as_ptr() as *mut T;\n            let n = p.as_ptr().offset_from(origin);\n            assert!(n >= 0);\n            assert!((n as usize) < N);\n            self.used[n as usize].set(false);\n        }\n    }\n}\n"
  },
  {
    "path": "embassy-net/src/time.rs",
    "content": "#![allow(unused)]\n\nuse embassy_time::{Duration, Instant};\nuse smoltcp::time::{Duration as SmolDuration, Instant as SmolInstant};\n\npub(crate) fn instant_to_smoltcp(instant: Instant) -> SmolInstant {\n    SmolInstant::from_micros(instant.as_micros() as i64)\n}\n\npub(crate) fn instant_from_smoltcp(instant: SmolInstant) -> Instant {\n    Instant::from_micros(instant.total_micros() as u64)\n}\n\npub(crate) fn duration_to_smoltcp(duration: Duration) -> SmolDuration {\n    SmolDuration::from_micros(duration.as_micros())\n}\n\npub(crate) fn duration_from_smoltcp(duration: SmolDuration) -> Duration {\n    Duration::from_micros(duration.total_micros())\n}\n"
  },
  {
    "path": "embassy-net/src/udp.rs",
    "content": "//! UDP sockets.\n\nuse core::future::{Future, poll_fn};\nuse core::mem;\nuse core::task::{Context, Poll};\n\nuse smoltcp::iface::{Interface, SocketHandle};\nuse smoltcp::socket::udp;\npub use smoltcp::socket::udp::{PacketMetadata, UdpMetadata};\nuse smoltcp::wire::IpListenEndpoint;\n\nuse crate::Stack;\n\n/// Error returned by [`UdpSocket::bind`].\n#[derive(PartialEq, Eq, Clone, Copy, Debug)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub enum BindError {\n    /// The socket was already open.\n    InvalidState,\n    /// No route to host.\n    NoRoute,\n}\n\n/// Error returned by [`UdpSocket::send_to`].\n#[derive(PartialEq, Eq, Clone, Copy, Debug)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub enum SendError {\n    /// No route to host.\n    NoRoute,\n    /// Socket not bound to an outgoing port.\n    SocketNotBound,\n    /// There is not enough transmit buffer capacity to ever send this packet.\n    PacketTooLarge,\n}\n\n/// Error returned by [`UdpSocket::recv_from`].\n#[derive(PartialEq, Eq, Clone, Copy, Debug)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub enum RecvError {\n    /// Provided buffer was smaller than the received packet.\n    Truncated,\n}\n\n/// An UDP socket.\npub struct UdpSocket<'a> {\n    stack: Stack<'a>,\n    handle: SocketHandle,\n}\n\nimpl<'a> UdpSocket<'a> {\n    /// Create a new UDP socket using the provided stack and buffers.\n    pub fn new(\n        stack: Stack<'a>,\n        rx_meta: &'a mut [PacketMetadata],\n        rx_buffer: &'a mut [u8],\n        tx_meta: &'a mut [PacketMetadata],\n        tx_buffer: &'a mut [u8],\n    ) -> Self {\n        let handle = stack.with_mut(|i| {\n            let rx_meta: &'static mut [PacketMetadata] = unsafe { mem::transmute(rx_meta) };\n            let rx_buffer: &'static mut [u8] = unsafe { mem::transmute(rx_buffer) };\n            let tx_meta: &'static mut [PacketMetadata] = unsafe { mem::transmute(tx_meta) };\n            let tx_buffer: &'static mut [u8] = unsafe { mem::transmute(tx_buffer) };\n            i.sockets.add(udp::Socket::new(\n                udp::PacketBuffer::new(rx_meta, rx_buffer),\n                udp::PacketBuffer::new(tx_meta, tx_buffer),\n            ))\n        });\n\n        Self { stack, handle }\n    }\n\n    /// Bind the socket to a local endpoint.\n    pub fn bind<T>(&mut self, endpoint: T) -> Result<(), BindError>\n    where\n        T: Into<IpListenEndpoint>,\n    {\n        let mut endpoint = endpoint.into();\n\n        if endpoint.port == 0 {\n            // If user didn't specify port allocate a dynamic port.\n            endpoint.port = self.stack.with_mut(|i| i.get_local_port());\n        }\n\n        match self.with_mut(|s, _| s.bind(endpoint)) {\n            Ok(()) => Ok(()),\n            Err(udp::BindError::InvalidState) => Err(BindError::InvalidState),\n            Err(udp::BindError::Unaddressable) => Err(BindError::NoRoute),\n        }\n    }\n\n    fn with<R>(&self, f: impl FnOnce(&udp::Socket, &Interface) -> R) -> R {\n        self.stack.with(|i| {\n            let socket = i.sockets.get::<udp::Socket>(self.handle);\n            f(socket, &i.iface)\n        })\n    }\n\n    fn with_mut<R>(&self, f: impl FnOnce(&mut udp::Socket, &mut Interface) -> R) -> R {\n        self.stack.with_mut(|i| {\n            let socket = i.sockets.get_mut::<udp::Socket>(self.handle);\n            let res = f(socket, &mut i.iface);\n            i.waker.wake();\n            res\n        })\n    }\n\n    /// Wait until the socket becomes readable.\n    ///\n    /// A socket is readable when a packet has been received, or when there are queued packets in\n    /// the buffer.\n    pub fn wait_recv_ready(&self) -> impl Future<Output = ()> + '_ {\n        poll_fn(move |cx| self.poll_recv_ready(cx))\n    }\n\n    /// Wait until a datagram can be read.\n    ///\n    /// When no datagram is readable, this method will return `Poll::Pending` and\n    /// register the current task to be notified when a datagram is received.\n    ///\n    /// When a datagram is received, this method will return `Poll::Ready`.\n    pub fn poll_recv_ready(&self, cx: &mut Context<'_>) -> Poll<()> {\n        self.with_mut(|s, _| {\n            if s.can_recv() {\n                Poll::Ready(())\n            } else {\n                // socket buffer is empty wait until at least one byte has arrived\n                s.register_recv_waker(cx.waker());\n                Poll::Pending\n            }\n        })\n    }\n\n    /// Receive a datagram.\n    ///\n    /// This method will wait until a datagram is received.\n    ///\n    /// Returns the number of bytes received and the remote endpoint.\n    pub fn recv_from<'s>(\n        &'s self,\n        buf: &'s mut [u8],\n    ) -> impl Future<Output = Result<(usize, UdpMetadata), RecvError>> + 's {\n        poll_fn(|cx| self.poll_recv_from(buf, cx))\n    }\n\n    /// Receive a datagram.\n    ///\n    /// When no datagram is available, this method will return `Poll::Pending` and\n    /// register the current task to be notified when a datagram is received.\n    ///\n    /// When a datagram is received, this method will return `Poll::Ready` with the\n    /// number of bytes received and the remote endpoint.\n    pub fn poll_recv_from(\n        &self,\n        buf: &mut [u8],\n        cx: &mut Context<'_>,\n    ) -> Poll<Result<(usize, UdpMetadata), RecvError>> {\n        self.with_mut(|s, _| match s.recv_slice(buf) {\n            Ok((n, meta)) => Poll::Ready(Ok((n, meta))),\n            // No data ready\n            Err(udp::RecvError::Truncated) => Poll::Ready(Err(RecvError::Truncated)),\n            Err(udp::RecvError::Exhausted) => {\n                s.register_recv_waker(cx.waker());\n                Poll::Pending\n            }\n        })\n    }\n\n    /// Receive a datagram with a zero-copy function.\n    ///\n    /// When no datagram is available, this method will return `Poll::Pending` and\n    /// register the current task to be notified when a datagram is received.\n    ///\n    /// When a datagram is received, this method will call the provided function\n    /// with a reference to the received bytes and the remote endpoint and return\n    /// `Poll::Ready` with the function's returned value.\n    pub async fn recv_from_with<F, R>(&mut self, f: F) -> R\n    where\n        F: FnOnce(&[u8], UdpMetadata) -> R,\n    {\n        let mut f = Some(f);\n        poll_fn(move |cx| {\n            self.with_mut(|s, _| {\n                match s.recv() {\n                    Ok((buffer, endpoint)) => Poll::Ready(unwrap!(f.take())(buffer, endpoint)),\n                    Err(udp::RecvError::Truncated) => unreachable!(),\n                    Err(udp::RecvError::Exhausted) => {\n                        // socket buffer is empty wait until at least one byte has arrived\n                        s.register_recv_waker(cx.waker());\n                        Poll::Pending\n                    }\n                }\n            })\n        })\n        .await\n    }\n\n    /// Wait until the socket becomes writable.\n    ///\n    /// A socket becomes writable when there is space in the buffer, from initial memory or after\n    /// dispatching datagrams on a full buffer.\n    pub fn wait_send_ready(&self) -> impl Future<Output = ()> + '_ {\n        poll_fn(|cx| self.poll_send_ready(cx))\n    }\n\n    /// Wait until a datagram can be sent.\n    ///\n    /// When no datagram can be sent (i.e. the buffer is full), this method will return\n    /// `Poll::Pending` and register the current task to be notified when\n    /// space is freed in the buffer after a datagram has been dispatched.\n    ///\n    /// When a datagram can be sent, this method will return `Poll::Ready`.\n    pub fn poll_send_ready(&self, cx: &mut Context<'_>) -> Poll<()> {\n        self.with_mut(|s, _| {\n            if s.can_send() {\n                Poll::Ready(())\n            } else {\n                // socket buffer is full wait until a datagram has been dispatched\n                s.register_send_waker(cx.waker());\n                Poll::Pending\n            }\n        })\n    }\n\n    /// Send a datagram to the specified remote endpoint.\n    ///\n    /// This method will wait until the datagram has been sent.\n    ///\n    /// If the socket's send buffer is too small to fit `buf`, this method will return `Err(SendError::PacketTooLarge)`\n    ///\n    /// When the remote endpoint is not reachable, this method will return `Err(SendError::NoRoute)`\n    pub async fn send_to<T>(&self, buf: &[u8], remote_endpoint: T) -> Result<(), SendError>\n    where\n        T: Into<UdpMetadata>,\n    {\n        let remote_endpoint: UdpMetadata = remote_endpoint.into();\n        poll_fn(move |cx| self.poll_send_to(buf, remote_endpoint, cx)).await\n    }\n\n    /// Send a datagram to the specified remote endpoint.\n    ///\n    /// When the datagram has been sent, this method will return `Poll::Ready(Ok())`.\n    ///\n    /// When the socket's send buffer is full, this method will return `Poll::Pending`\n    /// and register the current task to be notified when the buffer has space available.\n    ///\n    /// If the socket's send buffer is too small to fit `buf`, this method will return `Poll::Ready(Err(SendError::PacketTooLarge))`\n    ///\n    /// When the remote endpoint is not reachable, this method will return `Poll::Ready(Err(Error::NoRoute))`.\n    pub fn poll_send_to<T>(&self, buf: &[u8], remote_endpoint: T, cx: &mut Context<'_>) -> Poll<Result<(), SendError>>\n    where\n        T: Into<UdpMetadata>,\n    {\n        // Don't need to wake waker in `with_mut` if the buffer will never fit the udp tx_buffer.\n        let send_capacity_too_small = self.with(|s, _| s.payload_send_capacity() < buf.len());\n        if send_capacity_too_small {\n            return Poll::Ready(Err(SendError::PacketTooLarge));\n        }\n\n        self.with_mut(|s, _| match s.send_slice(buf, remote_endpoint) {\n            // Entire datagram has been sent\n            Ok(()) => Poll::Ready(Ok(())),\n            Err(udp::SendError::BufferFull) => {\n                s.register_send_waker(cx.waker());\n                Poll::Pending\n            }\n            Err(udp::SendError::Unaddressable) => {\n                // If no sender/outgoing port is specified, there is not really \"no route\"\n                if s.endpoint().port == 0 {\n                    Poll::Ready(Err(SendError::SocketNotBound))\n                } else {\n                    Poll::Ready(Err(SendError::NoRoute))\n                }\n            }\n        })\n    }\n\n    /// Send a datagram to the specified remote endpoint with a zero-copy function.\n    ///\n    /// This method will wait until the buffer can fit the requested size before\n    /// passing it to the closure. The closure returns the number of bytes\n    /// written into the buffer.\n    ///\n    /// If the socket's send buffer is too small to fit `max_size`, this method will return `Err(SendError::PacketTooLarge)`\n    ///\n    /// When the remote endpoint is not reachable, this method will return `Err(SendError::NoRoute)`\n    pub async fn send_to_with<T, F, R>(&mut self, max_size: usize, remote_endpoint: T, f: F) -> Result<R, SendError>\n    where\n        T: Into<UdpMetadata> + Copy,\n        F: FnOnce(&mut [u8]) -> (usize, R),\n    {\n        // Don't need to wake waker in `with_mut` if the buffer will never fit the udp tx_buffer.\n        let send_capacity_too_small = self.with(|s, _| s.payload_send_capacity() < max_size);\n        if send_capacity_too_small {\n            return Err(SendError::PacketTooLarge);\n        }\n\n        let mut f = Some(f);\n        poll_fn(move |cx| {\n            self.with_mut(|s, _| {\n                let mut ret = None;\n\n                match s.send_with(max_size, remote_endpoint, |buf| {\n                    let (size, r) = unwrap!(f.take())(buf);\n                    ret = Some(r);\n                    size\n                }) {\n                    Ok(_n) => Poll::Ready(Ok(unwrap!(ret))),\n                    Err(udp::SendError::BufferFull) => {\n                        s.register_send_waker(cx.waker());\n                        Poll::Pending\n                    }\n                    Err(udp::SendError::Unaddressable) => {\n                        // If no sender/outgoing port is specified, there is not really \"no route\"\n                        if s.endpoint().port == 0 {\n                            Poll::Ready(Err(SendError::SocketNotBound))\n                        } else {\n                            Poll::Ready(Err(SendError::NoRoute))\n                        }\n                    }\n                }\n            })\n        })\n        .await\n    }\n\n    /// Flush the socket.\n    ///\n    /// This method will wait until the socket is flushed.\n    pub fn flush(&mut self) -> impl Future<Output = ()> + '_ {\n        poll_fn(|cx| {\n            self.with_mut(|s, _| {\n                if s.send_queue() == 0 {\n                    Poll::Ready(())\n                } else {\n                    s.register_send_waker(cx.waker());\n                    Poll::Pending\n                }\n            })\n        })\n    }\n\n    /// Returns the local endpoint of the socket.\n    pub fn endpoint(&self) -> IpListenEndpoint {\n        self.with(|s, _| s.endpoint())\n    }\n\n    /// Returns whether the socket is open.\n\n    pub fn is_open(&self) -> bool {\n        self.with(|s, _| s.is_open())\n    }\n\n    /// Close the socket.\n    pub fn close(&mut self) {\n        self.with_mut(|s, _| s.close())\n    }\n\n    /// Returns whether the socket is ready to send data, i.e. it has enough buffer space to hold a packet.\n    pub fn may_send(&self) -> bool {\n        self.with(|s, _| s.can_send())\n    }\n\n    /// Returns whether the socket is ready to receive data, i.e. it has received a packet that's now in the buffer.\n    pub fn may_recv(&self) -> bool {\n        self.with(|s, _| s.can_recv())\n    }\n\n    /// Return the maximum number packets the socket can receive.\n    pub fn packet_recv_capacity(&self) -> usize {\n        self.with(|s, _| s.packet_recv_capacity())\n    }\n\n    /// Return the maximum number packets the socket can receive.\n    pub fn packet_send_capacity(&self) -> usize {\n        self.with(|s, _| s.packet_send_capacity())\n    }\n\n    /// Return the maximum number of bytes inside the recv buffer.\n    pub fn payload_recv_capacity(&self) -> usize {\n        self.with(|s, _| s.payload_recv_capacity())\n    }\n\n    /// Return the maximum number of bytes inside the transmit buffer.\n    pub fn payload_send_capacity(&self) -> usize {\n        self.with(|s, _| s.payload_send_capacity())\n    }\n\n    /// Set the hop limit field in the IP header of sent packets.\n    pub fn set_hop_limit(&mut self, hop_limit: Option<u8>) {\n        self.with_mut(|s, _| s.set_hop_limit(hop_limit))\n    }\n}\n\nimpl Drop for UdpSocket<'_> {\n    fn drop(&mut self) {\n        self.stack.with_mut(|i| i.sockets.remove(self.handle));\n    }\n}\n\nfn _assert_covariant<'a, 'b: 'a>(x: UdpSocket<'b>) -> UdpSocket<'a> {\n    x\n}\n"
  },
  {
    "path": "embassy-net-adin1110/CHANGELOG.md",
    "content": "# Changelog\n\nAll notable changes to this project will be documented in this file.\n\nThe format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),\nand this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).\n\n<!-- next-header -->\n## Unreleased - ReleaseDate\n\n## 0.4.0 - 2026-03-10\n\n- Update embassy-net-driver-channel to 0.4.0\n\n## 0.3.1 - 2025-08-26\n\n- First release with changelog.\n"
  },
  {
    "path": "embassy-net-adin1110/Cargo.toml",
    "content": "[package]\nname = \"embassy-net-adin1110\"\nversion = \"0.4.0\"\ndescription = \"embassy-net driver for the ADIN1110 ethernet chip\"\nkeywords = [\"embedded\", \"ADIN1110\", \"embassy-net\", \"embedded-hal-async\", \"ethernet\"]\ncategories = [\"embedded\", \"hardware-support\", \"no-std\", \"network-programming\", \"asynchronous\"]\nlicense = \"MIT OR Apache-2.0\"\nedition = \"2024\"\nrepository = \"https://github.com/embassy-rs/embassy\"\ndocumentation = \"https://docs.embassy.dev/embassy-net-adin1110\"\n\n[dependencies]\nheapless = \"0.9\"\ndefmt = { version = \"1.0.1\", optional = true }\nlog = { version = \"0.4\", default-features = false, optional = true }\nembedded-hal-1 = { package = \"embedded-hal\", version = \"1.0\" }\nembedded-hal-async = { version = \"1.0\" }\nembedded-hal-bus = { version = \"0.1\", features = [\"async\"] }\nembassy-net-driver-channel = { version = \"0.4.0\", path = \"../embassy-net-driver-channel\" }\nembassy-time = { version = \"0.5.1\", path = \"../embassy-time\" }\nembassy-futures = { version = \"0.1.2\", path = \"../embassy-futures\" }\nbitfield = \"0.14.0\"\n\n[dev-dependencies]\nembedded-hal-mock = { version = \"0.10.0\", features = [\"embedded-hal-async\", \"eh1\"] }\ncrc = \"3.0.1\"\nenv_logger = \"0.10\"\ncritical-section = { version = \"1.1.2\", features = [\"std\"] }\nfutures-test = \"0.3.28\"\n\n[features]\ndefmt = [\"dep:defmt\", \"embedded-hal-1/defmt-03\"]\nlog = [\"dep:log\"]\n\n[package.metadata.embassy_docs]\nsrc_base = \"https://github.com/embassy-rs/embassy/blob/embassy-net-adin1110-v$VERSION/embassy-net-adin1110/src/\"\nsrc_base_git = \"https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-net-adin1110/src/\"\ntarget = \"thumbv7em-none-eabi\"\nfeatures = [\"defmt\"]\n\n[package.metadata.docs.rs]\nfeatures = [\"defmt\"]\n"
  },
  {
    "path": "embassy-net-adin1110/README.md",
    "content": "# SPE ADIN1110 `embassy-net` integration\n\n[`embassy-net`](https://crates.io/crates/embassy-net) integration for the `Analog ADIN1110` SPI SPE ethernet chips.\n\n## What is SPE or Single Pair Ethernet / 10 BASE-T1L\n\nSPE stands for Single Pair Ethernet. As the names implies, SPE uses differential signalling with 2 wires (a twisted-pair) in a cable as the physical medium.\nSPE is full-duplex - it can transmit and receive ethernet packets at the same time. SPE is still ethernet, only the physical layer is different.\n\nSPE also supports [`PoDL (Power over Data Line)`](https://www.ti.com/lit/an/snla395/snla395.pdf), power delivery from 0.5 up to 50 Watts, similar to [`PoE`](https://en.wikipedia.org/wiki/Power_over_Ethernet), but an additional hardware and handshake protocol are needed.\n\nSPE has many link speeds but only `10 BASE-T1L` is able to reach cable lengths up to 1000 meters in `2.4 Vpp` transmit amplitude.\nCurrently in 2023, none of the standards are compatible with each other.\nThus `10 BASE-T1L` won't work with a `10 BASE-T1S`, `100 BASE-T1` or any standard `x BASE-T`.\n\nIn the industry SPE is also called [`APL (Advanced Physical Layer)`](https://www.ethernet-apl.org), and is based on the `10 BASE-T1L` standard.\n\nAPL can be used in [`intrinsic safety applications/explosion hazardous areas`](https://en.wikipedia.org/wiki/Electrical_equipment_in_hazardous_areas) which has its own name and standard called [`2-WISE (2-wire intrinsically safe ethernet) IEC TS 60079-47:2021`](https://webstore.iec.ch/publication/64292).\n\n`10 BASE-T1L` and `ADIN1110` are designed to support intrinsic safety applications. The power supply energy is fixed and PDoL is not supported.\n\n## Supported SPI modes\n\n`ADIN1110` supports two SPI modes. `Generic` and [`OPEN Alliance 10BASE-T1x MAC-PHY serial interface`](https://opensig.org/wp-content/uploads/2023/12/OPEN_Alliance_10BASET1x_MAC-PHY_Serial_Interface_V1.1.pdf)\n\nBoth modes support with and without additional CRC.\nCurrently only `Generic` SPI with or without CRC is supported.\n\n*NOTE:* SPI Mode is selected by the hardware pins `SPI_CFG0` and `SPI_CFG1`. Software can't detect nor change the mode.\n\n## Hardware\n\n- Tested on [`Analog Devices EVAL-ADIN1110EBZ`](https://www.analog.com/en/design-center/evaluation-hardware-and-software/evaluation-boards-kits/eval-adin1110.html) with an `STM32L4S5QII3P`, see [`spe_adin1110_http_server`](../examples/stm32l4/src/bin/spe_adin1110_http_server.rs) for an example.\n- [`SparkFun MicroMod Single Pair Ethernet Function Board`](https://www.sparkfun.com/products/19038) or [`SparkFun MicroMod Single Pair Ethernet Kit (End Of Life)`](https://www.sparkfun.com/products/19628), supporting multiple microcontrollers. **Make sure to check if it's a microcontroller that is supported by Embassy!**\n\n## Other SPE chips\n\n* [`Analog ADIN2111`](https://www.analog.com/en/products/adin2111.html) 2 Port SPI version. Can work with this driver.\n* [`Analog ADIN1100`](https://www.analog.com/en/products/adin1100.html) RGMII version.\n\n## Testing\n\nADIN1110 library can tested on the host with a mock SPI driver.\n\n$ `cargo test --target x86_64-unknown-linux-gnu`\n\n## Benchmark\n\n- Benchmarked on [`Analog Devices EVAL-ADIN1110EBZ`](https://www.analog.com/en/design-center/evaluation-hardware-and-software/evaluation-boards-kits/eval-adin1110.html), with [`spe_adin1110_http_server`](../examples/stm32l4/src/bin/spe_adin1110_http_server.rs) example.\n\nBasic `ping` benchmark\n```rust,ignore\n# ping <IP> -c 60\n\n60 packets transmitted, 60 received, 0% packet loss, time 59066ms\nrtt min/avg/max/mdev = 1.089/1.161/1.237/0.018 ms\n\n# ping <IP> -s 1472 -M do -c 60\n\n60 packets transmitted, 60 received, 0% packet loss, time 59066ms\nrtt min/avg/max/mdev = 5.122/5.162/6.177/0.133 ms\n```\n\nHTTP load generator benchmark with [`oha`](https://github.com/hatoo/oha)\n```rust,ignore\n# oha -c 1 http://<IP> -z 60s\nSummary:\n  Success rate: 50.00%\n  Total:        60.0005 secs\n  Slowest:      0.0055 secs\n  Fastest:      0.0033 secs\n  Average:      0.0034 secs\n  Requests/sec: 362.1971\n\n  Total data:   2.99 MiB\n  Size/request: 289 B\n  Size/sec:     51.11 KiB\n```\n"
  },
  {
    "path": "embassy-net-adin1110/src/crc32.rs",
    "content": "/// CRC32 lookup table.\npub const CRC32R_LOOKUP_TABLE: [u32; 256] = [\n    0x0000_0000,\n    0x7707_3096,\n    0xEE0E_612C,\n    0x9909_51BA,\n    0x076D_C419,\n    0x706A_F48F,\n    0xE963_A535,\n    0x9E64_95A3,\n    0x0EDB_8832,\n    0x79DC_B8A4,\n    0xE0D5_E91E,\n    0x97D2_D988,\n    0x09B6_4C2B,\n    0x7EB1_7CBD,\n    0xE7B8_2D07,\n    0x90BF_1D91,\n    0x1DB7_1064,\n    0x6AB0_20F2,\n    0xF3B9_7148,\n    0x84BE_41DE,\n    0x1ADA_D47D,\n    0x6DDD_E4EB,\n    0xF4D4_B551,\n    0x83D3_85C7,\n    0x136C_9856,\n    0x646B_A8C0,\n    0xFD62_F97A,\n    0x8A65_C9EC,\n    0x1401_5C4F,\n    0x6306_6CD9,\n    0xFA0F_3D63,\n    0x8D08_0DF5,\n    0x3B6E_20C8,\n    0x4C69_105E,\n    0xD560_41E4,\n    0xA267_7172,\n    0x3C03_E4D1,\n    0x4B04_D447,\n    0xD20D_85FD,\n    0xA50A_B56B,\n    0x35B5_A8FA,\n    0x42B2_986C,\n    0xDBBB_C9D6,\n    0xACBC_F940,\n    0x32D8_6CE3,\n    0x45DF_5C75,\n    0xDCD6_0DCF,\n    0xABD1_3D59,\n    0x26D9_30AC,\n    0x51DE_003A,\n    0xC8D7_5180,\n    0xBFD0_6116,\n    0x21B4_F4B5,\n    0x56B3_C423,\n    0xCFBA_9599,\n    0xB8BD_A50F,\n    0x2802_B89E,\n    0x5F05_8808,\n    0xC60C_D9B2,\n    0xB10B_E924,\n    0x2F6F_7C87,\n    0x5868_4C11,\n    0xC161_1DAB,\n    0xB666_2D3D,\n    0x76DC_4190,\n    0x01DB_7106,\n    0x98D2_20BC,\n    0xEFD5_102A,\n    0x71B1_8589,\n    0x06B6_B51F,\n    0x9FBF_E4A5,\n    0xE8B8_D433,\n    0x7807_C9A2,\n    0x0F00_F934,\n    0x9609_A88E,\n    0xE10E_9818,\n    0x7F6A_0DBB,\n    0x086D_3D2D,\n    0x9164_6C97,\n    0xE663_5C01,\n    0x6B6B_51F4,\n    0x1C6C_6162,\n    0x8565_30D8,\n    0xF262_004E,\n    0x6C06_95ED,\n    0x1B01_A57B,\n    0x8208_F4C1,\n    0xF50F_C457,\n    0x65B0_D9C6,\n    0x12B7_E950,\n    0x8BBE_B8EA,\n    0xFCB9_887C,\n    0x62DD_1DDF,\n    0x15DA_2D49,\n    0x8CD3_7CF3,\n    0xFBD4_4C65,\n    0x4DB2_6158,\n    0x3AB5_51CE,\n    0xA3BC_0074,\n    0xD4BB_30E2,\n    0x4ADF_A541,\n    0x3DD8_95D7,\n    0xA4D1_C46D,\n    0xD3D6_F4FB,\n    0x4369_E96A,\n    0x346E_D9FC,\n    0xAD67_8846,\n    0xDA60_B8D0,\n    0x4404_2D73,\n    0x3303_1DE5,\n    0xAA0A_4C5F,\n    0xDD0D_7CC9,\n    0x5005_713C,\n    0x2702_41AA,\n    0xBE0B_1010,\n    0xC90C_2086,\n    0x5768_B525,\n    0x206F_85B3,\n    0xB966_D409,\n    0xCE61_E49F,\n    0x5EDE_F90E,\n    0x29D9_C998,\n    0xB0D0_9822,\n    0xC7D7_A8B4,\n    0x59B3_3D17,\n    0x2EB4_0D81,\n    0xB7BD_5C3B,\n    0xC0BA_6CAD,\n    0xEDB8_8320,\n    0x9ABF_B3B6,\n    0x03B6_E20C,\n    0x74B1_D29A,\n    0xEAD5_4739,\n    0x9DD2_77AF,\n    0x04DB_2615,\n    0x73DC_1683,\n    0xE363_0B12,\n    0x9464_3B84,\n    0x0D6D_6A3E,\n    0x7A6A_5AA8,\n    0xE40E_CF0B,\n    0x9309_FF9D,\n    0x0A00_AE27,\n    0x7D07_9EB1,\n    0xF00F_9344,\n    0x8708_A3D2,\n    0x1E01_F268,\n    0x6906_C2FE,\n    0xF762_575D,\n    0x8065_67CB,\n    0x196C_3671,\n    0x6E6B_06E7,\n    0xFED4_1B76,\n    0x89D3_2BE0,\n    0x10DA_7A5A,\n    0x67DD_4ACC,\n    0xF9B9_DF6F,\n    0x8EBE_EFF9,\n    0x17B7_BE43,\n    0x60B0_8ED5,\n    0xD6D6_A3E8,\n    0xA1D1_937E,\n    0x38D8_C2C4,\n    0x4FDF_F252,\n    0xD1BB_67F1,\n    0xA6BC_5767,\n    0x3FB5_06DD,\n    0x48B2_364B,\n    0xD80D_2BDA,\n    0xAF0A_1B4C,\n    0x3603_4AF6,\n    0x4104_7A60,\n    0xDF60_EFC3,\n    0xA867_DF55,\n    0x316E_8EEF,\n    0x4669_BE79,\n    0xCB61_B38C,\n    0xBC66_831A,\n    0x256F_D2A0,\n    0x5268_E236,\n    0xCC0C_7795,\n    0xBB0B_4703,\n    0x2202_16B9,\n    0x5505_262F,\n    0xC5BA_3BBE,\n    0xB2BD_0B28,\n    0x2BB4_5A92,\n    0x5CB3_6A04,\n    0xC2D7_FFA7,\n    0xB5D0_CF31,\n    0x2CD9_9E8B,\n    0x5BDE_AE1D,\n    0x9B64_C2B0,\n    0xEC63_F226,\n    0x756A_A39C,\n    0x026D_930A,\n    0x9C09_06A9,\n    0xEB0E_363F,\n    0x7207_6785,\n    0x0500_5713,\n    0x95BF_4A82,\n    0xE2B8_7A14,\n    0x7BB1_2BAE,\n    0x0CB6_1B38,\n    0x92D2_8E9B,\n    0xE5D5_BE0D,\n    0x7CDC_EFB7,\n    0x0BDB_DF21,\n    0x86D3_D2D4,\n    0xF1D4_E242,\n    0x68DD_B3F8,\n    0x1FDA_836E,\n    0x81BE_16CD,\n    0xF6B9_265B,\n    0x6FB0_77E1,\n    0x18B7_4777,\n    0x8808_5AE6,\n    0xFF0F_6A70,\n    0x6606_3BCA,\n    0x1101_0B5C,\n    0x8F65_9EFF,\n    0xF862_AE69,\n    0x616B_FFD3,\n    0x166C_CF45,\n    0xA00A_E278,\n    0xD70D_D2EE,\n    0x4E04_8354,\n    0x3903_B3C2,\n    0xA767_2661,\n    0xD060_16F7,\n    0x4969_474D,\n    0x3E6E_77DB,\n    0xAED1_6A4A,\n    0xD9D6_5ADC,\n    0x40DF_0B66,\n    0x37D8_3BF0,\n    0xA9BC_AE53,\n    0xDEBB_9EC5,\n    0x47B2_CF7F,\n    0x30B5_FFE9,\n    0xBDBD_F21C,\n    0xCABA_C28A,\n    0x53B3_9330,\n    0x24B4_A3A6,\n    0xBAD0_3605,\n    0xCDD7_0693,\n    0x54DE_5729,\n    0x23D9_67BF,\n    0xB366_7A2E,\n    0xC461_4AB8,\n    0x5D68_1B02,\n    0x2A6F_2B94,\n    0xB40B_BE37,\n    0xC30C_8EA1,\n    0x5A05_DF1B,\n    0x2D02_EF8D,\n];\n\n/// Generate Ethernet Frame Check Sequence\n#[allow(non_camel_case_types)]\n#[derive(Debug)]\npub struct ETH_FCS(pub u32);\n\nimpl ETH_FCS {\n    const CRC32_OK: u32 = 0x2144_df1c;\n\n    /// Create a new frame check sequence from `data`.\n    #[must_use]\n    pub fn new(data: &[u8]) -> Self {\n        let fcs = data.iter().fold(u32::MAX, |crc, byte| {\n            let idx = u8::try_from(crc & 0xFF).unwrap() ^ byte;\n            CRC32R_LOOKUP_TABLE[usize::from(idx)] ^ (crc >> 8)\n        }) ^ u32::MAX;\n        Self(fcs)\n    }\n\n    /// Update the frame check sequence with `data`.\n    #[must_use]\n    pub fn update(self, data: &[u8]) -> Self {\n        let fcs = data.iter().fold(self.0 ^ u32::MAX, |crc, byte| {\n            let idx = u8::try_from(crc & 0xFF).unwrap() ^ byte;\n            CRC32R_LOOKUP_TABLE[usize::from(idx)] ^ (crc >> 8)\n        }) ^ u32::MAX;\n        Self(fcs)\n    }\n\n    /// Check if the frame check sequence is correct.\n    #[must_use]\n    pub fn crc_ok(&self) -> bool {\n        self.0 == Self::CRC32_OK\n    }\n\n    /// Switch byte order.\n    #[must_use]\n    pub fn hton_bytes(&self) -> [u8; 4] {\n        self.0.to_le_bytes()\n    }\n\n    /// Switch byte order as a u32.\n    #[must_use]\n    pub fn hton(&self) -> u32 {\n        self.0.to_le()\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n\n    #[test]\n    fn crc32_ethernet_frame() {\n        let packet_a = &[\n            0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0xe0, 0x4c, 0x68, 0xee, 0xee, 0xff, 0x06, 0x00, 0x01, 0x08, 0x00,\n            0x06, 0x04, 0x00, 0x01, 0x00, 0xe0, 0x4c, 0x68, 0x0e, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n            0x00, 0x00, 0xc0, 0xa8, 0x10, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf2, 0x65, 0x90, 0x3d,\n        ];\n\n        let packet_b = &[\n            0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0x00, 0xe0, 0x4c, 0x68, 0xee, 0xee, 0xdd, 0x06, 0x00, 0x01, 0x08, 0x00,\n            0x06, 0x04, 0x00, 0x02, 0x00, 0xe0, 0x4c, 0x68, 0x09, 0xde, 0xc0, 0xa8, 0x01, 0x02, 0x12, 0x34, 0x56, 0x78,\n            0x9a, 0xbc, 0xc0, 0xa8, 0x01, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x21, 0x3d, 0x67, 0x7c,\n        ];\n\n        // Packet A\n        let own_crc = ETH_FCS::new(&packet_a[0..60]);\n        let crc_bytes = own_crc.hton_bytes();\n        println!(\"{:08x} {:02x?}\", own_crc.0, crc_bytes);\n        assert_eq!(&crc_bytes, &packet_a[60..64]);\n\n        let own_crc = ETH_FCS::new(packet_a);\n        println!(\"{:08x}\", own_crc.0);\n        assert_eq!(own_crc.0, ETH_FCS::CRC32_OK);\n\n        // Packet B\n        let own_crc = ETH_FCS::new(&packet_b[0..60]);\n        let crc_bytes = own_crc.hton_bytes();\n        println!(\"{:08x} {:02x?}\", own_crc.0, crc_bytes);\n        assert_eq!(&crc_bytes, &packet_b[60..64]);\n\n        let own_crc = ETH_FCS::new(packet_b);\n        println!(\"{:08x}\", own_crc.0);\n        assert_eq!(own_crc.0, ETH_FCS::CRC32_OK);\n    }\n\n    #[test]\n    fn crc32_update() {\n        let full_data = &[\n            0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0x00, 0xe0, 0x4c, 0x68, 0xee, 0xee, 0xdd, 0x06, 0x00, 0x01, 0x08, 0x00,\n            0x06, 0x04, 0x00, 0x02, 0x00, 0xe0, 0x4c, 0x68, 0x09, 0xde, 0xc0, 0xa8, 0x01, 0x02, 0x12, 0x34, 0x56, 0x78,\n            0x9a, 0xbc, 0xc0, 0xa8, 0x01, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x21, 0x3d, 0x67, 0x7c,\n        ];\n\n        let (part_a, part_b) = full_data.split_at(16);\n        let crc_partially = ETH_FCS::new(part_a).update(part_b);\n\n        let crc_full = ETH_FCS::new(full_data);\n\n        assert_eq!(crc_full.0, crc_partially.0);\n    }\n}\n"
  },
  {
    "path": "embassy-net-adin1110/src/crc8.rs",
    "content": "/// CRC-8/ITU\nconst CRC8X_TABLE: [u8; 256] = [\n    0x00, 0x07, 0x0e, 0x09, 0x1c, 0x1b, 0x12, 0x15, 0x38, 0x3f, 0x36, 0x31, 0x24, 0x23, 0x2a, 0x2d, 0x70, 0x77, 0x7e,\n    0x79, 0x6c, 0x6b, 0x62, 0x65, 0x48, 0x4f, 0x46, 0x41, 0x54, 0x53, 0x5a, 0x5d, 0xe0, 0xe7, 0xee, 0xe9, 0xfc, 0xfb,\n    0xf2, 0xf5, 0xd8, 0xdf, 0xd6, 0xd1, 0xc4, 0xc3, 0xca, 0xcd, 0x90, 0x97, 0x9e, 0x99, 0x8c, 0x8b, 0x82, 0x85, 0xa8,\n    0xaf, 0xa6, 0xa1, 0xb4, 0xb3, 0xba, 0xbd, 0xc7, 0xc0, 0xc9, 0xce, 0xdb, 0xdc, 0xd5, 0xd2, 0xff, 0xf8, 0xf1, 0xf6,\n    0xe3, 0xe4, 0xed, 0xea, 0xb7, 0xb0, 0xb9, 0xbe, 0xab, 0xac, 0xa5, 0xa2, 0x8f, 0x88, 0x81, 0x86, 0x93, 0x94, 0x9d,\n    0x9a, 0x27, 0x20, 0x29, 0x2e, 0x3b, 0x3c, 0x35, 0x32, 0x1f, 0x18, 0x11, 0x16, 0x03, 0x04, 0x0d, 0x0a, 0x57, 0x50,\n    0x59, 0x5e, 0x4b, 0x4c, 0x45, 0x42, 0x6f, 0x68, 0x61, 0x66, 0x73, 0x74, 0x7d, 0x7a, 0x89, 0x8e, 0x87, 0x80, 0x95,\n    0x92, 0x9b, 0x9c, 0xb1, 0xb6, 0xbf, 0xb8, 0xad, 0xaa, 0xa3, 0xa4, 0xf9, 0xfe, 0xf7, 0xf0, 0xe5, 0xe2, 0xeb, 0xec,\n    0xc1, 0xc6, 0xcf, 0xc8, 0xdd, 0xda, 0xd3, 0xd4, 0x69, 0x6e, 0x67, 0x60, 0x75, 0x72, 0x7b, 0x7c, 0x51, 0x56, 0x5f,\n    0x58, 0x4d, 0x4a, 0x43, 0x44, 0x19, 0x1e, 0x17, 0x10, 0x05, 0x02, 0x0b, 0x0c, 0x21, 0x26, 0x2f, 0x28, 0x3d, 0x3a,\n    0x33, 0x34, 0x4e, 0x49, 0x40, 0x47, 0x52, 0x55, 0x5c, 0x5b, 0x76, 0x71, 0x78, 0x7f, 0x6a, 0x6d, 0x64, 0x63, 0x3e,\n    0x39, 0x30, 0x37, 0x22, 0x25, 0x2c, 0x2b, 0x06, 0x01, 0x08, 0x0f, 0x1a, 0x1d, 0x14, 0x13, 0xae, 0xa9, 0xa0, 0xa7,\n    0xb2, 0xb5, 0xbc, 0xbb, 0x96, 0x91, 0x98, 0x9f, 0x8a, 0x8d, 0x84, 0x83, 0xde, 0xd9, 0xd0, 0xd7, 0xc2, 0xc5, 0xcc,\n    0xcb, 0xe6, 0xe1, 0xe8, 0xef, 0xfa, 0xfd, 0xf4, 0xf3,\n];\n\n/// Calculate the crc of a piece of data.\npub fn crc8(data: &[u8]) -> u8 {\n    data.iter().fold(0, |crc, &byte| CRC8X_TABLE[usize::from(byte ^ crc)])\n}\n\n#[cfg(test)]\nmod tests {\n    use ::crc::{CRC_8_SMBUS, Crc};\n\n    use super::crc8;\n\n    #[test]\n    fn spi_header_crc8() {\n        let data = &[0x80, 0x00];\n\n        let c = Crc::<u8>::new(&CRC_8_SMBUS);\n        let mut dig = c.digest();\n        dig.update(data);\n        let sw_crc = dig.finalize();\n\n        let own_crc = crc8(data);\n\n        assert_eq!(own_crc, sw_crc);\n        assert_eq!(own_crc, 182);\n\n        let data = &[0x80, 0x01];\n        let mut dig = c.digest();\n        dig.update(data);\n        let sw_crc = dig.finalize();\n        let own_crc = crc8(data);\n\n        assert_eq!(own_crc, sw_crc);\n        assert_eq!(own_crc, 177);\n    }\n}\n"
  },
  {
    "path": "embassy-net-adin1110/src/fmt.rs",
    "content": "#![macro_use]\n#![allow(unused)]\n\nuse core::fmt::{Debug, Display, LowerHex};\n\n#[cfg(all(feature = \"defmt\", feature = \"log\"))]\ncompile_error!(\"You may not enable both `defmt` and `log` features.\");\n\n#[collapse_debuginfo(yes)]\nmacro_rules! assert {\n    ($($x:tt)*) => {\n        {\n            #[cfg(not(feature = \"defmt\"))]\n            ::core::assert!($($x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::assert!($($x)*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! assert_eq {\n    ($($x:tt)*) => {\n        {\n            #[cfg(not(feature = \"defmt\"))]\n            ::core::assert_eq!($($x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::assert_eq!($($x)*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! assert_ne {\n    ($($x:tt)*) => {\n        {\n            #[cfg(not(feature = \"defmt\"))]\n            ::core::assert_ne!($($x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::assert_ne!($($x)*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! debug_assert {\n    ($($x:tt)*) => {\n        {\n            #[cfg(not(feature = \"defmt\"))]\n            ::core::debug_assert!($($x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::debug_assert!($($x)*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! debug_assert_eq {\n    ($($x:tt)*) => {\n        {\n            #[cfg(not(feature = \"defmt\"))]\n            ::core::debug_assert_eq!($($x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::debug_assert_eq!($($x)*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! debug_assert_ne {\n    ($($x:tt)*) => {\n        {\n            #[cfg(not(feature = \"defmt\"))]\n            ::core::debug_assert_ne!($($x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::debug_assert_ne!($($x)*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! todo {\n    ($($x:tt)*) => {\n        {\n            #[cfg(not(feature = \"defmt\"))]\n            ::core::todo!($($x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::todo!($($x)*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! unreachable {\n    ($($x:tt)*) => {\n        {\n            #[cfg(not(feature = \"defmt\"))]\n            ::core::unreachable!($($x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::unreachable!($($x)*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! panic {\n    ($($x:tt)*) => {\n        {\n            #[cfg(not(feature = \"defmt\"))]\n            ::core::panic!($($x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::panic!($($x)*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! trace {\n    ($s:literal $(, $x:expr)* $(,)?) => {\n        {\n            #[cfg(feature = \"log\")]\n            ::log::trace!($s $(, $x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::trace!($s $(, $x)*);\n            #[cfg(not(any(feature = \"log\", feature=\"defmt\")))]\n            let _ = ($( & $x ),*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! debug {\n    ($s:literal $(, $x:expr)* $(,)?) => {\n        {\n            #[cfg(feature = \"log\")]\n            ::log::debug!($s $(, $x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::debug!($s $(, $x)*);\n            #[cfg(not(any(feature = \"log\", feature=\"defmt\")))]\n            let _ = ($( & $x ),*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! info {\n    ($s:literal $(, $x:expr)* $(,)?) => {\n        {\n            #[cfg(feature = \"log\")]\n            ::log::info!($s $(, $x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::info!($s $(, $x)*);\n            #[cfg(not(any(feature = \"log\", feature=\"defmt\")))]\n            let _ = ($( & $x ),*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! warn {\n    ($s:literal $(, $x:expr)* $(,)?) => {\n        {\n            #[cfg(feature = \"log\")]\n            ::log::warn!($s $(, $x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::warn!($s $(, $x)*);\n            #[cfg(not(any(feature = \"log\", feature=\"defmt\")))]\n            let _ = ($( & $x ),*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! error {\n    ($s:literal $(, $x:expr)* $(,)?) => {\n        {\n            #[cfg(feature = \"log\")]\n            ::log::error!($s $(, $x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::error!($s $(, $x)*);\n            #[cfg(not(any(feature = \"log\", feature=\"defmt\")))]\n            let _ = ($( & $x ),*);\n        }\n    };\n}\n\n#[cfg(feature = \"defmt\")]\n#[collapse_debuginfo(yes)]\nmacro_rules! unwrap {\n    ($($x:tt)*) => {\n        ::defmt::unwrap!($($x)*)\n    };\n}\n\n#[cfg(not(feature = \"defmt\"))]\n#[collapse_debuginfo(yes)]\nmacro_rules! unwrap {\n    ($arg:expr) => {\n        match $crate::fmt::Try::into_result($arg) {\n            ::core::result::Result::Ok(t) => t,\n            ::core::result::Result::Err(e) => {\n                ::core::panic!(\"unwrap of `{}` failed: {:?}\", ::core::stringify!($arg), e);\n            }\n        }\n    };\n    ($arg:expr, $($msg:expr),+ $(,)? ) => {\n        match $crate::fmt::Try::into_result($arg) {\n            ::core::result::Result::Ok(t) => t,\n            ::core::result::Result::Err(e) => {\n                ::core::panic!(\"unwrap of `{}` failed: {}: {:?}\", ::core::stringify!($arg), ::core::format_args!($($msg,)*), e);\n            }\n        }\n    }\n}\n\n#[derive(Debug, Copy, Clone, Eq, PartialEq)]\npub struct NoneError;\n\npub trait Try {\n    type Ok;\n    type Error;\n    fn into_result(self) -> Result<Self::Ok, Self::Error>;\n}\n\nimpl<T> Try for Option<T> {\n    type Ok = T;\n    type Error = NoneError;\n\n    #[inline]\n    fn into_result(self) -> Result<T, NoneError> {\n        self.ok_or(NoneError)\n    }\n}\n\nimpl<T, E> Try for Result<T, E> {\n    type Ok = T;\n    type Error = E;\n\n    #[inline]\n    fn into_result(self) -> Self {\n        self\n    }\n}\n\npub(crate) struct Bytes<'a>(pub &'a [u8]);\n\nimpl<'a> Debug for Bytes<'a> {\n    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {\n        write!(f, \"{:#02x?}\", self.0)\n    }\n}\n\nimpl<'a> Display for Bytes<'a> {\n    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {\n        write!(f, \"{:#02x?}\", self.0)\n    }\n}\n\nimpl<'a> LowerHex for Bytes<'a> {\n    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {\n        write!(f, \"{:#02x?}\", self.0)\n    }\n}\n\n#[cfg(feature = \"defmt\")]\nimpl<'a> defmt::Format for Bytes<'a> {\n    fn format(&self, fmt: defmt::Formatter) {\n        defmt::write!(fmt, \"{:02x}\", self.0)\n    }\n}\n"
  },
  {
    "path": "embassy-net-adin1110/src/lib.rs",
    "content": "#![cfg_attr(not(test), no_std)]\n#![deny(clippy::pedantic)]\n#![allow(async_fn_in_trait)]\n#![allow(clippy::module_name_repetitions)]\n#![allow(clippy::missing_errors_doc)]\n#![allow(clippy::missing_panics_doc)]\n#![doc = include_str!(\"../README.md\")]\n#![warn(missing_docs)]\n\n// must go first!\nmod fmt;\n\nmod crc32;\nmod crc8;\nmod mdio;\nmod phy;\nmod regs;\n\nuse ch::driver::LinkState;\nuse crc8::crc8;\npub use crc32::ETH_FCS;\nuse embassy_futures::select::{Either, select};\nuse embassy_net_driver_channel as ch;\nuse embassy_time::Timer;\nuse embedded_hal_1::digital::OutputPin;\nuse embedded_hal_async::digital::Wait;\nuse embedded_hal_async::spi::{Error, Operation, SpiDevice};\nuse heapless::Vec;\npub use mdio::MdioBus;\npub use phy::Phy10BaseT1x;\nuse phy::{RegsC22, RegsC45};\nuse regs::{Config0, Config2, SpiRegisters as sr, Status0, Status1};\n\nuse crate::fmt::Bytes;\nuse crate::regs::{LedCntrl, LedFunc, LedPol, LedPolarity, SpiHeader};\n\n/// ADIN1110 intern PHY ID\npub const PHYID: u32 = 0x0283_BC91;\n\n/// Error values ADIN1110\n#[derive(Debug)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\n#[allow(non_camel_case_types)]\npub enum AdinError<E> {\n    /// SPI-BUS Error\n    Spi(E),\n    /// Ethernet FCS error\n    FCS,\n    /// SPI Header CRC error\n    SPI_CRC,\n    /// Received or sended ethernet packet is too big\n    PACKET_TOO_BIG,\n    /// Received or sended ethernet packet is too small\n    PACKET_TOO_SMALL,\n    /// MDIO transaction timeout\n    MDIO_ACC_TIMEOUT,\n}\n\n/// Type alias `Result` type with `AdinError` as error type.\npub type AEResult<T, SPIError> = core::result::Result<T, AdinError<SPIError>>;\n\n/// Internet PHY address\npub const MDIO_PHY_ADDR: u8 = 0x01;\n\n/// Maximum Transmission Unit\npub const MTU: usize = 1514;\n\n/// Max SPI/Frame buffer size\npub const MAX_BUFF: usize = 2048;\n\nconst DONT_CARE_BYTE: u8 = 0x00;\nconst TURN_AROUND_BYTE: u8 = 0x00;\n\n/// Packet minimal frame/packet length\nconst ETH_MIN_LEN: usize = 64;\n/// Ethernet `Frame Check Sequence` length\nconst FCS_LEN: usize = 4;\n/// Packet minimal frame/packet length without `Frame Check Sequence` length\nconst ETH_MIN_WITHOUT_FCS_LEN: usize = ETH_MIN_LEN - FCS_LEN;\n\n/// SPI Header, contains SPI action and register id.\nconst SPI_HEADER_LEN: usize = 2;\n/// SPI Header CRC length\nconst SPI_HEADER_CRC_LEN: usize = 1;\n/// SPI Header Turn Around length\nconst SPI_HEADER_TA_LEN: usize = 1;\n/// Frame Header length\nconst FRAME_HEADER_LEN: usize = 2;\n/// Space for last bytes to create multipule 4 bytes on the end of a FIFO read/write.\nconst SPI_SPACE_MULTIPULE: usize = 3;\n\n/// P1 = 0x00, P2 = 0x01\nconst PORT_ID_BYTE: u8 = 0x00;\n\n/// Type alias for the embassy-net driver for ADIN1110\npub type Device<'d> = embassy_net_driver_channel::Device<'d, MTU>;\n\n/// Internal state for the embassy-net integration.\npub struct State<const N_RX: usize, const N_TX: usize> {\n    ch_state: ch::State<MTU, N_RX, N_TX>,\n}\nimpl<const N_RX: usize, const N_TX: usize> State<N_RX, N_TX> {\n    /// Create a new `State`.\n    #[must_use]\n    pub const fn new() -> Self {\n        Self {\n            ch_state: ch::State::new(),\n        }\n    }\n}\n\n/// ADIN1110 embassy-net driver\n#[derive(Debug)]\npub struct ADIN1110<SPI> {\n    /// SPI bus\n    spi: SPI,\n    /// Enable CRC on SPI transfer.\n    /// This must match with the hardware pin `SPI_CFG0` were low = CRC enable, high = CRC disabled.\n    spi_crc: bool,\n    /// Append FCS by the application of transmit packet, false = FCS is appended by the MAC, true = FCS appended by the application.\n    append_fcs_on_tx: bool,\n}\n\nimpl<SPI: SpiDevice> ADIN1110<SPI> {\n    /// Create a new ADIN1110 instance.\n    pub fn new(spi: SPI, spi_crc: bool, append_fcs_on_tx: bool) -> Self {\n        Self {\n            spi,\n            spi_crc,\n            append_fcs_on_tx,\n        }\n    }\n\n    /// Read a SPI register\n    pub async fn read_reg(&mut self, reg: sr) -> AEResult<u32, SPI::Error> {\n        let mut tx_buf = Vec::<u8, 16>::new();\n\n        let mut spi_hdr = SpiHeader(0);\n        spi_hdr.set_control(true);\n        spi_hdr.set_addr(reg);\n        let _ = tx_buf.extend_from_slice(spi_hdr.0.to_be_bytes().as_slice());\n\n        if self.spi_crc {\n            // Add CRC for header data\n            let _ = tx_buf.push(crc8(&tx_buf));\n        }\n\n        // Turn around byte, give the chip the time to access/setup the answer data.\n        let _ = tx_buf.push(TURN_AROUND_BYTE);\n\n        let mut rx_buf = [0; 5];\n\n        let spi_read_len = if self.spi_crc { rx_buf.len() } else { rx_buf.len() - 1 };\n\n        let mut spi_op = [Operation::Write(&tx_buf), Operation::Read(&mut rx_buf[0..spi_read_len])];\n\n        self.spi.transaction(&mut spi_op).await.map_err(AdinError::Spi)?;\n\n        if self.spi_crc {\n            let crc = crc8(&rx_buf[0..4]);\n            if crc != rx_buf[4] {\n                return Err(AdinError::SPI_CRC);\n            }\n        }\n\n        let value = u32::from_be_bytes(rx_buf[0..4].try_into().unwrap());\n\n        trace!(\"REG Read {} = {:08x} SPI {}\", reg, value, Bytes(&tx_buf));\n\n        Ok(value)\n    }\n\n    /// Write a SPI register\n    pub async fn write_reg(&mut self, reg: sr, value: u32) -> AEResult<(), SPI::Error> {\n        let mut tx_buf = Vec::<u8, 16>::new();\n\n        let mut spi_hdr = SpiHeader(0);\n        spi_hdr.set_control(true);\n        spi_hdr.set_write(true);\n        spi_hdr.set_addr(reg);\n        let _ = tx_buf.extend_from_slice(spi_hdr.0.to_be_bytes().as_slice());\n\n        if self.spi_crc {\n            // Add CRC for header data\n            let _ = tx_buf.push(crc8(&tx_buf));\n        }\n\n        let val = value.to_be_bytes();\n        let _ = tx_buf.extend_from_slice(val.as_slice());\n\n        if self.spi_crc {\n            // Add CRC for header data\n            let _ = tx_buf.push(crc8(val.as_slice()));\n        }\n\n        trace!(\"REG Write {} = {:08x} SPI {}\", reg, value, Bytes(&tx_buf));\n\n        self.spi.write(&tx_buf).await.map_err(AdinError::Spi)\n    }\n\n    /// helper function for write to `MDIO_ACC` register and wait for ready!\n    async fn write_mdio_acc_reg(&mut self, mdio_acc_val: u32) -> AEResult<u32, SPI::Error> {\n        self.write_reg(sr::MDIO_ACC, mdio_acc_val).await?;\n\n        // TODO: Add proper timeout!\n        for _ in 0..100_000 {\n            let val = self.read_reg(sr::MDIO_ACC).await?;\n            if val & 0x8000_0000 != 0 {\n                return Ok(val);\n            }\n        }\n\n        Err(AdinError::MDIO_ACC_TIMEOUT)\n    }\n\n    /// Read out fifo ethernet packet memory received via the wire.\n    pub async fn read_fifo(&mut self, frame: &mut [u8]) -> AEResult<usize, SPI::Error> {\n        const HEAD_LEN: usize = SPI_HEADER_LEN + SPI_HEADER_CRC_LEN + SPI_HEADER_TA_LEN;\n        const TAIL_LEN: usize = FCS_LEN + SPI_SPACE_MULTIPULE;\n\n        let mut tx_buf = Vec::<u8, HEAD_LEN>::new();\n\n        // Size of the frame, also includes the `frame header` and `FCS`.\n        let fifo_frame_size = self.read_reg(sr::RX_FSIZE).await? as usize;\n\n        if fifo_frame_size < ETH_MIN_LEN + FRAME_HEADER_LEN {\n            return Err(AdinError::PACKET_TOO_SMALL);\n        }\n\n        let packet_size = fifo_frame_size - FRAME_HEADER_LEN - FCS_LEN;\n\n        if packet_size > frame.len() {\n            trace!(\"MAX: {} WANT: {}\", frame.len(), packet_size);\n            return Err(AdinError::PACKET_TOO_BIG);\n        }\n\n        let mut spi_hdr = SpiHeader(0);\n        spi_hdr.set_control(true);\n        spi_hdr.set_addr(sr::RX);\n        let _ = tx_buf.extend_from_slice(spi_hdr.0.to_be_bytes().as_slice());\n\n        if self.spi_crc {\n            // Add CRC for header data\n            let _ = tx_buf.push(crc8(&tx_buf));\n        }\n\n        // Turn around byte, TODO: Unknown that this is.\n        let _ = tx_buf.push(TURN_AROUND_BYTE);\n\n        let mut frame_header = [0, 0];\n        let mut fcs_and_extra = [0; TAIL_LEN];\n\n        // Packet read of write to the MAC packet buffer must be a multipul of 4!\n        let tail_size = (fifo_frame_size & 0x03) + FCS_LEN;\n\n        let mut spi_op = [\n            Operation::Write(&tx_buf),\n            Operation::Read(&mut frame_header),\n            Operation::Read(&mut frame[0..packet_size]),\n            Operation::Read(&mut fcs_and_extra[0..tail_size]),\n        ];\n\n        self.spi.transaction(&mut spi_op).await.map_err(AdinError::Spi)?;\n\n        // According to register `CONFIG2`, bit 5 `CRC_APPEND` discription:\n        // \"Similarly, on receive, the CRC32 is forwarded with the frame to the host where the host must verify it is correct.\"\n        // The application must allways check the FCS. It seems that the MAC/PHY has no option to handle this.\n        let fcs_calc = ETH_FCS::new(&frame[0..packet_size]);\n\n        if fcs_calc.hton_bytes() == fcs_and_extra[0..4] {\n            Ok(packet_size)\n        } else {\n            Err(AdinError::FCS)\n        }\n    }\n\n    /// Write to fifo ethernet packet memory send over the wire.\n    pub async fn write_fifo(&mut self, frame: &[u8]) -> AEResult<(), SPI::Error> {\n        const HEAD_LEN: usize = SPI_HEADER_LEN + SPI_HEADER_CRC_LEN + FRAME_HEADER_LEN;\n        const TAIL_LEN: usize = ETH_MIN_LEN - FCS_LEN + FCS_LEN + SPI_SPACE_MULTIPULE;\n\n        if frame.len() < (6 + 6 + 2) {\n            return Err(AdinError::PACKET_TOO_SMALL);\n        }\n        if frame.len() > (MAX_BUFF - FRAME_HEADER_LEN) {\n            return Err(AdinError::PACKET_TOO_BIG);\n        }\n\n        // SPI HEADER + [OPTIONAL SPI CRC] + FRAME HEADER\n        let mut head_data = Vec::<u8, HEAD_LEN>::new();\n        // [OPTIONAL PAD DATA] + FCS + [OPTINAL BYTES MAKE SPI FRAME EVEN]\n        let mut tail_data = Vec::<u8, TAIL_LEN>::new();\n\n        let mut spi_hdr = SpiHeader(0);\n        spi_hdr.set_control(true);\n        spi_hdr.set_write(true);\n        spi_hdr.set_addr(sr::TX);\n\n        head_data\n            .extend_from_slice(spi_hdr.0.to_be_bytes().as_slice())\n            .map_err(|_e| AdinError::PACKET_TOO_BIG)?;\n\n        if self.spi_crc {\n            // Add CRC for header data\n            head_data\n                .push(crc8(&head_data[0..2]))\n                .map_err(|_| AdinError::PACKET_TOO_BIG)?;\n        }\n\n        // Add port number, ADIN1110 its fixed to zero/P1, but for ADIN2111 has two ports.\n        head_data\n            .extend_from_slice(u16::from(PORT_ID_BYTE).to_be_bytes().as_slice())\n            .map_err(|_e| AdinError::PACKET_TOO_BIG)?;\n\n        // ADIN1110 MAC and PHY don´t accept ethernet packet smaller than 64 bytes.\n        // So padded the data minus the FCS, FCS is automatilly added to by the MAC.\n        if frame.len() < ETH_MIN_WITHOUT_FCS_LEN {\n            let _ = tail_data.resize(ETH_MIN_WITHOUT_FCS_LEN - frame.len(), 0x00);\n        }\n\n        // Append FCS by the application\n        if self.append_fcs_on_tx {\n            let mut frame_fcs = ETH_FCS::new(frame);\n\n            if !tail_data.is_empty() {\n                frame_fcs = frame_fcs.update(&tail_data);\n            }\n\n            let _ = tail_data.extend_from_slice(frame_fcs.hton_bytes().as_slice());\n        }\n\n        // len = frame_size + optional padding + 2 bytes Frame header\n        let send_len_orig = frame.len() + tail_data.len() + FRAME_HEADER_LEN;\n\n        let send_len = u32::try_from(send_len_orig).map_err(|_| AdinError::PACKET_TOO_BIG)?;\n\n        // Packet read of write to the MAC packet buffer must be a multipul of 4 bytes!\n        let pad_len = send_len_orig & 0x03;\n        if pad_len != 0 {\n            let spi_pad_len = 4 - pad_len + tail_data.len();\n            let _ = tail_data.resize(spi_pad_len, DONT_CARE_BYTE);\n        }\n\n        self.write_reg(sr::TX_FSIZE, send_len).await?;\n\n        trace!(\n            \"TX: hdr {} [{}] {}-{}-{} SIZE: {}\",\n            head_data.len(),\n            frame.len(),\n            Bytes(head_data.as_slice()),\n            Bytes(frame),\n            Bytes(tail_data.as_slice()),\n            send_len,\n        );\n\n        let mut transaction = [\n            Operation::Write(head_data.as_slice()),\n            Operation::Write(frame),\n            Operation::Write(tail_data.as_slice()),\n        ];\n\n        self.spi.transaction(&mut transaction).await.map_err(AdinError::Spi)\n    }\n\n    /// Programs the mac address in the mac filters.\n    /// Also set the boardcast address.\n    /// The chip supports 2 priority queues but current code doesn't support this mode.\n    pub async fn set_mac_addr(&mut self, mac: &[u8; 6]) -> AEResult<(), SPI::Error> {\n        let mac_high_part = u16::from_be_bytes(mac[0..2].try_into().unwrap());\n        let mac_low_part = u32::from_be_bytes(mac[2..6].try_into().unwrap());\n\n        // program our mac address in the mac address filter\n        self.write_reg(sr::ADDR_FILT_UPR0, (1 << 16) | (1 << 30) | u32::from(mac_high_part))\n            .await?;\n        self.write_reg(sr::ADDR_FILT_LWR0, mac_low_part).await?;\n\n        self.write_reg(sr::ADDR_MSK_UPR0, u32::from(mac_high_part)).await?;\n        self.write_reg(sr::ADDR_MSK_LWR0, mac_low_part).await?;\n\n        // Also program broadcast address in the mac address filter\n        self.write_reg(sr::ADDR_FILT_UPR1, (1 << 16) | (1 << 30) | 0xFFFF)\n            .await?;\n        self.write_reg(sr::ADDR_FILT_LWR1, 0xFFFF_FFFF).await?;\n        self.write_reg(sr::ADDR_MSK_UPR1, 0xFFFF).await?;\n        self.write_reg(sr::ADDR_MSK_LWR1, 0xFFFF_FFFF).await?;\n\n        Ok(())\n    }\n}\n\nimpl<SPI: SpiDevice> mdio::MdioBus for ADIN1110<SPI> {\n    type Error = AdinError<SPI::Error>;\n\n    /// Read from the PHY Registers as Clause 22.\n    async fn read_cl22(&mut self, phy_id: u8, reg: u8) -> Result<u16, Self::Error> {\n        let mdio_acc_val: u32 =\n            (0x1 << 28) | u32::from(phy_id & 0x1F) << 21 | u32::from(reg & 0x1F) << 16 | (0x3 << 26);\n\n        // Result is in the lower half of the answer.\n        #[allow(clippy::cast_possible_truncation)]\n        self.write_mdio_acc_reg(mdio_acc_val).await.map(|val| val as u16)\n    }\n\n    /// Read from the PHY Registers as Clause 45.\n    async fn read_cl45(&mut self, phy_id: u8, regc45: (u8, u16)) -> Result<u16, Self::Error> {\n        let mdio_acc_val = u32::from(phy_id & 0x1F) << 21 | u32::from(regc45.0 & 0x1F) << 16 | u32::from(regc45.1);\n\n        self.write_mdio_acc_reg(mdio_acc_val).await?;\n\n        let mdio_acc_val = u32::from(phy_id & 0x1F) << 21 | u32::from(regc45.0 & 0x1F) << 16 | (0x03 << 26);\n\n        // Result is in the lower half of the answer.\n        #[allow(clippy::cast_possible_truncation)]\n        self.write_mdio_acc_reg(mdio_acc_val).await.map(|val| val as u16)\n    }\n\n    /// Write to the PHY Registers as Clause 22.\n    async fn write_cl22(&mut self, phy_id: u8, reg: u8, val: u16) -> Result<(), Self::Error> {\n        let mdio_acc_val: u32 =\n            (0x1 << 28) | u32::from(phy_id & 0x1F) << 21 | u32::from(reg & 0x1F) << 16 | (0x1 << 26) | u32::from(val);\n\n        self.write_mdio_acc_reg(mdio_acc_val).await.map(|_| ())\n    }\n\n    /// Write to the PHY Registers as Clause 45.\n    async fn write_cl45(&mut self, phy_id: u8, regc45: (u8, u16), value: u16) -> AEResult<(), SPI::Error> {\n        let phy_id = u32::from(phy_id & 0x1F) << 21;\n        let dev_addr = u32::from(regc45.0 & 0x1F) << 16;\n        let reg = u32::from(regc45.1);\n\n        let mdio_acc_val: u32 = phy_id | dev_addr | reg;\n        self.write_mdio_acc_reg(mdio_acc_val).await?;\n\n        let mdio_acc_val: u32 = phy_id | dev_addr | (0x01 << 26) | u32::from(value);\n        self.write_mdio_acc_reg(mdio_acc_val).await.map(|_| ())\n    }\n}\n\n/// Background runner for the ADIN1110.\n///\n/// You must call `.run()` in a background task for the ADIN1110 to operate.\npub struct Runner<'d, SPI, INT, RST> {\n    mac: ADIN1110<SPI>,\n    ch: ch::Runner<'d, MTU>,\n    int: INT,\n    is_link_up: bool,\n    _reset: RST,\n}\n\nimpl<'d, SPI: SpiDevice, INT: Wait, RST: OutputPin> Runner<'d, SPI, INT, RST> {\n    /// Run the driver.\n    #[allow(clippy::too_many_lines)]\n    pub async fn run(mut self) -> ! {\n        loop {\n            let (state_chan, mut rx_chan, mut tx_chan) = self.ch.split();\n\n            loop {\n                debug!(\"Waiting for interrupts\");\n                match select(self.int.wait_for_low(), tx_chan.tx_buf()).await {\n                    Either::First(_) => {\n                        let mut status1_clr = Status1(0);\n                        let mut status1 = Status1(self.mac.read_reg(sr::STATUS1).await.unwrap());\n\n                        while status1.p1_rx_rdy() {\n                            debug!(\"alloc RX packet buffer\");\n                            match select(rx_chan.rx_buf(), tx_chan.tx_buf()).await {\n                                // Handle frames that needs to transmit from the wire.\n                                // Note: rx_chan.rx_buf() channel don´t accept new request\n                                //       when the tx_chan is full. So these will be handled\n                                //       automaticly.\n                                Either::First(frame) => match self.mac.read_fifo(frame).await {\n                                    Ok(n) => {\n                                        rx_chan.rx_done(n);\n                                    }\n                                    Err(e) => match e {\n                                        AdinError::PACKET_TOO_BIG => {\n                                            error!(\"RX Packet too big, DROP\");\n                                            self.mac.write_reg(sr::FIFO_CLR, 1).await.unwrap();\n                                        }\n                                        AdinError::PACKET_TOO_SMALL => {\n                                            error!(\"RX Packet too small, DROP\");\n                                            self.mac.write_reg(sr::FIFO_CLR, 1).await.unwrap();\n                                        }\n                                        AdinError::Spi(e) => {\n                                            error!(\"RX Spi error {}\", e.kind());\n                                        }\n                                        e => {\n                                            error!(\"RX Error {:?}\", e);\n                                        }\n                                    },\n                                },\n                                Either::Second(frame) => {\n                                    // Handle frames that needs to transmit to the wire.\n                                    self.mac.write_fifo(frame).await.unwrap();\n                                    tx_chan.tx_done();\n                                }\n                            }\n                            status1 = Status1(self.mac.read_reg(sr::STATUS1).await.unwrap());\n                        }\n\n                        let status0 = Status0(self.mac.read_reg(sr::STATUS0).await.unwrap());\n                        if status1.0 & !0x1b != 0 {\n                            error!(\"SPE CHIP STATUS 0:{:08x} 1:{:08x}\", status0.0, status1.0);\n                        }\n\n                        if status1.tx_rdy() {\n                            status1_clr.set_tx_rdy(true);\n                            trace!(\"TX_DONE\");\n                        }\n\n                        if status1.link_change() {\n                            let link = status1.p1_link_status();\n                            self.is_link_up = link;\n\n                            if link {\n                                let link_status = self\n                                    .mac\n                                    .read_cl45(MDIO_PHY_ADDR, RegsC45::DA7::AN_STATUS_EXTRA.into())\n                                    .await\n                                    .unwrap();\n\n                                let volt = if link_status & (0b11 << 5) == (0b11 << 5) {\n                                    \"2.4\"\n                                } else {\n                                    \"1.0\"\n                                };\n\n                                let mse = self\n                                    .mac\n                                    .read_cl45(MDIO_PHY_ADDR, RegsC45::DA1::MSE_VAL.into())\n                                    .await\n                                    .unwrap();\n\n                                info!(\"LINK Changed: Link Up, Volt: {} V p-p, MSE: {:0004}\", volt, mse);\n                            } else {\n                                info!(\"LINK Changed: Link Down\");\n                            }\n\n                            state_chan.set_link_state(if link { LinkState::Up } else { LinkState::Down });\n                            status1_clr.set_link_change(true);\n                        }\n\n                        if status1.tx_ecc_err() {\n                            error!(\"SPI TX_ECC_ERR error, CLEAR TX FIFO\");\n                            self.mac.write_reg(sr::FIFO_CLR, 2).await.unwrap();\n                            status1_clr.set_tx_ecc_err(true);\n                        }\n\n                        if status1.rx_ecc_err() {\n                            error!(\"SPI RX_ECC_ERR error\");\n                            status1_clr.set_rx_ecc_err(true);\n                        }\n\n                        if status1.spi_err() {\n                            error!(\"SPI SPI_ERR CRC error\");\n                            status1_clr.set_spi_err(true);\n                        }\n\n                        if status0.phyint() {\n                            let crsm_irq_st = self\n                                .mac\n                                .read_cl45(MDIO_PHY_ADDR, RegsC45::DA1E::CRSM_IRQ_STATUS.into())\n                                .await\n                                .unwrap();\n\n                            let phy_irq_st = self\n                                .mac\n                                .read_cl45(MDIO_PHY_ADDR, RegsC45::DA1F::PHY_SYBSYS_IRQ_STATUS.into())\n                                .await\n                                .unwrap();\n\n                            warn!(\n                                \"SPE CHIP PHY CRSM_IRQ_STATUS {:04x} PHY_SUBSYS_IRQ_STATUS {:04x}\",\n                                crsm_irq_st, phy_irq_st\n                            );\n                        }\n\n                        if status0.txfcse() {\n                            error!(\"Ethernet Frame FCS and calc FCS don't match!\");\n                        }\n\n                        // Clear status0\n                        self.mac.write_reg(sr::STATUS0, 0xFFF).await.unwrap();\n                        self.mac.write_reg(sr::STATUS1, status1_clr.0).await.unwrap();\n                    }\n                    Either::Second(packet) => {\n                        // Handle frames that needs to transmit to the wire.\n                        self.mac.write_fifo(packet).await.unwrap();\n                        tx_chan.tx_done();\n                    }\n                }\n            }\n        }\n    }\n}\n\n/// Obtain a driver for using the ADIN1110 with [`embassy-net`](crates.io/crates/embassy-net).\npub async fn new<const N_RX: usize, const N_TX: usize, SPI: SpiDevice, INT: Wait, RST: OutputPin>(\n    mac_addr: [u8; 6],\n    state: &'_ mut State<N_RX, N_TX>,\n    spi_dev: SPI,\n    int: INT,\n    mut reset: RST,\n    spi_crc: bool,\n    append_fcs_on_tx: bool,\n) -> (Device<'_>, Runner<'_, SPI, INT, RST>) {\n    use crate::regs::{IMask0, IMask1};\n\n    info!(\"INIT ADIN1110\");\n\n    // Reset sequence\n    reset.set_low().unwrap();\n\n    // Wait t1: 20-43mS\n    Timer::after_millis(30).await;\n\n    reset.set_high().unwrap();\n\n    // Wait t3: 50mS\n    Timer::after_millis(50).await;\n\n    // Create device\n    let mut mac = ADIN1110::new(spi_dev, spi_crc, append_fcs_on_tx);\n\n    // Check PHYID\n    let id = mac.read_reg(sr::PHYID).await.unwrap();\n    assert_eq!(id, PHYID);\n\n    debug!(\"SPE: CHIP MAC/ID: {:08x}\", id);\n\n    #[cfg(any(feature = \"defmt\", feature = \"log\"))]\n    {\n        let adin_phy = Phy10BaseT1x::default();\n        let phy_id = adin_phy.get_id(&mut mac).await.unwrap();\n        debug!(\"SPE: CHIP: PHY ID: {:08x}\", phy_id);\n    }\n\n    let mi_control = mac.read_cl22(MDIO_PHY_ADDR, RegsC22::CONTROL as u8).await.unwrap();\n    debug!(\"SPE CHIP PHY MI_CONTROL {:04x}\", mi_control);\n    if mi_control & 0x0800 != 0 {\n        let val = mi_control & !0x0800;\n        debug!(\"SPE CHIP PHY MI_CONTROL Disable PowerDown\");\n        mac.write_cl22(MDIO_PHY_ADDR, RegsC22::CONTROL as u8, val)\n            .await\n            .unwrap();\n    }\n\n    // Config0\n    let mut config0 = Config0(0x0000_0006);\n    config0.set_txfcsve(mac.append_fcs_on_tx);\n    mac.write_reg(sr::CONFIG0, config0.0).await.unwrap();\n\n    // Config2\n    let mut config2 = Config2(0x0000_0800);\n    // crc_append must be disable if tx_fcs_validation_enable is true!\n    config2.set_crc_append(!mac.append_fcs_on_tx);\n    mac.write_reg(sr::CONFIG2, config2.0).await.unwrap();\n\n    // Pin Mux Config 1\n    let led_val = (0b11 << 6) | (0b11 << 4); // | (0b00 << 1);\n    mac.write_cl45(MDIO_PHY_ADDR, RegsC45::DA1E::DIGIO_PINMUX.into(), led_val)\n        .await\n        .unwrap();\n\n    let mut led_pol = LedPolarity(0);\n    led_pol.set_led1_polarity(LedPol::ActiveLow);\n    led_pol.set_led0_polarity(LedPol::ActiveLow);\n\n    // Led Polarity Regisgere Active Low\n    mac.write_cl45(MDIO_PHY_ADDR, RegsC45::DA1E::LED_POLARITY.into(), led_pol.0)\n        .await\n        .unwrap();\n\n    // Led Both On\n    let mut led_cntr = LedCntrl(0x0);\n\n    // LED1: Yellow\n    led_cntr.set_led1_en(true);\n    led_cntr.set_led1_function(LedFunc::TxLevel2P4);\n    // LED0: Green\n    led_cntr.set_led0_en(true);\n    led_cntr.set_led0_function(LedFunc::LinkupTxRxActicity);\n\n    mac.write_cl45(MDIO_PHY_ADDR, RegsC45::DA1E::LED_CNTRL.into(), led_cntr.0)\n        .await\n        .unwrap();\n\n    // Set ADIN1110 Interrupts, RX_READY and LINK_CHANGE\n    // Enable interrupts LINK_CHANGE, TX_RDY, RX_RDY(P1), SPI_ERR\n    // Have to clear the mask the enable it.\n    let mut imask0_val = IMask0(0x0000_1FBF);\n    imask0_val.set_txfcsem(false);\n    imask0_val.set_phyintm(false);\n    imask0_val.set_txboem(false);\n    imask0_val.set_rxboem(false);\n    imask0_val.set_txpem(false);\n\n    mac.write_reg(sr::IMASK0, imask0_val.0).await.unwrap();\n\n    // Set ADIN1110 Interrupts, RX_READY and LINK_CHANGE\n    // Enable interrupts LINK_CHANGE, TX_RDY, RX_RDY(P1), SPI_ERR\n    // Have to clear the mask the enable it.\n    let mut imask1_val = IMask1(0x43FA_1F1A);\n    imask1_val.set_link_change_mask(false);\n    imask1_val.set_p1_rx_rdy_mask(false);\n    imask1_val.set_spi_err_mask(false);\n    imask1_val.set_tx_ecc_err_mask(false);\n    imask1_val.set_rx_ecc_err_mask(false);\n\n    mac.write_reg(sr::IMASK1, imask1_val.0).await.unwrap();\n\n    // Program mac address but also sets mac filters.\n    mac.set_mac_addr(&mac_addr).await.unwrap();\n\n    let (runner, device) = ch::new(&mut state.ch_state, ch::driver::HardwareAddress::Ethernet(mac_addr));\n    (\n        device,\n        Runner {\n            ch: runner,\n            mac,\n            int,\n            is_link_up: false,\n            _reset: reset,\n        },\n    )\n}\n\n#[allow(clippy::similar_names)]\n#[cfg(test)]\nmod tests {\n    use core::convert::Infallible;\n\n    use embedded_hal_1::digital::{ErrorType, OutputPin};\n    use embedded_hal_async::delay::DelayNs;\n    use embedded_hal_bus::spi::ExclusiveDevice;\n    use embedded_hal_mock::common::Generic;\n    use embedded_hal_mock::eh1::spi::{Mock as SpiMock, Transaction as SpiTransaction};\n\n    #[derive(Debug, Default)]\n    struct CsPinMock {\n        pub high: u32,\n        pub low: u32,\n    }\n    impl OutputPin for CsPinMock {\n        fn set_low(&mut self) -> Result<(), Self::Error> {\n            self.low += 1;\n            Ok(())\n        }\n\n        fn set_high(&mut self) -> Result<(), Self::Error> {\n            self.high += 1;\n            Ok(())\n        }\n    }\n    impl ErrorType for CsPinMock {\n        type Error = Infallible;\n    }\n\n    use super::*;\n\n    // TODO: This is currently a workaround unit `ExclusiveDevice` is moved to `embedded-hal-bus`\n    // see https://github.com/rust-embedded/embedded-hal/pull/462#issuecomment-1560014426\n    struct MockDelay {}\n\n    impl DelayNs for MockDelay {\n        async fn delay_ns(&mut self, _ns: u32) {\n            todo!()\n        }\n\n        async fn delay_us(&mut self, _us: u32) {\n            todo!()\n        }\n\n        async fn delay_ms(&mut self, _ms: u32) {\n            todo!()\n        }\n    }\n\n    struct TestHarnass {\n        spe: ADIN1110<ExclusiveDevice<embedded_hal_mock::common::Generic<SpiTransaction<u8>>, CsPinMock, MockDelay>>,\n        spi: Generic<SpiTransaction<u8>>,\n    }\n\n    impl TestHarnass {\n        pub fn new(expectations: &[SpiTransaction<u8>], spi_crc: bool, append_fcs_on_tx: bool) -> Self {\n            let cs = CsPinMock::default();\n            let delay = MockDelay {};\n            let spi = SpiMock::new(expectations);\n            let spi_dev: ExclusiveDevice<embedded_hal_mock::common::Generic<SpiTransaction<u8>>, CsPinMock, MockDelay> =\n                ExclusiveDevice::new(spi.clone(), cs, delay);\n            let spe: ADIN1110<\n                ExclusiveDevice<embedded_hal_mock::common::Generic<SpiTransaction<u8>>, CsPinMock, MockDelay>,\n            > = ADIN1110::new(spi_dev, spi_crc, append_fcs_on_tx);\n\n            Self { spe, spi }\n        }\n\n        pub fn done(&mut self) {\n            self.spi.done();\n        }\n    }\n\n    #[futures_test::test]\n    async fn mac_read_registers_without_crc() {\n        // Configure expectations\n        let expectations = [\n            // 1st\n            SpiTransaction::write_vec(vec![0x80, 0x01, TURN_AROUND_BYTE]),\n            SpiTransaction::read_vec(vec![0x02, 0x83, 0xBC, 0x91]),\n            SpiTransaction::flush(),\n            // 2nd\n            SpiTransaction::write_vec(vec![0x80, 0x02, TURN_AROUND_BYTE]),\n            SpiTransaction::read_vec(vec![0x00, 0x00, 0x06, 0xC3]),\n            SpiTransaction::flush(),\n        ];\n\n        // Create TestHarnass\n        let mut th = TestHarnass::new(&expectations, false, true);\n\n        // Read PHIID\n        let val = th.spe.read_reg(sr::PHYID).await.expect(\"Error\");\n        assert_eq!(val, 0x0283_BC91);\n\n        // Read CAPAVILITY\n        let val = th.spe.read_reg(sr::CAPABILITY).await.expect(\"Error\");\n        assert_eq!(val, 0x0000_06C3);\n\n        // Mark end of the SPI test.\n        th.done();\n    }\n\n    #[futures_test::test]\n    async fn mac_read_registers_with_crc() {\n        // Configure expectations\n        let expectations = [\n            // 1st\n            SpiTransaction::write_vec(vec![0x80, 0x01, 177, TURN_AROUND_BYTE]),\n            SpiTransaction::read_vec(vec![0x02, 0x83, 0xBC, 0x91, 215]),\n            SpiTransaction::flush(),\n            // 2nd\n            SpiTransaction::write_vec(vec![0x80, 0x02, 184, TURN_AROUND_BYTE]),\n            SpiTransaction::read_vec(vec![0x00, 0x00, 0x06, 0xC3, 57]),\n            SpiTransaction::flush(),\n        ];\n\n        // Create TestHarnass\n        let mut th = TestHarnass::new(&expectations, true, true);\n\n        assert_eq!(crc8(0x0283_BC91_u32.to_be_bytes().as_slice()), 215);\n        assert_eq!(crc8(0x0000_06C3_u32.to_be_bytes().as_slice()), 57);\n\n        // Read PHIID\n        let val = th.spe.read_reg(sr::PHYID).await.expect(\"Error\");\n        assert_eq!(val, 0x0283_BC91);\n\n        // Read CAPAVILITY\n        let val = th.spe.read_reg(sr::CAPABILITY).await.expect(\"Error\");\n        assert_eq!(val, 0x0000_06C3);\n\n        // Mark end of the SPI test.\n        th.done();\n    }\n\n    #[futures_test::test]\n    async fn mac_write_registers_without_crc() {\n        // Configure expectations\n        let expectations = [\n            SpiTransaction::write_vec(vec![0xA0, 0x09, 0x12, 0x34, 0x56, 0x78]),\n            SpiTransaction::flush(),\n        ];\n\n        // Create TestHarnass\n        let mut th = TestHarnass::new(&expectations, false, true);\n\n        // Write reg: 0x1FFF\n        assert!(th.spe.write_reg(sr::STATUS1, 0x1234_5678).await.is_ok());\n\n        // Mark end of the SPI test.\n        th.done();\n    }\n\n    #[futures_test::test]\n    async fn mac_write_registers_with_crc() {\n        // Configure expectations\n        let expectations = [\n            SpiTransaction::write_vec(vec![0xA0, 0x09, 39, 0x12, 0x34, 0x56, 0x78, 28]),\n            SpiTransaction::flush(),\n        ];\n\n        // Create TestHarnass\n        let mut th = TestHarnass::new(&expectations, true, true);\n\n        // Write reg: 0x1FFF\n        assert!(th.spe.write_reg(sr::STATUS1, 0x1234_5678).await.is_ok());\n\n        // Mark end of the SPI test.\n        th.done();\n    }\n\n    #[futures_test::test]\n    async fn write_packet_to_fifo_minimal_with_crc() {\n        // Configure expectations\n        let mut expectations = vec![];\n\n        // Write TX_SIZE reg\n        expectations.push(SpiTransaction::write_vec(vec![160, 48, 136, 0, 0, 0, 66, 201]));\n        expectations.push(SpiTransaction::flush());\n\n        // Write TX reg.\n        // SPI Header + optional CRC + Frame Header\n        expectations.push(SpiTransaction::write_vec(vec![160, 49, 143, 0, 0]));\n        // Packet data\n        let packet = [0xFF_u8; 60];\n        expectations.push(SpiTransaction::write_vec(packet.to_vec()));\n\n        let mut tail = std::vec::Vec::<u8>::with_capacity(100);\n        // Padding\n        if let Some(padding_len) = (ETH_MIN_LEN - FCS_LEN).checked_sub(packet.len()) {\n            tail.resize(padding_len, 0x00);\n        }\n        // Packet FCS + optinal padding\n        tail.extend_from_slice(&[77, 241, 140, 244, DONT_CARE_BYTE, DONT_CARE_BYTE]);\n\n        expectations.push(SpiTransaction::write_vec(tail));\n        expectations.push(SpiTransaction::flush());\n\n        // Create TestHarnass\n        let mut th = TestHarnass::new(&expectations, true, true);\n\n        assert!(th.spe.write_fifo(&packet).await.is_ok());\n\n        // Mark end of the SPI test.\n        th.done();\n    }\n\n    #[futures_test::test]\n    async fn write_packet_to_fifo_minimal_with_crc_without_fcs() {\n        // Configure expectations\n        let mut expectations = vec![];\n\n        // Write TX_SIZE reg\n        expectations.push(SpiTransaction::write_vec(vec![160, 48, 136, 0, 0, 0, 62, 186]));\n        expectations.push(SpiTransaction::flush());\n\n        // Write TX reg.\n        // SPI Header + optional CRC + Frame Header\n        expectations.push(SpiTransaction::write_vec(vec![160, 49, 143, 0, 0]));\n        // Packet data\n        let packet = [0xFF_u8; 60];\n        expectations.push(SpiTransaction::write_vec(packet.to_vec()));\n\n        let mut tail = std::vec::Vec::<u8>::with_capacity(100);\n        // Padding\n        if let Some(padding_len) = (ETH_MIN_LEN - FCS_LEN).checked_sub(packet.len()) {\n            tail.resize(padding_len, 0x00);\n        }\n        // Packet FCS + optinal padding\n        tail.extend_from_slice(&[DONT_CARE_BYTE, DONT_CARE_BYTE]);\n\n        expectations.push(SpiTransaction::write_vec(tail));\n        expectations.push(SpiTransaction::flush());\n\n        // Create TestHarnass\n        let mut th = TestHarnass::new(&expectations, true, false);\n\n        assert!(th.spe.write_fifo(&packet).await.is_ok());\n\n        // Mark end of the SPI test.\n        th.done();\n    }\n\n    #[futures_test::test]\n    async fn write_packet_to_fifo_max_mtu_with_crc() {\n        assert_eq!(MTU, 1514);\n        // Configure expectations\n        let mut expectations = vec![];\n\n        // Write TX_SIZE reg\n        expectations.push(SpiTransaction::write_vec(vec![160, 48, 136, 0, 0, 5, 240, 159]));\n        expectations.push(SpiTransaction::flush());\n\n        // Write TX reg.\n        // SPI Header + optional CRC + Frame Header\n        expectations.push(SpiTransaction::write_vec(vec![160, 49, 143, 0, 0]));\n        // Packet data\n        let packet = [0xAA_u8; MTU];\n        expectations.push(SpiTransaction::write_vec(packet.to_vec()));\n\n        let mut tail = std::vec::Vec::<u8>::with_capacity(100);\n        // Padding\n        if let Some(padding_len) = (ETH_MIN_LEN - FCS_LEN).checked_sub(packet.len()) {\n            tail.resize(padding_len, 0x00);\n        }\n        // Packet FCS + optinal padding\n        tail.extend_from_slice(&[49, 196, 205, 160]);\n\n        expectations.push(SpiTransaction::write_vec(tail));\n        expectations.push(SpiTransaction::flush());\n\n        // Create TestHarnass\n        let mut th = TestHarnass::new(&expectations, true, true);\n\n        assert!(th.spe.write_fifo(&packet).await.is_ok());\n\n        // Mark end of the SPI test.\n        th.done();\n    }\n\n    #[futures_test::test]\n    async fn write_packet_to_fifo_invalid_lengths() {\n        assert_eq!(MTU, 1514);\n\n        // Configure expectations\n        let expectations = vec![];\n\n        // Max packet size = MAX_BUFF - FRAME_HEADER_LEN\n        let packet = [0xAA_u8; MAX_BUFF - FRAME_HEADER_LEN + 1];\n\n        // Create TestHarnass\n        let mut th = TestHarnass::new(&expectations, true, true);\n\n        // minimal\n        assert!(matches!(\n            th.spe.write_fifo(&packet[0..(6 + 6 + 2 - 1)]).await,\n            Err(AdinError::PACKET_TOO_SMALL)\n        ));\n\n        // max + 1\n        assert!(matches!(\n            th.spe.write_fifo(&packet).await,\n            Err(AdinError::PACKET_TOO_BIG)\n        ));\n\n        // Mark end of the SPI test.\n        th.done();\n    }\n\n    #[futures_test::test]\n    async fn write_packet_to_fifo_arp_46bytes_with_crc() {\n        // Configure expectations\n        let mut expectations = vec![];\n\n        // Write TX_SIZE reg\n        expectations.push(SpiTransaction::write_vec(vec![160, 48, 136, 0, 0, 0, 66, 201]));\n        expectations.push(SpiTransaction::flush());\n\n        // Write TX reg.\n        // Header\n        expectations.push(SpiTransaction::write_vec(vec![160, 49, 143, 0, 0]));\n        // Packet data\n        let packet = [\n            34, 51, 68, 85, 102, 119, 18, 52, 86, 120, 154, 188, 8, 6, 0, 1, 8, 0, 6, 4, 0, 2, 18, 52, 86, 120, 154,\n            188, 192, 168, 16, 4, 34, 51, 68, 85, 102, 119, 192, 168, 16, 1,\n        ];\n        expectations.push(SpiTransaction::write_vec(packet.to_vec()));\n\n        let mut tail = std::vec::Vec::<u8>::with_capacity(100);\n        // Padding\n        if let Some(padding_len) = (ETH_MIN_LEN - FCS_LEN).checked_sub(packet.len()) {\n            tail.resize(padding_len, 0x00);\n        }\n        // Packet FCS + optinal padding\n        tail.extend_from_slice(&[147, 149, 213, 68, DONT_CARE_BYTE, DONT_CARE_BYTE]);\n\n        expectations.push(SpiTransaction::write_vec(tail));\n        expectations.push(SpiTransaction::flush());\n\n        // Create TestHarnass\n        let mut th = TestHarnass::new(&expectations, true, true);\n\n        assert!(th.spe.write_fifo(&packet).await.is_ok());\n\n        // Mark end of the SPI test.\n        th.done();\n    }\n\n    #[futures_test::test]\n    async fn write_packet_to_fifo_arp_46bytes_without_crc() {\n        // Configure expectations\n        let mut expectations = vec![];\n\n        // Write TX_SIZE reg\n        expectations.push(SpiTransaction::write_vec(vec![160, 48, 0, 0, 0, 66]));\n        expectations.push(SpiTransaction::flush());\n\n        // Write TX reg.\n        // SPI Header + Frame Header\n        expectations.push(SpiTransaction::write_vec(vec![160, 49, 0, 0]));\n        // Packet data\n        let packet = [\n            34, 51, 68, 85, 102, 119, 18, 52, 86, 120, 154, 188, 8, 6, 0, 1, 8, 0, 6, 4, 0, 2, 18, 52, 86, 120, 154,\n            188, 192, 168, 16, 4, 34, 51, 68, 85, 102, 119, 192, 168, 16, 1,\n        ];\n        expectations.push(SpiTransaction::write_vec(packet.to_vec()));\n\n        let mut tail = std::vec::Vec::<u8>::with_capacity(100);\n        // Padding\n        if let Some(padding_len) = (ETH_MIN_LEN - FCS_LEN).checked_sub(packet.len()) {\n            tail.resize(padding_len, 0x00);\n        }\n        // Packet FCS + optinal padding\n        tail.extend_from_slice(&[147, 149, 213, 68, DONT_CARE_BYTE, DONT_CARE_BYTE]);\n\n        expectations.push(SpiTransaction::write_vec(tail));\n        expectations.push(SpiTransaction::flush());\n\n        // Create TestHarnass\n        let mut th = TestHarnass::new(&expectations, false, true);\n\n        assert!(th.spe.write_fifo(&packet).await.is_ok());\n\n        // Mark end of the SPI test.\n        th.done();\n    }\n\n    #[futures_test::test]\n    async fn read_packet_from_fifo_packet_too_big_for_frame_buffer() {\n        // Configure expectations\n        let mut expectations = vec![];\n\n        // Read RX_SIZE reg\n        let rx_size: u32 = u32::try_from(ETH_MIN_LEN + FRAME_HEADER_LEN + FCS_LEN).unwrap();\n        let mut rx_size_vec = rx_size.to_be_bytes().to_vec();\n        rx_size_vec.push(crc8(&rx_size_vec));\n\n        expectations.push(SpiTransaction::write_vec(vec![128, 144, 79, TURN_AROUND_BYTE]));\n        expectations.push(SpiTransaction::read_vec(rx_size_vec));\n        expectations.push(SpiTransaction::flush());\n\n        let mut frame = [0; MTU];\n\n        // Create TestHarnass\n        let mut th = TestHarnass::new(&expectations, true, true);\n\n        let ret = th.spe.read_fifo(&mut frame[0..ETH_MIN_LEN - 1]).await;\n        assert!(matches!(dbg!(ret), Err(AdinError::PACKET_TOO_BIG)));\n\n        // Mark end of the SPI test.\n        th.done();\n    }\n\n    #[futures_test::test]\n    async fn read_packet_from_fifo_packet_too_small() {\n        // Configure expectations\n        let mut expectations = vec![];\n\n        // This value is importen for this test!\n        assert_eq!(ETH_MIN_LEN, 64);\n\n        // Packet data, size = `ETH_MIN_LEN` - `FCS_LEN` - 1\n        let packet = [0; 64 - FCS_LEN - 1];\n\n        // Read RX_SIZE reg\n        let rx_size: u32 = u32::try_from(packet.len() + FRAME_HEADER_LEN + FCS_LEN).unwrap();\n        let mut rx_size_vec = rx_size.to_be_bytes().to_vec();\n        rx_size_vec.push(crc8(&rx_size_vec));\n\n        expectations.push(SpiTransaction::write_vec(vec![128, 144, 79, TURN_AROUND_BYTE]));\n        expectations.push(SpiTransaction::read_vec(rx_size_vec));\n        expectations.push(SpiTransaction::flush());\n\n        let mut frame = [0; MTU];\n\n        // Create TestHarnass\n        let mut th = TestHarnass::new(&expectations, true, true);\n\n        let ret = th.spe.read_fifo(&mut frame).await;\n        assert!(matches!(dbg!(ret), Err(AdinError::PACKET_TOO_SMALL)));\n\n        // Mark end of the SPI test.\n        th.done();\n    }\n\n    #[futures_test::test]\n    async fn read_packet_from_fifo_packet_corrupted_fcs() {\n        let mut frame = [0; MTU];\n        // Configure expectations\n        let mut expectations = vec![];\n\n        let packet = [0xDE; 60];\n        let crc_en = true;\n\n        // Read RX_SIZE reg\n        let rx_size: u32 = u32::try_from(packet.len() + FRAME_HEADER_LEN + FCS_LEN).unwrap();\n        let mut rx_size_vec = rx_size.to_be_bytes().to_vec();\n        if crc_en {\n            rx_size_vec.push(crc8(&rx_size_vec));\n        }\n\n        // SPI Header with CRC\n        let mut rx_fsize = vec![128, 144, 79, TURN_AROUND_BYTE];\n        if !crc_en {\n            // remove the CRC on idx 2\n            rx_fsize.swap_remove(2);\n        }\n        expectations.push(SpiTransaction::write_vec(rx_fsize));\n        expectations.push(SpiTransaction::read_vec(rx_size_vec));\n        expectations.push(SpiTransaction::flush());\n\n        // Read RX reg, SPI Header with CRC\n        let mut rx_reg = vec![128, 145, 72, TURN_AROUND_BYTE];\n        if !crc_en {\n            // remove the CRC on idx 2\n            rx_reg.swap_remove(2);\n        }\n        expectations.push(SpiTransaction::write_vec(rx_reg));\n        // Frame Header\n        expectations.push(SpiTransaction::read_vec(vec![0, 0]));\n        // Packet data\n        expectations.push(SpiTransaction::read_vec(packet.to_vec()));\n\n        let packet_crc = ETH_FCS::new(&packet);\n\n        let mut tail = std::vec::Vec::<u8>::with_capacity(100);\n\n        tail.extend_from_slice(&packet_crc.hton_bytes());\n        // increase last byte with 1.\n        if let Some(crc) = tail.last_mut() {\n            *crc = crc.wrapping_add(1);\n        }\n\n        // Need extra bytes?\n        let pad = (packet.len() + FCS_LEN + FRAME_HEADER_LEN) & 0x03;\n        if pad != 0 {\n            // Packet FCS + optinal padding\n            tail.resize(tail.len() + pad, DONT_CARE_BYTE);\n        }\n\n        expectations.push(SpiTransaction::read_vec(tail));\n        expectations.push(SpiTransaction::flush());\n\n        // Create TestHarnass\n        let mut th = TestHarnass::new(&expectations, crc_en, false);\n\n        let ret = th.spe.read_fifo(&mut frame).await.expect_err(\"Error!\");\n        assert!(matches!(ret, AdinError::FCS));\n\n        // Mark end of the SPI test.\n        th.done();\n    }\n\n    #[futures_test::test]\n    async fn read_packet_to_fifo_check_spi_read_multipule_of_u32_valid_lengths() {\n        let packet_buffer = [0; MTU];\n        let mut frame = [0; MTU];\n        let mut expectations = std::vec::Vec::with_capacity(16);\n\n        // Packet data, size = `ETH_MIN_LEN` - `FCS_LEN`\n        for packet_size in [60, 61, 62, 63, 64, MTU - 4, MTU - 3, MTU - 2, MTU - 1, MTU] {\n            for crc_en in [false, true] {\n                expectations.clear();\n\n                let packet = &packet_buffer[0..packet_size];\n\n                // Read RX_SIZE reg\n                let rx_size: u32 = u32::try_from(packet.len() + FRAME_HEADER_LEN + FCS_LEN).unwrap();\n                let mut rx_size_vec = rx_size.to_be_bytes().to_vec();\n                if crc_en {\n                    rx_size_vec.push(crc8(&rx_size_vec));\n                }\n\n                // SPI Header with CRC\n                let mut rx_fsize = vec![128, 144, 79, TURN_AROUND_BYTE];\n                if !crc_en {\n                    // remove the CRC on idx 2\n                    rx_fsize.swap_remove(2);\n                }\n                expectations.push(SpiTransaction::write_vec(rx_fsize));\n                expectations.push(SpiTransaction::read_vec(rx_size_vec));\n                expectations.push(SpiTransaction::flush());\n\n                // Read RX reg, SPI Header with CRC\n                let mut rx_reg = vec![128, 145, 72, TURN_AROUND_BYTE];\n                if !crc_en {\n                    // remove the CRC on idx 2\n                    rx_reg.swap_remove(2);\n                }\n                expectations.push(SpiTransaction::write_vec(rx_reg));\n                // Frame Header\n                expectations.push(SpiTransaction::read_vec(vec![0, 0]));\n                // Packet data\n                expectations.push(SpiTransaction::read_vec(packet.to_vec()));\n\n                let packet_crc = ETH_FCS::new(packet);\n\n                let mut tail = std::vec::Vec::<u8>::with_capacity(100);\n\n                tail.extend_from_slice(&packet_crc.hton_bytes());\n\n                // Need extra bytes?\n                let pad = (packet_size + FCS_LEN + FRAME_HEADER_LEN) & 0x03;\n                if pad != 0 {\n                    // Packet FCS + optinal padding\n                    tail.resize(tail.len() + pad, DONT_CARE_BYTE);\n                }\n\n                expectations.push(SpiTransaction::read_vec(tail));\n                expectations.push(SpiTransaction::flush());\n\n                // Create TestHarnass\n                let mut th = TestHarnass::new(&expectations, crc_en, false);\n\n                let ret = th.spe.read_fifo(&mut frame).await.expect(\"Error!\");\n                assert_eq!(ret, packet_size);\n\n                // Mark end of the SPI test.\n                th.done();\n            }\n        }\n    }\n\n    #[futures_test::test]\n    async fn spi_crc_error() {\n        // Configure expectations\n        let expectations = vec![\n            SpiTransaction::write_vec(vec![128, 144, 79, TURN_AROUND_BYTE]),\n            SpiTransaction::read_vec(vec![0x00, 0x00, 0x00, 0x00, 0xDD]),\n            SpiTransaction::flush(),\n        ];\n\n        // Create TestHarnass\n        let mut th = TestHarnass::new(&expectations, true, false);\n\n        let ret = th.spe.read_reg(sr::RX_FSIZE).await;\n        assert!(matches!(dbg!(ret), Err(AdinError::SPI_CRC)));\n\n        // Mark end of the SPI test.\n        th.done();\n    }\n}\n"
  },
  {
    "path": "embassy-net-adin1110/src/mdio.rs",
    "content": "/// PHY Address: (0..=0x1F), 5-bits long.\n#[allow(dead_code)]\ntype PhyAddr = u8;\n\n/// PHY Register: (0..=0x1F), 5-bits long.\n#[allow(dead_code)]\ntype RegC22 = u8;\n\n/// PHY Register Clause 45.\n#[allow(dead_code)]\ntype RegC45 = u16;\n\n/// PHY Register Value\n#[allow(dead_code)]\ntype RegVal = u16;\n\n#[allow(dead_code)]\nconst REG13: RegC22 = 13;\n#[allow(dead_code)]\nconst REG14: RegC22 = 14;\n\n#[allow(dead_code)]\nconst PHYADDR_MASK: u8 = 0x1f;\n#[allow(dead_code)]\nconst DEV_MASK: u8 = 0x1f;\n\n#[allow(dead_code)]\n#[repr(u16)]\nenum Reg13Op {\n    Addr = 0b00 << 14,\n    Write = 0b01 << 14,\n    PostReadIncAddr = 0b10 << 14,\n    Read = 0b11 << 14,\n}\n\n/// `MdioBus` trait\n/// Driver needs to implement the Clause 22\n/// Optional Clause 45 is the device supports this.\n///\n/// Clause 45 methodes are bases on <https://www.ieee802.org/3/efm/public/nov02/oam/pannell_oam_1_1102.pdf>\npub trait MdioBus {\n    /// Error type.\n    type Error;\n\n    /// Read, Clause 22\n    async fn read_cl22(&mut self, phy_id: PhyAddr, reg: RegC22) -> Result<RegVal, Self::Error>;\n\n    /// Write, Clause 22\n    async fn write_cl22(&mut self, phy_id: PhyAddr, reg: RegC22, reg_val: RegVal) -> Result<(), Self::Error>;\n\n    /// Read, Clause 45\n    /// This is the default implementation.\n    /// Many hardware these days support direct Clause 45 operations.\n    /// Implement this function when your hardware supports it.\n    async fn read_cl45(&mut self, phy_id: PhyAddr, regc45: (u8, RegC45)) -> Result<RegVal, Self::Error> {\n        // Write FN\n        let val = (Reg13Op::Addr as RegVal) | RegVal::from(regc45.0 & DEV_MASK);\n\n        self.write_cl22(phy_id, REG13, val).await?;\n        // Write Addr\n        self.write_cl22(phy_id, REG14, regc45.1).await?;\n\n        // Write FN\n        let val = (Reg13Op::Read as RegVal) | RegVal::from(regc45.0 & DEV_MASK);\n        self.write_cl22(phy_id, REG13, val).await?;\n        // Write Addr\n        self.read_cl22(phy_id, REG14).await\n    }\n\n    /// Write, Clause 45\n    /// This is the default implementation.\n    /// Many hardware these days support direct Clause 45 operations.\n    /// Implement this function when your hardware supports it.\n    async fn write_cl45(&mut self, phy_id: PhyAddr, regc45: (u8, RegC45), reg_val: RegVal) -> Result<(), Self::Error> {\n        let dev_addr = RegVal::from(regc45.0 & DEV_MASK);\n        let reg = regc45.1;\n\n        // Write FN\n        let val = (Reg13Op::Addr as RegVal) | dev_addr;\n        self.write_cl22(phy_id, REG13, val).await?;\n        // Write Addr\n        self.write_cl22(phy_id, REG14, reg).await?;\n\n        // Write FN\n        let val = (Reg13Op::Write as RegVal) | dev_addr;\n        self.write_cl22(phy_id, REG13, val).await?;\n        // Write Addr\n        self.write_cl22(phy_id, REG14, reg_val).await\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use core::convert::Infallible;\n\n    use super::{MdioBus, PhyAddr, RegC22, RegVal};\n\n    #[derive(Debug, PartialEq, Eq)]\n    enum A {\n        Read(PhyAddr, RegC22),\n        Write(PhyAddr, RegC22, RegVal),\n    }\n\n    struct MockMdioBus(Vec<A>);\n\n    impl MockMdioBus {\n        pub fn clear(&mut self) {\n            self.0.clear();\n        }\n    }\n\n    impl MdioBus for MockMdioBus {\n        type Error = Infallible;\n\n        async fn write_cl22(\n            &mut self,\n            phy_id: super::PhyAddr,\n            reg: super::RegC22,\n            reg_val: super::RegVal,\n        ) -> Result<(), Self::Error> {\n            self.0.push(A::Write(phy_id, reg, reg_val));\n            Ok(())\n        }\n\n        async fn read_cl22(\n            &mut self,\n            phy_id: super::PhyAddr,\n            reg: super::RegC22,\n        ) -> Result<super::RegVal, Self::Error> {\n            self.0.push(A::Read(phy_id, reg));\n            Ok(0)\n        }\n    }\n\n    #[futures_test::test]\n    async fn read_test() {\n        let mut mdiobus = MockMdioBus(Vec::with_capacity(20));\n\n        mdiobus.clear();\n        mdiobus.read_cl22(0x01, 0x00).await.unwrap();\n        assert_eq!(mdiobus.0, vec![A::Read(0x01, 0x00)]);\n\n        mdiobus.clear();\n        mdiobus.read_cl45(0x01, (0xBB, 0x1234)).await.unwrap();\n        assert_eq!(\n            mdiobus.0,\n            vec![\n                #[allow(clippy::identity_op)]\n                A::Write(0x01, 13, (0b00 << 14) | 27),\n                A::Write(0x01, 14, 0x1234),\n                A::Write(0x01, 13, (0b11 << 14) | 27),\n                A::Read(0x01, 14)\n            ]\n        );\n    }\n\n    #[futures_test::test]\n    async fn write_test() {\n        let mut mdiobus = MockMdioBus(Vec::with_capacity(20));\n\n        mdiobus.clear();\n        mdiobus.write_cl22(0x01, 0x00, 0xABCD).await.unwrap();\n        assert_eq!(mdiobus.0, vec![A::Write(0x01, 0x00, 0xABCD)]);\n\n        mdiobus.clear();\n        mdiobus.write_cl45(0x01, (0xBB, 0x1234), 0xABCD).await.unwrap();\n        assert_eq!(\n            mdiobus.0,\n            vec![\n                A::Write(0x01, 13, 27),\n                A::Write(0x01, 14, 0x1234),\n                A::Write(0x01, 13, (0b01 << 14) | 27),\n                A::Write(0x01, 14, 0xABCD)\n            ]\n        );\n    }\n}\n"
  },
  {
    "path": "embassy-net-adin1110/src/phy.rs",
    "content": "use crate::mdio::MdioBus;\n\n#[allow(dead_code, non_camel_case_types, clippy::upper_case_acronyms)]\n#[repr(u8)]\n/// Clause 22 Registers\npub enum RegsC22 {\n    /// MII Control Register\n    CONTROL = 0x00,\n    /// MII Status Register\n    STATUS = 0x01,\n    /// PHY Identifier 1 Register\n    PHY_ID1 = 0x02,\n    /// PHY Identifier 2 Register.\n    PHY_ID2 = 0x03,\n}\n\n/// Clause 45 Registers\n#[allow(non_snake_case, dead_code)]\npub mod RegsC45 {\n    /// Device Address: 0x01\n    #[allow(non_camel_case_types, clippy::upper_case_acronyms)]\n    #[repr(u16)]\n    pub enum DA1 {\n        /// PMA/PMD Control 1 Register\n        PMA_PMD_CNTRL1 = 0x0000,\n        /// PMA/PMD Status 1 Register\n        PMA_PMD_STAT1 = 0x0001,\n        /// MSE Value Register\n        MSE_VAL = 0x830B,\n    }\n\n    impl DA1 {\n        #[must_use]\n        pub fn into(self) -> (u8, u16) {\n            (0x01, self as u16)\n        }\n    }\n\n    /// Device Address: 0x03\n    #[allow(non_camel_case_types, clippy::upper_case_acronyms)]\n    #[repr(u16)]\n    pub enum DA3 {\n        /// PCS Control 1 Register\n        PCS_CNTRL1 = 0x0000,\n        /// PCS Status 1 Register\n        PCS_STAT1 = 0x0001,\n        /// PCS Status 2 Register\n        PCS_STAT2 = 0x0008,\n    }\n\n    impl DA3 {\n        #[must_use]\n        pub fn into(self) -> (u8, u16) {\n            (0x03, self as u16)\n        }\n    }\n\n    /// Device Address: 0x07\n    #[allow(non_camel_case_types, clippy::upper_case_acronyms)]\n    #[repr(u16)]\n    pub enum DA7 {\n        /// Extra Autonegotiation Status Register\n        AN_STATUS_EXTRA = 0x8001,\n    }\n\n    impl DA7 {\n        #[must_use]\n        pub fn into(self) -> (u8, u16) {\n            (0x07, self as u16)\n        }\n    }\n\n    /// Device Address: 0x1E\n    #[allow(non_camel_case_types, clippy::upper_case_acronyms)]\n    #[repr(u16)]\n    pub enum DA1E {\n        /// System Interrupt Status Register\n        CRSM_IRQ_STATUS = 0x0010,\n        /// System Interrupt Mask Register\n        CRSM_IRQ_MASK = 0x0020,\n        /// Pin Mux Configuration 1 Register\n        DIGIO_PINMUX = 0x8c56,\n        /// LED Control Register.\n        LED_CNTRL = 0x8C82,\n        /// LED Polarity Register\n        LED_POLARITY = 0x8C83,\n    }\n\n    impl DA1E {\n        #[must_use]\n        pub fn into(self) -> (u8, u16) {\n            (0x1e, self as u16)\n        }\n    }\n\n    /// Device Address: 0x1F\n    #[allow(non_camel_case_types, clippy::upper_case_acronyms)]\n    #[repr(u16)]\n    pub enum DA1F {\n        /// PHY Subsystem Interrupt Status Register\n        PHY_SYBSYS_IRQ_STATUS = 0x0011,\n        /// PHY Subsystem Interrupt Mask Register\n        PHY_SYBSYS_IRQ_MASK = 0x0021,\n    }\n\n    impl DA1F {\n        #[must_use]\n        pub fn into(self) -> (u8, u16) {\n            (0x1f, self as u16)\n        }\n    }\n}\n\n/// 10-BASE-T1x PHY functions.\npub struct Phy10BaseT1x(u8);\n\nimpl Default for Phy10BaseT1x {\n    fn default() -> Self {\n        Self(0x01)\n    }\n}\n\nimpl Phy10BaseT1x {\n    /// Get the both parts of the PHYID.\n    pub async fn get_id<MDIOBUS, MDE>(&self, mdiobus: &mut MDIOBUS) -> Result<u32, MDE>\n    where\n        MDIOBUS: MdioBus<Error = MDE>,\n        MDE: core::fmt::Debug,\n    {\n        let mut phyid = u32::from(mdiobus.read_cl22(self.0, RegsC22::PHY_ID1 as u8).await?) << 16;\n        phyid |= u32::from(mdiobus.read_cl22(self.0, RegsC22::PHY_ID2 as u8).await?);\n        Ok(phyid)\n    }\n\n    /// Get the Mean Squared Error Value.\n    pub async fn get_sqi<MDIOBUS, MDE>(&self, mdiobus: &mut MDIOBUS) -> Result<u16, MDE>\n    where\n        MDIOBUS: MdioBus<Error = MDE>,\n        MDE: core::fmt::Debug,\n    {\n        mdiobus.read_cl45(self.0, RegsC45::DA1::MSE_VAL.into()).await\n    }\n}\n"
  },
  {
    "path": "embassy-net-adin1110/src/regs.rs",
    "content": "use core::fmt::{Debug, Display};\n\nuse bitfield::{bitfield, bitfield_bitrange, bitfield_fields};\n\n#[allow(missing_docs)]\n#[allow(non_camel_case_types)]\n#[derive(Debug, Copy, Clone)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\n#[repr(u16)]\n/// SPI REGISTER DETAILS\n/// Table 38.\npub enum SpiRegisters {\n    IDVER = 0x00,\n    PHYID = 0x01,\n    CAPABILITY = 0x02,\n    RESET = 0x03,\n    CONFIG0 = 0x04,\n    CONFIG2 = 0x06,\n    STATUS0 = 0x08,\n    STATUS1 = 0x09,\n    IMASK0 = 0x0C,\n    IMASK1 = 0x0D,\n    MDIO_ACC = 0x20,\n    TX_FSIZE = 0x30,\n    TX = 0x31,\n    TX_SPACE = 0x32,\n    FIFO_CLR = 0x36,\n    ADDR_FILT_UPR0 = 0x50,\n    ADDR_FILT_LWR0 = 0x51,\n    ADDR_FILT_UPR1 = 0x52,\n    ADDR_FILT_LWR1 = 0x53,\n    ADDR_MSK_LWR0 = 0x70,\n    ADDR_MSK_UPR0 = 0x71,\n    ADDR_MSK_LWR1 = 0x72,\n    ADDR_MSK_UPR1 = 0x73,\n    RX_FSIZE = 0x90,\n    RX = 0x91,\n}\n\nimpl Display for SpiRegisters {\n    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {\n        write!(f, \"{self:?}\")\n    }\n}\n\nimpl From<SpiRegisters> for u16 {\n    fn from(val: SpiRegisters) -> Self {\n        val as u16\n    }\n}\n\nimpl From<u16> for SpiRegisters {\n    fn from(value: u16) -> Self {\n        match value {\n            0x00 => Self::IDVER,\n            0x01 => Self::PHYID,\n            0x02 => Self::CAPABILITY,\n            0x03 => Self::RESET,\n            0x04 => Self::CONFIG0,\n            0x06 => Self::CONFIG2,\n            0x08 => Self::STATUS0,\n            0x09 => Self::STATUS1,\n            0x0C => Self::IMASK0,\n            0x0D => Self::IMASK1,\n            0x20 => Self::MDIO_ACC,\n            0x30 => Self::TX_FSIZE,\n            0x31 => Self::TX,\n            0x32 => Self::TX_SPACE,\n            0x36 => Self::FIFO_CLR,\n            0x50 => Self::ADDR_FILT_UPR0,\n            0x51 => Self::ADDR_FILT_LWR0,\n            0x52 => Self::ADDR_FILT_UPR1,\n            0x53 => Self::ADDR_FILT_LWR1,\n            0x70 => Self::ADDR_MSK_LWR0,\n            0x71 => Self::ADDR_MSK_UPR0,\n            0x72 => Self::ADDR_MSK_LWR1,\n            0x73 => Self::ADDR_MSK_UPR1,\n            0x90 => Self::RX_FSIZE,\n            0x91 => Self::RX,\n            e => panic!(\"Unknown value {}\", e),\n        }\n    }\n}\n\n// Register definitions\nbitfield! {\n    /// Status0 Register bits\n    pub struct Status0(u32);\n    impl Debug;\n    u32;\n    /// Control Data Protection Error\n    pub cdpe, _ : 12;\n    /// Transmit Frame Check Squence Error\n    pub txfcse, _: 11;\n    /// Transmit Time Stamp Capture Available C\n    pub ttscac, _ : 10;\n    /// Transmit Time Stamp Capture Available B\n    pub ttscab, _ : 9;\n    /// Transmit Time Stamp Capture Available A\n    pub ttscaa, _ : 8;\n    /// PHY Interrupt for Port 1\n    pub phyint, _ : 7;\n    /// Reset Complete\n    pub resetc, _ : 6;\n    /// Header error\n    pub hdre, _ : 5;\n    /// Loss of Frame Error\n    pub lofe, _ : 4;\n    /// Receiver Buffer Overflow Error\n    pub rxboe, _ : 3;\n    /// Host Tx FIFO Under Run Error\n    pub txbue, _ : 2;\n    /// Host Tx FIFO Overflow\n    pub txboe, _ : 1;\n    /// Transmit Protocol Error\n    pub txpe, _ : 0;\n}\n\nbitfield! {\n    /// Status1 Register bits\n    pub struct Status1(u32);\n    impl Debug;\n    u32;\n    /// ECC Error on Reading the Frame Size from a Tx FIFO\n    pub tx_ecc_err, set_tx_ecc_err: 12;\n    /// ECC Error on Reading the Frame Size from an Rx FIFO\n    pub rx_ecc_err, set_rx_ecc_err : 11;\n    /// Detected an Error on an SPI Transaction\n    pub spi_err, set_spi_err: 10;\n    /// Rx MAC Interframe Gap Error\n    pub p1_rx_ifg_err, set_p1_rx_ifg_err : 8;\n    /// Port1 Rx Ready High Priority\n    pub p1_rx_rdy_hi, set_p1_rx_rdy_hi : 5;\n    /// Port 1 Rx FIFO Contains Data\n    pub p1_rx_rdy, set_p1_rx_rdy : 4;\n    /// Tx Ready\n    pub tx_rdy, set_tx_rdy : 3;\n    /// Link Status Changed\n    pub link_change, set_link_change : 1;\n    /// Port 1 Link Status\n    pub p1_link_status, _ : 0;\n}\n\nbitfield! {\n    /// Config0 Register bits\n    pub struct Config0(u32);\n    impl Debug;\n    u32;\n    /// Configuration Synchronization\n    pub sync, set_sync : 15;\n    /// Transmit Frame Check Sequence Validation Enable\n    pub txfcsve, set_txfcsve : 14;\n    /// !CS Align Receive Frame Enable\n    pub csarfe, set_csarfe : 13;\n    /// Zero Align Receive Frame Enable\n    pub zarfe, set_zarfe : 12;\n    /// Transmit Credit Threshold\n    pub tcxthresh, set_tcxthresh : 11, 10;\n    /// Transmit Cut Through Enable\n    pub txcte, set_txcte : 9;\n    /// Receive Cut Through Enable\n    pub rxcte, set_rxcte : 8;\n    /// Frame Time Stamp Enable\n    pub ftse, set_ftse : 7;\n    /// Receive Frame Time Stamp Select\n    pub ftss, set_ftss : 6;\n    /// Enable Control Data Read Write Protection\n    pub prote, set_prote : 5;\n    /// Enable TX Data Chunk Sequence and Retry\n    pub seqe, set_seqe : 4;\n    /// Chunk Payload Selector (N).\n    pub cps, set_cps : 2, 0;\n}\n\nbitfield! {\n    /// Config2 Register bits\n    pub struct Config2(u32);\n    impl Debug;\n    u32;\n    /// Assert TX_RDY When the Tx FIFO is Empty\n    pub tx_rdy_on_empty, set_tx_rdy_on_empty : 8;\n    /// Determines If the SFD is Detected in the PHY or MAC\n    pub sdf_detect_src, set_sdf_detect_src : 7;\n    /// Statistics Clear on Reading\n    pub stats_clr_on_rd, set_stats_clr_on_rd : 6;\n    /// Enable SPI CRC\n    pub crc_append, set_crc_append : 5;\n    /// Admit Frames with IFG Errors on Port 1 (P1)\n    pub p1_rcv_ifg_err_frm, set_p1_rcv_ifg_err_frm : 4;\n    /// Forward Frames Not Matching Any MAC Address to the Host\n    pub p1_fwd_unk2host, set_p1_fwd_unk2host : 2;\n    /// SPI to MDIO Bridge MDC Clock Speed\n    pub mspeed, set_mspeed : 0;\n}\n\nbitfield! {\n    /// IMASK0 Register bits\n    pub struct IMask0(u32);\n    impl Debug;\n    u32;\n    /// Control Data Protection Error Mask\n    pub cppem, set_cppem : 12;\n    /// Transmit Frame Check Sequence Error Mask\n    pub txfcsem, set_txfcsem : 11;\n    /// Transmit Time Stamp Capture Available C Mask\n    pub ttscacm, set_ttscacm : 10;\n    /// Transmit Time Stamp Capture Available B Mask\n    pub ttscabm, set_ttscabm : 9;\n    /// Transmit Time Stamp Capture Available A Mask\n    pub ttscaam, set_ttscaam : 8;\n    /// Physical Layer Interrupt Mask\n    pub phyintm, set_phyintm : 7;\n    /// RESET Complete Mask\n    pub resetcm, set_resetcm : 6;\n    /// Header Error Mask\n    pub hdrem, set_hdrem : 5;\n    /// Loss of Frame Error Mask\n    pub lofem, set_lofem : 4;\n    /// Receive Buffer Overflow Error Mask\n    pub rxboem, set_rxboem : 3;\n    /// Transmit Buffer Underflow Error Mask\n    pub txbuem, set_txbuem : 2;\n    /// Transmit Buffer Overflow Error Mask\n    pub txboem, set_txboem : 1;\n    /// Transmit Protocol Error Mask\n    pub txpem, set_txpem : 0;\n}\n\nbitfield! {\n    /// IMASK1 Register bits\n    pub struct IMask1(u32);\n    impl Debug;\n    u32;\n    /// Mask Bit for TXF_ECC_ERR\n    pub tx_ecc_err_mask, set_tx_ecc_err_mask : 12;\n    /// Mask Bit for RXF_ECC_ERR\n    pub rx_ecc_err_mask, set_rx_ecc_err_mask : 11;\n    /// Mask Bit for SPI_ERR\n    /// This field is only used with the generic SPI protocol\n    pub spi_err_mask, set_spi_err_mask : 10;\n    /// Mask Bit for RX_IFG_ERR\n    pub p1_rx_ifg_err_mask, set_p1_rx_ifg_err_mask : 8;\n    /// Mask Bit for P1_RX_RDY\n    /// This field is only used with the generic SPI protocol\n    pub p1_rx_rdy_mask, set_p1_rx_rdy_mask : 4;\n    /// Mask Bit for TX_FRM_DONE\n    /// This field is only used with the generic SPI protocol\n    pub tx_rdy_mask, set_tx_rdy_mask : 3;\n    /// Mask Bit for LINK_CHANGE\n    pub link_change_mask, set_link_change_mask : 1;\n}\n\n/// LED Functions\n#[repr(u8)]\npub enum LedFunc {\n    LinkupTxRxActicity = 0,\n    LinkupTxActicity,\n    LinkupRxActicity,\n    LinkupOnly,\n    TxRxActivity,\n    TxActivity,\n    RxActivity,\n    LinkupRxEr,\n    LinkupRxTxEr,\n    RxEr,\n    RxTxEr,\n    TxSop,\n    RxSop,\n    On,\n    Off,\n    Blink,\n    TxLevel2P4,\n    TxLevel1P0,\n    Master,\n    Slave,\n    IncompatiableLinkCfg,\n    AnLinkGood,\n    AnComplete,\n    TsTimer,\n    LocRcvrStatus,\n    RemRcvrStatus,\n    Clk25Ref,\n    TxTCLK,\n    Clk120MHz,\n}\n\nimpl From<LedFunc> for u8 {\n    fn from(val: LedFunc) -> Self {\n        val as u8\n    }\n}\n\nimpl From<u8> for LedFunc {\n    fn from(value: u8) -> Self {\n        match value {\n            0 => LedFunc::LinkupTxRxActicity,\n            1 => LedFunc::LinkupTxActicity,\n            2 => LedFunc::LinkupRxActicity,\n            3 => LedFunc::LinkupOnly,\n            4 => LedFunc::TxRxActivity,\n            5 => LedFunc::TxActivity,\n            6 => LedFunc::RxActivity,\n            7 => LedFunc::LinkupRxEr,\n            8 => LedFunc::LinkupRxTxEr,\n            9 => LedFunc::RxEr,\n            10 => LedFunc::RxTxEr,\n            11 => LedFunc::TxSop,\n            12 => LedFunc::RxSop,\n            13 => LedFunc::On,\n            14 => LedFunc::Off,\n            15 => LedFunc::Blink,\n            16 => LedFunc::TxLevel2P4,\n            17 => LedFunc::TxLevel1P0,\n            18 => LedFunc::Master,\n            19 => LedFunc::Slave,\n            20 => LedFunc::IncompatiableLinkCfg,\n            21 => LedFunc::AnLinkGood,\n            22 => LedFunc::AnComplete,\n            23 => LedFunc::TsTimer,\n            24 => LedFunc::LocRcvrStatus,\n            25 => LedFunc::RemRcvrStatus,\n            26 => LedFunc::Clk25Ref,\n            27 => LedFunc::TxTCLK,\n            28 => LedFunc::Clk120MHz,\n            e => panic!(\"Invalid value {}\", e),\n        }\n    }\n}\n\n/// LED Control Register\n#[derive(Copy, Clone, PartialEq, Eq, Hash)]\npub struct LedCntrl(pub u16);\nbitfield_bitrange! {struct LedCntrl(u16)}\n\nimpl LedCntrl {\n    bitfield_fields! {\n        u8;\n        /// LED 0 Pin Function\n        pub from into LedFunc, led0_function, set_led0_function: 4, 0;\n        /// LED 0 Mode Selection\n        pub led0_mode, set_led0_mode: 5;\n        /// Qualify Certain LED 0 Options with Link Status.\n        pub led0_link_st_qualify, set_led0_link_st_qualify: 6;\n        /// LED 0 Enable\n        pub led0_en, set_led0_en: 7;\n        /// LED 1 Pin Function\n        pub from into LedFunc, led1_function, set_led1_function: 12, 8;\n        /// /// LED 1 Mode Selection\n        pub led1_mode, set_led1_mode: 13;\n        /// Qualify Certain LED 1 Options with Link Status.\n        pub led1_link_st_qualify, set_led1_link_st_qualify: 14;\n        /// LED 1 Enable\n        pub led1_en, set_led1_en: 15;\n    }\n\n    pub fn new() -> Self {\n        LedCntrl(0)\n    }\n}\n\n// LED Polarity\n#[repr(u8)]\npub enum LedPol {\n    AutoSense = 0,\n    ActiveHigh,\n    ActiveLow,\n}\n\nimpl From<LedPol> for u8 {\n    fn from(val: LedPol) -> Self {\n        val as u8\n    }\n}\n\nimpl From<u8> for LedPol {\n    fn from(value: u8) -> Self {\n        match value {\n            0 => LedPol::AutoSense,\n            1 => LedPol::ActiveHigh,\n            2 => LedPol::ActiveLow,\n            e => panic!(\"Invalid value {}\", e),\n        }\n    }\n}\n\n/// LED Control Register\n#[derive(Copy, Clone, PartialEq, Eq, Hash)]\npub struct LedPolarity(pub u16);\nbitfield_bitrange! {struct LedPolarity(u16)}\n\nimpl LedPolarity {\n    bitfield_fields! {\n        u8;\n        /// LED 1 Polarity\n        pub from into LedPol, led1_polarity, set_led1_polarity: 3, 2;\n        /// LED 0 Polarity\n        pub from into LedPol, led0_polarity, set_led0_polarity: 1, 0;\n    }\n}\n\n/// SPI Header\n#[derive(Copy, Clone, PartialEq, Eq, Hash)]\npub struct SpiHeader(pub u16);\nbitfield_bitrange! {struct SpiHeader(u16)}\n\nimpl SpiHeader {\n    bitfield_fields! {\n        u16;\n        /// Mask Bit for TXF_ECC_ERR\n        pub control, set_control : 15;\n        pub full_duplex, set_full_duplex : 14;\n        /// Read or Write to register\n        pub write, set_write : 13;\n        /// Registers ID/addr\n        pub from into SpiRegisters, addr, set_addr: 11, 0;\n    }\n}\n"
  },
  {
    "path": "embassy-net-driver/CHANGELOG.md",
    "content": "# Changelog\n\nAll notable changes to this project will be documented in this file.\n\nThe format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),\nand this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).\n\n<!-- next-header -->\n## Unreleased - ReleaseDate\n\n## 0.2.0 - 2023-10-18\n\n- Added support for IEEE 802.15.4 mediums.\n- Added `Driver::hardware_address()`, `HardwareAddress`.\n- Removed `Medium` enum. The medium is deduced out of the hardware address.\n- Removed `Driver::ethernet_address()`. Replacement is `hardware_address()`.\n\n## 0.1.0 - 2023-06-29\n\n- First release\n"
  },
  {
    "path": "embassy-net-driver/Cargo.toml",
    "content": "[package]\nname = \"embassy-net-driver\"\nversion = \"0.2.0\"\nedition = \"2024\"\nlicense = \"MIT OR Apache-2.0\"\ndescription = \"Driver trait for the `embassy-net` async TCP/IP network stack.\"\nrepository = \"https://github.com/embassy-rs/embassy\"\ndocumentation = \"https://docs.embassy.dev/embassy-net-driver\"\ncategories = [\n    \"embedded\",\n    \"no-std\",\n    \"asynchronous\",\n]\n\n[package.metadata.embassy_docs]\nsrc_base = \"https://github.com/embassy-rs/embassy/blob/embassy-net-driver-v$VERSION/embassy-net-driver/src/\"\nsrc_base_git = \"https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-net-driver/src/\"\nfeatures = [\"defmt\"]\ntarget = \"thumbv7em-none-eabi\"\n\n[package.metadata.docs.rs]\nfeatures = [\"defmt\"]\n\n[dependencies]\ndefmt = { version = \"1.0.1\", optional = true }\n\n[features]\ndefmt = [\"dep:defmt\"]\n"
  },
  {
    "path": "embassy-net-driver/README.md",
    "content": "# embassy-net-driver\n\nThis crate contains the driver trait necessary for adding [`embassy-net`](https://crates.io/crates/embassy-net) support\nfor a new hardware platform.\n\nIf you want to *use* `embassy-net` with already made drivers, you should depend on the main `embassy-net` crate, not on this crate.\n\nIf you are writing a driver, you  should depend only on this crate, not on the main `embassy-net` crate.\nThis will allow your driver to continue working for newer `embassy-net` major versions, without needing an update,\nif the driver trait has not had breaking changes.\n\nSee also [`embassy-net-driver-channel`](https://crates.io/crates/embassy-net-driver-channel), which provides a higer-level API\nto construct a driver that processes packets in its own background task and communicates with the `embassy-net` task via\npacket queues for RX and TX.\n\n## Interoperability\n\nThis crate can run on any executor.\n"
  },
  {
    "path": "embassy-net-driver/src/lib.rs",
    "content": "#![no_std]\n#![allow(unsafe_op_in_unsafe_fn)]\n#![warn(missing_docs)]\n#![doc = include_str!(\"../README.md\")]\n\nuse core::task::Context;\n\n/// Representation of an hardware address, such as an Ethernet address or an IEEE802.15.4 address.\n#[derive(Debug, Clone, Copy, PartialEq, Eq)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\n#[non_exhaustive]\npub enum HardwareAddress {\n    /// Ethernet medium, with a A six-octet Ethernet address.\n    ///\n    /// Devices of this type send and receive Ethernet frames,\n    /// and interfaces using it must do neighbor discovery via ARP or NDISC.\n    ///\n    /// Examples of devices of this type are Ethernet, WiFi (802.11), Linux `tap`, and VPNs in tap (layer 2) mode.\n    Ethernet([u8; 6]),\n    /// 6LoWPAN over IEEE802.15.4, with an eight-octet address.\n    Ieee802154([u8; 8]),\n    /// Indicates that a Driver is IP-native, and has no hardware address.\n    ///\n    /// Devices of this type send and receive IP frames, without an\n    /// Ethernet header. MAC addresses are not used, and no neighbor discovery (ARP, NDISC) is done.\n    ///\n    /// Examples of devices of this type are the Linux `tun`, PPP interfaces, VPNs in tun (layer 3) mode.\n    Ip,\n}\n\n/// Main `embassy-net` driver API.\n///\n/// This is essentially an interface for sending and receiving raw network frames.\n///\n/// The interface is based on _tokens_, which are types that allow to receive/transmit a\n/// single packet. The `receive` and `transmit` functions only construct such tokens, the\n/// real sending/receiving operation are performed when the tokens are consumed.\npub trait Driver {\n    /// A token to receive a single network packet.\n    type RxToken<'a>: RxToken\n    where\n        Self: 'a;\n\n    /// A token to transmit a single network packet.\n    type TxToken<'a>: TxToken\n    where\n        Self: 'a;\n\n    /// Construct a token pair consisting of one receive token and one transmit token.\n    ///\n    /// If there is a packet ready to be received, this function must return `Some`.\n    /// If there isn't, it must return `None`, and wake `cx.waker()` when a packet is ready.\n    ///\n    /// The additional transmit token makes it possible to generate a reply packet based\n    /// on the contents of the received packet. For example, this makes it possible to\n    /// handle arbitrarily large ICMP echo (\"ping\") requests, where the all received bytes\n    /// need to be sent back, without heap allocation.\n    fn receive(&mut self, cx: &mut Context) -> Option<(Self::RxToken<'_>, Self::TxToken<'_>)>;\n\n    /// Construct a transmit token.\n    ///\n    /// If there is free space in the transmit buffer to transmit a packet, this function must return `Some`.\n    /// If there isn't, it must return `None`, and wake `cx.waker()` when space becomes available.\n    ///\n    /// Note that [`TxToken::consume`] is infallible, so it is not allowed to return a token\n    /// if there is no free space and fail later.\n    fn transmit(&mut self, cx: &mut Context) -> Option<Self::TxToken<'_>>;\n\n    /// Get the link state.\n    ///\n    /// This function must return the current link state of the device, and wake `cx.waker()` when\n    /// the link state changes.\n    fn link_state(&mut self, cx: &mut Context) -> LinkState;\n\n    /// Get a description of device capabilities.\n    fn capabilities(&self) -> Capabilities;\n\n    /// Get the device's hardware address.\n    ///\n    /// The returned hardware address also determines the \"medium\" of this driver. This indicates\n    /// what kind of packet the sent/received bytes are, and determines some behaviors of\n    /// the interface. For example, ARP/NDISC address resolution is only done for Ethernet mediums.\n    fn hardware_address(&self) -> HardwareAddress;\n}\n\nimpl<T: ?Sized + Driver> Driver for &mut T {\n    type RxToken<'a>\n        = T::RxToken<'a>\n    where\n        Self: 'a;\n    type TxToken<'a>\n        = T::TxToken<'a>\n    where\n        Self: 'a;\n\n    fn transmit(&mut self, cx: &mut Context) -> Option<Self::TxToken<'_>> {\n        T::transmit(self, cx)\n    }\n    fn receive(&mut self, cx: &mut Context) -> Option<(Self::RxToken<'_>, Self::TxToken<'_>)> {\n        T::receive(self, cx)\n    }\n    fn capabilities(&self) -> Capabilities {\n        T::capabilities(self)\n    }\n    fn link_state(&mut self, cx: &mut Context) -> LinkState {\n        T::link_state(self, cx)\n    }\n    fn hardware_address(&self) -> HardwareAddress {\n        T::hardware_address(self)\n    }\n}\n\n/// A token to receive a single network packet.\npub trait RxToken {\n    /// Consumes the token to receive a single network packet.\n    ///\n    /// This method receives a packet and then calls the given closure `f` with the raw\n    /// packet bytes as argument.\n    fn consume<R, F>(self, f: F) -> R\n    where\n        F: FnOnce(&mut [u8]) -> R;\n}\n\n/// A token to transmit a single network packet.\npub trait TxToken {\n    /// Consumes the token to send a single network packet.\n    ///\n    /// This method constructs a transmit buffer of size `len` and calls the passed\n    /// closure `f` with a mutable reference to that buffer. The closure should construct\n    /// a valid network packet (e.g. an ethernet packet) in the buffer. When the closure\n    /// returns, the transmit buffer is sent out.\n    fn consume<R, F>(self, len: usize, f: F) -> R\n    where\n        F: FnOnce(&mut [u8]) -> R;\n}\n\n/// A description of device capabilities.\n///\n/// Higher-level protocols may achieve higher throughput or lower latency if they consider\n/// the bandwidth or packet size limitations.\n#[derive(Debug, Clone, Default)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\n#[non_exhaustive]\npub struct Capabilities {\n    /// Maximum transmission unit.\n    ///\n    /// The network device is unable to send or receive frames larger than the value returned\n    /// by this function.\n    ///\n    /// For Ethernet devices, this is the maximum Ethernet frame size, including the Ethernet header (14 octets), but\n    /// *not* including the Ethernet FCS (4 octets). Therefore, Ethernet MTU = IP MTU + 14.\n    ///\n    /// Note that in Linux and other OSes, \"MTU\" is the IP MTU, not the Ethernet MTU, even for Ethernet\n    /// devices. This is a common source of confusion.\n    ///\n    /// Most common IP MTU is 1500. Minimum is 576 (for IPv4) or 1280 (for IPv6). Maximum is 9216 octets.\n    pub max_transmission_unit: usize,\n\n    /// Maximum burst size, in terms of MTU.\n    ///\n    /// The network device is unable to send or receive bursts large than the value returned\n    /// by this function.\n    ///\n    /// If `None`, there is no fixed limit on burst size, e.g. if network buffers are\n    /// dynamically allocated.\n    pub max_burst_size: Option<usize>,\n\n    /// Checksum behavior.\n    ///\n    /// If the network device is capable of verifying or computing checksums for some protocols,\n    /// it can request that the stack not do so in software to improve performance.\n    pub checksum: ChecksumCapabilities,\n}\n\n/// A description of checksum behavior for every supported protocol.\n#[derive(Debug, Clone, Default)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\n#[non_exhaustive]\npub struct ChecksumCapabilities {\n    /// Checksum behavior for IPv4.\n    pub ipv4: Checksum,\n    /// Checksum behavior for UDP.\n    pub udp: Checksum,\n    /// Checksum behavior for TCP.\n    pub tcp: Checksum,\n    /// Checksum behavior for ICMPv4.\n    pub icmpv4: Checksum,\n    /// Checksum behavior for ICMPv6.\n    pub icmpv6: Checksum,\n}\n\n/// A description of checksum behavior for a particular protocol.\n#[derive(Debug, Clone, Copy)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub enum Checksum {\n    /// Verify checksum when receiving and compute checksum when sending.\n    Both,\n    /// Verify checksum when receiving.\n    Rx,\n    /// Compute checksum before sending.\n    Tx,\n    /// Ignore checksum completely.\n    None,\n}\n\nimpl Default for Checksum {\n    fn default() -> Checksum {\n        Checksum::Both\n    }\n}\n\n/// The link state of a network device.\n#[derive(PartialEq, Eq, Clone, Copy)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub enum LinkState {\n    /// The link is down.\n    Down,\n    /// The link is up.\n    Up,\n}\n"
  },
  {
    "path": "embassy-net-driver-channel/CHANGELOG.md",
    "content": "# Changelog\n\nAll notable changes to this project will be documented in this file.\n\nThe format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),\nand this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).\n\n<!-- next-header -->\n## Unreleased - ReleaseDate\n\n## 0.4.0 - 2026-03-11\n\n- Update embassy-sync to 0.8.0\n\n## 0.3.2 - 2025-08-26\n\n## 0.3.1 - 2025-07-16\n\n- Update `embassy-sync` to v0.7.0\n\n## 0.3.0 - 2024-08-05\n\n- Add collapse_debuginfo to fmt.rs macros.\n- Update embassy-sync version\n\n## 0.2.0 - 2023-10-18\n\n- Update `embassy-net-driver` to v0.2\n- `Runner::new` now takes an `embassy_net_driver::HardwareAddress` parameter.\n- `Runner::set_ethernet_address` is now `set_hardware_address`.\n\n## 0.1.0 - 2023-06-29\n\n- First release\n"
  },
  {
    "path": "embassy-net-driver-channel/Cargo.toml",
    "content": "[package]\nname = \"embassy-net-driver-channel\"\nversion = \"0.4.0\"\nedition = \"2024\"\nlicense = \"MIT OR Apache-2.0\"\ndescription = \"High-level channel-based driver for the `embassy-net` async TCP/IP network stack.\"\nrepository = \"https://github.com/embassy-rs/embassy\"\ndocumentation = \"https://docs.embassy.dev/embassy-net-driver-channel\"\ncategories = [\n    \"embedded\",\n    \"no-std\",\n    \"asynchronous\",\n]\n\n[package.metadata.embassy_docs]\nsrc_base = \"https://github.com/embassy-rs/embassy/blob/embassy-net-driver-channel-v$VERSION/embassy-net-driver-channel/src/\"\nsrc_base_git = \"https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-net-driver-channel/src/\"\nfeatures = [\"defmt\"]\ntarget = \"thumbv7em-none-eabi\"\n\n[package.metadata.docs.rs]\nfeatures = [\"defmt\"]\n\n[dependencies]\ndefmt = { version = \"1.0.1\", optional = true }\nlog = { version = \"0.4.14\", optional = true }\n\nembassy-sync = { version = \"0.8.0\", path = \"../embassy-sync\" }\nembassy-futures = { version = \"0.1.2\", path = \"../embassy-futures\" }\nembassy-net-driver = { version = \"0.2.0\", path = \"../embassy-net-driver\" }\n\n[features]\ndefmt = [\"dep:defmt\"]\nlog = [\"dep:log\"]\n"
  },
  {
    "path": "embassy-net-driver-channel/README.md",
    "content": "# embassy-net-driver-channel\n\nThis crate provides a toolkit for implementing [`embassy-net`](https://crates.io/crates/embassy-net) drivers in a\nhigher level way than implementing the [`embassy-net-driver`](https://crates.io/crates/embassy-net-driver) trait directly.\n\nThe `embassy-net-driver` trait is polling-based. To implement it, you must write the packet receive/transmit state machines by\nhand, and hook up the `Waker`s provided by `embassy-net` to the right interrupt handlers so that `embassy-net`\nknows when to poll your driver again to make more progress.\n\nWith `embassy-net-driver-channel` you get a \"channel-like\" interface instead, where you can send/receive packets\nto/from embassy-net. The intended usage is to spawn a \"driver task\" in the background that does this, passing\npackets between the hardware and the channel.\n\n## A note about deadlocks\n\nWhen implementing a driver using this crate, it might be tempting to write it in the most straightforward way:\n\n```rust,ignore\nloop {\n    // Wait for either..\n    match select(\n        // ... the chip signaling an interrupt, indicating a packet is available to receive, or\n        irq_pin.wait_for_low(),\n        // ... a TX buffer becoming available, i.e. embassy-net wants to send a packet\n        tx_chan.tx_buf(),\n    ).await {\n        Either::First(_) => {\n            // a packet is ready to be received!\n            let buf = rx_chan.rx_buf().await; // allocate a rx buf from the packet queue\n            let n = receive_packet_over_spi(buf).await;\n            rx_chan.rx_done(n);\n        }\n        Either::Second(buf) => {\n            // a packet is ready to be sent!\n            send_packet_over_spi(buf).await;\n            tx_chan.tx_done();\n        }\n    }\n}\n```\n\nHowever, this code has a latent deadlock bug. The symptom is it can hang at `rx_chan.rx_buf().await` under load.\n\nThe reason is that, under load, both the TX and RX queues can get full at the same time. When this happens, the `embassy-net` task stalls trying to send because the TX queue is full, therefore it stops processing packets in the RX queue. Your driver task also stalls because the RX queue is full, therefore it stops processing packets in the TX queue.\n\nThe fix is to make sure to always service the TX queue while you're waiting for space to become available in the RX queue. For example, select on either \"tx_chan.tx_buf() available\" or \"INT is low AND rx_chan.rx_buf() available\":\n\n```rust,ignore\nloop {\n    // Wait for either..\n    match select(\n        async {\n            // ... the chip signaling an interrupt, indicating a packet is available to receive\n            irq_pin.wait_for_low().await;\n            // *AND* the buffer is ready...\n            rx_chan.rx_buf().await\n        },\n        // ... or a TX buffer becoming available, i.e. embassy-net wants to send a packet\n        tx_chan.tx_buf(),\n    ).await {\n        Either::First(buf) => {\n            // a packet is ready to be received!\n            let n = receive_packet_over_spi(buf).await;\n            rx_chan.rx_done(n);\n        }\n        Either::Second(buf) => {\n            // a packet is ready to be sent!\n            send_packet_over_spi(buf).await;\n            tx_chan.tx_done();\n        }\n    }\n}\n```\n\n## Examples\n\nThese `embassy-net` drivers are implemented using this crate. You can look at them for inspiration.\n\n- [`cyw43`](https://github.com/embassy-rs/embassy/tree/main/cyw43) for WiFi on CYW43xx chips, used in the Raspberry Pi Pico W\n- [`embassy-usb`](https://github.com/embassy-rs/embassy/tree/main/embassy-usb) for Ethernet-over-USB (CDC NCM) support.\n- [`embassy-net-wiznet`](https://github.com/embassy-rs/embassy/tree/main/embassy-net-wiznet) for Wiznet SPI Ethernet MAC+PHY chips.\n- [`embassy-net-esp-hosted`](https://github.com/embassy-rs/embassy/tree/main/embassy-net-esp-hosted) for using ESP32 chips with the [`esp-hosted`](https://github.com/espressif/esp-hosted) firmware as WiFi adapters for another non-ESP32 MCU.\n\n## Interoperability\n\nThis crate can run on any executor.\n"
  },
  {
    "path": "embassy-net-driver-channel/src/fmt.rs",
    "content": "#![macro_use]\n#![allow(unused)]\n\nuse core::fmt::{Debug, Display, LowerHex};\n\n#[cfg(all(feature = \"defmt\", feature = \"log\"))]\ncompile_error!(\"You may not enable both `defmt` and `log` features.\");\n\n#[collapse_debuginfo(yes)]\nmacro_rules! assert {\n    ($($x:tt)*) => {\n        {\n            #[cfg(not(feature = \"defmt\"))]\n            ::core::assert!($($x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::assert!($($x)*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! assert_eq {\n    ($($x:tt)*) => {\n        {\n            #[cfg(not(feature = \"defmt\"))]\n            ::core::assert_eq!($($x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::assert_eq!($($x)*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! assert_ne {\n    ($($x:tt)*) => {\n        {\n            #[cfg(not(feature = \"defmt\"))]\n            ::core::assert_ne!($($x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::assert_ne!($($x)*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! debug_assert {\n    ($($x:tt)*) => {\n        {\n            #[cfg(not(feature = \"defmt\"))]\n            ::core::debug_assert!($($x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::debug_assert!($($x)*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! debug_assert_eq {\n    ($($x:tt)*) => {\n        {\n            #[cfg(not(feature = \"defmt\"))]\n            ::core::debug_assert_eq!($($x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::debug_assert_eq!($($x)*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! debug_assert_ne {\n    ($($x:tt)*) => {\n        {\n            #[cfg(not(feature = \"defmt\"))]\n            ::core::debug_assert_ne!($($x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::debug_assert_ne!($($x)*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! todo {\n    ($($x:tt)*) => {\n        {\n            #[cfg(not(feature = \"defmt\"))]\n            ::core::todo!($($x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::todo!($($x)*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! unreachable {\n    ($($x:tt)*) => {\n        {\n            #[cfg(not(feature = \"defmt\"))]\n            ::core::unreachable!($($x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::unreachable!($($x)*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! panic {\n    ($($x:tt)*) => {\n        {\n            #[cfg(not(feature = \"defmt\"))]\n            ::core::panic!($($x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::panic!($($x)*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! trace {\n    ($s:literal $(, $x:expr)* $(,)?) => {\n        {\n            #[cfg(feature = \"log\")]\n            ::log::trace!($s $(, $x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::trace!($s $(, $x)*);\n            #[cfg(not(any(feature = \"log\", feature=\"defmt\")))]\n            let _ = ($( & $x ),*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! debug {\n    ($s:literal $(, $x:expr)* $(,)?) => {\n        {\n            #[cfg(feature = \"log\")]\n            ::log::debug!($s $(, $x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::debug!($s $(, $x)*);\n            #[cfg(not(any(feature = \"log\", feature=\"defmt\")))]\n            let _ = ($( & $x ),*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! info {\n    ($s:literal $(, $x:expr)* $(,)?) => {\n        {\n            #[cfg(feature = \"log\")]\n            ::log::info!($s $(, $x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::info!($s $(, $x)*);\n            #[cfg(not(any(feature = \"log\", feature=\"defmt\")))]\n            let _ = ($( & $x ),*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! warn {\n    ($s:literal $(, $x:expr)* $(,)?) => {\n        {\n            #[cfg(feature = \"log\")]\n            ::log::warn!($s $(, $x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::warn!($s $(, $x)*);\n            #[cfg(not(any(feature = \"log\", feature=\"defmt\")))]\n            let _ = ($( & $x ),*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! error {\n    ($s:literal $(, $x:expr)* $(,)?) => {\n        {\n            #[cfg(feature = \"log\")]\n            ::log::error!($s $(, $x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::error!($s $(, $x)*);\n            #[cfg(not(any(feature = \"log\", feature=\"defmt\")))]\n            let _ = ($( & $x ),*);\n        }\n    };\n}\n\n#[cfg(feature = \"defmt\")]\n#[collapse_debuginfo(yes)]\nmacro_rules! unwrap {\n    ($($x:tt)*) => {\n        ::defmt::unwrap!($($x)*)\n    };\n}\n\n#[cfg(not(feature = \"defmt\"))]\n#[collapse_debuginfo(yes)]\nmacro_rules! unwrap {\n    ($arg:expr) => {\n        match $crate::fmt::Try::into_result($arg) {\n            ::core::result::Result::Ok(t) => t,\n            ::core::result::Result::Err(e) => {\n                ::core::panic!(\"unwrap of `{}` failed: {:?}\", ::core::stringify!($arg), e);\n            }\n        }\n    };\n    ($arg:expr, $($msg:expr),+ $(,)? ) => {\n        match $crate::fmt::Try::into_result($arg) {\n            ::core::result::Result::Ok(t) => t,\n            ::core::result::Result::Err(e) => {\n                ::core::panic!(\"unwrap of `{}` failed: {}: {:?}\", ::core::stringify!($arg), ::core::format_args!($($msg,)*), e);\n            }\n        }\n    }\n}\n\n#[derive(Debug, Copy, Clone, Eq, PartialEq)]\npub struct NoneError;\n\npub trait Try {\n    type Ok;\n    type Error;\n    fn into_result(self) -> Result<Self::Ok, Self::Error>;\n}\n\nimpl<T> Try for Option<T> {\n    type Ok = T;\n    type Error = NoneError;\n\n    #[inline]\n    fn into_result(self) -> Result<T, NoneError> {\n        self.ok_or(NoneError)\n    }\n}\n\nimpl<T, E> Try for Result<T, E> {\n    type Ok = T;\n    type Error = E;\n\n    #[inline]\n    fn into_result(self) -> Self {\n        self\n    }\n}\n\npub(crate) struct Bytes<'a>(pub &'a [u8]);\n\nimpl<'a> Debug for Bytes<'a> {\n    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {\n        write!(f, \"{:#02x?}\", self.0)\n    }\n}\n\nimpl<'a> Display for Bytes<'a> {\n    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {\n        write!(f, \"{:#02x?}\", self.0)\n    }\n}\n\nimpl<'a> LowerHex for Bytes<'a> {\n    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {\n        write!(f, \"{:#02x?}\", self.0)\n    }\n}\n\n#[cfg(feature = \"defmt\")]\nimpl<'a> defmt::Format for Bytes<'a> {\n    fn format(&self, fmt: defmt::Formatter) {\n        defmt::write!(fmt, \"{:02x}\", self.0)\n    }\n}\n"
  },
  {
    "path": "embassy-net-driver-channel/src/lib.rs",
    "content": "#![no_std]\n#![doc = include_str!(\"../README.md\")]\n#![warn(missing_docs)]\n\n// must go first!\nmod fmt;\n\nuse core::cell::RefCell;\nuse core::mem::MaybeUninit;\nuse core::task::{Context, Poll};\n\npub use embassy_net_driver as driver;\nuse embassy_net_driver::{Capabilities, LinkState};\nuse embassy_sync::blocking_mutex::Mutex;\nuse embassy_sync::blocking_mutex::raw::NoopRawMutex;\nuse embassy_sync::waitqueue::WakerRegistration;\nuse embassy_sync::zerocopy_channel;\n\n/// Channel state.\n///\n/// Holds a buffer of packets with size MTU, for both TX and RX.\npub struct State<const MTU: usize, const N_RX: usize, const N_TX: usize> {\n    rx: [PacketBuf<MTU>; N_RX],\n    tx: [PacketBuf<MTU>; N_TX],\n    inner: MaybeUninit<StateInner<'static, MTU>>,\n}\n\nimpl<const MTU: usize, const N_RX: usize, const N_TX: usize> State<MTU, N_RX, N_TX> {\n    /// Create a new channel state.\n    pub const fn new() -> Self {\n        Self {\n            rx: [const { PacketBuf::new() }; N_RX],\n            tx: [const { PacketBuf::new() }; N_TX],\n            inner: MaybeUninit::uninit(),\n        }\n    }\n}\n\nstruct StateInner<'d, const MTU: usize> {\n    rx: zerocopy_channel::Channel<'d, NoopRawMutex, PacketBuf<MTU>>,\n    tx: zerocopy_channel::Channel<'d, NoopRawMutex, PacketBuf<MTU>>,\n    shared: Mutex<NoopRawMutex, RefCell<Shared>>,\n}\n\nstruct Shared {\n    link_state: LinkState,\n    waker: WakerRegistration,\n    hardware_address: driver::HardwareAddress,\n}\n\n/// Channel runner.\n///\n/// Holds the shared state and the lower end of channels for inbound and outbound packets.\npub struct Runner<'d, const MTU: usize> {\n    tx_chan: zerocopy_channel::Receiver<'d, NoopRawMutex, PacketBuf<MTU>>,\n    rx_chan: zerocopy_channel::Sender<'d, NoopRawMutex, PacketBuf<MTU>>,\n    shared: &'d Mutex<NoopRawMutex, RefCell<Shared>>,\n}\n\n/// State runner.\n///\n/// Holds the shared state of the channel such as link state.\n#[derive(Clone, Copy)]\npub struct StateRunner<'d> {\n    shared: &'d Mutex<NoopRawMutex, RefCell<Shared>>,\n}\n\n/// RX runner.\n///\n/// Holds the lower end of the channel for passing inbound packets up the stack.\npub struct RxRunner<'d, const MTU: usize> {\n    rx_chan: zerocopy_channel::Sender<'d, NoopRawMutex, PacketBuf<MTU>>,\n}\n\n/// TX runner.\n///\n/// Holds the lower end of the channel for passing outbound packets down the stack.\npub struct TxRunner<'d, const MTU: usize> {\n    tx_chan: zerocopy_channel::Receiver<'d, NoopRawMutex, PacketBuf<MTU>>,\n}\n\nimpl<'d, const MTU: usize> Runner<'d, MTU> {\n    /// Split the runner into separate runners for controlling state, rx and tx.\n    pub fn split(self) -> (StateRunner<'d>, RxRunner<'d, MTU>, TxRunner<'d, MTU>) {\n        (\n            StateRunner { shared: self.shared },\n            RxRunner { rx_chan: self.rx_chan },\n            TxRunner { tx_chan: self.tx_chan },\n        )\n    }\n\n    /// Split the runner into separate runners for controlling state, rx and tx borrowing the underlying state.\n    pub fn borrow_split(&mut self) -> (StateRunner<'_>, RxRunner<'_, MTU>, TxRunner<'_, MTU>) {\n        (\n            StateRunner { shared: self.shared },\n            RxRunner {\n                rx_chan: self.rx_chan.borrow(),\n            },\n            TxRunner {\n                tx_chan: self.tx_chan.borrow(),\n            },\n        )\n    }\n\n    /// Create a state runner sharing the state channel.\n    pub fn state_runner(&self) -> StateRunner<'d> {\n        StateRunner { shared: self.shared }\n    }\n\n    /// Set the link state.\n    pub fn set_link_state(&mut self, state: LinkState) {\n        self.shared.lock(|s| {\n            let s = &mut *s.borrow_mut();\n            s.link_state = state;\n            s.waker.wake();\n        });\n    }\n\n    /// Set the hardware address.\n    pub fn set_hardware_address(&mut self, address: driver::HardwareAddress) {\n        self.shared.lock(|s| {\n            let s = &mut *s.borrow_mut();\n            s.hardware_address = address;\n            s.waker.wake();\n        });\n    }\n\n    /// Wait until there is space for more inbound packets and return a slice they can be copied into.\n    pub async fn rx_buf(&mut self) -> &mut [u8] {\n        let p = self.rx_chan.send().await;\n        &mut p.buf\n    }\n\n    /// Check if there is space for more inbound packets right now.\n    pub fn try_rx_buf(&mut self) -> Option<&mut [u8]> {\n        let p = self.rx_chan.try_send()?;\n        Some(&mut p.buf)\n    }\n\n    /// Polling the inbound channel if there is space for packets.\n    pub fn poll_rx_buf(&mut self, cx: &mut Context) -> Poll<&mut [u8]> {\n        match self.rx_chan.poll_send(cx) {\n            Poll::Ready(p) => Poll::Ready(&mut p.buf),\n            Poll::Pending => Poll::Pending,\n        }\n    }\n\n    /// Mark packet of len bytes as pushed to the inbound channel.\n    pub fn rx_done(&mut self, len: usize) {\n        let p = self.rx_chan.try_send().unwrap();\n        p.len = len;\n        self.rx_chan.send_done();\n    }\n\n    /// Wait until there is space for more outbound packets and return a slice they can be copied into.\n    pub async fn tx_buf(&mut self) -> &mut [u8] {\n        let p = self.tx_chan.receive().await;\n        &mut p.buf[..p.len]\n    }\n\n    /// Check if there is space for more outbound packets right now.\n    pub fn try_tx_buf(&mut self) -> Option<&mut [u8]> {\n        let p = self.tx_chan.try_receive()?;\n        Some(&mut p.buf[..p.len])\n    }\n\n    /// Polling the outbound channel if there is space for packets.\n    pub fn poll_tx_buf(&mut self, cx: &mut Context) -> Poll<&mut [u8]> {\n        match self.tx_chan.poll_receive(cx) {\n            Poll::Ready(p) => Poll::Ready(&mut p.buf[..p.len]),\n            Poll::Pending => Poll::Pending,\n        }\n    }\n\n    /// Mark outbound packet as copied.\n    pub fn tx_done(&mut self) {\n        self.tx_chan.receive_done();\n    }\n}\n\nimpl<'d> StateRunner<'d> {\n    /// Set link state.\n    pub fn set_link_state(&self, state: LinkState) {\n        self.shared.lock(|s| {\n            let s = &mut *s.borrow_mut();\n            s.link_state = state;\n            s.waker.wake();\n        });\n    }\n\n    /// Set the hardware address.\n    pub fn set_hardware_address(&self, address: driver::HardwareAddress) {\n        self.shared.lock(|s| {\n            let s = &mut *s.borrow_mut();\n            s.hardware_address = address;\n            s.waker.wake();\n        });\n    }\n}\n\nimpl<'d, const MTU: usize> RxRunner<'d, MTU> {\n    /// Wait until there is space for more inbound packets and return a slice they can be copied into.\n    pub async fn rx_buf(&mut self) -> &mut [u8] {\n        let p = self.rx_chan.send().await;\n        &mut p.buf\n    }\n\n    /// Check if there is space for more inbound packets right now.\n    pub fn try_rx_buf(&mut self) -> Option<&mut [u8]> {\n        let p = self.rx_chan.try_send()?;\n        Some(&mut p.buf)\n    }\n\n    /// Polling the inbound channel if there is space for packets.\n    pub fn poll_rx_buf(&mut self, cx: &mut Context) -> Poll<&mut [u8]> {\n        match self.rx_chan.poll_send(cx) {\n            Poll::Ready(p) => Poll::Ready(&mut p.buf),\n            Poll::Pending => Poll::Pending,\n        }\n    }\n\n    /// Mark packet of len bytes as pushed to the inbound channel.\n    pub fn rx_done(&mut self, len: usize) {\n        let p = self.rx_chan.try_send().unwrap();\n        p.len = len;\n        self.rx_chan.send_done();\n    }\n}\n\nimpl<'d, const MTU: usize> TxRunner<'d, MTU> {\n    /// Wait until there is space for more outbound packets and return a slice they can be copied into.\n    pub async fn tx_buf(&mut self) -> &mut [u8] {\n        let p = self.tx_chan.receive().await;\n        &mut p.buf[..p.len]\n    }\n\n    /// Check if there is space for more outbound packets right now.\n    pub fn try_tx_buf(&mut self) -> Option<&mut [u8]> {\n        let p = self.tx_chan.try_receive()?;\n        Some(&mut p.buf[..p.len])\n    }\n\n    /// Polling the outbound channel if there is space for packets.\n    pub fn poll_tx_buf(&mut self, cx: &mut Context) -> Poll<&mut [u8]> {\n        match self.tx_chan.poll_receive(cx) {\n            Poll::Ready(p) => Poll::Ready(&mut p.buf[..p.len]),\n            Poll::Pending => Poll::Pending,\n        }\n    }\n\n    /// Mark outbound packet as copied.\n    pub fn tx_done(&mut self) {\n        self.tx_chan.receive_done();\n    }\n}\n\n/// Create a channel.\n///\n/// Returns a pair of handles for interfacing with the peripheral and the networking stack.\n///\n/// The runner is interfacing with the peripheral at the lower part of the stack.\n/// The device is interfacing with the networking stack on the layer above.\npub fn new<'d, const MTU: usize, const N_RX: usize, const N_TX: usize>(\n    state: &'d mut State<MTU, N_RX, N_TX>,\n    hardware_address: driver::HardwareAddress,\n) -> (Runner<'d, MTU>, Device<'d, MTU>) {\n    let mut caps = Capabilities::default();\n    caps.max_transmission_unit = MTU;\n\n    // safety: this is a self-referential struct, however:\n    // - it can't move while the `'d` borrow is active.\n    // - when the borrow ends, the dangling references inside the MaybeUninit will never be used again.\n    let state_uninit: *mut MaybeUninit<StateInner<'d, MTU>> =\n        (&mut state.inner as *mut MaybeUninit<StateInner<'static, MTU>>).cast();\n    let state = unsafe { &mut *state_uninit }.write(StateInner {\n        rx: zerocopy_channel::Channel::new(&mut state.rx[..]),\n        tx: zerocopy_channel::Channel::new(&mut state.tx[..]),\n        shared: Mutex::new(RefCell::new(Shared {\n            link_state: LinkState::Down,\n            hardware_address,\n            waker: WakerRegistration::new(),\n        })),\n    });\n\n    let (rx_sender, rx_receiver) = state.rx.split();\n    let (tx_sender, tx_receiver) = state.tx.split();\n\n    (\n        Runner {\n            tx_chan: tx_receiver,\n            rx_chan: rx_sender,\n            shared: &state.shared,\n        },\n        Device {\n            caps,\n            shared: &state.shared,\n            rx: rx_receiver,\n            tx: tx_sender,\n        },\n    )\n}\n\n/// Represents a packet of size MTU.\npub struct PacketBuf<const MTU: usize> {\n    len: usize,\n    buf: [u8; MTU],\n}\n\nimpl<const MTU: usize> PacketBuf<MTU> {\n    /// Create a new packet buffer.\n    pub const fn new() -> Self {\n        Self { len: 0, buf: [0; MTU] }\n    }\n}\n\n/// Channel device.\n///\n/// Holds the shared state and upper end of channels for inbound and outbound packets.\npub struct Device<'d, const MTU: usize> {\n    rx: zerocopy_channel::Receiver<'d, NoopRawMutex, PacketBuf<MTU>>,\n    tx: zerocopy_channel::Sender<'d, NoopRawMutex, PacketBuf<MTU>>,\n    shared: &'d Mutex<NoopRawMutex, RefCell<Shared>>,\n    caps: Capabilities,\n}\n\nimpl<'d, const MTU: usize> embassy_net_driver::Driver for Device<'d, MTU> {\n    type RxToken<'a>\n        = RxToken<'a, MTU>\n    where\n        Self: 'a;\n    type TxToken<'a>\n        = TxToken<'a, MTU>\n    where\n        Self: 'a;\n\n    fn receive(&mut self, cx: &mut Context) -> Option<(Self::RxToken<'_>, Self::TxToken<'_>)> {\n        if self.rx.poll_receive(cx).is_ready() && self.tx.poll_send(cx).is_ready() {\n            Some((RxToken { rx: self.rx.borrow() }, TxToken { tx: self.tx.borrow() }))\n        } else {\n            None\n        }\n    }\n\n    /// Construct a transmit token.\n    fn transmit(&mut self, cx: &mut Context) -> Option<Self::TxToken<'_>> {\n        if self.tx.poll_send(cx).is_ready() {\n            Some(TxToken { tx: self.tx.borrow() })\n        } else {\n            None\n        }\n    }\n\n    /// Get a description of device capabilities.\n    fn capabilities(&self) -> Capabilities {\n        self.caps.clone()\n    }\n\n    fn hardware_address(&self) -> driver::HardwareAddress {\n        self.shared.lock(|s| s.borrow().hardware_address)\n    }\n\n    fn link_state(&mut self, cx: &mut Context) -> LinkState {\n        self.shared.lock(|s| {\n            let s = &mut *s.borrow_mut();\n            s.waker.register(cx.waker());\n            s.link_state\n        })\n    }\n}\n\n/// A rx token.\n///\n/// Holds inbound receive channel and interfaces with embassy-net-driver.\npub struct RxToken<'a, const MTU: usize> {\n    rx: zerocopy_channel::Receiver<'a, NoopRawMutex, PacketBuf<MTU>>,\n}\n\nimpl<'a, const MTU: usize> embassy_net_driver::RxToken for RxToken<'a, MTU> {\n    fn consume<R, F>(mut self, f: F) -> R\n    where\n        F: FnOnce(&mut [u8]) -> R,\n    {\n        // NOTE(unwrap): we checked the queue wasn't full when creating the token.\n        let pkt = unwrap!(self.rx.try_receive());\n        let r = f(&mut pkt.buf[..pkt.len]);\n        self.rx.receive_done();\n        r\n    }\n}\n\n/// A tx token.\n///\n/// Holds outbound transmit channel and interfaces with embassy-net-driver.\npub struct TxToken<'a, const MTU: usize> {\n    tx: zerocopy_channel::Sender<'a, NoopRawMutex, PacketBuf<MTU>>,\n}\n\nimpl<'a, const MTU: usize> embassy_net_driver::TxToken for TxToken<'a, MTU> {\n    fn consume<R, F>(mut self, len: usize, f: F) -> R\n    where\n        F: FnOnce(&mut [u8]) -> R,\n    {\n        // NOTE(unwrap): we checked the queue wasn't full when creating the token.\n        let pkt = unwrap!(self.tx.try_send());\n        let r = f(&mut pkt.buf[..len]);\n        pkt.len = len;\n        self.tx.send_done();\n        r\n    }\n}\n"
  },
  {
    "path": "embassy-net-enc28j60/CHANGELOG.md",
    "content": "# Changelog\n\nAll notable changes to this project will be documented in this file.\n\nThe format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),\nand this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).\n\n<!-- next-header -->\n## Unreleased - ReleaseDate\n\n## 0.3.0 - 2026-03-10\n\n- Update embassy-net-driver-channel to 0.4.0\n\n## 0.2.1 - 2025-08-26\n\n## 0.1.1 - 2025-08-16\n\n- First release with changelog.\n"
  },
  {
    "path": "embassy-net-enc28j60/Cargo.toml",
    "content": "[package]\nname = \"embassy-net-enc28j60\"\nversion = \"0.3.0\"\ndescription = \"embassy-net driver for the ENC28J60 ethernet chip\"\nkeywords = [\"embedded\", \"enc28j60\", \"embassy-net\", \"embedded-hal-async\", \"ethernet\"]\ncategories = [\"embedded\", \"hardware-support\", \"no-std\", \"network-programming\", \"asynchronous\"]\nlicense = \"MIT OR Apache-2.0\"\nedition = \"2024\"\nrepository = \"https://github.com/embassy-rs/embassy\"\ndocumentation = \"https://docs.embassy.dev/embassy-net-enc28j60\"\n\n[dependencies]\nembedded-hal = { version = \"1.0\" }\nembedded-hal-async = { version = \"1.0\" }\nembassy-net-driver = { version = \"0.2.0\", path = \"../embassy-net-driver\" }\nembassy-time = { version = \"0.5.1\", path = \"../embassy-time\" }\nembassy-futures = { version = \"0.1.2\", path = \"../embassy-futures\" }\n\ndefmt = { version = \"1.0.1\", optional = true }\nlog = { version = \"0.4.14\", optional = true }\n\n[features]\ndefmt = [\"dep:defmt\", \"embassy-net-driver/defmt\"]\nlog = [\"dep:log\"]\n\n[package.metadata.embassy_docs]\nsrc_base = \"https://github.com/embassy-rs/embassy/blob/embassy-net-enc28j60-v$VERSION/embassy-net-enc28j60/src/\"\nsrc_base_git = \"https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-net-enc28j60/src/\"\ntarget = \"thumbv7em-none-eabi\"\nfeatures = [\"defmt\"]\n\n[package.metadata.docs.rs]\nfeatures = [\"defmt\"]\n"
  },
  {
    "path": "embassy-net-enc28j60/README.md",
    "content": "# `embassy-net-enc28j60`\n\n[`embassy-net`](https://crates.io/crates/embassy-net) integration for the Microchip ENC28J60 Ethernet chip.\n\nBased on [@japaric](https://github.com/japaric)'s [`enc28j60`](https://github.com/japaric/enc28j60) crate.\n\n## Interoperability\n\nThis crate can run on any executor.\n"
  },
  {
    "path": "embassy-net-enc28j60/src/bank0.rs",
    "content": "#[allow(dead_code)]\n#[derive(Clone, Copy)]\npub enum Register {\n    ERDPTL = 0x00,\n    ERDPTH = 0x01,\n    EWRPTL = 0x02,\n    EWRPTH = 0x03,\n    ETXSTL = 0x04,\n    ETXSTH = 0x05,\n    ETXNDL = 0x06,\n    ETXNDH = 0x07,\n    ERXSTL = 0x08,\n    ERXSTH = 0x09,\n    ERXNDL = 0x0a,\n    ERXNDH = 0x0b,\n    ERXRDPTL = 0x0c,\n    ERXRDPTH = 0x0d,\n    ERXWRPTL = 0x0e,\n    ERXWRPTH = 0x0f,\n    EDMASTL = 0x10,\n    EDMASTH = 0x11,\n    EDMANDL = 0x12,\n    EDMANDH = 0x13,\n    EDMADSTL = 0x14,\n    EDMADSTH = 0x15,\n    EDMACSL = 0x16,\n    EDMACSH = 0x17,\n}\n\nimpl Register {\n    pub(crate) fn addr(&self) -> u8 {\n        *self as u8\n    }\n\n    pub(crate) fn is_eth_register(&self) -> bool {\n        match *self {\n            Register::ERDPTL => true,\n            Register::ERDPTH => true,\n            Register::EWRPTL => true,\n            Register::EWRPTH => true,\n            Register::ETXSTL => true,\n            Register::ETXSTH => true,\n            Register::ETXNDL => true,\n            Register::ETXNDH => true,\n            Register::ERXSTL => true,\n            Register::ERXSTH => true,\n            Register::ERXNDL => true,\n            Register::ERXNDH => true,\n            Register::ERXRDPTL => true,\n            Register::ERXRDPTH => true,\n            Register::ERXWRPTL => true,\n            Register::ERXWRPTH => true,\n            Register::EDMASTL => true,\n            Register::EDMASTH => true,\n            Register::EDMANDL => true,\n            Register::EDMANDH => true,\n            Register::EDMADSTL => true,\n            Register::EDMADSTH => true,\n            Register::EDMACSL => true,\n            Register::EDMACSH => true,\n        }\n    }\n}\n\nimpl Into<super::Register> for Register {\n    fn into(self) -> super::Register {\n        super::Register::Bank0(self)\n    }\n}\n"
  },
  {
    "path": "embassy-net-enc28j60/src/bank1.rs",
    "content": "#[allow(dead_code)]\n#[derive(Clone, Copy)]\npub enum Register {\n    EHT0 = 0x00,\n    EHT1 = 0x01,\n    EHT2 = 0x02,\n    EHT3 = 0x03,\n    EHT4 = 0x04,\n    EHT5 = 0x05,\n    EHT6 = 0x06,\n    EHT7 = 0x07,\n    EPMM0 = 0x08,\n    EPMM1 = 0x09,\n    EPMM2 = 0x0a,\n    EPMM3 = 0x0b,\n    EPMM4 = 0x0c,\n    EPMM5 = 0x0d,\n    EPMM6 = 0x0e,\n    EPMM7 = 0x0f,\n    EPMCSL = 0x10,\n    EPMCSH = 0x11,\n    EPMOL = 0x14,\n    EPMOH = 0x15,\n    ERXFCON = 0x18,\n    EPKTCNT = 0x19,\n}\n\nimpl Register {\n    pub(crate) fn addr(&self) -> u8 {\n        *self as u8\n    }\n\n    pub(crate) fn is_eth_register(&self) -> bool {\n        match *self {\n            Register::EHT0 => true,\n            Register::EHT1 => true,\n            Register::EHT2 => true,\n            Register::EHT3 => true,\n            Register::EHT4 => true,\n            Register::EHT5 => true,\n            Register::EHT6 => true,\n            Register::EHT7 => true,\n            Register::EPMM0 => true,\n            Register::EPMM1 => true,\n            Register::EPMM2 => true,\n            Register::EPMM3 => true,\n            Register::EPMM4 => true,\n            Register::EPMM5 => true,\n            Register::EPMM6 => true,\n            Register::EPMM7 => true,\n            Register::EPMCSL => true,\n            Register::EPMCSH => true,\n            Register::EPMOL => true,\n            Register::EPMOH => true,\n            Register::ERXFCON => true,\n            Register::EPKTCNT => true,\n        }\n    }\n}\n\nimpl Into<super::Register> for Register {\n    fn into(self) -> super::Register {\n        super::Register::Bank1(self)\n    }\n}\n\nregister!(ERXFCON, 0b1010_0001, u8, {\n    #[doc = \"Broadcast Filter Enable bit\"]\n    bcen @ 0,\n    #[doc = \"Multicast Filter Enable bit\"]\n    mcen @ 1,\n    #[doc = \"Hash Table Filter Enable bit\"]\n    hten @ 2,\n    #[doc = \"Magic Packet™ Filter Enable bit\"]\n    mpen @ 3,\n    #[doc = \"Pattern Match Filter Enable bit\"]\n    pmen @ 4,\n    #[doc = \"Post-Filter CRC Check Enable bit\"]\n    crcen @ 5,\n    #[doc = \"AND/OR Filter Select bit\"]\n    andor @ 6,\n    #[doc = \"Unicast Filter Enable bit\"]\n    ucen @ 7,\n});\n"
  },
  {
    "path": "embassy-net-enc28j60/src/bank2.rs",
    "content": "#[allow(dead_code)]\n#[derive(Clone, Copy)]\npub enum Register {\n    MACON1 = 0x00,\n    MACON3 = 0x02,\n    MACON4 = 0x03,\n    MABBIPG = 0x04,\n    MAIPGL = 0x06,\n    MAIPGH = 0x07,\n    MACLCON1 = 0x08,\n    MACLCON2 = 0x09,\n    MAMXFLL = 0x0a,\n    MAMXFLH = 0x0b,\n    MICMD = 0x12,\n    MIREGADR = 0x14,\n    MIWRL = 0x16,\n    MIWRH = 0x17,\n    MIRDL = 0x18,\n    MIRDH = 0x19,\n}\n\nimpl Register {\n    pub(crate) fn addr(&self) -> u8 {\n        *self as u8\n    }\n\n    pub(crate) fn is_eth_register(&self) -> bool {\n        match *self {\n            Register::MACON1 => false,\n            Register::MACON3 => false,\n            Register::MACON4 => false,\n            Register::MABBIPG => false,\n            Register::MAIPGL => false,\n            Register::MAIPGH => false,\n            Register::MACLCON1 => false,\n            Register::MACLCON2 => false,\n            Register::MAMXFLL => false,\n            Register::MAMXFLH => false,\n            Register::MICMD => false,\n            Register::MIREGADR => false,\n            Register::MIWRL => false,\n            Register::MIWRH => false,\n            Register::MIRDL => false,\n            Register::MIRDH => false,\n        }\n    }\n}\n\nimpl Into<super::Register> for Register {\n    fn into(self) -> super::Register {\n        super::Register::Bank2(self)\n    }\n}\n\nregister!(MACON1, 0, u8, {\n    #[doc = \"Enable packets to be received by the MAC\"]\n    marxen @ 0,\n    #[doc = \"Control frames will be discarded after being processed by the MAC\"]\n    passall @ 1,\n    #[doc = \"Inhibit transmissions when pause control frames are received\"]\n    rxpaus @ 2,\n    #[doc = \"Allow the MAC to transmit pause control frames\"]\n    txpaus @ 3,\n});\n\nregister!(MACON3, 0, u8, {\n    #[doc = \"MAC will operate in Full-Duplex mode\"]\n    fuldpx @ 0,\n    #[doc = \"The type/length field of transmitted and received frames will be checked\"]\n    frmlnen @ 1,\n    #[doc = \"Frames bigger than MAMXFL will be aborted when transmitted or received\"]\n    hfrmen @ 2,\n    #[doc = \"No proprietary header is present\"]\n    phdren @ 3,\n    #[doc = \"MAC will append a valid CRC to all frames transmitted regardless of PADCFG bit\"]\n    txcrcen @ 4,\n    #[doc = \"All short frames will be zero-padded to 64 bytes and a valid CRC will then be appended\"]\n    padcfg @ 5..7,\n});\n\nregister!(MICMD, 0, u8, {\n    #[doc = \"MII Read Enable bit\"]\n    miird @ 0,\n    #[doc = \"MII Scan Enable bit\"]\n    miiscan @ 1,\n});\n"
  },
  {
    "path": "embassy-net-enc28j60/src/bank3.rs",
    "content": "#[allow(dead_code)]\n#[derive(Clone, Copy)]\npub enum Register {\n    MAADR5 = 0x00,\n    MAADR6 = 0x01,\n    MAADR3 = 0x02,\n    MAADR4 = 0x03,\n    MAADR1 = 0x04,\n    MAADR2 = 0x05,\n    EBSTSD = 0x06,\n    EBSTCON = 0x07,\n    EBSTCSL = 0x08,\n    EBSTCSH = 0x09,\n    MISTAT = 0x0a,\n    EREVID = 0x12,\n    ECOCON = 0x15,\n    EFLOCON = 0x17,\n    EPAUSL = 0x18,\n    EPAUSH = 0x19,\n}\n\nimpl Register {\n    pub(crate) fn addr(&self) -> u8 {\n        *self as u8\n    }\n\n    pub(crate) fn is_eth_register(&self) -> bool {\n        match *self {\n            Register::MAADR5 => false,\n            Register::MAADR6 => false,\n            Register::MAADR3 => false,\n            Register::MAADR4 => false,\n            Register::MAADR1 => false,\n            Register::MAADR2 => false,\n            Register::EBSTSD => true,\n            Register::EBSTCON => true,\n            Register::EBSTCSL => true,\n            Register::EBSTCSH => true,\n            Register::MISTAT => false,\n            Register::EREVID => true,\n            Register::ECOCON => true,\n            Register::EFLOCON => true,\n            Register::EPAUSL => true,\n            Register::EPAUSH => true,\n        }\n    }\n}\n\nimpl Into<super::Register> for Register {\n    fn into(self) -> super::Register {\n        super::Register::Bank3(self)\n    }\n}\n"
  },
  {
    "path": "embassy-net-enc28j60/src/common.rs",
    "content": "#[allow(dead_code)]\n#[derive(Clone, Copy)]\npub enum Register {\n    ECON1 = 0x1f,\n    ECON2 = 0x1e,\n    EIE = 0x1b,\n    EIR = 0x1c,\n    ESTAT = 0x1d,\n}\n\nimpl Register {\n    pub(crate) fn addr(&self) -> u8 {\n        *self as u8\n    }\n\n    pub(crate) fn is_eth_register(&self) -> bool {\n        match *self {\n            Register::ECON1 => true,\n            Register::ECON2 => true,\n            Register::EIE => true,\n            Register::EIR => true,\n            Register::ESTAT => true,\n        }\n    }\n}\n\nimpl Into<super::Register> for Register {\n    fn into(self) -> super::Register {\n        super::Register::Common(self)\n    }\n}\n\nregister!(EIE, 0, u8, {\n    #[doc = \"Receive Error Interrupt Enable bit\"]\n    rxerie @ 0,\n    #[doc = \"Transmit Error Interrupt Enable bit\"]\n    txerie @ 1,\n    #[doc = \"Transmit Enable bit\"]\n    txie @ 3,\n    #[doc = \"Link Status Change Interrupt Enable bit\"]\n    linkie @ 4,\n    #[doc = \"DMA Interrupt Enable bit\"]\n    dmaie @ 5,\n    #[doc = \"Receive Packet Pending Interrupt Enable bit\"]\n    pktie @ 6,\n    #[doc = \"Global INT Interrupt Enable bit\"]\n    intie @ 7,\n});\n\nregister!(EIR, 0, u8, {\n    #[doc = \"Receive Error Interrupt Flag bit\"]\n    rxerif @ 0,\n    #[doc = \"Transmit Error Interrupt Flag bit\"]\n    txerif @ 1,\n    #[doc = \"Transmit Interrupt Flag bit\"]\n    txif @ 3,\n    #[doc = \"Link Change Interrupt Flag bit\"]\n    linkif @ 4,\n    #[doc = \"DMA Interrupt Flag bit\"]\n    dmaif @ 5,\n    #[doc = \"Receive Packet Pending Interrupt Flag bit\"]\n    pktif @ 6,\n});\n\nregister!(ESTAT, 0, u8, {\n    #[doc = \"Clock Ready bit\"]\n    clkrdy @ 0,\n    #[doc = \"Transmit Abort Error bit\"]\n    txabrt @ 1,\n    #[doc = \"Receive Busy bit\"]\n    rxbusy @ 2,\n    #[doc = \"Late Collision Error bit\"]\n    latecol @ 4,\n    #[doc = \"Ethernet Buffer Error Status bit\"]\n    bufer @ 6,\n    #[doc = \"INT Interrupt Flag bit\"]\n    int @ 7,\n});\n\nregister!(ECON2, 0b1000_0000, u8, {\n    #[doc = \"Voltage Regulator Power Save Enable bit\"]\n    vrps @ 3,\n    #[doc = \"Power Save Enable bit\"]\n    pwrsv @ 5,\n    #[doc = \"Packet Decrement bit\"]\n    pktdec @ 6,\n    #[doc = \"Automatic Buffer Pointer Increment Enable bit\"]\n    autoinc @ 7,\n});\n\nregister!(ECON1, 0, u8, {\n    #[doc = \"Bank Select bits\"]\n    bsel @ 0..1,\n    #[doc = \"Receive Enable bi\"]\n    rxen @ 2,\n    #[doc = \"Transmit Request to Send bit\"]\n    txrts @ 3,\n    #[doc = \"DMA Checksum Enable bit\"]\n    csumen @ 4,\n    #[doc = \"DMA Start and Busy Status bit\"]\n    dmast @ 5,\n    #[doc = \"Receive Logic Reset bit\"]\n    rxrst @ 6,\n    #[doc = \"Transmit Logic Reset bit\"]\n    txrst @ 7,\n});\n"
  },
  {
    "path": "embassy-net-enc28j60/src/fmt.rs",
    "content": "#![macro_use]\n#![allow(unused)]\n\nuse core::fmt::{Debug, Display, LowerHex};\n\n#[cfg(all(feature = \"defmt\", feature = \"log\"))]\ncompile_error!(\"You may not enable both `defmt` and `log` features.\");\n\n#[collapse_debuginfo(yes)]\nmacro_rules! assert {\n    ($($x:tt)*) => {\n        {\n            #[cfg(not(feature = \"defmt\"))]\n            ::core::assert!($($x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::assert!($($x)*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! assert_eq {\n    ($($x:tt)*) => {\n        {\n            #[cfg(not(feature = \"defmt\"))]\n            ::core::assert_eq!($($x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::assert_eq!($($x)*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! assert_ne {\n    ($($x:tt)*) => {\n        {\n            #[cfg(not(feature = \"defmt\"))]\n            ::core::assert_ne!($($x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::assert_ne!($($x)*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! debug_assert {\n    ($($x:tt)*) => {\n        {\n            #[cfg(not(feature = \"defmt\"))]\n            ::core::debug_assert!($($x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::debug_assert!($($x)*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! debug_assert_eq {\n    ($($x:tt)*) => {\n        {\n            #[cfg(not(feature = \"defmt\"))]\n            ::core::debug_assert_eq!($($x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::debug_assert_eq!($($x)*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! debug_assert_ne {\n    ($($x:tt)*) => {\n        {\n            #[cfg(not(feature = \"defmt\"))]\n            ::core::debug_assert_ne!($($x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::debug_assert_ne!($($x)*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! todo {\n    ($($x:tt)*) => {\n        {\n            #[cfg(not(feature = \"defmt\"))]\n            ::core::todo!($($x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::todo!($($x)*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! unreachable {\n    ($($x:tt)*) => {\n        {\n            #[cfg(not(feature = \"defmt\"))]\n            ::core::unreachable!($($x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::unreachable!($($x)*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! panic {\n    ($($x:tt)*) => {\n        {\n            #[cfg(not(feature = \"defmt\"))]\n            ::core::panic!($($x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::panic!($($x)*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! trace {\n    ($s:literal $(, $x:expr)* $(,)?) => {\n        {\n            #[cfg(feature = \"log\")]\n            ::log::trace!($s $(, $x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::trace!($s $(, $x)*);\n            #[cfg(not(any(feature = \"log\", feature=\"defmt\")))]\n            let _ = ($( & $x ),*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! debug {\n    ($s:literal $(, $x:expr)* $(,)?) => {\n        {\n            #[cfg(feature = \"log\")]\n            ::log::debug!($s $(, $x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::debug!($s $(, $x)*);\n            #[cfg(not(any(feature = \"log\", feature=\"defmt\")))]\n            let _ = ($( & $x ),*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! info {\n    ($s:literal $(, $x:expr)* $(,)?) => {\n        {\n            #[cfg(feature = \"log\")]\n            ::log::info!($s $(, $x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::info!($s $(, $x)*);\n            #[cfg(not(any(feature = \"log\", feature=\"defmt\")))]\n            let _ = ($( & $x ),*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! warn {\n    ($s:literal $(, $x:expr)* $(,)?) => {\n        {\n            #[cfg(feature = \"log\")]\n            ::log::warn!($s $(, $x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::warn!($s $(, $x)*);\n            #[cfg(not(any(feature = \"log\", feature=\"defmt\")))]\n            let _ = ($( & $x ),*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! error {\n    ($s:literal $(, $x:expr)* $(,)?) => {\n        {\n            #[cfg(feature = \"log\")]\n            ::log::error!($s $(, $x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::error!($s $(, $x)*);\n            #[cfg(not(any(feature = \"log\", feature=\"defmt\")))]\n            let _ = ($( & $x ),*);\n        }\n    };\n}\n\n#[cfg(feature = \"defmt\")]\n#[collapse_debuginfo(yes)]\nmacro_rules! unwrap {\n    ($($x:tt)*) => {\n        ::defmt::unwrap!($($x)*)\n    };\n}\n\n#[cfg(not(feature = \"defmt\"))]\n#[collapse_debuginfo(yes)]\nmacro_rules! unwrap {\n    ($arg:expr) => {\n        match $crate::fmt::Try::into_result($arg) {\n            ::core::result::Result::Ok(t) => t,\n            ::core::result::Result::Err(e) => {\n                ::core::panic!(\"unwrap of `{}` failed: {:?}\", ::core::stringify!($arg), e);\n            }\n        }\n    };\n    ($arg:expr, $($msg:expr),+ $(,)? ) => {\n        match $crate::fmt::Try::into_result($arg) {\n            ::core::result::Result::Ok(t) => t,\n            ::core::result::Result::Err(e) => {\n                ::core::panic!(\"unwrap of `{}` failed: {}: {:?}\", ::core::stringify!($arg), ::core::format_args!($($msg,)*), e);\n            }\n        }\n    }\n}\n\n#[derive(Debug, Copy, Clone, Eq, PartialEq)]\npub struct NoneError;\n\npub trait Try {\n    type Ok;\n    type Error;\n    fn into_result(self) -> Result<Self::Ok, Self::Error>;\n}\n\nimpl<T> Try for Option<T> {\n    type Ok = T;\n    type Error = NoneError;\n\n    #[inline]\n    fn into_result(self) -> Result<T, NoneError> {\n        self.ok_or(NoneError)\n    }\n}\n\nimpl<T, E> Try for Result<T, E> {\n    type Ok = T;\n    type Error = E;\n\n    #[inline]\n    fn into_result(self) -> Self {\n        self\n    }\n}\n\npub(crate) struct Bytes<'a>(pub &'a [u8]);\n\nimpl<'a> Debug for Bytes<'a> {\n    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {\n        write!(f, \"{:#02x?}\", self.0)\n    }\n}\n\nimpl<'a> Display for Bytes<'a> {\n    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {\n        write!(f, \"{:#02x?}\", self.0)\n    }\n}\n\nimpl<'a> LowerHex for Bytes<'a> {\n    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {\n        write!(f, \"{:#02x?}\", self.0)\n    }\n}\n\n#[cfg(feature = \"defmt\")]\nimpl<'a> defmt::Format for Bytes<'a> {\n    fn format(&self, fmt: defmt::Formatter) {\n        defmt::write!(fmt, \"{:02x}\", self.0)\n    }\n}\n"
  },
  {
    "path": "embassy-net-enc28j60/src/header.rs",
    "content": "register!(RxStatus, 0, u32, {\n    #[doc = \"Indicates length of the received frame\"]\n    byte_count @ 0..15,\n    #[doc = \"Indicates a packet over 50,000 bit times occurred or that a packet was dropped since the last receive\"]\n    long_event @ 16,\n    #[doc = \"Indicates that at some time since the last receive, a carrier event was detected\"]\n    carrier_event @ 18,\n    #[doc = \"Indicates that frame CRC field value does not match the CRC calculated by the MAC\"]\n    crc_error @ 20,\n    #[doc = \"Indicates that frame length field value in the packet does not match the actual data byte length and specifies a valid length\"]\n    length_check_error @ 21,\n    #[doc = \"Indicates that frame type/length field was larger than 1500 bytes (type field)\"]\n    length_out_of_range @ 22,\n    #[doc = \"Indicates that at the packet had a valid CRC and no symbol errors\"]\n    received_ok @ 23,\n    #[doc = \"Indicates packet received had a valid Multicast address\"]\n    multicast @ 24,\n    #[doc = \"Indicates packet received had a valid Broadcast address.\"]\n    broadcast @ 25,\n    #[doc = \"Indicates that after the end of this packet, an additional 1 to 7 bits were received\"]\n    dribble_nibble @ 26,\n    #[doc = \"Current frame was recognized as a control frame for having a valid type/length designating it as a control frame\"]\n    receive_control_frame @ 27,\n    #[doc = \"Current frame was recognized as a control frame containing a valid pause frame opcode and a valid destination address\"]\n    receive_pause_control_frame @ 28,\n    #[doc = \"Current frame was recognized as a control frame but it contained an unknown opcode\"]\n    receive_unknown_opcode @ 29,\n    #[doc = \"Current frame was recognized as a VLAN tagged frame\"]\n    receive_vlan_type_detected @ 30,\n});\n"
  },
  {
    "path": "embassy-net-enc28j60/src/lib.rs",
    "content": "#![no_std]\n#![doc = include_str!(\"../README.md\")]\n#![warn(missing_docs)]\n\n// must go first.\nmod fmt;\n\n#[macro_use]\nmod macros;\nmod bank0;\nmod bank1;\nmod bank2;\nmod bank3;\nmod common;\nmod header;\nmod phy;\nmod traits;\n\nuse core::cmp;\n\nuse embassy_net_driver::{Capabilities, HardwareAddress, LinkState};\nuse embassy_time::Duration;\nuse embedded_hal::digital::OutputPin;\nuse embedded_hal::spi::{Operation, SpiDevice};\nuse traits::U16Ext;\n\n// Total buffer size (see section 3.2)\nconst BUF_SZ: u16 = 8 * 1024;\n\n// Maximum frame length\nconst MAX_FRAME_LENGTH: u16 = 1518; // value recommended in the data sheet\n\n// Size of the Frame check sequence (32-bit CRC)\nconst CRC_SZ: u16 = 4;\n\n// define the boundaries of the TX and RX buffers\n// to workaround errata #5 we do the opposite of what section 6.1 of the data sheet\n// says: we place the RX buffer at address 0 and the TX buffer after it\nconst RXST: u16 = 0x0000;\nconst RXND: u16 = 0x19ff;\nconst TXST: u16 = 0x1a00;\nconst _TXND: u16 = 0x1fff;\n\nconst MTU: usize = 1514; // 1500 IP + 14 ethernet header\n\n/// ENC28J60 embassy-net driver\npub struct Enc28j60<S, O> {\n    mac_addr: [u8; 6],\n\n    spi: S,\n    rst: Option<O>,\n\n    bank: Bank,\n\n    // address of the next packet in buffer memory\n    next_packet: u16,\n}\n\nimpl<S, O> Enc28j60<S, O>\nwhere\n    S: SpiDevice,\n    O: OutputPin,\n{\n    /// Create a new ENC28J60 driver instance.\n    ///\n    /// The RST pin is optional. If None, reset will be done with a SPI\n    /// soft reset command, instead of via the RST pin.\n    pub fn new(spi: S, rst: Option<O>, mac_addr: [u8; 6]) -> Self {\n        let mut res = Self {\n            mac_addr,\n            spi,\n            rst,\n\n            bank: Bank::Bank0,\n            next_packet: RXST,\n        };\n        res.init();\n        res\n    }\n\n    fn init(&mut self) {\n        if let Some(rst) = &mut self.rst {\n            rst.set_low().unwrap();\n            embassy_time::block_for(Duration::from_millis(5));\n            rst.set_high().unwrap();\n            embassy_time::block_for(Duration::from_millis(5));\n        } else {\n            embassy_time::block_for(Duration::from_millis(5));\n            self.soft_reset();\n            embassy_time::block_for(Duration::from_millis(5));\n        }\n\n        debug!(\n            \"enc28j60: erevid {=u8:x}\",\n            self.read_control_register(bank3::Register::EREVID)\n        );\n        debug!(\"enc28j60: waiting for clk\");\n        while common::ESTAT(self.read_control_register(common::Register::ESTAT)).clkrdy() == 0 {}\n        debug!(\"enc28j60: clk ok\");\n\n        if self.read_control_register(bank3::Register::EREVID) == 0 {\n            panic!(\"ErevidIsZero\");\n        }\n\n        // disable CLKOUT output\n        self.write_control_register(bank3::Register::ECOCON, 0);\n\n        self.init_rx();\n\n        // TX start\n        // \"It is recommended that an even address be used for ETXST\"\n        debug_assert_eq!(TXST % 2, 0);\n        self.write_control_register(bank0::Register::ETXSTL, TXST.low());\n        self.write_control_register(bank0::Register::ETXSTH, TXST.high());\n\n        // TX end is set in `transmit`\n\n        // MAC initialization (see section 6.5)\n        // 1. Set the MARXEN bit in MACON1 to enable the MAC to receive frames.\n        self.write_control_register(\n            bank2::Register::MACON1,\n            bank2::MACON1::default().marxen(1).passall(0).rxpaus(1).txpaus(1).bits(),\n        );\n\n        // 2. Configure the PADCFG, TXCRCEN and FULDPX bits of MACON3.\n        self.write_control_register(\n            bank2::Register::MACON3,\n            bank2::MACON3::default().frmlnen(1).txcrcen(1).padcfg(0b001).bits(),\n        );\n\n        // 4. Program the MAMXFL registers with the maximum frame length to be permitted to be\n        // received or transmitted\n        self.write_control_register(bank2::Register::MAMXFLL, MAX_FRAME_LENGTH.low());\n        self.write_control_register(bank2::Register::MAMXFLH, MAX_FRAME_LENGTH.high());\n\n        // 5. Configure the Back-to-Back Inter-Packet Gap register, MABBIPG.\n        // Use recommended value of 0x12\n        self.write_control_register(bank2::Register::MABBIPG, 0x12);\n\n        // 6. Configure the Non-Back-to-Back Inter-Packet Gap register low byte, MAIPGL.\n        // Use recommended value of 0x12\n        self.write_control_register(bank2::Register::MAIPGL, 0x12);\n        self.write_control_register(bank2::Register::MAIPGH, 0x0c);\n\n        // 9. Program the local MAC address into the MAADR1:MAADR6 registers\n        self.write_control_register(bank3::Register::MAADR1, self.mac_addr[0]);\n        self.write_control_register(bank3::Register::MAADR2, self.mac_addr[1]);\n        self.write_control_register(bank3::Register::MAADR3, self.mac_addr[2]);\n        self.write_control_register(bank3::Register::MAADR4, self.mac_addr[3]);\n        self.write_control_register(bank3::Register::MAADR5, self.mac_addr[4]);\n        self.write_control_register(bank3::Register::MAADR6, self.mac_addr[5]);\n\n        // Set the PHCON2.HDLDIS bit to prevent automatic loopback of the data which is transmitted\n        self.write_phy_register(phy::Register::PHCON2, phy::PHCON2::default().hdldis(1).bits());\n\n        // Globally enable interrupts\n        //self.bit_field_set(common::Register::EIE, common::EIE::mask().intie());\n\n        // Set the per packet control byte; we'll always use the value 0\n        self.write_buffer_memory(Some(TXST), &mut [0]);\n\n        // Enable reception\n        self.bit_field_set(common::Register::ECON1, common::ECON1::mask().rxen());\n    }\n\n    fn init_rx(&mut self) {\n        // RX start\n        // \"It is recommended that the ERXST Pointer be programmed with an even address\"\n        self.write_control_register(bank0::Register::ERXSTL, RXST.low());\n        self.write_control_register(bank0::Register::ERXSTH, RXST.high());\n\n        // RX read pointer\n        // NOTE Errata #14 so we are using an *odd* address here instead of ERXST\n        self.write_control_register(bank0::Register::ERXRDPTL, RXND.low());\n        self.write_control_register(bank0::Register::ERXRDPTH, RXND.high());\n\n        // RX end\n        self.write_control_register(bank0::Register::ERXNDL, RXND.low());\n        self.write_control_register(bank0::Register::ERXNDH, RXND.high());\n\n        // decrease the packet count to 0\n        while self.read_control_register(bank1::Register::EPKTCNT) != 0 {\n            self.bit_field_set(common::Register::ECON2, common::ECON2::mask().pktdec());\n        }\n\n        self.next_packet = RXST;\n    }\n\n    fn reset_rx(&mut self) {\n        self.bit_field_set(common::Register::ECON1, common::ECON1::mask().rxrst());\n        self.bit_field_clear(common::Register::ECON1, common::ECON1::mask().rxrst());\n        self.init_rx();\n        self.bit_field_set(common::Register::ECON1, common::ECON1::mask().rxen());\n    }\n\n    /// Returns the device's MAC address\n    pub fn address(&self) -> [u8; 6] {\n        self.mac_addr\n    }\n\n    /// Flushes the transmit buffer, ensuring all pending transmissions have completed\n    /// NOTE: The returned packet *must* be `read` or `ignore`-d, otherwise this method will always\n    /// return `None` on subsequent invocations\n    pub fn receive(&mut self, buf: &mut [u8]) -> Option<usize> {\n        if self.pending_packets() == 0 {\n            // Errata #6: we can't rely on PKTIF so we check PKTCNT\n            return None;\n        }\n\n        let curr_packet = self.next_packet;\n\n        // read out the first 6 bytes\n        let mut temp_buf = [0; 6];\n        self.read_buffer_memory(Some(curr_packet), &mut temp_buf);\n\n        // next packet pointer\n        let next_packet = u16::from_parts(temp_buf[0], temp_buf[1]);\n        // status vector\n        let status = header::RxStatus(u32::from_le_bytes(temp_buf[2..].try_into().unwrap()));\n        let len_with_crc = status.byte_count() as u16;\n\n        if len_with_crc < CRC_SZ || len_with_crc > 1600 || next_packet > RXND {\n            warn!(\"RX buffer corrupted, resetting RX logic to recover...\");\n            self.reset_rx();\n            return None;\n        }\n\n        let len = len_with_crc - CRC_SZ;\n        self.read_buffer_memory(None, &mut buf[..len as usize]);\n\n        // update ERXRDPT\n        // due to Errata #14 we must write an odd address to ERXRDPT\n        // we know that ERXST = 0, that ERXND is odd and that next_packet is even\n        let rxrdpt = if self.next_packet < 1 || self.next_packet > RXND + 1 {\n            RXND\n        } else {\n            self.next_packet - 1\n        };\n        // \"To move ERXRDPT, the host controller must write to ERXRDPTL first.\"\n        self.write_control_register(bank0::Register::ERXRDPTL, rxrdpt.low());\n        self.write_control_register(bank0::Register::ERXRDPTH, rxrdpt.high());\n\n        // decrease the packet count\n        self.bit_field_set(common::Register::ECON2, common::ECON2::mask().pktdec());\n\n        self.next_packet = next_packet;\n\n        Some(len as usize)\n    }\n\n    fn wait_tx_ready(&mut self) {\n        for _ in 0u32..10000 {\n            if common::ECON1(self.read_control_register(common::Register::ECON1)).txrts() == 0 {\n                return;\n            }\n        }\n\n        // work around errata #12 by resetting the transmit logic before every new\n        // transmission\n        self.bit_field_set(common::Register::ECON1, common::ECON1::mask().txrst());\n        self.bit_field_clear(common::Register::ECON1, common::ECON1::mask().txrst());\n        //self.bit_field_clear(common::Register::EIR, {\n        //    let mask = common::EIR::mask();\n        //    mask.txerif() | mask.txif()\n        //});\n    }\n\n    /// Starts the transmission of `bytes`\n    ///\n    /// It's up to the caller to ensure that `bytes` is a valid Ethernet frame. The interface will\n    /// take care of appending a (4 byte) CRC to the frame and of padding the frame to the minimum\n    /// size allowed by the Ethernet specification (64 bytes, or 46 bytes of payload).\n    ///\n    /// NOTE This method will flush any previous transmission that's in progress\n    ///\n    /// # Panics\n    ///\n    /// If `bytes` length is greater than 1514, the maximum frame length allowed by the interface,\n    /// or greater than the transmit buffer\n    pub fn transmit(&mut self, bytes: &[u8]) {\n        assert!(bytes.len() <= self.mtu() as usize);\n\n        self.wait_tx_ready();\n\n        // NOTE the plus one is to not overwrite the per packet control byte\n        let wrpt = TXST + 1;\n\n        // 1. ETXST was set during initialization\n\n        // 2. write the frame to the IC memory\n        self.write_buffer_memory(Some(wrpt), bytes);\n\n        let txnd = wrpt + bytes.len() as u16 - 1;\n\n        // 3. Set the end address of the transmit buffer\n        self.write_control_register(bank0::Register::ETXNDL, txnd.low());\n        self.write_control_register(bank0::Register::ETXNDH, txnd.high());\n\n        // 4. reset interrupt flag\n        //self.bit_field_clear(common::Register::EIR, common::EIR::mask().txif());\n\n        // 5. start transmission\n        self.bit_field_set(common::Register::ECON1, common::ECON1::mask().txrts());\n\n        // Wait until transmission finishes\n        //while common::ECON1(self.read_control_register(common::Register::ECON1)).txrts() == 1 {}\n\n        /*\n        // read the transmit status vector\n        let mut tx_stat = [0; 7];\n        self.read_buffer_memory(None, &mut tx_stat);\n\n        let stat = common::ESTAT(self.read_control_register(common::Register::ESTAT));\n\n        if stat.txabrt() == 1 {\n            // work around errata #12 by reading the transmit status vector\n            if stat.latecol() == 1 || (tx_stat[2] & (1 << 5)) != 0 {\n                panic!(\"LateCollision\")\n            } else {\n                panic!(\"TransmitAbort\")\n            }\n        }*/\n    }\n\n    /// Get whether the link is up\n    pub fn is_link_up(&mut self) -> bool {\n        let bits = self.read_phy_register(phy::Register::PHSTAT2);\n        phy::PHSTAT2(bits).lstat() == 1\n    }\n\n    /// Returns the interface Maximum Transmission Unit (MTU)\n    ///\n    /// The value returned by this function will never exceed 1514 bytes. The actual value depends\n    /// on the memory assigned to the transmission buffer when initializing the device\n    pub fn mtu(&self) -> u16 {\n        cmp::min(BUF_SZ - RXND - 1, MAX_FRAME_LENGTH - CRC_SZ)\n    }\n\n    /* Miscellaneous */\n    /// Returns the number of packets that have been received but have not been processed yet\n    pub fn pending_packets(&mut self) -> u8 {\n        self.read_control_register(bank1::Register::EPKTCNT)\n    }\n\n    /// Adjusts the receive filter to *accept* these packet types\n    pub fn accept(&mut self, packets: &[Packet]) {\n        let mask = bank1::ERXFCON::mask();\n        let mut val = 0;\n        for packet in packets {\n            match packet {\n                Packet::Broadcast => val |= mask.bcen(),\n                Packet::Multicast => val |= mask.mcen(),\n                Packet::Unicast => val |= mask.ucen(),\n            }\n        }\n\n        self.bit_field_set(bank1::Register::ERXFCON, val)\n    }\n\n    /// Adjusts the receive filter to *ignore* these packet types\n    pub fn ignore(&mut self, packets: &[Packet]) {\n        let mask = bank1::ERXFCON::mask();\n        let mut val = 0;\n        for packet in packets {\n            match packet {\n                Packet::Broadcast => val |= mask.bcen(),\n                Packet::Multicast => val |= mask.mcen(),\n                Packet::Unicast => val |= mask.ucen(),\n            }\n        }\n\n        self.bit_field_clear(bank1::Register::ERXFCON, val)\n    }\n\n    /* Private */\n    /* Read */\n    fn read_control_register<R>(&mut self, register: R) -> u8\n    where\n        R: Into<Register>,\n    {\n        self._read_control_register(register.into())\n    }\n\n    fn _read_control_register(&mut self, register: Register) -> u8 {\n        self.change_bank(register);\n\n        if register.is_eth_register() {\n            let mut buffer = [Instruction::RCR.opcode() | register.addr(), 0];\n            self.spi.transfer_in_place(&mut buffer).unwrap();\n            buffer[1]\n        } else {\n            // MAC, MII regs need a dummy byte.\n            let mut buffer = [Instruction::RCR.opcode() | register.addr(), 0, 0];\n            self.spi.transfer_in_place(&mut buffer).unwrap();\n            buffer[2]\n        }\n    }\n\n    fn read_phy_register(&mut self, register: phy::Register) -> u16 {\n        // set PHY register address\n        self.write_control_register(bank2::Register::MIREGADR, register.addr());\n\n        // start read operation\n        self.write_control_register(bank2::Register::MICMD, bank2::MICMD::default().miird(1).bits());\n\n        // wait until the read operation finishes\n        while self.read_control_register(bank3::Register::MISTAT) & 0b1 != 0 {}\n\n        self.write_control_register(bank2::Register::MICMD, bank2::MICMD::default().miird(0).bits());\n\n        let l = self.read_control_register(bank2::Register::MIRDL);\n        let h = self.read_control_register(bank2::Register::MIRDH);\n        (l as u16) | (h as u16) << 8\n    }\n\n    /* Write */\n    fn _write_control_register(&mut self, register: Register, value: u8) {\n        self.change_bank(register);\n\n        let buffer = [Instruction::WCR.opcode() | register.addr(), value];\n        self.spi.write(&buffer).unwrap();\n    }\n\n    fn write_control_register<R>(&mut self, register: R, value: u8)\n    where\n        R: Into<Register>,\n    {\n        self._write_control_register(register.into(), value)\n    }\n\n    fn write_phy_register(&mut self, register: phy::Register, value: u16) {\n        // set PHY register address\n        self.write_control_register(bank2::Register::MIREGADR, register.addr());\n\n        self.write_control_register(bank2::Register::MIWRL, (value & 0xff) as u8);\n        // this starts the write operation\n        self.write_control_register(bank2::Register::MIWRH, (value >> 8) as u8);\n\n        // wait until the write operation finishes\n        while self.read_control_register(bank3::Register::MISTAT) & 0b1 != 0 {}\n    }\n\n    /* RMW */\n    fn modify_control_register<R, F>(&mut self, register: R, f: F)\n    where\n        F: FnOnce(u8) -> u8,\n        R: Into<Register>,\n    {\n        self._modify_control_register(register.into(), f)\n    }\n\n    fn _modify_control_register<F>(&mut self, register: Register, f: F)\n    where\n        F: FnOnce(u8) -> u8,\n    {\n        let r = self._read_control_register(register);\n        self._write_control_register(register, f(r))\n    }\n\n    /* Auxiliary */\n    fn change_bank(&mut self, register: Register) {\n        let bank = register.bank();\n\n        if let Some(bank) = bank {\n            if self.bank == bank {\n                // already on the register bank\n                return;\n            }\n\n            // change bank\n            self.bank = bank;\n            match bank {\n                Bank::Bank0 => self.bit_field_clear(common::Register::ECON1, 0b11),\n                Bank::Bank1 => self.modify_control_register(common::Register::ECON1, |r| (r & !0b11) | 0b01),\n                Bank::Bank2 => self.modify_control_register(common::Register::ECON1, |r| (r & !0b11) | 0b10),\n                Bank::Bank3 => self.bit_field_set(common::Register::ECON1, 0b11),\n            }\n        } else {\n            // common register\n        }\n    }\n\n    /* Primitive operations */\n    fn bit_field_clear<R>(&mut self, register: R, mask: u8)\n    where\n        R: Into<Register>,\n    {\n        self._bit_field_clear(register.into(), mask)\n    }\n\n    fn _bit_field_clear(&mut self, register: Register, mask: u8) {\n        debug_assert!(register.is_eth_register());\n\n        self.change_bank(register);\n\n        self.spi\n            .write(&[Instruction::BFC.opcode() | register.addr(), mask])\n            .unwrap();\n    }\n\n    fn bit_field_set<R>(&mut self, register: R, mask: u8)\n    where\n        R: Into<Register>,\n    {\n        self._bit_field_set(register.into(), mask)\n    }\n\n    fn _bit_field_set(&mut self, register: Register, mask: u8) {\n        debug_assert!(register.is_eth_register());\n\n        self.change_bank(register);\n\n        self.spi\n            .write(&[Instruction::BFS.opcode() | register.addr(), mask])\n            .unwrap();\n    }\n\n    fn read_buffer_memory(&mut self, addr: Option<u16>, buf: &mut [u8]) {\n        if let Some(addr) = addr {\n            self.write_control_register(bank0::Register::ERDPTL, addr.low());\n            self.write_control_register(bank0::Register::ERDPTH, addr.high());\n        }\n\n        self.spi\n            .transaction(&mut [Operation::Write(&[Instruction::RBM.opcode()]), Operation::Read(buf)])\n            .unwrap();\n    }\n\n    fn soft_reset(&mut self) {\n        self.spi.write(&[Instruction::SRC.opcode()]).unwrap();\n    }\n\n    fn write_buffer_memory(&mut self, addr: Option<u16>, buffer: &[u8]) {\n        if let Some(addr) = addr {\n            self.write_control_register(bank0::Register::EWRPTL, addr.low());\n            self.write_control_register(bank0::Register::EWRPTH, addr.high());\n        }\n\n        self.spi\n            .transaction(&mut [Operation::Write(&[Instruction::WBM.opcode()]), Operation::Write(buffer)])\n            .unwrap();\n    }\n}\n\n#[derive(Clone, Copy, PartialEq)]\nenum Bank {\n    Bank0,\n    Bank1,\n    Bank2,\n    Bank3,\n}\n\n#[derive(Clone, Copy)]\nenum Instruction {\n    /// Read Control Register\n    RCR = 0b000_00000,\n    /// Read Buffer Memory\n    RBM = 0b001_11010,\n    /// Write Control Register\n    WCR = 0b010_00000,\n    /// Write Buffer Memory\n    WBM = 0b011_11010,\n    /// Bit Field Set\n    BFS = 0b100_00000,\n    /// Bit Field Clear\n    BFC = 0b101_00000,\n    /// System Reset Command\n    SRC = 0b111_11111,\n}\n\nimpl Instruction {\n    fn opcode(&self) -> u8 {\n        *self as u8\n    }\n}\n\n#[derive(Clone, Copy)]\nenum Register {\n    Bank0(bank0::Register),\n    Bank1(bank1::Register),\n    Bank2(bank2::Register),\n    Bank3(bank3::Register),\n    Common(common::Register),\n}\n\nimpl Register {\n    fn addr(&self) -> u8 {\n        match *self {\n            Register::Bank0(r) => r.addr(),\n            Register::Bank1(r) => r.addr(),\n            Register::Bank2(r) => r.addr(),\n            Register::Bank3(r) => r.addr(),\n            Register::Common(r) => r.addr(),\n        }\n    }\n\n    fn bank(&self) -> Option<Bank> {\n        Some(match *self {\n            Register::Bank0(_) => Bank::Bank0,\n            Register::Bank1(_) => Bank::Bank1,\n            Register::Bank2(_) => Bank::Bank2,\n            Register::Bank3(_) => Bank::Bank3,\n            Register::Common(_) => return None,\n        })\n    }\n\n    fn is_eth_register(&self) -> bool {\n        match *self {\n            Register::Bank0(r) => r.is_eth_register(),\n            Register::Bank1(r) => r.is_eth_register(),\n            Register::Bank2(r) => r.is_eth_register(),\n            Register::Bank3(r) => r.is_eth_register(),\n            Register::Common(r) => r.is_eth_register(),\n        }\n    }\n}\n\n/// Packet type, used to configure receive filters\n#[non_exhaustive]\n#[derive(Clone, Copy, Eq, PartialEq)]\npub enum Packet {\n    /// Broadcast packets\n    Broadcast,\n    /// Multicast packets\n    Multicast,\n    /// Unicast packets\n    Unicast,\n}\n\nstatic mut TX_BUF: [u8; MTU] = [0; MTU];\nstatic mut RX_BUF: [u8; MTU] = [0; MTU];\n\nimpl<S, O> embassy_net_driver::Driver for Enc28j60<S, O>\nwhere\n    S: SpiDevice,\n    O: OutputPin,\n{\n    type RxToken<'a>\n        = RxToken<'a>\n    where\n        Self: 'a;\n\n    type TxToken<'a>\n        = TxToken<'a, S, O>\n    where\n        Self: 'a;\n\n    fn receive(&mut self, cx: &mut core::task::Context) -> Option<(Self::RxToken<'_>, Self::TxToken<'_>)> {\n        let rx_buf = unsafe { &mut *core::ptr::addr_of_mut!(RX_BUF) };\n        let tx_buf = unsafe { &mut *core::ptr::addr_of_mut!(TX_BUF) };\n        if let Some(n) = self.receive(rx_buf) {\n            Some((RxToken { buf: &mut rx_buf[..n] }, TxToken { buf: tx_buf, eth: self }))\n        } else {\n            cx.waker().wake_by_ref();\n            None\n        }\n    }\n\n    fn transmit(&mut self, _cx: &mut core::task::Context) -> Option<Self::TxToken<'_>> {\n        let tx_buf = unsafe { &mut *core::ptr::addr_of_mut!(TX_BUF) };\n        Some(TxToken { buf: tx_buf, eth: self })\n    }\n\n    fn link_state(&mut self, cx: &mut core::task::Context) -> LinkState {\n        cx.waker().wake_by_ref();\n        match self.is_link_up() {\n            true => LinkState::Up,\n            false => LinkState::Down,\n        }\n    }\n\n    fn capabilities(&self) -> Capabilities {\n        let mut caps = Capabilities::default();\n        caps.max_transmission_unit = MTU;\n        caps\n    }\n\n    fn hardware_address(&self) -> HardwareAddress {\n        HardwareAddress::Ethernet(self.mac_addr)\n    }\n}\n\n/// embassy-net RX token.\npub struct RxToken<'a> {\n    buf: &'a mut [u8],\n}\n\nimpl<'a> embassy_net_driver::RxToken for RxToken<'a> {\n    fn consume<R, F>(self, f: F) -> R\n    where\n        F: FnOnce(&mut [u8]) -> R,\n    {\n        f(self.buf)\n    }\n}\n\n/// embassy-net TX token.\npub struct TxToken<'a, S, O>\nwhere\n    S: SpiDevice,\n    O: OutputPin,\n{\n    eth: &'a mut Enc28j60<S, O>,\n    buf: &'a mut [u8],\n}\n\nimpl<'a, S, O> embassy_net_driver::TxToken for TxToken<'a, S, O>\nwhere\n    S: SpiDevice,\n    O: OutputPin,\n{\n    fn consume<R, F>(self, len: usize, f: F) -> R\n    where\n        F: FnOnce(&mut [u8]) -> R,\n    {\n        assert!(len <= self.buf.len());\n        let r = f(&mut self.buf[..len]);\n        self.eth.transmit(&self.buf[..len]);\n        r\n    }\n}\n"
  },
  {
    "path": "embassy-net-enc28j60/src/macros.rs",
    "content": "macro_rules! register {\n    ($REGISTER:ident, $reset_value:expr, $uxx:ty, {\n        $(#[$($attr:tt)*] $bitfield:ident @ $range:expr,)+\n    }) => {\n        #[derive(Clone, Copy)]\n        pub(crate) struct $REGISTER<MODE> {\n            bits: $uxx,\n            _mode: ::core::marker::PhantomData<MODE>,\n        }\n\n        impl $REGISTER<super::traits::Mask> {\n            #[allow(dead_code)]\n            pub(crate) fn mask() -> $REGISTER<super::traits::Mask> {\n                $REGISTER { bits: 0, _mode: ::core::marker::PhantomData }\n            }\n\n            $(\n                #[allow(dead_code)]\n                pub(crate) fn $bitfield(&self) -> $uxx {\n                    use super::traits::OffsetSize;\n\n                    let size = $range.size();\n                    let offset = $range.offset();\n                    ((1 << size) - 1) << offset\n                }\n            )+\n        }\n\n        impl ::core::default::Default for $REGISTER<super::traits::W> {\n            fn default() -> Self {\n                $REGISTER { bits: $reset_value, _mode: ::core::marker::PhantomData }\n            }\n        }\n\n        #[allow(non_snake_case)]\n        #[allow(dead_code)]\n        pub(crate) fn $REGISTER(bits: $uxx) -> $REGISTER<super::traits::R> {\n            $REGISTER { bits, _mode: ::core::marker::PhantomData }\n        }\n\n        impl $REGISTER<super::traits::R> {\n            #[allow(dead_code)]\n            pub(crate) fn modify(self) -> $REGISTER<super::traits::W> {\n                $REGISTER { bits: self.bits, _mode: ::core::marker::PhantomData }\n            }\n\n            $(\n                #[$($attr)*]\n                #[allow(dead_code)]\n                pub(crate) fn $bitfield(&self) -> $uxx {\n                    use super::traits::OffsetSize;\n\n                    let offset = $range.offset();\n                    let size = $range.size();\n                    let mask = (1 << size) - 1;\n\n                    (self.bits >> offset) & mask\n                }\n            )+\n        }\n\n        impl $REGISTER<super::traits::W> {\n            #[allow(dead_code)]\n            pub(crate) fn bits(self) -> $uxx {\n                self.bits\n            }\n\n            $(\n                #[$($attr)*]\n                #[allow(dead_code)]\n                pub(crate) fn $bitfield(&mut self, mut bits: $uxx) -> &mut Self {\n                    use super::traits::OffsetSize;\n\n                    let offset = $range.offset();\n                    let size = $range.size();\n                    let mask = (1 << size) - 1;\n\n                    debug_assert!(bits <= mask);\n                    bits &= mask;\n\n                    self.bits &= !(mask << offset);\n                    self.bits |= bits << offset;\n\n                    self\n                }\n            )+\n        }\n    }\n}\n"
  },
  {
    "path": "embassy-net-enc28j60/src/phy.rs",
    "content": "#[allow(dead_code)]\n#[derive(Clone, Copy)]\npub enum Register {\n    PHCON1 = 0x00,\n    PHSTAT1 = 0x01,\n    PHID1 = 0x02,\n    PHID2 = 0x03,\n    PHCON2 = 0x10,\n    PHSTAT2 = 0x11,\n    PHIE = 0x12,\n    PHIR = 0x13,\n    PHLCON = 0x14,\n}\n\nimpl Register {\n    pub(crate) fn addr(&self) -> u8 {\n        *self as u8\n    }\n}\n\nregister!(PHCON2, 0, u16, {\n    #[doc = \"PHY Half-Duplex Loopback Disable bit\"]\n    hdldis @ 8,\n    #[doc = \"Jabber Correction Disable bit\"]\n    jabber @ 10,\n    #[doc = \"Twisted-Pair Transmitter Disable bit\"]\n    txdis @ 13,\n    #[doc = \"PHY Force Linkup bit\"]\n    frclnk @ 14,\n});\n\nregister!(PHSTAT2, 0, u16, {\n    #[doc = \"Link Status bit\"]\n    lstat @ 10,\n});\n"
  },
  {
    "path": "embassy-net-enc28j60/src/traits.rs",
    "content": "use core::ops::Range;\n\npub(crate) trait OffsetSize {\n    fn offset(self) -> u8;\n    fn size(self) -> u8;\n}\n\nimpl OffsetSize for u8 {\n    fn offset(self) -> u8 {\n        self\n    }\n\n    fn size(self) -> u8 {\n        1\n    }\n}\n\nimpl OffsetSize for Range<u8> {\n    fn offset(self) -> u8 {\n        self.start\n    }\n\n    fn size(self) -> u8 {\n        self.end - self.start\n    }\n}\n\npub(crate) trait U16Ext {\n    fn from_parts(low: u8, high: u8) -> Self;\n\n    fn low(self) -> u8;\n\n    fn high(self) -> u8;\n}\n\nimpl U16Ext for u16 {\n    fn from_parts(low: u8, high: u8) -> u16 {\n        ((high as u16) << 8) + low as u16\n    }\n\n    fn low(self) -> u8 {\n        (self & 0xff) as u8\n    }\n\n    fn high(self) -> u8 {\n        (self >> 8) as u8\n    }\n}\n\n#[derive(Clone, Copy)]\npub struct Mask;\n\n#[derive(Clone, Copy)]\npub struct R;\n\n#[derive(Clone, Copy)]\npub struct W;\n"
  },
  {
    "path": "embassy-net-esp-hosted/CHANGELOG.md",
    "content": "# Changelog\n\nAll notable changes to this project will be documented in this file.\n\nThe format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),\nand this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).\n\n<!-- next-header -->\n## Unreleased - ReleaseDate\n\n## 0.3.0 - 2026-03-10\n\n- Add an `Interface` trait to allow using other interface transports.\n- Switch to `micropb` for protobuf.\n- Update protos to latest `esp-hosted-fg`.\n- Add support for OTA firmware updates.\n- Update embassy-net-driver-channel to 0.4.0\n- Update embassy-sync to 0.8.0\n\n## 0.2.1 - 2025-08-26\n\n- First release with changelog.\n"
  },
  {
    "path": "embassy-net-esp-hosted/Cargo.toml",
    "content": "[package]\nname = \"embassy-net-esp-hosted\"\nversion = \"0.3.0\"\nedition = \"2024\"\ndescription = \"embassy-net driver for ESP-Hosted\"\nkeywords = [\"embedded\", \"esp-hosted\", \"embassy-net\", \"embedded-hal-async\", \"wifi\"]\ncategories = [\"embedded\", \"hardware-support\", \"no-std\", \"network-programming\", \"asynchronous\"]\nlicense = \"MIT OR Apache-2.0\"\nrepository = \"https://github.com/embassy-rs/embassy\"\ndocumentation = \"https://docs.embassy.dev/embassy-net-esp-hosted\"\n\n[features]\ndefmt = [\"dep:defmt\", \"heapless/defmt\"]\nlog = [\"dep:log\"]\n\n[dependencies]\ndefmt = { version = \"1.0.1\", optional = true }\nlog = { version = \"0.4.14\", optional = true }\n\nembassy-time = { version = \"0.5.1\", path = \"../embassy-time\" }\nembassy-sync = { version = \"0.8.0\", path = \"../embassy-sync\"}\nembassy-futures = { version = \"0.1.2\", path = \"../embassy-futures\"}\nembassy-net-driver-channel = { version = \"0.4.0\", path = \"../embassy-net-driver-channel\"}\n\nembedded-hal = { version = \"1.0\" }\nembedded-hal-async = { version = \"1.0\" }\n\nmicropb = { version = \"0.6.0\", default-features = false, features = [\"container-heapless-0-9\", \"encode\", \"decode\"] }\nheapless = \"0.9\"\n\n[package.metadata.embassy_docs]\nsrc_base = \"https://github.com/embassy-rs/embassy/blob/embassy-net-esp-hosted-v$VERSION/embassy-net-esp-hosted/src/\"\nsrc_base_git = \"https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-net-esp-hosted/src/\"\ntarget = \"thumbv7em-none-eabi\"\nfeatures = [\"defmt\"]\n\n[package.metadata.docs.rs]\nfeatures = [\"defmt\"]\n"
  },
  {
    "path": "embassy-net-esp-hosted/README.md",
    "content": "# ESP-Hosted `embassy-net` integration\n\n[`embassy-net`](https://crates.io/crates/embassy-net) integration for Espressif SoCs running the [ESP-Hosted](https://github.com/espressif/esp-hosted) stack.\n\nSee [`examples`](https://github.com/embassy-rs/embassy/tree/main/examples/nrf52840) directory for usage examples with the nRF52840.\n\n## Interoperability\n\nThis crate can run on any executor.\n\nIt supports any SPI driver implementing [`embedded-hal-async`](https://crates.io/crates/embedded-hal-async).\n"
  },
  {
    "path": "embassy-net-esp-hosted/src/control.rs",
    "content": "use embassy_net_driver_channel as ch;\nuse embassy_net_driver_channel::driver::{HardwareAddress, LinkState};\nuse heapless::String;\nuse micropb::{MessageDecode, MessageEncode, PbEncoder};\n\nuse crate::ioctl::Shared;\nuse crate::proto::{self, CtrlMsg};\n\n/// Errors reported by control.\n#[derive(Copy, Clone, PartialEq, Eq, Debug)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub enum Error {\n    /// The operation failed with the given error code.\n    Failed(u32),\n    /// The operation timed out.\n    Timeout,\n    /// Internal error.\n    Internal,\n}\n\n/// Handle for managing the network and WiFI state.\npub struct Control<'a> {\n    state_ch: ch::StateRunner<'a>,\n    shared: &'a Shared,\n}\n\n/// WiFi mode.\n#[allow(unused)]\n#[derive(Copy, Clone, PartialEq, Eq, Debug)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\nenum WifiMode {\n    /// No mode.\n    None = 0,\n    /// Client station.\n    Sta = 1,\n    /// Access point mode.\n    Ap = 2,\n    /// Repeater mode.\n    ApSta = 3,\n}\n\npub use proto::Ctrl_WifiSecProt as Security;\n\n/// WiFi status.\n#[derive(Clone, Debug)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub struct Status {\n    /// Service Set Identifier.\n    pub ssid: String<32>,\n    /// Basic Service Set Identifier.\n    pub bssid: [u8; 6],\n    /// Received Signal Strength Indicator.\n    pub rssi: i32,\n    /// WiFi channel.\n    pub channel: u32,\n    /// Security mode.\n    pub security: Security,\n}\n\nmacro_rules! ioctl {\n    ($self:ident, $req_variant:ident, $resp_variant:ident, $req:ident, $resp:ident) => {\n        let mut msg = proto::CtrlMsg {\n            msg_id: proto::CtrlMsgId::$req_variant,\n            msg_type: proto::CtrlMsgType::Req,\n            payload: Some(proto::CtrlMsg_::Payload::$req_variant($req)),\n            req_resp_type: 0,\n            uid: 0,\n        };\n        $self.ioctl(&mut msg).await?;\n        #[allow(unused_mut)]\n        let Some(proto::CtrlMsg_::Payload::$resp_variant(mut $resp)) = msg.payload else {\n            warn!(\"unexpected response variant\");\n            return Err(Error::Internal);\n        };\n        if $resp.resp != 0 {\n            return Err(Error::Failed($resp.resp as u32));\n        }\n    };\n}\n\nimpl<'a> Control<'a> {\n    pub(crate) fn new(state_ch: ch::StateRunner<'a>, shared: &'a Shared) -> Self {\n        Self { state_ch, shared }\n    }\n\n    /// Initialize device.\n    pub async fn init(&mut self) -> Result<(), Error> {\n        debug!(\"wait for init event...\");\n        self.shared.init_wait().await;\n\n        debug!(\"set heartbeat\");\n        self.set_heartbeat(10).await?;\n\n        debug!(\"set wifi mode\");\n        self.set_wifi_mode(WifiMode::Sta as _).await?;\n\n        let mac_addr = self.get_mac_addr().await?;\n        debug!(\"mac addr: {:02x}\", mac_addr);\n        self.state_ch.set_hardware_address(HardwareAddress::Ethernet(mac_addr));\n\n        Ok(())\n    }\n\n    /// Get the current status.\n    pub async fn get_status(&mut self) -> Result<Status, Error> {\n        let req = proto::CtrlMsg_Req_GetAPConfig {};\n        ioctl!(self, ReqGetApConfig, RespGetApConfig, req, resp);\n        let ssid = core::str::from_utf8(&resp.ssid).map_err(|_| Error::Internal)?;\n        let ssid = String::try_from(ssid.trim_end_matches('\\0')).map_err(|_| Error::Internal)?;\n        let bssid_str = core::str::from_utf8(&resp.bssid).map_err(|_| Error::Internal)?;\n        Ok(Status {\n            ssid,\n            bssid: parse_mac(bssid_str)?,\n            rssi: resp.rssi as _,\n            channel: resp.chnl as u32,\n            security: resp.sec_prot,\n        })\n    }\n\n    /// Connect to the network identified by ssid using the provided password.\n    pub async fn connect(&mut self, ssid: &str, password: &str) -> Result<(), Error> {\n        const WIFI_BAND_MODE_AUTO: i32 = 3; // 2.4GHz + 5GHz\n\n        let req = proto::CtrlMsg_Req_ConnectAP {\n            ssid: unwrap!(String::try_from(ssid)),\n            pwd: unwrap!(String::try_from(password)),\n            bssid: String::new(),\n            listen_interval: 3,\n            is_wpa3_supported: true,\n            band_mode: WIFI_BAND_MODE_AUTO,\n        };\n        ioctl!(self, ReqConnectAp, RespConnectAp, req, resp);\n\n        // TODO: in newer esp-hosted firmwares that added EventStationConnectedToAp\n        // the connect ioctl seems to be async, so we shouldn't immediately set LinkState::Up here.\n        self.state_ch.set_link_state(LinkState::Up);\n\n        Ok(())\n    }\n\n    /// Disconnect from any currently connected network.\n    pub async fn disconnect(&mut self) -> Result<(), Error> {\n        let req = proto::CtrlMsg_Req_GetStatus {};\n        ioctl!(self, ReqDisconnectAp, RespDisconnectAp, req, resp);\n        self.state_ch.set_link_state(LinkState::Down);\n        Ok(())\n    }\n\n    /// Initiate a firmware update.\n    pub async fn ota_begin(&mut self) -> Result<(), Error> {\n        let req = proto::CtrlMsg_Req_OTABegin {};\n        ioctl!(self, ReqOtaBegin, RespOtaBegin, req, resp);\n        Ok(())\n    }\n\n    /// Write slice of firmware to a device.\n    ///\n    /// [`ota_begin`] must be called first.\n    ///\n    /// The slice is split into chunks that can be sent across\n    /// the ioctl protocol to the wifi adapter.\n    pub async fn ota_write(&mut self, data: &[u8]) -> Result<(), Error> {\n        for chunk in data.chunks(256) {\n            let req = proto::CtrlMsg_Req_OTAWrite {\n                ota_data: heapless::Vec::from_slice(chunk).unwrap(),\n            };\n            ioctl!(self, ReqOtaWrite, RespOtaWrite, req, resp);\n        }\n        Ok(())\n    }\n\n    /// End the OTA session.\n    ///\n    /// [`ota_begin`] must be called first.\n    ///\n    /// NOTE: Will reset the wifi adapter after 5 seconds.\n    pub async fn ota_end(&mut self) -> Result<(), Error> {\n        let req = proto::CtrlMsg_Req_OTAEnd {};\n        ioctl!(self, ReqOtaEnd, RespOtaEnd, req, resp);\n        self.shared.ota_done();\n        // Wait for re-init\n        self.init().await?;\n        Ok(())\n    }\n\n    /// duration in seconds, clamped to [10, 3600]\n    async fn set_heartbeat(&mut self, duration: u32) -> Result<(), Error> {\n        let req = proto::CtrlMsg_Req_ConfigHeartbeat {\n            enable: true,\n            duration: duration as i32,\n        };\n        ioctl!(self, ReqConfigHeartbeat, RespConfigHeartbeat, req, resp);\n        Ok(())\n    }\n\n    async fn get_mac_addr(&mut self) -> Result<[u8; 6], Error> {\n        let req = proto::CtrlMsg_Req_GetMacAddress {\n            mode: WifiMode::Sta as _,\n        };\n        ioctl!(self, ReqGetMacAddress, RespGetMacAddress, req, resp);\n        let mac_str = core::str::from_utf8(&resp.mac).map_err(|_| Error::Internal)?;\n        parse_mac(mac_str)\n    }\n\n    async fn set_wifi_mode(&mut self, mode: u32) -> Result<(), Error> {\n        let req = proto::CtrlMsg_Req_SetMode { mode: mode as i32 };\n        ioctl!(self, ReqSetWifiMode, RespSetWifiMode, req, resp);\n\n        Ok(())\n    }\n\n    async fn ioctl(&mut self, msg: &mut CtrlMsg) -> Result<(), Error> {\n        debug!(\"ioctl req: {:?}\", &msg);\n\n        // Theoretical max overhead is 29 bytes. Biggest message is OTA write with 256 bytes.\n        let mut buf = [0u8; 256 + 29];\n        let buf_len = buf.len();\n\n        let mut encoder = PbEncoder::new(&mut buf[..]);\n        msg.encode(&mut encoder).map_err(|_| {\n            warn!(\"failed to serialize control request\");\n            Error::Internal\n        })?;\n        let remaining = encoder.into_writer();\n        let req_len = buf_len - remaining.len();\n\n        struct CancelOnDrop<'a>(&'a Shared);\n\n        impl CancelOnDrop<'_> {\n            fn defuse(self) {\n                core::mem::forget(self);\n            }\n        }\n\n        impl Drop for CancelOnDrop<'_> {\n            fn drop(&mut self) {\n                self.0.ioctl_cancel();\n            }\n        }\n\n        let ioctl = CancelOnDrop(self.shared);\n\n        let resp_len = ioctl.0.ioctl(&mut buf, req_len).await;\n\n        ioctl.defuse();\n\n        msg.decode_from_bytes(&buf[..resp_len]).map_err(|_| {\n            warn!(\"failed to deserialize control response\");\n            Error::Internal\n        })?;\n        debug!(\"ioctl resp: {:?}\", msg);\n\n        Ok(())\n    }\n}\n\n// WHY IS THIS A STRING? WHYYYY\nfn parse_mac(mac: &str) -> Result<[u8; 6], Error> {\n    fn nibble_from_hex(b: u8) -> Result<u8, Error> {\n        match b {\n            b'0'..=b'9' => Ok(b - b'0'),\n            b'a'..=b'f' => Ok(b + 0xa - b'a'),\n            b'A'..=b'F' => Ok(b + 0xa - b'A'),\n            _ => {\n                warn!(\"invalid hex digit {}\", b);\n                Err(Error::Internal)\n            }\n        }\n    }\n\n    let mac = mac.as_bytes();\n    let mut res = [0; 6];\n    if mac.len() != 17 {\n        warn!(\"unexpected MAC length\");\n        return Err(Error::Internal);\n    }\n    for (i, b) in res.iter_mut().enumerate() {\n        *b = (nibble_from_hex(mac[i * 3])? << 4) | nibble_from_hex(mac[i * 3 + 1])?\n    }\n    Ok(res)\n}\n"
  },
  {
    "path": "embassy-net-esp-hosted/src/esp_hosted_config.proto",
    "content": "/* Copyright (C) 2015-2025 Espressif Systems (Shanghai) PTE LTD */\n/* SPDX-License-Identifier: GPL-2.0-only OR Apache-2.0 */\n\nsyntax = \"proto3\";\n\n/* Enums similar to ESP IDF */\nenum Ctrl_VendorIEType {\n    Beacon = 0;\n    Probe_req = 1;\n    Probe_resp = 2;\n    Assoc_req = 3;\n    Assoc_resp = 4;\n}\n\nenum Ctrl_VendorIEID {\n    ID_0 = 0;\n    ID_1 = 1;\n}\n\nenum Ctrl_WifiMode {\n    NONE = 0;\n    STA = 1;\n    AP = 2;\n    APSTA = 3;\n}\n\nenum Ctrl_WifiBw {\n    BW_Invalid = 0;\n    HT20 = 1;\n    HT40 = 2;\n}\n\nenum Ctrl_WifiPowerSave {\n    NO_PS = 0;\n    MIN_MODEM = 1;\n    MAX_MODEM = 2;\n    PS_Invalid = 3;\n}\n\nenum Ctrl_WifiSecProt {\n    Open = 0;\n    WEP = 1;\n    WPA_PSK = 2;\n    WPA2_PSK = 3;\n    WPA_WPA2_PSK = 4;\n    WPA2_ENTERPRISE = 5;\n    WPA3_PSK = 6;\n    WPA2_WPA3_PSK = 7;\n}\n\n/* enums for Control path */\nenum Ctrl_Status {\n    Connected = 0;\n    Not_Connected = 1;\n    No_AP_Found = 2;\n    Connection_Fail = 3;\n    Invalid_Argument = 4;\n    Out_Of_Range = 5;\n}\n\n\nenum CtrlMsgType {\n    MsgType_Invalid = 0;\n    Req = 1;\n    Resp = 2;\n    Event = 3;\n    MsgType_Max = 4;\n}\n\nenum CtrlMsgId {\n    MsgId_Invalid = 0;\n\n    /** Request Msgs **/\n    Req_Base = 100;\n\n    Req_GetMACAddress = 101;\n    Req_SetMacAddress = 102;\n    Req_GetWifiMode = 103;\n    Req_SetWifiMode = 104;\n\n    Req_GetAPScanList = 105;\n    Req_GetAPConfig = 106;\n    Req_ConnectAP = 107;\n    Req_DisconnectAP = 108;\n\n    Req_GetSoftAPConfig = 109;\n    Req_SetSoftAPVendorSpecificIE = 110;\n    Req_StartSoftAP = 111;\n    Req_GetSoftAPConnectedSTAList = 112;\n    Req_StopSoftAP = 113;\n\n    Req_SetPowerSaveMode = 114;\n    Req_GetPowerSaveMode = 115;\n\n    Req_OTABegin = 116;\n    Req_OTAWrite = 117;\n    Req_OTAEnd = 118;\n\n    Req_SetWifiMaxTxPower = 119;\n    Req_GetWifiCurrTxPower = 120;\n\n    Req_ConfigHeartbeat = 121;\n    Req_EnableDisable = 122;\n    Req_GetFwVersion = 123;\n    Req_SetCountryCode = 124;\n    Req_GetCountryCode = 125;\n    Req_SetDhcpDnsStatus = 126;\n    Req_GetDhcpDnsStatus = 127;\n    Req_Custom_RPC_Unserialised_Msg = 128;\n    /* Add new control path command response before Req_Max\n     * and update Req_Max */\n    Req_Max = 129;\n\n    /** Response Msgs **/\n    Resp_Base = 200;\n\n    Resp_GetMACAddress = 201;\n    Resp_SetMacAddress = 202;\n    Resp_GetWifiMode = 203;\n    Resp_SetWifiMode = 204;\n\n    Resp_GetAPScanList = 205;\n    Resp_GetAPConfig = 206;\n    Resp_ConnectAP = 207;\n    Resp_DisconnectAP = 208;\n\n    Resp_GetSoftAPConfig = 209;\n    Resp_SetSoftAPVendorSpecificIE = 210;\n    Resp_StartSoftAP = 211;\n    Resp_GetSoftAPConnectedSTAList = 212;\n    Resp_StopSoftAP = 213;\n\n    Resp_SetPowerSaveMode = 214;\n    Resp_GetPowerSaveMode = 215;\n\n    Resp_OTABegin = 216;\n    Resp_OTAWrite = 217;\n    Resp_OTAEnd = 218;\n\n    Resp_SetWifiMaxTxPower = 219;\n    Resp_GetWifiCurrTxPower = 220;\n\n    Resp_ConfigHeartbeat = 221;\n    Resp_EnableDisable = 222;\n    Resp_GetFwVersion = 223;\n    Resp_SetCountryCode = 224;\n    Resp_GetCountryCode = 225;\n    Resp_SetDhcpDnsStatus = 226;\n    Resp_GetDhcpDnsStatus = 227;\n    Resp_Custom_RPC_Unserialised_Msg = 228;\n    /* Add new control path command response before Resp_Max\n     * and update Resp_Max */\n    Resp_Max = 229;\n\n    /** Event Msgs **/\n    Event_Base = 300;\n    Event_ESPInit = 301;\n    Event_Heartbeat = 302;\n    Event_StationDisconnectFromAP = 303;\n    Event_StationDisconnectFromESPSoftAP = 304;\n    Event_StationConnectedToAP = 305;\n    Event_StationConnectedToESPSoftAP = 306;\n    Event_SetDhcpDnsStatus = 307;\n    Event_Custom_RPC_Unserialised_Msg = 308;\n    /* Add new control path command notification before Event_Max\n     * and update Event_Max */\n    Event_Max = 309;\n}\n\nenum HostedFeature {\n    Hosted_InvalidFeature = 0;\n    Hosted_Wifi = 1;\n    Hosted_Bluetooth = 2;\n    Hosted_Is_Network_Split_On = 3;\n\n    /* Add your new features here and re-build prot using build_proto.sh */\n}\n\n/* internal supporting structures for CtrlMsg */\nmessage ScanResult {\n    bytes ssid = 1;\n    uint32 chnl = 2;\n    int32 rssi = 3;\n    bytes bssid = 4;\n    Ctrl_WifiSecProt sec_prot = 5;\n}\n\nmessage ConnectedSTAList {\n    bytes mac = 1;\n    int32 rssi = 2;\n}\n\n\n/* Control path structures */\n/** Req/Resp structure **/\nmessage CtrlMsg_Req_GetMacAddress {\n    int32 mode = 1;\n}\n\nmessage CtrlMsg_Resp_GetMacAddress {\n    bytes mac = 1;\n    int32 resp = 2;\n}\n\nmessage CtrlMsg_Req_GetMode {\n}\n\nmessage CtrlMsg_Resp_GetMode {\n    int32 mode = 1;\n    int32 resp = 2;\n}\n\nmessage CtrlMsg_Req_SetMode {\n    int32 mode = 1;\n}\n\nmessage CtrlMsg_Resp_SetMode {\n    int32 resp = 1;\n}\n\nmessage CtrlMsg_Req_GetStatus {\n}\n\nmessage CtrlMsg_Resp_GetStatus {\n    int32 resp = 1;\n}\n\nmessage CtrlMsg_Req_SetMacAddress {\n    bytes mac = 1;\n    int32 mode = 2;\n}\n\nmessage CtrlMsg_Resp_SetMacAddress {\n    int32 resp = 1;\n}\n\nmessage CtrlMsg_Req_GetAPConfig {\n}\n\nmessage CtrlMsg_Resp_GetAPConfig {\n    bytes ssid = 1;\n    bytes bssid = 2;\n    int32 rssi = 3;\n    int32 chnl = 4;\n    Ctrl_WifiSecProt sec_prot = 5;\n    int32 resp = 6;\n    int32 band_mode = 7;\n}\n\nmessage CtrlMsg_Req_ConnectAP {\n    string ssid = 1;\n    string pwd = 2;\n    string bssid = 3;\n    bool is_wpa3_supported = 4;\n    int32 listen_interval = 5;\n    int32 band_mode = 6;\n}\n\nmessage  CtrlMsg_Resp_ConnectAP {\n    int32 resp = 1;\n    bytes mac = 2;\n    int32 band_mode = 3;\n}\n\nmessage CtrlMsg_Req_GetSoftAPConfig {\n}\n\nmessage CtrlMsg_Resp_GetSoftAPConfig {\n    bytes ssid = 1;\n    bytes pwd = 2;\n    int32 chnl = 3;\n    Ctrl_WifiSecProt sec_prot = 4;\n    int32 max_conn = 5;\n    bool ssid_hidden = 6;\n    int32 bw = 7;\n    int32 resp = 8;\n    int32 band_mode = 9;\n}\n\nmessage CtrlMsg_Req_StartSoftAP {\n    string ssid = 1;\n    string pwd = 2;\n    int32 chnl = 3;\n    Ctrl_WifiSecProt sec_prot = 4;\n    int32 max_conn = 5;\n    bool ssid_hidden = 6;\n    int32 bw = 7;\n    int32 band_mode = 8;\n}\n\nmessage CtrlMsg_Resp_StartSoftAP {\n    int32 resp = 1;\n    bytes mac = 2;\n    int32 band_mode = 3;\n}\n\nmessage CtrlMsg_Req_ScanResult {\n}\n\nmessage CtrlMsg_Resp_ScanResult {\n    uint32 count = 1;\n    repeated ScanResult entries = 2;\n    int32 resp = 3;\n}\n\nmessage CtrlMsg_Req_SoftAPConnectedSTA {\n}\n\nmessage CtrlMsg_Resp_SoftAPConnectedSTA {\n    uint32 num = 1;\n    repeated ConnectedSTAList stations = 2;\n    int32 resp = 3;\n}\n\nmessage CtrlMsg_Req_OTABegin {\n}\n\nmessage CtrlMsg_Resp_OTABegin {\n    int32 resp = 1;\n}\n\nmessage CtrlMsg_Req_OTAWrite {\n    bytes ota_data = 1;\n}\n\nmessage CtrlMsg_Resp_OTAWrite {\n    int32 resp = 1;\n}\n\nmessage CtrlMsg_Req_OTAEnd {\n}\n\nmessage CtrlMsg_Resp_OTAEnd {\n    int32 resp = 1;\n}\n\nmessage CtrlMsg_Req_VendorIEData {\n    int32 element_id = 1;\n    int32 length = 2;\n    bytes vendor_oui = 3;\n    int32 vendor_oui_type = 4;\n    bytes payload = 5;\n}\n\nmessage CtrlMsg_Req_SetSoftAPVendorSpecificIE {\n    bool enable = 1;\n    Ctrl_VendorIEType type = 2;\n    Ctrl_VendorIEID idx = 3;\n    CtrlMsg_Req_VendorIEData vendor_ie_data = 4;\n}\n\nmessage CtrlMsg_Resp_SetSoftAPVendorSpecificIE {\n    int32 resp = 1;\n}\n\nmessage CtrlMsg_Req_SetWifiMaxTxPower {\n    int32 wifi_max_tx_power = 1;\n}\n\nmessage CtrlMsg_Resp_SetWifiMaxTxPower {\n    int32 resp = 1;\n}\n\nmessage CtrlMsg_Req_GetWifiCurrTxPower {\n}\n\nmessage CtrlMsg_Resp_GetWifiCurrTxPower {\n    int32 wifi_curr_tx_power = 1;\n    int32 resp = 2;\n}\n\nmessage CtrlMsg_Req_ConfigHeartbeat {\n    bool enable = 1;\n    int32 duration = 2;\n}\n\nmessage CtrlMsg_Resp_ConfigHeartbeat {\n    int32 resp = 1;\n}\n\nmessage CtrlMsg_Req_EnableDisable {\n    uint32 feature = 1;\n    bool enable = 2;\n}\n\nmessage CtrlMsg_Resp_EnableDisable {\n    int32 resp = 1;\n}\n\nmessage CtrlMsg_Req_GetFwVersion {\n}\n\nmessage CtrlMsg_Resp_GetFwVersion {\n    int32 resp = 1;\n    string name = 2;\n    uint32 major1 = 3;\n    uint32 major2 = 4;\n    uint32 minor = 5;\n    uint32 rev_patch1 = 6;\n    uint32 rev_patch2 = 7;\n}\n\nmessage CtrlMsg_Req_SetCountryCode {\n    bytes country = 1;\n    bool ieee80211d_enabled = 2;\n}\n\nmessage CtrlMsg_Resp_SetCountryCode {\n    int32 resp = 1;\n}\n\nmessage CtrlMsg_Req_GetCountryCode {\n}\n\nmessage CtrlMsg_Resp_GetCountryCode {\n    int32 resp = 1;\n    bytes country = 2;\n}\n\nmessage CtrlMsg_Req_SetDhcpDnsStatus {\n      int32 iface = 1;\n      int32 net_link_up = 2;\n\n      int32 dhcp_up = 3;\n      bytes dhcp_ip = 4;\n      bytes dhcp_nm = 5;\n      bytes dhcp_gw = 6;\n\n      int32 dns_up = 7;\n      bytes dns_ip = 8;\n      int32 dns_type = 9;\n}\n\nmessage CtrlMsg_Resp_SetDhcpDnsStatus {\n      int32 resp = 1;\n}\n\nmessage CtrlMsg_Req_GetDhcpDnsStatus {\n}\n\nmessage CtrlMsg_Resp_GetDhcpDnsStatus {\n      int32 resp = 1;\n      int32 iface = 2;\n      int32 net_link_up = 3;\n\n      int32 dhcp_up = 4;\n      bytes dhcp_ip = 5;\n      bytes dhcp_nm = 6;\n      bytes dhcp_gw = 7;\n\n      int32 dns_up = 8;\n      bytes dns_ip = 9;\n      int32 dns_type = 10;\n}\n\n/** Event structure **/\nmessage CtrlMsg_Event_ESPInit {\n    bytes init_data = 1;\n}\n\nmessage CtrlMsg_Event_Heartbeat {\n    int32 hb_num = 1;\n}\n\nmessage CtrlMsg_Event_StationDisconnectFromAP {\n    int32 resp = 1;\n    bytes ssid = 2;\n    uint32 ssid_len = 3;\n    bytes bssid = 4;\n    uint32 reason = 5;\n    int32 rssi = 6;\n}\n\nmessage CtrlMsg_Event_StationConnectedToAP {\n    int32 resp = 1;\n    bytes ssid = 2;\n    uint32 ssid_len = 3;\n    bytes bssid = 4;\n    uint32 channel = 5;\n    int32 authmode = 6;\n    int32 aid = 7;\n}\n\n\nmessage CtrlMsg_Event_StationDisconnectFromESPSoftAP {\n    int32 resp = 1;\n    bytes mac = 2;\n    uint32 aid = 3;\n    bool is_mesh_child = 4;\n    uint32 reason = 5;\n}\n\nmessage CtrlMsg_Event_StationConnectedToESPSoftAP {\n    int32 resp = 1;\n    bytes mac = 2;\n    uint32 aid = 3;\n    bool is_mesh_child = 4;\n}\n\nmessage CtrlMsg_Event_SetDhcpDnsStatus {\n      int32 iface = 1;\n      int32 net_link_up = 2;\n\n      int32 dhcp_up = 3;\n      bytes dhcp_ip = 4;\n      bytes dhcp_nm = 5;\n      bytes dhcp_gw = 6;\n\n      int32 dns_up = 7;\n      bytes dns_ip = 8;\n      int32 dns_type = 9;\n      int32 resp = 10;\n}\n\n/* Add Custom RPC message structures after existing message structures to make it easily notice */\nmessage CtrlMsg_Req_CustomRpcUnserialisedMsg {\n    uint32 custom_msg_id = 1;\n    bytes data = 2;\n}\n\nmessage CtrlMsg_Resp_CustomRpcUnserialisedMsg {\n    int32 resp = 1;\n    uint32 custom_msg_id = 2;\n    bytes data = 3;\n}\n\nmessage CtrlMsg_Event_CustomRpcUnserialisedMsg {\n    int32 resp = 1;\n    uint32 custom_evt_id = 2;\n    bytes data = 3;\n}\n\nmessage CtrlMsg {\n    /* msg_type could be req, resp or Event */\n    CtrlMsgType msg_type = 1;\n\n    /* msg id */\n    CtrlMsgId msg_id = 2;\n\n    /* UID of message */\n    int32 uid = 3;\n\n    /* Request/response type: sync or async */\n    uint32 req_resp_type = 4;\n\n    /* union of all msg ids */\n    oneof payload {\n        /** Requests **/\n        CtrlMsg_Req_GetMacAddress req_get_mac_address = 101;\n        CtrlMsg_Req_SetMacAddress req_set_mac_address = 102;\n        CtrlMsg_Req_GetMode req_get_wifi_mode = 103;\n        CtrlMsg_Req_SetMode req_set_wifi_mode = 104;\n\n        CtrlMsg_Req_ScanResult req_scan_ap_list = 105;\n        CtrlMsg_Req_GetAPConfig req_get_ap_config = 106;\n        CtrlMsg_Req_ConnectAP req_connect_ap = 107;\n        CtrlMsg_Req_GetStatus req_disconnect_ap = 108;\n\n        CtrlMsg_Req_GetSoftAPConfig req_get_softap_config = 109;\n        CtrlMsg_Req_SetSoftAPVendorSpecificIE req_set_softap_vendor_specific_ie = 110;\n        CtrlMsg_Req_StartSoftAP req_start_softap = 111;\n        CtrlMsg_Req_SoftAPConnectedSTA req_softap_connected_stas_list = 112;\n        CtrlMsg_Req_GetStatus req_stop_softap = 113;\n\n        CtrlMsg_Req_SetMode req_set_power_save_mode = 114;\n        CtrlMsg_Req_GetMode req_get_power_save_mode = 115;\n\n        CtrlMsg_Req_OTABegin req_ota_begin = 116;\n        CtrlMsg_Req_OTAWrite req_ota_write = 117;\n        CtrlMsg_Req_OTAEnd req_ota_end = 118;\n\n        CtrlMsg_Req_SetWifiMaxTxPower req_set_wifi_max_tx_power = 119;\n        CtrlMsg_Req_GetWifiCurrTxPower req_get_wifi_curr_tx_power = 120;\n        CtrlMsg_Req_ConfigHeartbeat req_config_heartbeat = 121;\n        CtrlMsg_Req_EnableDisable req_enable_disable_feat = 122;\n        CtrlMsg_Req_GetFwVersion req_get_fw_version = 123;\n        CtrlMsg_Req_SetCountryCode req_set_country_code = 124;\n        CtrlMsg_Req_GetCountryCode req_get_country_code = 125;\n        CtrlMsg_Req_SetDhcpDnsStatus req_set_dhcp_dns_status = 126;\n        CtrlMsg_Req_GetDhcpDnsStatus req_get_dhcp_dns_status = 127;\n        CtrlMsg_Req_CustomRpcUnserialisedMsg req_custom_rpc_unserialised_msg = 128;\n\n        /** Responses **/\n        CtrlMsg_Resp_GetMacAddress resp_get_mac_address = 201;\n        CtrlMsg_Resp_SetMacAddress resp_set_mac_address = 202;\n        CtrlMsg_Resp_GetMode resp_get_wifi_mode = 203;\n        CtrlMsg_Resp_SetMode resp_set_wifi_mode = 204;\n\n        CtrlMsg_Resp_ScanResult resp_scan_ap_list = 205;\n        CtrlMsg_Resp_GetAPConfig resp_get_ap_config = 206;\n        CtrlMsg_Resp_ConnectAP resp_connect_ap = 207;\n        CtrlMsg_Resp_GetStatus resp_disconnect_ap = 208;\n\n        CtrlMsg_Resp_GetSoftAPConfig resp_get_softap_config = 209;\n        CtrlMsg_Resp_SetSoftAPVendorSpecificIE resp_set_softap_vendor_specific_ie = 210;\n        CtrlMsg_Resp_StartSoftAP resp_start_softap = 211;\n        CtrlMsg_Resp_SoftAPConnectedSTA resp_softap_connected_stas_list = 212;\n        CtrlMsg_Resp_GetStatus resp_stop_softap = 213;\n\n        CtrlMsg_Resp_SetMode resp_set_power_save_mode = 214;\n        CtrlMsg_Resp_GetMode resp_get_power_save_mode = 215;\n\n        CtrlMsg_Resp_OTABegin resp_ota_begin = 216;\n        CtrlMsg_Resp_OTAWrite resp_ota_write = 217;\n        CtrlMsg_Resp_OTAEnd resp_ota_end = 218;\n        CtrlMsg_Resp_SetWifiMaxTxPower resp_set_wifi_max_tx_power = 219;\n        CtrlMsg_Resp_GetWifiCurrTxPower resp_get_wifi_curr_tx_power = 220;\n        CtrlMsg_Resp_ConfigHeartbeat resp_config_heartbeat = 221;\n        CtrlMsg_Resp_EnableDisable resp_enable_disable_feat = 222;\n        CtrlMsg_Resp_GetFwVersion resp_get_fw_version = 223;\n        CtrlMsg_Resp_SetCountryCode resp_set_country_code = 224;\n        CtrlMsg_Resp_GetCountryCode resp_get_country_code = 225;\n        CtrlMsg_Resp_SetDhcpDnsStatus resp_set_dhcp_dns_status = 226;\n        CtrlMsg_Resp_GetDhcpDnsStatus resp_get_dhcp_dns_status = 227;\n        CtrlMsg_Resp_CustomRpcUnserialisedMsg resp_custom_rpc_unserialised_msg = 228;\n\n        /** Notifications **/\n        CtrlMsg_Event_ESPInit event_esp_init = 301;\n        CtrlMsg_Event_Heartbeat event_heartbeat = 302;\n        CtrlMsg_Event_StationDisconnectFromAP event_station_disconnect_from_AP = 303;\n        CtrlMsg_Event_StationDisconnectFromESPSoftAP event_station_disconnect_from_ESP_SoftAP = 304;\n        CtrlMsg_Event_StationConnectedToAP event_station_connected_to_AP = 305;\n        CtrlMsg_Event_StationConnectedToESPSoftAP event_station_connected_to_ESP_SoftAP = 306;\n        CtrlMsg_Event_SetDhcpDnsStatus event_set_dhcp_dns_status = 307;\n        CtrlMsg_Event_CustomRpcUnserialisedMsg event_custom_rpc_unserialised_msg = 308;\n    }\n}\n"
  },
  {
    "path": "embassy-net-esp-hosted/src/fmt.rs",
    "content": "#![macro_use]\n#![allow(unused)]\n\nuse core::fmt::{Debug, Display, LowerHex};\n\n#[cfg(all(feature = \"defmt\", feature = \"log\"))]\ncompile_error!(\"You may not enable both `defmt` and `log` features.\");\n\n#[collapse_debuginfo(yes)]\nmacro_rules! assert {\n    ($($x:tt)*) => {\n        {\n            #[cfg(not(feature = \"defmt\"))]\n            ::core::assert!($($x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::assert!($($x)*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! assert_eq {\n    ($($x:tt)*) => {\n        {\n            #[cfg(not(feature = \"defmt\"))]\n            ::core::assert_eq!($($x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::assert_eq!($($x)*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! assert_ne {\n    ($($x:tt)*) => {\n        {\n            #[cfg(not(feature = \"defmt\"))]\n            ::core::assert_ne!($($x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::assert_ne!($($x)*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! debug_assert {\n    ($($x:tt)*) => {\n        {\n            #[cfg(not(feature = \"defmt\"))]\n            ::core::debug_assert!($($x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::debug_assert!($($x)*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! debug_assert_eq {\n    ($($x:tt)*) => {\n        {\n            #[cfg(not(feature = \"defmt\"))]\n            ::core::debug_assert_eq!($($x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::debug_assert_eq!($($x)*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! debug_assert_ne {\n    ($($x:tt)*) => {\n        {\n            #[cfg(not(feature = \"defmt\"))]\n            ::core::debug_assert_ne!($($x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::debug_assert_ne!($($x)*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! todo {\n    ($($x:tt)*) => {\n        {\n            #[cfg(not(feature = \"defmt\"))]\n            ::core::todo!($($x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::todo!($($x)*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! unreachable {\n    ($($x:tt)*) => {\n        {\n            #[cfg(not(feature = \"defmt\"))]\n            ::core::unreachable!($($x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::unreachable!($($x)*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! panic {\n    ($($x:tt)*) => {\n        {\n            #[cfg(not(feature = \"defmt\"))]\n            ::core::panic!($($x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::panic!($($x)*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! trace {\n    ($s:literal $(, $x:expr)* $(,)?) => {\n        {\n            #[cfg(feature = \"log\")]\n            ::log::trace!($s $(, $x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::trace!($s $(, $x)*);\n            #[cfg(not(any(feature = \"log\", feature=\"defmt\")))]\n            let _ = ($( & $x ),*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! debug {\n    ($s:literal $(, $x:expr)* $(,)?) => {\n        {\n            #[cfg(feature = \"log\")]\n            ::log::debug!($s $(, $x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::debug!($s $(, $x)*);\n            #[cfg(not(any(feature = \"log\", feature=\"defmt\")))]\n            let _ = ($( & $x ),*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! info {\n    ($s:literal $(, $x:expr)* $(,)?) => {\n        {\n            #[cfg(feature = \"log\")]\n            ::log::info!($s $(, $x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::info!($s $(, $x)*);\n            #[cfg(not(any(feature = \"log\", feature=\"defmt\")))]\n            let _ = ($( & $x ),*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! warn {\n    ($s:literal $(, $x:expr)* $(,)?) => {\n        {\n            #[cfg(feature = \"log\")]\n            ::log::warn!($s $(, $x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::warn!($s $(, $x)*);\n            #[cfg(not(any(feature = \"log\", feature=\"defmt\")))]\n            let _ = ($( & $x ),*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! error {\n    ($s:literal $(, $x:expr)* $(,)?) => {\n        {\n            #[cfg(feature = \"log\")]\n            ::log::error!($s $(, $x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::error!($s $(, $x)*);\n            #[cfg(not(any(feature = \"log\", feature=\"defmt\")))]\n            let _ = ($( & $x ),*);\n        }\n    };\n}\n\n#[cfg(feature = \"defmt\")]\n#[collapse_debuginfo(yes)]\nmacro_rules! unwrap {\n    ($($x:tt)*) => {\n        ::defmt::unwrap!($($x)*)\n    };\n}\n\n#[cfg(not(feature = \"defmt\"))]\n#[collapse_debuginfo(yes)]\nmacro_rules! unwrap {\n    ($arg:expr) => {\n        match $crate::fmt::Try::into_result($arg) {\n            ::core::result::Result::Ok(t) => t,\n            ::core::result::Result::Err(e) => {\n                ::core::panic!(\"unwrap of `{}` failed: {:?}\", ::core::stringify!($arg), e);\n            }\n        }\n    };\n    ($arg:expr, $($msg:expr),+ $(,)? ) => {\n        match $crate::fmt::Try::into_result($arg) {\n            ::core::result::Result::Ok(t) => t,\n            ::core::result::Result::Err(e) => {\n                ::core::panic!(\"unwrap of `{}` failed: {}: {:?}\", ::core::stringify!($arg), ::core::format_args!($($msg,)*), e);\n            }\n        }\n    }\n}\n\n#[derive(Debug, Copy, Clone, Eq, PartialEq)]\npub struct NoneError;\n\npub trait Try {\n    type Ok;\n    type Error;\n    fn into_result(self) -> Result<Self::Ok, Self::Error>;\n}\n\nimpl<T> Try for Option<T> {\n    type Ok = T;\n    type Error = NoneError;\n\n    #[inline]\n    fn into_result(self) -> Result<T, NoneError> {\n        self.ok_or(NoneError)\n    }\n}\n\nimpl<T, E> Try for Result<T, E> {\n    type Ok = T;\n    type Error = E;\n\n    #[inline]\n    fn into_result(self) -> Self {\n        self\n    }\n}\n\npub(crate) struct Bytes<'a>(pub &'a [u8]);\n\nimpl<'a> Debug for Bytes<'a> {\n    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {\n        write!(f, \"{:#02x?}\", self.0)\n    }\n}\n\nimpl<'a> Display for Bytes<'a> {\n    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {\n        write!(f, \"{:#02x?}\", self.0)\n    }\n}\n\nimpl<'a> LowerHex for Bytes<'a> {\n    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {\n        write!(f, \"{:#02x?}\", self.0)\n    }\n}\n\n#[cfg(feature = \"defmt\")]\nimpl<'a> defmt::Format for Bytes<'a> {\n    fn format(&self, fmt: defmt::Formatter) {\n        defmt::write!(fmt, \"{:02x}\", self.0)\n    }\n}\n"
  },
  {
    "path": "embassy-net-esp-hosted/src/iface.rs",
    "content": "use embassy_time::Timer;\nuse embedded_hal::digital::InputPin;\nuse embedded_hal_async::digital::Wait;\nuse embedded_hal_async::spi::SpiDevice;\n\n/// Physical interface trait for communicating with the ESP chip.\npub trait Interface {\n    /// Wait for the HANDSHAKE signal indicating the ESP is ready for a new transaction.\n    async fn wait_for_handshake(&mut self);\n\n    /// Wait for the READY signal indicating the ESP has data to send.\n    async fn wait_for_ready(&mut self);\n\n    /// Perform a SPI transfer, exchanging data with the ESP chip.\n    async fn transfer(&mut self, rx: &mut [u8], tx: &[u8]);\n}\n\n/// Standard SPI interface.\n///\n/// This interface is what's implemented in the upstream `esp-hosted-fg` firmware. It uses:\n/// - An `SpiDevice` for SPI communication (CS is handled by the device)\n/// - A handshake pin that signals when the ESP is ready for a new transaction\n/// - A ready pin that indicates when the ESP has data to send\npub struct SpiInterface<SPI, IN> {\n    spi: SPI,\n    handshake: IN,\n    ready: IN,\n}\n\nimpl<SPI, IN> SpiInterface<SPI, IN>\nwhere\n    SPI: SpiDevice,\n    IN: InputPin + Wait,\n{\n    /// Create a new SpiInterface.\n    pub fn new(spi: SPI, handshake: IN, ready: IN) -> Self {\n        Self { spi, handshake, ready }\n    }\n}\n\nimpl<SPI, IN> Interface for SpiInterface<SPI, IN>\nwhere\n    SPI: SpiDevice,\n    IN: InputPin + Wait,\n{\n    async fn wait_for_handshake(&mut self) {\n        self.handshake.wait_for_high().await.unwrap();\n    }\n\n    async fn wait_for_ready(&mut self) {\n        self.ready.wait_for_high().await.unwrap();\n    }\n\n    async fn transfer(&mut self, rx: &mut [u8], tx: &[u8]) {\n        self.spi.transfer(rx, tx).await.unwrap();\n\n        // The esp-hosted firmware deasserts the HANDSHAKE pin a few us AFTER ending the SPI transfer\n        // If we check it again too fast, we'll see it's high from the previous transfer, and if we send it\n        // data it will get lost.\n        Timer::after_micros(100).await;\n    }\n}\n"
  },
  {
    "path": "embassy-net-esp-hosted/src/ioctl.rs",
    "content": "use core::cell::RefCell;\nuse core::future::{Future, poll_fn};\nuse core::task::Poll;\n\nuse embassy_sync::waitqueue::WakerRegistration;\n\nuse crate::fmt::Bytes;\n\n#[derive(Clone, Copy)]\npub struct PendingIoctl {\n    pub buf: *mut [u8],\n    pub req_len: usize,\n}\n\n#[derive(Clone, Copy)]\nenum IoctlState {\n    Pending(PendingIoctl),\n    Sent { buf: *mut [u8] },\n    Done { resp_len: usize },\n}\n\npub struct Shared(RefCell<SharedInner>);\n\nstruct SharedInner {\n    ioctl: IoctlState,\n    state: ControlState,\n    control_waker: WakerRegistration,\n    runner_waker: WakerRegistration,\n}\n\n#[derive(Clone, Copy)]\npub(crate) enum ControlState {\n    Init,\n    Reboot,\n    Ready,\n}\n\nimpl Shared {\n    pub fn new() -> Self {\n        Self(RefCell::new(SharedInner {\n            ioctl: IoctlState::Done { resp_len: 0 },\n            state: ControlState::Init,\n            control_waker: WakerRegistration::new(),\n            runner_waker: WakerRegistration::new(),\n        }))\n    }\n\n    pub fn ioctl_wait_complete(&self) -> impl Future<Output = usize> + '_ {\n        poll_fn(|cx| {\n            let mut this = self.0.borrow_mut();\n            if let IoctlState::Done { resp_len } = this.ioctl {\n                Poll::Ready(resp_len)\n            } else {\n                this.control_waker.register(cx.waker());\n                Poll::Pending\n            }\n        })\n    }\n\n    pub async fn ioctl_wait_pending(&self) -> PendingIoctl {\n        let pending = poll_fn(|cx| {\n            let mut this = self.0.borrow_mut();\n            if let IoctlState::Pending(pending) = this.ioctl {\n                Poll::Ready(pending)\n            } else {\n                this.runner_waker.register(cx.waker());\n                Poll::Pending\n            }\n        })\n        .await;\n\n        self.0.borrow_mut().ioctl = IoctlState::Sent { buf: pending.buf };\n        pending\n    }\n\n    pub fn ioctl_cancel(&self) {\n        self.0.borrow_mut().ioctl = IoctlState::Done { resp_len: 0 };\n    }\n\n    pub async fn ioctl(&self, buf: &mut [u8], req_len: usize) -> usize {\n        trace!(\"ioctl req bytes: {:02x}\", Bytes(&buf[..req_len]));\n\n        {\n            let mut this = self.0.borrow_mut();\n            this.ioctl = IoctlState::Pending(PendingIoctl { buf, req_len });\n            this.runner_waker.wake();\n        }\n\n        self.ioctl_wait_complete().await\n    }\n\n    pub fn ioctl_done(&self, response: &[u8]) {\n        let mut this = self.0.borrow_mut();\n        if let IoctlState::Sent { buf } = this.ioctl {\n            trace!(\"ioctl resp bytes: {:02x}\", Bytes(response));\n\n            // TODO fix this\n            (unsafe { &mut *buf }[..response.len()]).copy_from_slice(response);\n\n            this.ioctl = IoctlState::Done {\n                resp_len: response.len(),\n            };\n            this.control_waker.wake();\n        } else {\n            warn!(\"IOCTL Response but no pending Ioctl\");\n        }\n    }\n\n    // ota\n    pub fn ota_done(&self) {\n        let mut this = self.0.borrow_mut();\n        this.state = ControlState::Reboot;\n    }\n\n    // // // // // // // // // // // // // // // // // // // //\n    //\n    // check if ota is in progress\n    pub(crate) fn state(&self) -> ControlState {\n        let this = self.0.borrow();\n        this.state\n    }\n\n    pub fn init_done(&self) {\n        let mut this = self.0.borrow_mut();\n        this.state = ControlState::Ready;\n        this.control_waker.wake();\n    }\n\n    pub fn init_wait(&self) -> impl Future<Output = ()> + '_ {\n        poll_fn(|cx| {\n            let mut this = self.0.borrow_mut();\n            if let ControlState::Ready = this.state {\n                Poll::Ready(())\n            } else {\n                this.control_waker.register(cx.waker());\n                Poll::Pending\n            }\n        })\n    }\n}\n"
  },
  {
    "path": "embassy-net-esp-hosted/src/lib.rs",
    "content": "#![no_std]\n#![doc = include_str!(\"../README.md\")]\n#![warn(missing_docs)]\n#![allow(async_fn_in_trait)]\n\nuse embassy_futures::select::{Either4, select4};\nuse embassy_net_driver_channel as ch;\nuse embassy_net_driver_channel::driver::LinkState;\nuse embassy_time::{Duration, Instant, Timer};\nuse embedded_hal::digital::OutputPin;\n\nuse crate::ioctl::{PendingIoctl, Shared};\nuse crate::proto::{CtrlMsg, CtrlMsg_};\n\n#[allow(unused)]\n#[allow(non_snake_case)]\n#[allow(non_camel_case_types)]\n#[allow(non_upper_case_globals)]\n#[allow(missing_docs)]\n#[allow(clippy::all)]\nmod proto;\n\n// must be first\nmod fmt;\n\nmod control;\nmod iface;\nmod ioctl;\n\npub use control::*;\npub use iface::*;\n\nconst MTU: usize = 1514;\n\nmacro_rules! impl_bytes {\n    ($t:ident) => {\n        impl $t {\n            pub const SIZE: usize = core::mem::size_of::<Self>();\n\n            #[allow(unused)]\n            pub fn to_bytes(&self) -> [u8; Self::SIZE] {\n                unsafe { core::mem::transmute(*self) }\n            }\n\n            #[allow(unused)]\n            pub fn from_bytes(bytes: &[u8; Self::SIZE]) -> &Self {\n                let alignment = core::mem::align_of::<Self>();\n                assert_eq!(\n                    bytes.as_ptr().align_offset(alignment),\n                    0,\n                    \"{} is not aligned\",\n                    core::any::type_name::<Self>()\n                );\n                unsafe { core::mem::transmute(bytes) }\n            }\n\n            #[allow(unused)]\n            pub fn from_bytes_mut(bytes: &mut [u8; Self::SIZE]) -> &mut Self {\n                let alignment = core::mem::align_of::<Self>();\n                assert_eq!(\n                    bytes.as_ptr().align_offset(alignment),\n                    0,\n                    \"{} is not aligned\",\n                    core::any::type_name::<Self>()\n                );\n\n                unsafe { core::mem::transmute(bytes) }\n            }\n        }\n    };\n}\n\n#[repr(C, packed)]\n#[derive(Clone, Copy, Debug, Default)]\nstruct PayloadHeader {\n    /// InterfaceType on lower 4 bits, number on higher 4 bits.\n    if_type_and_num: u8,\n\n    /// Flags.\n    ///\n    /// bit 0: more fragments.\n    flags: u8,\n\n    len: u16,\n    offset: u16,\n    checksum: u16,\n    seq_num: u16,\n    reserved2: u8,\n\n    /// Packet type for HCI or PRIV interface, reserved otherwise\n    hci_priv_packet_type: u8,\n}\nimpl_bytes!(PayloadHeader);\n\n#[allow(unused)]\n#[repr(u8)]\nenum InterfaceType {\n    Sta = 0,\n    Ap = 1,\n    Serial = 2,\n    Hci = 3,\n    Priv = 4,\n    Test = 5,\n}\n\nconst MAX_SPI_BUFFER_SIZE: usize = 1600;\nconst HEARTBEAT_MAX_GAP: Duration = Duration::from_secs(20);\n\n/// State for the esp-hosted driver.\npub struct State {\n    shared: Shared,\n    ch: ch::State<MTU, 4, 4>,\n}\n\nimpl State {\n    /// Create a new state.\n    pub fn new() -> Self {\n        Self {\n            shared: Shared::new(),\n            ch: ch::State::new(),\n        }\n    }\n}\n\n/// Type alias for network driver.\npub type NetDriver<'a> = ch::Device<'a, MTU>;\n\n/// Create a new esp-hosted driver using the provided state, interface, and reset pin.\n///\n/// Returns a device handle for interfacing with embassy-net, a control handle for\n/// interacting with the driver, and a runner for communicating with the WiFi device.\npub async fn new<'a, I, OUT>(\n    state: &'a mut State,\n    iface: I,\n    reset: OUT,\n) -> (NetDriver<'a>, Control<'a>, Runner<'a, I, OUT>)\nwhere\n    I: Interface,\n    OUT: OutputPin,\n{\n    let (ch_runner, device) = ch::new(&mut state.ch, ch::driver::HardwareAddress::Ethernet([0; 6]));\n    let state_ch = ch_runner.state_runner();\n\n    let runner = Runner {\n        ch: ch_runner,\n        state_ch,\n        shared: &state.shared,\n        next_seq: 1,\n        reset,\n        iface,\n        heartbeat_deadline: Instant::now() + HEARTBEAT_MAX_GAP,\n    };\n\n    (device, Control::new(state_ch, &state.shared), runner)\n}\n\n/// Runner for communicating with the WiFi device.\npub struct Runner<'a, I, OUT> {\n    ch: ch::Runner<'a, MTU>,\n    state_ch: ch::StateRunner<'a>,\n    shared: &'a Shared,\n\n    next_seq: u16,\n    heartbeat_deadline: Instant,\n\n    iface: I,\n    reset: OUT,\n}\n\nimpl<'a, I, OUT> Runner<'a, I, OUT>\nwhere\n    I: Interface,\n    OUT: OutputPin,\n{\n    /// Run the packet processing.\n    pub async fn run(mut self) -> ! {\n        debug!(\"resetting...\");\n        self.reset.set_low().unwrap();\n        Timer::after_millis(100).await;\n        self.reset.set_high().unwrap();\n        Timer::after_millis(1000).await;\n\n        let mut tx_buf = [0u8; MAX_SPI_BUFFER_SIZE];\n        let mut rx_buf = [0u8; MAX_SPI_BUFFER_SIZE];\n\n        loop {\n            self.iface.wait_for_handshake().await;\n\n            let ioctl = self.shared.ioctl_wait_pending();\n            let tx = self.ch.tx_buf();\n            let ev = async { self.iface.wait_for_ready().await };\n            let hb = Timer::at(self.heartbeat_deadline);\n\n            match select4(ioctl, tx, ev, hb).await {\n                Either4::First(PendingIoctl { buf, req_len }) => {\n                    tx_buf[12..24].copy_from_slice(b\"\\x01\\x08\\x00ctrlResp\\x02\");\n                    tx_buf[24..26].copy_from_slice(&(req_len as u16).to_le_bytes());\n                    tx_buf[26..][..req_len].copy_from_slice(&unsafe { &*buf }[..req_len]);\n\n                    let mut header = PayloadHeader {\n                        if_type_and_num: InterfaceType::Serial as _,\n                        len: (req_len + 14) as _,\n                        offset: PayloadHeader::SIZE as _,\n                        seq_num: self.next_seq,\n                        ..Default::default()\n                    };\n                    self.next_seq = self.next_seq.wrapping_add(1);\n\n                    // Calculate checksum\n                    tx_buf[0..12].copy_from_slice(&header.to_bytes());\n                    header.checksum = checksum(&tx_buf[..26 + req_len]);\n                    tx_buf[0..12].copy_from_slice(&header.to_bytes());\n                }\n                Either4::Second(packet) => {\n                    tx_buf[12..][..packet.len()].copy_from_slice(packet);\n\n                    let mut header = PayloadHeader {\n                        if_type_and_num: InterfaceType::Sta as _,\n                        len: packet.len() as _,\n                        offset: PayloadHeader::SIZE as _,\n                        seq_num: self.next_seq,\n                        ..Default::default()\n                    };\n                    self.next_seq = self.next_seq.wrapping_add(1);\n\n                    // Calculate checksum\n                    tx_buf[0..12].copy_from_slice(&header.to_bytes());\n                    header.checksum = checksum(&tx_buf[..12 + packet.len()]);\n                    tx_buf[0..12].copy_from_slice(&header.to_bytes());\n\n                    self.ch.tx_done();\n                }\n                Either4::Third(()) => {\n                    tx_buf[..PayloadHeader::SIZE].fill(0);\n                }\n                Either4::Fourth(()) => {\n                    // Extend the deadline if initializing\n                    if let ioctl::ControlState::Reboot = self.shared.state() {\n                        self.heartbeat_deadline = Instant::now() + HEARTBEAT_MAX_GAP;\n                        continue;\n                    }\n                    panic!(\"heartbeat from esp32 stopped\")\n                }\n            }\n\n            if tx_buf[0] != 0 {\n                trace!(\"tx: {:02x}\", &tx_buf[..40]);\n            }\n\n            self.iface.transfer(&mut rx_buf, &tx_buf).await;\n\n            self.handle_rx(&mut rx_buf);\n        }\n    }\n\n    fn handle_rx(&mut self, buf: &mut [u8]) {\n        trace!(\"rx: {:02x}\", &buf[..40]);\n\n        let buf_len = buf.len();\n        let h = PayloadHeader::from_bytes_mut((&mut buf[..PayloadHeader::SIZE]).try_into().unwrap());\n\n        if h.len == 0 || h.offset as usize != PayloadHeader::SIZE {\n            return;\n        }\n\n        let payload_len = h.len as usize;\n        if buf_len < PayloadHeader::SIZE + payload_len {\n            warn!(\"rx: len too big\");\n            return;\n        }\n\n        let if_type_and_num = h.if_type_and_num;\n        let want_checksum = h.checksum;\n        h.checksum = 0;\n        let got_checksum = checksum(&buf[..PayloadHeader::SIZE + payload_len]);\n        if want_checksum != got_checksum {\n            warn!(\"rx: bad checksum. Got {:04x}, want {:04x}\", got_checksum, want_checksum);\n            return;\n        }\n\n        let payload = &mut buf[PayloadHeader::SIZE..][..payload_len];\n\n        match if_type_and_num & 0x0f {\n            // STA\n            0 => match self.ch.try_rx_buf() {\n                Some(buf) => {\n                    buf[..payload.len()].copy_from_slice(payload);\n                    self.ch.rx_done(payload.len())\n                }\n                None => warn!(\"failed to push rxd packet to the channel.\"),\n            },\n            // serial\n            2 => {\n                trace!(\"serial rx: {:02x}\", payload);\n                if payload.len() < 14 {\n                    warn!(\"serial rx: too short\");\n                    return;\n                }\n\n                let is_event = match &payload[..12] {\n                    b\"\\x01\\x08\\x00ctrlResp\\x02\" => false,\n                    b\"\\x01\\x08\\x00ctrlEvnt\\x02\" => true,\n                    _ => {\n                        warn!(\"serial rx: bad tlv\");\n                        return;\n                    }\n                };\n\n                let len = u16::from_le_bytes(payload[12..14].try_into().unwrap()) as usize;\n                if payload.len() < 14 + len {\n                    warn!(\"serial rx: too short 2\");\n                    return;\n                }\n                let data = &payload[14..][..len];\n\n                if is_event {\n                    self.handle_event(data);\n                } else {\n                    self.shared.ioctl_done(data);\n                }\n            }\n            _ => warn!(\"unknown iftype {}\", if_type_and_num),\n        }\n    }\n\n    fn handle_event(&mut self, data: &[u8]) {\n        use micropb::MessageDecode;\n        let mut event = CtrlMsg::default();\n        if event.decode_from_bytes(data).is_err() {\n            warn!(\"failed to parse event\");\n            return;\n        }\n\n        debug!(\"event: {:?}\", &event);\n\n        let Some(payload) = &event.payload else {\n            warn!(\"event without payload?\");\n            return;\n        };\n\n        match payload {\n            CtrlMsg_::Payload::EventEspInit(_) => self.shared.init_done(),\n            CtrlMsg_::Payload::EventHeartbeat(_) => self.heartbeat_deadline = Instant::now() + HEARTBEAT_MAX_GAP,\n            CtrlMsg_::Payload::EventStationConnectedToAp(e) => {\n                info!(\"connected, code {}\", e.resp);\n                self.state_ch.set_link_state(LinkState::Up);\n            }\n            CtrlMsg_::Payload::EventStationDisconnectFromAp(e) => {\n                info!(\"disconnected, code {}\", e.resp);\n                self.state_ch.set_link_state(LinkState::Down);\n            }\n            _ => {}\n        }\n    }\n}\n\nfn checksum(buf: &[u8]) -> u16 {\n    let mut res = 0u16;\n    for &b in buf {\n        res = res.wrapping_add(b as _);\n    }\n    res\n}\n"
  },
  {
    "path": "embassy-net-esp-hosted/src/proto.rs",
    "content": "/// internal supporting structures for CtrlMsg\n#[derive(Debug, Default, PartialEq, Clone)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub struct ScanResult {\n    pub r#ssid: ::heapless::Vec<u8, 32>,\n    pub r#chnl: u32,\n    pub r#rssi: i32,\n    pub r#bssid: ::heapless::Vec<u8, 32>,\n    pub r#sec_prot: Ctrl_WifiSecProt,\n}\nimpl ScanResult {\n    /// Return a reference to `ssid`\n    #[inline]\n    pub fn r#ssid(&self) -> &::heapless::Vec<u8, 32> {\n        &self.r#ssid\n    }\n    /// Return a mutable reference to `ssid`\n    #[inline]\n    pub fn mut_ssid(&mut self) -> &mut ::heapless::Vec<u8, 32> {\n        &mut self.r#ssid\n    }\n    /// Set the value of `ssid`\n    #[inline]\n    pub fn set_ssid(&mut self, value: ::heapless::Vec<u8, 32>) -> &mut Self {\n        self.r#ssid = value.into();\n        self\n    }\n    /// Builder method that sets the value of `ssid`. Useful for initializing the message.\n    #[inline]\n    pub fn init_ssid(mut self, value: ::heapless::Vec<u8, 32>) -> Self {\n        self.r#ssid = value.into();\n        self\n    }\n    /// Return a reference to `chnl`\n    #[inline]\n    pub fn r#chnl(&self) -> &u32 {\n        &self.r#chnl\n    }\n    /// Return a mutable reference to `chnl`\n    #[inline]\n    pub fn mut_chnl(&mut self) -> &mut u32 {\n        &mut self.r#chnl\n    }\n    /// Set the value of `chnl`\n    #[inline]\n    pub fn set_chnl(&mut self, value: u32) -> &mut Self {\n        self.r#chnl = value.into();\n        self\n    }\n    /// Builder method that sets the value of `chnl`. Useful for initializing the message.\n    #[inline]\n    pub fn init_chnl(mut self, value: u32) -> Self {\n        self.r#chnl = value.into();\n        self\n    }\n    /// Return a reference to `rssi`\n    #[inline]\n    pub fn r#rssi(&self) -> &i32 {\n        &self.r#rssi\n    }\n    /// Return a mutable reference to `rssi`\n    #[inline]\n    pub fn mut_rssi(&mut self) -> &mut i32 {\n        &mut self.r#rssi\n    }\n    /// Set the value of `rssi`\n    #[inline]\n    pub fn set_rssi(&mut self, value: i32) -> &mut Self {\n        self.r#rssi = value.into();\n        self\n    }\n    /// Builder method that sets the value of `rssi`. Useful for initializing the message.\n    #[inline]\n    pub fn init_rssi(mut self, value: i32) -> Self {\n        self.r#rssi = value.into();\n        self\n    }\n    /// Return a reference to `bssid`\n    #[inline]\n    pub fn r#bssid(&self) -> &::heapless::Vec<u8, 32> {\n        &self.r#bssid\n    }\n    /// Return a mutable reference to `bssid`\n    #[inline]\n    pub fn mut_bssid(&mut self) -> &mut ::heapless::Vec<u8, 32> {\n        &mut self.r#bssid\n    }\n    /// Set the value of `bssid`\n    #[inline]\n    pub fn set_bssid(&mut self, value: ::heapless::Vec<u8, 32>) -> &mut Self {\n        self.r#bssid = value.into();\n        self\n    }\n    /// Builder method that sets the value of `bssid`. Useful for initializing the message.\n    #[inline]\n    pub fn init_bssid(mut self, value: ::heapless::Vec<u8, 32>) -> Self {\n        self.r#bssid = value.into();\n        self\n    }\n    /// Return a reference to `sec_prot`\n    #[inline]\n    pub fn r#sec_prot(&self) -> &Ctrl_WifiSecProt {\n        &self.r#sec_prot\n    }\n    /// Return a mutable reference to `sec_prot`\n    #[inline]\n    pub fn mut_sec_prot(&mut self) -> &mut Ctrl_WifiSecProt {\n        &mut self.r#sec_prot\n    }\n    /// Set the value of `sec_prot`\n    #[inline]\n    pub fn set_sec_prot(&mut self, value: Ctrl_WifiSecProt) -> &mut Self {\n        self.r#sec_prot = value.into();\n        self\n    }\n    /// Builder method that sets the value of `sec_prot`. Useful for initializing the message.\n    #[inline]\n    pub fn init_sec_prot(mut self, value: Ctrl_WifiSecProt) -> Self {\n        self.r#sec_prot = value.into();\n        self\n    }\n}\nimpl ::micropb::MessageDecode for ScanResult {\n    fn decode<IMPL_MICROPB_READ: ::micropb::PbRead>(\n        &mut self,\n        decoder: &mut ::micropb::PbDecoder<IMPL_MICROPB_READ>,\n        len: usize,\n    ) -> Result<(), ::micropb::DecodeError<IMPL_MICROPB_READ::Error>> {\n        use ::micropb::{FieldDecode, PbBytes, PbMap, PbString, PbVec};\n        let before = decoder.bytes_read();\n        while decoder.bytes_read() - before < len {\n            let tag = decoder.decode_tag()?;\n            match tag.field_num() {\n                0 => return Err(::micropb::DecodeError::ZeroField),\n                1u32 => {\n                    let mut_ref = &mut self.r#ssid;\n                    {\n                        decoder.decode_bytes(mut_ref, ::micropb::Presence::Implicit)?;\n                    };\n                }\n                2u32 => {\n                    let mut_ref = &mut self.r#chnl;\n                    {\n                        let val = decoder.decode_varint32()?;\n                        let val_ref = &val;\n                        if *val_ref != 0 {\n                            *mut_ref = val as _;\n                        }\n                    };\n                }\n                3u32 => {\n                    let mut_ref = &mut self.r#rssi;\n                    {\n                        let val = decoder.decode_int32()?;\n                        let val_ref = &val;\n                        if *val_ref != 0 {\n                            *mut_ref = val as _;\n                        }\n                    };\n                }\n                4u32 => {\n                    let mut_ref = &mut self.r#bssid;\n                    {\n                        decoder.decode_bytes(mut_ref, ::micropb::Presence::Implicit)?;\n                    };\n                }\n                5u32 => {\n                    let mut_ref = &mut self.r#sec_prot;\n                    {\n                        let val = decoder.decode_int32().map(|n| Ctrl_WifiSecProt(n as _))?;\n                        let val_ref = &val;\n                        if val_ref.0 != 0 {\n                            *mut_ref = val as _;\n                        }\n                    };\n                }\n                _ => {\n                    decoder.skip_wire_value(tag.wire_type())?;\n                }\n            }\n        }\n        Ok(())\n    }\n}\nimpl ::micropb::MessageEncode for ScanResult {\n    const MAX_SIZE: ::core::result::Result<usize, &'static str> = 'msg: {\n        let mut max_size = 0;\n        match ::micropb::const_map!(::core::result::Result::Ok(33usize), |size| size + 1usize) {\n            ::core::result::Result::Ok(size) => {\n                max_size += size;\n            }\n            ::core::result::Result::Err(err) => {\n                break 'msg (::core::result::Result::<usize, _>::Err(err));\n            }\n        }\n        match ::micropb::const_map!(::core::result::Result::Ok(5usize), |size| size + 1usize) {\n            ::core::result::Result::Ok(size) => {\n                max_size += size;\n            }\n            ::core::result::Result::Err(err) => {\n                break 'msg (::core::result::Result::<usize, _>::Err(err));\n            }\n        }\n        match ::micropb::const_map!(::core::result::Result::Ok(10usize), |size| size + 1usize) {\n            ::core::result::Result::Ok(size) => {\n                max_size += size;\n            }\n            ::core::result::Result::Err(err) => {\n                break 'msg (::core::result::Result::<usize, _>::Err(err));\n            }\n        }\n        match ::micropb::const_map!(::core::result::Result::Ok(33usize), |size| size + 1usize) {\n            ::core::result::Result::Ok(size) => {\n                max_size += size;\n            }\n            ::core::result::Result::Err(err) => {\n                break 'msg (::core::result::Result::<usize, _>::Err(err));\n            }\n        }\n        match ::micropb::const_map!(::core::result::Result::Ok(Ctrl_WifiSecProt::_MAX_SIZE), |size| size\n            + 1usize)\n        {\n            ::core::result::Result::Ok(size) => {\n                max_size += size;\n            }\n            ::core::result::Result::Err(err) => {\n                break 'msg (::core::result::Result::<usize, _>::Err(err));\n            }\n        }\n        ::core::result::Result::Ok(max_size)\n    };\n    fn encode<IMPL_MICROPB_WRITE: ::micropb::PbWrite>(\n        &self,\n        encoder: &mut ::micropb::PbEncoder<IMPL_MICROPB_WRITE>,\n    ) -> Result<(), IMPL_MICROPB_WRITE::Error> {\n        use ::micropb::{FieldEncode, PbMap};\n        {\n            let val_ref = &self.r#ssid;\n            if !val_ref.is_empty() {\n                encoder.encode_varint32(10u32)?;\n                encoder.encode_bytes(val_ref)?;\n            }\n        }\n        {\n            let val_ref = &self.r#chnl;\n            if *val_ref != 0 {\n                encoder.encode_varint32(16u32)?;\n                encoder.encode_varint32(*val_ref as _)?;\n            }\n        }\n        {\n            let val_ref = &self.r#rssi;\n            if *val_ref != 0 {\n                encoder.encode_varint32(24u32)?;\n                encoder.encode_int32(*val_ref as _)?;\n            }\n        }\n        {\n            let val_ref = &self.r#bssid;\n            if !val_ref.is_empty() {\n                encoder.encode_varint32(34u32)?;\n                encoder.encode_bytes(val_ref)?;\n            }\n        }\n        {\n            let val_ref = &self.r#sec_prot;\n            if val_ref.0 != 0 {\n                encoder.encode_varint32(40u32)?;\n                encoder.encode_int32(val_ref.0 as _)?;\n            }\n        }\n        Ok(())\n    }\n    fn compute_size(&self) -> usize {\n        use ::micropb::{FieldEncode, PbMap};\n        let mut size = 0;\n        {\n            let val_ref = &self.r#ssid;\n            if !val_ref.is_empty() {\n                size += 1usize + ::micropb::size::sizeof_len_record(val_ref.len());\n            }\n        }\n        {\n            let val_ref = &self.r#chnl;\n            if *val_ref != 0 {\n                size += 1usize + ::micropb::size::sizeof_varint32(*val_ref as _);\n            }\n        }\n        {\n            let val_ref = &self.r#rssi;\n            if *val_ref != 0 {\n                size += 1usize + ::micropb::size::sizeof_int32(*val_ref as _);\n            }\n        }\n        {\n            let val_ref = &self.r#bssid;\n            if !val_ref.is_empty() {\n                size += 1usize + ::micropb::size::sizeof_len_record(val_ref.len());\n            }\n        }\n        {\n            let val_ref = &self.r#sec_prot;\n            if val_ref.0 != 0 {\n                size += 1usize + ::micropb::size::sizeof_int32(val_ref.0 as _);\n            }\n        }\n        size\n    }\n}\n#[derive(Debug, Default, PartialEq, Clone)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub struct ConnectedSTAList {\n    pub r#mac: ::heapless::Vec<u8, 32>,\n    pub r#rssi: i32,\n}\nimpl ConnectedSTAList {\n    /// Return a reference to `mac`\n    #[inline]\n    pub fn r#mac(&self) -> &::heapless::Vec<u8, 32> {\n        &self.r#mac\n    }\n    /// Return a mutable reference to `mac`\n    #[inline]\n    pub fn mut_mac(&mut self) -> &mut ::heapless::Vec<u8, 32> {\n        &mut self.r#mac\n    }\n    /// Set the value of `mac`\n    #[inline]\n    pub fn set_mac(&mut self, value: ::heapless::Vec<u8, 32>) -> &mut Self {\n        self.r#mac = value.into();\n        self\n    }\n    /// Builder method that sets the value of `mac`. Useful for initializing the message.\n    #[inline]\n    pub fn init_mac(mut self, value: ::heapless::Vec<u8, 32>) -> Self {\n        self.r#mac = value.into();\n        self\n    }\n    /// Return a reference to `rssi`\n    #[inline]\n    pub fn r#rssi(&self) -> &i32 {\n        &self.r#rssi\n    }\n    /// Return a mutable reference to `rssi`\n    #[inline]\n    pub fn mut_rssi(&mut self) -> &mut i32 {\n        &mut self.r#rssi\n    }\n    /// Set the value of `rssi`\n    #[inline]\n    pub fn set_rssi(&mut self, value: i32) -> &mut Self {\n        self.r#rssi = value.into();\n        self\n    }\n    /// Builder method that sets the value of `rssi`. Useful for initializing the message.\n    #[inline]\n    pub fn init_rssi(mut self, value: i32) -> Self {\n        self.r#rssi = value.into();\n        self\n    }\n}\nimpl ::micropb::MessageDecode for ConnectedSTAList {\n    fn decode<IMPL_MICROPB_READ: ::micropb::PbRead>(\n        &mut self,\n        decoder: &mut ::micropb::PbDecoder<IMPL_MICROPB_READ>,\n        len: usize,\n    ) -> Result<(), ::micropb::DecodeError<IMPL_MICROPB_READ::Error>> {\n        use ::micropb::{FieldDecode, PbBytes, PbMap, PbString, PbVec};\n        let before = decoder.bytes_read();\n        while decoder.bytes_read() - before < len {\n            let tag = decoder.decode_tag()?;\n            match tag.field_num() {\n                0 => return Err(::micropb::DecodeError::ZeroField),\n                1u32 => {\n                    let mut_ref = &mut self.r#mac;\n                    {\n                        decoder.decode_bytes(mut_ref, ::micropb::Presence::Implicit)?;\n                    };\n                }\n                2u32 => {\n                    let mut_ref = &mut self.r#rssi;\n                    {\n                        let val = decoder.decode_int32()?;\n                        let val_ref = &val;\n                        if *val_ref != 0 {\n                            *mut_ref = val as _;\n                        }\n                    };\n                }\n                _ => {\n                    decoder.skip_wire_value(tag.wire_type())?;\n                }\n            }\n        }\n        Ok(())\n    }\n}\nimpl ::micropb::MessageEncode for ConnectedSTAList {\n    const MAX_SIZE: ::core::result::Result<usize, &'static str> = 'msg: {\n        let mut max_size = 0;\n        match ::micropb::const_map!(::core::result::Result::Ok(33usize), |size| size + 1usize) {\n            ::core::result::Result::Ok(size) => {\n                max_size += size;\n            }\n            ::core::result::Result::Err(err) => {\n                break 'msg (::core::result::Result::<usize, _>::Err(err));\n            }\n        }\n        match ::micropb::const_map!(::core::result::Result::Ok(10usize), |size| size + 1usize) {\n            ::core::result::Result::Ok(size) => {\n                max_size += size;\n            }\n            ::core::result::Result::Err(err) => {\n                break 'msg (::core::result::Result::<usize, _>::Err(err));\n            }\n        }\n        ::core::result::Result::Ok(max_size)\n    };\n    fn encode<IMPL_MICROPB_WRITE: ::micropb::PbWrite>(\n        &self,\n        encoder: &mut ::micropb::PbEncoder<IMPL_MICROPB_WRITE>,\n    ) -> Result<(), IMPL_MICROPB_WRITE::Error> {\n        use ::micropb::{FieldEncode, PbMap};\n        {\n            let val_ref = &self.r#mac;\n            if !val_ref.is_empty() {\n                encoder.encode_varint32(10u32)?;\n                encoder.encode_bytes(val_ref)?;\n            }\n        }\n        {\n            let val_ref = &self.r#rssi;\n            if *val_ref != 0 {\n                encoder.encode_varint32(16u32)?;\n                encoder.encode_int32(*val_ref as _)?;\n            }\n        }\n        Ok(())\n    }\n    fn compute_size(&self) -> usize {\n        use ::micropb::{FieldEncode, PbMap};\n        let mut size = 0;\n        {\n            let val_ref = &self.r#mac;\n            if !val_ref.is_empty() {\n                size += 1usize + ::micropb::size::sizeof_len_record(val_ref.len());\n            }\n        }\n        {\n            let val_ref = &self.r#rssi;\n            if *val_ref != 0 {\n                size += 1usize + ::micropb::size::sizeof_int32(*val_ref as _);\n            }\n        }\n        size\n    }\n}\n///* Req/Resp structure *\n#[derive(Debug, Default, PartialEq, Clone, Copy)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub struct CtrlMsg_Req_GetMacAddress {\n    pub r#mode: i32,\n}\nimpl CtrlMsg_Req_GetMacAddress {\n    /// Return a reference to `mode`\n    #[inline]\n    pub fn r#mode(&self) -> &i32 {\n        &self.r#mode\n    }\n    /// Return a mutable reference to `mode`\n    #[inline]\n    pub fn mut_mode(&mut self) -> &mut i32 {\n        &mut self.r#mode\n    }\n    /// Set the value of `mode`\n    #[inline]\n    pub fn set_mode(&mut self, value: i32) -> &mut Self {\n        self.r#mode = value.into();\n        self\n    }\n    /// Builder method that sets the value of `mode`. Useful for initializing the message.\n    #[inline]\n    pub fn init_mode(mut self, value: i32) -> Self {\n        self.r#mode = value.into();\n        self\n    }\n}\nimpl ::micropb::MessageDecode for CtrlMsg_Req_GetMacAddress {\n    fn decode<IMPL_MICROPB_READ: ::micropb::PbRead>(\n        &mut self,\n        decoder: &mut ::micropb::PbDecoder<IMPL_MICROPB_READ>,\n        len: usize,\n    ) -> Result<(), ::micropb::DecodeError<IMPL_MICROPB_READ::Error>> {\n        use ::micropb::{FieldDecode, PbBytes, PbMap, PbString, PbVec};\n        let before = decoder.bytes_read();\n        while decoder.bytes_read() - before < len {\n            let tag = decoder.decode_tag()?;\n            match tag.field_num() {\n                0 => return Err(::micropb::DecodeError::ZeroField),\n                1u32 => {\n                    let mut_ref = &mut self.r#mode;\n                    {\n                        let val = decoder.decode_int32()?;\n                        let val_ref = &val;\n                        if *val_ref != 0 {\n                            *mut_ref = val as _;\n                        }\n                    };\n                }\n                _ => {\n                    decoder.skip_wire_value(tag.wire_type())?;\n                }\n            }\n        }\n        Ok(())\n    }\n}\nimpl ::micropb::MessageEncode for CtrlMsg_Req_GetMacAddress {\n    const MAX_SIZE: ::core::result::Result<usize, &'static str> = 'msg: {\n        let mut max_size = 0;\n        match ::micropb::const_map!(::core::result::Result::Ok(10usize), |size| size + 1usize) {\n            ::core::result::Result::Ok(size) => {\n                max_size += size;\n            }\n            ::core::result::Result::Err(err) => {\n                break 'msg (::core::result::Result::<usize, _>::Err(err));\n            }\n        }\n        ::core::result::Result::Ok(max_size)\n    };\n    fn encode<IMPL_MICROPB_WRITE: ::micropb::PbWrite>(\n        &self,\n        encoder: &mut ::micropb::PbEncoder<IMPL_MICROPB_WRITE>,\n    ) -> Result<(), IMPL_MICROPB_WRITE::Error> {\n        use ::micropb::{FieldEncode, PbMap};\n        {\n            let val_ref = &self.r#mode;\n            if *val_ref != 0 {\n                encoder.encode_varint32(8u32)?;\n                encoder.encode_int32(*val_ref as _)?;\n            }\n        }\n        Ok(())\n    }\n    fn compute_size(&self) -> usize {\n        use ::micropb::{FieldEncode, PbMap};\n        let mut size = 0;\n        {\n            let val_ref = &self.r#mode;\n            if *val_ref != 0 {\n                size += 1usize + ::micropb::size::sizeof_int32(*val_ref as _);\n            }\n        }\n        size\n    }\n}\n#[derive(Debug, Default, PartialEq, Clone)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub struct CtrlMsg_Resp_GetMacAddress {\n    pub r#mac: ::heapless::Vec<u8, 32>,\n    pub r#resp: i32,\n}\nimpl CtrlMsg_Resp_GetMacAddress {\n    /// Return a reference to `mac`\n    #[inline]\n    pub fn r#mac(&self) -> &::heapless::Vec<u8, 32> {\n        &self.r#mac\n    }\n    /// Return a mutable reference to `mac`\n    #[inline]\n    pub fn mut_mac(&mut self) -> &mut ::heapless::Vec<u8, 32> {\n        &mut self.r#mac\n    }\n    /// Set the value of `mac`\n    #[inline]\n    pub fn set_mac(&mut self, value: ::heapless::Vec<u8, 32>) -> &mut Self {\n        self.r#mac = value.into();\n        self\n    }\n    /// Builder method that sets the value of `mac`. Useful for initializing the message.\n    #[inline]\n    pub fn init_mac(mut self, value: ::heapless::Vec<u8, 32>) -> Self {\n        self.r#mac = value.into();\n        self\n    }\n    /// Return a reference to `resp`\n    #[inline]\n    pub fn r#resp(&self) -> &i32 {\n        &self.r#resp\n    }\n    /// Return a mutable reference to `resp`\n    #[inline]\n    pub fn mut_resp(&mut self) -> &mut i32 {\n        &mut self.r#resp\n    }\n    /// Set the value of `resp`\n    #[inline]\n    pub fn set_resp(&mut self, value: i32) -> &mut Self {\n        self.r#resp = value.into();\n        self\n    }\n    /// Builder method that sets the value of `resp`. Useful for initializing the message.\n    #[inline]\n    pub fn init_resp(mut self, value: i32) -> Self {\n        self.r#resp = value.into();\n        self\n    }\n}\nimpl ::micropb::MessageDecode for CtrlMsg_Resp_GetMacAddress {\n    fn decode<IMPL_MICROPB_READ: ::micropb::PbRead>(\n        &mut self,\n        decoder: &mut ::micropb::PbDecoder<IMPL_MICROPB_READ>,\n        len: usize,\n    ) -> Result<(), ::micropb::DecodeError<IMPL_MICROPB_READ::Error>> {\n        use ::micropb::{FieldDecode, PbBytes, PbMap, PbString, PbVec};\n        let before = decoder.bytes_read();\n        while decoder.bytes_read() - before < len {\n            let tag = decoder.decode_tag()?;\n            match tag.field_num() {\n                0 => return Err(::micropb::DecodeError::ZeroField),\n                1u32 => {\n                    let mut_ref = &mut self.r#mac;\n                    {\n                        decoder.decode_bytes(mut_ref, ::micropb::Presence::Implicit)?;\n                    };\n                }\n                2u32 => {\n                    let mut_ref = &mut self.r#resp;\n                    {\n                        let val = decoder.decode_int32()?;\n                        let val_ref = &val;\n                        if *val_ref != 0 {\n                            *mut_ref = val as _;\n                        }\n                    };\n                }\n                _ => {\n                    decoder.skip_wire_value(tag.wire_type())?;\n                }\n            }\n        }\n        Ok(())\n    }\n}\nimpl ::micropb::MessageEncode for CtrlMsg_Resp_GetMacAddress {\n    const MAX_SIZE: ::core::result::Result<usize, &'static str> = 'msg: {\n        let mut max_size = 0;\n        match ::micropb::const_map!(::core::result::Result::Ok(33usize), |size| size + 1usize) {\n            ::core::result::Result::Ok(size) => {\n                max_size += size;\n            }\n            ::core::result::Result::Err(err) => {\n                break 'msg (::core::result::Result::<usize, _>::Err(err));\n            }\n        }\n        match ::micropb::const_map!(::core::result::Result::Ok(10usize), |size| size + 1usize) {\n            ::core::result::Result::Ok(size) => {\n                max_size += size;\n            }\n            ::core::result::Result::Err(err) => {\n                break 'msg (::core::result::Result::<usize, _>::Err(err));\n            }\n        }\n        ::core::result::Result::Ok(max_size)\n    };\n    fn encode<IMPL_MICROPB_WRITE: ::micropb::PbWrite>(\n        &self,\n        encoder: &mut ::micropb::PbEncoder<IMPL_MICROPB_WRITE>,\n    ) -> Result<(), IMPL_MICROPB_WRITE::Error> {\n        use ::micropb::{FieldEncode, PbMap};\n        {\n            let val_ref = &self.r#mac;\n            if !val_ref.is_empty() {\n                encoder.encode_varint32(10u32)?;\n                encoder.encode_bytes(val_ref)?;\n            }\n        }\n        {\n            let val_ref = &self.r#resp;\n            if *val_ref != 0 {\n                encoder.encode_varint32(16u32)?;\n                encoder.encode_int32(*val_ref as _)?;\n            }\n        }\n        Ok(())\n    }\n    fn compute_size(&self) -> usize {\n        use ::micropb::{FieldEncode, PbMap};\n        let mut size = 0;\n        {\n            let val_ref = &self.r#mac;\n            if !val_ref.is_empty() {\n                size += 1usize + ::micropb::size::sizeof_len_record(val_ref.len());\n            }\n        }\n        {\n            let val_ref = &self.r#resp;\n            if *val_ref != 0 {\n                size += 1usize + ::micropb::size::sizeof_int32(*val_ref as _);\n            }\n        }\n        size\n    }\n}\n#[derive(Debug, Default, PartialEq, Clone, Copy)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub struct CtrlMsg_Req_GetMode {}\nimpl CtrlMsg_Req_GetMode {}\nimpl ::micropb::MessageDecode for CtrlMsg_Req_GetMode {\n    fn decode<IMPL_MICROPB_READ: ::micropb::PbRead>(\n        &mut self,\n        decoder: &mut ::micropb::PbDecoder<IMPL_MICROPB_READ>,\n        len: usize,\n    ) -> Result<(), ::micropb::DecodeError<IMPL_MICROPB_READ::Error>> {\n        use ::micropb::{FieldDecode, PbBytes, PbMap, PbString, PbVec};\n        let before = decoder.bytes_read();\n        while decoder.bytes_read() - before < len {\n            let tag = decoder.decode_tag()?;\n            match tag.field_num() {\n                0 => return Err(::micropb::DecodeError::ZeroField),\n                _ => {\n                    decoder.skip_wire_value(tag.wire_type())?;\n                }\n            }\n        }\n        Ok(())\n    }\n}\nimpl ::micropb::MessageEncode for CtrlMsg_Req_GetMode {\n    const MAX_SIZE: ::core::result::Result<usize, &'static str> = 'msg: {\n        let mut max_size = 0;\n        ::core::result::Result::Ok(max_size)\n    };\n    fn encode<IMPL_MICROPB_WRITE: ::micropb::PbWrite>(\n        &self,\n        encoder: &mut ::micropb::PbEncoder<IMPL_MICROPB_WRITE>,\n    ) -> Result<(), IMPL_MICROPB_WRITE::Error> {\n        use ::micropb::{FieldEncode, PbMap};\n        Ok(())\n    }\n    fn compute_size(&self) -> usize {\n        use ::micropb::{FieldEncode, PbMap};\n        let mut size = 0;\n        size\n    }\n}\n#[derive(Debug, Default, PartialEq, Clone, Copy)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub struct CtrlMsg_Resp_GetMode {\n    pub r#mode: i32,\n    pub r#resp: i32,\n}\nimpl CtrlMsg_Resp_GetMode {\n    /// Return a reference to `mode`\n    #[inline]\n    pub fn r#mode(&self) -> &i32 {\n        &self.r#mode\n    }\n    /// Return a mutable reference to `mode`\n    #[inline]\n    pub fn mut_mode(&mut self) -> &mut i32 {\n        &mut self.r#mode\n    }\n    /// Set the value of `mode`\n    #[inline]\n    pub fn set_mode(&mut self, value: i32) -> &mut Self {\n        self.r#mode = value.into();\n        self\n    }\n    /// Builder method that sets the value of `mode`. Useful for initializing the message.\n    #[inline]\n    pub fn init_mode(mut self, value: i32) -> Self {\n        self.r#mode = value.into();\n        self\n    }\n    /// Return a reference to `resp`\n    #[inline]\n    pub fn r#resp(&self) -> &i32 {\n        &self.r#resp\n    }\n    /// Return a mutable reference to `resp`\n    #[inline]\n    pub fn mut_resp(&mut self) -> &mut i32 {\n        &mut self.r#resp\n    }\n    /// Set the value of `resp`\n    #[inline]\n    pub fn set_resp(&mut self, value: i32) -> &mut Self {\n        self.r#resp = value.into();\n        self\n    }\n    /// Builder method that sets the value of `resp`. Useful for initializing the message.\n    #[inline]\n    pub fn init_resp(mut self, value: i32) -> Self {\n        self.r#resp = value.into();\n        self\n    }\n}\nimpl ::micropb::MessageDecode for CtrlMsg_Resp_GetMode {\n    fn decode<IMPL_MICROPB_READ: ::micropb::PbRead>(\n        &mut self,\n        decoder: &mut ::micropb::PbDecoder<IMPL_MICROPB_READ>,\n        len: usize,\n    ) -> Result<(), ::micropb::DecodeError<IMPL_MICROPB_READ::Error>> {\n        use ::micropb::{FieldDecode, PbBytes, PbMap, PbString, PbVec};\n        let before = decoder.bytes_read();\n        while decoder.bytes_read() - before < len {\n            let tag = decoder.decode_tag()?;\n            match tag.field_num() {\n                0 => return Err(::micropb::DecodeError::ZeroField),\n                1u32 => {\n                    let mut_ref = &mut self.r#mode;\n                    {\n                        let val = decoder.decode_int32()?;\n                        let val_ref = &val;\n                        if *val_ref != 0 {\n                            *mut_ref = val as _;\n                        }\n                    };\n                }\n                2u32 => {\n                    let mut_ref = &mut self.r#resp;\n                    {\n                        let val = decoder.decode_int32()?;\n                        let val_ref = &val;\n                        if *val_ref != 0 {\n                            *mut_ref = val as _;\n                        }\n                    };\n                }\n                _ => {\n                    decoder.skip_wire_value(tag.wire_type())?;\n                }\n            }\n        }\n        Ok(())\n    }\n}\nimpl ::micropb::MessageEncode for CtrlMsg_Resp_GetMode {\n    const MAX_SIZE: ::core::result::Result<usize, &'static str> = 'msg: {\n        let mut max_size = 0;\n        match ::micropb::const_map!(::core::result::Result::Ok(10usize), |size| size + 1usize) {\n            ::core::result::Result::Ok(size) => {\n                max_size += size;\n            }\n            ::core::result::Result::Err(err) => {\n                break 'msg (::core::result::Result::<usize, _>::Err(err));\n            }\n        }\n        match ::micropb::const_map!(::core::result::Result::Ok(10usize), |size| size + 1usize) {\n            ::core::result::Result::Ok(size) => {\n                max_size += size;\n            }\n            ::core::result::Result::Err(err) => {\n                break 'msg (::core::result::Result::<usize, _>::Err(err));\n            }\n        }\n        ::core::result::Result::Ok(max_size)\n    };\n    fn encode<IMPL_MICROPB_WRITE: ::micropb::PbWrite>(\n        &self,\n        encoder: &mut ::micropb::PbEncoder<IMPL_MICROPB_WRITE>,\n    ) -> Result<(), IMPL_MICROPB_WRITE::Error> {\n        use ::micropb::{FieldEncode, PbMap};\n        {\n            let val_ref = &self.r#mode;\n            if *val_ref != 0 {\n                encoder.encode_varint32(8u32)?;\n                encoder.encode_int32(*val_ref as _)?;\n            }\n        }\n        {\n            let val_ref = &self.r#resp;\n            if *val_ref != 0 {\n                encoder.encode_varint32(16u32)?;\n                encoder.encode_int32(*val_ref as _)?;\n            }\n        }\n        Ok(())\n    }\n    fn compute_size(&self) -> usize {\n        use ::micropb::{FieldEncode, PbMap};\n        let mut size = 0;\n        {\n            let val_ref = &self.r#mode;\n            if *val_ref != 0 {\n                size += 1usize + ::micropb::size::sizeof_int32(*val_ref as _);\n            }\n        }\n        {\n            let val_ref = &self.r#resp;\n            if *val_ref != 0 {\n                size += 1usize + ::micropb::size::sizeof_int32(*val_ref as _);\n            }\n        }\n        size\n    }\n}\n#[derive(Debug, Default, PartialEq, Clone, Copy)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub struct CtrlMsg_Req_SetMode {\n    pub r#mode: i32,\n}\nimpl CtrlMsg_Req_SetMode {\n    /// Return a reference to `mode`\n    #[inline]\n    pub fn r#mode(&self) -> &i32 {\n        &self.r#mode\n    }\n    /// Return a mutable reference to `mode`\n    #[inline]\n    pub fn mut_mode(&mut self) -> &mut i32 {\n        &mut self.r#mode\n    }\n    /// Set the value of `mode`\n    #[inline]\n    pub fn set_mode(&mut self, value: i32) -> &mut Self {\n        self.r#mode = value.into();\n        self\n    }\n    /// Builder method that sets the value of `mode`. Useful for initializing the message.\n    #[inline]\n    pub fn init_mode(mut self, value: i32) -> Self {\n        self.r#mode = value.into();\n        self\n    }\n}\nimpl ::micropb::MessageDecode for CtrlMsg_Req_SetMode {\n    fn decode<IMPL_MICROPB_READ: ::micropb::PbRead>(\n        &mut self,\n        decoder: &mut ::micropb::PbDecoder<IMPL_MICROPB_READ>,\n        len: usize,\n    ) -> Result<(), ::micropb::DecodeError<IMPL_MICROPB_READ::Error>> {\n        use ::micropb::{FieldDecode, PbBytes, PbMap, PbString, PbVec};\n        let before = decoder.bytes_read();\n        while decoder.bytes_read() - before < len {\n            let tag = decoder.decode_tag()?;\n            match tag.field_num() {\n                0 => return Err(::micropb::DecodeError::ZeroField),\n                1u32 => {\n                    let mut_ref = &mut self.r#mode;\n                    {\n                        let val = decoder.decode_int32()?;\n                        let val_ref = &val;\n                        if *val_ref != 0 {\n                            *mut_ref = val as _;\n                        }\n                    };\n                }\n                _ => {\n                    decoder.skip_wire_value(tag.wire_type())?;\n                }\n            }\n        }\n        Ok(())\n    }\n}\nimpl ::micropb::MessageEncode for CtrlMsg_Req_SetMode {\n    const MAX_SIZE: ::core::result::Result<usize, &'static str> = 'msg: {\n        let mut max_size = 0;\n        match ::micropb::const_map!(::core::result::Result::Ok(10usize), |size| size + 1usize) {\n            ::core::result::Result::Ok(size) => {\n                max_size += size;\n            }\n            ::core::result::Result::Err(err) => {\n                break 'msg (::core::result::Result::<usize, _>::Err(err));\n            }\n        }\n        ::core::result::Result::Ok(max_size)\n    };\n    fn encode<IMPL_MICROPB_WRITE: ::micropb::PbWrite>(\n        &self,\n        encoder: &mut ::micropb::PbEncoder<IMPL_MICROPB_WRITE>,\n    ) -> Result<(), IMPL_MICROPB_WRITE::Error> {\n        use ::micropb::{FieldEncode, PbMap};\n        {\n            let val_ref = &self.r#mode;\n            if *val_ref != 0 {\n                encoder.encode_varint32(8u32)?;\n                encoder.encode_int32(*val_ref as _)?;\n            }\n        }\n        Ok(())\n    }\n    fn compute_size(&self) -> usize {\n        use ::micropb::{FieldEncode, PbMap};\n        let mut size = 0;\n        {\n            let val_ref = &self.r#mode;\n            if *val_ref != 0 {\n                size += 1usize + ::micropb::size::sizeof_int32(*val_ref as _);\n            }\n        }\n        size\n    }\n}\n#[derive(Debug, Default, PartialEq, Clone, Copy)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub struct CtrlMsg_Resp_SetMode {\n    pub r#resp: i32,\n}\nimpl CtrlMsg_Resp_SetMode {\n    /// Return a reference to `resp`\n    #[inline]\n    pub fn r#resp(&self) -> &i32 {\n        &self.r#resp\n    }\n    /// Return a mutable reference to `resp`\n    #[inline]\n    pub fn mut_resp(&mut self) -> &mut i32 {\n        &mut self.r#resp\n    }\n    /// Set the value of `resp`\n    #[inline]\n    pub fn set_resp(&mut self, value: i32) -> &mut Self {\n        self.r#resp = value.into();\n        self\n    }\n    /// Builder method that sets the value of `resp`. Useful for initializing the message.\n    #[inline]\n    pub fn init_resp(mut self, value: i32) -> Self {\n        self.r#resp = value.into();\n        self\n    }\n}\nimpl ::micropb::MessageDecode for CtrlMsg_Resp_SetMode {\n    fn decode<IMPL_MICROPB_READ: ::micropb::PbRead>(\n        &mut self,\n        decoder: &mut ::micropb::PbDecoder<IMPL_MICROPB_READ>,\n        len: usize,\n    ) -> Result<(), ::micropb::DecodeError<IMPL_MICROPB_READ::Error>> {\n        use ::micropb::{FieldDecode, PbBytes, PbMap, PbString, PbVec};\n        let before = decoder.bytes_read();\n        while decoder.bytes_read() - before < len {\n            let tag = decoder.decode_tag()?;\n            match tag.field_num() {\n                0 => return Err(::micropb::DecodeError::ZeroField),\n                1u32 => {\n                    let mut_ref = &mut self.r#resp;\n                    {\n                        let val = decoder.decode_int32()?;\n                        let val_ref = &val;\n                        if *val_ref != 0 {\n                            *mut_ref = val as _;\n                        }\n                    };\n                }\n                _ => {\n                    decoder.skip_wire_value(tag.wire_type())?;\n                }\n            }\n        }\n        Ok(())\n    }\n}\nimpl ::micropb::MessageEncode for CtrlMsg_Resp_SetMode {\n    const MAX_SIZE: ::core::result::Result<usize, &'static str> = 'msg: {\n        let mut max_size = 0;\n        match ::micropb::const_map!(::core::result::Result::Ok(10usize), |size| size + 1usize) {\n            ::core::result::Result::Ok(size) => {\n                max_size += size;\n            }\n            ::core::result::Result::Err(err) => {\n                break 'msg (::core::result::Result::<usize, _>::Err(err));\n            }\n        }\n        ::core::result::Result::Ok(max_size)\n    };\n    fn encode<IMPL_MICROPB_WRITE: ::micropb::PbWrite>(\n        &self,\n        encoder: &mut ::micropb::PbEncoder<IMPL_MICROPB_WRITE>,\n    ) -> Result<(), IMPL_MICROPB_WRITE::Error> {\n        use ::micropb::{FieldEncode, PbMap};\n        {\n            let val_ref = &self.r#resp;\n            if *val_ref != 0 {\n                encoder.encode_varint32(8u32)?;\n                encoder.encode_int32(*val_ref as _)?;\n            }\n        }\n        Ok(())\n    }\n    fn compute_size(&self) -> usize {\n        use ::micropb::{FieldEncode, PbMap};\n        let mut size = 0;\n        {\n            let val_ref = &self.r#resp;\n            if *val_ref != 0 {\n                size += 1usize + ::micropb::size::sizeof_int32(*val_ref as _);\n            }\n        }\n        size\n    }\n}\n#[derive(Debug, Default, PartialEq, Clone, Copy)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub struct CtrlMsg_Req_GetStatus {}\nimpl CtrlMsg_Req_GetStatus {}\nimpl ::micropb::MessageDecode for CtrlMsg_Req_GetStatus {\n    fn decode<IMPL_MICROPB_READ: ::micropb::PbRead>(\n        &mut self,\n        decoder: &mut ::micropb::PbDecoder<IMPL_MICROPB_READ>,\n        len: usize,\n    ) -> Result<(), ::micropb::DecodeError<IMPL_MICROPB_READ::Error>> {\n        use ::micropb::{FieldDecode, PbBytes, PbMap, PbString, PbVec};\n        let before = decoder.bytes_read();\n        while decoder.bytes_read() - before < len {\n            let tag = decoder.decode_tag()?;\n            match tag.field_num() {\n                0 => return Err(::micropb::DecodeError::ZeroField),\n                _ => {\n                    decoder.skip_wire_value(tag.wire_type())?;\n                }\n            }\n        }\n        Ok(())\n    }\n}\nimpl ::micropb::MessageEncode for CtrlMsg_Req_GetStatus {\n    const MAX_SIZE: ::core::result::Result<usize, &'static str> = 'msg: {\n        let mut max_size = 0;\n        ::core::result::Result::Ok(max_size)\n    };\n    fn encode<IMPL_MICROPB_WRITE: ::micropb::PbWrite>(\n        &self,\n        encoder: &mut ::micropb::PbEncoder<IMPL_MICROPB_WRITE>,\n    ) -> Result<(), IMPL_MICROPB_WRITE::Error> {\n        use ::micropb::{FieldEncode, PbMap};\n        Ok(())\n    }\n    fn compute_size(&self) -> usize {\n        use ::micropb::{FieldEncode, PbMap};\n        let mut size = 0;\n        size\n    }\n}\n#[derive(Debug, Default, PartialEq, Clone, Copy)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub struct CtrlMsg_Resp_GetStatus {\n    pub r#resp: i32,\n}\nimpl CtrlMsg_Resp_GetStatus {\n    /// Return a reference to `resp`\n    #[inline]\n    pub fn r#resp(&self) -> &i32 {\n        &self.r#resp\n    }\n    /// Return a mutable reference to `resp`\n    #[inline]\n    pub fn mut_resp(&mut self) -> &mut i32 {\n        &mut self.r#resp\n    }\n    /// Set the value of `resp`\n    #[inline]\n    pub fn set_resp(&mut self, value: i32) -> &mut Self {\n        self.r#resp = value.into();\n        self\n    }\n    /// Builder method that sets the value of `resp`. Useful for initializing the message.\n    #[inline]\n    pub fn init_resp(mut self, value: i32) -> Self {\n        self.r#resp = value.into();\n        self\n    }\n}\nimpl ::micropb::MessageDecode for CtrlMsg_Resp_GetStatus {\n    fn decode<IMPL_MICROPB_READ: ::micropb::PbRead>(\n        &mut self,\n        decoder: &mut ::micropb::PbDecoder<IMPL_MICROPB_READ>,\n        len: usize,\n    ) -> Result<(), ::micropb::DecodeError<IMPL_MICROPB_READ::Error>> {\n        use ::micropb::{FieldDecode, PbBytes, PbMap, PbString, PbVec};\n        let before = decoder.bytes_read();\n        while decoder.bytes_read() - before < len {\n            let tag = decoder.decode_tag()?;\n            match tag.field_num() {\n                0 => return Err(::micropb::DecodeError::ZeroField),\n                1u32 => {\n                    let mut_ref = &mut self.r#resp;\n                    {\n                        let val = decoder.decode_int32()?;\n                        let val_ref = &val;\n                        if *val_ref != 0 {\n                            *mut_ref = val as _;\n                        }\n                    };\n                }\n                _ => {\n                    decoder.skip_wire_value(tag.wire_type())?;\n                }\n            }\n        }\n        Ok(())\n    }\n}\nimpl ::micropb::MessageEncode for CtrlMsg_Resp_GetStatus {\n    const MAX_SIZE: ::core::result::Result<usize, &'static str> = 'msg: {\n        let mut max_size = 0;\n        match ::micropb::const_map!(::core::result::Result::Ok(10usize), |size| size + 1usize) {\n            ::core::result::Result::Ok(size) => {\n                max_size += size;\n            }\n            ::core::result::Result::Err(err) => {\n                break 'msg (::core::result::Result::<usize, _>::Err(err));\n            }\n        }\n        ::core::result::Result::Ok(max_size)\n    };\n    fn encode<IMPL_MICROPB_WRITE: ::micropb::PbWrite>(\n        &self,\n        encoder: &mut ::micropb::PbEncoder<IMPL_MICROPB_WRITE>,\n    ) -> Result<(), IMPL_MICROPB_WRITE::Error> {\n        use ::micropb::{FieldEncode, PbMap};\n        {\n            let val_ref = &self.r#resp;\n            if *val_ref != 0 {\n                encoder.encode_varint32(8u32)?;\n                encoder.encode_int32(*val_ref as _)?;\n            }\n        }\n        Ok(())\n    }\n    fn compute_size(&self) -> usize {\n        use ::micropb::{FieldEncode, PbMap};\n        let mut size = 0;\n        {\n            let val_ref = &self.r#resp;\n            if *val_ref != 0 {\n                size += 1usize + ::micropb::size::sizeof_int32(*val_ref as _);\n            }\n        }\n        size\n    }\n}\n#[derive(Debug, Default, PartialEq, Clone)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub struct CtrlMsg_Req_SetMacAddress {\n    pub r#mac: ::heapless::Vec<u8, 32>,\n    pub r#mode: i32,\n}\nimpl CtrlMsg_Req_SetMacAddress {\n    /// Return a reference to `mac`\n    #[inline]\n    pub fn r#mac(&self) -> &::heapless::Vec<u8, 32> {\n        &self.r#mac\n    }\n    /// Return a mutable reference to `mac`\n    #[inline]\n    pub fn mut_mac(&mut self) -> &mut ::heapless::Vec<u8, 32> {\n        &mut self.r#mac\n    }\n    /// Set the value of `mac`\n    #[inline]\n    pub fn set_mac(&mut self, value: ::heapless::Vec<u8, 32>) -> &mut Self {\n        self.r#mac = value.into();\n        self\n    }\n    /// Builder method that sets the value of `mac`. Useful for initializing the message.\n    #[inline]\n    pub fn init_mac(mut self, value: ::heapless::Vec<u8, 32>) -> Self {\n        self.r#mac = value.into();\n        self\n    }\n    /// Return a reference to `mode`\n    #[inline]\n    pub fn r#mode(&self) -> &i32 {\n        &self.r#mode\n    }\n    /// Return a mutable reference to `mode`\n    #[inline]\n    pub fn mut_mode(&mut self) -> &mut i32 {\n        &mut self.r#mode\n    }\n    /// Set the value of `mode`\n    #[inline]\n    pub fn set_mode(&mut self, value: i32) -> &mut Self {\n        self.r#mode = value.into();\n        self\n    }\n    /// Builder method that sets the value of `mode`. Useful for initializing the message.\n    #[inline]\n    pub fn init_mode(mut self, value: i32) -> Self {\n        self.r#mode = value.into();\n        self\n    }\n}\nimpl ::micropb::MessageDecode for CtrlMsg_Req_SetMacAddress {\n    fn decode<IMPL_MICROPB_READ: ::micropb::PbRead>(\n        &mut self,\n        decoder: &mut ::micropb::PbDecoder<IMPL_MICROPB_READ>,\n        len: usize,\n    ) -> Result<(), ::micropb::DecodeError<IMPL_MICROPB_READ::Error>> {\n        use ::micropb::{FieldDecode, PbBytes, PbMap, PbString, PbVec};\n        let before = decoder.bytes_read();\n        while decoder.bytes_read() - before < len {\n            let tag = decoder.decode_tag()?;\n            match tag.field_num() {\n                0 => return Err(::micropb::DecodeError::ZeroField),\n                1u32 => {\n                    let mut_ref = &mut self.r#mac;\n                    {\n                        decoder.decode_bytes(mut_ref, ::micropb::Presence::Implicit)?;\n                    };\n                }\n                2u32 => {\n                    let mut_ref = &mut self.r#mode;\n                    {\n                        let val = decoder.decode_int32()?;\n                        let val_ref = &val;\n                        if *val_ref != 0 {\n                            *mut_ref = val as _;\n                        }\n                    };\n                }\n                _ => {\n                    decoder.skip_wire_value(tag.wire_type())?;\n                }\n            }\n        }\n        Ok(())\n    }\n}\nimpl ::micropb::MessageEncode for CtrlMsg_Req_SetMacAddress {\n    const MAX_SIZE: ::core::result::Result<usize, &'static str> = 'msg: {\n        let mut max_size = 0;\n        match ::micropb::const_map!(::core::result::Result::Ok(33usize), |size| size + 1usize) {\n            ::core::result::Result::Ok(size) => {\n                max_size += size;\n            }\n            ::core::result::Result::Err(err) => {\n                break 'msg (::core::result::Result::<usize, _>::Err(err));\n            }\n        }\n        match ::micropb::const_map!(::core::result::Result::Ok(10usize), |size| size + 1usize) {\n            ::core::result::Result::Ok(size) => {\n                max_size += size;\n            }\n            ::core::result::Result::Err(err) => {\n                break 'msg (::core::result::Result::<usize, _>::Err(err));\n            }\n        }\n        ::core::result::Result::Ok(max_size)\n    };\n    fn encode<IMPL_MICROPB_WRITE: ::micropb::PbWrite>(\n        &self,\n        encoder: &mut ::micropb::PbEncoder<IMPL_MICROPB_WRITE>,\n    ) -> Result<(), IMPL_MICROPB_WRITE::Error> {\n        use ::micropb::{FieldEncode, PbMap};\n        {\n            let val_ref = &self.r#mac;\n            if !val_ref.is_empty() {\n                encoder.encode_varint32(10u32)?;\n                encoder.encode_bytes(val_ref)?;\n            }\n        }\n        {\n            let val_ref = &self.r#mode;\n            if *val_ref != 0 {\n                encoder.encode_varint32(16u32)?;\n                encoder.encode_int32(*val_ref as _)?;\n            }\n        }\n        Ok(())\n    }\n    fn compute_size(&self) -> usize {\n        use ::micropb::{FieldEncode, PbMap};\n        let mut size = 0;\n        {\n            let val_ref = &self.r#mac;\n            if !val_ref.is_empty() {\n                size += 1usize + ::micropb::size::sizeof_len_record(val_ref.len());\n            }\n        }\n        {\n            let val_ref = &self.r#mode;\n            if *val_ref != 0 {\n                size += 1usize + ::micropb::size::sizeof_int32(*val_ref as _);\n            }\n        }\n        size\n    }\n}\n#[derive(Debug, Default, PartialEq, Clone, Copy)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub struct CtrlMsg_Resp_SetMacAddress {\n    pub r#resp: i32,\n}\nimpl CtrlMsg_Resp_SetMacAddress {\n    /// Return a reference to `resp`\n    #[inline]\n    pub fn r#resp(&self) -> &i32 {\n        &self.r#resp\n    }\n    /// Return a mutable reference to `resp`\n    #[inline]\n    pub fn mut_resp(&mut self) -> &mut i32 {\n        &mut self.r#resp\n    }\n    /// Set the value of `resp`\n    #[inline]\n    pub fn set_resp(&mut self, value: i32) -> &mut Self {\n        self.r#resp = value.into();\n        self\n    }\n    /// Builder method that sets the value of `resp`. Useful for initializing the message.\n    #[inline]\n    pub fn init_resp(mut self, value: i32) -> Self {\n        self.r#resp = value.into();\n        self\n    }\n}\nimpl ::micropb::MessageDecode for CtrlMsg_Resp_SetMacAddress {\n    fn decode<IMPL_MICROPB_READ: ::micropb::PbRead>(\n        &mut self,\n        decoder: &mut ::micropb::PbDecoder<IMPL_MICROPB_READ>,\n        len: usize,\n    ) -> Result<(), ::micropb::DecodeError<IMPL_MICROPB_READ::Error>> {\n        use ::micropb::{FieldDecode, PbBytes, PbMap, PbString, PbVec};\n        let before = decoder.bytes_read();\n        while decoder.bytes_read() - before < len {\n            let tag = decoder.decode_tag()?;\n            match tag.field_num() {\n                0 => return Err(::micropb::DecodeError::ZeroField),\n                1u32 => {\n                    let mut_ref = &mut self.r#resp;\n                    {\n                        let val = decoder.decode_int32()?;\n                        let val_ref = &val;\n                        if *val_ref != 0 {\n                            *mut_ref = val as _;\n                        }\n                    };\n                }\n                _ => {\n                    decoder.skip_wire_value(tag.wire_type())?;\n                }\n            }\n        }\n        Ok(())\n    }\n}\nimpl ::micropb::MessageEncode for CtrlMsg_Resp_SetMacAddress {\n    const MAX_SIZE: ::core::result::Result<usize, &'static str> = 'msg: {\n        let mut max_size = 0;\n        match ::micropb::const_map!(::core::result::Result::Ok(10usize), |size| size + 1usize) {\n            ::core::result::Result::Ok(size) => {\n                max_size += size;\n            }\n            ::core::result::Result::Err(err) => {\n                break 'msg (::core::result::Result::<usize, _>::Err(err));\n            }\n        }\n        ::core::result::Result::Ok(max_size)\n    };\n    fn encode<IMPL_MICROPB_WRITE: ::micropb::PbWrite>(\n        &self,\n        encoder: &mut ::micropb::PbEncoder<IMPL_MICROPB_WRITE>,\n    ) -> Result<(), IMPL_MICROPB_WRITE::Error> {\n        use ::micropb::{FieldEncode, PbMap};\n        {\n            let val_ref = &self.r#resp;\n            if *val_ref != 0 {\n                encoder.encode_varint32(8u32)?;\n                encoder.encode_int32(*val_ref as _)?;\n            }\n        }\n        Ok(())\n    }\n    fn compute_size(&self) -> usize {\n        use ::micropb::{FieldEncode, PbMap};\n        let mut size = 0;\n        {\n            let val_ref = &self.r#resp;\n            if *val_ref != 0 {\n                size += 1usize + ::micropb::size::sizeof_int32(*val_ref as _);\n            }\n        }\n        size\n    }\n}\n#[derive(Debug, Default, PartialEq, Clone, Copy)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub struct CtrlMsg_Req_GetAPConfig {}\nimpl CtrlMsg_Req_GetAPConfig {}\nimpl ::micropb::MessageDecode for CtrlMsg_Req_GetAPConfig {\n    fn decode<IMPL_MICROPB_READ: ::micropb::PbRead>(\n        &mut self,\n        decoder: &mut ::micropb::PbDecoder<IMPL_MICROPB_READ>,\n        len: usize,\n    ) -> Result<(), ::micropb::DecodeError<IMPL_MICROPB_READ::Error>> {\n        use ::micropb::{FieldDecode, PbBytes, PbMap, PbString, PbVec};\n        let before = decoder.bytes_read();\n        while decoder.bytes_read() - before < len {\n            let tag = decoder.decode_tag()?;\n            match tag.field_num() {\n                0 => return Err(::micropb::DecodeError::ZeroField),\n                _ => {\n                    decoder.skip_wire_value(tag.wire_type())?;\n                }\n            }\n        }\n        Ok(())\n    }\n}\nimpl ::micropb::MessageEncode for CtrlMsg_Req_GetAPConfig {\n    const MAX_SIZE: ::core::result::Result<usize, &'static str> = 'msg: {\n        let mut max_size = 0;\n        ::core::result::Result::Ok(max_size)\n    };\n    fn encode<IMPL_MICROPB_WRITE: ::micropb::PbWrite>(\n        &self,\n        encoder: &mut ::micropb::PbEncoder<IMPL_MICROPB_WRITE>,\n    ) -> Result<(), IMPL_MICROPB_WRITE::Error> {\n        use ::micropb::{FieldEncode, PbMap};\n        Ok(())\n    }\n    fn compute_size(&self) -> usize {\n        use ::micropb::{FieldEncode, PbMap};\n        let mut size = 0;\n        size\n    }\n}\n#[derive(Debug, Default, PartialEq, Clone)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub struct CtrlMsg_Resp_GetAPConfig {\n    pub r#ssid: ::heapless::Vec<u8, 32>,\n    pub r#bssid: ::heapless::Vec<u8, 32>,\n    pub r#rssi: i32,\n    pub r#chnl: i32,\n    pub r#sec_prot: Ctrl_WifiSecProt,\n    pub r#resp: i32,\n    pub r#band_mode: i32,\n}\nimpl CtrlMsg_Resp_GetAPConfig {\n    /// Return a reference to `ssid`\n    #[inline]\n    pub fn r#ssid(&self) -> &::heapless::Vec<u8, 32> {\n        &self.r#ssid\n    }\n    /// Return a mutable reference to `ssid`\n    #[inline]\n    pub fn mut_ssid(&mut self) -> &mut ::heapless::Vec<u8, 32> {\n        &mut self.r#ssid\n    }\n    /// Set the value of `ssid`\n    #[inline]\n    pub fn set_ssid(&mut self, value: ::heapless::Vec<u8, 32>) -> &mut Self {\n        self.r#ssid = value.into();\n        self\n    }\n    /// Builder method that sets the value of `ssid`. Useful for initializing the message.\n    #[inline]\n    pub fn init_ssid(mut self, value: ::heapless::Vec<u8, 32>) -> Self {\n        self.r#ssid = value.into();\n        self\n    }\n    /// Return a reference to `bssid`\n    #[inline]\n    pub fn r#bssid(&self) -> &::heapless::Vec<u8, 32> {\n        &self.r#bssid\n    }\n    /// Return a mutable reference to `bssid`\n    #[inline]\n    pub fn mut_bssid(&mut self) -> &mut ::heapless::Vec<u8, 32> {\n        &mut self.r#bssid\n    }\n    /// Set the value of `bssid`\n    #[inline]\n    pub fn set_bssid(&mut self, value: ::heapless::Vec<u8, 32>) -> &mut Self {\n        self.r#bssid = value.into();\n        self\n    }\n    /// Builder method that sets the value of `bssid`. Useful for initializing the message.\n    #[inline]\n    pub fn init_bssid(mut self, value: ::heapless::Vec<u8, 32>) -> Self {\n        self.r#bssid = value.into();\n        self\n    }\n    /// Return a reference to `rssi`\n    #[inline]\n    pub fn r#rssi(&self) -> &i32 {\n        &self.r#rssi\n    }\n    /// Return a mutable reference to `rssi`\n    #[inline]\n    pub fn mut_rssi(&mut self) -> &mut i32 {\n        &mut self.r#rssi\n    }\n    /// Set the value of `rssi`\n    #[inline]\n    pub fn set_rssi(&mut self, value: i32) -> &mut Self {\n        self.r#rssi = value.into();\n        self\n    }\n    /// Builder method that sets the value of `rssi`. Useful for initializing the message.\n    #[inline]\n    pub fn init_rssi(mut self, value: i32) -> Self {\n        self.r#rssi = value.into();\n        self\n    }\n    /// Return a reference to `chnl`\n    #[inline]\n    pub fn r#chnl(&self) -> &i32 {\n        &self.r#chnl\n    }\n    /// Return a mutable reference to `chnl`\n    #[inline]\n    pub fn mut_chnl(&mut self) -> &mut i32 {\n        &mut self.r#chnl\n    }\n    /// Set the value of `chnl`\n    #[inline]\n    pub fn set_chnl(&mut self, value: i32) -> &mut Self {\n        self.r#chnl = value.into();\n        self\n    }\n    /// Builder method that sets the value of `chnl`. Useful for initializing the message.\n    #[inline]\n    pub fn init_chnl(mut self, value: i32) -> Self {\n        self.r#chnl = value.into();\n        self\n    }\n    /// Return a reference to `sec_prot`\n    #[inline]\n    pub fn r#sec_prot(&self) -> &Ctrl_WifiSecProt {\n        &self.r#sec_prot\n    }\n    /// Return a mutable reference to `sec_prot`\n    #[inline]\n    pub fn mut_sec_prot(&mut self) -> &mut Ctrl_WifiSecProt {\n        &mut self.r#sec_prot\n    }\n    /// Set the value of `sec_prot`\n    #[inline]\n    pub fn set_sec_prot(&mut self, value: Ctrl_WifiSecProt) -> &mut Self {\n        self.r#sec_prot = value.into();\n        self\n    }\n    /// Builder method that sets the value of `sec_prot`. Useful for initializing the message.\n    #[inline]\n    pub fn init_sec_prot(mut self, value: Ctrl_WifiSecProt) -> Self {\n        self.r#sec_prot = value.into();\n        self\n    }\n    /// Return a reference to `resp`\n    #[inline]\n    pub fn r#resp(&self) -> &i32 {\n        &self.r#resp\n    }\n    /// Return a mutable reference to `resp`\n    #[inline]\n    pub fn mut_resp(&mut self) -> &mut i32 {\n        &mut self.r#resp\n    }\n    /// Set the value of `resp`\n    #[inline]\n    pub fn set_resp(&mut self, value: i32) -> &mut Self {\n        self.r#resp = value.into();\n        self\n    }\n    /// Builder method that sets the value of `resp`. Useful for initializing the message.\n    #[inline]\n    pub fn init_resp(mut self, value: i32) -> Self {\n        self.r#resp = value.into();\n        self\n    }\n    /// Return a reference to `band_mode`\n    #[inline]\n    pub fn r#band_mode(&self) -> &i32 {\n        &self.r#band_mode\n    }\n    /// Return a mutable reference to `band_mode`\n    #[inline]\n    pub fn mut_band_mode(&mut self) -> &mut i32 {\n        &mut self.r#band_mode\n    }\n    /// Set the value of `band_mode`\n    #[inline]\n    pub fn set_band_mode(&mut self, value: i32) -> &mut Self {\n        self.r#band_mode = value.into();\n        self\n    }\n    /// Builder method that sets the value of `band_mode`. Useful for initializing the message.\n    #[inline]\n    pub fn init_band_mode(mut self, value: i32) -> Self {\n        self.r#band_mode = value.into();\n        self\n    }\n}\nimpl ::micropb::MessageDecode for CtrlMsg_Resp_GetAPConfig {\n    fn decode<IMPL_MICROPB_READ: ::micropb::PbRead>(\n        &mut self,\n        decoder: &mut ::micropb::PbDecoder<IMPL_MICROPB_READ>,\n        len: usize,\n    ) -> Result<(), ::micropb::DecodeError<IMPL_MICROPB_READ::Error>> {\n        use ::micropb::{FieldDecode, PbBytes, PbMap, PbString, PbVec};\n        let before = decoder.bytes_read();\n        while decoder.bytes_read() - before < len {\n            let tag = decoder.decode_tag()?;\n            match tag.field_num() {\n                0 => return Err(::micropb::DecodeError::ZeroField),\n                1u32 => {\n                    let mut_ref = &mut self.r#ssid;\n                    {\n                        decoder.decode_bytes(mut_ref, ::micropb::Presence::Implicit)?;\n                    };\n                }\n                2u32 => {\n                    let mut_ref = &mut self.r#bssid;\n                    {\n                        decoder.decode_bytes(mut_ref, ::micropb::Presence::Implicit)?;\n                    };\n                }\n                3u32 => {\n                    let mut_ref = &mut self.r#rssi;\n                    {\n                        let val = decoder.decode_int32()?;\n                        let val_ref = &val;\n                        if *val_ref != 0 {\n                            *mut_ref = val as _;\n                        }\n                    };\n                }\n                4u32 => {\n                    let mut_ref = &mut self.r#chnl;\n                    {\n                        let val = decoder.decode_int32()?;\n                        let val_ref = &val;\n                        if *val_ref != 0 {\n                            *mut_ref = val as _;\n                        }\n                    };\n                }\n                5u32 => {\n                    let mut_ref = &mut self.r#sec_prot;\n                    {\n                        let val = decoder.decode_int32().map(|n| Ctrl_WifiSecProt(n as _))?;\n                        let val_ref = &val;\n                        if val_ref.0 != 0 {\n                            *mut_ref = val as _;\n                        }\n                    };\n                }\n                6u32 => {\n                    let mut_ref = &mut self.r#resp;\n                    {\n                        let val = decoder.decode_int32()?;\n                        let val_ref = &val;\n                        if *val_ref != 0 {\n                            *mut_ref = val as _;\n                        }\n                    };\n                }\n                7u32 => {\n                    let mut_ref = &mut self.r#band_mode;\n                    {\n                        let val = decoder.decode_int32()?;\n                        let val_ref = &val;\n                        if *val_ref != 0 {\n                            *mut_ref = val as _;\n                        }\n                    };\n                }\n                _ => {\n                    decoder.skip_wire_value(tag.wire_type())?;\n                }\n            }\n        }\n        Ok(())\n    }\n}\nimpl ::micropb::MessageEncode for CtrlMsg_Resp_GetAPConfig {\n    const MAX_SIZE: ::core::result::Result<usize, &'static str> = 'msg: {\n        let mut max_size = 0;\n        match ::micropb::const_map!(::core::result::Result::Ok(33usize), |size| size + 1usize) {\n            ::core::result::Result::Ok(size) => {\n                max_size += size;\n            }\n            ::core::result::Result::Err(err) => {\n                break 'msg (::core::result::Result::<usize, _>::Err(err));\n            }\n        }\n        match ::micropb::const_map!(::core::result::Result::Ok(33usize), |size| size + 1usize) {\n            ::core::result::Result::Ok(size) => {\n                max_size += size;\n            }\n            ::core::result::Result::Err(err) => {\n                break 'msg (::core::result::Result::<usize, _>::Err(err));\n            }\n        }\n        match ::micropb::const_map!(::core::result::Result::Ok(10usize), |size| size + 1usize) {\n            ::core::result::Result::Ok(size) => {\n                max_size += size;\n            }\n            ::core::result::Result::Err(err) => {\n                break 'msg (::core::result::Result::<usize, _>::Err(err));\n            }\n        }\n        match ::micropb::const_map!(::core::result::Result::Ok(10usize), |size| size + 1usize) {\n            ::core::result::Result::Ok(size) => {\n                max_size += size;\n            }\n            ::core::result::Result::Err(err) => {\n                break 'msg (::core::result::Result::<usize, _>::Err(err));\n            }\n        }\n        match ::micropb::const_map!(::core::result::Result::Ok(Ctrl_WifiSecProt::_MAX_SIZE), |size| size\n            + 1usize)\n        {\n            ::core::result::Result::Ok(size) => {\n                max_size += size;\n            }\n            ::core::result::Result::Err(err) => {\n                break 'msg (::core::result::Result::<usize, _>::Err(err));\n            }\n        }\n        match ::micropb::const_map!(::core::result::Result::Ok(10usize), |size| size + 1usize) {\n            ::core::result::Result::Ok(size) => {\n                max_size += size;\n            }\n            ::core::result::Result::Err(err) => {\n                break 'msg (::core::result::Result::<usize, _>::Err(err));\n            }\n        }\n        match ::micropb::const_map!(::core::result::Result::Ok(10usize), |size| size + 1usize) {\n            ::core::result::Result::Ok(size) => {\n                max_size += size;\n            }\n            ::core::result::Result::Err(err) => {\n                break 'msg (::core::result::Result::<usize, _>::Err(err));\n            }\n        }\n        ::core::result::Result::Ok(max_size)\n    };\n    fn encode<IMPL_MICROPB_WRITE: ::micropb::PbWrite>(\n        &self,\n        encoder: &mut ::micropb::PbEncoder<IMPL_MICROPB_WRITE>,\n    ) -> Result<(), IMPL_MICROPB_WRITE::Error> {\n        use ::micropb::{FieldEncode, PbMap};\n        {\n            let val_ref = &self.r#ssid;\n            if !val_ref.is_empty() {\n                encoder.encode_varint32(10u32)?;\n                encoder.encode_bytes(val_ref)?;\n            }\n        }\n        {\n            let val_ref = &self.r#bssid;\n            if !val_ref.is_empty() {\n                encoder.encode_varint32(18u32)?;\n                encoder.encode_bytes(val_ref)?;\n            }\n        }\n        {\n            let val_ref = &self.r#rssi;\n            if *val_ref != 0 {\n                encoder.encode_varint32(24u32)?;\n                encoder.encode_int32(*val_ref as _)?;\n            }\n        }\n        {\n            let val_ref = &self.r#chnl;\n            if *val_ref != 0 {\n                encoder.encode_varint32(32u32)?;\n                encoder.encode_int32(*val_ref as _)?;\n            }\n        }\n        {\n            let val_ref = &self.r#sec_prot;\n            if val_ref.0 != 0 {\n                encoder.encode_varint32(40u32)?;\n                encoder.encode_int32(val_ref.0 as _)?;\n            }\n        }\n        {\n            let val_ref = &self.r#resp;\n            if *val_ref != 0 {\n                encoder.encode_varint32(48u32)?;\n                encoder.encode_int32(*val_ref as _)?;\n            }\n        }\n        {\n            let val_ref = &self.r#band_mode;\n            if *val_ref != 0 {\n                encoder.encode_varint32(56u32)?;\n                encoder.encode_int32(*val_ref as _)?;\n            }\n        }\n        Ok(())\n    }\n    fn compute_size(&self) -> usize {\n        use ::micropb::{FieldEncode, PbMap};\n        let mut size = 0;\n        {\n            let val_ref = &self.r#ssid;\n            if !val_ref.is_empty() {\n                size += 1usize + ::micropb::size::sizeof_len_record(val_ref.len());\n            }\n        }\n        {\n            let val_ref = &self.r#bssid;\n            if !val_ref.is_empty() {\n                size += 1usize + ::micropb::size::sizeof_len_record(val_ref.len());\n            }\n        }\n        {\n            let val_ref = &self.r#rssi;\n            if *val_ref != 0 {\n                size += 1usize + ::micropb::size::sizeof_int32(*val_ref as _);\n            }\n        }\n        {\n            let val_ref = &self.r#chnl;\n            if *val_ref != 0 {\n                size += 1usize + ::micropb::size::sizeof_int32(*val_ref as _);\n            }\n        }\n        {\n            let val_ref = &self.r#sec_prot;\n            if val_ref.0 != 0 {\n                size += 1usize + ::micropb::size::sizeof_int32(val_ref.0 as _);\n            }\n        }\n        {\n            let val_ref = &self.r#resp;\n            if *val_ref != 0 {\n                size += 1usize + ::micropb::size::sizeof_int32(*val_ref as _);\n            }\n        }\n        {\n            let val_ref = &self.r#band_mode;\n            if *val_ref != 0 {\n                size += 1usize + ::micropb::size::sizeof_int32(*val_ref as _);\n            }\n        }\n        size\n    }\n}\n#[derive(Debug, Default, PartialEq, Clone)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub struct CtrlMsg_Req_ConnectAP {\n    pub r#ssid: ::heapless::String<32>,\n    pub r#pwd: ::heapless::String<32>,\n    pub r#bssid: ::heapless::String<32>,\n    pub r#is_wpa3_supported: bool,\n    pub r#listen_interval: i32,\n    pub r#band_mode: i32,\n}\nimpl CtrlMsg_Req_ConnectAP {\n    /// Return a reference to `ssid`\n    #[inline]\n    pub fn r#ssid(&self) -> &::heapless::String<32> {\n        &self.r#ssid\n    }\n    /// Return a mutable reference to `ssid`\n    #[inline]\n    pub fn mut_ssid(&mut self) -> &mut ::heapless::String<32> {\n        &mut self.r#ssid\n    }\n    /// Set the value of `ssid`\n    #[inline]\n    pub fn set_ssid(&mut self, value: ::heapless::String<32>) -> &mut Self {\n        self.r#ssid = value.into();\n        self\n    }\n    /// Builder method that sets the value of `ssid`. Useful for initializing the message.\n    #[inline]\n    pub fn init_ssid(mut self, value: ::heapless::String<32>) -> Self {\n        self.r#ssid = value.into();\n        self\n    }\n    /// Return a reference to `pwd`\n    #[inline]\n    pub fn r#pwd(&self) -> &::heapless::String<32> {\n        &self.r#pwd\n    }\n    /// Return a mutable reference to `pwd`\n    #[inline]\n    pub fn mut_pwd(&mut self) -> &mut ::heapless::String<32> {\n        &mut self.r#pwd\n    }\n    /// Set the value of `pwd`\n    #[inline]\n    pub fn set_pwd(&mut self, value: ::heapless::String<32>) -> &mut Self {\n        self.r#pwd = value.into();\n        self\n    }\n    /// Builder method that sets the value of `pwd`. Useful for initializing the message.\n    #[inline]\n    pub fn init_pwd(mut self, value: ::heapless::String<32>) -> Self {\n        self.r#pwd = value.into();\n        self\n    }\n    /// Return a reference to `bssid`\n    #[inline]\n    pub fn r#bssid(&self) -> &::heapless::String<32> {\n        &self.r#bssid\n    }\n    /// Return a mutable reference to `bssid`\n    #[inline]\n    pub fn mut_bssid(&mut self) -> &mut ::heapless::String<32> {\n        &mut self.r#bssid\n    }\n    /// Set the value of `bssid`\n    #[inline]\n    pub fn set_bssid(&mut self, value: ::heapless::String<32>) -> &mut Self {\n        self.r#bssid = value.into();\n        self\n    }\n    /// Builder method that sets the value of `bssid`. Useful for initializing the message.\n    #[inline]\n    pub fn init_bssid(mut self, value: ::heapless::String<32>) -> Self {\n        self.r#bssid = value.into();\n        self\n    }\n    /// Return a reference to `is_wpa3_supported`\n    #[inline]\n    pub fn r#is_wpa3_supported(&self) -> &bool {\n        &self.r#is_wpa3_supported\n    }\n    /// Return a mutable reference to `is_wpa3_supported`\n    #[inline]\n    pub fn mut_is_wpa3_supported(&mut self) -> &mut bool {\n        &mut self.r#is_wpa3_supported\n    }\n    /// Set the value of `is_wpa3_supported`\n    #[inline]\n    pub fn set_is_wpa3_supported(&mut self, value: bool) -> &mut Self {\n        self.r#is_wpa3_supported = value.into();\n        self\n    }\n    /// Builder method that sets the value of `is_wpa3_supported`. Useful for initializing the message.\n    #[inline]\n    pub fn init_is_wpa3_supported(mut self, value: bool) -> Self {\n        self.r#is_wpa3_supported = value.into();\n        self\n    }\n    /// Return a reference to `listen_interval`\n    #[inline]\n    pub fn r#listen_interval(&self) -> &i32 {\n        &self.r#listen_interval\n    }\n    /// Return a mutable reference to `listen_interval`\n    #[inline]\n    pub fn mut_listen_interval(&mut self) -> &mut i32 {\n        &mut self.r#listen_interval\n    }\n    /// Set the value of `listen_interval`\n    #[inline]\n    pub fn set_listen_interval(&mut self, value: i32) -> &mut Self {\n        self.r#listen_interval = value.into();\n        self\n    }\n    /// Builder method that sets the value of `listen_interval`. Useful for initializing the message.\n    #[inline]\n    pub fn init_listen_interval(mut self, value: i32) -> Self {\n        self.r#listen_interval = value.into();\n        self\n    }\n    /// Return a reference to `band_mode`\n    #[inline]\n    pub fn r#band_mode(&self) -> &i32 {\n        &self.r#band_mode\n    }\n    /// Return a mutable reference to `band_mode`\n    #[inline]\n    pub fn mut_band_mode(&mut self) -> &mut i32 {\n        &mut self.r#band_mode\n    }\n    /// Set the value of `band_mode`\n    #[inline]\n    pub fn set_band_mode(&mut self, value: i32) -> &mut Self {\n        self.r#band_mode = value.into();\n        self\n    }\n    /// Builder method that sets the value of `band_mode`. Useful for initializing the message.\n    #[inline]\n    pub fn init_band_mode(mut self, value: i32) -> Self {\n        self.r#band_mode = value.into();\n        self\n    }\n}\nimpl ::micropb::MessageDecode for CtrlMsg_Req_ConnectAP {\n    fn decode<IMPL_MICROPB_READ: ::micropb::PbRead>(\n        &mut self,\n        decoder: &mut ::micropb::PbDecoder<IMPL_MICROPB_READ>,\n        len: usize,\n    ) -> Result<(), ::micropb::DecodeError<IMPL_MICROPB_READ::Error>> {\n        use ::micropb::{FieldDecode, PbBytes, PbMap, PbString, PbVec};\n        let before = decoder.bytes_read();\n        while decoder.bytes_read() - before < len {\n            let tag = decoder.decode_tag()?;\n            match tag.field_num() {\n                0 => return Err(::micropb::DecodeError::ZeroField),\n                1u32 => {\n                    let mut_ref = &mut self.r#ssid;\n                    {\n                        decoder.decode_string(mut_ref, ::micropb::Presence::Implicit)?;\n                    };\n                }\n                2u32 => {\n                    let mut_ref = &mut self.r#pwd;\n                    {\n                        decoder.decode_string(mut_ref, ::micropb::Presence::Implicit)?;\n                    };\n                }\n                3u32 => {\n                    let mut_ref = &mut self.r#bssid;\n                    {\n                        decoder.decode_string(mut_ref, ::micropb::Presence::Implicit)?;\n                    };\n                }\n                4u32 => {\n                    let mut_ref = &mut self.r#is_wpa3_supported;\n                    {\n                        let val = decoder.decode_bool()?;\n                        let val_ref = &val;\n                        if *val_ref {\n                            *mut_ref = val as _;\n                        }\n                    };\n                }\n                5u32 => {\n                    let mut_ref = &mut self.r#listen_interval;\n                    {\n                        let val = decoder.decode_int32()?;\n                        let val_ref = &val;\n                        if *val_ref != 0 {\n                            *mut_ref = val as _;\n                        }\n                    };\n                }\n                6u32 => {\n                    let mut_ref = &mut self.r#band_mode;\n                    {\n                        let val = decoder.decode_int32()?;\n                        let val_ref = &val;\n                        if *val_ref != 0 {\n                            *mut_ref = val as _;\n                        }\n                    };\n                }\n                _ => {\n                    decoder.skip_wire_value(tag.wire_type())?;\n                }\n            }\n        }\n        Ok(())\n    }\n}\nimpl ::micropb::MessageEncode for CtrlMsg_Req_ConnectAP {\n    const MAX_SIZE: ::core::result::Result<usize, &'static str> = 'msg: {\n        let mut max_size = 0;\n        match ::micropb::const_map!(::core::result::Result::Ok(33usize), |size| size + 1usize) {\n            ::core::result::Result::Ok(size) => {\n                max_size += size;\n            }\n            ::core::result::Result::Err(err) => {\n                break 'msg (::core::result::Result::<usize, _>::Err(err));\n            }\n        }\n        match ::micropb::const_map!(::core::result::Result::Ok(33usize), |size| size + 1usize) {\n            ::core::result::Result::Ok(size) => {\n                max_size += size;\n            }\n            ::core::result::Result::Err(err) => {\n                break 'msg (::core::result::Result::<usize, _>::Err(err));\n            }\n        }\n        match ::micropb::const_map!(::core::result::Result::Ok(33usize), |size| size + 1usize) {\n            ::core::result::Result::Ok(size) => {\n                max_size += size;\n            }\n            ::core::result::Result::Err(err) => {\n                break 'msg (::core::result::Result::<usize, _>::Err(err));\n            }\n        }\n        match ::micropb::const_map!(::core::result::Result::Ok(1usize), |size| size + 1usize) {\n            ::core::result::Result::Ok(size) => {\n                max_size += size;\n            }\n            ::core::result::Result::Err(err) => {\n                break 'msg (::core::result::Result::<usize, _>::Err(err));\n            }\n        }\n        match ::micropb::const_map!(::core::result::Result::Ok(10usize), |size| size + 1usize) {\n            ::core::result::Result::Ok(size) => {\n                max_size += size;\n            }\n            ::core::result::Result::Err(err) => {\n                break 'msg (::core::result::Result::<usize, _>::Err(err));\n            }\n        }\n        match ::micropb::const_map!(::core::result::Result::Ok(10usize), |size| size + 1usize) {\n            ::core::result::Result::Ok(size) => {\n                max_size += size;\n            }\n            ::core::result::Result::Err(err) => {\n                break 'msg (::core::result::Result::<usize, _>::Err(err));\n            }\n        }\n        ::core::result::Result::Ok(max_size)\n    };\n    fn encode<IMPL_MICROPB_WRITE: ::micropb::PbWrite>(\n        &self,\n        encoder: &mut ::micropb::PbEncoder<IMPL_MICROPB_WRITE>,\n    ) -> Result<(), IMPL_MICROPB_WRITE::Error> {\n        use ::micropb::{FieldEncode, PbMap};\n        {\n            let val_ref = &self.r#ssid;\n            if !val_ref.is_empty() {\n                encoder.encode_varint32(10u32)?;\n                encoder.encode_string(val_ref)?;\n            }\n        }\n        {\n            let val_ref = &self.r#pwd;\n            if !val_ref.is_empty() {\n                encoder.encode_varint32(18u32)?;\n                encoder.encode_string(val_ref)?;\n            }\n        }\n        {\n            let val_ref = &self.r#bssid;\n            if !val_ref.is_empty() {\n                encoder.encode_varint32(26u32)?;\n                encoder.encode_string(val_ref)?;\n            }\n        }\n        {\n            let val_ref = &self.r#is_wpa3_supported;\n            if *val_ref {\n                encoder.encode_varint32(32u32)?;\n                encoder.encode_bool(*val_ref)?;\n            }\n        }\n        {\n            let val_ref = &self.r#listen_interval;\n            if *val_ref != 0 {\n                encoder.encode_varint32(40u32)?;\n                encoder.encode_int32(*val_ref as _)?;\n            }\n        }\n        {\n            let val_ref = &self.r#band_mode;\n            if *val_ref != 0 {\n                encoder.encode_varint32(48u32)?;\n                encoder.encode_int32(*val_ref as _)?;\n            }\n        }\n        Ok(())\n    }\n    fn compute_size(&self) -> usize {\n        use ::micropb::{FieldEncode, PbMap};\n        let mut size = 0;\n        {\n            let val_ref = &self.r#ssid;\n            if !val_ref.is_empty() {\n                size += 1usize + ::micropb::size::sizeof_len_record(val_ref.len());\n            }\n        }\n        {\n            let val_ref = &self.r#pwd;\n            if !val_ref.is_empty() {\n                size += 1usize + ::micropb::size::sizeof_len_record(val_ref.len());\n            }\n        }\n        {\n            let val_ref = &self.r#bssid;\n            if !val_ref.is_empty() {\n                size += 1usize + ::micropb::size::sizeof_len_record(val_ref.len());\n            }\n        }\n        {\n            let val_ref = &self.r#is_wpa3_supported;\n            if *val_ref {\n                size += 1usize + 1;\n            }\n        }\n        {\n            let val_ref = &self.r#listen_interval;\n            if *val_ref != 0 {\n                size += 1usize + ::micropb::size::sizeof_int32(*val_ref as _);\n            }\n        }\n        {\n            let val_ref = &self.r#band_mode;\n            if *val_ref != 0 {\n                size += 1usize + ::micropb::size::sizeof_int32(*val_ref as _);\n            }\n        }\n        size\n    }\n}\n#[derive(Debug, Default, PartialEq, Clone)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub struct CtrlMsg_Resp_ConnectAP {\n    pub r#resp: i32,\n    pub r#mac: ::heapless::Vec<u8, 32>,\n    pub r#band_mode: i32,\n}\nimpl CtrlMsg_Resp_ConnectAP {\n    /// Return a reference to `resp`\n    #[inline]\n    pub fn r#resp(&self) -> &i32 {\n        &self.r#resp\n    }\n    /// Return a mutable reference to `resp`\n    #[inline]\n    pub fn mut_resp(&mut self) -> &mut i32 {\n        &mut self.r#resp\n    }\n    /// Set the value of `resp`\n    #[inline]\n    pub fn set_resp(&mut self, value: i32) -> &mut Self {\n        self.r#resp = value.into();\n        self\n    }\n    /// Builder method that sets the value of `resp`. Useful for initializing the message.\n    #[inline]\n    pub fn init_resp(mut self, value: i32) -> Self {\n        self.r#resp = value.into();\n        self\n    }\n    /// Return a reference to `mac`\n    #[inline]\n    pub fn r#mac(&self) -> &::heapless::Vec<u8, 32> {\n        &self.r#mac\n    }\n    /// Return a mutable reference to `mac`\n    #[inline]\n    pub fn mut_mac(&mut self) -> &mut ::heapless::Vec<u8, 32> {\n        &mut self.r#mac\n    }\n    /// Set the value of `mac`\n    #[inline]\n    pub fn set_mac(&mut self, value: ::heapless::Vec<u8, 32>) -> &mut Self {\n        self.r#mac = value.into();\n        self\n    }\n    /// Builder method that sets the value of `mac`. Useful for initializing the message.\n    #[inline]\n    pub fn init_mac(mut self, value: ::heapless::Vec<u8, 32>) -> Self {\n        self.r#mac = value.into();\n        self\n    }\n    /// Return a reference to `band_mode`\n    #[inline]\n    pub fn r#band_mode(&self) -> &i32 {\n        &self.r#band_mode\n    }\n    /// Return a mutable reference to `band_mode`\n    #[inline]\n    pub fn mut_band_mode(&mut self) -> &mut i32 {\n        &mut self.r#band_mode\n    }\n    /// Set the value of `band_mode`\n    #[inline]\n    pub fn set_band_mode(&mut self, value: i32) -> &mut Self {\n        self.r#band_mode = value.into();\n        self\n    }\n    /// Builder method that sets the value of `band_mode`. Useful for initializing the message.\n    #[inline]\n    pub fn init_band_mode(mut self, value: i32) -> Self {\n        self.r#band_mode = value.into();\n        self\n    }\n}\nimpl ::micropb::MessageDecode for CtrlMsg_Resp_ConnectAP {\n    fn decode<IMPL_MICROPB_READ: ::micropb::PbRead>(\n        &mut self,\n        decoder: &mut ::micropb::PbDecoder<IMPL_MICROPB_READ>,\n        len: usize,\n    ) -> Result<(), ::micropb::DecodeError<IMPL_MICROPB_READ::Error>> {\n        use ::micropb::{FieldDecode, PbBytes, PbMap, PbString, PbVec};\n        let before = decoder.bytes_read();\n        while decoder.bytes_read() - before < len {\n            let tag = decoder.decode_tag()?;\n            match tag.field_num() {\n                0 => return Err(::micropb::DecodeError::ZeroField),\n                1u32 => {\n                    let mut_ref = &mut self.r#resp;\n                    {\n                        let val = decoder.decode_int32()?;\n                        let val_ref = &val;\n                        if *val_ref != 0 {\n                            *mut_ref = val as _;\n                        }\n                    };\n                }\n                2u32 => {\n                    let mut_ref = &mut self.r#mac;\n                    {\n                        decoder.decode_bytes(mut_ref, ::micropb::Presence::Implicit)?;\n                    };\n                }\n                3u32 => {\n                    let mut_ref = &mut self.r#band_mode;\n                    {\n                        let val = decoder.decode_int32()?;\n                        let val_ref = &val;\n                        if *val_ref != 0 {\n                            *mut_ref = val as _;\n                        }\n                    };\n                }\n                _ => {\n                    decoder.skip_wire_value(tag.wire_type())?;\n                }\n            }\n        }\n        Ok(())\n    }\n}\nimpl ::micropb::MessageEncode for CtrlMsg_Resp_ConnectAP {\n    const MAX_SIZE: ::core::result::Result<usize, &'static str> = 'msg: {\n        let mut max_size = 0;\n        match ::micropb::const_map!(::core::result::Result::Ok(10usize), |size| size + 1usize) {\n            ::core::result::Result::Ok(size) => {\n                max_size += size;\n            }\n            ::core::result::Result::Err(err) => {\n                break 'msg (::core::result::Result::<usize, _>::Err(err));\n            }\n        }\n        match ::micropb::const_map!(::core::result::Result::Ok(33usize), |size| size + 1usize) {\n            ::core::result::Result::Ok(size) => {\n                max_size += size;\n            }\n            ::core::result::Result::Err(err) => {\n                break 'msg (::core::result::Result::<usize, _>::Err(err));\n            }\n        }\n        match ::micropb::const_map!(::core::result::Result::Ok(10usize), |size| size + 1usize) {\n            ::core::result::Result::Ok(size) => {\n                max_size += size;\n            }\n            ::core::result::Result::Err(err) => {\n                break 'msg (::core::result::Result::<usize, _>::Err(err));\n            }\n        }\n        ::core::result::Result::Ok(max_size)\n    };\n    fn encode<IMPL_MICROPB_WRITE: ::micropb::PbWrite>(\n        &self,\n        encoder: &mut ::micropb::PbEncoder<IMPL_MICROPB_WRITE>,\n    ) -> Result<(), IMPL_MICROPB_WRITE::Error> {\n        use ::micropb::{FieldEncode, PbMap};\n        {\n            let val_ref = &self.r#resp;\n            if *val_ref != 0 {\n                encoder.encode_varint32(8u32)?;\n                encoder.encode_int32(*val_ref as _)?;\n            }\n        }\n        {\n            let val_ref = &self.r#mac;\n            if !val_ref.is_empty() {\n                encoder.encode_varint32(18u32)?;\n                encoder.encode_bytes(val_ref)?;\n            }\n        }\n        {\n            let val_ref = &self.r#band_mode;\n            if *val_ref != 0 {\n                encoder.encode_varint32(24u32)?;\n                encoder.encode_int32(*val_ref as _)?;\n            }\n        }\n        Ok(())\n    }\n    fn compute_size(&self) -> usize {\n        use ::micropb::{FieldEncode, PbMap};\n        let mut size = 0;\n        {\n            let val_ref = &self.r#resp;\n            if *val_ref != 0 {\n                size += 1usize + ::micropb::size::sizeof_int32(*val_ref as _);\n            }\n        }\n        {\n            let val_ref = &self.r#mac;\n            if !val_ref.is_empty() {\n                size += 1usize + ::micropb::size::sizeof_len_record(val_ref.len());\n            }\n        }\n        {\n            let val_ref = &self.r#band_mode;\n            if *val_ref != 0 {\n                size += 1usize + ::micropb::size::sizeof_int32(*val_ref as _);\n            }\n        }\n        size\n    }\n}\n#[derive(Debug, Default, PartialEq, Clone, Copy)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub struct CtrlMsg_Req_GetSoftAPConfig {}\nimpl CtrlMsg_Req_GetSoftAPConfig {}\nimpl ::micropb::MessageDecode for CtrlMsg_Req_GetSoftAPConfig {\n    fn decode<IMPL_MICROPB_READ: ::micropb::PbRead>(\n        &mut self,\n        decoder: &mut ::micropb::PbDecoder<IMPL_MICROPB_READ>,\n        len: usize,\n    ) -> Result<(), ::micropb::DecodeError<IMPL_MICROPB_READ::Error>> {\n        use ::micropb::{FieldDecode, PbBytes, PbMap, PbString, PbVec};\n        let before = decoder.bytes_read();\n        while decoder.bytes_read() - before < len {\n            let tag = decoder.decode_tag()?;\n            match tag.field_num() {\n                0 => return Err(::micropb::DecodeError::ZeroField),\n                _ => {\n                    decoder.skip_wire_value(tag.wire_type())?;\n                }\n            }\n        }\n        Ok(())\n    }\n}\nimpl ::micropb::MessageEncode for CtrlMsg_Req_GetSoftAPConfig {\n    const MAX_SIZE: ::core::result::Result<usize, &'static str> = 'msg: {\n        let mut max_size = 0;\n        ::core::result::Result::Ok(max_size)\n    };\n    fn encode<IMPL_MICROPB_WRITE: ::micropb::PbWrite>(\n        &self,\n        encoder: &mut ::micropb::PbEncoder<IMPL_MICROPB_WRITE>,\n    ) -> Result<(), IMPL_MICROPB_WRITE::Error> {\n        use ::micropb::{FieldEncode, PbMap};\n        Ok(())\n    }\n    fn compute_size(&self) -> usize {\n        use ::micropb::{FieldEncode, PbMap};\n        let mut size = 0;\n        size\n    }\n}\n#[derive(Debug, Default, PartialEq, Clone)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub struct CtrlMsg_Resp_GetSoftAPConfig {\n    pub r#ssid: ::heapless::Vec<u8, 32>,\n    pub r#pwd: ::heapless::Vec<u8, 32>,\n    pub r#chnl: i32,\n    pub r#sec_prot: Ctrl_WifiSecProt,\n    pub r#max_conn: i32,\n    pub r#ssid_hidden: bool,\n    pub r#bw: i32,\n    pub r#resp: i32,\n    pub r#band_mode: i32,\n}\nimpl CtrlMsg_Resp_GetSoftAPConfig {\n    /// Return a reference to `ssid`\n    #[inline]\n    pub fn r#ssid(&self) -> &::heapless::Vec<u8, 32> {\n        &self.r#ssid\n    }\n    /// Return a mutable reference to `ssid`\n    #[inline]\n    pub fn mut_ssid(&mut self) -> &mut ::heapless::Vec<u8, 32> {\n        &mut self.r#ssid\n    }\n    /// Set the value of `ssid`\n    #[inline]\n    pub fn set_ssid(&mut self, value: ::heapless::Vec<u8, 32>) -> &mut Self {\n        self.r#ssid = value.into();\n        self\n    }\n    /// Builder method that sets the value of `ssid`. Useful for initializing the message.\n    #[inline]\n    pub fn init_ssid(mut self, value: ::heapless::Vec<u8, 32>) -> Self {\n        self.r#ssid = value.into();\n        self\n    }\n    /// Return a reference to `pwd`\n    #[inline]\n    pub fn r#pwd(&self) -> &::heapless::Vec<u8, 32> {\n        &self.r#pwd\n    }\n    /// Return a mutable reference to `pwd`\n    #[inline]\n    pub fn mut_pwd(&mut self) -> &mut ::heapless::Vec<u8, 32> {\n        &mut self.r#pwd\n    }\n    /// Set the value of `pwd`\n    #[inline]\n    pub fn set_pwd(&mut self, value: ::heapless::Vec<u8, 32>) -> &mut Self {\n        self.r#pwd = value.into();\n        self\n    }\n    /// Builder method that sets the value of `pwd`. Useful for initializing the message.\n    #[inline]\n    pub fn init_pwd(mut self, value: ::heapless::Vec<u8, 32>) -> Self {\n        self.r#pwd = value.into();\n        self\n    }\n    /// Return a reference to `chnl`\n    #[inline]\n    pub fn r#chnl(&self) -> &i32 {\n        &self.r#chnl\n    }\n    /// Return a mutable reference to `chnl`\n    #[inline]\n    pub fn mut_chnl(&mut self) -> &mut i32 {\n        &mut self.r#chnl\n    }\n    /// Set the value of `chnl`\n    #[inline]\n    pub fn set_chnl(&mut self, value: i32) -> &mut Self {\n        self.r#chnl = value.into();\n        self\n    }\n    /// Builder method that sets the value of `chnl`. Useful for initializing the message.\n    #[inline]\n    pub fn init_chnl(mut self, value: i32) -> Self {\n        self.r#chnl = value.into();\n        self\n    }\n    /// Return a reference to `sec_prot`\n    #[inline]\n    pub fn r#sec_prot(&self) -> &Ctrl_WifiSecProt {\n        &self.r#sec_prot\n    }\n    /// Return a mutable reference to `sec_prot`\n    #[inline]\n    pub fn mut_sec_prot(&mut self) -> &mut Ctrl_WifiSecProt {\n        &mut self.r#sec_prot\n    }\n    /// Set the value of `sec_prot`\n    #[inline]\n    pub fn set_sec_prot(&mut self, value: Ctrl_WifiSecProt) -> &mut Self {\n        self.r#sec_prot = value.into();\n        self\n    }\n    /// Builder method that sets the value of `sec_prot`. Useful for initializing the message.\n    #[inline]\n    pub fn init_sec_prot(mut self, value: Ctrl_WifiSecProt) -> Self {\n        self.r#sec_prot = value.into();\n        self\n    }\n    /// Return a reference to `max_conn`\n    #[inline]\n    pub fn r#max_conn(&self) -> &i32 {\n        &self.r#max_conn\n    }\n    /// Return a mutable reference to `max_conn`\n    #[inline]\n    pub fn mut_max_conn(&mut self) -> &mut i32 {\n        &mut self.r#max_conn\n    }\n    /// Set the value of `max_conn`\n    #[inline]\n    pub fn set_max_conn(&mut self, value: i32) -> &mut Self {\n        self.r#max_conn = value.into();\n        self\n    }\n    /// Builder method that sets the value of `max_conn`. Useful for initializing the message.\n    #[inline]\n    pub fn init_max_conn(mut self, value: i32) -> Self {\n        self.r#max_conn = value.into();\n        self\n    }\n    /// Return a reference to `ssid_hidden`\n    #[inline]\n    pub fn r#ssid_hidden(&self) -> &bool {\n        &self.r#ssid_hidden\n    }\n    /// Return a mutable reference to `ssid_hidden`\n    #[inline]\n    pub fn mut_ssid_hidden(&mut self) -> &mut bool {\n        &mut self.r#ssid_hidden\n    }\n    /// Set the value of `ssid_hidden`\n    #[inline]\n    pub fn set_ssid_hidden(&mut self, value: bool) -> &mut Self {\n        self.r#ssid_hidden = value.into();\n        self\n    }\n    /// Builder method that sets the value of `ssid_hidden`. Useful for initializing the message.\n    #[inline]\n    pub fn init_ssid_hidden(mut self, value: bool) -> Self {\n        self.r#ssid_hidden = value.into();\n        self\n    }\n    /// Return a reference to `bw`\n    #[inline]\n    pub fn r#bw(&self) -> &i32 {\n        &self.r#bw\n    }\n    /// Return a mutable reference to `bw`\n    #[inline]\n    pub fn mut_bw(&mut self) -> &mut i32 {\n        &mut self.r#bw\n    }\n    /// Set the value of `bw`\n    #[inline]\n    pub fn set_bw(&mut self, value: i32) -> &mut Self {\n        self.r#bw = value.into();\n        self\n    }\n    /// Builder method that sets the value of `bw`. Useful for initializing the message.\n    #[inline]\n    pub fn init_bw(mut self, value: i32) -> Self {\n        self.r#bw = value.into();\n        self\n    }\n    /// Return a reference to `resp`\n    #[inline]\n    pub fn r#resp(&self) -> &i32 {\n        &self.r#resp\n    }\n    /// Return a mutable reference to `resp`\n    #[inline]\n    pub fn mut_resp(&mut self) -> &mut i32 {\n        &mut self.r#resp\n    }\n    /// Set the value of `resp`\n    #[inline]\n    pub fn set_resp(&mut self, value: i32) -> &mut Self {\n        self.r#resp = value.into();\n        self\n    }\n    /// Builder method that sets the value of `resp`. Useful for initializing the message.\n    #[inline]\n    pub fn init_resp(mut self, value: i32) -> Self {\n        self.r#resp = value.into();\n        self\n    }\n    /// Return a reference to `band_mode`\n    #[inline]\n    pub fn r#band_mode(&self) -> &i32 {\n        &self.r#band_mode\n    }\n    /// Return a mutable reference to `band_mode`\n    #[inline]\n    pub fn mut_band_mode(&mut self) -> &mut i32 {\n        &mut self.r#band_mode\n    }\n    /// Set the value of `band_mode`\n    #[inline]\n    pub fn set_band_mode(&mut self, value: i32) -> &mut Self {\n        self.r#band_mode = value.into();\n        self\n    }\n    /// Builder method that sets the value of `band_mode`. Useful for initializing the message.\n    #[inline]\n    pub fn init_band_mode(mut self, value: i32) -> Self {\n        self.r#band_mode = value.into();\n        self\n    }\n}\nimpl ::micropb::MessageDecode for CtrlMsg_Resp_GetSoftAPConfig {\n    fn decode<IMPL_MICROPB_READ: ::micropb::PbRead>(\n        &mut self,\n        decoder: &mut ::micropb::PbDecoder<IMPL_MICROPB_READ>,\n        len: usize,\n    ) -> Result<(), ::micropb::DecodeError<IMPL_MICROPB_READ::Error>> {\n        use ::micropb::{FieldDecode, PbBytes, PbMap, PbString, PbVec};\n        let before = decoder.bytes_read();\n        while decoder.bytes_read() - before < len {\n            let tag = decoder.decode_tag()?;\n            match tag.field_num() {\n                0 => return Err(::micropb::DecodeError::ZeroField),\n                1u32 => {\n                    let mut_ref = &mut self.r#ssid;\n                    {\n                        decoder.decode_bytes(mut_ref, ::micropb::Presence::Implicit)?;\n                    };\n                }\n                2u32 => {\n                    let mut_ref = &mut self.r#pwd;\n                    {\n                        decoder.decode_bytes(mut_ref, ::micropb::Presence::Implicit)?;\n                    };\n                }\n                3u32 => {\n                    let mut_ref = &mut self.r#chnl;\n                    {\n                        let val = decoder.decode_int32()?;\n                        let val_ref = &val;\n                        if *val_ref != 0 {\n                            *mut_ref = val as _;\n                        }\n                    };\n                }\n                4u32 => {\n                    let mut_ref = &mut self.r#sec_prot;\n                    {\n                        let val = decoder.decode_int32().map(|n| Ctrl_WifiSecProt(n as _))?;\n                        let val_ref = &val;\n                        if val_ref.0 != 0 {\n                            *mut_ref = val as _;\n                        }\n                    };\n                }\n                5u32 => {\n                    let mut_ref = &mut self.r#max_conn;\n                    {\n                        let val = decoder.decode_int32()?;\n                        let val_ref = &val;\n                        if *val_ref != 0 {\n                            *mut_ref = val as _;\n                        }\n                    };\n                }\n                6u32 => {\n                    let mut_ref = &mut self.r#ssid_hidden;\n                    {\n                        let val = decoder.decode_bool()?;\n                        let val_ref = &val;\n                        if *val_ref {\n                            *mut_ref = val as _;\n                        }\n                    };\n                }\n                7u32 => {\n                    let mut_ref = &mut self.r#bw;\n                    {\n                        let val = decoder.decode_int32()?;\n                        let val_ref = &val;\n                        if *val_ref != 0 {\n                            *mut_ref = val as _;\n                        }\n                    };\n                }\n                8u32 => {\n                    let mut_ref = &mut self.r#resp;\n                    {\n                        let val = decoder.decode_int32()?;\n                        let val_ref = &val;\n                        if *val_ref != 0 {\n                            *mut_ref = val as _;\n                        }\n                    };\n                }\n                9u32 => {\n                    let mut_ref = &mut self.r#band_mode;\n                    {\n                        let val = decoder.decode_int32()?;\n                        let val_ref = &val;\n                        if *val_ref != 0 {\n                            *mut_ref = val as _;\n                        }\n                    };\n                }\n                _ => {\n                    decoder.skip_wire_value(tag.wire_type())?;\n                }\n            }\n        }\n        Ok(())\n    }\n}\nimpl ::micropb::MessageEncode for CtrlMsg_Resp_GetSoftAPConfig {\n    const MAX_SIZE: ::core::result::Result<usize, &'static str> = 'msg: {\n        let mut max_size = 0;\n        match ::micropb::const_map!(::core::result::Result::Ok(33usize), |size| size + 1usize) {\n            ::core::result::Result::Ok(size) => {\n                max_size += size;\n            }\n            ::core::result::Result::Err(err) => {\n                break 'msg (::core::result::Result::<usize, _>::Err(err));\n            }\n        }\n        match ::micropb::const_map!(::core::result::Result::Ok(33usize), |size| size + 1usize) {\n            ::core::result::Result::Ok(size) => {\n                max_size += size;\n            }\n            ::core::result::Result::Err(err) => {\n                break 'msg (::core::result::Result::<usize, _>::Err(err));\n            }\n        }\n        match ::micropb::const_map!(::core::result::Result::Ok(10usize), |size| size + 1usize) {\n            ::core::result::Result::Ok(size) => {\n                max_size += size;\n            }\n            ::core::result::Result::Err(err) => {\n                break 'msg (::core::result::Result::<usize, _>::Err(err));\n            }\n        }\n        match ::micropb::const_map!(::core::result::Result::Ok(Ctrl_WifiSecProt::_MAX_SIZE), |size| size\n            + 1usize)\n        {\n            ::core::result::Result::Ok(size) => {\n                max_size += size;\n            }\n            ::core::result::Result::Err(err) => {\n                break 'msg (::core::result::Result::<usize, _>::Err(err));\n            }\n        }\n        match ::micropb::const_map!(::core::result::Result::Ok(10usize), |size| size + 1usize) {\n            ::core::result::Result::Ok(size) => {\n                max_size += size;\n            }\n            ::core::result::Result::Err(err) => {\n                break 'msg (::core::result::Result::<usize, _>::Err(err));\n            }\n        }\n        match ::micropb::const_map!(::core::result::Result::Ok(1usize), |size| size + 1usize) {\n            ::core::result::Result::Ok(size) => {\n                max_size += size;\n            }\n            ::core::result::Result::Err(err) => {\n                break 'msg (::core::result::Result::<usize, _>::Err(err));\n            }\n        }\n        match ::micropb::const_map!(::core::result::Result::Ok(10usize), |size| size + 1usize) {\n            ::core::result::Result::Ok(size) => {\n                max_size += size;\n            }\n            ::core::result::Result::Err(err) => {\n                break 'msg (::core::result::Result::<usize, _>::Err(err));\n            }\n        }\n        match ::micropb::const_map!(::core::result::Result::Ok(10usize), |size| size + 1usize) {\n            ::core::result::Result::Ok(size) => {\n                max_size += size;\n            }\n            ::core::result::Result::Err(err) => {\n                break 'msg (::core::result::Result::<usize, _>::Err(err));\n            }\n        }\n        match ::micropb::const_map!(::core::result::Result::Ok(10usize), |size| size + 1usize) {\n            ::core::result::Result::Ok(size) => {\n                max_size += size;\n            }\n            ::core::result::Result::Err(err) => {\n                break 'msg (::core::result::Result::<usize, _>::Err(err));\n            }\n        }\n        ::core::result::Result::Ok(max_size)\n    };\n    fn encode<IMPL_MICROPB_WRITE: ::micropb::PbWrite>(\n        &self,\n        encoder: &mut ::micropb::PbEncoder<IMPL_MICROPB_WRITE>,\n    ) -> Result<(), IMPL_MICROPB_WRITE::Error> {\n        use ::micropb::{FieldEncode, PbMap};\n        {\n            let val_ref = &self.r#ssid;\n            if !val_ref.is_empty() {\n                encoder.encode_varint32(10u32)?;\n                encoder.encode_bytes(val_ref)?;\n            }\n        }\n        {\n            let val_ref = &self.r#pwd;\n            if !val_ref.is_empty() {\n                encoder.encode_varint32(18u32)?;\n                encoder.encode_bytes(val_ref)?;\n            }\n        }\n        {\n            let val_ref = &self.r#chnl;\n            if *val_ref != 0 {\n                encoder.encode_varint32(24u32)?;\n                encoder.encode_int32(*val_ref as _)?;\n            }\n        }\n        {\n            let val_ref = &self.r#sec_prot;\n            if val_ref.0 != 0 {\n                encoder.encode_varint32(32u32)?;\n                encoder.encode_int32(val_ref.0 as _)?;\n            }\n        }\n        {\n            let val_ref = &self.r#max_conn;\n            if *val_ref != 0 {\n                encoder.encode_varint32(40u32)?;\n                encoder.encode_int32(*val_ref as _)?;\n            }\n        }\n        {\n            let val_ref = &self.r#ssid_hidden;\n            if *val_ref {\n                encoder.encode_varint32(48u32)?;\n                encoder.encode_bool(*val_ref)?;\n            }\n        }\n        {\n            let val_ref = &self.r#bw;\n            if *val_ref != 0 {\n                encoder.encode_varint32(56u32)?;\n                encoder.encode_int32(*val_ref as _)?;\n            }\n        }\n        {\n            let val_ref = &self.r#resp;\n            if *val_ref != 0 {\n                encoder.encode_varint32(64u32)?;\n                encoder.encode_int32(*val_ref as _)?;\n            }\n        }\n        {\n            let val_ref = &self.r#band_mode;\n            if *val_ref != 0 {\n                encoder.encode_varint32(72u32)?;\n                encoder.encode_int32(*val_ref as _)?;\n            }\n        }\n        Ok(())\n    }\n    fn compute_size(&self) -> usize {\n        use ::micropb::{FieldEncode, PbMap};\n        let mut size = 0;\n        {\n            let val_ref = &self.r#ssid;\n            if !val_ref.is_empty() {\n                size += 1usize + ::micropb::size::sizeof_len_record(val_ref.len());\n            }\n        }\n        {\n            let val_ref = &self.r#pwd;\n            if !val_ref.is_empty() {\n                size += 1usize + ::micropb::size::sizeof_len_record(val_ref.len());\n            }\n        }\n        {\n            let val_ref = &self.r#chnl;\n            if *val_ref != 0 {\n                size += 1usize + ::micropb::size::sizeof_int32(*val_ref as _);\n            }\n        }\n        {\n            let val_ref = &self.r#sec_prot;\n            if val_ref.0 != 0 {\n                size += 1usize + ::micropb::size::sizeof_int32(val_ref.0 as _);\n            }\n        }\n        {\n            let val_ref = &self.r#max_conn;\n            if *val_ref != 0 {\n                size += 1usize + ::micropb::size::sizeof_int32(*val_ref as _);\n            }\n        }\n        {\n            let val_ref = &self.r#ssid_hidden;\n            if *val_ref {\n                size += 1usize + 1;\n            }\n        }\n        {\n            let val_ref = &self.r#bw;\n            if *val_ref != 0 {\n                size += 1usize + ::micropb::size::sizeof_int32(*val_ref as _);\n            }\n        }\n        {\n            let val_ref = &self.r#resp;\n            if *val_ref != 0 {\n                size += 1usize + ::micropb::size::sizeof_int32(*val_ref as _);\n            }\n        }\n        {\n            let val_ref = &self.r#band_mode;\n            if *val_ref != 0 {\n                size += 1usize + ::micropb::size::sizeof_int32(*val_ref as _);\n            }\n        }\n        size\n    }\n}\n#[derive(Debug, Default, PartialEq, Clone)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub struct CtrlMsg_Req_StartSoftAP {\n    pub r#ssid: ::heapless::String<32>,\n    pub r#pwd: ::heapless::String<32>,\n    pub r#chnl: i32,\n    pub r#sec_prot: Ctrl_WifiSecProt,\n    pub r#max_conn: i32,\n    pub r#ssid_hidden: bool,\n    pub r#bw: i32,\n    pub r#band_mode: i32,\n}\nimpl CtrlMsg_Req_StartSoftAP {\n    /// Return a reference to `ssid`\n    #[inline]\n    pub fn r#ssid(&self) -> &::heapless::String<32> {\n        &self.r#ssid\n    }\n    /// Return a mutable reference to `ssid`\n    #[inline]\n    pub fn mut_ssid(&mut self) -> &mut ::heapless::String<32> {\n        &mut self.r#ssid\n    }\n    /// Set the value of `ssid`\n    #[inline]\n    pub fn set_ssid(&mut self, value: ::heapless::String<32>) -> &mut Self {\n        self.r#ssid = value.into();\n        self\n    }\n    /// Builder method that sets the value of `ssid`. Useful for initializing the message.\n    #[inline]\n    pub fn init_ssid(mut self, value: ::heapless::String<32>) -> Self {\n        self.r#ssid = value.into();\n        self\n    }\n    /// Return a reference to `pwd`\n    #[inline]\n    pub fn r#pwd(&self) -> &::heapless::String<32> {\n        &self.r#pwd\n    }\n    /// Return a mutable reference to `pwd`\n    #[inline]\n    pub fn mut_pwd(&mut self) -> &mut ::heapless::String<32> {\n        &mut self.r#pwd\n    }\n    /// Set the value of `pwd`\n    #[inline]\n    pub fn set_pwd(&mut self, value: ::heapless::String<32>) -> &mut Self {\n        self.r#pwd = value.into();\n        self\n    }\n    /// Builder method that sets the value of `pwd`. Useful for initializing the message.\n    #[inline]\n    pub fn init_pwd(mut self, value: ::heapless::String<32>) -> Self {\n        self.r#pwd = value.into();\n        self\n    }\n    /// Return a reference to `chnl`\n    #[inline]\n    pub fn r#chnl(&self) -> &i32 {\n        &self.r#chnl\n    }\n    /// Return a mutable reference to `chnl`\n    #[inline]\n    pub fn mut_chnl(&mut self) -> &mut i32 {\n        &mut self.r#chnl\n    }\n    /// Set the value of `chnl`\n    #[inline]\n    pub fn set_chnl(&mut self, value: i32) -> &mut Self {\n        self.r#chnl = value.into();\n        self\n    }\n    /// Builder method that sets the value of `chnl`. Useful for initializing the message.\n    #[inline]\n    pub fn init_chnl(mut self, value: i32) -> Self {\n        self.r#chnl = value.into();\n        self\n    }\n    /// Return a reference to `sec_prot`\n    #[inline]\n    pub fn r#sec_prot(&self) -> &Ctrl_WifiSecProt {\n        &self.r#sec_prot\n    }\n    /// Return a mutable reference to `sec_prot`\n    #[inline]\n    pub fn mut_sec_prot(&mut self) -> &mut Ctrl_WifiSecProt {\n        &mut self.r#sec_prot\n    }\n    /// Set the value of `sec_prot`\n    #[inline]\n    pub fn set_sec_prot(&mut self, value: Ctrl_WifiSecProt) -> &mut Self {\n        self.r#sec_prot = value.into();\n        self\n    }\n    /// Builder method that sets the value of `sec_prot`. Useful for initializing the message.\n    #[inline]\n    pub fn init_sec_prot(mut self, value: Ctrl_WifiSecProt) -> Self {\n        self.r#sec_prot = value.into();\n        self\n    }\n    /// Return a reference to `max_conn`\n    #[inline]\n    pub fn r#max_conn(&self) -> &i32 {\n        &self.r#max_conn\n    }\n    /// Return a mutable reference to `max_conn`\n    #[inline]\n    pub fn mut_max_conn(&mut self) -> &mut i32 {\n        &mut self.r#max_conn\n    }\n    /// Set the value of `max_conn`\n    #[inline]\n    pub fn set_max_conn(&mut self, value: i32) -> &mut Self {\n        self.r#max_conn = value.into();\n        self\n    }\n    /// Builder method that sets the value of `max_conn`. Useful for initializing the message.\n    #[inline]\n    pub fn init_max_conn(mut self, value: i32) -> Self {\n        self.r#max_conn = value.into();\n        self\n    }\n    /// Return a reference to `ssid_hidden`\n    #[inline]\n    pub fn r#ssid_hidden(&self) -> &bool {\n        &self.r#ssid_hidden\n    }\n    /// Return a mutable reference to `ssid_hidden`\n    #[inline]\n    pub fn mut_ssid_hidden(&mut self) -> &mut bool {\n        &mut self.r#ssid_hidden\n    }\n    /// Set the value of `ssid_hidden`\n    #[inline]\n    pub fn set_ssid_hidden(&mut self, value: bool) -> &mut Self {\n        self.r#ssid_hidden = value.into();\n        self\n    }\n    /// Builder method that sets the value of `ssid_hidden`. Useful for initializing the message.\n    #[inline]\n    pub fn init_ssid_hidden(mut self, value: bool) -> Self {\n        self.r#ssid_hidden = value.into();\n        self\n    }\n    /// Return a reference to `bw`\n    #[inline]\n    pub fn r#bw(&self) -> &i32 {\n        &self.r#bw\n    }\n    /// Return a mutable reference to `bw`\n    #[inline]\n    pub fn mut_bw(&mut self) -> &mut i32 {\n        &mut self.r#bw\n    }\n    /// Set the value of `bw`\n    #[inline]\n    pub fn set_bw(&mut self, value: i32) -> &mut Self {\n        self.r#bw = value.into();\n        self\n    }\n    /// Builder method that sets the value of `bw`. Useful for initializing the message.\n    #[inline]\n    pub fn init_bw(mut self, value: i32) -> Self {\n        self.r#bw = value.into();\n        self\n    }\n    /// Return a reference to `band_mode`\n    #[inline]\n    pub fn r#band_mode(&self) -> &i32 {\n        &self.r#band_mode\n    }\n    /// Return a mutable reference to `band_mode`\n    #[inline]\n    pub fn mut_band_mode(&mut self) -> &mut i32 {\n        &mut self.r#band_mode\n    }\n    /// Set the value of `band_mode`\n    #[inline]\n    pub fn set_band_mode(&mut self, value: i32) -> &mut Self {\n        self.r#band_mode = value.into();\n        self\n    }\n    /// Builder method that sets the value of `band_mode`. Useful for initializing the message.\n    #[inline]\n    pub fn init_band_mode(mut self, value: i32) -> Self {\n        self.r#band_mode = value.into();\n        self\n    }\n}\nimpl ::micropb::MessageDecode for CtrlMsg_Req_StartSoftAP {\n    fn decode<IMPL_MICROPB_READ: ::micropb::PbRead>(\n        &mut self,\n        decoder: &mut ::micropb::PbDecoder<IMPL_MICROPB_READ>,\n        len: usize,\n    ) -> Result<(), ::micropb::DecodeError<IMPL_MICROPB_READ::Error>> {\n        use ::micropb::{FieldDecode, PbBytes, PbMap, PbString, PbVec};\n        let before = decoder.bytes_read();\n        while decoder.bytes_read() - before < len {\n            let tag = decoder.decode_tag()?;\n            match tag.field_num() {\n                0 => return Err(::micropb::DecodeError::ZeroField),\n                1u32 => {\n                    let mut_ref = &mut self.r#ssid;\n                    {\n                        decoder.decode_string(mut_ref, ::micropb::Presence::Implicit)?;\n                    };\n                }\n                2u32 => {\n                    let mut_ref = &mut self.r#pwd;\n                    {\n                        decoder.decode_string(mut_ref, ::micropb::Presence::Implicit)?;\n                    };\n                }\n                3u32 => {\n                    let mut_ref = &mut self.r#chnl;\n                    {\n                        let val = decoder.decode_int32()?;\n                        let val_ref = &val;\n                        if *val_ref != 0 {\n                            *mut_ref = val as _;\n                        }\n                    };\n                }\n                4u32 => {\n                    let mut_ref = &mut self.r#sec_prot;\n                    {\n                        let val = decoder.decode_int32().map(|n| Ctrl_WifiSecProt(n as _))?;\n                        let val_ref = &val;\n                        if val_ref.0 != 0 {\n                            *mut_ref = val as _;\n                        }\n                    };\n                }\n                5u32 => {\n                    let mut_ref = &mut self.r#max_conn;\n                    {\n                        let val = decoder.decode_int32()?;\n                        let val_ref = &val;\n                        if *val_ref != 0 {\n                            *mut_ref = val as _;\n                        }\n                    };\n                }\n                6u32 => {\n                    let mut_ref = &mut self.r#ssid_hidden;\n                    {\n                        let val = decoder.decode_bool()?;\n                        let val_ref = &val;\n                        if *val_ref {\n                            *mut_ref = val as _;\n                        }\n                    };\n                }\n                7u32 => {\n                    let mut_ref = &mut self.r#bw;\n                    {\n                        let val = decoder.decode_int32()?;\n                        let val_ref = &val;\n                        if *val_ref != 0 {\n                            *mut_ref = val as _;\n                        }\n                    };\n                }\n                8u32 => {\n                    let mut_ref = &mut self.r#band_mode;\n                    {\n                        let val = decoder.decode_int32()?;\n                        let val_ref = &val;\n                        if *val_ref != 0 {\n                            *mut_ref = val as _;\n                        }\n                    };\n                }\n                _ => {\n                    decoder.skip_wire_value(tag.wire_type())?;\n                }\n            }\n        }\n        Ok(())\n    }\n}\nimpl ::micropb::MessageEncode for CtrlMsg_Req_StartSoftAP {\n    const MAX_SIZE: ::core::result::Result<usize, &'static str> = 'msg: {\n        let mut max_size = 0;\n        match ::micropb::const_map!(::core::result::Result::Ok(33usize), |size| size + 1usize) {\n            ::core::result::Result::Ok(size) => {\n                max_size += size;\n            }\n            ::core::result::Result::Err(err) => {\n                break 'msg (::core::result::Result::<usize, _>::Err(err));\n            }\n        }\n        match ::micropb::const_map!(::core::result::Result::Ok(33usize), |size| size + 1usize) {\n            ::core::result::Result::Ok(size) => {\n                max_size += size;\n            }\n            ::core::result::Result::Err(err) => {\n                break 'msg (::core::result::Result::<usize, _>::Err(err));\n            }\n        }\n        match ::micropb::const_map!(::core::result::Result::Ok(10usize), |size| size + 1usize) {\n            ::core::result::Result::Ok(size) => {\n                max_size += size;\n            }\n            ::core::result::Result::Err(err) => {\n                break 'msg (::core::result::Result::<usize, _>::Err(err));\n            }\n        }\n        match ::micropb::const_map!(::core::result::Result::Ok(Ctrl_WifiSecProt::_MAX_SIZE), |size| size\n            + 1usize)\n        {\n            ::core::result::Result::Ok(size) => {\n                max_size += size;\n            }\n            ::core::result::Result::Err(err) => {\n                break 'msg (::core::result::Result::<usize, _>::Err(err));\n            }\n        }\n        match ::micropb::const_map!(::core::result::Result::Ok(10usize), |size| size + 1usize) {\n            ::core::result::Result::Ok(size) => {\n                max_size += size;\n            }\n            ::core::result::Result::Err(err) => {\n                break 'msg (::core::result::Result::<usize, _>::Err(err));\n            }\n        }\n        match ::micropb::const_map!(::core::result::Result::Ok(1usize), |size| size + 1usize) {\n            ::core::result::Result::Ok(size) => {\n                max_size += size;\n            }\n            ::core::result::Result::Err(err) => {\n                break 'msg (::core::result::Result::<usize, _>::Err(err));\n            }\n        }\n        match ::micropb::const_map!(::core::result::Result::Ok(10usize), |size| size + 1usize) {\n            ::core::result::Result::Ok(size) => {\n                max_size += size;\n            }\n            ::core::result::Result::Err(err) => {\n                break 'msg (::core::result::Result::<usize, _>::Err(err));\n            }\n        }\n        match ::micropb::const_map!(::core::result::Result::Ok(10usize), |size| size + 1usize) {\n            ::core::result::Result::Ok(size) => {\n                max_size += size;\n            }\n            ::core::result::Result::Err(err) => {\n                break 'msg (::core::result::Result::<usize, _>::Err(err));\n            }\n        }\n        ::core::result::Result::Ok(max_size)\n    };\n    fn encode<IMPL_MICROPB_WRITE: ::micropb::PbWrite>(\n        &self,\n        encoder: &mut ::micropb::PbEncoder<IMPL_MICROPB_WRITE>,\n    ) -> Result<(), IMPL_MICROPB_WRITE::Error> {\n        use ::micropb::{FieldEncode, PbMap};\n        {\n            let val_ref = &self.r#ssid;\n            if !val_ref.is_empty() {\n                encoder.encode_varint32(10u32)?;\n                encoder.encode_string(val_ref)?;\n            }\n        }\n        {\n            let val_ref = &self.r#pwd;\n            if !val_ref.is_empty() {\n                encoder.encode_varint32(18u32)?;\n                encoder.encode_string(val_ref)?;\n            }\n        }\n        {\n            let val_ref = &self.r#chnl;\n            if *val_ref != 0 {\n                encoder.encode_varint32(24u32)?;\n                encoder.encode_int32(*val_ref as _)?;\n            }\n        }\n        {\n            let val_ref = &self.r#sec_prot;\n            if val_ref.0 != 0 {\n                encoder.encode_varint32(32u32)?;\n                encoder.encode_int32(val_ref.0 as _)?;\n            }\n        }\n        {\n            let val_ref = &self.r#max_conn;\n            if *val_ref != 0 {\n                encoder.encode_varint32(40u32)?;\n                encoder.encode_int32(*val_ref as _)?;\n            }\n        }\n        {\n            let val_ref = &self.r#ssid_hidden;\n            if *val_ref {\n                encoder.encode_varint32(48u32)?;\n                encoder.encode_bool(*val_ref)?;\n            }\n        }\n        {\n            let val_ref = &self.r#bw;\n            if *val_ref != 0 {\n                encoder.encode_varint32(56u32)?;\n                encoder.encode_int32(*val_ref as _)?;\n            }\n        }\n        {\n            let val_ref = &self.r#band_mode;\n            if *val_ref != 0 {\n                encoder.encode_varint32(64u32)?;\n                encoder.encode_int32(*val_ref as _)?;\n            }\n        }\n        Ok(())\n    }\n    fn compute_size(&self) -> usize {\n        use ::micropb::{FieldEncode, PbMap};\n        let mut size = 0;\n        {\n            let val_ref = &self.r#ssid;\n            if !val_ref.is_empty() {\n                size += 1usize + ::micropb::size::sizeof_len_record(val_ref.len());\n            }\n        }\n        {\n            let val_ref = &self.r#pwd;\n            if !val_ref.is_empty() {\n                size += 1usize + ::micropb::size::sizeof_len_record(val_ref.len());\n            }\n        }\n        {\n            let val_ref = &self.r#chnl;\n            if *val_ref != 0 {\n                size += 1usize + ::micropb::size::sizeof_int32(*val_ref as _);\n            }\n        }\n        {\n            let val_ref = &self.r#sec_prot;\n            if val_ref.0 != 0 {\n                size += 1usize + ::micropb::size::sizeof_int32(val_ref.0 as _);\n            }\n        }\n        {\n            let val_ref = &self.r#max_conn;\n            if *val_ref != 0 {\n                size += 1usize + ::micropb::size::sizeof_int32(*val_ref as _);\n            }\n        }\n        {\n            let val_ref = &self.r#ssid_hidden;\n            if *val_ref {\n                size += 1usize + 1;\n            }\n        }\n        {\n            let val_ref = &self.r#bw;\n            if *val_ref != 0 {\n                size += 1usize + ::micropb::size::sizeof_int32(*val_ref as _);\n            }\n        }\n        {\n            let val_ref = &self.r#band_mode;\n            if *val_ref != 0 {\n                size += 1usize + ::micropb::size::sizeof_int32(*val_ref as _);\n            }\n        }\n        size\n    }\n}\n#[derive(Debug, Default, PartialEq, Clone)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub struct CtrlMsg_Resp_StartSoftAP {\n    pub r#resp: i32,\n    pub r#mac: ::heapless::Vec<u8, 32>,\n    pub r#band_mode: i32,\n}\nimpl CtrlMsg_Resp_StartSoftAP {\n    /// Return a reference to `resp`\n    #[inline]\n    pub fn r#resp(&self) -> &i32 {\n        &self.r#resp\n    }\n    /// Return a mutable reference to `resp`\n    #[inline]\n    pub fn mut_resp(&mut self) -> &mut i32 {\n        &mut self.r#resp\n    }\n    /// Set the value of `resp`\n    #[inline]\n    pub fn set_resp(&mut self, value: i32) -> &mut Self {\n        self.r#resp = value.into();\n        self\n    }\n    /// Builder method that sets the value of `resp`. Useful for initializing the message.\n    #[inline]\n    pub fn init_resp(mut self, value: i32) -> Self {\n        self.r#resp = value.into();\n        self\n    }\n    /// Return a reference to `mac`\n    #[inline]\n    pub fn r#mac(&self) -> &::heapless::Vec<u8, 32> {\n        &self.r#mac\n    }\n    /// Return a mutable reference to `mac`\n    #[inline]\n    pub fn mut_mac(&mut self) -> &mut ::heapless::Vec<u8, 32> {\n        &mut self.r#mac\n    }\n    /// Set the value of `mac`\n    #[inline]\n    pub fn set_mac(&mut self, value: ::heapless::Vec<u8, 32>) -> &mut Self {\n        self.r#mac = value.into();\n        self\n    }\n    /// Builder method that sets the value of `mac`. Useful for initializing the message.\n    #[inline]\n    pub fn init_mac(mut self, value: ::heapless::Vec<u8, 32>) -> Self {\n        self.r#mac = value.into();\n        self\n    }\n    /// Return a reference to `band_mode`\n    #[inline]\n    pub fn r#band_mode(&self) -> &i32 {\n        &self.r#band_mode\n    }\n    /// Return a mutable reference to `band_mode`\n    #[inline]\n    pub fn mut_band_mode(&mut self) -> &mut i32 {\n        &mut self.r#band_mode\n    }\n    /// Set the value of `band_mode`\n    #[inline]\n    pub fn set_band_mode(&mut self, value: i32) -> &mut Self {\n        self.r#band_mode = value.into();\n        self\n    }\n    /// Builder method that sets the value of `band_mode`. Useful for initializing the message.\n    #[inline]\n    pub fn init_band_mode(mut self, value: i32) -> Self {\n        self.r#band_mode = value.into();\n        self\n    }\n}\nimpl ::micropb::MessageDecode for CtrlMsg_Resp_StartSoftAP {\n    fn decode<IMPL_MICROPB_READ: ::micropb::PbRead>(\n        &mut self,\n        decoder: &mut ::micropb::PbDecoder<IMPL_MICROPB_READ>,\n        len: usize,\n    ) -> Result<(), ::micropb::DecodeError<IMPL_MICROPB_READ::Error>> {\n        use ::micropb::{FieldDecode, PbBytes, PbMap, PbString, PbVec};\n        let before = decoder.bytes_read();\n        while decoder.bytes_read() - before < len {\n            let tag = decoder.decode_tag()?;\n            match tag.field_num() {\n                0 => return Err(::micropb::DecodeError::ZeroField),\n                1u32 => {\n                    let mut_ref = &mut self.r#resp;\n                    {\n                        let val = decoder.decode_int32()?;\n                        let val_ref = &val;\n                        if *val_ref != 0 {\n                            *mut_ref = val as _;\n                        }\n                    };\n                }\n                2u32 => {\n                    let mut_ref = &mut self.r#mac;\n                    {\n                        decoder.decode_bytes(mut_ref, ::micropb::Presence::Implicit)?;\n                    };\n                }\n                3u32 => {\n                    let mut_ref = &mut self.r#band_mode;\n                    {\n                        let val = decoder.decode_int32()?;\n                        let val_ref = &val;\n                        if *val_ref != 0 {\n                            *mut_ref = val as _;\n                        }\n                    };\n                }\n                _ => {\n                    decoder.skip_wire_value(tag.wire_type())?;\n                }\n            }\n        }\n        Ok(())\n    }\n}\nimpl ::micropb::MessageEncode for CtrlMsg_Resp_StartSoftAP {\n    const MAX_SIZE: ::core::result::Result<usize, &'static str> = 'msg: {\n        let mut max_size = 0;\n        match ::micropb::const_map!(::core::result::Result::Ok(10usize), |size| size + 1usize) {\n            ::core::result::Result::Ok(size) => {\n                max_size += size;\n            }\n            ::core::result::Result::Err(err) => {\n                break 'msg (::core::result::Result::<usize, _>::Err(err));\n            }\n        }\n        match ::micropb::const_map!(::core::result::Result::Ok(33usize), |size| size + 1usize) {\n            ::core::result::Result::Ok(size) => {\n                max_size += size;\n            }\n            ::core::result::Result::Err(err) => {\n                break 'msg (::core::result::Result::<usize, _>::Err(err));\n            }\n        }\n        match ::micropb::const_map!(::core::result::Result::Ok(10usize), |size| size + 1usize) {\n            ::core::result::Result::Ok(size) => {\n                max_size += size;\n            }\n            ::core::result::Result::Err(err) => {\n                break 'msg (::core::result::Result::<usize, _>::Err(err));\n            }\n        }\n        ::core::result::Result::Ok(max_size)\n    };\n    fn encode<IMPL_MICROPB_WRITE: ::micropb::PbWrite>(\n        &self,\n        encoder: &mut ::micropb::PbEncoder<IMPL_MICROPB_WRITE>,\n    ) -> Result<(), IMPL_MICROPB_WRITE::Error> {\n        use ::micropb::{FieldEncode, PbMap};\n        {\n            let val_ref = &self.r#resp;\n            if *val_ref != 0 {\n                encoder.encode_varint32(8u32)?;\n                encoder.encode_int32(*val_ref as _)?;\n            }\n        }\n        {\n            let val_ref = &self.r#mac;\n            if !val_ref.is_empty() {\n                encoder.encode_varint32(18u32)?;\n                encoder.encode_bytes(val_ref)?;\n            }\n        }\n        {\n            let val_ref = &self.r#band_mode;\n            if *val_ref != 0 {\n                encoder.encode_varint32(24u32)?;\n                encoder.encode_int32(*val_ref as _)?;\n            }\n        }\n        Ok(())\n    }\n    fn compute_size(&self) -> usize {\n        use ::micropb::{FieldEncode, PbMap};\n        let mut size = 0;\n        {\n            let val_ref = &self.r#resp;\n            if *val_ref != 0 {\n                size += 1usize + ::micropb::size::sizeof_int32(*val_ref as _);\n            }\n        }\n        {\n            let val_ref = &self.r#mac;\n            if !val_ref.is_empty() {\n                size += 1usize + ::micropb::size::sizeof_len_record(val_ref.len());\n            }\n        }\n        {\n            let val_ref = &self.r#band_mode;\n            if *val_ref != 0 {\n                size += 1usize + ::micropb::size::sizeof_int32(*val_ref as _);\n            }\n        }\n        size\n    }\n}\n#[derive(Debug, Default, PartialEq, Clone, Copy)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub struct CtrlMsg_Req_ScanResult {}\nimpl CtrlMsg_Req_ScanResult {}\nimpl ::micropb::MessageDecode for CtrlMsg_Req_ScanResult {\n    fn decode<IMPL_MICROPB_READ: ::micropb::PbRead>(\n        &mut self,\n        decoder: &mut ::micropb::PbDecoder<IMPL_MICROPB_READ>,\n        len: usize,\n    ) -> Result<(), ::micropb::DecodeError<IMPL_MICROPB_READ::Error>> {\n        use ::micropb::{FieldDecode, PbBytes, PbMap, PbString, PbVec};\n        let before = decoder.bytes_read();\n        while decoder.bytes_read() - before < len {\n            let tag = decoder.decode_tag()?;\n            match tag.field_num() {\n                0 => return Err(::micropb::DecodeError::ZeroField),\n                _ => {\n                    decoder.skip_wire_value(tag.wire_type())?;\n                }\n            }\n        }\n        Ok(())\n    }\n}\nimpl ::micropb::MessageEncode for CtrlMsg_Req_ScanResult {\n    const MAX_SIZE: ::core::result::Result<usize, &'static str> = 'msg: {\n        let mut max_size = 0;\n        ::core::result::Result::Ok(max_size)\n    };\n    fn encode<IMPL_MICROPB_WRITE: ::micropb::PbWrite>(\n        &self,\n        encoder: &mut ::micropb::PbEncoder<IMPL_MICROPB_WRITE>,\n    ) -> Result<(), IMPL_MICROPB_WRITE::Error> {\n        use ::micropb::{FieldEncode, PbMap};\n        Ok(())\n    }\n    fn compute_size(&self) -> usize {\n        use ::micropb::{FieldEncode, PbMap};\n        let mut size = 0;\n        size\n    }\n}\n#[derive(Debug, Default, PartialEq, Clone)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub struct CtrlMsg_Resp_ScanResult {\n    pub r#count: u32,\n    pub r#entries: ::heapless::Vec<ScanResult, 16>,\n    pub r#resp: i32,\n}\nimpl CtrlMsg_Resp_ScanResult {\n    /// Return a reference to `count`\n    #[inline]\n    pub fn r#count(&self) -> &u32 {\n        &self.r#count\n    }\n    /// Return a mutable reference to `count`\n    #[inline]\n    pub fn mut_count(&mut self) -> &mut u32 {\n        &mut self.r#count\n    }\n    /// Set the value of `count`\n    #[inline]\n    pub fn set_count(&mut self, value: u32) -> &mut Self {\n        self.r#count = value.into();\n        self\n    }\n    /// Builder method that sets the value of `count`. Useful for initializing the message.\n    #[inline]\n    pub fn init_count(mut self, value: u32) -> Self {\n        self.r#count = value.into();\n        self\n    }\n    /// Return a reference to `resp`\n    #[inline]\n    pub fn r#resp(&self) -> &i32 {\n        &self.r#resp\n    }\n    /// Return a mutable reference to `resp`\n    #[inline]\n    pub fn mut_resp(&mut self) -> &mut i32 {\n        &mut self.r#resp\n    }\n    /// Set the value of `resp`\n    #[inline]\n    pub fn set_resp(&mut self, value: i32) -> &mut Self {\n        self.r#resp = value.into();\n        self\n    }\n    /// Builder method that sets the value of `resp`. Useful for initializing the message.\n    #[inline]\n    pub fn init_resp(mut self, value: i32) -> Self {\n        self.r#resp = value.into();\n        self\n    }\n}\nimpl ::micropb::MessageDecode for CtrlMsg_Resp_ScanResult {\n    fn decode<IMPL_MICROPB_READ: ::micropb::PbRead>(\n        &mut self,\n        decoder: &mut ::micropb::PbDecoder<IMPL_MICROPB_READ>,\n        len: usize,\n    ) -> Result<(), ::micropb::DecodeError<IMPL_MICROPB_READ::Error>> {\n        use ::micropb::{FieldDecode, PbBytes, PbMap, PbString, PbVec};\n        let before = decoder.bytes_read();\n        while decoder.bytes_read() - before < len {\n            let tag = decoder.decode_tag()?;\n            match tag.field_num() {\n                0 => return Err(::micropb::DecodeError::ZeroField),\n                1u32 => {\n                    let mut_ref = &mut self.r#count;\n                    {\n                        let val = decoder.decode_varint32()?;\n                        let val_ref = &val;\n                        if *val_ref != 0 {\n                            *mut_ref = val as _;\n                        }\n                    };\n                }\n                2u32 => {\n                    let mut val: ScanResult = ::core::default::Default::default();\n                    let mut_ref = &mut val;\n                    {\n                        mut_ref.decode_len_delimited(decoder)?;\n                    };\n                    if let (Err(_), false) = (self.r#entries.pb_push(val), decoder.ignore_repeated_cap_err) {\n                        return Err(::micropb::DecodeError::Capacity);\n                    }\n                }\n                3u32 => {\n                    let mut_ref = &mut self.r#resp;\n                    {\n                        let val = decoder.decode_int32()?;\n                        let val_ref = &val;\n                        if *val_ref != 0 {\n                            *mut_ref = val as _;\n                        }\n                    };\n                }\n                _ => {\n                    decoder.skip_wire_value(tag.wire_type())?;\n                }\n            }\n        }\n        Ok(())\n    }\n}\nimpl ::micropb::MessageEncode for CtrlMsg_Resp_ScanResult {\n    const MAX_SIZE: ::core::result::Result<usize, &'static str> = 'msg: {\n        let mut max_size = 0;\n        match ::micropb::const_map!(::core::result::Result::Ok(5usize), |size| size + 1usize) {\n            ::core::result::Result::Ok(size) => {\n                max_size += size;\n            }\n            ::core::result::Result::Err(err) => {\n                break 'msg (::core::result::Result::<usize, _>::Err(err));\n            }\n        }\n        match ::micropb::const_map!(\n            ::micropb::const_map!(<ScanResult as ::micropb::MessageEncode>::MAX_SIZE, |size| {\n                ::micropb::size::sizeof_len_record(size)\n            }),\n            |size| (size + 1usize) * 16usize\n        ) {\n            ::core::result::Result::Ok(size) => {\n                max_size += size;\n            }\n            ::core::result::Result::Err(err) => {\n                break 'msg (::core::result::Result::<usize, _>::Err(err));\n            }\n        }\n        match ::micropb::const_map!(::core::result::Result::Ok(10usize), |size| size + 1usize) {\n            ::core::result::Result::Ok(size) => {\n                max_size += size;\n            }\n            ::core::result::Result::Err(err) => {\n                break 'msg (::core::result::Result::<usize, _>::Err(err));\n            }\n        }\n        ::core::result::Result::Ok(max_size)\n    };\n    fn encode<IMPL_MICROPB_WRITE: ::micropb::PbWrite>(\n        &self,\n        encoder: &mut ::micropb::PbEncoder<IMPL_MICROPB_WRITE>,\n    ) -> Result<(), IMPL_MICROPB_WRITE::Error> {\n        use ::micropb::{FieldEncode, PbMap};\n        {\n            let val_ref = &self.r#count;\n            if *val_ref != 0 {\n                encoder.encode_varint32(8u32)?;\n                encoder.encode_varint32(*val_ref as _)?;\n            }\n        }\n        {\n            for (i, val_ref) in self.r#entries.iter().enumerate() {\n                encoder.encode_varint32(18u32)?;\n                val_ref.encode_len_delimited(encoder)?;\n            }\n        }\n        {\n            let val_ref = &self.r#resp;\n            if *val_ref != 0 {\n                encoder.encode_varint32(24u32)?;\n                encoder.encode_int32(*val_ref as _)?;\n            }\n        }\n        Ok(())\n    }\n    fn compute_size(&self) -> usize {\n        use ::micropb::{FieldEncode, PbMap};\n        let mut size = 0;\n        {\n            let val_ref = &self.r#count;\n            if *val_ref != 0 {\n                size += 1usize + ::micropb::size::sizeof_varint32(*val_ref as _);\n            }\n        }\n        {\n            for (i, val_ref) in self.r#entries.iter().enumerate() {\n                size += 1usize + ::micropb::size::sizeof_len_record(val_ref.compute_size());\n            }\n        }\n        {\n            let val_ref = &self.r#resp;\n            if *val_ref != 0 {\n                size += 1usize + ::micropb::size::sizeof_int32(*val_ref as _);\n            }\n        }\n        size\n    }\n}\n#[derive(Debug, Default, PartialEq, Clone, Copy)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub struct CtrlMsg_Req_SoftAPConnectedSTA {}\nimpl CtrlMsg_Req_SoftAPConnectedSTA {}\nimpl ::micropb::MessageDecode for CtrlMsg_Req_SoftAPConnectedSTA {\n    fn decode<IMPL_MICROPB_READ: ::micropb::PbRead>(\n        &mut self,\n        decoder: &mut ::micropb::PbDecoder<IMPL_MICROPB_READ>,\n        len: usize,\n    ) -> Result<(), ::micropb::DecodeError<IMPL_MICROPB_READ::Error>> {\n        use ::micropb::{FieldDecode, PbBytes, PbMap, PbString, PbVec};\n        let before = decoder.bytes_read();\n        while decoder.bytes_read() - before < len {\n            let tag = decoder.decode_tag()?;\n            match tag.field_num() {\n                0 => return Err(::micropb::DecodeError::ZeroField),\n                _ => {\n                    decoder.skip_wire_value(tag.wire_type())?;\n                }\n            }\n        }\n        Ok(())\n    }\n}\nimpl ::micropb::MessageEncode for CtrlMsg_Req_SoftAPConnectedSTA {\n    const MAX_SIZE: ::core::result::Result<usize, &'static str> = 'msg: {\n        let mut max_size = 0;\n        ::core::result::Result::Ok(max_size)\n    };\n    fn encode<IMPL_MICROPB_WRITE: ::micropb::PbWrite>(\n        &self,\n        encoder: &mut ::micropb::PbEncoder<IMPL_MICROPB_WRITE>,\n    ) -> Result<(), IMPL_MICROPB_WRITE::Error> {\n        use ::micropb::{FieldEncode, PbMap};\n        Ok(())\n    }\n    fn compute_size(&self) -> usize {\n        use ::micropb::{FieldEncode, PbMap};\n        let mut size = 0;\n        size\n    }\n}\n#[derive(Debug, Default, PartialEq, Clone)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub struct CtrlMsg_Resp_SoftAPConnectedSTA {\n    pub r#num: u32,\n    pub r#stations: ::heapless::Vec<ConnectedSTAList, 16>,\n    pub r#resp: i32,\n}\nimpl CtrlMsg_Resp_SoftAPConnectedSTA {\n    /// Return a reference to `num`\n    #[inline]\n    pub fn r#num(&self) -> &u32 {\n        &self.r#num\n    }\n    /// Return a mutable reference to `num`\n    #[inline]\n    pub fn mut_num(&mut self) -> &mut u32 {\n        &mut self.r#num\n    }\n    /// Set the value of `num`\n    #[inline]\n    pub fn set_num(&mut self, value: u32) -> &mut Self {\n        self.r#num = value.into();\n        self\n    }\n    /// Builder method that sets the value of `num`. Useful for initializing the message.\n    #[inline]\n    pub fn init_num(mut self, value: u32) -> Self {\n        self.r#num = value.into();\n        self\n    }\n    /// Return a reference to `resp`\n    #[inline]\n    pub fn r#resp(&self) -> &i32 {\n        &self.r#resp\n    }\n    /// Return a mutable reference to `resp`\n    #[inline]\n    pub fn mut_resp(&mut self) -> &mut i32 {\n        &mut self.r#resp\n    }\n    /// Set the value of `resp`\n    #[inline]\n    pub fn set_resp(&mut self, value: i32) -> &mut Self {\n        self.r#resp = value.into();\n        self\n    }\n    /// Builder method that sets the value of `resp`. Useful for initializing the message.\n    #[inline]\n    pub fn init_resp(mut self, value: i32) -> Self {\n        self.r#resp = value.into();\n        self\n    }\n}\nimpl ::micropb::MessageDecode for CtrlMsg_Resp_SoftAPConnectedSTA {\n    fn decode<IMPL_MICROPB_READ: ::micropb::PbRead>(\n        &mut self,\n        decoder: &mut ::micropb::PbDecoder<IMPL_MICROPB_READ>,\n        len: usize,\n    ) -> Result<(), ::micropb::DecodeError<IMPL_MICROPB_READ::Error>> {\n        use ::micropb::{FieldDecode, PbBytes, PbMap, PbString, PbVec};\n        let before = decoder.bytes_read();\n        while decoder.bytes_read() - before < len {\n            let tag = decoder.decode_tag()?;\n            match tag.field_num() {\n                0 => return Err(::micropb::DecodeError::ZeroField),\n                1u32 => {\n                    let mut_ref = &mut self.r#num;\n                    {\n                        let val = decoder.decode_varint32()?;\n                        let val_ref = &val;\n                        if *val_ref != 0 {\n                            *mut_ref = val as _;\n                        }\n                    };\n                }\n                2u32 => {\n                    let mut val: ConnectedSTAList = ::core::default::Default::default();\n                    let mut_ref = &mut val;\n                    {\n                        mut_ref.decode_len_delimited(decoder)?;\n                    };\n                    if let (Err(_), false) = (self.r#stations.pb_push(val), decoder.ignore_repeated_cap_err) {\n                        return Err(::micropb::DecodeError::Capacity);\n                    }\n                }\n                3u32 => {\n                    let mut_ref = &mut self.r#resp;\n                    {\n                        let val = decoder.decode_int32()?;\n                        let val_ref = &val;\n                        if *val_ref != 0 {\n                            *mut_ref = val as _;\n                        }\n                    };\n                }\n                _ => {\n                    decoder.skip_wire_value(tag.wire_type())?;\n                }\n            }\n        }\n        Ok(())\n    }\n}\nimpl ::micropb::MessageEncode for CtrlMsg_Resp_SoftAPConnectedSTA {\n    const MAX_SIZE: ::core::result::Result<usize, &'static str> = 'msg: {\n        let mut max_size = 0;\n        match ::micropb::const_map!(::core::result::Result::Ok(5usize), |size| size + 1usize) {\n            ::core::result::Result::Ok(size) => {\n                max_size += size;\n            }\n            ::core::result::Result::Err(err) => {\n                break 'msg (::core::result::Result::<usize, _>::Err(err));\n            }\n        }\n        match ::micropb::const_map!(\n            ::micropb::const_map!(<ConnectedSTAList as ::micropb::MessageEncode>::MAX_SIZE, |size| {\n                ::micropb::size::sizeof_len_record(size)\n            }),\n            |size| (size + 1usize) * 16usize\n        ) {\n            ::core::result::Result::Ok(size) => {\n                max_size += size;\n            }\n            ::core::result::Result::Err(err) => {\n                break 'msg (::core::result::Result::<usize, _>::Err(err));\n            }\n        }\n        match ::micropb::const_map!(::core::result::Result::Ok(10usize), |size| size + 1usize) {\n            ::core::result::Result::Ok(size) => {\n                max_size += size;\n            }\n            ::core::result::Result::Err(err) => {\n                break 'msg (::core::result::Result::<usize, _>::Err(err));\n            }\n        }\n        ::core::result::Result::Ok(max_size)\n    };\n    fn encode<IMPL_MICROPB_WRITE: ::micropb::PbWrite>(\n        &self,\n        encoder: &mut ::micropb::PbEncoder<IMPL_MICROPB_WRITE>,\n    ) -> Result<(), IMPL_MICROPB_WRITE::Error> {\n        use ::micropb::{FieldEncode, PbMap};\n        {\n            let val_ref = &self.r#num;\n            if *val_ref != 0 {\n                encoder.encode_varint32(8u32)?;\n                encoder.encode_varint32(*val_ref as _)?;\n            }\n        }\n        {\n            for (i, val_ref) in self.r#stations.iter().enumerate() {\n                encoder.encode_varint32(18u32)?;\n                val_ref.encode_len_delimited(encoder)?;\n            }\n        }\n        {\n            let val_ref = &self.r#resp;\n            if *val_ref != 0 {\n                encoder.encode_varint32(24u32)?;\n                encoder.encode_int32(*val_ref as _)?;\n            }\n        }\n        Ok(())\n    }\n    fn compute_size(&self) -> usize {\n        use ::micropb::{FieldEncode, PbMap};\n        let mut size = 0;\n        {\n            let val_ref = &self.r#num;\n            if *val_ref != 0 {\n                size += 1usize + ::micropb::size::sizeof_varint32(*val_ref as _);\n            }\n        }\n        {\n            for (i, val_ref) in self.r#stations.iter().enumerate() {\n                size += 1usize + ::micropb::size::sizeof_len_record(val_ref.compute_size());\n            }\n        }\n        {\n            let val_ref = &self.r#resp;\n            if *val_ref != 0 {\n                size += 1usize + ::micropb::size::sizeof_int32(*val_ref as _);\n            }\n        }\n        size\n    }\n}\n#[derive(Debug, Default, PartialEq, Clone, Copy)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub struct CtrlMsg_Req_OTABegin {}\nimpl CtrlMsg_Req_OTABegin {}\nimpl ::micropb::MessageDecode for CtrlMsg_Req_OTABegin {\n    fn decode<IMPL_MICROPB_READ: ::micropb::PbRead>(\n        &mut self,\n        decoder: &mut ::micropb::PbDecoder<IMPL_MICROPB_READ>,\n        len: usize,\n    ) -> Result<(), ::micropb::DecodeError<IMPL_MICROPB_READ::Error>> {\n        use ::micropb::{FieldDecode, PbBytes, PbMap, PbString, PbVec};\n        let before = decoder.bytes_read();\n        while decoder.bytes_read() - before < len {\n            let tag = decoder.decode_tag()?;\n            match tag.field_num() {\n                0 => return Err(::micropb::DecodeError::ZeroField),\n                _ => {\n                    decoder.skip_wire_value(tag.wire_type())?;\n                }\n            }\n        }\n        Ok(())\n    }\n}\nimpl ::micropb::MessageEncode for CtrlMsg_Req_OTABegin {\n    const MAX_SIZE: ::core::result::Result<usize, &'static str> = 'msg: {\n        let mut max_size = 0;\n        ::core::result::Result::Ok(max_size)\n    };\n    fn encode<IMPL_MICROPB_WRITE: ::micropb::PbWrite>(\n        &self,\n        encoder: &mut ::micropb::PbEncoder<IMPL_MICROPB_WRITE>,\n    ) -> Result<(), IMPL_MICROPB_WRITE::Error> {\n        use ::micropb::{FieldEncode, PbMap};\n        Ok(())\n    }\n    fn compute_size(&self) -> usize {\n        use ::micropb::{FieldEncode, PbMap};\n        let mut size = 0;\n        size\n    }\n}\n#[derive(Debug, Default, PartialEq, Clone, Copy)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub struct CtrlMsg_Resp_OTABegin {\n    pub r#resp: i32,\n}\nimpl CtrlMsg_Resp_OTABegin {\n    /// Return a reference to `resp`\n    #[inline]\n    pub fn r#resp(&self) -> &i32 {\n        &self.r#resp\n    }\n    /// Return a mutable reference to `resp`\n    #[inline]\n    pub fn mut_resp(&mut self) -> &mut i32 {\n        &mut self.r#resp\n    }\n    /// Set the value of `resp`\n    #[inline]\n    pub fn set_resp(&mut self, value: i32) -> &mut Self {\n        self.r#resp = value.into();\n        self\n    }\n    /// Builder method that sets the value of `resp`. Useful for initializing the message.\n    #[inline]\n    pub fn init_resp(mut self, value: i32) -> Self {\n        self.r#resp = value.into();\n        self\n    }\n}\nimpl ::micropb::MessageDecode for CtrlMsg_Resp_OTABegin {\n    fn decode<IMPL_MICROPB_READ: ::micropb::PbRead>(\n        &mut self,\n        decoder: &mut ::micropb::PbDecoder<IMPL_MICROPB_READ>,\n        len: usize,\n    ) -> Result<(), ::micropb::DecodeError<IMPL_MICROPB_READ::Error>> {\n        use ::micropb::{FieldDecode, PbBytes, PbMap, PbString, PbVec};\n        let before = decoder.bytes_read();\n        while decoder.bytes_read() - before < len {\n            let tag = decoder.decode_tag()?;\n            match tag.field_num() {\n                0 => return Err(::micropb::DecodeError::ZeroField),\n                1u32 => {\n                    let mut_ref = &mut self.r#resp;\n                    {\n                        let val = decoder.decode_int32()?;\n                        let val_ref = &val;\n                        if *val_ref != 0 {\n                            *mut_ref = val as _;\n                        }\n                    };\n                }\n                _ => {\n                    decoder.skip_wire_value(tag.wire_type())?;\n                }\n            }\n        }\n        Ok(())\n    }\n}\nimpl ::micropb::MessageEncode for CtrlMsg_Resp_OTABegin {\n    const MAX_SIZE: ::core::result::Result<usize, &'static str> = 'msg: {\n        let mut max_size = 0;\n        match ::micropb::const_map!(::core::result::Result::Ok(10usize), |size| size + 1usize) {\n            ::core::result::Result::Ok(size) => {\n                max_size += size;\n            }\n            ::core::result::Result::Err(err) => {\n                break 'msg (::core::result::Result::<usize, _>::Err(err));\n            }\n        }\n        ::core::result::Result::Ok(max_size)\n    };\n    fn encode<IMPL_MICROPB_WRITE: ::micropb::PbWrite>(\n        &self,\n        encoder: &mut ::micropb::PbEncoder<IMPL_MICROPB_WRITE>,\n    ) -> Result<(), IMPL_MICROPB_WRITE::Error> {\n        use ::micropb::{FieldEncode, PbMap};\n        {\n            let val_ref = &self.r#resp;\n            if *val_ref != 0 {\n                encoder.encode_varint32(8u32)?;\n                encoder.encode_int32(*val_ref as _)?;\n            }\n        }\n        Ok(())\n    }\n    fn compute_size(&self) -> usize {\n        use ::micropb::{FieldEncode, PbMap};\n        let mut size = 0;\n        {\n            let val_ref = &self.r#resp;\n            if *val_ref != 0 {\n                size += 1usize + ::micropb::size::sizeof_int32(*val_ref as _);\n            }\n        }\n        size\n    }\n}\n#[derive(Debug, Default, PartialEq, Clone)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub struct CtrlMsg_Req_OTAWrite {\n    pub r#ota_data: ::heapless::Vec<u8, 256>,\n}\nimpl CtrlMsg_Req_OTAWrite {\n    /// Return a reference to `ota_data`\n    #[inline]\n    pub fn r#ota_data(&self) -> &::heapless::Vec<u8, 256> {\n        &self.r#ota_data\n    }\n    /// Return a mutable reference to `ota_data`\n    #[inline]\n    pub fn mut_ota_data(&mut self) -> &mut ::heapless::Vec<u8, 256> {\n        &mut self.r#ota_data\n    }\n    /// Set the value of `ota_data`\n    #[inline]\n    pub fn set_ota_data(&mut self, value: ::heapless::Vec<u8, 256>) -> &mut Self {\n        self.r#ota_data = value.into();\n        self\n    }\n    /// Builder method that sets the value of `ota_data`. Useful for initializing the message.\n    #[inline]\n    pub fn init_ota_data(mut self, value: ::heapless::Vec<u8, 256>) -> Self {\n        self.r#ota_data = value.into();\n        self\n    }\n}\nimpl ::micropb::MessageDecode for CtrlMsg_Req_OTAWrite {\n    fn decode<IMPL_MICROPB_READ: ::micropb::PbRead>(\n        &mut self,\n        decoder: &mut ::micropb::PbDecoder<IMPL_MICROPB_READ>,\n        len: usize,\n    ) -> Result<(), ::micropb::DecodeError<IMPL_MICROPB_READ::Error>> {\n        use ::micropb::{FieldDecode, PbBytes, PbMap, PbString, PbVec};\n        let before = decoder.bytes_read();\n        while decoder.bytes_read() - before < len {\n            let tag = decoder.decode_tag()?;\n            match tag.field_num() {\n                0 => return Err(::micropb::DecodeError::ZeroField),\n                1u32 => {\n                    let mut_ref = &mut self.r#ota_data;\n                    {\n                        decoder.decode_bytes(mut_ref, ::micropb::Presence::Implicit)?;\n                    };\n                }\n                _ => {\n                    decoder.skip_wire_value(tag.wire_type())?;\n                }\n            }\n        }\n        Ok(())\n    }\n}\nimpl ::micropb::MessageEncode for CtrlMsg_Req_OTAWrite {\n    const MAX_SIZE: ::core::result::Result<usize, &'static str> = 'msg: {\n        let mut max_size = 0;\n        match ::micropb::const_map!(::core::result::Result::Ok(258usize), |size| size + 1usize) {\n            ::core::result::Result::Ok(size) => {\n                max_size += size;\n            }\n            ::core::result::Result::Err(err) => {\n                break 'msg (::core::result::Result::<usize, _>::Err(err));\n            }\n        }\n        ::core::result::Result::Ok(max_size)\n    };\n    fn encode<IMPL_MICROPB_WRITE: ::micropb::PbWrite>(\n        &self,\n        encoder: &mut ::micropb::PbEncoder<IMPL_MICROPB_WRITE>,\n    ) -> Result<(), IMPL_MICROPB_WRITE::Error> {\n        use ::micropb::{FieldEncode, PbMap};\n        {\n            let val_ref = &self.r#ota_data;\n            if !val_ref.is_empty() {\n                encoder.encode_varint32(10u32)?;\n                encoder.encode_bytes(val_ref)?;\n            }\n        }\n        Ok(())\n    }\n    fn compute_size(&self) -> usize {\n        use ::micropb::{FieldEncode, PbMap};\n        let mut size = 0;\n        {\n            let val_ref = &self.r#ota_data;\n            if !val_ref.is_empty() {\n                size += 1usize + ::micropb::size::sizeof_len_record(val_ref.len());\n            }\n        }\n        size\n    }\n}\n#[derive(Debug, Default, PartialEq, Clone, Copy)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub struct CtrlMsg_Resp_OTAWrite {\n    pub r#resp: i32,\n}\nimpl CtrlMsg_Resp_OTAWrite {\n    /// Return a reference to `resp`\n    #[inline]\n    pub fn r#resp(&self) -> &i32 {\n        &self.r#resp\n    }\n    /// Return a mutable reference to `resp`\n    #[inline]\n    pub fn mut_resp(&mut self) -> &mut i32 {\n        &mut self.r#resp\n    }\n    /// Set the value of `resp`\n    #[inline]\n    pub fn set_resp(&mut self, value: i32) -> &mut Self {\n        self.r#resp = value.into();\n        self\n    }\n    /// Builder method that sets the value of `resp`. Useful for initializing the message.\n    #[inline]\n    pub fn init_resp(mut self, value: i32) -> Self {\n        self.r#resp = value.into();\n        self\n    }\n}\nimpl ::micropb::MessageDecode for CtrlMsg_Resp_OTAWrite {\n    fn decode<IMPL_MICROPB_READ: ::micropb::PbRead>(\n        &mut self,\n        decoder: &mut ::micropb::PbDecoder<IMPL_MICROPB_READ>,\n        len: usize,\n    ) -> Result<(), ::micropb::DecodeError<IMPL_MICROPB_READ::Error>> {\n        use ::micropb::{FieldDecode, PbBytes, PbMap, PbString, PbVec};\n        let before = decoder.bytes_read();\n        while decoder.bytes_read() - before < len {\n            let tag = decoder.decode_tag()?;\n            match tag.field_num() {\n                0 => return Err(::micropb::DecodeError::ZeroField),\n                1u32 => {\n                    let mut_ref = &mut self.r#resp;\n                    {\n                        let val = decoder.decode_int32()?;\n                        let val_ref = &val;\n                        if *val_ref != 0 {\n                            *mut_ref = val as _;\n                        }\n                    };\n                }\n                _ => {\n                    decoder.skip_wire_value(tag.wire_type())?;\n                }\n            }\n        }\n        Ok(())\n    }\n}\nimpl ::micropb::MessageEncode for CtrlMsg_Resp_OTAWrite {\n    const MAX_SIZE: ::core::result::Result<usize, &'static str> = 'msg: {\n        let mut max_size = 0;\n        match ::micropb::const_map!(::core::result::Result::Ok(10usize), |size| size + 1usize) {\n            ::core::result::Result::Ok(size) => {\n                max_size += size;\n            }\n            ::core::result::Result::Err(err) => {\n                break 'msg (::core::result::Result::<usize, _>::Err(err));\n            }\n        }\n        ::core::result::Result::Ok(max_size)\n    };\n    fn encode<IMPL_MICROPB_WRITE: ::micropb::PbWrite>(\n        &self,\n        encoder: &mut ::micropb::PbEncoder<IMPL_MICROPB_WRITE>,\n    ) -> Result<(), IMPL_MICROPB_WRITE::Error> {\n        use ::micropb::{FieldEncode, PbMap};\n        {\n            let val_ref = &self.r#resp;\n            if *val_ref != 0 {\n                encoder.encode_varint32(8u32)?;\n                encoder.encode_int32(*val_ref as _)?;\n            }\n        }\n        Ok(())\n    }\n    fn compute_size(&self) -> usize {\n        use ::micropb::{FieldEncode, PbMap};\n        let mut size = 0;\n        {\n            let val_ref = &self.r#resp;\n            if *val_ref != 0 {\n                size += 1usize + ::micropb::size::sizeof_int32(*val_ref as _);\n            }\n        }\n        size\n    }\n}\n#[derive(Debug, Default, PartialEq, Clone, Copy)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub struct CtrlMsg_Req_OTAEnd {}\nimpl CtrlMsg_Req_OTAEnd {}\nimpl ::micropb::MessageDecode for CtrlMsg_Req_OTAEnd {\n    fn decode<IMPL_MICROPB_READ: ::micropb::PbRead>(\n        &mut self,\n        decoder: &mut ::micropb::PbDecoder<IMPL_MICROPB_READ>,\n        len: usize,\n    ) -> Result<(), ::micropb::DecodeError<IMPL_MICROPB_READ::Error>> {\n        use ::micropb::{FieldDecode, PbBytes, PbMap, PbString, PbVec};\n        let before = decoder.bytes_read();\n        while decoder.bytes_read() - before < len {\n            let tag = decoder.decode_tag()?;\n            match tag.field_num() {\n                0 => return Err(::micropb::DecodeError::ZeroField),\n                _ => {\n                    decoder.skip_wire_value(tag.wire_type())?;\n                }\n            }\n        }\n        Ok(())\n    }\n}\nimpl ::micropb::MessageEncode for CtrlMsg_Req_OTAEnd {\n    const MAX_SIZE: ::core::result::Result<usize, &'static str> = 'msg: {\n        let mut max_size = 0;\n        ::core::result::Result::Ok(max_size)\n    };\n    fn encode<IMPL_MICROPB_WRITE: ::micropb::PbWrite>(\n        &self,\n        encoder: &mut ::micropb::PbEncoder<IMPL_MICROPB_WRITE>,\n    ) -> Result<(), IMPL_MICROPB_WRITE::Error> {\n        use ::micropb::{FieldEncode, PbMap};\n        Ok(())\n    }\n    fn compute_size(&self) -> usize {\n        use ::micropb::{FieldEncode, PbMap};\n        let mut size = 0;\n        size\n    }\n}\n#[derive(Debug, Default, PartialEq, Clone, Copy)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub struct CtrlMsg_Resp_OTAEnd {\n    pub r#resp: i32,\n}\nimpl CtrlMsg_Resp_OTAEnd {\n    /// Return a reference to `resp`\n    #[inline]\n    pub fn r#resp(&self) -> &i32 {\n        &self.r#resp\n    }\n    /// Return a mutable reference to `resp`\n    #[inline]\n    pub fn mut_resp(&mut self) -> &mut i32 {\n        &mut self.r#resp\n    }\n    /// Set the value of `resp`\n    #[inline]\n    pub fn set_resp(&mut self, value: i32) -> &mut Self {\n        self.r#resp = value.into();\n        self\n    }\n    /// Builder method that sets the value of `resp`. Useful for initializing the message.\n    #[inline]\n    pub fn init_resp(mut self, value: i32) -> Self {\n        self.r#resp = value.into();\n        self\n    }\n}\nimpl ::micropb::MessageDecode for CtrlMsg_Resp_OTAEnd {\n    fn decode<IMPL_MICROPB_READ: ::micropb::PbRead>(\n        &mut self,\n        decoder: &mut ::micropb::PbDecoder<IMPL_MICROPB_READ>,\n        len: usize,\n    ) -> Result<(), ::micropb::DecodeError<IMPL_MICROPB_READ::Error>> {\n        use ::micropb::{FieldDecode, PbBytes, PbMap, PbString, PbVec};\n        let before = decoder.bytes_read();\n        while decoder.bytes_read() - before < len {\n            let tag = decoder.decode_tag()?;\n            match tag.field_num() {\n                0 => return Err(::micropb::DecodeError::ZeroField),\n                1u32 => {\n                    let mut_ref = &mut self.r#resp;\n                    {\n                        let val = decoder.decode_int32()?;\n                        let val_ref = &val;\n                        if *val_ref != 0 {\n                            *mut_ref = val as _;\n                        }\n                    };\n                }\n                _ => {\n                    decoder.skip_wire_value(tag.wire_type())?;\n                }\n            }\n        }\n        Ok(())\n    }\n}\nimpl ::micropb::MessageEncode for CtrlMsg_Resp_OTAEnd {\n    const MAX_SIZE: ::core::result::Result<usize, &'static str> = 'msg: {\n        let mut max_size = 0;\n        match ::micropb::const_map!(::core::result::Result::Ok(10usize), |size| size + 1usize) {\n            ::core::result::Result::Ok(size) => {\n                max_size += size;\n            }\n            ::core::result::Result::Err(err) => {\n                break 'msg (::core::result::Result::<usize, _>::Err(err));\n            }\n        }\n        ::core::result::Result::Ok(max_size)\n    };\n    fn encode<IMPL_MICROPB_WRITE: ::micropb::PbWrite>(\n        &self,\n        encoder: &mut ::micropb::PbEncoder<IMPL_MICROPB_WRITE>,\n    ) -> Result<(), IMPL_MICROPB_WRITE::Error> {\n        use ::micropb::{FieldEncode, PbMap};\n        {\n            let val_ref = &self.r#resp;\n            if *val_ref != 0 {\n                encoder.encode_varint32(8u32)?;\n                encoder.encode_int32(*val_ref as _)?;\n            }\n        }\n        Ok(())\n    }\n    fn compute_size(&self) -> usize {\n        use ::micropb::{FieldEncode, PbMap};\n        let mut size = 0;\n        {\n            let val_ref = &self.r#resp;\n            if *val_ref != 0 {\n                size += 1usize + ::micropb::size::sizeof_int32(*val_ref as _);\n            }\n        }\n        size\n    }\n}\n#[derive(Debug, Default, PartialEq, Clone)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub struct CtrlMsg_Req_VendorIEData {\n    pub r#element_id: i32,\n    pub r#length: i32,\n    pub r#vendor_oui: ::heapless::Vec<u8, 32>,\n    pub r#vendor_oui_type: i32,\n    pub r#payload: ::heapless::Vec<u8, 64>,\n}\nimpl CtrlMsg_Req_VendorIEData {\n    /// Return a reference to `element_id`\n    #[inline]\n    pub fn r#element_id(&self) -> &i32 {\n        &self.r#element_id\n    }\n    /// Return a mutable reference to `element_id`\n    #[inline]\n    pub fn mut_element_id(&mut self) -> &mut i32 {\n        &mut self.r#element_id\n    }\n    /// Set the value of `element_id`\n    #[inline]\n    pub fn set_element_id(&mut self, value: i32) -> &mut Self {\n        self.r#element_id = value.into();\n        self\n    }\n    /// Builder method that sets the value of `element_id`. Useful for initializing the message.\n    #[inline]\n    pub fn init_element_id(mut self, value: i32) -> Self {\n        self.r#element_id = value.into();\n        self\n    }\n    /// Return a reference to `length`\n    #[inline]\n    pub fn r#length(&self) -> &i32 {\n        &self.r#length\n    }\n    /// Return a mutable reference to `length`\n    #[inline]\n    pub fn mut_length(&mut self) -> &mut i32 {\n        &mut self.r#length\n    }\n    /// Set the value of `length`\n    #[inline]\n    pub fn set_length(&mut self, value: i32) -> &mut Self {\n        self.r#length = value.into();\n        self\n    }\n    /// Builder method that sets the value of `length`. Useful for initializing the message.\n    #[inline]\n    pub fn init_length(mut self, value: i32) -> Self {\n        self.r#length = value.into();\n        self\n    }\n    /// Return a reference to `vendor_oui`\n    #[inline]\n    pub fn r#vendor_oui(&self) -> &::heapless::Vec<u8, 32> {\n        &self.r#vendor_oui\n    }\n    /// Return a mutable reference to `vendor_oui`\n    #[inline]\n    pub fn mut_vendor_oui(&mut self) -> &mut ::heapless::Vec<u8, 32> {\n        &mut self.r#vendor_oui\n    }\n    /// Set the value of `vendor_oui`\n    #[inline]\n    pub fn set_vendor_oui(&mut self, value: ::heapless::Vec<u8, 32>) -> &mut Self {\n        self.r#vendor_oui = value.into();\n        self\n    }\n    /// Builder method that sets the value of `vendor_oui`. Useful for initializing the message.\n    #[inline]\n    pub fn init_vendor_oui(mut self, value: ::heapless::Vec<u8, 32>) -> Self {\n        self.r#vendor_oui = value.into();\n        self\n    }\n    /// Return a reference to `vendor_oui_type`\n    #[inline]\n    pub fn r#vendor_oui_type(&self) -> &i32 {\n        &self.r#vendor_oui_type\n    }\n    /// Return a mutable reference to `vendor_oui_type`\n    #[inline]\n    pub fn mut_vendor_oui_type(&mut self) -> &mut i32 {\n        &mut self.r#vendor_oui_type\n    }\n    /// Set the value of `vendor_oui_type`\n    #[inline]\n    pub fn set_vendor_oui_type(&mut self, value: i32) -> &mut Self {\n        self.r#vendor_oui_type = value.into();\n        self\n    }\n    /// Builder method that sets the value of `vendor_oui_type`. Useful for initializing the message.\n    #[inline]\n    pub fn init_vendor_oui_type(mut self, value: i32) -> Self {\n        self.r#vendor_oui_type = value.into();\n        self\n    }\n    /// Return a reference to `payload`\n    #[inline]\n    pub fn r#payload(&self) -> &::heapless::Vec<u8, 64> {\n        &self.r#payload\n    }\n    /// Return a mutable reference to `payload`\n    #[inline]\n    pub fn mut_payload(&mut self) -> &mut ::heapless::Vec<u8, 64> {\n        &mut self.r#payload\n    }\n    /// Set the value of `payload`\n    #[inline]\n    pub fn set_payload(&mut self, value: ::heapless::Vec<u8, 64>) -> &mut Self {\n        self.r#payload = value.into();\n        self\n    }\n    /// Builder method that sets the value of `payload`. Useful for initializing the message.\n    #[inline]\n    pub fn init_payload(mut self, value: ::heapless::Vec<u8, 64>) -> Self {\n        self.r#payload = value.into();\n        self\n    }\n}\nimpl ::micropb::MessageDecode for CtrlMsg_Req_VendorIEData {\n    fn decode<IMPL_MICROPB_READ: ::micropb::PbRead>(\n        &mut self,\n        decoder: &mut ::micropb::PbDecoder<IMPL_MICROPB_READ>,\n        len: usize,\n    ) -> Result<(), ::micropb::DecodeError<IMPL_MICROPB_READ::Error>> {\n        use ::micropb::{FieldDecode, PbBytes, PbMap, PbString, PbVec};\n        let before = decoder.bytes_read();\n        while decoder.bytes_read() - before < len {\n            let tag = decoder.decode_tag()?;\n            match tag.field_num() {\n                0 => return Err(::micropb::DecodeError::ZeroField),\n                1u32 => {\n                    let mut_ref = &mut self.r#element_id;\n                    {\n                        let val = decoder.decode_int32()?;\n                        let val_ref = &val;\n                        if *val_ref != 0 {\n                            *mut_ref = val as _;\n                        }\n                    };\n                }\n                2u32 => {\n                    let mut_ref = &mut self.r#length;\n                    {\n                        let val = decoder.decode_int32()?;\n                        let val_ref = &val;\n                        if *val_ref != 0 {\n                            *mut_ref = val as _;\n                        }\n                    };\n                }\n                3u32 => {\n                    let mut_ref = &mut self.r#vendor_oui;\n                    {\n                        decoder.decode_bytes(mut_ref, ::micropb::Presence::Implicit)?;\n                    };\n                }\n                4u32 => {\n                    let mut_ref = &mut self.r#vendor_oui_type;\n                    {\n                        let val = decoder.decode_int32()?;\n                        let val_ref = &val;\n                        if *val_ref != 0 {\n                            *mut_ref = val as _;\n                        }\n                    };\n                }\n                5u32 => {\n                    let mut_ref = &mut self.r#payload;\n                    {\n                        decoder.decode_bytes(mut_ref, ::micropb::Presence::Implicit)?;\n                    };\n                }\n                _ => {\n                    decoder.skip_wire_value(tag.wire_type())?;\n                }\n            }\n        }\n        Ok(())\n    }\n}\nimpl ::micropb::MessageEncode for CtrlMsg_Req_VendorIEData {\n    const MAX_SIZE: ::core::result::Result<usize, &'static str> = 'msg: {\n        let mut max_size = 0;\n        match ::micropb::const_map!(::core::result::Result::Ok(10usize), |size| size + 1usize) {\n            ::core::result::Result::Ok(size) => {\n                max_size += size;\n            }\n            ::core::result::Result::Err(err) => {\n                break 'msg (::core::result::Result::<usize, _>::Err(err));\n            }\n        }\n        match ::micropb::const_map!(::core::result::Result::Ok(10usize), |size| size + 1usize) {\n            ::core::result::Result::Ok(size) => {\n                max_size += size;\n            }\n            ::core::result::Result::Err(err) => {\n                break 'msg (::core::result::Result::<usize, _>::Err(err));\n            }\n        }\n        match ::micropb::const_map!(::core::result::Result::Ok(33usize), |size| size + 1usize) {\n            ::core::result::Result::Ok(size) => {\n                max_size += size;\n            }\n            ::core::result::Result::Err(err) => {\n                break 'msg (::core::result::Result::<usize, _>::Err(err));\n            }\n        }\n        match ::micropb::const_map!(::core::result::Result::Ok(10usize), |size| size + 1usize) {\n            ::core::result::Result::Ok(size) => {\n                max_size += size;\n            }\n            ::core::result::Result::Err(err) => {\n                break 'msg (::core::result::Result::<usize, _>::Err(err));\n            }\n        }\n        match ::micropb::const_map!(::core::result::Result::Ok(65usize), |size| size + 1usize) {\n            ::core::result::Result::Ok(size) => {\n                max_size += size;\n            }\n            ::core::result::Result::Err(err) => {\n                break 'msg (::core::result::Result::<usize, _>::Err(err));\n            }\n        }\n        ::core::result::Result::Ok(max_size)\n    };\n    fn encode<IMPL_MICROPB_WRITE: ::micropb::PbWrite>(\n        &self,\n        encoder: &mut ::micropb::PbEncoder<IMPL_MICROPB_WRITE>,\n    ) -> Result<(), IMPL_MICROPB_WRITE::Error> {\n        use ::micropb::{FieldEncode, PbMap};\n        {\n            let val_ref = &self.r#element_id;\n            if *val_ref != 0 {\n                encoder.encode_varint32(8u32)?;\n                encoder.encode_int32(*val_ref as _)?;\n            }\n        }\n        {\n            let val_ref = &self.r#length;\n            if *val_ref != 0 {\n                encoder.encode_varint32(16u32)?;\n                encoder.encode_int32(*val_ref as _)?;\n            }\n        }\n        {\n            let val_ref = &self.r#vendor_oui;\n            if !val_ref.is_empty() {\n                encoder.encode_varint32(26u32)?;\n                encoder.encode_bytes(val_ref)?;\n            }\n        }\n        {\n            let val_ref = &self.r#vendor_oui_type;\n            if *val_ref != 0 {\n                encoder.encode_varint32(32u32)?;\n                encoder.encode_int32(*val_ref as _)?;\n            }\n        }\n        {\n            let val_ref = &self.r#payload;\n            if !val_ref.is_empty() {\n                encoder.encode_varint32(42u32)?;\n                encoder.encode_bytes(val_ref)?;\n            }\n        }\n        Ok(())\n    }\n    fn compute_size(&self) -> usize {\n        use ::micropb::{FieldEncode, PbMap};\n        let mut size = 0;\n        {\n            let val_ref = &self.r#element_id;\n            if *val_ref != 0 {\n                size += 1usize + ::micropb::size::sizeof_int32(*val_ref as _);\n            }\n        }\n        {\n            let val_ref = &self.r#length;\n            if *val_ref != 0 {\n                size += 1usize + ::micropb::size::sizeof_int32(*val_ref as _);\n            }\n        }\n        {\n            let val_ref = &self.r#vendor_oui;\n            if !val_ref.is_empty() {\n                size += 1usize + ::micropb::size::sizeof_len_record(val_ref.len());\n            }\n        }\n        {\n            let val_ref = &self.r#vendor_oui_type;\n            if *val_ref != 0 {\n                size += 1usize + ::micropb::size::sizeof_int32(*val_ref as _);\n            }\n        }\n        {\n            let val_ref = &self.r#payload;\n            if !val_ref.is_empty() {\n                size += 1usize + ::micropb::size::sizeof_len_record(val_ref.len());\n            }\n        }\n        size\n    }\n}\n#[derive(Debug, Default, Clone)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub struct CtrlMsg_Req_SetSoftAPVendorSpecificIE {\n    pub r#enable: bool,\n    pub r#type: Ctrl_VendorIEType,\n    pub r#idx: Ctrl_VendorIEID,\n    /// *Note:* The presence of this field is tracked separately in the `_has` field. It's recommended to access this field via the accessor rather than directly.\n    pub r#vendor_ie_data: CtrlMsg_Req_VendorIEData,\n    /// Tracks presence of optional and message fields\n    pub _has: CtrlMsg_Req_SetSoftAPVendorSpecificIE_::_Hazzer,\n}\nimpl ::core::cmp::PartialEq for CtrlMsg_Req_SetSoftAPVendorSpecificIE {\n    fn eq(&self, other: &Self) -> bool {\n        let mut ret = true;\n        ret &= (self.r#enable == other.r#enable);\n        ret &= (self.r#type == other.r#type);\n        ret &= (self.r#idx == other.r#idx);\n        ret &= (self.r#vendor_ie_data() == other.r#vendor_ie_data());\n        ret\n    }\n}\nimpl CtrlMsg_Req_SetSoftAPVendorSpecificIE {\n    /// Return a reference to `enable`\n    #[inline]\n    pub fn r#enable(&self) -> &bool {\n        &self.r#enable\n    }\n    /// Return a mutable reference to `enable`\n    #[inline]\n    pub fn mut_enable(&mut self) -> &mut bool {\n        &mut self.r#enable\n    }\n    /// Set the value of `enable`\n    #[inline]\n    pub fn set_enable(&mut self, value: bool) -> &mut Self {\n        self.r#enable = value.into();\n        self\n    }\n    /// Builder method that sets the value of `enable`. Useful for initializing the message.\n    #[inline]\n    pub fn init_enable(mut self, value: bool) -> Self {\n        self.r#enable = value.into();\n        self\n    }\n    /// Return a reference to `type`\n    #[inline]\n    pub fn r#type(&self) -> &Ctrl_VendorIEType {\n        &self.r#type\n    }\n    /// Return a mutable reference to `type`\n    #[inline]\n    pub fn mut_type(&mut self) -> &mut Ctrl_VendorIEType {\n        &mut self.r#type\n    }\n    /// Set the value of `type`\n    #[inline]\n    pub fn set_type(&mut self, value: Ctrl_VendorIEType) -> &mut Self {\n        self.r#type = value.into();\n        self\n    }\n    /// Builder method that sets the value of `type`. Useful for initializing the message.\n    #[inline]\n    pub fn init_type(mut self, value: Ctrl_VendorIEType) -> Self {\n        self.r#type = value.into();\n        self\n    }\n    /// Return a reference to `idx`\n    #[inline]\n    pub fn r#idx(&self) -> &Ctrl_VendorIEID {\n        &self.r#idx\n    }\n    /// Return a mutable reference to `idx`\n    #[inline]\n    pub fn mut_idx(&mut self) -> &mut Ctrl_VendorIEID {\n        &mut self.r#idx\n    }\n    /// Set the value of `idx`\n    #[inline]\n    pub fn set_idx(&mut self, value: Ctrl_VendorIEID) -> &mut Self {\n        self.r#idx = value.into();\n        self\n    }\n    /// Builder method that sets the value of `idx`. Useful for initializing the message.\n    #[inline]\n    pub fn init_idx(mut self, value: Ctrl_VendorIEID) -> Self {\n        self.r#idx = value.into();\n        self\n    }\n    /// Return a reference to `vendor_ie_data` as an `Option`\n    #[inline]\n    pub fn r#vendor_ie_data(&self) -> ::core::option::Option<&CtrlMsg_Req_VendorIEData> {\n        self._has.r#vendor_ie_data().then_some(&self.r#vendor_ie_data)\n    }\n    /// Set the value and presence of `vendor_ie_data`\n    #[inline]\n    pub fn set_vendor_ie_data(&mut self, value: CtrlMsg_Req_VendorIEData) -> &mut Self {\n        self._has.set_vendor_ie_data();\n        self.r#vendor_ie_data = value.into();\n        self\n    }\n    /// Return a mutable reference to `vendor_ie_data` as an `Option`\n    #[inline]\n    pub fn mut_vendor_ie_data(&mut self) -> ::core::option::Option<&mut CtrlMsg_Req_VendorIEData> {\n        self._has.r#vendor_ie_data().then_some(&mut self.r#vendor_ie_data)\n    }\n    /// Clear the presence of `vendor_ie_data`\n    #[inline]\n    pub fn clear_vendor_ie_data(&mut self) -> &mut Self {\n        self._has.clear_vendor_ie_data();\n        self\n    }\n    /// Take the value of `vendor_ie_data` and clear its presence\n    #[inline]\n    pub fn take_vendor_ie_data(&mut self) -> ::core::option::Option<CtrlMsg_Req_VendorIEData> {\n        let val = self\n            ._has\n            .r#vendor_ie_data()\n            .then(|| ::core::mem::take(&mut self.r#vendor_ie_data));\n        self._has.clear_vendor_ie_data();\n        val\n    }\n    /// Builder method that sets the value of `vendor_ie_data`. Useful for initializing the message.\n    #[inline]\n    pub fn init_vendor_ie_data(mut self, value: CtrlMsg_Req_VendorIEData) -> Self {\n        self.set_vendor_ie_data(value);\n        self\n    }\n}\nimpl ::micropb::MessageDecode for CtrlMsg_Req_SetSoftAPVendorSpecificIE {\n    fn decode<IMPL_MICROPB_READ: ::micropb::PbRead>(\n        &mut self,\n        decoder: &mut ::micropb::PbDecoder<IMPL_MICROPB_READ>,\n        len: usize,\n    ) -> Result<(), ::micropb::DecodeError<IMPL_MICROPB_READ::Error>> {\n        use ::micropb::{FieldDecode, PbBytes, PbMap, PbString, PbVec};\n        let before = decoder.bytes_read();\n        while decoder.bytes_read() - before < len {\n            let tag = decoder.decode_tag()?;\n            match tag.field_num() {\n                0 => return Err(::micropb::DecodeError::ZeroField),\n                1u32 => {\n                    let mut_ref = &mut self.r#enable;\n                    {\n                        let val = decoder.decode_bool()?;\n                        let val_ref = &val;\n                        if *val_ref {\n                            *mut_ref = val as _;\n                        }\n                    };\n                }\n                2u32 => {\n                    let mut_ref = &mut self.r#type;\n                    {\n                        let val = decoder.decode_int32().map(|n| Ctrl_VendorIEType(n as _))?;\n                        let val_ref = &val;\n                        if val_ref.0 != 0 {\n                            *mut_ref = val as _;\n                        }\n                    };\n                }\n                3u32 => {\n                    let mut_ref = &mut self.r#idx;\n                    {\n                        let val = decoder.decode_int32().map(|n| Ctrl_VendorIEID(n as _))?;\n                        let val_ref = &val;\n                        if val_ref.0 != 0 {\n                            *mut_ref = val as _;\n                        }\n                    };\n                }\n                4u32 => {\n                    let mut_ref = &mut self.r#vendor_ie_data;\n                    {\n                        mut_ref.decode_len_delimited(decoder)?;\n                    };\n                    self._has.set_vendor_ie_data();\n                }\n                _ => {\n                    decoder.skip_wire_value(tag.wire_type())?;\n                }\n            }\n        }\n        Ok(())\n    }\n}\nimpl ::micropb::MessageEncode for CtrlMsg_Req_SetSoftAPVendorSpecificIE {\n    const MAX_SIZE: ::core::result::Result<usize, &'static str> = 'msg: {\n        let mut max_size = 0;\n        match ::micropb::const_map!(::core::result::Result::Ok(1usize), |size| size + 1usize) {\n            ::core::result::Result::Ok(size) => {\n                max_size += size;\n            }\n            ::core::result::Result::Err(err) => {\n                break 'msg (::core::result::Result::<usize, _>::Err(err));\n            }\n        }\n        match ::micropb::const_map!(::core::result::Result::Ok(Ctrl_VendorIEType::_MAX_SIZE), |size| size\n            + 1usize)\n        {\n            ::core::result::Result::Ok(size) => {\n                max_size += size;\n            }\n            ::core::result::Result::Err(err) => {\n                break 'msg (::core::result::Result::<usize, _>::Err(err));\n            }\n        }\n        match ::micropb::const_map!(::core::result::Result::Ok(Ctrl_VendorIEID::_MAX_SIZE), |size| size\n            + 1usize)\n        {\n            ::core::result::Result::Ok(size) => {\n                max_size += size;\n            }\n            ::core::result::Result::Err(err) => {\n                break 'msg (::core::result::Result::<usize, _>::Err(err));\n            }\n        }\n        match ::micropb::const_map!(\n            ::micropb::const_map!(\n                <CtrlMsg_Req_VendorIEData as ::micropb::MessageEncode>::MAX_SIZE,\n                |size| ::micropb::size::sizeof_len_record(size)\n            ),\n            |size| size + 1usize\n        ) {\n            ::core::result::Result::Ok(size) => {\n                max_size += size;\n            }\n            ::core::result::Result::Err(err) => {\n                break 'msg (::core::result::Result::<usize, _>::Err(err));\n            }\n        }\n        ::core::result::Result::Ok(max_size)\n    };\n    fn encode<IMPL_MICROPB_WRITE: ::micropb::PbWrite>(\n        &self,\n        encoder: &mut ::micropb::PbEncoder<IMPL_MICROPB_WRITE>,\n    ) -> Result<(), IMPL_MICROPB_WRITE::Error> {\n        use ::micropb::{FieldEncode, PbMap};\n        {\n            let val_ref = &self.r#enable;\n            if *val_ref {\n                encoder.encode_varint32(8u32)?;\n                encoder.encode_bool(*val_ref)?;\n            }\n        }\n        {\n            let val_ref = &self.r#type;\n            if val_ref.0 != 0 {\n                encoder.encode_varint32(16u32)?;\n                encoder.encode_int32(val_ref.0 as _)?;\n            }\n        }\n        {\n            let val_ref = &self.r#idx;\n            if val_ref.0 != 0 {\n                encoder.encode_varint32(24u32)?;\n                encoder.encode_int32(val_ref.0 as _)?;\n            }\n        }\n        {\n            if let ::core::option::Option::Some(val_ref) = self.r#vendor_ie_data() {\n                encoder.encode_varint32(34u32)?;\n                val_ref.encode_len_delimited(encoder)?;\n            }\n        }\n        Ok(())\n    }\n    fn compute_size(&self) -> usize {\n        use ::micropb::{FieldEncode, PbMap};\n        let mut size = 0;\n        {\n            let val_ref = &self.r#enable;\n            if *val_ref {\n                size += 1usize + 1;\n            }\n        }\n        {\n            let val_ref = &self.r#type;\n            if val_ref.0 != 0 {\n                size += 1usize + ::micropb::size::sizeof_int32(val_ref.0 as _);\n            }\n        }\n        {\n            let val_ref = &self.r#idx;\n            if val_ref.0 != 0 {\n                size += 1usize + ::micropb::size::sizeof_int32(val_ref.0 as _);\n            }\n        }\n        {\n            if let ::core::option::Option::Some(val_ref) = self.r#vendor_ie_data() {\n                size += 1usize + ::micropb::size::sizeof_len_record(val_ref.compute_size());\n            }\n        }\n        size\n    }\n}\n/// Inner types for `CtrlMsg_Req_SetSoftAPVendorSpecificIE`\npub mod CtrlMsg_Req_SetSoftAPVendorSpecificIE_ {\n    /// Compact bitfield for tracking presence of optional and message fields\n    #[derive(Debug, Default, PartialEq, Clone, Copy)]\n    #[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\n    pub struct _Hazzer([u8; 1]);\n    impl _Hazzer {\n        /// New hazzer with all fields set to off\n        #[inline]\n        pub const fn _new() -> Self {\n            Self([0; 1])\n        }\n        /// Query presence of `vendor_ie_data`\n        #[inline]\n        pub const fn r#vendor_ie_data(&self) -> bool {\n            (self.0[0] & 1) != 0\n        }\n        /// Set presence of `vendor_ie_data`\n        #[inline]\n        pub const fn set_vendor_ie_data(&mut self) -> &mut Self {\n            let elem = &mut self.0[0];\n            *elem |= 1;\n            self\n        }\n        /// Clear presence of `vendor_ie_data`\n        #[inline]\n        pub const fn clear_vendor_ie_data(&mut self) -> &mut Self {\n            let elem = &mut self.0[0];\n            *elem &= !1;\n            self\n        }\n        /// Builder method that sets the presence of `vendor_ie_data`. Useful for initializing the Hazzer.\n        #[inline]\n        pub const fn init_vendor_ie_data(mut self) -> Self {\n            self.set_vendor_ie_data();\n            self\n        }\n    }\n}\n#[derive(Debug, Default, PartialEq, Clone, Copy)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub struct CtrlMsg_Resp_SetSoftAPVendorSpecificIE {\n    pub r#resp: i32,\n}\nimpl CtrlMsg_Resp_SetSoftAPVendorSpecificIE {\n    /// Return a reference to `resp`\n    #[inline]\n    pub fn r#resp(&self) -> &i32 {\n        &self.r#resp\n    }\n    /// Return a mutable reference to `resp`\n    #[inline]\n    pub fn mut_resp(&mut self) -> &mut i32 {\n        &mut self.r#resp\n    }\n    /// Set the value of `resp`\n    #[inline]\n    pub fn set_resp(&mut self, value: i32) -> &mut Self {\n        self.r#resp = value.into();\n        self\n    }\n    /// Builder method that sets the value of `resp`. Useful for initializing the message.\n    #[inline]\n    pub fn init_resp(mut self, value: i32) -> Self {\n        self.r#resp = value.into();\n        self\n    }\n}\nimpl ::micropb::MessageDecode for CtrlMsg_Resp_SetSoftAPVendorSpecificIE {\n    fn decode<IMPL_MICROPB_READ: ::micropb::PbRead>(\n        &mut self,\n        decoder: &mut ::micropb::PbDecoder<IMPL_MICROPB_READ>,\n        len: usize,\n    ) -> Result<(), ::micropb::DecodeError<IMPL_MICROPB_READ::Error>> {\n        use ::micropb::{FieldDecode, PbBytes, PbMap, PbString, PbVec};\n        let before = decoder.bytes_read();\n        while decoder.bytes_read() - before < len {\n            let tag = decoder.decode_tag()?;\n            match tag.field_num() {\n                0 => return Err(::micropb::DecodeError::ZeroField),\n                1u32 => {\n                    let mut_ref = &mut self.r#resp;\n                    {\n                        let val = decoder.decode_int32()?;\n                        let val_ref = &val;\n                        if *val_ref != 0 {\n                            *mut_ref = val as _;\n                        }\n                    };\n                }\n                _ => {\n                    decoder.skip_wire_value(tag.wire_type())?;\n                }\n            }\n        }\n        Ok(())\n    }\n}\nimpl ::micropb::MessageEncode for CtrlMsg_Resp_SetSoftAPVendorSpecificIE {\n    const MAX_SIZE: ::core::result::Result<usize, &'static str> = 'msg: {\n        let mut max_size = 0;\n        match ::micropb::const_map!(::core::result::Result::Ok(10usize), |size| size + 1usize) {\n            ::core::result::Result::Ok(size) => {\n                max_size += size;\n            }\n            ::core::result::Result::Err(err) => {\n                break 'msg (::core::result::Result::<usize, _>::Err(err));\n            }\n        }\n        ::core::result::Result::Ok(max_size)\n    };\n    fn encode<IMPL_MICROPB_WRITE: ::micropb::PbWrite>(\n        &self,\n        encoder: &mut ::micropb::PbEncoder<IMPL_MICROPB_WRITE>,\n    ) -> Result<(), IMPL_MICROPB_WRITE::Error> {\n        use ::micropb::{FieldEncode, PbMap};\n        {\n            let val_ref = &self.r#resp;\n            if *val_ref != 0 {\n                encoder.encode_varint32(8u32)?;\n                encoder.encode_int32(*val_ref as _)?;\n            }\n        }\n        Ok(())\n    }\n    fn compute_size(&self) -> usize {\n        use ::micropb::{FieldEncode, PbMap};\n        let mut size = 0;\n        {\n            let val_ref = &self.r#resp;\n            if *val_ref != 0 {\n                size += 1usize + ::micropb::size::sizeof_int32(*val_ref as _);\n            }\n        }\n        size\n    }\n}\n#[derive(Debug, Default, PartialEq, Clone, Copy)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub struct CtrlMsg_Req_SetWifiMaxTxPower {\n    pub r#wifi_max_tx_power: i32,\n}\nimpl CtrlMsg_Req_SetWifiMaxTxPower {\n    /// Return a reference to `wifi_max_tx_power`\n    #[inline]\n    pub fn r#wifi_max_tx_power(&self) -> &i32 {\n        &self.r#wifi_max_tx_power\n    }\n    /// Return a mutable reference to `wifi_max_tx_power`\n    #[inline]\n    pub fn mut_wifi_max_tx_power(&mut self) -> &mut i32 {\n        &mut self.r#wifi_max_tx_power\n    }\n    /// Set the value of `wifi_max_tx_power`\n    #[inline]\n    pub fn set_wifi_max_tx_power(&mut self, value: i32) -> &mut Self {\n        self.r#wifi_max_tx_power = value.into();\n        self\n    }\n    /// Builder method that sets the value of `wifi_max_tx_power`. Useful for initializing the message.\n    #[inline]\n    pub fn init_wifi_max_tx_power(mut self, value: i32) -> Self {\n        self.r#wifi_max_tx_power = value.into();\n        self\n    }\n}\nimpl ::micropb::MessageDecode for CtrlMsg_Req_SetWifiMaxTxPower {\n    fn decode<IMPL_MICROPB_READ: ::micropb::PbRead>(\n        &mut self,\n        decoder: &mut ::micropb::PbDecoder<IMPL_MICROPB_READ>,\n        len: usize,\n    ) -> Result<(), ::micropb::DecodeError<IMPL_MICROPB_READ::Error>> {\n        use ::micropb::{FieldDecode, PbBytes, PbMap, PbString, PbVec};\n        let before = decoder.bytes_read();\n        while decoder.bytes_read() - before < len {\n            let tag = decoder.decode_tag()?;\n            match tag.field_num() {\n                0 => return Err(::micropb::DecodeError::ZeroField),\n                1u32 => {\n                    let mut_ref = &mut self.r#wifi_max_tx_power;\n                    {\n                        let val = decoder.decode_int32()?;\n                        let val_ref = &val;\n                        if *val_ref != 0 {\n                            *mut_ref = val as _;\n                        }\n                    };\n                }\n                _ => {\n                    decoder.skip_wire_value(tag.wire_type())?;\n                }\n            }\n        }\n        Ok(())\n    }\n}\nimpl ::micropb::MessageEncode for CtrlMsg_Req_SetWifiMaxTxPower {\n    const MAX_SIZE: ::core::result::Result<usize, &'static str> = 'msg: {\n        let mut max_size = 0;\n        match ::micropb::const_map!(::core::result::Result::Ok(10usize), |size| size + 1usize) {\n            ::core::result::Result::Ok(size) => {\n                max_size += size;\n            }\n            ::core::result::Result::Err(err) => {\n                break 'msg (::core::result::Result::<usize, _>::Err(err));\n            }\n        }\n        ::core::result::Result::Ok(max_size)\n    };\n    fn encode<IMPL_MICROPB_WRITE: ::micropb::PbWrite>(\n        &self,\n        encoder: &mut ::micropb::PbEncoder<IMPL_MICROPB_WRITE>,\n    ) -> Result<(), IMPL_MICROPB_WRITE::Error> {\n        use ::micropb::{FieldEncode, PbMap};\n        {\n            let val_ref = &self.r#wifi_max_tx_power;\n            if *val_ref != 0 {\n                encoder.encode_varint32(8u32)?;\n                encoder.encode_int32(*val_ref as _)?;\n            }\n        }\n        Ok(())\n    }\n    fn compute_size(&self) -> usize {\n        use ::micropb::{FieldEncode, PbMap};\n        let mut size = 0;\n        {\n            let val_ref = &self.r#wifi_max_tx_power;\n            if *val_ref != 0 {\n                size += 1usize + ::micropb::size::sizeof_int32(*val_ref as _);\n            }\n        }\n        size\n    }\n}\n#[derive(Debug, Default, PartialEq, Clone, Copy)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub struct CtrlMsg_Resp_SetWifiMaxTxPower {\n    pub r#resp: i32,\n}\nimpl CtrlMsg_Resp_SetWifiMaxTxPower {\n    /// Return a reference to `resp`\n    #[inline]\n    pub fn r#resp(&self) -> &i32 {\n        &self.r#resp\n    }\n    /// Return a mutable reference to `resp`\n    #[inline]\n    pub fn mut_resp(&mut self) -> &mut i32 {\n        &mut self.r#resp\n    }\n    /// Set the value of `resp`\n    #[inline]\n    pub fn set_resp(&mut self, value: i32) -> &mut Self {\n        self.r#resp = value.into();\n        self\n    }\n    /// Builder method that sets the value of `resp`. Useful for initializing the message.\n    #[inline]\n    pub fn init_resp(mut self, value: i32) -> Self {\n        self.r#resp = value.into();\n        self\n    }\n}\nimpl ::micropb::MessageDecode for CtrlMsg_Resp_SetWifiMaxTxPower {\n    fn decode<IMPL_MICROPB_READ: ::micropb::PbRead>(\n        &mut self,\n        decoder: &mut ::micropb::PbDecoder<IMPL_MICROPB_READ>,\n        len: usize,\n    ) -> Result<(), ::micropb::DecodeError<IMPL_MICROPB_READ::Error>> {\n        use ::micropb::{FieldDecode, PbBytes, PbMap, PbString, PbVec};\n        let before = decoder.bytes_read();\n        while decoder.bytes_read() - before < len {\n            let tag = decoder.decode_tag()?;\n            match tag.field_num() {\n                0 => return Err(::micropb::DecodeError::ZeroField),\n                1u32 => {\n                    let mut_ref = &mut self.r#resp;\n                    {\n                        let val = decoder.decode_int32()?;\n                        let val_ref = &val;\n                        if *val_ref != 0 {\n                            *mut_ref = val as _;\n                        }\n                    };\n                }\n                _ => {\n                    decoder.skip_wire_value(tag.wire_type())?;\n                }\n            }\n        }\n        Ok(())\n    }\n}\nimpl ::micropb::MessageEncode for CtrlMsg_Resp_SetWifiMaxTxPower {\n    const MAX_SIZE: ::core::result::Result<usize, &'static str> = 'msg: {\n        let mut max_size = 0;\n        match ::micropb::const_map!(::core::result::Result::Ok(10usize), |size| size + 1usize) {\n            ::core::result::Result::Ok(size) => {\n                max_size += size;\n            }\n            ::core::result::Result::Err(err) => {\n                break 'msg (::core::result::Result::<usize, _>::Err(err));\n            }\n        }\n        ::core::result::Result::Ok(max_size)\n    };\n    fn encode<IMPL_MICROPB_WRITE: ::micropb::PbWrite>(\n        &self,\n        encoder: &mut ::micropb::PbEncoder<IMPL_MICROPB_WRITE>,\n    ) -> Result<(), IMPL_MICROPB_WRITE::Error> {\n        use ::micropb::{FieldEncode, PbMap};\n        {\n            let val_ref = &self.r#resp;\n            if *val_ref != 0 {\n                encoder.encode_varint32(8u32)?;\n                encoder.encode_int32(*val_ref as _)?;\n            }\n        }\n        Ok(())\n    }\n    fn compute_size(&self) -> usize {\n        use ::micropb::{FieldEncode, PbMap};\n        let mut size = 0;\n        {\n            let val_ref = &self.r#resp;\n            if *val_ref != 0 {\n                size += 1usize + ::micropb::size::sizeof_int32(*val_ref as _);\n            }\n        }\n        size\n    }\n}\n#[derive(Debug, Default, PartialEq, Clone, Copy)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub struct CtrlMsg_Req_GetWifiCurrTxPower {}\nimpl CtrlMsg_Req_GetWifiCurrTxPower {}\nimpl ::micropb::MessageDecode for CtrlMsg_Req_GetWifiCurrTxPower {\n    fn decode<IMPL_MICROPB_READ: ::micropb::PbRead>(\n        &mut self,\n        decoder: &mut ::micropb::PbDecoder<IMPL_MICROPB_READ>,\n        len: usize,\n    ) -> Result<(), ::micropb::DecodeError<IMPL_MICROPB_READ::Error>> {\n        use ::micropb::{FieldDecode, PbBytes, PbMap, PbString, PbVec};\n        let before = decoder.bytes_read();\n        while decoder.bytes_read() - before < len {\n            let tag = decoder.decode_tag()?;\n            match tag.field_num() {\n                0 => return Err(::micropb::DecodeError::ZeroField),\n                _ => {\n                    decoder.skip_wire_value(tag.wire_type())?;\n                }\n            }\n        }\n        Ok(())\n    }\n}\nimpl ::micropb::MessageEncode for CtrlMsg_Req_GetWifiCurrTxPower {\n    const MAX_SIZE: ::core::result::Result<usize, &'static str> = 'msg: {\n        let mut max_size = 0;\n        ::core::result::Result::Ok(max_size)\n    };\n    fn encode<IMPL_MICROPB_WRITE: ::micropb::PbWrite>(\n        &self,\n        encoder: &mut ::micropb::PbEncoder<IMPL_MICROPB_WRITE>,\n    ) -> Result<(), IMPL_MICROPB_WRITE::Error> {\n        use ::micropb::{FieldEncode, PbMap};\n        Ok(())\n    }\n    fn compute_size(&self) -> usize {\n        use ::micropb::{FieldEncode, PbMap};\n        let mut size = 0;\n        size\n    }\n}\n#[derive(Debug, Default, PartialEq, Clone, Copy)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub struct CtrlMsg_Resp_GetWifiCurrTxPower {\n    pub r#wifi_curr_tx_power: i32,\n    pub r#resp: i32,\n}\nimpl CtrlMsg_Resp_GetWifiCurrTxPower {\n    /// Return a reference to `wifi_curr_tx_power`\n    #[inline]\n    pub fn r#wifi_curr_tx_power(&self) -> &i32 {\n        &self.r#wifi_curr_tx_power\n    }\n    /// Return a mutable reference to `wifi_curr_tx_power`\n    #[inline]\n    pub fn mut_wifi_curr_tx_power(&mut self) -> &mut i32 {\n        &mut self.r#wifi_curr_tx_power\n    }\n    /// Set the value of `wifi_curr_tx_power`\n    #[inline]\n    pub fn set_wifi_curr_tx_power(&mut self, value: i32) -> &mut Self {\n        self.r#wifi_curr_tx_power = value.into();\n        self\n    }\n    /// Builder method that sets the value of `wifi_curr_tx_power`. Useful for initializing the message.\n    #[inline]\n    pub fn init_wifi_curr_tx_power(mut self, value: i32) -> Self {\n        self.r#wifi_curr_tx_power = value.into();\n        self\n    }\n    /// Return a reference to `resp`\n    #[inline]\n    pub fn r#resp(&self) -> &i32 {\n        &self.r#resp\n    }\n    /// Return a mutable reference to `resp`\n    #[inline]\n    pub fn mut_resp(&mut self) -> &mut i32 {\n        &mut self.r#resp\n    }\n    /// Set the value of `resp`\n    #[inline]\n    pub fn set_resp(&mut self, value: i32) -> &mut Self {\n        self.r#resp = value.into();\n        self\n    }\n    /// Builder method that sets the value of `resp`. Useful for initializing the message.\n    #[inline]\n    pub fn init_resp(mut self, value: i32) -> Self {\n        self.r#resp = value.into();\n        self\n    }\n}\nimpl ::micropb::MessageDecode for CtrlMsg_Resp_GetWifiCurrTxPower {\n    fn decode<IMPL_MICROPB_READ: ::micropb::PbRead>(\n        &mut self,\n        decoder: &mut ::micropb::PbDecoder<IMPL_MICROPB_READ>,\n        len: usize,\n    ) -> Result<(), ::micropb::DecodeError<IMPL_MICROPB_READ::Error>> {\n        use ::micropb::{FieldDecode, PbBytes, PbMap, PbString, PbVec};\n        let before = decoder.bytes_read();\n        while decoder.bytes_read() - before < len {\n            let tag = decoder.decode_tag()?;\n            match tag.field_num() {\n                0 => return Err(::micropb::DecodeError::ZeroField),\n                1u32 => {\n                    let mut_ref = &mut self.r#wifi_curr_tx_power;\n                    {\n                        let val = decoder.decode_int32()?;\n                        let val_ref = &val;\n                        if *val_ref != 0 {\n                            *mut_ref = val as _;\n                        }\n                    };\n                }\n                2u32 => {\n                    let mut_ref = &mut self.r#resp;\n                    {\n                        let val = decoder.decode_int32()?;\n                        let val_ref = &val;\n                        if *val_ref != 0 {\n                            *mut_ref = val as _;\n                        }\n                    };\n                }\n                _ => {\n                    decoder.skip_wire_value(tag.wire_type())?;\n                }\n            }\n        }\n        Ok(())\n    }\n}\nimpl ::micropb::MessageEncode for CtrlMsg_Resp_GetWifiCurrTxPower {\n    const MAX_SIZE: ::core::result::Result<usize, &'static str> = 'msg: {\n        let mut max_size = 0;\n        match ::micropb::const_map!(::core::result::Result::Ok(10usize), |size| size + 1usize) {\n            ::core::result::Result::Ok(size) => {\n                max_size += size;\n            }\n            ::core::result::Result::Err(err) => {\n                break 'msg (::core::result::Result::<usize, _>::Err(err));\n            }\n        }\n        match ::micropb::const_map!(::core::result::Result::Ok(10usize), |size| size + 1usize) {\n            ::core::result::Result::Ok(size) => {\n                max_size += size;\n            }\n            ::core::result::Result::Err(err) => {\n                break 'msg (::core::result::Result::<usize, _>::Err(err));\n            }\n        }\n        ::core::result::Result::Ok(max_size)\n    };\n    fn encode<IMPL_MICROPB_WRITE: ::micropb::PbWrite>(\n        &self,\n        encoder: &mut ::micropb::PbEncoder<IMPL_MICROPB_WRITE>,\n    ) -> Result<(), IMPL_MICROPB_WRITE::Error> {\n        use ::micropb::{FieldEncode, PbMap};\n        {\n            let val_ref = &self.r#wifi_curr_tx_power;\n            if *val_ref != 0 {\n                encoder.encode_varint32(8u32)?;\n                encoder.encode_int32(*val_ref as _)?;\n            }\n        }\n        {\n            let val_ref = &self.r#resp;\n            if *val_ref != 0 {\n                encoder.encode_varint32(16u32)?;\n                encoder.encode_int32(*val_ref as _)?;\n            }\n        }\n        Ok(())\n    }\n    fn compute_size(&self) -> usize {\n        use ::micropb::{FieldEncode, PbMap};\n        let mut size = 0;\n        {\n            let val_ref = &self.r#wifi_curr_tx_power;\n            if *val_ref != 0 {\n                size += 1usize + ::micropb::size::sizeof_int32(*val_ref as _);\n            }\n        }\n        {\n            let val_ref = &self.r#resp;\n            if *val_ref != 0 {\n                size += 1usize + ::micropb::size::sizeof_int32(*val_ref as _);\n            }\n        }\n        size\n    }\n}\n#[derive(Debug, Default, PartialEq, Clone, Copy)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub struct CtrlMsg_Req_ConfigHeartbeat {\n    pub r#enable: bool,\n    pub r#duration: i32,\n}\nimpl CtrlMsg_Req_ConfigHeartbeat {\n    /// Return a reference to `enable`\n    #[inline]\n    pub fn r#enable(&self) -> &bool {\n        &self.r#enable\n    }\n    /// Return a mutable reference to `enable`\n    #[inline]\n    pub fn mut_enable(&mut self) -> &mut bool {\n        &mut self.r#enable\n    }\n    /// Set the value of `enable`\n    #[inline]\n    pub fn set_enable(&mut self, value: bool) -> &mut Self {\n        self.r#enable = value.into();\n        self\n    }\n    /// Builder method that sets the value of `enable`. Useful for initializing the message.\n    #[inline]\n    pub fn init_enable(mut self, value: bool) -> Self {\n        self.r#enable = value.into();\n        self\n    }\n    /// Return a reference to `duration`\n    #[inline]\n    pub fn r#duration(&self) -> &i32 {\n        &self.r#duration\n    }\n    /// Return a mutable reference to `duration`\n    #[inline]\n    pub fn mut_duration(&mut self) -> &mut i32 {\n        &mut self.r#duration\n    }\n    /// Set the value of `duration`\n    #[inline]\n    pub fn set_duration(&mut self, value: i32) -> &mut Self {\n        self.r#duration = value.into();\n        self\n    }\n    /// Builder method that sets the value of `duration`. Useful for initializing the message.\n    #[inline]\n    pub fn init_duration(mut self, value: i32) -> Self {\n        self.r#duration = value.into();\n        self\n    }\n}\nimpl ::micropb::MessageDecode for CtrlMsg_Req_ConfigHeartbeat {\n    fn decode<IMPL_MICROPB_READ: ::micropb::PbRead>(\n        &mut self,\n        decoder: &mut ::micropb::PbDecoder<IMPL_MICROPB_READ>,\n        len: usize,\n    ) -> Result<(), ::micropb::DecodeError<IMPL_MICROPB_READ::Error>> {\n        use ::micropb::{FieldDecode, PbBytes, PbMap, PbString, PbVec};\n        let before = decoder.bytes_read();\n        while decoder.bytes_read() - before < len {\n            let tag = decoder.decode_tag()?;\n            match tag.field_num() {\n                0 => return Err(::micropb::DecodeError::ZeroField),\n                1u32 => {\n                    let mut_ref = &mut self.r#enable;\n                    {\n                        let val = decoder.decode_bool()?;\n                        let val_ref = &val;\n                        if *val_ref {\n                            *mut_ref = val as _;\n                        }\n                    };\n                }\n                2u32 => {\n                    let mut_ref = &mut self.r#duration;\n                    {\n                        let val = decoder.decode_int32()?;\n                        let val_ref = &val;\n                        if *val_ref != 0 {\n                            *mut_ref = val as _;\n                        }\n                    };\n                }\n                _ => {\n                    decoder.skip_wire_value(tag.wire_type())?;\n                }\n            }\n        }\n        Ok(())\n    }\n}\nimpl ::micropb::MessageEncode for CtrlMsg_Req_ConfigHeartbeat {\n    const MAX_SIZE: ::core::result::Result<usize, &'static str> = 'msg: {\n        let mut max_size = 0;\n        match ::micropb::const_map!(::core::result::Result::Ok(1usize), |size| size + 1usize) {\n            ::core::result::Result::Ok(size) => {\n                max_size += size;\n            }\n            ::core::result::Result::Err(err) => {\n                break 'msg (::core::result::Result::<usize, _>::Err(err));\n            }\n        }\n        match ::micropb::const_map!(::core::result::Result::Ok(10usize), |size| size + 1usize) {\n            ::core::result::Result::Ok(size) => {\n                max_size += size;\n            }\n            ::core::result::Result::Err(err) => {\n                break 'msg (::core::result::Result::<usize, _>::Err(err));\n            }\n        }\n        ::core::result::Result::Ok(max_size)\n    };\n    fn encode<IMPL_MICROPB_WRITE: ::micropb::PbWrite>(\n        &self,\n        encoder: &mut ::micropb::PbEncoder<IMPL_MICROPB_WRITE>,\n    ) -> Result<(), IMPL_MICROPB_WRITE::Error> {\n        use ::micropb::{FieldEncode, PbMap};\n        {\n            let val_ref = &self.r#enable;\n            if *val_ref {\n                encoder.encode_varint32(8u32)?;\n                encoder.encode_bool(*val_ref)?;\n            }\n        }\n        {\n            let val_ref = &self.r#duration;\n            if *val_ref != 0 {\n                encoder.encode_varint32(16u32)?;\n                encoder.encode_int32(*val_ref as _)?;\n            }\n        }\n        Ok(())\n    }\n    fn compute_size(&self) -> usize {\n        use ::micropb::{FieldEncode, PbMap};\n        let mut size = 0;\n        {\n            let val_ref = &self.r#enable;\n            if *val_ref {\n                size += 1usize + 1;\n            }\n        }\n        {\n            let val_ref = &self.r#duration;\n            if *val_ref != 0 {\n                size += 1usize + ::micropb::size::sizeof_int32(*val_ref as _);\n            }\n        }\n        size\n    }\n}\n#[derive(Debug, Default, PartialEq, Clone, Copy)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub struct CtrlMsg_Resp_ConfigHeartbeat {\n    pub r#resp: i32,\n}\nimpl CtrlMsg_Resp_ConfigHeartbeat {\n    /// Return a reference to `resp`\n    #[inline]\n    pub fn r#resp(&self) -> &i32 {\n        &self.r#resp\n    }\n    /// Return a mutable reference to `resp`\n    #[inline]\n    pub fn mut_resp(&mut self) -> &mut i32 {\n        &mut self.r#resp\n    }\n    /// Set the value of `resp`\n    #[inline]\n    pub fn set_resp(&mut self, value: i32) -> &mut Self {\n        self.r#resp = value.into();\n        self\n    }\n    /// Builder method that sets the value of `resp`. Useful for initializing the message.\n    #[inline]\n    pub fn init_resp(mut self, value: i32) -> Self {\n        self.r#resp = value.into();\n        self\n    }\n}\nimpl ::micropb::MessageDecode for CtrlMsg_Resp_ConfigHeartbeat {\n    fn decode<IMPL_MICROPB_READ: ::micropb::PbRead>(\n        &mut self,\n        decoder: &mut ::micropb::PbDecoder<IMPL_MICROPB_READ>,\n        len: usize,\n    ) -> Result<(), ::micropb::DecodeError<IMPL_MICROPB_READ::Error>> {\n        use ::micropb::{FieldDecode, PbBytes, PbMap, PbString, PbVec};\n        let before = decoder.bytes_read();\n        while decoder.bytes_read() - before < len {\n            let tag = decoder.decode_tag()?;\n            match tag.field_num() {\n                0 => return Err(::micropb::DecodeError::ZeroField),\n                1u32 => {\n                    let mut_ref = &mut self.r#resp;\n                    {\n                        let val = decoder.decode_int32()?;\n                        let val_ref = &val;\n                        if *val_ref != 0 {\n                            *mut_ref = val as _;\n                        }\n                    };\n                }\n                _ => {\n                    decoder.skip_wire_value(tag.wire_type())?;\n                }\n            }\n        }\n        Ok(())\n    }\n}\nimpl ::micropb::MessageEncode for CtrlMsg_Resp_ConfigHeartbeat {\n    const MAX_SIZE: ::core::result::Result<usize, &'static str> = 'msg: {\n        let mut max_size = 0;\n        match ::micropb::const_map!(::core::result::Result::Ok(10usize), |size| size + 1usize) {\n            ::core::result::Result::Ok(size) => {\n                max_size += size;\n            }\n            ::core::result::Result::Err(err) => {\n                break 'msg (::core::result::Result::<usize, _>::Err(err));\n            }\n        }\n        ::core::result::Result::Ok(max_size)\n    };\n    fn encode<IMPL_MICROPB_WRITE: ::micropb::PbWrite>(\n        &self,\n        encoder: &mut ::micropb::PbEncoder<IMPL_MICROPB_WRITE>,\n    ) -> Result<(), IMPL_MICROPB_WRITE::Error> {\n        use ::micropb::{FieldEncode, PbMap};\n        {\n            let val_ref = &self.r#resp;\n            if *val_ref != 0 {\n                encoder.encode_varint32(8u32)?;\n                encoder.encode_int32(*val_ref as _)?;\n            }\n        }\n        Ok(())\n    }\n    fn compute_size(&self) -> usize {\n        use ::micropb::{FieldEncode, PbMap};\n        let mut size = 0;\n        {\n            let val_ref = &self.r#resp;\n            if *val_ref != 0 {\n                size += 1usize + ::micropb::size::sizeof_int32(*val_ref as _);\n            }\n        }\n        size\n    }\n}\n#[derive(Debug, Default, PartialEq, Clone, Copy)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub struct CtrlMsg_Req_EnableDisable {\n    pub r#feature: u32,\n    pub r#enable: bool,\n}\nimpl CtrlMsg_Req_EnableDisable {\n    /// Return a reference to `feature`\n    #[inline]\n    pub fn r#feature(&self) -> &u32 {\n        &self.r#feature\n    }\n    /// Return a mutable reference to `feature`\n    #[inline]\n    pub fn mut_feature(&mut self) -> &mut u32 {\n        &mut self.r#feature\n    }\n    /// Set the value of `feature`\n    #[inline]\n    pub fn set_feature(&mut self, value: u32) -> &mut Self {\n        self.r#feature = value.into();\n        self\n    }\n    /// Builder method that sets the value of `feature`. Useful for initializing the message.\n    #[inline]\n    pub fn init_feature(mut self, value: u32) -> Self {\n        self.r#feature = value.into();\n        self\n    }\n    /// Return a reference to `enable`\n    #[inline]\n    pub fn r#enable(&self) -> &bool {\n        &self.r#enable\n    }\n    /// Return a mutable reference to `enable`\n    #[inline]\n    pub fn mut_enable(&mut self) -> &mut bool {\n        &mut self.r#enable\n    }\n    /// Set the value of `enable`\n    #[inline]\n    pub fn set_enable(&mut self, value: bool) -> &mut Self {\n        self.r#enable = value.into();\n        self\n    }\n    /// Builder method that sets the value of `enable`. Useful for initializing the message.\n    #[inline]\n    pub fn init_enable(mut self, value: bool) -> Self {\n        self.r#enable = value.into();\n        self\n    }\n}\nimpl ::micropb::MessageDecode for CtrlMsg_Req_EnableDisable {\n    fn decode<IMPL_MICROPB_READ: ::micropb::PbRead>(\n        &mut self,\n        decoder: &mut ::micropb::PbDecoder<IMPL_MICROPB_READ>,\n        len: usize,\n    ) -> Result<(), ::micropb::DecodeError<IMPL_MICROPB_READ::Error>> {\n        use ::micropb::{FieldDecode, PbBytes, PbMap, PbString, PbVec};\n        let before = decoder.bytes_read();\n        while decoder.bytes_read() - before < len {\n            let tag = decoder.decode_tag()?;\n            match tag.field_num() {\n                0 => return Err(::micropb::DecodeError::ZeroField),\n                1u32 => {\n                    let mut_ref = &mut self.r#feature;\n                    {\n                        let val = decoder.decode_varint32()?;\n                        let val_ref = &val;\n                        if *val_ref != 0 {\n                            *mut_ref = val as _;\n                        }\n                    };\n                }\n                2u32 => {\n                    let mut_ref = &mut self.r#enable;\n                    {\n                        let val = decoder.decode_bool()?;\n                        let val_ref = &val;\n                        if *val_ref {\n                            *mut_ref = val as _;\n                        }\n                    };\n                }\n                _ => {\n                    decoder.skip_wire_value(tag.wire_type())?;\n                }\n            }\n        }\n        Ok(())\n    }\n}\nimpl ::micropb::MessageEncode for CtrlMsg_Req_EnableDisable {\n    const MAX_SIZE: ::core::result::Result<usize, &'static str> = 'msg: {\n        let mut max_size = 0;\n        match ::micropb::const_map!(::core::result::Result::Ok(5usize), |size| size + 1usize) {\n            ::core::result::Result::Ok(size) => {\n                max_size += size;\n            }\n            ::core::result::Result::Err(err) => {\n                break 'msg (::core::result::Result::<usize, _>::Err(err));\n            }\n        }\n        match ::micropb::const_map!(::core::result::Result::Ok(1usize), |size| size + 1usize) {\n            ::core::result::Result::Ok(size) => {\n                max_size += size;\n            }\n            ::core::result::Result::Err(err) => {\n                break 'msg (::core::result::Result::<usize, _>::Err(err));\n            }\n        }\n        ::core::result::Result::Ok(max_size)\n    };\n    fn encode<IMPL_MICROPB_WRITE: ::micropb::PbWrite>(\n        &self,\n        encoder: &mut ::micropb::PbEncoder<IMPL_MICROPB_WRITE>,\n    ) -> Result<(), IMPL_MICROPB_WRITE::Error> {\n        use ::micropb::{FieldEncode, PbMap};\n        {\n            let val_ref = &self.r#feature;\n            if *val_ref != 0 {\n                encoder.encode_varint32(8u32)?;\n                encoder.encode_varint32(*val_ref as _)?;\n            }\n        }\n        {\n            let val_ref = &self.r#enable;\n            if *val_ref {\n                encoder.encode_varint32(16u32)?;\n                encoder.encode_bool(*val_ref)?;\n            }\n        }\n        Ok(())\n    }\n    fn compute_size(&self) -> usize {\n        use ::micropb::{FieldEncode, PbMap};\n        let mut size = 0;\n        {\n            let val_ref = &self.r#feature;\n            if *val_ref != 0 {\n                size += 1usize + ::micropb::size::sizeof_varint32(*val_ref as _);\n            }\n        }\n        {\n            let val_ref = &self.r#enable;\n            if *val_ref {\n                size += 1usize + 1;\n            }\n        }\n        size\n    }\n}\n#[derive(Debug, Default, PartialEq, Clone, Copy)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub struct CtrlMsg_Resp_EnableDisable {\n    pub r#resp: i32,\n}\nimpl CtrlMsg_Resp_EnableDisable {\n    /// Return a reference to `resp`\n    #[inline]\n    pub fn r#resp(&self) -> &i32 {\n        &self.r#resp\n    }\n    /// Return a mutable reference to `resp`\n    #[inline]\n    pub fn mut_resp(&mut self) -> &mut i32 {\n        &mut self.r#resp\n    }\n    /// Set the value of `resp`\n    #[inline]\n    pub fn set_resp(&mut self, value: i32) -> &mut Self {\n        self.r#resp = value.into();\n        self\n    }\n    /// Builder method that sets the value of `resp`. Useful for initializing the message.\n    #[inline]\n    pub fn init_resp(mut self, value: i32) -> Self {\n        self.r#resp = value.into();\n        self\n    }\n}\nimpl ::micropb::MessageDecode for CtrlMsg_Resp_EnableDisable {\n    fn decode<IMPL_MICROPB_READ: ::micropb::PbRead>(\n        &mut self,\n        decoder: &mut ::micropb::PbDecoder<IMPL_MICROPB_READ>,\n        len: usize,\n    ) -> Result<(), ::micropb::DecodeError<IMPL_MICROPB_READ::Error>> {\n        use ::micropb::{FieldDecode, PbBytes, PbMap, PbString, PbVec};\n        let before = decoder.bytes_read();\n        while decoder.bytes_read() - before < len {\n            let tag = decoder.decode_tag()?;\n            match tag.field_num() {\n                0 => return Err(::micropb::DecodeError::ZeroField),\n                1u32 => {\n                    let mut_ref = &mut self.r#resp;\n                    {\n                        let val = decoder.decode_int32()?;\n                        let val_ref = &val;\n                        if *val_ref != 0 {\n                            *mut_ref = val as _;\n                        }\n                    };\n                }\n                _ => {\n                    decoder.skip_wire_value(tag.wire_type())?;\n                }\n            }\n        }\n        Ok(())\n    }\n}\nimpl ::micropb::MessageEncode for CtrlMsg_Resp_EnableDisable {\n    const MAX_SIZE: ::core::result::Result<usize, &'static str> = 'msg: {\n        let mut max_size = 0;\n        match ::micropb::const_map!(::core::result::Result::Ok(10usize), |size| size + 1usize) {\n            ::core::result::Result::Ok(size) => {\n                max_size += size;\n            }\n            ::core::result::Result::Err(err) => {\n                break 'msg (::core::result::Result::<usize, _>::Err(err));\n            }\n        }\n        ::core::result::Result::Ok(max_size)\n    };\n    fn encode<IMPL_MICROPB_WRITE: ::micropb::PbWrite>(\n        &self,\n        encoder: &mut ::micropb::PbEncoder<IMPL_MICROPB_WRITE>,\n    ) -> Result<(), IMPL_MICROPB_WRITE::Error> {\n        use ::micropb::{FieldEncode, PbMap};\n        {\n            let val_ref = &self.r#resp;\n            if *val_ref != 0 {\n                encoder.encode_varint32(8u32)?;\n                encoder.encode_int32(*val_ref as _)?;\n            }\n        }\n        Ok(())\n    }\n    fn compute_size(&self) -> usize {\n        use ::micropb::{FieldEncode, PbMap};\n        let mut size = 0;\n        {\n            let val_ref = &self.r#resp;\n            if *val_ref != 0 {\n                size += 1usize + ::micropb::size::sizeof_int32(*val_ref as _);\n            }\n        }\n        size\n    }\n}\n#[derive(Debug, Default, PartialEq, Clone, Copy)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub struct CtrlMsg_Req_GetFwVersion {}\nimpl CtrlMsg_Req_GetFwVersion {}\nimpl ::micropb::MessageDecode for CtrlMsg_Req_GetFwVersion {\n    fn decode<IMPL_MICROPB_READ: ::micropb::PbRead>(\n        &mut self,\n        decoder: &mut ::micropb::PbDecoder<IMPL_MICROPB_READ>,\n        len: usize,\n    ) -> Result<(), ::micropb::DecodeError<IMPL_MICROPB_READ::Error>> {\n        use ::micropb::{FieldDecode, PbBytes, PbMap, PbString, PbVec};\n        let before = decoder.bytes_read();\n        while decoder.bytes_read() - before < len {\n            let tag = decoder.decode_tag()?;\n            match tag.field_num() {\n                0 => return Err(::micropb::DecodeError::ZeroField),\n                _ => {\n                    decoder.skip_wire_value(tag.wire_type())?;\n                }\n            }\n        }\n        Ok(())\n    }\n}\nimpl ::micropb::MessageEncode for CtrlMsg_Req_GetFwVersion {\n    const MAX_SIZE: ::core::result::Result<usize, &'static str> = 'msg: {\n        let mut max_size = 0;\n        ::core::result::Result::Ok(max_size)\n    };\n    fn encode<IMPL_MICROPB_WRITE: ::micropb::PbWrite>(\n        &self,\n        encoder: &mut ::micropb::PbEncoder<IMPL_MICROPB_WRITE>,\n    ) -> Result<(), IMPL_MICROPB_WRITE::Error> {\n        use ::micropb::{FieldEncode, PbMap};\n        Ok(())\n    }\n    fn compute_size(&self) -> usize {\n        use ::micropb::{FieldEncode, PbMap};\n        let mut size = 0;\n        size\n    }\n}\n#[derive(Debug, Default, PartialEq, Clone)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub struct CtrlMsg_Resp_GetFwVersion {\n    pub r#resp: i32,\n    pub r#name: ::heapless::String<32>,\n    pub r#major1: u32,\n    pub r#major2: u32,\n    pub r#minor: u32,\n    pub r#rev_patch1: u32,\n    pub r#rev_patch2: u32,\n}\nimpl CtrlMsg_Resp_GetFwVersion {\n    /// Return a reference to `resp`\n    #[inline]\n    pub fn r#resp(&self) -> &i32 {\n        &self.r#resp\n    }\n    /// Return a mutable reference to `resp`\n    #[inline]\n    pub fn mut_resp(&mut self) -> &mut i32 {\n        &mut self.r#resp\n    }\n    /// Set the value of `resp`\n    #[inline]\n    pub fn set_resp(&mut self, value: i32) -> &mut Self {\n        self.r#resp = value.into();\n        self\n    }\n    /// Builder method that sets the value of `resp`. Useful for initializing the message.\n    #[inline]\n    pub fn init_resp(mut self, value: i32) -> Self {\n        self.r#resp = value.into();\n        self\n    }\n    /// Return a reference to `name`\n    #[inline]\n    pub fn r#name(&self) -> &::heapless::String<32> {\n        &self.r#name\n    }\n    /// Return a mutable reference to `name`\n    #[inline]\n    pub fn mut_name(&mut self) -> &mut ::heapless::String<32> {\n        &mut self.r#name\n    }\n    /// Set the value of `name`\n    #[inline]\n    pub fn set_name(&mut self, value: ::heapless::String<32>) -> &mut Self {\n        self.r#name = value.into();\n        self\n    }\n    /// Builder method that sets the value of `name`. Useful for initializing the message.\n    #[inline]\n    pub fn init_name(mut self, value: ::heapless::String<32>) -> Self {\n        self.r#name = value.into();\n        self\n    }\n    /// Return a reference to `major1`\n    #[inline]\n    pub fn r#major1(&self) -> &u32 {\n        &self.r#major1\n    }\n    /// Return a mutable reference to `major1`\n    #[inline]\n    pub fn mut_major1(&mut self) -> &mut u32 {\n        &mut self.r#major1\n    }\n    /// Set the value of `major1`\n    #[inline]\n    pub fn set_major1(&mut self, value: u32) -> &mut Self {\n        self.r#major1 = value.into();\n        self\n    }\n    /// Builder method that sets the value of `major1`. Useful for initializing the message.\n    #[inline]\n    pub fn init_major1(mut self, value: u32) -> Self {\n        self.r#major1 = value.into();\n        self\n    }\n    /// Return a reference to `major2`\n    #[inline]\n    pub fn r#major2(&self) -> &u32 {\n        &self.r#major2\n    }\n    /// Return a mutable reference to `major2`\n    #[inline]\n    pub fn mut_major2(&mut self) -> &mut u32 {\n        &mut self.r#major2\n    }\n    /// Set the value of `major2`\n    #[inline]\n    pub fn set_major2(&mut self, value: u32) -> &mut Self {\n        self.r#major2 = value.into();\n        self\n    }\n    /// Builder method that sets the value of `major2`. Useful for initializing the message.\n    #[inline]\n    pub fn init_major2(mut self, value: u32) -> Self {\n        self.r#major2 = value.into();\n        self\n    }\n    /// Return a reference to `minor`\n    #[inline]\n    pub fn r#minor(&self) -> &u32 {\n        &self.r#minor\n    }\n    /// Return a mutable reference to `minor`\n    #[inline]\n    pub fn mut_minor(&mut self) -> &mut u32 {\n        &mut self.r#minor\n    }\n    /// Set the value of `minor`\n    #[inline]\n    pub fn set_minor(&mut self, value: u32) -> &mut Self {\n        self.r#minor = value.into();\n        self\n    }\n    /// Builder method that sets the value of `minor`. Useful for initializing the message.\n    #[inline]\n    pub fn init_minor(mut self, value: u32) -> Self {\n        self.r#minor = value.into();\n        self\n    }\n    /// Return a reference to `rev_patch1`\n    #[inline]\n    pub fn r#rev_patch1(&self) -> &u32 {\n        &self.r#rev_patch1\n    }\n    /// Return a mutable reference to `rev_patch1`\n    #[inline]\n    pub fn mut_rev_patch1(&mut self) -> &mut u32 {\n        &mut self.r#rev_patch1\n    }\n    /// Set the value of `rev_patch1`\n    #[inline]\n    pub fn set_rev_patch1(&mut self, value: u32) -> &mut Self {\n        self.r#rev_patch1 = value.into();\n        self\n    }\n    /// Builder method that sets the value of `rev_patch1`. Useful for initializing the message.\n    #[inline]\n    pub fn init_rev_patch1(mut self, value: u32) -> Self {\n        self.r#rev_patch1 = value.into();\n        self\n    }\n    /// Return a reference to `rev_patch2`\n    #[inline]\n    pub fn r#rev_patch2(&self) -> &u32 {\n        &self.r#rev_patch2\n    }\n    /// Return a mutable reference to `rev_patch2`\n    #[inline]\n    pub fn mut_rev_patch2(&mut self) -> &mut u32 {\n        &mut self.r#rev_patch2\n    }\n    /// Set the value of `rev_patch2`\n    #[inline]\n    pub fn set_rev_patch2(&mut self, value: u32) -> &mut Self {\n        self.r#rev_patch2 = value.into();\n        self\n    }\n    /// Builder method that sets the value of `rev_patch2`. Useful for initializing the message.\n    #[inline]\n    pub fn init_rev_patch2(mut self, value: u32) -> Self {\n        self.r#rev_patch2 = value.into();\n        self\n    }\n}\nimpl ::micropb::MessageDecode for CtrlMsg_Resp_GetFwVersion {\n    fn decode<IMPL_MICROPB_READ: ::micropb::PbRead>(\n        &mut self,\n        decoder: &mut ::micropb::PbDecoder<IMPL_MICROPB_READ>,\n        len: usize,\n    ) -> Result<(), ::micropb::DecodeError<IMPL_MICROPB_READ::Error>> {\n        use ::micropb::{FieldDecode, PbBytes, PbMap, PbString, PbVec};\n        let before = decoder.bytes_read();\n        while decoder.bytes_read() - before < len {\n            let tag = decoder.decode_tag()?;\n            match tag.field_num() {\n                0 => return Err(::micropb::DecodeError::ZeroField),\n                1u32 => {\n                    let mut_ref = &mut self.r#resp;\n                    {\n                        let val = decoder.decode_int32()?;\n                        let val_ref = &val;\n                        if *val_ref != 0 {\n                            *mut_ref = val as _;\n                        }\n                    };\n                }\n                2u32 => {\n                    let mut_ref = &mut self.r#name;\n                    {\n                        decoder.decode_string(mut_ref, ::micropb::Presence::Implicit)?;\n                    };\n                }\n                3u32 => {\n                    let mut_ref = &mut self.r#major1;\n                    {\n                        let val = decoder.decode_varint32()?;\n                        let val_ref = &val;\n                        if *val_ref != 0 {\n                            *mut_ref = val as _;\n                        }\n                    };\n                }\n                4u32 => {\n                    let mut_ref = &mut self.r#major2;\n                    {\n                        let val = decoder.decode_varint32()?;\n                        let val_ref = &val;\n                        if *val_ref != 0 {\n                            *mut_ref = val as _;\n                        }\n                    };\n                }\n                5u32 => {\n                    let mut_ref = &mut self.r#minor;\n                    {\n                        let val = decoder.decode_varint32()?;\n                        let val_ref = &val;\n                        if *val_ref != 0 {\n                            *mut_ref = val as _;\n                        }\n                    };\n                }\n                6u32 => {\n                    let mut_ref = &mut self.r#rev_patch1;\n                    {\n                        let val = decoder.decode_varint32()?;\n                        let val_ref = &val;\n                        if *val_ref != 0 {\n                            *mut_ref = val as _;\n                        }\n                    };\n                }\n                7u32 => {\n                    let mut_ref = &mut self.r#rev_patch2;\n                    {\n                        let val = decoder.decode_varint32()?;\n                        let val_ref = &val;\n                        if *val_ref != 0 {\n                            *mut_ref = val as _;\n                        }\n                    };\n                }\n                _ => {\n                    decoder.skip_wire_value(tag.wire_type())?;\n                }\n            }\n        }\n        Ok(())\n    }\n}\nimpl ::micropb::MessageEncode for CtrlMsg_Resp_GetFwVersion {\n    const MAX_SIZE: ::core::result::Result<usize, &'static str> = 'msg: {\n        let mut max_size = 0;\n        match ::micropb::const_map!(::core::result::Result::Ok(10usize), |size| size + 1usize) {\n            ::core::result::Result::Ok(size) => {\n                max_size += size;\n            }\n            ::core::result::Result::Err(err) => {\n                break 'msg (::core::result::Result::<usize, _>::Err(err));\n            }\n        }\n        match ::micropb::const_map!(::core::result::Result::Ok(33usize), |size| size + 1usize) {\n            ::core::result::Result::Ok(size) => {\n                max_size += size;\n            }\n            ::core::result::Result::Err(err) => {\n                break 'msg (::core::result::Result::<usize, _>::Err(err));\n            }\n        }\n        match ::micropb::const_map!(::core::result::Result::Ok(5usize), |size| size + 1usize) {\n            ::core::result::Result::Ok(size) => {\n                max_size += size;\n            }\n            ::core::result::Result::Err(err) => {\n                break 'msg (::core::result::Result::<usize, _>::Err(err));\n            }\n        }\n        match ::micropb::const_map!(::core::result::Result::Ok(5usize), |size| size + 1usize) {\n            ::core::result::Result::Ok(size) => {\n                max_size += size;\n            }\n            ::core::result::Result::Err(err) => {\n                break 'msg (::core::result::Result::<usize, _>::Err(err));\n            }\n        }\n        match ::micropb::const_map!(::core::result::Result::Ok(5usize), |size| size + 1usize) {\n            ::core::result::Result::Ok(size) => {\n                max_size += size;\n            }\n            ::core::result::Result::Err(err) => {\n                break 'msg (::core::result::Result::<usize, _>::Err(err));\n            }\n        }\n        match ::micropb::const_map!(::core::result::Result::Ok(5usize), |size| size + 1usize) {\n            ::core::result::Result::Ok(size) => {\n                max_size += size;\n            }\n            ::core::result::Result::Err(err) => {\n                break 'msg (::core::result::Result::<usize, _>::Err(err));\n            }\n        }\n        match ::micropb::const_map!(::core::result::Result::Ok(5usize), |size| size + 1usize) {\n            ::core::result::Result::Ok(size) => {\n                max_size += size;\n            }\n            ::core::result::Result::Err(err) => {\n                break 'msg (::core::result::Result::<usize, _>::Err(err));\n            }\n        }\n        ::core::result::Result::Ok(max_size)\n    };\n    fn encode<IMPL_MICROPB_WRITE: ::micropb::PbWrite>(\n        &self,\n        encoder: &mut ::micropb::PbEncoder<IMPL_MICROPB_WRITE>,\n    ) -> Result<(), IMPL_MICROPB_WRITE::Error> {\n        use ::micropb::{FieldEncode, PbMap};\n        {\n            let val_ref = &self.r#resp;\n            if *val_ref != 0 {\n                encoder.encode_varint32(8u32)?;\n                encoder.encode_int32(*val_ref as _)?;\n            }\n        }\n        {\n            let val_ref = &self.r#name;\n            if !val_ref.is_empty() {\n                encoder.encode_varint32(18u32)?;\n                encoder.encode_string(val_ref)?;\n            }\n        }\n        {\n            let val_ref = &self.r#major1;\n            if *val_ref != 0 {\n                encoder.encode_varint32(24u32)?;\n                encoder.encode_varint32(*val_ref as _)?;\n            }\n        }\n        {\n            let val_ref = &self.r#major2;\n            if *val_ref != 0 {\n                encoder.encode_varint32(32u32)?;\n                encoder.encode_varint32(*val_ref as _)?;\n            }\n        }\n        {\n            let val_ref = &self.r#minor;\n            if *val_ref != 0 {\n                encoder.encode_varint32(40u32)?;\n                encoder.encode_varint32(*val_ref as _)?;\n            }\n        }\n        {\n            let val_ref = &self.r#rev_patch1;\n            if *val_ref != 0 {\n                encoder.encode_varint32(48u32)?;\n                encoder.encode_varint32(*val_ref as _)?;\n            }\n        }\n        {\n            let val_ref = &self.r#rev_patch2;\n            if *val_ref != 0 {\n                encoder.encode_varint32(56u32)?;\n                encoder.encode_varint32(*val_ref as _)?;\n            }\n        }\n        Ok(())\n    }\n    fn compute_size(&self) -> usize {\n        use ::micropb::{FieldEncode, PbMap};\n        let mut size = 0;\n        {\n            let val_ref = &self.r#resp;\n            if *val_ref != 0 {\n                size += 1usize + ::micropb::size::sizeof_int32(*val_ref as _);\n            }\n        }\n        {\n            let val_ref = &self.r#name;\n            if !val_ref.is_empty() {\n                size += 1usize + ::micropb::size::sizeof_len_record(val_ref.len());\n            }\n        }\n        {\n            let val_ref = &self.r#major1;\n            if *val_ref != 0 {\n                size += 1usize + ::micropb::size::sizeof_varint32(*val_ref as _);\n            }\n        }\n        {\n            let val_ref = &self.r#major2;\n            if *val_ref != 0 {\n                size += 1usize + ::micropb::size::sizeof_varint32(*val_ref as _);\n            }\n        }\n        {\n            let val_ref = &self.r#minor;\n            if *val_ref != 0 {\n                size += 1usize + ::micropb::size::sizeof_varint32(*val_ref as _);\n            }\n        }\n        {\n            let val_ref = &self.r#rev_patch1;\n            if *val_ref != 0 {\n                size += 1usize + ::micropb::size::sizeof_varint32(*val_ref as _);\n            }\n        }\n        {\n            let val_ref = &self.r#rev_patch2;\n            if *val_ref != 0 {\n                size += 1usize + ::micropb::size::sizeof_varint32(*val_ref as _);\n            }\n        }\n        size\n    }\n}\n#[derive(Debug, Default, PartialEq, Clone)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub struct CtrlMsg_Req_SetCountryCode {\n    pub r#country: ::heapless::Vec<u8, 32>,\n    pub r#ieee80211d_enabled: bool,\n}\nimpl CtrlMsg_Req_SetCountryCode {\n    /// Return a reference to `country`\n    #[inline]\n    pub fn r#country(&self) -> &::heapless::Vec<u8, 32> {\n        &self.r#country\n    }\n    /// Return a mutable reference to `country`\n    #[inline]\n    pub fn mut_country(&mut self) -> &mut ::heapless::Vec<u8, 32> {\n        &mut self.r#country\n    }\n    /// Set the value of `country`\n    #[inline]\n    pub fn set_country(&mut self, value: ::heapless::Vec<u8, 32>) -> &mut Self {\n        self.r#country = value.into();\n        self\n    }\n    /// Builder method that sets the value of `country`. Useful for initializing the message.\n    #[inline]\n    pub fn init_country(mut self, value: ::heapless::Vec<u8, 32>) -> Self {\n        self.r#country = value.into();\n        self\n    }\n    /// Return a reference to `ieee80211d_enabled`\n    #[inline]\n    pub fn r#ieee80211d_enabled(&self) -> &bool {\n        &self.r#ieee80211d_enabled\n    }\n    /// Return a mutable reference to `ieee80211d_enabled`\n    #[inline]\n    pub fn mut_ieee80211d_enabled(&mut self) -> &mut bool {\n        &mut self.r#ieee80211d_enabled\n    }\n    /// Set the value of `ieee80211d_enabled`\n    #[inline]\n    pub fn set_ieee80211d_enabled(&mut self, value: bool) -> &mut Self {\n        self.r#ieee80211d_enabled = value.into();\n        self\n    }\n    /// Builder method that sets the value of `ieee80211d_enabled`. Useful for initializing the message.\n    #[inline]\n    pub fn init_ieee80211d_enabled(mut self, value: bool) -> Self {\n        self.r#ieee80211d_enabled = value.into();\n        self\n    }\n}\nimpl ::micropb::MessageDecode for CtrlMsg_Req_SetCountryCode {\n    fn decode<IMPL_MICROPB_READ: ::micropb::PbRead>(\n        &mut self,\n        decoder: &mut ::micropb::PbDecoder<IMPL_MICROPB_READ>,\n        len: usize,\n    ) -> Result<(), ::micropb::DecodeError<IMPL_MICROPB_READ::Error>> {\n        use ::micropb::{FieldDecode, PbBytes, PbMap, PbString, PbVec};\n        let before = decoder.bytes_read();\n        while decoder.bytes_read() - before < len {\n            let tag = decoder.decode_tag()?;\n            match tag.field_num() {\n                0 => return Err(::micropb::DecodeError::ZeroField),\n                1u32 => {\n                    let mut_ref = &mut self.r#country;\n                    {\n                        decoder.decode_bytes(mut_ref, ::micropb::Presence::Implicit)?;\n                    };\n                }\n                2u32 => {\n                    let mut_ref = &mut self.r#ieee80211d_enabled;\n                    {\n                        let val = decoder.decode_bool()?;\n                        let val_ref = &val;\n                        if *val_ref {\n                            *mut_ref = val as _;\n                        }\n                    };\n                }\n                _ => {\n                    decoder.skip_wire_value(tag.wire_type())?;\n                }\n            }\n        }\n        Ok(())\n    }\n}\nimpl ::micropb::MessageEncode for CtrlMsg_Req_SetCountryCode {\n    const MAX_SIZE: ::core::result::Result<usize, &'static str> = 'msg: {\n        let mut max_size = 0;\n        match ::micropb::const_map!(::core::result::Result::Ok(33usize), |size| size + 1usize) {\n            ::core::result::Result::Ok(size) => {\n                max_size += size;\n            }\n            ::core::result::Result::Err(err) => {\n                break 'msg (::core::result::Result::<usize, _>::Err(err));\n            }\n        }\n        match ::micropb::const_map!(::core::result::Result::Ok(1usize), |size| size + 1usize) {\n            ::core::result::Result::Ok(size) => {\n                max_size += size;\n            }\n            ::core::result::Result::Err(err) => {\n                break 'msg (::core::result::Result::<usize, _>::Err(err));\n            }\n        }\n        ::core::result::Result::Ok(max_size)\n    };\n    fn encode<IMPL_MICROPB_WRITE: ::micropb::PbWrite>(\n        &self,\n        encoder: &mut ::micropb::PbEncoder<IMPL_MICROPB_WRITE>,\n    ) -> Result<(), IMPL_MICROPB_WRITE::Error> {\n        use ::micropb::{FieldEncode, PbMap};\n        {\n            let val_ref = &self.r#country;\n            if !val_ref.is_empty() {\n                encoder.encode_varint32(10u32)?;\n                encoder.encode_bytes(val_ref)?;\n            }\n        }\n        {\n            let val_ref = &self.r#ieee80211d_enabled;\n            if *val_ref {\n                encoder.encode_varint32(16u32)?;\n                encoder.encode_bool(*val_ref)?;\n            }\n        }\n        Ok(())\n    }\n    fn compute_size(&self) -> usize {\n        use ::micropb::{FieldEncode, PbMap};\n        let mut size = 0;\n        {\n            let val_ref = &self.r#country;\n            if !val_ref.is_empty() {\n                size += 1usize + ::micropb::size::sizeof_len_record(val_ref.len());\n            }\n        }\n        {\n            let val_ref = &self.r#ieee80211d_enabled;\n            if *val_ref {\n                size += 1usize + 1;\n            }\n        }\n        size\n    }\n}\n#[derive(Debug, Default, PartialEq, Clone, Copy)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub struct CtrlMsg_Resp_SetCountryCode {\n    pub r#resp: i32,\n}\nimpl CtrlMsg_Resp_SetCountryCode {\n    /// Return a reference to `resp`\n    #[inline]\n    pub fn r#resp(&self) -> &i32 {\n        &self.r#resp\n    }\n    /// Return a mutable reference to `resp`\n    #[inline]\n    pub fn mut_resp(&mut self) -> &mut i32 {\n        &mut self.r#resp\n    }\n    /// Set the value of `resp`\n    #[inline]\n    pub fn set_resp(&mut self, value: i32) -> &mut Self {\n        self.r#resp = value.into();\n        self\n    }\n    /// Builder method that sets the value of `resp`. Useful for initializing the message.\n    #[inline]\n    pub fn init_resp(mut self, value: i32) -> Self {\n        self.r#resp = value.into();\n        self\n    }\n}\nimpl ::micropb::MessageDecode for CtrlMsg_Resp_SetCountryCode {\n    fn decode<IMPL_MICROPB_READ: ::micropb::PbRead>(\n        &mut self,\n        decoder: &mut ::micropb::PbDecoder<IMPL_MICROPB_READ>,\n        len: usize,\n    ) -> Result<(), ::micropb::DecodeError<IMPL_MICROPB_READ::Error>> {\n        use ::micropb::{FieldDecode, PbBytes, PbMap, PbString, PbVec};\n        let before = decoder.bytes_read();\n        while decoder.bytes_read() - before < len {\n            let tag = decoder.decode_tag()?;\n            match tag.field_num() {\n                0 => return Err(::micropb::DecodeError::ZeroField),\n                1u32 => {\n                    let mut_ref = &mut self.r#resp;\n                    {\n                        let val = decoder.decode_int32()?;\n                        let val_ref = &val;\n                        if *val_ref != 0 {\n                            *mut_ref = val as _;\n                        }\n                    };\n                }\n                _ => {\n                    decoder.skip_wire_value(tag.wire_type())?;\n                }\n            }\n        }\n        Ok(())\n    }\n}\nimpl ::micropb::MessageEncode for CtrlMsg_Resp_SetCountryCode {\n    const MAX_SIZE: ::core::result::Result<usize, &'static str> = 'msg: {\n        let mut max_size = 0;\n        match ::micropb::const_map!(::core::result::Result::Ok(10usize), |size| size + 1usize) {\n            ::core::result::Result::Ok(size) => {\n                max_size += size;\n            }\n            ::core::result::Result::Err(err) => {\n                break 'msg (::core::result::Result::<usize, _>::Err(err));\n            }\n        }\n        ::core::result::Result::Ok(max_size)\n    };\n    fn encode<IMPL_MICROPB_WRITE: ::micropb::PbWrite>(\n        &self,\n        encoder: &mut ::micropb::PbEncoder<IMPL_MICROPB_WRITE>,\n    ) -> Result<(), IMPL_MICROPB_WRITE::Error> {\n        use ::micropb::{FieldEncode, PbMap};\n        {\n            let val_ref = &self.r#resp;\n            if *val_ref != 0 {\n                encoder.encode_varint32(8u32)?;\n                encoder.encode_int32(*val_ref as _)?;\n            }\n        }\n        Ok(())\n    }\n    fn compute_size(&self) -> usize {\n        use ::micropb::{FieldEncode, PbMap};\n        let mut size = 0;\n        {\n            let val_ref = &self.r#resp;\n            if *val_ref != 0 {\n                size += 1usize + ::micropb::size::sizeof_int32(*val_ref as _);\n            }\n        }\n        size\n    }\n}\n#[derive(Debug, Default, PartialEq, Clone, Copy)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub struct CtrlMsg_Req_GetCountryCode {}\nimpl CtrlMsg_Req_GetCountryCode {}\nimpl ::micropb::MessageDecode for CtrlMsg_Req_GetCountryCode {\n    fn decode<IMPL_MICROPB_READ: ::micropb::PbRead>(\n        &mut self,\n        decoder: &mut ::micropb::PbDecoder<IMPL_MICROPB_READ>,\n        len: usize,\n    ) -> Result<(), ::micropb::DecodeError<IMPL_MICROPB_READ::Error>> {\n        use ::micropb::{FieldDecode, PbBytes, PbMap, PbString, PbVec};\n        let before = decoder.bytes_read();\n        while decoder.bytes_read() - before < len {\n            let tag = decoder.decode_tag()?;\n            match tag.field_num() {\n                0 => return Err(::micropb::DecodeError::ZeroField),\n                _ => {\n                    decoder.skip_wire_value(tag.wire_type())?;\n                }\n            }\n        }\n        Ok(())\n    }\n}\nimpl ::micropb::MessageEncode for CtrlMsg_Req_GetCountryCode {\n    const MAX_SIZE: ::core::result::Result<usize, &'static str> = 'msg: {\n        let mut max_size = 0;\n        ::core::result::Result::Ok(max_size)\n    };\n    fn encode<IMPL_MICROPB_WRITE: ::micropb::PbWrite>(\n        &self,\n        encoder: &mut ::micropb::PbEncoder<IMPL_MICROPB_WRITE>,\n    ) -> Result<(), IMPL_MICROPB_WRITE::Error> {\n        use ::micropb::{FieldEncode, PbMap};\n        Ok(())\n    }\n    fn compute_size(&self) -> usize {\n        use ::micropb::{FieldEncode, PbMap};\n        let mut size = 0;\n        size\n    }\n}\n#[derive(Debug, Default, PartialEq, Clone)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub struct CtrlMsg_Resp_GetCountryCode {\n    pub r#resp: i32,\n    pub r#country: ::heapless::Vec<u8, 32>,\n}\nimpl CtrlMsg_Resp_GetCountryCode {\n    /// Return a reference to `resp`\n    #[inline]\n    pub fn r#resp(&self) -> &i32 {\n        &self.r#resp\n    }\n    /// Return a mutable reference to `resp`\n    #[inline]\n    pub fn mut_resp(&mut self) -> &mut i32 {\n        &mut self.r#resp\n    }\n    /// Set the value of `resp`\n    #[inline]\n    pub fn set_resp(&mut self, value: i32) -> &mut Self {\n        self.r#resp = value.into();\n        self\n    }\n    /// Builder method that sets the value of `resp`. Useful for initializing the message.\n    #[inline]\n    pub fn init_resp(mut self, value: i32) -> Self {\n        self.r#resp = value.into();\n        self\n    }\n    /// Return a reference to `country`\n    #[inline]\n    pub fn r#country(&self) -> &::heapless::Vec<u8, 32> {\n        &self.r#country\n    }\n    /// Return a mutable reference to `country`\n    #[inline]\n    pub fn mut_country(&mut self) -> &mut ::heapless::Vec<u8, 32> {\n        &mut self.r#country\n    }\n    /// Set the value of `country`\n    #[inline]\n    pub fn set_country(&mut self, value: ::heapless::Vec<u8, 32>) -> &mut Self {\n        self.r#country = value.into();\n        self\n    }\n    /// Builder method that sets the value of `country`. Useful for initializing the message.\n    #[inline]\n    pub fn init_country(mut self, value: ::heapless::Vec<u8, 32>) -> Self {\n        self.r#country = value.into();\n        self\n    }\n}\nimpl ::micropb::MessageDecode for CtrlMsg_Resp_GetCountryCode {\n    fn decode<IMPL_MICROPB_READ: ::micropb::PbRead>(\n        &mut self,\n        decoder: &mut ::micropb::PbDecoder<IMPL_MICROPB_READ>,\n        len: usize,\n    ) -> Result<(), ::micropb::DecodeError<IMPL_MICROPB_READ::Error>> {\n        use ::micropb::{FieldDecode, PbBytes, PbMap, PbString, PbVec};\n        let before = decoder.bytes_read();\n        while decoder.bytes_read() - before < len {\n            let tag = decoder.decode_tag()?;\n            match tag.field_num() {\n                0 => return Err(::micropb::DecodeError::ZeroField),\n                1u32 => {\n                    let mut_ref = &mut self.r#resp;\n                    {\n                        let val = decoder.decode_int32()?;\n                        let val_ref = &val;\n                        if *val_ref != 0 {\n                            *mut_ref = val as _;\n                        }\n                    };\n                }\n                2u32 => {\n                    let mut_ref = &mut self.r#country;\n                    {\n                        decoder.decode_bytes(mut_ref, ::micropb::Presence::Implicit)?;\n                    };\n                }\n                _ => {\n                    decoder.skip_wire_value(tag.wire_type())?;\n                }\n            }\n        }\n        Ok(())\n    }\n}\nimpl ::micropb::MessageEncode for CtrlMsg_Resp_GetCountryCode {\n    const MAX_SIZE: ::core::result::Result<usize, &'static str> = 'msg: {\n        let mut max_size = 0;\n        match ::micropb::const_map!(::core::result::Result::Ok(10usize), |size| size + 1usize) {\n            ::core::result::Result::Ok(size) => {\n                max_size += size;\n            }\n            ::core::result::Result::Err(err) => {\n                break 'msg (::core::result::Result::<usize, _>::Err(err));\n            }\n        }\n        match ::micropb::const_map!(::core::result::Result::Ok(33usize), |size| size + 1usize) {\n            ::core::result::Result::Ok(size) => {\n                max_size += size;\n            }\n            ::core::result::Result::Err(err) => {\n                break 'msg (::core::result::Result::<usize, _>::Err(err));\n            }\n        }\n        ::core::result::Result::Ok(max_size)\n    };\n    fn encode<IMPL_MICROPB_WRITE: ::micropb::PbWrite>(\n        &self,\n        encoder: &mut ::micropb::PbEncoder<IMPL_MICROPB_WRITE>,\n    ) -> Result<(), IMPL_MICROPB_WRITE::Error> {\n        use ::micropb::{FieldEncode, PbMap};\n        {\n            let val_ref = &self.r#resp;\n            if *val_ref != 0 {\n                encoder.encode_varint32(8u32)?;\n                encoder.encode_int32(*val_ref as _)?;\n            }\n        }\n        {\n            let val_ref = &self.r#country;\n            if !val_ref.is_empty() {\n                encoder.encode_varint32(18u32)?;\n                encoder.encode_bytes(val_ref)?;\n            }\n        }\n        Ok(())\n    }\n    fn compute_size(&self) -> usize {\n        use ::micropb::{FieldEncode, PbMap};\n        let mut size = 0;\n        {\n            let val_ref = &self.r#resp;\n            if *val_ref != 0 {\n                size += 1usize + ::micropb::size::sizeof_int32(*val_ref as _);\n            }\n        }\n        {\n            let val_ref = &self.r#country;\n            if !val_ref.is_empty() {\n                size += 1usize + ::micropb::size::sizeof_len_record(val_ref.len());\n            }\n        }\n        size\n    }\n}\n#[derive(Debug, Default, PartialEq, Clone)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub struct CtrlMsg_Req_SetDhcpDnsStatus {\n    pub r#iface: i32,\n    pub r#net_link_up: i32,\n    pub r#dhcp_up: i32,\n    pub r#dhcp_ip: ::heapless::Vec<u8, 32>,\n    pub r#dhcp_nm: ::heapless::Vec<u8, 32>,\n    pub r#dhcp_gw: ::heapless::Vec<u8, 32>,\n    pub r#dns_up: i32,\n    pub r#dns_ip: ::heapless::Vec<u8, 32>,\n    pub r#dns_type: i32,\n}\nimpl CtrlMsg_Req_SetDhcpDnsStatus {\n    /// Return a reference to `iface`\n    #[inline]\n    pub fn r#iface(&self) -> &i32 {\n        &self.r#iface\n    }\n    /// Return a mutable reference to `iface`\n    #[inline]\n    pub fn mut_iface(&mut self) -> &mut i32 {\n        &mut self.r#iface\n    }\n    /// Set the value of `iface`\n    #[inline]\n    pub fn set_iface(&mut self, value: i32) -> &mut Self {\n        self.r#iface = value.into();\n        self\n    }\n    /// Builder method that sets the value of `iface`. Useful for initializing the message.\n    #[inline]\n    pub fn init_iface(mut self, value: i32) -> Self {\n        self.r#iface = value.into();\n        self\n    }\n    /// Return a reference to `net_link_up`\n    #[inline]\n    pub fn r#net_link_up(&self) -> &i32 {\n        &self.r#net_link_up\n    }\n    /// Return a mutable reference to `net_link_up`\n    #[inline]\n    pub fn mut_net_link_up(&mut self) -> &mut i32 {\n        &mut self.r#net_link_up\n    }\n    /// Set the value of `net_link_up`\n    #[inline]\n    pub fn set_net_link_up(&mut self, value: i32) -> &mut Self {\n        self.r#net_link_up = value.into();\n        self\n    }\n    /// Builder method that sets the value of `net_link_up`. Useful for initializing the message.\n    #[inline]\n    pub fn init_net_link_up(mut self, value: i32) -> Self {\n        self.r#net_link_up = value.into();\n        self\n    }\n    /// Return a reference to `dhcp_up`\n    #[inline]\n    pub fn r#dhcp_up(&self) -> &i32 {\n        &self.r#dhcp_up\n    }\n    /// Return a mutable reference to `dhcp_up`\n    #[inline]\n    pub fn mut_dhcp_up(&mut self) -> &mut i32 {\n        &mut self.r#dhcp_up\n    }\n    /// Set the value of `dhcp_up`\n    #[inline]\n    pub fn set_dhcp_up(&mut self, value: i32) -> &mut Self {\n        self.r#dhcp_up = value.into();\n        self\n    }\n    /// Builder method that sets the value of `dhcp_up`. Useful for initializing the message.\n    #[inline]\n    pub fn init_dhcp_up(mut self, value: i32) -> Self {\n        self.r#dhcp_up = value.into();\n        self\n    }\n    /// Return a reference to `dhcp_ip`\n    #[inline]\n    pub fn r#dhcp_ip(&self) -> &::heapless::Vec<u8, 32> {\n        &self.r#dhcp_ip\n    }\n    /// Return a mutable reference to `dhcp_ip`\n    #[inline]\n    pub fn mut_dhcp_ip(&mut self) -> &mut ::heapless::Vec<u8, 32> {\n        &mut self.r#dhcp_ip\n    }\n    /// Set the value of `dhcp_ip`\n    #[inline]\n    pub fn set_dhcp_ip(&mut self, value: ::heapless::Vec<u8, 32>) -> &mut Self {\n        self.r#dhcp_ip = value.into();\n        self\n    }\n    /// Builder method that sets the value of `dhcp_ip`. Useful for initializing the message.\n    #[inline]\n    pub fn init_dhcp_ip(mut self, value: ::heapless::Vec<u8, 32>) -> Self {\n        self.r#dhcp_ip = value.into();\n        self\n    }\n    /// Return a reference to `dhcp_nm`\n    #[inline]\n    pub fn r#dhcp_nm(&self) -> &::heapless::Vec<u8, 32> {\n        &self.r#dhcp_nm\n    }\n    /// Return a mutable reference to `dhcp_nm`\n    #[inline]\n    pub fn mut_dhcp_nm(&mut self) -> &mut ::heapless::Vec<u8, 32> {\n        &mut self.r#dhcp_nm\n    }\n    /// Set the value of `dhcp_nm`\n    #[inline]\n    pub fn set_dhcp_nm(&mut self, value: ::heapless::Vec<u8, 32>) -> &mut Self {\n        self.r#dhcp_nm = value.into();\n        self\n    }\n    /// Builder method that sets the value of `dhcp_nm`. Useful for initializing the message.\n    #[inline]\n    pub fn init_dhcp_nm(mut self, value: ::heapless::Vec<u8, 32>) -> Self {\n        self.r#dhcp_nm = value.into();\n        self\n    }\n    /// Return a reference to `dhcp_gw`\n    #[inline]\n    pub fn r#dhcp_gw(&self) -> &::heapless::Vec<u8, 32> {\n        &self.r#dhcp_gw\n    }\n    /// Return a mutable reference to `dhcp_gw`\n    #[inline]\n    pub fn mut_dhcp_gw(&mut self) -> &mut ::heapless::Vec<u8, 32> {\n        &mut self.r#dhcp_gw\n    }\n    /// Set the value of `dhcp_gw`\n    #[inline]\n    pub fn set_dhcp_gw(&mut self, value: ::heapless::Vec<u8, 32>) -> &mut Self {\n        self.r#dhcp_gw = value.into();\n        self\n    }\n    /// Builder method that sets the value of `dhcp_gw`. Useful for initializing the message.\n    #[inline]\n    pub fn init_dhcp_gw(mut self, value: ::heapless::Vec<u8, 32>) -> Self {\n        self.r#dhcp_gw = value.into();\n        self\n    }\n    /// Return a reference to `dns_up`\n    #[inline]\n    pub fn r#dns_up(&self) -> &i32 {\n        &self.r#dns_up\n    }\n    /// Return a mutable reference to `dns_up`\n    #[inline]\n    pub fn mut_dns_up(&mut self) -> &mut i32 {\n        &mut self.r#dns_up\n    }\n    /// Set the value of `dns_up`\n    #[inline]\n    pub fn set_dns_up(&mut self, value: i32) -> &mut Self {\n        self.r#dns_up = value.into();\n        self\n    }\n    /// Builder method that sets the value of `dns_up`. Useful for initializing the message.\n    #[inline]\n    pub fn init_dns_up(mut self, value: i32) -> Self {\n        self.r#dns_up = value.into();\n        self\n    }\n    /// Return a reference to `dns_ip`\n    #[inline]\n    pub fn r#dns_ip(&self) -> &::heapless::Vec<u8, 32> {\n        &self.r#dns_ip\n    }\n    /// Return a mutable reference to `dns_ip`\n    #[inline]\n    pub fn mut_dns_ip(&mut self) -> &mut ::heapless::Vec<u8, 32> {\n        &mut self.r#dns_ip\n    }\n    /// Set the value of `dns_ip`\n    #[inline]\n    pub fn set_dns_ip(&mut self, value: ::heapless::Vec<u8, 32>) -> &mut Self {\n        self.r#dns_ip = value.into();\n        self\n    }\n    /// Builder method that sets the value of `dns_ip`. Useful for initializing the message.\n    #[inline]\n    pub fn init_dns_ip(mut self, value: ::heapless::Vec<u8, 32>) -> Self {\n        self.r#dns_ip = value.into();\n        self\n    }\n    /// Return a reference to `dns_type`\n    #[inline]\n    pub fn r#dns_type(&self) -> &i32 {\n        &self.r#dns_type\n    }\n    /// Return a mutable reference to `dns_type`\n    #[inline]\n    pub fn mut_dns_type(&mut self) -> &mut i32 {\n        &mut self.r#dns_type\n    }\n    /// Set the value of `dns_type`\n    #[inline]\n    pub fn set_dns_type(&mut self, value: i32) -> &mut Self {\n        self.r#dns_type = value.into();\n        self\n    }\n    /// Builder method that sets the value of `dns_type`. Useful for initializing the message.\n    #[inline]\n    pub fn init_dns_type(mut self, value: i32) -> Self {\n        self.r#dns_type = value.into();\n        self\n    }\n}\nimpl ::micropb::MessageDecode for CtrlMsg_Req_SetDhcpDnsStatus {\n    fn decode<IMPL_MICROPB_READ: ::micropb::PbRead>(\n        &mut self,\n        decoder: &mut ::micropb::PbDecoder<IMPL_MICROPB_READ>,\n        len: usize,\n    ) -> Result<(), ::micropb::DecodeError<IMPL_MICROPB_READ::Error>> {\n        use ::micropb::{FieldDecode, PbBytes, PbMap, PbString, PbVec};\n        let before = decoder.bytes_read();\n        while decoder.bytes_read() - before < len {\n            let tag = decoder.decode_tag()?;\n            match tag.field_num() {\n                0 => return Err(::micropb::DecodeError::ZeroField),\n                1u32 => {\n                    let mut_ref = &mut self.r#iface;\n                    {\n                        let val = decoder.decode_int32()?;\n                        let val_ref = &val;\n                        if *val_ref != 0 {\n                            *mut_ref = val as _;\n                        }\n                    };\n                }\n                2u32 => {\n                    let mut_ref = &mut self.r#net_link_up;\n                    {\n                        let val = decoder.decode_int32()?;\n                        let val_ref = &val;\n                        if *val_ref != 0 {\n                            *mut_ref = val as _;\n                        }\n                    };\n                }\n                3u32 => {\n                    let mut_ref = &mut self.r#dhcp_up;\n                    {\n                        let val = decoder.decode_int32()?;\n                        let val_ref = &val;\n                        if *val_ref != 0 {\n                            *mut_ref = val as _;\n                        }\n                    };\n                }\n                4u32 => {\n                    let mut_ref = &mut self.r#dhcp_ip;\n                    {\n                        decoder.decode_bytes(mut_ref, ::micropb::Presence::Implicit)?;\n                    };\n                }\n                5u32 => {\n                    let mut_ref = &mut self.r#dhcp_nm;\n                    {\n                        decoder.decode_bytes(mut_ref, ::micropb::Presence::Implicit)?;\n                    };\n                }\n                6u32 => {\n                    let mut_ref = &mut self.r#dhcp_gw;\n                    {\n                        decoder.decode_bytes(mut_ref, ::micropb::Presence::Implicit)?;\n                    };\n                }\n                7u32 => {\n                    let mut_ref = &mut self.r#dns_up;\n                    {\n                        let val = decoder.decode_int32()?;\n                        let val_ref = &val;\n                        if *val_ref != 0 {\n                            *mut_ref = val as _;\n                        }\n                    };\n                }\n                8u32 => {\n                    let mut_ref = &mut self.r#dns_ip;\n                    {\n                        decoder.decode_bytes(mut_ref, ::micropb::Presence::Implicit)?;\n                    };\n                }\n                9u32 => {\n                    let mut_ref = &mut self.r#dns_type;\n                    {\n                        let val = decoder.decode_int32()?;\n                        let val_ref = &val;\n                        if *val_ref != 0 {\n                            *mut_ref = val as _;\n                        }\n                    };\n                }\n                _ => {\n                    decoder.skip_wire_value(tag.wire_type())?;\n                }\n            }\n        }\n        Ok(())\n    }\n}\nimpl ::micropb::MessageEncode for CtrlMsg_Req_SetDhcpDnsStatus {\n    const MAX_SIZE: ::core::result::Result<usize, &'static str> = 'msg: {\n        let mut max_size = 0;\n        match ::micropb::const_map!(::core::result::Result::Ok(10usize), |size| size + 1usize) {\n            ::core::result::Result::Ok(size) => {\n                max_size += size;\n            }\n            ::core::result::Result::Err(err) => {\n                break 'msg (::core::result::Result::<usize, _>::Err(err));\n            }\n        }\n        match ::micropb::const_map!(::core::result::Result::Ok(10usize), |size| size + 1usize) {\n            ::core::result::Result::Ok(size) => {\n                max_size += size;\n            }\n            ::core::result::Result::Err(err) => {\n                break 'msg (::core::result::Result::<usize, _>::Err(err));\n            }\n        }\n        match ::micropb::const_map!(::core::result::Result::Ok(10usize), |size| size + 1usize) {\n            ::core::result::Result::Ok(size) => {\n                max_size += size;\n            }\n            ::core::result::Result::Err(err) => {\n                break 'msg (::core::result::Result::<usize, _>::Err(err));\n            }\n        }\n        match ::micropb::const_map!(::core::result::Result::Ok(33usize), |size| size + 1usize) {\n            ::core::result::Result::Ok(size) => {\n                max_size += size;\n            }\n            ::core::result::Result::Err(err) => {\n                break 'msg (::core::result::Result::<usize, _>::Err(err));\n            }\n        }\n        match ::micropb::const_map!(::core::result::Result::Ok(33usize), |size| size + 1usize) {\n            ::core::result::Result::Ok(size) => {\n                max_size += size;\n            }\n            ::core::result::Result::Err(err) => {\n                break 'msg (::core::result::Result::<usize, _>::Err(err));\n            }\n        }\n        match ::micropb::const_map!(::core::result::Result::Ok(33usize), |size| size + 1usize) {\n            ::core::result::Result::Ok(size) => {\n                max_size += size;\n            }\n            ::core::result::Result::Err(err) => {\n                break 'msg (::core::result::Result::<usize, _>::Err(err));\n            }\n        }\n        match ::micropb::const_map!(::core::result::Result::Ok(10usize), |size| size + 1usize) {\n            ::core::result::Result::Ok(size) => {\n                max_size += size;\n            }\n            ::core::result::Result::Err(err) => {\n                break 'msg (::core::result::Result::<usize, _>::Err(err));\n            }\n        }\n        match ::micropb::const_map!(::core::result::Result::Ok(33usize), |size| size + 1usize) {\n            ::core::result::Result::Ok(size) => {\n                max_size += size;\n            }\n            ::core::result::Result::Err(err) => {\n                break 'msg (::core::result::Result::<usize, _>::Err(err));\n            }\n        }\n        match ::micropb::const_map!(::core::result::Result::Ok(10usize), |size| size + 1usize) {\n            ::core::result::Result::Ok(size) => {\n                max_size += size;\n            }\n            ::core::result::Result::Err(err) => {\n                break 'msg (::core::result::Result::<usize, _>::Err(err));\n            }\n        }\n        ::core::result::Result::Ok(max_size)\n    };\n    fn encode<IMPL_MICROPB_WRITE: ::micropb::PbWrite>(\n        &self,\n        encoder: &mut ::micropb::PbEncoder<IMPL_MICROPB_WRITE>,\n    ) -> Result<(), IMPL_MICROPB_WRITE::Error> {\n        use ::micropb::{FieldEncode, PbMap};\n        {\n            let val_ref = &self.r#iface;\n            if *val_ref != 0 {\n                encoder.encode_varint32(8u32)?;\n                encoder.encode_int32(*val_ref as _)?;\n            }\n        }\n        {\n            let val_ref = &self.r#net_link_up;\n            if *val_ref != 0 {\n                encoder.encode_varint32(16u32)?;\n                encoder.encode_int32(*val_ref as _)?;\n            }\n        }\n        {\n            let val_ref = &self.r#dhcp_up;\n            if *val_ref != 0 {\n                encoder.encode_varint32(24u32)?;\n                encoder.encode_int32(*val_ref as _)?;\n            }\n        }\n        {\n            let val_ref = &self.r#dhcp_ip;\n            if !val_ref.is_empty() {\n                encoder.encode_varint32(34u32)?;\n                encoder.encode_bytes(val_ref)?;\n            }\n        }\n        {\n            let val_ref = &self.r#dhcp_nm;\n            if !val_ref.is_empty() {\n                encoder.encode_varint32(42u32)?;\n                encoder.encode_bytes(val_ref)?;\n            }\n        }\n        {\n            let val_ref = &self.r#dhcp_gw;\n            if !val_ref.is_empty() {\n                encoder.encode_varint32(50u32)?;\n                encoder.encode_bytes(val_ref)?;\n            }\n        }\n        {\n            let val_ref = &self.r#dns_up;\n            if *val_ref != 0 {\n                encoder.encode_varint32(56u32)?;\n                encoder.encode_int32(*val_ref as _)?;\n            }\n        }\n        {\n            let val_ref = &self.r#dns_ip;\n            if !val_ref.is_empty() {\n                encoder.encode_varint32(66u32)?;\n                encoder.encode_bytes(val_ref)?;\n            }\n        }\n        {\n            let val_ref = &self.r#dns_type;\n            if *val_ref != 0 {\n                encoder.encode_varint32(72u32)?;\n                encoder.encode_int32(*val_ref as _)?;\n            }\n        }\n        Ok(())\n    }\n    fn compute_size(&self) -> usize {\n        use ::micropb::{FieldEncode, PbMap};\n        let mut size = 0;\n        {\n            let val_ref = &self.r#iface;\n            if *val_ref != 0 {\n                size += 1usize + ::micropb::size::sizeof_int32(*val_ref as _);\n            }\n        }\n        {\n            let val_ref = &self.r#net_link_up;\n            if *val_ref != 0 {\n                size += 1usize + ::micropb::size::sizeof_int32(*val_ref as _);\n            }\n        }\n        {\n            let val_ref = &self.r#dhcp_up;\n            if *val_ref != 0 {\n                size += 1usize + ::micropb::size::sizeof_int32(*val_ref as _);\n            }\n        }\n        {\n            let val_ref = &self.r#dhcp_ip;\n            if !val_ref.is_empty() {\n                size += 1usize + ::micropb::size::sizeof_len_record(val_ref.len());\n            }\n        }\n        {\n            let val_ref = &self.r#dhcp_nm;\n            if !val_ref.is_empty() {\n                size += 1usize + ::micropb::size::sizeof_len_record(val_ref.len());\n            }\n        }\n        {\n            let val_ref = &self.r#dhcp_gw;\n            if !val_ref.is_empty() {\n                size += 1usize + ::micropb::size::sizeof_len_record(val_ref.len());\n            }\n        }\n        {\n            let val_ref = &self.r#dns_up;\n            if *val_ref != 0 {\n                size += 1usize + ::micropb::size::sizeof_int32(*val_ref as _);\n            }\n        }\n        {\n            let val_ref = &self.r#dns_ip;\n            if !val_ref.is_empty() {\n                size += 1usize + ::micropb::size::sizeof_len_record(val_ref.len());\n            }\n        }\n        {\n            let val_ref = &self.r#dns_type;\n            if *val_ref != 0 {\n                size += 1usize + ::micropb::size::sizeof_int32(*val_ref as _);\n            }\n        }\n        size\n    }\n}\n#[derive(Debug, Default, PartialEq, Clone, Copy)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub struct CtrlMsg_Resp_SetDhcpDnsStatus {\n    pub r#resp: i32,\n}\nimpl CtrlMsg_Resp_SetDhcpDnsStatus {\n    /// Return a reference to `resp`\n    #[inline]\n    pub fn r#resp(&self) -> &i32 {\n        &self.r#resp\n    }\n    /// Return a mutable reference to `resp`\n    #[inline]\n    pub fn mut_resp(&mut self) -> &mut i32 {\n        &mut self.r#resp\n    }\n    /// Set the value of `resp`\n    #[inline]\n    pub fn set_resp(&mut self, value: i32) -> &mut Self {\n        self.r#resp = value.into();\n        self\n    }\n    /// Builder method that sets the value of `resp`. Useful for initializing the message.\n    #[inline]\n    pub fn init_resp(mut self, value: i32) -> Self {\n        self.r#resp = value.into();\n        self\n    }\n}\nimpl ::micropb::MessageDecode for CtrlMsg_Resp_SetDhcpDnsStatus {\n    fn decode<IMPL_MICROPB_READ: ::micropb::PbRead>(\n        &mut self,\n        decoder: &mut ::micropb::PbDecoder<IMPL_MICROPB_READ>,\n        len: usize,\n    ) -> Result<(), ::micropb::DecodeError<IMPL_MICROPB_READ::Error>> {\n        use ::micropb::{FieldDecode, PbBytes, PbMap, PbString, PbVec};\n        let before = decoder.bytes_read();\n        while decoder.bytes_read() - before < len {\n            let tag = decoder.decode_tag()?;\n            match tag.field_num() {\n                0 => return Err(::micropb::DecodeError::ZeroField),\n                1u32 => {\n                    let mut_ref = &mut self.r#resp;\n                    {\n                        let val = decoder.decode_int32()?;\n                        let val_ref = &val;\n                        if *val_ref != 0 {\n                            *mut_ref = val as _;\n                        }\n                    };\n                }\n                _ => {\n                    decoder.skip_wire_value(tag.wire_type())?;\n                }\n            }\n        }\n        Ok(())\n    }\n}\nimpl ::micropb::MessageEncode for CtrlMsg_Resp_SetDhcpDnsStatus {\n    const MAX_SIZE: ::core::result::Result<usize, &'static str> = 'msg: {\n        let mut max_size = 0;\n        match ::micropb::const_map!(::core::result::Result::Ok(10usize), |size| size + 1usize) {\n            ::core::result::Result::Ok(size) => {\n                max_size += size;\n            }\n            ::core::result::Result::Err(err) => {\n                break 'msg (::core::result::Result::<usize, _>::Err(err));\n            }\n        }\n        ::core::result::Result::Ok(max_size)\n    };\n    fn encode<IMPL_MICROPB_WRITE: ::micropb::PbWrite>(\n        &self,\n        encoder: &mut ::micropb::PbEncoder<IMPL_MICROPB_WRITE>,\n    ) -> Result<(), IMPL_MICROPB_WRITE::Error> {\n        use ::micropb::{FieldEncode, PbMap};\n        {\n            let val_ref = &self.r#resp;\n            if *val_ref != 0 {\n                encoder.encode_varint32(8u32)?;\n                encoder.encode_int32(*val_ref as _)?;\n            }\n        }\n        Ok(())\n    }\n    fn compute_size(&self) -> usize {\n        use ::micropb::{FieldEncode, PbMap};\n        let mut size = 0;\n        {\n            let val_ref = &self.r#resp;\n            if *val_ref != 0 {\n                size += 1usize + ::micropb::size::sizeof_int32(*val_ref as _);\n            }\n        }\n        size\n    }\n}\n#[derive(Debug, Default, PartialEq, Clone, Copy)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub struct CtrlMsg_Req_GetDhcpDnsStatus {}\nimpl CtrlMsg_Req_GetDhcpDnsStatus {}\nimpl ::micropb::MessageDecode for CtrlMsg_Req_GetDhcpDnsStatus {\n    fn decode<IMPL_MICROPB_READ: ::micropb::PbRead>(\n        &mut self,\n        decoder: &mut ::micropb::PbDecoder<IMPL_MICROPB_READ>,\n        len: usize,\n    ) -> Result<(), ::micropb::DecodeError<IMPL_MICROPB_READ::Error>> {\n        use ::micropb::{FieldDecode, PbBytes, PbMap, PbString, PbVec};\n        let before = decoder.bytes_read();\n        while decoder.bytes_read() - before < len {\n            let tag = decoder.decode_tag()?;\n            match tag.field_num() {\n                0 => return Err(::micropb::DecodeError::ZeroField),\n                _ => {\n                    decoder.skip_wire_value(tag.wire_type())?;\n                }\n            }\n        }\n        Ok(())\n    }\n}\nimpl ::micropb::MessageEncode for CtrlMsg_Req_GetDhcpDnsStatus {\n    const MAX_SIZE: ::core::result::Result<usize, &'static str> = 'msg: {\n        let mut max_size = 0;\n        ::core::result::Result::Ok(max_size)\n    };\n    fn encode<IMPL_MICROPB_WRITE: ::micropb::PbWrite>(\n        &self,\n        encoder: &mut ::micropb::PbEncoder<IMPL_MICROPB_WRITE>,\n    ) -> Result<(), IMPL_MICROPB_WRITE::Error> {\n        use ::micropb::{FieldEncode, PbMap};\n        Ok(())\n    }\n    fn compute_size(&self) -> usize {\n        use ::micropb::{FieldEncode, PbMap};\n        let mut size = 0;\n        size\n    }\n}\n#[derive(Debug, Default, PartialEq, Clone)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub struct CtrlMsg_Resp_GetDhcpDnsStatus {\n    pub r#resp: i32,\n    pub r#iface: i32,\n    pub r#net_link_up: i32,\n    pub r#dhcp_up: i32,\n    pub r#dhcp_ip: ::heapless::Vec<u8, 32>,\n    pub r#dhcp_nm: ::heapless::Vec<u8, 32>,\n    pub r#dhcp_gw: ::heapless::Vec<u8, 32>,\n    pub r#dns_up: i32,\n    pub r#dns_ip: ::heapless::Vec<u8, 32>,\n    pub r#dns_type: i32,\n}\nimpl CtrlMsg_Resp_GetDhcpDnsStatus {\n    /// Return a reference to `resp`\n    #[inline]\n    pub fn r#resp(&self) -> &i32 {\n        &self.r#resp\n    }\n    /// Return a mutable reference to `resp`\n    #[inline]\n    pub fn mut_resp(&mut self) -> &mut i32 {\n        &mut self.r#resp\n    }\n    /// Set the value of `resp`\n    #[inline]\n    pub fn set_resp(&mut self, value: i32) -> &mut Self {\n        self.r#resp = value.into();\n        self\n    }\n    /// Builder method that sets the value of `resp`. Useful for initializing the message.\n    #[inline]\n    pub fn init_resp(mut self, value: i32) -> Self {\n        self.r#resp = value.into();\n        self\n    }\n    /// Return a reference to `iface`\n    #[inline]\n    pub fn r#iface(&self) -> &i32 {\n        &self.r#iface\n    }\n    /// Return a mutable reference to `iface`\n    #[inline]\n    pub fn mut_iface(&mut self) -> &mut i32 {\n        &mut self.r#iface\n    }\n    /// Set the value of `iface`\n    #[inline]\n    pub fn set_iface(&mut self, value: i32) -> &mut Self {\n        self.r#iface = value.into();\n        self\n    }\n    /// Builder method that sets the value of `iface`. Useful for initializing the message.\n    #[inline]\n    pub fn init_iface(mut self, value: i32) -> Self {\n        self.r#iface = value.into();\n        self\n    }\n    /// Return a reference to `net_link_up`\n    #[inline]\n    pub fn r#net_link_up(&self) -> &i32 {\n        &self.r#net_link_up\n    }\n    /// Return a mutable reference to `net_link_up`\n    #[inline]\n    pub fn mut_net_link_up(&mut self) -> &mut i32 {\n        &mut self.r#net_link_up\n    }\n    /// Set the value of `net_link_up`\n    #[inline]\n    pub fn set_net_link_up(&mut self, value: i32) -> &mut Self {\n        self.r#net_link_up = value.into();\n        self\n    }\n    /// Builder method that sets the value of `net_link_up`. Useful for initializing the message.\n    #[inline]\n    pub fn init_net_link_up(mut self, value: i32) -> Self {\n        self.r#net_link_up = value.into();\n        self\n    }\n    /// Return a reference to `dhcp_up`\n    #[inline]\n    pub fn r#dhcp_up(&self) -> &i32 {\n        &self.r#dhcp_up\n    }\n    /// Return a mutable reference to `dhcp_up`\n    #[inline]\n    pub fn mut_dhcp_up(&mut self) -> &mut i32 {\n        &mut self.r#dhcp_up\n    }\n    /// Set the value of `dhcp_up`\n    #[inline]\n    pub fn set_dhcp_up(&mut self, value: i32) -> &mut Self {\n        self.r#dhcp_up = value.into();\n        self\n    }\n    /// Builder method that sets the value of `dhcp_up`. Useful for initializing the message.\n    #[inline]\n    pub fn init_dhcp_up(mut self, value: i32) -> Self {\n        self.r#dhcp_up = value.into();\n        self\n    }\n    /// Return a reference to `dhcp_ip`\n    #[inline]\n    pub fn r#dhcp_ip(&self) -> &::heapless::Vec<u8, 32> {\n        &self.r#dhcp_ip\n    }\n    /// Return a mutable reference to `dhcp_ip`\n    #[inline]\n    pub fn mut_dhcp_ip(&mut self) -> &mut ::heapless::Vec<u8, 32> {\n        &mut self.r#dhcp_ip\n    }\n    /// Set the value of `dhcp_ip`\n    #[inline]\n    pub fn set_dhcp_ip(&mut self, value: ::heapless::Vec<u8, 32>) -> &mut Self {\n        self.r#dhcp_ip = value.into();\n        self\n    }\n    /// Builder method that sets the value of `dhcp_ip`. Useful for initializing the message.\n    #[inline]\n    pub fn init_dhcp_ip(mut self, value: ::heapless::Vec<u8, 32>) -> Self {\n        self.r#dhcp_ip = value.into();\n        self\n    }\n    /// Return a reference to `dhcp_nm`\n    #[inline]\n    pub fn r#dhcp_nm(&self) -> &::heapless::Vec<u8, 32> {\n        &self.r#dhcp_nm\n    }\n    /// Return a mutable reference to `dhcp_nm`\n    #[inline]\n    pub fn mut_dhcp_nm(&mut self) -> &mut ::heapless::Vec<u8, 32> {\n        &mut self.r#dhcp_nm\n    }\n    /// Set the value of `dhcp_nm`\n    #[inline]\n    pub fn set_dhcp_nm(&mut self, value: ::heapless::Vec<u8, 32>) -> &mut Self {\n        self.r#dhcp_nm = value.into();\n        self\n    }\n    /// Builder method that sets the value of `dhcp_nm`. Useful for initializing the message.\n    #[inline]\n    pub fn init_dhcp_nm(mut self, value: ::heapless::Vec<u8, 32>) -> Self {\n        self.r#dhcp_nm = value.into();\n        self\n    }\n    /// Return a reference to `dhcp_gw`\n    #[inline]\n    pub fn r#dhcp_gw(&self) -> &::heapless::Vec<u8, 32> {\n        &self.r#dhcp_gw\n    }\n    /// Return a mutable reference to `dhcp_gw`\n    #[inline]\n    pub fn mut_dhcp_gw(&mut self) -> &mut ::heapless::Vec<u8, 32> {\n        &mut self.r#dhcp_gw\n    }\n    /// Set the value of `dhcp_gw`\n    #[inline]\n    pub fn set_dhcp_gw(&mut self, value: ::heapless::Vec<u8, 32>) -> &mut Self {\n        self.r#dhcp_gw = value.into();\n        self\n    }\n    /// Builder method that sets the value of `dhcp_gw`. Useful for initializing the message.\n    #[inline]\n    pub fn init_dhcp_gw(mut self, value: ::heapless::Vec<u8, 32>) -> Self {\n        self.r#dhcp_gw = value.into();\n        self\n    }\n    /// Return a reference to `dns_up`\n    #[inline]\n    pub fn r#dns_up(&self) -> &i32 {\n        &self.r#dns_up\n    }\n    /// Return a mutable reference to `dns_up`\n    #[inline]\n    pub fn mut_dns_up(&mut self) -> &mut i32 {\n        &mut self.r#dns_up\n    }\n    /// Set the value of `dns_up`\n    #[inline]\n    pub fn set_dns_up(&mut self, value: i32) -> &mut Self {\n        self.r#dns_up = value.into();\n        self\n    }\n    /// Builder method that sets the value of `dns_up`. Useful for initializing the message.\n    #[inline]\n    pub fn init_dns_up(mut self, value: i32) -> Self {\n        self.r#dns_up = value.into();\n        self\n    }\n    /// Return a reference to `dns_ip`\n    #[inline]\n    pub fn r#dns_ip(&self) -> &::heapless::Vec<u8, 32> {\n        &self.r#dns_ip\n    }\n    /// Return a mutable reference to `dns_ip`\n    #[inline]\n    pub fn mut_dns_ip(&mut self) -> &mut ::heapless::Vec<u8, 32> {\n        &mut self.r#dns_ip\n    }\n    /// Set the value of `dns_ip`\n    #[inline]\n    pub fn set_dns_ip(&mut self, value: ::heapless::Vec<u8, 32>) -> &mut Self {\n        self.r#dns_ip = value.into();\n        self\n    }\n    /// Builder method that sets the value of `dns_ip`. Useful for initializing the message.\n    #[inline]\n    pub fn init_dns_ip(mut self, value: ::heapless::Vec<u8, 32>) -> Self {\n        self.r#dns_ip = value.into();\n        self\n    }\n    /// Return a reference to `dns_type`\n    #[inline]\n    pub fn r#dns_type(&self) -> &i32 {\n        &self.r#dns_type\n    }\n    /// Return a mutable reference to `dns_type`\n    #[inline]\n    pub fn mut_dns_type(&mut self) -> &mut i32 {\n        &mut self.r#dns_type\n    }\n    /// Set the value of `dns_type`\n    #[inline]\n    pub fn set_dns_type(&mut self, value: i32) -> &mut Self {\n        self.r#dns_type = value.into();\n        self\n    }\n    /// Builder method that sets the value of `dns_type`. Useful for initializing the message.\n    #[inline]\n    pub fn init_dns_type(mut self, value: i32) -> Self {\n        self.r#dns_type = value.into();\n        self\n    }\n}\nimpl ::micropb::MessageDecode for CtrlMsg_Resp_GetDhcpDnsStatus {\n    fn decode<IMPL_MICROPB_READ: ::micropb::PbRead>(\n        &mut self,\n        decoder: &mut ::micropb::PbDecoder<IMPL_MICROPB_READ>,\n        len: usize,\n    ) -> Result<(), ::micropb::DecodeError<IMPL_MICROPB_READ::Error>> {\n        use ::micropb::{FieldDecode, PbBytes, PbMap, PbString, PbVec};\n        let before = decoder.bytes_read();\n        while decoder.bytes_read() - before < len {\n            let tag = decoder.decode_tag()?;\n            match tag.field_num() {\n                0 => return Err(::micropb::DecodeError::ZeroField),\n                1u32 => {\n                    let mut_ref = &mut self.r#resp;\n                    {\n                        let val = decoder.decode_int32()?;\n                        let val_ref = &val;\n                        if *val_ref != 0 {\n                            *mut_ref = val as _;\n                        }\n                    };\n                }\n                2u32 => {\n                    let mut_ref = &mut self.r#iface;\n                    {\n                        let val = decoder.decode_int32()?;\n                        let val_ref = &val;\n                        if *val_ref != 0 {\n                            *mut_ref = val as _;\n                        }\n                    };\n                }\n                3u32 => {\n                    let mut_ref = &mut self.r#net_link_up;\n                    {\n                        let val = decoder.decode_int32()?;\n                        let val_ref = &val;\n                        if *val_ref != 0 {\n                            *mut_ref = val as _;\n                        }\n                    };\n                }\n                4u32 => {\n                    let mut_ref = &mut self.r#dhcp_up;\n                    {\n                        let val = decoder.decode_int32()?;\n                        let val_ref = &val;\n                        if *val_ref != 0 {\n                            *mut_ref = val as _;\n                        }\n                    };\n                }\n                5u32 => {\n                    let mut_ref = &mut self.r#dhcp_ip;\n                    {\n                        decoder.decode_bytes(mut_ref, ::micropb::Presence::Implicit)?;\n                    };\n                }\n                6u32 => {\n                    let mut_ref = &mut self.r#dhcp_nm;\n                    {\n                        decoder.decode_bytes(mut_ref, ::micropb::Presence::Implicit)?;\n                    };\n                }\n                7u32 => {\n                    let mut_ref = &mut self.r#dhcp_gw;\n                    {\n                        decoder.decode_bytes(mut_ref, ::micropb::Presence::Implicit)?;\n                    };\n                }\n                8u32 => {\n                    let mut_ref = &mut self.r#dns_up;\n                    {\n                        let val = decoder.decode_int32()?;\n                        let val_ref = &val;\n                        if *val_ref != 0 {\n                            *mut_ref = val as _;\n                        }\n                    };\n                }\n                9u32 => {\n                    let mut_ref = &mut self.r#dns_ip;\n                    {\n                        decoder.decode_bytes(mut_ref, ::micropb::Presence::Implicit)?;\n                    };\n                }\n                10u32 => {\n                    let mut_ref = &mut self.r#dns_type;\n                    {\n                        let val = decoder.decode_int32()?;\n                        let val_ref = &val;\n                        if *val_ref != 0 {\n                            *mut_ref = val as _;\n                        }\n                    };\n                }\n                _ => {\n                    decoder.skip_wire_value(tag.wire_type())?;\n                }\n            }\n        }\n        Ok(())\n    }\n}\nimpl ::micropb::MessageEncode for CtrlMsg_Resp_GetDhcpDnsStatus {\n    const MAX_SIZE: ::core::result::Result<usize, &'static str> = 'msg: {\n        let mut max_size = 0;\n        match ::micropb::const_map!(::core::result::Result::Ok(10usize), |size| size + 1usize) {\n            ::core::result::Result::Ok(size) => {\n                max_size += size;\n            }\n            ::core::result::Result::Err(err) => {\n                break 'msg (::core::result::Result::<usize, _>::Err(err));\n            }\n        }\n        match ::micropb::const_map!(::core::result::Result::Ok(10usize), |size| size + 1usize) {\n            ::core::result::Result::Ok(size) => {\n                max_size += size;\n            }\n            ::core::result::Result::Err(err) => {\n                break 'msg (::core::result::Result::<usize, _>::Err(err));\n            }\n        }\n        match ::micropb::const_map!(::core::result::Result::Ok(10usize), |size| size + 1usize) {\n            ::core::result::Result::Ok(size) => {\n                max_size += size;\n            }\n            ::core::result::Result::Err(err) => {\n                break 'msg (::core::result::Result::<usize, _>::Err(err));\n            }\n        }\n        match ::micropb::const_map!(::core::result::Result::Ok(10usize), |size| size + 1usize) {\n            ::core::result::Result::Ok(size) => {\n                max_size += size;\n            }\n            ::core::result::Result::Err(err) => {\n                break 'msg (::core::result::Result::<usize, _>::Err(err));\n            }\n        }\n        match ::micropb::const_map!(::core::result::Result::Ok(33usize), |size| size + 1usize) {\n            ::core::result::Result::Ok(size) => {\n                max_size += size;\n            }\n            ::core::result::Result::Err(err) => {\n                break 'msg (::core::result::Result::<usize, _>::Err(err));\n            }\n        }\n        match ::micropb::const_map!(::core::result::Result::Ok(33usize), |size| size + 1usize) {\n            ::core::result::Result::Ok(size) => {\n                max_size += size;\n            }\n            ::core::result::Result::Err(err) => {\n                break 'msg (::core::result::Result::<usize, _>::Err(err));\n            }\n        }\n        match ::micropb::const_map!(::core::result::Result::Ok(33usize), |size| size + 1usize) {\n            ::core::result::Result::Ok(size) => {\n                max_size += size;\n            }\n            ::core::result::Result::Err(err) => {\n                break 'msg (::core::result::Result::<usize, _>::Err(err));\n            }\n        }\n        match ::micropb::const_map!(::core::result::Result::Ok(10usize), |size| size + 1usize) {\n            ::core::result::Result::Ok(size) => {\n                max_size += size;\n            }\n            ::core::result::Result::Err(err) => {\n                break 'msg (::core::result::Result::<usize, _>::Err(err));\n            }\n        }\n        match ::micropb::const_map!(::core::result::Result::Ok(33usize), |size| size + 1usize) {\n            ::core::result::Result::Ok(size) => {\n                max_size += size;\n            }\n            ::core::result::Result::Err(err) => {\n                break 'msg (::core::result::Result::<usize, _>::Err(err));\n            }\n        }\n        match ::micropb::const_map!(::core::result::Result::Ok(10usize), |size| size + 1usize) {\n            ::core::result::Result::Ok(size) => {\n                max_size += size;\n            }\n            ::core::result::Result::Err(err) => {\n                break 'msg (::core::result::Result::<usize, _>::Err(err));\n            }\n        }\n        ::core::result::Result::Ok(max_size)\n    };\n    fn encode<IMPL_MICROPB_WRITE: ::micropb::PbWrite>(\n        &self,\n        encoder: &mut ::micropb::PbEncoder<IMPL_MICROPB_WRITE>,\n    ) -> Result<(), IMPL_MICROPB_WRITE::Error> {\n        use ::micropb::{FieldEncode, PbMap};\n        {\n            let val_ref = &self.r#resp;\n            if *val_ref != 0 {\n                encoder.encode_varint32(8u32)?;\n                encoder.encode_int32(*val_ref as _)?;\n            }\n        }\n        {\n            let val_ref = &self.r#iface;\n            if *val_ref != 0 {\n                encoder.encode_varint32(16u32)?;\n                encoder.encode_int32(*val_ref as _)?;\n            }\n        }\n        {\n            let val_ref = &self.r#net_link_up;\n            if *val_ref != 0 {\n                encoder.encode_varint32(24u32)?;\n                encoder.encode_int32(*val_ref as _)?;\n            }\n        }\n        {\n            let val_ref = &self.r#dhcp_up;\n            if *val_ref != 0 {\n                encoder.encode_varint32(32u32)?;\n                encoder.encode_int32(*val_ref as _)?;\n            }\n        }\n        {\n            let val_ref = &self.r#dhcp_ip;\n            if !val_ref.is_empty() {\n                encoder.encode_varint32(42u32)?;\n                encoder.encode_bytes(val_ref)?;\n            }\n        }\n        {\n            let val_ref = &self.r#dhcp_nm;\n            if !val_ref.is_empty() {\n                encoder.encode_varint32(50u32)?;\n                encoder.encode_bytes(val_ref)?;\n            }\n        }\n        {\n            let val_ref = &self.r#dhcp_gw;\n            if !val_ref.is_empty() {\n                encoder.encode_varint32(58u32)?;\n                encoder.encode_bytes(val_ref)?;\n            }\n        }\n        {\n            let val_ref = &self.r#dns_up;\n            if *val_ref != 0 {\n                encoder.encode_varint32(64u32)?;\n                encoder.encode_int32(*val_ref as _)?;\n            }\n        }\n        {\n            let val_ref = &self.r#dns_ip;\n            if !val_ref.is_empty() {\n                encoder.encode_varint32(74u32)?;\n                encoder.encode_bytes(val_ref)?;\n            }\n        }\n        {\n            let val_ref = &self.r#dns_type;\n            if *val_ref != 0 {\n                encoder.encode_varint32(80u32)?;\n                encoder.encode_int32(*val_ref as _)?;\n            }\n        }\n        Ok(())\n    }\n    fn compute_size(&self) -> usize {\n        use ::micropb::{FieldEncode, PbMap};\n        let mut size = 0;\n        {\n            let val_ref = &self.r#resp;\n            if *val_ref != 0 {\n                size += 1usize + ::micropb::size::sizeof_int32(*val_ref as _);\n            }\n        }\n        {\n            let val_ref = &self.r#iface;\n            if *val_ref != 0 {\n                size += 1usize + ::micropb::size::sizeof_int32(*val_ref as _);\n            }\n        }\n        {\n            let val_ref = &self.r#net_link_up;\n            if *val_ref != 0 {\n                size += 1usize + ::micropb::size::sizeof_int32(*val_ref as _);\n            }\n        }\n        {\n            let val_ref = &self.r#dhcp_up;\n            if *val_ref != 0 {\n                size += 1usize + ::micropb::size::sizeof_int32(*val_ref as _);\n            }\n        }\n        {\n            let val_ref = &self.r#dhcp_ip;\n            if !val_ref.is_empty() {\n                size += 1usize + ::micropb::size::sizeof_len_record(val_ref.len());\n            }\n        }\n        {\n            let val_ref = &self.r#dhcp_nm;\n            if !val_ref.is_empty() {\n                size += 1usize + ::micropb::size::sizeof_len_record(val_ref.len());\n            }\n        }\n        {\n            let val_ref = &self.r#dhcp_gw;\n            if !val_ref.is_empty() {\n                size += 1usize + ::micropb::size::sizeof_len_record(val_ref.len());\n            }\n        }\n        {\n            let val_ref = &self.r#dns_up;\n            if *val_ref != 0 {\n                size += 1usize + ::micropb::size::sizeof_int32(*val_ref as _);\n            }\n        }\n        {\n            let val_ref = &self.r#dns_ip;\n            if !val_ref.is_empty() {\n                size += 1usize + ::micropb::size::sizeof_len_record(val_ref.len());\n            }\n        }\n        {\n            let val_ref = &self.r#dns_type;\n            if *val_ref != 0 {\n                size += 1usize + ::micropb::size::sizeof_int32(*val_ref as _);\n            }\n        }\n        size\n    }\n}\n///* Event structure *\n#[derive(Debug, Default, PartialEq, Clone)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub struct CtrlMsg_Event_ESPInit {\n    pub r#init_data: ::heapless::Vec<u8, 64>,\n}\nimpl CtrlMsg_Event_ESPInit {\n    /// Return a reference to `init_data`\n    #[inline]\n    pub fn r#init_data(&self) -> &::heapless::Vec<u8, 64> {\n        &self.r#init_data\n    }\n    /// Return a mutable reference to `init_data`\n    #[inline]\n    pub fn mut_init_data(&mut self) -> &mut ::heapless::Vec<u8, 64> {\n        &mut self.r#init_data\n    }\n    /// Set the value of `init_data`\n    #[inline]\n    pub fn set_init_data(&mut self, value: ::heapless::Vec<u8, 64>) -> &mut Self {\n        self.r#init_data = value.into();\n        self\n    }\n    /// Builder method that sets the value of `init_data`. Useful for initializing the message.\n    #[inline]\n    pub fn init_init_data(mut self, value: ::heapless::Vec<u8, 64>) -> Self {\n        self.r#init_data = value.into();\n        self\n    }\n}\nimpl ::micropb::MessageDecode for CtrlMsg_Event_ESPInit {\n    fn decode<IMPL_MICROPB_READ: ::micropb::PbRead>(\n        &mut self,\n        decoder: &mut ::micropb::PbDecoder<IMPL_MICROPB_READ>,\n        len: usize,\n    ) -> Result<(), ::micropb::DecodeError<IMPL_MICROPB_READ::Error>> {\n        use ::micropb::{FieldDecode, PbBytes, PbMap, PbString, PbVec};\n        let before = decoder.bytes_read();\n        while decoder.bytes_read() - before < len {\n            let tag = decoder.decode_tag()?;\n            match tag.field_num() {\n                0 => return Err(::micropb::DecodeError::ZeroField),\n                1u32 => {\n                    let mut_ref = &mut self.r#init_data;\n                    {\n                        decoder.decode_bytes(mut_ref, ::micropb::Presence::Implicit)?;\n                    };\n                }\n                _ => {\n                    decoder.skip_wire_value(tag.wire_type())?;\n                }\n            }\n        }\n        Ok(())\n    }\n}\nimpl ::micropb::MessageEncode for CtrlMsg_Event_ESPInit {\n    const MAX_SIZE: ::core::result::Result<usize, &'static str> = 'msg: {\n        let mut max_size = 0;\n        match ::micropb::const_map!(::core::result::Result::Ok(65usize), |size| size + 1usize) {\n            ::core::result::Result::Ok(size) => {\n                max_size += size;\n            }\n            ::core::result::Result::Err(err) => {\n                break 'msg (::core::result::Result::<usize, _>::Err(err));\n            }\n        }\n        ::core::result::Result::Ok(max_size)\n    };\n    fn encode<IMPL_MICROPB_WRITE: ::micropb::PbWrite>(\n        &self,\n        encoder: &mut ::micropb::PbEncoder<IMPL_MICROPB_WRITE>,\n    ) -> Result<(), IMPL_MICROPB_WRITE::Error> {\n        use ::micropb::{FieldEncode, PbMap};\n        {\n            let val_ref = &self.r#init_data;\n            if !val_ref.is_empty() {\n                encoder.encode_varint32(10u32)?;\n                encoder.encode_bytes(val_ref)?;\n            }\n        }\n        Ok(())\n    }\n    fn compute_size(&self) -> usize {\n        use ::micropb::{FieldEncode, PbMap};\n        let mut size = 0;\n        {\n            let val_ref = &self.r#init_data;\n            if !val_ref.is_empty() {\n                size += 1usize + ::micropb::size::sizeof_len_record(val_ref.len());\n            }\n        }\n        size\n    }\n}\n#[derive(Debug, Default, PartialEq, Clone, Copy)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub struct CtrlMsg_Event_Heartbeat {\n    pub r#hb_num: i32,\n}\nimpl CtrlMsg_Event_Heartbeat {\n    /// Return a reference to `hb_num`\n    #[inline]\n    pub fn r#hb_num(&self) -> &i32 {\n        &self.r#hb_num\n    }\n    /// Return a mutable reference to `hb_num`\n    #[inline]\n    pub fn mut_hb_num(&mut self) -> &mut i32 {\n        &mut self.r#hb_num\n    }\n    /// Set the value of `hb_num`\n    #[inline]\n    pub fn set_hb_num(&mut self, value: i32) -> &mut Self {\n        self.r#hb_num = value.into();\n        self\n    }\n    /// Builder method that sets the value of `hb_num`. Useful for initializing the message.\n    #[inline]\n    pub fn init_hb_num(mut self, value: i32) -> Self {\n        self.r#hb_num = value.into();\n        self\n    }\n}\nimpl ::micropb::MessageDecode for CtrlMsg_Event_Heartbeat {\n    fn decode<IMPL_MICROPB_READ: ::micropb::PbRead>(\n        &mut self,\n        decoder: &mut ::micropb::PbDecoder<IMPL_MICROPB_READ>,\n        len: usize,\n    ) -> Result<(), ::micropb::DecodeError<IMPL_MICROPB_READ::Error>> {\n        use ::micropb::{FieldDecode, PbBytes, PbMap, PbString, PbVec};\n        let before = decoder.bytes_read();\n        while decoder.bytes_read() - before < len {\n            let tag = decoder.decode_tag()?;\n            match tag.field_num() {\n                0 => return Err(::micropb::DecodeError::ZeroField),\n                1u32 => {\n                    let mut_ref = &mut self.r#hb_num;\n                    {\n                        let val = decoder.decode_int32()?;\n                        let val_ref = &val;\n                        if *val_ref != 0 {\n                            *mut_ref = val as _;\n                        }\n                    };\n                }\n                _ => {\n                    decoder.skip_wire_value(tag.wire_type())?;\n                }\n            }\n        }\n        Ok(())\n    }\n}\nimpl ::micropb::MessageEncode for CtrlMsg_Event_Heartbeat {\n    const MAX_SIZE: ::core::result::Result<usize, &'static str> = 'msg: {\n        let mut max_size = 0;\n        match ::micropb::const_map!(::core::result::Result::Ok(10usize), |size| size + 1usize) {\n            ::core::result::Result::Ok(size) => {\n                max_size += size;\n            }\n            ::core::result::Result::Err(err) => {\n                break 'msg (::core::result::Result::<usize, _>::Err(err));\n            }\n        }\n        ::core::result::Result::Ok(max_size)\n    };\n    fn encode<IMPL_MICROPB_WRITE: ::micropb::PbWrite>(\n        &self,\n        encoder: &mut ::micropb::PbEncoder<IMPL_MICROPB_WRITE>,\n    ) -> Result<(), IMPL_MICROPB_WRITE::Error> {\n        use ::micropb::{FieldEncode, PbMap};\n        {\n            let val_ref = &self.r#hb_num;\n            if *val_ref != 0 {\n                encoder.encode_varint32(8u32)?;\n                encoder.encode_int32(*val_ref as _)?;\n            }\n        }\n        Ok(())\n    }\n    fn compute_size(&self) -> usize {\n        use ::micropb::{FieldEncode, PbMap};\n        let mut size = 0;\n        {\n            let val_ref = &self.r#hb_num;\n            if *val_ref != 0 {\n                size += 1usize + ::micropb::size::sizeof_int32(*val_ref as _);\n            }\n        }\n        size\n    }\n}\n#[derive(Debug, Default, PartialEq, Clone)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub struct CtrlMsg_Event_StationDisconnectFromAP {\n    pub r#resp: i32,\n    pub r#ssid: ::heapless::Vec<u8, 32>,\n    pub r#ssid_len: u32,\n    pub r#bssid: ::heapless::Vec<u8, 32>,\n    pub r#reason: u32,\n    pub r#rssi: i32,\n}\nimpl CtrlMsg_Event_StationDisconnectFromAP {\n    /// Return a reference to `resp`\n    #[inline]\n    pub fn r#resp(&self) -> &i32 {\n        &self.r#resp\n    }\n    /// Return a mutable reference to `resp`\n    #[inline]\n    pub fn mut_resp(&mut self) -> &mut i32 {\n        &mut self.r#resp\n    }\n    /// Set the value of `resp`\n    #[inline]\n    pub fn set_resp(&mut self, value: i32) -> &mut Self {\n        self.r#resp = value.into();\n        self\n    }\n    /// Builder method that sets the value of `resp`. Useful for initializing the message.\n    #[inline]\n    pub fn init_resp(mut self, value: i32) -> Self {\n        self.r#resp = value.into();\n        self\n    }\n    /// Return a reference to `ssid`\n    #[inline]\n    pub fn r#ssid(&self) -> &::heapless::Vec<u8, 32> {\n        &self.r#ssid\n    }\n    /// Return a mutable reference to `ssid`\n    #[inline]\n    pub fn mut_ssid(&mut self) -> &mut ::heapless::Vec<u8, 32> {\n        &mut self.r#ssid\n    }\n    /// Set the value of `ssid`\n    #[inline]\n    pub fn set_ssid(&mut self, value: ::heapless::Vec<u8, 32>) -> &mut Self {\n        self.r#ssid = value.into();\n        self\n    }\n    /// Builder method that sets the value of `ssid`. Useful for initializing the message.\n    #[inline]\n    pub fn init_ssid(mut self, value: ::heapless::Vec<u8, 32>) -> Self {\n        self.r#ssid = value.into();\n        self\n    }\n    /// Return a reference to `ssid_len`\n    #[inline]\n    pub fn r#ssid_len(&self) -> &u32 {\n        &self.r#ssid_len\n    }\n    /// Return a mutable reference to `ssid_len`\n    #[inline]\n    pub fn mut_ssid_len(&mut self) -> &mut u32 {\n        &mut self.r#ssid_len\n    }\n    /// Set the value of `ssid_len`\n    #[inline]\n    pub fn set_ssid_len(&mut self, value: u32) -> &mut Self {\n        self.r#ssid_len = value.into();\n        self\n    }\n    /// Builder method that sets the value of `ssid_len`. Useful for initializing the message.\n    #[inline]\n    pub fn init_ssid_len(mut self, value: u32) -> Self {\n        self.r#ssid_len = value.into();\n        self\n    }\n    /// Return a reference to `bssid`\n    #[inline]\n    pub fn r#bssid(&self) -> &::heapless::Vec<u8, 32> {\n        &self.r#bssid\n    }\n    /// Return a mutable reference to `bssid`\n    #[inline]\n    pub fn mut_bssid(&mut self) -> &mut ::heapless::Vec<u8, 32> {\n        &mut self.r#bssid\n    }\n    /// Set the value of `bssid`\n    #[inline]\n    pub fn set_bssid(&mut self, value: ::heapless::Vec<u8, 32>) -> &mut Self {\n        self.r#bssid = value.into();\n        self\n    }\n    /// Builder method that sets the value of `bssid`. Useful for initializing the message.\n    #[inline]\n    pub fn init_bssid(mut self, value: ::heapless::Vec<u8, 32>) -> Self {\n        self.r#bssid = value.into();\n        self\n    }\n    /// Return a reference to `reason`\n    #[inline]\n    pub fn r#reason(&self) -> &u32 {\n        &self.r#reason\n    }\n    /// Return a mutable reference to `reason`\n    #[inline]\n    pub fn mut_reason(&mut self) -> &mut u32 {\n        &mut self.r#reason\n    }\n    /// Set the value of `reason`\n    #[inline]\n    pub fn set_reason(&mut self, value: u32) -> &mut Self {\n        self.r#reason = value.into();\n        self\n    }\n    /// Builder method that sets the value of `reason`. Useful for initializing the message.\n    #[inline]\n    pub fn init_reason(mut self, value: u32) -> Self {\n        self.r#reason = value.into();\n        self\n    }\n    /// Return a reference to `rssi`\n    #[inline]\n    pub fn r#rssi(&self) -> &i32 {\n        &self.r#rssi\n    }\n    /// Return a mutable reference to `rssi`\n    #[inline]\n    pub fn mut_rssi(&mut self) -> &mut i32 {\n        &mut self.r#rssi\n    }\n    /// Set the value of `rssi`\n    #[inline]\n    pub fn set_rssi(&mut self, value: i32) -> &mut Self {\n        self.r#rssi = value.into();\n        self\n    }\n    /// Builder method that sets the value of `rssi`. Useful for initializing the message.\n    #[inline]\n    pub fn init_rssi(mut self, value: i32) -> Self {\n        self.r#rssi = value.into();\n        self\n    }\n}\nimpl ::micropb::MessageDecode for CtrlMsg_Event_StationDisconnectFromAP {\n    fn decode<IMPL_MICROPB_READ: ::micropb::PbRead>(\n        &mut self,\n        decoder: &mut ::micropb::PbDecoder<IMPL_MICROPB_READ>,\n        len: usize,\n    ) -> Result<(), ::micropb::DecodeError<IMPL_MICROPB_READ::Error>> {\n        use ::micropb::{FieldDecode, PbBytes, PbMap, PbString, PbVec};\n        let before = decoder.bytes_read();\n        while decoder.bytes_read() - before < len {\n            let tag = decoder.decode_tag()?;\n            match tag.field_num() {\n                0 => return Err(::micropb::DecodeError::ZeroField),\n                1u32 => {\n                    let mut_ref = &mut self.r#resp;\n                    {\n                        let val = decoder.decode_int32()?;\n                        let val_ref = &val;\n                        if *val_ref != 0 {\n                            *mut_ref = val as _;\n                        }\n                    };\n                }\n                2u32 => {\n                    let mut_ref = &mut self.r#ssid;\n                    {\n                        decoder.decode_bytes(mut_ref, ::micropb::Presence::Implicit)?;\n                    };\n                }\n                3u32 => {\n                    let mut_ref = &mut self.r#ssid_len;\n                    {\n                        let val = decoder.decode_varint32()?;\n                        let val_ref = &val;\n                        if *val_ref != 0 {\n                            *mut_ref = val as _;\n                        }\n                    };\n                }\n                4u32 => {\n                    let mut_ref = &mut self.r#bssid;\n                    {\n                        decoder.decode_bytes(mut_ref, ::micropb::Presence::Implicit)?;\n                    };\n                }\n                5u32 => {\n                    let mut_ref = &mut self.r#reason;\n                    {\n                        let val = decoder.decode_varint32()?;\n                        let val_ref = &val;\n                        if *val_ref != 0 {\n                            *mut_ref = val as _;\n                        }\n                    };\n                }\n                6u32 => {\n                    let mut_ref = &mut self.r#rssi;\n                    {\n                        let val = decoder.decode_int32()?;\n                        let val_ref = &val;\n                        if *val_ref != 0 {\n                            *mut_ref = val as _;\n                        }\n                    };\n                }\n                _ => {\n                    decoder.skip_wire_value(tag.wire_type())?;\n                }\n            }\n        }\n        Ok(())\n    }\n}\nimpl ::micropb::MessageEncode for CtrlMsg_Event_StationDisconnectFromAP {\n    const MAX_SIZE: ::core::result::Result<usize, &'static str> = 'msg: {\n        let mut max_size = 0;\n        match ::micropb::const_map!(::core::result::Result::Ok(10usize), |size| size + 1usize) {\n            ::core::result::Result::Ok(size) => {\n                max_size += size;\n            }\n            ::core::result::Result::Err(err) => {\n                break 'msg (::core::result::Result::<usize, _>::Err(err));\n            }\n        }\n        match ::micropb::const_map!(::core::result::Result::Ok(33usize), |size| size + 1usize) {\n            ::core::result::Result::Ok(size) => {\n                max_size += size;\n            }\n            ::core::result::Result::Err(err) => {\n                break 'msg (::core::result::Result::<usize, _>::Err(err));\n            }\n        }\n        match ::micropb::const_map!(::core::result::Result::Ok(5usize), |size| size + 1usize) {\n            ::core::result::Result::Ok(size) => {\n                max_size += size;\n            }\n            ::core::result::Result::Err(err) => {\n                break 'msg (::core::result::Result::<usize, _>::Err(err));\n            }\n        }\n        match ::micropb::const_map!(::core::result::Result::Ok(33usize), |size| size + 1usize) {\n            ::core::result::Result::Ok(size) => {\n                max_size += size;\n            }\n            ::core::result::Result::Err(err) => {\n                break 'msg (::core::result::Result::<usize, _>::Err(err));\n            }\n        }\n        match ::micropb::const_map!(::core::result::Result::Ok(5usize), |size| size + 1usize) {\n            ::core::result::Result::Ok(size) => {\n                max_size += size;\n            }\n            ::core::result::Result::Err(err) => {\n                break 'msg (::core::result::Result::<usize, _>::Err(err));\n            }\n        }\n        match ::micropb::const_map!(::core::result::Result::Ok(10usize), |size| size + 1usize) {\n            ::core::result::Result::Ok(size) => {\n                max_size += size;\n            }\n            ::core::result::Result::Err(err) => {\n                break 'msg (::core::result::Result::<usize, _>::Err(err));\n            }\n        }\n        ::core::result::Result::Ok(max_size)\n    };\n    fn encode<IMPL_MICROPB_WRITE: ::micropb::PbWrite>(\n        &self,\n        encoder: &mut ::micropb::PbEncoder<IMPL_MICROPB_WRITE>,\n    ) -> Result<(), IMPL_MICROPB_WRITE::Error> {\n        use ::micropb::{FieldEncode, PbMap};\n        {\n            let val_ref = &self.r#resp;\n            if *val_ref != 0 {\n                encoder.encode_varint32(8u32)?;\n                encoder.encode_int32(*val_ref as _)?;\n            }\n        }\n        {\n            let val_ref = &self.r#ssid;\n            if !val_ref.is_empty() {\n                encoder.encode_varint32(18u32)?;\n                encoder.encode_bytes(val_ref)?;\n            }\n        }\n        {\n            let val_ref = &self.r#ssid_len;\n            if *val_ref != 0 {\n                encoder.encode_varint32(24u32)?;\n                encoder.encode_varint32(*val_ref as _)?;\n            }\n        }\n        {\n            let val_ref = &self.r#bssid;\n            if !val_ref.is_empty() {\n                encoder.encode_varint32(34u32)?;\n                encoder.encode_bytes(val_ref)?;\n            }\n        }\n        {\n            let val_ref = &self.r#reason;\n            if *val_ref != 0 {\n                encoder.encode_varint32(40u32)?;\n                encoder.encode_varint32(*val_ref as _)?;\n            }\n        }\n        {\n            let val_ref = &self.r#rssi;\n            if *val_ref != 0 {\n                encoder.encode_varint32(48u32)?;\n                encoder.encode_int32(*val_ref as _)?;\n            }\n        }\n        Ok(())\n    }\n    fn compute_size(&self) -> usize {\n        use ::micropb::{FieldEncode, PbMap};\n        let mut size = 0;\n        {\n            let val_ref = &self.r#resp;\n            if *val_ref != 0 {\n                size += 1usize + ::micropb::size::sizeof_int32(*val_ref as _);\n            }\n        }\n        {\n            let val_ref = &self.r#ssid;\n            if !val_ref.is_empty() {\n                size += 1usize + ::micropb::size::sizeof_len_record(val_ref.len());\n            }\n        }\n        {\n            let val_ref = &self.r#ssid_len;\n            if *val_ref != 0 {\n                size += 1usize + ::micropb::size::sizeof_varint32(*val_ref as _);\n            }\n        }\n        {\n            let val_ref = &self.r#bssid;\n            if !val_ref.is_empty() {\n                size += 1usize + ::micropb::size::sizeof_len_record(val_ref.len());\n            }\n        }\n        {\n            let val_ref = &self.r#reason;\n            if *val_ref != 0 {\n                size += 1usize + ::micropb::size::sizeof_varint32(*val_ref as _);\n            }\n        }\n        {\n            let val_ref = &self.r#rssi;\n            if *val_ref != 0 {\n                size += 1usize + ::micropb::size::sizeof_int32(*val_ref as _);\n            }\n        }\n        size\n    }\n}\n#[derive(Debug, Default, PartialEq, Clone)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub struct CtrlMsg_Event_StationConnectedToAP {\n    pub r#resp: i32,\n    pub r#ssid: ::heapless::Vec<u8, 32>,\n    pub r#ssid_len: u32,\n    pub r#bssid: ::heapless::Vec<u8, 32>,\n    pub r#channel: u32,\n    pub r#authmode: i32,\n    pub r#aid: i32,\n}\nimpl CtrlMsg_Event_StationConnectedToAP {\n    /// Return a reference to `resp`\n    #[inline]\n    pub fn r#resp(&self) -> &i32 {\n        &self.r#resp\n    }\n    /// Return a mutable reference to `resp`\n    #[inline]\n    pub fn mut_resp(&mut self) -> &mut i32 {\n        &mut self.r#resp\n    }\n    /// Set the value of `resp`\n    #[inline]\n    pub fn set_resp(&mut self, value: i32) -> &mut Self {\n        self.r#resp = value.into();\n        self\n    }\n    /// Builder method that sets the value of `resp`. Useful for initializing the message.\n    #[inline]\n    pub fn init_resp(mut self, value: i32) -> Self {\n        self.r#resp = value.into();\n        self\n    }\n    /// Return a reference to `ssid`\n    #[inline]\n    pub fn r#ssid(&self) -> &::heapless::Vec<u8, 32> {\n        &self.r#ssid\n    }\n    /// Return a mutable reference to `ssid`\n    #[inline]\n    pub fn mut_ssid(&mut self) -> &mut ::heapless::Vec<u8, 32> {\n        &mut self.r#ssid\n    }\n    /// Set the value of `ssid`\n    #[inline]\n    pub fn set_ssid(&mut self, value: ::heapless::Vec<u8, 32>) -> &mut Self {\n        self.r#ssid = value.into();\n        self\n    }\n    /// Builder method that sets the value of `ssid`. Useful for initializing the message.\n    #[inline]\n    pub fn init_ssid(mut self, value: ::heapless::Vec<u8, 32>) -> Self {\n        self.r#ssid = value.into();\n        self\n    }\n    /// Return a reference to `ssid_len`\n    #[inline]\n    pub fn r#ssid_len(&self) -> &u32 {\n        &self.r#ssid_len\n    }\n    /// Return a mutable reference to `ssid_len`\n    #[inline]\n    pub fn mut_ssid_len(&mut self) -> &mut u32 {\n        &mut self.r#ssid_len\n    }\n    /// Set the value of `ssid_len`\n    #[inline]\n    pub fn set_ssid_len(&mut self, value: u32) -> &mut Self {\n        self.r#ssid_len = value.into();\n        self\n    }\n    /// Builder method that sets the value of `ssid_len`. Useful for initializing the message.\n    #[inline]\n    pub fn init_ssid_len(mut self, value: u32) -> Self {\n        self.r#ssid_len = value.into();\n        self\n    }\n    /// Return a reference to `bssid`\n    #[inline]\n    pub fn r#bssid(&self) -> &::heapless::Vec<u8, 32> {\n        &self.r#bssid\n    }\n    /// Return a mutable reference to `bssid`\n    #[inline]\n    pub fn mut_bssid(&mut self) -> &mut ::heapless::Vec<u8, 32> {\n        &mut self.r#bssid\n    }\n    /// Set the value of `bssid`\n    #[inline]\n    pub fn set_bssid(&mut self, value: ::heapless::Vec<u8, 32>) -> &mut Self {\n        self.r#bssid = value.into();\n        self\n    }\n    /// Builder method that sets the value of `bssid`. Useful for initializing the message.\n    #[inline]\n    pub fn init_bssid(mut self, value: ::heapless::Vec<u8, 32>) -> Self {\n        self.r#bssid = value.into();\n        self\n    }\n    /// Return a reference to `channel`\n    #[inline]\n    pub fn r#channel(&self) -> &u32 {\n        &self.r#channel\n    }\n    /// Return a mutable reference to `channel`\n    #[inline]\n    pub fn mut_channel(&mut self) -> &mut u32 {\n        &mut self.r#channel\n    }\n    /// Set the value of `channel`\n    #[inline]\n    pub fn set_channel(&mut self, value: u32) -> &mut Self {\n        self.r#channel = value.into();\n        self\n    }\n    /// Builder method that sets the value of `channel`. Useful for initializing the message.\n    #[inline]\n    pub fn init_channel(mut self, value: u32) -> Self {\n        self.r#channel = value.into();\n        self\n    }\n    /// Return a reference to `authmode`\n    #[inline]\n    pub fn r#authmode(&self) -> &i32 {\n        &self.r#authmode\n    }\n    /// Return a mutable reference to `authmode`\n    #[inline]\n    pub fn mut_authmode(&mut self) -> &mut i32 {\n        &mut self.r#authmode\n    }\n    /// Set the value of `authmode`\n    #[inline]\n    pub fn set_authmode(&mut self, value: i32) -> &mut Self {\n        self.r#authmode = value.into();\n        self\n    }\n    /// Builder method that sets the value of `authmode`. Useful for initializing the message.\n    #[inline]\n    pub fn init_authmode(mut self, value: i32) -> Self {\n        self.r#authmode = value.into();\n        self\n    }\n    /// Return a reference to `aid`\n    #[inline]\n    pub fn r#aid(&self) -> &i32 {\n        &self.r#aid\n    }\n    /// Return a mutable reference to `aid`\n    #[inline]\n    pub fn mut_aid(&mut self) -> &mut i32 {\n        &mut self.r#aid\n    }\n    /// Set the value of `aid`\n    #[inline]\n    pub fn set_aid(&mut self, value: i32) -> &mut Self {\n        self.r#aid = value.into();\n        self\n    }\n    /// Builder method that sets the value of `aid`. Useful for initializing the message.\n    #[inline]\n    pub fn init_aid(mut self, value: i32) -> Self {\n        self.r#aid = value.into();\n        self\n    }\n}\nimpl ::micropb::MessageDecode for CtrlMsg_Event_StationConnectedToAP {\n    fn decode<IMPL_MICROPB_READ: ::micropb::PbRead>(\n        &mut self,\n        decoder: &mut ::micropb::PbDecoder<IMPL_MICROPB_READ>,\n        len: usize,\n    ) -> Result<(), ::micropb::DecodeError<IMPL_MICROPB_READ::Error>> {\n        use ::micropb::{FieldDecode, PbBytes, PbMap, PbString, PbVec};\n        let before = decoder.bytes_read();\n        while decoder.bytes_read() - before < len {\n            let tag = decoder.decode_tag()?;\n            match tag.field_num() {\n                0 => return Err(::micropb::DecodeError::ZeroField),\n                1u32 => {\n                    let mut_ref = &mut self.r#resp;\n                    {\n                        let val = decoder.decode_int32()?;\n                        let val_ref = &val;\n                        if *val_ref != 0 {\n                            *mut_ref = val as _;\n                        }\n                    };\n                }\n                2u32 => {\n                    let mut_ref = &mut self.r#ssid;\n                    {\n                        decoder.decode_bytes(mut_ref, ::micropb::Presence::Implicit)?;\n                    };\n                }\n                3u32 => {\n                    let mut_ref = &mut self.r#ssid_len;\n                    {\n                        let val = decoder.decode_varint32()?;\n                        let val_ref = &val;\n                        if *val_ref != 0 {\n                            *mut_ref = val as _;\n                        }\n                    };\n                }\n                4u32 => {\n                    let mut_ref = &mut self.r#bssid;\n                    {\n                        decoder.decode_bytes(mut_ref, ::micropb::Presence::Implicit)?;\n                    };\n                }\n                5u32 => {\n                    let mut_ref = &mut self.r#channel;\n                    {\n                        let val = decoder.decode_varint32()?;\n                        let val_ref = &val;\n                        if *val_ref != 0 {\n                            *mut_ref = val as _;\n                        }\n                    };\n                }\n                6u32 => {\n                    let mut_ref = &mut self.r#authmode;\n                    {\n                        let val = decoder.decode_int32()?;\n                        let val_ref = &val;\n                        if *val_ref != 0 {\n                            *mut_ref = val as _;\n                        }\n                    };\n                }\n                7u32 => {\n                    let mut_ref = &mut self.r#aid;\n                    {\n                        let val = decoder.decode_int32()?;\n                        let val_ref = &val;\n                        if *val_ref != 0 {\n                            *mut_ref = val as _;\n                        }\n                    };\n                }\n                _ => {\n                    decoder.skip_wire_value(tag.wire_type())?;\n                }\n            }\n        }\n        Ok(())\n    }\n}\nimpl ::micropb::MessageEncode for CtrlMsg_Event_StationConnectedToAP {\n    const MAX_SIZE: ::core::result::Result<usize, &'static str> = 'msg: {\n        let mut max_size = 0;\n        match ::micropb::const_map!(::core::result::Result::Ok(10usize), |size| size + 1usize) {\n            ::core::result::Result::Ok(size) => {\n                max_size += size;\n            }\n            ::core::result::Result::Err(err) => {\n                break 'msg (::core::result::Result::<usize, _>::Err(err));\n            }\n        }\n        match ::micropb::const_map!(::core::result::Result::Ok(33usize), |size| size + 1usize) {\n            ::core::result::Result::Ok(size) => {\n                max_size += size;\n            }\n            ::core::result::Result::Err(err) => {\n                break 'msg (::core::result::Result::<usize, _>::Err(err));\n            }\n        }\n        match ::micropb::const_map!(::core::result::Result::Ok(5usize), |size| size + 1usize) {\n            ::core::result::Result::Ok(size) => {\n                max_size += size;\n            }\n            ::core::result::Result::Err(err) => {\n                break 'msg (::core::result::Result::<usize, _>::Err(err));\n            }\n        }\n        match ::micropb::const_map!(::core::result::Result::Ok(33usize), |size| size + 1usize) {\n            ::core::result::Result::Ok(size) => {\n                max_size += size;\n            }\n            ::core::result::Result::Err(err) => {\n                break 'msg (::core::result::Result::<usize, _>::Err(err));\n            }\n        }\n        match ::micropb::const_map!(::core::result::Result::Ok(5usize), |size| size + 1usize) {\n            ::core::result::Result::Ok(size) => {\n                max_size += size;\n            }\n            ::core::result::Result::Err(err) => {\n                break 'msg (::core::result::Result::<usize, _>::Err(err));\n            }\n        }\n        match ::micropb::const_map!(::core::result::Result::Ok(10usize), |size| size + 1usize) {\n            ::core::result::Result::Ok(size) => {\n                max_size += size;\n            }\n            ::core::result::Result::Err(err) => {\n                break 'msg (::core::result::Result::<usize, _>::Err(err));\n            }\n        }\n        match ::micropb::const_map!(::core::result::Result::Ok(10usize), |size| size + 1usize) {\n            ::core::result::Result::Ok(size) => {\n                max_size += size;\n            }\n            ::core::result::Result::Err(err) => {\n                break 'msg (::core::result::Result::<usize, _>::Err(err));\n            }\n        }\n        ::core::result::Result::Ok(max_size)\n    };\n    fn encode<IMPL_MICROPB_WRITE: ::micropb::PbWrite>(\n        &self,\n        encoder: &mut ::micropb::PbEncoder<IMPL_MICROPB_WRITE>,\n    ) -> Result<(), IMPL_MICROPB_WRITE::Error> {\n        use ::micropb::{FieldEncode, PbMap};\n        {\n            let val_ref = &self.r#resp;\n            if *val_ref != 0 {\n                encoder.encode_varint32(8u32)?;\n                encoder.encode_int32(*val_ref as _)?;\n            }\n        }\n        {\n            let val_ref = &self.r#ssid;\n            if !val_ref.is_empty() {\n                encoder.encode_varint32(18u32)?;\n                encoder.encode_bytes(val_ref)?;\n            }\n        }\n        {\n            let val_ref = &self.r#ssid_len;\n            if *val_ref != 0 {\n                encoder.encode_varint32(24u32)?;\n                encoder.encode_varint32(*val_ref as _)?;\n            }\n        }\n        {\n            let val_ref = &self.r#bssid;\n            if !val_ref.is_empty() {\n                encoder.encode_varint32(34u32)?;\n                encoder.encode_bytes(val_ref)?;\n            }\n        }\n        {\n            let val_ref = &self.r#channel;\n            if *val_ref != 0 {\n                encoder.encode_varint32(40u32)?;\n                encoder.encode_varint32(*val_ref as _)?;\n            }\n        }\n        {\n            let val_ref = &self.r#authmode;\n            if *val_ref != 0 {\n                encoder.encode_varint32(48u32)?;\n                encoder.encode_int32(*val_ref as _)?;\n            }\n        }\n        {\n            let val_ref = &self.r#aid;\n            if *val_ref != 0 {\n                encoder.encode_varint32(56u32)?;\n                encoder.encode_int32(*val_ref as _)?;\n            }\n        }\n        Ok(())\n    }\n    fn compute_size(&self) -> usize {\n        use ::micropb::{FieldEncode, PbMap};\n        let mut size = 0;\n        {\n            let val_ref = &self.r#resp;\n            if *val_ref != 0 {\n                size += 1usize + ::micropb::size::sizeof_int32(*val_ref as _);\n            }\n        }\n        {\n            let val_ref = &self.r#ssid;\n            if !val_ref.is_empty() {\n                size += 1usize + ::micropb::size::sizeof_len_record(val_ref.len());\n            }\n        }\n        {\n            let val_ref = &self.r#ssid_len;\n            if *val_ref != 0 {\n                size += 1usize + ::micropb::size::sizeof_varint32(*val_ref as _);\n            }\n        }\n        {\n            let val_ref = &self.r#bssid;\n            if !val_ref.is_empty() {\n                size += 1usize + ::micropb::size::sizeof_len_record(val_ref.len());\n            }\n        }\n        {\n            let val_ref = &self.r#channel;\n            if *val_ref != 0 {\n                size += 1usize + ::micropb::size::sizeof_varint32(*val_ref as _);\n            }\n        }\n        {\n            let val_ref = &self.r#authmode;\n            if *val_ref != 0 {\n                size += 1usize + ::micropb::size::sizeof_int32(*val_ref as _);\n            }\n        }\n        {\n            let val_ref = &self.r#aid;\n            if *val_ref != 0 {\n                size += 1usize + ::micropb::size::sizeof_int32(*val_ref as _);\n            }\n        }\n        size\n    }\n}\n#[derive(Debug, Default, PartialEq, Clone)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub struct CtrlMsg_Event_StationDisconnectFromESPSoftAP {\n    pub r#resp: i32,\n    pub r#mac: ::heapless::Vec<u8, 32>,\n    pub r#aid: u32,\n    pub r#is_mesh_child: bool,\n    pub r#reason: u32,\n}\nimpl CtrlMsg_Event_StationDisconnectFromESPSoftAP {\n    /// Return a reference to `resp`\n    #[inline]\n    pub fn r#resp(&self) -> &i32 {\n        &self.r#resp\n    }\n    /// Return a mutable reference to `resp`\n    #[inline]\n    pub fn mut_resp(&mut self) -> &mut i32 {\n        &mut self.r#resp\n    }\n    /// Set the value of `resp`\n    #[inline]\n    pub fn set_resp(&mut self, value: i32) -> &mut Self {\n        self.r#resp = value.into();\n        self\n    }\n    /// Builder method that sets the value of `resp`. Useful for initializing the message.\n    #[inline]\n    pub fn init_resp(mut self, value: i32) -> Self {\n        self.r#resp = value.into();\n        self\n    }\n    /// Return a reference to `mac`\n    #[inline]\n    pub fn r#mac(&self) -> &::heapless::Vec<u8, 32> {\n        &self.r#mac\n    }\n    /// Return a mutable reference to `mac`\n    #[inline]\n    pub fn mut_mac(&mut self) -> &mut ::heapless::Vec<u8, 32> {\n        &mut self.r#mac\n    }\n    /// Set the value of `mac`\n    #[inline]\n    pub fn set_mac(&mut self, value: ::heapless::Vec<u8, 32>) -> &mut Self {\n        self.r#mac = value.into();\n        self\n    }\n    /// Builder method that sets the value of `mac`. Useful for initializing the message.\n    #[inline]\n    pub fn init_mac(mut self, value: ::heapless::Vec<u8, 32>) -> Self {\n        self.r#mac = value.into();\n        self\n    }\n    /// Return a reference to `aid`\n    #[inline]\n    pub fn r#aid(&self) -> &u32 {\n        &self.r#aid\n    }\n    /// Return a mutable reference to `aid`\n    #[inline]\n    pub fn mut_aid(&mut self) -> &mut u32 {\n        &mut self.r#aid\n    }\n    /// Set the value of `aid`\n    #[inline]\n    pub fn set_aid(&mut self, value: u32) -> &mut Self {\n        self.r#aid = value.into();\n        self\n    }\n    /// Builder method that sets the value of `aid`. Useful for initializing the message.\n    #[inline]\n    pub fn init_aid(mut self, value: u32) -> Self {\n        self.r#aid = value.into();\n        self\n    }\n    /// Return a reference to `is_mesh_child`\n    #[inline]\n    pub fn r#is_mesh_child(&self) -> &bool {\n        &self.r#is_mesh_child\n    }\n    /// Return a mutable reference to `is_mesh_child`\n    #[inline]\n    pub fn mut_is_mesh_child(&mut self) -> &mut bool {\n        &mut self.r#is_mesh_child\n    }\n    /// Set the value of `is_mesh_child`\n    #[inline]\n    pub fn set_is_mesh_child(&mut self, value: bool) -> &mut Self {\n        self.r#is_mesh_child = value.into();\n        self\n    }\n    /// Builder method that sets the value of `is_mesh_child`. Useful for initializing the message.\n    #[inline]\n    pub fn init_is_mesh_child(mut self, value: bool) -> Self {\n        self.r#is_mesh_child = value.into();\n        self\n    }\n    /// Return a reference to `reason`\n    #[inline]\n    pub fn r#reason(&self) -> &u32 {\n        &self.r#reason\n    }\n    /// Return a mutable reference to `reason`\n    #[inline]\n    pub fn mut_reason(&mut self) -> &mut u32 {\n        &mut self.r#reason\n    }\n    /// Set the value of `reason`\n    #[inline]\n    pub fn set_reason(&mut self, value: u32) -> &mut Self {\n        self.r#reason = value.into();\n        self\n    }\n    /// Builder method that sets the value of `reason`. Useful for initializing the message.\n    #[inline]\n    pub fn init_reason(mut self, value: u32) -> Self {\n        self.r#reason = value.into();\n        self\n    }\n}\nimpl ::micropb::MessageDecode for CtrlMsg_Event_StationDisconnectFromESPSoftAP {\n    fn decode<IMPL_MICROPB_READ: ::micropb::PbRead>(\n        &mut self,\n        decoder: &mut ::micropb::PbDecoder<IMPL_MICROPB_READ>,\n        len: usize,\n    ) -> Result<(), ::micropb::DecodeError<IMPL_MICROPB_READ::Error>> {\n        use ::micropb::{FieldDecode, PbBytes, PbMap, PbString, PbVec};\n        let before = decoder.bytes_read();\n        while decoder.bytes_read() - before < len {\n            let tag = decoder.decode_tag()?;\n            match tag.field_num() {\n                0 => return Err(::micropb::DecodeError::ZeroField),\n                1u32 => {\n                    let mut_ref = &mut self.r#resp;\n                    {\n                        let val = decoder.decode_int32()?;\n                        let val_ref = &val;\n                        if *val_ref != 0 {\n                            *mut_ref = val as _;\n                        }\n                    };\n                }\n                2u32 => {\n                    let mut_ref = &mut self.r#mac;\n                    {\n                        decoder.decode_bytes(mut_ref, ::micropb::Presence::Implicit)?;\n                    };\n                }\n                3u32 => {\n                    let mut_ref = &mut self.r#aid;\n                    {\n                        let val = decoder.decode_varint32()?;\n                        let val_ref = &val;\n                        if *val_ref != 0 {\n                            *mut_ref = val as _;\n                        }\n                    };\n                }\n                4u32 => {\n                    let mut_ref = &mut self.r#is_mesh_child;\n                    {\n                        let val = decoder.decode_bool()?;\n                        let val_ref = &val;\n                        if *val_ref {\n                            *mut_ref = val as _;\n                        }\n                    };\n                }\n                5u32 => {\n                    let mut_ref = &mut self.r#reason;\n                    {\n                        let val = decoder.decode_varint32()?;\n                        let val_ref = &val;\n                        if *val_ref != 0 {\n                            *mut_ref = val as _;\n                        }\n                    };\n                }\n                _ => {\n                    decoder.skip_wire_value(tag.wire_type())?;\n                }\n            }\n        }\n        Ok(())\n    }\n}\nimpl ::micropb::MessageEncode for CtrlMsg_Event_StationDisconnectFromESPSoftAP {\n    const MAX_SIZE: ::core::result::Result<usize, &'static str> = 'msg: {\n        let mut max_size = 0;\n        match ::micropb::const_map!(::core::result::Result::Ok(10usize), |size| size + 1usize) {\n            ::core::result::Result::Ok(size) => {\n                max_size += size;\n            }\n            ::core::result::Result::Err(err) => {\n                break 'msg (::core::result::Result::<usize, _>::Err(err));\n            }\n        }\n        match ::micropb::const_map!(::core::result::Result::Ok(33usize), |size| size + 1usize) {\n            ::core::result::Result::Ok(size) => {\n                max_size += size;\n            }\n            ::core::result::Result::Err(err) => {\n                break 'msg (::core::result::Result::<usize, _>::Err(err));\n            }\n        }\n        match ::micropb::const_map!(::core::result::Result::Ok(5usize), |size| size + 1usize) {\n            ::core::result::Result::Ok(size) => {\n                max_size += size;\n            }\n            ::core::result::Result::Err(err) => {\n                break 'msg (::core::result::Result::<usize, _>::Err(err));\n            }\n        }\n        match ::micropb::const_map!(::core::result::Result::Ok(1usize), |size| size + 1usize) {\n            ::core::result::Result::Ok(size) => {\n                max_size += size;\n            }\n            ::core::result::Result::Err(err) => {\n                break 'msg (::core::result::Result::<usize, _>::Err(err));\n            }\n        }\n        match ::micropb::const_map!(::core::result::Result::Ok(5usize), |size| size + 1usize) {\n            ::core::result::Result::Ok(size) => {\n                max_size += size;\n            }\n            ::core::result::Result::Err(err) => {\n                break 'msg (::core::result::Result::<usize, _>::Err(err));\n            }\n        }\n        ::core::result::Result::Ok(max_size)\n    };\n    fn encode<IMPL_MICROPB_WRITE: ::micropb::PbWrite>(\n        &self,\n        encoder: &mut ::micropb::PbEncoder<IMPL_MICROPB_WRITE>,\n    ) -> Result<(), IMPL_MICROPB_WRITE::Error> {\n        use ::micropb::{FieldEncode, PbMap};\n        {\n            let val_ref = &self.r#resp;\n            if *val_ref != 0 {\n                encoder.encode_varint32(8u32)?;\n                encoder.encode_int32(*val_ref as _)?;\n            }\n        }\n        {\n            let val_ref = &self.r#mac;\n            if !val_ref.is_empty() {\n                encoder.encode_varint32(18u32)?;\n                encoder.encode_bytes(val_ref)?;\n            }\n        }\n        {\n            let val_ref = &self.r#aid;\n            if *val_ref != 0 {\n                encoder.encode_varint32(24u32)?;\n                encoder.encode_varint32(*val_ref as _)?;\n            }\n        }\n        {\n            let val_ref = &self.r#is_mesh_child;\n            if *val_ref {\n                encoder.encode_varint32(32u32)?;\n                encoder.encode_bool(*val_ref)?;\n            }\n        }\n        {\n            let val_ref = &self.r#reason;\n            if *val_ref != 0 {\n                encoder.encode_varint32(40u32)?;\n                encoder.encode_varint32(*val_ref as _)?;\n            }\n        }\n        Ok(())\n    }\n    fn compute_size(&self) -> usize {\n        use ::micropb::{FieldEncode, PbMap};\n        let mut size = 0;\n        {\n            let val_ref = &self.r#resp;\n            if *val_ref != 0 {\n                size += 1usize + ::micropb::size::sizeof_int32(*val_ref as _);\n            }\n        }\n        {\n            let val_ref = &self.r#mac;\n            if !val_ref.is_empty() {\n                size += 1usize + ::micropb::size::sizeof_len_record(val_ref.len());\n            }\n        }\n        {\n            let val_ref = &self.r#aid;\n            if *val_ref != 0 {\n                size += 1usize + ::micropb::size::sizeof_varint32(*val_ref as _);\n            }\n        }\n        {\n            let val_ref = &self.r#is_mesh_child;\n            if *val_ref {\n                size += 1usize + 1;\n            }\n        }\n        {\n            let val_ref = &self.r#reason;\n            if *val_ref != 0 {\n                size += 1usize + ::micropb::size::sizeof_varint32(*val_ref as _);\n            }\n        }\n        size\n    }\n}\n#[derive(Debug, Default, PartialEq, Clone)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub struct CtrlMsg_Event_StationConnectedToESPSoftAP {\n    pub r#resp: i32,\n    pub r#mac: ::heapless::Vec<u8, 32>,\n    pub r#aid: u32,\n    pub r#is_mesh_child: bool,\n}\nimpl CtrlMsg_Event_StationConnectedToESPSoftAP {\n    /// Return a reference to `resp`\n    #[inline]\n    pub fn r#resp(&self) -> &i32 {\n        &self.r#resp\n    }\n    /// Return a mutable reference to `resp`\n    #[inline]\n    pub fn mut_resp(&mut self) -> &mut i32 {\n        &mut self.r#resp\n    }\n    /// Set the value of `resp`\n    #[inline]\n    pub fn set_resp(&mut self, value: i32) -> &mut Self {\n        self.r#resp = value.into();\n        self\n    }\n    /// Builder method that sets the value of `resp`. Useful for initializing the message.\n    #[inline]\n    pub fn init_resp(mut self, value: i32) -> Self {\n        self.r#resp = value.into();\n        self\n    }\n    /// Return a reference to `mac`\n    #[inline]\n    pub fn r#mac(&self) -> &::heapless::Vec<u8, 32> {\n        &self.r#mac\n    }\n    /// Return a mutable reference to `mac`\n    #[inline]\n    pub fn mut_mac(&mut self) -> &mut ::heapless::Vec<u8, 32> {\n        &mut self.r#mac\n    }\n    /// Set the value of `mac`\n    #[inline]\n    pub fn set_mac(&mut self, value: ::heapless::Vec<u8, 32>) -> &mut Self {\n        self.r#mac = value.into();\n        self\n    }\n    /// Builder method that sets the value of `mac`. Useful for initializing the message.\n    #[inline]\n    pub fn init_mac(mut self, value: ::heapless::Vec<u8, 32>) -> Self {\n        self.r#mac = value.into();\n        self\n    }\n    /// Return a reference to `aid`\n    #[inline]\n    pub fn r#aid(&self) -> &u32 {\n        &self.r#aid\n    }\n    /// Return a mutable reference to `aid`\n    #[inline]\n    pub fn mut_aid(&mut self) -> &mut u32 {\n        &mut self.r#aid\n    }\n    /// Set the value of `aid`\n    #[inline]\n    pub fn set_aid(&mut self, value: u32) -> &mut Self {\n        self.r#aid = value.into();\n        self\n    }\n    /// Builder method that sets the value of `aid`. Useful for initializing the message.\n    #[inline]\n    pub fn init_aid(mut self, value: u32) -> Self {\n        self.r#aid = value.into();\n        self\n    }\n    /// Return a reference to `is_mesh_child`\n    #[inline]\n    pub fn r#is_mesh_child(&self) -> &bool {\n        &self.r#is_mesh_child\n    }\n    /// Return a mutable reference to `is_mesh_child`\n    #[inline]\n    pub fn mut_is_mesh_child(&mut self) -> &mut bool {\n        &mut self.r#is_mesh_child\n    }\n    /// Set the value of `is_mesh_child`\n    #[inline]\n    pub fn set_is_mesh_child(&mut self, value: bool) -> &mut Self {\n        self.r#is_mesh_child = value.into();\n        self\n    }\n    /// Builder method that sets the value of `is_mesh_child`. Useful for initializing the message.\n    #[inline]\n    pub fn init_is_mesh_child(mut self, value: bool) -> Self {\n        self.r#is_mesh_child = value.into();\n        self\n    }\n}\nimpl ::micropb::MessageDecode for CtrlMsg_Event_StationConnectedToESPSoftAP {\n    fn decode<IMPL_MICROPB_READ: ::micropb::PbRead>(\n        &mut self,\n        decoder: &mut ::micropb::PbDecoder<IMPL_MICROPB_READ>,\n        len: usize,\n    ) -> Result<(), ::micropb::DecodeError<IMPL_MICROPB_READ::Error>> {\n        use ::micropb::{FieldDecode, PbBytes, PbMap, PbString, PbVec};\n        let before = decoder.bytes_read();\n        while decoder.bytes_read() - before < len {\n            let tag = decoder.decode_tag()?;\n            match tag.field_num() {\n                0 => return Err(::micropb::DecodeError::ZeroField),\n                1u32 => {\n                    let mut_ref = &mut self.r#resp;\n                    {\n                        let val = decoder.decode_int32()?;\n                        let val_ref = &val;\n                        if *val_ref != 0 {\n                            *mut_ref = val as _;\n                        }\n                    };\n                }\n                2u32 => {\n                    let mut_ref = &mut self.r#mac;\n                    {\n                        decoder.decode_bytes(mut_ref, ::micropb::Presence::Implicit)?;\n                    };\n                }\n                3u32 => {\n                    let mut_ref = &mut self.r#aid;\n                    {\n                        let val = decoder.decode_varint32()?;\n                        let val_ref = &val;\n                        if *val_ref != 0 {\n                            *mut_ref = val as _;\n                        }\n                    };\n                }\n                4u32 => {\n                    let mut_ref = &mut self.r#is_mesh_child;\n                    {\n                        let val = decoder.decode_bool()?;\n                        let val_ref = &val;\n                        if *val_ref {\n                            *mut_ref = val as _;\n                        }\n                    };\n                }\n                _ => {\n                    decoder.skip_wire_value(tag.wire_type())?;\n                }\n            }\n        }\n        Ok(())\n    }\n}\nimpl ::micropb::MessageEncode for CtrlMsg_Event_StationConnectedToESPSoftAP {\n    const MAX_SIZE: ::core::result::Result<usize, &'static str> = 'msg: {\n        let mut max_size = 0;\n        match ::micropb::const_map!(::core::result::Result::Ok(10usize), |size| size + 1usize) {\n            ::core::result::Result::Ok(size) => {\n                max_size += size;\n            }\n            ::core::result::Result::Err(err) => {\n                break 'msg (::core::result::Result::<usize, _>::Err(err));\n            }\n        }\n        match ::micropb::const_map!(::core::result::Result::Ok(33usize), |size| size + 1usize) {\n            ::core::result::Result::Ok(size) => {\n                max_size += size;\n            }\n            ::core::result::Result::Err(err) => {\n                break 'msg (::core::result::Result::<usize, _>::Err(err));\n            }\n        }\n        match ::micropb::const_map!(::core::result::Result::Ok(5usize), |size| size + 1usize) {\n            ::core::result::Result::Ok(size) => {\n                max_size += size;\n            }\n            ::core::result::Result::Err(err) => {\n                break 'msg (::core::result::Result::<usize, _>::Err(err));\n            }\n        }\n        match ::micropb::const_map!(::core::result::Result::Ok(1usize), |size| size + 1usize) {\n            ::core::result::Result::Ok(size) => {\n                max_size += size;\n            }\n            ::core::result::Result::Err(err) => {\n                break 'msg (::core::result::Result::<usize, _>::Err(err));\n            }\n        }\n        ::core::result::Result::Ok(max_size)\n    };\n    fn encode<IMPL_MICROPB_WRITE: ::micropb::PbWrite>(\n        &self,\n        encoder: &mut ::micropb::PbEncoder<IMPL_MICROPB_WRITE>,\n    ) -> Result<(), IMPL_MICROPB_WRITE::Error> {\n        use ::micropb::{FieldEncode, PbMap};\n        {\n            let val_ref = &self.r#resp;\n            if *val_ref != 0 {\n                encoder.encode_varint32(8u32)?;\n                encoder.encode_int32(*val_ref as _)?;\n            }\n        }\n        {\n            let val_ref = &self.r#mac;\n            if !val_ref.is_empty() {\n                encoder.encode_varint32(18u32)?;\n                encoder.encode_bytes(val_ref)?;\n            }\n        }\n        {\n            let val_ref = &self.r#aid;\n            if *val_ref != 0 {\n                encoder.encode_varint32(24u32)?;\n                encoder.encode_varint32(*val_ref as _)?;\n            }\n        }\n        {\n            let val_ref = &self.r#is_mesh_child;\n            if *val_ref {\n                encoder.encode_varint32(32u32)?;\n                encoder.encode_bool(*val_ref)?;\n            }\n        }\n        Ok(())\n    }\n    fn compute_size(&self) -> usize {\n        use ::micropb::{FieldEncode, PbMap};\n        let mut size = 0;\n        {\n            let val_ref = &self.r#resp;\n            if *val_ref != 0 {\n                size += 1usize + ::micropb::size::sizeof_int32(*val_ref as _);\n            }\n        }\n        {\n            let val_ref = &self.r#mac;\n            if !val_ref.is_empty() {\n                size += 1usize + ::micropb::size::sizeof_len_record(val_ref.len());\n            }\n        }\n        {\n            let val_ref = &self.r#aid;\n            if *val_ref != 0 {\n                size += 1usize + ::micropb::size::sizeof_varint32(*val_ref as _);\n            }\n        }\n        {\n            let val_ref = &self.r#is_mesh_child;\n            if *val_ref {\n                size += 1usize + 1;\n            }\n        }\n        size\n    }\n}\n#[derive(Debug, Default, PartialEq, Clone)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub struct CtrlMsg_Event_SetDhcpDnsStatus {\n    pub r#iface: i32,\n    pub r#net_link_up: i32,\n    pub r#dhcp_up: i32,\n    pub r#dhcp_ip: ::heapless::Vec<u8, 32>,\n    pub r#dhcp_nm: ::heapless::Vec<u8, 32>,\n    pub r#dhcp_gw: ::heapless::Vec<u8, 32>,\n    pub r#dns_up: i32,\n    pub r#dns_ip: ::heapless::Vec<u8, 32>,\n    pub r#dns_type: i32,\n    pub r#resp: i32,\n}\nimpl CtrlMsg_Event_SetDhcpDnsStatus {\n    /// Return a reference to `iface`\n    #[inline]\n    pub fn r#iface(&self) -> &i32 {\n        &self.r#iface\n    }\n    /// Return a mutable reference to `iface`\n    #[inline]\n    pub fn mut_iface(&mut self) -> &mut i32 {\n        &mut self.r#iface\n    }\n    /// Set the value of `iface`\n    #[inline]\n    pub fn set_iface(&mut self, value: i32) -> &mut Self {\n        self.r#iface = value.into();\n        self\n    }\n    /// Builder method that sets the value of `iface`. Useful for initializing the message.\n    #[inline]\n    pub fn init_iface(mut self, value: i32) -> Self {\n        self.r#iface = value.into();\n        self\n    }\n    /// Return a reference to `net_link_up`\n    #[inline]\n    pub fn r#net_link_up(&self) -> &i32 {\n        &self.r#net_link_up\n    }\n    /// Return a mutable reference to `net_link_up`\n    #[inline]\n    pub fn mut_net_link_up(&mut self) -> &mut i32 {\n        &mut self.r#net_link_up\n    }\n    /// Set the value of `net_link_up`\n    #[inline]\n    pub fn set_net_link_up(&mut self, value: i32) -> &mut Self {\n        self.r#net_link_up = value.into();\n        self\n    }\n    /// Builder method that sets the value of `net_link_up`. Useful for initializing the message.\n    #[inline]\n    pub fn init_net_link_up(mut self, value: i32) -> Self {\n        self.r#net_link_up = value.into();\n        self\n    }\n    /// Return a reference to `dhcp_up`\n    #[inline]\n    pub fn r#dhcp_up(&self) -> &i32 {\n        &self.r#dhcp_up\n    }\n    /// Return a mutable reference to `dhcp_up`\n    #[inline]\n    pub fn mut_dhcp_up(&mut self) -> &mut i32 {\n        &mut self.r#dhcp_up\n    }\n    /// Set the value of `dhcp_up`\n    #[inline]\n    pub fn set_dhcp_up(&mut self, value: i32) -> &mut Self {\n        self.r#dhcp_up = value.into();\n        self\n    }\n    /// Builder method that sets the value of `dhcp_up`. Useful for initializing the message.\n    #[inline]\n    pub fn init_dhcp_up(mut self, value: i32) -> Self {\n        self.r#dhcp_up = value.into();\n        self\n    }\n    /// Return a reference to `dhcp_ip`\n    #[inline]\n    pub fn r#dhcp_ip(&self) -> &::heapless::Vec<u8, 32> {\n        &self.r#dhcp_ip\n    }\n    /// Return a mutable reference to `dhcp_ip`\n    #[inline]\n    pub fn mut_dhcp_ip(&mut self) -> &mut ::heapless::Vec<u8, 32> {\n        &mut self.r#dhcp_ip\n    }\n    /// Set the value of `dhcp_ip`\n    #[inline]\n    pub fn set_dhcp_ip(&mut self, value: ::heapless::Vec<u8, 32>) -> &mut Self {\n        self.r#dhcp_ip = value.into();\n        self\n    }\n    /// Builder method that sets the value of `dhcp_ip`. Useful for initializing the message.\n    #[inline]\n    pub fn init_dhcp_ip(mut self, value: ::heapless::Vec<u8, 32>) -> Self {\n        self.r#dhcp_ip = value.into();\n        self\n    }\n    /// Return a reference to `dhcp_nm`\n    #[inline]\n    pub fn r#dhcp_nm(&self) -> &::heapless::Vec<u8, 32> {\n        &self.r#dhcp_nm\n    }\n    /// Return a mutable reference to `dhcp_nm`\n    #[inline]\n    pub fn mut_dhcp_nm(&mut self) -> &mut ::heapless::Vec<u8, 32> {\n        &mut self.r#dhcp_nm\n    }\n    /// Set the value of `dhcp_nm`\n    #[inline]\n    pub fn set_dhcp_nm(&mut self, value: ::heapless::Vec<u8, 32>) -> &mut Self {\n        self.r#dhcp_nm = value.into();\n        self\n    }\n    /// Builder method that sets the value of `dhcp_nm`. Useful for initializing the message.\n    #[inline]\n    pub fn init_dhcp_nm(mut self, value: ::heapless::Vec<u8, 32>) -> Self {\n        self.r#dhcp_nm = value.into();\n        self\n    }\n    /// Return a reference to `dhcp_gw`\n    #[inline]\n    pub fn r#dhcp_gw(&self) -> &::heapless::Vec<u8, 32> {\n        &self.r#dhcp_gw\n    }\n    /// Return a mutable reference to `dhcp_gw`\n    #[inline]\n    pub fn mut_dhcp_gw(&mut self) -> &mut ::heapless::Vec<u8, 32> {\n        &mut self.r#dhcp_gw\n    }\n    /// Set the value of `dhcp_gw`\n    #[inline]\n    pub fn set_dhcp_gw(&mut self, value: ::heapless::Vec<u8, 32>) -> &mut Self {\n        self.r#dhcp_gw = value.into();\n        self\n    }\n    /// Builder method that sets the value of `dhcp_gw`. Useful for initializing the message.\n    #[inline]\n    pub fn init_dhcp_gw(mut self, value: ::heapless::Vec<u8, 32>) -> Self {\n        self.r#dhcp_gw = value.into();\n        self\n    }\n    /// Return a reference to `dns_up`\n    #[inline]\n    pub fn r#dns_up(&self) -> &i32 {\n        &self.r#dns_up\n    }\n    /// Return a mutable reference to `dns_up`\n    #[inline]\n    pub fn mut_dns_up(&mut self) -> &mut i32 {\n        &mut self.r#dns_up\n    }\n    /// Set the value of `dns_up`\n    #[inline]\n    pub fn set_dns_up(&mut self, value: i32) -> &mut Self {\n        self.r#dns_up = value.into();\n        self\n    }\n    /// Builder method that sets the value of `dns_up`. Useful for initializing the message.\n    #[inline]\n    pub fn init_dns_up(mut self, value: i32) -> Self {\n        self.r#dns_up = value.into();\n        self\n    }\n    /// Return a reference to `dns_ip`\n    #[inline]\n    pub fn r#dns_ip(&self) -> &::heapless::Vec<u8, 32> {\n        &self.r#dns_ip\n    }\n    /// Return a mutable reference to `dns_ip`\n    #[inline]\n    pub fn mut_dns_ip(&mut self) -> &mut ::heapless::Vec<u8, 32> {\n        &mut self.r#dns_ip\n    }\n    /// Set the value of `dns_ip`\n    #[inline]\n    pub fn set_dns_ip(&mut self, value: ::heapless::Vec<u8, 32>) -> &mut Self {\n        self.r#dns_ip = value.into();\n        self\n    }\n    /// Builder method that sets the value of `dns_ip`. Useful for initializing the message.\n    #[inline]\n    pub fn init_dns_ip(mut self, value: ::heapless::Vec<u8, 32>) -> Self {\n        self.r#dns_ip = value.into();\n        self\n    }\n    /// Return a reference to `dns_type`\n    #[inline]\n    pub fn r#dns_type(&self) -> &i32 {\n        &self.r#dns_type\n    }\n    /// Return a mutable reference to `dns_type`\n    #[inline]\n    pub fn mut_dns_type(&mut self) -> &mut i32 {\n        &mut self.r#dns_type\n    }\n    /// Set the value of `dns_type`\n    #[inline]\n    pub fn set_dns_type(&mut self, value: i32) -> &mut Self {\n        self.r#dns_type = value.into();\n        self\n    }\n    /// Builder method that sets the value of `dns_type`. Useful for initializing the message.\n    #[inline]\n    pub fn init_dns_type(mut self, value: i32) -> Self {\n        self.r#dns_type = value.into();\n        self\n    }\n    /// Return a reference to `resp`\n    #[inline]\n    pub fn r#resp(&self) -> &i32 {\n        &self.r#resp\n    }\n    /// Return a mutable reference to `resp`\n    #[inline]\n    pub fn mut_resp(&mut self) -> &mut i32 {\n        &mut self.r#resp\n    }\n    /// Set the value of `resp`\n    #[inline]\n    pub fn set_resp(&mut self, value: i32) -> &mut Self {\n        self.r#resp = value.into();\n        self\n    }\n    /// Builder method that sets the value of `resp`. Useful for initializing the message.\n    #[inline]\n    pub fn init_resp(mut self, value: i32) -> Self {\n        self.r#resp = value.into();\n        self\n    }\n}\nimpl ::micropb::MessageDecode for CtrlMsg_Event_SetDhcpDnsStatus {\n    fn decode<IMPL_MICROPB_READ: ::micropb::PbRead>(\n        &mut self,\n        decoder: &mut ::micropb::PbDecoder<IMPL_MICROPB_READ>,\n        len: usize,\n    ) -> Result<(), ::micropb::DecodeError<IMPL_MICROPB_READ::Error>> {\n        use ::micropb::{FieldDecode, PbBytes, PbMap, PbString, PbVec};\n        let before = decoder.bytes_read();\n        while decoder.bytes_read() - before < len {\n            let tag = decoder.decode_tag()?;\n            match tag.field_num() {\n                0 => return Err(::micropb::DecodeError::ZeroField),\n                1u32 => {\n                    let mut_ref = &mut self.r#iface;\n                    {\n                        let val = decoder.decode_int32()?;\n                        let val_ref = &val;\n                        if *val_ref != 0 {\n                            *mut_ref = val as _;\n                        }\n                    };\n                }\n                2u32 => {\n                    let mut_ref = &mut self.r#net_link_up;\n                    {\n                        let val = decoder.decode_int32()?;\n                        let val_ref = &val;\n                        if *val_ref != 0 {\n                            *mut_ref = val as _;\n                        }\n                    };\n                }\n                3u32 => {\n                    let mut_ref = &mut self.r#dhcp_up;\n                    {\n                        let val = decoder.decode_int32()?;\n                        let val_ref = &val;\n                        if *val_ref != 0 {\n                            *mut_ref = val as _;\n                        }\n                    };\n                }\n                4u32 => {\n                    let mut_ref = &mut self.r#dhcp_ip;\n                    {\n                        decoder.decode_bytes(mut_ref, ::micropb::Presence::Implicit)?;\n                    };\n                }\n                5u32 => {\n                    let mut_ref = &mut self.r#dhcp_nm;\n                    {\n                        decoder.decode_bytes(mut_ref, ::micropb::Presence::Implicit)?;\n                    };\n                }\n                6u32 => {\n                    let mut_ref = &mut self.r#dhcp_gw;\n                    {\n                        decoder.decode_bytes(mut_ref, ::micropb::Presence::Implicit)?;\n                    };\n                }\n                7u32 => {\n                    let mut_ref = &mut self.r#dns_up;\n                    {\n                        let val = decoder.decode_int32()?;\n                        let val_ref = &val;\n                        if *val_ref != 0 {\n                            *mut_ref = val as _;\n                        }\n                    };\n                }\n                8u32 => {\n                    let mut_ref = &mut self.r#dns_ip;\n                    {\n                        decoder.decode_bytes(mut_ref, ::micropb::Presence::Implicit)?;\n                    };\n                }\n                9u32 => {\n                    let mut_ref = &mut self.r#dns_type;\n                    {\n                        let val = decoder.decode_int32()?;\n                        let val_ref = &val;\n                        if *val_ref != 0 {\n                            *mut_ref = val as _;\n                        }\n                    };\n                }\n                10u32 => {\n                    let mut_ref = &mut self.r#resp;\n                    {\n                        let val = decoder.decode_int32()?;\n                        let val_ref = &val;\n                        if *val_ref != 0 {\n                            *mut_ref = val as _;\n                        }\n                    };\n                }\n                _ => {\n                    decoder.skip_wire_value(tag.wire_type())?;\n                }\n            }\n        }\n        Ok(())\n    }\n}\nimpl ::micropb::MessageEncode for CtrlMsg_Event_SetDhcpDnsStatus {\n    const MAX_SIZE: ::core::result::Result<usize, &'static str> = 'msg: {\n        let mut max_size = 0;\n        match ::micropb::const_map!(::core::result::Result::Ok(10usize), |size| size + 1usize) {\n            ::core::result::Result::Ok(size) => {\n                max_size += size;\n            }\n            ::core::result::Result::Err(err) => {\n                break 'msg (::core::result::Result::<usize, _>::Err(err));\n            }\n        }\n        match ::micropb::const_map!(::core::result::Result::Ok(10usize), |size| size + 1usize) {\n            ::core::result::Result::Ok(size) => {\n                max_size += size;\n            }\n            ::core::result::Result::Err(err) => {\n                break 'msg (::core::result::Result::<usize, _>::Err(err));\n            }\n        }\n        match ::micropb::const_map!(::core::result::Result::Ok(10usize), |size| size + 1usize) {\n            ::core::result::Result::Ok(size) => {\n                max_size += size;\n            }\n            ::core::result::Result::Err(err) => {\n                break 'msg (::core::result::Result::<usize, _>::Err(err));\n            }\n        }\n        match ::micropb::const_map!(::core::result::Result::Ok(33usize), |size| size + 1usize) {\n            ::core::result::Result::Ok(size) => {\n                max_size += size;\n            }\n            ::core::result::Result::Err(err) => {\n                break 'msg (::core::result::Result::<usize, _>::Err(err));\n            }\n        }\n        match ::micropb::const_map!(::core::result::Result::Ok(33usize), |size| size + 1usize) {\n            ::core::result::Result::Ok(size) => {\n                max_size += size;\n            }\n            ::core::result::Result::Err(err) => {\n                break 'msg (::core::result::Result::<usize, _>::Err(err));\n            }\n        }\n        match ::micropb::const_map!(::core::result::Result::Ok(33usize), |size| size + 1usize) {\n            ::core::result::Result::Ok(size) => {\n                max_size += size;\n            }\n            ::core::result::Result::Err(err) => {\n                break 'msg (::core::result::Result::<usize, _>::Err(err));\n            }\n        }\n        match ::micropb::const_map!(::core::result::Result::Ok(10usize), |size| size + 1usize) {\n            ::core::result::Result::Ok(size) => {\n                max_size += size;\n            }\n            ::core::result::Result::Err(err) => {\n                break 'msg (::core::result::Result::<usize, _>::Err(err));\n            }\n        }\n        match ::micropb::const_map!(::core::result::Result::Ok(33usize), |size| size + 1usize) {\n            ::core::result::Result::Ok(size) => {\n                max_size += size;\n            }\n            ::core::result::Result::Err(err) => {\n                break 'msg (::core::result::Result::<usize, _>::Err(err));\n            }\n        }\n        match ::micropb::const_map!(::core::result::Result::Ok(10usize), |size| size + 1usize) {\n            ::core::result::Result::Ok(size) => {\n                max_size += size;\n            }\n            ::core::result::Result::Err(err) => {\n                break 'msg (::core::result::Result::<usize, _>::Err(err));\n            }\n        }\n        match ::micropb::const_map!(::core::result::Result::Ok(10usize), |size| size + 1usize) {\n            ::core::result::Result::Ok(size) => {\n                max_size += size;\n            }\n            ::core::result::Result::Err(err) => {\n                break 'msg (::core::result::Result::<usize, _>::Err(err));\n            }\n        }\n        ::core::result::Result::Ok(max_size)\n    };\n    fn encode<IMPL_MICROPB_WRITE: ::micropb::PbWrite>(\n        &self,\n        encoder: &mut ::micropb::PbEncoder<IMPL_MICROPB_WRITE>,\n    ) -> Result<(), IMPL_MICROPB_WRITE::Error> {\n        use ::micropb::{FieldEncode, PbMap};\n        {\n            let val_ref = &self.r#iface;\n            if *val_ref != 0 {\n                encoder.encode_varint32(8u32)?;\n                encoder.encode_int32(*val_ref as _)?;\n            }\n        }\n        {\n            let val_ref = &self.r#net_link_up;\n            if *val_ref != 0 {\n                encoder.encode_varint32(16u32)?;\n                encoder.encode_int32(*val_ref as _)?;\n            }\n        }\n        {\n            let val_ref = &self.r#dhcp_up;\n            if *val_ref != 0 {\n                encoder.encode_varint32(24u32)?;\n                encoder.encode_int32(*val_ref as _)?;\n            }\n        }\n        {\n            let val_ref = &self.r#dhcp_ip;\n            if !val_ref.is_empty() {\n                encoder.encode_varint32(34u32)?;\n                encoder.encode_bytes(val_ref)?;\n            }\n        }\n        {\n            let val_ref = &self.r#dhcp_nm;\n            if !val_ref.is_empty() {\n                encoder.encode_varint32(42u32)?;\n                encoder.encode_bytes(val_ref)?;\n            }\n        }\n        {\n            let val_ref = &self.r#dhcp_gw;\n            if !val_ref.is_empty() {\n                encoder.encode_varint32(50u32)?;\n                encoder.encode_bytes(val_ref)?;\n            }\n        }\n        {\n            let val_ref = &self.r#dns_up;\n            if *val_ref != 0 {\n                encoder.encode_varint32(56u32)?;\n                encoder.encode_int32(*val_ref as _)?;\n            }\n        }\n        {\n            let val_ref = &self.r#dns_ip;\n            if !val_ref.is_empty() {\n                encoder.encode_varint32(66u32)?;\n                encoder.encode_bytes(val_ref)?;\n            }\n        }\n        {\n            let val_ref = &self.r#dns_type;\n            if *val_ref != 0 {\n                encoder.encode_varint32(72u32)?;\n                encoder.encode_int32(*val_ref as _)?;\n            }\n        }\n        {\n            let val_ref = &self.r#resp;\n            if *val_ref != 0 {\n                encoder.encode_varint32(80u32)?;\n                encoder.encode_int32(*val_ref as _)?;\n            }\n        }\n        Ok(())\n    }\n    fn compute_size(&self) -> usize {\n        use ::micropb::{FieldEncode, PbMap};\n        let mut size = 0;\n        {\n            let val_ref = &self.r#iface;\n            if *val_ref != 0 {\n                size += 1usize + ::micropb::size::sizeof_int32(*val_ref as _);\n            }\n        }\n        {\n            let val_ref = &self.r#net_link_up;\n            if *val_ref != 0 {\n                size += 1usize + ::micropb::size::sizeof_int32(*val_ref as _);\n            }\n        }\n        {\n            let val_ref = &self.r#dhcp_up;\n            if *val_ref != 0 {\n                size += 1usize + ::micropb::size::sizeof_int32(*val_ref as _);\n            }\n        }\n        {\n            let val_ref = &self.r#dhcp_ip;\n            if !val_ref.is_empty() {\n                size += 1usize + ::micropb::size::sizeof_len_record(val_ref.len());\n            }\n        }\n        {\n            let val_ref = &self.r#dhcp_nm;\n            if !val_ref.is_empty() {\n                size += 1usize + ::micropb::size::sizeof_len_record(val_ref.len());\n            }\n        }\n        {\n            let val_ref = &self.r#dhcp_gw;\n            if !val_ref.is_empty() {\n                size += 1usize + ::micropb::size::sizeof_len_record(val_ref.len());\n            }\n        }\n        {\n            let val_ref = &self.r#dns_up;\n            if *val_ref != 0 {\n                size += 1usize + ::micropb::size::sizeof_int32(*val_ref as _);\n            }\n        }\n        {\n            let val_ref = &self.r#dns_ip;\n            if !val_ref.is_empty() {\n                size += 1usize + ::micropb::size::sizeof_len_record(val_ref.len());\n            }\n        }\n        {\n            let val_ref = &self.r#dns_type;\n            if *val_ref != 0 {\n                size += 1usize + ::micropb::size::sizeof_int32(*val_ref as _);\n            }\n        }\n        {\n            let val_ref = &self.r#resp;\n            if *val_ref != 0 {\n                size += 1usize + ::micropb::size::sizeof_int32(*val_ref as _);\n            }\n        }\n        size\n    }\n}\n/// Add Custom RPC message structures after existing message structures to make it easily notice\n#[derive(Debug, Default, PartialEq, Clone)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub struct CtrlMsg_Req_CustomRpcUnserialisedMsg {\n    pub r#custom_msg_id: u32,\n    pub r#data: ::heapless::Vec<u8, 32>,\n}\nimpl CtrlMsg_Req_CustomRpcUnserialisedMsg {\n    /// Return a reference to `custom_msg_id`\n    #[inline]\n    pub fn r#custom_msg_id(&self) -> &u32 {\n        &self.r#custom_msg_id\n    }\n    /// Return a mutable reference to `custom_msg_id`\n    #[inline]\n    pub fn mut_custom_msg_id(&mut self) -> &mut u32 {\n        &mut self.r#custom_msg_id\n    }\n    /// Set the value of `custom_msg_id`\n    #[inline]\n    pub fn set_custom_msg_id(&mut self, value: u32) -> &mut Self {\n        self.r#custom_msg_id = value.into();\n        self\n    }\n    /// Builder method that sets the value of `custom_msg_id`. Useful for initializing the message.\n    #[inline]\n    pub fn init_custom_msg_id(mut self, value: u32) -> Self {\n        self.r#custom_msg_id = value.into();\n        self\n    }\n    /// Return a reference to `data`\n    #[inline]\n    pub fn r#data(&self) -> &::heapless::Vec<u8, 32> {\n        &self.r#data\n    }\n    /// Return a mutable reference to `data`\n    #[inline]\n    pub fn mut_data(&mut self) -> &mut ::heapless::Vec<u8, 32> {\n        &mut self.r#data\n    }\n    /// Set the value of `data`\n    #[inline]\n    pub fn set_data(&mut self, value: ::heapless::Vec<u8, 32>) -> &mut Self {\n        self.r#data = value.into();\n        self\n    }\n    /// Builder method that sets the value of `data`. Useful for initializing the message.\n    #[inline]\n    pub fn init_data(mut self, value: ::heapless::Vec<u8, 32>) -> Self {\n        self.r#data = value.into();\n        self\n    }\n}\nimpl ::micropb::MessageDecode for CtrlMsg_Req_CustomRpcUnserialisedMsg {\n    fn decode<IMPL_MICROPB_READ: ::micropb::PbRead>(\n        &mut self,\n        decoder: &mut ::micropb::PbDecoder<IMPL_MICROPB_READ>,\n        len: usize,\n    ) -> Result<(), ::micropb::DecodeError<IMPL_MICROPB_READ::Error>> {\n        use ::micropb::{FieldDecode, PbBytes, PbMap, PbString, PbVec};\n        let before = decoder.bytes_read();\n        while decoder.bytes_read() - before < len {\n            let tag = decoder.decode_tag()?;\n            match tag.field_num() {\n                0 => return Err(::micropb::DecodeError::ZeroField),\n                1u32 => {\n                    let mut_ref = &mut self.r#custom_msg_id;\n                    {\n                        let val = decoder.decode_varint32()?;\n                        let val_ref = &val;\n                        if *val_ref != 0 {\n                            *mut_ref = val as _;\n                        }\n                    };\n                }\n                2u32 => {\n                    let mut_ref = &mut self.r#data;\n                    {\n                        decoder.decode_bytes(mut_ref, ::micropb::Presence::Implicit)?;\n                    };\n                }\n                _ => {\n                    decoder.skip_wire_value(tag.wire_type())?;\n                }\n            }\n        }\n        Ok(())\n    }\n}\nimpl ::micropb::MessageEncode for CtrlMsg_Req_CustomRpcUnserialisedMsg {\n    const MAX_SIZE: ::core::result::Result<usize, &'static str> = 'msg: {\n        let mut max_size = 0;\n        match ::micropb::const_map!(::core::result::Result::Ok(5usize), |size| size + 1usize) {\n            ::core::result::Result::Ok(size) => {\n                max_size += size;\n            }\n            ::core::result::Result::Err(err) => {\n                break 'msg (::core::result::Result::<usize, _>::Err(err));\n            }\n        }\n        match ::micropb::const_map!(::core::result::Result::Ok(33usize), |size| size + 1usize) {\n            ::core::result::Result::Ok(size) => {\n                max_size += size;\n            }\n            ::core::result::Result::Err(err) => {\n                break 'msg (::core::result::Result::<usize, _>::Err(err));\n            }\n        }\n        ::core::result::Result::Ok(max_size)\n    };\n    fn encode<IMPL_MICROPB_WRITE: ::micropb::PbWrite>(\n        &self,\n        encoder: &mut ::micropb::PbEncoder<IMPL_MICROPB_WRITE>,\n    ) -> Result<(), IMPL_MICROPB_WRITE::Error> {\n        use ::micropb::{FieldEncode, PbMap};\n        {\n            let val_ref = &self.r#custom_msg_id;\n            if *val_ref != 0 {\n                encoder.encode_varint32(8u32)?;\n                encoder.encode_varint32(*val_ref as _)?;\n            }\n        }\n        {\n            let val_ref = &self.r#data;\n            if !val_ref.is_empty() {\n                encoder.encode_varint32(18u32)?;\n                encoder.encode_bytes(val_ref)?;\n            }\n        }\n        Ok(())\n    }\n    fn compute_size(&self) -> usize {\n        use ::micropb::{FieldEncode, PbMap};\n        let mut size = 0;\n        {\n            let val_ref = &self.r#custom_msg_id;\n            if *val_ref != 0 {\n                size += 1usize + ::micropb::size::sizeof_varint32(*val_ref as _);\n            }\n        }\n        {\n            let val_ref = &self.r#data;\n            if !val_ref.is_empty() {\n                size += 1usize + ::micropb::size::sizeof_len_record(val_ref.len());\n            }\n        }\n        size\n    }\n}\n#[derive(Debug, Default, PartialEq, Clone)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub struct CtrlMsg_Resp_CustomRpcUnserialisedMsg {\n    pub r#resp: i32,\n    pub r#custom_msg_id: u32,\n    pub r#data: ::heapless::Vec<u8, 32>,\n}\nimpl CtrlMsg_Resp_CustomRpcUnserialisedMsg {\n    /// Return a reference to `resp`\n    #[inline]\n    pub fn r#resp(&self) -> &i32 {\n        &self.r#resp\n    }\n    /// Return a mutable reference to `resp`\n    #[inline]\n    pub fn mut_resp(&mut self) -> &mut i32 {\n        &mut self.r#resp\n    }\n    /// Set the value of `resp`\n    #[inline]\n    pub fn set_resp(&mut self, value: i32) -> &mut Self {\n        self.r#resp = value.into();\n        self\n    }\n    /// Builder method that sets the value of `resp`. Useful for initializing the message.\n    #[inline]\n    pub fn init_resp(mut self, value: i32) -> Self {\n        self.r#resp = value.into();\n        self\n    }\n    /// Return a reference to `custom_msg_id`\n    #[inline]\n    pub fn r#custom_msg_id(&self) -> &u32 {\n        &self.r#custom_msg_id\n    }\n    /// Return a mutable reference to `custom_msg_id`\n    #[inline]\n    pub fn mut_custom_msg_id(&mut self) -> &mut u32 {\n        &mut self.r#custom_msg_id\n    }\n    /// Set the value of `custom_msg_id`\n    #[inline]\n    pub fn set_custom_msg_id(&mut self, value: u32) -> &mut Self {\n        self.r#custom_msg_id = value.into();\n        self\n    }\n    /// Builder method that sets the value of `custom_msg_id`. Useful for initializing the message.\n    #[inline]\n    pub fn init_custom_msg_id(mut self, value: u32) -> Self {\n        self.r#custom_msg_id = value.into();\n        self\n    }\n    /// Return a reference to `data`\n    #[inline]\n    pub fn r#data(&self) -> &::heapless::Vec<u8, 32> {\n        &self.r#data\n    }\n    /// Return a mutable reference to `data`\n    #[inline]\n    pub fn mut_data(&mut self) -> &mut ::heapless::Vec<u8, 32> {\n        &mut self.r#data\n    }\n    /// Set the value of `data`\n    #[inline]\n    pub fn set_data(&mut self, value: ::heapless::Vec<u8, 32>) -> &mut Self {\n        self.r#data = value.into();\n        self\n    }\n    /// Builder method that sets the value of `data`. Useful for initializing the message.\n    #[inline]\n    pub fn init_data(mut self, value: ::heapless::Vec<u8, 32>) -> Self {\n        self.r#data = value.into();\n        self\n    }\n}\nimpl ::micropb::MessageDecode for CtrlMsg_Resp_CustomRpcUnserialisedMsg {\n    fn decode<IMPL_MICROPB_READ: ::micropb::PbRead>(\n        &mut self,\n        decoder: &mut ::micropb::PbDecoder<IMPL_MICROPB_READ>,\n        len: usize,\n    ) -> Result<(), ::micropb::DecodeError<IMPL_MICROPB_READ::Error>> {\n        use ::micropb::{FieldDecode, PbBytes, PbMap, PbString, PbVec};\n        let before = decoder.bytes_read();\n        while decoder.bytes_read() - before < len {\n            let tag = decoder.decode_tag()?;\n            match tag.field_num() {\n                0 => return Err(::micropb::DecodeError::ZeroField),\n                1u32 => {\n                    let mut_ref = &mut self.r#resp;\n                    {\n                        let val = decoder.decode_int32()?;\n                        let val_ref = &val;\n                        if *val_ref != 0 {\n                            *mut_ref = val as _;\n                        }\n                    };\n                }\n                2u32 => {\n                    let mut_ref = &mut self.r#custom_msg_id;\n                    {\n                        let val = decoder.decode_varint32()?;\n                        let val_ref = &val;\n                        if *val_ref != 0 {\n                            *mut_ref = val as _;\n                        }\n                    };\n                }\n                3u32 => {\n                    let mut_ref = &mut self.r#data;\n                    {\n                        decoder.decode_bytes(mut_ref, ::micropb::Presence::Implicit)?;\n                    };\n                }\n                _ => {\n                    decoder.skip_wire_value(tag.wire_type())?;\n                }\n            }\n        }\n        Ok(())\n    }\n}\nimpl ::micropb::MessageEncode for CtrlMsg_Resp_CustomRpcUnserialisedMsg {\n    const MAX_SIZE: ::core::result::Result<usize, &'static str> = 'msg: {\n        let mut max_size = 0;\n        match ::micropb::const_map!(::core::result::Result::Ok(10usize), |size| size + 1usize) {\n            ::core::result::Result::Ok(size) => {\n                max_size += size;\n            }\n            ::core::result::Result::Err(err) => {\n                break 'msg (::core::result::Result::<usize, _>::Err(err));\n            }\n        }\n        match ::micropb::const_map!(::core::result::Result::Ok(5usize), |size| size + 1usize) {\n            ::core::result::Result::Ok(size) => {\n                max_size += size;\n            }\n            ::core::result::Result::Err(err) => {\n                break 'msg (::core::result::Result::<usize, _>::Err(err));\n            }\n        }\n        match ::micropb::const_map!(::core::result::Result::Ok(33usize), |size| size + 1usize) {\n            ::core::result::Result::Ok(size) => {\n                max_size += size;\n            }\n            ::core::result::Result::Err(err) => {\n                break 'msg (::core::result::Result::<usize, _>::Err(err));\n            }\n        }\n        ::core::result::Result::Ok(max_size)\n    };\n    fn encode<IMPL_MICROPB_WRITE: ::micropb::PbWrite>(\n        &self,\n        encoder: &mut ::micropb::PbEncoder<IMPL_MICROPB_WRITE>,\n    ) -> Result<(), IMPL_MICROPB_WRITE::Error> {\n        use ::micropb::{FieldEncode, PbMap};\n        {\n            let val_ref = &self.r#resp;\n            if *val_ref != 0 {\n                encoder.encode_varint32(8u32)?;\n                encoder.encode_int32(*val_ref as _)?;\n            }\n        }\n        {\n            let val_ref = &self.r#custom_msg_id;\n            if *val_ref != 0 {\n                encoder.encode_varint32(16u32)?;\n                encoder.encode_varint32(*val_ref as _)?;\n            }\n        }\n        {\n            let val_ref = &self.r#data;\n            if !val_ref.is_empty() {\n                encoder.encode_varint32(26u32)?;\n                encoder.encode_bytes(val_ref)?;\n            }\n        }\n        Ok(())\n    }\n    fn compute_size(&self) -> usize {\n        use ::micropb::{FieldEncode, PbMap};\n        let mut size = 0;\n        {\n            let val_ref = &self.r#resp;\n            if *val_ref != 0 {\n                size += 1usize + ::micropb::size::sizeof_int32(*val_ref as _);\n            }\n        }\n        {\n            let val_ref = &self.r#custom_msg_id;\n            if *val_ref != 0 {\n                size += 1usize + ::micropb::size::sizeof_varint32(*val_ref as _);\n            }\n        }\n        {\n            let val_ref = &self.r#data;\n            if !val_ref.is_empty() {\n                size += 1usize + ::micropb::size::sizeof_len_record(val_ref.len());\n            }\n        }\n        size\n    }\n}\n#[derive(Debug, Default, PartialEq, Clone)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub struct CtrlMsg_Event_CustomRpcUnserialisedMsg {\n    pub r#resp: i32,\n    pub r#custom_evt_id: u32,\n    pub r#data: ::heapless::Vec<u8, 32>,\n}\nimpl CtrlMsg_Event_CustomRpcUnserialisedMsg {\n    /// Return a reference to `resp`\n    #[inline]\n    pub fn r#resp(&self) -> &i32 {\n        &self.r#resp\n    }\n    /// Return a mutable reference to `resp`\n    #[inline]\n    pub fn mut_resp(&mut self) -> &mut i32 {\n        &mut self.r#resp\n    }\n    /// Set the value of `resp`\n    #[inline]\n    pub fn set_resp(&mut self, value: i32) -> &mut Self {\n        self.r#resp = value.into();\n        self\n    }\n    /// Builder method that sets the value of `resp`. Useful for initializing the message.\n    #[inline]\n    pub fn init_resp(mut self, value: i32) -> Self {\n        self.r#resp = value.into();\n        self\n    }\n    /// Return a reference to `custom_evt_id`\n    #[inline]\n    pub fn r#custom_evt_id(&self) -> &u32 {\n        &self.r#custom_evt_id\n    }\n    /// Return a mutable reference to `custom_evt_id`\n    #[inline]\n    pub fn mut_custom_evt_id(&mut self) -> &mut u32 {\n        &mut self.r#custom_evt_id\n    }\n    /// Set the value of `custom_evt_id`\n    #[inline]\n    pub fn set_custom_evt_id(&mut self, value: u32) -> &mut Self {\n        self.r#custom_evt_id = value.into();\n        self\n    }\n    /// Builder method that sets the value of `custom_evt_id`. Useful for initializing the message.\n    #[inline]\n    pub fn init_custom_evt_id(mut self, value: u32) -> Self {\n        self.r#custom_evt_id = value.into();\n        self\n    }\n    /// Return a reference to `data`\n    #[inline]\n    pub fn r#data(&self) -> &::heapless::Vec<u8, 32> {\n        &self.r#data\n    }\n    /// Return a mutable reference to `data`\n    #[inline]\n    pub fn mut_data(&mut self) -> &mut ::heapless::Vec<u8, 32> {\n        &mut self.r#data\n    }\n    /// Set the value of `data`\n    #[inline]\n    pub fn set_data(&mut self, value: ::heapless::Vec<u8, 32>) -> &mut Self {\n        self.r#data = value.into();\n        self\n    }\n    /// Builder method that sets the value of `data`. Useful for initializing the message.\n    #[inline]\n    pub fn init_data(mut self, value: ::heapless::Vec<u8, 32>) -> Self {\n        self.r#data = value.into();\n        self\n    }\n}\nimpl ::micropb::MessageDecode for CtrlMsg_Event_CustomRpcUnserialisedMsg {\n    fn decode<IMPL_MICROPB_READ: ::micropb::PbRead>(\n        &mut self,\n        decoder: &mut ::micropb::PbDecoder<IMPL_MICROPB_READ>,\n        len: usize,\n    ) -> Result<(), ::micropb::DecodeError<IMPL_MICROPB_READ::Error>> {\n        use ::micropb::{FieldDecode, PbBytes, PbMap, PbString, PbVec};\n        let before = decoder.bytes_read();\n        while decoder.bytes_read() - before < len {\n            let tag = decoder.decode_tag()?;\n            match tag.field_num() {\n                0 => return Err(::micropb::DecodeError::ZeroField),\n                1u32 => {\n                    let mut_ref = &mut self.r#resp;\n                    {\n                        let val = decoder.decode_int32()?;\n                        let val_ref = &val;\n                        if *val_ref != 0 {\n                            *mut_ref = val as _;\n                        }\n                    };\n                }\n                2u32 => {\n                    let mut_ref = &mut self.r#custom_evt_id;\n                    {\n                        let val = decoder.decode_varint32()?;\n                        let val_ref = &val;\n                        if *val_ref != 0 {\n                            *mut_ref = val as _;\n                        }\n                    };\n                }\n                3u32 => {\n                    let mut_ref = &mut self.r#data;\n                    {\n                        decoder.decode_bytes(mut_ref, ::micropb::Presence::Implicit)?;\n                    };\n                }\n                _ => {\n                    decoder.skip_wire_value(tag.wire_type())?;\n                }\n            }\n        }\n        Ok(())\n    }\n}\nimpl ::micropb::MessageEncode for CtrlMsg_Event_CustomRpcUnserialisedMsg {\n    const MAX_SIZE: ::core::result::Result<usize, &'static str> = 'msg: {\n        let mut max_size = 0;\n        match ::micropb::const_map!(::core::result::Result::Ok(10usize), |size| size + 1usize) {\n            ::core::result::Result::Ok(size) => {\n                max_size += size;\n            }\n            ::core::result::Result::Err(err) => {\n                break 'msg (::core::result::Result::<usize, _>::Err(err));\n            }\n        }\n        match ::micropb::const_map!(::core::result::Result::Ok(5usize), |size| size + 1usize) {\n            ::core::result::Result::Ok(size) => {\n                max_size += size;\n            }\n            ::core::result::Result::Err(err) => {\n                break 'msg (::core::result::Result::<usize, _>::Err(err));\n            }\n        }\n        match ::micropb::const_map!(::core::result::Result::Ok(33usize), |size| size + 1usize) {\n            ::core::result::Result::Ok(size) => {\n                max_size += size;\n            }\n            ::core::result::Result::Err(err) => {\n                break 'msg (::core::result::Result::<usize, _>::Err(err));\n            }\n        }\n        ::core::result::Result::Ok(max_size)\n    };\n    fn encode<IMPL_MICROPB_WRITE: ::micropb::PbWrite>(\n        &self,\n        encoder: &mut ::micropb::PbEncoder<IMPL_MICROPB_WRITE>,\n    ) -> Result<(), IMPL_MICROPB_WRITE::Error> {\n        use ::micropb::{FieldEncode, PbMap};\n        {\n            let val_ref = &self.r#resp;\n            if *val_ref != 0 {\n                encoder.encode_varint32(8u32)?;\n                encoder.encode_int32(*val_ref as _)?;\n            }\n        }\n        {\n            let val_ref = &self.r#custom_evt_id;\n            if *val_ref != 0 {\n                encoder.encode_varint32(16u32)?;\n                encoder.encode_varint32(*val_ref as _)?;\n            }\n        }\n        {\n            let val_ref = &self.r#data;\n            if !val_ref.is_empty() {\n                encoder.encode_varint32(26u32)?;\n                encoder.encode_bytes(val_ref)?;\n            }\n        }\n        Ok(())\n    }\n    fn compute_size(&self) -> usize {\n        use ::micropb::{FieldEncode, PbMap};\n        let mut size = 0;\n        {\n            let val_ref = &self.r#resp;\n            if *val_ref != 0 {\n                size += 1usize + ::micropb::size::sizeof_int32(*val_ref as _);\n            }\n        }\n        {\n            let val_ref = &self.r#custom_evt_id;\n            if *val_ref != 0 {\n                size += 1usize + ::micropb::size::sizeof_varint32(*val_ref as _);\n            }\n        }\n        {\n            let val_ref = &self.r#data;\n            if !val_ref.is_empty() {\n                size += 1usize + ::micropb::size::sizeof_len_record(val_ref.len());\n            }\n        }\n        size\n    }\n}\n#[derive(Debug, Default, PartialEq, Clone)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub struct CtrlMsg {\n    /// msg_type could be req, resp or Event\n    pub r#msg_type: CtrlMsgType,\n    /// msg id\n    pub r#msg_id: CtrlMsgId,\n    /// UID of message\n    pub r#uid: i32,\n    /// Request/response type: sync or async\n    pub r#req_resp_type: u32,\n    /// union of all msg ids\n    pub r#payload: ::core::option::Option<CtrlMsg_::Payload>,\n}\nimpl CtrlMsg {\n    /// Return a reference to `msg_type`\n    #[inline]\n    pub fn r#msg_type(&self) -> &CtrlMsgType {\n        &self.r#msg_type\n    }\n    /// Return a mutable reference to `msg_type`\n    #[inline]\n    pub fn mut_msg_type(&mut self) -> &mut CtrlMsgType {\n        &mut self.r#msg_type\n    }\n    /// Set the value of `msg_type`\n    #[inline]\n    pub fn set_msg_type(&mut self, value: CtrlMsgType) -> &mut Self {\n        self.r#msg_type = value.into();\n        self\n    }\n    /// Builder method that sets the value of `msg_type`. Useful for initializing the message.\n    #[inline]\n    pub fn init_msg_type(mut self, value: CtrlMsgType) -> Self {\n        self.r#msg_type = value.into();\n        self\n    }\n    /// Return a reference to `msg_id`\n    #[inline]\n    pub fn r#msg_id(&self) -> &CtrlMsgId {\n        &self.r#msg_id\n    }\n    /// Return a mutable reference to `msg_id`\n    #[inline]\n    pub fn mut_msg_id(&mut self) -> &mut CtrlMsgId {\n        &mut self.r#msg_id\n    }\n    /// Set the value of `msg_id`\n    #[inline]\n    pub fn set_msg_id(&mut self, value: CtrlMsgId) -> &mut Self {\n        self.r#msg_id = value.into();\n        self\n    }\n    /// Builder method that sets the value of `msg_id`. Useful for initializing the message.\n    #[inline]\n    pub fn init_msg_id(mut self, value: CtrlMsgId) -> Self {\n        self.r#msg_id = value.into();\n        self\n    }\n    /// Return a reference to `uid`\n    #[inline]\n    pub fn r#uid(&self) -> &i32 {\n        &self.r#uid\n    }\n    /// Return a mutable reference to `uid`\n    #[inline]\n    pub fn mut_uid(&mut self) -> &mut i32 {\n        &mut self.r#uid\n    }\n    /// Set the value of `uid`\n    #[inline]\n    pub fn set_uid(&mut self, value: i32) -> &mut Self {\n        self.r#uid = value.into();\n        self\n    }\n    /// Builder method that sets the value of `uid`. Useful for initializing the message.\n    #[inline]\n    pub fn init_uid(mut self, value: i32) -> Self {\n        self.r#uid = value.into();\n        self\n    }\n    /// Return a reference to `req_resp_type`\n    #[inline]\n    pub fn r#req_resp_type(&self) -> &u32 {\n        &self.r#req_resp_type\n    }\n    /// Return a mutable reference to `req_resp_type`\n    #[inline]\n    pub fn mut_req_resp_type(&mut self) -> &mut u32 {\n        &mut self.r#req_resp_type\n    }\n    /// Set the value of `req_resp_type`\n    #[inline]\n    pub fn set_req_resp_type(&mut self, value: u32) -> &mut Self {\n        self.r#req_resp_type = value.into();\n        self\n    }\n    /// Builder method that sets the value of `req_resp_type`. Useful for initializing the message.\n    #[inline]\n    pub fn init_req_resp_type(mut self, value: u32) -> Self {\n        self.r#req_resp_type = value.into();\n        self\n    }\n}\nimpl ::micropb::MessageDecode for CtrlMsg {\n    fn decode<IMPL_MICROPB_READ: ::micropb::PbRead>(\n        &mut self,\n        decoder: &mut ::micropb::PbDecoder<IMPL_MICROPB_READ>,\n        len: usize,\n    ) -> Result<(), ::micropb::DecodeError<IMPL_MICROPB_READ::Error>> {\n        use ::micropb::{FieldDecode, PbBytes, PbMap, PbString, PbVec};\n        let before = decoder.bytes_read();\n        while decoder.bytes_read() - before < len {\n            let tag = decoder.decode_tag()?;\n            match tag.field_num() {\n                0 => return Err(::micropb::DecodeError::ZeroField),\n                1u32 => {\n                    let mut_ref = &mut self.r#msg_type;\n                    {\n                        let val = decoder.decode_int32().map(|n| CtrlMsgType(n as _))?;\n                        let val_ref = &val;\n                        if val_ref.0 != 0 {\n                            *mut_ref = val as _;\n                        }\n                    };\n                }\n                2u32 => {\n                    let mut_ref = &mut self.r#msg_id;\n                    {\n                        let val = decoder.decode_int32().map(|n| CtrlMsgId(n as _))?;\n                        let val_ref = &val;\n                        if val_ref.0 != 0 {\n                            *mut_ref = val as _;\n                        }\n                    };\n                }\n                3u32 => {\n                    let mut_ref = &mut self.r#uid;\n                    {\n                        let val = decoder.decode_int32()?;\n                        let val_ref = &val;\n                        if *val_ref != 0 {\n                            *mut_ref = val as _;\n                        }\n                    };\n                }\n                4u32 => {\n                    let mut_ref = &mut self.r#req_resp_type;\n                    {\n                        let val = decoder.decode_varint32()?;\n                        let val_ref = &val;\n                        if *val_ref != 0 {\n                            *mut_ref = val as _;\n                        }\n                    };\n                }\n                101u32 => {\n                    let mut_ref = loop {\n                        if let ::core::option::Option::Some(variant) = &mut self.r#payload {\n                            if let CtrlMsg_::Payload::ReqGetMacAddress(variant) = &mut *variant {\n                                break &mut *variant;\n                            }\n                        }\n                        self.r#payload = ::core::option::Option::Some(CtrlMsg_::Payload::ReqGetMacAddress(\n                            ::core::default::Default::default(),\n                        ));\n                    };\n                    mut_ref.decode_len_delimited(decoder)?;\n                }\n                102u32 => {\n                    let mut_ref = loop {\n                        if let ::core::option::Option::Some(variant) = &mut self.r#payload {\n                            if let CtrlMsg_::Payload::ReqSetMacAddress(variant) = &mut *variant {\n                                break &mut *variant;\n                            }\n                        }\n                        self.r#payload = ::core::option::Option::Some(CtrlMsg_::Payload::ReqSetMacAddress(\n                            ::core::default::Default::default(),\n                        ));\n                    };\n                    mut_ref.decode_len_delimited(decoder)?;\n                }\n                103u32 => {\n                    let mut_ref = loop {\n                        if let ::core::option::Option::Some(variant) = &mut self.r#payload {\n                            if let CtrlMsg_::Payload::ReqGetWifiMode(variant) = &mut *variant {\n                                break &mut *variant;\n                            }\n                        }\n                        self.r#payload = ::core::option::Option::Some(CtrlMsg_::Payload::ReqGetWifiMode(\n                            ::core::default::Default::default(),\n                        ));\n                    };\n                    mut_ref.decode_len_delimited(decoder)?;\n                }\n                104u32 => {\n                    let mut_ref = loop {\n                        if let ::core::option::Option::Some(variant) = &mut self.r#payload {\n                            if let CtrlMsg_::Payload::ReqSetWifiMode(variant) = &mut *variant {\n                                break &mut *variant;\n                            }\n                        }\n                        self.r#payload = ::core::option::Option::Some(CtrlMsg_::Payload::ReqSetWifiMode(\n                            ::core::default::Default::default(),\n                        ));\n                    };\n                    mut_ref.decode_len_delimited(decoder)?;\n                }\n                105u32 => {\n                    let mut_ref = loop {\n                        if let ::core::option::Option::Some(variant) = &mut self.r#payload {\n                            if let CtrlMsg_::Payload::ReqScanApList(variant) = &mut *variant {\n                                break &mut *variant;\n                            }\n                        }\n                        self.r#payload = ::core::option::Option::Some(CtrlMsg_::Payload::ReqScanApList(\n                            ::core::default::Default::default(),\n                        ));\n                    };\n                    mut_ref.decode_len_delimited(decoder)?;\n                }\n                106u32 => {\n                    let mut_ref = loop {\n                        if let ::core::option::Option::Some(variant) = &mut self.r#payload {\n                            if let CtrlMsg_::Payload::ReqGetApConfig(variant) = &mut *variant {\n                                break &mut *variant;\n                            }\n                        }\n                        self.r#payload = ::core::option::Option::Some(CtrlMsg_::Payload::ReqGetApConfig(\n                            ::core::default::Default::default(),\n                        ));\n                    };\n                    mut_ref.decode_len_delimited(decoder)?;\n                }\n                107u32 => {\n                    let mut_ref = loop {\n                        if let ::core::option::Option::Some(variant) = &mut self.r#payload {\n                            if let CtrlMsg_::Payload::ReqConnectAp(variant) = &mut *variant {\n                                break &mut *variant;\n                            }\n                        }\n                        self.r#payload = ::core::option::Option::Some(CtrlMsg_::Payload::ReqConnectAp(\n                            ::core::default::Default::default(),\n                        ));\n                    };\n                    mut_ref.decode_len_delimited(decoder)?;\n                }\n                108u32 => {\n                    let mut_ref = loop {\n                        if let ::core::option::Option::Some(variant) = &mut self.r#payload {\n                            if let CtrlMsg_::Payload::ReqDisconnectAp(variant) = &mut *variant {\n                                break &mut *variant;\n                            }\n                        }\n                        self.r#payload = ::core::option::Option::Some(CtrlMsg_::Payload::ReqDisconnectAp(\n                            ::core::default::Default::default(),\n                        ));\n                    };\n                    mut_ref.decode_len_delimited(decoder)?;\n                }\n                109u32 => {\n                    let mut_ref = loop {\n                        if let ::core::option::Option::Some(variant) = &mut self.r#payload {\n                            if let CtrlMsg_::Payload::ReqGetSoftapConfig(variant) = &mut *variant {\n                                break &mut *variant;\n                            }\n                        }\n                        self.r#payload = ::core::option::Option::Some(CtrlMsg_::Payload::ReqGetSoftapConfig(\n                            ::core::default::Default::default(),\n                        ));\n                    };\n                    mut_ref.decode_len_delimited(decoder)?;\n                }\n                110u32 => {\n                    let mut_ref = loop {\n                        if let ::core::option::Option::Some(variant) = &mut self.r#payload {\n                            if let CtrlMsg_::Payload::ReqSetSoftapVendorSpecificIe(variant) = &mut *variant {\n                                break &mut *variant;\n                            }\n                        }\n                        self.r#payload = ::core::option::Option::Some(CtrlMsg_::Payload::ReqSetSoftapVendorSpecificIe(\n                            ::core::default::Default::default(),\n                        ));\n                    };\n                    mut_ref.decode_len_delimited(decoder)?;\n                }\n                111u32 => {\n                    let mut_ref = loop {\n                        if let ::core::option::Option::Some(variant) = &mut self.r#payload {\n                            if let CtrlMsg_::Payload::ReqStartSoftap(variant) = &mut *variant {\n                                break &mut *variant;\n                            }\n                        }\n                        self.r#payload = ::core::option::Option::Some(CtrlMsg_::Payload::ReqStartSoftap(\n                            ::core::default::Default::default(),\n                        ));\n                    };\n                    mut_ref.decode_len_delimited(decoder)?;\n                }\n                112u32 => {\n                    let mut_ref = loop {\n                        if let ::core::option::Option::Some(variant) = &mut self.r#payload {\n                            if let CtrlMsg_::Payload::ReqSoftapConnectedStasList(variant) = &mut *variant {\n                                break &mut *variant;\n                            }\n                        }\n                        self.r#payload = ::core::option::Option::Some(CtrlMsg_::Payload::ReqSoftapConnectedStasList(\n                            ::core::default::Default::default(),\n                        ));\n                    };\n                    mut_ref.decode_len_delimited(decoder)?;\n                }\n                113u32 => {\n                    let mut_ref = loop {\n                        if let ::core::option::Option::Some(variant) = &mut self.r#payload {\n                            if let CtrlMsg_::Payload::ReqStopSoftap(variant) = &mut *variant {\n                                break &mut *variant;\n                            }\n                        }\n                        self.r#payload = ::core::option::Option::Some(CtrlMsg_::Payload::ReqStopSoftap(\n                            ::core::default::Default::default(),\n                        ));\n                    };\n                    mut_ref.decode_len_delimited(decoder)?;\n                }\n                114u32 => {\n                    let mut_ref = loop {\n                        if let ::core::option::Option::Some(variant) = &mut self.r#payload {\n                            if let CtrlMsg_::Payload::ReqSetPowerSaveMode(variant) = &mut *variant {\n                                break &mut *variant;\n                            }\n                        }\n                        self.r#payload = ::core::option::Option::Some(CtrlMsg_::Payload::ReqSetPowerSaveMode(\n                            ::core::default::Default::default(),\n                        ));\n                    };\n                    mut_ref.decode_len_delimited(decoder)?;\n                }\n                115u32 => {\n                    let mut_ref = loop {\n                        if let ::core::option::Option::Some(variant) = &mut self.r#payload {\n                            if let CtrlMsg_::Payload::ReqGetPowerSaveMode(variant) = &mut *variant {\n                                break &mut *variant;\n                            }\n                        }\n                        self.r#payload = ::core::option::Option::Some(CtrlMsg_::Payload::ReqGetPowerSaveMode(\n                            ::core::default::Default::default(),\n                        ));\n                    };\n                    mut_ref.decode_len_delimited(decoder)?;\n                }\n                116u32 => {\n                    let mut_ref = loop {\n                        if let ::core::option::Option::Some(variant) = &mut self.r#payload {\n                            if let CtrlMsg_::Payload::ReqOtaBegin(variant) = &mut *variant {\n                                break &mut *variant;\n                            }\n                        }\n                        self.r#payload = ::core::option::Option::Some(CtrlMsg_::Payload::ReqOtaBegin(\n                            ::core::default::Default::default(),\n                        ));\n                    };\n                    mut_ref.decode_len_delimited(decoder)?;\n                }\n                117u32 => {\n                    let mut_ref = loop {\n                        if let ::core::option::Option::Some(variant) = &mut self.r#payload {\n                            if let CtrlMsg_::Payload::ReqOtaWrite(variant) = &mut *variant {\n                                break &mut *variant;\n                            }\n                        }\n                        self.r#payload = ::core::option::Option::Some(CtrlMsg_::Payload::ReqOtaWrite(\n                            ::core::default::Default::default(),\n                        ));\n                    };\n                    mut_ref.decode_len_delimited(decoder)?;\n                }\n                118u32 => {\n                    let mut_ref = loop {\n                        if let ::core::option::Option::Some(variant) = &mut self.r#payload {\n                            if let CtrlMsg_::Payload::ReqOtaEnd(variant) = &mut *variant {\n                                break &mut *variant;\n                            }\n                        }\n                        self.r#payload = ::core::option::Option::Some(CtrlMsg_::Payload::ReqOtaEnd(\n                            ::core::default::Default::default(),\n                        ));\n                    };\n                    mut_ref.decode_len_delimited(decoder)?;\n                }\n                119u32 => {\n                    let mut_ref = loop {\n                        if let ::core::option::Option::Some(variant) = &mut self.r#payload {\n                            if let CtrlMsg_::Payload::ReqSetWifiMaxTxPower(variant) = &mut *variant {\n                                break &mut *variant;\n                            }\n                        }\n                        self.r#payload = ::core::option::Option::Some(CtrlMsg_::Payload::ReqSetWifiMaxTxPower(\n                            ::core::default::Default::default(),\n                        ));\n                    };\n                    mut_ref.decode_len_delimited(decoder)?;\n                }\n                120u32 => {\n                    let mut_ref = loop {\n                        if let ::core::option::Option::Some(variant) = &mut self.r#payload {\n                            if let CtrlMsg_::Payload::ReqGetWifiCurrTxPower(variant) = &mut *variant {\n                                break &mut *variant;\n                            }\n                        }\n                        self.r#payload = ::core::option::Option::Some(CtrlMsg_::Payload::ReqGetWifiCurrTxPower(\n                            ::core::default::Default::default(),\n                        ));\n                    };\n                    mut_ref.decode_len_delimited(decoder)?;\n                }\n                121u32 => {\n                    let mut_ref = loop {\n                        if let ::core::option::Option::Some(variant) = &mut self.r#payload {\n                            if let CtrlMsg_::Payload::ReqConfigHeartbeat(variant) = &mut *variant {\n                                break &mut *variant;\n                            }\n                        }\n                        self.r#payload = ::core::option::Option::Some(CtrlMsg_::Payload::ReqConfigHeartbeat(\n                            ::core::default::Default::default(),\n                        ));\n                    };\n                    mut_ref.decode_len_delimited(decoder)?;\n                }\n                122u32 => {\n                    let mut_ref = loop {\n                        if let ::core::option::Option::Some(variant) = &mut self.r#payload {\n                            if let CtrlMsg_::Payload::ReqEnableDisableFeat(variant) = &mut *variant {\n                                break &mut *variant;\n                            }\n                        }\n                        self.r#payload = ::core::option::Option::Some(CtrlMsg_::Payload::ReqEnableDisableFeat(\n                            ::core::default::Default::default(),\n                        ));\n                    };\n                    mut_ref.decode_len_delimited(decoder)?;\n                }\n                123u32 => {\n                    let mut_ref = loop {\n                        if let ::core::option::Option::Some(variant) = &mut self.r#payload {\n                            if let CtrlMsg_::Payload::ReqGetFwVersion(variant) = &mut *variant {\n                                break &mut *variant;\n                            }\n                        }\n                        self.r#payload = ::core::option::Option::Some(CtrlMsg_::Payload::ReqGetFwVersion(\n                            ::core::default::Default::default(),\n                        ));\n                    };\n                    mut_ref.decode_len_delimited(decoder)?;\n                }\n                124u32 => {\n                    let mut_ref = loop {\n                        if let ::core::option::Option::Some(variant) = &mut self.r#payload {\n                            if let CtrlMsg_::Payload::ReqSetCountryCode(variant) = &mut *variant {\n                                break &mut *variant;\n                            }\n                        }\n                        self.r#payload = ::core::option::Option::Some(CtrlMsg_::Payload::ReqSetCountryCode(\n                            ::core::default::Default::default(),\n                        ));\n                    };\n                    mut_ref.decode_len_delimited(decoder)?;\n                }\n                125u32 => {\n                    let mut_ref = loop {\n                        if let ::core::option::Option::Some(variant) = &mut self.r#payload {\n                            if let CtrlMsg_::Payload::ReqGetCountryCode(variant) = &mut *variant {\n                                break &mut *variant;\n                            }\n                        }\n                        self.r#payload = ::core::option::Option::Some(CtrlMsg_::Payload::ReqGetCountryCode(\n                            ::core::default::Default::default(),\n                        ));\n                    };\n                    mut_ref.decode_len_delimited(decoder)?;\n                }\n                126u32 => {\n                    let mut_ref = loop {\n                        if let ::core::option::Option::Some(variant) = &mut self.r#payload {\n                            if let CtrlMsg_::Payload::ReqSetDhcpDnsStatus(variant) = &mut *variant {\n                                break &mut *variant;\n                            }\n                        }\n                        self.r#payload = ::core::option::Option::Some(CtrlMsg_::Payload::ReqSetDhcpDnsStatus(\n                            ::core::default::Default::default(),\n                        ));\n                    };\n                    mut_ref.decode_len_delimited(decoder)?;\n                }\n                127u32 => {\n                    let mut_ref = loop {\n                        if let ::core::option::Option::Some(variant) = &mut self.r#payload {\n                            if let CtrlMsg_::Payload::ReqGetDhcpDnsStatus(variant) = &mut *variant {\n                                break &mut *variant;\n                            }\n                        }\n                        self.r#payload = ::core::option::Option::Some(CtrlMsg_::Payload::ReqGetDhcpDnsStatus(\n                            ::core::default::Default::default(),\n                        ));\n                    };\n                    mut_ref.decode_len_delimited(decoder)?;\n                }\n                128u32 => {\n                    let mut_ref = loop {\n                        if let ::core::option::Option::Some(variant) = &mut self.r#payload {\n                            if let CtrlMsg_::Payload::ReqCustomRpcUnserialisedMsg(variant) = &mut *variant {\n                                break &mut *variant;\n                            }\n                        }\n                        self.r#payload = ::core::option::Option::Some(CtrlMsg_::Payload::ReqCustomRpcUnserialisedMsg(\n                            ::core::default::Default::default(),\n                        ));\n                    };\n                    mut_ref.decode_len_delimited(decoder)?;\n                }\n                201u32 => {\n                    let mut_ref = loop {\n                        if let ::core::option::Option::Some(variant) = &mut self.r#payload {\n                            if let CtrlMsg_::Payload::RespGetMacAddress(variant) = &mut *variant {\n                                break &mut *variant;\n                            }\n                        }\n                        self.r#payload = ::core::option::Option::Some(CtrlMsg_::Payload::RespGetMacAddress(\n                            ::core::default::Default::default(),\n                        ));\n                    };\n                    mut_ref.decode_len_delimited(decoder)?;\n                }\n                202u32 => {\n                    let mut_ref = loop {\n                        if let ::core::option::Option::Some(variant) = &mut self.r#payload {\n                            if let CtrlMsg_::Payload::RespSetMacAddress(variant) = &mut *variant {\n                                break &mut *variant;\n                            }\n                        }\n                        self.r#payload = ::core::option::Option::Some(CtrlMsg_::Payload::RespSetMacAddress(\n                            ::core::default::Default::default(),\n                        ));\n                    };\n                    mut_ref.decode_len_delimited(decoder)?;\n                }\n                203u32 => {\n                    let mut_ref = loop {\n                        if let ::core::option::Option::Some(variant) = &mut self.r#payload {\n                            if let CtrlMsg_::Payload::RespGetWifiMode(variant) = &mut *variant {\n                                break &mut *variant;\n                            }\n                        }\n                        self.r#payload = ::core::option::Option::Some(CtrlMsg_::Payload::RespGetWifiMode(\n                            ::core::default::Default::default(),\n                        ));\n                    };\n                    mut_ref.decode_len_delimited(decoder)?;\n                }\n                204u32 => {\n                    let mut_ref = loop {\n                        if let ::core::option::Option::Some(variant) = &mut self.r#payload {\n                            if let CtrlMsg_::Payload::RespSetWifiMode(variant) = &mut *variant {\n                                break &mut *variant;\n                            }\n                        }\n                        self.r#payload = ::core::option::Option::Some(CtrlMsg_::Payload::RespSetWifiMode(\n                            ::core::default::Default::default(),\n                        ));\n                    };\n                    mut_ref.decode_len_delimited(decoder)?;\n                }\n                205u32 => {\n                    let mut_ref = loop {\n                        if let ::core::option::Option::Some(variant) = &mut self.r#payload {\n                            if let CtrlMsg_::Payload::RespScanApList(variant) = &mut *variant {\n                                break &mut *variant;\n                            }\n                        }\n                        self.r#payload = ::core::option::Option::Some(CtrlMsg_::Payload::RespScanApList(\n                            ::core::default::Default::default(),\n                        ));\n                    };\n                    mut_ref.decode_len_delimited(decoder)?;\n                }\n                206u32 => {\n                    let mut_ref = loop {\n                        if let ::core::option::Option::Some(variant) = &mut self.r#payload {\n                            if let CtrlMsg_::Payload::RespGetApConfig(variant) = &mut *variant {\n                                break &mut *variant;\n                            }\n                        }\n                        self.r#payload = ::core::option::Option::Some(CtrlMsg_::Payload::RespGetApConfig(\n                            ::core::default::Default::default(),\n                        ));\n                    };\n                    mut_ref.decode_len_delimited(decoder)?;\n                }\n                207u32 => {\n                    let mut_ref = loop {\n                        if let ::core::option::Option::Some(variant) = &mut self.r#payload {\n                            if let CtrlMsg_::Payload::RespConnectAp(variant) = &mut *variant {\n                                break &mut *variant;\n                            }\n                        }\n                        self.r#payload = ::core::option::Option::Some(CtrlMsg_::Payload::RespConnectAp(\n                            ::core::default::Default::default(),\n                        ));\n                    };\n                    mut_ref.decode_len_delimited(decoder)?;\n                }\n                208u32 => {\n                    let mut_ref = loop {\n                        if let ::core::option::Option::Some(variant) = &mut self.r#payload {\n                            if let CtrlMsg_::Payload::RespDisconnectAp(variant) = &mut *variant {\n                                break &mut *variant;\n                            }\n                        }\n                        self.r#payload = ::core::option::Option::Some(CtrlMsg_::Payload::RespDisconnectAp(\n                            ::core::default::Default::default(),\n                        ));\n                    };\n                    mut_ref.decode_len_delimited(decoder)?;\n                }\n                209u32 => {\n                    let mut_ref = loop {\n                        if let ::core::option::Option::Some(variant) = &mut self.r#payload {\n                            if let CtrlMsg_::Payload::RespGetSoftapConfig(variant) = &mut *variant {\n                                break &mut *variant;\n                            }\n                        }\n                        self.r#payload = ::core::option::Option::Some(CtrlMsg_::Payload::RespGetSoftapConfig(\n                            ::core::default::Default::default(),\n                        ));\n                    };\n                    mut_ref.decode_len_delimited(decoder)?;\n                }\n                210u32 => {\n                    let mut_ref = loop {\n                        if let ::core::option::Option::Some(variant) = &mut self.r#payload {\n                            if let CtrlMsg_::Payload::RespSetSoftapVendorSpecificIe(variant) = &mut *variant {\n                                break &mut *variant;\n                            }\n                        }\n                        self.r#payload = ::core::option::Option::Some(\n                            CtrlMsg_::Payload::RespSetSoftapVendorSpecificIe(::core::default::Default::default()),\n                        );\n                    };\n                    mut_ref.decode_len_delimited(decoder)?;\n                }\n                211u32 => {\n                    let mut_ref = loop {\n                        if let ::core::option::Option::Some(variant) = &mut self.r#payload {\n                            if let CtrlMsg_::Payload::RespStartSoftap(variant) = &mut *variant {\n                                break &mut *variant;\n                            }\n                        }\n                        self.r#payload = ::core::option::Option::Some(CtrlMsg_::Payload::RespStartSoftap(\n                            ::core::default::Default::default(),\n                        ));\n                    };\n                    mut_ref.decode_len_delimited(decoder)?;\n                }\n                212u32 => {\n                    let mut_ref = loop {\n                        if let ::core::option::Option::Some(variant) = &mut self.r#payload {\n                            if let CtrlMsg_::Payload::RespSoftapConnectedStasList(variant) = &mut *variant {\n                                break &mut *variant;\n                            }\n                        }\n                        self.r#payload = ::core::option::Option::Some(CtrlMsg_::Payload::RespSoftapConnectedStasList(\n                            ::core::default::Default::default(),\n                        ));\n                    };\n                    mut_ref.decode_len_delimited(decoder)?;\n                }\n                213u32 => {\n                    let mut_ref = loop {\n                        if let ::core::option::Option::Some(variant) = &mut self.r#payload {\n                            if let CtrlMsg_::Payload::RespStopSoftap(variant) = &mut *variant {\n                                break &mut *variant;\n                            }\n                        }\n                        self.r#payload = ::core::option::Option::Some(CtrlMsg_::Payload::RespStopSoftap(\n                            ::core::default::Default::default(),\n                        ));\n                    };\n                    mut_ref.decode_len_delimited(decoder)?;\n                }\n                214u32 => {\n                    let mut_ref = loop {\n                        if let ::core::option::Option::Some(variant) = &mut self.r#payload {\n                            if let CtrlMsg_::Payload::RespSetPowerSaveMode(variant) = &mut *variant {\n                                break &mut *variant;\n                            }\n                        }\n                        self.r#payload = ::core::option::Option::Some(CtrlMsg_::Payload::RespSetPowerSaveMode(\n                            ::core::default::Default::default(),\n                        ));\n                    };\n                    mut_ref.decode_len_delimited(decoder)?;\n                }\n                215u32 => {\n                    let mut_ref = loop {\n                        if let ::core::option::Option::Some(variant) = &mut self.r#payload {\n                            if let CtrlMsg_::Payload::RespGetPowerSaveMode(variant) = &mut *variant {\n                                break &mut *variant;\n                            }\n                        }\n                        self.r#payload = ::core::option::Option::Some(CtrlMsg_::Payload::RespGetPowerSaveMode(\n                            ::core::default::Default::default(),\n                        ));\n                    };\n                    mut_ref.decode_len_delimited(decoder)?;\n                }\n                216u32 => {\n                    let mut_ref = loop {\n                        if let ::core::option::Option::Some(variant) = &mut self.r#payload {\n                            if let CtrlMsg_::Payload::RespOtaBegin(variant) = &mut *variant {\n                                break &mut *variant;\n                            }\n                        }\n                        self.r#payload = ::core::option::Option::Some(CtrlMsg_::Payload::RespOtaBegin(\n                            ::core::default::Default::default(),\n                        ));\n                    };\n                    mut_ref.decode_len_delimited(decoder)?;\n                }\n                217u32 => {\n                    let mut_ref = loop {\n                        if let ::core::option::Option::Some(variant) = &mut self.r#payload {\n                            if let CtrlMsg_::Payload::RespOtaWrite(variant) = &mut *variant {\n                                break &mut *variant;\n                            }\n                        }\n                        self.r#payload = ::core::option::Option::Some(CtrlMsg_::Payload::RespOtaWrite(\n                            ::core::default::Default::default(),\n                        ));\n                    };\n                    mut_ref.decode_len_delimited(decoder)?;\n                }\n                218u32 => {\n                    let mut_ref = loop {\n                        if let ::core::option::Option::Some(variant) = &mut self.r#payload {\n                            if let CtrlMsg_::Payload::RespOtaEnd(variant) = &mut *variant {\n                                break &mut *variant;\n                            }\n                        }\n                        self.r#payload = ::core::option::Option::Some(CtrlMsg_::Payload::RespOtaEnd(\n                            ::core::default::Default::default(),\n                        ));\n                    };\n                    mut_ref.decode_len_delimited(decoder)?;\n                }\n                219u32 => {\n                    let mut_ref = loop {\n                        if let ::core::option::Option::Some(variant) = &mut self.r#payload {\n                            if let CtrlMsg_::Payload::RespSetWifiMaxTxPower(variant) = &mut *variant {\n                                break &mut *variant;\n                            }\n                        }\n                        self.r#payload = ::core::option::Option::Some(CtrlMsg_::Payload::RespSetWifiMaxTxPower(\n                            ::core::default::Default::default(),\n                        ));\n                    };\n                    mut_ref.decode_len_delimited(decoder)?;\n                }\n                220u32 => {\n                    let mut_ref = loop {\n                        if let ::core::option::Option::Some(variant) = &mut self.r#payload {\n                            if let CtrlMsg_::Payload::RespGetWifiCurrTxPower(variant) = &mut *variant {\n                                break &mut *variant;\n                            }\n                        }\n                        self.r#payload = ::core::option::Option::Some(CtrlMsg_::Payload::RespGetWifiCurrTxPower(\n                            ::core::default::Default::default(),\n                        ));\n                    };\n                    mut_ref.decode_len_delimited(decoder)?;\n                }\n                221u32 => {\n                    let mut_ref = loop {\n                        if let ::core::option::Option::Some(variant) = &mut self.r#payload {\n                            if let CtrlMsg_::Payload::RespConfigHeartbeat(variant) = &mut *variant {\n                                break &mut *variant;\n                            }\n                        }\n                        self.r#payload = ::core::option::Option::Some(CtrlMsg_::Payload::RespConfigHeartbeat(\n                            ::core::default::Default::default(),\n                        ));\n                    };\n                    mut_ref.decode_len_delimited(decoder)?;\n                }\n                222u32 => {\n                    let mut_ref = loop {\n                        if let ::core::option::Option::Some(variant) = &mut self.r#payload {\n                            if let CtrlMsg_::Payload::RespEnableDisableFeat(variant) = &mut *variant {\n                                break &mut *variant;\n                            }\n                        }\n                        self.r#payload = ::core::option::Option::Some(CtrlMsg_::Payload::RespEnableDisableFeat(\n                            ::core::default::Default::default(),\n                        ));\n                    };\n                    mut_ref.decode_len_delimited(decoder)?;\n                }\n                223u32 => {\n                    let mut_ref = loop {\n                        if let ::core::option::Option::Some(variant) = &mut self.r#payload {\n                            if let CtrlMsg_::Payload::RespGetFwVersion(variant) = &mut *variant {\n                                break &mut *variant;\n                            }\n                        }\n                        self.r#payload = ::core::option::Option::Some(CtrlMsg_::Payload::RespGetFwVersion(\n                            ::core::default::Default::default(),\n                        ));\n                    };\n                    mut_ref.decode_len_delimited(decoder)?;\n                }\n                224u32 => {\n                    let mut_ref = loop {\n                        if let ::core::option::Option::Some(variant) = &mut self.r#payload {\n                            if let CtrlMsg_::Payload::RespSetCountryCode(variant) = &mut *variant {\n                                break &mut *variant;\n                            }\n                        }\n                        self.r#payload = ::core::option::Option::Some(CtrlMsg_::Payload::RespSetCountryCode(\n                            ::core::default::Default::default(),\n                        ));\n                    };\n                    mut_ref.decode_len_delimited(decoder)?;\n                }\n                225u32 => {\n                    let mut_ref = loop {\n                        if let ::core::option::Option::Some(variant) = &mut self.r#payload {\n                            if let CtrlMsg_::Payload::RespGetCountryCode(variant) = &mut *variant {\n                                break &mut *variant;\n                            }\n                        }\n                        self.r#payload = ::core::option::Option::Some(CtrlMsg_::Payload::RespGetCountryCode(\n                            ::core::default::Default::default(),\n                        ));\n                    };\n                    mut_ref.decode_len_delimited(decoder)?;\n                }\n                226u32 => {\n                    let mut_ref = loop {\n                        if let ::core::option::Option::Some(variant) = &mut self.r#payload {\n                            if let CtrlMsg_::Payload::RespSetDhcpDnsStatus(variant) = &mut *variant {\n                                break &mut *variant;\n                            }\n                        }\n                        self.r#payload = ::core::option::Option::Some(CtrlMsg_::Payload::RespSetDhcpDnsStatus(\n                            ::core::default::Default::default(),\n                        ));\n                    };\n                    mut_ref.decode_len_delimited(decoder)?;\n                }\n                227u32 => {\n                    let mut_ref = loop {\n                        if let ::core::option::Option::Some(variant) = &mut self.r#payload {\n                            if let CtrlMsg_::Payload::RespGetDhcpDnsStatus(variant) = &mut *variant {\n                                break &mut *variant;\n                            }\n                        }\n                        self.r#payload = ::core::option::Option::Some(CtrlMsg_::Payload::RespGetDhcpDnsStatus(\n                            ::core::default::Default::default(),\n                        ));\n                    };\n                    mut_ref.decode_len_delimited(decoder)?;\n                }\n                228u32 => {\n                    let mut_ref = loop {\n                        if let ::core::option::Option::Some(variant) = &mut self.r#payload {\n                            if let CtrlMsg_::Payload::RespCustomRpcUnserialisedMsg(variant) = &mut *variant {\n                                break &mut *variant;\n                            }\n                        }\n                        self.r#payload = ::core::option::Option::Some(CtrlMsg_::Payload::RespCustomRpcUnserialisedMsg(\n                            ::core::default::Default::default(),\n                        ));\n                    };\n                    mut_ref.decode_len_delimited(decoder)?;\n                }\n                301u32 => {\n                    let mut_ref = loop {\n                        if let ::core::option::Option::Some(variant) = &mut self.r#payload {\n                            if let CtrlMsg_::Payload::EventEspInit(variant) = &mut *variant {\n                                break &mut *variant;\n                            }\n                        }\n                        self.r#payload = ::core::option::Option::Some(CtrlMsg_::Payload::EventEspInit(\n                            ::core::default::Default::default(),\n                        ));\n                    };\n                    mut_ref.decode_len_delimited(decoder)?;\n                }\n                302u32 => {\n                    let mut_ref = loop {\n                        if let ::core::option::Option::Some(variant) = &mut self.r#payload {\n                            if let CtrlMsg_::Payload::EventHeartbeat(variant) = &mut *variant {\n                                break &mut *variant;\n                            }\n                        }\n                        self.r#payload = ::core::option::Option::Some(CtrlMsg_::Payload::EventHeartbeat(\n                            ::core::default::Default::default(),\n                        ));\n                    };\n                    mut_ref.decode_len_delimited(decoder)?;\n                }\n                303u32 => {\n                    let mut_ref = loop {\n                        if let ::core::option::Option::Some(variant) = &mut self.r#payload {\n                            if let CtrlMsg_::Payload::EventStationDisconnectFromAp(variant) = &mut *variant {\n                                break &mut *variant;\n                            }\n                        }\n                        self.r#payload = ::core::option::Option::Some(CtrlMsg_::Payload::EventStationDisconnectFromAp(\n                            ::core::default::Default::default(),\n                        ));\n                    };\n                    mut_ref.decode_len_delimited(decoder)?;\n                }\n                304u32 => {\n                    let mut_ref = loop {\n                        if let ::core::option::Option::Some(variant) = &mut self.r#payload {\n                            if let CtrlMsg_::Payload::EventStationDisconnectFromEspSoftAp(variant) = &mut *variant {\n                                break &mut *variant;\n                            }\n                        }\n                        self.r#payload = ::core::option::Option::Some(\n                            CtrlMsg_::Payload::EventStationDisconnectFromEspSoftAp(::core::default::Default::default()),\n                        );\n                    };\n                    mut_ref.decode_len_delimited(decoder)?;\n                }\n                305u32 => {\n                    let mut_ref = loop {\n                        if let ::core::option::Option::Some(variant) = &mut self.r#payload {\n                            if let CtrlMsg_::Payload::EventStationConnectedToAp(variant) = &mut *variant {\n                                break &mut *variant;\n                            }\n                        }\n                        self.r#payload = ::core::option::Option::Some(CtrlMsg_::Payload::EventStationConnectedToAp(\n                            ::core::default::Default::default(),\n                        ));\n                    };\n                    mut_ref.decode_len_delimited(decoder)?;\n                }\n                306u32 => {\n                    let mut_ref = loop {\n                        if let ::core::option::Option::Some(variant) = &mut self.r#payload {\n                            if let CtrlMsg_::Payload::EventStationConnectedToEspSoftAp(variant) = &mut *variant {\n                                break &mut *variant;\n                            }\n                        }\n                        self.r#payload = ::core::option::Option::Some(\n                            CtrlMsg_::Payload::EventStationConnectedToEspSoftAp(::core::default::Default::default()),\n                        );\n                    };\n                    mut_ref.decode_len_delimited(decoder)?;\n                }\n                307u32 => {\n                    let mut_ref = loop {\n                        if let ::core::option::Option::Some(variant) = &mut self.r#payload {\n                            if let CtrlMsg_::Payload::EventSetDhcpDnsStatus(variant) = &mut *variant {\n                                break &mut *variant;\n                            }\n                        }\n                        self.r#payload = ::core::option::Option::Some(CtrlMsg_::Payload::EventSetDhcpDnsStatus(\n                            ::core::default::Default::default(),\n                        ));\n                    };\n                    mut_ref.decode_len_delimited(decoder)?;\n                }\n                308u32 => {\n                    let mut_ref = loop {\n                        if let ::core::option::Option::Some(variant) = &mut self.r#payload {\n                            if let CtrlMsg_::Payload::EventCustomRpcUnserialisedMsg(variant) = &mut *variant {\n                                break &mut *variant;\n                            }\n                        }\n                        self.r#payload = ::core::option::Option::Some(\n                            CtrlMsg_::Payload::EventCustomRpcUnserialisedMsg(::core::default::Default::default()),\n                        );\n                    };\n                    mut_ref.decode_len_delimited(decoder)?;\n                }\n                _ => {\n                    decoder.skip_wire_value(tag.wire_type())?;\n                }\n            }\n        }\n        Ok(())\n    }\n}\nimpl ::micropb::MessageEncode for CtrlMsg {\n    const MAX_SIZE: ::core::result::Result<usize, &'static str> = 'msg: {\n        let mut max_size = 0;\n        match ::micropb::const_map!(::core::result::Result::Ok(CtrlMsgType::_MAX_SIZE), |size| size + 1usize) {\n            ::core::result::Result::Ok(size) => {\n                max_size += size;\n            }\n            ::core::result::Result::Err(err) => {\n                break 'msg (::core::result::Result::<usize, _>::Err(err));\n            }\n        }\n        match ::micropb::const_map!(::core::result::Result::Ok(CtrlMsgId::_MAX_SIZE), |size| size + 1usize) {\n            ::core::result::Result::Ok(size) => {\n                max_size += size;\n            }\n            ::core::result::Result::Err(err) => {\n                break 'msg (::core::result::Result::<usize, _>::Err(err));\n            }\n        }\n        match ::micropb::const_map!(::core::result::Result::Ok(10usize), |size| size + 1usize) {\n            ::core::result::Result::Ok(size) => {\n                max_size += size;\n            }\n            ::core::result::Result::Err(err) => {\n                break 'msg (::core::result::Result::<usize, _>::Err(err));\n            }\n        }\n        match ::micropb::const_map!(::core::result::Result::Ok(5usize), |size| size + 1usize) {\n            ::core::result::Result::Ok(size) => {\n                max_size += size;\n            }\n            ::core::result::Result::Err(err) => {\n                break 'msg (::core::result::Result::<usize, _>::Err(err));\n            }\n        }\n        match 'oneof: {\n            let mut max_size = 0;\n            match ::micropb::const_map!(\n                ::micropb::const_map!(\n                    <CtrlMsg_Req_GetMacAddress as ::micropb::MessageEncode>::MAX_SIZE,\n                    |size| ::micropb::size::sizeof_len_record(size)\n                ),\n                |size| size + 2usize\n            ) {\n                ::core::result::Result::Ok(size) => {\n                    if size > max_size {\n                        max_size = size;\n                    }\n                }\n                ::core::result::Result::Err(err) => {\n                    break 'oneof (::core::result::Result::<usize, _>::Err(err));\n                }\n            }\n            match ::micropb::const_map!(\n                ::micropb::const_map!(\n                    <CtrlMsg_Req_SetMacAddress as ::micropb::MessageEncode>::MAX_SIZE,\n                    |size| ::micropb::size::sizeof_len_record(size)\n                ),\n                |size| size + 2usize\n            ) {\n                ::core::result::Result::Ok(size) => {\n                    if size > max_size {\n                        max_size = size;\n                    }\n                }\n                ::core::result::Result::Err(err) => {\n                    break 'oneof (::core::result::Result::<usize, _>::Err(err));\n                }\n            }\n            match ::micropb::const_map!(\n                ::micropb::const_map!(<CtrlMsg_Req_GetMode as ::micropb::MessageEncode>::MAX_SIZE, |size| {\n                    ::micropb::size::sizeof_len_record(size)\n                }),\n                |size| size + 2usize\n            ) {\n                ::core::result::Result::Ok(size) => {\n                    if size > max_size {\n                        max_size = size;\n                    }\n                }\n                ::core::result::Result::Err(err) => {\n                    break 'oneof (::core::result::Result::<usize, _>::Err(err));\n                }\n            }\n            match ::micropb::const_map!(\n                ::micropb::const_map!(<CtrlMsg_Req_SetMode as ::micropb::MessageEncode>::MAX_SIZE, |size| {\n                    ::micropb::size::sizeof_len_record(size)\n                }),\n                |size| size + 2usize\n            ) {\n                ::core::result::Result::Ok(size) => {\n                    if size > max_size {\n                        max_size = size;\n                    }\n                }\n                ::core::result::Result::Err(err) => {\n                    break 'oneof (::core::result::Result::<usize, _>::Err(err));\n                }\n            }\n            match ::micropb::const_map!(\n                ::micropb::const_map!(<CtrlMsg_Req_ScanResult as ::micropb::MessageEncode>::MAX_SIZE, |size| {\n                    ::micropb::size::sizeof_len_record(size)\n                }),\n                |size| size + 2usize\n            ) {\n                ::core::result::Result::Ok(size) => {\n                    if size > max_size {\n                        max_size = size;\n                    }\n                }\n                ::core::result::Result::Err(err) => {\n                    break 'oneof (::core::result::Result::<usize, _>::Err(err));\n                }\n            }\n            match ::micropb::const_map!(\n                ::micropb::const_map!(\n                    <CtrlMsg_Req_GetAPConfig as ::micropb::MessageEncode>::MAX_SIZE,\n                    |size| ::micropb::size::sizeof_len_record(size)\n                ),\n                |size| size + 2usize\n            ) {\n                ::core::result::Result::Ok(size) => {\n                    if size > max_size {\n                        max_size = size;\n                    }\n                }\n                ::core::result::Result::Err(err) => {\n                    break 'oneof (::core::result::Result::<usize, _>::Err(err));\n                }\n            }\n            match ::micropb::const_map!(\n                ::micropb::const_map!(<CtrlMsg_Req_ConnectAP as ::micropb::MessageEncode>::MAX_SIZE, |size| {\n                    ::micropb::size::sizeof_len_record(size)\n                }),\n                |size| size + 2usize\n            ) {\n                ::core::result::Result::Ok(size) => {\n                    if size > max_size {\n                        max_size = size;\n                    }\n                }\n                ::core::result::Result::Err(err) => {\n                    break 'oneof (::core::result::Result::<usize, _>::Err(err));\n                }\n            }\n            match ::micropb::const_map!(\n                ::micropb::const_map!(<CtrlMsg_Req_GetStatus as ::micropb::MessageEncode>::MAX_SIZE, |size| {\n                    ::micropb::size::sizeof_len_record(size)\n                }),\n                |size| size + 2usize\n            ) {\n                ::core::result::Result::Ok(size) => {\n                    if size > max_size {\n                        max_size = size;\n                    }\n                }\n                ::core::result::Result::Err(err) => {\n                    break 'oneof (::core::result::Result::<usize, _>::Err(err));\n                }\n            }\n            match ::micropb::const_map!(\n                ::micropb::const_map!(\n                    <CtrlMsg_Req_GetSoftAPConfig as ::micropb::MessageEncode>::MAX_SIZE,\n                    |size| ::micropb::size::sizeof_len_record(size)\n                ),\n                |size| size + 2usize\n            ) {\n                ::core::result::Result::Ok(size) => {\n                    if size > max_size {\n                        max_size = size;\n                    }\n                }\n                ::core::result::Result::Err(err) => {\n                    break 'oneof (::core::result::Result::<usize, _>::Err(err));\n                }\n            }\n            match ::micropb::const_map!(\n                ::micropb::const_map!(\n                    <CtrlMsg_Req_SetSoftAPVendorSpecificIE as ::micropb::MessageEncode>::MAX_SIZE,\n                    |size| ::micropb::size::sizeof_len_record(size)\n                ),\n                |size| size + 2usize\n            ) {\n                ::core::result::Result::Ok(size) => {\n                    if size > max_size {\n                        max_size = size;\n                    }\n                }\n                ::core::result::Result::Err(err) => {\n                    break 'oneof (::core::result::Result::<usize, _>::Err(err));\n                }\n            }\n            match ::micropb::const_map!(\n                ::micropb::const_map!(\n                    <CtrlMsg_Req_StartSoftAP as ::micropb::MessageEncode>::MAX_SIZE,\n                    |size| ::micropb::size::sizeof_len_record(size)\n                ),\n                |size| size + 2usize\n            ) {\n                ::core::result::Result::Ok(size) => {\n                    if size > max_size {\n                        max_size = size;\n                    }\n                }\n                ::core::result::Result::Err(err) => {\n                    break 'oneof (::core::result::Result::<usize, _>::Err(err));\n                }\n            }\n            match ::micropb::const_map!(\n                ::micropb::const_map!(\n                    <CtrlMsg_Req_SoftAPConnectedSTA as ::micropb::MessageEncode>::MAX_SIZE,\n                    |size| ::micropb::size::sizeof_len_record(size)\n                ),\n                |size| size + 2usize\n            ) {\n                ::core::result::Result::Ok(size) => {\n                    if size > max_size {\n                        max_size = size;\n                    }\n                }\n                ::core::result::Result::Err(err) => {\n                    break 'oneof (::core::result::Result::<usize, _>::Err(err));\n                }\n            }\n            match ::micropb::const_map!(\n                ::micropb::const_map!(<CtrlMsg_Req_GetStatus as ::micropb::MessageEncode>::MAX_SIZE, |size| {\n                    ::micropb::size::sizeof_len_record(size)\n                }),\n                |size| size + 2usize\n            ) {\n                ::core::result::Result::Ok(size) => {\n                    if size > max_size {\n                        max_size = size;\n                    }\n                }\n                ::core::result::Result::Err(err) => {\n                    break 'oneof (::core::result::Result::<usize, _>::Err(err));\n                }\n            }\n            match ::micropb::const_map!(\n                ::micropb::const_map!(<CtrlMsg_Req_SetMode as ::micropb::MessageEncode>::MAX_SIZE, |size| {\n                    ::micropb::size::sizeof_len_record(size)\n                }),\n                |size| size + 2usize\n            ) {\n                ::core::result::Result::Ok(size) => {\n                    if size > max_size {\n                        max_size = size;\n                    }\n                }\n                ::core::result::Result::Err(err) => {\n                    break 'oneof (::core::result::Result::<usize, _>::Err(err));\n                }\n            }\n            match ::micropb::const_map!(\n                ::micropb::const_map!(<CtrlMsg_Req_GetMode as ::micropb::MessageEncode>::MAX_SIZE, |size| {\n                    ::micropb::size::sizeof_len_record(size)\n                }),\n                |size| size + 2usize\n            ) {\n                ::core::result::Result::Ok(size) => {\n                    if size > max_size {\n                        max_size = size;\n                    }\n                }\n                ::core::result::Result::Err(err) => {\n                    break 'oneof (::core::result::Result::<usize, _>::Err(err));\n                }\n            }\n            match ::micropb::const_map!(\n                ::micropb::const_map!(<CtrlMsg_Req_OTABegin as ::micropb::MessageEncode>::MAX_SIZE, |size| {\n                    ::micropb::size::sizeof_len_record(size)\n                }),\n                |size| size + 2usize\n            ) {\n                ::core::result::Result::Ok(size) => {\n                    if size > max_size {\n                        max_size = size;\n                    }\n                }\n                ::core::result::Result::Err(err) => {\n                    break 'oneof (::core::result::Result::<usize, _>::Err(err));\n                }\n            }\n            match ::micropb::const_map!(\n                ::micropb::const_map!(<CtrlMsg_Req_OTAWrite as ::micropb::MessageEncode>::MAX_SIZE, |size| {\n                    ::micropb::size::sizeof_len_record(size)\n                }),\n                |size| size + 2usize\n            ) {\n                ::core::result::Result::Ok(size) => {\n                    if size > max_size {\n                        max_size = size;\n                    }\n                }\n                ::core::result::Result::Err(err) => {\n                    break 'oneof (::core::result::Result::<usize, _>::Err(err));\n                }\n            }\n            match ::micropb::const_map!(\n                ::micropb::const_map!(<CtrlMsg_Req_OTAEnd as ::micropb::MessageEncode>::MAX_SIZE, |size| {\n                    ::micropb::size::sizeof_len_record(size)\n                }),\n                |size| size + 2usize\n            ) {\n                ::core::result::Result::Ok(size) => {\n                    if size > max_size {\n                        max_size = size;\n                    }\n                }\n                ::core::result::Result::Err(err) => {\n                    break 'oneof (::core::result::Result::<usize, _>::Err(err));\n                }\n            }\n            match ::micropb::const_map!(\n                ::micropb::const_map!(\n                    <CtrlMsg_Req_SetWifiMaxTxPower as ::micropb::MessageEncode>::MAX_SIZE,\n                    |size| ::micropb::size::sizeof_len_record(size)\n                ),\n                |size| size + 2usize\n            ) {\n                ::core::result::Result::Ok(size) => {\n                    if size > max_size {\n                        max_size = size;\n                    }\n                }\n                ::core::result::Result::Err(err) => {\n                    break 'oneof (::core::result::Result::<usize, _>::Err(err));\n                }\n            }\n            match ::micropb::const_map!(\n                ::micropb::const_map!(\n                    <CtrlMsg_Req_GetWifiCurrTxPower as ::micropb::MessageEncode>::MAX_SIZE,\n                    |size| ::micropb::size::sizeof_len_record(size)\n                ),\n                |size| size + 2usize\n            ) {\n                ::core::result::Result::Ok(size) => {\n                    if size > max_size {\n                        max_size = size;\n                    }\n                }\n                ::core::result::Result::Err(err) => {\n                    break 'oneof (::core::result::Result::<usize, _>::Err(err));\n                }\n            }\n            match ::micropb::const_map!(\n                ::micropb::const_map!(\n                    <CtrlMsg_Req_ConfigHeartbeat as ::micropb::MessageEncode>::MAX_SIZE,\n                    |size| ::micropb::size::sizeof_len_record(size)\n                ),\n                |size| size + 2usize\n            ) {\n                ::core::result::Result::Ok(size) => {\n                    if size > max_size {\n                        max_size = size;\n                    }\n                }\n                ::core::result::Result::Err(err) => {\n                    break 'oneof (::core::result::Result::<usize, _>::Err(err));\n                }\n            }\n            match ::micropb::const_map!(\n                ::micropb::const_map!(\n                    <CtrlMsg_Req_EnableDisable as ::micropb::MessageEncode>::MAX_SIZE,\n                    |size| ::micropb::size::sizeof_len_record(size)\n                ),\n                |size| size + 2usize\n            ) {\n                ::core::result::Result::Ok(size) => {\n                    if size > max_size {\n                        max_size = size;\n                    }\n                }\n                ::core::result::Result::Err(err) => {\n                    break 'oneof (::core::result::Result::<usize, _>::Err(err));\n                }\n            }\n            match ::micropb::const_map!(\n                ::micropb::const_map!(\n                    <CtrlMsg_Req_GetFwVersion as ::micropb::MessageEncode>::MAX_SIZE,\n                    |size| ::micropb::size::sizeof_len_record(size)\n                ),\n                |size| size + 2usize\n            ) {\n                ::core::result::Result::Ok(size) => {\n                    if size > max_size {\n                        max_size = size;\n                    }\n                }\n                ::core::result::Result::Err(err) => {\n                    break 'oneof (::core::result::Result::<usize, _>::Err(err));\n                }\n            }\n            match ::micropb::const_map!(\n                ::micropb::const_map!(\n                    <CtrlMsg_Req_SetCountryCode as ::micropb::MessageEncode>::MAX_SIZE,\n                    |size| ::micropb::size::sizeof_len_record(size)\n                ),\n                |size| size + 2usize\n            ) {\n                ::core::result::Result::Ok(size) => {\n                    if size > max_size {\n                        max_size = size;\n                    }\n                }\n                ::core::result::Result::Err(err) => {\n                    break 'oneof (::core::result::Result::<usize, _>::Err(err));\n                }\n            }\n            match ::micropb::const_map!(\n                ::micropb::const_map!(\n                    <CtrlMsg_Req_GetCountryCode as ::micropb::MessageEncode>::MAX_SIZE,\n                    |size| ::micropb::size::sizeof_len_record(size)\n                ),\n                |size| size + 2usize\n            ) {\n                ::core::result::Result::Ok(size) => {\n                    if size > max_size {\n                        max_size = size;\n                    }\n                }\n                ::core::result::Result::Err(err) => {\n                    break 'oneof (::core::result::Result::<usize, _>::Err(err));\n                }\n            }\n            match ::micropb::const_map!(\n                ::micropb::const_map!(\n                    <CtrlMsg_Req_SetDhcpDnsStatus as ::micropb::MessageEncode>::MAX_SIZE,\n                    |size| ::micropb::size::sizeof_len_record(size)\n                ),\n                |size| size + 2usize\n            ) {\n                ::core::result::Result::Ok(size) => {\n                    if size > max_size {\n                        max_size = size;\n                    }\n                }\n                ::core::result::Result::Err(err) => {\n                    break 'oneof (::core::result::Result::<usize, _>::Err(err));\n                }\n            }\n            match ::micropb::const_map!(\n                ::micropb::const_map!(\n                    <CtrlMsg_Req_GetDhcpDnsStatus as ::micropb::MessageEncode>::MAX_SIZE,\n                    |size| ::micropb::size::sizeof_len_record(size)\n                ),\n                |size| size + 2usize\n            ) {\n                ::core::result::Result::Ok(size) => {\n                    if size > max_size {\n                        max_size = size;\n                    }\n                }\n                ::core::result::Result::Err(err) => {\n                    break 'oneof (::core::result::Result::<usize, _>::Err(err));\n                }\n            }\n            match ::micropb::const_map!(\n                ::micropb::const_map!(\n                    <CtrlMsg_Req_CustomRpcUnserialisedMsg as ::micropb::MessageEncode>::MAX_SIZE,\n                    |size| ::micropb::size::sizeof_len_record(size)\n                ),\n                |size| size + 2usize\n            ) {\n                ::core::result::Result::Ok(size) => {\n                    if size > max_size {\n                        max_size = size;\n                    }\n                }\n                ::core::result::Result::Err(err) => {\n                    break 'oneof (::core::result::Result::<usize, _>::Err(err));\n                }\n            }\n            match ::micropb::const_map!(\n                ::micropb::const_map!(\n                    <CtrlMsg_Resp_GetMacAddress as ::micropb::MessageEncode>::MAX_SIZE,\n                    |size| ::micropb::size::sizeof_len_record(size)\n                ),\n                |size| size + 2usize\n            ) {\n                ::core::result::Result::Ok(size) => {\n                    if size > max_size {\n                        max_size = size;\n                    }\n                }\n                ::core::result::Result::Err(err) => {\n                    break 'oneof (::core::result::Result::<usize, _>::Err(err));\n                }\n            }\n            match ::micropb::const_map!(\n                ::micropb::const_map!(\n                    <CtrlMsg_Resp_SetMacAddress as ::micropb::MessageEncode>::MAX_SIZE,\n                    |size| ::micropb::size::sizeof_len_record(size)\n                ),\n                |size| size + 2usize\n            ) {\n                ::core::result::Result::Ok(size) => {\n                    if size > max_size {\n                        max_size = size;\n                    }\n                }\n                ::core::result::Result::Err(err) => {\n                    break 'oneof (::core::result::Result::<usize, _>::Err(err));\n                }\n            }\n            match ::micropb::const_map!(\n                ::micropb::const_map!(<CtrlMsg_Resp_GetMode as ::micropb::MessageEncode>::MAX_SIZE, |size| {\n                    ::micropb::size::sizeof_len_record(size)\n                }),\n                |size| size + 2usize\n            ) {\n                ::core::result::Result::Ok(size) => {\n                    if size > max_size {\n                        max_size = size;\n                    }\n                }\n                ::core::result::Result::Err(err) => {\n                    break 'oneof (::core::result::Result::<usize, _>::Err(err));\n                }\n            }\n            match ::micropb::const_map!(\n                ::micropb::const_map!(<CtrlMsg_Resp_SetMode as ::micropb::MessageEncode>::MAX_SIZE, |size| {\n                    ::micropb::size::sizeof_len_record(size)\n                }),\n                |size| size + 2usize\n            ) {\n                ::core::result::Result::Ok(size) => {\n                    if size > max_size {\n                        max_size = size;\n                    }\n                }\n                ::core::result::Result::Err(err) => {\n                    break 'oneof (::core::result::Result::<usize, _>::Err(err));\n                }\n            }\n            match ::micropb::const_map!(\n                ::micropb::const_map!(\n                    <CtrlMsg_Resp_ScanResult as ::micropb::MessageEncode>::MAX_SIZE,\n                    |size| ::micropb::size::sizeof_len_record(size)\n                ),\n                |size| size + 2usize\n            ) {\n                ::core::result::Result::Ok(size) => {\n                    if size > max_size {\n                        max_size = size;\n                    }\n                }\n                ::core::result::Result::Err(err) => {\n                    break 'oneof (::core::result::Result::<usize, _>::Err(err));\n                }\n            }\n            match ::micropb::const_map!(\n                ::micropb::const_map!(\n                    <CtrlMsg_Resp_GetAPConfig as ::micropb::MessageEncode>::MAX_SIZE,\n                    |size| ::micropb::size::sizeof_len_record(size)\n                ),\n                |size| size + 2usize\n            ) {\n                ::core::result::Result::Ok(size) => {\n                    if size > max_size {\n                        max_size = size;\n                    }\n                }\n                ::core::result::Result::Err(err) => {\n                    break 'oneof (::core::result::Result::<usize, _>::Err(err));\n                }\n            }\n            match ::micropb::const_map!(\n                ::micropb::const_map!(<CtrlMsg_Resp_ConnectAP as ::micropb::MessageEncode>::MAX_SIZE, |size| {\n                    ::micropb::size::sizeof_len_record(size)\n                }),\n                |size| size + 2usize\n            ) {\n                ::core::result::Result::Ok(size) => {\n                    if size > max_size {\n                        max_size = size;\n                    }\n                }\n                ::core::result::Result::Err(err) => {\n                    break 'oneof (::core::result::Result::<usize, _>::Err(err));\n                }\n            }\n            match ::micropb::const_map!(\n                ::micropb::const_map!(<CtrlMsg_Resp_GetStatus as ::micropb::MessageEncode>::MAX_SIZE, |size| {\n                    ::micropb::size::sizeof_len_record(size)\n                }),\n                |size| size + 2usize\n            ) {\n                ::core::result::Result::Ok(size) => {\n                    if size > max_size {\n                        max_size = size;\n                    }\n                }\n                ::core::result::Result::Err(err) => {\n                    break 'oneof (::core::result::Result::<usize, _>::Err(err));\n                }\n            }\n            match ::micropb::const_map!(\n                ::micropb::const_map!(\n                    <CtrlMsg_Resp_GetSoftAPConfig as ::micropb::MessageEncode>::MAX_SIZE,\n                    |size| ::micropb::size::sizeof_len_record(size)\n                ),\n                |size| size + 2usize\n            ) {\n                ::core::result::Result::Ok(size) => {\n                    if size > max_size {\n                        max_size = size;\n                    }\n                }\n                ::core::result::Result::Err(err) => {\n                    break 'oneof (::core::result::Result::<usize, _>::Err(err));\n                }\n            }\n            match ::micropb::const_map!(\n                ::micropb::const_map!(\n                    <CtrlMsg_Resp_SetSoftAPVendorSpecificIE as ::micropb::MessageEncode>::MAX_SIZE,\n                    |size| ::micropb::size::sizeof_len_record(size)\n                ),\n                |size| size + 2usize\n            ) {\n                ::core::result::Result::Ok(size) => {\n                    if size > max_size {\n                        max_size = size;\n                    }\n                }\n                ::core::result::Result::Err(err) => {\n                    break 'oneof (::core::result::Result::<usize, _>::Err(err));\n                }\n            }\n            match ::micropb::const_map!(\n                ::micropb::const_map!(\n                    <CtrlMsg_Resp_StartSoftAP as ::micropb::MessageEncode>::MAX_SIZE,\n                    |size| ::micropb::size::sizeof_len_record(size)\n                ),\n                |size| size + 2usize\n            ) {\n                ::core::result::Result::Ok(size) => {\n                    if size > max_size {\n                        max_size = size;\n                    }\n                }\n                ::core::result::Result::Err(err) => {\n                    break 'oneof (::core::result::Result::<usize, _>::Err(err));\n                }\n            }\n            match ::micropb::const_map!(\n                ::micropb::const_map!(\n                    <CtrlMsg_Resp_SoftAPConnectedSTA as ::micropb::MessageEncode>::MAX_SIZE,\n                    |size| ::micropb::size::sizeof_len_record(size)\n                ),\n                |size| size + 2usize\n            ) {\n                ::core::result::Result::Ok(size) => {\n                    if size > max_size {\n                        max_size = size;\n                    }\n                }\n                ::core::result::Result::Err(err) => {\n                    break 'oneof (::core::result::Result::<usize, _>::Err(err));\n                }\n            }\n            match ::micropb::const_map!(\n                ::micropb::const_map!(<CtrlMsg_Resp_GetStatus as ::micropb::MessageEncode>::MAX_SIZE, |size| {\n                    ::micropb::size::sizeof_len_record(size)\n                }),\n                |size| size + 2usize\n            ) {\n                ::core::result::Result::Ok(size) => {\n                    if size > max_size {\n                        max_size = size;\n                    }\n                }\n                ::core::result::Result::Err(err) => {\n                    break 'oneof (::core::result::Result::<usize, _>::Err(err));\n                }\n            }\n            match ::micropb::const_map!(\n                ::micropb::const_map!(<CtrlMsg_Resp_SetMode as ::micropb::MessageEncode>::MAX_SIZE, |size| {\n                    ::micropb::size::sizeof_len_record(size)\n                }),\n                |size| size + 2usize\n            ) {\n                ::core::result::Result::Ok(size) => {\n                    if size > max_size {\n                        max_size = size;\n                    }\n                }\n                ::core::result::Result::Err(err) => {\n                    break 'oneof (::core::result::Result::<usize, _>::Err(err));\n                }\n            }\n            match ::micropb::const_map!(\n                ::micropb::const_map!(<CtrlMsg_Resp_GetMode as ::micropb::MessageEncode>::MAX_SIZE, |size| {\n                    ::micropb::size::sizeof_len_record(size)\n                }),\n                |size| size + 2usize\n            ) {\n                ::core::result::Result::Ok(size) => {\n                    if size > max_size {\n                        max_size = size;\n                    }\n                }\n                ::core::result::Result::Err(err) => {\n                    break 'oneof (::core::result::Result::<usize, _>::Err(err));\n                }\n            }\n            match ::micropb::const_map!(\n                ::micropb::const_map!(<CtrlMsg_Resp_OTABegin as ::micropb::MessageEncode>::MAX_SIZE, |size| {\n                    ::micropb::size::sizeof_len_record(size)\n                }),\n                |size| size + 2usize\n            ) {\n                ::core::result::Result::Ok(size) => {\n                    if size > max_size {\n                        max_size = size;\n                    }\n                }\n                ::core::result::Result::Err(err) => {\n                    break 'oneof (::core::result::Result::<usize, _>::Err(err));\n                }\n            }\n            match ::micropb::const_map!(\n                ::micropb::const_map!(<CtrlMsg_Resp_OTAWrite as ::micropb::MessageEncode>::MAX_SIZE, |size| {\n                    ::micropb::size::sizeof_len_record(size)\n                }),\n                |size| size + 2usize\n            ) {\n                ::core::result::Result::Ok(size) => {\n                    if size > max_size {\n                        max_size = size;\n                    }\n                }\n                ::core::result::Result::Err(err) => {\n                    break 'oneof (::core::result::Result::<usize, _>::Err(err));\n                }\n            }\n            match ::micropb::const_map!(\n                ::micropb::const_map!(<CtrlMsg_Resp_OTAEnd as ::micropb::MessageEncode>::MAX_SIZE, |size| {\n                    ::micropb::size::sizeof_len_record(size)\n                }),\n                |size| size + 2usize\n            ) {\n                ::core::result::Result::Ok(size) => {\n                    if size > max_size {\n                        max_size = size;\n                    }\n                }\n                ::core::result::Result::Err(err) => {\n                    break 'oneof (::core::result::Result::<usize, _>::Err(err));\n                }\n            }\n            match ::micropb::const_map!(\n                ::micropb::const_map!(\n                    <CtrlMsg_Resp_SetWifiMaxTxPower as ::micropb::MessageEncode>::MAX_SIZE,\n                    |size| ::micropb::size::sizeof_len_record(size)\n                ),\n                |size| size + 2usize\n            ) {\n                ::core::result::Result::Ok(size) => {\n                    if size > max_size {\n                        max_size = size;\n                    }\n                }\n                ::core::result::Result::Err(err) => {\n                    break 'oneof (::core::result::Result::<usize, _>::Err(err));\n                }\n            }\n            match ::micropb::const_map!(\n                ::micropb::const_map!(\n                    <CtrlMsg_Resp_GetWifiCurrTxPower as ::micropb::MessageEncode>::MAX_SIZE,\n                    |size| ::micropb::size::sizeof_len_record(size)\n                ),\n                |size| size + 2usize\n            ) {\n                ::core::result::Result::Ok(size) => {\n                    if size > max_size {\n                        max_size = size;\n                    }\n                }\n                ::core::result::Result::Err(err) => {\n                    break 'oneof (::core::result::Result::<usize, _>::Err(err));\n                }\n            }\n            match ::micropb::const_map!(\n                ::micropb::const_map!(\n                    <CtrlMsg_Resp_ConfigHeartbeat as ::micropb::MessageEncode>::MAX_SIZE,\n                    |size| ::micropb::size::sizeof_len_record(size)\n                ),\n                |size| size + 2usize\n            ) {\n                ::core::result::Result::Ok(size) => {\n                    if size > max_size {\n                        max_size = size;\n                    }\n                }\n                ::core::result::Result::Err(err) => {\n                    break 'oneof (::core::result::Result::<usize, _>::Err(err));\n                }\n            }\n            match ::micropb::const_map!(\n                ::micropb::const_map!(\n                    <CtrlMsg_Resp_EnableDisable as ::micropb::MessageEncode>::MAX_SIZE,\n                    |size| ::micropb::size::sizeof_len_record(size)\n                ),\n                |size| size + 2usize\n            ) {\n                ::core::result::Result::Ok(size) => {\n                    if size > max_size {\n                        max_size = size;\n                    }\n                }\n                ::core::result::Result::Err(err) => {\n                    break 'oneof (::core::result::Result::<usize, _>::Err(err));\n                }\n            }\n            match ::micropb::const_map!(\n                ::micropb::const_map!(\n                    <CtrlMsg_Resp_GetFwVersion as ::micropb::MessageEncode>::MAX_SIZE,\n                    |size| ::micropb::size::sizeof_len_record(size)\n                ),\n                |size| size + 2usize\n            ) {\n                ::core::result::Result::Ok(size) => {\n                    if size > max_size {\n                        max_size = size;\n                    }\n                }\n                ::core::result::Result::Err(err) => {\n                    break 'oneof (::core::result::Result::<usize, _>::Err(err));\n                }\n            }\n            match ::micropb::const_map!(\n                ::micropb::const_map!(\n                    <CtrlMsg_Resp_SetCountryCode as ::micropb::MessageEncode>::MAX_SIZE,\n                    |size| ::micropb::size::sizeof_len_record(size)\n                ),\n                |size| size + 2usize\n            ) {\n                ::core::result::Result::Ok(size) => {\n                    if size > max_size {\n                        max_size = size;\n                    }\n                }\n                ::core::result::Result::Err(err) => {\n                    break 'oneof (::core::result::Result::<usize, _>::Err(err));\n                }\n            }\n            match ::micropb::const_map!(\n                ::micropb::const_map!(\n                    <CtrlMsg_Resp_GetCountryCode as ::micropb::MessageEncode>::MAX_SIZE,\n                    |size| ::micropb::size::sizeof_len_record(size)\n                ),\n                |size| size + 2usize\n            ) {\n                ::core::result::Result::Ok(size) => {\n                    if size > max_size {\n                        max_size = size;\n                    }\n                }\n                ::core::result::Result::Err(err) => {\n                    break 'oneof (::core::result::Result::<usize, _>::Err(err));\n                }\n            }\n            match ::micropb::const_map!(\n                ::micropb::const_map!(\n                    <CtrlMsg_Resp_SetDhcpDnsStatus as ::micropb::MessageEncode>::MAX_SIZE,\n                    |size| ::micropb::size::sizeof_len_record(size)\n                ),\n                |size| size + 2usize\n            ) {\n                ::core::result::Result::Ok(size) => {\n                    if size > max_size {\n                        max_size = size;\n                    }\n                }\n                ::core::result::Result::Err(err) => {\n                    break 'oneof (::core::result::Result::<usize, _>::Err(err));\n                }\n            }\n            match ::micropb::const_map!(\n                ::micropb::const_map!(\n                    <CtrlMsg_Resp_GetDhcpDnsStatus as ::micropb::MessageEncode>::MAX_SIZE,\n                    |size| ::micropb::size::sizeof_len_record(size)\n                ),\n                |size| size + 2usize\n            ) {\n                ::core::result::Result::Ok(size) => {\n                    if size > max_size {\n                        max_size = size;\n                    }\n                }\n                ::core::result::Result::Err(err) => {\n                    break 'oneof (::core::result::Result::<usize, _>::Err(err));\n                }\n            }\n            match ::micropb::const_map!(\n                ::micropb::const_map!(\n                    <CtrlMsg_Resp_CustomRpcUnserialisedMsg as ::micropb::MessageEncode>::MAX_SIZE,\n                    |size| ::micropb::size::sizeof_len_record(size)\n                ),\n                |size| size + 2usize\n            ) {\n                ::core::result::Result::Ok(size) => {\n                    if size > max_size {\n                        max_size = size;\n                    }\n                }\n                ::core::result::Result::Err(err) => {\n                    break 'oneof (::core::result::Result::<usize, _>::Err(err));\n                }\n            }\n            match ::micropb::const_map!(\n                ::micropb::const_map!(<CtrlMsg_Event_ESPInit as ::micropb::MessageEncode>::MAX_SIZE, |size| {\n                    ::micropb::size::sizeof_len_record(size)\n                }),\n                |size| size + 2usize\n            ) {\n                ::core::result::Result::Ok(size) => {\n                    if size > max_size {\n                        max_size = size;\n                    }\n                }\n                ::core::result::Result::Err(err) => {\n                    break 'oneof (::core::result::Result::<usize, _>::Err(err));\n                }\n            }\n            match ::micropb::const_map!(\n                ::micropb::const_map!(\n                    <CtrlMsg_Event_Heartbeat as ::micropb::MessageEncode>::MAX_SIZE,\n                    |size| ::micropb::size::sizeof_len_record(size)\n                ),\n                |size| size + 2usize\n            ) {\n                ::core::result::Result::Ok(size) => {\n                    if size > max_size {\n                        max_size = size;\n                    }\n                }\n                ::core::result::Result::Err(err) => {\n                    break 'oneof (::core::result::Result::<usize, _>::Err(err));\n                }\n            }\n            match ::micropb::const_map!(\n                ::micropb::const_map!(\n                    <CtrlMsg_Event_StationDisconnectFromAP as ::micropb::MessageEncode>::MAX_SIZE,\n                    |size| ::micropb::size::sizeof_len_record(size)\n                ),\n                |size| size + 2usize\n            ) {\n                ::core::result::Result::Ok(size) => {\n                    if size > max_size {\n                        max_size = size;\n                    }\n                }\n                ::core::result::Result::Err(err) => {\n                    break 'oneof (::core::result::Result::<usize, _>::Err(err));\n                }\n            }\n            match ::micropb::const_map!(\n                ::micropb::const_map!(\n                    <CtrlMsg_Event_StationDisconnectFromESPSoftAP as ::micropb::MessageEncode>::MAX_SIZE,\n                    |size| ::micropb::size::sizeof_len_record(size)\n                ),\n                |size| size + 2usize\n            ) {\n                ::core::result::Result::Ok(size) => {\n                    if size > max_size {\n                        max_size = size;\n                    }\n                }\n                ::core::result::Result::Err(err) => {\n                    break 'oneof (::core::result::Result::<usize, _>::Err(err));\n                }\n            }\n            match ::micropb::const_map!(\n                ::micropb::const_map!(\n                    <CtrlMsg_Event_StationConnectedToAP as ::micropb::MessageEncode>::MAX_SIZE,\n                    |size| ::micropb::size::sizeof_len_record(size)\n                ),\n                |size| size + 2usize\n            ) {\n                ::core::result::Result::Ok(size) => {\n                    if size > max_size {\n                        max_size = size;\n                    }\n                }\n                ::core::result::Result::Err(err) => {\n                    break 'oneof (::core::result::Result::<usize, _>::Err(err));\n                }\n            }\n            match ::micropb::const_map!(\n                ::micropb::const_map!(\n                    <CtrlMsg_Event_StationConnectedToESPSoftAP as ::micropb::MessageEncode>::MAX_SIZE,\n                    |size| ::micropb::size::sizeof_len_record(size)\n                ),\n                |size| size + 2usize\n            ) {\n                ::core::result::Result::Ok(size) => {\n                    if size > max_size {\n                        max_size = size;\n                    }\n                }\n                ::core::result::Result::Err(err) => {\n                    break 'oneof (::core::result::Result::<usize, _>::Err(err));\n                }\n            }\n            match ::micropb::const_map!(\n                ::micropb::const_map!(\n                    <CtrlMsg_Event_SetDhcpDnsStatus as ::micropb::MessageEncode>::MAX_SIZE,\n                    |size| ::micropb::size::sizeof_len_record(size)\n                ),\n                |size| size + 2usize\n            ) {\n                ::core::result::Result::Ok(size) => {\n                    if size > max_size {\n                        max_size = size;\n                    }\n                }\n                ::core::result::Result::Err(err) => {\n                    break 'oneof (::core::result::Result::<usize, _>::Err(err));\n                }\n            }\n            match ::micropb::const_map!(\n                ::micropb::const_map!(\n                    <CtrlMsg_Event_CustomRpcUnserialisedMsg as ::micropb::MessageEncode>::MAX_SIZE,\n                    |size| ::micropb::size::sizeof_len_record(size)\n                ),\n                |size| size + 2usize\n            ) {\n                ::core::result::Result::Ok(size) => {\n                    if size > max_size {\n                        max_size = size;\n                    }\n                }\n                ::core::result::Result::Err(err) => {\n                    break 'oneof (::core::result::Result::<usize, _>::Err(err));\n                }\n            }\n            ::core::result::Result::Ok(max_size)\n        } {\n            ::core::result::Result::Ok(size) => {\n                max_size += size;\n            }\n            ::core::result::Result::Err(err) => {\n                break 'msg (::core::result::Result::<usize, _>::Err(err));\n            }\n        }\n        ::core::result::Result::Ok(max_size)\n    };\n    fn encode<IMPL_MICROPB_WRITE: ::micropb::PbWrite>(\n        &self,\n        encoder: &mut ::micropb::PbEncoder<IMPL_MICROPB_WRITE>,\n    ) -> Result<(), IMPL_MICROPB_WRITE::Error> {\n        use ::micropb::{FieldEncode, PbMap};\n        {\n            let val_ref = &self.r#msg_type;\n            if val_ref.0 != 0 {\n                encoder.encode_varint32(8u32)?;\n                encoder.encode_int32(val_ref.0 as _)?;\n            }\n        }\n        {\n            let val_ref = &self.r#msg_id;\n            if val_ref.0 != 0 {\n                encoder.encode_varint32(16u32)?;\n                encoder.encode_int32(val_ref.0 as _)?;\n            }\n        }\n        {\n            let val_ref = &self.r#uid;\n            if *val_ref != 0 {\n                encoder.encode_varint32(24u32)?;\n                encoder.encode_int32(*val_ref as _)?;\n            }\n        }\n        {\n            let val_ref = &self.r#req_resp_type;\n            if *val_ref != 0 {\n                encoder.encode_varint32(32u32)?;\n                encoder.encode_varint32(*val_ref as _)?;\n            }\n        }\n        if let Some(oneof) = &self.r#payload {\n            match &*oneof {\n                CtrlMsg_::Payload::ReqGetMacAddress(val_ref) => {\n                    let val_ref = &*val_ref;\n                    encoder.encode_varint32(810u32)?;\n                    val_ref.encode_len_delimited(encoder)?;\n                }\n                CtrlMsg_::Payload::ReqSetMacAddress(val_ref) => {\n                    let val_ref = &*val_ref;\n                    encoder.encode_varint32(818u32)?;\n                    val_ref.encode_len_delimited(encoder)?;\n                }\n                CtrlMsg_::Payload::ReqGetWifiMode(val_ref) => {\n                    let val_ref = &*val_ref;\n                    encoder.encode_varint32(826u32)?;\n                    val_ref.encode_len_delimited(encoder)?;\n                }\n                CtrlMsg_::Payload::ReqSetWifiMode(val_ref) => {\n                    let val_ref = &*val_ref;\n                    encoder.encode_varint32(834u32)?;\n                    val_ref.encode_len_delimited(encoder)?;\n                }\n                CtrlMsg_::Payload::ReqScanApList(val_ref) => {\n                    let val_ref = &*val_ref;\n                    encoder.encode_varint32(842u32)?;\n                    val_ref.encode_len_delimited(encoder)?;\n                }\n                CtrlMsg_::Payload::ReqGetApConfig(val_ref) => {\n                    let val_ref = &*val_ref;\n                    encoder.encode_varint32(850u32)?;\n                    val_ref.encode_len_delimited(encoder)?;\n                }\n                CtrlMsg_::Payload::ReqConnectAp(val_ref) => {\n                    let val_ref = &*val_ref;\n                    encoder.encode_varint32(858u32)?;\n                    val_ref.encode_len_delimited(encoder)?;\n                }\n                CtrlMsg_::Payload::ReqDisconnectAp(val_ref) => {\n                    let val_ref = &*val_ref;\n                    encoder.encode_varint32(866u32)?;\n                    val_ref.encode_len_delimited(encoder)?;\n                }\n                CtrlMsg_::Payload::ReqGetSoftapConfig(val_ref) => {\n                    let val_ref = &*val_ref;\n                    encoder.encode_varint32(874u32)?;\n                    val_ref.encode_len_delimited(encoder)?;\n                }\n                CtrlMsg_::Payload::ReqSetSoftapVendorSpecificIe(val_ref) => {\n                    let val_ref = &*val_ref;\n                    encoder.encode_varint32(882u32)?;\n                    val_ref.encode_len_delimited(encoder)?;\n                }\n                CtrlMsg_::Payload::ReqStartSoftap(val_ref) => {\n                    let val_ref = &*val_ref;\n                    encoder.encode_varint32(890u32)?;\n                    val_ref.encode_len_delimited(encoder)?;\n                }\n                CtrlMsg_::Payload::ReqSoftapConnectedStasList(val_ref) => {\n                    let val_ref = &*val_ref;\n                    encoder.encode_varint32(898u32)?;\n                    val_ref.encode_len_delimited(encoder)?;\n                }\n                CtrlMsg_::Payload::ReqStopSoftap(val_ref) => {\n                    let val_ref = &*val_ref;\n                    encoder.encode_varint32(906u32)?;\n                    val_ref.encode_len_delimited(encoder)?;\n                }\n                CtrlMsg_::Payload::ReqSetPowerSaveMode(val_ref) => {\n                    let val_ref = &*val_ref;\n                    encoder.encode_varint32(914u32)?;\n                    val_ref.encode_len_delimited(encoder)?;\n                }\n                CtrlMsg_::Payload::ReqGetPowerSaveMode(val_ref) => {\n                    let val_ref = &*val_ref;\n                    encoder.encode_varint32(922u32)?;\n                    val_ref.encode_len_delimited(encoder)?;\n                }\n                CtrlMsg_::Payload::ReqOtaBegin(val_ref) => {\n                    let val_ref = &*val_ref;\n                    encoder.encode_varint32(930u32)?;\n                    val_ref.encode_len_delimited(encoder)?;\n                }\n                CtrlMsg_::Payload::ReqOtaWrite(val_ref) => {\n                    let val_ref = &*val_ref;\n                    encoder.encode_varint32(938u32)?;\n                    val_ref.encode_len_delimited(encoder)?;\n                }\n                CtrlMsg_::Payload::ReqOtaEnd(val_ref) => {\n                    let val_ref = &*val_ref;\n                    encoder.encode_varint32(946u32)?;\n                    val_ref.encode_len_delimited(encoder)?;\n                }\n                CtrlMsg_::Payload::ReqSetWifiMaxTxPower(val_ref) => {\n                    let val_ref = &*val_ref;\n                    encoder.encode_varint32(954u32)?;\n                    val_ref.encode_len_delimited(encoder)?;\n                }\n                CtrlMsg_::Payload::ReqGetWifiCurrTxPower(val_ref) => {\n                    let val_ref = &*val_ref;\n                    encoder.encode_varint32(962u32)?;\n                    val_ref.encode_len_delimited(encoder)?;\n                }\n                CtrlMsg_::Payload::ReqConfigHeartbeat(val_ref) => {\n                    let val_ref = &*val_ref;\n                    encoder.encode_varint32(970u32)?;\n                    val_ref.encode_len_delimited(encoder)?;\n                }\n                CtrlMsg_::Payload::ReqEnableDisableFeat(val_ref) => {\n                    let val_ref = &*val_ref;\n                    encoder.encode_varint32(978u32)?;\n                    val_ref.encode_len_delimited(encoder)?;\n                }\n                CtrlMsg_::Payload::ReqGetFwVersion(val_ref) => {\n                    let val_ref = &*val_ref;\n                    encoder.encode_varint32(986u32)?;\n                    val_ref.encode_len_delimited(encoder)?;\n                }\n                CtrlMsg_::Payload::ReqSetCountryCode(val_ref) => {\n                    let val_ref = &*val_ref;\n                    encoder.encode_varint32(994u32)?;\n                    val_ref.encode_len_delimited(encoder)?;\n                }\n                CtrlMsg_::Payload::ReqGetCountryCode(val_ref) => {\n                    let val_ref = &*val_ref;\n                    encoder.encode_varint32(1002u32)?;\n                    val_ref.encode_len_delimited(encoder)?;\n                }\n                CtrlMsg_::Payload::ReqSetDhcpDnsStatus(val_ref) => {\n                    let val_ref = &*val_ref;\n                    encoder.encode_varint32(1010u32)?;\n                    val_ref.encode_len_delimited(encoder)?;\n                }\n                CtrlMsg_::Payload::ReqGetDhcpDnsStatus(val_ref) => {\n                    let val_ref = &*val_ref;\n                    encoder.encode_varint32(1018u32)?;\n                    val_ref.encode_len_delimited(encoder)?;\n                }\n                CtrlMsg_::Payload::ReqCustomRpcUnserialisedMsg(val_ref) => {\n                    let val_ref = &*val_ref;\n                    encoder.encode_varint32(1026u32)?;\n                    val_ref.encode_len_delimited(encoder)?;\n                }\n                CtrlMsg_::Payload::RespGetMacAddress(val_ref) => {\n                    let val_ref = &*val_ref;\n                    encoder.encode_varint32(1610u32)?;\n                    val_ref.encode_len_delimited(encoder)?;\n                }\n                CtrlMsg_::Payload::RespSetMacAddress(val_ref) => {\n                    let val_ref = &*val_ref;\n                    encoder.encode_varint32(1618u32)?;\n                    val_ref.encode_len_delimited(encoder)?;\n                }\n                CtrlMsg_::Payload::RespGetWifiMode(val_ref) => {\n                    let val_ref = &*val_ref;\n                    encoder.encode_varint32(1626u32)?;\n                    val_ref.encode_len_delimited(encoder)?;\n                }\n                CtrlMsg_::Payload::RespSetWifiMode(val_ref) => {\n                    let val_ref = &*val_ref;\n                    encoder.encode_varint32(1634u32)?;\n                    val_ref.encode_len_delimited(encoder)?;\n                }\n                CtrlMsg_::Payload::RespScanApList(val_ref) => {\n                    let val_ref = &*val_ref;\n                    encoder.encode_varint32(1642u32)?;\n                    val_ref.encode_len_delimited(encoder)?;\n                }\n                CtrlMsg_::Payload::RespGetApConfig(val_ref) => {\n                    let val_ref = &*val_ref;\n                    encoder.encode_varint32(1650u32)?;\n                    val_ref.encode_len_delimited(encoder)?;\n                }\n                CtrlMsg_::Payload::RespConnectAp(val_ref) => {\n                    let val_ref = &*val_ref;\n                    encoder.encode_varint32(1658u32)?;\n                    val_ref.encode_len_delimited(encoder)?;\n                }\n                CtrlMsg_::Payload::RespDisconnectAp(val_ref) => {\n                    let val_ref = &*val_ref;\n                    encoder.encode_varint32(1666u32)?;\n                    val_ref.encode_len_delimited(encoder)?;\n                }\n                CtrlMsg_::Payload::RespGetSoftapConfig(val_ref) => {\n                    let val_ref = &*val_ref;\n                    encoder.encode_varint32(1674u32)?;\n                    val_ref.encode_len_delimited(encoder)?;\n                }\n                CtrlMsg_::Payload::RespSetSoftapVendorSpecificIe(val_ref) => {\n                    let val_ref = &*val_ref;\n                    encoder.encode_varint32(1682u32)?;\n                    val_ref.encode_len_delimited(encoder)?;\n                }\n                CtrlMsg_::Payload::RespStartSoftap(val_ref) => {\n                    let val_ref = &*val_ref;\n                    encoder.encode_varint32(1690u32)?;\n                    val_ref.encode_len_delimited(encoder)?;\n                }\n                CtrlMsg_::Payload::RespSoftapConnectedStasList(val_ref) => {\n                    let val_ref = &*val_ref;\n                    encoder.encode_varint32(1698u32)?;\n                    val_ref.encode_len_delimited(encoder)?;\n                }\n                CtrlMsg_::Payload::RespStopSoftap(val_ref) => {\n                    let val_ref = &*val_ref;\n                    encoder.encode_varint32(1706u32)?;\n                    val_ref.encode_len_delimited(encoder)?;\n                }\n                CtrlMsg_::Payload::RespSetPowerSaveMode(val_ref) => {\n                    let val_ref = &*val_ref;\n                    encoder.encode_varint32(1714u32)?;\n                    val_ref.encode_len_delimited(encoder)?;\n                }\n                CtrlMsg_::Payload::RespGetPowerSaveMode(val_ref) => {\n                    let val_ref = &*val_ref;\n                    encoder.encode_varint32(1722u32)?;\n                    val_ref.encode_len_delimited(encoder)?;\n                }\n                CtrlMsg_::Payload::RespOtaBegin(val_ref) => {\n                    let val_ref = &*val_ref;\n                    encoder.encode_varint32(1730u32)?;\n                    val_ref.encode_len_delimited(encoder)?;\n                }\n                CtrlMsg_::Payload::RespOtaWrite(val_ref) => {\n                    let val_ref = &*val_ref;\n                    encoder.encode_varint32(1738u32)?;\n                    val_ref.encode_len_delimited(encoder)?;\n                }\n                CtrlMsg_::Payload::RespOtaEnd(val_ref) => {\n                    let val_ref = &*val_ref;\n                    encoder.encode_varint32(1746u32)?;\n                    val_ref.encode_len_delimited(encoder)?;\n                }\n                CtrlMsg_::Payload::RespSetWifiMaxTxPower(val_ref) => {\n                    let val_ref = &*val_ref;\n                    encoder.encode_varint32(1754u32)?;\n                    val_ref.encode_len_delimited(encoder)?;\n                }\n                CtrlMsg_::Payload::RespGetWifiCurrTxPower(val_ref) => {\n                    let val_ref = &*val_ref;\n                    encoder.encode_varint32(1762u32)?;\n                    val_ref.encode_len_delimited(encoder)?;\n                }\n                CtrlMsg_::Payload::RespConfigHeartbeat(val_ref) => {\n                    let val_ref = &*val_ref;\n                    encoder.encode_varint32(1770u32)?;\n                    val_ref.encode_len_delimited(encoder)?;\n                }\n                CtrlMsg_::Payload::RespEnableDisableFeat(val_ref) => {\n                    let val_ref = &*val_ref;\n                    encoder.encode_varint32(1778u32)?;\n                    val_ref.encode_len_delimited(encoder)?;\n                }\n                CtrlMsg_::Payload::RespGetFwVersion(val_ref) => {\n                    let val_ref = &*val_ref;\n                    encoder.encode_varint32(1786u32)?;\n                    val_ref.encode_len_delimited(encoder)?;\n                }\n                CtrlMsg_::Payload::RespSetCountryCode(val_ref) => {\n                    let val_ref = &*val_ref;\n                    encoder.encode_varint32(1794u32)?;\n                    val_ref.encode_len_delimited(encoder)?;\n                }\n                CtrlMsg_::Payload::RespGetCountryCode(val_ref) => {\n                    let val_ref = &*val_ref;\n                    encoder.encode_varint32(1802u32)?;\n                    val_ref.encode_len_delimited(encoder)?;\n                }\n                CtrlMsg_::Payload::RespSetDhcpDnsStatus(val_ref) => {\n                    let val_ref = &*val_ref;\n                    encoder.encode_varint32(1810u32)?;\n                    val_ref.encode_len_delimited(encoder)?;\n                }\n                CtrlMsg_::Payload::RespGetDhcpDnsStatus(val_ref) => {\n                    let val_ref = &*val_ref;\n                    encoder.encode_varint32(1818u32)?;\n                    val_ref.encode_len_delimited(encoder)?;\n                }\n                CtrlMsg_::Payload::RespCustomRpcUnserialisedMsg(val_ref) => {\n                    let val_ref = &*val_ref;\n                    encoder.encode_varint32(1826u32)?;\n                    val_ref.encode_len_delimited(encoder)?;\n                }\n                CtrlMsg_::Payload::EventEspInit(val_ref) => {\n                    let val_ref = &*val_ref;\n                    encoder.encode_varint32(2410u32)?;\n                    val_ref.encode_len_delimited(encoder)?;\n                }\n                CtrlMsg_::Payload::EventHeartbeat(val_ref) => {\n                    let val_ref = &*val_ref;\n                    encoder.encode_varint32(2418u32)?;\n                    val_ref.encode_len_delimited(encoder)?;\n                }\n                CtrlMsg_::Payload::EventStationDisconnectFromAp(val_ref) => {\n                    let val_ref = &*val_ref;\n                    encoder.encode_varint32(2426u32)?;\n                    val_ref.encode_len_delimited(encoder)?;\n                }\n                CtrlMsg_::Payload::EventStationDisconnectFromEspSoftAp(val_ref) => {\n                    let val_ref = &*val_ref;\n                    encoder.encode_varint32(2434u32)?;\n                    val_ref.encode_len_delimited(encoder)?;\n                }\n                CtrlMsg_::Payload::EventStationConnectedToAp(val_ref) => {\n                    let val_ref = &*val_ref;\n                    encoder.encode_varint32(2442u32)?;\n                    val_ref.encode_len_delimited(encoder)?;\n                }\n                CtrlMsg_::Payload::EventStationConnectedToEspSoftAp(val_ref) => {\n                    let val_ref = &*val_ref;\n                    encoder.encode_varint32(2450u32)?;\n                    val_ref.encode_len_delimited(encoder)?;\n                }\n                CtrlMsg_::Payload::EventSetDhcpDnsStatus(val_ref) => {\n                    let val_ref = &*val_ref;\n                    encoder.encode_varint32(2458u32)?;\n                    val_ref.encode_len_delimited(encoder)?;\n                }\n                CtrlMsg_::Payload::EventCustomRpcUnserialisedMsg(val_ref) => {\n                    let val_ref = &*val_ref;\n                    encoder.encode_varint32(2466u32)?;\n                    val_ref.encode_len_delimited(encoder)?;\n                }\n            }\n        }\n        Ok(())\n    }\n    fn compute_size(&self) -> usize {\n        use ::micropb::{FieldEncode, PbMap};\n        let mut size = 0;\n        {\n            let val_ref = &self.r#msg_type;\n            if val_ref.0 != 0 {\n                size += 1usize + ::micropb::size::sizeof_int32(val_ref.0 as _);\n            }\n        }\n        {\n            let val_ref = &self.r#msg_id;\n            if val_ref.0 != 0 {\n                size += 1usize + ::micropb::size::sizeof_int32(val_ref.0 as _);\n            }\n        }\n        {\n            let val_ref = &self.r#uid;\n            if *val_ref != 0 {\n                size += 1usize + ::micropb::size::sizeof_int32(*val_ref as _);\n            }\n        }\n        {\n            let val_ref = &self.r#req_resp_type;\n            if *val_ref != 0 {\n                size += 1usize + ::micropb::size::sizeof_varint32(*val_ref as _);\n            }\n        }\n        if let Some(oneof) = &self.r#payload {\n            match &*oneof {\n                CtrlMsg_::Payload::ReqGetMacAddress(val_ref) => {\n                    let val_ref = &*val_ref;\n                    size += 2usize + ::micropb::size::sizeof_len_record(val_ref.compute_size());\n                }\n                CtrlMsg_::Payload::ReqSetMacAddress(val_ref) => {\n                    let val_ref = &*val_ref;\n                    size += 2usize + ::micropb::size::sizeof_len_record(val_ref.compute_size());\n                }\n                CtrlMsg_::Payload::ReqGetWifiMode(val_ref) => {\n                    let val_ref = &*val_ref;\n                    size += 2usize + ::micropb::size::sizeof_len_record(val_ref.compute_size());\n                }\n                CtrlMsg_::Payload::ReqSetWifiMode(val_ref) => {\n                    let val_ref = &*val_ref;\n                    size += 2usize + ::micropb::size::sizeof_len_record(val_ref.compute_size());\n                }\n                CtrlMsg_::Payload::ReqScanApList(val_ref) => {\n                    let val_ref = &*val_ref;\n                    size += 2usize + ::micropb::size::sizeof_len_record(val_ref.compute_size());\n                }\n                CtrlMsg_::Payload::ReqGetApConfig(val_ref) => {\n                    let val_ref = &*val_ref;\n                    size += 2usize + ::micropb::size::sizeof_len_record(val_ref.compute_size());\n                }\n                CtrlMsg_::Payload::ReqConnectAp(val_ref) => {\n                    let val_ref = &*val_ref;\n                    size += 2usize + ::micropb::size::sizeof_len_record(val_ref.compute_size());\n                }\n                CtrlMsg_::Payload::ReqDisconnectAp(val_ref) => {\n                    let val_ref = &*val_ref;\n                    size += 2usize + ::micropb::size::sizeof_len_record(val_ref.compute_size());\n                }\n                CtrlMsg_::Payload::ReqGetSoftapConfig(val_ref) => {\n                    let val_ref = &*val_ref;\n                    size += 2usize + ::micropb::size::sizeof_len_record(val_ref.compute_size());\n                }\n                CtrlMsg_::Payload::ReqSetSoftapVendorSpecificIe(val_ref) => {\n                    let val_ref = &*val_ref;\n                    size += 2usize + ::micropb::size::sizeof_len_record(val_ref.compute_size());\n                }\n                CtrlMsg_::Payload::ReqStartSoftap(val_ref) => {\n                    let val_ref = &*val_ref;\n                    size += 2usize + ::micropb::size::sizeof_len_record(val_ref.compute_size());\n                }\n                CtrlMsg_::Payload::ReqSoftapConnectedStasList(val_ref) => {\n                    let val_ref = &*val_ref;\n                    size += 2usize + ::micropb::size::sizeof_len_record(val_ref.compute_size());\n                }\n                CtrlMsg_::Payload::ReqStopSoftap(val_ref) => {\n                    let val_ref = &*val_ref;\n                    size += 2usize + ::micropb::size::sizeof_len_record(val_ref.compute_size());\n                }\n                CtrlMsg_::Payload::ReqSetPowerSaveMode(val_ref) => {\n                    let val_ref = &*val_ref;\n                    size += 2usize + ::micropb::size::sizeof_len_record(val_ref.compute_size());\n                }\n                CtrlMsg_::Payload::ReqGetPowerSaveMode(val_ref) => {\n                    let val_ref = &*val_ref;\n                    size += 2usize + ::micropb::size::sizeof_len_record(val_ref.compute_size());\n                }\n                CtrlMsg_::Payload::ReqOtaBegin(val_ref) => {\n                    let val_ref = &*val_ref;\n                    size += 2usize + ::micropb::size::sizeof_len_record(val_ref.compute_size());\n                }\n                CtrlMsg_::Payload::ReqOtaWrite(val_ref) => {\n                    let val_ref = &*val_ref;\n                    size += 2usize + ::micropb::size::sizeof_len_record(val_ref.compute_size());\n                }\n                CtrlMsg_::Payload::ReqOtaEnd(val_ref) => {\n                    let val_ref = &*val_ref;\n                    size += 2usize + ::micropb::size::sizeof_len_record(val_ref.compute_size());\n                }\n                CtrlMsg_::Payload::ReqSetWifiMaxTxPower(val_ref) => {\n                    let val_ref = &*val_ref;\n                    size += 2usize + ::micropb::size::sizeof_len_record(val_ref.compute_size());\n                }\n                CtrlMsg_::Payload::ReqGetWifiCurrTxPower(val_ref) => {\n                    let val_ref = &*val_ref;\n                    size += 2usize + ::micropb::size::sizeof_len_record(val_ref.compute_size());\n                }\n                CtrlMsg_::Payload::ReqConfigHeartbeat(val_ref) => {\n                    let val_ref = &*val_ref;\n                    size += 2usize + ::micropb::size::sizeof_len_record(val_ref.compute_size());\n                }\n                CtrlMsg_::Payload::ReqEnableDisableFeat(val_ref) => {\n                    let val_ref = &*val_ref;\n                    size += 2usize + ::micropb::size::sizeof_len_record(val_ref.compute_size());\n                }\n                CtrlMsg_::Payload::ReqGetFwVersion(val_ref) => {\n                    let val_ref = &*val_ref;\n                    size += 2usize + ::micropb::size::sizeof_len_record(val_ref.compute_size());\n                }\n                CtrlMsg_::Payload::ReqSetCountryCode(val_ref) => {\n                    let val_ref = &*val_ref;\n                    size += 2usize + ::micropb::size::sizeof_len_record(val_ref.compute_size());\n                }\n                CtrlMsg_::Payload::ReqGetCountryCode(val_ref) => {\n                    let val_ref = &*val_ref;\n                    size += 2usize + ::micropb::size::sizeof_len_record(val_ref.compute_size());\n                }\n                CtrlMsg_::Payload::ReqSetDhcpDnsStatus(val_ref) => {\n                    let val_ref = &*val_ref;\n                    size += 2usize + ::micropb::size::sizeof_len_record(val_ref.compute_size());\n                }\n                CtrlMsg_::Payload::ReqGetDhcpDnsStatus(val_ref) => {\n                    let val_ref = &*val_ref;\n                    size += 2usize + ::micropb::size::sizeof_len_record(val_ref.compute_size());\n                }\n                CtrlMsg_::Payload::ReqCustomRpcUnserialisedMsg(val_ref) => {\n                    let val_ref = &*val_ref;\n                    size += 2usize + ::micropb::size::sizeof_len_record(val_ref.compute_size());\n                }\n                CtrlMsg_::Payload::RespGetMacAddress(val_ref) => {\n                    let val_ref = &*val_ref;\n                    size += 2usize + ::micropb::size::sizeof_len_record(val_ref.compute_size());\n                }\n                CtrlMsg_::Payload::RespSetMacAddress(val_ref) => {\n                    let val_ref = &*val_ref;\n                    size += 2usize + ::micropb::size::sizeof_len_record(val_ref.compute_size());\n                }\n                CtrlMsg_::Payload::RespGetWifiMode(val_ref) => {\n                    let val_ref = &*val_ref;\n                    size += 2usize + ::micropb::size::sizeof_len_record(val_ref.compute_size());\n                }\n                CtrlMsg_::Payload::RespSetWifiMode(val_ref) => {\n                    let val_ref = &*val_ref;\n                    size += 2usize + ::micropb::size::sizeof_len_record(val_ref.compute_size());\n                }\n                CtrlMsg_::Payload::RespScanApList(val_ref) => {\n                    let val_ref = &*val_ref;\n                    size += 2usize + ::micropb::size::sizeof_len_record(val_ref.compute_size());\n                }\n                CtrlMsg_::Payload::RespGetApConfig(val_ref) => {\n                    let val_ref = &*val_ref;\n                    size += 2usize + ::micropb::size::sizeof_len_record(val_ref.compute_size());\n                }\n                CtrlMsg_::Payload::RespConnectAp(val_ref) => {\n                    let val_ref = &*val_ref;\n                    size += 2usize + ::micropb::size::sizeof_len_record(val_ref.compute_size());\n                }\n                CtrlMsg_::Payload::RespDisconnectAp(val_ref) => {\n                    let val_ref = &*val_ref;\n                    size += 2usize + ::micropb::size::sizeof_len_record(val_ref.compute_size());\n                }\n                CtrlMsg_::Payload::RespGetSoftapConfig(val_ref) => {\n                    let val_ref = &*val_ref;\n                    size += 2usize + ::micropb::size::sizeof_len_record(val_ref.compute_size());\n                }\n                CtrlMsg_::Payload::RespSetSoftapVendorSpecificIe(val_ref) => {\n                    let val_ref = &*val_ref;\n                    size += 2usize + ::micropb::size::sizeof_len_record(val_ref.compute_size());\n                }\n                CtrlMsg_::Payload::RespStartSoftap(val_ref) => {\n                    let val_ref = &*val_ref;\n                    size += 2usize + ::micropb::size::sizeof_len_record(val_ref.compute_size());\n                }\n                CtrlMsg_::Payload::RespSoftapConnectedStasList(val_ref) => {\n                    let val_ref = &*val_ref;\n                    size += 2usize + ::micropb::size::sizeof_len_record(val_ref.compute_size());\n                }\n                CtrlMsg_::Payload::RespStopSoftap(val_ref) => {\n                    let val_ref = &*val_ref;\n                    size += 2usize + ::micropb::size::sizeof_len_record(val_ref.compute_size());\n                }\n                CtrlMsg_::Payload::RespSetPowerSaveMode(val_ref) => {\n                    let val_ref = &*val_ref;\n                    size += 2usize + ::micropb::size::sizeof_len_record(val_ref.compute_size());\n                }\n                CtrlMsg_::Payload::RespGetPowerSaveMode(val_ref) => {\n                    let val_ref = &*val_ref;\n                    size += 2usize + ::micropb::size::sizeof_len_record(val_ref.compute_size());\n                }\n                CtrlMsg_::Payload::RespOtaBegin(val_ref) => {\n                    let val_ref = &*val_ref;\n                    size += 2usize + ::micropb::size::sizeof_len_record(val_ref.compute_size());\n                }\n                CtrlMsg_::Payload::RespOtaWrite(val_ref) => {\n                    let val_ref = &*val_ref;\n                    size += 2usize + ::micropb::size::sizeof_len_record(val_ref.compute_size());\n                }\n                CtrlMsg_::Payload::RespOtaEnd(val_ref) => {\n                    let val_ref = &*val_ref;\n                    size += 2usize + ::micropb::size::sizeof_len_record(val_ref.compute_size());\n                }\n                CtrlMsg_::Payload::RespSetWifiMaxTxPower(val_ref) => {\n                    let val_ref = &*val_ref;\n                    size += 2usize + ::micropb::size::sizeof_len_record(val_ref.compute_size());\n                }\n                CtrlMsg_::Payload::RespGetWifiCurrTxPower(val_ref) => {\n                    let val_ref = &*val_ref;\n                    size += 2usize + ::micropb::size::sizeof_len_record(val_ref.compute_size());\n                }\n                CtrlMsg_::Payload::RespConfigHeartbeat(val_ref) => {\n                    let val_ref = &*val_ref;\n                    size += 2usize + ::micropb::size::sizeof_len_record(val_ref.compute_size());\n                }\n                CtrlMsg_::Payload::RespEnableDisableFeat(val_ref) => {\n                    let val_ref = &*val_ref;\n                    size += 2usize + ::micropb::size::sizeof_len_record(val_ref.compute_size());\n                }\n                CtrlMsg_::Payload::RespGetFwVersion(val_ref) => {\n                    let val_ref = &*val_ref;\n                    size += 2usize + ::micropb::size::sizeof_len_record(val_ref.compute_size());\n                }\n                CtrlMsg_::Payload::RespSetCountryCode(val_ref) => {\n                    let val_ref = &*val_ref;\n                    size += 2usize + ::micropb::size::sizeof_len_record(val_ref.compute_size());\n                }\n                CtrlMsg_::Payload::RespGetCountryCode(val_ref) => {\n                    let val_ref = &*val_ref;\n                    size += 2usize + ::micropb::size::sizeof_len_record(val_ref.compute_size());\n                }\n                CtrlMsg_::Payload::RespSetDhcpDnsStatus(val_ref) => {\n                    let val_ref = &*val_ref;\n                    size += 2usize + ::micropb::size::sizeof_len_record(val_ref.compute_size());\n                }\n                CtrlMsg_::Payload::RespGetDhcpDnsStatus(val_ref) => {\n                    let val_ref = &*val_ref;\n                    size += 2usize + ::micropb::size::sizeof_len_record(val_ref.compute_size());\n                }\n                CtrlMsg_::Payload::RespCustomRpcUnserialisedMsg(val_ref) => {\n                    let val_ref = &*val_ref;\n                    size += 2usize + ::micropb::size::sizeof_len_record(val_ref.compute_size());\n                }\n                CtrlMsg_::Payload::EventEspInit(val_ref) => {\n                    let val_ref = &*val_ref;\n                    size += 2usize + ::micropb::size::sizeof_len_record(val_ref.compute_size());\n                }\n                CtrlMsg_::Payload::EventHeartbeat(val_ref) => {\n                    let val_ref = &*val_ref;\n                    size += 2usize + ::micropb::size::sizeof_len_record(val_ref.compute_size());\n                }\n                CtrlMsg_::Payload::EventStationDisconnectFromAp(val_ref) => {\n                    let val_ref = &*val_ref;\n                    size += 2usize + ::micropb::size::sizeof_len_record(val_ref.compute_size());\n                }\n                CtrlMsg_::Payload::EventStationDisconnectFromEspSoftAp(val_ref) => {\n                    let val_ref = &*val_ref;\n                    size += 2usize + ::micropb::size::sizeof_len_record(val_ref.compute_size());\n                }\n                CtrlMsg_::Payload::EventStationConnectedToAp(val_ref) => {\n                    let val_ref = &*val_ref;\n                    size += 2usize + ::micropb::size::sizeof_len_record(val_ref.compute_size());\n                }\n                CtrlMsg_::Payload::EventStationConnectedToEspSoftAp(val_ref) => {\n                    let val_ref = &*val_ref;\n                    size += 2usize + ::micropb::size::sizeof_len_record(val_ref.compute_size());\n                }\n                CtrlMsg_::Payload::EventSetDhcpDnsStatus(val_ref) => {\n                    let val_ref = &*val_ref;\n                    size += 2usize + ::micropb::size::sizeof_len_record(val_ref.compute_size());\n                }\n                CtrlMsg_::Payload::EventCustomRpcUnserialisedMsg(val_ref) => {\n                    let val_ref = &*val_ref;\n                    size += 2usize + ::micropb::size::sizeof_len_record(val_ref.compute_size());\n                }\n            }\n        }\n        size\n    }\n}\n/// Inner types for `CtrlMsg`\npub mod CtrlMsg_ {\n    /// union of all msg ids\n    #[derive(Debug, PartialEq, Clone)]\n    #[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\n    pub enum Payload {\n        ///* Requests *\n        ReqGetMacAddress(super::CtrlMsg_Req_GetMacAddress),\n        ReqSetMacAddress(super::CtrlMsg_Req_SetMacAddress),\n        ReqGetWifiMode(super::CtrlMsg_Req_GetMode),\n        ReqSetWifiMode(super::CtrlMsg_Req_SetMode),\n        ReqScanApList(super::CtrlMsg_Req_ScanResult),\n        ReqGetApConfig(super::CtrlMsg_Req_GetAPConfig),\n        ReqConnectAp(super::CtrlMsg_Req_ConnectAP),\n        ReqDisconnectAp(super::CtrlMsg_Req_GetStatus),\n        ReqGetSoftapConfig(super::CtrlMsg_Req_GetSoftAPConfig),\n        ReqSetSoftapVendorSpecificIe(super::CtrlMsg_Req_SetSoftAPVendorSpecificIE),\n        ReqStartSoftap(super::CtrlMsg_Req_StartSoftAP),\n        ReqSoftapConnectedStasList(super::CtrlMsg_Req_SoftAPConnectedSTA),\n        ReqStopSoftap(super::CtrlMsg_Req_GetStatus),\n        ReqSetPowerSaveMode(super::CtrlMsg_Req_SetMode),\n        ReqGetPowerSaveMode(super::CtrlMsg_Req_GetMode),\n        ReqOtaBegin(super::CtrlMsg_Req_OTABegin),\n        ReqOtaWrite(super::CtrlMsg_Req_OTAWrite),\n        ReqOtaEnd(super::CtrlMsg_Req_OTAEnd),\n        ReqSetWifiMaxTxPower(super::CtrlMsg_Req_SetWifiMaxTxPower),\n        ReqGetWifiCurrTxPower(super::CtrlMsg_Req_GetWifiCurrTxPower),\n        ReqConfigHeartbeat(super::CtrlMsg_Req_ConfigHeartbeat),\n        ReqEnableDisableFeat(super::CtrlMsg_Req_EnableDisable),\n        ReqGetFwVersion(super::CtrlMsg_Req_GetFwVersion),\n        ReqSetCountryCode(super::CtrlMsg_Req_SetCountryCode),\n        ReqGetCountryCode(super::CtrlMsg_Req_GetCountryCode),\n        ReqSetDhcpDnsStatus(super::CtrlMsg_Req_SetDhcpDnsStatus),\n        ReqGetDhcpDnsStatus(super::CtrlMsg_Req_GetDhcpDnsStatus),\n        ReqCustomRpcUnserialisedMsg(super::CtrlMsg_Req_CustomRpcUnserialisedMsg),\n        ///* Responses *\n        RespGetMacAddress(super::CtrlMsg_Resp_GetMacAddress),\n        RespSetMacAddress(super::CtrlMsg_Resp_SetMacAddress),\n        RespGetWifiMode(super::CtrlMsg_Resp_GetMode),\n        RespSetWifiMode(super::CtrlMsg_Resp_SetMode),\n        RespScanApList(super::CtrlMsg_Resp_ScanResult),\n        RespGetApConfig(super::CtrlMsg_Resp_GetAPConfig),\n        RespConnectAp(super::CtrlMsg_Resp_ConnectAP),\n        RespDisconnectAp(super::CtrlMsg_Resp_GetStatus),\n        RespGetSoftapConfig(super::CtrlMsg_Resp_GetSoftAPConfig),\n        RespSetSoftapVendorSpecificIe(super::CtrlMsg_Resp_SetSoftAPVendorSpecificIE),\n        RespStartSoftap(super::CtrlMsg_Resp_StartSoftAP),\n        RespSoftapConnectedStasList(super::CtrlMsg_Resp_SoftAPConnectedSTA),\n        RespStopSoftap(super::CtrlMsg_Resp_GetStatus),\n        RespSetPowerSaveMode(super::CtrlMsg_Resp_SetMode),\n        RespGetPowerSaveMode(super::CtrlMsg_Resp_GetMode),\n        RespOtaBegin(super::CtrlMsg_Resp_OTABegin),\n        RespOtaWrite(super::CtrlMsg_Resp_OTAWrite),\n        RespOtaEnd(super::CtrlMsg_Resp_OTAEnd),\n        RespSetWifiMaxTxPower(super::CtrlMsg_Resp_SetWifiMaxTxPower),\n        RespGetWifiCurrTxPower(super::CtrlMsg_Resp_GetWifiCurrTxPower),\n        RespConfigHeartbeat(super::CtrlMsg_Resp_ConfigHeartbeat),\n        RespEnableDisableFeat(super::CtrlMsg_Resp_EnableDisable),\n        RespGetFwVersion(super::CtrlMsg_Resp_GetFwVersion),\n        RespSetCountryCode(super::CtrlMsg_Resp_SetCountryCode),\n        RespGetCountryCode(super::CtrlMsg_Resp_GetCountryCode),\n        RespSetDhcpDnsStatus(super::CtrlMsg_Resp_SetDhcpDnsStatus),\n        RespGetDhcpDnsStatus(super::CtrlMsg_Resp_GetDhcpDnsStatus),\n        RespCustomRpcUnserialisedMsg(super::CtrlMsg_Resp_CustomRpcUnserialisedMsg),\n        ///* Notifications *\n        EventEspInit(super::CtrlMsg_Event_ESPInit),\n        EventHeartbeat(super::CtrlMsg_Event_Heartbeat),\n        EventStationDisconnectFromAp(super::CtrlMsg_Event_StationDisconnectFromAP),\n        EventStationDisconnectFromEspSoftAp(super::CtrlMsg_Event_StationDisconnectFromESPSoftAP),\n        EventStationConnectedToAp(super::CtrlMsg_Event_StationConnectedToAP),\n        EventStationConnectedToEspSoftAp(super::CtrlMsg_Event_StationConnectedToESPSoftAP),\n        EventSetDhcpDnsStatus(super::CtrlMsg_Event_SetDhcpDnsStatus),\n        EventCustomRpcUnserialisedMsg(super::CtrlMsg_Event_CustomRpcUnserialisedMsg),\n    }\n}\n/// Enums similar to ESP IDF\n#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]\n#[repr(transparent)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub struct Ctrl_VendorIEType(pub i32);\nimpl Ctrl_VendorIEType {\n    /// Maximum encoded size of the enum\n    pub const _MAX_SIZE: usize = 10usize;\n    pub const Beacon: Self = Self(0);\n    pub const ProbeReq: Self = Self(1);\n    pub const ProbeResp: Self = Self(2);\n    pub const AssocReq: Self = Self(3);\n    pub const AssocResp: Self = Self(4);\n}\nimpl core::default::Default for Ctrl_VendorIEType {\n    fn default() -> Self {\n        Self(0)\n    }\n}\nimpl core::convert::From<i32> for Ctrl_VendorIEType {\n    fn from(val: i32) -> Self {\n        Self(val)\n    }\n}\n#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]\n#[repr(transparent)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub struct Ctrl_VendorIEID(pub i32);\nimpl Ctrl_VendorIEID {\n    /// Maximum encoded size of the enum\n    pub const _MAX_SIZE: usize = 10usize;\n    pub const Id0: Self = Self(0);\n    pub const Id1: Self = Self(1);\n}\nimpl core::default::Default for Ctrl_VendorIEID {\n    fn default() -> Self {\n        Self(0)\n    }\n}\nimpl core::convert::From<i32> for Ctrl_VendorIEID {\n    fn from(val: i32) -> Self {\n        Self(val)\n    }\n}\n#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]\n#[repr(transparent)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub struct Ctrl_WifiMode(pub i32);\nimpl Ctrl_WifiMode {\n    /// Maximum encoded size of the enum\n    pub const _MAX_SIZE: usize = 10usize;\n    pub const None: Self = Self(0);\n    pub const Sta: Self = Self(1);\n    pub const Ap: Self = Self(2);\n    pub const Apsta: Self = Self(3);\n}\nimpl core::default::Default for Ctrl_WifiMode {\n    fn default() -> Self {\n        Self(0)\n    }\n}\nimpl core::convert::From<i32> for Ctrl_WifiMode {\n    fn from(val: i32) -> Self {\n        Self(val)\n    }\n}\n#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]\n#[repr(transparent)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub struct Ctrl_WifiBw(pub i32);\nimpl Ctrl_WifiBw {\n    /// Maximum encoded size of the enum\n    pub const _MAX_SIZE: usize = 10usize;\n    pub const BwInvalid: Self = Self(0);\n    pub const Ht20: Self = Self(1);\n    pub const Ht40: Self = Self(2);\n}\nimpl core::default::Default for Ctrl_WifiBw {\n    fn default() -> Self {\n        Self(0)\n    }\n}\nimpl core::convert::From<i32> for Ctrl_WifiBw {\n    fn from(val: i32) -> Self {\n        Self(val)\n    }\n}\n#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]\n#[repr(transparent)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub struct Ctrl_WifiPowerSave(pub i32);\nimpl Ctrl_WifiPowerSave {\n    /// Maximum encoded size of the enum\n    pub const _MAX_SIZE: usize = 10usize;\n    pub const NoPs: Self = Self(0);\n    pub const MinModem: Self = Self(1);\n    pub const MaxModem: Self = Self(2);\n    pub const PsInvalid: Self = Self(3);\n}\nimpl core::default::Default for Ctrl_WifiPowerSave {\n    fn default() -> Self {\n        Self(0)\n    }\n}\nimpl core::convert::From<i32> for Ctrl_WifiPowerSave {\n    fn from(val: i32) -> Self {\n        Self(val)\n    }\n}\n#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]\n#[repr(transparent)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub struct Ctrl_WifiSecProt(pub i32);\nimpl Ctrl_WifiSecProt {\n    /// Maximum encoded size of the enum\n    pub const _MAX_SIZE: usize = 10usize;\n    pub const Open: Self = Self(0);\n    pub const Wep: Self = Self(1);\n    pub const WpaPsk: Self = Self(2);\n    pub const Wpa2Psk: Self = Self(3);\n    pub const WpaWpa2Psk: Self = Self(4);\n    pub const Wpa2Enterprise: Self = Self(5);\n    pub const Wpa3Psk: Self = Self(6);\n    pub const Wpa2Wpa3Psk: Self = Self(7);\n}\nimpl core::default::Default for Ctrl_WifiSecProt {\n    fn default() -> Self {\n        Self(0)\n    }\n}\nimpl core::convert::From<i32> for Ctrl_WifiSecProt {\n    fn from(val: i32) -> Self {\n        Self(val)\n    }\n}\n/// enums for Control path\n#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]\n#[repr(transparent)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub struct Ctrl_Status(pub i32);\nimpl Ctrl_Status {\n    /// Maximum encoded size of the enum\n    pub const _MAX_SIZE: usize = 10usize;\n    pub const Connected: Self = Self(0);\n    pub const NotConnected: Self = Self(1);\n    pub const NoApFound: Self = Self(2);\n    pub const ConnectionFail: Self = Self(3);\n    pub const InvalidArgument: Self = Self(4);\n    pub const OutOfRange: Self = Self(5);\n}\nimpl core::default::Default for Ctrl_Status {\n    fn default() -> Self {\n        Self(0)\n    }\n}\nimpl core::convert::From<i32> for Ctrl_Status {\n    fn from(val: i32) -> Self {\n        Self(val)\n    }\n}\n#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]\n#[repr(transparent)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub struct CtrlMsgType(pub i32);\nimpl CtrlMsgType {\n    /// Maximum encoded size of the enum\n    pub const _MAX_SIZE: usize = 10usize;\n    pub const MsgTypeInvalid: Self = Self(0);\n    pub const Req: Self = Self(1);\n    pub const Resp: Self = Self(2);\n    pub const Event: Self = Self(3);\n    pub const MsgTypeMax: Self = Self(4);\n}\nimpl core::default::Default for CtrlMsgType {\n    fn default() -> Self {\n        Self(0)\n    }\n}\nimpl core::convert::From<i32> for CtrlMsgType {\n    fn from(val: i32) -> Self {\n        Self(val)\n    }\n}\n#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]\n#[repr(transparent)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub struct CtrlMsgId(pub i32);\nimpl CtrlMsgId {\n    /// Maximum encoded size of the enum\n    pub const _MAX_SIZE: usize = 10usize;\n    pub const MsgIdInvalid: Self = Self(0);\n    ///* Request Msgs *\n    pub const ReqBase: Self = Self(100);\n    pub const ReqGetMacAddress: Self = Self(101);\n    pub const ReqSetMacAddress: Self = Self(102);\n    pub const ReqGetWifiMode: Self = Self(103);\n    pub const ReqSetWifiMode: Self = Self(104);\n    pub const ReqGetApScanList: Self = Self(105);\n    pub const ReqGetApConfig: Self = Self(106);\n    pub const ReqConnectAp: Self = Self(107);\n    pub const ReqDisconnectAp: Self = Self(108);\n    pub const ReqGetSoftApConfig: Self = Self(109);\n    pub const ReqSetSoftApVendorSpecificIe: Self = Self(110);\n    pub const ReqStartSoftAp: Self = Self(111);\n    pub const ReqGetSoftApConnectedStaList: Self = Self(112);\n    pub const ReqStopSoftAp: Self = Self(113);\n    pub const ReqSetPowerSaveMode: Self = Self(114);\n    pub const ReqGetPowerSaveMode: Self = Self(115);\n    pub const ReqOtaBegin: Self = Self(116);\n    pub const ReqOtaWrite: Self = Self(117);\n    pub const ReqOtaEnd: Self = Self(118);\n    pub const ReqSetWifiMaxTxPower: Self = Self(119);\n    pub const ReqGetWifiCurrTxPower: Self = Self(120);\n    pub const ReqConfigHeartbeat: Self = Self(121);\n    pub const ReqEnableDisable: Self = Self(122);\n    pub const ReqGetFwVersion: Self = Self(123);\n    pub const ReqSetCountryCode: Self = Self(124);\n    pub const ReqGetCountryCode: Self = Self(125);\n    pub const ReqSetDhcpDnsStatus: Self = Self(126);\n    pub const ReqGetDhcpDnsStatus: Self = Self(127);\n    pub const ReqCustomRpcUnserialisedMsg: Self = Self(128);\n    /// Add new control path command response before Req_Max\n    /// and update Req_Max\n    pub const ReqMax: Self = Self(129);\n    ///* Response Msgs *\n    pub const RespBase: Self = Self(200);\n    pub const RespGetMacAddress: Self = Self(201);\n    pub const RespSetMacAddress: Self = Self(202);\n    pub const RespGetWifiMode: Self = Self(203);\n    pub const RespSetWifiMode: Self = Self(204);\n    pub const RespGetApScanList: Self = Self(205);\n    pub const RespGetApConfig: Self = Self(206);\n    pub const RespConnectAp: Self = Self(207);\n    pub const RespDisconnectAp: Self = Self(208);\n    pub const RespGetSoftApConfig: Self = Self(209);\n    pub const RespSetSoftApVendorSpecificIe: Self = Self(210);\n    pub const RespStartSoftAp: Self = Self(211);\n    pub const RespGetSoftApConnectedStaList: Self = Self(212);\n    pub const RespStopSoftAp: Self = Self(213);\n    pub const RespSetPowerSaveMode: Self = Self(214);\n    pub const RespGetPowerSaveMode: Self = Self(215);\n    pub const RespOtaBegin: Self = Self(216);\n    pub const RespOtaWrite: Self = Self(217);\n    pub const RespOtaEnd: Self = Self(218);\n    pub const RespSetWifiMaxTxPower: Self = Self(219);\n    pub const RespGetWifiCurrTxPower: Self = Self(220);\n    pub const RespConfigHeartbeat: Self = Self(221);\n    pub const RespEnableDisable: Self = Self(222);\n    pub const RespGetFwVersion: Self = Self(223);\n    pub const RespSetCountryCode: Self = Self(224);\n    pub const RespGetCountryCode: Self = Self(225);\n    pub const RespSetDhcpDnsStatus: Self = Self(226);\n    pub const RespGetDhcpDnsStatus: Self = Self(227);\n    pub const RespCustomRpcUnserialisedMsg: Self = Self(228);\n    /// Add new control path command response before Resp_Max\n    /// and update Resp_Max\n    pub const RespMax: Self = Self(229);\n    ///* Event Msgs *\n    pub const EventBase: Self = Self(300);\n    pub const EventEspInit: Self = Self(301);\n    pub const EventHeartbeat: Self = Self(302);\n    pub const EventStationDisconnectFromAp: Self = Self(303);\n    pub const EventStationDisconnectFromEspSoftAp: Self = Self(304);\n    pub const EventStationConnectedToAp: Self = Self(305);\n    pub const EventStationConnectedToEspSoftAp: Self = Self(306);\n    pub const EventSetDhcpDnsStatus: Self = Self(307);\n    pub const EventCustomRpcUnserialisedMsg: Self = Self(308);\n    /// Add new control path command notification before Event_Max\n    /// and update Event_Max\n    pub const EventMax: Self = Self(309);\n}\nimpl core::default::Default for CtrlMsgId {\n    fn default() -> Self {\n        Self(0)\n    }\n}\nimpl core::convert::From<i32> for CtrlMsgId {\n    fn from(val: i32) -> Self {\n        Self(val)\n    }\n}\n#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]\n#[repr(transparent)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub struct HostedFeature(pub i32);\nimpl HostedFeature {\n    /// Maximum encoded size of the enum\n    pub const _MAX_SIZE: usize = 10usize;\n    pub const HostedInvalidFeature: Self = Self(0);\n    pub const HostedWifi: Self = Self(1);\n    pub const HostedBluetooth: Self = Self(2);\n    pub const HostedIsNetworkSplitOn: Self = Self(3);\n}\nimpl core::default::Default for HostedFeature {\n    fn default() -> Self {\n        Self(0)\n    }\n}\nimpl core::convert::From<i32> for HostedFeature {\n    fn from(val: i32) -> Self {\n        Self(val)\n    }\n}\n"
  },
  {
    "path": "embassy-net-nrf91/CHANGELOG.md",
    "content": "# Changelog\n\nAll notable changes to this project will be documented in this file.\n\nThe format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),\nand this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).\n\n<!-- next-header -->\n## Unreleased - ReleaseDate\n\n## 0.2.0 - 2026-03-10\n\n- Signal link state based on link attach to prevent too early transmit confusing LTE modem.\n- Upgrade embassy-sync to 0.8.0\n- Update embassy-net-driver-channel to 0.4.0\n\n## 0.1.0 - 2025-12-15\n\n- Initial release\n"
  },
  {
    "path": "embassy-net-nrf91/Cargo.toml",
    "content": "[package]\nname = \"embassy-net-nrf91\"\nversion = \"0.2.0\"\nedition = \"2024\"\ndescription = \"embassy-net driver for Nordic nRF91-series cellular modems\"\nkeywords = [\"embedded\", \"nrf91\", \"embassy-net\", \"cellular\"]\ncategories = [\"embedded\", \"hardware-support\", \"no-std\", \"network-programming\", \"asynchronous\"]\nlicense = \"MIT OR Apache-2.0\"\nrepository = \"https://github.com/embassy-rs/embassy\"\ndocumentation = \"https://docs.embassy.dev/embassy-net-nrf91\"\npublish = true\n\n[features]\ndefmt = [\"dep:defmt\", \"heapless/defmt\", \"embassy-time/defmt\"]\nlog = [\"dep:log\"]\n\n[dependencies]\ndefmt = { version = \"1.0.1\", optional = true }\nlog = { version = \"0.4.14\", optional = true }\n\n# nrf-pac = { version = \"0.2.0\", git = \"https://github.com/embassy-rs/nrf-pac.git\", rev = \"074935b9b24ab302fe812f91725937f57cd082cf\" }\nnrf-pac = \"0.3.0\"\ncortex-m = \"0.7.7\"\n\nembassy-time = { version = \"0.5.1\", path = \"../embassy-time\" }\nembassy-sync = { version = \"0.8.0\", path = \"../embassy-sync\" }\nembassy-futures = { version = \"0.1.2\", path = \"../embassy-futures\" }\nembassy-net-driver-channel = { version = \"0.4.0\", path = \"../embassy-net-driver-channel\" }\n\nheapless = \"0.9\"\nembedded-io = { version = \"0.7.1\" }\nat-commands = \"0.5.4\"\n\n[package.metadata.embassy]\nbuild = [\n    {target = \"thumbv7em-none-eabi\", features = [\"defmt\", \"nrf-pac/nrf9160\"]}\n]\n\n[package.metadata.embassy_docs]\nsrc_base = \"https://github.com/embassy-rs/embassy/blob/embassy-net-nrf91-v$VERSION/embassy-net-nrf91/src/\"\nsrc_base_git = \"https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-net-nrf91/src/\"\ntarget = \"thumbv7em-none-eabi\"\nfeatures = [\"defmt\", \"nrf-pac/nrf9160\"]\n\n[package.metadata.docs.rs]\nfeatures = [\"defmt\", \"nrf-pac/nrf9160\"]\n"
  },
  {
    "path": "embassy-net-nrf91/README.md",
    "content": "# nRF91 `embassy-net` integration\n\n[`embassy-net`](https://crates.io/crates/embassy-net) driver for Nordic nRF91-series cellular modems.\n\nSee the [`examples`](https://github.com/embassy-rs/embassy/tree/main/examples/nrf9160) directory for usage examples with the nRF9160.\n\n## Interoperability\n\nThis crate can run on any executor.\n"
  },
  {
    "path": "embassy-net-nrf91/src/context.rs",
    "content": "//! Helper utility to configure a specific modem context.\nuse core::net::IpAddr;\nuse core::str::FromStr;\n\nuse at_commands::builder::CommandBuilder;\nuse at_commands::parser::CommandParser;\nuse embassy_net_driver_channel::driver::LinkState;\nuse embassy_time::{Duration, Timer};\nuse heapless::Vec;\n\n/// Provides a higher level API for controlling a given context.\npub struct Control<'a> {\n    control: crate::Control<'a>,\n    cid: u8,\n}\n\n/// Configuration for a given context\npub struct Config<'a> {\n    /// Desired APN address.\n    pub apn: &'a [u8],\n    /// Desired authentication protocol.\n    pub auth_prot: AuthProt,\n    /// Credentials.\n    pub auth: Option<(&'a [u8], &'a [u8])>,\n    /// SIM pin\n    pub pin: Option<&'a [u8]>,\n}\n\n/// Authentication protocol.\n#[derive(Clone, Copy, PartialEq, Debug)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\n#[repr(u8)]\npub enum AuthProt {\n    /// No authentication.\n    None = 0,\n    /// PAP authentication.\n    Pap = 1,\n    /// CHAP authentication.\n    Chap = 2,\n}\n\n/// Error returned by control.\n#[derive(Clone, Copy, PartialEq, Debug)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub enum Error {\n    /// Not enough space for command.\n    BufferTooSmall,\n    /// Error parsing response from modem.\n    AtParseError,\n    /// Error parsing IP addresses.\n    AddrParseError,\n}\n\nimpl From<at_commands::parser::ParseError> for Error {\n    fn from(_: at_commands::parser::ParseError) -> Self {\n        Self::AtParseError\n    }\n}\n\n/// Status of a given context.\n#[derive(PartialEq, Debug)]\npub struct Status {\n    /// Attached to APN or not.\n    pub attached: bool,\n    /// IP if assigned.\n    pub ip: Option<IpAddr>,\n    /// Gateway if assigned.\n    pub gateway: Option<IpAddr>,\n    /// DNS servers if assigned.\n    pub dns: Vec<IpAddr, 2>,\n}\n\n#[cfg(feature = \"defmt\")]\nimpl defmt::Format for Status {\n    fn format(&self, f: defmt::Formatter<'_>) {\n        defmt::write!(f, \"attached: {}\", self.attached);\n        if let Some(ip) = &self.ip {\n            defmt::write!(f, \", ip: {}\", defmt::Debug2Format(&ip));\n        }\n    }\n}\n\nimpl<'a> Control<'a> {\n    /// Create a new instance of a control handle for a given context.\n    ///\n    /// Will wait for the modem to be initialized if not.\n    pub async fn new(control: crate::Control<'a>, cid: u8) -> Self {\n        control.wait_init().await;\n        Self { control, cid }\n    }\n\n    /// Perform a raw AT command\n    pub async fn at_command(&self, req: &[u8], resp: &mut [u8]) -> usize {\n        self.control.at_command(req, resp).await\n    }\n\n    /// Configures the modem with the provided config.\n    ///\n    /// NOTE: This will disconnect the modem from any current APN and should not\n    /// be called if the configuration has not been changed.\n    ///\n    /// After configuring, invoke [`enable()`] to activate the configuration.\n    pub async fn configure(&self, config: &Config<'_>) -> Result<(), Error> {\n        let mut cmd: [u8; 256] = [0; 256];\n        let mut buf: [u8; 256] = [0; 256];\n\n        let op = CommandBuilder::create_set(&mut cmd, true)\n            .named(\"+CFUN\")\n            .with_int_parameter(0)\n            .finish()\n            .map_err(|_| Error::BufferTooSmall)?;\n        let n = self.control.at_command(op, &mut buf).await;\n        CommandParser::parse(&buf[..n]).expect_identifier(b\"OK\").finish()?;\n\n        let op = CommandBuilder::create_set(&mut cmd, true)\n            .named(\"+CGDCONT\")\n            .with_int_parameter(self.cid)\n            .with_string_parameter(\"IP\")\n            .with_string_parameter(config.apn)\n            .finish()\n            .map_err(|_| Error::BufferTooSmall)?;\n        let n = self.control.at_command(op, &mut buf).await;\n        // info!(\"RES1: {}\", unsafe { core::str::from_utf8_unchecked(&buf[..n]) });\n        CommandParser::parse(&buf[..n]).expect_identifier(b\"OK\").finish()?;\n\n        let mut op = CommandBuilder::create_set(&mut cmd, true)\n            .named(\"+CGAUTH\")\n            .with_int_parameter(self.cid)\n            .with_int_parameter(config.auth_prot as u8);\n        if let Some((username, password)) = config.auth {\n            op = op.with_string_parameter(username).with_string_parameter(password);\n        }\n        let op = op.finish().map_err(|_| Error::BufferTooSmall)?;\n\n        let n = self.control.at_command(op, &mut buf).await;\n        // info!(\"RES2: {}\", unsafe { core::str::from_utf8_unchecked(&buf[..n]) });\n        CommandParser::parse(&buf[..n]).expect_identifier(b\"OK\").finish()?;\n\n        if let Some(pin) = config.pin {\n            let op = CommandBuilder::create_set(&mut cmd, true)\n                .named(\"+CPIN\")\n                .with_string_parameter(pin)\n                .finish()\n                .map_err(|_| Error::BufferTooSmall)?;\n            let _ = self.control.at_command(op, &mut buf).await;\n            // Ignore ERROR which means no pin required\n        }\n\n        Ok(())\n    }\n\n    /// Attach to the PDN\n    pub async fn attach(&self) -> Result<(), Error> {\n        let mut cmd: [u8; 256] = [0; 256];\n        let mut buf: [u8; 256] = [0; 256];\n        let op = CommandBuilder::create_set(&mut cmd, true)\n            .named(\"+CGATT\")\n            .with_int_parameter(1)\n            .finish()\n            .map_err(|_| Error::BufferTooSmall)?;\n        let n = self.control.at_command(op, &mut buf).await;\n        CommandParser::parse(&buf[..n]).expect_identifier(b\"OK\").finish()?;\n        Ok(())\n    }\n\n    /// Read current connectivity status for modem.\n    pub async fn detach(&self) -> Result<(), Error> {\n        let mut cmd: [u8; 256] = [0; 256];\n        let mut buf: [u8; 256] = [0; 256];\n        let op = CommandBuilder::create_set(&mut cmd, true)\n            .named(\"+CGATT\")\n            .with_int_parameter(0)\n            .finish()\n            .map_err(|_| Error::BufferTooSmall)?;\n        let n = self.control.at_command(op, &mut buf).await;\n        CommandParser::parse(&buf[..n]).expect_identifier(b\"OK\").finish()?;\n        Ok(())\n    }\n\n    async fn attached(&self) -> Result<bool, Error> {\n        let mut cmd: [u8; 256] = [0; 256];\n        let mut buf: [u8; 256] = [0; 256];\n\n        let op = CommandBuilder::create_query(&mut cmd, true)\n            .named(\"+CGATT\")\n            .finish()\n            .map_err(|_| Error::BufferTooSmall)?;\n        let n = self.control.at_command(op, &mut buf).await;\n        let (res,) = CommandParser::parse(&buf[..n])\n            .expect_identifier(b\"+CGATT: \")\n            .expect_int_parameter()\n            .expect_identifier(b\"\\r\\nOK\")\n            .finish()?;\n        Ok(res == 1)\n    }\n\n    /// Read current connectivity status for modem.\n    pub async fn status(&self) -> Result<Status, Error> {\n        let mut cmd: [u8; 256] = [0; 256];\n        let mut buf: [u8; 256] = [0; 256];\n\n        let op = CommandBuilder::create_query(&mut cmd, true)\n            .named(\"+CGATT\")\n            .finish()\n            .map_err(|_| Error::BufferTooSmall)?;\n        let n = self.control.at_command(op, &mut buf).await;\n        let (res,) = CommandParser::parse(&buf[..n])\n            .expect_identifier(b\"+CGATT: \")\n            .expect_int_parameter()\n            .expect_identifier(b\"\\r\\nOK\")\n            .finish()?;\n        let attached = res == 1;\n        if !attached {\n            return Ok(Status {\n                attached,\n                ip: None,\n                gateway: None,\n                dns: Vec::new(),\n            });\n        }\n\n        let op = CommandBuilder::create_set(&mut cmd, true)\n            .named(\"+CGPADDR\")\n            .with_int_parameter(self.cid)\n            .finish()\n            .map_err(|_| Error::BufferTooSmall)?;\n        let n = self.control.at_command(op, &mut buf).await;\n        let (_, ip1, _ip2) = CommandParser::parse(&buf[..n])\n            .expect_identifier(b\"+CGPADDR: \")\n            .expect_int_parameter()\n            .expect_optional_string_parameter()\n            .expect_optional_string_parameter()\n            .expect_identifier(b\"\\r\\nOK\")\n            .finish()?;\n\n        let ip = if let Some(ip) = ip1 {\n            let ip = IpAddr::from_str(ip).map_err(|_| Error::AddrParseError)?;\n            Some(ip)\n        } else {\n            None\n        };\n\n        let op = CommandBuilder::create_set(&mut cmd, true)\n            .named(\"+CGCONTRDP\")\n            .with_int_parameter(self.cid)\n            .finish()\n            .map_err(|_| Error::BufferTooSmall)?;\n        let n = self.control.at_command(op, &mut buf).await;\n        let (_cid, _bid, _apn, _mask, gateway, dns1, dns2, _, _, _, _, _mtu) = CommandParser::parse(&buf[..n])\n            .expect_identifier(b\"+CGCONTRDP: \")\n            .expect_int_parameter()\n            .expect_optional_int_parameter()\n            .expect_optional_string_parameter()\n            .expect_optional_string_parameter()\n            .expect_optional_string_parameter()\n            .expect_optional_string_parameter()\n            .expect_optional_string_parameter()\n            .expect_optional_int_parameter()\n            .expect_optional_int_parameter()\n            .expect_optional_int_parameter()\n            .expect_optional_int_parameter()\n            .expect_optional_int_parameter()\n            .expect_identifier(b\"\\r\\nOK\")\n            .finish()?;\n\n        let gateway = if let Some(ip) = gateway {\n            if ip.is_empty() {\n                None\n            } else {\n                Some(IpAddr::from_str(ip).map_err(|_| Error::AddrParseError)?)\n            }\n        } else {\n            None\n        };\n\n        let mut dns = Vec::new();\n        if let Some(ip) = dns1 {\n            dns.push(IpAddr::from_str(ip).map_err(|_| Error::AddrParseError)?)\n                .unwrap();\n        }\n\n        if let Some(ip) = dns2 {\n            dns.push(IpAddr::from_str(ip).map_err(|_| Error::AddrParseError)?)\n                .unwrap();\n        }\n\n        Ok(Status {\n            attached,\n            ip,\n            gateway,\n            dns,\n        })\n    }\n\n    async fn wait_attached(&self) -> Result<Status, Error> {\n        while !self.attached().await? {\n            Timer::after(Duration::from_secs(1)).await;\n        }\n        let status = self.status().await?;\n        Ok(status)\n    }\n\n    /// Disable modem\n    pub async fn disable(&self) -> Result<(), Error> {\n        let mut cmd: [u8; 256] = [0; 256];\n        let mut buf: [u8; 256] = [0; 256];\n\n        let op = CommandBuilder::create_set(&mut cmd, true)\n            .named(\"+CFUN\")\n            .with_int_parameter(0)\n            .finish()\n            .map_err(|_| Error::BufferTooSmall)?;\n        let n = self.control.at_command(op, &mut buf).await;\n        CommandParser::parse(&buf[..n]).expect_identifier(b\"OK\").finish()?;\n\n        Ok(())\n    }\n\n    /// Enable modem\n    pub async fn enable(&self) -> Result<(), Error> {\n        let mut cmd: [u8; 256] = [0; 256];\n        let mut buf: [u8; 256] = [0; 256];\n\n        let op = CommandBuilder::create_set(&mut cmd, true)\n            .named(\"+CFUN\")\n            .with_int_parameter(1)\n            .finish()\n            .map_err(|_| Error::BufferTooSmall)?;\n        let n = self.control.at_command(op, &mut buf).await;\n        CommandParser::parse(&buf[..n]).expect_identifier(b\"OK\").finish()?;\n\n        // Make modem survive PDN detaches\n        let op = CommandBuilder::create_set(&mut cmd, true)\n            .named(\"%XPDNCFG\")\n            .with_int_parameter(1)\n            .finish()\n            .map_err(|_| Error::BufferTooSmall)?;\n        let n = self.control.at_command(op, &mut buf).await;\n        CommandParser::parse(&buf[..n]).expect_identifier(b\"OK\").finish()?;\n        Ok(())\n    }\n\n    /// Run a control loop for this context, ensuring that reaattach is handled.\n    pub async fn run<F: Fn(&Status)>(&self, reattach: F) -> Result<(), Error> {\n        self.enable().await?;\n        let status = self.wait_attached().await?;\n        self.control.set_link_state(LinkState::Up);\n        let mut fd = self.control.open_raw_socket().await;\n        reattach(&status);\n\n        loop {\n            if !self.attached().await? {\n                self.control.set_link_state(LinkState::Down);\n                trace!(\"detached\");\n\n                self.control.close_raw_socket(fd).await;\n                let status = self.wait_attached().await?;\n                trace!(\"attached\");\n                self.control.set_link_state(LinkState::Up);\n                fd = self.control.open_raw_socket().await;\n                reattach(&status);\n            }\n            Timer::after(Duration::from_secs(10)).await;\n        }\n    }\n}\n"
  },
  {
    "path": "embassy-net-nrf91/src/fmt.rs",
    "content": "#![macro_use]\n#![allow(unused)]\n\nuse core::fmt::{Debug, Display, LowerHex};\n\n#[cfg(all(feature = \"defmt\", feature = \"log\"))]\ncompile_error!(\"You may not enable both `defmt` and `log` features.\");\n\n#[collapse_debuginfo(yes)]\nmacro_rules! assert {\n    ($($x:tt)*) => {\n        {\n            #[cfg(not(feature = \"defmt\"))]\n            ::core::assert!($($x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::assert!($($x)*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! assert_eq {\n    ($($x:tt)*) => {\n        {\n            #[cfg(not(feature = \"defmt\"))]\n            ::core::assert_eq!($($x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::assert_eq!($($x)*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! assert_ne {\n    ($($x:tt)*) => {\n        {\n            #[cfg(not(feature = \"defmt\"))]\n            ::core::assert_ne!($($x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::assert_ne!($($x)*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! debug_assert {\n    ($($x:tt)*) => {\n        {\n            #[cfg(not(feature = \"defmt\"))]\n            ::core::debug_assert!($($x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::debug_assert!($($x)*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! debug_assert_eq {\n    ($($x:tt)*) => {\n        {\n            #[cfg(not(feature = \"defmt\"))]\n            ::core::debug_assert_eq!($($x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::debug_assert_eq!($($x)*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! debug_assert_ne {\n    ($($x:tt)*) => {\n        {\n            #[cfg(not(feature = \"defmt\"))]\n            ::core::debug_assert_ne!($($x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::debug_assert_ne!($($x)*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! todo {\n    ($($x:tt)*) => {\n        {\n            #[cfg(not(feature = \"defmt\"))]\n            ::core::todo!($($x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::todo!($($x)*);\n        }\n    };\n}\n\n#[cfg(not(feature = \"defmt\"))]\n#[collapse_debuginfo(yes)]\nmacro_rules! unreachable {\n    ($($x:tt)*) => {\n        ::core::unreachable!($($x)*)\n    };\n}\n\n#[cfg(feature = \"defmt\")]\n#[collapse_debuginfo(yes)]\nmacro_rules! unreachable {\n    ($($x:tt)*) => {\n        ::defmt::unreachable!($($x)*)\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! panic {\n    ($($x:tt)*) => {\n        {\n            #[cfg(not(feature = \"defmt\"))]\n            ::core::panic!($($x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::panic!($($x)*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! trace {\n    ($s:literal $(, $x:expr)* $(,)?) => {\n        {\n            #[cfg(feature = \"log\")]\n            ::log::trace!($s $(, $x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::trace!($s $(, $x)*);\n            #[cfg(not(any(feature = \"log\", feature=\"defmt\")))]\n            let _ = ($( & $x ),*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! debug {\n    ($s:literal $(, $x:expr)* $(,)?) => {\n        {\n            #[cfg(feature = \"log\")]\n            ::log::debug!($s $(, $x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::debug!($s $(, $x)*);\n            #[cfg(not(any(feature = \"log\", feature=\"defmt\")))]\n            let _ = ($( & $x ),*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! info {\n    ($s:literal $(, $x:expr)* $(,)?) => {\n        {\n            #[cfg(feature = \"log\")]\n            ::log::info!($s $(, $x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::info!($s $(, $x)*);\n            #[cfg(not(any(feature = \"log\", feature=\"defmt\")))]\n            let _ = ($( & $x ),*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! warn {\n    ($s:literal $(, $x:expr)* $(,)?) => {\n        {\n            #[cfg(feature = \"log\")]\n            ::log::warn!($s $(, $x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::warn!($s $(, $x)*);\n            #[cfg(not(any(feature = \"log\", feature=\"defmt\")))]\n            let _ = ($( & $x ),*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! error {\n    ($s:literal $(, $x:expr)* $(,)?) => {\n        {\n            #[cfg(feature = \"log\")]\n            ::log::error!($s $(, $x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::error!($s $(, $x)*);\n            #[cfg(not(any(feature = \"log\", feature=\"defmt\")))]\n            let _ = ($( & $x ),*);\n        }\n    };\n}\n\n#[cfg(feature = \"defmt\")]\n#[collapse_debuginfo(yes)]\nmacro_rules! unwrap {\n    ($($x:tt)*) => {\n        ::defmt::unwrap!($($x)*)\n    };\n}\n\n#[cfg(not(feature = \"defmt\"))]\n#[collapse_debuginfo(yes)]\nmacro_rules! unwrap {\n    ($arg:expr) => {\n        match $crate::fmt::Try::into_result($arg) {\n            ::core::result::Result::Ok(t) => t,\n            ::core::result::Result::Err(e) => {\n                ::core::panic!(\"unwrap of `{}` failed: {:?}\", ::core::stringify!($arg), e);\n            }\n        }\n    };\n    ($arg:expr, $($msg:expr),+ $(,)? ) => {\n        match $crate::fmt::Try::into_result($arg) {\n            ::core::result::Result::Ok(t) => t,\n            ::core::result::Result::Err(e) => {\n                ::core::panic!(\"unwrap of `{}` failed: {}: {:?}\", ::core::stringify!($arg), ::core::format_args!($($msg,)*), e);\n            }\n        }\n    }\n}\n\n#[derive(Debug, Copy, Clone, Eq, PartialEq)]\npub struct NoneError;\n\npub trait Try {\n    type Ok;\n    type Error;\n    fn into_result(self) -> Result<Self::Ok, Self::Error>;\n}\n\nimpl<T> Try for Option<T> {\n    type Ok = T;\n    type Error = NoneError;\n\n    #[inline]\n    fn into_result(self) -> Result<T, NoneError> {\n        self.ok_or(NoneError)\n    }\n}\n\nimpl<T, E> Try for Result<T, E> {\n    type Ok = T;\n    type Error = E;\n\n    #[inline]\n    fn into_result(self) -> Self {\n        self\n    }\n}\n\npub(crate) struct Bytes<'a>(pub &'a [u8]);\n\nimpl<'a> Debug for Bytes<'a> {\n    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {\n        write!(f, \"{:#02x?}\", self.0)\n    }\n}\n\nimpl<'a> Display for Bytes<'a> {\n    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {\n        write!(f, \"{:#02x?}\", self.0)\n    }\n}\n\nimpl<'a> LowerHex for Bytes<'a> {\n    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {\n        write!(f, \"{:#02x?}\", self.0)\n    }\n}\n\n#[cfg(feature = \"defmt\")]\nimpl<'a> defmt::Format for Bytes<'a> {\n    fn format(&self, fmt: defmt::Formatter) {\n        defmt::write!(fmt, \"{:02x}\", self.0)\n    }\n}\n"
  },
  {
    "path": "embassy-net-nrf91/src/lib.rs",
    "content": "#![no_std]\n#![allow(unsafe_op_in_unsafe_fn)]\n#![doc = include_str!(\"../README.md\")]\n#![warn(missing_docs)]\n#![deny(unused_must_use)]\n\n// must be first\nmod fmt;\n\npub mod context;\n\nuse core::cell::RefCell;\nuse core::future::{Future, poll_fn};\nuse core::marker::PhantomData;\nuse core::mem::{self, MaybeUninit};\nuse core::ptr::{self, addr_of, addr_of_mut, copy_nonoverlapping};\nuse core::slice;\nuse core::sync::atomic::{Ordering, compiler_fence, fence};\nuse core::task::{Poll, Waker};\n\nuse cortex_m::peripheral::NVIC;\nuse embassy_sync::blocking_mutex::raw::NoopRawMutex;\nuse embassy_sync::pipe;\nuse embassy_sync::waitqueue::{AtomicWaker, WakerRegistration};\nuse heapless::Vec;\nuse {embassy_net_driver_channel as ch, nrf_pac as pac};\n\nconst RX_SIZE: usize = 8 * 1024;\nconst TRACE_SIZE: usize = 16 * 1024;\nconst TRACE_BUF: usize = 1024;\nconst MTU: usize = 1500;\n\n/// Network driver.\n///\n/// This is the type you have to pass to `embassy-net` when creating the network stack.\npub type NetDriver<'a> = ch::Device<'a, MTU>;\n\nstatic WAKER: AtomicWaker = AtomicWaker::new();\n\n/// Call this function on IPC IRQ\npub fn on_ipc_irq() {\n    trace!(\"irq\");\n\n    pac::IPC_NS.inten().write(|_| ());\n    WAKER.wake();\n}\n\nstruct Allocator<'a> {\n    start: *mut u8,\n    end: *mut u8,\n    _phantom: PhantomData<&'a mut u8>,\n}\n\nimpl<'a> Allocator<'a> {\n    fn alloc_bytes(&mut self, size: usize) -> &'a mut [MaybeUninit<u8>] {\n        // safety: both pointers come from the same allocation.\n        let available_size = unsafe { self.end.offset_from(self.start) } as usize;\n        if size > available_size {\n            panic!(\"out of memory\")\n        }\n\n        // safety: we've checked above this doesn't go out of bounds.\n        let p = self.start;\n        self.start = unsafe { p.add(size) };\n\n        // safety: we've checked the pointer is in-bounds.\n        unsafe { slice::from_raw_parts_mut(p as *mut _, size) }\n    }\n\n    fn alloc<T>(&mut self) -> &'a mut MaybeUninit<T> {\n        let align = mem::align_of::<T>();\n        let size = mem::size_of::<T>();\n\n        let align_size = match (self.start as usize) % align {\n            0 => 0,\n            n => align - n,\n        };\n\n        // safety: both pointers come from the same allocation.\n        let available_size = unsafe { self.end.offset_from(self.start) } as usize;\n        if align_size + size > available_size {\n            panic!(\"out of memory\")\n        }\n\n        // safety: we've checked above this doesn't go out of bounds.\n        let p = unsafe { self.start.add(align_size) };\n        self.start = unsafe { p.add(size) };\n\n        // safety: we've checked the pointer is aligned and in-bounds.\n        unsafe { &mut *(p as *mut _) }\n    }\n}\n\n/// Create a new nRF91 embassy-net driver.\npub async fn new<'a>(\n    state: &'a mut State,\n    shmem: &'a mut [MaybeUninit<u8>],\n) -> (NetDriver<'a>, Control<'a>, Runner<'a>) {\n    let (n, c, r, _) = new_internal(state, shmem, None).await;\n    (n, c, r)\n}\n\n/// Create a new nRF91 embassy-net driver with trace.\npub async fn new_with_trace<'a>(\n    state: &'a mut State,\n    shmem: &'a mut [MaybeUninit<u8>],\n    trace_buffer: &'a mut TraceBuffer,\n) -> (NetDriver<'a>, Control<'a>, Runner<'a>, TraceReader<'a>) {\n    let (n, c, r, t) = new_internal(state, shmem, Some(trace_buffer)).await;\n    (n, c, r, t.unwrap())\n}\n\n/// Create a new nRF91 embassy-net driver.\nasync fn new_internal<'a>(\n    state: &'a mut State,\n    shmem: &'a mut [MaybeUninit<u8>],\n    trace_buffer: Option<&'a mut TraceBuffer>,\n) -> (NetDriver<'a>, Control<'a>, Runner<'a>, Option<TraceReader<'a>>) {\n    let shmem_len = shmem.len();\n    let shmem_ptr = shmem.as_mut_ptr() as *mut u8;\n\n    const SPU_REGION_SIZE: usize = 8192; // 8kb\n    trace!(\"  shmem_ptr = {}, shmem_len = {}\", shmem_ptr, shmem_len);\n\n    assert!(shmem_len != 0, \"shmem length must not be zero\");\n    assert!(\n        shmem_len % SPU_REGION_SIZE == 0,\n        \"shmem length must be a multiple of 8kb\"\n    );\n    assert!(\n        (shmem_ptr as usize) % SPU_REGION_SIZE == 0,\n        \"shmem pointer must be 8kb-aligned\"\n    );\n    assert!(\n        (shmem_ptr as usize + shmem_len) < 0x2002_0000,\n        \"shmem must be in the lower 128kb of RAM\"\n    );\n\n    let spu = pac::SPU_S;\n    debug!(\"Setting IPC RAM as nonsecure...\");\n    trace!(\n        \"  SPU_REGION_SIZE={}, shmem_ptr=0x{:08X}, shmem_len={}\",\n        SPU_REGION_SIZE, shmem_ptr as usize, shmem_len\n    );\n    let region_start = (shmem_ptr as usize - 0x2000_0000) / SPU_REGION_SIZE;\n    let region_end = region_start + shmem_len / SPU_REGION_SIZE;\n    trace!(\"  region_start={}, region_end={}\", region_start, region_end);\n    for i in region_start..region_end {\n        spu.ramregion(i).perm().write(|w| {\n            w.set_execute(true);\n            w.set_write(true);\n            w.set_read(true);\n            w.set_secattr(false);\n            w.set_lock(false);\n        })\n    }\n\n    spu.periphid(42).perm().write(|w| w.set_secattr(false));\n\n    let mut alloc = Allocator {\n        start: shmem_ptr,\n        end: unsafe { shmem_ptr.add(shmem_len) },\n        _phantom: PhantomData,\n    };\n    trace!(\n        \"  Allocator: start=0x{:08X}, end=0x{:08X}\",\n        alloc.start as usize, alloc.end as usize\n    );\n\n    let cb: &mut ControlBlock = alloc.alloc().write(unsafe { mem::zeroed() });\n\n    let rx = alloc.alloc_bytes(RX_SIZE);\n    trace!(\"  RX buffer at {}, size={}\", rx.as_ptr(), RX_SIZE);\n    let trace = alloc.alloc_bytes(TRACE_SIZE);\n    trace!(\"  Trace buffer at {}, size={}\", trace.as_ptr(), TRACE_SIZE);\n\n    cb.version = 0x00010000;\n    cb.rx_base = rx.as_mut_ptr() as _;\n    cb.rx_size = RX_SIZE;\n    cb.control_list_ptr = &mut cb.lists[0];\n    cb.data_list_ptr = &mut cb.lists[1];\n    cb.modem_info_ptr = &mut cb.modem_info;\n    cb.trace_ptr = &mut cb.trace;\n    cb.lists[0].len = LIST_LEN;\n    cb.lists[1].len = LIST_LEN;\n    cb.trace.base = trace.as_mut_ptr() as _;\n    cb.trace.size = TRACE_SIZE;\n\n    let ipc = pac::IPC_NS;\n    ipc.gpmem(0).write_value(cb as *mut _ as u32);\n    ipc.gpmem(1).write_value(0);\n    trace!(\"  GPMEM[0]={:#X}, GPMEM[1]={}\", cb as *mut _ as u32, 0);\n\n    // connect task/event i to channel i\n    for i in 0..8 {\n        ipc.send_cnf(i).write(|w| w.0 = 1 << i);\n        ipc.receive_cnf(i).write(|w| w.0 = 1 << i);\n    }\n\n    compiler_fence(Ordering::SeqCst);\n\n    let power = pac::POWER_S;\n    power\n        .ltemodem()\n        .startn()\n        .write(|w| w.set_startn(pac::power::vals::Startn::START));\n\n    unsafe { NVIC::unmask(pac::Interrupt::IPC) };\n\n    let state_inner = &*state.inner.write(RefCell::new(StateInner {\n        init: false,\n        init_waker: WakerRegistration::new(),\n        cb,\n        requests: [const { None }; REQ_COUNT],\n        next_req_serial: 0x12345678,\n        net_fd: None,\n\n        rx_control_list: ptr::null_mut(),\n        rx_data_list: ptr::null_mut(),\n        rx_control_len: 0,\n        rx_data_len: 0,\n        rx_seq_no: 0,\n        rx_check: PointerChecker {\n            start: rx.as_mut_ptr() as *mut u8,\n            end: (rx.as_mut_ptr() as *mut u8).wrapping_add(RX_SIZE),\n        },\n\n        tx_seq_no: 0,\n        tx_buf_used: [false; TX_BUF_COUNT],\n        tx_waker: WakerRegistration::new(),\n\n        trace_chans: Vec::new(),\n        trace_check: PointerChecker {\n            start: trace.as_mut_ptr() as *mut u8,\n            end: (trace.as_mut_ptr() as *mut u8).wrapping_add(TRACE_SIZE),\n        },\n    }));\n\n    let (ch_runner, device) = ch::new(&mut state.ch, ch::driver::HardwareAddress::Ip);\n    let state_ch = ch_runner.state_runner();\n\n    let control = Control {\n        state: state_inner,\n        state_ch,\n    };\n\n    let (trace_reader, trace_writer) = if let Some(trace) = trace_buffer {\n        let (r, w) = trace.trace.split();\n        (Some(r), Some(w))\n    } else {\n        (None, None)\n    };\n\n    let runner = Runner {\n        ch: ch_runner,\n        state: state_inner,\n        trace_writer,\n    };\n\n    (device, control, runner, trace_reader)\n}\n\n/// State holding modem traces.\npub struct TraceBuffer {\n    trace: pipe::Pipe<NoopRawMutex, TRACE_BUF>,\n}\n\n/// Represents writer half of the trace buffer.\npub type TraceWriter<'a> = pipe::Writer<'a, NoopRawMutex, TRACE_BUF>;\n\n/// Represents the reader half of the trace buffer.\npub type TraceReader<'a> = pipe::Reader<'a, NoopRawMutex, TRACE_BUF>;\n\nimpl TraceBuffer {\n    /// Create a new TraceBuffer.\n    pub const fn new() -> Self {\n        Self {\n            trace: pipe::Pipe::new(),\n        }\n    }\n}\n\n/// Shared state for the driver.\npub struct State {\n    ch: ch::State<MTU, 4, 4>,\n    inner: MaybeUninit<RefCell<StateInner>>,\n}\n\nimpl State {\n    /// Create a new State.\n    pub const fn new() -> Self {\n        Self {\n            ch: ch::State::new(),\n            inner: MaybeUninit::uninit(),\n        }\n    }\n}\n\nconst TX_BUF_COUNT: usize = 4;\nconst TX_BUF_SIZE: usize = 1500;\n\nstruct TraceChannelInfo {\n    ptr: *mut TraceChannel,\n    start: *mut u8,\n    end: *mut u8,\n}\n\nconst REQ_COUNT: usize = 4;\n\nstruct PendingRequest {\n    req_serial: u32,\n    resp_msg: *mut Message,\n    waker: Waker,\n}\n\n#[derive(Copy, Clone, PartialEq, Eq, Debug)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\nstruct NoFreeBufs;\n\nstruct StateInner {\n    init: bool,\n    init_waker: WakerRegistration,\n\n    cb: *mut ControlBlock,\n    requests: [Option<PendingRequest>; REQ_COUNT],\n    next_req_serial: u32,\n\n    net_fd: Option<u32>,\n\n    rx_control_list: *mut List,\n    rx_data_list: *mut List,\n    /// Number of entries in the control list\n    rx_control_len: usize,\n    /// Number of entries in the data list\n    rx_data_len: usize,\n    rx_seq_no: u16,\n    rx_check: PointerChecker,\n\n    tx_seq_no: u16,\n    tx_buf_used: [bool; TX_BUF_COUNT],\n    tx_waker: WakerRegistration,\n\n    trace_chans: Vec<TraceChannelInfo, TRACE_CHANNEL_COUNT>,\n    trace_check: PointerChecker,\n}\n\nimpl StateInner {\n    fn poll(&mut self, trace_writer: &mut Option<TraceWriter<'_>>, ch: &mut ch::Runner<MTU>) {\n        trace!(\"poll!\");\n        let ipc = pac::IPC_NS;\n\n        if ipc.events_receive(0).read() != 0 {\n            ipc.events_receive(0).write_value(0);\n            trace!(\"ipc 0\");\n        }\n\n        if ipc.events_receive(2).read() != 0 {\n            ipc.events_receive(2).write_value(0);\n            trace!(\"ipc 2\");\n\n            if !self.init {\n                let desc = unsafe { addr_of!((*self.cb).modem_info).read_volatile() };\n                assert_eq!(desc.version, 1);\n\n                self.rx_check.check_mut(desc.control_list_ptr);\n                self.rx_check.check_mut(desc.data_list_ptr);\n\n                self.rx_control_list = desc.control_list_ptr;\n                self.rx_data_list = desc.data_list_ptr;\n                let rx_control_len = unsafe { addr_of!((*self.rx_control_list).len).read_volatile() };\n                let rx_data_len = unsafe { addr_of!((*self.rx_data_list).len).read_volatile() };\n\n                trace!(\"modem control list length: {}\", rx_control_len);\n                trace!(\"modem data    list length: {}\", rx_data_len);\n                self.rx_control_len = rx_control_len;\n                self.rx_data_len = rx_data_len;\n                self.init = true;\n\n                debug!(\"IPC initialized OK!\");\n                self.init_waker.wake();\n            }\n        }\n\n        if ipc.events_receive(4).read() != 0 {\n            ipc.events_receive(4).write_value(0);\n            trace!(\"ipc 4\");\n\n            loop {\n                let list = unsafe { &mut *self.rx_control_list };\n                let control_work = self.process(list, true, ch);\n                let list = unsafe { &mut *self.rx_data_list };\n                let data_work = self.process(list, false, ch);\n                if !control_work && !data_work {\n                    break;\n                }\n            }\n        }\n\n        if ipc.events_receive(6).read() != 0 {\n            ipc.events_receive(6).write_value(0);\n            trace!(\"ipc 6\");\n        }\n\n        if ipc.events_receive(7).read() != 0 {\n            ipc.events_receive(7).write_value(0);\n            trace!(\"ipc 7: trace\");\n\n            let msg = unsafe { addr_of!((*self.cb).trace.rx_state).read_volatile() };\n            if msg != 0 {\n                trace!(\"trace msg {}\", msg);\n                match msg {\n                    0 => unreachable!(),\n                    1 => {\n                        let ctx = unsafe { addr_of!((*self.cb).trace.rx_ptr).read_volatile() } as *mut TraceContext;\n                        debug!(\"trace init: {:?}\", ctx);\n                        self.trace_check.check(ctx);\n                        let chans = unsafe { addr_of!((*ctx).chans).read_volatile() };\n                        for chan_ptr in chans {\n                            let chan = self.trace_check.check_read(chan_ptr);\n                            self.trace_check.check(chan.start);\n                            self.trace_check.check(chan.end);\n                            assert!(chan.start < chan.end);\n                            self.trace_chans\n                                .push(TraceChannelInfo {\n                                    ptr: chan_ptr,\n                                    start: chan.start,\n                                    end: chan.end,\n                                })\n                                .map_err(|_| ())\n                                .unwrap()\n                        }\n                    }\n                    2 => {\n                        for chan_info in &self.trace_chans {\n                            let read_ptr = unsafe { addr_of!((*chan_info.ptr).read_ptr).read_volatile() };\n                            let write_ptr = unsafe { addr_of!((*chan_info.ptr).write_ptr).read_volatile() };\n                            assert!(read_ptr >= chan_info.start && read_ptr <= chan_info.end);\n                            assert!(write_ptr >= chan_info.start && write_ptr <= chan_info.end);\n                            if read_ptr != write_ptr {\n                                let id = unsafe { addr_of!((*chan_info.ptr).id).read_volatile() };\n                                fence(Ordering::SeqCst); // synchronize volatile accesses with the slice access.\n                                if read_ptr < write_ptr {\n                                    Self::handle_trace(trace_writer, id, unsafe {\n                                        slice::from_raw_parts(read_ptr, write_ptr.offset_from(read_ptr) as _)\n                                    });\n                                } else {\n                                    Self::handle_trace(trace_writer, id, unsafe {\n                                        slice::from_raw_parts(read_ptr, chan_info.end.offset_from(read_ptr) as _)\n                                    });\n                                    Self::handle_trace(trace_writer, id, unsafe {\n                                        slice::from_raw_parts(\n                                            chan_info.start,\n                                            write_ptr.offset_from(chan_info.start) as _,\n                                        )\n                                    });\n                                }\n                                fence(Ordering::SeqCst); // synchronize volatile accesses with the slice access.\n                                unsafe { addr_of_mut!((*chan_info.ptr).read_ptr).write_volatile(write_ptr) };\n                            }\n                        }\n                    }\n                    _ => warn!(\"unknown trace msg {}\", msg),\n                }\n                unsafe { addr_of_mut!((*self.cb).trace.rx_state).write_volatile(0) };\n            }\n        }\n\n        ipc.intenset().write(|w| {\n            w.set_receive0(true);\n            w.set_receive2(true);\n            w.set_receive4(true);\n            w.set_receive6(true);\n            w.set_receive7(true);\n        });\n    }\n\n    fn handle_trace(writer: &mut Option<TraceWriter<'_>>, id: u8, data: &[u8]) {\n        if let Some(writer) = writer {\n            trace!(\"trace: {} {}\", id, data.len());\n            let mut header = [0u8; 5];\n            header[0] = 0xEF;\n            header[1] = 0xBE;\n            header[2..4].copy_from_slice(&(data.len() as u16).to_le_bytes());\n            header[4] = id;\n            writer.try_write(&header).ok();\n            writer.try_write(data).ok();\n        }\n    }\n\n    fn process(&mut self, list: *mut List, is_control: bool, ch: &mut ch::Runner<MTU>) -> bool {\n        let mut did_work = false;\n        let max = if is_control {\n            self.rx_control_len\n        } else {\n            self.rx_data_len\n        };\n        for i in 0..max {\n            let item_ptr = unsafe { addr_of_mut!((*list).items[i]) };\n            let preamble = unsafe { addr_of!((*item_ptr).state).read_volatile() };\n            if preamble & 0xFF == 0x01 && preamble >> 16 == self.rx_seq_no as u32 {\n                let msg_ptr = unsafe { addr_of!((*item_ptr).message).read_volatile() };\n                let msg = self.rx_check.check_read(msg_ptr);\n\n                debug!(\"rx seq {} msg: {:?}\", preamble >> 16, msg);\n\n                if is_control {\n                    self.handle_control(&msg);\n                } else {\n                    self.handle_data(&msg, ch);\n                }\n\n                unsafe { addr_of_mut!((*item_ptr).state).write_volatile(0x03) };\n                self.rx_seq_no = self.rx_seq_no.wrapping_add(1);\n\n                did_work = true;\n            }\n        }\n        did_work\n    }\n\n    fn find_free_message(&mut self, ch: usize) -> Option<usize> {\n        for i in 0..LIST_LEN {\n            let preamble = unsafe { addr_of!((*self.cb).lists[ch].items[i].state).read_volatile() };\n            if matches!(preamble & 0xFF, 0 | 3) {\n                trace!(\"using tx msg idx {}\", i);\n                return Some(i);\n            }\n        }\n        return None;\n    }\n\n    fn find_free_tx_buf(&mut self) -> Option<usize> {\n        for i in 0..TX_BUF_COUNT {\n            if !self.tx_buf_used[i] {\n                trace!(\"using tx buf idx {}\", i);\n                return Some(i);\n            }\n        }\n        return None;\n    }\n\n    fn send_message(&mut self, msg: &mut Message, data: &[u8]) -> Result<(), NoFreeBufs> {\n        if data.is_empty() {\n            msg.data = ptr::null_mut();\n            msg.data_len = 0;\n            self.send_message_raw(msg)\n        } else {\n            assert!(data.len() <= TX_BUF_SIZE);\n            let buf_idx = self.find_free_tx_buf().ok_or(NoFreeBufs)?;\n            let buf = unsafe { addr_of_mut!((*self.cb).tx_bufs[buf_idx]) } as *mut u8;\n            unsafe { copy_nonoverlapping(data.as_ptr(), buf, data.len()) }\n            msg.data = buf;\n            msg.data_len = data.len();\n            self.tx_buf_used[buf_idx] = true;\n\n            fence(Ordering::SeqCst); // synchronize copy_nonoverlapping (non-volatile) with volatile writes below.\n            if let Err(e) = self.send_message_raw(msg) {\n                msg.data = ptr::null_mut();\n                msg.data_len = 0;\n                self.tx_buf_used[buf_idx] = false;\n                self.tx_waker.wake();\n                Err(e)\n            } else {\n                Ok(())\n            }\n        }\n    }\n\n    fn send_message_raw(&mut self, msg: &Message) -> Result<(), NoFreeBufs> {\n        let (ch, ipc_ch) = match msg.channel {\n            1 => (0, 1), // control\n            2 => (1, 3), // data\n            _ => unreachable!(),\n        };\n\n        // allocate a msg.\n        let idx = self.find_free_message(ch).ok_or(NoFreeBufs)?;\n\n        debug!(\"tx seq {} msg: {:?}\", self.tx_seq_no, msg);\n\n        let msg_slot = unsafe { addr_of_mut!((*self.cb).msgs[ch][idx]) };\n        unsafe { msg_slot.write_volatile(*msg) }\n        let list_item = unsafe { addr_of_mut!((*self.cb).lists[ch].items[idx]) };\n        unsafe { addr_of_mut!((*list_item).message).write_volatile(msg_slot) }\n        unsafe { addr_of_mut!((*list_item).state).write_volatile((self.tx_seq_no as u32) << 16 | 0x01) }\n        self.tx_seq_no = self.tx_seq_no.wrapping_add(1);\n\n        let ipc = pac::IPC_NS;\n        ipc.tasks_send(ipc_ch).write_value(1);\n        Ok(())\n    }\n\n    fn handle_control(&mut self, msg: &Message) {\n        match msg.id >> 16 {\n            1 => debug!(\"control msg: modem ready\"),\n            2 => self.handle_control_free(msg.data),\n            _ => warn!(\"unknown control message id {:08x}\", msg.id),\n        }\n    }\n\n    fn handle_control_free(&mut self, ptr: *mut u8) {\n        let base = unsafe { addr_of!((*self.cb).tx_bufs) } as usize;\n        let ptr = ptr as usize;\n\n        if ptr < base {\n            warn!(\"control free bad pointer {:08x}\", ptr);\n            return;\n        }\n\n        let diff = ptr - base;\n        let idx = diff / TX_BUF_SIZE;\n\n        if idx >= TX_BUF_COUNT || idx * TX_BUF_SIZE != diff {\n            warn!(\"control free bad pointer {:08x}\", ptr);\n            return;\n        }\n\n        trace!(\"control free pointer {:08x} idx {}\", ptr, idx);\n        if !self.tx_buf_used[idx] {\n            warn!(\n                \"control free pointer {:08x} idx {}: buffer was already free??\",\n                ptr, idx\n            );\n        }\n        self.tx_buf_used[idx] = false;\n        self.tx_waker.wake();\n    }\n\n    fn handle_data(&mut self, msg: &Message, ch: &mut ch::Runner<MTU>) {\n        if !msg.data.is_null() {\n            self.rx_check.check_length(msg.data, msg.data_len);\n        }\n\n        let freed = match msg.id & 0xFFFF {\n            // AT\n            3 => {\n                match msg.id >> 16 {\n                    // AT request ack\n                    2 => false,\n                    // AT response\n                    3 => self.handle_resp(msg),\n                    // AT notification\n                    4 => false,\n                    x => {\n                        warn!(\"received unknown AT kind {}\", x);\n                        false\n                    }\n                }\n            }\n            // IP\n            4 => {\n                match msg.id >> 28 {\n                    // IP response\n                    8 => self.handle_resp(msg),\n                    // IP notification\n                    9 => match (msg.id >> 16) & 0xFFF {\n                        // IP receive notification\n                        1 => {\n                            if let Some(buf) = ch.try_rx_buf() {\n                                let mut len = msg.data_len;\n                                if len > buf.len() {\n                                    warn!(\"truncating rx'd packet from {} to {} bytes\", len, buf.len());\n                                    len = buf.len();\n                                }\n                                fence(Ordering::SeqCst); // synchronize volatile accesses with the nonvolatile copy_nonoverlapping.\n                                unsafe { ptr::copy_nonoverlapping(msg.data, buf.as_mut_ptr(), len) }\n                                fence(Ordering::SeqCst); // synchronize volatile accesses with the nonvolatile copy_nonoverlapping.\n                                ch.rx_done(len);\n                            }\n                            false\n                        }\n                        _ => false,\n                    },\n                    x => {\n                        warn!(\"received unknown IP kind {}\", x);\n                        false\n                    }\n                }\n            }\n            x => {\n                warn!(\"received unknown kind {}\", x);\n                false\n            }\n        };\n\n        if !freed {\n            self.send_free(msg);\n        }\n    }\n\n    fn handle_resp(&mut self, msg: &Message) -> bool {\n        let req_serial = u32::from_le_bytes(msg.param[0..4].try_into().unwrap());\n        if req_serial == 0 {\n            return false;\n        }\n\n        for optr in &mut self.requests {\n            if let Some(r) = optr {\n                if r.req_serial == req_serial {\n                    let r = optr.take().unwrap();\n                    unsafe { r.resp_msg.write(*msg) }\n                    r.waker.wake();\n                    *optr = None;\n                    return true;\n                }\n            }\n        }\n\n        warn!(\n            \"resp with id {} serial {} doesn't match any pending req\",\n            msg.id, req_serial\n        );\n        false\n    }\n\n    fn send_free(&mut self, msg: &Message) {\n        if msg.data.is_null() {\n            return;\n        }\n\n        let mut free_msg: Message = unsafe { mem::zeroed() };\n        free_msg.channel = 1; // control\n        free_msg.id = 0x20001; // free\n        free_msg.data = msg.data;\n        free_msg.data_len = msg.data_len;\n\n        unwrap!(self.send_message_raw(&free_msg));\n    }\n}\n\nstruct PointerChecker {\n    start: *mut u8,\n    end: *mut u8,\n}\n\nimpl PointerChecker {\n    // check the pointer is in bounds in the arena, panic otherwise.\n    fn check_length(&self, ptr: *const u8, len: usize) {\n        assert!(ptr as usize >= self.start as usize);\n        let end_ptr = (ptr as usize).checked_add(len).unwrap();\n        assert!(end_ptr <= self.end as usize);\n    }\n\n    // check the pointer is in bounds in the arena, panic otherwise.\n    fn check<T>(&self, ptr: *const T) {\n        assert!(ptr.is_aligned());\n        self.check_length(ptr as *const u8, mem::size_of::<T>());\n    }\n\n    // check the pointer is in bounds in the arena, panic otherwise.\n    fn check_read<T>(&self, ptr: *const T) -> T {\n        self.check(ptr);\n        unsafe { ptr.read_volatile() }\n    }\n\n    // check the pointer is in bounds in the arena, panic otherwise.\n    fn check_mut<T>(&self, ptr: *mut T) {\n        self.check(ptr as *const T)\n    }\n}\n\n/// Control handle for the driver.\n///\n/// You can use this object to control the modem at runtime, such as running AT commands.\npub struct Control<'a> {\n    state: &'a RefCell<StateInner>,\n    state_ch: ch::StateRunner<'a>,\n}\n\nimpl<'a> Control<'a> {\n    /// Wait for modem IPC to be initialized.\n    pub fn wait_init(&self) -> impl Future<Output = ()> + '_ {\n        poll_fn(|cx| {\n            let mut state = self.state.borrow_mut();\n            if state.init {\n                return Poll::Ready(());\n            }\n            state.init_waker.register(cx.waker());\n            Poll::Pending\n        })\n    }\n\n    async fn request(&self, msg: &mut Message, req_data: &[u8], resp_data: &mut [u8]) -> usize {\n        // get waker\n        let waker = poll_fn(|cx| Poll::Ready(cx.waker().clone())).await;\n\n        // Send request\n        let mut state = self.state.borrow_mut();\n        let mut req_serial = state.next_req_serial;\n        if msg.id & 0xFFFF == 3 {\n            // AT response seems to keep only the lower 8 bits. Others do keep the full 32 bits..??\n            req_serial &= 0xFF;\n        }\n\n        // increment next_req_serial, skip zero because we use it as an \"ignore\" value.\n        // We have to skip when the *lowest byte* is zero because AT responses.\n        state.next_req_serial = state.next_req_serial.wrapping_add(1);\n        if state.next_req_serial & 0xFF == 0 {\n            state.next_req_serial = state.next_req_serial.wrapping_add(1);\n        }\n\n        drop(state); // don't borrow state across awaits.\n\n        msg.param[0..4].copy_from_slice(&req_serial.to_le_bytes());\n\n        poll_fn(|cx| {\n            let mut state = self.state.borrow_mut();\n            state.tx_waker.register(cx.waker());\n            match state.send_message(msg, req_data) {\n                Ok(_) => Poll::Ready(()),\n                Err(NoFreeBufs) => Poll::Pending,\n            }\n        })\n        .await;\n\n        // Setup the pending request state.\n        let mut state = self.state.borrow_mut();\n        let (req_slot_idx, req_slot) = state\n            .requests\n            .iter_mut()\n            .enumerate()\n            .find(|(_, x)| x.is_none())\n            .unwrap();\n        msg.id = 0; // zero out id, so when it becomes nonzero we know the req is done.\n        let msg_ptr: *mut Message = msg;\n        *req_slot = Some(PendingRequest {\n            req_serial,\n            resp_msg: msg_ptr,\n            waker,\n        });\n\n        drop(state); // don't borrow state across awaits.\n\n        // On cancel, unregister the request slot.\n        let _drop = OnDrop::new(|| {\n            // Remove request slot.\n            let mut state = self.state.borrow_mut();\n            let slot = &mut state.requests[req_slot_idx];\n            if let Some(s) = slot {\n                if s.req_serial == req_serial {\n                    *slot = None;\n                }\n            }\n\n            // If cancelation raced with actually receiving the response,\n            // we own the data, so we have to free it.\n            let msg = unsafe { &mut *msg_ptr };\n            if msg.id != 0 {\n                state.send_free(msg);\n            }\n        });\n        // Wait for response.\n        poll_fn(|_| {\n            // we have to use the raw pointer and not the original reference `msg`\n            // because that'd invalidate the raw ptr that's still stored in `req_slot`.\n            if unsafe { (*msg_ptr).id } != 0 {\n                Poll::Ready(())\n            } else {\n                Poll::Pending\n            }\n        })\n        .await;\n        _drop.defuse();\n\n        if msg.data.is_null() {\n            // no response data.\n            return 0;\n        }\n\n        // Copy response data out, if any.\n        // Pointer was validated in StateInner::handle_data().\n        let mut len = msg.data_len;\n        if len > resp_data.len() {\n            warn!(\"truncating response data from {} to {}\", len, resp_data.len());\n            len = resp_data.len();\n        }\n        fence(Ordering::SeqCst); // synchronize volatile accesses with the nonvolatile copy_nonoverlapping.\n        unsafe { ptr::copy_nonoverlapping(msg.data, resp_data.as_mut_ptr(), len) }\n        fence(Ordering::SeqCst); // synchronize volatile accesses with the nonvolatile copy_nonoverlapping.\n        self.state.borrow_mut().send_free(msg);\n        len\n    }\n\n    /// Run an AT command.\n    ///\n    /// The response is written in `resp` and its length returned.\n    pub async fn at_command(&self, req: &[u8], resp: &mut [u8]) -> usize {\n        let mut msg: Message = unsafe { mem::zeroed() };\n        msg.channel = 2; // data\n        msg.id = 0x0001_0003; // AT command\n        msg.param_len = 4;\n\n        self.request(&mut msg, req, resp).await\n    }\n\n    /// Open the raw socket used for sending/receiving IP packets.\n    ///\n    /// This must be done after `AT+CFUN=1` (?)\n    async fn open_raw_socket(&self) -> u32 {\n        let mut msg: Message = unsafe { mem::zeroed() };\n        msg.channel = 2; // data\n        msg.id = 0x7001_0004; // open socket\n        msg.param_len = 20;\n\n        let param = [\n            0xFF, 0xFF, 0xFF, 0xFF, // req_serial\n            0xFF, 0xFF, 0xFF, 0xFF, // ???\n            0x05, 0x00, 0x00, 0x00, // family\n            0x03, 0x00, 0x00, 0x00, // type\n            0x00, 0x00, 0x00, 0x00, // protocol\n        ];\n        msg.param[..param.len()].copy_from_slice(&param);\n\n        self.request(&mut msg, &[], &mut []).await;\n\n        assert_eq!(msg.id, 0x80010004);\n        assert!(msg.param_len >= 12);\n        let status = u32::from_le_bytes(msg.param[8..12].try_into().unwrap());\n        assert_eq!(status, 0);\n        assert_eq!(msg.param_len, 16);\n        let fd = u32::from_le_bytes(msg.param[12..16].try_into().unwrap());\n        self.state.borrow_mut().net_fd.replace(fd);\n\n        trace!(\"got FD: {}\", fd);\n        fd\n    }\n\n    async fn close_raw_socket(&self, fd: u32) {\n        let mut msg: Message = unsafe { mem::zeroed() };\n        msg.channel = 2; // data\n        msg.id = 0x7009_0004; // close socket\n        msg.param_len = 8;\n        msg.param[4..8].copy_from_slice(&fd.to_le_bytes());\n\n        self.request(&mut msg, &[], &mut []).await;\n\n        assert_eq!(msg.id, 0x80090004);\n        assert!(msg.param_len >= 12);\n        let status = u32::from_le_bytes(msg.param[8..12].try_into().unwrap());\n        assert_eq!(status, 0);\n    }\n\n    fn set_link_state(&self, state: ch::driver::LinkState) {\n        self.state_ch.set_link_state(state);\n    }\n}\n\n/// Background runner for the driver.\npub struct Runner<'a> {\n    ch: ch::Runner<'a, MTU>,\n    state: &'a RefCell<StateInner>,\n    trace_writer: Option<TraceWriter<'a>>,\n}\n\nimpl<'a> Runner<'a> {\n    /// Run the driver operation in the background.\n    ///\n    /// You must run this in a background task, concurrently with all network operations.\n    pub async fn run(mut self) -> ! {\n        poll_fn(|cx| {\n            WAKER.register(cx.waker());\n\n            let mut state = self.state.borrow_mut();\n            state.poll(&mut self.trace_writer, &mut self.ch);\n\n            if let Poll::Ready(buf) = self.ch.poll_tx_buf(cx) {\n                if let Some(fd) = state.net_fd {\n                    let mut msg: Message = unsafe { mem::zeroed() };\n                    msg.channel = 2; // data\n                    msg.id = 0x7006_0004; // IP send\n                    msg.param_len = 12;\n                    msg.param[4..8].copy_from_slice(&fd.to_le_bytes());\n                    if let Err(e) = state.send_message(&mut msg, buf) {\n                        warn!(\"tx failed: {:?}\", e);\n                    }\n                    self.ch.tx_done();\n                }\n            }\n\n            Poll::Pending\n        })\n        .await\n    }\n}\n\nconst LIST_LEN: usize = 32;\n\n#[repr(C)]\nstruct ControlBlock {\n    version: u32,\n    rx_base: *mut u8,\n    rx_size: usize,\n    control_list_ptr: *mut List,\n    data_list_ptr: *mut List,\n    modem_info_ptr: *mut ModemInfo,\n    trace_ptr: *mut Trace,\n    unk: u32,\n\n    modem_info: ModemInfo,\n    trace: Trace,\n\n    // 0 = control, 1 = data\n    lists: [List; 2],\n    msgs: [[Message; LIST_LEN]; 2],\n\n    tx_bufs: [[u8; TX_BUF_SIZE]; TX_BUF_COUNT],\n}\n\n#[repr(C)]\nstruct ModemInfo {\n    version: u32,\n    control_list_ptr: *mut List,\n    data_list_ptr: *mut List,\n    padding: [u32; 5],\n}\n\n#[repr(C)]\nstruct Trace {\n    size: usize,\n    base: *mut u8,\n    tx_state: u32,\n    tx_ptr: *mut u8,\n    rx_state: u32,\n    rx_ptr: *mut u8,\n    unk1: u32,\n    unk2: u32,\n}\n\nconst TRACE_CHANNEL_COUNT: usize = 3;\n\n#[repr(C)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\nstruct TraceContext {\n    unk1: u32,\n    unk2: u32,\n    len: u32,\n    chans: [*mut TraceChannel; TRACE_CHANNEL_COUNT],\n}\n\n#[repr(C)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\nstruct TraceChannel {\n    id: u8,\n    unk1: u8,\n    unk2: u8,\n    unk3: u8,\n    write_ptr: *mut u8,\n    read_ptr: *mut u8,\n    start: *mut u8,\n    end: *mut u8,\n}\n\n#[repr(C)]\nstruct List {\n    len: usize,\n    items: [ListItem; LIST_LEN],\n}\n\n#[repr(C)]\nstruct ListItem {\n    /// top 16 bits: seqno\n    /// bottom 8 bits:\n    ///     0x01: sent\n    ///     0x02: held\n    ///     0x03: freed\n    state: u32,\n    message: *mut Message,\n}\n\n#[repr(C)]\n#[derive(Clone, Copy)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\nstruct Message {\n    id: u32,\n\n    /// 1 = control, 2 = data\n    channel: u8,\n    unk1: u8,\n    unk2: u8,\n    unk3: u8,\n\n    data: *mut u8,\n    data_len: usize,\n    param_len: usize,\n    param: [u8; 44],\n}\n\nstruct OnDrop<F: FnOnce()> {\n    f: MaybeUninit<F>,\n}\n\nimpl<F: FnOnce()> OnDrop<F> {\n    pub fn new(f: F) -> Self {\n        Self { f: MaybeUninit::new(f) }\n    }\n\n    pub fn defuse(self) {\n        mem::forget(self)\n    }\n}\n\nimpl<F: FnOnce()> Drop for OnDrop<F> {\n    fn drop(&mut self) {\n        unsafe { self.f.as_ptr().read()() }\n    }\n}\n"
  },
  {
    "path": "embassy-net-ppp/CHANGELOG.md",
    "content": "# Changelog for embassy-net-ppp\n\nAll notable changes to this project will be documented in this file.\n\nThe format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),\nand this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).\n\n<!-- next-header -->\n## Unreleased - ReleaseDate\n\n## 0.3.0 - 2026-03-11\n\n- Update to embedded-io 0.7\n- Update embassy-net-driver-channel to 0.4.0\n- Update embassy-sync to 0.8.0\n\n## 0.2.1 - 2025-08-26\n\n## 0.2.0 - 2025-01-12\n\n- Update `ppproto` to v0.2.\n- Use `core::net` IP types for IPv4 configuration instead of a custom type.\n\n## 0.1.0 - 2024-01-12\n\nFirst release.\n"
  },
  {
    "path": "embassy-net-ppp/Cargo.toml",
    "content": "[package]\nname = \"embassy-net-ppp\"\nversion = \"0.3.0\"\ndescription = \"embassy-net driver for PPP over Serial\"\nkeywords = [\"embedded\", \"ppp\", \"embassy-net\", \"embedded-hal-async\", \"async\"]\ncategories = [\"embedded\", \"hardware-support\", \"no-std\", \"network-programming\", \"asynchronous\"]\nlicense = \"MIT OR Apache-2.0\"\nedition = \"2024\"\nrepository = \"https://github.com/embassy-rs/embassy\"\ndocumentation = \"https://docs.embassy.dev/embassy-net-ppp\"\n\n[features]\ndefmt = [\"dep:defmt\", \"ppproto/defmt\"]\nlog = [\"dep:log\", \"ppproto/log\"]\n\n[dependencies]\ndefmt = { version = \"1.0.1\", optional = true }\nlog = { version = \"0.4.14\", optional = true }\n\nembedded-io-async = { version = \"0.7.0\" }\nembassy-net-driver-channel = { version = \"0.4.0\", path = \"../embassy-net-driver-channel\" }\nembassy-futures = { version = \"0.1.2\", path = \"../embassy-futures\" }\nppproto = { version = \"0.2.1\"}\nembassy-sync = { version = \"0.8.0\", path = \"../embassy-sync\" }\n\n[package.metadata.embassy_docs]\nsrc_base = \"https://github.com/embassy-rs/embassy/blob/embassy-net-ppp-v$VERSION/embassy-net-ppp/src/\"\nsrc_base_git = \"https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-net-ppp/src/\"\ntarget = \"thumbv7em-none-eabi\"\nfeatures = [\"defmt\"]\n\n[package.metadata.docs.rs]\nfeatures = [\"defmt\"]\n"
  },
  {
    "path": "embassy-net-ppp/README.md",
    "content": "# `embassy-net-ppp`\n\n[`embassy-net`](https://crates.io/crates/embassy-net) integration for PPP over Serial.\n\n## Interoperability\n\nThis crate can run on any executor.\n\nIt supports any serial port implementing [`embedded-io-async`](https://crates.io/crates/embedded-io-async).\n"
  },
  {
    "path": "embassy-net-ppp/src/fmt.rs",
    "content": "#![macro_use]\n#![allow(unused)]\n\nuse core::fmt::{Debug, Display, LowerHex};\n\n#[cfg(all(feature = \"defmt\", feature = \"log\"))]\ncompile_error!(\"You may not enable both `defmt` and `log` features.\");\n\n#[collapse_debuginfo(yes)]\nmacro_rules! assert {\n    ($($x:tt)*) => {\n        {\n            #[cfg(not(feature = \"defmt\"))]\n            ::core::assert!($($x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::assert!($($x)*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! assert_eq {\n    ($($x:tt)*) => {\n        {\n            #[cfg(not(feature = \"defmt\"))]\n            ::core::assert_eq!($($x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::assert_eq!($($x)*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! assert_ne {\n    ($($x:tt)*) => {\n        {\n            #[cfg(not(feature = \"defmt\"))]\n            ::core::assert_ne!($($x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::assert_ne!($($x)*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! debug_assert {\n    ($($x:tt)*) => {\n        {\n            #[cfg(not(feature = \"defmt\"))]\n            ::core::debug_assert!($($x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::debug_assert!($($x)*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! debug_assert_eq {\n    ($($x:tt)*) => {\n        {\n            #[cfg(not(feature = \"defmt\"))]\n            ::core::debug_assert_eq!($($x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::debug_assert_eq!($($x)*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! debug_assert_ne {\n    ($($x:tt)*) => {\n        {\n            #[cfg(not(feature = \"defmt\"))]\n            ::core::debug_assert_ne!($($x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::debug_assert_ne!($($x)*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! todo {\n    ($($x:tt)*) => {\n        {\n            #[cfg(not(feature = \"defmt\"))]\n            ::core::todo!($($x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::todo!($($x)*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! unreachable {\n    ($($x:tt)*) => {\n        {\n            #[cfg(not(feature = \"defmt\"))]\n            ::core::unreachable!($($x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::unreachable!($($x)*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! panic {\n    ($($x:tt)*) => {\n        {\n            #[cfg(not(feature = \"defmt\"))]\n            ::core::panic!($($x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::panic!($($x)*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! trace {\n    ($s:literal $(, $x:expr)* $(,)?) => {\n        {\n            #[cfg(feature = \"log\")]\n            ::log::trace!($s $(, $x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::trace!($s $(, $x)*);\n            #[cfg(not(any(feature = \"log\", feature=\"defmt\")))]\n            let _ = ($( & $x ),*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! debug {\n    ($s:literal $(, $x:expr)* $(,)?) => {\n        {\n            #[cfg(feature = \"log\")]\n            ::log::debug!($s $(, $x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::debug!($s $(, $x)*);\n            #[cfg(not(any(feature = \"log\", feature=\"defmt\")))]\n            let _ = ($( & $x ),*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! info {\n    ($s:literal $(, $x:expr)* $(,)?) => {\n        {\n            #[cfg(feature = \"log\")]\n            ::log::info!($s $(, $x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::info!($s $(, $x)*);\n            #[cfg(not(any(feature = \"log\", feature=\"defmt\")))]\n            let _ = ($( & $x ),*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! warn {\n    ($s:literal $(, $x:expr)* $(,)?) => {\n        {\n            #[cfg(feature = \"log\")]\n            ::log::warn!($s $(, $x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::warn!($s $(, $x)*);\n            #[cfg(not(any(feature = \"log\", feature=\"defmt\")))]\n            let _ = ($( & $x ),*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! error {\n    ($s:literal $(, $x:expr)* $(,)?) => {\n        {\n            #[cfg(feature = \"log\")]\n            ::log::error!($s $(, $x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::error!($s $(, $x)*);\n            #[cfg(not(any(feature = \"log\", feature=\"defmt\")))]\n            let _ = ($( & $x ),*);\n        }\n    };\n}\n\n#[cfg(feature = \"defmt\")]\n#[collapse_debuginfo(yes)]\nmacro_rules! unwrap {\n    ($($x:tt)*) => {\n        ::defmt::unwrap!($($x)*)\n    };\n}\n\n#[cfg(not(feature = \"defmt\"))]\n#[collapse_debuginfo(yes)]\nmacro_rules! unwrap {\n    ($arg:expr) => {\n        match $crate::fmt::Try::into_result($arg) {\n            ::core::result::Result::Ok(t) => t,\n            ::core::result::Result::Err(e) => {\n                ::core::panic!(\"unwrap of `{}` failed: {:?}\", ::core::stringify!($arg), e);\n            }\n        }\n    };\n    ($arg:expr, $($msg:expr),+ $(,)? ) => {\n        match $crate::fmt::Try::into_result($arg) {\n            ::core::result::Result::Ok(t) => t,\n            ::core::result::Result::Err(e) => {\n                ::core::panic!(\"unwrap of `{}` failed: {}: {:?}\", ::core::stringify!($arg), ::core::format_args!($($msg,)*), e);\n            }\n        }\n    }\n}\n\n#[derive(Debug, Copy, Clone, Eq, PartialEq)]\npub struct NoneError;\n\npub trait Try {\n    type Ok;\n    type Error;\n    fn into_result(self) -> Result<Self::Ok, Self::Error>;\n}\n\nimpl<T> Try for Option<T> {\n    type Ok = T;\n    type Error = NoneError;\n\n    #[inline]\n    fn into_result(self) -> Result<T, NoneError> {\n        self.ok_or(NoneError)\n    }\n}\n\nimpl<T, E> Try for Result<T, E> {\n    type Ok = T;\n    type Error = E;\n\n    #[inline]\n    fn into_result(self) -> Self {\n        self\n    }\n}\n\npub(crate) struct Bytes<'a>(pub &'a [u8]);\n\nimpl<'a> Debug for Bytes<'a> {\n    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {\n        write!(f, \"{:#02x?}\", self.0)\n    }\n}\n\nimpl<'a> Display for Bytes<'a> {\n    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {\n        write!(f, \"{:#02x?}\", self.0)\n    }\n}\n\nimpl<'a> LowerHex for Bytes<'a> {\n    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {\n        write!(f, \"{:#02x?}\", self.0)\n    }\n}\n\n#[cfg(feature = \"defmt\")]\nimpl<'a> defmt::Format for Bytes<'a> {\n    fn format(&self, fmt: defmt::Formatter) {\n        defmt::write!(fmt, \"{:02x}\", self.0)\n    }\n}\n"
  },
  {
    "path": "embassy-net-ppp/src/lib.rs",
    "content": "#![no_std]\n#![warn(missing_docs)]\n#![doc = include_str!(\"../README.md\")]\n\n// must be first\nmod fmt;\n\nuse core::convert::Infallible;\nuse core::mem::MaybeUninit;\n\nuse embassy_futures::select::{Either, select};\nuse embassy_net_driver_channel as ch;\nuse embassy_net_driver_channel::driver::LinkState;\nuse embedded_io_async::{BufRead, Write};\nuse ppproto::pppos::{BufferFullError, PPPoS, PPPoSAction};\npub use ppproto::{Config, Ipv4Status};\n\nconst MTU: usize = 1500;\n\n/// Type alias for the embassy-net driver.\npub type Device<'d> = embassy_net_driver_channel::Device<'d, MTU>;\n\n/// Internal state for the embassy-net integration.\npub struct State<const N_RX: usize, const N_TX: usize> {\n    ch_state: ch::State<MTU, N_RX, N_TX>,\n}\n\nimpl<const N_RX: usize, const N_TX: usize> State<N_RX, N_TX> {\n    /// Create a new `State`.\n    pub const fn new() -> Self {\n        Self {\n            ch_state: ch::State::new(),\n        }\n    }\n}\n\n/// Background runner for the driver.\n///\n/// You must call `.run()` in a background task for the driver to operate.\npub struct Runner<'d> {\n    ch: ch::Runner<'d, MTU>,\n}\n\n/// Error returned by [`Runner::run`].\n#[derive(Debug)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub enum RunError<E> {\n    /// Reading from the serial port failed.\n    Read(E),\n    /// Writing to the serial port failed.\n    Write(E),\n    /// Writing to the serial got EOF.\n    Eof,\n    /// PPP protocol was terminated by the peer\n    Terminated,\n}\n\nimpl<'d> Runner<'d> {\n    /// You must call this in a background task for the driver to operate.\n    ///\n    /// If reading/writing to the underlying serial port fails, the link state\n    /// is set to Down and the error is returned.\n    ///\n    /// It is allowed to cancel this function's future (i.e. drop it). This will terminate\n    /// the PPP connection and set the link state to Down.\n    ///\n    /// After this function returns or is canceled, you can call it again to establish\n    /// a new PPP connection.\n    pub async fn run<RW: BufRead + Write>(\n        &mut self,\n        mut rw: RW,\n        config: ppproto::Config<'_>,\n        mut on_ipv4_up: impl FnMut(Ipv4Status),\n    ) -> Result<Infallible, RunError<RW::Error>> {\n        let mut ppp = PPPoS::new(config);\n        ppp.open().unwrap();\n\n        let (state_chan, mut rx_chan, mut tx_chan) = self.ch.borrow_split();\n        state_chan.set_link_state(LinkState::Down);\n        let _ondrop = OnDrop::new(|| state_chan.set_link_state(LinkState::Down));\n\n        let mut rx_buf = [0; 2048];\n        let mut tx_buf = [0; 2048];\n\n        let mut needs_poll = true;\n        let mut was_up = false;\n\n        loop {\n            let rx_fut = async {\n                let buf = rx_chan.rx_buf().await;\n                let rx_data = match needs_poll {\n                    true => &[][..],\n                    false => match rw.fill_buf().await {\n                        Ok(rx_data) if rx_data.len() == 0 => return Err(RunError::Eof),\n                        Ok(rx_data) => rx_data,\n                        Err(e) => return Err(RunError::Read(e)),\n                    },\n                };\n                Ok((buf, rx_data))\n            };\n            let tx_fut = tx_chan.tx_buf();\n            match select(rx_fut, tx_fut).await {\n                Either::First(r) => {\n                    needs_poll = false;\n\n                    let (buf, rx_data) = r?;\n                    let n = ppp.consume(rx_data, &mut rx_buf);\n                    rw.consume(n);\n\n                    match ppp.poll(&mut tx_buf, &mut rx_buf) {\n                        PPPoSAction::None => {}\n                        PPPoSAction::Received(rg) => {\n                            let pkt = &rx_buf[rg];\n                            buf[..pkt.len()].copy_from_slice(pkt);\n                            rx_chan.rx_done(pkt.len());\n                        }\n                        PPPoSAction::Transmit(n) => rw.write_all(&tx_buf[..n]).await.map_err(RunError::Write)?,\n                    }\n\n                    let status = ppp.status();\n                    match status.phase {\n                        ppproto::Phase::Dead => {\n                            return Err(RunError::Terminated);\n                        }\n                        ppproto::Phase::Open => {\n                            if !was_up {\n                                on_ipv4_up(status.ipv4.unwrap());\n                            }\n                            was_up = true;\n                            state_chan.set_link_state(LinkState::Up);\n                        }\n                        _ => {\n                            was_up = false;\n                            state_chan.set_link_state(LinkState::Down);\n                        }\n                    }\n                }\n                Either::Second(pkt) => {\n                    match ppp.send(pkt, &mut tx_buf) {\n                        Ok(n) => rw.write_all(&tx_buf[..n]).await.map_err(RunError::Write)?,\n                        Err(BufferFullError) => unreachable!(),\n                    }\n                    tx_chan.tx_done();\n                }\n            }\n        }\n    }\n}\n\n/// Create a PPP embassy-net driver instance.\n///\n/// This returns two structs:\n/// - a `Device` that you must pass to the `embassy-net` stack.\n/// - a `Runner`. You must call `.run()` on it in a background task.\npub fn new<'a, const N_RX: usize, const N_TX: usize>(state: &'a mut State<N_RX, N_TX>) -> (Device<'a>, Runner<'a>) {\n    let (runner, device) = ch::new(&mut state.ch_state, ch::driver::HardwareAddress::Ip);\n    (device, Runner { ch: runner })\n}\n\nstruct OnDrop<F: FnOnce()> {\n    f: MaybeUninit<F>,\n}\n\nimpl<F: FnOnce()> OnDrop<F> {\n    fn new(f: F) -> Self {\n        Self { f: MaybeUninit::new(f) }\n    }\n}\n\nimpl<F: FnOnce()> Drop for OnDrop<F> {\n    fn drop(&mut self) {\n        unsafe { self.f.as_ptr().read()() }\n    }\n}\n"
  },
  {
    "path": "embassy-net-tuntap/Cargo.toml",
    "content": "[package]\nname = \"embassy-net-tuntap\"\nversion = \"0.1.1\"\ndescription = \"embassy-net driver for Linux TUN/TAP interfaces.\"\nkeywords = [\"embedded\", \"tuntap\", \"embassy-net\", \"ethernet\", \"async\"]\ncategories = [\"embedded\", \"hardware-support\", \"network-programming\", \"asynchronous\"]\nlicense = \"MIT OR Apache-2.0\"\nedition = \"2024\"\nrepository = \"https://github.com/embassy-rs/embassy\"\ndocumentation = \"https://docs.embassy.dev/embassy-net-tuntap\"\n\n[dependencies]\nembassy-net-driver = { version = \"0.2.0\", path = \"../embassy-net-driver\" }\nasync-io = \"2.6.0\"\nlog = \"0.4.14\"\nlibc = \"0.2.101\"\n\n[package.metadata.embassy_docs]\nsrc_base = \"https://github.com/embassy-rs/embassy/blob/embassy-net-tuntap-v$VERSION/embassy-net-tuntap/src/\"\nsrc_base_git = \"https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-net-tuntap/src/\"\ntarget = \"x86_64-unknown-linux-gnu\"\n"
  },
  {
    "path": "embassy-net-tuntap/README.md",
    "content": "# `embassy-net` integration for Linux TUN/TAP interfaces.\n\n[`embassy-net`](https://crates.io/crates/embassy-net) integration for for Linux TUN (IP medium) and TAP (Ethernet medium) interfaces.\n\n## Interoperability\n\nThis crate can run on any executor.\n"
  },
  {
    "path": "embassy-net-tuntap/src/lib.rs",
    "content": "#![warn(missing_docs)]\n#![doc = include_str!(\"../README.md\")]\nuse std::io;\nuse std::io::{Read, Write};\nuse std::os::fd::AsFd;\nuse std::os::unix::io::{AsRawFd, RawFd};\nuse std::os::unix::prelude::BorrowedFd;\nuse std::task::Context;\n\nuse async_io::Async;\nuse embassy_net_driver::{Capabilities, Driver, HardwareAddress, LinkState};\nuse log::*;\n\nconst ETHERNET_HEADER_LEN: usize = 14;\n\n#[repr(C)]\n#[derive(Debug)]\n#[allow(non_camel_case_types)]\nstruct ifreq {\n    ifr_name: [libc::c_char; libc::IF_NAMESIZE],\n    ifr_data: libc::c_int, /* ifr_ifindex or ifr_mtu */\n}\n\nfn ifreq_for(name: &str) -> ifreq {\n    let mut ifreq = ifreq {\n        ifr_name: [0; libc::IF_NAMESIZE],\n        ifr_data: 0,\n    };\n    for (i, byte) in name.as_bytes().iter().enumerate() {\n        ifreq.ifr_name[i] = *byte as libc::c_char\n    }\n    ifreq\n}\n\nfn ifreq_ioctl(lower: libc::c_int, ifreq: &mut ifreq, cmd: libc::c_ulong) -> io::Result<libc::c_int> {\n    unsafe {\n        let res = libc::ioctl(lower, cmd as _, ifreq as *mut ifreq);\n        if res == -1 {\n            return Err(io::Error::last_os_error());\n        }\n    }\n\n    Ok(ifreq.ifr_data)\n}\n\n/// A TUN/TAP device.\n#[derive(Debug)]\npub struct TunTap {\n    fd: libc::c_int,\n    mtu: usize,\n}\n\nimpl AsRawFd for TunTap {\n    fn as_raw_fd(&self) -> RawFd {\n        self.fd\n    }\n}\n\nimpl AsFd for TunTap {\n    fn as_fd(&self) -> BorrowedFd<'_> {\n        unsafe { BorrowedFd::borrow_raw(self.fd) }\n    }\n}\n\nimpl TunTap {\n    /// Create a new TUN/TAP device.\n    pub fn new(name: &str) -> io::Result<TunTap> {\n        unsafe {\n            let fd = libc::open(c\"/dev/net/tun\".as_ptr(), libc::O_RDWR | libc::O_NONBLOCK);\n            if fd == -1 {\n                return Err(io::Error::last_os_error());\n            }\n\n            let mut ifreq = ifreq_for(name);\n            ifreq.ifr_data = libc::IFF_TAP | libc::IFF_NO_PI;\n            ifreq_ioctl(fd, &mut ifreq, libc::TUNSETIFF)?;\n\n            let socket = libc::socket(libc::AF_INET, libc::SOCK_DGRAM, libc::IPPROTO_IP);\n            if socket == -1 {\n                return Err(io::Error::last_os_error());\n            }\n\n            let ip_mtu = ifreq_ioctl(socket, &mut ifreq, libc::SIOCGIFMTU);\n            libc::close(socket);\n            let ip_mtu = ip_mtu? as usize;\n\n            // SIOCGIFMTU returns the IP MTU (typically 1500 bytes.)\n            // smoltcp counts the entire Ethernet packet in the MTU, so add the Ethernet header size to it.\n            let mtu = ip_mtu + ETHERNET_HEADER_LEN;\n\n            Ok(TunTap { fd, mtu })\n        }\n    }\n}\n\nimpl Drop for TunTap {\n    fn drop(&mut self) {\n        unsafe {\n            libc::close(self.fd);\n        }\n    }\n}\n\nimpl io::Read for TunTap {\n    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {\n        let len = unsafe { libc::read(self.fd, buf.as_mut_ptr() as *mut libc::c_void, buf.len()) };\n        if len == -1 {\n            Err(io::Error::last_os_error())\n        } else {\n            Ok(len as usize)\n        }\n    }\n}\n\nimpl io::Write for TunTap {\n    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {\n        let len = unsafe { libc::write(self.fd, buf.as_ptr() as *mut libc::c_void, buf.len()) };\n        if len == -1 {\n            Err(io::Error::last_os_error())\n        } else {\n            Ok(len as usize)\n        }\n    }\n\n    fn flush(&mut self) -> io::Result<()> {\n        Ok(())\n    }\n}\n\n/// A TUN/TAP device, wrapped in an async interface.\npub struct TunTapDevice {\n    device: Async<TunTap>,\n    hardware_address: [u8; 6],\n}\n\nimpl TunTapDevice {\n    /// Create a new TUN/TAP device.\n    pub fn new(name: &str) -> io::Result<TunTapDevice> {\n        Ok(Self {\n            device: Async::new(TunTap::new(name)?)?,\n            hardware_address: [0x02, 0x03, 0x04, 0x05, 0x06, 0x07],\n        })\n    }\n\n    /// Sets the MAC address of the TAP device.\n    ///\n    /// Note that this can not be completely random; for example, choosing a multicast address\n    /// (least significant bit of the first octet is 1) would cause smoltcp to crash.\n    pub fn set_hardware_address(&mut self, hardware_address: [u8; 6]) {\n        self.hardware_address = hardware_address;\n    }\n}\n\nimpl Driver for TunTapDevice {\n    type RxToken<'a>\n        = RxToken\n    where\n        Self: 'a;\n    type TxToken<'a>\n        = TxToken<'a>\n    where\n        Self: 'a;\n\n    fn receive(&mut self, cx: &mut Context) -> Option<(Self::RxToken<'_>, Self::TxToken<'_>)> {\n        let mut buf = vec![0; self.device.get_ref().mtu];\n        loop {\n            match unsafe { self.device.get_mut() }.read(&mut buf) {\n                Ok(n) => {\n                    buf.truncate(n);\n                    return Some((\n                        RxToken { buffer: buf },\n                        TxToken {\n                            device: &mut self.device,\n                        },\n                    ));\n                }\n                Err(e) if e.kind() == io::ErrorKind::WouldBlock => {\n                    if !self.device.poll_readable(cx).is_ready() {\n                        return None;\n                    }\n                }\n                Err(e) => panic!(\"read error: {:?}\", e),\n            }\n        }\n    }\n\n    fn transmit(&mut self, _cx: &mut Context) -> Option<Self::TxToken<'_>> {\n        Some(TxToken {\n            device: &mut self.device,\n        })\n    }\n\n    fn capabilities(&self) -> Capabilities {\n        let mut caps = Capabilities::default();\n        caps.max_transmission_unit = self.device.get_ref().mtu;\n        caps\n    }\n\n    fn link_state(&mut self, _cx: &mut Context) -> LinkState {\n        LinkState::Up\n    }\n\n    fn hardware_address(&self) -> HardwareAddress {\n        HardwareAddress::Ethernet(self.hardware_address)\n    }\n}\n\n#[doc(hidden)]\npub struct RxToken {\n    buffer: Vec<u8>,\n}\n\nimpl embassy_net_driver::RxToken for RxToken {\n    fn consume<R, F>(mut self, f: F) -> R\n    where\n        F: FnOnce(&mut [u8]) -> R,\n    {\n        f(&mut self.buffer)\n    }\n}\n\n#[doc(hidden)]\npub struct TxToken<'a> {\n    device: &'a mut Async<TunTap>,\n}\n\nimpl<'a> embassy_net_driver::TxToken for TxToken<'a> {\n    fn consume<R, F>(self, len: usize, f: F) -> R\n    where\n        F: FnOnce(&mut [u8]) -> R,\n    {\n        let mut buffer = vec![0; len];\n        let result = f(&mut buffer);\n\n        // todo handle WouldBlock with async\n        match unsafe { self.device.get_mut() }.write(&buffer) {\n            Ok(_) => {}\n            Err(e) if e.kind() == io::ErrorKind::WouldBlock => info!(\"transmit WouldBlock\"),\n            Err(e) => panic!(\"transmit error: {:?}\", e),\n        }\n\n        result\n    }\n}\n"
  },
  {
    "path": "embassy-net-wiznet/CHANGELOG.md",
    "content": "# Changelog\n\nAll notable changes to this project will be documented in this file.\n\nThe format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),\nand this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).\n\n<!-- next-header -->\n## Unreleased - ReleaseDate\n\n## 0.3.0 - 2026-03-10\n\n- Added experimental W6100 driver with disabled MAC filter (does not currently work with it enabled)\n- Added W6300 driver\n- Introduced `SOCKET_INTR_CLR` register which is needed on W6100 and later models (on W5100/W5500 this is shared with `SOCKET_INTR` and the address is the same)\n- Upgrade embassy-net-driver-channel to 0.4.0\n\n## 0.2.1 - 2025-08-26\n\n## 0.1.1 - 2025-08-14\n\n- First release with changelog.\n"
  },
  {
    "path": "embassy-net-wiznet/Cargo.toml",
    "content": "[package]\nname = \"embassy-net-wiznet\"\nversion = \"0.3.0\"\ndescription = \"embassy-net driver for WIZnet SPI Ethernet chips\"\nkeywords = [\"embedded\", \"embassy-net\", \"embedded-hal-async\", \"ethernet\", \"async\"]\ncategories = [\"embedded\", \"hardware-support\", \"no-std\", \"network-programming\", \"asynchronous\"]\nlicense = \"MIT OR Apache-2.0\"\nedition = \"2024\"\nrepository = \"https://github.com/embassy-rs/embassy\"\ndocumentation = \"https://docs.embassy.dev/embassy-net-wiznet\"\n\n[dependencies]\nembedded-hal = { version = \"1.0\" }\nembedded-hal-async = { version = \"1.0\" }\nembassy-net-driver-channel = { version = \"0.4.0\", path = \"../embassy-net-driver-channel\" }\nembassy-time = { version = \"0.5.1\", path = \"../embassy-time\" }\nembassy-futures = { version = \"0.1.2\", path = \"../embassy-futures\" }\ndefmt = { version = \"1.0.1\", optional = true }\n\n[features]\ndefmt = [\"dep:defmt\", \"embassy-net-driver-channel/defmt\"]\n\n[package.metadata.embassy_docs]\nsrc_base = \"https://github.com/embassy-rs/embassy/blob/embassy-net-wiznet-v$VERSION/embassy-net-wiznet/src/\"\nsrc_base_git = \"https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-net-wiznet/src/\"\ntarget = \"thumbv7em-none-eabi\"\nfeatures = [\"defmt\"]\n\n[package.metadata.docs.rs]\nfeatures = [\"defmt\"]\n"
  },
  {
    "path": "embassy-net-wiznet/README.md",
    "content": "# WIZnet `embassy-net` integration\n\n[`embassy-net`](https://crates.io/crates/embassy-net) integration for the WIZnet SPI ethernet chips, operating in MACRAW mode.\n\nSee [`examples`](https://github.com/embassy-rs/embassy/tree/main/examples/rp) directory for usage examples with the rp2040 [`WIZnet W5500-EVB-Pico`](https://docs.wiznet.io/Product/iEthernet/W5500/w5500-evb-pico)) module.\n\n## Supported chips\n\n- W5500\n- W5100S\n- W6100\n- W6300 (Single SPI only)\n\n## Interoperability\n\nThis crate can run on any executor.\n\nIt supports any SPI driver implementing [`embedded-hal-async`](https://crates.io/crates/embedded-hal-async).\n"
  },
  {
    "path": "embassy-net-wiznet/src/chip/mod.rs",
    "content": "//! Wiznet W5100s, W5500, W6100 and W6300 family driver.\nmod w5500;\npub use w5500::W5500;\n\nmod w5100s;\npub use w5100s::W5100S;\n\nmod w6100;\npub use w6100::W6100;\n\nmod w6300;\nuse embedded_hal_async::spi::SpiDevice;\npub use w6300::W6300;\n\npub(crate) trait SealedChip {\n    type Address;\n\n    /// The version of the chip as reported by the VERSIONR register.\n    /// This is used to verify that the chip is supported by the driver,\n    /// and that SPI communication is working.\n    const CHIP_VERSION: u8;\n\n    const COMMON_MODE: Self::Address;\n    const COMMON_MAC: Self::Address;\n    const COMMON_SOCKET_INTR: Self::Address;\n    const COMMON_PHY_CFG: Self::Address;\n    const COMMON_VERSION: Self::Address;\n\n    const SOCKET_MODE: Self::Address;\n    const SOCKET_COMMAND: Self::Address;\n    const SOCKET_RXBUF_SIZE: Self::Address;\n    const SOCKET_TXBUF_SIZE: Self::Address;\n    const SOCKET_TX_FREE_SIZE: Self::Address;\n    const SOCKET_TX_DATA_WRITE_PTR: Self::Address;\n    const SOCKET_RECVD_SIZE: Self::Address;\n    const SOCKET_RX_DATA_READ_PTR: Self::Address;\n    const SOCKET_INTR_MASK: Self::Address;\n    #[allow(dead_code)]\n    const SOCKET_INTR: Self::Address;\n    const SOCKET_INTR_CLR: Self::Address;\n\n    const SOCKET_MODE_VALUE: u8;\n\n    const BUF_SIZE: u16;\n    const AUTO_WRAP: bool;\n\n    fn rx_addr(addr: u16) -> Self::Address;\n    fn tx_addr(addr: u16) -> Self::Address;\n\n    async fn bus_read<SPI: SpiDevice>(spi: &mut SPI, address: Self::Address, data: &mut [u8])\n    -> Result<(), SPI::Error>;\n    async fn bus_write<SPI: SpiDevice>(spi: &mut SPI, address: Self::Address, data: &[u8]) -> Result<(), SPI::Error>;\n}\n\n/// Trait for Wiznet chips.\n#[allow(private_bounds)]\npub trait Chip: SealedChip {}\n"
  },
  {
    "path": "embassy-net-wiznet/src/chip/w5100s.rs",
    "content": "use embedded_hal_async::spi::{Operation, SpiDevice};\n\nconst SOCKET_BASE: u16 = 0x400;\nconst TX_BASE: u16 = 0x4000;\nconst RX_BASE: u16 = 0x6000;\n\n/// Wizard W5100S chip.\npub enum W5100S {}\n\nimpl super::Chip for W5100S {}\nimpl super::SealedChip for W5100S {\n    type Address = u16;\n\n    const CHIP_VERSION: u8 = 0x51;\n\n    const COMMON_MODE: Self::Address = 0x00;\n    const COMMON_MAC: Self::Address = 0x09;\n    const COMMON_SOCKET_INTR: Self::Address = 0x16;\n    const COMMON_PHY_CFG: Self::Address = 0x3c;\n    const COMMON_VERSION: Self::Address = 0x80;\n\n    const SOCKET_MODE: Self::Address = SOCKET_BASE + 0x00;\n    const SOCKET_COMMAND: Self::Address = SOCKET_BASE + 0x01;\n    const SOCKET_RXBUF_SIZE: Self::Address = SOCKET_BASE + 0x1E;\n    const SOCKET_TXBUF_SIZE: Self::Address = SOCKET_BASE + 0x1F;\n    const SOCKET_TX_FREE_SIZE: Self::Address = SOCKET_BASE + 0x20;\n    const SOCKET_TX_DATA_WRITE_PTR: Self::Address = SOCKET_BASE + 0x24;\n    const SOCKET_RECVD_SIZE: Self::Address = SOCKET_BASE + 0x26;\n    const SOCKET_RX_DATA_READ_PTR: Self::Address = SOCKET_BASE + 0x28;\n    const SOCKET_INTR_MASK: Self::Address = SOCKET_BASE + 0x2C;\n    const SOCKET_INTR: Self::Address = SOCKET_BASE + 0x02;\n    const SOCKET_INTR_CLR: Self::Address = SOCKET_BASE + 0x02;\n\n    const SOCKET_MODE_VALUE: u8 = (1 << 2) | (1 << 6);\n\n    const BUF_SIZE: u16 = 0x2000;\n    const AUTO_WRAP: bool = false;\n\n    fn rx_addr(addr: u16) -> Self::Address {\n        RX_BASE + addr\n    }\n\n    fn tx_addr(addr: u16) -> Self::Address {\n        TX_BASE + addr\n    }\n\n    async fn bus_read<SPI: SpiDevice>(\n        spi: &mut SPI,\n        address: Self::Address,\n        data: &mut [u8],\n    ) -> Result<(), SPI::Error> {\n        spi.transaction(&mut [\n            Operation::Write(&[0x0F, (address >> 8) as u8, address as u8]),\n            Operation::Read(data),\n        ])\n        .await\n    }\n\n    async fn bus_write<SPI: SpiDevice>(spi: &mut SPI, address: Self::Address, data: &[u8]) -> Result<(), SPI::Error> {\n        spi.transaction(&mut [\n            Operation::Write(&[0xF0, (address >> 8) as u8, address as u8]),\n            Operation::Write(data),\n        ])\n        .await\n    }\n}\n"
  },
  {
    "path": "embassy-net-wiznet/src/chip/w5500.rs",
    "content": "use embedded_hal_async::spi::{Operation, SpiDevice};\n\n#[repr(u8)]\npub enum RegisterBlock {\n    Common = 0x00,\n    Socket0 = 0x01,\n    TxBuf = 0x02,\n    RxBuf = 0x03,\n}\n\n/// Wiznet W5500 chip.\npub enum W5500 {}\n\nimpl super::Chip for W5500 {}\nimpl super::SealedChip for W5500 {\n    type Address = (RegisterBlock, u16);\n\n    const CHIP_VERSION: u8 = 0x04;\n\n    const COMMON_MODE: Self::Address = (RegisterBlock::Common, 0x00);\n    const COMMON_MAC: Self::Address = (RegisterBlock::Common, 0x09);\n    const COMMON_SOCKET_INTR: Self::Address = (RegisterBlock::Common, 0x18);\n    const COMMON_PHY_CFG: Self::Address = (RegisterBlock::Common, 0x2E);\n    const COMMON_VERSION: Self::Address = (RegisterBlock::Common, 0x39);\n\n    const SOCKET_MODE: Self::Address = (RegisterBlock::Socket0, 0x00);\n    const SOCKET_COMMAND: Self::Address = (RegisterBlock::Socket0, 0x01);\n    const SOCKET_RXBUF_SIZE: Self::Address = (RegisterBlock::Socket0, 0x1E);\n    const SOCKET_TXBUF_SIZE: Self::Address = (RegisterBlock::Socket0, 0x1F);\n    const SOCKET_TX_FREE_SIZE: Self::Address = (RegisterBlock::Socket0, 0x20);\n    const SOCKET_TX_DATA_WRITE_PTR: Self::Address = (RegisterBlock::Socket0, 0x24);\n    const SOCKET_RECVD_SIZE: Self::Address = (RegisterBlock::Socket0, 0x26);\n    const SOCKET_RX_DATA_READ_PTR: Self::Address = (RegisterBlock::Socket0, 0x28);\n    const SOCKET_INTR_MASK: Self::Address = (RegisterBlock::Socket0, 0x2C);\n    const SOCKET_INTR: Self::Address = (RegisterBlock::Socket0, 0x02);\n    const SOCKET_INTR_CLR: Self::Address = (RegisterBlock::Socket0, 0x02);\n\n    const SOCKET_MODE_VALUE: u8 = (1 << 2) | (1 << 7);\n\n    const BUF_SIZE: u16 = 0x4000;\n    const AUTO_WRAP: bool = true;\n\n    fn rx_addr(addr: u16) -> Self::Address {\n        (RegisterBlock::RxBuf, addr)\n    }\n\n    fn tx_addr(addr: u16) -> Self::Address {\n        (RegisterBlock::TxBuf, addr)\n    }\n\n    async fn bus_read<SPI: SpiDevice>(\n        spi: &mut SPI,\n        address: Self::Address,\n        data: &mut [u8],\n    ) -> Result<(), SPI::Error> {\n        let address_phase = address.1.to_be_bytes();\n        let control_phase = [(address.0 as u8) << 3];\n        let operations = &mut [\n            Operation::Write(&address_phase),\n            Operation::Write(&control_phase),\n            Operation::TransferInPlace(data),\n        ];\n        spi.transaction(operations).await\n    }\n\n    async fn bus_write<SPI: SpiDevice>(spi: &mut SPI, address: Self::Address, data: &[u8]) -> Result<(), SPI::Error> {\n        let address_phase = address.1.to_be_bytes();\n        let control_phase = [(address.0 as u8) << 3 | 0b0000_0100];\n        let data_phase = data;\n        let operations = &mut [\n            Operation::Write(&address_phase[..]),\n            Operation::Write(&control_phase),\n            Operation::Write(&data_phase),\n        ];\n        spi.transaction(operations).await\n    }\n}\n"
  },
  {
    "path": "embassy-net-wiznet/src/chip/w6100.rs",
    "content": "use embedded_hal_async::spi::{Operation, SpiDevice};\n\n#[repr(u8)]\npub enum RegisterBlock {\n    Common = 0x00,\n    Socket0 = 0x01,\n    TxBuf = 0x02,\n    RxBuf = 0x03,\n}\n\n/// Wiznet W6100 chip.\npub enum W6100 {}\n\nimpl super::Chip for W6100 {}\nimpl super::SealedChip for W6100 {\n    type Address = (RegisterBlock, u16);\n\n    const CHIP_VERSION: u8 = 0x46;\n\n    const COMMON_MODE: Self::Address = (RegisterBlock::Common, 0x2004);\n    const COMMON_MAC: Self::Address = (RegisterBlock::Common, 0x4120);\n    // SIMR (SOCKET Interrupt Mask Register)\n    const COMMON_SOCKET_INTR: Self::Address = (RegisterBlock::Common, 0x2114);\n    const COMMON_PHY_CFG: Self::Address = (RegisterBlock::Common, 0x3000);\n    const COMMON_VERSION: Self::Address = (RegisterBlock::Common, 0x0002);\n\n    const SOCKET_MODE: Self::Address = (RegisterBlock::Socket0, 0x0000);\n    const SOCKET_COMMAND: Self::Address = (RegisterBlock::Socket0, 0x0010);\n    const SOCKET_RXBUF_SIZE: Self::Address = (RegisterBlock::Socket0, 0x0220);\n    const SOCKET_TXBUF_SIZE: Self::Address = (RegisterBlock::Socket0, 0x0200);\n    const SOCKET_TX_FREE_SIZE: Self::Address = (RegisterBlock::Socket0, 0x0204);\n    const SOCKET_TX_DATA_WRITE_PTR: Self::Address = (RegisterBlock::Socket0, 0x020C);\n    const SOCKET_RECVD_SIZE: Self::Address = (RegisterBlock::Socket0, 0x0224);\n    const SOCKET_RX_DATA_READ_PTR: Self::Address = (RegisterBlock::Socket0, 0x0228);\n    // Sn_IMR (SOCKET n Interrupt Mask Register)\n    const SOCKET_INTR_MASK: Self::Address = (RegisterBlock::Socket0, 0x0024);\n    // Sn_IR (SOCKET n Interrupt Register)\n    const SOCKET_INTR: Self::Address = (RegisterBlock::Socket0, 0x0020);\n    // Sn_IRCLR (Sn_IR Clear Register)\n    const SOCKET_INTR_CLR: Self::Address = (RegisterBlock::Socket0, 0x0028);\n\n    // MACRAW mode. See Page 57 of https://docs.wiznet.io/img/products/w6100/w6100_ds_v105e.pdf\n    // Note: Bit 7 is MAC filter. On the W5500 this is normally turned ON however the W6100 will not successfully retrieve an IP address with this enabled. Disabling for now and will have live with the extra noise.\n    const SOCKET_MODE_VALUE: u8 = 0b0000_0111;\n\n    const BUF_SIZE: u16 = 0x1000;\n    const AUTO_WRAP: bool = true;\n\n    fn rx_addr(addr: u16) -> Self::Address {\n        (RegisterBlock::RxBuf, addr)\n    }\n\n    fn tx_addr(addr: u16) -> Self::Address {\n        (RegisterBlock::TxBuf, addr)\n    }\n\n    async fn bus_read<SPI: SpiDevice>(\n        spi: &mut SPI,\n        address: Self::Address,\n        data: &mut [u8],\n    ) -> Result<(), SPI::Error> {\n        let address_phase = address.1.to_be_bytes();\n        let control_phase = [(address.0 as u8) << 3];\n        let operations = &mut [\n            Operation::Write(&address_phase),\n            Operation::Write(&control_phase),\n            Operation::TransferInPlace(data),\n        ];\n        spi.transaction(operations).await\n    }\n\n    async fn bus_write<SPI: SpiDevice>(spi: &mut SPI, address: Self::Address, data: &[u8]) -> Result<(), SPI::Error> {\n        let address_phase = address.1.to_be_bytes();\n        let control_phase = [(address.0 as u8) << 3 | 0b0000_0100];\n        let data_phase = data;\n        let operations = &mut [\n            Operation::Write(&address_phase[..]),\n            Operation::Write(&control_phase),\n            Operation::Write(&data_phase),\n        ];\n        spi.transaction(operations).await\n    }\n}\n"
  },
  {
    "path": "embassy-net-wiznet/src/chip/w6300.rs",
    "content": "use embedded_hal_async::spi::{Operation, SpiDevice};\n\n#[repr(u8)]\npub enum RegisterBlock {\n    Common = 0x00,\n    Socket0 = 0x01,\n    TxBuf = 0x02,\n    RxBuf = 0x03,\n}\n\n/// Wiznet W6300 chip.\npub enum W6300 {}\n\nimpl super::Chip for W6300 {}\nimpl super::SealedChip for W6300 {\n    type Address = (RegisterBlock, u16);\n\n    // CIDR2 Minor Chip ID\n    const CHIP_VERSION: u8 = 0x11;\n\n    const COMMON_MODE: Self::Address = (RegisterBlock::Common, 0x2004);\n    // SHAR0 (Source Hardware Address Register)\n    const COMMON_MAC: Self::Address = (RegisterBlock::Common, 0x4120);\n    // SIMR (SOCKET Interrupt Mask Register)\n    const COMMON_SOCKET_INTR: Self::Address = (RegisterBlock::Common, 0x2114);\n    // PHYSR (PHY Status Register)\n    const COMMON_PHY_CFG: Self::Address = (RegisterBlock::Common, 0x3000);\n    // CIDR2 (Minor Chip IP Register)\n    const COMMON_VERSION: Self::Address = (RegisterBlock::Common, 0x0004);\n\n    // Sn_MR (SOCKET n Mode Register)\n    const SOCKET_MODE: Self::Address = (RegisterBlock::Socket0, 0x0000);\n    // Sn_CR (SOCKET n Command Register)\n    const SOCKET_COMMAND: Self::Address = (RegisterBlock::Socket0, 0x0010);\n    // Sn_RX_BSR (SOCKET n RX Buffer Size Register)\n    const SOCKET_RXBUF_SIZE: Self::Address = (RegisterBlock::Socket0, 0x0220);\n    // Sn_TX_BSR (SOCKET n TX Buffer Size Register)\n    const SOCKET_TXBUF_SIZE: Self::Address = (RegisterBlock::Socket0, 0x0200);\n    // Sn_TX_FSR0 (SOCKET n TX Free Size Register)\n    const SOCKET_TX_FREE_SIZE: Self::Address = (RegisterBlock::Socket0, 0x0204);\n    // Sn_TX_WR0 (SOCKET n TX Write Pointer Register)\n    const SOCKET_TX_DATA_WRITE_PTR: Self::Address = (RegisterBlock::Socket0, 0x020C);\n    // Sn_RX_RSR0 (SOCKET n RX Received Size Register)\n    const SOCKET_RECVD_SIZE: Self::Address = (RegisterBlock::Socket0, 0x0224);\n    // Sn_RX_RD0 (SOCKET n RX Read Pointer Register)\n    const SOCKET_RX_DATA_READ_PTR: Self::Address = (RegisterBlock::Socket0, 0x0228);\n    // Sn_IMR (SOCKET n Interrupt Mask Register)\n    const SOCKET_INTR_MASK: Self::Address = (RegisterBlock::Socket0, 0x0024);\n    // Sn_IR (SOCKET n Interrupt Register)\n    const SOCKET_INTR: Self::Address = (RegisterBlock::Socket0, 0x0020);\n    // Sn_IRCLR (Sn_IR Clear Register)\n    const SOCKET_INTR_CLR: Self::Address = (RegisterBlock::Socket0, 0x0028);\n\n    // MACRAW mode. See Page 57 of https://docs.wiznet.io/pdf-viewer?file=%2Fassets%2Ffiles%2F20251204_W6300_DS_V101E-4f4cd2e75de8d76f51a741f6a492ea01.pdf\n    // Note: Bit 7 is MAC filter. On the W5500 this is normally turned ON however the W6300 will not successfully retrieve an IP address with this enabled. Disabling for now and will have live with the extra noise.\n    const SOCKET_MODE_VALUE: u8 = 0b0000_0111;\n\n    const BUF_SIZE: u16 = 0x1000;\n    const AUTO_WRAP: bool = true;\n\n    fn rx_addr(addr: u16) -> Self::Address {\n        (RegisterBlock::RxBuf, addr)\n    }\n\n    fn tx_addr(addr: u16) -> Self::Address {\n        (RegisterBlock::TxBuf, addr)\n    }\n\n    async fn bus_read<SPI: SpiDevice>(\n        spi: &mut SPI,\n        address: Self::Address,\n        data: &mut [u8],\n    ) -> Result<(), SPI::Error> {\n        let instruction_phase = [address.0 as u8];\n        let address_phase = address.1.to_be_bytes();\n        let dummy_phase = [0u8];\n        let operations = &mut [\n            Operation::Write(&instruction_phase),\n            Operation::Write(&address_phase),\n            Operation::Write(&dummy_phase),\n            Operation::TransferInPlace(data),\n        ];\n        spi.transaction(operations).await\n    }\n\n    async fn bus_write<SPI: SpiDevice>(spi: &mut SPI, address: Self::Address, data: &[u8]) -> Result<(), SPI::Error> {\n        // Set the Write Access Bit\n        let instruction_phase = [(address.0 as u8) | 0b0010_0000];\n        let address_phase = address.1.to_be_bytes();\n        let dummy_phase = [0u8];\n\n        let operations = &mut [\n            Operation::Write(&instruction_phase),\n            Operation::Write(&address_phase),\n            Operation::Write(&dummy_phase),\n            Operation::Write(data),\n        ];\n        spi.transaction(operations).await\n    }\n}\n"
  },
  {
    "path": "embassy-net-wiznet/src/device.rs",
    "content": "use core::marker::PhantomData;\n\nuse embedded_hal_async::spi::SpiDevice;\n\nuse crate::chip::Chip;\n\n#[repr(u8)]\nenum Command {\n    Open = 0x01,\n    Send = 0x20,\n    Receive = 0x40,\n}\n\n#[repr(u8)]\nenum Interrupt {\n    Receive = 0b00100_u8,\n}\n\n/// Wiznet chip in MACRAW mode\n#[derive(Debug)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub(crate) struct WiznetDevice<C, SPI> {\n    spi: SPI,\n    _phantom: PhantomData<C>,\n}\n\n/// Error type when initializing a new Wiznet device\npub enum InitError<SE> {\n    /// Error occurred when sending or receiving SPI data\n    SpiError(SE),\n    /// The chip returned a version that isn't expected or supported\n    InvalidChipVersion {\n        /// The version that is supported\n        expected: u8,\n        /// The version that was returned by the chip\n        actual: u8,\n    },\n}\n\nimpl<SE> From<SE> for InitError<SE> {\n    fn from(e: SE) -> Self {\n        InitError::SpiError(e)\n    }\n}\n\nimpl<SE> core::fmt::Debug for InitError<SE>\nwhere\n    SE: core::fmt::Debug,\n{\n    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {\n        match self {\n            InitError::SpiError(e) => write!(f, \"SpiError({:?})\", e),\n            InitError::InvalidChipVersion { expected, actual } => {\n                write!(f, \"InvalidChipVersion {{ expected: {}, actual: {} }}\", expected, actual)\n            }\n        }\n    }\n}\n\n#[cfg(feature = \"defmt\")]\nimpl<SE> defmt::Format for InitError<SE>\nwhere\n    SE: defmt::Format,\n{\n    fn format(&self, f: defmt::Formatter) {\n        match self {\n            InitError::SpiError(e) => defmt::write!(f, \"SpiError({})\", e),\n            InitError::InvalidChipVersion { expected, actual } => {\n                defmt::write!(f, \"InvalidChipVersion {{ expected: {}, actual: {} }}\", expected, actual)\n            }\n        }\n    }\n}\n\nimpl<C: Chip, SPI: SpiDevice> WiznetDevice<C, SPI> {\n    /// Create and initialize the driver\n    pub async fn new(spi: SPI, mac_addr: [u8; 6]) -> Result<Self, InitError<SPI::Error>> {\n        let mut this = Self {\n            spi,\n            _phantom: PhantomData,\n        };\n\n        // Reset device\n        this.bus_write(C::COMMON_MODE, &[0x80]).await?;\n\n        // Check the version of the chip\n        let mut version = [0];\n        this.bus_read(C::COMMON_VERSION, &mut version).await?;\n        if version[0] != C::CHIP_VERSION {\n            #[cfg(feature = \"defmt\")]\n            defmt::error!(\"invalid chip version: {} (expected {})\", version[0], C::CHIP_VERSION);\n            return Err(InitError::InvalidChipVersion {\n                actual: version[0],\n                expected: C::CHIP_VERSION,\n            });\n        }\n\n        // Enable interrupt pin\n        this.bus_write(C::COMMON_SOCKET_INTR, &[0x01]).await?;\n        // Enable receive interrupt\n        this.bus_write(C::SOCKET_INTR_MASK, &[Interrupt::Receive as u8]).await?;\n\n        // Set MAC address\n        this.bus_write(C::COMMON_MAC, &mac_addr).await?;\n\n        // Set the raw socket RX/TX buffer sizes.\n        let buf_kbs = (C::BUF_SIZE / 1024) as u8;\n        this.bus_write(C::SOCKET_TXBUF_SIZE, &[buf_kbs]).await?;\n        this.bus_write(C::SOCKET_RXBUF_SIZE, &[buf_kbs]).await?;\n\n        // MACRAW mode with MAC filtering.\n        this.bus_write(C::SOCKET_MODE, &[C::SOCKET_MODE_VALUE]).await?;\n        this.command(Command::Open).await?;\n\n        Ok(this)\n    }\n\n    async fn bus_read(&mut self, address: C::Address, data: &mut [u8]) -> Result<(), SPI::Error> {\n        C::bus_read(&mut self.spi, address, data).await\n    }\n\n    async fn bus_write(&mut self, address: C::Address, data: &[u8]) -> Result<(), SPI::Error> {\n        C::bus_write(&mut self.spi, address, data).await\n    }\n\n    async fn reset_interrupt(&mut self, code: Interrupt) -> Result<(), SPI::Error> {\n        let data = [code as u8];\n        self.bus_write(C::SOCKET_INTR_CLR, &data).await\n    }\n\n    async fn get_tx_write_ptr(&mut self) -> Result<u16, SPI::Error> {\n        let mut data = [0u8; 2];\n        self.bus_read(C::SOCKET_TX_DATA_WRITE_PTR, &mut data).await?;\n        Ok(u16::from_be_bytes(data))\n    }\n\n    async fn set_tx_write_ptr(&mut self, ptr: u16) -> Result<(), SPI::Error> {\n        let data = ptr.to_be_bytes();\n        self.bus_write(C::SOCKET_TX_DATA_WRITE_PTR, &data).await\n    }\n\n    async fn get_rx_read_ptr(&mut self) -> Result<u16, SPI::Error> {\n        let mut data = [0u8; 2];\n        self.bus_read(C::SOCKET_RX_DATA_READ_PTR, &mut data).await?;\n        Ok(u16::from_be_bytes(data))\n    }\n\n    async fn set_rx_read_ptr(&mut self, ptr: u16) -> Result<(), SPI::Error> {\n        let data = ptr.to_be_bytes();\n        self.bus_write(C::SOCKET_RX_DATA_READ_PTR, &data).await\n    }\n\n    async fn command(&mut self, command: Command) -> Result<(), SPI::Error> {\n        let data = [command as u8];\n        self.bus_write(C::SOCKET_COMMAND, &data).await\n    }\n\n    async fn get_rx_size(&mut self) -> Result<u16, SPI::Error> {\n        loop {\n            // Wait until two sequential reads are equal\n            let mut res0 = [0u8; 2];\n            self.bus_read(C::SOCKET_RECVD_SIZE, &mut res0).await?;\n            let mut res1 = [0u8; 2];\n            self.bus_read(C::SOCKET_RECVD_SIZE, &mut res1).await?;\n            if res0 == res1 {\n                break Ok(u16::from_be_bytes(res0));\n            }\n        }\n    }\n\n    async fn get_tx_free_size(&mut self) -> Result<u16, SPI::Error> {\n        let mut data = [0; 2];\n        self.bus_read(C::SOCKET_TX_FREE_SIZE, &mut data).await?;\n        Ok(u16::from_be_bytes(data))\n    }\n\n    /// Read bytes from the RX buffer.\n    async fn read_bytes(&mut self, read_ptr: &mut u16, buffer: &mut [u8]) -> Result<(), SPI::Error> {\n        if C::AUTO_WRAP {\n            self.bus_read(C::rx_addr(*read_ptr), buffer).await?;\n        } else {\n            let addr = *read_ptr % C::BUF_SIZE;\n            if addr as usize + buffer.len() <= C::BUF_SIZE as usize {\n                self.bus_read(C::rx_addr(addr), buffer).await?;\n            } else {\n                let n = C::BUF_SIZE - addr;\n                self.bus_read(C::rx_addr(addr), &mut buffer[..n as usize]).await?;\n                self.bus_read(C::rx_addr(0), &mut buffer[n as usize..]).await?;\n            }\n        }\n\n        *read_ptr = (*read_ptr).wrapping_add(buffer.len() as u16);\n\n        Ok(())\n    }\n\n    /// Read an ethernet frame from the device. Returns the number of bytes read.\n    pub async fn read_frame(&mut self, frame: &mut [u8]) -> Result<usize, SPI::Error> {\n        let rx_size = self.get_rx_size().await? as usize;\n        if rx_size == 0 {\n            return Ok(0);\n        }\n\n        self.reset_interrupt(Interrupt::Receive).await?;\n\n        let mut read_ptr = self.get_rx_read_ptr().await?;\n\n        // First two bytes gives the size of the received ethernet frame\n        let expected_frame_size: usize = {\n            let mut frame_bytes = [0u8; 2];\n            self.read_bytes(&mut read_ptr, &mut frame_bytes).await?;\n            u16::from_be_bytes(frame_bytes) as usize - 2\n        };\n\n        // Read the ethernet frame\n        self.read_bytes(&mut read_ptr, &mut frame[..expected_frame_size])\n            .await?;\n\n        // Register RX as completed\n        self.set_rx_read_ptr(read_ptr).await?;\n        self.command(Command::Receive).await?;\n\n        Ok(expected_frame_size)\n    }\n\n    /// Write an ethernet frame to the device. Returns number of bytes written\n    pub async fn write_frame(&mut self, frame: &[u8]) -> Result<usize, SPI::Error> {\n        while self.get_tx_free_size().await? < frame.len() as u16 {}\n        let write_ptr = self.get_tx_write_ptr().await?;\n\n        if C::AUTO_WRAP {\n            self.bus_write(C::tx_addr(write_ptr), frame).await?;\n        } else {\n            let addr = write_ptr % C::BUF_SIZE;\n            if addr as usize + frame.len() <= C::BUF_SIZE as usize {\n                self.bus_write(C::tx_addr(addr), frame).await?;\n            } else {\n                let n = C::BUF_SIZE - addr;\n                self.bus_write(C::tx_addr(addr), &frame[..n as usize]).await?;\n                self.bus_write(C::tx_addr(0), &frame[n as usize..]).await?;\n            }\n        }\n\n        self.set_tx_write_ptr(write_ptr.wrapping_add(frame.len() as u16))\n            .await?;\n        self.command(Command::Send).await?;\n        Ok(frame.len())\n    }\n\n    pub async fn is_link_up(&mut self) -> bool {\n        let mut link = [0];\n        self.bus_read(C::COMMON_PHY_CFG, &mut link).await.ok();\n        link[0] & 1 == 1\n    }\n}\n"
  },
  {
    "path": "embassy-net-wiznet/src/lib.rs",
    "content": "#![no_std]\n#![allow(async_fn_in_trait)]\n#![doc = include_str!(\"../README.md\")]\n#![warn(missing_docs)]\n\npub mod chip;\nmod device;\n\nuse embassy_futures::select::{Either3, select3};\nuse embassy_net_driver_channel as ch;\nuse embassy_net_driver_channel::driver::LinkState;\nuse embassy_time::{Duration, Ticker, Timer};\nuse embedded_hal::digital::OutputPin;\nuse embedded_hal_async::digital::Wait;\nuse embedded_hal_async::spi::SpiDevice;\n\nuse crate::chip::Chip;\npub use crate::device::InitError;\nuse crate::device::WiznetDevice;\n\n// If you change this update the docs of State\nconst MTU: usize = 1514;\n\n/// Type alias for the embassy-net driver.\npub type Device<'d> = embassy_net_driver_channel::Device<'d, MTU>;\n\n/// Internal state for the embassy-net integration.\n///\n/// The two generic arguments `N_RX` and `N_TX` set the size of the receive and\n/// send packet queue. With a the ethernet MTU of _1514_ this takes up `N_RX +\n/// NTX * 1514` bytes. While setting these both to 1 is the minimum this might\n/// hurt performance as a packet can not be received while processing another.\n///\n/// # Warning\n/// On devices with a small amount of ram (think ~64k) watch out with the size\n/// of there parameters. They will quickly use too much RAM.\npub struct State<const N_RX: usize, const N_TX: usize> {\n    ch_state: ch::State<MTU, N_RX, N_TX>,\n}\n\nimpl<const N_RX: usize, const N_TX: usize> State<N_RX, N_TX> {\n    /// Create a new `State`.\n    pub const fn new() -> Self {\n        Self {\n            ch_state: ch::State::new(),\n        }\n    }\n}\n\n/// Background runner for the driver.\n///\n/// You must call `.run()` in a background task for the driver to operate.\npub struct Runner<'d, C: Chip, SPI: SpiDevice, INT: Wait, RST: OutputPin> {\n    mac: WiznetDevice<C, SPI>,\n    ch: ch::Runner<'d, MTU>,\n    int: INT,\n    _reset: RST,\n}\n\n/// You must call this in a background task for the driver to operate.\nimpl<'d, C: Chip, SPI: SpiDevice, INT: Wait, RST: OutputPin> Runner<'d, C, SPI, INT, RST> {\n    /// Run the driver.\n    pub async fn run(mut self) -> ! {\n        let (state_chan, mut rx_chan, mut tx_chan) = self.ch.split();\n        let mut tick = Ticker::every(Duration::from_millis(500));\n        loop {\n            match select3(\n                async {\n                    self.int.wait_for_low().await.ok();\n                    rx_chan.rx_buf().await\n                },\n                tx_chan.tx_buf(),\n                tick.next(),\n            )\n            .await\n            {\n                Either3::First(p) => {\n                    if let Ok(n) = self.mac.read_frame(p).await {\n                        rx_chan.rx_done(n);\n                    }\n                }\n                Either3::Second(p) => {\n                    self.mac.write_frame(p).await.ok();\n                    tx_chan.tx_done();\n                }\n                Either3::Third(()) => {\n                    if self.mac.is_link_up().await {\n                        state_chan.set_link_state(LinkState::Up);\n                    } else {\n                        state_chan.set_link_state(LinkState::Down);\n                    }\n                }\n            }\n        }\n    }\n}\n\n/// Create a Wiznet ethernet chip driver for [`embassy-net`](https://crates.io/crates/embassy-net).\n///\n/// This returns two structs:\n/// - a `Device` that you must pass to the `embassy-net` stack.\n/// - a `Runner`. You must call `.run()` on it in a background task.\npub async fn new<'a, const N_RX: usize, const N_TX: usize, C: Chip, SPI: SpiDevice, INT: Wait, RST: OutputPin>(\n    mac_addr: [u8; 6],\n    state: &'a mut State<N_RX, N_TX>,\n    spi_dev: SPI,\n    int: INT,\n    mut reset: RST,\n) -> Result<(Device<'a>, Runner<'a, C, SPI, INT, RST>), InitError<SPI::Error>> {\n    // Reset the chip.\n    reset.set_low().ok();\n    // Ensure the reset is registered.\n    Timer::after_millis(1).await;\n    reset.set_high().ok();\n\n    // Wait for PLL lock. Some chips are slower than others.\n    // Slowest is w5100s which is 100ms, so let's just wait that.\n    Timer::after_millis(100).await;\n\n    let mac = WiznetDevice::new(spi_dev, mac_addr).await?;\n\n    let (runner, device) = ch::new(&mut state.ch_state, ch::driver::HardwareAddress::Ethernet(mac_addr));\n\n    Ok((\n        device,\n        Runner {\n            ch: runner,\n            mac,\n            int,\n            _reset: reset,\n        },\n    ))\n}\n"
  },
  {
    "path": "embassy-nrf/CHANGELOG.md",
    "content": "# Changelog for embassy-nrf\n\nAll notable changes to this project will be documented in this file.\n\nThe format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),\nand this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).\n\n<!-- next-header -->\n## Unreleased - ReleaseDate\n\n## 0.10.0 - 2026-03-10\n\n- feat: implement CryptoCell RNG driver (nrf52840, nrf5340, nrf9120, nrf9160)\n- bugfix: avoid hang if calling `now()` before syscounter is enabled on nrf54\n- bugfix: use correct pin count for the nrf54 chip family\n- bugfix: nrf54lm20 uses separate register for burst config\n- bugfix: enable burst for nrf54 if oversampling\n- bugfix: put SCL/SDA into high state during TWIM initialization\n- Update to embedded-io 0.7\n- Update embassy-sync to 0.8.0\n- Update embassy-embedded-hal to 0.6.0\n- Update embassy-net-driver-channel to 0.4.0\n\n## 0.9.0 - 2025-12-15\n\n- changed: apply trimming values from FICR.TRIMCNF on nrf53/54l\n- changed: do not panic on BufferedUarte overrun\n- added: allow direct access to the input pin of `gpiote::InputChannel`\n- bugfix: use DETECTMODE_SEC in GPIOTE in secure mode\n- added: allow configuring the idle state of GPIO pins connected to PWM channels\n- changed: allow configuring the PWM peripheral in the constructor of `SimplePwm`\n- changed: support setting duty cycles with inverted polarity in `SimplePwm`\n- added: support setting the duty cycles of all channels at once in `SimplePwm`\n- changed: updated to nrf-pac with nrf52/nrf53/nrf91 register layout more similar to nrf54\n- added: support for nrf54l peripherals: uart, gpiote, twim, twis, spim, spis, dppi, pwm, saadc, cracen\n- added: support for changing nrf54l clock speed\n- bugfix: Do not write to UICR from non-secure code on nrf53\n- bugfix: Add delay to uart init anomaly fix\n- changed: `BufferedUarte::read_ready` now uses the same definition for 'empty' so following read calls will not block when true is returned\n- added: add `gpiote::InputChannel::wait_for_high()` and `wait_for_low()` to wait for specific signal level\n- changed: `gpiote::InputChannel::wait()` now takes a mutable reference to `self` to avoid interference from concurrent calls\n- changed: `gpiote::InputChannel::wait()` now ensures events are seen as soon as the function is called, even if the future is not polled\n- bugfix: use correct flash size for nRF54l\n- changed: add workaround for anomaly 66 on nrf52\n- added: expose PPI events available on SPIS peripheral\n- added: add basic GRTC time driver support for nRF54L\n* added: support for nrf54l10 and nrf54l05\n* added: expose uicr write functions\n* added: support for nrf54lm20a\n- added: support buffered rram for nrf54\n\n## 0.8.0 - 2025-09-30\n\n- changed: Remove `T: Instance` generic params in all drivers.\n- changed: nrf54l: Disable glitch detection and enable DC/DC in init.\n- changed: Add embassy-net-driver-channel implementation for IEEE 802.15.4\n- changed: add persist() method for gpio and ppi\n- added: basic RTC driver\n- changed: add persist() method for gpio, gpiote, timer and ppi\n- changed: impl Drop for Timer\n- added: expose `regs` for timer driver\n- added: timer driver CC `clear_events` method\n- changed: Saadc reset in Drop impl, anomaly 241 - high power usage\n\n## 0.7.0 - 2025-08-26\n\n- bugfix: use correct analog input SAADC pins on nrf5340\n\n## 0.6.0 - 2025-08-04\n\n- changed: update to latest embassy-time-queue-utils\n\n## 0.5.0 - 2025-07-16\n\n- changed: update to latest embassy-usb-driver\n\n## 0.4.1 - 2025-07-14\n\n- changed: nrf52833: configure internal LDO\n- changed: nrf5340: add more options to clock config\n- bugfix: clean the SAADC's register while dropping\n- changed: Remove Peripheral trait, rename PeripheralRef->Peri.\n- changed: take pins before interrupts in buffered uart init\n- changed: nrf5340: add wdt support\n- changed: remove nrf radio BLE\n- changed: add Blocking/Async Mode param.\n- bugfix: fix PWM loop count\n- bugfix: fixing the nrf54l drive configuration bug\n- changed: add temp driver for nrf5340\n- changed: add support for rand 0.9\n\n## 0.3.1 - 2025-01-09\n\n- bugfix: nrf twim return errors in async\\_wait instead of waiting indefinitely\n- bugfix: fix missing setting input as disconnected.\n- changed: Modify Uarte and BufferedUarte initialization to take pins before interrupts ([#3983](https://github.com/embassy-rs/embassy/pull/3983))\n\n\n## 0.3.0 - 2025-01-06\n\nFirstly, this release switches embassy-nrf to chiptool-based `nrf-pac`\nimplementations and lots of improvements, but also changes to API like\nperipheral and interrupt naming.\n\nSecond big change is a refactoring of time driver contract with\nembassy-time-driver. From now on, the timer queue is handled by the\ntime-driver implementation and `generic-queue` feature is provided by\nthe `embassy-time-queue-utils` crate. Newly required dependencies are\nfollowing:\n  - embassy-time-0.4\n  - embassy-time-driver-0.2\n  - embassy-time-queue-utils-0.1\n\nAdd support for following NRF chips:\n  - nRF54L15 (only gpio and timer support)\n\nSupport for chip-specific features:\n  - RESET operations for nrf5340\n  - POWER operations (system-off and wake-on-field) for nrf52840 and nrf9160\n\n- nfc:\n  - Adds support for NFC Tag emulator driver\n- pwm:\n  - Fix incorrect pin assignments\n  - Properly disconnect inputs when pins are set as output\n- uart:\n  - `try_write` support for `BufferedUarte`\n  - Support for `embedded_io_async` trait\n- spim:\n  - Support SPIM4 peripheral on nrf5340-app\n- time:\n  - Generic refactor of embassy-time-driver API\n  - Fix for missed executor alarms in certain occasions (issue #3672, PR #3705).\n- twim:\n  - Implement support for transactions\n  - Remove support for consecutive Read operations due to hardware limitations\n\n## 0.2.0 - 2024-08-05\n\n- Support for NRF chips:\n  - nrf51\n  - nrf9151\n- Support for new peripherals:\n  - EGU\n  - radio - low-level support for IEEE 802.15.4 and BLE via radio peripheral\n- Peripheral changes:\n  - gpio: Drop GPIO Pin generics (API break)\n  - pdm: Fix gain register value derivation\n  - pwm:\n    - Expose `duty` method\n    - Expose `pwm::PWM_CLK_HZ` and add `is_enabled` method\n    - Allow specifying OutputDrive for PWM channels\n    - Fix infinite loop\n  - spim:\n    - Reduce trace-level messages (\"Copying SPIM tx buffer..\")\n    - Support configuring bit order for bus\n    - Allow specifying OutputDrive for SPI pins\n    - Add bounds checks for EasyDMA buffer size\n    - Implement chunked DMA transfers\n  - uart:\n    - Add support for rx- or tx-only BufferedUart\n    - Implement splitting Rx/Tx\n    - Add support for handling RX errors\n- Miscellaneous changes:\n  - Add `collapse_debuginfo` to fmt.rs macros.\n  - Drop `sealed` mod\n  - nrf52840: Add dcdc voltage parameter to configure REG0 regulator\n\n## 0.1.0 - 2024-01-12\n\n- First release with support for following NRF chips:\n  - nrf52805\n  - nrf52810\n  - nrf52811\n  - nrf52820\n  - nrf52832\n  - nrf52833\n  - nrf52840\n  - nrf5340\n  - nrf9160\n\n"
  },
  {
    "path": "embassy-nrf/Cargo.toml",
    "content": "[package]\nname = \"embassy-nrf\"\nversion = \"0.10.0\"\nedition = \"2024\"\nlicense = \"MIT OR Apache-2.0\"\ndescription = \"Embassy Hardware Abstraction Layer (HAL) for nRF series microcontrollers\"\nkeywords = [\"embedded\", \"async\", \"nordic\", \"nrf\", \"embedded-hal\"]\ncategories = [\"embedded\", \"hardware-support\", \"no-std\", \"asynchronous\"]\nrepository = \"https://github.com/embassy-rs/embassy\"\ndocumentation = \"https://docs.embassy.dev/embassy-nrf\"\n\n[package.metadata.embassy]\nbuild = [\n    {target = \"thumbv6m-none-eabi\", features = [\"gpiote\", \"nrf51\", \"time\", \"time-driver-rtc1\"]},\n    {target = \"thumbv7em-none-eabi\", features = [\"gpiote\", \"nrf52805\", \"time\", \"time-driver-rtc1\"]},\n    {target = \"thumbv7em-none-eabi\", features = [\"gpiote\", \"nrf52810\", \"time\", \"time-driver-rtc1\"]},\n    {target = \"thumbv7em-none-eabi\", features = [\"gpiote\", \"nrf52811\", \"time\", \"time-driver-rtc1\"]},\n    {target = \"thumbv7em-none-eabi\", features = [\"gpiote\", \"nrf52820\", \"time\", \"time-driver-rtc1\"]},\n    {target = \"thumbv7em-none-eabi\", features = [\"gpiote\", \"nrf52832\", \"reset-pin-as-gpio\", \"time\", \"time-driver-rtc1\"]},\n    {target = \"thumbv7em-none-eabi\", features = [\"gpiote\", \"nfc-pins-as-gpio\", \"nrf52833\", \"time\", \"time-driver-rtc1\"]},\n    {target = \"thumbv8m.main-none-eabihf\", features = [\"gpiote\", \"nrf9160-s\", \"time\", \"time-driver-rtc1\"]},\n    {target = \"thumbv8m.main-none-eabihf\", features = [\"gpiote\", \"nrf9160-ns\", \"time\", \"time-driver-rtc1\"]},\n    {target = \"thumbv8m.main-none-eabihf\", features = [\"gpiote\", \"nrf5340-app-s\", \"time\", \"time-driver-rtc1\"]},\n    {target = \"thumbv8m.main-none-eabihf\", features = [\"gpiote\", \"nrf5340-app-ns\", \"time\", \"time-driver-rtc1\"]},\n    {target = \"thumbv8m.main-none-eabihf\", features = [\"gpiote\", \"nrf5340-net\", \"time\", \"time-driver-rtc1\"]},\n    {target = \"thumbv8m.main-none-eabihf\", features = [\"gpiote\", \"nrf54l15-app-s\", \"time\", \"time-driver-grtc\"]},\n    {target = \"thumbv8m.main-none-eabihf\", features = [\"gpiote\", \"nrf54l15-app-ns\", \"time\", \"time-driver-grtc\"]},\n    {target = \"thumbv8m.main-none-eabihf\", features = [\"gpiote\", \"nrf54l10-app-s\", \"time\", \"time-driver-grtc\"]},\n    {target = \"thumbv8m.main-none-eabihf\", features = [\"gpiote\", \"nrf54l10-app-ns\", \"time\", \"time-driver-grtc\"]},\n    {target = \"thumbv8m.main-none-eabihf\", features = [\"gpiote\", \"nrf54l05-app-s\", \"time\", \"time-driver-grtc\"]},\n    {target = \"thumbv8m.main-none-eabihf\", features = [\"gpiote\", \"nrf54l05-app-ns\", \"time\", \"time-driver-grtc\"]},\n    {target = \"thumbv8m.main-none-eabihf\", features = [\"gpiote\", \"nrf54lm20-app-s\", \"time\", \"time-driver-grtc\"]},\n    {target = \"thumbv7em-none-eabi\", features = [\"gpiote\", \"nrf52840\", \"time\"]},\n    {target = \"thumbv7em-none-eabi\", features = [\"gpiote\", \"nrf52840\", \"time-driver-rtc1\"]},\n    {target = \"thumbv7em-none-eabi\", features = [\"gpiote\", \"nrf52840\", \"time\", \"time-driver-rtc1\"]},\n    {target = \"thumbv7em-none-eabi\", features = [\"gpiote\", \"log\", \"nrf52840\", \"time\"]},\n    {target = \"thumbv7em-none-eabi\", features = [\"gpiote\", \"log\", \"nrf52840\", \"time-driver-rtc1\"]},\n    {target = \"thumbv7em-none-eabi\", features = [\"gpiote\", \"log\", \"nrf52840\", \"time\", \"time-driver-rtc1\"]},\n    {target = \"thumbv7em-none-eabi\", features = [\"defmt\", \"gpiote\", \"nrf52840\", \"time\"]},\n    {target = \"thumbv7em-none-eabi\", features = [\"defmt\", \"gpiote\", \"nrf52840\", \"time-driver-rtc1\"]},\n    {target = \"thumbv7em-none-eabi\", features = [\"defmt\", \"gpiote\", \"nrf52840\", \"time\", \"time-driver-rtc1\"]},\n    {target = \"thumbv7em-none-eabi\", features = [\"defmt\", \"gpiote\", \"nrf52840\", \"time\", \"time-driver-rtc1\",\"qspi-multiwrite-flash\"]},\n    {target = \"thumbv6m-none-eabi\", features = [\"defmt\", \"nrf51\", \"time\", \"time-driver-rtc1\"]},\n    {target = \"thumbv6m-none-eabi\", features = [\"defmt\", \"nrf51\", \"time\"]},\n    {target = \"thumbv6m-none-eabi\", features = [\"nrf51\", \"time\"]},\n]\n\n[package.metadata.embassy_docs]\nsrc_base = \"https://github.com/embassy-rs/embassy/blob/embassy-nrf-v$VERSION/embassy-nrf/src/\"\nsrc_base_git = \"https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-nrf/src/\"\n\nfeatures = [\"time\", \"defmt\", \"unstable-pac\", \"gpiote\", \"time-driver-rtc1\"]\nflavors = [\n    { regex_feature = \"nrf51\", target = \"thumbv6m-none-eabi\" },\n    { regex_feature = \"nrf52.*\", target = \"thumbv7em-none-eabihf\" },\n    { regex_feature = \"nrf53.*\", target = \"thumbv8m.main-none-eabihf\" },\n    { regex_feature = \"nrf54.*\", target = \"thumbv8m.main-none-eabihf\" },\n    { regex_feature = \"nrf91.*\", target = \"thumbv8m.main-none-eabihf\" },\n]\n\n[package.metadata.docs.rs]\nfeatures = [\"nrf52840\", \"time\", \"defmt\", \"unstable-pac\", \"gpiote\", \"time-driver-rtc1\"]\nrustdoc-args = [\"--cfg\", \"docsrs\"]\n\n[features]\ndefault = [\"rt\"]\n## Cortex-M runtime (enabled by default)\nrt = [\"nrf-pac/rt\"]\n\n## Enable features requiring `embassy-time`\ntime = [\"dep:embassy-time\", \"embassy-embedded-hal/time\"]\n\n## Enable defmt\ndefmt = [\"dep:defmt\", \"embassy-hal-internal/defmt\", \"embassy-sync/defmt\", \"embassy-usb-driver/defmt\", \"embassy-embedded-hal/defmt\"]\n## Enable log\nlog = [\"dep:log\"]\n\n## Reexport the PAC for the currently enabled chip at `embassy_nrf::pac` (unstable)\nunstable-pac = []\n# This is unstable because semver-minor (non-breaking) releases of embassy-nrf may major-bump (breaking) the PAC version.\n# If this is an issue for you, you're encouraged to directly depend on a fixed version of the PAC.\n# There are no plans to make this stable.\n\n## Enable GPIO tasks and events\ngpiote = []\n\n## Use RTC1 as the time driver for `embassy-time`, with a tick rate of 32.768khz\ntime-driver-rtc1 = [\"_time-driver\", \"embassy-time-driver?/tick-hz-32_768\"]\n\n## Use GRTC (CC n=1, GRTC_1 irq) as the time driver for `embassy-time`, with a tick rate of 1 MHz\ntime-driver-grtc = [\"_time-driver\", \"embassy-time-driver?/tick-hz-1_000_000\"]\n\n## Enable embassy-net 802.15.4 driver\nnet-driver = [\"_net-driver\"]\n\n## Allow using the NFC pins as regular GPIO pins (P0_09/P0_10 on nRF52, P0_02/P0_03 on nRF53)\nnfc-pins-as-gpio = []\n\n## Allow using the RST pin as a regular GPIO pin.\n##  * nRF52805, nRF52810, nRF52811, nRF52832: P0_21\n##  * nRF52820, nRF52833, nRF52840: P0_18\nreset-pin-as-gpio = []\n\n## Allow using the LFXO pins as regular GPIO pins (P0_00/P0_01 on nRF53)\nlfxo-pins-as-gpio = []\n\n## Implements the MultiwriteNorFlash trait for QSPI. Should only be enabled if your external\n## flash supports the semantics described [here](https://docs.rs/embedded-storage/0.3.1/embedded_storage/nor_flash/trait.MultiwriteNorFlash.html)\nqspi-multiwrite-flash = []\n\n#! ### Chip selection features\n## nRF51\nnrf51 = [\"nrf-pac/nrf51\", \"_nrf51\", \"_spi-v1\"]\n## nRF52805\nnrf52805 = [\"nrf-pac/nrf52805\", \"_nrf52\", \"_spi-v1\"]\n## nRF52810\nnrf52810 = [\"nrf-pac/nrf52810\", \"_nrf52\", \"_spi-v1\"]\n## nRF52811\nnrf52811 = [\"nrf-pac/nrf52811\", \"_nrf52\", \"_spi-v1\"]\n## nRF52820\nnrf52820 = [\"nrf-pac/nrf52820\", \"_nrf52\", \"_spi-v1\"]\n## nRF52832\nnrf52832 = [\"nrf-pac/nrf52832\", \"_nrf52\", \"_nrf52832_anomaly_109\", \"_spi-v1\"]\n## nRF52833\nnrf52833 = [\"nrf-pac/nrf52833\", \"_nrf52\", \"_gpio-p1\"]\n## nRF52840\nnrf52840 = [\"nrf-pac/nrf52840\", \"_nrf52\", \"_gpio-p1\"]\n## nRF5340 application core in Secure mode\nnrf5340-app-s = [\"_nrf5340-app\", \"_s\"]\n## nRF5340 application core in Non-Secure mode\nnrf5340-app-ns = [\"_nrf5340-app\", \"_ns\"]\n## nRF5340 network core\nnrf5340-net = [\"_nrf5340-net\"]\n## nRF54L15 application core in Secure mode\nnrf54l15-app-s = [\"_nrf54l15-app\", \"_s\", \"_multi_wdt\"]\n## nRF54L15 application core in Non-Secure mode\nnrf54l15-app-ns = [\"_nrf54l15-app\", \"_ns\"]\n## nRF54L10 application core in Secure mode\nnrf54l10-app-s = [\"_nrf54l10-app\", \"_s\", \"_multi_wdt\"]\n## nRF54L10 application core in Non-Secure mode\nnrf54l10-app-ns = [\"_nrf54l10-app\", \"_ns\"]\n## nRF54L05 application core in Secure mode\nnrf54l05-app-s = [\"_nrf54l05-app\", \"_s\", \"_multi_wdt\"]\n## nRF54L05 application core in Non-Secure mode\nnrf54l05-app-ns = [\"_nrf54l05-app\", \"_ns\"]\n## nRF54LM20 application core in Secure mode\nnrf54lm20-app-s = [\"_nrf54lm20-app\", \"_s\", \"_multi_wdt\"]\n\n## nRF9160 in Secure mode\nnrf9160-s = [\"_nrf9160\", \"_s\", \"_nrf91\"]\n## nRF9160 in Non-Secure mode\nnrf9160-ns = [\"_nrf9160\", \"_ns\", \"_nrf91\"]\n## The nRF9120 is the internal part number for the nRF9161 and nRF9151.\n## nRF9120 in Secure mode\nnrf9120-s = [\"_nrf9120\", \"_s\", \"_nrf91\"]\nnrf9151-s = [\"nrf9120-s\"]\nnrf9161-s = [\"nrf9120-s\"]\n## nRF9120 in Non-Secure mode\nnrf9120-ns = [\"_nrf9120\", \"_ns\", \"_nrf91\"]\nnrf9151-ns = [\"nrf9120-ns\"]\nnrf9161-ns = [\"nrf9120-ns\"]\n\n# Features starting with `_` are for internal use only. They're not intended\n# to be enabled by other crates, and are not covered by semver guarantees.\n\n_nrf5340-app = [\"_nrf5340\", \"_multi_wdt\", \"nrf-pac/nrf5340-app\"]\n_nrf5340-net = [\"_nrf5340\", \"nrf-pac/nrf5340-net\"]\n_nrf5340 = [\"_gpio-p1\", \"_dppi\"]\n_nrf54l15-app = [\"_nrf54l15\", \"nrf-pac/nrf54l15-app\"]\n_nrf54l15 = [\"_nrf54l\", \"_gpio-p1\", \"_gpio-p2\"]\n_nrf54l10-app = [\"_nrf54l10\", \"nrf-pac/nrf54l10-app\"]\n_nrf54l10 = [\"_nrf54l\", \"_gpio-p1\", \"_gpio-p2\"]\n_nrf54l05-app = [\"_nrf54l05\", \"nrf-pac/nrf54l05-app\"]\n_nrf54l05 = [\"_nrf54l\", \"_gpio-p1\", \"_gpio-p2\"]\n_nrf54lm20-app = [\"_nrf54lm20\", \"nrf-pac/nrf54lm20a-app\"]\n_nrf54lm20 = [\"_nrf54l\", \"_gpio-p1\", \"_gpio-p2\"]\n_nrf54l = [\"_dppi\", \"_grtc\"]\n\n_nrf9160 = [\"nrf-pac/nrf9160\", \"_dppi\", \"_spi-v1\"]\n_nrf9120 = [\"nrf-pac/nrf9120\", \"_dppi\", \"_spi-v1\"]\n_nrf52 = [\"_ppi\"]\n_nrf51 = [\"_ppi\", \"_spi-v1\"]\n_nrf91 = []\n\n_time-driver = [\"dep:embassy-time-driver\", \"dep:embassy-time-queue-utils\", \"embassy-embedded-hal/time\"]\n\n_net-driver = [\"dep:embassy-net-driver-channel\"]\n\n# trustzone state.\n_s = []\n_ns = []\n\n_ppi = []\n_dppi = []\n_gpio-p1 = []\n_gpio-p2 = []\n_spi-v1 = []\n_grtc = []\n\n# Errata workarounds\n_nrf52832_anomaly_109 = []\n\n# watchdog timer\n_multi_wdt = []\n\n[dependencies]\nembassy-time-driver = { version = \"0.2.2\", path = \"../embassy-time-driver\", optional = true }\nembassy-time-queue-utils = { version = \"0.3.0\", path = \"../embassy-time-queue-utils\", optional = true }\nembassy-time = { version = \"0.5.1\", path = \"../embassy-time\", optional = true }\nembassy-sync = { version = \"0.8.0\", path = \"../embassy-sync\" }\nembassy-hal-internal = { version = \"0.5.0\", path = \"../embassy-hal-internal\", features = [\"cortex-m\", \"prio-bits-3\"] }\nembassy-embedded-hal = { version = \"0.6.0\", path = \"../embassy-embedded-hal\", default-features = false }\nembassy-usb-driver = { version = \"0.2.0\", path = \"../embassy-usb-driver\" }\nembassy-net-driver-channel = { version = \"0.4.0\", path = \"../embassy-net-driver-channel\", optional = true}\nembassy-futures = { version = \"0.1.2\", path = \"../embassy-futures\" }\n\nembedded-hal-02 = { package = \"embedded-hal\", version = \"0.2.6\", features = [\"unproven\"] }\nembedded-hal-1 = { package = \"embedded-hal\", version = \"1.0\" }\nembedded-hal-async = { version = \"1.0\" }\nembedded-io = { version = \"0.7.1\" }\nembedded-io-async = { version = \"0.7.0\" }\n\nrand-core-06 = { package = \"rand_core\", version = \"0.6\" }\nrand-core-09 = { package = \"rand_core\", version = \"0.9\" }\n\n# nrf-pac = { version = \"0.2.0\", git = \"https://github.com/embassy-rs/nrf-pac.git\", rev = \"074935b9b24ab302fe812f91725937f57cd082cf\" }\nnrf-pac = \"0.3.0\"\n\ndefmt = { version = \"1.0.1\", optional = true }\nbitflags = \"2.10.0\"\nlog = { version = \"0.4.14\", optional = true }\ncortex-m-rt = \">=0.6.15,<0.8\"\ncortex-m = \"0.7.6\"\ncritical-section = \"1.1\"\nfixed = \"1.10.0\"\nembedded-storage = \"0.3.1\"\nembedded-storage-async = \"0.4.1\"\ncfg-if = \"1.0.0\"\ndocument-features = \"0.2.7\"\n"
  },
  {
    "path": "embassy-nrf/README.md",
    "content": "# Embassy nRF HAL\n\nHALs implement safe, idiomatic Rust APIs to use the hardware capabilities, so raw register manipulation is not needed.\n\nThe Embassy nRF HAL targets the Nordic Semiconductor nRF family of hardware. The HAL implements both blocking and async APIs\nfor many peripherals. The benefit of using the async APIs is that the HAL takes care of waiting for peripherals to\ncomplete operations in low power mode and handling interrupts, so that applications can focus on more important matters.\n\nNOTE: The Embassy HALs can be used both for non-async and async operations. For async, you can choose which runtime you want to use.\n\nFor a complete list of available peripherals and features, see the [embassy-nrf documentation](https://docs.embassy.dev/embassy-nrf).\n\n## Hardware support\n\nThe `embassy-nrf` HAL supports most variants of the nRF family:\n\n* nRF51 ([examples](https://github.com/embassy-rs/embassy/tree/main/examples/nrf51))\n* nRF52 ([examples](https://github.com/embassy-rs/embassy/tree/main/examples/nrf52840))\n* nRF53 ([examples](https://github.com/embassy-rs/embassy/tree/main/examples/nrf5340))\n* nRF54 ([examples](https://github.com/embassy-rs/embassy/tree/main/examples/nrf54l15))\n* nRF91 ([examples](https://github.com/embassy-rs/embassy/tree/main/examples/nrf9160))\n\nMost peripherals are supported, but can vary between chip families. To check what's available, make sure to pick the MCU you're targeting in the top menu in the [documentation](https://docs.embassy.dev/embassy-nrf).\n\nFor MCUs with TrustZone support, both Secure (S) and Non-Secure (NS) modes are supported. Running in Secure mode\nallows running Rust code without a SPM or TF-M binary, saving flash space and simplifying development.\n\n## Time driver\n\nIf the `time-driver-rtc1` feature is enabled, the HAL uses the RTC peripheral as a global time driver for [embassy-time](https://crates.io/crates/embassy-time), with a tick rate of 32768 Hz.\n\n## Embassy-net-driver\n\nIf the board supports IEEE 802.15.4 (see `src/radio/mod.rs`) the corresponding [embassy-net-driver](https://crates.io/crates/embassy-net-driver) implementation can be enabled with the feature `net-driver`.\n\n## Embedded-hal\n\nThe `embassy-nrf` HAL implements the traits from [embedded-hal](https://crates.io/crates/embedded-hal) (v0.2 and 1.0) and [embedded-hal-async](https://crates.io/crates/embedded-hal-async), as well as [embedded-io](https://crates.io/crates/embedded-io) and [embedded-io-async](https://crates.io/crates/embedded-io-async).\n\n## Interoperability\n\nThis crate can run on any executor.\n\nOptionally, some features requiring [`embassy-time`](https://crates.io/crates/embassy-time) can be activated with the `time` feature. If you enable it,\nyou must link an `embassy-time` driver in your project.\n\n## EasyDMA considerations\n\nOn nRF chips, peripherals can use the so called EasyDMA feature to offload the task of interacting\nwith peripherals. It takes care of sending/receiving data over a variety of bus protocols (TWI/I2C, UART, SPI).\nHowever, EasyDMA requires the buffers used to transmit and receive data to reside in RAM. Unfortunately, Rust\nslices will not always do so. The following example using the SPI peripheral shows a common situation where this might happen:\n\n```rust,ignore\n// As we pass a slice to the function whose contents will not ever change,\n// the compiler writes it into the flash and thus the pointer to it will\n// reference static memory. Since EasyDMA requires slices to reside in RAM,\n// this function call will fail.\nlet result = spim.write_from_ram(&[1, 2, 3]);\nassert_eq!(result, Err(Error::BufferNotInRAM));\n\n// The data is still static and located in flash. However, since we are assigning\n// it to a variable, the compiler will load it into memory. Passing a reference to the\n// variable will yield a pointer that references dynamic memory, thus making EasyDMA happy.\n// This function call succeeds.\nlet data = [1, 2, 3];\nlet result = spim.write_from_ram(&data);\nassert!(result.is_ok());\n```\n\nEach peripheral struct which uses EasyDMA ([`Spim`](spim::Spim), [`Uarte`](uarte::Uarte), [`Twim`](twim::Twim)) has two variants of their mutating functions:\n- Functions with the suffix (e.g. [`write_from_ram`](spim::Spim::write_from_ram), [`transfer_from_ram`](spim::Spim::transfer_from_ram)) will return an error if the passed slice does not reside in RAM.\n- Functions without the suffix (e.g. [`write`](spim::Spim::write), [`transfer`](spim::Spim::transfer)) will check whether the data is in RAM and copy it into memory prior to transmission.\n\nSince copying incurs a overhead, you are given the option to choose from `_from_ram` variants which will\nfail and notify you, or the more convenient versions without the suffix which are potentially a little bit\nmore inefficient. Be aware that this overhead is not only in terms of instruction count but also in terms of memory usage\nas the methods without the suffix will be allocating a statically sized buffer (up to 512 bytes for the nRF52840).\n\nNote that the methods that read data like [`read`](spim::Spim::read) and [`transfer_in_place`](spim::Spim::transfer_in_place) do not have the corresponding `_from_ram` variants as\nmutable slices always reside in RAM.\n"
  },
  {
    "path": "embassy-nrf/src/buffered_uarte/mod.rs",
    "content": "//! Async buffered UART driver.\n//!\n//! Note that discarding a future from a read or write operation may lead to losing\n//! data. For example, when using `futures_util::future::select` and completion occurs\n//! on the \"other\" future, you should capture the incomplete future and continue to use\n//! it for the next read or write. This pattern is a consideration for all IO, and not\n//! just serial communications.\n//!\n//! Please also see [crate::uarte] to understand when [BufferedUarte] should be used.\n#[cfg_attr(not(feature = \"_nrf54l\"), path = \"v1.rs\")]\n#[cfg_attr(feature = \"_nrf54l\", path = \"v2.rs\")]\nmod _version;\n\npub use _version::*;\n"
  },
  {
    "path": "embassy-nrf/src/buffered_uarte/v1.rs",
    "content": "//! Async buffered UART driver.\n//!\n//! Note that discarding a future from a read or write operation may lead to losing\n//! data. For example, when using `futures_util::future::select` and completion occurs\n//! on the \"other\" future, you should capture the incomplete future and continue to use\n//! it for the next read or write. This pattern is a consideration for all IO, and not\n//! just serial communications.\n//!\n//! Please also see [crate::uarte] to understand when [BufferedUarte] should be used.\n\nuse core::cmp::min;\nuse core::future::{Future, poll_fn};\nuse core::marker::PhantomData;\nuse core::slice;\nuse core::sync::atomic::{AtomicBool, AtomicU8, AtomicUsize, Ordering, compiler_fence};\nuse core::task::Poll;\n\nuse embassy_hal_internal::Peri;\nuse embassy_hal_internal::atomic_ring_buffer::RingBuffer;\nuse pac::uarte::vals;\n// Re-export SVD variants to allow user to directly set values\npub use pac::uarte::vals::{Baudrate, ConfigParity as Parity};\n\nuse crate::gpio::{AnyPin, Pin as GpioPin};\nuse crate::interrupt::InterruptExt;\nuse crate::interrupt::typelevel::Interrupt;\nuse crate::ppi::{\n    self, AnyConfigurableChannel, AnyGroup, Channel, ConfigurableChannel, Event, Group, Ppi, PpiGroup, Task,\n};\nuse crate::timer::{Instance as TimerInstance, Timer};\nuse crate::uarte::{Config, Instance as UarteInstance, configure, configure_rx_pins, configure_tx_pins, drop_tx_rx};\nuse crate::{EASY_DMA_SIZE, interrupt, pac};\n\npub(crate) struct State {\n    tx_buf: RingBuffer,\n    tx_count: AtomicUsize,\n\n    rx_buf: RingBuffer,\n    rx_started: AtomicBool,\n    rx_started_count: AtomicU8,\n    rx_ended_count: AtomicU8,\n    rx_ppi_ch: AtomicU8,\n    rx_overrun: AtomicBool,\n}\n\n/// UART error.\n#[derive(Debug, Clone, Copy, PartialEq, Eq)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\n#[non_exhaustive]\npub enum Error {\n    /// Buffer Overrun\n    Overrun,\n}\n\nimpl State {\n    pub(crate) const fn new() -> Self {\n        Self {\n            tx_buf: RingBuffer::new(),\n            tx_count: AtomicUsize::new(0),\n\n            rx_buf: RingBuffer::new(),\n            rx_started: AtomicBool::new(false),\n            rx_started_count: AtomicU8::new(0),\n            rx_ended_count: AtomicU8::new(0),\n            rx_ppi_ch: AtomicU8::new(0),\n            rx_overrun: AtomicBool::new(false),\n        }\n    }\n}\n\n/// Interrupt handler.\npub struct InterruptHandler<U: UarteInstance> {\n    _phantom: PhantomData<U>,\n}\n\nimpl<U: UarteInstance> interrupt::typelevel::Handler<U::Interrupt> for InterruptHandler<U> {\n    unsafe fn on_interrupt() {\n        //trace!(\"irq: start\");\n        let r = U::regs();\n        let ss = U::state();\n        let s = U::buffered_state();\n\n        if let Some(mut rx) = unsafe { s.rx_buf.try_writer() } {\n            let buf_len = s.rx_buf.len();\n            let half_len = buf_len / 2;\n\n            if r.events_error().read() != 0 {\n                r.events_error().write_value(0);\n                let errs = r.errorsrc().read();\n                r.errorsrc().write_value(errs);\n\n                if errs.overrun() {\n                    s.rx_overrun.store(true, Ordering::Release);\n                    ss.rx_waker.wake();\n                }\n            }\n\n            // Received some bytes, wake task.\n            if r.inten().read().rxdrdy() && r.events_rxdrdy().read() != 0 {\n                r.intenclr().write(|w| w.set_rxdrdy(true));\n                r.events_rxdrdy().write_value(0);\n                ss.rx_waker.wake();\n            }\n\n            if r.events_dma().rx().end().read() != 0 {\n                //trace!(\"  irq_rx: endrx\");\n                r.events_dma().rx().end().write_value(0);\n\n                let val = s.rx_ended_count.load(Ordering::Relaxed);\n                s.rx_ended_count.store(val.wrapping_add(1), Ordering::Relaxed);\n            }\n\n            if r.events_dma().rx().ready().read() != 0 || !s.rx_started.load(Ordering::Relaxed) {\n                //trace!(\"  irq_rx: rxstarted\");\n                let (ptr, len) = rx.push_buf();\n                if len >= half_len {\n                    r.events_dma().rx().ready().write_value(0);\n\n                    //trace!(\"  irq_rx: starting second {:?}\", half_len);\n\n                    // Set up the DMA read\n                    r.dma().rx().ptr().write_value(ptr as u32);\n                    r.dma().rx().maxcnt().write(|w| w.set_maxcnt(half_len as _));\n\n                    let chn = s.rx_ppi_ch.load(Ordering::Relaxed);\n\n                    // Enable endrx -> startrx PPI channel.\n                    // From this point on, if endrx happens, startrx is automatically fired.\n                    ppi::regs().chenset().write(|w| w.0 = 1 << chn);\n\n                    // It is possible that endrx happened BEFORE enabling the PPI. In this case\n                    // the PPI channel doesn't trigger, and we'd hang. We have to detect this\n                    // and manually start.\n\n                    // check again in case endrx has happened between the last check and now.\n                    if r.events_dma().rx().end().read() != 0 {\n                        //trace!(\"  irq_rx: endrx\");\n                        r.events_dma().rx().end().write_value(0);\n\n                        let val = s.rx_ended_count.load(Ordering::Relaxed);\n                        s.rx_ended_count.store(val.wrapping_add(1), Ordering::Relaxed);\n                    }\n\n                    let rx_ended = s.rx_ended_count.load(Ordering::Relaxed);\n                    let rx_started = s.rx_started_count.load(Ordering::Relaxed);\n\n                    // If we started the same amount of transfers as ended, the last rxend has\n                    // already occured.\n                    let rxend_happened = rx_started == rx_ended;\n\n                    // Check if the PPI channel is still enabled. The PPI channel disables itself\n                    // when it fires, so if it's still enabled it hasn't fired.\n                    let ppi_ch_enabled = ppi::regs().chen().read().ch(chn as _);\n\n                    // if rxend happened, and the ppi channel hasn't fired yet, the rxend got missed.\n                    // this condition also naturally matches if `!started`, needed to kickstart the DMA.\n                    if rxend_happened && ppi_ch_enabled {\n                        //trace!(\"manually starting.\");\n\n                        // disable the ppi ch, it's of no use anymore.\n                        ppi::regs().chenclr().write(|w| w.set_ch(chn as _, true));\n\n                        // manually start\n                        r.tasks_dma().rx().start().write_value(1);\n                    }\n\n                    rx.push_done(half_len);\n\n                    s.rx_started_count.store(rx_started.wrapping_add(1), Ordering::Relaxed);\n                    s.rx_started.store(true, Ordering::Relaxed);\n                } else {\n                    //trace!(\"  irq_rx: rxstarted no buf\");\n                    r.intenclr().write(|w| w.set_dmarxready(true));\n                }\n            }\n        }\n\n        // =============================\n\n        if let Some(mut tx) = unsafe { s.tx_buf.try_reader() } {\n            // TX end\n            if r.events_dma().tx().end().read() != 0 {\n                r.events_dma().tx().end().write_value(0);\n\n                let n = s.tx_count.load(Ordering::Relaxed);\n                //trace!(\"  irq_tx: endtx {:?}\", n);\n                tx.pop_done(n);\n                ss.tx_waker.wake();\n                s.tx_count.store(0, Ordering::Relaxed);\n            }\n\n            // If not TXing, start.\n            if s.tx_count.load(Ordering::Relaxed) == 0 {\n                let (ptr, len) = tx.pop_buf();\n                let len = len.min(EASY_DMA_SIZE);\n                if len != 0 {\n                    //trace!(\"  irq_tx: starting {:?}\", len);\n                    s.tx_count.store(len, Ordering::Relaxed);\n\n                    // Set up the DMA write\n                    r.dma().tx().ptr().write_value(ptr as u32);\n                    r.dma().tx().maxcnt().write(|w| w.set_maxcnt(len as _));\n\n                    // Start UARTE Transmit transaction\n                    r.tasks_dma().tx().start().write_value(1);\n                }\n            }\n        }\n\n        //trace!(\"irq: end\");\n    }\n}\n\n/// Buffered UARTE driver.\npub struct BufferedUarte<'d> {\n    tx: BufferedUarteTx<'d>,\n    rx: BufferedUarteRx<'d>,\n}\n\nimpl<'d> Unpin for BufferedUarte<'d> {}\n\nimpl<'d> BufferedUarte<'d> {\n    /// Create a new BufferedUarte without hardware flow control.\n    ///\n    /// # Panics\n    ///\n    /// Panics if `rx_buffer.len()` is odd.\n    #[allow(clippy::too_many_arguments)]\n    pub fn new<U: UarteInstance, T: TimerInstance>(\n        uarte: Peri<'d, U>,\n        timer: Peri<'d, T>,\n        ppi_ch1: Peri<'d, impl ConfigurableChannel>,\n        ppi_ch2: Peri<'d, impl ConfigurableChannel>,\n        ppi_group: Peri<'d, impl Group>,\n        rxd: Peri<'d, impl GpioPin>,\n        txd: Peri<'d, impl GpioPin>,\n        _irq: impl interrupt::typelevel::Binding<U::Interrupt, InterruptHandler<U>> + 'd,\n        config: Config,\n        rx_buffer: &'d mut [u8],\n        tx_buffer: &'d mut [u8],\n    ) -> Self {\n        Self::new_inner(\n            uarte,\n            timer,\n            ppi_ch1.into(),\n            ppi_ch2.into(),\n            ppi_group.into(),\n            rxd.into(),\n            txd.into(),\n            None,\n            None,\n            config,\n            rx_buffer,\n            tx_buffer,\n        )\n    }\n\n    /// Create a new BufferedUarte with hardware flow control (RTS/CTS)\n    ///\n    /// # Panics\n    ///\n    /// Panics if `rx_buffer.len()` is odd.\n    #[allow(clippy::too_many_arguments)]\n    pub fn new_with_rtscts<U: UarteInstance, T: TimerInstance>(\n        uarte: Peri<'d, U>,\n        timer: Peri<'d, T>,\n        ppi_ch1: Peri<'d, impl ConfigurableChannel>,\n        ppi_ch2: Peri<'d, impl ConfigurableChannel>,\n        ppi_group: Peri<'d, impl Group>,\n        rxd: Peri<'d, impl GpioPin>,\n        txd: Peri<'d, impl GpioPin>,\n        cts: Peri<'d, impl GpioPin>,\n        rts: Peri<'d, impl GpioPin>,\n        _irq: impl interrupt::typelevel::Binding<U::Interrupt, InterruptHandler<U>> + 'd,\n        config: Config,\n        rx_buffer: &'d mut [u8],\n        tx_buffer: &'d mut [u8],\n    ) -> Self {\n        Self::new_inner(\n            uarte,\n            timer,\n            ppi_ch1.into(),\n            ppi_ch2.into(),\n            ppi_group.into(),\n            rxd.into(),\n            txd.into(),\n            Some(cts.into()),\n            Some(rts.into()),\n            config,\n            rx_buffer,\n            tx_buffer,\n        )\n    }\n\n    #[allow(clippy::too_many_arguments)]\n    fn new_inner<U: UarteInstance, T: TimerInstance>(\n        peri: Peri<'d, U>,\n        timer: Peri<'d, T>,\n        ppi_ch1: Peri<'d, AnyConfigurableChannel>,\n        ppi_ch2: Peri<'d, AnyConfigurableChannel>,\n        ppi_group: Peri<'d, AnyGroup>,\n        rxd: Peri<'d, AnyPin>,\n        txd: Peri<'d, AnyPin>,\n        cts: Option<Peri<'d, AnyPin>>,\n        rts: Option<Peri<'d, AnyPin>>,\n        config: Config,\n        rx_buffer: &'d mut [u8],\n        tx_buffer: &'d mut [u8],\n    ) -> Self {\n        let r = U::regs();\n        let irq = U::Interrupt::IRQ;\n        let state = U::state();\n\n        configure(r, config, cts.is_some());\n\n        let tx = BufferedUarteTx::new_innerer(unsafe { peri.clone_unchecked() }, txd, cts, tx_buffer);\n        let rx = BufferedUarteRx::new_innerer(peri, timer, ppi_ch1, ppi_ch2, ppi_group, rxd, rts, rx_buffer);\n\n        r.enable().write(|w| w.set_enable(vals::Enable::ENABLED));\n        irq.pend();\n        unsafe { irq.enable() };\n\n        state.tx_rx_refcount.store(2, Ordering::Relaxed);\n\n        Self { tx, rx }\n    }\n\n    /// Adjust the baud rate to the provided value.\n    pub fn set_baudrate(&mut self, baudrate: Baudrate) {\n        self.tx.set_baudrate(baudrate);\n    }\n\n    /// Split the UART in reader and writer parts.\n    ///\n    /// This allows reading and writing concurrently from independent tasks.\n    pub fn split(self) -> (BufferedUarteRx<'d>, BufferedUarteTx<'d>) {\n        (self.rx, self.tx)\n    }\n\n    /// Split the UART in reader and writer parts, by reference.\n    ///\n    /// The returned halves borrow from `self`, so you can drop them and go back to using\n    /// the \"un-split\" `self`. This allows temporarily splitting the UART.\n    pub fn split_by_ref(&mut self) -> (&mut BufferedUarteRx<'d>, &mut BufferedUarteTx<'d>) {\n        (&mut self.rx, &mut self.tx)\n    }\n\n    /// Pull some bytes from this source into the specified buffer, returning how many bytes were read.\n    pub async fn read(&mut self, buf: &mut [u8]) -> Result<usize, Error> {\n        self.rx.read(buf).await\n    }\n\n    /// Return the contents of the internal buffer, filling it with more data from the inner reader if it is empty.\n    pub async fn fill_buf(&mut self) -> Result<&[u8], Error> {\n        self.rx.fill_buf().await\n    }\n\n    /// Tell this buffer that `amt` bytes have been consumed from the buffer, so they should no longer be returned in calls to `fill_buf`.\n    pub fn consume(&mut self, amt: usize) {\n        self.rx.consume(amt)\n    }\n\n    /// Write a buffer into this writer, returning how many bytes were written.\n    pub async fn write(&mut self, buf: &[u8]) -> Result<usize, Error> {\n        self.tx.write(buf).await\n    }\n\n    /// Try writing a buffer without waiting, returning how many bytes were written.\n    pub fn try_write(&mut self, buf: &[u8]) -> Result<usize, Error> {\n        self.tx.try_write(buf)\n    }\n\n    /// Flush this output stream, ensuring that all intermediately buffered contents reach their destination.\n    pub async fn flush(&mut self) -> Result<(), Error> {\n        self.tx.flush().await\n    }\n}\n\n/// Reader part of the buffered UARTE driver.\npub struct BufferedUarteTx<'d> {\n    r: pac::uarte::Uarte,\n    _irq: interrupt::Interrupt,\n    state: &'static crate::uarte::State,\n    buffered_state: &'static State,\n    _p: PhantomData<&'d ()>,\n}\n\nimpl<'d> BufferedUarteTx<'d> {\n    /// Create a new BufferedUarteTx without hardware flow control.\n    pub fn new<U: UarteInstance>(\n        uarte: Peri<'d, U>,\n        txd: Peri<'d, impl GpioPin>,\n        _irq: impl interrupt::typelevel::Binding<U::Interrupt, InterruptHandler<U>> + 'd,\n        config: Config,\n        tx_buffer: &'d mut [u8],\n    ) -> Self {\n        Self::new_inner(uarte, txd.into(), None, config, tx_buffer)\n    }\n\n    /// Create a new BufferedUarte with hardware flow control (RTS/CTS)\n    ///\n    /// # Panics\n    ///\n    /// Panics if `rx_buffer.len()` is odd.\n    pub fn new_with_cts<U: UarteInstance>(\n        uarte: Peri<'d, U>,\n        txd: Peri<'d, impl GpioPin>,\n        cts: Peri<'d, impl GpioPin>,\n        _irq: impl interrupt::typelevel::Binding<U::Interrupt, InterruptHandler<U>> + 'd,\n        config: Config,\n        tx_buffer: &'d mut [u8],\n    ) -> Self {\n        Self::new_inner(uarte, txd.into(), Some(cts.into()), config, tx_buffer)\n    }\n\n    fn new_inner<U: UarteInstance>(\n        peri: Peri<'d, U>,\n        txd: Peri<'d, AnyPin>,\n        cts: Option<Peri<'d, AnyPin>>,\n        config: Config,\n        tx_buffer: &'d mut [u8],\n    ) -> Self {\n        let r = U::regs();\n        let irq = U::Interrupt::IRQ;\n        let state = U::state();\n        let _buffered_state = U::buffered_state();\n\n        configure(r, config, cts.is_some());\n\n        let this = Self::new_innerer(peri, txd, cts, tx_buffer);\n\n        r.enable().write(|w| w.set_enable(vals::Enable::ENABLED));\n        irq.pend();\n        unsafe { irq.enable() };\n\n        state.tx_rx_refcount.store(1, Ordering::Relaxed);\n\n        this\n    }\n\n    fn new_innerer<U: UarteInstance>(\n        _peri: Peri<'d, U>,\n        txd: Peri<'d, AnyPin>,\n        cts: Option<Peri<'d, AnyPin>>,\n        tx_buffer: &'d mut [u8],\n    ) -> Self {\n        let r = U::regs();\n        let irq = U::Interrupt::IRQ;\n        let state = U::state();\n        let buffered_state = U::buffered_state();\n\n        configure_tx_pins(r, txd, cts);\n\n        // Initialize state\n        buffered_state.tx_count.store(0, Ordering::Relaxed);\n        let len = tx_buffer.len();\n        unsafe { buffered_state.tx_buf.init(tx_buffer.as_mut_ptr(), len) };\n\n        r.events_dma().tx().ready().write_value(0);\n\n        // Enable interrupts\n        r.intenset().write(|w| {\n            w.set_dmatxend(true);\n        });\n\n        Self {\n            r,\n            _irq: irq,\n            state,\n            buffered_state,\n            _p: PhantomData,\n        }\n    }\n\n    /// Write a buffer into this writer, returning how many bytes were written.\n    pub fn write<'a>(&'a mut self, buf: &'a [u8]) -> impl Future<Output = Result<usize, Error>> + 'a + use<'a, 'd> {\n        poll_fn(move |cx| {\n            //trace!(\"poll_write: {:?}\", buf.len());\n            let ss = self.state;\n            let s = self.buffered_state;\n            let mut tx = unsafe { s.tx_buf.writer() };\n\n            let tx_buf = tx.push_slice();\n            if tx_buf.is_empty() {\n                //trace!(\"poll_write: pending\");\n                ss.tx_waker.register(cx.waker());\n                return Poll::Pending;\n            }\n\n            let n = min(tx_buf.len(), buf.len());\n            tx_buf[..n].copy_from_slice(&buf[..n]);\n            tx.push_done(n);\n\n            //trace!(\"poll_write: queued {:?}\", n);\n\n            compiler_fence(Ordering::SeqCst);\n            self._irq.pend();\n\n            Poll::Ready(Ok(n))\n        })\n    }\n\n    /// Try writing a buffer without waiting, returning how many bytes were written.\n    pub fn try_write(&mut self, buf: &[u8]) -> Result<usize, Error> {\n        //trace!(\"poll_write: {:?}\", buf.len());\n        let s = self.buffered_state;\n        let mut tx = unsafe { s.tx_buf.writer() };\n\n        let tx_buf = tx.push_slice();\n        if tx_buf.is_empty() {\n            return Ok(0);\n        }\n\n        let n = min(tx_buf.len(), buf.len());\n        tx_buf[..n].copy_from_slice(&buf[..n]);\n        tx.push_done(n);\n\n        //trace!(\"poll_write: queued {:?}\", n);\n\n        compiler_fence(Ordering::SeqCst);\n        self._irq.pend();\n\n        Ok(n)\n    }\n\n    /// Flush this output stream, ensuring that all intermediately buffered contents reach their destination.\n    pub fn flush(&mut self) -> impl Future<Output = Result<(), Error>> + '_ {\n        let ss = self.state;\n        let s = self.buffered_state;\n        poll_fn(move |cx| {\n            //trace!(\"poll_flush\");\n            if !s.tx_buf.is_empty() {\n                //trace!(\"poll_flush: pending\");\n                ss.tx_waker.register(cx.waker());\n                return Poll::Pending;\n            }\n\n            Poll::Ready(Ok(()))\n        })\n    }\n\n    /// Adjust the baud rate to the provided value.\n    pub fn set_baudrate(&mut self, baudrate: Baudrate) {\n        self.r.baudrate().write(|w| w.set_baudrate(baudrate));\n    }\n}\n\nimpl<'a> Drop for BufferedUarteTx<'a> {\n    fn drop(&mut self) {\n        let r = self.r;\n\n        r.intenclr().write(|w| {\n            w.set_txdrdy(true);\n            w.set_dmatxready(true);\n            w.set_txstopped(true);\n        });\n        r.events_txstopped().write_value(0);\n        r.tasks_dma().tx().stop().write_value(1);\n        while r.events_txstopped().read() == 0 {}\n\n        let s = self.buffered_state;\n        unsafe { s.tx_buf.deinit() }\n\n        let s = self.state;\n        drop_tx_rx(r, s);\n    }\n}\n\n/// Reader part of the buffered UARTE driver.\npub struct BufferedUarteRx<'d> {\n    r: pac::uarte::Uarte,\n    state: &'static crate::uarte::State,\n    buffered_state: &'static State,\n    timer: Timer<'d>,\n    _ppi_ch1: Ppi<'d, AnyConfigurableChannel, 1, 1>,\n    _ppi_ch2: Ppi<'d, AnyConfigurableChannel, 1, 2>,\n    _ppi_group: PpiGroup<'d, AnyGroup>,\n    _p: PhantomData<&'d ()>,\n}\n\nimpl<'d> BufferedUarteRx<'d> {\n    /// Create a new BufferedUarte without hardware flow control.\n    ///\n    /// # Panics\n    ///\n    /// Panics if `rx_buffer.len()` is odd.\n    #[allow(clippy::too_many_arguments)]\n    pub fn new<U: UarteInstance, T: TimerInstance>(\n        uarte: Peri<'d, U>,\n        timer: Peri<'d, T>,\n        ppi_ch1: Peri<'d, impl ConfigurableChannel>,\n        ppi_ch2: Peri<'d, impl ConfigurableChannel>,\n        ppi_group: Peri<'d, impl Group>,\n        _irq: impl interrupt::typelevel::Binding<U::Interrupt, InterruptHandler<U>> + 'd,\n        rxd: Peri<'d, impl GpioPin>,\n        config: Config,\n        rx_buffer: &'d mut [u8],\n    ) -> Self {\n        Self::new_inner(\n            uarte,\n            timer,\n            ppi_ch1.into(),\n            ppi_ch2.into(),\n            ppi_group.into(),\n            rxd.into(),\n            None,\n            config,\n            rx_buffer,\n        )\n    }\n\n    /// Create a new BufferedUarte with hardware flow control (RTS/CTS)\n    ///\n    /// # Panics\n    ///\n    /// Panics if `rx_buffer.len()` is odd.\n    #[allow(clippy::too_many_arguments)]\n    pub fn new_with_rts<U: UarteInstance, T: TimerInstance>(\n        uarte: Peri<'d, U>,\n        timer: Peri<'d, T>,\n        ppi_ch1: Peri<'d, impl ConfigurableChannel>,\n        ppi_ch2: Peri<'d, impl ConfigurableChannel>,\n        ppi_group: Peri<'d, impl Group>,\n        rxd: Peri<'d, impl GpioPin>,\n        rts: Peri<'d, impl GpioPin>,\n        _irq: impl interrupt::typelevel::Binding<U::Interrupt, InterruptHandler<U>> + 'd,\n        config: Config,\n        rx_buffer: &'d mut [u8],\n    ) -> Self {\n        Self::new_inner(\n            uarte,\n            timer,\n            ppi_ch1.into(),\n            ppi_ch2.into(),\n            ppi_group.into(),\n            rxd.into(),\n            Some(rts.into()),\n            config,\n            rx_buffer,\n        )\n    }\n\n    #[allow(clippy::too_many_arguments)]\n    fn new_inner<U: UarteInstance, T: TimerInstance>(\n        peri: Peri<'d, U>,\n        timer: Peri<'d, T>,\n        ppi_ch1: Peri<'d, AnyConfigurableChannel>,\n        ppi_ch2: Peri<'d, AnyConfigurableChannel>,\n        ppi_group: Peri<'d, AnyGroup>,\n        rxd: Peri<'d, AnyPin>,\n        rts: Option<Peri<'d, AnyPin>>,\n        config: Config,\n        rx_buffer: &'d mut [u8],\n    ) -> Self {\n        let r = U::regs();\n        let irq = U::Interrupt::IRQ;\n        let state = U::state();\n        let _buffered_state = U::buffered_state();\n\n        configure(r, config, rts.is_some());\n\n        let this = Self::new_innerer(peri, timer, ppi_ch1, ppi_ch2, ppi_group, rxd, rts, rx_buffer);\n\n        r.enable().write(|w| w.set_enable(vals::Enable::ENABLED));\n        irq.pend();\n        unsafe { irq.enable() };\n\n        state.tx_rx_refcount.store(1, Ordering::Relaxed);\n\n        this\n    }\n\n    #[allow(clippy::too_many_arguments)]\n    fn new_innerer<U: UarteInstance, T: TimerInstance>(\n        _peri: Peri<'d, U>,\n        timer: Peri<'d, T>,\n        ppi_ch1: Peri<'d, AnyConfigurableChannel>,\n        ppi_ch2: Peri<'d, AnyConfigurableChannel>,\n        ppi_group: Peri<'d, AnyGroup>,\n        rxd: Peri<'d, AnyPin>,\n        rts: Option<Peri<'d, AnyPin>>,\n        rx_buffer: &'d mut [u8],\n    ) -> Self {\n        assert!(rx_buffer.len() % 2 == 0);\n\n        let r = U::regs();\n        let state = U::state();\n        let buffered_state = U::buffered_state();\n\n        configure_rx_pins(r, rxd, rts);\n\n        // Initialize state\n        buffered_state.rx_started_count.store(0, Ordering::Relaxed);\n        buffered_state.rx_ended_count.store(0, Ordering::Relaxed);\n        buffered_state.rx_started.store(false, Ordering::Relaxed);\n        buffered_state.rx_overrun.store(false, Ordering::Relaxed);\n        let rx_len = rx_buffer.len().min(EASY_DMA_SIZE * 2);\n        unsafe { buffered_state.rx_buf.init(rx_buffer.as_mut_ptr(), rx_len) };\n\n        // clear errors\n        let errors = r.errorsrc().read();\n        r.errorsrc().write_value(errors);\n\n        r.events_dma().rx().ready().write_value(0);\n        r.events_error().write_value(0);\n        r.events_dma().rx().end().write_value(0);\n\n        // Enable interrupts\n        r.intenset().write(|w| {\n            w.set_dmatxend(true);\n            w.set_dmarxready(true);\n            w.set_error(true);\n            w.set_dmarxend(true);\n        });\n\n        // Configure byte counter.\n        let timer = Timer::new_counter(timer);\n        timer.cc(1).write(rx_len as u32 * 2);\n        timer.cc(1).short_compare_clear();\n        timer.clear();\n        timer.start();\n\n        let mut ppi_ch1 = Ppi::new_one_to_one(ppi_ch1, Event::from_reg(r.events_rxdrdy()), timer.task_count());\n        ppi_ch1.enable();\n\n        buffered_state\n            .rx_ppi_ch\n            .store(ppi_ch2.number() as u8, Ordering::Relaxed);\n        let mut ppi_group = PpiGroup::new(ppi_group);\n        let mut ppi_ch2 = Ppi::new_one_to_two(\n            ppi_ch2,\n            Event::from_reg(r.events_dma().rx().end()),\n            Task::from_reg(r.tasks_dma().rx().start()),\n            ppi_group.task_disable_all(),\n        );\n        ppi_ch2.disable();\n        ppi_group.add_channel(&ppi_ch2);\n\n        Self {\n            r,\n            state,\n            buffered_state,\n            timer,\n            _ppi_ch1: ppi_ch1,\n            _ppi_ch2: ppi_ch2,\n            _ppi_group: ppi_group,\n            _p: PhantomData,\n        }\n    }\n\n    fn get_rxdrdy_counter(&self) -> usize {\n        let s = self.buffered_state;\n        let timer = &self.timer;\n\n        // Read the RXDRDY counter.\n        timer.cc(0).capture();\n        let mut rxdrdy = timer.cc(0).read() as usize;\n        //trace!(\"  rxdrdy count = {:?}\", rxdrdy);\n\n        // We've set a compare channel that resets the counter to 0 when it reaches `len*2`.\n        // However, it's unclear if that's instant, or there's a small window where you can\n        // still read `len()*2`.\n        // This could happen if in one clock cycle the counter is updated, and in the next the\n        // clear takes effect. The docs are very sparse, they just say \"Task delays: After TIMER\n        // is started, the CLEAR, COUNT, and STOP tasks are guaranteed to take effect within one\n        // clock cycle of the PCLK16M.\" :shrug:\n        // So, we wrap the counter ourselves, just in case.\n        if rxdrdy > s.rx_buf.len() * 2 {\n            rxdrdy = 0;\n        }\n\n        rxdrdy\n    }\n\n    /// Pull some bytes from this source into the specified buffer, returning how many bytes were read.\n    pub async fn read(&mut self, buf: &mut [u8]) -> Result<usize, Error> {\n        let data = self.fill_buf().await?;\n        let n = data.len().min(buf.len());\n        buf[..n].copy_from_slice(&data[..n]);\n        self.consume(n);\n        Ok(n)\n    }\n\n    /// Return the contents of the internal buffer, filling it with more data from the inner reader if it is empty.\n    pub fn fill_buf(&mut self) -> impl Future<Output = Result<&'_ [u8], Error>> {\n        let r = self.r;\n        let s = self.buffered_state;\n        let ss = self.state;\n\n        poll_fn(move |cx| {\n            compiler_fence(Ordering::SeqCst);\n            //trace!(\"poll_read\");\n\n            if s.rx_overrun.swap(false, Ordering::Acquire) {\n                return Poll::Ready(Err(Error::Overrun));\n            }\n\n            let mut end = self.get_rxdrdy_counter();\n\n            // This logic mirrors `atomic_ring_buffer::Reader::pop_buf()`\n            let mut start = s.rx_buf.start.load(Ordering::Relaxed);\n            let len = s.rx_buf.len();\n            if start == end {\n                //trace!(\"  empty\");\n                ss.rx_waker.register(cx.waker());\n                r.intenset().write(|w| w.set_rxdrdy(true));\n                return Poll::Pending;\n            }\n\n            if start >= len {\n                start -= len\n            }\n            if end >= len {\n                end -= len\n            }\n\n            let n = if end > start { end - start } else { len - start };\n            assert!(n != 0);\n            //trace!(\"  uarte ringbuf: pop_buf {:?}..{:?}\", start, start + n);\n\n            let buf = s.rx_buf.buf.load(Ordering::Relaxed);\n            Poll::Ready(Ok(unsafe { slice::from_raw_parts(buf.add(start), n) }))\n        })\n    }\n\n    /// Tell this buffer that `amt` bytes have been consumed from the buffer, so they should no longer be returned in calls to `fill_buf`.\n    pub fn consume(&mut self, amt: usize) {\n        if amt == 0 {\n            return;\n        }\n\n        let s = self.buffered_state;\n        let mut rx = unsafe { s.rx_buf.reader() };\n        rx.pop_done(amt);\n        self.r.intenset().write(|w| w.set_dmarxready(true));\n    }\n\n    /// we are ready to read if there is data in the buffer\n    fn read_ready(&self) -> Result<bool, Error> {\n        let state = self.buffered_state;\n        if state.rx_overrun.swap(false, Ordering::Acquire) {\n            return Err(Error::Overrun);\n        }\n\n        let start = state.rx_buf.start.load(Ordering::Relaxed);\n        let end = self.get_rxdrdy_counter();\n\n        Ok(start != end)\n    }\n}\n\nimpl<'a> Drop for BufferedUarteRx<'a> {\n    fn drop(&mut self) {\n        self._ppi_group.disable_all();\n\n        let r = self.r;\n\n        self.timer.stop();\n\n        r.intenclr().write(|w| {\n            w.set_rxdrdy(true);\n            w.set_dmarxready(true);\n            w.set_rxto(true);\n        });\n        r.events_rxto().write_value(0);\n        r.tasks_dma().rx().stop().write_value(1);\n        while r.events_rxto().read() == 0 {}\n\n        let s = self.buffered_state;\n        unsafe { s.rx_buf.deinit() }\n\n        let s = self.state;\n        drop_tx_rx(r, s);\n    }\n}\n\nimpl core::fmt::Display for Error {\n    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {\n        match *self {\n            Error::Overrun => write!(f, \"Buffer Overrun\"),\n        }\n    }\n}\nimpl core::error::Error for Error {}\n\nmod _embedded_io {\n    use super::*;\n\n    impl embedded_io_async::Error for Error {\n        fn kind(&self) -> embedded_io_async::ErrorKind {\n            match *self {\n                Error::Overrun => embedded_io_async::ErrorKind::OutOfMemory,\n            }\n        }\n    }\n\n    impl<'d> embedded_io_async::ErrorType for BufferedUarte<'d> {\n        type Error = Error;\n    }\n\n    impl<'d> embedded_io_async::ErrorType for BufferedUarteRx<'d> {\n        type Error = Error;\n    }\n\n    impl<'d> embedded_io_async::ErrorType for BufferedUarteTx<'d> {\n        type Error = Error;\n    }\n\n    impl<'d> embedded_io_async::Read for BufferedUarte<'d> {\n        async fn read(&mut self, buf: &mut [u8]) -> Result<usize, Self::Error> {\n            self.read(buf).await\n        }\n    }\n\n    impl<'d> embedded_io_async::Read for BufferedUarteRx<'d> {\n        async fn read(&mut self, buf: &mut [u8]) -> Result<usize, Self::Error> {\n            self.read(buf).await\n        }\n    }\n\n    impl<'d> embedded_io_async::ReadReady for BufferedUarte<'d> {\n        fn read_ready(&mut self) -> Result<bool, Self::Error> {\n            self.rx.read_ready()\n        }\n    }\n\n    impl<'d> embedded_io_async::ReadReady for BufferedUarteRx<'d> {\n        fn read_ready(&mut self) -> Result<bool, Self::Error> {\n            let state = self.buffered_state;\n            Ok(!state.rx_buf.is_empty())\n        }\n    }\n\n    impl<'d> embedded_io_async::BufRead for BufferedUarte<'d> {\n        async fn fill_buf(&mut self) -> Result<&[u8], Self::Error> {\n            self.fill_buf().await\n        }\n\n        fn consume(&mut self, amt: usize) {\n            self.consume(amt)\n        }\n    }\n\n    impl<'d> embedded_io_async::BufRead for BufferedUarteRx<'d> {\n        async fn fill_buf(&mut self) -> Result<&[u8], Self::Error> {\n            self.fill_buf().await\n        }\n\n        fn consume(&mut self, amt: usize) {\n            self.consume(amt)\n        }\n    }\n\n    impl<'d> embedded_io_async::Write for BufferedUarte<'d> {\n        async fn write(&mut self, buf: &[u8]) -> Result<usize, Self::Error> {\n            self.write(buf).await\n        }\n\n        async fn flush(&mut self) -> Result<(), Self::Error> {\n            self.flush().await\n        }\n    }\n\n    impl<'d> embedded_io_async::Write for BufferedUarteTx<'d> {\n        async fn write(&mut self, buf: &[u8]) -> Result<usize, Self::Error> {\n            self.write(buf).await\n        }\n\n        async fn flush(&mut self) -> Result<(), Self::Error> {\n            self.flush().await\n        }\n    }\n}\n"
  },
  {
    "path": "embassy-nrf/src/buffered_uarte/v2.rs",
    "content": "//! Async buffered UART driver.\n//!\n//! Note that discarding a future from a read or write operation may lead to losing\n//! data. For example, when using `futures_util::future::select` and completion occurs\n//! on the \"other\" future, you should capture the incomplete future and continue to use\n//! it for the next read or write. This pattern is a consideration for all IO, and not\n//! just serial communications.\n//!\n//! Please also see [crate::uarte] to understand when [BufferedUarte] should be used.\n//!\n//! The code is based on the generic buffered_uarte implementation but uses the nrf54l\n//! frame timeout event to correctly determine the size of transferred data.\n//! Counting of rxrdy events, used in the generic implementation, cannot be applied\n//! to nrf54l chips, as they buffer up to 4 bytes in a single DMA transaction.\n//! The only reliable way to find the number of bytes received is to stop the transfer,\n//! wait for the DMA stopped event, and read the value in the rx.dma.amount register.\n//! This also flushes all in-flight data to RAM.\n\nuse core::cmp::min;\nuse core::future::{Future, poll_fn};\nuse core::marker::PhantomData;\nuse core::slice;\nuse core::sync::atomic::{AtomicBool, AtomicUsize, Ordering, compiler_fence};\nuse core::task::Poll;\n\nuse embassy_hal_internal::Peri;\nuse embassy_hal_internal::atomic_ring_buffer::RingBuffer;\nuse pac::uarte::vals;\n// Re-export SVD variants to allow user to directly set values\npub use pac::uarte::vals::{Baudrate, ConfigParity as Parity};\n\nuse crate::gpio::{AnyPin, Pin as GpioPin};\nuse crate::interrupt::typelevel::Interrupt;\nuse crate::uarte::{Config, Instance as UarteInstance, configure, configure_rx_pins, configure_tx_pins, drop_tx_rx};\nuse crate::{EASY_DMA_SIZE, interrupt, pac};\n\npub(crate) struct State {\n    tx_buf: RingBuffer,\n    tx_count: AtomicUsize,\n\n    rx_buf: RingBuffer,\n    rx_started: AtomicBool,\n}\n\n/// UART error.\n#[derive(Debug, Clone, Copy, PartialEq, Eq)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\n#[non_exhaustive]\npub enum Error {\n    // No errors for now\n}\n\nimpl core::fmt::Display for Error {\n    fn fmt(&self, _f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {\n        match *self {}\n    }\n}\n\nimpl core::error::Error for Error {}\n\nimpl State {\n    pub(crate) const fn new() -> Self {\n        Self {\n            tx_buf: RingBuffer::new(),\n            tx_count: AtomicUsize::new(0),\n\n            rx_buf: RingBuffer::new(),\n            rx_started: AtomicBool::new(false),\n        }\n    }\n}\n\n/// Interrupt handler.\npub struct InterruptHandler<U: UarteInstance> {\n    _phantom: PhantomData<U>,\n}\n\nimpl<U: UarteInstance> interrupt::typelevel::Handler<U::Interrupt> for InterruptHandler<U> {\n    unsafe fn on_interrupt() {\n        info!(\"irq: start\");\n        let r = U::regs();\n        let ss = U::state();\n        let s = U::buffered_state();\n\n        if let Some(mut rx) = unsafe { s.rx_buf.try_writer() } {\n            let buf_len = s.rx_buf.len();\n            let half_len = buf_len / 2;\n\n            if r.events_error().read() != 0 {\n                r.events_error().write_value(0);\n                let errs = r.errorsrc().read();\n                r.errorsrc().write_value(errs);\n\n                if errs.overrun() {\n                    panic!(\"BufferedUarte UART overrun\");\n                }\n            }\n\n            let first_run = !s.rx_started.swap(true, Ordering::Relaxed);\n            if r.events_dma().rx().end().read() != 0 || first_run {\n                //trace!(\"  irq_rx: endrx\");\n                r.events_dma().rx().end().write_value(0);\n\n                if !first_run {\n                    // Received some bytes, wake task.\n                    let rxed = r.dma().rx().amount().read().amount() as usize;\n                    rx.push_done(rxed);\n                    ss.rx_waker.wake();\n                }\n\n                let (ptr, len) = rx.push_buf();\n                if len == 0 {\n                    panic!(\"BufferedUarte buffer overrun\");\n                }\n\n                let len = if len > half_len { half_len } else { len };\n\n                // Set up the DMA read\n                r.dma().rx().ptr().write_value(ptr as u32);\n                r.dma().rx().maxcnt().write(|w| w.set_maxcnt(len as _));\n\n                // manually start\n                r.tasks_dma().rx().start().write_value(1);\n            }\n        }\n\n        // =============================\n\n        if let Some(mut tx) = unsafe { s.tx_buf.try_reader() } {\n            // TX end\n            if r.events_dma().tx().end().read() != 0 {\n                r.events_dma().tx().end().write_value(0);\n\n                let n = s.tx_count.load(Ordering::Relaxed);\n                //trace!(\"  irq_tx: endtx {:?}\", n);\n                tx.pop_done(n);\n                ss.tx_waker.wake();\n                s.tx_count.store(0, Ordering::Relaxed);\n            }\n\n            // If not TXing, start.\n            if s.tx_count.load(Ordering::Relaxed) == 0 {\n                let (ptr, len) = tx.pop_buf();\n                let len = len.min(EASY_DMA_SIZE);\n                if len != 0 {\n                    //trace!(\"  irq_tx: starting {:?}\", len);\n                    s.tx_count.store(len, Ordering::Relaxed);\n\n                    // Set up the DMA write\n                    r.dma().tx().ptr().write_value(ptr as u32);\n                    r.dma().tx().maxcnt().write(|w| w.set_maxcnt(len as _));\n\n                    // Start UARTE Transmit transaction\n                    r.tasks_dma().tx().start().write_value(1);\n                }\n            }\n        }\n\n        //trace!(\"irq: end\");\n    }\n}\n\n/// Buffered UARTE driver.\npub struct BufferedUarte<'d, U: UarteInstance> {\n    tx: BufferedUarteTx<'d, U>,\n    rx: BufferedUarteRx<'d, U>,\n}\n\nimpl<'d, U: UarteInstance> Unpin for BufferedUarte<'d, U> {}\n\nimpl<'d, U: UarteInstance> BufferedUarte<'d, U> {\n    /// Create a new BufferedUarte without hardware flow control.\n    #[allow(clippy::too_many_arguments)]\n    pub fn new(\n        uarte: Peri<'d, U>,\n        rxd: Peri<'d, impl GpioPin>,\n        txd: Peri<'d, impl GpioPin>,\n        _irq: impl interrupt::typelevel::Binding<U::Interrupt, InterruptHandler<U>> + 'd,\n        config: Config,\n        rx_buffer: &'d mut [u8],\n        tx_buffer: &'d mut [u8],\n    ) -> Self {\n        Self::new_inner(uarte, rxd.into(), txd.into(), None, None, config, rx_buffer, tx_buffer)\n    }\n\n    /// Create a new BufferedUarte with hardware flow control (RTS/CTS)\n    #[allow(clippy::too_many_arguments)]\n    pub fn new_with_rtscts(\n        uarte: Peri<'d, U>,\n        rxd: Peri<'d, impl GpioPin>,\n        txd: Peri<'d, impl GpioPin>,\n        cts: Peri<'d, impl GpioPin>,\n        rts: Peri<'d, impl GpioPin>,\n        _irq: impl interrupt::typelevel::Binding<U::Interrupt, InterruptHandler<U>> + 'd,\n        config: Config,\n        rx_buffer: &'d mut [u8],\n        tx_buffer: &'d mut [u8],\n    ) -> Self {\n        Self::new_inner(\n            uarte,\n            rxd.into(),\n            txd.into(),\n            Some(cts.into()),\n            Some(rts.into()),\n            config,\n            rx_buffer,\n            tx_buffer,\n        )\n    }\n\n    #[allow(clippy::too_many_arguments)]\n    fn new_inner(\n        peri: Peri<'d, U>,\n        rxd: Peri<'d, AnyPin>,\n        txd: Peri<'d, AnyPin>,\n        cts: Option<Peri<'d, AnyPin>>,\n        rts: Option<Peri<'d, AnyPin>>,\n        config: Config,\n        rx_buffer: &'d mut [u8],\n        tx_buffer: &'d mut [u8],\n    ) -> Self {\n        configure(U::regs(), config, cts.is_some());\n\n        let tx = BufferedUarteTx::new_innerer(unsafe { peri.clone_unchecked() }, txd, cts, tx_buffer);\n        let rx = BufferedUarteRx::new_innerer(peri, rxd, rts, rx_buffer);\n\n        U::regs().enable().write(|w| w.set_enable(vals::Enable::ENABLED));\n        U::Interrupt::pend();\n        unsafe { U::Interrupt::enable() };\n\n        U::state().tx_rx_refcount.store(2, Ordering::Relaxed);\n\n        Self { tx, rx }\n    }\n\n    /// Adjust the baud rate to the provided value.\n    pub fn set_baudrate(&mut self, baudrate: Baudrate) {\n        let r = U::regs();\n        r.baudrate().write(|w| w.set_baudrate(baudrate));\n    }\n\n    /// Split the UART in reader and writer parts.\n    ///\n    /// This allows reading and writing concurrently from independent tasks.\n    pub fn split(self) -> (BufferedUarteRx<'d, U>, BufferedUarteTx<'d, U>) {\n        (self.rx, self.tx)\n    }\n\n    /// Split the UART in reader and writer parts, by reference.\n    ///\n    /// The returned halves borrow from `self`, so you can drop them and go back to using\n    /// the \"un-split\" `self`. This allows temporarily splitting the UART.\n    pub fn split_by_ref(&mut self) -> (&mut BufferedUarteRx<'d, U>, &mut BufferedUarteTx<'d, U>) {\n        (&mut self.rx, &mut self.tx)\n    }\n\n    /// Pull some bytes from this source into the specified buffer, returning how many bytes were read.\n    pub async fn read(&mut self, buf: &mut [u8]) -> Result<usize, Error> {\n        self.rx.read(buf).await\n    }\n\n    /// Return the contents of the internal buffer, filling it with more data from the inner reader if it is empty.\n    pub async fn fill_buf(&mut self) -> Result<&[u8], Error> {\n        self.rx.fill_buf().await\n    }\n\n    /// Tell this buffer that `amt` bytes have been consumed from the buffer, so they should no longer be returned in calls to `fill_buf`.\n    pub fn consume(&mut self, amt: usize) {\n        self.rx.consume(amt)\n    }\n\n    /// Write a buffer into this writer, returning how many bytes were written.\n    pub async fn write(&mut self, buf: &[u8]) -> Result<usize, Error> {\n        self.tx.write(buf).await\n    }\n\n    /// Try writing a buffer without waiting, returning how many bytes were written.\n    pub fn try_write(&mut self, buf: &[u8]) -> Result<usize, Error> {\n        self.tx.try_write(buf)\n    }\n\n    /// Flush this output stream, ensuring that all intermediately buffered contents reach their destination.\n    pub async fn flush(&mut self) -> Result<(), Error> {\n        self.tx.flush().await\n    }\n}\n\n/// Reader part of the buffered UARTE driver.\npub struct BufferedUarteTx<'d, U: UarteInstance> {\n    _peri: Peri<'d, U>,\n}\n\nimpl<'d, U: UarteInstance> BufferedUarteTx<'d, U> {\n    /// Create a new BufferedUarteTx without hardware flow control.\n    pub fn new(\n        uarte: Peri<'d, U>,\n        txd: Peri<'d, impl GpioPin>,\n        _irq: impl interrupt::typelevel::Binding<U::Interrupt, InterruptHandler<U>> + 'd,\n        config: Config,\n        tx_buffer: &'d mut [u8],\n    ) -> Self {\n        Self::new_inner(uarte, txd.into(), None, config, tx_buffer)\n    }\n\n    /// Create a new BufferedUarte with hardware flow control (RTS/CTS)\n    pub fn new_with_cts(\n        uarte: Peri<'d, U>,\n        txd: Peri<'d, impl GpioPin>,\n        cts: Peri<'d, impl GpioPin>,\n        _irq: impl interrupt::typelevel::Binding<U::Interrupt, InterruptHandler<U>> + 'd,\n        config: Config,\n        tx_buffer: &'d mut [u8],\n    ) -> Self {\n        Self::new_inner(uarte, txd.into(), Some(cts.into()), config, tx_buffer)\n    }\n\n    fn new_inner(\n        peri: Peri<'d, U>,\n        txd: Peri<'d, AnyPin>,\n        cts: Option<Peri<'d, AnyPin>>,\n        config: Config,\n        tx_buffer: &'d mut [u8],\n    ) -> Self {\n        configure(U::regs(), config, cts.is_some());\n\n        let this = Self::new_innerer(peri, txd, cts, tx_buffer);\n\n        U::regs().enable().write(|w| w.set_enable(vals::Enable::ENABLED));\n        U::Interrupt::pend();\n        unsafe { U::Interrupt::enable() };\n\n        U::state().tx_rx_refcount.store(1, Ordering::Relaxed);\n\n        this\n    }\n\n    fn new_innerer(\n        peri: Peri<'d, U>,\n        txd: Peri<'d, AnyPin>,\n        cts: Option<Peri<'d, AnyPin>>,\n        tx_buffer: &'d mut [u8],\n    ) -> Self {\n        let r = U::regs();\n\n        configure_tx_pins(r, txd, cts);\n\n        // Initialize state\n        let s = U::buffered_state();\n        s.tx_count.store(0, Ordering::Relaxed);\n        let len = tx_buffer.len();\n        unsafe { s.tx_buf.init(tx_buffer.as_mut_ptr(), len) };\n\n        r.events_dma().tx().ready().write_value(0);\n\n        // Enable interrupts\n        r.intenset().write(|w| {\n            w.set_dmatxend(true);\n        });\n\n        Self { _peri: peri }\n    }\n\n    /// Write a buffer into this writer, returning how many bytes were written.\n    pub fn write<'a>(&'a mut self, buf: &'a [u8]) -> impl Future<Output = Result<usize, Error>> + 'a {\n        poll_fn(move |cx| {\n            //trace!(\"poll_write: {:?}\", buf.len());\n            let ss = U::state();\n            let s = U::buffered_state();\n            let mut tx = unsafe { s.tx_buf.writer() };\n\n            let tx_buf = tx.push_slice();\n            if tx_buf.is_empty() {\n                //trace!(\"poll_write: pending\");\n                ss.tx_waker.register(cx.waker());\n                return Poll::Pending;\n            }\n\n            let n = min(tx_buf.len(), buf.len());\n            tx_buf[..n].copy_from_slice(&buf[..n]);\n            tx.push_done(n);\n\n            //trace!(\"poll_write: queued {:?}\", n);\n\n            compiler_fence(Ordering::SeqCst);\n            U::Interrupt::pend();\n\n            Poll::Ready(Ok(n))\n        })\n    }\n\n    /// Try writing a buffer without waiting, returning how many bytes were written.\n    pub fn try_write(&mut self, buf: &[u8]) -> Result<usize, Error> {\n        //trace!(\"poll_write: {:?}\", buf.len());\n        let s = U::buffered_state();\n        let mut tx = unsafe { s.tx_buf.writer() };\n\n        let tx_buf = tx.push_slice();\n        if tx_buf.is_empty() {\n            return Ok(0);\n        }\n\n        let n = min(tx_buf.len(), buf.len());\n        tx_buf[..n].copy_from_slice(&buf[..n]);\n        tx.push_done(n);\n\n        //trace!(\"poll_write: queued {:?}\", n);\n\n        compiler_fence(Ordering::SeqCst);\n        U::Interrupt::pend();\n\n        Ok(n)\n    }\n\n    /// Flush this output stream, ensuring that all intermediately buffered contents reach their destination.\n    pub fn flush(&mut self) -> impl Future<Output = Result<(), Error>> + '_ {\n        poll_fn(move |cx| {\n            //trace!(\"poll_flush\");\n            let ss = U::state();\n            let s = U::buffered_state();\n            if !s.tx_buf.is_empty() {\n                //trace!(\"poll_flush: pending\");\n                ss.tx_waker.register(cx.waker());\n                return Poll::Pending;\n            }\n\n            Poll::Ready(Ok(()))\n        })\n    }\n}\n\nimpl<'a, U: UarteInstance> Drop for BufferedUarteTx<'a, U> {\n    fn drop(&mut self) {\n        let r = U::regs();\n\n        r.intenclr().write(|w| {\n            w.set_txdrdy(true);\n            w.set_dmatxready(true);\n            w.set_txstopped(true);\n        });\n        r.events_txstopped().write_value(0);\n        r.tasks_dma().tx().stop().write_value(1);\n        while r.events_txstopped().read() == 0 {}\n\n        let s = U::buffered_state();\n        unsafe { s.tx_buf.deinit() }\n\n        let s = U::state();\n        drop_tx_rx(r, s);\n    }\n}\n\n/// Reader part of the buffered UARTE driver.\npub struct BufferedUarteRx<'d, U: UarteInstance> {\n    _peri: Peri<'d, U>,\n}\n\nimpl<'d, U: UarteInstance> BufferedUarteRx<'d, U> {\n    /// Create a new BufferedUarte without hardware flow control.\n    #[allow(clippy::too_many_arguments)]\n    pub fn new(\n        uarte: Peri<'d, U>,\n        _irq: impl interrupt::typelevel::Binding<U::Interrupt, InterruptHandler<U>> + 'd,\n        rxd: Peri<'d, impl GpioPin>,\n        config: Config,\n        rx_buffer: &'d mut [u8],\n    ) -> Self {\n        Self::new_inner(uarte, rxd.into(), None, config, rx_buffer)\n    }\n\n    /// Create a new BufferedUarte with hardware flow control (RTS/CTS)\n    #[allow(clippy::too_many_arguments)]\n    pub fn new_with_rts(\n        uarte: Peri<'d, U>,\n        rxd: Peri<'d, impl GpioPin>,\n        rts: Peri<'d, impl GpioPin>,\n        _irq: impl interrupt::typelevel::Binding<U::Interrupt, InterruptHandler<U>> + 'd,\n        config: Config,\n        rx_buffer: &'d mut [u8],\n    ) -> Self {\n        Self::new_inner(uarte, rxd.into(), Some(rts.into()), config, rx_buffer)\n    }\n\n    #[allow(clippy::too_many_arguments)]\n    fn new_inner(\n        peri: Peri<'d, U>,\n        rxd: Peri<'d, AnyPin>,\n        rts: Option<Peri<'d, AnyPin>>,\n        config: Config,\n        rx_buffer: &'d mut [u8],\n    ) -> Self {\n        configure(U::regs(), config, rts.is_some());\n\n        let this = Self::new_innerer(peri, rxd, rts, rx_buffer);\n\n        U::regs().enable().write(|w| w.set_enable(vals::Enable::ENABLED));\n        U::Interrupt::pend();\n        unsafe { U::Interrupt::enable() };\n\n        U::state().tx_rx_refcount.store(1, Ordering::Relaxed);\n\n        this\n    }\n\n    #[allow(clippy::too_many_arguments)]\n    fn new_innerer(\n        peri: Peri<'d, U>,\n        rxd: Peri<'d, AnyPin>,\n        rts: Option<Peri<'d, AnyPin>>,\n        rx_buffer: &'d mut [u8],\n    ) -> Self {\n        let r = U::regs();\n\n        configure_rx_pins(r, rxd, rts);\n\n        // Initialize state\n        let s = U::buffered_state();\n        let rx_len = rx_buffer.len().min(EASY_DMA_SIZE * 2);\n        let rx_ptr = rx_buffer.as_mut_ptr();\n        unsafe { s.rx_buf.init(rx_ptr, rx_len) };\n\n        // clear errors\n        let errors = r.errorsrc().read();\n        r.errorsrc().write_value(errors);\n\n        r.events_error().write_value(0);\n        r.events_dma().rx().end().write_value(0);\n\n        // set timeout-to-stop short\n        r.shorts().write(|w| {\n            w.set_frametimeout_dma_rx_stop(true);\n        });\n\n        // set default timeout\n        r.frametimeout().write_value(pac::uarte::regs::Frametimeout(0x10));\n\n        // Enable interrupts\n        r.intenset().write(|w| {\n            w.set_dmatxend(true);\n            w.set_error(true);\n            w.set_dmarxend(true);\n        });\n\n        Self { _peri: peri }\n    }\n\n    /// Pull some bytes from this source into the specified buffer, returning how many bytes were read.\n    pub async fn read(&mut self, buf: &mut [u8]) -> Result<usize, Error> {\n        let data = self.fill_buf().await?;\n        let n = data.len().min(buf.len());\n        buf[..n].copy_from_slice(&data[..n]);\n        self.consume(n);\n        Ok(n)\n    }\n\n    /// Return the contents of the internal buffer, filling it with more data from the inner reader if it is empty.\n    pub fn fill_buf(&mut self) -> impl Future<Output = Result<&'_ [u8], Error>> {\n        poll_fn(move |cx| {\n            compiler_fence(Ordering::SeqCst);\n            //trace!(\"poll_read\");\n\n            let s = U::buffered_state();\n            let ss = U::state();\n            let mut rx = unsafe { s.rx_buf.reader() };\n\n            let (ptr, n) = rx.pop_buf();\n            if n == 0 {\n                //trace!(\"  empty\");\n                ss.rx_waker.register(cx.waker());\n                Poll::Pending\n            } else {\n                Poll::Ready(Ok(unsafe { slice::from_raw_parts(ptr, n) }))\n            }\n        })\n    }\n\n    /// Tell this buffer that `amt` bytes have been consumed from the buffer, so they should no longer be returned in calls to `fill_buf`.\n    pub fn consume(&mut self, amt: usize) {\n        if amt == 0 {\n            return;\n        }\n\n        let s = U::buffered_state();\n        let mut rx = unsafe { s.rx_buf.reader() };\n        rx.pop_done(amt);\n    }\n\n    /// we are ready to read if there is data in the buffer\n    fn read_ready() -> Result<bool, Error> {\n        let state = U::buffered_state();\n        Ok(!state.rx_buf.is_empty())\n    }\n}\n\nimpl<'a, U: UarteInstance> Drop for BufferedUarteRx<'a, U> {\n    fn drop(&mut self) {\n        let r = U::regs();\n\n        r.intenclr().write(|w| {\n            w.set_rxto(true);\n        });\n        r.events_rxto().write_value(0);\n\n        let s = U::buffered_state();\n        unsafe { s.rx_buf.deinit() }\n\n        let s = U::state();\n        drop_tx_rx(r, s);\n    }\n}\n\nmod _embedded_io {\n    use super::*;\n\n    impl embedded_io_async::Error for Error {\n        fn kind(&self) -> embedded_io_async::ErrorKind {\n            match *self {}\n        }\n    }\n\n    impl<'d, U: UarteInstance> embedded_io_async::ErrorType for BufferedUarte<'d, U> {\n        type Error = Error;\n    }\n\n    impl<'d, U: UarteInstance> embedded_io_async::ErrorType for BufferedUarteRx<'d, U> {\n        type Error = Error;\n    }\n\n    impl<'d, U: UarteInstance> embedded_io_async::ErrorType for BufferedUarteTx<'d, U> {\n        type Error = Error;\n    }\n\n    impl<'d, U: UarteInstance> embedded_io_async::Read for BufferedUarte<'d, U> {\n        async fn read(&mut self, buf: &mut [u8]) -> Result<usize, Self::Error> {\n            self.read(buf).await\n        }\n    }\n\n    impl<'d: 'd, U: UarteInstance> embedded_io_async::Read for BufferedUarteRx<'d, U> {\n        async fn read(&mut self, buf: &mut [u8]) -> Result<usize, Self::Error> {\n            self.read(buf).await\n        }\n    }\n\n    impl<'d, U: UarteInstance> embedded_io_async::ReadReady for BufferedUarte<'d, U> {\n        fn read_ready(&mut self) -> Result<bool, Self::Error> {\n            BufferedUarteRx::<'d, U>::read_ready()\n        }\n    }\n\n    impl<'d, U: UarteInstance> embedded_io_async::ReadReady for BufferedUarteRx<'d, U> {\n        fn read_ready(&mut self) -> Result<bool, Self::Error> {\n            Self::read_ready()\n        }\n    }\n\n    impl<'d, U: UarteInstance> embedded_io_async::BufRead for BufferedUarte<'d, U> {\n        async fn fill_buf(&mut self) -> Result<&[u8], Self::Error> {\n            self.fill_buf().await\n        }\n\n        fn consume(&mut self, amt: usize) {\n            self.consume(amt)\n        }\n    }\n\n    impl<'d: 'd, U: UarteInstance> embedded_io_async::BufRead for BufferedUarteRx<'d, U> {\n        async fn fill_buf(&mut self) -> Result<&[u8], Self::Error> {\n            self.fill_buf().await\n        }\n\n        fn consume(&mut self, amt: usize) {\n            self.consume(amt)\n        }\n    }\n\n    impl<'d, U: UarteInstance> embedded_io_async::Write for BufferedUarte<'d, U> {\n        async fn write(&mut self, buf: &[u8]) -> Result<usize, Self::Error> {\n            self.write(buf).await\n        }\n\n        async fn flush(&mut self) -> Result<(), Self::Error> {\n            self.flush().await\n        }\n    }\n\n    impl<'d: 'd, U: UarteInstance> embedded_io_async::Write for BufferedUarteTx<'d, U> {\n        async fn write(&mut self, buf: &[u8]) -> Result<usize, Self::Error> {\n            self.write(buf).await\n        }\n\n        async fn flush(&mut self) -> Result<(), Self::Error> {\n            self.flush().await\n        }\n    }\n}\n"
  },
  {
    "path": "embassy-nrf/src/chips/nrf51.rs",
    "content": "pub use nrf_pac as pac;\n\n/// The maximum buffer size that the EasyDMA can send/recv in one operation.\npub const EASY_DMA_SIZE: usize = (1 << 14) - 1;\n\npub const FLASH_SIZE: usize = 128 * 1024;\n\nembassy_hal_internal::peripherals! {\n    // RTC\n    RTC0,\n    #[cfg(not(feature = \"time-driver-rtc1\"))]\n    RTC1,\n\n    // WDT\n    WDT,\n\n    // NVMC\n    NVMC,\n\n    // RNG\n    RNG,\n\n    // UARTE\n    UART0,\n\n    // SPI/TWI\n    TWI0,\n    SPI0,\n\n    // ADC\n    ADC,\n\n    // TIMER\n    TIMER0,\n    TIMER1,\n    TIMER2,\n\n    // GPIOTE\n    GPIOTE_CH0,\n    GPIOTE_CH1,\n    GPIOTE_CH2,\n    GPIOTE_CH3,\n\n    // PPI\n    PPI_CH0,\n    PPI_CH1,\n    PPI_CH2,\n    PPI_CH3,\n    PPI_CH4,\n    PPI_CH5,\n    PPI_CH6,\n    PPI_CH7,\n    PPI_CH8,\n    PPI_CH9,\n    PPI_CH10,\n    PPI_CH11,\n    PPI_CH12,\n    PPI_CH13,\n    PPI_CH14,\n    PPI_CH15,\n\n    PPI_GROUP0,\n    PPI_GROUP1,\n    PPI_GROUP2,\n    PPI_GROUP3,\n\n    // GPIO port 0\n    P0_00,\n    P0_01,\n    P0_02,\n    P0_03,\n    P0_04,\n    P0_05,\n    P0_06,\n    P0_07,\n    P0_08,\n    P0_09,\n    P0_10,\n    P0_11,\n    P0_12,\n    P0_13,\n    P0_14,\n    P0_15,\n    P0_16,\n    P0_17,\n    P0_18,\n    P0_19,\n    P0_20,\n    P0_21,\n    P0_22,\n    P0_23,\n    P0_24,\n    P0_25,\n    P0_26,\n    P0_27,\n    P0_28,\n    P0_29,\n    P0_30,\n    P0_31,\n\n    // TEMP\n    TEMP,\n\n    // Radio\n    RADIO,\n}\n\nimpl_timer!(TIMER0, TIMER0, TIMER0);\nimpl_timer!(TIMER1, TIMER1, TIMER1);\nimpl_timer!(TIMER2, TIMER2, TIMER2);\n\nimpl_rng!(RNG, RNG, RNG);\n\nimpl_rtc!(RTC0, RTC0, RTC0);\n#[cfg(not(feature = \"time-driver-rtc1\"))]\nimpl_rtc!(RTC1, RTC1, RTC1);\n\nimpl_ppi_group!(PPI_GROUP0, PPI, 0);\nimpl_ppi_group!(PPI_GROUP1, PPI, 1);\nimpl_ppi_group!(PPI_GROUP2, PPI, 2);\nimpl_ppi_group!(PPI_GROUP3, PPI, 3);\n\nimpl_pin!(P0_00, 0, 0);\nimpl_pin!(P0_01, 0, 1);\nimpl_pin!(P0_02, 0, 2);\nimpl_pin!(P0_03, 0, 3);\nimpl_pin!(P0_04, 0, 4);\nimpl_pin!(P0_05, 0, 5);\nimpl_pin!(P0_06, 0, 6);\nimpl_pin!(P0_07, 0, 7);\nimpl_pin!(P0_08, 0, 8);\nimpl_pin!(P0_09, 0, 9);\nimpl_pin!(P0_10, 0, 10);\nimpl_pin!(P0_11, 0, 11);\nimpl_pin!(P0_12, 0, 12);\nimpl_pin!(P0_13, 0, 13);\nimpl_pin!(P0_14, 0, 14);\nimpl_pin!(P0_15, 0, 15);\nimpl_pin!(P0_16, 0, 16);\nimpl_pin!(P0_17, 0, 17);\nimpl_pin!(P0_18, 0, 18);\nimpl_pin!(P0_19, 0, 19);\nimpl_pin!(P0_20, 0, 20);\nimpl_pin!(P0_21, 0, 21);\nimpl_pin!(P0_22, 0, 22);\nimpl_pin!(P0_23, 0, 23);\nimpl_pin!(P0_24, 0, 24);\nimpl_pin!(P0_25, 0, 25);\nimpl_pin!(P0_26, 0, 26);\nimpl_pin!(P0_27, 0, 27);\nimpl_pin!(P0_28, 0, 28);\nimpl_pin!(P0_29, 0, 29);\nimpl_pin!(P0_30, 0, 30);\nimpl_pin!(P0_31, 0, 31);\n\nimpl_radio!(RADIO, RADIO, RADIO);\n\nimpl_wdt!(WDT, WDT, WDT, 0);\n\nembassy_hal_internal::interrupt_mod!(\n    CLOCK_POWER,\n    RADIO,\n    UART0,\n    TWISPI0,\n    TWISPI1,\n    GPIOTE,\n    ADC,\n    TIMER0,\n    TIMER1,\n    TIMER2,\n    RTC0,\n    TEMP,\n    RNG,\n    ECB,\n    AAR_CCM,\n    WDT,\n    RTC1,\n    QDEC,\n    LPCOMP,\n    SWI0,\n    SWI1,\n    SWI2,\n    SWI3,\n    SWI4,\n    SWI5,\n);\n"
  },
  {
    "path": "embassy-nrf/src/chips/nrf52805.rs",
    "content": "pub use nrf_pac as pac;\n\n/// The maximum buffer size that the EasyDMA can send/recv in one operation.\npub const EASY_DMA_SIZE: usize = (1 << 14) - 1;\npub const FORCE_COPY_BUFFER_SIZE: usize = 256;\n\npub const FLASH_SIZE: usize = 192 * 1024;\n\npub const RESET_PIN: u32 = 21;\npub const APPROTECT_MIN_BUILD_CODE: u8 = b'B';\n\nembassy_hal_internal::peripherals! {\n    // RTC\n    RTC0,\n    #[cfg(not(feature=\"time-driver-rtc1\"))]\n    RTC1,\n\n    // WDT\n    WDT,\n\n    // NVMC\n    NVMC,\n\n    // RNG\n    RNG,\n\n    // UARTE\n    UARTE0,\n\n    // SPI/TWI\n    TWI0,\n    SPI0,\n\n    // SAADC\n    SAADC,\n\n    // TIMER\n    TIMER0,\n    TIMER1,\n    TIMER2,\n\n    // GPIOTE\n    GPIOTE_CH0,\n    GPIOTE_CH1,\n    GPIOTE_CH2,\n    GPIOTE_CH3,\n    GPIOTE_CH4,\n    GPIOTE_CH5,\n    GPIOTE_CH6,\n    GPIOTE_CH7,\n\n    // PPI\n    PPI_CH0,\n    PPI_CH1,\n    PPI_CH2,\n    PPI_CH3,\n    PPI_CH4,\n    PPI_CH5,\n    PPI_CH6,\n    PPI_CH7,\n    PPI_CH8,\n    PPI_CH9,\n    PPI_CH10,\n    PPI_CH11,\n    PPI_CH12,\n    PPI_CH13,\n    PPI_CH14,\n    PPI_CH15,\n    PPI_CH16,\n    PPI_CH17,\n    PPI_CH18,\n    PPI_CH19,\n    PPI_CH20,\n    PPI_CH21,\n    PPI_CH22,\n    PPI_CH23,\n    PPI_CH24,\n    PPI_CH25,\n    PPI_CH26,\n    PPI_CH27,\n    PPI_CH28,\n    PPI_CH29,\n    PPI_CH30,\n    PPI_CH31,\n\n    PPI_GROUP0,\n    PPI_GROUP1,\n    PPI_GROUP2,\n    PPI_GROUP3,\n    PPI_GROUP4,\n    PPI_GROUP5,\n\n    // GPIO port 0\n    P0_00,\n    P0_01,\n    P0_02,\n    P0_03,\n    P0_04,\n    P0_05,\n    P0_06,\n    P0_07,\n    P0_08,\n    P0_09,\n    P0_10,\n    P0_11,\n    P0_12,\n    P0_13,\n    P0_14,\n    P0_15,\n    P0_16,\n    P0_17,\n    P0_18,\n    P0_19,\n    P0_20,\n    #[cfg(feature=\"reset-pin-as-gpio\")]\n    P0_21,\n    P0_22,\n    P0_23,\n    P0_24,\n    P0_25,\n    P0_26,\n    P0_27,\n    P0_28,\n    P0_29,\n    P0_30,\n    P0_31,\n\n    // TEMP\n    TEMP,\n\n    // QDEC\n    QDEC,\n\n    // Radio\n    RADIO,\n\n    // EGU\n    EGU0,\n    EGU1,\n}\n\nimpl_uarte!(UARTE0, UARTE0, UARTE0);\n\nimpl_spim!(SPI0, SPIM0, SPI0);\n\nimpl_spis!(SPI0, SPIS0, SPI0);\n\nimpl_twim!(TWI0, TWIM0, TWI0);\n\nimpl_twis!(TWI0, TWIS0, TWI0);\n\nimpl_qdec!(QDEC, QDEC, QDEC);\n\nimpl_rng!(RNG, RNG, RNG);\n\nimpl_timer!(TIMER0, TIMER0, TIMER0);\nimpl_timer!(TIMER1, TIMER1, TIMER1);\nimpl_timer!(TIMER2, TIMER2, TIMER2);\n\nimpl_rtc!(RTC0, RTC0, RTC0);\n#[cfg(not(feature = \"time-driver-rtc1\"))]\nimpl_rtc!(RTC1, RTC1, RTC1);\n\nimpl_pin!(P0_00, 0, 0);\nimpl_pin!(P0_01, 0, 1);\nimpl_pin!(P0_02, 0, 2);\nimpl_pin!(P0_03, 0, 3);\nimpl_pin!(P0_04, 0, 4);\nimpl_pin!(P0_05, 0, 5);\nimpl_pin!(P0_06, 0, 6);\nimpl_pin!(P0_07, 0, 7);\nimpl_pin!(P0_08, 0, 8);\nimpl_pin!(P0_09, 0, 9);\nimpl_pin!(P0_10, 0, 10);\nimpl_pin!(P0_11, 0, 11);\nimpl_pin!(P0_12, 0, 12);\nimpl_pin!(P0_13, 0, 13);\nimpl_pin!(P0_14, 0, 14);\nimpl_pin!(P0_15, 0, 15);\nimpl_pin!(P0_16, 0, 16);\nimpl_pin!(P0_17, 0, 17);\nimpl_pin!(P0_18, 0, 18);\nimpl_pin!(P0_19, 0, 19);\nimpl_pin!(P0_20, 0, 20);\n#[cfg(feature = \"reset-pin-as-gpio\")]\nimpl_pin!(P0_21, 0, 21);\nimpl_pin!(P0_22, 0, 22);\nimpl_pin!(P0_23, 0, 23);\nimpl_pin!(P0_24, 0, 24);\nimpl_pin!(P0_25, 0, 25);\nimpl_pin!(P0_26, 0, 26);\nimpl_pin!(P0_27, 0, 27);\nimpl_pin!(P0_28, 0, 28);\nimpl_pin!(P0_29, 0, 29);\nimpl_pin!(P0_30, 0, 30);\nimpl_pin!(P0_31, 0, 31);\n\nimpl_ppi_channel!(PPI_CH0, PPI, 0 => configurable);\nimpl_ppi_channel!(PPI_CH1, PPI, 1 => configurable);\nimpl_ppi_channel!(PPI_CH2, PPI, 2 => configurable);\nimpl_ppi_channel!(PPI_CH3, PPI, 3 => configurable);\nimpl_ppi_channel!(PPI_CH4, PPI, 4 => configurable);\nimpl_ppi_channel!(PPI_CH5, PPI, 5 => configurable);\nimpl_ppi_channel!(PPI_CH6, PPI, 6 => configurable);\nimpl_ppi_channel!(PPI_CH7, PPI, 7 => configurable);\nimpl_ppi_channel!(PPI_CH8, PPI, 8 => configurable);\nimpl_ppi_channel!(PPI_CH9, PPI, 9 => configurable);\nimpl_ppi_channel!(PPI_CH20, PPI, 20 => static);\nimpl_ppi_channel!(PPI_CH21, PPI, 21 => static);\nimpl_ppi_channel!(PPI_CH22, PPI, 22 => static);\nimpl_ppi_channel!(PPI_CH23, PPI, 23 => static);\nimpl_ppi_channel!(PPI_CH24, PPI, 24 => static);\nimpl_ppi_channel!(PPI_CH25, PPI, 25 => static);\nimpl_ppi_channel!(PPI_CH26, PPI, 26 => static);\nimpl_ppi_channel!(PPI_CH27, PPI, 27 => static);\nimpl_ppi_channel!(PPI_CH28, PPI, 28 => static);\nimpl_ppi_channel!(PPI_CH29, PPI, 29 => static);\nimpl_ppi_channel!(PPI_CH30, PPI, 30 => static);\nimpl_ppi_channel!(PPI_CH31, PPI, 31 => static);\n\nimpl_ppi_group!(PPI_GROUP0, PPI, 0);\nimpl_ppi_group!(PPI_GROUP1, PPI, 1);\nimpl_ppi_group!(PPI_GROUP2, PPI, 2);\nimpl_ppi_group!(PPI_GROUP3, PPI, 3);\nimpl_ppi_group!(PPI_GROUP4, PPI, 4);\nimpl_ppi_group!(PPI_GROUP5, PPI, 5);\n\nimpl_saadc_input!(P0_04, ANALOG_INPUT2);\nimpl_saadc_input!(P0_05, ANALOG_INPUT3);\n\nimpl_radio!(RADIO, RADIO, RADIO);\n\nimpl_egu!(EGU0, EGU0, EGU0_SWI0);\nimpl_egu!(EGU1, EGU1, EGU1_SWI1);\n\nimpl_wdt!(WDT, WDT, WDT, 0);\n\nembassy_hal_internal::interrupt_mod!(\n    CLOCK_POWER,\n    RADIO,\n    UARTE0,\n    TWI0,\n    SPI0,\n    GPIOTE,\n    SAADC,\n    TIMER0,\n    TIMER1,\n    TIMER2,\n    TEMP,\n    RNG,\n    ECB,\n    AAR_CCM,\n    WDT,\n    RTC0,\n    RTC1,\n    QDEC,\n    EGU0_SWI0,\n    EGU1_SWI1,\n    SWI2,\n    SWI3,\n    SWI4,\n    SWI5,\n);\n"
  },
  {
    "path": "embassy-nrf/src/chips/nrf52810.rs",
    "content": "pub use nrf_pac as pac;\n\n/// The maximum buffer size that the EasyDMA can send/recv in one operation.\npub const EASY_DMA_SIZE: usize = (1 << 10) - 1;\npub const FORCE_COPY_BUFFER_SIZE: usize = 256;\n\npub const FLASH_SIZE: usize = 192 * 1024;\n\npub const RESET_PIN: u32 = 21;\npub const APPROTECT_MIN_BUILD_CODE: u8 = b'E';\n\nembassy_hal_internal::peripherals! {\n    // RTC\n    RTC0,\n    #[cfg(not(feature=\"time-driver-rtc1\"))]\n    RTC1,\n\n    // WDT\n    WDT,\n\n    // NVMC\n    NVMC,\n\n    // RNG\n    RNG,\n\n    // UARTE\n    UARTE0,\n\n    // SPI/TWI\n    TWI0,\n    SPI0,\n\n    // SAADC\n    SAADC,\n\n    // PWM\n    PWM0,\n\n    // TIMER\n    TIMER0,\n    TIMER1,\n    TIMER2,\n\n    // GPIOTE\n    GPIOTE_CH0,\n    GPIOTE_CH1,\n    GPIOTE_CH2,\n    GPIOTE_CH3,\n    GPIOTE_CH4,\n    GPIOTE_CH5,\n    GPIOTE_CH6,\n    GPIOTE_CH7,\n\n    // PPI\n    PPI_CH0,\n    PPI_CH1,\n    PPI_CH2,\n    PPI_CH3,\n    PPI_CH4,\n    PPI_CH5,\n    PPI_CH6,\n    PPI_CH7,\n    PPI_CH8,\n    PPI_CH9,\n    PPI_CH10,\n    PPI_CH11,\n    PPI_CH12,\n    PPI_CH13,\n    PPI_CH14,\n    PPI_CH15,\n    PPI_CH16,\n    PPI_CH17,\n    PPI_CH18,\n    PPI_CH19,\n    PPI_CH20,\n    PPI_CH21,\n    PPI_CH22,\n    PPI_CH23,\n    PPI_CH24,\n    PPI_CH25,\n    PPI_CH26,\n    PPI_CH27,\n    PPI_CH28,\n    PPI_CH29,\n    PPI_CH30,\n    PPI_CH31,\n\n    PPI_GROUP0,\n    PPI_GROUP1,\n    PPI_GROUP2,\n    PPI_GROUP3,\n    PPI_GROUP4,\n    PPI_GROUP5,\n\n    // GPIO port 0\n    P0_00,\n    P0_01,\n    P0_02,\n    P0_03,\n    P0_04,\n    P0_05,\n    P0_06,\n    P0_07,\n    P0_08,\n    P0_09,\n    P0_10,\n    P0_11,\n    P0_12,\n    P0_13,\n    P0_14,\n    P0_15,\n    P0_16,\n    P0_17,\n    P0_18,\n    P0_19,\n    P0_20,\n    #[cfg(feature=\"reset-pin-as-gpio\")]\n    P0_21,\n    P0_22,\n    P0_23,\n    P0_24,\n    P0_25,\n    P0_26,\n    P0_27,\n    P0_28,\n    P0_29,\n    P0_30,\n    P0_31,\n\n    // TEMP\n    TEMP,\n\n    // QDEC\n    QDEC,\n\n    // PDM\n    PDM,\n\n    // Radio\n    RADIO,\n\n    // EGU\n    EGU0,\n    EGU1,\n}\n\nimpl_uarte!(UARTE0, UARTE0, UARTE0);\n\nimpl_spim!(SPI0, SPIM0, SPI0);\n\nimpl_spis!(SPI0, SPIS0, SPI0);\n\nimpl_twim!(TWI0, TWIM0, TWI0);\n\nimpl_twis!(TWI0, TWIS0, TWI0);\n\nimpl_pwm!(PWM0, PWM0, PWM0);\n\nimpl_pdm!(PDM, PDM, PDM);\n\nimpl_qdec!(QDEC, QDEC, QDEC);\n\nimpl_rng!(RNG, RNG, RNG);\n\nimpl_timer!(TIMER0, TIMER0, TIMER0);\nimpl_timer!(TIMER1, TIMER1, TIMER1);\nimpl_timer!(TIMER2, TIMER2, TIMER2);\n\nimpl_rtc!(RTC0, RTC0, RTC0);\n#[cfg(not(feature = \"time-driver-rtc1\"))]\nimpl_rtc!(RTC1, RTC1, RTC1);\n\nimpl_pin!(P0_00, 0, 0);\nimpl_pin!(P0_01, 0, 1);\nimpl_pin!(P0_02, 0, 2);\nimpl_pin!(P0_03, 0, 3);\nimpl_pin!(P0_04, 0, 4);\nimpl_pin!(P0_05, 0, 5);\nimpl_pin!(P0_06, 0, 6);\nimpl_pin!(P0_07, 0, 7);\nimpl_pin!(P0_08, 0, 8);\nimpl_pin!(P0_09, 0, 9);\nimpl_pin!(P0_10, 0, 10);\nimpl_pin!(P0_11, 0, 11);\nimpl_pin!(P0_12, 0, 12);\nimpl_pin!(P0_13, 0, 13);\nimpl_pin!(P0_14, 0, 14);\nimpl_pin!(P0_15, 0, 15);\nimpl_pin!(P0_16, 0, 16);\nimpl_pin!(P0_17, 0, 17);\nimpl_pin!(P0_18, 0, 18);\nimpl_pin!(P0_19, 0, 19);\nimpl_pin!(P0_20, 0, 20);\n#[cfg(feature = \"reset-pin-as-gpio\")]\nimpl_pin!(P0_21, 0, 21);\nimpl_pin!(P0_22, 0, 22);\nimpl_pin!(P0_23, 0, 23);\nimpl_pin!(P0_24, 0, 24);\nimpl_pin!(P0_25, 0, 25);\nimpl_pin!(P0_26, 0, 26);\nimpl_pin!(P0_27, 0, 27);\nimpl_pin!(P0_28, 0, 28);\nimpl_pin!(P0_29, 0, 29);\nimpl_pin!(P0_30, 0, 30);\nimpl_pin!(P0_31, 0, 31);\n\nimpl_ppi_channel!(PPI_CH0, PPI, 0 => configurable);\nimpl_ppi_channel!(PPI_CH1, PPI, 1 => configurable);\nimpl_ppi_channel!(PPI_CH2, PPI, 2 => configurable);\nimpl_ppi_channel!(PPI_CH3, PPI, 3 => configurable);\nimpl_ppi_channel!(PPI_CH4, PPI, 4 => configurable);\nimpl_ppi_channel!(PPI_CH5, PPI, 5 => configurable);\nimpl_ppi_channel!(PPI_CH6, PPI, 6 => configurable);\nimpl_ppi_channel!(PPI_CH7, PPI, 7 => configurable);\nimpl_ppi_channel!(PPI_CH8, PPI, 8 => configurable);\nimpl_ppi_channel!(PPI_CH9, PPI, 9 => configurable);\nimpl_ppi_channel!(PPI_CH10, PPI, 10 => configurable);\nimpl_ppi_channel!(PPI_CH11, PPI, 11 => configurable);\nimpl_ppi_channel!(PPI_CH12, PPI, 12 => configurable);\nimpl_ppi_channel!(PPI_CH13, PPI, 13 => configurable);\nimpl_ppi_channel!(PPI_CH14, PPI, 14 => configurable);\nimpl_ppi_channel!(PPI_CH15, PPI, 15 => configurable);\nimpl_ppi_channel!(PPI_CH16, PPI, 16 => configurable);\nimpl_ppi_channel!(PPI_CH17, PPI, 17 => configurable);\nimpl_ppi_channel!(PPI_CH18, PPI, 18 => configurable);\nimpl_ppi_channel!(PPI_CH19, PPI, 19 => configurable);\nimpl_ppi_channel!(PPI_CH20, PPI, 20 => static);\nimpl_ppi_channel!(PPI_CH21, PPI, 21 => static);\nimpl_ppi_channel!(PPI_CH22, PPI, 22 => static);\nimpl_ppi_channel!(PPI_CH23, PPI, 23 => static);\nimpl_ppi_channel!(PPI_CH24, PPI, 24 => static);\nimpl_ppi_channel!(PPI_CH25, PPI, 25 => static);\nimpl_ppi_channel!(PPI_CH26, PPI, 26 => static);\nimpl_ppi_channel!(PPI_CH27, PPI, 27 => static);\nimpl_ppi_channel!(PPI_CH28, PPI, 28 => static);\nimpl_ppi_channel!(PPI_CH29, PPI, 29 => static);\nimpl_ppi_channel!(PPI_CH30, PPI, 30 => static);\nimpl_ppi_channel!(PPI_CH31, PPI, 31 => static);\n\nimpl_ppi_group!(PPI_GROUP0, PPI, 0);\nimpl_ppi_group!(PPI_GROUP1, PPI, 1);\nimpl_ppi_group!(PPI_GROUP2, PPI, 2);\nimpl_ppi_group!(PPI_GROUP3, PPI, 3);\nimpl_ppi_group!(PPI_GROUP4, PPI, 4);\nimpl_ppi_group!(PPI_GROUP5, PPI, 5);\n\nimpl_saadc_input!(P0_02, ANALOG_INPUT0);\nimpl_saadc_input!(P0_03, ANALOG_INPUT1);\nimpl_saadc_input!(P0_04, ANALOG_INPUT2);\nimpl_saadc_input!(P0_05, ANALOG_INPUT3);\nimpl_saadc_input!(P0_28, ANALOG_INPUT4);\nimpl_saadc_input!(P0_29, ANALOG_INPUT5);\nimpl_saadc_input!(P0_30, ANALOG_INPUT6);\nimpl_saadc_input!(P0_31, ANALOG_INPUT7);\n\nimpl_radio!(RADIO, RADIO, RADIO);\n\nimpl_egu!(EGU0, EGU0, EGU0_SWI0);\nimpl_egu!(EGU1, EGU1, EGU1_SWI1);\n\nimpl_wdt!(WDT, WDT, WDT, 0);\n\nembassy_hal_internal::interrupt_mod!(\n    CLOCK_POWER,\n    RADIO,\n    UARTE0,\n    TWI0,\n    SPI0,\n    GPIOTE,\n    SAADC,\n    TIMER0,\n    TIMER1,\n    TIMER2,\n    RTC0,\n    TEMP,\n    RNG,\n    ECB,\n    AAR_CCM,\n    WDT,\n    RTC1,\n    QDEC,\n    COMP,\n    EGU0_SWI0,\n    EGU1_SWI1,\n    SWI2,\n    SWI3,\n    SWI4,\n    SWI5,\n    PWM0,\n    PDM,\n);\n"
  },
  {
    "path": "embassy-nrf/src/chips/nrf52811.rs",
    "content": "pub use nrf_pac as pac;\n\n/// The maximum buffer size that the EasyDMA can send/recv in one operation.\npub const EASY_DMA_SIZE: usize = (1 << 14) - 1;\npub const FORCE_COPY_BUFFER_SIZE: usize = 256;\n\npub const FLASH_SIZE: usize = 192 * 1024;\n\npub const RESET_PIN: u32 = 21;\npub const APPROTECT_MIN_BUILD_CODE: u8 = b'B';\n\nembassy_hal_internal::peripherals! {\n    // RTC\n    RTC0,\n    #[cfg(not(feature=\"time-driver-rtc1\"))]\n    RTC1,\n\n    // WDT\n    WDT,\n\n    // NVMC\n    NVMC,\n\n    // RNG\n    RNG,\n\n    // UARTE\n    UARTE0,\n\n    // SPI/TWI\n    TWI0_SPI1,\n    SPI0,\n\n    // SAADC\n    SAADC,\n\n    // PWM\n    PWM0,\n\n    // TIMER\n    TIMER0,\n    TIMER1,\n    TIMER2,\n\n    // GPIOTE\n    GPIOTE_CH0,\n    GPIOTE_CH1,\n    GPIOTE_CH2,\n    GPIOTE_CH3,\n    GPIOTE_CH4,\n    GPIOTE_CH5,\n    GPIOTE_CH6,\n    GPIOTE_CH7,\n\n    // PPI\n    PPI_CH0,\n    PPI_CH1,\n    PPI_CH2,\n    PPI_CH3,\n    PPI_CH4,\n    PPI_CH5,\n    PPI_CH6,\n    PPI_CH7,\n    PPI_CH8,\n    PPI_CH9,\n    PPI_CH10,\n    PPI_CH11,\n    PPI_CH12,\n    PPI_CH13,\n    PPI_CH14,\n    PPI_CH15,\n    PPI_CH16,\n    PPI_CH17,\n    PPI_CH18,\n    PPI_CH19,\n    PPI_CH20,\n    PPI_CH21,\n    PPI_CH22,\n    PPI_CH23,\n    PPI_CH24,\n    PPI_CH25,\n    PPI_CH26,\n    PPI_CH27,\n    PPI_CH28,\n    PPI_CH29,\n    PPI_CH30,\n    PPI_CH31,\n\n    PPI_GROUP0,\n    PPI_GROUP1,\n    PPI_GROUP2,\n    PPI_GROUP3,\n    PPI_GROUP4,\n    PPI_GROUP5,\n\n    // GPIO port 0\n    P0_00,\n    P0_01,\n    P0_02,\n    P0_03,\n    P0_04,\n    P0_05,\n    P0_06,\n    P0_07,\n    P0_08,\n    P0_09,\n    P0_10,\n    P0_11,\n    P0_12,\n    P0_13,\n    P0_14,\n    P0_15,\n    P0_16,\n    P0_17,\n    P0_18,\n    P0_19,\n    P0_20,\n    #[cfg(feature=\"reset-pin-as-gpio\")]\n    P0_21,\n    P0_22,\n    P0_23,\n    P0_24,\n    P0_25,\n    P0_26,\n    P0_27,\n    P0_28,\n    P0_29,\n    P0_30,\n    P0_31,\n\n    // TEMP\n    TEMP,\n\n    // QDEC\n    QDEC,\n\n    // PDM\n    PDM,\n\n    // Radio\n    RADIO,\n\n    // EGU\n    EGU0,\n    EGU1,\n}\n\nimpl_uarte!(UARTE0, UARTE0, UARTE0);\n\nimpl_spim!(SPI0, SPIM0, SPI0);\nimpl_spim!(TWI0_SPI1, SPIM1, TWI0_SPI1);\n\nimpl_spis!(SPI0, SPIS0, SPI0);\nimpl_spis!(TWI0_SPI1, SPIS1, TWI0_SPI1);\n\nimpl_twim!(TWI0_SPI1, TWIM0, TWI0_SPI1);\n\nimpl_twis!(TWI0_SPI1, TWIS0, TWI0_SPI1);\n\nimpl_pwm!(PWM0, PWM0, PWM0);\n\nimpl_pdm!(PDM, PDM, PDM);\n\nimpl_qdec!(QDEC, QDEC, QDEC);\n\nimpl_rng!(RNG, RNG, RNG);\n\nimpl_timer!(TIMER0, TIMER0, TIMER0);\nimpl_timer!(TIMER1, TIMER1, TIMER1);\nimpl_timer!(TIMER2, TIMER2, TIMER2);\n\nimpl_rtc!(RTC0, RTC0, RTC0);\n#[cfg(not(feature = \"time-driver-rtc1\"))]\nimpl_rtc!(RTC1, RTC1, RTC1);\n\nimpl_pin!(P0_00, 0, 0);\nimpl_pin!(P0_01, 0, 1);\nimpl_pin!(P0_02, 0, 2);\nimpl_pin!(P0_03, 0, 3);\nimpl_pin!(P0_04, 0, 4);\nimpl_pin!(P0_05, 0, 5);\nimpl_pin!(P0_06, 0, 6);\nimpl_pin!(P0_07, 0, 7);\nimpl_pin!(P0_08, 0, 8);\nimpl_pin!(P0_09, 0, 9);\nimpl_pin!(P0_10, 0, 10);\nimpl_pin!(P0_11, 0, 11);\nimpl_pin!(P0_12, 0, 12);\nimpl_pin!(P0_13, 0, 13);\nimpl_pin!(P0_14, 0, 14);\nimpl_pin!(P0_15, 0, 15);\nimpl_pin!(P0_16, 0, 16);\nimpl_pin!(P0_17, 0, 17);\nimpl_pin!(P0_18, 0, 18);\nimpl_pin!(P0_19, 0, 19);\nimpl_pin!(P0_20, 0, 20);\n#[cfg(feature = \"reset-pin-as-gpio\")]\nimpl_pin!(P0_21, 0, 21);\nimpl_pin!(P0_22, 0, 22);\nimpl_pin!(P0_23, 0, 23);\nimpl_pin!(P0_24, 0, 24);\nimpl_pin!(P0_25, 0, 25);\nimpl_pin!(P0_26, 0, 26);\nimpl_pin!(P0_27, 0, 27);\nimpl_pin!(P0_28, 0, 28);\nimpl_pin!(P0_29, 0, 29);\nimpl_pin!(P0_30, 0, 30);\nimpl_pin!(P0_31, 0, 31);\n\nimpl_ppi_channel!(PPI_CH0, PPI, 0 => configurable);\nimpl_ppi_channel!(PPI_CH1, PPI, 1 => configurable);\nimpl_ppi_channel!(PPI_CH2, PPI, 2 => configurable);\nimpl_ppi_channel!(PPI_CH3, PPI, 3 => configurable);\nimpl_ppi_channel!(PPI_CH4, PPI, 4 => configurable);\nimpl_ppi_channel!(PPI_CH5, PPI, 5 => configurable);\nimpl_ppi_channel!(PPI_CH6, PPI, 6 => configurable);\nimpl_ppi_channel!(PPI_CH7, PPI, 7 => configurable);\nimpl_ppi_channel!(PPI_CH8, PPI, 8 => configurable);\nimpl_ppi_channel!(PPI_CH9, PPI, 9 => configurable);\nimpl_ppi_channel!(PPI_CH10, PPI, 10 => configurable);\nimpl_ppi_channel!(PPI_CH11, PPI, 11 => configurable);\nimpl_ppi_channel!(PPI_CH12, PPI, 12 => configurable);\nimpl_ppi_channel!(PPI_CH13, PPI, 13 => configurable);\nimpl_ppi_channel!(PPI_CH14, PPI, 14 => configurable);\nimpl_ppi_channel!(PPI_CH15, PPI, 15 => configurable);\nimpl_ppi_channel!(PPI_CH16, PPI, 16 => configurable);\nimpl_ppi_channel!(PPI_CH17, PPI, 17 => configurable);\nimpl_ppi_channel!(PPI_CH18, PPI, 18 => configurable);\nimpl_ppi_channel!(PPI_CH19, PPI, 19 => configurable);\nimpl_ppi_channel!(PPI_CH20, PPI, 20 => static);\nimpl_ppi_channel!(PPI_CH21, PPI, 21 => static);\nimpl_ppi_channel!(PPI_CH22, PPI, 22 => static);\nimpl_ppi_channel!(PPI_CH23, PPI, 23 => static);\nimpl_ppi_channel!(PPI_CH24, PPI, 24 => static);\nimpl_ppi_channel!(PPI_CH25, PPI, 25 => static);\nimpl_ppi_channel!(PPI_CH26, PPI, 26 => static);\nimpl_ppi_channel!(PPI_CH27, PPI, 27 => static);\nimpl_ppi_channel!(PPI_CH28, PPI, 28 => static);\nimpl_ppi_channel!(PPI_CH29, PPI, 29 => static);\nimpl_ppi_channel!(PPI_CH30, PPI, 30 => static);\nimpl_ppi_channel!(PPI_CH31, PPI, 31 => static);\n\nimpl_ppi_group!(PPI_GROUP0, PPI, 0);\nimpl_ppi_group!(PPI_GROUP1, PPI, 1);\nimpl_ppi_group!(PPI_GROUP2, PPI, 2);\nimpl_ppi_group!(PPI_GROUP3, PPI, 3);\nimpl_ppi_group!(PPI_GROUP4, PPI, 4);\nimpl_ppi_group!(PPI_GROUP5, PPI, 5);\n\nimpl_saadc_input!(P0_02, ANALOG_INPUT0);\nimpl_saadc_input!(P0_03, ANALOG_INPUT1);\nimpl_saadc_input!(P0_04, ANALOG_INPUT2);\nimpl_saadc_input!(P0_05, ANALOG_INPUT3);\nimpl_saadc_input!(P0_28, ANALOG_INPUT4);\nimpl_saadc_input!(P0_29, ANALOG_INPUT5);\nimpl_saadc_input!(P0_30, ANALOG_INPUT6);\nimpl_saadc_input!(P0_31, ANALOG_INPUT7);\n\nimpl_radio!(RADIO, RADIO, RADIO);\n\nimpl_egu!(EGU0, EGU0, EGU0_SWI0);\nimpl_egu!(EGU1, EGU1, EGU1_SWI1);\n\nimpl_wdt!(WDT, WDT, WDT, 0);\n\nembassy_hal_internal::interrupt_mod!(\n    CLOCK_POWER,\n    RADIO,\n    UARTE0,\n    TWI0_SPI1,\n    SPI0,\n    GPIOTE,\n    SAADC,\n    TIMER0,\n    TIMER1,\n    TIMER2,\n    RTC0,\n    TEMP,\n    RNG,\n    ECB,\n    AAR_CCM,\n    WDT,\n    RTC1,\n    QDEC,\n    COMP,\n    EGU0_SWI0,\n    EGU1_SWI1,\n    SWI2,\n    SWI3,\n    SWI4,\n    SWI5,\n    PWM0,\n    PDM,\n);\n"
  },
  {
    "path": "embassy-nrf/src/chips/nrf52820.rs",
    "content": "pub use nrf_pac as pac;\n\n/// The maximum buffer size that the EasyDMA can send/recv in one operation.\npub const EASY_DMA_SIZE: usize = (1 << 15) - 1;\npub const FORCE_COPY_BUFFER_SIZE: usize = 512;\n\npub const FLASH_SIZE: usize = 256 * 1024;\n\npub const RESET_PIN: u32 = 18;\npub const APPROTECT_MIN_BUILD_CODE: u8 = b'D';\n\nembassy_hal_internal::peripherals! {\n    // USB\n    USBD,\n\n    // RTC\n    RTC0,\n    #[cfg(not(feature=\"time-driver-rtc1\"))]\n    RTC1,\n\n    // WDT\n    WDT,\n\n    // NVMC\n    NVMC,\n\n    // RNG\n    RNG,\n\n    // UARTE\n    UARTE0,\n\n    // SPI/TWI\n    TWISPI0,\n    TWISPI1,\n\n    // TIMER\n    TIMER0,\n    TIMER1,\n    TIMER2,\n    TIMER3,\n\n    // GPIOTE\n    GPIOTE_CH0,\n    GPIOTE_CH1,\n    GPIOTE_CH2,\n    GPIOTE_CH3,\n    GPIOTE_CH4,\n    GPIOTE_CH5,\n    GPIOTE_CH6,\n    GPIOTE_CH7,\n\n    // PPI\n    PPI_CH0,\n    PPI_CH1,\n    PPI_CH2,\n    PPI_CH3,\n    PPI_CH4,\n    PPI_CH5,\n    PPI_CH6,\n    PPI_CH7,\n    PPI_CH8,\n    PPI_CH9,\n    PPI_CH10,\n    PPI_CH11,\n    PPI_CH12,\n    PPI_CH13,\n    PPI_CH14,\n    PPI_CH15,\n    PPI_CH16,\n    PPI_CH17,\n    PPI_CH18,\n    PPI_CH19,\n    PPI_CH20,\n    PPI_CH21,\n    PPI_CH22,\n    PPI_CH23,\n    PPI_CH24,\n    PPI_CH25,\n    PPI_CH26,\n    PPI_CH27,\n    PPI_CH28,\n    PPI_CH29,\n    PPI_CH30,\n    PPI_CH31,\n\n    PPI_GROUP0,\n    PPI_GROUP1,\n    PPI_GROUP2,\n    PPI_GROUP3,\n    PPI_GROUP4,\n    PPI_GROUP5,\n\n    // GPIO port 0\n    P0_00,\n    P0_01,\n    P0_02,\n    P0_03,\n    P0_04,\n    P0_05,\n    P0_06,\n    P0_07,\n    P0_08,\n    P0_09,\n    P0_10,\n    P0_11,\n    P0_12,\n    P0_13,\n    P0_14,\n    P0_15,\n    P0_16,\n    P0_17,\n    #[cfg(feature=\"reset-pin-as-gpio\")]\n    P0_18,\n    P0_19,\n    P0_20,\n    P0_21,\n    P0_22,\n    P0_23,\n    P0_24,\n    P0_25,\n    P0_26,\n    P0_27,\n    P0_28,\n    P0_29,\n    P0_30,\n    P0_31,\n\n    // TEMP\n    TEMP,\n\n    // QDEC\n    QDEC,\n\n    // Radio\n    RADIO,\n\n    // EGU\n    EGU0,\n    EGU1,\n    EGU2,\n    EGU3,\n    EGU4,\n    EGU5,\n}\n\nimpl_usb!(USBD, USBD, USBD);\n\nimpl_uarte!(UARTE0, UARTE0, UARTE0);\n\nimpl_spim!(TWISPI0, SPIM0, TWISPI0);\nimpl_spim!(TWISPI1, SPIM1, TWISPI1);\n\nimpl_spis!(TWISPI0, SPIS0, TWISPI0);\nimpl_spis!(TWISPI1, SPIS1, TWISPI1);\n\nimpl_twim!(TWISPI0, TWIM0, TWISPI0);\nimpl_twim!(TWISPI1, TWIM1, TWISPI1);\n\nimpl_twis!(TWISPI0, TWIS0, TWISPI0);\nimpl_twis!(TWISPI1, TWIS1, TWISPI1);\n\nimpl_timer!(TIMER0, TIMER0, TIMER0);\nimpl_timer!(TIMER1, TIMER1, TIMER1);\nimpl_timer!(TIMER2, TIMER2, TIMER2);\nimpl_timer!(TIMER3, TIMER3, TIMER3, extended);\n\nimpl_rtc!(RTC0, RTC0, RTC0);\n#[cfg(not(feature = \"time-driver-rtc1\"))]\nimpl_rtc!(RTC1, RTC1, RTC1);\n\nimpl_qdec!(QDEC, QDEC, QDEC);\n\nimpl_rng!(RNG, RNG, RNG);\n\nimpl_pin!(P0_00, 0, 0);\nimpl_pin!(P0_01, 0, 1);\nimpl_pin!(P0_02, 0, 2);\nimpl_pin!(P0_03, 0, 3);\nimpl_pin!(P0_04, 0, 4);\nimpl_pin!(P0_05, 0, 5);\nimpl_pin!(P0_06, 0, 6);\nimpl_pin!(P0_07, 0, 7);\nimpl_pin!(P0_08, 0, 8);\nimpl_pin!(P0_09, 0, 9);\nimpl_pin!(P0_10, 0, 10);\nimpl_pin!(P0_11, 0, 11);\nimpl_pin!(P0_12, 0, 12);\nimpl_pin!(P0_13, 0, 13);\nimpl_pin!(P0_14, 0, 14);\nimpl_pin!(P0_15, 0, 15);\nimpl_pin!(P0_16, 0, 16);\nimpl_pin!(P0_17, 0, 17);\n#[cfg(feature = \"reset-pin-as-gpio\")]\nimpl_pin!(P0_18, 0, 18);\nimpl_pin!(P0_19, 0, 19);\nimpl_pin!(P0_20, 0, 20);\nimpl_pin!(P0_21, 0, 21);\nimpl_pin!(P0_22, 0, 22);\nimpl_pin!(P0_23, 0, 23);\nimpl_pin!(P0_24, 0, 24);\nimpl_pin!(P0_25, 0, 25);\nimpl_pin!(P0_26, 0, 26);\nimpl_pin!(P0_27, 0, 27);\nimpl_pin!(P0_28, 0, 28);\nimpl_pin!(P0_29, 0, 29);\nimpl_pin!(P0_30, 0, 30);\nimpl_pin!(P0_31, 0, 31);\n\nimpl_ppi_channel!(PPI_CH0, PPI, 0 => configurable);\nimpl_ppi_channel!(PPI_CH1, PPI, 1 => configurable);\nimpl_ppi_channel!(PPI_CH2, PPI, 2 => configurable);\nimpl_ppi_channel!(PPI_CH3, PPI, 3 => configurable);\nimpl_ppi_channel!(PPI_CH4, PPI, 4 => configurable);\nimpl_ppi_channel!(PPI_CH5, PPI, 5 => configurable);\nimpl_ppi_channel!(PPI_CH6, PPI, 6 => configurable);\nimpl_ppi_channel!(PPI_CH7, PPI, 7 => configurable);\nimpl_ppi_channel!(PPI_CH8, PPI, 8 => configurable);\nimpl_ppi_channel!(PPI_CH9, PPI, 9 => configurable);\nimpl_ppi_channel!(PPI_CH10, PPI, 10 => configurable);\nimpl_ppi_channel!(PPI_CH11, PPI, 11 => configurable);\nimpl_ppi_channel!(PPI_CH12, PPI, 12 => configurable);\nimpl_ppi_channel!(PPI_CH13, PPI, 13 => configurable);\nimpl_ppi_channel!(PPI_CH14, PPI, 14 => configurable);\nimpl_ppi_channel!(PPI_CH15, PPI, 15 => configurable);\nimpl_ppi_channel!(PPI_CH16, PPI, 16 => configurable);\nimpl_ppi_channel!(PPI_CH17, PPI, 17 => configurable);\nimpl_ppi_channel!(PPI_CH18, PPI, 18 => configurable);\nimpl_ppi_channel!(PPI_CH19, PPI, 19 => configurable);\nimpl_ppi_channel!(PPI_CH20, PPI, 20 => static);\nimpl_ppi_channel!(PPI_CH21, PPI, 21 => static);\nimpl_ppi_channel!(PPI_CH22, PPI, 22 => static);\nimpl_ppi_channel!(PPI_CH23, PPI, 23 => static);\nimpl_ppi_channel!(PPI_CH24, PPI, 24 => static);\nimpl_ppi_channel!(PPI_CH25, PPI, 25 => static);\nimpl_ppi_channel!(PPI_CH26, PPI, 26 => static);\nimpl_ppi_channel!(PPI_CH27, PPI, 27 => static);\nimpl_ppi_channel!(PPI_CH28, PPI, 28 => static);\nimpl_ppi_channel!(PPI_CH29, PPI, 29 => static);\nimpl_ppi_channel!(PPI_CH30, PPI, 30 => static);\nimpl_ppi_channel!(PPI_CH31, PPI, 31 => static);\n\nimpl_ppi_group!(PPI_GROUP0, PPI, 0);\nimpl_ppi_group!(PPI_GROUP1, PPI, 1);\nimpl_ppi_group!(PPI_GROUP2, PPI, 2);\nimpl_ppi_group!(PPI_GROUP3, PPI, 3);\nimpl_ppi_group!(PPI_GROUP4, PPI, 4);\nimpl_ppi_group!(PPI_GROUP5, PPI, 5);\n\nimpl_radio!(RADIO, RADIO, RADIO);\n\nimpl_egu!(EGU0, EGU0, EGU0_SWI0);\nimpl_egu!(EGU1, EGU1, EGU1_SWI1);\nimpl_egu!(EGU2, EGU2, EGU2_SWI2);\nimpl_egu!(EGU3, EGU3, EGU3_SWI3);\nimpl_egu!(EGU4, EGU4, EGU4_SWI4);\nimpl_egu!(EGU5, EGU5, EGU5_SWI5);\n\nimpl_wdt!(WDT, WDT, WDT, 0);\n\nembassy_hal_internal::interrupt_mod!(\n    CLOCK_POWER,\n    RADIO,\n    UARTE0,\n    TWISPI0,\n    TWISPI1,\n    GPIOTE,\n    TIMER0,\n    TIMER1,\n    TIMER2,\n    RTC0,\n    TEMP,\n    RNG,\n    ECB,\n    AAR_CCM,\n    WDT,\n    RTC1,\n    QDEC,\n    COMP,\n    EGU0_SWI0,\n    EGU1_SWI1,\n    EGU2_SWI2,\n    EGU3_SWI3,\n    EGU4_SWI4,\n    EGU5_SWI5,\n    TIMER3,\n    USBD,\n);\n"
  },
  {
    "path": "embassy-nrf/src/chips/nrf52832.rs",
    "content": "pub use nrf_pac as pac;\n\n/// The maximum buffer size that the EasyDMA can send/recv in one operation.\npub const EASY_DMA_SIZE: usize = (1 << 8) - 1;\npub const FORCE_COPY_BUFFER_SIZE: usize = 255;\n\n// There are two variants. We set the higher size to make the entire flash\n// usable in xxAA, but we'll probably split this in two cargi features later.\n// nrf52832xxAA = 512kb\n// nrf52832xxAB = 256kb\npub const FLASH_SIZE: usize = 512 * 1024;\n\npub const RESET_PIN: u32 = 21;\npub const APPROTECT_MIN_BUILD_CODE: u8 = b'G';\n\nembassy_hal_internal::peripherals! {\n    // RTC\n    RTC0,\n    #[cfg(not(feature=\"time-driver-rtc1\"))]\n    RTC1,\n    RTC2,\n\n    // WDT\n    WDT,\n\n    // NVMC\n    NVMC,\n\n    // RNG\n    RNG,\n\n    // UARTE\n    UARTE0,\n\n    // SPI/TWI\n    TWISPI0,\n    TWISPI1,\n    SPI2,\n\n    // SAADC\n    SAADC,\n\n    // PWM\n    PWM0,\n    PWM1,\n    PWM2,\n\n    // TIMER\n    TIMER0,\n    TIMER1,\n    TIMER2,\n    TIMER3,\n    TIMER4,\n\n    // GPIOTE\n    GPIOTE_CH0,\n    GPIOTE_CH1,\n    GPIOTE_CH2,\n    GPIOTE_CH3,\n    GPIOTE_CH4,\n    GPIOTE_CH5,\n    GPIOTE_CH6,\n    GPIOTE_CH7,\n\n    // PPI\n    PPI_CH0,\n    PPI_CH1,\n    PPI_CH2,\n    PPI_CH3,\n    PPI_CH4,\n    PPI_CH5,\n    PPI_CH6,\n    PPI_CH7,\n    PPI_CH8,\n    PPI_CH9,\n    PPI_CH10,\n    PPI_CH11,\n    PPI_CH12,\n    PPI_CH13,\n    PPI_CH14,\n    PPI_CH15,\n    PPI_CH16,\n    PPI_CH17,\n    PPI_CH18,\n    PPI_CH19,\n    PPI_CH20,\n    PPI_CH21,\n    PPI_CH22,\n    PPI_CH23,\n    PPI_CH24,\n    PPI_CH25,\n    PPI_CH26,\n    PPI_CH27,\n    PPI_CH28,\n    PPI_CH29,\n    PPI_CH30,\n    PPI_CH31,\n\n    PPI_GROUP0,\n    PPI_GROUP1,\n    PPI_GROUP2,\n    PPI_GROUP3,\n    PPI_GROUP4,\n    PPI_GROUP5,\n\n    // GPIO port 0\n    P0_00,\n    P0_01,\n    P0_02,\n    P0_03,\n    P0_04,\n    P0_05,\n    P0_06,\n    P0_07,\n    P0_08,\n    #[cfg(feature = \"nfc-pins-as-gpio\")]\n    P0_09,\n    #[cfg(feature = \"nfc-pins-as-gpio\")]\n    P0_10,\n    P0_11,\n    P0_12,\n    P0_13,\n    P0_14,\n    P0_15,\n    P0_16,\n    P0_17,\n    P0_18,\n    P0_19,\n    P0_20,\n    #[cfg(feature=\"reset-pin-as-gpio\")]\n    P0_21,\n    P0_22,\n    P0_23,\n    P0_24,\n    P0_25,\n    P0_26,\n    P0_27,\n    P0_28,\n    P0_29,\n    P0_30,\n    P0_31,\n\n    // TEMP\n    TEMP,\n\n    // QDEC\n    QDEC,\n\n    // I2S\n    I2S,\n\n    // PDM\n    PDM,\n\n    // Radio\n    RADIO,\n\n    // EGU\n    EGU0,\n    EGU1,\n    EGU2,\n    EGU3,\n    EGU4,\n    EGU5,\n\n    // NFC\n    NFCT,\n}\n\nimpl_uarte!(UARTE0, UARTE0, UARTE0);\n\nimpl_spim!(TWISPI0, SPIM0, TWISPI0);\nimpl_spim!(TWISPI1, SPIM1, TWISPI1);\nimpl_spim!(SPI2, SPIM2, SPI2);\n\nimpl_spis!(TWISPI0, SPIS0, TWISPI0);\nimpl_spis!(TWISPI1, SPIS1, TWISPI1);\nimpl_spis!(SPI2, SPIS2, SPI2);\n\nimpl_twim!(TWISPI0, TWIM0, TWISPI0);\nimpl_twim!(TWISPI1, TWIM1, TWISPI1);\n\nimpl_twis!(TWISPI0, TWIS0, TWISPI0);\nimpl_twis!(TWISPI1, TWIS1, TWISPI1);\n\nimpl_rtc!(RTC0, RTC0, RTC0);\n#[cfg(not(feature = \"time-driver-rtc1\"))]\nimpl_rtc!(RTC1, RTC1, RTC1);\nimpl_rtc!(RTC2, RTC2, RTC2);\n\nimpl_pwm!(PWM0, PWM0, PWM0);\nimpl_pwm!(PWM1, PWM1, PWM1);\nimpl_pwm!(PWM2, PWM2, PWM2);\n\nimpl_pdm!(PDM, PDM, PDM);\n\nimpl_qdec!(QDEC, QDEC, QDEC);\n\nimpl_rng!(RNG, RNG, RNG);\n\nimpl_timer!(TIMER0, TIMER0, TIMER0);\nimpl_timer!(TIMER1, TIMER1, TIMER1);\nimpl_timer!(TIMER2, TIMER2, TIMER2);\nimpl_timer!(TIMER3, TIMER3, TIMER3, extended);\nimpl_timer!(TIMER4, TIMER4, TIMER4, extended);\n\nimpl_pin!(P0_00, 0, 0);\nimpl_pin!(P0_01, 0, 1);\nimpl_pin!(P0_02, 0, 2);\nimpl_pin!(P0_03, 0, 3);\nimpl_pin!(P0_04, 0, 4);\nimpl_pin!(P0_05, 0, 5);\nimpl_pin!(P0_06, 0, 6);\nimpl_pin!(P0_07, 0, 7);\nimpl_pin!(P0_08, 0, 8);\n#[cfg(feature = \"nfc-pins-as-gpio\")]\nimpl_pin!(P0_09, 0, 9);\n#[cfg(feature = \"nfc-pins-as-gpio\")]\nimpl_pin!(P0_10, 0, 10);\nimpl_pin!(P0_11, 0, 11);\nimpl_pin!(P0_12, 0, 12);\nimpl_pin!(P0_13, 0, 13);\nimpl_pin!(P0_14, 0, 14);\nimpl_pin!(P0_15, 0, 15);\nimpl_pin!(P0_16, 0, 16);\nimpl_pin!(P0_17, 0, 17);\nimpl_pin!(P0_18, 0, 18);\nimpl_pin!(P0_19, 0, 19);\nimpl_pin!(P0_20, 0, 20);\n#[cfg(feature = \"reset-pin-as-gpio\")]\nimpl_pin!(P0_21, 0, 21);\nimpl_pin!(P0_22, 0, 22);\nimpl_pin!(P0_23, 0, 23);\nimpl_pin!(P0_24, 0, 24);\nimpl_pin!(P0_25, 0, 25);\nimpl_pin!(P0_26, 0, 26);\nimpl_pin!(P0_27, 0, 27);\nimpl_pin!(P0_28, 0, 28);\nimpl_pin!(P0_29, 0, 29);\nimpl_pin!(P0_30, 0, 30);\nimpl_pin!(P0_31, 0, 31);\n\nimpl_ppi_channel!(PPI_CH0, PPI, 0 => configurable);\nimpl_ppi_channel!(PPI_CH1, PPI, 1 => configurable);\nimpl_ppi_channel!(PPI_CH2, PPI, 2 => configurable);\nimpl_ppi_channel!(PPI_CH3, PPI, 3 => configurable);\nimpl_ppi_channel!(PPI_CH4, PPI, 4 => configurable);\nimpl_ppi_channel!(PPI_CH5, PPI, 5 => configurable);\nimpl_ppi_channel!(PPI_CH6, PPI, 6 => configurable);\nimpl_ppi_channel!(PPI_CH7, PPI, 7 => configurable);\nimpl_ppi_channel!(PPI_CH8, PPI, 8 => configurable);\nimpl_ppi_channel!(PPI_CH9, PPI, 9 => configurable);\nimpl_ppi_channel!(PPI_CH10, PPI, 10 => configurable);\nimpl_ppi_channel!(PPI_CH11, PPI, 11 => configurable);\nimpl_ppi_channel!(PPI_CH12, PPI, 12 => configurable);\nimpl_ppi_channel!(PPI_CH13, PPI, 13 => configurable);\nimpl_ppi_channel!(PPI_CH14, PPI, 14 => configurable);\nimpl_ppi_channel!(PPI_CH15, PPI, 15 => configurable);\nimpl_ppi_channel!(PPI_CH16, PPI, 16 => configurable);\nimpl_ppi_channel!(PPI_CH17, PPI, 17 => configurable);\nimpl_ppi_channel!(PPI_CH18, PPI, 18 => configurable);\nimpl_ppi_channel!(PPI_CH19, PPI, 19 => configurable);\nimpl_ppi_channel!(PPI_CH20, PPI, 20 => static);\nimpl_ppi_channel!(PPI_CH21, PPI, 21 => static);\nimpl_ppi_channel!(PPI_CH22, PPI, 22 => static);\nimpl_ppi_channel!(PPI_CH23, PPI, 23 => static);\nimpl_ppi_channel!(PPI_CH24, PPI, 24 => static);\nimpl_ppi_channel!(PPI_CH25, PPI, 25 => static);\nimpl_ppi_channel!(PPI_CH26, PPI, 26 => static);\nimpl_ppi_channel!(PPI_CH27, PPI, 27 => static);\nimpl_ppi_channel!(PPI_CH28, PPI, 28 => static);\nimpl_ppi_channel!(PPI_CH29, PPI, 29 => static);\nimpl_ppi_channel!(PPI_CH30, PPI, 30 => static);\nimpl_ppi_channel!(PPI_CH31, PPI, 31 => static);\n\nimpl_ppi_group!(PPI_GROUP0, PPI, 0);\nimpl_ppi_group!(PPI_GROUP1, PPI, 1);\nimpl_ppi_group!(PPI_GROUP2, PPI, 2);\nimpl_ppi_group!(PPI_GROUP3, PPI, 3);\nimpl_ppi_group!(PPI_GROUP4, PPI, 4);\nimpl_ppi_group!(PPI_GROUP5, PPI, 5);\n\nimpl_saadc_input!(P0_02, ANALOG_INPUT0);\nimpl_saadc_input!(P0_03, ANALOG_INPUT1);\nimpl_saadc_input!(P0_04, ANALOG_INPUT2);\nimpl_saadc_input!(P0_05, ANALOG_INPUT3);\nimpl_saadc_input!(P0_28, ANALOG_INPUT4);\nimpl_saadc_input!(P0_29, ANALOG_INPUT5);\nimpl_saadc_input!(P0_30, ANALOG_INPUT6);\nimpl_saadc_input!(P0_31, ANALOG_INPUT7);\n\nimpl_i2s!(I2S, I2S, I2S);\n\nimpl_radio!(RADIO, RADIO, RADIO);\n\nimpl_egu!(EGU0, EGU0, EGU0_SWI0);\nimpl_egu!(EGU1, EGU1, EGU1_SWI1);\nimpl_egu!(EGU2, EGU2, EGU2_SWI2);\nimpl_egu!(EGU3, EGU3, EGU3_SWI3);\nimpl_egu!(EGU4, EGU4, EGU4_SWI4);\nimpl_egu!(EGU5, EGU5, EGU5_SWI5);\n\nimpl_wdt!(WDT, WDT, WDT, 0);\n\nembassy_hal_internal::interrupt_mod!(\n    CLOCK_POWER,\n    RADIO,\n    UARTE0,\n    TWISPI0,\n    TWISPI1,\n    NFCT,\n    GPIOTE,\n    SAADC,\n    TIMER0,\n    TIMER1,\n    TIMER2,\n    RTC0,\n    TEMP,\n    RNG,\n    ECB,\n    AAR_CCM,\n    WDT,\n    RTC1,\n    QDEC,\n    COMP_LPCOMP,\n    EGU0_SWI0,\n    EGU1_SWI1,\n    EGU2_SWI2,\n    EGU3_SWI3,\n    EGU4_SWI4,\n    EGU5_SWI5,\n    TIMER3,\n    TIMER4,\n    PWM0,\n    PDM,\n    MWU,\n    PWM1,\n    PWM2,\n    SPI2,\n    RTC2,\n    I2S,\n    FPU,\n);\n"
  },
  {
    "path": "embassy-nrf/src/chips/nrf52833.rs",
    "content": "pub use nrf_pac as pac;\n\n/// The maximum buffer size that the EasyDMA can send/recv in one operation.\npub const EASY_DMA_SIZE: usize = (1 << 16) - 1;\npub const FORCE_COPY_BUFFER_SIZE: usize = 512;\n\npub const FLASH_SIZE: usize = 512 * 1024;\n\npub const RESET_PIN: u32 = 18;\npub const APPROTECT_MIN_BUILD_CODE: u8 = b'B';\n\nembassy_hal_internal::peripherals! {\n    // USB\n    USBD,\n\n    // RTC\n    RTC0,\n    #[cfg(not(feature = \"time-driver-rtc1\"))]\n    RTC1,\n    RTC2,\n\n    // WDT\n    WDT,\n\n    // NVMC\n    NVMC,\n\n    // RNG\n    RNG,\n\n    // UARTE\n    UARTE0,\n    UARTE1,\n\n    // SPI/TWI\n    TWISPI0,\n    TWISPI1,\n    SPI2,\n    SPI3,\n\n    // SAADC\n    SAADC,\n\n    // PWM\n    PWM0,\n    PWM1,\n    PWM2,\n    PWM3,\n\n    // TIMER\n    TIMER0,\n    TIMER1,\n    TIMER2,\n    TIMER3,\n    TIMER4,\n\n    // GPIOTE\n    GPIOTE_CH0,\n    GPIOTE_CH1,\n    GPIOTE_CH2,\n    GPIOTE_CH3,\n    GPIOTE_CH4,\n    GPIOTE_CH5,\n    GPIOTE_CH6,\n    GPIOTE_CH7,\n\n    // PPI\n    PPI_CH0,\n    PPI_CH1,\n    PPI_CH2,\n    PPI_CH3,\n    PPI_CH4,\n    PPI_CH5,\n    PPI_CH6,\n    PPI_CH7,\n    PPI_CH8,\n    PPI_CH9,\n    PPI_CH10,\n    PPI_CH11,\n    PPI_CH12,\n    PPI_CH13,\n    PPI_CH14,\n    PPI_CH15,\n    PPI_CH16,\n    PPI_CH17,\n    PPI_CH18,\n    PPI_CH19,\n    PPI_CH20,\n    PPI_CH21,\n    PPI_CH22,\n    PPI_CH23,\n    PPI_CH24,\n    PPI_CH25,\n    PPI_CH26,\n    PPI_CH27,\n    PPI_CH28,\n    PPI_CH29,\n    PPI_CH30,\n    PPI_CH31,\n\n    PPI_GROUP0,\n    PPI_GROUP1,\n    PPI_GROUP2,\n    PPI_GROUP3,\n    PPI_GROUP4,\n    PPI_GROUP5,\n\n    // GPIO port 0\n    P0_00,\n    P0_01,\n    P0_02,\n    P0_03,\n    P0_04,\n    P0_05,\n    P0_06,\n    P0_07,\n    P0_08,\n    #[cfg(feature = \"nfc-pins-as-gpio\")]\n    P0_09,\n    #[cfg(feature = \"nfc-pins-as-gpio\")]\n    P0_10,\n    P0_11,\n    P0_12,\n    P0_13,\n    P0_14,\n    P0_15,\n    P0_16,\n    P0_17,\n    #[cfg(feature=\"reset-pin-as-gpio\")]\n    P0_18,\n    P0_19,\n    P0_20,\n    P0_21,\n    P0_22,\n    P0_23,\n    P0_24,\n    P0_25,\n    P0_26,\n    P0_27,\n    P0_28,\n    P0_29,\n    P0_30,\n    P0_31,\n\n    // GPIO port 1\n    P1_00,\n    P1_01,\n    P1_02,\n    P1_03,\n    P1_04,\n    P1_05,\n    P1_06,\n    P1_07,\n    P1_08,\n    P1_09,\n    P1_10,\n    P1_11,\n    P1_12,\n    P1_13,\n    P1_14,\n    P1_15,\n\n    // TEMP\n    TEMP,\n\n    // QDEC\n    QDEC,\n\n    // PDM\n    PDM,\n\n    // I2S\n    I2S,\n\n    // Radio\n    RADIO,\n\n    // EGU\n    EGU0,\n    EGU1,\n    EGU2,\n    EGU3,\n    EGU4,\n    EGU5,\n\n    // NFC\n    NFCT,\n}\n\nimpl_usb!(USBD, USBD, USBD);\n\nimpl_uarte!(UARTE0, UARTE0, UARTE0);\nimpl_uarte!(UARTE1, UARTE1, UARTE1);\n\nimpl_spim!(TWISPI0, SPIM0, TWISPI0);\nimpl_spim!(TWISPI1, SPIM1, TWISPI1);\nimpl_spim!(SPI2, SPIM2, SPI2);\nimpl_spim!(SPI3, SPIM3, SPIM3);\n\nimpl_spis!(TWISPI0, SPIS0, TWISPI0);\nimpl_spis!(TWISPI1, SPIS1, TWISPI1);\nimpl_spis!(SPI2, SPIS2, SPI2);\n\nimpl_twim!(TWISPI0, TWIM0, TWISPI0);\nimpl_twim!(TWISPI1, TWIM1, TWISPI1);\n\nimpl_twis!(TWISPI0, TWIS0, TWISPI0);\nimpl_twis!(TWISPI1, TWIS1, TWISPI1);\n\nimpl_pwm!(PWM0, PWM0, PWM0);\nimpl_pwm!(PWM1, PWM1, PWM1);\nimpl_pwm!(PWM2, PWM2, PWM2);\nimpl_pwm!(PWM3, PWM3, PWM3);\n\nimpl_pdm!(PDM, PDM, PDM);\n\nimpl_qdec!(QDEC, QDEC, QDEC);\n\nimpl_rng!(RNG, RNG, RNG);\n\nimpl_timer!(TIMER0, TIMER0, TIMER0);\nimpl_timer!(TIMER1, TIMER1, TIMER1);\nimpl_timer!(TIMER2, TIMER2, TIMER2);\nimpl_timer!(TIMER3, TIMER3, TIMER3, extended);\nimpl_timer!(TIMER4, TIMER4, TIMER4, extended);\n\nimpl_rtc!(RTC0, RTC0, RTC0);\n#[cfg(not(feature = \"time-driver-rtc1\"))]\nimpl_rtc!(RTC1, RTC1, RTC1);\nimpl_rtc!(RTC2, RTC2, RTC2);\n\nimpl_pin!(P0_00, 0, 0);\nimpl_pin!(P0_01, 0, 1);\nimpl_pin!(P0_02, 0, 2);\nimpl_pin!(P0_03, 0, 3);\nimpl_pin!(P0_04, 0, 4);\nimpl_pin!(P0_05, 0, 5);\nimpl_pin!(P0_06, 0, 6);\nimpl_pin!(P0_07, 0, 7);\nimpl_pin!(P0_08, 0, 8);\n#[cfg(feature = \"nfc-pins-as-gpio\")]\nimpl_pin!(P0_09, 0, 9);\n#[cfg(feature = \"nfc-pins-as-gpio\")]\nimpl_pin!(P0_10, 0, 10);\nimpl_pin!(P0_11, 0, 11);\nimpl_pin!(P0_12, 0, 12);\nimpl_pin!(P0_13, 0, 13);\nimpl_pin!(P0_14, 0, 14);\nimpl_pin!(P0_15, 0, 15);\nimpl_pin!(P0_16, 0, 16);\nimpl_pin!(P0_17, 0, 17);\n#[cfg(feature = \"reset-pin-as-gpio\")]\nimpl_pin!(P0_18, 0, 18);\nimpl_pin!(P0_19, 0, 19);\nimpl_pin!(P0_20, 0, 20);\nimpl_pin!(P0_21, 0, 21);\nimpl_pin!(P0_22, 0, 22);\nimpl_pin!(P0_23, 0, 23);\nimpl_pin!(P0_24, 0, 24);\nimpl_pin!(P0_25, 0, 25);\nimpl_pin!(P0_26, 0, 26);\nimpl_pin!(P0_27, 0, 27);\nimpl_pin!(P0_28, 0, 28);\nimpl_pin!(P0_29, 0, 29);\nimpl_pin!(P0_30, 0, 30);\nimpl_pin!(P0_31, 0, 31);\n\nimpl_pin!(P1_00, 1, 0);\nimpl_pin!(P1_01, 1, 1);\nimpl_pin!(P1_02, 1, 2);\nimpl_pin!(P1_03, 1, 3);\nimpl_pin!(P1_04, 1, 4);\nimpl_pin!(P1_05, 1, 5);\nimpl_pin!(P1_06, 1, 6);\nimpl_pin!(P1_07, 1, 7);\nimpl_pin!(P1_08, 1, 8);\nimpl_pin!(P1_09, 1, 9);\nimpl_pin!(P1_10, 1, 10);\nimpl_pin!(P1_11, 1, 11);\nimpl_pin!(P1_12, 1, 12);\nimpl_pin!(P1_13, 1, 13);\nimpl_pin!(P1_14, 1, 14);\nimpl_pin!(P1_15, 1, 15);\n\nimpl_ppi_channel!(PPI_CH0, PPI, 0 => configurable);\nimpl_ppi_channel!(PPI_CH1, PPI, 1 => configurable);\nimpl_ppi_channel!(PPI_CH2, PPI, 2 => configurable);\nimpl_ppi_channel!(PPI_CH3, PPI, 3 => configurable);\nimpl_ppi_channel!(PPI_CH4, PPI, 4 => configurable);\nimpl_ppi_channel!(PPI_CH5, PPI, 5 => configurable);\nimpl_ppi_channel!(PPI_CH6, PPI, 6 => configurable);\nimpl_ppi_channel!(PPI_CH7, PPI, 7 => configurable);\nimpl_ppi_channel!(PPI_CH8, PPI, 8 => configurable);\nimpl_ppi_channel!(PPI_CH9, PPI, 9 => configurable);\nimpl_ppi_channel!(PPI_CH10, PPI, 10 => configurable);\nimpl_ppi_channel!(PPI_CH11, PPI, 11 => configurable);\nimpl_ppi_channel!(PPI_CH12, PPI, 12 => configurable);\nimpl_ppi_channel!(PPI_CH13, PPI, 13 => configurable);\nimpl_ppi_channel!(PPI_CH14, PPI, 14 => configurable);\nimpl_ppi_channel!(PPI_CH15, PPI, 15 => configurable);\nimpl_ppi_channel!(PPI_CH16, PPI, 16 => configurable);\nimpl_ppi_channel!(PPI_CH17, PPI, 17 => configurable);\nimpl_ppi_channel!(PPI_CH18, PPI, 18 => configurable);\nimpl_ppi_channel!(PPI_CH19, PPI, 19 => configurable);\nimpl_ppi_channel!(PPI_CH20, PPI, 20 => static);\nimpl_ppi_channel!(PPI_CH21, PPI, 21 => static);\nimpl_ppi_channel!(PPI_CH22, PPI, 22 => static);\nimpl_ppi_channel!(PPI_CH23, PPI, 23 => static);\nimpl_ppi_channel!(PPI_CH24, PPI, 24 => static);\nimpl_ppi_channel!(PPI_CH25, PPI, 25 => static);\nimpl_ppi_channel!(PPI_CH26, PPI, 26 => static);\nimpl_ppi_channel!(PPI_CH27, PPI, 27 => static);\nimpl_ppi_channel!(PPI_CH28, PPI, 28 => static);\nimpl_ppi_channel!(PPI_CH29, PPI, 29 => static);\nimpl_ppi_channel!(PPI_CH30, PPI, 30 => static);\nimpl_ppi_channel!(PPI_CH31, PPI, 31 => static);\n\nimpl_ppi_group!(PPI_GROUP0, PPI, 0);\nimpl_ppi_group!(PPI_GROUP1, PPI, 1);\nimpl_ppi_group!(PPI_GROUP2, PPI, 2);\nimpl_ppi_group!(PPI_GROUP3, PPI, 3);\nimpl_ppi_group!(PPI_GROUP4, PPI, 4);\nimpl_ppi_group!(PPI_GROUP5, PPI, 5);\n\nimpl_saadc_input!(P0_02, ANALOG_INPUT0);\nimpl_saadc_input!(P0_03, ANALOG_INPUT1);\nimpl_saadc_input!(P0_04, ANALOG_INPUT2);\nimpl_saadc_input!(P0_05, ANALOG_INPUT3);\nimpl_saadc_input!(P0_28, ANALOG_INPUT4);\nimpl_saadc_input!(P0_29, ANALOG_INPUT5);\nimpl_saadc_input!(P0_30, ANALOG_INPUT6);\nimpl_saadc_input!(P0_31, ANALOG_INPUT7);\n\nimpl_i2s!(I2S, I2S, I2S);\n\nimpl_radio!(RADIO, RADIO, RADIO);\n\nimpl_egu!(EGU0, EGU0, EGU0_SWI0);\nimpl_egu!(EGU1, EGU1, EGU1_SWI1);\nimpl_egu!(EGU2, EGU2, EGU2_SWI2);\nimpl_egu!(EGU3, EGU3, EGU3_SWI3);\nimpl_egu!(EGU4, EGU4, EGU4_SWI4);\nimpl_egu!(EGU5, EGU5, EGU5_SWI5);\n\nimpl_wdt!(WDT, WDT, WDT, 0);\n\nembassy_hal_internal::interrupt_mod!(\n    CLOCK_POWER,\n    RADIO,\n    UARTE0,\n    TWISPI0,\n    TWISPI1,\n    NFCT,\n    GPIOTE,\n    SAADC,\n    TIMER0,\n    TIMER1,\n    TIMER2,\n    RTC0,\n    TEMP,\n    RNG,\n    ECB,\n    AAR_CCM,\n    WDT,\n    RTC1,\n    QDEC,\n    COMP_LPCOMP,\n    EGU0_SWI0,\n    EGU1_SWI1,\n    EGU2_SWI2,\n    EGU3_SWI3,\n    EGU4_SWI4,\n    EGU5_SWI5,\n    TIMER3,\n    TIMER4,\n    PWM0,\n    PDM,\n    MWU,\n    PWM1,\n    PWM2,\n    SPI2,\n    RTC2,\n    I2S,\n    FPU,\n    USBD,\n    UARTE1,\n    PWM3,\n    SPIM3,\n);\n"
  },
  {
    "path": "embassy-nrf/src/chips/nrf52840.rs",
    "content": "pub use nrf_pac as pac;\n\n/// The maximum buffer size that the EasyDMA can send/recv in one operation.\npub const EASY_DMA_SIZE: usize = (1 << 16) - 1;\npub const FORCE_COPY_BUFFER_SIZE: usize = 512;\n\npub const FLASH_SIZE: usize = 1024 * 1024;\n\npub const RESET_PIN: u32 = 18;\npub const APPROTECT_MIN_BUILD_CODE: u8 = b'F';\n\nembassy_hal_internal::peripherals! {\n    // USB\n    USBD,\n\n    // RTC\n    RTC0,\n    #[cfg(not(feature = \"time-driver-rtc1\"))]\n    RTC1,\n    RTC2,\n\n    // WDT\n    WDT,\n\n    // NVMC\n    NVMC,\n\n    // RNG\n    RNG,\n\n    // QSPI\n    QSPI,\n\n    // QDEC\n    QDEC,\n\n    // UARTE\n    UARTE0,\n    UARTE1,\n\n    // SPI/TWI\n    TWISPI0,\n    TWISPI1,\n    SPI2,\n    SPI3,\n\n    // SAADC\n    SAADC,\n\n    // PWM\n    PWM0,\n    PWM1,\n    PWM2,\n    PWM3,\n\n    // TIMER\n    TIMER0,\n    TIMER1,\n    TIMER2,\n    TIMER3,\n    TIMER4,\n\n    // GPIOTE\n    GPIOTE_CH0,\n    GPIOTE_CH1,\n    GPIOTE_CH2,\n    GPIOTE_CH3,\n    GPIOTE_CH4,\n    GPIOTE_CH5,\n    GPIOTE_CH6,\n    GPIOTE_CH7,\n\n    // PPI\n    PPI_CH0,\n    PPI_CH1,\n    PPI_CH2,\n    PPI_CH3,\n    PPI_CH4,\n    PPI_CH5,\n    PPI_CH6,\n    PPI_CH7,\n    PPI_CH8,\n    PPI_CH9,\n    PPI_CH10,\n    PPI_CH11,\n    PPI_CH12,\n    PPI_CH13,\n    PPI_CH14,\n    PPI_CH15,\n    PPI_CH16,\n    PPI_CH17,\n    PPI_CH18,\n    PPI_CH19,\n    PPI_CH20,\n    PPI_CH21,\n    PPI_CH22,\n    PPI_CH23,\n    PPI_CH24,\n    PPI_CH25,\n    PPI_CH26,\n    PPI_CH27,\n    PPI_CH28,\n    PPI_CH29,\n    PPI_CH30,\n    PPI_CH31,\n\n    PPI_GROUP0,\n    PPI_GROUP1,\n    PPI_GROUP2,\n    PPI_GROUP3,\n    PPI_GROUP4,\n    PPI_GROUP5,\n\n    // GPIO port 0\n    P0_00,\n    P0_01,\n    P0_02,\n    P0_03,\n    P0_04,\n    P0_05,\n    P0_06,\n    P0_07,\n    P0_08,\n    #[cfg(feature = \"nfc-pins-as-gpio\")]\n    P0_09,\n    #[cfg(feature = \"nfc-pins-as-gpio\")]\n    P0_10,\n    P0_11,\n    P0_12,\n    P0_13,\n    P0_14,\n    P0_15,\n    P0_16,\n    P0_17,\n    #[cfg(feature=\"reset-pin-as-gpio\")]\n    P0_18,\n    P0_19,\n    P0_20,\n    P0_21,\n    P0_22,\n    P0_23,\n    P0_24,\n    P0_25,\n    P0_26,\n    P0_27,\n    P0_28,\n    P0_29,\n    P0_30,\n    P0_31,\n\n    // GPIO port 1\n    P1_00,\n    P1_01,\n    P1_02,\n    P1_03,\n    P1_04,\n    P1_05,\n    P1_06,\n    P1_07,\n    P1_08,\n    P1_09,\n    P1_10,\n    P1_11,\n    P1_12,\n    P1_13,\n    P1_14,\n    P1_15,\n\n    // TEMP\n    TEMP,\n\n    // PDM\n    PDM,\n\n    // I2S\n    I2S,\n\n    // Radio\n    RADIO,\n\n    // EGU\n    EGU0,\n    EGU1,\n    EGU2,\n    EGU3,\n    EGU4,\n    EGU5,\n\n    // NFC\n    NFCT,\n\n    // CryptoCell RNG\n    CC_RNG\n}\n\nimpl_usb!(USBD, USBD, USBD);\n\nimpl_uarte!(UARTE0, UARTE0, UARTE0);\nimpl_uarte!(UARTE1, UARTE1, UARTE1);\n\nimpl_spim!(TWISPI0, SPIM0, TWISPI0);\nimpl_spim!(TWISPI1, SPIM1, TWISPI1);\nimpl_spim!(SPI2, SPIM2, SPI2);\nimpl_spim!(SPI3, SPIM3, SPIM3);\n\nimpl_spis!(TWISPI0, SPIS0, TWISPI0);\nimpl_spis!(TWISPI1, SPIS1, TWISPI1);\nimpl_spis!(SPI2, SPIS2, SPI2);\n\nimpl_twim!(TWISPI0, TWIM0, TWISPI0);\nimpl_twim!(TWISPI1, TWIM1, TWISPI1);\n\nimpl_twis!(TWISPI0, TWIS0, TWISPI0);\nimpl_twis!(TWISPI1, TWIS1, TWISPI1);\n\nimpl_pwm!(PWM0, PWM0, PWM0);\nimpl_pwm!(PWM1, PWM1, PWM1);\nimpl_pwm!(PWM2, PWM2, PWM2);\nimpl_pwm!(PWM3, PWM3, PWM3);\n\nimpl_ccrng!(CC_RNG, CC_RNG, CRYPTOCELL);\n\nimpl_timer!(TIMER0, TIMER0, TIMER0);\nimpl_timer!(TIMER1, TIMER1, TIMER1);\nimpl_timer!(TIMER2, TIMER2, TIMER2);\nimpl_timer!(TIMER3, TIMER3, TIMER3, extended);\nimpl_timer!(TIMER4, TIMER4, TIMER4, extended);\n\nimpl_rtc!(RTC0, RTC0, RTC0);\n#[cfg(not(feature = \"time-driver-rtc1\"))]\nimpl_rtc!(RTC1, RTC1, RTC1);\nimpl_rtc!(RTC2, RTC2, RTC2);\n\nimpl_qspi!(QSPI, QSPI, QSPI);\n\nimpl_pdm!(PDM, PDM, PDM);\n\nimpl_qdec!(QDEC, QDEC, QDEC);\n\nimpl_rng!(RNG, RNG, RNG);\n\nimpl_pin!(P0_00, 0, 0);\nimpl_pin!(P0_01, 0, 1);\nimpl_pin!(P0_02, 0, 2);\nimpl_pin!(P0_03, 0, 3);\nimpl_pin!(P0_04, 0, 4);\nimpl_pin!(P0_05, 0, 5);\nimpl_pin!(P0_06, 0, 6);\nimpl_pin!(P0_07, 0, 7);\nimpl_pin!(P0_08, 0, 8);\n#[cfg(feature = \"nfc-pins-as-gpio\")]\nimpl_pin!(P0_09, 0, 9);\n#[cfg(feature = \"nfc-pins-as-gpio\")]\nimpl_pin!(P0_10, 0, 10);\nimpl_pin!(P0_11, 0, 11);\nimpl_pin!(P0_12, 0, 12);\nimpl_pin!(P0_13, 0, 13);\nimpl_pin!(P0_14, 0, 14);\nimpl_pin!(P0_15, 0, 15);\nimpl_pin!(P0_16, 0, 16);\nimpl_pin!(P0_17, 0, 17);\n#[cfg(feature = \"reset-pin-as-gpio\")]\nimpl_pin!(P0_18, 0, 18);\nimpl_pin!(P0_19, 0, 19);\nimpl_pin!(P0_20, 0, 20);\nimpl_pin!(P0_21, 0, 21);\nimpl_pin!(P0_22, 0, 22);\nimpl_pin!(P0_23, 0, 23);\nimpl_pin!(P0_24, 0, 24);\nimpl_pin!(P0_25, 0, 25);\nimpl_pin!(P0_26, 0, 26);\nimpl_pin!(P0_27, 0, 27);\nimpl_pin!(P0_28, 0, 28);\nimpl_pin!(P0_29, 0, 29);\nimpl_pin!(P0_30, 0, 30);\nimpl_pin!(P0_31, 0, 31);\n\nimpl_pin!(P1_00, 1, 0);\nimpl_pin!(P1_01, 1, 1);\nimpl_pin!(P1_02, 1, 2);\nimpl_pin!(P1_03, 1, 3);\nimpl_pin!(P1_04, 1, 4);\nimpl_pin!(P1_05, 1, 5);\nimpl_pin!(P1_06, 1, 6);\nimpl_pin!(P1_07, 1, 7);\nimpl_pin!(P1_08, 1, 8);\nimpl_pin!(P1_09, 1, 9);\nimpl_pin!(P1_10, 1, 10);\nimpl_pin!(P1_11, 1, 11);\nimpl_pin!(P1_12, 1, 12);\nimpl_pin!(P1_13, 1, 13);\nimpl_pin!(P1_14, 1, 14);\nimpl_pin!(P1_15, 1, 15);\n\nimpl_ppi_channel!(PPI_CH0, PPI, 0 => configurable);\nimpl_ppi_channel!(PPI_CH1, PPI, 1 => configurable);\nimpl_ppi_channel!(PPI_CH2, PPI, 2 => configurable);\nimpl_ppi_channel!(PPI_CH3, PPI, 3 => configurable);\nimpl_ppi_channel!(PPI_CH4, PPI, 4 => configurable);\nimpl_ppi_channel!(PPI_CH5, PPI, 5 => configurable);\nimpl_ppi_channel!(PPI_CH6, PPI, 6 => configurable);\nimpl_ppi_channel!(PPI_CH7, PPI, 7 => configurable);\nimpl_ppi_channel!(PPI_CH8, PPI, 8 => configurable);\nimpl_ppi_channel!(PPI_CH9, PPI, 9 => configurable);\nimpl_ppi_channel!(PPI_CH10, PPI, 10 => configurable);\nimpl_ppi_channel!(PPI_CH11, PPI, 11 => configurable);\nimpl_ppi_channel!(PPI_CH12, PPI, 12 => configurable);\nimpl_ppi_channel!(PPI_CH13, PPI, 13 => configurable);\nimpl_ppi_channel!(PPI_CH14, PPI, 14 => configurable);\nimpl_ppi_channel!(PPI_CH15, PPI, 15 => configurable);\nimpl_ppi_channel!(PPI_CH16, PPI, 16 => configurable);\nimpl_ppi_channel!(PPI_CH17, PPI, 17 => configurable);\nimpl_ppi_channel!(PPI_CH18, PPI, 18 => configurable);\nimpl_ppi_channel!(PPI_CH19, PPI, 19 => configurable);\nimpl_ppi_channel!(PPI_CH20, PPI, 20 => static);\nimpl_ppi_channel!(PPI_CH21, PPI, 21 => static);\nimpl_ppi_channel!(PPI_CH22, PPI, 22 => static);\nimpl_ppi_channel!(PPI_CH23, PPI, 23 => static);\nimpl_ppi_channel!(PPI_CH24, PPI, 24 => static);\nimpl_ppi_channel!(PPI_CH25, PPI, 25 => static);\nimpl_ppi_channel!(PPI_CH26, PPI, 26 => static);\nimpl_ppi_channel!(PPI_CH27, PPI, 27 => static);\nimpl_ppi_channel!(PPI_CH28, PPI, 28 => static);\nimpl_ppi_channel!(PPI_CH29, PPI, 29 => static);\nimpl_ppi_channel!(PPI_CH30, PPI, 30 => static);\nimpl_ppi_channel!(PPI_CH31, PPI, 31 => static);\n\nimpl_ppi_group!(PPI_GROUP0, PPI, 0);\nimpl_ppi_group!(PPI_GROUP1, PPI, 1);\nimpl_ppi_group!(PPI_GROUP2, PPI, 2);\nimpl_ppi_group!(PPI_GROUP3, PPI, 3);\nimpl_ppi_group!(PPI_GROUP4, PPI, 4);\nimpl_ppi_group!(PPI_GROUP5, PPI, 5);\n\nimpl_saadc_input!(P0_02, ANALOG_INPUT0);\nimpl_saadc_input!(P0_03, ANALOG_INPUT1);\nimpl_saadc_input!(P0_04, ANALOG_INPUT2);\nimpl_saadc_input!(P0_05, ANALOG_INPUT3);\nimpl_saadc_input!(P0_28, ANALOG_INPUT4);\nimpl_saadc_input!(P0_29, ANALOG_INPUT5);\nimpl_saadc_input!(P0_30, ANALOG_INPUT6);\nimpl_saadc_input!(P0_31, ANALOG_INPUT7);\n\nimpl_i2s!(I2S, I2S, I2S);\n\nimpl_radio!(RADIO, RADIO, RADIO);\n\nimpl_egu!(EGU0, EGU0, EGU0_SWI0);\nimpl_egu!(EGU1, EGU1, EGU1_SWI1);\nimpl_egu!(EGU2, EGU2, EGU2_SWI2);\nimpl_egu!(EGU3, EGU3, EGU3_SWI3);\nimpl_egu!(EGU4, EGU4, EGU4_SWI4);\nimpl_egu!(EGU5, EGU5, EGU5_SWI5);\n\nimpl_wdt!(WDT, WDT, WDT, 0);\n\nembassy_hal_internal::interrupt_mod!(\n    CLOCK_POWER,\n    RADIO,\n    UARTE0,\n    TWISPI0,\n    TWISPI1,\n    NFCT,\n    GPIOTE,\n    SAADC,\n    TIMER0,\n    TIMER1,\n    TIMER2,\n    RTC0,\n    TEMP,\n    RNG,\n    ECB,\n    AAR_CCM,\n    WDT,\n    RTC1,\n    QDEC,\n    COMP_LPCOMP,\n    EGU0_SWI0,\n    EGU1_SWI1,\n    EGU2_SWI2,\n    EGU3_SWI3,\n    EGU4_SWI4,\n    EGU5_SWI5,\n    TIMER3,\n    TIMER4,\n    PWM0,\n    PDM,\n    MWU,\n    PWM1,\n    PWM2,\n    SPI2,\n    RTC2,\n    I2S,\n    FPU,\n    USBD,\n    UARTE1,\n    QSPI,\n    CRYPTOCELL,\n    PWM3,\n    SPIM3,\n);\n"
  },
  {
    "path": "embassy-nrf/src/chips/nrf5340_app.rs",
    "content": "/// Peripheral Access Crate\n#[allow(unused_imports)]\n#[rustfmt::skip]\npub mod pac {\n    pub use nrf_pac::*;\n\n    #[cfg(feature = \"_ns\")]\n    #[doc(no_inline)]\n    pub use nrf_pac::{\n        CLOCK_NS as CLOCK,\n        COMP_NS as COMP,\n        CTRLAP_NS as CTRLAP,\n        DCNF_NS as DCNF,\n        DPPIC_NS as DPPIC,\n        EGU0_NS as EGU0,\n        EGU1_NS as EGU1,\n        EGU2_NS as EGU2,\n        EGU3_NS as EGU3,\n        EGU4_NS as EGU4,\n        EGU5_NS as EGU5,\n        FPU_NS as FPU,\n        GPIOTE1_NS as GPIOTE1,\n        I2S0_NS as I2S0,\n        IPC_NS as IPC,\n        KMU_NS as KMU,\n        LPCOMP_NS as LPCOMP,\n        MUTEX_NS as MUTEX,\n        NFCT_NS as NFCT,\n        NVMC_NS as NVMC,\n        OSCILLATORS_NS as OSCILLATORS,\n        P0_NS as P0,\n        P1_NS as P1,\n        PDM0_NS as PDM0,\n        POWER_NS as POWER,\n        PWM0_NS as PWM0,\n        PWM1_NS as PWM1,\n        PWM2_NS as PWM2,\n        PWM3_NS as PWM3,\n        QDEC0_NS as QDEC0,\n        QDEC1_NS as QDEC1,\n        QSPI_NS as QSPI,\n        REGULATORS_NS as REGULATORS,\n        RESET_NS as RESET,\n        RTC0_NS as RTC0,\n        RTC1_NS as RTC1,\n        SAADC_NS as SAADC,\n        SPIM0_NS as SPIM0,\n        SPIM1_NS as SPIM1,\n        SPIM2_NS as SPIM2,\n        SPIM3_NS as SPIM3,\n        SPIM4_NS as SPIM4,\n        SPIS0_NS as SPIS0,\n        SPIS1_NS as SPIS1,\n        SPIS2_NS as SPIS2,\n        SPIS3_NS as SPIS3,\n        TIMER0_NS as TIMER0,\n        TIMER1_NS as TIMER1,\n        TIMER2_NS as TIMER2,\n        TWIM0_NS as TWIM0,\n        TWIM1_NS as TWIM1,\n        TWIM2_NS as TWIM2,\n        TWIM3_NS as TWIM3,\n        TWIS0_NS as TWIS0,\n        TWIS1_NS as TWIS1,\n        TWIS2_NS as TWIS2,\n        TWIS3_NS as TWIS3,\n        UARTE0_NS as UARTE0,\n        UARTE1_NS as UARTE1,\n        UARTE2_NS as UARTE2,\n        UARTE3_NS as UARTE3,\n        USBD_NS as USBD,\n        USBREGULATOR_NS as USBREGULATOR,\n        VMC_NS as VMC,\n        WDT0_NS as WDT0,\n        WDT1_NS as WDT1,\n    };\n\n    #[cfg(feature = \"_s\")]\n    #[doc(no_inline)]\n    pub use nrf_pac::{\n        CACHEDATA_S as CACHEDATA,\n        CACHEINFO_S as CACHEINFO,\n        CACHE_S as CACHE,\n        CC_HOST_RGF_S as CC_HOST_RGF,\n        CC_RNG_S as CC_RNG,\n        CLOCK_S as CLOCK,\n        COMP_S as COMP,\n        CRYPTOCELL_S as CRYPTOCELL,\n        CTI_S as CTI,\n        CTRLAP_S as CTRLAP,\n        DCNF_S as DCNF,\n        DPPIC_S as DPPIC,\n        EGU0_S as EGU0,\n        EGU1_S as EGU1,\n        EGU2_S as EGU2,\n        EGU3_S as EGU3,\n        EGU4_S as EGU4,\n        EGU5_S as EGU5,\n        FICR_S as FICR,\n        FPU_S as FPU,\n        GPIOTE0_S as GPIOTE0,\n        I2S0_S as I2S0,\n        IPC_S as IPC,\n        KMU_S as KMU,\n        LPCOMP_S as LPCOMP,\n        MUTEX_S as MUTEX,\n        NFCT_S as NFCT,\n        NVMC_S as NVMC,\n        OSCILLATORS_S as OSCILLATORS,\n        P0_S as P0,\n        P1_S as P1,\n        PDM0_S as PDM0,\n        POWER_S as POWER,\n        PWM0_S as PWM0,\n        PWM1_S as PWM1,\n        PWM2_S as PWM2,\n        PWM3_S as PWM3,\n        QDEC0_S as QDEC0,\n        QDEC1_S as QDEC1,\n        QSPI_S as QSPI,\n        REGULATORS_S as REGULATORS,\n        RESET_S as RESET,\n        RTC0_S as RTC0,\n        RTC1_S as RTC1,\n        SAADC_S as SAADC,\n        SPIM0_S as SPIM0,\n        SPIM1_S as SPIM1,\n        SPIM2_S as SPIM2,\n        SPIM3_S as SPIM3,\n        SPIM4_S as SPIM4,\n        SPIS0_S as SPIS0,\n        SPIS1_S as SPIS1,\n        SPIS2_S as SPIS2,\n        SPIS3_S as SPIS3,\n        SPU_S as SPU,\n        TAD_S as TAD,\n        TIMER0_S as TIMER0,\n        TIMER1_S as TIMER1,\n        TIMER2_S as TIMER2,\n        TWIM0_S as TWIM0,\n        TWIM1_S as TWIM1,\n        TWIM2_S as TWIM2,\n        TWIM3_S as TWIM3,\n        TWIS0_S as TWIS0,\n        TWIS1_S as TWIS1,\n        TWIS2_S as TWIS2,\n        TWIS3_S as TWIS3,\n        UARTE0_S as UARTE0,\n        UARTE1_S as UARTE1,\n        UARTE2_S as UARTE2,\n        UARTE3_S as UARTE3,\n        UICR_S as UICR,\n        USBD_S as USBD,\n        USBREGULATOR_S as USBREGULATOR,\n        VMC_S as VMC,\n        WDT0_S as WDT0,\n        WDT1_S as WDT1,\n    };\n}\n\n/// The maximum buffer size that the EasyDMA can send/recv in one operation.\npub const EASY_DMA_SIZE: usize = (1 << 16) - 1;\npub const FORCE_COPY_BUFFER_SIZE: usize = 1024;\n\npub const FLASH_SIZE: usize = 1024 * 1024;\n\nembassy_hal_internal::peripherals! {\n    // USB\n    USBD,\n\n    // RTC\n    RTC0,\n    #[cfg(not(feature = \"time-driver-rtc1\"))]\n    RTC1,\n\n    // WDT\n    WDT0,\n    WDT1,\n\n    // NVMC\n    NVMC,\n\n    // NFC\n    NFCT,\n\n    // UARTE, TWI & SPI\n    SERIAL0,\n    SERIAL1,\n    SERIAL2,\n    SERIAL3,\n    SPIM4,\n\n    // SAADC\n    SAADC,\n\n    // PWM\n    PWM0,\n    PWM1,\n    PWM2,\n    PWM3,\n\n    // TIMER\n    TIMER0,\n    TIMER1,\n    TIMER2,\n\n    // QSPI\n    QSPI,\n\n    // PDM\n    PDM0,\n\n    // QDEC\n    QDEC0,\n    QDEC1,\n\n    // GPIOTE\n    GPIOTE_CH0,\n    GPIOTE_CH1,\n    GPIOTE_CH2,\n    GPIOTE_CH3,\n    GPIOTE_CH4,\n    GPIOTE_CH5,\n    GPIOTE_CH6,\n    GPIOTE_CH7,\n\n    // PPI\n    PPI_CH0,\n    PPI_CH1,\n    PPI_CH2,\n    PPI_CH3,\n    PPI_CH4,\n    PPI_CH5,\n    PPI_CH6,\n    PPI_CH7,\n    PPI_CH8,\n    PPI_CH9,\n    PPI_CH10,\n    PPI_CH11,\n    PPI_CH12,\n    PPI_CH13,\n    PPI_CH14,\n    PPI_CH15,\n    PPI_CH16,\n    PPI_CH17,\n    PPI_CH18,\n    PPI_CH19,\n    PPI_CH20,\n    PPI_CH21,\n    PPI_CH22,\n    PPI_CH23,\n    PPI_CH24,\n    PPI_CH25,\n    PPI_CH26,\n    PPI_CH27,\n    PPI_CH28,\n    PPI_CH29,\n    PPI_CH30,\n    PPI_CH31,\n\n    PPI_GROUP0,\n    PPI_GROUP1,\n    PPI_GROUP2,\n    PPI_GROUP3,\n    PPI_GROUP4,\n    PPI_GROUP5,\n\n    // IPC\n    IPC,\n\n    // GPIO port 0\n    #[cfg(feature = \"lfxo-pins-as-gpio\")]\n    P0_00,\n    #[cfg(feature = \"lfxo-pins-as-gpio\")]\n    P0_01,\n    #[cfg(feature = \"nfc-pins-as-gpio\")]\n    P0_02,\n    #[cfg(feature = \"nfc-pins-as-gpio\")]\n    P0_03,\n    P0_04,\n    P0_05,\n    P0_06,\n    P0_07,\n    P0_08,\n    P0_09,\n    P0_10,\n    P0_11,\n    P0_12,\n    P0_13,\n    P0_14,\n    P0_15,\n    P0_16,\n    P0_17,\n    P0_18,\n    P0_19,\n    P0_20,\n    P0_21,\n    P0_22,\n    P0_23,\n    P0_24,\n    P0_25,\n    P0_26,\n    P0_27,\n    P0_28,\n    P0_29,\n    P0_30,\n    P0_31,\n\n    // GPIO port 1\n    P1_00,\n    P1_01,\n    P1_02,\n    P1_03,\n    P1_04,\n    P1_05,\n    P1_06,\n    P1_07,\n    P1_08,\n    P1_09,\n    P1_10,\n    P1_11,\n    P1_12,\n    P1_13,\n    P1_14,\n    P1_15,\n\n    // EGU\n    EGU0,\n    EGU1,\n    EGU2,\n    EGU3,\n    EGU4,\n    EGU5,\n\n    // CryptoCell RNG\n    #[cfg(feature = \"_s\")]\n    CC_RNG\n}\n\nimpl_ipc!(IPC, IPC, IPC);\n\nimpl_usb!(USBD, USBD, USBD);\n\nimpl_uarte!(SERIAL0, UARTE0, SERIAL0);\nimpl_uarte!(SERIAL1, UARTE1, SERIAL1);\nimpl_uarte!(SERIAL2, UARTE2, SERIAL2);\nimpl_uarte!(SERIAL3, UARTE3, SERIAL3);\n\nimpl_spim!(SERIAL0, SPIM0, SERIAL0);\nimpl_spim!(SERIAL1, SPIM1, SERIAL1);\nimpl_spim!(SERIAL2, SPIM2, SERIAL2);\nimpl_spim!(SERIAL3, SPIM3, SERIAL3);\nimpl_spim!(SPIM4, SPIM4, SPIM4);\n\nimpl_spis!(SERIAL0, SPIS0, SERIAL0);\nimpl_spis!(SERIAL1, SPIS1, SERIAL1);\nimpl_spis!(SERIAL2, SPIS2, SERIAL2);\nimpl_spis!(SERIAL3, SPIS3, SERIAL3);\n\nimpl_twim!(SERIAL0, TWIM0, SERIAL0);\nimpl_twim!(SERIAL1, TWIM1, SERIAL1);\nimpl_twim!(SERIAL2, TWIM2, SERIAL2);\nimpl_twim!(SERIAL3, TWIM3, SERIAL3);\n\nimpl_twis!(SERIAL0, TWIS0, SERIAL0);\nimpl_twis!(SERIAL1, TWIS1, SERIAL1);\nimpl_twis!(SERIAL2, TWIS2, SERIAL2);\nimpl_twis!(SERIAL3, TWIS3, SERIAL3);\n\nimpl_pwm!(PWM0, PWM0, PWM0);\nimpl_pwm!(PWM1, PWM1, PWM1);\nimpl_pwm!(PWM2, PWM2, PWM2);\nimpl_pwm!(PWM3, PWM3, PWM3);\n\n#[cfg(feature = \"_s\")]\nimpl_ccrng!(CC_RNG, CC_RNG, CRYPTOCELL);\n\nimpl_timer!(TIMER0, TIMER0, TIMER0);\nimpl_timer!(TIMER1, TIMER1, TIMER1);\nimpl_timer!(TIMER2, TIMER2, TIMER2);\n\nimpl_rtc!(RTC0, RTC0, RTC0);\n#[cfg(not(feature = \"time-driver-rtc1\"))]\nimpl_rtc!(RTC1, RTC1, RTC1);\n\nimpl_qspi!(QSPI, QSPI, QSPI);\n\nimpl_pdm!(PDM0, PDM0, PDM0);\n\nimpl_qdec!(QDEC0, QDEC0, QDEC0);\nimpl_qdec!(QDEC1, QDEC1, QDEC1);\n\n#[cfg(feature = \"lfxo-pins-as-gpio\")]\nimpl_pin!(P0_00, 0, 0);\n#[cfg(feature = \"lfxo-pins-as-gpio\")]\nimpl_pin!(P0_01, 0, 1);\n#[cfg(feature = \"nfc-pins-as-gpio\")]\nimpl_pin!(P0_02, 0, 2);\n#[cfg(feature = \"nfc-pins-as-gpio\")]\nimpl_pin!(P0_03, 0, 3);\nimpl_pin!(P0_04, 0, 4);\nimpl_pin!(P0_05, 0, 5);\nimpl_pin!(P0_06, 0, 6);\nimpl_pin!(P0_07, 0, 7);\nimpl_pin!(P0_08, 0, 8);\nimpl_pin!(P0_09, 0, 9);\nimpl_pin!(P0_10, 0, 10);\nimpl_pin!(P0_11, 0, 11);\nimpl_pin!(P0_12, 0, 12);\nimpl_pin!(P0_13, 0, 13);\nimpl_pin!(P0_14, 0, 14);\nimpl_pin!(P0_15, 0, 15);\nimpl_pin!(P0_16, 0, 16);\nimpl_pin!(P0_17, 0, 17);\nimpl_pin!(P0_18, 0, 18);\nimpl_pin!(P0_19, 0, 19);\nimpl_pin!(P0_20, 0, 20);\nimpl_pin!(P0_21, 0, 21);\nimpl_pin!(P0_22, 0, 22);\nimpl_pin!(P0_23, 0, 23);\nimpl_pin!(P0_24, 0, 24);\nimpl_pin!(P0_25, 0, 25);\nimpl_pin!(P0_26, 0, 26);\nimpl_pin!(P0_27, 0, 27);\nimpl_pin!(P0_28, 0, 28);\nimpl_pin!(P0_29, 0, 29);\nimpl_pin!(P0_30, 0, 30);\nimpl_pin!(P0_31, 0, 31);\n\nimpl_pin!(P1_00, 1, 0);\nimpl_pin!(P1_01, 1, 1);\nimpl_pin!(P1_02, 1, 2);\nimpl_pin!(P1_03, 1, 3);\nimpl_pin!(P1_04, 1, 4);\nimpl_pin!(P1_05, 1, 5);\nimpl_pin!(P1_06, 1, 6);\nimpl_pin!(P1_07, 1, 7);\nimpl_pin!(P1_08, 1, 8);\nimpl_pin!(P1_09, 1, 9);\nimpl_pin!(P1_10, 1, 10);\nimpl_pin!(P1_11, 1, 11);\nimpl_pin!(P1_12, 1, 12);\nimpl_pin!(P1_13, 1, 13);\nimpl_pin!(P1_14, 1, 14);\nimpl_pin!(P1_15, 1, 15);\n\nimpl_ppi_channel!(PPI_CH0, DPPIC, 0 => configurable);\nimpl_ppi_channel!(PPI_CH1, DPPIC, 1 => configurable);\nimpl_ppi_channel!(PPI_CH2, DPPIC, 2 => configurable);\nimpl_ppi_channel!(PPI_CH3, DPPIC, 3 => configurable);\nimpl_ppi_channel!(PPI_CH4, DPPIC, 4 => configurable);\nimpl_ppi_channel!(PPI_CH5, DPPIC, 5 => configurable);\nimpl_ppi_channel!(PPI_CH6, DPPIC, 6 => configurable);\nimpl_ppi_channel!(PPI_CH7, DPPIC, 7 => configurable);\nimpl_ppi_channel!(PPI_CH8, DPPIC, 8 => configurable);\nimpl_ppi_channel!(PPI_CH9, DPPIC, 9 => configurable);\nimpl_ppi_channel!(PPI_CH10, DPPIC, 10 => configurable);\nimpl_ppi_channel!(PPI_CH11, DPPIC, 11 => configurable);\nimpl_ppi_channel!(PPI_CH12, DPPIC, 12 => configurable);\nimpl_ppi_channel!(PPI_CH13, DPPIC, 13 => configurable);\nimpl_ppi_channel!(PPI_CH14, DPPIC, 14 => configurable);\nimpl_ppi_channel!(PPI_CH15, DPPIC, 15 => configurable);\nimpl_ppi_channel!(PPI_CH16, DPPIC, 16 => configurable);\nimpl_ppi_channel!(PPI_CH17, DPPIC, 17 => configurable);\nimpl_ppi_channel!(PPI_CH18, DPPIC, 18 => configurable);\nimpl_ppi_channel!(PPI_CH19, DPPIC, 19 => configurable);\nimpl_ppi_channel!(PPI_CH20, DPPIC, 20 => configurable);\nimpl_ppi_channel!(PPI_CH21, DPPIC, 21 => configurable);\nimpl_ppi_channel!(PPI_CH22, DPPIC, 22 => configurable);\nimpl_ppi_channel!(PPI_CH23, DPPIC, 23 => configurable);\nimpl_ppi_channel!(PPI_CH24, DPPIC, 24 => configurable);\nimpl_ppi_channel!(PPI_CH25, DPPIC, 25 => configurable);\nimpl_ppi_channel!(PPI_CH26, DPPIC, 26 => configurable);\nimpl_ppi_channel!(PPI_CH27, DPPIC, 27 => configurable);\nimpl_ppi_channel!(PPI_CH28, DPPIC, 28 => configurable);\nimpl_ppi_channel!(PPI_CH29, DPPIC, 29 => configurable);\nimpl_ppi_channel!(PPI_CH30, DPPIC, 30 => configurable);\nimpl_ppi_channel!(PPI_CH31, DPPIC, 31 => configurable);\n\nimpl_ppi_group!(PPI_GROUP0, DPPIC, 0);\nimpl_ppi_group!(PPI_GROUP1, DPPIC, 1);\nimpl_ppi_group!(PPI_GROUP2, DPPIC, 2);\nimpl_ppi_group!(PPI_GROUP3, DPPIC, 3);\nimpl_ppi_group!(PPI_GROUP4, DPPIC, 4);\nimpl_ppi_group!(PPI_GROUP5, DPPIC, 5);\n\nimpl_saadc_input!(P0_04, ANALOG_INPUT0);\nimpl_saadc_input!(P0_05, ANALOG_INPUT1);\nimpl_saadc_input!(P0_06, ANALOG_INPUT2);\nimpl_saadc_input!(P0_07, ANALOG_INPUT3);\nimpl_saadc_input!(P0_25, ANALOG_INPUT4);\nimpl_saadc_input!(P0_26, ANALOG_INPUT5);\nimpl_saadc_input!(P0_27, ANALOG_INPUT6);\nimpl_saadc_input!(P0_28, ANALOG_INPUT7);\n\nimpl_egu!(EGU0, EGU0, EGU0);\nimpl_egu!(EGU1, EGU1, EGU1);\nimpl_egu!(EGU2, EGU2, EGU2);\nimpl_egu!(EGU3, EGU3, EGU3);\nimpl_egu!(EGU4, EGU4, EGU4);\nimpl_egu!(EGU5, EGU5, EGU5);\n\nimpl_wdt!(WDT0, WDT0, WDT0, 0);\nimpl_wdt!(WDT1, WDT1, WDT1, 1);\n\nembassy_hal_internal::interrupt_mod!(\n    FPU,\n    CACHE,\n    SPU,\n    CLOCK_POWER,\n    SERIAL0,\n    SERIAL1,\n    SPIM4,\n    SERIAL2,\n    SERIAL3,\n    GPIOTE0,\n    SAADC,\n    TIMER0,\n    TIMER1,\n    TIMER2,\n    RTC0,\n    RTC1,\n    WDT0,\n    WDT1,\n    COMP_LPCOMP,\n    EGU0,\n    EGU1,\n    EGU2,\n    EGU3,\n    EGU4,\n    EGU5,\n    PWM0,\n    PWM1,\n    PWM2,\n    PWM3,\n    PDM0,\n    I2S0,\n    IPC,\n    QSPI,\n    NFCT,\n    GPIOTE1,\n    QDEC0,\n    QDEC1,\n    USBD,\n    USBREGULATOR,\n    KMU,\n    CRYPTOCELL,\n);\n"
  },
  {
    "path": "embassy-nrf/src/chips/nrf5340_net.rs",
    "content": "/// Peripheral Access Crate\n#[allow(unused_imports)]\n#[rustfmt::skip]\npub mod pac {\n    pub use nrf_pac::*;\n\n    #[doc(no_inline)]\n    pub use nrf_pac::{\n        AAR_NS as AAR,\n        ACL_NS as ACL,\n        APPMUTEX_NS as APPMUTEX,\n        APPMUTEX_S as APPMUTEX_S,\n        CCM_NS as CCM,\n        CLOCK_NS as CLOCK,\n        CTI_NS as CTI,\n        CTRLAP_NS as CTRLAP,\n        DCNF_NS as DCNF,\n        DPPIC_NS as DPPIC,\n        ECB_NS as ECB,\n        EGU0_NS as EGU0,\n        FICR_NS as FICR,\n        GPIOTE_NS as GPIOTE,\n        IPC_NS as IPC,\n        NVMC_NS as NVMC,\n        P0_NS as P0,\n        P1_NS as P1,\n        POWER_NS as POWER,\n        RADIO_NS as RADIO,\n        RESET_NS as RESET,\n        RNG_NS as RNG,\n        RTC0_NS as RTC0,\n        RTC1_NS as RTC1,\n        SPIM0_NS as SPIM0,\n        SPIS0_NS as SPIS0,\n        SWI0_NS as SWI0,\n        SWI1_NS as SWI1,\n        SWI2_NS as SWI2,\n        SWI3_NS as SWI3,\n        TEMP_NS as TEMP,\n        TIMER0_NS as TIMER0,\n        TIMER1_NS as TIMER1,\n        TIMER2_NS as TIMER2,\n        TWIM0_NS as TWIM0,\n        TWIS0_NS as TWIS0,\n        UARTE0_NS as UARTE0,\n        UICR_NS as UICR,\n        VMC_NS as VMC,\n        VREQCTRL_NS as VREQCTRL,\n        WDT_NS as WDT,\n    };\n}\n\n/// The maximum buffer size that the EasyDMA can send/recv in one operation.\npub const EASY_DMA_SIZE: usize = (1 << 16) - 1;\npub const FORCE_COPY_BUFFER_SIZE: usize = 1024;\n\npub const FLASH_SIZE: usize = 256 * 1024;\n\nembassy_hal_internal::peripherals! {\n    // RTC\n    RTC0,\n    #[cfg(not(feature = \"time-driver-rtc1\"))]\n    RTC1,\n\n    // WDT\n    WDT,\n\n    // NVMC\n    NVMC,\n\n    // UARTE, TWI & SPI\n    SERIAL0,\n    SERIAL1,\n    SERIAL2,\n    SERIAL3,\n\n    // SAADC\n    SAADC,\n\n    // RNG\n    RNG,\n\n    // PWM\n    PWM0,\n    PWM1,\n    PWM2,\n    PWM3,\n\n    // TIMER\n    TIMER0,\n    TIMER1,\n    TIMER2,\n\n    // GPIOTE\n    GPIOTE_CH0,\n    GPIOTE_CH1,\n    GPIOTE_CH2,\n    GPIOTE_CH3,\n    GPIOTE_CH4,\n    GPIOTE_CH5,\n    GPIOTE_CH6,\n    GPIOTE_CH7,\n\n    // PPI\n    PPI_CH0,\n    PPI_CH1,\n    PPI_CH2,\n    PPI_CH3,\n    PPI_CH4,\n    PPI_CH5,\n    PPI_CH6,\n    PPI_CH7,\n    PPI_CH8,\n    PPI_CH9,\n    PPI_CH10,\n    PPI_CH11,\n    PPI_CH12,\n    PPI_CH13,\n    PPI_CH14,\n    PPI_CH15,\n    PPI_CH16,\n    PPI_CH17,\n    PPI_CH18,\n    PPI_CH19,\n    PPI_CH20,\n    PPI_CH21,\n    PPI_CH22,\n    PPI_CH23,\n    PPI_CH24,\n    PPI_CH25,\n    PPI_CH26,\n    PPI_CH27,\n    PPI_CH28,\n    PPI_CH29,\n    PPI_CH30,\n    PPI_CH31,\n\n    PPI_GROUP0,\n    PPI_GROUP1,\n    PPI_GROUP2,\n    PPI_GROUP3,\n    PPI_GROUP4,\n    PPI_GROUP5,\n\n    // IPC\n    IPC,\n\n    // GPIO port 0\n    P0_00,\n    P0_01,\n    P0_02,\n    P0_03,\n    P0_04,\n    P0_05,\n    P0_06,\n    P0_07,\n    P0_08,\n    P0_09,\n    P0_10,\n    P0_11,\n    P0_12,\n    P0_13,\n    P0_14,\n    P0_15,\n    P0_16,\n    P0_17,\n    P0_18,\n    P0_19,\n    P0_20,\n    P0_21,\n    P0_22,\n    P0_23,\n    P0_24,\n    P0_25,\n    P0_26,\n    P0_27,\n    P0_28,\n    P0_29,\n    P0_30,\n    P0_31,\n\n    // GPIO port 1\n    P1_00,\n    P1_01,\n    P1_02,\n    P1_03,\n    P1_04,\n    P1_05,\n    P1_06,\n    P1_07,\n    P1_08,\n    P1_09,\n    P1_10,\n    P1_11,\n    P1_12,\n    P1_13,\n    P1_14,\n    P1_15,\n\n    // Radio\n    RADIO,\n\n    // EGU\n    EGU0,\n\n    // TEMP\n    TEMP,\n}\n\nimpl_ipc!(IPC, IPC, IPC);\n\nimpl_uarte!(SERIAL0, UARTE0, SERIAL0);\nimpl_spim!(SERIAL0, SPIM0, SERIAL0);\nimpl_spis!(SERIAL0, SPIS0, SERIAL0);\nimpl_twim!(SERIAL0, TWIM0, SERIAL0);\nimpl_twis!(SERIAL0, TWIS0, SERIAL0);\n\nimpl_timer!(TIMER0, TIMER0, TIMER0);\nimpl_timer!(TIMER1, TIMER1, TIMER1);\nimpl_timer!(TIMER2, TIMER2, TIMER2);\n\nimpl_rtc!(RTC0, RTC0, RTC0);\n#[cfg(not(feature = \"time-driver-rtc1\"))]\nimpl_rtc!(RTC1, RTC1, RTC1);\n\nimpl_rng!(RNG, RNG, RNG);\n\nimpl_pin!(P0_00, 0, 0);\nimpl_pin!(P0_01, 0, 1);\nimpl_pin!(P0_02, 0, 2);\nimpl_pin!(P0_03, 0, 3);\nimpl_pin!(P0_04, 0, 4);\nimpl_pin!(P0_05, 0, 5);\nimpl_pin!(P0_06, 0, 6);\nimpl_pin!(P0_07, 0, 7);\nimpl_pin!(P0_08, 0, 8);\nimpl_pin!(P0_09, 0, 9);\nimpl_pin!(P0_10, 0, 10);\nimpl_pin!(P0_11, 0, 11);\nimpl_pin!(P0_12, 0, 12);\nimpl_pin!(P0_13, 0, 13);\nimpl_pin!(P0_14, 0, 14);\nimpl_pin!(P0_15, 0, 15);\nimpl_pin!(P0_16, 0, 16);\nimpl_pin!(P0_17, 0, 17);\nimpl_pin!(P0_18, 0, 18);\nimpl_pin!(P0_19, 0, 19);\nimpl_pin!(P0_20, 0, 20);\nimpl_pin!(P0_21, 0, 21);\nimpl_pin!(P0_22, 0, 22);\nimpl_pin!(P0_23, 0, 23);\nimpl_pin!(P0_24, 0, 24);\nimpl_pin!(P0_25, 0, 25);\nimpl_pin!(P0_26, 0, 26);\nimpl_pin!(P0_27, 0, 27);\nimpl_pin!(P0_28, 0, 28);\nimpl_pin!(P0_29, 0, 29);\nimpl_pin!(P0_30, 0, 30);\nimpl_pin!(P0_31, 0, 31);\n\nimpl_pin!(P1_00, 1, 0);\nimpl_pin!(P1_01, 1, 1);\nimpl_pin!(P1_02, 1, 2);\nimpl_pin!(P1_03, 1, 3);\nimpl_pin!(P1_04, 1, 4);\nimpl_pin!(P1_05, 1, 5);\nimpl_pin!(P1_06, 1, 6);\nimpl_pin!(P1_07, 1, 7);\nimpl_pin!(P1_08, 1, 8);\nimpl_pin!(P1_09, 1, 9);\nimpl_pin!(P1_10, 1, 10);\nimpl_pin!(P1_11, 1, 11);\nimpl_pin!(P1_12, 1, 12);\nimpl_pin!(P1_13, 1, 13);\nimpl_pin!(P1_14, 1, 14);\nimpl_pin!(P1_15, 1, 15);\n\nimpl_ppi_channel!(PPI_CH0, DPPIC, 0 => configurable);\nimpl_ppi_channel!(PPI_CH1, DPPIC, 1 => configurable);\nimpl_ppi_channel!(PPI_CH2, DPPIC, 2 => configurable);\nimpl_ppi_channel!(PPI_CH3, DPPIC, 3 => configurable);\nimpl_ppi_channel!(PPI_CH4, DPPIC, 4 => configurable);\nimpl_ppi_channel!(PPI_CH5, DPPIC, 5 => configurable);\nimpl_ppi_channel!(PPI_CH6, DPPIC, 6 => configurable);\nimpl_ppi_channel!(PPI_CH7, DPPIC, 7 => configurable);\nimpl_ppi_channel!(PPI_CH8, DPPIC, 8 => configurable);\nimpl_ppi_channel!(PPI_CH9, DPPIC, 9 => configurable);\nimpl_ppi_channel!(PPI_CH10, DPPIC, 10 => configurable);\nimpl_ppi_channel!(PPI_CH11, DPPIC, 11 => configurable);\nimpl_ppi_channel!(PPI_CH12, DPPIC, 12 => configurable);\nimpl_ppi_channel!(PPI_CH13, DPPIC, 13 => configurable);\nimpl_ppi_channel!(PPI_CH14, DPPIC, 14 => configurable);\nimpl_ppi_channel!(PPI_CH15, DPPIC, 15 => configurable);\nimpl_ppi_channel!(PPI_CH16, DPPIC, 16 => configurable);\nimpl_ppi_channel!(PPI_CH17, DPPIC, 17 => configurable);\nimpl_ppi_channel!(PPI_CH18, DPPIC, 18 => configurable);\nimpl_ppi_channel!(PPI_CH19, DPPIC, 19 => configurable);\nimpl_ppi_channel!(PPI_CH20, DPPIC, 20 => configurable);\nimpl_ppi_channel!(PPI_CH21, DPPIC, 21 => configurable);\nimpl_ppi_channel!(PPI_CH22, DPPIC, 22 => configurable);\nimpl_ppi_channel!(PPI_CH23, DPPIC, 23 => configurable);\nimpl_ppi_channel!(PPI_CH24, DPPIC, 24 => configurable);\nimpl_ppi_channel!(PPI_CH25, DPPIC, 25 => configurable);\nimpl_ppi_channel!(PPI_CH26, DPPIC, 26 => configurable);\nimpl_ppi_channel!(PPI_CH27, DPPIC, 27 => configurable);\nimpl_ppi_channel!(PPI_CH28, DPPIC, 28 => configurable);\nimpl_ppi_channel!(PPI_CH29, DPPIC, 29 => configurable);\nimpl_ppi_channel!(PPI_CH30, DPPIC, 30 => configurable);\nimpl_ppi_channel!(PPI_CH31, DPPIC, 31 => configurable);\n\nimpl_ppi_group!(PPI_GROUP0, DPPIC, 0);\nimpl_ppi_group!(PPI_GROUP1, DPPIC, 1);\nimpl_ppi_group!(PPI_GROUP2, DPPIC, 2);\nimpl_ppi_group!(PPI_GROUP3, DPPIC, 3);\nimpl_ppi_group!(PPI_GROUP4, DPPIC, 4);\nimpl_ppi_group!(PPI_GROUP5, DPPIC, 5);\n\nimpl_radio!(RADIO, RADIO, RADIO);\n\nimpl_egu!(EGU0, EGU0, EGU0);\n\nimpl_wdt!(WDT, WDT, WDT, 0);\n\nembassy_hal_internal::interrupt_mod!(\n    CLOCK_POWER,\n    RADIO,\n    RNG,\n    GPIOTE,\n    WDT,\n    TIMER0,\n    ECB,\n    AAR_CCM,\n    TEMP,\n    RTC0,\n    IPC,\n    SERIAL0,\n    EGU0,\n    RTC1,\n    TIMER1,\n    TIMER2,\n    SWI0,\n    SWI1,\n    SWI2,\n    SWI3,\n);\n"
  },
  {
    "path": "embassy-nrf/src/chips/nrf54l05_app.rs",
    "content": "/// Peripheral Access Crate\n#[allow(unused_imports)]\n#[rustfmt::skip]\npub mod pac {\n    pub use nrf_pac::*;\n\n    #[cfg(feature = \"_ns\")]\n    #[doc(no_inline)]\n    pub use nrf_pac::{\n        FICR_NS as FICR,\n        DPPIC00_NS as DPPIC00,\n        PPIB00_NS as PPIB00,\n        PPIB01_NS as PPIB01,\n        AAR00_NS as AAR00,\n        CCM00_NS as CCM00,\n        ECB00_NS as ECB00,\n        SPIM00_NS as SPIM00,\n        SPIS00_NS as SPIS00,\n        UARTE00_NS as UARTE00,\n        VPR00_NS as VPR00,\n        P2_NS as P2,\n        CTRLAP_NS as CTRLAP,\n        TAD_NS as TAD,\n        TIMER00_NS as TIMER00,\n        DPPIC10_NS as DPPIC10,\n        PPIB10_NS as PPIB10,\n        PPIB11_NS as PPIB11,\n        TIMER10_NS as TIMER10,\n        EGU10_NS as EGU10,\n        RADIO_NS as RADIO,\n        DPPIC20_NS as DPPIC20,\n        PPIB20_NS as PPIB20,\n        PPIB21_NS as PPIB21,\n        PPIB22_NS as PPIB22,\n        SPIM20_NS as SPIM20,\n        SPIS20_NS as SPIS20,\n        TWIM20_NS as TWIM20,\n        TWIS20_NS as TWIS20,\n        UARTE20_NS as UARTE20,\n        SPIM21_NS as SPIM21,\n        SPIS21_NS as SPIS21,\n        TWIM21_NS as TWIM21,\n        TWIS21_NS as TWIS21,\n        UARTE21_NS as UARTE21,\n        SPIM22_NS as SPIM22,\n        SPIS22_NS as SPIS22,\n        TWIM22_NS as TWIM22,\n        TWIS22_NS as TWIS22,\n        UARTE22_NS as UARTE22,\n        EGU20_NS as EGU20,\n        TIMER20_NS as TIMER20,\n        TIMER21_NS as TIMER21,\n        TIMER22_NS as TIMER22,\n        TIMER23_NS as TIMER23,\n        TIMER24_NS as TIMER24,\n        MEMCONF_NS as MEMCONF,\n        PDM20_NS as PDM20,\n        PDM21_NS as PDM21,\n        PWM20_NS as PWM20,\n        PWM21_NS as PWM21,\n        PWM22_NS as PWM22,\n        SAADC_NS as SAADC,\n        NFCT_NS as NFCT,\n        TEMP_NS as TEMP,\n        P1_NS as P1,\n        GPIOTE20_NS as GPIOTE20,\n        I2S20_NS as I2S20,\n        QDEC20_NS as QDEC20,\n        QDEC21_NS as QDEC21,\n        GRTC_NS as GRTC,\n        DPPIC30_NS as DPPIC30,\n        PPIB30_NS as PPIB30,\n        SPIM30_NS as SPIM30,\n        SPIS30_NS as SPIS30,\n        TWIM30_NS as TWIM30,\n        TWIS30_NS as TWIS30,\n        UARTE30_NS as UARTE30,\n        COMP_NS as COMP,\n        LPCOMP_NS as LPCOMP,\n        WDT31_NS as WDT31,\n        P0_NS as P0,\n        GPIOTE30_NS as GPIOTE30,\n        CLOCK_NS as CLOCK,\n        POWER_NS as POWER,\n        RESET_NS as RESET,\n        OSCILLATORS_NS as OSCILLATORS,\n        REGULATORS_NS as REGULATORS,\n        TPIU_NS as TPIU,\n        ETM_NS as ETM,\n    };\n\n    #[cfg(feature = \"_s\")]\n    #[doc(no_inline)]\n    pub use nrf_pac::{\n        FICR_NS as FICR,\n        SICR_S as SICR,\n        ICACHEDATA_S as ICACHEDATA,\n        ICACHEINFO_S as ICACHEINFO,\n        SWI00_S as SWI00,\n        SWI01_S as SWI01,\n        SWI02_S as SWI02,\n        SWI03_S as SWI03,\n        SPU00_S as SPU00,\n        MPC00_S as MPC00,\n        DPPIC00_S as DPPIC00,\n        PPIB00_S as PPIB00,\n        PPIB01_S as PPIB01,\n        KMU_S as KMU,\n        AAR00_S as AAR00,\n        CCM00_S as CCM00,\n        ECB00_S as ECB00,\n        CRACEN_S as CRACEN,\n        SPIM00_S as SPIM00,\n        SPIS00_S as SPIS00,\n        UARTE00_S as UARTE00,\n        GLITCHDET_S as GLITCHDET,\n        RRAMC_S as RRAMC,\n        VPR00_S as VPR00,\n        P2_S as P2,\n        CTRLAP_S as CTRLAP,\n        TAD_S as TAD,\n        TIMER00_S as TIMER00,\n        SPU10_S as SPU10,\n        DPPIC10_S as DPPIC10,\n        PPIB10_S as PPIB10,\n        PPIB11_S as PPIB11,\n        TIMER10_S as TIMER10,\n        EGU10_S as EGU10,\n        RADIO_S as RADIO,\n        SPU20_S as SPU20,\n        DPPIC20_S as DPPIC20,\n        PPIB20_S as PPIB20,\n        PPIB21_S as PPIB21,\n        PPIB22_S as PPIB22,\n        SPIM20_S as SPIM20,\n        SPIS20_S as SPIS20,\n        TWIM20_S as TWIM20,\n        TWIS20_S as TWIS20,\n        UARTE20_S as UARTE20,\n        SPIM21_S as SPIM21,\n        SPIS21_S as SPIS21,\n        TWIM21_S as TWIM21,\n        TWIS21_S as TWIS21,\n        UARTE21_S as UARTE21,\n        SPIM22_S as SPIM22,\n        SPIS22_S as SPIS22,\n        TWIM22_S as TWIM22,\n        TWIS22_S as TWIS22,\n        UARTE22_S as UARTE22,\n        EGU20_S as EGU20,\n        TIMER20_S as TIMER20,\n        TIMER21_S as TIMER21,\n        TIMER22_S as TIMER22,\n        TIMER23_S as TIMER23,\n        TIMER24_S as TIMER24,\n        MEMCONF_S as MEMCONF,\n        PDM20_S as PDM20,\n        PDM21_S as PDM21,\n        PWM20_S as PWM20,\n        PWM21_S as PWM21,\n        PWM22_S as PWM22,\n        SAADC_S as SAADC,\n        NFCT_S as NFCT,\n        TEMP_S as TEMP,\n        P1_S as P1,\n        GPIOTE20_S as GPIOTE20,\n        TAMPC_S as TAMPC,\n        I2S20_S as I2S20,\n        QDEC20_S as QDEC20,\n        QDEC21_S as QDEC21,\n        GRTC_S as GRTC,\n        SPU30_S as SPU30,\n        DPPIC30_S as DPPIC30,\n        PPIB30_S as PPIB30,\n        SPIM30_S as SPIM30,\n        SPIS30_S as SPIS30,\n        TWIM30_S as TWIM30,\n        TWIS30_S as TWIS30,\n        UARTE30_S as UARTE30,\n        COMP_S as COMP,\n        LPCOMP_S as LPCOMP,\n        WDT30_S as WDT30,\n        WDT31_S as WDT31,\n        P0_S as P0,\n        GPIOTE30_S as GPIOTE30,\n        CLOCK_S as CLOCK,\n        POWER_S as POWER,\n        RESET_S as RESET,\n        OSCILLATORS_S as OSCILLATORS,\n        REGULATORS_S as REGULATORS,\n        CRACENCORE_S as CRACENCORE,\n        CPUC_S as CPUC,\n        ICACHE_S as ICACHE,\n    };\n}\n\n/// The maximum buffer size that the EasyDMA can send/recv in one operation.\npub const EASY_DMA_SIZE: usize = (1 << 16) - 1;\npub const FORCE_COPY_BUFFER_SIZE: usize = 1024;\n\n// 1.5 MB NVM\n#[allow(unused)]\npub const FLASH_SIZE: usize = 1524 * 1024;\n\nembassy_hal_internal::peripherals! {\n    // PPI\n    PPI00_CH0,\n    PPI00_CH1,\n    PPI00_CH2,\n    PPI00_CH3,\n    PPI00_CH4,\n    PPI00_CH5,\n    PPI00_CH6,\n    PPI00_CH7,\n\n    PPI10_CH0,\n    PPI10_CH1,\n    PPI10_CH2,\n    PPI10_CH3,\n    PPI10_CH4,\n    PPI10_CH5,\n    PPI10_CH6,\n    PPI10_CH7,\n    PPI10_CH8,\n    PPI10_CH9,\n    PPI10_CH10,\n    PPI10_CH11,\n    PPI10_CH12,\n    PPI10_CH13,\n    PPI10_CH14,\n    PPI10_CH15,\n    PPI10_CH16,\n    PPI10_CH17,\n    PPI10_CH18,\n    PPI10_CH19,\n    PPI10_CH20,\n    PPI10_CH21,\n    PPI10_CH22,\n    PPI10_CH23,\n\n    PPI20_CH0,\n    PPI20_CH1,\n    PPI20_CH2,\n    PPI20_CH3,\n    PPI20_CH4,\n    PPI20_CH5,\n    PPI20_CH6,\n    PPI20_CH7,\n    PPI20_CH8,\n    PPI20_CH9,\n    PPI20_CH10,\n    PPI20_CH11,\n    PPI20_CH12,\n    PPI20_CH13,\n    PPI20_CH14,\n    PPI20_CH15,\n\n    PPI30_CH0,\n    PPI30_CH1,\n    PPI30_CH2,\n    PPI30_CH3,\n\n    PPI00_GROUP0,\n    PPI00_GROUP1,\n\n    PPI10_GROUP0,\n    PPI10_GROUP1,\n    PPI10_GROUP2,\n    PPI10_GROUP3,\n    PPI10_GROUP4,\n    PPI10_GROUP5,\n\n    PPI20_GROUP0,\n    PPI20_GROUP1,\n    PPI20_GROUP2,\n    PPI20_GROUP3,\n    PPI20_GROUP4,\n    PPI20_GROUP5,\n\n    PPI30_GROUP0,\n    PPI30_GROUP1,\n\n    // PPI BRIDGE channels\n    PPIB00_CH0,\n    PPIB00_CH1,\n    PPIB00_CH2,\n    PPIB00_CH3,\n    PPIB00_CH4,\n    PPIB00_CH5,\n    PPIB00_CH6,\n    PPIB00_CH7,\n\n    PPIB01_CH0,\n    PPIB01_CH1,\n    PPIB01_CH2,\n    PPIB01_CH3,\n    PPIB01_CH4,\n    PPIB01_CH5,\n    PPIB01_CH6,\n    PPIB01_CH7,\n\n    PPIB10_CH0,\n    PPIB10_CH1,\n    PPIB10_CH2,\n    PPIB10_CH3,\n    PPIB10_CH4,\n    PPIB10_CH5,\n    PPIB10_CH6,\n    PPIB10_CH7,\n\n    PPIB11_CH0,\n    PPIB11_CH1,\n    PPIB11_CH2,\n    PPIB11_CH3,\n    PPIB11_CH4,\n    PPIB11_CH5,\n    PPIB11_CH6,\n    PPIB11_CH7,\n    PPIB11_CH8,\n    PPIB11_CH9,\n    PPIB11_CH10,\n    PPIB11_CH11,\n    PPIB11_CH12,\n    PPIB11_CH13,\n    PPIB11_CH14,\n    PPIB11_CH15,\n\n    PPIB20_CH0,\n    PPIB20_CH1,\n    PPIB20_CH2,\n    PPIB20_CH3,\n    PPIB20_CH4,\n    PPIB20_CH5,\n    PPIB20_CH6,\n    PPIB20_CH7,\n\n    PPIB21_CH0,\n    PPIB21_CH1,\n    PPIB21_CH2,\n    PPIB21_CH3,\n    PPIB21_CH4,\n    PPIB21_CH5,\n    PPIB21_CH6,\n    PPIB21_CH7,\n    PPIB21_CH8,\n    PPIB21_CH9,\n    PPIB21_CH10,\n    PPIB21_CH11,\n    PPIB21_CH12,\n    PPIB21_CH13,\n    PPIB21_CH14,\n    PPIB21_CH15,\n\n    PPIB22_CH0,\n    PPIB22_CH1,\n    PPIB22_CH2,\n    PPIB22_CH3,\n\n    PPIB30_CH0,\n    PPIB30_CH1,\n    PPIB30_CH2,\n    PPIB30_CH3,\n\n    // Timers\n    TIMER00,\n    TIMER10,\n    TIMER20,\n    TIMER21,\n    TIMER22,\n    TIMER23,\n    TIMER24,\n\n    // GPIO port 0\n    P0_00,\n    P0_01,\n    P0_02,\n    P0_03,\n    P0_04,\n    P0_05,\n    P0_06,\n\n    // GPIO port 1\n    P1_00,\n    P1_01,\n    P1_02,\n    P1_03,\n    P1_04,\n    P1_05,\n    P1_06,\n    P1_07,\n    P1_08,\n    P1_09,\n    P1_10,\n    P1_11,\n    P1_12,\n    P1_13,\n    P1_14,\n    P1_15,\n    P1_16,\n\n\n    // GPIO port 2\n    P2_00,\n    P2_01,\n    P2_02,\n    P2_03,\n    P2_04,\n    P2_05,\n    P2_06,\n    P2_07,\n    P2_08,\n    P2_09,\n    P2_10,\n\n    // GRTC\n    GRTC_CH0,\n    #[cfg(not(feature = \"time-driver-grtc\"))]\n    GRTC_CH1,\n    GRTC_CH2,\n    GRTC_CH3,\n    GRTC_CH4,\n    GRTC_CH5,\n    GRTC_CH6,\n    GRTC_CH7,\n    GRTC_CH8,\n    GRTC_CH9,\n    GRTC_CH10,\n    GRTC_CH11,\n\n    // PWM\n    PWM20,\n    PWM21,\n    PWM22,\n\n    // SERIAL\n    SERIAL00,\n    SERIAL20,\n    SERIAL21,\n    SERIAL22,\n    SERIAL30,\n\n    // SAADC\n    SAADC,\n\n    // RADIO\n    RADIO,\n\n\n    // GPIOTE instances\n    GPIOTE20,\n    GPIOTE30,\n\n    // GPIOTE channels\n    GPIOTE20_CH0,\n    GPIOTE20_CH1,\n    GPIOTE20_CH2,\n    GPIOTE20_CH3,\n    GPIOTE20_CH4,\n    GPIOTE20_CH5,\n    GPIOTE20_CH6,\n    GPIOTE20_CH7,\n    GPIOTE30_CH0,\n    GPIOTE30_CH1,\n    GPIOTE30_CH2,\n    GPIOTE30_CH3,\n\n    // CRACEN\n    #[cfg(feature = \"_s\")]\n    CRACEN,\n\n    #[cfg(feature = \"_s\")]\n    // RRAMC\n    RRAMC,\n\n    // TEMP\n    TEMP,\n\n    // WDT\n    #[cfg(feature = \"_ns\")]\n    WDT,\n    #[cfg(feature = \"_s\")]\n    WDT0,\n    #[cfg(feature = \"_s\")]\n    WDT1,\n}\n\nimpl_pin!(P0_00, 0, 0);\nimpl_pin!(P0_01, 0, 1);\nimpl_pin!(P0_02, 0, 2);\nimpl_pin!(P0_03, 0, 3);\nimpl_pin!(P0_04, 0, 4);\nimpl_pin!(P0_05, 0, 5);\nimpl_pin!(P0_06, 0, 6);\n\nimpl_pin!(P1_00, 1, 0);\nimpl_pin!(P1_01, 1, 1);\nimpl_pin!(P1_02, 1, 2);\nimpl_pin!(P1_03, 1, 3);\nimpl_pin!(P1_04, 1, 4);\nimpl_pin!(P1_05, 1, 5);\nimpl_pin!(P1_06, 1, 6);\nimpl_pin!(P1_07, 1, 7);\nimpl_pin!(P1_08, 1, 8);\nimpl_pin!(P1_09, 1, 9);\nimpl_pin!(P1_10, 1, 10);\nimpl_pin!(P1_11, 1, 11);\nimpl_pin!(P1_12, 1, 12);\nimpl_pin!(P1_13, 1, 13);\nimpl_pin!(P1_14, 1, 14);\nimpl_pin!(P1_15, 1, 15);\nimpl_pin!(P1_16, 1, 16);\n\nimpl_pin!(P2_00, 2, 0);\nimpl_pin!(P2_01, 2, 1);\nimpl_pin!(P2_02, 2, 2);\nimpl_pin!(P2_03, 2, 3);\nimpl_pin!(P2_04, 2, 4);\nimpl_pin!(P2_05, 2, 5);\nimpl_pin!(P2_06, 2, 6);\nimpl_pin!(P2_07, 2, 7);\nimpl_pin!(P2_08, 2, 8);\nimpl_pin!(P2_09, 2, 9);\nimpl_pin!(P2_10, 2, 10);\n\ncfg_if::cfg_if! {\n    if #[cfg(feature = \"gpiote\")] {\n        impl_gpiote_pin!(P0_00, GPIOTE30);\n        impl_gpiote_pin!(P0_01, GPIOTE30);\n        impl_gpiote_pin!(P0_02, GPIOTE30);\n        impl_gpiote_pin!(P0_03, GPIOTE30);\n        impl_gpiote_pin!(P0_04, GPIOTE30);\n        impl_gpiote_pin!(P0_05, GPIOTE30);\n        impl_gpiote_pin!(P0_06, GPIOTE30);\n\n        impl_gpiote_pin!(P1_00, GPIOTE20);\n        impl_gpiote_pin!(P1_01, GPIOTE20);\n        impl_gpiote_pin!(P1_02, GPIOTE20);\n        impl_gpiote_pin!(P1_03, GPIOTE20);\n        impl_gpiote_pin!(P1_04, GPIOTE20);\n        impl_gpiote_pin!(P1_05, GPIOTE20);\n        impl_gpiote_pin!(P1_06, GPIOTE20);\n        impl_gpiote_pin!(P1_07, GPIOTE20);\n        impl_gpiote_pin!(P1_08, GPIOTE20);\n        impl_gpiote_pin!(P1_09, GPIOTE20);\n        impl_gpiote_pin!(P1_10, GPIOTE20);\n        impl_gpiote_pin!(P1_11, GPIOTE20);\n        impl_gpiote_pin!(P1_12, GPIOTE20);\n        impl_gpiote_pin!(P1_13, GPIOTE20);\n        impl_gpiote_pin!(P1_14, GPIOTE20);\n        impl_gpiote_pin!(P1_15, GPIOTE20);\n        impl_gpiote_pin!(P1_16, GPIOTE20);\n    }\n}\n\n#[cfg(feature = \"_ns\")]\nimpl_wdt!(WDT, WDT31, WDT31, 0);\n#[cfg(feature = \"_s\")]\nimpl_wdt!(WDT0, WDT31, WDT31, 0);\n#[cfg(feature = \"_s\")]\nimpl_wdt!(WDT1, WDT30, WDT30, 1);\n// DPPI00 channels\nimpl_ppi_channel!(PPI00_CH0, DPPIC00, 0 => configurable);\nimpl_ppi_channel!(PPI00_CH1, DPPIC00, 1 => configurable);\nimpl_ppi_channel!(PPI00_CH2, DPPIC00, 2 => configurable);\nimpl_ppi_channel!(PPI00_CH3, DPPIC00, 3 => configurable);\nimpl_ppi_channel!(PPI00_CH4, DPPIC00, 4 => configurable);\nimpl_ppi_channel!(PPI00_CH5, DPPIC00, 5 => configurable);\nimpl_ppi_channel!(PPI00_CH6, DPPIC00, 6 => configurable);\nimpl_ppi_channel!(PPI00_CH7, DPPIC00, 7 => configurable);\n\n// DPPI10 channels\nimpl_ppi_channel!(PPI10_CH0, DPPIC10, 0 => static);\n\n// DPPI20 channels\nimpl_ppi_channel!(PPI20_CH0, DPPIC20, 0 => configurable);\nimpl_ppi_channel!(PPI20_CH1, DPPIC20, 1 => configurable);\nimpl_ppi_channel!(PPI20_CH2, DPPIC20, 2 => configurable);\nimpl_ppi_channel!(PPI20_CH3, DPPIC20, 3 => configurable);\nimpl_ppi_channel!(PPI20_CH4, DPPIC20, 4 => configurable);\nimpl_ppi_channel!(PPI20_CH5, DPPIC20, 5 => configurable);\nimpl_ppi_channel!(PPI20_CH6, DPPIC20, 6 => configurable);\nimpl_ppi_channel!(PPI20_CH7, DPPIC20, 7 => configurable);\nimpl_ppi_channel!(PPI20_CH8, DPPIC20, 8 => configurable);\nimpl_ppi_channel!(PPI20_CH9, DPPIC20, 9 => configurable);\nimpl_ppi_channel!(PPI20_CH10, DPPIC20, 10 => configurable);\nimpl_ppi_channel!(PPI20_CH11, DPPIC20, 11 => configurable);\nimpl_ppi_channel!(PPI20_CH12, DPPIC20, 12 => configurable);\nimpl_ppi_channel!(PPI20_CH13, DPPIC20, 13 => configurable);\nimpl_ppi_channel!(PPI20_CH14, DPPIC20, 14 => configurable);\nimpl_ppi_channel!(PPI20_CH15, DPPIC20, 15 => configurable);\n\n// DPPI30 channels\nimpl_ppi_channel!(PPI30_CH0, DPPIC30, 0 => configurable);\nimpl_ppi_channel!(PPI30_CH1, DPPIC30, 1 => configurable);\nimpl_ppi_channel!(PPI30_CH2, DPPIC30, 2 => configurable);\nimpl_ppi_channel!(PPI30_CH3, DPPIC30, 3 => configurable);\n\n// DPPI00 groups\nimpl_ppi_group!(PPI00_GROUP0, DPPIC00, 0);\nimpl_ppi_group!(PPI00_GROUP1, DPPIC00, 1);\n\n// DPPI10 groups\nimpl_ppi_group!(PPI10_GROUP0, DPPIC10, 0);\n\n// DPPI20 groups\nimpl_ppi_group!(PPI20_GROUP0, DPPIC20, 0);\nimpl_ppi_group!(PPI20_GROUP1, DPPIC20, 1);\nimpl_ppi_group!(PPI20_GROUP2, DPPIC20, 2);\nimpl_ppi_group!(PPI20_GROUP3, DPPIC20, 3);\nimpl_ppi_group!(PPI20_GROUP4, DPPIC20, 4);\nimpl_ppi_group!(PPI20_GROUP5, DPPIC20, 5);\n\n// DPPI30 groups\nimpl_ppi_group!(PPI30_GROUP0, DPPIC30, 0);\nimpl_ppi_group!(PPI30_GROUP1, DPPIC30, 1);\n\nimpl_timer!(TIMER00, TIMER00, TIMER00);\nimpl_timer!(TIMER10, TIMER10, TIMER10);\nimpl_timer!(TIMER20, TIMER20, TIMER20);\nimpl_timer!(TIMER21, TIMER21, TIMER21);\nimpl_timer!(TIMER22, TIMER22, TIMER22);\nimpl_timer!(TIMER23, TIMER23, TIMER23);\nimpl_timer!(TIMER24, TIMER24, TIMER24);\n\nimpl_twim!(SERIAL20, TWIM20, SERIAL20);\nimpl_twim!(SERIAL21, TWIM21, SERIAL21);\nimpl_twim!(SERIAL22, TWIM22, SERIAL22);\nimpl_twim!(SERIAL30, TWIM30, SERIAL30);\n\nimpl_twis!(SERIAL20, TWIS20, SERIAL20);\nimpl_twis!(SERIAL21, TWIS21, SERIAL21);\nimpl_twis!(SERIAL22, TWIS22, SERIAL22);\nimpl_twis!(SERIAL30, TWIS30, SERIAL30);\n\nimpl_pwm!(PWM20, PWM20, PWM20);\nimpl_pwm!(PWM21, PWM21, PWM21);\nimpl_pwm!(PWM22, PWM22, PWM22);\n\n#[cfg(feature = \"_s\")]\nimpl_spim!(\n    SERIAL00,\n    SPIM00,\n    SERIAL00,\n    match pac::OSCILLATORS_S.pll().currentfreq().read().currentfreq() {\n        pac::oscillators::vals::Currentfreq::CK128M => 128_000_000,\n        pac::oscillators::vals::Currentfreq::CK64M => 64_000_000,\n        _ => unreachable!(),\n    }\n);\n#[cfg(feature = \"_ns\")]\nimpl_spim!(\n    SERIAL00,\n    SPIM00,\n    SERIAL00,\n    match pac::OSCILLATORS_NS.pll().currentfreq().read().currentfreq() {\n        pac::oscillators::vals::Currentfreq::CK128M => 128_000_000,\n        pac::oscillators::vals::Currentfreq::CK64M => 64_000_000,\n        _ => unreachable!(),\n    }\n);\nimpl_spim!(SERIAL20, SPIM20, SERIAL20, 16_000_000);\nimpl_spim!(SERIAL21, SPIM21, SERIAL21, 16_000_000);\nimpl_spim!(SERIAL22, SPIM22, SERIAL22, 16_000_000);\nimpl_spim!(SERIAL30, SPIM30, SERIAL30, 16_000_000);\n\nimpl_spis!(SERIAL20, SPIS20, SERIAL20);\nimpl_spis!(SERIAL21, SPIS21, SERIAL21);\nimpl_spis!(SERIAL22, SPIS22, SERIAL22);\nimpl_spis!(SERIAL30, SPIS30, SERIAL30);\n\nimpl_uarte!(SERIAL00, UARTE00, SERIAL00);\nimpl_uarte!(SERIAL20, UARTE20, SERIAL20);\nimpl_uarte!(SERIAL21, UARTE21, SERIAL21);\nimpl_uarte!(SERIAL22, UARTE22, SERIAL22);\nimpl_uarte!(SERIAL30, UARTE30, SERIAL30);\n\n// NB: SAADC uses \"pin\" abstraction, not \"AIN\"\nimpl_saadc_input!(P1_04, 1, 4);\nimpl_saadc_input!(P1_05, 1, 5);\nimpl_saadc_input!(P1_06, 1, 6);\nimpl_saadc_input!(P1_07, 1, 7);\nimpl_saadc_input!(P1_11, 1, 11);\nimpl_saadc_input!(P1_12, 1, 12);\nimpl_saadc_input!(P1_13, 1, 13);\nimpl_saadc_input!(P1_14, 1, 14);\n\n#[cfg(feature = \"_s\")]\nimpl_cracen!(CRACEN, CRACEN, CRACEN);\n\nembassy_hal_internal::interrupt_mod!(\n    SWI00,\n    SWI01,\n    SWI02,\n    SWI03,\n    SPU00,\n    MPC00,\n    AAR00_CCM00,\n    ECB00,\n    CRACEN,\n    SERIAL00,\n    RRAMC,\n    VPR00,\n    CTRLAP,\n    TIMER00,\n    SPU10,\n    TIMER10,\n    EGU10,\n    RADIO_0,\n    RADIO_1,\n    SPU20,\n    SERIAL20,\n    SERIAL21,\n    SERIAL22,\n    EGU20,\n    TIMER20,\n    TIMER21,\n    TIMER22,\n    TIMER23,\n    TIMER24,\n    PDM20,\n    PDM21,\n    PWM20,\n    PWM21,\n    PWM22,\n    SAADC,\n    NFCT,\n    TEMP,\n    GPIOTE20_0,\n    GPIOTE20_1,\n    TAMPC,\n    I2S20,\n    QDEC20,\n    QDEC21,\n    GRTC_0,\n    GRTC_1,\n    GRTC_2,\n    GRTC_3,\n    SPU30,\n    SERIAL30,\n    COMP_LPCOMP,\n    WDT30,\n    WDT31,\n    GPIOTE30_0,\n    GPIOTE30_1,\n    CLOCK_POWER,\n);\n"
  },
  {
    "path": "embassy-nrf/src/chips/nrf54l10_app.rs",
    "content": "/// Peripheral Access Crate\n#[allow(unused_imports)]\n#[rustfmt::skip]\npub mod pac {\n    pub use nrf_pac::*;\n\n    #[cfg(feature = \"_ns\")]\n    #[doc(no_inline)]\n    pub use nrf_pac::{\n        FICR_NS as FICR,\n        DPPIC00_NS as DPPIC00,\n        PPIB00_NS as PPIB00,\n        PPIB01_NS as PPIB01,\n        AAR00_NS as AAR00,\n        CCM00_NS as CCM00,\n        ECB00_NS as ECB00,\n        SPIM00_NS as SPIM00,\n        SPIS00_NS as SPIS00,\n        UARTE00_NS as UARTE00,\n        VPR00_NS as VPR00,\n        P2_NS as P2,\n        CTRLAP_NS as CTRLAP,\n        TAD_NS as TAD,\n        TIMER00_NS as TIMER00,\n        DPPIC10_NS as DPPIC10,\n        PPIB10_NS as PPIB10,\n        PPIB11_NS as PPIB11,\n        TIMER10_NS as TIMER10,\n        EGU10_NS as EGU10,\n        RADIO_NS as RADIO,\n        DPPIC20_NS as DPPIC20,\n        PPIB20_NS as PPIB20,\n        PPIB21_NS as PPIB21,\n        PPIB22_NS as PPIB22,\n        SPIM20_NS as SPIM20,\n        SPIS20_NS as SPIS20,\n        TWIM20_NS as TWIM20,\n        TWIS20_NS as TWIS20,\n        UARTE20_NS as UARTE20,\n        SPIM21_NS as SPIM21,\n        SPIS21_NS as SPIS21,\n        TWIM21_NS as TWIM21,\n        TWIS21_NS as TWIS21,\n        UARTE21_NS as UARTE21,\n        SPIM22_NS as SPIM22,\n        SPIS22_NS as SPIS22,\n        TWIM22_NS as TWIM22,\n        TWIS22_NS as TWIS22,\n        UARTE22_NS as UARTE22,\n        EGU20_NS as EGU20,\n        TIMER20_NS as TIMER20,\n        TIMER21_NS as TIMER21,\n        TIMER22_NS as TIMER22,\n        TIMER23_NS as TIMER23,\n        TIMER24_NS as TIMER24,\n        MEMCONF_NS as MEMCONF,\n        PDM20_NS as PDM20,\n        PDM21_NS as PDM21,\n        PWM20_NS as PWM20,\n        PWM21_NS as PWM21,\n        PWM22_NS as PWM22,\n        SAADC_NS as SAADC,\n        NFCT_NS as NFCT,\n        TEMP_NS as TEMP,\n        P1_NS as P1,\n        GPIOTE20_NS as GPIOTE20,\n        I2S20_NS as I2S20,\n        QDEC20_NS as QDEC20,\n        QDEC21_NS as QDEC21,\n        GRTC_NS as GRTC,\n        DPPIC30_NS as DPPIC30,\n        PPIB30_NS as PPIB30,\n        SPIM30_NS as SPIM30,\n        SPIS30_NS as SPIS30,\n        TWIM30_NS as TWIM30,\n        TWIS30_NS as TWIS30,\n        UARTE30_NS as UARTE30,\n        COMP_NS as COMP,\n        LPCOMP_NS as LPCOMP,\n        WDT31_NS as WDT31,\n        P0_NS as P0,\n        GPIOTE30_NS as GPIOTE30,\n        CLOCK_NS as CLOCK,\n        POWER_NS as POWER,\n        RESET_NS as RESET,\n        OSCILLATORS_NS as OSCILLATORS,\n        REGULATORS_NS as REGULATORS,\n        TPIU_NS as TPIU,\n        ETM_NS as ETM,\n    };\n\n    #[cfg(feature = \"_s\")]\n    #[doc(no_inline)]\n    pub use nrf_pac::{\n        FICR_NS as FICR,\n        SICR_S as SICR,\n        ICACHEDATA_S as ICACHEDATA,\n        ICACHEINFO_S as ICACHEINFO,\n        SWI00_S as SWI00,\n        SWI01_S as SWI01,\n        SWI02_S as SWI02,\n        SWI03_S as SWI03,\n        SPU00_S as SPU00,\n        MPC00_S as MPC00,\n        DPPIC00_S as DPPIC00,\n        PPIB00_S as PPIB00,\n        PPIB01_S as PPIB01,\n        KMU_S as KMU,\n        AAR00_S as AAR00,\n        CCM00_S as CCM00,\n        ECB00_S as ECB00,\n        CRACEN_S as CRACEN,\n        SPIM00_S as SPIM00,\n        SPIS00_S as SPIS00,\n        UARTE00_S as UARTE00,\n        GLITCHDET_S as GLITCHDET,\n        RRAMC_S as RRAMC,\n        VPR00_S as VPR00,\n        P2_S as P2,\n        CTRLAP_S as CTRLAP,\n        TAD_S as TAD,\n        TIMER00_S as TIMER00,\n        SPU10_S as SPU10,\n        DPPIC10_S as DPPIC10,\n        PPIB10_S as PPIB10,\n        PPIB11_S as PPIB11,\n        TIMER10_S as TIMER10,\n        EGU10_S as EGU10,\n        RADIO_S as RADIO,\n        SPU20_S as SPU20,\n        DPPIC20_S as DPPIC20,\n        PPIB20_S as PPIB20,\n        PPIB21_S as PPIB21,\n        PPIB22_S as PPIB22,\n        SPIM20_S as SPIM20,\n        SPIS20_S as SPIS20,\n        TWIM20_S as TWIM20,\n        TWIS20_S as TWIS20,\n        UARTE20_S as UARTE20,\n        SPIM21_S as SPIM21,\n        SPIS21_S as SPIS21,\n        TWIM21_S as TWIM21,\n        TWIS21_S as TWIS21,\n        UARTE21_S as UARTE21,\n        SPIM22_S as SPIM22,\n        SPIS22_S as SPIS22,\n        TWIM22_S as TWIM22,\n        TWIS22_S as TWIS22,\n        UARTE22_S as UARTE22,\n        EGU20_S as EGU20,\n        TIMER20_S as TIMER20,\n        TIMER21_S as TIMER21,\n        TIMER22_S as TIMER22,\n        TIMER23_S as TIMER23,\n        TIMER24_S as TIMER24,\n        MEMCONF_S as MEMCONF,\n        PDM20_S as PDM20,\n        PDM21_S as PDM21,\n        PWM20_S as PWM20,\n        PWM21_S as PWM21,\n        PWM22_S as PWM22,\n        SAADC_S as SAADC,\n        NFCT_S as NFCT,\n        TEMP_S as TEMP,\n        P1_S as P1,\n        GPIOTE20_S as GPIOTE20,\n        TAMPC_S as TAMPC,\n        I2S20_S as I2S20,\n        QDEC20_S as QDEC20,\n        QDEC21_S as QDEC21,\n        GRTC_S as GRTC,\n        SPU30_S as SPU30,\n        DPPIC30_S as DPPIC30,\n        PPIB30_S as PPIB30,\n        SPIM30_S as SPIM30,\n        SPIS30_S as SPIS30,\n        TWIM30_S as TWIM30,\n        TWIS30_S as TWIS30,\n        UARTE30_S as UARTE30,\n        COMP_S as COMP,\n        LPCOMP_S as LPCOMP,\n        WDT30_S as WDT30,\n        WDT31_S as WDT31,\n        P0_S as P0,\n        GPIOTE30_S as GPIOTE30,\n        CLOCK_S as CLOCK,\n        POWER_S as POWER,\n        RESET_S as RESET,\n        OSCILLATORS_S as OSCILLATORS,\n        REGULATORS_S as REGULATORS,\n        CRACENCORE_S as CRACENCORE,\n        CPUC_S as CPUC,\n        ICACHE_S as ICACHE,\n    };\n}\n\n/// The maximum buffer size that the EasyDMA can send/recv in one operation.\npub const EASY_DMA_SIZE: usize = (1 << 16) - 1;\npub const FORCE_COPY_BUFFER_SIZE: usize = 1024;\n\n// 1.5 MB NVM\n#[allow(unused)]\npub const FLASH_SIZE: usize = 1524 * 1024;\n\nembassy_hal_internal::peripherals! {\n    // PPI\n    PPI00_CH0,\n    PPI00_CH1,\n    PPI00_CH2,\n    PPI00_CH3,\n    PPI00_CH4,\n    PPI00_CH5,\n    PPI00_CH6,\n    PPI00_CH7,\n\n    PPI10_CH0,\n    PPI10_CH1,\n    PPI10_CH2,\n    PPI10_CH3,\n    PPI10_CH4,\n    PPI10_CH5,\n    PPI10_CH6,\n    PPI10_CH7,\n    PPI10_CH8,\n    PPI10_CH9,\n    PPI10_CH10,\n    PPI10_CH11,\n    PPI10_CH12,\n    PPI10_CH13,\n    PPI10_CH14,\n    PPI10_CH15,\n    PPI10_CH16,\n    PPI10_CH17,\n    PPI10_CH18,\n    PPI10_CH19,\n    PPI10_CH20,\n    PPI10_CH21,\n    PPI10_CH22,\n    PPI10_CH23,\n\n    PPI20_CH0,\n    PPI20_CH1,\n    PPI20_CH2,\n    PPI20_CH3,\n    PPI20_CH4,\n    PPI20_CH5,\n    PPI20_CH6,\n    PPI20_CH7,\n    PPI20_CH8,\n    PPI20_CH9,\n    PPI20_CH10,\n    PPI20_CH11,\n    PPI20_CH12,\n    PPI20_CH13,\n    PPI20_CH14,\n    PPI20_CH15,\n\n    PPI30_CH0,\n    PPI30_CH1,\n    PPI30_CH2,\n    PPI30_CH3,\n\n    PPI00_GROUP0,\n    PPI00_GROUP1,\n\n    PPI10_GROUP0,\n    PPI10_GROUP1,\n    PPI10_GROUP2,\n    PPI10_GROUP3,\n    PPI10_GROUP4,\n    PPI10_GROUP5,\n\n    PPI20_GROUP0,\n    PPI20_GROUP1,\n    PPI20_GROUP2,\n    PPI20_GROUP3,\n    PPI20_GROUP4,\n    PPI20_GROUP5,\n\n    PPI30_GROUP0,\n    PPI30_GROUP1,\n\n    // PPI BRIDGE channels\n    PPIB00_CH0,\n    PPIB00_CH1,\n    PPIB00_CH2,\n    PPIB00_CH3,\n    PPIB00_CH4,\n    PPIB00_CH5,\n    PPIB00_CH6,\n    PPIB00_CH7,\n\n    PPIB01_CH0,\n    PPIB01_CH1,\n    PPIB01_CH2,\n    PPIB01_CH3,\n    PPIB01_CH4,\n    PPIB01_CH5,\n    PPIB01_CH6,\n    PPIB01_CH7,\n\n    PPIB10_CH0,\n    PPIB10_CH1,\n    PPIB10_CH2,\n    PPIB10_CH3,\n    PPIB10_CH4,\n    PPIB10_CH5,\n    PPIB10_CH6,\n    PPIB10_CH7,\n\n    PPIB11_CH0,\n    PPIB11_CH1,\n    PPIB11_CH2,\n    PPIB11_CH3,\n    PPIB11_CH4,\n    PPIB11_CH5,\n    PPIB11_CH6,\n    PPIB11_CH7,\n    PPIB11_CH8,\n    PPIB11_CH9,\n    PPIB11_CH10,\n    PPIB11_CH11,\n    PPIB11_CH12,\n    PPIB11_CH13,\n    PPIB11_CH14,\n    PPIB11_CH15,\n\n    PPIB20_CH0,\n    PPIB20_CH1,\n    PPIB20_CH2,\n    PPIB20_CH3,\n    PPIB20_CH4,\n    PPIB20_CH5,\n    PPIB20_CH6,\n    PPIB20_CH7,\n\n    PPIB21_CH0,\n    PPIB21_CH1,\n    PPIB21_CH2,\n    PPIB21_CH3,\n    PPIB21_CH4,\n    PPIB21_CH5,\n    PPIB21_CH6,\n    PPIB21_CH7,\n    PPIB21_CH8,\n    PPIB21_CH9,\n    PPIB21_CH10,\n    PPIB21_CH11,\n    PPIB21_CH12,\n    PPIB21_CH13,\n    PPIB21_CH14,\n    PPIB21_CH15,\n\n    PPIB22_CH0,\n    PPIB22_CH1,\n    PPIB22_CH2,\n    PPIB22_CH3,\n\n    PPIB30_CH0,\n    PPIB30_CH1,\n    PPIB30_CH2,\n    PPIB30_CH3,\n\n    // Timers\n    TIMER00,\n    TIMER10,\n    TIMER20,\n    TIMER21,\n    TIMER22,\n    TIMER23,\n    TIMER24,\n\n    // GPIO port 0\n    P0_00,\n    P0_01,\n    P0_02,\n    P0_03,\n    P0_04,\n    P0_05,\n    P0_06,\n\n    // GPIO port 1\n    P1_00,\n    P1_01,\n    P1_02,\n    P1_03,\n    P1_04,\n    P1_05,\n    P1_06,\n    P1_07,\n    P1_08,\n    P1_09,\n    P1_10,\n    P1_11,\n    P1_12,\n    P1_13,\n    P1_14,\n    P1_15,\n    P1_16,\n\n\n    // GPIO port 2\n    P2_00,\n    P2_01,\n    P2_02,\n    P2_03,\n    P2_04,\n    P2_05,\n    P2_06,\n    P2_07,\n    P2_08,\n    P2_09,\n    P2_10,\n\n    // GRTC\n    GRTC_CH0,\n    #[cfg(not(feature = \"time-driver-grtc\"))]\n    GRTC_CH1,\n    GRTC_CH2,\n    GRTC_CH3,\n    GRTC_CH4,\n    GRTC_CH5,\n    GRTC_CH6,\n    GRTC_CH7,\n    GRTC_CH8,\n    GRTC_CH9,\n    GRTC_CH10,\n    GRTC_CH11,\n\n    // PWM\n    PWM20,\n    PWM21,\n    PWM22,\n\n    // SERIAL\n    SERIAL00,\n    SERIAL20,\n    SERIAL21,\n    SERIAL22,\n    SERIAL30,\n\n    // SAADC\n    SAADC,\n\n    // RADIO\n    RADIO,\n\n\n    // GPIOTE instances\n    GPIOTE20,\n    GPIOTE30,\n\n    // GPIOTE channels\n    GPIOTE20_CH0,\n    GPIOTE20_CH1,\n    GPIOTE20_CH2,\n    GPIOTE20_CH3,\n    GPIOTE20_CH4,\n    GPIOTE20_CH5,\n    GPIOTE20_CH6,\n    GPIOTE20_CH7,\n    GPIOTE30_CH0,\n    GPIOTE30_CH1,\n    GPIOTE30_CH2,\n    GPIOTE30_CH3,\n\n    // CRACEN\n    #[cfg(feature = \"_s\")]\n    CRACEN,\n\n    #[cfg(feature = \"_s\")]\n    // RRAMC\n    RRAMC,\n\n    // TEMP\n    TEMP,\n\n    // WDT\n    #[cfg(feature = \"_ns\")]\n    WDT,\n    #[cfg(feature = \"_s\")]\n    WDT0,\n    #[cfg(feature = \"_s\")]\n    WDT1,\n}\n\nimpl_pin!(P0_00, 0, 0);\nimpl_pin!(P0_01, 0, 1);\nimpl_pin!(P0_02, 0, 2);\nimpl_pin!(P0_03, 0, 3);\nimpl_pin!(P0_04, 0, 4);\nimpl_pin!(P0_05, 0, 5);\nimpl_pin!(P0_06, 0, 6);\n\nimpl_pin!(P1_00, 1, 0);\nimpl_pin!(P1_01, 1, 1);\nimpl_pin!(P1_02, 1, 2);\nimpl_pin!(P1_03, 1, 3);\nimpl_pin!(P1_04, 1, 4);\nimpl_pin!(P1_05, 1, 5);\nimpl_pin!(P1_06, 1, 6);\nimpl_pin!(P1_07, 1, 7);\nimpl_pin!(P1_08, 1, 8);\nimpl_pin!(P1_09, 1, 9);\nimpl_pin!(P1_10, 1, 10);\nimpl_pin!(P1_11, 1, 11);\nimpl_pin!(P1_12, 1, 12);\nimpl_pin!(P1_13, 1, 13);\nimpl_pin!(P1_14, 1, 14);\nimpl_pin!(P1_15, 1, 15);\nimpl_pin!(P1_16, 1, 16);\n\nimpl_pin!(P2_00, 2, 0);\nimpl_pin!(P2_01, 2, 1);\nimpl_pin!(P2_02, 2, 2);\nimpl_pin!(P2_03, 2, 3);\nimpl_pin!(P2_04, 2, 4);\nimpl_pin!(P2_05, 2, 5);\nimpl_pin!(P2_06, 2, 6);\nimpl_pin!(P2_07, 2, 7);\nimpl_pin!(P2_08, 2, 8);\nimpl_pin!(P2_09, 2, 9);\nimpl_pin!(P2_10, 2, 10);\n\ncfg_if::cfg_if! {\n    if #[cfg(feature = \"gpiote\")] {\n        impl_gpiote_pin!(P0_00, GPIOTE30);\n        impl_gpiote_pin!(P0_01, GPIOTE30);\n        impl_gpiote_pin!(P0_02, GPIOTE30);\n        impl_gpiote_pin!(P0_03, GPIOTE30);\n        impl_gpiote_pin!(P0_04, GPIOTE30);\n        impl_gpiote_pin!(P0_05, GPIOTE30);\n        impl_gpiote_pin!(P0_06, GPIOTE30);\n\n        impl_gpiote_pin!(P1_00, GPIOTE20);\n        impl_gpiote_pin!(P1_01, GPIOTE20);\n        impl_gpiote_pin!(P1_02, GPIOTE20);\n        impl_gpiote_pin!(P1_03, GPIOTE20);\n        impl_gpiote_pin!(P1_04, GPIOTE20);\n        impl_gpiote_pin!(P1_05, GPIOTE20);\n        impl_gpiote_pin!(P1_06, GPIOTE20);\n        impl_gpiote_pin!(P1_07, GPIOTE20);\n        impl_gpiote_pin!(P1_08, GPIOTE20);\n        impl_gpiote_pin!(P1_09, GPIOTE20);\n        impl_gpiote_pin!(P1_10, GPIOTE20);\n        impl_gpiote_pin!(P1_11, GPIOTE20);\n        impl_gpiote_pin!(P1_12, GPIOTE20);\n        impl_gpiote_pin!(P1_13, GPIOTE20);\n        impl_gpiote_pin!(P1_14, GPIOTE20);\n        impl_gpiote_pin!(P1_15, GPIOTE20);\n        impl_gpiote_pin!(P1_16, GPIOTE20);\n    }\n}\n\n#[cfg(feature = \"_ns\")]\nimpl_wdt!(WDT, WDT31, WDT31, 0);\n#[cfg(feature = \"_s\")]\nimpl_wdt!(WDT0, WDT31, WDT31, 0);\n#[cfg(feature = \"_s\")]\nimpl_wdt!(WDT1, WDT30, WDT30, 1);\n// DPPI00 channels\nimpl_ppi_channel!(PPI00_CH0, DPPIC00, 0 => configurable);\nimpl_ppi_channel!(PPI00_CH1, DPPIC00, 1 => configurable);\nimpl_ppi_channel!(PPI00_CH2, DPPIC00, 2 => configurable);\nimpl_ppi_channel!(PPI00_CH3, DPPIC00, 3 => configurable);\nimpl_ppi_channel!(PPI00_CH4, DPPIC00, 4 => configurable);\nimpl_ppi_channel!(PPI00_CH5, DPPIC00, 5 => configurable);\nimpl_ppi_channel!(PPI00_CH6, DPPIC00, 6 => configurable);\nimpl_ppi_channel!(PPI00_CH7, DPPIC00, 7 => configurable);\n\n// DPPI10 channels\nimpl_ppi_channel!(PPI10_CH0, DPPIC10, 0 => static);\n\n// DPPI20 channels\nimpl_ppi_channel!(PPI20_CH0, DPPIC20, 0 => configurable);\nimpl_ppi_channel!(PPI20_CH1, DPPIC20, 1 => configurable);\nimpl_ppi_channel!(PPI20_CH2, DPPIC20, 2 => configurable);\nimpl_ppi_channel!(PPI20_CH3, DPPIC20, 3 => configurable);\nimpl_ppi_channel!(PPI20_CH4, DPPIC20, 4 => configurable);\nimpl_ppi_channel!(PPI20_CH5, DPPIC20, 5 => configurable);\nimpl_ppi_channel!(PPI20_CH6, DPPIC20, 6 => configurable);\nimpl_ppi_channel!(PPI20_CH7, DPPIC20, 7 => configurable);\nimpl_ppi_channel!(PPI20_CH8, DPPIC20, 8 => configurable);\nimpl_ppi_channel!(PPI20_CH9, DPPIC20, 9 => configurable);\nimpl_ppi_channel!(PPI20_CH10, DPPIC20, 10 => configurable);\nimpl_ppi_channel!(PPI20_CH11, DPPIC20, 11 => configurable);\nimpl_ppi_channel!(PPI20_CH12, DPPIC20, 12 => configurable);\nimpl_ppi_channel!(PPI20_CH13, DPPIC20, 13 => configurable);\nimpl_ppi_channel!(PPI20_CH14, DPPIC20, 14 => configurable);\nimpl_ppi_channel!(PPI20_CH15, DPPIC20, 15 => configurable);\n\n// DPPI30 channels\nimpl_ppi_channel!(PPI30_CH0, DPPIC30, 0 => configurable);\nimpl_ppi_channel!(PPI30_CH1, DPPIC30, 1 => configurable);\nimpl_ppi_channel!(PPI30_CH2, DPPIC30, 2 => configurable);\nimpl_ppi_channel!(PPI30_CH3, DPPIC30, 3 => configurable);\n\n// DPPI00 groups\nimpl_ppi_group!(PPI00_GROUP0, DPPIC00, 0);\nimpl_ppi_group!(PPI00_GROUP1, DPPIC00, 1);\n\n// DPPI10 groups\nimpl_ppi_group!(PPI10_GROUP0, DPPIC10, 0);\n\n// DPPI20 groups\nimpl_ppi_group!(PPI20_GROUP0, DPPIC20, 0);\nimpl_ppi_group!(PPI20_GROUP1, DPPIC20, 1);\nimpl_ppi_group!(PPI20_GROUP2, DPPIC20, 2);\nimpl_ppi_group!(PPI20_GROUP3, DPPIC20, 3);\nimpl_ppi_group!(PPI20_GROUP4, DPPIC20, 4);\nimpl_ppi_group!(PPI20_GROUP5, DPPIC20, 5);\n\n// DPPI30 groups\nimpl_ppi_group!(PPI30_GROUP0, DPPIC30, 0);\nimpl_ppi_group!(PPI30_GROUP1, DPPIC30, 1);\n\nimpl_timer!(TIMER00, TIMER00, TIMER00);\nimpl_timer!(TIMER10, TIMER10, TIMER10);\nimpl_timer!(TIMER20, TIMER20, TIMER20);\nimpl_timer!(TIMER21, TIMER21, TIMER21);\nimpl_timer!(TIMER22, TIMER22, TIMER22);\nimpl_timer!(TIMER23, TIMER23, TIMER23);\nimpl_timer!(TIMER24, TIMER24, TIMER24);\n\nimpl_twim!(SERIAL20, TWIM20, SERIAL20);\nimpl_twim!(SERIAL21, TWIM21, SERIAL21);\nimpl_twim!(SERIAL22, TWIM22, SERIAL22);\nimpl_twim!(SERIAL30, TWIM30, SERIAL30);\n\nimpl_twis!(SERIAL20, TWIS20, SERIAL20);\nimpl_twis!(SERIAL21, TWIS21, SERIAL21);\nimpl_twis!(SERIAL22, TWIS22, SERIAL22);\nimpl_twis!(SERIAL30, TWIS30, SERIAL30);\n\nimpl_pwm!(PWM20, PWM20, PWM20);\nimpl_pwm!(PWM21, PWM21, PWM21);\nimpl_pwm!(PWM22, PWM22, PWM22);\n\n#[cfg(feature = \"_s\")]\nimpl_spim!(\n    SERIAL00,\n    SPIM00,\n    SERIAL00,\n    match pac::OSCILLATORS_S.pll().currentfreq().read().currentfreq() {\n        pac::oscillators::vals::Currentfreq::CK128M => 128_000_000,\n        pac::oscillators::vals::Currentfreq::CK64M => 64_000_000,\n        _ => unreachable!(),\n    }\n);\n#[cfg(feature = \"_ns\")]\nimpl_spim!(\n    SERIAL00,\n    SPIM00,\n    SERIAL00,\n    match pac::OSCILLATORS_NS.pll().currentfreq().read().currentfreq() {\n        pac::oscillators::vals::Currentfreq::CK128M => 128_000_000,\n        pac::oscillators::vals::Currentfreq::CK64M => 64_000_000,\n        _ => unreachable!(),\n    }\n);\nimpl_spim!(SERIAL20, SPIM20, SERIAL20, 16_000_000);\nimpl_spim!(SERIAL21, SPIM21, SERIAL21, 16_000_000);\nimpl_spim!(SERIAL22, SPIM22, SERIAL22, 16_000_000);\nimpl_spim!(SERIAL30, SPIM30, SERIAL30, 16_000_000);\n\nimpl_spis!(SERIAL20, SPIS20, SERIAL20);\nimpl_spis!(SERIAL21, SPIS21, SERIAL21);\nimpl_spis!(SERIAL22, SPIS22, SERIAL22);\nimpl_spis!(SERIAL30, SPIS30, SERIAL30);\n\nimpl_uarte!(SERIAL00, UARTE00, SERIAL00);\nimpl_uarte!(SERIAL20, UARTE20, SERIAL20);\nimpl_uarte!(SERIAL21, UARTE21, SERIAL21);\nimpl_uarte!(SERIAL22, UARTE22, SERIAL22);\nimpl_uarte!(SERIAL30, UARTE30, SERIAL30);\n\n// NB: SAADC uses \"pin\" abstraction, not \"AIN\"\nimpl_saadc_input!(P1_04, 1, 4);\nimpl_saadc_input!(P1_05, 1, 5);\nimpl_saadc_input!(P1_06, 1, 6);\nimpl_saadc_input!(P1_07, 1, 7);\nimpl_saadc_input!(P1_11, 1, 11);\nimpl_saadc_input!(P1_12, 1, 12);\nimpl_saadc_input!(P1_13, 1, 13);\nimpl_saadc_input!(P1_14, 1, 14);\n\n#[cfg(feature = \"_s\")]\nimpl_cracen!(CRACEN, CRACEN, CRACEN);\n\nembassy_hal_internal::interrupt_mod!(\n    SWI00,\n    SWI01,\n    SWI02,\n    SWI03,\n    SPU00,\n    MPC00,\n    AAR00_CCM00,\n    ECB00,\n    CRACEN,\n    SERIAL00,\n    RRAMC,\n    VPR00,\n    CTRLAP,\n    TIMER00,\n    SPU10,\n    TIMER10,\n    EGU10,\n    RADIO_0,\n    RADIO_1,\n    SPU20,\n    SERIAL20,\n    SERIAL21,\n    SERIAL22,\n    EGU20,\n    TIMER20,\n    TIMER21,\n    TIMER22,\n    TIMER23,\n    TIMER24,\n    PDM20,\n    PDM21,\n    PWM20,\n    PWM21,\n    PWM22,\n    SAADC,\n    NFCT,\n    TEMP,\n    GPIOTE20_0,\n    GPIOTE20_1,\n    TAMPC,\n    I2S20,\n    QDEC20,\n    QDEC21,\n    GRTC_0,\n    GRTC_1,\n    GRTC_2,\n    GRTC_3,\n    SPU30,\n    SERIAL30,\n    COMP_LPCOMP,\n    WDT30,\n    WDT31,\n    GPIOTE30_0,\n    GPIOTE30_1,\n    CLOCK_POWER,\n);\n"
  },
  {
    "path": "embassy-nrf/src/chips/nrf54l15_app.rs",
    "content": "/// Peripheral Access Crate\n#[allow(unused_imports)]\n#[rustfmt::skip]\npub mod pac {\n    pub use nrf_pac::*;\n\n    #[cfg(feature = \"_ns\")]\n    #[doc(no_inline)]\n    pub use nrf_pac::{\n        FICR_NS as FICR,\n        DPPIC00_NS as DPPIC00,\n        PPIB00_NS as PPIB00,\n        PPIB01_NS as PPIB01,\n        AAR00_NS as AAR00,\n        CCM00_NS as CCM00,\n        ECB00_NS as ECB00,\n        SPIM00_NS as SPIM00,\n        SPIS00_NS as SPIS00,\n        UARTE00_NS as UARTE00,\n        VPR00_NS as VPR00,\n        P2_NS as P2,\n        CTRLAP_NS as CTRLAP,\n        TAD_NS as TAD,\n        TIMER00_NS as TIMER00,\n        DPPIC10_NS as DPPIC10,\n        PPIB10_NS as PPIB10,\n        PPIB11_NS as PPIB11,\n        TIMER10_NS as TIMER10,\n        EGU10_NS as EGU10,\n        RADIO_NS as RADIO,\n        DPPIC20_NS as DPPIC20,\n        PPIB20_NS as PPIB20,\n        PPIB21_NS as PPIB21,\n        PPIB22_NS as PPIB22,\n        SPIM20_NS as SPIM20,\n        SPIS20_NS as SPIS20,\n        TWIM20_NS as TWIM20,\n        TWIS20_NS as TWIS20,\n        UARTE20_NS as UARTE20,\n        SPIM21_NS as SPIM21,\n        SPIS21_NS as SPIS21,\n        TWIM21_NS as TWIM21,\n        TWIS21_NS as TWIS21,\n        UARTE21_NS as UARTE21,\n        SPIM22_NS as SPIM22,\n        SPIS22_NS as SPIS22,\n        TWIM22_NS as TWIM22,\n        TWIS22_NS as TWIS22,\n        UARTE22_NS as UARTE22,\n        EGU20_NS as EGU20,\n        TIMER20_NS as TIMER20,\n        TIMER21_NS as TIMER21,\n        TIMER22_NS as TIMER22,\n        TIMER23_NS as TIMER23,\n        TIMER24_NS as TIMER24,\n        MEMCONF_NS as MEMCONF,\n        PDM20_NS as PDM20,\n        PDM21_NS as PDM21,\n        PWM20_NS as PWM20,\n        PWM21_NS as PWM21,\n        PWM22_NS as PWM22,\n        SAADC_NS as SAADC,\n        NFCT_NS as NFCT,\n        TEMP_NS as TEMP,\n        P1_NS as P1,\n        GPIOTE20_NS as GPIOTE20,\n        I2S20_NS as I2S20,\n        QDEC20_NS as QDEC20,\n        QDEC21_NS as QDEC21,\n        GRTC_NS as GRTC,\n        DPPIC30_NS as DPPIC30,\n        PPIB30_NS as PPIB30,\n        SPIM30_NS as SPIM30,\n        SPIS30_NS as SPIS30,\n        TWIM30_NS as TWIM30,\n        TWIS30_NS as TWIS30,\n        UARTE30_NS as UARTE30,\n        COMP_NS as COMP,\n        LPCOMP_NS as LPCOMP,\n        WDT31_NS as WDT31,\n        P0_NS as P0,\n        GPIOTE30_NS as GPIOTE30,\n        CLOCK_NS as CLOCK,\n        POWER_NS as POWER,\n        RESET_NS as RESET,\n        OSCILLATORS_NS as OSCILLATORS,\n        REGULATORS_NS as REGULATORS,\n        TPIU_NS as TPIU,\n        ETM_NS as ETM,\n    };\n\n    #[cfg(feature = \"_s\")]\n    #[doc(no_inline)]\n    pub use nrf_pac::{\n        FICR_NS as FICR,\n        SICR_S as SICR,\n        ICACHEDATA_S as ICACHEDATA,\n        ICACHEINFO_S as ICACHEINFO,\n        SWI00_S as SWI00,\n        SWI01_S as SWI01,\n        SWI02_S as SWI02,\n        SWI03_S as SWI03,\n        SPU00_S as SPU00,\n        MPC00_S as MPC00,\n        DPPIC00_S as DPPIC00,\n        PPIB00_S as PPIB00,\n        PPIB01_S as PPIB01,\n        KMU_S as KMU,\n        AAR00_S as AAR00,\n        CCM00_S as CCM00,\n        ECB00_S as ECB00,\n        CRACEN_S as CRACEN,\n        SPIM00_S as SPIM00,\n        SPIS00_S as SPIS00,\n        UARTE00_S as UARTE00,\n        GLITCHDET_S as GLITCHDET,\n        RRAMC_S as RRAMC,\n        VPR00_S as VPR00,\n        P2_S as P2,\n        CTRLAP_S as CTRLAP,\n        TAD_S as TAD,\n        TIMER00_S as TIMER00,\n        SPU10_S as SPU10,\n        DPPIC10_S as DPPIC10,\n        PPIB10_S as PPIB10,\n        PPIB11_S as PPIB11,\n        TIMER10_S as TIMER10,\n        EGU10_S as EGU10,\n        RADIO_S as RADIO,\n        SPU20_S as SPU20,\n        DPPIC20_S as DPPIC20,\n        PPIB20_S as PPIB20,\n        PPIB21_S as PPIB21,\n        PPIB22_S as PPIB22,\n        SPIM20_S as SPIM20,\n        SPIS20_S as SPIS20,\n        TWIM20_S as TWIM20,\n        TWIS20_S as TWIS20,\n        UARTE20_S as UARTE20,\n        SPIM21_S as SPIM21,\n        SPIS21_S as SPIS21,\n        TWIM21_S as TWIM21,\n        TWIS21_S as TWIS21,\n        UARTE21_S as UARTE21,\n        SPIM22_S as SPIM22,\n        SPIS22_S as SPIS22,\n        TWIM22_S as TWIM22,\n        TWIS22_S as TWIS22,\n        UARTE22_S as UARTE22,\n        EGU20_S as EGU20,\n        TIMER20_S as TIMER20,\n        TIMER21_S as TIMER21,\n        TIMER22_S as TIMER22,\n        TIMER23_S as TIMER23,\n        TIMER24_S as TIMER24,\n        MEMCONF_S as MEMCONF,\n        PDM20_S as PDM20,\n        PDM21_S as PDM21,\n        PWM20_S as PWM20,\n        PWM21_S as PWM21,\n        PWM22_S as PWM22,\n        SAADC_S as SAADC,\n        NFCT_S as NFCT,\n        TEMP_S as TEMP,\n        P1_S as P1,\n        GPIOTE20_S as GPIOTE20,\n        TAMPC_S as TAMPC,\n        I2S20_S as I2S20,\n        QDEC20_S as QDEC20,\n        QDEC21_S as QDEC21,\n        GRTC_S as GRTC,\n        SPU30_S as SPU30,\n        DPPIC30_S as DPPIC30,\n        PPIB30_S as PPIB30,\n        SPIM30_S as SPIM30,\n        SPIS30_S as SPIS30,\n        TWIM30_S as TWIM30,\n        TWIS30_S as TWIS30,\n        UARTE30_S as UARTE30,\n        COMP_S as COMP,\n        LPCOMP_S as LPCOMP,\n        WDT30_S as WDT30,\n        WDT31_S as WDT31,\n        P0_S as P0,\n        GPIOTE30_S as GPIOTE30,\n        CLOCK_S as CLOCK,\n        POWER_S as POWER,\n        RESET_S as RESET,\n        OSCILLATORS_S as OSCILLATORS,\n        REGULATORS_S as REGULATORS,\n        CRACENCORE_S as CRACENCORE,\n        CPUC_S as CPUC,\n        ICACHE_S as ICACHE,\n    };\n}\n\n/// The maximum buffer size that the EasyDMA can send/recv in one operation.\npub const EASY_DMA_SIZE: usize = (1 << 16) - 1;\npub const FORCE_COPY_BUFFER_SIZE: usize = 1024;\n\n// 1.5 MB NVM\n#[allow(unused)]\npub const FLASH_SIZE: usize = 1524 * 1024;\n\nembassy_hal_internal::peripherals! {\n    // PPI\n    PPI00_CH0,\n    PPI00_CH1,\n    PPI00_CH2,\n    PPI00_CH3,\n    PPI00_CH4,\n    PPI00_CH5,\n    PPI00_CH6,\n    PPI00_CH7,\n\n    PPI10_CH0,\n    PPI10_CH1,\n    PPI10_CH2,\n    PPI10_CH3,\n    PPI10_CH4,\n    PPI10_CH5,\n    PPI10_CH6,\n    PPI10_CH7,\n    PPI10_CH8,\n    PPI10_CH9,\n    PPI10_CH10,\n    PPI10_CH11,\n    PPI10_CH12,\n    PPI10_CH13,\n    PPI10_CH14,\n    PPI10_CH15,\n    PPI10_CH16,\n    PPI10_CH17,\n    PPI10_CH18,\n    PPI10_CH19,\n    PPI10_CH20,\n    PPI10_CH21,\n    PPI10_CH22,\n    PPI10_CH23,\n\n    PPI20_CH0,\n    PPI20_CH1,\n    PPI20_CH2,\n    PPI20_CH3,\n    PPI20_CH4,\n    PPI20_CH5,\n    PPI20_CH6,\n    PPI20_CH7,\n    PPI20_CH8,\n    PPI20_CH9,\n    PPI20_CH10,\n    PPI20_CH11,\n    PPI20_CH12,\n    PPI20_CH13,\n    PPI20_CH14,\n    PPI20_CH15,\n\n    PPI30_CH0,\n    PPI30_CH1,\n    PPI30_CH2,\n    PPI30_CH3,\n\n    PPI00_GROUP0,\n    PPI00_GROUP1,\n\n    PPI10_GROUP0,\n    PPI10_GROUP1,\n    PPI10_GROUP2,\n    PPI10_GROUP3,\n    PPI10_GROUP4,\n    PPI10_GROUP5,\n\n    PPI20_GROUP0,\n    PPI20_GROUP1,\n    PPI20_GROUP2,\n    PPI20_GROUP3,\n    PPI20_GROUP4,\n    PPI20_GROUP5,\n\n    PPI30_GROUP0,\n    PPI30_GROUP1,\n\n    // PPI BRIDGE channels\n    PPIB00_CH0,\n    PPIB00_CH1,\n    PPIB00_CH2,\n    PPIB00_CH3,\n    PPIB00_CH4,\n    PPIB00_CH5,\n    PPIB00_CH6,\n    PPIB00_CH7,\n\n    PPIB01_CH0,\n    PPIB01_CH1,\n    PPIB01_CH2,\n    PPIB01_CH3,\n    PPIB01_CH4,\n    PPIB01_CH5,\n    PPIB01_CH6,\n    PPIB01_CH7,\n\n    PPIB10_CH0,\n    PPIB10_CH1,\n    PPIB10_CH2,\n    PPIB10_CH3,\n    PPIB10_CH4,\n    PPIB10_CH5,\n    PPIB10_CH6,\n    PPIB10_CH7,\n\n    PPIB11_CH0,\n    PPIB11_CH1,\n    PPIB11_CH2,\n    PPIB11_CH3,\n    PPIB11_CH4,\n    PPIB11_CH5,\n    PPIB11_CH6,\n    PPIB11_CH7,\n    PPIB11_CH8,\n    PPIB11_CH9,\n    PPIB11_CH10,\n    PPIB11_CH11,\n    PPIB11_CH12,\n    PPIB11_CH13,\n    PPIB11_CH14,\n    PPIB11_CH15,\n\n    PPIB20_CH0,\n    PPIB20_CH1,\n    PPIB20_CH2,\n    PPIB20_CH3,\n    PPIB20_CH4,\n    PPIB20_CH5,\n    PPIB20_CH6,\n    PPIB20_CH7,\n\n    PPIB21_CH0,\n    PPIB21_CH1,\n    PPIB21_CH2,\n    PPIB21_CH3,\n    PPIB21_CH4,\n    PPIB21_CH5,\n    PPIB21_CH6,\n    PPIB21_CH7,\n    PPIB21_CH8,\n    PPIB21_CH9,\n    PPIB21_CH10,\n    PPIB21_CH11,\n    PPIB21_CH12,\n    PPIB21_CH13,\n    PPIB21_CH14,\n    PPIB21_CH15,\n\n    PPIB22_CH0,\n    PPIB22_CH1,\n    PPIB22_CH2,\n    PPIB22_CH3,\n\n    PPIB30_CH0,\n    PPIB30_CH1,\n    PPIB30_CH2,\n    PPIB30_CH3,\n\n    // Timers\n    TIMER00,\n    TIMER10,\n    TIMER20,\n    TIMER21,\n    TIMER22,\n    TIMER23,\n    TIMER24,\n\n    // GPIO port 0\n    P0_00,\n    P0_01,\n    P0_02,\n    P0_03,\n    P0_04,\n    P0_05,\n    P0_06,\n\n    // GPIO port 1\n    P1_00,\n    P1_01,\n    P1_02,\n    P1_03,\n    P1_04,\n    P1_05,\n    P1_06,\n    P1_07,\n    P1_08,\n    P1_09,\n    P1_10,\n    P1_11,\n    P1_12,\n    P1_13,\n    P1_14,\n    P1_15,\n    P1_16,\n\n\n    // GPIO port 2\n    P2_00,\n    P2_01,\n    P2_02,\n    P2_03,\n    P2_04,\n    P2_05,\n    P2_06,\n    P2_07,\n    P2_08,\n    P2_09,\n    P2_10,\n\n    // GRTC\n    GRTC_CH0,\n    #[cfg(not(feature = \"time-driver-grtc\"))]\n    GRTC_CH1,\n    GRTC_CH2,\n    GRTC_CH3,\n    GRTC_CH4,\n    GRTC_CH5,\n    GRTC_CH6,\n    GRTC_CH7,\n    GRTC_CH8,\n    GRTC_CH9,\n    GRTC_CH10,\n    GRTC_CH11,\n\n    // PWM\n    PWM20,\n    PWM21,\n    PWM22,\n\n    // SERIAL\n    SERIAL00,\n    SERIAL20,\n    SERIAL21,\n    SERIAL22,\n    SERIAL30,\n\n    // SAADC\n    SAADC,\n\n    // RADIO\n    RADIO,\n\n\n    // GPIOTE instances\n    GPIOTE20,\n    GPIOTE30,\n\n    // GPIOTE channels\n    GPIOTE20_CH0,\n    GPIOTE20_CH1,\n    GPIOTE20_CH2,\n    GPIOTE20_CH3,\n    GPIOTE20_CH4,\n    GPIOTE20_CH5,\n    GPIOTE20_CH6,\n    GPIOTE20_CH7,\n    GPIOTE30_CH0,\n    GPIOTE30_CH1,\n    GPIOTE30_CH2,\n    GPIOTE30_CH3,\n\n    // CRACEN\n    #[cfg(feature = \"_s\")]\n    CRACEN,\n\n    #[cfg(feature = \"_s\")]\n    // RRAMC\n    RRAMC,\n\n    // TEMP\n    TEMP,\n\n    // WDT\n    #[cfg(feature = \"_ns\")]\n    WDT,\n    #[cfg(feature = \"_s\")]\n    WDT0,\n    #[cfg(feature = \"_s\")]\n    WDT1,\n}\n\nimpl_pin!(P0_00, 0, 0);\nimpl_pin!(P0_01, 0, 1);\nimpl_pin!(P0_02, 0, 2);\nimpl_pin!(P0_03, 0, 3);\nimpl_pin!(P0_04, 0, 4);\nimpl_pin!(P0_05, 0, 5);\nimpl_pin!(P0_06, 0, 6);\n\nimpl_pin!(P1_00, 1, 0);\nimpl_pin!(P1_01, 1, 1);\nimpl_pin!(P1_02, 1, 2);\nimpl_pin!(P1_03, 1, 3);\nimpl_pin!(P1_04, 1, 4);\nimpl_pin!(P1_05, 1, 5);\nimpl_pin!(P1_06, 1, 6);\nimpl_pin!(P1_07, 1, 7);\nimpl_pin!(P1_08, 1, 8);\nimpl_pin!(P1_09, 1, 9);\nimpl_pin!(P1_10, 1, 10);\nimpl_pin!(P1_11, 1, 11);\nimpl_pin!(P1_12, 1, 12);\nimpl_pin!(P1_13, 1, 13);\nimpl_pin!(P1_14, 1, 14);\nimpl_pin!(P1_15, 1, 15);\nimpl_pin!(P1_16, 1, 16);\n\nimpl_pin!(P2_00, 2, 0);\nimpl_pin!(P2_01, 2, 1);\nimpl_pin!(P2_02, 2, 2);\nimpl_pin!(P2_03, 2, 3);\nimpl_pin!(P2_04, 2, 4);\nimpl_pin!(P2_05, 2, 5);\nimpl_pin!(P2_06, 2, 6);\nimpl_pin!(P2_07, 2, 7);\nimpl_pin!(P2_08, 2, 8);\nimpl_pin!(P2_09, 2, 9);\nimpl_pin!(P2_10, 2, 10);\n\ncfg_if::cfg_if! {\n    if #[cfg(feature = \"gpiote\")] {\n        impl_gpiote_pin!(P0_00, GPIOTE30);\n        impl_gpiote_pin!(P0_01, GPIOTE30);\n        impl_gpiote_pin!(P0_02, GPIOTE30);\n        impl_gpiote_pin!(P0_03, GPIOTE30);\n        impl_gpiote_pin!(P0_04, GPIOTE30);\n        impl_gpiote_pin!(P0_05, GPIOTE30);\n        impl_gpiote_pin!(P0_06, GPIOTE30);\n\n        impl_gpiote_pin!(P1_00, GPIOTE20);\n        impl_gpiote_pin!(P1_01, GPIOTE20);\n        impl_gpiote_pin!(P1_02, GPIOTE20);\n        impl_gpiote_pin!(P1_03, GPIOTE20);\n        impl_gpiote_pin!(P1_04, GPIOTE20);\n        impl_gpiote_pin!(P1_05, GPIOTE20);\n        impl_gpiote_pin!(P1_06, GPIOTE20);\n        impl_gpiote_pin!(P1_07, GPIOTE20);\n        impl_gpiote_pin!(P1_08, GPIOTE20);\n        impl_gpiote_pin!(P1_09, GPIOTE20);\n        impl_gpiote_pin!(P1_10, GPIOTE20);\n        impl_gpiote_pin!(P1_11, GPIOTE20);\n        impl_gpiote_pin!(P1_12, GPIOTE20);\n        impl_gpiote_pin!(P1_13, GPIOTE20);\n        impl_gpiote_pin!(P1_14, GPIOTE20);\n        impl_gpiote_pin!(P1_15, GPIOTE20);\n        impl_gpiote_pin!(P1_16, GPIOTE20);\n    }\n}\n\n#[cfg(feature = \"_ns\")]\nimpl_wdt!(WDT, WDT31, WDT31, 0);\n#[cfg(feature = \"_s\")]\nimpl_wdt!(WDT0, WDT31, WDT31, 0);\n#[cfg(feature = \"_s\")]\nimpl_wdt!(WDT1, WDT30, WDT30, 1);\n// DPPI00 channels\nimpl_ppi_channel!(PPI00_CH0, DPPIC00, 0 => configurable);\nimpl_ppi_channel!(PPI00_CH1, DPPIC00, 1 => configurable);\nimpl_ppi_channel!(PPI00_CH2, DPPIC00, 2 => configurable);\nimpl_ppi_channel!(PPI00_CH3, DPPIC00, 3 => configurable);\nimpl_ppi_channel!(PPI00_CH4, DPPIC00, 4 => configurable);\nimpl_ppi_channel!(PPI00_CH5, DPPIC00, 5 => configurable);\nimpl_ppi_channel!(PPI00_CH6, DPPIC00, 6 => configurable);\nimpl_ppi_channel!(PPI00_CH7, DPPIC00, 7 => configurable);\n\n// DPPI10 channels\nimpl_ppi_channel!(PPI10_CH0, DPPIC10, 0 => static);\n\n// DPPI20 channels\nimpl_ppi_channel!(PPI20_CH0, DPPIC20, 0 => configurable);\nimpl_ppi_channel!(PPI20_CH1, DPPIC20, 1 => configurable);\nimpl_ppi_channel!(PPI20_CH2, DPPIC20, 2 => configurable);\nimpl_ppi_channel!(PPI20_CH3, DPPIC20, 3 => configurable);\nimpl_ppi_channel!(PPI20_CH4, DPPIC20, 4 => configurable);\nimpl_ppi_channel!(PPI20_CH5, DPPIC20, 5 => configurable);\nimpl_ppi_channel!(PPI20_CH6, DPPIC20, 6 => configurable);\nimpl_ppi_channel!(PPI20_CH7, DPPIC20, 7 => configurable);\nimpl_ppi_channel!(PPI20_CH8, DPPIC20, 8 => configurable);\nimpl_ppi_channel!(PPI20_CH9, DPPIC20, 9 => configurable);\nimpl_ppi_channel!(PPI20_CH10, DPPIC20, 10 => configurable);\nimpl_ppi_channel!(PPI20_CH11, DPPIC20, 11 => configurable);\nimpl_ppi_channel!(PPI20_CH12, DPPIC20, 12 => configurable);\nimpl_ppi_channel!(PPI20_CH13, DPPIC20, 13 => configurable);\nimpl_ppi_channel!(PPI20_CH14, DPPIC20, 14 => configurable);\nimpl_ppi_channel!(PPI20_CH15, DPPIC20, 15 => configurable);\n\n// DPPI30 channels\nimpl_ppi_channel!(PPI30_CH0, DPPIC30, 0 => configurable);\nimpl_ppi_channel!(PPI30_CH1, DPPIC30, 1 => configurable);\nimpl_ppi_channel!(PPI30_CH2, DPPIC30, 2 => configurable);\nimpl_ppi_channel!(PPI30_CH3, DPPIC30, 3 => configurable);\n\n// DPPI00 groups\nimpl_ppi_group!(PPI00_GROUP0, DPPIC00, 0);\nimpl_ppi_group!(PPI00_GROUP1, DPPIC00, 1);\n\n// DPPI10 groups\nimpl_ppi_group!(PPI10_GROUP0, DPPIC10, 0);\n\n// DPPI20 groups\nimpl_ppi_group!(PPI20_GROUP0, DPPIC20, 0);\nimpl_ppi_group!(PPI20_GROUP1, DPPIC20, 1);\nimpl_ppi_group!(PPI20_GROUP2, DPPIC20, 2);\nimpl_ppi_group!(PPI20_GROUP3, DPPIC20, 3);\nimpl_ppi_group!(PPI20_GROUP4, DPPIC20, 4);\nimpl_ppi_group!(PPI20_GROUP5, DPPIC20, 5);\n\n// DPPI30 groups\nimpl_ppi_group!(PPI30_GROUP0, DPPIC30, 0);\nimpl_ppi_group!(PPI30_GROUP1, DPPIC30, 1);\n\nimpl_timer!(TIMER00, TIMER00, TIMER00);\nimpl_timer!(TIMER10, TIMER10, TIMER10);\nimpl_timer!(TIMER20, TIMER20, TIMER20);\nimpl_timer!(TIMER21, TIMER21, TIMER21);\nimpl_timer!(TIMER22, TIMER22, TIMER22);\nimpl_timer!(TIMER23, TIMER23, TIMER23);\nimpl_timer!(TIMER24, TIMER24, TIMER24);\n\nimpl_twim!(SERIAL20, TWIM20, SERIAL20);\nimpl_twim!(SERIAL21, TWIM21, SERIAL21);\nimpl_twim!(SERIAL22, TWIM22, SERIAL22);\nimpl_twim!(SERIAL30, TWIM30, SERIAL30);\n\nimpl_twis!(SERIAL20, TWIS20, SERIAL20);\nimpl_twis!(SERIAL21, TWIS21, SERIAL21);\nimpl_twis!(SERIAL22, TWIS22, SERIAL22);\nimpl_twis!(SERIAL30, TWIS30, SERIAL30);\n\nimpl_pwm!(PWM20, PWM20, PWM20);\nimpl_pwm!(PWM21, PWM21, PWM21);\nimpl_pwm!(PWM22, PWM22, PWM22);\n\n#[cfg(feature = \"_s\")]\nimpl_spim!(\n    SERIAL00,\n    SPIM00,\n    SERIAL00,\n    match pac::OSCILLATORS_S.pll().currentfreq().read().currentfreq() {\n        pac::oscillators::vals::Currentfreq::CK128M => 128_000_000,\n        pac::oscillators::vals::Currentfreq::CK64M => 64_000_000,\n        _ => unreachable!(),\n    }\n);\n#[cfg(feature = \"_ns\")]\nimpl_spim!(\n    SERIAL00,\n    SPIM00,\n    SERIAL00,\n    match pac::OSCILLATORS_NS.pll().currentfreq().read().currentfreq() {\n        pac::oscillators::vals::Currentfreq::CK128M => 128_000_000,\n        pac::oscillators::vals::Currentfreq::CK64M => 64_000_000,\n        _ => unreachable!(),\n    }\n);\nimpl_spim!(SERIAL20, SPIM20, SERIAL20, 16_000_000);\nimpl_spim!(SERIAL21, SPIM21, SERIAL21, 16_000_000);\nimpl_spim!(SERIAL22, SPIM22, SERIAL22, 16_000_000);\nimpl_spim!(SERIAL30, SPIM30, SERIAL30, 16_000_000);\n\nimpl_spis!(SERIAL20, SPIS20, SERIAL20);\nimpl_spis!(SERIAL21, SPIS21, SERIAL21);\nimpl_spis!(SERIAL22, SPIS22, SERIAL22);\nimpl_spis!(SERIAL30, SPIS30, SERIAL30);\n\nimpl_uarte!(SERIAL00, UARTE00, SERIAL00);\nimpl_uarte!(SERIAL20, UARTE20, SERIAL20);\nimpl_uarte!(SERIAL21, UARTE21, SERIAL21);\nimpl_uarte!(SERIAL22, UARTE22, SERIAL22);\nimpl_uarte!(SERIAL30, UARTE30, SERIAL30);\n\n// NB: SAADC uses \"pin\" abstraction, not \"AIN\"\nimpl_saadc_input!(P1_04, 1, 4);\nimpl_saadc_input!(P1_05, 1, 5);\nimpl_saadc_input!(P1_06, 1, 6);\nimpl_saadc_input!(P1_07, 1, 7);\nimpl_saadc_input!(P1_11, 1, 11);\nimpl_saadc_input!(P1_12, 1, 12);\nimpl_saadc_input!(P1_13, 1, 13);\nimpl_saadc_input!(P1_14, 1, 14);\n\n#[cfg(feature = \"_s\")]\nimpl_cracen!(CRACEN, CRACEN, CRACEN);\n\nembassy_hal_internal::interrupt_mod!(\n    SWI00,\n    SWI01,\n    SWI02,\n    SWI03,\n    SPU00,\n    MPC00,\n    AAR00_CCM00,\n    ECB00,\n    CRACEN,\n    SERIAL00,\n    RRAMC,\n    VPR00,\n    CTRLAP,\n    TIMER00,\n    SPU10,\n    TIMER10,\n    EGU10,\n    RADIO_0,\n    RADIO_1,\n    SPU20,\n    SERIAL20,\n    SERIAL21,\n    SERIAL22,\n    EGU20,\n    TIMER20,\n    TIMER21,\n    TIMER22,\n    TIMER23,\n    TIMER24,\n    PDM20,\n    PDM21,\n    PWM20,\n    PWM21,\n    PWM22,\n    SAADC,\n    NFCT,\n    TEMP,\n    GPIOTE20_0,\n    GPIOTE20_1,\n    TAMPC,\n    I2S20,\n    QDEC20,\n    QDEC21,\n    GRTC_0,\n    GRTC_1,\n    GRTC_2,\n    GRTC_3,\n    SPU30,\n    SERIAL30,\n    COMP_LPCOMP,\n    WDT30,\n    WDT31,\n    GPIOTE30_0,\n    GPIOTE30_1,\n    CLOCK_POWER,\n);\n"
  },
  {
    "path": "embassy-nrf/src/chips/nrf54lm20_app.rs",
    "content": "/// Peripheral Access Crate\n#[allow(unused_imports)]\n#[rustfmt::skip]\npub mod pac {\n    pub use nrf_pac::*;\n\n    #[cfg(feature = \"_ns\")]\n    #[doc(no_inline)]\n    pub use nrf_pac::{\n        FICR_NS as FICR,\n        DPPIC00_NS as DPPIC00,\n        PPIB00_NS as PPIB00,\n        PPIB01_NS as PPIB01,\n        AAR00_NS as AAR00,\n        CCM00_NS as CCM00,\n        ECB00_NS as ECB00,\n        SPIM00_NS as SPIM00,\n        SPIS00_NS as SPIS00,\n        UARTE00_NS as UARTE00,\n        VPR00_NS as VPR00,\n        P2_NS as P2,\n        CTRLAP_NS as CTRLAP,\n        TAD_NS as TAD,\n        TIMER00_NS as TIMER00,\n        DPPIC10_NS as DPPIC10,\n        PPIB10_NS as PPIB10,\n        PPIB11_NS as PPIB11,\n        TIMER10_NS as TIMER10,\n        EGU10_NS as EGU10,\n        RADIO_NS as RADIO,\n        DPPIC20_NS as DPPIC20,\n        PPIB20_NS as PPIB20,\n        PPIB21_NS as PPIB21,\n        PPIB22_NS as PPIB22,\n        SPIM20_NS as SPIM20,\n        SPIS20_NS as SPIS20,\n        TWIM20_NS as TWIM20,\n        TWIS20_NS as TWIS20,\n        UARTE20_NS as UARTE20,\n        SPIM21_NS as SPIM21,\n        SPIS21_NS as SPIS21,\n        TWIM21_NS as TWIM21,\n        TWIS21_NS as TWIS21,\n        UARTE21_NS as UARTE21,\n        SPIM22_NS as SPIM22,\n        SPIS22_NS as SPIS22,\n        TWIM22_NS as TWIM22,\n        TWIS22_NS as TWIS22,\n        UARTE22_NS as UARTE22,\n        EGU20_NS as EGU20,\n        TIMER20_NS as TIMER20,\n        TIMER21_NS as TIMER21,\n        TIMER22_NS as TIMER22,\n        TIMER23_NS as TIMER23,\n        TIMER24_NS as TIMER24,\n        MEMCONF_NS as MEMCONF,\n        PDM20_NS as PDM20,\n        PDM21_NS as PDM21,\n        PWM20_NS as PWM20,\n        PWM21_NS as PWM21,\n        PWM22_NS as PWM22,\n        SAADC_NS as SAADC,\n        NFCT_NS as NFCT,\n        TEMP_NS as TEMP,\n        P1_NS as P1,\n        GPIOTE20_NS as GPIOTE20,\n        I2S20_NS as I2S20,\n        QDEC20_NS as QDEC20,\n        QDEC21_NS as QDEC21,\n        GRTC_NS as GRTC,\n        DPPIC30_NS as DPPIC30,\n        PPIB30_NS as PPIB30,\n        SPIM30_NS as SPIM30,\n        SPIS30_NS as SPIS30,\n        TWIM30_NS as TWIM30,\n        TWIS30_NS as TWIS30,\n        UARTE30_NS as UARTE30,\n        COMP_NS as COMP,\n        LPCOMP_NS as LPCOMP,\n        WDT31_NS as WDT31,\n        P0_NS as P0,\n        GPIOTE30_NS as GPIOTE30,\n        CLOCK_NS as CLOCK,\n        POWER_NS as POWER,\n        RESET_NS as RESET,\n        OSCILLATORS_NS as OSCILLATORS,\n        REGULATORS_NS as REGULATORS,\n        TPIU_NS as TPIU,\n        ETM_NS as ETM,\n    };\n\n    #[cfg(feature = \"_s\")]\n    #[doc(no_inline)]\n    pub use nrf_pac::{\n        FICR_NS as FICR,\n        SICR_S as SICR,\n        ICACHEDATA_S as ICACHEDATA,\n        ICACHEINFO_S as ICACHEINFO,\n        SWI00_S as SWI00,\n        SWI01_S as SWI01,\n        SWI02_S as SWI02,\n        SWI03_S as SWI03,\n        SPU00_S as SPU00,\n        MPC00_S as MPC00,\n        DPPIC00_S as DPPIC00,\n        PPIB00_S as PPIB00,\n        PPIB01_S as PPIB01,\n        KMU_S as KMU,\n        AAR00_S as AAR00,\n        CCM00_S as CCM00,\n        ECB00_S as ECB00,\n        CRACEN_S as CRACEN,\n        SPIM00_S as SPIM00,\n        SPIS00_S as SPIS00,\n        UARTE00_S as UARTE00,\n        GLITCHDET_S as GLITCHDET,\n        RRAMC_S as RRAMC,\n        VPR00_S as VPR00,\n        P2_S as P2,\n        CTRLAP_S as CTRLAP,\n        TAD_S as TAD,\n        TIMER00_S as TIMER00,\n        SPU10_S as SPU10,\n        DPPIC10_S as DPPIC10,\n        PPIB10_S as PPIB10,\n        PPIB11_S as PPIB11,\n        TIMER10_S as TIMER10,\n        EGU10_S as EGU10,\n        RADIO_S as RADIO,\n        SPU20_S as SPU20,\n        DPPIC20_S as DPPIC20,\n        PPIB20_S as PPIB20,\n        PPIB21_S as PPIB21,\n        PPIB22_S as PPIB22,\n        SPIM20_S as SPIM20,\n        SPIS20_S as SPIS20,\n        TWIM20_S as TWIM20,\n        TWIS20_S as TWIS20,\n        UARTE20_S as UARTE20,\n        SPIM21_S as SPIM21,\n        SPIS21_S as SPIS21,\n        TWIM21_S as TWIM21,\n        TWIS21_S as TWIS21,\n        UARTE21_S as UARTE21,\n        SPIM22_S as SPIM22,\n        SPIS22_S as SPIS22,\n        TWIM22_S as TWIM22,\n        TWIS22_S as TWIS22,\n        UARTE22_S as UARTE22,\n        EGU20_S as EGU20,\n        TIMER20_S as TIMER20,\n        TIMER21_S as TIMER21,\n        TIMER22_S as TIMER22,\n        TIMER23_S as TIMER23,\n        TIMER24_S as TIMER24,\n        MEMCONF_S as MEMCONF,\n        PDM20_S as PDM20,\n        PDM21_S as PDM21,\n        PWM20_S as PWM20,\n        PWM21_S as PWM21,\n        PWM22_S as PWM22,\n        SAADC_S as SAADC,\n        NFCT_S as NFCT,\n        TEMP_S as TEMP,\n        P1_S as P1,\n        GPIOTE20_S as GPIOTE20,\n        TAMPC_S as TAMPC,\n        QDEC20_S as QDEC20,\n        QDEC21_S as QDEC21,\n        GRTC_S as GRTC,\n        SPU30_S as SPU30,\n        DPPIC30_S as DPPIC30,\n        PPIB30_S as PPIB30,\n        SPIM30_S as SPIM30,\n        SPIS30_S as SPIS30,\n        TWIM30_S as TWIM30,\n        TWIS30_S as TWIS30,\n        UARTE30_S as UARTE30,\n        COMP_S as COMP,\n        LPCOMP_S as LPCOMP,\n        WDT30_S as WDT30,\n        WDT31_S as WDT31,\n        P0_S as P0,\n        GPIOTE30_S as GPIOTE30,\n        CLOCK_S as CLOCK,\n        POWER_S as POWER,\n        RESET_S as RESET,\n        OSCILLATORS_S as OSCILLATORS,\n        REGULATORS_S as REGULATORS,\n        CRACENCORE_S as CRACENCORE,\n        CPUC_S as CPUC,\n        ICACHE_S as ICACHE,\n    };\n}\n\n/// The maximum buffer size that the EasyDMA can send/recv in one operation.\npub const EASY_DMA_SIZE: usize = (1 << 16) - 1;\npub const FORCE_COPY_BUFFER_SIZE: usize = 1024;\n\n// 2 MB NVM\n#[allow(unused)]\npub const FLASH_SIZE: usize = 2048 * 1024;\n\nembassy_hal_internal::peripherals! {\n    // PPI\n    PPI00_CH0,\n    PPI00_CH1,\n    PPI00_CH2,\n    PPI00_CH3,\n    PPI00_CH4,\n    PPI00_CH5,\n    PPI00_CH6,\n    PPI00_CH7,\n\n    PPI10_CH0,\n    PPI10_CH1,\n    PPI10_CH2,\n    PPI10_CH3,\n    PPI10_CH4,\n    PPI10_CH5,\n    PPI10_CH6,\n    PPI10_CH7,\n    PPI10_CH8,\n    PPI10_CH9,\n    PPI10_CH10,\n    PPI10_CH11,\n    PPI10_CH12,\n    PPI10_CH13,\n    PPI10_CH14,\n    PPI10_CH15,\n    PPI10_CH16,\n    PPI10_CH17,\n    PPI10_CH18,\n    PPI10_CH19,\n    PPI10_CH20,\n    PPI10_CH21,\n    PPI10_CH22,\n    PPI10_CH23,\n\n    PPI20_CH0,\n    PPI20_CH1,\n    PPI20_CH2,\n    PPI20_CH3,\n    PPI20_CH4,\n    PPI20_CH5,\n    PPI20_CH6,\n    PPI20_CH7,\n    PPI20_CH8,\n    PPI20_CH9,\n    PPI20_CH10,\n    PPI20_CH11,\n    PPI20_CH12,\n    PPI20_CH13,\n    PPI20_CH14,\n    PPI20_CH15,\n\n    PPI30_CH0,\n    PPI30_CH1,\n    PPI30_CH2,\n    PPI30_CH3,\n\n    PPI00_GROUP0,\n    PPI00_GROUP1,\n\n    PPI10_GROUP0,\n    PPI10_GROUP1,\n    PPI10_GROUP2,\n    PPI10_GROUP3,\n    PPI10_GROUP4,\n    PPI10_GROUP5,\n\n    PPI20_GROUP0,\n    PPI20_GROUP1,\n    PPI20_GROUP2,\n    PPI20_GROUP3,\n    PPI20_GROUP4,\n    PPI20_GROUP5,\n\n    PPI30_GROUP0,\n    PPI30_GROUP1,\n\n    // PPI BRIDGE channels\n    PPIB00_CH0,\n    PPIB00_CH1,\n    PPIB00_CH2,\n    PPIB00_CH3,\n    PPIB00_CH4,\n    PPIB00_CH5,\n    PPIB00_CH6,\n    PPIB00_CH7,\n\n    PPIB01_CH0,\n    PPIB01_CH1,\n    PPIB01_CH2,\n    PPIB01_CH3,\n    PPIB01_CH4,\n    PPIB01_CH5,\n    PPIB01_CH6,\n    PPIB01_CH7,\n\n    PPIB10_CH0,\n    PPIB10_CH1,\n    PPIB10_CH2,\n    PPIB10_CH3,\n    PPIB10_CH4,\n    PPIB10_CH5,\n    PPIB10_CH6,\n    PPIB10_CH7,\n\n    PPIB11_CH0,\n    PPIB11_CH1,\n    PPIB11_CH2,\n    PPIB11_CH3,\n    PPIB11_CH4,\n    PPIB11_CH5,\n    PPIB11_CH6,\n    PPIB11_CH7,\n    PPIB11_CH8,\n    PPIB11_CH9,\n    PPIB11_CH10,\n    PPIB11_CH11,\n    PPIB11_CH12,\n    PPIB11_CH13,\n    PPIB11_CH14,\n    PPIB11_CH15,\n\n    PPIB20_CH0,\n    PPIB20_CH1,\n    PPIB20_CH2,\n    PPIB20_CH3,\n    PPIB20_CH4,\n    PPIB20_CH5,\n    PPIB20_CH6,\n    PPIB20_CH7,\n\n    PPIB21_CH0,\n    PPIB21_CH1,\n    PPIB21_CH2,\n    PPIB21_CH3,\n    PPIB21_CH4,\n    PPIB21_CH5,\n    PPIB21_CH6,\n    PPIB21_CH7,\n    PPIB21_CH8,\n    PPIB21_CH9,\n    PPIB21_CH10,\n    PPIB21_CH11,\n    PPIB21_CH12,\n    PPIB21_CH13,\n    PPIB21_CH14,\n    PPIB21_CH15,\n\n    PPIB22_CH0,\n    PPIB22_CH1,\n    PPIB22_CH2,\n    PPIB22_CH3,\n\n    PPIB30_CH0,\n    PPIB30_CH1,\n    PPIB30_CH2,\n    PPIB30_CH3,\n\n    // Timers\n    TIMER00,\n    TIMER10,\n    TIMER20,\n    TIMER21,\n    TIMER22,\n    TIMER23,\n    TIMER24,\n\n    // GPIO port 0\n    P0_00,\n    P0_01,\n    P0_02,\n    P0_03,\n    P0_04,\n    P0_05,\n    P0_06,\n    P0_07,\n    P0_08,\n    P0_09,\n\n    // GPIO port 1\n    P1_00,\n    P1_01,\n    P1_02,\n    P1_03,\n    P1_04,\n    P1_05,\n    P1_06,\n    P1_07,\n    P1_08,\n    P1_09,\n    P1_10,\n    P1_11,\n    P1_12,\n    P1_13,\n    P1_14,\n    P1_15,\n    P1_16,\n    P1_17,\n    P1_18,\n    P1_19,\n    P1_20,\n    P1_21,\n    P1_22,\n    P1_23,\n    P1_24,\n    P1_25,\n    P1_26,\n    P1_27,\n    P1_28,\n    P1_29,\n    P1_30,\n    P1_31,\n\n\n    // GPIO port 2\n    P2_00,\n    P2_01,\n    P2_02,\n    P2_03,\n    P2_04,\n    P2_05,\n    P2_06,\n    P2_07,\n    P2_08,\n    P2_09,\n    P2_10,\n\n    // GPIO port 3\n    P3_00,\n    P3_01,\n    P3_02,\n    P3_03,\n    P3_04,\n    P3_05,\n    P3_06,\n    P3_07,\n    P3_08,\n    P3_09,\n    P3_10,\n    P3_11,\n    P3_12,\n\n    // GRTC\n    GRTC_CH0,\n    #[cfg(not(feature = \"time-driver-grtc\"))]\n    GRTC_CH1,\n    GRTC_CH2,\n    GRTC_CH3,\n    GRTC_CH4,\n    GRTC_CH5,\n    GRTC_CH6,\n    GRTC_CH7,\n    GRTC_CH8,\n    GRTC_CH9,\n    GRTC_CH10,\n    GRTC_CH11,\n\n    // PWM\n    PWM20,\n    PWM21,\n    PWM22,\n\n    // SERIAL\n    SERIAL00,\n    SERIAL20,\n    SERIAL21,\n    SERIAL22,\n    SERIAL30,\n\n    // SAADC\n    SAADC,\n\n    // RADIO\n    RADIO,\n\n\n    // GPIOTE instances\n    GPIOTE20,\n    GPIOTE30,\n\n    // GPIOTE channels\n    GPIOTE20_CH0,\n    GPIOTE20_CH1,\n    GPIOTE20_CH2,\n    GPIOTE20_CH3,\n    GPIOTE20_CH4,\n    GPIOTE20_CH5,\n    GPIOTE20_CH6,\n    GPIOTE20_CH7,\n    GPIOTE30_CH0,\n    GPIOTE30_CH1,\n    GPIOTE30_CH2,\n    GPIOTE30_CH3,\n\n    // CRACEN\n    #[cfg(feature = \"_s\")]\n    CRACEN,\n\n    #[cfg(feature = \"_s\")]\n    // RRAMC\n    RRAMC,\n\n    // TEMP\n    TEMP,\n\n    // WDT\n    #[cfg(feature = \"_ns\")]\n    WDT,\n    #[cfg(feature = \"_s\")]\n    WDT0,\n    #[cfg(feature = \"_s\")]\n    WDT1,\n}\n\nimpl_pin!(P0_00, 0, 0);\nimpl_pin!(P0_01, 0, 1);\nimpl_pin!(P0_02, 0, 2);\nimpl_pin!(P0_03, 0, 3);\nimpl_pin!(P0_04, 0, 4);\nimpl_pin!(P0_05, 0, 5);\nimpl_pin!(P0_06, 0, 6);\nimpl_pin!(P0_07, 0, 7);\nimpl_pin!(P0_08, 0, 8);\nimpl_pin!(P0_09, 0, 9);\n\nimpl_pin!(P1_00, 1, 0);\nimpl_pin!(P1_01, 1, 1);\nimpl_pin!(P1_02, 1, 2);\nimpl_pin!(P1_03, 1, 3);\nimpl_pin!(P1_04, 1, 4);\nimpl_pin!(P1_05, 1, 5);\nimpl_pin!(P1_06, 1, 6);\nimpl_pin!(P1_07, 1, 7);\nimpl_pin!(P1_08, 1, 8);\nimpl_pin!(P1_09, 1, 9);\nimpl_pin!(P1_10, 1, 10);\nimpl_pin!(P1_11, 1, 11);\nimpl_pin!(P1_12, 1, 12);\nimpl_pin!(P1_13, 1, 13);\nimpl_pin!(P1_14, 1, 14);\nimpl_pin!(P1_15, 1, 15);\nimpl_pin!(P1_16, 1, 16);\nimpl_pin!(P1_17, 1, 17);\nimpl_pin!(P1_18, 1, 18);\nimpl_pin!(P1_19, 1, 19);\nimpl_pin!(P1_20, 1, 20);\nimpl_pin!(P1_21, 1, 21);\nimpl_pin!(P1_22, 1, 22);\nimpl_pin!(P1_23, 1, 23);\nimpl_pin!(P1_24, 1, 24);\nimpl_pin!(P1_25, 1, 25);\nimpl_pin!(P1_26, 1, 26);\nimpl_pin!(P1_27, 1, 27);\nimpl_pin!(P1_28, 1, 28);\nimpl_pin!(P1_29, 1, 29);\nimpl_pin!(P1_30, 1, 30);\nimpl_pin!(P1_31, 1, 31);\n\nimpl_pin!(P2_00, 2, 0);\nimpl_pin!(P2_01, 2, 1);\nimpl_pin!(P2_02, 2, 2);\nimpl_pin!(P2_03, 2, 3);\nimpl_pin!(P2_04, 2, 4);\nimpl_pin!(P2_05, 2, 5);\nimpl_pin!(P2_06, 2, 6);\nimpl_pin!(P2_07, 2, 7);\nimpl_pin!(P2_08, 2, 8);\nimpl_pin!(P2_09, 2, 9);\nimpl_pin!(P2_10, 2, 10);\n\nimpl_pin!(P3_00, 3, 0);\nimpl_pin!(P3_01, 3, 1);\nimpl_pin!(P3_02, 3, 2);\nimpl_pin!(P3_03, 3, 3);\nimpl_pin!(P3_04, 3, 4);\nimpl_pin!(P3_05, 3, 5);\nimpl_pin!(P3_06, 3, 6);\nimpl_pin!(P3_07, 3, 7);\nimpl_pin!(P3_08, 3, 8);\nimpl_pin!(P3_09, 3, 9);\nimpl_pin!(P3_10, 3, 10);\nimpl_pin!(P3_11, 3, 11);\nimpl_pin!(P3_12, 3, 12);\n\ncfg_if::cfg_if! {\n    if #[cfg(feature = \"gpiote\")] {\n        impl_gpiote_pin!(P0_00, GPIOTE30);\n        impl_gpiote_pin!(P0_01, GPIOTE30);\n        impl_gpiote_pin!(P0_02, GPIOTE30);\n        impl_gpiote_pin!(P0_03, GPIOTE30);\n        impl_gpiote_pin!(P0_04, GPIOTE30);\n        impl_gpiote_pin!(P0_05, GPIOTE30);\n        impl_gpiote_pin!(P0_06, GPIOTE30);\n        impl_gpiote_pin!(P0_07, GPIOTE30);\n        impl_gpiote_pin!(P0_08, GPIOTE30);\n        impl_gpiote_pin!(P0_09, GPIOTE30);\n\n        impl_gpiote_pin!(P1_00, GPIOTE20);\n        impl_gpiote_pin!(P1_01, GPIOTE20);\n        impl_gpiote_pin!(P1_02, GPIOTE20);\n        impl_gpiote_pin!(P1_03, GPIOTE20);\n        impl_gpiote_pin!(P1_04, GPIOTE20);\n        impl_gpiote_pin!(P1_05, GPIOTE20);\n        impl_gpiote_pin!(P1_06, GPIOTE20);\n        impl_gpiote_pin!(P1_07, GPIOTE20);\n        impl_gpiote_pin!(P1_08, GPIOTE20);\n        impl_gpiote_pin!(P1_09, GPIOTE20);\n        impl_gpiote_pin!(P1_10, GPIOTE20);\n        impl_gpiote_pin!(P1_11, GPIOTE20);\n        impl_gpiote_pin!(P1_12, GPIOTE20);\n        impl_gpiote_pin!(P1_13, GPIOTE20);\n        impl_gpiote_pin!(P1_14, GPIOTE20);\n        impl_gpiote_pin!(P1_15, GPIOTE20);\n        impl_gpiote_pin!(P1_16, GPIOTE20);\n        impl_gpiote_pin!(P1_17, GPIOTE20);\n        impl_gpiote_pin!(P1_18, GPIOTE20);\n        impl_gpiote_pin!(P1_19, GPIOTE20);\n        impl_gpiote_pin!(P1_20, GPIOTE20);\n        impl_gpiote_pin!(P1_21, GPIOTE20);\n        impl_gpiote_pin!(P1_22, GPIOTE20);\n        impl_gpiote_pin!(P1_23, GPIOTE20);\n        impl_gpiote_pin!(P1_24, GPIOTE20);\n        impl_gpiote_pin!(P1_25, GPIOTE20);\n        impl_gpiote_pin!(P1_26, GPIOTE20);\n        impl_gpiote_pin!(P1_27, GPIOTE20);\n        impl_gpiote_pin!(P1_28, GPIOTE20);\n        impl_gpiote_pin!(P1_29, GPIOTE20);\n        impl_gpiote_pin!(P1_30, GPIOTE20);\n        impl_gpiote_pin!(P1_31, GPIOTE20);\n\n        impl_gpiote_pin!(P3_00, GPIOTE20);\n        impl_gpiote_pin!(P3_01, GPIOTE20);\n        impl_gpiote_pin!(P3_02, GPIOTE20);\n        impl_gpiote_pin!(P3_03, GPIOTE20);\n        impl_gpiote_pin!(P3_04, GPIOTE20);\n        impl_gpiote_pin!(P3_05, GPIOTE20);\n        impl_gpiote_pin!(P3_06, GPIOTE20);\n        impl_gpiote_pin!(P3_07, GPIOTE20);\n        impl_gpiote_pin!(P3_08, GPIOTE20);\n        impl_gpiote_pin!(P3_09, GPIOTE20);\n        impl_gpiote_pin!(P3_10, GPIOTE20);\n        impl_gpiote_pin!(P3_11, GPIOTE20);\n        impl_gpiote_pin!(P3_12, GPIOTE20);\n    }\n}\n\n#[cfg(feature = \"_ns\")]\nimpl_wdt!(WDT, WDT31, WDT31, 0);\n#[cfg(feature = \"_s\")]\nimpl_wdt!(WDT0, WDT31, WDT31, 0);\n#[cfg(feature = \"_s\")]\nimpl_wdt!(WDT1, WDT30, WDT30, 1);\n// DPPI00 channels\nimpl_ppi_channel!(PPI00_CH0, DPPIC00, 0 => configurable);\nimpl_ppi_channel!(PPI00_CH1, DPPIC00, 1 => configurable);\nimpl_ppi_channel!(PPI00_CH2, DPPIC00, 2 => configurable);\nimpl_ppi_channel!(PPI00_CH3, DPPIC00, 3 => configurable);\nimpl_ppi_channel!(PPI00_CH4, DPPIC00, 4 => configurable);\nimpl_ppi_channel!(PPI00_CH5, DPPIC00, 5 => configurable);\nimpl_ppi_channel!(PPI00_CH6, DPPIC00, 6 => configurable);\nimpl_ppi_channel!(PPI00_CH7, DPPIC00, 7 => configurable);\n\n// DPPI10 channels\nimpl_ppi_channel!(PPI10_CH0, DPPIC10, 0 => static);\n\n// DPPI20 channels\nimpl_ppi_channel!(PPI20_CH0, DPPIC20, 0 => configurable);\nimpl_ppi_channel!(PPI20_CH1, DPPIC20, 1 => configurable);\nimpl_ppi_channel!(PPI20_CH2, DPPIC20, 2 => configurable);\nimpl_ppi_channel!(PPI20_CH3, DPPIC20, 3 => configurable);\nimpl_ppi_channel!(PPI20_CH4, DPPIC20, 4 => configurable);\nimpl_ppi_channel!(PPI20_CH5, DPPIC20, 5 => configurable);\nimpl_ppi_channel!(PPI20_CH6, DPPIC20, 6 => configurable);\nimpl_ppi_channel!(PPI20_CH7, DPPIC20, 7 => configurable);\nimpl_ppi_channel!(PPI20_CH8, DPPIC20, 8 => configurable);\nimpl_ppi_channel!(PPI20_CH9, DPPIC20, 9 => configurable);\nimpl_ppi_channel!(PPI20_CH10, DPPIC20, 10 => configurable);\nimpl_ppi_channel!(PPI20_CH11, DPPIC20, 11 => configurable);\nimpl_ppi_channel!(PPI20_CH12, DPPIC20, 12 => configurable);\nimpl_ppi_channel!(PPI20_CH13, DPPIC20, 13 => configurable);\nimpl_ppi_channel!(PPI20_CH14, DPPIC20, 14 => configurable);\nimpl_ppi_channel!(PPI20_CH15, DPPIC20, 15 => configurable);\n\n// DPPI30 channels\nimpl_ppi_channel!(PPI30_CH0, DPPIC30, 0 => configurable);\nimpl_ppi_channel!(PPI30_CH1, DPPIC30, 1 => configurable);\nimpl_ppi_channel!(PPI30_CH2, DPPIC30, 2 => configurable);\nimpl_ppi_channel!(PPI30_CH3, DPPIC30, 3 => configurable);\n\n// DPPI00 groups\nimpl_ppi_group!(PPI00_GROUP0, DPPIC00, 0);\nimpl_ppi_group!(PPI00_GROUP1, DPPIC00, 1);\n\n// DPPI10 groups\nimpl_ppi_group!(PPI10_GROUP0, DPPIC10, 0);\n\n// DPPI20 groups\nimpl_ppi_group!(PPI20_GROUP0, DPPIC20, 0);\nimpl_ppi_group!(PPI20_GROUP1, DPPIC20, 1);\nimpl_ppi_group!(PPI20_GROUP2, DPPIC20, 2);\nimpl_ppi_group!(PPI20_GROUP3, DPPIC20, 3);\nimpl_ppi_group!(PPI20_GROUP4, DPPIC20, 4);\nimpl_ppi_group!(PPI20_GROUP5, DPPIC20, 5);\n\n// DPPI30 groups\nimpl_ppi_group!(PPI30_GROUP0, DPPIC30, 0);\nimpl_ppi_group!(PPI30_GROUP1, DPPIC30, 1);\n\nimpl_timer!(TIMER00, TIMER00, TIMER00);\nimpl_timer!(TIMER10, TIMER10, TIMER10);\nimpl_timer!(TIMER20, TIMER20, TIMER20);\nimpl_timer!(TIMER21, TIMER21, TIMER21);\nimpl_timer!(TIMER22, TIMER22, TIMER22);\nimpl_timer!(TIMER23, TIMER23, TIMER23);\nimpl_timer!(TIMER24, TIMER24, TIMER24);\n\nimpl_twim!(SERIAL20, TWIM20, SERIAL20);\nimpl_twim!(SERIAL21, TWIM21, SERIAL21);\nimpl_twim!(SERIAL22, TWIM22, SERIAL22);\nimpl_twim!(SERIAL30, TWIM30, SERIAL30);\n\nimpl_twis!(SERIAL20, TWIS20, SERIAL20);\nimpl_twis!(SERIAL21, TWIS21, SERIAL21);\nimpl_twis!(SERIAL22, TWIS22, SERIAL22);\nimpl_twis!(SERIAL30, TWIS30, SERIAL30);\n\nimpl_pwm!(PWM20, PWM20, PWM20);\nimpl_pwm!(PWM21, PWM21, PWM21);\nimpl_pwm!(PWM22, PWM22, PWM22);\n\n#[cfg(feature = \"_s\")]\nimpl_spim!(\n    SERIAL00,\n    SPIM00,\n    SERIAL00,\n    match pac::OSCILLATORS_S.pll().currentfreq().read().currentfreq() {\n        pac::oscillators::vals::Currentfreq::CK128M => 128_000_000,\n        pac::oscillators::vals::Currentfreq::CK64M => 64_000_000,\n        _ => unreachable!(),\n    }\n);\n#[cfg(feature = \"_ns\")]\nimpl_spim!(\n    SERIAL00,\n    SPIM00,\n    SERIAL00,\n    match pac::OSCILLATORS_NS.pll().currentfreq().read().currentfreq() {\n        pac::oscillators::vals::Currentfreq::CK128M => 128_000_000,\n        pac::oscillators::vals::Currentfreq::CK64M => 64_000_000,\n        _ => unreachable!(),\n    }\n);\nimpl_spim!(SERIAL20, SPIM20, SERIAL20, 16_000_000);\nimpl_spim!(SERIAL21, SPIM21, SERIAL21, 16_000_000);\nimpl_spim!(SERIAL22, SPIM22, SERIAL22, 16_000_000);\nimpl_spim!(SERIAL30, SPIM30, SERIAL30, 16_000_000);\n\nimpl_spis!(SERIAL20, SPIS20, SERIAL20);\nimpl_spis!(SERIAL21, SPIS21, SERIAL21);\nimpl_spis!(SERIAL22, SPIS22, SERIAL22);\nimpl_spis!(SERIAL30, SPIS30, SERIAL30);\n\nimpl_uarte!(SERIAL00, UARTE00, SERIAL00);\nimpl_uarte!(SERIAL20, UARTE20, SERIAL20);\nimpl_uarte!(SERIAL21, UARTE21, SERIAL21);\nimpl_uarte!(SERIAL22, UARTE22, SERIAL22);\nimpl_uarte!(SERIAL30, UARTE30, SERIAL30);\n\n// NB: SAADC uses \"pin\" abstraction, not \"AIN\"\nimpl_saadc_input!(P1_04, 1, 4);\nimpl_saadc_input!(P1_05, 1, 5);\nimpl_saadc_input!(P1_06, 1, 6);\nimpl_saadc_input!(P1_07, 1, 7);\nimpl_saadc_input!(P1_11, 1, 11);\nimpl_saadc_input!(P1_12, 1, 12);\nimpl_saadc_input!(P1_13, 1, 13);\nimpl_saadc_input!(P1_14, 1, 14);\n\n#[cfg(feature = \"_s\")]\nimpl_cracen!(CRACEN, CRACEN, CRACEN);\n\nembassy_hal_internal::interrupt_mod!(\n    SWI00,\n    SWI01,\n    SWI02,\n    SWI03,\n    SPU00,\n    MPC00,\n    AAR00_CCM00,\n    ECB00,\n    CRACEN,\n    SERIAL00,\n    RRAMC,\n    VPR00,\n    CTRLAP,\n    TIMER00,\n    SPU10,\n    TIMER10,\n    EGU10,\n    RADIO_0,\n    RADIO_1,\n    SPU20,\n    SERIAL20,\n    SERIAL21,\n    SERIAL22,\n    EGU20,\n    TIMER20,\n    TIMER21,\n    TIMER22,\n    TIMER23,\n    TIMER24,\n    PDM20,\n    PDM21,\n    PWM20,\n    PWM21,\n    PWM22,\n    SAADC,\n    NFCT,\n    TEMP,\n    GPIOTE20_0,\n    GPIOTE20_1,\n    TAMPC,\n    QDEC20,\n    QDEC21,\n    GRTC_0,\n    GRTC_1,\n    GRTC_2,\n    GRTC_3,\n    SPU30,\n    SERIAL30,\n    COMP_LPCOMP,\n    WDT30,\n    WDT31,\n    GPIOTE30_0,\n    GPIOTE30_1,\n    CLOCK_POWER,\n);\n"
  },
  {
    "path": "embassy-nrf/src/chips/nrf9120.rs",
    "content": "/// Peripheral Access Crate\n#[allow(unused_imports)]\n#[rustfmt::skip]\npub mod pac {\n    pub use nrf_pac::*;\n\n    #[cfg(feature = \"_ns\")]\n    #[doc(no_inline)]\n    pub use nrf_pac::{\n        CLOCK_NS as CLOCK,\n        DPPIC_NS as DPPIC,\n        EGU0_NS as EGU0,\n        EGU1_NS as EGU1,\n        EGU2_NS as EGU2,\n        EGU3_NS as EGU3,\n        EGU4_NS as EGU4,\n        EGU5_NS as EGU5,\n        FPU_NS as FPU,\n        GPIOTE1_NS as GPIOTE1,\n        I2S_NS as I2S,\n        IPC_NS as IPC,\n        KMU_NS as KMU,\n        NVMC_NS as NVMC,\n        P0_NS as P0,\n        PDM_NS as PDM,\n        POWER_NS as POWER,\n        PWM0_NS as PWM0,\n        PWM1_NS as PWM1,\n        PWM2_NS as PWM2,\n        PWM3_NS as PWM3,\n        REGULATORS_NS as REGULATORS,\n        RTC0_NS as RTC0,\n        RTC1_NS as RTC1,\n        SAADC_NS as SAADC,\n        SPIM0_NS as SPIM0,\n        SPIM1_NS as SPIM1,\n        SPIM2_NS as SPIM2,\n        SPIM3_NS as SPIM3,\n        SPIS0_NS as SPIS0,\n        SPIS1_NS as SPIS1,\n        SPIS2_NS as SPIS2,\n        SPIS3_NS as SPIS3,\n        TIMER0_NS as TIMER0,\n        TIMER1_NS as TIMER1,\n        TIMER2_NS as TIMER2,\n        TWIM0_NS as TWIM0,\n        TWIM1_NS as TWIM1,\n        TWIM2_NS as TWIM2,\n        TWIM3_NS as TWIM3,\n        TWIS0_NS as TWIS0,\n        TWIS1_NS as TWIS1,\n        TWIS2_NS as TWIS2,\n        TWIS3_NS as TWIS3,\n        UARTE0_NS as UARTE0,\n        UARTE1_NS as UARTE1,\n        UARTE2_NS as UARTE2,\n        UARTE3_NS as UARTE3,\n        VMC_NS as VMC,\n        WDT_NS as WDT,\n    };\n\n    #[cfg(feature = \"_s\")]\n    #[doc(no_inline)]\n    pub use nrf_pac::{\n        CC_HOST_RGF_S as CC_HOST_RGF,\n        CC_RNG_S as CC_RNG,\n        CLOCK_S as CLOCK,\n        CRYPTOCELL_S as CRYPTOCELL,\n        CTRL_AP_PERI_S as CTRL_AP_PERI,\n        DPPIC_S as DPPIC,\n        EGU0_S as EGU0,\n        EGU1_S as EGU1,\n        EGU2_S as EGU2,\n        EGU3_S as EGU3,\n        EGU4_S as EGU4,\n        EGU5_S as EGU5,\n        FICR_S as FICR,\n        FPU_NS as FPU,\n        GPIOTE0_S as GPIOTE0,\n        I2S_S as I2S,\n        IPC_S as IPC,\n        KMU_S as KMU,\n        NVMC_S as NVMC,\n        P0_S as P0,\n        PDM_S as PDM,\n        POWER_S as POWER,\n        PWM0_S as PWM0,\n        PWM1_S as PWM1,\n        PWM2_S as PWM2,\n        PWM3_S as PWM3,\n        REGULATORS_S as REGULATORS,\n        RTC0_S as RTC0,\n        RTC1_S as RTC1,\n        SAADC_S as SAADC,\n        SPIM0_S as SPIM0,\n        SPIM1_S as SPIM1,\n        SPIM2_S as SPIM2,\n        SPIM3_S as SPIM3,\n        SPIS0_S as SPIS0,\n        SPIS1_S as SPIS1,\n        SPIS2_S as SPIS2,\n        SPIS3_S as SPIS3,\n        SPU_S as SPU,\n        TAD_S as TAD,\n        TIMER0_S as TIMER0,\n        TIMER1_S as TIMER1,\n        TIMER2_S as TIMER2,\n        TWIM0_S as TWIM0,\n        TWIM1_S as TWIM1,\n        TWIM2_S as TWIM2,\n        TWIM3_S as TWIM3,\n        TWIS0_S as TWIS0,\n        TWIS1_S as TWIS1,\n        TWIS2_S as TWIS2,\n        TWIS3_S as TWIS3,\n        UARTE0_S as UARTE0,\n        UARTE1_S as UARTE1,\n        UARTE2_S as UARTE2,\n        UARTE3_S as UARTE3,\n        UICR_S as UICR,\n        VMC_S as VMC,\n        WDT_S as WDT,\n    };\n}\n\n/// The maximum buffer size that the EasyDMA can send/recv in one operation.\npub const EASY_DMA_SIZE: usize = (1 << 13) - 1;\npub const FORCE_COPY_BUFFER_SIZE: usize = 1024;\n\npub const FLASH_SIZE: usize = 1024 * 1024;\n\nembassy_hal_internal::peripherals! {\n    // RTC\n    RTC0,\n    #[cfg(not(feature = \"time-driver-rtc1\"))]\n    RTC1,\n\n    // WDT\n    WDT,\n\n    // NVMC\n    NVMC,\n\n    // UARTE, TWI & SPI\n    SERIAL0,\n    SERIAL1,\n    SERIAL2,\n    SERIAL3,\n\n    // SAADC\n    SAADC,\n\n    // PWM\n    PWM0,\n    PWM1,\n    PWM2,\n    PWM3,\n\n    // TIMER\n    TIMER0,\n    TIMER1,\n    TIMER2,\n\n    // GPIOTE\n    GPIOTE_CH0,\n    GPIOTE_CH1,\n    GPIOTE_CH2,\n    GPIOTE_CH3,\n    GPIOTE_CH4,\n    GPIOTE_CH5,\n    GPIOTE_CH6,\n    GPIOTE_CH7,\n\n    // PPI\n    PPI_CH0,\n    PPI_CH1,\n    PPI_CH2,\n    PPI_CH3,\n    PPI_CH4,\n    PPI_CH5,\n    PPI_CH6,\n    PPI_CH7,\n    PPI_CH8,\n    PPI_CH9,\n    PPI_CH10,\n    PPI_CH11,\n    PPI_CH12,\n    PPI_CH13,\n    PPI_CH14,\n    PPI_CH15,\n\n    PPI_GROUP0,\n    PPI_GROUP1,\n    PPI_GROUP2,\n    PPI_GROUP3,\n    PPI_GROUP4,\n    PPI_GROUP5,\n\n    // GPIO port 0\n    P0_00,\n    P0_01,\n    P0_02,\n    P0_03,\n    P0_04,\n    P0_05,\n    P0_06,\n    P0_07,\n    P0_08,\n    P0_09,\n    P0_10,\n    P0_11,\n    P0_12,\n    P0_13,\n    P0_14,\n    P0_15,\n    P0_16,\n    P0_17,\n    P0_18,\n    P0_19,\n    P0_20,\n    P0_21,\n    P0_22,\n    P0_23,\n    P0_24,\n    P0_25,\n    P0_26,\n    P0_27,\n    P0_28,\n    P0_29,\n    P0_30,\n    P0_31,\n\n    // PDM\n    PDM,\n\n    // EGU\n    EGU0,\n    EGU1,\n    EGU2,\n    EGU3,\n    EGU4,\n    EGU5,\n\n    // CryptoCell RNG\n    #[cfg(feature = \"_s\")]\n    CC_RNG\n}\n\nimpl_uarte!(SERIAL0, UARTE0, SERIAL0);\nimpl_uarte!(SERIAL1, UARTE1, SERIAL1);\nimpl_uarte!(SERIAL2, UARTE2, SERIAL2);\nimpl_uarte!(SERIAL3, UARTE3, SERIAL3);\n\nimpl_spim!(SERIAL0, SPIM0, SERIAL0);\nimpl_spim!(SERIAL1, SPIM1, SERIAL1);\nimpl_spim!(SERIAL2, SPIM2, SERIAL2);\nimpl_spim!(SERIAL3, SPIM3, SERIAL3);\n\nimpl_spis!(SERIAL0, SPIS0, SERIAL0);\nimpl_spis!(SERIAL1, SPIS1, SERIAL1);\nimpl_spis!(SERIAL2, SPIS2, SERIAL2);\nimpl_spis!(SERIAL3, SPIS3, SERIAL3);\n\nimpl_twim!(SERIAL0, TWIM0, SERIAL0);\nimpl_twim!(SERIAL1, TWIM1, SERIAL1);\nimpl_twim!(SERIAL2, TWIM2, SERIAL2);\nimpl_twim!(SERIAL3, TWIM3, SERIAL3);\n\nimpl_twis!(SERIAL0, TWIS0, SERIAL0);\nimpl_twis!(SERIAL1, TWIS1, SERIAL1);\nimpl_twis!(SERIAL2, TWIS2, SERIAL2);\nimpl_twis!(SERIAL3, TWIS3, SERIAL3);\n\nimpl_pwm!(PWM0, PWM0, PWM0);\nimpl_pwm!(PWM1, PWM1, PWM1);\nimpl_pwm!(PWM2, PWM2, PWM2);\nimpl_pwm!(PWM3, PWM3, PWM3);\n\nimpl_pdm!(PDM, PDM, PDM);\n\n#[cfg(feature = \"_s\")]\nimpl_ccrng!(CC_RNG, CC_RNG, CRYPTOCELL);\n\nimpl_timer!(TIMER0, TIMER0, TIMER0);\nimpl_timer!(TIMER1, TIMER1, TIMER1);\nimpl_timer!(TIMER2, TIMER2, TIMER2);\n\nimpl_rtc!(RTC0, RTC0, RTC0);\n#[cfg(not(feature = \"time-driver-rtc1\"))]\nimpl_rtc!(RTC1, RTC1, RTC1);\n\nimpl_pin!(P0_00, 0, 0);\nimpl_pin!(P0_01, 0, 1);\nimpl_pin!(P0_02, 0, 2);\nimpl_pin!(P0_03, 0, 3);\nimpl_pin!(P0_04, 0, 4);\nimpl_pin!(P0_05, 0, 5);\nimpl_pin!(P0_06, 0, 6);\nimpl_pin!(P0_07, 0, 7);\nimpl_pin!(P0_08, 0, 8);\nimpl_pin!(P0_09, 0, 9);\nimpl_pin!(P0_10, 0, 10);\nimpl_pin!(P0_11, 0, 11);\nimpl_pin!(P0_12, 0, 12);\nimpl_pin!(P0_13, 0, 13);\nimpl_pin!(P0_14, 0, 14);\nimpl_pin!(P0_15, 0, 15);\nimpl_pin!(P0_16, 0, 16);\nimpl_pin!(P0_17, 0, 17);\nimpl_pin!(P0_18, 0, 18);\nimpl_pin!(P0_19, 0, 19);\nimpl_pin!(P0_20, 0, 20);\nimpl_pin!(P0_21, 0, 21);\nimpl_pin!(P0_22, 0, 22);\nimpl_pin!(P0_23, 0, 23);\nimpl_pin!(P0_24, 0, 24);\nimpl_pin!(P0_25, 0, 25);\nimpl_pin!(P0_26, 0, 26);\nimpl_pin!(P0_27, 0, 27);\nimpl_pin!(P0_28, 0, 28);\nimpl_pin!(P0_29, 0, 29);\nimpl_pin!(P0_30, 0, 30);\nimpl_pin!(P0_31, 0, 31);\n\nimpl_ppi_channel!(PPI_CH0, DPPIC, 0 => configurable);\nimpl_ppi_channel!(PPI_CH1, DPPIC, 1 => configurable);\nimpl_ppi_channel!(PPI_CH2, DPPIC, 2 => configurable);\nimpl_ppi_channel!(PPI_CH3, DPPIC, 3 => configurable);\nimpl_ppi_channel!(PPI_CH4, DPPIC, 4 => configurable);\nimpl_ppi_channel!(PPI_CH5, DPPIC, 5 => configurable);\nimpl_ppi_channel!(PPI_CH6, DPPIC, 6 => configurable);\nimpl_ppi_channel!(PPI_CH7, DPPIC, 7 => configurable);\nimpl_ppi_channel!(PPI_CH8, DPPIC, 8 => configurable);\nimpl_ppi_channel!(PPI_CH9, DPPIC, 9 => configurable);\nimpl_ppi_channel!(PPI_CH10, DPPIC, 10 => configurable);\nimpl_ppi_channel!(PPI_CH11, DPPIC, 11 => configurable);\nimpl_ppi_channel!(PPI_CH12, DPPIC, 12 => configurable);\nimpl_ppi_channel!(PPI_CH13, DPPIC, 13 => configurable);\nimpl_ppi_channel!(PPI_CH14, DPPIC, 14 => configurable);\nimpl_ppi_channel!(PPI_CH15, DPPIC, 15 => configurable);\n\nimpl_ppi_group!(PPI_GROUP0, DPPIC, 0);\nimpl_ppi_group!(PPI_GROUP1, DPPIC, 1);\nimpl_ppi_group!(PPI_GROUP2, DPPIC, 2);\nimpl_ppi_group!(PPI_GROUP3, DPPIC, 3);\nimpl_ppi_group!(PPI_GROUP4, DPPIC, 4);\nimpl_ppi_group!(PPI_GROUP5, DPPIC, 5);\n\nimpl_saadc_input!(P0_13, ANALOG_INPUT0);\nimpl_saadc_input!(P0_14, ANALOG_INPUT1);\nimpl_saadc_input!(P0_15, ANALOG_INPUT2);\nimpl_saadc_input!(P0_16, ANALOG_INPUT3);\nimpl_saadc_input!(P0_17, ANALOG_INPUT4);\nimpl_saadc_input!(P0_18, ANALOG_INPUT5);\nimpl_saadc_input!(P0_19, ANALOG_INPUT6);\nimpl_saadc_input!(P0_20, ANALOG_INPUT7);\n\nimpl_egu!(EGU0, EGU0, EGU0);\nimpl_egu!(EGU1, EGU1, EGU1);\nimpl_egu!(EGU2, EGU2, EGU2);\nimpl_egu!(EGU3, EGU3, EGU3);\nimpl_egu!(EGU4, EGU4, EGU4);\nimpl_egu!(EGU5, EGU5, EGU5);\n\nimpl_wdt!(WDT, WDT, WDT, 0);\n\nembassy_hal_internal::interrupt_mod!(\n    SPU,\n    CLOCK_POWER,\n    SERIAL0,\n    SERIAL1,\n    SERIAL2,\n    SERIAL3,\n    GPIOTE0,\n    SAADC,\n    TIMER0,\n    TIMER1,\n    TIMER2,\n    RTC0,\n    RTC1,\n    WDT,\n    EGU0,\n    EGU1,\n    EGU2,\n    EGU3,\n    EGU4,\n    EGU5,\n    PWM0,\n    PWM1,\n    PWM2,\n    PWM3,\n    PDM,\n    I2S,\n    IPC,\n    FPU,\n    GPIOTE1,\n    KMU,\n    CRYPTOCELL,\n);\n"
  },
  {
    "path": "embassy-nrf/src/chips/nrf9160.rs",
    "content": "/// Peripheral Access Crate\n#[allow(unused_imports)]\n#[rustfmt::skip]\npub mod pac {\n    pub use nrf_pac::*;\n\n    #[cfg(feature = \"_ns\")]\n    #[doc(no_inline)]\n    pub use nrf_pac::{\n        CLOCK_NS as CLOCK,\n        DPPIC_NS as DPPIC,\n        EGU0_NS as EGU0,\n        EGU1_NS as EGU1,\n        EGU2_NS as EGU2,\n        EGU3_NS as EGU3,\n        EGU4_NS as EGU4,\n        EGU5_NS as EGU5,\n        FPU_NS as FPU,\n        GPIOTE1_NS as GPIOTE1,\n        I2S_NS as I2S,\n        IPC_NS as IPC,\n        KMU_NS as KMU,\n        NVMC_NS as NVMC,\n        P0_NS as P0,\n        PDM_NS as PDM,\n        POWER_NS as POWER,\n        PWM0_NS as PWM0,\n        PWM1_NS as PWM1,\n        PWM2_NS as PWM2,\n        PWM3_NS as PWM3,\n        REGULATORS_NS as REGULATORS,\n        RTC0_NS as RTC0,\n        RTC1_NS as RTC1,\n        SAADC_NS as SAADC,\n        SPIM0_NS as SPIM0,\n        SPIM1_NS as SPIM1,\n        SPIM2_NS as SPIM2,\n        SPIM3_NS as SPIM3,\n        SPIS0_NS as SPIS0,\n        SPIS1_NS as SPIS1,\n        SPIS2_NS as SPIS2,\n        SPIS3_NS as SPIS3,\n        TIMER0_NS as TIMER0,\n        TIMER1_NS as TIMER1,\n        TIMER2_NS as TIMER2,\n        TWIM0_NS as TWIM0,\n        TWIM1_NS as TWIM1,\n        TWIM2_NS as TWIM2,\n        TWIM3_NS as TWIM3,\n        TWIS0_NS as TWIS0,\n        TWIS1_NS as TWIS1,\n        TWIS2_NS as TWIS2,\n        TWIS3_NS as TWIS3,\n        UARTE0_NS as UARTE0,\n        UARTE1_NS as UARTE1,\n        UARTE2_NS as UARTE2,\n        UARTE3_NS as UARTE3,\n        VMC_NS as VMC,\n        WDT_NS as WDT,\n    };\n\n    #[cfg(feature = \"_s\")]\n    #[doc(no_inline)]\n    pub use nrf_pac::{\n        CC_HOST_RGF_S as CC_HOST_RGF,\n        CC_RNG_S as CC_RNG,\n        CLOCK_S as CLOCK,\n        CRYPTOCELL_S as CRYPTOCELL,\n        CTRL_AP_PERI_S as CTRL_AP_PERI,\n        DPPIC_S as DPPIC,\n        EGU0_S as EGU0,\n        EGU1_S as EGU1,\n        EGU2_S as EGU2,\n        EGU3_S as EGU3,\n        EGU4_S as EGU4,\n        EGU5_S as EGU5,\n        FICR_S as FICR,\n        FPU_NS as FPU,\n        GPIOTE0_S as GPIOTE0,\n        I2S_S as I2S,\n        IPC_S as IPC,\n        KMU_S as KMU,\n        NVMC_S as NVMC,\n        P0_S as P0,\n        PDM_S as PDM,\n        POWER_S as POWER,\n        PWM0_S as PWM0,\n        PWM1_S as PWM1,\n        PWM2_S as PWM2,\n        PWM3_S as PWM3,\n        REGULATORS_S as REGULATORS,\n        RTC0_S as RTC0,\n        RTC1_S as RTC1,\n        SAADC_S as SAADC,\n        SPIM0_S as SPIM0,\n        SPIM1_S as SPIM1,\n        SPIM2_S as SPIM2,\n        SPIM3_S as SPIM3,\n        SPIS0_S as SPIS0,\n        SPIS1_S as SPIS1,\n        SPIS2_S as SPIS2,\n        SPIS3_S as SPIS3,\n        SPU_S as SPU,\n        TAD_S as TAD,\n        TIMER0_S as TIMER0,\n        TIMER1_S as TIMER1,\n        TIMER2_S as TIMER2,\n        TWIM0_S as TWIM0,\n        TWIM1_S as TWIM1,\n        TWIM2_S as TWIM2,\n        TWIM3_S as TWIM3,\n        TWIS0_S as TWIS0,\n        TWIS1_S as TWIS1,\n        TWIS2_S as TWIS2,\n        TWIS3_S as TWIS3,\n        UARTE0_S as UARTE0,\n        UARTE1_S as UARTE1,\n        UARTE2_S as UARTE2,\n        UARTE3_S as UARTE3,\n        UICR_S as UICR,\n        VMC_S as VMC,\n        WDT_S as WDT,\n    };\n}\n\n/// The maximum buffer size that the EasyDMA can send/recv in one operation.\npub const EASY_DMA_SIZE: usize = (1 << 13) - 1;\npub const FORCE_COPY_BUFFER_SIZE: usize = 1024;\n\npub const FLASH_SIZE: usize = 1024 * 1024;\n\nembassy_hal_internal::peripherals! {\n    // RTC\n    RTC0,\n    #[cfg(not(feature = \"time-driver-rtc1\"))]\n    RTC1,\n\n    // WDT\n    WDT,\n\n    // NVMC\n    NVMC,\n\n    // UARTE, TWI & SPI\n    SERIAL0,\n    SERIAL1,\n    SERIAL2,\n    SERIAL3,\n\n    // SAADC\n    SAADC,\n\n    // PWM\n    PWM0,\n    PWM1,\n    PWM2,\n    PWM3,\n\n    // TIMER\n    TIMER0,\n    TIMER1,\n    TIMER2,\n\n    // GPIOTE\n    GPIOTE_CH0,\n    GPIOTE_CH1,\n    GPIOTE_CH2,\n    GPIOTE_CH3,\n    GPIOTE_CH4,\n    GPIOTE_CH5,\n    GPIOTE_CH6,\n    GPIOTE_CH7,\n\n    // PPI\n    PPI_CH0,\n    PPI_CH1,\n    PPI_CH2,\n    PPI_CH3,\n    PPI_CH4,\n    PPI_CH5,\n    PPI_CH6,\n    PPI_CH7,\n    PPI_CH8,\n    PPI_CH9,\n    PPI_CH10,\n    PPI_CH11,\n    PPI_CH12,\n    PPI_CH13,\n    PPI_CH14,\n    PPI_CH15,\n\n    PPI_GROUP0,\n    PPI_GROUP1,\n    PPI_GROUP2,\n    PPI_GROUP3,\n    PPI_GROUP4,\n    PPI_GROUP5,\n\n    // GPIO port 0\n    P0_00,\n    P0_01,\n    P0_02,\n    P0_03,\n    P0_04,\n    P0_05,\n    P0_06,\n    P0_07,\n    P0_08,\n    P0_09,\n    P0_10,\n    P0_11,\n    P0_12,\n    P0_13,\n    P0_14,\n    P0_15,\n    P0_16,\n    P0_17,\n    P0_18,\n    P0_19,\n    P0_20,\n    P0_21,\n    P0_22,\n    P0_23,\n    P0_24,\n    P0_25,\n    P0_26,\n    P0_27,\n    P0_28,\n    P0_29,\n    P0_30,\n    P0_31,\n\n    // PDM\n    PDM,\n\n    // EGU\n    EGU0,\n    EGU1,\n    EGU2,\n    EGU3,\n    EGU4,\n    EGU5,\n\n    // CryptoCell RNG\n    #[cfg(feature = \"_s\")]\n    CC_RNG\n}\n\nimpl_uarte!(SERIAL0, UARTE0, SERIAL0);\nimpl_uarte!(SERIAL1, UARTE1, SERIAL1);\nimpl_uarte!(SERIAL2, UARTE2, SERIAL2);\nimpl_uarte!(SERIAL3, UARTE3, SERIAL3);\n\nimpl_spim!(SERIAL0, SPIM0, SERIAL0);\nimpl_spim!(SERIAL1, SPIM1, SERIAL1);\nimpl_spim!(SERIAL2, SPIM2, SERIAL2);\nimpl_spim!(SERIAL3, SPIM3, SERIAL3);\n\nimpl_spis!(SERIAL0, SPIS0, SERIAL0);\nimpl_spis!(SERIAL1, SPIS1, SERIAL1);\nimpl_spis!(SERIAL2, SPIS2, SERIAL2);\nimpl_spis!(SERIAL3, SPIS3, SERIAL3);\n\nimpl_twim!(SERIAL0, TWIM0, SERIAL0);\nimpl_twim!(SERIAL1, TWIM1, SERIAL1);\nimpl_twim!(SERIAL2, TWIM2, SERIAL2);\nimpl_twim!(SERIAL3, TWIM3, SERIAL3);\n\nimpl_twis!(SERIAL0, TWIS0, SERIAL0);\nimpl_twis!(SERIAL1, TWIS1, SERIAL1);\nimpl_twis!(SERIAL2, TWIS2, SERIAL2);\nimpl_twis!(SERIAL3, TWIS3, SERIAL3);\n\nimpl_pwm!(PWM0, PWM0, PWM0);\nimpl_pwm!(PWM1, PWM1, PWM1);\nimpl_pwm!(PWM2, PWM2, PWM2);\nimpl_pwm!(PWM3, PWM3, PWM3);\n\nimpl_pdm!(PDM, PDM, PDM);\n\n#[cfg(feature = \"_s\")]\nimpl_ccrng!(CC_RNG, CC_RNG, CRYPTOCELL);\n\nimpl_timer!(TIMER0, TIMER0, TIMER0);\nimpl_timer!(TIMER1, TIMER1, TIMER1);\nimpl_timer!(TIMER2, TIMER2, TIMER2);\n\nimpl_rtc!(RTC0, RTC0, RTC0);\n#[cfg(not(feature = \"time-driver-rtc1\"))]\nimpl_rtc!(RTC1, RTC1, RTC1);\n\nimpl_pin!(P0_00, 0, 0);\nimpl_pin!(P0_01, 0, 1);\nimpl_pin!(P0_02, 0, 2);\nimpl_pin!(P0_03, 0, 3);\nimpl_pin!(P0_04, 0, 4);\nimpl_pin!(P0_05, 0, 5);\nimpl_pin!(P0_06, 0, 6);\nimpl_pin!(P0_07, 0, 7);\nimpl_pin!(P0_08, 0, 8);\nimpl_pin!(P0_09, 0, 9);\nimpl_pin!(P0_10, 0, 10);\nimpl_pin!(P0_11, 0, 11);\nimpl_pin!(P0_12, 0, 12);\nimpl_pin!(P0_13, 0, 13);\nimpl_pin!(P0_14, 0, 14);\nimpl_pin!(P0_15, 0, 15);\nimpl_pin!(P0_16, 0, 16);\nimpl_pin!(P0_17, 0, 17);\nimpl_pin!(P0_18, 0, 18);\nimpl_pin!(P0_19, 0, 19);\nimpl_pin!(P0_20, 0, 20);\nimpl_pin!(P0_21, 0, 21);\nimpl_pin!(P0_22, 0, 22);\nimpl_pin!(P0_23, 0, 23);\nimpl_pin!(P0_24, 0, 24);\nimpl_pin!(P0_25, 0, 25);\nimpl_pin!(P0_26, 0, 26);\nimpl_pin!(P0_27, 0, 27);\nimpl_pin!(P0_28, 0, 28);\nimpl_pin!(P0_29, 0, 29);\nimpl_pin!(P0_30, 0, 30);\nimpl_pin!(P0_31, 0, 31);\n\nimpl_ppi_channel!(PPI_CH0, DPPIC, 0 => configurable);\nimpl_ppi_channel!(PPI_CH1, DPPIC, 1 => configurable);\nimpl_ppi_channel!(PPI_CH2, DPPIC, 2 => configurable);\nimpl_ppi_channel!(PPI_CH3, DPPIC, 3 => configurable);\nimpl_ppi_channel!(PPI_CH4, DPPIC, 4 => configurable);\nimpl_ppi_channel!(PPI_CH5, DPPIC, 5 => configurable);\nimpl_ppi_channel!(PPI_CH6, DPPIC, 6 => configurable);\nimpl_ppi_channel!(PPI_CH7, DPPIC, 7 => configurable);\nimpl_ppi_channel!(PPI_CH8, DPPIC, 8 => configurable);\nimpl_ppi_channel!(PPI_CH9, DPPIC, 9 => configurable);\nimpl_ppi_channel!(PPI_CH10, DPPIC, 10 => configurable);\nimpl_ppi_channel!(PPI_CH11, DPPIC, 11 => configurable);\nimpl_ppi_channel!(PPI_CH12, DPPIC, 12 => configurable);\nimpl_ppi_channel!(PPI_CH13, DPPIC, 13 => configurable);\nimpl_ppi_channel!(PPI_CH14, DPPIC, 14 => configurable);\nimpl_ppi_channel!(PPI_CH15, DPPIC, 15 => configurable);\n\nimpl_ppi_group!(PPI_GROUP0, DPPIC, 0);\nimpl_ppi_group!(PPI_GROUP1, DPPIC, 1);\nimpl_ppi_group!(PPI_GROUP2, DPPIC, 2);\nimpl_ppi_group!(PPI_GROUP3, DPPIC, 3);\nimpl_ppi_group!(PPI_GROUP4, DPPIC, 4);\nimpl_ppi_group!(PPI_GROUP5, DPPIC, 5);\n\nimpl_saadc_input!(P0_13, ANALOG_INPUT0);\nimpl_saadc_input!(P0_14, ANALOG_INPUT1);\nimpl_saadc_input!(P0_15, ANALOG_INPUT2);\nimpl_saadc_input!(P0_16, ANALOG_INPUT3);\nimpl_saadc_input!(P0_17, ANALOG_INPUT4);\nimpl_saadc_input!(P0_18, ANALOG_INPUT5);\nimpl_saadc_input!(P0_19, ANALOG_INPUT6);\nimpl_saadc_input!(P0_20, ANALOG_INPUT7);\n\nimpl_egu!(EGU0, EGU0, EGU0);\nimpl_egu!(EGU1, EGU1, EGU1);\nimpl_egu!(EGU2, EGU2, EGU2);\nimpl_egu!(EGU3, EGU3, EGU3);\nimpl_egu!(EGU4, EGU4, EGU4);\nimpl_egu!(EGU5, EGU5, EGU5);\n\nimpl_wdt!(WDT, WDT, WDT, 0);\n\nembassy_hal_internal::interrupt_mod!(\n    SPU,\n    CLOCK_POWER,\n    SERIAL0,\n    SERIAL1,\n    SERIAL2,\n    SERIAL3,\n    GPIOTE0,\n    SAADC,\n    TIMER0,\n    TIMER1,\n    TIMER2,\n    RTC0,\n    RTC1,\n    WDT,\n    EGU0,\n    EGU1,\n    EGU2,\n    EGU3,\n    EGU4,\n    EGU5,\n    PWM0,\n    PWM1,\n    PWM2,\n    PWM3,\n    PDM,\n    I2S,\n    IPC,\n    FPU,\n    GPIOTE1,\n    KMU,\n    CRYPTOCELL,\n);\n"
  },
  {
    "path": "embassy-nrf/src/cracen.rs",
    "content": "//! CRACEN - Cryptographic Accelerator Engine driver.\n\n#![macro_use]\n\nuse core::marker::PhantomData;\n\nuse crate::mode::{Blocking, Mode};\nuse crate::{Peri, interrupt, pac, peripherals};\n\n/// A wrapper around an nRF54 CRACEN peripheral.\n///\n/// It has a blocking api through `rand`.\npub struct Cracen<'d, M: Mode> {\n    _peri: Peri<'d, peripherals::CRACEN>,\n    _p: PhantomData<M>,\n}\n\nimpl<'d> Cracen<'d, Blocking> {\n    /// Create a new CRACEN driver.\n    pub fn new_blocking(_peri: Peri<'d, peripherals::CRACEN>) -> Self {\n        Self { _peri, _p: PhantomData }\n    }\n}\n\nimpl<'d, M: Mode> Cracen<'d, M> {\n    fn regs() -> pac::cracen::Cracen {\n        pac::CRACEN\n    }\n\n    fn core() -> pac::cracencore::Cracencore {\n        pac::CRACENCORE\n    }\n\n    fn start_rng(&self) {\n        let r = Self::regs();\n        r.enable().write(|w| {\n            w.set_rng(true);\n        });\n\n        let r = Self::core();\n        r.rngcontrol().control().write(|w| {\n            w.set_enable(true);\n        });\n\n        while r.rngcontrol().status().read().state() == pac::cracencore::vals::State::STARTUP {}\n    }\n\n    fn stop_rng(&self) {\n        let r = Self::core();\n        r.rngcontrol().control().write(|w| {\n            w.set_enable(false);\n        });\n\n        while r.rngcontrol().status().read().state() != pac::cracencore::vals::State::RESET {}\n\n        let r = Self::regs();\n        r.enable().write(|w| {\n            w.set_cryptomaster(false);\n            w.set_rng(false);\n            w.set_pkeikg(false);\n        });\n    }\n\n    /// Fill the buffer with random bytes, blocking version.\n    pub fn blocking_fill_bytes(&mut self, dest: &mut [u8]) {\n        self.start_rng();\n\n        let r = Self::core();\n        for chunk in dest.chunks_mut(4) {\n            while r.rngcontrol().fifolevel().read() == 0 {}\n            let word = r.rngcontrol().fifo(0).read().to_ne_bytes();\n            let to_copy = word.len().min(chunk.len());\n            chunk[..to_copy].copy_from_slice(&word[..to_copy]);\n        }\n\n        self.stop_rng();\n    }\n\n    /// Generate a random u32\n    pub fn blocking_next_u32(&mut self) -> u32 {\n        let mut bytes = [0; 4];\n        self.blocking_fill_bytes(&mut bytes);\n        // We don't care about the endianness, so just use the native one.\n        u32::from_ne_bytes(bytes)\n    }\n\n    /// Generate a random u64\n    pub fn blocking_next_u64(&mut self) -> u64 {\n        let mut bytes = [0; 8];\n        self.blocking_fill_bytes(&mut bytes);\n        u64::from_ne_bytes(bytes)\n    }\n}\n\nimpl<'d, M: Mode> Drop for Cracen<'d, M> {\n    fn drop(&mut self) {\n        // nothing to do here, since we stop+disable rng for each operation.\n    }\n}\n\nimpl<'d, M: Mode> rand_core_06::RngCore for Cracen<'d, M> {\n    fn fill_bytes(&mut self, dest: &mut [u8]) {\n        self.blocking_fill_bytes(dest);\n    }\n    fn next_u32(&mut self) -> u32 {\n        self.blocking_next_u32()\n    }\n    fn next_u64(&mut self) -> u64 {\n        self.blocking_next_u64()\n    }\n    fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), rand_core_06::Error> {\n        self.blocking_fill_bytes(dest);\n        Ok(())\n    }\n}\n\nimpl<'d, M: Mode> rand_core_06::CryptoRng for Cracen<'d, M> {}\n\nimpl<'d, M: Mode> rand_core_09::RngCore for Cracen<'d, M> {\n    fn fill_bytes(&mut self, dest: &mut [u8]) {\n        self.blocking_fill_bytes(dest);\n    }\n    fn next_u32(&mut self) -> u32 {\n        self.blocking_next_u32()\n    }\n    fn next_u64(&mut self) -> u64 {\n        self.blocking_next_u64()\n    }\n}\n\nimpl<'d, M: Mode> rand_core_09::CryptoRng for Cracen<'d, M> {}\n\npub(crate) trait SealedInstance {}\n\n/// CRACEN peripheral instance.\n#[allow(private_bounds)]\npub trait Instance: SealedInstance + 'static + Send {\n    /// Interrupt for this peripheral.\n    type Interrupt: interrupt::typelevel::Interrupt;\n}\n\nmacro_rules! impl_cracen {\n    ($type:ident, $pac_type:ident, $irq:ident) => {\n        impl crate::cracen::SealedInstance for peripherals::$type {}\n        impl crate::cracen::Instance for peripherals::$type {\n            type Interrupt = crate::interrupt::typelevel::$irq;\n        }\n    };\n}\n"
  },
  {
    "path": "embassy-nrf/src/cryptocell_rng.rs",
    "content": "//! Random Number Generator (RNG) driver.\n\n#![macro_use]\n\nuse core::cell::{RefCell, RefMut};\nuse core::future::poll_fn;\nuse core::marker::PhantomData;\nuse core::ptr;\nuse core::task::Poll;\n\nuse critical_section::{CriticalSection, Mutex};\n#[cfg(feature = \"_nrf5340-app\")]\nuse embassy_futures::{select::select, yield_now};\nuse embassy_hal_internal::drop::OnDrop;\nuse embassy_hal_internal::{Peri, PeripheralType};\nuse embassy_sync::waitqueue::WakerRegistration;\n\nuse crate::interrupt::typelevel::Interrupt;\nuse crate::mode::{Async, Blocking, Mode};\nuse crate::{interrupt, pac};\n\n/// Interrupt handler.\npub struct InterruptHandler<T: Instance> {\n    _phantom: PhantomData<T>,\n}\n\nimpl<T: Instance> interrupt::typelevel::Handler<T::Interrupt> for InterruptHandler<T> {\n    unsafe fn on_interrupt() {\n        let r = T::regs();\n\n        // Clear the event.\n        r.rng_icr().write(|w| w.set_ehr_valid_clear(true));\n        pac::CC_HOST_RGF.icr().write(|w| w.set_rng_clear(true));\n\n        // Mutate the slice within a critical section,\n        // so that the future isn't dropped in between us loading the pointer and actually dereferencing it.\n        critical_section::with(|cs| {\n            let mut state = T::state().borrow_mut(cs);\n            // We need to make sure we haven't already filled the whole slice,\n            // in case the interrupt fired again before the executor got back to the future.\n            if !state.ptr.is_null() && state.ptr != state.end {\n                // If the future was dropped, the pointer would have been set to null,\n                // so we're still good to mutate the slice.\n                // The safety contract of `CcRng::new` means that the future can't have been dropped\n                // without calling its destructor.\n\n                for i in 0..6 {\n                    let bytes = r.ehr_data(i).read().to_ne_bytes();\n                    for b in bytes {\n                        unsafe {\n                            *state.ptr = b;\n                            state.ptr = state.ptr.add(1);\n                        }\n\n                        if state.ptr == state.end {\n                            state.waker.wake();\n                            return;\n                        }\n                    }\n                }\n            }\n        });\n    }\n}\n\n/// A wrapper around an nRF CryptoCell RNG peripheral.\n///\n/// It has a non-blocking API, and a blocking api through `rand`.\npub struct CcRng<'d, M: Mode> {\n    r: pac::cc_rng::CcRng,\n    state: &'static State,\n    _phantom: PhantomData<(&'d (), M)>,\n}\n\nimpl<'d> CcRng<'d, Blocking> {\n    /// Creates a new RNG driver from the `CC_RNG` peripheral and interrupt.\n    ///\n    /// SAFETY: The future returned from `fill_bytes` must not have its lifetime end without running its destructor,\n    /// e.g. using `mem::forget`.\n    ///\n    /// The synchronous API is safe.\n    pub fn new_blocking<T: Instance>(_rng: Peri<'d, T>) -> Self {\n        let this = Self {\n            r: T::regs(),\n            state: T::state(),\n            _phantom: PhantomData,\n        };\n\n        this.stop();\n\n        this\n    }\n}\n\nimpl<'d> CcRng<'d, Async> {\n    /// Creates a new RNG driver from the `CC_RNG` peripheral and interrupt.\n    ///\n    /// SAFETY: The future returned from `fill_bytes` must not have its lifetime end without running its destructor,\n    /// e.g. using `mem::forget`.\n    ///\n    /// The synchronous API is safe.\n    pub fn new<T: Instance>(\n        _rng: Peri<'d, T>,\n        _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd,\n    ) -> Self {\n        let this = Self {\n            r: T::regs(),\n            state: T::state(),\n            _phantom: PhantomData,\n        };\n\n        this.disable_irq();\n        this.stop();\n\n        T::Interrupt::unpend();\n\n        unsafe { T::Interrupt::enable() };\n\n        this\n    }\n\n    fn enable_irq(&self) {\n        pac::CC_HOST_RGF\n            .imr()\n            .modify(|w| w.set_rng_mask(pac::cc_host_rgf::vals::RngMask::IRQENABLE));\n        self.r\n            .rng_imr()\n            .modify(|w| w.set_ehr_valid_mask(pac::cc_rng::vals::EhrValidMask::IRQENABLE));\n    }\n\n    fn disable_irq(&self) {\n        self.r.rng_icr().write(|w| w.set_ehr_valid_clear(true));\n        pac::CC_HOST_RGF.icr().write(|w| w.set_rng_clear(true));\n        self.r\n            .rng_imr()\n            .modify(|w| w.set_ehr_valid_mask(pac::cc_rng::vals::EhrValidMask::IRQDISABLE));\n        pac::CC_HOST_RGF\n            .imr()\n            .modify(|w| w.set_rng_mask(pac::cc_host_rgf::vals::RngMask::IRQDISABLE));\n    }\n\n    /// Fill the buffer with random bytes.\n    pub async fn fill_bytes(&mut self, dest: &mut [u8]) {\n        if dest.is_empty() {\n            return; // Nothing to fill\n        }\n\n        let range = dest.as_mut_ptr_range();\n\n        let state = self.state;\n        // Even if we've preempted the interrupt, it can't preempt us again,\n        // so we don't need to worry about the order we write these in.\n        critical_section::with(|cs| {\n            let mut state = state.borrow_mut(cs);\n            state.ptr = range.start;\n            state.end = range.end;\n        });\n\n        // In self.start() there are calls to set_enable() that resets the interrupt mask,\n        // self.enable_irq() needs to be called after self.start().\n        self.start();\n\n        self.enable_irq();\n\n        let on_drop = OnDrop::new(|| {\n            self.disable_irq();\n            self.stop();\n\n            critical_section::with(|cs| {\n                let mut state = state.borrow_mut(cs);\n                state.ptr = ptr::null_mut();\n                state.end = ptr::null_mut();\n            });\n        });\n\n        let fill_future = poll_fn(|cx| {\n            critical_section::with(|cs| {\n                let mut s = state.borrow_mut(cs);\n                s.waker.register(cx.waker());\n                if s.ptr == s.end {\n                    // We're done.\n                    Poll::Ready(())\n                } else {\n                    Poll::Pending\n                }\n            })\n        });\n\n        // nrf5340 needs to be reading from the CryptoCell in order to receive an interrupt from it.\n        #[cfg(feature = \"_nrf5340-app\")]\n        let _ = select(fill_future, async {\n            loop {\n                let _ = pac::CRYPTOCELL.enable().read().enable();\n                yield_now().await;\n            }\n        })\n        .await;\n        #[cfg(not(feature = \"_nrf5340-app\"))]\n        fill_future.await;\n\n        // Trigger the teardown\n        drop(on_drop);\n    }\n}\n\nimpl<'d, M: Mode> CcRng<'d, M> {\n    fn start(&self) {\n        // FIXME: CRYPTOCELL is never disabled.\n        if !pac::CRYPTOCELL.enable().read().enable() {\n            pac::CRYPTOCELL.enable().write(|w| w.set_enable(true));\n        }\n\n        self.r.rng_clk().write(|w| w.set_enable(true));\n        self.r.rng_sw_reset().write(|w| w.set_reset(true));\n\n        // Wait for calibration\n        // ROSC1 (ring oscillator lenght) chosen arbitrarly, can be later exposed as configuration.\n        loop {\n            self.r.rng_clk().write(|w| w.set_enable(true));\n            self.r.sample_cnt().write_value(pac::FICR.trng90b().rosc1().read());\n            if self.r.sample_cnt().read() == pac::FICR.trng90b().rosc1().read() {\n                break;\n            };\n        }\n        self.r\n            .trng_config()\n            .modify(|w| w.set_rosc_len(pac::cc_rng::vals::TrngConfigRoscLen::ROSC1));\n        self.r.noise_source().modify(|w| w.set_enable(true));\n    }\n\n    fn stop(&self) {\n        self.r.noise_source().modify(|w| w.set_enable(false));\n\n        self.r.rng_clk().write(|w| w.set_enable(false));\n    }\n\n    /// Fill the buffer with random bytes, blocking version.\n    pub fn blocking_fill_bytes(&mut self, dest: &mut [u8]) {\n        self.start();\n        self.inner_fill_bytes(dest);\n        self.stop();\n    }\n\n    // inner function so we can use `return` to end all the loops\n    fn inner_fill_bytes(&mut self, dest: &mut [u8]) {\n        let mut index = 0;\n        while index < dest.len() {\n            while !self.r.rng_isr().read().ehr_valid_int() {}\n            self.r.rng_icr().write(|w| w.set_ehr_valid_clear(true));\n\n            for i in 0..6 {\n                let bytes = self.r.ehr_data(i).read().to_ne_bytes();\n                for b in bytes {\n                    dest[index] = b;\n                    index += 1;\n\n                    if index >= dest.len() {\n                        return;\n                    }\n                }\n            }\n        }\n    }\n\n    /// Generate a random u32\n    pub fn blocking_next_u32(&mut self) -> u32 {\n        let mut bytes = [0; 4];\n        self.blocking_fill_bytes(&mut bytes);\n        // We don't care about the endianness, so just use the native one.\n        u32::from_ne_bytes(bytes)\n    }\n\n    /// Generate a random u64\n    pub fn blocking_next_u64(&mut self) -> u64 {\n        let mut bytes = [0; 8];\n        self.blocking_fill_bytes(&mut bytes);\n        u64::from_ne_bytes(bytes)\n    }\n}\n\nimpl<'d, M: Mode> Drop for CcRng<'d, M> {\n    fn drop(&mut self) {\n        self.stop();\n        critical_section::with(|cs| {\n            let mut state = self.state.borrow_mut(cs);\n            state.ptr = ptr::null_mut();\n            state.end = ptr::null_mut();\n        });\n    }\n}\n\nimpl<'d, M: Mode> rand_core_06::RngCore for CcRng<'d, M> {\n    fn fill_bytes(&mut self, dest: &mut [u8]) {\n        self.blocking_fill_bytes(dest);\n    }\n    fn next_u32(&mut self) -> u32 {\n        self.blocking_next_u32()\n    }\n    fn next_u64(&mut self) -> u64 {\n        self.blocking_next_u64()\n    }\n    fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), rand_core_06::Error> {\n        self.blocking_fill_bytes(dest);\n        Ok(())\n    }\n}\n\nimpl<'d, M: Mode> rand_core_06::CryptoRng for CcRng<'d, M> {}\n\nimpl<'d, M: Mode> rand_core_09::RngCore for CcRng<'d, M> {\n    fn fill_bytes(&mut self, dest: &mut [u8]) {\n        self.blocking_fill_bytes(dest);\n    }\n    fn next_u32(&mut self) -> u32 {\n        self.blocking_next_u32()\n    }\n    fn next_u64(&mut self) -> u64 {\n        self.blocking_next_u64()\n    }\n}\n\nimpl<'d, M: Mode> rand_core_09::CryptoRng for CcRng<'d, M> {}\n\n/// Peripheral static state\npub(crate) struct State {\n    inner: Mutex<RefCell<InnerState>>,\n}\n\nstruct InnerState {\n    ptr: *mut u8,\n    end: *mut u8,\n    waker: WakerRegistration,\n}\n\nunsafe impl Send for InnerState {}\n\nimpl State {\n    pub(crate) const fn new() -> Self {\n        Self {\n            inner: Mutex::new(RefCell::new(InnerState::new())),\n        }\n    }\n\n    fn borrow_mut<'cs>(&'cs self, cs: CriticalSection<'cs>) -> RefMut<'cs, InnerState> {\n        self.inner.borrow(cs).borrow_mut()\n    }\n}\n\nimpl InnerState {\n    const fn new() -> Self {\n        Self {\n            ptr: ptr::null_mut(),\n            end: ptr::null_mut(),\n            waker: WakerRegistration::new(),\n        }\n    }\n}\n\npub(crate) trait SealedInstance {\n    fn regs() -> pac::cc_rng::CcRng;\n    fn state() -> &'static State;\n}\n\n/// RNG peripheral instance.\n#[allow(private_bounds)]\npub trait Instance: SealedInstance + PeripheralType + 'static + Send {\n    /// Interrupt for this peripheral.\n    type Interrupt: interrupt::typelevel::Interrupt;\n}\n\nmacro_rules! impl_ccrng {\n    ($type:ident, $pac_type:ident, $irq:ident) => {\n        impl crate::cryptocell_rng::SealedInstance for peripherals::$type {\n            fn regs() -> pac::cc_rng::CcRng {\n                pac::$pac_type\n            }\n            fn state() -> &'static crate::cryptocell_rng::State {\n                static STATE: crate::cryptocell_rng::State = crate::cryptocell_rng::State::new();\n                &STATE\n            }\n        }\n        impl crate::cryptocell_rng::Instance for peripherals::$type {\n            type Interrupt = crate::interrupt::typelevel::$irq;\n        }\n    };\n}\n"
  },
  {
    "path": "embassy-nrf/src/egu.rs",
    "content": "//! EGU driver.\n//!\n//! The event generator driver provides a higher level API for task triggering\n//! and events to use with PPI.\n\n#![macro_use]\n\nuse core::marker::PhantomData;\n\nuse embassy_hal_internal::PeripheralType;\n\nuse crate::ppi::{Event, Task};\nuse crate::{Peri, interrupt, pac};\n\n/// An instance of the EGU.\npub struct Egu<'d> {\n    r: pac::egu::Egu,\n    _phantom: PhantomData<&'d ()>,\n}\n\nimpl<'d> Egu<'d> {\n    /// Create a new EGU instance.\n    pub fn new<T: Instance>(_p: Peri<'d, T>) -> Self {\n        Self {\n            r: T::regs(),\n            _phantom: PhantomData,\n        }\n    }\n\n    /// Get a handle to a trigger for the EGU.\n    pub fn trigger(&mut self, number: TriggerNumber) -> Trigger<'d> {\n        Trigger {\n            number,\n            r: self.r,\n            _phantom: PhantomData,\n        }\n    }\n}\n\npub(crate) trait SealedInstance {\n    fn regs() -> pac::egu::Egu;\n}\n\n/// Basic Egu instance.\n#[allow(private_bounds)]\npub trait Instance: SealedInstance + PeripheralType + 'static + Send {\n    /// Interrupt for this peripheral.\n    type Interrupt: interrupt::typelevel::Interrupt;\n}\n\nmacro_rules! impl_egu {\n    ($type:ident, $pac_type:ident, $irq:ident) => {\n        impl crate::egu::SealedInstance for peripherals::$type {\n            fn regs() -> pac::egu::Egu {\n                pac::$pac_type\n            }\n        }\n        impl crate::egu::Instance for peripherals::$type {\n            type Interrupt = crate::interrupt::typelevel::$irq;\n        }\n    };\n}\n\n/// Represents a trigger within the EGU.\npub struct Trigger<'d> {\n    number: TriggerNumber,\n    r: pac::egu::Egu,\n    _phantom: PhantomData<&'d ()>,\n}\n\nimpl<'d> Trigger<'d> {\n    /// Get task for this trigger to use with PPI.\n    pub fn task(&self) -> Task<'d> {\n        let nr = self.number as usize;\n        Task::from_reg(self.r.tasks_trigger(nr))\n    }\n\n    /// Get event for this trigger to use with PPI.\n    pub fn event(&self) -> Event<'d> {\n        let nr = self.number as usize;\n        Event::from_reg(self.r.events_triggered(nr))\n    }\n\n    /// Enable interrupts for this trigger\n    pub fn enable_interrupt(&mut self) {\n        self.r\n            .intenset()\n            .modify(|w| w.set_triggered(self.number as usize, true));\n    }\n\n    /// Enable interrupts for this trigger\n    pub fn disable_interrupt(&mut self) {\n        self.r\n            .intenset()\n            .modify(|w| w.set_triggered(self.number as usize, false));\n    }\n}\n\n/// Represents a trigger within an EGU.\n#[allow(missing_docs)]\n#[derive(Clone, Copy, PartialEq)]\n#[repr(u8)]\npub enum TriggerNumber {\n    Trigger0 = 0,\n    Trigger1 = 1,\n    Trigger2 = 2,\n    Trigger3 = 3,\n    Trigger4 = 4,\n    Trigger5 = 5,\n    Trigger6 = 6,\n    Trigger7 = 7,\n    Trigger8 = 8,\n    Trigger9 = 9,\n    Trigger10 = 10,\n    Trigger11 = 11,\n    Trigger12 = 12,\n    Trigger13 = 13,\n    Trigger14 = 14,\n    Trigger15 = 15,\n}\n"
  },
  {
    "path": "embassy-nrf/src/embassy_net_802154_driver.rs",
    "content": "//! embassy-net IEEE 802.15.4 driver\n\nuse embassy_futures::select::{Either3, select3};\nuse embassy_net_driver_channel::driver::LinkState;\nuse embassy_net_driver_channel::{self as ch};\nuse embassy_time::{Duration, Ticker};\n\nuse crate::radio::InterruptHandler;\nuse crate::radio::ieee802154::{Packet, Radio};\nuse crate::{self as nrf, interrupt};\n\n/// MTU for the nrf radio.\npub const MTU: usize = Packet::CAPACITY as usize;\n\n/// embassy-net device for the driver.\npub type Device<'d> = embassy_net_driver_channel::Device<'d, MTU>;\n\n/// Internal state for the embassy-net driver.\npub struct State<const N_RX: usize, const N_TX: usize> {\n    ch_state: ch::State<MTU, N_RX, N_TX>,\n}\n\nimpl<const N_RX: usize, const N_TX: usize> State<N_RX, N_TX> {\n    /// Create a new `State`.\n    pub const fn new() -> Self {\n        Self {\n            ch_state: ch::State::new(),\n        }\n    }\n}\n\n/// Background runner for the driver.\n///\n/// You must call `.run()` in a background task for the driver to operate.\npub struct Runner<'d> {\n    radio: nrf::radio::ieee802154::Radio<'d>,\n    ch: ch::Runner<'d, MTU>,\n}\n\nimpl<'d> Runner<'d> {\n    /// Drives the radio. Needs to run to use the driver.\n    pub async fn run(mut self) -> ! {\n        let (state_chan, mut rx_chan, mut tx_chan) = self.ch.split();\n        let mut tick = Ticker::every(Duration::from_millis(500));\n        let mut packet = Packet::new();\n        state_chan.set_link_state(LinkState::Up);\n        loop {\n            match select3(\n                async {\n                    let rx_buf = rx_chan.rx_buf().await;\n                    self.radio.receive(&mut packet).await.ok().map(|_| rx_buf)\n                },\n                tx_chan.tx_buf(),\n                tick.next(),\n            )\n            .await\n            {\n                Either3::First(Some(rx_buf)) => {\n                    let len = rx_buf.len().min(packet.len() as usize);\n                    (&mut rx_buf[..len]).copy_from_slice(&*packet);\n                    rx_chan.rx_done(len);\n                }\n                Either3::Second(tx_buf) => {\n                    let len = tx_buf.len().min(Packet::CAPACITY as usize);\n                    packet.copy_from_slice(&tx_buf[..len]);\n                    self.radio.try_send(&mut packet).await.ok().unwrap();\n                    tx_chan.tx_done();\n                }\n                _ => {}\n            }\n        }\n    }\n}\n\n/// Make sure to use `HfclkSource::ExternalXtal` as the `hfclk_source`\n/// to use the radio (nrf52840 product spec v1.11 5.4.1)\n/// ```\n/// # use embassy_nrf::config::*;\n/// let mut config = Config::default();\n/// config.hfclk_source = HfclkSource::ExternalXtal;\n/// ```\npub async fn new<'a, const N_RX: usize, const N_TX: usize, T: nrf::radio::Instance, Irq>(\n    mac_addr: [u8; 8],\n    radio: nrf::Peri<'a, T>,\n    irq: Irq,\n    state: &'a mut State<N_RX, N_TX>,\n) -> Result<(Device<'a>, Runner<'a>), ()>\nwhere\n    Irq: interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'a,\n{\n    let radio = Radio::new(radio, irq);\n\n    let (runner, device) = ch::new(&mut state.ch_state, ch::driver::HardwareAddress::Ieee802154(mac_addr));\n\n    Ok((device, Runner { ch: runner, radio }))\n}\n"
  },
  {
    "path": "embassy-nrf/src/fmt.rs",
    "content": "#![macro_use]\n#![allow(unused)]\n\nuse core::fmt::{Debug, Display, LowerHex};\n\n#[cfg(all(feature = \"defmt\", feature = \"log\"))]\ncompile_error!(\"You may not enable both `defmt` and `log` features.\");\n\n#[collapse_debuginfo(yes)]\nmacro_rules! assert {\n    ($($x:tt)*) => {\n        {\n            #[cfg(not(feature = \"defmt\"))]\n            ::core::assert!($($x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::assert!($($x)*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! assert_eq {\n    ($($x:tt)*) => {\n        {\n            #[cfg(not(feature = \"defmt\"))]\n            ::core::assert_eq!($($x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::assert_eq!($($x)*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! assert_ne {\n    ($($x:tt)*) => {\n        {\n            #[cfg(not(feature = \"defmt\"))]\n            ::core::assert_ne!($($x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::assert_ne!($($x)*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! debug_assert {\n    ($($x:tt)*) => {\n        {\n            #[cfg(not(feature = \"defmt\"))]\n            ::core::debug_assert!($($x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::debug_assert!($($x)*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! debug_assert_eq {\n    ($($x:tt)*) => {\n        {\n            #[cfg(not(feature = \"defmt\"))]\n            ::core::debug_assert_eq!($($x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::debug_assert_eq!($($x)*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! debug_assert_ne {\n    ($($x:tt)*) => {\n        {\n            #[cfg(not(feature = \"defmt\"))]\n            ::core::debug_assert_ne!($($x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::debug_assert_ne!($($x)*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! todo {\n    ($($x:tt)*) => {\n        {\n            #[cfg(not(feature = \"defmt\"))]\n            ::core::todo!($($x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::todo!($($x)*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! unreachable {\n    ($($x:tt)*) => {\n        {\n            #[cfg(not(feature = \"defmt\"))]\n            ::core::unreachable!($($x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::unreachable!($($x)*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! panic {\n    ($($x:tt)*) => {\n        {\n            #[cfg(not(feature = \"defmt\"))]\n            ::core::panic!($($x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::panic!($($x)*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! trace {\n    ($s:literal $(, $x:expr)* $(,)?) => {\n        {\n            #[cfg(feature = \"log\")]\n            ::log::trace!($s $(, $x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::trace!($s $(, $x)*);\n            #[cfg(not(any(feature = \"log\", feature=\"defmt\")))]\n            let _ = ($( & $x ),*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! debug {\n    ($s:literal $(, $x:expr)* $(,)?) => {\n        {\n            #[cfg(feature = \"log\")]\n            ::log::debug!($s $(, $x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::debug!($s $(, $x)*);\n            #[cfg(not(any(feature = \"log\", feature=\"defmt\")))]\n            let _ = ($( & $x ),*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! info {\n    ($s:literal $(, $x:expr)* $(,)?) => {\n        {\n            #[cfg(feature = \"log\")]\n            ::log::info!($s $(, $x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::info!($s $(, $x)*);\n            #[cfg(not(any(feature = \"log\", feature=\"defmt\")))]\n            let _ = ($( & $x ),*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! warn {\n    ($s:literal $(, $x:expr)* $(,)?) => {\n        {\n            #[cfg(feature = \"log\")]\n            ::log::warn!($s $(, $x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::warn!($s $(, $x)*);\n            #[cfg(not(any(feature = \"log\", feature=\"defmt\")))]\n            let _ = ($( & $x ),*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! error {\n    ($s:literal $(, $x:expr)* $(,)?) => {\n        {\n            #[cfg(feature = \"log\")]\n            ::log::error!($s $(, $x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::error!($s $(, $x)*);\n            #[cfg(not(any(feature = \"log\", feature=\"defmt\")))]\n            let _ = ($( & $x ),*);\n        }\n    };\n}\n\n#[cfg(feature = \"defmt\")]\n#[collapse_debuginfo(yes)]\nmacro_rules! unwrap {\n    ($($x:tt)*) => {\n        ::defmt::unwrap!($($x)*)\n    };\n}\n\n#[cfg(not(feature = \"defmt\"))]\n#[collapse_debuginfo(yes)]\nmacro_rules! unwrap {\n    ($arg:expr) => {\n        match $crate::fmt::Try::into_result($arg) {\n            ::core::result::Result::Ok(t) => t,\n            ::core::result::Result::Err(e) => {\n                ::core::panic!(\"unwrap of `{}` failed: {:?}\", ::core::stringify!($arg), e);\n            }\n        }\n    };\n    ($arg:expr, $($msg:expr),+ $(,)? ) => {\n        match $crate::fmt::Try::into_result($arg) {\n            ::core::result::Result::Ok(t) => t,\n            ::core::result::Result::Err(e) => {\n                ::core::panic!(\"unwrap of `{}` failed: {}: {:?}\", ::core::stringify!($arg), ::core::format_args!($($msg,)*), e);\n            }\n        }\n    }\n}\n\n#[derive(Debug, Copy, Clone, Eq, PartialEq)]\npub struct NoneError;\n\npub trait Try {\n    type Ok;\n    type Error;\n    fn into_result(self) -> Result<Self::Ok, Self::Error>;\n}\n\nimpl<T> Try for Option<T> {\n    type Ok = T;\n    type Error = NoneError;\n\n    #[inline]\n    fn into_result(self) -> Result<T, NoneError> {\n        self.ok_or(NoneError)\n    }\n}\n\nimpl<T, E> Try for Result<T, E> {\n    type Ok = T;\n    type Error = E;\n\n    #[inline]\n    fn into_result(self) -> Self {\n        self\n    }\n}\n\npub(crate) struct Bytes<'a>(pub &'a [u8]);\n\nimpl<'a> Debug for Bytes<'a> {\n    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {\n        write!(f, \"{:#02x?}\", self.0)\n    }\n}\n\nimpl<'a> Display for Bytes<'a> {\n    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {\n        write!(f, \"{:#02x?}\", self.0)\n    }\n}\n\nimpl<'a> LowerHex for Bytes<'a> {\n    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {\n        write!(f, \"{:#02x?}\", self.0)\n    }\n}\n\n#[cfg(feature = \"defmt\")]\nimpl<'a> defmt::Format for Bytes<'a> {\n    fn format(&self, fmt: defmt::Formatter) {\n        defmt::write!(fmt, \"{:02x}\", self.0)\n    }\n}\n"
  },
  {
    "path": "embassy-nrf/src/gpio.rs",
    "content": "//! General purpose input/output (GPIO) driver.\n#![macro_use]\n\nuse core::convert::Infallible;\nuse core::hint::unreachable_unchecked;\n\nuse cfg_if::cfg_if;\nuse embassy_hal_internal::{Peri, PeripheralType, impl_peripheral};\n\nuse crate::pac;\nuse crate::pac::common::{RW, Reg};\nuse crate::pac::gpio;\nuse crate::pac::gpio::vals;\n#[cfg(not(feature = \"_nrf51\"))]\nuse crate::pac::shared::{regs::Psel, vals::Connect};\n\n/// A GPIO port with up to 32 pins.\n#[derive(Debug, Eq, PartialEq)]\npub enum Port {\n    /// Port 0, available on nRF9160 and all nRF52 and nRF51 MCUs.\n    Port0,\n\n    /// Port 1, only available on some MCUs.\n    #[cfg(feature = \"_gpio-p1\")]\n    Port1,\n\n    /// Port 2, only available on some MCUs.\n    #[cfg(feature = \"_gpio-p2\")]\n    Port2,\n}\n\n/// Pull setting for an input.\n#[derive(Clone, Copy, Debug, Eq, PartialEq)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub enum Pull {\n    /// No pull.\n    None,\n    /// Internal pull-up resistor.\n    Up,\n    /// Internal pull-down resistor.\n    Down,\n}\n\n/// GPIO input driver.\npub struct Input<'d> {\n    pub(crate) pin: Flex<'d>,\n}\n\nimpl<'d> Input<'d> {\n    /// Create GPIO input driver for a [Pin] with the provided [Pull] configuration.\n    #[inline]\n    pub fn new(pin: Peri<'d, impl Pin>, pull: Pull) -> Self {\n        let mut pin = Flex::new(pin);\n        pin.set_as_input(pull);\n\n        Self { pin }\n    }\n\n    /// Get whether the pin input level is high.\n    #[inline]\n    pub fn is_high(&self) -> bool {\n        self.pin.is_high()\n    }\n\n    /// Get whether the pin input level is low.\n    #[inline]\n    pub fn is_low(&self) -> bool {\n        self.pin.is_low()\n    }\n\n    /// Get the pin input level.\n    #[inline]\n    pub fn get_level(&self) -> Level {\n        self.pin.get_level()\n    }\n}\n\nimpl Input<'static> {\n    /// Persist the pin's configuration for the rest of the program's lifetime. This method should\n    /// be preferred over [`core::mem::forget()`] because the `'static` bound prevents accidental\n    /// reuse of the underlying peripheral.\n    pub fn persist(self) {\n        self.pin.persist()\n    }\n}\n\n/// Digital input or output level.\n#[derive(Clone, Copy, Debug, Eq, PartialEq)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub enum Level {\n    /// Logical low.\n    Low,\n    /// Logical high.\n    High,\n}\n\nimpl From<bool> for Level {\n    fn from(val: bool) -> Self {\n        match val {\n            true => Self::High,\n            false => Self::Low,\n        }\n    }\n}\n\nimpl From<Level> for bool {\n    fn from(level: Level) -> bool {\n        match level {\n            Level::Low => false,\n            Level::High => true,\n        }\n    }\n}\n\n/// Drive strength settings for a given output level.\n// These numbers match vals::Drive exactly so hopefully the compiler will unify them.\n#[cfg(feature = \"_nrf54l\")]\n#[derive(Clone, Copy, Debug, PartialEq)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\n#[repr(u8)]\npub enum LevelDrive {\n    /// Disconnect (do not drive the output at all)\n    Disconnect = 2,\n    /// Standard\n    Standard = 0,\n    /// High drive\n    High = 1,\n    /// Extra high drive\n    ExtraHigh = 3,\n}\n\n/// Drive strength settings for an output pin.\n///\n/// This is a combination of two drive levels, used when the pin is set\n/// low and high respectively.\n#[cfg(feature = \"_nrf54l\")]\n#[derive(Clone, Copy, Debug, PartialEq)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub struct OutputDrive {\n    low: LevelDrive,\n    high: LevelDrive,\n}\n\n#[cfg(feature = \"_nrf54l\")]\n#[allow(non_upper_case_globals)]\nimpl OutputDrive {\n    /// Standard '0', standard '1'\n    pub const Standard: Self = Self {\n        low: LevelDrive::Standard,\n        high: LevelDrive::Standard,\n    };\n    /// High drive '0', standard '1'\n    pub const HighDrive0Standard1: Self = Self {\n        low: LevelDrive::High,\n        high: LevelDrive::Standard,\n    };\n    /// Standard '0', high drive '1'\n    pub const Standard0HighDrive1: Self = Self {\n        low: LevelDrive::Standard,\n        high: LevelDrive::High,\n    };\n    /// High drive '0', high 'drive '1'\n    pub const HighDrive: Self = Self {\n        low: LevelDrive::High,\n        high: LevelDrive::High,\n    };\n    /// Disconnect '0' standard '1' (normally used for wired-or connections)\n    pub const Disconnect0Standard1: Self = Self {\n        low: LevelDrive::Disconnect,\n        high: LevelDrive::Standard,\n    };\n    /// Disconnect '0', high drive '1' (normally used for wired-or connections)\n    pub const Disconnect0HighDrive1: Self = Self {\n        low: LevelDrive::Disconnect,\n        high: LevelDrive::High,\n    };\n    /// Standard '0'. disconnect '1' (also known as \"open drain\", normally used for wired-and connections)\n    pub const Standard0Disconnect1: Self = Self {\n        low: LevelDrive::Standard,\n        high: LevelDrive::Disconnect,\n    };\n    /// High drive '0', disconnect '1' (also known as \"open drain\", normally used for wired-and connections)\n    pub const HighDrive0Disconnect1: Self = Self {\n        low: LevelDrive::High,\n        high: LevelDrive::Disconnect,\n    };\n}\n\n/// Drive strength settings for an output pin.\n// These numbers match vals::Drive exactly so hopefully the compiler will unify them.\n#[cfg(not(feature = \"_nrf54l\"))]\n#[derive(Clone, Copy, Debug, PartialEq)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\n#[repr(u8)]\npub enum OutputDrive {\n    /// Standard '0', standard '1'\n    Standard = 0,\n    /// High drive '0', standard '1'\n    HighDrive0Standard1 = 1,\n    /// Standard '0', high drive '1'\n    Standard0HighDrive1 = 2,\n    /// High drive '0', high 'drive '1'\n    HighDrive = 3,\n    /// Disconnect '0' standard '1' (normally used for wired-or connections)\n    Disconnect0Standard1 = 4,\n    /// Disconnect '0', high drive '1' (normally used for wired-or connections)\n    Disconnect0HighDrive1 = 5,\n    /// Standard '0'. disconnect '1' (also known as \"open drain\", normally used for wired-and connections)\n    Standard0Disconnect1 = 6,\n    /// High drive '0', disconnect '1' (also known as \"open drain\", normally used for wired-and connections)\n    HighDrive0Disconnect1 = 7,\n}\n\n/// GPIO output driver.\npub struct Output<'d> {\n    pub(crate) pin: Flex<'d>,\n}\n\nimpl<'d> Output<'d> {\n    /// Create GPIO output driver for a [Pin] with the provided [Level] and [OutputDrive] configuration.\n    #[inline]\n    pub fn new(pin: Peri<'d, impl Pin>, initial_output: Level, drive: OutputDrive) -> Self {\n        let mut pin = Flex::new(pin);\n        match initial_output {\n            Level::High => pin.set_high(),\n            Level::Low => pin.set_low(),\n        }\n        pin.set_as_output(drive);\n\n        Self { pin }\n    }\n\n    /// Set the output as high.\n    #[inline]\n    pub fn set_high(&mut self) {\n        self.pin.set_high()\n    }\n\n    /// Set the output as low.\n    #[inline]\n    pub fn set_low(&mut self) {\n        self.pin.set_low()\n    }\n\n    /// Toggle the output level.\n    #[inline]\n    pub fn toggle(&mut self) {\n        self.pin.toggle()\n    }\n\n    /// Set the output level.\n    #[inline]\n    pub fn set_level(&mut self, level: Level) {\n        self.pin.set_level(level)\n    }\n\n    /// Get whether the output level is set to high.\n    #[inline]\n    pub fn is_set_high(&self) -> bool {\n        self.pin.is_set_high()\n    }\n\n    /// Get whether the output level is set to low.\n    #[inline]\n    pub fn is_set_low(&self) -> bool {\n        self.pin.is_set_low()\n    }\n\n    /// Get the current output level.\n    #[inline]\n    pub fn get_output_level(&self) -> Level {\n        self.pin.get_output_level()\n    }\n}\n\nimpl Output<'static> {\n    /// Persist the pin's configuration for the rest of the program's lifetime. This method should\n    /// be preferred over [`core::mem::forget()`] because the `'static` bound prevents accidental\n    /// reuse of the underlying peripheral.\n    pub fn persist(self) {\n        self.pin.persist()\n    }\n}\n\npub(crate) fn convert_drive(w: &mut pac::gpio::regs::PinCnf, drive: OutputDrive) {\n    #[cfg(not(feature = \"_nrf54l\"))]\n    {\n        let drive = match drive {\n            OutputDrive::Standard => vals::Drive::S0S1,\n            OutputDrive::HighDrive0Standard1 => vals::Drive::H0S1,\n            OutputDrive::Standard0HighDrive1 => vals::Drive::S0H1,\n            OutputDrive::HighDrive => vals::Drive::H0H1,\n            OutputDrive::Disconnect0Standard1 => vals::Drive::D0S1,\n            OutputDrive::Disconnect0HighDrive1 => vals::Drive::D0H1,\n            OutputDrive::Standard0Disconnect1 => vals::Drive::S0D1,\n            OutputDrive::HighDrive0Disconnect1 => vals::Drive::H0D1,\n        };\n        w.set_drive(drive);\n    }\n\n    #[cfg(feature = \"_nrf54l\")]\n    {\n        fn convert(d: LevelDrive) -> vals::Drive {\n            match d {\n                LevelDrive::Disconnect => vals::Drive::D,\n                LevelDrive::Standard => vals::Drive::S,\n                LevelDrive::High => vals::Drive::H,\n                LevelDrive::ExtraHigh => vals::Drive::E,\n            }\n        }\n\n        w.set_drive0(convert(drive.low));\n        w.set_drive1(convert(drive.high));\n    }\n}\n\nfn convert_pull(pull: Pull) -> vals::Pull {\n    match pull {\n        Pull::None => vals::Pull::DISABLED,\n        Pull::Up => vals::Pull::PULLUP,\n        Pull::Down => vals::Pull::PULLDOWN,\n    }\n}\n\n/// GPIO flexible pin.\n///\n/// This pin can either be a disconnected, input, or output pin, or both. The level register bit will remain\n/// set while not in output mode, so the pin's level will be 'remembered' when it is not in output\n/// mode.\npub struct Flex<'d> {\n    pub(crate) pin: Peri<'d, AnyPin>,\n}\n\nimpl<'d> Flex<'d> {\n    /// Wrap the pin in a `Flex`.\n    ///\n    /// The pin remains disconnected. The initial output level is unspecified, but can be changed\n    /// before the pin is put into output mode.\n    #[inline]\n    pub fn new(pin: Peri<'d, impl Pin>) -> Self {\n        // Pin will be in disconnected state.\n        Self { pin: pin.into() }\n    }\n\n    /// Put the pin into input mode.\n    #[inline]\n    pub fn set_as_input(&mut self, pull: Pull) {\n        self.pin.conf().write(|w| {\n            w.set_dir(vals::Dir::INPUT);\n            w.set_input(vals::Input::CONNECT);\n            w.set_pull(convert_pull(pull));\n            convert_drive(w, OutputDrive::Standard);\n            w.set_sense(vals::Sense::DISABLED);\n        });\n    }\n\n    /// Put the pin into output mode.\n    ///\n    /// The pin level will be whatever was set before (or low by default). If you want it to begin\n    /// at a specific level, call `set_high`/`set_low` on the pin first.\n    #[inline]\n    pub fn set_as_output(&mut self, drive: OutputDrive) {\n        self.pin.conf().write(|w| {\n            w.set_dir(vals::Dir::OUTPUT);\n            w.set_input(vals::Input::DISCONNECT);\n            w.set_pull(vals::Pull::DISABLED);\n            convert_drive(w, drive);\n            w.set_sense(vals::Sense::DISABLED);\n        });\n    }\n\n    /// Put the pin into input + output mode.\n    ///\n    /// This is commonly used for \"open drain\" mode. If you set `drive = Standard0Disconnect1`,\n    /// the hardware will drive the line low if you set it to low, and will leave it floating if you set\n    /// it to high, in which case you can read the input to figure out whether another device\n    /// is driving the line low.\n    ///\n    /// The pin level will be whatever was set before (or low by default). If you want it to begin\n    /// at a specific level, call `set_high`/`set_low` on the pin first.\n    #[inline]\n    pub fn set_as_input_output(&mut self, pull: Pull, drive: OutputDrive) {\n        self.pin.conf().write(|w| {\n            w.set_dir(vals::Dir::OUTPUT);\n            w.set_input(vals::Input::CONNECT);\n            w.set_pull(convert_pull(pull));\n            convert_drive(w, drive);\n            w.set_sense(vals::Sense::DISABLED);\n        });\n    }\n\n    /// Put the pin into disconnected mode.\n    #[inline]\n    pub fn set_as_disconnected(&mut self) {\n        self.pin.conf().write(|w| {\n            w.set_input(vals::Input::DISCONNECT);\n        });\n    }\n\n    /// Get whether the pin input level is high.\n    #[inline]\n    pub fn is_high(&self) -> bool {\n        self.pin.block().in_().read().pin(self.pin.pin() as _)\n    }\n\n    /// Get whether the pin input level is low.\n    #[inline]\n    pub fn is_low(&self) -> bool {\n        !self.is_high()\n    }\n\n    /// Get the pin input level.\n    #[inline]\n    pub fn get_level(&self) -> Level {\n        self.is_high().into()\n    }\n\n    /// Set the output as high.\n    #[inline]\n    pub fn set_high(&mut self) {\n        self.pin.set_high()\n    }\n\n    /// Set the output as low.\n    #[inline]\n    pub fn set_low(&mut self) {\n        self.pin.set_low()\n    }\n\n    /// Toggle the output level.\n    #[inline]\n    pub fn toggle(&mut self) {\n        if self.is_set_low() {\n            self.set_high()\n        } else {\n            self.set_low()\n        }\n    }\n\n    /// Set the output level.\n    #[inline]\n    pub fn set_level(&mut self, level: Level) {\n        match level {\n            Level::Low => self.pin.set_low(),\n            Level::High => self.pin.set_high(),\n        }\n    }\n\n    /// Get whether the output level is set to high.\n    #[inline]\n    pub fn is_set_high(&self) -> bool {\n        self.pin.block().out().read().pin(self.pin.pin() as _)\n    }\n\n    /// Get whether the output level is set to low.\n    #[inline]\n    pub fn is_set_low(&self) -> bool {\n        !self.is_set_high()\n    }\n\n    /// Get the current output level.\n    #[inline]\n    pub fn get_output_level(&self) -> Level {\n        self.is_set_high().into()\n    }\n}\n\nimpl Flex<'static> {\n    /// Persist the pin's configuration for the rest of the program's lifetime. This method should\n    /// be preferred over [`core::mem::forget()`] because the `'static` bound prevents accidental\n    /// reuse of the underlying peripheral.\n    pub fn persist(self) {\n        core::mem::forget(self);\n    }\n}\n\nimpl<'d> Drop for Flex<'d> {\n    fn drop(&mut self) {\n        self.set_as_disconnected();\n    }\n}\n\npub(crate) trait SealedPin {\n    fn pin_port(&self) -> u8;\n\n    #[inline]\n    fn _pin(&self) -> u8 {\n        cfg_if! {\n            if #[cfg(feature = \"_gpio-p1\")] {\n                self.pin_port() % 32\n            } else {\n                self.pin_port()\n            }\n        }\n    }\n\n    #[inline]\n    fn block(&self) -> gpio::Gpio {\n        match self.pin_port() / 32 {\n            #[cfg(feature = \"_nrf51\")]\n            0 => pac::GPIO,\n            #[cfg(not(feature = \"_nrf51\"))]\n            0 => pac::P0,\n            #[cfg(feature = \"_gpio-p1\")]\n            1 => pac::P1,\n            #[cfg(feature = \"_gpio-p2\")]\n            2 => pac::P2,\n            _ => unsafe { unreachable_unchecked() },\n        }\n    }\n\n    #[inline]\n    fn conf(&self) -> Reg<gpio::regs::PinCnf, RW> {\n        self.block().pin_cnf(self._pin() as usize)\n    }\n\n    /// Set the output as high.\n    #[inline]\n    fn set_high(&self) {\n        self.block().outset().write(|w| w.set_pin(self._pin() as _, true))\n    }\n\n    /// Set the output as low.\n    #[inline]\n    fn set_low(&self) {\n        self.block().outclr().write(|w| w.set_pin(self._pin() as _, true))\n    }\n}\n\n/// Interface for a Pin that can be configured by an [Input] or [Output] driver, or converted to an [AnyPin].\n#[allow(private_bounds)]\npub trait Pin: PeripheralType + Into<AnyPin> + SealedPin + Sized + 'static {\n    /// Number of the pin within the port (0..31)\n    #[inline]\n    fn pin(&self) -> u8 {\n        self._pin()\n    }\n\n    /// Port of the pin\n    #[inline]\n    fn port(&self) -> Port {\n        match self.pin_port() / 32 {\n            0 => Port::Port0,\n            #[cfg(feature = \"_gpio-p1\")]\n            1 => Port::Port1,\n            #[cfg(feature = \"_gpio-p2\")]\n            2 => Port::Port2,\n            _ => unsafe { unreachable_unchecked() },\n        }\n    }\n\n    /// Peripheral port register value\n    #[inline]\n    #[cfg(not(feature = \"_nrf51\"))]\n    fn psel_bits(&self) -> pac::shared::regs::Psel {\n        pac::shared::regs::Psel(self.pin_port() as u32)\n    }\n}\n\n/// Type-erased GPIO pin\npub struct AnyPin {\n    pub(crate) pin_port: u8,\n}\n\nimpl AnyPin {\n    /// Create an [AnyPin] for a specific pin.\n    ///\n    /// # Safety\n    /// - `pin_port` should not in use by another driver.\n    #[inline]\n    pub unsafe fn steal(pin_port: u8) -> Peri<'static, Self> {\n        Peri::new_unchecked(Self { pin_port })\n    }\n}\n\nimpl_peripheral!(AnyPin);\nimpl Pin for AnyPin {}\nimpl SealedPin for AnyPin {\n    #[inline]\n    fn pin_port(&self) -> u8 {\n        self.pin_port\n    }\n}\n\n// ====================\n\n#[cfg(not(feature = \"_nrf51\"))]\npub(crate) trait PselBits {\n    fn psel_bits(&self) -> pac::shared::regs::Psel;\n}\n\n#[cfg(not(feature = \"_nrf51\"))]\nimpl<'a, P: Pin> PselBits for Option<Peri<'a, P>> {\n    #[inline]\n    fn psel_bits(&self) -> pac::shared::regs::Psel {\n        match self {\n            Some(pin) => pin.psel_bits(),\n            None => DISCONNECTED,\n        }\n    }\n}\n\n#[cfg(not(feature = \"_nrf51\"))]\npub(crate) const DISCONNECTED: Psel = Psel(1 << 31);\n\n#[cfg(not(feature = \"_nrf51\"))]\n#[allow(dead_code)]\npub(crate) fn deconfigure_pin(psel: Psel) {\n    if psel.connect() == Connect::DISCONNECTED {\n        return;\n    }\n    unsafe { AnyPin::steal(psel.0 as _) }.conf().write(|w| {\n        w.set_input(vals::Input::DISCONNECT);\n    })\n}\n\n// ====================\n\nmacro_rules! impl_pin {\n    ($type:ident, $port_num:expr, $pin_num:expr) => {\n        impl crate::gpio::Pin for peripherals::$type {}\n        impl crate::gpio::SealedPin for peripherals::$type {\n            #[inline]\n            fn pin_port(&self) -> u8 {\n                $port_num * 32 + $pin_num\n            }\n        }\n\n        impl From<peripherals::$type> for crate::gpio::AnyPin {\n            fn from(_val: peripherals::$type) -> Self {\n                Self {\n                    pin_port: $port_num * 32 + $pin_num,\n                }\n            }\n        }\n    };\n}\n\n// ====================\n\nmod eh02 {\n    use super::*;\n\n    impl<'d> embedded_hal_02::digital::v2::InputPin for Input<'d> {\n        type Error = Infallible;\n\n        fn is_high(&self) -> Result<bool, Self::Error> {\n            Ok(self.is_high())\n        }\n\n        fn is_low(&self) -> Result<bool, Self::Error> {\n            Ok(self.is_low())\n        }\n    }\n\n    impl<'d> embedded_hal_02::digital::v2::OutputPin for Output<'d> {\n        type Error = Infallible;\n\n        fn set_high(&mut self) -> Result<(), Self::Error> {\n            self.set_high();\n            Ok(())\n        }\n\n        fn set_low(&mut self) -> Result<(), Self::Error> {\n            self.set_low();\n            Ok(())\n        }\n    }\n\n    impl<'d> embedded_hal_02::digital::v2::StatefulOutputPin for Output<'d> {\n        fn is_set_high(&self) -> Result<bool, Self::Error> {\n            Ok(self.is_set_high())\n        }\n\n        fn is_set_low(&self) -> Result<bool, Self::Error> {\n            Ok(self.is_set_low())\n        }\n    }\n\n    impl<'d> embedded_hal_02::digital::v2::ToggleableOutputPin for Output<'d> {\n        type Error = Infallible;\n        #[inline]\n        fn toggle(&mut self) -> Result<(), Self::Error> {\n            self.toggle();\n            Ok(())\n        }\n    }\n\n    /// Implement [`embedded_hal_02::digital::v2::InputPin`] for [`Flex`];\n    ///\n    /// If the pin is not in input mode the result is unspecified.\n    impl<'d> embedded_hal_02::digital::v2::InputPin for Flex<'d> {\n        type Error = Infallible;\n\n        fn is_high(&self) -> Result<bool, Self::Error> {\n            Ok(self.is_high())\n        }\n\n        fn is_low(&self) -> Result<bool, Self::Error> {\n            Ok(self.is_low())\n        }\n    }\n\n    impl<'d> embedded_hal_02::digital::v2::OutputPin for Flex<'d> {\n        type Error = Infallible;\n\n        fn set_high(&mut self) -> Result<(), Self::Error> {\n            self.set_high();\n            Ok(())\n        }\n\n        fn set_low(&mut self) -> Result<(), Self::Error> {\n            self.set_low();\n            Ok(())\n        }\n    }\n\n    impl<'d> embedded_hal_02::digital::v2::StatefulOutputPin for Flex<'d> {\n        fn is_set_high(&self) -> Result<bool, Self::Error> {\n            Ok(self.is_set_high())\n        }\n\n        fn is_set_low(&self) -> Result<bool, Self::Error> {\n            Ok(self.is_set_low())\n        }\n    }\n\n    impl<'d> embedded_hal_02::digital::v2::ToggleableOutputPin for Flex<'d> {\n        type Error = Infallible;\n        #[inline]\n        fn toggle(&mut self) -> Result<(), Self::Error> {\n            self.toggle();\n            Ok(())\n        }\n    }\n}\n\nimpl<'d> embedded_hal_1::digital::ErrorType for Input<'d> {\n    type Error = Infallible;\n}\n\nimpl<'d> embedded_hal_1::digital::InputPin for Input<'d> {\n    fn is_high(&mut self) -> Result<bool, Self::Error> {\n        Ok((*self).is_high())\n    }\n\n    fn is_low(&mut self) -> Result<bool, Self::Error> {\n        Ok((*self).is_low())\n    }\n}\n\nimpl<'d> embedded_hal_1::digital::ErrorType for Output<'d> {\n    type Error = Infallible;\n}\n\nimpl<'d> embedded_hal_1::digital::OutputPin for Output<'d> {\n    fn set_high(&mut self) -> Result<(), Self::Error> {\n        self.set_high();\n        Ok(())\n    }\n\n    fn set_low(&mut self) -> Result<(), Self::Error> {\n        self.set_low();\n        Ok(())\n    }\n}\n\nimpl<'d> embedded_hal_1::digital::StatefulOutputPin for Output<'d> {\n    fn is_set_high(&mut self) -> Result<bool, Self::Error> {\n        Ok((*self).is_set_high())\n    }\n\n    fn is_set_low(&mut self) -> Result<bool, Self::Error> {\n        Ok((*self).is_set_low())\n    }\n}\n\nimpl<'d> embedded_hal_1::digital::ErrorType for Flex<'d> {\n    type Error = Infallible;\n}\n\n/// Implement [embedded_hal_1::digital::InputPin] for [`Flex`];\n///\n/// If the pin is not in input mode the result is unspecified.\nimpl<'d> embedded_hal_1::digital::InputPin for Flex<'d> {\n    fn is_high(&mut self) -> Result<bool, Self::Error> {\n        Ok((*self).is_high())\n    }\n\n    fn is_low(&mut self) -> Result<bool, Self::Error> {\n        Ok((*self).is_low())\n    }\n}\n\nimpl<'d> embedded_hal_1::digital::OutputPin for Flex<'d> {\n    fn set_high(&mut self) -> Result<(), Self::Error> {\n        self.set_high();\n        Ok(())\n    }\n\n    fn set_low(&mut self) -> Result<(), Self::Error> {\n        self.set_low();\n        Ok(())\n    }\n}\n\nimpl<'d> embedded_hal_1::digital::StatefulOutputPin for Flex<'d> {\n    fn is_set_high(&mut self) -> Result<bool, Self::Error> {\n        Ok((*self).is_set_high())\n    }\n\n    fn is_set_low(&mut self) -> Result<bool, Self::Error> {\n        Ok((*self).is_set_low())\n    }\n}\n"
  },
  {
    "path": "embassy-nrf/src/gpiote.rs",
    "content": "//! GPIO task/event (GPIOTE) driver.\n#![macro_use]\n\nuse core::convert::Infallible;\nuse core::future::{Future, poll_fn};\nuse core::task::{Context, Poll};\n\nuse embassy_hal_internal::{Peri, PeripheralType, impl_peripheral};\nuse embassy_sync::waitqueue::AtomicWaker;\n\nuse crate::gpio::{AnyPin, Flex, Input, Level, Output, OutputDrive, Pin as GpioPin, Pull, SealedPin as _};\nuse crate::interrupt::InterruptExt;\n#[cfg(not(feature = \"_nrf51\"))]\nuse crate::pac::gpio::vals::Detectmode;\nuse crate::pac::gpio::vals::Sense;\nuse crate::pac::gpiote::vals::{Mode, Outinit, Polarity};\nuse crate::ppi::{Event, Task};\nuse crate::{interrupt, pac, peripherals};\n\n#[cfg(feature = \"_nrf51\")]\n/// Amount of GPIOTE channels in the chip.\nconst CHANNEL_COUNT: usize = 4;\n#[cfg(not(any(feature = \"_nrf51\", feature = \"_nrf54l\")))]\n/// Amount of GPIOTE channels in the chip.\nconst CHANNEL_COUNT: usize = 8;\n#[cfg(any(feature = \"_nrf54l\"))]\n/// Amount of GPIOTE channels in the chip.\nconst CHANNEL_COUNT: usize = 12;\n/// Max channels per port\nconst CHANNELS_PER_PORT: usize = 8;\n\n#[cfg(any(feature = \"nrf52833\", feature = \"nrf52840\", feature = \"_nrf5340\"))]\nconst PIN_COUNT: usize = 48;\n#[cfg(any(feature = \"_nrf54l15\", feature = \"_nrf54l10\", feature = \"_nrf54l05\"))]\n// Highest pin index is P2.10 => (2 * 32 + 10) = 74\nconst PIN_COUNT: usize = 75;\n#[cfg(feature = \"_nrf54lm20\")]\n// Highest pin index is P3.12 => (3 * 32 + 12) = 108\nconst PIN_COUNT: usize = 109;\n#[cfg(not(any(\n    feature = \"nrf52833\",\n    feature = \"nrf52840\",\n    feature = \"_nrf5340\",\n    feature = \"_nrf54l\",\n)))]\nconst PIN_COUNT: usize = 32;\n\n#[allow(clippy::declare_interior_mutable_const)]\nstatic CHANNEL_WAKERS: [AtomicWaker; CHANNEL_COUNT] = [const { AtomicWaker::new() }; CHANNEL_COUNT];\nstatic PORT_WAKERS: [AtomicWaker; PIN_COUNT] = [const { AtomicWaker::new() }; PIN_COUNT];\n\n/// Polarity for listening to events for GPIOTE input channels.\npub enum InputChannelPolarity {\n    /// Don't listen for any pin changes.\n    None,\n    /// Listen for high to low changes.\n    HiToLo,\n    /// Listen for low to high changes.\n    LoToHi,\n    /// Listen for any change, either low to high or high to low.\n    Toggle,\n}\n\n/// Polarity of the OUT task operation for GPIOTE output channels.\npub enum OutputChannelPolarity {\n    /// Set the pin high.\n    Set,\n    /// Set the pin low.\n    Clear,\n    /// Toggle the pin.\n    Toggle,\n}\n\npub(crate) fn init(irq_prio: crate::interrupt::Priority) {\n    // no latched GPIO detect in nrf51.\n    #[cfg(not(feature = \"_nrf51\"))]\n    {\n        #[cfg(any(feature = \"nrf52833\", feature = \"nrf52840\", feature = \"_nrf5340\"))]\n        let ports = &[pac::P0, pac::P1];\n        #[cfg(not(any(feature = \"_nrf51\", feature = \"nrf52833\", feature = \"nrf52840\", feature = \"_nrf5340\")))]\n        let ports = &[pac::P0];\n\n        for &p in ports {\n            // Enable latched detection\n            #[cfg(all(feature = \"_s\", not(feature = \"_nrf54l\")))]\n            p.detectmode_sec().write(|w| w.set_detectmode(Detectmode::LDETECT));\n            #[cfg(any(not(feature = \"_s\"), all(feature = \"_s\", feature = \"_nrf54l\")))]\n            p.detectmode().write(|w| w.set_detectmode(Detectmode::LDETECT));\n            // Clear latch\n            p.latch().write(|w| w.0 = 0xFFFFFFFF)\n        }\n    }\n\n    // Enable interrupts\n    #[cfg(any(feature = \"nrf5340-app-s\", feature = \"nrf9160-s\", feature = \"nrf9120-s\"))]\n    let irqs = &[(pac::GPIOTE0, interrupt::GPIOTE0)];\n    #[cfg(any(feature = \"nrf5340-app-ns\", feature = \"nrf9160-ns\", feature = \"nrf9120-ns\"))]\n    let irqs = &[(pac::GPIOTE1, interrupt::GPIOTE1)];\n    #[cfg(any(feature = \"_nrf51\", feature = \"_nrf52\", feature = \"nrf5340-net\"))]\n    let irqs = &[(pac::GPIOTE, interrupt::GPIOTE)];\n    #[cfg(any(feature = \"_nrf54l\"))]\n    let irqs = &[\n        #[cfg(feature = \"_s\")]\n        (pac::GPIOTE20, interrupt::GPIOTE20_0),\n        #[cfg(feature = \"_s\")]\n        (pac::GPIOTE30, interrupt::GPIOTE30_0),\n        #[cfg(feature = \"_ns\")]\n        (pac::GPIOTE20, interrupt::GPIOTE20_1),\n        #[cfg(feature = \"_ns\")]\n        (pac::GPIOTE30, interrupt::GPIOTE30_1),\n    ];\n\n    for (inst, irq) in irqs {\n        irq.unpend();\n        irq.set_priority(irq_prio);\n        unsafe { irq.enable() };\n\n        let g = inst;\n        #[cfg(not(feature = \"_nrf54l\"))]\n        g.intenset(INTNUM).write(|w| w.set_port(true));\n\n        #[cfg(all(feature = \"_nrf54l\", feature = \"_ns\"))]\n        g.intenset(INTNUM).write(|w| w.set_port0nonsecure(true));\n\n        #[cfg(all(feature = \"_nrf54l\", feature = \"_s\"))]\n        g.intenset(INTNUM).write(|w| w.set_port0secure(true));\n    }\n}\n\n#[cfg(all(feature = \"_nrf54l\", feature = \"_ns\"))]\nconst INTNUM: usize = 1;\n\n#[cfg(any(not(feature = \"_nrf54l\"), feature = \"_s\"))]\nconst INTNUM: usize = 0;\n\n#[cfg(any(feature = \"nrf5340-app-s\", feature = \"nrf9160-s\", feature = \"nrf9120-s\"))]\n#[cfg(feature = \"rt\")]\n#[interrupt]\nfn GPIOTE0() {\n    unsafe { handle_gpiote_interrupt(pac::GPIOTE0) };\n}\n\n#[cfg(any(feature = \"nrf5340-app-ns\", feature = \"nrf9160-ns\", feature = \"nrf9120-ns\"))]\n#[cfg(feature = \"rt\")]\n#[interrupt]\nfn GPIOTE1() {\n    unsafe { handle_gpiote_interrupt(pac::GPIOTE1) };\n}\n\n#[cfg(any(feature = \"_nrf51\", feature = \"_nrf52\", feature = \"nrf5340-net\"))]\n#[cfg(feature = \"rt\")]\n#[interrupt]\nfn GPIOTE() {\n    unsafe { handle_gpiote_interrupt(pac::GPIOTE) };\n}\n\n#[cfg(all(feature = \"_nrf54l\", feature = \"_s\"))]\n#[cfg(feature = \"rt\")]\n#[interrupt]\nfn GPIOTE20_0() {\n    unsafe { handle_gpiote_interrupt(pac::GPIOTE20) };\n}\n\n#[cfg(all(feature = \"_nrf54l\", feature = \"_s\"))]\n#[cfg(feature = \"rt\")]\n#[interrupt]\nfn GPIOTE30_0() {\n    unsafe { handle_gpiote_interrupt(pac::GPIOTE30) };\n}\n\n#[cfg(all(feature = \"_nrf54l\", feature = \"_ns\"))]\n#[cfg(feature = \"rt\")]\n#[interrupt]\nfn GPIOTE20_1() {\n    unsafe { handle_gpiote_interrupt(pac::GPIOTE20) };\n}\n\n#[cfg(all(feature = \"_nrf54l\", feature = \"_ns\"))]\n#[cfg(feature = \"rt\")]\n#[interrupt]\nfn GPIOTE30_1() {\n    unsafe { handle_gpiote_interrupt(pac::GPIOTE30) };\n}\n\nunsafe fn handle_gpiote_interrupt(g: pac::gpiote::Gpiote) {\n    for c in 0..CHANNEL_COUNT {\n        let i = c % CHANNELS_PER_PORT;\n        if g.events_in(i).read() != 0 {\n            g.intenclr(INTNUM).write(|w| w.0 = 1 << i);\n            CHANNEL_WAKERS[c].wake();\n        }\n    }\n\n    #[cfg(not(feature = \"_nrf54l\"))]\n    let eport = g.events_port(0);\n\n    #[cfg(all(feature = \"_nrf54l\", feature = \"_ns\"))]\n    let eport = g.events_port(0).nonsecure();\n\n    #[cfg(all(feature = \"_nrf54l\", feature = \"_s\"))]\n    let eport = g.events_port(0).secure();\n\n    if eport.read() != 0 {\n        eport.write_value(0);\n\n        #[cfg(any(\n            feature = \"nrf52833\",\n            feature = \"nrf52840\",\n            feature = \"_nrf5340\",\n            feature = \"_nrf54l\"\n        ))]\n        let ports = &[pac::P0, pac::P1];\n        #[cfg(not(any(\n            feature = \"_nrf51\",\n            feature = \"nrf52833\",\n            feature = \"nrf52840\",\n            feature = \"_nrf5340\",\n            feature = \"_nrf54l\"\n        )))]\n        let ports = &[pac::P0];\n        #[cfg(feature = \"_nrf51\")]\n        let ports = &[pac::GPIO];\n\n        #[cfg(feature = \"_nrf51\")]\n        for (port, &p) in ports.iter().enumerate() {\n            let inp = p.in_().read();\n            for pin in 0..32 {\n                let fired = match p.pin_cnf(pin as usize).read().sense() {\n                    Sense::HIGH => inp.pin(pin),\n                    Sense::LOW => !inp.pin(pin),\n                    _ => false,\n                };\n\n                if fired {\n                    PORT_WAKERS[port * 32 + pin as usize].wake();\n                    p.pin_cnf(pin as usize).modify(|w| w.set_sense(Sense::DISABLED));\n                }\n            }\n        }\n\n        #[cfg(not(feature = \"_nrf51\"))]\n        for (port, &p) in ports.iter().enumerate() {\n            let bits = p.latch().read().0;\n            for pin in BitIter(bits) {\n                p.pin_cnf(pin as usize).modify(|w| w.set_sense(Sense::DISABLED));\n                PORT_WAKERS[port * 32 + pin as usize].wake();\n            }\n            p.latch().write(|w| w.0 = bits);\n        }\n    }\n}\n\n#[cfg(not(feature = \"_nrf51\"))]\nstruct BitIter(u32);\n\n#[cfg(not(feature = \"_nrf51\"))]\nimpl Iterator for BitIter {\n    type Item = u32;\n\n    fn next(&mut self) -> Option<Self::Item> {\n        match self.0.trailing_zeros() {\n            32 => None,\n            b => {\n                self.0 &= !(1 << b);\n                Some(b)\n            }\n        }\n    }\n}\n\n/// GPIOTE channel driver in input mode\npub struct InputChannel<'d> {\n    ch: Peri<'d, AnyChannel>,\n    pin: Input<'d>,\n}\n\nimpl InputChannel<'static> {\n    /// Persist the channel's configuration for the rest of the program's lifetime. This method\n    /// should be preferred over [`core::mem::forget()`] because the `'static` bound prevents\n    /// accidental reuse of the underlying peripheral.\n    pub fn persist(self) {\n        core::mem::forget(self);\n    }\n}\n\nimpl<'d> Drop for InputChannel<'d> {\n    fn drop(&mut self) {\n        let g = self.ch.regs();\n        let num = self.ch.number();\n        g.config(num).write(|w| w.set_mode(Mode::DISABLED));\n        g.intenclr(INTNUM).write(|w| w.0 = 1 << num);\n    }\n}\n\nimpl<'d> InputChannel<'d> {\n    /// Create a new GPIOTE input channel driver.\n    #[cfg(feature = \"_nrf54l\")]\n    pub fn new<C: Channel, T: GpiotePin<Instance = C::Instance>>(\n        ch: Peri<'d, C>,\n        pin: Peri<'d, T>,\n        pull: Pull,\n        polarity: InputChannelPolarity,\n    ) -> Self {\n        let pin = Input::new(pin, pull);\n        let ch = ch.into();\n        Self::new_inner(ch, pin, polarity)\n    }\n\n    /// Create a new GPIOTE output channel driver.\n    #[cfg(not(feature = \"_nrf54l\"))]\n    pub fn new<C: Channel, T: GpioPin>(\n        ch: Peri<'d, C>,\n        pin: Peri<'d, T>,\n        pull: Pull,\n        polarity: InputChannelPolarity,\n    ) -> Self {\n        let pin = Input::new(pin, pull);\n        let ch = ch.into();\n        Self::new_inner(ch, pin, polarity)\n    }\n\n    fn new_inner(ch: Peri<'d, AnyChannel>, pin: Input<'d>, polarity: InputChannelPolarity) -> Self {\n        let g = ch.regs();\n        let num = ch.number();\n        g.config(num).write(|w| {\n            w.set_mode(Mode::EVENT);\n            match polarity {\n                InputChannelPolarity::HiToLo => w.set_polarity(Polarity::HI_TO_LO),\n                InputChannelPolarity::LoToHi => w.set_polarity(Polarity::LO_TO_HI),\n                InputChannelPolarity::None => w.set_polarity(Polarity::NONE),\n                InputChannelPolarity::Toggle => w.set_polarity(Polarity::TOGGLE),\n            };\n            #[cfg(any(feature = \"nrf52833\", feature = \"nrf52840\", feature = \"_nrf5340\",))]\n            w.set_port(match pin.pin.pin.port() {\n                crate::gpio::Port::Port0 => false,\n                crate::gpio::Port::Port1 => true,\n            });\n            #[cfg(any(feature = \"_nrf54l\"))]\n            w.set_port(match pin.pin.pin.port() {\n                crate::gpio::Port::Port0 => 0,\n                crate::gpio::Port::Port1 => 1,\n                crate::gpio::Port::Port2 => 2,\n            });\n            w.set_psel(pin.pin.pin.pin());\n        });\n\n        g.events_in(num).write_value(0);\n\n        InputChannel { ch, pin }\n    }\n\n    /// Asynchronously wait for an event in this channel.\n    ///\n    /// It is possible to call this function and await the returned future later.\n    /// If an even occurs in the mean time, the future will immediately report ready.\n    pub fn wait(&mut self) -> impl Future<Output = ()> {\n        // NOTE: This is `-> impl Future` and not an `async fn` on purpose.\n        // Otherwise, events will only be detected starting at the first poll of the returned future.\n        Self::wait_internal(&mut self.ch)\n    }\n\n    /// Asynchronously wait for the pin to become high.\n    ///\n    /// The channel must be configured with [`InputChannelPolarity::LoToHi`] or [`InputChannelPolarity::Toggle`].\n    /// If the channel is not configured to detect rising edges, it is unspecified when the returned future completes.\n    ///\n    /// It is possible to call this function and await the returned future later.\n    /// If an even occurs in the mean time, the future will immediately report ready.\n    pub fn wait_for_high(&mut self) -> impl Future<Output = ()> {\n        // NOTE: This is `-> impl Future` and not an `async fn` on purpose.\n        // Otherwise, events will only be detected starting at the first poll of the returned future.\n\n        // Subscribe to the event before checking the pin level.\n        let wait = Self::wait_internal(&mut self.ch);\n        let pin = &self.pin;\n        async move {\n            if pin.is_high() {\n                return;\n            }\n            wait.await;\n        }\n    }\n\n    /// Asynchronously wait for the pin to become low.\n    ///\n    /// The channel must be configured with [`InputChannelPolarity::HiToLo`] or [`InputChannelPolarity::Toggle`].\n    /// If the channel is not configured to detect falling edges, it is unspecified when the returned future completes.\n    ///\n    /// It is possible to call this function and await the returned future later.\n    /// If an even occurs in the mean time, the future will immediately report ready.\n    pub fn wait_for_low(&mut self) -> impl Future<Output = ()> {\n        // NOTE: This is `-> impl Future` and not an `async fn` on purpose.\n        // Otherwise, events will only be detected starting at the first poll of the returned future.\n\n        // Subscribe to the event before checking the pin level.\n        let wait = Self::wait_internal(&mut self.ch);\n        let pin = &self.pin;\n        async move {\n            if pin.is_low() {\n                return;\n            }\n            wait.await;\n        }\n    }\n\n    /// Internal implementation for `wait()` and friends.\n    fn wait_internal(channel: &mut Peri<'_, AnyChannel>) -> impl Future<Output = ()> {\n        // NOTE: This is `-> impl Future` and not an `async fn` on purpose.\n        // Otherwise, events will only be detected starting at the first poll of the returned future.\n\n        let g = channel.regs();\n        let num = channel.number();\n        let waker = channel.waker();\n\n        // Enable interrupt\n        g.events_in(num).write_value(0);\n        g.intenset(INTNUM).write(|w| w.0 = 1 << num);\n\n        poll_fn(move |cx| {\n            CHANNEL_WAKERS[waker].register(cx.waker());\n\n            if g.events_in(num).read() != 0 {\n                Poll::Ready(())\n            } else {\n                Poll::Pending\n            }\n        })\n    }\n\n    /// Get the associated input pin.\n    pub fn pin(&self) -> &Input<'_> {\n        &self.pin\n    }\n\n    /// Returns the IN event, for use with PPI.\n    pub fn event_in(&self) -> Event<'d> {\n        let g = self.ch.regs();\n        Event::from_reg(g.events_in(self.ch.number()))\n    }\n}\n\n/// GPIOTE channel driver in output mode\npub struct OutputChannel<'d> {\n    ch: Peri<'d, AnyChannel>,\n    _pin: Output<'d>,\n}\n\nimpl OutputChannel<'static> {\n    /// Persist the channel's configuration for the rest of the program's lifetime. This method\n    /// should be preferred over [`core::mem::forget()`] because the `'static` bound prevents\n    /// accidental reuse of the underlying peripheral.\n    pub fn persist(self) {\n        core::mem::forget(self);\n    }\n}\n\nimpl<'d> Drop for OutputChannel<'d> {\n    fn drop(&mut self) {\n        let g = self.ch.regs();\n        let num = self.ch.number();\n        g.config(num).write(|w| w.set_mode(Mode::DISABLED));\n        g.intenclr(INTNUM).write(|w| w.0 = 1 << num);\n    }\n}\n\nimpl<'d> OutputChannel<'d> {\n    /// Create a new GPIOTE output channel driver.\n    #[cfg(feature = \"_nrf54l\")]\n    pub fn new<C: Channel, T: GpiotePin<Instance = C::Instance>>(\n        ch: Peri<'d, C>,\n        pin: Peri<'d, T>,\n        initial_output: Level,\n        drive: OutputDrive,\n        polarity: OutputChannelPolarity,\n    ) -> Self {\n        let pin = Output::new(pin, initial_output, drive);\n        let ch = ch.into();\n        Self::new_inner(ch, pin, polarity)\n    }\n\n    /// Create a new GPIOTE output channel driver.\n    #[cfg(not(feature = \"_nrf54l\"))]\n    pub fn new<C: Channel, T: GpioPin>(\n        ch: Peri<'d, C>,\n        pin: Peri<'d, T>,\n        initial_output: Level,\n        drive: OutputDrive,\n        polarity: OutputChannelPolarity,\n    ) -> Self {\n        let pin = Output::new(pin, initial_output, drive);\n        let ch = ch.into();\n        Self::new_inner(ch, pin, polarity)\n    }\n\n    fn new_inner(ch: Peri<'d, AnyChannel>, pin: Output<'d>, polarity: OutputChannelPolarity) -> Self {\n        let g = ch.regs();\n        let num = ch.number();\n\n        g.config(num).write(|w| {\n            w.set_mode(Mode::TASK);\n            match pin.is_set_high() {\n                true => w.set_outinit(Outinit::HIGH),\n                false => w.set_outinit(Outinit::LOW),\n            };\n            match polarity {\n                OutputChannelPolarity::Set => w.set_polarity(Polarity::HI_TO_LO),\n                OutputChannelPolarity::Clear => w.set_polarity(Polarity::LO_TO_HI),\n                OutputChannelPolarity::Toggle => w.set_polarity(Polarity::TOGGLE),\n            };\n            #[cfg(any(feature = \"nrf52833\", feature = \"nrf52840\", feature = \"_nrf5340\"))]\n            w.set_port(match pin.pin.pin.port() {\n                crate::gpio::Port::Port0 => false,\n                crate::gpio::Port::Port1 => true,\n            });\n            #[cfg(any(feature = \"_nrf54l\"))]\n            w.set_port(match pin.pin.pin.port() {\n                crate::gpio::Port::Port0 => 0,\n                crate::gpio::Port::Port1 => 1,\n                crate::gpio::Port::Port2 => 2,\n            });\n            w.set_psel(pin.pin.pin.pin());\n        });\n\n        OutputChannel { ch, _pin: pin }\n    }\n\n    /// Triggers the OUT task (does the action as configured with task_out_polarity, defaults to Toggle).\n    pub fn out(&self) {\n        let g = self.ch.regs();\n        g.tasks_out(self.ch.number()).write_value(1);\n    }\n\n    /// Triggers the SET task (set associated pin high).\n    #[cfg(not(feature = \"_nrf51\"))]\n    pub fn set(&self) {\n        let g = self.ch.regs();\n        g.tasks_set(self.ch.number()).write_value(1);\n    }\n\n    /// Triggers the CLEAR task (set associated pin low).\n    #[cfg(not(feature = \"_nrf51\"))]\n    pub fn clear(&self) {\n        let g = self.ch.regs();\n        g.tasks_clr(self.ch.number()).write_value(1);\n    }\n\n    /// Returns the OUT task, for use with PPI.\n    pub fn task_out(&self) -> Task<'d> {\n        let g = self.ch.regs();\n        Task::from_reg(g.tasks_out(self.ch.number()))\n    }\n\n    /// Returns the CLR task, for use with PPI.\n    #[cfg(not(feature = \"_nrf51\"))]\n    pub fn task_clr(&self) -> Task<'d> {\n        let g = self.ch.regs();\n        Task::from_reg(g.tasks_clr(self.ch.number()))\n    }\n\n    /// Returns the SET task, for use with PPI.\n    #[cfg(not(feature = \"_nrf51\"))]\n    pub fn task_set(&self) -> Task<'d> {\n        let g = self.ch.regs();\n        Task::from_reg(g.tasks_set(self.ch.number()))\n    }\n}\n\n// =======================\n\n#[must_use = \"futures do nothing unless you `.await` or poll them\"]\npub(crate) struct PortInputFuture<'a> {\n    pin: Peri<'a, AnyPin>,\n}\n\nimpl<'a> PortInputFuture<'a> {\n    fn new(pin: Peri<'a, impl GpioPin>) -> Self {\n        Self { pin: pin.into() }\n    }\n}\n\nimpl<'a> Unpin for PortInputFuture<'a> {}\n\nimpl<'a> Drop for PortInputFuture<'a> {\n    fn drop(&mut self) {\n        self.pin.conf().modify(|w| w.set_sense(Sense::DISABLED));\n    }\n}\n\nimpl<'a> Future for PortInputFuture<'a> {\n    type Output = ();\n\n    fn poll(self: core::pin::Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {\n        PORT_WAKERS[self.pin.pin_port() as usize].register(cx.waker());\n\n        if self.pin.conf().read().sense() == Sense::DISABLED {\n            Poll::Ready(())\n        } else {\n            Poll::Pending\n        }\n    }\n}\n\nimpl<'d> Input<'d> {\n    /// Wait until the pin is high. If it is already high, return immediately.\n    pub async fn wait_for_high(&mut self) {\n        self.pin.wait_for_high().await\n    }\n\n    /// Wait until the pin is low. If it is already low, return immediately.\n    pub async fn wait_for_low(&mut self) {\n        self.pin.wait_for_low().await\n    }\n\n    /// Wait for the pin to undergo a transition from low to high.\n    pub async fn wait_for_rising_edge(&mut self) {\n        self.pin.wait_for_rising_edge().await\n    }\n\n    /// Wait for the pin to undergo a transition from high to low.\n    pub async fn wait_for_falling_edge(&mut self) {\n        self.pin.wait_for_falling_edge().await\n    }\n\n    /// Wait for the pin to undergo any transition, i.e low to high OR high to low.\n    pub async fn wait_for_any_edge(&mut self) {\n        self.pin.wait_for_any_edge().await\n    }\n}\n\nimpl<'d> Flex<'d> {\n    /// Wait until the pin is high. If it is already high, return immediately.\n    pub async fn wait_for_high(&mut self) {\n        self.pin.conf().modify(|w| w.set_sense(Sense::HIGH));\n        PortInputFuture::new(self.pin.reborrow()).await\n    }\n\n    /// Wait until the pin is low. If it is already low, return immediately.\n    pub async fn wait_for_low(&mut self) {\n        self.pin.conf().modify(|w| w.set_sense(Sense::LOW));\n        PortInputFuture::new(self.pin.reborrow()).await\n    }\n\n    /// Wait for the pin to undergo a transition from low to high.\n    pub async fn wait_for_rising_edge(&mut self) {\n        self.wait_for_low().await;\n        self.wait_for_high().await;\n    }\n\n    /// Wait for the pin to undergo a transition from high to low.\n    pub async fn wait_for_falling_edge(&mut self) {\n        self.wait_for_high().await;\n        self.wait_for_low().await;\n    }\n\n    /// Wait for the pin to undergo any transition, i.e low to high OR high to low.\n    pub async fn wait_for_any_edge(&mut self) {\n        if self.is_high() {\n            self.pin.conf().modify(|w| w.set_sense(Sense::LOW));\n        } else {\n            self.pin.conf().modify(|w| w.set_sense(Sense::HIGH));\n        }\n        PortInputFuture::new(self.pin.reborrow()).await\n    }\n}\n// =======================\n//\n\ntrait SealedChannel {\n    fn waker(&self) -> usize;\n    fn regs(&self) -> pac::gpiote::Gpiote;\n}\n\n/// GPIOTE channel trait.\n///\n/// Implemented by all GPIOTE channels.\n#[allow(private_bounds)]\npub trait Channel: PeripheralType + SealedChannel + Into<AnyChannel> + Sized + 'static {\n    #[cfg(feature = \"_nrf54l\")]\n    /// GPIOTE instance this channel belongs to.\n    type Instance: GpioteInstance;\n    /// Get the channel number.\n    fn number(&self) -> usize;\n}\n\nstruct AnyChannel {\n    number: u8,\n    regs: pac::gpiote::Gpiote,\n    waker: u8,\n}\n\nimpl_peripheral!(AnyChannel);\n\nimpl SealedChannel for AnyChannel {\n    fn waker(&self) -> usize {\n        self.waker as usize\n    }\n\n    fn regs(&self) -> pac::gpiote::Gpiote {\n        self.regs\n    }\n}\n\n#[cfg(feature = \"_nrf54l\")]\nimpl AnyChannel {\n    fn number(&self) -> usize {\n        self.number as usize\n    }\n}\n\n#[cfg(not(feature = \"_nrf54l\"))]\nimpl Channel for AnyChannel {\n    fn number(&self) -> usize {\n        self.number as usize\n    }\n}\n\nmacro_rules! impl_channel {\n    ($type:ident, $inst:ident, $number:expr, $waker:expr) => {\n        impl SealedChannel for peripherals::$type {\n            fn waker(&self) -> usize {\n                $waker as usize\n            }\n\n            fn regs(&self) -> pac::gpiote::Gpiote {\n                pac::$inst\n            }\n        }\n        impl Channel for peripherals::$type {\n            #[cfg(feature = \"_nrf54l\")]\n            type Instance = peripherals::$inst;\n            fn number(&self) -> usize {\n                $number as usize\n            }\n        }\n\n        impl From<peripherals::$type> for AnyChannel {\n            fn from(val: peripherals::$type) -> Self {\n                Self {\n                    number: val.number() as u8,\n                    waker: val.waker() as u8,\n                    regs: val.regs(),\n                }\n            }\n        }\n    };\n}\n\ncfg_if::cfg_if! {\n    if #[cfg(feature = \"_nrf54l\")] {\n        trait SealedGpioteInstance {}\n        /// Represents a GPIOTE instance.\n        #[allow(private_bounds)]\n        pub trait GpioteInstance: PeripheralType + SealedGpioteInstance + Sized + 'static {}\n\n        macro_rules! impl_gpiote {\n            ($type:ident) => {\n                impl SealedGpioteInstance for peripherals::$type {}\n                impl GpioteInstance for peripherals::$type {}\n            };\n        }\n\n        pub(crate) trait SealedGpiotePin {}\n\n        /// Represents a GPIO pin that can be used with GPIOTE.\n        #[allow(private_bounds)]\n        pub trait GpiotePin: GpioPin + SealedGpiotePin {\n            /// The GPIOTE instance this pin belongs to.\n            type Instance: GpioteInstance;\n        }\n\n        macro_rules! impl_gpiote_pin {\n            ($type:ident, $inst:ident) => {\n                impl crate::gpiote::SealedGpiotePin for peripherals::$type {}\n                impl crate::gpiote::GpiotePin for peripherals::$type {\n                    type Instance = peripherals::$inst;\n                }\n            };\n        }\n\n        impl_gpiote!(GPIOTE20);\n        impl_gpiote!(GPIOTE30);\n        impl_channel!(GPIOTE20_CH0, GPIOTE20, 0, 0);\n        impl_channel!(GPIOTE20_CH1, GPIOTE20, 1, 1);\n        impl_channel!(GPIOTE20_CH2, GPIOTE20, 2, 2);\n        impl_channel!(GPIOTE20_CH3, GPIOTE20, 3, 3);\n        impl_channel!(GPIOTE20_CH4, GPIOTE20, 4, 4);\n        impl_channel!(GPIOTE20_CH5, GPIOTE20, 5, 5);\n        impl_channel!(GPIOTE20_CH6, GPIOTE20, 6, 6);\n        impl_channel!(GPIOTE20_CH7, GPIOTE20, 7, 7);\n\n        impl_channel!(GPIOTE30_CH0, GPIOTE30, 0, 8);\n        impl_channel!(GPIOTE30_CH1, GPIOTE30, 1, 9);\n        impl_channel!(GPIOTE30_CH2, GPIOTE30, 2, 10);\n        impl_channel!(GPIOTE30_CH3, GPIOTE30, 3, 11);\n    } else if #[cfg(feature = \"_nrf51\")] {\n        impl_channel!(GPIOTE_CH0, GPIOTE, 0, 0);\n        impl_channel!(GPIOTE_CH1, GPIOTE, 1, 1);\n        impl_channel!(GPIOTE_CH2, GPIOTE, 2, 2);\n        impl_channel!(GPIOTE_CH3, GPIOTE, 3, 3);\n    } else if #[cfg(all(feature = \"_s\", any(feature = \"_nrf91\", feature = \"_nrf5340\")))] {\n        impl_channel!(GPIOTE_CH0, GPIOTE0, 0, 0);\n        impl_channel!(GPIOTE_CH1, GPIOTE0, 1, 1);\n        impl_channel!(GPIOTE_CH2, GPIOTE0, 2, 2);\n        impl_channel!(GPIOTE_CH3, GPIOTE0, 3, 3);\n        impl_channel!(GPIOTE_CH4, GPIOTE0, 4, 4);\n        impl_channel!(GPIOTE_CH5, GPIOTE0, 5, 5);\n        impl_channel!(GPIOTE_CH6, GPIOTE0, 6, 6);\n        impl_channel!(GPIOTE_CH7, GPIOTE0, 7, 7);\n    } else if #[cfg(all(feature = \"_ns\", any(feature = \"_nrf91\", feature = \"_nrf5340\")))] {\n        impl_channel!(GPIOTE_CH0, GPIOTE1, 0, 0);\n        impl_channel!(GPIOTE_CH1, GPIOTE1, 1, 1);\n        impl_channel!(GPIOTE_CH2, GPIOTE1, 2, 2);\n        impl_channel!(GPIOTE_CH3, GPIOTE1, 3, 3);\n        impl_channel!(GPIOTE_CH4, GPIOTE1, 4, 4);\n        impl_channel!(GPIOTE_CH5, GPIOTE1, 5, 5);\n        impl_channel!(GPIOTE_CH6, GPIOTE1, 6, 6);\n        impl_channel!(GPIOTE_CH7, GPIOTE1, 7, 7);\n    } else {\n        impl_channel!(GPIOTE_CH0, GPIOTE, 0, 0);\n        impl_channel!(GPIOTE_CH1, GPIOTE, 1, 1);\n        impl_channel!(GPIOTE_CH2, GPIOTE, 2, 2);\n        impl_channel!(GPIOTE_CH3, GPIOTE, 3, 3);\n        impl_channel!(GPIOTE_CH4, GPIOTE, 4, 4);\n        impl_channel!(GPIOTE_CH5, GPIOTE, 5, 5);\n        impl_channel!(GPIOTE_CH6, GPIOTE, 6, 6);\n        impl_channel!(GPIOTE_CH7, GPIOTE, 7, 7);\n    }\n}\n\n// ====================\n\nmod eh02 {\n    use super::*;\n\n    impl<'d> embedded_hal_02::digital::v2::InputPin for InputChannel<'d> {\n        type Error = Infallible;\n\n        fn is_high(&self) -> Result<bool, Self::Error> {\n            Ok(self.pin.is_high())\n        }\n\n        fn is_low(&self) -> Result<bool, Self::Error> {\n            Ok(self.pin.is_low())\n        }\n    }\n}\n\nimpl<'d> embedded_hal_1::digital::ErrorType for InputChannel<'d> {\n    type Error = Infallible;\n}\n\nimpl<'d> embedded_hal_1::digital::InputPin for InputChannel<'d> {\n    fn is_high(&mut self) -> Result<bool, Self::Error> {\n        Ok(self.pin.is_high())\n    }\n\n    fn is_low(&mut self) -> Result<bool, Self::Error> {\n        Ok(self.pin.is_low())\n    }\n}\n\nimpl<'d> embedded_hal_async::digital::Wait for Input<'d> {\n    async fn wait_for_high(&mut self) -> Result<(), Self::Error> {\n        Ok(self.wait_for_high().await)\n    }\n\n    async fn wait_for_low(&mut self) -> Result<(), Self::Error> {\n        Ok(self.wait_for_low().await)\n    }\n\n    async fn wait_for_rising_edge(&mut self) -> Result<(), Self::Error> {\n        Ok(self.wait_for_rising_edge().await)\n    }\n\n    async fn wait_for_falling_edge(&mut self) -> Result<(), Self::Error> {\n        Ok(self.wait_for_falling_edge().await)\n    }\n\n    async fn wait_for_any_edge(&mut self) -> Result<(), Self::Error> {\n        Ok(self.wait_for_any_edge().await)\n    }\n}\n\nimpl<'d> embedded_hal_async::digital::Wait for Flex<'d> {\n    async fn wait_for_high(&mut self) -> Result<(), Self::Error> {\n        Ok(self.wait_for_high().await)\n    }\n\n    async fn wait_for_low(&mut self) -> Result<(), Self::Error> {\n        Ok(self.wait_for_low().await)\n    }\n\n    async fn wait_for_rising_edge(&mut self) -> Result<(), Self::Error> {\n        Ok(self.wait_for_rising_edge().await)\n    }\n\n    async fn wait_for_falling_edge(&mut self) -> Result<(), Self::Error> {\n        Ok(self.wait_for_falling_edge().await)\n    }\n\n    async fn wait_for_any_edge(&mut self) -> Result<(), Self::Error> {\n        Ok(self.wait_for_any_edge().await)\n    }\n}\n"
  },
  {
    "path": "embassy-nrf/src/i2s.rs",
    "content": "//! Inter-IC Sound (I2S) driver.\n\n#![macro_use]\n\nuse core::future::poll_fn;\nuse core::marker::PhantomData;\nuse core::mem::size_of;\nuse core::ops::{Deref, DerefMut};\nuse core::sync::atomic::{AtomicBool, Ordering, compiler_fence};\nuse core::task::Poll;\n\nuse embassy_hal_internal::drop::OnDrop;\nuse embassy_hal_internal::{Peri, PeripheralType};\nuse embassy_sync::waitqueue::AtomicWaker;\n\nuse crate::gpio::{AnyPin, Pin as GpioPin, PselBits};\nuse crate::interrupt::typelevel::Interrupt;\nuse crate::pac::i2s::vals;\nuse crate::util::slice_in_ram_or;\nuse crate::{EASY_DMA_SIZE, interrupt, pac};\n\n/// Type alias for `MultiBuffering` with 2 buffers.\npub type DoubleBuffering<S, const NS: usize> = MultiBuffering<S, 2, NS>;\n\n/// I2S transfer error.\n#[derive(Debug, Clone, Copy, PartialEq, Eq)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\n#[non_exhaustive]\npub enum Error {\n    /// The buffer is too long.\n    BufferTooLong,\n    /// The buffer is empty.\n    BufferZeroLength,\n    /// The buffer is not in data RAM. It's most likely in flash, and nRF's DMA cannot access flash.\n    BufferNotInRAM,\n    /// The buffer address is not aligned.\n    BufferMisaligned,\n    /// The buffer length is not a multiple of the alignment.\n    BufferLengthMisaligned,\n}\n\n/// I2S configuration.\n#[derive(Clone)]\n#[non_exhaustive]\npub struct Config {\n    /// Sample width\n    pub sample_width: SampleWidth,\n    /// Alignment\n    pub align: Align,\n    /// Sample format\n    pub format: Format,\n    /// Channel configuration.\n    pub channels: Channels,\n}\n\nimpl Default for Config {\n    fn default() -> Self {\n        Self {\n            sample_width: SampleWidth::_16bit,\n            align: Align::Left,\n            format: Format::I2S,\n            channels: Channels::Stereo,\n        }\n    }\n}\n\n/// I2S clock configuration.\n#[derive(Debug, Eq, PartialEq, Clone, Copy)]\npub struct MasterClock {\n    freq: MckFreq,\n    ratio: Ratio,\n}\n\nimpl MasterClock {\n    /// Create a new `MasterClock`.\n    pub fn new(freq: MckFreq, ratio: Ratio) -> Self {\n        Self { freq, ratio }\n    }\n}\n\nimpl MasterClock {\n    /// Get the sample rate for this clock configuration.\n    pub fn sample_rate(&self) -> u32 {\n        self.freq.to_frequency() / self.ratio.to_divisor()\n    }\n}\n\n/// Master clock generator frequency.\n#[derive(Debug, Eq, PartialEq, Clone, Copy)]\npub enum MckFreq {\n    /// 32 Mhz / 8 = 4000.00 kHz\n    _32MDiv8,\n    /// 32 Mhz / 10 = 3200.00 kHz\n    _32MDiv10,\n    /// 32 Mhz / 11 = 2909.09 kHz\n    _32MDiv11,\n    /// 32 Mhz / 15 = 2133.33 kHz\n    _32MDiv15,\n    /// 32 Mhz / 16 = 2000.00 kHz\n    _32MDiv16,\n    /// 32 Mhz / 21 = 1523.81 kHz\n    _32MDiv21,\n    /// 32 Mhz / 23 = 1391.30 kHz\n    _32MDiv23,\n    /// 32 Mhz / 30 = 1066.67 kHz\n    _32MDiv30,\n    /// 32 Mhz / 31 = 1032.26 kHz\n    _32MDiv31,\n    /// 32 Mhz / 32 = 1000.00 kHz\n    _32MDiv32,\n    /// 32 Mhz / 42 = 761.90 kHz\n    _32MDiv42,\n    /// 32 Mhz / 63 = 507.94 kHz\n    _32MDiv63,\n    /// 32 Mhz / 125 = 256.00 kHz\n    _32MDiv125,\n}\n\nimpl MckFreq {\n    const REGISTER_VALUES: &'static [vals::Mckfreq] = &[\n        vals::Mckfreq::_32MDIV8,\n        vals::Mckfreq::_32MDIV10,\n        vals::Mckfreq::_32MDIV11,\n        vals::Mckfreq::_32MDIV15,\n        vals::Mckfreq::_32MDIV16,\n        vals::Mckfreq::_32MDIV21,\n        vals::Mckfreq::_32MDIV23,\n        vals::Mckfreq::_32MDIV30,\n        vals::Mckfreq::_32MDIV31,\n        vals::Mckfreq::_32MDIV32,\n        vals::Mckfreq::_32MDIV42,\n        vals::Mckfreq::_32MDIV63,\n        vals::Mckfreq::_32MDIV125,\n    ];\n\n    const FREQUENCIES: &'static [u32] = &[\n        4000000, 3200000, 2909090, 2133333, 2000000, 1523809, 1391304, 1066666, 1032258, 1000000, 761904, 507936,\n        256000,\n    ];\n\n    /// Return the value that needs to be written to the register.\n    pub fn to_register_value(&self) -> vals::Mckfreq {\n        Self::REGISTER_VALUES[usize::from(*self)]\n    }\n\n    /// Return the master clock frequency.\n    pub fn to_frequency(&self) -> u32 {\n        Self::FREQUENCIES[usize::from(*self)]\n    }\n}\n\nimpl From<MckFreq> for usize {\n    fn from(variant: MckFreq) -> Self {\n        variant as _\n    }\n}\n\n/// Master clock frequency ratio\n///\n/// Sample Rate = LRCK = MCK / Ratio\n///\n#[derive(Debug, Eq, PartialEq, Clone, Copy)]\npub enum Ratio {\n    /// Divide by 32\n    _32x,\n    /// Divide by 48\n    _48x,\n    /// Divide by 64\n    _64x,\n    /// Divide by 96\n    _96x,\n    /// Divide by 128\n    _128x,\n    /// Divide by 192\n    _192x,\n    /// Divide by 256\n    _256x,\n    /// Divide by 384\n    _384x,\n    /// Divide by 512\n    _512x,\n}\n\nimpl Ratio {\n    const RATIOS: &'static [u32] = &[32, 48, 64, 96, 128, 192, 256, 384, 512];\n\n    /// Return the value that needs to be written to the register.\n    pub fn to_register_value(&self) -> vals::Ratio {\n        vals::Ratio::from_bits(*self as u8)\n    }\n\n    /// Return the divisor for this ratio\n    pub fn to_divisor(&self) -> u32 {\n        Self::RATIOS[usize::from(*self)]\n    }\n}\n\nimpl From<Ratio> for usize {\n    fn from(variant: Ratio) -> Self {\n        variant as _\n    }\n}\n\n/// Approximate sample rates.\n///\n/// Those are common sample rates that can not be configured without an small error.\n///\n/// For custom master clock configuration, please refer to [MasterClock].\n#[derive(Clone, Copy)]\npub enum ApproxSampleRate {\n    /// 11025 Hz\n    _11025,\n    /// 16000 Hz\n    _16000,\n    /// 22050 Hz\n    _22050,\n    /// 32000 Hz\n    _32000,\n    /// 44100 Hz\n    _44100,\n    /// 48000 Hz\n    _48000,\n}\n\nimpl From<ApproxSampleRate> for MasterClock {\n    fn from(value: ApproxSampleRate) -> Self {\n        match value {\n            // error = 86\n            ApproxSampleRate::_11025 => MasterClock::new(MckFreq::_32MDiv15, Ratio::_192x),\n            // error = 127\n            ApproxSampleRate::_16000 => MasterClock::new(MckFreq::_32MDiv21, Ratio::_96x),\n            // error = 172\n            ApproxSampleRate::_22050 => MasterClock::new(MckFreq::_32MDiv15, Ratio::_96x),\n            // error = 254\n            ApproxSampleRate::_32000 => MasterClock::new(MckFreq::_32MDiv21, Ratio::_48x),\n            // error = 344\n            ApproxSampleRate::_44100 => MasterClock::new(MckFreq::_32MDiv15, Ratio::_48x),\n            // error = 381\n            ApproxSampleRate::_48000 => MasterClock::new(MckFreq::_32MDiv21, Ratio::_32x),\n        }\n    }\n}\n\nimpl ApproxSampleRate {\n    /// Get the sample rate as an integer.\n    pub fn sample_rate(&self) -> u32 {\n        MasterClock::from(*self).sample_rate()\n    }\n}\n\n/// Exact sample rates.\n///\n/// Those are non standard sample rates that can be configured without error.\n///\n/// For custom master clock configuration, please refer to [vals::Mode].\n#[derive(Clone, Copy)]\npub enum ExactSampleRate {\n    /// 8000 Hz\n    _8000,\n    /// 10582 Hz\n    _10582,\n    /// 12500 Hz\n    _12500,\n    /// 15625 Hz\n    _15625,\n    /// 15873 Hz\n    _15873,\n    /// 25000 Hz\n    _25000,\n    /// 31250 Hz\n    _31250,\n    /// 50000 Hz\n    _50000,\n    /// 62500 Hz\n    _62500,\n    /// 100000 Hz\n    _100000,\n    /// 125000 Hz\n    _125000,\n}\n\nimpl ExactSampleRate {\n    /// Get the sample rate as an integer.\n    pub fn sample_rate(&self) -> u32 {\n        MasterClock::from(*self).sample_rate()\n    }\n}\n\nimpl From<ExactSampleRate> for MasterClock {\n    fn from(value: ExactSampleRate) -> Self {\n        match value {\n            ExactSampleRate::_8000 => MasterClock::new(MckFreq::_32MDiv125, Ratio::_32x),\n            ExactSampleRate::_10582 => MasterClock::new(MckFreq::_32MDiv63, Ratio::_48x),\n            ExactSampleRate::_12500 => MasterClock::new(MckFreq::_32MDiv10, Ratio::_256x),\n            ExactSampleRate::_15625 => MasterClock::new(MckFreq::_32MDiv32, Ratio::_64x),\n            ExactSampleRate::_15873 => MasterClock::new(MckFreq::_32MDiv63, Ratio::_32x),\n            ExactSampleRate::_25000 => MasterClock::new(MckFreq::_32MDiv10, Ratio::_128x),\n            ExactSampleRate::_31250 => MasterClock::new(MckFreq::_32MDiv32, Ratio::_32x),\n            ExactSampleRate::_50000 => MasterClock::new(MckFreq::_32MDiv10, Ratio::_64x),\n            ExactSampleRate::_62500 => MasterClock::new(MckFreq::_32MDiv16, Ratio::_32x),\n            ExactSampleRate::_100000 => MasterClock::new(MckFreq::_32MDiv10, Ratio::_32x),\n            ExactSampleRate::_125000 => MasterClock::new(MckFreq::_32MDiv8, Ratio::_32x),\n        }\n    }\n}\n\n/// Sample width.\n#[derive(Debug, Eq, PartialEq, Clone, Copy)]\npub enum SampleWidth {\n    /// 8 bit samples.\n    _8bit,\n    /// 16 bit samples.\n    _16bit,\n    /// 24 bit samples.\n    _24bit,\n}\n\nimpl From<SampleWidth> for vals::Swidth {\n    fn from(variant: SampleWidth) -> Self {\n        vals::Swidth::from_bits(variant as u8)\n    }\n}\n\n/// Channel used for the most significant sample value in a frame.\n#[derive(Debug, Eq, PartialEq, Clone, Copy)]\npub enum Align {\n    /// Left-align samples.\n    Left,\n    /// Right-align samples.\n    Right,\n}\n\nimpl From<Align> for vals::Align {\n    fn from(variant: Align) -> Self {\n        match variant {\n            Align::Left => vals::Align::LEFT,\n            Align::Right => vals::Align::RIGHT,\n        }\n    }\n}\n\n/// Frame format.\n#[derive(Debug, Eq, PartialEq, Clone, Copy)]\npub enum Format {\n    /// I2S frame format\n    I2S,\n    /// Aligned frame format\n    Aligned,\n}\n\nimpl From<Format> for vals::Format {\n    fn from(variant: Format) -> Self {\n        match variant {\n            Format::I2S => vals::Format::I2S,\n            Format::Aligned => vals::Format::ALIGNED,\n        }\n    }\n}\n\n/// Channels\n#[derive(Debug, Eq, PartialEq, Clone, Copy)]\npub enum Channels {\n    /// Stereo (2 channels).\n    Stereo,\n    /// Mono, left channel only.\n    MonoLeft,\n    /// Mono, right channel only.\n    MonoRight,\n}\n\nimpl From<Channels> for vals::Channels {\n    fn from(variant: Channels) -> Self {\n        vals::Channels::from_bits(variant as u8)\n    }\n}\n\n/// Interrupt handler.\npub struct InterruptHandler<T: Instance> {\n    _phantom: PhantomData<T>,\n}\n\nimpl<T: Instance> interrupt::typelevel::Handler<T::Interrupt> for InterruptHandler<T> {\n    unsafe fn on_interrupt() {\n        let device = Device::new(T::regs());\n        let s = T::state();\n\n        if device.is_tx_ptr_updated() {\n            trace!(\"TX INT\");\n            s.tx_waker.wake();\n            device.disable_tx_ptr_interrupt();\n        }\n\n        if device.is_rx_ptr_updated() {\n            trace!(\"RX INT\");\n            s.rx_waker.wake();\n            device.disable_rx_ptr_interrupt();\n        }\n\n        if device.is_stopped() {\n            trace!(\"STOPPED INT\");\n            s.stop_waker.wake();\n            device.disable_stopped_interrupt();\n        }\n    }\n}\n\n/// I2S driver.\npub struct I2S<'d> {\n    r: pac::i2s::I2s,\n    state: &'static State,\n    mck: Option<Peri<'d, AnyPin>>,\n    sck: Peri<'d, AnyPin>,\n    lrck: Peri<'d, AnyPin>,\n    sdin: Option<Peri<'d, AnyPin>>,\n    sdout: Option<Peri<'d, AnyPin>>,\n    master_clock: Option<MasterClock>,\n    config: Config,\n}\n\nimpl<'d> I2S<'d> {\n    /// Create a new I2S in master mode\n    pub fn new_master<T: Instance>(\n        _i2s: Peri<'d, T>,\n        _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd,\n        mck: Peri<'d, impl GpioPin>,\n        sck: Peri<'d, impl GpioPin>,\n        lrck: Peri<'d, impl GpioPin>,\n        master_clock: MasterClock,\n        config: Config,\n    ) -> Self {\n        T::Interrupt::unpend();\n        unsafe { T::Interrupt::enable() };\n\n        Self {\n            r: T::regs(),\n            state: T::state(),\n            mck: Some(mck.into()),\n            sck: sck.into(),\n            lrck: lrck.into(),\n            sdin: None,\n            sdout: None,\n            master_clock: Some(master_clock),\n            config,\n        }\n    }\n\n    /// Create a new I2S in slave mode\n    pub fn new_slave<T: Instance>(\n        _i2s: Peri<'d, T>,\n        _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd,\n        sck: Peri<'d, impl GpioPin>,\n        lrck: Peri<'d, impl GpioPin>,\n        config: Config,\n    ) -> Self {\n        T::Interrupt::unpend();\n        unsafe { T::Interrupt::enable() };\n\n        Self {\n            r: T::regs(),\n            state: T::state(),\n            mck: None,\n            sck: sck.into(),\n            lrck: lrck.into(),\n            sdin: None,\n            sdout: None,\n            master_clock: None,\n            config,\n        }\n    }\n\n    /// I2S output only\n    pub fn output<S: Sample, const NB: usize, const NS: usize>(\n        mut self,\n        sdout: Peri<'d, impl GpioPin>,\n        buffers: MultiBuffering<S, NB, NS>,\n    ) -> OutputStream<'d, S, NB, NS> {\n        self.sdout = Some(sdout.into());\n        let p = self.build();\n        OutputStream {\n            r: p.0,\n            state: p.1,\n            _phantom: PhantomData,\n            buffers,\n        }\n    }\n\n    /// I2S input only\n    pub fn input<S: Sample, const NB: usize, const NS: usize>(\n        mut self,\n        sdin: Peri<'d, impl GpioPin>,\n        buffers: MultiBuffering<S, NB, NS>,\n    ) -> InputStream<'d, S, NB, NS> {\n        self.sdin = Some(sdin.into());\n        let p = self.build();\n        InputStream {\n            r: p.0,\n            state: p.1,\n            buffers,\n            _phantom: PhantomData,\n        }\n    }\n\n    /// I2S full duplex (input and output)\n    pub fn full_duplex<S: Sample, const NB: usize, const NS: usize>(\n        mut self,\n        sdin: Peri<'d, impl GpioPin>,\n        sdout: Peri<'d, impl GpioPin>,\n        buffers_out: MultiBuffering<S, NB, NS>,\n        buffers_in: MultiBuffering<S, NB, NS>,\n    ) -> FullDuplexStream<'d, S, NB, NS> {\n        self.sdout = Some(sdout.into());\n        self.sdin = Some(sdin.into());\n        let p = self.build();\n\n        FullDuplexStream {\n            r: p.0,\n            state: p.1,\n            _phantom: PhantomData,\n            buffers_out,\n            buffers_in,\n        }\n    }\n\n    fn build(self) -> (pac::i2s::I2s, &'static State) {\n        self.apply_config();\n        self.select_pins();\n        self.setup_interrupt();\n\n        let device = Device::new(self.r);\n        device.enable();\n\n        (self.r, self.state)\n    }\n\n    fn apply_config(&self) {\n        let c = self.r.config();\n        match &self.master_clock {\n            Some(MasterClock { freq, ratio }) => {\n                c.mode().write(|w| w.set_mode(vals::Mode::MASTER));\n                c.mcken().write(|w| w.set_mcken(true));\n                c.mckfreq().write(|w| w.set_mckfreq(freq.to_register_value()));\n                c.ratio().write(|w| w.set_ratio(ratio.to_register_value()));\n            }\n            None => {\n                c.mode().write(|w| w.set_mode(vals::Mode::SLAVE));\n            }\n        };\n\n        c.swidth().write(|w| w.set_swidth(self.config.sample_width.into()));\n        c.align().write(|w| w.set_align(self.config.align.into()));\n        c.format().write(|w| w.set_format(self.config.format.into()));\n        c.channels().write(|w| w.set_channels(self.config.channels.into()));\n    }\n\n    fn select_pins(&self) {\n        let psel = self.r.psel();\n        psel.mck().write_value(self.mck.psel_bits());\n        psel.sck().write_value(self.sck.psel_bits());\n        psel.lrck().write_value(self.lrck.psel_bits());\n        psel.sdin().write_value(self.sdin.psel_bits());\n        psel.sdout().write_value(self.sdout.psel_bits());\n    }\n\n    fn setup_interrupt(&self) {\n        // Interrupt is already set up in constructor\n\n        let device = Device::new(self.r);\n        device.disable_tx_ptr_interrupt();\n        device.disable_rx_ptr_interrupt();\n        device.disable_stopped_interrupt();\n\n        device.reset_tx_ptr_event();\n        device.reset_rx_ptr_event();\n        device.reset_stopped_event();\n\n        device.enable_tx_ptr_interrupt();\n        device.enable_rx_ptr_interrupt();\n        device.enable_stopped_interrupt();\n    }\n\n    async fn stop(r: pac::i2s::I2s, state: &State) {\n        compiler_fence(Ordering::SeqCst);\n\n        let device = Device::new(r);\n        device.stop();\n\n        state.started.store(false, Ordering::Relaxed);\n\n        poll_fn(|cx| {\n            state.stop_waker.register(cx.waker());\n\n            if device.is_stopped() {\n                trace!(\"STOP: Ready\");\n                device.reset_stopped_event();\n                Poll::Ready(())\n            } else {\n                trace!(\"STOP: Pending\");\n                Poll::Pending\n            }\n        })\n        .await;\n\n        device.disable();\n    }\n\n    async fn send_from_ram<S>(r: pac::i2s::I2s, state: &State, buffer_ptr: *const [S]) -> Result<(), Error>\n    where\n        S: Sample,\n    {\n        trace!(\"SEND: {}\", buffer_ptr as *const S as u32);\n\n        slice_in_ram_or(buffer_ptr, Error::BufferNotInRAM)?;\n\n        compiler_fence(Ordering::SeqCst);\n\n        let device = Device::new(r);\n\n        device.update_tx(buffer_ptr)?;\n\n        Self::wait_tx_ptr_update(r, state).await;\n\n        compiler_fence(Ordering::SeqCst);\n\n        Ok(())\n    }\n\n    async fn wait_tx_ptr_update(r: pac::i2s::I2s, state: &State) {\n        let drop = OnDrop::new(move || {\n            trace!(\"TX DROP: Stopping\");\n\n            let device = Device::new(r);\n            device.disable_tx_ptr_interrupt();\n            device.reset_tx_ptr_event();\n            device.disable_tx();\n\n            // TX is stopped almost instantly, spinning is fine.\n            while !device.is_tx_ptr_updated() {}\n\n            trace!(\"TX DROP: Stopped\");\n        });\n\n        poll_fn(|cx| {\n            state.tx_waker.register(cx.waker());\n\n            let device = Device::new(r);\n            if device.is_tx_ptr_updated() {\n                trace!(\"TX POLL: Ready\");\n                device.reset_tx_ptr_event();\n                device.enable_tx_ptr_interrupt();\n                Poll::Ready(())\n            } else {\n                trace!(\"TX POLL: Pending\");\n                Poll::Pending\n            }\n        })\n        .await;\n\n        drop.defuse();\n    }\n\n    async fn receive_from_ram<S>(r: pac::i2s::I2s, state: &State, buffer_ptr: *mut [S]) -> Result<(), Error>\n    where\n        S: Sample,\n    {\n        trace!(\"RECEIVE: {}\", buffer_ptr as *const S as u32);\n\n        // NOTE: RAM slice check for rx is not necessary, as a mutable\n        // slice can only be built from data located in RAM.\n\n        compiler_fence(Ordering::SeqCst);\n\n        let device = Device::new(r);\n\n        device.update_rx(buffer_ptr)?;\n\n        Self::wait_rx_ptr_update(r, state).await;\n\n        compiler_fence(Ordering::SeqCst);\n\n        Ok(())\n    }\n\n    async fn wait_rx_ptr_update(r: pac::i2s::I2s, state: &State) {\n        let drop = OnDrop::new(move || {\n            trace!(\"RX DROP: Stopping\");\n\n            let device = Device::new(r);\n            device.disable_rx_ptr_interrupt();\n            device.reset_rx_ptr_event();\n            device.disable_rx();\n\n            // TX is stopped almost instantly, spinning is fine.\n            while !device.is_rx_ptr_updated() {}\n\n            trace!(\"RX DROP: Stopped\");\n        });\n\n        poll_fn(|cx| {\n            state.rx_waker.register(cx.waker());\n\n            let device = Device::new(r);\n            if device.is_rx_ptr_updated() {\n                trace!(\"RX POLL: Ready\");\n                device.reset_rx_ptr_event();\n                device.enable_rx_ptr_interrupt();\n                Poll::Ready(())\n            } else {\n                trace!(\"RX POLL: Pending\");\n                Poll::Pending\n            }\n        })\n        .await;\n\n        drop.defuse();\n    }\n}\n\n/// I2S output\npub struct OutputStream<'d, S: Sample, const NB: usize, const NS: usize> {\n    r: pac::i2s::I2s,\n    state: &'static State,\n    buffers: MultiBuffering<S, NB, NS>,\n    _phantom: PhantomData<&'d ()>,\n}\n\nimpl<'d, S: Sample, const NB: usize, const NS: usize> OutputStream<'d, S, NB, NS> {\n    /// Get a mutable reference to the current buffer.\n    pub fn buffer(&mut self) -> &mut [S] {\n        self.buffers.get_mut()\n    }\n\n    /// Prepare the initial buffer and start the I2S transfer.\n    pub async fn start(&mut self) -> Result<(), Error>\n    where\n        S: Sample,\n    {\n        let device = Device::new(self.r);\n\n        if self.state.started.load(Ordering::Relaxed) {\n            self.stop().await;\n        }\n\n        device.enable();\n        device.enable_tx();\n\n        device.update_tx(self.buffers.switch())?;\n\n        self.state.started.store(true, Ordering::Relaxed);\n\n        device.start();\n\n        I2S::wait_tx_ptr_update(self.r, self.state).await;\n\n        Ok(())\n    }\n\n    /// Stops the I2S transfer and waits until it has stopped.\n    #[inline(always)]\n    pub async fn stop(&self) {\n        I2S::stop(self.r, self.state).await\n    }\n\n    /// Sends the current buffer for transmission in the DMA.\n    /// Switches to use the next available buffer.\n    pub async fn send(&mut self) -> Result<(), Error>\n    where\n        S: Sample,\n    {\n        I2S::send_from_ram(self.r, self.state, self.buffers.switch()).await\n    }\n}\n\n/// I2S input\npub struct InputStream<'d, S: Sample, const NB: usize, const NS: usize> {\n    r: pac::i2s::I2s,\n    state: &'static State,\n    buffers: MultiBuffering<S, NB, NS>,\n    _phantom: PhantomData<&'d ()>,\n}\n\nimpl<'d, S: Sample, const NB: usize, const NS: usize> InputStream<'d, S, NB, NS> {\n    /// Get a mutable reference to the current buffer.\n    pub fn buffer(&mut self) -> &mut [S] {\n        self.buffers.get_mut()\n    }\n\n    /// Prepare the initial buffer and start the I2S transfer.\n    pub async fn start(&mut self) -> Result<(), Error>\n    where\n        S: Sample,\n    {\n        let device = Device::new(self.r);\n\n        if self.state.started.load(Ordering::Relaxed) {\n            self.stop().await;\n        }\n\n        device.enable();\n        device.enable_rx();\n\n        device.update_rx(self.buffers.switch())?;\n\n        self.state.started.store(true, Ordering::Relaxed);\n\n        device.start();\n\n        I2S::wait_rx_ptr_update(self.r, self.state).await;\n\n        Ok(())\n    }\n\n    /// Stops the I2S transfer and waits until it has stopped.\n    #[inline(always)]\n    pub async fn stop(&self) {\n        I2S::stop(self.r, self.state).await\n    }\n\n    /// Sets the current buffer for reception from the DMA.\n    /// Switches to use the next available buffer.\n    #[allow(unused_mut)]\n    pub async fn receive(&mut self) -> Result<(), Error>\n    where\n        S: Sample,\n    {\n        I2S::receive_from_ram(self.r, self.state, self.buffers.switch_mut()).await\n    }\n}\n\n/// I2S full duplex stream (input & output)\npub struct FullDuplexStream<'d, S: Sample, const NB: usize, const NS: usize> {\n    r: pac::i2s::I2s,\n    state: &'static State,\n    buffers_out: MultiBuffering<S, NB, NS>,\n    buffers_in: MultiBuffering<S, NB, NS>,\n    _phantom: PhantomData<&'d ()>,\n}\n\nimpl<'d, S: Sample, const NB: usize, const NS: usize> FullDuplexStream<'d, S, NB, NS> {\n    /// Get the current output and input buffers.\n    pub fn buffers(&mut self) -> (&mut [S], &[S]) {\n        (self.buffers_out.get_mut(), self.buffers_in.get())\n    }\n\n    /// Prepare the initial buffers and start the I2S transfer.\n    pub async fn start(&mut self) -> Result<(), Error>\n    where\n        S: Sample,\n    {\n        let device = Device::new(self.r);\n\n        if self.state.started.load(Ordering::Relaxed) {\n            self.stop().await;\n        }\n\n        device.enable();\n        device.enable_tx();\n        device.enable_rx();\n\n        device.update_tx(self.buffers_out.switch())?;\n        device.update_rx(self.buffers_in.switch_mut())?;\n\n        self.state.started.store(true, Ordering::Relaxed);\n\n        device.start();\n\n        I2S::wait_tx_ptr_update(self.r, self.state).await;\n        I2S::wait_rx_ptr_update(self.r, self.state).await;\n\n        Ok(())\n    }\n\n    /// Stops the I2S transfer and waits until it has stopped.\n    #[inline(always)]\n    pub async fn stop(&self) {\n        I2S::stop(self.r, self.state).await\n    }\n\n    /// Sets the current buffers for output and input for transmission/reception from the DMA.\n    /// Switch to use the next available buffers for output/input.\n    pub async fn send_and_receive(&mut self) -> Result<(), Error>\n    where\n        S: Sample,\n    {\n        I2S::send_from_ram(self.r, self.state, self.buffers_out.switch()).await?;\n        I2S::receive_from_ram(self.r, self.state, self.buffers_in.switch_mut()).await?;\n        Ok(())\n    }\n}\n\n/// Helper encapsulating common I2S device operations.\nstruct Device(pac::i2s::I2s);\n\nimpl Device {\n    fn new(r: pac::i2s::I2s) -> Self {\n        Self(r)\n    }\n\n    #[inline(always)]\n    pub fn enable(&self) {\n        trace!(\"ENABLED\");\n        self.0.enable().write(|w| w.set_enable(true));\n    }\n\n    #[inline(always)]\n    pub fn disable(&self) {\n        trace!(\"DISABLED\");\n        self.0.enable().write(|w| w.set_enable(false));\n    }\n\n    #[inline(always)]\n    fn enable_tx(&self) {\n        trace!(\"TX ENABLED\");\n        self.0.config().txen().write(|w| w.set_txen(true));\n    }\n\n    #[inline(always)]\n    fn disable_tx(&self) {\n        trace!(\"TX DISABLED\");\n        self.0.config().txen().write(|w| w.set_txen(false));\n    }\n\n    #[inline(always)]\n    fn enable_rx(&self) {\n        trace!(\"RX ENABLED\");\n        self.0.config().rxen().write(|w| w.set_rxen(true));\n    }\n\n    #[inline(always)]\n    fn disable_rx(&self) {\n        trace!(\"RX DISABLED\");\n        self.0.config().rxen().write(|w| w.set_rxen(false));\n    }\n\n    #[inline(always)]\n    fn start(&self) {\n        trace!(\"START\");\n        self.0.tasks_start().write_value(1);\n    }\n\n    #[inline(always)]\n    fn stop(&self) {\n        self.0.tasks_stop().write_value(1);\n    }\n\n    #[inline(always)]\n    fn is_stopped(&self) -> bool {\n        self.0.events_stopped().read() != 0\n    }\n\n    #[inline(always)]\n    fn reset_stopped_event(&self) {\n        trace!(\"STOPPED EVENT: Reset\");\n        self.0.events_stopped().write_value(0);\n    }\n\n    #[inline(always)]\n    fn disable_stopped_interrupt(&self) {\n        trace!(\"STOPPED INTERRUPT: Disabled\");\n        self.0.intenclr().write(|w| w.set_stopped(true));\n    }\n\n    #[inline(always)]\n    fn enable_stopped_interrupt(&self) {\n        trace!(\"STOPPED INTERRUPT: Enabled\");\n        self.0.intenset().write(|w| w.set_stopped(true));\n    }\n\n    #[inline(always)]\n    fn reset_tx_ptr_event(&self) {\n        trace!(\"TX PTR EVENT: Reset\");\n        self.0.events_txptrupd().write_value(0);\n    }\n\n    #[inline(always)]\n    fn reset_rx_ptr_event(&self) {\n        trace!(\"RX PTR EVENT: Reset\");\n        self.0.events_rxptrupd().write_value(0);\n    }\n\n    #[inline(always)]\n    fn disable_tx_ptr_interrupt(&self) {\n        trace!(\"TX PTR INTERRUPT: Disabled\");\n        self.0.intenclr().write(|w| w.set_txptrupd(true));\n    }\n\n    #[inline(always)]\n    fn disable_rx_ptr_interrupt(&self) {\n        trace!(\"RX PTR INTERRUPT: Disabled\");\n        self.0.intenclr().write(|w| w.set_rxptrupd(true));\n    }\n\n    #[inline(always)]\n    fn enable_tx_ptr_interrupt(&self) {\n        trace!(\"TX PTR INTERRUPT: Enabled\");\n        self.0.intenset().write(|w| w.set_txptrupd(true));\n    }\n\n    #[inline(always)]\n    fn enable_rx_ptr_interrupt(&self) {\n        trace!(\"RX PTR INTERRUPT: Enabled\");\n        self.0.intenset().write(|w| w.set_rxptrupd(true));\n    }\n\n    #[inline(always)]\n    fn is_tx_ptr_updated(&self) -> bool {\n        self.0.events_txptrupd().read() != 0\n    }\n\n    #[inline(always)]\n    fn is_rx_ptr_updated(&self) -> bool {\n        self.0.events_rxptrupd().read() != 0\n    }\n\n    #[inline]\n    fn update_tx<S>(&self, buffer_ptr: *const [S]) -> Result<(), Error> {\n        let (ptr, maxcnt) = Self::validated_dma_parts(buffer_ptr)?;\n        self.0.rxtxd().maxcnt().write(|w| w.0 = maxcnt);\n        self.0.txd().ptr().write_value(ptr);\n        Ok(())\n    }\n\n    #[inline]\n    fn update_rx<S>(&self, buffer_ptr: *const [S]) -> Result<(), Error> {\n        let (ptr, maxcnt) = Self::validated_dma_parts(buffer_ptr)?;\n        self.0.rxtxd().maxcnt().write(|w| w.0 = maxcnt);\n        self.0.rxd().ptr().write_value(ptr);\n        Ok(())\n    }\n\n    fn validated_dma_parts<S>(buffer_ptr: *const [S]) -> Result<(u32, u32), Error> {\n        let ptr = buffer_ptr as *const S as u32;\n        let bytes_len = buffer_ptr.len() * size_of::<S>();\n        let maxcnt = (bytes_len / size_of::<u32>()) as u32;\n\n        trace!(\"PTR={}, MAXCNT={}\", ptr, maxcnt);\n\n        if ptr % 4 != 0 {\n            Err(Error::BufferMisaligned)\n        } else if bytes_len % 4 != 0 {\n            Err(Error::BufferLengthMisaligned)\n        } else if maxcnt as usize > EASY_DMA_SIZE {\n            Err(Error::BufferTooLong)\n        } else {\n            Ok((ptr, maxcnt))\n        }\n    }\n}\n\n/// Sample details\npub trait Sample: Sized + Copy + Default {\n    /// Width of this sample type.\n    const WIDTH: usize;\n\n    /// Scale of this sample.\n    const SCALE: Self;\n}\n\nimpl Sample for i8 {\n    const WIDTH: usize = 8;\n    const SCALE: Self = 1 << (Self::WIDTH - 1);\n}\n\nimpl Sample for i16 {\n    const WIDTH: usize = 16;\n    const SCALE: Self = 1 << (Self::WIDTH - 1);\n}\n\nimpl Sample for i32 {\n    const WIDTH: usize = 24;\n    const SCALE: Self = 1 << (Self::WIDTH - 1);\n}\n\n/// A 4-bytes aligned buffer. Needed for DMA access.\n#[derive(Clone, Copy)]\n#[repr(align(4))]\npub struct AlignedBuffer<T: Sample, const N: usize>([T; N]);\n\nimpl<T: Sample, const N: usize> AlignedBuffer<T, N> {\n    /// Create a new `AlignedBuffer`.\n    pub fn new(array: [T; N]) -> Self {\n        Self(array)\n    }\n}\n\nimpl<T: Sample, const N: usize> Default for AlignedBuffer<T, N> {\n    fn default() -> Self {\n        Self([T::default(); N])\n    }\n}\n\nimpl<T: Sample, const N: usize> Deref for AlignedBuffer<T, N> {\n    type Target = [T];\n    fn deref(&self) -> &Self::Target {\n        self.0.as_slice()\n    }\n}\n\nimpl<T: Sample, const N: usize> DerefMut for AlignedBuffer<T, N> {\n    fn deref_mut(&mut self) -> &mut Self::Target {\n        self.0.as_mut_slice()\n    }\n}\n\n/// Set of multiple buffers, for multi-buffering transfers.\npub struct MultiBuffering<S: Sample, const NB: usize, const NS: usize> {\n    buffers: [AlignedBuffer<S, NS>; NB],\n    index: usize,\n}\n\nimpl<S: Sample, const NB: usize, const NS: usize> MultiBuffering<S, NB, NS> {\n    /// Create a new `MultiBuffering`.\n    pub fn new() -> Self {\n        assert!(NB > 1);\n        Self {\n            buffers: [AlignedBuffer::<S, NS>::default(); NB],\n            index: 0,\n        }\n    }\n\n    fn get(&self) -> &[S] {\n        &self.buffers[self.index]\n    }\n\n    fn get_mut(&mut self) -> &mut [S] {\n        &mut self.buffers[self.index]\n    }\n\n    /// Advance to use the next buffer and return a non mutable pointer to the previous one.\n    fn switch(&mut self) -> *const [S] {\n        let prev_index = self.index;\n        self.index = (self.index + 1) % NB;\n        self.buffers[prev_index].deref() as *const [S]\n    }\n\n    /// Advance to use the next buffer and return a mutable pointer to the previous one.\n    fn switch_mut(&mut self) -> *mut [S] {\n        let prev_index = self.index;\n        self.index = (self.index + 1) % NB;\n        self.buffers[prev_index].deref_mut() as *mut [S]\n    }\n}\n\n/// Peripheral static state\npub(crate) struct State {\n    started: AtomicBool,\n    rx_waker: AtomicWaker,\n    tx_waker: AtomicWaker,\n    stop_waker: AtomicWaker,\n}\n\nimpl State {\n    pub(crate) const fn new() -> Self {\n        Self {\n            started: AtomicBool::new(false),\n            rx_waker: AtomicWaker::new(),\n            tx_waker: AtomicWaker::new(),\n            stop_waker: AtomicWaker::new(),\n        }\n    }\n}\n\npub(crate) trait SealedInstance {\n    fn regs() -> pac::i2s::I2s;\n    fn state() -> &'static State;\n}\n\n/// I2S peripheral instance.\n#[allow(private_bounds)]\npub trait Instance: SealedInstance + PeripheralType + 'static + Send {\n    /// Interrupt for this peripheral.\n    type Interrupt: interrupt::typelevel::Interrupt;\n}\n\nmacro_rules! impl_i2s {\n    ($type:ident, $pac_type:ident, $irq:ident) => {\n        impl crate::i2s::SealedInstance for peripherals::$type {\n            fn regs() -> pac::i2s::I2s {\n                pac::$pac_type\n            }\n            fn state() -> &'static crate::i2s::State {\n                static STATE: crate::i2s::State = crate::i2s::State::new();\n                &STATE\n            }\n        }\n        impl crate::i2s::Instance for peripherals::$type {\n            type Interrupt = crate::interrupt::typelevel::$irq;\n        }\n    };\n}\n"
  },
  {
    "path": "embassy-nrf/src/ipc.rs",
    "content": "//! InterProcessor Communication (IPC)\n\n#![macro_use]\n\nuse core::future::poll_fn;\nuse core::marker::PhantomData;\nuse core::task::Poll;\n\nuse embassy_hal_internal::{Peri, PeripheralType};\nuse embassy_sync::waitqueue::AtomicWaker;\n\nuse crate::interrupt::typelevel::Interrupt;\nuse crate::{interrupt, pac, ppi};\n\nconst EVENT_COUNT: usize = 16;\n\n/// IPC Event\n#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]\npub enum EventNumber {\n    /// IPC Event 0\n    Event0 = 0,\n    /// IPC Event 1\n    Event1 = 1,\n    /// IPC Event 2\n    Event2 = 2,\n    /// IPC Event 3\n    Event3 = 3,\n    /// IPC Event 4\n    Event4 = 4,\n    /// IPC Event 5\n    Event5 = 5,\n    /// IPC Event 6\n    Event6 = 6,\n    /// IPC Event 7\n    Event7 = 7,\n    /// IPC Event 8\n    Event8 = 8,\n    /// IPC Event 9\n    Event9 = 9,\n    /// IPC Event 10\n    Event10 = 10,\n    /// IPC Event 11\n    Event11 = 11,\n    /// IPC Event 12\n    Event12 = 12,\n    /// IPC Event 13\n    Event13 = 13,\n    /// IPC Event 14\n    Event14 = 14,\n    /// IPC Event 15\n    Event15 = 15,\n}\n\nconst EVENTS: [EventNumber; EVENT_COUNT] = [\n    EventNumber::Event0,\n    EventNumber::Event1,\n    EventNumber::Event2,\n    EventNumber::Event3,\n    EventNumber::Event4,\n    EventNumber::Event5,\n    EventNumber::Event6,\n    EventNumber::Event7,\n    EventNumber::Event8,\n    EventNumber::Event9,\n    EventNumber::Event10,\n    EventNumber::Event11,\n    EventNumber::Event12,\n    EventNumber::Event13,\n    EventNumber::Event14,\n    EventNumber::Event15,\n];\n\n/// IPC Channel\n#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]\npub enum IpcChannel {\n    /// IPC Channel 0\n    Channel0,\n    /// IPC Channel 1\n    Channel1,\n    /// IPC Channel 2\n    Channel2,\n    /// IPC Channel 3\n    Channel3,\n    /// IPC Channel 4\n    Channel4,\n    /// IPC Channel 5\n    Channel5,\n    /// IPC Channel 6\n    Channel6,\n    /// IPC Channel 7\n    Channel7,\n    /// IPC Channel 8\n    Channel8,\n    /// IPC Channel 9\n    Channel9,\n    /// IPC Channel 10\n    Channel10,\n    /// IPC Channel 11\n    Channel11,\n    /// IPC Channel 12\n    Channel12,\n    /// IPC Channel 13\n    Channel13,\n    /// IPC Channel 14\n    Channel14,\n    /// IPC Channel 15\n    Channel15,\n}\n\nimpl IpcChannel {\n    fn mask(self) -> u32 {\n        1 << (self as u32)\n    }\n}\n\n/// Interrupt Handler\npub struct InterruptHandler<T: Instance> {\n    _phantom: PhantomData<T>,\n}\n\nimpl<T: Instance> interrupt::typelevel::Handler<T::Interrupt> for InterruptHandler<T> {\n    unsafe fn on_interrupt() {\n        let regs = T::regs();\n\n        // Check if an event was generated, and if it was, trigger the corresponding waker\n        for event in EVENTS {\n            if regs.events_receive(event as usize).read() & 0x01 == 0x01 {\n                regs.intenclr().write(|w| w.0 = 0x01 << event as u32);\n                T::state().wakers[event as usize].wake();\n            }\n        }\n    }\n}\n\n/// IPC driver\n#[non_exhaustive]\npub struct Ipc<'d> {\n    /// Event 0\n    pub event0: Event<'d>,\n    /// Event 1\n    pub event1: Event<'d>,\n    /// Event 2\n    pub event2: Event<'d>,\n    /// Event 3\n    pub event3: Event<'d>,\n    /// Event 4\n    pub event4: Event<'d>,\n    /// Event 5\n    pub event5: Event<'d>,\n    /// Event 6\n    pub event6: Event<'d>,\n    /// Event 7\n    pub event7: Event<'d>,\n    /// Event 8\n    pub event8: Event<'d>,\n    /// Event 9\n    pub event9: Event<'d>,\n    /// Event 10\n    pub event10: Event<'d>,\n    /// Event 11\n    pub event11: Event<'d>,\n    /// Event 12\n    pub event12: Event<'d>,\n    /// Event 13\n    pub event13: Event<'d>,\n    /// Event 14\n    pub event14: Event<'d>,\n    /// Event 15\n    pub event15: Event<'d>,\n}\n\nimpl<'d> Ipc<'d> {\n    /// Create a new IPC driver.\n    pub fn new<T: Instance>(\n        _p: Peri<'d, T>,\n        _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd,\n    ) -> Self {\n        T::Interrupt::unpend();\n        unsafe { T::Interrupt::enable() };\n\n        let r = T::regs();\n        let state = T::state();\n        #[rustfmt::skip]\n        let result = Self { // attributes on expressions are experimental\n            event0: Event { number: EventNumber::Event0, r, state, _phantom: PhantomData },\n            event1: Event { number: EventNumber::Event1, r, state, _phantom: PhantomData },\n            event2: Event { number: EventNumber::Event2, r, state, _phantom: PhantomData },\n            event3: Event { number: EventNumber::Event3, r, state, _phantom: PhantomData },\n            event4: Event { number: EventNumber::Event4, r, state, _phantom: PhantomData },\n            event5: Event { number: EventNumber::Event5, r, state, _phantom: PhantomData },\n            event6: Event { number: EventNumber::Event6, r, state, _phantom: PhantomData },\n            event7: Event { number: EventNumber::Event7, r, state, _phantom: PhantomData },\n            event8: Event { number: EventNumber::Event8, r, state, _phantom: PhantomData },\n            event9: Event { number: EventNumber::Event9, r, state, _phantom: PhantomData },\n            event10: Event { number: EventNumber::Event10, r, state, _phantom: PhantomData },\n            event11: Event { number: EventNumber::Event11, r, state, _phantom: PhantomData },\n            event12: Event { number: EventNumber::Event12, r, state, _phantom: PhantomData },\n            event13: Event { number: EventNumber::Event13, r, state, _phantom: PhantomData },\n            event14: Event { number: EventNumber::Event14, r, state, _phantom: PhantomData },\n            event15: Event { number: EventNumber::Event15, r, state, _phantom: PhantomData },\n        };\n        result\n    }\n}\n\n/// IPC event\npub struct Event<'d> {\n    number: EventNumber,\n    r: pac::ipc::Ipc,\n    state: &'static State,\n    _phantom: PhantomData<&'d ()>,\n}\n\nimpl<'d> Event<'d> {\n    /// Trigger the event.\n    pub fn trigger(&self) {\n        let nr = self.number;\n        self.r.tasks_send(nr as usize).write_value(1);\n    }\n\n    /// Wait for the event to be triggered.\n    pub async fn wait(&mut self) {\n        let nr = self.number as usize;\n        self.r.intenset().write(|w| w.0 = 1 << nr);\n        poll_fn(|cx| {\n            self.state.wakers[nr].register(cx.waker());\n\n            if self.r.events_receive(nr).read() == 1 {\n                self.r.events_receive(nr).write_value(0x00);\n                Poll::Ready(())\n            } else {\n                Poll::Pending\n            }\n        })\n        .await;\n    }\n\n    /// Returns the [`EventNumber`] of the event.\n    pub fn number(&self) -> EventNumber {\n        self.number\n    }\n\n    /// Create a handle that can trigger the event.\n    pub fn trigger_handle(&self) -> EventTrigger<'d> {\n        EventTrigger {\n            number: self.number,\n            r: self.r,\n            _phantom: PhantomData,\n        }\n    }\n\n    /// Configure the channels the event will broadcast to\n    pub fn configure_trigger<I: IntoIterator<Item = IpcChannel>>(&mut self, channels: I) {\n        self.r.send_cnf(self.number as usize).write(|w| {\n            for channel in channels {\n                w.0 |= channel.mask();\n            }\n        })\n    }\n\n    /// Configure the channels the event will listen on\n    pub fn configure_wait<I: IntoIterator<Item = IpcChannel>>(&mut self, channels: I) {\n        self.r.receive_cnf(self.number as usize).write(|w| {\n            for channel in channels {\n                w.0 |= channel.mask();\n            }\n        });\n    }\n\n    /// Get the task for the IPC event to use with PPI.\n    pub fn task(&self) -> ppi::Task<'d> {\n        let nr = self.number as usize;\n        ppi::Task::from_reg(self.r.tasks_send(nr))\n    }\n\n    /// Get the event for the IPC event to use with PPI.\n    pub fn event(&self) -> ppi::Event<'d> {\n        let nr = self.number as usize;\n        ppi::Event::from_reg(self.r.events_receive(nr))\n    }\n\n    /// Reborrow into a \"child\" Event.\n    ///\n    /// `self` will stay borrowed until the child Event is dropped.\n    pub fn reborrow(&mut self) -> Event<'_> {\n        Event {\n            number: self.number,\n            r: self.r,\n            state: self.state,\n            _phantom: PhantomData,\n        }\n    }\n\n    /// Steal an IPC event by number.\n    ///\n    /// # Safety\n    ///\n    /// The event number must not be in use by another [`Event`].\n    pub unsafe fn steal<T: Instance>(number: EventNumber) -> Self {\n        Self {\n            number,\n            r: T::regs(),\n            state: T::state(),\n            _phantom: PhantomData,\n        }\n    }\n}\n\n/// A handle that can trigger an IPC event.\n///\n/// This `struct` is returned by [`Event::trigger_handle`].\npub struct EventTrigger<'d> {\n    number: EventNumber,\n    r: pac::ipc::Ipc,\n    _phantom: PhantomData<&'d ()>,\n}\n\nimpl EventTrigger<'_> {\n    /// Trigger the event.\n    pub fn trigger(&self) {\n        let nr = self.number;\n        self.r.tasks_send(nr as usize).write_value(1);\n    }\n\n    /// Returns the [`EventNumber`] of the event.\n    pub fn number(&self) -> EventNumber {\n        self.number\n    }\n}\n\npub(crate) struct State {\n    wakers: [AtomicWaker; EVENT_COUNT],\n}\n\nimpl State {\n    pub(crate) const fn new() -> Self {\n        Self {\n            wakers: [const { AtomicWaker::new() }; EVENT_COUNT],\n        }\n    }\n}\n\npub(crate) trait SealedInstance {\n    fn regs() -> pac::ipc::Ipc;\n    fn state() -> &'static State;\n}\n\n/// IPC peripheral instance.\n#[allow(private_bounds)]\npub trait Instance: PeripheralType + SealedInstance + 'static + Send {\n    /// Interrupt for this peripheral.\n    type Interrupt: interrupt::typelevel::Interrupt;\n}\n\nmacro_rules! impl_ipc {\n    ($type:ident, $pac_type:ident, $irq:ident) => {\n        impl crate::ipc::SealedInstance for peripherals::$type {\n            fn regs() -> pac::ipc::Ipc {\n                pac::$pac_type\n            }\n\n            fn state() -> &'static crate::ipc::State {\n                static STATE: crate::ipc::State = crate::ipc::State::new();\n                &STATE\n            }\n        }\n        impl crate::ipc::Instance for peripherals::$type {\n            type Interrupt = crate::interrupt::typelevel::$irq;\n        }\n    };\n}\n"
  },
  {
    "path": "embassy-nrf/src/lib.rs",
    "content": "#![no_std]\n#![allow(async_fn_in_trait)]\n#![allow(unsafe_op_in_unsafe_fn)]\n#![cfg_attr(\n    docsrs,\n    doc = \"<div style='padding:30px;background:#810;color:#fff;text-align:center;'><p>You might want to <a href='https://docs.embassy.dev/embassy-nrf'>browse the `embassy-nrf` documentation on the Embassy website</a> instead.</p><p>The documentation here on `docs.rs` is built for a single chip only (nRF52840 in particular), while on the Embassy website you can pick your exact chip from the top menu. Available peripherals and their APIs change depending on the chip.</p></div>\\n\\n\"\n)]\n#![doc = include_str!(\"../README.md\")]\n#![warn(missing_docs)]\n\n//! ## Feature flags\n#![doc = document_features::document_features!(feature_label = r#\"<span class=\"stab portability\"><code>{feature}</code></span>\"#)]\n\n#[cfg(not(any(\n    feature = \"_nrf51\",\n    feature = \"nrf52805\",\n    feature = \"nrf52810\",\n    feature = \"nrf52811\",\n    feature = \"nrf52820\",\n    feature = \"nrf52832\",\n    feature = \"nrf52833\",\n    feature = \"nrf52840\",\n    feature = \"nrf5340-app-s\",\n    feature = \"nrf5340-app-ns\",\n    feature = \"nrf5340-net\",\n    feature = \"nrf54l15-app-s\",\n    feature = \"nrf54l15-app-ns\",\n    feature = \"nrf54l10-app-s\",\n    feature = \"nrf54l10-app-ns\",\n    feature = \"nrf54l05-app-s\",\n    feature = \"nrf54l05-app-ns\",\n    feature = \"nrf54lm20-app-s\",\n    feature = \"nrf9160-s\",\n    feature = \"nrf9160-ns\",\n    feature = \"nrf9120-s\",\n    feature = \"nrf9120-ns\",\n    feature = \"nrf9151-s\",\n    feature = \"nrf9151-ns\",\n    feature = \"nrf9161-s\",\n    feature = \"nrf9161-ns\",\n)))]\ncompile_error!(\n    \"No chip feature activated. You must activate exactly one of the following features:\n    nrf51,\n    nrf52805,\n    nrf52810,\n    nrf52811,\n    nrf52820,\n    nrf52832,\n    nrf52833,\n    nrf52840,\n    nrf5340-app-s,\n    nrf5340-app-ns,\n    nrf5340-net,\n    nrf54l15-app-s,\n    nrf54l15-app-ns,\n    nrf54l10-app-s,\n    nrf54l10-app-ns,\n    nrf54l05-app-s,\n    nrf54l05-app-ns,\n    nrf54lm20-app-s,\n    nrf9160-s,\n    nrf9160-ns,\n    nrf9120-s,\n    nrf9120-ns,\n    nrf9151-s,\n    nrf9151-ns,\n    nrf9161-s,\n    nrf9161-ns,\n    \"\n);\n\n#[cfg(all(feature = \"reset-pin-as-gpio\", not(feature = \"_nrf52\")))]\ncompile_error!(\"feature `reset-pin-as-gpio` is only valid for nRF52 series chips.\");\n\n#[cfg(all(feature = \"nfc-pins-as-gpio\", not(any(feature = \"_nrf52\", feature = \"_nrf5340-app\"))))]\ncompile_error!(\"feature `nfc-pins-as-gpio` is only valid for nRF52, or nRF53's application core.\");\n\n#[cfg(all(feature = \"lfxo-pins-as-gpio\", not(feature = \"_nrf5340\")))]\ncompile_error!(\"feature `lfxo-pins-as-gpio` is only valid for nRF53 series chips.\");\n\n// This mod MUST go first, so that the others see its macros.\npub(crate) mod fmt;\npub(crate) mod util;\n\n#[cfg(feature = \"_time-driver\")]\nmod time_driver;\n\n#[cfg(not(feature = \"_nrf51\"))]\npub mod buffered_uarte;\n#[cfg(not(feature = \"_nrf54l\"))] // TODO\n#[cfg(not(feature = \"_nrf51\"))]\npub mod egu;\npub mod gpio;\n#[cfg(feature = \"gpiote\")]\npub mod gpiote;\n#[cfg(not(feature = \"_nrf54l\"))] // TODO\n#[cfg(any(feature = \"nrf52832\", feature = \"nrf52833\", feature = \"nrf52840\"))]\npub mod i2s;\n#[cfg(feature = \"_nrf5340\")]\npub mod ipc;\n#[cfg(not(feature = \"_nrf54l\"))] // TODO\n#[cfg(any(\n    feature = \"nrf52832\",\n    feature = \"nrf52833\",\n    feature = \"nrf52840\",\n    feature = \"_nrf5340-app\"\n))]\npub mod nfct;\n#[cfg(not(feature = \"_nrf54l\"))]\npub mod nvmc;\n#[cfg(all(feature = \"_nrf54l\", feature = \"_s\"))]\npub mod rramc;\n#[cfg(all(feature = \"_nrf54l\", feature = \"_s\"))]\npub use rramc as nvmc;\n#[cfg(not(feature = \"_nrf54l\"))] // TODO\n#[cfg(any(\n    feature = \"nrf52810\",\n    feature = \"nrf52811\",\n    feature = \"nrf52832\",\n    feature = \"nrf52833\",\n    feature = \"nrf52840\",\n    feature = \"_nrf5340-app\",\n    feature = \"_nrf91\",\n))]\npub mod pdm;\n#[cfg(not(feature = \"_nrf54l\"))] // TODO\n#[cfg(any(feature = \"nrf52840\", feature = \"nrf9160-s\", feature = \"nrf9160-ns\"))]\npub mod power;\npub mod ppi;\n#[cfg(not(any(\n    feature = \"_nrf51\",\n    feature = \"nrf52805\",\n    feature = \"nrf52820\",\n    feature = \"_nrf5340-net\"\n)))]\npub mod pwm;\n#[cfg(not(feature = \"_nrf54l\"))] // TODO\n#[cfg(not(any(feature = \"_nrf51\", feature = \"_nrf91\", feature = \"_nrf5340-net\")))]\npub mod qdec;\n#[cfg(not(feature = \"_nrf54l\"))] // TODO\n#[cfg(any(feature = \"nrf52840\", feature = \"_nrf5340-app\"))]\npub mod qspi;\n#[cfg(not(feature = \"_nrf54l\"))] // TODO\n#[cfg(not(any(feature = \"_nrf91\", feature = \"_nrf5340-app\")))]\npub mod radio;\n\n#[cfg(any(\n    feature = \"nrf52811\",\n    feature = \"nrf52820\",\n    feature = \"nrf52833\",\n    feature = \"nrf52840\",\n    feature = \"_nrf5340-net\"\n))]\n#[cfg(feature = \"_net-driver\")]\npub mod embassy_net_802154_driver;\n\n#[cfg(all(feature = \"_nrf54l\", feature = \"_s\"))]\npub mod cracen;\n#[cfg(not(feature = \"_nrf54l\"))] // TODO\n#[cfg(feature = \"_nrf5340\")]\npub mod reset;\n#[cfg(not(feature = \"_nrf54l\"))]\n#[cfg(not(any(feature = \"_nrf5340-app\", feature = \"_nrf91\")))]\npub mod rng;\n\n// Currently supported chips\n#[cfg(any(\n    feature = \"nrf52840\",\n    all(any(feature = \"_nrf91\", feature = \"_nrf5340-app\"), feature = \"_s\"),\n))]\npub mod cryptocell_rng;\n\n#[cfg(not(feature = \"_nrf54l\"))]\npub mod rtc;\n#[cfg(not(any(feature = \"_nrf51\", feature = \"nrf52820\", feature = \"_nrf5340-net\")))]\npub mod saadc;\n#[cfg(not(feature = \"_nrf51\"))]\npub mod spim;\n#[cfg(not(feature = \"_nrf51\"))]\npub mod spis;\n#[cfg(not(any(feature = \"_nrf5340-app\", feature = \"_nrf91\")))]\npub mod temp;\npub mod timer;\n#[cfg(not(feature = \"_nrf51\"))]\npub mod twim;\n#[cfg(not(feature = \"_nrf51\"))]\npub mod twis;\n#[cfg(not(feature = \"_nrf51\"))]\npub mod uarte;\n#[cfg(not(feature = \"_nrf54l\"))] // TODO\n#[cfg(any(\n    feature = \"_nrf5340-app\",\n    feature = \"nrf52820\",\n    feature = \"nrf52833\",\n    feature = \"nrf52840\"\n))]\npub mod usb;\npub mod wdt;\n\n// This mod MUST go last, so that it sees all the `impl_foo!` macros\n#[cfg_attr(feature = \"_nrf51\", path = \"chips/nrf51.rs\")]\n#[cfg_attr(feature = \"nrf52805\", path = \"chips/nrf52805.rs\")]\n#[cfg_attr(feature = \"nrf52810\", path = \"chips/nrf52810.rs\")]\n#[cfg_attr(feature = \"nrf52811\", path = \"chips/nrf52811.rs\")]\n#[cfg_attr(feature = \"nrf52820\", path = \"chips/nrf52820.rs\")]\n#[cfg_attr(feature = \"nrf52832\", path = \"chips/nrf52832.rs\")]\n#[cfg_attr(feature = \"nrf52833\", path = \"chips/nrf52833.rs\")]\n#[cfg_attr(feature = \"nrf52840\", path = \"chips/nrf52840.rs\")]\n#[cfg_attr(feature = \"_nrf5340-app\", path = \"chips/nrf5340_app.rs\")]\n#[cfg_attr(feature = \"_nrf5340-net\", path = \"chips/nrf5340_net.rs\")]\n#[cfg_attr(feature = \"_nrf54l15-app\", path = \"chips/nrf54l15_app.rs\")]\n#[cfg_attr(feature = \"_nrf54l10-app\", path = \"chips/nrf54l10_app.rs\")]\n#[cfg_attr(feature = \"_nrf54l05-app\", path = \"chips/nrf54l05_app.rs\")]\n#[cfg_attr(feature = \"_nrf54lm20-app\", path = \"chips/nrf54lm20_app.rs\")]\n#[cfg_attr(feature = \"_nrf9160\", path = \"chips/nrf9160.rs\")]\n#[cfg_attr(feature = \"_nrf9120\", path = \"chips/nrf9120.rs\")]\nmod chip;\n\n/// Macro to bind interrupts to handlers.\n///\n/// This defines the right interrupt handlers, and creates a unit struct (like `struct Irqs;`)\n/// and implements the right [crate::interrupt::typelevel::Binding]s for it. You can pass this struct to drivers to\n/// prove at compile-time that the right interrupts have been bound.\n///\n/// Example of how to bind one interrupt:\n///\n/// ```rust,ignore\n/// use embassy_nrf::{bind_interrupts, spim, peripherals};\n///\n/// bind_interrupts!(\n///     /// Binds the SPIM3 interrupt.\n///     struct Irqs {\n///         SPIM3 => spim::InterruptHandler<peripherals::SPI3>;\n///     }\n/// );\n/// ```\n///\n/// Example of how to bind multiple interrupts in a single macro invocation:\n///\n/// ```rust,ignore\n/// use embassy_nrf::{bind_interrupts, spim, twim, peripherals};\n///\n/// bind_interrupts!(struct Irqs {\n///     SPIM3 => spim::InterruptHandler<peripherals::SPI3>;\n///     TWISPI0 => twim::InterruptHandler<peripherals::TWISPI0>;\n/// });\n/// ```\n\n// developer note: this macro can't be in `embassy-hal-internal` due to the use of `$crate`.\n#[macro_export]\nmacro_rules! bind_interrupts {\n    ($(#[$attr:meta])* $vis:vis struct $name:ident {\n        $(\n            $(#[cfg($cond_irq:meta)])?\n            $irq:ident => $(\n                $(#[cfg($cond_handler:meta)])?\n                $handler:ty\n            ),*;\n        )*\n    }) => {\n        #[derive(Copy, Clone)]\n        $(#[$attr])*\n        $vis struct $name;\n\n        $(\n            #[allow(non_snake_case)]\n            #[unsafe(no_mangle)]\n            $(#[cfg($cond_irq)])?\n            unsafe extern \"C\" fn $irq() {\n                unsafe {\n                    $(\n                        $(#[cfg($cond_handler)])?\n                        <$handler as $crate::interrupt::typelevel::Handler<$crate::interrupt::typelevel::$irq>>::on_interrupt();\n\n                    )*\n                }\n            }\n\n            $(#[cfg($cond_irq)])?\n            $crate::bind_interrupts!(@inner\n                $(\n                    $(#[cfg($cond_handler)])?\n                    unsafe impl $crate::interrupt::typelevel::Binding<$crate::interrupt::typelevel::$irq, $handler> for $name {}\n                )*\n            );\n        )*\n    };\n    (@inner $($t:tt)*) => {\n        $($t)*\n    }\n}\n\n// Reexports\n\n#[cfg(feature = \"unstable-pac\")]\npub use chip::pac;\n#[cfg(not(feature = \"unstable-pac\"))]\npub(crate) use chip::pac;\npub use chip::{EASY_DMA_SIZE, Peripherals, peripherals};\npub use embassy_hal_internal::{Peri, PeripheralType};\n\npub use crate::chip::interrupt;\n#[cfg(feature = \"rt\")]\npub use crate::pac::NVIC_PRIO_BITS;\n\npub mod config {\n    //! Configuration options used when initializing the HAL.\n\n    /// Clock speed\n    #[cfg(feature = \"_nrf54l\")]\n    pub enum ClockSpeed {\n        /// Run at 128 MHz.\n        CK128,\n        /// Run at 64 MHz.\n        CK64,\n    }\n\n    /// High frequency clock source.\n    pub enum HfclkSource {\n        /// Internal source\n        Internal,\n        /// External source from xtal.\n        ExternalXtal,\n    }\n\n    /// Low frequency clock source\n    pub enum LfclkSource {\n        /// Internal RC oscillator\n        InternalRC,\n        /// Synthesized from the high frequency clock source.\n        #[cfg(not(feature = \"_nrf91\"))]\n        Synthesized,\n        /// External source from xtal.\n        #[cfg(not(feature = \"lfxo-pins-as-gpio\"))]\n        ExternalXtal,\n        /// External source from xtal with low swing applied.\n        #[cfg(not(any(feature = \"lfxo-pins-as-gpio\", feature = \"_nrf91\", feature = \"_nrf54l\")))]\n        ExternalLowSwing,\n        /// External source from xtal with full swing applied.\n        #[cfg(not(any(feature = \"lfxo-pins-as-gpio\", feature = \"_nrf91\", feature = \"_nrf54l\")))]\n        ExternalFullSwing,\n    }\n\n    /// SWD access port protection setting.\n    #[non_exhaustive]\n    pub enum Debug {\n        /// Debugging is allowed (APPROTECT is disabled). Default.\n        Allowed,\n        /// Debugging is not allowed (APPROTECT is enabled).\n        Disallowed,\n        /// APPROTECT is not configured (neither to enable it or disable it).\n        /// This can be useful if you're already doing it by other means and\n        /// you don't want embassy-nrf to touch UICR.\n        NotConfigured,\n    }\n\n    /// Settings for enabling the built in DCDC converters.\n    #[cfg(not(any(feature = \"_nrf5340\", feature = \"_nrf91\")))]\n    pub struct DcdcConfig {\n        /// Config for the first stage DCDC (VDDH -> VDD), if disabled LDO will be used.\n        #[cfg(feature = \"nrf52840\")]\n        pub reg0: bool,\n        /// Configure the voltage of the first stage DCDC. It is stored in non-volatile memory (UICR.REGOUT0 register); pass None to not touch it.\n        #[cfg(any(feature = \"nrf52840\", feature = \"nrf52833\"))]\n        pub reg0_voltage: Option<Reg0Voltage>,\n        /// Config for the second stage DCDC (VDD -> DEC4), if disabled LDO will be used.\n        pub reg1: bool,\n    }\n\n    ///  Output voltage setting for REG0 regulator stage.\n    #[cfg(any(feature = \"nrf52840\", feature = \"nrf52833\"))]\n    pub enum Reg0Voltage {\n        /// 1.8 V\n        _1V8 = 0,\n        /// 2.1 V\n        _2V1 = 1,\n        /// 2.4 V\n        _2V4 = 2,\n        /// 2.7 V\n        _2V7 = 3,\n        /// 3.0 V\n        _3V0 = 4,\n        /// 3.3 V\n        _3V3 = 5,\n        //ERASED = 7, means 1.8V\n    }\n\n    /// Settings for enabling the built in DCDC converters.\n    #[cfg(feature = \"_nrf5340-app\")]\n    pub struct DcdcConfig {\n        /// Config for the high voltage stage, if disabled LDO will be used.\n        pub regh: bool,\n        /// Configure the voltage of the high voltage stage. It is stored in non-volatile memory (UICR.VREGHVOUT register); pass None to not touch it.\n        #[cfg(feature = \"nrf5340-app-s\")]\n        pub regh_voltage: Option<ReghVoltage>,\n        /// Config for the main rail, if disabled LDO will be used.\n        pub regmain: bool,\n        /// Config for the radio rail, if disabled LDO will be used.\n        pub regradio: bool,\n    }\n\n    ///  Output voltage setting for VREGH regulator stage.\n    #[cfg(feature = \"nrf5340-app-s\")]\n    pub enum ReghVoltage {\n        /// 1.8 V\n        _1V8 = 0,\n        /// 2.1 V\n        _2V1 = 1,\n        /// 2.4 V\n        _2V4 = 2,\n        /// 2.7 V\n        _2V7 = 3,\n        /// 3.0 V\n        _3V0 = 4,\n        /// 3.3 V\n        _3V3 = 5,\n        //ERASED = 7, means 1.8V\n    }\n\n    /// Settings for enabling the built in DCDC converter.\n    #[cfg(feature = \"_nrf91\")]\n    pub struct DcdcConfig {\n        /// Config for the main rail, if disabled LDO will be used.\n        pub regmain: bool,\n    }\n\n    /// Settings for the internal capacitors.\n    #[cfg(feature = \"nrf5340-app-s\")]\n    pub struct InternalCapacitors {\n        /// Config for the internal capacitors on pins XC1 and XC2. Pass `None` to not touch it.\n        pub hfxo: Option<HfxoCapacitance>,\n        /// Config for the internal capacitors between pins XL1 and XL2. Pass `None` to not touch\n        /// it.\n        pub lfxo: Option<LfxoCapacitance>,\n    }\n\n    /// Internal capacitance value for the HFXO.\n    #[cfg(feature = \"nrf5340-app-s\")]\n    #[derive(Copy, Clone)]\n    pub enum HfxoCapacitance {\n        /// Use external capacitors\n        External,\n        /// 7.0 pF\n        _7_0pF,\n        /// 7.5 pF\n        _7_5pF,\n        /// 8.0 pF\n        _8_0pF,\n        /// 8.5 pF\n        _8_5pF,\n        /// 9.0 pF\n        _9_0pF,\n        /// 9.5 pF\n        _9_5pF,\n        /// 10.0 pF\n        _10_0pF,\n        /// 10.5 pF\n        _10_5pF,\n        /// 11.0 pF\n        _11_0pF,\n        /// 11.5 pF\n        _11_5pF,\n        /// 12.0 pF\n        _12_0pF,\n        /// 12.5 pF\n        _12_5pF,\n        /// 13.0 pF\n        _13_0pF,\n        /// 13.5 pF\n        _13_5pF,\n        /// 14.0 pF\n        _14_0pF,\n        /// 14.5 pF\n        _14_5pF,\n        /// 15.0 pF\n        _15_0pF,\n        /// 15.5 pF\n        _15_5pF,\n        /// 16.0 pF\n        _16_0pF,\n        /// 16.5 pF\n        _16_5pF,\n        /// 17.0 pF\n        _17_0pF,\n        /// 17.5 pF\n        _17_5pF,\n        /// 18.0 pF\n        _18_0pF,\n        /// 18.5 pF\n        _18_5pF,\n        /// 19.0 pF\n        _19_0pF,\n        /// 19.5 pF\n        _19_5pF,\n        /// 20.0 pF\n        _20_0pF,\n    }\n\n    #[cfg(feature = \"nrf5340-app-s\")]\n    impl HfxoCapacitance {\n        /// The capacitance value times two.\n        pub(crate) fn value2(self) -> i32 {\n            match self {\n                HfxoCapacitance::External => unreachable!(),\n                HfxoCapacitance::_7_0pF => 14,\n                HfxoCapacitance::_7_5pF => 15,\n                HfxoCapacitance::_8_0pF => 16,\n                HfxoCapacitance::_8_5pF => 17,\n                HfxoCapacitance::_9_0pF => 18,\n                HfxoCapacitance::_9_5pF => 19,\n                HfxoCapacitance::_10_0pF => 20,\n                HfxoCapacitance::_10_5pF => 21,\n                HfxoCapacitance::_11_0pF => 22,\n                HfxoCapacitance::_11_5pF => 23,\n                HfxoCapacitance::_12_0pF => 24,\n                HfxoCapacitance::_12_5pF => 25,\n                HfxoCapacitance::_13_0pF => 26,\n                HfxoCapacitance::_13_5pF => 27,\n                HfxoCapacitance::_14_0pF => 28,\n                HfxoCapacitance::_14_5pF => 29,\n                HfxoCapacitance::_15_0pF => 30,\n                HfxoCapacitance::_15_5pF => 31,\n                HfxoCapacitance::_16_0pF => 32,\n                HfxoCapacitance::_16_5pF => 33,\n                HfxoCapacitance::_17_0pF => 34,\n                HfxoCapacitance::_17_5pF => 35,\n                HfxoCapacitance::_18_0pF => 36,\n                HfxoCapacitance::_18_5pF => 37,\n                HfxoCapacitance::_19_0pF => 38,\n                HfxoCapacitance::_19_5pF => 39,\n                HfxoCapacitance::_20_0pF => 40,\n            }\n        }\n\n        pub(crate) fn external(self) -> bool {\n            matches!(self, Self::External)\n        }\n    }\n\n    /// Internal capacitance value for the LFXO.\n    #[cfg(feature = \"nrf5340-app-s\")]\n    pub enum LfxoCapacitance {\n        /// Use external capacitors\n        External = 0,\n        /// 6 pF\n        _6pF = 1,\n        /// 7 pF\n        _7pF = 2,\n        /// 9 pF\n        _9pF = 3,\n    }\n\n    #[cfg(feature = \"nrf5340-app-s\")]\n    impl From<LfxoCapacitance> for super::pac::oscillators::vals::Intcap {\n        fn from(t: LfxoCapacitance) -> Self {\n            match t {\n                LfxoCapacitance::External => Self::EXTERNAL,\n                LfxoCapacitance::_6pF => Self::C6PF,\n                LfxoCapacitance::_7pF => Self::C7PF,\n                LfxoCapacitance::_9pF => Self::C9PF,\n            }\n        }\n    }\n\n    /// Configuration for peripherals. Default configuration should work on any nRF chip.\n    #[non_exhaustive]\n    pub struct Config {\n        /// High frequency clock source.\n        pub hfclk_source: HfclkSource,\n        /// Low frequency clock source.\n        pub lfclk_source: LfclkSource,\n        #[cfg(feature = \"nrf5340-app-s\")]\n        /// Internal capacitor configuration, for use with the `ExternalXtal` clock source. See\n        /// nrf5340-PS §4.12.\n        pub internal_capacitors: InternalCapacitors,\n        #[cfg(not(any(feature = \"_nrf5340-net\", feature = \"_nrf54l\")))]\n        /// DCDC configuration.\n        pub dcdc: DcdcConfig,\n        /// GPIOTE interrupt priority. Should be lower priority than softdevice if used.\n        #[cfg(feature = \"gpiote\")]\n        pub gpiote_interrupt_priority: crate::interrupt::Priority,\n        /// Time driver interrupt priority. Should be lower priority than softdevice if used.\n        #[cfg(feature = \"_time-driver\")]\n        pub time_interrupt_priority: crate::interrupt::Priority,\n        /// Enable or disable the debug port.\n        pub debug: Debug,\n        /// Clock speed configuration.\n        #[cfg(feature = \"_nrf54l\")]\n        pub clock_speed: ClockSpeed,\n    }\n\n    impl Default for Config {\n        fn default() -> Self {\n            Self {\n                // There are hobby nrf52 boards out there without external XTALs...\n                // Default everything to internal so it Just Works. User can enable external\n                // xtals if they know they have them.\n                hfclk_source: HfclkSource::Internal,\n                lfclk_source: LfclkSource::InternalRC,\n                #[cfg(feature = \"nrf5340-app-s\")]\n                internal_capacitors: InternalCapacitors { hfxo: None, lfxo: None },\n                #[cfg(not(any(feature = \"_nrf5340\", feature = \"_nrf91\", feature = \"_nrf54l\")))]\n                dcdc: DcdcConfig {\n                    #[cfg(feature = \"nrf52840\")]\n                    reg0: false,\n                    #[cfg(any(feature = \"nrf52840\", feature = \"nrf52833\"))]\n                    reg0_voltage: None,\n                    reg1: false,\n                },\n                #[cfg(feature = \"_nrf5340-app\")]\n                dcdc: DcdcConfig {\n                    regh: false,\n                    #[cfg(feature = \"nrf5340-app-s\")]\n                    regh_voltage: None,\n                    regmain: false,\n                    regradio: false,\n                },\n                #[cfg(feature = \"_nrf91\")]\n                dcdc: DcdcConfig { regmain: false },\n                #[cfg(feature = \"gpiote\")]\n                gpiote_interrupt_priority: crate::interrupt::Priority::P0,\n                #[cfg(feature = \"_time-driver\")]\n                time_interrupt_priority: crate::interrupt::Priority::P0,\n\n                // In NS mode, default to NotConfigured, assuming the S firmware will do it.\n                #[cfg(feature = \"_ns\")]\n                debug: Debug::NotConfigured,\n                #[cfg(not(feature = \"_ns\"))]\n                debug: Debug::Allowed,\n                #[cfg(feature = \"_nrf54l\")]\n                clock_speed: ClockSpeed::CK64,\n            }\n        }\n    }\n}\n\n#[cfg(feature = \"_nrf91\")]\n#[allow(unused)]\nmod consts {\n    pub const UICR_APPROTECT: *mut u32 = 0x00FF8000 as *mut u32;\n    pub const UICR_HFXOSRC: *mut u32 = 0x00FF801C as *mut u32;\n    pub const UICR_HFXOCNT: *mut u32 = 0x00FF8020 as *mut u32;\n    pub const UICR_SECUREAPPROTECT: *mut u32 = 0x00FF802C as *mut u32;\n    pub const APPROTECT_ENABLED: u32 = 0x0000_0000;\n    #[cfg(feature = \"_nrf9120\")]\n    pub const APPROTECT_DISABLED: u32 = 0x50FA50FA;\n}\n\n#[cfg(feature = \"_nrf5340-app\")]\n#[allow(unused)]\nmod consts {\n    pub const UICR_APPROTECT: *mut u32 = 0x00FF8000 as *mut u32;\n    pub const UICR_VREGHVOUT: *mut u32 = 0x00FF8010 as *mut u32;\n    pub const UICR_SECUREAPPROTECT: *mut u32 = 0x00FF801C as *mut u32;\n    pub const UICR_NFCPINS: *mut u32 = 0x00FF8028 as *mut u32;\n    pub const APPROTECT_ENABLED: u32 = 0x0000_0000;\n    pub const APPROTECT_DISABLED: u32 = 0x50FA50FA;\n}\n\n#[cfg(feature = \"_nrf5340-net\")]\n#[allow(unused)]\nmod consts {\n    pub const UICR_APPROTECT: *mut u32 = 0x01FF8000 as *mut u32;\n    pub const APPROTECT_ENABLED: u32 = 0x0000_0000;\n    pub const APPROTECT_DISABLED: u32 = 0x50FA50FA;\n}\n\n#[cfg(feature = \"_nrf52\")]\n#[allow(unused)]\nmod consts {\n    pub const UICR_PSELRESET1: *mut u32 = 0x10001200 as *mut u32;\n    pub const UICR_PSELRESET2: *mut u32 = 0x10001204 as *mut u32;\n    pub const UICR_NFCPINS: *mut u32 = 0x1000120C as *mut u32;\n    pub const UICR_APPROTECT: *mut u32 = 0x10001208 as *mut u32;\n    pub const UICR_REGOUT0: *mut u32 = 0x10001304 as *mut u32;\n    pub const APPROTECT_ENABLED: u32 = 0x0000_0000;\n    pub const APPROTECT_DISABLED: u32 = 0x0000_005a;\n}\n\n/// Result from writing UICR.\n#[cfg(not(any(feature = \"_nrf51\", feature = \"_nrf54l\")))]\n#[derive(Debug, Copy, Clone, Eq, PartialEq)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub enum WriteResult {\n    /// Word was written successfully, needs reset.\n    Written,\n    /// Word was already set to the value we wanted to write, nothing was done.\n    Noop,\n    /// Word is already set to something else, we couldn't write the desired value.\n    Failed,\n}\n\n/// Write the UICR value at the provided address, ensuring that flash\n/// settings are correctly apply to persist the value.\n///\n/// Safety: the address must be a valid UICR register.\n#[cfg(not(any(feature = \"_nrf51\", feature = \"_nrf54l\")))]\npub unsafe fn uicr_write(address: *mut u32, value: u32) -> WriteResult {\n    uicr_write_masked(address, value, 0xFFFF_FFFF)\n}\n\n#[cfg(not(any(feature = \"_nrf51\", feature = \"_nrf54l\")))]\n/// Write the UICR value at the provided address, ensuring that flash\n/// settings are correctly apply to persist the value.\n///\n/// Safety: the address must be a valid UICR register.\npub unsafe fn uicr_write_masked(address: *mut u32, value: u32, mask: u32) -> WriteResult {\n    let curr_val = address.read_volatile();\n    if curr_val & mask == value & mask {\n        return WriteResult::Noop;\n    }\n\n    // We can only change `1` bits to `0` bits.\n    if curr_val & value & mask != value & mask {\n        return WriteResult::Failed;\n    }\n\n    // Nrf9151 errata 7, need to disable interrups + use DSB https://docs.nordicsemi.com/bundle/errata_nRF9151_Rev2/page/ERR/nRF9151/Rev2/latest/anomaly_151_7.html\n    cortex_m::interrupt::free(|_cs| {\n        let nvmc = pac::NVMC;\n\n        nvmc.config().write(|w| w.set_wen(pac::nvmc::vals::Wen::WEN));\n        while !nvmc.ready().read().ready() {}\n        address.write_volatile(value | !mask);\n        cortex_m::asm::dsb();\n        while !nvmc.ready().read().ready() {}\n        nvmc.config().write(|_| {});\n        while !nvmc.ready().read().ready() {}\n    });\n\n    WriteResult::Written\n}\n\n/// Initialize the `embassy-nrf` HAL with the provided configuration.\n///\n/// This returns the peripheral singletons that can be used for creating drivers.\n///\n/// This should only be called once at startup, otherwise it panics.\npub fn init(config: config::Config) -> Peripherals {\n    // Do this first, so that it panics if user is calling `init` a second time\n    // before doing anything important.\n    let peripherals = Peripherals::take();\n\n    #[allow(unused_mut)]\n    let mut needs_reset = false;\n\n    // set clock speed\n    #[cfg(feature = \"_nrf54l\")]\n    {\n        #[cfg(feature = \"_s\")]\n        let regs = pac::OSCILLATORS_S;\n        #[cfg(feature = \"_ns\")]\n        let regs = pac::OSCILLATORS_NS;\n\n        use pac::oscillators::vals::Freq;\n        regs.pll().freq().write(|w| {\n            w.set_freq(match config.clock_speed {\n                config::ClockSpeed::CK64 => Freq::CK64M,\n                config::ClockSpeed::CK128 => Freq::CK128M,\n            });\n        });\n    }\n\n    // Workaround used in the nrf mdk: file system_nrf91.c , function SystemInit(), after `#if !defined(NRF_SKIP_UICR_HFXO_WORKAROUND)`\n    #[cfg(all(feature = \"_nrf91\", feature = \"_s\"))]\n    {\n        let uicr = pac::UICR_S;\n        let hfxocnt = uicr.hfxocnt().read().hfxocnt().to_bits();\n        let hfxosrc = uicr.hfxosrc().read().hfxosrc().to_bits();\n\n        if hfxosrc == 1 {\n            unsafe {\n                let _ = uicr_write(consts::UICR_HFXOSRC, 0);\n            }\n            needs_reset = true;\n        }\n\n        if hfxocnt == 255 {\n            unsafe {\n                let _ = uicr_write(consts::UICR_HFXOCNT, 32);\n            }\n            needs_reset = true;\n        }\n    }\n\n    // Apply trimming values from the FICR.\n    #[cfg(any(\n        all(feature = \"_nrf5340-app\", feature = \"_s\"),\n        all(feature = \"_nrf54l\", feature = \"_s\"),\n        feature = \"_nrf5340-net\",\n    ))]\n    {\n        #[cfg(feature = \"_nrf5340\")]\n        let n = 32;\n        #[cfg(feature = \"_nrf54l\")]\n        let n = 64;\n        for i in 0..n {\n            let info = pac::FICR.trimcnf(i);\n            let addr = info.addr().read();\n            if addr == 0 || addr == 0xFFFF_FFFF {\n                break;\n            }\n            unsafe {\n                (addr as *mut u32).write_volatile(info.data().read());\n            }\n        }\n    }\n\n    // Workaround for anomaly 66\n    #[cfg(feature = \"_nrf52\")]\n    {\n        let ficr = pac::FICR;\n        let temp = pac::TEMP;\n        temp.a(0).write_value(ficr.temp().a0().read().0);\n        temp.a(1).write_value(ficr.temp().a1().read().0);\n        temp.a(2).write_value(ficr.temp().a2().read().0);\n        temp.a(3).write_value(ficr.temp().a3().read().0);\n        temp.a(4).write_value(ficr.temp().a4().read().0);\n        temp.a(5).write_value(ficr.temp().a5().read().0);\n        temp.b(0).write_value(ficr.temp().b0().read().0);\n        temp.b(1).write_value(ficr.temp().b1().read().0);\n        temp.b(2).write_value(ficr.temp().b2().read().0);\n        temp.b(3).write_value(ficr.temp().b3().read().0);\n        temp.b(4).write_value(ficr.temp().b4().read().0);\n        temp.b(5).write_value(ficr.temp().b5().read().0);\n        temp.t(0).write_value(ficr.temp().t0().read().0);\n        temp.t(1).write_value(ficr.temp().t1().read().0);\n        temp.t(2).write_value(ficr.temp().t2().read().0);\n        temp.t(3).write_value(ficr.temp().t3().read().0);\n        temp.t(4).write_value(ficr.temp().t4().read().0);\n    }\n\n    // GLITCHDET is only accessible for secure code\n    #[cfg(all(feature = \"_nrf54l\", feature = \"_s\"))]\n    {\n        // The voltage glitch detectors are automatically enabled after reset.\n        // To save power, the glitch detectors must be disabled when not in use.\n        pac::GLITCHDET.config().write(|w| w.set_enable(false));\n    }\n\n    // Setup debug protection.\n    #[cfg(not(feature = \"_nrf51\"))]\n    match config.debug {\n        config::Debug::Allowed => {\n            #[cfg(feature = \"_nrf52\")]\n            unsafe {\n                let variant = (0x1000_0104 as *mut u32).read_volatile();\n                // Get the letter for the build code (b'A' .. b'F')\n                let build_code = (variant >> 8) as u8;\n\n                if build_code >= chip::APPROTECT_MIN_BUILD_CODE {\n                    // Chips with a certain chip type-specific build code or higher have an\n                    // improved APPROTECT (\"hardware and software controlled access port protection\")\n                    // which needs explicit action by the firmware to keep it unlocked\n                    // See https://docs.nordicsemi.com/bundle/ps_nrf52840/page/dif.html#d402e184\n\n                    // UICR.APPROTECT = HwDisabled\n                    let res = uicr_write(consts::UICR_APPROTECT, consts::APPROTECT_DISABLED);\n                    needs_reset |= res == WriteResult::Written;\n                    // APPROTECT.DISABLE = SwDisabled\n                    (0x4000_0558 as *mut u32).write_volatile(consts::APPROTECT_DISABLED);\n                } else {\n                    // nothing to do on older chips, debug is allowed by default.\n                }\n            }\n\n            #[cfg(feature = \"_nrf5340\")]\n            unsafe {\n                let p = pac::CTRLAP;\n\n                let res = uicr_write(consts::UICR_APPROTECT, consts::APPROTECT_DISABLED);\n                needs_reset |= res == WriteResult::Written;\n                p.approtect().disable().write_value(consts::APPROTECT_DISABLED);\n\n                #[cfg(feature = \"_nrf5340-app\")]\n                {\n                    let res = uicr_write(consts::UICR_SECUREAPPROTECT, consts::APPROTECT_DISABLED);\n                    needs_reset |= res == WriteResult::Written;\n                    p.secureapprotect().disable().write_value(consts::APPROTECT_DISABLED);\n                }\n            }\n\n            // TAMPC is only accessible for secure code\n            #[cfg(all(feature = \"_nrf54l\", feature = \"_s\"))]\n            {\n                use crate::pac::tampc::vals;\n\n                // UICR cannot be written here, because it can only be written once after an erase all.\n                // The erase all value means that debug access is allowed if permitted by the firmware.\n\n                // Write to TAMPC to permit debug access\n                //\n                // See https://docs.nordicsemi.com/bundle/ps_nrf54L15/page/debug.html#ariaid-title6\n\n                let p = pac::TAMPC;\n\n                // Unlock dbgen\n                p.protect().domain(0).dbgen().ctrl().write(|w| {\n                    w.set_key(vals::DomainDbgenCtrlKey::KEY);\n                    w.set_writeprotection(vals::DomainDbgenCtrlWriteprotection::CLEAR);\n                });\n                p.protect().domain(0).dbgen().ctrl().write(|w| {\n                    w.set_key(vals::DomainDbgenCtrlKey::KEY);\n                    w.set_value(vals::DomainDbgenCtrlValue::HIGH);\n                });\n\n                // Unlock niden\n                p.protect().domain(0).niden().ctrl().write(|w| {\n                    w.set_key(vals::NidenCtrlKey::KEY);\n                    w.set_writeprotection(vals::NidenCtrlWriteprotection::CLEAR);\n                });\n                p.protect().domain(0).niden().ctrl().write(|w| {\n                    w.set_key(vals::NidenCtrlKey::KEY);\n                    w.set_value(vals::NidenCtrlValue::HIGH);\n                });\n\n                p.protect().domain(0).spiden().ctrl().write(|w| {\n                    w.set_key(vals::SpidenCtrlKey::KEY);\n                    w.set_writeprotection(vals::SpidenCtrlWriteprotection::CLEAR);\n                });\n                p.protect().domain(0).spiden().ctrl().write(|w| {\n                    w.set_key(vals::SpidenCtrlKey::KEY);\n                    w.set_value(vals::SpidenCtrlValue::HIGH);\n                });\n\n                p.protect().domain(0).spniden().ctrl().write(|w| {\n                    w.set_key(vals::SpnidenCtrlKey::KEY);\n                    w.set_writeprotection(vals::SpnidenCtrlWriteprotection::CLEAR);\n                });\n                p.protect().domain(0).spniden().ctrl().write(|w| {\n                    w.set_key(vals::SpnidenCtrlKey::KEY);\n                    w.set_value(vals::SpnidenCtrlValue::HIGH);\n                });\n            }\n\n            // nothing to do on the nrf9160, debug is allowed by default.\n\n            // nrf9151, nrf9161 use the new-style approtect that requires writing a register.\n            #[cfg(feature = \"nrf9120-s\")]\n            unsafe {\n                let p = pac::APPROTECT_S;\n\n                let res = uicr_write(consts::UICR_APPROTECT, consts::APPROTECT_DISABLED);\n                needs_reset |= res == WriteResult::Written;\n                p.approtect()\n                    .disable()\n                    .write(|w| w.set_disable(pac::approtect::vals::ApprotectDisableDisable::SW_UNPROTECTED));\n\n                let res = uicr_write(consts::UICR_SECUREAPPROTECT, consts::APPROTECT_DISABLED);\n                needs_reset |= res == WriteResult::Written;\n                p.secureapprotect()\n                    .disable()\n                    .write(|w| w.set_disable(pac::approtect::vals::SecureapprotectDisableDisable::SW_UNPROTECTED));\n\n                // TODO: maybe add workaround for this errata\n                // It uses extra power, not sure how to let the user choose.\n                // https://docs.nordicsemi.com/bundle/errata_nRF9151_Rev1/page/ERR/nRF9151/Rev1/latest/anomaly_151_36.html#anomaly_151_36\n            }\n        }\n        config::Debug::Disallowed => {\n            // TODO: Handle nRF54L\n            //       By default, debug access is not allowed if the firmware doesn't allow it.\n            //       Code could be added here to disable debug access in UICR as well.\n            #[cfg(not(feature = \"_nrf54l\"))]\n            unsafe {\n                // UICR.APPROTECT = Enabled\n                let res = uicr_write(consts::UICR_APPROTECT, consts::APPROTECT_ENABLED);\n                needs_reset |= res == WriteResult::Written;\n                #[cfg(any(feature = \"_nrf5340-app\", feature = \"_nrf91\"))]\n                {\n                    let res = uicr_write(consts::UICR_SECUREAPPROTECT, consts::APPROTECT_ENABLED);\n                    needs_reset |= res == WriteResult::Written;\n                }\n\n                #[cfg(feature = \"nrf9120-s\")]\n                {\n                    let p = pac::APPROTECT_S;\n                    p.approtect().forceprotect().write(|w| w.set_forceprotect(true));\n                    p.secureapprotect().forceprotect().write(|w| w.set_forceprotect(true));\n                }\n            }\n        }\n        config::Debug::NotConfigured => {}\n    }\n\n    #[cfg(feature = \"_nrf52\")]\n    unsafe {\n        let value = if cfg!(feature = \"reset-pin-as-gpio\") {\n            !0\n        } else {\n            chip::RESET_PIN\n        };\n        let res1 = uicr_write(consts::UICR_PSELRESET1, value);\n        let res2 = uicr_write(consts::UICR_PSELRESET2, value);\n        needs_reset |= res1 == WriteResult::Written || res2 == WriteResult::Written;\n        if res1 == WriteResult::Failed || res2 == WriteResult::Failed {\n            #[cfg(not(feature = \"reset-pin-as-gpio\"))]\n            warn!(\n                \"You have requested enabling chip reset functionality on the reset pin, by not enabling the Cargo feature `reset-pin-as-gpio`.\\n\\\n                However, UICR is already programmed to some other setting, and can't be changed without erasing it.\\n\\\n                To fix this, erase UICR manually, for example using `probe-rs erase` or `nrfjprog --eraseuicr`.\"\n            );\n            #[cfg(feature = \"reset-pin-as-gpio\")]\n            warn!(\n                \"You have requested using the reset pin as GPIO, by enabling the Cargo feature `reset-pin-as-gpio`.\\n\\\n                However, UICR is already programmed to some other setting, and can't be changed without erasing it.\\n\\\n                To fix this, erase UICR manually, for example using `probe-rs erase` or `nrfjprog --eraseuicr`.\"\n            );\n        }\n    }\n\n    #[cfg(any(feature = \"_nrf52\", all(feature = \"_nrf5340-app\", feature = \"_s\")))]\n    unsafe {\n        let value = if cfg!(feature = \"nfc-pins-as-gpio\") { 0 } else { 1 };\n        let res = uicr_write_masked(consts::UICR_NFCPINS, value, 1);\n        needs_reset |= res == WriteResult::Written;\n        if res == WriteResult::Failed {\n            // with nfc-pins-as-gpio, this can never fail because we're writing all zero bits.\n            #[cfg(not(feature = \"nfc-pins-as-gpio\"))]\n            warn!(\n                \"You have requested to use P0.09 and P0.10 pins for NFC, by not enabling the Cargo feature `nfc-pins-as-gpio`.\\n\\\n                However, UICR is already programmed to some other setting, and can't be changed without erasing it.\\n\\\n                To fix this, erase UICR manually, for example using `probe-rs erase` or `nrfjprog --eraseuicr`.\"\n            );\n        }\n    }\n\n    #[cfg(any(feature = \"nrf52840\", feature = \"nrf52833\"))]\n    unsafe {\n        if let Some(value) = config.dcdc.reg0_voltage {\n            let value = value as u32;\n            let res = uicr_write_masked(consts::UICR_REGOUT0, value, 0b00000000_00000000_00000000_00000111);\n            needs_reset |= res == WriteResult::Written;\n            if res == WriteResult::Failed {\n                warn!(\n                    \"Failed to set regulator voltage, as UICR is already programmed to some other setting, and can't be changed without erasing it.\\n\\\n                    To fix this, erase UICR manually, for example using `probe-rs erase` or `nrfjprog --eraseuicr`.\"\n                );\n            }\n        }\n    }\n\n    #[cfg(feature = \"nrf5340-app-s\")]\n    unsafe {\n        if let Some(value) = config.dcdc.regh_voltage {\n            let value = value as u32;\n            let res = uicr_write_masked(consts::UICR_VREGHVOUT, value, 0b00000000_00000000_00000000_00000111);\n            needs_reset |= res == WriteResult::Written;\n            if res == WriteResult::Failed {\n                warn!(\n                    \"Failed to set regulator voltage, as UICR is already programmed to some other setting, and can't be changed without erasing it.\\n\\\n                    To fix this, erase UICR manually, for example using `probe-rs erase` or `nrfjprog --eraseuicr`.\"\n                );\n            }\n        }\n    }\n\n    if needs_reset {\n        cortex_m::peripheral::SCB::sys_reset();\n    }\n\n    // Configure internal capacitors\n    #[cfg(feature = \"nrf5340-app-s\")]\n    {\n        if let Some(cap) = config.internal_capacitors.hfxo {\n            if cap.external() {\n                pac::OSCILLATORS.xosc32mcaps().write(|w| w.set_enable(false));\n            } else {\n                let mut slope = pac::FICR.xosc32mtrim().read().slope() as i32;\n                let offset = pac::FICR.xosc32mtrim().read().offset() as i32;\n                // slope is a signed 5-bit integer\n                if slope >= 16 {\n                    slope -= 32;\n                }\n                let capvalue = (((slope + 56) * (cap.value2() - 14)) + ((offset - 8) << 4) + 32) >> 6;\n                pac::OSCILLATORS.xosc32mcaps().write(|w| {\n                    w.set_capvalue(capvalue as u8);\n                    w.set_enable(true);\n                });\n            }\n        }\n        if let Some(cap) = config.internal_capacitors.lfxo {\n            pac::OSCILLATORS.xosc32ki().intcap().write(|w| w.set_intcap(cap.into()));\n        }\n    }\n\n    let r = pac::CLOCK;\n\n    // Start HFCLK.\n    match config.hfclk_source {\n        config::HfclkSource::Internal => {}\n        config::HfclkSource::ExternalXtal => {\n            #[cfg(feature = \"_nrf54l\")]\n            {\n                r.events_xostarted().write_value(0);\n                r.tasks_xostart().write_value(1);\n                while r.events_xostarted().read() == 0 {}\n            }\n\n            #[cfg(not(feature = \"_nrf54l\"))]\n            {\n                // Datasheet says this is likely to take 0.36ms\n                r.events_hfclkstarted().write_value(0);\n                r.tasks_hfclkstart().write_value(1);\n                while r.events_hfclkstarted().read() == 0 {}\n            }\n        }\n    }\n\n    // Workaround for anomaly 140\n    #[cfg(feature = \"nrf5340-app-s\")]\n    if unsafe { (0x50032420 as *mut u32).read_volatile() } & 0x80000000 != 0 {\n        r.events_lfclkstarted().write_value(0);\n        r.lfclksrc()\n            .write(|w| w.set_src(nrf_pac::clock::vals::Lfclksrc::LFSYNT));\n        r.tasks_lfclkstart().write_value(1);\n        while r.events_lfclkstarted().read() == 0 {}\n        r.events_lfclkstarted().write_value(0);\n        r.tasks_lfclkstop().write_value(1);\n        r.lfclksrc().write(|w| w.set_src(nrf_pac::clock::vals::Lfclksrc::LFRC));\n    }\n\n    // Configure LFCLK.\n    #[cfg(not(any(feature = \"_nrf51\", feature = \"_nrf5340\", feature = \"_nrf91\", feature = \"_nrf54l\")))]\n    match config.lfclk_source {\n        config::LfclkSource::InternalRC => r.lfclksrc().write(|w| w.set_src(pac::clock::vals::Lfclksrc::RC)),\n        config::LfclkSource::Synthesized => r.lfclksrc().write(|w| w.set_src(pac::clock::vals::Lfclksrc::SYNTH)),\n        config::LfclkSource::ExternalXtal => r.lfclksrc().write(|w| w.set_src(pac::clock::vals::Lfclksrc::XTAL)),\n        config::LfclkSource::ExternalLowSwing => r.lfclksrc().write(|w| {\n            w.set_src(pac::clock::vals::Lfclksrc::XTAL);\n            w.set_external(true);\n            w.set_bypass(false);\n        }),\n        config::LfclkSource::ExternalFullSwing => r.lfclksrc().write(|w| {\n            w.set_src(pac::clock::vals::Lfclksrc::XTAL);\n            w.set_external(true);\n            w.set_bypass(true);\n        }),\n    }\n    #[cfg(feature = \"_nrf5340\")]\n    {\n        #[allow(unused_mut)]\n        let mut lfxo = false;\n        match config.lfclk_source {\n            config::LfclkSource::InternalRC => r.lfclksrc().write(|w| w.set_src(pac::clock::vals::Lfclksrc::LFRC)),\n            config::LfclkSource::Synthesized => r.lfclksrc().write(|w| w.set_src(pac::clock::vals::Lfclksrc::LFSYNT)),\n            #[cfg(not(feature = \"lfxo-pins-as-gpio\"))]\n            config::LfclkSource::ExternalXtal => lfxo = true,\n            #[cfg(not(feature = \"lfxo-pins-as-gpio\"))]\n            config::LfclkSource::ExternalLowSwing => lfxo = true,\n            #[cfg(not(feature = \"lfxo-pins-as-gpio\"))]\n            config::LfclkSource::ExternalFullSwing => {\n                #[cfg(feature = \"_nrf5340-app\")]\n                pac::OSCILLATORS.xosc32ki().bypass().write(|w| w.set_bypass(true));\n                lfxo = true;\n            }\n        }\n        if lfxo {\n            if cfg!(feature = \"_s\") {\n                // MCUSEL is only accessible from secure code.\n                let p0 = pac::P0;\n                p0.pin_cnf(0)\n                    .write(|w| w.set_mcusel(pac::gpio::vals::Mcusel::PERIPHERAL));\n                p0.pin_cnf(1)\n                    .write(|w| w.set_mcusel(pac::gpio::vals::Mcusel::PERIPHERAL));\n            }\n            r.lfclksrc().write(|w| w.set_src(pac::clock::vals::Lfclksrc::LFXO));\n        }\n    }\n    #[cfg(feature = \"_nrf91\")]\n    match config.lfclk_source {\n        config::LfclkSource::InternalRC => r.lfclksrc().write(|w| w.set_src(pac::clock::vals::Lfclksrc::LFRC)),\n        config::LfclkSource::ExternalXtal => r.lfclksrc().write(|w| w.set_src(pac::clock::vals::Lfclksrc::LFXO)),\n    }\n    #[cfg(feature = \"_nrf54l\")]\n    match config.lfclk_source {\n        config::LfclkSource::InternalRC => r.lfclk().src().write(|w| w.set_src(pac::clock::vals::Lfclksrc::LFRC)),\n        config::LfclkSource::Synthesized => r.lfclk().src().write(|w| w.set_src(pac::clock::vals::Lfclksrc::LFSYNT)),\n        config::LfclkSource::ExternalXtal => r.lfclk().src().write(|w| w.set_src(pac::clock::vals::Lfclksrc::LFXO)),\n    }\n\n    // Start LFCLK.\n    // Datasheet says this could take 100us from synth source\n    // 600us from rc source, 0.25s from an external source.\n    r.events_lfclkstarted().write_value(0);\n    r.tasks_lfclkstart().write_value(1);\n    while r.events_lfclkstarted().read() == 0 {}\n\n    #[cfg(not(any(feature = \"_nrf5340\", feature = \"_nrf91\", feature = \"_nrf54l\")))]\n    {\n        // Setup DCDCs.\n        #[cfg(feature = \"nrf52840\")]\n        if config.dcdc.reg0 {\n            pac::POWER.dcdcen0().write(|w| w.set_dcdcen(true));\n        }\n        if config.dcdc.reg1 {\n            pac::POWER.dcdcen().write(|w| w.set_dcdcen(true));\n        }\n    }\n    #[cfg(feature = \"_nrf91\")]\n    {\n        // Setup DCDC.\n        if config.dcdc.regmain {\n            pac::REGULATORS.dcdcen().write(|w| w.set_dcdcen(true));\n        }\n    }\n    #[cfg(feature = \"_nrf5340-app\")]\n    {\n        // Setup DCDC.\n        let reg = pac::REGULATORS;\n        if config.dcdc.regh {\n            reg.vregh().dcdcen().write(|w| w.set_dcdcen(true));\n        }\n        if config.dcdc.regmain {\n            reg.vregmain().dcdcen().write(|w| w.set_dcdcen(true));\n        }\n        if config.dcdc.regradio {\n            reg.vregradio().dcdcen().write(|w| w.set_dcdcen(true));\n        }\n    }\n    #[cfg(feature = \"_nrf54l\")]\n    {\n        // Turn on DCDC\n        // From Product specification:\n        // \"Once the device starts, the DC/DC regulator must be enabled using register VREGMAIN.DCDCEN.\n        // When enabling the DC/DC regulator, the device checks if an inductor is connected to the DCC pin.\n        // If an inductor is not detected, the device remains in LDO mode\"\n        pac::REGULATORS.vregmain().dcdcen().write(|w| w.set_val(true));\n    }\n\n    // Init GPIOTE\n    #[cfg(feature = \"gpiote\")]\n    gpiote::init(config.gpiote_interrupt_priority);\n\n    // init RTC time driver\n    #[cfg(feature = \"_time-driver\")]\n    time_driver::init(config.time_interrupt_priority);\n\n    // Disable UARTE (enabled by default for some reason)\n    #[cfg(feature = \"_nrf91\")]\n    {\n        use pac::uarte::vals::Enable;\n        pac::UARTE0.enable().write(|w| w.set_enable(Enable::DISABLED));\n        pac::UARTE1.enable().write(|w| w.set_enable(Enable::DISABLED));\n    }\n\n    peripherals\n}\n\n/// Operating modes for peripherals.\npub mod mode {\n    trait SealedMode {}\n\n    /// Operating mode for a peripheral.\n    #[allow(private_bounds)]\n    pub trait Mode: SealedMode {}\n\n    macro_rules! impl_mode {\n        ($name:ident) => {\n            impl SealedMode for $name {}\n            impl Mode for $name {}\n        };\n    }\n\n    /// Blocking mode.\n    pub struct Blocking;\n    /// Async mode.\n    pub struct Async;\n\n    impl_mode!(Blocking);\n    impl_mode!(Async);\n}\n"
  },
  {
    "path": "embassy-nrf/src/nfct.rs",
    "content": "//! NFC tag emulator driver.\n//!\n//! This driver implements support for emulating an ISO14443-3 card. Anticollision and selection\n//! are handled automatically in hardware, then the driver lets you receive and reply to\n//! raw ISO14443-3 frames in software.\n//!\n//! Higher layers such as ISO14443-4 aka ISO-DEP and ISO7816 must be handled on top\n//! in software.\n\n#![macro_use]\n\nuse core::future::poll_fn;\nuse core::sync::atomic::{Ordering, compiler_fence};\nuse core::task::Poll;\n\nuse embassy_sync::waitqueue::AtomicWaker;\npub use vals::{Bitframesdd as SddPat, Discardmode as DiscardMode};\n\nuse crate::interrupt::InterruptExt;\nuse crate::pac::NFCT;\nuse crate::pac::nfct::vals;\nuse crate::peripherals::NFCT;\nuse crate::util::slice_in_ram;\nuse crate::{Peri, interrupt, pac};\n\n/// NFCID1 (aka UID) of different sizes.\n#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)]\npub enum NfcId {\n    /// 4-byte UID.\n    SingleSize([u8; 4]),\n    /// 7-byte UID.\n    DoubleSize([u8; 7]),\n    /// 10-byte UID.\n    TripleSize([u8; 10]),\n}\n\n/// The protocol field to be sent in the `SEL_RES` response byte (b6-b7).\n#[derive(Default, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)]\npub enum SelResProtocol {\n    /// Configured for Type 2 Tag platform.\n    #[default]\n    Type2 = 0,\n    /// Configured for Type 4A Tag platform, compliant with ISO/IEC_14443.\n    Type4A = 1,\n    /// Configured for the NFC-DEP Protocol.\n    NfcDep = 2,\n    /// Configured for the NFC-DEP Protocol and Type 4A Tag platform.\n    NfcDepAndType4A = 3,\n}\n\n/// Config for the `NFCT` peripheral driver.\n#[derive(Clone)]\npub struct Config {\n    /// NFCID1 to use during autocollision.\n    pub nfcid1: NfcId,\n    /// SDD pattern to be sent in `SENS_RES`.\n    pub sdd_pat: SddPat,\n    /// Platform config to be sent in `SEL_RES`.\n    pub plat_conf: u8,\n    /// Protocol to be sent in the `SEL_RES` response.\n    pub protocol: SelResProtocol,\n}\n\n/// Interrupt handler.\npub struct InterruptHandler {\n    _private: (),\n}\n\nimpl interrupt::typelevel::Handler<interrupt::typelevel::NFCT> for InterruptHandler {\n    unsafe fn on_interrupt() {\n        trace!(\"irq\");\n        pac::NFCT.inten().write(|w| w.0 = 0);\n        WAKER.wake();\n    }\n}\n\nstatic WAKER: AtomicWaker = AtomicWaker::new();\n\n/// NFC error.\n#[derive(Debug, Clone, Copy, PartialEq, Eq)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\n#[non_exhaustive]\npub enum Error {\n    /// Rx Error received while waiting for frame\n    RxError,\n    /// Rx buffer was overrun, increase your buffer size to resolve this\n    RxOverrun,\n    /// Lost field.\n    Deactivated,\n    /// Collision\n    Collision,\n    /// The buffer is not in data RAM. It's most likely in flash, and nRF's DMA cannot access flash.\n    BufferNotInRAM,\n}\n\n/// NFC tag emulator driver.\npub struct NfcT<'d> {\n    _p: Peri<'d, NFCT>,\n    rx_buf: [u8; 256],\n    tx_buf: [u8; 256],\n}\n\nimpl<'d> NfcT<'d> {\n    /// Create an Nfc Tag driver\n    pub fn new(\n        _p: Peri<'d, NFCT>,\n        _irq: impl interrupt::typelevel::Binding<interrupt::typelevel::NFCT, InterruptHandler> + 'd,\n        config: &Config,\n    ) -> Self {\n        let r = pac::NFCT;\n\n        unsafe {\n            let reset = (r.as_ptr() as *mut u32).add(0xFFC / 4);\n            reset.write_volatile(0);\n            reset.read_volatile();\n            reset.write_volatile(1);\n        }\n\n        let nfcid_size = match &config.nfcid1 {\n            NfcId::SingleSize(bytes) => {\n                r.nfcid1_last().write(|w| w.0 = u32::from_be_bytes(*bytes));\n\n                vals::Nfcidsize::NFCID1SINGLE\n            }\n            NfcId::DoubleSize(bytes) => {\n                let (bytes, chunk) = bytes.split_last_chunk::<4>().unwrap();\n                r.nfcid1_last().write(|w| w.0 = u32::from_be_bytes(*chunk));\n\n                let mut chunk = [0u8; 4];\n                chunk[1..].copy_from_slice(bytes);\n                r.nfcid1_2nd_last().write(|w| w.0 = u32::from_be_bytes(chunk));\n\n                vals::Nfcidsize::NFCID1DOUBLE\n            }\n            NfcId::TripleSize(bytes) => {\n                let (bytes, chunk) = bytes.split_last_chunk::<4>().unwrap();\n                r.nfcid1_last().write(|w| w.0 = u32::from_be_bytes(*chunk));\n\n                let (bytes, chunk2) = bytes.split_last_chunk::<3>().unwrap();\n                let mut chunk = [0u8; 4];\n                chunk[1..].copy_from_slice(chunk2);\n                r.nfcid1_2nd_last().write(|w| w.0 = u32::from_be_bytes(chunk));\n\n                let mut chunk = [0u8; 4];\n                chunk[1..].copy_from_slice(bytes);\n                r.nfcid1_3rd_last().write(|w| w.0 = u32::from_be_bytes(chunk));\n\n                vals::Nfcidsize::NFCID1TRIPLE\n            }\n        };\n\n        r.sensres().write(|w| {\n            w.set_nfcidsize(nfcid_size);\n            w.set_bitframesdd(config.sdd_pat);\n            w.set_platfconfig(config.plat_conf & 0xF);\n        });\n\n        r.selres().write(|w| {\n            w.set_protocol(config.protocol as u8);\n        });\n\n        // errata\n        #[cfg(feature = \"nrf52832\")]\n        unsafe {\n            // Errata 57 nrf52832 only\n            //(0x40005610 as *mut u32).write_volatile(0x00000005);\n            //(0x40005688 as *mut u32).write_volatile(0x00000001);\n            //(0x40005618 as *mut u32).write_volatile(0x00000000);\n            //(0x40005614 as *mut u32).write_volatile(0x0000003F);\n\n            // Errata 98\n            (0x4000568C as *mut u32).write_volatile(0x00038148);\n        }\n\n        r.inten().write(|w| w.0 = 0);\n\n        interrupt::NFCT.unpend();\n        unsafe { interrupt::NFCT.enable() };\n\n        // clear all shorts\n        r.shorts().write(|_| {});\n\n        let res = Self {\n            _p,\n            tx_buf: [0u8; 256],\n            rx_buf: [0u8; 256],\n        };\n\n        assert!(slice_in_ram(&res.tx_buf), \"TX Buf not in ram\");\n        assert!(slice_in_ram(&res.rx_buf), \"RX Buf not in ram\");\n\n        res\n    }\n\n    /// Wait for field on and select.\n    ///\n    /// This waits for the field to become on, and then for a reader to select us. The ISO14443-3\n    /// sense, anticollision and select procedure is handled entirely in hardware.\n    ///\n    /// When this returns, we have successfully been selected as a card. You must then\n    /// loop calling [`receive`](Self::receive) and responding with [`transmit`](Self::transmit).\n    pub async fn activate(&mut self) {\n        let r = pac::NFCT;\n        loop {\n            r.events_fieldlost().write_value(0);\n            r.events_fielddetected().write_value(0);\n            r.tasks_sense().write_value(1);\n\n            // enable autocoll\n            #[cfg(not(feature = \"nrf52832\"))]\n            r.autocolresconfig().write(|w| w.0 = 0b10);\n\n            // framedelaymax=4096 is needed to make it work with phones from\n            // a certain company named after some fruit.\n            r.framedelaymin().write(|w| w.set_framedelaymin(1152));\n            r.framedelaymax().write(|w| w.set_framedelaymax(4096));\n            r.framedelaymode().write(|w| {\n                w.set_framedelaymode(vals::Framedelaymode::WINDOW_GRID);\n            });\n\n            info!(\"waiting for field\");\n            poll_fn(|cx| {\n                WAKER.register(cx.waker());\n\n                if r.events_fielddetected().read() != 0 {\n                    r.events_fielddetected().write_value(0);\n                    return Poll::Ready(());\n                }\n\n                r.inten().write(|w| {\n                    w.set_fielddetected(true);\n                });\n                Poll::Pending\n            })\n            .await;\n\n            #[cfg(feature = \"time\")]\n            embassy_time::Timer::after_millis(1).await; // workaround errata 190\n\n            r.events_selected().write_value(0);\n            r.tasks_activate().write_value(1);\n\n            trace!(\"Waiting to be selected\");\n            poll_fn(|cx| {\n                let r = pac::NFCT;\n\n                WAKER.register(cx.waker());\n\n                if r.events_selected().read() != 0 || r.events_fieldlost().read() != 0 {\n                    return Poll::Ready(());\n                }\n\n                r.inten().write(|w| {\n                    w.set_selected(true);\n                    w.set_fieldlost(true);\n                });\n                Poll::Pending\n            })\n            .await;\n            if r.events_fieldlost().read() != 0 {\n                continue;\n            }\n\n            // disable autocoll\n            #[cfg(not(feature = \"nrf52832\"))]\n            r.autocolresconfig().write(|w| w.0 = 0b11u32);\n\n            // once anticoll is done, set framedelaymax to the maximum possible.\n            // this gives the firmware as much time as possible to reply.\n            // higher layer still has to reply faster than the FWT it specifies in the iso14443-4 ATS,\n            // but that's not our concern.\n            //\n            // nrf52832 field is 16bit instead of 20bit. this seems to force a too short timeout, maybe it's a SVD bug?\n            #[cfg(not(feature = \"nrf52832\"))]\n            r.framedelaymax().write(|w| w.set_framedelaymax(0xF_FFFF));\n            #[cfg(feature = \"nrf52832\")]\n            r.framedelaymax().write(|w| w.set_framedelaymax(0xFFFF));\n\n            return;\n        }\n    }\n\n    /// Transmit an ISO14443-3 frame to the reader.\n    ///\n    /// You must call this only after receiving a frame with [`receive`](Self::receive),\n    /// and only once. Higher-layer protocols usually define timeouts, so calling this\n    /// too late can cause things to fail.\n    ///\n    /// This will fail with [`Error::Deactivated`] if we have been deselected due to either\n    /// the field being switched off or due to the ISO14443 state machine. When this happens,\n    /// you must stop calling [`receive`](Self::receive) and [`transmit`](Self::transmit), reset\n    /// all protocol state, and go back to calling [`activate`](Self::activate).\n    pub async fn transmit(&mut self, buf: &[u8]) -> Result<(), Error> {\n        let r = pac::NFCT;\n\n        //Setup DMA\n        self.tx_buf[..buf.len()].copy_from_slice(buf);\n        r.packetptr().write_value(self.tx_buf.as_ptr() as u32);\n        r.maxlen().write(|w| w.0 = buf.len() as _);\n\n        // Set packet length\n        r.txd().amount().write(|w| {\n            w.set_txdatabits(0);\n            w.set_txdatabytes(buf.len() as _);\n        });\n\n        r.txd().frameconfig().write(|w| {\n            w.set_crcmodetx(true);\n            w.set_discardmode(DiscardMode::DISCARD_END);\n            w.set_parity(true);\n            w.set_sof(true);\n        });\n\n        r.events_error().write_value(0);\n        r.events_txframeend().write_value(0);\n        r.errorstatus().write(|w| w.0 = 0xffff_ffff);\n\n        // Start starttx task\n        compiler_fence(Ordering::SeqCst);\n        r.tasks_starttx().write_value(1);\n\n        poll_fn(move |cx| {\n            trace!(\"polling tx\");\n            let r = pac::NFCT;\n            WAKER.register(cx.waker());\n\n            if r.events_fieldlost().read() != 0 {\n                return Poll::Ready(Err(Error::Deactivated));\n            }\n\n            if r.events_txframeend().read() != 0 {\n                trace!(\"Txframend hit, should be finished trasmitting\");\n                return Poll::Ready(Ok(()));\n            }\n\n            if r.events_error().read() != 0 {\n                trace!(\"Got error?\");\n                let errs = r.errorstatus().read();\n                r.errorstatus().write(|w| w.0 = 0xFFFF_FFFF);\n                trace!(\"errors: {:08x}\", errs.0);\n                r.events_error().write_value(0);\n                return Poll::Ready(Err(Error::RxError));\n            }\n\n            r.inten().write(|w| {\n                w.set_txframeend(true);\n                w.set_error(true);\n                w.set_fieldlost(true);\n            });\n\n            Poll::Pending\n        })\n        .await\n    }\n\n    /// Receive an ISO14443-3 frame from the reader.\n    ///\n    /// After calling this, you must send back a response with [`transmit`](Self::transmit),\n    /// and only once. Higher-layer protocols usually define timeouts, so calling this\n    /// too late can cause things to fail.\n    pub async fn receive(&mut self, buf: &mut [u8]) -> Result<usize, Error> {\n        let r = pac::NFCT;\n\n        r.rxd().frameconfig().write(|w| {\n            w.set_crcmoderx(true);\n            w.set_parity(true);\n            w.set_sof(true);\n        });\n\n        //Setup DMA\n        r.packetptr().write_value(self.rx_buf.as_mut_ptr() as u32);\n        r.maxlen().write(|w| w.0 = self.rx_buf.len() as _);\n\n        // Reset and enable the end event\n        r.events_rxframeend().write_value(0);\n        r.events_rxerror().write_value(0);\n\n        // Start enablerxdata only after configs are finished writing\n        compiler_fence(Ordering::SeqCst);\n        r.tasks_enablerxdata().write_value(1);\n\n        poll_fn(move |cx| {\n            trace!(\"polling rx\");\n            let r = pac::NFCT;\n            WAKER.register(cx.waker());\n\n            if r.events_fieldlost().read() != 0 {\n                return Poll::Ready(Err(Error::Deactivated));\n            }\n\n            if r.events_rxerror().read() != 0 {\n                trace!(\"RXerror got in recv frame, should be back in idle state\");\n                r.events_rxerror().write_value(0);\n                let errs = r.framestatus().rx().read();\n                r.framestatus().rx().write(|w| w.0 = 0xFFFF_FFFF);\n                trace!(\"errors: {:08x}\", errs.0);\n                return Poll::Ready(Err(Error::RxError));\n            }\n\n            if r.events_rxframeend().read() != 0 {\n                trace!(\"RX Frameend got in recv frame, should have data\");\n                r.events_rxframeend().write_value(0);\n                return Poll::Ready(Ok(()));\n            }\n\n            r.inten().write(|w| {\n                w.set_rxframeend(true);\n                w.set_rxerror(true);\n                w.set_fieldlost(true);\n            });\n\n            Poll::Pending\n        })\n        .await?;\n\n        let n = r.rxd().amount().read().rxdatabytes() as usize - 2;\n        buf[..n].copy_from_slice(&self.rx_buf[..n]);\n        Ok(n)\n    }\n}\n\n/// Wake the system if there if an NFC field close to the antenna\npub fn wake_on_nfc_sense() {\n    NFCT.tasks_sense().write_value(0x01);\n}\n"
  },
  {
    "path": "embassy-nrf/src/nvmc.rs",
    "content": "//! Non-Volatile Memory Controller (NVMC, AKA internal flash) driver.\n\nuse core::{ptr, slice};\n\nuse embedded_storage::nor_flash::{\n    ErrorType, MultiwriteNorFlash, NorFlash, NorFlashError, NorFlashErrorKind, ReadNorFlash,\n};\n\nuse crate::pac::nvmc::vals;\nuse crate::peripherals::NVMC;\nuse crate::{Peri, pac};\n\n#[cfg(not(feature = \"_nrf5340-net\"))]\n/// Erase size of NVMC flash in bytes.\npub const PAGE_SIZE: usize = 4096;\n#[cfg(feature = \"_nrf5340-net\")]\n/// Erase size of NVMC flash in bytes.\npub const PAGE_SIZE: usize = 2048;\n\n/// Size of NVMC flash in bytes.\npub const FLASH_SIZE: usize = crate::chip::FLASH_SIZE;\n\n/// Error type for NVMC operations.\n#[derive(Debug, Copy, Clone, PartialEq, Eq)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub enum Error {\n    /// Operation using a location not in flash.\n    OutOfBounds,\n    /// Unaligned operation or using unaligned buffers.\n    Unaligned,\n}\n\nimpl NorFlashError for Error {\n    fn kind(&self) -> NorFlashErrorKind {\n        match self {\n            Self::OutOfBounds => NorFlashErrorKind::OutOfBounds,\n            Self::Unaligned => NorFlashErrorKind::NotAligned,\n        }\n    }\n}\n\n/// Non-Volatile Memory Controller (NVMC) that implements the `embedded-storage` traits.\npub struct Nvmc<'d> {\n    _p: Peri<'d, NVMC>,\n}\n\nimpl<'d> Nvmc<'d> {\n    /// Create Nvmc driver.\n    pub fn new(_p: Peri<'d, NVMC>) -> Self {\n        Self { _p }\n    }\n\n    fn regs() -> pac::nvmc::Nvmc {\n        pac::NVMC\n    }\n\n    fn wait_ready(&mut self) {\n        let p = Self::regs();\n        while !p.ready().read().ready() {}\n    }\n\n    #[cfg(not(any(feature = \"_nrf91\", feature = \"_nrf5340\")))]\n    fn wait_ready_write(&mut self) {\n        self.wait_ready();\n    }\n\n    #[cfg(any(feature = \"_nrf91\", feature = \"_nrf5340\"))]\n    fn wait_ready_write(&mut self) {\n        let p = Self::regs();\n        while !p.readynext().read().readynext() {}\n    }\n\n    #[cfg(not(any(feature = \"_nrf91\", feature = \"_nrf5340\")))]\n    fn erase_page(&mut self, page_addr: u32) {\n        Self::regs().erasepage().write_value(page_addr);\n    }\n\n    #[cfg(any(feature = \"_nrf91\", feature = \"_nrf5340\"))]\n    fn erase_page(&mut self, page_addr: u32) {\n        let first_page_word = page_addr as *mut u32;\n        unsafe {\n            first_page_word.write_volatile(0xFFFF_FFFF);\n        }\n    }\n\n    fn enable_erase(&self) {\n        #[cfg(not(feature = \"_ns\"))]\n        Self::regs().config().write(|w| w.set_wen(vals::Wen::EEN));\n        #[cfg(feature = \"_ns\")]\n        Self::regs().configns().write(|w| w.set_wen(vals::ConfignsWen::EEN));\n    }\n\n    fn enable_read(&self) {\n        #[cfg(not(feature = \"_ns\"))]\n        Self::regs().config().write(|w| w.set_wen(vals::Wen::REN));\n        #[cfg(feature = \"_ns\")]\n        Self::regs().configns().write(|w| w.set_wen(vals::ConfignsWen::REN));\n    }\n\n    fn enable_write(&self) {\n        #[cfg(not(feature = \"_ns\"))]\n        Self::regs().config().write(|w| w.set_wen(vals::Wen::WEN));\n        #[cfg(feature = \"_ns\")]\n        Self::regs().configns().write(|w| w.set_wen(vals::ConfignsWen::WEN));\n    }\n}\n\nimpl<'d> MultiwriteNorFlash for Nvmc<'d> {}\n\nimpl<'d> ErrorType for Nvmc<'d> {\n    type Error = Error;\n}\n\nimpl<'d> ReadNorFlash for Nvmc<'d> {\n    const READ_SIZE: usize = 1;\n\n    fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Self::Error> {\n        if offset as usize >= FLASH_SIZE || offset as usize + bytes.len() > FLASH_SIZE {\n            return Err(Error::OutOfBounds);\n        }\n\n        let flash_data = unsafe { slice::from_raw_parts(offset as *const u8, bytes.len()) };\n        bytes.copy_from_slice(flash_data);\n        Ok(())\n    }\n\n    fn capacity(&self) -> usize {\n        FLASH_SIZE\n    }\n}\n\nimpl<'d> NorFlash for Nvmc<'d> {\n    const WRITE_SIZE: usize = 4;\n    const ERASE_SIZE: usize = PAGE_SIZE;\n\n    fn erase(&mut self, from: u32, to: u32) -> Result<(), Self::Error> {\n        if to < from || to as usize > FLASH_SIZE {\n            return Err(Error::OutOfBounds);\n        }\n        if from as usize % PAGE_SIZE != 0 || to as usize % PAGE_SIZE != 0 {\n            return Err(Error::Unaligned);\n        }\n\n        self.enable_erase();\n        self.wait_ready();\n\n        for page_addr in (from..to).step_by(PAGE_SIZE) {\n            self.erase_page(page_addr);\n            self.wait_ready();\n        }\n\n        self.enable_read();\n        self.wait_ready();\n\n        Ok(())\n    }\n\n    fn write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Self::Error> {\n        if offset as usize + bytes.len() > FLASH_SIZE {\n            return Err(Error::OutOfBounds);\n        }\n        if offset as usize % 4 != 0 || bytes.len() % 4 != 0 {\n            return Err(Error::Unaligned);\n        }\n\n        self.enable_write();\n        self.wait_ready();\n\n        unsafe {\n            let p_src = bytes.as_ptr() as *const u32;\n            let p_dst = offset as *mut u32;\n            let words = bytes.len() / 4;\n            for i in 0..words {\n                let w = ptr::read_unaligned(p_src.add(i));\n                ptr::write_volatile(p_dst.add(i), w);\n                self.wait_ready_write();\n            }\n        }\n\n        self.enable_read();\n        self.wait_ready();\n\n        Ok(())\n    }\n}\n"
  },
  {
    "path": "embassy-nrf/src/pdm.rs",
    "content": "//! Pulse Density Modulation (PDM) microphone driver\n\n#![macro_use]\n\nuse core::future::poll_fn;\nuse core::marker::PhantomData;\nuse core::sync::atomic::{Ordering, compiler_fence};\nuse core::task::Poll;\n\nuse embassy_hal_internal::drop::OnDrop;\nuse embassy_hal_internal::{Peri, PeripheralType};\nuse embassy_sync::waitqueue::AtomicWaker;\nuse fixed::types::I7F1;\n\nuse crate::chip::EASY_DMA_SIZE;\nuse crate::gpio::{AnyPin, DISCONNECTED, Pin as GpioPin, SealedPin};\nuse crate::interrupt::typelevel::Interrupt;\nuse crate::pac::gpio::vals as gpiovals;\nuse crate::pac::pdm::vals;\npub use crate::pac::pdm::vals::Freq as Frequency;\n#[cfg(any(\n    feature = \"nrf52840\",\n    feature = \"nrf52833\",\n    feature = \"_nrf5340-app\",\n    feature = \"_nrf91\",\n))]\npub use crate::pac::pdm::vals::Ratio;\nuse crate::{interrupt, pac};\n\n/// Interrupt handler\npub struct InterruptHandler<T: Instance> {\n    _phantom: PhantomData<T>,\n}\n\nimpl<T: Instance> interrupt::typelevel::Handler<T::Interrupt> for InterruptHandler<T> {\n    unsafe fn on_interrupt() {\n        let r = T::regs();\n\n        if r.events_end().read() != 0 {\n            r.intenclr().write(|w| w.set_end(true));\n        }\n\n        if r.events_started().read() != 0 {\n            r.intenclr().write(|w| w.set_started(true));\n        }\n\n        if r.events_stopped().read() != 0 {\n            r.intenclr().write(|w| w.set_stopped(true));\n        }\n\n        T::state().waker.wake();\n    }\n}\n\n/// PDM microphone interface\npub struct Pdm<'d> {\n    r: pac::pdm::Pdm,\n    state: &'static State,\n    _phantom: PhantomData<&'d ()>,\n}\n\n/// PDM error\n#[derive(Debug, Clone, Copy, PartialEq, Eq)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\n#[non_exhaustive]\npub enum Error {\n    /// Buffer is too long\n    BufferTooLong,\n    /// Buffer is empty\n    BufferZeroLength,\n    /// PDM is not running\n    NotRunning,\n    /// PDM is already running\n    AlreadyRunning,\n}\n\nstatic DUMMY_BUFFER: [i16; 1] = [0; 1];\n\n/// The state of a continuously running sampler. While it reflects\n/// the progress of a sampler, it also signals what should be done\n/// next. For example, if the sampler has stopped then the PDM implementation\n/// can then tear down its infrastructure\n#[derive(PartialEq)]\npub enum SamplerState {\n    /// The sampler processed the samples and is ready for more\n    Sampled,\n    /// The sampler is done processing samples\n    Stopped,\n}\n\nimpl<'d> Pdm<'d> {\n    /// Create PDM driver\n    pub fn new<T: Instance>(\n        pdm: Peri<'d, T>,\n        _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd,\n        clk: Peri<'d, impl GpioPin>,\n        din: Peri<'d, impl GpioPin>,\n        config: Config,\n    ) -> Self {\n        Self::new_inner(pdm, clk.into(), din.into(), config)\n    }\n\n    fn new_inner<T: Instance>(_pdm: Peri<'d, T>, clk: Peri<'d, AnyPin>, din: Peri<'d, AnyPin>, config: Config) -> Self {\n        let r = T::regs();\n\n        // setup gpio pins\n        din.conf().write(|w| w.set_input(gpiovals::Input::CONNECT));\n        r.psel().din().write_value(din.psel_bits());\n        clk.set_low();\n        clk.conf().write(|w| w.set_dir(gpiovals::Dir::OUTPUT));\n        r.psel().clk().write_value(clk.psel_bits());\n\n        // configure\n        r.pdmclkctrl().write(|w| w.set_freq(config.frequency));\n        #[cfg(any(\n            feature = \"nrf52840\",\n            feature = \"nrf52833\",\n            feature = \"_nrf5340-app\",\n            feature = \"_nrf91\",\n        ))]\n        r.ratio().write(|w| w.set_ratio(config.ratio));\n        r.mode().write(|w| {\n            w.set_operation(config.operation_mode.into());\n            w.set_edge(config.edge.into());\n        });\n\n        Self::_set_gain(r, config.gain_left, config.gain_right);\n\n        // Disable all events interrupts\n        r.intenclr().write(|w| w.0 = 0x003F_FFFF);\n\n        // IRQ\n        T::Interrupt::unpend();\n        unsafe { T::Interrupt::enable() };\n\n        r.enable().write(|w| w.set_enable(true));\n\n        Self {\n            r: T::regs(),\n            state: T::state(),\n            _phantom: PhantomData,\n        }\n    }\n\n    fn _set_gain(r: pac::pdm::Pdm, gain_left: I7F1, gain_right: I7F1) {\n        let gain_to_bits = |gain: I7F1| -> vals::Gain {\n            let gain: i8 = gain.saturating_add(I7F1::from_bits(0x28)).to_bits().clamp(0, 0x50);\n            vals::Gain::from_bits(gain as u8)\n        };\n        r.gainl().write(|w| w.set_gainl(gain_to_bits(gain_left)));\n        r.gainr().write(|w| w.set_gainr(gain_to_bits(gain_right)));\n    }\n\n    /// Adjust the gain of the PDM microphone on the fly\n    pub fn set_gain(&mut self, gain_left: I7F1, gain_right: I7F1) {\n        Self::_set_gain(self.r, gain_left, gain_right)\n    }\n\n    /// Start sampling microphone data into a dummy buffer.\n    /// Useful to start the microphone and keep it active between recording samples.\n    pub async fn start(&mut self) {\n        // start dummy sampling because microphone needs some setup time\n        self.r.sample().ptr().write_value(DUMMY_BUFFER.as_ptr() as u32);\n        self.r\n            .sample()\n            .maxcnt()\n            .write(|w| w.set_buffsize(DUMMY_BUFFER.len() as _));\n\n        self.r.tasks_start().write_value(1);\n    }\n\n    /// Stop sampling microphone data inta a dummy buffer\n    pub async fn stop(&mut self) {\n        self.r.tasks_stop().write_value(1);\n        self.r.events_started().write_value(0);\n    }\n\n    /// Sample data into the given buffer\n    pub async fn sample(&mut self, buffer: &mut [i16]) -> Result<(), Error> {\n        if buffer.is_empty() {\n            return Err(Error::BufferZeroLength);\n        }\n        if buffer.len() > EASY_DMA_SIZE {\n            return Err(Error::BufferTooLong);\n        }\n\n        if self.r.events_started().read() == 0 {\n            return Err(Error::NotRunning);\n        }\n\n        let r = self.r;\n        let drop = OnDrop::new(move || {\n            r.intenclr().write(|w| w.set_end(true));\n            r.events_stopped().write_value(0);\n\n            // reset to dummy buffer\n            r.sample().ptr().write_value(DUMMY_BUFFER.as_ptr() as u32);\n            r.sample().maxcnt().write(|w| w.set_buffsize(DUMMY_BUFFER.len() as _));\n\n            while r.events_stopped().read() == 0 {}\n        });\n\n        // setup user buffer\n        let ptr = buffer.as_ptr();\n        let len = buffer.len();\n        self.r.sample().ptr().write_value(ptr as u32);\n        self.r.sample().maxcnt().write(|w| w.set_buffsize(len as _));\n\n        // wait till the current sample is finished and the user buffer sample is started\n        self.wait_for_sample().await;\n\n        // reset the buffer back to the dummy buffer\n        self.r.sample().ptr().write_value(DUMMY_BUFFER.as_ptr() as u32);\n        self.r\n            .sample()\n            .maxcnt()\n            .write(|w| w.set_buffsize(DUMMY_BUFFER.len() as _));\n\n        // wait till the user buffer is sampled\n        self.wait_for_sample().await;\n\n        drop.defuse();\n\n        Ok(())\n    }\n\n    async fn wait_for_sample(&mut self) {\n        self.r.events_end().write_value(0);\n        self.r.intenset().write(|w| w.set_end(true));\n\n        compiler_fence(Ordering::SeqCst);\n\n        let state = self.state;\n        let r = self.r;\n        poll_fn(|cx| {\n            state.waker.register(cx.waker());\n            if r.events_end().read() != 0 {\n                return Poll::Ready(());\n            }\n            Poll::Pending\n        })\n        .await;\n\n        compiler_fence(Ordering::SeqCst);\n    }\n\n    /// Continuous sampling with double buffers.\n    ///\n    /// A sampler closure is provided that receives the buffer of samples, noting\n    /// that the size of this buffer can be less than the original buffer's size.\n    /// A command is return from the closure that indicates whether the sampling\n    /// should continue or stop.\n    ///\n    /// NOTE: The time spent within the callback supplied should not exceed the time\n    /// taken to acquire the samples into a single buffer. You should measure the\n    /// time taken by the callback and set the sample buffer size accordingly.\n    /// Exceeding this time can lead to samples becoming dropped.\n    pub async fn run_task_sampler<S, const N: usize>(\n        &mut self,\n        bufs: &mut [[i16; N]; 2],\n        mut sampler: S,\n    ) -> Result<(), Error>\n    where\n        S: FnMut(&[i16; N]) -> SamplerState,\n    {\n        if self.r.events_started().read() != 0 {\n            return Err(Error::AlreadyRunning);\n        }\n\n        self.r.sample().ptr().write_value(bufs[0].as_mut_ptr() as u32);\n        self.r.sample().maxcnt().write(|w| w.set_buffsize(N as _));\n\n        // Reset and enable the events\n        self.r.events_end().write_value(0);\n        self.r.events_started().write_value(0);\n        self.r.events_stopped().write_value(0);\n        self.r.intenset().write(|w| {\n            w.set_end(true);\n            w.set_started(true);\n            w.set_stopped(true);\n        });\n\n        // Don't reorder the start event before the previous writes. Hopefully self\n        // wouldn't happen anyway\n        compiler_fence(Ordering::SeqCst);\n\n        self.r.tasks_start().write_value(1);\n\n        let mut current_buffer = 0;\n\n        let mut done = false;\n\n        let r = self.r;\n        let drop = OnDrop::new(move || {\n            r.tasks_stop().write_value(1);\n            // N.B. It would be better if this were async, but Drop only support sync code\n            while r.events_stopped().read() != 0 {}\n        });\n\n        let state = self.state;\n        let r = self.r;\n        // Wait for events and complete when the sampler indicates it has had enough\n        poll_fn(|cx| {\n            state.waker.register(cx.waker());\n\n            if r.events_end().read() != 0 {\n                compiler_fence(Ordering::SeqCst);\n\n                r.events_end().write_value(0);\n                r.intenset().write(|w| w.set_end(true));\n\n                if !done {\n                    // Discard the last buffer after the user requested a stop\n                    if sampler(&bufs[current_buffer]) == SamplerState::Sampled {\n                        let next_buffer = 1 - current_buffer;\n                        current_buffer = next_buffer;\n                    } else {\n                        r.tasks_stop().write_value(1);\n                        done = true;\n                    };\n                };\n            }\n\n            if r.events_started().read() != 0 {\n                r.events_started().write_value(0);\n                r.intenset().write(|w| w.set_started(true));\n\n                let next_buffer = 1 - current_buffer;\n                r.sample().ptr().write_value(bufs[next_buffer].as_mut_ptr() as u32);\n            }\n\n            if r.events_stopped().read() != 0 {\n                return Poll::Ready(());\n            }\n\n            Poll::Pending\n        })\n        .await;\n        drop.defuse();\n        Ok(())\n    }\n}\n\n/// PDM microphone driver Config\npub struct Config {\n    /// Use stero or mono operation\n    pub operation_mode: OperationMode,\n    /// On which edge the left channel should be samples\n    pub edge: Edge,\n    /// Clock frequency\n    pub frequency: Frequency,\n    /// Clock ratio\n    #[cfg(any(\n        feature = \"nrf52840\",\n        feature = \"nrf52833\",\n        feature = \"_nrf5340-app\",\n        feature = \"_nrf91\",\n    ))]\n    pub ratio: Ratio,\n    /// Gain left in dB\n    pub gain_left: I7F1,\n    /// Gain right in dB\n    pub gain_right: I7F1,\n}\n\nimpl Default for Config {\n    fn default() -> Self {\n        Self {\n            operation_mode: OperationMode::Mono,\n            edge: Edge::LeftFalling,\n            frequency: Frequency::DEFAULT,\n            #[cfg(any(\n                feature = \"nrf52840\",\n                feature = \"nrf52833\",\n                feature = \"_nrf5340-app\",\n                feature = \"_nrf91\",\n            ))]\n            ratio: Ratio::RATIO80,\n            gain_left: I7F1::ZERO,\n            gain_right: I7F1::ZERO,\n        }\n    }\n}\n\n/// PDM operation mode\n#[derive(PartialEq)]\npub enum OperationMode {\n    /// Mono (1 channel)\n    Mono,\n    /// Stereo (2 channels)\n    Stereo,\n}\n\nimpl From<OperationMode> for vals::Operation {\n    fn from(mode: OperationMode) -> Self {\n        match mode {\n            OperationMode::Mono => vals::Operation::MONO,\n            OperationMode::Stereo => vals::Operation::STEREO,\n        }\n    }\n}\n\n/// PDM edge polarity\n#[derive(PartialEq)]\npub enum Edge {\n    /// Left edge is rising\n    LeftRising,\n    /// Left edge is falling\n    LeftFalling,\n}\n\nimpl From<Edge> for vals::Edge {\n    fn from(edge: Edge) -> Self {\n        match edge {\n            Edge::LeftRising => vals::Edge::LEFT_RISING,\n            Edge::LeftFalling => vals::Edge::LEFT_FALLING,\n        }\n    }\n}\n\nimpl<'d> Drop for Pdm<'d> {\n    fn drop(&mut self) {\n        self.r.tasks_stop().write_value(1);\n\n        self.r.enable().write(|w| w.set_enable(false));\n\n        self.r.psel().din().write_value(DISCONNECTED);\n        self.r.psel().clk().write_value(DISCONNECTED);\n    }\n}\n\n/// Peripheral static state\npub(crate) struct State {\n    waker: AtomicWaker,\n}\n\nimpl State {\n    pub(crate) const fn new() -> Self {\n        Self {\n            waker: AtomicWaker::new(),\n        }\n    }\n}\n\npub(crate) trait SealedInstance {\n    fn regs() -> crate::pac::pdm::Pdm;\n    fn state() -> &'static State;\n}\n\n/// PDM peripheral instance\n#[allow(private_bounds)]\npub trait Instance: SealedInstance + PeripheralType + 'static + Send {\n    /// Interrupt for this peripheral\n    type Interrupt: interrupt::typelevel::Interrupt;\n}\n\nmacro_rules! impl_pdm {\n    ($type:ident, $pac_type:ident, $irq:ident) => {\n        impl crate::pdm::SealedInstance for peripherals::$type {\n            fn regs() -> crate::pac::pdm::Pdm {\n                pac::$pac_type\n            }\n            fn state() -> &'static crate::pdm::State {\n                static STATE: crate::pdm::State = crate::pdm::State::new();\n                &STATE\n            }\n        }\n        impl crate::pdm::Instance for peripherals::$type {\n            type Interrupt = crate::interrupt::typelevel::$irq;\n        }\n    };\n}\n"
  },
  {
    "path": "embassy-nrf/src/power.rs",
    "content": "//! Power\n\n#[cfg(feature = \"nrf52840\")]\nuse crate::chip::pac::POWER;\n#[cfg(any(feature = \"nrf9160-s\", feature = \"nrf9160-ns\"))]\nuse crate::chip::pac::REGULATORS;\n\n/// Puts the MCU into \"System Off\" mode with minimal power usage\npub fn set_system_off() {\n    #[cfg(feature = \"nrf52840\")]\n    POWER.systemoff().write(|w| w.set_systemoff(true));\n    #[cfg(any(feature = \"nrf9160-s\", feature = \"nrf9160-ns\"))]\n    REGULATORS.systemoff().write(|w| w.set_systemoff(true));\n}\n"
  },
  {
    "path": "embassy-nrf/src/ppi/dppi.rs",
    "content": "use super::{Channel, ConfigurableChannel, Event, Ppi, Task};\nuse crate::Peri;\n\nconst DPPI_ENABLE_BIT: u32 = 0x8000_0000;\nconst DPPI_CHANNEL_MASK: u32 = 0x0000_00FF;\n\n#[cfg(not(feature = \"_nrf54l\"))]\npub(crate) fn regs() -> crate::pac::dppic::Dppic {\n    crate::pac::DPPIC\n}\n\nimpl<'d, C: ConfigurableChannel> Ppi<'d, C, 1, 1> {\n    /// Configure PPI channel to trigger `task` on `event`.\n    pub fn new_one_to_one(ch: Peri<'d, C>, event: Event<'d>, task: Task<'d>) -> Self {\n        Ppi::new_many_to_many(ch, [event], [task])\n    }\n}\n\nimpl<'d, C: ConfigurableChannel> Ppi<'d, C, 1, 2> {\n    /// Configure PPI channel to trigger both `task1` and `task2` on `event`.\n    pub fn new_one_to_two(ch: Peri<'d, C>, event: Event<'d>, task1: Task<'d>, task2: Task<'d>) -> Self {\n        Ppi::new_many_to_many(ch, [event], [task1, task2])\n    }\n}\n\nimpl<'d, C: ConfigurableChannel, const EVENT_COUNT: usize, const TASK_COUNT: usize>\n    Ppi<'d, C, EVENT_COUNT, TASK_COUNT>\n{\n    /// Configure a DPPI channel to trigger all `tasks` when any of the `events` fires.\n    pub fn new_many_to_many(ch: Peri<'d, C>, events: [Event<'d>; EVENT_COUNT], tasks: [Task<'d>; TASK_COUNT]) -> Self {\n        let val = DPPI_ENABLE_BIT | (ch.number() as u32 & DPPI_CHANNEL_MASK);\n        for task in tasks {\n            if unsafe { task.subscribe_reg().read_volatile() } != 0 {\n                panic!(\"Task is already in use\");\n            }\n            unsafe { task.subscribe_reg().write_volatile(val) }\n        }\n        for event in events {\n            if unsafe { event.publish_reg().read_volatile() } != 0 {\n                panic!(\"Event is already in use\");\n            }\n            unsafe { event.publish_reg().write_volatile(val) }\n        }\n\n        Self { ch, events, tasks }\n    }\n}\n\nimpl<'d, C: Channel, const EVENT_COUNT: usize, const TASK_COUNT: usize> Ppi<'d, C, EVENT_COUNT, TASK_COUNT> {\n    /// Enables the channel.\n    pub fn enable(&mut self) {\n        let n = self.ch.number();\n        self.ch.regs().chenset().write(|w| w.0 = 1 << n);\n    }\n\n    /// Disables the channel.\n    pub fn disable(&mut self) {\n        let n = self.ch.number();\n        self.ch.regs().chenclr().write(|w| w.0 = 1 << n);\n    }\n}\n\nimpl<C: Channel, const EVENT_COUNT: usize, const TASK_COUNT: usize> Ppi<'static, C, EVENT_COUNT, TASK_COUNT> {\n    /// Persist the channel's configuration for the rest of the program's lifetime. This method\n    /// should be preferred over [`core::mem::forget()`] because the `'static` bound prevents\n    /// accidental reuse of the underlying peripheral.\n    pub fn persist(self) {\n        core::mem::forget(self);\n    }\n}\n\nimpl<'d, C: Channel, const EVENT_COUNT: usize, const TASK_COUNT: usize> Drop for Ppi<'d, C, EVENT_COUNT, TASK_COUNT> {\n    fn drop(&mut self) {\n        self.disable();\n\n        for task in self.tasks {\n            unsafe { task.subscribe_reg().write_volatile(0) }\n        }\n        for event in self.events {\n            unsafe { event.publish_reg().write_volatile(0) }\n        }\n    }\n}\n"
  },
  {
    "path": "embassy-nrf/src/ppi/mod.rs",
    "content": "#![macro_use]\n\n//! Programmable Peripheral Interconnect (PPI/DPPI) driver.\n//!\n//! The (Distributed) Programmable Peripheral Interconnect interface allows for an autonomous interoperability\n//! between peripherals through their events and tasks. There are fixed PPI channels and fully\n//! configurable ones. Fixed channels can only connect specific events to specific tasks. For fully\n//! configurable channels, it is possible to choose, via software, the event and the task that it\n//! will triggered by the event.\n//!\n//! On nRF52 devices, there is also a fork task endpoint, where the user can configure one more task\n//! to be triggered by the same event, even fixed PPI channels have a configurable fork task.\n//!\n//! The DPPI for nRF53 and nRF91 devices works in a different way. Every channel can support infinitely\n//! many tasks and events, but any single task or event can only be coupled with one channel.\n//!\n\nuse core::marker::PhantomData;\nuse core::ptr::NonNull;\n\nuse embassy_hal_internal::{Peri, PeripheralType, impl_peripheral};\n\nuse crate::pac::common::{RW, Reg, W};\nuse crate::pac::{self};\n\n#[cfg_attr(feature = \"_dppi\", path = \"dppi.rs\")]\n#[cfg_attr(feature = \"_ppi\", path = \"ppi.rs\")]\nmod _version;\n\n#[allow(unused_imports)]\npub(crate) use _version::*;\n\n/// PPI channel driver.\npub struct Ppi<'d, C: Channel, const EVENT_COUNT: usize, const TASK_COUNT: usize> {\n    ch: Peri<'d, C>,\n    #[cfg(feature = \"_dppi\")]\n    events: [Event<'d>; EVENT_COUNT],\n    #[cfg(feature = \"_dppi\")]\n    tasks: [Task<'d>; TASK_COUNT],\n}\n\n/// PPI channel group driver.\npub struct PpiGroup<'d, G: Group> {\n    g: Peri<'d, G>,\n}\n\nimpl<'d, G: Group> PpiGroup<'d, G> {\n    /// Create a new PPI group driver.\n    ///\n    /// The group is initialized as containing no channels.\n    pub fn new(g: Peri<'d, G>) -> Self {\n        let r = g.regs();\n        let n = g.number();\n        r.chg(n).write(|_| ());\n\n        Self { g }\n    }\n\n    /// Add a PPI channel to this group.\n    ///\n    /// If the channel is already in the group, this is a no-op.\n    pub fn add_channel<C: Channel, const EVENT_COUNT: usize, const TASK_COUNT: usize>(\n        &mut self,\n        ch: &Ppi<'_, C, EVENT_COUNT, TASK_COUNT>,\n    ) {\n        let r = self.g.regs();\n        let ng = self.g.number();\n        let nc = ch.ch.number();\n        r.chg(ng).modify(|w| w.set_ch(nc, true));\n    }\n\n    /// Remove a PPI channel from this group.\n    ///\n    /// If the channel is already not in the group, this is a no-op.\n    pub fn remove_channel<C: Channel, const EVENT_COUNT: usize, const TASK_COUNT: usize>(\n        &mut self,\n        ch: &Ppi<'_, C, EVENT_COUNT, TASK_COUNT>,\n    ) {\n        let r = self.g.regs();\n        let ng = self.g.number();\n        let nc = ch.ch.number();\n        r.chg(ng).modify(|w| w.set_ch(nc, false));\n    }\n\n    /// Enable all the channels in this group.\n    pub fn enable_all(&mut self) {\n        let n = self.g.number();\n        self.g.regs().tasks_chg(n).en().write_value(1);\n    }\n\n    /// Disable all the channels in this group.\n    pub fn disable_all(&mut self) {\n        let n = self.g.number();\n        self.g.regs().tasks_chg(n).dis().write_value(1);\n    }\n\n    /// Get a reference to the \"enable all\" task.\n    ///\n    /// When triggered, it will enable all the channels in this group.\n    pub fn task_enable_all(&self) -> Task<'d> {\n        let n = self.g.number();\n        Task::from_reg(self.g.regs().tasks_chg(n).en())\n    }\n\n    /// Get a reference to the \"disable all\" task.\n    ///\n    /// When triggered, it will disable all the channels in this group.\n    pub fn task_disable_all(&self) -> Task<'d> {\n        let n = self.g.number();\n        Task::from_reg(self.g.regs().tasks_chg(n).dis())\n    }\n}\nimpl<G: Group> PpiGroup<'static, G> {\n    /// Persist this group's configuration for the rest of the program's lifetime. This method\n    /// should be preferred over [`core::mem::forget()`] because the `'static` bound prevents\n    /// accidental reuse of the underlying peripheral.\n    pub fn persist(self) {\n        core::mem::forget(self);\n    }\n}\n\nimpl<'d, G: Group> Drop for PpiGroup<'d, G> {\n    fn drop(&mut self) {\n        let r = self.g.regs();\n        let n = self.g.number();\n        r.chg(n).write(|_| ());\n    }\n}\n\n#[cfg(feature = \"_dppi\")]\nconst REGISTER_DPPI_CONFIG_OFFSET: usize = 0x80 / core::mem::size_of::<u32>();\n\n/// Represents a task that a peripheral can do.\n///\n/// When a task is subscribed to a PPI channel, it will run when the channel is triggered by\n/// a published event.\n#[derive(PartialEq, Eq, Clone, Copy)]\npub struct Task<'d>(NonNull<u32>, PhantomData<&'d ()>);\n\nimpl<'d> Task<'d> {\n    /// Create a new `Task` from a task register pointer\n    ///\n    /// # Safety\n    ///\n    /// `ptr` must be a pointer to a valid `TASKS_*` register from an nRF peripheral.\n    pub unsafe fn new_unchecked(ptr: NonNull<u32>) -> Self {\n        Self(ptr, PhantomData)\n    }\n\n    /// Triggers this task.\n    pub fn trigger(&mut self) {\n        unsafe { self.0.as_ptr().write_volatile(1) };\n    }\n\n    pub(crate) fn from_reg(reg: Reg<u32, W>) -> Self {\n        Self(unsafe { NonNull::new_unchecked(reg.as_ptr()) }, PhantomData)\n    }\n\n    /// Address of subscription register for this task.\n    #[cfg(feature = \"_dppi\")]\n    pub fn subscribe_reg(&self) -> *mut u32 {\n        unsafe { self.0.as_ptr().add(REGISTER_DPPI_CONFIG_OFFSET) }\n    }\n}\n\n/// # Safety\n///\n/// NonNull is not send, but this event is only allowed to point at registers and those exist in any context on the same core.\nunsafe impl Send for Task<'_> {}\n\n/// Represents an event that a peripheral can publish.\n///\n/// An event can be set to publish on a PPI channel when the event happens.\n#[derive(PartialEq, Eq, Clone, Copy)]\npub struct Event<'d>(NonNull<u32>, PhantomData<&'d ()>);\n\nimpl<'d> Event<'d> {\n    /// Create a new `Event` from an event register pointer\n    ///\n    /// # Safety\n    ///\n    /// `ptr` must be a pointer to a valid `EVENTS_*` register from an nRF peripheral.\n    pub unsafe fn new_unchecked(ptr: NonNull<u32>) -> Self {\n        Self(ptr, PhantomData)\n    }\n\n    pub(crate) fn from_reg(reg: Reg<u32, RW>) -> Self {\n        Self(unsafe { NonNull::new_unchecked(reg.as_ptr()) }, PhantomData)\n    }\n\n    /// Describes whether this Event is currently in a triggered state.\n    pub fn is_triggered(&self) -> bool {\n        unsafe { self.0.as_ptr().read_volatile() == 1 }\n    }\n\n    /// Clear the current register's triggered state, reverting it to 0.\n    pub fn clear(&mut self) {\n        unsafe { self.0.as_ptr().write_volatile(0) };\n    }\n\n    /// Address of publish register for this event.\n    #[cfg(feature = \"_dppi\")]\n    pub fn publish_reg(&self) -> *mut u32 {\n        unsafe { self.0.as_ptr().add(REGISTER_DPPI_CONFIG_OFFSET) }\n    }\n}\n\n/// # Safety\n///\n/// NonNull is not send, but this event is only allowed to point at registers and those exist in any context on the same core.\nunsafe impl Send for Event<'_> {}\n\n// ======================\n//       traits\n\npub(crate) trait SealedChannel {\n    #[cfg(feature = \"_dppi\")]\n    fn regs(&self) -> pac::dppic::Dppic;\n}\npub(crate) trait SealedGroup {\n    #[cfg(feature = \"_dppi\")]\n    fn regs(&self) -> pac::dppic::Dppic;\n    #[cfg(not(feature = \"_dppi\"))]\n    fn regs(&self) -> pac::ppi::Ppi;\n}\n\n/// Interface for PPI channels.\n#[allow(private_bounds)]\npub trait Channel: SealedChannel + PeripheralType + Sized + 'static {\n    /// Returns the number of the channel\n    fn number(&self) -> usize;\n}\n\n/// Interface for PPI channels that can be configured.\npub trait ConfigurableChannel: Channel + Into<AnyConfigurableChannel> {}\n\n/// Interface for PPI channels that cannot be configured.\npub trait StaticChannel: Channel + Into<AnyStaticChannel> {}\n\n/// Interface for a group of PPI channels.\n#[allow(private_bounds)]\npub trait Group: SealedGroup + PeripheralType + Into<AnyGroup> + Sized + 'static {\n    /// Returns the number of the group.\n    fn number(&self) -> usize;\n}\n\n// ======================\n//       channels\n\n/// The any channel can represent any static channel at runtime.\n/// This can be used to have fewer generic parameters in some places.\npub struct AnyStaticChannel {\n    pub(crate) number: u8,\n    #[cfg(feature = \"_dppi\")]\n    pub(crate) regs: pac::dppic::Dppic,\n}\nimpl_peripheral!(AnyStaticChannel);\nimpl SealedChannel for AnyStaticChannel {\n    #[cfg(feature = \"_dppi\")]\n    fn regs(&self) -> pac::dppic::Dppic {\n        self.regs\n    }\n}\nimpl Channel for AnyStaticChannel {\n    fn number(&self) -> usize {\n        self.number as usize\n    }\n}\nimpl StaticChannel for AnyStaticChannel {}\n\n/// The any configurable channel can represent any configurable channel at runtime.\n/// This can be used to have fewer generic parameters in some places.\npub struct AnyConfigurableChannel {\n    pub(crate) number: u8,\n    #[cfg(feature = \"_dppi\")]\n    pub(crate) regs: pac::dppic::Dppic,\n}\nimpl_peripheral!(AnyConfigurableChannel);\nimpl SealedChannel for AnyConfigurableChannel {\n    #[cfg(feature = \"_dppi\")]\n    fn regs(&self) -> pac::dppic::Dppic {\n        self.regs\n    }\n}\nimpl Channel for AnyConfigurableChannel {\n    fn number(&self) -> usize {\n        self.number as usize\n    }\n}\nimpl ConfigurableChannel for AnyConfigurableChannel {}\n\n#[cfg(not(feature = \"_nrf51\"))]\nmacro_rules! impl_ppi_channel {\n    ($type:ident, $inst:ident, $number:expr) => {\n        impl crate::ppi::SealedChannel for peripherals::$type {\n            #[cfg(feature = \"_dppi\")]\n            fn regs(&self) -> pac::dppic::Dppic {\n                pac::$inst\n            }\n        }\n        impl crate::ppi::Channel for peripherals::$type {\n            fn number(&self) -> usize {\n                $number\n            }\n        }\n    };\n    ($type:ident, $inst:ident, $number:expr => static) => {\n        impl_ppi_channel!($type, $inst, $number);\n        impl crate::ppi::StaticChannel for peripherals::$type {}\n        impl From<peripherals::$type> for crate::ppi::AnyStaticChannel {\n            fn from(val: peripherals::$type) -> Self {\n                Self {\n                    number: crate::ppi::Channel::number(&val) as u8,\n                    #[cfg(feature = \"_dppi\")]\n                    regs: pac::$inst,\n                }\n            }\n        }\n    };\n    ($type:ident, $inst:ident, $number:expr => configurable) => {\n        impl_ppi_channel!($type, $inst, $number);\n        impl crate::ppi::ConfigurableChannel for peripherals::$type {}\n        impl From<peripherals::$type> for crate::ppi::AnyConfigurableChannel {\n            fn from(val: peripherals::$type) -> Self {\n                Self {\n                    number: crate::ppi::Channel::number(&val) as u8,\n                    #[cfg(feature = \"_dppi\")]\n                    regs: pac::$inst,\n                }\n            }\n        }\n    };\n}\n\n// ======================\n//       groups\n\n/// A type erased PPI group.\npub struct AnyGroup {\n    pub(crate) number: u8,\n    #[cfg(feature = \"_dppi\")]\n    pub(crate) regs: pac::dppic::Dppic,\n    #[cfg(not(feature = \"_dppi\"))]\n    pub(crate) regs: pac::ppi::Ppi,\n}\nimpl_peripheral!(AnyGroup);\nimpl SealedGroup for AnyGroup {\n    #[cfg(feature = \"_dppi\")]\n    fn regs(&self) -> pac::dppic::Dppic {\n        self.regs\n    }\n    #[cfg(not(feature = \"_dppi\"))]\n    fn regs(&self) -> pac::ppi::Ppi {\n        self.regs\n    }\n}\nimpl Group for AnyGroup {\n    fn number(&self) -> usize {\n        self.number as usize\n    }\n}\n\nmacro_rules! impl_ppi_group {\n    ($type:ident, $inst:ident, $number:expr) => {\n        impl crate::ppi::SealedGroup for crate::peripherals::$type {\n            #[cfg(feature = \"_dppi\")]\n            fn regs(&self) -> pac::dppic::Dppic {\n                pac::$inst\n            }\n            #[cfg(not(feature = \"_dppi\"))]\n            fn regs(&self) -> pac::ppi::Ppi {\n                pac::$inst\n            }\n        }\n        impl crate::ppi::Group for crate::peripherals::$type {\n            fn number(&self) -> usize {\n                $number\n            }\n        }\n\n        impl From<crate::peripherals::$type> for crate::ppi::AnyGroup {\n            fn from(val: crate::peripherals::$type) -> Self {\n                Self {\n                    number: crate::ppi::Group::number(&val) as u8,\n                    regs: pac::$inst,\n                }\n            }\n        }\n    };\n}\n"
  },
  {
    "path": "embassy-nrf/src/ppi/ppi.rs",
    "content": "use super::{Channel, ConfigurableChannel, Event, Ppi, Task};\nuse crate::{Peri, pac};\n\nimpl<'d> Task<'d> {\n    fn reg_val(&self) -> u32 {\n        self.0.as_ptr() as _\n    }\n}\nimpl<'d> Event<'d> {\n    fn reg_val(&self) -> u32 {\n        self.0.as_ptr() as _\n    }\n}\n\npub(crate) fn regs() -> pac::ppi::Ppi {\n    pac::PPI\n}\n\n#[cfg(not(feature = \"_nrf51\"))] // Not for nrf51 because of the fork task\nimpl<'d, C: super::StaticChannel> Ppi<'d, C, 0, 1> {\n    /// Configure PPI channel to trigger `task`.\n    pub fn new_zero_to_one(ch: Peri<'d, C>, task: Task) -> Self {\n        let r = regs();\n        let n = ch.number();\n        r.fork(n).tep().write_value(task.reg_val());\n\n        Self { ch }\n    }\n}\n\nimpl<'d, C: ConfigurableChannel> Ppi<'d, C, 1, 1> {\n    /// Configure PPI channel to trigger `task` on `event`.\n    pub fn new_one_to_one(ch: Peri<'d, C>, event: Event<'d>, task: Task<'d>) -> Self {\n        let r = regs();\n        let n = ch.number();\n        r.ch(n).eep().write_value(event.reg_val());\n        r.ch(n).tep().write_value(task.reg_val());\n\n        Self { ch }\n    }\n}\n\n#[cfg(not(feature = \"_nrf51\"))] // Not for nrf51 because of the fork task\nimpl<'d, C: ConfigurableChannel> Ppi<'d, C, 1, 2> {\n    /// Configure PPI channel to trigger both `task1` and `task2` on `event`.\n    pub fn new_one_to_two(ch: Peri<'d, C>, event: Event<'d>, task1: Task<'d>, task2: Task<'d>) -> Self {\n        let r = regs();\n        let n = ch.number();\n        r.ch(n).eep().write_value(event.reg_val());\n        r.ch(n).tep().write_value(task1.reg_val());\n        r.fork(n).tep().write_value(task2.reg_val());\n\n        Self { ch }\n    }\n}\n\nimpl<'d, C: Channel, const EVENT_COUNT: usize, const TASK_COUNT: usize> Ppi<'d, C, EVENT_COUNT, TASK_COUNT> {\n    /// Enables the channel.\n    pub fn enable(&mut self) {\n        let n = self.ch.number();\n        regs().chenset().write(|w| w.set_ch(n, true));\n    }\n\n    /// Disables the channel.\n    pub fn disable(&mut self) {\n        let n = self.ch.number();\n        regs().chenclr().write(|w| w.set_ch(n, true));\n    }\n}\n\nimpl<C: Channel, const EVENT_COUNT: usize, const TASK_COUNT: usize> Ppi<'static, C, EVENT_COUNT, TASK_COUNT> {\n    /// Persist the channel's configuration for the rest of the program's lifetime. This method\n    /// should be preferred over [`core::mem::forget()`] because the `'static` bound prevents\n    /// accidental reuse of the underlying peripheral.\n    pub fn persist(self) {\n        core::mem::forget(self);\n    }\n}\n\nimpl<'d, C: Channel, const EVENT_COUNT: usize, const TASK_COUNT: usize> Drop for Ppi<'d, C, EVENT_COUNT, TASK_COUNT> {\n    fn drop(&mut self) {\n        self.disable();\n\n        let r = regs();\n        let n = self.ch.number();\n        r.ch(n).eep().write_value(0);\n        r.ch(n).tep().write_value(0);\n        #[cfg(not(feature = \"_nrf51\"))]\n        r.fork(n).tep().write_value(0);\n    }\n}\n"
  },
  {
    "path": "embassy-nrf/src/pwm.rs",
    "content": "//! Pulse Width Modulation (PWM) driver.\n\n#![macro_use]\n\nuse core::sync::atomic::{Ordering, compiler_fence};\n\nuse embassy_hal_internal::{Peri, PeripheralType};\n\nuse crate::gpio::{AnyPin, DISCONNECTED, Level, OutputDrive, Pin as GpioPin, PselBits, SealedPin as _, convert_drive};\nuse crate::pac::gpio::vals as gpiovals;\nuse crate::pac::pwm::vals;\nuse crate::ppi::{Event, Task};\nuse crate::util::slice_in_ram_or;\nuse crate::{interrupt, pac};\n\n/// SimplePwm is the traditional pwm interface you're probably used to, allowing\n/// to simply set a duty cycle across up to four channels.\npub struct SimplePwm<'d> {\n    r: pac::pwm::Pwm,\n    duty: [DutyCycle; 4],\n    ch0: Option<Peri<'d, AnyPin>>,\n    ch1: Option<Peri<'d, AnyPin>>,\n    ch2: Option<Peri<'d, AnyPin>>,\n    ch3: Option<Peri<'d, AnyPin>>,\n}\n\n/// SequencePwm allows you to offload the updating of a sequence of duty cycles\n/// to up to four channels, as well as repeat that sequence n times.\npub struct SequencePwm<'d> {\n    r: pac::pwm::Pwm,\n    ch0: Option<Peri<'d, AnyPin>>,\n    ch1: Option<Peri<'d, AnyPin>>,\n    ch2: Option<Peri<'d, AnyPin>>,\n    ch3: Option<Peri<'d, AnyPin>>,\n}\n\n/// PWM error\n#[derive(Debug, Clone, Copy, PartialEq, Eq)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\n#[non_exhaustive]\npub enum Error {\n    /// Max Sequence size is 32767\n    SequenceTooLong,\n    /// Min Sequence count is 1\n    SequenceTimesAtLeastOne,\n    /// EasyDMA can only read from data memory, read only buffers in flash will fail.\n    BufferNotInRAM,\n}\n\nconst MAX_SEQUENCE_LEN: usize = 32767;\n/// The used pwm clock frequency\npub const PWM_CLK_HZ: u32 = 16_000_000;\n\nimpl<'d> SequencePwm<'d> {\n    /// Create a new 1-channel PWM\n    pub fn new_1ch<T: Instance>(pwm: Peri<'d, T>, ch0: Peri<'d, impl GpioPin>, config: Config) -> Result<Self, Error> {\n        Self::new_inner(pwm, Some(ch0.into()), None, None, None, config)\n    }\n\n    /// Create a new 2-channel PWM\n    pub fn new_2ch<T: Instance>(\n        pwm: Peri<'d, T>,\n        ch0: Peri<'d, impl GpioPin>,\n        ch1: Peri<'d, impl GpioPin>,\n        config: Config,\n    ) -> Result<Self, Error> {\n        Self::new_inner(pwm, Some(ch0.into()), Some(ch1.into()), None, None, config)\n    }\n\n    /// Create a new 3-channel PWM\n    pub fn new_3ch<T: Instance>(\n        pwm: Peri<'d, T>,\n        ch0: Peri<'d, impl GpioPin>,\n        ch1: Peri<'d, impl GpioPin>,\n        ch2: Peri<'d, impl GpioPin>,\n        config: Config,\n    ) -> Result<Self, Error> {\n        Self::new_inner(pwm, Some(ch0.into()), Some(ch1.into()), Some(ch2.into()), None, config)\n    }\n\n    /// Create a new 4-channel PWM\n    pub fn new_4ch<T: Instance>(\n        pwm: Peri<'d, T>,\n        ch0: Peri<'d, impl GpioPin>,\n        ch1: Peri<'d, impl GpioPin>,\n        ch2: Peri<'d, impl GpioPin>,\n        ch3: Peri<'d, impl GpioPin>,\n        config: Config,\n    ) -> Result<Self, Error> {\n        Self::new_inner(\n            pwm,\n            Some(ch0.into()),\n            Some(ch1.into()),\n            Some(ch2.into()),\n            Some(ch3.into()),\n            config,\n        )\n    }\n\n    fn new_inner<T: Instance>(\n        _pwm: Peri<'d, T>,\n        ch0: Option<Peri<'d, AnyPin>>,\n        ch1: Option<Peri<'d, AnyPin>>,\n        ch2: Option<Peri<'d, AnyPin>>,\n        ch3: Option<Peri<'d, AnyPin>>,\n        config: Config,\n    ) -> Result<Self, Error> {\n        let r = T::regs();\n\n        let channels = [\n            (&ch0, config.ch0_drive, config.ch0_idle_level),\n            (&ch1, config.ch1_drive, config.ch1_idle_level),\n            (&ch2, config.ch2_drive, config.ch2_idle_level),\n            (&ch3, config.ch3_drive, config.ch3_idle_level),\n        ];\n        for (i, (pin, drive, idle_level)) in channels.into_iter().enumerate() {\n            if let Some(pin) = pin {\n                match idle_level {\n                    Level::Low => pin.set_low(),\n                    Level::High => pin.set_high(),\n                }\n                pin.conf().write(|w| {\n                    w.set_dir(gpiovals::Dir::OUTPUT);\n                    w.set_input(gpiovals::Input::DISCONNECT);\n                    convert_drive(w, drive);\n                });\n            }\n            r.psel().out(i).write_value(pin.psel_bits());\n        }\n\n        // Disable all interrupts\n        r.intenclr().write(|w| w.0 = 0xFFFF_FFFF);\n        r.shorts().write(|_| ());\n        r.events_stopped().write_value(0);\n        r.events_loopsdone().write_value(0);\n        r.events_seqend(0).write_value(0);\n        r.events_seqend(1).write_value(0);\n        r.events_pwmperiodend().write_value(0);\n        r.events_seqstarted(0).write_value(0);\n        r.events_seqstarted(1).write_value(0);\n\n        r.decoder().write(|w| {\n            w.set_load(vals::Load::from_bits(config.sequence_load as u8));\n            w.set_mode(vals::Mode::REFRESH_COUNT);\n        });\n\n        r.mode().write(|w| match config.counter_mode {\n            CounterMode::UpAndDown => w.set_updown(vals::Updown::UP_AND_DOWN),\n            CounterMode::Up => w.set_updown(vals::Updown::UP),\n        });\n        r.prescaler()\n            .write(|w| w.set_prescaler(vals::Prescaler::from_bits(config.prescaler as u8)));\n        r.countertop().write(|w| w.set_countertop(config.max_duty));\n\n        Ok(Self { r, ch0, ch1, ch2, ch3 })\n    }\n\n    /// Returns reference to `Stopped` event endpoint for PPI.\n    #[inline(always)]\n    pub fn event_stopped(&self) -> Event<'d> {\n        Event::from_reg(self.r.events_stopped())\n    }\n\n    /// Returns reference to `LoopsDone` event endpoint for PPI.\n    #[inline(always)]\n    pub fn event_loops_done(&self) -> Event<'d> {\n        Event::from_reg(self.r.events_loopsdone())\n    }\n\n    /// Returns reference to `PwmPeriodEnd` event endpoint for PPI.\n    #[inline(always)]\n    pub fn event_pwm_period_end(&self) -> Event<'d> {\n        Event::from_reg(self.r.events_pwmperiodend())\n    }\n\n    /// Returns reference to `Seq0 End` event endpoint for PPI.\n    #[inline(always)]\n    pub fn event_seq_end(&self) -> Event<'d> {\n        Event::from_reg(self.r.events_seqend(0))\n    }\n\n    /// Returns reference to `Seq1 End` event endpoint for PPI.\n    #[inline(always)]\n    pub fn event_seq1_end(&self) -> Event<'d> {\n        Event::from_reg(self.r.events_seqend(1))\n    }\n\n    /// Returns reference to `Seq0 Started` event endpoint for PPI.\n    #[inline(always)]\n    pub fn event_seq0_started(&self) -> Event<'d> {\n        Event::from_reg(self.r.events_seqstarted(0))\n    }\n\n    /// Returns reference to `Seq1 Started` event endpoint for PPI.\n    #[inline(always)]\n    pub fn event_seq1_started(&self) -> Event<'d> {\n        Event::from_reg(self.r.events_seqstarted(1))\n    }\n\n    /// Returns reference to `Seq0 Start` task endpoint for PPI.\n    /// # Safety\n    ///\n    /// Interacting with the sequence while it runs puts it in an unknown state\n    #[inline(always)]\n    pub unsafe fn task_start_seq0(&self) -> Task<'d> {\n        Task::from_reg(self.r.tasks_dma().seq(0).start())\n    }\n\n    /// Returns reference to `Seq1 Started` task endpoint for PPI.\n    /// # Safety\n    ///\n    /// Interacting with the sequence while it runs puts it in an unknown state\n    #[inline(always)]\n    pub unsafe fn task_start_seq1(&self) -> Task<'d> {\n        Task::from_reg(self.r.tasks_dma().seq(1).start())\n    }\n\n    /// Returns reference to `NextStep` task endpoint for PPI.\n    /// # Safety\n    ///\n    /// Interacting with the sequence while it runs puts it in an unknown state\n    #[inline(always)]\n    pub unsafe fn task_next_step(&self) -> Task<'d> {\n        Task::from_reg(self.r.tasks_nextstep())\n    }\n\n    /// Returns reference to `Stop` task endpoint for PPI.\n    /// # Safety\n    ///\n    /// Interacting with the sequence while it runs puts it in an unknown state\n    #[inline(always)]\n    pub unsafe fn task_stop(&self) -> Task<'d> {\n        Task::from_reg(self.r.tasks_stop())\n    }\n}\n\nimpl<'a> Drop for SequencePwm<'a> {\n    fn drop(&mut self) {\n        if let Some(pin) = &self.ch0 {\n            pin.set_low();\n            pin.conf().write(|_| ());\n            self.r.psel().out(0).write_value(DISCONNECTED);\n        }\n        if let Some(pin) = &self.ch1 {\n            pin.set_low();\n            pin.conf().write(|_| ());\n            self.r.psel().out(1).write_value(DISCONNECTED);\n        }\n        if let Some(pin) = &self.ch2 {\n            pin.set_low();\n            pin.conf().write(|_| ());\n            self.r.psel().out(2).write_value(DISCONNECTED);\n        }\n        if let Some(pin) = &self.ch3 {\n            pin.set_low();\n            pin.conf().write(|_| ());\n            self.r.psel().out(3).write_value(DISCONNECTED);\n        }\n\n        self.r.enable().write(|w| w.set_enable(false));\n    }\n}\n\n/// Configuration for the PWM as a whole.\n#[derive(Debug, Clone)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\n#[non_exhaustive]\npub struct Config {\n    /// Selects up mode or up-and-down mode for the counter\n    pub counter_mode: CounterMode,\n    /// Top value to be compared against buffer values\n    pub max_duty: u16,\n    /// Configuration for PWM_CLK\n    pub prescaler: Prescaler,\n    /// How a sequence is read from RAM and is spread to the compare register\n    pub sequence_load: SequenceLoad,\n    /// Drive strength for the channel 0 line.\n    pub ch0_drive: OutputDrive,\n    /// Drive strength for the channel 1 line.\n    pub ch1_drive: OutputDrive,\n    /// Drive strength for the channel 2 line.\n    pub ch2_drive: OutputDrive,\n    /// Drive strength for the channel 3 line.\n    pub ch3_drive: OutputDrive,\n    /// Output level for the channel 0 line when PWM if disabled.\n    pub ch0_idle_level: Level,\n    /// Output level for the channel 1 line when PWM if disabled.\n    pub ch1_idle_level: Level,\n    /// Output level for the channel 2 line when PWM if disabled.\n    pub ch2_idle_level: Level,\n    /// Output level for the channel 3 line when PWM if disabled.\n    pub ch3_idle_level: Level,\n}\n\nimpl Default for Config {\n    fn default() -> Self {\n        Self {\n            counter_mode: CounterMode::Up,\n            max_duty: 1000,\n            prescaler: Prescaler::Div16,\n            sequence_load: SequenceLoad::Common,\n            ch0_drive: OutputDrive::Standard,\n            ch1_drive: OutputDrive::Standard,\n            ch2_drive: OutputDrive::Standard,\n            ch3_drive: OutputDrive::Standard,\n            ch0_idle_level: Level::Low,\n            ch1_idle_level: Level::Low,\n            ch2_idle_level: Level::Low,\n            ch3_idle_level: Level::Low,\n        }\n    }\n}\n\n/// Configuration for the simple PWM driver.\n#[derive(Debug, Clone)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\n#[non_exhaustive]\npub struct SimpleConfig {\n    /// Selects up mode or up-and-down mode for the counter\n    pub counter_mode: CounterMode,\n    /// Top value to be compared against buffer values\n    pub max_duty: u16,\n    /// Configuration for PWM_CLK\n    pub prescaler: Prescaler,\n    /// Drive strength for the channel 0 line.\n    pub ch0_drive: OutputDrive,\n    /// Drive strength for the channel 1 line.\n    pub ch1_drive: OutputDrive,\n    /// Drive strength for the channel 2 line.\n    pub ch2_drive: OutputDrive,\n    /// Drive strength for the channel 3 line.\n    pub ch3_drive: OutputDrive,\n    /// Output level for the channel 0 line when PWM if disabled.\n    pub ch0_idle_level: Level,\n    /// Output level for the channel 1 line when PWM if disabled.\n    pub ch1_idle_level: Level,\n    /// Output level for the channel 2 line when PWM if disabled.\n    pub ch2_idle_level: Level,\n    /// Output level for the channel 3 line when PWM if disabled.\n    pub ch3_idle_level: Level,\n}\n\nimpl Default for SimpleConfig {\n    fn default() -> Self {\n        Self {\n            counter_mode: CounterMode::Up,\n            max_duty: 1000,\n            prescaler: Prescaler::Div16,\n            ch0_drive: OutputDrive::Standard,\n            ch1_drive: OutputDrive::Standard,\n            ch2_drive: OutputDrive::Standard,\n            ch3_drive: OutputDrive::Standard,\n            ch0_idle_level: Level::Low,\n            ch1_idle_level: Level::Low,\n            ch2_idle_level: Level::Low,\n            ch3_idle_level: Level::Low,\n        }\n    }\n}\n\n/// Configuration per sequence\n#[non_exhaustive]\n#[derive(Debug, Clone)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub struct SequenceConfig {\n    /// Number of PWM periods to delay between each sequence sample\n    pub refresh: u32,\n    /// Number of PWM periods after the sequence ends before starting the next sequence\n    pub end_delay: u32,\n}\n\nimpl Default for SequenceConfig {\n    fn default() -> SequenceConfig {\n        SequenceConfig {\n            refresh: 0,\n            end_delay: 0,\n        }\n    }\n}\n\n/// A composition of a sequence buffer and its configuration.\n#[non_exhaustive]\n#[derive(Debug, Clone)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub struct Sequence<'s> {\n    /// The words comprising the sequence. Must not exceed 32767 words.\n    pub words: &'s [u16],\n    /// Configuration associated with the sequence.\n    pub config: SequenceConfig,\n}\n\nimpl<'s> Sequence<'s> {\n    /// Create a new `Sequence`\n    pub fn new(words: &'s [u16], config: SequenceConfig) -> Self {\n        Self { words, config }\n    }\n}\n\n/// A single sequence that can be started and stopped.\n/// Takes one sequence along with its configuration.\n#[non_exhaustive]\npub struct SingleSequencer<'d, 's> {\n    sequencer: Sequencer<'d, 's>,\n}\n\nimpl<'d, 's> SingleSequencer<'d, 's> {\n    /// Create a new sequencer\n    pub fn new(pwm: &'s mut SequencePwm<'d>, words: &'s [u16], config: SequenceConfig) -> Self {\n        Self {\n            sequencer: Sequencer::new(pwm, Sequence::new(words, config), None),\n        }\n    }\n\n    /// Start or restart playback.\n    #[inline(always)]\n    pub fn start(&self, times: SingleSequenceMode) -> Result<(), Error> {\n        let (start_seq, times) = match times {\n            SingleSequenceMode::Times(n) if n == 1 => (StartSequence::One, SequenceMode::Loop(1)),\n            SingleSequenceMode::Times(n) if n & 1 == 1 => (StartSequence::One, SequenceMode::Loop((n / 2) + 1)),\n            SingleSequenceMode::Times(n) => (StartSequence::Zero, SequenceMode::Loop(n / 2)),\n            SingleSequenceMode::Infinite => (StartSequence::Zero, SequenceMode::Infinite),\n        };\n        self.sequencer.start(start_seq, times)\n    }\n\n    /// Stop playback. Disables the peripheral. Does NOT clear the last duty\n    /// cycle from the pin. Returns any sequences previously provided to\n    /// `start` so that they may be further mutated.\n    #[inline(always)]\n    pub fn stop(&self) {\n        self.sequencer.stop();\n    }\n}\n\n/// A composition of sequences that can be started and stopped.\n/// Takes at least one sequence along with its configuration.\n/// Optionally takes a second sequence and its configuration.\n/// In the case where no second sequence is provided then the first sequence\n/// is used.\n#[non_exhaustive]\npub struct Sequencer<'d, 's> {\n    _pwm: &'s mut SequencePwm<'d>,\n    sequence0: Sequence<'s>,\n    sequence1: Option<Sequence<'s>>,\n}\n\n#[cfg(feature = \"_nrf54l\")]\nfn pwmseq(r: pac::pwm::Pwm, n: usize) -> pac::pwm::PwmSeq {\n    r.seq(n)\n}\n\n#[cfg(not(feature = \"_nrf54l\"))]\nfn pwmseq(r: pac::pwm::Pwm, n: usize) -> pac::pwm::DmaSeq {\n    r.dma().seq(n)\n}\n\n#[cfg(feature = \"_nrf54l\")]\nconst CNT_UNIT: u32 = 2;\n#[cfg(not(feature = \"_nrf54l\"))]\nconst CNT_UNIT: u32 = 1;\n\nimpl<'d, 's> Sequencer<'d, 's> {\n    /// Create a new double sequence. In the absence of sequence 1, sequence 0\n    /// will be used twice in the one loop.\n    pub fn new(pwm: &'s mut SequencePwm<'d>, sequence0: Sequence<'s>, sequence1: Option<Sequence<'s>>) -> Self {\n        Sequencer {\n            _pwm: pwm,\n            sequence0,\n            sequence1,\n        }\n    }\n\n    /// Start or restart playback. The sequence mode applies to both sequences combined as one.\n    #[inline(always)]\n    pub fn start(&self, start_seq: StartSequence, times: SequenceMode) -> Result<(), Error> {\n        let sequence0 = &self.sequence0;\n        let alt_sequence = self.sequence1.as_ref().unwrap_or(&self.sequence0);\n\n        slice_in_ram_or(sequence0.words, Error::BufferNotInRAM)?;\n        slice_in_ram_or(alt_sequence.words, Error::BufferNotInRAM)?;\n\n        if sequence0.words.len() > MAX_SEQUENCE_LEN || alt_sequence.words.len() > MAX_SEQUENCE_LEN {\n            return Err(Error::SequenceTooLong);\n        }\n\n        if let SequenceMode::Loop(0) = times {\n            return Err(Error::SequenceTimesAtLeastOne);\n        }\n\n        self.stop();\n\n        let r = self._pwm.r;\n\n        pwmseq(r, 0).refresh().write(|w| w.0 = sequence0.config.refresh);\n        pwmseq(r, 0).enddelay().write(|w| w.0 = sequence0.config.end_delay);\n        r.dma().seq(0).ptr().write_value(sequence0.words.as_ptr() as u32);\n        r.dma()\n            .seq(0)\n            .maxcnt()\n            .write(|w| w.0 = sequence0.words.len() as u32 * CNT_UNIT);\n\n        pwmseq(r, 1).refresh().write(|w| w.0 = alt_sequence.config.refresh);\n        pwmseq(r, 1).enddelay().write(|w| w.0 = alt_sequence.config.end_delay);\n        r.dma().seq(1).ptr().write_value(alt_sequence.words.as_ptr() as u32);\n        r.dma()\n            .seq(1)\n            .maxcnt()\n            .write(|w| w.0 = alt_sequence.words.len() as u32 * CNT_UNIT);\n\n        r.enable().write(|w| w.set_enable(true));\n\n        // defensive before seqstart\n        compiler_fence(Ordering::SeqCst);\n\n        let seqstart_index = if start_seq == StartSequence::One { 1 } else { 0 };\n\n        match times {\n            SequenceMode::Loop(n) => {\n                r.loop_().write(|w| w.set_cnt(vals::LoopCnt::from_bits(n)));\n            }\n            // to play infinitely, repeat the sequence one time, then have loops done self trigger seq0 again\n            SequenceMode::Infinite => {\n                r.loop_().write(|w| w.set_cnt(vals::LoopCnt::from_bits(1)));\n                r.shorts().write(|w| w.set_loopsdone_dma_seq0_start(true));\n            }\n        }\n\n        r.tasks_dma().seq(seqstart_index).start().write_value(1);\n\n        Ok(())\n    }\n\n    /// Stop playback. Disables the peripheral. Does NOT clear the last duty\n    /// cycle from the pin. Returns any sequences previously provided to\n    /// `start` so that they may be further mutated.\n    #[inline(always)]\n    pub fn stop(&self) {\n        let r = self._pwm.r;\n\n        r.shorts().write(|_| ());\n\n        compiler_fence(Ordering::SeqCst);\n\n        r.tasks_stop().write_value(1);\n        r.enable().write(|w| w.set_enable(false));\n    }\n}\n\nimpl<'d, 's> Drop for Sequencer<'d, 's> {\n    fn drop(&mut self) {\n        self.stop();\n    }\n}\n\n/// How many times to run a single sequence\n#[derive(Debug, Eq, PartialEq, Clone, Copy)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub enum SingleSequenceMode {\n    /// Run a single sequence n Times total.\n    Times(u16),\n    /// Repeat until `stop` is called.\n    Infinite,\n}\n\n/// Which sequence to start a loop with\n#[derive(Debug, Eq, PartialEq, Clone, Copy)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub enum StartSequence {\n    /// Start with Sequence 0\n    Zero,\n    /// Start with Sequence 1\n    One,\n}\n\n/// How many loops to run two sequences\n#[derive(Debug, Eq, PartialEq, Clone, Copy)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub enum SequenceMode {\n    /// Run two sequences n loops i.e. (n * (seq0 + seq1.unwrap_or(seq0)))\n    Loop(u16),\n    /// Repeat until `stop` is called.\n    Infinite,\n}\n\n/// PWM Base clock is system clock (16MHz) divided by prescaler\n#[derive(Debug, Eq, PartialEq, Clone, Copy)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub enum Prescaler {\n    /// Divide by 1\n    Div1,\n    /// Divide by 2\n    Div2,\n    /// Divide by 4\n    Div4,\n    /// Divide by 8\n    Div8,\n    /// Divide by 16\n    Div16,\n    /// Divide by 32\n    Div32,\n    /// Divide by 64\n    Div64,\n    /// Divide by 128\n    Div128,\n}\n\n/// How the sequence values are distributed across the channels\n#[derive(Debug, Eq, PartialEq, Clone, Copy)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub enum SequenceLoad {\n    /// Provided sequence will be used across all channels\n    Common,\n    /// Provided sequence contains grouped values for each channel ex:\n    /// [ch0_0_and_ch1_0, ch2_0_and_ch3_0, ... ch0_n_and_ch1_n, ch2_n_and_ch3_n]\n    Grouped,\n    /// Provided sequence contains individual values for each channel ex:\n    /// [ch0_0, ch1_0, ch2_0, ch3_0... ch0_n, ch1_n, ch2_n, ch3_n]\n    Individual,\n    /// Similar to Individual mode, but only three channels are used. The fourth\n    /// value is loaded into the pulse generator counter as its top value.\n    Waveform,\n}\n\n/// Selects up mode or up-and-down mode for the counter\n#[derive(Debug, Eq, PartialEq, Clone, Copy)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub enum CounterMode {\n    /// Up counter (edge-aligned PWM duty cycle)\n    Up,\n    /// Up and down counter (center-aligned PWM duty cycle)\n    UpAndDown,\n}\n\n/// Duty value and polarity for a single channel.\n///\n/// If the channel has inverted polarity, the output is set high as long as the counter is below the duty value.\n#[repr(transparent)]\n#[derive(Eq, PartialEq, Clone, Copy)]\npub struct DutyCycle {\n    /// The raw duty cycle valuea.\n    ///\n    /// This has the duty cycle in the lower 15 bits.\n    /// The highest bit indicates that the duty cycle has inverted polarity.\n    raw: u16,\n}\n\nimpl DutyCycle {\n    /// Make a new duty value with normal polarity.\n    ///\n    /// The value is truncated to 15 bits.\n    ///\n    /// The output is set high if the counter is at or above the duty value.\n    pub const fn normal(value: u16) -> Self {\n        let raw = value & 0x7FFF;\n        Self { raw }\n    }\n\n    /// Make a new duty cycle with inverted polarity.\n    ///\n    /// The value is truncated to 15 bits.\n    ///\n    /// The output is set high if the counter is below the duty value.\n    pub const fn inverted(value: u16) -> Self {\n        let raw = value | 0x8000;\n        Self { raw }\n    }\n\n    /// Adjust the polarity of the duty cycle (returns a new object).\n    #[must_use = \"this function return a new object, it does not modify self\"]\n    pub const fn with_inverted(self, inverted_polarity: bool) -> Self {\n        if inverted_polarity {\n            Self::inverted(self.value())\n        } else {\n            Self::normal(self.value())\n        }\n    }\n\n    /// Gets the 15-bit value of the duty cycle.\n    pub const fn value(&self) -> u16 {\n        self.raw & 0x7FFF\n    }\n\n    /// Checks if the duty period has inverted polarity.\n    ///\n    /// If the channel has inverted polarity, the output is set high as long as the counter is below the duty value.\n    pub const fn is_inverted(&self) -> bool {\n        self.raw & 0x8000 != 0\n    }\n}\n\nimpl core::fmt::Debug for DutyCycle {\n    fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {\n        f.debug_struct(\"DutyCycle\")\n            .field(\"value\", &self.value())\n            .field(\"inverted\", &self.is_inverted())\n            .finish()\n    }\n}\n\n#[cfg(feature = \"defmt\")]\nimpl defmt::Format for DutyCycle {\n    fn format(&self, f: defmt::Formatter) {\n        defmt::write!(\n            f,\n            \"DutyCycle {{ value: {=u16}, inverted: {=bool} }}\",\n            self.value(),\n            self.is_inverted(),\n        );\n    }\n}\n\nimpl<'d> SimplePwm<'d> {\n    /// Create a new 1-channel PWM\n    pub fn new_1ch<T: Instance>(pwm: Peri<'d, T>, ch0: Peri<'d, impl GpioPin>, config: &SimpleConfig) -> Self {\n        Self::new_inner(pwm, Some(ch0.into()), None, None, None, config)\n    }\n\n    /// Create a new 2-channel PWM\n    pub fn new_2ch<T: Instance>(\n        pwm: Peri<'d, T>,\n        ch0: Peri<'d, impl GpioPin>,\n        ch1: Peri<'d, impl GpioPin>,\n        config: &SimpleConfig,\n    ) -> Self {\n        Self::new_inner(pwm, Some(ch0.into()), Some(ch1.into()), None, None, config)\n    }\n\n    /// Create a new 3-channel PWM\n    pub fn new_3ch<T: Instance>(\n        pwm: Peri<'d, T>,\n        ch0: Peri<'d, impl GpioPin>,\n        ch1: Peri<'d, impl GpioPin>,\n        ch2: Peri<'d, impl GpioPin>,\n        config: &SimpleConfig,\n    ) -> Self {\n        Self::new_inner(pwm, Some(ch0.into()), Some(ch1.into()), Some(ch2.into()), None, config)\n    }\n\n    /// Create a new 4-channel PWM\n    pub fn new_4ch<T: Instance>(\n        pwm: Peri<'d, T>,\n        ch0: Peri<'d, impl GpioPin>,\n        ch1: Peri<'d, impl GpioPin>,\n        ch2: Peri<'d, impl GpioPin>,\n        ch3: Peri<'d, impl GpioPin>,\n        config: &SimpleConfig,\n    ) -> Self {\n        Self::new_inner(\n            pwm,\n            Some(ch0.into()),\n            Some(ch1.into()),\n            Some(ch2.into()),\n            Some(ch3.into()),\n            config,\n        )\n    }\n\n    fn new_inner<T: Instance>(\n        _pwm: Peri<'d, T>,\n        ch0: Option<Peri<'d, AnyPin>>,\n        ch1: Option<Peri<'d, AnyPin>>,\n        ch2: Option<Peri<'d, AnyPin>>,\n        ch3: Option<Peri<'d, AnyPin>>,\n        config: &SimpleConfig,\n    ) -> Self {\n        let r = T::regs();\n\n        let channels = [\n            (&ch0, config.ch0_drive, config.ch0_idle_level),\n            (&ch1, config.ch1_drive, config.ch1_idle_level),\n            (&ch2, config.ch2_drive, config.ch2_idle_level),\n            (&ch3, config.ch3_drive, config.ch3_idle_level),\n        ];\n        for (i, (pin, drive, idle_level)) in channels.into_iter().enumerate() {\n            if let Some(pin) = pin {\n                match idle_level {\n                    Level::Low => pin.set_low(),\n                    Level::High => pin.set_high(),\n                }\n                pin.conf().write(|w| {\n                    w.set_dir(gpiovals::Dir::OUTPUT);\n                    w.set_input(gpiovals::Input::DISCONNECT);\n                    convert_drive(w, drive);\n                });\n            }\n            r.psel().out(i).write_value(pin.psel_bits());\n        }\n\n        let pwm = Self {\n            r,\n            ch0,\n            ch1,\n            ch2,\n            ch3,\n            duty: [const { DutyCycle::normal(0) }; 4],\n        };\n\n        // Disable all interrupts\n        r.intenclr().write(|w| w.0 = 0xFFFF_FFFF);\n        r.shorts().write(|_| ());\n\n        // Enable\n        r.enable().write(|w| w.set_enable(true));\n\n        r.dma().seq(0).ptr().write_value((pwm.duty).as_ptr() as u32);\n        r.dma().seq(0).maxcnt().write(|w| w.0 = 4 * CNT_UNIT);\n        pwmseq(r, 0).refresh().write(|w| w.0 = 0);\n        pwmseq(r, 0).enddelay().write(|w| w.0 = 0);\n\n        r.decoder().write(|w| {\n            w.set_load(vals::Load::INDIVIDUAL);\n            w.set_mode(vals::Mode::REFRESH_COUNT);\n        });\n        r.mode().write(|w| match config.counter_mode {\n            CounterMode::UpAndDown => w.set_updown(vals::Updown::UP_AND_DOWN),\n            CounterMode::Up => w.set_updown(vals::Updown::UP),\n        });\n        r.prescaler()\n            .write(|w| w.set_prescaler(vals::Prescaler::from_bits(config.prescaler as u8)));\n        r.countertop().write(|w| w.set_countertop(config.max_duty));\n        r.loop_().write(|w| w.set_cnt(vals::LoopCnt::DISABLED));\n\n        pwm\n    }\n\n    /// Returns the enable state of the pwm counter\n    #[inline(always)]\n    pub fn is_enabled(&self) -> bool {\n        self.r.enable().read().enable()\n    }\n\n    /// Enables the PWM generator.\n    #[inline(always)]\n    pub fn enable(&self) {\n        self.r.enable().write(|w| w.set_enable(true));\n    }\n\n    /// Disables the PWM generator. Does NOT clear the last duty cycle from the pin.\n    #[inline(always)]\n    pub fn disable(&self) {\n        self.r.enable().write(|w| w.set_enable(false));\n    }\n\n    /// Returns the current duty of the channel.\n    pub fn duty(&self, channel: usize) -> DutyCycle {\n        self.duty[channel]\n    }\n\n    /// Sets duty cycle (15 bit) and polarity for a PWM channel.\n    pub fn set_duty(&mut self, channel: usize, duty: DutyCycle) {\n        self.duty[channel] = duty;\n        self.sync_duty_cyles_to_peripheral();\n    }\n\n    /// Sets the duty cycle (15 bit) and polarity for all PWM channels.\n    ///\n    /// You can safely set the duty cycle of disabled PWM channels.\n    ///\n    /// When using this function, a single DMA transfer sets all the duty cycles.\n    /// If you call [`Self::set_duty()`] multiple times,\n    /// each duty cycle will be set by a separate DMA transfer.\n    pub fn set_all_duties(&mut self, duty: [DutyCycle; 4]) {\n        self.duty = duty;\n        self.sync_duty_cyles_to_peripheral();\n    }\n\n    /// Transfer the duty cycles from `self` to the peripheral.\n    fn sync_duty_cyles_to_peripheral(&self) {\n        // reload ptr in case self was moved\n        self.r.dma().seq(0).ptr().write_value((self.duty).as_ptr() as u32);\n\n        // defensive before seqstart\n        compiler_fence(Ordering::SeqCst);\n\n        self.r.events_seqend(0).write_value(0);\n\n        // tasks_seqstart() doesn't exist in all svds so write its bit instead\n        self.r.tasks_dma().seq(0).start().write_value(1);\n\n        // defensive wait until waveform is loaded after seqstart so set_duty\n        // can't be called again while dma is still reading\n        if self.is_enabled() {\n            while self.r.events_seqend(0).read() == 0 {}\n        }\n    }\n\n    /// Sets the PWM clock prescaler.\n    #[inline(always)]\n    pub fn set_prescaler(&self, div: Prescaler) {\n        self.r\n            .prescaler()\n            .write(|w| w.set_prescaler(vals::Prescaler::from_bits(div as u8)));\n    }\n\n    /// Gets the PWM clock prescaler.\n    #[inline(always)]\n    pub fn prescaler(&self) -> Prescaler {\n        match self.r.prescaler().read().prescaler().to_bits() {\n            0 => Prescaler::Div1,\n            1 => Prescaler::Div2,\n            2 => Prescaler::Div4,\n            3 => Prescaler::Div8,\n            4 => Prescaler::Div16,\n            5 => Prescaler::Div32,\n            6 => Prescaler::Div64,\n            7 => Prescaler::Div128,\n            _ => unreachable!(),\n        }\n    }\n\n    /// Sets the maximum duty cycle value.\n    #[inline(always)]\n    pub fn set_max_duty(&self, duty: u16) {\n        self.r.countertop().write(|w| w.set_countertop(duty.min(32767u16)));\n    }\n\n    /// Returns the maximum duty cycle value.\n    #[inline(always)]\n    pub fn max_duty(&self) -> u16 {\n        self.r.countertop().read().countertop()\n    }\n\n    /// Sets the PWM output frequency.\n    #[inline(always)]\n    pub fn set_period(&self, freq: u32) {\n        let clk = PWM_CLK_HZ >> (self.prescaler() as u8);\n        let duty = clk / freq;\n        self.set_max_duty(duty.min(32767) as u16);\n    }\n\n    /// Returns the PWM output frequency.\n    #[inline(always)]\n    pub fn period(&self) -> u32 {\n        let clk = PWM_CLK_HZ >> (self.prescaler() as u8);\n        let max_duty = self.max_duty() as u32;\n        clk / max_duty\n    }\n\n    /// Sets the PWM-Channel0 output drive strength\n    #[inline(always)]\n    pub fn set_ch0_drive(&self, drive: OutputDrive) {\n        if let Some(pin) = &self.ch0 {\n            pin.conf().modify(|w| convert_drive(w, drive));\n        }\n    }\n\n    /// Sets the PWM-Channel1 output drive strength\n    #[inline(always)]\n    pub fn set_ch1_drive(&self, drive: OutputDrive) {\n        if let Some(pin) = &self.ch1 {\n            pin.conf().modify(|w| convert_drive(w, drive));\n        }\n    }\n\n    /// Sets the PWM-Channel2 output drive strength\n    #[inline(always)]\n    pub fn set_ch2_drive(&self, drive: OutputDrive) {\n        if let Some(pin) = &self.ch2 {\n            pin.conf().modify(|w| convert_drive(w, drive));\n        }\n    }\n\n    /// Sets the PWM-Channel3 output drive strength\n    #[inline(always)]\n    pub fn set_ch3_drive(&self, drive: OutputDrive) {\n        if let Some(pin) = &self.ch3 {\n            pin.conf().modify(|w| convert_drive(w, drive));\n        }\n    }\n}\n\nimpl<'a> Drop for SimplePwm<'a> {\n    fn drop(&mut self) {\n        let r = &self.r;\n\n        self.disable();\n\n        if let Some(pin) = &self.ch0 {\n            pin.set_low();\n            pin.conf().write(|_| ());\n            r.psel().out(0).write_value(DISCONNECTED);\n        }\n        if let Some(pin) = &self.ch1 {\n            pin.set_low();\n            pin.conf().write(|_| ());\n            r.psel().out(1).write_value(DISCONNECTED);\n        }\n        if let Some(pin) = &self.ch2 {\n            pin.set_low();\n            pin.conf().write(|_| ());\n            r.psel().out(2).write_value(DISCONNECTED);\n        }\n        if let Some(pin) = &self.ch3 {\n            pin.set_low();\n            pin.conf().write(|_| ());\n            r.psel().out(3).write_value(DISCONNECTED);\n        }\n    }\n}\n\npub(crate) trait SealedInstance {\n    fn regs() -> pac::pwm::Pwm;\n}\n\n/// PWM peripheral instance.\n#[allow(private_bounds)]\npub trait Instance: SealedInstance + PeripheralType + 'static {\n    /// Interrupt for this peripheral.\n    type Interrupt: interrupt::typelevel::Interrupt;\n}\n\nmacro_rules! impl_pwm {\n    ($type:ident, $pac_type:ident, $irq:ident) => {\n        impl crate::pwm::SealedInstance for peripherals::$type {\n            fn regs() -> pac::pwm::Pwm {\n                pac::$pac_type\n            }\n        }\n        impl crate::pwm::Instance for peripherals::$type {\n            type Interrupt = crate::interrupt::typelevel::$irq;\n        }\n    };\n}\n"
  },
  {
    "path": "embassy-nrf/src/qdec.rs",
    "content": "//! Quadrature decoder (QDEC) driver.\n\n#![macro_use]\n\nuse core::future::poll_fn;\nuse core::marker::PhantomData;\nuse core::task::Poll;\n\nuse embassy_hal_internal::{Peri, PeripheralType};\nuse embassy_sync::waitqueue::AtomicWaker;\n\nuse crate::gpio::{AnyPin, Pin as GpioPin, SealedPin as _};\nuse crate::interrupt::typelevel::Interrupt;\nuse crate::pac::gpio::vals as gpiovals;\nuse crate::pac::qdec::vals;\nuse crate::{interrupt, pac};\n\n/// Quadrature decoder driver.\npub struct Qdec<'d> {\n    r: pac::qdec::Qdec,\n    state: &'static State,\n    _phantom: PhantomData<&'d ()>,\n}\n\n/// QDEC config\n#[non_exhaustive]\npub struct Config {\n    /// Number of samples\n    pub num_samples: NumSamples,\n    /// Sample period\n    pub period: SamplePeriod,\n    /// Set LED output pin polarity\n    pub led_polarity: LedPolarity,\n    /// Enable/disable input debounce filters\n    pub debounce: bool,\n    /// Time period the LED is switched ON prior to sampling (0..511 us).\n    pub led_pre_usecs: u16,\n}\n\nimpl Default for Config {\n    fn default() -> Self {\n        Self {\n            num_samples: NumSamples::_1smpl,\n            period: SamplePeriod::_256us,\n            led_polarity: LedPolarity::ActiveHigh,\n            debounce: true,\n            led_pre_usecs: 0,\n        }\n    }\n}\n\n/// Interrupt handler.\npub struct InterruptHandler<T: Instance> {\n    _phantom: PhantomData<T>,\n}\n\nimpl<T: Instance> interrupt::typelevel::Handler<T::Interrupt> for InterruptHandler<T> {\n    unsafe fn on_interrupt() {\n        T::regs().intenclr().write(|w| w.set_reportrdy(true));\n        T::state().waker.wake();\n    }\n}\n\nimpl<'d> Qdec<'d> {\n    /// Create a new QDEC.\n    pub fn new<T: Instance>(\n        qdec: Peri<'d, T>,\n        _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd,\n        a: Peri<'d, impl GpioPin>,\n        b: Peri<'d, impl GpioPin>,\n        config: Config,\n    ) -> Self {\n        Self::new_inner(qdec, a.into(), b.into(), None, config)\n    }\n\n    /// Create a new QDEC, with a pin for LED output.\n    pub fn new_with_led<T: Instance>(\n        qdec: Peri<'d, T>,\n        _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd,\n        a: Peri<'d, impl GpioPin>,\n        b: Peri<'d, impl GpioPin>,\n        led: Peri<'d, impl GpioPin>,\n        config: Config,\n    ) -> Self {\n        Self::new_inner(qdec, a.into(), b.into(), Some(led.into()), config)\n    }\n\n    fn new_inner<T: Instance>(\n        _p: Peri<'d, T>,\n        a: Peri<'d, AnyPin>,\n        b: Peri<'d, AnyPin>,\n        led: Option<Peri<'d, AnyPin>>,\n        config: Config,\n    ) -> Self {\n        let r = T::regs();\n\n        // Select pins.\n        a.conf().write(|w| {\n            w.set_input(gpiovals::Input::CONNECT);\n            w.set_pull(gpiovals::Pull::PULLUP);\n        });\n        b.conf().write(|w| {\n            w.set_input(gpiovals::Input::CONNECT);\n            w.set_pull(gpiovals::Pull::PULLUP);\n        });\n        r.psel().a().write_value(a.psel_bits());\n        r.psel().b().write_value(b.psel_bits());\n        if let Some(led_pin) = &led {\n            led_pin.conf().write(|w| w.set_dir(gpiovals::Dir::OUTPUT));\n            r.psel().led().write_value(led_pin.psel_bits());\n        }\n\n        // Enables/disable input debounce filters\n        r.dbfen().write(|w| match config.debounce {\n            true => w.set_dbfen(true),\n            false => w.set_dbfen(false),\n        });\n\n        // Set LED output pin polarity\n        r.ledpol().write(|w| match config.led_polarity {\n            LedPolarity::ActiveHigh => w.set_ledpol(vals::Ledpol::ACTIVE_HIGH),\n            LedPolarity::ActiveLow => w.set_ledpol(vals::Ledpol::ACTIVE_LOW),\n        });\n\n        // Set time period the LED is switched ON prior to sampling (0..511 us).\n        r.ledpre().write(|w| w.set_ledpre(config.led_pre_usecs.min(511)));\n\n        // Set sample period\n        r.sampleper().write(|w| match config.period {\n            SamplePeriod::_128us => w.set_sampleper(vals::Sampleper::_128US),\n            SamplePeriod::_256us => w.set_sampleper(vals::Sampleper::_256US),\n            SamplePeriod::_512us => w.set_sampleper(vals::Sampleper::_512US),\n            SamplePeriod::_1024us => w.set_sampleper(vals::Sampleper::_1024US),\n            SamplePeriod::_2048us => w.set_sampleper(vals::Sampleper::_2048US),\n            SamplePeriod::_4096us => w.set_sampleper(vals::Sampleper::_4096US),\n            SamplePeriod::_8192us => w.set_sampleper(vals::Sampleper::_8192US),\n            SamplePeriod::_16384us => w.set_sampleper(vals::Sampleper::_16384US),\n            SamplePeriod::_32ms => w.set_sampleper(vals::Sampleper::_32MS),\n            SamplePeriod::_65ms => w.set_sampleper(vals::Sampleper::_65MS),\n            SamplePeriod::_131ms => w.set_sampleper(vals::Sampleper::_131MS),\n        });\n\n        T::Interrupt::unpend();\n        unsafe { T::Interrupt::enable() };\n\n        // Enable peripheral\n        r.enable().write(|w| w.set_enable(true));\n\n        // Start sampling\n        r.tasks_start().write_value(1);\n\n        Self {\n            r: T::regs(),\n            state: T::state(),\n            _phantom: PhantomData,\n        }\n    }\n\n    /// Perform an asynchronous read of the decoder.\n    /// The returned future can be awaited to obtain the number of steps.\n    ///\n    /// If the future is dropped, the read is cancelled.\n    ///\n    /// # Example\n    ///\n    /// ```no_run\n    /// use embassy_nrf::qdec::{self, Qdec};\n    /// use embassy_nrf::{bind_interrupts, peripherals};\n    ///\n    /// bind_interrupts!(struct Irqs {\n    ///     QDEC => qdec::InterruptHandler<peripherals::QDEC>;\n    /// });\n    ///\n    /// # async {\n    /// # let p: embassy_nrf::Peripherals = todo!();\n    /// let config = qdec::Config::default();\n    /// let mut q = Qdec::new(p.QDEC, Irqs, p.P0_31, p.P0_30, config);\n    /// let delta = q.read().await;\n    /// # };\n    /// ```\n    pub async fn read(&mut self) -> i16 {\n        self.r.intenset().write(|w| w.set_reportrdy(true));\n        self.r.tasks_readclracc().write_value(1);\n\n        let state = self.state;\n        let r = self.r;\n        poll_fn(move |cx| {\n            state.waker.register(cx.waker());\n            if r.events_reportrdy().read() == 0 {\n                Poll::Pending\n            } else {\n                r.events_reportrdy().write_value(0);\n                let acc = r.accread().read();\n                Poll::Ready(acc as i16)\n            }\n        })\n        .await\n    }\n}\n\n/// Sample period\n#[derive(Debug, Eq, PartialEq, Clone, Copy)]\npub enum SamplePeriod {\n    /// 128 us\n    _128us,\n    /// 256 us\n    _256us,\n    /// 512 us\n    _512us,\n    /// 1024 us\n    _1024us,\n    /// 2048 us\n    _2048us,\n    /// 4096 us\n    _4096us,\n    /// 8192 us\n    _8192us,\n    /// 16384 us\n    _16384us,\n    /// 32 ms\n    _32ms,\n    /// 65 ms\n    _65ms,\n    /// 131 ms\n    _131ms,\n}\n\n/// Number of samples taken.\n#[derive(Debug, Eq, PartialEq, Clone, Copy)]\npub enum NumSamples {\n    /// 10 samples\n    _10smpl,\n    /// 40 samples\n    _40smpl,\n    /// 80 samples\n    _80smpl,\n    /// 120 samples\n    _120smpl,\n    /// 160 samples\n    _160smpl,\n    /// 200 samples\n    _200smpl,\n    /// 240 samples\n    _240smpl,\n    /// 280 samples\n    _280smpl,\n    /// 1 sample\n    _1smpl,\n}\n\n/// LED polarity\n#[derive(Debug, Eq, PartialEq, Clone, Copy)]\npub enum LedPolarity {\n    /// Active high (a high output turns on the LED).\n    ActiveHigh,\n    /// Active low (a low output turns on the LED).\n    ActiveLow,\n}\n\n/// Peripheral static state\npub(crate) struct State {\n    waker: AtomicWaker,\n}\n\nimpl State {\n    pub(crate) const fn new() -> Self {\n        Self {\n            waker: AtomicWaker::new(),\n        }\n    }\n}\n\npub(crate) trait SealedInstance {\n    fn regs() -> pac::qdec::Qdec;\n    fn state() -> &'static State;\n}\n\n/// qdec peripheral instance.\n#[allow(private_bounds)]\npub trait Instance: SealedInstance + PeripheralType + 'static + Send {\n    /// Interrupt for this peripheral.\n    type Interrupt: interrupt::typelevel::Interrupt;\n}\n\nmacro_rules! impl_qdec {\n    ($type:ident, $pac_type:ident, $irq:ident) => {\n        impl crate::qdec::SealedInstance for peripherals::$type {\n            fn regs() -> pac::qdec::Qdec {\n                pac::$pac_type\n            }\n            fn state() -> &'static crate::qdec::State {\n                static STATE: crate::qdec::State = crate::qdec::State::new();\n                &STATE\n            }\n        }\n        impl crate::qdec::Instance for peripherals::$type {\n            type Interrupt = crate::interrupt::typelevel::$irq;\n        }\n    };\n}\n"
  },
  {
    "path": "embassy-nrf/src/qspi.rs",
    "content": "//! Quad Serial Peripheral Interface (QSPI) flash driver.\n\n#![macro_use]\n\nuse core::future::{Future, poll_fn};\nuse core::marker::PhantomData;\nuse core::ptr;\nuse core::task::Poll;\n\nuse embassy_hal_internal::drop::OnDrop;\nuse embassy_hal_internal::{Peri, PeripheralType};\nuse embassy_sync::waitqueue::AtomicWaker;\nuse embedded_storage::nor_flash::{ErrorType, NorFlash, NorFlashError, NorFlashErrorKind, ReadNorFlash};\n\nuse crate::gpio::{self, Pin as GpioPin};\nuse crate::interrupt::typelevel::Interrupt;\nuse crate::pac::gpio::vals as gpiovals;\nuse crate::pac::qspi::vals;\npub use crate::pac::qspi::vals::{\n    Addrmode as AddressMode, Ppsize as WritePageSize, Readoc as ReadOpcode, Spimode as SpiMode, Writeoc as WriteOpcode,\n};\nuse crate::{interrupt, pac};\n\n/// Deep power-down config.\npub struct DeepPowerDownConfig {\n    /// Time required for entering DPM, in units of 16us\n    pub enter_time: u16,\n    /// Time required for exiting DPM, in units of 16us\n    pub exit_time: u16,\n}\n\n/// QSPI bus frequency.\npub enum Frequency {\n    /// 32 Mhz\n    M32 = 0,\n    /// 16 Mhz\n    M16 = 1,\n    /// 10.7 Mhz\n    M10_7 = 2,\n    /// 8 Mhz\n    M8 = 3,\n    /// 6.4 Mhz\n    M6_4 = 4,\n    /// 5.3 Mhz\n    M5_3 = 5,\n    /// 4.6 Mhz\n    M4_6 = 6,\n    /// 4 Mhz\n    M4 = 7,\n    /// 3.6 Mhz\n    M3_6 = 8,\n    /// 3.2 Mhz\n    M3_2 = 9,\n    /// 2.9 Mhz\n    M2_9 = 10,\n    /// 2.7 Mhz\n    M2_7 = 11,\n    /// 2.5 Mhz\n    M2_5 = 12,\n    /// 2.3 Mhz\n    M2_3 = 13,\n    /// 2.1 Mhz\n    M2_1 = 14,\n    /// 2 Mhz\n    M2 = 15,\n}\n\n/// QSPI config.\n#[non_exhaustive]\npub struct Config {\n    /// XIP offset.\n    pub xip_offset: u32,\n    /// Opcode used for read operations.\n    pub read_opcode: ReadOpcode,\n    /// Opcode used for write operations.\n    pub write_opcode: WriteOpcode,\n    /// Page size for write operations.\n    pub write_page_size: WritePageSize,\n    /// Configuration for deep power down. If None, deep power down is disabled.\n    pub deep_power_down: Option<DeepPowerDownConfig>,\n    /// QSPI bus frequency.\n    pub frequency: Frequency,\n    /// Value is specified in number of 16 MHz periods (62.5 ns)\n    pub sck_delay: u8,\n    /// Value is specified in number of 64 MHz periods (15.625 ns), valid values between 0 and 7 (inclusive)\n    pub rx_delay: u8,\n    /// Whether data is captured on the clock rising edge and data is output on a falling edge (MODE0) or vice-versa (MODE3)\n    pub spi_mode: SpiMode,\n    /// Addressing mode (24-bit or 32-bit)\n    pub address_mode: AddressMode,\n    /// Flash memory capacity in bytes. This is the value reported by the `embedded-storage` traits.\n    pub capacity: u32,\n}\n\nimpl Default for Config {\n    fn default() -> Self {\n        Self {\n            read_opcode: ReadOpcode::READ4IO,\n            write_opcode: WriteOpcode::PP4IO,\n            xip_offset: 0,\n            write_page_size: WritePageSize::_256BYTES,\n            deep_power_down: None,\n            frequency: Frequency::M8,\n            sck_delay: 80,\n            rx_delay: 2,\n            spi_mode: SpiMode::MODE0,\n            address_mode: AddressMode::_24BIT,\n            capacity: 0,\n        }\n    }\n}\n\n/// Error\n#[derive(Debug, Copy, Clone, Eq, PartialEq)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\n#[non_exhaustive]\npub enum Error {\n    /// Operation address was out of bounds.\n    OutOfBounds,\n    // TODO add \"not in data memory\" error and check for it\n}\n\n/// Interrupt handler.\npub struct InterruptHandler<T: Instance> {\n    _phantom: PhantomData<T>,\n}\n\nimpl<T: Instance> interrupt::typelevel::Handler<T::Interrupt> for InterruptHandler<T> {\n    unsafe fn on_interrupt() {\n        let r = T::regs();\n        let s = T::state();\n\n        if r.events_ready().read() != 0 {\n            s.waker.wake();\n            r.intenclr().write(|w| w.set_ready(true));\n        }\n    }\n}\n\n/// QSPI flash driver.\npub struct Qspi<'d> {\n    r: pac::qspi::Qspi,\n    state: &'static State,\n    dpm_enabled: bool,\n    capacity: u32,\n    _phantom: PhantomData<&'d ()>,\n}\n\nimpl<'d> Qspi<'d> {\n    /// Create a new QSPI driver.\n    pub fn new<T: Instance>(\n        _qspi: Peri<'d, T>,\n        _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd,\n        sck: Peri<'d, impl GpioPin>,\n        csn: Peri<'d, impl GpioPin>,\n        io0: Peri<'d, impl GpioPin>,\n        io1: Peri<'d, impl GpioPin>,\n        io2: Peri<'d, impl GpioPin>,\n        io3: Peri<'d, impl GpioPin>,\n        config: Config,\n    ) -> Self {\n        let r = T::regs();\n\n        macro_rules! config_pin {\n            ($pin:ident) => {\n                $pin.set_high();\n                $pin.conf().write(|w| {\n                    w.set_dir(gpiovals::Dir::OUTPUT);\n                    w.set_drive(gpiovals::Drive::H0H1);\n                    #[cfg(all(feature = \"_nrf5340\", feature = \"_s\"))]\n                    w.set_mcusel(gpiovals::Mcusel::PERIPHERAL);\n                });\n                r.psel().$pin().write_value($pin.psel_bits());\n            };\n        }\n\n        config_pin!(sck);\n        config_pin!(csn);\n        config_pin!(io0);\n        config_pin!(io1);\n        config_pin!(io2);\n        config_pin!(io3);\n\n        r.ifconfig0().write(|w| {\n            w.set_addrmode(config.address_mode);\n            w.set_dpmenable(config.deep_power_down.is_some());\n            w.set_ppsize(config.write_page_size);\n            w.set_readoc(config.read_opcode);\n            w.set_writeoc(config.write_opcode);\n        });\n\n        if let Some(dpd) = &config.deep_power_down {\n            r.dpmdur().write(|w| {\n                w.set_enter(dpd.enter_time);\n                w.set_exit(dpd.exit_time);\n            })\n        }\n\n        r.ifconfig1().write(|w| {\n            w.set_sckdelay(config.sck_delay);\n            w.set_dpmen(false);\n            w.set_spimode(config.spi_mode);\n            w.set_sckfreq(config.frequency as u8);\n        });\n\n        r.iftiming().write(|w| {\n            w.set_rxdelay(config.rx_delay & 0b111);\n        });\n\n        r.xipoffset().write_value(config.xip_offset);\n\n        T::Interrupt::unpend();\n        unsafe { T::Interrupt::enable() };\n\n        // Enable it\n        r.enable().write(|w| w.set_enable(true));\n\n        let res = Self {\n            r: T::regs(),\n            state: T::state(),\n            dpm_enabled: config.deep_power_down.is_some(),\n            capacity: config.capacity,\n            _phantom: PhantomData,\n        };\n\n        r.events_ready().write_value(0);\n        r.intenset().write(|w| w.set_ready(true));\n\n        r.tasks_activate().write_value(1);\n\n        Self::blocking_wait_ready();\n\n        res\n    }\n\n    /// Do a custom QSPI instruction.\n    pub async fn custom_instruction(&mut self, opcode: u8, req: &[u8], resp: &mut [u8]) -> Result<(), Error> {\n        let ondrop = OnDrop::new(Self::blocking_wait_ready);\n\n        let len = core::cmp::max(req.len(), resp.len()) as u8;\n        self.custom_instruction_start(opcode, req, len)?;\n\n        self.wait_ready().await;\n\n        self.custom_instruction_finish(resp)?;\n\n        ondrop.defuse();\n\n        Ok(())\n    }\n\n    /// Do a custom QSPI instruction, blocking version.\n    pub fn blocking_custom_instruction(&mut self, opcode: u8, req: &[u8], resp: &mut [u8]) -> Result<(), Error> {\n        let len = core::cmp::max(req.len(), resp.len()) as u8;\n        self.custom_instruction_start(opcode, req, len)?;\n\n        Self::blocking_wait_ready();\n\n        self.custom_instruction_finish(resp)?;\n\n        Ok(())\n    }\n\n    fn custom_instruction_start(&mut self, opcode: u8, req: &[u8], len: u8) -> Result<(), Error> {\n        assert!(req.len() <= 8);\n\n        let mut dat0: u32 = 0;\n        let mut dat1: u32 = 0;\n\n        for i in 0..4 {\n            if i < req.len() {\n                dat0 |= (req[i] as u32) << (i * 8);\n            }\n        }\n        for i in 0..4 {\n            if i + 4 < req.len() {\n                dat1 |= (req[i + 4] as u32) << (i * 8);\n            }\n        }\n\n        self.r.cinstrdat0().write(|w| w.0 = dat0);\n        self.r.cinstrdat1().write(|w| w.0 = dat1);\n\n        self.r.events_ready().write_value(0);\n        self.r.intenset().write(|w| w.set_ready(true));\n\n        self.r.cinstrconf().write(|w| {\n            w.set_opcode(opcode);\n            w.set_length(vals::Length::from_bits(len + 1));\n            w.set_lio2(true);\n            w.set_lio3(true);\n            w.set_wipwait(true);\n            w.set_wren(true);\n            w.set_lfen(false);\n            w.set_lfstop(false);\n        });\n        Ok(())\n    }\n\n    fn custom_instruction_finish(&mut self, resp: &mut [u8]) -> Result<(), Error> {\n        let dat0 = self.r.cinstrdat0().read().0;\n        let dat1 = self.r.cinstrdat1().read().0;\n        for i in 0..4 {\n            if i < resp.len() {\n                resp[i] = (dat0 >> (i * 8)) as u8;\n            }\n        }\n        for i in 0..4 {\n            if i + 4 < resp.len() {\n                resp[i] = (dat1 >> (i * 8)) as u8;\n            }\n        }\n        Ok(())\n    }\n\n    fn wait_ready(&mut self) -> impl Future<Output = ()> {\n        let r = self.r;\n        let s = self.state;\n        poll_fn(move |cx| {\n            s.waker.register(cx.waker());\n            if r.events_ready().read() != 0 {\n                return Poll::Ready(());\n            }\n            Poll::Pending\n        })\n    }\n\n    fn blocking_wait_ready() {\n        loop {\n            let r = pac::QSPI;\n            if r.events_ready().read() != 0 {\n                break;\n            }\n        }\n    }\n\n    fn start_read(&mut self, address: u32, data: &mut [u8]) -> Result<(), Error> {\n        // TODO: Return these as errors instead.\n        assert_eq!(data.as_ptr() as u32 % 4, 0);\n        assert_eq!(data.len() as u32 % 4, 0);\n        assert_eq!(address % 4, 0);\n\n        self.r.read().src().write_value(address);\n        self.r.read().dst().write_value(data.as_ptr() as u32);\n        self.r.read().cnt().write(|w| w.set_cnt(data.len() as u32));\n\n        self.r.events_ready().write_value(0);\n        self.r.intenset().write(|w| w.set_ready(true));\n        self.r.tasks_readstart().write_value(1);\n\n        Ok(())\n    }\n\n    fn start_write(&mut self, address: u32, data: &[u8]) -> Result<(), Error> {\n        // TODO: Return these as errors instead.\n        assert_eq!(data.as_ptr() as u32 % 4, 0);\n        assert_eq!(data.len() as u32 % 4, 0);\n        assert_eq!(address % 4, 0);\n\n        self.r.write().src().write_value(data.as_ptr() as u32);\n        self.r.write().dst().write_value(address);\n        self.r.write().cnt().write(|w| w.set_cnt(data.len() as u32));\n\n        self.r.events_ready().write_value(0);\n        self.r.intenset().write(|w| w.set_ready(true));\n        self.r.tasks_writestart().write_value(1);\n\n        Ok(())\n    }\n\n    fn start_erase(&mut self, address: u32) -> Result<(), Error> {\n        // TODO: Return these as errors instead.\n        assert_eq!(address % 4096, 0);\n\n        self.r.erase().ptr().write_value(address);\n        self.r.erase().len().write(|w| w.set_len(vals::Len::_4KB));\n\n        self.r.events_ready().write_value(0);\n        self.r.intenset().write(|w| w.set_ready(true));\n        self.r.tasks_erasestart().write_value(1);\n\n        Ok(())\n    }\n\n    /// Raw QSPI read.\n    ///\n    /// The difference with `read` is that this does not do bounds checks\n    /// against the flash capacity. It is intended for use when QSPI is used as\n    /// a raw bus, not with flash memory.\n    pub async fn read_raw(&mut self, address: u32, data: &mut [u8]) -> Result<(), Error> {\n        // Avoid blocking_wait_ready() blocking forever on zero-length buffers.\n        if data.is_empty() {\n            return Ok(());\n        }\n\n        let ondrop = OnDrop::new(Self::blocking_wait_ready);\n\n        self.start_read(address, data)?;\n        self.wait_ready().await;\n\n        ondrop.defuse();\n\n        Ok(())\n    }\n\n    /// Raw QSPI write.\n    ///\n    /// The difference with `write` is that this does not do bounds checks\n    /// against the flash capacity. It is intended for use when QSPI is used as\n    /// a raw bus, not with flash memory.\n    pub async fn write_raw(&mut self, address: u32, data: &[u8]) -> Result<(), Error> {\n        // Avoid blocking_wait_ready() blocking forever on zero-length buffers.\n        if data.is_empty() {\n            return Ok(());\n        }\n\n        let ondrop = OnDrop::new(Self::blocking_wait_ready);\n\n        self.start_write(address, data)?;\n        self.wait_ready().await;\n\n        ondrop.defuse();\n\n        Ok(())\n    }\n\n    /// Raw QSPI read, blocking version.\n    ///\n    /// The difference with `blocking_read` is that this does not do bounds checks\n    /// against the flash capacity. It is intended for use when QSPI is used as\n    /// a raw bus, not with flash memory.\n    pub fn blocking_read_raw(&mut self, address: u32, data: &mut [u8]) -> Result<(), Error> {\n        // Avoid blocking_wait_ready() blocking forever on zero-length buffers.\n        if data.is_empty() {\n            return Ok(());\n        }\n\n        self.start_read(address, data)?;\n        Self::blocking_wait_ready();\n        Ok(())\n    }\n\n    /// Raw QSPI write, blocking version.\n    ///\n    /// The difference with `blocking_write` is that this does not do bounds checks\n    /// against the flash capacity. It is intended for use when QSPI is used as\n    /// a raw bus, not with flash memory.\n    pub fn blocking_write_raw(&mut self, address: u32, data: &[u8]) -> Result<(), Error> {\n        // Avoid blocking_wait_ready() blocking forever on zero-length buffers.\n        if data.is_empty() {\n            return Ok(());\n        }\n\n        self.start_write(address, data)?;\n        Self::blocking_wait_ready();\n        Ok(())\n    }\n\n    /// Read data from the flash memory.\n    pub async fn read(&mut self, address: u32, data: &mut [u8]) -> Result<(), Error> {\n        self.bounds_check(address, data.len())?;\n        self.read_raw(address, data).await\n    }\n\n    /// Write data to the flash memory.\n    pub async fn write(&mut self, address: u32, data: &[u8]) -> Result<(), Error> {\n        self.bounds_check(address, data.len())?;\n        self.write_raw(address, data).await\n    }\n\n    /// Erase a sector on the flash memory.\n    pub async fn erase(&mut self, address: u32) -> Result<(), Error> {\n        if address >= self.capacity {\n            return Err(Error::OutOfBounds);\n        }\n\n        let ondrop = OnDrop::new(Self::blocking_wait_ready);\n\n        self.start_erase(address)?;\n        self.wait_ready().await;\n\n        ondrop.defuse();\n\n        Ok(())\n    }\n\n    /// Read data from the flash memory, blocking version.\n    pub fn blocking_read(&mut self, address: u32, data: &mut [u8]) -> Result<(), Error> {\n        self.bounds_check(address, data.len())?;\n        self.blocking_read_raw(address, data)\n    }\n\n    /// Write data to the flash memory, blocking version.\n    pub fn blocking_write(&mut self, address: u32, data: &[u8]) -> Result<(), Error> {\n        self.bounds_check(address, data.len())?;\n        self.blocking_write_raw(address, data)\n    }\n\n    /// Erase a sector on the flash memory, blocking version.\n    pub fn blocking_erase(&mut self, address: u32) -> Result<(), Error> {\n        if address >= self.capacity {\n            return Err(Error::OutOfBounds);\n        }\n\n        self.start_erase(address)?;\n        Self::blocking_wait_ready();\n        Ok(())\n    }\n\n    fn bounds_check(&self, address: u32, len: usize) -> Result<(), Error> {\n        let len_u32: u32 = len.try_into().map_err(|_| Error::OutOfBounds)?;\n        let end_address = address.checked_add(len_u32).ok_or(Error::OutOfBounds)?;\n        if end_address > self.capacity {\n            return Err(Error::OutOfBounds);\n        }\n        Ok(())\n    }\n}\n\nimpl<'d> Drop for Qspi<'d> {\n    fn drop(&mut self) {\n        if self.dpm_enabled {\n            trace!(\"qspi: doing deep powerdown...\");\n\n            self.r.ifconfig1().modify(|w| w.set_dpmen(true));\n\n            // Wait for DPM enter.\n            // Unfortunately we must spin. There's no way to do this interrupt-driven.\n            // The READY event does NOT fire on DPM enter (but it does fire on DPM exit :shrug:)\n            while !self.r.status().read().dpm() {}\n\n            // Wait MORE for DPM enter.\n            // I have absolutely no idea why, but the wait above is not enough :'(\n            // Tested with mx25r64 in nrf52840-dk, and with mx25r16 in custom board\n            cortex_m::asm::delay(4096);\n        }\n\n        // it seems events_ready is not generated in response to deactivate. nrfx doesn't wait for it.\n        self.r.tasks_deactivate().write_value(1);\n\n        // Workaround https://docs.nordicsemi.com/bundle/errata_nRF52840_Rev3/page/ERR/nRF52840/Rev3/latest/anomaly_840_122.html\n        // Note that the doc has 2 register writes, but the first one is really the write to tasks_deactivate,\n        // so we only do the second one here.\n        unsafe { ptr::write_volatile(0x40029054 as *mut u32, 1) }\n\n        self.r.enable().write(|w| w.set_enable(false));\n\n        // Note: we do NOT deconfigure CSN here. If DPM is in use and we disconnect CSN,\n        // leaving it floating, the flash chip might read it as zero which would cause it to\n        // spuriously exit DPM.\n        gpio::deconfigure_pin(self.r.psel().sck().read());\n        gpio::deconfigure_pin(self.r.psel().io0().read());\n        gpio::deconfigure_pin(self.r.psel().io1().read());\n        gpio::deconfigure_pin(self.r.psel().io2().read());\n        gpio::deconfigure_pin(self.r.psel().io3().read());\n\n        trace!(\"qspi: dropped\");\n    }\n}\n\nimpl<'d> ErrorType for Qspi<'d> {\n    type Error = Error;\n}\n\nimpl NorFlashError for Error {\n    fn kind(&self) -> NorFlashErrorKind {\n        NorFlashErrorKind::Other\n    }\n}\n\nimpl<'d> ReadNorFlash for Qspi<'d> {\n    const READ_SIZE: usize = 4;\n\n    fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Self::Error> {\n        self.blocking_read(offset, bytes)?;\n        Ok(())\n    }\n\n    fn capacity(&self) -> usize {\n        self.capacity as usize\n    }\n}\n\nimpl<'d> NorFlash for Qspi<'d> {\n    const WRITE_SIZE: usize = 4;\n    const ERASE_SIZE: usize = 4096;\n\n    fn erase(&mut self, from: u32, to: u32) -> Result<(), Self::Error> {\n        for address in (from..to).step_by(<Self as NorFlash>::ERASE_SIZE) {\n            self.blocking_erase(address)?;\n        }\n        Ok(())\n    }\n\n    fn write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Self::Error> {\n        self.blocking_write(offset, bytes)?;\n        Ok(())\n    }\n}\n\n#[cfg(feature = \"qspi-multiwrite-flash\")]\nimpl<'d> embedded_storage::nor_flash::MultiwriteNorFlash for Qspi<'d> {}\n\nmod _eh1 {\n    use embedded_storage_async::nor_flash::{NorFlash as AsyncNorFlash, ReadNorFlash as AsyncReadNorFlash};\n\n    use super::*;\n\n    impl<'d> AsyncNorFlash for Qspi<'d> {\n        const WRITE_SIZE: usize = <Self as NorFlash>::WRITE_SIZE;\n        const ERASE_SIZE: usize = <Self as NorFlash>::ERASE_SIZE;\n\n        async fn write(&mut self, offset: u32, data: &[u8]) -> Result<(), Self::Error> {\n            self.write(offset, data).await\n        }\n\n        async fn erase(&mut self, from: u32, to: u32) -> Result<(), Self::Error> {\n            for address in (from..to).step_by(<Self as AsyncNorFlash>::ERASE_SIZE) {\n                self.erase(address).await?\n            }\n            Ok(())\n        }\n    }\n\n    impl<'d> AsyncReadNorFlash for Qspi<'d> {\n        const READ_SIZE: usize = 4;\n        async fn read(&mut self, address: u32, data: &mut [u8]) -> Result<(), Self::Error> {\n            self.read(address, data).await\n        }\n\n        fn capacity(&self) -> usize {\n            self.capacity as usize\n        }\n    }\n\n    #[cfg(feature = \"qspi-multiwrite-flash\")]\n    impl<'d> embedded_storage_async::nor_flash::MultiwriteNorFlash for Qspi<'d> {}\n}\n\n/// Peripheral static state\npub(crate) struct State {\n    waker: AtomicWaker,\n}\n\nimpl State {\n    pub(crate) const fn new() -> Self {\n        Self {\n            waker: AtomicWaker::new(),\n        }\n    }\n}\n\npub(crate) trait SealedInstance {\n    fn regs() -> pac::qspi::Qspi;\n    fn state() -> &'static State;\n}\n\n/// QSPI peripheral instance.\n#[allow(private_bounds)]\npub trait Instance: SealedInstance + PeripheralType + 'static + Send {\n    /// Interrupt for this peripheral.\n    type Interrupt: interrupt::typelevel::Interrupt;\n}\n\nmacro_rules! impl_qspi {\n    ($type:ident, $pac_type:ident, $irq:ident) => {\n        impl crate::qspi::SealedInstance for peripherals::$type {\n            fn regs() -> pac::qspi::Qspi {\n                pac::$pac_type\n            }\n            fn state() -> &'static crate::qspi::State {\n                static STATE: crate::qspi::State = crate::qspi::State::new();\n                &STATE\n            }\n        }\n        impl crate::qspi::Instance for peripherals::$type {\n            type Interrupt = crate::interrupt::typelevel::$irq;\n        }\n    };\n}\n"
  },
  {
    "path": "embassy-nrf/src/radio/ieee802154.rs",
    "content": "//! IEEE 802.15.4 radio driver\n\nuse core::marker::PhantomData;\nuse core::sync::atomic::{Ordering, compiler_fence};\nuse core::task::Poll;\n\nuse embassy_hal_internal::drop::OnDrop;\n\nuse super::{Error, InterruptHandler, TxPower};\nuse crate::Peri;\nuse crate::interrupt::typelevel::Interrupt;\nuse crate::interrupt::{self};\nuse crate::pac::radio::vals;\npub use crate::pac::radio::vals::State as RadioState;\nuse crate::radio::Instance;\n\n/// Default (IEEE compliant) Start of Frame Delimiter\npub const DEFAULT_SFD: u8 = 0xA7;\n\n// TODO expose the other variants in `pac::CCAMODE_A`\n/// Clear Channel Assessment method\npub enum Cca {\n    /// Carrier sense\n    CarrierSense,\n    /// Energy Detection / Energy Above Threshold\n    EnergyDetection {\n        /// Energy measurements above this value mean that the channel is assumed to be busy.\n        /// Note the measurement range is 0..0xFF - where 0 means that the received power was\n        /// less than 10 dB above the selected receiver sensitivity. This value is not given in dBm,\n        /// but can be converted. See the nrf52840 Product Specification Section 6.20.12.4\n        /// for details.\n        ed_threshold: u8,\n    },\n}\n\n/// IEEE 802.15.4 radio driver.\npub struct Radio<'d> {\n    r: crate::pac::radio::Radio,\n    state: &'static crate::radio::State,\n    needs_enable: bool,\n    phantom: PhantomData<&'d ()>,\n}\n\nimpl<'d> Radio<'d> {\n    /// Create a new IEEE 802.15.4 radio driver.\n    pub fn new<T: Instance>(\n        _radio: Peri<'d, T>,\n        _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd,\n    ) -> Self {\n        let r = crate::pac::RADIO;\n\n        // Disable and enable to reset peripheral\n        r.power().write(|w| w.set_power(false));\n        r.power().write(|w| w.set_power(true));\n        errata::post_power();\n\n        // Enable 802.15.4 mode\n        r.mode().write(|w| w.set_mode(vals::Mode::IEEE802154_250KBIT));\n        // Configure CRC skip address\n        r.crccnf().write(|w| {\n            w.set_len(vals::Len::TWO);\n            w.set_skipaddr(vals::Skipaddr::IEEE802154);\n        });\n        // Configure CRC polynomial and init\n        r.crcpoly().write(|w| w.set_crcpoly(0x0001_1021));\n        r.crcinit().write(|w| w.set_crcinit(0));\n        r.pcnf0().write(|w| {\n            // 8-bit on air length\n            w.set_lflen(8);\n            // Zero bytes S0 field length\n            w.set_s0len(false);\n            // Zero bytes S1 field length\n            w.set_s1len(0);\n            // Do not include S1 field in RAM if S1 length > 0\n            w.set_s1incl(vals::S1incl::AUTOMATIC);\n            // Zero code Indicator length\n            w.set_cilen(0);\n            // 32-bit zero preamble\n            w.set_plen(vals::Plen::_32BIT_ZERO);\n            // Include CRC in length\n            w.set_crcinc(vals::Crcinc::INCLUDE);\n        });\n        r.pcnf1().write(|w| {\n            // Maximum packet length\n            w.set_maxlen(Packet::MAX_PSDU_LEN);\n            // Zero static length\n            w.set_statlen(0);\n            // Zero base address length\n            w.set_balen(0);\n            // Little-endian\n            w.set_endian(vals::Endian::LITTLE);\n            // Disable packet whitening\n            w.set_whiteen(false);\n        });\n\n        // Enable NVIC interrupt\n        crate::interrupt::typelevel::RADIO::unpend();\n        unsafe { crate::interrupt::typelevel::RADIO::enable() };\n\n        let mut radio = Self {\n            r: crate::pac::RADIO,\n            state: T::state(),\n            needs_enable: false,\n            phantom: PhantomData,\n        };\n\n        radio.set_sfd(DEFAULT_SFD);\n        radio.set_transmission_power(0);\n        radio.set_channel(11);\n        radio.set_cca(Cca::CarrierSense);\n\n        radio\n    }\n\n    /// Changes the radio channel\n    pub fn set_channel(&mut self, channel: u8) {\n        let r = self.r;\n        if channel < 11 || channel > 26 {\n            panic!(\"Bad 802.15.4 channel\");\n        }\n        let frequency_offset = (channel - 10) * 5;\n        self.needs_enable = true;\n        r.frequency().write(|w| {\n            w.set_frequency(frequency_offset);\n            w.set_map(vals::Map::DEFAULT);\n        });\n    }\n\n    /// Changes the Clear Channel Assessment method\n    pub fn set_cca(&mut self, cca: Cca) {\n        let r = self.r;\n        self.needs_enable = true;\n        match cca {\n            Cca::CarrierSense => r.ccactrl().write(|w| w.set_ccamode(vals::Ccamode::CARRIER_MODE)),\n            Cca::EnergyDetection { ed_threshold } => {\n                // \"[ED] is enabled by first configuring the field CCAMODE=EdMode in CCACTRL\n                // and writing the CCAEDTHRES field to a chosen value.\"\n                r.ccactrl().write(|w| {\n                    w.set_ccamode(vals::Ccamode::ED_MODE);\n                    w.set_ccaedthres(ed_threshold);\n                });\n            }\n        }\n    }\n\n    /// Changes the Start of Frame Delimiter (SFD)\n    pub fn set_sfd(&mut self, sfd: u8) {\n        let r = self.r;\n        r.sfd().write(|w| w.set_sfd(sfd));\n    }\n\n    /// Clear interrupts\n    pub fn clear_all_interrupts(&mut self) {\n        let r = self.r;\n        r.intenclr().write(|w| w.0 = 0xffff_ffff);\n    }\n\n    /// Changes the radio transmission power\n    pub fn set_transmission_power(&mut self, power: i8) {\n        let r = self.r;\n        self.needs_enable = true;\n\n        let tx_power: TxPower = match power {\n            #[cfg(not(any(feature = \"nrf52811\", feature = \"_nrf5340-net\")))]\n            8 => TxPower::POS8_DBM,\n            #[cfg(not(any(feature = \"nrf52811\", feature = \"_nrf5340-net\")))]\n            7 => TxPower::POS7_DBM,\n            #[cfg(not(any(feature = \"nrf52811\", feature = \"_nrf5340-net\")))]\n            6 => TxPower::POS6_DBM,\n            #[cfg(not(any(feature = \"nrf52811\", feature = \"_nrf5340-net\")))]\n            5 => TxPower::POS5_DBM,\n            #[cfg(not(feature = \"_nrf5340-net\"))]\n            4 => TxPower::POS4_DBM,\n            #[cfg(not(feature = \"_nrf5340-net\"))]\n            3 => TxPower::POS3_DBM,\n            #[cfg(not(any(feature = \"nrf52811\", feature = \"_nrf5340-net\")))]\n            2 => TxPower::POS2_DBM,\n            0 => TxPower::_0_DBM,\n            #[cfg(feature = \"_nrf5340-net\")]\n            -1 => TxPower::NEG1_DBM,\n            #[cfg(feature = \"_nrf5340-net\")]\n            -2 => TxPower::NEG2_DBM,\n            #[cfg(feature = \"_nrf5340-net\")]\n            -3 => TxPower::NEG3_DBM,\n            -4 => TxPower::NEG4_DBM,\n            #[cfg(feature = \"_nrf5340-net\")]\n            -5 => TxPower::NEG5_DBM,\n            #[cfg(feature = \"_nrf5340-net\")]\n            -6 => TxPower::NEG6_DBM,\n            #[cfg(feature = \"_nrf5340-net\")]\n            -7 => TxPower::NEG7_DBM,\n            -8 => TxPower::NEG8_DBM,\n            -12 => TxPower::NEG12_DBM,\n            -16 => TxPower::NEG16_DBM,\n            -20 => TxPower::NEG20_DBM,\n            -30 => TxPower::NEG30_DBM,\n            -40 => TxPower::NEG40_DBM,\n            _ => panic!(\"Invalid transmission power value\"),\n        };\n\n        r.txpower().write(|w| w.set_txpower(tx_power));\n    }\n\n    /// Waits until the radio state matches the given `state`\n    fn wait_for_radio_state(&self, state: RadioState) {\n        while self.state() != state {}\n    }\n\n    /// Get the current radio state\n    fn state(&self) -> RadioState {\n        self.r.state().read().state()\n    }\n\n    /// Moves the radio from any state to the DISABLED state\n    fn disable(&mut self) {\n        let r = self.r;\n        // See figure 110 in nRF52840-PS\n        loop {\n            match self.state() {\n                RadioState::DISABLED => return,\n                // idle or ramping up\n                RadioState::RX_RU | RadioState::RX_IDLE | RadioState::TX_RU | RadioState::TX_IDLE => {\n                    r.tasks_disable().write_value(1);\n                    self.wait_for_radio_state(RadioState::DISABLED);\n                    return;\n                }\n                // ramping down\n                RadioState::RX_DISABLE | RadioState::TX_DISABLE => {\n                    self.wait_for_radio_state(RadioState::DISABLED);\n                    return;\n                }\n                // cancel ongoing transfer or ongoing CCA\n                RadioState::RX => {\n                    r.tasks_ccastop().write_value(1);\n                    r.tasks_stop().write_value(1);\n                    self.wait_for_radio_state(RadioState::RX_IDLE);\n                }\n                RadioState::TX => {\n                    r.tasks_stop().write_value(1);\n                    self.wait_for_radio_state(RadioState::TX_IDLE);\n                }\n                _ => unreachable!(),\n            }\n        }\n    }\n\n    fn set_buffer(&mut self, buffer: &[u8]) {\n        let r = self.r;\n        r.packetptr().write_value(buffer.as_ptr() as u32);\n    }\n\n    /// Moves the radio to the RXIDLE state\n    fn receive_prepare(&mut self) {\n        // clear related events\n        self.r.events_ccabusy().write_value(0);\n        self.r.events_phyend().write_value(0);\n        // NOTE to avoid errata 204 (see rev1 v1.4) we do TX_IDLE -> DISABLED -> RXIDLE\n        let disable = match self.state() {\n            RadioState::DISABLED => false,\n            RadioState::RX_IDLE => self.needs_enable,\n            _ => true,\n        };\n        if disable {\n            self.disable();\n        }\n        self.needs_enable = false;\n    }\n\n    /// Prepare radio for receiving a packet\n    fn receive_start(&mut self, packet: &mut Packet) {\n        // NOTE we do NOT check the address of `packet` because the mutable reference ensures it's\n        // allocated in RAM\n        let r = self.r;\n\n        self.receive_prepare();\n\n        // Configure shortcuts\n        //\n        // The radio goes through following states when receiving a 802.15.4 packet\n        //\n        // enable RX → ramp up RX → RX idle → Receive → end (PHYEND)\n        r.shorts().write(|w| w.set_rxready_start(true));\n\n        // set up RX buffer\n        self.set_buffer(packet.buffer.as_mut());\n\n        // start transfer\n        dma_start_fence();\n\n        match self.state() {\n            // Re-start receiver\n            RadioState::RX_IDLE => r.tasks_start().write_value(1),\n            // Enable receiver\n            _ => r.tasks_rxen().write_value(1),\n        }\n    }\n\n    /// Cancel receiving packet\n    fn receive_cancel() {\n        let r = crate::pac::RADIO;\n        r.shorts().write(|_| {});\n        r.tasks_stop().write_value(1);\n        loop {\n            match r.state().read().state() {\n                RadioState::DISABLED | RadioState::RX_IDLE => break,\n                _ => (),\n            }\n        }\n        // DMA transfer may have been in progress so synchronize with its memory operations\n        dma_end_fence();\n    }\n\n    /// Receives one radio packet and copies its contents into the given `packet` buffer\n    ///\n    /// This methods returns the `Ok` variant if the CRC included the packet was successfully\n    /// validated by the hardware; otherwise it returns the `Err` variant. In either case, `packet`\n    /// will be updated with the received packet's data\n    pub async fn receive(&mut self, packet: &mut Packet) -> Result<(), Error> {\n        let s = self.state;\n        let r = self.r;\n\n        // Start the read\n        self.receive_start(packet);\n\n        let dropper = OnDrop::new(|| Self::receive_cancel());\n\n        self.clear_all_interrupts();\n        // wait until we have received something\n        core::future::poll_fn(|cx| {\n            s.event_waker.register(cx.waker());\n\n            if r.events_phyend().read() != 0 {\n                r.events_phyend().write_value(0);\n                trace!(\"RX done poll\");\n                return Poll::Ready(());\n            } else {\n                r.intenset().write(|w| w.set_phyend(true));\n            };\n\n            Poll::Pending\n        })\n        .await;\n\n        dma_end_fence();\n        dropper.defuse();\n\n        let crc = r.rxcrc().read().rxcrc() as u16;\n        if r.crcstatus().read().crcstatus() == vals::Crcstatus::CRCOK {\n            Ok(())\n        } else {\n            Err(Error::CrcFailed(crc))\n        }\n    }\n\n    /// Tries to send the given `packet`\n    ///\n    /// This method performs Clear Channel Assessment (CCA) first and sends the `packet` only if the\n    /// channel is observed to be *clear* (no transmission is currently ongoing), otherwise no\n    /// packet is transmitted and the `Err` variant is returned\n    ///\n    /// NOTE this method will *not* modify the `packet` argument. The mutable reference is used to\n    /// ensure the `packet` buffer is allocated in RAM, which is required by the RADIO peripheral\n    // NOTE we do NOT check the address of `packet` because the mutable reference ensures it's\n    // allocated in RAM\n    pub async fn try_send(&mut self, packet: &mut Packet) -> Result<(), Error> {\n        let s = self.state;\n        let r = self.r;\n\n        // enable radio to perform cca\n        self.receive_prepare();\n\n        /// transmit result\n        #[derive(Debug, Clone, Copy, PartialEq, Eq)]\n        #[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\n        pub enum TransmitResult {\n            /// Success\n            Success,\n            /// Clear channel assessment reported channel in use\n            ChannelInUse,\n        }\n\n        // Configure shortcuts\n        //\n        // The radio goes through following states when sending a 802.15.4 packet\n        //\n        // enable RX → ramp up RX → clear channel assessment (CCA) → CCA result\n        // CCA idle → enable TX → start TX → TX → end (PHYEND) → disabled\n        //\n        // CCA might end up in the event CCABUSY in which there will be no transmission\n        r.shorts().write(|w| {\n            w.set_rxready_ccastart(true);\n            w.set_ccaidle_txen(true);\n            w.set_txready_start(true);\n            w.set_ccabusy_disable(true);\n            w.set_phyend_disable(true);\n        });\n\n        // Set transmission buffer\n        self.set_buffer(packet.buffer.as_mut());\n\n        // the DMA transfer will start at some point after the following write operation so\n        // we place the compiler fence here\n        dma_start_fence();\n        // start CCA. In case the channel is clear, the data at packetptr will be sent automatically\n\n        match self.state() {\n            // Re-start receiver\n            RadioState::RX_IDLE => r.tasks_ccastart().write_value(1),\n            // Enable receiver\n            _ => r.tasks_rxen().write_value(1),\n        }\n\n        self.clear_all_interrupts();\n        let result = core::future::poll_fn(|cx| {\n            s.event_waker.register(cx.waker());\n\n            if r.events_phyend().read() != 0 {\n                r.events_phyend().write_value(0);\n                r.events_ccabusy().write_value(0);\n                trace!(\"TX done poll\");\n                return Poll::Ready(TransmitResult::Success);\n            } else if r.events_ccabusy().read() != 0 {\n                r.events_ccabusy().write_value(0);\n                trace!(\"TX no CCA\");\n                return Poll::Ready(TransmitResult::ChannelInUse);\n            }\n\n            r.intenset().write(|w| {\n                w.set_phyend(true);\n                w.set_ccabusy(true);\n            });\n\n            Poll::Pending\n        })\n        .await;\n\n        match result {\n            TransmitResult::Success => Ok(()),\n            TransmitResult::ChannelInUse => Err(Error::ChannelInUse),\n        }\n    }\n}\n\n/// An IEEE 802.15.4 packet\n///\n/// This `Packet` is a PHY layer packet. It's made up of the physical header (PHR) and the PSDU\n/// (PHY service data unit). The PSDU of this `Packet` will always include the MAC level CRC, AKA\n/// the FCS (Frame Control Sequence) -- the CRC is fully computed in hardware and automatically\n/// appended on transmission and verified on reception.\n///\n/// The API lets users modify the usable part (not the CRC) of the PSDU via the `deref` and\n/// `copy_from_slice` methods. These methods will automatically update the PHR.\n///\n/// See figure 119 in the Product Specification of the nRF52840 for more details\npub struct Packet {\n    buffer: [u8; Self::SIZE],\n}\n\n// See figure 124 in nRF52840-PS\nimpl Packet {\n    // for indexing purposes\n    const PHY_HDR: usize = 0;\n    const DATA: core::ops::RangeFrom<usize> = 1..;\n\n    /// Maximum amount of usable payload (CRC excluded) a single packet can contain, in bytes\n    pub const CAPACITY: u8 = 125;\n    const CRC: u8 = 2; // size of the CRC, which is *never* copied to / from RAM\n    const MAX_PSDU_LEN: u8 = Self::CAPACITY + Self::CRC;\n    const SIZE: usize = 1 /* PHR */ + Self::MAX_PSDU_LEN as usize;\n\n    /// Returns an empty packet (length = 0)\n    pub fn new() -> Self {\n        let mut packet = Self {\n            buffer: [0; Self::SIZE],\n        };\n        packet.set_len(0);\n        packet\n    }\n\n    /// Fills the packet payload with given `src` data\n    ///\n    /// # Panics\n    ///\n    /// This function panics if `src` is larger than `Self::CAPACITY`\n    pub fn copy_from_slice(&mut self, src: &[u8]) {\n        assert!(src.len() <= Self::CAPACITY as usize);\n        let len = src.len() as u8;\n        self.buffer[Self::DATA][..len as usize].copy_from_slice(&src[..len.into()]);\n        self.set_len(len);\n    }\n\n    /// Returns the size of this packet's payload\n    pub fn len(&self) -> u8 {\n        self.buffer[Self::PHY_HDR] - Self::CRC\n    }\n\n    /// Changes the size of the packet's payload\n    ///\n    /// # Panics\n    ///\n    /// This function panics if `len` is larger than `Self::CAPACITY`\n    pub fn set_len(&mut self, len: u8) {\n        assert!(len <= Self::CAPACITY);\n        self.buffer[Self::PHY_HDR] = len + Self::CRC;\n    }\n\n    /// Returns the LQI (Link Quality Indicator) of the received packet\n    ///\n    /// Note that the LQI is stored in the `Packet`'s internal buffer by the hardware so the value\n    /// returned by this method is only valid after a `Radio.recv` operation. Operations that\n    /// modify the `Packet`, like `copy_from_slice` or `set_len`+`deref_mut`, will overwrite the\n    /// stored LQI value.\n    ///\n    /// Also note that the hardware will *not* compute a LQI for packets smaller than 3 bytes so\n    /// this method will return an invalid value for those packets.\n    pub fn lqi(&self) -> u8 {\n        self.buffer[1 /* PHY_HDR */ + self.len() as usize /* data */]\n    }\n}\n\nimpl core::ops::Deref for Packet {\n    type Target = [u8];\n\n    fn deref(&self) -> &[u8] {\n        &self.buffer[Self::DATA][..self.len() as usize]\n    }\n}\n\nimpl core::ops::DerefMut for Packet {\n    fn deref_mut(&mut self) -> &mut [u8] {\n        let len = self.len();\n        &mut self.buffer[Self::DATA][..len as usize]\n    }\n}\n\n/// NOTE must be followed by a volatile write operation\nfn dma_start_fence() {\n    compiler_fence(Ordering::Release);\n}\n\n/// NOTE must be preceded by a volatile read operation\nfn dma_end_fence() {\n    compiler_fence(Ordering::Acquire);\n}\n\nmod errata {\n    pub fn post_power() {\n        // Workaround for anomaly 158\n        #[cfg(feature = \"_nrf5340-net\")]\n        for i in 0..32 {\n            let info = crate::pac::FICR.trimcnf(i);\n            let addr = info.addr().read();\n            if addr & 0xFFFF_F000 == crate::pac::RADIO.as_ptr() as u32 {\n                unsafe {\n                    (addr as *mut u32).write_volatile(info.data().read());\n                }\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "embassy-nrf/src/radio/mod.rs",
    "content": "//! Integrated 2.4 GHz Radio\n//!\n//! The 2.4 GHz radio transceiver is compatible with multiple radio standards\n//! such as 1Mbps, 2Mbps and Long Range Bluetooth Low Energy.\n\n#![macro_use]\n\n/// Bluetooth Low Energy Radio driver.\n#[cfg(any(\n    feature = \"nrf52811\",\n    feature = \"nrf52820\",\n    feature = \"nrf52833\",\n    feature = \"nrf52840\",\n    feature = \"_nrf5340-net\"\n))]\n/// IEEE 802.15.4\npub mod ieee802154;\n\nuse core::marker::PhantomData;\n\nuse embassy_hal_internal::PeripheralType;\nuse embassy_sync::waitqueue::AtomicWaker;\npub use pac::radio::vals::Txpower as TxPower;\n\nuse crate::{interrupt, pac};\n\n/// RADIO error.\n#[derive(Debug, Clone, Copy, PartialEq, Eq)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\n#[non_exhaustive]\npub enum Error {\n    /// Buffer was too long.\n    BufferTooLong,\n    /// Buffer was too short.\n    BufferTooShort,\n    /// The buffer is not in data RAM. It's most likely in flash, and nRF's DMA cannot access flash.\n    BufferNotInRAM,\n    /// Clear channel assessment reported channel in use\n    ChannelInUse,\n    /// CRC check failed\n    CrcFailed(u16),\n}\n\n/// Interrupt handler\npub struct InterruptHandler<T: Instance> {\n    _phantom: PhantomData<T>,\n}\n\nimpl<T: Instance> interrupt::typelevel::Handler<T::Interrupt> for InterruptHandler<T> {\n    unsafe fn on_interrupt() {\n        let r = T::regs();\n        let s = T::state();\n        // clear all interrupts\n        r.intenclr().write(|w| w.0 = 0xffff_ffff);\n        s.event_waker.wake();\n    }\n}\n\npub(crate) struct State {\n    /// end packet transmission or reception\n    event_waker: AtomicWaker,\n}\nimpl State {\n    pub(crate) const fn new() -> Self {\n        Self {\n            event_waker: AtomicWaker::new(),\n        }\n    }\n}\n\npub(crate) trait SealedInstance {\n    fn regs() -> crate::pac::radio::Radio;\n    fn state() -> &'static State;\n}\n\nmacro_rules! impl_radio {\n    ($type:ident, $pac_type:ident, $irq:ident) => {\n        impl crate::radio::SealedInstance for peripherals::$type {\n            fn regs() -> crate::pac::radio::Radio {\n                pac::$pac_type\n            }\n\n            #[allow(unused)]\n            fn state() -> &'static crate::radio::State {\n                static STATE: crate::radio::State = crate::radio::State::new();\n                &STATE\n            }\n        }\n        impl crate::radio::Instance for peripherals::$type {\n            type Interrupt = crate::interrupt::typelevel::$irq;\n        }\n    };\n}\n\n/// Radio peripheral instance.\n#[allow(private_bounds)]\npub trait Instance: SealedInstance + PeripheralType + 'static + Send {\n    /// Interrupt for this peripheral.\n    type Interrupt: interrupt::typelevel::Interrupt;\n}\n"
  },
  {
    "path": "embassy-nrf/src/reset.rs",
    "content": "//! Reset\n\n#![macro_use]\n\nuse bitflags::bitflags;\nuse nrf_pac::reset::regs::Resetreas;\n#[cfg(not(feature = \"_nrf5340-net\"))]\nuse nrf_pac::reset::vals::Forceoff;\n\nuse crate::chip::pac::RESET;\n\nbitflags! {\n    #[derive(Debug, Copy, Clone, PartialEq)]\n    /// Bitflag representation of the `RESETREAS` register\n    pub struct ResetReason: u32 {\n        /// Reset Pin\n        const RESETPIN = 1;\n        /// Application watchdog timer 0\n        const DOG0 = 1 << 1;\n        /// Application CTRL-AP\n        const CTRLAP = 1 << 2;\n        /// Application soft reset\n        const SREQ = 1 << 3;\n        /// Application CPU lockup\n        const LOCKUP = 1 << 4;\n        /// Wakeup from System OFF when wakeup is triggered by DETECT signal from GPIO\n        const OFF = 1 << 5;\n        /// Wakeup from System OFF when wakeup is triggered by ANADETECT signal from LPCOMP\n        const LPCOMP = 1 << 6;\n        /// Wakeup from System OFF when wakeup is triggered by entering the Debug Interface mode\n        const DIF = 1 << 7;\n        /// Network soft reset\n        #[cfg(feature = \"_nrf5340-net\")]\n        const LSREQ = 1 << 16;\n        /// Network CPU lockup\n        #[cfg(feature = \"_nrf5340-net\")]\n        const LLOCKUP = 1 << 17;\n        /// Network watchdog timer\n        #[cfg(feature = \"_nrf5340-net\")]\n        const LDOG = 1 << 18;\n        /// Force-OFF reset from application core\n        #[cfg(feature = \"_nrf5340-net\")]\n        const MFORCEOFF = 1 << 23;\n        /// Wakeup from System OFF mode due to NFC field being detected\n        const NFC = 1 << 24;\n        /// Application watchdog timer 1\n        const DOG1 = 1 << 25;\n        /// Wakeup from System OFF mode due to VBUS rising into valid range\n        const VBUS = 1 << 26;\n        /// Network CTRL-AP\n        #[cfg(feature = \"_nrf5340-net\")]\n        const LCTRLAP = 1 << 27;\n    }\n}\n\n/// Reads the bitflag of the reset reasons\npub fn read_reasons() -> ResetReason {\n    ResetReason::from_bits_retain(RESET.resetreas().read().0)\n}\n\n/// Resets the reset reasons\npub fn clear_reasons() {\n    RESET.resetreas().write(|w| *w = Resetreas(ResetReason::all().bits()));\n}\n\n/// Returns if the network core is held in reset\n#[cfg(not(feature = \"_nrf5340-net\"))]\npub fn network_core_held() -> bool {\n    RESET.network().forceoff().read().forceoff() == Forceoff::HOLD\n}\n\n/// Releases the network core from the FORCEOFF state\n#[cfg(not(feature = \"_nrf5340-net\"))]\npub fn release_network_core() {\n    RESET.network().forceoff().write(|w| w.set_forceoff(Forceoff::RELEASE));\n}\n\n/// Holds the network core in the FORCEOFF state\n#[cfg(not(feature = \"_nrf5340-net\"))]\npub fn hold_network_core() {\n    RESET.network().forceoff().write(|w| w.set_forceoff(Forceoff::HOLD));\n}\n"
  },
  {
    "path": "embassy-nrf/src/rng.rs",
    "content": "//! Random Number Generator (RNG) driver.\n\n#![macro_use]\n\nuse core::cell::{RefCell, RefMut};\nuse core::future::poll_fn;\nuse core::marker::PhantomData;\nuse core::ptr;\nuse core::task::Poll;\n\nuse critical_section::{CriticalSection, Mutex};\nuse embassy_hal_internal::drop::OnDrop;\nuse embassy_hal_internal::{Peri, PeripheralType};\nuse embassy_sync::waitqueue::WakerRegistration;\n\nuse crate::interrupt::typelevel::Interrupt;\nuse crate::mode::{Async, Blocking, Mode};\nuse crate::{interrupt, pac};\n\n/// Interrupt handler.\npub struct InterruptHandler<T: Instance> {\n    _phantom: PhantomData<T>,\n}\n\nimpl<T: Instance> interrupt::typelevel::Handler<T::Interrupt> for InterruptHandler<T> {\n    unsafe fn on_interrupt() {\n        let r = T::regs();\n\n        // Clear the event.\n        r.events_valrdy().write_value(0);\n\n        // Mutate the slice within a critical section,\n        // so that the future isn't dropped in between us loading the pointer and actually dereferencing it.\n        critical_section::with(|cs| {\n            let mut state = T::state().borrow_mut(cs);\n            // We need to make sure we haven't already filled the whole slice,\n            // in case the interrupt fired again before the executor got back to the future.\n            if !state.ptr.is_null() && state.ptr != state.end {\n                // If the future was dropped, the pointer would have been set to null,\n                // so we're still good to mutate the slice.\n                // The safety contract of `Rng::new` means that the future can't have been dropped\n                // without calling its destructor.\n                unsafe {\n                    *state.ptr = r.value().read().value();\n                    state.ptr = state.ptr.add(1);\n                }\n\n                if state.ptr == state.end {\n                    state.waker.wake();\n                }\n            }\n        });\n    }\n}\n\n/// A wrapper around an nRF RNG peripheral.\n///\n/// It has a non-blocking API, and a blocking api through `rand`.\npub struct Rng<'d, M: Mode> {\n    r: pac::rng::Rng,\n    state: &'static State,\n    _phantom: PhantomData<(&'d (), M)>,\n}\n\nimpl<'d> Rng<'d, Blocking> {\n    /// Creates a new RNG driver from the `RNG` peripheral and interrupt.\n    ///\n    /// SAFETY: The future returned from `fill_bytes` must not have its lifetime end without running its destructor,\n    /// e.g. using `mem::forget`.\n    ///\n    /// The synchronous API is safe.\n    pub fn new_blocking<T: Instance>(_rng: Peri<'d, T>) -> Self {\n        let this = Self {\n            r: T::regs(),\n            state: T::state(),\n            _phantom: PhantomData,\n        };\n\n        this.stop();\n\n        this\n    }\n}\n\nimpl<'d> Rng<'d, Async> {\n    /// Creates a new RNG driver from the `RNG` peripheral and interrupt.\n    ///\n    /// SAFETY: The future returned from `fill_bytes` must not have its lifetime end without running its destructor,\n    /// e.g. using `mem::forget`.\n    ///\n    /// The synchronous API is safe.\n    pub fn new<T: Instance>(\n        _rng: Peri<'d, T>,\n        _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd,\n    ) -> Self {\n        let this = Self {\n            r: T::regs(),\n            state: T::state(),\n            _phantom: PhantomData,\n        };\n\n        this.stop();\n        this.disable_irq();\n\n        T::Interrupt::unpend();\n        unsafe { T::Interrupt::enable() };\n\n        this\n    }\n\n    fn enable_irq(&self) {\n        self.r.intenset().write(|w| w.set_valrdy(true));\n    }\n\n    fn disable_irq(&self) {\n        self.r.intenclr().write(|w| w.set_valrdy(true));\n    }\n\n    /// Fill the buffer with random bytes.\n    pub async fn fill_bytes(&mut self, dest: &mut [u8]) {\n        if dest.is_empty() {\n            return; // Nothing to fill\n        }\n\n        let range = dest.as_mut_ptr_range();\n        let state = self.state;\n        // Even if we've preempted the interrupt, it can't preempt us again,\n        // so we don't need to worry about the order we write these in.\n        critical_section::with(|cs| {\n            let mut state = state.borrow_mut(cs);\n            state.ptr = range.start;\n            state.end = range.end;\n        });\n\n        self.enable_irq();\n        self.start();\n\n        let on_drop = OnDrop::new(|| {\n            self.stop();\n            self.disable_irq();\n\n            critical_section::with(|cs| {\n                let mut state = state.borrow_mut(cs);\n                state.ptr = ptr::null_mut();\n                state.end = ptr::null_mut();\n            });\n        });\n\n        poll_fn(|cx| {\n            critical_section::with(|cs| {\n                let mut s = state.borrow_mut(cs);\n                s.waker.register(cx.waker());\n                if s.ptr == s.end {\n                    // We're done.\n                    Poll::Ready(())\n                } else {\n                    Poll::Pending\n                }\n            })\n        })\n        .await;\n\n        // Trigger the teardown\n        drop(on_drop);\n    }\n}\n\nimpl<'d, M: Mode> Rng<'d, M> {\n    fn stop(&self) {\n        self.r.tasks_stop().write_value(1)\n    }\n\n    fn start(&self) {\n        self.r.tasks_start().write_value(1)\n    }\n\n    /// Enable or disable the RNG's bias correction.\n    ///\n    /// Bias correction removes any bias towards a '1' or a '0' in the bits generated.\n    /// However, this makes the generation of numbers slower.\n    ///\n    /// Defaults to disabled.\n    pub fn set_bias_correction(&self, enable: bool) {\n        self.r.config().write(|w| w.set_dercen(enable))\n    }\n\n    /// Fill the buffer with random bytes, blocking version.\n    pub fn blocking_fill_bytes(&mut self, dest: &mut [u8]) {\n        self.start();\n\n        for byte in dest.iter_mut() {\n            let regs = self.r;\n            while regs.events_valrdy().read() == 0 {}\n            regs.events_valrdy().write_value(0);\n            *byte = regs.value().read().value();\n        }\n\n        self.stop();\n    }\n\n    /// Generate a random u32\n    pub fn blocking_next_u32(&mut self) -> u32 {\n        let mut bytes = [0; 4];\n        self.blocking_fill_bytes(&mut bytes);\n        // We don't care about the endianness, so just use the native one.\n        u32::from_ne_bytes(bytes)\n    }\n\n    /// Generate a random u64\n    pub fn blocking_next_u64(&mut self) -> u64 {\n        let mut bytes = [0; 8];\n        self.blocking_fill_bytes(&mut bytes);\n        u64::from_ne_bytes(bytes)\n    }\n}\n\nimpl<'d, M: Mode> Drop for Rng<'d, M> {\n    fn drop(&mut self) {\n        self.stop();\n        critical_section::with(|cs| {\n            let mut state = self.state.borrow_mut(cs);\n            state.ptr = ptr::null_mut();\n            state.end = ptr::null_mut();\n        });\n    }\n}\n\nimpl<'d, M: Mode> rand_core_06::RngCore for Rng<'d, M> {\n    fn fill_bytes(&mut self, dest: &mut [u8]) {\n        self.blocking_fill_bytes(dest);\n    }\n    fn next_u32(&mut self) -> u32 {\n        self.blocking_next_u32()\n    }\n    fn next_u64(&mut self) -> u64 {\n        self.blocking_next_u64()\n    }\n    fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), rand_core_06::Error> {\n        self.blocking_fill_bytes(dest);\n        Ok(())\n    }\n}\n\nimpl<'d, M: Mode> rand_core_06::CryptoRng for Rng<'d, M> {}\n\nimpl<'d, M: Mode> rand_core_09::RngCore for Rng<'d, M> {\n    fn fill_bytes(&mut self, dest: &mut [u8]) {\n        self.blocking_fill_bytes(dest);\n    }\n    fn next_u32(&mut self) -> u32 {\n        self.blocking_next_u32()\n    }\n    fn next_u64(&mut self) -> u64 {\n        self.blocking_next_u64()\n    }\n}\n\nimpl<'d, M: Mode> rand_core_09::CryptoRng for Rng<'d, M> {}\n\n/// Peripheral static state\npub(crate) struct State {\n    inner: Mutex<RefCell<InnerState>>,\n}\n\nstruct InnerState {\n    ptr: *mut u8,\n    end: *mut u8,\n    waker: WakerRegistration,\n}\n\nunsafe impl Send for InnerState {}\n\nimpl State {\n    pub(crate) const fn new() -> Self {\n        Self {\n            inner: Mutex::new(RefCell::new(InnerState::new())),\n        }\n    }\n\n    fn borrow_mut<'cs>(&'cs self, cs: CriticalSection<'cs>) -> RefMut<'cs, InnerState> {\n        self.inner.borrow(cs).borrow_mut()\n    }\n}\n\nimpl InnerState {\n    const fn new() -> Self {\n        Self {\n            ptr: ptr::null_mut(),\n            end: ptr::null_mut(),\n            waker: WakerRegistration::new(),\n        }\n    }\n}\n\npub(crate) trait SealedInstance {\n    fn regs() -> pac::rng::Rng;\n    fn state() -> &'static State;\n}\n\n/// RNG peripheral instance.\n#[allow(private_bounds)]\npub trait Instance: SealedInstance + PeripheralType + 'static + Send {\n    /// Interrupt for this peripheral.\n    type Interrupt: interrupt::typelevel::Interrupt;\n}\n\nmacro_rules! impl_rng {\n    ($type:ident, $pac_type:ident, $irq:ident) => {\n        impl crate::rng::SealedInstance for peripherals::$type {\n            fn regs() -> crate::pac::rng::Rng {\n                pac::$pac_type\n            }\n            fn state() -> &'static crate::rng::State {\n                static STATE: crate::rng::State = crate::rng::State::new();\n                &STATE\n            }\n        }\n        impl crate::rng::Instance for peripherals::$type {\n            type Interrupt = crate::interrupt::typelevel::$irq;\n        }\n    };\n}\n"
  },
  {
    "path": "embassy-nrf/src/rramc.rs",
    "content": "//! Resistive Random-Access Memory Controller driver.\n\nuse core::marker::PhantomData;\nuse core::{ptr, slice};\n\nuse embedded_storage::nor_flash::{\n    ErrorType, MultiwriteNorFlash, NorFlash, NorFlashError, NorFlashErrorKind, ReadNorFlash,\n};\n\nuse crate::peripherals::RRAMC;\nuse crate::{Peri, pac};\n\n/// Unbuffered RRAMC mode.\npub struct Unbuffered;\n\n/// Buffered RRAMC mode.\npub struct Buffered<const BUFFER_SIZE_BYTES: usize>;\n\ntrait SealedRramMode {}\n\n/// Operating modes for RRAMC\n#[allow(private_bounds)]\npub trait RramMode: SealedRramMode {}\n\nimpl SealedRramMode for Unbuffered {}\nimpl RramMode for Unbuffered {}\nimpl<const BUFFER_SIZE_BYTES: usize> SealedRramMode for Buffered<BUFFER_SIZE_BYTES> {}\nimpl<const BUFFER_SIZE_BYTES: usize> RramMode for Buffered<BUFFER_SIZE_BYTES> {}\n\n//\n// Export Nvmc alias and page size for downstream compatibility\n//\n/// RRAM-backed `Nvmc` compatibile driver.\npub type Nvmc<'d> = Rramc<'d, Unbuffered>;\n/// Emulated page size.  RRAM does not use pages. This exists only for downstream compatibility.\npub const PAGE_SIZE: usize = 4096;\n\n// In bytes, one line is 128 bits\nconst WRITE_LINE_SIZE: usize = 16;\n\n/// Size of RRAM flash in bytes.\npub const FLASH_SIZE: usize = crate::chip::FLASH_SIZE;\n\n/// Error type for RRAMC operations.\n#[derive(Debug, Copy, Clone, PartialEq, Eq)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub enum Error {\n    /// Operation using a location not in flash.\n    OutOfBounds,\n    /// Unaligned operation or using unaligned buffers.\n    Unaligned,\n}\n\nimpl NorFlashError for Error {\n    fn kind(&self) -> NorFlashErrorKind {\n        match self {\n            Self::OutOfBounds => NorFlashErrorKind::OutOfBounds,\n            Self::Unaligned => NorFlashErrorKind::NotAligned,\n        }\n    }\n}\n\n/// Resistive Random-Access Memory Controller (RRAMC) that implements the `embedded-storage`\n/// traits.\npub struct Rramc<'d, MODE: RramMode> {\n    _p: Peri<'d, RRAMC>,\n    _d: PhantomData<MODE>,\n}\n\nimpl<'d> Rramc<'d, Unbuffered> {\n    /// Create Rramc driver.\n    pub fn new(_p: Peri<'d, RRAMC>) -> Rramc<'d, Unbuffered> {\n        Self { _p, _d: PhantomData }\n    }\n}\n\nimpl<'d, const BUFFER_SIZE_BYTES: usize> Rramc<'d, Buffered<BUFFER_SIZE_BYTES>> {\n    /// Create Rramc driver.\n    pub fn new_buffered(_p: Peri<'d, RRAMC>) -> Rramc<'d, Buffered<BUFFER_SIZE_BYTES>> {\n        assert!(BUFFER_SIZE_BYTES > 0 && BUFFER_SIZE_BYTES <= 512);\n        Self { _p, _d: PhantomData }\n    }\n}\n\nimpl<'d, MODE: RramMode> Rramc<'d, MODE> {\n    fn regs() -> pac::rramc::Rramc {\n        pac::RRAMC\n    }\n\n    fn wait_ready(&mut self) {\n        let p = Self::regs();\n        while !p.ready().read().ready() {}\n    }\n\n    fn enable_read(&self) {\n        Self::regs().config().write(|w| w.set_wen(false));\n    }\n\n    fn finish_write(&mut self) {\n        self.enable_read();\n        self.wait_ready();\n    }\n}\n\nimpl<'d> Rramc<'d, Unbuffered> {\n    fn wait_ready_write(&mut self) {\n        let p = Self::regs();\n        while !p.readynext().read().readynext() {}\n    }\n\n    fn enable_write(&self) {\n        Self::regs().config().write(|w| {\n            w.set_wen(true);\n            w.set_writebufsize(pac::rramc::vals::Writebufsize::UNBUFFERED)\n        });\n    }\n}\n\nimpl<'d, const SIZE: usize> Rramc<'d, Buffered<SIZE>> {\n    fn wait_ready_write(&mut self) {\n        let p = Self::regs();\n        while !p.readynext().read().readynext() {}\n        while !p.bufstatus().writebufempty().read().empty() {}\n    }\n\n    fn commit(&self) {\n        let p = Self::regs();\n        p.tasks_commitwritebuf().write_value(1);\n        while !p.bufstatus().writebufempty().read().empty() {}\n    }\n\n    fn enable_write(&self) {\n        Self::regs().config().write(|w| {\n            w.set_wen(true);\n            w.set_writebufsize(pac::rramc::vals::Writebufsize::from_bits(SIZE as _))\n        });\n    }\n}\n\n//\n// RRAM is not NOR flash, but many crates require embedded-storage NorFlash traits. We therefore\n// implement the traits for downstream compatibility.\n//\n\nimpl<'d> MultiwriteNorFlash for Rramc<'d, Unbuffered> {}\n\nimpl<'d> ErrorType for Rramc<'d, Unbuffered> {\n    type Error = Error;\n}\n\nimpl<'d> ReadNorFlash for Rramc<'d, Unbuffered> {\n    const READ_SIZE: usize = 1;\n\n    fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Self::Error> {\n        if offset as usize >= FLASH_SIZE || offset as usize + bytes.len() > FLASH_SIZE {\n            return Err(Error::OutOfBounds);\n        }\n\n        let flash_data = unsafe { slice::from_raw_parts(offset as *const u8, bytes.len()) };\n        bytes.copy_from_slice(flash_data);\n        Ok(())\n    }\n\n    fn capacity(&self) -> usize {\n        FLASH_SIZE\n    }\n}\n\nimpl<'d> NorFlash for Rramc<'d, Unbuffered> {\n    const WRITE_SIZE: usize = WRITE_LINE_SIZE;\n    const ERASE_SIZE: usize = PAGE_SIZE;\n\n    // RRAM can overwrite in-place, so emulate page erases by overwriting the page bytes with 0xFF.\n    fn erase(&mut self, from: u32, to: u32) -> Result<(), Self::Error> {\n        if to < from || to as usize > FLASH_SIZE {\n            return Err(Error::OutOfBounds);\n        }\n        if from as usize % Self::ERASE_SIZE != 0 || to as usize % Self::ERASE_SIZE != 0 {\n            return Err(Error::Unaligned);\n        }\n\n        self.enable_write();\n        self.wait_ready();\n\n        // Treat each emulated page separately so callers can rely on post‑erase read‑back\n        // returning 0xFF just like on real NOR flash.\n        let buf = [0xFFu8; Self::WRITE_SIZE];\n        for page_addr in (from..to).step_by(Self::ERASE_SIZE) {\n            let page_end = page_addr + Self::ERASE_SIZE as u32;\n            for line_addr in (page_addr..page_end).step_by(Self::WRITE_SIZE) {\n                unsafe {\n                    let src = buf.as_ptr() as *const u32;\n                    let dst = line_addr as *mut u32;\n                    for i in 0..(Self::WRITE_SIZE / 4) {\n                        core::ptr::write_volatile(dst.add(i), core::ptr::read_unaligned(src.add(i)));\n                    }\n                }\n                self.wait_ready_write();\n            }\n        }\n        self.finish_write();\n        Ok(())\n    }\n\n    fn write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Self::Error> {\n        if offset as usize + bytes.len() > FLASH_SIZE {\n            return Err(Error::OutOfBounds);\n        }\n        if offset as usize % Self::WRITE_SIZE != 0 || bytes.len() % Self::WRITE_SIZE != 0 {\n            return Err(Error::Unaligned);\n        }\n\n        self.enable_write();\n        self.wait_ready();\n\n        unsafe {\n            let p_src = bytes.as_ptr() as *const u32;\n            let p_dst = offset as *mut u32;\n            let words = bytes.len() / 4;\n            for i in 0..words {\n                let w = ptr::read_unaligned(p_src.add(i));\n                ptr::write_volatile(p_dst.add(i), w);\n                if (i + 1) % (Self::WRITE_SIZE / 4) == 0 {\n                    self.wait_ready_write();\n                }\n            }\n        }\n\n        self.enable_read();\n        self.wait_ready();\n        Ok(())\n    }\n}\n\nimpl<'d, const BUFFER_SIZE_BYTES: usize> MultiwriteNorFlash for Rramc<'d, Buffered<BUFFER_SIZE_BYTES>> {}\n\nimpl<'d, const BUFFER_SIZE_BYTES: usize> ErrorType for Rramc<'d, Buffered<BUFFER_SIZE_BYTES>> {\n    type Error = Error;\n}\n\nimpl<'d, const BUFFER_SIZE_BYTES: usize> ReadNorFlash for Rramc<'d, Buffered<BUFFER_SIZE_BYTES>> {\n    const READ_SIZE: usize = 1;\n\n    fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Self::Error> {\n        if offset as usize >= FLASH_SIZE || offset as usize + bytes.len() > FLASH_SIZE {\n            return Err(Error::OutOfBounds);\n        }\n\n        let flash_data = unsafe { slice::from_raw_parts(offset as *const u8, bytes.len()) };\n        bytes.copy_from_slice(flash_data);\n        Ok(())\n    }\n\n    fn capacity(&self) -> usize {\n        FLASH_SIZE\n    }\n}\n\nimpl<'d, const BUFFER_SIZE_BYTES: usize> NorFlash for Rramc<'d, Buffered<BUFFER_SIZE_BYTES>> {\n    const WRITE_SIZE: usize = WRITE_LINE_SIZE;\n    const ERASE_SIZE: usize = PAGE_SIZE;\n\n    // RRAM can overwrite in-place, so emulate page erases by overwriting the page bytes with 0xFF.\n    fn erase(&mut self, from: u32, to: u32) -> Result<(), Self::Error> {\n        if to < from || to as usize > FLASH_SIZE {\n            return Err(Error::OutOfBounds);\n        }\n        if from as usize % Self::ERASE_SIZE != 0 || to as usize % Self::ERASE_SIZE != 0 {\n            return Err(Error::Unaligned);\n        }\n\n        self.enable_write();\n        self.wait_ready();\n\n        // Treat each emulated page separately so callers can rely on post‑erase read‑back\n        // returning 0xFF just like on real NOR flash.\n        let buf = [0xFFu8; BUFFER_SIZE_BYTES];\n        for page_addr in (from..to).step_by(Self::ERASE_SIZE) {\n            let page_end = page_addr + Self::ERASE_SIZE as u32;\n            for line_addr in (page_addr..page_end).step_by(BUFFER_SIZE_BYTES) {\n                unsafe {\n                    let src = buf.as_ptr() as *const u32;\n                    let dst = line_addr as *mut u32;\n                    for i in 0..(Self::WRITE_SIZE / 4) {\n                        core::ptr::write_volatile(dst.add(i), core::ptr::read_unaligned(src.add(i)));\n                    }\n                }\n                self.wait_ready_write();\n            }\n        }\n        self.commit();\n        self.finish_write();\n        Ok(())\n    }\n\n    fn write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Self::Error> {\n        if offset as usize + bytes.len() > FLASH_SIZE {\n            return Err(Error::OutOfBounds);\n        }\n        if offset as usize % Self::WRITE_SIZE != 0 || bytes.len() % Self::WRITE_SIZE != 0 {\n            return Err(Error::Unaligned);\n        }\n\n        self.enable_write();\n        self.wait_ready();\n\n        unsafe {\n            let p_src = bytes.as_ptr() as *const u32;\n            let p_dst = offset as *mut u32;\n            let words = bytes.len() / 4;\n            for i in 0..words {\n                let w = ptr::read_unaligned(p_src.add(i));\n                ptr::write_volatile(p_dst.add(i), w);\n                if (i + 1) % (Self::WRITE_SIZE / 4) == 0 {\n                    self.wait_ready_write();\n                }\n            }\n        }\n\n        self.commit();\n        self.enable_read();\n        self.wait_ready();\n        Ok(())\n    }\n}\n"
  },
  {
    "path": "embassy-nrf/src/rtc.rs",
    "content": "//! Low-level RTC driver.\n\n#![macro_use]\n\nuse core::marker::PhantomData;\n\nuse embassy_hal_internal::interrupt::InterruptExt;\nuse embassy_hal_internal::{Peri, PeripheralType};\n\nuse crate::interrupt::typelevel::Interrupt as _;\nuse crate::{interrupt, pac};\n\n/// Prescaler has an invalid value which exceeds 12 bits.\n#[derive(Debug, PartialEq, Eq)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub struct PrescalerOutOfRangeError(u32);\n\n/// Compare value has an invalid value which exceeds 24 bits.\n#[derive(Debug, PartialEq, Eq)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub struct CompareOutOfRangeError(u32);\n\n/// Interrupts/Events that can be generated by the RTCn peripheral.\n#[derive(Debug, Copy, Clone, PartialEq, Eq)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub enum Interrupt {\n    /// Tick interrupt.\n    Tick,\n    /// Overflow interrupt.\n    Overflow,\n    /// Compare 0 interrupt.\n    Compare0,\n    /// Compare 1 interrupt.\n    Compare1,\n    /// Compare 2 interrupt.\n    Compare2,\n    /// Compare 3 interrupt. Only implemented for RTC1 and RTC2.\n    Compare3,\n}\n\n/// Compare registers available on the RTCn.\n#[derive(Debug, Copy, Clone)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub enum CompareChannel {\n    /// Channel 0\n    _0,\n    /// Channel 1\n    _1,\n    /// Channel 2\n    _2,\n    /// Channel 3. Only implemented for RTC1 and RTC2.\n    _3,\n}\n\npub(crate) trait SealedInstance {\n    fn regs() -> pac::rtc::Rtc;\n}\n\n/// Basic RTC instance.\n#[allow(private_bounds)]\npub trait Instance: SealedInstance + PeripheralType + 'static + Send {\n    /// Interrupt for this peripheral.\n    type Interrupt: crate::interrupt::typelevel::Interrupt;\n\n    /// Unsafely create a peripheral instance.\n    ///\n    /// # Safety\n    ///\n    /// Potentially allows to create multiple instances of the driver for the same peripheral\n    /// which can lead to undefined behavior.\n    unsafe fn steal() -> Peri<'static, Self>;\n}\n\nmacro_rules! impl_rtc {\n    ($type:ident, $pac_type:ident, $irq:ident) => {\n        impl crate::rtc::SealedInstance for peripherals::$type {\n            #[inline]\n            fn regs() -> pac::rtc::Rtc {\n                unsafe { pac::rtc::Rtc::from_ptr(pac::$pac_type.as_ptr()) }\n            }\n        }\n\n        impl crate::rtc::Instance for peripherals::$type {\n            type Interrupt = crate::interrupt::typelevel::$irq;\n\n            unsafe fn steal() -> embassy_hal_internal::Peri<'static, Self> {\n                unsafe { peripherals::$type::steal() }\n            }\n        }\n    };\n}\n\n/// nRF RTC driver.\npub struct Rtc<'d> {\n    r: pac::rtc::Rtc,\n    irq: interrupt::Interrupt,\n    _phantom: PhantomData<&'d ()>,\n}\n\nimpl<'d> Rtc<'d> {\n    /// Create a new `Rtc` driver.\n    ///\n    /// fRTC \\[Hz\\] = 32_768 / (`prescaler` + 1 )\n    pub fn new<T: Instance>(_rtc: Peri<'d, T>, prescaler: u32) -> Result<Self, PrescalerOutOfRangeError> {\n        if prescaler >= (1 << 12) {\n            return Err(PrescalerOutOfRangeError(prescaler));\n        }\n\n        T::regs().prescaler().write(|w| w.set_prescaler(prescaler as u16));\n        Ok(Self {\n            r: T::regs(),\n            irq: T::Interrupt::IRQ,\n            _phantom: PhantomData,\n        })\n    }\n\n    /// Create a new `Rtc` driver, configuring it to run at the given frequency.\n    pub fn new_for_freq<T: Instance>(rtc: Peri<'d, T>, freq_hz: u32) -> Result<Self, PrescalerOutOfRangeError> {\n        let prescaler = (32_768 / freq_hz).saturating_sub(1);\n        Self::new(rtc, prescaler)\n    }\n\n    /// Steal the RTC peripheral, without checking if it's already taken.\n    ///\n    /// # Safety\n    ///\n    /// Potentially allows to create multiple instances of the driver for the same peripheral\n    /// which can lead to undefined behavior.\n    pub unsafe fn steal<T: Instance>() -> Self {\n        Self {\n            r: T::regs(),\n            irq: T::Interrupt::IRQ,\n            _phantom: PhantomData,\n        }\n    }\n\n    /// Direct access to the RTC registers.\n    #[cfg(feature = \"unstable-pac\")]\n    #[inline]\n    pub fn regs(&mut self) -> pac::rtc::Rtc {\n        self.r\n    }\n\n    /// Enable the RTC.\n    #[inline]\n    pub fn enable(&mut self) {\n        self.r.tasks_start().write_value(1);\n    }\n\n    /// Disable the RTC.\n    #[inline]\n    pub fn disable(&mut self) {\n        self.r.tasks_stop().write_value(1);\n    }\n\n    /// Enables interrupts for the given [Interrupt] source.\n    ///\n    /// Optionally also enables the interrupt in the NVIC.\n    pub fn enable_interrupt(&mut self, int: Interrupt, enable_in_nvic: bool) {\n        let regs = self.r;\n        match int {\n            Interrupt::Tick => regs.intenset().write(|w| w.set_tick(true)),\n            Interrupt::Overflow => regs.intenset().write(|w| w.set_ovrflw(true)),\n            Interrupt::Compare0 => regs.intenset().write(|w| w.set_compare(0, true)),\n            Interrupt::Compare1 => regs.intenset().write(|w| w.set_compare(1, true)),\n            Interrupt::Compare2 => regs.intenset().write(|w| w.set_compare(2, true)),\n            Interrupt::Compare3 => regs.intenset().write(|w| w.set_compare(3, true)),\n        }\n        if enable_in_nvic {\n            unsafe { self.irq.enable() };\n        }\n    }\n\n    /// Disables interrupts for the given [Interrupt] source.\n    ///\n    /// Optionally also disables the interrupt in the NVIC.\n    pub fn disable_interrupt(&mut self, int: Interrupt, disable_in_nvic: bool) {\n        let regs = self.r;\n        match int {\n            Interrupt::Tick => regs.intenclr().write(|w| w.set_tick(true)),\n            Interrupt::Overflow => regs.intenclr().write(|w| w.set_ovrflw(true)),\n            Interrupt::Compare0 => regs.intenclr().write(|w| w.set_compare(0, true)),\n            Interrupt::Compare1 => regs.intenclr().write(|w| w.set_compare(1, true)),\n            Interrupt::Compare2 => regs.intenclr().write(|w| w.set_compare(2, true)),\n            Interrupt::Compare3 => regs.intenclr().write(|w| w.set_compare(3, true)),\n        }\n        if disable_in_nvic {\n            self.irq.disable();\n        }\n    }\n\n    /// Enable the generation of a hardware event from a given stimulus.\n    pub fn enable_event(&mut self, evt: Interrupt) {\n        let regs = self.r;\n        match evt {\n            Interrupt::Tick => regs.evtenset().write(|w| w.set_tick(true)),\n            Interrupt::Overflow => regs.evtenset().write(|w| w.set_ovrflw(true)),\n            Interrupt::Compare0 => regs.evtenset().write(|w| w.set_compare(0, true)),\n            Interrupt::Compare1 => regs.evtenset().write(|w| w.set_compare(1, true)),\n            Interrupt::Compare2 => regs.evtenset().write(|w| w.set_compare(2, true)),\n            Interrupt::Compare3 => regs.evtenset().write(|w| w.set_compare(3, true)),\n        }\n    }\n\n    /// Disable the generation of a hardware event from a given stimulus.\n    pub fn disable_event(&mut self, evt: Interrupt) {\n        let regs = self.r;\n        match evt {\n            Interrupt::Tick => regs.evtenclr().write(|w| w.set_tick(true)),\n            Interrupt::Overflow => regs.evtenclr().write(|w| w.set_ovrflw(true)),\n            Interrupt::Compare0 => regs.evtenclr().write(|w| w.set_compare(0, true)),\n            Interrupt::Compare1 => regs.evtenclr().write(|w| w.set_compare(1, true)),\n            Interrupt::Compare2 => regs.evtenclr().write(|w| w.set_compare(2, true)),\n            Interrupt::Compare3 => regs.evtenclr().write(|w| w.set_compare(3, true)),\n        }\n    }\n\n    /// Resets the given event.\n    pub fn reset_event(&mut self, evt: Interrupt) {\n        let regs = self.r;\n        match evt {\n            Interrupt::Tick => regs.events_tick().write_value(0),\n            Interrupt::Overflow => regs.events_ovrflw().write_value(0),\n            Interrupt::Compare0 => regs.events_compare(0).write_value(0),\n            Interrupt::Compare1 => regs.events_compare(1).write_value(0),\n            Interrupt::Compare2 => regs.events_compare(2).write_value(0),\n            Interrupt::Compare3 => regs.events_compare(3).write_value(0),\n        }\n    }\n\n    /// Checks if the given event has been triggered.\n    pub fn is_event_triggered(&self, evt: Interrupt) -> bool {\n        let regs = self.r;\n        let val = match evt {\n            Interrupt::Tick => regs.events_tick().read(),\n            Interrupt::Overflow => regs.events_ovrflw().read(),\n            Interrupt::Compare0 => regs.events_compare(0).read(),\n            Interrupt::Compare1 => regs.events_compare(1).read(),\n            Interrupt::Compare2 => regs.events_compare(2).read(),\n            Interrupt::Compare3 => regs.events_compare(3).read(),\n        };\n        val == 1\n    }\n\n    /// Set the compare value of a given register. The compare registers have a width\n    /// of 24 bits.\n    pub fn set_compare(&mut self, reg: CompareChannel, val: u32) -> Result<(), CompareOutOfRangeError> {\n        if val >= (1 << 24) {\n            return Err(CompareOutOfRangeError(val));\n        }\n\n        let reg = match reg {\n            CompareChannel::_0 => 0,\n            CompareChannel::_1 => 1,\n            CompareChannel::_2 => 2,\n            CompareChannel::_3 => 3,\n        };\n\n        self.r.cc(reg).write(|w| w.set_compare(val));\n        Ok(())\n    }\n\n    /// Clear the Real Time Counter.\n    #[inline]\n    pub fn clear(&self) {\n        self.r.tasks_clear().write_value(1);\n    }\n\n    /// Obtain the current value of the Real Time Counter, 24 bits of range.\n    #[inline]\n    pub fn read(&self) -> u32 {\n        self.r.counter().read().counter()\n    }\n}\n"
  },
  {
    "path": "embassy-nrf/src/saadc.rs",
    "content": "//! Successive Approximation Analog-to-Digital Converter (SAADC) driver.\n\n#![macro_use]\n\nuse core::future::poll_fn;\nuse core::marker::PhantomData;\nuse core::sync::atomic::{Ordering, compiler_fence};\nuse core::task::Poll;\n\nuse embassy_hal_internal::drop::OnDrop;\nuse embassy_hal_internal::{Peri, impl_peripheral};\nuse embassy_sync::waitqueue::AtomicWaker;\n#[cfg(not(feature = \"_nrf54l\"))]\npub(crate) use vals::Psel as InputChannel;\n\nuse crate::interrupt::InterruptExt;\nuse crate::pac::saadc::vals;\nuse crate::ppi::{ConfigurableChannel, Event, Ppi, Task};\nuse crate::timer::{Frequency, Instance as TimerInstance, Timer};\nuse crate::{interrupt, pac, peripherals};\n\n/// SAADC error\n#[derive(Debug, Clone, Copy, PartialEq, Eq)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\n#[non_exhaustive]\npub enum Error {}\n\n/// Interrupt handler.\npub struct InterruptHandler {\n    _private: (),\n}\n\nimpl interrupt::typelevel::Handler<interrupt::typelevel::SAADC> for InterruptHandler {\n    unsafe fn on_interrupt() {\n        let r = pac::SAADC;\n\n        if r.events_calibratedone().read() != 0 {\n            r.intenclr().write(|w| w.set_calibratedone(true));\n            WAKER.wake();\n        }\n\n        if r.events_end().read() != 0 {\n            r.intenclr().write(|w| w.set_end(true));\n            WAKER.wake();\n        }\n\n        if r.events_started().read() != 0 {\n            r.intenclr().write(|w| w.set_started(true));\n            WAKER.wake();\n        }\n    }\n}\n\nstatic WAKER: AtomicWaker = AtomicWaker::new();\n\n/// Used to configure the SAADC peripheral.\n///\n/// See the `Default` impl for suitable default values.\n#[non_exhaustive]\npub struct Config {\n    /// Output resolution in bits.\n    pub resolution: Resolution,\n    /// Average 2^`oversample` input samples before transferring the result into memory.\n    pub oversample: Oversample,\n}\n\nimpl Default for Config {\n    /// Default configuration for single channel sampling.\n    fn default() -> Self {\n        Self {\n            resolution: Resolution::_12BIT,\n            oversample: Oversample::BYPASS,\n        }\n    }\n}\n\n/// Used to configure an individual SAADC peripheral channel.\n///\n/// Construct using the `single_ended` or `differential` methods.  These provide sensible defaults\n/// for the public fields, which can be overridden if required.\n#[non_exhaustive]\npub struct ChannelConfig<'d> {\n    /// Reference voltage of the SAADC input.\n    pub reference: Reference,\n    /// Gain used to control the effective input range of the SAADC.\n    pub gain: Gain,\n    /// Positive channel resistor control.\n    #[cfg(not(feature = \"_nrf54l\"))]\n    pub resistor: Resistor,\n    /// Acquisition time in microseconds.\n    pub time: Time,\n    /// Positive channel to sample\n    p_channel: AnyInput<'d>,\n    /// An optional negative channel to sample\n    n_channel: Option<AnyInput<'d>>,\n}\n\nimpl<'d> ChannelConfig<'d> {\n    /// Default configuration for single ended channel sampling.\n    pub fn single_ended(input: impl Input + 'd) -> Self {\n        Self {\n            reference: Reference::INTERNAL,\n            #[cfg(not(feature = \"_nrf54l\"))]\n            gain: Gain::GAIN1_6,\n            #[cfg(feature = \"_nrf54l\")]\n            gain: Gain::GAIN2_8,\n            #[cfg(not(feature = \"_nrf54l\"))]\n            resistor: Resistor::BYPASS,\n            time: Time::_10US,\n            p_channel: input.degrade_saadc(),\n            n_channel: None,\n        }\n    }\n    /// Default configuration for differential channel sampling.\n    pub fn differential(p_input: impl Input + 'd, n_input: impl Input + 'd) -> Self {\n        Self {\n            reference: Reference::INTERNAL,\n            #[cfg(not(feature = \"_nrf54l\"))]\n            gain: Gain::GAIN1_6,\n            #[cfg(feature = \"_nrf54l\")]\n            gain: Gain::GAIN2_8,\n            #[cfg(not(feature = \"_nrf54l\"))]\n            resistor: Resistor::BYPASS,\n            time: Time::_10US,\n            p_channel: p_input.degrade_saadc(),\n            n_channel: Some(n_input.degrade_saadc()),\n        }\n    }\n}\n\nconst CNT_UNIT: usize = if cfg!(feature = \"_nrf54l\") { 2 } else { 1 };\n\n/// Value returned by the SAADC callback, deciding what happens next.\n#[derive(PartialEq)]\npub enum CallbackResult {\n    /// The SAADC should keep sampling and calling the callback.\n    Continue,\n    /// The SAADC should stop sampling, and return.\n    Stop,\n}\n\n/// One-shot and continuous SAADC.\npub struct Saadc<'d, const N: usize> {\n    _p: Peri<'d, peripherals::SAADC>,\n}\n\nimpl<'d, const N: usize> Saadc<'d, N> {\n    /// Create a new SAADC driver.\n    pub fn new(\n        saadc: Peri<'d, peripherals::SAADC>,\n        _irq: impl interrupt::typelevel::Binding<interrupt::typelevel::SAADC, InterruptHandler> + 'd,\n        config: Config,\n        channel_configs: [ChannelConfig; N],\n    ) -> Self {\n        let r = pac::SAADC;\n\n        let Config { resolution, oversample } = config;\n\n        // Configure channels\n        r.enable().write(|w| w.set_enable(true));\n        r.resolution().write(|w| w.set_val(resolution.into()));\n        r.oversample().write(|w| w.set_oversample(oversample.into()));\n\n        for (i, cc) in channel_configs.iter().enumerate() {\n            #[cfg(not(feature = \"_nrf54l\"))]\n            r.ch(i).pselp().write(|w| w.set_pselp(cc.p_channel.channel()));\n            #[cfg(feature = \"_nrf54l\")]\n            r.ch(i).pselp().write(|w| {\n                w.set_port(cc.p_channel.port());\n                w.set_pin(cc.p_channel.pin());\n                w.set_internal(cc.p_channel.internal());\n                w.set_connect(cc.p_channel.connect());\n            });\n            if let Some(n_channel) = &cc.n_channel {\n                #[cfg(not(feature = \"_nrf54l\"))]\n                r.ch(i).pseln().write(|w| w.set_pseln(n_channel.channel()));\n                #[cfg(feature = \"_nrf54l\")]\n                r.ch(i).pseln().write(|w| {\n                    w.set_port(n_channel.port());\n                    w.set_pin(n_channel.pin());\n                    w.set_connect(n_channel.connect().to_bits().into());\n                });\n            }\n            r.ch(i).config().write(|w| {\n                w.set_refsel(cc.reference.into());\n                w.set_gain(cc.gain.into());\n                w.set_tacq(cc.time.into());\n                #[cfg(feature = \"_nrf54l\")]\n                w.set_tconv(7); // 7 is the default from the Nordic C driver\n                w.set_mode(match cc.n_channel {\n                    None => vals::ConfigMode::SE,\n                    Some(_) => vals::ConfigMode::DIFF,\n                });\n                #[cfg(not(feature = \"_nrf54l\"))]\n                w.set_resp(cc.resistor.into());\n                #[cfg(not(feature = \"_nrf54l\"))]\n                w.set_resn(vals::Resn::BYPASS);\n                #[cfg(not(feature = \"_nrf54lm20\"))]\n                w.set_burst(!matches!(oversample, Oversample::BYPASS));\n            });\n            #[cfg(feature = \"_nrf54lm20\")]\n            r.burst()\n                .write(|w| w.set_burst(!matches!(oversample, Oversample::BYPASS)));\n        }\n\n        // Disable all events interrupts\n        r.intenclr().write(|w| w.0 = 0x003F_FFFF);\n\n        interrupt::SAADC.unpend();\n        unsafe { interrupt::SAADC.enable() };\n\n        Self { _p: saadc }\n    }\n\n    fn regs() -> pac::saadc::Saadc {\n        pac::SAADC\n    }\n\n    /// Perform SAADC calibration. Completes when done.\n    pub async fn calibrate(&self) {\n        let r = Self::regs();\n\n        // Reset and enable the end event\n        r.events_calibratedone().write_value(0);\n        r.intenset().write(|w| w.set_calibratedone(true));\n\n        // Order is important\n        compiler_fence(Ordering::SeqCst);\n\n        r.tasks_calibrateoffset().write_value(1);\n\n        // Wait for 'calibratedone' event.\n        poll_fn(|cx| {\n            let r = Self::regs();\n\n            WAKER.register(cx.waker());\n\n            if r.events_calibratedone().read() != 0 {\n                r.events_calibratedone().write_value(0);\n                return Poll::Ready(());\n            }\n\n            Poll::Pending\n        })\n        .await;\n    }\n\n    /// One shot sampling. The buffer must be the same size as the number of channels configured.\n    /// The sampling is stopped prior to returning in order to reduce power consumption (power\n    /// consumption remains higher if sampling is not stopped explicitly). Cancellation will\n    /// also cause the sampling to be stopped.\n    pub async fn sample(&mut self, buf: &mut [i16; N]) {\n        // In case the future is dropped, stop the task and wait for it to end.\n        let on_drop = OnDrop::new(Self::stop_sampling_immediately);\n\n        let r = Self::regs();\n\n        // Set up the DMA\n        r.result().ptr().write_value(buf.as_mut_ptr() as u32);\n        r.result().maxcnt().write(|w| w.set_maxcnt((N * CNT_UNIT) as _));\n\n        // Reset and enable the end event\n        r.events_end().write_value(0);\n        r.intenset().write(|w| w.set_end(true));\n\n        // Don't reorder the ADC start event before the previous writes. Hopefully self\n        // wouldn't happen anyway.\n        compiler_fence(Ordering::SeqCst);\n\n        r.tasks_start().write_value(1);\n        r.tasks_sample().write_value(1);\n\n        // Wait for 'end' event.\n        poll_fn(|cx| {\n            let r = Self::regs();\n\n            WAKER.register(cx.waker());\n\n            if r.events_end().read() != 0 {\n                r.events_end().write_value(0);\n                return Poll::Ready(());\n            }\n\n            Poll::Pending\n        })\n        .await;\n\n        drop(on_drop);\n    }\n\n    /// Continuous sampling with double buffers.\n    ///\n    /// A TIMER and two PPI peripherals are passed in so that precise sampling\n    /// can be attained. The sampling interval is expressed by selecting a\n    /// timer clock frequency to use along with a counter threshold to be reached.\n    /// For example, 1KHz can be achieved using a frequency of 1MHz and a counter\n    /// threshold of 1000.\n    ///\n    /// A sampler closure is provided that receives the buffer of samples, noting\n    /// that the size of this buffer can be less than the original buffer's size.\n    /// A command is return from the closure that indicates whether the sampling\n    /// should continue or stop.\n    ///\n    /// NOTE: The time spent within the callback supplied should not exceed the time\n    /// taken to acquire the samples into a single buffer. You should measure the\n    /// time taken by the callback and set the sample buffer size accordingly.\n    /// Exceeding this time can lead to samples becoming dropped.\n    ///\n    /// The sampling is stopped prior to returning in order to reduce power consumption (power\n    /// consumption remains higher if sampling is not stopped explicitly), and to\n    /// free the buffers from being used by the peripheral. Cancellation will\n    /// also cause the sampling to be stopped.\n\n    pub async fn run_task_sampler<F, T: TimerInstance, const N0: usize>(\n        &mut self,\n        timer: Peri<'_, T>,\n        ppi_ch1: Peri<'_, impl ConfigurableChannel>,\n        ppi_ch2: Peri<'_, impl ConfigurableChannel>,\n        frequency: Frequency,\n        sample_counter: u32,\n        bufs: &mut [[[i16; N]; N0]; 2],\n        callback: F,\n    ) where\n        F: FnMut(&[[i16; N]]) -> CallbackResult,\n    {\n        let r = Self::regs();\n\n        // We want the task start to effectively short with the last one ending so\n        // we don't miss any samples. It'd be great for the SAADC to offer a SHORTS\n        // register instead, but it doesn't, so we must use PPI.\n        let mut start_ppi = Ppi::new_one_to_one(\n            ppi_ch1,\n            Event::from_reg(r.events_end()),\n            Task::from_reg(r.tasks_start()),\n        );\n        start_ppi.enable();\n\n        let timer = Timer::new(timer);\n        timer.set_frequency(frequency);\n        timer.cc(0).write(sample_counter);\n        timer.cc(0).short_compare_clear();\n\n        let timer_cc = timer.cc(0);\n\n        let mut sample_ppi = Ppi::new_one_to_one(ppi_ch2, timer_cc.event_compare(), Task::from_reg(r.tasks_sample()));\n\n        timer.start();\n\n        self.run_sampler(\n            bufs,\n            None,\n            || {\n                sample_ppi.enable();\n            },\n            callback,\n        )\n        .await;\n    }\n\n    async fn run_sampler<I, F, const N0: usize>(\n        &mut self,\n        bufs: &mut [[[i16; N]; N0]; 2],\n        sample_rate_divisor: Option<u16>,\n        mut init: I,\n        mut callback: F,\n    ) where\n        I: FnMut(),\n        F: FnMut(&[[i16; N]]) -> CallbackResult,\n    {\n        // In case the future is dropped, stop the task and wait for it to end.\n        let on_drop = OnDrop::new(Self::stop_sampling_immediately);\n\n        let r = Self::regs();\n\n        // Establish mode and sample rate\n        match sample_rate_divisor {\n            Some(sr) => {\n                r.samplerate().write(|w| {\n                    w.set_cc(sr);\n                    w.set_mode(vals::SamplerateMode::TIMERS);\n                });\n                r.tasks_sample().write_value(1); // Need to kick-start the internal timer\n            }\n            None => r.samplerate().write(|w| {\n                w.set_cc(0);\n                w.set_mode(vals::SamplerateMode::TASK);\n            }),\n        }\n\n        // Set up the initial DMA\n        r.result().ptr().write_value(bufs[0].as_mut_ptr() as u32);\n        r.result().maxcnt().write(|w| w.set_maxcnt((N0 * N * CNT_UNIT) as _));\n\n        // Reset and enable the events\n        r.events_end().write_value(0);\n        r.events_started().write_value(0);\n        r.intenset().write(|w| {\n            w.set_end(true);\n            w.set_started(true);\n        });\n\n        // Don't reorder the ADC start event before the previous writes. Hopefully self\n        // wouldn't happen anyway.\n        compiler_fence(Ordering::SeqCst);\n\n        r.tasks_start().write_value(1);\n\n        let mut inited = false;\n        let mut current_buffer = 0;\n\n        // Wait for events and complete when the sampler indicates it has had enough.\n        let r = poll_fn(|cx| {\n            let r = Self::regs();\n\n            WAKER.register(cx.waker());\n\n            if r.events_end().read() != 0 {\n                compiler_fence(Ordering::SeqCst);\n\n                r.events_end().write_value(0);\n                r.intenset().write(|w| w.set_end(true));\n\n                match callback(&bufs[current_buffer]) {\n                    CallbackResult::Continue => {\n                        let next_buffer = 1 - current_buffer;\n                        current_buffer = next_buffer;\n                    }\n                    CallbackResult::Stop => {\n                        return Poll::Ready(());\n                    }\n                }\n            }\n\n            if r.events_started().read() != 0 {\n                r.events_started().write_value(0);\n                r.intenset().write(|w| w.set_started(true));\n\n                if !inited {\n                    init();\n                    inited = true;\n                }\n\n                let next_buffer = 1 - current_buffer;\n                r.result().ptr().write_value(bufs[next_buffer].as_mut_ptr() as u32);\n            }\n\n            Poll::Pending\n        })\n        .await;\n\n        drop(on_drop);\n\n        r\n    }\n\n    // Stop sampling and wait for it to stop in a blocking fashion\n    fn stop_sampling_immediately() {\n        let r = Self::regs();\n\n        compiler_fence(Ordering::SeqCst);\n\n        r.events_stopped().write_value(0);\n        r.tasks_stop().write_value(1);\n\n        while r.events_stopped().read() == 0 {}\n        r.events_stopped().write_value(0);\n    }\n}\n\nimpl<'d> Saadc<'d, 1> {\n    /// Continuous sampling on a single channel with double buffers.\n    ///\n    /// The internal clock is to be used with a sample rate expressed as a divisor of\n    /// 16MHz, ranging from 80..2047. For example, 1600 represents a sample rate of 10KHz\n    /// given 16_000_000 / 10_000_000 = 1600.\n    ///\n    /// A sampler closure is provided that receives the buffer of samples, noting\n    /// that the size of this buffer can be less than the original buffer's size.\n    /// A command is return from the closure that indicates whether the sampling\n    /// should continue or stop.\n    pub async fn run_timer_sampler<I, S, const N0: usize>(\n        &mut self,\n        bufs: &mut [[[i16; 1]; N0]; 2],\n        sample_rate_divisor: u16,\n        sampler: S,\n    ) where\n        S: FnMut(&[[i16; 1]]) -> CallbackResult,\n    {\n        self.run_sampler(bufs, Some(sample_rate_divisor), || {}, sampler).await;\n    }\n}\n\nimpl<'d, const N: usize> Drop for Saadc<'d, N> {\n    fn drop(&mut self) {\n        // Reset of SAADC.\n        //\n        // This is needed when more than one pin is sampled to avoid needless power consumption.\n        // More information can be found in [nrf52 Anomaly 241](https://docs.nordicsemi.com/bundle/errata_nRF52810_Rev1/page/ERR/nRF52810/Rev1/latest/anomaly_810_241.html).\n        // The workaround seems like it copies the configuration before reset and reapplies it after.\n        // The instance is dropped, forcing a reconfiguration at compile time, hence we only\n        // call what is the reset portion of the workaround.\n        #[cfg(feature = \"_nrf52\")]\n        {\n            unsafe { core::ptr::write_volatile(0x40007FFC as *mut u32, 0) }\n            unsafe { core::ptr::read_volatile(0x40007FFC as *const ()) }\n            unsafe { core::ptr::write_volatile(0x40007FFC as *mut u32, 1) }\n        }\n        let r = Self::regs();\n        r.enable().write(|w| w.set_enable(false));\n        for i in 0..N {\n            #[cfg(not(feature = \"_nrf54l\"))]\n            {\n                r.ch(i).pselp().write(|w| w.set_pselp(InputChannel::NC));\n                r.ch(i).pseln().write(|w| w.set_pseln(InputChannel::NC));\n            }\n            #[cfg(feature = \"_nrf54l\")]\n            {\n                r.ch(i).pselp().write(|w| w.set_connect(vals::PselpConnect::NC));\n                r.ch(i).pseln().write(|w| w.set_connect(vals::PselnConnect::NC));\n            }\n        }\n    }\n}\n\n#[cfg(not(feature = \"_nrf54l\"))]\nimpl From<Gain> for vals::Gain {\n    fn from(gain: Gain) -> Self {\n        match gain {\n            Gain::GAIN1_6 => vals::Gain::GAIN1_6,\n            Gain::GAIN1_5 => vals::Gain::GAIN1_5,\n            Gain::GAIN1_4 => vals::Gain::GAIN1_4,\n            Gain::GAIN1_3 => vals::Gain::GAIN1_3,\n            Gain::GAIN1_2 => vals::Gain::GAIN1_2,\n            Gain::GAIN1 => vals::Gain::GAIN1,\n            Gain::GAIN2 => vals::Gain::GAIN2,\n            Gain::GAIN4 => vals::Gain::GAIN4,\n        }\n    }\n}\n\n#[cfg(feature = \"_nrf54l\")]\nimpl From<Gain> for vals::Gain {\n    fn from(gain: Gain) -> Self {\n        match gain {\n            Gain::GAIN2_8 => vals::Gain::GAIN2_8,\n            Gain::GAIN2_7 => vals::Gain::GAIN2_7,\n            Gain::GAIN2_6 => vals::Gain::GAIN2_6,\n            Gain::GAIN2_5 => vals::Gain::GAIN2_5,\n            Gain::GAIN2_4 => vals::Gain::GAIN2_4,\n            Gain::GAIN2_3 => vals::Gain::GAIN2_3,\n            Gain::GAIN1 => vals::Gain::GAIN1,\n            Gain::GAIN2 => vals::Gain::GAIN2,\n        }\n    }\n}\n\n/// Gain control\n#[cfg(not(feature = \"_nrf54l\"))]\n#[non_exhaustive]\n#[derive(Clone, Copy)]\npub enum Gain {\n    /// 1/6\n    GAIN1_6 = 0,\n    /// 1/5\n    GAIN1_5 = 1,\n    /// 1/4\n    GAIN1_4 = 2,\n    /// 1/3\n    GAIN1_3 = 3,\n    /// 1/2\n    GAIN1_2 = 4,\n    /// 1\n    GAIN1 = 5,\n    /// 2\n    GAIN2 = 6,\n    /// 4\n    GAIN4 = 7,\n}\n\n/// Gain control\n#[cfg(feature = \"_nrf54l\")]\n#[non_exhaustive]\n#[derive(Clone, Copy)]\npub enum Gain {\n    /// 2/8\n    GAIN2_8 = 0,\n    /// 2/7\n    GAIN2_7 = 1,\n    /// 2/6\n    GAIN2_6 = 2,\n    /// 2/5\n    GAIN2_5 = 3,\n    /// 2/4\n    GAIN2_4 = 4,\n    /// 2/3\n    GAIN2_3 = 5,\n    /// 1\n    GAIN1 = 6,\n    /// 2\n    GAIN2 = 7,\n}\n\nimpl From<Reference> for vals::Refsel {\n    fn from(reference: Reference) -> Self {\n        match reference {\n            Reference::INTERNAL => vals::Refsel::INTERNAL,\n            #[cfg(not(feature = \"_nrf54l\"))]\n            Reference::VDD1_4 => vals::Refsel::VDD1_4,\n            #[cfg(feature = \"_nrf54l\")]\n            Reference::EXTERNAL => vals::Refsel::EXTERNAL,\n        }\n    }\n}\n\n/// Reference control\n#[non_exhaustive]\n#[derive(Clone, Copy)]\npub enum Reference {\n    /// Internal reference (0.6 V)\n    INTERNAL = 0,\n    #[cfg(not(feature = \"_nrf54l\"))]\n    /// VDD/4 as reference\n    VDD1_4 = 1,\n    /// PADC_EXT_REF_1V2 as reference\n    #[cfg(feature = \"_nrf54l\")]\n    EXTERNAL = 1,\n}\n\n#[cfg(not(feature = \"_nrf54l\"))]\nimpl From<Resistor> for vals::Resp {\n    fn from(resistor: Resistor) -> Self {\n        match resistor {\n            Resistor::BYPASS => vals::Resp::BYPASS,\n            Resistor::PULLDOWN => vals::Resp::PULLDOWN,\n            Resistor::PULLUP => vals::Resp::PULLUP,\n            Resistor::VDD1_2 => vals::Resp::VDD1_2,\n        }\n    }\n}\n\n/// Positive channel resistor control\n#[non_exhaustive]\n#[derive(Clone, Copy)]\n#[cfg(not(feature = \"_nrf54l\"))]\npub enum Resistor {\n    /// Bypass resistor ladder\n    BYPASS = 0,\n    /// Pull-down to GND\n    PULLDOWN = 1,\n    /// Pull-up to VDD\n    PULLUP = 2,\n    /// Set input at VDD/2\n    VDD1_2 = 3,\n}\n\n#[cfg(not(feature = \"_nrf54l\"))]\nimpl From<Time> for vals::Tacq {\n    fn from(time: Time) -> Self {\n        match time {\n            Time::_3US => vals::Tacq::_3US,\n            Time::_5US => vals::Tacq::_5US,\n            Time::_10US => vals::Tacq::_10US,\n            Time::_15US => vals::Tacq::_15US,\n            Time::_20US => vals::Tacq::_20US,\n            Time::_40US => vals::Tacq::_40US,\n        }\n    }\n}\n\n#[cfg(feature = \"_nrf54l\")]\nimpl From<Time> for u16 {\n    fn from(time: Time) -> Self {\n        match time {\n            Time::_3US => (3000 / 125) - 1,\n            Time::_5US => (5000 / 125) - 1,\n            Time::_10US => (10000 / 125) - 1,\n            Time::_15US => (15000 / 125) - 1,\n            Time::_20US => (20000 / 125) - 1,\n            Time::_40US => (40000 / 125) - 1,\n        }\n    }\n}\n\n/// Acquisition time, the time the SAADC uses to sample the input voltage\n#[non_exhaustive]\n#[derive(Clone, Copy)]\npub enum Time {\n    /// 3 us\n    _3US = 0,\n    ///  5 us\n    _5US = 1,\n    /// 10 us\n    _10US = 2,\n    /// 15 us\n    _15US = 3,\n    /// 20 us\n    _20US = 4,\n    /// 40 us\n    _40US = 5,\n}\n\nimpl From<Oversample> for vals::Oversample {\n    fn from(oversample: Oversample) -> Self {\n        match oversample {\n            Oversample::BYPASS => vals::Oversample::BYPASS,\n            Oversample::OVER2X => vals::Oversample::OVER2X,\n            Oversample::OVER4X => vals::Oversample::OVER4X,\n            Oversample::OVER8X => vals::Oversample::OVER8X,\n            Oversample::OVER16X => vals::Oversample::OVER16X,\n            Oversample::OVER32X => vals::Oversample::OVER32X,\n            Oversample::OVER64X => vals::Oversample::OVER64X,\n            Oversample::OVER128X => vals::Oversample::OVER128X,\n            Oversample::OVER256X => vals::Oversample::OVER256X,\n        }\n    }\n}\n\n/// Oversample control\n#[non_exhaustive]\n#[derive(Clone, Copy)]\npub enum Oversample {\n    /// Bypass oversampling\n    BYPASS = 0,\n    /// Oversample 2x\n    OVER2X = 1,\n    /// Oversample 4x\n    OVER4X = 2,\n    /// Oversample 8x\n    OVER8X = 3,\n    /// Oversample 16x\n    OVER16X = 4,\n    /// Oversample 32x\n    OVER32X = 5,\n    /// Oversample 64x\n    OVER64X = 6,\n    /// Oversample 128x\n    OVER128X = 7,\n    /// Oversample 256x\n    OVER256X = 8,\n}\n\nimpl From<Resolution> for vals::Val {\n    fn from(resolution: Resolution) -> Self {\n        match resolution {\n            Resolution::_8BIT => vals::Val::_8BIT,\n            Resolution::_10BIT => vals::Val::_10BIT,\n            Resolution::_12BIT => vals::Val::_12BIT,\n            Resolution::_14BIT => vals::Val::_14BIT,\n        }\n    }\n}\n\n/// Set the resolution\n#[non_exhaustive]\n#[derive(Clone, Copy)]\npub enum Resolution {\n    /// 8 bits\n    _8BIT = 0,\n    /// 10 bits\n    _10BIT = 1,\n    /// 12 bits\n    _12BIT = 2,\n    /// 14 bits\n    _14BIT = 3,\n}\n\npub(crate) trait SealedInput {\n    #[cfg(not(feature = \"_nrf54l\"))]\n    fn channel(&self) -> InputChannel;\n\n    #[cfg(feature = \"_nrf54l\")]\n    fn pin(&self) -> u8;\n\n    #[cfg(feature = \"_nrf54l\")]\n    fn port(&self) -> u8;\n\n    #[cfg(feature = \"_nrf54l\")]\n    fn internal(&self) -> vals::PselpInternal;\n\n    #[cfg(feature = \"_nrf54l\")]\n    fn connect(&self) -> vals::PselpConnect;\n}\n\n/// An input that can be used as either or negative end of a ADC differential in the SAADC periperhal.\n#[allow(private_bounds)]\npub trait Input: SealedInput + Sized {\n    /// Convert this SAADC input to a type-erased `AnyInput`.\n    ///\n    /// This allows using several inputs  in situations that might require\n    /// them to be the same type, like putting them in an array.\n    #[cfg(not(feature = \"_nrf54l\"))]\n    fn degrade_saadc<'a>(self) -> AnyInput<'a>\n    where\n        Self: 'a,\n    {\n        AnyInput {\n            channel: self.channel(),\n            _phantom: core::marker::PhantomData,\n        }\n    }\n\n    /// Convert this SAADC input to a type-erased `AnyInput`.\n    ///\n    /// This allows using several inputs  in situations that might require\n    /// them to be the same type, like putting them in an array.\n    #[cfg(feature = \"_nrf54l\")]\n    fn degrade_saadc<'a>(self) -> AnyInput<'a>\n    where\n        Self: 'a,\n    {\n        AnyInput {\n            pin: self.pin(),\n            port: self.port(),\n            internal: self.internal(),\n            connect: self.connect(),\n            _phantom: core::marker::PhantomData,\n        }\n    }\n}\n\n/// A type-erased SAADC input.\n///\n/// This allows using several inputs  in situations that might require\n/// them to be the same type, like putting them in an array.\n#[cfg(not(feature = \"_nrf54l\"))]\npub struct AnyInput<'a> {\n    channel: InputChannel,\n    _phantom: PhantomData<&'a ()>,\n}\n\n/// A type-erased SAADC input.\n///\n/// This allows using several inputs  in situations that might require\n/// them to be the same type, like putting them in an array.\n#[cfg(feature = \"_nrf54l\")]\npub struct AnyInput<'a> {\n    pin: u8,\n    port: u8,\n    internal: vals::PselpInternal,\n    connect: vals::PselpConnect,\n    _phantom: PhantomData<&'a ()>,\n}\n\nimpl<'a> AnyInput<'a> {\n    /// Reborrow into a \"child\" AnyInput.\n    ///\n    /// `self` will stay borrowed until the child AnyInput is dropped.\n    pub fn reborrow(&mut self) -> AnyInput<'_> {\n        // safety: we're returning the clone inside a new Peripheral that borrows\n        // self, so user code can't use both at the same time.\n        #[cfg(not(feature = \"_nrf54l\"))]\n        {\n            Self {\n                channel: self.channel,\n                _phantom: PhantomData,\n            }\n        }\n        #[cfg(feature = \"_nrf54l\")]\n        {\n            Self {\n                pin: self.pin,\n                port: self.port,\n                internal: self.internal,\n                connect: self.connect,\n                _phantom: PhantomData,\n            }\n        }\n    }\n}\n\nimpl SealedInput for AnyInput<'_> {\n    #[cfg(not(feature = \"_nrf54l\"))]\n    fn channel(&self) -> InputChannel {\n        self.channel\n    }\n\n    #[cfg(feature = \"_nrf54l\")]\n    fn pin(&self) -> u8 {\n        self.pin\n    }\n\n    #[cfg(feature = \"_nrf54l\")]\n    fn port(&self) -> u8 {\n        self.port\n    }\n\n    #[cfg(feature = \"_nrf54l\")]\n    fn internal(&self) -> vals::PselpInternal {\n        self.internal\n    }\n\n    #[cfg(feature = \"_nrf54l\")]\n    fn connect(&self) -> vals::PselpConnect {\n        self.connect\n    }\n}\n\nimpl Input for AnyInput<'_> {}\n\n#[cfg(not(feature = \"_nrf54l\"))]\nmacro_rules! impl_saadc_input {\n    ($pin:ident, $ch:ident) => {\n        impl_saadc_input!(@local, crate::Peri<'_, crate::peripherals::$pin>, $ch);\n    };\n    (@local, $pin:ty, $ch:ident) => {\n        impl crate::saadc::SealedInput for $pin {\n            fn channel(&self) -> crate::saadc::InputChannel {\n                crate::saadc::InputChannel::$ch\n            }\n        }\n        impl crate::saadc::Input for $pin {}\n    };\n}\n\n#[cfg(feature = \"_nrf54l\")]\nmacro_rules! impl_saadc_input {\n    ($pin:ident, $port:expr, $ain:expr) => {\n        impl_saadc_input!(@local, crate::Peri<'_, crate::peripherals::$pin>, $port, $ain, AVDD, ANALOG_INPUT);\n    };\n    (@local, $pin:ty, $port:expr, $ain:expr, $internal:ident, $connect:ident) => {\n        impl crate::saadc::SealedInput for $pin {\n            fn pin(&self) -> u8 {\n                $ain\n            }\n\n            fn port(&self) -> u8 {\n                $port\n            }\n\n            fn internal(&self) -> crate::pac::saadc::vals::PselpInternal {\n                crate::pac::saadc::vals::PselpInternal::$internal\n            }\n\n            fn connect(&self) -> crate::pac::saadc::vals::PselpConnect {\n                crate::pac::saadc::vals::PselpConnect::$connect\n            }\n        }\n        impl crate::saadc::Input for $pin {}\n    };\n}\n\n/// A dummy `Input` pin implementation for SAADC peripheral sampling from the\n/// internal voltage.\npub struct VddInput;\n\nimpl_peripheral!(VddInput);\n#[cfg(not(feature = \"_nrf54l\"))]\n#[cfg(not(feature = \"_nrf91\"))]\nimpl_saadc_input!(@local, VddInput, VDD);\n#[cfg(feature = \"_nrf91\")]\nimpl_saadc_input!(@local, VddInput, VDD_GPIO);\n#[cfg(feature = \"_nrf54l\")]\nimpl_saadc_input!(@local, VddInput, 0, 0, VDD, INTERNAL);\n\n/// A dummy `Input` pin implementation for SAADC peripheral sampling from the\n/// VDDH / 5 voltage.\n#[cfg(any(feature = \"_nrf5340-app\", feature = \"nrf52833\", feature = \"nrf52840\"))]\npub struct VddhDiv5Input;\n\n#[cfg(any(feature = \"_nrf5340-app\", feature = \"nrf52833\", feature = \"nrf52840\"))]\nimpl_peripheral!(VddhDiv5Input);\n\n#[cfg(any(feature = \"_nrf5340-app\", feature = \"nrf52833\", feature = \"nrf52840\"))]\nimpl_saadc_input!(@local, VddhDiv5Input, VDDHDIV5);\n\n/// A dummy `Input` pin implementation for SAADC peripheral sampling from the\n/// AVDD internal voltage of the nrf54l chip family.\n#[cfg(feature = \"_nrf54l\")]\npub struct AVddInput;\n#[cfg(feature = \"_nrf54l\")]\nembassy_hal_internal::impl_peripheral!(AVddInput);\n#[cfg(feature = \"_nrf54l\")]\nimpl_saadc_input!(@local, AVddInput, 0, 0, AVDD, INTERNAL);\n\n/// A dummy `Input` pin implementation for SAADC peripheral sampling from the\n/// DVDD internal voltage of the nrf54l chip family.\n#[cfg(feature = \"_nrf54l\")]\npub struct DVddInput;\n#[cfg(feature = \"_nrf54l\")]\nembassy_hal_internal::impl_peripheral!(DVddInput);\n#[cfg(feature = \"_nrf54l\")]\nimpl_saadc_input!(@local, DVddInput, 0, 0, DVDD, INTERNAL);\n"
  },
  {
    "path": "embassy-nrf/src/spim.rs",
    "content": "//! Serial Peripheral Instance in master mode (SPIM) driver.\n\n#![macro_use]\n\nuse core::future::poll_fn;\nuse core::marker::PhantomData;\n#[cfg(feature = \"_nrf52832_anomaly_109\")]\nuse core::sync::atomic::AtomicU8;\nuse core::sync::atomic::{Ordering, compiler_fence};\nuse core::task::Poll;\n\nuse embassy_embedded_hal::SetConfig;\nuse embassy_hal_internal::{Peri, PeripheralType};\nuse embassy_sync::waitqueue::AtomicWaker;\npub use embedded_hal_02::spi::{MODE_0, MODE_1, MODE_2, MODE_3, Mode, Phase, Polarity};\npub use pac::spim::vals::Order as BitOrder;\n\nuse crate::chip::{EASY_DMA_SIZE, FORCE_COPY_BUFFER_SIZE};\nuse crate::gpio::{self, AnyPin, OutputDrive, Pin as GpioPin, PselBits, SealedPin as _, convert_drive};\nuse crate::interrupt::typelevel::Interrupt;\nuse crate::pac::gpio::vals as gpiovals;\nuse crate::pac::spim::vals;\nuse crate::util::slice_in_ram_or;\nuse crate::{interrupt, pac};\n\n/// SPI frequencies.\n#[repr(transparent)]\n#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd)]\npub struct Frequency(u32);\nimpl Frequency {\n    #[doc = \"125 kbps\"]\n    pub const K125: Self = Self(0x0200_0000);\n    #[doc = \"250 kbps\"]\n    pub const K250: Self = Self(0x0400_0000);\n    #[doc = \"500 kbps\"]\n    pub const K500: Self = Self(0x0800_0000);\n    #[doc = \"1 Mbps\"]\n    pub const M1: Self = Self(0x1000_0000);\n    #[doc = \"2 Mbps\"]\n    pub const M2: Self = Self(0x2000_0000);\n    #[doc = \"4 Mbps\"]\n    pub const M4: Self = Self(0x4000_0000);\n    #[doc = \"8 Mbps\"]\n    pub const M8: Self = Self(0x8000_0000);\n    #[cfg(not(feature = \"_spi-v1\"))]\n    #[doc = \"16 Mbps\"]\n    pub const M16: Self = Self(0x0a00_0000);\n    #[cfg(not(feature = \"_spi-v1\"))]\n    #[doc = \"32 Mbps\"]\n    pub const M32: Self = Self(0x1400_0000);\n}\n\nimpl Frequency {\n    #[cfg(feature = \"_nrf54l\")]\n    fn to_divisor(&self, clk: u32) -> u8 {\n        let frequency = match *self {\n            #[cfg(not(feature = \"_spi-v1\"))]\n            Self::M32 => 32_000_000,\n            #[cfg(not(feature = \"_spi-v1\"))]\n            Self::M16 => 16_000_000,\n            Self::M8 => 8_000_000,\n            Self::M4 => 4_000_000,\n            Self::M2 => 2_000_000,\n            Self::M1 => 1_000_000,\n            Self::K500 => 500_000,\n            Self::K250 => 250_000,\n            Self::K125 => 125_000,\n            _ => unreachable!(),\n        };\n        let divisor = (clk / frequency) as u8;\n        divisor\n    }\n}\nimpl core::fmt::Debug for Frequency {\n    fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {\n        match self.0 {\n            0x0200_0000 => f.write_str(\"K125\"),\n            0x0400_0000 => f.write_str(\"K250\"),\n            0x0800_0000 => f.write_str(\"K500\"),\n            0x0a00_0000 => f.write_str(\"M16\"),\n            0x1000_0000 => f.write_str(\"M1\"),\n            0x1400_0000 => f.write_str(\"M32\"),\n            0x2000_0000 => f.write_str(\"M2\"),\n            0x4000_0000 => f.write_str(\"M4\"),\n            0x8000_0000 => f.write_str(\"M8\"),\n            other => core::write!(f, \"0x{:02X}\", other),\n        }\n    }\n}\n\n#[cfg(feature = \"defmt\")]\nimpl defmt::Format for Frequency {\n    fn format(&self, f: defmt::Formatter) {\n        match self.0 {\n            0x0200_0000 => defmt::write!(f, \"K125\"),\n            0x0400_0000 => defmt::write!(f, \"K250\"),\n            0x0800_0000 => defmt::write!(f, \"K500\"),\n            0x0a00_0000 => defmt::write!(f, \"M16\"),\n            0x1000_0000 => defmt::write!(f, \"M1\"),\n            0x1400_0000 => defmt::write!(f, \"M32\"),\n            0x2000_0000 => defmt::write!(f, \"M2\"),\n            0x4000_0000 => defmt::write!(f, \"M4\"),\n            0x8000_0000 => defmt::write!(f, \"M8\"),\n            other => defmt::write!(f, \"0x{:02X}\", other),\n        }\n    }\n}\n\n#[cfg(not(feature = \"_nrf54l\"))]\nimpl Into<pac::spim::vals::Frequency> for Frequency {\n    fn into(self) -> pac::spim::vals::Frequency {\n        use pac::spim::vals::Frequency as Freq;\n        match self {\n            #[cfg(not(feature = \"_spi-v1\"))]\n            Self::M32 => Freq::M32,\n            #[cfg(not(feature = \"_spi-v1\"))]\n            Self::M16 => Freq::M16,\n            Self::M8 => Freq::M8,\n            Self::M4 => Freq::M4,\n            Self::M2 => Freq::M2,\n            Self::M1 => Freq::M1,\n            Self::K500 => Freq::K500,\n            Self::K250 => Freq::K250,\n            Self::K125 => Freq::K125,\n            _ => unreachable!(),\n        }\n    }\n}\n\n/// SPIM error\n#[derive(Debug, Clone, Copy, PartialEq, Eq)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\n#[non_exhaustive]\npub enum Error {\n    /// EasyDMA can only read from data memory, read only buffers in flash will fail.\n    BufferNotInRAM,\n}\n\n/// SPIM configuration.\n#[non_exhaustive]\n#[derive(Clone)]\npub struct Config {\n    /// Frequency\n    pub frequency: Frequency,\n\n    /// SPI mode\n    pub mode: Mode,\n\n    /// Bit order\n    pub bit_order: BitOrder,\n\n    /// Overread character.\n    ///\n    /// When doing bidirectional transfers, if the TX buffer is shorter than the RX buffer,\n    /// this byte will be transmitted in the MOSI line for the left-over bytes.\n    pub orc: u8,\n\n    /// Drive strength for the SCK line.\n    pub sck_drive: OutputDrive,\n\n    /// Drive strength for the MOSI line.\n    pub mosi_drive: OutputDrive,\n}\n\nimpl Default for Config {\n    fn default() -> Self {\n        Self {\n            frequency: Frequency::M1,\n            mode: MODE_0,\n            bit_order: BitOrder::MSB_FIRST,\n            orc: 0x00,\n            sck_drive: OutputDrive::HighDrive,\n            mosi_drive: OutputDrive::HighDrive,\n        }\n    }\n}\n\n/// Interrupt handler.\npub struct InterruptHandler<T: Instance> {\n    _phantom: PhantomData<T>,\n}\n\nimpl<T: Instance> interrupt::typelevel::Handler<T::Interrupt> for InterruptHandler<T> {\n    unsafe fn on_interrupt() {\n        let r = T::regs();\n        let s = T::state();\n\n        #[cfg(feature = \"_nrf52832_anomaly_109\")]\n        {\n            // Ideally we should call this only during the first chunk transfer,\n            // but so far calling this every time doesn't seem to be causing any issues.\n            if r.events_started().read() != 0 {\n                s.waker.wake();\n                r.intenclr().write(|w| w.set_started(true));\n            }\n        }\n\n        if r.events_end().read() != 0 {\n            s.waker.wake();\n            r.intenclr().write(|w| w.set_end(true));\n        }\n    }\n}\n\n/// SPIM driver.\npub struct Spim<'d> {\n    r: pac::spim::Spim,\n    irq: interrupt::Interrupt,\n    state: &'static State,\n    #[cfg(feature = \"_nrf54l\")]\n    clk: u32,\n    _p: PhantomData<&'d ()>,\n}\n\nimpl<'d> Spim<'d> {\n    /// Create a new SPIM driver.\n    pub fn new<T: Instance>(\n        spim: Peri<'d, T>,\n        _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd,\n        sck: Peri<'d, impl GpioPin>,\n        miso: Peri<'d, impl GpioPin>,\n        mosi: Peri<'d, impl GpioPin>,\n        config: Config,\n    ) -> Self {\n        Self::new_inner(spim, Some(sck.into()), Some(miso.into()), Some(mosi.into()), config)\n    }\n\n    /// Create a new SPIM driver, capable of TX only (MOSI only).\n    pub fn new_txonly<T: Instance>(\n        spim: Peri<'d, T>,\n        _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd,\n        sck: Peri<'d, impl GpioPin>,\n        mosi: Peri<'d, impl GpioPin>,\n        config: Config,\n    ) -> Self {\n        Self::new_inner(spim, Some(sck.into()), None, Some(mosi.into()), config)\n    }\n\n    /// Create a new SPIM driver, capable of RX only (MISO only).\n    pub fn new_rxonly<T: Instance>(\n        spim: Peri<'d, T>,\n        _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd,\n        sck: Peri<'d, impl GpioPin>,\n        miso: Peri<'d, impl GpioPin>,\n        config: Config,\n    ) -> Self {\n        Self::new_inner(spim, Some(sck.into()), Some(miso.into()), None, config)\n    }\n\n    /// Create a new SPIM driver, capable of TX only (MOSI only), without SCK pin.\n    pub fn new_txonly_nosck<T: Instance>(\n        spim: Peri<'d, T>,\n        _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd,\n        mosi: Peri<'d, impl GpioPin>,\n        config: Config,\n    ) -> Self {\n        Self::new_inner(spim, None, None, Some(mosi.into()), config)\n    }\n\n    fn new_inner<T: Instance>(\n        _spim: Peri<'d, T>,\n        sck: Option<Peri<'d, AnyPin>>,\n        miso: Option<Peri<'d, AnyPin>>,\n        mosi: Option<Peri<'d, AnyPin>>,\n        config: Config,\n    ) -> Self {\n        let r = T::regs();\n\n        // Configure pins\n        if let Some(sck) = &sck {\n            sck.conf().write(|w| {\n                w.set_dir(gpiovals::Dir::OUTPUT);\n                convert_drive(w, config.sck_drive);\n            });\n        }\n        if let Some(mosi) = &mosi {\n            mosi.conf().write(|w| {\n                w.set_dir(gpiovals::Dir::OUTPUT);\n                convert_drive(w, config.mosi_drive);\n            });\n        }\n        if let Some(miso) = &miso {\n            miso.conf().write(|w| w.set_input(gpiovals::Input::CONNECT));\n        }\n\n        match config.mode.polarity {\n            Polarity::IdleHigh => {\n                if let Some(sck) = &sck {\n                    sck.set_high();\n                }\n                if let Some(mosi) = &mosi {\n                    mosi.set_high();\n                }\n            }\n            Polarity::IdleLow => {\n                if let Some(sck) = &sck {\n                    sck.set_low();\n                }\n                if let Some(mosi) = &mosi {\n                    mosi.set_low();\n                }\n            }\n        }\n\n        // Select pins.\n        r.psel().sck().write_value(sck.psel_bits());\n        r.psel().mosi().write_value(mosi.psel_bits());\n        r.psel().miso().write_value(miso.psel_bits());\n\n        // Enable SPIM instance.\n        r.enable().write(|w| w.set_enable(vals::Enable::ENABLED));\n\n        let mut spim = Self {\n            r: T::regs(),\n            irq: T::Interrupt::IRQ,\n            state: T::state(),\n            #[cfg(feature = \"_nrf54l\")]\n            clk: T::clk(),\n            _p: PhantomData {},\n        };\n\n        // Apply runtime peripheral configuration\n        Self::set_config(&mut spim, &config).unwrap();\n\n        // Disable all events interrupts\n        r.intenclr().write(|w| w.0 = 0xFFFF_FFFF);\n\n        T::Interrupt::unpend();\n        unsafe { T::Interrupt::enable() };\n\n        spim\n    }\n\n    fn prepare_dma_transfer(&mut self, rx: *mut [u8], tx: *const [u8], offset: usize, length: usize) {\n        compiler_fence(Ordering::SeqCst);\n\n        let r = self.r;\n\n        fn xfer_params(ptr: u32, total: usize, offset: usize, length: usize) -> (u32, usize) {\n            if total > offset {\n                (ptr.wrapping_add(offset as _), core::cmp::min(total - offset, length))\n            } else {\n                (ptr, 0)\n            }\n        }\n\n        // Set up the DMA read.\n        let (rx_ptr, rx_len) = xfer_params(rx as *mut u8 as _, rx.len() as _, offset, length);\n        r.dma().rx().ptr().write_value(rx_ptr);\n        r.dma().rx().maxcnt().write(|w| w.set_maxcnt(rx_len as _));\n\n        // Set up the DMA write.\n        let (tx_ptr, tx_len) = xfer_params(tx as *const u8 as _, tx.len() as _, offset, length);\n        r.dma().tx().ptr().write_value(tx_ptr);\n        r.dma().tx().maxcnt().write(|w| w.set_maxcnt(tx_len as _));\n\n        /*\n        trace!(\"XFER: offset: {}, length: {}\", offset, length);\n        trace!(\"RX(len: {}, ptr: {=u32:02x})\", rx_len, rx_ptr as u32);\n        trace!(\"TX(len: {}, ptr: {=u32:02x})\", tx_len, tx_ptr as u32);\n        */\n\n        #[cfg(feature = \"_nrf52832_anomaly_109\")]\n        if offset == 0 {\n            let s = self.state;\n\n            r.events_started().write_value(0);\n\n            // Set rx/tx buffer lengths to 0...\n            r.dma().tx().maxcnt().write(|_| ());\n            r.dma().rx().maxcnt().write(|_| ());\n\n            // ...and keep track of original buffer lengths...\n            s.tx.store(tx_len as _, Ordering::Relaxed);\n            s.rx.store(rx_len as _, Ordering::Relaxed);\n\n            // ...signalling the start of the fake transfer.\n            r.intenset().write(|w| w.set_started(true));\n        }\n\n        // Reset and enable the event\n        r.events_end().write_value(0);\n        r.intenset().write(|w| w.set_end(true));\n\n        // Start SPI transaction.\n        r.tasks_start().write_value(1);\n    }\n\n    fn blocking_inner_from_ram_chunk(&mut self, rx: *mut [u8], tx: *const [u8], offset: usize, length: usize) {\n        self.prepare_dma_transfer(rx, tx, offset, length);\n\n        #[cfg(feature = \"_nrf52832_anomaly_109\")]\n        if offset == 0 {\n            while self.nrf52832_dma_workaround_status().is_pending() {}\n        }\n\n        // Wait for 'end' event.\n        while self.r.events_end().read() == 0 {}\n\n        compiler_fence(Ordering::SeqCst);\n    }\n\n    fn blocking_inner_from_ram(&mut self, rx: *mut [u8], tx: *const [u8]) -> Result<(), Error> {\n        slice_in_ram_or(tx, Error::BufferNotInRAM)?;\n        // NOTE: RAM slice check for rx is not necessary, as a mutable\n        // slice can only be built from data located in RAM.\n\n        let xfer_len = core::cmp::max(rx.len(), tx.len());\n        for offset in (0..xfer_len).step_by(EASY_DMA_SIZE) {\n            let length = core::cmp::min(xfer_len - offset, EASY_DMA_SIZE);\n            self.blocking_inner_from_ram_chunk(rx, tx, offset, length);\n        }\n        Ok(())\n    }\n\n    fn blocking_inner(&mut self, rx: &mut [u8], tx: &[u8]) -> Result<(), Error> {\n        match self.blocking_inner_from_ram(rx, tx) {\n            Ok(_) => Ok(()),\n            Err(Error::BufferNotInRAM) => {\n                // trace!(\"Copying SPIM tx buffer into RAM for DMA\");\n                let tx_ram_buf = &mut [0; FORCE_COPY_BUFFER_SIZE][..tx.len()];\n                tx_ram_buf.copy_from_slice(tx);\n                self.blocking_inner_from_ram(rx, tx_ram_buf)\n            }\n        }\n    }\n\n    async fn async_inner_from_ram_chunk(&mut self, rx: *mut [u8], tx: *const [u8], offset: usize, length: usize) {\n        self.prepare_dma_transfer(rx, tx, offset, length);\n\n        #[cfg(feature = \"_nrf52832_anomaly_109\")]\n        if offset == 0 {\n            poll_fn(|cx| {\n                let s = self.state;\n\n                s.waker.register(cx.waker());\n\n                self.nrf52832_dma_workaround_status()\n            })\n            .await;\n        }\n\n        // Wait for 'end' event.\n        poll_fn(|cx| {\n            self.state.waker.register(cx.waker());\n            if self.r.events_end().read() != 0 {\n                return Poll::Ready(());\n            }\n\n            Poll::Pending\n        })\n        .await;\n\n        compiler_fence(Ordering::SeqCst);\n    }\n\n    async fn async_inner_from_ram(&mut self, rx: *mut [u8], tx: *const [u8]) -> Result<(), Error> {\n        slice_in_ram_or(tx, Error::BufferNotInRAM)?;\n        // NOTE: RAM slice check for rx is not necessary, as a mutable\n        // slice can only be built from data located in RAM.\n\n        let xfer_len = core::cmp::max(rx.len(), tx.len());\n        for offset in (0..xfer_len).step_by(EASY_DMA_SIZE) {\n            let length = core::cmp::min(xfer_len - offset, EASY_DMA_SIZE);\n            self.async_inner_from_ram_chunk(rx, tx, offset, length).await;\n        }\n        Ok(())\n    }\n\n    async fn async_inner(&mut self, rx: &mut [u8], tx: &[u8]) -> Result<(), Error> {\n        match self.async_inner_from_ram(rx, tx).await {\n            Ok(_) => Ok(()),\n            Err(Error::BufferNotInRAM) => {\n                // trace!(\"Copying SPIM tx buffer into RAM for DMA\");\n                let tx_ram_buf = &mut [0; FORCE_COPY_BUFFER_SIZE][..tx.len()];\n                tx_ram_buf.copy_from_slice(tx);\n                self.async_inner_from_ram(rx, tx_ram_buf).await\n            }\n        }\n    }\n\n    /// Reads data from the SPI bus without sending anything. Blocks until the buffer has been filled.\n    pub fn blocking_read(&mut self, data: &mut [u8]) -> Result<(), Error> {\n        self.blocking_inner(data, &[])\n    }\n\n    /// Simultaneously sends and receives data. Blocks until the transmission is completed.\n    /// If necessary, the write buffer will be copied into RAM (see struct description for detail).\n    pub fn blocking_transfer(&mut self, read: &mut [u8], write: &[u8]) -> Result<(), Error> {\n        self.blocking_inner(read, write)\n    }\n\n    /// Same as [`blocking_transfer`](Spim::blocking_transfer) but will fail instead of copying data into RAM. Consult the module level documentation to learn more.\n    pub fn blocking_transfer_from_ram(&mut self, read: &mut [u8], write: &[u8]) -> Result<(), Error> {\n        self.blocking_inner(read, write)\n    }\n\n    /// Simultaneously sends and receives data.\n    /// Places the received data into the same buffer and blocks until the transmission is completed.\n    pub fn blocking_transfer_in_place(&mut self, data: &mut [u8]) -> Result<(), Error> {\n        self.blocking_inner_from_ram(data, data)\n    }\n\n    /// Sends data, discarding any received data. Blocks  until the transmission is completed.\n    /// If necessary, the write buffer will be copied into RAM (see struct description for detail).\n    pub fn blocking_write(&mut self, data: &[u8]) -> Result<(), Error> {\n        self.blocking_inner(&mut [], data)\n    }\n\n    /// Same as [`blocking_write`](Spim::blocking_write) but will fail instead of copying data into RAM. Consult the module level documentation to learn more.\n    pub fn blocking_write_from_ram(&mut self, data: &[u8]) -> Result<(), Error> {\n        self.blocking_inner(&mut [], data)\n    }\n\n    /// Reads data from the SPI bus without sending anything.\n    pub async fn read(&mut self, data: &mut [u8]) -> Result<(), Error> {\n        self.async_inner(data, &[]).await\n    }\n\n    /// Simultaneously sends and receives data.\n    /// If necessary, the write buffer will be copied into RAM (see struct description for detail).\n    pub async fn transfer(&mut self, read: &mut [u8], write: &[u8]) -> Result<(), Error> {\n        self.async_inner(read, write).await\n    }\n\n    /// Same as [`transfer`](Spim::transfer) but will fail instead of copying data into RAM. Consult the module level documentation to learn more.\n    pub async fn transfer_from_ram(&mut self, read: &mut [u8], write: &[u8]) -> Result<(), Error> {\n        self.async_inner_from_ram(read, write).await\n    }\n\n    /// Simultaneously sends and receives data. Places the received data into the same buffer.\n    pub async fn transfer_in_place(&mut self, data: &mut [u8]) -> Result<(), Error> {\n        self.async_inner_from_ram(data, data).await\n    }\n\n    /// Sends data, discarding any received data.\n    /// If necessary, the write buffer will be copied into RAM (see struct description for detail).\n    pub async fn write(&mut self, data: &[u8]) -> Result<(), Error> {\n        self.async_inner(&mut [], data).await\n    }\n\n    /// Same as [`write`](Spim::write) but will fail instead of copying data into RAM. Consult the module level documentation to learn more.\n    pub async fn write_from_ram(&mut self, data: &[u8]) -> Result<(), Error> {\n        self.async_inner_from_ram(&mut [], data).await\n    }\n\n    #[cfg(feature = \"_nrf52832_anomaly_109\")]\n    fn nrf52832_dma_workaround_status(&mut self) -> Poll<()> {\n        let r = self.r;\n        if r.events_started().read() != 0 {\n            let s = self.state;\n\n            // Handle the first \"fake\" transmission\n            r.events_started().write_value(0);\n            r.events_end().write_value(0);\n\n            // Update DMA registers with correct rx/tx buffer sizes\n            r.dma()\n                .rx()\n                .maxcnt()\n                .write(|w| w.set_maxcnt(s.rx.load(Ordering::Relaxed)));\n            r.dma()\n                .tx()\n                .maxcnt()\n                .write(|w| w.set_maxcnt(s.tx.load(Ordering::Relaxed)));\n\n            r.intenset().write(|w| w.set_end(true));\n            // ... and start actual, hopefully glitch-free transmission\n            r.tasks_start().write_value(1);\n            return Poll::Ready(());\n        }\n        Poll::Pending\n    }\n}\n\nimpl<'d> Drop for Spim<'d> {\n    fn drop(&mut self) {\n        trace!(\"spim drop\");\n\n        // TODO check for abort, wait for xxxstopped\n\n        // disable!\n        let r = self.r;\n        r.enable().write(|w| w.set_enable(vals::Enable::DISABLED));\n\n        gpio::deconfigure_pin(r.psel().sck().read());\n        gpio::deconfigure_pin(r.psel().miso().read());\n        gpio::deconfigure_pin(r.psel().mosi().read());\n\n        // Disable all events interrupts\n        cortex_m::peripheral::NVIC::mask(self.irq);\n\n        trace!(\"spim drop: done\");\n    }\n}\n\npub(crate) struct State {\n    waker: AtomicWaker,\n    #[cfg(feature = \"_nrf52832_anomaly_109\")]\n    rx: AtomicU8,\n    #[cfg(feature = \"_nrf52832_anomaly_109\")]\n    tx: AtomicU8,\n}\n\nimpl State {\n    pub(crate) const fn new() -> Self {\n        Self {\n            waker: AtomicWaker::new(),\n            #[cfg(feature = \"_nrf52832_anomaly_109\")]\n            rx: AtomicU8::new(0),\n            #[cfg(feature = \"_nrf52832_anomaly_109\")]\n            tx: AtomicU8::new(0),\n        }\n    }\n}\n\npub(crate) trait SealedInstance {\n    fn regs() -> pac::spim::Spim;\n    fn state() -> &'static State;\n    #[cfg(feature = \"_nrf54l\")]\n    fn clk() -> u32;\n}\n\n/// SPIM peripheral instance\n#[allow(private_bounds)]\npub trait Instance: SealedInstance + PeripheralType + 'static {\n    /// Interrupt for this peripheral.\n    type Interrupt: interrupt::typelevel::Interrupt;\n}\n\n#[cfg(feature = \"_nrf54l\")]\nmacro_rules! impl_spim {\n    ($type:ident, $pac_type:ident, $irq:ident, $clk:expr) => {\n        impl crate::spim::SealedInstance for peripherals::$type {\n            fn regs() -> pac::spim::Spim {\n                pac::$pac_type\n            }\n            fn state() -> &'static crate::spim::State {\n                static STATE: crate::spim::State = crate::spim::State::new();\n                &STATE\n            }\n            fn clk() -> u32 {\n                $clk\n            }\n        }\n        impl crate::spim::Instance for peripherals::$type {\n            type Interrupt = crate::interrupt::typelevel::$irq;\n        }\n    };\n}\n\n#[cfg(not(feature = \"_nrf54l\"))]\nmacro_rules! impl_spim {\n    ($type:ident, $pac_type:ident, $irq:ident) => {\n        impl crate::spim::SealedInstance for peripherals::$type {\n            fn regs() -> pac::spim::Spim {\n                pac::$pac_type\n            }\n            fn state() -> &'static crate::spim::State {\n                static STATE: crate::spim::State = crate::spim::State::new();\n                &STATE\n            }\n        }\n        impl crate::spim::Instance for peripherals::$type {\n            type Interrupt = crate::interrupt::typelevel::$irq;\n        }\n    };\n}\n\n// ====================\n\nmod eh02 {\n    use super::*;\n\n    impl<'d> embedded_hal_02::blocking::spi::Transfer<u8> for Spim<'d> {\n        type Error = Error;\n        fn transfer<'w>(&mut self, words: &'w mut [u8]) -> Result<&'w [u8], Self::Error> {\n            self.blocking_transfer_in_place(words)?;\n            Ok(words)\n        }\n    }\n\n    impl<'d> embedded_hal_02::blocking::spi::Write<u8> for Spim<'d> {\n        type Error = Error;\n\n        fn write(&mut self, words: &[u8]) -> Result<(), Self::Error> {\n            self.blocking_write(words)\n        }\n    }\n}\n\nimpl embedded_hal_1::spi::Error for Error {\n    fn kind(&self) -> embedded_hal_1::spi::ErrorKind {\n        match *self {\n            Self::BufferNotInRAM => embedded_hal_1::spi::ErrorKind::Other,\n        }\n    }\n}\n\nimpl<'d> embedded_hal_1::spi::ErrorType for Spim<'d> {\n    type Error = Error;\n}\n\nimpl<'d> embedded_hal_1::spi::SpiBus<u8> for Spim<'d> {\n    fn flush(&mut self) -> Result<(), Self::Error> {\n        Ok(())\n    }\n\n    fn read(&mut self, words: &mut [u8]) -> Result<(), Self::Error> {\n        self.blocking_transfer(words, &[])\n    }\n\n    fn write(&mut self, words: &[u8]) -> Result<(), Self::Error> {\n        self.blocking_write(words)\n    }\n\n    fn transfer(&mut self, read: &mut [u8], write: &[u8]) -> Result<(), Self::Error> {\n        self.blocking_transfer(read, write)\n    }\n\n    fn transfer_in_place(&mut self, words: &mut [u8]) -> Result<(), Self::Error> {\n        self.blocking_transfer_in_place(words)\n    }\n}\n\nimpl<'d> embedded_hal_async::spi::SpiBus<u8> for Spim<'d> {\n    async fn flush(&mut self) -> Result<(), Error> {\n        Ok(())\n    }\n\n    async fn read(&mut self, words: &mut [u8]) -> Result<(), Error> {\n        self.read(words).await\n    }\n\n    async fn write(&mut self, data: &[u8]) -> Result<(), Error> {\n        self.write(data).await\n    }\n\n    async fn transfer(&mut self, rx: &mut [u8], tx: &[u8]) -> Result<(), Error> {\n        self.transfer(rx, tx).await\n    }\n\n    async fn transfer_in_place(&mut self, words: &mut [u8]) -> Result<(), Error> {\n        self.transfer_in_place(words).await\n    }\n}\n\nimpl<'d> SetConfig for Spim<'d> {\n    type Config = Config;\n    type ConfigError = ();\n    fn set_config(&mut self, config: &Self::Config) -> Result<(), Self::ConfigError> {\n        let r = self.r;\n        // Configure mode.\n        let mode = config.mode;\n        r.config().write(|w| {\n            w.set_order(config.bit_order);\n            match mode {\n                MODE_0 => {\n                    w.set_cpol(vals::Cpol::ACTIVE_HIGH);\n                    w.set_cpha(vals::Cpha::LEADING);\n                }\n                MODE_1 => {\n                    w.set_cpol(vals::Cpol::ACTIVE_HIGH);\n                    w.set_cpha(vals::Cpha::TRAILING);\n                }\n                MODE_2 => {\n                    w.set_cpol(vals::Cpol::ACTIVE_LOW);\n                    w.set_cpha(vals::Cpha::LEADING);\n                }\n                MODE_3 => {\n                    w.set_cpol(vals::Cpol::ACTIVE_LOW);\n                    w.set_cpha(vals::Cpha::TRAILING);\n                }\n            }\n        });\n\n        // Configure frequency.\n        let frequency = config.frequency;\n        #[cfg(not(feature = \"_nrf54l\"))]\n        r.frequency().write(|w| w.set_frequency(frequency.into()));\n        #[cfg(feature = \"_nrf54l\")]\n        {\n            r.prescaler().write(|w| w.set_divisor(frequency.to_divisor(self.clk)));\n        }\n\n        // Set over-read character\n        let orc = config.orc;\n        r.orc().write(|w| w.set_orc(orc));\n\n        Ok(())\n    }\n}\n"
  },
  {
    "path": "embassy-nrf/src/spis.rs",
    "content": "//! Serial Peripheral Instance in slave mode (SPIS) driver.\n\n#![macro_use]\nuse core::future::poll_fn;\nuse core::marker::PhantomData;\nuse core::sync::atomic::{Ordering, compiler_fence};\nuse core::task::Poll;\n\nuse embassy_embedded_hal::SetConfig;\nuse embassy_hal_internal::{Peri, PeripheralType};\nuse embassy_sync::waitqueue::AtomicWaker;\npub use embedded_hal_02::spi::{MODE_0, MODE_1, MODE_2, MODE_3, Mode, Phase, Polarity};\npub use pac::spis::vals::Order as BitOrder;\n\nuse crate::chip::{EASY_DMA_SIZE, FORCE_COPY_BUFFER_SIZE};\nuse crate::gpio::{self, AnyPin, OutputDrive, Pin as GpioPin, SealedPin as _, convert_drive};\nuse crate::interrupt::typelevel::Interrupt;\nuse crate::pac::gpio::vals as gpiovals;\nuse crate::pac::spis::vals;\nuse crate::ppi::Event;\nuse crate::util::slice_in_ram_or;\nuse crate::{interrupt, pac};\n\n/// SPIS error\n#[derive(Debug, Clone, Copy, PartialEq, Eq)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\n#[non_exhaustive]\npub enum Error {\n    /// TX buffer was too long.\n    TxBufferTooLong,\n    /// RX buffer was too long.\n    RxBufferTooLong,\n    /// EasyDMA can only read from data memory, read only buffers in flash will fail.\n    BufferNotInRAM,\n}\n\n/// SPIS configuration.\n#[non_exhaustive]\npub struct Config {\n    /// SPI mode\n    pub mode: Mode,\n\n    /// Bit order\n    pub bit_order: BitOrder,\n\n    /// Overread character.\n    ///\n    /// If the master keeps clocking the bus after all the bytes in the TX buffer have\n    /// already been transmitted, this byte will be constantly transmitted in the MISO line.\n    pub orc: u8,\n\n    /// Default byte.\n    ///\n    /// This is the byte clocked out in the MISO line for ignored transactions (if the master\n    /// sets CSN low while the semaphore is owned by the firmware)\n    pub def: u8,\n\n    /// Automatically make the firmware side acquire the semaphore on transfer end.\n    pub auto_acquire: bool,\n\n    /// Drive strength for the MISO line.\n    pub miso_drive: OutputDrive,\n}\n\nimpl Default for Config {\n    fn default() -> Self {\n        Self {\n            mode: MODE_0,\n            bit_order: BitOrder::MSB_FIRST,\n            orc: 0x00,\n            def: 0x00,\n            auto_acquire: true,\n            miso_drive: OutputDrive::HighDrive,\n        }\n    }\n}\n\n/// Interrupt handler.\npub struct InterruptHandler<T: Instance> {\n    _phantom: PhantomData<T>,\n}\n\nimpl<T: Instance> interrupt::typelevel::Handler<T::Interrupt> for InterruptHandler<T> {\n    unsafe fn on_interrupt() {\n        let r = T::regs();\n        let s = T::state();\n\n        if r.events_end().read() != 0 {\n            s.waker.wake();\n            r.intenclr().write(|w| w.set_end(true));\n        }\n\n        if r.events_acquired().read() != 0 {\n            s.waker.wake();\n            r.intenclr().write(|w| w.set_acquired(true));\n        }\n    }\n}\n\n/// Serial Peripheral Interface in slave mode.\npub struct Spis<'d> {\n    r: pac::spis::Spis,\n    state: &'static State,\n    _p: PhantomData<&'d ()>,\n}\n\nimpl<'d> Spis<'d> {\n    /// Create a new SPIS driver.\n    pub fn new<T: Instance>(\n        spis: Peri<'d, T>,\n        _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd,\n        cs: Peri<'d, impl GpioPin>,\n        sck: Peri<'d, impl GpioPin>,\n        miso: Peri<'d, impl GpioPin>,\n        mosi: Peri<'d, impl GpioPin>,\n        config: Config,\n    ) -> Self {\n        Self::new_inner(\n            spis,\n            cs.into(),\n            Some(sck.into()),\n            Some(miso.into()),\n            Some(mosi.into()),\n            config,\n        )\n    }\n\n    /// Create a new SPIS driver, capable of TX only (MISO only).\n    pub fn new_txonly<T: Instance>(\n        spis: Peri<'d, T>,\n        _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd,\n        cs: Peri<'d, impl GpioPin>,\n        sck: Peri<'d, impl GpioPin>,\n        miso: Peri<'d, impl GpioPin>,\n        config: Config,\n    ) -> Self {\n        Self::new_inner(spis, cs.into(), Some(sck.into()), Some(miso.into()), None, config)\n    }\n\n    /// Create a new SPIS driver, capable of RX only (MOSI only).\n    pub fn new_rxonly<T: Instance>(\n        spis: Peri<'d, T>,\n        _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd,\n        cs: Peri<'d, impl GpioPin>,\n        sck: Peri<'d, impl GpioPin>,\n        mosi: Peri<'d, impl GpioPin>,\n        config: Config,\n    ) -> Self {\n        Self::new_inner(spis, cs.into(), Some(sck.into()), None, Some(mosi.into()), config)\n    }\n\n    /// Create a new SPIS driver, capable of TX only (MISO only) without SCK pin.\n    pub fn new_txonly_nosck<T: Instance>(\n        spis: Peri<'d, T>,\n        _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd,\n        cs: Peri<'d, impl GpioPin>,\n        miso: Peri<'d, impl GpioPin>,\n        config: Config,\n    ) -> Self {\n        Self::new_inner(spis, cs.into(), None, Some(miso.into()), None, config)\n    }\n\n    fn new_inner<T: Instance>(\n        _spis: Peri<'d, T>,\n        cs: Peri<'d, AnyPin>,\n        sck: Option<Peri<'d, AnyPin>>,\n        miso: Option<Peri<'d, AnyPin>>,\n        mosi: Option<Peri<'d, AnyPin>>,\n        config: Config,\n    ) -> Self {\n        compiler_fence(Ordering::SeqCst);\n\n        let r = T::regs();\n\n        // Configure pins.\n        cs.conf().write(|w| w.set_input(gpiovals::Input::CONNECT));\n        r.psel().csn().write_value(cs.psel_bits());\n        if let Some(sck) = &sck {\n            sck.conf().write(|w| w.set_input(gpiovals::Input::CONNECT));\n            r.psel().sck().write_value(sck.psel_bits());\n        }\n        if let Some(mosi) = &mosi {\n            mosi.conf().write(|w| w.set_input(gpiovals::Input::CONNECT));\n            r.psel().mosi().write_value(mosi.psel_bits());\n        }\n        if let Some(miso) = &miso {\n            miso.conf().write(|w| {\n                w.set_dir(gpiovals::Dir::OUTPUT);\n                convert_drive(w, config.miso_drive);\n            });\n            r.psel().miso().write_value(miso.psel_bits());\n        }\n\n        // Enable SPIS instance.\n        r.enable().write(|w| w.set_enable(vals::Enable::ENABLED));\n\n        let mut spis = Self {\n            r: T::regs(),\n            state: T::state(),\n            _p: PhantomData,\n        };\n\n        // Apply runtime peripheral configuration\n        spis.set_config(&config).unwrap();\n\n        // Disable all events interrupts.\n        r.intenclr().write(|w| w.0 = 0xFFFF_FFFF);\n\n        T::Interrupt::unpend();\n        unsafe { T::Interrupt::enable() };\n\n        spis\n    }\n\n    fn prepare(&mut self, rx: *mut [u8], tx: *const [u8]) -> Result<(), Error> {\n        slice_in_ram_or(tx, Error::BufferNotInRAM)?;\n        // NOTE: RAM slice check for rx is not necessary, as a mutable\n        // slice can only be built from data located in RAM.\n\n        compiler_fence(Ordering::SeqCst);\n\n        let r = self.r;\n\n        // Set up the DMA write.\n        if tx.len() > EASY_DMA_SIZE {\n            return Err(Error::TxBufferTooLong);\n        }\n        r.dma().tx().ptr().write_value(tx as *const u8 as _);\n        r.dma().tx().maxcnt().write(|w| w.set_maxcnt(tx.len() as _));\n\n        // Set up the DMA read.\n        if rx.len() > EASY_DMA_SIZE {\n            return Err(Error::RxBufferTooLong);\n        }\n        r.dma().rx().ptr().write_value(rx as *mut u8 as _);\n        r.dma().rx().maxcnt().write(|w| w.set_maxcnt(rx.len() as _));\n\n        // Reset end event.\n        r.events_end().write_value(0);\n\n        // Release the semaphore.\n        r.tasks_release().write_value(1);\n\n        Ok(())\n    }\n\n    fn blocking_inner_from_ram(&mut self, rx: *mut [u8], tx: *const [u8]) -> Result<(usize, usize), Error> {\n        compiler_fence(Ordering::SeqCst);\n        let r = self.r;\n\n        // Acquire semaphore.\n        if r.semstat().read().0 != 1 {\n            r.events_acquired().write_value(0);\n            r.tasks_acquire().write_value(1);\n            // Wait until CPU has acquired the semaphore.\n            while r.semstat().read().0 != 1 {}\n        }\n\n        self.prepare(rx, tx)?;\n\n        // Wait for 'end' event.\n        while r.events_end().read() == 0 {}\n\n        let n_rx = r.dma().rx().amount().read().0 as usize;\n        let n_tx = r.dma().tx().amount().read().0 as usize;\n\n        compiler_fence(Ordering::SeqCst);\n\n        Ok((n_rx, n_tx))\n    }\n\n    fn blocking_inner(&mut self, rx: &mut [u8], tx: &[u8]) -> Result<(usize, usize), Error> {\n        match self.blocking_inner_from_ram(rx, tx) {\n            Ok(n) => Ok(n),\n            Err(Error::BufferNotInRAM) => {\n                trace!(\"Copying SPIS tx buffer into RAM for DMA\");\n                let tx_ram_buf = &mut [0; FORCE_COPY_BUFFER_SIZE][..tx.len()];\n                tx_ram_buf.copy_from_slice(tx);\n                self.blocking_inner_from_ram(rx, tx_ram_buf)\n            }\n            Err(error) => Err(error),\n        }\n    }\n\n    async fn async_inner_from_ram(&mut self, rx: *mut [u8], tx: *const [u8]) -> Result<(usize, usize), Error> {\n        let r = self.r;\n        let s = self.state;\n\n        // Clear status register.\n        r.status().write(|w| {\n            w.set_overflow(true);\n            w.set_overread(true);\n        });\n\n        // Acquire semaphore.\n        if r.semstat().read().0 != 1 {\n            // Reset and enable the acquire event.\n            r.events_acquired().write_value(0);\n            r.intenset().write(|w| w.set_acquired(true));\n\n            // Request acquiring the SPIS semaphore.\n            r.tasks_acquire().write_value(1);\n\n            // Wait until CPU has acquired the semaphore.\n            poll_fn(|cx| {\n                s.waker.register(cx.waker());\n                if r.events_acquired().read() == 1 {\n                    r.events_acquired().write_value(0);\n                    return Poll::Ready(());\n                }\n                Poll::Pending\n            })\n            .await;\n        }\n\n        self.prepare(rx, tx)?;\n\n        // Wait for 'end' event.\n        r.intenset().write(|w| w.set_end(true));\n        poll_fn(|cx| {\n            s.waker.register(cx.waker());\n            if r.events_end().read() != 0 {\n                r.events_end().write_value(0);\n                return Poll::Ready(());\n            }\n            Poll::Pending\n        })\n        .await;\n\n        let n_rx = r.dma().rx().amount().read().0 as usize;\n        let n_tx = r.dma().tx().amount().read().0 as usize;\n\n        compiler_fence(Ordering::SeqCst);\n\n        Ok((n_rx, n_tx))\n    }\n\n    /// Returns the ACQUIRED event, for use with PPI.\n    ///\n    /// This event will fire when the semaphore is acquired.\n    pub fn event_acquired(&self) -> Event<'d> {\n        Event::from_reg(self.r.events_acquired())\n    }\n\n    /// Returns the END event, for use with PPI.\n    ///\n    /// This event will fire when the slave transaction is complete.\n    pub fn event_end(&self) -> Event<'d> {\n        Event::from_reg(self.r.events_end())\n    }\n\n    async fn async_inner(&mut self, rx: &mut [u8], tx: &[u8]) -> Result<(usize, usize), Error> {\n        match self.async_inner_from_ram(rx, tx).await {\n            Ok(n) => Ok(n),\n            Err(Error::BufferNotInRAM) => {\n                trace!(\"Copying SPIS tx buffer into RAM for DMA\");\n                let tx_ram_buf = &mut [0; FORCE_COPY_BUFFER_SIZE][..tx.len()];\n                tx_ram_buf.copy_from_slice(tx);\n                self.async_inner_from_ram(rx, tx_ram_buf).await\n            }\n            Err(error) => Err(error),\n        }\n    }\n\n    /// Reads data from the SPI bus without sending anything. Blocks until `cs` is deasserted.\n    /// Returns number of bytes read.\n    pub fn blocking_read(&mut self, data: &mut [u8]) -> Result<usize, Error> {\n        self.blocking_inner(data, &[]).map(|n| n.0)\n    }\n\n    /// Simultaneously sends and receives data. Blocks until the transmission is completed.\n    /// If necessary, the write buffer will be copied into RAM (see struct description for detail).\n    /// Returns number of bytes transferred `(n_rx, n_tx)`.\n    pub fn blocking_transfer(&mut self, read: &mut [u8], write: &[u8]) -> Result<(usize, usize), Error> {\n        self.blocking_inner(read, write)\n    }\n\n    /// Same as [`blocking_transfer`](Spis::blocking_transfer) but will fail instead of copying data into RAM. Consult the module level documentation to learn more.\n    /// Returns number of bytes transferred `(n_rx, n_tx)`.\n    pub fn blocking_transfer_from_ram(&mut self, read: &mut [u8], write: &[u8]) -> Result<(usize, usize), Error> {\n        self.blocking_inner_from_ram(read, write)\n    }\n\n    /// Simultaneously sends and receives data.\n    /// Places the received data into the same buffer and blocks until the transmission is completed.\n    /// Returns number of bytes transferred.\n    pub fn blocking_transfer_in_place(&mut self, data: &mut [u8]) -> Result<usize, Error> {\n        self.blocking_inner_from_ram(data, data).map(|n| n.0)\n    }\n\n    /// Sends data, discarding any received data. Blocks  until the transmission is completed.\n    /// If necessary, the write buffer will be copied into RAM (see struct description for detail).\n    /// Returns number of bytes written.\n    pub fn blocking_write(&mut self, data: &[u8]) -> Result<usize, Error> {\n        self.blocking_inner(&mut [], data).map(|n| n.1)\n    }\n\n    /// Same as [`blocking_write`](Spis::blocking_write) but will fail instead of copying data into RAM. Consult the module level documentation to learn more.\n    /// Returns number of bytes written.\n    pub fn blocking_write_from_ram(&mut self, data: &[u8]) -> Result<usize, Error> {\n        self.blocking_inner_from_ram(&mut [], data).map(|n| n.1)\n    }\n\n    /// Reads data from the SPI bus without sending anything.\n    /// Returns number of bytes read.\n    pub async fn read(&mut self, data: &mut [u8]) -> Result<usize, Error> {\n        self.async_inner(data, &[]).await.map(|n| n.0)\n    }\n\n    /// Simultaneously sends and receives data.\n    /// If necessary, the write buffer will be copied into RAM (see struct description for detail).\n    /// Returns number of bytes transferred `(n_rx, n_tx)`.\n    pub async fn transfer(&mut self, read: &mut [u8], write: &[u8]) -> Result<(usize, usize), Error> {\n        self.async_inner(read, write).await\n    }\n\n    /// Same as [`transfer`](Spis::transfer) but will fail instead of copying data into RAM. Consult the module level documentation to learn more.\n    /// Returns number of bytes transferred `(n_rx, n_tx)`.\n    pub async fn transfer_from_ram(&mut self, read: &mut [u8], write: &[u8]) -> Result<(usize, usize), Error> {\n        self.async_inner_from_ram(read, write).await\n    }\n\n    /// Simultaneously sends and receives data. Places the received data into the same buffer.\n    /// Returns number of bytes transferred.\n    pub async fn transfer_in_place(&mut self, data: &mut [u8]) -> Result<usize, Error> {\n        self.async_inner_from_ram(data, data).await.map(|n| n.0)\n    }\n\n    /// Sends data, discarding any received data.\n    /// If necessary, the write buffer will be copied into RAM (see struct description for detail).\n    /// Returns number of bytes written.\n    pub async fn write(&mut self, data: &[u8]) -> Result<usize, Error> {\n        self.async_inner(&mut [], data).await.map(|n| n.1)\n    }\n\n    /// Same as [`write`](Spis::write) but will fail instead of copying data into RAM. Consult the module level documentation to learn more.\n    /// Returns number of bytes written.\n    pub async fn write_from_ram(&mut self, data: &[u8]) -> Result<usize, Error> {\n        self.async_inner_from_ram(&mut [], data).await.map(|n| n.1)\n    }\n\n    /// Checks if last transaction overread.\n    pub fn is_overread(&mut self) -> bool {\n        self.r.status().read().overread()\n    }\n\n    /// Checks if last transaction overflowed.\n    pub fn is_overflow(&mut self) -> bool {\n        self.r.status().read().overflow()\n    }\n}\n\nimpl<'d> Drop for Spis<'d> {\n    fn drop(&mut self) {\n        trace!(\"spis drop\");\n\n        // Disable\n        let r = self.r;\n        r.enable().write(|w| w.set_enable(vals::Enable::DISABLED));\n\n        gpio::deconfigure_pin(r.psel().sck().read());\n        gpio::deconfigure_pin(r.psel().csn().read());\n        gpio::deconfigure_pin(r.psel().miso().read());\n        gpio::deconfigure_pin(r.psel().mosi().read());\n\n        trace!(\"spis drop: done\");\n    }\n}\n\npub(crate) struct State {\n    waker: AtomicWaker,\n}\n\nimpl State {\n    pub(crate) const fn new() -> Self {\n        Self {\n            waker: AtomicWaker::new(),\n        }\n    }\n}\n\npub(crate) trait SealedInstance {\n    fn regs() -> pac::spis::Spis;\n    fn state() -> &'static State;\n}\n\n/// SPIS peripheral instance\n#[allow(private_bounds)]\npub trait Instance: SealedInstance + PeripheralType + 'static {\n    /// Interrupt for this peripheral.\n    type Interrupt: interrupt::typelevel::Interrupt;\n}\n\nmacro_rules! impl_spis {\n    ($type:ident, $pac_type:ident, $irq:ident) => {\n        impl crate::spis::SealedInstance for peripherals::$type {\n            fn regs() -> pac::spis::Spis {\n                pac::$pac_type\n            }\n            fn state() -> &'static crate::spis::State {\n                static STATE: crate::spis::State = crate::spis::State::new();\n                &STATE\n            }\n        }\n        impl crate::spis::Instance for peripherals::$type {\n            type Interrupt = crate::interrupt::typelevel::$irq;\n        }\n    };\n}\n\n// ====================\n\nimpl<'d> SetConfig for Spis<'d> {\n    type Config = Config;\n    type ConfigError = ();\n    fn set_config(&mut self, config: &Self::Config) -> Result<(), Self::ConfigError> {\n        let r = self.r;\n        // Configure mode.\n        let mode = config.mode;\n        r.config().write(|w| {\n            w.set_order(config.bit_order);\n            match mode {\n                MODE_0 => {\n                    w.set_cpol(vals::Cpol::ACTIVE_HIGH);\n                    w.set_cpha(vals::Cpha::LEADING);\n                }\n                MODE_1 => {\n                    w.set_cpol(vals::Cpol::ACTIVE_HIGH);\n                    w.set_cpha(vals::Cpha::TRAILING);\n                }\n                MODE_2 => {\n                    w.set_cpol(vals::Cpol::ACTIVE_LOW);\n                    w.set_cpha(vals::Cpha::LEADING);\n                }\n                MODE_3 => {\n                    w.set_cpol(vals::Cpol::ACTIVE_LOW);\n                    w.set_cpha(vals::Cpha::TRAILING);\n                }\n            }\n        });\n\n        // Set over-read character.\n        let orc = config.orc;\n        r.orc().write(|w| w.set_orc(orc));\n\n        // Set default character.\n        let def = config.def;\n        r.def().write(|w| w.set_def(def));\n\n        // Configure auto-acquire on 'transfer end' event.\n        let auto_acquire = config.auto_acquire;\n        r.shorts().write(|w| w.set_end_acquire(auto_acquire));\n\n        Ok(())\n    }\n}\n"
  },
  {
    "path": "embassy-nrf/src/temp.rs",
    "content": "//! Builtin temperature sensor driver.\n\nuse core::future::poll_fn;\nuse core::task::Poll;\n\nuse embassy_hal_internal::drop::OnDrop;\nuse embassy_sync::waitqueue::AtomicWaker;\nuse fixed::types::I30F2;\n\nuse crate::interrupt::InterruptExt;\nuse crate::peripherals::TEMP;\nuse crate::{Peri, interrupt, pac};\n\n/// Interrupt handler.\npub struct InterruptHandler {\n    _private: (),\n}\n\nimpl interrupt::typelevel::Handler<interrupt::typelevel::TEMP> for InterruptHandler {\n    unsafe fn on_interrupt() {\n        let r = pac::TEMP;\n        r.intenclr().write(|w| w.set_datardy(true));\n        WAKER.wake();\n    }\n}\n\n/// Builtin temperature sensor driver.\npub struct Temp<'d> {\n    _peri: Peri<'d, TEMP>,\n}\n\nstatic WAKER: AtomicWaker = AtomicWaker::new();\n\nimpl<'d> Temp<'d> {\n    /// Create a new temperature sensor driver.\n    pub fn new(\n        _peri: Peri<'d, TEMP>,\n        _irq: impl interrupt::typelevel::Binding<interrupt::typelevel::TEMP, InterruptHandler> + 'd,\n    ) -> Self {\n        // Enable interrupt that signals temperature values\n        interrupt::TEMP.unpend();\n        unsafe { interrupt::TEMP.enable() };\n\n        Self { _peri }\n    }\n\n    /// Perform an asynchronous temperature measurement. The returned future\n    /// can be awaited to obtain the measurement.\n    ///\n    /// If the future is dropped, the measurement is cancelled.\n    ///\n    /// # Example\n    ///\n    /// ```no_run\n    /// use embassy_nrf::{bind_interrupts, temp};\n    /// use embassy_nrf::temp::Temp;\n    ///\n    /// bind_interrupts!(struct Irqs {\n    ///     TEMP => temp::InterruptHandler;\n    /// });\n    ///\n    /// # async {\n    /// # let p: embassy_nrf::Peripherals = todo!();\n    /// let mut t = Temp::new(p.TEMP, Irqs);\n    /// let v: u16 = t.read().await.to_num::<u16>();\n    /// # };\n    /// ```\n    pub async fn read(&mut self) -> I30F2 {\n        // In case the future is dropped, stop the task and reset events.\n        let on_drop = OnDrop::new(|| {\n            let t = Self::regs();\n            t.tasks_stop().write_value(1);\n            t.events_datardy().write_value(0);\n        });\n\n        let t = Self::regs();\n        t.intenset().write(|w| w.set_datardy(true));\n        t.tasks_start().write_value(1);\n\n        let value = poll_fn(|cx| {\n            WAKER.register(cx.waker());\n            if t.events_datardy().read() == 0 {\n                Poll::Pending\n            } else {\n                t.events_datardy().write_value(0);\n                let raw = t.temp().read();\n                Poll::Ready(I30F2::from_bits(raw as i32))\n            }\n        })\n        .await;\n        on_drop.defuse();\n        value\n    }\n\n    fn regs() -> pac::temp::Temp {\n        pac::TEMP\n    }\n}\n"
  },
  {
    "path": "embassy-nrf/src/time_driver.rs",
    "content": "use core::cell::{Cell, RefCell};\n#[cfg(not(feature = \"_grtc\"))]\nuse core::sync::atomic::{AtomicU32, Ordering, compiler_fence};\n\nuse critical_section::CriticalSection;\nuse embassy_sync::blocking_mutex::CriticalSectionMutex as Mutex;\nuse embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;\nuse embassy_time_driver::Driver;\nuse embassy_time_queue_utils::Queue;\n\nuse crate::interrupt::InterruptExt;\n#[cfg(feature = \"_grtc\")]\nuse crate::pac::grtc::vals::Busy;\nuse crate::{interrupt, pac};\n\n#[cfg(feature = \"_grtc\")]\nfn rtc() -> pac::grtc::Grtc {\n    pac::GRTC\n}\n\n#[cfg(not(feature = \"_grtc\"))]\nfn rtc() -> pac::rtc::Rtc {\n    pac::RTC1\n}\n\n/// Calculate the timestamp from the period count and the tick count.\n///\n/// For nRF54 devices and newer, the GRTC counter is 52 bits, so the time driver uses the\n/// syscounter and ignores the periods handling, since it overflows every 142 years.\n///\n/// For most other devices, the RTC counter is 24 bit. Ticking at 32768hz, it overflows every ~8 minutes.\n/// This is too short. We must make it \"never\" overflow.\n///\n/// The obvious way would be to count overflow periods. Every time the counter overflows,\n/// increase a `periods` variable. `now()` simply does `periods << 24 + counter`. So, the logic\n/// around an overflow would look like this:\n///\n/// ```not_rust\n/// periods = 1, counter = 0xFF_FFFE --> now = 0x1FF_FFFE\n/// periods = 1, counter = 0xFF_FFFF --> now = 0x1FF_FFFF\n/// **OVERFLOW**\n/// periods = 2, counter = 0x00_0000 --> now = 0x200_0000\n/// periods = 2, counter = 0x00_0001 --> now = 0x200_0001\n/// ```\n///\n/// The problem is this is vulnerable to race conditions if `now()` runs at the exact time an\n/// overflow happens.\n///\n/// If `now()` reads `periods` first and `counter` later, and overflow happens between the reads,\n/// it would return a wrong value:\n///\n/// ```not_rust\n/// periods = 1 (OLD), counter = 0x00_0000 (NEW) --> now = 0x100_0000 -> WRONG\n/// ```\n///\n/// It fails similarly if it reads `counter` first and `periods` second.\n///\n/// To fix this, we define a \"period\" to be 2^23 ticks (instead of 2^24). One \"overflow cycle\" is 2 periods.\n///\n/// - `period` is incremented on overflow (at counter value 0)\n/// - `period` is incremented \"midway\" between overflows (at counter value 0x80_0000)\n///\n/// Therefore, when `period` is even, counter is in 0..0x7f_ffff. When odd, counter is in 0x80_0000..0xFF_FFFF\n/// This allows for now() to return the correct value even if it races an overflow.\n///\n/// To get `now()`, `period` is read first, then `counter` is read. If the counter value matches\n/// the expected range for the `period` parity, we're done. If it doesn't, this means that\n/// a new period start has raced us between reading `period` and `counter`, so we assume the `counter` value\n/// corresponds to the next period.\n///\n/// `period` is a 32bit integer, so It overflows on 2^32 * 2^23 / 32768 seconds of uptime, which is 34865\n/// years. For comparison, flash memory like the one containing your firmware is usually rated to retain\n/// data for only 10-20 years. 34865 years is long enough!\n#[cfg(not(feature = \"_grtc\"))]\nfn calc_now(period: u32, counter: u32) -> u64 {\n    ((period as u64) << 23) + ((counter ^ ((period & 1) << 23)) as u64)\n}\n\n#[cfg(feature = \"_grtc\")]\nfn syscounter() -> u64 {\n    let r = rtc();\n    if !r.mode().read().syscounteren() {\n        return 0;\n    }\n\n    r.syscounter(0).active().write(|w| w.set_active(true));\n    loop {\n        let countl: u32 = r.syscounter(0).syscounterl().read();\n        let counth = r.syscounter(0).syscounterh().read();\n\n        if counth.busy() == Busy::READY && !counth.overflow() {\n            let counth: u32 = counth.value();\n            let count = countl as u64 | ((counth as u64) << 32);\n            r.syscounter(0).active().write(|w| w.set_active(false));\n            return count;\n        }\n        // If overflow or not ready, loop will re-read both registers\n    }\n}\n\n#[cfg(not(feature = \"_grtc\"))]\nfn compare_n(n: usize) -> u32 {\n    1 << (n + 16)\n}\n\n#[cfg(feature = \"_grtc\")]\nfn compare_n(n: usize) -> u32 {\n    1 << n // GRTC uses bits 0-11 for COMPARE[0-11]\n}\n\n#[cfg(test)]\nmod test {\n    use super::*;\n\n    #[test]\n    fn test_calc_now() {\n        assert_eq!(calc_now(0, 0x000000), 0x0_000000);\n        assert_eq!(calc_now(0, 0x000001), 0x0_000001);\n        assert_eq!(calc_now(0, 0x7FFFFF), 0x0_7FFFFF);\n        assert_eq!(calc_now(1, 0x7FFFFF), 0x1_7FFFFF);\n        assert_eq!(calc_now(0, 0x800000), 0x0_800000);\n        assert_eq!(calc_now(1, 0x800000), 0x0_800000);\n        assert_eq!(calc_now(1, 0x800001), 0x0_800001);\n        assert_eq!(calc_now(1, 0xFFFFFF), 0x0_FFFFFF);\n        assert_eq!(calc_now(2, 0xFFFFFF), 0x1_FFFFFF);\n        assert_eq!(calc_now(1, 0x000000), 0x1_000000);\n        assert_eq!(calc_now(2, 0x000000), 0x1_000000);\n    }\n}\n\nstruct AlarmState {\n    timestamp: Cell<u64>,\n}\n\nunsafe impl Send for AlarmState {}\n\nimpl AlarmState {\n    const fn new() -> Self {\n        Self {\n            timestamp: Cell::new(u64::MAX),\n        }\n    }\n}\n\nstruct RtcDriver {\n    /// Number of 2^23 periods elapsed since boot.\n    #[cfg(not(feature = \"_grtc\"))]\n    period: AtomicU32,\n    /// Timestamp at which to fire alarm. u64::MAX if no alarm is scheduled.\n    alarms: Mutex<AlarmState>,\n    queue: Mutex<RefCell<Queue>>,\n}\n\nembassy_time_driver::time_driver_impl!(static DRIVER: RtcDriver = RtcDriver {\n    #[cfg(not(feature = \"_grtc\"))]\n    period: AtomicU32::new(0),\n    alarms: Mutex::const_new(CriticalSectionRawMutex::new(), AlarmState::new()),\n    queue: Mutex::new(RefCell::new(Queue::new())),\n});\n\nimpl RtcDriver {\n    fn init(&'static self, irq_prio: crate::interrupt::Priority) {\n        let r = rtc();\n        // Chips without GRTC needs to deal with overflow\n        #[cfg(not(feature = \"_grtc\"))]\n        {\n            r.cc(3).write(|w| w.set_compare(0x800000));\n\n            r.intenset().write(|w| {\n                w.set_ovrflw(true);\n                w.set_compare(3, true);\n            });\n        }\n\n        #[cfg(feature = \"_grtc\")]\n        {\n            r.mode().write(|w| {\n                w.set_syscounteren(true);\n            });\n        }\n        r.tasks_clear().write_value(1);\n        r.tasks_start().write_value(1);\n\n        // Wait for clear\n        #[cfg(not(feature = \"_grtc\"))]\n        while r.counter().read().0 != 0 {}\n\n        #[cfg(feature = \"_grtc\")]\n        loop {\n            if r.status().lftimer().read().ready() {\n                break;\n            }\n        }\n\n        #[cfg(feature = \"_grtc\")]\n        {\n            interrupt::GRTC_1.set_priority(irq_prio);\n            unsafe { interrupt::GRTC_1.enable() };\n        }\n        #[cfg(not(feature = \"_grtc\"))]\n        {\n            interrupt::RTC1.set_priority(irq_prio);\n            unsafe { interrupt::RTC1.enable() };\n        }\n    }\n\n    fn on_interrupt(&self) {\n        let r = rtc();\n\n        #[cfg(not(feature = \"_grtc\"))]\n        if r.events_ovrflw().read() == 1 {\n            r.events_ovrflw().write_value(0);\n            self.next_period();\n        }\n\n        #[cfg(not(feature = \"_grtc\"))]\n        if r.events_compare(3).read() == 1 {\n            r.events_compare(3).write_value(0);\n            self.next_period();\n        }\n\n        let n = 0;\n        if r.events_compare(n).read() == 1 {\n            r.events_compare(n).write_value(0);\n            critical_section::with(|cs| {\n                self.trigger_alarm(cs);\n            });\n        }\n    }\n\n    #[cfg(not(feature = \"_grtc\"))]\n    fn next_period(&self) {\n        critical_section::with(|cs| {\n            let r = rtc();\n            let period = self.period.load(Ordering::Relaxed) + 1;\n            self.period.store(period, Ordering::Relaxed);\n            let t = (period as u64) << 23;\n\n            let n = 0;\n            let alarm = &self.alarms.borrow(cs);\n            let at = alarm.timestamp.get();\n\n            if at < t + 0xc00000 {\n                // just enable it. `set_alarm` has already set the correct CC val.\n                r.intenset().write(|w| w.0 = compare_n(n));\n            }\n        })\n    }\n\n    fn trigger_alarm(&self, cs: CriticalSection) {\n        let n = 0;\n        let r = rtc();\n        #[cfg(not(feature = \"_grtc\"))]\n        r.intenclr().write(|w| w.0 = compare_n(n));\n        #[cfg(feature = \"_grtc\")]\n        r.intenclr(1).write(|w| w.0 = compare_n(n));\n\n        let alarm = &self.alarms.borrow(cs);\n        alarm.timestamp.set(u64::MAX);\n\n        // Call after clearing alarm, so the callback can set another alarm.\n        let mut next = self.queue.borrow(cs).borrow_mut().next_expiration(self.now());\n        while !self.set_alarm(cs, next) {\n            next = self.queue.borrow(cs).borrow_mut().next_expiration(self.now());\n        }\n    }\n\n    fn set_alarm(&self, cs: CriticalSection, timestamp: u64) -> bool {\n        let n = 0;\n        let alarm = &self.alarms.borrow(cs);\n        alarm.timestamp.set(timestamp);\n\n        let r = rtc();\n\n        loop {\n            let t = self.now();\n            if timestamp <= t {\n                // If alarm timestamp has passed the alarm will not fire.\n                // Disarm the alarm and return `false` to indicate that.\n                #[cfg(not(feature = \"_grtc\"))]\n                r.intenclr().write(|w| w.0 = compare_n(n));\n                #[cfg(feature = \"_grtc\")]\n                r.intenclr(1).write(|w| w.0 = compare_n(n));\n\n                alarm.timestamp.set(u64::MAX);\n\n                return false;\n            }\n\n            // If it hasn't triggered yet, setup it in the compare channel.\n\n            // Write the CC value regardless of whether we're going to enable it now or not.\n            // This way, when we enable it later, the right value is already set.\n\n            // nrf52 docs say :\n            //    If the COUNTER is N, writing N or N+1 to a CC register may not trigger a COMPARE event.\n            // To workaround this, we never write a timestamp smaller than N+3.\n            // N+2 is not safe because rtc can tick from N to N+1 between calling now() and writing cc.\n            //\n            // Since the critical section does not guarantee that a higher prio interrupt causes\n            // this to be delayed, we need to re-check how much time actually passed after setting the\n            // alarm, and retry if we are within the unsafe interval still.\n            //\n            // This means that an alarm can be delayed for up to 2 ticks (from t+1 to t+3), but this is allowed\n            // by the Alarm trait contract. What's not allowed is triggering alarms *before* their scheduled time,\n            // and we don't do that here.\n            #[cfg(not(feature = \"_grtc\"))]\n            {\n                let safe_timestamp = timestamp.max(t + 3);\n                r.cc(n).write(|w| w.set_compare(safe_timestamp as u32 & 0xFFFFFF));\n                let diff = timestamp - t;\n                if diff < 0xc00000 {\n                    r.intenset().write(|w| w.0 = compare_n(n));\n\n                    // If we have not passed the timestamp, we can be sure the alarm will be invoked. Otherwise,\n                    // we need to retry setting the alarm.\n                    if self.now() + 2 <= timestamp {\n                        return true;\n                    }\n                } else {\n                    // If it's too far in the future, don't setup the compare channel yet.\n                    // It will be setup later by `next_period`.\n                    r.intenclr().write(|w| w.0 = compare_n(n));\n                    return true;\n                }\n            }\n\n            // The nRF54 datasheet states that 'The EVENTS_COMPARE[n] event is generated immediately if the\n            // configured compare value at CC[n] is less than the current SYSCOUNTER value.'. This means we\n            // can write the expected timestamp and be sure the alarm is triggered.\n            #[cfg(feature = \"_grtc\")]\n            {\n                let ccl = timestamp as u32;\n                let cch = (timestamp >> 32) as u32 & 0xFFFFF; // 20 bits for CCH\n\n                r.cc(n).ccl().write_value(ccl);\n                r.cc(n).cch().write(|w| w.set_cch(cch));\n                r.intenset(1).write(|w| w.0 = compare_n(n));\n\n                return true;\n            }\n        }\n    }\n}\n\nimpl Driver for RtcDriver {\n    #[cfg(not(feature = \"_grtc\"))]\n    fn now(&self) -> u64 {\n        // `period` MUST be read before `counter`, see comment at the top for details.\n        let period = self.period.load(Ordering::Relaxed);\n        compiler_fence(Ordering::Acquire);\n        let counter = rtc().counter().read().0;\n        calc_now(period, counter)\n    }\n\n    #[cfg(feature = \"_grtc\")]\n    fn now(&self) -> u64 {\n        syscounter()\n    }\n\n    fn schedule_wake(&self, at: u64, waker: &core::task::Waker) {\n        critical_section::with(|cs| {\n            let mut queue = self.queue.borrow(cs).borrow_mut();\n            if queue.schedule_wake(at, waker) {\n                let mut next = queue.next_expiration(self.now());\n                while !self.set_alarm(cs, next) {\n                    next = queue.next_expiration(self.now());\n                }\n            }\n        })\n    }\n}\n\n#[cfg(feature = \"_grtc\")]\n#[cfg(feature = \"rt\")]\n#[interrupt]\nfn GRTC_1() {\n    DRIVER.on_interrupt()\n}\n\n#[cfg(not(feature = \"_grtc\"))]\n#[cfg(feature = \"rt\")]\n#[interrupt]\nfn RTC1() {\n    DRIVER.on_interrupt()\n}\n\npub(crate) fn init(irq_prio: crate::interrupt::Priority) {\n    DRIVER.init(irq_prio)\n}\n"
  },
  {
    "path": "embassy-nrf/src/timer.rs",
    "content": "//! Timer driver.\n//!\n//! Important note! This driver is very low level. For most time-related use cases, like\n//! \"sleep for X seconds\", \"do something every X seconds\", or measuring time, you should\n//! use [`embassy-time`](https://crates.io/crates/embassy-time) instead!\n\n#![macro_use]\n\nuse core::marker::PhantomData;\n\nuse embassy_hal_internal::{Peri, PeripheralType};\n\nuse crate::pac;\nuse crate::pac::timer::vals;\nuse crate::ppi::{Event, Task};\n\npub(crate) trait SealedInstance {\n    /// The number of CC registers this instance has.\n    const CCS: usize;\n    fn regs() -> pac::timer::Timer;\n}\n\n/// Basic Timer instance.\n#[allow(private_bounds)]\npub trait Instance: SealedInstance + PeripheralType + 'static + Send {\n    /// Interrupt for this peripheral.\n    type Interrupt: crate::interrupt::typelevel::Interrupt;\n}\n\n/// Extended timer instance.\npub trait ExtendedInstance: Instance {}\n\nmacro_rules! impl_timer {\n    ($type:ident, $pac_type:ident, $irq:ident, $ccs:literal) => {\n        impl crate::timer::SealedInstance for peripherals::$type {\n            const CCS: usize = $ccs;\n            fn regs() -> pac::timer::Timer {\n                unsafe { pac::timer::Timer::from_ptr(pac::$pac_type.as_ptr()) }\n            }\n        }\n        impl crate::timer::Instance for peripherals::$type {\n            type Interrupt = crate::interrupt::typelevel::$irq;\n        }\n    };\n    ($type:ident, $pac_type:ident, $irq:ident) => {\n        impl_timer!($type, $pac_type, $irq, 4);\n    };\n    ($type:ident, $pac_type:ident, $irq:ident, extended) => {\n        impl_timer!($type, $pac_type, $irq, 6);\n        impl crate::timer::ExtendedInstance for peripherals::$type {}\n    };\n}\n\n/// Timer frequency\n#[repr(u8)]\npub enum Frequency {\n    /// 16MHz\n    F16MHz = 0,\n    /// 8MHz\n    F8MHz = 1,\n    /// 4MHz\n    F4MHz = 2,\n    /// 2MHz\n    F2MHz = 3,\n    /// 1MHz\n    F1MHz = 4,\n    /// 500kHz\n    F500kHz = 5,\n    /// 250kHz\n    F250kHz = 6,\n    /// 125kHz\n    F125kHz = 7,\n    /// 62500Hz\n    F62500Hz = 8,\n    /// 31250Hz\n    F31250Hz = 9,\n}\n\n/// nRF Timer driver.\n///\n/// The timer has an internal counter, which is incremented for every tick of the timer.\n/// The counter is 32-bit, so it wraps back to 0 when it reaches 2^32.\n///\n/// It has either 4 or 6 Capture/Compare registers, which can be used to capture the current state of the counter\n/// or trigger an event when the counter reaches a certain value.\npub struct Timer<'d> {\n    r: pac::timer::Timer,\n    ccs: usize,\n    _p: PhantomData<&'d ()>,\n}\n\nimpl<'d> Timer<'d> {\n    /// Create a new `Timer` driver.\n    ///\n    /// This can be useful for triggering tasks via PPI.\n    /// `Uarte` uses this internally.\n    pub fn new<T: Instance>(timer: Peri<'d, T>) -> Self {\n        Self::new_inner(timer, false)\n    }\n\n    /// Create a new `Timer` driver in counter mode.\n    ///\n    /// This can be useful for triggering tasks via PPI.\n    /// `Uarte` uses this internally.\n    pub fn new_counter<T: Instance>(timer: Peri<'d, T>) -> Self {\n        Self::new_inner(timer, true)\n    }\n\n    fn new_inner<T: Instance>(_timer: Peri<'d, T>, is_counter: bool) -> Self {\n        let regs = T::regs();\n\n        let this = Self {\n            r: regs,\n            ccs: T::CCS,\n            _p: PhantomData,\n        };\n\n        // Stop the timer before doing anything else,\n        // since changing BITMODE while running can cause 'unpredictable behaviour' according to the specification.\n        this.stop();\n\n        regs.mode().write(|w| {\n            w.set_mode(match is_counter {\n                #[cfg(not(feature = \"_nrf51\"))]\n                true => vals::Mode::LOW_POWER_COUNTER,\n                #[cfg(feature = \"_nrf51\")]\n                true => vals::Mode::COUNTER,\n                false => vals::Mode::TIMER,\n            })\n        });\n\n        // Make the counter's max value as high as possible.\n        // TODO: is there a reason someone would want to set this lower?\n        regs.bitmode().write(|w| w.set_bitmode(vals::Bitmode::_32BIT));\n\n        // Initialize the counter at 0.\n        this.clear();\n\n        // Default to the max frequency of the lower power clock\n        this.set_frequency(Frequency::F1MHz);\n\n        for n in 0..this.ccs {\n            let cc = this.cc(n);\n            // Initialize all the shorts as disabled.\n            cc.unshort_compare_clear();\n            cc.unshort_compare_stop();\n            // Initialize the CC registers as 0.\n            cc.write(0);\n        }\n\n        this\n    }\n\n    /// Direct access to the register block.\n    #[cfg(feature = \"unstable-pac\")]\n    #[inline]\n    pub fn regs(&mut self) -> pac::timer::Timer {\n        self.r\n    }\n\n    /// Starts the timer.\n    pub fn start(&self) {\n        self.r.tasks_start().write_value(1)\n    }\n\n    /// Stops the timer.\n    pub fn stop(&self) {\n        self.r.tasks_stop().write_value(1)\n    }\n\n    /// Reset the timer's counter to 0.\n    pub fn clear(&self) {\n        self.r.tasks_clear().write_value(1)\n    }\n\n    /// Returns the START task, for use with PPI.\n    ///\n    /// When triggered, this task starts the timer.\n    pub fn task_start(&self) -> Task<'d> {\n        Task::from_reg(self.r.tasks_start())\n    }\n\n    /// Returns the STOP task, for use with PPI.\n    ///\n    /// When triggered, this task stops the timer.\n    pub fn task_stop(&self) -> Task<'d> {\n        Task::from_reg(self.r.tasks_stop())\n    }\n\n    /// Returns the CLEAR task, for use with PPI.\n    ///\n    /// When triggered, this task resets the timer's counter to 0.\n    pub fn task_clear(&self) -> Task<'d> {\n        Task::from_reg(self.r.tasks_clear())\n    }\n\n    /// Returns the COUNT task, for use with PPI.\n    ///\n    /// When triggered, this task increments the timer's counter by 1.\n    /// Only works in counter mode.\n    pub fn task_count(&self) -> Task<'d> {\n        Task::from_reg(self.r.tasks_count())\n    }\n\n    /// Change the timer's frequency.\n    ///\n    /// This will stop the timer if it isn't already stopped,\n    /// because the timer may exhibit 'unpredictable behaviour' if it's frequency is changed while it's running.\n    pub fn set_frequency(&self, frequency: Frequency) {\n        self.stop();\n\n        self.r\n            .prescaler()\n            // SAFETY: `frequency` is a variant of `Frequency`,\n            // whose values are all in the range of 0-9 (the valid range of `prescaler`).\n            .write(|w| w.set_prescaler(frequency as u8))\n    }\n\n    /// Returns this timer's `n`th CC register.\n    ///\n    /// # Panics\n    /// Panics if `n` >= the number of CC registers this timer has (4 for a normal timer, 6 for an extended timer).\n    pub fn cc(&self, n: usize) -> Cc<'d> {\n        if n >= self.ccs {\n            panic!(\"Cannot get CC register {} of timer with {} CC registers.\", n, self.ccs);\n        }\n        Cc {\n            n,\n            r: self.r,\n            _p: PhantomData,\n        }\n    }\n}\n\nimpl Timer<'static> {\n    /// Persist the timer's configuration for the rest of the program's lifetime. This method\n    /// should be preferred over [`core::mem::forget()`] because the `'static` bound prevents\n    /// accidental reuse of the underlying peripheral.\n    pub fn persist(self) {\n        core::mem::forget(self);\n    }\n}\n\nimpl<'d> Drop for Timer<'d> {\n    fn drop(&mut self) {\n        self.stop();\n    }\n}\n\n/// A representation of a timer's Capture/Compare (CC) register.\n///\n/// A CC register holds a 32-bit value.\n/// This is used either to store a capture of the timer's current count, or to specify the value for the timer to compare against.\n///\n/// The timer will fire the register's COMPARE event when its counter reaches the value stored in the register.\n/// When the register's CAPTURE task is triggered, the timer will store the current value of its counter in the register\npub struct Cc<'d> {\n    n: usize,\n    r: pac::timer::Timer,\n    _p: PhantomData<&'d ()>,\n}\n\nimpl<'d> Cc<'d> {\n    /// Get the current value stored in the register.\n    pub fn read(&self) -> u32 {\n        self.r.cc(self.n).read()\n    }\n\n    /// Set the value stored in the register.\n    ///\n    /// `event_compare` will fire when the timer's counter reaches this value.\n    pub fn write(&self, value: u32) {\n        self.r.cc(self.n).write_value(value);\n    }\n\n    /// Capture the current value of the timer's counter in this register, and return it.\n    pub fn capture(&self) -> u32 {\n        self.r.tasks_capture(self.n).write_value(1);\n        self.read()\n    }\n\n    /// Returns this CC register's CAPTURE task, for use with PPI.\n    ///\n    /// When triggered, this task will capture the current value of the timer's counter in this register.\n    pub fn task_capture(&self) -> Task<'d> {\n        Task::from_reg(self.r.tasks_capture(self.n))\n    }\n\n    /// Returns this CC register's COMPARE event, for use with PPI.\n    ///\n    /// This event will fire when the timer's counter reaches the value in this CC register.\n    pub fn event_compare(&self) -> Event<'d> {\n        Event::from_reg(self.r.events_compare(self.n))\n    }\n\n    /// Clear the COMPARE event for this CC register.\n    #[inline]\n    pub fn clear_events(&self) {\n        self.r.events_compare(self.n).write_value(0);\n    }\n\n    /// Enable the shortcut between this CC register's COMPARE event and the timer's CLEAR task.\n    ///\n    /// This means that when the COMPARE event is fired, the CLEAR task will be triggered.\n    ///\n    /// So, when the timer's counter reaches the value stored in this register, the timer's counter will be reset to 0.\n    pub fn short_compare_clear(&self) {\n        self.r.shorts().modify(|w| w.set_compare_clear(self.n, true))\n    }\n\n    /// Disable the shortcut between this CC register's COMPARE event and the timer's CLEAR task.\n    pub fn unshort_compare_clear(&self) {\n        self.r.shorts().modify(|w| w.set_compare_clear(self.n, false))\n    }\n\n    /// Enable the shortcut between this CC register's COMPARE event and the timer's STOP task.\n    ///\n    /// This means that when the COMPARE event is fired, the STOP task will be triggered.\n    ///\n    /// So, when the timer's counter reaches the value stored in this register, the timer will stop counting up.\n    pub fn short_compare_stop(&self) {\n        self.r.shorts().modify(|w| w.set_compare_stop(self.n, true))\n    }\n\n    /// Disable the shortcut between this CC register's COMPARE event and the timer's STOP task.\n    pub fn unshort_compare_stop(&self) {\n        self.r.shorts().modify(|w| w.set_compare_stop(self.n, false))\n    }\n}\n"
  },
  {
    "path": "embassy-nrf/src/twim.rs",
    "content": "//! I2C-compatible Two Wire Interface in master mode (TWIM) driver.\n\n#![macro_use]\n\nuse core::future::poll_fn;\nuse core::marker::PhantomData;\nuse core::sync::atomic::Ordering::SeqCst;\nuse core::sync::atomic::compiler_fence;\nuse core::task::Poll;\n\nuse embassy_embedded_hal::SetConfig;\nuse embassy_hal_internal::{Peri, PeripheralType};\nuse embassy_sync::waitqueue::AtomicWaker;\n#[cfg(feature = \"time\")]\nuse embassy_time::{Duration, Instant};\nuse embedded_hal_1::i2c::Operation;\npub use pac::twim::vals::Frequency;\n\nuse crate::chip::EASY_DMA_SIZE;\nuse crate::gpio::Pin as GpioPin;\nuse crate::interrupt::typelevel::Interrupt;\nuse crate::pac::gpio::vals as gpiovals;\nuse crate::pac::twim::vals;\nuse crate::util::slice_in_ram;\nuse crate::{gpio, interrupt, pac};\n\n/// TWIM config.\n#[non_exhaustive]\npub struct Config {\n    /// Frequency\n    pub frequency: Frequency,\n\n    /// Enable high drive for the SDA line.\n    pub sda_high_drive: bool,\n\n    /// Enable internal pullup for the SDA line.\n    ///\n    /// Note that using external pullups is recommended for I2C, and\n    /// most boards already have them.\n    pub sda_pullup: bool,\n\n    /// Enable high drive for the SCL line.\n    pub scl_high_drive: bool,\n\n    /// Enable internal pullup for the SCL line.\n    ///\n    /// Note that using external pullups is recommended for I2C, and\n    /// most boards already have them.\n    pub scl_pullup: bool,\n}\n\nimpl Default for Config {\n    fn default() -> Self {\n        Self {\n            frequency: Frequency::K100,\n            scl_high_drive: false,\n            sda_pullup: false,\n            sda_high_drive: false,\n            scl_pullup: false,\n        }\n    }\n}\n\n/// TWI error.\n#[derive(Debug, Copy, Clone, Eq, PartialEq)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\n#[non_exhaustive]\npub enum Error {\n    /// TX buffer was too long.\n    TxBufferTooLong,\n    /// RX buffer was too long.\n    RxBufferTooLong,\n    /// Data transmit failed.\n    Transmit,\n    /// Data reception failed.\n    Receive,\n    /// The buffer is not in data RAM and is larger than the RAM buffer. It's most likely in flash, and nRF's DMA cannot access flash.\n    RAMBufferTooSmall,\n    /// Didn't receive an ACK bit after the address byte. Address might be wrong, or the i2c device chip might not be connected properly.\n    AddressNack,\n    /// Didn't receive an ACK bit after a data byte.\n    DataNack,\n    /// Overrun error.\n    Overrun,\n    /// Timeout error.\n    Timeout,\n}\n\n/// Interrupt handler.\npub struct InterruptHandler<T: Instance> {\n    _phantom: PhantomData<T>,\n}\n\nimpl<T: Instance> interrupt::typelevel::Handler<T::Interrupt> for InterruptHandler<T> {\n    unsafe fn on_interrupt() {\n        let r = T::regs();\n        let s = T::state();\n\n        if r.events_suspended().read() != 0 {\n            s.end_waker.wake();\n            r.intenclr().write(|w| w.set_suspended(true));\n        }\n        if r.events_stopped().read() != 0 {\n            s.end_waker.wake();\n            r.intenclr().write(|w| w.set_stopped(true));\n        }\n        if r.events_error().read() != 0 {\n            s.end_waker.wake();\n            r.intenclr().write(|w| w.set_error(true));\n        }\n    }\n}\n\n/// TWI driver.\npub struct Twim<'d> {\n    r: pac::twim::Twim,\n    state: &'static State,\n    tx_ram_buffer: &'d mut [u8],\n    _p: PhantomData<&'d ()>,\n}\n\nimpl<'d> Twim<'d> {\n    /// Create a new TWI driver.\n    ///\n    /// `tx_ram_buffer` is required if any write operations will be performed with data that is not in RAM.\n    /// Usually this is static data that the compiler locates in flash instead of RAM. The `tx_ram_buffer`\n    /// needs to be at least as large as the largest write operation that will be executed with a buffer\n    /// that is not in RAM. If all write operations will be performed from RAM, an empty buffer (`&[]`) may\n    /// be used.\n    pub fn new<T: Instance>(\n        _twim: Peri<'d, T>,\n        _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd,\n        sda: Peri<'d, impl GpioPin>,\n        scl: Peri<'d, impl GpioPin>,\n        config: Config,\n        tx_ram_buffer: &'d mut [u8],\n    ) -> Self {\n        let r = T::regs();\n\n        // Configure pins\n        sda.set_high();\n        scl.set_high();\n        sda.conf().write(|w| {\n            w.set_dir(gpiovals::Dir::OUTPUT);\n            w.set_input(gpiovals::Input::CONNECT);\n            #[cfg(not(feature = \"_nrf54l\"))]\n            w.set_drive(match config.sda_high_drive {\n                true => gpiovals::Drive::H0D1,\n                false => gpiovals::Drive::S0D1,\n            });\n            #[cfg(feature = \"_nrf54l\")]\n            {\n                w.set_drive0(match config.sda_high_drive {\n                    true => gpiovals::Drive::H,\n                    false => gpiovals::Drive::S,\n                });\n                w.set_drive1(gpiovals::Drive::D);\n            }\n            if config.sda_pullup {\n                w.set_pull(gpiovals::Pull::PULLUP);\n            }\n        });\n        scl.conf().write(|w| {\n            w.set_dir(gpiovals::Dir::OUTPUT);\n            w.set_input(gpiovals::Input::CONNECT);\n            #[cfg(not(feature = \"_nrf54l\"))]\n            w.set_drive(match config.scl_high_drive {\n                true => gpiovals::Drive::H0D1,\n                false => gpiovals::Drive::S0D1,\n            });\n            #[cfg(feature = \"_nrf54l\")]\n            {\n                w.set_drive0(match config.scl_high_drive {\n                    true => gpiovals::Drive::H,\n                    false => gpiovals::Drive::S,\n                });\n                w.set_drive1(gpiovals::Drive::D);\n            }\n            if config.scl_pullup {\n                w.set_pull(gpiovals::Pull::PULLUP);\n            }\n        });\n\n        // Select pins.\n        r.psel().sda().write_value(sda.psel_bits());\n        r.psel().scl().write_value(scl.psel_bits());\n\n        // Enable TWIM instance.\n        r.enable().write(|w| w.set_enable(vals::Enable::ENABLED));\n\n        let mut twim = Self {\n            r: T::regs(),\n            state: T::state(),\n            tx_ram_buffer,\n            _p: PhantomData {},\n        };\n\n        // Apply runtime peripheral configuration\n        Self::set_config(&mut twim, &config).unwrap();\n\n        // Disable all events interrupts\n        r.intenclr().write(|w| w.0 = 0xFFFF_FFFF);\n\n        T::Interrupt::unpend();\n        unsafe { T::Interrupt::enable() };\n\n        twim\n    }\n\n    /// Set TX buffer, checking that it is in RAM and has suitable length.\n    unsafe fn set_tx_buffer(&mut self, buffer: &[u8]) -> Result<(), Error> {\n        let buffer = if slice_in_ram(buffer) {\n            buffer\n        } else {\n            if buffer.len() > self.tx_ram_buffer.len() {\n                return Err(Error::RAMBufferTooSmall);\n            }\n            trace!(\"Copying TWIM tx buffer into RAM for DMA\");\n            let ram_buffer = &mut self.tx_ram_buffer[..buffer.len()];\n            ram_buffer.copy_from_slice(buffer);\n            &*ram_buffer\n        };\n\n        if buffer.len() > EASY_DMA_SIZE {\n            return Err(Error::TxBufferTooLong);\n        }\n\n        let r = self.r;\n\n        // We're giving the register a pointer to the stack. Since we're\n        // waiting for the I2C transaction to end before this stack pointer\n        // becomes invalid, there's nothing wrong here.\n        r.dma().tx().ptr().write_value(buffer.as_ptr() as u32);\n        r.dma().tx().maxcnt().write(|w|\n            // We're giving it the length of the buffer, so no danger of\n            // accessing invalid memory. We have verified that the length of the\n            // buffer fits in an `u8`, so the cast to `u8` is also fine.\n            //\n            // The MAXCNT field is 8 bits wide and accepts the full range of\n            // values.\n            w.set_maxcnt(buffer.len() as _));\n\n        Ok(())\n    }\n\n    /// Set RX buffer, checking that it has suitable length.\n    unsafe fn set_rx_buffer(&mut self, buffer: &mut [u8]) -> Result<(), Error> {\n        // NOTE: RAM slice check is not necessary, as a mutable\n        // slice can only be built from data located in RAM.\n\n        if buffer.len() > EASY_DMA_SIZE {\n            return Err(Error::RxBufferTooLong);\n        }\n\n        let r = self.r;\n\n        // We're giving the register a pointer to the stack. Since we're\n        // waiting for the I2C transaction to end before this stack pointer\n        // becomes invalid, there's nothing wrong here.\n        r.dma().rx().ptr().write_value(buffer.as_mut_ptr() as u32);\n        r.dma().rx().maxcnt().write(|w|\n            // We're giving it the length of the buffer, so no danger of\n            // accessing invalid memory. We have verified that the length of the\n            // buffer fits in an `u8`, so the cast to the type of maxcnt\n            // is also fine.\n            //\n            // Note that that nrf52840 maxcnt is a wider\n            // type than a u8, so we use a `_` cast rather than a `u8` cast.\n            // The MAXCNT field is thus at least 8 bits wide and accepts the\n            // full range of values that fit in a `u8`.\n            w.set_maxcnt(buffer.len() as _));\n\n        Ok(())\n    }\n\n    fn clear_errorsrc(&mut self) {\n        let r = self.r;\n        r.errorsrc().write(|w| {\n            w.set_anack(true);\n            w.set_dnack(true);\n            w.set_overrun(true);\n        });\n    }\n\n    /// Get Error instance, if any occurred.\n    fn check_errorsrc(&mut self) -> Result<(), Error> {\n        let r = self.r;\n\n        let err = r.errorsrc().read();\n        if err.anack() {\n            return Err(Error::AddressNack);\n        }\n        if err.dnack() {\n            return Err(Error::DataNack);\n        }\n        if err.overrun() {\n            return Err(Error::Overrun);\n        }\n        Ok(())\n    }\n\n    fn check_rx(&self, len: usize) -> Result<(), Error> {\n        let r = self.r;\n        if r.dma().rx().amount().read().0 != len as u32 {\n            Err(Error::Receive)\n        } else {\n            Ok(())\n        }\n    }\n\n    fn check_tx(&self, len: usize) -> Result<(), Error> {\n        let r = self.r;\n        if r.dma().tx().amount().read().0 != len as u32 {\n            Err(Error::Transmit)\n        } else {\n            Ok(())\n        }\n    }\n\n    /// Wait for stop or error\n    fn blocking_wait(&mut self) {\n        let r = self.r;\n        loop {\n            if r.events_suspended().read() != 0 || r.events_stopped().read() != 0 {\n                r.events_suspended().write_value(0);\n                r.events_stopped().write_value(0);\n                break;\n            }\n            if r.events_error().read() != 0 {\n                r.events_error().write_value(0);\n                r.tasks_stop().write_value(1);\n            }\n        }\n    }\n\n    /// Wait for stop or error\n    #[cfg(feature = \"time\")]\n    fn blocking_wait_timeout(&mut self, timeout: Duration) -> Result<(), Error> {\n        let r = self.r;\n        let deadline = Instant::now() + timeout;\n        loop {\n            if r.events_suspended().read() != 0 || r.events_stopped().read() != 0 {\n                r.events_stopped().write_value(0);\n                break;\n            }\n            if r.events_error().read() != 0 {\n                r.events_error().write_value(0);\n                r.tasks_stop().write_value(1);\n            }\n            if Instant::now() > deadline {\n                r.tasks_stop().write_value(1);\n                return Err(Error::Timeout);\n            }\n        }\n\n        Ok(())\n    }\n\n    /// Wait for stop or error\n    async fn async_wait(&mut self) -> Result<(), Error> {\n        poll_fn(|cx| {\n            let r = self.r;\n            let s = self.state;\n\n            s.end_waker.register(cx.waker());\n            if r.events_suspended().read() != 0 || r.events_stopped().read() != 0 {\n                r.events_stopped().write_value(0);\n\n                return Poll::Ready(Ok(()));\n            }\n\n            // stop if an error occurred\n            if r.events_error().read() != 0 {\n                r.events_error().write_value(0);\n                r.tasks_stop().write_value(1);\n                if let Err(e) = self.check_errorsrc() {\n                    return Poll::Ready(Err(e));\n                } else {\n                    return Poll::Ready(Err(Error::Timeout));\n                }\n            }\n\n            Poll::Pending\n        })\n        .await\n    }\n\n    fn setup_operations(\n        &mut self,\n        address: u8,\n        operations: &mut [Operation<'_>],\n        last_op: Option<&Operation<'_>>,\n        inten: bool,\n    ) -> Result<usize, Error> {\n        let r = self.r;\n\n        compiler_fence(SeqCst);\n\n        r.address().write(|w| w.set_address(address));\n\n        r.events_suspended().write_value(0);\n        r.events_stopped().write_value(0);\n        r.events_error().write_value(0);\n        self.clear_errorsrc();\n\n        if inten {\n            r.intenset().write(|w| {\n                w.set_suspended(true);\n                w.set_stopped(true);\n                w.set_error(true);\n            });\n        } else {\n            r.intenclr().write(|w| {\n                w.set_suspended(true);\n                w.set_stopped(true);\n                w.set_error(true);\n            });\n        }\n\n        assert!(!operations.is_empty());\n        match operations {\n            [Operation::Read(_), Operation::Read(_), ..] => {\n                panic!(\"Consecutive read operations are not supported!\")\n            }\n            [Operation::Read(rd_buffer), Operation::Write(wr_buffer), rest @ ..] => {\n                let stop = rest.is_empty();\n\n                // Set up DMA buffers.\n                unsafe {\n                    self.set_tx_buffer(wr_buffer)?;\n                    self.set_rx_buffer(rd_buffer)?;\n                }\n\n                r.shorts().write(|w| {\n                    w.set_lastrx_dma_tx_start(true);\n                    if stop {\n                        w.set_lasttx_stop(true);\n                    } else {\n                        w.set_lasttx_suspend(true);\n                    }\n                });\n\n                // Start read+write operation.\n                r.tasks_dma().rx().start().write_value(1);\n                if last_op.is_some() {\n                    r.tasks_resume().write_value(1);\n                }\n\n                // TODO: Handle empty write buffer\n                if rd_buffer.is_empty() {\n                    // With a zero-length buffer, LASTRX doesn't fire (because there's no last byte!), so do the STARTTX ourselves.\n                    r.tasks_dma().tx().start().write_value(1);\n                }\n\n                Ok(2)\n            }\n            [Operation::Read(buffer)] => {\n                // Set up DMA buffers.\n                unsafe {\n                    self.set_rx_buffer(buffer)?;\n                }\n\n                r.shorts().write(|w| w.set_lastrx_stop(true));\n\n                // Start read operation.\n                r.tasks_dma().rx().start().write_value(1);\n                if last_op.is_some() {\n                    r.tasks_resume().write_value(1);\n                }\n\n                if buffer.is_empty() {\n                    // With a zero-length buffer, LASTRX doesn't fire (because there's no last byte!), so do the STOP ourselves.\n                    r.tasks_stop().write_value(1);\n                }\n\n                Ok(1)\n            }\n            [Operation::Write(wr_buffer), Operation::Read(rd_buffer)]\n                if !wr_buffer.is_empty() && !rd_buffer.is_empty() =>\n            {\n                // Set up DMA buffers.\n                unsafe {\n                    self.set_tx_buffer(wr_buffer)?;\n                    self.set_rx_buffer(rd_buffer)?;\n                }\n\n                // Start write+read operation.\n                r.shorts().write(|w| {\n                    w.set_lasttx_dma_rx_start(true);\n                    w.set_lastrx_stop(true);\n                });\n\n                r.tasks_dma().tx().start().write_value(1);\n                if last_op.is_some() {\n                    r.tasks_resume().write_value(1);\n                }\n\n                Ok(2)\n            }\n            [Operation::Write(buffer), rest @ ..] => {\n                let stop = rest.is_empty();\n\n                // Set up DMA buffers.\n                unsafe {\n                    self.set_tx_buffer(buffer)?;\n                }\n\n                // Start write operation.\n                r.shorts().write(|w| {\n                    if stop {\n                        w.set_lasttx_stop(true);\n                    } else {\n                        w.set_lasttx_suspend(true);\n                    }\n                });\n\n                r.tasks_dma().tx().start().write_value(1);\n                if last_op.is_some() {\n                    r.tasks_resume().write_value(1);\n                }\n\n                if buffer.is_empty() {\n                    // With a zero-length buffer, LASTTX doesn't fire (because there's no last byte!), so do the STOP/SUSPEND ourselves.\n                    if stop {\n                        r.tasks_stop().write_value(1);\n                    } else {\n                        r.tasks_suspend().write_value(1);\n                    }\n                }\n\n                Ok(1)\n            }\n            [] => unreachable!(),\n        }\n    }\n\n    fn check_operations(&mut self, operations: &[Operation<'_>]) -> Result<(), Error> {\n        compiler_fence(SeqCst);\n        self.check_errorsrc()?;\n\n        assert!(operations.len() == 1 || operations.len() == 2);\n        match operations {\n            [Operation::Read(rd_buffer), Operation::Write(wr_buffer)]\n            | [Operation::Write(wr_buffer), Operation::Read(rd_buffer)] => {\n                self.check_rx(rd_buffer.len())?;\n                self.check_tx(wr_buffer.len())?;\n            }\n            [Operation::Read(buffer)] => {\n                self.check_rx(buffer.len())?;\n            }\n            [Operation::Write(buffer), ..] => {\n                self.check_tx(buffer.len())?;\n            }\n            _ => unreachable!(),\n        }\n        Ok(())\n    }\n\n    // ===========================================\n\n    /// Execute the provided operations on the I2C bus.\n    ///\n    /// Each buffer must have a length of at most 255 bytes on the nRF52832\n    /// and at most 65535 bytes on the nRF52840.\n    ///\n    /// Consecutive `Operation::Read`s are not supported due to hardware\n    /// limitations.\n    ///\n    /// An `Operation::Write` following an `Operation::Read` must have a\n    /// non-empty buffer.\n    pub fn blocking_transaction(&mut self, address: u8, mut operations: &mut [Operation<'_>]) -> Result<(), Error> {\n        let mut last_op = None;\n        while !operations.is_empty() {\n            let ops = self.setup_operations(address, operations, last_op, false)?;\n            let (in_progress, rest) = operations.split_at_mut(ops);\n            self.blocking_wait();\n            self.check_operations(in_progress)?;\n            last_op = in_progress.last();\n            operations = rest;\n        }\n        Ok(())\n    }\n\n    /// Execute the provided operations on the I2C bus with timeout.\n    ///\n    /// See [Self::blocking_transaction].\n    #[cfg(feature = \"time\")]\n    pub fn blocking_transaction_timeout(\n        &mut self,\n        address: u8,\n        mut operations: &mut [Operation<'_>],\n        timeout: Duration,\n    ) -> Result<(), Error> {\n        let mut last_op = None;\n        while !operations.is_empty() {\n            let ops = self.setup_operations(address, operations, last_op, false)?;\n            let (in_progress, rest) = operations.split_at_mut(ops);\n            self.blocking_wait_timeout(timeout)?;\n            self.check_operations(in_progress)?;\n            last_op = in_progress.last();\n            operations = rest;\n        }\n        Ok(())\n    }\n\n    /// Execute the provided operations on the I2C bus.\n    ///\n    /// Each buffer must have a length of at most 255 bytes on the nRF52832\n    /// and at most 65535 bytes on the nRF52840.\n    ///\n    /// Consecutive `Operation::Read`s are not supported due to hardware\n    /// limitations.\n    ///\n    /// An `Operation::Write` following an `Operation::Read` must have a\n    /// non-empty buffer.\n    pub async fn transaction(&mut self, address: u8, mut operations: &mut [Operation<'_>]) -> Result<(), Error> {\n        let mut last_op = None;\n        while !operations.is_empty() {\n            let ops = self.setup_operations(address, operations, last_op, true)?;\n            let (in_progress, rest) = operations.split_at_mut(ops);\n            self.async_wait().await?;\n            self.check_operations(in_progress)?;\n            last_op = in_progress.last();\n            operations = rest;\n        }\n        Ok(())\n    }\n\n    // ===========================================\n\n    /// Write to an I2C slave.\n    ///\n    /// The buffer must have a length of at most 255 bytes on the nRF52832\n    /// and at most 65535 bytes on the nRF52840.\n    pub fn blocking_write(&mut self, address: u8, buffer: &[u8]) -> Result<(), Error> {\n        self.blocking_transaction(address, &mut [Operation::Write(buffer)])\n    }\n\n    /// Read from an I2C slave.\n    ///\n    /// The buffer must have a length of at most 255 bytes on the nRF52832\n    /// and at most 65535 bytes on the nRF52840.\n    pub fn blocking_read(&mut self, address: u8, buffer: &mut [u8]) -> Result<(), Error> {\n        self.blocking_transaction(address, &mut [Operation::Read(buffer)])\n    }\n\n    /// Write data to an I2C slave, then read data from the slave without\n    /// triggering a stop condition between the two.\n    ///\n    /// The buffers must have a length of at most 255 bytes on the nRF52832\n    /// and at most 65535 bytes on the nRF52840.\n    pub fn blocking_write_read(&mut self, address: u8, wr_buffer: &[u8], rd_buffer: &mut [u8]) -> Result<(), Error> {\n        self.blocking_transaction(address, &mut [Operation::Write(wr_buffer), Operation::Read(rd_buffer)])\n    }\n\n    // ===========================================\n\n    /// Write to an I2C slave with timeout.\n    ///\n    /// See [Self::blocking_write].\n    #[cfg(feature = \"time\")]\n    pub fn blocking_write_timeout(&mut self, address: u8, buffer: &[u8], timeout: Duration) -> Result<(), Error> {\n        self.blocking_transaction_timeout(address, &mut [Operation::Write(buffer)], timeout)\n    }\n\n    /// Read from an I2C slave.\n    ///\n    /// The buffer must have a length of at most 255 bytes on the nRF52832\n    /// and at most 65535 bytes on the nRF52840.\n    #[cfg(feature = \"time\")]\n    pub fn blocking_read_timeout(&mut self, address: u8, buffer: &mut [u8], timeout: Duration) -> Result<(), Error> {\n        self.blocking_transaction_timeout(address, &mut [Operation::Read(buffer)], timeout)\n    }\n\n    /// Write data to an I2C slave, then read data from the slave without\n    /// triggering a stop condition between the two.\n    ///\n    /// The buffers must have a length of at most 255 bytes on the nRF52832\n    /// and at most 65535 bytes on the nRF52840.\n    #[cfg(feature = \"time\")]\n    pub fn blocking_write_read_timeout(\n        &mut self,\n        address: u8,\n        wr_buffer: &[u8],\n        rd_buffer: &mut [u8],\n        timeout: Duration,\n    ) -> Result<(), Error> {\n        self.blocking_transaction_timeout(\n            address,\n            &mut [Operation::Write(wr_buffer), Operation::Read(rd_buffer)],\n            timeout,\n        )\n    }\n\n    // ===========================================\n\n    /// Read from an I2C slave.\n    ///\n    /// The buffer must have a length of at most 255 bytes on the nRF52832\n    /// and at most 65535 bytes on the nRF52840.\n    pub async fn read(&mut self, address: u8, buffer: &mut [u8]) -> Result<(), Error> {\n        self.transaction(address, &mut [Operation::Read(buffer)]).await\n    }\n\n    /// Write to an I2C slave.\n    ///\n    /// The buffer must have a length of at most 255 bytes on the nRF52832\n    /// and at most 65535 bytes on the nRF52840.\n    pub async fn write(&mut self, address: u8, buffer: &[u8]) -> Result<(), Error> {\n        self.transaction(address, &mut [Operation::Write(buffer)]).await\n    }\n\n    /// Write data to an I2C slave, then read data from the slave without\n    /// triggering a stop condition between the two.\n    ///\n    /// The buffers must have a length of at most 255 bytes on the nRF52832\n    /// and at most 65535 bytes on the nRF52840.\n    pub async fn write_read(&mut self, address: u8, wr_buffer: &[u8], rd_buffer: &mut [u8]) -> Result<(), Error> {\n        self.transaction(address, &mut [Operation::Write(wr_buffer), Operation::Read(rd_buffer)])\n            .await\n    }\n}\n\nimpl<'a> Drop for Twim<'a> {\n    fn drop(&mut self) {\n        trace!(\"twim drop\");\n\n        // TODO: check for abort\n\n        // disable!\n        let r = self.r;\n        r.enable().write(|w| w.set_enable(vals::Enable::DISABLED));\n\n        gpio::deconfigure_pin(r.psel().sda().read());\n        gpio::deconfigure_pin(r.psel().scl().read());\n\n        trace!(\"twim drop: done\");\n    }\n}\n\npub(crate) struct State {\n    end_waker: AtomicWaker,\n}\n\nimpl State {\n    pub(crate) const fn new() -> Self {\n        Self {\n            end_waker: AtomicWaker::new(),\n        }\n    }\n}\n\npub(crate) trait SealedInstance {\n    fn regs() -> pac::twim::Twim;\n    fn state() -> &'static State;\n}\n\n/// TWIM peripheral instance.\n#[allow(private_bounds)]\npub trait Instance: SealedInstance + PeripheralType + 'static {\n    /// Interrupt for this peripheral.\n    type Interrupt: interrupt::typelevel::Interrupt;\n}\n\nmacro_rules! impl_twim {\n    ($type:ident, $pac_type:ident, $irq:ident) => {\n        impl crate::twim::SealedInstance for peripherals::$type {\n            fn regs() -> pac::twim::Twim {\n                pac::$pac_type\n            }\n            fn state() -> &'static crate::twim::State {\n                static STATE: crate::twim::State = crate::twim::State::new();\n                &STATE\n            }\n        }\n        impl crate::twim::Instance for peripherals::$type {\n            type Interrupt = crate::interrupt::typelevel::$irq;\n        }\n    };\n}\n\n// ====================\n\nmod eh02 {\n    use super::*;\n\n    impl<'a> embedded_hal_02::blocking::i2c::Write for Twim<'a> {\n        type Error = Error;\n\n        fn write(&mut self, addr: u8, bytes: &[u8]) -> Result<(), Error> {\n            self.blocking_write(addr, bytes)\n        }\n    }\n\n    impl<'a> embedded_hal_02::blocking::i2c::Read for Twim<'a> {\n        type Error = Error;\n\n        fn read(&mut self, addr: u8, bytes: &mut [u8]) -> Result<(), Error> {\n            self.blocking_read(addr, bytes)\n        }\n    }\n\n    impl<'a> embedded_hal_02::blocking::i2c::WriteRead for Twim<'a> {\n        type Error = Error;\n\n        fn write_read<'w>(&mut self, addr: u8, bytes: &'w [u8], buffer: &'w mut [u8]) -> Result<(), Error> {\n            self.blocking_write_read(addr, bytes, buffer)\n        }\n    }\n}\n\nimpl embedded_hal_1::i2c::Error for Error {\n    fn kind(&self) -> embedded_hal_1::i2c::ErrorKind {\n        match *self {\n            Self::TxBufferTooLong => embedded_hal_1::i2c::ErrorKind::Other,\n            Self::RxBufferTooLong => embedded_hal_1::i2c::ErrorKind::Other,\n            Self::Transmit => embedded_hal_1::i2c::ErrorKind::Other,\n            Self::Receive => embedded_hal_1::i2c::ErrorKind::Other,\n            Self::RAMBufferTooSmall => embedded_hal_1::i2c::ErrorKind::Other,\n            Self::AddressNack => {\n                embedded_hal_1::i2c::ErrorKind::NoAcknowledge(embedded_hal_1::i2c::NoAcknowledgeSource::Address)\n            }\n            Self::DataNack => {\n                embedded_hal_1::i2c::ErrorKind::NoAcknowledge(embedded_hal_1::i2c::NoAcknowledgeSource::Data)\n            }\n            Self::Overrun => embedded_hal_1::i2c::ErrorKind::Overrun,\n            Self::Timeout => embedded_hal_1::i2c::ErrorKind::Other,\n        }\n    }\n}\n\nimpl<'d> embedded_hal_1::i2c::ErrorType for Twim<'d> {\n    type Error = Error;\n}\n\nimpl<'d> embedded_hal_1::i2c::I2c for Twim<'d> {\n    fn transaction(&mut self, address: u8, operations: &mut [Operation<'_>]) -> Result<(), Self::Error> {\n        self.blocking_transaction(address, operations)\n    }\n}\n\nimpl<'d> embedded_hal_async::i2c::I2c for Twim<'d> {\n    async fn transaction(&mut self, address: u8, operations: &mut [Operation<'_>]) -> Result<(), Self::Error> {\n        self.transaction(address, operations).await\n    }\n}\n\nimpl<'d> SetConfig for Twim<'d> {\n    type Config = Config;\n    type ConfigError = ();\n    fn set_config(&mut self, config: &Self::Config) -> Result<(), Self::ConfigError> {\n        let r = self.r;\n        r.frequency().write(|w| w.set_frequency(config.frequency));\n\n        Ok(())\n    }\n}\n"
  },
  {
    "path": "embassy-nrf/src/twis.rs",
    "content": "//! I2C-compatible Two Wire Interface in slave mode (TWIM) driver.\n\n#![macro_use]\n\nuse core::future::{Future, poll_fn};\nuse core::marker::PhantomData;\nuse core::sync::atomic::Ordering::SeqCst;\nuse core::sync::atomic::compiler_fence;\nuse core::task::Poll;\n\nuse embassy_hal_internal::{Peri, PeripheralType};\nuse embassy_sync::waitqueue::AtomicWaker;\n#[cfg(feature = \"time\")]\nuse embassy_time::{Duration, Instant};\n\nuse crate::chip::{EASY_DMA_SIZE, FORCE_COPY_BUFFER_SIZE};\nuse crate::gpio::Pin as GpioPin;\nuse crate::interrupt::typelevel::Interrupt;\nuse crate::pac::gpio::vals as gpiovals;\nuse crate::pac::twis::vals;\nuse crate::util::slice_in_ram_or;\nuse crate::{gpio, interrupt, pac};\n\n/// TWIS config.\n#[non_exhaustive]\npub struct Config {\n    /// First address\n    pub address0: u8,\n\n    /// Second address, optional.\n    pub address1: Option<u8>,\n\n    /// Overread character.\n    ///\n    /// If the master keeps clocking the bus after all the bytes in the TX buffer have\n    /// already been transmitted, this byte will be constantly transmitted.\n    pub orc: u8,\n\n    /// Enable high drive for the SDA line.\n    pub sda_high_drive: bool,\n\n    /// Enable internal pullup for the SDA line.\n    ///\n    /// Note that using external pullups is recommended for I2C, and\n    /// most boards already have them.\n    pub sda_pullup: bool,\n\n    /// Enable high drive for the SCL line.\n    pub scl_high_drive: bool,\n\n    /// Enable internal pullup for the SCL line.\n    ///\n    /// Note that using external pullups is recommended for I2C, and\n    /// most boards already have them.\n    pub scl_pullup: bool,\n}\n\nimpl Default for Config {\n    fn default() -> Self {\n        Self {\n            address0: 0x55,\n            address1: None,\n            orc: 0x00,\n            scl_high_drive: false,\n            sda_pullup: false,\n            sda_high_drive: false,\n            scl_pullup: false,\n        }\n    }\n}\n\n#[derive(Debug, Copy, Clone, Eq, PartialEq)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\nenum Status {\n    Read,\n    Write,\n}\n\n/// TWIS error.\n#[derive(Debug, Copy, Clone, Eq, PartialEq)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\n#[non_exhaustive]\npub enum Error {\n    /// TX buffer was too long.\n    TxBufferTooLong,\n    /// RX buffer was too long.\n    RxBufferTooLong,\n    /// Didn't receive an ACK bit after a data byte.\n    DataNack,\n    /// Bus error.\n    Bus,\n    /// The buffer is not in data RAM. It's most likely in flash, and nRF's DMA cannot access flash.\n    BufferNotInRAM,\n    /// Overflow\n    Overflow,\n    /// Overread\n    OverRead,\n    /// Timeout\n    Timeout,\n}\n\n/// Received command\n#[derive(Debug, Copy, Clone, Eq, PartialEq)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub enum Command {\n    /// Read\n    Read,\n    /// Write+read\n    WriteRead(usize),\n    /// Write\n    Write(usize),\n}\n\n/// Interrupt handler.\npub struct InterruptHandler<T: Instance> {\n    _phantom: PhantomData<T>,\n}\n\nimpl<T: Instance> interrupt::typelevel::Handler<T::Interrupt> for InterruptHandler<T> {\n    unsafe fn on_interrupt() {\n        let r = T::regs();\n        let s = T::state();\n\n        if r.events_read().read() != 0 || r.events_write().read() != 0 {\n            s.waker.wake();\n            r.intenclr().write(|w| {\n                w.set_read(true);\n                w.set_write(true);\n            });\n        }\n        if r.events_stopped().read() != 0 {\n            s.waker.wake();\n            r.intenclr().write(|w| w.set_stopped(true));\n        }\n        if r.events_error().read() != 0 {\n            s.waker.wake();\n            r.intenclr().write(|w| w.set_error(true));\n        }\n    }\n}\n\n/// TWIS driver.\npub struct Twis<'d> {\n    r: pac::twis::Twis,\n    state: &'static State,\n    _p: PhantomData<&'d ()>,\n}\n\nimpl<'d> Twis<'d> {\n    /// Create a new TWIS driver.\n    pub fn new<T: Instance>(\n        _twis: Peri<'d, T>,\n        _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd,\n        sda: Peri<'d, impl GpioPin>,\n        scl: Peri<'d, impl GpioPin>,\n        config: Config,\n    ) -> Self {\n        let r = T::regs();\n\n        // Configure pins\n        sda.conf().write(|w| {\n            w.set_dir(gpiovals::Dir::INPUT);\n            w.set_input(gpiovals::Input::CONNECT);\n            #[cfg(not(feature = \"_nrf54l\"))]\n            w.set_drive(match config.sda_high_drive {\n                true => gpiovals::Drive::H0D1,\n                false => gpiovals::Drive::S0D1,\n            });\n            #[cfg(feature = \"_nrf54l\")]\n            {\n                w.set_drive0(match config.sda_high_drive {\n                    true => gpiovals::Drive::H,\n                    false => gpiovals::Drive::S,\n                });\n                w.set_drive1(gpiovals::Drive::D);\n            }\n            if config.sda_pullup {\n                w.set_pull(gpiovals::Pull::PULLUP);\n            }\n        });\n        scl.conf().write(|w| {\n            w.set_dir(gpiovals::Dir::INPUT);\n            w.set_input(gpiovals::Input::CONNECT);\n            #[cfg(not(feature = \"_nrf54l\"))]\n            w.set_drive(match config.scl_high_drive {\n                true => gpiovals::Drive::H0D1,\n                false => gpiovals::Drive::S0D1,\n            });\n            #[cfg(feature = \"_nrf54l\")]\n            {\n                w.set_drive0(match config.scl_high_drive {\n                    true => gpiovals::Drive::H,\n                    false => gpiovals::Drive::S,\n                });\n                w.set_drive1(gpiovals::Drive::D);\n            }\n            if config.sda_pullup {\n                w.set_pull(gpiovals::Pull::PULLUP);\n            }\n        });\n\n        // Select pins.\n        r.psel().sda().write_value(sda.psel_bits());\n        r.psel().scl().write_value(scl.psel_bits());\n\n        // Enable TWIS instance.\n        r.enable().write(|w| w.set_enable(vals::Enable::ENABLED));\n\n        // Disable all events interrupts\n        r.intenclr().write(|w| w.0 = 0xFFFF_FFFF);\n\n        // Set address\n        r.address(0).write(|w| w.set_address(config.address0));\n        r.config().write(|w| w.set_address0(true));\n        if let Some(address1) = config.address1 {\n            r.address(1).write(|w| w.set_address(address1));\n            r.config().modify(|w| w.set_address1(true));\n        }\n\n        // Set over-read character\n        r.orc().write(|w| w.set_orc(config.orc));\n\n        // Generate suspend on read event\n        r.shorts().write(|w| w.set_read_suspend(true));\n\n        T::Interrupt::unpend();\n        unsafe { T::Interrupt::enable() };\n\n        Self {\n            r: T::regs(),\n            state: T::state(),\n            _p: PhantomData,\n        }\n    }\n\n    /// Set TX buffer, checking that it is in RAM and has suitable length.\n    unsafe fn set_tx_buffer(&mut self, buffer: &[u8]) -> Result<(), Error> {\n        slice_in_ram_or(buffer, Error::BufferNotInRAM)?;\n\n        if buffer.len() > EASY_DMA_SIZE {\n            return Err(Error::TxBufferTooLong);\n        }\n\n        let r = self.r;\n\n        // We're giving the register a pointer to the stack. Since we're\n        // waiting for the I2C transaction to end before this stack pointer\n        // becomes invalid, there's nothing wrong here.\n        r.dma().tx().ptr().write_value(buffer.as_ptr() as u32);\n        r.dma().tx().maxcnt().write(|w|\n            // We're giving it the length of the buffer, so no danger of\n            // accessing invalid memory. We have verified that the length of the\n            // buffer fits in an `u8`, so the cast to `u8` is also fine.\n            //\n            // The MAXCNT field is 8 bits wide and accepts the full range of\n            // values.\n            w.set_maxcnt(buffer.len() as _));\n\n        Ok(())\n    }\n\n    /// Set RX buffer, checking that it has suitable length.\n    unsafe fn set_rx_buffer(&mut self, buffer: &mut [u8]) -> Result<(), Error> {\n        // NOTE: RAM slice check is not necessary, as a mutable\n        // slice can only be built from data located in RAM.\n\n        if buffer.len() > EASY_DMA_SIZE {\n            return Err(Error::RxBufferTooLong);\n        }\n\n        let r = self.r;\n\n        // We're giving the register a pointer to the stack. Since we're\n        // waiting for the I2C transaction to end before this stack pointer\n        // becomes invalid, there's nothing wrong here.\n        r.dma().rx().ptr().write_value(buffer.as_mut_ptr() as u32);\n        r.dma().rx().maxcnt().write(|w|\n            // We're giving it the length of the buffer, so no danger of\n            // accessing invalid memory. We have verified that the length of the\n            // buffer fits in an `u8`, so the cast to the type of maxcnt\n            // is also fine.\n            //\n            // Note that that nrf52840 maxcnt is a wider\n            // type than a u8, so we use a `_` cast rather than a `u8` cast.\n            // The MAXCNT field is thus at least 8 bits wide and accepts the\n            // full range of values that fit in a `u8`.\n            w.set_maxcnt(buffer.len() as _));\n\n        Ok(())\n    }\n\n    fn clear_errorsrc(&mut self) {\n        let r = self.r;\n        r.errorsrc().write(|w| {\n            w.set_overflow(true);\n            w.set_overread(true);\n            w.set_dnack(true);\n        });\n    }\n\n    /// Returns matched address for latest command.\n    pub fn address_match(&self) -> u8 {\n        let r = self.r;\n        r.address(r.match_().read().0 as usize).read().address()\n    }\n\n    /// Returns the index of the address matched in the latest command.\n    pub fn address_match_index(&self) -> usize {\n        self.r.match_().read().0 as _\n    }\n\n    /// Wait for read, write, stop or error\n    fn blocking_listen_wait(&mut self) -> Result<Status, Error> {\n        let r = self.r;\n        loop {\n            if r.events_error().read() != 0 {\n                r.events_error().write_value(0);\n                r.tasks_stop().write_value(1);\n                while r.events_stopped().read() == 0 {}\n                return Err(Error::Overflow);\n            }\n            if r.events_stopped().read() != 0 {\n                r.events_stopped().write_value(0);\n                return Err(Error::Bus);\n            }\n            if r.events_read().read() != 0 {\n                r.events_read().write_value(0);\n                return Ok(Status::Read);\n            }\n            if r.events_write().read() != 0 {\n                r.events_write().write_value(0);\n                return Ok(Status::Write);\n            }\n        }\n    }\n\n    /// Wait for stop, repeated start or error\n    fn blocking_listen_wait_end(&mut self, status: Status) -> Result<Command, Error> {\n        let r = self.r;\n        loop {\n            // stop if an error occurred\n            if r.events_error().read() != 0 {\n                r.events_error().write_value(0);\n                r.tasks_stop().write_value(1);\n                return Err(Error::Overflow);\n            } else if r.events_stopped().read() != 0 {\n                r.events_stopped().write_value(0);\n                return match status {\n                    Status::Read => Ok(Command::Read),\n                    Status::Write => {\n                        let n = r.dma().rx().amount().read().0 as usize;\n                        Ok(Command::Write(n))\n                    }\n                };\n            } else if r.events_read().read() != 0 {\n                r.events_read().write_value(0);\n                let n = r.dma().rx().amount().read().0 as usize;\n                return Ok(Command::WriteRead(n));\n            }\n        }\n    }\n\n    /// Wait for stop or error\n    fn blocking_wait(&mut self) -> Result<usize, Error> {\n        let r = self.r;\n        loop {\n            // stop if an error occurred\n            if r.events_error().read() != 0 {\n                r.events_error().write_value(0);\n                r.tasks_stop().write_value(1);\n                let errorsrc = r.errorsrc().read();\n                if errorsrc.overread() {\n                    return Err(Error::OverRead);\n                } else if errorsrc.dnack() {\n                    return Err(Error::DataNack);\n                } else {\n                    return Err(Error::Bus);\n                }\n            } else if r.events_stopped().read() != 0 {\n                r.events_stopped().write_value(0);\n                let n = r.dma().tx().amount().read().0 as usize;\n                return Ok(n);\n            }\n        }\n    }\n\n    /// Wait for stop or error with timeout\n    #[cfg(feature = \"time\")]\n    fn blocking_wait_timeout(&mut self, timeout: Duration) -> Result<usize, Error> {\n        let r = self.r;\n        let deadline = Instant::now() + timeout;\n        loop {\n            // stop if an error occurred\n            if r.events_error().read() != 0 {\n                r.events_error().write_value(0);\n                r.tasks_stop().write_value(1);\n                let errorsrc = r.errorsrc().read();\n                if errorsrc.overread() {\n                    return Err(Error::OverRead);\n                } else if errorsrc.dnack() {\n                    return Err(Error::DataNack);\n                } else {\n                    return Err(Error::Bus);\n                }\n            } else if r.events_stopped().read() != 0 {\n                r.events_stopped().write_value(0);\n                let n = r.dma().tx().amount().read().0 as usize;\n                return Ok(n);\n            } else if Instant::now() > deadline {\n                r.tasks_stop().write_value(1);\n                return Err(Error::Timeout);\n            }\n        }\n    }\n\n    /// Wait for read, write, stop or error with timeout\n    #[cfg(feature = \"time\")]\n    fn blocking_listen_wait_timeout(&mut self, timeout: Duration) -> Result<Status, Error> {\n        let r = self.r;\n        let deadline = Instant::now() + timeout;\n        loop {\n            if r.events_error().read() != 0 {\n                r.events_error().write_value(0);\n                r.tasks_stop().write_value(1);\n                while r.events_stopped().read() == 0 {}\n                return Err(Error::Overflow);\n            }\n            if r.events_stopped().read() != 0 {\n                r.events_stopped().write_value(0);\n                return Err(Error::Bus);\n            }\n            if r.events_read().read() != 0 {\n                r.events_read().write_value(0);\n                return Ok(Status::Read);\n            }\n            if r.events_write().read() != 0 {\n                r.events_write().write_value(0);\n                return Ok(Status::Write);\n            }\n            if Instant::now() > deadline {\n                r.tasks_stop().write_value(1);\n                return Err(Error::Timeout);\n            }\n        }\n    }\n\n    /// Wait for stop, repeated start or error with timeout\n    #[cfg(feature = \"time\")]\n    fn blocking_listen_wait_end_timeout(&mut self, status: Status, timeout: Duration) -> Result<Command, Error> {\n        let r = self.r;\n        let deadline = Instant::now() + timeout;\n        loop {\n            // stop if an error occurred\n            if r.events_error().read() != 0 {\n                r.events_error().write_value(0);\n                r.tasks_stop().write_value(1);\n                return Err(Error::Overflow);\n            } else if r.events_stopped().read() != 0 {\n                r.events_stopped().write_value(0);\n                return match status {\n                    Status::Read => Ok(Command::Read),\n                    Status::Write => {\n                        let n = r.dma().rx().amount().read().0 as usize;\n                        Ok(Command::Write(n))\n                    }\n                };\n            } else if r.events_read().read() != 0 {\n                r.events_read().write_value(0);\n                let n = r.dma().rx().amount().read().0 as usize;\n                return Ok(Command::WriteRead(n));\n            } else if Instant::now() > deadline {\n                r.tasks_stop().write_value(1);\n                return Err(Error::Timeout);\n            }\n        }\n    }\n\n    /// Wait for stop or error\n    fn async_wait(&mut self) -> impl Future<Output = Result<usize, Error>> {\n        let r = self.r;\n        let s = self.state;\n        poll_fn(move |cx| {\n            s.waker.register(cx.waker());\n\n            // stop if an error occurred\n            if r.events_error().read() != 0 {\n                r.events_error().write_value(0);\n                r.tasks_stop().write_value(1);\n                let errorsrc = r.errorsrc().read();\n                if errorsrc.overread() {\n                    return Poll::Ready(Err(Error::OverRead));\n                } else if errorsrc.dnack() {\n                    return Poll::Ready(Err(Error::DataNack));\n                } else {\n                    return Poll::Ready(Err(Error::Bus));\n                }\n            } else if r.events_stopped().read() != 0 {\n                r.events_stopped().write_value(0);\n                let n = r.dma().tx().amount().read().0 as usize;\n                return Poll::Ready(Ok(n));\n            }\n\n            Poll::Pending\n        })\n    }\n\n    /// Wait for read or write\n    fn async_listen_wait(&mut self) -> impl Future<Output = Result<Status, Error>> {\n        let r = self.r;\n        let s = self.state;\n        poll_fn(move |cx| {\n            s.waker.register(cx.waker());\n\n            // stop if an error occurred\n            if r.events_error().read() != 0 {\n                r.events_error().write_value(0);\n                r.tasks_stop().write_value(1);\n                return Poll::Ready(Err(Error::Overflow));\n            } else if r.events_read().read() != 0 {\n                r.events_read().write_value(0);\n                return Poll::Ready(Ok(Status::Read));\n            } else if r.events_write().read() != 0 {\n                r.events_write().write_value(0);\n                return Poll::Ready(Ok(Status::Write));\n            } else if r.events_stopped().read() != 0 {\n                r.events_stopped().write_value(0);\n                return Poll::Ready(Err(Error::Bus));\n            }\n            Poll::Pending\n        })\n    }\n\n    /// Wait for stop, repeated start or error\n    fn async_listen_wait_end(&mut self, status: Status) -> impl Future<Output = Result<Command, Error>> {\n        let r = self.r;\n        let s = self.state;\n        poll_fn(move |cx| {\n            s.waker.register(cx.waker());\n\n            // stop if an error occurred\n            if r.events_error().read() != 0 {\n                r.events_error().write_value(0);\n                r.tasks_stop().write_value(1);\n                return Poll::Ready(Err(Error::Overflow));\n            } else if r.events_stopped().read() != 0 {\n                r.events_stopped().write_value(0);\n                return match status {\n                    Status::Read => Poll::Ready(Ok(Command::Read)),\n                    Status::Write => {\n                        let n = r.dma().rx().amount().read().0 as usize;\n                        Poll::Ready(Ok(Command::Write(n)))\n                    }\n                };\n            } else if r.events_read().read() != 0 {\n                r.events_read().write_value(0);\n                let n = r.dma().rx().amount().read().0 as usize;\n                return Poll::Ready(Ok(Command::WriteRead(n)));\n            }\n            Poll::Pending\n        })\n    }\n\n    fn setup_respond_from_ram(&mut self, buffer: &[u8], inten: bool) -> Result<(), Error> {\n        let r = self.r;\n\n        compiler_fence(SeqCst);\n\n        // Set up the DMA write.\n        unsafe { self.set_tx_buffer(buffer)? };\n\n        // Clear events\n        r.events_stopped().write_value(0);\n        r.events_error().write_value(0);\n        self.clear_errorsrc();\n\n        if inten {\n            r.intenset().write(|w| {\n                w.set_stopped(true);\n                w.set_error(true);\n            });\n        } else {\n            r.intenclr().write(|w| {\n                w.set_stopped(true);\n                w.set_error(true);\n            });\n        }\n\n        // Start write operation.\n        r.tasks_preparetx().write_value(1);\n        r.tasks_resume().write_value(1);\n        Ok(())\n    }\n\n    fn setup_respond(&mut self, wr_buffer: &[u8], inten: bool) -> Result<(), Error> {\n        match self.setup_respond_from_ram(wr_buffer, inten) {\n            Ok(_) => Ok(()),\n            Err(Error::BufferNotInRAM) => {\n                trace!(\"Copying TWIS tx buffer into RAM for DMA\");\n                let tx_ram_buf = &mut [0; FORCE_COPY_BUFFER_SIZE][..wr_buffer.len()];\n                tx_ram_buf.copy_from_slice(wr_buffer);\n                self.setup_respond_from_ram(tx_ram_buf, inten)\n            }\n            Err(error) => Err(error),\n        }\n    }\n\n    fn setup_listen(&mut self, buffer: &mut [u8], inten: bool) -> Result<(), Error> {\n        let r = self.r;\n        compiler_fence(SeqCst);\n\n        // Set up the DMA read.\n        unsafe { self.set_rx_buffer(buffer)? };\n\n        // Clear events\n        r.events_read().write_value(0);\n        r.events_write().write_value(0);\n        r.events_stopped().write_value(0);\n        r.events_error().write_value(0);\n        self.clear_errorsrc();\n\n        if inten {\n            r.intenset().write(|w| {\n                w.set_stopped(true);\n                w.set_error(true);\n                w.set_read(true);\n                w.set_write(true);\n            });\n        } else {\n            r.intenclr().write(|w| {\n                w.set_stopped(true);\n                w.set_error(true);\n                w.set_read(true);\n                w.set_write(true);\n            });\n        }\n\n        // Start read operation.\n        r.tasks_preparerx().write_value(1);\n\n        Ok(())\n    }\n\n    fn setup_listen_end(&mut self, inten: bool) -> Result<(), Error> {\n        let r = self.r;\n        compiler_fence(SeqCst);\n\n        // Clear events\n        r.events_read().write_value(0);\n        r.events_write().write_value(0);\n        r.events_stopped().write_value(0);\n        r.events_error().write_value(0);\n        self.clear_errorsrc();\n\n        if inten {\n            r.intenset().write(|w| {\n                w.set_stopped(true);\n                w.set_error(true);\n                w.set_read(true);\n            });\n        } else {\n            r.intenclr().write(|w| {\n                w.set_stopped(true);\n                w.set_error(true);\n                w.set_read(true);\n            });\n        }\n\n        Ok(())\n    }\n\n    /// Wait for commands from an I2C master.\n    /// `buffer` is provided in case master does a 'write' and is unused for 'read'.\n    /// The buffer must have a length of at most 255 bytes on the nRF52832\n    /// and at most 65535 bytes on the nRF52840.\n    /// To know which one of the addresses were matched, call `address_match` or `address_match_index`\n    pub fn blocking_listen(&mut self, buffer: &mut [u8]) -> Result<Command, Error> {\n        self.setup_listen(buffer, false)?;\n        let status = self.blocking_listen_wait()?;\n        if status == Status::Write {\n            self.setup_listen_end(false)?;\n            let command = self.blocking_listen_wait_end(status)?;\n            return Ok(command);\n        }\n        Ok(Command::Read)\n    }\n\n    /// Respond to an I2C master READ command.\n    /// Returns the number of bytes written.\n    /// The buffer must have a length of at most 255 bytes on the nRF52832\n    /// and at most 65535 bytes on the nRF52840.\n    pub fn blocking_respond_to_read(&mut self, buffer: &[u8]) -> Result<usize, Error> {\n        self.setup_respond(buffer, false)?;\n        self.blocking_wait()\n    }\n\n    /// Same as [`blocking_respond_to_read`](Twis::blocking_respond_to_read) but will fail instead of copying data into RAM.\n    /// Consult the module level documentation to learn more.\n    pub fn blocking_respond_to_read_from_ram(&mut self, buffer: &[u8]) -> Result<usize, Error> {\n        self.setup_respond_from_ram(buffer, false)?;\n        self.blocking_wait()\n    }\n\n    // ===========================================\n\n    /// Wait for commands from an I2C master, with timeout.\n    /// `buffer` is provided in case master does a 'write' and is unused for 'read'.\n    /// The buffer must have a length of at most 255 bytes on the nRF52832\n    /// and at most 65535 bytes on the nRF52840.\n    /// To know which one of the addresses were matched, call `address_match` or `address_match_index`\n    #[cfg(feature = \"time\")]\n    pub fn blocking_listen_timeout(&mut self, buffer: &mut [u8], timeout: Duration) -> Result<Command, Error> {\n        self.setup_listen(buffer, false)?;\n        let status = self.blocking_listen_wait_timeout(timeout)?;\n        if status == Status::Write {\n            self.setup_listen_end(false)?;\n            let command = self.blocking_listen_wait_end_timeout(status, timeout)?;\n            return Ok(command);\n        }\n        Ok(Command::Read)\n    }\n\n    /// Respond to an I2C master READ command with timeout.\n    /// Returns the number of bytes written.\n    /// See [Self::blocking_respond_to_read].\n    #[cfg(feature = \"time\")]\n    pub fn blocking_respond_to_read_timeout(&mut self, buffer: &[u8], timeout: Duration) -> Result<usize, Error> {\n        self.setup_respond(buffer, false)?;\n        self.blocking_wait_timeout(timeout)\n    }\n\n    /// Same as [`blocking_respond_to_read_timeout`](Twis::blocking_respond_to_read_timeout) but will fail instead of copying data into RAM.\n    /// Consult the module level documentation to learn more.\n    #[cfg(feature = \"time\")]\n    pub fn blocking_respond_to_read_from_ram_timeout(\n        &mut self,\n        buffer: &[u8],\n        timeout: Duration,\n    ) -> Result<usize, Error> {\n        self.setup_respond_from_ram(buffer, false)?;\n        self.blocking_wait_timeout(timeout)\n    }\n\n    // ===========================================\n\n    /// Wait asynchronously for commands from an I2C master.\n    /// `buffer` is provided in case master does a 'write' and is unused for 'read'.\n    /// The buffer must have a length of at most 255 bytes on the nRF52832\n    /// and at most 65535 bytes on the nRF52840.\n    /// To know which one of the addresses were matched, call `address_match` or `address_match_index`\n    pub async fn listen(&mut self, buffer: &mut [u8]) -> Result<Command, Error> {\n        self.setup_listen(buffer, true)?;\n        let status = self.async_listen_wait().await?;\n        if status == Status::Write {\n            self.setup_listen_end(true)?;\n            let command = self.async_listen_wait_end(status).await?;\n            return Ok(command);\n        }\n        Ok(Command::Read)\n    }\n\n    /// Respond to an I2C master READ command, asynchronously.\n    /// Returns the number of bytes written.\n    /// The buffer must have a length of at most 255 bytes on the nRF52832\n    /// and at most 65535 bytes on the nRF52840.\n    pub async fn respond_to_read(&mut self, buffer: &[u8]) -> Result<usize, Error> {\n        self.setup_respond(buffer, true)?;\n        self.async_wait().await\n    }\n\n    /// Same as [`respond_to_read`](Twis::respond_to_read) but will fail instead of copying data into RAM. Consult the module level documentation to learn more.\n    pub async fn respond_to_read_from_ram(&mut self, buffer: &[u8]) -> Result<usize, Error> {\n        self.setup_respond_from_ram(buffer, true)?;\n        self.async_wait().await\n    }\n}\n\nimpl<'a> Drop for Twis<'a> {\n    fn drop(&mut self) {\n        trace!(\"twis drop\");\n\n        // TODO: check for abort\n\n        // disable!\n        let r = self.r;\n        r.enable().write(|w| w.set_enable(vals::Enable::DISABLED));\n\n        gpio::deconfigure_pin(r.psel().sda().read());\n        gpio::deconfigure_pin(r.psel().scl().read());\n\n        trace!(\"twis drop: done\");\n    }\n}\n\npub(crate) struct State {\n    waker: AtomicWaker,\n}\n\nimpl State {\n    pub(crate) const fn new() -> Self {\n        Self {\n            waker: AtomicWaker::new(),\n        }\n    }\n}\n\npub(crate) trait SealedInstance {\n    fn regs() -> pac::twis::Twis;\n    fn state() -> &'static State;\n}\n\n/// TWIS peripheral instance.\n#[allow(private_bounds)]\npub trait Instance: SealedInstance + PeripheralType + 'static {\n    /// Interrupt for this peripheral.\n    type Interrupt: interrupt::typelevel::Interrupt;\n}\n\nmacro_rules! impl_twis {\n    ($type:ident, $pac_type:ident, $irq:ident) => {\n        impl crate::twis::SealedInstance for peripherals::$type {\n            fn regs() -> pac::twis::Twis {\n                pac::$pac_type\n            }\n            fn state() -> &'static crate::twis::State {\n                static STATE: crate::twis::State = crate::twis::State::new();\n                &STATE\n            }\n        }\n        impl crate::twis::Instance for peripherals::$type {\n            type Interrupt = crate::interrupt::typelevel::$irq;\n        }\n    };\n}\n"
  },
  {
    "path": "embassy-nrf/src/uarte.rs",
    "content": "//! Universal Asynchronous Receiver Transmitter (UART) driver.\n//!\n//! The UART driver is provided in two flavors - this one and also [crate::buffered_uarte::BufferedUarte].\n//! The [Uarte] here is useful for those use-cases where reading the UARTE peripheral is\n//! exclusively awaited on. If the [Uarte] is required to be awaited on with some other future,\n//! for example when using `futures_util::future::select`, then you should consider\n//! [crate::buffered_uarte::BufferedUarte] so that reads may continue while processing these\n//! other futures. If you do not then you may lose data between reads.\n//!\n//! An advantage of the [Uarte] has over [crate::buffered_uarte::BufferedUarte] is that less\n//! memory may be used given that buffers are passed in directly to its read and write\n//! methods.\n\n#![macro_use]\n\nuse core::future::poll_fn;\nuse core::marker::PhantomData;\nuse core::sync::atomic::{AtomicU8, Ordering, compiler_fence};\nuse core::task::Poll;\n\nuse embassy_hal_internal::drop::OnDrop;\nuse embassy_hal_internal::{Peri, PeripheralType};\nuse embassy_sync::waitqueue::AtomicWaker;\n// Re-export SVD variants to allow user to directly set values.\npub use pac::uarte::vals::{Baudrate, ConfigParity as Parity};\n\nuse crate::chip::{EASY_DMA_SIZE, FORCE_COPY_BUFFER_SIZE};\nuse crate::gpio::{self, AnyPin, DISCONNECTED, Pin as GpioPin, PselBits, SealedPin as _};\nuse crate::interrupt::typelevel::Interrupt;\nuse crate::pac::gpio::vals as gpiovals;\nuse crate::pac::uarte::vals;\nuse crate::ppi::{AnyConfigurableChannel, ConfigurableChannel, Event, Ppi, Task};\nuse crate::timer::{Frequency, Instance as TimerInstance, Timer};\nuse crate::util::slice_in_ram_or;\nuse crate::{interrupt, pac};\n\n/// UARTE config.\n#[derive(Clone)]\n#[non_exhaustive]\npub struct Config {\n    /// Parity bit.\n    pub parity: Parity,\n    /// Baud rate.\n    pub baudrate: Baudrate,\n}\n\nimpl Default for Config {\n    fn default() -> Self {\n        Self {\n            parity: Parity::EXCLUDED,\n            baudrate: Baudrate::BAUD115200,\n        }\n    }\n}\n\nbitflags::bitflags! {\n    /// Error source flags\n    pub(crate) struct ErrorSource: u32 {\n        /// Buffer overrun\n        const OVERRUN = 0x01;\n        /// Parity error\n        const PARITY = 0x02;\n        /// Framing error\n        const FRAMING = 0x04;\n        /// Break condition\n        const BREAK = 0x08;\n    }\n}\n\nimpl ErrorSource {\n    #[inline]\n    fn check(self) -> Result<(), Error> {\n        if self.contains(ErrorSource::OVERRUN) {\n            Err(Error::Overrun)\n        } else if self.contains(ErrorSource::PARITY) {\n            Err(Error::Parity)\n        } else if self.contains(ErrorSource::FRAMING) {\n            Err(Error::Framing)\n        } else if self.contains(ErrorSource::BREAK) {\n            Err(Error::Break)\n        } else {\n            Ok(())\n        }\n    }\n}\n\n/// UART error.\n#[derive(Debug, Clone, Copy, PartialEq, Eq)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\n#[non_exhaustive]\npub enum Error {\n    /// Buffer was too long.\n    BufferTooLong,\n    /// The buffer is not in data RAM. It's most likely in flash, and nRF's DMA cannot access flash.\n    BufferNotInRAM,\n    /// Framing Error\n    Framing,\n    /// Parity Error\n    Parity,\n    /// Buffer Overrun\n    Overrun,\n    /// Break condition\n    Break,\n}\n\n/// Interrupt handler.\npub struct InterruptHandler<T: Instance> {\n    _phantom: PhantomData<T>,\n}\n\nimpl<T: Instance> interrupt::typelevel::Handler<T::Interrupt> for InterruptHandler<T> {\n    unsafe fn on_interrupt() {\n        let r = T::regs();\n        let s = T::state();\n\n        let endrx = r.events_dma().rx().end().read();\n        let error = r.events_error().read();\n        let rxto = r.events_rxto().read();\n        if endrx != 0 || error != 0 || rxto != 0 {\n            s.rx_waker.wake();\n            if endrx != 0 {\n                r.intenclr().write(|w| w.set_dmarxend(true));\n            }\n            if error != 0 {\n                r.intenclr().write(|w| w.set_error(true));\n            }\n            if rxto != 0 {\n                r.intenclr().write(|w| w.set_rxto(true));\n            }\n        }\n        if r.events_dma().tx().end().read() != 0 {\n            s.tx_waker.wake();\n            r.intenclr().write(|w| w.set_dmatxend(true));\n        }\n    }\n}\n\n/// UARTE driver.\npub struct Uarte<'d> {\n    tx: UarteTx<'d>,\n    rx: UarteRx<'d>,\n}\n\n/// Transmitter part of the UARTE driver.\n///\n/// This can be obtained via [`Uarte::split`], or created directly.\npub struct UarteTx<'d> {\n    r: pac::uarte::Uarte,\n    state: &'static State,\n    _p: PhantomData<&'d ()>,\n}\n\n/// Receiver part of the UARTE driver.\n///\n/// This can be obtained via [`Uarte::split`], or created directly.\npub struct UarteRx<'d> {\n    r: pac::uarte::Uarte,\n    state: &'static State,\n    _p: PhantomData<&'d ()>,\n    rx_on: bool,\n}\n\nimpl<'d> Uarte<'d> {\n    /// Create a new UARTE without hardware flow control\n    pub fn new<T: Instance>(\n        uarte: Peri<'d, T>,\n        rxd: Peri<'d, impl GpioPin>,\n        txd: Peri<'d, impl GpioPin>,\n        _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd,\n        config: Config,\n    ) -> Self {\n        Self::new_inner(uarte, rxd.into(), txd.into(), None, None, config)\n    }\n\n    /// Create a new UARTE with hardware flow control (RTS/CTS)\n    pub fn new_with_rtscts<T: Instance>(\n        uarte: Peri<'d, T>,\n        rxd: Peri<'d, impl GpioPin>,\n        txd: Peri<'d, impl GpioPin>,\n        cts: Peri<'d, impl GpioPin>,\n        rts: Peri<'d, impl GpioPin>,\n        _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd,\n        config: Config,\n    ) -> Self {\n        Self::new_inner(\n            uarte,\n            rxd.into(),\n            txd.into(),\n            Some(cts.into()),\n            Some(rts.into()),\n            config,\n        )\n    }\n\n    fn new_inner<T: Instance>(\n        _uarte: Peri<'d, T>,\n        rxd: Peri<'d, AnyPin>,\n        txd: Peri<'d, AnyPin>,\n        cts: Option<Peri<'d, AnyPin>>,\n        rts: Option<Peri<'d, AnyPin>>,\n        config: Config,\n    ) -> Self {\n        let r = T::regs();\n\n        let hardware_flow_control = match (rts.is_some(), cts.is_some()) {\n            (false, false) => false,\n            (true, true) => true,\n            _ => panic!(\"RTS and CTS pins must be either both set or none set.\"),\n        };\n        configure(r, config, hardware_flow_control);\n        configure_rx_pins(r, rxd, rts);\n        configure_tx_pins(r, txd, cts);\n\n        T::Interrupt::unpend();\n        unsafe { T::Interrupt::enable() };\n        r.enable().write(|w| w.set_enable(vals::Enable::ENABLED));\n\n        let s = T::state();\n        s.tx_rx_refcount.store(2, Ordering::Relaxed);\n\n        Self {\n            tx: UarteTx {\n                r: T::regs(),\n                state: T::state(),\n                _p: PhantomData {},\n            },\n            rx: UarteRx {\n                r: T::regs(),\n                state: T::state(),\n                _p: PhantomData {},\n                rx_on: false,\n            },\n        }\n    }\n\n    /// Split the Uarte into the transmitter and receiver parts.\n    ///\n    /// This is useful to concurrently transmit and receive from independent tasks.\n    pub fn split(self) -> (UarteTx<'d>, UarteRx<'d>) {\n        (self.tx, self.rx)\n    }\n\n    /// Split the UART in reader and writer parts, by reference.\n    ///\n    /// The returned halves borrow from `self`, so you can drop them and go back to using\n    /// the \"un-split\" `self`. This allows temporarily splitting the UART.\n    pub fn split_by_ref(&mut self) -> (&mut UarteTx<'d>, &mut UarteRx<'d>) {\n        (&mut self.tx, &mut self.rx)\n    }\n\n    /// Split the Uarte into the transmitter and receiver with idle support parts.\n    ///\n    /// This is useful to concurrently transmit and receive from independent tasks.\n    pub fn split_with_idle<U: TimerInstance>(\n        self,\n        timer: Peri<'d, U>,\n        ppi_ch1: Peri<'d, impl ConfigurableChannel + 'd>,\n        ppi_ch2: Peri<'d, impl ConfigurableChannel + 'd>,\n    ) -> (UarteTx<'d>, UarteRxWithIdle<'d>) {\n        (self.tx, self.rx.with_idle(timer, ppi_ch1, ppi_ch2))\n    }\n\n    /// Return the endtx event for use with PPI\n    pub fn event_endtx(&self) -> Event<'_> {\n        let r = self.tx.r;\n        Event::from_reg(r.events_dma().tx().end())\n    }\n\n    /// Read bytes until the buffer is filled.\n    pub async fn read(&mut self, buffer: &mut [u8]) -> Result<(), Error> {\n        self.rx.read(buffer).await\n    }\n\n    /// Flush the RX FIFO to RAM without activating the receiver.\n    pub async fn flush_rx(&mut self, buffer: &mut [u8]) -> Result<usize, Error> {\n        self.rx.flush_rx(buffer).await\n    }\n\n    /// Write all bytes in the buffer.\n    pub async fn write(&mut self, buffer: &[u8]) -> Result<(), Error> {\n        self.tx.write(buffer).await\n    }\n\n    /// Same as [`write`](Uarte::write) but will fail instead of copying data into RAM. Consult the module level documentation to learn more.\n    pub async fn write_from_ram(&mut self, buffer: &[u8]) -> Result<(), Error> {\n        self.tx.write_from_ram(buffer).await\n    }\n\n    /// Read bytes until the buffer is filled.\n    pub fn blocking_read(&mut self, buffer: &mut [u8]) -> Result<(), Error> {\n        self.rx.blocking_read(buffer)\n    }\n\n    /// Flush the RX FIFO to RAM without activating the receiver.\n    pub fn blocking_flush_rx(&mut self, buffer: &mut [u8]) -> Result<usize, Error> {\n        self.rx.blocking_flush_rx(buffer)\n    }\n\n    /// Write all bytes in the buffer.\n    pub fn blocking_write(&mut self, buffer: &[u8]) -> Result<(), Error> {\n        self.tx.blocking_write(buffer)\n    }\n\n    /// Same as [`blocking_write`](Uarte::blocking_write) but will fail instead of copying data into RAM. Consult the module level documentation to learn more.\n    pub fn blocking_write_from_ram(&mut self, buffer: &[u8]) -> Result<(), Error> {\n        self.tx.blocking_write_from_ram(buffer)\n    }\n}\n\npub(crate) fn configure_tx_pins(r: pac::uarte::Uarte, txd: Peri<'_, AnyPin>, cts: Option<Peri<'_, AnyPin>>) {\n    txd.set_high();\n    txd.conf().write(|w| {\n        w.set_dir(gpiovals::Dir::OUTPUT);\n        w.set_input(gpiovals::Input::DISCONNECT);\n        #[cfg(not(feature = \"_nrf54l\"))]\n        w.set_drive(gpiovals::Drive::H0H1);\n        #[cfg(feature = \"_nrf54l\")]\n        {\n            w.set_drive0(gpiovals::Drive::H);\n            w.set_drive1(gpiovals::Drive::H);\n        }\n    });\n    r.psel().txd().write_value(txd.psel_bits());\n\n    if let Some(pin) = &cts {\n        pin.conf().write(|w| {\n            w.set_dir(gpiovals::Dir::INPUT);\n            w.set_input(gpiovals::Input::CONNECT);\n            #[cfg(not(feature = \"_nrf54l\"))]\n            w.set_drive(gpiovals::Drive::H0H1);\n            #[cfg(feature = \"_nrf54l\")]\n            {\n                w.set_drive0(gpiovals::Drive::H);\n                w.set_drive1(gpiovals::Drive::H);\n            }\n        });\n    }\n    r.psel().cts().write_value(cts.psel_bits());\n}\n\npub(crate) fn configure_rx_pins(r: pac::uarte::Uarte, rxd: Peri<'_, AnyPin>, rts: Option<Peri<'_, AnyPin>>) {\n    rxd.conf().write(|w| {\n        w.set_dir(gpiovals::Dir::INPUT);\n        w.set_input(gpiovals::Input::CONNECT);\n        #[cfg(not(feature = \"_nrf54l\"))]\n        w.set_drive(gpiovals::Drive::H0H1);\n        #[cfg(feature = \"_nrf54l\")]\n        {\n            w.set_drive0(gpiovals::Drive::H);\n            w.set_drive1(gpiovals::Drive::H);\n        }\n    });\n    r.psel().rxd().write_value(rxd.psel_bits());\n\n    if let Some(pin) = &rts {\n        pin.set_high();\n        pin.conf().write(|w| {\n            w.set_dir(gpiovals::Dir::OUTPUT);\n            w.set_input(gpiovals::Input::DISCONNECT);\n            #[cfg(not(feature = \"_nrf54l\"))]\n            w.set_drive(gpiovals::Drive::H0H1);\n            #[cfg(feature = \"_nrf54l\")]\n            {\n                w.set_drive0(gpiovals::Drive::H);\n                w.set_drive1(gpiovals::Drive::H);\n            }\n        });\n    }\n    r.psel().rts().write_value(rts.psel_bits());\n}\n\npub(crate) fn configure(r: pac::uarte::Uarte, config: Config, hardware_flow_control: bool) {\n    r.config().write(|w| {\n        w.set_hwfc(hardware_flow_control);\n        w.set_parity(config.parity);\n        #[cfg(feature = \"_nrf54l\")]\n        w.set_framesize(vals::Framesize::_8BIT);\n        #[cfg(feature = \"_nrf54l\")]\n        w.set_frametimeout(true);\n    });\n    r.baudrate().write(|w| w.set_baudrate(config.baudrate));\n\n    // Disable all interrupts\n    r.intenclr().write(|w| w.0 = 0xFFFF_FFFF);\n\n    // Reset rxstarted, txstarted. These are used by drop to know whether a transfer was\n    // stopped midway or not.\n    r.events_dma().rx().ready().write_value(0);\n    r.events_dma().tx().ready().write_value(0);\n\n    // reset all pins\n    r.psel().txd().write_value(DISCONNECTED);\n    r.psel().rxd().write_value(DISCONNECTED);\n    r.psel().cts().write_value(DISCONNECTED);\n    r.psel().rts().write_value(DISCONNECTED);\n\n    apply_workaround_for_enable_anomaly(r);\n}\n\nimpl<'d> UarteTx<'d> {\n    /// Create a new tx-only UARTE without hardware flow control\n    pub fn new<T: Instance>(\n        uarte: Peri<'d, T>,\n        _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd,\n        txd: Peri<'d, impl GpioPin>,\n        config: Config,\n    ) -> Self {\n        Self::new_inner(uarte, txd.into(), None, config)\n    }\n\n    /// Create a new tx-only UARTE with hardware flow control (RTS/CTS)\n    pub fn new_with_rtscts<T: Instance>(\n        uarte: Peri<'d, T>,\n        _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd,\n        txd: Peri<'d, impl GpioPin>,\n        cts: Peri<'d, impl GpioPin>,\n        config: Config,\n    ) -> Self {\n        Self::new_inner(uarte, txd.into(), Some(cts.into()), config)\n    }\n\n    fn new_inner<T: Instance>(\n        _uarte: Peri<'d, T>,\n        txd: Peri<'d, AnyPin>,\n        cts: Option<Peri<'d, AnyPin>>,\n        config: Config,\n    ) -> Self {\n        let r = T::regs();\n\n        configure(r, config, cts.is_some());\n        configure_tx_pins(r, txd, cts);\n\n        T::Interrupt::unpend();\n        unsafe { T::Interrupt::enable() };\n        r.enable().write(|w| w.set_enable(vals::Enable::ENABLED));\n\n        let s = T::state();\n        s.tx_rx_refcount.store(1, Ordering::Relaxed);\n\n        Self {\n            r: T::regs(),\n            state: T::state(),\n            _p: PhantomData {},\n        }\n    }\n\n    /// Write all bytes in the buffer.\n    pub async fn write(&mut self, buffer: &[u8]) -> Result<(), Error> {\n        match self.write_from_ram(buffer).await {\n            Ok(_) => Ok(()),\n            Err(Error::BufferNotInRAM) => {\n                trace!(\"Copying UARTE tx buffer into RAM for DMA\");\n                let ram_buf = &mut [0; FORCE_COPY_BUFFER_SIZE][..buffer.len()];\n                ram_buf.copy_from_slice(buffer);\n                self.write_from_ram(ram_buf).await\n            }\n            Err(error) => Err(error),\n        }\n    }\n\n    /// Same as [`write`](Self::write) but will fail instead of copying data into RAM. Consult the module level documentation to learn more.\n    pub async fn write_from_ram(&mut self, buffer: &[u8]) -> Result<(), Error> {\n        if buffer.is_empty() {\n            return Ok(());\n        }\n\n        slice_in_ram_or(buffer, Error::BufferNotInRAM)?;\n        if buffer.len() > EASY_DMA_SIZE {\n            return Err(Error::BufferTooLong);\n        }\n\n        let ptr = buffer.as_ptr();\n        let len = buffer.len();\n\n        let r = self.r;\n        let s = self.state;\n\n        let drop = OnDrop::new(move || {\n            trace!(\"write drop: stopping\");\n\n            r.intenclr().write(|w| w.set_dmatxend(true));\n            r.events_txstopped().write_value(0);\n            r.tasks_dma().tx().stop().write_value(1);\n\n            // TX is stopped almost instantly, spinning is fine.\n            while r.events_dma().tx().end().read() == 0 {}\n            trace!(\"write drop: stopped\");\n        });\n\n        r.dma().tx().ptr().write_value(ptr as u32);\n        r.dma().tx().maxcnt().write(|w| w.set_maxcnt(len as _));\n\n        r.events_dma().tx().end().write_value(0);\n        r.intenset().write(|w| w.set_dmatxend(true));\n\n        compiler_fence(Ordering::SeqCst);\n\n        trace!(\"starttx\");\n        r.tasks_dma().tx().start().write_value(1);\n\n        poll_fn(|cx| {\n            s.tx_waker.register(cx.waker());\n            if r.events_dma().tx().end().read() != 0 {\n                return Poll::Ready(());\n            }\n            Poll::Pending\n        })\n        .await;\n\n        compiler_fence(Ordering::SeqCst);\n        r.events_dma().tx().ready().write_value(0);\n        drop.defuse();\n\n        Ok(())\n    }\n\n    /// Write all bytes in the buffer.\n    pub fn blocking_write(&mut self, buffer: &[u8]) -> Result<(), Error> {\n        match self.blocking_write_from_ram(buffer) {\n            Ok(_) => Ok(()),\n            Err(Error::BufferNotInRAM) => {\n                trace!(\"Copying UARTE tx buffer into RAM for DMA\");\n                let ram_buf = &mut [0; FORCE_COPY_BUFFER_SIZE][..buffer.len()];\n                ram_buf.copy_from_slice(buffer);\n                self.blocking_write_from_ram(ram_buf)\n            }\n            Err(error) => Err(error),\n        }\n    }\n\n    /// Same as [`write_from_ram`](Self::write_from_ram) but will fail instead of copying data into RAM. Consult the module level documentation to learn more.\n    pub fn blocking_write_from_ram(&mut self, buffer: &[u8]) -> Result<(), Error> {\n        if buffer.is_empty() {\n            return Ok(());\n        }\n\n        slice_in_ram_or(buffer, Error::BufferNotInRAM)?;\n        if buffer.len() > EASY_DMA_SIZE {\n            return Err(Error::BufferTooLong);\n        }\n\n        let ptr = buffer.as_ptr();\n        let len = buffer.len();\n\n        let r = self.r;\n\n        r.dma().tx().ptr().write_value(ptr as u32);\n        r.dma().tx().maxcnt().write(|w| w.set_maxcnt(len as _));\n\n        r.events_dma().tx().end().write_value(0);\n        r.intenclr().write(|w| w.set_dmatxend(true));\n\n        compiler_fence(Ordering::SeqCst);\n\n        trace!(\"starttx\");\n        r.tasks_dma().tx().start().write_value(1);\n\n        while r.events_dma().tx().end().read() == 0 {}\n\n        compiler_fence(Ordering::SeqCst);\n        r.events_dma().tx().ready().write_value(0);\n\n        Ok(())\n    }\n}\n\nimpl<'a> Drop for UarteTx<'a> {\n    fn drop(&mut self) {\n        trace!(\"uarte tx drop\");\n\n        let r = self.r;\n\n        let did_stoptx = r.events_dma().tx().ready().read() != 0;\n        trace!(\"did_stoptx {}\", did_stoptx);\n\n        // Wait for txstopped, if needed.\n        while did_stoptx && r.events_txstopped().read() == 0 {}\n\n        let s = self.state;\n\n        drop_tx_rx(r, s);\n    }\n}\n\nimpl<'d> UarteRx<'d> {\n    /// Create a new rx-only UARTE without hardware flow control\n    pub fn new<T: Instance>(\n        uarte: Peri<'d, T>,\n        _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd,\n        rxd: Peri<'d, impl GpioPin>,\n        config: Config,\n    ) -> Self {\n        Self::new_inner(uarte, rxd.into(), None, config)\n    }\n\n    /// Create a new rx-only UARTE with hardware flow control (RTS/CTS)\n    pub fn new_with_rtscts<T: Instance>(\n        uarte: Peri<'d, T>,\n        _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd,\n        rxd: Peri<'d, impl GpioPin>,\n        rts: Peri<'d, impl GpioPin>,\n        config: Config,\n    ) -> Self {\n        Self::new_inner(uarte, rxd.into(), Some(rts.into()), config)\n    }\n\n    /// Check for errors and clear the error register if an error occured.\n    fn check_and_clear_errors(&mut self) -> Result<(), Error> {\n        let r = self.r;\n        let err_bits = r.errorsrc().read();\n        r.errorsrc().write_value(err_bits);\n        ErrorSource::from_bits_truncate(err_bits.0).check()\n    }\n\n    fn new_inner<T: Instance>(\n        _uarte: Peri<'d, T>,\n        rxd: Peri<'d, AnyPin>,\n        rts: Option<Peri<'d, AnyPin>>,\n        config: Config,\n    ) -> Self {\n        let r = T::regs();\n\n        configure(r, config, rts.is_some());\n        configure_rx_pins(r, rxd, rts);\n\n        T::Interrupt::unpend();\n        unsafe { T::Interrupt::enable() };\n        r.enable().write(|w| w.set_enable(vals::Enable::ENABLED));\n\n        let s = T::state();\n        s.tx_rx_refcount.store(1, Ordering::Relaxed);\n\n        Self {\n            r: T::regs(),\n            state: T::state(),\n            _p: PhantomData {},\n            rx_on: false,\n        }\n    }\n\n    /// Upgrade to an instance that supports idle line detection.\n    pub fn with_idle<U: TimerInstance>(\n        self,\n        timer: Peri<'d, U>,\n        ppi_ch1: Peri<'d, impl ConfigurableChannel + 'd>,\n        ppi_ch2: Peri<'d, impl ConfigurableChannel + 'd>,\n    ) -> UarteRxWithIdle<'d> {\n        let timer = Timer::new(timer);\n\n        let r = self.r;\n\n        // BAUDRATE register values are `baudrate * 2^32 / 16000000`\n        // source: https://devzone.nordicsemi.com/f/nordic-q-a/391/uart-baudrate-register-values\n        //\n        // We want to stop RX if line is idle for 2 bytes worth of time\n        // That is 20 bits (each byte is 1 start bit + 8 data bits + 1 stop bit)\n        // This gives us the amount of 16M ticks for 20 bits.\n        let baudrate = r.baudrate().read().baudrate();\n        let timeout = 0x8000_0000 / (baudrate.to_bits() / 40);\n\n        timer.set_frequency(Frequency::F16MHz);\n        timer.cc(0).write(timeout);\n        timer.cc(0).short_compare_clear();\n        timer.cc(0).short_compare_stop();\n\n        let mut ppi_ch1 = Ppi::new_one_to_two(\n            ppi_ch1.into(),\n            Event::from_reg(r.events_rxdrdy()),\n            timer.task_clear(),\n            timer.task_start(),\n        );\n        ppi_ch1.enable();\n\n        let mut ppi_ch2 = Ppi::new_one_to_one(\n            ppi_ch2.into(),\n            timer.cc(0).event_compare(),\n            Task::from_reg(r.tasks_dma().rx().stop()),\n        );\n        ppi_ch2.enable();\n\n        let state = self.state;\n\n        UarteRxWithIdle {\n            rx: self,\n            timer,\n            ppi_ch1: ppi_ch1,\n            _ppi_ch2: ppi_ch2,\n            r: r,\n            state: state,\n        }\n    }\n\n    /// Read bytes until the buffer is filled.\n    pub async fn read(&mut self, buffer: &mut [u8]) -> Result<(), Error> {\n        if buffer.is_empty() {\n            return Ok(());\n        }\n        if buffer.len() > EASY_DMA_SIZE {\n            return Err(Error::BufferTooLong);\n        }\n\n        let ptr = buffer.as_ptr();\n        let len = buffer.len();\n\n        let r = self.r;\n        let s = self.state;\n\n        let drop = OnDrop::new(move || {\n            trace!(\"read drop: stopping\");\n\n            r.intenclr().write(|w| {\n                w.set_dmarxend(true);\n                w.set_error(true);\n            });\n            r.events_error().write_value(0);\n            r.tasks_dma().rx().stop().write_value(1);\n\n            while r.events_dma().rx().end().read() == 0 {}\n\n            trace!(\"read drop: stopped\");\n        });\n\n        r.dma().rx().ptr().write_value(ptr as u32);\n        r.dma().rx().maxcnt().write(|w| w.set_maxcnt(len as _));\n\n        self.rx_on = true;\n        r.events_rxto().write_value(0);\n        r.events_dma().rx().end().write_value(0);\n        r.events_error().write_value(0);\n        r.intenset().write(|w| {\n            w.set_dmarxend(true);\n            w.set_error(true);\n        });\n\n        compiler_fence(Ordering::SeqCst);\n\n        trace!(\"startrx\");\n        r.tasks_dma().rx().start().write_value(1);\n\n        let result = poll_fn(|cx| {\n            s.rx_waker.register(cx.waker());\n\n            if let Err(e) = self.check_and_clear_errors() {\n                r.tasks_dma().rx().stop().write_value(1);\n                return Poll::Ready(Err(e));\n            }\n            if r.events_dma().rx().end().read() != 0 {\n                return Poll::Ready(Ok(()));\n            }\n            Poll::Pending\n        })\n        .await;\n\n        compiler_fence(Ordering::SeqCst);\n        r.events_dma().rx().ready().write_value(0);\n        drop.defuse();\n\n        result\n    }\n\n    /// Read bytes until the buffer is filled.\n    pub fn blocking_read(&mut self, buffer: &mut [u8]) -> Result<(), Error> {\n        if buffer.is_empty() {\n            return Ok(());\n        }\n        if buffer.len() > EASY_DMA_SIZE {\n            return Err(Error::BufferTooLong);\n        }\n\n        let ptr = buffer.as_ptr();\n        let len = buffer.len();\n\n        let r = self.r;\n\n        r.dma().rx().ptr().write_value(ptr as u32);\n        r.dma().rx().maxcnt().write(|w| w.set_maxcnt(len as _));\n\n        r.events_dma().rx().end().write_value(0);\n        r.events_error().write_value(0);\n        r.intenclr().write(|w| {\n            w.set_dmarxend(true);\n            w.set_error(true);\n        });\n\n        compiler_fence(Ordering::SeqCst);\n\n        trace!(\"startrx\");\n        r.tasks_dma().rx().start().write_value(1);\n\n        while r.events_dma().rx().end().read() == 0 && r.events_error().read() == 0 {}\n\n        compiler_fence(Ordering::SeqCst);\n        r.events_dma().rx().ready().write_value(0);\n\n        self.check_and_clear_errors()\n    }\n\n    /// Stop the receiver.\n    ///\n    /// This function waits for the receiver to stop (as indicated by a `RXTO` event).\n    ///\n    /// Note that the receiver may still receive up to 4 bytes while being stopped.\n    /// You can use [`Self::flush_rx()`] to remove the data from the internal RX FIFO\n    /// without re-activating the receiver.\n    pub async fn stop_rx(&mut self) {\n        let r = self.r;\n        let s = self.state;\n\n        // If the future is dropped, disable the RXTO interrupt.\n        let drop = OnDrop::new(move || {\n            r.intenclr().write(|w| {\n                w.set_rxto(true);\n            });\n        });\n\n        // Enable the interrupt.\n        // NOTE: Do not clear the RXTO bit: we use it as a status bit to see if the receiver is off.\n        // We only clear it when we start the receiver.\n        r.intenset().write(|w| {\n            w.set_rxto(true);\n        });\n\n        compiler_fence(Ordering::SeqCst);\n\n        // Trigger the STOPRX task.\n        trace!(\"stop_rx\");\n        r.tasks_dma().rx().stop().write_value(1);\n\n        // Wait for the RXTO bit.\n        poll_fn(|cx| {\n            s.rx_waker.register(cx.waker());\n\n            if !self.rx_on || r.events_rxto().read() != 0 {\n                return Poll::Ready(());\n            }\n            Poll::Pending\n        })\n        .await;\n\n        compiler_fence(Ordering::SeqCst);\n\n        // Clear the rx_on flag and disable the interrupt.\n        self.rx_on = false;\n        r.intenclr().write(|w| {\n            w.set_rxto(true);\n        });\n\n        drop.defuse();\n    }\n\n    /// Stop the receiver.\n    ///\n    /// This function waits for the receiver to stop (as indicated by a `RXTO` event).\n    ///\n    /// Note that the receiver may still receive up to 4 bytes while being stopped.\n    /// You can use [`Self::blocking_flush_rx()`] to remove the data from the internal RX FIFO\n    /// without re-activating the receiver.\n    pub fn blocking_stop_rx(&mut self) {\n        let r = self.r;\n\n        compiler_fence(Ordering::SeqCst);\n\n        // Trigger the STOPRX task.\n        // NOTE: Do not clear the RXTO bit: we use it as a status bit to see if the receiver is off.\n        // We only clear it when we start the receiver.\n        trace!(\"stop_rx\");\n        r.tasks_dma().rx().stop().write_value(1);\n\n        // Wait for the RXTO bit.\n        while self.rx_on && r.events_rxto().read() == 0 {}\n        self.rx_on = false;\n\n        compiler_fence(Ordering::SeqCst);\n\n        self.rx_on = false;\n    }\n\n    /// Flush the RX FIFO to RAM without activating the receiver.\n    pub async fn flush_rx(&mut self, buffer: &mut [u8]) -> Result<usize, Error> {\n        if buffer.is_empty() {\n            return Ok(0);\n        }\n        if buffer.len() > EASY_DMA_SIZE {\n            return Err(Error::BufferTooLong);\n        }\n\n        let ptr = buffer.as_ptr();\n        let len = buffer.len();\n\n        let r = self.r;\n        let s = self.state;\n\n        let drop = OnDrop::new(move || {\n            trace!(\"flush_rx drop: stopping\");\n\n            r.intenclr().write(|w| {\n                w.set_dmarxend(true);\n            });\n            r.tasks_dma().rx().stop().write_value(1);\n\n            while r.events_dma().rx().end().read() == 0 {}\n\n            trace!(\"flush_rx drop: stopped\");\n        });\n\n        r.dma().rx().ptr().write_value(ptr as u32);\n        r.dma().rx().maxcnt().write(|w| w.set_maxcnt(len as _));\n\n        r.events_dma().rx().end().write_value(0);\n        r.intenset().write(|w| {\n            w.set_dmarxend(true);\n        });\n\n        compiler_fence(Ordering::SeqCst);\n\n        trace!(\"flush_rx\");\n        r.tasks_flushrx().write_value(1);\n\n        poll_fn(|cx| {\n            s.rx_waker.register(cx.waker());\n\n            if r.events_dma().rx().end().read() != 0 {\n                return Poll::Ready(());\n            }\n            Poll::Pending\n        })\n        .await;\n\n        compiler_fence(Ordering::SeqCst);\n        r.events_dma().rx().ready().write_value(0);\n        let amount = r.dma().rx().amount().read().amount();\n        drop.defuse();\n\n        Ok(amount as usize)\n    }\n\n    /// Flush the RX FIFO to RAM without activating the receiver.\n    pub fn blocking_flush_rx(&mut self, buffer: &mut [u8]) -> Result<usize, Error> {\n        if buffer.is_empty() {\n            return Ok(0);\n        }\n        if buffer.len() > EASY_DMA_SIZE {\n            return Err(Error::BufferTooLong);\n        }\n\n        let ptr = buffer.as_ptr();\n        let len = buffer.len();\n\n        let r = self.r;\n\n        r.dma().rx().ptr().write_value(ptr as u32);\n        r.dma().rx().maxcnt().write(|w| w.set_maxcnt(len as _));\n\n        r.events_dma().rx().end().write_value(0);\n        r.intenclr().write(|w| {\n            w.set_dmarxend(true);\n        });\n\n        compiler_fence(Ordering::SeqCst);\n\n        trace!(\"flush_rx\");\n        r.tasks_flushrx().write_value(1);\n\n        while r.events_dma().rx().end().read() == 0 {}\n\n        compiler_fence(Ordering::SeqCst);\n        r.events_dma().rx().ready().write_value(0);\n\n        let amount = r.dma().rx().amount().read().amount();\n        Ok(amount as usize)\n    }\n}\n\nimpl<'a> Drop for UarteRx<'a> {\n    fn drop(&mut self) {\n        trace!(\"uarte rx drop\");\n\n        let r = self.r;\n\n        let did_stoprx = r.events_dma().rx().ready().read() != 0;\n        trace!(\"did_stoprx {}\", did_stoprx);\n\n        // Wait for rxto, if needed.\n        while did_stoprx && r.events_rxto().read() == 0 {}\n\n        let s = self.state;\n\n        drop_tx_rx(r, s);\n    }\n}\n\n/// Receiver part of the UARTE driver, with `read_until_idle` support.\n///\n/// This can be obtained via [`Uarte::split_with_idle`].\npub struct UarteRxWithIdle<'d> {\n    rx: UarteRx<'d>,\n    timer: Timer<'d>,\n    ppi_ch1: Ppi<'d, AnyConfigurableChannel, 1, 2>,\n    _ppi_ch2: Ppi<'d, AnyConfigurableChannel, 1, 1>,\n    r: pac::uarte::Uarte,\n    state: &'static State,\n}\n\nimpl<'d> UarteRxWithIdle<'d> {\n    /// Read bytes until the buffer is filled.\n    pub async fn read(&mut self, buffer: &mut [u8]) -> Result<(), Error> {\n        self.ppi_ch1.disable();\n        self.rx.read(buffer).await\n    }\n\n    /// Read bytes until the buffer is filled.\n    pub fn blocking_read(&mut self, buffer: &mut [u8]) -> Result<(), Error> {\n        self.ppi_ch1.disable();\n        self.rx.blocking_read(buffer)\n    }\n\n    /// Stop the receiver.\n    ///\n    /// This function waits for the receiver to stop (as indicated by a `RXTO` event).\n    ///\n    /// Note that the receiver may still receive up to 4 bytes while being stopped.\n    /// You can use [`Self::flush_rx()`] to remove the data from the internal RX FIFO\n    /// without re-activating the receiver.\n    pub async fn stop_rx(&mut self) {\n        self.rx.stop_rx().await\n    }\n\n    /// Stop the receiver.\n    ///\n    /// This function waits for the receiver to stop (as indicated by a `RXTO` event).\n    ///\n    /// Note that the receiver may still receive up to 4 bytes while being stopped.\n    /// You can use [`Self::blocking_flush_rx()`] to remove the data from the internal RX FIFO\n    /// without re-activating the receiver.\n    pub fn blocking_stop_rx(&mut self) {\n        self.rx.blocking_stop_rx()\n    }\n\n    /// Flush the RX FIFO to RAM without activating the receiver.\n    pub async fn flush_rx(&mut self, buffer: &mut [u8]) -> Result<usize, Error> {\n        self.rx.flush_rx(buffer).await\n    }\n\n    /// Flush the RX FIFO to RAM without activating the receiver.\n    pub fn blocking_flush_rx(&mut self, buffer: &mut [u8]) -> Result<usize, Error> {\n        self.rx.blocking_flush_rx(buffer)\n    }\n\n    /// Read bytes until the buffer is filled, or the line becomes idle.\n    ///\n    /// Returns the amount of bytes read.\n    pub async fn read_until_idle(&mut self, buffer: &mut [u8]) -> Result<usize, Error> {\n        if buffer.is_empty() {\n            return Ok(0);\n        }\n        if buffer.len() > EASY_DMA_SIZE {\n            return Err(Error::BufferTooLong);\n        }\n\n        let ptr = buffer.as_ptr();\n        let len = buffer.len();\n\n        let r = self.r;\n        let s = self.state;\n\n        self.ppi_ch1.enable();\n\n        let drop = OnDrop::new(|| {\n            self.timer.stop();\n\n            r.intenclr().write(|w| {\n                w.set_dmarxend(true);\n                w.set_error(true);\n            });\n            r.events_error().write_value(0);\n            r.tasks_dma().rx().stop().write_value(1);\n\n            while r.events_dma().rx().end().read() == 0 {}\n        });\n\n        r.dma().rx().ptr().write_value(ptr as u32);\n        r.dma().rx().maxcnt().write(|w| w.set_maxcnt(len as _));\n\n        self.rx.rx_on = true;\n        r.events_rxto().write_value(0);\n        r.events_dma().rx().end().write_value(0);\n        r.events_error().write_value(0);\n        r.intenset().write(|w| {\n            w.set_dmarxend(true);\n            w.set_error(true);\n        });\n\n        compiler_fence(Ordering::SeqCst);\n\n        r.tasks_dma().rx().start().write_value(1);\n\n        let result = poll_fn(|cx| {\n            s.rx_waker.register(cx.waker());\n\n            if let Err(e) = self.rx.check_and_clear_errors() {\n                r.tasks_dma().rx().stop().write_value(1);\n                return Poll::Ready(Err(e));\n            }\n            if r.events_dma().rx().end().read() != 0 {\n                return Poll::Ready(Ok(()));\n            }\n\n            Poll::Pending\n        })\n        .await;\n\n        compiler_fence(Ordering::SeqCst);\n        let n = r.dma().rx().amount().read().0 as usize;\n\n        self.timer.stop();\n        r.events_dma().rx().ready().write_value(0);\n\n        drop.defuse();\n\n        result.map(|_| n)\n    }\n\n    /// Read bytes until the buffer is filled, or the line becomes idle.\n    ///\n    /// Returns the amount of bytes read.\n    pub fn blocking_read_until_idle(&mut self, buffer: &mut [u8]) -> Result<usize, Error> {\n        if buffer.is_empty() {\n            return Ok(0);\n        }\n        if buffer.len() > EASY_DMA_SIZE {\n            return Err(Error::BufferTooLong);\n        }\n\n        let ptr = buffer.as_ptr();\n        let len = buffer.len();\n\n        let r = self.r;\n\n        self.ppi_ch1.enable();\n\n        r.dma().rx().ptr().write_value(ptr as u32);\n        r.dma().rx().maxcnt().write(|w| w.set_maxcnt(len as _));\n\n        self.rx.rx_on = true;\n        r.events_rxto().write_value(0);\n        r.events_dma().rx().end().write_value(0);\n        r.events_error().write_value(0);\n        r.intenclr().write(|w| {\n            w.set_dmarxend(true);\n            w.set_error(true);\n        });\n\n        compiler_fence(Ordering::SeqCst);\n\n        r.tasks_dma().rx().start().write_value(1);\n\n        while r.events_dma().rx().end().read() == 0 && r.events_error().read() == 0 {}\n\n        compiler_fence(Ordering::SeqCst);\n        let n = r.dma().rx().amount().read().0 as usize;\n\n        self.timer.stop();\n        r.events_dma().rx().ready().write_value(0);\n\n        self.rx.check_and_clear_errors().map(|_| n)\n    }\n}\n\n#[cfg(not(any(feature = \"_nrf9160\", feature = \"_nrf5340\")))]\npub(crate) fn apply_workaround_for_enable_anomaly(_r: pac::uarte::Uarte) {\n    // Do nothing\n}\n\n#[cfg(any(feature = \"_nrf9160\", feature = \"_nrf5340\"))]\npub(crate) fn apply_workaround_for_enable_anomaly(r: pac::uarte::Uarte) {\n    // Apply workaround for anomalies:\n    // - nRF9160 - anomaly 23\n    // - nRF5340 - anomaly 44\n    let rp = r.as_ptr() as *mut u32;\n    let rxenable_reg = unsafe { rp.add(0x564 / 4) };\n    let txenable_reg = unsafe { rp.add(0x568 / 4) };\n\n    // NB Safety: This is taken from Nordic's driver -\n    // https://github.com/NordicSemiconductor/nrfx/blob/master/drivers/src/nrfx_uarte.c#L197\n    if unsafe { core::ptr::read_volatile(txenable_reg) } == 1 {\n        r.tasks_dma().tx().stop().write_value(1);\n    }\n\n    // NB Safety: This is taken from Nordic's driver -\n    // https://github.com/NordicSemiconductor/nrfx/blob/master/drivers/src/nrfx_uarte.c#L197\n    if unsafe { core::ptr::read_volatile(rxenable_reg) } == 1 {\n        r.enable().write(|w| w.set_enable(vals::Enable::ENABLED));\n        r.tasks_dma().rx().stop().write_value(1);\n\n        let mut workaround_succeded = false;\n        // The UARTE is able to receive up to four bytes after the STOPRX task has been triggered.\n        // On lowest supported baud rate (1200 baud), with parity bit and two stop bits configured\n        // (resulting in 12 bits per data byte sent), this may take up to 40 ms.\n        for _ in 0..40000 {\n            // NB Safety: This is taken from Nordic's driver -\n            // https://github.com/NordicSemiconductor/nrfx/blob/master/drivers/src/nrfx_uarte.c#L197\n            if unsafe { core::ptr::read_volatile(rxenable_reg) } == 0 {\n                workaround_succeded = true;\n                break;\n            } else {\n                // Need to sleep for 1us here\n\n                // Get the worst case clock speed\n                #[cfg(feature = \"_nrf9160\")]\n                const CLOCK_SPEED: u32 = 64_000_000;\n                #[cfg(feature = \"_nrf5340\")]\n                const CLOCK_SPEED: u32 = 128_000_000;\n\n                cortex_m::asm::delay(CLOCK_SPEED / 1_000_000);\n            }\n        }\n\n        if !workaround_succeded {\n            panic!(\"Failed to apply workaround for UART\");\n        }\n\n        // write back the bits we just read to clear them\n        let errors = r.errorsrc().read();\n        r.errorsrc().write_value(errors);\n        r.enable().write(|w| w.set_enable(vals::Enable::DISABLED));\n    }\n}\n\npub(crate) fn drop_tx_rx(r: pac::uarte::Uarte, s: &State) {\n    if s.tx_rx_refcount.fetch_sub(1, Ordering::Relaxed) == 1 {\n        // Finally we can disable, and we do so for the peripheral\n        // i.e. not just rx concerns.\n        r.enable().write(|w| w.set_enable(vals::Enable::DISABLED));\n\n        gpio::deconfigure_pin(r.psel().rxd().read());\n        gpio::deconfigure_pin(r.psel().txd().read());\n        gpio::deconfigure_pin(r.psel().rts().read());\n        gpio::deconfigure_pin(r.psel().cts().read());\n\n        trace!(\"uarte tx and rx drop: done\");\n    }\n}\n\npub(crate) struct State {\n    pub(crate) rx_waker: AtomicWaker,\n    pub(crate) tx_waker: AtomicWaker,\n    pub(crate) tx_rx_refcount: AtomicU8,\n}\nimpl State {\n    pub(crate) const fn new() -> Self {\n        Self {\n            rx_waker: AtomicWaker::new(),\n            tx_waker: AtomicWaker::new(),\n            tx_rx_refcount: AtomicU8::new(0),\n        }\n    }\n}\n\npub(crate) trait SealedInstance {\n    fn regs() -> pac::uarte::Uarte;\n    fn state() -> &'static State;\n    fn buffered_state() -> &'static crate::buffered_uarte::State;\n}\n\n/// UARTE peripheral instance.\n#[allow(private_bounds)]\npub trait Instance: SealedInstance + PeripheralType + 'static + Send {\n    /// Interrupt for this peripheral.\n    type Interrupt: interrupt::typelevel::Interrupt;\n}\n\nmacro_rules! impl_uarte {\n    ($type:ident, $pac_type:ident, $irq:ident) => {\n        impl crate::uarte::SealedInstance for peripherals::$type {\n            fn regs() -> pac::uarte::Uarte {\n                pac::$pac_type\n            }\n            fn state() -> &'static crate::uarte::State {\n                static STATE: crate::uarte::State = crate::uarte::State::new();\n                &STATE\n            }\n            fn buffered_state() -> &'static crate::buffered_uarte::State {\n                static STATE: crate::buffered_uarte::State = crate::buffered_uarte::State::new();\n                &STATE\n            }\n        }\n        impl crate::uarte::Instance for peripherals::$type {\n            type Interrupt = crate::interrupt::typelevel::$irq;\n        }\n    };\n}\n\n// ====================\n\nmod eh02 {\n    use super::*;\n\n    impl<'d> embedded_hal_02::blocking::serial::Write<u8> for Uarte<'d> {\n        type Error = Error;\n\n        fn bwrite_all(&mut self, buffer: &[u8]) -> Result<(), Self::Error> {\n            self.blocking_write(buffer)\n        }\n\n        fn bflush(&mut self) -> Result<(), Self::Error> {\n            Ok(())\n        }\n    }\n\n    impl<'d> embedded_hal_02::blocking::serial::Write<u8> for UarteTx<'d> {\n        type Error = Error;\n\n        fn bwrite_all(&mut self, buffer: &[u8]) -> Result<(), Self::Error> {\n            self.blocking_write(buffer)\n        }\n\n        fn bflush(&mut self) -> Result<(), Self::Error> {\n            Ok(())\n        }\n    }\n}\n\nimpl core::fmt::Display for Error {\n    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {\n        match *self {\n            Self::BufferTooLong => f.write_str(\"BufferTooLong\"),\n            Self::BufferNotInRAM => f.write_str(\"BufferNotInRAM\"),\n            Self::Framing => f.write_str(\"Framing\"),\n            Self::Parity => f.write_str(\"Parity\"),\n            Self::Overrun => f.write_str(\"Overrun\"),\n            Self::Break => f.write_str(\"Break\"),\n        }\n    }\n}\nimpl core::error::Error for Error {}\n\nmod _embedded_io {\n    use super::*;\n\n    impl embedded_io_async::Error for Error {\n        fn kind(&self) -> embedded_io_async::ErrorKind {\n            match *self {\n                Error::BufferTooLong => embedded_io_async::ErrorKind::InvalidInput,\n                Error::BufferNotInRAM => embedded_io_async::ErrorKind::Unsupported,\n                Error::Framing => embedded_io_async::ErrorKind::InvalidData,\n                Error::Parity => embedded_io_async::ErrorKind::InvalidData,\n                Error::Overrun => embedded_io_async::ErrorKind::OutOfMemory,\n                Error::Break => embedded_io_async::ErrorKind::ConnectionAborted,\n            }\n        }\n    }\n\n    impl<'d> embedded_io_async::ErrorType for Uarte<'d> {\n        type Error = Error;\n    }\n\n    impl<'d> embedded_io_async::ErrorType for UarteTx<'d> {\n        type Error = Error;\n    }\n\n    impl<'d> embedded_io_async::Write for Uarte<'d> {\n        async fn write(&mut self, buf: &[u8]) -> Result<usize, Self::Error> {\n            self.write(buf).await?;\n            Ok(buf.len())\n        }\n        async fn flush(&mut self) -> Result<(), Self::Error> {\n            Ok(())\n        }\n    }\n\n    impl<'d> embedded_io_async::Write for UarteTx<'d> {\n        async fn write(&mut self, buf: &[u8]) -> Result<usize, Self::Error> {\n            self.write(buf).await?;\n            Ok(buf.len())\n        }\n        async fn flush(&mut self) -> Result<(), Self::Error> {\n            Ok(())\n        }\n    }\n}\n"
  },
  {
    "path": "embassy-nrf/src/usb/mod.rs",
    "content": "//! Universal Serial Bus (USB) driver.\n\n#![macro_use]\n\npub mod vbus_detect;\n\nuse core::future::{Future, poll_fn};\nuse core::marker::PhantomData;\nuse core::mem::MaybeUninit;\nuse core::sync::atomic::{AtomicU32, Ordering, compiler_fence};\nuse core::task::Poll;\n\nuse cortex_m::peripheral::NVIC;\nuse embassy_hal_internal::{Peri, PeripheralType};\nuse embassy_sync::waitqueue::AtomicWaker;\nuse embassy_usb_driver as driver;\nuse embassy_usb_driver::{Direction, EndpointAddress, EndpointError, EndpointInfo, EndpointType, Event, Unsupported};\n\nuse self::vbus_detect::VbusDetect;\nuse crate::interrupt::typelevel::Interrupt;\nuse crate::pac::usbd::vals;\nuse crate::util::slice_in_ram;\nuse crate::{interrupt, pac};\n\nstatic BUS_WAKER: AtomicWaker = AtomicWaker::new();\nstatic EP0_WAKER: AtomicWaker = AtomicWaker::new();\nstatic EP_IN_WAKERS: [AtomicWaker; 8] = [const { AtomicWaker::new() }; 8];\nstatic EP_OUT_WAKERS: [AtomicWaker; 8] = [const { AtomicWaker::new() }; 8];\nstatic READY_ENDPOINTS: AtomicU32 = AtomicU32::new(0);\n\n/// Interrupt handler.\npub struct InterruptHandler<T: Instance> {\n    _phantom: PhantomData<T>,\n}\n\nimpl<T: Instance> interrupt::typelevel::Handler<T::Interrupt> for InterruptHandler<T> {\n    unsafe fn on_interrupt() {\n        let regs = T::regs();\n\n        if regs.events_usbreset().read() != 0 {\n            regs.intenclr().write(|w| w.set_usbreset(true));\n            BUS_WAKER.wake();\n            EP0_WAKER.wake();\n        }\n\n        if regs.events_ep0setup().read() != 0 {\n            regs.intenclr().write(|w| w.set_ep0setup(true));\n            EP0_WAKER.wake();\n        }\n\n        if regs.events_ep0datadone().read() != 0 {\n            regs.intenclr().write(|w| w.set_ep0datadone(true));\n            EP0_WAKER.wake();\n        }\n\n        // USBEVENT and EPDATA events are weird. They're the \"aggregate\"\n        // of individual bits in EVENTCAUSE and EPDATASTATUS. We handle them\n        // differently than events normally.\n        //\n        // They seem to be edge-triggered, not level-triggered: when an\n        // individual bit goes 0->1, the event fires *just once*.\n        // Therefore, it's fine to clear just the event, and let main thread\n        // check the individual bits in EVENTCAUSE and EPDATASTATUS. It\n        // doesn't cause an infinite irq loop.\n        if regs.events_usbevent().read() != 0 {\n            regs.events_usbevent().write_value(0);\n            BUS_WAKER.wake();\n        }\n\n        if regs.events_epdata().read() != 0 {\n            regs.events_epdata().write_value(0);\n\n            let r = regs.epdatastatus().read();\n            regs.epdatastatus().write_value(r);\n            READY_ENDPOINTS.fetch_or(r.0, Ordering::AcqRel);\n            for i in 1..=7 {\n                if r.0 & In::mask(i) != 0 {\n                    In::waker(i).wake();\n                }\n                if r.0 & Out::mask(i) != 0 {\n                    Out::waker(i).wake();\n                }\n            }\n        }\n    }\n}\n\n/// USB driver.\npub struct Driver<'d, V: VbusDetect> {\n    regs: pac::usbd::Usbd,\n    alloc_in: Allocator,\n    alloc_out: Allocator,\n    vbus_detect: V,\n    _phantom: PhantomData<&'d ()>,\n}\n\nimpl<'d, V: VbusDetect> Driver<'d, V> {\n    /// Create a new USB driver.\n    pub fn new<T: Instance>(\n        _usb: Peri<'d, T>,\n        _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd,\n        vbus_detect: V,\n    ) -> Self {\n        T::Interrupt::unpend();\n        unsafe { T::Interrupt::enable() };\n\n        Self {\n            regs: crate::pac::USBD,\n            alloc_in: Allocator::new(),\n            alloc_out: Allocator::new(),\n            vbus_detect,\n            _phantom: PhantomData,\n        }\n    }\n}\n\nimpl<'d, V: VbusDetect + 'd> driver::Driver<'d> for Driver<'d, V> {\n    type EndpointOut = Endpoint<'d, Out>;\n    type EndpointIn = Endpoint<'d, In>;\n    type ControlPipe = ControlPipe<'d>;\n    type Bus = Bus<'d, V>;\n\n    fn alloc_endpoint_in(\n        &mut self,\n        ep_type: EndpointType,\n        ep_addr: Option<EndpointAddress>,\n        packet_size: u16,\n        interval_ms: u8,\n    ) -> Result<Self::EndpointIn, driver::EndpointAllocError> {\n        let index = self.alloc_in.allocate(ep_type, ep_addr)?;\n        let ep_addr = EndpointAddress::from_parts(index, Direction::In);\n        Ok(Endpoint::new(\n            self.regs,\n            EndpointInfo {\n                addr: ep_addr,\n                ep_type,\n                max_packet_size: packet_size,\n                interval_ms,\n            },\n        ))\n    }\n\n    fn alloc_endpoint_out(\n        &mut self,\n        ep_type: EndpointType,\n        ep_addr: Option<EndpointAddress>,\n        packet_size: u16,\n        interval_ms: u8,\n    ) -> Result<Self::EndpointOut, driver::EndpointAllocError> {\n        let index = self.alloc_out.allocate(ep_type, ep_addr)?;\n        let ep_addr = EndpointAddress::from_parts(index, Direction::Out);\n        Ok(Endpoint::new(\n            self.regs,\n            EndpointInfo {\n                addr: ep_addr,\n                ep_type,\n                max_packet_size: packet_size,\n                interval_ms,\n            },\n        ))\n    }\n\n    fn start(self, control_max_packet_size: u16) -> (Self::Bus, Self::ControlPipe) {\n        (\n            Bus {\n                regs: self.regs,\n                power_available: false,\n                vbus_detect: self.vbus_detect,\n                _phantom: PhantomData,\n            },\n            ControlPipe {\n                regs: self.regs,\n                max_packet_size: control_max_packet_size,\n                _phantom: PhantomData,\n            },\n        )\n    }\n}\n\n/// USB bus.\npub struct Bus<'d, V: VbusDetect> {\n    regs: pac::usbd::Usbd,\n    power_available: bool,\n    vbus_detect: V,\n    _phantom: PhantomData<&'d ()>,\n}\n\nimpl<'d, V: VbusDetect> driver::Bus for Bus<'d, V> {\n    async fn enable(&mut self) {\n        let regs = self.regs;\n\n        errata::pre_enable();\n\n        regs.enable().write(|w| w.set_enable(true));\n\n        // Wait until the peripheral is ready.\n        regs.intenset().write(|w| w.set_usbevent(true));\n        poll_fn(|cx| {\n            BUS_WAKER.register(cx.waker());\n            if regs.eventcause().read().ready() {\n                Poll::Ready(())\n            } else {\n                Poll::Pending\n            }\n        })\n        .await;\n        regs.eventcause().write(|w| w.set_ready(true));\n\n        errata::post_enable();\n\n        unsafe { NVIC::unmask(pac::Interrupt::USBD) };\n\n        regs.intenset().write(|w| {\n            w.set_usbreset(true);\n            w.set_usbevent(true);\n            w.set_epdata(true);\n        });\n\n        if self.vbus_detect.wait_power_ready().await.is_ok() {\n            // Enable the USB pullup, allowing enumeration.\n            regs.usbpullup().write(|w| w.set_connect(true));\n            trace!(\"enabled\");\n        } else {\n            trace!(\"usb power not ready due to usb removal\");\n        }\n    }\n\n    async fn disable(&mut self) {\n        let regs = self.regs;\n        regs.enable().write(|x| x.set_enable(false));\n    }\n\n    fn poll(&mut self) -> impl Future<Output = Event> {\n        poll_fn(|cx| {\n            BUS_WAKER.register(cx.waker());\n            let regs = self.regs;\n\n            if regs.events_usbreset().read() != 0 {\n                regs.events_usbreset().write_value(0);\n                regs.intenset().write(|w| w.set_usbreset(true));\n\n                // Disable all endpoints except EP0\n                regs.epinen().write(|w| w.0 = 0x01);\n                regs.epouten().write(|w| w.0 = 0x01);\n                READY_ENDPOINTS.store(In::mask(0), Ordering::Release);\n                for i in 1..=7 {\n                    In::waker(i).wake();\n                    Out::waker(i).wake();\n                }\n\n                return Poll::Ready(Event::Reset);\n            }\n\n            let r = regs.eventcause().read();\n\n            if r.isooutcrc() {\n                regs.eventcause().write(|w| w.set_isooutcrc(true));\n                trace!(\"USB event: isooutcrc\");\n            }\n            if r.usbwuallowed() {\n                regs.eventcause().write(|w| w.set_usbwuallowed(true));\n                trace!(\"USB event: usbwuallowed\");\n            }\n            if r.suspend() {\n                regs.eventcause().write(|w| w.set_suspend(true));\n                regs.lowpower().write(|w| w.set_lowpower(vals::Lowpower::LOW_POWER));\n                return Poll::Ready(Event::Suspend);\n            }\n            if r.resume() {\n                regs.eventcause().write(|w| w.set_resume(true));\n                return Poll::Ready(Event::Resume);\n            }\n            if r.ready() {\n                regs.eventcause().write(|w| w.set_ready(true));\n                trace!(\"USB event: ready\");\n            }\n\n            if self.vbus_detect.is_usb_detected() != self.power_available {\n                self.power_available = !self.power_available;\n                if self.power_available {\n                    trace!(\"Power event: available\");\n                    return Poll::Ready(Event::PowerDetected);\n                } else {\n                    trace!(\"Power event: removed\");\n                    return Poll::Ready(Event::PowerRemoved);\n                }\n            }\n\n            Poll::Pending\n        })\n    }\n\n    fn endpoint_set_stalled(&mut self, ep_addr: EndpointAddress, stalled: bool) {\n        let regs = self.regs;\n        if ep_addr.index() == 0 {\n            if stalled {\n                regs.tasks_ep0stall().write_value(1);\n            }\n        } else {\n            regs.epstall().write(|w| {\n                w.set_ep(ep_addr.index() as u8 & 0b111);\n                w.set_io(match ep_addr.direction() {\n                    Direction::In => vals::Io::IN,\n                    Direction::Out => vals::Io::OUT,\n                });\n                w.set_stall(stalled);\n            });\n        }\n    }\n\n    fn endpoint_is_stalled(&mut self, ep_addr: EndpointAddress) -> bool {\n        let regs = self.regs;\n        let i = ep_addr.index();\n        match ep_addr.direction() {\n            Direction::Out => regs.halted().epout(i).read().getstatus() == vals::Getstatus::HALTED,\n            Direction::In => regs.halted().epin(i).read().getstatus() == vals::Getstatus::HALTED,\n        }\n    }\n\n    fn endpoint_set_enabled(&mut self, ep_addr: EndpointAddress, enabled: bool) {\n        let regs = self.regs;\n\n        let i = ep_addr.index();\n        let mask = 1 << i;\n\n        debug!(\"endpoint_set_enabled {:?} {}\", ep_addr, enabled);\n\n        match ep_addr.direction() {\n            Direction::In => {\n                let mut was_enabled = false;\n                regs.epinen().modify(|w| {\n                    was_enabled = (w.0 & mask) != 0;\n                    if enabled { w.0 |= mask } else { w.0 &= !mask }\n                });\n\n                let ready_mask = In::mask(i);\n                if enabled {\n                    if !was_enabled {\n                        READY_ENDPOINTS.fetch_or(ready_mask, Ordering::AcqRel);\n                    }\n                } else {\n                    READY_ENDPOINTS.fetch_and(!ready_mask, Ordering::AcqRel);\n                }\n\n                In::waker(i).wake();\n            }\n            Direction::Out => {\n                regs.epouten()\n                    .modify(|w| if enabled { w.0 |= mask } else { w.0 &= !mask });\n\n                let ready_mask = Out::mask(i);\n                if enabled {\n                    // when first enabled, bulk/interrupt OUT endpoints will *not* receive data (the\n                    // peripheral will NAK all incoming packets) until we write a zero to the SIZE\n                    // register (see figure 203 of the 52840 manual). To avoid that we write a 0 to the\n                    // SIZE register\n                    regs.size().epout(i).write(|_| ());\n                } else {\n                    READY_ENDPOINTS.fetch_and(!ready_mask, Ordering::AcqRel);\n                }\n\n                Out::waker(i).wake();\n            }\n        }\n    }\n\n    #[inline]\n    async fn remote_wakeup(&mut self) -> Result<(), Unsupported> {\n        let regs = self.regs;\n\n        if regs.lowpower().read().lowpower() == vals::Lowpower::LOW_POWER {\n            errata::pre_wakeup();\n\n            regs.lowpower().write(|w| w.set_lowpower(vals::Lowpower::FORCE_NORMAL));\n\n            poll_fn(|cx| {\n                BUS_WAKER.register(cx.waker());\n                let regs = self.regs;\n                let r = regs.eventcause().read();\n\n                if regs.events_usbreset().read() != 0 {\n                    Poll::Ready(())\n                } else if r.resume() {\n                    Poll::Ready(())\n                } else if r.usbwuallowed() {\n                    regs.eventcause().write(|w| w.set_usbwuallowed(true));\n                    regs.dpdmvalue().write(|w| w.set_state(vals::State::RESUME));\n                    regs.tasks_dpdmdrive().write_value(1);\n\n                    Poll::Ready(())\n                } else {\n                    Poll::Pending\n                }\n            })\n            .await;\n\n            errata::post_wakeup();\n        }\n\n        Ok(())\n    }\n}\n\n/// Type-level marker for OUT endpoints.\npub enum Out {}\n\n/// Type-level marker for IN endpoints.\npub enum In {}\n\ntrait EndpointDir {\n    fn waker(i: usize) -> &'static AtomicWaker;\n    fn mask(i: usize) -> u32;\n    fn is_enabled(regs: pac::usbd::Usbd, i: usize) -> bool;\n}\n\nimpl EndpointDir for In {\n    #[inline]\n    fn waker(i: usize) -> &'static AtomicWaker {\n        &EP_IN_WAKERS[i - 1]\n    }\n\n    #[inline]\n    fn mask(i: usize) -> u32 {\n        1 << i\n    }\n\n    #[inline]\n    fn is_enabled(regs: pac::usbd::Usbd, i: usize) -> bool {\n        regs.epinen().read().in_(i)\n    }\n}\n\nimpl EndpointDir for Out {\n    #[inline]\n    fn waker(i: usize) -> &'static AtomicWaker {\n        &EP_OUT_WAKERS[i - 1]\n    }\n\n    #[inline]\n    fn mask(i: usize) -> u32 {\n        1 << (i + 16)\n    }\n\n    #[inline]\n    fn is_enabled(regs: pac::usbd::Usbd, i: usize) -> bool {\n        regs.epouten().read().out(i)\n    }\n}\n\n/// USB endpoint.\npub struct Endpoint<'d, Dir> {\n    regs: pac::usbd::Usbd,\n    info: EndpointInfo,\n    _phantom: PhantomData<(&'d (), Dir)>,\n}\n\nimpl<'d, Dir> Endpoint<'d, Dir> {\n    fn new(regs: pac::usbd::Usbd, info: EndpointInfo) -> Self {\n        Self {\n            regs,\n            info,\n            _phantom: PhantomData,\n        }\n    }\n}\n\nimpl<'d, Dir: EndpointDir> driver::Endpoint for Endpoint<'d, Dir> {\n    fn info(&self) -> &EndpointInfo {\n        &self.info\n    }\n\n    async fn wait_enabled(&mut self) {\n        self.wait_enabled_state(true).await\n    }\n}\n\n#[allow(private_bounds)]\nimpl<'d, Dir: EndpointDir> Endpoint<'d, Dir> {\n    fn wait_enabled_state(&mut self, state: bool) -> impl Future<Output = ()> + use<'_, 'd, Dir> {\n        let i = self.info.addr.index();\n        assert!(i != 0);\n\n        poll_fn(move |cx| {\n            Dir::waker(i).register(cx.waker());\n            if Dir::is_enabled(self.regs, i) == state {\n                Poll::Ready(())\n            } else {\n                Poll::Pending\n            }\n        })\n    }\n\n    /// Wait for the endpoint to be disabled\n    pub fn wait_disabled(&mut self) -> impl Future<Output = ()> + use<'_, 'd, Dir> {\n        self.wait_enabled_state(false)\n    }\n}\n\nimpl<'d, Dir> Endpoint<'d, Dir> {\n    async fn wait_data_ready(&mut self) -> Result<(), ()>\n    where\n        Dir: EndpointDir,\n    {\n        let i = self.info.addr.index();\n        assert!(i != 0);\n        poll_fn(|cx| {\n            Dir::waker(i).register(cx.waker());\n            let r = READY_ENDPOINTS.load(Ordering::Acquire);\n            if !Dir::is_enabled(self.regs, i) {\n                Poll::Ready(Err(()))\n            } else if r & Dir::mask(i) != 0 {\n                Poll::Ready(Ok(()))\n            } else {\n                Poll::Pending\n            }\n        })\n        .await?;\n\n        // Mark as not ready\n        READY_ENDPOINTS.fetch_and(!Dir::mask(i), Ordering::AcqRel);\n\n        Ok(())\n    }\n}\n\nunsafe fn read_dma(regs: pac::usbd::Usbd, i: usize, buf: &mut [u8]) -> Result<usize, EndpointError> {\n    // Check that the packet fits into the buffer\n    let size = regs.size().epout(i).read().0 as usize;\n    if size > buf.len() {\n        return Err(EndpointError::BufferOverflow);\n    }\n\n    regs.epout(i).ptr().write_value(buf.as_ptr() as u32);\n    // MAXCNT must match SIZE\n    regs.epout(i).maxcnt().write(|w| w.set_maxcnt(size as _));\n\n    dma_start();\n    regs.events_endepout(i).write_value(0);\n    regs.tasks_startepout(i).write_value(1);\n    while regs.events_endepout(i).read() == 0 {}\n    regs.events_endepout(i).write_value(0);\n    dma_end();\n\n    regs.size().epout(i).write(|_| ());\n\n    Ok(size)\n}\n\nunsafe fn write_dma(regs: pac::usbd::Usbd, i: usize, buf: &[u8]) {\n    assert!(buf.len() <= 64);\n\n    let mut ram_buf: MaybeUninit<[u8; 64]> = MaybeUninit::uninit();\n    let ptr = if !slice_in_ram(buf) {\n        // EasyDMA can't read FLASH, so we copy through RAM\n        let ptr = ram_buf.as_mut_ptr() as *mut u8;\n        core::ptr::copy_nonoverlapping(buf.as_ptr(), ptr, buf.len());\n        ptr\n    } else {\n        buf.as_ptr()\n    };\n\n    // Set the buffer length so the right number of bytes are transmitted.\n    // Safety: `buf.len()` has been checked to be <= the max buffer length.\n    regs.epin(i).ptr().write_value(ptr as u32);\n    regs.epin(i).maxcnt().write(|w| w.set_maxcnt(buf.len() as u8));\n\n    regs.events_endepin(i).write_value(0);\n\n    dma_start();\n    regs.tasks_startepin(i).write_value(1);\n    while regs.events_endepin(i).read() == 0 {}\n    dma_end();\n}\n\nimpl<'d> driver::EndpointOut for Endpoint<'d, Out> {\n    async fn read(&mut self, buf: &mut [u8]) -> Result<usize, EndpointError> {\n        let i = self.info.addr.index();\n        assert!(i != 0);\n\n        self.wait_data_ready().await.map_err(|_| EndpointError::Disabled)?;\n\n        unsafe { read_dma(self.regs, i, buf) }\n    }\n}\n\nimpl<'d> driver::EndpointIn for Endpoint<'d, In> {\n    async fn write(&mut self, buf: &[u8]) -> Result<(), EndpointError> {\n        let i = self.info.addr.index();\n        assert!(i != 0);\n\n        self.wait_data_ready().await.map_err(|_| EndpointError::Disabled)?;\n\n        unsafe { write_dma(self.regs, i, buf) }\n\n        Ok(())\n    }\n}\n\n/// USB control pipe.\npub struct ControlPipe<'d> {\n    regs: pac::usbd::Usbd,\n    max_packet_size: u16,\n    _phantom: PhantomData<&'d ()>,\n}\n\nimpl<'d> driver::ControlPipe for ControlPipe<'d> {\n    fn max_packet_size(&self) -> usize {\n        usize::from(self.max_packet_size)\n    }\n\n    async fn setup(&mut self) -> [u8; 8] {\n        let regs = self.regs;\n\n        // Reset shorts\n        regs.shorts().write(|_| ());\n\n        // Wait for SETUP packet\n        regs.intenset().write(|w| w.set_ep0setup(true));\n        poll_fn(|cx| {\n            EP0_WAKER.register(cx.waker());\n            let regs = self.regs;\n            if regs.events_ep0setup().read() != 0 {\n                Poll::Ready(())\n            } else {\n                Poll::Pending\n            }\n        })\n        .await;\n\n        regs.events_ep0setup().write_value(0);\n\n        let mut buf = [0; 8];\n        buf[0] = regs.bmrequesttype().read().0 as u8;\n        buf[1] = regs.brequest().read().0 as u8;\n        buf[2] = regs.wvaluel().read().0 as u8;\n        buf[3] = regs.wvalueh().read().0 as u8;\n        buf[4] = regs.windexl().read().0 as u8;\n        buf[5] = regs.windexh().read().0 as u8;\n        buf[6] = regs.wlengthl().read().0 as u8;\n        buf[7] = regs.wlengthh().read().0 as u8;\n\n        buf\n    }\n\n    async fn data_out(&mut self, buf: &mut [u8], _first: bool, _last: bool) -> Result<usize, EndpointError> {\n        let regs = self.regs;\n\n        regs.events_ep0datadone().write_value(0);\n\n        // This starts a RX on EP0. events_ep0datadone notifies when done.\n        regs.tasks_ep0rcvout().write_value(1);\n\n        // Wait until ready\n        regs.intenset().write(|w| {\n            w.set_usbreset(true);\n            w.set_ep0setup(true);\n            w.set_ep0datadone(true);\n        });\n        poll_fn(|cx| {\n            EP0_WAKER.register(cx.waker());\n            let regs = self.regs;\n            if regs.events_ep0datadone().read() != 0 {\n                Poll::Ready(Ok(()))\n            } else if regs.events_usbreset().read() != 0 {\n                trace!(\"aborted control data_out: usb reset\");\n                Poll::Ready(Err(EndpointError::Disabled))\n            } else if regs.events_ep0setup().read() != 0 {\n                trace!(\"aborted control data_out: received another SETUP\");\n                Poll::Ready(Err(EndpointError::Disabled))\n            } else {\n                Poll::Pending\n            }\n        })\n        .await?;\n\n        unsafe { read_dma(self.regs, 0, buf) }\n    }\n\n    async fn data_in(&mut self, buf: &[u8], _first: bool, last: bool) -> Result<(), EndpointError> {\n        let regs = self.regs;\n        regs.events_ep0datadone().write_value(0);\n\n        regs.shorts().write(|w| w.set_ep0datadone_ep0status(last));\n\n        // This starts a TX on EP0. events_ep0datadone notifies when done.\n        unsafe { write_dma(self.regs, 0, buf) }\n\n        regs.intenset().write(|w| {\n            w.set_usbreset(true);\n            w.set_ep0setup(true);\n            w.set_ep0datadone(true);\n        });\n\n        poll_fn(|cx| {\n            cx.waker().wake_by_ref();\n            EP0_WAKER.register(cx.waker());\n            let regs = self.regs;\n            if regs.events_ep0datadone().read() != 0 {\n                Poll::Ready(Ok(()))\n            } else if regs.events_usbreset().read() != 0 {\n                trace!(\"aborted control data_in: usb reset\");\n                Poll::Ready(Err(EndpointError::Disabled))\n            } else if regs.events_ep0setup().read() != 0 {\n                trace!(\"aborted control data_in: received another SETUP\");\n                Poll::Ready(Err(EndpointError::Disabled))\n            } else {\n                Poll::Pending\n            }\n        })\n        .await\n    }\n\n    async fn accept(&mut self) {\n        let regs = self.regs;\n        regs.tasks_ep0status().write_value(1);\n    }\n\n    async fn reject(&mut self) {\n        let regs = self.regs;\n        regs.tasks_ep0stall().write_value(1);\n    }\n\n    async fn accept_set_address(&mut self, _addr: u8) {\n        self.accept().await;\n        // Nothing to do, the peripheral handles this.\n    }\n}\n\nfn dma_start() {\n    compiler_fence(Ordering::Release);\n}\n\nfn dma_end() {\n    compiler_fence(Ordering::Acquire);\n}\n\nstruct Allocator {\n    used: u16,\n}\n\nimpl Allocator {\n    fn new() -> Self {\n        Self { used: 0 }\n    }\n\n    fn allocate(\n        &mut self,\n        ep_type: EndpointType,\n        ep_addr: Option<EndpointAddress>,\n    ) -> Result<usize, driver::EndpointAllocError> {\n        // Endpoint addresses are fixed in hardware:\n        // - 0x80 / 0x00 - Control        EP0\n        // - 0x81 / 0x01 - Bulk/Interrupt EP1\n        // - 0x82 / 0x02 - Bulk/Interrupt EP2\n        // - 0x83 / 0x03 - Bulk/Interrupt EP3\n        // - 0x84 / 0x04 - Bulk/Interrupt EP4\n        // - 0x85 / 0x05 - Bulk/Interrupt EP5\n        // - 0x86 / 0x06 - Bulk/Interrupt EP6\n        // - 0x87 / 0x07 - Bulk/Interrupt EP7\n        // - 0x88 / 0x08 - Isochronous\n\n        // Endpoint directions are allocated individually.\n\n        let alloc_index = if let Some(addr) = ep_addr {\n            // Use the specified endpoint address\n            let requested_index = addr.index();\n            // Validate the requested index based on endpoint type\n            match ep_type {\n                EndpointType::Isochronous => {\n                    if requested_index != 8 {\n                        return Err(driver::EndpointAllocError);\n                    }\n                }\n                EndpointType::Control => return Err(driver::EndpointAllocError),\n                EndpointType::Interrupt | EndpointType::Bulk => {\n                    if requested_index < 1 || requested_index > 7 {\n                        return Err(driver::EndpointAllocError);\n                    }\n                }\n            }\n            requested_index\n        } else {\n            // Allocate any available endpoint\n            match ep_type {\n                EndpointType::Isochronous => 8,\n                EndpointType::Control => return Err(driver::EndpointAllocError),\n                EndpointType::Interrupt | EndpointType::Bulk => {\n                    // Find rightmost zero bit in 1..=7\n                    let ones = (self.used >> 1).trailing_ones() as usize;\n                    if ones >= 7 {\n                        return Err(driver::EndpointAllocError);\n                    }\n                    ones + 1\n                }\n            }\n        };\n\n        if self.used & (1 << alloc_index) != 0 {\n            return Err(driver::EndpointAllocError);\n        }\n\n        self.used |= 1 << alloc_index;\n\n        Ok(alloc_index)\n    }\n}\n\npub(crate) trait SealedInstance {\n    fn regs() -> pac::usbd::Usbd;\n}\n\n/// USB peripheral instance.\n#[allow(private_bounds)]\npub trait Instance: SealedInstance + PeripheralType + 'static + Send {\n    /// Interrupt for this peripheral.\n    type Interrupt: interrupt::typelevel::Interrupt;\n}\n\nmacro_rules! impl_usb {\n    ($type:ident, $pac_type:ident, $irq:ident) => {\n        impl crate::usb::SealedInstance for peripherals::$type {\n            fn regs() -> pac::usbd::Usbd {\n                pac::$pac_type\n            }\n        }\n        impl crate::usb::Instance for peripherals::$type {\n            type Interrupt = crate::interrupt::typelevel::$irq;\n        }\n    };\n}\n\nmod errata {\n\n    /// Writes `val` to `addr`. Used to apply Errata workarounds.\n    #[cfg(any(feature = \"nrf52840\", feature = \"nrf52833\", feature = \"nrf52820\"))]\n    unsafe fn poke(addr: u32, val: u32) {\n        (addr as *mut u32).write_volatile(val);\n    }\n\n    /// Reads 32 bits from `addr`.\n    #[cfg(feature = \"nrf52840\")]\n    unsafe fn peek(addr: u32) -> u32 {\n        (addr as *mut u32).read_volatile()\n    }\n\n    pub fn pre_enable() {\n        // Works around Erratum 187 on chip revisions 1 and 2.\n        #[cfg(any(feature = \"nrf52840\", feature = \"nrf52833\", feature = \"nrf52820\"))]\n        unsafe {\n            poke(0x4006EC00, 0x00009375);\n            poke(0x4006ED14, 0x00000003);\n            poke(0x4006EC00, 0x00009375);\n        }\n\n        pre_wakeup();\n    }\n\n    pub fn post_enable() {\n        post_wakeup();\n\n        // Works around Erratum 187 on chip revisions 1 and 2.\n        #[cfg(any(feature = \"nrf52840\", feature = \"nrf52833\", feature = \"nrf52820\"))]\n        unsafe {\n            poke(0x4006EC00, 0x00009375);\n            poke(0x4006ED14, 0x00000000);\n            poke(0x4006EC00, 0x00009375);\n        }\n    }\n\n    pub fn pre_wakeup() {\n        // Works around Erratum 171 on chip revisions 1 and 2.\n\n        #[cfg(feature = \"nrf52840\")]\n        unsafe {\n            if peek(0x4006EC00) == 0x00000000 {\n                poke(0x4006EC00, 0x00009375);\n            }\n\n            poke(0x4006EC14, 0x000000C0);\n            poke(0x4006EC00, 0x00009375);\n        }\n    }\n\n    pub fn post_wakeup() {\n        // Works around Erratum 171 on chip revisions 1 and 2.\n\n        #[cfg(feature = \"nrf52840\")]\n        unsafe {\n            if peek(0x4006EC00) == 0x00000000 {\n                poke(0x4006EC00, 0x00009375);\n            }\n\n            poke(0x4006EC14, 0x00000000);\n            poke(0x4006EC00, 0x00009375);\n        }\n    }\n}\n"
  },
  {
    "path": "embassy-nrf/src/usb/vbus_detect.rs",
    "content": "//! Trait and implementations for performing VBUS detection.\n\nuse core::future::{Future, poll_fn};\nuse core::sync::atomic::{AtomicBool, Ordering};\nuse core::task::Poll;\n\nuse embassy_sync::waitqueue::AtomicWaker;\n\nuse super::BUS_WAKER;\nuse crate::interrupt::typelevel::Interrupt;\nuse crate::{interrupt, pac};\n\n/// Trait for detecting USB VBUS power.\n///\n/// There are multiple ways to detect USB power. The behavior\n/// here provides a hook into determining whether it is.\npub trait VbusDetect {\n    /// Report whether power is detected.\n    ///\n    /// This is indicated by the `USBREGSTATUS.VBUSDETECT` register, or the\n    /// `USBDETECTED`, `USBREMOVED` events from the `POWER` peripheral.\n    fn is_usb_detected(&self) -> bool;\n\n    /// Wait until USB power is ready.\n    ///\n    /// USB power ready is indicated by the `USBREGSTATUS.OUTPUTRDY` register, or the\n    /// `USBPWRRDY` event from the `POWER` peripheral.\n    async fn wait_power_ready(&mut self) -> Result<(), ()>;\n}\n\n#[cfg(not(feature = \"_nrf5340\"))]\ntype UsbRegIrq = interrupt::typelevel::CLOCK_POWER;\n#[cfg(feature = \"_nrf5340\")]\ntype UsbRegIrq = interrupt::typelevel::USBREGULATOR;\n\n#[cfg(not(feature = \"_nrf5340\"))]\nconst USB_REG_PERI: pac::power::Power = pac::POWER;\n#[cfg(feature = \"_nrf5340\")]\nconst USB_REG_PERI: pac::usbreg::Usbreg = pac::USBREGULATOR;\n\n/// Interrupt handler.\npub struct InterruptHandler {\n    _private: (),\n}\n\nimpl interrupt::typelevel::Handler<UsbRegIrq> for InterruptHandler {\n    unsafe fn on_interrupt() {\n        let regs = USB_REG_PERI;\n\n        if regs.events_usbdetected().read() != 0 {\n            regs.events_usbdetected().write_value(0);\n            BUS_WAKER.wake();\n        }\n\n        if regs.events_usbremoved().read() != 0 {\n            regs.events_usbremoved().write_value(0);\n            BUS_WAKER.wake();\n            POWER_WAKER.wake();\n        }\n\n        if regs.events_usbpwrrdy().read() != 0 {\n            regs.events_usbpwrrdy().write_value(0);\n            POWER_WAKER.wake();\n        }\n    }\n}\n\n/// [`VbusDetect`] implementation using the native hardware POWER peripheral.\n///\n/// Unsuitable for usage with the nRF softdevice, since it reserves exclusive acces\n/// to POWER. In that case, use [SoftwareVbusDetect].\npub struct HardwareVbusDetect {\n    _private: (),\n}\n\nstatic POWER_WAKER: AtomicWaker = AtomicWaker::new();\n\nimpl HardwareVbusDetect {\n    /// Create a new `VbusDetectNative`.\n    pub fn new(_irq: impl interrupt::typelevel::Binding<UsbRegIrq, InterruptHandler> + 'static) -> Self {\n        let regs = USB_REG_PERI;\n\n        UsbRegIrq::unpend();\n        unsafe { UsbRegIrq::enable() };\n\n        regs.intenset().write(|w| {\n            w.set_usbdetected(true);\n            w.set_usbremoved(true);\n            w.set_usbpwrrdy(true);\n        });\n\n        Self { _private: () }\n    }\n}\n\nimpl VbusDetect for HardwareVbusDetect {\n    fn is_usb_detected(&self) -> bool {\n        let regs = USB_REG_PERI;\n        regs.usbregstatus().read().vbusdetect()\n    }\n\n    fn wait_power_ready(&mut self) -> impl Future<Output = Result<(), ()>> {\n        poll_fn(|cx| {\n            POWER_WAKER.register(cx.waker());\n            let regs = USB_REG_PERI;\n\n            if regs.usbregstatus().read().outputrdy() {\n                Poll::Ready(Ok(()))\n            } else if !self.is_usb_detected() {\n                Poll::Ready(Err(()))\n            } else {\n                Poll::Pending\n            }\n        })\n    }\n}\n\n/// Software-backed [`VbusDetect`] implementation.\n///\n/// This implementation does not interact with the hardware, it allows user code\n/// to notify the power events by calling functions instead.\n///\n/// This is suitable for use with the nRF softdevice, by calling the functions\n/// when the softdevice reports power-related events.\npub struct SoftwareVbusDetect {\n    usb_detected: AtomicBool,\n    power_ready: AtomicBool,\n}\n\nimpl SoftwareVbusDetect {\n    /// Create a new `SoftwareVbusDetect`.\n    pub fn new(usb_detected: bool, power_ready: bool) -> Self {\n        BUS_WAKER.wake();\n\n        Self {\n            usb_detected: AtomicBool::new(usb_detected),\n            power_ready: AtomicBool::new(power_ready),\n        }\n    }\n\n    /// Report whether power was detected.\n    ///\n    /// Equivalent to the `USBDETECTED`, `USBREMOVED` events from the `POWER` peripheral.\n    pub fn detected(&self, detected: bool) {\n        self.usb_detected.store(detected, Ordering::Relaxed);\n        self.power_ready.store(false, Ordering::Relaxed);\n        BUS_WAKER.wake();\n        POWER_WAKER.wake();\n    }\n\n    /// Report when USB power is ready.\n    ///\n    /// Equivalent to the `USBPWRRDY` event from the `POWER` peripheral.\n    pub fn ready(&self) {\n        self.power_ready.store(true, Ordering::Relaxed);\n        POWER_WAKER.wake();\n    }\n}\n\nimpl VbusDetect for &SoftwareVbusDetect {\n    fn is_usb_detected(&self) -> bool {\n        self.usb_detected.load(Ordering::Relaxed)\n    }\n\n    fn wait_power_ready(&mut self) -> impl Future<Output = Result<(), ()>> {\n        poll_fn(move |cx| {\n            POWER_WAKER.register(cx.waker());\n\n            if self.power_ready.load(Ordering::Relaxed) {\n                Poll::Ready(Ok(()))\n            } else if !self.usb_detected.load(Ordering::Relaxed) {\n                Poll::Ready(Err(()))\n            } else {\n                Poll::Pending\n            }\n        })\n    }\n}\n"
  },
  {
    "path": "embassy-nrf/src/util.rs",
    "content": "#![allow(dead_code)]\n\nconst SRAM_LOWER: usize = 0x2000_0000;\nconst SRAM_UPPER: usize = 0x3000_0000;\n\n/// Does this slice reside entirely within RAM?\npub(crate) fn slice_in_ram<T>(slice: *const [T]) -> bool {\n    if slice.is_empty() {\n        return true;\n    }\n\n    let ptr = slice as *const T as usize;\n    ptr >= SRAM_LOWER && (ptr + slice.len() * core::mem::size_of::<T>()) < SRAM_UPPER\n}\n\n/// Return an error if slice is not in RAM. Skips check if slice is zero-length.\npub(crate) fn slice_in_ram_or<T, E>(slice: *const [T], err: E) -> Result<(), E> {\n    if slice_in_ram(slice) { Ok(()) } else { Err(err) }\n}\n"
  },
  {
    "path": "embassy-nrf/src/wdt.rs",
    "content": "//! Watchdog Timer (WDT) driver.\n//!\n//! This HAL implements a basic watchdog timer with 1..=8 handles.\n//! Once the watchdog has been started, it cannot be stopped.\n\n#![macro_use]\n\nuse core::hint::unreachable_unchecked;\n\nuse embassy_hal_internal::PeripheralType;\n\nuse crate::pac::wdt::vals;\npub use crate::pac::wdt::vals::{Halt as HaltConfig, Sleep as SleepConfig};\nuse crate::{Peri, interrupt, pac, peripherals};\n\nconst MIN_TICKS: u32 = 15;\n\n/// WDT configuration.\n#[non_exhaustive]\npub struct Config {\n    /// Number of 32768 Hz ticks in each watchdog period.\n    ///\n    /// Note: there is a minimum of 15 ticks (458 microseconds). If a lower\n    /// number is provided, 15 ticks will be used as the configured value.\n    pub timeout_ticks: u32,\n\n    /// Should the watchdog continue to count during sleep modes?\n    pub action_during_sleep: SleepConfig,\n\n    /// Should the watchdog continue to count when the CPU is halted for debug?\n    pub action_during_debug_halt: HaltConfig,\n}\n\nimpl Config {\n    /// Create a config structure from the current configuration of the WDT\n    /// peripheral.\n    pub fn try_new<T: Instance>(_wdt: &Peri<'_, T>) -> Option<Self> {\n        let r = T::REGS;\n\n        #[cfg(not(any(feature = \"_nrf91\", feature = \"_nrf5340\", feature = \"_nrf54l\")))]\n        let runstatus = r.runstatus().read().runstatus();\n        #[cfg(any(feature = \"_nrf91\", feature = \"_nrf5340\", feature = \"_nrf54l\"))]\n        let runstatus = r.runstatus().read().runstatuswdt();\n\n        if runstatus {\n            let config = r.config().read();\n            Some(Self {\n                timeout_ticks: r.crv().read(),\n                action_during_sleep: config.sleep(),\n                action_during_debug_halt: config.halt(),\n            })\n        } else {\n            None\n        }\n    }\n}\n\nimpl Default for Config {\n    fn default() -> Self {\n        Self {\n            timeout_ticks: 32768, // 1 second\n            action_during_debug_halt: HaltConfig::RUN,\n            action_during_sleep: SleepConfig::RUN,\n        }\n    }\n}\n\n/// Watchdog driver.\npub struct Watchdog {\n    r: pac::wdt::Wdt,\n}\n\nimpl Watchdog {\n    /// Try to create a new watchdog driver.\n    ///\n    /// This function will return an error if the watchdog is already active\n    /// with a `config` different to the requested one, or a different number of\n    /// enabled handles.\n    ///\n    /// `N` must be between 1 and 8, inclusive.\n    #[inline]\n    pub fn try_new<T: Instance, const N: usize>(\n        wdt: Peri<'static, T>,\n        config: Config,\n    ) -> Result<(Self, [WatchdogHandle; N]), Peri<'static, T>> {\n        assert!(N >= 1 && N <= 8);\n\n        let r = T::REGS;\n\n        let crv = config.timeout_ticks.max(MIN_TICKS);\n        let rren = crate::pac::wdt::regs::Rren((1u32 << N) - 1);\n\n        #[cfg(not(any(feature = \"_nrf91\", feature = \"_nrf5340\", feature = \"_nrf54l\")))]\n        let runstatus = r.runstatus().read().runstatus();\n        #[cfg(any(feature = \"_nrf91\", feature = \"_nrf5340\", feature = \"_nrf54l\"))]\n        let runstatus = r.runstatus().read().runstatuswdt();\n\n        if runstatus {\n            let curr_config = r.config().read();\n            if curr_config.halt() != config.action_during_debug_halt\n                || curr_config.sleep() != config.action_during_sleep\n                || r.crv().read() != crv\n                || r.rren().read() != rren\n            {\n                return Err(wdt);\n            }\n        } else {\n            r.config().write(|w| {\n                w.set_sleep(config.action_during_sleep);\n                w.set_halt(config.action_during_debug_halt);\n            });\n            r.intenset().write(|w| w.set_timeout(true));\n\n            r.crv().write_value(crv);\n            r.rren().write_value(rren);\n            r.tasks_start().write_value(1);\n        }\n\n        let this = Self { r: T::REGS };\n\n        let mut handles = [const { WatchdogHandle { index: 0 } }; N];\n        for i in 0..N {\n            handles[i] = unsafe { WatchdogHandle::steal::<T>(i as u8) };\n            handles[i].pet();\n        }\n\n        Ok((this, handles))\n    }\n\n    /// Enable the watchdog interrupt.\n    ///\n    /// NOTE: Although the interrupt will occur, there is no way to prevent\n    /// the reset from occurring. From the time the event was fired, the\n    /// system will reset two LFCLK ticks later (61 microseconds) if the\n    /// interrupt has been enabled.\n    #[inline(always)]\n    pub fn enable_interrupt(&mut self) {\n        self.r.intenset().write(|w| w.set_timeout(true));\n    }\n\n    /// Disable the watchdog interrupt.\n    ///\n    /// NOTE: This has no effect on the reset caused by the Watchdog.\n    #[inline(always)]\n    pub fn disable_interrupt(&mut self) {\n        self.r.intenclr().write(|w| w.set_timeout(true));\n    }\n\n    /// Is the watchdog still awaiting pets from any handle?\n    ///\n    /// This reports whether sufficient pets have been received from all\n    /// handles to prevent a reset this time period.\n    #[inline(always)]\n    pub fn awaiting_pets(&self) -> bool {\n        let enabled = self.r.rren().read().0;\n        let status = self.r.reqstatus().read().0;\n        (status & enabled) == 0\n    }\n}\n\n/// Watchdog handle.\npub struct WatchdogHandle {\n    index: u8,\n}\n\nimpl WatchdogHandle {\n    fn regs(&self) -> pac::wdt::Wdt {\n        match self.index / 8 {\n            #[cfg(not(feature = \"_multi_wdt\"))]\n            peripherals::WDT::INDEX => peripherals::WDT::REGS,\n            #[cfg(feature = \"_multi_wdt\")]\n            peripherals::WDT0::INDEX => peripherals::WDT0::REGS,\n            #[cfg(feature = \"_multi_wdt\")]\n            peripherals::WDT1::INDEX => peripherals::WDT1::REGS,\n            _ => unsafe { unreachable_unchecked() },\n        }\n    }\n\n    fn rr_index(&self) -> usize {\n        usize::from(self.index % 8)\n    }\n\n    /// Pet the watchdog.\n    ///\n    /// This function pets the given watchdog handle.\n    ///\n    /// NOTE: All active handles must be pet within the time interval to\n    /// prevent a reset from occurring.\n    #[inline]\n    pub fn pet(&mut self) {\n        let r = self.regs();\n        r.rr(self.rr_index()).write(|w| w.set_rr(vals::Rr::RELOAD));\n    }\n\n    /// Has this handle been pet within the current window?\n    pub fn is_pet(&self) -> bool {\n        let r = self.regs();\n        !r.reqstatus().read().rr(self.rr_index())\n    }\n\n    /// Steal a watchdog handle by index.\n    ///\n    /// # Safety\n    /// Watchdog must be initialized and `index` must be between `0` and `N-1`\n    /// where `N` is the handle count when initializing.\n    pub unsafe fn steal<T: Instance>(index: u8) -> Self {\n        Self {\n            index: T::INDEX * 8 + index,\n        }\n    }\n}\n\npub(crate) trait SealedInstance {\n    const REGS: pac::wdt::Wdt;\n    const INDEX: u8;\n}\n\n/// WDT instance.\n#[allow(private_bounds)]\npub trait Instance: SealedInstance + PeripheralType + 'static + Send {\n    /// Interrupt for this peripheral.\n    type Interrupt: interrupt::typelevel::Interrupt;\n}\n\nmacro_rules! impl_wdt {\n    ($type:ident, $pac_type:ident, $irq:ident, $index:literal) => {\n        impl crate::wdt::SealedInstance for peripherals::$type {\n            const REGS: pac::wdt::Wdt = pac::$pac_type;\n            const INDEX: u8 = $index;\n        }\n        impl crate::wdt::Instance for peripherals::$type {\n            type Interrupt = crate::interrupt::typelevel::$irq;\n        }\n    };\n}\n"
  },
  {
    "path": "embassy-nxp/CHANGELOG.md",
    "content": "# Changelog\n\nAll notable changes to this project will be documented in this file.\n\nThe format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),\nand this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).\n\n<!-- next-header -->\n## Unreleased - ReleaseDate\n- Codegen using `nxp-pac` metadata\n- LPC55: PWM simple\n- LPC55: Move ALT definitions for USART to TX/RX pin impls. \n- LPC55: Remove internal match_iocon macro\n- LPC55: DMA Controller and asynchronous version of USART\n- Moved NXP LPC55S69 from `lpc55-pac` to `nxp-pac`\n- First release with changelog.\n"
  },
  {
    "path": "embassy-nxp/Cargo.toml",
    "content": "[package]\nname = \"embassy-nxp\"\nversion = \"0.1.0\"\nedition = \"2024\"\nlicense = \"MIT OR Apache-2.0\"\n\npublish = false\n\n[package.metadata.embassy]\nbuild = [\n    {target = \"thumbv8m.main-none-eabihf\", features = [\"defmt\", \"lpc55-core0\"]},\n    {target = \"thumbv8m.main-none-eabihf\", features = [\"defmt\", \"lpc55s16\"]},\n    {target = \"thumbv7em-none-eabihf\", features = [\"defmt\", \"mimxrt1011\", \"rt\", \"time-driver-pit\"]},\n    {target = \"thumbv7em-none-eabihf\", features = [\"defmt\", \"mimxrt1062\", \"rt\", \"time-driver-pit\"]},\n]\n\n[package.metadata.embassy_docs]\nsrc_base = \"https://github.com/embassy-rs/embassy/blob/embassy-nxp-v$VERSION/embassy-nxp/src/\"\nsrc_base_git = \"https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-nxp/src/\"\nfeatures = [\"defmt\", \"unstable-pac\" ] # TODO: Add time-driver-any, as both lpc55 and mimxrt1xxx use different drivers.\n\nflavors = [\n    { regex_feature = \"lpc55-core0\", target = \"thumbv8m.main-none-eabihf\" },\n    { regex_feature = \"lpc55s16\", target = \"thumbv8m.main-none-eabihf\" },\n    { regex_feature = \"mimxrt.*\", target = \"thumbv7em-none-eabihf\" },\n]\n\n[dependencies]\ncortex-m = \"0.7.7\"\ncortex-m-rt = \"0.7.0\"\ncritical-section = \"1.1.2\"\nembassy-hal-internal = { version = \"0.5.0\", path = \"../embassy-hal-internal\", features = [\"cortex-m\", \"prio-bits-2\"] }\nembassy-sync = { version = \"0.8.0\", path = \"../embassy-sync\" }\nembassy-futures = { version = \"0.1.2\", path = \"../embassy-futures\"}\ndefmt = { version = \"1\", optional = true }\nlog = { version = \"0.4.27\", optional = true }\nembassy-time = { version = \"0.5.1\", path = \"../embassy-time\", optional = true }\nembassy-time-driver = { version = \"0.2.2\", path = \"../embassy-time-driver\", optional = true }\nembassy-time-queue-utils = { version = \"0.3.0\", path = \"../embassy-time-queue-utils\", optional = true }\nembedded-io = { version = \"0.7.1\" }\nembedded-hal-02 = { package = \"embedded-hal\", version = \"0.2.6\", features = [\"unproven\"] }\n## Chip dependencies\nnxp-pac = { version = \"0.1.0\", optional = true, git = \"https://github.com/embassy-rs/nxp-pac\", rev = \"47dada0976da4114c348ea16f5ead31f38f36eea\" }\n\nimxrt-rt = { version = \"0.1.7\", optional = true, features = [\"device\"] }\n\n[build-dependencies]\ncfg_aliases = \"0.2.1\"\nnxp-pac = { version = \"0.1.0\", git = \"https://github.com/embassy-rs/nxp-pac\", rev = \"47dada0976da4114c348ea16f5ead31f38f36eea\", features = [\"metadata\"] }\nproc-macro2 = \"1.0.95\"\nquote = \"1.0.15\"\n\n[features]\ndefault = [\"rt\"]\n# Enable PACs as optional dependencies, since some chip families will use different pac crates (temporarily).\nrt = [\"nxp-pac?/rt\"]\n\n## Enable [defmt support](https://docs.rs/defmt) and enables `defmt` debug-log messages and formatting in embassy drivers.\ndefmt = [\"dep:defmt\", \"embassy-hal-internal/defmt\", \"embassy-sync/defmt\"]\n\nlog = [\"dep:log\"]\n\n## Use Periodic Interrupt Timer (PIT) as the time driver for `embassy-time`, with a tick rate of 1 MHz\ntime-driver-pit = [\"_time_driver\", \"embassy-time?/tick-hz-1_000_000\"]\n\n## Use Real Time Clock (RTC) as the time driver for `embassy-time`, with a tick rate of 32768 Hz\ntime-driver-rtc = [\"_time_driver\", \"embassy-time?/tick-hz-32_768\"]\n\n## Reexport the PAC for the currently enabled chip at `embassy_nxp::pac` (unstable)\nunstable-pac = []\n# This is unstable because semver-minor (non-breaking) releases of embassy-nxp may major-bump (breaking) the PAC version.\n# If this is an issue for you, you're encouraged to directly depend on a fixed version of the PAC.\n# There are no plans to make this stable.\n\n## internal use only\n#\n# This feature is unfortunately a hack around the fact that cfg_aliases cannot apply to the buildscript\n# that creates the aliases.\n_rt1xxx = []\n_lpc55 = []\n\n# A timer driver is enabled.\n_time_driver = [\"dep:embassy-time-driver\", \"dep:embassy-time-queue-utils\"]\n\n#! ### Chip selection features\nlpc55-core0 = [\"nxp-pac/lpc55s69_cm33_core0\"]\nlpc55s16 = [\"nxp-pac/lpc55s16\", \"_lpc55\"]\nmimxrt1011 = [\"nxp-pac/mimxrt1011\", \"_rt1xxx\", \"dep:imxrt-rt\"]\nmimxrt1062 = [\"nxp-pac/mimxrt1062\", \"_rt1xxx\", \"dep:imxrt-rt\"]\n"
  },
  {
    "path": "embassy-nxp/build.rs",
    "content": "use std::io::Write;\nuse std::path::{Path, PathBuf};\nuse std::process::Command;\nuse std::{env, fs};\n\nuse cfg_aliases::cfg_aliases;\nuse nxp_pac::metadata;\nuse nxp_pac::metadata::{METADATA, Peripheral};\n#[allow(unused)]\nuse proc_macro2::TokenStream;\nuse proc_macro2::{Ident, Literal, Span};\nuse quote::format_ident;\n#[allow(unused)]\nuse quote::quote;\n\n#[path = \"./build_common.rs\"]\nmod common;\n\nfn main() {\n    let mut cfgs = common::CfgSet::new();\n    common::set_target_cfgs(&mut cfgs);\n\n    let chip_name = match env::vars()\n        .map(|(a, _)| a)\n        .filter(|x| x.starts_with(\"CARGO_FEATURE_MIMXRT\") || x.starts_with(\"CARGO_FEATURE_LPC\"))\n        .get_one()\n    {\n        Ok(x) => x,\n        Err(GetOneError::None) => panic!(\"No mimxrt/lpc Cargo feature enabled\"),\n        Err(GetOneError::Multiple) => panic!(\"Multiple mimxrt/lpc Cargo features enabled\"),\n    }\n    .strip_prefix(\"CARGO_FEATURE_\")\n    .unwrap()\n    .to_ascii_lowercase();\n\n    let singletons = singletons(&mut cfgs);\n\n    cfg_aliases! {\n        rt1xxx: { any(feature = \"mimxrt1011\", feature = \"mimxrt1062\") },\n    }\n\n    cfg_aliases! {\n        lpc55: { any(feature = \"lpc55s16\", feature = \"lpc55-core0\") },\n    }\n\n    eprintln!(\"chip: {chip_name}\");\n\n    generate_code(&mut cfgs, &singletons);\n}\n\n/// A peripheral singleton returned by `embassy_nxp::init`.\nstruct Singleton {\n    name: String,\n\n    /// A cfg guard which indicates whether the `Peripherals` struct will give the user this singleton.\n    cfg: Option<TokenStream>,\n}\n\nfn singletons(cfgs: &mut common::CfgSet) -> Vec<Singleton> {\n    let mut singletons = Vec::new();\n\n    for peripheral in METADATA.peripherals {\n        // GPIO and DMA are generated in a 2nd pass.\n        let skip_singleton = if peripheral.name.starts_with(\"GPIO\") || peripheral.name.starts_with(\"DMA\") {\n            true\n        } else {\n            false\n        };\n\n        if !skip_singleton {\n            singletons.push(Singleton {\n                name: peripheral.name.into(),\n                cfg: None,\n            });\n        }\n    }\n\n    cfgs.declare_all(&[\n        \"gpio1\",\n        \"gpio1_hi\",\n        \"gpio2\",\n        \"gpio2_hi\",\n        \"gpio3\",\n        \"gpio3_hi\",\n        \"gpio4\",\n        \"gpio4_hi\",\n        \"gpio5\",\n        \"gpio5_hi\",\n        \"gpio10\",\n        \"gpio10_hi\",\n    ]);\n\n    for peripheral in METADATA.peripherals.iter().filter(|p| p.name.starts_with(\"GPIO\")) {\n        let number = peripheral.name.strip_prefix(\"GPIO\").unwrap();\n        assert!(number.parse::<u8>().is_ok());\n        cfgs.enable(format!(\"gpio{}\", number));\n\n        for signal in peripheral.signals.iter() {\n            let pin_number = signal.name.parse::<u8>().unwrap();\n\n            if pin_number > 15 {\n                cfgs.enable(format!(\"gpio{}_hi\", number));\n            }\n\n            // GPIO signals only defined a single signal, on a single pin.\n            assert_eq!(signal.pins.len(), 1);\n\n            singletons.push(Singleton {\n                name: signal.pins[0].pin.into(),\n                cfg: None,\n            });\n        }\n    }\n\n    for peripheral in METADATA.peripherals.iter().filter(|p| p.name.starts_with(\"DMA\")) {\n        let instance = peripheral.name.strip_prefix(\"DMA\").unwrap();\n        assert!(instance.parse::<u8>().is_ok());\n\n        for signal in peripheral.signals.iter() {\n            let channel_number = signal.name.parse::<u8>().unwrap();\n            let name = format!(\"DMA{instance}_CH{channel_number}\");\n\n            // DMA has no pins.\n            assert!(signal.pins.is_empty());\n\n            singletons.push(Singleton { name, cfg: None });\n        }\n    }\n\n    for peripheral in METADATA.peripherals.iter().filter(|p| p.name.starts_with(\"SCT\")) {\n        let instance = peripheral.name.strip_prefix(\"SCT\").unwrap();\n        assert!(instance.parse::<u8>().is_ok());\n\n        for signal in peripheral.signals.iter() {\n            if !signal.name.starts_with(\"OUT\") {\n                continue;\n            }\n\n            let channel_number = signal.name.strip_prefix(\"OUT\").unwrap().parse::<u8>().unwrap();\n            let name = format!(\"SCT{instance}_OUT{channel_number}\");\n\n            singletons.push(Singleton { name, cfg: None });\n        }\n    }\n\n    singletons\n}\n\n#[cfg(feature = \"_rt1xxx\")]\nfn generate_iomuxc() -> TokenStream {\n    let iomuxc_pad_impls = metadata::METADATA\n        .pins\n        .iter()\n        .filter(|p| p.iomuxc.as_ref().filter(|i| i.mux.is_some()).is_some())\n        .map(|pin| {\n            let Some(ref iomuxc) = pin.iomuxc else {\n                panic!(\"Pin {} has no IOMUXC definitions\", pin.name);\n            };\n\n            let name = Ident::new(pin.name, Span::call_site());\n            let mux = iomuxc.mux.unwrap();\n            let pad = iomuxc.pad;\n\n            quote! {\n                impl_iomuxc_pad!(#name, #pad, #mux);\n            }\n        });\n\n    let base_match_arms = metadata::METADATA\n        .peripherals\n        .iter()\n        .filter(|p| p.name.starts_with(\"GPIO\"))\n        .map(|peripheral| {\n            peripheral.signals.iter().map(|signal| {\n                // All GPIO signals have a single pin.\n                let pin = &signal.pins[0];\n                let instance = peripheral.name.strip_prefix(\"GPIO\").unwrap();\n                let bank_match = format_ident!(\"Gpio{}\", instance);\n                let pin_number = signal.name.parse::<u8>().unwrap();\n                let pin_ident = Ident::new(pin.pin, Span::call_site());\n\n                quote! {\n                    (Bank::#bank_match, #pin_number) => <crate::peripherals::#pin_ident as crate::iomuxc::SealedPad>\n                }\n            })\n        })\n        .flatten()\n        .collect::<Vec<_>>();\n\n    let pad_match_arms = base_match_arms.iter().map(|arm| {\n        quote! { #arm::PAD }\n    });\n\n    let mux_match_arms = base_match_arms.iter().map(|arm| {\n        quote! { #arm::MUX }\n    });\n\n    quote! {\n        #(#iomuxc_pad_impls)*\n\n        pub(crate) fn iomuxc_pad(bank: crate::gpio::Bank, pin: u8) -> *mut () {\n            use crate::gpio::Bank;\n\n            match (bank, pin) {\n                #(#pad_match_arms),*,\n                _ => unreachable!()\n            }\n        }\n\n        pub(crate) fn iomuxc_mux(bank: crate::gpio::Bank, pin: u8) -> Option<*mut ()> {\n            use crate::gpio::Bank;\n\n            match (bank, pin) {\n                #(#mux_match_arms),*,\n                _ => unreachable!()\n            }\n        }\n    }\n}\n\nfn generate_code(cfgs: &mut common::CfgSet, singletons: &[Singleton]) {\n    #[allow(unused)]\n    use std::fmt::Write;\n\n    let out_dir = &PathBuf::from(env::var_os(\"OUT_DIR\").unwrap());\n    #[allow(unused_mut)]\n    let mut output = String::new();\n\n    writeln!(&mut output, \"{}\", peripherals(singletons)).unwrap();\n\n    #[cfg(feature = \"_rt1xxx\")]\n    writeln!(&mut output, \"{}\", generate_iomuxc()).unwrap();\n\n    writeln!(&mut output, \"{}\", interrupts()).unwrap();\n    writeln!(&mut output, \"{}\", impl_peripherals(cfgs, singletons)).unwrap();\n\n    let out_file = out_dir.join(\"_generated.rs\").to_string_lossy().to_string();\n    fs::write(&out_file, output).unwrap();\n    rustfmt(&out_file);\n}\n\nfn interrupts() -> TokenStream {\n    let interrupts = METADATA.interrupts.iter().map(|interrupt| format_ident!(\"{interrupt}\"));\n\n    quote! {\n        embassy_hal_internal::interrupt_mod!(#(#interrupts),*);\n    }\n}\n\nfn peripherals(singletons: &[Singleton]) -> TokenStream {\n    let defs = singletons.iter().map(|s| {\n        let ident = Ident::new(&s.name, Span::call_site());\n        quote! { #ident }\n    });\n\n    let peripherals = singletons.iter().map(|s| {\n        let ident = Ident::new(&s.name, Span::call_site());\n        let cfg = s.cfg.clone().unwrap_or_else(|| quote! {});\n        quote! {\n            #cfg\n            #ident\n        }\n    });\n\n    quote! {\n        embassy_hal_internal::peripherals_definition!(#(#defs),*);\n        embassy_hal_internal::peripherals_struct!(#(#peripherals),*);\n    }\n}\n\nfn impl_gpio_pin(impls: &mut Vec<TokenStream>, peripheral: &Peripheral) {\n    let instance = peripheral.name.strip_prefix(\"GPIO\").unwrap();\n    let bank = format_ident!(\"Gpio{}\", instance);\n    // let pin =\n\n    for signal in peripheral.signals.iter() {\n        let pin_number = signal.name.parse::<u8>().unwrap();\n        let pin = Ident::new(signal.pins[0].pin, Span::call_site());\n\n        impls.push(quote! {\n            impl_pin!(#pin, #bank, #pin_number);\n        });\n    }\n}\n\nfn impl_dma_channel(impls: &mut Vec<TokenStream>, peripheral: &Peripheral) {\n    let instance = Ident::new(peripheral.name, Span::call_site());\n\n    for signal in peripheral.signals.iter() {\n        let channel_number = signal.name.parse::<u8>().unwrap();\n        let channel_name = format_ident!(\"{instance}_CH{channel_number}\");\n\n        impls.push(quote! {\n            impl_dma_channel!(#instance, #channel_name, #channel_number);\n        });\n    }\n}\n\nfn impl_usart(cfgs: &mut common::CfgSet, impls: &mut Vec<TokenStream>, peripheral: &Peripheral) {\n    cfgs.declare_all(&[\n        \"has_usart_txd_pins\",\n        \"has_usart_rxd_pins\",\n        \"has_usart_cts_pins\",\n        \"has_usart_rts_pins\",\n        \"has_usart_sck_pins\",\n    ]);\n\n    let instance = Ident::new(peripheral.name, Span::call_site());\n    let flexcomm = Ident::new(\n        peripheral.flexcomm.expect(\"LPC55 must specify FLEXCOMM instance\"),\n        Span::call_site(),\n    );\n    let number = Literal::u8_unsuffixed(peripheral.name.strip_prefix(\"USART\").unwrap().parse::<u8>().unwrap());\n\n    impls.push(quote! {\n        impl_usart_instance!(#instance, #flexcomm, #number);\n    });\n\n    for signal in peripheral.signals {\n        let r#macro = match signal.name {\n            \"TXD\" => {\n                cfgs.enable(\"has_usart_txd_pins\");\n                format_ident!(\"impl_usart_txd_pin\")\n            }\n            \"RXD\" => {\n                cfgs.enable(\"has_usart_rxd_pins\");\n                format_ident!(\"impl_usart_rxd_pin\")\n            }\n            \"CTS\" => {\n                cfgs.enable(\"has_usart_cts_pins\");\n                format_ident!(\"impl_usart_cts_pin\")\n            }\n            \"RTS\" => {\n                cfgs.enable(\"has_usart_rts_pins\");\n                format_ident!(\"impl_usart_rts_pin\")\n            }\n            \"SCK\" => {\n                cfgs.enable(\"has_usart_sck_pins\");\n                format_ident!(\"impl_usart_sck_pin\")\n            }\n            _ => unreachable!(),\n        };\n\n        for pin in signal.pins {\n            let alt = format_ident!(\"ALT{}\", pin.alt);\n            let pin = format_ident!(\"{}\", pin.pin);\n\n            impls.push(quote! {\n                #r#macro!(#pin, #instance, #alt);\n            });\n        }\n    }\n\n    for dma_mux in peripheral.dma_muxing {\n        assert_eq!(dma_mux.mux, \"DMA0\", \"TODO: USART for more than LPC55\");\n\n        let r#macro = match dma_mux.signal {\n            \"TX\" => format_ident!(\"impl_usart_tx_channel\"),\n            \"RX\" => format_ident!(\"impl_usart_rx_channel\"),\n            _ => unreachable!(),\n        };\n\n        let channel = format_ident!(\"DMA0_CH{}\", dma_mux.request);\n\n        impls.push(quote! {\n            #r#macro!(#instance, #channel);\n        });\n    }\n}\n\nfn impl_sct(impls: &mut Vec<TokenStream>, peripheral: &Peripheral) {\n    let instance = Ident::new(peripheral.name, Span::call_site());\n\n    impls.push(quote! {\n        impl_sct_instance!(#instance);\n    });\n\n    for signal in peripheral.signals.iter() {\n        if signal.name.starts_with(\"OUT\") {\n            let channel_number = signal.name.strip_prefix(\"OUT\").unwrap().parse::<u8>().unwrap();\n\n            let channel_name = format_ident!(\"{instance}_OUT{channel_number}\");\n\n            impls.push(quote! {\n                impl_sct_output_instance!(#instance, #channel_name, #channel_number);\n            });\n\n            if signal.name.starts_with(\"OUT\") {\n                for pin in signal.pins {\n                    let pin_name = format_ident!(\"{}\", pin.pin);\n                    let alt = format_ident!(\"ALT{}\", pin.alt);\n\n                    impls.push(quote! {\n                        impl_sct_output_pin!(#instance, #channel_name, #pin_name, #alt);\n                    });\n                }\n            }\n        }\n    }\n}\n\nfn impl_peripherals(cfgs: &mut common::CfgSet, _singletons: &[Singleton]) -> TokenStream {\n    let mut impls = Vec::new();\n\n    for peripheral in metadata::METADATA.peripherals.iter() {\n        if peripheral.name.starts_with(\"GPIO\") {\n            impl_gpio_pin(&mut impls, peripheral);\n        }\n\n        if peripheral.name.starts_with(\"DMA\") {\n            impl_dma_channel(&mut impls, peripheral);\n        }\n\n        if peripheral.name.starts_with(\"USART\") {\n            impl_usart(cfgs, &mut impls, peripheral);\n        }\n\n        if peripheral.name.starts_with(\"SCT\") {\n            impl_sct(&mut impls, peripheral);\n        }\n    }\n\n    quote! {\n        #(#impls)*\n    }\n}\n\n/// rustfmt a given path.\n/// Failures are logged to stderr and ignored.\nfn rustfmt(path: impl AsRef<Path>) {\n    let path = path.as_ref();\n    match Command::new(\"rustfmt\").args([path]).output() {\n        Err(e) => {\n            eprintln!(\"failed to exec rustfmt {:?}: {:?}\", path, e);\n        }\n        Ok(out) => {\n            if !out.status.success() {\n                eprintln!(\"rustfmt {:?} failed:\", path);\n                eprintln!(\"=== STDOUT:\");\n                std::io::stderr().write_all(&out.stdout).unwrap();\n                eprintln!(\"=== STDERR:\");\n                std::io::stderr().write_all(&out.stderr).unwrap();\n            }\n        }\n    }\n}\n\nenum GetOneError {\n    None,\n    Multiple,\n}\n\ntrait IteratorExt: Iterator {\n    fn get_one(self) -> Result<Self::Item, GetOneError>;\n}\n\nimpl<T: Iterator> IteratorExt for T {\n    fn get_one(mut self) -> Result<Self::Item, GetOneError> {\n        match self.next() {\n            None => Err(GetOneError::None),\n            Some(res) => match self.next() {\n                Some(_) => Err(GetOneError::Multiple),\n                None => Ok(res),\n            },\n        }\n    }\n}\n"
  },
  {
    "path": "embassy-nxp/build_common.rs",
    "content": "// NOTE: this file is copy-pasted between several Embassy crates, because there is no\n// straightforward way to share this code:\n// - it cannot be placed into the root of the repo and linked from each build.rs using `#[path =\n// \"../build_common.rs\"]`, because `cargo publish` requires that all files published with a crate\n// reside in the crate's directory,\n// - it cannot be symlinked from `embassy-xxx/build_common.rs` to `../build_common.rs`, because\n// symlinks don't work on Windows.\n\nuse std::collections::HashSet;\nuse std::env;\n\n/// Helper for emitting cargo instruction for enabling configs (`cargo:rustc-cfg=X`) and declaring\n/// them (`cargo:rust-check-cfg=cfg(X)`).\n#[derive(Debug)]\npub struct CfgSet {\n    enabled: HashSet<String>,\n    declared: HashSet<String>,\n}\n\nimpl CfgSet {\n    pub fn new() -> Self {\n        Self {\n            enabled: HashSet::new(),\n            declared: HashSet::new(),\n        }\n    }\n\n    /// Enable a config, which can then be used in `#[cfg(...)]` for conditional compilation.\n    ///\n    /// All configs that can potentially be enabled should be unconditionally declared using\n    /// [`Self::declare()`].\n    pub fn enable(&mut self, cfg: impl AsRef<str>) {\n        if self.enabled.insert(cfg.as_ref().to_owned()) {\n            println!(\"cargo:rustc-cfg={}\", cfg.as_ref());\n        }\n    }\n\n    pub fn enable_all(&mut self, cfgs: &[impl AsRef<str>]) {\n        for cfg in cfgs.iter() {\n            self.enable(cfg.as_ref());\n        }\n    }\n\n    /// Declare a valid config for conditional compilation, without enabling it.\n    ///\n    /// This enables rustc to check that the configs in `#[cfg(...)]` attributes are valid.\n    pub fn declare(&mut self, cfg: impl AsRef<str>) {\n        if self.declared.insert(cfg.as_ref().to_owned()) {\n            println!(\"cargo:rustc-check-cfg=cfg({})\", cfg.as_ref());\n        }\n    }\n\n    pub fn declare_all(&mut self, cfgs: &[impl AsRef<str>]) {\n        for cfg in cfgs.iter() {\n            self.declare(cfg.as_ref());\n        }\n    }\n\n    pub fn set(&mut self, cfg: impl Into<String>, enable: bool) {\n        let cfg = cfg.into();\n        if enable {\n            self.enable(cfg.clone());\n        }\n        self.declare(cfg);\n    }\n}\n\n/// Sets configs that describe the target platform.\npub fn set_target_cfgs(cfgs: &mut CfgSet) {\n    let target = env::var(\"TARGET\").unwrap();\n\n    if target.starts_with(\"thumbv6m-\") {\n        cfgs.enable_all(&[\"cortex_m\", \"armv6m\"]);\n    } else if target.starts_with(\"thumbv7m-\") {\n        cfgs.enable_all(&[\"cortex_m\", \"armv7m\"]);\n    } else if target.starts_with(\"thumbv7em-\") {\n        cfgs.enable_all(&[\"cortex_m\", \"armv7m\", \"armv7em\"]);\n    } else if target.starts_with(\"thumbv8m.base\") {\n        cfgs.enable_all(&[\"cortex_m\", \"armv8m\", \"armv8m_base\"]);\n    } else if target.starts_with(\"thumbv8m.main\") {\n        cfgs.enable_all(&[\"cortex_m\", \"armv8m\", \"armv8m_main\"]);\n    }\n    cfgs.declare_all(&[\n        \"cortex_m\",\n        \"armv6m\",\n        \"armv7m\",\n        \"armv7em\",\n        \"armv8m\",\n        \"armv8m_base\",\n        \"armv8m_main\",\n    ]);\n\n    cfgs.set(\"has_fpu\", target.ends_with(\"-eabihf\"));\n}\n"
  },
  {
    "path": "embassy-nxp/src/chips/lpc55.rs",
    "content": "pub(crate) mod _generated {\n    #![allow(dead_code)]\n    #![allow(unused_imports)]\n    #![allow(non_snake_case)]\n    #![allow(missing_docs)]\n\n    include!(concat!(env!(\"OUT_DIR\"), \"/_generated.rs\"));\n}\n\npub use _generated::*;\n"
  },
  {
    "path": "embassy-nxp/src/chips/mimxrt1011.rs",
    "content": "// This must be imported so that __preinit is defined.\nuse imxrt_rt as _;\n\npub(crate) mod _generated {\n    #![allow(dead_code)]\n    #![allow(unused_imports)]\n    #![allow(non_snake_case)]\n    #![allow(missing_docs)]\n\n    include!(concat!(env!(\"OUT_DIR\"), \"/_generated.rs\"));\n}\n\npub use _generated::*;\n"
  },
  {
    "path": "embassy-nxp/src/chips/mimxrt1062.rs",
    "content": "// This must be imported so that __preinit is defined.\nuse imxrt_rt as _;\n\npub(crate) mod _generated {\n    #![allow(dead_code)]\n    #![allow(unused_imports)]\n    #![allow(non_snake_case)]\n    #![allow(missing_docs)]\n\n    include!(concat!(env!(\"OUT_DIR\"), \"/_generated.rs\"));\n}\n\npub use _generated::*;\n"
  },
  {
    "path": "embassy-nxp/src/dma/lpc55.rs",
    "content": "#![macro_use]\n\nuse core::cell::RefCell;\nuse core::future::Future;\nuse core::pin::Pin;\nuse core::sync::atomic::{Ordering, compiler_fence};\nuse core::task::{Context, Poll};\n\nuse critical_section::Mutex;\nuse embassy_hal_internal::interrupt::InterruptExt;\nuse embassy_hal_internal::{PeripheralType, impl_peripheral};\nuse embassy_sync::waitqueue::AtomicWaker;\n\nuse crate::Peri;\n#[cfg(feature = \"rt\")]\nuse crate::pac::interrupt;\nuse crate::pac::{SYSCON, *};\n\n#[cfg(feature = \"rt\")]\n#[interrupt]\nfn DMA0() {\n    let inta = DMA0.inta0().read().ia();\n    for channel in 0..CHANNEL_COUNT {\n        if (DMA0.errint0().read().err() & (1 << channel)) != 0 {\n            panic!(\"DMA: error on DMA_0 channel {}\", channel);\n        }\n\n        if (inta & (1 << channel)) != 0 {\n            CHANNEL_WAKERS[channel].wake();\n            DMA0.inta0().modify(|w| w.set_ia(1 << channel));\n        }\n    }\n}\n\npub(crate) fn init() {\n    assert_eq!(core::mem::size_of::<DmaDescriptor>(), 16, \"Descriptor must be 16 bytes\");\n    assert_eq!(\n        core::mem::align_of::<DmaDescriptor>(),\n        16,\n        \"Descriptor must be 16-byte aligned\"\n    );\n    assert_eq!(\n        core::mem::align_of::<DmaDescriptorTable>(),\n        512,\n        \"Table must be 512-byte aligned\"\n    );\n    // Start clock for DMA\n    SYSCON.ahbclkctrl0().modify(|w| w.set_dma0(true));\n    // Reset DMA\n    SYSCON\n        .presetctrl0()\n        .modify(|w| w.set_dma0_rst(syscon::vals::Dma0Rst::ASSERTED));\n    SYSCON\n        .presetctrl0()\n        .modify(|w| w.set_dma0_rst(syscon::vals::Dma0Rst::RELEASED));\n\n    // Address bits 31:9 of the beginning of the DMA descriptor table\n    critical_section::with(|cs| {\n        DMA0.srambase()\n            .write(|w| w.set_offset((DMA_DESCRIPTORS.borrow(cs).as_ptr() as u32) >> 9));\n    });\n    // Enable DMA controller\n    DMA0.ctrl().modify(|w| w.set_enable(true));\n\n    unsafe {\n        crate::pac::interrupt::DMA0.enable();\n    }\n    info!(\"DMA initialized\");\n}\n\n/// DMA read.\n///\n/// SAFETY: Slice must point to a valid location reachable by DMA.\npub unsafe fn read<'a, C: Channel, W: Word>(ch: Peri<'a, C>, from: *const W, to: *mut [W]) -> Transfer<'a, C> {\n    copy_inner(\n        ch,\n        from as *const u32,\n        to as *mut W as *mut u32,\n        to.len(),\n        W::size(),\n        false,\n        true,\n    )\n}\n\n/// DMA write.\n///\n/// SAFETY: Slice must point to a valid location reachable by DMA.\npub unsafe fn write<'a, C: Channel, W: Word>(ch: Peri<'a, C>, from: *const [W], to: *mut W) -> Transfer<'a, C> {\n    copy_inner(\n        ch,\n        from as *const W as *const u32,\n        to as *mut u32,\n        from.len(),\n        W::size(),\n        true,\n        false,\n    )\n}\n\n/// DMA copy between slices.\n///\n/// SAFETY: Slices must point to locations reachable by DMA.\npub unsafe fn copy<'a, C: Channel, W: Word>(ch: Peri<'a, C>, from: &[W], to: &mut [W]) -> Transfer<'a, C> {\n    let from_len = from.len();\n    let to_len = to.len();\n    assert_eq!(from_len, to_len);\n    copy_inner(\n        ch,\n        from.as_ptr() as *const u32,\n        to.as_mut_ptr() as *mut u32,\n        from_len,\n        W::size(),\n        true,\n        true,\n    )\n}\n\nfn copy_inner<'a, C: Channel>(\n    ch: Peri<'a, C>,\n    from: *const u32,\n    to: *mut u32,\n    len: usize,\n    data_size: crate::pac::dma::vals::Width,\n    incr_src: bool,\n    incr_dest: bool,\n) -> Transfer<'a, C> {\n    let p = ch.regs();\n\n    // Buffer ending address = buffer starting address + (XFERCOUNT * the transfer increment)\n    // XREFCOUNT = the number of transfers performed - 1.\n    // The 1st transfer is included in the starting address.\n    let source_end_addr = if incr_src {\n        from as u32 + len as u32 - 1\n    } else {\n        from as u32\n    };\n    let dest_end_addr = if incr_dest {\n        to as u32 + len as u32 - 1\n    } else {\n        to as u32\n    };\n\n    compiler_fence(Ordering::SeqCst);\n\n    critical_section::with(|cs| {\n        DMA_DESCRIPTORS.borrow(cs).borrow_mut().descriptors[ch.number() as usize] = DmaDescriptor {\n            reserved: 0,\n            source_end_addr,\n            dest_end_addr,\n            next_desc: 0, // Since only single transfers are made, there is no need for reload descriptor address.\n        }\n    });\n\n    compiler_fence(Ordering::SeqCst);\n\n    p.cfg().modify(|w| {\n        // Peripheral DMA requests are enabled.\n        // DMA requests that pace transfers can be interpreted then.\n        w.set_periphreqen(true);\n        // There is no need to have them on.\n        // No complex transfers are performed for now.\n        w.set_hwtrigen(false);\n        w.set_chpriority(0);\n    });\n\n    p.xfercfg().modify(|w| {\n        // This bit indicates whether the current channel descriptor is\n        // valid and can potentially be acted upon,\n        // if all other activation criteria are fulfilled.\n        w.set_cfgvalid(true);\n        // Indicates whether the channel’s control structure will be reloaded\n        // when the current descriptor is exhausted.\n        // Reloading allows ping-pong and linked transfers.\n        w.set_reload(false);\n        // There is no hardware distinction between interrupt A and B.\n        // They can be used by software to assist with more complex descriptor usage.\n        // By convention, interrupt A may be used when only one interrupt flag is needed.\n        w.set_setinta(true);\n        w.set_setintb(false);\n        w.set_width(data_size);\n        w.set_srcinc(if incr_src {\n            dma::vals::Srcinc::WIDTH_X_1\n        } else {\n            dma::vals::Srcinc::NO_INCREMENT\n        });\n        w.set_dstinc(if incr_dest {\n            dma::vals::Dstinc::WIDTH_X_1\n        } else {\n            dma::vals::Dstinc::NO_INCREMENT\n        });\n        // Total number of transfers to be performed, minus 1 encoded.\n        w.set_xfercount((len as u16) - 1);\n        // Before triggering the channel, it has to be enabled.\n        w.set_swtrig(false);\n    });\n\n    compiler_fence(Ordering::SeqCst);\n    DMA0.enableset0().write(|w| w.set_ena(1 << ch.number()));\n    DMA0.intenset0().write(|w| w.set_inten(1 << ch.number()));\n\n    compiler_fence(Ordering::SeqCst);\n    // Start transfer.\n    DMA0.settrig0().write(|w| w.set_trig(1 << ch.number()));\n    compiler_fence(Ordering::SeqCst);\n    Transfer::new(ch)\n}\n\n/// DMA transfer driver.\n#[must_use = \"futures do nothing unless you `.await` or poll them\"]\npub struct Transfer<'a, C: Channel> {\n    channel: Peri<'a, C>,\n}\n\nimpl<'a, C: Channel> Transfer<'a, C> {\n    pub(crate) fn new(channel: Peri<'a, C>) -> Self {\n        Self { channel }\n    }\n}\n\nimpl<'a, C: Channel> Drop for Transfer<'a, C> {\n    fn drop(&mut self) {\n        DMA0.enableclr0().write(|w| w.set_clr(1 << self.channel.number()));\n        while (DMA0.busy0().read().bsy() & (1 << self.channel.number())) != 0 {}\n        DMA0.abort0().write(|w| w.set_abortctrl(1 << self.channel.number()));\n    }\n}\n\nimpl<'a, C: Channel> Unpin for Transfer<'a, C> {}\nimpl<'a, C: Channel> Future for Transfer<'a, C> {\n    type Output = ();\n    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {\n        // We need to register/re-register the waker for each poll because any\n        // calls to wake will deregister the waker.\n        CHANNEL_WAKERS[self.channel.number() as usize].register(cx.waker());\n        // Check if it is busy or not.\n        if (DMA0.busy0().read().bsy() & (1 << self.channel.number())) != 0 {\n            Poll::Pending\n        } else {\n            Poll::Ready(())\n        }\n    }\n}\n\n// Total number of channles including both DMA0 and DMA1.\n// In spite of using only DMA0 channels, the descriptor table\n// should be of this size.\npub(crate) const CHANNEL_COUNT: usize = 32;\n\nstatic CHANNEL_WAKERS: [AtomicWaker; CHANNEL_COUNT] = [const { AtomicWaker::new() }; CHANNEL_COUNT];\n\n// See section 22.5.2 (table 450)\n// UM11126, Rev. 2.8\n// The size of a descriptor must be aligned to a multiple of 16 bytes.\n#[repr(C, align(16))]\n#[derive(Clone, Copy)]\nstruct DmaDescriptor {\n    /// 0x0 Reserved.\n    reserved: u32,\n    /// 0x4 Source data end address.\n    source_end_addr: u32,\n    /// 0x8 Destination end address.\n    dest_end_addr: u32,\n    /// 0xC Link to next descriptor.\n    next_desc: u32,\n}\n\n// See section 22.6.3\n// UM11126, Rev. 2.8\n// The table must begin on a 512 byte boundary.\n#[repr(C, align(512))]\nstruct DmaDescriptorTable {\n    descriptors: [DmaDescriptor; CHANNEL_COUNT],\n}\n\n// DMA descriptors are stored in on-chip SRAM.\nstatic DMA_DESCRIPTORS: Mutex<RefCell<DmaDescriptorTable>> = Mutex::new(RefCell::new(DmaDescriptorTable {\n    descriptors: [DmaDescriptor {\n        reserved: 0,\n        source_end_addr: 0,\n        dest_end_addr: 0,\n        next_desc: 0,\n    }; CHANNEL_COUNT],\n}));\n\npub(crate) trait SealedChannel {}\ntrait SealedWord {}\n\n/// DMA channel interface.\n#[allow(private_bounds)]\npub trait Channel: PeripheralType + SealedChannel + Into<AnyChannel> + Sized + 'static {\n    /// Channel number.\n    fn number(&self) -> u8;\n\n    /// Channel registry block.\n    fn regs(&self) -> crate::pac::dma::Channel {\n        crate::pac::DMA0.channel(self.number() as _)\n    }\n}\n\n/// DMA word.\n#[allow(private_bounds)]\npub trait Word: SealedWord {\n    /// Word size.\n    fn size() -> crate::pac::dma::vals::Width;\n}\n\nimpl SealedWord for u8 {}\nimpl Word for u8 {\n    fn size() -> crate::pac::dma::vals::Width {\n        crate::pac::dma::vals::Width::BIT_8\n    }\n}\n\nimpl SealedWord for u16 {}\nimpl Word for u16 {\n    fn size() -> crate::pac::dma::vals::Width {\n        crate::pac::dma::vals::Width::BIT_16\n    }\n}\n\nimpl SealedWord for u32 {}\nimpl Word for u32 {\n    fn size() -> crate::pac::dma::vals::Width {\n        crate::pac::dma::vals::Width::BIT_32\n    }\n}\n\n/// Type erased DMA channel.\npub struct AnyChannel {\n    pub(crate) number: u8,\n}\n\nimpl_peripheral!(AnyChannel);\n\nimpl SealedChannel for AnyChannel {}\nimpl Channel for AnyChannel {\n    fn number(&self) -> u8 {\n        self.number\n    }\n}\n\nmacro_rules! impl_dma_channel {\n    ($instance:ident, $name:ident, $num:expr) => {\n        impl crate::dma::SealedChannel for crate::peripherals::$name {}\n        impl crate::dma::Channel for crate::peripherals::$name {\n            fn number(&self) -> u8 {\n                $num\n            }\n        }\n\n        impl From<peripherals::$name> for crate::dma::AnyChannel {\n            fn from(val: peripherals::$name) -> Self {\n                use crate::dma::Channel;\n\n                Self { number: val.number() }\n            }\n        }\n    };\n}\n"
  },
  {
    "path": "embassy-nxp/src/dma.rs",
    "content": "#![macro_use]\n//! Direct Memory Access (DMA) driver.\n\n#[cfg_attr(lpc55, path = \"./dma/lpc55.rs\")]\nmod inner;\npub use inner::*;\n"
  },
  {
    "path": "embassy-nxp/src/fmt.rs",
    "content": "//! Copied from embassy-rp\n#![macro_use]\n#![allow(unused)]\n\nuse core::fmt::{Debug, Display, LowerHex};\n\n#[cfg(all(feature = \"defmt\", feature = \"log\"))]\ncompile_error!(\"You may not enable both `defmt` and `log` features.\");\n\n#[collapse_debuginfo(yes)]\nmacro_rules! assert {\n    ($($x:tt)*) => {\n        {\n            #[cfg(not(feature = \"defmt\"))]\n            ::core::assert!($($x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::assert!($($x)*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! assert_eq {\n    ($($x:tt)*) => {\n        {\n            #[cfg(not(feature = \"defmt\"))]\n            ::core::assert_eq!($($x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::assert_eq!($($x)*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! assert_ne {\n    ($($x:tt)*) => {\n        {\n            #[cfg(not(feature = \"defmt\"))]\n            ::core::assert_ne!($($x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::assert_ne!($($x)*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! debug_assert {\n    ($($x:tt)*) => {\n        {\n            #[cfg(not(feature = \"defmt\"))]\n            ::core::debug_assert!($($x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::debug_assert!($($x)*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! debug_assert_eq {\n    ($($x:tt)*) => {\n        {\n            #[cfg(not(feature = \"defmt\"))]\n            ::core::debug_assert_eq!($($x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::debug_assert_eq!($($x)*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! debug_assert_ne {\n    ($($x:tt)*) => {\n        {\n            #[cfg(not(feature = \"defmt\"))]\n            ::core::debug_assert_ne!($($x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::debug_assert_ne!($($x)*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! todo {\n    ($($x:tt)*) => {\n        {\n            #[cfg(not(feature = \"defmt\"))]\n            ::core::todo!($($x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::todo!($($x)*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! unreachable {\n    ($($x:tt)*) => {\n        {\n            #[cfg(not(feature = \"defmt\"))]\n            ::core::unreachable!($($x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::unreachable!($($x)*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! unimplemented {\n    ($($x:tt)*) => {\n        {\n            #[cfg(not(feature = \"defmt\"))]\n            ::core::unimplemented!($($x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::unimplemented!($($x)*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! panic {\n    ($($x:tt)*) => {\n        {\n            #[cfg(not(feature = \"defmt\"))]\n            ::core::panic!($($x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::panic!($($x)*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! trace {\n    ($s:literal $(, $x:expr)* $(,)?) => {\n        {\n            #[cfg(feature = \"log\")]\n            ::log::trace!($s $(, $x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::trace!($s $(, $x)*);\n            #[cfg(not(any(feature = \"log\", feature=\"defmt\")))]\n            let _ = ($( & $x ),*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! debug {\n    ($s:literal $(, $x:expr)* $(,)?) => {\n        {\n            #[cfg(feature = \"log\")]\n            ::log::debug!($s $(, $x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::debug!($s $(, $x)*);\n            #[cfg(not(any(feature = \"log\", feature=\"defmt\")))]\n            let _ = ($( & $x ),*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! info {\n    ($s:literal $(, $x:expr)* $(,)?) => {\n        {\n            #[cfg(feature = \"log\")]\n            ::log::info!($s $(, $x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::info!($s $(, $x)*);\n            #[cfg(not(any(feature = \"log\", feature=\"defmt\")))]\n            let _ = ($( & $x ),*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! warn {\n    ($s:literal $(, $x:expr)* $(,)?) => {\n        {\n            #[cfg(feature = \"log\")]\n            ::log::warn!($s $(, $x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::warn!($s $(, $x)*);\n            #[cfg(not(any(feature = \"log\", feature=\"defmt\")))]\n            let _ = ($( & $x ),*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! error {\n    ($s:literal $(, $x:expr)* $(,)?) => {\n        {\n            #[cfg(feature = \"log\")]\n            ::log::error!($s $(, $x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::error!($s $(, $x)*);\n            #[cfg(not(any(feature = \"log\", feature=\"defmt\")))]\n            let _ = ($( & $x ),*);\n        }\n    };\n}\n\n#[cfg(feature = \"defmt\")]\n#[collapse_debuginfo(yes)]\nmacro_rules! unwrap {\n    ($($x:tt)*) => {\n        ::defmt::unwrap!($($x)*)\n    };\n}\n\n#[cfg(not(feature = \"defmt\"))]\n#[collapse_debuginfo(yes)]\nmacro_rules! unwrap {\n    ($arg:expr) => {\n        match $crate::fmt::Try::into_result($arg) {\n            ::core::result::Result::Ok(t) => t,\n            ::core::result::Result::Err(e) => {\n                ::core::panic!(\"unwrap of `{}` failed: {:?}\", ::core::stringify!($arg), e);\n            }\n        }\n    };\n    ($arg:expr, $($msg:expr),+ $(,)? ) => {\n        match $crate::fmt::Try::into_result($arg) {\n            ::core::result::Result::Ok(t) => t,\n            ::core::result::Result::Err(e) => {\n                ::core::panic!(\"unwrap of `{}` failed: {}: {:?}\", ::core::stringify!($arg), ::core::format_args!($($msg,)*), e);\n            }\n        }\n    }\n}\n\n#[derive(Debug, Copy, Clone, Eq, PartialEq)]\npub struct NoneError;\n\npub trait Try {\n    type Ok;\n    type Error;\n    fn into_result(self) -> Result<Self::Ok, Self::Error>;\n}\n\nimpl<T> Try for Option<T> {\n    type Ok = T;\n    type Error = NoneError;\n\n    #[inline]\n    fn into_result(self) -> Result<T, NoneError> {\n        self.ok_or(NoneError)\n    }\n}\n\nimpl<T, E> Try for Result<T, E> {\n    type Ok = T;\n    type Error = E;\n\n    #[inline]\n    fn into_result(self) -> Self {\n        self\n    }\n}\n\npub(crate) struct Bytes<'a>(pub &'a [u8]);\n\nimpl<'a> Debug for Bytes<'a> {\n    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {\n        write!(f, \"{:#02x?}\", self.0)\n    }\n}\n\nimpl<'a> Display for Bytes<'a> {\n    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {\n        write!(f, \"{:#02x?}\", self.0)\n    }\n}\n\nimpl<'a> LowerHex for Bytes<'a> {\n    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {\n        write!(f, \"{:#02x?}\", self.0)\n    }\n}\n\n#[cfg(feature = \"defmt\")]\nimpl<'a> defmt::Format for Bytes<'a> {\n    fn format(&self, fmt: defmt::Formatter) {\n        defmt::write!(fmt, \"{:02x}\", self.0)\n    }\n}\n"
  },
  {
    "path": "embassy-nxp/src/gpio/lpc55.rs",
    "content": "#![macro_use]\n\nuse embassy_hal_internal::{PeripheralType, impl_peripheral};\n\nuse crate::Peri;\nuse crate::pac::common::{RW, Reg};\nuse crate::pac::iocon::vals::{PioDigimode, PioMode};\nuse crate::pac::{GPIO, IOCON, SYSCON, iocon};\n\npub(crate) fn init() {\n    // Enable clocks for GPIO, PINT, and IOCON\n    SYSCON.ahbclkctrl0().modify(|w| {\n        w.set_gpio0(true);\n        w.set_gpio1(true);\n        w.set_mux(true);\n        w.set_iocon(true);\n    });\n    info!(\"GPIO initialized\");\n}\n\n/// The GPIO pin level for pins set on \"Digital\" mode.\n#[derive(Debug, Eq, PartialEq, Clone, Copy)]\npub enum Level {\n    /// Logical low. Corresponds to 0V.\n    Low,\n    /// Logical high. Corresponds to VDD.\n    High,\n}\n\n/// Pull setting for a GPIO input set on \"Digital\" mode.\n#[derive(Debug, Clone, Copy, Eq, PartialEq)]\npub enum Pull {\n    /// No pull.\n    None,\n    /// Internal pull-up resistor.\n    Up,\n    /// Internal pull-down resistor.\n    Down,\n}\n\n/// The LPC55 boards have two GPIO banks, each with 32 pins. This enum represents the two banks.\n#[derive(Debug, Eq, PartialEq, Clone, Copy)]\npub enum Bank {\n    Gpio0 = 0,\n    Gpio1 = 1,\n}\n\n/// GPIO output driver. Internally, this is a specialized [Flex] pin.\npub struct Output<'d> {\n    pub(crate) pin: Flex<'d>,\n}\n\nimpl<'d> Output<'d> {\n    /// Create GPIO output driver for a [Pin] with the provided [initial output](Level).\n    #[inline]\n    pub fn new(pin: Peri<'d, impl Pin>, initial_output: Level) -> Self {\n        let mut pin = Flex::new(pin);\n        pin.set_as_output();\n        let mut result = Self { pin };\n\n        match initial_output {\n            Level::High => result.set_high(),\n            Level::Low => result.set_low(),\n        };\n\n        result\n    }\n\n    pub fn set_high(&mut self) {\n        GPIO.set(self.pin.pin_bank() as usize)\n            .write(|w| w.set_setp(self.pin.bit()));\n    }\n\n    pub fn set_low(&mut self) {\n        GPIO.clr(self.pin.pin_bank() as usize)\n            .write(|w| w.set_clrp(self.pin.bit()));\n    }\n\n    pub fn toggle(&mut self) {\n        GPIO.not(self.pin.pin_bank() as usize)\n            .write(|w| w.set_notp(self.pin.bit()));\n    }\n\n    /// Get the current output level of the pin. Note that the value returned by this function is\n    /// the voltage level reported by the pin, not the value set by the output driver.\n    pub fn level(&self) -> Level {\n        let bits = GPIO.pin(self.pin.pin_bank() as usize).read().port();\n        if bits & self.pin.bit() != 0 {\n            Level::High\n        } else {\n            Level::Low\n        }\n    }\n}\n\n/// GPIO input driver. Internally, this is a specialized [Flex] pin.\npub struct Input<'d> {\n    pub(crate) pin: Flex<'d>,\n}\n\nimpl<'d> Input<'d> {\n    /// Create GPIO output driver for a [Pin] with the provided [Pull].\n    #[inline]\n    pub fn new(pin: Peri<'d, impl Pin>, pull: Pull) -> Self {\n        let mut pin = Flex::new(pin);\n        pin.set_as_input();\n        let mut result = Self { pin };\n        result.set_pull(pull);\n\n        result\n    }\n\n    /// Set the pull configuration for the pin. To disable the pull, use [Pull::None].\n    pub fn set_pull(&mut self, pull: Pull) {\n        self.pin.set_pull(pull);\n    }\n\n    /// Get the current input level of the pin.\n    pub fn read(&self) -> Level {\n        let bits = GPIO.pin(self.pin.pin_bank() as usize).read().port();\n        if bits & self.pin.bit() != 0 {\n            Level::High\n        } else {\n            Level::Low\n        }\n    }\n}\n\n/// A flexible GPIO (digital mode) pin whose mode is not yet determined. Under the hood, this is a\n/// reference to a type-erased pin called [\"AnyPin\"](AnyPin).\npub struct Flex<'d> {\n    pub(crate) pin: Peri<'d, AnyPin>,\n}\n\nimpl<'d> Flex<'d> {\n    /// Wrap the pin in a `Flex`.\n    ///\n    /// Note: you cannot assume that the pin will be in Digital mode after this call.\n    #[inline]\n    pub fn new(pin: Peri<'d, impl Pin>) -> Self {\n        Self { pin: pin.into() }\n    }\n\n    /// Get the bank of this pin. See also [Bank].\n    ///\n    /// # Example\n    ///\n    /// ```\n    /// use embassy_nxp::gpio::{Bank, Flex};\n    ///\n    /// let p = embassy_nxp::init(Default::default());\n    /// let pin = Flex::new(p.PIO1_15);\n    ///\n    /// assert_eq!(pin.pin_bank(), Bank::Bank1);\n    /// ```\n    pub fn pin_bank(&self) -> Bank {\n        self.pin.pin_bank()\n    }\n\n    /// Get the number of this pin within its bank. See also [Bank].\n    ///\n    /// # Example\n    ///\n    /// ```\n    /// use embassy_nxp::gpio::Flex;\n    ///\n    /// let p = embassy_nxp::init(Default::default());\n    /// let pin = Flex::new(p.PIO1_15);\n    ///\n    /// assert_eq!(pin.pin_number(), 15 as u8);\n    /// ```\n    pub fn pin_number(&self) -> u8 {\n        self.pin.pin_number()\n    }\n\n    /// Get the bit mask for this pin. Useful for setting or clearing bits in a register. Note:\n    /// PIOx_0 is bit 0, PIOx_1 is bit 1, etc.\n    ///\n    /// # Example\n    ///\n    /// ```\n    /// use embassy_nxp::gpio::Flex;\n    ///\n    /// let p = embassy_nxp::init(Default::default());\n    /// let pin = Flex::new(p.PIO1_3);\n    ///\n    /// assert_eq!(pin.bit(), 0b0000_1000);\n    /// ```\n    pub fn bit(&self) -> u32 {\n        1 << self.pin.pin_number()\n    }\n\n    /// Set the pull configuration for the pin. To disable the pull, use [Pull::None].\n    pub fn set_pull(&mut self, pull: Pull) {\n        self.pin.pio().modify(|w| match pull {\n            Pull::None => w.set_mode(PioMode::INACTIVE),\n            Pull::Up => w.set_mode(PioMode::PULL_UP),\n            Pull::Down => w.set_mode(PioMode::PULL_DOWN),\n        });\n    }\n\n    /// Set the pin to digital mode. This is required for using a pin as a GPIO pin. The default\n    /// setting for pins is (usually) non-digital.\n    fn set_as_digital(&mut self) {\n        self.pin.pio().modify(|w| {\n            w.set_digimode(PioDigimode::DIGITAL);\n        });\n    }\n\n    /// Set the pin in output mode. This implies setting the pin to digital mode, which this\n    /// function handles itself.\n    pub fn set_as_output(&mut self) {\n        self.set_as_digital();\n        GPIO.dirset(self.pin.pin_bank() as usize)\n            .write(|w| w.set_dirsetp(self.bit()))\n    }\n\n    pub fn set_as_input(&mut self) {\n        self.set_as_digital();\n        GPIO.dirclr(self.pin.pin_bank() as usize)\n            .write(|w| w.set_dirclrp(self.bit()))\n    }\n}\n\n/// Sealed trait for pins. This trait is sealed and cannot be implemented outside of this crate.\npub(crate) trait SealedPin: Sized {\n    fn pin_bank(&self) -> Bank;\n    fn pin_number(&self) -> u8;\n\n    #[inline]\n    fn pio(&self) -> Reg<iocon::regs::Pio, RW> {\n        match self.pin_bank() {\n            Bank::Gpio0 => IOCON.pio0(self.pin_number() as usize),\n            Bank::Gpio1 => IOCON.pio1(self.pin_number() as usize),\n        }\n    }\n}\n\n/// Interface for a Pin that can be configured by an [Input] or [Output] driver, or converted to an\n/// [AnyPin]. By default, this trait is sealed and cannot be implemented outside of the\n/// `embassy-nxp` crate due to the [SealedPin] trait.\n#[allow(private_bounds)]\npub trait Pin: PeripheralType + Into<AnyPin> + SealedPin + Sized + 'static {\n    /// Returns the pin number within a bank\n    #[inline]\n    fn pin(&self) -> u8 {\n        self.pin_number()\n    }\n\n    /// Returns the bank of this pin\n    #[inline]\n    fn bank(&self) -> Bank {\n        self.pin_bank()\n    }\n}\n\n/// Type-erased GPIO pin.\npub struct AnyPin {\n    pub(crate) pin_bank: Bank,\n    pub(crate) pin_number: u8,\n}\n\nimpl AnyPin {\n    /// Unsafely create a new type-erased pin.\n    ///\n    /// # Safety\n    ///\n    /// You must ensure that you’re only using one instance of this type at a time.\n    pub unsafe fn steal(pin_bank: Bank, pin_number: u8) -> Peri<'static, Self> {\n        Peri::new_unchecked(Self { pin_bank, pin_number })\n    }\n}\n\nimpl_peripheral!(AnyPin);\n\nimpl Pin for AnyPin {}\nimpl SealedPin for AnyPin {\n    #[inline]\n    fn pin_bank(&self) -> Bank {\n        self.pin_bank\n    }\n\n    #[inline]\n    fn pin_number(&self) -> u8 {\n        self.pin_number\n    }\n}\n\nmacro_rules! impl_pin {\n    ($name:ident, $bank:ident, $pin_num:expr) => {\n        impl crate::gpio::Pin for peripherals::$name {}\n        impl crate::gpio::SealedPin for peripherals::$name {\n            #[inline]\n            fn pin_bank(&self) -> crate::gpio::Bank {\n                crate::gpio::Bank::$bank\n            }\n\n            #[inline]\n            fn pin_number(&self) -> u8 {\n                $pin_num\n            }\n        }\n\n        impl From<peripherals::$name> for crate::gpio::AnyPin {\n            fn from(val: peripherals::$name) -> Self {\n                use crate::gpio::SealedPin;\n\n                Self {\n                    pin_bank: val.pin_bank(),\n                    pin_number: val.pin_number(),\n                }\n            }\n        }\n    };\n}\n"
  },
  {
    "path": "embassy-nxp/src/gpio/rt1xxx.rs",
    "content": "#![macro_use]\n\nuse core::future::Future;\nuse core::ops::Not;\nuse core::pin::Pin as FuturePin;\nuse core::task::{Context, Poll};\n\nuse embassy_hal_internal::{Peri, PeripheralType, impl_peripheral};\nuse embassy_sync::waitqueue::AtomicWaker;\nuse nxp_pac::gpio::vals::Icr;\nuse nxp_pac::iomuxc::vals::Pus;\n\nuse crate::chip::{iomuxc_mux, iomuxc_pad};\nuse crate::pac::common::{RW, Reg};\nuse crate::pac::gpio::Gpio;\n#[cfg(feature = \"rt\")]\nuse crate::pac::interrupt;\nuse crate::pac::iomuxc::regs::{Ctl, MuxCtl};\nuse crate::pac::{self};\n\n/// The GPIO pin level for pins set on \"Digital\" mode.\n#[derive(Debug, Eq, PartialEq, Clone, Copy)]\npub enum Level {\n    /// Logical low. Corresponds to 0V.\n    Low,\n    /// Logical high. Corresponds to VDD.\n    High,\n}\n\nimpl From<bool> for Level {\n    fn from(val: bool) -> Self {\n        match val {\n            true => Self::High,\n            false => Self::Low,\n        }\n    }\n}\n\nimpl From<Level> for bool {\n    fn from(level: Level) -> bool {\n        match level {\n            Level::Low => false,\n            Level::High => true,\n        }\n    }\n}\n\nimpl Not for Level {\n    type Output = Self;\n\n    fn not(self) -> Self::Output {\n        match self {\n            Level::Low => Level::High,\n            Level::High => Level::Low,\n        }\n    }\n}\n\n/// Pull setting for a GPIO input set on \"Digital\" mode.\n#[derive(Debug, Clone, Copy, Eq, PartialEq)]\npub enum Pull {\n    /// No pull.\n    None,\n\n    // TODO: What Does PUE::KEEPER mean here?\n\n    // 22 kOhm pull-up resistor.\n    Up22K,\n\n    // 47 kOhm pull-up resistor.\n    Up47K,\n\n    // 100 kOhm pull-up resistor.\n    Up100K,\n\n    // 100 kOhm pull-down resistor.\n    Down100K,\n}\n\n/// Drive strength of an output\n#[derive(Copy, Clone, Debug, Eq, PartialEq)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub enum Drive {\n    Disabled,\n    _150R,\n    _75R,\n    _50R,\n    _37R,\n    _30R,\n    _25R,\n    _20R,\n}\n\n/// Slew rate of an output\n#[derive(Copy, Clone, Debug, Eq, PartialEq)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub enum SlewRate {\n    Slow,\n\n    Fast,\n}\n\n#[derive(Clone, Copy, Debug, PartialEq, Eq)]\npub enum Bank {\n    /// Bank 1\n    #[cfg(gpio1)]\n    Gpio1,\n\n    /// Bank 2\n    #[cfg(gpio2)]\n    Gpio2,\n\n    /// Bank 3\n    #[cfg(gpio3)]\n    Gpio3,\n\n    /// Bank 4\n    #[cfg(gpio4)]\n    Gpio4,\n\n    /// Bank 5\n    #[cfg(gpio5)]\n    Gpio5,\n\n    #[cfg(gpio10)]\n    /// Bank 10\n    Gpio10,\n}\n\n/// GPIO flexible pin.\n///\n/// This pin can either be a disconnected, input, or output pin, or both. The level register bit will remain\n/// set while not in output mode, so the pin's level will be 'remembered' when it is not in output\n/// mode.\npub struct Flex<'d> {\n    pub(crate) pin: Peri<'d, AnyPin>,\n}\n\nimpl<'d> Flex<'d> {\n    /// Wrap the pin in a `Flex`.\n    ///\n    /// The pin remains disconnected. The initial output level is unspecified, but can be changed\n    /// before the pin is put into output mode.\n    #[inline]\n    pub fn new(pin: Peri<'d, impl Pin>) -> Self {\n        Self { pin: pin.into() }\n    }\n\n    /// Set the pin's pull.\n    #[inline]\n    pub fn set_pull(&mut self, pull: Pull) {\n        let (pke, pue, pus) = match pull {\n            Pull::None => (false, true, Pus::PUS_0_100K_OHM_PULL_DOWN),\n            Pull::Up22K => (true, true, Pus::PUS_3_22K_OHM_PULL_UP),\n            Pull::Up47K => (true, true, Pus::PUS_1_47K_OHM_PULL_UP),\n            Pull::Up100K => (true, true, Pus::PUS_2_100K_OHM_PULL_UP),\n            Pull::Down100K => (true, true, Pus::PUS_0_100K_OHM_PULL_DOWN),\n        };\n\n        self.pin.pad().modify(|w| {\n            w.set_pke(pke);\n            w.set_pue(pue);\n            w.set_pus(pus);\n        });\n    }\n\n    // Set the pin's slew rate.\n    #[inline]\n    pub fn set_slewrate(&mut self, rate: SlewRate) {\n        self.pin.pad().modify(|w| {\n            w.set_sre(match rate {\n                SlewRate::Slow => false,\n                SlewRate::Fast => true,\n            });\n        });\n    }\n\n    /// Set the pin's Schmitt trigger.\n    #[inline]\n    pub fn set_schmitt(&mut self, enable: bool) {\n        self.pin.pad().modify(|w| {\n            w.set_hys(enable);\n        });\n    }\n\n    /// Put the pin into input mode.\n    ///\n    /// The pull setting is left unchanged.\n    #[inline]\n    pub fn set_as_input(&mut self) {\n        self.pin.mux().modify(|w| {\n            w.set_mux_mode(GPIO_MUX_MODE);\n        });\n\n        // Setting direction is RMW\n        critical_section::with(|_cs| {\n            self.pin.block().gdir().modify(|w| {\n                w.set_gdir(self.pin.pin_number() as usize, false);\n            });\n        })\n    }\n\n    /// Put the pin into output mode.\n    ///\n    /// The pin level will be whatever was set before (or low by default). If you want it to begin\n    /// at a specific level, call `set_high`/`set_low` on the pin first.\n    #[inline]\n    pub fn set_as_output(&mut self) {\n        self.pin.mux().modify(|w| {\n            w.set_mux_mode(GPIO_MUX_MODE);\n        });\n\n        // Setting direction is RMW\n        critical_section::with(|_cs| {\n            self.pin.block().gdir().modify(|w| {\n                w.set_gdir(self.pin.pin_number() as usize, true);\n            });\n        })\n    }\n\n    /// Put the pin into input + open-drain output mode.\n    ///\n    /// The hardware will drive the line low if you set it to low, and will leave it floating if you set\n    /// it to high, in which case you can read the input to figure out whether another device\n    /// is driving the line low.\n    ///\n    /// The pin level will be whatever was set before (or low by default). If you want it to begin\n    /// at a specific level, call `set_high`/`set_low` on the pin first.\n    ///\n    /// The internal weak pull-up and pull-down resistors will be disabled.\n    #[inline]\n    pub fn set_as_input_output(&mut self) {\n        self.pin.pad().modify(|w| {\n            w.set_ode(true);\n        });\n    }\n\n    /// Set the pin as \"disconnected\", ie doing nothing and consuming the lowest\n    /// amount of power possible.\n    ///\n    /// This is currently the same as [`Self::set_as_analog()`] but is semantically different\n    /// really. Drivers should `set_as_disconnected()` pins when dropped.\n    ///\n    /// Note that this also disables the pull-up and pull-down resistors.\n    #[inline]\n    pub fn set_as_disconnected(&mut self) {\n        self.pin.pad().modify(|w| {\n            w.set_ode(false);\n            w.set_pke(false);\n            w.set_pue(false);\n            w.set_pus(Pus::PUS_0_100K_OHM_PULL_DOWN);\n        });\n    }\n\n    /// Get whether the pin input level is high.\n    #[inline]\n    pub fn is_high(&self) -> bool {\n        self.pin.block().psr().read().psr(self.pin.pin_number() as usize)\n    }\n\n    /// Get whether the pin input level is low.\n    #[inline]\n    pub fn is_low(&self) -> bool {\n        !self.is_high()\n    }\n\n    /// Returns current pin level\n    #[inline]\n    pub fn get_level(&self) -> Level {\n        self.is_high().into()\n    }\n\n    /// Set the output as high.\n    #[inline]\n    pub fn set_high(&mut self) {\n        self.pin.block().dr_set().write(|w| {\n            w.set_dr_set(self.pin.pin_number() as usize, true);\n        });\n    }\n\n    /// Set the output as low.\n    #[inline]\n    pub fn set_low(&mut self) {\n        self.pin.block().dr_clear().write(|w| {\n            w.set_dr_clear(self.pin.pin_number() as usize, true);\n        });\n    }\n\n    /// Toggle pin output\n    #[inline]\n    pub fn toggle(&mut self) {\n        self.pin.block().dr_toggle().write(|w| {\n            w.set_dr_toggle(self.pin.pin_number() as usize, true);\n        });\n    }\n\n    /// Set the output level.\n    #[inline]\n    pub fn set_level(&mut self, level: Level) {\n        match level {\n            Level::Low => self.set_low(),\n            Level::High => self.set_high(),\n        }\n    }\n\n    /// Get the current pin output level.\n    #[inline]\n    pub fn get_output_level(&self) -> Level {\n        self.is_set_high().into()\n    }\n\n    /// Is the output level high?\n    ///\n    /// If the [`Flex`] is set as an input, then this is equivalent to [`Flex::is_high`].\n    #[inline]\n    pub fn is_set_high(&self) -> bool {\n        self.pin.block().dr().read().dr(self.pin.pin_number() as usize)\n    }\n\n    /// Is the output level low?\n    ///\n    /// If the [`Flex`] is set as an input, then this is equivalent to [`Flex::is_low`].\n    #[inline]\n    pub fn is_set_low(&self) -> bool {\n        !self.is_set_high()\n    }\n\n    /// Wait until the pin is high. If it is already high, return immediately.\n    #[inline]\n    pub async fn wait_for_high(&mut self) {\n        InputFuture::new(self.pin.reborrow(), InterruptConfiguration::High).await\n    }\n\n    /// Wait until the pin is low. If it is already low, return immediately.\n    #[inline]\n    pub async fn wait_for_low(&mut self) {\n        InputFuture::new(self.pin.reborrow(), InterruptConfiguration::Low).await\n    }\n\n    /// Wait for the pin to undergo a transition from low to high.\n    #[inline]\n    pub async fn wait_for_rising_edge(&mut self) {\n        InputFuture::new(self.pin.reborrow(), InterruptConfiguration::RisingEdge).await\n    }\n\n    /// Wait for the pin to undergo a transition from high to low.\n    #[inline]\n    pub async fn wait_for_falling_edge(&mut self) {\n        InputFuture::new(self.pin.reborrow(), InterruptConfiguration::FallingEdge).await\n    }\n\n    /// Wait for the pin to undergo any transition, i.e low to high OR high to low.\n    #[inline]\n    pub async fn wait_for_any_edge(&mut self) {\n        InputFuture::new(self.pin.reborrow(), InterruptConfiguration::AnyEdge).await\n    }\n}\n\nimpl<'d> Drop for Flex<'d> {\n    fn drop(&mut self) {\n        self.set_as_disconnected();\n    }\n}\n\n/// GPIO input driver.\npub struct Input<'d> {\n    pin: Flex<'d>,\n}\n\nimpl<'d> Input<'d> {\n    /// Create GPIO input driver for a [Pin] with the provided [Pull] configuration.\n    #[inline]\n    pub fn new(pin: Peri<'d, impl Pin>, pull: Pull) -> Self {\n        let mut pin = Flex::new(pin);\n        pin.set_as_input();\n        pin.set_pull(pull);\n        Self { pin }\n    }\n\n    /// Get whether the pin input level is high.\n    #[inline]\n    pub fn is_high(&self) -> bool {\n        self.pin.is_high()\n    }\n\n    /// Get whether the pin input level is low.\n    #[inline]\n    pub fn is_low(&self) -> bool {\n        self.pin.is_low()\n    }\n\n    /// Get the current pin input level.\n    #[inline]\n    pub fn get_level(&self) -> Level {\n        self.pin.get_level()\n    }\n\n    /// Wait until the pin is high. If it is already high, return immediately.\n    #[inline]\n    pub async fn wait_for_high(&mut self) {\n        self.pin.wait_for_high().await\n    }\n\n    /// Wait until the pin is low. If it is already low, return immediately.\n    #[inline]\n    pub async fn wait_for_low(&mut self) {\n        self.pin.wait_for_low().await\n    }\n\n    /// Wait for the pin to undergo a transition from low to high.\n    #[inline]\n    pub async fn wait_for_rising_edge(&mut self) {\n        self.pin.wait_for_rising_edge().await\n    }\n\n    /// Wait for the pin to undergo a transition from high to low.\n    #[inline]\n    pub async fn wait_for_falling_edge(&mut self) {\n        self.pin.wait_for_falling_edge().await\n    }\n\n    /// Wait for the pin to undergo any transition, i.e low to high OR high to low.\n    #[inline]\n    pub async fn wait_for_any_edge(&mut self) {\n        self.pin.wait_for_any_edge().await\n    }\n}\n\n/// GPIO output driver.\n///\n/// Note that pins will **return to their floating state** when `Output` is dropped.\n/// If pins should retain their state indefinitely, either keep ownership of the\n/// `Output`, or pass it to [`core::mem::forget`].\npub struct Output<'d> {\n    pin: Flex<'d>,\n}\n\nimpl<'d> Output<'d> {\n    /// Create GPIO output driver for a [Pin] with the provided [Level] configuration.\n    #[inline]\n    pub fn new(pin: Peri<'d, impl Pin>, initial_output: Level) -> Self {\n        let mut pin = Flex::new(pin);\n        pin.set_as_output();\n        pin.set_level(initial_output);\n        Self { pin }\n    }\n\n    /// Set the output as high.\n    #[inline]\n    pub fn set_high(&mut self) {\n        self.pin.set_high();\n    }\n\n    /// Set the output as low.\n    #[inline]\n    pub fn set_low(&mut self) {\n        self.pin.set_low();\n    }\n\n    /// Set the output level.\n    #[inline]\n    pub fn set_level(&mut self, level: Level) {\n        self.pin.set_level(level)\n    }\n\n    /// Is the output pin set as high?\n    #[inline]\n    pub fn is_set_high(&self) -> bool {\n        self.pin.is_set_high()\n    }\n\n    /// Is the output pin set as low?\n    #[inline]\n    pub fn is_set_low(&self) -> bool {\n        self.pin.is_set_low()\n    }\n\n    /// What level output is set to\n    #[inline]\n    pub fn get_output_level(&self) -> Level {\n        self.pin.get_output_level()\n    }\n\n    /// Toggle pin output\n    #[inline]\n    pub fn toggle(&mut self) {\n        self.pin.toggle();\n    }\n}\n\n/// GPIO output open-drain driver.\n///\n/// Note that pins will **return to their floating state** when `OutputOpenDrain` is dropped.\n/// If pins should retain their state indefinitely, either keep ownership of the\n/// `OutputOpenDrain`, or pass it to [`core::mem::forget`].\npub struct OutputOpenDrain<'d> {\n    pin: Flex<'d>,\n}\n\nimpl<'d> OutputOpenDrain<'d> {\n    /// Create a new GPIO open drain output driver for a [Pin] with the provided [Level].\n    #[inline]\n    pub fn new(pin: Peri<'d, impl Pin>, initial_output: Level) -> Self {\n        let mut pin = Flex::new(pin);\n        pin.set_level(initial_output);\n        pin.set_as_input_output();\n        Self { pin }\n    }\n\n    /// Get whether the pin input level is high.\n    #[inline]\n    pub fn is_high(&self) -> bool {\n        !self.pin.is_low()\n    }\n\n    /// Get whether the pin input level is low.\n    #[inline]\n    pub fn is_low(&self) -> bool {\n        self.pin.is_low()\n    }\n\n    /// Get the current pin input level.\n    #[inline]\n    pub fn get_level(&self) -> Level {\n        self.pin.get_level()\n    }\n\n    /// Set the output as high.\n    #[inline]\n    pub fn set_high(&mut self) {\n        self.pin.set_high();\n    }\n\n    /// Set the output as low.\n    #[inline]\n    pub fn set_low(&mut self) {\n        self.pin.set_low();\n    }\n\n    /// Set the output level.\n    #[inline]\n    pub fn set_level(&mut self, level: Level) {\n        self.pin.set_level(level);\n    }\n\n    /// Get whether the output level is set to high.\n    #[inline]\n    pub fn is_set_high(&self) -> bool {\n        self.pin.is_set_high()\n    }\n\n    /// Get whether the output level is set to low.\n    #[inline]\n    pub fn is_set_low(&self) -> bool {\n        self.pin.is_set_low()\n    }\n\n    /// Get the current output level.\n    #[inline]\n    pub fn get_output_level(&self) -> Level {\n        self.pin.get_output_level()\n    }\n\n    /// Toggle pin output\n    #[inline]\n    pub fn toggle(&mut self) {\n        self.pin.toggle()\n    }\n\n    /// Wait until the pin is high. If it is already high, return immediately.\n    #[inline]\n    pub async fn wait_for_high(&mut self) {\n        self.pin.wait_for_high().await\n    }\n\n    /// Wait until the pin is low. If it is already low, return immediately.\n    #[inline]\n    pub async fn wait_for_low(&mut self) {\n        self.pin.wait_for_low().await\n    }\n\n    /// Wait for the pin to undergo a transition from low to high.\n    #[inline]\n    pub async fn wait_for_rising_edge(&mut self) {\n        self.pin.wait_for_rising_edge().await\n    }\n\n    /// Wait for the pin to undergo a transition from high to low.\n    #[inline]\n    pub async fn wait_for_falling_edge(&mut self) {\n        self.pin.wait_for_falling_edge().await\n    }\n\n    /// Wait for the pin to undergo any transition, i.e low to high OR high to low.\n    #[inline]\n    pub async fn wait_for_any_edge(&mut self) {\n        self.pin.wait_for_any_edge().await\n    }\n}\n\n#[allow(private_bounds)]\npub trait Pin: PeripheralType + Into<AnyPin> + SealedPin + Sized + 'static {\n    /// Returns the pin number within a bank\n    #[inline]\n    fn pin(&self) -> u8 {\n        self.pin_number()\n    }\n\n    #[inline]\n    fn bank(&self) -> Bank {\n        self._bank()\n    }\n}\n\n/// Type-erased GPIO pin.\npub struct AnyPin {\n    pub(crate) pin_number: u8,\n    pub(crate) bank: Bank,\n}\n\nimpl AnyPin {\n    /// Unsafely create a new type-erased pin.\n    ///\n    /// # Safety\n    ///\n    /// You must ensure that you’re only using one instance of this type at a time.\n    pub unsafe fn steal(bank: Bank, pin_number: u8) -> Peri<'static, Self> {\n        Peri::new_unchecked(Self { pin_number, bank })\n    }\n}\n\nimpl_peripheral!(AnyPin);\n\nimpl Pin for AnyPin {}\nimpl SealedPin for AnyPin {\n    #[inline]\n    fn pin_number(&self) -> u8 {\n        self.pin_number\n    }\n\n    #[inline]\n    fn _bank(&self) -> Bank {\n        self.bank\n    }\n}\n\n// Impl details\n\n/// Mux mode for GPIO pins. This is constant across all RT1xxx parts.\nconst GPIO_MUX_MODE: u8 = 0b101;\n\n// FIXME: These don't always need to be 32 entries. GPIO5 on RT1101 contains a single pin and GPIO2 only 14.\n#[cfg(gpio1)]\nstatic GPIO1_WAKERS: [AtomicWaker; 32] = [const { AtomicWaker::new() }; 32];\n#[cfg(gpio2)]\nstatic GPIO2_WAKERS: [AtomicWaker; 32] = [const { AtomicWaker::new() }; 32];\n#[cfg(gpio3)]\nstatic GPIO3_WAKERS: [AtomicWaker; 32] = [const { AtomicWaker::new() }; 32];\n#[cfg(gpio4)]\nstatic GPIO4_WAKERS: [AtomicWaker; 32] = [const { AtomicWaker::new() }; 32];\n#[cfg(gpio5)]\nstatic GPIO5_WAKERS: [AtomicWaker; 32] = [const { AtomicWaker::new() }; 32];\n#[cfg(gpio10)]\nstatic GPIO10_WAKERS: [AtomicWaker; 32] = [const { AtomicWaker::new() }; 32];\n\n/// Sealed trait for pins. This trait is sealed and cannot be implemented outside of this crate.\npub(crate) trait SealedPin: Sized {\n    fn pin_number(&self) -> u8;\n\n    fn _bank(&self) -> Bank;\n\n    #[inline]\n    fn block(&self) -> Gpio {\n        match self._bank() {\n            #[cfg(gpio1)]\n            Bank::Gpio1 => pac::GPIO1,\n            #[cfg(gpio2)]\n            Bank::Gpio2 => pac::GPIO2,\n            #[cfg(gpio3)]\n            Bank::Gpio3 => pac::GPIO3,\n            #[cfg(gpio4)]\n            Bank::Gpio4 => pac::GPIO4,\n            #[cfg(gpio5)]\n            Bank::Gpio5 => pac::GPIO5,\n            #[cfg(gpio10)]\n            Bank::Gpio10 => pac::GPIO10,\n        }\n    }\n\n    #[inline]\n    fn mux(&self) -> Reg<MuxCtl, RW> {\n        // SAFETY: The generated mux address table is valid since it is generated from the SVD files.\n        let address = unsafe { iomuxc_mux(self._bank(), self.pin_number()).unwrap_unchecked() };\n\n        // SAFETY: The register at the address is an instance of MuxCtl.\n        unsafe { Reg::from_ptr(address as *mut _) }\n    }\n\n    #[inline]\n    fn pad(&self) -> Reg<Ctl, RW> {\n        let address = iomuxc_pad(self._bank(), self.pin_number());\n\n        // SAFETY: The register at the address is an instance of Ctl.\n        unsafe { Reg::from_ptr(address as *mut _) }\n    }\n\n    fn waker(&self) -> &AtomicWaker {\n        match self._bank() {\n            #[cfg(gpio1)]\n            Bank::Gpio1 => &GPIO1_WAKERS[self.pin_number() as usize],\n            #[cfg(gpio2)]\n            Bank::Gpio2 => &GPIO2_WAKERS[self.pin_number() as usize],\n            #[cfg(gpio3)]\n            Bank::Gpio3 => &GPIO3_WAKERS[self.pin_number() as usize],\n            #[cfg(gpio4)]\n            Bank::Gpio4 => &GPIO4_WAKERS[self.pin_number() as usize],\n            #[cfg(gpio5)]\n            Bank::Gpio5 => &GPIO5_WAKERS[self.pin_number() as usize],\n            #[cfg(gpio10)]\n            Bank::Gpio10 => &GPIO10_WAKERS[self.pin_number() as usize],\n        }\n    }\n}\n\n/// This enum matches the layout of Icr.\nenum InterruptConfiguration {\n    Low,\n    High,\n    RisingEdge,\n    FallingEdge,\n    AnyEdge,\n}\n\n#[must_use = \"futures do nothing unless you `.await` or poll them\"]\nstruct InputFuture<'d> {\n    pin: Peri<'d, AnyPin>,\n}\n\nimpl<'d> InputFuture<'d> {\n    fn new(pin: Peri<'d, AnyPin>, config: InterruptConfiguration) -> Self {\n        let block = pin.block();\n\n        let (icr, edge_sel) = match config {\n            InterruptConfiguration::Low => (Icr::LOW_LEVEL, false),\n            InterruptConfiguration::High => (Icr::HIGH_LEVEL, false),\n            InterruptConfiguration::RisingEdge => (Icr::RISING_EDGE, false),\n            InterruptConfiguration::FallingEdge => (Icr::FALLING_EDGE, false),\n            InterruptConfiguration::AnyEdge => (Icr::FALLING_EDGE, true),\n        };\n\n        let index = if pin.pin_number() > 15 { 1 } else { 0 };\n\n        // Interrupt configuration performs RMW\n        critical_section::with(|_cs| {\n            // Disable interrupt so a level/edge detection change does not cause ISR to be set.\n            block.imr().modify(|w| {\n                w.set_imr(pin.pin_number() as usize, false);\n            });\n\n            block.icr(index).modify(|w| {\n                w.set_pin(pin.pin_number() as usize, icr);\n            });\n\n            block.edge_sel().modify(|w| {\n                w.set_edge_sel(pin.pin_number() as usize, edge_sel);\n            });\n\n            // Clear the previous interrupt.\n            block.isr().modify(|w| {\n                // \"Status flags are cleared by writing a 1 to the corresponding bit position.\"\n                w.set_isr(pin.pin_number() as usize, true);\n            });\n        });\n\n        Self { pin }\n    }\n}\n\nimpl<'d> Future for InputFuture<'d> {\n    type Output = ();\n\n    fn poll(self: FuturePin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {\n        // We need to register/re-register the waker for each poll because any\n        // calls to wake will deregister the waker.\n        let waker = self.pin.waker();\n        waker.register(cx.waker());\n\n        // Enabling interrupt is RMW\n        critical_section::with(|_cs| {\n            self.pin.block().imr().modify(|w| {\n                w.set_imr(self.pin.pin_number() as usize, true);\n            });\n        });\n\n        let isr = self.pin.block().isr().read();\n\n        if isr.isr(self.pin.pin_number() as usize) {\n            return Poll::Ready(());\n        }\n\n        Poll::Pending\n    }\n}\n\nmacro_rules! impl_pin {\n    ($name: ident, $bank: ident, $pin_num: expr) => {\n        impl crate::gpio::Pin for crate::peripherals::$name {}\n        impl crate::gpio::SealedPin for crate::peripherals::$name {\n            #[inline]\n            fn pin_number(&self) -> u8 {\n                $pin_num\n            }\n\n            #[inline]\n            fn _bank(&self) -> crate::gpio::Bank {\n                crate::gpio::Bank::$bank\n            }\n        }\n\n        impl From<peripherals::$name> for crate::gpio::AnyPin {\n            fn from(val: peripherals::$name) -> Self {\n                use crate::gpio::SealedPin;\n\n                Self {\n                    pin_number: val.pin_number(),\n                    bank: val._bank(),\n                }\n            }\n        }\n    };\n}\n\npub(crate) fn init() {\n    #[cfg(feature = \"rt\")]\n    unsafe {\n        use embassy_hal_internal::interrupt::InterruptExt;\n\n        pac::Interrupt::GPIO1_COMBINED_0_15.enable();\n        pac::Interrupt::GPIO1_COMBINED_16_31.enable();\n        pac::Interrupt::GPIO2_COMBINED_0_15.enable();\n        pac::Interrupt::GPIO5_COMBINED_0_15.enable();\n    }\n}\n\n/// IRQ handler for GPIO pins.\n///\n/// If `high_bits` is false, then the interrupt is for pins 0 through 15. If true, then the interrupt\n/// is for pins 16 through 31\n#[cfg(feature = \"rt\")]\nfn irq_handler(block: Gpio, wakers: &[AtomicWaker; 32], high_bits: bool) {\n    use crate::BitIter;\n\n    let isr = block.isr().read().0;\n    let imr = block.imr().read().0;\n    let mask = if high_bits { 0xFFFF_0000 } else { 0x0000_FFFF };\n    let bits = isr & imr & mask;\n\n    for bit in BitIter(bits) {\n        wakers[bit as usize].wake();\n\n        // Disable further interrupts for this pin. The input future will check ISR (which is kept\n        // until reset).\n        block.imr().modify(|w| {\n            w.set_imr(bit as usize, false);\n        });\n    }\n}\n\n#[cfg(all(any(feature = \"mimxrt1011\", feature = \"mimxrt1062\"), feature = \"rt\"))]\n#[interrupt]\nfn GPIO1_COMBINED_0_15() {\n    irq_handler(pac::GPIO1, &GPIO1_WAKERS, false);\n}\n\n#[cfg(all(any(feature = \"mimxrt1011\", feature = \"mimxrt1062\"), feature = \"rt\"))]\n#[interrupt]\nfn GPIO1_COMBINED_16_31() {\n    irq_handler(pac::GPIO1, &GPIO1_WAKERS, true);\n}\n\n#[cfg(all(any(feature = \"mimxrt1011\", feature = \"mimxrt1062\"), feature = \"rt\"))]\n#[interrupt]\nfn GPIO2_COMBINED_0_15() {\n    irq_handler(pac::GPIO2, &GPIO2_WAKERS, false);\n}\n\n#[cfg(all(feature = \"mimxrt1062\", feature = \"rt\"))]\n#[interrupt]\nfn GPIO2_COMBINED_16_31() {\n    irq_handler(pac::GPIO2, &GPIO2_WAKERS, true);\n}\n\n#[cfg(all(feature = \"mimxrt1062\", feature = \"rt\"))]\n#[interrupt]\nfn GPIO3_COMBINED_0_15() {\n    irq_handler(pac::GPIO3, &GPIO3_WAKERS, false);\n}\n\n#[cfg(all(feature = \"mimxrt1062\", feature = \"rt\"))]\n#[interrupt]\nfn GPIO3_COMBINED_16_31() {\n    irq_handler(pac::GPIO3, &GPIO3_WAKERS, true);\n}\n\n#[cfg(all(feature = \"mimxrt1062\", feature = \"rt\"))]\n#[interrupt]\nfn GPIO4_COMBINED_0_15() {\n    irq_handler(pac::GPIO4, &GPIO4_WAKERS, false);\n}\n\n#[cfg(all(feature = \"mimxrt1062\", feature = \"rt\"))]\n#[interrupt]\nfn GPIO4_COMBINED_16_31() {\n    irq_handler(pac::GPIO4, &GPIO4_WAKERS, true);\n}\n\n#[cfg(all(any(feature = \"mimxrt1011\", feature = \"mimxrt1062\"), feature = \"rt\"))]\n#[interrupt]\nfn GPIO5_COMBINED_0_15() {\n    irq_handler(pac::GPIO5, &GPIO5_WAKERS, false);\n}\n"
  },
  {
    "path": "embassy-nxp/src/gpio.rs",
    "content": "//! General purpose input/output (GPIO) driver.\n#![macro_use]\n\n#[cfg_attr(lpc55, path = \"./gpio/lpc55.rs\")]\n#[cfg_attr(rt1xxx, path = \"./gpio/rt1xxx.rs\")]\nmod inner;\npub use inner::*;\n"
  },
  {
    "path": "embassy-nxp/src/iomuxc.rs",
    "content": "#![macro_use]\n\n/// An IOMUXC pad.\n///\n/// This trait does not imply that GPIO can be used with this pad. [`Pin`](crate::gpio::Pin) must\n/// also be implemented for GPIO.\n#[allow(private_bounds)]\npub trait Pad: SealedPad {}\n\npub(crate) trait SealedPad {\n    /// Address of the pad register for this pad.\n    const PAD: *mut ();\n\n    /// Address of the mux register for this pad.\n    ///\n    /// Some pads do not allow muxing (e.g. ONOFF).\n    const MUX: Option<*mut ()>;\n}\n\nmacro_rules! impl_iomuxc_pad {\n    ($name: ident, $pad: expr, $mux: expr) => {\n        impl crate::iomuxc::SealedPad for crate::peripherals::$name {\n            const PAD: *mut () = $pad as *mut ();\n            const MUX: Option<*mut ()> = Some($mux as *mut ());\n        }\n\n        impl crate::iomuxc::Pad for crate::peripherals::$name {}\n    };\n}\n"
  },
  {
    "path": "embassy-nxp/src/lib.rs",
    "content": "#![no_std]\n#![allow(unsafe_op_in_unsafe_fn)]\n\n// This mod MUST go first, so that the others see its macros.\npub(crate) mod fmt;\n\n#[cfg(lpc55)]\npub mod dma;\npub mod gpio;\n#[cfg(lpc55)]\npub mod pint;\n#[cfg(lpc55)]\npub mod pwm;\n#[cfg(lpc55)]\npub mod sct;\n#[cfg(lpc55)]\npub mod usart;\n\n#[cfg(rt1xxx)]\nmod iomuxc;\n\n#[cfg(feature = \"_time_driver\")]\n#[cfg_attr(feature = \"time-driver-pit\", path = \"time_driver/pit.rs\")]\n#[cfg_attr(feature = \"time-driver-rtc\", path = \"time_driver/rtc.rs\")]\nmod time_driver;\n\n// This mod MUST go last, so that it sees all the `impl_foo!` macros\n#[cfg_attr(lpc55, path = \"chips/lpc55.rs\")]\n#[cfg_attr(feature = \"mimxrt1011\", path = \"chips/mimxrt1011.rs\")]\n#[cfg_attr(feature = \"mimxrt1062\", path = \"chips/mimxrt1062.rs\")]\nmod chip;\n\npub use chip::{Peripherals, interrupt, peripherals};\npub use embassy_hal_internal::{Peri, PeripheralType};\n#[cfg(feature = \"unstable-pac\")]\npub use nxp_pac as pac;\n#[cfg(not(feature = \"unstable-pac\"))]\npub(crate) use nxp_pac as pac;\n\n/// Macro to bind interrupts to handlers.\n/// (Copied from `embassy-rp`)\n/// This defines the right interrupt handlers, and creates a unit struct (like `struct Irqs;`)\n/// and implements the right [`Binding`]s for it. You can pass this struct to drivers to\n/// prove at compile-time that the right interrupts have been bound.\n///\n/// Example of how to bind one interrupt:\n///\n/// ```rust,ignore\n/// use embassy_nxp::{bind_interrupts, usart, peripherals};\n///\n/// bind_interrupts!(\n///     /// Binds the USART Interrupts.\n///     struct Irqs {\n///         FLEXCOMM0 => usart::InterruptHandler<peripherals::USART0>;\n///     }\n/// );\n/// ```\n#[macro_export]\nmacro_rules! bind_interrupts {\n    ($(#[$attr:meta])* $vis:vis struct $name:ident {\n        $(\n            $(#[cfg($cond_irq:meta)])?\n            $irq:ident => $(\n                $(#[cfg($cond_handler:meta)])?\n                $handler:ty\n            ),*;\n        )*\n    }) => {\n        #[derive(Copy, Clone)]\n        $(#[$attr])*\n        $vis struct $name;\n\n        $(\n            #[allow(non_snake_case)]\n            #[unsafe(no_mangle)]\n            $(#[cfg($cond_irq)])?\n            unsafe extern \"C\" fn $irq() {\n                unsafe {\n                    $(\n                        $(#[cfg($cond_handler)])?\n                        <$handler as $crate::interrupt::typelevel::Handler<$crate::interrupt::typelevel::$irq>>::on_interrupt();\n\n                    )*\n                }\n            }\n\n            $(#[cfg($cond_irq)])?\n            $crate::bind_interrupts!(@inner\n                $(\n                    $(#[cfg($cond_handler)])?\n                    unsafe impl $crate::interrupt::typelevel::Binding<$crate::interrupt::typelevel::$irq, $handler> for $name {}\n                )*\n            );\n        )*\n    };\n    (@inner $($t:tt)*) => {\n        $($t)*\n    }\n}\n\n/// Initialize the `embassy-nxp` HAL with the provided configuration.\n///\n/// This returns the peripheral singletons that can be used for creating drivers.\n///\n/// This should only be called once and at startup, otherwise it panics.\npub fn init(_config: config::Config) -> Peripherals {\n    // Do this first, so that it panics if user is calling `init` a second time\n    // before doing anything important.\n    let peripherals = Peripherals::take();\n\n    #[cfg(feature = \"mimxrt1011\")]\n    {\n        // The RT1010 Reference manual states that core clock root must be switched before\n        // reprogramming PLL2.\n        pac::CCM.cbcdr().modify(|w| {\n            w.set_periph_clk_sel(pac::ccm::vals::PeriphClkSel::PERIPH_CLK_SEL_1);\n        });\n\n        while matches!(\n            pac::CCM.cdhipr().read().periph_clk_sel_busy(),\n            pac::ccm::vals::PeriphClkSelBusy::PERIPH_CLK_SEL_BUSY_1\n        ) {}\n\n        info!(\"Core clock root switched\");\n\n        // 480 * 18 / 24 = 360\n        pac::CCM_ANALOG.pfd_480().modify(|x| x.set_pfd2_frac(12));\n\n        //480*18/24(pfd0)/4\n        pac::CCM_ANALOG.pfd_480().modify(|x| x.set_pfd0_frac(24));\n        pac::CCM.cscmr1().modify(|x| x.set_flexspi_podf(3.into()));\n\n        // CPU Core\n        pac::CCM_ANALOG.pfd_528().modify(|x| x.set_pfd3_frac(18));\n        cortex_m::asm::delay(500_000);\n\n        // Clock core clock with PLL 2.\n        pac::CCM\n            .cbcdr()\n            .modify(|x| x.set_periph_clk_sel(pac::ccm::vals::PeriphClkSel::PERIPH_CLK_SEL_0)); // false\n\n        while matches!(\n            pac::CCM.cdhipr().read().periph_clk_sel_busy(),\n            pac::ccm::vals::PeriphClkSelBusy::PERIPH_CLK_SEL_BUSY_1\n        ) {}\n\n        pac::CCM\n            .cbcmr()\n            .write(|v| v.set_pre_periph_clk_sel(pac::ccm::vals::PrePeriphClkSel::PRE_PERIPH_CLK_SEL_0));\n\n        // TODO: Some for USB PLLs\n\n        // DCDC clock?\n        pac::CCM.ccgr6().modify(|v| v.set_cg0(1));\n    }\n\n    #[cfg(any(lpc55, rt1xxx))]\n    gpio::init();\n\n    #[cfg(lpc55)]\n    {\n        pint::init();\n        pwm::Pwm::reset();\n    }\n\n    #[cfg(feature = \"_time_driver\")]\n    time_driver::init();\n\n    #[cfg(lpc55)]\n    dma::init();\n\n    peripherals\n}\n\n/// HAL configuration for the NXP board.\npub mod config {\n    #[derive(Default)]\n    pub struct Config {}\n}\n\n#[allow(unused)]\nstruct BitIter(u32);\n\nimpl Iterator for BitIter {\n    type Item = u32;\n\n    fn next(&mut self) -> Option<Self::Item> {\n        match self.0.trailing_zeros() {\n            32 => None,\n            b => {\n                self.0 &= !(1 << b);\n                Some(b)\n            }\n        }\n    }\n}\n\ntrait SealedMode {}\n\n/// UART mode.\n#[allow(private_bounds)]\npub trait Mode: SealedMode {}\n\nmacro_rules! impl_mode {\n    ($name:ident) => {\n        impl SealedMode for $name {}\n        impl Mode for $name {}\n    };\n}\n\n/// Blocking mode.\npub struct Blocking;\n/// Asynchronous mode.\npub struct Async;\n\nimpl_mode!(Blocking);\nimpl_mode!(Async);\n"
  },
  {
    "path": "embassy-nxp/src/pint.rs",
    "content": "//! Pin Interrupt module.\nuse core::cell::RefCell;\nuse core::future::Future;\nuse core::pin::Pin as FuturePin;\nuse core::task::{Context, Poll};\n\nuse critical_section::Mutex;\nuse embassy_hal_internal::interrupt::InterruptExt;\nuse embassy_sync::waitqueue::AtomicWaker;\n\nuse crate::Peri;\nuse crate::gpio::{self, AnyPin, Level, SealedPin};\nuse crate::pac::{INPUTMUX, PINT, SYSCON, interrupt};\n\nstruct PinInterrupt {\n    assigned: bool,\n    waker: AtomicWaker,\n    /// If true, the interrupt was triggered due to this PinInterrupt. This is used to determine if\n    /// an [InputFuture] should return Poll::Ready.\n    at_fault: bool,\n}\n\nimpl PinInterrupt {\n    pub fn interrupt_active(&self) -> bool {\n        self.assigned\n    }\n\n    /// Mark the interrupt as assigned to a pin.\n    pub fn enable(&mut self) {\n        self.assigned = true;\n        self.at_fault = false;\n    }\n\n    /// Mark the interrupt as available.\n    pub fn disable(&mut self) {\n        self.assigned = false;\n        self.at_fault = false;\n    }\n\n    /// Returns true if the interrupt was triggered due to this PinInterrupt.\n    ///\n    /// If this function returns true, it will also reset the at_fault flag.\n    pub fn at_fault(&mut self) -> bool {\n        let val = self.at_fault;\n        self.at_fault = false;\n        val\n    }\n\n    /// Set the at_fault flag to true.\n    pub fn fault(&mut self) {\n        self.at_fault = true;\n    }\n}\n\nconst INTERUPT_COUNT: usize = 8;\nstatic PIN_INTERRUPTS: Mutex<RefCell<[PinInterrupt; INTERUPT_COUNT]>> = Mutex::new(RefCell::new(\n    [const {\n        PinInterrupt {\n            assigned: false,\n            waker: AtomicWaker::new(),\n            at_fault: false,\n        }\n    }; INTERUPT_COUNT],\n));\n\nfn next_available_interrupt() -> Option<usize> {\n    critical_section::with(|cs| {\n        for (i, pin_interrupt) in PIN_INTERRUPTS.borrow(cs).borrow().iter().enumerate() {\n            if !pin_interrupt.interrupt_active() {\n                return Some(i);\n            }\n        }\n\n        None\n    })\n}\n\n#[derive(Clone, Copy, PartialEq, Eq)]\nenum Edge {\n    Rising,\n    Falling,\n    Both,\n}\n\n#[derive(Clone, Copy, PartialEq, Eq)]\nenum InterruptOn {\n    Level(Level),\n    Edge(Edge),\n}\n\npub(crate) fn init() {\n    SYSCON.ahbclkctrl0().modify(|w| w.set_pint(true));\n\n    // Enable interrupts\n    unsafe {\n        interrupt::PIN_INT0.enable();\n        interrupt::PIN_INT1.enable();\n        interrupt::PIN_INT2.enable();\n        interrupt::PIN_INT3.enable();\n        interrupt::PIN_INT4.enable();\n        interrupt::PIN_INT5.enable();\n        interrupt::PIN_INT6.enable();\n        interrupt::PIN_INT7.enable();\n    };\n\n    info!(\"Pin interrupts initialized\");\n}\n\n#[must_use = \"futures do nothing unless you `.await` or poll them\"]\nstruct InputFuture<'d> {\n    #[allow(dead_code)]\n    pin: Peri<'d, AnyPin>,\n    interrupt_number: usize,\n}\n\nimpl<'d> InputFuture<'d> {\n    /// Create a new input future. Returns None if all interrupts are in use.\n    fn new(pin: Peri<'d, impl gpio::Pin>, interrupt_on: InterruptOn) -> Option<Self> {\n        let pin = pin.into();\n        let interrupt_number = next_available_interrupt()?;\n\n        // Clear interrupt, just in case\n        PINT.rise().write(|w| w.set_rdet(1 << interrupt_number));\n        PINT.fall().write(|w| w.set_fdet(1 << interrupt_number));\n\n        // Enable input multiplexing on pin interrupt register 0 for pin (32*bank + pin_number)\n        INPUTMUX\n            .pintsel(interrupt_number as usize)\n            .write(|w| w.set_intpin(32 * pin.pin_bank() as u8 + pin.pin_number()));\n\n        match interrupt_on {\n            InterruptOn::Level(level) => {\n                // Set pin interrupt register to edge sensitive or level sensitive\n                // 0 = edge sensitive, 1 = level sensitive\n                PINT.isel().modify(|w| w.set_pmode(w.pmode() | (1 << interrupt_number)));\n\n                // Enable level interrupt.\n                //\n                // Note: Level sensitive interrupts are enabled by the same register as rising edge\n                // is activated.\n\n                // 0 = no-op, 1 = enable\n                PINT.sienr().write(|w| w.set_setenrl(1 << interrupt_number));\n\n                // Set active level\n                match level {\n                    Level::Low => {\n                        // 0 = no-op, 1 = select LOW\n                        PINT.cienf().write(|w| w.set_cenaf(1 << interrupt_number));\n                    }\n                    Level::High => {\n                        // 0 = no-op, 1 = select HIGH\n                        PINT.sienf().write(|w| w.set_setenaf(1 << interrupt_number));\n                    }\n                }\n            }\n            InterruptOn::Edge(edge) => {\n                // Set pin interrupt register to edge sensitive or level sensitive\n                // 0 = edge sensitive, 1 = level sensitive\n                PINT.isel()\n                    .modify(|w| w.set_pmode(w.pmode() & !(1 << interrupt_number)));\n\n                // Enable rising/falling edge detection\n                match edge {\n                    Edge::Rising => {\n                        // 0 = no-op, 1 = enable rising edge\n                        PINT.sienr().write(|w| w.set_setenrl(1 << interrupt_number));\n                        // 0 = no-op, 1 = disable falling edge\n                        PINT.cienf().write(|w| w.set_cenaf(1 << interrupt_number));\n                    }\n                    Edge::Falling => {\n                        // 0 = no-op, 1 = enable falling edge\n                        PINT.sienf().write(|w| w.set_setenaf(1 << interrupt_number));\n                        // 0 = no-op, 1 = disable rising edge\n                        PINT.cienr().write(|w| w.set_cenrl(1 << interrupt_number));\n                    }\n                    Edge::Both => {\n                        // 0 = no-op, 1 = enable\n                        PINT.sienr().write(|w| w.set_setenrl(1 << interrupt_number));\n                        PINT.sienf().write(|w| w.set_setenaf(1 << interrupt_number));\n                    }\n                }\n            }\n        }\n\n        critical_section::with(|cs| {\n            let mut pin_interrupts = PIN_INTERRUPTS.borrow(cs).borrow_mut();\n            let pin_interrupt = &mut pin_interrupts[interrupt_number];\n\n            pin_interrupt.enable();\n        });\n\n        Some(Self { pin, interrupt_number })\n    }\n\n    /// Returns true if the interrupt was triggered for this pin.\n    fn interrupt_triggered(&self) -> bool {\n        let interrupt_number = self.interrupt_number;\n\n        // Initially, we determine if the interrupt was triggered by this InputFuture by checking\n        // the flags of the interrupt_number. However, by the time we get to this point, the\n        // interrupt may have been triggered again, so we needed to clear the cpu flags immediately.\n        // As a solution, we mark which [PinInterrupt] is responsible for the interrupt (\"at fault\")\n        critical_section::with(|cs| {\n            let mut pin_interrupts = PIN_INTERRUPTS.borrow(cs).borrow_mut();\n            let pin_interrupt = &mut pin_interrupts[interrupt_number];\n\n            pin_interrupt.at_fault()\n        })\n    }\n}\n\nimpl<'d> Drop for InputFuture<'d> {\n    fn drop(&mut self) {\n        let interrupt_number = self.interrupt_number;\n\n        // Disable pin interrupt\n        // 0 = no-op, 1 = disable\n        PINT.cienr().write(|w| w.set_cenrl(1 << interrupt_number));\n        PINT.cienf().write(|w| w.set_cenaf(1 << interrupt_number));\n\n        critical_section::with(|cs| {\n            let mut pin_interrupts = PIN_INTERRUPTS.borrow(cs).borrow_mut();\n            let pin_interrupt = &mut pin_interrupts[interrupt_number];\n\n            pin_interrupt.disable();\n        });\n    }\n}\n\nimpl<'d> Future for InputFuture<'d> {\n    type Output = ();\n\n    fn poll(self: FuturePin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {\n        let interrupt_number = self.interrupt_number;\n\n        critical_section::with(|cs| {\n            let mut pin_interrupts = PIN_INTERRUPTS.borrow(cs).borrow_mut();\n            let pin_interrupt = &mut pin_interrupts[interrupt_number];\n\n            pin_interrupt.waker.register(cx.waker());\n        });\n\n        if self.interrupt_triggered() {\n            Poll::Ready(())\n        } else {\n            Poll::Pending\n        }\n    }\n}\n\nfn handle_interrupt(interrupt_number: usize) {\n    PINT.rise().write(|w| w.set_rdet(1 << interrupt_number));\n    PINT.fall().write(|w| w.set_fdet(1 << interrupt_number));\n\n    critical_section::with(|cs| {\n        let mut pin_interrupts = PIN_INTERRUPTS.borrow(cs).borrow_mut();\n        let pin_interrupt = &mut pin_interrupts[interrupt_number];\n\n        pin_interrupt.fault();\n        pin_interrupt.waker.wake();\n    });\n}\n\n#[allow(non_snake_case)]\n#[interrupt]\nfn PIN_INT0() {\n    handle_interrupt(0);\n}\n\n#[allow(non_snake_case)]\n#[interrupt]\nfn PIN_INT1() {\n    handle_interrupt(1);\n}\n\n#[allow(non_snake_case)]\n#[interrupt]\nfn PIN_INT2() {\n    handle_interrupt(2);\n}\n\n#[allow(non_snake_case)]\n#[interrupt]\nfn PIN_INT3() {\n    handle_interrupt(3);\n}\n\n#[allow(non_snake_case)]\n#[interrupt]\nfn PIN_INT4() {\n    handle_interrupt(4);\n}\n\n#[allow(non_snake_case)]\n#[interrupt]\nfn PIN_INT5() {\n    handle_interrupt(5);\n}\n\n#[allow(non_snake_case)]\n#[interrupt]\nfn PIN_INT6() {\n    handle_interrupt(6);\n}\n\n#[allow(non_snake_case)]\n#[interrupt]\nfn PIN_INT7() {\n    handle_interrupt(7);\n}\n\nimpl gpio::Flex<'_> {\n    /// Wait for a falling or rising edge on the pin. You can have at most 8 pins waiting. If you\n    /// try to wait for more than 8 pins, this function will return `None`.\n    pub async fn wait_for_any_edge(&mut self) -> Option<()> {\n        InputFuture::new(self.pin.reborrow(), InterruptOn::Edge(Edge::Both))?.await;\n        Some(())\n    }\n\n    /// Wait for a falling edge on the pin. You can have at most 8 pins waiting. If you try to wait\n    /// for more than 8 pins, this function will return `None`.\n    pub async fn wait_for_falling_edge(&mut self) -> Option<()> {\n        InputFuture::new(self.pin.reborrow(), InterruptOn::Edge(Edge::Falling))?.await;\n        Some(())\n    }\n\n    /// Wait for a rising edge on the pin. You can have at most 8 pins waiting. If you try to wait\n    /// for more than 8 pins, this function will return `None`.\n    pub async fn wait_for_rising_edge(&mut self) -> Option<()> {\n        InputFuture::new(self.pin.reborrow(), InterruptOn::Edge(Edge::Rising))?.await;\n        Some(())\n    }\n\n    /// Wait for a low level on the pin. You can have at most 8 pins waiting. If you try to wait for\n    /// more than 8 pins, this function will return `None`.\n    pub async fn wait_for_low(&mut self) -> Option<()> {\n        InputFuture::new(self.pin.reborrow(), InterruptOn::Level(Level::Low))?.await;\n        Some(())\n    }\n\n    /// Wait for a high level on the pin. You can have at most 8 pins waiting. If you try to wait for\n    /// more than 8 pins, this function will return `None`.\n    pub async fn wait_for_high(&mut self) -> Option<()> {\n        InputFuture::new(self.pin.reborrow(), InterruptOn::Level(Level::High))?.await;\n        Some(())\n    }\n}\n\nimpl gpio::Input<'_> {\n    /// Wait for a falling or rising edge on the pin. You can have at most 8 pins waiting. If you\n    /// try to wait for more than 8 pins, this function will return `None`.\n    pub async fn wait_for_any_edge(&mut self) -> Option<()> {\n        self.pin.wait_for_any_edge().await\n    }\n\n    /// Wait for a falling edge on the pin. You can have at most 8 pins waiting. If you try to wait\n    /// for more than 8 pins, this function will return `None`.\n    pub async fn wait_for_falling_edge(&mut self) -> Option<()> {\n        self.pin.wait_for_falling_edge().await\n    }\n\n    /// Wait for a rising edge on the pin. You can have at most 8 pins waiting. If you try to wait\n    /// for more than 8 pins, this function will return `None`.\n    pub async fn wait_for_rising_edge(&mut self) -> Option<()> {\n        self.pin.wait_for_rising_edge().await\n    }\n\n    /// Wait for a low level on the pin. You can have at most 8 pins waiting. If you try to wait for\n    /// more than 8 pins, this function will return `None`.\n    pub async fn wait_for_low(&mut self) -> Option<()> {\n        self.pin.wait_for_low().await\n    }\n\n    /// Wait for a high level on the pin. You can have at most 8 pins waiting. If you try to wait for\n    /// more than 8 pins, this function will return `None`.\n    pub async fn wait_for_high(&mut self) -> Option<()> {\n        self.pin.wait_for_high().await\n    }\n}\n\nimpl gpio::Output<'_> {\n    /// Wait for a falling or rising edge on the pin. You can have at most 8 pins waiting. If you\n    /// try to wait for more than 8 pins, this function will return `None`.\n    pub async fn wait_for_any_edge(&mut self) -> Option<()> {\n        self.pin.wait_for_any_edge().await\n    }\n\n    /// Wait for a falling edge on the pin. You can have at most 8 pins waiting. If you try to wait\n    /// for more than 8 pins, this function will return `None`.\n    pub async fn wait_for_falling_edge(&mut self) -> Option<()> {\n        self.pin.wait_for_falling_edge().await\n    }\n\n    /// Wait for a rising edge on the pin. You can have at most 8 pins waiting. If you try to wait\n    /// for more than 8 pins, this function will return `None`.\n    pub async fn wait_for_rising_edge(&mut self) -> Option<()> {\n        self.pin.wait_for_rising_edge().await\n    }\n\n    /// Wait for a low level on the pin. You can have at most 8 pins waiting. If you try to wait for\n    /// more than 8 pins, this function will return `None`.\n    pub async fn wait_for_low(&mut self) -> Option<()> {\n        self.pin.wait_for_low().await\n    }\n\n    /// Wait for a high level on the pin. You can have at most 8 pins waiting. If you try to wait for\n    /// more than 8 pins, this function will return `None`.\n    pub async fn wait_for_high(&mut self) -> Option<()> {\n        self.pin.wait_for_high().await\n    }\n}\n"
  },
  {
    "path": "embassy-nxp/src/pwm/lpc55.rs",
    "content": "#![macro_use]\n\nuse core::sync::atomic::{AtomicU8, AtomicU32, Ordering};\n\nuse embassy_hal_internal::Peri;\n\nuse crate::gpio::AnyPin;\nuse crate::pac::iocon::vals::{PioDigimode, PioMode, PioOd, PioSlew};\nuse crate::pac::sct0::vals;\nuse crate::pac::syscon::vals::{SctRst, SctclkselSel};\nuse crate::pac::{SCT0, SYSCON};\nuse crate::sct;\n\n// Since for now the counter is shared, the TOP value has to be kept.\nstatic TOP_VALUE: AtomicU32 = AtomicU32::new(0);\n// To check if there are still active instances.\nstatic REF_COUNT: AtomicU8 = AtomicU8::new(0);\n\n/// The configuration of a PWM output.\n/// Note the period in clock cycles of an output can be computed as:\n/// `(top + 1) * (phase_correct ? 1 : 2) * divider * prescale_factor`\n/// By default, the clock used is 96 MHz.\n#[non_exhaustive]\n#[derive(Clone)]\npub struct Config {\n    /// Inverts the PWM output signal.\n    pub invert: bool,\n    /// Enables phase-correct mode for PWM operation.\n    /// In phase-correct mode, the PWM signal is generated in such a way that\n    /// the pulse is always centered regardless of the duty cycle.\n    /// The output frequency is halved when phase-correct mode is enabled.\n    pub phase_correct: bool,\n    /// Enables the PWM output, allowing it to generate an output.\n    pub enable: bool,\n    /// A SYSCON clock divider allows precise control over\n    /// the PWM output frequency by gating the PWM counter increment.\n    /// A higher value will result in a slower output frequency.\n    /// The clock is divided by `divider + 1`.\n    pub divider: u8,\n    /// Specifies the factor by which the SCT clock is prescaled to produce the unified\n    /// counter clock. The counter clock is clocked at the rate of the SCT clock divided by\n    /// `PRE + 1`.\n    pub prescale_factor: u8,\n    /// The output goes high when `compare` is higher than the\n    /// counter. A compare of 0 will produce an always low output, while a\n    /// compare of `top` will produce an always high output.\n    pub compare: u32,\n    /// The point at which the counter resets, representing the maximum possible\n    /// period. The counter will either wrap to 0 or reverse depending on the\n    /// setting of `phase_correct`.\n    pub top: u32,\n}\n\nimpl Config {\n    pub fn new(compare: u32, top: u32) -> Self {\n        Self {\n            invert: false,\n            phase_correct: false,\n            enable: true,\n            divider: 255,\n            prescale_factor: 255,\n            compare,\n            top,\n        }\n    }\n}\n\n/// PWM driver.\npub struct Pwm<'d> {\n    _pin: Peri<'d, AnyPin>,\n    output: usize,\n}\n\nimpl<'d> Pwm<'d> {\n    pub(crate) fn reset() {\n        // Reset SCTimer => Reset counter and halt it.\n        // It should be done only once during the initialization of the board.\n        SYSCON.presetctrl1().modify(|w| w.set_sct_rst(SctRst::ASSERTED));\n        SYSCON.presetctrl1().modify(|w| w.set_sct_rst(SctRst::RELEASED));\n    }\n    fn new_inner<T: sct::Instance, O: sct::Output<T>>(\n        output: usize,\n        channel: Peri<'d, impl sct::OutputPin<T, O>>,\n        config: Config,\n    ) -> Self {\n        // Enable clocks (Syscon is enabled by default)\n        critical_section::with(|_cs| {\n            if !SYSCON.ahbclkctrl0().read().iocon() {\n                SYSCON.ahbclkctrl0().modify(|w| w.set_iocon(true));\n            }\n            if !SYSCON.ahbclkctrl1().read().sct() {\n                SYSCON.ahbclkctrl1().modify(|w| w.set_sct(true));\n            }\n        });\n\n        // Choose the clock for PWM.\n        SYSCON.sctclksel().modify(|w| w.set_sel(SctclkselSel::ENUM_0X3));\n        // For now, 96 MHz.\n\n        // IOCON Setup\n        channel.pio().modify(|w| {\n            w.set_func(channel.pin_func());\n            w.set_digimode(PioDigimode::DIGITAL);\n            w.set_slew(PioSlew::STANDARD);\n            w.set_mode(PioMode::INACTIVE);\n            w.set_od(PioOd::NORMAL);\n        });\n\n        Self::configure(output, &config);\n        REF_COUNT.fetch_add(1, Ordering::Relaxed);\n        Self {\n            _pin: channel.into(),\n            output,\n        }\n    }\n\n    /// Create PWM driver with a single 'a' pin as output.\n    #[inline]\n    pub fn new_output<T: sct::Instance, O: sct::Output<T>>(\n        output: Peri<'d, O>,\n        channel: Peri<'d, impl sct::OutputPin<T, O>>,\n        config: Config,\n    ) -> Self {\n        Self::new_inner::<T, O>(output.number(), channel, config)\n    }\n\n    /// Set the PWM config.\n    pub fn set_config(&mut self, config: &Config) {\n        Self::configure(self.output, config);\n    }\n\n    fn configure(output_number: usize, config: &Config) {\n        // Stop and reset the counter\n        SCT0.ctrl().modify(|w| {\n            if config.phase_correct {\n                w.set_bidir_l(vals::Bidir::UP_DOWN);\n            } else {\n                w.set_bidir_l(vals::Bidir::UP);\n            }\n            w.set_halt_l(true); // halt the counter to make new changes\n            w.set_clrctr_l(true); // clear the counter\n        });\n        // Divides clock by 1-255\n        SYSCON.sctclkdiv().modify(|w| w.set_div(config.divider));\n\n        SCT0.config().modify(|w| {\n            w.set_unify(vals::Unify::UNIFIED_COUNTER);\n            w.set_clkmode(vals::Clkmode::SYSTEM_CLOCK_MODE);\n            w.set_noreload_l(true);\n            w.set_autolimit_l(true);\n        });\n\n        // Before setting the match registers, we have to make sure that `compare` is lower or equal to `top`,\n        // otherwise the counter will not reach the match and, therefore, no events will happen.\n        assert!(config.compare <= config.top);\n\n        if TOP_VALUE.load(Ordering::Relaxed) == 0 {\n            // Match 0 will reset the timer using TOP value\n            SCT0.match_(0).modify(|w| {\n                w.set_matchn_l((config.top & 0xFFFF) as u16);\n                w.set_matchn_h((config.top >> 16) as u16);\n            });\n        } else {\n            panic!(\"The top value cannot be changed after the initialization.\");\n        }\n        // The actual matches that are used for event logic\n        SCT0.match_(output_number + 1).modify(|w| {\n            w.set_matchn_l((config.compare & 0xFFFF) as u16);\n            w.set_matchn_h((config.compare >> 16) as u16);\n        });\n\n        SCT0.match_(15).modify(|w| {\n            w.set_matchn_l(0);\n            w.set_matchn_h(0);\n        });\n\n        // Event configuration\n        critical_section::with(|_cs| {\n            // If it is already set, don't change\n            if SCT0.ev(0).ev_ctrl().read().matchsel() != 15 {\n                SCT0.ev(0).ev_ctrl().modify(|w| {\n                    w.set_matchsel(15);\n                    w.set_combmode(vals::Combmode::MATCH);\n                    // STATE + statev, where STATE is a on-board variable.\n                    w.set_stateld(vals::Stateld::ADD);\n                    w.set_statev(0);\n                });\n            }\n        });\n        SCT0.ev(output_number + 1).ev_ctrl().modify(|w| {\n            w.set_matchsel((output_number + 1) as u8);\n            w.set_combmode(vals::Combmode::MATCH);\n            w.set_stateld(vals::Stateld::ADD);\n            // STATE + statev, where STATE is a on-board variable.\n            w.set_statev(0);\n        });\n\n        // Assign events to states\n        SCT0.ev(0).ev_state().modify(|w| w.set_statemskn(1 << 0));\n        SCT0.ev(output_number + 1)\n            .ev_state()\n            .modify(|w| w.set_statemskn(1 << 0));\n        // TODO(frihetselsker): optimize nxp-pac so that `set_clr` and `set_set` are turned into a bit array.\n        if config.invert {\n            // Low when event 0 is active\n            SCT0.out(output_number).out_clr().modify(|w| w.set_clr(0, true));\n            // High when event `output_number + 1` is active\n            SCT0.out(output_number)\n                .out_set()\n                .modify(|w| w.set_set(output_number, true));\n        } else {\n            // High when event 0 is active\n            SCT0.out(output_number).out_set().modify(|w| w.set_set(0, true));\n            // Low when event `output_number + 1` is active\n            SCT0.out(output_number)\n                .out_clr()\n                .modify(|w| w.set_clr(output_number, true));\n        }\n\n        if config.phase_correct {\n            // Take into account the set matches and reverse their actions while counting back.\n            SCT0.outputdirctrl()\n                .modify(|w| w.set_setclr(output_number, vals::Setclr::L_REVERSED));\n        }\n\n        // State 0 by default\n        SCT0.state().modify(|w| w.set_state_l(0));\n        // Remove halt and start the actual counter\n        SCT0.ctrl().modify(|w| {\n            w.set_halt_l(!config.enable);\n        });\n    }\n\n    /// Read PWM counter.\n    #[inline]\n    pub fn counter(&self) -> u32 {\n        SCT0.count().read().0\n    }\n}\n\nimpl<'d> Drop for Pwm<'d> {\n    fn drop(&mut self) {\n        REF_COUNT.fetch_sub(1, Ordering::AcqRel);\n        if REF_COUNT.load(Ordering::Acquire) == 0 {\n            TOP_VALUE.store(0, Ordering::Release);\n        }\n    }\n}\n"
  },
  {
    "path": "embassy-nxp/src/pwm.rs",
    "content": "#![macro_use]\n\n//! Pulse-Width Modulation (PWM) driver.\n\n#[cfg_attr(lpc55, path = \"./pwm/lpc55.rs\")]\nmod inner;\npub use inner::*;\n"
  },
  {
    "path": "embassy-nxp/src/sct.rs",
    "content": "#![macro_use]\n\nuse embassy_hal_internal::PeripheralType;\nuse nxp_pac::iocon::vals::PioFunc;\n\nuse crate::gpio;\n\n/// SCT instance.\n#[allow(private_bounds)]\npub trait Instance: SealedInstance + PeripheralType {}\n\npub(crate) trait SealedInstance {}\n\n/// An SCT output.\n#[allow(private_bounds)]\npub trait Output<T: Instance>: SealedOutput + PeripheralType {}\n\npub(crate) trait SealedOutput {\n    /// Output number.\n    fn number(&self) -> usize;\n}\n\n/// An SCT output capable pin.\npub trait OutputPin<T: Instance, O: Output<T>>: gpio::Pin {\n    fn pin_func(&self) -> PioFunc;\n}\n\nmacro_rules! impl_sct_instance {\n    ($instance: ident) => {\n        impl crate::sct::SealedInstance for crate::peripherals::$instance {}\n        impl crate::sct::Instance for crate::peripherals::$instance {}\n    };\n}\n\nmacro_rules! impl_sct_output_instance {\n    ($instance: ident, $name: ident, $num: expr) => {\n        impl crate::sct::SealedOutput for crate::peripherals::$name {\n            fn number(&self) -> usize {\n                $num as usize\n            }\n        }\n        impl crate::sct::Output<crate::peripherals::$instance> for crate::peripherals::$name {}\n    };\n}\n\nmacro_rules! impl_sct_output_pin {\n    ($instance: ident, $output_instance: ident, $pin: ident, $alt: ident) => {\n        impl crate::sct::OutputPin<crate::peripherals::$instance, crate::peripherals::$output_instance>\n            for crate::peripherals::$pin\n        {\n            fn pin_func(&self) -> crate::pac::iocon::vals::PioFunc {\n                crate::pac::iocon::vals::PioFunc::$alt\n            }\n        }\n    };\n}\n"
  },
  {
    "path": "embassy-nxp/src/time_driver/pit.rs",
    "content": "//! Time driver using Periodic Interrupt Timer (PIT)\n//!\n//! This driver is used with the iMXRT1xxx parts.\n//!\n//! The PIT is run in lifetime mode. Timer 1 is chained to timer 0 to provide a free-running 64-bit timer.\n//! The 64-bit timer is used to track how many ticks since boot.\n//!\n//! Timer 2 counts how many ticks there are within the current u32::MAX tick period. Timer 2 is restarted when\n//! a new alarm is set (or every u32::MAX ticks). One caveat is that an alarm could be a few ticks late due to\n//! restart. However the Cortex-M7 cores run at 500 MHz easily and the PIT will generally run at 1 MHz or lower.\n//! Along with the fact that scheduling an alarm takes a critical section worst case an alarm may be a few\n//! microseconds late.\n//!\n//! All PIT timers are clocked in lockstep, so the late start will not cause the now() count to drift.\n\nuse core::cell::{Cell, RefCell};\nuse core::task::Waker;\n\nuse critical_section::{CriticalSection, Mutex};\nuse embassy_hal_internal::interrupt::InterruptExt;\nuse embassy_time_driver::Driver as _;\nuse embassy_time_queue_utils::Queue;\n\nuse crate::pac::{self, interrupt};\n\nstruct Driver {\n    alarm: Mutex<Cell<u64>>,\n    queue: Mutex<RefCell<Queue>>,\n}\n\nimpl embassy_time_driver::Driver for Driver {\n    fn now(&self) -> u64 {\n        loop {\n            // Even though reading LTMR64H will latch LTMR64L if another thread preempts between any of the\n            // three reads and calls now() then the value in LTMR64L will be wrong when execution returns to\n            // thread which was preempted.\n            let hi = pac::PIT.ltmr64h().read().lth();\n            let lo = pac::PIT.ltmr64l().read().ltl();\n            let hi2 = pac::PIT.ltmr64h().read().lth();\n\n            if hi == hi2 {\n                // PIT timers always count down.\n                return u64::MAX - ((hi as u64) << 32 | (lo as u64));\n            }\n        }\n    }\n\n    fn schedule_wake(&self, at: u64, waker: &Waker) {\n        critical_section::with(|cs| {\n            let mut queue = self.queue.borrow(cs).borrow_mut();\n\n            if queue.schedule_wake(at, waker) {\n                let mut next = queue.next_expiration(self.now());\n\n                while !self.set_alarm(cs, next) {\n                    next = queue.next_expiration(self.now());\n                }\n            }\n        })\n    }\n}\n\nimpl Driver {\n    fn init(&'static self) {\n        // Disable PIT clock during mux configuration.\n        pac::CCM.ccgr1().modify(|r| r.set_cg6(0b00));\n\n        // TODO: This forces the PIT to be driven by the oscillator. However that isn't the only option as you\n        // could divide the clock root by up to 64.\n        pac::CCM.cscmr1().modify(|r| {\n            // 1 MHz\n            r.set_perclk_podf(pac::ccm::vals::PerclkPodf::DIVIDE_24);\n            r.set_perclk_clk_sel(pac::ccm::vals::PerclkClkSel::PERCLK_CLK_SEL_1);\n        });\n\n        pac::CCM.ccgr1().modify(|r| r.set_cg6(0b11));\n\n        // Disable clock during init.\n        //\n        // It is important that the PIT clock is prepared to not exceed limit (50 MHz on RT1011), or else\n        // you will need to recover the device with boot mode switches when using any PIT registers.\n        pac::PIT.mcr().modify(|w| {\n            w.set_mdis(true);\n        });\n\n        pac::PIT.timer(0).ldval().write_value(u32::MAX);\n        pac::PIT.timer(1).ldval().write_value(u32::MAX);\n        pac::PIT.timer(2).ldval().write_value(0);\n        pac::PIT.timer(3).ldval().write_value(0);\n\n        pac::PIT.timer(1).tctrl().write(|w| {\n            // In lifetime mode, timer 1 is chained to timer 0 to form a 64-bit timer.\n            w.set_chn(true);\n            w.set_ten(true);\n            w.set_tie(false);\n        });\n\n        pac::PIT.timer(0).tctrl().write(|w| {\n            w.set_chn(false);\n            w.set_ten(true);\n            w.set_tie(false);\n        });\n\n        pac::PIT.timer(2).tctrl().write(|w| {\n            w.set_tie(true);\n        });\n\n        unsafe { interrupt::PIT.enable() };\n\n        pac::PIT.mcr().write(|w| {\n            w.set_mdis(false);\n        });\n    }\n\n    fn set_alarm(&self, cs: CriticalSection, timestamp: u64) -> bool {\n        let alarm = self.alarm.borrow(cs);\n        alarm.set(timestamp);\n\n        let timer = pac::PIT.timer(2);\n        let now = self.now();\n\n        if timestamp <= now {\n            alarm.set(u64::MAX);\n\n            return false;\n        }\n\n        timer.tctrl().modify(|x| x.set_ten(false));\n        timer.tflg().modify(|x| x.set_tif(true));\n\n        // If the next alarm happens in more than u32::MAX cycles then the alarm will be restarted later.\n        timer.ldval().write_value((timestamp - now) as u32);\n        timer.tctrl().modify(|x| x.set_ten(true));\n\n        true\n    }\n\n    fn trigger_alarm(&self, cs: CriticalSection) {\n        let mut next = self.queue.borrow_ref_mut(cs).next_expiration(self.now());\n\n        while !self.set_alarm(cs, next) {\n            next = self.queue.borrow_ref_mut(cs).next_expiration(self.now());\n        }\n    }\n\n    fn on_interrupt(&self) {\n        critical_section::with(|cs| {\n            let timer = pac::PIT.timer(2);\n            let alarm = self.alarm.borrow(cs);\n            let interrupted = timer.tflg().read().tif();\n            timer.tflg().write(|r| r.set_tif(true));\n\n            if interrupted {\n                // A new load value will not apply until the next timer expiration.\n                //\n                // The expiry may be up to u32::MAX cycles away, so the timer must be restarted.\n                timer.tctrl().modify(|r| r.set_ten(false));\n\n                let now = self.now();\n                let timestamp = alarm.get();\n\n                if timestamp <= now {\n                    self.trigger_alarm(cs);\n                } else {\n                    // The alarm is not ready. Wait for u32::MAX cycles and check again or set the next alarm.\n                    timer.ldval().write_value((timestamp - now) as u32);\n                    timer.tctrl().modify(|r| r.set_ten(true));\n                }\n            }\n        });\n    }\n}\n\nembassy_time_driver::time_driver_impl!(static DRIVER: Driver = Driver {\n    alarm: Mutex::new(Cell::new(0)),\n    queue: Mutex::new(RefCell::new(Queue::new()))\n});\n\npub(crate) fn init() {\n    DRIVER.init();\n}\n\n#[cfg(feature = \"rt\")]\n#[interrupt]\nfn PIT() {\n    DRIVER.on_interrupt();\n}\n"
  },
  {
    "path": "embassy-nxp/src/time_driver/rtc.rs",
    "content": "use core::cell::{Cell, RefCell};\nuse core::task::Waker;\n\nuse critical_section::CriticalSection;\nuse embassy_hal_internal::interrupt::{InterruptExt, Priority};\nuse embassy_sync::blocking_mutex::CriticalSectionMutex as Mutex;\nuse embassy_time_driver::{Driver, time_driver_impl};\nuse embassy_time_queue_utils::Queue;\n\nuse crate::pac::{PMC, RTC, SYSCON, interrupt, pmc, rtc};\n\nstruct AlarmState {\n    timestamp: Cell<u64>,\n}\n\nunsafe impl Send for AlarmState {}\n\nimpl AlarmState {\n    const fn new() -> Self {\n        Self {\n            timestamp: Cell::new(u64::MAX),\n        }\n    }\n}\n\npub struct RtcDriver {\n    alarms: Mutex<AlarmState>,\n    queue: Mutex<RefCell<Queue>>,\n}\n\ntime_driver_impl!(static DRIVER: RtcDriver = RtcDriver {\n    alarms: Mutex::new(AlarmState::new()),\n    queue: Mutex::new(RefCell::new(Queue::new())),\n});\nimpl RtcDriver {\n    fn init(&'static self) {\n        let syscon = SYSCON;\n        let pmc = PMC;\n        let rtc = RTC;\n\n        syscon.ahbclkctrl0().modify(|w| w.set_rtc(true));\n\n        // By default the RTC enters software reset. If for some reason it is\n        // not in reset, we enter and them promptly leave.q\n        rtc.ctrl().modify(|w| w.set_swreset(true));\n        rtc.ctrl().modify(|w| w.set_swreset(false));\n\n        // Select clock source - either XTAL or FRO\n        // pmc.rtcosc32k().write(|w| w.set_sel(pmc::vals::Sel::XTAL32K));\n        pmc.rtcosc32k().write(|w| w.set_sel(pmc::vals::Sel::FRO32K));\n\n        // Start the RTC peripheral\n        rtc.ctrl().modify(|w| w.set_rtc_osc_pd(rtc::vals::RtcOscPd::POWER_UP));\n\n        //reset/clear(?) counter\n        rtc.count().modify(|w| w.set_val(0));\n        //en rtc main counter\n        rtc.ctrl().modify(|w| w.set_rtc_en(true));\n        rtc.ctrl().modify(|w| w.set_rtc1khz_en(true));\n        // subsec counter enable\n        rtc.ctrl()\n            .modify(|w| w.set_rtc_subsec_ena(rtc::vals::RtcSubsecEna::POWER_UP));\n\n        // enable irq\n        unsafe {\n            interrupt::RTC.set_priority(Priority::from(3));\n            interrupt::RTC.enable();\n        }\n    }\n\n    fn set_alarm(&self, cs: CriticalSection, timestamp: u64) -> bool {\n        let rtc = RTC;\n        let alarm = &self.alarms.borrow(cs);\n        alarm.timestamp.set(timestamp);\n        let now = self.now();\n\n        if timestamp <= now {\n            alarm.timestamp.set(u64::MAX);\n            return false;\n        }\n\n        //time diff in sub-sec not ticks (32kHz)\n        let diff = timestamp - now;\n        let sec = (diff / 32768) as u32;\n        let subsec = (diff % 32768) as u32;\n\n        let current_sec = rtc.count().read().val();\n        let target_sec = current_sec.wrapping_add(sec as u32);\n\n        rtc.match_().write(|w| w.set_matval(target_sec));\n        rtc.wake().write(|w| {\n            let ms = (subsec * 1000) / 32768;\n            w.set_val(ms as u16)\n        });\n\n        if subsec > 0 {\n            let ms = (subsec * 1000) / 32768;\n            rtc.wake().write(|w| w.set_val(ms as u16));\n        }\n\n        rtc.ctrl().modify(|w| {\n            w.set_alarm1hz(false);\n            w.set_wake1khz(rtc::vals::Wake1khz::RUN)\n        });\n        true\n    }\n\n    fn on_interrupt(&self) {\n        critical_section::with(|cs| {\n            let rtc = RTC;\n            let flags = rtc.ctrl().read();\n            if flags.alarm1hz() == false {\n                rtc.ctrl().modify(|w| w.set_alarm1hz(true));\n                self.trigger_alarm(cs);\n            }\n\n            if flags.wake1khz() == rtc::vals::Wake1khz::RUN {\n                rtc.ctrl().modify(|w| w.set_wake1khz(rtc::vals::Wake1khz::TIMEOUT));\n                self.trigger_alarm(cs);\n            }\n        });\n    }\n\n    fn trigger_alarm(&self, cs: CriticalSection) {\n        let alarm = &self.alarms.borrow(cs);\n        alarm.timestamp.set(u64::MAX);\n        let mut next = self.queue.borrow(cs).borrow_mut().next_expiration(self.now());\n        if next == u64::MAX {\n            // no scheduled events, skipping\n            return;\n        }\n        while !self.set_alarm(cs, next) {\n            next = self.queue.borrow(cs).borrow_mut().next_expiration(self.now());\n            if next == u64::MAX {\n                //no next event found after retry\n                return;\n            }\n        }\n    }\n}\n\nimpl Driver for RtcDriver {\n    fn now(&self) -> u64 {\n        let rtc = RTC;\n\n        loop {\n            let sec1 = rtc.count().read().val() as u64;\n            let sub1 = rtc.subsec().read().subsec() as u64;\n            let sec2 = rtc.count().read().val() as u64;\n            let sub2 = rtc.subsec().read().subsec() as u64;\n\n            if sec1 == sec2 && sub1 == sub2 {\n                return sec1 * 32768 + sub1;\n            }\n        }\n    }\n\n    fn schedule_wake(&self, at: u64, waker: &Waker) {\n        critical_section::with(|cs| {\n            let mut queue = self.queue.borrow(cs).borrow_mut();\n\n            if queue.schedule_wake(at, waker) {\n                let mut next = queue.next_expiration(self.now());\n                while !self.set_alarm(cs, next) {\n                    next = queue.next_expiration(self.now());\n                }\n            }\n        })\n    }\n}\n#[interrupt]\nfn RTC() {\n    DRIVER.on_interrupt();\n}\n\npub fn init() {\n    DRIVER.init();\n}\n"
  },
  {
    "path": "embassy-nxp/src/usart/lpc55.rs",
    "content": "#![macro_use]\n\nuse core::fmt::Debug;\nuse core::future::poll_fn;\nuse core::marker::PhantomData;\nuse core::sync::atomic::{AtomicBool, Ordering};\nuse core::task::Poll;\n\nuse embassy_futures::select::{Either, select};\nuse embassy_hal_internal::interrupt::InterruptExt;\nuse embassy_hal_internal::{Peri, PeripheralType};\nuse embassy_sync::waitqueue::AtomicWaker;\nuse embedded_io::{self, ErrorKind};\n\nuse crate::dma::{AnyChannel, Channel};\nuse crate::gpio::{AnyPin, SealedPin};\nuse crate::interrupt::Interrupt;\nuse crate::interrupt::typelevel::Binding;\nuse crate::pac::flexcomm::Flexcomm as FlexcommReg;\nuse crate::pac::iocon::vals::PioFunc;\nuse crate::pac::usart::Usart as UsartReg;\nuse crate::pac::*;\nuse crate::{Async, Blocking, Mode};\n\n/// Serial error\n#[derive(Debug, Eq, PartialEq, Copy, Clone)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\n#[non_exhaustive]\npub enum Error {\n    /// Triggered when the FIFO (or shift-register) is overflowed.\n    Overrun,\n    /// Triggered when there is a parity mismatch between what's received and\n    /// our settings.\n    Parity,\n    /// Triggered when the received character didn't have a valid stop bit.\n    Framing,\n    /// Triggered when the receiver detects noise\n    Noise,\n    /// Triggered when the receiver gets a break\n    Break,\n}\n\nimpl embedded_io::Error for Error {\n    fn kind(&self) -> ErrorKind {\n        match self {\n            Error::Overrun => ErrorKind::Other,\n            Error::Parity => ErrorKind::InvalidData,\n            Error::Framing => ErrorKind::InvalidData,\n            Error::Noise => ErrorKind::Other,\n            Error::Break => ErrorKind::Interrupted,\n        }\n    }\n}\n\nimpl core::fmt::Display for Error {\n    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {\n        core::fmt::Debug::fmt(self, f)\n    }\n}\n\nimpl core::error::Error for Error {}\n\n/// Word length.\n#[derive(Clone, Copy, PartialEq, Eq, Debug)]\npub enum DataBits {\n    /// 7 bits data length.\n    DataBits7,\n    /// 8 bits data length.\n    DataBits8,\n    /// 9 bits data length. The 9th bit is commonly used for addressing in multidrop mode.\n    DataBits9,\n}\n\n/// Parity bit.\n#[derive(Clone, Copy, PartialEq, Eq, Debug)]\npub enum Parity {\n    /// No parity.\n    ParityNone,\n    /// Even parity.\n    ParityEven,\n    /// Odd parity.\n    ParityOdd,\n}\n\n/// Stop bits.\n#[derive(Clone, Copy, PartialEq, Eq, Debug)]\npub enum StopBits {\n    /// 1 stop bit.\n    Stop1,\n    /// 2 stop bits. This setting should only be used for asynchronous communication.\n    Stop2,\n}\n\n/// UART config.\n#[non_exhaustive]\n#[derive(Clone, Debug)]\npub struct Config {\n    /// Baud rate.\n    pub baudrate: u32,\n    /// Word length.\n    pub data_bits: DataBits,\n    /// Stop bits.\n    pub stop_bits: StopBits,\n    /// Parity bit.\n    pub parity: Parity,\n    /// Invert the tx pin output\n    pub invert_tx: bool,\n    /// Invert the rx pin input\n    pub invert_rx: bool,\n}\n\nimpl Default for Config {\n    fn default() -> Self {\n        Self {\n            baudrate: 115200,\n            data_bits: DataBits::DataBits8,\n            stop_bits: StopBits::Stop1,\n            parity: Parity::ParityNone,\n            invert_rx: false,\n            invert_tx: false,\n        }\n    }\n}\n\n/// Internal DMA state of UART RX.\npub struct DmaState {\n    pub(crate) rx_err_waker: AtomicWaker,\n    pub(crate) rx_err: AtomicBool,\n}\n\n/// # Type parameters\n/// 'd: the lifetime marker ensuring correct borrow checking for peripherals used at compile time\n/// T: the peripheral instance type allowing usage of peripheral specific registers\n/// M: the operating mode of USART peripheral\npub struct Usart<'d, M: Mode> {\n    tx: UsartTx<'d, M>,\n    rx: UsartRx<'d, M>,\n}\n\npub struct UsartTx<'d, M: Mode> {\n    info: &'static Info,\n    tx_dma: Option<Peri<'d, AnyChannel>>,\n    phantom: PhantomData<M>,\n}\n\npub struct UsartRx<'d, M: Mode> {\n    info: &'static Info,\n    dma_state: &'static DmaState,\n    rx_dma: Option<Peri<'d, AnyChannel>>,\n    phantom: PhantomData<M>,\n}\n\nimpl<'d, M: Mode> UsartTx<'d, M> {\n    pub fn new<T: Instance>(\n        _usart: Peri<'d, T>,\n        tx: Peri<'d, impl TxPin<T>>,\n        tx_dma: Peri<'d, impl Channel>,\n        config: Config,\n    ) -> Self {\n        let tx_func = tx.pin_func();\n        Usart::<M>::init::<T>(Some((tx.into(), tx_func)), None, config);\n        Self::new_inner(T::info(), Some(tx_dma.into()))\n    }\n\n    #[inline]\n    fn new_inner(info: &'static Info, tx_dma: Option<Peri<'d, AnyChannel>>) -> Self {\n        Self {\n            info,\n            tx_dma,\n            phantom: PhantomData,\n        }\n    }\n\n    pub fn blocking_write(&mut self, buffer: &[u8]) -> Result<(), Error> {\n        for &b in buffer {\n            while !(self.info.usart_reg.fifostat().read().txnotfull()) {}\n            self.info.usart_reg.fifowr().write(|w| w.set_txdata(b as u16));\n        }\n        Ok(())\n    }\n\n    pub fn blocking_flush(&mut self) -> Result<(), Error> {\n        while !(self.info.usart_reg.fifostat().read().txempty()) {}\n        Ok(())\n    }\n\n    pub fn tx_busy(&self) -> bool {\n        !(self.info.usart_reg.fifostat().read().txempty())\n    }\n}\n\nimpl<'d> UsartTx<'d, Blocking> {\n    pub fn new_blocking<T: Instance>(_usart: Peri<'d, T>, tx: Peri<'d, impl TxPin<T>>, config: Config) -> Self {\n        let tx_func = tx.pin_func();\n        Usart::<Blocking>::init::<T>(Some((tx.into(), tx_func)), None, config);\n        Self::new_inner(T::info(), None)\n    }\n}\n\nimpl<'d> UsartTx<'d, Async> {\n    /// Write to UART TX from the provided buffer using DMA.\n    pub async fn write(&mut self, buffer: &[u8]) -> Result<(), Error> {\n        // Unwrap() can be used because UsartTx::new() in Async mode always sets it to Some\n        let ch = self.tx_dma.as_mut().unwrap().reborrow();\n        let transfer = unsafe {\n            // Enable to pace DMA transfers.\n            self.info.usart_reg.fifocfg().modify(|w| w.set_dmatx(true));\n            // If future is not assigned to a variable, the data register pointer\n            // is held across an await and makes the future non-Send.\n            crate::dma::write(ch, buffer, self.info.usart_reg.fifowr().as_ptr() as *mut _)\n        };\n        transfer.await;\n        Ok(())\n    }\n}\nimpl<'d, M: Mode> UsartRx<'d, M> {\n    pub fn new<T: Instance>(\n        _usart: Peri<'d, T>,\n        rx: Peri<'d, impl RxPin<T>>,\n        has_irq: bool,\n        rx_dma: Peri<'d, impl Channel>,\n        config: Config,\n    ) -> Self {\n        let rx_func = rx.pin_func();\n        Usart::<M>::init::<T>(None, Some((rx.into(), rx_func)), config);\n        Self::new_inner(T::info(), T::dma_state(), has_irq, Some(rx_dma.into()))\n    }\n\n    fn new_inner(\n        info: &'static Info,\n        dma_state: &'static DmaState,\n        has_irq: bool,\n        rx_dma: Option<Peri<'d, AnyChannel>>,\n    ) -> Self {\n        core::debug_assert_eq!(has_irq, rx_dma.is_some());\n        if has_irq {\n            // Disable all the related interrupts for now.\n            info.usart_reg.intenclr().write(|w| {\n                w.set_framerrclr(true);\n                w.set_parityerrclr(true);\n                w.set_rxnoiseclr(true);\n            });\n            info.usart_reg.fifointenclr().modify(|w| {\n                w.set_rxlvl(true);\n                w.set_rxerr(true);\n            });\n            info.interrupt.unpend();\n            unsafe {\n                info.interrupt.enable();\n            }\n        }\n        Self {\n            info,\n            dma_state,\n            rx_dma,\n            phantom: PhantomData,\n        }\n    }\n\n    pub fn blocking_read(&mut self, mut buffer: &mut [u8]) -> Result<(), Error> {\n        while !buffer.is_empty() {\n            match Self::drain_fifo(self, buffer) {\n                Ok(0) => continue, // Wait for more data\n                Ok(n) => buffer = &mut buffer[n..],\n                Err((_, err)) => return Err(err),\n            }\n        }\n        Ok(())\n    }\n\n    /// Returns:\n    /// - Ok(n) -> read n bytes\n    /// - Err(n, Error) -> read n-1 bytes, but encountered an error while reading nth byte\n    fn drain_fifo(&mut self, buffer: &mut [u8]) -> Result<usize, (usize, Error)> {\n        for (i, b) in buffer.iter_mut().enumerate() {\n            while !(self.info.usart_reg.fifostat().read().rxnotempty()) {}\n            if self.info.usart_reg.fifostat().read().rxerr() {\n                return Err((i, Error::Overrun));\n            } else if self.info.usart_reg.fifordnopop().read().parityerr() {\n                return Err((i, Error::Parity));\n            } else if self.info.usart_reg.fifordnopop().read().framerr() {\n                return Err((i, Error::Framing));\n            } else if self.info.usart_reg.fifordnopop().read().rxnoise() {\n                return Err((i, Error::Noise));\n            } else if self.info.usart_reg.intstat().read().deltarxbrk() {\n                return Err((i, Error::Break));\n            }\n            let dr = self.info.usart_reg.fiford().read().rxdata() as u8;\n            *b = dr;\n        }\n        Ok(buffer.len())\n    }\n}\n\nimpl<'d> UsartRx<'d, Blocking> {\n    pub fn new_blocking<T: Instance>(_usart: Peri<'d, T>, rx: Peri<'d, impl RxPin<T>>, config: Config) -> Self {\n        let rx_func = rx.pin_func();\n        Usart::<Blocking>::init::<T>(None, Some((rx.into(), rx_func)), config);\n        Self::new_inner(T::info(), T::dma_state(), false, None)\n    }\n}\n\n/// Interrupt handler.\npub struct InterruptHandler<T: Instance> {\n    _uart: PhantomData<T>,\n}\n\nimpl<T: Instance> crate::interrupt::typelevel::Handler<T::Interrupt> for InterruptHandler<T> {\n    unsafe fn on_interrupt() {\n        let regs = T::info().usart_reg;\n        if !regs.fifocfg().read().dmarx() {\n            return;\n        }\n        let state = T::dma_state();\n        state.rx_err.store(true, Ordering::Relaxed);\n        state.rx_err_waker.wake();\n        // Disable the error interrupts instead of clearing the flags. Clearing the\n        // flags would allow the DMA transfer to continue, potentially signaling\n        // completion before we can check for errors that happened *during* the transfer.\n        regs.intenclr().write(|w| {\n            w.set_framerrclr(true);\n            w.set_rxnoiseclr(true);\n            w.set_parityerrclr(true);\n        });\n        regs.fifointenclr().write(|w| w.set_rxerr(true));\n    }\n}\n\nimpl<'d> UsartRx<'d, Async> {\n    /// Read from USART RX into the provided buffer.\n    pub async fn read(&mut self, buffer: &mut [u8]) -> Result<(), Error> {\n        // Clear error flags before the FIFO is drained. Errors that have accumulated\n        // in the flags will also be present in the FIFO.\n        self.dma_state.rx_err.store(false, Ordering::Relaxed);\n        self.info.usart_reg.intenclr().write(|w| {\n            w.set_framerrclr(true);\n            w.set_parityerrclr(true);\n            w.set_rxnoiseclr(true);\n        });\n        self.info.usart_reg.fifointenclr().modify(|w| w.set_rxerr(true));\n        // Then drain the fifo. It is necessary to read at most 16 bytes (the size of FIFO).\n        // Errors that apply to FIFO bytes will be reported directly.\n        let buffer = match {\n            let limit = buffer.len().min(16);\n            self.drain_fifo(&mut buffer[0..limit])\n        } {\n            Ok(len) if len < buffer.len() => &mut buffer[len..],\n            Ok(_) => return Ok(()),\n            Err((_i, e)) => return Err(e),\n        };\n\n        // Start a DMA transfer. If errors have happened in the interim some error\n        // interrupt flags will have been raised, and those will be picked up immediately\n        // by the interrupt handler.\n        // Unwrap() can be used because UsartRx::new() in Async mode always sets it to Some\n        let ch = self.rx_dma.as_mut().unwrap().reborrow();\n\n        self.info.usart_reg.intenset().write(|w| {\n            w.set_framerren(true);\n            w.set_parityerren(true);\n            w.set_rxnoiseen(true);\n        });\n        self.info.usart_reg.fifointenset().modify(|w| w.set_rxerr(true));\n        self.info.usart_reg.fifocfg().modify(|w| w.set_dmarx(true));\n        let transfer = unsafe {\n            // If we don't assign future to a variable, the data register pointer\n            // is held across an await and makes the future non-Send.\n            crate::dma::read(ch, self.info.usart_reg.fiford().as_ptr() as *const _, buffer)\n        };\n\n        // wait for either the transfer to complete or an error to happen.\n        let transfer_result = select(\n            transfer,\n            poll_fn(|cx| {\n                self.dma_state.rx_err_waker.register(cx.waker());\n                match self.dma_state.rx_err.swap(false, Ordering::Relaxed) {\n                    false => Poll::Pending,\n                    e => Poll::Ready(e),\n                }\n            }),\n        )\n        .await;\n\n        let errors = match transfer_result {\n            Either::First(()) => {\n                // The DMA controller finished, BUT if an error occurred on the LAST\n                // byte, then we may still need to grab the error state!\n                self.dma_state.rx_err.swap(false, Ordering::Relaxed)\n            }\n            Either::Second(e) => {\n                // There is an error, which means this is the error that\n                // was problematic.\n                e\n            }\n        };\n\n        // If we got no error, just return at this point\n        if !errors {\n            return Ok(());\n        }\n\n        // If we DID get an error, we need to figure out which one it was.\n        if self.info.usart_reg.intstat().read().framerrint() {\n            return Err(Error::Framing);\n        } else if self.info.usart_reg.intstat().read().parityerrint() {\n            return Err(Error::Parity);\n        } else if self.info.usart_reg.intstat().read().rxnoiseint() {\n            return Err(Error::Noise);\n        } else if self.info.usart_reg.fifointstat().read().rxerr() {\n            return Err(Error::Overrun);\n        }\n        unreachable!(\"unrecognized rx error\");\n    }\n}\n\nimpl<'d> Usart<'d, Blocking> {\n    pub fn new_blocking<T: Instance>(\n        usart: Peri<'d, T>,\n        tx: Peri<'d, impl TxPin<T>>,\n        rx: Peri<'d, impl RxPin<T>>,\n        config: Config,\n    ) -> Self {\n        let tx_func = tx.pin_func();\n        let rx_func = rx.pin_func();\n\n        Self::new_inner(usart, tx.into(), tx_func, rx.into(), rx_func, false, None, None, config)\n    }\n}\n\nimpl<'d> Usart<'d, Async> {\n    pub fn new<T: Instance>(\n        uart: Peri<'d, T>,\n        tx: Peri<'d, impl TxPin<T>>,\n        rx: Peri<'d, impl RxPin<T>>,\n        _irq: impl Binding<T::Interrupt, InterruptHandler<T>>,\n        tx_dma: Peri<'d, impl TxChannel<T>>,\n        rx_dma: Peri<'d, impl RxChannel<T>>,\n        config: Config,\n    ) -> Self {\n        let tx_func = tx.pin_func();\n        let rx_func = rx.pin_func();\n\n        Self::new_inner(\n            uart,\n            tx.into(),\n            tx_func,\n            rx.into(),\n            rx_func,\n            true,\n            Some(tx_dma.into()),\n            Some(rx_dma.into()),\n            config,\n        )\n    }\n}\n\nimpl<'d, M: Mode> Usart<'d, M> {\n    fn new_inner<T: Instance>(\n        _usart: Peri<'d, T>,\n        mut tx: Peri<'d, AnyPin>,\n        tx_func: PioFunc,\n        mut rx: Peri<'d, AnyPin>,\n        rx_func: PioFunc,\n        has_irq: bool,\n        tx_dma: Option<Peri<'d, AnyChannel>>,\n        rx_dma: Option<Peri<'d, AnyChannel>>,\n        config: Config,\n    ) -> Self {\n        Self::init::<T>(Some((tx.reborrow(), tx_func)), Some((rx.reborrow(), rx_func)), config);\n        Self {\n            tx: UsartTx::new_inner(T::info(), tx_dma),\n            rx: UsartRx::new_inner(T::info(), T::dma_state(), has_irq, rx_dma),\n        }\n    }\n\n    fn init<T: Instance>(\n        tx: Option<(Peri<'_, AnyPin>, PioFunc)>,\n        rx: Option<(Peri<'_, AnyPin>, PioFunc)>,\n        config: Config,\n    ) {\n        Self::configure_flexcomm(T::info().fc_reg, T::instance_number());\n        Self::configure_clock::<T>(&config);\n        Self::pin_config::<T>(tx, rx);\n        Self::configure_usart(T::info(), &config);\n    }\n\n    fn configure_clock<T: Instance>(config: &Config) {\n        // Select source clock\n\n        // Adaptive clock choice based on baud rate\n        // To get the desired baud rate, it is essential to choose the clock bigger than baud rate so that it can be 'chiseled'\n        // There are two types of dividers: integer divider (baud rate generator register and oversample selection value)\n        // and fractional divider (fractional rate divider).\n\n        // By default, oversampling rate is 16 which is an industry standard.\n        // That means 16 clocks are used to deliver the byte to recipient.\n        // In this way the probability of getting correct bytes instead of noise directly increases as oversampling increases as well.\n\n        // Minimum and maximum values were computed taking these formulas into account:\n        // For minimum value, MULT = 0, BRGVAL = 0\n        // For maximum value, MULT = 255, BRGVAL = 255\n        // Flexcomm Interface function clock = (clock selected via FCCLKSEL) / (1 + MULT / DIV)\n        // By default, OSRVAL = 15 (see above)\n        // Baud rate = [FCLK / (OSRVAL+1)] / (BRGVAL + 1)\n        let source_clock = match config.baudrate {\n            750_001..=6_000_000 => {\n                SYSCON\n                    .fcclksel(T::instance_number())\n                    .modify(|w| w.set_sel(syscon::vals::FcclkselSel::ENUM_0X3)); // 96 MHz\n                96_000_000\n            }\n            1501..=750_000 => {\n                SYSCON\n                    .fcclksel(T::instance_number())\n                    .modify(|w| w.set_sel(syscon::vals::FcclkselSel::ENUM_0X2)); // 12 MHz\n                12_000_000\n            }\n            121..=1500 => {\n                SYSCON\n                    .fcclksel(T::instance_number())\n                    .modify(|w| w.set_sel(syscon::vals::FcclkselSel::ENUM_0X4)); // 1 MHz\n                1_000_000\n            }\n            _ => {\n                panic!(\"{} baudrate is not permitted in this mode\", config.baudrate);\n            }\n        };\n        // Calculate MULT and BRG values based on baudrate\n\n        // There are two types of dividers: integer divider (baud rate generator register and oversample selection value)\n        // and fractional divider (fractional rate divider).\n        // For oversampling, the default is industry standard 16x oversampling, i.e. OSRVAL = 15\n\n        // The formulas are:\n\n        // FLCK = (clock selected via FCCLKSEL) / (1 + MULT / DIV)\n        // DIV is always 256, then:\n        // FLCK = (clock selected via FCCLKSEL) / (1 + MULT / 256)\n\n        // Baud rate = [FCLK / (OSRVAL+1)] / (BRGVAL + 1) =>\n        // Baud rate = [FCLK / 16] / (BRGVAL + 1)\n\n        // There are 2 unknowns: MULT and BRGVAL.\n        // MULT is responsible for fractional division\n        // BRGVAL is responsible for integer division\n\n        //  The Fractional Rate Generator can be used to obtain more precise baud rates when the\n        //  function clock is not a good multiple of standard (or otherwise desirable) baud rates.\n        //  The FRG is typically set up to produce an integer multiple of the highest required baud\n        //  rate, or a very close approximation. The BRG is then used to obtain the actual baud rate\n        //  needed.\n\n        // Firstly, BRGVAL is calculated to get the raw clock which is a rough approximation that has to be adjusted\n        // so that the desired baud rate is obtained.\n        // Secondly, MULT is calculated to ultimately 'chisel' the clock to get the baud rate.\n        // The deduced formulas are written below.\n\n        let brg_value = (source_clock / (16 * config.baudrate)).min(255);\n        let raw_clock = source_clock / (16 * brg_value);\n        let mult_value = ((raw_clock * 256 / config.baudrate) - 256).min(255);\n\n        // Write values to the registers\n\n        // FCLK =  (clock selected via FCCLKSEL) / (1+ MULT / DIV)\n        // Remark: To use the fractional baud rate generator, 0xFF must be wirtten to the DIV value\n        // to yield a denominator vale of 256. All other values are not supported\n        SYSCON.flexfrgctrl(T::instance_number()).modify(|w| {\n            w.set_div(0xFF);\n            w.set_mult(mult_value as u8);\n        });\n\n        // Baud rate = [FCLK / (OSRVAL+1)] / (BRGVAL + 1)\n        // By default, oversampling is 16x, i.e. OSRVAL = 15\n\n        // Typical industry standard USARTs use a 16x oversample clock to transmit and receive\n        // asynchronous data. This is the number of BRG clocks used for one data bit. The\n        // Oversample Select Register (OSR) allows this USART to use a 16x down to a 5x\n        // oversample clock. There is no oversampling in synchronous modes.\n        T::info()\n            .usart_reg\n            .brg()\n            .modify(|w| w.set_brgval((brg_value - 1) as u16));\n    }\n\n    fn pin_config<T: Instance>(tx: Option<(Peri<'_, AnyPin>, PioFunc)>, rx: Option<(Peri<'_, AnyPin>, PioFunc)>) {\n        if let Some((tx_pin, func)) = tx {\n            tx_pin.pio().modify(|w| {\n                w.set_func(func);\n                w.set_mode(iocon::vals::PioMode::INACTIVE);\n                w.set_slew(iocon::vals::PioSlew::STANDARD);\n                w.set_invert(false);\n                w.set_digimode(iocon::vals::PioDigimode::DIGITAL);\n                w.set_od(iocon::vals::PioOd::NORMAL);\n            });\n        }\n\n        if let Some((rx_pin, func)) = rx {\n            rx_pin.pio().modify(|w| {\n                w.set_func(func);\n                w.set_mode(iocon::vals::PioMode::INACTIVE);\n                w.set_slew(iocon::vals::PioSlew::STANDARD);\n                w.set_invert(false);\n                w.set_digimode(iocon::vals::PioDigimode::DIGITAL);\n                w.set_od(iocon::vals::PioOd::NORMAL);\n            });\n        };\n    }\n\n    fn configure_flexcomm(flexcomm_register: crate::pac::flexcomm::Flexcomm, instance_number: usize) {\n        critical_section::with(|_cs| {\n            if !(SYSCON.ahbclkctrl0().read().iocon()) {\n                SYSCON.ahbclkctrl0().modify(|w| w.set_iocon(true));\n            }\n        });\n        critical_section::with(|_cs| {\n            if !(SYSCON.ahbclkctrl1().read().fc(instance_number)) {\n                SYSCON.ahbclkctrl1().modify(|w| w.set_fc(instance_number, true));\n            }\n        });\n        SYSCON\n            .presetctrl1()\n            .modify(|w| w.set_fc_rst(instance_number, syscon::vals::FcRst::ASSERTED));\n        SYSCON\n            .presetctrl1()\n            .modify(|w| w.set_fc_rst(instance_number, syscon::vals::FcRst::RELEASED));\n        flexcomm_register.pselid().modify(|w| {\n            w.set_persel(flexcomm::vals::Persel::USART);\n            // This will lock the peripheral PERSEL and will not allow any changes until the board is reset.\n            w.set_lock(true);\n        });\n    }\n\n    fn configure_usart(info: &'static Info, config: &Config) {\n        let registers = info.usart_reg;\n        // See section 34.6.1\n        registers.cfg().modify(|w| {\n            // LIN break mode enable\n            // Disabled. Break detect and generate is configured for normal operation.\n            w.set_linmode(false);\n            //CTS Enable. Determines whether CTS is used for flow control. CTS can be from the\n            //input pin, or from the USART’s own RTS if loopback mode is enabled.\n            // No flow control. The transmitter does not receive any automatic flow control signal.\n            w.set_ctsen(false);\n            // Selects synchronous or asynchronous operation.\n            w.set_syncen(usart::vals::Syncen::ASYNCHRONOUS_MODE);\n            // Selects the clock polarity and sampling edge of received data in synchronous mode.\n            w.set_clkpol(usart::vals::Clkpol::RISING_EDGE);\n            // Synchronous mode Master select.\n            // When synchronous mode is enabled, the USART is a master.\n            w.set_syncmst(usart::vals::Syncmst::MASTER);\n            // Selects data loopback mode\n            w.set_loop_(usart::vals::Loop::NORMAL);\n            // Output Enable Turnaround time enable for RS-485 operation.\n            // Disabled. If selected by OESEL, the Output Enable signal deasserted at the end of\n            // the last stop bit of a transmission.\n            w.set_oeta(false);\n            // Output enable select.\n            // Standard. The RTS signal is used as the standard flow control function.\n            w.set_oesel(usart::vals::Oesel::STANDARD);\n            // Automatic address matching enable.\n            // Disabled. When addressing is enabled by ADDRDET, address matching is done by\n            // software. This provides the possibility of versatile addressing (e.g. respond to more\n            // than one address)\n            w.set_autoaddr(false);\n            // Output enable polarity.\n            // Low. If selected by OESEL, the output enable is active low.\n            w.set_oepol(usart::vals::Oepol::LOW);\n        });\n\n        // Configurations based on the config written by a user\n        registers.cfg().modify(|w| {\n            w.set_datalen(match config.data_bits {\n                DataBits::DataBits7 => usart::vals::Datalen::BIT_7,\n                DataBits::DataBits8 => usart::vals::Datalen::BIT_8,\n                DataBits::DataBits9 => usart::vals::Datalen::BIT_9,\n            });\n            w.set_paritysel(match config.parity {\n                Parity::ParityNone => usart::vals::Paritysel::NO_PARITY,\n                Parity::ParityEven => usart::vals::Paritysel::EVEN_PARITY,\n                Parity::ParityOdd => usart::vals::Paritysel::ODD_PARITY,\n            });\n            w.set_stoplen(match config.stop_bits {\n                StopBits::Stop1 => usart::vals::Stoplen::BIT_1,\n                StopBits::Stop2 => usart::vals::Stoplen::BITS_2,\n            });\n            w.set_rxpol(match config.invert_rx {\n                false => usart::vals::Rxpol::STANDARD,\n                true => usart::vals::Rxpol::INVERTED,\n            });\n            w.set_txpol(match config.invert_tx {\n                false => usart::vals::Txpol::STANDARD,\n                true => usart::vals::Txpol::INVERTED,\n            });\n        });\n\n        // DMA-related settings\n        registers.fifocfg().modify(|w| {\n            w.set_dmatx(false);\n            w.set_dmarx(false);\n        });\n\n        // Enabling USART\n        registers.fifocfg().modify(|w| {\n            w.set_enabletx(true);\n            w.set_enablerx(true);\n        });\n        registers.cfg().modify(|w| {\n            w.set_enable(true);\n        });\n\n        registers.fifointenset().modify(|w| {\n            w.set_rxerr(true);\n        });\n\n        // Drain RX FIFO in case it still has some unrelevant data\n        while registers.fifostat().read().rxnotempty() {\n            let _ = registers.fiford().read().0;\n        }\n    }\n}\n\nimpl<'d, M: Mode> Usart<'d, M> {\n    /// Transmit the provided buffer blocking execution until done.\n    pub fn blocking_write(&mut self, buffer: &[u8]) -> Result<(), Error> {\n        self.tx.blocking_write(buffer)\n    }\n\n    /// Flush USART TX blocking execution until done.\n    pub fn blocking_flush(&mut self) -> Result<(), Error> {\n        self.tx.blocking_flush()\n    }\n\n    /// Read from USART RX blocking execution until done.\n    pub fn blocking_read(&mut self, buffer: &mut [u8]) -> Result<(), Error> {\n        self.rx.blocking_read(buffer)\n    }\n\n    /// Check if UART is busy transmitting.\n    pub fn tx_busy(&self) -> bool {\n        self.tx.tx_busy()\n    }\n\n    /// Split the Usart into a transmitter and receiver, which is particularly\n    /// useful when having two tasks correlating to transmitting and receiving.\n    pub fn split(self) -> (UsartTx<'d, M>, UsartRx<'d, M>) {\n        (self.tx, self.rx)\n    }\n\n    /// Split the Usart into a transmitter and receiver by mutable reference,\n    /// which is particularly useful when having two tasks correlating to\n    /// transmitting and receiving.\n    pub fn split_ref(&mut self) -> (&mut UsartTx<'d, M>, &mut UsartRx<'d, M>) {\n        (&mut self.tx, &mut self.rx)\n    }\n}\n\nimpl<'d> Usart<'d, Async> {\n    /// Write to UART TX from the provided buffer.\n    pub async fn write(&mut self, buffer: &[u8]) -> Result<(), Error> {\n        self.tx.write(buffer).await\n    }\n\n    /// Read from UART RX into the provided buffer.\n    pub async fn read(&mut self, buffer: &mut [u8]) -> Result<(), Error> {\n        self.rx.read(buffer).await\n    }\n}\nimpl<'d, M: Mode> embedded_hal_02::blocking::serial::Write<u8> for UsartTx<'d, M> {\n    type Error = Error;\n\n    fn bwrite_all(&mut self, buffer: &[u8]) -> Result<(), Self::Error> {\n        self.blocking_write(buffer)\n    }\n\n    fn bflush(&mut self) -> Result<(), Self::Error> {\n        self.blocking_flush()\n    }\n}\n\nimpl<'d, M: Mode> embedded_hal_02::blocking::serial::Write<u8> for Usart<'d, M> {\n    type Error = Error;\n\n    fn bwrite_all(&mut self, buffer: &[u8]) -> Result<(), Self::Error> {\n        self.blocking_write(buffer)\n    }\n\n    fn bflush(&mut self) -> Result<(), Self::Error> {\n        self.blocking_flush()\n    }\n}\n\nimpl<'d> embedded_io::ErrorType for UsartTx<'d, Blocking> {\n    type Error = Error;\n}\n\nimpl<'d> embedded_io::Write for UsartTx<'d, Blocking> {\n    fn write(&mut self, buf: &[u8]) -> Result<usize, Self::Error> {\n        self.blocking_write(buf).map(|_| buf.len())\n    }\n\n    fn flush(&mut self) -> Result<(), Self::Error> {\n        self.blocking_flush()\n    }\n}\n\nimpl<'d> embedded_io::ErrorType for UsartRx<'d, Blocking> {\n    type Error = Error;\n}\n\nimpl<'d> embedded_io::Read for UsartRx<'d, Blocking> {\n    fn read(&mut self, buf: &mut [u8]) -> Result<usize, Self::Error> {\n        self.blocking_read(buf).map(|_| buf.len())\n    }\n}\n\nimpl<'d> embedded_io::ErrorType for Usart<'d, Blocking> {\n    type Error = Error;\n}\n\nimpl<'d> embedded_io::Write for Usart<'d, Blocking> {\n    fn write(&mut self, buf: &[u8]) -> Result<usize, Self::Error> {\n        self.blocking_write(buf).map(|_| buf.len())\n    }\n\n    fn flush(&mut self) -> Result<(), Self::Error> {\n        self.blocking_flush()\n    }\n}\n\nimpl<'d> embedded_io::Read for Usart<'d, Blocking> {\n    fn read(&mut self, buf: &mut [u8]) -> Result<usize, Self::Error> {\n        self.blocking_read(buf).map(|_| buf.len())\n    }\n}\n\npub(crate) struct Info {\n    pub(crate) usart_reg: UsartReg,\n    pub(crate) fc_reg: FlexcommReg,\n    pub(crate) interrupt: Interrupt,\n}\n\npub(crate) trait SealedInstance {\n    fn info() -> &'static Info;\n    fn dma_state() -> &'static DmaState;\n    fn instance_number() -> usize;\n}\n\n/// UART instance.\n#[allow(private_bounds)]\npub trait Instance: SealedInstance + PeripheralType {\n    /// Interrupt for this instance.\n    type Interrupt: crate::interrupt::typelevel::Interrupt;\n}\n\nmacro_rules! impl_usart_instance {\n    ($inst:ident, $fc:ident, $fc_num:expr) => {\n        impl crate::usart::SealedInstance for $crate::peripherals::$inst {\n            fn info() -> &'static crate::usart::Info {\n                use crate::interrupt::typelevel::Interrupt;\n                use crate::usart::Info;\n\n                static INFO: Info = Info {\n                    usart_reg: crate::pac::$inst,\n                    fc_reg: crate::pac::$fc,\n                    interrupt: crate::interrupt::typelevel::$fc::IRQ,\n                };\n                &INFO\n            }\n\n            fn dma_state() -> &'static crate::usart::DmaState {\n                use core::sync::atomic::AtomicBool;\n\n                use embassy_sync::waitqueue::AtomicWaker;\n\n                use crate::usart::DmaState;\n\n                static STATE: DmaState = DmaState {\n                    rx_err_waker: AtomicWaker::new(),\n                    rx_err: AtomicBool::new(false),\n                };\n                &STATE\n            }\n            #[inline]\n            fn instance_number() -> usize {\n                $fc_num\n            }\n        }\n        impl $crate::usart::Instance for $crate::peripherals::$inst {\n            type Interrupt = crate::interrupt::typelevel::$fc;\n        }\n    };\n}\n\n#[cfg(has_usart_txd_pins)]\npub(crate) trait SealedTxPin<T: Instance>: crate::gpio::Pin {\n    fn pin_func(&self) -> PioFunc;\n}\n\n#[cfg(has_usart_rxd_pins)]\npub(crate) trait SealedRxPin<T: Instance>: crate::gpio::Pin {\n    fn pin_func(&self) -> PioFunc;\n}\n\n#[cfg(has_usart_cts_pins)]\npub(crate) trait SealedCtsPin<T: Instance>: crate::gpio::Pin {\n    // TODO(wt): This needs to be wired up in the usart driver.\n    #[allow(unused)]\n    fn pin_func(&self) -> PioFunc;\n}\n\n#[cfg(has_usart_rts_pins)]\npub(crate) trait SealedRtsPin<T: Instance>: crate::gpio::Pin {\n    // TODO(wt): This needs to be wired up in the usart driver.\n    #[allow(unused)]\n    fn pin_func(&self) -> PioFunc;\n}\n\n#[cfg(has_usart_sck_pins)]\npub(crate) trait SealedSckPin<T: Instance>: crate::gpio::Pin {\n    // TODO(wt): This needs to be wired up in the usart driver.\n    #[allow(unused)]\n    fn pin_func(&self) -> PioFunc;\n}\n\n/// Trait for TX pins.\n#[cfg(has_usart_txd_pins)]\n#[allow(private_bounds)]\npub trait TxPin<T: Instance>: SealedTxPin<T> + crate::gpio::Pin {}\n\n/// Trait for RX pins.\n#[cfg(has_usart_rxd_pins)]\n#[allow(private_bounds)]\npub trait RxPin<T: Instance>: SealedRxPin<T> + crate::gpio::Pin {}\n\n/// Trait for Cts pins.\n#[cfg(has_usart_cts_pins)]\n#[allow(private_bounds)]\npub trait CtsPin<T: Instance>: SealedCtsPin<T> + crate::gpio::Pin {}\n\n/// Trait for Rts pins.\n#[cfg(has_usart_rts_pins)]\n#[allow(private_bounds)]\npub trait RtsPin<T: Instance>: SealedRtsPin<T> + crate::gpio::Pin {}\n\n/// Trait for Sck pins.\n#[cfg(has_usart_sck_pins)]\n#[allow(private_bounds)]\npub trait SckPin<T: Instance>: SealedSckPin<T> + crate::gpio::Pin {}\n\n#[cfg(has_usart_txd_pins)]\nmacro_rules! impl_usart_txd_pin {\n    ($pin:ident, $instance:ident, $func: ident) => {\n        impl crate::usart::SealedTxPin<crate::peripherals::$instance> for crate::peripherals::$pin {\n            fn pin_func(&self) -> crate::pac::iocon::vals::PioFunc {\n                use crate::pac::iocon::vals::PioFunc;\n                PioFunc::$func\n            }\n        }\n\n        impl crate::usart::TxPin<crate::peripherals::$instance> for crate::peripherals::$pin {}\n    };\n}\n\n#[cfg(has_usart_rxd_pins)]\nmacro_rules! impl_usart_rxd_pin {\n    ($pin:ident, $instance:ident, $func: ident) => {\n        impl crate::usart::SealedRxPin<crate::peripherals::$instance> for crate::peripherals::$pin {\n            fn pin_func(&self) -> crate::pac::iocon::vals::PioFunc {\n                use crate::pac::iocon::vals::PioFunc;\n                PioFunc::$func\n            }\n        }\n\n        impl crate::usart::RxPin<crate::peripherals::$instance> for crate::peripherals::$pin {}\n    };\n}\n\n#[cfg(has_usart_cts_pins)]\nmacro_rules! impl_usart_cts_pin {\n    ($pin:ident, $instance:ident, $func: ident) => {\n        impl crate::usart::SealedCtsPin<crate::peripherals::$instance> for crate::peripherals::$pin {\n            fn pin_func(&self) -> crate::pac::iocon::vals::PioFunc {\n                use crate::pac::iocon::vals::PioFunc;\n                PioFunc::$func\n            }\n        }\n\n        impl crate::usart::CtsPin<crate::peripherals::$instance> for crate::peripherals::$pin {}\n    };\n}\n\n#[cfg(has_usart_rts_pins)]\nmacro_rules! impl_usart_rts_pin {\n    ($pin:ident, $instance:ident, $func: ident) => {\n        impl crate::usart::SealedRtsPin<crate::peripherals::$instance> for crate::peripherals::$pin {\n            fn pin_func(&self) -> crate::pac::iocon::vals::PioFunc {\n                use crate::pac::iocon::vals::PioFunc;\n                PioFunc::$func\n            }\n        }\n\n        impl crate::usart::RtsPin<crate::peripherals::$instance> for crate::peripherals::$pin {}\n    };\n}\n\n#[cfg(has_usart_sck_pins)]\nmacro_rules! impl_usart_sck_pin {\n    ($pin:ident, $instance:ident, $func: ident) => {\n        impl crate::usart::SealedSckPin<crate::peripherals::$instance> for crate::peripherals::$pin {\n            fn pin_func(&self) -> crate::pac::iocon::vals::PioFunc {\n                use crate::pac::iocon::vals::PioFunc;\n                PioFunc::$func\n            }\n        }\n\n        impl crate::usart::SckPin<crate::peripherals::$instance> for crate::peripherals::$pin {}\n    };\n}\n\n/// Marker trait indicating a DMA channel may be used for USART transmit.\npub trait TxChannel<T: Instance>: crate::dma::Channel {}\n\n/// Marker trait indicating a DMA channel may be used for USART recieve.\npub trait RxChannel<T: Instance>: crate::dma::Channel {}\n\nmacro_rules! impl_usart_tx_channel {\n    ($instance: ident, $channel: ident) => {\n        impl crate::usart::TxChannel<crate::peripherals::$instance> for crate::peripherals::$channel {}\n    };\n}\n\nmacro_rules! impl_usart_rx_channel {\n    ($instance: ident, $channel: ident) => {\n        impl crate::usart::RxChannel<crate::peripherals::$instance> for crate::peripherals::$channel {}\n    };\n}\n"
  },
  {
    "path": "embassy-nxp/src/usart.rs",
    "content": "#![macro_use]\n\n//! Universal Synchronous/Asynchronous Receiver/Transmitter (USART) driver.\n\n#[cfg_attr(lpc55, path = \"./usart/lpc55.rs\")]\nmod inner;\npub use inner::*;\n"
  },
  {
    "path": "embassy-rp/CHANGELOG.md",
    "content": "# Changelog for embassy-rp\n\nAll notable changes to this project will be documented in this file.\n\nThe format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),\nand this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).\n\n<!-- next-header -->\n## Unreleased - ReleaseDate\n\n## 0.10.0 - 2026-03-10\n- Add AON Timer driver for RP2350 with configurable clock sources and alarm wake modes\n- Add output enable inversion API (gpio, pio)\n- Add PIO clock generator\n- Change PioBatch interface\n- Add custom multicore-ready executors\n- breaking: Change watchdog interface — `feed` now takes a duration argument, added `stop`\n- Add DMA `Channel` driver struct\n- breaking: DMA renames — `read_repeated`→`read_discard`, `write_repeated`→`write_zeros`, `dma_push_repeated`→`dma_push_zeros`, `dma_pull_repeated`→`dma_pull_discard`\n- DMA: add byte swap option\n- DMA: made `Channel::regs` private\n- DMA: disallow construction of `Transfer` outside dma.rs\n- Fix race in DMA IRQ handler\n- Add PIO StateMachine `rx_fifo_ptr`, `tx_fifo_ptr`, `rx_treq`, `tx_treq` functions\n- Add I2S start/stop functions\n- Fix onewire bug with multiple family codes\n- Fix PIO freeze regression\n- Increase VCO max frequency to 1600 MHz\n- Allow sourcing gpout clock from LPOSC\n- Complete missing Gpin/GpoutPin impls for RP235x\n- Improve PIO clock divider math\n- Fix chrono compilation\n- Update embassy-sync to 0.8.0\n- Update embassy-embedded-hal to 0.6.0\n\n## 0.9.0 - 2025-11-27\n- Add documentation for pio `get_x` about autopush.\n- Fix several minor typos in documentation\n- Add PIO SPI\n- Add PIO I2S input\n- Add PIO onewire parasite power strong pullup\n- add `wait_for_alarm` and `alarm_scheduled` methods to rtc module ([#4216](https://github.com/embassy-rs/embassy/pull/4216))\n- rp235x: use msplim for stack guard instead of MPU\n- Add reset_to_usb_boot for rp235x ([#4705](https://github.com/embassy-rs/embassy/pull/4705))\n- Add fix #4822 in PIO onewire. Change to disable the state machine before setting y register ([#4824](https://github.com/embassy-rs/embassy/pull/4824))\n- Add PIO::Ws2812 color order support\n- Fix configuration of embassy_rp adc div register ([#4815](https://github.com/embassy-rs/embassy/pull/4815))\n- Add TX-only, no SCK SPI support\n- Remove atomic-polyfill with critical-section instead ([#4948](https://github.com/embassy-rs/embassy/pull/4948))\n\n## 0.8.0 - 2025-08-26\n\n## 0.7.1 - 2025-08-26\n- add `i2c` internal pullup options ([#4564](https://github.com/embassy-rs/embassy/pull/4564))\n\n## 0.7.0 - 2025-08-04\n\n- changed: update to latest embassy-time-queue-utils\n\n## 0.6.0 - 2025-07-16\n\n- update to latest embassy-usb-driver\n\n## 0.5.0 - 2025-07-15\n\n- Fix wrong `funcsel` on RP2350 gpout/gpin ([#3975](https://github.com/embassy-rs/embassy/pull/3975))\n- Fix potential race condition in `ADC::wait_for_ready` ([#4012](https://github.com/embassy-rs/embassy/pull/4012))\n- `flash`: rename `BOOTROM_BASE` to `BOOTRAM_BASE` ([#4014](https://github.com/embassy-rs/embassy/pull/4014))\n- Remove `Peripheral` trait & rename `PeripheralRef` to `Peri` ([#3999](https://github.com/embassy-rs/embassy/pull/3999))\n- Fix watchdog count on RP235x ([#4021](https://github.com/embassy-rs/embassy/pull/4021))\n- I2C: ensure that wakers are registered before checking status of `wait_on` helpers ([#4043](https://github.com/embassy-rs/embassy/pull/4043))\n- Modify `Uarte` and `BufferedUarte` initialization to take pins before interrupts ([#3983](https://github.com/embassy-rs/embassy/pull/3983))\n- `uart`: increase RX FIFO watermark from 1/8 to 7/8 ([#4055](https://github.com/embassy-rs/embassy/pull/4055))\n- Add `spinlock_mutex` ([#4017](https://github.com/embassy-rs/embassy/pull/4017))\n- Enable input mode for PWM pins on RP235x and disable it on drop ([#4093](https://github.com/embassy-rs/embassy/pull/4093))\n- Add `impl rand_core::CryptoRng for Trng` ([#4096](https://github.com/embassy-rs/embassy/pull/4096))\n- `pwm`: enable pull-down resistors for pins in `Drop` implementation ([#4115](https://github.com/embassy-rs/embassy/pull/4115))\n- Rewrite PIO onewire implementation ([#4128](https://github.com/embassy-rs/embassy/pull/4128))\n- Implement RP2040 overclocking ([#4150](https://github.com/embassy-rs/embassy/pull/4150))\n- Implement RP235x overclocking ([#4187](https://github.com/embassy-rs/embassy/pull/4187))\n- `trng`: improve error handling ([#4139](https://github.com/embassy-rs/embassy/pull/4139))\n- Remove `<T: Instance>` from `Uart` and `BufferedUart` ([#4155](https://github.com/embassy-rs/embassy/pull/4155))\n- Make bit-depth of I2S PIO program configurable ([#4193](https://github.com/embassy-rs/embassy/pull/4193))\n- Add the possibility to document `bind_interrupts` `struct`s ([#4206](https://github.com/embassy-rs/embassy/pull/4206))\n- Add missing `Debug` and `defmt::Format` `derive`s for ADC & `AnyPin` ([#4205](https://github.com/embassy-rs/embassy/pull/4205))\n- Add `rand-core` v0.9 support ([#4217](https://github.com/embassy-rs/embassy/pull/4217))\n- Update `embassy-sync` to v0.7.0 ([#4234](https://github.com/embassy-rs/embassy/pull/4234))\n- Add compatibility with ws2812 leds that have 4 addressable lights ([#4236](https://github.com/embassy-rs/embassy/pull/4236))\n- Implement input/output inversion ([#4237](https://github.com/embassy-rs/embassy/pull/4237))\n- Add `multicore::current_core` API ([#4362](https://github.com/embassy-rs/embassy/pull/4362))\n\n## 0.4.0 - 2025-03-09\n\n- Add PIO functions. ([#3857](https://github.com/embassy-rs/embassy/pull/3857))\n  The functions added in this change are `get_addr` `get_tx_threshold`, `set_tx_threshold`, `get_rx_threshold`, `set_rx_threshold`, `set_thresholds`.\n- Expose the watchdog reset reason. ([#3877](https://github.com/embassy-rs/embassy/pull/3877))\n- Update pio-rs, reexport, move instr methods to SM. ([#3865](https://github.com/embassy-rs/embassy/pull/3865))\n- rp235x: add ImageDef features. ([#3890](https://github.com/embassy-rs/embassy/pull/3890))\n- doc: Fix \"the the\" ([#3903](https://github.com/embassy-rs/embassy/pull/3903))\n- pio: Add access to DMA engine byte swapping ([#3935](https://github.com/embassy-rs/embassy/pull/3935))\n- Modify BufferedUart initialization to take pins before interrupts ([#3983](https://github.com/embassy-rs/embassy/pull/3983))\n\n## 0.3.1 - 2025-02-06\n\nSmall release fixing a few gnarly bugs, upgrading is strongly recommended.\n\n- Fix a race condition in the time driver that could cause missed interrupts. ([#3758](https://github.com/embassy-rs/embassy/issues/3758), [#3763](https://github.com/embassy-rs/embassy/pull/3763))\n- rp235x: Make atomics work across cores. ([#3851](https://github.com/embassy-rs/embassy/pull/3851))\n- rp235x: add workaround \"SIO spinlock stuck after reset\" bug, same as RP2040 ([#3851](https://github.com/embassy-rs/embassy/pull/3851))\n- rp235x: Ensure core1 is reset if core0 resets. ([#3851](https://github.com/embassy-rs/embassy/pull/3851))\n- rp235xb: correct ADC channel numbers. ([#3823](https://github.com/embassy-rs/embassy/pull/3823))\n- rp235x: enable watchdog tick generator. ([#3777](https://github.com/embassy-rs/embassy/pull/3777))\n- Relax I2C address validity check to allow using 7-bit addresses that would be reserved for 10-bit addresses. ([#3809](https://github.com/embassy-rs/embassy/issues/3809), [#3810](https://github.com/embassy-rs/embassy/pull/3810))\n\n## 0.3.0 - 2025-01-05\n\n- Updated `embassy-time` to v0.4\n- Initial rp235x support\n- Setup timer0 tick when initializing clocks\n- Allow separate control of duty cycle for each channel in a pwm slice by splitting the Pwm driver.\n- Implement `embedded_io::Write` for Uart<'d, T: Instance, Blocking> and UartTx<'d, T: Instance, Blocking>\n- Add `set_pullup()` to OutputOpenDrain.\n\n## 0.2.0 - 2024-08-05\n\n- Add read_to_break_with_count\n- add option to provide your own boot2\n- Add multichannel ADC\n- Add collapse_debuginfo to fmt.rs macros.\n- Use raw slices .len() method instead of unsafe hacks.\n- Add missing word \"pin\" in rp pwm documentation\n- Add Clone and Copy to Error types\n- fix spinlocks staying locked after reset.\n- wait until read matches for PSM accesses.\n- Remove generics\n- fix drop implementation of BufferedUartRx and BufferedUartTx\n- implement `embedded_storage_async::nor_flash::MultiwriteNorFlash`\n- rp usb: wake ep-wakers after stalling\n- rp usb: add stall implementation\n- Add parameter for enabling pull-up and pull-down in RP PWM input mode\n- rp: remove mod sealed.\n- rename pins data type and the macro\n- rename pwm channels to pwm slices, including in documentation\n- rename the Channel trait to Slice and the PwmPin to PwmChannel\n- i2c: Fix race condition that appears on fast repeated transfers.\n- Add a basic \"read to break\" function\n"
  },
  {
    "path": "embassy-rp/Cargo.toml",
    "content": "[package]\nname = \"embassy-rp\"\nversion = \"0.10.0\"\nedition = \"2024\"\nlicense = \"MIT OR Apache-2.0\"\ndescription = \"Embassy Hardware Abstraction Layer (HAL) for the Raspberry Pi RP2040 or RP235x microcontroller\"\nkeywords = [\"embedded\", \"async\", \"rp235x\", \"rp2040\", \"embedded-hal\"]\ncategories = [\"embedded\", \"hardware-support\", \"no-std\", \"asynchronous\"]\nrepository = \"https://github.com/embassy-rs/embassy\"\ndocumentation = \"https://docs.embassy.dev/embassy-rp\"\n\n[package.metadata.embassy]\nbuild = [\n    {target = \"thumbv6m-none-eabi\", features = [\"defmt\", \"rp2040\", \"time-driver\"]},\n    {target = \"thumbv6m-none-eabi\", features = [\"chrono\", \"rp2040\", \"time-driver\"]},\n    {target = \"thumbv6m-none-eabi\", features = [\"chrono\", \"defmt\", \"rp2040\", \"time-driver\"]},\n    {target = \"thumbv6m-none-eabi\", features = [\"log\", \"rp2040\", \"time-driver\"]},\n    {target = \"thumbv6m-none-eabi\", features = [\"intrinsics\", \"rp2040\", \"time-driver\"]},\n    {target = \"thumbv6m-none-eabi\", features = [\"qspi-as-gpio\", \"rp2040\", \"time-driver\"]},\n    {target = \"thumbv8m.main-none-eabihf\", features = [\"defmt\", \"rp235xa\", \"time-driver\"]},\n    {target = \"thumbv8m.main-none-eabihf\", features = [\"chrono\", \"rp235xa\", \"time-driver\"]},\n    {target = \"thumbv8m.main-none-eabihf\", features = [\"chrono\", \"defmt\", \"rp235xa\", \"time-driver\"]},\n    {target = \"thumbv8m.main-none-eabihf\", features = [\"log\", \"rp235xa\", \"time-driver\"]},\n    {target = \"thumbv8m.main-none-eabihf\", features = [\"binary-info\", \"rp235xa\", \"time-driver\"]},\n]\n\n[package.metadata.embassy_docs]\nsrc_base = \"https://github.com/embassy-rs/embassy/blob/embassy-rp-v$VERSION/embassy-rp/src/\"\nsrc_base_git = \"https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-rp/src/\"\nfeatures = [\"defmt\", \"unstable-pac\", \"time-driver\"]\nflavors = [\n    { name = \"rp2040\", target = \"thumbv6m-none-eabi\", features = [\"rp2040\"] },\n    { name = \"rp235xa\", target = \"thumbv8m.main-none-eabihf\", features = [\"rp235xa\"] },\n    { name = \"rp235xb\", target = \"thumbv8m.main-none-eabihf\", features = [\"rp235xb\"] },\n]\n\n[package.metadata.docs.rs]\n# TODO: it's not GREAT to set a specific target, but docs.rs builds will fail otherwise\n# for now, default to rp2040\nfeatures = [\"defmt\", \"unstable-pac\", \"time-driver\", \"rp2040\"]\n\n[features]\ndefault = [ \"rt\" ]\n\n## Enable the `rt` feature of [`rp-pac`](https://docs.rs/rp-pac).\n## With `rt` enabled the PAC provides interrupt vectors instead of letting [`cortex-m-rt`](https://docs.rs/cortex-m-rt) do that.\n## See <https://docs.rs/cortex-m-rt/latest/cortex_m_rt/#device> for more info.\nrt = [ \"rp-pac/rt\" ]\n\n## Enable [defmt support](https://docs.rs/defmt) and enables `defmt` debug-log messages and formatting in embassy drivers.\ndefmt = [\"dep:defmt\", \"embassy-usb-driver/defmt\", \"embassy-hal-internal/defmt\", \"chrono?/defmt\"]\n## Enable log support\nlog = [\"dep:log\"]\n## Enable chrono support\nchrono = [\"dep:chrono\"]\n\n## Configure the [`critical-section`](https://docs.rs/critical-section) crate to use an implementation that is safe for multicore use on rp2040.\ncritical-section-impl = [\"critical-section/restore-state-u8\"]\n\n## Reexport the PAC for the currently enabled chip at `embassy_rp::pac`.\n## This is unstable because semver-minor (non-breaking) releases of `embassy-rp` may major-bump (breaking) the PAC version.\n## If this is an issue for you, you're encouraged to directly depend on a fixed version of the PAC.\n## There are no plans to make this stable.\nunstable-pac = []\n\n## Enable the timer for use with `embassy-time` with a 1MHz tick rate.\ntime-driver = [\"dep:embassy-time-driver\", \"embassy-time-driver?/tick-hz-1_000_000\", \"dep:embassy-time-queue-utils\"]\n\n## Enable the thread-mode executor.\nexecutor-thread = [\"dep:embassy-executor\", \"dep:embassy-executor-macros\"]\n\n## Enable the interrupt-mode executor.\nexecutor-interrupt = [\"dep:embassy-executor\", \"dep:embassy-executor-macros\"]\n\n## Enable ROM function cache. This will store the address of a ROM function when first used, improving performance of subsequent calls.\nrom-func-cache = []\n## Enable implementations of some compiler intrinsics using functions in the rp2040 Mask ROM.\n## These should be as fast or faster than the implementations in compiler-builtins. They also save code space and reduce memory contention.\n## Compiler intrinsics are used automatically, you do not need to change your code to get performance improvements from this feature.\nintrinsics = []\n## Enable intrinsics based on extra ROM functions added in the v2 version of the rp2040 Mask ROM.\n## This version added a lot more floating point operations - many f64 functions and a few f32 functions were added in ROM v2.\nrom-v2-intrinsics = []\n\n## Allow using QSPI pins as GPIO pins. This is mostly not what you want (because your flash is attached via QSPI pins)\n## and adds code and memory overhead when this feature is enabled.\nqspi-as-gpio = []\n\n## Indicate code is running from RAM.\n## Set this if all code is in RAM, and the cores never access memory-mapped flash memory through XIP.\n## This allows the flash driver to not force pausing execution on both cores when doing flash operations.\nrun-from-ram = []\n\n#! ### boot2 flash chip support\n#! RP2040's internal bootloader is only able to run code from the first 256 bytes of flash.\n#! A 2nd stage bootloader (boot2) is required to run larger programs from external flash.\n#! Select from existing boot2 implementations via the following features. If none are selected,\n#! boot2-w25q080 will be used (w25q080 is the flash chip used on the pico).\n#! Each implementation uses flash commands and timings specific to a QSPI flash chip family for better performance.\n## Use boot2 with support for Renesas/Dialog AT25SF128a SPI flash.\nboot2-at25sf128a = []\n## Use boot2 with support for Gigadevice GD25Q64C SPI flash.\nboot2-gd25q64cs = []\n## Use boot2 that only uses generic flash commands - these are supported by all SPI flash, but are slower.\nboot2-generic-03h = []\n## Use boot2 with support for ISSI IS25LP080 SPI flash.\nboot2-is25lp080 = []\n## Use boot2 that copies the entire program to RAM before booting. This uses generic flash commands to perform the copy.\nboot2-ram-memcpy = []\n## Use boot2 with support for Winbond W25Q080 SPI flash.\nboot2-w25q080 = []\n## Use boot2 with support for Winbond W25X10CL SPI flash.\nboot2-w25x10cl = []\n## Have embassy-rp not provide the boot2 so you can use your own.\n## Place your own in the \".boot2\" section like:\n## ```\n## #[unsafe(link_section = \".boot2\")]\n## #[used]\n## static BOOT2: [u8; 256] = [0; 256]; // Provide your own with e.g. include_bytes!\n## ```\nboot2-none = []\n\n#! ### Image Definition support\n#! RP2350's internal bootloader will only execute firmware if it has a valid Image Definition.\n#! There are other tags that can be used (for example, you can tag an image as \"DATA\")\n#! but programs will want to have an exe header. \"SECURE_EXE\" is usually what you want.\n#! Use imagedef-none if you want to configure the Image Definition manually\n#! If none are selected, imagedef-secure-exe will be used\n\n## Image is executable and starts in Secure mode\nimagedef-secure-exe = []\n## Image is executable and starts in Non-secure mode\nimagedef-nonsecure-exe = []\n\n## Have embassy-rp not provide the Image Definition so you can use your own.\n## Place your own in the \".start_block\" section like:\n## ```ignore\n## use embassy_rp::block::ImageDef;\n##\n## #[unsafe(link_section = \".start_block\")]\n## #[used]\n## static IMAGE_DEF: ImageDef = ImageDef::secure_exe(); // Update this with your own implementation.\n## ```\nimagedef-none = []\n\n## Configure the hal for use with the rp2040\nrp2040 = [\"rp-pac/rp2040\"]\n_rp235x = [\"rp-pac/rp235x\"]\n## Configure the hal for use with the rp235xA\nrp235xa = [\"_rp235x\"]\n## Configure the hal for use with the rp235xB\nrp235xb = [\"_rp235x\"]\n\n# hack around cortex-m peripherals being wrong when running tests.\n_test = []\n\n## Add a binary-info header block containing picotool-compatible metadata.\n##\n## Takes up a little flash space, but picotool can then report the name of your\n## program and other details.\nbinary-info = [\"rt\", \"dep:rp-binary-info\", \"rp-binary-info?/binary-info\"]\n\n[dependencies]\nembassy-sync = { version = \"0.8.0\", path = \"../embassy-sync\" }\nembassy-time-driver = { version = \"0.2.2\", path = \"../embassy-time-driver\", optional = true }\nembassy-time-queue-utils = { version = \"0.3.0\", path = \"../embassy-time-queue-utils\", optional = true }\nembassy-time = { version = \"0.5.1\", path = \"../embassy-time\" }\nembassy-futures = { version = \"0.1.2\", path = \"../embassy-futures\" }\nembassy-hal-internal = { version = \"0.5.0\", path = \"../embassy-hal-internal\", features = [\"cortex-m\", \"prio-bits-2\"] }\nembassy-embedded-hal = { version = \"0.6.0\", path = \"../embassy-embedded-hal\" }\nembassy-usb-driver = { version = \"0.2.0\", path = \"../embassy-usb-driver\" }\nembassy-executor = { version = \"0.10.0\", path = \"../embassy-executor\", optional = true }\nembassy-executor-macros = { version = \"0.8.0\", path = \"../embassy-executor-macros\", optional = true }\ndefmt = { version = \"1.0.1\", optional = true }\nlog = { version = \"0.4.14\", optional = true }\nnb = \"1.1.0\"\ncfg-if = \"1.0.0\"\ncortex-m-rt = \">=0.6.15,<0.8\"\ncortex-m = \"0.7.6\"\ncritical-section = \"1.2.0\"\nchrono = { version = \"0.4.43\", default-features = false, optional = true }\nembedded-io = { version = \"0.7.1\" }\nembedded-io-async = { version = \"0.7.0\" }\nembedded-storage = { version = \"0.3\" }\nembedded-storage-async = { version = \"0.4.1\" }\nfixed = \"1.28.0\"\n\nrp-pac = { version = \"7.0.0\" }\n\nembedded-hal-02 = { package = \"embedded-hal\", version = \"0.2.6\", features = [\"unproven\"] }\nembedded-hal-1 = { package = \"embedded-hal\", version = \"1.0\" }\nembedded-hal-async = { version = \"1.0\" }\nembedded-hal-nb = { version = \"1.0\" }\n\nrand-core-06 = { package = \"rand_core\", version = \"0.6\" }\nrand-core-09 = { package = \"rand_core\", version = \"0.9\" }\n\npio = { version = \"0.3\" }\nrp2040-boot2 = \"0.3\"\ndocument-features = \"0.2.10\"\nsha2-const-stable = \"0.1\"\nrp-binary-info = { version = \"0.1.0\", optional = true }\nsmart-leds = \"0.4.0\"\n\n[dev-dependencies]\nembassy-executor = { version = \"0.10.0\", path = \"../embassy-executor\", features = [\"platform-std\", \"executor-thread\"] }\nstatic_cell = { version = \"2\" }\n"
  },
  {
    "path": "embassy-rp/LICENSE-APACHE",
    "content": "                              Apache License\n                        Version 2.0, January 2004\n                     http://www.apache.org/licenses/\n\nTERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n1. Definitions.\n\n   \"License\" shall mean the terms and conditions for use, reproduction,\n   and distribution as defined by Sections 1 through 9 of this document.\n\n   \"Licensor\" shall mean the copyright owner or entity authorized by\n   the copyright owner that is granting the License.\n\n   \"Legal Entity\" shall mean the union of the acting entity and all\n   other entities that control, are controlled by, or are under common\n   control with that entity. For the purposes of this definition,\n   \"control\" means (i) the power, direct or indirect, to cause the\n   direction or management of such entity, whether by contract or\n   otherwise, or (ii) ownership of fifty percent (50%) or more of the\n   outstanding shares, or (iii) beneficial ownership of such entity.\n\n   \"You\" (or \"Your\") shall mean an individual or Legal Entity\n   exercising permissions granted by this License.\n\n   \"Source\" form shall mean the preferred form for making modifications,\n   including but not limited to software source code, documentation\n   source, and configuration files.\n\n   \"Object\" form shall mean any form resulting from mechanical\n   transformation or translation of a Source form, including but\n   not limited to compiled object code, generated documentation,\n   and conversions to other media types.\n\n   \"Work\" shall mean the work of authorship, whether in Source or\n   Object form, made available under the License, as indicated by a\n   copyright notice that is included in or attached to the work\n   (an example is provided in the Appendix below).\n\n   \"Derivative Works\" shall mean any work, whether in Source or Object\n   form, that is based on (or derived from) the Work and for which the\n   editorial revisions, annotations, elaborations, or other modifications\n   represent, as a whole, an original work of authorship. For the purposes\n   of this License, Derivative Works shall not include works that remain\n   separable from, or merely link (or bind by name) to the interfaces of,\n   the Work and Derivative Works thereof.\n\n   \"Contribution\" shall mean any work of authorship, including\n   the original version of the Work and any modifications or additions\n   to that Work or Derivative Works thereof, that is intentionally\n   submitted to Licensor for inclusion in the Work by the copyright owner\n   or by an individual or Legal Entity authorized to submit on behalf of\n   the copyright owner. For the purposes of this definition, \"submitted\"\n   means any form of electronic, verbal, or written communication sent\n   to the Licensor or its representatives, including but not limited to\n   communication on electronic mailing lists, source code control systems,\n   and issue tracking systems that are managed by, or on behalf of, the\n   Licensor for the purpose of discussing and improving the Work, but\n   excluding communication that is conspicuously marked or otherwise\n   designated in writing by the copyright owner as \"Not a Contribution.\"\n\n   \"Contributor\" shall mean Licensor and any individual or Legal Entity\n   on behalf of whom a Contribution has been received by Licensor and\n   subsequently incorporated within the Work.\n\n2. Grant of Copyright License. Subject to the terms and conditions of\n   this License, each Contributor hereby grants to You a perpetual,\n   worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n   copyright license to reproduce, prepare Derivative Works of,\n   publicly display, publicly perform, sublicense, and distribute the\n   Work and such Derivative Works in Source or Object form.\n\n3. Grant of Patent License. Subject to the terms and conditions of\n   this License, each Contributor hereby grants to You a perpetual,\n   worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n   (except as stated in this section) patent license to make, have made,\n   use, offer to sell, sell, import, and otherwise transfer the Work,\n   where such license applies only to those patent claims licensable\n   by such Contributor that are necessarily infringed by their\n   Contribution(s) alone or by combination of their Contribution(s)\n   with the Work to which such Contribution(s) was submitted. If You\n   institute patent litigation against any entity (including a\n   cross-claim or counterclaim in a lawsuit) alleging that the Work\n   or a Contribution incorporated within the Work constitutes direct\n   or contributory patent infringement, then any patent licenses\n   granted to You under this License for that Work shall terminate\n   as of the date such litigation is filed.\n\n4. Redistribution. You may reproduce and distribute copies of the\n   Work or Derivative Works thereof in any medium, with or without\n   modifications, and in Source or Object form, provided that You\n   meet the following conditions:\n\n   (a) You must give any other recipients of the Work or\n       Derivative Works a copy of this License; and\n\n   (b) You must cause any modified files to carry prominent notices\n       stating that You changed the files; and\n\n   (c) You must retain, in the Source form of any Derivative Works\n       that You distribute, all copyright, patent, trademark, and\n       attribution notices from the Source form of the Work,\n       excluding those notices that do not pertain to any part of\n       the Derivative Works; and\n\n   (d) If the Work includes a \"NOTICE\" text file as part of its\n       distribution, then any Derivative Works that You distribute must\n       include a readable copy of the attribution notices contained\n       within such NOTICE file, excluding those notices that do not\n       pertain to any part of the Derivative Works, in at least one\n       of the following places: within a NOTICE text file distributed\n       as part of the Derivative Works; within the Source form or\n       documentation, if provided along with the Derivative Works; or,\n       within a display generated by the Derivative Works, if and\n       wherever such third-party notices normally appear. The contents\n       of the NOTICE file are for informational purposes only and\n       do not modify the License. You may add Your own attribution\n       notices within Derivative Works that You distribute, alongside\n       or as an addendum to the NOTICE text from the Work, provided\n       that such additional attribution notices cannot be construed\n       as modifying the License.\n\n   You may add Your own copyright statement to Your modifications and\n   may provide additional or different license terms and conditions\n   for use, reproduction, or distribution of Your modifications, or\n   for any such Derivative Works as a whole, provided Your use,\n   reproduction, and distribution of the Work otherwise complies with\n   the conditions stated in this License.\n\n5. Submission of Contributions. Unless You explicitly state otherwise,\n   any Contribution intentionally submitted for inclusion in the Work\n   by You to the Licensor shall be under the terms and conditions of\n   this License, without any additional terms or conditions.\n   Notwithstanding the above, nothing herein shall supersede or modify\n   the terms of any separate license agreement you may have executed\n   with Licensor regarding such Contributions.\n\n6. Trademarks. This License does not grant permission to use the trade\n   names, trademarks, service marks, or product names of the Licensor,\n   except as required for reasonable and customary use in describing the\n   origin of the Work and reproducing the content of the NOTICE file.\n\n7. Disclaimer of Warranty. Unless required by applicable law or\n   agreed to in writing, Licensor provides the Work (and each\n   Contributor provides its Contributions) on an \"AS IS\" BASIS,\n   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n   implied, including, without limitation, any warranties or conditions\n   of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n   PARTICULAR PURPOSE. You are solely responsible for determining the\n   appropriateness of using or redistributing the Work and assume any\n   risks associated with Your exercise of permissions under this License.\n\n8. Limitation of Liability. In no event and under no legal theory,\n   whether in tort (including negligence), contract, or otherwise,\n   unless required by applicable law (such as deliberate and grossly\n   negligent acts) or agreed to in writing, shall any Contributor be\n   liable to You for damages, including any direct, indirect, special,\n   incidental, or consequential damages of any character arising as a\n   result of this License or out of the use or inability to use the\n   Work (including but not limited to damages for loss of goodwill,\n   work stoppage, computer failure or malfunction, or any and all\n   other commercial damages or losses), even if such Contributor\n   has been advised of the possibility of such damages.\n\n9. Accepting Warranty or Additional Liability. While redistributing\n   the Work or Derivative Works thereof, You may choose to offer,\n   and charge a fee for, acceptance of support, warranty, indemnity,\n   or other liability obligations and/or rights consistent with this\n   License. However, in accepting such obligations, You may act only\n   on Your own behalf and on Your sole responsibility, not on behalf\n   of any other Contributor, and only if You agree to indemnify,\n   defend, and hold each Contributor harmless for any liability\n   incurred by, or claims asserted against, such Contributor by reason\n   of your accepting any such warranty or additional liability.\n\nEND OF TERMS AND CONDITIONS\n\nAPPENDIX: How to apply the Apache License to your work.\n\n   To apply the Apache License to your work, attach the following\n   boilerplate notice, with the fields enclosed by brackets \"[]\"\n   replaced with your own identifying information. (Don't include\n   the brackets!)  The text should be enclosed in the appropriate\n   comment syntax for the file format. We also recommend that a\n   file or class name and description of purpose be included on the\n   same \"printed page\" as the copyright notice for easier\n   identification within third-party archives.\n\nCopyright (c) Embassy project contributors\nPortions copyright (c) rp-rs organization\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n\thttp://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n"
  },
  {
    "path": "embassy-rp/LICENSE-MIT",
    "content": "Copyright (c) Embassy project contributors\nPortions copyright (c) rp-rs organization\n\nPermission is hereby granted, free of charge, to any\nperson obtaining a copy of this software and associated\ndocumentation files (the \"Software\"), to deal in the\nSoftware without restriction, including without\nlimitation the rights to use, copy, modify, merge,\npublish, distribute, sublicense, and/or sell copies of\nthe Software, and to permit persons to whom the Software\nis furnished to do so, subject to the following\nconditions:\n\nThe above copyright notice and this permission notice\nshall be included in all copies or substantial portions\nof the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF\nANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED\nTO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A\nPARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT\nSHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\nCLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\nOF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR\nIN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\nDEALINGS IN THE SOFTWARE.\n"
  },
  {
    "path": "embassy-rp/README.md",
    "content": "# Embassy RP HAL\n\nHALs implement safe, idiomatic Rust APIs to use the hardware capabilities, so raw register manipulation is not needed.\n\nThe embassy-rp HAL targets the Raspberry Pi RP2040 as well as RP235x microcontroller. The HAL implements both blocking and async APIs\nfor many peripherals. The benefit of using the async APIs is that the HAL takes care of waiting for peripherals to\ncomplete operations in low power mode and handling interrupts, so that applications can focus on more important matters.\n\n* [embassy-rp on crates.io](https://crates.io/crates/embassy-rp)\n* [Documentation](https://docs.embassy.dev/embassy-rp/)\n* [Source](https://github.com/embassy-rs/embassy/tree/main/embassy-rp)\n* [Examples](https://github.com/embassy-rs/embassy/tree/main/examples/rp/src/bin)\n\n## `embassy-time` time driver\n\nIf the `time-driver` feature is enabled, the HAL uses the TIMER peripheral as a global time driver for [embassy-time](https://crates.io/crates/embassy-time), with a tick rate of 1MHz.\n\n## Embedded-hal\n\nThe `embassy-rp` HAL implements the traits from [embedded-hal](https://crates.io/crates/embedded-hal) (v0.2 and 1.0) and [embedded-hal-async](https://crates.io/crates/embedded-hal-async), as well as [embedded-io](https://crates.io/crates/embedded-io) and [embedded-io-async](https://crates.io/crates/embedded-io-async).\n\n## Interoperability\n\nThis crate can run on any executor.\n\nOptionally, some features requiring [`embassy-time`](https://crates.io/crates/embassy-time) can be activated with the `time-driver` feature. If you enable it,\nyou must link an `embassy-time` driver in your project.\n"
  },
  {
    "path": "embassy-rp/build.rs",
    "content": "use std::env;\nuse std::ffi::OsStr;\nuse std::fs::File;\nuse std::io::Write;\nuse std::path::{Path, PathBuf};\n\nfn main() {\n    if env::var(\"CARGO_FEATURE_RP2040\").is_ok() {\n        // Put the linker script somewhere the linker can find it\n        let out = &PathBuf::from(env::var_os(\"OUT_DIR\").unwrap());\n        let link_x = include_bytes!(\"link-rp.x.in\");\n        let mut f = File::create(out.join(\"link-rp.x\")).unwrap();\n        f.write_all(link_x).unwrap();\n\n        println!(\"cargo:rustc-link-search={}\", out.display());\n\n        println!(\"cargo:rerun-if-changed=build.rs\");\n        println!(\"cargo:rerun-if-changed=link-rp.x.in\");\n    }\n\n    // code below taken from https://github.com/rust-embedded/cortex-m/blob/master/cortex-m-rt/build.rs\n\n    let mut target = env::var(\"TARGET\").unwrap();\n\n    // When using a custom target JSON, `$TARGET` contains the path to that JSON file. By\n    // convention, these files are named after the actual target triple, eg.\n    // `thumbv7m-customos-elf.json`, so we extract the file stem here to allow custom target specs.\n    let path = Path::new(&target);\n    if path.extension() == Some(OsStr::new(\"json\")) {\n        target = path\n            .file_stem()\n            .map_or(target.clone(), |stem| stem.to_str().unwrap().to_string());\n    }\n\n    println!(\"cargo::rustc-check-cfg=cfg(has_fpu)\");\n    if target.ends_with(\"-eabihf\") {\n        println!(\"cargo:rustc-cfg=has_fpu\");\n    }\n}\n"
  },
  {
    "path": "embassy-rp/funcsel.txt",
    "content": "0   SPI0 RX     UART0 TX     I2C0 SDA      PWM0 A      SIO       PIO0      PIO1                    USB OVCUR DET\n1   SPI0 CSn    UART0 RX     I2C0 SCL      PWM0 B      SIO       PIO0      PIO1                    USB VBUS DET\n2   SPI0 SCK    UART0 CTS    I2C1 SDA      PWM1 A      SIO       PIO0      PIO1                    USB VBUS EN\n3   SPI0 TX     UART0 RTS    I2C1 SCL      PWM1 B      SIO       PIO0      PIO1                    USB OVCUR DET\n4   SPI0 RX     UART1 TX     I2C0 SDA      PWM2 A      SIO       PIO0      PIO1                    USB VBUS DET\n5   SPI0 CSn    UART1 RX     I2C0 SCL      PWM2 B      SIO       PIO0      PIO1                    USB VBUS EN\n6   SPI0 SCK    UART1 CTS    I2C1 SDA      PWM3 A      SIO       PIO0      PIO1                    USB OVCUR DET\n7   SPI0 TX     UART1 RTS    I2C1 SCL      PWM3 B      SIO       PIO0      PIO1                    USB VBUS DET\n8   SPI1 RX     UART1 TX     I2C0 SDA      PWM4 A      SIO       PIO0      PIO1                    USB VBUS EN\n9   SPI1 CSn    UART1 RX     I2C0 SCL      PWM4 B      SIO       PIO0      PIO1                    USB OVCUR DET\n10  SPI1 SCK    UART1 CTS    I2C1 SDA      PWM5 A      SIO       PIO0      PIO1                    USB VBUS DET\n11  SPI1 TX     UART1 RTS    I2C1 SCL      PWM5 B      SIO       PIO0      PIO1                    USB VBUS EN\n12  SPI1 RX     UART0 TX     I2C0 SDA      PWM6 A      SIO       PIO0      PIO1                    USB OVCUR DET\n13  SPI1 CSn    UART0 RX     I2C0 SCL      PWM6 B      SIO       PIO0      PIO1                    USB VBUS DET\n14  SPI1 SCK    UART0 CTS    I2C1 SDA      PWM7 A      SIO       PIO0      PIO1                    USB VBUS EN\n15  SPI1 TX     UART0 RTS    I2C1 SCL      PWM7 B      SIO       PIO0      PIO1                    USB OVCUR DET\n16  SPI0 RX     UART0 TX     I2C0 SDA      PWM0 A      SIO       PIO0      PIO1                    USB VBUS DET\n17  SPI0 CSn    UART0 RX     I2C0 SCL      PWM0 B      SIO       PIO0      PIO1                    USB VBUS EN\n18  SPI0 SCK    UART0 CTS    I2C1 SDA      PWM1 A      SIO       PIO0      PIO1                    USB OVCUR DET\n19  SPI0 TX     UART0 RTS    I2C1 SCL      PWM1 B      SIO       PIO0      PIO1                    USB VBUS DET\n20  SPI0 RX     UART1 TX     I2C0 SDA      PWM2 A      SIO       PIO0      PIO1    CLOCK GPIN0     USB VBUS EN\n21  SPI0 CSn    UART1 RX     I2C0 SCL      PWM2 B      SIO       PIO0      PIO1    CLOCK GPOUT0    USB OVCUR DET\n22  SPI0 SCK    UART1 CTS    I2C1 SDA      PWM3 A      SIO       PIO0      PIO1    CLOCK GPIN1     USB VBUS DET\n23  SPI0 TX     UART1 RTS    I2C1 SCL      PWM3 B      SIO       PIO0      PIO1    CLOCK GPOUT1    USB VBUS EN\n24  SPI1 RX     UART1 TX     I2C0 SDA      PWM4 A      SIO       PIO0      PIO1    CLOCK GPOUT2    USB OVCUR DET\n25  SPI1 CSn    UART1 RX     I2C0 SCL      PWM4 B      SIO       PIO0      PIO1    CLOCK GPOUT3    USB VBUS DET\n26  SPI1 SCK    UART1 CTS    I2C1 SDA      PWM5 A      SIO       PIO0      PIO1                    USB VBUS EN\n27  SPI1 TX     UART1 RTS    I2C1 SCL      PWM5 B      SIO       PIO0      PIO1                    USB OVCUR DET\n28  SPI1 RX     UART0 TX     I2C0 SDA      PWM6 A      SIO       PIO0      PIO1                    USB VBUS DET\n29  SPI1 CSn    UART0 RX     I2C0 SCL      PWM6 B      SIO       PIO0      PIO1                    USB VBUS EN "
  },
  {
    "path": "embassy-rp/link-rp.x.in",
    "content": "\nSECTIONS {\n    /* ### Boot loader */\n    .boot2 ORIGIN(BOOT2) :\n    {\n        KEEP(*(.boot2));\n    } > BOOT2\n}\n"
  },
  {
    "path": "embassy-rp/src/adc.rs",
    "content": "//! ADC driver.\nuse core::future::{Future, poll_fn};\nuse core::marker::PhantomData;\nuse core::mem;\nuse core::sync::atomic::{Ordering, compiler_fence};\nuse core::task::Poll;\n\nuse embassy_sync::waitqueue::AtomicWaker;\n\nuse crate::gpio::{self, AnyPin, Pull, SealedPin as GpioPin};\nuse crate::interrupt::InterruptExt;\nuse crate::interrupt::typelevel::Binding;\nuse crate::pac::dma::vals::TreqSel;\nuse crate::peripherals::{ADC, ADC_TEMP_SENSOR};\nuse crate::{Peri, RegExt, dma, interrupt, pac, peripherals};\n\nstatic WAKER: AtomicWaker = AtomicWaker::new();\n\n/// ADC config.\n#[non_exhaustive]\n#[derive(Default)]\npub struct Config {}\n\n#[derive(Debug)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\nenum Source<'p> {\n    Pin(Peri<'p, AnyPin>),\n    TempSensor(Peri<'p, ADC_TEMP_SENSOR>),\n}\n\n/// ADC channel.\npub struct Channel<'p>(Source<'p>);\n\nimpl<'p> Channel<'p> {\n    /// Create a new ADC channel from pin with the provided [Pull] configuration.\n    pub fn new_pin(pin: Peri<'p, impl AdcPin + 'p>, pull: Pull) -> Self {\n        pin.pad_ctrl().modify(|w| {\n            #[cfg(feature = \"_rp235x\")]\n            w.set_iso(false);\n            // manual says:\n            //\n            // > When using an ADC input shared with a GPIO pin, the pin’s\n            // > digital functions must be disabled by setting IE low and OD\n            // > high in the pin’s pad control register\n            w.set_ie(false);\n            w.set_od(true);\n            w.set_pue(pull == Pull::Up);\n            w.set_pde(pull == Pull::Down);\n        });\n        Self(Source::Pin(pin.into()))\n    }\n\n    /// Create a new ADC channel for the internal temperature sensor.\n    pub fn new_temp_sensor(s: Peri<'p, ADC_TEMP_SENSOR>) -> Self {\n        let r = pac::ADC;\n        r.cs().write_set(|w| w.set_ts_en(true));\n        Self(Source::TempSensor(s))\n    }\n\n    fn channel(&self) -> u8 {\n        #[cfg(any(feature = \"rp2040\", feature = \"rp235xa\"))]\n        const CH_OFFSET: u8 = 26;\n        #[cfg(feature = \"rp235xb\")]\n        const CH_OFFSET: u8 = 40;\n\n        #[cfg(any(feature = \"rp2040\", feature = \"rp235xa\"))]\n        const TS_CHAN: u8 = 4;\n        #[cfg(feature = \"rp235xb\")]\n        const TS_CHAN: u8 = 8;\n\n        match &self.0 {\n            // this requires adc pins to be sequential and matching the adc channels,\n            // which is the case for rp2040/rp235xy\n            Source::Pin(p) => p._pin() - CH_OFFSET,\n            Source::TempSensor(_) => TS_CHAN,\n        }\n    }\n}\n\nimpl<'p> Drop for Source<'p> {\n    fn drop(&mut self) {\n        match self {\n            Source::Pin(p) => {\n                p.pad_ctrl().modify(|w| {\n                    w.set_ie(true);\n                    w.set_od(false);\n                    w.set_pue(false);\n                    w.set_pde(true);\n                });\n            }\n            Source::TempSensor(_) => {\n                pac::ADC.cs().write_clear(|w| w.set_ts_en(true));\n            }\n        }\n    }\n}\n\n/// ADC sample.\n#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug, Default)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\n#[repr(transparent)]\npub struct Sample(u16);\n\nimpl Sample {\n    /// Sample is valid.\n    pub fn good(&self) -> bool {\n        self.0 < 0x8000\n    }\n\n    /// Sample value.\n    pub fn value(&self) -> u16 {\n        self.0 & !0x8000\n    }\n}\n\n/// ADC error.\n#[derive(Debug, Eq, PartialEq, Copy, Clone)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub enum Error {\n    /// Error converting value.\n    ConversionFailed,\n}\n\n/// ADC mode.\npub trait Mode {}\n\n/// ADC async mode.\npub struct Async;\nimpl Mode for Async {}\n\n/// ADC blocking mode.\npub struct Blocking;\nimpl Mode for Blocking {}\n\n/// ADC driver.\npub struct Adc<'d, M: Mode> {\n    phantom: PhantomData<(&'d ADC, M)>,\n}\n\nimpl<'d, M: Mode> Drop for Adc<'d, M> {\n    fn drop(&mut self) {\n        let r = Self::regs();\n        // disable ADC. leaving it enabled comes with a ~150µA static\n        // current draw. the temperature sensor has already been disabled\n        // by the temperature-reading methods, so we don't need to touch that.\n        r.cs().write(|w| w.set_en(false));\n    }\n}\n\nimpl<'d, M: Mode> Adc<'d, M> {\n    #[inline]\n    fn regs() -> pac::adc::Adc {\n        pac::ADC\n    }\n\n    #[inline]\n    fn reset() -> pac::resets::regs::Peripherals {\n        let mut ret = pac::resets::regs::Peripherals::default();\n        ret.set_adc(true);\n        ret\n    }\n\n    fn setup() {\n        let reset = Self::reset();\n        crate::reset::reset(reset);\n        crate::reset::unreset_wait(reset);\n        let r = Self::regs();\n        // Enable ADC\n        r.cs().write(|w| w.set_en(true));\n        // Wait for ADC ready\n        while !r.cs().read().ready() {}\n    }\n\n    /// Sample a value from a channel in blocking mode.\n    pub fn blocking_read(&mut self, ch: &mut Channel) -> Result<u16, Error> {\n        let r = Self::regs();\n        r.cs().modify(|w| {\n            w.set_ainsel(ch.channel());\n            w.set_start_once(true);\n            w.set_err(true);\n        });\n        while !r.cs().read().ready() {}\n        match r.cs().read().err() {\n            true => Err(Error::ConversionFailed),\n            false => Ok(r.result().read().result()),\n        }\n    }\n}\n\nimpl<'d> Adc<'d, Async> {\n    /// Create ADC driver in async mode.\n    pub fn new(\n        _inner: Peri<'d, ADC>,\n        _irq: impl Binding<interrupt::typelevel::ADC_IRQ_FIFO, InterruptHandler>,\n        _config: Config,\n    ) -> Self {\n        Self::setup();\n\n        // Setup IRQ\n        interrupt::ADC_IRQ_FIFO.unpend();\n        unsafe { interrupt::ADC_IRQ_FIFO.enable() };\n\n        Self { phantom: PhantomData }\n    }\n\n    fn wait_for_ready() -> impl Future<Output = ()> {\n        let r = Self::regs();\n\n        poll_fn(move |cx| {\n            WAKER.register(cx.waker());\n\n            r.inte().write(|w| w.set_fifo(true));\n            compiler_fence(Ordering::SeqCst);\n\n            if r.cs().read().ready() {\n                return Poll::Ready(());\n            }\n            Poll::Pending\n        })\n    }\n\n    /// Sample a value from a channel until completed.\n    pub async fn read(&mut self, ch: &mut Channel<'_>) -> Result<u16, Error> {\n        let r = Self::regs();\n        r.cs().modify(|w| {\n            w.set_ainsel(ch.channel());\n            w.set_start_once(true);\n            w.set_err(true);\n        });\n        Self::wait_for_ready().await;\n        match r.cs().read().err() {\n            true => Err(Error::ConversionFailed),\n            false => Ok(r.result().read().result()),\n        }\n    }\n\n    // Note for refactoring: we don't require the actual Channels here, just the channel numbers.\n    // The public api is responsible for asserting ownership of the actual Channels.\n    async fn read_many_inner<W: dma::Word>(\n        &mut self,\n        channels: impl Iterator<Item = u8>,\n        buf: &mut [W],\n        fcs_err: bool,\n        div: u16,\n        dma_ch: &mut dma::Channel<'_>,\n    ) -> Result<(), Error> {\n        #[cfg(feature = \"rp2040\")]\n        let mut rrobin = 0_u8;\n        #[cfg(feature = \"_rp235x\")]\n        let mut rrobin = 0_u16;\n        for c in channels {\n            rrobin |= 1 << c;\n        }\n        let first_ch = rrobin.trailing_zeros() as u8;\n        if rrobin.count_ones() == 1 {\n            rrobin = 0;\n        }\n\n        let r = Self::regs();\n        // clear previous errors and set channel\n        r.cs().modify(|w| {\n            w.set_ainsel(first_ch);\n            w.set_rrobin(rrobin);\n            w.set_err_sticky(true); // clear previous errors\n            w.set_start_many(false);\n        });\n        // wait for previous conversions and drain fifo. an earlier batch read may have\n        // been cancelled, leaving the adc running.\n        while !r.cs().read().ready() {}\n        while !r.fcs().read().empty() {\n            r.fifo().read();\n        }\n\n        // set up fifo for dma\n        r.fcs().write(|w| {\n            w.set_thresh(1);\n            w.set_dreq_en(true);\n            w.set_shift(mem::size_of::<W>() == 1);\n            w.set_en(true);\n            w.set_err(fcs_err);\n        });\n\n        // reset dma config on drop, regardless of whether it was a future being cancelled\n        // or the method returning normally.\n        struct ResetDmaConfig;\n        impl Drop for ResetDmaConfig {\n            fn drop(&mut self) {\n                pac::ADC.cs().write_clear(|w| w.set_start_many(true));\n                while !pac::ADC.cs().read().ready() {}\n                pac::ADC.fcs().write_clear(|w| {\n                    w.set_dreq_en(true);\n                    w.set_shift(true);\n                    w.set_en(true);\n                });\n            }\n        }\n        let auto_reset = ResetDmaConfig;\n\n        let dma = unsafe { dma_ch.read(r.fifo().as_ptr() as *const W, buf as *mut [W], TreqSel::ADC, false) };\n        // start conversions and wait for dma to finish. we can't report errors early\n        // because there's no interrupt to signal them, and inspecting every element\n        // of the fifo is too costly to do here.\n        r.div().modify(|w| w.set_int(div));\n        r.cs().write_set(|w| w.set_start_many(true));\n        dma.await;\n        mem::drop(auto_reset);\n        // we can't report errors before the conversions have ended since no interrupt\n        // exists to report them early, and since they're exceedingly rare we probably don't\n        // want to anyway.\n        match r.cs().read().err_sticky() {\n            false => Ok(()),\n            true => Err(Error::ConversionFailed),\n        }\n    }\n\n    /// Sample multiple values from multiple channels using DMA.\n    /// Samples are stored in an interleaved fashion inside the buffer.\n    /// `div` is the integer part of the clock divider and can be calculated with `floor(48MHz / sample_rate * num_channels - 1)`\n    /// Any `div` value of less than 96 will have the same effect as setting it to 0\n    #[inline]\n    pub async fn read_many_multichannel<S: AdcSample>(\n        &mut self,\n        ch: &mut [Channel<'_>],\n        buf: &mut [S],\n        div: u16,\n        dma: &mut dma::Channel<'_>,\n    ) -> Result<(), Error> {\n        self.read_many_inner(ch.iter().map(|c| c.channel()), buf, false, div, dma)\n            .await\n    }\n\n    /// Sample multiple values from multiple channels using DMA, with errors inlined in samples.\n    /// Samples are stored in an interleaved fashion inside the buffer.\n    /// `div` is the integer part of the clock divider and can be calculated with `floor(48MHz / sample_rate * num_channels - 1)`\n    /// Any `div` value of less than 96 will have the same effect as setting it to 0\n    #[inline]\n    pub async fn read_many_multichannel_raw(\n        &mut self,\n        ch: &mut [Channel<'_>],\n        buf: &mut [Sample],\n        div: u16,\n        dma: &mut dma::Channel<'_>,\n    ) {\n        // errors are reported in individual samples\n        let _ = self\n            .read_many_inner(\n                ch.iter().map(|c| c.channel()),\n                unsafe { mem::transmute::<_, &mut [u16]>(buf) },\n                true,\n                div,\n                dma,\n            )\n            .await;\n    }\n\n    /// Sample multiple values from a channel using DMA.\n    /// `div` is the integer part of the clock divider and can be calculated with `floor(48MHz / sample_rate - 1)`\n    /// Any `div` value of less than 96 will have the same effect as setting it to 0\n    #[inline]\n    pub async fn read_many<S: AdcSample>(\n        &mut self,\n        ch: &mut Channel<'_>,\n        buf: &mut [S],\n        div: u16,\n        dma: &mut dma::Channel<'_>,\n    ) -> Result<(), Error> {\n        self.read_many_inner([ch.channel()].into_iter(), buf, false, div, dma)\n            .await\n    }\n\n    /// Sample multiple values from a channel using DMA, with errors inlined in samples.\n    /// `div` is the integer part of the clock divider and can be calculated with `floor(48MHz / sample_rate - 1)`\n    /// Any `div` value of less than 96 will have the same effect as setting it to 0\n    #[inline]\n    pub async fn read_many_raw(\n        &mut self,\n        ch: &mut Channel<'_>,\n        buf: &mut [Sample],\n        div: u16,\n        dma: &mut dma::Channel<'_>,\n    ) {\n        // errors are reported in individual samples\n        let _ = self\n            .read_many_inner(\n                [ch.channel()].into_iter(),\n                unsafe { mem::transmute::<_, &mut [u16]>(buf) },\n                true,\n                div,\n                dma,\n            )\n            .await;\n    }\n}\n\nimpl<'d> Adc<'d, Blocking> {\n    /// Create ADC driver in blocking mode.\n    pub fn new_blocking(_inner: Peri<'d, ADC>, _config: Config) -> Self {\n        Self::setup();\n\n        Self { phantom: PhantomData }\n    }\n}\n\n/// Interrupt handler.\npub struct InterruptHandler {\n    _empty: (),\n}\n\nimpl interrupt::typelevel::Handler<interrupt::typelevel::ADC_IRQ_FIFO> for InterruptHandler {\n    unsafe fn on_interrupt() {\n        let r = Adc::<Async>::regs();\n        r.inte().write(|w| w.set_fifo(false));\n        WAKER.wake();\n    }\n}\n\ntrait SealedAdcSample: crate::dma::Word {}\ntrait SealedAdcChannel {}\n\n/// ADC sample.\n#[allow(private_bounds)]\npub trait AdcSample: SealedAdcSample {}\n\nimpl SealedAdcSample for u16 {}\nimpl AdcSample for u16 {}\n\nimpl SealedAdcSample for u8 {}\nimpl AdcSample for u8 {}\n\n/// ADC channel.\n#[allow(private_bounds)]\npub trait AdcChannel: SealedAdcChannel {}\n/// ADC pin.\npub trait AdcPin: AdcChannel + gpio::Pin {}\n\nmacro_rules! impl_pin {\n    ($pin:ident, $channel:expr) => {\n        impl SealedAdcChannel for peripherals::$pin {}\n        impl AdcChannel for peripherals::$pin {}\n        impl AdcPin for peripherals::$pin {}\n    };\n}\n\n#[cfg(any(feature = \"rp235xa\", feature = \"rp2040\"))]\nimpl_pin!(PIN_26, 0);\n#[cfg(any(feature = \"rp235xa\", feature = \"rp2040\"))]\nimpl_pin!(PIN_27, 1);\n#[cfg(any(feature = \"rp235xa\", feature = \"rp2040\"))]\nimpl_pin!(PIN_28, 2);\n#[cfg(any(feature = \"rp235xa\", feature = \"rp2040\"))]\nimpl_pin!(PIN_29, 3);\n\n#[cfg(feature = \"rp235xb\")]\nimpl_pin!(PIN_40, 0);\n#[cfg(feature = \"rp235xb\")]\nimpl_pin!(PIN_41, 1);\n#[cfg(feature = \"rp235xb\")]\nimpl_pin!(PIN_42, 2);\n#[cfg(feature = \"rp235xb\")]\nimpl_pin!(PIN_43, 3);\n#[cfg(feature = \"rp235xb\")]\nimpl_pin!(PIN_44, 4);\n#[cfg(feature = \"rp235xb\")]\nimpl_pin!(PIN_45, 5);\n#[cfg(feature = \"rp235xb\")]\nimpl_pin!(PIN_46, 6);\n#[cfg(feature = \"rp235xb\")]\nimpl_pin!(PIN_47, 7);\n\nimpl SealedAdcChannel for peripherals::ADC_TEMP_SENSOR {}\nimpl AdcChannel for peripherals::ADC_TEMP_SENSOR {}\n"
  },
  {
    "path": "embassy-rp/src/aon_timer/mod.rs",
    "content": "//! AON (Always-On) Timer driver for RP2350\n//!\n//! The AON Timer is a 64-bit counter that typically runs at 1 kHz (1ms resolution) and can operate during\n//! low-power modes. It's part of the POWMAN peripheral and provides:\n//!\n//! - Millisecond resolution counter\n//! - Alarm support for wakeup from low-power modes (WFI/WFE and DORMANT)\n//! - Async alarm waiting with interrupt support (POWMAN_IRQ_TIMER)\n//! - Choice of XOSC or LPOSC clock sources\n//!\n//! # Alarm Wake Modes\n//!\n//! The AON Timer supports multiple wake modes via the [`AlarmWakeMode`] enum:\n//!\n//! - **`WfiOnly` (default)**: Alarm triggers `POWMAN_IRQ_TIMER` interrupt to wake from\n//!   WFI/WFE (light sleep). Use `wait_for_alarm().await` for async waiting. Works with\n//!   both XOSC and LPOSC clock sources.\n//!\n//! - **`DormantOnly`**: Hardware power-up wake from DORMANT (deep sleep). Sets the\n//!   `PWRUP_ON_ALARM` bit to trigger hardware power-up event (no interrupt, since CPU\n//!   clock is stopped). **Requirements:**\n//!   - Must use LPOSC clock source (XOSC is powered down in DORMANT)\n//!   - Requires Secure privilege level (TIMER register is Secure-only)\n//!\n//! - **`Both`**: Enables both interrupt wake (WFI/WFE) and hardware power-up wake (DORMANT).\n//!   Subject to the same requirements as `DormantOnly` for DORMANT support.\n//!\n//! - **`Disabled`**: Alarm flag is set but no wake mechanisms are enabled. Use `alarm_fired()`\n//!   to manually poll the alarm status.\n//!\n//! You can set the wake mode either in [`Config`] at initialization, or at runtime via\n//! [`AonTimer::set_wake_mode()`].\n//!\n//! # Security Considerations\n//!\n//! The TIMER register (including the `PWRUP_ON_ALARM` bit) is **Secure-only** per the\n//! RP2350 datasheet. Setting wake modes that involve DORMANT wake (`DormantOnly` or `Both`)\n//! may fail silently or have no effect when running in Non-secure contexts. Methods\n//! `enable_dormant_wake()`, `disable_dormant_wake()`, and `set_wake_mode()` that configure\n//! DORMANT wake are subject to this restriction.\n//!\n//! # Important Notes\n//!\n//! - All POWMAN registers require password `0x5AFE` in upper 16 bits for writes\n//! - Timer must be stopped before setting the counter value\n//! - Resolution is 1ms (1 kHz tick rate)\n//!\n//! # Example - WFI/WFE Wake (Default)\n//!\n//! ```rust,ignore\n//! use embassy_rp::aon_timer::{AonTimer, Config, ClockSource, AlarmWakeMode};\n//! use embassy_rp::bind_interrupts;\n//! use embassy_time::Duration;\n//!\n//! // Bind the interrupt handler\n//! bind_interrupts!(struct Irqs {\n//!     POWMAN_IRQ_TIMER => embassy_rp::aon_timer::InterruptHandler;\n//! });\n//!\n//! let config = Config {\n//!     clock_source: ClockSource::Xosc,\n//!     clock_freq_khz: 12000, // 12 MHz\n//!     alarm_wake_mode: AlarmWakeMode::WfiOnly, // Default\n//! };\n//!\n//! let mut timer = AonTimer::new(p.POWMAN, Irqs, config);\n//! timer.set_counter(0);\n//! timer.start();\n//!\n//! // Set an alarm and wait asynchronously (interrupt-based wake)\n//! timer.set_alarm_after(Duration::from_secs(5)).unwrap();\n//! timer.wait_for_alarm().await;  // CPU enters WFI low-power mode\n//! ```\n//!\n//! # Example - DORMANT Wake\n//!\n//! ```rust,ignore\n//! use embassy_rp::aon_timer::{AonTimer, Config, ClockSource, AlarmWakeMode};\n//! use embassy_rp::bind_interrupts;\n//! use embassy_time::Duration;\n//!\n//! bind_interrupts!(struct Irqs {\n//!     POWMAN_IRQ_TIMER => embassy_rp::aon_timer::InterruptHandler;\n//! });\n//!\n//! let config = Config {\n//!     clock_source: ClockSource::Lposc,  // Required for DORMANT\n//!     clock_freq_khz: 32,                // ~32 kHz LPOSC\n//!     alarm_wake_mode: AlarmWakeMode::DormantOnly,\n//! };\n//!\n//! let mut timer = AonTimer::new(p.POWMAN, Irqs, config);\n//! timer.set_counter(0);\n//! timer.start();\n//!\n//! // Set alarm for DORMANT wake (hardware power-up)\n//! timer.set_alarm_after(Duration::from_secs(10)).unwrap();\n//! // Enter DORMANT mode here - alarm will wake via power-up event\n//! ```\n//!\n//! # Example - Runtime Wake Mode Change\n//!\n//! ```rust,ignore\n//! use embassy_rp::aon_timer::{AonTimer, Config, ClockSource, AlarmWakeMode};\n//! use embassy_rp::bind_interrupts;\n//! use embassy_time::Duration;\n//!\n//! bind_interrupts!(struct Irqs {\n//!     POWMAN_IRQ_TIMER => embassy_rp::aon_timer::InterruptHandler;\n//! });\n//!\n//! let mut timer = AonTimer::new(p.POWMAN, Irqs, Config::default());\n//! timer.set_counter(0);\n//! timer.start();\n//!\n//! // Use WFI wake initially\n//! timer.set_alarm_after(Duration::from_secs(5)).unwrap();\n//! timer.wait_for_alarm().await;\n//!\n//! // Switch to both wake modes at runtime\n//! timer.set_wake_mode(AlarmWakeMode::Both);\n//! timer.set_alarm_after(Duration::from_secs(10)).unwrap();\n//! // Now supports both WFI and DORMANT wake\n//! ```\n//!\n//! # Example - Using DateTime with AON Timer\n//!\n//! ```rust,ignore\n//! use embassy_rp::aon_timer::{AonTimer, Config, DateTime, DayOfWeek};\n//! use embassy_rp::bind_interrupts;\n//!\n//! bind_interrupts!(struct Irqs {\n//!     POWMAN_IRQ_TIMER => embassy_rp::aon_timer::InterruptHandler;\n//! });\n//!\n//! let mut timer = AonTimer::new(p.POWMAN, Irqs, Config::default());\n//!\n//! // Set timer to a specific DateTime (e.g., 2024-06-15 12:30:00 UTC)\n//! let start_time = DateTime {\n//!     year: 2024,\n//!     month: 6,\n//!     day: 15,\n//!     day_of_week: DayOfWeek::Saturday,\n//!     hour: 12,\n//!     minute: 30,\n//!     second: 0,\n//! };\n//! timer.set_datetime(start_time).unwrap();\n//! timer.start();\n//!\n//! // Later, read current DateTime\n//! let current = timer.now_as_datetime().unwrap();\n//! info!(\"Current time: {}-{:02}-{:02} {:02}:{:02}:{:02}\",\n//!       current.year, current.month, current.day,\n//!       current.hour, current.minute, current.second);\n//!\n//! // Set alarm for specific DateTime (1 hour later)\n//! let alarm_time = DateTime {\n//!     year: 2024,\n//!     month: 6,\n//!     day: 15,\n//!     day_of_week: DayOfWeek::Saturday,\n//!     hour: 13,\n//!     minute: 30,\n//!     second: 0,\n//! };\n//! timer.set_alarm_at_datetime(alarm_time).unwrap();\n//! timer.wait_for_alarm().await;\n//! ```\n\nuse core::future::poll_fn;\nuse core::marker::PhantomData;\nuse core::sync::atomic::{AtomicBool, Ordering, compiler_fence};\nuse core::task::Poll;\n\nuse embassy_hal_internal::Peri;\nuse embassy_hal_internal::interrupt::InterruptExt;\nuse embassy_sync::waitqueue::AtomicWaker;\nuse embassy_time::Duration;\n\npub use crate::datetime::{DateTime, DayOfWeek, Error as DateTimeError};\nuse crate::{interrupt, pac};\n\nconst POWMAN_PASSWORD: u32 = 0x5AFE << 16;\n\nstatic WAKER: AtomicWaker = AtomicWaker::new();\nstatic ALARM_OCCURRED: AtomicBool = AtomicBool::new(false);\n\n/// Alarm wake mode configuration\n///\n/// Controls which low-power wake mechanisms are enabled for the alarm.\n#[derive(Clone, Copy, PartialEq, Eq, Debug)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub enum AlarmWakeMode {\n    /// Wake from WFI/WFE only (interrupt-based via POWMAN_IRQ_TIMER)\n    ///\n    /// This is the default and most common mode. The alarm triggers an interrupt\n    /// that wakes the CPU from light sleep (WFI/WFE). Works with both XOSC and LPOSC.\n    WfiOnly,\n\n    /// Wake from DORMANT mode only (hardware power-up via PWRUP_ON_ALARM)\n    ///\n    /// The alarm wakes the chip from deep sleep (DORMANT) by triggering a hardware\n    /// power-up event. No interrupt is used since the CPU clock is stopped in DORMANT.\n    ///\n    /// **Requirements:**\n    /// - Must use LPOSC clock source (XOSC is powered down in DORMANT)\n    /// - Requires Secure privilege level (TIMER register is Secure-only)\n    /// - May fail silently in Non-secure contexts\n    DormantOnly,\n\n    /// Wake from both WFI/WFE and DORMANT modes\n    ///\n    /// Enables both interrupt-based wake (WFI/WFE) and hardware power-up wake (DORMANT).\n    /// Subject to the same requirements as DormantOnly for DORMANT wake support.\n    Both,\n\n    /// Alarm fires but doesn't wake (manual polling only)\n    ///\n    /// The alarm flag is set in hardware but no wake mechanisms are enabled.\n    /// Use `alarm_fired()` to manually poll the alarm status.\n    Disabled,\n}\n\n/// AON Timer configuration\n#[derive(Clone, Copy)]\npub struct Config {\n    /// Clock source for the timer\n    pub clock_source: ClockSource,\n    /// Clock frequency in kHz\n    pub clock_freq_khz: u32,\n    /// Alarm wake mode\n    ///\n    /// Controls which low-power wake mechanisms are enabled for alarms.\n    /// See [`AlarmWakeMode`] for details on each mode.\n    pub alarm_wake_mode: AlarmWakeMode,\n}\n\nimpl Default for Config {\n    fn default() -> Self {\n        Self {\n            clock_source: ClockSource::Xosc,\n            clock_freq_khz: 12000, // 12 MHz XOSC\n            alarm_wake_mode: AlarmWakeMode::WfiOnly,\n        }\n    }\n}\n\n/// Clock source for the AON Timer\n#[derive(Clone, Copy, PartialEq, Eq, Debug)]\npub enum ClockSource {\n    /// Crystal oscillator (more accurate, requires external crystal)\n    Xosc,\n    /// Low-power oscillator (less accurate, ~32 kHz, available in all power modes)\n    Lposc,\n}\n\n/// AON Timer errors\n#[derive(Debug, Clone, Copy, PartialEq, Eq)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub enum Error {\n    /// The alarm time is in the past\n    AlarmInPast,\n    /// DateTime conversion error\n    DateTime(DateTimeError),\n}\n\n/// AON Timer driver\npub struct AonTimer<'d> {\n    _phantom: PhantomData<&'d ()>,\n    config: Config,\n}\n\nimpl<'d> AonTimer<'d> {\n    /// Create a new AON Timer instance\n    ///\n    /// This configures the clock source, frequency, and alarm wake mode but does not\n    /// start the timer. Call `start()` to begin counting.\n    ///\n    /// The wake mode in `config.alarm_wake_mode` determines how alarms wake the CPU:\n    /// - `WfiOnly` (default): Interrupt-based wake from WFI/WFE\n    /// - `DormantOnly`: Hardware power-up wake from DORMANT (requires LPOSC + Secure mode)\n    /// - `Both`: Both interrupt and hardware power-up wake\n    /// - `Disabled`: No automatic wake (manual polling only)\n    ///\n    /// For interrupt-based wake modes (`WfiOnly` or `Both`), you must bind the\n    /// `POWMAN_IRQ_TIMER` interrupt:\n    /// ```rust,ignore\n    /// bind_interrupts!(struct Irqs {\n    ///     POWMAN_IRQ_TIMER => aon_timer::InterruptHandler;\n    /// });\n    /// let timer = AonTimer::new(p.POWMAN, Irqs, config);\n    /// ```\n    pub fn new(\n        _inner: Peri<'d, crate::peripherals::POWMAN>,\n        _irq: impl interrupt::typelevel::Binding<interrupt::typelevel::POWMAN_IRQ_TIMER, InterruptHandler> + 'd,\n        config: Config,\n    ) -> Self {\n        let powman = pac::POWMAN;\n\n        match config.clock_source {\n            ClockSource::Xosc => {\n                powman.xosc_freq_khz_int().write(|w| {\n                    w.0 = (config.clock_freq_khz & 0xFFFF) | POWMAN_PASSWORD;\n                    *w\n                });\n                powman.xosc_freq_khz_frac().write(|w| {\n                    w.0 = POWMAN_PASSWORD;\n                    *w\n                });\n            }\n            ClockSource::Lposc => {\n                powman.lposc_freq_khz_int().write(|w| {\n                    w.0 = (config.clock_freq_khz & 0xFFFF) | POWMAN_PASSWORD;\n                    *w\n                });\n                powman.lposc_freq_khz_frac().write(|w| {\n                    w.0 = POWMAN_PASSWORD;\n                    *w\n                });\n            }\n        }\n\n        interrupt::POWMAN_IRQ_TIMER.unpend();\n        unsafe { interrupt::POWMAN_IRQ_TIMER.enable() };\n\n        Self {\n            _phantom: PhantomData,\n            config,\n        }\n    }\n\n    /// Start the timer\n    ///\n    /// The timer will begin counting from its current value.\n    pub fn start(&mut self) {\n        let powman = pac::POWMAN;\n        powman.timer().modify(|w| {\n            w.0 = (w.0 & 0x0000FFFF) | POWMAN_PASSWORD;\n            match self.config.clock_source {\n                ClockSource::Lposc => w.set_use_lposc(true),\n                ClockSource::Xosc => w.set_use_xosc(true),\n            }\n            w.set_run(true);\n            *w\n        });\n    }\n\n    /// Stop the timer\n    ///\n    /// The timer will stop counting but retain its current value.\n    pub fn stop(&mut self) {\n        let powman = pac::POWMAN;\n        powman.timer().modify(|w| {\n            w.0 = (w.0 & 0x0000FFFF) | POWMAN_PASSWORD;\n            w.set_run(false);\n            *w\n        });\n    }\n\n    /// Check if the timer is currently running\n    pub fn is_running(&self) -> bool {\n        let powman = pac::POWMAN;\n        powman.timer().read().run()\n    }\n\n    /// Read the current counter value in milliseconds\n    ///\n    /// This reads the 64-bit counter value with rollover protection.\n    /// The value represents milliseconds since the counter was last set.\n    pub fn now(&self) -> u64 {\n        let powman = pac::POWMAN;\n        // Read with rollover protection: read upper, lower, upper again\n        loop {\n            let upper1 = powman.read_time_upper().read();\n            let lower = powman.read_time_lower().read();\n            let upper2 = powman.read_time_upper().read();\n\n            // If upper didn't change, we got a consistent read\n            if upper1 == upper2 {\n                return ((upper1 as u64) << 32) | (lower as u64);\n            }\n            // Otherwise retry (rollover occurred)\n        }\n    }\n\n    /// Set the counter value in milliseconds\n    ///\n    /// This allows you to initialize the counter to any value (e.g., milliseconds since epoch,\n    /// or 0 to start counting from boot).\n    ///\n    /// Note: Timer must be stopped before calling this function.\n    pub fn set_counter(&mut self, value_ms: u64) {\n        if self.is_running() {\n            panic!(\"timer must be stopped before setting counter\");\n        }\n        let powman = pac::POWMAN;\n        powman.set_time_15to0().write(|w| {\n            w.0 = ((value_ms & 0xFFFF) as u32) | POWMAN_PASSWORD;\n            *w\n        });\n        powman.set_time_31to16().write(|w| {\n            w.0 = (((value_ms >> 16) & 0xFFFF) as u32) | POWMAN_PASSWORD;\n            *w\n        });\n        powman.set_time_47to32().write(|w| {\n            w.0 = (((value_ms >> 32) & 0xFFFF) as u32) | POWMAN_PASSWORD;\n            *w\n        });\n        powman.set_time_63to48().write(|w| {\n            w.0 = (((value_ms >> 48) & 0xFFFF) as u32) | POWMAN_PASSWORD;\n            *w\n        });\n    }\n\n    /// Set an alarm for a specific counter value (in milliseconds)\n    ///\n    /// The alarm will fire when the counter reaches this value.\n    /// Returns an error if the alarm time is in the past.\n    ///\n    /// The wake behavior depends on the configured `alarm_wake_mode`:\n    /// - `WfiOnly`: Alarm triggers interrupt wake from WFI/WFE\n    /// - `DormantOnly`: Alarm triggers power-up from DORMANT mode\n    /// - `Both`: Alarm triggers both interrupt and power-up wake\n    /// - `Disabled`: Alarm flag is set but no wake occurs\n    pub fn set_alarm(&mut self, alarm_ms: u64) -> Result<(), Error> {\n        let current_ms = self.now();\n        if alarm_ms <= current_ms {\n            return Err(Error::AlarmInPast);\n        }\n\n        self.disable_alarm();\n        self.set_alarm_value(alarm_ms);\n        self.clear_alarm();\n\n        match self.config.alarm_wake_mode {\n            AlarmWakeMode::WfiOnly => {\n                self.disable_dormant_wake();\n                self.enable_alarm_interrupt();\n            }\n            AlarmWakeMode::DormantOnly => {\n                self.enable_dormant_wake();\n                self.disable_alarm_interrupt();\n            }\n            AlarmWakeMode::Both => {\n                self.enable_dormant_wake();\n                self.enable_alarm_interrupt();\n            }\n            AlarmWakeMode::Disabled => {\n                self.disable_dormant_wake();\n                self.disable_alarm_interrupt();\n            }\n        }\n\n        self.enable_alarm();\n\n        Ok(())\n    }\n\n    /// Enable the alarm interrupt (INTE.TIMER)\n    ///\n    /// This allows the alarm to trigger POWMAN_IRQ_TIMER and wake from WFI/WFE.\n    fn enable_alarm_interrupt(&mut self) {\n        let powman = pac::POWMAN;\n        powman.inte().modify(|w| w.set_timer(true));\n    }\n\n    /// Disable the alarm interrupt (INTE.TIMER)\n    pub fn disable_alarm_interrupt(&mut self) {\n        let powman = pac::POWMAN;\n        powman.inte().modify(|w| w.set_timer(false));\n    }\n\n    /// Set the internal alarm value in milliseconds\n    #[inline(always)]\n    fn set_alarm_value(&mut self, value: u64) {\n        let powman = pac::POWMAN;\n        powman.alarm_time_15to0().write(|w| {\n            w.0 = ((value & 0xFFFF) as u32) | POWMAN_PASSWORD;\n            *w\n        });\n        powman.alarm_time_31to16().write(|w| {\n            w.0 = (((value >> 16) & 0xFFFF) as u32) | POWMAN_PASSWORD;\n            *w\n        });\n        powman.alarm_time_47to32().write(|w| {\n            w.0 = (((value >> 32) & 0xFFFF) as u32) | POWMAN_PASSWORD;\n            *w\n        });\n        powman.alarm_time_63to48().write(|w| {\n            w.0 = (((value >> 48) & 0xFFFF) as u32) | POWMAN_PASSWORD;\n            *w\n        });\n    }\n\n    /// Check if the alarm has fired\n    pub fn alarm_fired(&self) -> bool {\n        let powman = pac::POWMAN;\n        powman.timer().read().alarm()\n    }\n\n    /// Clear the alarm flag\n    pub fn clear_alarm(&mut self) {\n        let powman = pac::POWMAN;\n        powman.timer().modify(|w| {\n            w.0 = (w.0 & 0x0000FFFF) | POWMAN_PASSWORD;\n            w.set_alarm(true); // Write 1 to clear\n            *w\n        });\n    }\n\n    /// Disable the alarm\n    pub fn disable_alarm(&mut self) {\n        let powman = pac::POWMAN;\n        powman.timer().modify(|w| {\n            w.0 = (w.0 & 0x0000FFFF) | POWMAN_PASSWORD;\n            w.set_alarm_enab(false);\n            *w\n        });\n    }\n\n    /// Enable the alarm\n    pub fn enable_alarm(&mut self) {\n        let powman = pac::POWMAN;\n        powman.timer().modify(|w| {\n            w.0 = (w.0 & 0x0000FFFF) | POWMAN_PASSWORD;\n            w.set_alarm_enab(true);\n            *w\n        });\n    }\n\n    /// Enable DORMANT mode wake on alarm\n    ///\n    /// Sets the TIMER.PWRUP_ON_ALARM bit to allow the alarm to wake the chip from\n    /// DORMANT (deep sleep) mode. This is a hardware power-up event, distinct from\n    /// interrupt-based WFI/WFE wake.\n    ///\n    /// **Security Note**: The TIMER register is Secure-only per the RP2350 datasheet.\n    /// This method may fail silently or have no effect when called from Non-secure contexts.\n    ///\n    /// **Clock Source**: DORMANT wake requires LPOSC as the clock source, since XOSC\n    /// is powered down in DORMANT mode.\n    pub fn enable_dormant_wake(&mut self) {\n        let powman = pac::POWMAN;\n        powman.timer().modify(|w| {\n            w.0 = (w.0 & 0x0000FFFF) | POWMAN_PASSWORD;\n            w.set_pwrup_on_alarm(true);\n            *w\n        });\n    }\n\n    /// Disable DORMANT mode wake on alarm\n    ///\n    /// Clears the TIMER.PWRUP_ON_ALARM bit. The alarm will no longer wake the chip\n    /// from DORMANT mode, but can still wake from WFI/WFE via interrupts.\n    ///\n    /// **Security Note**: The TIMER register is Secure-only per the RP2350 datasheet.\n    /// This method may fail silently or have no effect when called from Non-secure contexts.\n    pub fn disable_dormant_wake(&mut self) {\n        let powman = pac::POWMAN;\n        powman.timer().modify(|w| {\n            w.0 = (w.0 & 0x0000FFFF) | POWMAN_PASSWORD;\n            w.set_pwrup_on_alarm(false);\n            *w\n        });\n    }\n\n    /// Set the alarm wake mode\n    ///\n    /// Configures which low-power wake mechanisms are enabled for the alarm.\n    /// This immediately updates the hardware configuration.\n    ///\n    /// # Arguments\n    /// * `mode` - The desired wake mode (see [`AlarmWakeMode`])\n    ///\n    /// # Security Note\n    /// Setting modes that involve DORMANT wake (DormantOnly or Both) requires\n    /// Secure privilege level. These modes may fail silently in Non-secure contexts.\n    pub fn set_wake_mode(&mut self, mode: AlarmWakeMode) {\n        match mode {\n            AlarmWakeMode::WfiOnly => {\n                self.enable_alarm_interrupt();\n                self.disable_dormant_wake();\n            }\n            AlarmWakeMode::DormantOnly => {\n                self.disable_alarm_interrupt();\n                self.enable_dormant_wake();\n            }\n            AlarmWakeMode::Both => {\n                self.enable_alarm_interrupt();\n                self.enable_dormant_wake();\n            }\n            AlarmWakeMode::Disabled => {\n                self.disable_alarm_interrupt();\n                self.disable_dormant_wake();\n            }\n        }\n        self.config.alarm_wake_mode = mode;\n    }\n\n    /// Set an alarm to fire after a duration from now\n    ///\n    /// This is a convenience method that sets the alarm to `now() + duration`.\n    pub fn set_alarm_after(&mut self, duration: Duration) -> Result<(), Error> {\n        let current_ms = self.now();\n        let alarm_ms = current_ms + duration.as_millis();\n        self.set_alarm(alarm_ms)\n    }\n\n    /// Get the current counter value as a Duration\n    ///\n    /// This is useful for measuring time spans. The duration represents\n    /// the time since the counter was last set to 0.\n    pub fn elapsed(&self) -> Duration {\n        Duration::from_millis(self.now())\n    }\n\n    /// Set the counter from a DateTime (Unix epoch)\n    ///\n    /// # Errors\n    /// Returns error if DateTime is before 1970-01-01.\n    ///\n    /// # Panics\n    /// Panics if timer is running.\n    pub fn set_datetime(&mut self, dt: DateTime) -> Result<(), DateTimeError> {\n        #[cfg(feature = \"chrono\")]\n        let millis = crate::datetime::timestamp_millis(&dt)?;\n        #[cfg(not(feature = \"chrono\"))]\n        let millis = dt.timestamp_millis()?;\n\n        self.set_counter(millis);\n        Ok(())\n    }\n\n    /// Get the current counter value as a DateTime (Unix epoch)\n    ///\n    /// # Errors\n    /// Returns error if counter value cannot be represented as valid DateTime.\n    pub fn now_as_datetime(&self) -> Result<DateTime, DateTimeError> {\n        let millis = self.now();\n\n        #[cfg(feature = \"chrono\")]\n        return crate::datetime::from_timestamp_millis(millis);\n        #[cfg(not(feature = \"chrono\"))]\n        return DateTime::from_timestamp_millis(millis);\n    }\n\n    /// Set an alarm for a specific DateTime\n    ///\n    /// # Errors\n    /// Returns error if DateTime conversion fails or alarm time is in the past.\n    pub fn set_alarm_at_datetime(&mut self, dt: DateTime) -> Result<(), Error> {\n        #[cfg(feature = \"chrono\")]\n        let alarm_ms = crate::datetime::timestamp_millis(&dt).map_err(Error::DateTime)?;\n        #[cfg(not(feature = \"chrono\"))]\n        let alarm_ms = dt.timestamp_millis().map_err(Error::DateTime)?;\n\n        self.set_alarm(alarm_ms)\n    }\n\n    /// Wait asynchronously for the alarm to fire\n    ///\n    /// This function will wait until the AON Timer alarm is triggered.\n    /// If the alarm is already triggered, it will return immediately.\n    ///\n    /// **Wake Mode Behavior:**\n    /// - `WfiOnly` or `Both`: CPU enters WFI (Wait For Interrupt) low-power mode while\n    ///   waiting. The alarm interrupt will wake the CPU and this function will return.\n    /// - `DormantOnly` or `Disabled`: This function will NOT automatically wake from\n    ///   DORMANT mode. For DORMANT wake, the hardware power-up event will restart the\n    ///   chip, not resume this async function.\n    ///\n    /// This method is primarily intended for `WfiOnly` and `Both` wake modes where\n    /// interrupt-based wake is available.\n    ///\n    /// # Example\n    /// ```rust,ignore\n    /// // Set alarm for 5 seconds from now\n    /// timer.set_alarm_after(Duration::from_secs(5)).unwrap();\n    ///\n    /// // Wait for the alarm (CPU enters WFI low-power mode)\n    /// timer.wait_for_alarm().await;\n    /// ```\n    pub async fn wait_for_alarm(&mut self) {\n        poll_fn(|cx| {\n            WAKER.register(cx.waker());\n\n            if ALARM_OCCURRED.swap(false, Ordering::SeqCst) {\n                self.clear_alarm();\n\n                compiler_fence(Ordering::SeqCst);\n                Poll::Ready(())\n            } else {\n                Poll::Pending\n            }\n        })\n        .await;\n    }\n}\n\n/// Interrupt handler for AON Timer alarms\npub struct InterruptHandler {\n    _empty: (),\n}\n\nimpl interrupt::typelevel::Handler<interrupt::typelevel::POWMAN_IRQ_TIMER> for InterruptHandler {\n    #[inline(always)]\n    unsafe fn on_interrupt() {\n        let powman = crate::pac::POWMAN;\n\n        powman.inte().modify(|w| w.set_timer(false));\n\n        powman.timer().modify(|w| {\n            w.0 = (w.0 & 0x0000FFFF) | POWMAN_PASSWORD;\n            w.set_alarm_enab(false);\n            *w\n        });\n\n        powman.intr().modify(|w| w.set_timer(true));\n\n        ALARM_OCCURRED.store(true, Ordering::SeqCst);\n        WAKER.wake();\n    }\n}\n"
  },
  {
    "path": "embassy-rp/src/block.rs",
    "content": "//! Support for the RP235x Boot ROM's \"Block\" structures\n//!\n//! Blocks contain pointers, to form Block Loops.\n//!\n//! The `IMAGE_DEF` Block (here the `ImageDef` type) tells the ROM how to boot a\n//! firmware image. The `PARTITION_TABLE` Block (here the `PartitionTable` type)\n//! tells the ROM how to divide the flash space up into partitions.\n\n// Credit: Taken from https://github.com/thejpster/rp-hal-rp2350-public (also licensed Apache 2.0 + MIT).\n// Copyright (c) rp-rs organization\n\n// These all have a 1 byte size\n\n/// An item ID for encoding a Vector Table address\npub const ITEM_1BS_VECTOR_TABLE: u8 = 0x03;\n\n/// An item ID for encoding a Rolling Window Delta\npub const ITEM_1BS_ROLLING_WINDOW_DELTA: u8 = 0x05;\n\n/// An item ID for encoding a Signature\npub const ITEM_1BS_SIGNATURE: u8 = 0x09;\n\n/// An item ID for encoding a Salt\npub const ITEM_1BS_SALT: u8 = 0x0c;\n\n/// An item ID for encoding an Image Type\npub const ITEM_1BS_IMAGE_TYPE: u8 = 0x42;\n\n/// An item ID for encoding the image's Entry Point\npub const ITEM_1BS_ENTRY_POINT: u8 = 0x44;\n\n/// An item ID for encoding the definition of a Hash\npub const ITEM_2BS_HASH_DEF: u8 = 0x47;\n\n/// An item ID for encoding a Version\npub const ITEM_1BS_VERSION: u8 = 0x48;\n\n/// An item ID for encoding a Hash\npub const ITEM_1BS_HASH_VALUE: u8 = 0x4b;\n\n// These all have a 2-byte size\n\n/// An item ID for encoding a Load Map\npub const ITEM_2BS_LOAD_MAP: u8 = 0x06;\n\n/// An item ID for encoding a Partition Table\npub const ITEM_2BS_PARTITION_TABLE: u8 = 0x0a;\n\n/// An item ID for encoding a placeholder entry that is ignored\n///\n/// Allows a Block to not be empty.\npub const ITEM_2BS_IGNORED: u8 = 0xfe;\n\n/// An item ID for encoding the special last item in a Block\n///\n/// It records how long the Block is.\npub const ITEM_2BS_LAST: u8 = 0xff;\n\n// Options for ITEM_1BS_IMAGE_TYPE\n\n/// A [`ITEM_1BS_IMAGE_TYPE`] value bitmask to mark an image as invalid\npub const IMAGE_TYPE_INVALID: u16 = 0x0000;\n\n/// A [`ITEM_1BS_IMAGE_TYPE`] value bitmask to mark an image as an executable\npub const IMAGE_TYPE_EXE: u16 = 0x0001;\n\n/// A [`ITEM_1BS_IMAGE_TYPE`] value bitmask to mark an image as data\npub const IMAGE_TYPE_DATA: u16 = 0x0002;\n\n/// A [`ITEM_1BS_IMAGE_TYPE`] value bitmask to mark the CPU security mode as unspecified\npub const IMAGE_TYPE_EXE_TYPE_SECURITY_UNSPECIFIED: u16 = 0x0000;\n\n/// A [`ITEM_1BS_IMAGE_TYPE`] value bitmask to mark the CPU security mode as Non Secure\npub const IMAGE_TYPE_EXE_TYPE_SECURITY_NS: u16 = 0x0010;\n\n/// A [`ITEM_1BS_IMAGE_TYPE`] value bitmask to mark the CPU security mode as Non Secure\npub const IMAGE_TYPE_EXE_TYPE_SECURITY_S: u16 = 0x0020;\n\n/// A [`ITEM_1BS_IMAGE_TYPE`] value bitmask to mark the CPU type as Arm\npub const IMAGE_TYPE_EXE_CPU_ARM: u16 = 0x0000;\n\n/// A [`ITEM_1BS_IMAGE_TYPE`] value bitmask to mark the CPU type as RISC-V\npub const IMAGE_TYPE_EXE_CPU_RISCV: u16 = 0x0100;\n\n/// A [`ITEM_1BS_IMAGE_TYPE`] value bitmask to mark the CPU as an RP2040\npub const IMAGE_TYPE_EXE_CHIP_RP2040: u16 = 0x0000;\n\n/// A [`ITEM_1BS_IMAGE_TYPE`] value bitmask to mark the CPU as an RP2350\npub const IMAGE_TYPE_EXE_CHIP_RP2350: u16 = 0x1000;\n\n/// A [`ITEM_1BS_IMAGE_TYPE`] value bitmask to mark the image as Try Before You Buy.\n///\n/// This means the image must be marked as 'Bought' with the ROM before the\n/// watchdog times out the trial period, otherwise it is erased and the previous\n/// image will be booted.\npub const IMAGE_TYPE_TBYB: u16 = 0x8000;\n\n/// This is the magic Block Start value.\n///\n/// The Pico-SDK calls it `PICOBIN_BLOCK_MARKER_START`\nconst BLOCK_MARKER_START: u32 = 0xffffded3;\n\n/// This is the magic Block END value.\n///\n/// The Pico-SDK calls it `PICOBIN_BLOCK_MARKER_END`\nconst BLOCK_MARKER_END: u32 = 0xab123579;\n\n/// An Image Definition has one item in it - an [`ITEM_1BS_IMAGE_TYPE`]\npub type ImageDef = Block<1>;\n\n/// A Block as understood by the Boot ROM.\n///\n/// This could be an Image Definition, or a Partition Table, or maybe some other\n/// kind of block.\n///\n/// It contains within the special start and end markers the Boot ROM is looking\n/// for.\n#[derive(Debug)]\n#[repr(C)]\npub struct Block<const N: usize> {\n    marker_start: u32,\n    items: [u32; N],\n    length: u32,\n    offset: *const u32,\n    marker_end: u32,\n}\n\nunsafe impl<const N: usize> Sync for Block<N> {}\n\nimpl<const N: usize> Block<N> {\n    /// Construct a new Binary Block, with the given items.\n    ///\n    /// The length, and the Start and End markers are added automatically. The\n    /// Block Loop pointer initially points to itself.\n    pub const fn new(items: [u32; N]) -> Block<N> {\n        Block {\n            marker_start: BLOCK_MARKER_START,\n            items,\n            length: item_last(N as u16),\n            // offset from this block to next block in loop. By default\n            // we form a Block Loop with a single Block in it.\n            offset: core::ptr::null(),\n            marker_end: BLOCK_MARKER_END,\n        }\n    }\n\n    /// Change the Block Loop offset value.\n    ///\n    /// This method isn't that useful because you can't evaluate the difference\n    /// between two pointers in a const context as the addresses aren't assigned\n    /// until long after the const evaluator has run.\n    ///\n    /// If you think you need this method, you might want to set a unique random\n    /// value here and swap it for the real offset as a post-processing step.\n    pub const fn with_offset(self, offset: *const u32) -> Block<N> {\n        Block { offset, ..self }\n    }\n}\n\nimpl Block<0> {\n    /// Construct an empty block.\n    pub const fn empty() -> Block<0> {\n        Block::new([])\n    }\n\n    /// Make the block one word larger\n    pub const fn extend(self, word: u32) -> Block<1> {\n        Block::new([word])\n    }\n}\n\nimpl Block<1> {\n    /// Make the block one word larger\n    pub const fn extend(self, word: u32) -> Block<2> {\n        Block::new([self.items[0], word])\n    }\n}\n\nimpl Block<2> {\n    /// Make the block one word larger\n    pub const fn extend(self, word: u32) -> Block<3> {\n        Block::new([self.items[0], self.items[1], word])\n    }\n}\n\nimpl ImageDef {\n    /// Construct a new IMAGE_DEF Block, for an EXE with the given security and\n    /// architecture.\n    pub const fn arch_exe(security: Security, architecture: Architecture) -> Self {\n        Self::new([item_image_type_exe(security, architecture)])\n    }\n\n    /// Construct a new IMAGE_DEF Block, for an EXE with the given security.\n    ///\n    /// The target architecture is taken from the current build target (i.e. Arm\n    /// or RISC-V).\n    pub const fn exe(security: Security) -> Self {\n        if cfg!(all(target_arch = \"riscv32\", target_os = \"none\")) {\n            Self::arch_exe(security, Architecture::Riscv)\n        } else {\n            Self::arch_exe(security, Architecture::Arm)\n        }\n    }\n\n    /// Construct a new IMAGE_DEF Block, for a Non-Secure EXE.\n    ///\n    /// The target architecture is taken from the current build target (i.e. Arm\n    /// or RISC-V).\n    pub const fn non_secure_exe() -> Self {\n        Self::exe(Security::NonSecure)\n    }\n\n    /// Construct a new IMAGE_DEF Block, for a Secure EXE.\n    ///\n    /// The target architecture is taken from the current build target (i.e. Arm\n    /// or RISC-V).\n    pub const fn secure_exe() -> Self {\n        Self::exe(Security::Secure)\n    }\n}\n\n/// We make our partition table this fixed size.\npub const PARTITION_TABLE_MAX_ITEMS: usize = 128;\n\n/// Describes a unpartitioned space\n#[derive(Debug, Clone, PartialEq, Eq, Default)]\npub struct UnpartitionedSpace {\n    permissions_and_location: u32,\n    permissions_and_flags: u32,\n}\n\nimpl UnpartitionedSpace {\n    /// Create a new unpartitioned space.\n    ///\n    /// It defaults to no permissions.\n    pub const fn new() -> Self {\n        Self {\n            permissions_and_location: 0,\n            permissions_and_flags: 0,\n        }\n    }\n\n    /// Create a new unpartitioned space from run-time values.\n    ///\n    /// Get these from the ROM function `get_partition_table_info` with an argument of `PT_INFO`.\n    pub const fn from_raw(permissions_and_location: u32, permissions_and_flags: u32) -> Self {\n        Self {\n            permissions_and_location,\n            permissions_and_flags,\n        }\n    }\n\n    /// Add a permission\n    pub const fn with_permission(self, permission: Permission) -> Self {\n        Self {\n            permissions_and_flags: self.permissions_and_flags | permission as u32,\n            permissions_and_location: self.permissions_and_location | permission as u32,\n        }\n    }\n\n    /// Set a flag\n    pub const fn with_flag(self, flag: UnpartitionedFlag) -> Self {\n        Self {\n            permissions_and_flags: self.permissions_and_flags | flag as u32,\n            ..self\n        }\n    }\n\n    /// Get the partition start and end\n    ///\n    /// The offsets are in 4 KiB sectors, inclusive.\n    pub fn get_first_last_sectors(&self) -> (u16, u16) {\n        (\n            (self.permissions_and_location & 0x0000_1FFF) as u16,\n            ((self.permissions_and_location >> 13) & 0x0000_1FFF) as u16,\n        )\n    }\n\n    /// Get the partition start and end\n    ///\n    /// The offsets are in bytes, inclusive.\n    pub fn get_first_last_bytes(&self) -> (u32, u32) {\n        let (first, last) = self.get_first_last_sectors();\n        (u32::from(first) * 4096, (u32::from(last) * 4096) + 4095)\n    }\n\n    /// Check if it has a permission\n    pub fn has_permission(&self, permission: Permission) -> bool {\n        let mask = permission as u32;\n        (self.permissions_and_flags & mask) != 0\n    }\n\n    /// Check if the partition has a flag set\n    pub fn has_flag(&self, flag: UnpartitionedFlag) -> bool {\n        let mask = flag as u32;\n        (self.permissions_and_flags & mask) != 0\n    }\n}\n\nimpl core::fmt::Display for UnpartitionedSpace {\n    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {\n        let (first, last) = self.get_first_last_bytes();\n        write!(\n            f,\n            \"{:#010x}..{:#010x} S:{}{} NS:{}{} B:{}{}\",\n            first,\n            last,\n            if self.has_permission(Permission::SecureRead) {\n                'R'\n            } else {\n                '_'\n            },\n            if self.has_permission(Permission::SecureWrite) {\n                'W'\n            } else {\n                '_'\n            },\n            if self.has_permission(Permission::NonSecureRead) {\n                'R'\n            } else {\n                '_'\n            },\n            if self.has_permission(Permission::NonSecureWrite) {\n                'W'\n            } else {\n                '_'\n            },\n            if self.has_permission(Permission::BootRead) {\n                'R'\n            } else {\n                '_'\n            },\n            if self.has_permission(Permission::BootWrite) {\n                'W'\n            } else {\n                '_'\n            }\n        )\n    }\n}\n\n/// Describes a Partition\n#[derive(Debug, Clone, PartialEq, Eq)]\npub struct Partition {\n    permissions_and_location: u32,\n    permissions_and_flags: u32,\n    id: Option<u64>,\n    extra_families: [u32; 4],\n    extra_families_len: usize,\n    name: [u8; 128],\n}\n\nimpl Partition {\n    const FLAGS_HAS_ID: u32 = 0b1;\n    const FLAGS_LINK_TYPE_A_PARTITION: u32 = 0b01 << 1;\n    const FLAGS_LINK_TYPE_OWNER: u32 = 0b10 << 1;\n    const FLAGS_LINK_MASK: u32 = 0b111111 << 1;\n    const FLAGS_HAS_NAME: u32 = 0b1 << 12;\n    const FLAGS_HAS_EXTRA_FAMILIES_SHIFT: u8 = 7;\n    const FLAGS_HAS_EXTRA_FAMILIES_MASK: u32 = 0b11 << Self::FLAGS_HAS_EXTRA_FAMILIES_SHIFT;\n\n    /// Create a new partition, with the given start and end sectors.\n    ///\n    /// It defaults to no permissions.\n    pub const fn new(first_sector: u16, last_sector: u16) -> Self {\n        // 0x2000 sectors of 4 KiB is 32 MiB, which is the total XIP area\n        core::assert!(first_sector < 0x2000);\n        core::assert!(last_sector < 0x2000);\n        core::assert!(first_sector <= last_sector);\n        Self {\n            permissions_and_location: (last_sector as u32) << 13 | first_sector as u32,\n            permissions_and_flags: 0,\n            id: None,\n            extra_families: [0; 4],\n            extra_families_len: 0,\n            name: [0; 128],\n        }\n    }\n\n    /// Create a new partition from run-time values.\n    ///\n    /// Get these from the ROM function `get_partition_table_info` with an argument of `PARTITION_LOCATION_AND_FLAGS`.\n    pub const fn from_raw(permissions_and_location: u32, permissions_and_flags: u32) -> Self {\n        Self {\n            permissions_and_location,\n            permissions_and_flags,\n            id: None,\n            extra_families: [0; 4],\n            extra_families_len: 0,\n            name: [0; 128],\n        }\n    }\n\n    /// Add a permission\n    pub const fn with_permission(self, permission: Permission) -> Self {\n        Self {\n            permissions_and_location: self.permissions_and_location | permission as u32,\n            permissions_and_flags: self.permissions_and_flags | permission as u32,\n            ..self\n        }\n    }\n\n    /// Set the name of the partition\n    pub const fn with_name(self, name: &str) -> Self {\n        let mut new_name = [0u8; 128];\n        let name = name.as_bytes();\n        let mut idx = 0;\n        new_name[0] = name.len() as u8;\n        while idx < name.len() {\n            new_name[idx + 1] = name[idx];\n            idx += 1;\n        }\n        Self {\n            name: new_name,\n            permissions_and_flags: self.permissions_and_flags | Self::FLAGS_HAS_NAME,\n            ..self\n        }\n    }\n\n    /// Set the extra families for the partition.\n    ///\n    /// You can supply up to four.\n    pub const fn with_extra_families(self, extra_families: &[u32]) -> Self {\n        core::assert!(extra_families.len() <= 4);\n        let mut new_extra_families = [0u32; 4];\n        let mut idx = 0;\n        while idx < extra_families.len() {\n            new_extra_families[idx] = extra_families[idx];\n            idx += 1;\n        }\n        Self {\n            extra_families: new_extra_families,\n            extra_families_len: extra_families.len(),\n            permissions_and_flags: (self.permissions_and_flags & !Self::FLAGS_HAS_EXTRA_FAMILIES_MASK)\n                | (extra_families.len() as u32) << Self::FLAGS_HAS_EXTRA_FAMILIES_SHIFT,\n            ..self\n        }\n    }\n\n    /// Set the ID\n    pub const fn with_id(self, id: u64) -> Self {\n        Self {\n            id: Some(id),\n            permissions_and_flags: self.permissions_and_flags | Self::FLAGS_HAS_ID,\n            ..self\n        }\n    }\n\n    /// Add a link\n    pub const fn with_link(self, link: Link) -> Self {\n        let mut new_flags = self.permissions_and_flags & !Self::FLAGS_LINK_MASK;\n        match link {\n            Link::Nothing => {}\n            Link::ToA { partition_idx } => {\n                core::assert!(partition_idx < 16);\n                new_flags |= Self::FLAGS_LINK_TYPE_A_PARTITION;\n                new_flags |= (partition_idx as u32) << 3;\n            }\n            Link::ToOwner { partition_idx } => {\n                core::assert!(partition_idx < 16);\n                new_flags |= Self::FLAGS_LINK_TYPE_OWNER;\n                new_flags |= (partition_idx as u32) << 3;\n            }\n        }\n        Self {\n            permissions_and_flags: new_flags,\n            ..self\n        }\n    }\n\n    /// Set a flag\n    pub const fn with_flag(self, flag: PartitionFlag) -> Self {\n        Self {\n            permissions_and_flags: self.permissions_and_flags | flag as u32,\n            ..self\n        }\n    }\n\n    /// Get the partition start and end\n    ///\n    /// The offsets are in 4 KiB sectors, inclusive.\n    pub fn get_first_last_sectors(&self) -> (u16, u16) {\n        (\n            (self.permissions_and_location & 0x0000_1FFF) as u16,\n            ((self.permissions_and_location >> 13) & 0x0000_1FFF) as u16,\n        )\n    }\n\n    /// Get the partition start and end\n    ///\n    /// The offsets are in bytes, inclusive.\n    pub fn get_first_last_bytes(&self) -> (u32, u32) {\n        let (first, last) = self.get_first_last_sectors();\n        (u32::from(first) * 4096, (u32::from(last) * 4096) + 4095)\n    }\n\n    /// Check if it has a permission\n    pub fn has_permission(&self, permission: Permission) -> bool {\n        let mask = permission as u32;\n        (self.permissions_and_flags & mask) != 0\n    }\n\n    /// Get which extra families are allowed in this partition\n    pub fn get_extra_families(&self) -> &[u32] {\n        &self.extra_families[0..self.extra_families_len]\n    }\n\n    /// Get the name of the partition\n    ///\n    /// Returns `None` if there's no name, or the name is not valid UTF-8.\n    pub fn get_name(&self) -> Option<&str> {\n        let len = self.name[0] as usize;\n        if len == 0 {\n            None\n        } else {\n            core::str::from_utf8(&self.name[1..=len]).ok()\n        }\n    }\n\n    /// Get the ID\n    pub fn get_id(&self) -> Option<u64> {\n        self.id\n    }\n\n    /// Check if this partition is linked\n    pub fn get_link(&self) -> Link {\n        if (self.permissions_and_flags & Self::FLAGS_LINK_TYPE_A_PARTITION) != 0 {\n            let partition_idx = ((self.permissions_and_flags >> 3) & 0x0F) as u8;\n            Link::ToA { partition_idx }\n        } else if (self.permissions_and_flags & Self::FLAGS_LINK_TYPE_OWNER) != 0 {\n            let partition_idx = ((self.permissions_and_flags >> 3) & 0x0F) as u8;\n            Link::ToOwner { partition_idx }\n        } else {\n            Link::Nothing\n        }\n    }\n\n    /// Check if the partition has a flag set\n    pub fn has_flag(&self, flag: PartitionFlag) -> bool {\n        let mask = flag as u32;\n        (self.permissions_and_flags & mask) != 0\n    }\n}\n\nimpl core::fmt::Display for Partition {\n    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {\n        let (first, last) = self.get_first_last_bytes();\n        write!(\n            f,\n            \"{:#010x}..{:#010x} S:{}{} NS:{}{} B:{}{}\",\n            first,\n            last,\n            if self.has_permission(Permission::SecureRead) {\n                'R'\n            } else {\n                '_'\n            },\n            if self.has_permission(Permission::SecureWrite) {\n                'W'\n            } else {\n                '_'\n            },\n            if self.has_permission(Permission::NonSecureRead) {\n                'R'\n            } else {\n                '_'\n            },\n            if self.has_permission(Permission::NonSecureWrite) {\n                'W'\n            } else {\n                '_'\n            },\n            if self.has_permission(Permission::BootRead) {\n                'R'\n            } else {\n                '_'\n            },\n            if self.has_permission(Permission::BootWrite) {\n                'W'\n            } else {\n                '_'\n            }\n        )\n    }\n}\n\n/// Describes a partition table.\n///\n/// Don't store this as a static - make sure you convert it to a block.\n#[derive(Clone)]\npub struct PartitionTableBlock {\n    /// This must look like a block, including the 1 word header and the 3 word footer.\n    contents: [u32; PARTITION_TABLE_MAX_ITEMS],\n    /// This value doesn't include the 1 word header or the 3 word footer\n    num_items: usize,\n}\n\nimpl PartitionTableBlock {\n    /// Create an empty Block, big enough for a partition table.\n    ///\n    /// At a minimum you need to call [`Self::add_partition_item`].\n    pub const fn new() -> PartitionTableBlock {\n        let mut contents = [0; PARTITION_TABLE_MAX_ITEMS];\n        contents[0] = BLOCK_MARKER_START;\n        contents[1] = item_last(0);\n        contents[2] = 0;\n        contents[3] = BLOCK_MARKER_END;\n        PartitionTableBlock { contents, num_items: 0 }\n    }\n\n    /// Add a partition to the partition table\n    pub const fn add_partition_item(self, unpartitioned: UnpartitionedSpace, partitions: &[Partition]) -> Self {\n        let mut new_table = PartitionTableBlock::new();\n        let mut idx = 0;\n        // copy over old table, with the header but not the footer\n        while idx < self.num_items + 1 {\n            new_table.contents[idx] = self.contents[idx];\n            idx += 1;\n        }\n\n        // 1. add item header space (we fill this in later)\n        let header_idx = idx;\n        new_table.contents[idx] = 0;\n        idx += 1;\n\n        // 2. unpartitioned space flags\n        //\n        // (the location of unpartition space is not recorded here - it is\n        // inferred because the unpartitioned space is where the partitions are\n        // not)\n        new_table.contents[idx] = unpartitioned.permissions_and_flags;\n        idx += 1;\n\n        // 3. partition info\n\n        let mut partition_no = 0;\n        while partition_no < partitions.len() {\n            // a. permissions_and_location (4K units)\n            new_table.contents[idx] = partitions[partition_no].permissions_and_location;\n            idx += 1;\n\n            // b. permissions_and_flags\n            new_table.contents[idx] = partitions[partition_no].permissions_and_flags;\n            idx += 1;\n\n            // c. ID\n            if let Some(id) = partitions[partition_no].id {\n                new_table.contents[idx] = id as u32;\n                new_table.contents[idx + 1] = (id >> 32) as u32;\n                idx += 2;\n            }\n\n            // d. Extra Families\n            let mut extra_families_idx = 0;\n            while extra_families_idx < partitions[partition_no].extra_families_len {\n                new_table.contents[idx] = partitions[partition_no].extra_families[extra_families_idx];\n                idx += 1;\n                extra_families_idx += 1;\n            }\n\n            // e. Name\n            let mut name_idx = 0;\n            while name_idx < partitions[partition_no].name[0] as usize {\n                let name_chunk = [\n                    partitions[partition_no].name[name_idx],\n                    partitions[partition_no].name[name_idx + 1],\n                    partitions[partition_no].name[name_idx + 2],\n                    partitions[partition_no].name[name_idx + 3],\n                ];\n                new_table.contents[idx] = u32::from_le_bytes(name_chunk);\n                name_idx += 4;\n                idx += 1;\n            }\n\n            partition_no += 1;\n        }\n\n        let len = idx - header_idx;\n        new_table.contents[header_idx] = item_generic_2bs(partitions.len() as u8, len as u16, ITEM_2BS_PARTITION_TABLE);\n\n        // 7. New Footer\n        new_table.contents[idx] = item_last(idx as u16 - 1);\n        new_table.contents[idx + 1] = 0;\n        new_table.contents[idx + 2] = BLOCK_MARKER_END;\n\n        // ignore the header\n        new_table.num_items = idx - 1;\n        new_table\n    }\n\n    /// Add a version number to the partition table\n    pub const fn with_version(self, major: u16, minor: u16) -> Self {\n        let mut new_table = PartitionTableBlock::new();\n        let mut idx = 0;\n        // copy over old table, with the header but not the footer\n        while idx < self.num_items + 1 {\n            new_table.contents[idx] = self.contents[idx];\n            idx += 1;\n        }\n\n        // 1. add item\n        new_table.contents[idx] = item_generic_2bs(0, 2, ITEM_1BS_VERSION);\n        idx += 1;\n        new_table.contents[idx] = (major as u32) << 16 | minor as u32;\n        idx += 1;\n\n        // 2. New Footer\n        new_table.contents[idx] = item_last(idx as u16 - 1);\n        new_table.contents[idx + 1] = 0;\n        new_table.contents[idx + 2] = BLOCK_MARKER_END;\n\n        // ignore the header\n        new_table.num_items = idx - 1;\n        new_table\n    }\n\n    /// Add a SHA256 hash of the Block\n    ///\n    /// Adds a `HASH_DEF` covering all the previous items in the Block, and a\n    /// `HASH_VALUE` with a SHA-256 hash of them.\n    pub const fn with_sha256(self) -> Self {\n        let mut new_table = PartitionTableBlock::new();\n        let mut idx = 0;\n        // copy over old table, with the header but not the footer\n        while idx < self.num_items + 1 {\n            new_table.contents[idx] = self.contents[idx];\n            idx += 1;\n        }\n\n        // 1. HASH_DEF says what is hashed\n        new_table.contents[idx] = item_generic_2bs(1, 2, ITEM_2BS_HASH_DEF);\n        idx += 1;\n        // we're hashing all the previous contents - including this line.\n        new_table.contents[idx] = (idx + 1) as u32;\n        idx += 1;\n\n        // calculate hash over prior contents\n        let input = unsafe { core::slice::from_raw_parts(new_table.contents.as_ptr() as *const u8, idx * 4) };\n        let hash: [u8; 32] = sha2_const_stable::Sha256::new().update(input).finalize();\n\n        // 2. HASH_VALUE contains the hash\n        new_table.contents[idx] = item_generic_2bs(0, 9, ITEM_1BS_HASH_VALUE);\n        idx += 1;\n\n        let mut hash_idx = 0;\n        while hash_idx < hash.len() {\n            new_table.contents[idx] = u32::from_le_bytes([\n                hash[hash_idx],\n                hash[hash_idx + 1],\n                hash[hash_idx + 2],\n                hash[hash_idx + 3],\n            ]);\n            idx += 1;\n            hash_idx += 4;\n        }\n\n        // 3. New Footer\n        new_table.contents[idx] = item_last(idx as u16 - 1);\n        new_table.contents[idx + 1] = 0;\n        new_table.contents[idx + 2] = BLOCK_MARKER_END;\n\n        // ignore the header\n        new_table.num_items = idx - 1;\n        new_table\n    }\n}\n\nimpl Default for PartitionTableBlock {\n    fn default() -> Self {\n        Self::new()\n    }\n}\n\n/// Flags that a Partition can have\n#[derive(Debug, Copy, Clone, PartialEq, Eq)]\n#[repr(u32)]\n#[allow(missing_docs)]\npub enum PartitionFlag {\n    NotBootableArm = 1 << 9,\n    NotBootableRiscv = 1 << 10,\n    Uf2DownloadAbNonBootableOwnerAffinity = 1 << 11,\n    Uf2DownloadNoReboot = 1 << 13,\n    AcceptsDefaultFamilyRp2040 = 1 << 14,\n    AcceptsDefaultFamilyData = 1 << 16,\n    AcceptsDefaultFamilyRp2350ArmS = 1 << 17,\n    AcceptsDefaultFamilyRp2350Riscv = 1 << 18,\n    AcceptsDefaultFamilyRp2350ArmNs = 1 << 19,\n}\n\n/// Flags that a Partition can have\n#[derive(Debug, Copy, Clone, PartialEq, Eq)]\n#[repr(u32)]\n#[allow(missing_docs)]\npub enum UnpartitionedFlag {\n    Uf2DownloadNoReboot = 1 << 13,\n    AcceptsDefaultFamilyRp2040 = 1 << 14,\n    AcceptsDefaultFamilyAbsolute = 1 << 15,\n    AcceptsDefaultFamilyData = 1 << 16,\n    AcceptsDefaultFamilyRp2350ArmS = 1 << 17,\n    AcceptsDefaultFamilyRp2350Riscv = 1 << 18,\n    AcceptsDefaultFamilyRp2350ArmNs = 1 << 19,\n}\n\n/// Kinds of linked partition\n#[derive(Debug, Copy, Clone, PartialEq, Eq)]\npub enum Link {\n    /// Not linked to anything\n    Nothing,\n    /// This is a B partition - link to our A partition.\n    ToA {\n        /// The index of our matching A partition.\n        partition_idx: u8,\n    },\n    /// Link to the partition that owns this one.\n    ToOwner {\n        /// The idx of our owner\n        partition_idx: u8,\n    },\n}\n\n/// Permissions that a Partition can have\n#[derive(Debug, Copy, Clone, PartialEq, Eq)]\n#[repr(u32)]\npub enum Permission {\n    /// Can be read in Secure Mode\n    ///\n    /// Corresponds to `PERMISSION_S_R_BITS` in the Pico SDK\n    SecureRead = 1 << 26,\n    /// Can be written in Secure Mode\n    ///\n    /// Corresponds to `PERMISSION_S_W_BITS` in the Pico SDK\n    SecureWrite = 1 << 27,\n    /// Can be read in Non-Secure Mode\n    ///\n    /// Corresponds to `PERMISSION_NS_R_BITS` in the Pico SDK\n    NonSecureRead = 1 << 28,\n    /// Can be written in Non-Secure Mode\n    ///\n    /// Corresponds to `PERMISSION_NS_W_BITS` in the Pico SDK\n    NonSecureWrite = 1 << 29,\n    /// Can be read in Non-Secure Bootloader mode\n    ///\n    /// Corresponds to `PERMISSION_NSBOOT_R_BITS` in the Pico SDK\n    BootRead = 1 << 30,\n    /// Can be written in Non-Secure Bootloader mode\n    ///\n    /// Corresponds to `PERMISSION_NSBOOT_W_BITS` in the Pico SDK\n    BootWrite = 1 << 31,\n}\n\nimpl Permission {\n    /// Is this permission bit set this in this bitmask?\n    pub const fn is_in(self, mask: u32) -> bool {\n        (mask & (self as u32)) != 0\n    }\n}\n\n/// The supported RP2350 CPU architectures\n#[derive(Debug, Copy, Clone, PartialEq, Eq)]\npub enum Architecture {\n    /// Core is in Arm Cortex-M33 mode\n    Arm,\n    /// Core is in RISC-V / Hazard3 mode\n    Riscv,\n}\n\n/// The kinds of Secure Boot we support\n#[derive(Debug, Copy, Clone)]\npub enum Security {\n    /// Security mode not given\n    Unspecified,\n    /// Start in Non-Secure mode\n    NonSecure,\n    /// Start in Secure mode\n    Secure,\n}\n\n/// Make an item containing a tag, 1 byte length and two extra bytes.\n///\n/// The `command` arg should contain `1BS`\npub const fn item_generic_1bs(value: u16, length: u8, command: u8) -> u32 {\n    ((value as u32) << 16) | ((length as u32) << 8) | (command as u32)\n}\n\n/// Make an item containing a tag, 2 byte length and one extra byte.\n///\n/// The `command` arg should contain `2BS`\npub const fn item_generic_2bs(value: u8, length: u16, command: u8) -> u32 {\n    ((value as u32) << 24) | ((length as u32) << 8) | (command as u32)\n}\n\n/// Create Image Type item, of type IGNORED.\npub const fn item_ignored() -> u32 {\n    item_generic_2bs(0, 1, ITEM_2BS_IGNORED)\n}\n\n/// Create Image Type item, of type INVALID.\npub const fn item_image_type_invalid() -> u32 {\n    let value = IMAGE_TYPE_INVALID;\n    item_generic_1bs(value, 1, ITEM_1BS_IMAGE_TYPE)\n}\n\n/// Create Image Type item, of type DATA.\npub const fn item_image_type_data() -> u32 {\n    let value = IMAGE_TYPE_DATA;\n    item_generic_1bs(value, 1, ITEM_1BS_IMAGE_TYPE)\n}\n\n/// Create Image Type item, of type EXE.\npub const fn item_image_type_exe(security: Security, arch: Architecture) -> u32 {\n    let mut value = IMAGE_TYPE_EXE | IMAGE_TYPE_EXE_CHIP_RP2350;\n\n    match arch {\n        Architecture::Arm => {\n            value |= IMAGE_TYPE_EXE_CPU_ARM;\n        }\n        Architecture::Riscv => {\n            value |= IMAGE_TYPE_EXE_CPU_RISCV;\n        }\n    }\n\n    match security {\n        Security::Unspecified => value |= IMAGE_TYPE_EXE_TYPE_SECURITY_UNSPECIFIED,\n        Security::NonSecure => value |= IMAGE_TYPE_EXE_TYPE_SECURITY_NS,\n        Security::Secure => value |= IMAGE_TYPE_EXE_TYPE_SECURITY_S,\n    }\n\n    item_generic_1bs(value, 1, ITEM_1BS_IMAGE_TYPE)\n}\n\n/// Create a Block Last item.\npub const fn item_last(length: u16) -> u32 {\n    item_generic_2bs(0, length, ITEM_2BS_LAST)\n}\n\n/// Create a Vector Table item.\n///\n/// This is only allowed on Arm systems.\npub const fn item_vector_table(table_ptr: u32) -> [u32; 2] {\n    [item_generic_1bs(0, 2, ITEM_1BS_VECTOR_TABLE), table_ptr]\n}\n\n/// Create an Entry Point item.\npub const fn item_entry_point(entry_point: u32, initial_sp: u32) -> [u32; 3] {\n    [item_generic_1bs(0, 3, ITEM_1BS_ENTRY_POINT), entry_point, initial_sp]\n}\n\n/// Create an Rolling Window item.\n///\n/// The delta is the number of bytes into the image that 0x10000000 should\n/// be mapped.\npub const fn item_rolling_window(delta: u32) -> [u32; 2] {\n    [item_generic_1bs(0, 3, ITEM_1BS_ROLLING_WINDOW_DELTA), delta]\n}\n\n#[cfg(test)]\nmod test {\n    use super::*;\n\n    /// I used this JSON, with `picotool partition create`:\n    ///\n    /// ```json\n    /// {\n    ///   \"version\": [1, 0],\n    ///   \"unpartitioned\": {\n    ///     \"families\": [\"absolute\"],\n    ///     \"permissions\": {\n    ///       \"secure\": \"rw\",\n    ///       \"nonsecure\": \"rw\",\n    ///       \"bootloader\": \"rw\"\n    ///     }\n    ///   },\n    ///   \"partitions\": [\n    ///     {\n    ///       \"name\": \"A\",\n    ///       \"id\": 0,\n    ///       \"size\": \"2044K\",\n    ///       \"families\": [\"rp2350-arm-s\", \"rp2350-riscv\"],\n    ///       \"permissions\": {\n    ///         \"secure\": \"rw\",\n    ///         \"nonsecure\": \"rw\",\n    ///         \"bootloader\": \"rw\"\n    ///       }\n    ///     },\n    ///     {\n    ///       \"name\": \"B\",\n    ///       \"id\": 1,\n    ///       \"size\": \"2044K\",\n    ///       \"families\": [\"rp2350-arm-s\", \"rp2350-riscv\"],\n    ///       \"permissions\": {\n    ///         \"secure\": \"rw\",\n    ///         \"nonsecure\": \"rw\",\n    ///         \"bootloader\": \"rw\"\n    ///       },\n    ///       \"link\": [\"a\", 0]\n    ///     }\n    ///   ]\n    /// }\n    /// ```\n    #[test]\n    fn make_hashed_partition_table() {\n        let table = PartitionTableBlock::new()\n            .add_partition_item(\n                UnpartitionedSpace::new()\n                    .with_permission(Permission::SecureRead)\n                    .with_permission(Permission::SecureWrite)\n                    .with_permission(Permission::NonSecureRead)\n                    .with_permission(Permission::NonSecureWrite)\n                    .with_permission(Permission::BootRead)\n                    .with_permission(Permission::BootWrite)\n                    .with_flag(UnpartitionedFlag::AcceptsDefaultFamilyAbsolute),\n                &[\n                    Partition::new(2, 512)\n                        .with_id(0)\n                        .with_flag(PartitionFlag::AcceptsDefaultFamilyRp2350ArmS)\n                        .with_flag(PartitionFlag::AcceptsDefaultFamilyRp2350Riscv)\n                        .with_permission(Permission::SecureRead)\n                        .with_permission(Permission::SecureWrite)\n                        .with_permission(Permission::NonSecureRead)\n                        .with_permission(Permission::NonSecureWrite)\n                        .with_permission(Permission::BootRead)\n                        .with_permission(Permission::BootWrite)\n                        .with_name(\"A\"),\n                    Partition::new(513, 1023)\n                        .with_id(1)\n                        .with_flag(PartitionFlag::AcceptsDefaultFamilyRp2350ArmS)\n                        .with_flag(PartitionFlag::AcceptsDefaultFamilyRp2350Riscv)\n                        .with_link(Link::ToA { partition_idx: 0 })\n                        .with_permission(Permission::SecureRead)\n                        .with_permission(Permission::SecureWrite)\n                        .with_permission(Permission::NonSecureRead)\n                        .with_permission(Permission::NonSecureWrite)\n                        .with_permission(Permission::BootRead)\n                        .with_permission(Permission::BootWrite)\n                        .with_name(\"B\"),\n                ],\n            )\n            .with_version(1, 0)\n            .with_sha256();\n        let expected = &[\n            0xffffded3, // start\n            0x02000c0a, // Item = PARTITION_TABLE\n            0xfc008000, // Unpartitioned Space - permissions_and_flags\n            0xfc400002, // Partition 0 - permissions_and_location (512 * 4096, 2 * 4096)\n            0xfc061001, //     permissions_and_flags HAS_ID | HAS_NAME | ARM-S | RISC-V\n            0x00000000, //     ID\n            0x00000000, //     ID\n            0x00004101, //     Name (\"A\")\n            0xfc7fe201, // Partition 1 - permissions_and_location (1023 * 4096, 513 * 4096)\n            0xfc061003, //     permissions_and_flags LINKA(0) | HAS_ID | HAS_NAME | ARM-S | RISC-V\n            0x00000001, //     ID\n            0x00000000, //     ID\n            0x00004201, //     Name (\"B\")\n            0x00000248, // Item = Version\n            0x00010000, // 0, 1\n            0x01000247, // HASH_DEF with 2 words, and SHA256 hash\n            0x00000011, // 17 words hashed\n            0x0000094b, // HASH_VALUE with 9 words\n            0x1945cdad, // Hash word 0\n            0x6b5f9773, // Hash word 1\n            0xe2bf39bd, // Hash word 2\n            0xb243e599, // Hash word 3\n            0xab2f0e9a, // Hash word 4\n            0x4d5d6d0b, // Hash word 5\n            0xf973050f, // Hash word 6\n            0x5ab6dadb, // Hash word 7\n            0x000019ff, // Last Item\n            0x00000000, // Block Loop Next Offset\n            0xab123579, // End\n        ];\n        core::assert_eq!(\n            &table.contents[..29],\n            expected,\n            \"{:#010x?}\\n != \\n{:#010x?}\",\n            &table.contents[0..29],\n            expected,\n        );\n    }\n}\n"
  },
  {
    "path": "embassy-rp/src/bootsel.rs",
    "content": "//! Boot Select button\n//!\n//! The RP2040 rom supports a BOOTSEL button that is used to enter the USB bootloader\n//! if held during reset. To avoid wasting GPIO pins, the button is multiplexed onto\n//! the CS pin of the QSPI flash, but that makes it somewhat expensive and complicated\n//! to utilize outside of the rom's bootloader.\n//!\n//! This module provides functionality to poll BOOTSEL from an embassy application.\n\nuse crate::Peri;\nuse crate::flash::in_ram;\n\n/// Reads the BOOTSEL button. Returns true if the button is pressed.\n///\n/// Reading isn't cheap, as this function waits for core 1 to finish it's current\n/// task and for any DMAs from flash to complete\npub fn is_bootsel_pressed(_p: Peri<'_, crate::peripherals::BOOTSEL>) -> bool {\n    let mut cs_status = Default::default();\n\n    unsafe { in_ram(|| cs_status = ram_helpers::read_cs_status()) }.expect(\"Must be called from Core 0\");\n\n    // bootsel is active low, so invert\n    !cs_status.infrompad()\n}\n\nmod ram_helpers {\n    use rp_pac::io::regs::GpioStatus;\n\n    /// Temporally reconfigures the CS gpio and returns the GpioStatus.\n\n    /// This function runs from RAM so it can disable flash XIP.\n    ///\n    /// # Safety\n    ///\n    /// The caller must ensure flash is idle and will remain idle.\n    /// This function must live in ram. It uses inline asm to avoid any\n    /// potential calls to ABI functions that might be in flash.\n    #[inline(never)]\n    #[unsafe(link_section = \".data.ram_func\")]\n    #[cfg(target_arch = \"arm\")]\n    pub unsafe fn read_cs_status() -> GpioStatus {\n        let result: u32;\n\n        // Magic value, used as both OEOVER::DISABLE and delay loop counter\n        let magic = 0x2000;\n\n        core::arch::asm!(\n            \".equiv GPIO_STATUS, 0x0\",\n            \".equiv GPIO_CTRL,   0x4\",\n\n            \"ldr {orig_ctrl}, [{cs_gpio}, $GPIO_CTRL]\",\n\n            // The BOOTSEL pulls the flash's CS line low though a 1K resistor.\n            // this is weak enough to avoid disrupting normal operation.\n            // But, if we disable CS's output drive and allow it to float...\n            \"str {val}, [{cs_gpio}, $GPIO_CTRL]\",\n\n            // ...then wait for the state to settle...\n            \"2:\", // ~4000 cycle delay loop\n            \"subs {val}, #8\",\n            \"bne 2b\",\n\n            // ...we can read the current state of bootsel\n            \"ldr {val}, [{cs_gpio}, $GPIO_STATUS]\",\n\n            // Finally, restore CS to normal operation so XIP can continue\n            \"str {orig_ctrl}, [{cs_gpio}, $GPIO_CTRL]\",\n\n            cs_gpio = in(reg) rp_pac::IO_QSPI.gpio(1).as_ptr(),\n            orig_ctrl = out(reg) _,\n            val = inout(reg) magic => result,\n            options(nostack),\n        );\n\n        core::mem::transmute(result)\n    }\n\n    #[cfg(not(target_arch = \"arm\"))]\n    pub unsafe fn read_cs_status() -> GpioStatus {\n        unimplemented!()\n    }\n}\n"
  },
  {
    "path": "embassy-rp/src/clocks.rs",
    "content": "//! # Clock configuration for the RP2040 and RP235x microcontrollers.\n//!\n//! # Clock Configuration API\n//!\n//! This module provides both high-level convenience functions and low-level manual\n//! configuration options for the RP2040 clock system.\n//!\n//! ## High-Level Convenience Functions\n//!\n//! For most users, these functions provide an easy way to configure clocks:\n//!\n//! - `ClockConfig::system_freq(125_000_000)` - Set system clock to a specific frequency with automatic voltage scaling\n//! - `ClockConfig::crystal(12_000_000)` - Default configuration with 12MHz crystal giving 125MHz system clock\n//!\n//! ## Manual Configuration\n//!\n//! For advanced users who need precise control:\n//!\n//! ```rust,ignore\n//! // Start with default configuration and customize it\n//! let mut config = ClockConfig::default();\n//!\n//! // Set custom PLL parameters\n//! config.xosc = Some(XoscConfig {\n//!     hz: 12_000_000,\n//!     sys_pll: Some(PllConfig {\n//!         refdiv: 1,\n//!         fbdiv: 200,\n//!         post_div1: 6,\n//!         post_div2: 2,\n//!     }),\n//!     // ... other fields\n//! });\n//!\n//! // Set voltage for overclocking\n//! config.core_voltage = CoreVoltage::V1_15;\n//! ```\n//!\n//! ## Examples\n//!\n//! ### Standard 125MHz (rp2040) or 150Mhz (rp235x) configuration\n//! ```rust,ignore\n//! let config = ClockConfig::crystal(12_000_000);\n//! ```\n//!\n//! Or using the default configuration:\n//! ```rust,ignore\n//! let config = ClockConfig::default();\n//! ```\n//!\n//! ### Overclock to 200MHz\n//! ```rust,ignore\n//! let config = ClockConfig::system_freq(200_000_000);\n//! ```\n//!\n//! ### Manual configuration for advanced scenarios\n//! ```rust,ignore\n//! use embassy_rp::clocks::{ClockConfig, XoscConfig, PllConfig, CoreVoltage};\n//!\n//! // Start with defaults and customize\n//! let mut config = ClockConfig::default();\n//! config.core_voltage = CoreVoltage::V1_15;\n//! // Set other parameters as needed...\n//! ```\n\nuse core::arch::asm;\nuse core::marker::PhantomData;\n#[cfg(feature = \"rp2040\")]\nuse core::sync::atomic::AtomicU16;\nuse core::sync::atomic::{AtomicU32, Ordering};\n\nuse pac::clocks::vals::*;\n\nuse crate::gpio::{AnyPin, SealedPin};\nuse crate::pac::common::{RW, Reg};\nuse crate::{Peri, pac, reset};\n\n// VCO range from 750 MHz to 1600 MHz (RP2040 datasheet 20 February 2025 2.18.1, RP2350 datasheet 29 July 2025 8.6.1)\nstatic VCO_MIN: u32 = 750_000_000;\nstatic VCO_MAX: u32 = 1_600_000_000;\n\n// NOTE: all gpin handling is commented out for future reference.\n// gpin is not usually safe to use during the boot init() call, so it won't\n// be very useful until we have runtime clock reconfiguration. once this\n// happens we can resurrect the commented-out gpin bits.\n\n/// Clock error types.\n#[derive(Debug, Clone, Copy, PartialEq, Eq)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub enum ClockError {\n    /// PLL failed to lock within the timeout period.\n    PllLockTimedOut,\n    /// Could not find valid PLL parameters for system clock.\n    InvalidPllParameters,\n    /// Reading the core voltage failed due to an unexpected value in the register.\n    UnexpectedCoreVoltageRead,\n}\n\nstruct Clocks {\n    xosc: AtomicU32,\n    sys: AtomicU32,\n    reference: AtomicU32,\n    pll_sys: AtomicU32,\n    pll_usb: AtomicU32,\n    usb: AtomicU32,\n    adc: AtomicU32,\n    // See above re gpin handling being commented out\n    // gpin0: AtomicU32,\n    // gpin1: AtomicU32,\n    rosc: AtomicU32,\n    peri: AtomicU32,\n    #[cfg(feature = \"rp2040\")]\n    rtc: AtomicU16,\n}\n\nstatic CLOCKS: Clocks = Clocks {\n    xosc: AtomicU32::new(0),\n    sys: AtomicU32::new(0),\n    reference: AtomicU32::new(0),\n    pll_sys: AtomicU32::new(0),\n    pll_usb: AtomicU32::new(0),\n    usb: AtomicU32::new(0),\n    adc: AtomicU32::new(0),\n    // See above re gpin handling being commented out\n    // gpin0: AtomicU32::new(0),\n    // gpin1: AtomicU32::new(0),\n    rosc: AtomicU32::new(0),\n    peri: AtomicU32::new(0),\n    #[cfg(feature = \"rp2040\")]\n    rtc: AtomicU16::new(0),\n};\n\n/// Peripheral clock sources.\n#[repr(u8)]\n#[non_exhaustive]\n#[derive(Clone, Copy, Debug, PartialEq, Eq)]\npub enum PeriClkSrc {\n    /// SYS.\n    Sys = ClkPeriCtrlAuxsrc::CLK_SYS as _,\n    /// PLL SYS.\n    PllSys = ClkPeriCtrlAuxsrc::CLKSRC_PLL_SYS as _,\n    /// PLL USB.\n    PllUsb = ClkPeriCtrlAuxsrc::CLKSRC_PLL_USB as _,\n    /// ROSC.\n    Rosc = ClkPeriCtrlAuxsrc::ROSC_CLKSRC_PH as _,\n    /// XOSC.\n    Xosc = ClkPeriCtrlAuxsrc::XOSC_CLKSRC as _,\n    // See above re gpin handling being commented out\n    // Gpin0 = ClkPeriCtrlAuxsrc::CLKSRC_GPIN0 as _ ,\n    // Gpin1 = ClkPeriCtrlAuxsrc::CLKSRC_GPIN1 as _ ,\n}\n\n/// Core voltage regulator settings.\n///\n/// The voltage regulator can be configured for different output voltages.\n/// Higher voltages allow for higher clock frequencies but increase power consumption and heat.\n#[cfg(feature = \"rp2040\")]\n#[repr(u8)]\n#[derive(Clone, Copy, Debug, PartialEq, Eq)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub enum CoreVoltage {\n    /// 0.80V\n    V0_80 = 0b0000,\n    /// 0.85V\n    V0_85 = 0b0110,\n    /// 0.90V\n    V0_90 = 0b0111,\n    /// 0.95V\n    V0_95 = 0b1000,\n    /// 1.00V\n    V1_00 = 0b1001,\n    /// 1.05V\n    V1_05 = 0b1010,\n    /// 1.10V - Default voltage level\n    V1_10 = 0b1011,\n    /// 1.15V - Required for overclocking to 133-200MHz\n    V1_15 = 0b1100,\n    /// 1.20V\n    V1_20 = 0b1101,\n    /// 1.25V\n    V1_25 = 0b1110,\n    /// 1.30V\n    V1_30 = 0b1111,\n}\n\n/// Core voltage regulator settings.\n///\n/// The voltage regulator can be configured for different output voltages.\n/// Higher voltages allow for higher clock frequencies but increase power consumption and heat.\n///\n/// **Note**: The maximum voltage is 1.30V, unless unlocked by setting unless the voltage limit\n/// is disabled using the disable_voltage_limit field in the vreg_ctrl register. For lack of practical use at this\n/// point in time, this is not implemented here. So the maximum voltage in this enum is 1.30V for now.\n#[cfg(feature = \"_rp235x\")]\n#[repr(u8)]\n#[derive(Clone, Copy, Debug, PartialEq, Eq)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub enum CoreVoltage {\n    /// 0.55V\n    V0_55 = 0b00000,\n    /// 0.60V\n    V0_60 = 0b00001,\n    /// 0.65V\n    V0_65 = 0b00010,\n    /// 0.70V\n    V0_70 = 0b00011,\n    /// 0.75V\n    V0_75 = 0b00100,\n    /// 0.80V\n    V0_80 = 0b00101,\n    /// 0.85V\n    V0_85 = 0b00110,\n    /// 0.90V\n    V0_90 = 0b00111,\n    /// 0.95V\n    V0_95 = 0b01000,\n    /// 1.00V\n    V1_00 = 0b01001,\n    /// 1.05V\n    V1_05 = 0b01010,\n    /// 1.10V - Default voltage level\n    V1_10 = 0b01011,\n    /// 1.15V\n    V1_15 = 0b01100,\n    /// 1.20V\n    V1_20 = 0b01101,\n    /// 1.25V\n    V1_25 = 0b01110,\n    /// 1.30V\n    V1_30 = 0b01111,\n}\n\nimpl CoreVoltage {\n    /// Get the recommended Brown-Out Detection (BOD) setting for this voltage.\n    /// Sets the BOD threshold to approximately 80% of the core voltage.\n    fn recommended_bod(self) -> u8 {\n        #[cfg(feature = \"rp2040\")]\n        match self {\n            CoreVoltage::V0_80 => 0b0100, // 0.645V (~81% of 0.80V)\n            CoreVoltage::V0_85 => 0b0101, // 0.688V (~81% of 0.85V)\n            CoreVoltage::V0_90 => 0b0110, // 0.731V (~81% of 0.90V)\n            CoreVoltage::V0_95 => 0b0111, // 0.774V (~81% of 0.95V)\n            CoreVoltage::V1_00 => 0b1000, // 0.817V (~82% of 1.00V)\n            CoreVoltage::V1_05 => 0b1000, // 0.817V (~78% of 1.05V)\n            CoreVoltage::V1_10 => 0b1001, // 0.860V (~78% of 1.10V), the default\n            CoreVoltage::V1_15 => 0b1010, // 0.903V (~79% of 1.15V)\n            CoreVoltage::V1_20 => 0b1011, // 0.946V (~79% of 1.20V)\n            CoreVoltage::V1_25 => 0b1100, // 0.989V (~79% of 1.25V)\n            CoreVoltage::V1_30 => 0b1101, // 1.032V (~79% of 1.30V)\n        }\n        #[cfg(feature = \"_rp235x\")]\n        match self {\n            CoreVoltage::V0_55 => 0b00001, // 0.516V (~94% of 0.55V)\n            CoreVoltage::V0_60 => 0b00010, // 0.559V (~93% of 0.60V)\n            CoreVoltage::V0_65 => 0b00011, // 0.602V (~93% of 0.65V)\n            CoreVoltage::V0_70 => 0b00011, // 0.602V (~86% of 0.70V)\n            CoreVoltage::V0_75 => 0b00100, // 0.645V (~86% of 0.75V)\n            CoreVoltage::V0_80 => 0b00101, // 0.688V (~86% of 0.80V)\n            CoreVoltage::V0_85 => 0b00110, // 0.731V (~86% of 0.85V)\n            CoreVoltage::V0_90 => 0b00110, // 0.731V (~81% of 0.90V)\n            CoreVoltage::V0_95 => 0b00111, // 0.774V (~81% of 0.95V)\n            CoreVoltage::V1_00 => 0b01000, // 0.817V (~82% of 1.00V)\n            CoreVoltage::V1_05 => 0b01000, // 0.817V (~78% of 1.05V)\n            CoreVoltage::V1_10 => 0b01001, // 0.860V (~78% of 1.10V), the default\n            CoreVoltage::V1_15 => 0b01001, // 0.860V (~75% of 1.15V)\n            CoreVoltage::V1_20 => 0b01010, // 0.903V (~75% of 1.20V)\n            CoreVoltage::V1_25 => 0b01010, // 0.903V (~72% of 1.25V)\n            CoreVoltage::V1_30 => 0b01011, // 0.946V (~73% of 1.30V)\n                                            // all others: 0.946V (see CoreVoltage: we do not support setting Voltages higher than 1.30V at this point)\n        }\n    }\n}\n\n/// Clock configuration.\n#[non_exhaustive]\npub struct ClockConfig {\n    /// Ring oscillator configuration.\n    pub rosc: Option<RoscConfig>,\n    /// External oscillator configuration.\n    pub xosc: Option<XoscConfig>,\n    /// Reference clock configuration.\n    pub ref_clk: RefClkConfig,\n    /// System clock configuration.\n    pub sys_clk: SysClkConfig,\n    /// Peripheral clock source configuration.\n    pub peri_clk_src: Option<PeriClkSrc>,\n    /// USB clock configuration.\n    pub usb_clk: Option<UsbClkConfig>,\n    /// ADC clock configuration.\n    pub adc_clk: Option<AdcClkConfig>,\n    /// RTC clock configuration.\n    #[cfg(feature = \"rp2040\")]\n    pub rtc_clk: Option<RtcClkConfig>,\n    /// Core voltage scaling. Defaults to 1.10V.\n    pub core_voltage: CoreVoltage,\n    /// Voltage stabilization delay in microseconds.\n    /// If not set, defaults will be used based on voltage level.\n    pub voltage_stabilization_delay_us: Option<u32>,\n    // See above re gpin handling being commented out\n    // gpin0: Option<(u32, Gpin<'static, AnyPin>)>,\n    // gpin1: Option<(u32, Gpin<'static, AnyPin>)>,\n}\n\nimpl Default for ClockConfig {\n    /// Creates a minimal default configuration with safe values.\n    ///\n    /// This configuration uses the ring oscillator (ROSC) as the clock source\n    /// and sets minimal defaults that guarantee a working system. It's intended\n    /// as a starting point for manual configuration.\n    ///\n    /// Most users should use one of the more specific configuration functions:\n    /// - `ClockConfig::crystal()` - Standard configuration with external crystal\n    /// - `ClockConfig::rosc()` - Configuration using only the internal oscillator\n    /// - `ClockConfig::system_freq()` - Configuration for a specific system frequency\n    fn default() -> Self {\n        Self {\n            rosc: None,\n            xosc: None,\n            ref_clk: RefClkConfig {\n                src: RefClkSrc::Rosc,\n                div: 1,\n            },\n            sys_clk: SysClkConfig {\n                src: SysClkSrc::Rosc,\n                div_int: 1,\n                div_frac: 0,\n            },\n            peri_clk_src: None,\n            usb_clk: None,\n            adc_clk: None,\n            #[cfg(feature = \"rp2040\")]\n            rtc_clk: None,\n            core_voltage: CoreVoltage::V1_10,\n            voltage_stabilization_delay_us: None,\n            // See above re gpin handling being commented out\n            // gpin0: None,\n            // gpin1: None,\n        }\n    }\n}\n\nimpl ClockConfig {\n    /// Clock configuration derived from external crystal.\n    ///\n    /// This uses default settings for most parameters, suitable for typical use cases.\n    /// For manual control of PLL parameters, use `new_manual()` or modify the struct fields directly.\n    pub fn crystal(crystal_hz: u32) -> Self {\n        Self {\n            rosc: Some(RoscConfig {\n                hz: 6_500_000,\n                range: RoscRange::Medium,\n                drive_strength: [0; 8],\n                div: 16,\n            }),\n            xosc: Some(XoscConfig {\n                hz: crystal_hz,\n                sys_pll: Some(PllConfig {\n                    refdiv: 1,\n                    fbdiv: 125,\n                    #[cfg(feature = \"rp2040\")]\n                    post_div1: 6,\n                    #[cfg(feature = \"_rp235x\")]\n                    post_div1: 5,\n                    post_div2: 2,\n                }),\n                usb_pll: Some(PllConfig {\n                    refdiv: 1,\n                    fbdiv: 120,\n                    post_div1: 6,\n                    post_div2: 5,\n                }),\n                delay_multiplier: 128,\n            }),\n            ref_clk: RefClkConfig {\n                src: RefClkSrc::Xosc,\n                div: 1,\n            },\n            sys_clk: SysClkConfig {\n                src: SysClkSrc::PllSys,\n                div_int: 1,\n                div_frac: 0,\n            },\n            peri_clk_src: Some(PeriClkSrc::Sys),\n            // CLK USB = PLL USB (48MHz) / 1 = 48MHz\n            usb_clk: Some(UsbClkConfig {\n                src: UsbClkSrc::PllUsb,\n                div: 1,\n                phase: 0,\n            }),\n            // CLK ADC = PLL USB (48MHZ) / 1 = 48MHz\n            adc_clk: Some(AdcClkConfig {\n                src: AdcClkSrc::PllUsb,\n                div: 1,\n                phase: 0,\n            }),\n            // CLK RTC = PLL USB (48MHz) / 1024 = 46875Hz\n            #[cfg(feature = \"rp2040\")]\n            rtc_clk: Some(RtcClkConfig {\n                src: RtcClkSrc::PllUsb,\n                div_int: 1024,\n                div_frac: 0,\n                phase: 0,\n            }),\n            core_voltage: CoreVoltage::V1_10, // Use hardware default (1.10V)\n            voltage_stabilization_delay_us: None,\n            // See above re gpin handling being commented out\n            // gpin0: None,\n            // gpin1: None,\n        }\n    }\n\n    /// Clock configuration from internal oscillator.\n    pub fn rosc() -> Self {\n        Self {\n            rosc: Some(RoscConfig {\n                hz: 140_000_000,\n                range: RoscRange::High,\n                drive_strength: [0; 8],\n                div: 1,\n            }),\n            xosc: None,\n            ref_clk: RefClkConfig {\n                src: RefClkSrc::Rosc,\n                div: 1,\n            },\n            sys_clk: SysClkConfig {\n                src: SysClkSrc::Rosc,\n                div_int: 1,\n                div_frac: 0,\n            },\n            peri_clk_src: Some(PeriClkSrc::Rosc),\n            usb_clk: None,\n            // CLK ADC = ROSC (140MHz) / 3 ≅ 48MHz\n            adc_clk: Some(AdcClkConfig {\n                src: AdcClkSrc::Rosc,\n                div: 3,\n                phase: 0,\n            }),\n            // CLK RTC = ROSC (140MHz) / 2986.667969 ≅ 46875Hz\n            #[cfg(feature = \"rp2040\")]\n            rtc_clk: Some(RtcClkConfig {\n                src: RtcClkSrc::Rosc,\n                div_int: 2986,\n                div_frac: 171,\n                phase: 0,\n            }),\n            core_voltage: CoreVoltage::V1_10, // Use hardware default (1.10V)\n            voltage_stabilization_delay_us: None,\n            // See above re gpin handling being commented out\n            // gpin0: None,\n            // gpin1: None,\n        }\n    }\n\n    /// Configure clocks derived from an external crystal with specific system frequency.\n    ///\n    /// This function calculates optimal PLL parameters to achieve the requested system\n    /// frequency. This only works for the usual 12MHz crystal. In case a different crystal is used,\n    /// You will have to set the PLL parameters manually.\n    ///\n    /// # Arguments\n    ///\n    /// * `sys_freq_hz` - The desired system clock frequency in Hz\n    ///\n    /// # Returns\n    ///\n    /// A ClockConfig configured to achieve the requested system frequency using the\n    /// the usual 12Mhz crystal, or an error if no valid parameters can be found.\n    ///\n    /// # Note on core voltage:\n    ///\n    /// **For RP2040**:\n    /// To date the only officially documented core voltages (see Datasheet section 2.15.3.1. Instances) are:\n    /// - Up to 133MHz: V1_10 (default)\n    /// - Above 133MHz: V1_15, but in the context of the datasheet covering reaching up to 200Mhz\n    /// That way all other frequencies below 133MHz or above 200MHz are not explicitly documented and not covered here.\n    /// In case You want to go below 133MHz or above 200MHz and want a different voltage, You will have to set that manually and with caution.\n    ///\n    /// **For RP235x**:\n    /// At this point in time there is no official manufacturer endorsement for running the chip on other core voltages and/or other clock speeds than the defaults.\n    /// Using this function is experimental and may not work as expected or even damage the chip.\n    ///\n    /// # Returns\n    ///\n    /// A Result containing either the configured ClockConfig or a ClockError.\n    pub fn system_freq(hz: u32) -> Result<Self, ClockError> {\n        // Start with the standard configuration from crystal()\n        const DEFAULT_CRYSTAL_HZ: u32 = 12_000_000;\n        let mut config = Self::crystal(DEFAULT_CRYSTAL_HZ);\n\n        // No need to modify anything if target frequency is already 125MHz\n        // (which is what crystal() configures by default)\n        #[cfg(feature = \"rp2040\")]\n        if hz == 125_000_000 {\n            return Ok(config);\n        }\n        #[cfg(feature = \"_rp235x\")]\n        if hz == 150_000_000 {\n            return Ok(config);\n        }\n\n        // Find optimal PLL parameters for the requested frequency\n        let sys_pll_params = find_pll_params(DEFAULT_CRYSTAL_HZ, hz).ok_or(ClockError::InvalidPllParameters)?;\n\n        // Replace the sys_pll configuration with our custom parameters\n        if let Some(xosc) = &mut config.xosc {\n            xosc.sys_pll = Some(sys_pll_params);\n        }\n\n        // Set the voltage scale based on the target frequency\n        // Higher frequencies require higher voltage\n        #[cfg(feature = \"rp2040\")]\n        {\n            config.core_voltage = match hz {\n                freq if freq > 133_000_000 => CoreVoltage::V1_15,\n                _ => CoreVoltage::V1_10, // Use default voltage (V1_10)\n            };\n        }\n        #[cfg(feature = \"_rp235x\")]\n        {\n            config.core_voltage = match hz {\n                // There is no official support for running the chip on other core voltages and/or other clock speeds than the defaults.\n                // So for now we have not way of knowing what the voltage should be. Change this if the manufacturer provides more information.\n                _ => CoreVoltage::V1_10, // Use default voltage (V1_10)\n            };\n        }\n\n        Ok(config)\n    }\n\n    /// Configure with manual PLL settings for full control over system clock\n    ///\n    /// This method provides a simple way to configure the system with custom PLL parameters\n    /// without needing to understand the full nested configuration structure.\n    ///\n    /// # Arguments\n    ///\n    /// * `xosc_hz` - The frequency of the external crystal in Hz\n    /// * `pll_config` - The PLL configuration parameters to achieve desired frequency\n    /// * `core_voltage` - Voltage scaling for overclocking (required for >133MHz)\n    ///\n    /// # Returns\n    ///\n    /// A ClockConfig configured with the specified PLL parameters\n    ///\n    /// # Example\n    ///\n    /// ```rust,ignore\n    /// // Configure for 200MHz operation\n    /// let config = Config::default();\n    /// config.clocks = ClockConfig::manual_pll(\n    ///     12_000_000,\n    ///     PllConfig {\n    ///         refdiv: 1,    // Reference divider (12 MHz / 1 = 12 MHz)\n    ///         fbdiv: 100,   // Feedback divider (12 MHz * 100 = 1200 MHz VCO)\n    ///         post_div1: 3, // First post divider (1200 MHz / 3 = 400 MHz)\n    ///         post_div2: 2, // Second post divider (400 MHz / 2 = 200 MHz)\n    ///     },\n    ///     CoreVoltage::V1_15\n    /// );\n    /// ```\n    #[cfg(feature = \"rp2040\")]\n    pub fn manual_pll(xosc_hz: u32, pll_config: PllConfig, core_voltage: CoreVoltage) -> Self {\n        // Validate PLL parameters\n        assert!(pll_config.is_valid(xosc_hz), \"Invalid PLL parameters\");\n\n        let mut config = Self::default();\n\n        config.xosc = Some(XoscConfig {\n            hz: xosc_hz,\n            sys_pll: Some(pll_config),\n            usb_pll: Some(PllConfig {\n                refdiv: 1,\n                fbdiv: 120,\n                post_div1: 6,\n                post_div2: 5,\n            }),\n            delay_multiplier: 128,\n        });\n\n        config.ref_clk = RefClkConfig {\n            src: RefClkSrc::Xosc,\n            div: 1,\n        };\n\n        config.sys_clk = SysClkConfig {\n            src: SysClkSrc::PllSys,\n            div_int: 1,\n            div_frac: 0,\n        };\n\n        config.core_voltage = core_voltage;\n        config.peri_clk_src = Some(PeriClkSrc::Sys);\n\n        // Set reasonable defaults for other clocks\n        config.usb_clk = Some(UsbClkConfig {\n            src: UsbClkSrc::PllUsb,\n            div: 1,\n            phase: 0,\n        });\n\n        config.adc_clk = Some(AdcClkConfig {\n            src: AdcClkSrc::PllUsb,\n            div: 1,\n            phase: 0,\n        });\n\n        config.rtc_clk = Some(RtcClkConfig {\n            src: RtcClkSrc::PllUsb,\n            div_int: 1024,\n            div_frac: 0,\n            phase: 0,\n        });\n\n        config\n    }\n}\n\n/// ROSC freq range.\n#[repr(u16)]\n#[non_exhaustive]\n#[derive(Clone, Copy, Debug, PartialEq, Eq)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub enum RoscRange {\n    /// Low range.\n    Low = pac::rosc::vals::FreqRange::LOW.0,\n    /// Medium range (1.33x low)\n    Medium = pac::rosc::vals::FreqRange::MEDIUM.0,\n    /// High range (2x low)\n    High = pac::rosc::vals::FreqRange::HIGH.0,\n    /// Too high. Should not be used.\n    TooHigh = pac::rosc::vals::FreqRange::TOOHIGH.0,\n}\n\n/// On-chip ring oscillator configuration.\npub struct RoscConfig {\n    /// Final frequency of the oscillator, after the divider has been applied.\n    /// The oscillator has a nominal frequency of 6.5MHz at medium range with\n    /// divider 16 and all drive strengths set to 0, other values should be\n    /// measured in situ.\n    pub hz: u32,\n    /// Oscillator range.\n    pub range: RoscRange,\n    /// Drive strength for oscillator.\n    pub drive_strength: [u8; 8],\n    /// Output divider.\n    pub div: u16,\n}\n\n/// Crystal oscillator configuration.\npub struct XoscConfig {\n    /// Final frequency of the oscillator.\n    pub hz: u32,\n    /// Configuring PLL for the system clock.\n    pub sys_pll: Option<PllConfig>,\n    /// Configuring PLL for the USB clock.\n    pub usb_pll: Option<PllConfig>,\n    /// Multiplier for the startup delay.\n    pub delay_multiplier: u32,\n}\n\n/// PLL configuration.\n#[derive(Clone, Copy, Debug)]\npub struct PllConfig {\n    /// Reference divisor.\n    pub refdiv: u8,\n    /// Feedback divisor.\n    pub fbdiv: u16,\n    /// Output divisor 1.\n    pub post_div1: u8,\n    /// Output divisor 2.\n    pub post_div2: u8,\n}\n\nimpl PllConfig {\n    /// Calculate the output frequency for this PLL configuration\n    /// given an input frequency.\n    pub fn output_frequency(&self, input_hz: u32) -> u32 {\n        let ref_freq = input_hz / self.refdiv as u32;\n        let vco_freq = ref_freq * self.fbdiv as u32;\n        vco_freq / ((self.post_div1 * self.post_div2) as u32)\n    }\n\n    /// Check if this PLL configuration is valid for the given input frequency.\n    pub fn is_valid(&self, input_hz: u32) -> bool {\n        // Check divisor constraints\n        if self.refdiv < 1 || self.refdiv > 63 {\n            return false;\n        }\n        if self.fbdiv < 16 || self.fbdiv > 320 {\n            return false;\n        }\n        if self.post_div1 < 1 || self.post_div1 > 7 {\n            return false;\n        }\n        if self.post_div2 < 1 || self.post_div2 > 7 {\n            return false;\n        }\n        if self.post_div2 > self.post_div1 {\n            return false;\n        }\n\n        // Calculate reference frequency\n        let ref_freq = input_hz / self.refdiv as u32;\n\n        // Check reference frequency range\n        if ref_freq < 5_000_000 || ref_freq > 800_000_000 {\n            return false;\n        }\n\n        // Calculate VCO frequency\n        let vco_freq = ref_freq * self.fbdiv as u32;\n\n        // Check VCO frequency range\n        vco_freq >= VCO_MIN && vco_freq <= VCO_MAX\n    }\n}\n\n/// Reference clock config.\npub struct RefClkConfig {\n    /// Reference clock source.\n    pub src: RefClkSrc,\n    /// Reference clock divider.\n    pub div: u8,\n}\n\n/// Reference clock source.\n#[non_exhaustive]\n#[derive(Clone, Copy, Debug, PartialEq, Eq)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub enum RefClkSrc {\n    /// XOSC.\n    Xosc,\n    /// ROSC.\n    Rosc,\n    /// PLL USB.\n    PllUsb,\n    // See above re gpin handling being commented out\n    // Gpin0,\n    // Gpin1,\n}\n\n/// SYS clock source.\n#[non_exhaustive]\n#[derive(Clone, Copy, Debug, PartialEq, Eq)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub enum SysClkSrc {\n    /// REF.\n    Ref,\n    /// PLL SYS.\n    PllSys,\n    /// PLL USB.\n    PllUsb,\n    /// ROSC.\n    Rosc,\n    /// XOSC.\n    Xosc,\n    // See above re gpin handling being commented out\n    // Gpin0,\n    // Gpin1,\n}\n\n/// SYS clock config.\npub struct SysClkConfig {\n    /// SYS clock source.\n    pub src: SysClkSrc,\n    /// SYS clock divider.\n    #[cfg(feature = \"rp2040\")]\n    pub div_int: u32,\n    /// SYS clock fraction.\n    #[cfg(feature = \"rp2040\")]\n    pub div_frac: u8,\n    /// SYS clock divider.\n    #[cfg(feature = \"_rp235x\")]\n    pub div_int: u16,\n    /// SYS clock fraction.\n    #[cfg(feature = \"_rp235x\")]\n    pub div_frac: u16,\n}\n\n/// USB clock source.\n#[repr(u8)]\n#[non_exhaustive]\n#[derive(Clone, Copy, Debug, PartialEq, Eq)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub enum UsbClkSrc {\n    /// PLL USB.\n    PllUsb = ClkUsbCtrlAuxsrc::CLKSRC_PLL_USB as _,\n    /// PLL SYS.\n    PllSys = ClkUsbCtrlAuxsrc::CLKSRC_PLL_SYS as _,\n    /// ROSC.\n    Rosc = ClkUsbCtrlAuxsrc::ROSC_CLKSRC_PH as _,\n    /// XOSC.\n    Xosc = ClkUsbCtrlAuxsrc::XOSC_CLKSRC as _,\n    // See above re gpin handling being commented out\n    // Gpin0 = ClkUsbCtrlAuxsrc::CLKSRC_GPIN0 as _ ,\n    // Gpin1 = ClkUsbCtrlAuxsrc::CLKSRC_GPIN1 as _ ,\n}\n\n/// USB clock config.\npub struct UsbClkConfig {\n    /// USB clock source.\n    pub src: UsbClkSrc,\n    /// USB clock divider.\n    pub div: u8,\n    /// USB clock phase.\n    pub phase: u8,\n}\n\n/// ADC clock source.\n#[repr(u8)]\n#[non_exhaustive]\n#[derive(Clone, Copy, Debug, PartialEq, Eq)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub enum AdcClkSrc {\n    /// PLL USB.\n    PllUsb = ClkAdcCtrlAuxsrc::CLKSRC_PLL_USB as _,\n    /// PLL SYS.\n    PllSys = ClkAdcCtrlAuxsrc::CLKSRC_PLL_SYS as _,\n    /// ROSC.\n    Rosc = ClkAdcCtrlAuxsrc::ROSC_CLKSRC_PH as _,\n    /// XOSC.\n    Xosc = ClkAdcCtrlAuxsrc::XOSC_CLKSRC as _,\n    // See above re gpin handling being commented out\n    // Gpin0 = ClkAdcCtrlAuxsrc::CLKSRC_GPIN0 as _ ,\n    // Gpin1 = ClkAdcCtrlAuxsrc::CLKSRC_GPIN1 as _ ,\n}\n\n/// ADC clock config.\npub struct AdcClkConfig {\n    /// ADC clock source.\n    pub src: AdcClkSrc,\n    /// ADC clock divider.\n    pub div: u8,\n    /// ADC clock phase.\n    pub phase: u8,\n}\n\n/// RTC clock source.\n#[repr(u8)]\n#[non_exhaustive]\n#[derive(Clone, Copy, Debug, PartialEq, Eq)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\n#[cfg(feature = \"rp2040\")]\npub enum RtcClkSrc {\n    /// PLL USB.\n    PllUsb = ClkRtcCtrlAuxsrc::CLKSRC_PLL_USB as _,\n    /// PLL SYS.\n    PllSys = ClkRtcCtrlAuxsrc::CLKSRC_PLL_SYS as _,\n    /// ROSC.\n    Rosc = ClkRtcCtrlAuxsrc::ROSC_CLKSRC_PH as _,\n    /// XOSC.\n    Xosc = ClkRtcCtrlAuxsrc::XOSC_CLKSRC as _,\n    // See above re gpin handling being commented out\n    // Gpin0 = ClkRtcCtrlAuxsrc::CLKSRC_GPIN0 as _ ,\n    // Gpin1 = ClkRtcCtrlAuxsrc::CLKSRC_GPIN1 as _ ,\n}\n\n/// RTC clock config.\n#[cfg(feature = \"rp2040\")]\npub struct RtcClkConfig {\n    /// RTC clock source.\n    pub src: RtcClkSrc,\n    /// RTC clock divider.\n    pub div_int: u32,\n    /// RTC clock divider fraction.\n    pub div_frac: u8,\n    /// RTC clock phase.\n    pub phase: u8,\n}\n\n/// Find valid PLL parameters (refdiv, fbdiv, post_div1, post_div2) for a target output frequency\n/// based on the input frequency.\n///\n/// This function searches for the best PLL configuration to achieve the requested target frequency\n/// while staying within the VCO frequency range of 750MHz to 1600MHz. It prioritizes stability\n/// over exact frequency matching by using larger divisors where possible.\n///\n/// # Parameters\n///\n/// * `input_hz`: The input frequency in Hz (typically the crystal frequency, e.g. 12MHz for th most common one used on rp2040 boards)\n/// * `target_hz`: The desired output frequency in Hz (e.g. 125MHz for standard RP2040 operation)\n///\n/// # Returns\n///\n/// * `Some(PllConfig)` if valid parameters were found\n/// * `None` if no valid parameters could be found for the requested combination\n///\n/// # Example\n///\n/// ```rust,ignore\n/// // Find parameters for 133MHz system clock from 12MHz crystal\n/// let pll_params = find_pll_params(12_000_000, 133_000_000).unwrap();\n/// ```\nfn find_pll_params(input_hz: u32, target_hz: u32) -> Option<PllConfig> {\n    // Fixed reference divider for system PLL\n    const PLL_SYS_REFDIV: u8 = 1;\n\n    // Calculate reference frequency\n    let reference_freq = input_hz as u64 / PLL_SYS_REFDIV as u64;\n\n    // Start from highest fbdiv for better stability (like SDK does)\n    for fbdiv in (16..=320).rev() {\n        let vco_freq = reference_freq * fbdiv;\n\n        // Check VCO frequency is within valid range\n        if vco_freq < VCO_MIN as u64 || vco_freq > VCO_MAX as u64 {\n            continue;\n        }\n\n        // Try all possible postdiv combinations starting from larger values\n        // (more conservative/stable approach)\n        for post_div1 in (1..=7).rev() {\n            for post_div2 in (1..=post_div1).rev() {\n                let out_freq = vco_freq / (post_div1 * post_div2);\n\n                // Check if we get the exact target frequency without remainder\n                if out_freq == target_hz as u64 && (vco_freq % (post_div1 * post_div2) == 0) {\n                    return Some(PllConfig {\n                        refdiv: PLL_SYS_REFDIV,\n                        fbdiv: fbdiv as u16,\n                        post_div1: post_div1 as u8,\n                        post_div2: post_div2 as u8,\n                    });\n                }\n            }\n        }\n    }\n\n    // If we couldn't find an exact match, find the closest match\n    let mut best_config = None;\n    let mut min_diff = u32::MAX;\n\n    for fbdiv in (16..=320).rev() {\n        let vco_freq = reference_freq * fbdiv;\n\n        if vco_freq < VCO_MIN as u64 || vco_freq > VCO_MAX as u64 {\n            continue;\n        }\n\n        for post_div1 in (1..=7).rev() {\n            for post_div2 in (1..=post_div1).rev() {\n                let out_freq = (vco_freq / (post_div1 * post_div2) as u64) as u32;\n                let diff = if out_freq > target_hz {\n                    out_freq - target_hz\n                } else {\n                    target_hz - out_freq\n                };\n\n                // If this is closer to the target, save it\n                if diff < min_diff {\n                    min_diff = diff;\n                    best_config = Some(PllConfig {\n                        refdiv: PLL_SYS_REFDIV,\n                        fbdiv: fbdiv as u16,\n                        post_div1: post_div1 as u8,\n                        post_div2: post_div2 as u8,\n                    });\n                }\n            }\n        }\n    }\n\n    // Return the closest match if we found one\n    best_config\n}\n\n/// safety: must be called exactly once at bootup\npub(crate) unsafe fn init(config: ClockConfig) {\n    // Reset everything except:\n    // - QSPI (we're using it to run this code!)\n    // - PLLs (it may be suicide if that's what's clocking us)\n    // - USB, SYSCFG (breaks usb-to-swd on core1)\n    // - RTC (else there would be no more time...)\n    let mut peris = reset::ALL_PERIPHERALS;\n    peris.set_io_qspi(false);\n    // peris.set_io_bank0(false); // might be suicide if we're clocked from gpin\n    peris.set_pads_qspi(false);\n    peris.set_pll_sys(false);\n    peris.set_pll_usb(false);\n    peris.set_usbctrl(false);\n    peris.set_syscfg(false);\n    //peris.set_rtc(false);\n    reset::reset(peris);\n\n    // Disable resus that may be enabled from previous software\n    let c = pac::CLOCKS;\n    c.clk_sys_resus_ctrl()\n        .write_value(pac::clocks::regs::ClkSysResusCtrl(0));\n\n    // Before we touch PLLs, switch sys and ref cleanly away from their aux sources.\n    c.clk_sys_ctrl().modify(|w| w.set_src(ClkSysCtrlSrc::CLK_REF));\n    #[cfg(feature = \"rp2040\")]\n    while c.clk_sys_selected().read() != 1 {}\n    #[cfg(feature = \"_rp235x\")]\n    while c.clk_sys_selected().read() != pac::clocks::regs::ClkSysSelected(1) {}\n    c.clk_ref_ctrl().modify(|w| w.set_src(ClkRefCtrlSrc::ROSC_CLKSRC_PH));\n    #[cfg(feature = \"rp2040\")]\n    while c.clk_ref_selected().read() != 1 {}\n    #[cfg(feature = \"_rp235x\")]\n    while c.clk_ref_selected().read() != pac::clocks::regs::ClkRefSelected(1) {}\n\n    // Reset the PLLs\n    let mut peris = reset::Peripherals(0);\n    peris.set_pll_sys(true);\n    peris.set_pll_usb(true);\n    reset::reset(peris);\n    reset::unreset_wait(peris);\n\n    // See above re gpin handling being commented out\n    // let gpin0_freq = config.gpin0.map_or(0, |p| {\n    //     core::mem::forget(p.1);\n    //     p.0\n    // });\n    // CLOCKS.gpin0.store(gpin0_freq, Ordering::Relaxed);\n    // let gpin1_freq = config.gpin1.map_or(0, |p| {\n    //     core::mem::forget(p.1);\n    //     p.0\n    // });\n    // CLOCKS.gpin1.store(gpin1_freq, Ordering::Relaxed);\n\n    let rosc_freq = match config.rosc {\n        Some(config) => configure_rosc(config),\n        None => 0,\n    };\n    CLOCKS.rosc.store(rosc_freq, Ordering::Relaxed);\n\n    // Set Core Voltage, if we have config for it and we're not using the default\n    {\n        let voltage = config.core_voltage;\n\n        #[cfg(feature = \"rp2040\")]\n        let vreg = pac::VREG_AND_CHIP_RESET;\n        #[cfg(feature = \"_rp235x\")]\n        let vreg = pac::POWMAN;\n\n        let current_vsel = vreg.vreg().read().vsel();\n        let target_vsel = voltage as u8;\n\n        // If the target voltage is different from the current one, we need to change it\n        if target_vsel != current_vsel {\n            // Set the voltage regulator to the target voltage\n            #[cfg(feature = \"rp2040\")]\n            vreg.vreg().modify(|w| w.set_vsel(target_vsel));\n            #[cfg(feature = \"_rp235x\")]\n            // For rp235x changes to the voltage regulator are protected by a password, see datasheet section 6.4 Power Management (POWMAN) Registers\n            // The password is \"5AFE\" (0x5AFE), it must be set in the top 16 bits of the register\n            vreg.vreg().modify(|w| {\n                w.0 = (w.0 & 0x0000FFFF) | (0x5AFE << 16); // Set the password\n                w.set_vsel(target_vsel);\n                *w\n            });\n\n            // Wait for the voltage to stabilize. Use the provided delay or default based on voltage\n            let settling_time_us = config.voltage_stabilization_delay_us.unwrap_or_else(|| {\n                match voltage {\n                    CoreVoltage::V1_15 => 1000,                                           // 1ms for 1.15V\n                    CoreVoltage::V1_20 | CoreVoltage::V1_25 | CoreVoltage::V1_30 => 2000, // 2ms for higher voltages\n                    _ => 0,                                                               // no delay for all others\n                }\n            });\n\n            if settling_time_us != 0 {\n                // Delay in microseconds, using the ROSC frequency to calculate cycles\n                let cycles_per_us = rosc_freq / 1_000_000;\n                let delay_cycles = settling_time_us * cycles_per_us;\n                cortex_m::asm::delay(delay_cycles);\n            }\n\n            // Only now set the BOD level. At this point the voltage is considered stable.\n            #[cfg(feature = \"rp2040\")]\n            vreg.bod().write(|w| {\n                w.set_vsel(voltage.recommended_bod());\n                w.set_en(true); // Enable brownout detection\n            });\n            #[cfg(feature = \"_rp235x\")]\n            vreg.bod().write(|w| {\n                w.0 = (w.0 & 0x0000FFFF) | (0x5AFE << 16); // Set the password\n                w.set_vsel(voltage.recommended_bod());\n                w.set_en(true); // Enable brownout detection\n            });\n        }\n    }\n\n    let (xosc_freq, pll_sys_freq, pll_usb_freq) = match config.xosc {\n        Some(config) => {\n            // start XOSC\n            start_xosc(config.hz, config.delay_multiplier);\n\n            let pll_sys_freq = match config.sys_pll {\n                Some(sys_pll_config) => match configure_pll(pac::PLL_SYS, config.hz, sys_pll_config) {\n                    Ok(freq) => freq,\n                    Err(e) => panic!(\"Failed to configure PLL_SYS: {:?}\", e),\n                },\n                None => 0,\n            };\n            let pll_usb_freq = match config.usb_pll {\n                Some(usb_pll_config) => match configure_pll(pac::PLL_USB, config.hz, usb_pll_config) {\n                    Ok(freq) => freq,\n                    Err(e) => panic!(\"Failed to configure PLL_USB: {:?}\", e),\n                },\n                None => 0,\n            };\n\n            (config.hz, pll_sys_freq, pll_usb_freq)\n        }\n        None => (0, 0, 0),\n    };\n\n    CLOCKS.xosc.store(xosc_freq, Ordering::Relaxed);\n    CLOCKS.pll_sys.store(pll_sys_freq, Ordering::Relaxed);\n    CLOCKS.pll_usb.store(pll_usb_freq, Ordering::Relaxed);\n\n    let (ref_src, ref_aux, clk_ref_freq) = {\n        use {ClkRefCtrlAuxsrc as Aux, ClkRefCtrlSrc as Src};\n        let div = config.ref_clk.div as u32;\n        assert!(div >= 1 && div <= 4);\n        match config.ref_clk.src {\n            RefClkSrc::Xosc => (Src::XOSC_CLKSRC, Aux::CLKSRC_PLL_USB, xosc_freq / div),\n            RefClkSrc::Rosc => (Src::ROSC_CLKSRC_PH, Aux::CLKSRC_PLL_USB, rosc_freq / div),\n            RefClkSrc::PllUsb => (Src::CLKSRC_CLK_REF_AUX, Aux::CLKSRC_PLL_USB, pll_usb_freq / div),\n            // See above re gpin handling being commented out\n            // RefClkSrc::Gpin0 => (Src::CLKSRC_CLK_REF_AUX, Aux::CLKSRC_GPIN0, gpin0_freq / div),\n            // RefClkSrc::Gpin1 => (Src::CLKSRC_CLK_REF_AUX, Aux::CLKSRC_GPIN1, gpin1_freq / div),\n        }\n    };\n    assert!(clk_ref_freq != 0);\n    CLOCKS.reference.store(clk_ref_freq, Ordering::Relaxed);\n    c.clk_ref_ctrl().write(|w| {\n        w.set_src(ref_src);\n        w.set_auxsrc(ref_aux);\n    });\n    #[cfg(feature = \"rp2040\")]\n    while c.clk_ref_selected().read() != (1 << ref_src as u32) {}\n    #[cfg(feature = \"_rp235x\")]\n    while c.clk_ref_selected().read() != pac::clocks::regs::ClkRefSelected(1 << ref_src as u32) {}\n    c.clk_ref_div().write(|w| {\n        w.set_int(config.ref_clk.div);\n    });\n\n    // Configure tick generation on the 2040.\n    #[cfg(feature = \"rp2040\")]\n    pac::WATCHDOG.tick().write(|w| {\n        w.set_cycles((clk_ref_freq / 1_000_000) as u16);\n        w.set_enable(true);\n    });\n    // Configure tick generator on the 2350\n    #[cfg(feature = \"_rp235x\")]\n    {\n        let cycle_count = clk_ref_freq / 1_000_000;\n\n        pac::TICKS.timer0_cycles().write(|w| w.0 = cycle_count);\n        pac::TICKS.timer0_ctrl().write(|w| w.set_enable(true));\n\n        pac::TICKS.watchdog_cycles().write(|w| w.0 = cycle_count);\n        pac::TICKS.watchdog_ctrl().write(|w| w.set_enable(true));\n    }\n\n    let (sys_src, sys_aux, clk_sys_freq) = {\n        use {ClkSysCtrlAuxsrc as Aux, ClkSysCtrlSrc as Src};\n        let (src, aux, freq) = match config.sys_clk.src {\n            SysClkSrc::Ref => (Src::CLK_REF, Aux::CLKSRC_PLL_SYS, clk_ref_freq),\n            SysClkSrc::PllSys => (Src::CLKSRC_CLK_SYS_AUX, Aux::CLKSRC_PLL_SYS, pll_sys_freq),\n            SysClkSrc::PllUsb => (Src::CLKSRC_CLK_SYS_AUX, Aux::CLKSRC_PLL_USB, pll_usb_freq),\n            SysClkSrc::Rosc => (Src::CLKSRC_CLK_SYS_AUX, Aux::ROSC_CLKSRC, rosc_freq),\n            SysClkSrc::Xosc => (Src::CLKSRC_CLK_SYS_AUX, Aux::XOSC_CLKSRC, xosc_freq),\n            // See above re gpin handling being commented out\n            // SysClkSrc::Gpin0 => (Src::CLKSRC_CLK_SYS_AUX, Aux::CLKSRC_GPIN0, gpin0_freq),\n            // SysClkSrc::Gpin1 => (Src::CLKSRC_CLK_SYS_AUX, Aux::CLKSRC_GPIN1, gpin1_freq),\n        };\n        let div = config.sys_clk.div_int as u64 * 256 + config.sys_clk.div_frac as u64;\n        (src, aux, ((freq as u64 * 256) / div) as u32)\n    };\n    assert!(clk_sys_freq != 0);\n    CLOCKS.sys.store(clk_sys_freq, Ordering::Relaxed);\n    if sys_src != ClkSysCtrlSrc::CLK_REF {\n        c.clk_sys_ctrl().write(|w| w.set_src(ClkSysCtrlSrc::CLK_REF));\n        #[cfg(feature = \"rp2040\")]\n        while c.clk_sys_selected().read() != (1 << ClkSysCtrlSrc::CLK_REF as u32) {}\n        #[cfg(feature = \"_rp235x\")]\n        while c.clk_sys_selected().read() != pac::clocks::regs::ClkSysSelected(1 << ClkSysCtrlSrc::CLK_REF as u32) {}\n    }\n    c.clk_sys_ctrl().write(|w| {\n        w.set_auxsrc(sys_aux);\n        w.set_src(sys_src);\n    });\n\n    #[cfg(feature = \"rp2040\")]\n    while c.clk_sys_selected().read() != (1 << sys_src as u32) {}\n    #[cfg(feature = \"_rp235x\")]\n    while c.clk_sys_selected().read() != pac::clocks::regs::ClkSysSelected(1 << sys_src as u32) {}\n\n    c.clk_sys_div().write(|w| {\n        w.set_int(config.sys_clk.div_int);\n        w.set_frac(config.sys_clk.div_frac);\n    });\n\n    let mut peris = reset::ALL_PERIPHERALS;\n\n    if let Some(src) = config.peri_clk_src {\n        c.clk_peri_ctrl().write(|w| {\n            w.set_enable(true);\n            w.set_auxsrc(ClkPeriCtrlAuxsrc::from_bits(src as _));\n        });\n        let peri_freq = match src {\n            PeriClkSrc::Sys => clk_sys_freq,\n            PeriClkSrc::PllSys => pll_sys_freq,\n            PeriClkSrc::PllUsb => pll_usb_freq,\n            PeriClkSrc::Rosc => rosc_freq,\n            PeriClkSrc::Xosc => xosc_freq,\n            // See above re gpin handling being commented out\n            // PeriClkSrc::Gpin0 => gpin0_freq,\n            // PeriClkSrc::Gpin1 => gpin1_freq,\n        };\n        assert!(peri_freq != 0);\n        CLOCKS.peri.store(peri_freq, Ordering::Relaxed);\n    } else {\n        peris.set_spi0(false);\n        peris.set_spi1(false);\n        peris.set_uart0(false);\n        peris.set_uart1(false);\n        CLOCKS.peri.store(0, Ordering::Relaxed);\n    }\n\n    if let Some(conf) = config.usb_clk {\n        c.clk_usb_div().write(|w| w.set_int(conf.div));\n        c.clk_usb_ctrl().write(|w| {\n            w.set_phase(conf.phase);\n            w.set_enable(true);\n            w.set_auxsrc(ClkUsbCtrlAuxsrc::from_bits(conf.src as _));\n        });\n        let usb_freq = match conf.src {\n            UsbClkSrc::PllUsb => pll_usb_freq,\n            UsbClkSrc::PllSys => pll_sys_freq,\n            UsbClkSrc::Rosc => rosc_freq,\n            UsbClkSrc::Xosc => xosc_freq,\n            // See above re gpin handling being commented out\n            // UsbClkSrc::Gpin0 => gpin0_freq,\n            // UsbClkSrc::Gpin1 => gpin1_freq,\n        };\n        assert!(usb_freq != 0);\n        assert!(conf.div >= 1 && conf.div <= 4);\n        CLOCKS.usb.store(usb_freq / conf.div as u32, Ordering::Relaxed);\n    } else {\n        peris.set_usbctrl(false);\n        CLOCKS.usb.store(0, Ordering::Relaxed);\n    }\n\n    if let Some(conf) = config.adc_clk {\n        c.clk_adc_div().write(|w| w.set_int(conf.div));\n        c.clk_adc_ctrl().write(|w| {\n            w.set_phase(conf.phase);\n            w.set_enable(true);\n            w.set_auxsrc(ClkAdcCtrlAuxsrc::from_bits(conf.src as _));\n        });\n        let adc_in_freq = match conf.src {\n            AdcClkSrc::PllUsb => pll_usb_freq,\n            AdcClkSrc::PllSys => pll_sys_freq,\n            AdcClkSrc::Rosc => rosc_freq,\n            AdcClkSrc::Xosc => xosc_freq,\n            // See above re gpin handling being commented out\n            // AdcClkSrc::Gpin0 => gpin0_freq,\n            // AdcClkSrc::Gpin1 => gpin1_freq,\n        };\n        assert!(adc_in_freq != 0);\n        assert!(conf.div >= 1 && conf.div <= 4);\n        CLOCKS.adc.store(adc_in_freq / conf.div as u32, Ordering::Relaxed);\n    } else {\n        peris.set_adc(false);\n        CLOCKS.adc.store(0, Ordering::Relaxed);\n    }\n\n    // rp2040 specific clocks\n    #[cfg(feature = \"rp2040\")]\n    if let Some(conf) = config.rtc_clk {\n        c.clk_rtc_div().write(|w| {\n            w.set_int(conf.div_int);\n            w.set_frac(conf.div_frac);\n        });\n        c.clk_rtc_ctrl().write(|w| {\n            w.set_phase(conf.phase);\n            w.set_enable(true);\n            w.set_auxsrc(ClkRtcCtrlAuxsrc::from_bits(conf.src as _));\n        });\n        let rtc_in_freq = match conf.src {\n            RtcClkSrc::PllUsb => pll_usb_freq,\n            RtcClkSrc::PllSys => pll_sys_freq,\n            RtcClkSrc::Rosc => rosc_freq,\n            RtcClkSrc::Xosc => xosc_freq,\n            // See above re gpin handling being commented out\n            // RtcClkSrc::Gpin0 => gpin0_freq,\n            // RtcClkSrc::Gpin1 => gpin1_freq,\n        };\n        assert!(rtc_in_freq != 0);\n        assert!(config.sys_clk.div_int <= 0x1000000);\n        CLOCKS.rtc.store(\n            ((rtc_in_freq as u64 * 256) / (conf.div_int as u64 * 256 + conf.div_frac as u64)) as u16,\n            Ordering::Relaxed,\n        );\n    } else {\n        peris.set_rtc(false);\n        CLOCKS.rtc.store(0, Ordering::Relaxed);\n    }\n\n    // rp235x specific clocks\n    #[cfg(feature = \"_rp235x\")]\n    {\n        // TODO hstx clock\n        peris.set_hstx(false);\n    }\n\n    // Peripheral clocks should now all be running\n    reset::unreset_wait(peris);\n}\n\nfn configure_rosc(config: RoscConfig) -> u32 {\n    let p = pac::ROSC;\n\n    p.freqa().write(|w| {\n        w.set_passwd(pac::rosc::vals::Passwd::PASS);\n        w.set_ds0(config.drive_strength[0]);\n        w.set_ds1(config.drive_strength[1]);\n        w.set_ds2(config.drive_strength[2]);\n        w.set_ds3(config.drive_strength[3]);\n    });\n\n    p.freqb().write(|w| {\n        w.set_passwd(pac::rosc::vals::Passwd::PASS);\n        w.set_ds4(config.drive_strength[4]);\n        w.set_ds5(config.drive_strength[5]);\n        w.set_ds6(config.drive_strength[6]);\n        w.set_ds7(config.drive_strength[7]);\n    });\n\n    p.div().write(|w| {\n        w.set_div(pac::rosc::vals::Div(config.div + pac::rosc::vals::Div::PASS.0));\n    });\n\n    p.ctrl().write(|w| {\n        w.set_enable(pac::rosc::vals::Enable::ENABLE);\n        w.set_freq_range(pac::rosc::vals::FreqRange(config.range as u16));\n    });\n\n    config.hz\n}\n\n/// ROSC clock frequency.\npub fn rosc_freq() -> u32 {\n    CLOCKS.rosc.load(Ordering::Relaxed)\n}\n\n/// XOSC clock frequency.\npub fn xosc_freq() -> u32 {\n    CLOCKS.xosc.load(Ordering::Relaxed)\n}\n\n// See above re gpin handling being commented out\n// pub fn gpin0_freq() -> u32 {\n//     CLOCKS.gpin0.load(Ordering::Relaxed)\n// }\n// pub fn gpin1_freq() -> u32 {\n//     CLOCKS.gpin1.load(Ordering::Relaxed)\n// }\n\n/// PLL SYS clock frequency.\npub fn pll_sys_freq() -> u32 {\n    CLOCKS.pll_sys.load(Ordering::Relaxed)\n}\n\n/// PLL USB clock frequency.\npub fn pll_usb_freq() -> u32 {\n    CLOCKS.pll_usb.load(Ordering::Relaxed)\n}\n\n/// SYS clock frequency.\npub fn clk_sys_freq() -> u32 {\n    CLOCKS.sys.load(Ordering::Relaxed)\n}\n\n/// REF clock frequency.\npub fn clk_ref_freq() -> u32 {\n    CLOCKS.reference.load(Ordering::Relaxed)\n}\n\n/// Peripheral clock frequency.\npub fn clk_peri_freq() -> u32 {\n    CLOCKS.peri.load(Ordering::Relaxed)\n}\n\n/// USB clock frequency.\npub fn clk_usb_freq() -> u32 {\n    CLOCKS.usb.load(Ordering::Relaxed)\n}\n\n/// ADC clock frequency.\npub fn clk_adc_freq() -> u32 {\n    CLOCKS.adc.load(Ordering::Relaxed)\n}\n\n/// RTC clock frequency.\n#[cfg(feature = \"rp2040\")]\npub fn clk_rtc_freq() -> u16 {\n    CLOCKS.rtc.load(Ordering::Relaxed)\n}\n\n/// The core voltage of the chip.\n///\n/// Returns the current core voltage or an error if the voltage register\n/// contains an unknown value.\npub fn core_voltage() -> Result<CoreVoltage, ClockError> {\n    #[cfg(feature = \"rp2040\")]\n    {\n        let vreg = pac::VREG_AND_CHIP_RESET;\n        let vsel = vreg.vreg().read().vsel();\n        match vsel {\n            0b0000 => Ok(CoreVoltage::V0_80),\n            0b0110 => Ok(CoreVoltage::V0_85),\n            0b0111 => Ok(CoreVoltage::V0_90),\n            0b1000 => Ok(CoreVoltage::V0_95),\n            0b1001 => Ok(CoreVoltage::V1_00),\n            0b1010 => Ok(CoreVoltage::V1_05),\n            0b1011 => Ok(CoreVoltage::V1_10),\n            0b1100 => Ok(CoreVoltage::V1_15),\n            0b1101 => Ok(CoreVoltage::V1_20),\n            0b1110 => Ok(CoreVoltage::V1_25),\n            0b1111 => Ok(CoreVoltage::V1_30),\n            _ => Err(ClockError::UnexpectedCoreVoltageRead),\n        }\n    }\n\n    #[cfg(feature = \"_rp235x\")]\n    {\n        let vreg = pac::POWMAN;\n        let vsel = vreg.vreg().read().vsel();\n        match vsel {\n            0b00000 => Ok(CoreVoltage::V0_55),\n            0b00001 => Ok(CoreVoltage::V0_60),\n            0b00010 => Ok(CoreVoltage::V0_65),\n            0b00011 => Ok(CoreVoltage::V0_70),\n            0b00100 => Ok(CoreVoltage::V0_75),\n            0b00101 => Ok(CoreVoltage::V0_80),\n            0b00110 => Ok(CoreVoltage::V0_85),\n            0b00111 => Ok(CoreVoltage::V0_90),\n            0b01000 => Ok(CoreVoltage::V0_95),\n            0b01001 => Ok(CoreVoltage::V1_00),\n            0b01010 => Ok(CoreVoltage::V1_05),\n            0b01011 => Ok(CoreVoltage::V1_10),\n            0b01100 => Ok(CoreVoltage::V1_15),\n            0b01101 => Ok(CoreVoltage::V1_20),\n            0b01110 => Ok(CoreVoltage::V1_25),\n            0b01111 => Ok(CoreVoltage::V1_30),\n            _ => Err(ClockError::UnexpectedCoreVoltageRead),\n            // see CoreVoltage: we do not support setting Voltages higher than 1.30V at this point\n        }\n    }\n}\n\nfn start_xosc(crystal_hz: u32, delay_multiplier: u32) {\n    let startup_delay = (((crystal_hz / 1000) * delay_multiplier) + 128) / 256;\n    pac::XOSC.startup().write(|w| w.set_delay(startup_delay as u16));\n    pac::XOSC.ctrl().write(|w| {\n        w.set_freq_range(pac::xosc::vals::CtrlFreqRange::_1_15MHZ);\n        w.set_enable(pac::xosc::vals::Enable::ENABLE);\n    });\n    while !pac::XOSC.status().read().stable() {}\n}\n\n/// PLL (Phase-Locked Loop) configuration\n#[inline(always)]\nfn configure_pll(p: pac::pll::Pll, input_freq: u32, config: PllConfig) -> Result<u32, ClockError> {\n    // Calculate reference frequency\n    let ref_freq = input_freq / config.refdiv as u32;\n\n    // Validate PLL parameters\n    // Feedback divider (FBDIV) must be between 16 and 320\n    assert!(config.fbdiv >= 16 && config.fbdiv <= 320);\n\n    // Post divider 1 (POSTDIV1) must be between 1 and 7\n    assert!(config.post_div1 >= 1 && config.post_div1 <= 7);\n\n    // Post divider 2 (POSTDIV2) must be between 1 and 7\n    assert!(config.post_div2 >= 1 && config.post_div2 <= 7);\n\n    // Post divider 2 (POSTDIV2) must be less than or equal to post divider 1 (POSTDIV1)\n    assert!(config.post_div2 <= config.post_div1);\n\n    // Reference divider (REFDIV) must be between 1 and 63\n    assert!(config.refdiv >= 1 && config.refdiv <= 63);\n\n    // Reference frequency (REF_FREQ) must be between 5MHz and 800MHz\n    assert!(ref_freq >= 5_000_000 && ref_freq <= 800_000_000);\n\n    // Calculate VCO frequency\n    let vco_freq = ref_freq.saturating_mul(config.fbdiv as u32);\n\n    // VCO (Voltage Controlled Oscillator) frequency must be between 750MHz and 1600MHz\n    assert!(vco_freq >= VCO_MIN && vco_freq <= VCO_MAX);\n\n    // We follow the SDK's approach to PLL configuration which is:\n    // 1. Power down PLL\n    // 2. Configure the reference divider\n    // 3. Configure the feedback divider\n    // 4. Power up PLL and VCO\n    // 5. Wait for PLL to lock\n    // 6. Configure post-dividers\n    // 7. Enable post-divider output\n\n    // 1. Power down PLL before configuration\n    p.pwr().write(|w| {\n        w.set_pd(true); // Power down the PLL\n        w.set_vcopd(true); // Power down the VCO\n        w.set_postdivpd(true); // Power down the post divider\n        w.set_dsmpd(true); // Disable fractional mode\n        *w\n    });\n\n    // Short delay after powering down\n    cortex_m::asm::delay(10);\n\n    // 2. Configure reference divider first\n    p.cs().write(|w| w.set_refdiv(config.refdiv as _));\n\n    // 3. Configure feedback divider\n    p.fbdiv_int().write(|w| w.set_fbdiv_int(config.fbdiv));\n\n    // 4. Power up PLL and VCO, but keep post divider powered down during initial lock\n    p.pwr().write(|w| {\n        w.set_pd(false); // Power up the PLL\n        w.set_vcopd(false); // Power up the VCO\n        w.set_postdivpd(true); // Keep post divider powered down during initial lock\n        w.set_dsmpd(true); // Disable fractional mode (simpler configuration)\n        *w\n    });\n\n    // 5. Wait for PLL to lock with a timeout\n    let mut timeout = 1_000_000;\n    while !p.cs().read().lock() {\n        timeout -= 1;\n        if timeout == 0 {\n            // PLL failed to lock, return 0 to indicate failure\n            return Err(ClockError::PllLockTimedOut);\n        }\n    }\n\n    // 6. Configure post dividers after PLL is locked\n    p.prim().write(|w| {\n        w.set_postdiv1(config.post_div1);\n        w.set_postdiv2(config.post_div2);\n    });\n\n    // 7. Enable the post divider output\n    p.pwr().modify(|w| {\n        w.set_postdivpd(false); // Power up post divider\n        *w\n    });\n\n    // Final delay to ensure everything is stable\n    cortex_m::asm::delay(100);\n\n    // Calculate and return actual output frequency\n    Ok(vco_freq / ((config.post_div1 * config.post_div2) as u32))\n}\n\n/// General purpose input clock pin.\npub trait GpinPin: crate::gpio::Pin {\n    /// Pin number.\n    const NR: usize;\n}\n\nmacro_rules! impl_gpinpin {\n    ($name:ident, $gpin_num:expr) => {\n        impl GpinPin for crate::peripherals::$name {\n            const NR: usize = $gpin_num;\n        }\n    };\n}\n\n#[cfg(feature = \"_rp235x\")]\nimpl_gpinpin!(PIN_12, 0);\n#[cfg(feature = \"_rp235x\")]\nimpl_gpinpin!(PIN_14, 1);\n\nimpl_gpinpin!(PIN_20, 0);\nimpl_gpinpin!(PIN_22, 1);\n\n/// General purpose clock input driver.\npub struct Gpin<'d, T: GpinPin> {\n    gpin: Peri<'d, AnyPin>,\n    _phantom: PhantomData<T>,\n}\n\nimpl<'d, T: GpinPin> Gpin<'d, T> {\n    /// Create new gpin driver.\n    pub fn new(gpin: Peri<'d, T>) -> Self {\n        #[cfg(feature = \"rp2040\")]\n        gpin.gpio().ctrl().write(|w| w.set_funcsel(0x08));\n\n        // On RP2350 GPIN changed from F8 toF9\n        #[cfg(feature = \"_rp235x\")]\n        gpin.gpio().ctrl().write(|w| w.set_funcsel(0x09));\n\n        #[cfg(feature = \"_rp235x\")]\n        gpin.pad_ctrl().write(|w| {\n            w.set_iso(false);\n        });\n\n        Gpin {\n            gpin: gpin.into(),\n            _phantom: PhantomData,\n        }\n    }\n}\n\nimpl<'d, T: GpinPin> Drop for Gpin<'d, T> {\n    fn drop(&mut self) {\n        self.gpin.pad_ctrl().write(|_| {});\n        self.gpin\n            .gpio()\n            .ctrl()\n            .write(|w| w.set_funcsel(pac::io::vals::Gpio0ctrlFuncsel::NULL as _));\n    }\n}\n\n/// General purpose clock output pin.\npub trait GpoutPin: crate::gpio::Pin {\n    /// Pin number.\n    fn number(&self) -> usize;\n}\n\nmacro_rules! impl_gpoutpin {\n    ($name:ident, $gpout_num:expr) => {\n        impl GpoutPin for crate::peripherals::$name {\n            fn number(&self) -> usize {\n                $gpout_num\n            }\n        }\n    };\n}\n\n#[cfg(feature = \"_rp235x\")]\nimpl_gpoutpin!(PIN_13, 0);\n#[cfg(feature = \"_rp235x\")]\nimpl_gpoutpin!(PIN_15, 1);\n\nimpl_gpoutpin!(PIN_21, 0);\nimpl_gpoutpin!(PIN_23, 1);\nimpl_gpoutpin!(PIN_24, 2);\nimpl_gpoutpin!(PIN_25, 3);\n\n/// Gpout clock source.\n#[repr(u8)]\npub enum GpoutSrc {\n    /// Sys PLL.\n    PllSys = ClkGpoutCtrlAuxsrc::CLKSRC_PLL_SYS as _,\n    // See above re gpin handling being commented out\n    // Gpin0 = ClkGpoutCtrlAuxsrc::CLKSRC_GPIN0 as _ ,\n    // Gpin1 = ClkGpoutCtrlAuxsrc::CLKSRC_GPIN1 as _ ,\n    /// USB PLL.\n    PllUsb = ClkGpoutCtrlAuxsrc::CLKSRC_PLL_USB as _,\n    /// ROSC.\n    Rosc = ClkGpoutCtrlAuxsrc::ROSC_CLKSRC as _,\n    /// XOSC.\n    Xosc = ClkGpoutCtrlAuxsrc::XOSC_CLKSRC as _,\n    /// LPOSC.\n    #[cfg(feature = \"_rp235x\")]\n    Lposc = ClkGpoutCtrlAuxsrc::LPOSC_CLKSRC as _,\n    /// SYS.\n    Sys = ClkGpoutCtrlAuxsrc::CLK_SYS as _,\n    /// USB.\n    Usb = ClkGpoutCtrlAuxsrc::CLK_USB as _,\n    /// ADC.\n    Adc = ClkGpoutCtrlAuxsrc::CLK_ADC as _,\n    /// RTC.\n    #[cfg(feature = \"rp2040\")]\n    Rtc = ClkGpoutCtrlAuxsrc::CLK_RTC as _,\n    /// REF.\n    Ref = ClkGpoutCtrlAuxsrc::CLK_REF as _,\n}\n\n/// General purpose clock output driver.\npub struct Gpout<'d, T: GpoutPin> {\n    gpout: Peri<'d, T>,\n}\n\nimpl<'d, T: GpoutPin> Gpout<'d, T> {\n    /// Create new general purpose clock output.\n    pub fn new(gpout: Peri<'d, T>) -> Self {\n        #[cfg(feature = \"rp2040\")]\n        gpout.gpio().ctrl().write(|w| w.set_funcsel(0x08));\n\n        // On RP2350 GPOUT changed from F8 toF9\n        #[cfg(feature = \"_rp235x\")]\n        gpout.gpio().ctrl().write(|w| w.set_funcsel(0x09));\n\n        #[cfg(feature = \"_rp235x\")]\n        gpout.pad_ctrl().write(|w| {\n            w.set_iso(false);\n        });\n\n        Self { gpout }\n    }\n\n    /// Set clock divider.\n    #[cfg(feature = \"rp2040\")]\n    pub fn set_div(&self, int: u32, frac: u8) {\n        let c = pac::CLOCKS;\n        c.clk_gpout_div(self.gpout.number()).write(|w| {\n            w.set_int(int);\n            w.set_frac(frac);\n        });\n    }\n\n    /// Set clock divider.\n    #[cfg(feature = \"_rp235x\")]\n    pub fn set_div(&self, int: u16, frac: u16) {\n        let c = pac::CLOCKS;\n        c.clk_gpout_div(self.gpout.number()).write(|w| {\n            w.set_int(int);\n            w.set_frac(frac);\n        });\n    }\n\n    /// Set clock source.\n    pub fn set_src(&self, src: GpoutSrc) {\n        let c = pac::CLOCKS;\n        c.clk_gpout_ctrl(self.gpout.number()).modify(|w| {\n            w.set_auxsrc(ClkGpoutCtrlAuxsrc::from_bits(src as _));\n        });\n    }\n\n    /// Enable clock.\n    pub fn enable(&self) {\n        let c = pac::CLOCKS;\n        c.clk_gpout_ctrl(self.gpout.number()).modify(|w| {\n            w.set_enable(true);\n        });\n    }\n\n    /// Disable clock.\n    pub fn disable(&self) {\n        let c = pac::CLOCKS;\n        c.clk_gpout_ctrl(self.gpout.number()).modify(|w| {\n            w.set_enable(false);\n        });\n    }\n\n    /// Clock frequency.\n    pub fn get_freq(&self) -> u32 {\n        let c = pac::CLOCKS;\n        let src = c.clk_gpout_ctrl(self.gpout.number()).read().auxsrc();\n\n        let base = match src {\n            ClkGpoutCtrlAuxsrc::CLKSRC_PLL_SYS => pll_sys_freq(),\n            // See above re gpin handling being commented out\n            // ClkGpoutCtrlAuxsrc::CLKSRC_GPIN0 => gpin0_freq(),\n            // ClkGpoutCtrlAuxsrc::CLKSRC_GPIN1 => gpin1_freq(),\n            ClkGpoutCtrlAuxsrc::CLKSRC_PLL_USB => pll_usb_freq(),\n            ClkGpoutCtrlAuxsrc::ROSC_CLKSRC => rosc_freq(),\n            ClkGpoutCtrlAuxsrc::XOSC_CLKSRC => xosc_freq(),\n            ClkGpoutCtrlAuxsrc::CLK_SYS => clk_sys_freq(),\n            ClkGpoutCtrlAuxsrc::CLK_USB => clk_usb_freq(),\n            ClkGpoutCtrlAuxsrc::CLK_ADC => clk_adc_freq(),\n            ClkGpoutCtrlAuxsrc::CLK_REF => clk_ref_freq(),\n            _ => unreachable!(),\n        };\n\n        let div = c.clk_gpout_div(self.gpout.number()).read();\n        let int = if div.int() == 0 { 0xFFFF } else { div.int() } as u64;\n        let frac = div.frac() as u64;\n\n        ((base as u64 * 256) / (int * 256 + frac)) as u32\n    }\n}\n\nimpl<'d, T: GpoutPin> Drop for Gpout<'d, T> {\n    fn drop(&mut self) {\n        self.disable();\n        self.gpout.pad_ctrl().write(|_| {});\n        self.gpout\n            .gpio()\n            .ctrl()\n            .write(|w| w.set_funcsel(pac::io::vals::Gpio0ctrlFuncsel::NULL as _));\n    }\n}\n\n/// Random number generator based on the ROSC RANDOMBIT register.\n///\n/// This will not produce random values if the ROSC is stopped or run at some\n/// harmonic of the bus frequency. With default clock settings these are not\n/// issues.\npub struct RoscRng;\n\nimpl RoscRng {\n    /// Get a random u8\n    pub fn next_u8() -> u8 {\n        let random_reg = pac::ROSC.randombit();\n        let mut acc = 0;\n        for _ in 0..u8::BITS {\n            acc <<= 1;\n            acc |= random_reg.read().randombit() as u8;\n        }\n        acc\n    }\n\n    /// Get a random u32\n    pub fn next_u32(&mut self) -> u32 {\n        rand_core_09::impls::next_u32_via_fill(self)\n    }\n\n    /// Get a random u64\n    pub fn next_u64(&mut self) -> u64 {\n        rand_core_09::impls::next_u64_via_fill(self)\n    }\n\n    /// Fill a slice with random bytes\n    pub fn fill_bytes(&mut self, dest: &mut [u8]) {\n        dest.fill_with(Self::next_u8)\n    }\n}\n\nimpl rand_core_06::RngCore for RoscRng {\n    fn next_u32(&mut self) -> u32 {\n        self.next_u32()\n    }\n\n    fn next_u64(&mut self) -> u64 {\n        self.next_u64()\n    }\n\n    fn fill_bytes(&mut self, dest: &mut [u8]) {\n        self.fill_bytes(dest);\n    }\n\n    fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), rand_core_06::Error> {\n        self.fill_bytes(dest);\n        Ok(())\n    }\n}\n\nimpl rand_core_06::CryptoRng for RoscRng {}\n\nimpl rand_core_09::RngCore for RoscRng {\n    fn next_u32(&mut self) -> u32 {\n        self.next_u32()\n    }\n\n    fn next_u64(&mut self) -> u64 {\n        self.next_u64()\n    }\n\n    fn fill_bytes(&mut self, dest: &mut [u8]) {\n        self.fill_bytes(dest);\n    }\n}\n\nimpl rand_core_09::CryptoRng for RoscRng {}\n\n/// Enter the `DORMANT` sleep state. This will stop *all* internal clocks\n/// and can only be exited through resets, dormant-wake GPIO interrupts,\n/// and RTC interrupts. If RTC is clocked from an internal clock source\n/// it will be stopped and not function as a wakeup source.\n#[cfg(all(target_arch = \"arm\"))]\npub fn dormant_sleep() {\n    struct Set<T: Copy, F: Fn()>(Reg<T, RW>, T, F);\n\n    impl<T: Copy, F: Fn()> Drop for Set<T, F> {\n        fn drop(&mut self) {\n            self.0.write_value(self.1);\n            self.2();\n        }\n    }\n\n    fn set_with_post_restore<T: Copy, After: Fn(), F: FnOnce(&mut T) -> After>(\n        reg: Reg<T, RW>,\n        f: F,\n    ) -> Set<T, impl Fn()> {\n        reg.modify(|w| {\n            let old = *w;\n            let after = f(w);\n            Set(reg, old, after)\n        })\n    }\n\n    fn set<T: Copy, F: FnOnce(&mut T)>(reg: Reg<T, RW>, f: F) -> Set<T, impl Fn()> {\n        set_with_post_restore(reg, |r| {\n            f(r);\n            || ()\n        })\n    }\n\n    // disable all clocks that are not vital in preparation for disabling clock sources.\n    // we'll keep gpout and rtc clocks untouched, gpout because we don't care about them\n    // and rtc because it's a possible wakeup source. if clk_rtc is not configured for\n    // gpin we'll never wake from rtc, but that's what the user asked for then.\n    let _stop_adc = set(pac::CLOCKS.clk_adc_ctrl(), |w| w.set_enable(false));\n    let _stop_usb = set(pac::CLOCKS.clk_usb_ctrl(), |w| w.set_enable(false));\n    let _stop_peri = set(pac::CLOCKS.clk_peri_ctrl(), |w| w.set_enable(false));\n    // set up rosc. we could ask the user to tell us which clock source to wake from like\n    // the C SDK does, but that seems rather unfriendly. we *may* disturb rtc by changing\n    // rosc configuration if it's currently the rtc clock source, so we'll configure rosc\n    // to the slowest frequency to minimize that impact.\n    let _configure_rosc = (\n        set(pac::ROSC.ctrl(), |w| {\n            w.set_enable(pac::rosc::vals::Enable::ENABLE);\n            w.set_freq_range(pac::rosc::vals::FreqRange::LOW);\n        }),\n        // div=32\n        set(pac::ROSC.div(), |w| w.set_div(pac::rosc::vals::Div(0xaa0))),\n    );\n    while !pac::ROSC.status().read().stable() {}\n    // switch over to rosc as the system clock source. this will change clock sources for\n    // watchdog and timer clocks, but timers won't be a concern and the watchdog won't\n    // speed up by enough to worry about (unless it's clocked from gpin, which we don't\n    // support anyway).\n    let _switch_clk_ref = set(pac::CLOCKS.clk_ref_ctrl(), |w| {\n        w.set_src(pac::clocks::vals::ClkRefCtrlSrc::ROSC_CLKSRC_PH);\n    });\n    let _switch_clk_sys = set(pac::CLOCKS.clk_sys_ctrl(), |w| {\n        w.set_src(pac::clocks::vals::ClkSysCtrlSrc::CLK_REF);\n    });\n    // oscillator dormancy does not power down plls, we have to do that ourselves. we'll\n    // restore them to their prior glory when woken though since the system may be clocked\n    // from either (and usb/adc will probably need the USB PLL anyway)\n    let _stop_pll_sys = set_with_post_restore(pac::PLL_SYS.pwr(), |w| {\n        let wake = !w.pd() && !w.vcopd();\n        w.set_pd(true);\n        w.set_vcopd(true);\n        move || while wake && !pac::PLL_SYS.cs().read().lock() {}\n    });\n    let _stop_pll_usb = set_with_post_restore(pac::PLL_USB.pwr(), |w| {\n        let wake = !w.pd() && !w.vcopd();\n        w.set_pd(true);\n        w.set_vcopd(true);\n        move || while wake && !pac::PLL_USB.cs().read().lock() {}\n    });\n    // dormancy only stops the oscillator we're telling to go dormant, the other remains\n    // running. nothing can use xosc at this point any more. not doing this costs an 200µA.\n    let _stop_xosc = set_with_post_restore(pac::XOSC.ctrl(), |w| {\n        let wake = w.enable() == pac::xosc::vals::Enable::ENABLE;\n        if wake {\n            w.set_enable(pac::xosc::vals::Enable::DISABLE);\n        }\n        move || while wake && !pac::XOSC.status().read().stable() {}\n    });\n    let _power_down_xip_cache = set(pac::XIP_CTRL.ctrl(), |w| w.set_power_down(true));\n\n    // only power down memory if we're running from XIP (or ROM? how?).\n    // powering down memory otherwise would require a lot of exacting checks that\n    // are better done by the user in a local copy of this function.\n    // powering down memories saves ~100µA, so it's well worth doing.\n    unsafe {\n        let is_in_flash = {\n            // we can't rely on the address of this function as rust sees it since linker\n            // magic or even boot2 may place it into ram.\n            let pc: usize;\n            asm!(\n                \"mov {pc}, pc\",\n                pc = out (reg) pc\n            );\n            pc < 0x20000000\n        };\n        if is_in_flash {\n            // we will be powering down memories, so we must be *absolutely*\n            // certain that we're running entirely from XIP and registers until\n            // memories are powered back up again. accessing memory that's powered\n            // down may corrupt memory contents (see section 2.11.4 of the manual).\n            // additionally a 20ns wait time is needed after powering up memories\n            // again. rosc is likely to run at only a few MHz at most, so the\n            // inter-instruction delay alone will be enough to satisfy this bound.\n            asm!(\n                \"ldr {old_mem}, [{mempowerdown}]\",\n                \"str {power_down_mems}, [{mempowerdown}]\",\n                \"str {coma}, [{dormant}]\",\n                \"str {old_mem}, [{mempowerdown}]\",\n                old_mem = out (reg) _,\n                mempowerdown = in (reg) pac::SYSCFG.mempowerdown().as_ptr(),\n                power_down_mems = in (reg) 0b11111111,\n                dormant = in (reg) pac::ROSC.dormant().as_ptr(),\n                coma = in (reg) 0x636f6d61,\n            );\n        } else {\n            pac::ROSC.dormant().write_value(rp_pac::rosc::regs::Dormant(0x636f6d61));\n        }\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n\n    #[cfg(feature = \"rp2040\")]\n    #[test]\n    fn test_find_pll_params() {\n        #[cfg(feature = \"rp2040\")]\n        {\n            // Test standard 125 MHz configuration with 12 MHz crystal\n            let params = find_pll_params(12_000_000, 125_000_000).unwrap();\n            assert_eq!(params.refdiv, 1);\n            assert_eq!(params.fbdiv, 125);\n\n            // Test USB PLL configuration for 48MHz\n            // The algorithm may find different valid parameters than the SDK defaults\n            // We'll check that it generates a valid configuration that produces 48MHz\n            let params = find_pll_params(12_000_000, 48_000_000).unwrap();\n            assert_eq!(params.refdiv, 1);\n\n            // Calculate the actual output frequency\n            let ref_freq = 12_000_000 / params.refdiv as u32;\n            let vco_freq = ref_freq as u64 * params.fbdiv as u64;\n            let output_freq = (vco_freq / ((params.post_div1 * params.post_div2) as u64)) as u32;\n\n            // Verify the output frequency is correct\n            assert_eq!(output_freq, 48_000_000);\n\n            // Verify VCO frequency is in valid range\n            assert!(vco_freq >= VCO_MIN as u64 && vco_freq <= VCO_MAX as u64);\n\n            // Test overclocked configuration for 200 MHz\n            let params = find_pll_params(12_000_000, 200_000_000).unwrap();\n            assert_eq!(params.refdiv, 1);\n            let vco_freq = 12_000_000 as u64 * params.fbdiv as u64;\n            let output_freq = (vco_freq / ((params.post_div1 * params.post_div2) as u64)) as u32;\n            assert_eq!(output_freq, 200_000_000);\n            assert!(vco_freq >= VCO_MIN as u64 && vco_freq <= VCO_MAX as u64); // VCO in valid range\n\n            // Test non-standard crystal with 16 MHz\n            let params = find_pll_params(16_000_000, 125_000_000).unwrap();\n            let vco_freq = (16_000_000 / params.refdiv as u32) as u64 * params.fbdiv as u64;\n            let output_freq = (vco_freq / ((params.post_div1 * params.post_div2) as u64)) as u32;\n\n            // Test non-standard crystal with 15 MHz\n            let params = find_pll_params(15_000_000, 125_000_000).unwrap();\n            let vco_freq = (15_000_000 / params.refdiv as u32) as u64 * params.fbdiv as u64;\n            let output_freq = (vco_freq / ((params.post_div1 * params.post_div2) as u64)) as u32;\n\n            // With a 15 MHz crystal, we might not get exactly 125 MHz\n            // Check that it's close enough (within 0.2% margin)\n            let freq_diff = if output_freq > 125_000_000 {\n                output_freq - 125_000_000\n            } else {\n                125_000_000 - output_freq\n            };\n            let error_percentage = (freq_diff as f64 / 125_000_000.0) * 100.0;\n            assert!(\n                error_percentage < 0.2,\n                \"Output frequency {} is not close enough to target 125 MHz. Error: {:.2}%\",\n                output_freq,\n                error_percentage\n            );\n\n            assert!(vco_freq >= VCO_MIN as u64 && vco_freq <= VCO_MAX as u64);\n        }\n    }\n\n    #[cfg(feature = \"rp2040\")]\n    #[test]\n    fn test_pll_config_validation() {\n        // Test PLL configuration validation logic\n        let valid_config = PllConfig {\n            refdiv: 1,\n            fbdiv: 125,\n            post_div1: 6,\n            post_div2: 2,\n        };\n\n        // Valid configuration should pass validation\n        assert!(valid_config.is_valid(12_000_000));\n\n        // Test fbdiv constraints\n        let mut invalid_config = valid_config;\n        invalid_config.fbdiv = 15; // Below minimum of 16\n        assert!(!invalid_config.is_valid(12_000_000));\n\n        invalid_config.fbdiv = 321; // Above maximum of 320\n        assert!(!invalid_config.is_valid(12_000_000));\n\n        // Test post_div constraints\n        invalid_config = valid_config;\n        invalid_config.post_div1 = 0; // Below minimum of 1\n        assert!(!invalid_config.is_valid(12_000_000));\n\n        invalid_config = valid_config;\n        invalid_config.post_div1 = 8; // Above maximum of 7\n        assert!(!invalid_config.is_valid(12_000_000));\n\n        // Test post_div2 must be <= post_div1\n        invalid_config = valid_config;\n        invalid_config.post_div2 = 7;\n        invalid_config.post_div1 = 3;\n        assert!(!invalid_config.is_valid(12_000_000));\n\n        // Test reference frequency constraints\n        invalid_config = valid_config;\n        assert!(!invalid_config.is_valid(4_000_000)); // Below minimum of 5 MHz\n        assert!(!invalid_config.is_valid(900_000_000)); // Above maximum of 800 MHz\n\n        // Test VCO frequency constraints\n        invalid_config = valid_config;\n        invalid_config.fbdiv = 16;\n        assert!(!invalid_config.is_valid(12_000_000)); // VCO too low: 12MHz * 16 = 192MHz\n\n        // Test VCO frequency constraints - too high\n        invalid_config = valid_config;\n        invalid_config.fbdiv = 200;\n        invalid_config.refdiv = 1;\n        // This should be INVALID: 12MHz * 200 = 2400MHz exceeds max VCO of 1600MHz\n        assert!(!invalid_config.is_valid(12_000_000));\n\n        // Test a valid high VCO configuration\n        invalid_config.fbdiv = 160; // 10MHz * 160 = 1600MHz, exactly at the limit\n        assert!(invalid_config.is_valid(10_000_000));\n    }\n\n    #[cfg(feature = \"rp2040\")]\n    #[test]\n    fn test_manual_pll_helper() {\n        {\n            // Test the new manual_pll helper method\n            let config = ClockConfig::manual_pll(\n                12_000_000,\n                PllConfig {\n                    refdiv: 1,\n                    fbdiv: 100,\n                    post_div1: 3,\n                    post_div2: 2,\n                },\n                CoreVoltage::V1_15,\n            );\n\n            // Check voltage scale was set correctly\n            assert_eq!(config.core_voltage, CoreVoltage::V1_15);\n\n            // Check PLL config was set correctly\n            assert_eq!(config.xosc.as_ref().unwrap().sys_pll.as_ref().unwrap().refdiv, 1);\n            assert_eq!(config.xosc.as_ref().unwrap().sys_pll.as_ref().unwrap().fbdiv, 100);\n            assert_eq!(config.xosc.as_ref().unwrap().sys_pll.as_ref().unwrap().post_div1, 3);\n            assert_eq!(config.xosc.as_ref().unwrap().sys_pll.as_ref().unwrap().post_div2, 2);\n\n            // Check we get the expected frequency\n            assert_eq!(\n                config\n                    .xosc\n                    .as_ref()\n                    .unwrap()\n                    .sys_pll\n                    .as_ref()\n                    .unwrap()\n                    .output_frequency(12_000_000),\n                200_000_000\n            );\n        }\n    }\n\n    #[cfg(feature = \"rp2040\")]\n    #[test]\n    fn test_auto_voltage_scaling() {\n        {\n            // Test automatic voltage scaling based on frequency\n            // Under 133 MHz should use default voltage (V1_10)\n            let config = ClockConfig::system_freq(125_000_000).unwrap();\n            assert_eq!(config.core_voltage, CoreVoltage::V1_10);\n\n            // 133-200 MHz should use V1_15\n            let config = ClockConfig::system_freq(150_000_000).unwrap();\n            assert_eq!(config.core_voltage, CoreVoltage::V1_15);\n            let config = ClockConfig::system_freq(200_000_000).unwrap();\n            assert_eq!(config.core_voltage, CoreVoltage::V1_15);\n\n            // Above 200 MHz should use V1_15\n            let config = ClockConfig::system_freq(250_000_000).unwrap();\n            assert_eq!(config.core_voltage, CoreVoltage::V1_15);\n\n            // Below 125 MHz should use V1_10\n            let config = ClockConfig::system_freq(100_000_000).unwrap();\n            assert_eq!(config.core_voltage, CoreVoltage::V1_10);\n        }\n    }\n}\n"
  },
  {
    "path": "embassy-rp/src/critical_section_impl.rs",
    "content": "use core::sync::atomic::{AtomicU8, Ordering};\n\nuse crate::pac;\nuse crate::spinlock::Spinlock;\n\nstruct RpSpinlockCs;\ncritical_section::set_impl!(RpSpinlockCs);\n\n/// Marker value to indicate no-one has the lock.\n///\n/// Initialising `LOCK_OWNER` to 0 means cheaper static initialisation so it's the best choice\nconst LOCK_UNOWNED: u8 = 0;\n\n/// Indicates which core owns the lock so that we can call critical_section recursively.\n///\n/// 0 = no one has the lock, 1 = core0 has the lock, 2 = core1 has the lock\nstatic LOCK_OWNER: AtomicU8 = AtomicU8::new(LOCK_UNOWNED);\n\n/// Marker value to indicate that we already owned the lock when we started the `critical_section`.\n///\n/// Since we can't take the spinlock when we already have it, we need some other way to keep track of `critical_section` ownership.\n/// `critical_section` provides a token for communicating between `acquire` and `release` so we use that.\n/// If we're the outermost call to `critical_section` we use the values 0 and 1 to indicate we should release the spinlock and set the interrupts back to disabled and enabled, respectively.\n/// The value 2 indicates that we aren't the outermost call, and should not release the spinlock or re-enable interrupts in `release`\nconst LOCK_ALREADY_OWNED: u8 = 2;\n\nunsafe impl critical_section::Impl for RpSpinlockCs {\n    unsafe fn acquire() -> u8 {\n        RpSpinlockCs::acquire()\n    }\n\n    unsafe fn release(token: u8) {\n        RpSpinlockCs::release(token);\n    }\n}\n\nimpl RpSpinlockCs {\n    unsafe fn acquire() -> u8 {\n        // Store the initial interrupt state and current core id in stack variables\n        let interrupts_active = cortex_m::register::primask::read().is_active();\n        // We reserved 0 as our `LOCK_UNOWNED` value, so add 1 to core_id so we get 1 for core0, 2 for core1.\n        let core = pac::SIO.cpuid().read() as u8 + 1;\n        // Do we already own the spinlock?\n        if LOCK_OWNER.load(Ordering::Acquire) == core {\n            // We already own the lock, so we must have called acquire within a critical_section.\n            // Return the magic inner-loop value so that we know not to re-enable interrupts in release()\n            LOCK_ALREADY_OWNED\n        } else {\n            // Spin until we get the lock\n            loop {\n                // Need to disable interrupts to ensure that we will not deadlock\n                // if an interrupt enters critical_section::Impl after we acquire the lock\n                cortex_m::interrupt::disable();\n                // Ensure the compiler doesn't re-order accesses and violate safety here\n                core::sync::atomic::compiler_fence(Ordering::SeqCst);\n                // Read the spinlock reserved for `critical_section`\n                if let Some(lock) = Spinlock31::try_claim() {\n                    // We just acquired the lock.\n                    // 1. Forget it, so we don't immediately unlock\n                    core::mem::forget(lock);\n                    // 2. Store which core we are so we can tell if we're called recursively\n                    LOCK_OWNER.store(core, Ordering::Relaxed);\n                    break;\n                }\n                // We didn't get the lock, enable interrupts if they were enabled before we started\n                if interrupts_active {\n                    cortex_m::interrupt::enable();\n                }\n            }\n            // If we broke out of the loop we have just acquired the lock\n            // As the outermost loop, we want to return the interrupt status to restore later\n            interrupts_active as _\n        }\n    }\n\n    unsafe fn release(token: u8) {\n        // Did we already own the lock at the start of the `critical_section`?\n        if token != LOCK_ALREADY_OWNED {\n            // No, it wasn't owned at the start of this `critical_section`, so this core no longer owns it.\n            // Set `LOCK_OWNER` back to `LOCK_UNOWNED` to ensure the next critical section tries to obtain the spinlock instead\n            LOCK_OWNER.store(LOCK_UNOWNED, Ordering::Relaxed);\n            // Ensure the compiler doesn't re-order accesses and violate safety here\n            core::sync::atomic::compiler_fence(Ordering::SeqCst);\n            // Release the spinlock to allow others to enter critical_section again\n            Spinlock31::release();\n            // Re-enable interrupts if they were enabled when we first called acquire()\n            // We only do this on the outermost `critical_section` to ensure interrupts stay disabled\n            // for the whole time that we have the lock\n            if token != 0 {\n                cortex_m::interrupt::enable();\n            }\n        }\n    }\n}\n\npub(crate) type Spinlock31 = Spinlock<31>;\n"
  },
  {
    "path": "embassy-rp/src/datetime/datetime_chrono.rs",
    "content": "/// Alias for [`chrono::NaiveDateTime`]\npub type DateTime = chrono::NaiveDateTime;\n/// Alias for [`chrono::Weekday`]\npub type DayOfWeek = chrono::Weekday;\n\n/// Errors regarding the [`DateTime`] struct.\n#[derive(Clone, Copy, Debug, PartialEq, Eq)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub enum Error {\n    /// The [DateTime] has an invalid year. The year must be between 0 and 4095.\n    InvalidYear,\n    /// The [DateTime] contains an invalid date.\n    InvalidDate,\n    /// The [DateTime] contains an invalid time.\n    InvalidTime,\n    /// Outside valid range for Unix timestamp conversion\n    OutOfRange,\n    /// Invalid timestamp or cannot be converted to valid DateTime\n    InvalidTimestamp,\n}\n\n/// Convert to Unix timestamp (milliseconds since 1970-01-01 00:00:00 UTC)\n///\n/// # Errors\n/// Returns error if DateTime is before 1970-01-01.\n#[cfg(feature = \"_rp235x\")]\npub fn timestamp_millis(dt: &DateTime) -> Result<u64, Error> {\n    crate::datetime::epoch::datetime_to_millis(dt)\n}\n\n/// Create from Unix timestamp (milliseconds since 1970-01-01 00:00:00 UTC)\n///\n/// # Errors\n/// Returns error if timestamp cannot be represented as valid DateTime.\n#[cfg(feature = \"_rp235x\")]\npub fn from_timestamp_millis(millis: u64) -> Result<DateTime, Error> {\n    crate::datetime::epoch::millis_to_datetime(millis)\n}\n"
  },
  {
    "path": "embassy-rp/src/datetime/datetime_no_deps.rs",
    "content": "/// Errors regarding the [`DateTime`] struct.\n#[derive(Clone, Copy, Debug, PartialEq, Eq)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub enum Error {\n    /// The [DateTime] contains an invalid year value. Must be between `0..=4095`.\n    InvalidYear,\n    /// The [DateTime] contains an invalid month value. Must be between `1..=12`.\n    InvalidMonth,\n    /// The [DateTime] contains an invalid day value. Must be between `1..=31`.\n    InvalidDay,\n    /// The [DateTime] contains an invalid day of week. Must be between `0..=6` where 0 is Sunday.\n    InvalidDayOfWeek,\n    /// The [DateTime] contains an invalid hour value. Must be between `0..=23`.\n    InvalidHour,\n    /// The [DateTime] contains an invalid minute value. Must be between `0..=59`.\n    InvalidMinute,\n    /// The [DateTime] contains an invalid second value. Must be between `0..=59`.\n    InvalidSecond,\n    /// Outside valid range for Unix timestamp conversion\n    OutOfRange,\n    /// Invalid timestamp or cannot be converted to valid DateTime\n    InvalidTimestamp,\n}\n\n/// Structure containing date and time information\n#[derive(Clone, Debug)]\npub struct DateTime {\n    /// 0..4095\n    pub year: u16,\n    /// 1..12, 1 is January\n    pub month: u8,\n    /// 1..28,29,30,31 depending on month\n    pub day: u8,\n    /// 0..6, 0 is Sunday\n    pub day_of_week: DayOfWeek,\n    /// 0..23\n    pub hour: u8,\n    /// 0..59\n    pub minute: u8,\n    /// 0..59\n    pub second: u8,\n}\n\n/// A day of the week\n#[repr(u8)]\n#[derive(Copy, Clone, Debug, PartialEq, Eq, Ord, PartialOrd, Hash)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\n#[allow(missing_docs)]\npub enum DayOfWeek {\n    Sunday = 0,\n    Monday = 1,\n    Tuesday = 2,\n    Wednesday = 3,\n    Thursday = 4,\n    Friday = 5,\n    Saturday = 6,\n}\n\nimpl DateTime {\n    /// Convert to Unix timestamp (milliseconds since 1970-01-01 00:00:00 UTC)\n    ///\n    /// # Errors\n    /// Returns error if DateTime is before 1970-01-01.\n    pub fn timestamp_millis(&self) -> Result<u64, Error> {\n        crate::datetime::epoch::datetime_to_millis(self)\n    }\n\n    /// Create from Unix timestamp (milliseconds since 1970-01-01 00:00:00 UTC)\n    ///\n    /// # Errors\n    /// Returns error if timestamp cannot be represented as valid DateTime.\n    pub fn from_timestamp_millis(millis: u64) -> Result<Self, Error> {\n        crate::datetime::epoch::millis_to_datetime(millis)\n    }\n}\n"
  },
  {
    "path": "embassy-rp/src/datetime/epoch.rs",
    "content": "//! Unix epoch conversion (milliseconds since 1970-01-01 00:00:00 UTC)\n\n#[cfg(any(not(feature = \"chrono\"), feature = \"_rp235x\"))]\npub(crate) const EPOCH_YEAR: u16 = 1970;\n\n#[cfg(not(feature = \"chrono\"))]\nmod no_deps {\n    use super::super::{DateTime, DayOfWeek, Error};\n    use super::EPOCH_YEAR;\n    const DAYS_IN_MONTH: [u8; 12] = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];\n    const MS_PER_SECOND: u64 = 1000;\n    const MS_PER_MINUTE: u64 = 60 * MS_PER_SECOND;\n    const MS_PER_HOUR: u64 = 60 * MS_PER_MINUTE;\n    const MS_PER_DAY: u64 = 24 * MS_PER_HOUR;\n    const EPOCH_DAY_OF_WEEK: u8 = 4; // Thursday\n\n    const fn is_leap_year(year: u16) -> bool {\n        (year % 4 == 0) && ((year % 100 != 0) || (year % 400 == 0))\n    }\n\n    fn days_in_month(year: u16, month: u8) -> u8 {\n        if month == 2 && is_leap_year(year) {\n            29\n        } else {\n            DAYS_IN_MONTH[(month - 1) as usize]\n        }\n    }\n\n    fn days_from_epoch_to_year(year: u16) -> u32 {\n        let mut days = 0u32;\n        for y in EPOCH_YEAR..year {\n            days += if is_leap_year(y) { 366 } else { 365 };\n        }\n        days\n    }\n\n    fn day_of_week_from_days(days_since_epoch: u32) -> u8 {\n        ((days_since_epoch + EPOCH_DAY_OF_WEEK as u32) % 7) as u8\n    }\n\n    pub(crate) fn datetime_to_millis(dt: &DateTime) -> Result<u64, Error> {\n        if dt.year < EPOCH_YEAR {\n            return Err(Error::OutOfRange);\n        }\n\n        let mut total_days = days_from_epoch_to_year(dt.year);\n        for m in 1..dt.month {\n            total_days += days_in_month(dt.year, m) as u32;\n        }\n        total_days += (dt.day - 1) as u32;\n\n        // Convert to u64 only for final millisecond calculation\n        let mut millis = total_days as u64 * MS_PER_DAY;\n        millis += dt.hour as u64 * MS_PER_HOUR;\n        millis += dt.minute as u64 * MS_PER_MINUTE;\n        millis += dt.second as u64 * MS_PER_SECOND;\n\n        Ok(millis)\n    }\n\n    #[cfg(not(feature = \"chrono\"))]\n    pub(crate) fn millis_to_datetime(millis: u64) -> Result<DateTime, Error> {\n        // Use u64 for initial division, then cast to u32 for subsequent calculations\n        // Max total_days for year 4095 is ~776,000, fits in u32\n        let total_days = (millis / MS_PER_DAY) as u32;\n        // remaining_ms is at most MS_PER_DAY - 1 = 86,399,999, fits in u32\n        let remaining_ms = (millis % MS_PER_DAY) as u32;\n\n        let hour = (remaining_ms / MS_PER_HOUR as u32) as u8;\n        let remaining_ms = remaining_ms % MS_PER_HOUR as u32;\n        let minute = (remaining_ms / MS_PER_MINUTE as u32) as u8;\n        let second = ((remaining_ms % MS_PER_MINUTE as u32) / MS_PER_SECOND as u32) as u8;\n\n        let day_of_week = match day_of_week_from_days(total_days) {\n            0 => DayOfWeek::Sunday,\n            1 => DayOfWeek::Monday,\n            2 => DayOfWeek::Tuesday,\n            3 => DayOfWeek::Wednesday,\n            4 => DayOfWeek::Thursday,\n            5 => DayOfWeek::Friday,\n            6 => DayOfWeek::Saturday,\n            _ => unreachable!(),\n        };\n\n        let mut year = EPOCH_YEAR;\n        let mut days_remaining = total_days;\n\n        loop {\n            let days_in_year: u32 = if is_leap_year(year) { 366 } else { 365 };\n            if days_remaining < days_in_year {\n                break;\n            }\n            days_remaining -= days_in_year;\n            year += 1;\n\n            if year > 4095 {\n                return Err(Error::InvalidTimestamp);\n            }\n        }\n\n        let mut month = 1u8;\n        while month <= 12 {\n            let days_in_this_month = days_in_month(year, month) as u32;\n            if days_remaining < days_in_this_month {\n                break;\n            }\n            days_remaining -= days_in_this_month;\n            month += 1;\n        }\n        let day = (days_remaining + 1) as u8;\n\n        Ok(DateTime {\n            year,\n            month,\n            day,\n            day_of_week,\n            hour,\n            minute,\n            second,\n        })\n    }\n}\n\n#[cfg(not(feature = \"chrono\"))]\npub(crate) use no_deps::{datetime_to_millis, millis_to_datetime};\n\n#[cfg(all(feature = \"chrono\", feature = \"_rp235x\"))]\nmod chrono_rp235x {\n    use super::super::{DateTime, Error};\n    use super::EPOCH_YEAR;\n\n    pub(crate) fn datetime_to_millis(dt: &DateTime) -> Result<u64, Error> {\n        use chrono::Datelike;\n\n        if dt.year() < EPOCH_YEAR as i32 {\n            return Err(Error::OutOfRange);\n        }\n\n        let timestamp_millis = dt.and_utc().timestamp_millis();\n        if timestamp_millis < 0 {\n            return Err(Error::OutOfRange);\n        }\n\n        Ok(timestamp_millis as u64)\n    }\n\n    pub(crate) fn millis_to_datetime(millis: u64) -> Result<DateTime, Error> {\n        use chrono::Datelike;\n\n        let millis_i64 = millis.try_into().map_err(|_| Error::InvalidTimestamp)?;\n        let dt = chrono::DateTime::from_timestamp_millis(millis_i64)\n            .ok_or(Error::InvalidTimestamp)?\n            .naive_utc();\n\n        if dt.year() < 0 || dt.year() > 4095 {\n            return Err(Error::InvalidYear);\n        }\n\n        Ok(dt)\n    }\n}\n\n#[cfg(all(feature = \"chrono\", feature = \"_rp235x\"))]\npub(crate) use chrono_rp235x::{datetime_to_millis, millis_to_datetime};\n"
  },
  {
    "path": "embassy-rp/src/datetime/mod.rs",
    "content": "//! Shared datetime types for RTC and other time-keeping peripherals.\n//!\n//! This module provides DateTime and DayOfWeek types that can be used across\n//! multiple peripherals in embassy-rp. The implementation can be backed by\n//! the `chrono` crate (when the `chrono` feature is enabled) or use a\n//! standalone no-dependencies implementation.\n\n#[cfg_attr(feature = \"chrono\", path = \"datetime_chrono.rs\")]\n#[cfg_attr(not(feature = \"chrono\"), path = \"datetime_no_deps.rs\")]\nmod datetime;\n\npub(crate) mod epoch;\n\npub use self::datetime::{DateTime, DayOfWeek, Error};\n#[cfg(all(feature = \"chrono\", feature = \"_rp235x\"))]\npub use self::datetime::{from_timestamp_millis, timestamp_millis};\n"
  },
  {
    "path": "embassy-rp/src/dma.rs",
    "content": "//! Direct Memory Access (DMA)\nuse core::future::Future;\nuse core::marker::PhantomData;\nuse core::pin::Pin;\nuse core::sync::atomic::{Ordering, compiler_fence};\nuse core::task::{Context, Poll};\n\nuse embassy_hal_internal::interrupt::InterruptExt;\nuse embassy_hal_internal::{Peri, PeripheralType};\nuse embassy_sync::waitqueue::AtomicWaker;\nuse pac::dma::vals::DataSize;\n\nuse crate::interrupt::typelevel::Interrupt;\nuse crate::pac::dma::vals;\nuse crate::{RegExt, interrupt, pac, peripherals};\n\n/// DMA interrupt handler.\npub struct InterruptHandler<T: ChannelInstance> {\n    _phantom: PhantomData<T>,\n}\n\nimpl<T: ChannelInstance> interrupt::typelevel::Handler<T::Interrupt> for InterruptHandler<T> {\n    unsafe fn on_interrupt() {\n        let channel = T::number() as usize;\n        let ctrl_trig = pac::DMA.ch(channel).ctrl_trig().read();\n        if ctrl_trig.ahb_error() {\n            panic!(\"DMA: error on DMA_0 channel {}\", channel);\n        }\n\n        let ints0 = pac::DMA.ints(0).read();\n        if ints0 & (1 << channel) != 0 {\n            pac::DMA.ints(0).write_value(1 << channel);\n\n            CHANNEL_WAKERS[channel].wake();\n        }\n    }\n}\n\npub(crate) unsafe fn init() {\n    interrupt::DMA_IRQ_0.set_priority(interrupt::Priority::P3);\n}\n\n/// DMA channel driver.\npub struct Channel<'d> {\n    number: u8,\n    phantom: PhantomData<&'d ()>,\n}\n\nimpl<'d> Channel<'d> {\n    /// Create a new DMA channel driver.\n    pub fn new<T: ChannelInstance>(\n        _ch: Peri<'d, T>,\n        _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd,\n    ) -> Self {\n        let number = T::number();\n\n        // Enable interrupt for this channel\n        pac::DMA.inte(0).write_set(|v| *v = 1 << number);\n        unsafe { T::Interrupt::enable() };\n\n        Self {\n            number,\n            phantom: PhantomData,\n        }\n    }\n\n    /// Get the channel number.\n    fn number(&self) -> u8 {\n        self.number\n    }\n\n    /// Get the channel register block.\n    #[cfg(feature = \"unstable-pac\")]\n    pub fn regs(&self) -> pac::dma::Channel {\n        pac::DMA.ch(self.number as _)\n    }\n\n    /// Get the channel register block.\n    #[cfg(not(feature = \"unstable-pac\"))]\n    fn regs(&self) -> pac::dma::Channel {\n        pac::DMA.ch(self.number as _)\n    }\n\n    /// Get next write address.\n    pub fn write_addr(&self) -> u32 {\n        self.regs().write_addr().read()\n    }\n\n    /// Reborrow the channel, allowing it to be used in multiple places.\n    pub fn reborrow(&mut self) -> Channel<'_> {\n        Channel {\n            number: self.number,\n            phantom: PhantomData,\n        }\n    }\n\n    unsafe fn configure(\n        &self,\n        from: *const u32,\n        to: *mut u32,\n        len: usize,\n        data_size: DataSize,\n        incr_read: bool,\n        incr_write: bool,\n        dreq: vals::TreqSel,\n        bswap: bool,\n    ) {\n        let p = self.regs();\n\n        p.read_addr().write_value(from as u32);\n        p.write_addr().write_value(to as u32);\n        #[cfg(feature = \"rp2040\")]\n        p.trans_count().write(|w| {\n            *w = len as u32;\n        });\n        #[cfg(feature = \"_rp235x\")]\n        p.trans_count().write(|w| {\n            w.set_mode(0.into());\n            w.set_count(len as u32);\n        });\n\n        compiler_fence(Ordering::SeqCst);\n\n        p.ctrl_trig().write(|w| {\n            w.set_treq_sel(dreq);\n            w.set_data_size(data_size);\n            w.set_incr_read(incr_read);\n            w.set_incr_write(incr_write);\n            w.set_chain_to(self.number());\n            w.set_bswap(bswap);\n            w.set_en(true);\n        });\n\n        compiler_fence(Ordering::SeqCst);\n    }\n\n    /// DMA read from a peripheral to memory.\n    ///\n    /// SAFETY: Slice must point to a valid location reachable by DMA.\n    pub unsafe fn read<'a, W: Word>(\n        &'a mut self,\n        from: *const W,\n        to: *mut [W],\n        dreq: vals::TreqSel,\n        bswap: bool,\n    ) -> Transfer<'a> {\n        self.configure(\n            from as *const u32,\n            to as *mut W as *mut u32,\n            to.len(),\n            W::size(),\n            false,\n            true,\n            dreq,\n            bswap,\n        );\n        Transfer::new(self.reborrow())\n    }\n\n    /// Repetedly read from a peripheral to discard data.\n    ///\n    /// SAFETY: `from` must point to a valid location reachable by DMA.\n    pub unsafe fn read_discard<'a, W: Word>(\n        &'a mut self,\n        from: *mut W,\n        len: usize,\n        dreq: vals::TreqSel,\n    ) -> Transfer<'a> {\n        // static mut so that this is allocated in RAM.\n        static mut DUMMY: u32 = 0;\n\n        self.configure(\n            from as *mut u32,\n            core::ptr::addr_of_mut!(DUMMY) as *mut u32,\n            len,\n            W::size(),\n            false,\n            false,\n            dreq,\n            false,\n        );\n        Transfer::new(self.reborrow())\n    }\n\n    /// DMA write from memory to a peripheral.\n    ///\n    /// SAFETY: Slice must point to a valid location reachable by DMA.\n    pub unsafe fn write<'a, W: Word>(\n        &'a mut self,\n        from: *const [W],\n        to: *mut W,\n        dreq: vals::TreqSel,\n        bswap: bool,\n    ) -> Transfer<'a> {\n        self.configure(\n            from as *const W as *const u32,\n            to as *mut u32,\n            from.len(),\n            W::size(),\n            true,\n            false,\n            dreq,\n            bswap,\n        );\n        Transfer::new(self.reborrow())\n    }\n\n    /// Repetedly write 0 to peripeheral.\n    ///\n    /// SAFETY: `to` must point to a valid location reachable by DMA.\n    pub unsafe fn write_zeros<'a, W: Word>(\n        &'a mut self,\n        count: usize,\n        to: *mut W,\n        dreq: vals::TreqSel,\n    ) -> Transfer<'a> {\n        // static mut so that this is allocated in RAM.\n        static mut DUMMY: u32 = 0;\n\n        self.configure(\n            core::ptr::addr_of_mut!(DUMMY) as *const u32,\n            to as *mut u32,\n            count,\n            W::size(),\n            false,\n            false,\n            dreq,\n            false,\n        );\n        Transfer::new(self.reborrow())\n    }\n\n    /// DMA copy between memory regions.\n    ///\n    /// SAFETY: Slices must point to locations reachable by DMA.\n    pub unsafe fn copy<'a, W: Word>(&'a mut self, from: &[W], to: &mut [W]) -> Transfer<'a> {\n        let from_len = from.len();\n        let to_len = to.len();\n        assert_eq!(from_len, to_len);\n        self.configure(\n            from.as_ptr() as *const u32,\n            to.as_mut_ptr() as *mut u32,\n            from_len,\n            W::size(),\n            true,\n            true,\n            vals::TreqSel::PERMANENT,\n            false,\n        );\n        Transfer::new(self.reborrow())\n    }\n}\n\n/// DMA transfer driver.\n#[must_use = \"futures do nothing unless you `.await` or poll them\"]\npub struct Transfer<'a> {\n    channel: Channel<'a>,\n}\n\nimpl<'a> Transfer<'a> {\n    fn new(channel: Channel<'a>) -> Self {\n        Self { channel }\n    }\n}\n\nimpl<'a> Drop for Transfer<'a> {\n    fn drop(&mut self) {\n        let p = self.channel.regs();\n        pac::DMA\n            .chan_abort()\n            .modify(|m| m.set_chan_abort(1 << self.channel.number()));\n        while p.ctrl_trig().read().busy() {}\n    }\n}\n\nimpl<'a> Unpin for Transfer<'a> {}\nimpl<'a> Future for Transfer<'a> {\n    type Output = ();\n    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {\n        // We need to register/re-register the waker for each poll because any\n        // calls to wake will deregister the waker.\n        CHANNEL_WAKERS[self.channel.number() as usize].register(cx.waker());\n\n        if self.channel.regs().ctrl_trig().read().busy() {\n            Poll::Pending\n        } else {\n            Poll::Ready(())\n        }\n    }\n}\n\n#[cfg(feature = \"rp2040\")]\npub(crate) const CHANNEL_COUNT: usize = 12;\n#[cfg(feature = \"_rp235x\")]\npub(crate) const CHANNEL_COUNT: usize = 16;\nstatic CHANNEL_WAKERS: [AtomicWaker; CHANNEL_COUNT] = [const { AtomicWaker::new() }; CHANNEL_COUNT];\n\ntrait SealedChannelInstance {}\ntrait SealedWord {}\n\n/// DMA channel instance trait.\n#[allow(private_bounds)]\npub trait ChannelInstance: PeripheralType + SealedChannelInstance + Sized + 'static {\n    /// The interrupt type for this DMA channel.\n    type Interrupt: interrupt::typelevel::Interrupt;\n\n    /// Channel number.\n    fn number() -> u8;\n\n    /// Channel registry block.\n    fn regs() -> pac::dma::Channel {\n        pac::DMA.ch(Self::number() as _)\n    }\n}\n\n/// DMA word.\n#[allow(private_bounds)]\npub trait Word: SealedWord {\n    /// Word size.\n    fn size() -> vals::DataSize;\n}\n\nimpl SealedWord for u8 {}\nimpl Word for u8 {\n    fn size() -> vals::DataSize {\n        vals::DataSize::SIZE_BYTE\n    }\n}\n\nimpl SealedWord for u16 {}\nimpl Word for u16 {\n    fn size() -> vals::DataSize {\n        vals::DataSize::SIZE_HALFWORD\n    }\n}\n\nimpl SealedWord for u32 {}\nimpl Word for u32 {\n    fn size() -> vals::DataSize {\n        vals::DataSize::SIZE_WORD\n    }\n}\n\nmacro_rules! channel {\n    ($name:ident, $num:expr, $irq:ident) => {\n        impl SealedChannelInstance for peripherals::$name {}\n        impl ChannelInstance for peripherals::$name {\n            type Interrupt = interrupt::typelevel::$irq;\n\n            fn number() -> u8 {\n                $num\n            }\n        }\n    };\n}\n\nchannel!(DMA_CH0, 0, DMA_IRQ_0);\nchannel!(DMA_CH1, 1, DMA_IRQ_0);\nchannel!(DMA_CH2, 2, DMA_IRQ_0);\nchannel!(DMA_CH3, 3, DMA_IRQ_0);\nchannel!(DMA_CH4, 4, DMA_IRQ_0);\nchannel!(DMA_CH5, 5, DMA_IRQ_0);\nchannel!(DMA_CH6, 6, DMA_IRQ_0);\nchannel!(DMA_CH7, 7, DMA_IRQ_0);\nchannel!(DMA_CH8, 8, DMA_IRQ_0);\nchannel!(DMA_CH9, 9, DMA_IRQ_0);\nchannel!(DMA_CH10, 10, DMA_IRQ_0);\nchannel!(DMA_CH11, 11, DMA_IRQ_0);\n#[cfg(feature = \"_rp235x\")]\nchannel!(DMA_CH12, 12, DMA_IRQ_0);\n#[cfg(feature = \"_rp235x\")]\nchannel!(DMA_CH13, 13, DMA_IRQ_0);\n#[cfg(feature = \"_rp235x\")]\nchannel!(DMA_CH14, 14, DMA_IRQ_0);\n#[cfg(feature = \"_rp235x\")]\nchannel!(DMA_CH15, 15, DMA_IRQ_0);\n"
  },
  {
    "path": "embassy-rp/src/executor.rs",
    "content": "//! Multicore-ready executors.\n//!\n//! These custom executors are modified versions of the ones provided by\n//! [embassy-executor](https://crates.io/crates/embassy-executor).\n//! They can be used across both cores so that an interrupt executor\n//! on one core can be woken up by the other.\n//!\n//! When using this executors, they must be explicitely passed to the `main`\n//! macro using\n//!\n//! `#[embassy_executor::main(executor = \"embassy_rp::executor::Executor\",  entry = \"cortex_m_rt::entry\")]`\n\n#[unsafe(export_name = \"__pender\")]\n#[cfg(any(feature = \"executor-thread\", feature = \"executor-interrupt\"))]\nfn __pender(context: *mut ()) {\n    unsafe {\n        // Safety: `context` is either `usize::MAX` created by `Executor::run`, or a valid interrupt\n        // request number given to `InterruptExecutor::start`.\n\n        let context = context as usize;\n\n        #[cfg(feature = \"executor-thread\")]\n        // Try to make Rust optimize the branching away if we only use thread mode.\n        if !cfg!(feature = \"executor-interrupt\") || context == THREAD_PENDER {\n            core::arch::asm!(\"sev\");\n            return;\n        }\n\n        #[cfg(feature = \"executor-interrupt\")]\n        {\n            use cortex_m::interrupt::InterruptNumber;\n            use cortex_m::peripheral::NVIC;\n\n            use crate::multicore::{PEND_IRQ_TOKEN, current_core, fifo_write};\n\n            #[derive(Clone, Copy)]\n            struct Irq(u16);\n            unsafe impl InterruptNumber for Irq {\n                fn number(self) -> u16 {\n                    self.0\n                }\n            }\n\n            // The context contains the core number in the upper 16 bits.\n            let core_no = (context >> 16) as u8;\n            let context = context & 0xFFFF;\n\n            if core_no == current_core() as u8 {\n                let irq = Irq(context as u16);\n\n                // STIR is faster, but is only available in v7 and higher.\n                #[cfg(feature = \"_rp235x\")]\n                {\n                    let mut nvic: NVIC = core::mem::transmute(());\n                    nvic.request(irq);\n                }\n\n                #[cfg(feature = \"rp2040\")]\n                NVIC::pend(irq);\n            } else {\n                fifo_write(PEND_IRQ_TOKEN | context as u32);\n            }\n        }\n    }\n}\n\n#[cfg(feature = \"executor-thread\")]\npub use thread::*;\n#[cfg(feature = \"executor-thread\")]\nmod thread {\n    pub(super) const THREAD_PENDER: usize = usize::MAX;\n\n    use core::arch::asm;\n    use core::marker::PhantomData;\n\n    use embassy_executor::{Spawner, raw};\n\n    /// Thread mode executor, using WFE/SEV.\n    ///\n    /// This is the simplest and most common kind of executor. It runs on\n    /// thread mode (at the lowest priority level), and uses the `WFE` ARM instruction\n    /// to sleep when it has no more work to do. When a task is woken, a `SEV` instruction\n    /// is executed, to make the `WFE` exit from sleep and poll the task.\n    ///\n    /// This executor allows for ultra low power consumption for chips where `WFE`\n    /// triggers low-power sleep without extra steps. If your chip requires extra steps,\n    /// you may use [`raw::Executor`] directly to program custom behavior.\n    pub struct Executor {\n        inner: raw::Executor,\n        not_send: PhantomData<*mut ()>,\n    }\n\n    impl Executor {\n        /// Create a new Executor.\n        pub fn new() -> Self {\n            Self {\n                inner: raw::Executor::new(THREAD_PENDER as *mut ()),\n                not_send: PhantomData,\n            }\n        }\n\n        /// Run the executor.\n        ///\n        /// The `init` closure is called with a [`Spawner`] that spawns tasks on\n        /// this executor. Use it to spawn the initial task(s). After `init` returns,\n        /// the executor starts running the tasks.\n        ///\n        /// To spawn more tasks later, you may keep copies of the [`Spawner`] (it is `Copy`),\n        /// for example by passing it as an argument to the initial tasks.\n        ///\n        /// This function requires `&'static mut self`. This means you have to store the\n        /// Executor instance in a place where it'll live forever and grants you mutable\n        /// access. There's a few ways to do this:\n        ///\n        /// - a [StaticCell](https://docs.rs/static_cell/latest/static_cell/) (safe)\n        /// - a `static mut` (unsafe)\n        /// - a local variable in a function you know never returns (like `fn main() -> !`), upgrading its lifetime with `transmute`. (unsafe)\n        ///\n        /// This function never returns.\n        pub fn run(&'static mut self, init: impl FnOnce(Spawner)) -> ! {\n            init(self.inner.spawner());\n\n            loop {\n                unsafe {\n                    self.inner.poll();\n                    asm!(\"wfe\");\n                };\n            }\n        }\n    }\n}\n\n#[cfg(feature = \"executor-interrupt\")]\npub use interrupt::*;\n#[cfg(feature = \"executor-interrupt\")]\nmod interrupt {\n    use core::cell::{Cell, UnsafeCell};\n    use core::mem::MaybeUninit;\n\n    use cortex_m::interrupt::InterruptNumber;\n    use cortex_m::peripheral::NVIC;\n    use critical_section::Mutex;\n    use embassy_executor::raw;\n\n    use crate::multicore::current_core;\n\n    /// Interrupt mode executor.\n    ///\n    /// This executor runs tasks in interrupt mode. The interrupt handler is set up\n    /// to poll tasks, and when a task is woken the interrupt is pended from software.\n    ///\n    /// This allows running async tasks at a priority higher than thread mode. One\n    /// use case is to leave thread mode free for non-async tasks. Another use case is\n    /// to run multiple executors: one in thread mode for low priority tasks and another in\n    /// interrupt mode for higher priority tasks. Higher priority tasks will preempt lower\n    /// priority ones.\n    ///\n    /// It is even possible to run multiple interrupt mode executors at different priorities,\n    /// by assigning different priorities to the interrupts. For an example on how to do this,\n    /// See the 'multiprio' example for 'embassy-nrf'.\n    ///\n    /// To use it, you have to pick an interrupt that won't be used by the hardware.\n    /// Some chips reserve some interrupts for this purpose, sometimes named \"software interrupts\" (SWI).\n    /// If this is not the case, you may use an interrupt from any unused peripheral.\n    ///\n    /// It is somewhat more complex to use, it's recommended to use the thread-mode\n    /// [`Executor`](embassy_executor::Executor) instead, if it works for your use case.\n    pub struct InterruptExecutor {\n        started: Mutex<Cell<bool>>,\n        executor: UnsafeCell<MaybeUninit<raw::Executor>>,\n    }\n\n    unsafe impl Send for InterruptExecutor {}\n    unsafe impl Sync for InterruptExecutor {}\n\n    impl InterruptExecutor {\n        /// Create a new, not started `InterruptExecutor`.\n        #[inline]\n        pub const fn new() -> Self {\n            Self {\n                started: Mutex::new(Cell::new(false)),\n                executor: UnsafeCell::new(MaybeUninit::uninit()),\n            }\n        }\n\n        /// Executor interrupt callback.\n        ///\n        /// # Safety\n        ///\n        /// - You MUST call this from the interrupt handler, and from nowhere else.\n        /// - You must not call this before calling `start()`.\n        pub unsafe fn on_interrupt(&'static self) {\n            let executor = unsafe { (&*self.executor.get()).assume_init_ref() };\n            executor.poll();\n        }\n\n        /// Start the executor.\n        ///\n        /// This initializes the executor, enables the interrupt, and returns.\n        /// The executor keeps running in the background through the interrupt.\n        ///\n        /// This returns a [`SendSpawner`] you can use to spawn tasks on it. A [`SendSpawner`]\n        /// is returned instead of a [`Spawner`](embassy_executor::Spawner) because the executor effectively runs in a\n        /// different \"thread\" (the interrupt), so spawning tasks on it is effectively\n        /// sending them.\n        ///\n        /// To obtain a [`Spawner`](embassy_executor::Spawner) for this executor, use [`Spawner::for_current_executor()`](embassy_executor::Spawner::for_current_executor()) from\n        /// a task running in it.\n        ///\n        /// # Interrupt requirements\n        ///\n        /// You must write the interrupt handler yourself, and make it call [`on_interrupt()`](Self::on_interrupt).\n        ///\n        /// This method already enables (unmasks) the interrupt, you must NOT do it yourself.\n        ///\n        /// You must set the interrupt priority before calling this method. You MUST NOT\n        /// do it after.\n        ///\n        /// [`SendSpawner`]: embassy_executor::SendSpawner\n        pub fn start(&'static self, irq: impl InterruptNumber) -> embassy_executor::SendSpawner {\n            if critical_section::with(|cs| self.started.borrow(cs).replace(true)) {\n                panic!(\"InterruptExecutor::start() called multiple times on the same executor.\");\n            }\n\n            unsafe {\n                let context = (irq.number() as usize | (current_core() as usize) << 16) as *mut ();\n                (&mut *self.executor.get())\n                    .as_mut_ptr()\n                    .write(raw::Executor::new(context))\n            }\n\n            let executor = unsafe { (&*self.executor.get()).assume_init_ref() };\n\n            unsafe { NVIC::unmask(irq) }\n\n            executor.spawner().make_send()\n        }\n\n        /// Get a SendSpawner for this executor\n        ///\n        /// This returns a [`SendSpawner`](embassy_executor::SendSpawner) you can use to spawn tasks on this\n        /// executor.\n        ///\n        /// This MUST only be called on an executor that has already been started.\n        /// The function will panic otherwise.\n        pub fn spawner(&'static self) -> embassy_executor::SendSpawner {\n            if !critical_section::with(|cs| self.started.borrow(cs).get()) {\n                panic!(\"InterruptExecutor::spawner() called on uninitialized executor.\");\n            }\n            let executor = unsafe { (&*self.executor.get()).assume_init_ref() };\n            executor.spawner().make_send()\n        }\n    }\n}\n"
  },
  {
    "path": "embassy-rp/src/flash.rs",
    "content": "//! Flash driver.\nuse core::future::Future;\nuse core::marker::PhantomData;\nuse core::pin::Pin;\nuse core::task::{Context, Poll};\n\nuse embassy_hal_internal::{Peri, PeripheralType};\nuse embedded_storage::nor_flash::{\n    ErrorType, MultiwriteNorFlash, NorFlash, NorFlashError, NorFlashErrorKind, ReadNorFlash, check_erase, check_read,\n    check_write,\n};\n\nuse crate::peripherals::FLASH;\nuse crate::{dma, interrupt, pac};\n\n/// Flash base address.\npub const FLASH_BASE: *const u32 = 0x10000000 as _;\n\n/// Address for xip setup function set up by the 235x bootrom.\n#[cfg(feature = \"_rp235x\")]\npub const BOOTRAM_BASE: *const u32 = 0x400e0000 as _;\n\n/// If running from RAM, we might have no boot2. Use bootrom `flash_enter_cmd_xip` instead.\n// TODO: when run-from-ram is set, completely skip the \"pause cores and jumpp to RAM\" dance.\npub const USE_BOOT2: bool = !cfg!(feature = \"run-from-ram\") | cfg!(feature = \"_rp235x\");\n\n// **NOTE**:\n//\n// These limitations are currently enforced because of using the\n// RP2040 boot-rom flash functions, that are optimized for flash compatibility\n// rather than performance.\n/// Flash page size.\npub const PAGE_SIZE: usize = 256;\n/// Flash write size.\npub const WRITE_SIZE: usize = 1;\n/// Flash read size.\npub const READ_SIZE: usize = 1;\n/// Flash erase size.\npub const ERASE_SIZE: usize = 4096;\n/// Flash DMA read size.\npub const ASYNC_READ_SIZE: usize = 4;\n\n/// Error type for NVMC operations.\n#[derive(Debug, Copy, Clone, PartialEq, Eq)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub enum Error {\n    /// Operation using a location not in flash.\n    OutOfBounds,\n    /// Unaligned operation or using unaligned buffers.\n    Unaligned,\n    /// Accessed from the wrong core.\n    InvalidCore,\n    /// Other error\n    Other,\n}\n\nimpl From<NorFlashErrorKind> for Error {\n    fn from(e: NorFlashErrorKind) -> Self {\n        match e {\n            NorFlashErrorKind::NotAligned => Self::Unaligned,\n            NorFlashErrorKind::OutOfBounds => Self::OutOfBounds,\n            _ => Self::Other,\n        }\n    }\n}\n\nimpl NorFlashError for Error {\n    fn kind(&self) -> NorFlashErrorKind {\n        match self {\n            Self::OutOfBounds => NorFlashErrorKind::OutOfBounds,\n            Self::Unaligned => NorFlashErrorKind::NotAligned,\n            _ => NorFlashErrorKind::Other,\n        }\n    }\n}\n\n/// Future that waits for completion of a background read\n#[must_use = \"futures do nothing unless you `.await` or poll them\"]\npub struct BackgroundRead<'a, 'd, T: Instance, const FLASH_SIZE: usize> {\n    flash: PhantomData<&'a mut Flash<'d, T, Async, FLASH_SIZE>>,\n    transfer: dma::Transfer<'a>,\n}\n\nimpl<'a, 'd, T: Instance, const FLASH_SIZE: usize> Future for BackgroundRead<'a, 'd, T, FLASH_SIZE> {\n    type Output = ();\n    fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {\n        Pin::new(&mut self.transfer).poll(cx)\n    }\n}\n\nimpl<'a, 'd, T: Instance, const FLASH_SIZE: usize> Drop for BackgroundRead<'a, 'd, T, FLASH_SIZE> {\n    fn drop(&mut self) {\n        if pac::XIP_CTRL.stream_ctr().read().0 == 0 {\n            return;\n        }\n        pac::XIP_CTRL\n            .stream_ctr()\n            .write_value(pac::xip_ctrl::regs::StreamCtr(0));\n        core::sync::atomic::compiler_fence(core::sync::atomic::Ordering::SeqCst);\n        // Errata RP2040-E8: Perform an uncached read to make sure there's not a transfer in\n        // flight that might effect an address written to start a new transfer.  This stalls\n        // until after any transfer is complete, so the address will not change anymore.\n        #[cfg(feature = \"rp2040\")]\n        const XIP_NOCACHE_NOALLOC_BASE: *const u32 = 0x13000000 as *const _;\n        #[cfg(feature = \"_rp235x\")]\n        const XIP_NOCACHE_NOALLOC_BASE: *const u32 = 0x14000000 as *const _;\n        unsafe {\n            core::ptr::read_volatile(XIP_NOCACHE_NOALLOC_BASE);\n        }\n        core::sync::atomic::compiler_fence(core::sync::atomic::Ordering::SeqCst);\n    }\n}\n\n/// Flash driver.\npub struct Flash<'d, T: Instance, M: Mode, const FLASH_SIZE: usize> {\n    dma: Option<dma::Channel<'d>>,\n    phantom: PhantomData<(&'d mut T, M)>,\n}\n\nimpl<'d, T: Instance, M: Mode, const FLASH_SIZE: usize> Flash<'d, T, M, FLASH_SIZE> {\n    /// Blocking read.\n    ///\n    /// The offset and buffer must be aligned.\n    ///\n    /// NOTE: `offset` is an offset from the flash start, NOT an absolute address.\n    pub fn blocking_read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Error> {\n        trace!(\n            \"Reading from 0x{:x} to 0x{:x}\",\n            FLASH_BASE as u32 + offset,\n            FLASH_BASE as u32 + offset + bytes.len() as u32\n        );\n        check_read(self, offset, bytes.len())?;\n\n        let flash_data = unsafe { core::slice::from_raw_parts((FLASH_BASE as u32 + offset) as *const u8, bytes.len()) };\n\n        bytes.copy_from_slice(flash_data);\n        Ok(())\n    }\n\n    /// Flash capacity.\n    pub fn capacity(&self) -> usize {\n        FLASH_SIZE\n    }\n\n    /// Blocking erase.\n    ///\n    /// NOTE: `offset` is an offset from the flash start, NOT an absolute address.\n    pub fn blocking_erase(&mut self, from: u32, to: u32) -> Result<(), Error> {\n        check_erase(self, from, to)?;\n\n        trace!(\n            \"Erasing from 0x{:x} to 0x{:x}\",\n            FLASH_BASE as u32 + from,\n            FLASH_BASE as u32 + to\n        );\n\n        let len = to - from;\n\n        unsafe { in_ram(|| ram_helpers::flash_range_erase(from, len))? };\n\n        Ok(())\n    }\n\n    /// Blocking write.\n    ///\n    /// The offset and buffer must be aligned.\n    ///\n    /// NOTE: `offset` is an offset from the flash start, NOT an absolute address.\n    pub fn blocking_write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Error> {\n        check_write(self, offset, bytes.len())?;\n\n        trace!(\"Writing {:?} bytes to 0x{:x}\", bytes.len(), FLASH_BASE as u32 + offset);\n\n        let end_offset = offset as usize + bytes.len();\n\n        let padded_offset = (offset as *const u8).align_offset(PAGE_SIZE);\n        let start_padding = core::cmp::min(padded_offset, bytes.len());\n\n        // Pad in the beginning\n        if start_padding > 0 {\n            let start = PAGE_SIZE - padded_offset;\n            let end = start + start_padding;\n\n            let mut pad_buf = [0xFF_u8; PAGE_SIZE];\n            pad_buf[start..end].copy_from_slice(&bytes[..start_padding]);\n\n            let unaligned_offset = offset as usize - start;\n\n            unsafe { in_ram(|| ram_helpers::flash_range_program(unaligned_offset as u32, &pad_buf))? }\n        }\n\n        let remaining_len = bytes.len() - start_padding;\n        let end_padding = start_padding + PAGE_SIZE * (remaining_len / PAGE_SIZE);\n\n        // Write aligned slice of length in multiples of 256 bytes\n        // If the remaining bytes to be written is more than a full page.\n        if remaining_len >= PAGE_SIZE {\n            let mut aligned_offset = if start_padding > 0 {\n                offset as usize + padded_offset\n            } else {\n                offset as usize\n            };\n\n            if bytes.as_ptr() as usize >= 0x2000_0000 {\n                let aligned_data = &bytes[start_padding..end_padding];\n\n                unsafe { in_ram(|| ram_helpers::flash_range_program(aligned_offset as u32, aligned_data))? }\n            } else {\n                for chunk in bytes[start_padding..end_padding].chunks_exact(PAGE_SIZE) {\n                    let mut ram_buf = [0xFF_u8; PAGE_SIZE];\n                    ram_buf.copy_from_slice(chunk);\n                    unsafe { in_ram(|| ram_helpers::flash_range_program(aligned_offset as u32, &ram_buf))? }\n                    aligned_offset += PAGE_SIZE;\n                }\n            }\n        }\n\n        // Pad in the end\n        let rem_offset = (end_offset as *const u8).align_offset(PAGE_SIZE);\n        let rem_padding = remaining_len % PAGE_SIZE;\n        if rem_padding > 0 {\n            let mut pad_buf = [0xFF_u8; PAGE_SIZE];\n            pad_buf[..rem_padding].copy_from_slice(&bytes[end_padding..]);\n\n            let unaligned_offset = end_offset - (PAGE_SIZE - rem_offset);\n\n            unsafe { in_ram(|| ram_helpers::flash_range_program(unaligned_offset as u32, &pad_buf))? }\n        }\n\n        Ok(())\n    }\n\n    /// Read SPI flash unique ID\n    #[cfg(feature = \"rp2040\")]\n    pub fn blocking_unique_id(&mut self, uid: &mut [u8]) -> Result<(), Error> {\n        unsafe { in_ram(|| ram_helpers::flash_unique_id(uid))? };\n        Ok(())\n    }\n\n    /// Read SPI flash JEDEC ID\n    #[cfg(feature = \"rp2040\")]\n    pub fn blocking_jedec_id(&mut self) -> Result<u32, Error> {\n        let mut jedec = None;\n        unsafe {\n            in_ram(|| {\n                jedec.replace(ram_helpers::flash_jedec_id());\n            })?;\n        };\n        Ok(jedec.unwrap())\n    }\n}\n\nimpl<'d, T: Instance, const FLASH_SIZE: usize> Flash<'d, T, Blocking, FLASH_SIZE> {\n    /// Create a new flash driver in blocking mode.\n    pub fn new_blocking(_flash: Peri<'d, T>) -> Self {\n        Self {\n            dma: None,\n            phantom: PhantomData,\n        }\n    }\n}\n\nimpl<'d, T: Instance, const FLASH_SIZE: usize> Flash<'d, T, Async, FLASH_SIZE> {\n    /// Create a new flash driver in async mode.\n    pub fn new<D: dma::ChannelInstance>(\n        _flash: Peri<'d, T>,\n        dma: Peri<'d, D>,\n        irq: impl interrupt::typelevel::Binding<D::Interrupt, dma::InterruptHandler<D>> + 'd,\n    ) -> Self {\n        Self {\n            dma: Some(dma::Channel::new(dma, irq)),\n            phantom: PhantomData,\n        }\n    }\n\n    /// Start a background read operation.\n    ///\n    /// The offset and buffer must be aligned.\n    ///\n    /// NOTE: `offset` is an offset from the flash start, NOT an absolute address.\n    pub fn background_read<'a>(\n        &'a mut self,\n        offset: u32,\n        data: &'a mut [u32],\n    ) -> Result<BackgroundRead<'a, 'd, T, FLASH_SIZE>, Error> {\n        trace!(\n            \"Reading in background from 0x{:x} to 0x{:x}\",\n            FLASH_BASE as u32 + offset,\n            FLASH_BASE as u32 + offset + (data.len() * 4) as u32\n        );\n        // Can't use check_read because we need to enforce 4-byte alignment\n        let offset = offset as usize;\n        let length = data.len() * 4;\n        if length > self.capacity() || offset > self.capacity() - length {\n            return Err(Error::OutOfBounds);\n        }\n        if offset % 4 != 0 {\n            return Err(Error::Unaligned);\n        }\n\n        while !pac::XIP_CTRL.stat().read().fifo_empty() {\n            pac::XIP_CTRL.stream_fifo().read();\n        }\n\n        pac::XIP_CTRL\n            .stream_addr()\n            .write_value(pac::xip_ctrl::regs::StreamAddr(FLASH_BASE as u32 + offset as u32));\n        pac::XIP_CTRL\n            .stream_ctr()\n            .write_value(pac::xip_ctrl::regs::StreamCtr(data.len() as u32));\n\n        // Use the XIP AUX bus port, rather than the FIFO register access (e.x.\n        // pac::XIP_CTRL.stream_fifo().as_ptr()) to avoid DMA stalling on\n        // general XIP access.\n        #[cfg(feature = \"rp2040\")]\n        const XIP_AUX_BASE: *const u32 = 0x50400000 as *const _;\n        #[cfg(feature = \"_rp235x\")]\n        const XIP_AUX_BASE: *const u32 = 0x50500000 as *const _;\n        let transfer = unsafe {\n            self.dma\n                .as_mut()\n                .unwrap()\n                .read(XIP_AUX_BASE, data, pac::dma::vals::TreqSel::XIP_STREAM, false)\n        };\n\n        Ok(BackgroundRead {\n            flash: PhantomData,\n            transfer,\n        })\n    }\n\n    /// Async read.\n    ///\n    /// The offset and buffer must be aligned.\n    ///\n    /// NOTE: `offset` is an offset from the flash start, NOT an absolute address.\n    pub async fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Error> {\n        use core::mem::MaybeUninit;\n\n        // Checked early to simplify address validity checks\n        if bytes.len() % 4 != 0 {\n            return Err(Error::Unaligned);\n        }\n\n        // If the destination address is already aligned, then we can just DMA directly\n        if (bytes.as_ptr() as u32) % 4 == 0 {\n            // Safety: alignment and size have been checked for compatibility\n            let buf: &mut [u32] =\n                unsafe { core::slice::from_raw_parts_mut(bytes.as_mut_ptr() as *mut u32, bytes.len() / 4) };\n            self.background_read(offset, buf)?.await;\n            return Ok(());\n        }\n\n        // Destination address is unaligned, so use an intermediate buffer\n        const REALIGN_CHUNK: usize = PAGE_SIZE;\n        // Safety: MaybeUninit requires no initialization\n        let mut buf: [MaybeUninit<u32>; REALIGN_CHUNK / 4] = unsafe { MaybeUninit::uninit().assume_init() };\n        let mut chunk_offset: usize = 0;\n        while chunk_offset < bytes.len() {\n            let chunk_size = (bytes.len() - chunk_offset).min(REALIGN_CHUNK);\n            let buf = &mut buf[..(chunk_size / 4)];\n\n            // Safety: this is written to completely by DMA before any reads\n            let buf = unsafe { &mut *(buf as *mut [MaybeUninit<u32>] as *mut [u32]) };\n            self.background_read(offset + chunk_offset as u32, buf)?.await;\n\n            // Safety: [u8] has more relaxed alignment and size requirements than [u32], so this is just aliasing\n            let buf = unsafe { core::slice::from_raw_parts(buf.as_ptr() as *const _, buf.len() * 4) };\n            bytes[chunk_offset..(chunk_offset + chunk_size)].copy_from_slice(&buf[..chunk_size]);\n\n            chunk_offset += chunk_size;\n        }\n\n        Ok(())\n    }\n}\n\nimpl<'d, T: Instance, M: Mode, const FLASH_SIZE: usize> ErrorType for Flash<'d, T, M, FLASH_SIZE> {\n    type Error = Error;\n}\n\nimpl<'d, T: Instance, M: Mode, const FLASH_SIZE: usize> ReadNorFlash for Flash<'d, T, M, FLASH_SIZE> {\n    const READ_SIZE: usize = READ_SIZE;\n\n    fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Self::Error> {\n        self.blocking_read(offset, bytes)\n    }\n\n    fn capacity(&self) -> usize {\n        self.capacity()\n    }\n}\n\nimpl<'d, T: Instance, M: Mode, const FLASH_SIZE: usize> MultiwriteNorFlash for Flash<'d, T, M, FLASH_SIZE> {}\n\nimpl<'d, T: Instance, const FLASH_SIZE: usize> embedded_storage_async::nor_flash::MultiwriteNorFlash\n    for Flash<'d, T, Async, FLASH_SIZE>\n{\n}\n\nimpl<'d, T: Instance, M: Mode, const FLASH_SIZE: usize> NorFlash for Flash<'d, T, M, FLASH_SIZE> {\n    const WRITE_SIZE: usize = WRITE_SIZE;\n\n    const ERASE_SIZE: usize = ERASE_SIZE;\n\n    fn erase(&mut self, from: u32, to: u32) -> Result<(), Self::Error> {\n        self.blocking_erase(from, to)\n    }\n\n    fn write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Self::Error> {\n        self.blocking_write(offset, bytes)\n    }\n}\n\nimpl<'d, T: Instance, const FLASH_SIZE: usize> embedded_storage_async::nor_flash::ReadNorFlash\n    for Flash<'d, T, Async, FLASH_SIZE>\n{\n    const READ_SIZE: usize = ASYNC_READ_SIZE;\n\n    async fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Self::Error> {\n        self.read(offset, bytes).await\n    }\n\n    fn capacity(&self) -> usize {\n        self.capacity()\n    }\n}\n\nimpl<'d, T: Instance, const FLASH_SIZE: usize> embedded_storage_async::nor_flash::NorFlash\n    for Flash<'d, T, Async, FLASH_SIZE>\n{\n    const WRITE_SIZE: usize = WRITE_SIZE;\n\n    const ERASE_SIZE: usize = ERASE_SIZE;\n\n    async fn erase(&mut self, from: u32, to: u32) -> Result<(), Self::Error> {\n        self.blocking_erase(from, to)\n    }\n\n    async fn write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Self::Error> {\n        self.blocking_write(offset, bytes)\n    }\n}\n\n#[allow(dead_code)]\nmod ram_helpers {\n    use super::*;\n    use crate::rom_data;\n\n    #[repr(C)]\n    struct FlashFunctionPointers<'a> {\n        connect_internal_flash: unsafe extern \"C\" fn() -> (),\n        flash_exit_xip: unsafe extern \"C\" fn() -> (),\n        flash_range_erase: Option<unsafe extern \"C\" fn(addr: u32, count: usize, block_size: u32, block_cmd: u8) -> ()>,\n        flash_range_program: Option<unsafe extern \"C\" fn(addr: u32, data: *const u8, count: usize) -> ()>,\n        flash_flush_cache: unsafe extern \"C\" fn() -> (),\n        flash_enter_cmd_xip: unsafe extern \"C\" fn() -> (),\n        phantom: PhantomData<&'a ()>,\n    }\n\n    #[allow(unused)]\n    fn flash_function_pointers(erase: bool, write: bool) -> FlashFunctionPointers<'static> {\n        FlashFunctionPointers {\n            connect_internal_flash: rom_data::connect_internal_flash::ptr(),\n            flash_exit_xip: rom_data::flash_exit_xip::ptr(),\n            flash_range_erase: if erase {\n                Some(rom_data::flash_range_erase::ptr())\n            } else {\n                None\n            },\n            flash_range_program: if write {\n                Some(rom_data::flash_range_program::ptr())\n            } else {\n                None\n            },\n            flash_flush_cache: rom_data::flash_flush_cache::ptr(),\n            flash_enter_cmd_xip: rom_data::flash_enter_cmd_xip::ptr(),\n            phantom: PhantomData,\n        }\n    }\n\n    #[allow(unused)]\n    /// # Safety\n    ///\n    /// `boot2` must contain a valid 2nd stage boot loader which can be called to re-initialize XIP mode\n    unsafe fn flash_function_pointers_with_boot2(\n        erase: bool,\n        write: bool,\n        boot2: &[u32; 64],\n    ) -> FlashFunctionPointers<'_> {\n        let boot2_fn_ptr = (boot2 as *const u32 as *const u8).offset(1);\n        let boot2_fn: unsafe extern \"C\" fn() -> () = core::mem::transmute(boot2_fn_ptr);\n        FlashFunctionPointers {\n            connect_internal_flash: rom_data::connect_internal_flash::ptr(),\n            flash_exit_xip: rom_data::flash_exit_xip::ptr(),\n            flash_range_erase: if erase {\n                Some(rom_data::flash_range_erase::ptr())\n            } else {\n                None\n            },\n            flash_range_program: if write {\n                Some(rom_data::flash_range_program::ptr())\n            } else {\n                None\n            },\n            flash_flush_cache: rom_data::flash_flush_cache::ptr(),\n            flash_enter_cmd_xip: boot2_fn,\n            phantom: PhantomData,\n        }\n    }\n\n    /// Erase a flash range starting at `addr` with length `len`.\n    ///\n    /// `addr` and `len` must be multiples of 4096\n    ///\n    /// If `USE_BOOT2` is `true`, a copy of the 2nd stage boot loader\n    /// is used to re-initialize the XIP engine after flashing.\n    ///\n    /// # Safety\n    ///\n    /// Nothing must access flash while this is running.\n    /// Usually this means:\n    ///   - interrupts must be disabled\n    ///   - 2nd core must be running code from RAM or ROM with interrupts disabled\n    ///   - DMA must not access flash memory\n    ///\n    /// `addr` and `len` parameters must be valid and are not checked.\n    pub unsafe fn flash_range_erase(addr: u32, len: u32) {\n        let mut boot2 = [0u32; 256 / 4];\n        let ptrs = if USE_BOOT2 {\n            #[cfg(feature = \"rp2040\")]\n            rom_data::memcpy44(&mut boot2 as *mut _, FLASH_BASE, 256);\n            #[cfg(feature = \"_rp235x\")]\n            core::ptr::copy_nonoverlapping(BOOTRAM_BASE as *const u8, boot2.as_mut_ptr() as *mut u8, 256);\n            flash_function_pointers_with_boot2(true, false, &boot2)\n        } else {\n            flash_function_pointers(true, false)\n        };\n\n        core::sync::atomic::compiler_fence(core::sync::atomic::Ordering::SeqCst);\n\n        write_flash_inner(addr, len, None, &ptrs as *const FlashFunctionPointers);\n    }\n\n    /// Erase and rewrite a flash range starting at `addr` with data `data`.\n    ///\n    /// `addr` and `data.len()` must be multiples of 4096\n    ///\n    /// If `USE_BOOT2` is `true`, a copy of the 2nd stage boot loader\n    /// is used to re-initialize the XIP engine after flashing.\n    ///\n    /// # Safety\n    ///\n    /// Nothing must access flash while this is running.\n    /// Usually this means:\n    ///   - interrupts must be disabled\n    ///   - 2nd core must be running code from RAM or ROM with interrupts disabled\n    ///   - DMA must not access flash memory\n    ///\n    /// `addr` and `len` parameters must be valid and are not checked.\n    pub unsafe fn flash_range_erase_and_program(addr: u32, data: &[u8]) {\n        let mut boot2 = [0u32; 256 / 4];\n        let ptrs = if USE_BOOT2 {\n            #[cfg(feature = \"rp2040\")]\n            rom_data::memcpy44(&mut boot2 as *mut _, FLASH_BASE, 256);\n            #[cfg(feature = \"_rp235x\")]\n            core::ptr::copy_nonoverlapping(BOOTRAM_BASE as *const u8, (boot2).as_mut_ptr() as *mut u8, 256);\n            flash_function_pointers_with_boot2(true, true, &boot2)\n        } else {\n            flash_function_pointers(true, true)\n        };\n\n        core::sync::atomic::compiler_fence(core::sync::atomic::Ordering::SeqCst);\n\n        write_flash_inner(\n            addr,\n            data.len() as u32,\n            Some(data),\n            &ptrs as *const FlashFunctionPointers,\n        );\n    }\n\n    /// Write a flash range starting at `addr` with data `data`.\n    ///\n    /// `addr` and `data.len()` must be multiples of 256\n    ///\n    /// If `USE_BOOT2` is `true`, a copy of the 2nd stage boot loader\n    /// is used to re-initialize the XIP engine after flashing.\n    ///\n    /// # Safety\n    ///\n    /// Nothing must access flash while this is running.\n    /// Usually this means:\n    ///   - interrupts must be disabled\n    ///   - 2nd core must be running code from RAM or ROM with interrupts disabled\n    ///   - DMA must not access flash memory\n    ///\n    /// `addr` and `len` parameters must be valid and are not checked.\n    pub unsafe fn flash_range_program(addr: u32, data: &[u8]) {\n        let mut boot2 = [0u32; 256 / 4];\n        let ptrs = if USE_BOOT2 {\n            #[cfg(feature = \"rp2040\")]\n            rom_data::memcpy44(&mut boot2 as *mut _, FLASH_BASE, 256);\n            #[cfg(feature = \"_rp235x\")]\n            core::ptr::copy_nonoverlapping(BOOTRAM_BASE as *const u8, boot2.as_mut_ptr() as *mut u8, 256);\n            flash_function_pointers_with_boot2(false, true, &boot2)\n        } else {\n            flash_function_pointers(false, true)\n        };\n\n        core::sync::atomic::compiler_fence(core::sync::atomic::Ordering::SeqCst);\n\n        write_flash_inner(\n            addr,\n            data.len() as u32,\n            Some(data),\n            &ptrs as *const FlashFunctionPointers,\n        );\n    }\n\n    /// # Safety\n    ///\n    /// Nothing must access flash while this is running.\n    /// Usually this means:\n    ///   - interrupts must be disabled\n    ///   - 2nd core must be running code from RAM or ROM with interrupts disabled\n    ///   - DMA must not access flash memory\n    /// Length of data must be a multiple of 4096\n    /// addr must be aligned to 4096\n    #[inline(never)]\n    #[unsafe(link_section = \".data.ram_func\")]\n    #[cfg(feature = \"rp2040\")]\n    unsafe fn write_flash_inner(addr: u32, len: u32, data: Option<&[u8]>, ptrs: *const FlashFunctionPointers) {\n        #[cfg(target_arch = \"arm\")]\n        core::arch::asm!(\n            \"mov r8, r0\",\n            \"mov r9, r2\",\n            \"mov r10, r1\",\n            \"ldr r4, [{ptrs}, #0]\",\n            \"blx r4\", // connect_internal_flash()\n\n            \"ldr r4, [{ptrs}, #4]\",\n            \"blx r4\", // flash_exit_xip()\n\n            \"mov r0, r8\", // r0 = addr\n            \"mov r1, r10\", // r1 = len\n            \"movs r2, #1\",\n            \"lsls r2, r2, #31\", // r2 = 1 << 31\n            \"movs r3, #0\", // r3 = 0\n            \"ldr r4, [{ptrs}, #8]\",\n            \"cmp r4, #0\",\n            \"beq 2f\",\n            \"blx r4\", // flash_range_erase(addr, len, 1 << 31, 0)\n            \"2:\",\n\n            \"mov r0, r8\", // r0 = addr\n            \"mov r1, r9\", // r0 = data\n            \"mov r2, r10\", // r2 = len\n            \"ldr r4, [{ptrs}, #12]\",\n            \"cmp r4, #0\",\n            \"beq 2f\",\n            \"blx r4\", // flash_range_program(addr, data, len);\n            \"2:\",\n\n            \"ldr r4, [{ptrs}, #16]\",\n            \"blx r4\", // flash_flush_cache();\n\n            \"ldr r4, [{ptrs}, #20]\",\n            \"blx r4\", // flash_enter_cmd_xip();\n            ptrs = in(reg) ptrs,\n            // Registers r8-r15 are not allocated automatically,\n            // so assign them manually. We need to use them as\n            // otherwise there are not enough registers available.\n            in(\"r0\") addr,\n            in(\"r2\") data.map(|d| d.as_ptr()).unwrap_or(core::ptr::null()),\n            in(\"r1\") len,\n            out(\"r3\") _,\n            out(\"r4\") _,\n            lateout(\"r8\") _,\n            lateout(\"r9\") _,\n            lateout(\"r10\") _,\n            clobber_abi(\"C\"),\n        );\n    }\n\n    /// # Safety\n    ///\n    /// Nothing must access flash while this is running.\n    /// Usually this means:\n    ///   - interrupts must be disabled\n    ///   - 2nd core must be running code from RAM or ROM with interrupts disabled\n    ///   - DMA must not access flash memory\n    /// Length of data must be a multiple of 4096\n    /// addr must be aligned to 4096\n    #[inline(never)]\n    #[unsafe(link_section = \".data.ram_func\")]\n    #[cfg(feature = \"_rp235x\")]\n    unsafe fn write_flash_inner(addr: u32, len: u32, data: Option<&[u8]>, ptrs: *const FlashFunctionPointers) {\n        let data = data.map(|d| d.as_ptr()).unwrap_or(core::ptr::null());\n        ((*ptrs).connect_internal_flash)();\n        ((*ptrs).flash_exit_xip)();\n        if (*ptrs).flash_range_erase.is_some() {\n            ((*ptrs).flash_range_erase.unwrap())(addr, len as usize, 1 << 31, 0);\n        }\n        if (*ptrs).flash_range_program.is_some() {\n            ((*ptrs).flash_range_program.unwrap())(addr, data as *const _, len as usize);\n        }\n        ((*ptrs).flash_flush_cache)();\n        ((*ptrs).flash_enter_cmd_xip)();\n    }\n\n    #[repr(C)]\n    struct FlashCommand {\n        cmd_addr: *const u8,\n        cmd_addr_len: u32,\n        dummy_len: u32,\n        data: *mut u8,\n        data_len: u32,\n    }\n\n    /// Return SPI flash unique ID\n    ///\n    /// Not all SPI flashes implement this command, so check the JEDEC\n    /// ID before relying on it. The Winbond parts commonly seen on\n    /// RP2040 devboards (JEDEC=0xEF7015) support an 8-byte unique ID;\n    /// https://forums.raspberrypi.com/viewtopic.php?t=331949 suggests\n    /// that LCSC (Zetta) parts have a 16-byte unique ID (which is\n    /// *not* unique in just its first 8 bytes),\n    /// JEDEC=0xBA6015. Macronix and Spansion parts do not have a\n    /// unique ID.\n    ///\n    /// The returned bytes are relatively predictable and should be\n    /// salted and hashed before use if that is an issue (e.g. for MAC\n    /// addresses).\n    ///\n    /// # Safety\n    ///\n    /// Nothing must access flash while this is running.\n    /// Usually this means:\n    ///   - interrupts must be disabled\n    ///   - 2nd core must be running code from RAM or ROM with interrupts disabled\n    ///   - DMA must not access flash memory\n    ///\n    /// Credit: taken from `rp2040-flash` (also licensed Apache+MIT)\n    #[cfg(feature = \"rp2040\")]\n    pub unsafe fn flash_unique_id(out: &mut [u8]) {\n        let mut boot2 = [0u32; 256 / 4];\n        let ptrs = if USE_BOOT2 {\n            rom_data::memcpy44(&mut boot2 as *mut _, FLASH_BASE, 256);\n            flash_function_pointers_with_boot2(false, false, &boot2)\n        } else {\n            flash_function_pointers(false, false)\n        };\n\n        // 4B - read unique ID\n        let cmd = [0x4B];\n        read_flash(&cmd[..], 4, out, &ptrs as *const FlashFunctionPointers);\n    }\n\n    /// Return SPI flash JEDEC ID\n    ///\n    /// This is the three-byte manufacturer-and-model identifier\n    /// commonly used to check before using manufacturer-specific SPI\n    /// flash features, e.g. 0xEF7015 for Winbond W25Q16JV.\n    ///\n    /// # Safety\n    ///\n    /// Nothing must access flash while this is running.\n    /// Usually this means:\n    ///   - interrupts must be disabled\n    ///   - 2nd core must be running code from RAM or ROM with interrupts disabled\n    ///   - DMA must not access flash memory\n    ///\n    /// Credit: taken from `rp2040-flash` (also licensed Apache+MIT)\n    #[cfg(feature = \"rp2040\")]\n    pub unsafe fn flash_jedec_id() -> u32 {\n        let mut boot2 = [0u32; 256 / 4];\n        let ptrs = if USE_BOOT2 {\n            rom_data::memcpy44(&mut boot2 as *mut _, FLASH_BASE, 256);\n            flash_function_pointers_with_boot2(false, false, &boot2)\n        } else {\n            flash_function_pointers(false, false)\n        };\n\n        let mut id = [0u8; 4];\n        // 9F - read JEDEC ID\n        let cmd = [0x9F];\n        read_flash(&cmd[..], 0, &mut id[1..4], &ptrs as *const FlashFunctionPointers);\n        u32::from_be_bytes(id)\n    }\n\n    #[cfg(feature = \"rp2040\")]\n    unsafe fn read_flash(cmd_addr: &[u8], dummy_len: u32, out: &mut [u8], ptrs: *const FlashFunctionPointers) {\n        read_flash_inner(\n            FlashCommand {\n                cmd_addr: cmd_addr.as_ptr(),\n                cmd_addr_len: cmd_addr.len() as u32,\n                dummy_len,\n                data: out.as_mut_ptr(),\n                data_len: out.len() as u32,\n            },\n            ptrs,\n        );\n    }\n\n    /// Issue a generic SPI flash read command\n    ///\n    /// # Arguments\n    ///\n    /// * `cmd` - `FlashCommand` structure\n    /// * `ptrs` - Flash function pointers as per `write_flash_inner`\n    ///\n    /// Credit: taken from `rp2040-flash` (also licensed Apache+MIT)\n    #[inline(never)]\n    #[unsafe(link_section = \".data.ram_func\")]\n    #[cfg(feature = \"rp2040\")]\n    unsafe fn read_flash_inner(cmd: FlashCommand, ptrs: *const FlashFunctionPointers) {\n        #[cfg(target_arch = \"arm\")]\n        core::arch::asm!(\n            \"mov r10, r0\", // cmd\n            \"mov r5, r1\", // ptrs\n\n            \"ldr r4, [r5, #0]\",\n            \"blx r4\", // connect_internal_flash()\n\n            \"ldr r4, [r5, #4]\",\n            \"blx r4\", // flash_exit_xip()\n\n\n            \"movs r4, #0x18\",\n            \"lsls r4, r4, #24\", // 0x18000000, SSI, RP2040 datasheet 4.10.13\n\n            // Disable, write 0 to SSIENR\n            \"movs r0, #0\",\n            \"str r0, [r4, #8]\", // SSIENR\n\n            // Write ctrlr0\n            \"movs r0, #0x3\",\n            \"lsls r0, r0, #8\", // TMOD=0x300\n            \"ldr r1, [r4, #0]\", // CTRLR0\n            \"orrs r1, r0\",\n            \"str r1, [r4, #0]\",\n\n            // Write ctrlr1 with len-1\n            \"mov r3, r10\", // cmd\n            \"ldr r0, [r3, #8]\", // dummy_len\n            \"ldr r1, [r3, #16]\", // data_len\n            \"add r0, r1\",\n            \"subs r0, #1\",\n            \"str r0, [r4, #0x04]\", // CTRLR1\n\n            // Enable, write 1 to ssienr\n            \"movs r0, #1\",\n            \"str r0, [r4, #8]\", // SSIENR\n\n            // Write cmd/addr phase to DR\n            \"mov r2, r4\",\n            \"adds r2, 0x60\", // &DR\n            \"ldr r0, [r3, #0]\", // cmd_addr\n            \"ldr r1, [r3, #4]\", // cmd_addr_len\n            \"3:\",\n            \"ldrb r3, [r0]\",\n            \"strb r3, [r2]\", // DR\n            \"adds r0, #1\",\n            \"subs r1, #1\",\n            \"bne 3b\",\n\n            // Skip any dummy cycles\n            \"mov r3, r10\", // cmd\n            \"ldr r1, [r3, #8]\", // dummy_len\n            \"cmp r1, #0\",\n            \"beq 9f\",\n            \"4:\",\n            \"ldr r3, [r4, #0x28]\", // SR\n            \"movs r2, #0x8\",\n            \"tst r3, r2\", // SR.RFNE\n            \"beq 4b\",\n\n            \"mov r2, r4\",\n            \"adds r2, 0x60\", // &DR\n            \"ldrb r3, [r2]\", // DR\n            \"subs r1, #1\",\n            \"bne 4b\",\n\n            // Read RX fifo\n            \"9:\",\n            \"mov r2, r10\", // cmd\n            \"ldr r0, [r2, #12]\", // data\n            \"ldr r1, [r2, #16]\", // data_len\n\n            \"2:\",\n            \"ldr r3, [r4, #0x28]\", // SR\n            \"movs r2, #0x8\",\n            \"tst r3, r2\", // SR.RFNE\n            \"beq 2b\",\n\n            \"mov r2, r4\",\n            \"adds r2, 0x60\", // &DR\n            \"ldrb r3, [r2]\", // DR\n            \"strb r3, [r0]\",\n            \"adds r0, #1\",\n            \"subs r1, #1\",\n            \"bne 2b\",\n\n            // Disable, write 0 to ssienr\n            \"movs r0, #0\",\n            \"str r0, [r4, #8]\", // SSIENR\n\n            // Write 0 to CTRLR1 (returning to its default value)\n            //\n            // flash_enter_cmd_xip does NOT do this, and everything goes\n            // wrong unless we do it here\n            \"str r0, [r4, #4]\", // CTRLR1\n\n            \"ldr r4, [r5, #20]\",\n            \"blx r4\", // flash_enter_cmd_xip();\n\n            in(\"r0\") &cmd as *const FlashCommand,\n            in(\"r1\") ptrs,\n            out(\"r2\") _,\n            out(\"r3\") _,\n            out(\"r4\") _,\n            out(\"r5\") _,\n            // Registers r8-r10 are used to store values\n            // from r0-r2 in registers not clobbered by\n            // function calls.\n            // The values can't be passed in using r8-r10 directly\n            // due to https://github.com/rust-lang/rust/issues/99071\n            out(\"r10\") _,\n            clobber_abi(\"C\"),\n        );\n    }\n}\n\n/// Make sure to uphold the contract points with rp2040-flash.\n/// - interrupts must be disabled\n/// - DMA must not access flash memory\npub(crate) unsafe fn in_ram(operation: impl FnOnce()) -> Result<(), Error> {\n    // Make sure we're running on CORE0\n    let core_id: u32 = pac::SIO.cpuid().read();\n    if core_id != 0 {\n        return Err(Error::InvalidCore);\n    }\n\n    // Make sure CORE1 is paused during the entire duration of the RAM function\n    crate::multicore::pause_core1();\n\n    critical_section::with(|_| {\n        // Wait for all DMA channels in flash to finish before ram operation\n        const SRAM_LOWER: u32 = 0x2000_0000;\n        for n in 0..crate::dma::CHANNEL_COUNT {\n            let ch = crate::pac::DMA.ch(n);\n            while ch.read_addr().read() < SRAM_LOWER && ch.ctrl_trig().read().busy() {}\n        }\n        // Wait for completion of any background reads\n        while pac::XIP_CTRL.stream_ctr().read().0 > 0 {}\n\n        // Run our flash operation in RAM\n        operation();\n    });\n\n    // Resume CORE1 execution\n    crate::multicore::resume_core1();\n    Ok(())\n}\n\ntrait SealedInstance {}\ntrait SealedMode {}\n\n/// Flash instance.\n#[allow(private_bounds)]\npub trait Instance: SealedInstance + PeripheralType {}\n/// Flash mode.\n#[allow(private_bounds)]\npub trait Mode: SealedMode {}\n\nimpl SealedInstance for FLASH {}\nimpl Instance for FLASH {}\n\nmacro_rules! impl_mode {\n    ($name:ident) => {\n        impl SealedMode for $name {}\n        impl Mode for $name {}\n    };\n}\n\n/// Flash blocking mode.\npub struct Blocking;\n/// Flash async mode.\npub struct Async;\n\nimpl_mode!(Blocking);\nimpl_mode!(Async);\n"
  },
  {
    "path": "embassy-rp/src/float/add_sub.rs",
    "content": "// Credit: taken from `rp-hal` (also licensed Apache+MIT)\n// https://github.com/rp-rs/rp-hal/blob/main/rp2040-hal/src/float/add_sub.rs\n\nuse super::{Float, Int};\nuse crate::rom_data;\n\ntrait ROMAdd {\n    fn rom_add(self, b: Self) -> Self;\n}\n\nimpl ROMAdd for f32 {\n    fn rom_add(self, b: Self) -> Self {\n        rom_data::float_funcs::fadd(self, b)\n    }\n}\n\nimpl ROMAdd for f64 {\n    fn rom_add(self, b: Self) -> Self {\n        rom_data::double_funcs::dadd(self, b)\n    }\n}\n\nfn add<F: Float + ROMAdd>(a: F, b: F) -> F {\n    if a.is_not_finite() {\n        if b.is_not_finite() {\n            let class_a = a.repr() & (F::SIGNIFICAND_MASK | F::SIGN_MASK);\n            let class_b = b.repr() & (F::SIGNIFICAND_MASK | F::SIGN_MASK);\n\n            if class_a == F::Int::ZERO && class_b == F::Int::ZERO {\n                // inf + inf = inf\n                return a;\n            }\n            if class_a == F::SIGN_MASK && class_b == F::SIGN_MASK {\n                // -inf + (-inf) = -inf\n                return a;\n            }\n\n            // Sign mismatch, or either is NaN already\n            return F::NAN;\n        }\n\n        // [-]inf/NaN + X = [-]inf/NaN\n        return a;\n    }\n\n    if b.is_not_finite() {\n        // X + [-]inf/NaN = [-]inf/NaN\n        return b;\n    }\n\n    a.rom_add(b)\n}\n\nintrinsics! {\n    #[alias = __addsf3vfp]\n    #[aeabi = __aeabi_fadd]\n    extern \"C\" fn __addsf3(a: f32, b: f32) -> f32 {\n        add(a, b)\n    }\n\n    #[bootrom_v2]\n    #[alias = __adddf3vfp]\n    #[aeabi = __aeabi_dadd]\n    extern \"C\" fn __adddf3(a: f64, b: f64) -> f64 {\n        add(a, b)\n    }\n\n    // The ROM just implements subtraction the same way, so just do it here\n    // and save the work of implementing more complicated NaN/inf handling.\n\n    #[alias = __subsf3vfp]\n    #[aeabi = __aeabi_fsub]\n    extern \"C\" fn __subsf3(a: f32, b: f32) -> f32 {\n        add(a, -b)\n    }\n\n    #[bootrom_v2]\n    #[alias = __subdf3vfp]\n    #[aeabi = __aeabi_dsub]\n    extern \"C\" fn __subdf3(a: f64, b: f64) -> f64 {\n        add(a, -b)\n    }\n\n    extern \"aapcs\" fn __aeabi_frsub(a: f32, b: f32) -> f32 {\n        add(b, -a)\n    }\n\n    #[bootrom_v2]\n    extern \"aapcs\" fn __aeabi_drsub(a: f64, b: f64) -> f64 {\n        add(b, -a)\n    }\n}\n"
  },
  {
    "path": "embassy-rp/src/float/cmp.rs",
    "content": "// Credit: taken from `rp-hal` (also licensed Apache+MIT)\n// https://github.com/rp-rs/rp-hal/blob/main/rp2040-hal/src/float/cmp.rs\n\nuse super::Float;\nuse crate::rom_data;\n\ntrait ROMCmp {\n    fn rom_cmp(self, b: Self) -> i32;\n}\n\nimpl ROMCmp for f32 {\n    fn rom_cmp(self, b: Self) -> i32 {\n        rom_data::float_funcs::fcmp(self, b)\n    }\n}\n\nimpl ROMCmp for f64 {\n    fn rom_cmp(self, b: Self) -> i32 {\n        rom_data::double_funcs::dcmp(self, b)\n    }\n}\n\nfn le_abi<F: Float + ROMCmp>(a: F, b: F) -> i32 {\n    if a.is_nan() || b.is_nan() { 1 } else { a.rom_cmp(b) }\n}\n\nfn ge_abi<F: Float + ROMCmp>(a: F, b: F) -> i32 {\n    if a.is_nan() || b.is_nan() { -1 } else { a.rom_cmp(b) }\n}\n\nintrinsics! {\n    #[slower_than_default]\n    #[bootrom_v2]\n    #[alias = __eqsf2, __ltsf2, __nesf2]\n    extern \"C\" fn __lesf2(a: f32, b: f32) -> i32 {\n        le_abi(a, b)\n    }\n\n    #[slower_than_default]\n    #[bootrom_v2]\n    #[alias = __eqdf2, __ltdf2, __nedf2]\n    extern \"C\" fn __ledf2(a: f64, b: f64) -> i32 {\n        le_abi(a, b)\n    }\n\n    #[slower_than_default]\n    #[bootrom_v2]\n    #[alias = __gtsf2]\n    extern \"C\" fn __gesf2(a: f32, b: f32) -> i32 {\n        ge_abi(a, b)\n    }\n\n    #[slower_than_default]\n    #[bootrom_v2]\n    #[alias = __gtdf2]\n    extern \"C\" fn __gedf2(a: f64, b: f64) -> i32 {\n        ge_abi(a, b)\n    }\n\n\n    #[slower_than_default]\n    #[bootrom_v2]\n    extern \"aapcs\" fn __aeabi_fcmple(a: f32, b: f32) -> i32 {\n        (le_abi(a, b) <= 0) as i32\n    }\n\n    #[slower_than_default]\n    #[bootrom_v2]\n    extern \"aapcs\" fn __aeabi_fcmpge(a: f32, b: f32) -> i32 {\n        (ge_abi(a, b) >= 0) as i32\n    }\n\n    #[slower_than_default]\n    #[bootrom_v2]\n    extern \"aapcs\" fn __aeabi_fcmpeq(a: f32, b: f32) -> i32 {\n        (le_abi(a, b) == 0) as i32\n    }\n\n    #[slower_than_default]\n    #[bootrom_v2]\n    extern \"aapcs\" fn __aeabi_fcmplt(a: f32, b: f32) -> i32 {\n        (le_abi(a, b) < 0) as i32\n    }\n\n    #[slower_than_default]\n    #[bootrom_v2]\n    extern \"aapcs\" fn __aeabi_fcmpgt(a: f32, b: f32) -> i32 {\n        (ge_abi(a, b) > 0) as i32\n    }\n\n    #[slower_than_default]\n    #[bootrom_v2]\n    extern \"aapcs\" fn __aeabi_dcmple(a: f64, b: f64) -> i32 {\n        (le_abi(a, b) <= 0) as i32\n    }\n\n    #[slower_than_default]\n    #[bootrom_v2]\n    extern \"aapcs\" fn __aeabi_dcmpge(a: f64, b: f64) -> i32 {\n        (ge_abi(a, b) >= 0) as i32\n    }\n\n    #[slower_than_default]\n    #[bootrom_v2]\n    extern \"aapcs\" fn __aeabi_dcmpeq(a: f64, b: f64) -> i32 {\n        (le_abi(a, b) == 0) as i32\n    }\n\n    #[slower_than_default]\n    #[bootrom_v2]\n    extern \"aapcs\" fn __aeabi_dcmplt(a: f64, b: f64) -> i32 {\n        (le_abi(a, b) < 0) as i32\n    }\n\n    #[slower_than_default]\n    #[bootrom_v2]\n    extern \"aapcs\" fn __aeabi_dcmpgt(a: f64, b: f64) -> i32 {\n        (ge_abi(a, b) > 0) as i32\n    }\n\n\n    #[slower_than_default]\n    #[bootrom_v2]\n    extern \"C\" fn __gesf2vfp(a: f32, b: f32) -> i32 {\n        (ge_abi(a, b) >= 0) as i32\n    }\n\n    #[slower_than_default]\n    #[bootrom_v2]\n    extern \"C\" fn __gedf2vfp(a: f64, b: f64) -> i32 {\n        (ge_abi(a, b) >= 0) as i32\n    }\n\n    #[slower_than_default]\n    #[bootrom_v2]\n    extern \"C\" fn __gtsf2vfp(a: f32, b: f32) -> i32 {\n        (ge_abi(a, b) > 0) as i32\n    }\n\n    #[slower_than_default]\n    #[bootrom_v2]\n    extern \"C\" fn __gtdf2vfp(a: f64, b: f64) -> i32 {\n        (ge_abi(a, b) > 0) as i32\n    }\n\n    #[slower_than_default]\n    #[bootrom_v2]\n    extern \"C\" fn __ltsf2vfp(a: f32, b: f32) -> i32 {\n        (le_abi(a, b) < 0) as i32\n    }\n\n    #[slower_than_default]\n    #[bootrom_v2]\n    extern \"C\" fn __ltdf2vfp(a: f64, b: f64) -> i32 {\n        (le_abi(a, b) < 0) as i32\n    }\n\n    #[slower_than_default]\n    #[bootrom_v2]\n    extern \"C\" fn __lesf2vfp(a: f32, b: f32) -> i32 {\n        (le_abi(a, b) <= 0) as i32\n    }\n\n    #[slower_than_default]\n    #[bootrom_v2]\n    extern \"C\" fn __ledf2vfp(a: f64, b: f64) -> i32 {\n        (le_abi(a, b) <= 0) as i32\n    }\n\n    #[slower_than_default]\n    #[bootrom_v2]\n    extern \"C\" fn __nesf2vfp(a: f32, b: f32) -> i32 {\n        (le_abi(a, b) != 0) as i32\n    }\n\n    #[slower_than_default]\n    #[bootrom_v2]\n    extern \"C\" fn __nedf2vfp(a: f64, b: f64) -> i32 {\n        (le_abi(a, b) != 0) as i32\n    }\n\n    #[slower_than_default]\n    #[bootrom_v2]\n    extern \"C\" fn __eqsf2vfp(a: f32, b: f32) -> i32 {\n        (le_abi(a, b) == 0) as i32\n    }\n\n    #[slower_than_default]\n    #[bootrom_v2]\n    extern \"C\" fn __eqdf2vfp(a: f64, b: f64) -> i32 {\n        (le_abi(a, b) == 0) as i32\n    }\n}\n"
  },
  {
    "path": "embassy-rp/src/float/conv.rs",
    "content": "// Credit: taken from `rp-hal` (also licensed Apache+MIT)\n// https://github.com/rp-rs/rp-hal/blob/main/rp2040-hal/src/float/conv.rs\n\nuse super::Float;\nuse crate::rom_data;\n\n// Some of these are also not connected in the Pico SDK.  This is probably\n// because the ROM version actually does a fixed point conversion, just with\n// the fractional width set to zero.\n\nintrinsics! {\n    // Not connected in the Pico SDK\n    #[slower_than_default]\n    #[aeabi = __aeabi_i2f]\n    extern \"C\" fn __floatsisf(i: i32) -> f32 {\n        rom_data::float_funcs::int_to_float(i)\n    }\n\n    // Not connected in the Pico SDK\n    #[slower_than_default]\n    #[aeabi = __aeabi_i2d]\n    extern \"C\" fn __floatsidf(i: i32) -> f64 {\n        rom_data::double_funcs::int_to_double(i)\n    }\n\n    // Questionable gain\n    #[aeabi = __aeabi_l2f]\n    extern \"C\" fn __floatdisf(i: i64) -> f32 {\n        rom_data::float_funcs::int64_to_float(i)\n    }\n\n    #[bootrom_v2]\n    #[aeabi = __aeabi_l2d]\n    extern \"C\" fn __floatdidf(i: i64) -> f64 {\n        rom_data::double_funcs::int64_to_double(i)\n    }\n\n    // Not connected in the Pico SDK\n    #[slower_than_default]\n    #[aeabi = __aeabi_ui2f]\n    extern \"C\" fn __floatunsisf(i: u32) -> f32 {\n        rom_data::float_funcs::uint_to_float(i)\n    }\n\n    // Questionable gain\n    #[bootrom_v2]\n    #[aeabi = __aeabi_ui2d]\n    extern \"C\" fn __floatunsidf(i: u32) -> f64 {\n        rom_data::double_funcs::uint_to_double(i)\n    }\n\n    // Questionable gain\n    #[bootrom_v2]\n    #[aeabi = __aeabi_ul2f]\n    extern \"C\" fn __floatundisf(i: u64) -> f32 {\n        rom_data::float_funcs::uint64_to_float(i)\n    }\n\n    #[bootrom_v2]\n    #[aeabi = __aeabi_ul2d]\n    extern \"C\" fn __floatundidf(i: u64) -> f64 {\n        rom_data::double_funcs::uint64_to_double(i)\n    }\n\n\n    // The Pico SDK does some optimization here (e.x. fast paths for zero and\n    // one), but we can just directly connect it.\n    #[aeabi = __aeabi_f2iz]\n    extern \"C\" fn __fixsfsi(f: f32) -> i32 {\n        rom_data::float_funcs::float_to_int(f)\n    }\n\n    #[bootrom_v2]\n    #[aeabi = __aeabi_f2lz]\n    extern \"C\" fn __fixsfdi(f: f32) -> i64 {\n        rom_data::float_funcs::float_to_int64(f)\n    }\n\n    // Not connected in the Pico SDK\n    #[slower_than_default]\n    #[bootrom_v2]\n    #[aeabi = __aeabi_d2iz]\n    extern \"C\" fn __fixdfsi(f: f64) -> i32 {\n        rom_data::double_funcs::double_to_int(f)\n    }\n\n    // Like with the 32 bit version, there's optimization that we just\n    // skip.\n    #[bootrom_v2]\n    #[aeabi = __aeabi_d2lz]\n    extern \"C\" fn __fixdfdi(f: f64) -> i64 {\n        rom_data::double_funcs::double_to_int64(f)\n    }\n\n    #[slower_than_default]\n    #[aeabi = __aeabi_f2uiz]\n    extern \"C\" fn __fixunssfsi(f: f32) -> u32 {\n        rom_data::float_funcs::float_to_uint(f)\n    }\n\n    #[slower_than_default]\n    #[bootrom_v2]\n    #[aeabi = __aeabi_f2ulz]\n    extern \"C\" fn __fixunssfdi(f: f32) -> u64 {\n        rom_data::float_funcs::float_to_uint64(f)\n    }\n\n    #[slower_than_default]\n    #[bootrom_v2]\n    #[aeabi = __aeabi_d2uiz]\n    extern \"C\" fn __fixunsdfsi(f: f64) -> u32 {\n        rom_data::double_funcs::double_to_uint(f)\n    }\n\n    #[slower_than_default]\n    #[bootrom_v2]\n    #[aeabi = __aeabi_d2ulz]\n    extern \"C\" fn __fixunsdfdi(f: f64) -> u64 {\n        rom_data::double_funcs::double_to_uint64(f)\n    }\n\n    #[bootrom_v2]\n    #[alias = __extendsfdf2vfp]\n    #[aeabi = __aeabi_f2d]\n    extern \"C\" fn  __extendsfdf2(f: f32) -> f64 {\n        if f.is_not_finite() {\n            return f64::from_repr(\n                // Not finite\n                f64::EXPONENT_MASK |\n                // Preserve NaN or inf\n                ((f.repr() & f32::SIGNIFICAND_MASK) as u64) |\n                // Preserve sign\n                ((f.repr() & f32::SIGN_MASK) as u64) << (f64::BITS-f32::BITS)\n            );\n        }\n        rom_data::float_funcs::float_to_double(f)\n    }\n\n    #[bootrom_v2]\n    #[alias = __truncdfsf2vfp]\n    #[aeabi = __aeabi_d2f]\n    extern \"C\" fn __truncdfsf2(f: f64) -> f32 {\n        if f.is_not_finite() {\n            let mut repr: u32 =\n                // Not finite\n                f32::EXPONENT_MASK |\n                // Preserve sign\n                ((f.repr() & f64::SIGN_MASK) >> (f64::BITS-f32::BITS)) as u32;\n            // Set NaN\n            if  (f.repr() & f64::SIGNIFICAND_MASK) != 0 {\n                repr |= 1;\n            }\n            return f32::from_repr(repr);\n        }\n        rom_data::double_funcs::double_to_float(f)\n    }\n}\n"
  },
  {
    "path": "embassy-rp/src/float/div.rs",
    "content": "// Credit: taken from `rp-hal` (also licensed Apache+MIT)\n// https://github.com/rp-rs/rp-hal/blob/main/rp2040-hal/src/float/conv.rs\n\nuse super::Float;\nuse crate::rom_data;\n\n// Make sure this stays as a separate call, because when it's inlined the\n// compiler will move the save of the registers used to contain the divider\n// state into the function prologue.  That save and restore (push/pop) takes\n// longer than the actual division, so doing it in the common case where\n// they are not required wastes a lot of time.\n#[inline(never)]\n#[cold]\nfn save_divider_and_call<F, R>(f: F) -> R\nwhere\n    F: FnOnce() -> R,\n{\n    let sio = rp_pac::SIO;\n\n    // Since we can't save the signed-ness of the calculation, we have to make\n    // sure that there's at least an 8 cycle delay before we read the result.\n    // The Pico SDK ensures this by using a 6 cycle push and two 1 cycle reads.\n    // Since we can't be sure the Rust implementation will optimize to the same,\n    // just use an explicit wait.\n    while !sio.div().csr().read().ready() {}\n\n    // Read the quotient last, since that's what clears the dirty flag\n    let dividend = sio.div().udividend().read();\n    let divisor = sio.div().udivisor().read();\n    let remainder = sio.div().remainder().read();\n    let quotient = sio.div().quotient().read();\n\n    // If we get interrupted here (before a write sets the DIRTY flag) its fine, since\n    // we have the full state, so the interruptor doesn't have to restore it.  Once the\n    // write happens and the DIRTY flag is set, the interruptor becomes responsible for\n    // restoring our state.\n    let result = f();\n\n    // If we are interrupted here, then the interruptor will start an incorrect calculation\n    // using a wrong divisor, but we'll restore the divisor and result ourselves correctly.\n    // This sets DIRTY, so any interruptor will save the state.\n    sio.div().udividend().write_value(dividend);\n    // If we are interrupted here, the interruptor may start the calculation using\n    // incorrectly signed inputs, but we'll restore the result ourselves.\n    // This sets DIRTY, so any interruptor will save the state.\n    sio.div().udivisor().write_value(divisor);\n    // If we are interrupted here, the interruptor will have restored everything but the\n    // quotient may be wrongly signed.  If the calculation started by the above writes is\n    // still ongoing it is stopped, so it won't replace the result we're restoring.\n    // DIRTY and READY set, but only DIRTY matters to make the interruptor save the state.\n    sio.div().remainder().write_value(remainder);\n    // State fully restored after the quotient write.  This sets both DIRTY and READY, so\n    // whatever we may have interrupted can read the result.\n    sio.div().quotient().write_value(quotient);\n\n    result\n}\n\nfn save_divider<F, R>(f: F) -> R\nwhere\n    F: FnOnce() -> R,\n{\n    let sio = rp_pac::SIO;\n    if !sio.div().csr().read().dirty() {\n        // Not dirty, so nothing is waiting for the calculation.  So we can just\n        // issue it directly without a save/restore.\n        f()\n    } else {\n        save_divider_and_call(f)\n    }\n}\n\ntrait ROMDiv {\n    fn rom_div(self, b: Self) -> Self;\n}\n\nimpl ROMDiv for f32 {\n    fn rom_div(self, b: Self) -> Self {\n        // ROM implementation uses the hardware divider, so we have to save it\n        save_divider(|| rom_data::float_funcs::fdiv(self, b))\n    }\n}\n\nimpl ROMDiv for f64 {\n    fn rom_div(self, b: Self) -> Self {\n        // ROM implementation uses the hardware divider, so we have to save it\n        save_divider(|| rom_data::double_funcs::ddiv(self, b))\n    }\n}\n\nfn div<F: Float + ROMDiv>(a: F, b: F) -> F {\n    if a.is_not_finite() {\n        if b.is_not_finite() {\n            // inf/NaN / inf/NaN = NaN\n            return F::NAN;\n        }\n\n        if b.is_zero() {\n            // inf/NaN / 0 = NaN\n            return F::NAN;\n        }\n\n        return if b.is_sign_negative() {\n            // [+/-]inf/NaN / (-X) = [-/+]inf/NaN\n            a.negate()\n        } else {\n            // [-]inf/NaN / X = [-]inf/NaN\n            a\n        };\n    }\n\n    if b.is_nan() {\n        // X / NaN = NaN\n        return b;\n    }\n\n    // ROM handles X / 0 = [-]inf and X / [-]inf = [-]0, so we only\n    // need to catch 0 / 0\n    if b.is_zero() && a.is_zero() {\n        return F::NAN;\n    }\n\n    a.rom_div(b)\n}\n\nintrinsics! {\n    #[alias = __divsf3vfp]\n    #[aeabi = __aeabi_fdiv]\n    extern \"C\" fn __divsf3(a: f32, b: f32) -> f32 {\n        div(a, b)\n    }\n\n    #[bootrom_v2]\n    #[alias = __divdf3vfp]\n    #[aeabi = __aeabi_ddiv]\n    extern \"C\" fn __divdf3(a: f64, b: f64) -> f64 {\n        div(a, b)\n    }\n}\n"
  },
  {
    "path": "embassy-rp/src/float/functions.rs",
    "content": "// Credit: taken from `rp-hal` (also licensed Apache+MIT)\n// https://github.com/rp-rs/rp-hal/blob/main/rp2040-hal/src/float/functions.rs\n\nuse crate::float::{Float, Int};\nuse crate::rom_data;\n\ntrait ROMFunctions {\n    fn sqrt(self) -> Self;\n    fn ln(self) -> Self;\n    fn exp(self) -> Self;\n    fn sin(self) -> Self;\n    fn cos(self) -> Self;\n    fn tan(self) -> Self;\n    fn atan2(self, y: Self) -> Self;\n\n    fn to_trig_range(self) -> Self;\n}\n\nimpl ROMFunctions for f32 {\n    fn sqrt(self) -> Self {\n        rom_data::float_funcs::fsqrt(self)\n    }\n\n    fn ln(self) -> Self {\n        rom_data::float_funcs::fln(self)\n    }\n\n    fn exp(self) -> Self {\n        rom_data::float_funcs::fexp(self)\n    }\n\n    fn sin(self) -> Self {\n        rom_data::float_funcs::fsin(self)\n    }\n\n    fn cos(self) -> Self {\n        rom_data::float_funcs::fcos(self)\n    }\n\n    fn tan(self) -> Self {\n        rom_data::float_funcs::ftan(self)\n    }\n\n    fn atan2(self, y: Self) -> Self {\n        rom_data::float_funcs::fatan2(self, y)\n    }\n\n    fn to_trig_range(self) -> Self {\n        // -128 < X < 128, logic from the Pico SDK\n        let exponent = (self.repr() & Self::EXPONENT_MASK) >> Self::SIGNIFICAND_BITS;\n        if exponent < 134 {\n            self\n        } else {\n            self % (core::f32::consts::PI * 2.0)\n        }\n    }\n}\n\nimpl ROMFunctions for f64 {\n    fn sqrt(self) -> Self {\n        rom_data::double_funcs::dsqrt(self)\n    }\n\n    fn ln(self) -> Self {\n        rom_data::double_funcs::dln(self)\n    }\n\n    fn exp(self) -> Self {\n        rom_data::double_funcs::dexp(self)\n    }\n\n    fn sin(self) -> Self {\n        rom_data::double_funcs::dsin(self)\n    }\n\n    fn cos(self) -> Self {\n        rom_data::double_funcs::dcos(self)\n    }\n    fn tan(self) -> Self {\n        rom_data::double_funcs::dtan(self)\n    }\n\n    fn atan2(self, y: Self) -> Self {\n        rom_data::double_funcs::datan2(self, y)\n    }\n\n    fn to_trig_range(self) -> Self {\n        // -1024 < X < 1024, logic from the Pico SDK\n        let exponent = (self.repr() & Self::EXPONENT_MASK) >> Self::SIGNIFICAND_BITS;\n        if exponent < 1033 {\n            self\n        } else {\n            self % (core::f64::consts::PI * 2.0)\n        }\n    }\n}\n\nfn is_negative_nonzero_or_nan<F: Float>(f: F) -> bool {\n    let repr = f.repr();\n    if (repr & F::SIGN_MASK) != F::Int::ZERO {\n        // Negative, so anything other than exactly zero\n        return (repr & (!F::SIGN_MASK)) != F::Int::ZERO;\n    }\n    // NaN\n    (repr & (F::EXPONENT_MASK | F::SIGNIFICAND_MASK)) > F::EXPONENT_MASK\n}\n\nfn sqrt<F: Float + ROMFunctions>(f: F) -> F {\n    if is_negative_nonzero_or_nan(f) {\n        F::NAN\n    } else {\n        f.sqrt()\n    }\n}\n\nfn ln<F: Float + ROMFunctions>(f: F) -> F {\n    if is_negative_nonzero_or_nan(f) { F::NAN } else { f.ln() }\n}\n\nfn exp<F: Float + ROMFunctions>(f: F) -> F {\n    if f.is_nan() { F::NAN } else { f.exp() }\n}\n\nfn sin<F: Float + ROMFunctions>(f: F) -> F {\n    if f.is_not_finite() {\n        F::NAN\n    } else {\n        f.to_trig_range().sin()\n    }\n}\n\nfn cos<F: Float + ROMFunctions>(f: F) -> F {\n    if f.is_not_finite() {\n        F::NAN\n    } else {\n        f.to_trig_range().cos()\n    }\n}\n\nfn tan<F: Float + ROMFunctions>(f: F) -> F {\n    if f.is_not_finite() {\n        F::NAN\n    } else {\n        f.to_trig_range().tan()\n    }\n}\n\nfn atan2<F: Float + ROMFunctions>(x: F, y: F) -> F {\n    if x.is_nan() || y.is_nan() {\n        F::NAN\n    } else {\n        x.to_trig_range().atan2(y)\n    }\n}\n\n// Name collisions\nmod intrinsics {\n    intrinsics! {\n        extern \"C\" fn sqrtf(f: f32) -> f32 {\n            super::sqrt(f)\n        }\n\n        #[bootrom_v2]\n        extern \"C\" fn sqrt(f: f64) -> f64 {\n            super::sqrt(f)\n        }\n\n        extern \"C\" fn logf(f: f32) -> f32 {\n            super::ln(f)\n        }\n\n        #[bootrom_v2]\n        extern \"C\" fn log(f: f64) -> f64 {\n            super::ln(f)\n        }\n\n        extern \"C\" fn expf(f: f32) -> f32 {\n            super::exp(f)\n        }\n\n        #[bootrom_v2]\n        extern \"C\" fn exp(f: f64) -> f64 {\n            super::exp(f)\n        }\n\n        #[slower_than_default]\n        extern \"C\" fn sinf(f: f32) -> f32 {\n            super::sin(f)\n        }\n\n        #[slower_than_default]\n        #[bootrom_v2]\n        extern \"C\" fn sin(f: f64) -> f64 {\n            super::sin(f)\n        }\n\n        #[slower_than_default]\n        extern \"C\" fn cosf(f: f32) -> f32 {\n            super::cos(f)\n        }\n\n        #[slower_than_default]\n        #[bootrom_v2]\n        extern \"C\" fn cos(f: f64) -> f64 {\n            super::cos(f)\n        }\n\n        #[slower_than_default]\n        extern \"C\" fn tanf(f: f32) -> f32 {\n            super::tan(f)\n        }\n\n        #[slower_than_default]\n        #[bootrom_v2]\n        extern \"C\" fn tan(f: f64) -> f64 {\n            super::tan(f)\n        }\n\n        // Questionable gain\n        #[bootrom_v2]\n        extern \"C\" fn atan2f(a: f32, b: f32) -> f32 {\n            super::atan2(a, b)\n        }\n\n        // Questionable gain\n        #[bootrom_v2]\n        extern \"C\" fn atan2(a: f64, b: f64) -> f64 {\n            super::atan2(a, b)\n        }\n    }\n}\n"
  },
  {
    "path": "embassy-rp/src/float/mod.rs",
    "content": "// Credit: taken from `rp-hal` (also licensed Apache+MIT)\n// https://github.com/rp-rs/rp-hal/blob/main/rp2040-hal/src/float/mod.rs\n\nuse core::ops;\n\n// Borrowed and simplified from compiler-builtins so we can use bit ops\n// on floating point without macro soup.\npub(crate) trait Int:\n    Copy\n    + core::fmt::Debug\n    + PartialEq\n    + PartialOrd\n    + ops::AddAssign\n    + ops::SubAssign\n    + ops::BitAndAssign\n    + ops::BitOrAssign\n    + ops::BitXorAssign\n    + ops::ShlAssign<i32>\n    + ops::ShrAssign<u32>\n    + ops::Add<Output = Self>\n    + ops::Sub<Output = Self>\n    + ops::Div<Output = Self>\n    + ops::Shl<u32, Output = Self>\n    + ops::Shr<u32, Output = Self>\n    + ops::BitOr<Output = Self>\n    + ops::BitXor<Output = Self>\n    + ops::BitAnd<Output = Self>\n    + ops::Not<Output = Self>\n{\n    const ZERO: Self;\n}\n\nmacro_rules! int_impl {\n    ($ty:ty) => {\n        impl Int for $ty {\n            const ZERO: Self = 0;\n        }\n    };\n}\n\nint_impl!(u32);\nint_impl!(u64);\n\npub(crate) trait Float:\n    Copy\n    + core::fmt::Debug\n    + PartialEq\n    + PartialOrd\n    + ops::AddAssign\n    + ops::MulAssign\n    + ops::Add<Output = Self>\n    + ops::Sub<Output = Self>\n    + ops::Div<Output = Self>\n    + ops::Rem<Output = Self>\n{\n    /// A uint of the same with as the float\n    type Int: Int;\n\n    /// NaN representation for the float\n    const NAN: Self;\n\n    /// The bitwidth of the float type\n    const BITS: u32;\n\n    /// The bitwidth of the significand\n    const SIGNIFICAND_BITS: u32;\n\n    /// A mask for the sign bit\n    const SIGN_MASK: Self::Int;\n\n    /// A mask for the significand\n    const SIGNIFICAND_MASK: Self::Int;\n\n    /// A mask for the exponent\n    const EXPONENT_MASK: Self::Int;\n\n    /// Returns `self` transmuted to `Self::Int`\n    fn repr(self) -> Self::Int;\n\n    /// Returns a `Self::Int` transmuted back to `Self`\n    fn from_repr(a: Self::Int) -> Self;\n\n    /// Return a sign swapped `self`\n    fn negate(self) -> Self;\n\n    /// Returns true if `self` is either NaN or infinity\n    fn is_not_finite(self) -> bool {\n        (self.repr() & Self::EXPONENT_MASK) == Self::EXPONENT_MASK\n    }\n\n    /// Returns true if `self` is infinity\n    #[allow(unused)]\n    fn is_infinity(self) -> bool {\n        (self.repr() & (Self::EXPONENT_MASK | Self::SIGNIFICAND_MASK)) == Self::EXPONENT_MASK\n    }\n\n    /// Returns true if `self is NaN\n    fn is_nan(self) -> bool {\n        (self.repr() & (Self::EXPONENT_MASK | Self::SIGNIFICAND_MASK)) > Self::EXPONENT_MASK\n    }\n\n    /// Returns true if `self` is negative\n    fn is_sign_negative(self) -> bool {\n        (self.repr() & Self::SIGN_MASK) != Self::Int::ZERO\n    }\n\n    /// Returns true if `self` is zero (either sign)\n    fn is_zero(self) -> bool {\n        (self.repr() & (Self::SIGNIFICAND_MASK | Self::EXPONENT_MASK)) == Self::Int::ZERO\n    }\n}\n\nmacro_rules! float_impl {\n    ($ty:ident, $ity:ident, $bits:expr, $significand_bits:expr) => {\n        impl Float for $ty {\n            type Int = $ity;\n\n            const NAN: Self = <$ty>::NAN;\n\n            const BITS: u32 = $bits;\n            const SIGNIFICAND_BITS: u32 = $significand_bits;\n\n            const SIGN_MASK: Self::Int = 1 << (Self::BITS - 1);\n            const SIGNIFICAND_MASK: Self::Int = (1 << Self::SIGNIFICAND_BITS) - 1;\n            const EXPONENT_MASK: Self::Int = !(Self::SIGN_MASK | Self::SIGNIFICAND_MASK);\n\n            fn repr(self) -> Self::Int {\n                self.to_bits()\n            }\n\n            fn from_repr(a: Self::Int) -> Self {\n                Self::from_bits(a)\n            }\n\n            fn negate(self) -> Self {\n                -self\n            }\n        }\n    };\n}\n\nfloat_impl!(f32, u32, 32, 23);\nfloat_impl!(f64, u64, 64, 52);\n\nmod add_sub;\nmod cmp;\nmod conv;\nmod div;\nmod functions;\nmod mul;\n"
  },
  {
    "path": "embassy-rp/src/float/mul.rs",
    "content": "// Credit: taken from `rp-hal` (also licensed Apache+MIT)\n// https://github.com/rp-rs/rp-hal/blob/main/rp2040-hal/src/float/mul.rs\n\nuse super::Float;\nuse crate::rom_data;\n\ntrait ROMMul {\n    fn rom_mul(self, b: Self) -> Self;\n}\n\nimpl ROMMul for f32 {\n    fn rom_mul(self, b: Self) -> Self {\n        rom_data::float_funcs::fmul(self, b)\n    }\n}\n\nimpl ROMMul for f64 {\n    fn rom_mul(self, b: Self) -> Self {\n        rom_data::double_funcs::dmul(self, b)\n    }\n}\n\nfn mul<F: Float + ROMMul>(a: F, b: F) -> F {\n    if a.is_not_finite() {\n        if b.is_zero() {\n            // [-]inf/NaN * 0 = NaN\n            return F::NAN;\n        }\n\n        return if b.is_sign_negative() {\n            // [+/-]inf/NaN * (-X) = [-/+]inf/NaN\n            a.negate()\n        } else {\n            // [-]inf/NaN * X = [-]inf/NaN\n            a\n        };\n    }\n\n    if b.is_not_finite() {\n        if a.is_zero() {\n            // 0 * [-]inf/NaN = NaN\n            return F::NAN;\n        }\n\n        return if b.is_sign_negative() {\n            // (-X) * [+/-]inf/NaN = [-/+]inf/NaN\n            b.negate()\n        } else {\n            // X * [-]inf/NaN = [-]inf/NaN\n            b\n        };\n    }\n\n    a.rom_mul(b)\n}\n\nintrinsics! {\n    #[alias = __mulsf3vfp]\n    #[aeabi = __aeabi_fmul]\n    extern \"C\" fn __mulsf3(a: f32, b: f32) -> f32 {\n        mul(a, b)\n    }\n\n    #[bootrom_v2]\n    #[alias = __muldf3vfp]\n    #[aeabi = __aeabi_dmul]\n    extern \"C\" fn __muldf3(a: f64, b: f64) -> f64 {\n        mul(a, b)\n    }\n}\n"
  },
  {
    "path": "embassy-rp/src/fmt.rs",
    "content": "#![macro_use]\n#![allow(unused)]\n\nuse core::fmt::{Debug, Display, LowerHex};\n\n#[cfg(all(feature = \"defmt\", feature = \"log\"))]\ncompile_error!(\"You may not enable both `defmt` and `log` features.\");\n\n#[collapse_debuginfo(yes)]\nmacro_rules! assert {\n    ($($x:tt)*) => {\n        {\n            #[cfg(not(feature = \"defmt\"))]\n            ::core::assert!($($x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::assert!($($x)*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! assert_eq {\n    ($($x:tt)*) => {\n        {\n            #[cfg(not(feature = \"defmt\"))]\n            ::core::assert_eq!($($x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::assert_eq!($($x)*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! assert_ne {\n    ($($x:tt)*) => {\n        {\n            #[cfg(not(feature = \"defmt\"))]\n            ::core::assert_ne!($($x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::assert_ne!($($x)*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! debug_assert {\n    ($($x:tt)*) => {\n        {\n            #[cfg(not(feature = \"defmt\"))]\n            ::core::debug_assert!($($x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::debug_assert!($($x)*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! debug_assert_eq {\n    ($($x:tt)*) => {\n        {\n            #[cfg(not(feature = \"defmt\"))]\n            ::core::debug_assert_eq!($($x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::debug_assert_eq!($($x)*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! debug_assert_ne {\n    ($($x:tt)*) => {\n        {\n            #[cfg(not(feature = \"defmt\"))]\n            ::core::debug_assert_ne!($($x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::debug_assert_ne!($($x)*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! todo {\n    ($($x:tt)*) => {\n        {\n            #[cfg(not(feature = \"defmt\"))]\n            ::core::todo!($($x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::todo!($($x)*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! unreachable {\n    ($($x:tt)*) => {\n        {\n            #[cfg(not(feature = \"defmt\"))]\n            ::core::unreachable!($($x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::unreachable!($($x)*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! panic {\n    ($($x:tt)*) => {\n        {\n            #[cfg(not(feature = \"defmt\"))]\n            ::core::panic!($($x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::panic!($($x)*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! trace {\n    ($s:literal $(, $x:expr)* $(,)?) => {\n        {\n            #[cfg(feature = \"log\")]\n            ::log::trace!($s $(, $x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::trace!($s $(, $x)*);\n            #[cfg(not(any(feature = \"log\", feature=\"defmt\")))]\n            let _ = ($( & $x ),*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! debug {\n    ($s:literal $(, $x:expr)* $(,)?) => {\n        {\n            #[cfg(feature = \"log\")]\n            ::log::debug!($s $(, $x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::debug!($s $(, $x)*);\n            #[cfg(not(any(feature = \"log\", feature=\"defmt\")))]\n            let _ = ($( & $x ),*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! info {\n    ($s:literal $(, $x:expr)* $(,)?) => {\n        {\n            #[cfg(feature = \"log\")]\n            ::log::info!($s $(, $x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::info!($s $(, $x)*);\n            #[cfg(not(any(feature = \"log\", feature=\"defmt\")))]\n            let _ = ($( & $x ),*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! warn {\n    ($s:literal $(, $x:expr)* $(,)?) => {\n        {\n            #[cfg(feature = \"log\")]\n            ::log::warn!($s $(, $x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::warn!($s $(, $x)*);\n            #[cfg(not(any(feature = \"log\", feature=\"defmt\")))]\n            let _ = ($( & $x ),*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! error {\n    ($s:literal $(, $x:expr)* $(,)?) => {\n        {\n            #[cfg(feature = \"log\")]\n            ::log::error!($s $(, $x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::error!($s $(, $x)*);\n            #[cfg(not(any(feature = \"log\", feature=\"defmt\")))]\n            let _ = ($( & $x ),*);\n        }\n    };\n}\n\n#[cfg(feature = \"defmt\")]\n#[collapse_debuginfo(yes)]\nmacro_rules! unwrap {\n    ($($x:tt)*) => {\n        ::defmt::unwrap!($($x)*)\n    };\n}\n\n#[cfg(not(feature = \"defmt\"))]\n#[collapse_debuginfo(yes)]\nmacro_rules! unwrap {\n    ($arg:expr) => {\n        match $crate::fmt::Try::into_result($arg) {\n            ::core::result::Result::Ok(t) => t,\n            ::core::result::Result::Err(e) => {\n                ::core::panic!(\"unwrap of `{}` failed: {:?}\", ::core::stringify!($arg), e);\n            }\n        }\n    };\n    ($arg:expr, $($msg:expr),+ $(,)? ) => {\n        match $crate::fmt::Try::into_result($arg) {\n            ::core::result::Result::Ok(t) => t,\n            ::core::result::Result::Err(e) => {\n                ::core::panic!(\"unwrap of `{}` failed: {}: {:?}\", ::core::stringify!($arg), ::core::format_args!($($msg,)*), e);\n            }\n        }\n    }\n}\n\n#[derive(Debug, Copy, Clone, Eq, PartialEq)]\npub struct NoneError;\n\npub trait Try {\n    type Ok;\n    type Error;\n    fn into_result(self) -> Result<Self::Ok, Self::Error>;\n}\n\nimpl<T> Try for Option<T> {\n    type Ok = T;\n    type Error = NoneError;\n\n    #[inline]\n    fn into_result(self) -> Result<T, NoneError> {\n        self.ok_or(NoneError)\n    }\n}\n\nimpl<T, E> Try for Result<T, E> {\n    type Ok = T;\n    type Error = E;\n\n    #[inline]\n    fn into_result(self) -> Self {\n        self\n    }\n}\n\npub(crate) struct Bytes<'a>(pub &'a [u8]);\n\nimpl<'a> Debug for Bytes<'a> {\n    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {\n        write!(f, \"{:#02x?}\", self.0)\n    }\n}\n\nimpl<'a> Display for Bytes<'a> {\n    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {\n        write!(f, \"{:#02x?}\", self.0)\n    }\n}\n\nimpl<'a> LowerHex for Bytes<'a> {\n    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {\n        write!(f, \"{:#02x?}\", self.0)\n    }\n}\n\n#[cfg(feature = \"defmt\")]\nimpl<'a> defmt::Format for Bytes<'a> {\n    fn format(&self, fmt: defmt::Formatter) {\n        defmt::write!(fmt, \"{:02x}\", self.0)\n    }\n}\n"
  },
  {
    "path": "embassy-rp/src/gpio.rs",
    "content": "//! GPIO driver.\n#![macro_use]\nuse core::convert::Infallible;\nuse core::future::Future;\nuse core::pin::Pin as FuturePin;\nuse core::task::{Context, Poll};\n\nuse embassy_hal_internal::{Peri, PeripheralType, impl_peripheral};\nuse embassy_sync::waitqueue::AtomicWaker;\n\nuse crate::interrupt::InterruptExt;\nuse crate::pac::SIO;\nuse crate::pac::common::{RW, Reg};\nuse crate::{RegExt, interrupt, pac, peripherals};\n\n#[cfg(any(feature = \"rp2040\", feature = \"rp235xa\"))]\npub(crate) const BANK0_PIN_COUNT: usize = 30;\n#[cfg(feature = \"rp235xb\")]\npub(crate) const BANK0_PIN_COUNT: usize = 48;\n\nstatic BANK0_WAKERS: [AtomicWaker; BANK0_PIN_COUNT] = [const { AtomicWaker::new() }; BANK0_PIN_COUNT];\n#[cfg(feature = \"qspi-as-gpio\")]\nconst QSPI_PIN_COUNT: usize = 6;\n#[cfg(feature = \"qspi-as-gpio\")]\nstatic QSPI_WAKERS: [AtomicWaker; QSPI_PIN_COUNT] = [const { AtomicWaker::new() }; QSPI_PIN_COUNT];\n\n/// Represents a digital input or output level.\n#[derive(Debug, Eq, PartialEq, Clone, Copy)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub enum Level {\n    /// Logical low.\n    Low,\n    /// Logical high.\n    High,\n}\n\nimpl From<bool> for Level {\n    fn from(val: bool) -> Self {\n        match val {\n            true => Self::High,\n            false => Self::Low,\n        }\n    }\n}\n\nimpl From<Level> for bool {\n    fn from(level: Level) -> bool {\n        match level {\n            Level::Low => false,\n            Level::High => true,\n        }\n    }\n}\n\n/// Represents a pull setting for an input.\n#[derive(Debug, Clone, Copy, Eq, PartialEq)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub enum Pull {\n    /// No pull.\n    None,\n    /// Internal pull-up resistor.\n    Up,\n    /// Internal pull-down resistor.\n    Down,\n}\n\n/// Drive strength of an output\n#[derive(Debug, Eq, PartialEq)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub enum Drive {\n    /// 2 mA drive.\n    _2mA,\n    /// 4 mA drive.\n    _4mA,\n    /// 8 mA drive.\n    _8mA,\n    /// 1 2mA drive.\n    _12mA,\n}\n/// Slew rate of an output\n#[derive(Debug, Eq, PartialEq)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub enum SlewRate {\n    /// Fast slew rate.\n    Fast,\n    /// Slow slew rate.\n    Slow,\n}\n\n/// A GPIO bank with up to 32 pins.\n#[derive(Debug, Eq, PartialEq)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub enum Bank {\n    /// Bank 0.\n    Bank0 = 0,\n    /// QSPI.\n    #[cfg(feature = \"qspi-as-gpio\")]\n    Qspi = 1,\n}\n\n/// Dormant mode config.\n#[derive(Debug, Eq, PartialEq, Copy, Clone, Default)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub struct DormantWakeConfig {\n    /// Wake on edge high.\n    pub edge_high: bool,\n    /// Wake on edge low.\n    pub edge_low: bool,\n    /// Wake on level high.\n    pub level_high: bool,\n    /// Wake on level low.\n    pub level_low: bool,\n}\n\n/// GPIO input driver.\n#[derive(Debug)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub struct Input<'d> {\n    pin: Flex<'d>,\n}\n\nimpl<'d> Input<'d> {\n    /// Create GPIO input driver for a [Pin] with the provided [Pull] configuration.\n    #[inline]\n    pub fn new(pin: Peri<'d, impl Pin>, pull: Pull) -> Self {\n        let mut pin = Flex::new(pin);\n        pin.set_as_input();\n        pin.set_pull(pull);\n        Self { pin }\n    }\n\n    /// Set the pin's Schmitt trigger.\n    #[inline]\n    pub fn set_schmitt(&mut self, enable: bool) {\n        self.pin.set_schmitt(enable)\n    }\n\n    /// Get whether the pin input level is high.\n    #[inline]\n    pub fn is_high(&self) -> bool {\n        self.pin.is_high()\n    }\n\n    /// Get whether the pin input level is low.\n    #[inline]\n    pub fn is_low(&self) -> bool {\n        self.pin.is_low()\n    }\n\n    /// Returns current pin level\n    #[inline]\n    pub fn get_level(&self) -> Level {\n        self.pin.get_level()\n    }\n\n    /// Configure the input logic inversion of this pin.\n    #[inline]\n    pub fn set_inversion(&mut self, invert: bool) {\n        self.pin.set_input_inversion(invert)\n    }\n\n    /// Wait until the pin is high. If it is already high, return immediately.\n    #[inline]\n    pub async fn wait_for_high(&mut self) {\n        self.pin.wait_for_high().await;\n    }\n\n    /// Wait until the pin is low. If it is already low, return immediately.\n    #[inline]\n    pub async fn wait_for_low(&mut self) {\n        self.pin.wait_for_low().await;\n    }\n\n    /// Wait for the pin to undergo a transition from low to high.\n    #[inline]\n    pub async fn wait_for_rising_edge(&mut self) {\n        self.pin.wait_for_rising_edge().await;\n    }\n\n    /// Wait for the pin to undergo a transition from high to low.\n    #[inline]\n    pub async fn wait_for_falling_edge(&mut self) {\n        self.pin.wait_for_falling_edge().await;\n    }\n\n    /// Wait for the pin to undergo any transition, i.e low to high OR high to low.\n    #[inline]\n    pub async fn wait_for_any_edge(&mut self) {\n        self.pin.wait_for_any_edge().await;\n    }\n\n    /// Configure dormant wake.\n    #[inline]\n    pub fn dormant_wake(&mut self, cfg: DormantWakeConfig) -> DormantWake<'_> {\n        self.pin.dormant_wake(cfg)\n    }\n\n    /// Set the pin's pad isolation\n    #[cfg(feature = \"_rp235x\")]\n    #[inline]\n    pub fn set_pad_isolation(&mut self, isolate: bool) {\n        self.pin.set_pad_isolation(isolate)\n    }\n}\n\n/// Interrupt trigger levels.\n#[derive(Debug, Eq, PartialEq, Copy, Clone)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub enum InterruptTrigger {\n    /// Trigger on pin low.\n    LevelLow,\n    /// Trigger on pin high.\n    LevelHigh,\n    /// Trigger on high to low transition.\n    EdgeLow,\n    /// Trigger on low to high transition.\n    EdgeHigh,\n    /// Trigger on any transition.\n    AnyEdge,\n}\n\npub(crate) unsafe fn init() {\n    interrupt::IO_IRQ_BANK0.disable();\n    interrupt::IO_IRQ_BANK0.set_priority(interrupt::Priority::P3);\n    interrupt::IO_IRQ_BANK0.enable();\n\n    #[cfg(feature = \"qspi-as-gpio\")]\n    {\n        interrupt::IO_IRQ_QSPI.disable();\n        interrupt::IO_IRQ_QSPI.set_priority(interrupt::Priority::P3);\n        interrupt::IO_IRQ_QSPI.enable();\n    }\n}\n\n#[cfg(feature = \"rt\")]\nfn irq_handler<const N: usize>(bank: pac::io::Io, wakers: &[AtomicWaker; N]) {\n    let cpu = SIO.cpuid().read() as usize;\n    // There are two sets of interrupt registers, one for cpu0 and one for cpu1\n    // and here we are selecting the set that belongs to the currently executing\n    // cpu.\n    let proc_intx: pac::io::Int = bank.int_proc(cpu);\n    for pin in 0..N {\n        // There are 4 raw interrupt status registers, PROCx_INTS0, PROCx_INTS1,\n        // PROCx_INTS2, and PROCx_INTS3, and we are selecting the one that the\n        // current pin belongs to.\n        let intsx = proc_intx.ints(pin / 8);\n        // The status register is divided into groups of four, one group for\n        // each pin. Each group consists of four trigger levels LEVEL_LOW,\n        // LEVEL_HIGH, EDGE_LOW, and EDGE_HIGH for each pin.\n        let pin_group = pin % 8;\n        let event = (intsx.read().0 >> (pin_group * 4)) & 0xf;\n\n        // no more than one event can be awaited per pin at any given time, so\n        // we can just clear all interrupt enables for that pin without having\n        // to check which event was signalled.\n        if event != 0 {\n            proc_intx.inte(pin / 8).write_clear(|w| {\n                w.set_edge_high(pin_group, true);\n                w.set_edge_low(pin_group, true);\n                w.set_level_high(pin_group, true);\n                w.set_level_low(pin_group, true);\n            });\n            wakers[pin].wake();\n        }\n    }\n}\n\n#[cfg(feature = \"rt\")]\n#[interrupt]\nfn IO_IRQ_BANK0() {\n    irq_handler(pac::IO_BANK0, &BANK0_WAKERS);\n}\n\n#[cfg(all(feature = \"rt\", feature = \"qspi-as-gpio\"))]\n#[interrupt]\nfn IO_IRQ_QSPI() {\n    irq_handler(pac::IO_QSPI, &QSPI_WAKERS);\n}\n\n#[must_use = \"futures do nothing unless you `.await` or poll them\"]\nstruct InputFuture<'d> {\n    pin: Peri<'d, AnyPin>,\n}\n\nimpl<'d> InputFuture<'d> {\n    fn new(pin: Peri<'d, AnyPin>, level: InterruptTrigger) -> Self {\n        let pin_group = (pin.pin() % 8) as usize;\n        // first, clear the INTR register bits. without this INTR will still\n        // contain reports of previous edges, causing the IRQ to fire early\n        // on stale state. clearing these means that we can only detect edges\n        // that occur *after* the clear happened, but since both this and the\n        // alternative are fundamentally racy it's probably fine.\n        // (the alternative being checking the current level and waiting for\n        // its inverse, but that requires reading the current level and thus\n        // missing anything that happened before the level was read.)\n        pin.io().intr(pin.pin() as usize / 8).write(|w| {\n            w.set_edge_high(pin_group, true);\n            w.set_edge_low(pin_group, true);\n        });\n\n        // Each INTR register is divided into 8 groups, one group for each\n        // pin, and each group consists of LEVEL_LOW, LEVEL_HIGH, EDGE_LOW,\n        // and EDGE_HIGH.\n        pin.int_proc()\n            .inte((pin.pin() / 8) as usize)\n            .write_set(|w| match level {\n                InterruptTrigger::LevelHigh => {\n                    w.set_level_high(pin_group, true);\n                }\n                InterruptTrigger::LevelLow => {\n                    w.set_level_low(pin_group, true);\n                }\n                InterruptTrigger::EdgeHigh => {\n                    w.set_edge_high(pin_group, true);\n                }\n                InterruptTrigger::EdgeLow => {\n                    w.set_edge_low(pin_group, true);\n                }\n                InterruptTrigger::AnyEdge => {\n                    w.set_edge_high(pin_group, true);\n                    w.set_edge_low(pin_group, true);\n                }\n            });\n\n        Self { pin }\n    }\n}\n\nimpl<'d> Future for InputFuture<'d> {\n    type Output = ();\n\n    fn poll(self: FuturePin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {\n        // We need to register/re-register the waker for each poll because any\n        // calls to wake will deregister the waker.\n        let waker = match self.pin.bank() {\n            Bank::Bank0 => &BANK0_WAKERS[self.pin.pin() as usize],\n            #[cfg(feature = \"qspi-as-gpio\")]\n            Bank::Qspi => &QSPI_WAKERS[self.pin.pin() as usize],\n        };\n        waker.register(cx.waker());\n\n        // self.int_proc() will get the register offset for the current cpu,\n        // then we want to access the interrupt enable register for our\n        // pin (there are 4 of these PROC0_INTE0, PROC0_INTE1, PROC0_INTE2, and\n        // PROC0_INTE3 per cpu).\n        let inte: pac::io::regs::Int = self.pin.int_proc().inte((self.pin.pin() / 8) as usize).read();\n        // The register is divided into groups of four, one group for\n        // each pin. Each group consists of four trigger levels LEVEL_LOW,\n        // LEVEL_HIGH, EDGE_LOW, and EDGE_HIGH for each pin.\n        let pin_group = (self.pin.pin() % 8) as usize;\n\n        // since the interrupt handler clears all INTE flags we'll check that\n        // all have been cleared and unconditionally return Ready(()) if so.\n        // we don't need further handshaking since only a single event wait\n        // is possible for any given pin at any given time.\n        if !inte.edge_high(pin_group)\n            && !inte.edge_low(pin_group)\n            && !inte.level_high(pin_group)\n            && !inte.level_low(pin_group)\n        {\n            return Poll::Ready(());\n        }\n        Poll::Pending\n    }\n}\n\n/// GPIO output driver.\n#[derive(Debug)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub struct Output<'d> {\n    pin: Flex<'d>,\n}\n\nimpl<'d> Output<'d> {\n    /// Create GPIO output driver for a [Pin] with the provided [Level].\n    #[inline]\n    pub fn new(pin: Peri<'d, impl Pin>, initial_output: Level) -> Self {\n        let mut pin = Flex::new(pin);\n        match initial_output {\n            Level::High => pin.set_high(),\n            Level::Low => pin.set_low(),\n        }\n\n        pin.set_as_output();\n        Self { pin }\n    }\n\n    /// Set the pin's drive strength.\n    #[inline]\n    pub fn set_drive_strength(&mut self, strength: Drive) {\n        self.pin.set_drive_strength(strength)\n    }\n\n    /// Set the pin's slew rate.\n    #[inline]\n    pub fn set_slew_rate(&mut self, slew_rate: SlewRate) {\n        self.pin.set_slew_rate(slew_rate)\n    }\n\n    /// Configure the output logic inversion of this pin.\n    #[inline]\n    pub fn set_inversion(&mut self, invert: bool) {\n        self.pin.set_output_inversion(invert)\n    }\n\n    /// Set the output as high.\n    #[inline]\n    pub fn set_high(&mut self) {\n        self.pin.set_high()\n    }\n\n    /// Set the output as low.\n    #[inline]\n    pub fn set_low(&mut self) {\n        self.pin.set_low()\n    }\n\n    /// Set the output level.\n    #[inline]\n    pub fn set_level(&mut self, level: Level) {\n        self.pin.set_level(level)\n    }\n\n    /// Is the output pin set as high?\n    #[inline]\n    pub fn is_set_high(&self) -> bool {\n        self.pin.is_set_high()\n    }\n\n    /// Is the output pin set as low?\n    #[inline]\n    pub fn is_set_low(&self) -> bool {\n        self.pin.is_set_low()\n    }\n\n    /// What level output is set to\n    #[inline]\n    pub fn get_output_level(&self) -> Level {\n        self.pin.get_output_level()\n    }\n\n    /// Toggle pin output\n    #[inline]\n    pub fn toggle(&mut self) {\n        self.pin.toggle()\n    }\n\n    /// Set the pin's pad isolation\n    #[cfg(feature = \"_rp235x\")]\n    #[inline]\n    pub fn set_pad_isolation(&mut self, isolate: bool) {\n        self.pin.set_pad_isolation(isolate)\n    }\n}\n\n/// GPIO output open-drain.\n#[derive(Debug)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub struct OutputOpenDrain<'d> {\n    pin: Flex<'d>,\n}\n\nimpl<'d> OutputOpenDrain<'d> {\n    /// Create GPIO output driver for a [Pin] in open drain mode with the provided [Level].\n    #[inline]\n    pub fn new(pin: Peri<'d, impl Pin>, initial_output: Level) -> Self {\n        let mut pin = Flex::new(pin);\n        pin.set_low();\n        match initial_output {\n            Level::High => pin.set_as_input(),\n            Level::Low => pin.set_as_output(),\n        }\n        Self { pin }\n    }\n\n    /// Set the pin's pull-up.\n    #[inline]\n    pub fn set_pullup(&mut self, enable: bool) {\n        if enable {\n            self.pin.set_pull(Pull::Up);\n        } else {\n            self.pin.set_pull(Pull::None);\n        }\n    }\n\n    /// Set the pin's drive strength.\n    #[inline]\n    pub fn set_drive_strength(&mut self, strength: Drive) {\n        self.pin.set_drive_strength(strength)\n    }\n\n    /// Set the pin's slew rate.\n    #[inline]\n    pub fn set_slew_rate(&mut self, slew_rate: SlewRate) {\n        self.pin.set_slew_rate(slew_rate)\n    }\n\n    /// Set the output as high.\n    #[inline]\n    pub fn set_high(&mut self) {\n        // For Open Drain High, disable the output pin.\n        self.pin.set_as_input()\n    }\n\n    /// Set the output as low.\n    #[inline]\n    pub fn set_low(&mut self) {\n        // For Open Drain Low, enable the output pin.\n        self.pin.set_as_output()\n    }\n\n    /// Set the output level.\n    #[inline]\n    pub fn set_level(&mut self, level: Level) {\n        match level {\n            Level::Low => self.set_low(),\n            Level::High => self.set_high(),\n        }\n    }\n\n    /// Is the output level high?\n    #[inline]\n    pub fn is_set_high(&self) -> bool {\n        !self.is_set_low()\n    }\n\n    /// Is the output level low?\n    #[inline]\n    pub fn is_set_low(&self) -> bool {\n        self.pin.is_set_as_output()\n    }\n\n    /// What level output is set to\n    #[inline]\n    pub fn get_output_level(&self) -> Level {\n        self.is_set_high().into()\n    }\n\n    /// Toggle pin output\n    #[inline]\n    pub fn toggle(&mut self) {\n        self.pin.toggle_set_as_output()\n    }\n\n    /// Get whether the pin input level is high.\n    #[inline]\n    pub fn is_high(&self) -> bool {\n        self.pin.is_high()\n    }\n\n    /// Get whether the pin input level is low.\n    #[inline]\n    pub fn is_low(&self) -> bool {\n        self.pin.is_low()\n    }\n\n    /// Returns current pin level\n    #[inline]\n    pub fn get_level(&self) -> Level {\n        self.is_high().into()\n    }\n\n    /// Wait until the pin is high. If it is already high, return immediately.\n    #[inline]\n    pub async fn wait_for_high(&mut self) {\n        self.pin.wait_for_high().await;\n    }\n\n    /// Wait until the pin is low. If it is already low, return immediately.\n    #[inline]\n    pub async fn wait_for_low(&mut self) {\n        self.pin.wait_for_low().await;\n    }\n\n    /// Wait for the pin to undergo a transition from low to high.\n    #[inline]\n    pub async fn wait_for_rising_edge(&mut self) {\n        self.pin.wait_for_rising_edge().await;\n    }\n\n    /// Wait for the pin to undergo a transition from high to low.\n    #[inline]\n    pub async fn wait_for_falling_edge(&mut self) {\n        self.pin.wait_for_falling_edge().await;\n    }\n\n    /// Wait for the pin to undergo any transition, i.e low to high OR high to low.\n    #[inline]\n    pub async fn wait_for_any_edge(&mut self) {\n        self.pin.wait_for_any_edge().await;\n    }\n\n    /// Set the pin's pad isolation\n    #[cfg(feature = \"_rp235x\")]\n    #[inline]\n    pub fn set_pad_isolation(&mut self, isolate: bool) {\n        self.pin.set_pad_isolation(isolate)\n    }\n}\n\n/// GPIO flexible pin.\n///\n/// This pin can be either an input or output pin. The output level register bit will remain\n/// set while not in output mode, so the pin's level will be 'remembered' when it is not in output\n/// mode.\n#[derive(Debug)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub struct Flex<'d> {\n    pin: Peri<'d, AnyPin>,\n}\n\nimpl<'d> Flex<'d> {\n    /// Wrap the pin in a `Flex`.\n    ///\n    /// The pin remains disconnected. The initial output level is unspecified, but can be changed\n    /// before the pin is put into output mode.\n    #[inline]\n    pub fn new(pin: Peri<'d, impl Pin>) -> Self {\n        pin.pad_ctrl().write(|w| {\n            #[cfg(feature = \"_rp235x\")]\n            w.set_iso(false);\n            w.set_ie(true);\n        });\n\n        pin.gpio().ctrl().write(|w| {\n            #[cfg(feature = \"rp2040\")]\n            w.set_funcsel(pac::io::vals::Gpio0ctrlFuncsel::SIO_0 as _);\n            #[cfg(feature = \"_rp235x\")]\n            w.set_funcsel(pac::io::vals::Gpio0ctrlFuncsel::SIOB_PROC_0 as _);\n        });\n\n        Self { pin: pin.into() }\n    }\n\n    #[inline]\n    fn bit(&self) -> u32 {\n        1 << (self.pin.pin() % 32)\n    }\n\n    /// Set the pin's pull.\n    #[inline]\n    pub fn set_pull(&mut self, pull: Pull) {\n        self.pin.pad_ctrl().modify(|w| {\n            w.set_ie(true);\n            let (pu, pd) = match pull {\n                Pull::Up => (true, false),\n                Pull::Down => (false, true),\n                Pull::None => (false, false),\n            };\n            w.set_pue(pu);\n            w.set_pde(pd);\n        });\n    }\n\n    /// Set the pin's drive strength.\n    #[inline]\n    pub fn set_drive_strength(&mut self, strength: Drive) {\n        self.pin.pad_ctrl().modify(|w| {\n            w.set_drive(match strength {\n                Drive::_2mA => pac::pads::vals::Drive::_2M_A,\n                Drive::_4mA => pac::pads::vals::Drive::_4M_A,\n                Drive::_8mA => pac::pads::vals::Drive::_8M_A,\n                Drive::_12mA => pac::pads::vals::Drive::_12M_A,\n            });\n        });\n    }\n\n    /// Set the pin's slew rate.\n    #[inline]\n    pub fn set_slew_rate(&mut self, slew_rate: SlewRate) {\n        self.pin.pad_ctrl().modify(|w| {\n            w.set_slewfast(slew_rate == SlewRate::Fast);\n        });\n    }\n\n    /// Set the pin's Schmitt trigger.\n    #[inline]\n    pub fn set_schmitt(&mut self, enable: bool) {\n        self.pin.pad_ctrl().modify(|w| {\n            w.set_schmitt(enable);\n        });\n    }\n\n    /// Put the pin into input mode.\n    ///\n    /// The pull setting is left unchanged.\n    #[inline]\n    pub fn set_as_input(&mut self) {\n        self.pin.sio_oe().value_clr().write_value(self.bit())\n    }\n\n    /// Put the pin into output mode.\n    ///\n    /// The pin level will be whatever was set before (or low by default). If you want it to begin\n    /// at a specific level, call `set_high`/`set_low` on the pin first.\n    #[inline]\n    pub fn set_as_output(&mut self) {\n        self.pin.sio_oe().value_set().write_value(self.bit())\n    }\n\n    /// Set as output pin.\n    #[inline]\n    fn is_set_as_output(&self) -> bool {\n        (self.pin.sio_oe().value().read() & self.bit()) != 0\n    }\n\n    /// Toggle output pin.\n    #[inline]\n    pub fn toggle_set_as_output(&mut self) {\n        self.pin.sio_oe().value_xor().write_value(self.bit())\n    }\n\n    /// Configure the input logic inversion of this pin.\n    #[inline]\n    pub fn set_input_inversion(&mut self, invert: bool) {\n        self.pin.gpio().ctrl().modify(|w| {\n            w.set_inover(if invert {\n                pac::io::vals::Inover::INVERT\n            } else {\n                pac::io::vals::Inover::NORMAL\n            })\n        });\n    }\n\n    /// Configure the output enable inversion of this pin\n    #[inline]\n    pub fn set_output_enable_inversion(&mut self, invert: bool) {\n        self.pin.gpio().ctrl().modify(|w| {\n            w.set_oeover(if invert {\n                pac::io::vals::Oeover::INVERT\n            } else {\n                pac::io::vals::Oeover::NORMAL\n            })\n        })\n    }\n\n    /// Configure the output logic inversion of this pin.\n    #[inline]\n    pub fn set_output_inversion(&mut self, invert: bool) {\n        self.pin.gpio().ctrl().modify(|w| {\n            w.set_outover(if invert {\n                pac::io::vals::Outover::INVERT\n            } else {\n                pac::io::vals::Outover::NORMAL\n            })\n        });\n    }\n\n    /// Get whether the pin input level is high.\n    #[inline]\n    pub fn is_high(&self) -> bool {\n        !self.is_low()\n    }\n    /// Get whether the pin input level is low.\n\n    #[inline]\n    pub fn is_low(&self) -> bool {\n        self.pin.sio_in().read() & self.bit() == 0\n    }\n\n    /// Returns current pin level\n    #[inline]\n    pub fn get_level(&self) -> Level {\n        self.is_high().into()\n    }\n\n    /// Set the output as high.\n    #[inline]\n    pub fn set_high(&mut self) {\n        self.pin.sio_out().value_set().write_value(self.bit())\n    }\n\n    /// Set the output as low.\n    #[inline]\n    pub fn set_low(&mut self) {\n        self.pin.sio_out().value_clr().write_value(self.bit())\n    }\n\n    /// Set the output level.\n    #[inline]\n    pub fn set_level(&mut self, level: Level) {\n        match level {\n            Level::Low => self.set_low(),\n            Level::High => self.set_high(),\n        }\n    }\n\n    /// Is the output level high?\n    #[inline]\n    pub fn is_set_high(&self) -> bool {\n        !self.is_set_low()\n    }\n\n    /// Is the output level low?\n    #[inline]\n    pub fn is_set_low(&self) -> bool {\n        (self.pin.sio_out().value().read() & self.bit()) == 0\n    }\n\n    /// What level output is set to\n    #[inline]\n    pub fn get_output_level(&self) -> Level {\n        self.is_set_high().into()\n    }\n\n    /// Toggle pin output\n    #[inline]\n    pub fn toggle(&mut self) {\n        self.pin.sio_out().value_xor().write_value(self.bit())\n    }\n\n    /// Wait until the pin is high. If it is already high, return immediately.\n    #[inline]\n    pub async fn wait_for_high(&mut self) {\n        InputFuture::new(self.pin.reborrow(), InterruptTrigger::LevelHigh).await;\n    }\n\n    /// Wait until the pin is low. If it is already low, return immediately.\n    #[inline]\n    pub async fn wait_for_low(&mut self) {\n        InputFuture::new(self.pin.reborrow(), InterruptTrigger::LevelLow).await;\n    }\n\n    /// Wait for the pin to undergo a transition from low to high.\n    #[inline]\n    pub async fn wait_for_rising_edge(&mut self) {\n        InputFuture::new(self.pin.reborrow(), InterruptTrigger::EdgeHigh).await;\n    }\n\n    /// Wait for the pin to undergo a transition from high to low.\n    #[inline]\n    pub async fn wait_for_falling_edge(&mut self) {\n        InputFuture::new(self.pin.reborrow(), InterruptTrigger::EdgeLow).await;\n    }\n\n    /// Wait for the pin to undergo any transition, i.e low to high OR high to low.\n    #[inline]\n    pub async fn wait_for_any_edge(&mut self) {\n        InputFuture::new(self.pin.reborrow(), InterruptTrigger::AnyEdge).await;\n    }\n\n    /// Configure dormant wake.\n    #[inline]\n    pub fn dormant_wake(&mut self, cfg: DormantWakeConfig) -> DormantWake<'_> {\n        let idx = self.pin._pin() as usize;\n        self.pin.io().intr(idx / 8).write(|w| {\n            w.set_edge_high(idx % 8, cfg.edge_high);\n            w.set_edge_low(idx % 8, cfg.edge_low);\n        });\n        self.pin.io().int_dormant_wake().inte(idx / 8).write_set(|w| {\n            w.set_edge_high(idx % 8, cfg.edge_high);\n            w.set_edge_low(idx % 8, cfg.edge_low);\n            w.set_level_high(idx % 8, cfg.level_high);\n            w.set_level_low(idx % 8, cfg.level_low);\n        });\n        DormantWake {\n            pin: self.pin.reborrow(),\n            cfg,\n        }\n    }\n\n    /// Set the pin's pad isolation\n    #[cfg(feature = \"_rp235x\")]\n    #[inline]\n    pub fn set_pad_isolation(&mut self, isolate: bool) {\n        self.pin.pad_ctrl().modify(|w| {\n            w.set_iso(isolate);\n        });\n    }\n}\n\nimpl<'d> Drop for Flex<'d> {\n    #[inline]\n    fn drop(&mut self) {\n        let idx = self.pin._pin() as usize;\n        self.pin.pad_ctrl().write(|_| {});\n        self.pin.gpio().ctrl().write(|w| {\n            w.set_funcsel(pac::io::vals::Gpio0ctrlFuncsel::NULL as _);\n            w.set_inover(pac::io::vals::Inover::NORMAL);\n            w.set_outover(pac::io::vals::Outover::NORMAL);\n        });\n        self.pin.io().int_dormant_wake().inte(idx / 8).write_clear(|w| {\n            w.set_edge_high(idx % 8, true);\n            w.set_edge_low(idx % 8, true);\n            w.set_level_high(idx % 8, true);\n            w.set_level_low(idx % 8, true);\n        });\n    }\n}\n\n/// Dormant wake driver.\n#[derive(Debug)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub struct DormantWake<'w> {\n    pin: Peri<'w, AnyPin>,\n    cfg: DormantWakeConfig,\n}\n\nimpl<'w> Drop for DormantWake<'w> {\n    fn drop(&mut self) {\n        let idx = self.pin._pin() as usize;\n        self.pin.io().intr(idx / 8).write(|w| {\n            w.set_edge_high(idx % 8, self.cfg.edge_high);\n            w.set_edge_low(idx % 8, self.cfg.edge_low);\n        });\n        self.pin.io().int_dormant_wake().inte(idx / 8).write_clear(|w| {\n            w.set_edge_high(idx % 8, true);\n            w.set_edge_low(idx % 8, true);\n            w.set_level_high(idx % 8, true);\n            w.set_level_low(idx % 8, true);\n        });\n    }\n}\n\npub(crate) trait SealedPin: Sized {\n    fn pin_bank(&self) -> u8;\n\n    #[inline]\n    fn _pin(&self) -> u8 {\n        self.pin_bank() & 0x7f\n    }\n\n    #[inline]\n    fn _bank(&self) -> Bank {\n        match self.pin_bank() >> 7 {\n            #[cfg(feature = \"qspi-as-gpio\")]\n            1 => Bank::Qspi,\n            _ => Bank::Bank0,\n        }\n    }\n\n    fn io(&self) -> pac::io::Io {\n        match self._bank() {\n            Bank::Bank0 => crate::pac::IO_BANK0,\n            #[cfg(feature = \"qspi-as-gpio\")]\n            Bank::Qspi => crate::pac::IO_QSPI,\n        }\n    }\n\n    fn gpio(&self) -> pac::io::Gpio {\n        self.io().gpio(self._pin() as _)\n    }\n\n    fn pad_ctrl(&self) -> Reg<pac::pads::regs::GpioCtrl, RW> {\n        let block = match self._bank() {\n            Bank::Bank0 => crate::pac::PADS_BANK0,\n            #[cfg(feature = \"qspi-as-gpio\")]\n            Bank::Qspi => crate::pac::PADS_QSPI,\n        };\n        block.gpio(self._pin() as _)\n    }\n\n    fn sio_out(&self) -> pac::sio::Gpio {\n        if cfg!(feature = \"rp2040\") {\n            SIO.gpio_out(self._bank() as _)\n        } else {\n            SIO.gpio_out((self._pin() / 32) as _)\n        }\n    }\n\n    fn sio_oe(&self) -> pac::sio::Gpio {\n        if cfg!(feature = \"rp2040\") {\n            SIO.gpio_oe(self._bank() as _)\n        } else {\n            SIO.gpio_oe((self._pin() / 32) as _)\n        }\n    }\n\n    fn sio_in(&self) -> Reg<u32, RW> {\n        if cfg!(feature = \"rp2040\") {\n            SIO.gpio_in(self._bank() as _)\n        } else {\n            SIO.gpio_in((self._pin() / 32) as _)\n        }\n    }\n\n    fn int_proc(&self) -> pac::io::Int {\n        let proc = SIO.cpuid().read();\n        self.io().int_proc(proc as _)\n    }\n}\n\n/// Interface for a Pin that can be configured by an [Input] or [Output] driver, or converted to an [AnyPin].\n#[allow(private_bounds)]\npub trait Pin: PeripheralType + Into<AnyPin> + SealedPin + Sized + 'static {\n    /// Returns the pin number within a bank\n    #[inline]\n    fn pin(&self) -> u8 {\n        self._pin()\n    }\n\n    /// Returns the bank of this pin\n    #[inline]\n    fn bank(&self) -> Bank {\n        self._bank()\n    }\n}\n\n/// Type-erased GPIO pin\n#[derive(Debug)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub struct AnyPin {\n    pin_bank: u8,\n}\n\nimpl AnyPin {\n    /// Unsafely create a new type-erased pin.\n    ///\n    /// # Safety\n    ///\n    /// You must ensure that you’re only using one instance of this type at a time.\n    pub unsafe fn steal(pin_bank: u8) -> Peri<'static, Self> {\n        Peri::new_unchecked(Self { pin_bank })\n    }\n}\n\nimpl_peripheral!(AnyPin);\n\nimpl Pin for AnyPin {}\nimpl SealedPin for AnyPin {\n    fn pin_bank(&self) -> u8 {\n        self.pin_bank\n    }\n}\n\n// ==========================\n\nmacro_rules! impl_pin {\n    ($name:ident, $bank:expr, $pin_num:expr) => {\n        impl Pin for peripherals::$name {}\n        impl SealedPin for peripherals::$name {\n            #[inline]\n            fn pin_bank(&self) -> u8 {\n                ($bank as u8) * 128 + $pin_num\n            }\n        }\n\n        impl From<peripherals::$name> for crate::gpio::AnyPin {\n            fn from(val: peripherals::$name) -> Self {\n                Self {\n                    pin_bank: val.pin_bank(),\n                }\n            }\n        }\n    };\n}\n\nimpl_pin!(PIN_0, Bank::Bank0, 0);\nimpl_pin!(PIN_1, Bank::Bank0, 1);\nimpl_pin!(PIN_2, Bank::Bank0, 2);\nimpl_pin!(PIN_3, Bank::Bank0, 3);\nimpl_pin!(PIN_4, Bank::Bank0, 4);\nimpl_pin!(PIN_5, Bank::Bank0, 5);\nimpl_pin!(PIN_6, Bank::Bank0, 6);\nimpl_pin!(PIN_7, Bank::Bank0, 7);\nimpl_pin!(PIN_8, Bank::Bank0, 8);\nimpl_pin!(PIN_9, Bank::Bank0, 9);\nimpl_pin!(PIN_10, Bank::Bank0, 10);\nimpl_pin!(PIN_11, Bank::Bank0, 11);\nimpl_pin!(PIN_12, Bank::Bank0, 12);\nimpl_pin!(PIN_13, Bank::Bank0, 13);\nimpl_pin!(PIN_14, Bank::Bank0, 14);\nimpl_pin!(PIN_15, Bank::Bank0, 15);\nimpl_pin!(PIN_16, Bank::Bank0, 16);\nimpl_pin!(PIN_17, Bank::Bank0, 17);\nimpl_pin!(PIN_18, Bank::Bank0, 18);\nimpl_pin!(PIN_19, Bank::Bank0, 19);\nimpl_pin!(PIN_20, Bank::Bank0, 20);\nimpl_pin!(PIN_21, Bank::Bank0, 21);\nimpl_pin!(PIN_22, Bank::Bank0, 22);\nimpl_pin!(PIN_23, Bank::Bank0, 23);\nimpl_pin!(PIN_24, Bank::Bank0, 24);\nimpl_pin!(PIN_25, Bank::Bank0, 25);\nimpl_pin!(PIN_26, Bank::Bank0, 26);\nimpl_pin!(PIN_27, Bank::Bank0, 27);\nimpl_pin!(PIN_28, Bank::Bank0, 28);\nimpl_pin!(PIN_29, Bank::Bank0, 29);\n\n#[cfg(feature = \"rp235xb\")]\nimpl_pin!(PIN_30, Bank::Bank0, 30);\n#[cfg(feature = \"rp235xb\")]\nimpl_pin!(PIN_31, Bank::Bank0, 31);\n#[cfg(feature = \"rp235xb\")]\nimpl_pin!(PIN_32, Bank::Bank0, 32);\n#[cfg(feature = \"rp235xb\")]\nimpl_pin!(PIN_33, Bank::Bank0, 33);\n#[cfg(feature = \"rp235xb\")]\nimpl_pin!(PIN_34, Bank::Bank0, 34);\n#[cfg(feature = \"rp235xb\")]\nimpl_pin!(PIN_35, Bank::Bank0, 35);\n#[cfg(feature = \"rp235xb\")]\nimpl_pin!(PIN_36, Bank::Bank0, 36);\n#[cfg(feature = \"rp235xb\")]\nimpl_pin!(PIN_37, Bank::Bank0, 37);\n#[cfg(feature = \"rp235xb\")]\nimpl_pin!(PIN_38, Bank::Bank0, 38);\n#[cfg(feature = \"rp235xb\")]\nimpl_pin!(PIN_39, Bank::Bank0, 39);\n#[cfg(feature = \"rp235xb\")]\nimpl_pin!(PIN_40, Bank::Bank0, 40);\n#[cfg(feature = \"rp235xb\")]\nimpl_pin!(PIN_41, Bank::Bank0, 41);\n#[cfg(feature = \"rp235xb\")]\nimpl_pin!(PIN_42, Bank::Bank0, 42);\n#[cfg(feature = \"rp235xb\")]\nimpl_pin!(PIN_43, Bank::Bank0, 43);\n#[cfg(feature = \"rp235xb\")]\nimpl_pin!(PIN_44, Bank::Bank0, 44);\n#[cfg(feature = \"rp235xb\")]\nimpl_pin!(PIN_45, Bank::Bank0, 45);\n#[cfg(feature = \"rp235xb\")]\nimpl_pin!(PIN_46, Bank::Bank0, 46);\n#[cfg(feature = \"rp235xb\")]\nimpl_pin!(PIN_47, Bank::Bank0, 47);\n\n// TODO rp235x bank1 as gpio support\n#[cfg(feature = \"qspi-as-gpio\")]\nimpl_pin!(PIN_QSPI_SCLK, Bank::Qspi, 0);\n#[cfg(feature = \"qspi-as-gpio\")]\nimpl_pin!(PIN_QSPI_SS, Bank::Qspi, 1);\n#[cfg(feature = \"qspi-as-gpio\")]\nimpl_pin!(PIN_QSPI_SD0, Bank::Qspi, 2);\n#[cfg(feature = \"qspi-as-gpio\")]\nimpl_pin!(PIN_QSPI_SD1, Bank::Qspi, 3);\n#[cfg(feature = \"qspi-as-gpio\")]\nimpl_pin!(PIN_QSPI_SD2, Bank::Qspi, 4);\n#[cfg(feature = \"qspi-as-gpio\")]\nimpl_pin!(PIN_QSPI_SD3, Bank::Qspi, 5);\n\n// ====================\n\nmod eh02 {\n    use super::*;\n\n    impl<'d> embedded_hal_02::digital::v2::InputPin for Input<'d> {\n        type Error = Infallible;\n\n        fn is_high(&self) -> Result<bool, Self::Error> {\n            Ok(self.is_high())\n        }\n\n        fn is_low(&self) -> Result<bool, Self::Error> {\n            Ok(self.is_low())\n        }\n    }\n\n    impl<'d> embedded_hal_02::digital::v2::OutputPin for Output<'d> {\n        type Error = Infallible;\n\n        fn set_high(&mut self) -> Result<(), Self::Error> {\n            Ok(self.set_high())\n        }\n\n        fn set_low(&mut self) -> Result<(), Self::Error> {\n            Ok(self.set_low())\n        }\n    }\n\n    impl<'d> embedded_hal_02::digital::v2::StatefulOutputPin for Output<'d> {\n        fn is_set_high(&self) -> Result<bool, Self::Error> {\n            Ok(self.is_set_high())\n        }\n\n        fn is_set_low(&self) -> Result<bool, Self::Error> {\n            Ok(self.is_set_low())\n        }\n    }\n\n    impl<'d> embedded_hal_02::digital::v2::ToggleableOutputPin for Output<'d> {\n        type Error = Infallible;\n        #[inline]\n        fn toggle(&mut self) -> Result<(), Self::Error> {\n            Ok(self.toggle())\n        }\n    }\n\n    impl<'d> embedded_hal_02::digital::v2::InputPin for OutputOpenDrain<'d> {\n        type Error = Infallible;\n\n        fn is_high(&self) -> Result<bool, Self::Error> {\n            Ok(self.is_high())\n        }\n\n        fn is_low(&self) -> Result<bool, Self::Error> {\n            Ok(self.is_low())\n        }\n    }\n\n    impl<'d> embedded_hal_02::digital::v2::OutputPin for OutputOpenDrain<'d> {\n        type Error = Infallible;\n\n        #[inline]\n        fn set_high(&mut self) -> Result<(), Self::Error> {\n            Ok(self.set_high())\n        }\n\n        #[inline]\n        fn set_low(&mut self) -> Result<(), Self::Error> {\n            Ok(self.set_low())\n        }\n    }\n\n    impl<'d> embedded_hal_02::digital::v2::StatefulOutputPin for OutputOpenDrain<'d> {\n        fn is_set_high(&self) -> Result<bool, Self::Error> {\n            Ok(self.is_set_high())\n        }\n\n        fn is_set_low(&self) -> Result<bool, Self::Error> {\n            Ok(self.is_set_low())\n        }\n    }\n\n    impl<'d> embedded_hal_02::digital::v2::ToggleableOutputPin for OutputOpenDrain<'d> {\n        type Error = Infallible;\n        #[inline]\n        fn toggle(&mut self) -> Result<(), Self::Error> {\n            Ok(self.toggle())\n        }\n    }\n\n    impl<'d> embedded_hal_02::digital::v2::InputPin for Flex<'d> {\n        type Error = Infallible;\n\n        fn is_high(&self) -> Result<bool, Self::Error> {\n            Ok(self.is_high())\n        }\n\n        fn is_low(&self) -> Result<bool, Self::Error> {\n            Ok(self.is_low())\n        }\n    }\n\n    impl<'d> embedded_hal_02::digital::v2::OutputPin for Flex<'d> {\n        type Error = Infallible;\n\n        fn set_high(&mut self) -> Result<(), Self::Error> {\n            Ok(self.set_high())\n        }\n\n        fn set_low(&mut self) -> Result<(), Self::Error> {\n            Ok(self.set_low())\n        }\n    }\n\n    impl<'d> embedded_hal_02::digital::v2::StatefulOutputPin for Flex<'d> {\n        fn is_set_high(&self) -> Result<bool, Self::Error> {\n            Ok(self.is_set_high())\n        }\n\n        fn is_set_low(&self) -> Result<bool, Self::Error> {\n            Ok(self.is_set_low())\n        }\n    }\n\n    impl<'d> embedded_hal_02::digital::v2::ToggleableOutputPin for Flex<'d> {\n        type Error = Infallible;\n        #[inline]\n        fn toggle(&mut self) -> Result<(), Self::Error> {\n            Ok(self.toggle())\n        }\n    }\n}\n\nimpl<'d> embedded_hal_1::digital::ErrorType for Input<'d> {\n    type Error = Infallible;\n}\n\nimpl<'d> embedded_hal_1::digital::InputPin for Input<'d> {\n    fn is_high(&mut self) -> Result<bool, Self::Error> {\n        Ok((*self).is_high())\n    }\n\n    fn is_low(&mut self) -> Result<bool, Self::Error> {\n        Ok((*self).is_low())\n    }\n}\n\nimpl<'d> embedded_hal_1::digital::ErrorType for Output<'d> {\n    type Error = Infallible;\n}\n\nimpl<'d> embedded_hal_1::digital::OutputPin for Output<'d> {\n    fn set_high(&mut self) -> Result<(), Self::Error> {\n        Ok(self.set_high())\n    }\n\n    fn set_low(&mut self) -> Result<(), Self::Error> {\n        Ok(self.set_low())\n    }\n}\n\nimpl<'d> embedded_hal_1::digital::StatefulOutputPin for Output<'d> {\n    fn is_set_high(&mut self) -> Result<bool, Self::Error> {\n        Ok((*self).is_set_high())\n    }\n\n    fn is_set_low(&mut self) -> Result<bool, Self::Error> {\n        Ok((*self).is_set_low())\n    }\n}\n\nimpl<'d> embedded_hal_1::digital::ErrorType for OutputOpenDrain<'d> {\n    type Error = Infallible;\n}\n\nimpl<'d> embedded_hal_1::digital::OutputPin for OutputOpenDrain<'d> {\n    fn set_high(&mut self) -> Result<(), Self::Error> {\n        Ok(self.set_high())\n    }\n\n    fn set_low(&mut self) -> Result<(), Self::Error> {\n        Ok(self.set_low())\n    }\n}\n\nimpl<'d> embedded_hal_1::digital::StatefulOutputPin for OutputOpenDrain<'d> {\n    fn is_set_high(&mut self) -> Result<bool, Self::Error> {\n        Ok((*self).is_set_high())\n    }\n\n    fn is_set_low(&mut self) -> Result<bool, Self::Error> {\n        Ok((*self).is_set_low())\n    }\n}\n\nimpl<'d> embedded_hal_1::digital::InputPin for OutputOpenDrain<'d> {\n    fn is_high(&mut self) -> Result<bool, Self::Error> {\n        Ok((*self).is_high())\n    }\n\n    fn is_low(&mut self) -> Result<bool, Self::Error> {\n        Ok((*self).is_low())\n    }\n}\n\nimpl<'d> embedded_hal_1::digital::ErrorType for Flex<'d> {\n    type Error = Infallible;\n}\n\nimpl<'d> embedded_hal_1::digital::InputPin for Flex<'d> {\n    fn is_high(&mut self) -> Result<bool, Self::Error> {\n        Ok((*self).is_high())\n    }\n\n    fn is_low(&mut self) -> Result<bool, Self::Error> {\n        Ok((*self).is_low())\n    }\n}\n\nimpl<'d> embedded_hal_1::digital::OutputPin for Flex<'d> {\n    fn set_high(&mut self) -> Result<(), Self::Error> {\n        Ok(self.set_high())\n    }\n\n    fn set_low(&mut self) -> Result<(), Self::Error> {\n        Ok(self.set_low())\n    }\n}\n\nimpl<'d> embedded_hal_1::digital::StatefulOutputPin for Flex<'d> {\n    fn is_set_high(&mut self) -> Result<bool, Self::Error> {\n        Ok((*self).is_set_high())\n    }\n\n    fn is_set_low(&mut self) -> Result<bool, Self::Error> {\n        Ok((*self).is_set_low())\n    }\n}\n\nimpl<'d> embedded_hal_async::digital::Wait for Flex<'d> {\n    async fn wait_for_high(&mut self) -> Result<(), Self::Error> {\n        self.wait_for_high().await;\n        Ok(())\n    }\n\n    async fn wait_for_low(&mut self) -> Result<(), Self::Error> {\n        self.wait_for_low().await;\n        Ok(())\n    }\n\n    async fn wait_for_rising_edge(&mut self) -> Result<(), Self::Error> {\n        self.wait_for_rising_edge().await;\n        Ok(())\n    }\n\n    async fn wait_for_falling_edge(&mut self) -> Result<(), Self::Error> {\n        self.wait_for_falling_edge().await;\n        Ok(())\n    }\n\n    async fn wait_for_any_edge(&mut self) -> Result<(), Self::Error> {\n        self.wait_for_any_edge().await;\n        Ok(())\n    }\n}\n\nimpl<'d> embedded_hal_async::digital::Wait for Input<'d> {\n    async fn wait_for_high(&mut self) -> Result<(), Self::Error> {\n        self.wait_for_high().await;\n        Ok(())\n    }\n\n    async fn wait_for_low(&mut self) -> Result<(), Self::Error> {\n        self.wait_for_low().await;\n        Ok(())\n    }\n\n    async fn wait_for_rising_edge(&mut self) -> Result<(), Self::Error> {\n        self.wait_for_rising_edge().await;\n        Ok(())\n    }\n\n    async fn wait_for_falling_edge(&mut self) -> Result<(), Self::Error> {\n        self.wait_for_falling_edge().await;\n        Ok(())\n    }\n\n    async fn wait_for_any_edge(&mut self) -> Result<(), Self::Error> {\n        self.wait_for_any_edge().await;\n        Ok(())\n    }\n}\n\nimpl<'d> embedded_hal_async::digital::Wait for OutputOpenDrain<'d> {\n    async fn wait_for_high(&mut self) -> Result<(), Self::Error> {\n        self.wait_for_high().await;\n        Ok(())\n    }\n\n    async fn wait_for_low(&mut self) -> Result<(), Self::Error> {\n        self.wait_for_low().await;\n        Ok(())\n    }\n\n    async fn wait_for_rising_edge(&mut self) -> Result<(), Self::Error> {\n        self.wait_for_rising_edge().await;\n        Ok(())\n    }\n\n    async fn wait_for_falling_edge(&mut self) -> Result<(), Self::Error> {\n        self.wait_for_falling_edge().await;\n        Ok(())\n    }\n\n    async fn wait_for_any_edge(&mut self) -> Result<(), Self::Error> {\n        self.wait_for_any_edge().await;\n        Ok(())\n    }\n}\n"
  },
  {
    "path": "embassy-rp/src/i2c.rs",
    "content": "#![cfg_attr(feature = \"defmt\", allow(deprecated))] // Suppress warnings for defmt::Format using Error::AddressReserved\n\n//! I2C driver.\nuse core::future;\nuse core::marker::PhantomData;\nuse core::task::Poll;\n\nuse embassy_hal_internal::{Peri, PeripheralType};\nuse embassy_sync::waitqueue::AtomicWaker;\nuse pac::i2c;\n\nuse crate::gpio::AnyPin;\nuse crate::interrupt::typelevel::{Binding, Interrupt};\nuse crate::{interrupt, pac, peripherals};\n\n/// I2C error abort reason\n#[derive(Debug, PartialEq, Eq, Clone, Copy)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub enum AbortReason {\n    /// A bus operation was not acknowledged, e.g. due to the addressed device\n    /// not being available on the bus or the device not being ready to process\n    /// requests at the moment\n    NoAcknowledge,\n    /// The arbitration was lost, e.g. electrical problems with the clock signal\n    ArbitrationLoss,\n    /// Transmit ended with data still in fifo\n    TxNotEmpty(u16),\n    /// Other reason.\n    Other(u32),\n}\n\n/// I2C error\n#[derive(Debug, PartialEq, Eq, Clone, Copy)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub enum Error {\n    /// I2C abort with error\n    Abort(AbortReason),\n    /// User passed in a read buffer that was 0 length\n    InvalidReadBufferLength,\n    /// User passed in a write buffer that was 0 length\n    InvalidWriteBufferLength,\n    /// Target i2c address is out of range\n    AddressOutOfRange(u16),\n    /// Target i2c address is reserved\n    #[deprecated = \"embassy_rp no longer prevents accesses to reserved addresses.\"]\n    AddressReserved(u16),\n}\n\n/// I2C Config error\n#[derive(Debug, PartialEq, Eq)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub enum ConfigError {\n    /// Max i2c speed is 1MHz\n    FrequencyTooHigh,\n    /// The sys clock is too slow to support given frequency\n    ClockTooSlow,\n    /// The sys clock is too fast to support given frequency\n    ClockTooFast,\n}\n\n/// I2C config.\n#[non_exhaustive]\n#[derive(Copy, Clone)]\npub struct Config {\n    /// Frequency.\n    pub frequency: u32,\n    /// Enable internal pullup on SDA.\n    ///\n    /// Using external pullup resistors is recommended for I2C. If you do\n    /// have external pullups you should not enable this.\n    pub sda_pullup: bool,\n    /// Enable internal pullup on SCL.\n    ///\n    /// Using external pullup resistors is recommended for I2C. If you do\n    /// have external pullups you should not enable this.\n    pub scl_pullup: bool,\n}\nimpl Default for Config {\n    fn default() -> Self {\n        Self {\n            frequency: 100_000,\n            sda_pullup: true,\n            scl_pullup: true,\n        }\n    }\n}\n/// Size of I2C FIFO.\npub const FIFO_SIZE: u8 = 16;\n\n/// I2C driver.\n#[derive(Debug)]\npub struct I2c<'d, T: Instance, M: Mode> {\n    phantom: PhantomData<(&'d mut T, M)>,\n}\n\nimpl<'d, T: Instance> I2c<'d, T, Blocking> {\n    /// Create a new driver instance in blocking mode.\n    pub fn new_blocking(\n        peri: Peri<'d, T>,\n        scl: Peri<'d, impl SclPin<T>>,\n        sda: Peri<'d, impl SdaPin<T>>,\n        config: Config,\n    ) -> Self {\n        Self::new_inner(peri, scl.into(), sda.into(), config)\n    }\n}\n\nimpl<'d, T: Instance> I2c<'d, T, Async> {\n    /// Create a new driver instance in async mode.\n    pub fn new_async(\n        peri: Peri<'d, T>,\n        scl: Peri<'d, impl SclPin<T>>,\n        sda: Peri<'d, impl SdaPin<T>>,\n        _irq: impl Binding<T::Interrupt, InterruptHandler<T>>,\n        config: Config,\n    ) -> Self {\n        let i2c = Self::new_inner(peri, scl.into(), sda.into(), config);\n\n        let r = T::regs();\n\n        // mask everything initially\n        r.ic_intr_mask().write_value(i2c::regs::IcIntrMask(0));\n        T::Interrupt::unpend();\n        unsafe { T::Interrupt::enable() };\n\n        i2c\n    }\n\n    /// Calls `f` to check if we are ready or not.\n    /// If not, `g` is called once(to eg enable the required interrupts).\n    /// The waker will always be registered prior to calling `f`.\n    async fn wait_on<F, U, G>(&mut self, mut f: F, mut g: G) -> U\n    where\n        F: FnMut(&mut Self) -> Poll<U>,\n        G: FnMut(&mut Self),\n    {\n        future::poll_fn(|cx| {\n            // Register prior to checking the condition\n            T::waker().register(cx.waker());\n            let r = f(self);\n\n            if r.is_pending() {\n                g(self);\n            }\n            r\n        })\n        .await\n    }\n\n    async fn read_async_internal(&mut self, buffer: &mut [u8], restart: bool, send_stop: bool) -> Result<(), Error> {\n        if buffer.is_empty() {\n            return Err(Error::InvalidReadBufferLength);\n        }\n\n        let p = T::regs();\n\n        let mut remaining = buffer.len();\n        let mut remaining_queue = buffer.len();\n\n        let mut abort_reason = Ok(());\n\n        while remaining > 0 {\n            // Waggle SCK - basically the same as write\n            let tx_fifo_space = Self::tx_fifo_capacity();\n            let mut batch = 0;\n\n            debug_assert!(remaining_queue > 0);\n\n            for _ in 0..remaining_queue.min(tx_fifo_space as usize) {\n                remaining_queue -= 1;\n                let last = remaining_queue == 0;\n                batch += 1;\n\n                p.ic_data_cmd().write(|w| {\n                    w.set_restart(restart && remaining_queue == buffer.len() - 1);\n                    w.set_stop(last && send_stop);\n                    w.set_cmd(true);\n                });\n            }\n\n            // We've either run out of txfifo or just plain finished setting up\n            // the clocks for the message - either way we need to wait for rx\n            // data.\n\n            debug_assert!(batch > 0);\n            let res = self\n                .wait_on(\n                    |me| {\n                        let rxfifo = Self::rx_fifo_len();\n                        if let Err(abort_reason) = me.read_and_clear_abort_reason() {\n                            Poll::Ready(Err(abort_reason))\n                        } else if rxfifo >= batch {\n                            Poll::Ready(Ok(rxfifo))\n                        } else {\n                            Poll::Pending\n                        }\n                    },\n                    |_me| {\n                        // Set the read threshold to the number of bytes we're\n                        // expecting so we don't get spurious interrupts.\n                        p.ic_rx_tl().write(|w| w.set_rx_tl(batch - 1));\n\n                        p.ic_intr_mask().modify(|w| {\n                            w.set_m_rx_full(true);\n                            w.set_m_tx_abrt(true);\n                        });\n                    },\n                )\n                .await;\n\n            match res {\n                Err(reason) => {\n                    abort_reason = Err(reason);\n                    break;\n                }\n                Ok(rxfifo) => {\n                    // Fetch things from rx fifo. We're assuming we're the only\n                    // rxfifo reader, so nothing else can take things from it.\n                    let rxbytes = (rxfifo as usize).min(remaining);\n                    let received = buffer.len() - remaining;\n                    for b in &mut buffer[received..received + rxbytes] {\n                        *b = p.ic_data_cmd().read().dat();\n                    }\n                    remaining -= rxbytes;\n                }\n            };\n        }\n\n        self.wait_stop_det(abort_reason, send_stop).await\n    }\n\n    async fn write_async_internal(\n        &mut self,\n        bytes: impl IntoIterator<Item = u8>,\n        send_stop: bool,\n    ) -> Result<(), Error> {\n        let p = T::regs();\n\n        let mut bytes = bytes.into_iter().peekable();\n\n        let res = 'xmit: loop {\n            let tx_fifo_space = Self::tx_fifo_capacity();\n\n            for _ in 0..tx_fifo_space {\n                if let Some(byte) = bytes.next() {\n                    let last = bytes.peek().is_none();\n\n                    p.ic_data_cmd().write(|w| {\n                        w.set_stop(last && send_stop);\n                        w.set_cmd(false);\n                        w.set_dat(byte);\n                    });\n                } else {\n                    break 'xmit Ok(());\n                }\n            }\n\n            let res = self\n                .wait_on(\n                    |me| {\n                        if let abort_reason @ Err(_) = me.read_and_clear_abort_reason() {\n                            Poll::Ready(abort_reason)\n                        } else if !Self::tx_fifo_full() {\n                            // resume if there's any space free in the tx fifo\n                            Poll::Ready(Ok(()))\n                        } else {\n                            Poll::Pending\n                        }\n                    },\n                    |_me| {\n                        // Set tx \"free\" threshold a little high so that we get\n                        // woken before the fifo completely drains to minimize\n                        // transfer stalls.\n                        p.ic_tx_tl().write(|w| w.set_tx_tl(1));\n\n                        p.ic_intr_mask().modify(|w| {\n                            w.set_m_tx_empty(true);\n                            w.set_m_tx_abrt(true);\n                        })\n                    },\n                )\n                .await;\n            if res.is_err() {\n                break res;\n            }\n        };\n\n        self.wait_stop_det(res, send_stop).await\n    }\n\n    /// Helper to wait for a stop bit, for both tx and rx. If we had an abort,\n    /// then we'll get a hardware-generated stop, otherwise wait for a stop if\n    /// we're expecting it.\n    ///\n    /// Also handles an abort which arises while processing the tx fifo.\n    async fn wait_stop_det(&mut self, had_abort: Result<(), Error>, do_stop: bool) -> Result<(), Error> {\n        if had_abort.is_err() || do_stop {\n            let p = T::regs();\n\n            let had_abort2 = self\n                .wait_on(\n                    |me| {\n                        // We could see an abort while processing fifo backlog,\n                        // so handle it here.\n                        let abort = me.read_and_clear_abort_reason();\n                        if had_abort.is_ok() && abort.is_err() {\n                            Poll::Ready(abort)\n                        } else if p.ic_raw_intr_stat().read().stop_det() {\n                            Poll::Ready(Ok(()))\n                        } else {\n                            Poll::Pending\n                        }\n                    },\n                    |_me| {\n                        p.ic_intr_mask().modify(|w| {\n                            w.set_m_stop_det(true);\n                            w.set_m_tx_abrt(true);\n                        });\n                    },\n                )\n                .await;\n            p.ic_clr_stop_det().read();\n\n            had_abort.and(had_abort2)\n        } else {\n            had_abort\n        }\n    }\n\n    /// Read from address into buffer asynchronously.\n    pub async fn read_async(&mut self, addr: impl Into<u16>, buffer: &mut [u8]) -> Result<(), Error> {\n        Self::setup(addr.into())?;\n        self.read_async_internal(buffer, true, true).await\n    }\n\n    /// Write to address from buffer asynchronously.\n    pub async fn write_async(\n        &mut self,\n        addr: impl Into<u16>,\n        bytes: impl IntoIterator<Item = u8>,\n    ) -> Result<(), Error> {\n        Self::setup(addr.into())?;\n        self.write_async_internal(bytes, true).await\n    }\n\n    /// Write to address from bytes and read from address into buffer asynchronously.\n    pub async fn write_read_async(\n        &mut self,\n        addr: impl Into<u16>,\n        bytes: impl IntoIterator<Item = u8>,\n        buffer: &mut [u8],\n    ) -> Result<(), Error> {\n        Self::setup(addr.into())?;\n        self.write_async_internal(bytes, false).await?;\n        self.read_async_internal(buffer, true, true).await\n    }\n}\n\n/// Interrupt handler.\npub struct InterruptHandler<T: Instance> {\n    _uart: PhantomData<T>,\n}\n\nimpl<T: Instance> interrupt::typelevel::Handler<T::Interrupt> for InterruptHandler<T> {\n    // Mask interrupts and wake any task waiting for this interrupt\n    unsafe fn on_interrupt() {\n        let i2c = T::regs();\n        i2c.ic_intr_mask().write_value(pac::i2c::regs::IcIntrMask::default());\n\n        T::waker().wake();\n    }\n}\n\npub(crate) fn set_up_i2c_pin<P, T>(pin: &P, pullup: bool)\nwhere\n    P: core::ops::Deref<Target = T>,\n    T: crate::gpio::Pin,\n{\n    pin.gpio().ctrl().write(|w| w.set_funcsel(3));\n    pin.pad_ctrl().write(|w| {\n        #[cfg(feature = \"_rp235x\")]\n        w.set_iso(false);\n        w.set_schmitt(true);\n        w.set_slewfast(false);\n        w.set_ie(true);\n        w.set_od(false);\n        w.set_pue(pullup);\n        w.set_pde(false);\n    });\n}\n\nimpl<'d, T: Instance + 'd, M: Mode> I2c<'d, T, M> {\n    fn new_inner(_peri: Peri<'d, T>, scl: Peri<'d, AnyPin>, sda: Peri<'d, AnyPin>, config: Config) -> Self {\n        let reset = T::reset();\n        crate::reset::reset(reset);\n        crate::reset::unreset_wait(reset);\n\n        // Configure SCL & SDA pins\n        set_up_i2c_pin(&scl, config.scl_pullup);\n        set_up_i2c_pin(&sda, config.sda_pullup);\n\n        let mut me = Self { phantom: PhantomData };\n\n        if let Err(e) = me.set_config_inner(&config) {\n            panic!(\"Error configuring i2c: {:?}\", e);\n        }\n\n        me\n    }\n\n    fn set_config_inner(&mut self, config: &Config) -> Result<(), ConfigError> {\n        if config.frequency > 1_000_000 {\n            return Err(ConfigError::FrequencyTooHigh);\n        }\n\n        let p = T::regs();\n\n        p.ic_enable().write(|w| w.set_enable(false));\n\n        // Configure baudrate\n\n        // There are some subtleties to I2C timing which we are completely\n        // ignoring here See:\n        // https://github.com/raspberrypi/pico-sdk/blob/bfcbefafc5d2a210551a4d9d80b4303d4ae0adf7/src/rp2_common/hardware_i2c/i2c.c#L69\n        let clk_base = crate::clocks::clk_peri_freq();\n\n        let period = (clk_base + config.frequency / 2) / config.frequency;\n        let lcnt = period * 3 / 5; // spend 3/5 (60%) of the period low\n        let hcnt = period - lcnt; // and 2/5 (40%) of the period high\n\n        // Check for out-of-range divisors:\n        if hcnt > 0xffff || lcnt > 0xffff {\n            return Err(ConfigError::ClockTooFast);\n        }\n        if hcnt < 8 || lcnt < 8 {\n            return Err(ConfigError::ClockTooSlow);\n        }\n\n        // Per I2C-bus specification a device in standard or fast mode must\n        // internally provide a hold time of at least 300ns for the SDA\n        // signal to bridge the undefined region of the falling edge of SCL.\n        // A smaller hold time of 120ns is used for fast mode plus.\n        let sda_tx_hold_count = if config.frequency < 1_000_000 {\n            // sda_tx_hold_count = clk_base [cycles/s] * 300ns * (1s /\n            // 1e9ns) Reduce 300/1e9 to 3/1e7 to avoid numbers that don't\n            // fit in uint. Add 1 to avoid division truncation.\n            ((clk_base * 3) / 10_000_000) + 1\n        } else {\n            // fast mode plus requires a clk_base > 32MHz\n            if clk_base <= 32_000_000 {\n                return Err(ConfigError::ClockTooSlow);\n            }\n\n            // sda_tx_hold_count = clk_base [cycles/s] * 120ns * (1s /\n            // 1e9ns) Reduce 120/1e9 to 3/25e6 to avoid numbers that don't\n            // fit in uint. Add 1 to avoid division truncation.\n            ((clk_base * 3) / 25_000_000) + 1\n        };\n\n        if sda_tx_hold_count > lcnt - 2 {\n            return Err(ConfigError::ClockTooSlow);\n        }\n\n        p.ic_fs_scl_hcnt().write(|w| w.set_ic_fs_scl_hcnt(hcnt as u16));\n        p.ic_fs_scl_lcnt().write(|w| w.set_ic_fs_scl_lcnt(lcnt as u16));\n        p.ic_fs_spklen()\n            .write(|w| w.set_ic_fs_spklen(if lcnt < 16 { 1 } else { (lcnt / 16) as u8 }));\n        p.ic_sda_hold()\n            .modify(|w| w.set_ic_sda_tx_hold(sda_tx_hold_count as u16));\n\n        p.ic_enable().write(|w| w.set_enable(true));\n\n        Ok(())\n    }\n\n    fn setup(addr: u16) -> Result<(), Error> {\n        if addr >= 0x80 {\n            return Err(Error::AddressOutOfRange(addr));\n        }\n\n        let p = T::regs();\n        p.ic_enable().write(|w| w.set_enable(false));\n        p.ic_tar().write(|w| w.set_ic_tar(addr));\n        p.ic_enable().write(|w| w.set_enable(true));\n        Ok(())\n    }\n\n    #[inline]\n    fn tx_fifo_full() -> bool {\n        Self::tx_fifo_capacity() == 0\n    }\n\n    #[inline]\n    fn tx_fifo_capacity() -> u8 {\n        let p = T::regs();\n        FIFO_SIZE - p.ic_txflr().read().txflr()\n    }\n\n    #[inline]\n    fn rx_fifo_len() -> u8 {\n        let p = T::regs();\n        p.ic_rxflr().read().rxflr()\n    }\n\n    fn read_and_clear_abort_reason(&mut self) -> Result<(), Error> {\n        let p = T::regs();\n        let abort_reason = p.ic_tx_abrt_source().read();\n        if abort_reason.0 != 0 {\n            // Note clearing the abort flag also clears the reason, and this\n            // instance of flag is clear-on-read! Note also the\n            // IC_CLR_TX_ABRT register always reads as 0.\n            p.ic_clr_tx_abrt().read();\n\n            let reason = if abort_reason.abrt_7b_addr_noack()\n                | abort_reason.abrt_10addr1_noack()\n                | abort_reason.abrt_10addr2_noack()\n            {\n                AbortReason::NoAcknowledge\n            } else if abort_reason.arb_lost() {\n                AbortReason::ArbitrationLoss\n            } else {\n                AbortReason::Other(abort_reason.0)\n            };\n\n            Err(Error::Abort(reason))\n        } else {\n            Ok(())\n        }\n    }\n\n    fn read_blocking_internal(&mut self, read: &mut [u8], restart: bool, send_stop: bool) -> Result<(), Error> {\n        if read.is_empty() {\n            return Err(Error::InvalidReadBufferLength);\n        }\n\n        let p = T::regs();\n        let lastindex = read.len() - 1;\n        for (i, byte) in read.iter_mut().enumerate() {\n            let first = i == 0;\n            let last = i == lastindex;\n\n            // wait until there is space in the FIFO to write the next byte\n            while Self::tx_fifo_full() {}\n\n            p.ic_data_cmd().write(|w| {\n                w.set_restart(restart && first);\n                w.set_stop(send_stop && last);\n\n                w.set_cmd(true);\n            });\n\n            while Self::rx_fifo_len() == 0 {\n                self.read_and_clear_abort_reason()?;\n            }\n\n            *byte = p.ic_data_cmd().read().dat();\n        }\n\n        Ok(())\n    }\n\n    fn write_blocking_internal(&mut self, write: &[u8], send_stop: bool) -> Result<(), Error> {\n        if write.is_empty() {\n            return Err(Error::InvalidWriteBufferLength);\n        }\n\n        let p = T::regs();\n\n        for (i, byte) in write.iter().enumerate() {\n            let last = i == write.len() - 1;\n\n            p.ic_data_cmd().write(|w| {\n                w.set_stop(send_stop && last);\n                w.set_dat(*byte);\n            });\n\n            // Wait until the transmission of the address/data from the\n            // internal shift register has completed. For this to function\n            // correctly, the TX_EMPTY_CTRL flag in IC_CON must be set. The\n            // TX_EMPTY_CTRL flag was set in i2c_init.\n            while !p.ic_raw_intr_stat().read().tx_empty() {}\n\n            let abort_reason = self.read_and_clear_abort_reason();\n\n            if abort_reason.is_err() || (send_stop && last) {\n                // If the transaction was aborted or if it completed\n                // successfully wait until the STOP condition has occurred.\n\n                while !p.ic_raw_intr_stat().read().stop_det() {}\n\n                p.ic_clr_stop_det().read().clr_stop_det();\n            }\n\n            // Note the hardware issues a STOP automatically on an abort\n            // condition. Note also the hardware clears RX FIFO as well as\n            // TX on abort, ecause we set hwparam\n            // IC_AVOID_RX_FIFO_FLUSH_ON_TX_ABRT to 0.\n            abort_reason?;\n        }\n        Ok(())\n    }\n\n    // =========================\n    // Blocking public API\n    // =========================\n\n    /// Read from address into buffer blocking caller until done.\n    pub fn blocking_read(&mut self, address: impl Into<u16>, read: &mut [u8]) -> Result<(), Error> {\n        Self::setup(address.into())?;\n        self.read_blocking_internal(read, true, true)\n        // Automatic Stop\n    }\n\n    /// Write to address from buffer blocking caller until done.\n    pub fn blocking_write(&mut self, address: impl Into<u16>, write: &[u8]) -> Result<(), Error> {\n        Self::setup(address.into())?;\n        self.write_blocking_internal(write, true)\n    }\n\n    /// Write to address from bytes and read from address into buffer blocking caller until done.\n    pub fn blocking_write_read(&mut self, address: impl Into<u16>, write: &[u8], read: &mut [u8]) -> Result<(), Error> {\n        Self::setup(address.into())?;\n        self.write_blocking_internal(write, false)?;\n        self.read_blocking_internal(read, true, true)\n        // Automatic Stop\n    }\n}\n\nimpl<'d, T: Instance, M: Mode> embedded_hal_02::blocking::i2c::Read for I2c<'d, T, M> {\n    type Error = Error;\n\n    fn read(&mut self, address: u8, buffer: &mut [u8]) -> Result<(), Self::Error> {\n        self.blocking_read(address, buffer)\n    }\n}\n\nimpl<'d, T: Instance, M: Mode> embedded_hal_02::blocking::i2c::Write for I2c<'d, T, M> {\n    type Error = Error;\n\n    fn write(&mut self, address: u8, bytes: &[u8]) -> Result<(), Self::Error> {\n        self.blocking_write(address, bytes)\n    }\n}\n\nimpl<'d, T: Instance, M: Mode> embedded_hal_02::blocking::i2c::WriteRead for I2c<'d, T, M> {\n    type Error = Error;\n\n    fn write_read(&mut self, address: u8, bytes: &[u8], buffer: &mut [u8]) -> Result<(), Self::Error> {\n        self.blocking_write_read(address, bytes, buffer)\n    }\n}\n\nimpl<'d, T: Instance, M: Mode> embedded_hal_02::blocking::i2c::Transactional for I2c<'d, T, M> {\n    type Error = Error;\n\n    fn exec(\n        &mut self,\n        address: u8,\n        operations: &mut [embedded_hal_02::blocking::i2c::Operation<'_>],\n    ) -> Result<(), Self::Error> {\n        Self::setup(address.into())?;\n        for i in 0..operations.len() {\n            let last = i == operations.len() - 1;\n            match &mut operations[i] {\n                embedded_hal_02::blocking::i2c::Operation::Read(buf) => {\n                    self.read_blocking_internal(buf, false, last)?\n                }\n                embedded_hal_02::blocking::i2c::Operation::Write(buf) => self.write_blocking_internal(buf, last)?,\n            }\n        }\n        Ok(())\n    }\n}\n\nimpl embedded_hal_1::i2c::Error for Error {\n    fn kind(&self) -> embedded_hal_1::i2c::ErrorKind {\n        match *self {\n            Self::Abort(AbortReason::ArbitrationLoss) => embedded_hal_1::i2c::ErrorKind::ArbitrationLoss,\n            Self::Abort(AbortReason::NoAcknowledge) => {\n                embedded_hal_1::i2c::ErrorKind::NoAcknowledge(embedded_hal_1::i2c::NoAcknowledgeSource::Address)\n            }\n            Self::Abort(AbortReason::TxNotEmpty(_)) => embedded_hal_1::i2c::ErrorKind::Other,\n            Self::Abort(AbortReason::Other(_)) => embedded_hal_1::i2c::ErrorKind::Other,\n            Self::InvalidReadBufferLength => embedded_hal_1::i2c::ErrorKind::Other,\n            Self::InvalidWriteBufferLength => embedded_hal_1::i2c::ErrorKind::Other,\n            Self::AddressOutOfRange(_) => embedded_hal_1::i2c::ErrorKind::Other,\n            #[allow(deprecated)]\n            Self::AddressReserved(_) => embedded_hal_1::i2c::ErrorKind::Other,\n        }\n    }\n}\n\nimpl<'d, T: Instance, M: Mode> embedded_hal_1::i2c::ErrorType for I2c<'d, T, M> {\n    type Error = Error;\n}\n\nimpl<'d, T: Instance, M: Mode> embedded_hal_1::i2c::I2c for I2c<'d, T, M> {\n    fn read(&mut self, address: u8, read: &mut [u8]) -> Result<(), Self::Error> {\n        self.blocking_read(address, read)\n    }\n\n    fn write(&mut self, address: u8, write: &[u8]) -> Result<(), Self::Error> {\n        self.blocking_write(address, write)\n    }\n\n    fn write_read(&mut self, address: u8, write: &[u8], read: &mut [u8]) -> Result<(), Self::Error> {\n        self.blocking_write_read(address, write, read)\n    }\n\n    fn transaction(\n        &mut self,\n        address: u8,\n        operations: &mut [embedded_hal_1::i2c::Operation<'_>],\n    ) -> Result<(), Self::Error> {\n        Self::setup(address.into())?;\n        for i in 0..operations.len() {\n            let last = i == operations.len() - 1;\n            match &mut operations[i] {\n                embedded_hal_1::i2c::Operation::Read(buf) => self.read_blocking_internal(buf, false, last)?,\n                embedded_hal_1::i2c::Operation::Write(buf) => self.write_blocking_internal(buf, last)?,\n            }\n        }\n        Ok(())\n    }\n}\n\nimpl<'d, A, T> embedded_hal_async::i2c::I2c<A> for I2c<'d, T, Async>\nwhere\n    A: embedded_hal_async::i2c::AddressMode + Into<u16> + 'static,\n    T: Instance + 'd,\n{\n    async fn read(&mut self, address: A, read: &mut [u8]) -> Result<(), Self::Error> {\n        self.read_async(address, read).await\n    }\n\n    async fn write(&mut self, address: A, write: &[u8]) -> Result<(), Self::Error> {\n        self.write_async(address, write.iter().copied()).await\n    }\n\n    async fn write_read(&mut self, address: A, write: &[u8], read: &mut [u8]) -> Result<(), Self::Error> {\n        self.write_read_async(address, write.iter().copied(), read).await\n    }\n\n    async fn transaction(\n        &mut self,\n        address: A,\n        operations: &mut [embedded_hal_1::i2c::Operation<'_>],\n    ) -> Result<(), Self::Error> {\n        use embedded_hal_1::i2c::Operation;\n\n        let addr: u16 = address.into();\n\n        if !operations.is_empty() {\n            Self::setup(addr)?;\n        }\n        let mut iterator = operations.iter_mut();\n\n        while let Some(op) = iterator.next() {\n            let last = iterator.len() == 0;\n\n            match op {\n                Operation::Read(buffer) => {\n                    self.read_async_internal(buffer, false, last).await?;\n                }\n                Operation::Write(buffer) => {\n                    self.write_async_internal(buffer.iter().cloned(), last).await?;\n                }\n            }\n        }\n        Ok(())\n    }\n}\n\nimpl<'d, T: Instance, M: Mode> embassy_embedded_hal::SetConfig for I2c<'d, T, M> {\n    type Config = Config;\n    type ConfigError = ConfigError;\n\n    fn set_config(&mut self, config: &Self::Config) -> Result<(), Self::ConfigError> {\n        self.set_config_inner(config)\n    }\n}\n\npub(crate) trait SealedInstance {\n    fn regs() -> crate::pac::i2c::I2c;\n    fn reset() -> crate::pac::resets::regs::Peripherals;\n    fn waker() -> &'static AtomicWaker;\n}\n\ntrait SealedMode {}\n\n/// Driver mode.\n#[allow(private_bounds)]\npub trait Mode: SealedMode {}\n\nmacro_rules! impl_mode {\n    ($name:ident) => {\n        impl SealedMode for $name {}\n        impl Mode for $name {}\n    };\n}\n\n/// Blocking mode.\npub struct Blocking;\n/// Async mode.\npub struct Async;\n\nimpl_mode!(Blocking);\nimpl_mode!(Async);\n\n/// I2C instance.\n#[allow(private_bounds)]\npub trait Instance: SealedInstance + PeripheralType {\n    /// Interrupt for this peripheral.\n    type Interrupt: interrupt::typelevel::Interrupt;\n}\n\nmacro_rules! impl_instance {\n    ($type:ident, $irq:ident, $reset:ident) => {\n        impl SealedInstance for peripherals::$type {\n            #[inline]\n            fn regs() -> pac::i2c::I2c {\n                pac::$type\n            }\n\n            #[inline]\n            fn reset() -> pac::resets::regs::Peripherals {\n                let mut ret = pac::resets::regs::Peripherals::default();\n                ret.$reset(true);\n                ret\n            }\n\n            #[inline]\n            fn waker() -> &'static AtomicWaker {\n                static WAKER: AtomicWaker = AtomicWaker::new();\n\n                &WAKER\n            }\n        }\n        impl Instance for peripherals::$type {\n            type Interrupt = crate::interrupt::typelevel::$irq;\n        }\n    };\n}\n\nimpl_instance!(I2C0, I2C0_IRQ, set_i2c0);\nimpl_instance!(I2C1, I2C1_IRQ, set_i2c1);\n\n/// SDA pin.\npub trait SdaPin<T: Instance>: crate::gpio::Pin {}\n/// SCL pin.\npub trait SclPin<T: Instance>: crate::gpio::Pin {}\n\nmacro_rules! impl_pin {\n    ($pin:ident, $instance:ident, $function:ident) => {\n        impl $function<peripherals::$instance> for peripherals::$pin {}\n    };\n}\n\nimpl_pin!(PIN_0, I2C0, SdaPin);\nimpl_pin!(PIN_1, I2C0, SclPin);\nimpl_pin!(PIN_2, I2C1, SdaPin);\nimpl_pin!(PIN_3, I2C1, SclPin);\nimpl_pin!(PIN_4, I2C0, SdaPin);\nimpl_pin!(PIN_5, I2C0, SclPin);\nimpl_pin!(PIN_6, I2C1, SdaPin);\nimpl_pin!(PIN_7, I2C1, SclPin);\nimpl_pin!(PIN_8, I2C0, SdaPin);\nimpl_pin!(PIN_9, I2C0, SclPin);\nimpl_pin!(PIN_10, I2C1, SdaPin);\nimpl_pin!(PIN_11, I2C1, SclPin);\nimpl_pin!(PIN_12, I2C0, SdaPin);\nimpl_pin!(PIN_13, I2C0, SclPin);\nimpl_pin!(PIN_14, I2C1, SdaPin);\nimpl_pin!(PIN_15, I2C1, SclPin);\nimpl_pin!(PIN_16, I2C0, SdaPin);\nimpl_pin!(PIN_17, I2C0, SclPin);\nimpl_pin!(PIN_18, I2C1, SdaPin);\nimpl_pin!(PIN_19, I2C1, SclPin);\nimpl_pin!(PIN_20, I2C0, SdaPin);\nimpl_pin!(PIN_21, I2C0, SclPin);\nimpl_pin!(PIN_22, I2C1, SdaPin);\nimpl_pin!(PIN_23, I2C1, SclPin);\nimpl_pin!(PIN_24, I2C0, SdaPin);\nimpl_pin!(PIN_25, I2C0, SclPin);\nimpl_pin!(PIN_26, I2C1, SdaPin);\nimpl_pin!(PIN_27, I2C1, SclPin);\nimpl_pin!(PIN_28, I2C0, SdaPin);\nimpl_pin!(PIN_29, I2C0, SclPin);\n#[cfg(feature = \"rp235xb\")]\nimpl_pin!(PIN_30, I2C1, SdaPin);\n#[cfg(feature = \"rp235xb\")]\nimpl_pin!(PIN_31, I2C1, SclPin);\n#[cfg(feature = \"rp235xb\")]\nimpl_pin!(PIN_32, I2C0, SdaPin);\n#[cfg(feature = \"rp235xb\")]\nimpl_pin!(PIN_33, I2C0, SclPin);\n#[cfg(feature = \"rp235xb\")]\nimpl_pin!(PIN_34, I2C1, SdaPin);\n#[cfg(feature = \"rp235xb\")]\nimpl_pin!(PIN_35, I2C1, SclPin);\n#[cfg(feature = \"rp235xb\")]\nimpl_pin!(PIN_36, I2C0, SdaPin);\n#[cfg(feature = \"rp235xb\")]\nimpl_pin!(PIN_37, I2C0, SclPin);\n#[cfg(feature = \"rp235xb\")]\nimpl_pin!(PIN_38, I2C1, SdaPin);\n#[cfg(feature = \"rp235xb\")]\nimpl_pin!(PIN_39, I2C1, SclPin);\n#[cfg(feature = \"rp235xb\")]\nimpl_pin!(PIN_40, I2C0, SdaPin);\n#[cfg(feature = \"rp235xb\")]\nimpl_pin!(PIN_41, I2C0, SclPin);\n#[cfg(feature = \"rp235xb\")]\nimpl_pin!(PIN_42, I2C1, SdaPin);\n#[cfg(feature = \"rp235xb\")]\nimpl_pin!(PIN_43, I2C1, SclPin);\n#[cfg(feature = \"rp235xb\")]\nimpl_pin!(PIN_44, I2C0, SdaPin);\n#[cfg(feature = \"rp235xb\")]\nimpl_pin!(PIN_45, I2C0, SclPin);\n#[cfg(feature = \"rp235xb\")]\nimpl_pin!(PIN_46, I2C1, SdaPin);\n#[cfg(feature = \"rp235xb\")]\nimpl_pin!(PIN_47, I2C1, SclPin);\n"
  },
  {
    "path": "embassy-rp/src/i2c_slave.rs",
    "content": "//! I2C slave driver.\nuse core::future;\nuse core::marker::PhantomData;\nuse core::task::Poll;\n\nuse pac::i2c;\n\nuse crate::i2c::{AbortReason, FIFO_SIZE, Instance, InterruptHandler, SclPin, SdaPin, set_up_i2c_pin};\nuse crate::interrupt::typelevel::{Binding, Interrupt};\nuse crate::{Peri, pac};\n\n/// I2C error\n#[derive(Debug, PartialEq, Eq, Clone, Copy)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\n#[non_exhaustive]\npub enum Error {\n    /// I2C abort with error\n    Abort(AbortReason),\n    /// User passed in a response buffer that was 0 length\n    InvalidResponseBufferLength,\n    /// The response buffer length was too short to contain the message\n    ///\n    /// The length parameter will always be the length of the buffer, and is\n    /// provided as a convenience for matching alongside `Command::Write`.\n    PartialWrite(usize),\n    /// The response buffer length was too short to contain the message\n    ///\n    /// The length parameter will always be the length of the buffer, and is\n    /// provided as a convenience for matching alongside `Command::GeneralCall`.\n    PartialGeneralCall(usize),\n}\n\n/// Received command\n#[derive(Debug, Copy, Clone, Eq, PartialEq)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub enum Command {\n    /// General Call\n    GeneralCall(usize),\n    /// Read\n    Read,\n    /// Write+read\n    WriteRead(usize),\n    /// Write\n    Write(usize),\n}\n\n/// Possible responses to responding to a read\n#[derive(Debug, Copy, Clone, Eq, PartialEq)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub enum ReadStatus {\n    /// Transaction Complete, controller naked our last byte\n    Done,\n    /// Transaction Incomplete, controller trying to read more bytes than were provided\n    NeedMoreBytes,\n    /// Transaction Complete, but controller stopped reading bytes before we ran out\n    LeftoverBytes(u16),\n}\n\n/// Slave Configuration\n#[non_exhaustive]\n#[derive(Copy, Clone)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub struct Config {\n    /// Target Address\n    pub addr: u16,\n    /// Control if the peripheral should ack to and report general calls.\n    pub general_call: bool,\n    /// Enable internal pullup on SDA.\n    ///\n    /// Using external pullup resistors is recommended for I2C. If you do\n    /// have external pullups you should not enable this.\n    pub sda_pullup: bool,\n    /// Enable internal pullup on SCL.\n    ///\n    /// Using external pullup resistors is recommended for I2C. If you do\n    /// have external pullups you should not enable this.\n    pub scl_pullup: bool,\n}\n\nimpl Default for Config {\n    fn default() -> Self {\n        Self {\n            addr: 0x55,\n            general_call: true,\n            sda_pullup: true,\n            scl_pullup: true,\n        }\n    }\n}\n\n/// I2CSlave driver.\npub struct I2cSlave<'d, T: Instance> {\n    phantom: PhantomData<&'d mut T>,\n    pending_byte: Option<u8>,\n    config: Config,\n}\n\nimpl<'d, T: Instance> I2cSlave<'d, T> {\n    /// Create a new instance.\n    pub fn new(\n        _peri: Peri<'d, T>,\n        scl: Peri<'d, impl SclPin<T>>,\n        sda: Peri<'d, impl SdaPin<T>>,\n        _irq: impl Binding<T::Interrupt, InterruptHandler<T>>,\n        config: Config,\n    ) -> Self {\n        assert!(config.addr != 0);\n\n        // Configure SCL & SDA pins\n        set_up_i2c_pin(&scl, config.scl_pullup);\n        set_up_i2c_pin(&sda, config.sda_pullup);\n\n        let mut ret = Self {\n            phantom: PhantomData,\n            pending_byte: None,\n            config,\n        };\n\n        ret.reset();\n\n        ret\n    }\n\n    /// Reset the i2c peripheral. If you cancel a respond_to_read, you may stall the bus.\n    /// You can recover the bus by calling this function, but doing so will almost certainly cause\n    /// an i/o error in the master.\n    pub fn reset(&mut self) {\n        let p = T::regs();\n\n        let reset = T::reset();\n        crate::reset::reset(reset);\n        crate::reset::unreset_wait(reset);\n\n        p.ic_enable().write(|w| w.set_enable(false));\n\n        p.ic_sar().write(|w| w.set_ic_sar(self.config.addr));\n        p.ic_con().modify(|w| {\n            w.set_master_mode(false);\n            w.set_ic_slave_disable(false);\n            w.set_tx_empty_ctrl(true);\n            w.set_rx_fifo_full_hld_ctrl(true);\n\n            // This typically makes no sense for a slave, but it is used to\n            // tune spike suppression, according to the datasheet.\n            w.set_speed(pac::i2c::vals::Speed::FAST);\n\n            // Generate stop interrupts for general calls\n            // This also causes stop interrupts for other devices on the bus but those will not be\n            // propagated up to the application.\n            w.set_stop_det_ifaddressed(!self.config.general_call);\n        });\n        p.ic_ack_general_call()\n            .write(|w| w.set_ack_gen_call(self.config.general_call));\n\n        // Set FIFO watermarks to 1 to make things simpler. This is encoded\n        // by a register value of 0. Rx watermark should never change, but Tx watermark will be\n        // adjusted in operation.\n        p.ic_tx_tl().write(|w| w.set_tx_tl(0));\n        p.ic_rx_tl().write(|w| w.set_rx_tl(0));\n\n        // Clear interrupts\n        p.ic_clr_intr().read();\n\n        // Enable I2C block\n        p.ic_enable().write(|w| w.set_enable(true));\n\n        // mask everything initially\n        p.ic_intr_mask().write_value(i2c::regs::IcIntrMask(0));\n        T::Interrupt::unpend();\n        unsafe { T::Interrupt::enable() };\n    }\n\n    /// Calls `f` to check if we are ready or not.\n    /// If not, `g` is called once(to eg enable the required interrupts).\n    /// The waker will always be registered prior to calling `f`.\n    #[inline(always)]\n    async fn wait_on<F, U, G>(&mut self, mut f: F, mut g: G) -> U\n    where\n        F: FnMut(&mut Self) -> Poll<U>,\n        G: FnMut(&mut Self),\n    {\n        future::poll_fn(|cx| {\n            // Register prior to checking the condition\n            T::waker().register(cx.waker());\n            let r = f(self);\n\n            if r.is_pending() {\n                g(self);\n            }\n\n            r\n        })\n        .await\n    }\n\n    #[inline(always)]\n    fn drain_fifo(&mut self, buffer: &mut [u8], offset: &mut usize) {\n        let p = T::regs();\n\n        if let Some(pending) = self.pending_byte.take() {\n            buffer[*offset] = pending;\n            *offset += 1;\n        }\n\n        for b in &mut buffer[*offset..] {\n            if !p.ic_status().read().rfne() {\n                break;\n            }\n\n            let dat = p.ic_data_cmd().read();\n            if *offset != 0 && dat.first_data_byte() {\n                // The RP2040 state machine will keep placing bytes into the\n                // FIFO, even if they are part of a subsequent write transaction.\n                //\n                // Unfortunately merely reading ic_data_cmd will consume that\n                // byte, the first byte of the next transaction, so we need\n                // to store it elsewhere\n                self.pending_byte = Some(dat.dat());\n                break;\n            }\n\n            *b = dat.dat();\n            *offset += 1;\n        }\n    }\n\n    /// Wait asynchronously for commands from an I2C master.\n    /// `buffer` is provided in case master does a 'write', 'write read', or 'general call' and is unused for 'read'.\n    pub async fn listen(&mut self, buffer: &mut [u8]) -> Result<Command, Error> {\n        let p = T::regs();\n\n        // set rx fifo watermark to 1 byte\n        p.ic_rx_tl().write(|w| w.set_rx_tl(0));\n\n        let mut len = 0;\n        self.wait_on(\n            |me| {\n                let stat = p.ic_raw_intr_stat().read();\n                trace!(\"ls:{:013b} len:{}\", stat.0, len);\n\n                if p.ic_rxflr().read().rxflr() > 0 || me.pending_byte.is_some() {\n                    me.drain_fifo(buffer, &mut len);\n                    // we're receiving data, set rx fifo watermark to 12 bytes (3/4 full) to reduce interrupt noise\n                    p.ic_rx_tl().write(|w| w.set_rx_tl(11));\n                }\n\n                if buffer.len() == len {\n                    if stat.gen_call() {\n                        return Poll::Ready(Err(Error::PartialGeneralCall(buffer.len())));\n                    } else {\n                        return Poll::Ready(Err(Error::PartialWrite(buffer.len())));\n                    }\n                }\n                trace!(\"len:{}, pend:{:?}\", len, me.pending_byte);\n                if me.pending_byte.is_some() {\n                    warn!(\"pending\")\n                }\n\n                if stat.restart_det() && stat.rd_req() {\n                    p.ic_clr_restart_det().read();\n                    Poll::Ready(Ok(Command::WriteRead(len)))\n                } else if stat.gen_call() && stat.stop_det() && len > 0 {\n                    p.ic_clr_gen_call().read();\n                    p.ic_clr_stop_det().read();\n                    Poll::Ready(Ok(Command::GeneralCall(len)))\n                } else if stat.stop_det() && len > 0 {\n                    p.ic_clr_stop_det().read();\n                    Poll::Ready(Ok(Command::Write(len)))\n                } else if stat.rd_req() {\n                    p.ic_clr_stop_det().read();\n                    p.ic_clr_restart_det().read();\n                    p.ic_clr_gen_call().read();\n                    Poll::Ready(Ok(Command::Read))\n                } else if stat.stop_det() {\n                    // clear stuck stop bit\n                    // This can happen if the SDA/SCL pullups are enabled after calling this func\n                    p.ic_clr_stop_det().read();\n                    Poll::Pending\n                } else {\n                    Poll::Pending\n                }\n            },\n            |_me| {\n                p.ic_intr_mask().write(|w| {\n                    w.set_m_stop_det(true);\n                    w.set_m_restart_det(true);\n                    w.set_m_gen_call(true);\n                    w.set_m_rd_req(true);\n                    w.set_m_rx_full(true);\n                });\n            },\n        )\n        .await\n    }\n\n    /// Respond to an I2C master READ command, asynchronously.\n    pub async fn respond_to_read(&mut self, buffer: &[u8]) -> Result<ReadStatus, Error> {\n        let p = T::regs();\n\n        if buffer.is_empty() {\n            return Err(Error::InvalidResponseBufferLength);\n        }\n\n        let mut chunks = buffer.chunks(FIFO_SIZE as usize);\n\n        self.wait_on(\n            |me| {\n                let stat = p.ic_raw_intr_stat().read();\n                trace!(\"rs:{:013b}\", stat.0);\n\n                if stat.tx_abrt() {\n                    if let Err(abort_reason) = me.read_and_clear_abort_reason() {\n                        if let Error::Abort(AbortReason::TxNotEmpty(bytes)) = abort_reason {\n                            p.ic_clr_intr().read();\n                            return Poll::Ready(Ok(ReadStatus::LeftoverBytes(bytes)));\n                        } else {\n                            return Poll::Ready(Err(abort_reason));\n                        }\n                    }\n                }\n\n                if let Some(chunk) = chunks.next() {\n                    for byte in chunk {\n                        p.ic_clr_rd_req().read();\n                        p.ic_data_cmd().write(|w| w.set_dat(*byte));\n                    }\n\n                    Poll::Pending\n                } else if stat.rx_done() {\n                    p.ic_clr_rx_done().read();\n                    Poll::Ready(Ok(ReadStatus::Done))\n                } else if stat.rd_req() && stat.tx_empty() {\n                    Poll::Ready(Ok(ReadStatus::NeedMoreBytes))\n                } else {\n                    Poll::Pending\n                }\n            },\n            |_me| {\n                p.ic_intr_mask().write(|w| {\n                    w.set_m_rx_done(true);\n                    w.set_m_tx_empty(true);\n                    w.set_m_tx_abrt(true);\n                })\n            },\n        )\n        .await\n    }\n\n    /// Respond to reads with the fill byte until the controller stops asking\n    pub async fn respond_till_stop(&mut self, fill: u8) -> Result<(), Error> {\n        // Send fill bytes a full fifo at a time, to reduce interrupt noise.\n        // This does mean we'll almost certainly abort the write, but since these are fill bytes,\n        // we don't care.\n        let buff = [fill; FIFO_SIZE as usize];\n        loop {\n            match self.respond_to_read(&buff).await {\n                Ok(ReadStatus::NeedMoreBytes) => (),\n                Ok(ReadStatus::LeftoverBytes(_)) => break Ok(()),\n                Ok(_) => break Ok(()),\n                Err(e) => break Err(e),\n            }\n        }\n    }\n\n    /// Respond to a master read, then fill any remaining read bytes with `fill`\n    pub async fn respond_and_fill(&mut self, buffer: &[u8], fill: u8) -> Result<ReadStatus, Error> {\n        let resp_stat = self.respond_to_read(buffer).await?;\n\n        if resp_stat == ReadStatus::NeedMoreBytes {\n            self.respond_till_stop(fill).await?;\n            Ok(ReadStatus::Done)\n        } else {\n            Ok(resp_stat)\n        }\n    }\n\n    #[inline(always)]\n    fn read_and_clear_abort_reason(&mut self) -> Result<(), Error> {\n        let p = T::regs();\n        let abort_reason = p.ic_tx_abrt_source().read();\n\n        if abort_reason.0 != 0 {\n            // Note clearing the abort flag also clears the reason, and this\n            // instance of flag is clear-on-read! Note also the\n            // IC_CLR_TX_ABRT register always reads as 0.\n            p.ic_clr_tx_abrt().read();\n\n            let reason = if abort_reason.abrt_7b_addr_noack()\n                | abort_reason.abrt_10addr1_noack()\n                | abort_reason.abrt_10addr2_noack()\n            {\n                AbortReason::NoAcknowledge\n            } else if abort_reason.arb_lost() {\n                AbortReason::ArbitrationLoss\n            } else if abort_reason.tx_flush_cnt() > 0 {\n                AbortReason::TxNotEmpty(abort_reason.tx_flush_cnt())\n            } else {\n                AbortReason::Other(abort_reason.0)\n            };\n\n            Err(Error::Abort(reason))\n        } else {\n            Ok(())\n        }\n    }\n}\n"
  },
  {
    "path": "embassy-rp/src/intrinsics.rs",
    "content": "#![macro_use]\n\n// Credit: taken from `rp-hal` (also licensed Apache+MIT)\n// https://github.com/rp-rs/rp-hal/blob/main/rp2040-hal/src/intrinsics.rs\n\n/// Generate a series of aliases for an intrinsic function.\nmacro_rules! intrinsics_aliases {\n    (\n        extern $abi:tt fn $name:ident( $($argname:ident: $ty:ty),* ) -> $ret:ty,\n    ) => {};\n    (\n        unsafe extern $abi:tt fn $name:ident( $($argname:ident: $ty:ty),* ) -> $ret:ty,\n    ) => {};\n\n    (\n        extern $abi:tt fn $name:ident( $($argname:ident: $ty:ty),* ) -> $ret:ty,\n        $alias:ident\n        $($rest:ident)*\n    ) => {\n        #[cfg(all(target_arch = \"arm\", feature = \"intrinsics\"))]\n        intrinsics! {\n            extern $abi fn $alias( $($argname: $ty),* ) -> $ret {\n                $name($($argname),*)\n            }\n        }\n\n        intrinsics_aliases! {\n            extern $abi fn $name( $($argname: $ty),* ) -> $ret,\n            $($rest)*\n        }\n    };\n\n    (\n        unsafe extern $abi:tt fn $name:ident( $($argname:ident: $ty:ty),* ) -> $ret:ty,\n        $alias:ident\n        $($rest:ident)*\n    ) => {\n        #[cfg(all(target_arch = \"arm\", feature = \"intrinsics\"))]\n        intrinsics! {\n            unsafe extern $abi fn $alias( $($argname: $ty),* ) -> $ret {\n                $name($($argname),*)\n            }\n        }\n\n        intrinsics_aliases! {\n            unsafe extern $abi fn $name( $($argname: $ty),* ) -> $ret,\n            $($rest)*\n        }\n    };\n}\n\n/// The macro used to define overridden intrinsics.\n///\n/// This is heavily inspired by the macro used by compiler-builtins.  The idea\n/// is to abstract anything special that needs to be done to override an\n/// intrinsic function.  Intrinsic generation is disabled for non-ARM targets\n/// so things like CI and docs generation do not have problems.  Additionally\n/// they can be disabled by disabling the crate feature `intrinsics` for\n/// testing or comparing performance.\n///\n/// Like the compiler-builtins macro, it accepts a series of functions that\n/// looks like normal Rust code:\n///\n/// ```rust,ignore\n/// intrinsics! {\n///     extern \"C\" fn foo(a: i32) -> u32 {\n///         // ...\n///     }\n///     #[nonstandard_attribute]\n///     extern \"C\" fn bar(a: i32) -> u32 {\n///         // ...\n///     }\n/// }\n/// ```\n///\n/// Each function can also be decorated with nonstandard attributes to control\n/// additional behaviour:\n///\n/// * `slower_than_default` - indicates that the override is slower than the\n///   default implementation.  Currently this just disables the override\n///   entirely.\n/// * `bootrom_v2` - indicates that the override is only available\n///   on a V2 bootrom or higher.  Only enabled when the feature\n///   `rom-v2-intrinsics` is set.\n/// * `alias` - accepts a list of names to alias the intrinsic to.\n/// * `aeabi` - accepts a list of ARM EABI names to alias to.\n///\nmacro_rules! intrinsics {\n    () => {};\n\n    (\n        #[slower_than_default]\n        $(#[$($attr:tt)*])*\n        extern $abi:tt fn $name:ident( $($argname:ident: $ty:ty),* ) -> $ret:ty {\n            $($body:tt)*\n        }\n\n        $($rest:tt)*\n    ) => {\n        // Not exported, but defined so the actual implementation is\n        // considered used\n        #[allow(dead_code)]\n        fn $name( $($argname: $ty),* ) -> $ret {\n            $($body)*\n        }\n\n        intrinsics!($($rest)*);\n    };\n\n    (\n        #[bootrom_v2]\n        $(#[$($attr:tt)*])*\n        extern $abi:tt fn $name:ident( $($argname:ident: $ty:ty),* ) -> $ret:ty {\n            $($body:tt)*\n        }\n\n        $($rest:tt)*\n    ) => {\n        // Not exported, but defined so the actual implementation is\n        // considered used\n        #[cfg(not(feature = \"rom-v2-intrinsics\"))]\n        #[allow(dead_code)]\n        fn $name( $($argname: $ty),* ) -> $ret {\n            $($body)*\n        }\n\n        #[cfg(feature = \"rom-v2-intrinsics\")]\n        intrinsics! {\n            $(#[$($attr)*])*\n            extern $abi fn $name( $($argname: $ty),* ) -> $ret {\n                $($body)*\n            }\n        }\n\n        intrinsics!($($rest)*);\n    };\n\n    (\n        #[alias = $($alias:ident),*]\n        $(#[$($attr:tt)*])*\n        extern $abi:tt fn $name:ident( $($argname:ident: $ty:ty),* ) -> $ret:ty {\n            $($body:tt)*\n        }\n\n        $($rest:tt)*\n    ) => {\n        intrinsics! {\n            $(#[$($attr)*])*\n            extern $abi fn $name( $($argname: $ty),* ) -> $ret {\n                $($body)*\n            }\n        }\n\n        intrinsics_aliases! {\n            extern $abi fn $name( $($argname: $ty),* ) -> $ret,\n            $($alias) *\n        }\n\n        intrinsics!($($rest)*);\n    };\n\n    (\n        #[alias = $($alias:ident),*]\n        $(#[$($attr:tt)*])*\n        unsafe extern $abi:tt fn $name:ident( $($argname:ident: $ty:ty),* ) -> $ret:ty {\n            $($body:tt)*\n        }\n\n        $($rest:tt)*\n    ) => {\n        intrinsics! {\n            $(#[$($attr)*])*\n            unsafe extern $abi fn $name( $($argname: $ty),* ) -> $ret {\n                $($body)*\n            }\n        }\n\n        intrinsics_aliases! {\n            unsafe extern $abi fn $name( $($argname: $ty),* ) -> $ret,\n            $($alias) *\n        }\n\n        intrinsics!($($rest)*);\n    };\n\n    (\n        #[aeabi = $($alias:ident),*]\n        $(#[$($attr:tt)*])*\n        extern $abi:tt fn $name:ident( $($argname:ident: $ty:ty),* ) -> $ret:ty {\n            $($body:tt)*\n        }\n\n        $($rest:tt)*\n    ) => {\n        intrinsics! {\n            $(#[$($attr)*])*\n            extern $abi fn $name( $($argname: $ty),* ) -> $ret {\n                $($body)*\n            }\n        }\n\n        intrinsics_aliases! {\n            extern \"aapcs\" fn $name( $($argname: $ty),* ) -> $ret,\n            $($alias) *\n        }\n\n        intrinsics!($($rest)*);\n    };\n\n    (\n        $(#[$($attr:tt)*])*\n        extern $abi:tt fn $name:ident( $($argname:ident: $ty:ty),* ) -> $ret:ty {\n            $($body:tt)*\n        }\n\n        $($rest:tt)*\n    ) => {\n        #[cfg(all(target_arch = \"arm\", feature = \"intrinsics\"))]\n        $(#[$($attr)*])*\n        extern $abi fn $name( $($argname: $ty),* ) -> $ret {\n            $($body)*\n        }\n\n        #[cfg(all(target_arch = \"arm\", feature = \"intrinsics\"))]\n        mod $name {\n            #[unsafe(no_mangle)]\n            $(#[$($attr)*])*\n            pub extern $abi fn $name( $($argname: $ty),* ) -> $ret {\n                super::$name($($argname),*)\n            }\n        }\n\n        // Not exported, but defined so the actual implementation is\n        // considered used\n        #[cfg(not(all(target_arch = \"arm\", feature = \"intrinsics\")))]\n        #[allow(dead_code)]\n        fn $name( $($argname: $ty),* ) -> $ret {\n            $($body)*\n        }\n\n        intrinsics!($($rest)*);\n    };\n\n    (\n        $(#[$($attr:tt)*])*\n        unsafe extern $abi:tt fn $name:ident( $($argname:ident: $ty:ty),* ) -> $ret:ty {\n            $($body:tt)*\n        }\n\n        $($rest:tt)*\n    ) => {\n        #[cfg(all(target_arch = \"arm\", feature = \"intrinsics\"))]\n        $(#[$($attr)*])*\n        unsafe extern $abi fn $name( $($argname: $ty),* ) -> $ret {\n            $($body)*\n        }\n\n        #[cfg(all(target_arch = \"arm\", feature = \"intrinsics\"))]\n        mod $name {\n            #[unsafe(no_mangle)]\n            $(#[$($attr)*])*\n            pub unsafe extern $abi fn $name( $($argname: $ty),* ) -> $ret {\n                super::$name($($argname),*)\n            }\n        }\n\n        // Not exported, but defined so the actual implementation is\n        // considered used\n        #[cfg(not(all(target_arch = \"arm\", feature = \"intrinsics\")))]\n        #[allow(dead_code)]\n        unsafe fn $name( $($argname: $ty),* ) -> $ret {\n            $($body)*\n        }\n\n        intrinsics!($($rest)*);\n    };\n}\n\n// Credit: taken from `rp-hal` (also licensed Apache+MIT)\n// https://github.com/rp-rs/rp-hal/blob/main/rp2040-hal/src/sio.rs\n\n// This takes advantage of how AAPCS defines a 64-bit return on 32-bit registers\n// by packing it into r0[0:31] and r1[32:63].  So all we need to do is put\n// the remainder in the high order 32 bits of a 64 bit result.   We can also\n// alias the division operators to these for a similar reason r0 is the\n// result either way and r1 a scratch register, so the caller can't assume it\n// retains the argument value.\n#[cfg(target_arch = \"arm\")]\ncore::arch::global_asm!(\n    \".macro hwdivider_head\",\n    \"ldr    r2, =(0xd0000000)\", // SIO_BASE\n    // Check the DIRTY state of the divider by shifting it into the C\n    // status bit.\n    \"ldr    r3, [r2, #0x078]\", // DIV_CSR\n    \"lsrs   r3, #2\",           // DIRTY = 1, so shift 2 down\n    // We only need to save the state when DIRTY, otherwise we can just do the\n    // division directly.\n    \"bcs    2f\",\n    \"1:\",\n    // Do the actual division now, we're either not DIRTY, or we've saved the\n    // state and branched back here so it's safe now.\n    \".endm\",\n    \".macro hwdivider_tail\",\n    // 8 cycle delay to wait for the result.  Each branch takes two cycles\n    // and fits into a 2-byte Thumb instruction, so this is smaller than\n    // 8 NOPs.\n    \"b      3f\",\n    \"3: b   3f\",\n    \"3: b   3f\",\n    \"3: b   3f\",\n    \"3:\",\n    // Read the quotient last, since that's what clears the dirty flag.\n    \"ldr    r1, [r2, #0x074]\", // DIV_REMAINDER\n    \"ldr    r0, [r2, #0x070]\", // DIV_QUOTIENT\n    // Either return to the caller or back to the state restore.\n    \"bx     lr\",\n    \"2:\",\n    // Since we can't save the signed-ness of the calculation, we have to make\n    // sure that there's at least an 8 cycle delay before we read the result.\n    // The push takes 5 cycles, and we've already spent at least 7 checking\n    // the DIRTY state to get here.\n    \"push   {{r4-r6, lr}}\",\n    // Read the quotient last, since that's what clears the dirty flag.\n    \"ldr    r3, [r2, #0x060]\", // DIV_UDIVIDEND\n    \"ldr    r4, [r2, #0x064]\", // DIV_UDIVISOR\n    \"ldr    r5, [r2, #0x074]\", // DIV_REMAINDER\n    \"ldr    r6, [r2, #0x070]\", // DIV_QUOTIENT\n    // If we get interrupted here (before a write sets the DIRTY flag) it's\n    // fine, since we have the full state, so the interruptor doesn't have to\n    // restore it.  Once the write happens and the DIRTY flag is set, the\n    // interruptor becomes responsible for restoring our state.\n    \"bl     1b\",\n    // If we are interrupted here, then the interruptor will start an incorrect\n    // calculation using a wrong divisor, but we'll restore the divisor and\n    // result ourselves correctly. This sets DIRTY, so any interruptor will\n    // save the state.\n    \"str    r3, [r2, #0x060]\", // DIV_UDIVIDEND\n    // If we are interrupted here, the interruptor may start the calculation\n    // using incorrectly signed inputs, but we'll restore the result ourselves.\n    // This sets DIRTY, so any interruptor will save the state.\n    \"str    r4, [r2, #0x064]\", // DIV_UDIVISOR\n    // If we are interrupted here, the interruptor will have restored\n    // everything but the quotient may be wrongly signed.  If the calculation\n    // started by the above writes is still ongoing it is stopped, so it won't\n    // replace the result we're restoring.  DIRTY and READY set, but only\n    // DIRTY matters to make the interruptor save the state.\n    \"str    r5, [r2, #0x074]\", // DIV_REMAINDER\n    // State fully restored after the quotient write.  This sets both DIRTY\n    // and READY, so whatever we may have interrupted can read the result.\n    \"str    r6, [r2, #0x070]\", // DIV_QUOTIENT\n    \"pop    {{r4-r6, pc}}\",\n    \".endm\",\n);\n\nmacro_rules! division_function {\n    (\n        $name:ident $($intrinsic:ident)* ( $argty:ty ) {\n            $($begin:literal),+\n        }\n    ) => {\n        #[cfg(all(target_arch = \"arm\", feature = \"intrinsics\"))]\n        core::arch::global_asm!(\n            // Mangle the name slightly, since this is a global symbol.\n            concat!(\".section .text._erphal_\", stringify!($name)),\n            concat!(\".global _erphal_\", stringify!($name)),\n            concat!(\".type _erphal_\", stringify!($name), \", %function\"),\n            \".align 2\",\n            concat!(\"_erphal_\", stringify!($name), \":\"),\n            $(\n                concat!(\".global \", stringify!($intrinsic)),\n                concat!(\".type \", stringify!($intrinsic), \", %function\"),\n                concat!(stringify!($intrinsic), \":\"),\n            )*\n\n            \"hwdivider_head\",\n            $($begin),+ ,\n            \"hwdivider_tail\",\n        );\n\n        #[cfg(all(target_arch = \"arm\", not(feature = \"intrinsics\")))]\n        core::arch::global_asm!(\n            // Mangle the name slightly, since this is a global symbol.\n            concat!(\".section .text._erphal_\", stringify!($name)),\n            concat!(\".global _erphal_\", stringify!($name)),\n            concat!(\".type _erphal_\", stringify!($name), \", %function\"),\n            \".align 2\",\n            concat!(\"_erphal_\", stringify!($name), \":\"),\n\n            \"hwdivider_head\",\n            $($begin),+ ,\n            \"hwdivider_tail\",\n        );\n\n        #[cfg(target_arch = \"arm\")]\n        unsafe extern \"aapcs\" {\n            // Connect a local name to global symbol above through FFI.\n            #[link_name = concat!(\"_erphal_\", stringify!($name)) ]\n            fn $name(n: $argty, d: $argty) -> u64;\n        }\n\n        #[cfg(not(target_arch = \"arm\"))]\n        #[allow(unused_variables)]\n        unsafe fn $name(n: $argty, d: $argty) -> u64 { 0 }\n    };\n}\n\ndivision_function! {\n    unsigned_divmod __aeabi_uidivmod __aeabi_uidiv ( u32 ) {\n        \"str    r0, [r2, #0x060]\", // DIV_UDIVIDEND\n        \"str    r1, [r2, #0x064]\"  // DIV_UDIVISOR\n    }\n}\n\ndivision_function! {\n    signed_divmod __aeabi_idivmod __aeabi_idiv ( i32 ) {\n        \"str    r0, [r2, #0x068]\", // DIV_SDIVIDEND\n        \"str    r1, [r2, #0x06c]\"  // DIV_SDIVISOR\n    }\n}\n\nfn divider_unsigned(n: u32, d: u32) -> DivResult<u32> {\n    let packed = unsafe { unsigned_divmod(n, d) };\n    DivResult {\n        quotient: packed as u32,\n        remainder: (packed >> 32) as u32,\n    }\n}\n\nfn divider_signed(n: i32, d: i32) -> DivResult<i32> {\n    let packed = unsafe { signed_divmod(n, d) };\n    // Double casts to avoid sign extension\n    DivResult {\n        quotient: packed as u32 as i32,\n        remainder: (packed >> 32) as u32 as i32,\n    }\n}\n\n/// Result of divide/modulo operation\nstruct DivResult<T> {\n    /// The quotient of divide/modulo operation\n    pub quotient: T,\n    /// The remainder of divide/modulo operation\n    pub remainder: T,\n}\n\nintrinsics! {\n    extern \"C\" fn __udivsi3(n: u32, d: u32) -> u32 {\n        divider_unsigned(n, d).quotient\n    }\n\n    extern \"C\" fn __umodsi3(n: u32, d: u32) -> u32 {\n        divider_unsigned(n, d).remainder\n    }\n\n    extern \"C\" fn __udivmodsi4(n: u32, d: u32, rem: Option<&mut u32>) -> u32 {\n        let quo_rem = divider_unsigned(n, d);\n        if let Some(rem) = rem {\n            *rem = quo_rem.remainder;\n        }\n        quo_rem.quotient\n    }\n\n    extern \"C\" fn __divsi3(n: i32, d: i32) -> i32 {\n        divider_signed(n, d).quotient\n    }\n\n    extern \"C\" fn __modsi3(n: i32, d: i32) -> i32 {\n        divider_signed(n, d).remainder\n    }\n\n    extern \"C\" fn __divmodsi4(n: i32, d: i32, rem: &mut i32) -> i32 {\n        let quo_rem = divider_signed(n, d);\n        *rem = quo_rem.remainder;\n        quo_rem.quotient\n    }\n}\n"
  },
  {
    "path": "embassy-rp/src/lib.rs",
    "content": "#![no_std]\n#![allow(async_fn_in_trait)]\n#![allow(unsafe_op_in_unsafe_fn)]\n#![allow(unused_unsafe)]\n#![doc = include_str!(\"../README.md\")]\n#![warn(missing_docs)]\n\n//! ## Feature flags\n#![doc = document_features::document_features!(feature_label = r#\"<span class=\"stab portability\"><code>{feature}</code></span>\"#)]\n\n// This mod MUST go first, so that the others see its macros.\npub(crate) mod fmt;\n\n#[cfg(feature = \"binary-info\")]\npub use rp_binary_info as binary_info;\n\n#[cfg(feature = \"critical-section-impl\")]\nmod critical_section_impl;\n\n#[cfg(feature = \"rp2040\")]\nmod intrinsics;\n\npub mod adc;\n#[cfg(feature = \"_rp235x\")]\npub mod aon_timer;\n#[cfg(feature = \"_rp235x\")]\npub mod block;\n#[cfg(feature = \"rp2040\")]\npub mod bootsel;\npub mod clocks;\npub(crate) mod datetime;\npub mod dma;\n#[cfg(any(feature = \"executor-thread\", feature = \"executor-interrupt\"))]\npub mod executor;\npub mod flash;\n#[cfg(feature = \"rp2040\")]\nmod float;\npub mod gpio;\npub mod i2c;\npub mod i2c_slave;\npub mod multicore;\n#[cfg(feature = \"_rp235x\")]\npub mod otp;\npub mod pio_programs;\n#[cfg(feature = \"_rp235x\")]\npub mod psram;\npub mod pwm;\n#[cfg(feature = \"_rp235x\")]\npub mod qmi_cs1;\nmod reset;\npub mod rom_data;\n#[cfg(feature = \"rp2040\")]\npub mod rtc;\npub mod spi;\nmod spinlock;\npub mod spinlock_mutex;\n#[cfg(feature = \"time-driver\")]\npub mod time_driver;\n#[cfg(feature = \"_rp235x\")]\npub mod trng;\npub mod uart;\npub mod usb;\npub mod watchdog;\n\n// PIO\npub mod pio;\npub(crate) mod relocate;\n\n// Reexports\npub use embassy_hal_internal::{Peri, PeripheralType};\n#[cfg(feature = \"unstable-pac\")]\npub use rp_pac as pac;\n#[cfg(not(feature = \"unstable-pac\"))]\npub(crate) use rp_pac as pac;\n\n#[cfg(feature = \"rt\")]\npub use crate::pac::NVIC_PRIO_BITS;\n\n#[cfg(feature = \"rp2040\")]\nembassy_hal_internal::interrupt_mod!(\n    TIMER_IRQ_0,\n    TIMER_IRQ_1,\n    TIMER_IRQ_2,\n    TIMER_IRQ_3,\n    PWM_IRQ_WRAP,\n    USBCTRL_IRQ,\n    XIP_IRQ,\n    PIO0_IRQ_0,\n    PIO0_IRQ_1,\n    PIO1_IRQ_0,\n    PIO1_IRQ_1,\n    DMA_IRQ_0,\n    DMA_IRQ_1,\n    IO_IRQ_BANK0,\n    IO_IRQ_QSPI,\n    SIO_IRQ_PROC0,\n    SIO_IRQ_PROC1,\n    CLOCKS_IRQ,\n    SPI0_IRQ,\n    SPI1_IRQ,\n    UART0_IRQ,\n    UART1_IRQ,\n    ADC_IRQ_FIFO,\n    I2C0_IRQ,\n    I2C1_IRQ,\n    RTC_IRQ,\n    SWI_IRQ_0,\n    SWI_IRQ_1,\n    SWI_IRQ_2,\n    SWI_IRQ_3,\n    SWI_IRQ_4,\n    SWI_IRQ_5,\n);\n\n#[cfg(feature = \"_rp235x\")]\nembassy_hal_internal::interrupt_mod!(\n    TIMER0_IRQ_0,\n    TIMER0_IRQ_1,\n    TIMER0_IRQ_2,\n    TIMER0_IRQ_3,\n    TIMER1_IRQ_0,\n    TIMER1_IRQ_1,\n    TIMER1_IRQ_2,\n    TIMER1_IRQ_3,\n    PWM_IRQ_WRAP_0,\n    PWM_IRQ_WRAP_1,\n    DMA_IRQ_0,\n    DMA_IRQ_1,\n    USBCTRL_IRQ,\n    PIO0_IRQ_0,\n    PIO0_IRQ_1,\n    PIO1_IRQ_0,\n    PIO1_IRQ_1,\n    PIO2_IRQ_0,\n    PIO2_IRQ_1,\n    IO_IRQ_BANK0,\n    IO_IRQ_BANK0_NS,\n    IO_IRQ_QSPI,\n    IO_IRQ_QSPI_NS,\n    SIO_IRQ_FIFO,\n    SIO_IRQ_BELL,\n    SIO_IRQ_FIFO_NS,\n    SIO_IRQ_BELL_NS,\n    CLOCKS_IRQ,\n    SPI0_IRQ,\n    SPI1_IRQ,\n    UART0_IRQ,\n    UART1_IRQ,\n    ADC_IRQ_FIFO,\n    I2C0_IRQ,\n    I2C1_IRQ,\n    TRNG_IRQ,\n    PLL_SYS_IRQ,\n    PLL_USB_IRQ,\n    POWMAN_IRQ_POW,\n    POWMAN_IRQ_TIMER,\n    SWI_IRQ_0,\n    SWI_IRQ_1,\n    SWI_IRQ_2,\n    SWI_IRQ_3,\n    SWI_IRQ_4,\n    SWI_IRQ_5,\n);\n\n/// Macro to bind interrupts to handlers.\n///\n/// This defines the right interrupt handlers, and creates a unit struct (like `struct Irqs;`)\n/// and implements the right [`Binding`]s for it. You can pass this struct to drivers to\n/// prove at compile-time that the right interrupts have been bound.\n///\n/// Example of how to bind one interrupt:\n///\n/// ```rust,ignore\n/// use embassy_rp::{bind_interrupts, usb, peripherals};\n///\n/// bind_interrupts!(\n///     /// Binds the USB Interrupts.\n///     struct Irqs {\n///         USBCTRL_IRQ => usb::InterruptHandler<peripherals::USB>;\n///     }\n/// );\n/// ```\n///\n// developer note: this macro can't be in `embassy-hal-internal` due to the use of `$crate`.\n#[macro_export]\nmacro_rules! bind_interrupts {\n    ($(#[$attr:meta])* $vis:vis struct $name:ident {\n        $(\n            $(#[cfg($cond_irq:meta)])?\n            $irq:ident => $(\n                $(#[cfg($cond_handler:meta)])?\n                $handler:ty\n            ),*;\n        )*\n    }) => {\n        #[derive(Copy, Clone)]\n        $(#[$attr])*\n        $vis struct $name;\n\n        $(\n            #[allow(non_snake_case)]\n            #[unsafe(no_mangle)]\n            $(#[cfg($cond_irq)])?\n            unsafe extern \"C\" fn $irq() {\n                unsafe {\n                    $(\n                        $(#[cfg($cond_handler)])?\n                        <$handler as $crate::interrupt::typelevel::Handler<$crate::interrupt::typelevel::$irq>>::on_interrupt();\n\n                    )*\n                }\n            }\n\n            $(#[cfg($cond_irq)])?\n            $crate::bind_interrupts!(@inner\n                $(\n                    $(#[cfg($cond_handler)])?\n                    unsafe impl $crate::interrupt::typelevel::Binding<$crate::interrupt::typelevel::$irq, $handler> for $name {}\n                )*\n            );\n        )*\n    };\n    (@inner $($t:tt)*) => {\n        $($t)*\n    }\n}\n\n#[cfg(feature = \"rp2040\")]\nembassy_hal_internal::peripherals! {\n    PIN_0,\n    PIN_1,\n    PIN_2,\n    PIN_3,\n    PIN_4,\n    PIN_5,\n    PIN_6,\n    PIN_7,\n    PIN_8,\n    PIN_9,\n    PIN_10,\n    PIN_11,\n    PIN_12,\n    PIN_13,\n    PIN_14,\n    PIN_15,\n    PIN_16,\n    PIN_17,\n    PIN_18,\n    PIN_19,\n    PIN_20,\n    PIN_21,\n    PIN_22,\n    PIN_23,\n    PIN_24,\n    PIN_25,\n    PIN_26,\n    PIN_27,\n    PIN_28,\n    PIN_29,\n    PIN_QSPI_SCLK,\n    PIN_QSPI_SS,\n    PIN_QSPI_SD0,\n    PIN_QSPI_SD1,\n    PIN_QSPI_SD2,\n    PIN_QSPI_SD3,\n\n    UART0,\n    UART1,\n\n    SPI0,\n    SPI1,\n\n    I2C0,\n    I2C1,\n\n    DMA_CH0,\n    DMA_CH1,\n    DMA_CH2,\n    DMA_CH3,\n    DMA_CH4,\n    DMA_CH5,\n    DMA_CH6,\n    DMA_CH7,\n    DMA_CH8,\n    DMA_CH9,\n    DMA_CH10,\n    DMA_CH11,\n\n    PWM_SLICE0,\n    PWM_SLICE1,\n    PWM_SLICE2,\n    PWM_SLICE3,\n    PWM_SLICE4,\n    PWM_SLICE5,\n    PWM_SLICE6,\n    PWM_SLICE7,\n\n    USB,\n\n    RTC,\n\n    FLASH,\n\n    ADC,\n    ADC_TEMP_SENSOR,\n\n    CORE1,\n\n    PIO0,\n    PIO1,\n\n    WATCHDOG,\n    BOOTSEL,\n}\n\n#[cfg(feature = \"_rp235x\")]\nembassy_hal_internal::peripherals! {\n    PIN_0,\n    PIN_1,\n    PIN_2,\n    PIN_3,\n    PIN_4,\n    PIN_5,\n    PIN_6,\n    PIN_7,\n    PIN_8,\n    PIN_9,\n    PIN_10,\n    PIN_11,\n    PIN_12,\n    PIN_13,\n    PIN_14,\n    PIN_15,\n    PIN_16,\n    PIN_17,\n    PIN_18,\n    PIN_19,\n    PIN_20,\n    PIN_21,\n    PIN_22,\n    PIN_23,\n    PIN_24,\n    PIN_25,\n    PIN_26,\n    PIN_27,\n    PIN_28,\n    PIN_29,\n    #[cfg(feature = \"rp235xb\")]\n    PIN_30,\n    #[cfg(feature = \"rp235xb\")]\n    PIN_31,\n    #[cfg(feature = \"rp235xb\")]\n    PIN_32,\n    #[cfg(feature = \"rp235xb\")]\n    PIN_33,\n    #[cfg(feature = \"rp235xb\")]\n    PIN_34,\n    #[cfg(feature = \"rp235xb\")]\n    PIN_35,\n    #[cfg(feature = \"rp235xb\")]\n    PIN_36,\n    #[cfg(feature = \"rp235xb\")]\n    PIN_37,\n    #[cfg(feature = \"rp235xb\")]\n    PIN_38,\n    #[cfg(feature = \"rp235xb\")]\n    PIN_39,\n    #[cfg(feature = \"rp235xb\")]\n    PIN_40,\n    #[cfg(feature = \"rp235xb\")]\n    PIN_41,\n    #[cfg(feature = \"rp235xb\")]\n    PIN_42,\n    #[cfg(feature = \"rp235xb\")]\n    PIN_43,\n    #[cfg(feature = \"rp235xb\")]\n    PIN_44,\n    #[cfg(feature = \"rp235xb\")]\n    PIN_45,\n    #[cfg(feature = \"rp235xb\")]\n    PIN_46,\n    #[cfg(feature = \"rp235xb\")]\n    PIN_47,\n    PIN_QSPI_SCLK,\n    PIN_QSPI_SS,\n    PIN_QSPI_SD0,\n    PIN_QSPI_SD1,\n    PIN_QSPI_SD2,\n    PIN_QSPI_SD3,\n\n    UART0,\n    UART1,\n\n    SPI0,\n    SPI1,\n\n    QMI_CS1,\n\n    I2C0,\n    I2C1,\n\n    DMA_CH0,\n    DMA_CH1,\n    DMA_CH2,\n    DMA_CH3,\n    DMA_CH4,\n    DMA_CH5,\n    DMA_CH6,\n    DMA_CH7,\n    DMA_CH8,\n    DMA_CH9,\n    DMA_CH10,\n    DMA_CH11,\n    DMA_CH12,\n    DMA_CH13,\n    DMA_CH14,\n    DMA_CH15,\n\n    PWM_SLICE0,\n    PWM_SLICE1,\n    PWM_SLICE2,\n    PWM_SLICE3,\n    PWM_SLICE4,\n    PWM_SLICE5,\n    PWM_SLICE6,\n    PWM_SLICE7,\n    PWM_SLICE8,\n    PWM_SLICE9,\n    PWM_SLICE10,\n    PWM_SLICE11,\n\n    USB,\n\n    RTC,\n\n    FLASH,\n\n    ADC,\n    ADC_TEMP_SENSOR,\n\n    CORE1,\n\n    PIO0,\n    PIO1,\n    PIO2,\n\n    WATCHDOG,\n    BOOTSEL,\n\n    POWMAN,\n    TRNG\n}\n\n#[cfg(all(not(feature = \"boot2-none\"), feature = \"rp2040\"))]\nmacro_rules! select_bootloader {\n    ( $( $feature:literal => $loader:ident, )+ default => $default:ident ) => {\n        $(\n            #[cfg(feature = $feature)]\n            #[unsafe(link_section = \".boot2\")]\n            #[used]\n            static BOOT2: [u8; 256] = rp2040_boot2::$loader;\n        )*\n\n        #[cfg(not(any( $( feature = $feature),* )))]\n        #[unsafe(link_section = \".boot2\")]\n        #[used]\n        static BOOT2: [u8; 256] = rp2040_boot2::$default;\n    }\n}\n\n#[cfg(all(not(feature = \"boot2-none\"), feature = \"rp2040\"))]\nselect_bootloader! {\n    \"boot2-at25sf128a\" => BOOT_LOADER_AT25SF128A,\n    \"boot2-gd25q64cs\" => BOOT_LOADER_GD25Q64CS,\n    \"boot2-generic-03h\" => BOOT_LOADER_GENERIC_03H,\n    \"boot2-is25lp080\" => BOOT_LOADER_IS25LP080,\n    \"boot2-ram-memcpy\" => BOOT_LOADER_RAM_MEMCPY,\n    \"boot2-w25q080\" => BOOT_LOADER_W25Q080,\n    \"boot2-w25x10cl\" => BOOT_LOADER_W25X10CL,\n    default => BOOT_LOADER_W25Q080\n}\n\n#[cfg(all(not(feature = \"imagedef-none\"), feature = \"_rp235x\"))]\nmacro_rules! select_imagedef {\n    ( $( $feature:literal => $imagedef:ident, )+ default => $default:ident ) => {\n        $(\n            #[cfg(feature = $feature)]\n            #[unsafe(link_section = \".start_block\")]\n            #[used]\n            static IMAGE_DEF: crate::block::ImageDef = crate::block::ImageDef::$imagedef();\n        )*\n\n        #[cfg(not(any( $( feature = $feature),* )))]\n        #[unsafe(link_section = \".start_block\")]\n        #[used]\n        static IMAGE_DEF: crate::block::ImageDef = crate::block::ImageDef::$default();\n    }\n}\n\n#[cfg(all(not(feature = \"imagedef-none\"), feature = \"_rp235x\"))]\nselect_imagedef! {\n    \"imagedef-secure-exe\" => secure_exe,\n    \"imagedef-nonsecure-exe\" => non_secure_exe,\n    default => secure_exe\n}\n\n/// Installs a stack guard for the CORE0 stack in MPU region 0.\n/// Will fail if the MPU is already configured. This function requires\n/// a `_stack_end` symbol to be defined by the linker script, and expects\n/// `_stack_end` to be located at the lowest address (largest depth) of\n/// the stack.\n///\n/// This method can *only* set up stack guards on the currently\n/// executing core. Stack guards for CORE1 are set up automatically,\n/// only CORE0 should ever use this.\n///\n/// # Usage\n///\n/// ```no_run\n/// use embassy_rp::install_core0_stack_guard;\n/// use embassy_executor::{Executor, Spawner};\n///\n/// #[embassy_executor::main]\n/// async fn main(_spawner: Spawner) {\n///     // set up by the linker as follows:\n///     //\n///     //     MEMORY {\n///     //       STACK0: ORIGIN = 0x20040000, LENGTH = 4K\n///     //     }\n///     //\n///     //     _stack_end = ORIGIN(STACK0);\n///     //     _stack_start = _stack_end + LENGTH(STACK0);\n///     //\n///     install_core0_stack_guard().expect(\"MPU already configured\");\n///     let p = embassy_rp::init(Default::default());\n///\n///     // ...\n/// }\n/// ```\npub fn install_core0_stack_guard() -> Result<(), ()> {\n    unsafe extern \"C\" {\n        static mut _stack_end: usize;\n    }\n    unsafe { install_stack_guard(core::ptr::addr_of_mut!(_stack_end)) }\n}\n\n#[cfg(all(feature = \"rp2040\", not(feature = \"_test\")))]\n#[inline(always)]\nunsafe fn install_stack_guard(stack_bottom: *mut usize) -> Result<(), ()> {\n    let core = unsafe { cortex_m::Peripherals::steal() };\n\n    // Fail if MPU is already configured\n    if core.MPU.ctrl.read() != 0 {\n        return Err(());\n    }\n\n    // The minimum we can protect is 32 bytes on a 32 byte boundary, so round up which will\n    // just shorten the valid stack range a tad.\n    let addr = (stack_bottom as u32 + 31) & !31;\n    // Mask is 1 bit per 32 bytes of the 256 byte range... clear the bit for the segment we want\n    let subregion_select = 0xff ^ (1 << ((addr >> 5) & 7));\n    unsafe {\n        core.MPU.ctrl.write(5); // enable mpu with background default map\n        core.MPU.rbar.write((addr & !0xff) | (1 << 4)); // set address and update RNR\n        core.MPU.rasr.write(\n            1 // enable region\n               | (0x7 << 1) // size 2^(7 + 1) = 256\n               | (subregion_select << 8)\n               | 0x10000000, // XN = disable instruction fetch; no other bits means no permissions\n        );\n    }\n    Ok(())\n}\n\n#[cfg(all(feature = \"_rp235x\", not(feature = \"_test\")))]\n#[inline(always)]\nunsafe fn install_stack_guard(stack_bottom: *mut usize) -> Result<(), ()> {\n    // The RP2350 arm cores are cortex-m33 and can use the MSPLIM register to guard the end of stack.\n    // We'll need to do something else for the riscv cores.\n    cortex_m::register::msplim::write(stack_bottom.addr() as u32);\n\n    Ok(())\n}\n\n// This is to hack around cortex_m defaulting to ARMv7 when building tests,\n// so the compile fails when we try to use ARMv8 peripherals.\n#[cfg(feature = \"_test\")]\n#[inline(always)]\nunsafe fn install_stack_guard(_stack_bottom: *mut usize) -> Result<(), ()> {\n    Ok(())\n}\n\n/// HAL configuration for RP.\npub mod config {\n    use crate::clocks::ClockConfig;\n\n    /// HAL configuration passed when initializing.\n    #[non_exhaustive]\n    pub struct Config {\n        /// Clock configuration.\n        pub clocks: ClockConfig,\n    }\n\n    impl Default for Config {\n        fn default() -> Self {\n            Self {\n                clocks: ClockConfig::crystal(12_000_000),\n            }\n        }\n    }\n\n    impl Config {\n        /// Create a new configuration with the provided clock config.\n        pub fn new(clocks: ClockConfig) -> Self {\n            Self { clocks }\n        }\n    }\n}\n\n/// Initialize the `embassy-rp` HAL with the provided configuration.\n///\n/// This returns the peripheral singletons that can be used for creating drivers.\n///\n/// This should only be called once at startup, otherwise it panics.\npub fn init(config: config::Config) -> Peripherals {\n    // Do this first, so that it panics if user is calling `init` a second time\n    // before doing anything important.\n    let peripherals = Peripherals::take();\n\n    unsafe {\n        clocks::init(config.clocks);\n        #[cfg(feature = \"time-driver\")]\n        time_driver::init();\n        dma::init();\n        gpio::init();\n    }\n\n    peripherals\n}\n\n#[cfg(feature = \"rt\")]\n#[cortex_m_rt::pre_init]\nunsafe fn pre_init() {\n    // SIO does not get reset when core0 is reset with either `scb::sys_reset()` or with SWD.\n    // Since we're using SIO spinlock 31 for the critical-section impl, this causes random\n    // hangs if we reset in the middle of a CS, because the next boot sees the spinlock\n    // as locked and waits forever.\n    //\n    // See https://github.com/embassy-rs/embassy/issues/1736\n    // and https://github.com/rp-rs/rp-hal/issues/292\n    // and https://matrix.to/#/!vhKMWjizPZBgKeknOo:matrix.org/$VfOkQgyf1PjmaXZbtycFzrCje1RorAXd8BQFHTl4d5M\n    //\n    // According to Raspberry Pi, this is considered Working As Intended, and not an errata,\n    // even though this behavior is different from every other ARM chip (sys_reset usually resets\n    // the *system* as its name implies, not just the current core).\n    //\n    // To fix this, reset SIO on boot. We must do this in pre_init because it's unsound to do it\n    // in `embassy_rp::init`, since the user could've acquired a CS by then. pre_init is guaranteed\n    // to run before any user code.\n    //\n    // A similar thing could happen with PROC1. It is unclear whether it's possible for PROC1\n    // to stay unreset through a PROC0 reset, so we reset it anyway just in case.\n    //\n    // Important info from PSM logic (from Luke Wren in above Matrix thread)\n    //\n    //     The logic is, each PSM stage is reset if either of the following is true:\n    //     - The previous stage is in reset and FRCE_ON is false\n    //     - FRCE_OFF is true\n    //\n    // The PSM order is SIO -> PROC0 -> PROC1.\n    // So, we have to force-on PROC0 to prevent it from getting reset when resetting SIO.\n    #[cfg(feature = \"rp2040\")]\n    {\n        pac::PSM.frce_on().write_and_wait(|w| {\n            w.set_proc0(true);\n        });\n        // Then reset SIO and PROC1.\n        pac::PSM.frce_off().write_and_wait(|w| {\n            w.set_sio(true);\n            w.set_proc1(true);\n        });\n        // clear force_off first, force_on second. The other way around would reset PROC0.\n        pac::PSM.frce_off().write_and_wait(|_| {});\n        pac::PSM.frce_on().write_and_wait(|_| {});\n    }\n\n    #[cfg(feature = \"_rp235x\")]\n    {\n        // on RP235x, datasheet says \"The FRCE_ON register is a development feature that does nothing in production devices.\"\n        // No idea why they removed it. Removing it means we can't use PSM to reset SIO, because it comes before\n        // PROC0, so we'd need FRCE_ON to prevent resetting ourselves.\n        //\n        // So we just unlock the spinlock manually.\n        pac::SIO.spinlock(31).write_value(1);\n\n        // We can still use PSM to reset PROC1 since it comes after PROC0 in the state machine.\n        pac::PSM.frce_off().write_and_wait(|w| w.set_proc1(true));\n        pac::PSM.frce_off().write_and_wait(|_| {});\n\n        // Make atomics work between cores.\n        enable_actlr_extexclall();\n    }\n}\n\n/// Set the EXTEXCLALL bit in ACTLR.\n///\n/// The default MPU memory map marks all memory as non-shareable, so atomics don't\n/// synchronize memory accesses between cores at all. This bit forces all memory to be\n/// considered shareable regardless of what the MPU says.\n///\n/// TODO: does this interfere somehow if the user wants to use a custom MPU configuration?\n/// maybe we need to add a way to disable this?\n///\n/// This must be done FOR EACH CORE.\n#[cfg(feature = \"_rp235x\")]\nunsafe fn enable_actlr_extexclall() {\n    (&*cortex_m::peripheral::ICB::PTR).actlr.modify(|w| w | (1 << 29));\n}\n\n/// Extension trait for PAC regs, adding atomic xor/bitset/bitclear writes.\n#[allow(unused)]\ntrait RegExt<T: Copy> {\n    #[allow(unused)]\n    fn write_xor<R>(&self, f: impl FnOnce(&mut T) -> R) -> R;\n    fn write_set<R>(&self, f: impl FnOnce(&mut T) -> R) -> R;\n    fn write_clear<R>(&self, f: impl FnOnce(&mut T) -> R) -> R;\n    fn write_and_wait<R>(&self, f: impl FnOnce(&mut T) -> R) -> R\n    where\n        T: PartialEq;\n}\n\nimpl<T: Default + Copy, A: pac::common::Write> RegExt<T> for pac::common::Reg<T, A> {\n    fn write_xor<R>(&self, f: impl FnOnce(&mut T) -> R) -> R {\n        let mut val = Default::default();\n        let res = f(&mut val);\n        unsafe {\n            let ptr = (self.as_ptr() as *mut u8).add(0x1000) as *mut T;\n            ptr.write_volatile(val);\n        }\n        res\n    }\n\n    fn write_set<R>(&self, f: impl FnOnce(&mut T) -> R) -> R {\n        let mut val = Default::default();\n        let res = f(&mut val);\n        unsafe {\n            let ptr = (self.as_ptr() as *mut u8).add(0x2000) as *mut T;\n            ptr.write_volatile(val);\n        }\n        res\n    }\n\n    fn write_clear<R>(&self, f: impl FnOnce(&mut T) -> R) -> R {\n        let mut val = Default::default();\n        let res = f(&mut val);\n        unsafe {\n            let ptr = (self.as_ptr() as *mut u8).add(0x3000) as *mut T;\n            ptr.write_volatile(val);\n        }\n        res\n    }\n\n    fn write_and_wait<R>(&self, f: impl FnOnce(&mut T) -> R) -> R\n    where\n        T: PartialEq,\n    {\n        let mut val = Default::default();\n        let res = f(&mut val);\n        unsafe {\n            self.as_ptr().write_volatile(val);\n            while self.as_ptr().read_volatile() != val {}\n        }\n        res\n    }\n}\n"
  },
  {
    "path": "embassy-rp/src/multicore.rs",
    "content": "//! Multicore support\n//!\n//! This module handles setup of the 2nd cpu core on the rp2040, which we refer to as core1.\n//! It provides functionality for setting up the stack, and starting core1.\n//!\n//! The entrypoint for core1 can be any function that never returns, including closures.\n//!\n//! Enable the `critical-section-impl` feature in embassy-rp when sharing data across cores using\n//! the `embassy-sync` primitives and `CriticalSectionRawMutex`.\n//!\n//! # Usage\n//!\n//! ```no_run\n//! use embassy_rp::multicore::Stack;\n//! use static_cell::StaticCell;\n//! use embassy_executor::Executor;\n//! use core::ptr::addr_of_mut;\n//!\n//! static mut CORE1_STACK: Stack<4096> = Stack::new();\n//! static EXECUTOR0: StaticCell<Executor> = StaticCell::new();\n//! static EXECUTOR1: StaticCell<Executor> = StaticCell::new();\n//!\n//! # // workaround weird error: `main` function not found in crate `rust_out`\n//! # let _ = ();\n//!\n//! #[embassy_executor::task]\n//! async fn core0_task() {\n//!     // ...\n//! }\n//!\n//! #[embassy_executor::task]\n//! async fn core1_task() {\n//!     // ...\n//! }\n//!\n//! #[cortex_m_rt::entry]\n//! fn main() -> ! {\n//!     let p = embassy_rp::init(Default::default());\n//!\n//!     embassy_rp::multicore::spawn_core1(p.CORE1, unsafe { &mut *addr_of_mut!(CORE1_STACK) }, move || {\n//!         let executor1 = EXECUTOR1.init(Executor::new());\n//!         executor1.run(|spawner| spawner.spawn(core1_task().unwrap()));\n//!     });\n//!\n//!     let executor0 = EXECUTOR0.init(Executor::new());\n//!     executor0.run(|spawner| spawner.spawn(core0_task().unwrap()))\n//! }\n//! ```\n\nuse core::mem::ManuallyDrop;\nuse core::sync::atomic::{AtomicBool, Ordering, compiler_fence};\n\n#[cfg(all(feature = \"rt\", any(feature = \"rp2040\", feature = \"_rp235x\")))]\nuse cortex_m::interrupt::InterruptNumber;\n#[cfg(all(feature = \"rt\", any(feature = \"rp2040\", feature = \"_rp235x\")))]\nuse cortex_m::peripheral::NVIC;\n\nuse crate::interrupt::InterruptExt;\nuse crate::peripherals::CORE1;\nuse crate::{Peri, gpio, install_stack_guard, interrupt, pac};\n\nconst PAUSE_TOKEN: u32 = 0xDEADBEEF;\nconst RESUME_TOKEN: u32 = !0xDEADBEEF;\n#[cfg(all(feature = \"rt\", any(feature = \"rp2040\", feature = \"_rp235x\")))]\npub(crate) const PEND_IRQ_TOKEN: u32 = 0xCAFE0000;\n\nstatic IS_CORE1_INIT: AtomicBool = AtomicBool::new(false);\n\n/// Represents a particular CPU core (SIO_CPUID)\n#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\n#[repr(u8)]\npub enum CoreId {\n    /// Core 0\n    Core0 = 0x0,\n    /// Core 1\n    Core1 = 0x1,\n}\n\n/// Gets which core we are currently executing from\npub fn current_core() -> CoreId {\n    if pac::SIO.cpuid().read() == 0 {\n        CoreId::Core0\n    } else {\n        CoreId::Core1\n    }\n}\n\n#[inline(always)]\nunsafe fn core1_setup(stack_bottom: *mut usize) {\n    if install_stack_guard(stack_bottom).is_err() {\n        // currently only happens if the MPU was already set up, which\n        // would indicate that the core is already in use from outside\n        // embassy, somehow. trap if so since we can't deal with that.\n        cortex_m::asm::udf();\n    }\n\n    #[cfg(feature = \"_rp235x\")]\n    crate::enable_actlr_extexclall();\n\n    unsafe {\n        gpio::init();\n    }\n}\n\n/// Data type for a properly aligned stack of N bytes\n#[repr(C, align(32))]\npub struct Stack<const SIZE: usize> {\n    /// Memory to be used for the stack\n    pub mem: [u8; SIZE],\n}\n\nimpl<const SIZE: usize> Stack<SIZE> {\n    /// Construct a stack of length SIZE, initialized to 0\n    pub const fn new() -> Stack<SIZE> {\n        Stack { mem: [0_u8; SIZE] }\n    }\n}\n\n#[cfg(all(feature = \"rt\", any(feature = \"rp2040\", feature = \"_rp235x\")))]\n#[derive(Clone, Copy)]\nstruct Irq(u16);\n#[cfg(all(feature = \"rt\", any(feature = \"rp2040\", feature = \"_rp235x\")))]\nunsafe impl InterruptNumber for Irq {\n    fn number(self) -> u16 {\n        self.0\n    }\n}\n\n#[cfg(all(feature = \"rt\", feature = \"rp2040\"))]\n#[interrupt]\nunsafe fn SIO_IRQ_PROC1() {\n    let sio = pac::SIO;\n    // Clear IRQ\n    sio.fifo().st().write(|w| w.set_wof(false));\n\n    while sio.fifo().st().read().vld() {\n        let fifo_read = fifo_read_wfe();\n        if fifo_read == PAUSE_TOKEN {\n            // Pause CORE1 execution and disable interrupts\n            cortex_m::interrupt::disable();\n            // Signal to CORE0 that execution is paused\n            fifo_write(PAUSE_TOKEN);\n            // Wait for `resume` signal from CORE0\n            while fifo_read_wfe() != RESUME_TOKEN {\n                cortex_m::asm::nop();\n            }\n            cortex_m::interrupt::enable();\n            // Signal to CORE0 that execution is resumed\n            fifo_write(RESUME_TOKEN);\n        } else if fifo_read & 0xFFFF0000 == PEND_IRQ_TOKEN {\n            // Pend the IRQ to wake up interrupt executors.\n            let irq = Irq((fifo_read & 0xFFFF) as u16);\n            NVIC::pend(irq);\n        }\n    }\n}\n\n#[cfg(all(feature = \"rt\", feature = \"_rp235x\"))]\n#[interrupt]\nunsafe fn SIO_IRQ_FIFO() {\n    let sio = pac::SIO;\n    // Clear IRQ\n    sio.fifo().st().write(|w| w.set_wof(false));\n\n    while sio.fifo().st().read().vld() {\n        let fifo_read = fifo_read_wfe();\n        if fifo_read == PAUSE_TOKEN {\n            // Pause CORE1 execution and disable interrupts\n            cortex_m::interrupt::disable();\n            // Signal to CORE0 that execution is paused\n            fifo_write(PAUSE_TOKEN);\n            // Wait for `resume` signal from CORE0\n            while fifo_read_wfe() != RESUME_TOKEN {\n                cortex_m::asm::nop();\n            }\n            cortex_m::interrupt::enable();\n            // Signal to CORE0 that execution is resumed\n            fifo_write(RESUME_TOKEN);\n        } else if fifo_read & 0xFFFF0000 == PEND_IRQ_TOKEN {\n            // Pend the IRQ to wake up interrupt executors.\n            let irq = Irq((fifo_read & 0xFFFF) as u16);\n            let mut nvic: NVIC = core::mem::transmute(());\n            nvic.request(irq);\n        }\n    }\n}\n\n/// Spawn a function on this core\npub fn spawn_core1<F, const SIZE: usize>(_core1: Peri<'static, CORE1>, stack: &'static mut Stack<SIZE>, entry: F)\nwhere\n    F: FnOnce() -> bad::Never + Send + 'static,\n{\n    // The first two ignored `u64` parameters are there to take up all of the registers,\n    // which means that the rest of the arguments are taken from the stack,\n    // where we're able to put them from core 0.\n    extern \"C\" fn core1_startup<F: FnOnce() -> bad::Never>(\n        _: u64,\n        _: u64,\n        entry: *mut ManuallyDrop<F>,\n        stack_bottom: *mut usize,\n    ) -> ! {\n        unsafe { core1_setup(stack_bottom) };\n\n        let entry = unsafe { ManuallyDrop::take(&mut *entry) };\n\n        // make sure the preceding read doesn't get reordered past the following fifo write\n        compiler_fence(Ordering::SeqCst);\n\n        // Signal that it's safe for core 0 to get rid of the original value now.\n        fifo_write(1);\n\n        IS_CORE1_INIT.store(true, Ordering::Release);\n        // Enable fifo interrupt on CORE1 for `pause` functionality.\n        #[cfg(feature = \"rp2040\")]\n        unsafe {\n            interrupt::SIO_IRQ_PROC1.enable()\n        };\n        #[cfg(feature = \"_rp235x\")]\n        unsafe {\n            interrupt::SIO_IRQ_FIFO.enable()\n        };\n\n        // Enable FPU\n        #[cfg(all(feature = \"_rp235x\", has_fpu))]\n        unsafe {\n            let p = cortex_m::Peripherals::steal();\n            p.SCB.cpacr.modify(|cpacr| cpacr | (3 << 20) | (3 << 22));\n        }\n\n        entry()\n    }\n\n    // Reset the core\n    let psm = pac::PSM;\n    psm.frce_off().modify(|w| w.set_proc1(true));\n    while !psm.frce_off().read().proc1() {\n        cortex_m::asm::nop();\n    }\n    psm.frce_off().modify(|w| w.set_proc1(false));\n\n    // The ARM AAPCS ABI requires 8-byte stack alignment.\n    // #[align] on `struct Stack` ensures the bottom is aligned, but the top could still be\n    // unaligned if the user chooses a stack size that's not multiple of 8.\n    // So, we round down to the next multiple of 8.\n    let stack_words = stack.mem.len() / 8 * 2;\n    let mem = unsafe { core::slice::from_raw_parts_mut(stack.mem.as_mut_ptr() as *mut usize, stack_words) };\n\n    // Set up the stack\n    let mut stack_ptr = unsafe { mem.as_mut_ptr().add(mem.len()) };\n\n    // We don't want to drop this, since it's getting moved to the other core.\n    let mut entry = ManuallyDrop::new(entry);\n\n    // Push the arguments to `core1_startup` onto the stack.\n    unsafe {\n        // Push `stack_bottom`.\n        stack_ptr = stack_ptr.sub(1);\n        stack_ptr.cast::<*mut usize>().write(mem.as_mut_ptr());\n\n        // Push `entry`.\n        stack_ptr = stack_ptr.sub(1);\n        stack_ptr.cast::<*mut ManuallyDrop<F>>().write(&mut entry);\n    }\n\n    // Make sure the compiler does not reorder the stack writes after to after the\n    // below FIFO writes, which would result in them not being seen by the second\n    // core.\n    //\n    // From the compiler perspective, this doesn't guarantee that the second core\n    // actually sees those writes. However, we know that the RP2040 doesn't have\n    // memory caches, and writes happen in-order.\n    compiler_fence(Ordering::Release);\n\n    let p = unsafe { cortex_m::Peripherals::steal() };\n    let vector_table = p.SCB.vtor.read();\n\n    // After reset, core 1 is waiting to receive commands over FIFO.\n    // This is the sequence to have it jump to some code.\n    let cmd_seq = [\n        0,\n        0,\n        1,\n        vector_table as usize,\n        stack_ptr as usize,\n        core1_startup::<F> as *const () as usize,\n    ];\n\n    let mut seq = 0;\n    let mut fails = 0;\n    loop {\n        let cmd = cmd_seq[seq] as u32;\n        if cmd == 0 {\n            fifo_drain();\n            cortex_m::asm::sev();\n        }\n        fifo_write(cmd);\n\n        let response = fifo_read();\n        if cmd == response {\n            seq += 1;\n        } else {\n            seq = 0;\n            fails += 1;\n            if fails > 16 {\n                // The second core isn't responding, and isn't going to take the entrypoint\n                panic!(\"CORE1 not responding\");\n            }\n        }\n        if seq >= cmd_seq.len() {\n            break;\n        }\n    }\n\n    // Wait until the other core has copied `entry` before returning.\n    fifo_read();\n\n    // Enable fifo interrupt on CORE0 for `pend irq` functionality.\n    #[cfg(all(feature = \"rp2040\", feature = \"executor-interrupt\"))]\n    unsafe {\n        interrupt::SIO_IRQ_PROC1.enable()\n    };\n    #[cfg(all(feature = \"_rp235x\", feature = \"executor-interrupt\"))]\n    unsafe {\n        interrupt::SIO_IRQ_FIFO.enable()\n    };\n}\n\n/// Pause execution on CORE1.\npub fn pause_core1() {\n    if IS_CORE1_INIT.load(Ordering::Acquire) {\n        fifo_write(PAUSE_TOKEN);\n        // Wait for CORE1 to signal it has paused execution.\n        while fifo_read() != PAUSE_TOKEN {}\n    }\n}\n\n/// Resume CORE1 execution.\npub fn resume_core1() {\n    if IS_CORE1_INIT.load(Ordering::Acquire) {\n        fifo_write(RESUME_TOKEN);\n        // Wait for CORE1 to signal it has resumed execution.\n        while fifo_read() != RESUME_TOKEN {}\n    }\n}\n\n// Push a value to the inter-core FIFO, block until space is available\n#[inline(always)]\npub(crate) fn fifo_write(value: u32) {\n    let sio = pac::SIO;\n    // Wait for the FIFO to have enough space\n    while !sio.fifo().st().read().rdy() {\n        cortex_m::asm::nop();\n    }\n    sio.fifo().wr().write_value(value);\n    // Fire off an event to the other core.\n    // This is required as the other core may be `wfe` (waiting for event)\n    cortex_m::asm::sev();\n}\n\n// Pop a value from inter-core FIFO, block until available\n#[inline(always)]\nfn fifo_read() -> u32 {\n    let sio = pac::SIO;\n    // Wait until FIFO has data\n    while !sio.fifo().st().read().vld() {\n        cortex_m::asm::nop();\n    }\n    sio.fifo().rd().read()\n}\n\n// Pop a value from inter-core FIFO, `wfe` until available\n#[inline(always)]\n#[allow(unused)]\nfn fifo_read_wfe() -> u32 {\n    let sio = pac::SIO;\n    // Wait until FIFO has data\n    while !sio.fifo().st().read().vld() {\n        cortex_m::asm::wfe();\n    }\n    sio.fifo().rd().read()\n}\n\n// Drain inter-core FIFO\n#[inline(always)]\nfn fifo_drain() {\n    let sio = pac::SIO;\n    while sio.fifo().st().read().vld() {\n        let _ = sio.fifo().rd().read();\n    }\n}\n\n// https://github.com/nvzqz/bad-rs/blob/master/src/never.rs\nmod bad {\n    pub(crate) type Never = <F as HasOutput>::Output;\n\n    pub trait HasOutput {\n        type Output;\n    }\n\n    impl<O> HasOutput for fn() -> O {\n        type Output = O;\n    }\n\n    type F = fn() -> !;\n}\n"
  },
  {
    "path": "embassy-rp/src/otp.rs",
    "content": "//! Interface to the RP2350's One Time Programmable Memory\n\n// Credit: taken from `rp-hal` (also licensed Apache+MIT)\n// https://github.com/rp-rs/rp-hal/blob/main/rp235x-hal/src/rom_data.rs\n\nuse crate::rom_data::otp_access;\n\n/// The ways in which we can fail to access OTP\n#[derive(Debug, Clone)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub enum Error {\n    /// The user passed an invalid index to a function.\n    InvalidIndex,\n    /// The hardware refused to let us read this word, probably due to\n    /// read or write lock set earlier in the boot process.\n    InvalidPermissions,\n    /// Modification is impossible based on current state; e.g.\n    /// attempted to clear an OTP bit.\n    UnsupportedModification,\n    /// Value being written is bigger than 24 bits allowed for raw writes.\n    Overflow,\n    /// An unexpected failure that contains the exact return code\n    UnexpectedFailure(i32),\n}\n\n/// OTP read address, using automatic Error Correction.\n///\n/// A 32-bit read returns the ECC-corrected data for two neighbouring rows, or\n/// all-ones on permission failure. Only the first 8 KiB is populated.\npub const OTP_DATA_BASE: *const u32 = 0x4013_0000 as *const u32;\n\n/// OTP read address, without using any automatic Error Correction.\n///\n/// A 32-bit read returns 24-bits of raw data from the OTP word.\npub const OTP_DATA_RAW_BASE: *const u32 = 0x4013_4000 as *const u32;\n\n/// How many pages in OTP (post error-correction)\npub const NUM_PAGES: usize = 64;\n\n/// How many rows in one page in OTP (post error-correction)\npub const NUM_ROWS_PER_PAGE: usize = 64;\n\n/// How many rows in OTP (post error-correction)\npub const NUM_ROWS: usize = NUM_PAGES * NUM_ROWS_PER_PAGE;\n\n/// 24bit mask for raw writes\npub const RAW_WRITE_BIT_MASK: u32 = 0x00FF_FFFF;\n\n/// Read one ECC protected word from the OTP\npub fn read_ecc_word(row: usize) -> Result<u16, Error> {\n    if row >= NUM_ROWS {\n        return Err(Error::InvalidIndex);\n    }\n    // First do a raw read to check permissions\n    let _ = read_raw_word(row)?;\n    // One 32-bit read gets us two rows\n    let offset = row >> 1;\n    // # Safety\n    //\n    // We checked this offset was in range already.\n    let value = unsafe { OTP_DATA_BASE.add(offset).read() };\n    if (row & 1) == 0 {\n        Ok(value as u16)\n    } else {\n        Ok((value >> 16) as u16)\n    }\n}\n\n/// Read one raw word from the OTP\n///\n/// You get the 24-bit raw value in the lower part of the 32-bit result.\npub fn read_raw_word(row: usize) -> Result<u32, Error> {\n    if row >= NUM_ROWS {\n        return Err(Error::InvalidIndex);\n    }\n    // One 32-bit read gets us one row\n    // # Safety\n    //\n    // We checked this offset was in range already.\n    let value = unsafe { OTP_DATA_RAW_BASE.add(row).read() };\n    if value == 0xFFFF_FFFF {\n        Err(Error::InvalidPermissions)\n    } else {\n        Ok(value)\n    }\n}\n/// Write one raw word to the OTP\n///\n/// 24 bit value will be written to the OTP\npub fn write_raw_word(row: usize, data: u32) -> Result<(), Error> {\n    if data > RAW_WRITE_BIT_MASK {\n        return Err(Error::Overflow);\n    }\n    if row >= NUM_ROWS {\n        return Err(Error::InvalidIndex);\n    }\n    let row_with_write_bit = row | 0x00010000;\n    // # Safety\n    //\n    // We checked this row was in range already.\n    let result = unsafe { otp_access(data.to_le_bytes().as_mut_ptr(), 4, row_with_write_bit as u32) };\n    if result == 0 {\n        Ok(())\n    } else {\n        // 5.4.3. API Function Return Codes\n        let error = match result {\n            -4 => Error::InvalidPermissions,\n            -18 => Error::UnsupportedModification,\n            _ => Error::UnexpectedFailure(result),\n        };\n        Err(error)\n    }\n}\n\n/// Write one raw word to the OTP with ECC\n///\n/// 16 bit value will be written + ECC\npub fn write_ecc_word(row: usize, data: u16) -> Result<(), Error> {\n    if row >= NUM_ROWS {\n        return Err(Error::InvalidIndex);\n    }\n    let row_with_write_and_ecc_bit = row | 0x00030000;\n\n    // # Safety\n    //\n    // We checked this row was in range already.\n\n    let result = unsafe { otp_access(data.to_le_bytes().as_mut_ptr(), 2, row_with_write_and_ecc_bit as u32) };\n    if result == 0 {\n        Ok(())\n    } else {\n        // 5.4.3. API Function Return Codes\n        // 5.4.3. API Function Return Codes\n        let error = match result {\n            -4 => Error::InvalidPermissions,\n            -18 => Error::UnsupportedModification,\n            _ => Error::UnexpectedFailure(result),\n        };\n        Err(error)\n    }\n}\n\n/// Get the random 64bit chipid from rows 0x0-0x3.\npub fn get_chipid() -> Result<u64, Error> {\n    let w0 = read_ecc_word(0x000)?.to_be_bytes();\n    let w1 = read_ecc_word(0x001)?.to_be_bytes();\n    let w2 = read_ecc_word(0x002)?.to_be_bytes();\n    let w3 = read_ecc_word(0x003)?.to_be_bytes();\n\n    Ok(u64::from_be_bytes([\n        w3[0], w3[1], w2[0], w2[1], w1[0], w1[1], w0[0], w0[1],\n    ]))\n}\n\n/// Get the 128bit private random number from rows 0x4-0xb.\n///\n/// This ID is not exposed through the USB PICOBOOT GET_INFO command\n/// or the ROM get_sys_info() API. However note that the USB PICOBOOT OTP\n/// access point can read the entirety of page 0, so this value is not\n/// meaningfully private unless the USB PICOBOOT interface is disabled via the\n//// DISABLE_BOOTSEL_USB_PICOBOOT_IFC flag in BOOT_FLAGS0\npub fn get_private_random_number() -> Result<u128, Error> {\n    let w0 = read_ecc_word(0x004)?.to_be_bytes();\n    let w1 = read_ecc_word(0x005)?.to_be_bytes();\n    let w2 = read_ecc_word(0x006)?.to_be_bytes();\n    let w3 = read_ecc_word(0x007)?.to_be_bytes();\n    let w4 = read_ecc_word(0x008)?.to_be_bytes();\n    let w5 = read_ecc_word(0x009)?.to_be_bytes();\n    let w6 = read_ecc_word(0x00a)?.to_be_bytes();\n    let w7 = read_ecc_word(0x00b)?.to_be_bytes();\n\n    Ok(u128::from_be_bytes([\n        w7[0], w7[1], w6[0], w6[1], w5[0], w5[1], w4[0], w4[1], w3[0], w3[1], w2[0], w2[1], w1[0], w1[1], w0[0], w0[1],\n    ]))\n}\n"
  },
  {
    "path": "embassy-rp/src/pio/instr.rs",
    "content": "//! Instructions controlling the PIO.\nuse pio::{InSource, InstructionOperands, JmpCondition, OutDestination, SetDestination};\n\nuse crate::pio::{Instance, StateMachine};\n\nimpl<'d, PIO: Instance, const SM: usize> StateMachine<'d, PIO, SM> {\n    /// Set value of scratch register X.\n    ///\n    /// SAFETY: autopull enabled else it will write undefined data.\n    /// Make sure to have setup the according configuration see\n    /// [shift_out](crate::pio::Config::shift_out) and [auto_fill](crate::pio::ShiftConfig::auto_fill).\n    pub unsafe fn set_x(&mut self, value: u32) {\n        const OUT: u16 = InstructionOperands::OUT {\n            destination: OutDestination::X,\n            bit_count: 32,\n        }\n        .encode();\n        self.tx().push(value);\n        self.exec_instr(OUT);\n    }\n\n    /// Get value of scratch register X.\n    ///\n    /// SAFETY: autopush enabled else it will read undefined data.\n    /// Make sure to have setup the according configurations see\n    /// [shift_in](crate::pio::Config::shift_in) and [auto_fill](crate::pio::ShiftConfig::auto_fill).\n    pub unsafe fn get_x(&mut self) -> u32 {\n        const IN: u16 = InstructionOperands::IN {\n            source: InSource::X,\n            bit_count: 32,\n        }\n        .encode();\n        self.exec_instr(IN);\n        self.rx().pull()\n    }\n\n    /// Set value of scratch register Y.\n    ///\n    /// SAFETY: autopull enabled else it will write undefined data.\n    /// Make sure to have setup the according configuration see\n    /// [shift_out](crate::pio::Config::shift_out) and [auto_fill](crate::pio::ShiftConfig::auto_fill).\n    pub unsafe fn set_y(&mut self, value: u32) {\n        const OUT: u16 = InstructionOperands::OUT {\n            destination: OutDestination::Y,\n            bit_count: 32,\n        }\n        .encode();\n        self.tx().push(value);\n        self.exec_instr(OUT);\n    }\n\n    /// Get value of scratch register Y.\n    ///\n    /// SAFETY: autopush enabled else it will read undefined data.\n    /// Make sure to have setup the according configurations see\n    /// [shift_in](crate::pio::Config::shift_in) and [auto_fill](crate::pio::ShiftConfig::auto_fill).\n    pub unsafe fn get_y(&mut self) -> u32 {\n        const IN: u16 = InstructionOperands::IN {\n            source: InSource::Y,\n            bit_count: 32,\n        }\n        .encode();\n        self.exec_instr(IN);\n\n        self.rx().pull()\n    }\n\n    /// Set instruction for pindir destination.\n    pub unsafe fn set_pindir(&mut self, data: u8) {\n        let set: u16 = InstructionOperands::SET {\n            destination: SetDestination::PINDIRS,\n            data,\n        }\n        .encode();\n        self.exec_instr(set);\n    }\n\n    /// Set instruction for pin destination.\n    pub unsafe fn set_pin(&mut self, data: u8) {\n        let set: u16 = InstructionOperands::SET {\n            destination: SetDestination::PINS,\n            data,\n        }\n        .encode();\n        self.exec_instr(set);\n    }\n\n    /// Out instruction for pin destination.\n    pub unsafe fn set_out_pin(&mut self, data: u32) {\n        const OUT: u16 = InstructionOperands::OUT {\n            destination: OutDestination::PINS,\n            bit_count: 32,\n        }\n        .encode();\n        self.tx().push(data);\n        self.exec_instr(OUT);\n    }\n\n    /// Out instruction for pindir destination.\n    pub unsafe fn set_out_pindir(&mut self, data: u32) {\n        const OUT: u16 = InstructionOperands::OUT {\n            destination: OutDestination::PINDIRS,\n            bit_count: 32,\n        }\n        .encode();\n        self.tx().push(data);\n        self.exec_instr(OUT);\n    }\n\n    /// Jump instruction to address.\n    pub unsafe fn exec_jmp(&mut self, to_addr: u8) {\n        let jmp: u16 = InstructionOperands::JMP {\n            address: to_addr,\n            condition: JmpCondition::Always,\n        }\n        .encode();\n        self.exec_instr(jmp);\n    }\n}\n"
  },
  {
    "path": "embassy-rp/src/pio/mod.rs",
    "content": "//! PIO driver.\nuse core::future::Future;\nuse core::marker::PhantomData;\nuse core::pin::Pin as FuturePin;\nuse core::sync::atomic::{AtomicU8, AtomicU32, Ordering};\nuse core::task::{Context, Poll};\n\nuse embassy_hal_internal::{Peri, PeripheralType};\nuse embassy_sync::waitqueue::AtomicWaker;\nuse fixed::FixedU32;\nuse fixed::types::extra::U8;\nuse pio::{Program, SideSet, Wrap};\n\nuse crate::dma::{self, Transfer, Word};\nuse crate::gpio::{self, AnyPin, Drive, Level, Pull, SealedPin, SlewRate};\nuse crate::interrupt::typelevel::{Binding, Handler, Interrupt};\nuse crate::relocate::RelocatedProgram;\nuse crate::{RegExt, pac, peripherals};\n\nmod instr;\n\n#[doc(inline)]\npub use pio as program;\n\n/// Wakers for interrupts and FIFOs.\npub struct Wakers([AtomicWaker; 12]);\n\nimpl Wakers {\n    #[inline(always)]\n    fn fifo_in(&self) -> &[AtomicWaker] {\n        &self.0[0..4]\n    }\n    #[inline(always)]\n    fn fifo_out(&self) -> &[AtomicWaker] {\n        &self.0[4..8]\n    }\n    #[inline(always)]\n    fn irq(&self) -> &[AtomicWaker] {\n        &self.0[8..12]\n    }\n}\n\n/// FIFO config.\n#[derive(Clone, Copy, PartialEq, Eq, Default, Debug)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\n#[repr(u8)]\npub enum FifoJoin {\n    /// Both TX and RX fifo is enabled\n    #[default]\n    Duplex,\n    /// Rx fifo twice as deep. TX fifo disabled\n    RxOnly,\n    /// Tx fifo twice as deep. RX fifo disabled\n    TxOnly,\n    /// Enable random writes (`FJOIN_RX_PUT`) from the state machine (through ISR),\n    /// and random reads from the system (using [`StateMachine::get_rxf_entry`]).\n    #[cfg(feature = \"_rp235x\")]\n    RxAsStatus,\n    /// Enable random reads (`FJOIN_RX_GET`) from the state machine (through OSR),\n    /// and random writes from the system (using [`StateMachine::set_rxf_entry`]).\n    #[cfg(feature = \"_rp235x\")]\n    RxAsControl,\n    /// FJOIN_RX_PUT | FJOIN_RX_GET: RX can be used as a scratch register,\n    /// not accessible from the CPU\n    #[cfg(feature = \"_rp235x\")]\n    PioScratch,\n}\n\n/// Shift direction.\n#[allow(missing_docs)]\n#[derive(Clone, Copy, PartialEq, Eq, Default, Debug)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\n#[repr(u8)]\npub enum ShiftDirection {\n    #[default]\n    Right = 1,\n    Left = 0,\n}\n\n/// Pin direction.\n#[allow(missing_docs)]\n#[derive(Clone, Copy, PartialEq, Eq, Debug)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\n#[repr(u8)]\npub enum Direction {\n    In = 0,\n    Out = 1,\n}\n\n/// Which fifo level to use in status check.\n#[derive(Clone, Copy, PartialEq, Eq, Default, Debug)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\n#[repr(u8)]\npub enum StatusSource {\n    #[default]\n    /// All-ones if TX FIFO level < N, otherwise all-zeroes.\n    TxFifoLevel = 0,\n    /// All-ones if RX FIFO level < N, otherwise all-zeroes.\n    RxFifoLevel = 1,\n    /// All-ones if the indexed IRQ flag is raised, otherwise all-zeroes\n    #[cfg(feature = \"_rp235x\")]\n    Irq = 2,\n}\n\nconst RXNEMPTY_MASK: u32 = 1 << 0;\nconst TXNFULL_MASK: u32 = 1 << 4;\nconst SMIRQ_MASK: u32 = 1 << 8;\n\n/// Interrupt handler for PIO.\npub struct InterruptHandler<PIO: Instance> {\n    _pio: PhantomData<PIO>,\n}\n\nimpl<PIO: Instance> Handler<PIO::Interrupt> for InterruptHandler<PIO> {\n    unsafe fn on_interrupt() {\n        let ints = PIO::PIO.irqs(0).ints().read().0;\n        for bit in 0..12 {\n            if ints & (1 << bit) != 0 {\n                PIO::wakers().0[bit].wake();\n            }\n        }\n        PIO::PIO.irqs(0).inte().write_clear(|m| m.0 = ints);\n    }\n}\n\n/// Future that waits for TX-FIFO to become writable\n#[must_use = \"futures do nothing unless you `.await` or poll them\"]\npub struct FifoOutFuture<'a, 'd, PIO: Instance, const SM: usize> {\n    sm_tx: &'a mut StateMachineTx<'d, PIO, SM>,\n    value: u32,\n}\n\nimpl<'a, 'd, PIO: Instance, const SM: usize> FifoOutFuture<'a, 'd, PIO, SM> {\n    /// Create a new future waiting for TX-FIFO to become writable.\n    pub fn new(sm: &'a mut StateMachineTx<'d, PIO, SM>, value: u32) -> Self {\n        FifoOutFuture { sm_tx: sm, value }\n    }\n}\n\nimpl<'a, 'd, PIO: Instance, const SM: usize> Future for FifoOutFuture<'a, 'd, PIO, SM> {\n    type Output = ();\n    fn poll(self: FuturePin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {\n        //debug!(\"Poll {},{}\", PIO::PIO_NO, SM);\n        let value = self.value;\n        if self.get_mut().sm_tx.try_push(value) {\n            Poll::Ready(())\n        } else {\n            PIO::wakers().fifo_out()[SM].register(cx.waker());\n            PIO::PIO.irqs(0).inte().write_set(|m| {\n                m.0 = TXNFULL_MASK << SM;\n            });\n            // debug!(\"Pending\");\n            Poll::Pending\n        }\n    }\n}\n\nimpl<'a, 'd, PIO: Instance, const SM: usize> Drop for FifoOutFuture<'a, 'd, PIO, SM> {\n    fn drop(&mut self) {\n        PIO::PIO.irqs(0).inte().write_clear(|m| {\n            m.0 = TXNFULL_MASK << SM;\n        });\n    }\n}\n\n/// Future that waits for RX-FIFO to become readable.\n#[must_use = \"futures do nothing unless you `.await` or poll them\"]\npub struct FifoInFuture<'a, 'd, PIO: Instance, const SM: usize> {\n    sm_rx: &'a mut StateMachineRx<'d, PIO, SM>,\n}\n\nimpl<'a, 'd, PIO: Instance, const SM: usize> FifoInFuture<'a, 'd, PIO, SM> {\n    /// Create future that waits for RX-FIFO to become readable.\n    pub fn new(sm: &'a mut StateMachineRx<'d, PIO, SM>) -> Self {\n        FifoInFuture { sm_rx: sm }\n    }\n}\n\nimpl<'a, 'd, PIO: Instance, const SM: usize> Future for FifoInFuture<'a, 'd, PIO, SM> {\n    type Output = u32;\n    fn poll(mut self: FuturePin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {\n        //debug!(\"Poll {},{}\", PIO::PIO_NO, SM);\n        if let Some(v) = self.sm_rx.try_pull() {\n            Poll::Ready(v)\n        } else {\n            PIO::wakers().fifo_in()[SM].register(cx.waker());\n            PIO::PIO.irqs(0).inte().write_set(|m| {\n                m.0 = RXNEMPTY_MASK << SM;\n            });\n            //debug!(\"Pending\");\n            Poll::Pending\n        }\n    }\n}\n\nimpl<'a, 'd, PIO: Instance, const SM: usize> Drop for FifoInFuture<'a, 'd, PIO, SM> {\n    fn drop(&mut self) {\n        PIO::PIO.irqs(0).inte().write_clear(|m| {\n            m.0 = RXNEMPTY_MASK << SM;\n        });\n    }\n}\n\n/// Future that waits for IRQ\n#[must_use = \"futures do nothing unless you `.await` or poll them\"]\npub struct IrqFuture<'a, 'd, PIO: Instance> {\n    pio: PhantomData<&'a mut Irq<'d, PIO, 0>>,\n    irq_no: u8,\n}\n\nimpl<'a, 'd, PIO: Instance> Future for IrqFuture<'a, 'd, PIO> {\n    type Output = ();\n    fn poll(self: FuturePin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {\n        //debug!(\"Poll {},{}\", PIO::PIO_NO, SM);\n\n        // Check if IRQ flag is already set\n        if PIO::PIO.irq().read().0 & (1 << self.irq_no) != 0 {\n            PIO::PIO.irq().write(|m| m.0 = 1 << self.irq_no);\n            return Poll::Ready(());\n        }\n\n        PIO::wakers().irq()[self.irq_no as usize].register(cx.waker());\n        PIO::PIO.irqs(0).inte().write_set(|m| {\n            m.0 = SMIRQ_MASK << self.irq_no;\n        });\n        Poll::Pending\n    }\n}\n\nimpl<'a, 'd, PIO: Instance> Drop for IrqFuture<'a, 'd, PIO> {\n    fn drop(&mut self) {\n        PIO::PIO.irqs(0).inte().write_clear(|m| {\n            m.0 = SMIRQ_MASK << self.irq_no;\n        });\n    }\n}\n\n/// Type representing a PIO pin.\npub struct Pin<'l, PIO: Instance> {\n    pin: Peri<'l, AnyPin>,\n    pio: PhantomData<PIO>,\n}\n\nimpl<'l, PIO: Instance> Pin<'l, PIO> {\n    /// Set the pin's drive strength.\n    #[inline]\n    pub fn set_drive_strength(&mut self, strength: Drive) {\n        self.pin.pad_ctrl().modify(|w| {\n            w.set_drive(match strength {\n                Drive::_2mA => pac::pads::vals::Drive::_2M_A,\n                Drive::_4mA => pac::pads::vals::Drive::_4M_A,\n                Drive::_8mA => pac::pads::vals::Drive::_8M_A,\n                Drive::_12mA => pac::pads::vals::Drive::_12M_A,\n            });\n        });\n    }\n\n    /// Set the pin's slew rate.\n    #[inline]\n    pub fn set_slew_rate(&mut self, slew_rate: SlewRate) {\n        self.pin.pad_ctrl().modify(|w| {\n            w.set_slewfast(slew_rate == SlewRate::Fast);\n        });\n    }\n\n    /// Set the pin's pull.\n    #[inline]\n    pub fn set_pull(&mut self, pull: Pull) {\n        self.pin.pad_ctrl().modify(|w| {\n            w.set_pue(pull == Pull::Up);\n            w.set_pde(pull == Pull::Down);\n        });\n    }\n\n    /// Set the pin's schmitt trigger.\n    #[inline]\n    pub fn set_schmitt(&mut self, enable: bool) {\n        self.pin.pad_ctrl().modify(|w| {\n            w.set_schmitt(enable);\n        });\n    }\n\n    /// Configure the output logic inversion of this pin.\n    #[inline]\n    pub fn set_output_inversion(&mut self, invert: bool) {\n        self.pin.gpio().ctrl().modify(|w| {\n            w.set_outover(if invert {\n                pac::io::vals::Outover::INVERT\n            } else {\n                pac::io::vals::Outover::NORMAL\n            })\n        });\n    }\n\n    /// Configure the output enable inversion of this pin\n    #[inline]\n    pub fn set_output_enable_inversion(&mut self, invert: bool) {\n        self.pin.gpio().ctrl().modify(|w| {\n            w.set_oeover(if invert {\n                pac::io::vals::Oeover::INVERT\n            } else {\n                pac::io::vals::Oeover::NORMAL\n            })\n        })\n    }\n\n    /// Set the pin's input sync bypass.\n    pub fn set_input_sync_bypass(&mut self, bypass: bool) {\n        let mask = 1 << self.pin();\n        if bypass {\n            PIO::PIO.input_sync_bypass().write_set(|w| *w = mask);\n        } else {\n            PIO::PIO.input_sync_bypass().write_clear(|w| *w = mask);\n        }\n    }\n\n    /// Get the underlying pin number.\n    pub fn pin(&self) -> u8 {\n        self.pin._pin()\n    }\n}\n\n/// Type representing a state machine RX FIFO.\npub struct StateMachineRx<'d, PIO: Instance, const SM: usize> {\n    pio: PhantomData<&'d mut PIO>,\n}\n\nimpl<'d, PIO: Instance, const SM: usize> StateMachineRx<'d, PIO, SM> {\n    /// Check if RX FIFO is empty.\n    pub fn empty(&self) -> bool {\n        PIO::PIO.fstat().read().rxempty() & (1u8 << SM) != 0\n    }\n\n    /// Check if RX FIFO is full.\n    pub fn full(&self) -> bool {\n        PIO::PIO.fstat().read().rxfull() & (1u8 << SM) != 0\n    }\n\n    /// Check RX FIFO level.\n    pub fn level(&self) -> u8 {\n        (PIO::PIO.flevel().read().0 >> (SM * 8 + 4)) as u8 & 0x0f\n    }\n\n    /// Check if state machine has stalled on full RX FIFO.\n    pub fn stalled(&self) -> bool {\n        let fdebug = PIO::PIO.fdebug();\n        let ret = fdebug.read().rxstall() & (1 << SM) != 0;\n        if ret {\n            fdebug.write(|w| w.set_rxstall(1 << SM));\n        }\n        ret\n    }\n\n    /// Check if RX FIFO underflow (i.e. read-on-empty by the system) has occurred.\n    pub fn underflowed(&self) -> bool {\n        let fdebug = PIO::PIO.fdebug();\n        let ret = fdebug.read().rxunder() & (1 << SM) != 0;\n        if ret {\n            fdebug.write(|w| w.set_rxunder(1 << SM));\n        }\n        ret\n    }\n\n    /// Pull data from RX FIFO.\n    ///\n    /// This function doesn't check if there is data available to be read.\n    /// If the rx FIFO is empty, an undefined value is returned. If you only\n    /// want to pull if data is available, use `try_pull` instead.\n    pub fn pull(&mut self) -> u32 {\n        PIO::PIO.rxf(SM).read()\n    }\n\n    /// Attempt pulling data from RX FIFO.\n    pub fn try_pull(&mut self) -> Option<u32> {\n        if self.empty() {\n            return None;\n        }\n        Some(self.pull())\n    }\n\n    /// Wait for RX FIFO readable.\n    pub fn wait_pull<'a>(&'a mut self) -> FifoInFuture<'a, 'd, PIO, SM> {\n        FifoInFuture::new(self)\n    }\n\n    fn dreq() -> crate::pac::dma::vals::TreqSel {\n        crate::pac::dma::vals::TreqSel::from(PIO::PIO_NO * 8 + SM as u8 + 4)\n    }\n\n    /// Prepare DMA transfer from RX FIFO.\n    pub fn dma_pull<'a, W: Word>(\n        &'a mut self,\n        ch: &'a mut dma::Channel<'_>,\n        data: &'a mut [W],\n        bswap: bool,\n    ) -> Transfer<'a> {\n        unsafe { ch.read(PIO::PIO.rxf(SM).as_ptr() as *const W, data, Self::dreq(), bswap) }\n    }\n\n    /// Prepare a repeated DMA transfer from RX FIFO.\n    pub fn dma_pull_discard<'a, W: Word>(&'a mut self, ch: &'a mut dma::Channel<'_>, len: usize) -> Transfer<'a> {\n        unsafe { ch.read_discard(PIO::PIO.rxf(SM).as_ptr(), len, Self::dreq()) }\n    }\n}\n\n/// Type representing a state machine TX FIFO.\npub struct StateMachineTx<'d, PIO: Instance, const SM: usize> {\n    pio: PhantomData<&'d mut PIO>,\n}\n\nimpl<'d, PIO: Instance, const SM: usize> StateMachineTx<'d, PIO, SM> {\n    /// Check if TX FIFO is empty.\n    pub fn empty(&self) -> bool {\n        PIO::PIO.fstat().read().txempty() & (1u8 << SM) != 0\n    }\n\n    /// Check if TX FIFO is full.\n    pub fn full(&self) -> bool {\n        PIO::PIO.fstat().read().txfull() & (1u8 << SM) != 0\n    }\n\n    /// Check TX FIFO level.\n    pub fn level(&self) -> u8 {\n        (PIO::PIO.flevel().read().0 >> (SM * 8)) as u8 & 0x0f\n    }\n\n    /// Check if state machine has stalled on empty TX FIFO.\n    pub fn stalled(&self) -> bool {\n        let fdebug = PIO::PIO.fdebug();\n        let ret = fdebug.read().txstall() & (1 << SM) != 0;\n        if ret {\n            fdebug.write(|w| w.set_txstall(1 << SM));\n        }\n        ret\n    }\n\n    /// Check if FIFO overflowed.\n    pub fn overflowed(&self) -> bool {\n        let fdebug = PIO::PIO.fdebug();\n        let ret = fdebug.read().txover() & (1 << SM) != 0;\n        if ret {\n            fdebug.write(|w| w.set_txover(1 << SM));\n        }\n        ret\n    }\n\n    /// Force push data to TX FIFO.\n    pub fn push(&mut self, v: u32) {\n        PIO::PIO.txf(SM).write_value(v);\n    }\n\n    /// Attempt to push data to TX FIFO.\n    pub fn try_push(&mut self, v: u32) -> bool {\n        if self.full() {\n            return false;\n        }\n        self.push(v);\n        true\n    }\n\n    /// Wait until FIFO is ready for writing.\n    pub fn wait_push<'a>(&'a mut self, value: u32) -> FifoOutFuture<'a, 'd, PIO, SM> {\n        FifoOutFuture::new(self, value)\n    }\n\n    fn dreq() -> crate::pac::dma::vals::TreqSel {\n        crate::pac::dma::vals::TreqSel::from(PIO::PIO_NO * 8 + SM as u8)\n    }\n\n    /// Prepare a DMA transfer to TX FIFO.\n    pub fn dma_push<'a, W: Word>(\n        &'a mut self,\n        ch: &'a mut dma::Channel<'_>,\n        data: &'a [W],\n        bswap: bool,\n    ) -> Transfer<'a> {\n        unsafe { ch.write(data, PIO::PIO.txf(SM).as_ptr() as *mut W, Self::dreq(), bswap) }\n    }\n\n    /// Prepare a repeated DMA transfer to TX FIFO.\n    pub fn dma_push_zeros<'a, W: Word>(&'a mut self, ch: &'a mut dma::Channel<'_>, len: usize) -> Transfer<'a> {\n        unsafe { ch.write_zeros(len, PIO::PIO.txf(SM).as_ptr() as *mut W, Self::dreq()) }\n    }\n}\n\n/// A type representing a single PIO state machine.\npub struct StateMachine<'d, PIO: Instance, const SM: usize> {\n    rx: StateMachineRx<'d, PIO, SM>,\n    tx: StateMachineTx<'d, PIO, SM>,\n}\n\nimpl<'d, PIO: Instance, const SM: usize> Drop for StateMachine<'d, PIO, SM> {\n    fn drop(&mut self) {\n        PIO::PIO.ctrl().write_clear(|w| w.set_sm_enable(1 << SM));\n        on_pio_drop::<PIO>();\n    }\n}\n\nfn assert_consecutive<PIO: Instance>(pins: &[&Pin<PIO>]) {\n    for (p1, p2) in pins.iter().zip(pins.iter().skip(1)) {\n        // purposely does not allow wrap-around because we can't claim pins 30 and 31.\n        assert!(p1.pin() + 1 == p2.pin(), \"pins must be consecutive\");\n    }\n}\n\n/// PIO Execution config.\n#[derive(Clone, Copy, Default, Debug)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\n#[non_exhaustive]\npub struct ExecConfig {\n    /// If true, the MSB of the Delay/Side-set instruction field is used as side-set enable, rather than a side-set data bit.\n    pub side_en: bool,\n    /// If true, side-set data is asserted to pin directions, instead of pin values.\n    pub side_pindir: bool,\n    /// Pin to trigger jump.\n    pub jmp_pin: u8,\n    /// After reaching this address, execution is wrapped to wrap_bottom.\n    pub wrap_top: u8,\n    /// After reaching wrap_top, execution is wrapped to this address.\n    pub wrap_bottom: u8,\n}\n\n/// PIO shift register config for input or output.\n#[derive(Clone, Copy, Default, Debug)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub struct ShiftConfig {\n    /// Number of bits shifted out of OSR before autopull.\n    pub threshold: u8,\n    /// Shift direction.\n    pub direction: ShiftDirection,\n    /// For output: Pull automatically output shift register is emptied.\n    /// For input: Push automatically when the input shift register is filled.\n    pub auto_fill: bool,\n}\n\n/// PIO pin config.\n#[derive(Clone, Copy, Default, Debug)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub struct PinConfig {\n    /// The number of MSBs of the Delay/Side-set instruction field which are used for side-set.\n    pub sideset_count: u8,\n    /// The number of pins asserted by a SET. In the range 0 to 5 inclusive.\n    pub set_count: u8,\n    /// The number of pins asserted by an OUT PINS, OUT PINDIRS or MOV PINS instruction. In the range 0 to 32 inclusive.\n    pub out_count: u8,\n    /// The pin which is mapped to the least-significant bit of a state machine's IN data bus.\n    pub in_base: u8,\n    /// The lowest-numbered pin that will be affected by a side-set operation.\n    pub sideset_base: u8,\n    /// The lowest-numbered pin that will be affected by a SET PINS or SET PINDIRS instruction.\n    pub set_base: u8,\n    /// The lowest-numbered pin that will be affected by an OUT PINS, OUT PINDIRS or MOV PINS instruction.\n    pub out_base: u8,\n}\n\n/// Comparison level or IRQ index for the MOV x, STATUS instruction.\n#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\n#[cfg(feature = \"_rp235x\")]\npub enum StatusN {\n    /// IRQ flag in this PIO block\n    This(u8),\n    /// IRQ flag in the next lower PIO block\n    Lower(u8),\n    /// IRQ flag in the next higher PIO block\n    Higher(u8),\n}\n\n#[cfg(feature = \"_rp235x\")]\nimpl Default for StatusN {\n    fn default() -> Self {\n        Self::This(0)\n    }\n}\n\n#[cfg(feature = \"_rp235x\")]\nimpl Into<crate::pac::pio::vals::ExecctrlStatusN> for StatusN {\n    fn into(self) -> crate::pac::pio::vals::ExecctrlStatusN {\n        let x = match self {\n            StatusN::This(n) => n,\n            StatusN::Lower(n) => n + 0x08,\n            StatusN::Higher(n) => n + 0x10,\n        };\n\n        crate::pac::pio::vals::ExecctrlStatusN(x)\n    }\n}\n\n/// PIO config.\n#[derive(Clone, Copy, Debug)]\npub struct Config<'d, PIO: Instance> {\n    /// Clock divisor register for state machines.\n    pub clock_divider: FixedU32<U8>,\n    /// Which data bit to use for inline OUT enable.\n    pub out_en_sel: u8,\n    /// Use a bit of OUT data as an auxiliary write enable When used in conjunction with OUT_STICKY.\n    pub inline_out_en: bool,\n    /// Continuously assert the most recent OUT/SET to the pins.\n    pub out_sticky: bool,\n    /// Which source to use for checking status.\n    pub status_sel: StatusSource,\n    /// Status comparison level.\n    #[cfg(feature = \"rp2040\")]\n    pub status_n: u8,\n    // This cfg probably shouldn't be required, but the SVD for the 2040 doesn't have the enum\n    #[cfg(feature = \"_rp235x\")]\n    /// Status comparison level.\n    pub status_n: StatusN,\n    exec: ExecConfig,\n    origin: Option<u8>,\n    /// Configure FIFO allocation.\n    pub fifo_join: FifoJoin,\n    /// Input shifting config.\n    pub shift_in: ShiftConfig,\n    /// Output shifting config.\n    pub shift_out: ShiftConfig,\n    // PINCTRL\n    pins: PinConfig,\n    in_count: u8,\n    _pio: PhantomData<&'d mut PIO>,\n}\n\nimpl<'d, PIO: Instance> Default for Config<'d, PIO> {\n    fn default() -> Self {\n        Self {\n            clock_divider: 1u8.into(),\n            out_en_sel: Default::default(),\n            inline_out_en: Default::default(),\n            out_sticky: Default::default(),\n            status_sel: Default::default(),\n            status_n: Default::default(),\n            exec: Default::default(),\n            origin: Default::default(),\n            fifo_join: Default::default(),\n            shift_in: Default::default(),\n            shift_out: Default::default(),\n            pins: Default::default(),\n            in_count: Default::default(),\n            _pio: Default::default(),\n        }\n    }\n}\n\nimpl<'d, PIO: Instance> Config<'d, PIO> {\n    /// Get execution configuration.\n    pub fn get_exec(&self) -> ExecConfig {\n        self.exec\n    }\n\n    /// Update execution configuration.\n    pub unsafe fn set_exec(&mut self, e: ExecConfig) {\n        self.exec = e;\n    }\n\n    /// Get pin configuration.\n    pub fn get_pins(&self) -> PinConfig {\n        self.pins\n    }\n\n    /// Update pin configuration.\n    pub unsafe fn set_pins(&mut self, p: PinConfig) {\n        self.pins = p;\n    }\n\n    /// Configures this state machine to use the given program, including jumping to the origin\n    /// of the program. The state machine is not started.\n    ///\n    /// `side_set` sets the range of pins affected by side-sets. The range must be consecutive.\n    /// Sideset pins must configured as outputs using [`StateMachine::set_pin_dirs`] to be\n    /// effective.\n    pub fn use_program(&mut self, prog: &LoadedProgram<'d, PIO>, side_set: &[&Pin<'d, PIO>]) {\n        assert!((prog.side_set.bits() - prog.side_set.optional() as u8) as usize == side_set.len());\n        assert_consecutive(side_set);\n        self.exec.side_en = prog.side_set.optional();\n        self.exec.side_pindir = prog.side_set.pindirs();\n        self.exec.wrap_bottom = prog.wrap.target;\n        self.exec.wrap_top = prog.wrap.source;\n        self.pins.sideset_count = prog.side_set.bits();\n        self.pins.sideset_base = side_set.first().map_or(0, |p| p.pin());\n        self.origin = Some(prog.origin);\n    }\n\n    /// Set pin used to signal jump.\n    pub fn set_jmp_pin(&mut self, pin: &Pin<'d, PIO>) {\n        self.exec.jmp_pin = pin.pin();\n    }\n\n    /// Sets the range of pins affected by SET instructions. The range must be consecutive.\n    /// Set pins must configured as outputs using [`StateMachine::set_pin_dirs`] to be\n    /// effective.\n    pub fn set_set_pins(&mut self, pins: &[&Pin<'d, PIO>]) {\n        assert!(pins.len() <= 5);\n        assert_consecutive(pins);\n        self.pins.set_base = pins.first().map_or(0, |p| p.pin());\n        self.pins.set_count = pins.len() as u8;\n    }\n\n    /// Sets the range of pins affected by OUT instructions. The range must be consecutive.\n    /// Out pins must configured as outputs using [`StateMachine::set_pin_dirs`] to be\n    /// effective.\n    pub fn set_out_pins(&mut self, pins: &[&Pin<'d, PIO>]) {\n        assert_consecutive(pins);\n        self.pins.out_base = pins.first().map_or(0, |p| p.pin());\n        self.pins.out_count = pins.len() as u8;\n    }\n\n    /// Sets the range of pins used by IN instructions. The range must be consecutive.\n    /// In pins must configured as inputs using [`StateMachine::set_pin_dirs`] to be\n    /// effective.\n    pub fn set_in_pins(&mut self, pins: &[&Pin<'d, PIO>]) {\n        assert_consecutive(pins);\n        self.pins.in_base = pins.first().map_or(0, |p| p.pin());\n        self.in_count = pins.len() as u8;\n    }\n}\n\nimpl<'d, PIO: Instance + 'd, const SM: usize> StateMachine<'d, PIO, SM> {\n    /// Set the config for a given PIO state machine.\n    pub fn set_config(&mut self, config: &Config<'d, PIO>) {\n        // sm expects 0 for 65536, truncation makes that happen\n        assert!(config.clock_divider <= 65536, \"clkdiv must be <= 65536\");\n        assert!(config.clock_divider >= 1, \"clkdiv must be >= 1\");\n        assert!(config.out_en_sel < 32, \"out_en_sel must be < 32\");\n        //assert!(config.status_n < 32, \"status_n must be < 32\");\n        // sm expects 0 for 32, truncation makes that happen\n        assert!(config.shift_in.threshold <= 32, \"shift_in.threshold must be <= 32\");\n        assert!(config.shift_out.threshold <= 32, \"shift_out.threshold must be <= 32\");\n        let sm = Self::this_sm();\n        sm.clkdiv().write(|w| w.0 = config.clock_divider.to_bits() << 8);\n        sm.execctrl().write(|w| {\n            w.set_side_en(config.exec.side_en);\n            w.set_side_pindir(config.exec.side_pindir);\n            w.set_jmp_pin(config.exec.jmp_pin);\n            w.set_out_en_sel(config.out_en_sel);\n            w.set_inline_out_en(config.inline_out_en);\n            w.set_out_sticky(config.out_sticky);\n            w.set_wrap_top(config.exec.wrap_top);\n            w.set_wrap_bottom(config.exec.wrap_bottom);\n            #[cfg(feature = \"_rp235x\")]\n            w.set_status_sel(match config.status_sel {\n                StatusSource::TxFifoLevel => pac::pio::vals::ExecctrlStatusSel::TXLEVEL,\n                StatusSource::RxFifoLevel => pac::pio::vals::ExecctrlStatusSel::RXLEVEL,\n                StatusSource::Irq => pac::pio::vals::ExecctrlStatusSel::IRQ,\n            });\n            #[cfg(feature = \"rp2040\")]\n            w.set_status_sel(match config.status_sel {\n                StatusSource::TxFifoLevel => pac::pio::vals::SmExecctrlStatusSel::TXLEVEL,\n                StatusSource::RxFifoLevel => pac::pio::vals::SmExecctrlStatusSel::RXLEVEL,\n            });\n            w.set_status_n(config.status_n.into());\n        });\n        sm.shiftctrl().write(|w| {\n            w.set_fjoin_rx(config.fifo_join == FifoJoin::RxOnly);\n            w.set_fjoin_tx(config.fifo_join == FifoJoin::TxOnly);\n            w.set_pull_thresh(config.shift_out.threshold);\n            w.set_push_thresh(config.shift_in.threshold);\n            w.set_out_shiftdir(config.shift_out.direction == ShiftDirection::Right);\n            w.set_in_shiftdir(config.shift_in.direction == ShiftDirection::Right);\n            w.set_autopull(config.shift_out.auto_fill);\n            w.set_autopush(config.shift_in.auto_fill);\n\n            #[cfg(feature = \"_rp235x\")]\n            {\n                w.set_fjoin_rx_get(\n                    config.fifo_join == FifoJoin::RxAsControl || config.fifo_join == FifoJoin::PioScratch,\n                );\n                w.set_fjoin_rx_put(\n                    config.fifo_join == FifoJoin::RxAsStatus || config.fifo_join == FifoJoin::PioScratch,\n                );\n                w.set_in_count(config.in_count);\n            }\n        });\n\n        #[cfg(feature = \"rp2040\")]\n        sm.pinctrl().write(|w| {\n            w.set_sideset_count(config.pins.sideset_count);\n            w.set_set_count(config.pins.set_count);\n            w.set_out_count(config.pins.out_count);\n            w.set_in_base(config.pins.in_base);\n            w.set_sideset_base(config.pins.sideset_base);\n            w.set_set_base(config.pins.set_base);\n            w.set_out_base(config.pins.out_base);\n        });\n\n        #[cfg(feature = \"_rp235x\")]\n        {\n            let mut low_ok = true;\n            let mut high_ok = true;\n\n            let in_pins = config.pins.in_base..config.pins.in_base + config.in_count;\n            let side_pins = config.pins.sideset_base..config.pins.sideset_base + config.pins.sideset_count;\n            let set_pins = config.pins.set_base..config.pins.set_base + config.pins.set_count;\n            let out_pins = config.pins.out_base..config.pins.out_base + config.pins.out_count;\n\n            for pin_range in [in_pins, side_pins, set_pins, out_pins] {\n                for pin in pin_range {\n                    low_ok &= pin < 32;\n                    high_ok &= pin >= 16;\n                }\n            }\n\n            if !low_ok && !high_ok {\n                panic!(\n                    \"All pins must either be <32 or >=16, in:{:?}-{:?}, side:{:?}-{:?}, set:{:?}-{:?}, out:{:?}-{:?}\",\n                    config.pins.in_base,\n                    config.pins.in_base + config.in_count - 1,\n                    config.pins.sideset_base,\n                    config.pins.sideset_base + config.pins.sideset_count - 1,\n                    config.pins.set_base,\n                    config.pins.set_base + config.pins.set_count - 1,\n                    config.pins.out_base,\n                    config.pins.out_base + config.pins.out_count - 1,\n                )\n            }\n            let shift = if low_ok { 0 } else { 16 };\n\n            sm.pinctrl().write(|w| {\n                w.set_sideset_count(config.pins.sideset_count);\n                w.set_set_count(config.pins.set_count);\n                w.set_out_count(config.pins.out_count);\n                w.set_in_base(config.pins.in_base.checked_sub(shift).unwrap_or_default());\n                w.set_sideset_base(config.pins.sideset_base.checked_sub(shift).unwrap_or_default());\n                w.set_set_base(config.pins.set_base.checked_sub(shift).unwrap_or_default());\n                w.set_out_base(config.pins.out_base.checked_sub(shift).unwrap_or_default());\n            });\n\n            PIO::PIO.gpiobase().write(|w| w.set_gpiobase(shift == 16));\n        }\n\n        if let Some(origin) = config.origin {\n            unsafe { self.exec_jmp(origin) }\n        }\n    }\n\n    /// Get pointer to rx fifo\n    pub fn rx_fifo_ptr(&self) -> *mut u32 {\n        PIO::PIO.rxf(SM).as_ptr()\n    }\n\n    /// Get pointer to tx fifo\n    pub fn tx_fifo_ptr(&self) -> *mut u32 {\n        PIO::PIO.txf(SM).as_ptr()\n    }\n\n    /// Get dma Treq of rx fifo\n    pub fn rx_treq(&self) -> crate::pac::dma::vals::TreqSel {\n        StateMachineRx::<PIO, SM>::dreq()\n    }\n\n    /// Get dma Treq of tx fifo\n    pub fn tx_treq(&self) -> crate::pac::dma::vals::TreqSel {\n        StateMachineTx::<PIO, SM>::dreq()\n    }\n\n    /// Read current instruction address for this state machine\n    pub fn get_addr(&self) -> u8 {\n        let addr = Self::this_sm().addr();\n        addr.read().addr()\n    }\n\n    /// Read TX FIFO threshold for this state machine.\n    pub fn get_tx_threshold(&self) -> u8 {\n        let shiftctrl = Self::this_sm().shiftctrl();\n        shiftctrl.read().pull_thresh()\n    }\n\n    /// Set/change the TX FIFO threshold for this state machine.\n    pub fn set_tx_threshold(&mut self, threshold: u8) {\n        assert!(threshold <= 31);\n        let shiftctrl = Self::this_sm().shiftctrl();\n        shiftctrl.modify(|w| {\n            w.set_pull_thresh(threshold);\n        });\n    }\n\n    /// Read TX FIFO threshold for this state machine.\n    pub fn get_rx_threshold(&self) -> u8 {\n        Self::this_sm().shiftctrl().read().push_thresh()\n    }\n\n    /// Set/change the RX FIFO threshold for this state machine.\n    pub fn set_rx_threshold(&mut self, threshold: u8) {\n        assert!(threshold <= 31);\n        let shiftctrl = Self::this_sm().shiftctrl();\n        shiftctrl.modify(|w| {\n            w.set_push_thresh(threshold);\n        });\n    }\n\n    /// Set/change both TX and RX FIFO thresholds for this state machine.\n    pub fn set_thresholds(&mut self, threshold: u8) {\n        assert!(threshold <= 31);\n        let shiftctrl = Self::this_sm().shiftctrl();\n        shiftctrl.modify(|w| {\n            w.set_push_thresh(threshold);\n            w.set_pull_thresh(threshold);\n        });\n    }\n\n    /// Set the clock divider for this state machine.\n    pub fn set_clock_divider(&mut self, clock_divider: FixedU32<U8>) {\n        let sm = Self::this_sm();\n        sm.clkdiv().write(|w| w.0 = clock_divider.to_bits() << 8);\n    }\n\n    #[inline(always)]\n    fn this_sm() -> crate::pac::pio::StateMachine {\n        PIO::PIO.sm(SM)\n    }\n\n    /// Restart this state machine.\n    pub fn restart(&mut self) {\n        let mask = 1u8 << SM;\n        PIO::PIO.ctrl().write_set(|w| w.set_sm_restart(mask));\n    }\n\n    /// Enable state machine.\n    pub fn set_enable(&mut self, enable: bool) {\n        let mask = 1u8 << SM;\n        if enable {\n            PIO::PIO.ctrl().write_set(|w| w.set_sm_enable(mask));\n        } else {\n            PIO::PIO.ctrl().write_clear(|w| w.set_sm_enable(mask));\n        }\n    }\n\n    /// Check if state machine is enabled.\n    pub fn is_enabled(&self) -> bool {\n        PIO::PIO.ctrl().read().sm_enable() & (1u8 << SM) != 0\n    }\n\n    /// Restart a state machine's clock divider from an initial phase of 0.\n    pub fn clkdiv_restart(&mut self) {\n        let mask = 1u8 << SM;\n        PIO::PIO.ctrl().write_set(|w| w.set_clkdiv_restart(mask));\n    }\n\n    fn with_paused(&mut self, f: impl FnOnce(&mut Self)) {\n        let enabled = self.is_enabled();\n        self.set_enable(false);\n        let pincfg = Self::this_sm().pinctrl().read();\n        let execcfg = Self::this_sm().execctrl().read();\n        Self::this_sm().execctrl().write_clear(|w| w.set_out_sticky(true));\n        f(self);\n        Self::this_sm().pinctrl().write_value(pincfg);\n        Self::this_sm().execctrl().write_value(execcfg);\n        self.set_enable(enabled);\n    }\n\n    #[cfg(feature = \"rp2040\")]\n    fn pin_base() -> u8 {\n        0\n    }\n\n    #[cfg(feature = \"_rp235x\")]\n    fn pin_base() -> u8 {\n        if PIO::PIO.gpiobase().read().gpiobase() { 16 } else { 0 }\n    }\n\n    /// Sets pin directions. This pauses the current state machine to run `SET` commands\n    /// and temporarily unsets the `OUT_STICKY` bit.\n    pub fn set_pin_dirs(&mut self, dir: Direction, pins: &[&Pin<'d, PIO>]) {\n        self.with_paused(|sm| {\n            for pin in pins {\n                Self::this_sm().pinctrl().write(|w| {\n                    w.set_set_base(pin.pin() - Self::pin_base());\n                    w.set_set_count(1);\n                });\n                // SET PINDIRS, (dir)\n                unsafe { sm.exec_instr(0b111_00000_100_00000 | dir as u16) };\n            }\n        });\n    }\n\n    /// Sets pin output values. This pauses the current state machine to run\n    /// `SET` commands and temporarily unsets the `OUT_STICKY` bit.\n    pub fn set_pins(&mut self, level: Level, pins: &[&Pin<'d, PIO>]) {\n        self.with_paused(|sm| {\n            for pin in pins {\n                Self::this_sm().pinctrl().write(|w| {\n                    w.set_set_base(pin.pin() - Self::pin_base());\n                    w.set_set_count(1);\n                });\n                // SET PINS, (dir)\n                unsafe { sm.exec_instr(0b11100_000_000_00000 | level as u16) };\n            }\n        });\n    }\n\n    /// Flush FIFOs for state machine.\n    pub fn clear_fifos(&mut self) {\n        // Toggle FJOIN_RX to flush FIFOs\n        let shiftctrl = Self::this_sm().shiftctrl();\n        shiftctrl.modify(|w| {\n            w.set_fjoin_rx(!w.fjoin_rx());\n        });\n        shiftctrl.modify(|w| {\n            w.set_fjoin_rx(!w.fjoin_rx());\n        });\n    }\n\n    /// Instruct state machine to execute a given instructions\n    ///\n    /// SAFETY: The state machine must be in a state where executing\n    /// an arbitrary instruction does not crash it.\n    pub unsafe fn exec_instr(&mut self, instr: u16) {\n        Self::this_sm().instr().write(|w| w.set_instr(instr));\n    }\n\n    /// Return a read handle for reading state machine outputs.\n    pub fn rx(&mut self) -> &mut StateMachineRx<'d, PIO, SM> {\n        &mut self.rx\n    }\n\n    /// Return a handle for writing to inputs.\n    pub fn tx(&mut self) -> &mut StateMachineTx<'d, PIO, SM> {\n        &mut self.tx\n    }\n\n    /// Return both read and write handles for the state machine.\n    pub fn rx_tx(&mut self) -> (&mut StateMachineRx<'d, PIO, SM>, &mut StateMachineTx<'d, PIO, SM>) {\n        (&mut self.rx, &mut self.tx)\n    }\n\n    /// Return the contents of the nth entry of the RX FIFO\n    /// (should be used only when the FIFO config is set to [`FifoJoin::RxAsStatus`])\n    #[cfg(feature = \"_rp235x\")]\n    pub fn get_rxf_entry(&self, n: usize) -> u32 {\n        PIO::PIO.rxf_putget(SM).putget(n).read()\n    }\n\n    /// Set the contents of the nth entry of the RX FIFO\n    /// (should be used only when the FIFO config is set to [`FifoJoin::RxAsControl`])\n    #[cfg(feature = \"_rp235x\")]\n    pub fn set_rxf_entry(&self, n: usize, val: u32) {\n        PIO::PIO.rxf_putget(SM).putget(n).write_value(val)\n    }\n}\n\n/// PIO handle.\npub struct Common<'d, PIO: Instance> {\n    instructions_used: u32,\n    pio: PhantomData<&'d mut PIO>,\n}\n\nimpl<'d, PIO: Instance> Drop for Common<'d, PIO> {\n    fn drop(&mut self) {\n        on_pio_drop::<PIO>();\n    }\n}\n\n/// Memory of PIO instance.\npub struct InstanceMemory<'d, PIO: Instance> {\n    used_mask: u32,\n    pio: PhantomData<&'d mut PIO>,\n}\n\n/// A loaded PIO program.\npub struct LoadedProgram<'d, PIO: Instance> {\n    /// Memory used by program.\n    pub used_memory: InstanceMemory<'d, PIO>,\n    /// Program origin for loading.\n    pub origin: u8,\n    /// Wrap controls what to do once program is done executing.\n    pub wrap: Wrap,\n    /// Data for 'side' set instruction parameters.\n    pub side_set: SideSet,\n}\n\n/// Errors loading a PIO program.\n#[derive(Clone, Copy, PartialEq, Eq, Debug)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub enum LoadError {\n    /// Insufficient consecutive free instruction space to load program.\n    InsufficientSpace,\n    /// Loading the program would overwrite an instruction address already\n    /// used by another program.\n    AddressInUse(usize),\n}\n\nimpl<'d, PIO: Instance> Common<'d, PIO> {\n    /// Load a PIO program. This will automatically relocate the program to\n    /// an available chunk of free instruction memory if the program origin\n    /// was not explicitly specified, otherwise it will attempt to load the\n    /// program only at its origin.\n    pub fn load_program<const SIZE: usize>(&mut self, prog: &Program<SIZE>) -> LoadedProgram<'d, PIO> {\n        match self.try_load_program(prog) {\n            Ok(r) => r,\n            Err(e) => panic!(\"Failed to load PIO program: {:?}\", e),\n        }\n    }\n\n    /// Load a PIO program. This will automatically relocate the program to\n    /// an available chunk of free instruction memory if the program origin\n    /// was not explicitly specified, otherwise it will attempt to load the\n    /// program only at its origin.\n    pub fn try_load_program<const SIZE: usize>(\n        &mut self,\n        prog: &Program<SIZE>,\n    ) -> Result<LoadedProgram<'d, PIO>, LoadError> {\n        match prog.origin {\n            Some(origin) => self.try_load_program_at(prog, origin).map_err(LoadError::AddressInUse),\n            None => {\n                // naively search for free space, allowing wraparound since\n                // PIO does support that. with only 32 instruction slots it\n                // doesn't make much sense to do anything more fancy.\n                let mut origin = 0;\n                while origin < 32 {\n                    match self.try_load_program_at(prog, origin as _) {\n                        Ok(r) => return Ok(r),\n                        Err(a) => origin = a + 1,\n                    }\n                }\n                Err(LoadError::InsufficientSpace)\n            }\n        }\n    }\n\n    fn try_load_program_at<const SIZE: usize>(\n        &mut self,\n        prog: &Program<SIZE>,\n        origin: u8,\n    ) -> Result<LoadedProgram<'d, PIO>, usize> {\n        #[cfg(not(feature = \"_rp235x\"))]\n        assert!(prog.version == pio::PioVersion::V0);\n\n        let prog = RelocatedProgram::new_with_origin(prog, origin);\n        let used_memory = self.try_write_instr(prog.origin() as _, prog.code())?;\n        Ok(LoadedProgram {\n            used_memory,\n            origin: prog.origin(),\n            wrap: prog.wrap(),\n            side_set: prog.side_set(),\n        })\n    }\n\n    fn try_write_instr<I>(&mut self, start: usize, instrs: I) -> Result<InstanceMemory<'d, PIO>, usize>\n    where\n        I: Iterator<Item = u16>,\n    {\n        let mut used_mask = 0;\n        for (i, instr) in instrs.enumerate() {\n            // wrapping around the end of program memory is valid, let's make use of that.\n            let addr = (i + start) % 32;\n            let mask = 1 << addr;\n            if (self.instructions_used | used_mask) & mask != 0 {\n                return Err(addr);\n            }\n            PIO::PIO.instr_mem(addr).write(|w| {\n                w.set_instr_mem(instr);\n            });\n            used_mask |= mask;\n        }\n        self.instructions_used |= used_mask;\n        Ok(InstanceMemory {\n            used_mask,\n            pio: PhantomData,\n        })\n    }\n\n    /// Free instruction memory. This is always possible but unsafe if any\n    /// state machine is still using this bit of memory.\n    pub unsafe fn free_instr(&mut self, instrs: InstanceMemory<PIO>) {\n        self.instructions_used &= !instrs.used_mask;\n    }\n\n    /// Bypass flipflop synchronizer on GPIO inputs.\n    pub fn set_input_sync_bypass<'a>(&'a mut self, bypass: u32, mask: u32) {\n        // this can interfere with per-pin bypass functions. splitting the\n        // modification is going to be fine since nothing that relies on\n        // it can reasonably run before we finish.\n        PIO::PIO.input_sync_bypass().write_set(|w| *w = mask & bypass);\n        PIO::PIO.input_sync_bypass().write_clear(|w| *w = mask & !bypass);\n    }\n\n    /// Get bypass configuration.\n    pub fn get_input_sync_bypass(&self) -> u32 {\n        PIO::PIO.input_sync_bypass().read()\n    }\n\n    /// Register a pin for PIO usage. Pins will be released from the PIO block\n    /// (i.e., have their `FUNCSEL` reset to `NULL`) when the [`Common`] *and*\n    /// all [`StateMachine`]s for this block have been dropped. **Other members\n    /// of [`Pio`] do not keep pin registrations alive.**\n    pub fn make_pio_pin(&mut self, pin: Peri<'d, impl PioPin + 'd>) -> Pin<'d, PIO> {\n        // enable the outputs\n        pin.pad_ctrl().write(|w| w.set_od(false));\n        // especially important on the 235x, where IE defaults to 0\n        pin.pad_ctrl().write(|w| w.set_ie(true));\n\n        pin.gpio().ctrl().write(|w| w.set_funcsel(PIO::FUNCSEL as _));\n        pin.pad_ctrl().write(|w| {\n            #[cfg(feature = \"_rp235x\")]\n            w.set_iso(false);\n            w.set_schmitt(true);\n            w.set_slewfast(false);\n            // TODO rp235x errata E9 recommends to not enable IE if we're not\n            // going to use input. Maybe add an API for the user to enable/disable this?\n            w.set_ie(true);\n            w.set_od(false);\n            w.set_pue(false);\n            w.set_pde(false);\n        });\n        // we can be relaxed about this because we're &mut here and nothing is cached\n        critical_section::with(|_| {\n            let val = PIO::state().used_pins.load(Ordering::Relaxed);\n            PIO::state()\n                .used_pins\n                .store(val | 1 << pin.pin_bank(), Ordering::Relaxed);\n        });\n\n        Pin {\n            pin: pin.into(),\n            pio: PhantomData::default(),\n        }\n    }\n}\n\n/// Represents multiple state machines in a single type.\npub struct PioBatch<'a, PIO: Instance> {\n    clkdiv_restart: u8,\n    sm_restart: u8,\n    sm_enable_mask: u8,\n    sm_enable: u8,\n    _pio: PhantomData<&'a PIO>,\n}\n\nimpl<'a, PIO: Instance> PioBatch<'a, PIO> {\n    /// Create nop PioBatch object\n    pub fn new() -> Self {\n        Self {\n            clkdiv_restart: 0,\n            sm_restart: 0,\n            sm_enable_mask: 0,\n            sm_enable: 0,\n            _pio: PhantomData,\n        }\n    }\n\n    /// Restart a state machine's clock divider from an initial phase of 0.\n    pub fn restart<const SM: usize>(&mut self, _sm: &mut StateMachine<'a, PIO, SM>) {\n        self.clkdiv_restart |= 1 << SM;\n    }\n\n    /// Enable a specific state machine.\n    pub fn set_enable<const SM: usize>(&mut self, _sm: &mut StateMachine<'a, PIO, SM>, enable: bool) {\n        self.sm_enable_mask |= 1 << SM;\n        self.sm_enable |= (enable as u8) << SM;\n    }\n\n    /// Apply changes to state machines in a batch.\n    pub fn execute(&mut self) {\n        PIO::PIO.ctrl().modify(|w| {\n            w.set_clkdiv_restart(self.clkdiv_restart);\n            w.set_sm_restart(self.sm_restart);\n            w.set_sm_enable((w.sm_enable() & !self.sm_enable_mask) | self.sm_enable);\n        });\n    }\n}\n\n/// Type representing a PIO interrupt.\npub struct Irq<'d, PIO: Instance, const N: usize> {\n    pio: PhantomData<&'d mut PIO>,\n}\n\nimpl<'d, PIO: Instance, const N: usize> Irq<'d, PIO, N> {\n    /// Wait for an IRQ to fire.\n    pub fn wait<'a>(&'a mut self) -> IrqFuture<'a, 'd, PIO> {\n        IrqFuture {\n            pio: PhantomData,\n            irq_no: N as u8,\n        }\n    }\n}\n\n/// Interrupt flags for a PIO instance.\n#[derive(Clone)]\npub struct IrqFlags<'d, PIO: Instance> {\n    pio: PhantomData<&'d mut PIO>,\n}\n\nimpl<'d, PIO: Instance> IrqFlags<'d, PIO> {\n    /// Check if interrupt fired.\n    pub fn check(&self, irq_no: u8) -> bool {\n        assert!(irq_no < 8);\n        self.check_any(1 << irq_no)\n    }\n\n    /// Check if any of the interrupts in the bitmap fired.\n    pub fn check_any(&self, irqs: u8) -> bool {\n        PIO::PIO.irq().read().irq() & irqs != 0\n    }\n\n    /// Check if all interrupts have fired.\n    pub fn check_all(&self, irqs: u8) -> bool {\n        PIO::PIO.irq().read().irq() & irqs == irqs\n    }\n\n    /// Clear interrupt for interrupt number.\n    pub fn clear(&self, irq_no: usize) {\n        assert!(irq_no < 8);\n        self.clear_all(1 << irq_no);\n    }\n\n    /// Clear all interrupts set in the bitmap.\n    pub fn clear_all(&self, irqs: u8) {\n        PIO::PIO.irq().write(|w| w.set_irq(irqs))\n    }\n\n    /// Fire a given interrupt.\n    pub fn set(&self, irq_no: usize) {\n        assert!(irq_no < 8);\n        self.set_all(1 << irq_no);\n    }\n\n    /// Fire all interrupts.\n    pub fn set_all(&self, irqs: u8) {\n        PIO::PIO.irq_force().write(|w| w.set_irq_force(irqs))\n    }\n}\n\n/// An instance of the PIO driver.\npub struct Pio<'d, PIO: Instance> {\n    /// PIO handle.\n    pub common: Common<'d, PIO>,\n    /// PIO IRQ flags.\n    pub irq_flags: IrqFlags<'d, PIO>,\n    /// IRQ0 configuration.\n    pub irq0: Irq<'d, PIO, 0>,\n    /// IRQ1 configuration.\n    pub irq1: Irq<'d, PIO, 1>,\n    /// IRQ2 configuration.\n    pub irq2: Irq<'d, PIO, 2>,\n    /// IRQ3 configuration.\n    pub irq3: Irq<'d, PIO, 3>,\n    /// State machine 0 handle.\n    pub sm0: StateMachine<'d, PIO, 0>,\n    /// State machine 1 handle.\n    pub sm1: StateMachine<'d, PIO, 1>,\n    /// State machine 2 handle.\n    pub sm2: StateMachine<'d, PIO, 2>,\n    /// State machine 3 handle.\n    pub sm3: StateMachine<'d, PIO, 3>,\n    _pio: PhantomData<&'d mut PIO>,\n}\n\nimpl<'d, PIO: Instance> Pio<'d, PIO> {\n    /// Create a new instance of a PIO peripheral.\n    pub fn new(_pio: Peri<'d, PIO>, _irq: impl Binding<PIO::Interrupt, InterruptHandler<PIO>>) -> Self {\n        PIO::state().users.store(5, Ordering::Release);\n        PIO::state().used_pins.store(0, Ordering::Release);\n        PIO::Interrupt::unpend();\n\n        unsafe { PIO::Interrupt::enable() };\n        Self {\n            common: Common {\n                instructions_used: 0,\n                pio: PhantomData,\n            },\n            irq_flags: IrqFlags { pio: PhantomData },\n            irq0: Irq { pio: PhantomData },\n            irq1: Irq { pio: PhantomData },\n            irq2: Irq { pio: PhantomData },\n            irq3: Irq { pio: PhantomData },\n            sm0: StateMachine {\n                rx: StateMachineRx { pio: PhantomData },\n                tx: StateMachineTx { pio: PhantomData },\n            },\n            sm1: StateMachine {\n                rx: StateMachineRx { pio: PhantomData },\n                tx: StateMachineTx { pio: PhantomData },\n            },\n            sm2: StateMachine {\n                rx: StateMachineRx { pio: PhantomData },\n                tx: StateMachineTx { pio: PhantomData },\n            },\n            sm3: StateMachine {\n                rx: StateMachineRx { pio: PhantomData },\n                tx: StateMachineTx { pio: PhantomData },\n            },\n            _pio: PhantomData,\n        }\n    }\n}\n\nstruct AtomicU64 {\n    upper_32: AtomicU32,\n    lower_32: AtomicU32,\n}\n\nimpl AtomicU64 {\n    const fn new(val: u64) -> Self {\n        let upper_32 = (val >> 32) as u32;\n        let lower_32 = val as u32;\n\n        Self {\n            upper_32: AtomicU32::new(upper_32),\n            lower_32: AtomicU32::new(lower_32),\n        }\n    }\n\n    fn load(&self, order: Ordering) -> u64 {\n        let (upper, lower) = critical_section::with(|_| (self.upper_32.load(order), self.lower_32.load(order)));\n\n        let upper = (upper as u64) << 32;\n        let lower = lower as u64;\n\n        upper | lower\n    }\n\n    fn store(&self, val: u64, order: Ordering) {\n        let upper_32 = (val >> 32) as u32;\n        let lower_32 = val as u32;\n\n        critical_section::with(|_| {\n            self.upper_32.store(upper_32, order);\n            self.lower_32.store(lower_32, order);\n        });\n    }\n}\n\n/// Representation of the PIO state keeping a record of which pins are assigned to\n/// each PIO.\n// make_pio_pin notionally takes ownership of the pin it is given, but the wrapped pin\n// cannot be treated as an owned resource since dropping it would have to deconfigure\n// the pin, breaking running state machines in the process. pins are also shared\n// between all state machines, which makes ownership even messier to track any\n// other way.\npub struct State {\n    users: AtomicU8,\n    used_pins: AtomicU64,\n}\n\nfn on_pio_drop<PIO: Instance>() {\n    let state = PIO::state();\n    let users_state = critical_section::with(|_| {\n        let val = state.users.load(Ordering::Acquire);\n        state.users.store(val - 1, Ordering::Release);\n        val\n    });\n    if users_state == 1 {\n        let used_pins = state.used_pins.load(Ordering::Relaxed);\n        let null = pac::io::vals::Gpio0ctrlFuncsel::NULL as _;\n        for i in 0..crate::gpio::BANK0_PIN_COUNT {\n            if used_pins & (1 << i) != 0 {\n                pac::IO_BANK0.gpio(i).ctrl().write(|w| w.set_funcsel(null));\n            }\n        }\n    }\n}\n\ntrait SealedInstance {\n    const PIO_NO: u8;\n    const PIO: &'static crate::pac::pio::Pio;\n    const FUNCSEL: crate::pac::io::vals::Gpio0ctrlFuncsel;\n\n    #[inline]\n    fn wakers() -> &'static Wakers {\n        static WAKERS: Wakers = Wakers([const { AtomicWaker::new() }; 12]);\n        &WAKERS\n    }\n\n    #[inline]\n    fn state() -> &'static State {\n        static STATE: State = State {\n            users: AtomicU8::new(0),\n            used_pins: AtomicU64::new(0),\n        };\n\n        &STATE\n    }\n}\n\n/// PIO instance.\n#[allow(private_bounds)]\npub trait Instance: SealedInstance + PeripheralType + Sized + Unpin {\n    /// Interrupt for this peripheral.\n    type Interrupt: crate::interrupt::typelevel::Interrupt;\n}\n\nmacro_rules! impl_pio {\n    ($name:ident, $pio:expr, $pac:ident, $funcsel:ident, $irq:ident) => {\n        impl SealedInstance for peripherals::$name {\n            const PIO_NO: u8 = $pio;\n            const PIO: &'static pac::pio::Pio = &pac::$pac;\n            const FUNCSEL: pac::io::vals::Gpio0ctrlFuncsel = pac::io::vals::Gpio0ctrlFuncsel::$funcsel;\n        }\n        impl Instance for peripherals::$name {\n            type Interrupt = crate::interrupt::typelevel::$irq;\n        }\n    };\n}\n\nimpl_pio!(PIO0, 0, PIO0, PIO0_0, PIO0_IRQ_0);\nimpl_pio!(PIO1, 1, PIO1, PIO1_0, PIO1_IRQ_0);\n#[cfg(feature = \"_rp235x\")]\nimpl_pio!(PIO2, 2, PIO2, PIO2_0, PIO2_IRQ_0);\n\n/// PIO pin.\npub trait PioPin: gpio::Pin {}\n\nmacro_rules! impl_pio_pin {\n    ($( $pin:ident, )*) => {\n        $(\n            impl PioPin for peripherals::$pin {}\n        )*\n    };\n}\n\nimpl_pio_pin! {\n    PIN_0,\n    PIN_1,\n    PIN_2,\n    PIN_3,\n    PIN_4,\n    PIN_5,\n    PIN_6,\n    PIN_7,\n    PIN_8,\n    PIN_9,\n    PIN_10,\n    PIN_11,\n    PIN_12,\n    PIN_13,\n    PIN_14,\n    PIN_15,\n    PIN_16,\n    PIN_17,\n    PIN_18,\n    PIN_19,\n    PIN_20,\n    PIN_21,\n    PIN_22,\n    PIN_23,\n    PIN_24,\n    PIN_25,\n    PIN_26,\n    PIN_27,\n    PIN_28,\n    PIN_29,\n}\n\n#[cfg(feature = \"rp235xb\")]\nimpl_pio_pin! {\n    PIN_30,\n    PIN_31,\n    PIN_32,\n    PIN_33,\n    PIN_34,\n    PIN_35,\n    PIN_36,\n    PIN_37,\n    PIN_38,\n    PIN_39,\n    PIN_40,\n    PIN_41,\n    PIN_42,\n    PIN_43,\n    PIN_44,\n    PIN_45,\n    PIN_46,\n    PIN_47,\n}\n"
  },
  {
    "path": "embassy-rp/src/pio_programs/clk.rs",
    "content": "//! Clock output signal generator using PIO\n\nuse crate::Peri;\nuse crate::pio::program::pio_asm;\nuse crate::pio::{Common, Config, Direction, Instance, LoadedProgram, Pin, PioBatch, PioPin, StateMachine};\nuse crate::pio_programs::clock_divider::calculate_pio_clock_divider;\n\n/// Clock generator PIO program\npub struct PioClkProgram<'a, PIO: Instance> {\n    prg: LoadedProgram<'a, PIO>,\n}\n\nconst PIO_CLK_PROGRAM_CLK_MULTIPLIER: u32 = 2;\n\nimpl<'a, PIO: Instance> PioClkProgram<'a, PIO> {\n    /// Load the program into PIO instruction memory\n    pub fn new(common: &mut Common<'a, PIO>) -> Self {\n        let prg = pio_asm!(\"set pins 0\", \"set pins 1\");\n        let prg = common.load_program(&prg.program);\n        Self { prg }\n    }\n}\n\n/// Pio backed clock generator\npub struct PioClk<'d, T: Instance, const SM: usize> {\n    sm: StateMachine<'d, T, SM>,\n    pin: Pin<'d, T>,\n}\n\nimpl<'d, T: Instance, const SM: usize> PioClk<'d, T, SM> {\n    /// Configure state machine for clock generation\n    pub fn new(\n        pio: &mut Common<'d, T>,\n        mut sm: StateMachine<'d, T, SM>,\n        pin: Peri<'d, impl PioPin>,\n        program: &PioClkProgram<'d, T>,\n        frequency: u32,\n    ) -> Self {\n        let pin = pio.make_pio_pin(pin);\n        sm.set_pin_dirs(Direction::Out, &[&pin]);\n\n        let mut cfg = Config::default();\n        let sm_frequency = frequency * PIO_CLK_PROGRAM_CLK_MULTIPLIER;\n        cfg.clock_divider = calculate_pio_clock_divider(sm_frequency);\n        cfg.set_set_pins(&[&pin]);\n        cfg.use_program(&program.prg, &[]);\n\n        sm.set_config(&cfg);\n\n        Self { sm, pin }\n    }\n\n    /// Start at the the same as other drivers\n    pub fn start_batched(&mut self, b: &mut PioBatch<'d, T>) {\n        b.set_enable(&mut self.sm, true);\n    }\n\n    /// Stop at the the same as other drivers\n    pub fn stop_batched(&mut self, b: &mut PioBatch<'d, T>) {\n        b.set_enable(&mut self.sm, false);\n    }\n\n    /// Start emmiting clock\n    pub fn start(&mut self) {\n        self.sm.set_enable(true);\n    }\n\n    /// Stop emmiting clock\n    pub fn stop(&mut self) {\n        self.sm.set_enable(false);\n    }\n\n    /// Return the state machine and pin\n    pub fn release(mut self) -> (StateMachine<'d, T, SM>, Pin<'d, T>) {\n        self.stop();\n        (self.sm, self.pin)\n    }\n}\n"
  },
  {
    "path": "embassy-rp/src/pio_programs/clock_divider.rs",
    "content": "//! Helper functions for calculating PIO clock dividers\n\nuse fixed::types::extra::U8;\n\nuse crate::clocks::clk_sys_freq;\n\n/// Calculate a PIO clock divider value based on the desired target frequency.\n///\n/// # Arguments\n///\n/// * `target_hz` - The desired PIO clock frequency in Hz\n///\n/// # Returns\n///\n/// A fixed-point divider value suitable for use in a PIO state machine configuration\npub fn calculate_pio_clock_divider(target_hz: u32) -> fixed::FixedU32<U8> {\n    calculate_pio_clock_divider_value(clk_sys_freq(), target_hz)\n}\n\n/// Calculate a PIO clock divider value based on the desired target frequency.\n///\n/// # Arguments\n///\n/// * `sys_hz` - The system clock frequency in Hz\n/// * `target_hz` - The desired PIO clock frequency in Hz\n///\n/// # Returns\n///\n/// A fixed-point divider value suitable for use in a PIO state machine configuration\npub const fn calculate_pio_clock_divider_value(sys_hz: u32, target_hz: u32) -> fixed::FixedU32<U8> {\n    // Requires a non-zero frequency\n    core::assert!(target_hz > 0);\n\n    // Compute the integer and fractional part of the divider.\n    // Doing it this way allows us to avoid u64 division while\n    // maintaining precision.\n    let integer = sys_hz / target_hz;\n    let remainder = sys_hz % target_hz;\n    let frac = (remainder << 8) / target_hz;\n\n    let result = integer << 8 | frac;\n\n    // Ensure the result will fit in 16+8 bits.\n    core::assert!(result <= 0xffff_ff);\n    // The clock divider can't be used to go faster than the system clock.\n    core::assert!(result >= 0x0001_00);\n\n    fixed::FixedU32::from_bits(result)\n}\n\n#[cfg(test)]\nmod tests {\n    use fixed::traits::ToFixed;\n\n    use super::*;\n\n    #[test]\n    fn clock_divider_math() {\n        // A simple divider that must have a fractional part.\n        let divider = calculate_pio_clock_divider_value(125_000_000, 40_000_000);\n        let expected: fixed::FixedU32<U8> = 3.125.to_fixed();\n        assert_eq!(divider, expected);\n\n        // A system clk so high it would overflow a u32 if shifted left.\n        let divider = calculate_pio_clock_divider_value(2_000_000_000, 40_000);\n        let expected: fixed::FixedU32<U8> = 50000.to_fixed();\n        assert_eq!(divider, expected);\n\n        // A divider that requires all 8 fractional bits.\n        let divider = calculate_pio_clock_divider_value(134_283_264, 16_777_216);\n        let expected: fixed::FixedU32<U8> = 8.00390625.to_fixed();\n        assert_eq!(divider, expected);\n    }\n}\n"
  },
  {
    "path": "embassy-rp/src/pio_programs/hd44780.rs",
    "content": "//! [HD44780 display driver](https://www.sparkfun.com/datasheets/LCD/HD44780.pdf)\n\nuse crate::pio::{\n    Common, Config, Direction, FifoJoin, Instance, Irq, LoadedProgram, PioPin, ShiftConfig, ShiftDirection,\n    StateMachine,\n};\nuse crate::pio_programs::clock_divider::calculate_pio_clock_divider;\nuse crate::{Peri, dma, interrupt};\n\n/// This struct represents a HD44780 program that takes command words (<wait:24> <command:4> <0:4>)\npub struct PioHD44780CommandWordProgram<'a, PIO: Instance> {\n    prg: LoadedProgram<'a, PIO>,\n}\n\nimpl<'a, PIO: Instance> PioHD44780CommandWordProgram<'a, PIO> {\n    /// Load the program into the given pio\n    pub fn new(common: &mut Common<'a, PIO>) -> Self {\n        let prg = pio::pio_asm!(\n            r#\"\n                .side_set 1 opt\n                .origin 20\n\n                loop:\n                    out x,     24\n                delay:\n                    jmp x--,   delay\n                    out pins,  4     side 1\n                    out null,  4     side 0\n                    jmp !osre, loop\n                irq 0\n            \"#,\n        );\n\n        let prg = common.load_program(&prg.program);\n\n        Self { prg }\n    }\n}\n\n/// This struct represents a HD44780 program that takes command sequences (<rs:1> <count:7>, data...)\npub struct PioHD44780CommandSequenceProgram<'a, PIO: Instance> {\n    prg: LoadedProgram<'a, PIO>,\n}\n\nimpl<'a, PIO: Instance> PioHD44780CommandSequenceProgram<'a, PIO> {\n    /// Load the program into the given pio\n    pub fn new(common: &mut Common<'a, PIO>) -> Self {\n        // many side sets are only there to free up a delay bit!\n        let prg = pio::pio_asm!(\n            r#\"\n                .origin 27\n                .side_set 1\n\n                .wrap_target\n                pull     side 0\n                out  x 1 side 0 ; !rs\n                out  y 7 side 0 ; #data - 1\n\n                ; rs/rw to e: >= 60ns\n                ; e high time: >= 500ns\n                ; e low time: >= 500ns\n                ; read data valid after e falling: ~5ns\n                ; write data hold after e falling: ~10ns\n\n                loop:\n                    pull                 side 0\n                    jmp  !x       data   side 0\n                command:\n                    set  pins     0b00   side 0\n                    jmp  shift           side 0\n                data:\n                    set  pins     0b01   side 0\n                shift:\n                    out  pins     4      side 1 [9]\n                    nop                  side 0 [9]\n                    out  pins     4      side 1 [9]\n                    mov  osr      null   side 0 [7]\n                    out  pindirs  4      side 0\n                    set  pins     0b10   side 0\n                busy:\n                    nop                  side 1 [9]\n                    jmp  pin      more   side 0 [9]\n                    mov  osr      ~osr   side 1 [9]\n                    nop                  side 0 [4]\n                    out  pindirs  4      side 0\n                    jmp  y--      loop   side 0\n                .wrap\n                more:\n                    nop                  side 1 [9]\n                    jmp busy             side 0 [9]\n            \"#\n        );\n\n        let prg = common.load_program(&prg.program);\n\n        Self { prg }\n    }\n}\n\n/// Pio backed HD44780 driver\npub struct PioHD44780<'l, P: Instance, const S: usize> {\n    dma: dma::Channel<'l>,\n    sm: StateMachine<'l, P, S>,\n\n    buf: [u8; 40],\n}\n\nimpl<'l, P: Instance, const S: usize> PioHD44780<'l, P, S> {\n    /// Configure the given state machine to first init, then write data to, a HD44780 display.\n    pub async fn new<D: dma::ChannelInstance>(\n        common: &mut Common<'l, P>,\n        mut sm: StateMachine<'l, P, S>,\n        mut irq: Irq<'l, P, S>,\n        dma: Peri<'l, D>,\n        dma_irq: impl interrupt::typelevel::Binding<D::Interrupt, dma::InterruptHandler<D>> + 'l,\n        rs: Peri<'l, impl PioPin>,\n        rw: Peri<'l, impl PioPin>,\n        e: Peri<'l, impl PioPin>,\n        db4: Peri<'l, impl PioPin>,\n        db5: Peri<'l, impl PioPin>,\n        db6: Peri<'l, impl PioPin>,\n        db7: Peri<'l, impl PioPin>,\n        word_prg: &PioHD44780CommandWordProgram<'l, P>,\n        seq_prg: &PioHD44780CommandSequenceProgram<'l, P>,\n    ) -> PioHD44780<'l, P, S> {\n        let mut dma_ch = dma::Channel::new(dma, dma_irq);\n        let rs = common.make_pio_pin(rs);\n        let rw = common.make_pio_pin(rw);\n        let e = common.make_pio_pin(e);\n        let db4 = common.make_pio_pin(db4);\n        let db5 = common.make_pio_pin(db5);\n        let db6 = common.make_pio_pin(db6);\n        let db7 = common.make_pio_pin(db7);\n\n        sm.set_pin_dirs(Direction::Out, &[&rs, &rw, &e, &db4, &db5, &db6, &db7]);\n\n        let mut cfg = Config::default();\n        cfg.use_program(&word_prg.prg, &[&e]);\n\n        // Target 1 MHz PIO clock (each cycle is 1µs)\n        cfg.clock_divider = calculate_pio_clock_divider(1_000_000);\n\n        cfg.set_out_pins(&[&db4, &db5, &db6, &db7]);\n        cfg.shift_out = ShiftConfig {\n            auto_fill: true,\n            direction: ShiftDirection::Left,\n            threshold: 32,\n        };\n        cfg.fifo_join = FifoJoin::TxOnly;\n        sm.set_config(&cfg);\n\n        sm.set_enable(true);\n        // init to 8 bit thrice\n        sm.tx().push((50000 << 8) | 0x30);\n        sm.tx().push((5000 << 8) | 0x30);\n        sm.tx().push((200 << 8) | 0x30);\n        // init 4 bit\n        sm.tx().push((200 << 8) | 0x20);\n        // set font and lines\n        sm.tx().push((50 << 8) | 0x20);\n        sm.tx().push(0b1100_0000);\n\n        irq.wait().await;\n        sm.set_enable(false);\n\n        let mut cfg = Config::default();\n        cfg.use_program(&seq_prg.prg, &[&e]);\n\n        // Target ~15.6 MHz PIO clock (~64ns/insn)\n        cfg.clock_divider = calculate_pio_clock_divider(15_600_000);\n\n        cfg.set_jmp_pin(&db7);\n        cfg.set_set_pins(&[&rs, &rw]);\n        cfg.set_out_pins(&[&db4, &db5, &db6, &db7]);\n        cfg.shift_out.direction = ShiftDirection::Left;\n        cfg.fifo_join = FifoJoin::TxOnly;\n        sm.set_config(&cfg);\n\n        sm.set_enable(true);\n\n        // display on and cursor on and blinking, reset display\n        sm.tx().dma_push(&mut dma_ch, &[0x81u8, 0x0f, 1], false).await;\n\n        Self {\n            dma: dma_ch,\n            sm,\n            buf: [0x20; 40],\n        }\n    }\n\n    /// Write a line to the display\n    pub async fn add_line(&mut self, s: &[u8]) {\n        // move cursor to 0:0, prepare 16 characters\n        self.buf[..3].copy_from_slice(&[0x80, 0x80, 15]);\n        // move line 2 up\n        self.buf.copy_within(22..38, 3);\n        // move cursor to 1:0, prepare 16 characters\n        self.buf[19..22].copy_from_slice(&[0x80, 0xc0, 15]);\n        // file line 2 with spaces\n        self.buf[22..38].fill(0x20);\n        // copy input line\n        let len = s.len().min(16);\n        self.buf[22..22 + len].copy_from_slice(&s[0..len]);\n        // set cursor to 1:15\n        self.buf[38..].copy_from_slice(&[0x80, 0xcf]);\n\n        self.sm.tx().dma_push(&mut self.dma, &self.buf, false).await;\n    }\n}\n"
  },
  {
    "path": "embassy-rp/src/pio_programs/i2s.rs",
    "content": "//! Pio backed I2S output and output drivers\n\nuse crate::dma::Transfer;\nuse crate::gpio::Pull;\nuse crate::pio::{\n    Common, Config, Direction, FifoJoin, Instance, LoadedProgram, PioBatch, PioPin, ShiftConfig, ShiftDirection,\n    StateMachine,\n};\nuse crate::pio_programs::clock_divider::calculate_pio_clock_divider;\nuse crate::{Peri, dma, interrupt};\n\n/// This struct represents an I2S receiver & controller driver program\npub struct PioI2sInProgram<'d, PIO: Instance> {\n    prg: LoadedProgram<'d, PIO>,\n}\n\nconst PIO_I2S_IN_PROGRAM_CLK_MULTIPLIER: u32 = 2;\n\nimpl<'d, PIO: Instance> PioI2sInProgram<'d, PIO> {\n    /// Load the input program into the given pio\n    pub fn new(common: &mut Common<'d, PIO>) -> Self {\n        let prg = pio::pio_asm! {\n            \".side_set 2\",\n            \"   set x, 14               side 0b01\",\n            \"left_data:\",\n            \"   in pins, 1              side 0b00\", // read one left-channel bit from SD\n            \"   jmp x-- left_data       side 0b01\",\n            \"   in pins, 1              side 0b10\", // ws changes 1 clock before MSB\n            \"   set x, 14               side 0b11\",\n            \"right_data:\",\n            \"   in pins, 1             side 0b10\",\n            \"   jmp x-- right_data     side 0b11\",\n            \"   in pins, 1             side 0b00\" // ws changes 1 clock before ms\n        };\n        let prg = common.load_program(&prg.program);\n        Self { prg }\n    }\n}\n\n/// Pio backed I2S input driver\npub struct PioI2sIn<'d, P: Instance, const S: usize> {\n    dma: dma::Channel<'d>,\n    sm: StateMachine<'d, P, S>,\n}\n\nimpl<'d, P: Instance, const S: usize> PioI2sIn<'d, P, S> {\n    /// Configure a state machine to act as both the controller (provider of SCK and WS) and receiver (of SD) for an I2S signal\n    pub fn new<D: dma::ChannelInstance>(\n        common: &mut Common<'d, P>,\n        mut sm: StateMachine<'d, P, S>,\n        dma: Peri<'d, D>,\n        irq: impl interrupt::typelevel::Binding<D::Interrupt, dma::InterruptHandler<D>> + 'd,\n        // Whether or not to use the MCU's internal pull-down resistor, as the\n        // Pico 2 is known to have problems with the inbuilt pulldowns, many\n        // opt to just use an external pull down resistor to meet requirements of common\n        // I2S microphones such as the INMP441\n        data_pulldown: bool,\n        data_pin: Peri<'d, impl PioPin>,\n        bit_clock_pin: Peri<'d, impl PioPin>,\n        lr_clock_pin: Peri<'d, impl PioPin>,\n        sample_rate: u32,\n        bit_depth: u32,\n        channels: u32,\n        program: &PioI2sInProgram<'d, P>,\n    ) -> Self {\n        let mut data_pin = common.make_pio_pin(data_pin);\n        if data_pulldown {\n            data_pin.set_pull(Pull::Down);\n        }\n        let bit_clock_pin = common.make_pio_pin(bit_clock_pin);\n        let lr_clock_pin = common.make_pio_pin(lr_clock_pin);\n\n        let cfg = {\n            let mut cfg = Config::default();\n            cfg.use_program(&program.prg, &[&bit_clock_pin, &lr_clock_pin]);\n            cfg.set_in_pins(&[&data_pin]);\n            let clock_frequency = sample_rate * bit_depth * channels;\n            cfg.clock_divider = calculate_pio_clock_divider(clock_frequency * PIO_I2S_IN_PROGRAM_CLK_MULTIPLIER);\n            cfg.shift_in = ShiftConfig {\n                threshold: 32,\n                direction: ShiftDirection::Left,\n                auto_fill: true,\n            };\n            // join fifos to have twice the time to start the next dma transfer\n            cfg.fifo_join = FifoJoin::RxOnly; // both control signals are sent via side-setting\n            cfg\n        };\n        sm.set_config(&cfg);\n        sm.set_pin_dirs(Direction::In, &[&data_pin]);\n        sm.set_pin_dirs(Direction::Out, &[&lr_clock_pin, &bit_clock_pin]);\n\n        Self {\n            dma: dma::Channel::new(dma, irq),\n            sm,\n        }\n    }\n\n    /// Start the i2s interface\n    pub fn start(&mut self) {\n        self.sm.set_enable(true);\n    }\n\n    /// Stop the i2s interface\n    pub fn stop(&mut self) {\n        self.sm.set_enable(true);\n    }\n\n    /// Start at the the same as other drivers\n    pub fn start_batched(&mut self, b: &mut PioBatch<'d, P>) {\n        b.set_enable(&mut self.sm, true);\n    }\n\n    /// Stop at the the same as other drivers\n    pub fn stop_batched(&mut self, b: &mut PioBatch<'d, P>) {\n        b.set_enable(&mut self.sm, false);\n    }\n\n    /// Return an in-progress dma transfer future. Awaiting it will guarantee a complete transfer.\n    pub fn read<'b>(&'b mut self, buff: &'b mut [u32]) -> Transfer<'b> {\n        self.sm.rx().dma_pull(&mut self.dma, buff, false)\n    }\n}\n\n/// This struct represents an I2S output driver program\n///\n/// The sample bit-depth is set through scratch register `Y`.\n/// `Y` has to be set to sample bit-depth - 2.\n/// (14 = 16bit, 22 = 24bit, 30 = 32bit)\npub struct PioI2sOutProgram<'d, PIO: Instance> {\n    prg: LoadedProgram<'d, PIO>,\n}\n\nconst PIO_I2S_OUT_PROGRAM_CLK_MULTIPLIER: u32 = 2;\n\nimpl<'d, PIO: Instance> PioI2sOutProgram<'d, PIO> {\n    /// Load the program into the given pio\n    pub fn new(common: &mut Common<'d, PIO>) -> Self {\n        let prg = pio::pio_asm!(\n            \".side_set 2\",                      // side 0bWB - W = Word Clock, B = Bit Clock\n            \"    mov x, y           side 0b01\", // y stores sample depth - 2 (14 = 16bit, 22 = 24bit, 30 = 32bit)\n            \"left_data:\",\n            \"    out pins, 1        side 0b00\",\n            \"    jmp x-- left_data  side 0b01\",\n            \"    out pins, 1        side 0b10\",\n            \"    mov x, y           side 0b11\",\n            \"right_data:\",\n            \"    out pins, 1         side 0b10\",\n            \"    jmp x-- right_data side 0b11\",\n            \"    out pins, 1         side 0b00\",\n        );\n\n        let prg = common.load_program(&prg.program);\n\n        Self { prg }\n    }\n}\n\n/// Pio backed I2S output driver\npub struct PioI2sOut<'d, P: Instance, const S: usize> {\n    dma: dma::Channel<'d>,\n    sm: StateMachine<'d, P, S>,\n}\n\nimpl<'d, P: Instance, const S: usize> PioI2sOut<'d, P, S> {\n    /// Configure a state machine to output I2S\n    pub fn new<D: dma::ChannelInstance>(\n        common: &mut Common<'d, P>,\n        mut sm: StateMachine<'d, P, S>,\n        dma: Peri<'d, D>,\n        irq: impl interrupt::typelevel::Binding<D::Interrupt, dma::InterruptHandler<D>> + 'd,\n        data_pin: Peri<'d, impl PioPin>,\n        bit_clock_pin: Peri<'d, impl PioPin>,\n        lr_clock_pin: Peri<'d, impl PioPin>,\n        sample_rate: u32,\n        bit_depth: u32,\n        program: &PioI2sOutProgram<'d, P>,\n    ) -> Self {\n        let data_pin = common.make_pio_pin(data_pin);\n        let bit_clock_pin = common.make_pio_pin(bit_clock_pin);\n        let lr_clock_pin = common.make_pio_pin(lr_clock_pin);\n\n        let bclk_frequency: u32 = sample_rate * bit_depth * 2;\n\n        let cfg = {\n            let mut cfg = Config::default();\n            cfg.use_program(&program.prg, &[&bit_clock_pin, &lr_clock_pin]);\n            cfg.set_out_pins(&[&data_pin]);\n            cfg.clock_divider = calculate_pio_clock_divider(bclk_frequency * PIO_I2S_OUT_PROGRAM_CLK_MULTIPLIER);\n            cfg.shift_out = ShiftConfig {\n                threshold: 32,\n                direction: ShiftDirection::Left,\n                auto_fill: true,\n            };\n            // join fifos to have twice the time to start the next dma transfer\n            cfg.fifo_join = FifoJoin::TxOnly;\n            cfg\n        };\n        sm.set_config(&cfg);\n        sm.set_pin_dirs(Direction::Out, &[&data_pin, &lr_clock_pin, &bit_clock_pin]);\n\n        // Set the `y` register up to configure the sample depth\n        // The SM counts down to 0 and uses one clock cycle to set up the counter,\n        // which results in bit_depth - 2 as register value.\n        unsafe { sm.set_y(bit_depth - 2) };\n\n        Self {\n            dma: dma::Channel::new(dma, irq),\n            sm,\n        }\n    }\n\n    /// Start the i2s interface\n    pub fn start(&mut self) {\n        self.sm.set_enable(true);\n    }\n\n    /// Stop the i2s interface\n    pub fn stop(&mut self) {\n        self.sm.set_enable(false);\n    }\n\n    /// Start at the the same as other drivers\n    pub fn start_batched(&mut self, b: &mut PioBatch<'d, P>) {\n        b.set_enable(&mut self.sm, true);\n    }\n\n    /// Stop at the the same as other drivers\n    pub fn stop_batched(&mut self, b: &mut PioBatch<'d, P>) {\n        b.set_enable(&mut self.sm, false);\n    }\n\n    /// Return an in-progress dma transfer future. Awaiting it will guarantee a complete transfer.\n    pub fn write<'b>(&'b mut self, buff: &'b [u32]) -> Transfer<'b> {\n        self.sm.tx().dma_push(&mut self.dma, buff, false)\n    }\n}\n"
  },
  {
    "path": "embassy-rp/src/pio_programs/mod.rs",
    "content": "//! Pre-built pio programs for common interfaces\n\npub mod clk;\npub mod clock_divider;\npub mod hd44780;\npub mod i2s;\npub mod onewire;\npub mod pwm;\npub mod rotary_encoder;\npub mod spi;\npub mod stepper;\npub mod uart;\npub mod ws2812;\n"
  },
  {
    "path": "embassy-rp/src/pio_programs/onewire.rs",
    "content": "//! OneWire pio driver\n\nuse crate::Peri;\nuse crate::clocks::clk_sys_freq;\nuse crate::gpio::Level;\nuse crate::pio::{\n    Common, Config, Direction, Instance, LoadedProgram, PioPin, ShiftConfig, ShiftDirection, StateMachine,\n};\n\n/// This struct represents a onewire driver program\npub struct PioOneWireProgram<'a, PIO: Instance> {\n    prg: LoadedProgram<'a, PIO>,\n    reset_addr: u8,\n    next_bit_addr: u8,\n}\n\nimpl<'a, PIO: Instance> PioOneWireProgram<'a, PIO> {\n    /// Load the program into the given pio\n    pub fn new(common: &mut Common<'a, PIO>) -> Self {\n        let prg = pio::pio_asm!(\n            r#\"\n                ; We need to use the pins direction to simulate open drain output\n                ; This results in all the side-set values being swapped from the actual pin value\n                .side_set 1 pindirs\n\n                ; Set the origin to 0 so we can correctly use jmp instructions externally\n                .origin 0\n\n                ; Tick rate is 1 tick per 6us, so all delays should be calculated back to that\n                ; All the instructions have a calculated delay XX in us as [(XX / CLK) - 1].\n                ; The - 1 is for the instruction which also takes one clock cyle.\n                ; The delay can be 0 which will result in just 6us for the instruction itself\n                .define CLK 6\n\n                ; Write the reset block after trigger\n                public reset:\n                        set x, 4                side 0 [(60 / CLK) - 1]     ; idle before reset\n                    reset_inner:                                            ; Repeat the following 5 times, so 5*96us = 480us in total\n                        nop                     side 1 [(90 / CLK) - 1]\n                        jmp x--, reset_inner    side 1 [( 6 / CLK) - 1]\n                        ; Fallthrough\n\n                    ; Check for presence of one or more devices.\n                    ; This samples 32 times with an interval of 12us after a 18us delay.\n                    ; If any bit is zero in the end value, there is a detection\n                    ; This whole function takes 480us\n                        set x, 31               side 0 [(24 / CLK) - 1]     ; Loop 32 times -> 32*12us = 384us\n                    presence_check:\n                        in pins, 1              side 0 [( 6 / CLK) - 1]     ; poll pin and push to isr\n                        jmp x--, presence_check side 0 [( 6 / CLK) - 1]\n                        jmp next_bit            side 0 [(72 / CLK) - 1]\n\n                    ; The low pulse was already done, we only need to delay and poll the bit in case we are reading\n                    write_1:\n                        jmp y--, continue_1     side 0 [( 6 / CLK) - 1]     ; Delay before sampling input. Always decrement y\n                    continue_1:\n                        in pins, 1              side 0 [(48 / CLK) - 1]     ; This writes the state of the pin into the ISR\n                        ; Fallthrough\n\n                ; This is the entry point when reading and writing data\n                public next_bit:\n                    .wrap_target\n                        out x, 1                side 0 [(12 / CLK) - 1]     ; Stalls if no data available in TX FIFO and OSR\n                        jmp x--, write_1        side 1 [( 6 / CLK) - 1]     ; Do the always low part of a bit, jump to write_1 if we want to write a 1 bit\n                        jmp y--, continue_0     side 1 [(48 / CLK) - 1]     ; Do the remainder of the low part of a 0 bit\n                        jmp pullup              side 1 [( 6 / CLK) - 1]     ; Remain low while jumping\n                    continue_0:\n                        in null, 1              side 1 [( 6 / CLK) - 1]     ; This writes 0 into the ISR so that the shift count stays in sync\n                    .wrap\n\n                    ; Assume that strong pullup commands always have MSB (the last bit) = 0,\n                    ; since the rising edge can be used to start the operation.\n                    ; That's the case for DS18B20 (44h and 48h).\n                    pullup:\n                        set pins, 1             side 1[( 6 / CLK) - 1]      ; Drive pin high output immediately.\n                                                                            ; Strong pullup must be within 10us of rise.\n                        in null, 1              side 1[( 6 / CLK) - 1]      ; Keep ISR in sync. Must occur after the y--.\n                        out null, 8             side 1[( 6 / CLK) - 1]      ; Wait for write_bytes_pullup() delay to complete.\n                                                                            ; The delay is hundreds of ms, so done externally.\n                        set pins, 0             side 0[( 6 / CLK) - 1]      ; Back to open drain, pin low when driven\n                        in null, 8              side 1[( 6 / CLK) - 1]      ; Inform write_bytes_pullup() it's ready\n                        jmp next_bit            side 0[( 6 / CLK) - 1]      ; Continue\n            \"#\n        );\n\n        Self {\n            prg: common.load_program(&prg.program),\n            reset_addr: prg.public_defines.reset as u8,\n            next_bit_addr: prg.public_defines.next_bit as u8,\n        }\n    }\n}\n/// Pio backed OneWire driver\npub struct PioOneWire<'d, PIO: Instance, const SM: usize> {\n    sm: StateMachine<'d, PIO, SM>,\n    cfg: Config<'d, PIO>,\n    reset_addr: u8,\n    next_bit_addr: u8,\n}\n\nimpl<'d, PIO: Instance, const SM: usize> PioOneWire<'d, PIO, SM> {\n    /// Create a new instance the driver\n    pub fn new(\n        common: &mut Common<'d, PIO>,\n        mut sm: StateMachine<'d, PIO, SM>,\n        pin: Peri<'d, impl PioPin>,\n        program: &PioOneWireProgram<'d, PIO>,\n    ) -> Self {\n        let pin = common.make_pio_pin(pin);\n\n        sm.set_pin_dirs(Direction::In, &[&pin]);\n        sm.set_pins(Level::Low, &[&pin]);\n\n        let mut cfg = Config::default();\n        cfg.use_program(&program.prg, &[&pin]);\n        cfg.set_in_pins(&[&pin]);\n        cfg.set_set_pins(&[&pin]);\n\n        let shift_cfg = ShiftConfig {\n            auto_fill: true,\n            direction: ShiftDirection::Right,\n            threshold: 8,\n        };\n        cfg.shift_in = shift_cfg;\n        cfg.shift_out = shift_cfg;\n\n        let divider = (clk_sys_freq() / 1000000) as u16 * 6;\n        cfg.clock_divider = divider.into();\n\n        sm.set_config(&cfg);\n        sm.clear_fifos();\n        sm.restart();\n        unsafe {\n            sm.exec_jmp(program.next_bit_addr);\n        }\n        sm.set_enable(true);\n\n        Self {\n            sm,\n            cfg,\n            reset_addr: program.reset_addr,\n            next_bit_addr: program.next_bit_addr,\n        }\n    }\n\n    /// Perform an initialization sequence, will return true if a presence pulse was detected from a device\n    pub async fn reset(&mut self) -> bool {\n        // The state machine immediately starts running when jumping to this address\n        unsafe {\n            self.sm.exec_jmp(self.reset_addr);\n        }\n\n        let rx = self.sm.rx();\n        let mut found = false;\n        for _ in 0..4 {\n            if rx.wait_pull().await != 0 {\n                found = true;\n            }\n        }\n\n        found\n    }\n\n    /// Write bytes to the onewire bus\n    pub async fn write_bytes(&mut self, data: &[u8]) {\n        unsafe {\n            self.sm.set_enable(false);\n            self.sm.set_y(u32::MAX as u32);\n            self.sm.set_enable(true);\n        }\n        let (rx, tx) = self.sm.rx_tx();\n        for b in data {\n            tx.wait_push(*b as u32).await;\n\n            // Empty the buffer that is being filled with every write\n            let _ = rx.wait_pull().await;\n        }\n    }\n\n    /// Write bytes to the onewire bus, then apply a strong pullup\n    pub async fn write_bytes_pullup(&mut self, data: &[u8], pullup_time: embassy_time::Duration) {\n        unsafe {\n            self.sm.set_enable(false);\n            self.sm.set_y(data.len() as u32 * 8 - 1);\n            self.sm.set_enable(true);\n        };\n        let (rx, tx) = self.sm.rx_tx();\n        for b in data {\n            tx.wait_push(*b as u32).await;\n\n            // Empty the buffer that is being filled with every write\n            let _ = rx.wait_pull().await;\n        }\n\n        // Perform the delay, usually hundreds of ms.\n        embassy_time::Timer::after(pullup_time).await;\n\n        // Signal that delay has completed\n        tx.wait_push(0 as u32).await;\n        // Wait until it's back at 0 low, open drain\n        let _ = rx.wait_pull().await;\n    }\n\n    /// Read bytes from the onewire bus\n    pub async fn read_bytes(&mut self, data: &mut [u8]) {\n        unsafe {\n            self.sm.set_enable(false);\n            self.sm.set_y(u32::MAX as u32);\n            self.sm.set_enable(true);\n        };\n        let (rx, tx) = self.sm.rx_tx();\n        for b in data {\n            // Write all 1's so that we can read what the device responds\n            tx.wait_push(0xff).await;\n\n            *b = (rx.wait_pull().await >> 24) as u8;\n        }\n    }\n\n    async fn search(&mut self, state: &mut PioOneWireSearch) -> Option<u64> {\n        if !self.reset().await {\n            // No device present, no use in searching\n            state.finished = true;\n            return None;\n        }\n        self.write_bytes(&[0xF0]).await; // 0xF0 is the search rom command\n\n        self.prepare_search();\n\n        let (rx, tx) = self.sm.rx_tx();\n\n        let mut value = 0;\n        let mut last_zero = 0;\n\n        // The original Dallas app note used 1-based bit numbering with 0 being a\n        // sentinel value (ie None).  This is important if you have sensors with\n        // both 0 and 1 as LSB of the family code.\n        for bit in 1..=64 {\n            // Write 2 dummy bits to read a bit and its complement\n            tx.wait_push(0x1).await;\n            tx.wait_push(0x1).await;\n            let in1 = rx.wait_pull().await;\n            let in2 = rx.wait_pull().await;\n            let push = match (in1, in2) {\n                (0, 0) => {\n                    // If both are 0, it means we have devices with 0 and 1 bits in this position\n                    let write_value = if bit < state.last_discrepancy {\n                        (state.last_rom & (1 << (bit - 1))) != 0\n                    } else {\n                        bit == state.last_discrepancy\n                    };\n\n                    if write_value {\n                        1\n                    } else {\n                        last_zero = bit;\n                        0\n                    }\n                }\n                (0, 1) => 0, // Only devices with a 0 bit in this position\n                (1, 0) => 1, // Only devices with a 1 bit in this position\n                _ => {\n                    // If both are 1, it means there is no device active and there is no point in continuing\n                    self.restore_after_search();\n                    state.finished = true;\n                    return None;\n                }\n            };\n            value >>= 1;\n            if push == 1 {\n                value |= 1 << 63;\n            }\n            tx.wait_push(push).await;\n            let _ = rx.wait_pull().await; // Discard the result of the write action\n        }\n\n        self.restore_after_search();\n\n        state.last_discrepancy = last_zero;\n        state.finished = last_zero == 0;\n        state.last_rom = value;\n        Some(value)\n    }\n\n    fn prepare_search(&mut self) {\n        self.cfg.shift_in.threshold = 1;\n        self.cfg.shift_in.direction = ShiftDirection::Left;\n        self.cfg.shift_out.threshold = 1;\n\n        self.sm.set_enable(false);\n        self.sm.set_config(&self.cfg);\n\n        // set_config jumps to the wrong address so jump to the right one here\n        unsafe {\n            self.sm.exec_jmp(self.next_bit_addr);\n        }\n        self.sm.set_enable(true);\n    }\n\n    fn restore_after_search(&mut self) {\n        self.cfg.shift_in.threshold = 8;\n        self.cfg.shift_in.direction = ShiftDirection::Right;\n        self.cfg.shift_out.threshold = 8;\n\n        self.sm.set_enable(false);\n        self.sm.set_config(&self.cfg);\n\n        // Clear the state in case we aborted prematurely with some bits still in the shift registers\n        self.sm.clear_fifos();\n        self.sm.restart();\n\n        // set_config jumps to the wrong address so jump to the right one here\n        unsafe {\n            self.sm.exec_jmp(self.next_bit_addr);\n        }\n        self.sm.set_enable(true);\n    }\n}\n\n/// Onewire search state\npub struct PioOneWireSearch {\n    last_rom: u64,\n    last_discrepancy: u8,\n    finished: bool,\n}\n\nimpl PioOneWireSearch {\n    /// Create a new Onewire search state\n    pub fn new() -> Self {\n        Self {\n            last_rom: 0,\n            last_discrepancy: 0,\n            finished: false,\n        }\n    }\n\n    /// Search for the next address on the bus\n    pub async fn next<PIO: Instance, const SM: usize>(&mut self, pio: &mut PioOneWire<'_, PIO, SM>) -> Option<u64> {\n        if self.finished { None } else { pio.search(self).await }\n    }\n\n    /// Is finished when all devices have been found\n    pub fn is_finished(&self) -> bool {\n        self.finished\n    }\n}\n"
  },
  {
    "path": "embassy-rp/src/pio_programs/pwm.rs",
    "content": "//! PIO backed PWM driver\n\nuse core::time::Duration;\n\nuse pio::InstructionOperands;\n\nuse crate::gpio::Level;\nuse crate::pio::{Common, Config, Direction, Instance, LoadedProgram, Pin, PioPin, StateMachine};\nuse crate::{Peri, clocks};\n\n/// This converts the duration provided into the number of cycles the PIO needs to run to make it take the same time\nfn to_pio_cycles(duration: Duration) -> u32 {\n    (clocks::clk_sys_freq() / 1_000_000) / 3 * duration.as_micros() as u32 // parentheses are required to prevent overflow\n}\n\n/// This struct represents a PWM program loaded into pio instruction memory.\npub struct PioPwmProgram<'a, PIO: Instance> {\n    prg: LoadedProgram<'a, PIO>,\n}\n\nimpl<'a, PIO: Instance> PioPwmProgram<'a, PIO> {\n    /// Load the program into the given pio\n    pub fn new(common: &mut Common<'a, PIO>) -> Self {\n        let prg = pio::pio_asm!(\n            \".side_set 1 opt\"\n                \"pull noblock    side 0\"\n                \"mov x, osr\"\n                \"mov y, isr\"\n            \"countloop:\"\n                \"jmp x!=y noset\"\n                \"jmp skip        side 1\"\n            \"noset:\"\n                \"nop\"\n            \"skip:\"\n                \"jmp y-- countloop\"\n        );\n\n        let prg = common.load_program(&prg.program);\n\n        Self { prg }\n    }\n}\n\n/// Pio backed PWM output\npub struct PioPwm<'d, T: Instance, const SM: usize> {\n    sm: StateMachine<'d, T, SM>,\n    pin: Pin<'d, T>,\n}\n\nimpl<'d, T: Instance, const SM: usize> PioPwm<'d, T, SM> {\n    /// Configure a state machine as a PWM output\n    pub fn new(\n        pio: &mut Common<'d, T>,\n        mut sm: StateMachine<'d, T, SM>,\n        pin: Peri<'d, impl PioPin>,\n        program: &PioPwmProgram<'d, T>,\n    ) -> Self {\n        let pin = pio.make_pio_pin(pin);\n        sm.set_pins(Level::High, &[&pin]);\n        sm.set_pin_dirs(Direction::Out, &[&pin]);\n\n        let mut cfg = Config::default();\n        cfg.use_program(&program.prg, &[&pin]);\n\n        sm.set_config(&cfg);\n\n        Self { sm, pin }\n    }\n\n    /// Enables the PIO program, continuing the wave generation from the PIO program.\n    pub fn start(&mut self) {\n        self.sm.set_enable(true);\n    }\n\n    /// Stops the PIO program, ceasing all signals from the PIN that were generated via PIO.\n    pub fn stop(&mut self) {\n        self.sm.set_enable(false);\n    }\n\n    /// Sets the pwm period, which is the length of time for each pio wave until reset.\n    pub fn set_period(&mut self, duration: Duration) {\n        let is_enabled = self.sm.is_enabled();\n        while !self.sm.tx().empty() {} // Make sure that the queue is empty\n        self.sm.set_enable(false);\n        self.sm.tx().push(to_pio_cycles(duration));\n        unsafe {\n            self.sm.exec_instr(\n                InstructionOperands::PULL {\n                    if_empty: false,\n                    block: false,\n                }\n                .encode(),\n            );\n            self.sm.exec_instr(\n                InstructionOperands::OUT {\n                    destination: ::pio::OutDestination::ISR,\n                    bit_count: 32,\n                }\n                .encode(),\n            );\n        };\n        if is_enabled {\n            self.sm.set_enable(true) // Enable if previously enabled\n        }\n    }\n\n    /// Set the number of pio cycles to set the wave on high to.\n    pub fn set_level(&mut self, level: u32) {\n        self.sm.tx().push(level);\n    }\n\n    /// Set the pulse width high time\n    pub fn write(&mut self, duration: Duration) {\n        self.set_level(to_pio_cycles(duration));\n    }\n\n    /// Return the state machine and pin.\n    pub fn release(self) -> (StateMachine<'d, T, SM>, Pin<'d, T>) {\n        (self.sm, self.pin)\n    }\n}\n"
  },
  {
    "path": "embassy-rp/src/pio_programs/rotary_encoder.rs",
    "content": "//! PIO backed quadrature encoder\n\nuse crate::Peri;\nuse crate::gpio::Pull;\nuse crate::pio::{\n    Common, Config, Direction as PioDirection, FifoJoin, Instance, LoadedProgram, PioPin, ShiftDirection, StateMachine,\n};\nuse crate::pio_programs::clock_divider::calculate_pio_clock_divider;\n\n/// This struct represents an Encoder program loaded into pio instruction memory.\npub struct PioEncoderProgram<'a, PIO: Instance> {\n    prg: LoadedProgram<'a, PIO>,\n}\n\nimpl<'a, PIO: Instance> PioEncoderProgram<'a, PIO> {\n    /// Load the program into the given pio\n    pub fn new(common: &mut Common<'a, PIO>) -> Self {\n        let prg = pio::pio_asm!(\"wait 1 pin 1\", \"wait 0 pin 1\", \"in pins, 2\", \"push\",);\n\n        let prg = common.load_program(&prg.program);\n\n        Self { prg }\n    }\n}\n\n/// Pio Backed quadrature encoder reader\npub struct PioEncoder<'d, T: Instance, const SM: usize> {\n    sm: StateMachine<'d, T, SM>,\n}\n\nimpl<'d, T: Instance, const SM: usize> PioEncoder<'d, T, SM> {\n    /// Configure a state machine with the loaded [PioEncoderProgram]\n    pub fn new(\n        pio: &mut Common<'d, T>,\n        mut sm: StateMachine<'d, T, SM>,\n        pin_a: Peri<'d, impl PioPin>,\n        pin_b: Peri<'d, impl PioPin>,\n        program: &PioEncoderProgram<'d, T>,\n    ) -> Self {\n        let mut pin_a = pio.make_pio_pin(pin_a);\n        let mut pin_b = pio.make_pio_pin(pin_b);\n        pin_a.set_pull(Pull::Up);\n        pin_b.set_pull(Pull::Up);\n        sm.set_pin_dirs(PioDirection::In, &[&pin_a, &pin_b]);\n\n        let mut cfg = Config::default();\n        cfg.set_in_pins(&[&pin_a, &pin_b]);\n        cfg.fifo_join = FifoJoin::RxOnly;\n        cfg.shift_in.direction = ShiftDirection::Left;\n\n        // Target 12.5 KHz PIO clock\n        cfg.clock_divider = calculate_pio_clock_divider(12_500);\n\n        cfg.use_program(&program.prg, &[]);\n        sm.set_config(&cfg);\n        sm.set_enable(true);\n        Self { sm }\n    }\n\n    /// Read a single count from the encoder\n    pub async fn read(&mut self) -> Direction {\n        loop {\n            match self.sm.rx().wait_pull().await {\n                0 => return Direction::CounterClockwise,\n                1 => return Direction::Clockwise,\n                _ => {}\n            }\n        }\n    }\n}\n\n/// Encoder Count Direction\npub enum Direction {\n    /// Encoder turned clockwise\n    Clockwise,\n    /// Encoder turned counter clockwise\n    CounterClockwise,\n}\n"
  },
  {
    "path": "embassy-rp/src/pio_programs/spi.rs",
    "content": "//! PIO backed SPI drivers\n\nuse core::marker::PhantomData;\n\nuse embassy_futures::join::join;\nuse embassy_hal_internal::Peri;\nuse embedded_hal_02::spi::{Phase, Polarity};\nuse fixed::traits::ToFixed;\nuse fixed::types::extra::U8;\n\nuse crate::clocks::clk_sys_freq;\nuse crate::gpio::Level;\nuse crate::pio::{Common, Direction, Instance, LoadedProgram, Pin, PioPin, ShiftDirection, StateMachine};\nuse crate::spi::{Async, Blocking, Config, Mode};\nuse crate::{dma, interrupt};\n\n/// This struct represents an SPI program loaded into pio instruction memory.\nstruct PioSpiProgram<'d, PIO: Instance> {\n    prg: LoadedProgram<'d, PIO>,\n    phase: Phase,\n}\n\nimpl<'d, PIO: Instance> PioSpiProgram<'d, PIO> {\n    /// Load the spi program into the given pio\n    pub fn new(common: &mut Common<'d, PIO>, phase: Phase) -> Self {\n        // These PIO programs are taken straight from the datasheet (3.6.1 in\n        // RP2040 datasheet, 11.6.1 in RP2350 datasheet)\n\n        // Pin assignments:\n        // - SCK is side-set pin 0\n        // - MOSI is OUT pin 0\n        // - MISO is IN pin 0\n        //\n        // Auto-push and auto-pull must be enabled, and the serial frame size is set by\n        // configuring the push/pull threshold. Shift left/right is fine, but you must\n        // justify the data yourself. This is done most conveniently for frame sizes of\n        // 8 or 16 bits by using the narrow store replication and narrow load byte\n        // picking behavior of RP2040's IO fabric.\n\n        let prg = match phase {\n            Phase::CaptureOnFirstTransition => {\n                let prg = pio::pio_asm!(\n                    r#\"\n                        .side_set 1\n\n                        ; Clock phase = 0: data is captured on the leading edge of each SCK pulse, and\n                        ; transitions on the trailing edge, or some time before the first leading edge.\n\n                        out pins, 1 side 0 [1] ; Stall here on empty (sideset proceeds even if\n                        in pins, 1  side 1 [1] ; instruction stalls, so we stall with SCK low)\n                    \"#\n                );\n\n                common.load_program(&prg.program)\n            }\n            Phase::CaptureOnSecondTransition => {\n                let prg = pio::pio_asm!(\n                    r#\"\n                        .side_set 1\n\n                        ; Clock phase = 1: data transitions on the leading edge of each SCK pulse, and\n                        ; is captured on the trailing edge.\n\n                        out x, 1    side 0     ; Stall here on empty (keep SCK de-asserted)\n                        mov pins, x side 1 [1] ; Output data, assert SCK (mov pins uses OUT mapping)\n                        in pins, 1  side 0     ; Input data, de-assert SCK\n                    \"#\n                );\n\n                common.load_program(&prg.program)\n            }\n        };\n\n        Self { prg, phase }\n    }\n}\n\n/// PIO SPI errors.\n#[derive(Debug, Clone, Copy, PartialEq, Eq)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\n#[non_exhaustive]\npub enum Error {\n    // No errors for now\n}\n\n/// PIO based SPI driver.\n/// Unlike other PIO programs, the PIO SPI driver owns and holds a reference to\n/// the PIO memory it uses. This is so that it can be reconfigured at runtime if\n/// desired.\npub struct Spi<'d, PIO: Instance, const SM: usize, M: Mode> {\n    sm: StateMachine<'d, PIO, SM>,\n    cfg: crate::pio::Config<'d, PIO>,\n    program: Option<PioSpiProgram<'d, PIO>>,\n    clk_pin: Pin<'d, PIO>,\n    tx_dma: Option<dma::Channel<'d>>,\n    rx_dma: Option<dma::Channel<'d>>,\n    phantom: PhantomData<M>,\n}\n\nimpl<'d, PIO: Instance, const SM: usize, M: Mode> Spi<'d, PIO, SM, M> {\n    #[allow(clippy::too_many_arguments)]\n    fn new_inner(\n        pio: &mut Common<'d, PIO>,\n        mut sm: StateMachine<'d, PIO, SM>,\n        clk_pin: Peri<'d, impl PioPin>,\n        mosi_pin: Peri<'d, impl PioPin>,\n        miso_pin: Peri<'d, impl PioPin>,\n        tx_dma: Option<dma::Channel<'d>>,\n        rx_dma: Option<dma::Channel<'d>>,\n        config: Config,\n    ) -> Self {\n        let program = PioSpiProgram::new(pio, config.phase);\n\n        let mut clk_pin = pio.make_pio_pin(clk_pin);\n        let mosi_pin = pio.make_pio_pin(mosi_pin);\n        let miso_pin = pio.make_pio_pin(miso_pin);\n\n        if let Polarity::IdleHigh = config.polarity {\n            clk_pin.set_output_inversion(true);\n        } else {\n            clk_pin.set_output_inversion(false);\n        }\n\n        let mut cfg = crate::pio::Config::default();\n\n        cfg.use_program(&program.prg, &[&clk_pin]);\n        cfg.set_out_pins(&[&mosi_pin]);\n        cfg.set_in_pins(&[&miso_pin]);\n\n        cfg.shift_in.auto_fill = true;\n        cfg.shift_in.direction = ShiftDirection::Left;\n        cfg.shift_in.threshold = 8;\n\n        cfg.shift_out.auto_fill = true;\n        cfg.shift_out.direction = ShiftDirection::Left;\n        cfg.shift_out.threshold = 8;\n\n        cfg.clock_divider = calculate_clock_divider(config.frequency);\n\n        sm.set_config(&cfg);\n\n        sm.set_pins(Level::Low, &[&clk_pin, &mosi_pin]);\n        sm.set_pin_dirs(Direction::Out, &[&clk_pin, &mosi_pin]);\n        sm.set_pin_dirs(Direction::In, &[&miso_pin]);\n\n        sm.set_enable(true);\n\n        Self {\n            sm,\n            program: Some(program),\n            cfg,\n            clk_pin,\n            tx_dma,\n            rx_dma,\n            phantom: PhantomData,\n        }\n    }\n\n    fn blocking_read_u8(&mut self) -> Result<u8, Error> {\n        while self.sm.rx().empty() {}\n        let value = self.sm.rx().pull() as u8;\n\n        Ok(value)\n    }\n\n    fn blocking_write_u8(&mut self, v: u8) -> Result<(), Error> {\n        let value = u32::from_be_bytes([v, 0, 0, 0]);\n\n        while !self.sm.tx().try_push(value) {}\n\n        // need to clear here for flush to work correctly\n        self.sm.tx().stalled();\n\n        Ok(())\n    }\n\n    /// Read data from SPI blocking execution until done.\n    pub fn blocking_read(&mut self, data: &mut [u8]) -> Result<(), Error> {\n        for v in data {\n            self.blocking_write_u8(0)?;\n            *v = self.blocking_read_u8()?;\n        }\n        self.flush()?;\n        Ok(())\n    }\n\n    /// Write data to SPI blocking execution until done.\n    pub fn blocking_write(&mut self, data: &[u8]) -> Result<(), Error> {\n        for v in data {\n            self.blocking_write_u8(*v)?;\n            let _ = self.blocking_read_u8()?;\n        }\n        self.flush()?;\n        Ok(())\n    }\n\n    /// Transfer data to SPI blocking execution until done.\n    pub fn blocking_transfer(&mut self, read: &mut [u8], write: &[u8]) -> Result<(), Error> {\n        let len = read.len().max(write.len());\n        for i in 0..len {\n            let wb = write.get(i).copied().unwrap_or(0);\n            self.blocking_write_u8(wb)?;\n\n            let rb = self.blocking_read_u8()?;\n            if let Some(r) = read.get_mut(i) {\n                *r = rb;\n            }\n        }\n        self.flush()?;\n        Ok(())\n    }\n\n    /// Transfer data in place to SPI blocking execution until done.\n    pub fn blocking_transfer_in_place(&mut self, data: &mut [u8]) -> Result<(), Error> {\n        for v in data {\n            self.blocking_write_u8(*v)?;\n            *v = self.blocking_read_u8()?;\n        }\n        self.flush()?;\n        Ok(())\n    }\n\n    /// Block execution until SPI is done.\n    pub fn flush(&mut self) -> Result<(), Error> {\n        // Wait for all words in the FIFO to have been pulled by the SM\n        while !self.sm.tx().empty() {}\n\n        // Wait for last value to be written out to the wire\n        while !self.sm.tx().stalled() {}\n\n        Ok(())\n    }\n\n    /// Set SPI frequency.\n    pub fn set_frequency(&mut self, freq: u32) {\n        self.sm.set_enable(false);\n\n        let divider = calculate_clock_divider(freq);\n\n        // save into the config for later but dont use sm.set_config() since\n        // that operation is relatively more expensive than just setting the\n        // clock divider\n        self.cfg.clock_divider = divider;\n        self.sm.set_clock_divider(divider);\n\n        self.sm.set_enable(true);\n    }\n\n    /// Set SPI config.\n    ///\n    /// This operation will panic if the PIO program needs to be reloaded and\n    /// there is insufficient room. This is unlikely since the programs for each\n    /// phase only differ in size by a single instruction.\n    pub fn set_config(&mut self, pio: &mut Common<'d, PIO>, config: &Config) {\n        self.sm.set_enable(false);\n\n        self.cfg.clock_divider = calculate_clock_divider(config.frequency);\n\n        if let Polarity::IdleHigh = config.polarity {\n            self.clk_pin.set_output_inversion(true);\n        } else {\n            self.clk_pin.set_output_inversion(false);\n        }\n\n        if self.program.as_ref().unwrap().phase != config.phase {\n            let old_program = self.program.take().unwrap();\n\n            // SAFETY: the state machine is disabled while this happens\n            unsafe { pio.free_instr(old_program.prg.used_memory) };\n\n            let new_program = PioSpiProgram::new(pio, config.phase);\n\n            self.cfg.use_program(&new_program.prg, &[&self.clk_pin]);\n            self.program = Some(new_program);\n        }\n\n        self.sm.set_config(&self.cfg);\n        self.sm.restart();\n\n        self.sm.set_enable(true);\n    }\n}\n\nfn calculate_clock_divider(frequency_hz: u32) -> fixed::FixedU32<U8> {\n    // we multiply by 4 since each clock period is equal to 4 instructions\n\n    let sys_freq = clk_sys_freq().to_fixed::<fixed::FixedU64<U8>>();\n    let target_freq = (frequency_hz * 4).to_fixed::<fixed::FixedU64<U8>>();\n    (sys_freq / target_freq).to_fixed()\n}\n\nimpl<'d, PIO: Instance, const SM: usize> Spi<'d, PIO, SM, Blocking> {\n    /// Create an SPI driver in blocking mode.\n    pub fn new_blocking(\n        pio: &mut Common<'d, PIO>,\n        sm: StateMachine<'d, PIO, SM>,\n        clk: Peri<'d, impl PioPin>,\n        mosi: Peri<'d, impl PioPin>,\n        miso: Peri<'d, impl PioPin>,\n        config: Config,\n    ) -> Self {\n        Self::new_inner(pio, sm, clk, mosi, miso, None, None, config)\n    }\n}\n\nimpl<'d, PIO: Instance, const SM: usize> Spi<'d, PIO, SM, Async> {\n    /// Create an SPI driver in async mode supporting DMA operations.\n    #[allow(clippy::too_many_arguments)]\n    pub fn new<TxDma: dma::ChannelInstance, RxDma: dma::ChannelInstance>(\n        pio: &mut Common<'d, PIO>,\n        sm: StateMachine<'d, PIO, SM>,\n        clk: Peri<'d, impl PioPin>,\n        mosi: Peri<'d, impl PioPin>,\n        miso: Peri<'d, impl PioPin>,\n        tx_dma: Peri<'d, TxDma>,\n        rx_dma: Peri<'d, RxDma>,\n        irq: impl interrupt::typelevel::Binding<TxDma::Interrupt, dma::InterruptHandler<TxDma>>\n        + interrupt::typelevel::Binding<RxDma::Interrupt, dma::InterruptHandler<RxDma>>\n        + 'd,\n        config: Config,\n    ) -> Self {\n        let tx_dma_ch = dma::Channel::new(tx_dma, irq);\n        let rx_dma_ch = dma::Channel::new(rx_dma, irq);\n        Self::new_inner(pio, sm, clk, mosi, miso, Some(tx_dma_ch), Some(rx_dma_ch), config)\n    }\n\n    /// Read data from SPI using DMA.\n    pub async fn read(&mut self, buffer: &mut [u8]) -> Result<(), Error> {\n        let (rx, tx) = self.sm.rx_tx();\n\n        let len = buffer.len();\n\n        let mut rx_ch = self.rx_dma.as_mut().unwrap().reborrow();\n        let rx_transfer = rx.dma_pull(&mut rx_ch, buffer, false);\n\n        let mut tx_ch = self.tx_dma.as_mut().unwrap().reborrow();\n        let tx_transfer = tx.dma_push_zeros::<u8>(&mut tx_ch, len);\n\n        join(tx_transfer, rx_transfer).await;\n\n        Ok(())\n    }\n\n    /// Write data to SPI using DMA.\n    pub async fn write(&mut self, buffer: &[u8]) -> Result<(), Error> {\n        let (rx, tx) = self.sm.rx_tx();\n\n        let mut rx_ch = self.rx_dma.as_mut().unwrap().reborrow();\n        let rx_transfer = rx.dma_pull_discard::<u8>(&mut rx_ch, buffer.len());\n\n        let mut tx_ch = self.tx_dma.as_mut().unwrap().reborrow();\n        let tx_transfer = tx.dma_push(&mut tx_ch, buffer, false);\n\n        join(tx_transfer, rx_transfer).await;\n\n        Ok(())\n    }\n\n    /// Transfer data to SPI using DMA.\n    pub async fn transfer(&mut self, rx_buffer: &mut [u8], tx_buffer: &[u8]) -> Result<(), Error> {\n        self.transfer_inner(rx_buffer, tx_buffer).await\n    }\n\n    /// Transfer data in place to SPI using DMA.\n    pub async fn transfer_in_place(&mut self, words: &mut [u8]) -> Result<(), Error> {\n        self.transfer_inner(words, words).await\n    }\n\n    async fn transfer_inner(&mut self, rx_buffer: *mut [u8], tx_buffer: *const [u8]) -> Result<(), Error> {\n        let (rx, tx) = self.sm.rx_tx();\n\n        let mut rx_ch = self.rx_dma.as_mut().unwrap().reborrow();\n        let rx_transfer = async {\n            rx.dma_pull(&mut rx_ch, unsafe { &mut *rx_buffer }, false).await;\n\n            if tx_buffer.len() > rx_buffer.len() {\n                let read_bytes_len = tx_buffer.len() - rx_buffer.len();\n\n                rx.dma_pull_discard::<u8>(&mut rx_ch, read_bytes_len).await;\n            }\n        };\n\n        let mut tx_ch = self.tx_dma.as_mut().unwrap().reborrow();\n        let tx_transfer = async {\n            tx.dma_push(&mut tx_ch, unsafe { &*tx_buffer }, false).await;\n\n            if rx_buffer.len() > tx_buffer.len() {\n                let write_bytes_len = rx_buffer.len() - tx_buffer.len();\n\n                tx.dma_push_zeros::<u8>(&mut tx_ch, write_bytes_len).await;\n            }\n        };\n\n        join(tx_transfer, rx_transfer).await;\n\n        Ok(())\n    }\n}\n\n// ====================\n\nimpl<'d, PIO: Instance, const SM: usize, M: Mode> embedded_hal_02::blocking::spi::Transfer<u8> for Spi<'d, PIO, SM, M> {\n    type Error = Error;\n    fn transfer<'w>(&mut self, words: &'w mut [u8]) -> Result<&'w [u8], Self::Error> {\n        self.blocking_transfer_in_place(words)?;\n        Ok(words)\n    }\n}\n\nimpl<'d, PIO: Instance, const SM: usize, M: Mode> embedded_hal_02::blocking::spi::Write<u8> for Spi<'d, PIO, SM, M> {\n    type Error = Error;\n\n    fn write(&mut self, words: &[u8]) -> Result<(), Self::Error> {\n        self.blocking_write(words)\n    }\n}\n\nimpl embedded_hal_1::spi::Error for Error {\n    fn kind(&self) -> embedded_hal_1::spi::ErrorKind {\n        match *self {}\n    }\n}\n\nimpl<'d, PIO: Instance, const SM: usize, M: Mode> embedded_hal_1::spi::ErrorType for Spi<'d, PIO, SM, M> {\n    type Error = Error;\n}\n\nimpl<'d, PIO: Instance, const SM: usize, M: Mode> embedded_hal_1::spi::SpiBus<u8> for Spi<'d, PIO, SM, M> {\n    fn flush(&mut self) -> Result<(), Self::Error> {\n        Ok(())\n    }\n\n    fn read(&mut self, words: &mut [u8]) -> Result<(), Self::Error> {\n        self.blocking_transfer(words, &[])\n    }\n\n    fn write(&mut self, words: &[u8]) -> Result<(), Self::Error> {\n        self.blocking_write(words)\n    }\n\n    fn transfer(&mut self, read: &mut [u8], write: &[u8]) -> Result<(), Self::Error> {\n        self.blocking_transfer(read, write)\n    }\n\n    fn transfer_in_place(&mut self, words: &mut [u8]) -> Result<(), Self::Error> {\n        self.blocking_transfer_in_place(words)\n    }\n}\n\nimpl<'d, PIO: Instance, const SM: usize> embedded_hal_async::spi::SpiBus<u8> for Spi<'d, PIO, SM, Async> {\n    async fn flush(&mut self) -> Result<(), Self::Error> {\n        Ok(())\n    }\n\n    async fn write(&mut self, words: &[u8]) -> Result<(), Self::Error> {\n        self.write(words).await\n    }\n\n    async fn read(&mut self, words: &mut [u8]) -> Result<(), Self::Error> {\n        self.read(words).await\n    }\n\n    async fn transfer(&mut self, read: &mut [u8], write: &[u8]) -> Result<(), Self::Error> {\n        self.transfer(read, write).await\n    }\n\n    async fn transfer_in_place(&mut self, words: &mut [u8]) -> Result<(), Self::Error> {\n        self.transfer_in_place(words).await\n    }\n}\n"
  },
  {
    "path": "embassy-rp/src/pio_programs/stepper.rs",
    "content": "//! Pio Stepper Driver for 5-wire steppers\n\nuse core::mem::{self, MaybeUninit};\n\nuse crate::Peri;\nuse crate::pio::{Common, Config, Direction, Instance, Irq, LoadedProgram, PioPin, StateMachine};\nuse crate::pio_programs::clock_divider::calculate_pio_clock_divider;\n\n/// This struct represents a Stepper driver program loaded into pio instruction memory.\npub struct PioStepperProgram<'a, PIO: Instance> {\n    prg: LoadedProgram<'a, PIO>,\n}\n\nimpl<'a, PIO: Instance> PioStepperProgram<'a, PIO> {\n    /// Load the program into the given pio\n    pub fn new(common: &mut Common<'a, PIO>) -> Self {\n        let prg = pio::pio_asm!(\n            \"pull block\",\n            \"mov x, osr\",\n            \"pull block\",\n            \"mov y, osr\",\n            \"jmp !x end\",\n            \"loop:\",\n            \"jmp !osre step\",\n            \"mov osr, y\",\n            \"step:\",\n            \"out pins, 4 [31]\"\n            \"jmp x-- loop\",\n            \"end:\",\n            \"irq 0 rel\"\n        );\n\n        let prg = common.load_program(&prg.program);\n\n        Self { prg }\n    }\n}\n\n/// Pio backed Stepper driver\npub struct PioStepper<'d, T: Instance, const SM: usize> {\n    irq: Irq<'d, T, SM>,\n    sm: StateMachine<'d, T, SM>,\n}\n\nimpl<'d, T: Instance, const SM: usize> PioStepper<'d, T, SM> {\n    /// Configure a state machine to drive a stepper\n    pub fn new(\n        pio: &mut Common<'d, T>,\n        mut sm: StateMachine<'d, T, SM>,\n        irq: Irq<'d, T, SM>,\n        pin0: Peri<'d, impl PioPin>,\n        pin1: Peri<'d, impl PioPin>,\n        pin2: Peri<'d, impl PioPin>,\n        pin3: Peri<'d, impl PioPin>,\n        program: &PioStepperProgram<'d, T>,\n    ) -> Self {\n        let pin0 = pio.make_pio_pin(pin0);\n        let pin1 = pio.make_pio_pin(pin1);\n        let pin2 = pio.make_pio_pin(pin2);\n        let pin3 = pio.make_pio_pin(pin3);\n        sm.set_pin_dirs(Direction::Out, &[&pin0, &pin1, &pin2, &pin3]);\n        let mut cfg = Config::default();\n        cfg.set_out_pins(&[&pin0, &pin1, &pin2, &pin3]);\n\n        cfg.clock_divider = calculate_pio_clock_divider(100 * 136);\n\n        cfg.use_program(&program.prg, &[]);\n        sm.set_config(&cfg);\n        sm.set_enable(true);\n        Self { irq, sm }\n    }\n\n    /// Set pulse frequency\n    pub fn set_frequency(&mut self, freq: u32) {\n        let clock_divider = calculate_pio_clock_divider(freq * 136);\n        let divider_f32 = clock_divider.to_num::<f32>();\n        assert!(divider_f32 <= 65536.0, \"clkdiv must be <= 65536\");\n        assert!(divider_f32 >= 1.0, \"clkdiv must be >= 1\");\n\n        self.sm.set_clock_divider(clock_divider);\n        self.sm.clkdiv_restart();\n    }\n\n    /// Full step, one phase\n    pub async fn step(&mut self, steps: i32) {\n        if steps > 0 {\n            self.run(steps, 0b1000_0100_0010_0001_1000_0100_0010_0001).await\n        } else {\n            self.run(-steps, 0b0001_0010_0100_1000_0001_0010_0100_1000).await\n        }\n    }\n\n    /// Full step, two phase\n    pub async fn step2(&mut self, steps: i32) {\n        if steps > 0 {\n            self.run(steps, 0b1001_1100_0110_0011_1001_1100_0110_0011).await\n        } else {\n            self.run(-steps, 0b0011_0110_1100_1001_0011_0110_1100_1001).await\n        }\n    }\n\n    /// Half step\n    pub async fn step_half(&mut self, steps: i32) {\n        if steps > 0 {\n            self.run(steps, 0b1001_1000_1100_0100_0110_0010_0011_0001).await\n        } else {\n            self.run(-steps, 0b0001_0011_0010_0110_0100_1100_1000_1001).await\n        }\n    }\n\n    async fn run(&mut self, steps: i32, pattern: u32) {\n        self.sm.tx().wait_push(steps as u32).await;\n        self.sm.tx().wait_push(pattern).await;\n        let drop = OnDrop::new(|| {\n            self.sm.clear_fifos();\n            unsafe {\n                self.sm.exec_instr(\n                    pio::InstructionOperands::JMP {\n                        address: 0,\n                        condition: pio::JmpCondition::Always,\n                    }\n                    .encode(),\n                );\n            }\n        });\n        self.irq.wait().await;\n        drop.defuse();\n    }\n}\n\nstruct OnDrop<F: FnOnce()> {\n    f: MaybeUninit<F>,\n}\n\nimpl<F: FnOnce()> OnDrop<F> {\n    pub fn new(f: F) -> Self {\n        Self { f: MaybeUninit::new(f) }\n    }\n\n    pub fn defuse(self) {\n        mem::forget(self)\n    }\n}\n\nimpl<F: FnOnce()> Drop for OnDrop<F> {\n    fn drop(&mut self) {\n        unsafe { self.f.as_ptr().read()() }\n    }\n}\n"
  },
  {
    "path": "embassy-rp/src/pio_programs/uart.rs",
    "content": "//! Pio backed uart drivers\n\nuse core::convert::Infallible;\n\nuse embedded_io_async::{ErrorType, Read, Write};\nuse fixed::traits::ToFixed;\n\nuse crate::Peri;\nuse crate::clocks::clk_sys_freq;\nuse crate::gpio::Level;\nuse crate::pio::{\n    Common, Config, Direction as PioDirection, FifoJoin, Instance, LoadedProgram, PioPin, ShiftDirection, StateMachine,\n};\n\n/// This struct represents a uart tx program loaded into pio instruction memory.\npub struct PioUartTxProgram<'d, PIO: Instance> {\n    prg: LoadedProgram<'d, PIO>,\n}\n\nimpl<'d, PIO: Instance> PioUartTxProgram<'d, PIO> {\n    /// Load the uart tx program into the given pio\n    pub fn new(common: &mut Common<'d, PIO>) -> Self {\n        let prg = pio::pio_asm!(\n            r#\"\n                .side_set 1 opt\n\n                ; An 8n1 UART transmit program.\n                ; OUT pin 0 and side-set pin 0 are both mapped to UART TX pin.\n\n                    pull       side 1 [7]  ; Assert stop bit, or stall with line in idle state\n                    set x, 7   side 0 [7]  ; Preload bit counter, assert start bit for 8 clocks\n                bitloop:                   ; This loop will run 8 times (8n1 UART)\n                    out pins, 1            ; Shift 1 bit from OSR to the first OUT pin\n                    jmp x-- bitloop   [6]  ; Each loop iteration is 8 cycles.\n            \"#\n        );\n\n        let prg = common.load_program(&prg.program);\n\n        Self { prg }\n    }\n}\n\n/// PIO backed Uart transmitter\npub struct PioUartTx<'d, PIO: Instance, const SM: usize> {\n    sm_tx: StateMachine<'d, PIO, SM>,\n}\n\nimpl<'d, PIO: Instance, const SM: usize> PioUartTx<'d, PIO, SM> {\n    /// Configure a pio state machine to use the loaded tx program.\n    pub fn new(\n        baud: u32,\n        common: &mut Common<'d, PIO>,\n        mut sm_tx: StateMachine<'d, PIO, SM>,\n        tx_pin: Peri<'d, impl PioPin>,\n        program: &PioUartTxProgram<'d, PIO>,\n    ) -> Self {\n        let tx_pin = common.make_pio_pin(tx_pin);\n        sm_tx.set_pins(Level::High, &[&tx_pin]);\n        sm_tx.set_pin_dirs(PioDirection::Out, &[&tx_pin]);\n\n        let mut cfg = Config::default();\n\n        cfg.set_out_pins(&[&tx_pin]);\n        cfg.use_program(&program.prg, &[&tx_pin]);\n        cfg.shift_out.auto_fill = false;\n        cfg.shift_out.direction = ShiftDirection::Right;\n        cfg.fifo_join = FifoJoin::TxOnly;\n        cfg.clock_divider = (clk_sys_freq() / (8 * baud)).to_fixed();\n        sm_tx.set_config(&cfg);\n        sm_tx.set_enable(true);\n\n        Self { sm_tx }\n    }\n\n    /// Write a single u8\n    pub async fn write_u8(&mut self, data: u8) {\n        self.sm_tx.tx().wait_push(data as u32).await;\n    }\n}\n\nimpl<PIO: Instance, const SM: usize> ErrorType for PioUartTx<'_, PIO, SM> {\n    type Error = Infallible;\n}\n\nimpl<PIO: Instance, const SM: usize> Write for PioUartTx<'_, PIO, SM> {\n    async fn write(&mut self, buf: &[u8]) -> Result<usize, Infallible> {\n        for byte in buf {\n            self.write_u8(*byte).await;\n        }\n        Ok(buf.len())\n    }\n\n    async fn flush(&mut self) -> Result<(), Infallible> {\n        Ok(())\n    }\n}\n\n/// This struct represents a Uart Rx program loaded into pio instruction memory.\npub struct PioUartRxProgram<'d, PIO: Instance> {\n    prg: LoadedProgram<'d, PIO>,\n}\n\nimpl<'d, PIO: Instance> PioUartRxProgram<'d, PIO> {\n    /// Load the uart rx program into the given pio\n    pub fn new(common: &mut Common<'d, PIO>) -> Self {\n        let prg = pio::pio_asm!(\n            r#\"\n                ; Slightly more fleshed-out 8n1 UART receiver which handles framing errors and\n                ; break conditions more gracefully.\n                ; IN pin 0 and JMP pin are both mapped to the GPIO used as UART RX.\n\n                start:\n                    wait 0 pin 0        ; Stall until start bit is asserted\n                    set x, 7    [10]    ; Preload bit counter, then delay until halfway through\n                rx_bitloop:             ; the first data bit (12 cycles incl wait, set).\n                    in pins, 1          ; Shift data bit into ISR\n                    jmp x-- rx_bitloop [6] ; Loop 8 times, each loop iteration is 8 cycles\n                    jmp pin good_rx_stop   ; Check stop bit (should be high)\n\n                    irq 4 rel           ; Either a framing error or a break. Set a sticky flag,\n                    wait 1 pin 0        ; and wait for line to return to idle state.\n                    jmp start           ; Don't push data if we didn't see good framing.\n\n                good_rx_stop:           ; No delay before returning to start; a little slack is\n                    in null 24\n                    push                ; important in case the TX clock is slightly too fast.\n            \"#\n        );\n\n        let prg = common.load_program(&prg.program);\n\n        Self { prg }\n    }\n}\n\n/// PIO backed Uart receiver\npub struct PioUartRx<'d, PIO: Instance, const SM: usize> {\n    sm_rx: StateMachine<'d, PIO, SM>,\n}\n\nimpl<'d, PIO: Instance, const SM: usize> PioUartRx<'d, PIO, SM> {\n    /// Configure a pio state machine to use the loaded rx program.\n    pub fn new(\n        baud: u32,\n        common: &mut Common<'d, PIO>,\n        mut sm_rx: StateMachine<'d, PIO, SM>,\n        rx_pin: Peri<'d, impl PioPin>,\n        program: &PioUartRxProgram<'d, PIO>,\n    ) -> Self {\n        let mut cfg = Config::default();\n        cfg.use_program(&program.prg, &[]);\n\n        let rx_pin = common.make_pio_pin(rx_pin);\n        sm_rx.set_pins(Level::High, &[&rx_pin]);\n        cfg.set_in_pins(&[&rx_pin]);\n        cfg.set_jmp_pin(&rx_pin);\n        sm_rx.set_pin_dirs(PioDirection::In, &[&rx_pin]);\n\n        cfg.clock_divider = (clk_sys_freq() / (8 * baud)).to_fixed();\n        cfg.shift_in.auto_fill = false;\n        cfg.shift_in.direction = ShiftDirection::Right;\n        cfg.shift_in.threshold = 32;\n        cfg.fifo_join = FifoJoin::RxOnly;\n        sm_rx.set_config(&cfg);\n        sm_rx.set_enable(true);\n\n        Self { sm_rx }\n    }\n\n    /// Wait for a single u8\n    pub async fn read_u8(&mut self) -> u8 {\n        self.sm_rx.rx().wait_pull().await as u8\n    }\n}\n\nimpl<PIO: Instance, const SM: usize> ErrorType for PioUartRx<'_, PIO, SM> {\n    type Error = Infallible;\n}\n\nimpl<PIO: Instance, const SM: usize> Read for PioUartRx<'_, PIO, SM> {\n    async fn read(&mut self, buf: &mut [u8]) -> Result<usize, Infallible> {\n        let mut i = 0;\n        while i < buf.len() {\n            buf[i] = self.read_u8().await;\n            i += 1;\n        }\n        Ok(i)\n    }\n}\n"
  },
  {
    "path": "embassy-rp/src/pio_programs/ws2812.rs",
    "content": "//! [ws2812](https://www.sparkfun.com/categories/tags/ws2812)\n\nuse embassy_time::Timer;\nuse fixed::types::U24F8;\nuse smart_leds::{RGB8, RGBW};\n\nuse crate::clocks::clk_sys_freq;\nuse crate::pio::{\n    Common, Config, FifoJoin, Instance, LoadedProgram, PioPin, ShiftConfig, ShiftDirection, StateMachine,\n};\nuse crate::{Peri, dma, interrupt};\n\nconst T1: u8 = 2; // start bit\nconst T2: u8 = 5; // data bit\nconst T3: u8 = 3; // stop bit\nconst CYCLES_PER_BIT: u32 = (T1 + T2 + T3) as u32;\n\n/// Color orders for WS2812B, type RGB8\npub trait RgbColorOrder {\n    /// Pack an 8-bit RGB color into a u32\n    fn pack(color: RGB8) -> u32;\n}\n\n/// Green, Red, Blue order is the common default for WS2812B\npub struct Grb;\nimpl RgbColorOrder for Grb {\n    /// Pack an 8-bit RGB color into a u32 in GRB order\n    fn pack(color: RGB8) -> u32 {\n        (u32::from(color.g) << 24) | (u32::from(color.r) << 16) | (u32::from(color.b) << 8)\n    }\n}\n\n/// Red, Green, Blue is used by some WS2812B implementations\npub struct Rgb;\nimpl RgbColorOrder for Rgb {\n    /// Pack an 8-bit RGB color into a u32 in RGB order\n    fn pack(color: RGB8) -> u32 {\n        (u32::from(color.r) << 24) | (u32::from(color.g) << 16) | (u32::from(color.b) << 8)\n    }\n}\n\n/// Color orders RGBW strips\npub trait RgbwColorOrder {\n    /// Pack an RGB+W color into a u32\n    fn pack(color: RGBW<u8>) -> u32;\n}\n\n/// Green, Red, Blue, White order is the common default for RGBW strips\npub struct Grbw;\nimpl RgbwColorOrder for Grbw {\n    /// Pack an RGB+W color into a u32 in GRBW order\n    fn pack(color: RGBW<u8>) -> u32 {\n        (u32::from(color.g) << 24) | (u32::from(color.r) << 16) | (u32::from(color.b) << 8) | u32::from(color.a.0)\n    }\n}\n\n/// Red, Green, Blue, White order\npub struct Rgbw;\nimpl RgbwColorOrder for Rgbw {\n    /// Pack an RGB+W color into a u32 in RGBW order\n    fn pack(color: RGBW<u8>) -> u32 {\n        (u32::from(color.r) << 24) | (u32::from(color.g) << 16) | (u32::from(color.b) << 8) | u32::from(color.a.0)\n    }\n}\n\n/// This struct represents a ws2812 program loaded into pio instruction memory.\npub struct PioWs2812Program<'a, PIO: Instance> {\n    prg: LoadedProgram<'a, PIO>,\n}\n\nimpl<'a, PIO: Instance> PioWs2812Program<'a, PIO> {\n    /// Load the ws2812 program into the given pio\n    pub fn new(common: &mut Common<'a, PIO>) -> Self {\n        let side_set = pio::SideSet::new(false, 1, false);\n        let mut a: pio::Assembler<32> = pio::Assembler::new_with_side_set(side_set);\n\n        let mut wrap_target = a.label();\n        let mut wrap_source = a.label();\n        let mut do_zero = a.label();\n        a.set_with_side_set(pio::SetDestination::PINDIRS, 1, 0);\n        a.bind(&mut wrap_target);\n        // Do stop bit\n        a.out_with_delay_and_side_set(pio::OutDestination::X, 1, T3 - 1, 0);\n        // Do start bit\n        a.jmp_with_delay_and_side_set(pio::JmpCondition::XIsZero, &mut do_zero, T1 - 1, 1);\n        // Do data bit = 1\n        a.jmp_with_delay_and_side_set(pio::JmpCondition::Always, &mut wrap_target, T2 - 1, 1);\n        a.bind(&mut do_zero);\n        // Do data bit = 0\n        a.nop_with_delay_and_side_set(T2 - 1, 0);\n        a.bind(&mut wrap_source);\n\n        let prg = a.assemble_with_wrap(wrap_source, wrap_target);\n        let prg = common.load_program(&prg);\n\n        Self { prg }\n    }\n}\n\n/// Pio backed RGB ws2812 driver\n/// Const N is the number of ws2812 leds attached to this pin\npub struct PioWs2812<'d, P: Instance, const S: usize, const N: usize, ORDER>\nwhere\n    ORDER: RgbColorOrder,\n{\n    dma: dma::Channel<'d>,\n    sm: StateMachine<'d, P, S>,\n    _order: core::marker::PhantomData<ORDER>,\n}\n\nimpl<'d, P: Instance, const S: usize, const N: usize> PioWs2812<'d, P, S, N, Grb> {\n    /// Configure a pio state machine to use the loaded ws2812 program.\n    /// Uses the default GRB order.\n    pub fn new<D: dma::ChannelInstance>(\n        pio: &mut Common<'d, P>,\n        sm: StateMachine<'d, P, S>,\n        dma: Peri<'d, D>,\n        irq: impl interrupt::typelevel::Binding<D::Interrupt, dma::InterruptHandler<D>> + 'd,\n        pin: Peri<'d, impl PioPin>,\n        program: &PioWs2812Program<'d, P>,\n    ) -> Self {\n        Self::with_color_order(pio, sm, dma, irq, pin, program)\n    }\n}\n\nimpl<'d, P: Instance, const S: usize, const N: usize, ORDER> PioWs2812<'d, P, S, N, ORDER>\nwhere\n    ORDER: RgbColorOrder,\n{\n    /// Configure a pio state machine to use the loaded ws2812 program.\n    /// Uses the specified color order.\n    pub fn with_color_order<D: dma::ChannelInstance>(\n        pio: &mut Common<'d, P>,\n        mut sm: StateMachine<'d, P, S>,\n        dma: Peri<'d, D>,\n        irq: impl interrupt::typelevel::Binding<D::Interrupt, dma::InterruptHandler<D>> + 'd,\n        pin: Peri<'d, impl PioPin>,\n        program: &PioWs2812Program<'d, P>,\n    ) -> Self {\n        // Setup sm0\n        let mut cfg = Config::default();\n\n        // Pin config\n        let out_pin = pio.make_pio_pin(pin);\n        cfg.set_out_pins(&[&out_pin]);\n        cfg.set_set_pins(&[&out_pin]);\n\n        cfg.use_program(&program.prg, &[&out_pin]);\n\n        // Clock config, measured in kHz to avoid overflows\n        let clock_freq = U24F8::from_num(clk_sys_freq() / 1000);\n        let ws2812_freq = U24F8::from_num(800);\n        let bit_freq = ws2812_freq * CYCLES_PER_BIT;\n        cfg.clock_divider = clock_freq / bit_freq;\n\n        // FIFO config\n        cfg.fifo_join = FifoJoin::TxOnly;\n        cfg.shift_out = ShiftConfig {\n            auto_fill: true,\n            threshold: 24,\n            direction: ShiftDirection::Left,\n        };\n\n        sm.set_config(&cfg);\n        sm.set_enable(true);\n\n        Self {\n            dma: dma::Channel::new(dma, irq),\n            sm,\n            _order: core::marker::PhantomData,\n        }\n    }\n\n    /// Write a buffer of [smart_leds::RGB8] to the ws2812 string\n    pub async fn write(&mut self, colors: &[RGB8; N]) {\n        // Precompute the word bytes from the colors\n        let mut words = [0u32; N];\n        for i in 0..N {\n            words[i] = ORDER::pack(colors[i]);\n        }\n\n        // DMA transfer\n        self.sm.tx().dma_push(&mut self.dma, &words, false).await;\n\n        Timer::after_micros(55).await;\n    }\n}\n\n/// Pio backed RGBW ws2812 driver\n/// This version is intended for ws2812 leds with 4 addressable lights\n/// Const N is the number of ws2812 leds attached to this pin\npub struct RgbwPioWs2812<'d, P: Instance, const S: usize, const N: usize, ORDER>\nwhere\n    ORDER: RgbwColorOrder,\n{\n    dma: dma::Channel<'d>,\n    sm: StateMachine<'d, P, S>,\n    _order: core::marker::PhantomData<ORDER>,\n}\n\nimpl<'d, P: Instance, const S: usize, const N: usize> RgbwPioWs2812<'d, P, S, N, Grbw> {\n    /// Configure a pio state machine to use the loaded ws2812 program.\n    /// Uses the default GRBW color order\n    pub fn new<D: dma::ChannelInstance>(\n        pio: &mut Common<'d, P>,\n        sm: StateMachine<'d, P, S>,\n        dma: Peri<'d, D>,\n        irq: impl interrupt::typelevel::Binding<D::Interrupt, dma::InterruptHandler<D>> + 'd,\n        pin: Peri<'d, impl PioPin>,\n        program: &PioWs2812Program<'d, P>,\n    ) -> Self {\n        Self::with_color_order(pio, sm, dma, irq, pin, program)\n    }\n}\n\nimpl<'d, P: Instance, const S: usize, const N: usize, ORDER> RgbwPioWs2812<'d, P, S, N, ORDER>\nwhere\n    ORDER: RgbwColorOrder,\n{\n    /// Configure a pio state machine to use the loaded ws2812 program.\n    /// Uses the specified color order\n    pub fn with_color_order<D: dma::ChannelInstance>(\n        pio: &mut Common<'d, P>,\n        mut sm: StateMachine<'d, P, S>,\n        dma: Peri<'d, D>,\n        irq: impl interrupt::typelevel::Binding<D::Interrupt, dma::InterruptHandler<D>> + 'd,\n        pin: Peri<'d, impl PioPin>,\n        program: &PioWs2812Program<'d, P>,\n    ) -> Self {\n        // Setup sm0\n        let mut cfg = Config::default();\n\n        // Pin config\n        let out_pin = pio.make_pio_pin(pin);\n        cfg.set_out_pins(&[&out_pin]);\n        cfg.set_set_pins(&[&out_pin]);\n\n        cfg.use_program(&program.prg, &[&out_pin]);\n\n        // Clock config, measured in kHz to avoid overflows\n        let clock_freq = U24F8::from_num(clk_sys_freq() / 1000);\n        let ws2812_freq = U24F8::from_num(800);\n        let bit_freq = ws2812_freq * CYCLES_PER_BIT;\n        cfg.clock_divider = clock_freq / bit_freq;\n\n        // FIFO config\n        cfg.fifo_join = FifoJoin::TxOnly;\n        cfg.shift_out = ShiftConfig {\n            auto_fill: true,\n            threshold: 32,\n            direction: ShiftDirection::Left,\n        };\n\n        sm.set_config(&cfg);\n        sm.set_enable(true);\n\n        Self {\n            dma: dma::Channel::new(dma, irq),\n            sm,\n            _order: core::marker::PhantomData,\n        }\n    }\n\n    /// Write a buffer of [smart_leds::RGBW] to the ws2812 string\n    pub async fn write(&mut self, colors: &[RGBW<u8>; N]) {\n        // Precompute the word bytes from the colors\n        let mut words = [0u32; N];\n        for i in 0..N {\n            words[i] = ORDER::pack(colors[i]);\n        }\n\n        // DMA transfer\n        self.sm.tx().dma_push(&mut self.dma, &words, false).await;\n\n        Timer::after_micros(55).await;\n    }\n}\n"
  },
  {
    "path": "embassy-rp/src/psram.rs",
    "content": "//! PSRAM driver for APS6404L and compatible devices\n//!\n//! This driver provides support for PSRAM (Pseudo-Static RAM) devices connected via QMI CS1.\n//! It handles device verification, initialization, and memory-mapped access configuration.\n//!\n//! This driver is only available on RP235x chips as it requires the QMI CS1 peripheral.\n\n// Credit: Initially based on https://github.com/Altaflux/gb-rp2350 (also licensed Apache 2.0 + MIT).\n// Copyright (c) Altaflux\n\n#![cfg(feature = \"_rp235x\")]\n\nuse critical_section::{CriticalSection, RestoreState, acquire, release};\n\nuse crate::pac;\nuse crate::qmi_cs1::QmiCs1;\n\n/// PSRAM errors.\n#[derive(Debug, Clone, Copy, PartialEq, Eq)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\n#[non_exhaustive]\npub enum Error {\n    /// PSRAM device is not detected or not supported\n    DeviceNotFound,\n    /// Invalid configuration\n    InvalidConfig,\n    /// Detected PSRAM size does not match the expected size\n    SizeMismatch,\n}\n\n/// PSRAM device verification type.\n#[derive(Clone, Copy)]\npub enum VerificationType {\n    /// Skip device verification\n    None,\n    /// Verify as APS6404L device\n    Aps6404l,\n}\n\n/// Memory configuration.\n#[derive(Clone)]\npub struct Config {\n    /// System clock frequency in Hz\n    pub clock_hz: u32,\n    /// Maximum memory operating frequency in Hz\n    pub max_mem_freq: u32,\n    /// Maximum CS assert time in microseconds (must be <= 8 us)\n    pub max_select_us: u32,\n    /// Minimum CS deassert time in nanoseconds (must be >= 18 ns)\n    pub min_deselect_ns: u32,\n    /// Cooldown period between operations (in SCLK cycles)\n    pub cooldown: u8,\n    /// Page break size for memory operations\n    pub page_break: PageBreak,\n    /// Clock divisor for direct mode operations during initialization\n    pub init_clkdiv: u8,\n    /// Enter Quad Mode command\n    pub enter_quad_cmd: Option<u8>,\n    /// Quad Read command (fast read with 4-bit data)\n    pub quad_read_cmd: u8,\n    /// Quad Write command (page program with 4-bit data)\n    pub quad_write_cmd: Option<u8>,\n    /// Number of dummy cycles for quad read operations\n    pub dummy_cycles: u8,\n    /// Read format configuration\n    pub read_format: FormatConfig,\n    /// Write format configuration\n    pub write_format: Option<FormatConfig>,\n    /// Expected memory size in bytes\n    pub mem_size: usize,\n    /// Device verification type\n    pub verification_type: VerificationType,\n    /// Whether the memory is writable via XIP (e.g., PSRAM vs. read-only flash)\n    pub xip_writable: bool,\n}\n\n/// Page break configuration for memory window operations.\n#[derive(Clone, Copy)]\npub enum PageBreak {\n    /// No page breaks\n    None,\n    /// Break at 256-byte boundaries\n    _256,\n    /// Break at 1024-byte boundaries\n    _1024,\n    /// Break at 4096-byte boundaries\n    _4096,\n}\n\n/// Format configuration for read/write operations.\n#[derive(Clone)]\npub struct FormatConfig {\n    /// Width of command prefix phase\n    pub prefix_width: Width,\n    /// Width of address phase\n    pub addr_width: Width,\n    /// Width of command suffix phase\n    pub suffix_width: Width,\n    /// Width of dummy/turnaround phase\n    pub dummy_width: Width,\n    /// Width of data phase\n    pub data_width: Width,\n    /// Length of prefix (None or 8 bits)\n    pub prefix_len: bool,\n    /// Length of suffix (None or 8 bits)\n    pub suffix_len: bool,\n}\n\n/// Interface width for different phases of SPI transfer.\n#[derive(Clone, Copy)]\npub enum Width {\n    /// Single-bit (standard SPI)\n    Single,\n    /// Dual-bit (2 data lines)\n    Dual,\n    /// Quad-bit (4 data lines)\n    Quad,\n}\n\nimpl Default for Config {\n    fn default() -> Self {\n        Self::aps6404l()\n    }\n}\n\nimpl Config {\n    /// Create configuration for APS6404L PSRAM.\n    pub fn aps6404l() -> Self {\n        Self {\n            clock_hz: 125_000_000,        // Default to 125MHz\n            max_mem_freq: 133_000_000,    // APS6404L max frequency\n            max_select_us: 8,             // 8 microseconds max CS assert\n            min_deselect_ns: 18,          // 18 nanoseconds min CS deassert\n            cooldown: 1,                  // 1 SCLK cycle cooldown\n            page_break: PageBreak::_1024, // 1024-byte page boundaries\n            init_clkdiv: 10,              // Medium clock for initialization\n            enter_quad_cmd: Some(0x35),   // Enter Quad Mode\n            quad_read_cmd: 0xEB,          // Fast Quad Read\n            quad_write_cmd: Some(0x38),   // Quad Page Program\n            dummy_cycles: 24,             // 24 dummy cycles for quad read\n            read_format: FormatConfig {\n                prefix_width: Width::Quad,\n                addr_width: Width::Quad,\n                suffix_width: Width::Quad,\n                dummy_width: Width::Quad,\n                data_width: Width::Quad,\n                prefix_len: true,  // 8-bit prefix\n                suffix_len: false, // No suffix\n            },\n            write_format: Some(FormatConfig {\n                prefix_width: Width::Quad,\n                addr_width: Width::Quad,\n                suffix_width: Width::Quad,\n                dummy_width: Width::Quad,\n                data_width: Width::Quad,\n                prefix_len: true,  // 8-bit prefix\n                suffix_len: false, // No suffix\n            }),\n            mem_size: 8 * 1024 * 1024, // 8MB for APS6404L\n            verification_type: VerificationType::Aps6404l,\n            xip_writable: true, // PSRAM is writable\n        }\n    }\n\n    /// Create a custom memory configuration.\n    pub fn custom(\n        clock_hz: u32,\n        max_mem_freq: u32,\n        max_select_us: u32,\n        min_deselect_ns: u32,\n        cooldown: u8,\n        page_break: PageBreak,\n        init_clkdiv: u8,\n        enter_quad_cmd: Option<u8>,\n        quad_read_cmd: u8,\n        quad_write_cmd: Option<u8>,\n        dummy_cycles: u8,\n        read_format: FormatConfig,\n        write_format: Option<FormatConfig>,\n        mem_size: usize,\n        verification_type: VerificationType,\n        xip_writable: bool,\n    ) -> Self {\n        Self {\n            clock_hz,\n            max_mem_freq,\n            max_select_us,\n            min_deselect_ns,\n            cooldown,\n            page_break,\n            init_clkdiv,\n            enter_quad_cmd,\n            quad_read_cmd,\n            quad_write_cmd,\n            dummy_cycles,\n            read_format,\n            write_format,\n            mem_size,\n            verification_type,\n            xip_writable,\n        }\n    }\n}\n\n/// PSRAM driver.\npub struct Psram<'d> {\n    #[allow(dead_code)]\n    qmi_cs1: QmiCs1<'d>,\n    size: usize,\n}\n\nimpl<'d> Psram<'d> {\n    /// Create a new PSRAM driver instance.\n    ///\n    /// This will detect the PSRAM device and configure it for memory-mapped access.\n    pub fn new(qmi_cs1: QmiCs1<'d>, config: Config) -> Result<Self, Error> {\n        let qmi = pac::QMI;\n        let xip = pac::XIP_CTRL;\n\n        // Verify PSRAM device if requested\n        match config.verification_type {\n            VerificationType::Aps6404l => {\n                Self::verify_aps6404l(&qmi, config.mem_size)?;\n                debug!(\"APS6404L PSRAM verified, size: {:#x}\", config.mem_size);\n            }\n            VerificationType::None => {\n                debug!(\"Skipping PSRAM verification, assuming size: {:#x}\", config.mem_size);\n            }\n        }\n\n        // Initialize PSRAM with proper timing\n        Self::init_psram(&qmi, &xip, &config)?;\n\n        Ok(Self {\n            qmi_cs1,\n            size: config.mem_size,\n        })\n    }\n\n    /// Get the detected PSRAM size in bytes.\n    pub fn size(&self) -> usize {\n        self.size\n    }\n\n    /// Get the base address for memory-mapped access.\n    ///\n    /// After initialization, PSRAM can be accessed directly through memory mapping.\n    /// The base address for CS1 is typically 0x11000000.\n    pub fn base_address(&self) -> *mut u8 {\n        0x1100_0000 as *mut u8\n    }\n\n    /// Verify APS6404L PSRAM device matches expected configuration.\n    #[unsafe(link_section = \".data.ram_func\")]\n    #[inline(never)]\n    fn verify_aps6404l(qmi: &pac::qmi::Qmi, expected_size: usize) -> Result<(), Error> {\n        // APS6404L-specific constants\n        const EXPECTED_KGD: u8 = 0x5D;\n        crate::multicore::pause_core1();\n        core::sync::atomic::compiler_fence(core::sync::atomic::Ordering::SeqCst);\n\n        {\n            // Helper for making sure `release` is called even if `f` panics.\n            struct Guard {\n                state: RestoreState,\n            }\n\n            impl Drop for Guard {\n                #[inline(always)]\n                fn drop(&mut self) {\n                    unsafe { release(self.state) }\n                }\n            }\n\n            let state = unsafe { acquire() };\n            let _guard = Guard { state };\n\n            let _cs = unsafe { CriticalSection::new() };\n\n            let (kgd, eid) = unsafe { Self::read_aps6404l_kgd_eid(qmi) };\n\n            let mut detected_size: u32 = 0;\n            if kgd == EXPECTED_KGD as u32 {\n                detected_size = 1024 * 1024;\n                let size_id = eid >> 5;\n                if eid == 0x26 || size_id == 2 {\n                    // APS6404L-3SQR-SN or 8MB variants\n                    detected_size *= 8;\n                } else if size_id == 0 {\n                    detected_size *= 2;\n                } else if size_id == 1 {\n                    detected_size *= 4;\n                }\n            }\n\n            // Verify the detected size matches the expected size\n            if detected_size as usize != expected_size {\n                return Err(Error::SizeMismatch);\n            }\n\n            Ok(())\n        }?;\n\n        crate::multicore::resume_core1();\n\n        Ok(())\n    }\n\n    #[unsafe(link_section = \".data.ram_func\")]\n    #[inline(never)]\n    unsafe fn read_aps6404l_kgd_eid(qmi: &pac::qmi::Qmi) -> (u32, u32) {\n        const RESET_ENABLE_CMD: u8 = 0xf5;\n        const READ_ID_CMD: u8 = 0x9f;\n\n        #[allow(unused_assignments)]\n        let mut kgd: u32 = 0;\n        #[allow(unused_assignments)]\n        let mut eid: u32 = 0;\n\n        let qmi_base = qmi.as_ptr() as usize;\n\n        #[cfg(target_arch = \"arm\")]\n        core::arch::asm!(\n        // Configure DIRECT_CSR: shift clkdiv (30) to bits 29:22 and set EN (bit 0)\n        \"movs {temp}, #30\",\n        \"lsls {temp}, {temp}, #22\",\n        \"orr {temp}, {temp}, #1\",        // Set EN bit\n        \"str {temp}, [{qmi_base}]\",\n\n        // Poll for BUSY to clear before first operation\n        \"1:\",\n        \"ldr {temp}, [{qmi_base}]\",\n        \"lsls {temp}, {temp}, #30\",      // Shift BUSY bit to sign position\n        \"bmi 1b\",                        // Branch if negative (BUSY = 1)\n\n        // Assert CS1N (bit 3)\n        \"ldr {temp}, [{qmi_base}]\",\n        \"orr {temp}, {temp}, #8\",        // Set ASSERT_CS1N bit (bit 3)\n        \"str {temp}, [{qmi_base}]\",\n\n        // Transmit RESET_ENABLE_CMD as quad\n        // DIRECT_TX: OE=1 (bit 19), IWIDTH=2 (bits 17:16), DATA=RESET_ENABLE_CMD\n        \"movs {temp}, {reset_enable_cmd}\",\n        \"orr {temp}, {temp}, #0x80000\",  // Set OE (bit 19)\n        \"orr {temp}, {temp}, #0x20000\",  // Set IWIDTH=2 (quad, bits 17:16)\n        \"str {temp}, [{qmi_base}, #4]\",  // Store to DIRECT_TX\n\n        // Wait for BUSY to clear\n        \"2:\",\n        \"ldr {temp}, [{qmi_base}]\",\n        \"lsls {temp}, {temp}, #30\",\n        \"bmi 2b\",\n\n        // Read and discard RX data\n        \"ldr {temp}, [{qmi_base}, #8]\",\n\n        // Deassert CS1N\n        \"ldr {temp}, [{qmi_base}]\",\n        \"bic {temp}, {temp}, #8\",        // Clear ASSERT_CS1N bit\n        \"str {temp}, [{qmi_base}]\",\n\n        // Assert CS1N again\n        \"ldr {temp}, [{qmi_base}]\",\n        \"orr {temp}, {temp}, #8\",        // Set ASSERT_CS1N bit\n        \"str {temp}, [{qmi_base}]\",\n\n        // Read ID loop (7 iterations)\n        \"movs {counter}, #0\",            // Initialize counter\n\n        \"3:\",                            // Loop start\n        \"cmp {counter}, #0\",\n        \"bne 4f\",                        // If not first iteration, send 0xFF\n\n        // First iteration: send READ_ID_CMD\n        \"movs {temp}, {read_id_cmd}\",\n        \"b 5f\",\n        \"4:\",                            // Other iterations: send 0xFF\n        \"movs {temp}, #0xFF\",\n        \"5:\",\n        \"str {temp}, [{qmi_base}, #4]\",  // Store to DIRECT_TX\n\n        // Wait for TXEMPTY\n        \"6:\",\n        \"ldr {temp}, [{qmi_base}]\",\n        \"lsls {temp}, {temp}, #20\",      // Shift TXEMPTY (bit 11) to bit 31\n        \"bpl 6b\",                        // Branch if positive (TXEMPTY = 0)\n\n        // Wait for BUSY to clear\n        \"7:\",\n        \"ldr {temp}, [{qmi_base}]\",\n        \"lsls {temp}, {temp}, #30\",      // Shift BUSY bit to sign position\n        \"bmi 7b\",                        // Branch if negative (BUSY = 1)\n\n        // Read RX data\n        \"ldr {temp}, [{qmi_base}, #8]\",\n        \"uxth {temp}, {temp}\",           // Extract lower 16 bits\n\n        // Store KGD or EID based on iteration\n        \"cmp {counter}, #5\",\n        \"bne 8f\",\n        \"mov {kgd}, {temp}\",             // Store KGD\n        \"b 9f\",\n        \"8:\",\n        \"cmp {counter}, #6\",\n        \"bne 9f\",\n        \"mov {eid}, {temp}\",             // Store EID\n\n        \"9:\",\n        \"adds {counter}, #1\",\n        \"cmp {counter}, #7\",\n        \"blt 3b\",                        // Continue loop if counter < 7\n\n        // Disable direct mode: clear EN and ASSERT_CS1N\n        \"movs {temp}, #0\",\n        \"str {temp}, [{qmi_base}]\",\n\n        // Memory barriers\n        \"dmb\",\n        \"dsb\",\n        \"isb\",\n        qmi_base = in(reg) qmi_base,\n        temp = out(reg) _,\n        counter = out(reg) _,\n        kgd = out(reg) kgd,\n        eid = out(reg) eid,\n        reset_enable_cmd = const RESET_ENABLE_CMD as u32,\n        read_id_cmd = const READ_ID_CMD as u32,\n        options(nostack),\n        );\n\n        #[cfg(target_arch = \"riscv32\")]\n        unimplemented!(\"APS6404L PSRAM verification not implemented for RISC-V\");\n\n        (kgd, eid)\n    }\n\n    /// Initialize PSRAM with proper timing.\n    #[unsafe(link_section = \".data.ram_func\")]\n    #[inline(never)]\n    fn init_psram(qmi: &pac::qmi::Qmi, xip_ctrl: &pac::xip_ctrl::XipCtrl, config: &Config) -> Result<(), Error> {\n        // Set PSRAM timing for APS6404\n        //\n        // Using an rxdelay equal to the divisor isn't enough when running the APS6404 close to 133 MHz.\n        // So: don't allow running at divisor 1 above 100 MHz (because delay of 2 would be too late),\n        // and add an extra 1 to the rxdelay if the divided clock is > 100 MHz (i.e., sys clock > 200 MHz).\n        let clock_hz = config.clock_hz;\n        let max_psram_freq = config.max_mem_freq;\n\n        let mut divisor: u32 = (clock_hz + max_psram_freq - 1) / max_psram_freq;\n        if divisor == 1 && clock_hz > 100_000_000 {\n            divisor = 2;\n        }\n        let mut rxdelay: u32 = divisor;\n        if clock_hz / divisor > 100_000_000 {\n            rxdelay += 1;\n        }\n\n        // - Max select must be <= 8 us. The value is given in multiples of 64 system clocks.\n        // - Min deselect must be >= 18ns. The value is given in system clock cycles - ceil(divisor / 2).\n        let clock_period_fs: u64 = 1_000_000_000_000_000_u64 / u64::from(clock_hz);\n        let max_select: u8 = (((config.max_select_us as u64 * 1_000_000_000) / clock_period_fs) / 64) as u8;\n        let min_deselect: u32 = ((config.min_deselect_ns as u64 * 1_000_000 + (clock_period_fs - 1)) / clock_period_fs\n            - u64::from(divisor + 1) / 2) as u32;\n\n        crate::multicore::pause_core1();\n        core::sync::atomic::compiler_fence(core::sync::atomic::Ordering::SeqCst);\n\n        if let Some(enter_quad_cmd) = config.enter_quad_cmd {\n            // Helper for making sure `release` is called even if `f` panics.\n            struct Guard {\n                state: RestoreState,\n            }\n\n            impl Drop for Guard {\n                #[inline(always)]\n                fn drop(&mut self) {\n                    unsafe { release(self.state) }\n                }\n            }\n\n            let state = unsafe { acquire() };\n            let _guard = Guard { state };\n\n            let _cs = unsafe { CriticalSection::new() };\n\n            unsafe { Self::direct_csr_send_init_command(config, enter_quad_cmd) };\n\n            qmi.mem(1).timing().write(|w| {\n                w.set_cooldown(config.cooldown);\n                w.set_pagebreak(match config.page_break {\n                    PageBreak::None => pac::qmi::vals::Pagebreak::NONE,\n                    PageBreak::_256 => pac::qmi::vals::Pagebreak::_256,\n                    PageBreak::_1024 => pac::qmi::vals::Pagebreak::_1024,\n                    PageBreak::_4096 => pac::qmi::vals::Pagebreak::_4096,\n                });\n                w.set_max_select(max_select);\n                w.set_min_deselect(min_deselect as u8);\n                w.set_rxdelay(rxdelay as u8);\n                w.set_clkdiv(divisor as u8);\n            });\n\n            // Set PSRAM commands and formats\n            qmi.mem(1).rfmt().write(|w| {\n                let width_to_pac = |w: Width| match w {\n                    Width::Single => pac::qmi::vals::PrefixWidth::S,\n                    Width::Dual => pac::qmi::vals::PrefixWidth::D,\n                    Width::Quad => pac::qmi::vals::PrefixWidth::Q,\n                };\n\n                w.set_prefix_width(width_to_pac(config.read_format.prefix_width));\n                w.set_addr_width(match config.read_format.addr_width {\n                    Width::Single => pac::qmi::vals::AddrWidth::S,\n                    Width::Dual => pac::qmi::vals::AddrWidth::D,\n                    Width::Quad => pac::qmi::vals::AddrWidth::Q,\n                });\n                w.set_suffix_width(match config.read_format.suffix_width {\n                    Width::Single => pac::qmi::vals::SuffixWidth::S,\n                    Width::Dual => pac::qmi::vals::SuffixWidth::D,\n                    Width::Quad => pac::qmi::vals::SuffixWidth::Q,\n                });\n                w.set_dummy_width(match config.read_format.dummy_width {\n                    Width::Single => pac::qmi::vals::DummyWidth::S,\n                    Width::Dual => pac::qmi::vals::DummyWidth::D,\n                    Width::Quad => pac::qmi::vals::DummyWidth::Q,\n                });\n                w.set_data_width(match config.read_format.data_width {\n                    Width::Single => pac::qmi::vals::DataWidth::S,\n                    Width::Dual => pac::qmi::vals::DataWidth::D,\n                    Width::Quad => pac::qmi::vals::DataWidth::Q,\n                });\n                w.set_prefix_len(if config.read_format.prefix_len {\n                    pac::qmi::vals::PrefixLen::_8\n                } else {\n                    pac::qmi::vals::PrefixLen::NONE\n                });\n                w.set_suffix_len(if config.read_format.suffix_len {\n                    pac::qmi::vals::SuffixLen::_8\n                } else {\n                    pac::qmi::vals::SuffixLen::NONE\n                });\n                w.set_dummy_len(match config.dummy_cycles {\n                    0 => pac::qmi::vals::DummyLen::NONE,\n                    4 => pac::qmi::vals::DummyLen::_4,\n                    8 => pac::qmi::vals::DummyLen::_8,\n                    12 => pac::qmi::vals::DummyLen::_12,\n                    16 => pac::qmi::vals::DummyLen::_16,\n                    20 => pac::qmi::vals::DummyLen::_20,\n                    24 => pac::qmi::vals::DummyLen::_24,\n                    28 => pac::qmi::vals::DummyLen::_28,\n                    _ => pac::qmi::vals::DummyLen::_24, // Default to 24\n                });\n            });\n\n            qmi.mem(1).rcmd().write(|w| w.set_prefix(config.quad_read_cmd));\n\n            if let Some(ref write_format) = config.write_format {\n                qmi.mem(1).wfmt().write(|w| {\n                    w.set_prefix_width(match write_format.prefix_width {\n                        Width::Single => pac::qmi::vals::PrefixWidth::S,\n                        Width::Dual => pac::qmi::vals::PrefixWidth::D,\n                        Width::Quad => pac::qmi::vals::PrefixWidth::Q,\n                    });\n                    w.set_addr_width(match write_format.addr_width {\n                        Width::Single => pac::qmi::vals::AddrWidth::S,\n                        Width::Dual => pac::qmi::vals::AddrWidth::D,\n                        Width::Quad => pac::qmi::vals::AddrWidth::Q,\n                    });\n                    w.set_suffix_width(match write_format.suffix_width {\n                        Width::Single => pac::qmi::vals::SuffixWidth::S,\n                        Width::Dual => pac::qmi::vals::SuffixWidth::D,\n                        Width::Quad => pac::qmi::vals::SuffixWidth::Q,\n                    });\n                    w.set_dummy_width(match write_format.dummy_width {\n                        Width::Single => pac::qmi::vals::DummyWidth::S,\n                        Width::Dual => pac::qmi::vals::DummyWidth::D,\n                        Width::Quad => pac::qmi::vals::DummyWidth::Q,\n                    });\n                    w.set_data_width(match write_format.data_width {\n                        Width::Single => pac::qmi::vals::DataWidth::S,\n                        Width::Dual => pac::qmi::vals::DataWidth::D,\n                        Width::Quad => pac::qmi::vals::DataWidth::Q,\n                    });\n                    w.set_prefix_len(if write_format.prefix_len {\n                        pac::qmi::vals::PrefixLen::_8\n                    } else {\n                        pac::qmi::vals::PrefixLen::NONE\n                    });\n                    w.set_suffix_len(if write_format.suffix_len {\n                        pac::qmi::vals::SuffixLen::_8\n                    } else {\n                        pac::qmi::vals::SuffixLen::NONE\n                    });\n                });\n            }\n\n            if let Some(quad_write_cmd) = config.quad_write_cmd {\n                qmi.mem(1).wcmd().write(|w| w.set_prefix(quad_write_cmd));\n            }\n\n            if config.xip_writable {\n                // Enable XIP writable mode for PSRAM\n                xip_ctrl.ctrl().modify(|w| w.set_writable_m1(true));\n            } else {\n                // Disable XIP writable mode\n                xip_ctrl.ctrl().modify(|w| w.set_writable_m1(false));\n            }\n        }\n        crate::multicore::resume_core1();\n\n        Ok(())\n    }\n\n    #[unsafe(link_section = \".data.ram_func\")]\n    #[inline(never)]\n    unsafe fn direct_csr_send_init_command(config: &Config, init_cmd: u8) {\n        #[cfg(target_arch = \"arm\")]\n        core::arch::asm!(\n        // Full memory barrier\n        \"dmb\",\n        \"dsb\",\n        \"isb\",\n\n        // Configure QMI Direct CSR register\n        // Load base address of QMI (0x400D0000)\n        \"movw {base}, #0x0000\",\n        \"movt {base}, #0x400D\",\n\n        // Load init_clkdiv and shift to bits 29:22\n        \"lsl {temp}, {clkdiv}, #22\",\n\n        // OR with EN (bit 0) and AUTO_CS1N (bit 7)\n        \"orr {temp}, {temp}, #0x81\",\n\n        // Store to DIRECT_CSR register\n        \"str {temp}, [{base}, #0]\",\n\n        // Memory barrier\n        \"dmb\",\n\n        // First busy wait loop\n        \"1:\",\n        \"ldr {temp}, [{base}, #0]\",      // Load DIRECT_CSR\n        \"tst {temp}, #0x2\",              // Test BUSY bit (bit 1)\n        \"bne 1b\",                        // Branch if busy\n\n        // Write to Direct TX register\n        \"mov {temp}, {enter_quad_cmd}\",\n\n        // OR with NOPUSH (bit 20)\n        \"orr {temp}, {temp}, #0x100000\",\n\n        // Store to DIRECT_TX register (offset 0x4)\n        \"str {temp}, [{base}, #4]\",\n\n        // Memory barrier\n        \"dmb\",\n\n        // Second busy wait loop\n        \"2:\",\n        \"ldr {temp}, [{base}, #0]\",      // Load DIRECT_CSR\n        \"tst {temp}, #0x2\",              // Test BUSY bit (bit 1)\n        \"bne 2b\",                        // Branch if busy\n\n        // Disable Direct CSR\n        \"mov {temp}, #0\",\n        \"str {temp}, [{base}, #0]\",      // Clear DIRECT_CSR register\n\n        // Full memory barrier to ensure no prefetching\n        \"dmb\",\n        \"dsb\",\n        \"isb\",\n        base = out(reg) _,\n        temp = out(reg) _,\n        clkdiv = in(reg) config.init_clkdiv as u32,\n        enter_quad_cmd = in(reg) u32::from(init_cmd),\n        options(nostack),\n        );\n\n        #[cfg(target_arch = \"riscv32\")]\n        unimplemented!(\"Direct CSR command sending is not implemented for RISC-V yet\");\n    }\n}\n"
  },
  {
    "path": "embassy-rp/src/pwm.rs",
    "content": "//! Pulse Width Modulation (PWM)\n\nuse embassy_hal_internal::{Peri, PeripheralType};\npub use embedded_hal_1::pwm::SetDutyCycle;\nuse embedded_hal_1::pwm::{Error, ErrorKind, ErrorType};\nuse fixed::FixedU16;\nuse fixed::traits::ToFixed;\nuse pac::pwm::regs::{ChDiv, Intr};\nuse pac::pwm::vals::Divmode;\n\nuse crate::gpio::{AnyPin, Pin as GpioPin, Pull, SealedPin as _};\nuse crate::{RegExt, pac, peripherals};\n\n/// The configuration of a PWM slice.\n/// Note the period in clock cycles of a slice can be computed as:\n/// `(top + 1) * (phase_correct ? 1 : 2) * divider`\n#[non_exhaustive]\n#[derive(Clone)]\npub struct Config {\n    /// Inverts the PWM output signal on channel A.\n    pub invert_a: bool,\n    /// Inverts the PWM output signal on channel B.\n    pub invert_b: bool,\n    /// Enables phase-correct mode for PWM operation.\n    /// In phase-correct mode, the PWM signal is generated in such a way that\n    /// the pulse is always centered regardless of the duty cycle.\n    /// The output frequency is halved when phase-correct mode is enabled.\n    pub phase_correct: bool,\n    /// Enables the PWM slice, allowing it to generate an output.\n    pub enable: bool,\n    /// A fractional clock divider, represented as a fixed-point number with\n    /// 8 integer bits and 4 fractional bits. It allows precise control over\n    /// the PWM output frequency by gating the PWM counter increment.\n    /// A higher value will result in a slower output frequency.\n    pub divider: fixed::FixedU16<fixed::types::extra::U4>,\n    /// The output on channel A goes high when `compare_a` is higher than the\n    /// counter. A compare of 0 will produce an always low output, while a\n    /// compare of `top + 1` will produce an always high output.\n    pub compare_a: u16,\n    /// The output on channel B goes high when `compare_b` is higher than the\n    /// counter. A compare of 0 will produce an always low output, while a\n    /// compare of `top + 1` will produce an always high output.\n    pub compare_b: u16,\n    /// The point at which the counter wraps, representing the maximum possible\n    /// period. The counter will either wrap to 0 or reverse depending on the\n    /// setting of `phase_correct`.\n    pub top: u16,\n}\n\nimpl Default for Config {\n    fn default() -> Self {\n        Self {\n            invert_a: false,\n            invert_b: false,\n            phase_correct: false,\n            enable: true, // differs from reset value\n            divider: 1.to_fixed(),\n            compare_a: 0,\n            compare_b: 0,\n            top: 0xffff,\n        }\n    }\n}\n\n/// PWM input mode.\npub enum InputMode {\n    /// Level mode.\n    Level,\n    /// Rising edge mode.\n    RisingEdge,\n    /// Falling edge mode.\n    FallingEdge,\n}\n\nimpl From<InputMode> for Divmode {\n    fn from(value: InputMode) -> Self {\n        match value {\n            InputMode::Level => Divmode::LEVEL,\n            InputMode::RisingEdge => Divmode::RISE,\n            InputMode::FallingEdge => Divmode::FALL,\n        }\n    }\n}\n\n/// PWM error.\n#[derive(Debug)]\npub enum PwmError {\n    /// Invalid Duty Cycle.\n    InvalidDutyCycle,\n}\n\nimpl Error for PwmError {\n    fn kind(&self) -> ErrorKind {\n        match self {\n            PwmError::InvalidDutyCycle => ErrorKind::Other,\n        }\n    }\n}\n\n/// PWM driver.\npub struct Pwm<'d> {\n    pin_a: Option<Peri<'d, AnyPin>>,\n    pin_b: Option<Peri<'d, AnyPin>>,\n    slice: usize,\n}\n\nimpl<'d> ErrorType for Pwm<'d> {\n    type Error = PwmError;\n}\n\nimpl<'d> SetDutyCycle for Pwm<'d> {\n    fn max_duty_cycle(&self) -> u16 {\n        pac::PWM.ch(self.slice).top().read().top()\n    }\n\n    fn set_duty_cycle(&mut self, duty: u16) -> Result<(), Self::Error> {\n        let max_duty = self.max_duty_cycle();\n        if duty > max_duty {\n            return Err(PwmError::InvalidDutyCycle);\n        }\n\n        let p = pac::PWM.ch(self.slice);\n        p.cc().modify(|w| {\n            w.set_a(duty);\n            w.set_b(duty);\n        });\n        Ok(())\n    }\n}\n\nimpl<'d> Pwm<'d> {\n    fn new_inner(\n        slice: usize,\n        a: Option<Peri<'d, AnyPin>>,\n        b: Option<Peri<'d, AnyPin>>,\n        b_pull: Pull,\n        config: Config,\n        divmode: Divmode,\n    ) -> Self {\n        let p = pac::PWM.ch(slice);\n        p.csr().modify(|w| {\n            w.set_divmode(divmode);\n            w.set_en(false);\n        });\n        p.ctr().write(|w| w.0 = 0);\n        Self::configure(p, &config);\n\n        if let Some(pin) = &a {\n            pin.gpio().ctrl().write(|w| w.set_funcsel(4));\n            #[cfg(feature = \"_rp235x\")]\n            pin.pad_ctrl().modify(|w| {\n                w.set_iso(false);\n            });\n        }\n        if let Some(pin) = &b {\n            pin.gpio().ctrl().write(|w| w.set_funcsel(4));\n            pin.pad_ctrl().modify(|w| {\n                #[cfg(feature = \"_rp235x\")]\n                w.set_iso(false);\n                #[cfg(feature = \"_rp235x\")]\n                if divmode != Divmode::DIV {\n                    // Is in input mode and so must enable input mode for the pin\n                    w.set_ie(true);\n                }\n                w.set_pue(b_pull == Pull::Up);\n                w.set_pde(b_pull == Pull::Down);\n            });\n        }\n        Self {\n            // inner: p.into(),\n            pin_a: a,\n            pin_b: b,\n            slice,\n        }\n    }\n\n    /// Create PWM driver without any configured pins.\n    #[inline]\n    pub fn new_free<T: Slice>(slice: Peri<'d, T>, config: Config) -> Self {\n        Self::new_inner(slice.number(), None, None, Pull::None, config, Divmode::DIV)\n    }\n\n    /// Create PWM driver with a single 'a' pin as output.\n    #[inline]\n    pub fn new_output_a<T: Slice>(slice: Peri<'d, T>, a: Peri<'d, impl ChannelAPin<T>>, config: Config) -> Self {\n        Self::new_inner(slice.number(), Some(a.into()), None, Pull::None, config, Divmode::DIV)\n    }\n\n    /// Create PWM driver with a single 'b' pin as output.\n    #[inline]\n    pub fn new_output_b<T: Slice>(slice: Peri<'d, T>, b: Peri<'d, impl ChannelBPin<T>>, config: Config) -> Self {\n        Self::new_inner(slice.number(), None, Some(b.into()), Pull::None, config, Divmode::DIV)\n    }\n\n    /// Create PWM driver with a 'a' and 'b' pins as output.\n    #[inline]\n    pub fn new_output_ab<T: Slice>(\n        slice: Peri<'d, T>,\n        a: Peri<'d, impl ChannelAPin<T>>,\n        b: Peri<'d, impl ChannelBPin<T>>,\n        config: Config,\n    ) -> Self {\n        Self::new_inner(\n            slice.number(),\n            Some(a.into()),\n            Some(b.into()),\n            Pull::None,\n            config,\n            Divmode::DIV,\n        )\n    }\n\n    /// Create PWM driver with a single 'b' as input pin.\n    #[inline]\n    pub fn new_input<T: Slice>(\n        slice: Peri<'d, T>,\n        b: Peri<'d, impl ChannelBPin<T>>,\n        b_pull: Pull,\n        mode: InputMode,\n        config: Config,\n    ) -> Self {\n        Self::new_inner(slice.number(), None, Some(b.into()), b_pull, config, mode.into())\n    }\n\n    /// Create PWM driver with a 'a' and 'b' pins in the desired input mode.\n    #[inline]\n    pub fn new_output_input<T: Slice>(\n        slice: Peri<'d, T>,\n        a: Peri<'d, impl ChannelAPin<T>>,\n        b: Peri<'d, impl ChannelBPin<T>>,\n        b_pull: Pull,\n        mode: InputMode,\n        config: Config,\n    ) -> Self {\n        Self::new_inner(\n            slice.number(),\n            Some(a.into()),\n            Some(b.into()),\n            b_pull,\n            config,\n            mode.into(),\n        )\n    }\n\n    /// Set the PWM config.\n    pub fn set_config(&mut self, config: &Config) {\n        Self::configure(pac::PWM.ch(self.slice), config);\n    }\n\n    fn configure(p: pac::pwm::Channel, config: &Config) {\n        if config.divider > FixedU16::<fixed::types::extra::U4>::from_bits(0xFFF) {\n            panic!(\"Requested divider is too large\");\n        }\n\n        p.div().write_value(ChDiv(config.divider.to_bits() as u32));\n        p.cc().write(|w| {\n            w.set_a(config.compare_a);\n            w.set_b(config.compare_b);\n        });\n        p.top().write(|w| w.set_top(config.top));\n        p.csr().modify(|w| {\n            w.set_a_inv(config.invert_a);\n            w.set_b_inv(config.invert_b);\n            w.set_ph_correct(config.phase_correct);\n            w.set_en(config.enable);\n        });\n    }\n\n    /// Advances a slice's output phase by one count while it is running\n    /// by inserting a pulse into the clock enable. The counter\n    /// will not count faster than once per cycle.\n    #[inline]\n    pub fn phase_advance(&mut self) {\n        let p = pac::PWM.ch(self.slice);\n        p.csr().write_set(|w| w.set_ph_adv(true));\n        while p.csr().read().ph_adv() {}\n    }\n\n    /// Retards a slice's output phase by one count while it is running\n    /// by deleting a pulse from the clock enable. The counter will not\n    /// count backward when clock enable is permanently low.\n    #[inline]\n    pub fn phase_retard(&mut self) {\n        let p = pac::PWM.ch(self.slice);\n        p.csr().write_set(|w| w.set_ph_ret(true));\n        while p.csr().read().ph_ret() {}\n    }\n\n    /// Read PWM counter.\n    #[inline]\n    pub fn counter(&self) -> u16 {\n        pac::PWM.ch(self.slice).ctr().read().ctr()\n    }\n\n    /// Write PWM counter.\n    #[inline]\n    pub fn set_counter(&self, ctr: u16) {\n        pac::PWM.ch(self.slice).ctr().write(|w| w.set_ctr(ctr))\n    }\n\n    /// Wait for channel interrupt.\n    #[inline]\n    pub fn wait_for_wrap(&mut self) {\n        while !self.wrapped() {}\n        self.clear_wrapped();\n    }\n\n    /// Check if interrupt for channel is set.\n    #[inline]\n    pub fn wrapped(&mut self) -> bool {\n        pac::PWM.intr().read().0 & self.bit() != 0\n    }\n\n    #[inline]\n    /// Clear interrupt flag.\n    pub fn clear_wrapped(&mut self) {\n        pac::PWM.intr().write_value(Intr(self.bit() as _));\n    }\n\n    #[inline]\n    fn bit(&self) -> u32 {\n        1 << self.slice as usize\n    }\n\n    /// Splits the PWM driver into separate `PwmOutput` instances for channels A and B.\n    #[inline]\n    pub fn split(mut self) -> (Option<PwmOutput<'d>>, Option<PwmOutput<'d>>) {\n        (\n            self.pin_a\n                .take()\n                .map(|pin| PwmOutput::new(PwmChannelPin::A(pin), self.slice.clone(), true)),\n            self.pin_b\n                .take()\n                .map(|pin| PwmOutput::new(PwmChannelPin::B(pin), self.slice.clone(), true)),\n        )\n    }\n    /// Splits the PWM driver by reference to allow for separate duty cycle control\n    /// of each channel (A and B) without taking ownership of the PWM instance.\n    #[inline]\n    pub fn split_by_ref(&mut self) -> (Option<PwmOutput<'_>>, Option<PwmOutput<'_>>) {\n        (\n            self.pin_a\n                .as_mut()\n                .map(|pin| PwmOutput::new(PwmChannelPin::A(pin.reborrow()), self.slice.clone(), false)),\n            self.pin_b\n                .as_mut()\n                .map(|pin| PwmOutput::new(PwmChannelPin::B(pin.reborrow()), self.slice.clone(), false)),\n        )\n    }\n}\n\nenum PwmChannelPin<'d> {\n    A(Peri<'d, AnyPin>),\n    B(Peri<'d, AnyPin>),\n}\n\n/// Single channel of Pwm driver.\npub struct PwmOutput<'d> {\n    //pin that can be ether ChannelAPin or ChannelBPin\n    channel_pin: PwmChannelPin<'d>,\n    slice: usize,\n    is_owned: bool,\n}\n\nimpl<'d> PwmOutput<'d> {\n    fn new(channel_pin: PwmChannelPin<'d>, slice: usize, is_owned: bool) -> Self {\n        Self {\n            channel_pin,\n            slice,\n            is_owned,\n        }\n    }\n}\n\nimpl<'d> Drop for PwmOutput<'d> {\n    fn drop(&mut self) {\n        if self.is_owned {\n            let p = pac::PWM.ch(self.slice);\n            match &self.channel_pin {\n                PwmChannelPin::A(pin) => {\n                    p.cc().modify(|w| {\n                        w.set_a(0);\n                    });\n\n                    pin.gpio().ctrl().write(|w| w.set_funcsel(31));\n                    //Enable pin PULL-DOWN\n                    pin.pad_ctrl().modify(|w| {\n                        w.set_pde(true);\n                    });\n                }\n                PwmChannelPin::B(pin) => {\n                    p.cc().modify(|w| {\n                        w.set_b(0);\n                    });\n                    pin.gpio().ctrl().write(|w| w.set_funcsel(31));\n                    //Enable pin PULL-DOWN\n                    pin.pad_ctrl().modify(|w| {\n                        w.set_pde(true);\n                    });\n                }\n            }\n        }\n    }\n}\n\nimpl<'d> ErrorType for PwmOutput<'d> {\n    type Error = PwmError;\n}\n\nimpl<'d> SetDutyCycle for PwmOutput<'d> {\n    fn max_duty_cycle(&self) -> u16 {\n        pac::PWM.ch(self.slice).top().read().top()\n    }\n\n    fn set_duty_cycle(&mut self, duty: u16) -> Result<(), Self::Error> {\n        let max_duty = self.max_duty_cycle();\n        if duty > max_duty {\n            return Err(PwmError::InvalidDutyCycle);\n        }\n\n        let p = pac::PWM.ch(self.slice);\n        match self.channel_pin {\n            PwmChannelPin::A(_) => {\n                p.cc().modify(|w| {\n                    w.set_a(duty);\n                });\n            }\n            PwmChannelPin::B(_) => {\n                p.cc().modify(|w| {\n                    w.set_b(duty);\n                });\n            }\n        }\n\n        Ok(())\n    }\n}\n\n/// Batch representation of PWM slices.\npub struct PwmBatch(u32);\n\nimpl PwmBatch {\n    #[inline]\n    /// Enable a PWM slice in this batch.\n    pub fn enable(&mut self, pwm: &Pwm<'_>) {\n        self.0 |= pwm.bit();\n    }\n\n    #[inline]\n    /// Enable slices in this batch in a PWM.\n    pub fn set_enabled(enabled: bool, batch: impl FnOnce(&mut PwmBatch)) {\n        let mut en = PwmBatch(0);\n        batch(&mut en);\n        if enabled {\n            pac::PWM.en().write_set(|w| w.0 = en.0);\n        } else {\n            pac::PWM.en().write_clear(|w| w.0 = en.0);\n        }\n    }\n}\n\nimpl<'d> Drop for Pwm<'d> {\n    fn drop(&mut self) {\n        pac::PWM.ch(self.slice).csr().write_clear(|w| w.set_en(false));\n        if let Some(pin) = &self.pin_a {\n            pin.gpio().ctrl().write(|w| w.set_funcsel(31));\n            // Enable pin PULL-DOWN\n            pin.pad_ctrl().modify(|w| {\n                w.set_pde(true);\n            });\n        }\n        if let Some(pin) = &self.pin_b {\n            pin.gpio().ctrl().write(|w| w.set_funcsel(31));\n            #[cfg(feature = \"_rp235x\")]\n            // Disable input mode. Only pin_b can be input, so not needed for pin_a\n            pin.pad_ctrl().modify(|w| {\n                w.set_ie(false);\n            });\n            // Enable pin PULL-DOWN\n            pin.pad_ctrl().modify(|w| {\n                w.set_pde(true);\n            });\n        }\n    }\n}\n\ntrait SealedSlice {}\n\n/// PWM Slice.\n#[allow(private_bounds)]\npub trait Slice: PeripheralType + SealedSlice + Sized + 'static {\n    /// Slice number.\n    fn number(&self) -> usize;\n}\n\nmacro_rules! slice {\n    ($name:ident, $num:expr) => {\n        impl SealedSlice for peripherals::$name {}\n        impl Slice for peripherals::$name {\n            fn number(&self) -> usize {\n                $num\n            }\n        }\n    };\n}\n\nslice!(PWM_SLICE0, 0);\nslice!(PWM_SLICE1, 1);\nslice!(PWM_SLICE2, 2);\nslice!(PWM_SLICE3, 3);\nslice!(PWM_SLICE4, 4);\nslice!(PWM_SLICE5, 5);\nslice!(PWM_SLICE6, 6);\nslice!(PWM_SLICE7, 7);\n\n#[cfg(feature = \"_rp235x\")]\nslice!(PWM_SLICE8, 8);\n#[cfg(feature = \"_rp235x\")]\nslice!(PWM_SLICE9, 9);\n#[cfg(feature = \"_rp235x\")]\nslice!(PWM_SLICE10, 10);\n#[cfg(feature = \"_rp235x\")]\nslice!(PWM_SLICE11, 11);\n\n/// PWM Channel A.\npub trait ChannelAPin<T: Slice>: GpioPin {}\n/// PWM Channel B.\npub trait ChannelBPin<T: Slice>: GpioPin {}\n\nmacro_rules! impl_pin {\n    ($pin:ident, $channel:ident, $kind:ident) => {\n        impl $kind<peripherals::$channel> for peripherals::$pin {}\n    };\n}\n\nimpl_pin!(PIN_0, PWM_SLICE0, ChannelAPin);\nimpl_pin!(PIN_1, PWM_SLICE0, ChannelBPin);\nimpl_pin!(PIN_2, PWM_SLICE1, ChannelAPin);\nimpl_pin!(PIN_3, PWM_SLICE1, ChannelBPin);\nimpl_pin!(PIN_4, PWM_SLICE2, ChannelAPin);\nimpl_pin!(PIN_5, PWM_SLICE2, ChannelBPin);\nimpl_pin!(PIN_6, PWM_SLICE3, ChannelAPin);\nimpl_pin!(PIN_7, PWM_SLICE3, ChannelBPin);\nimpl_pin!(PIN_8, PWM_SLICE4, ChannelAPin);\nimpl_pin!(PIN_9, PWM_SLICE4, ChannelBPin);\nimpl_pin!(PIN_10, PWM_SLICE5, ChannelAPin);\nimpl_pin!(PIN_11, PWM_SLICE5, ChannelBPin);\nimpl_pin!(PIN_12, PWM_SLICE6, ChannelAPin);\nimpl_pin!(PIN_13, PWM_SLICE6, ChannelBPin);\nimpl_pin!(PIN_14, PWM_SLICE7, ChannelAPin);\nimpl_pin!(PIN_15, PWM_SLICE7, ChannelBPin);\nimpl_pin!(PIN_16, PWM_SLICE0, ChannelAPin);\nimpl_pin!(PIN_17, PWM_SLICE0, ChannelBPin);\nimpl_pin!(PIN_18, PWM_SLICE1, ChannelAPin);\nimpl_pin!(PIN_19, PWM_SLICE1, ChannelBPin);\nimpl_pin!(PIN_20, PWM_SLICE2, ChannelAPin);\nimpl_pin!(PIN_21, PWM_SLICE2, ChannelBPin);\nimpl_pin!(PIN_22, PWM_SLICE3, ChannelAPin);\nimpl_pin!(PIN_23, PWM_SLICE3, ChannelBPin);\nimpl_pin!(PIN_24, PWM_SLICE4, ChannelAPin);\nimpl_pin!(PIN_25, PWM_SLICE4, ChannelBPin);\nimpl_pin!(PIN_26, PWM_SLICE5, ChannelAPin);\nimpl_pin!(PIN_27, PWM_SLICE5, ChannelBPin);\nimpl_pin!(PIN_28, PWM_SLICE6, ChannelAPin);\nimpl_pin!(PIN_29, PWM_SLICE6, ChannelBPin);\n#[cfg(feature = \"rp235xb\")]\nimpl_pin!(PIN_30, PWM_SLICE7, ChannelAPin);\n#[cfg(feature = \"rp235xb\")]\nimpl_pin!(PIN_31, PWM_SLICE7, ChannelBPin);\n#[cfg(feature = \"rp235xb\")]\nimpl_pin!(PIN_32, PWM_SLICE8, ChannelAPin);\n#[cfg(feature = \"rp235xb\")]\nimpl_pin!(PIN_33, PWM_SLICE8, ChannelBPin);\n#[cfg(feature = \"rp235xb\")]\nimpl_pin!(PIN_34, PWM_SLICE9, ChannelAPin);\n#[cfg(feature = \"rp235xb\")]\nimpl_pin!(PIN_35, PWM_SLICE9, ChannelBPin);\n#[cfg(feature = \"rp235xb\")]\nimpl_pin!(PIN_36, PWM_SLICE10, ChannelAPin);\n#[cfg(feature = \"rp235xb\")]\nimpl_pin!(PIN_37, PWM_SLICE10, ChannelBPin);\n#[cfg(feature = \"rp235xb\")]\nimpl_pin!(PIN_38, PWM_SLICE11, ChannelAPin);\n#[cfg(feature = \"rp235xb\")]\nimpl_pin!(PIN_39, PWM_SLICE11, ChannelBPin);\n#[cfg(feature = \"rp235xb\")]\nimpl_pin!(PIN_40, PWM_SLICE8, ChannelAPin);\n#[cfg(feature = \"rp235xb\")]\nimpl_pin!(PIN_41, PWM_SLICE8, ChannelBPin);\n#[cfg(feature = \"rp235xb\")]\nimpl_pin!(PIN_42, PWM_SLICE9, ChannelAPin);\n#[cfg(feature = \"rp235xb\")]\nimpl_pin!(PIN_43, PWM_SLICE9, ChannelBPin);\n#[cfg(feature = \"rp235xb\")]\nimpl_pin!(PIN_44, PWM_SLICE10, ChannelAPin);\n#[cfg(feature = \"rp235xb\")]\nimpl_pin!(PIN_45, PWM_SLICE10, ChannelBPin);\n#[cfg(feature = \"rp235xb\")]\nimpl_pin!(PIN_46, PWM_SLICE11, ChannelAPin);\n#[cfg(feature = \"rp235xb\")]\nimpl_pin!(PIN_47, PWM_SLICE11, ChannelBPin);\n"
  },
  {
    "path": "embassy-rp/src/qmi_cs1.rs",
    "content": "//! QMI CS1 peripheral for RP235x\n//!\n//! This module provides access to the QMI CS1 functionality for use with external memory devices\n//! such as PSRAM. The QMI (Quad SPI) controller supports CS1 as a second chip select signal.\n//!\n//! This peripheral is only available on RP235x chips.\n\n#![cfg(feature = \"_rp235x\")]\n\nuse embassy_hal_internal::{Peri, PeripheralType};\n\nuse crate::gpio::Pin as GpioPin;\nuse crate::{pac, peripherals};\n\n/// QMI CS1 driver.\npub struct QmiCs1<'d> {\n    _inner: Peri<'d, peripherals::QMI_CS1>,\n}\n\nimpl<'d> QmiCs1<'d> {\n    /// Create a new QMI CS1 instance.\n    pub fn new(qmi_cs1: Peri<'d, peripherals::QMI_CS1>, cs1: Peri<'d, impl QmiCs1Pin>) -> Self {\n        // Configure CS1 pin for QMI function (funcsel = 9)\n        cs1.gpio().ctrl().write(|w| w.set_funcsel(9));\n\n        // Configure pad settings for high-speed operation\n        cs1.pad_ctrl().write(|w| {\n            #[cfg(feature = \"_rp235x\")]\n            w.set_iso(false);\n            w.set_ie(true);\n            w.set_drive(pac::pads::vals::Drive::_12M_A);\n            w.set_slewfast(true);\n        });\n\n        Self { _inner: qmi_cs1 }\n    }\n}\n\ntrait SealedInstance {}\n\n/// QMI CS1 instance trait.\n#[allow(private_bounds)]\npub trait Instance: SealedInstance + PeripheralType {}\n\nimpl SealedInstance for peripherals::QMI_CS1 {}\n\nimpl Instance for peripherals::QMI_CS1 {}\n\n/// CS1 pin trait for QMI.\npub trait QmiCs1Pin: GpioPin {}\n\n// Implement pin traits for CS1-capable GPIO pins\nimpl QmiCs1Pin for peripherals::PIN_0 {}\nimpl QmiCs1Pin for peripherals::PIN_8 {}\nimpl QmiCs1Pin for peripherals::PIN_19 {}\n#[cfg(feature = \"rp235xb\")]\nimpl QmiCs1Pin for peripherals::PIN_47 {}\n"
  },
  {
    "path": "embassy-rp/src/relocate.rs",
    "content": "use pio::{Program, SideSet, Wrap};\n\npub struct CodeIterator<'a, I>\nwhere\n    I: Iterator<Item = &'a u16>,\n{\n    iter: I,\n    offset: u8,\n}\n\nimpl<'a, I: Iterator<Item = &'a u16>> CodeIterator<'a, I> {\n    pub fn new(iter: I, offset: u8) -> CodeIterator<'a, I> {\n        CodeIterator { iter, offset }\n    }\n}\n\nimpl<'a, I> Iterator for CodeIterator<'a, I>\nwhere\n    I: Iterator<Item = &'a u16>,\n{\n    type Item = u16;\n    fn next(&mut self) -> Option<Self::Item> {\n        self.iter.next().map(|&instr| {\n            if instr & 0b1110_0000_0000_0000 == 0 {\n                // this is a JMP instruction -> add offset to address\n                let address = (instr & 0b1_1111) as u8;\n                let address = address.wrapping_add(self.offset) % 32;\n                instr & (!0b11111) | address as u16\n            } else {\n                instr\n            }\n        })\n    }\n}\n\npub struct RelocatedProgram<'a, const PROGRAM_SIZE: usize> {\n    program: &'a Program<PROGRAM_SIZE>,\n    origin: u8,\n}\n\nimpl<'a, const PROGRAM_SIZE: usize> RelocatedProgram<'a, PROGRAM_SIZE> {\n    pub fn new_with_origin(program: &Program<PROGRAM_SIZE>, origin: u8) -> RelocatedProgram<'_, PROGRAM_SIZE> {\n        RelocatedProgram { program, origin }\n    }\n\n    pub fn code(&'a self) -> CodeIterator<'a, core::slice::Iter<'a, u16>> {\n        CodeIterator::new(self.program.code.iter(), self.origin)\n    }\n\n    pub fn wrap(&self) -> Wrap {\n        let wrap = self.program.wrap;\n        let origin = self.origin;\n        Wrap {\n            source: wrap.source.wrapping_add(origin) % 32,\n            target: wrap.target.wrapping_add(origin) % 32,\n        }\n    }\n\n    pub fn side_set(&self) -> SideSet {\n        self.program.side_set\n    }\n\n    pub fn origin(&self) -> u8 {\n        self.origin\n    }\n}\n"
  },
  {
    "path": "embassy-rp/src/reset.rs",
    "content": "pub use pac::resets::regs::Peripherals;\n\nuse crate::pac;\n\npub const ALL_PERIPHERALS: Peripherals = Peripherals(0x01ff_ffff);\n\npub(crate) fn reset(peris: Peripherals) {\n    pac::RESETS.reset().write_value(peris);\n}\n\npub(crate) fn unreset_wait(peris: Peripherals) {\n    // TODO use the \"atomic clear\" register version\n    pac::RESETS.reset().modify(|v| *v = Peripherals(v.0 & !peris.0));\n    while ((!pac::RESETS.reset_done().read().0) & peris.0) != 0 {}\n}\n"
  },
  {
    "path": "embassy-rp/src/rom_data/mod.rs",
    "content": "#![cfg_attr(\n    feature = \"rp2040\",\n    doc = r\"\nFunctions and data from the RPI Bootrom.\n\nFrom the [RP2040 datasheet](https://datasheets.raspberrypi.org/rp2040/rp2040-datasheet.pdf), Section 2.8.2.1:\n\n> The Bootrom contains a number of public functions that provide useful\n> RP2040 functionality that might be needed in the absence of any other code\n> on the device, as well as highly optimized versions of certain key\n> functionality that would otherwise have to take up space in most user\n> binaries.\n\"\n)]\n#![cfg_attr(\n    feature = \"_rp235x\",\n    doc = r\"\nFunctions and data from the RPI Bootrom.\n\nFrom [Section 5.4](https://rptl.io/rp2350-datasheet#section_bootrom) of the\nRP2350 datasheet:\n\n> Whilst some ROM space is dedicated to the implementation of the boot\n> sequence and USB/UART boot interfaces, the bootrom also contains public\n> functions that provide useful RP2350 functionality that may be useful for\n> any code or runtime running on the device\n\"\n)]\n\n#[cfg_attr(feature = \"rp2040\", path = \"rp2040.rs\")]\n#[cfg_attr(feature = \"_rp235x\", path = \"rp235x.rs\")]\nmod inner;\npub use inner::*;\n"
  },
  {
    "path": "embassy-rp/src/rom_data/rp2040.rs",
    "content": "//! Functions and data from the RPI Bootrom.\n//!\n//! From the [RP2040 datasheet](https://datasheets.raspberrypi.org/rp2040/rp2040-datasheet.pdf), Section 2.8.2.1:\n//!\n//! > The Bootrom contains a number of public functions that provide useful\n//! > RP2040 functionality that might be needed in the absence of any other code\n//! > on the device, as well as highly optimized versions of certain key\n//! > functionality that would otherwise have to take up space in most user\n//! > binaries.\n\n// Credit: taken from `rp-hal` (also licensed Apache+MIT)\n// https://github.com/rp-rs/rp-hal/blob/main/rp2040-hal/src/rom_data.rs\n\n/// A bootrom function table code.\npub type RomFnTableCode = [u8; 2];\n\n/// This function searches for (table)\ntype RomTableLookupFn<T> = unsafe extern \"C\" fn(*const u16, u32) -> T;\n\n/// The following addresses are described at `2.8.2. Bootrom Contents`\n/// Pointer to the lookup table function supplied by the rom.\nconst ROM_TABLE_LOOKUP_PTR: *const u16 = 0x0000_0018 as _;\n\n/// Pointer to helper functions lookup table.\nconst FUNC_TABLE: *const u16 = 0x0000_0014 as _;\n\n/// Pointer to the public data lookup table.\nconst DATA_TABLE: *const u16 = 0x0000_0016 as _;\n\n/// Address of the version number of the ROM.\nconst VERSION_NUMBER: *const u8 = 0x0000_0013 as _;\n\n/// Retrieve rom content from a table using a code.\nfn rom_table_lookup<T>(table: *const u16, tag: RomFnTableCode) -> T {\n    unsafe {\n        let rom_table_lookup_ptr: *const u32 = rom_hword_as_ptr(ROM_TABLE_LOOKUP_PTR);\n        let rom_table_lookup: RomTableLookupFn<T> = core::mem::transmute(rom_table_lookup_ptr);\n        rom_table_lookup(rom_hword_as_ptr(table) as *const u16, u16::from_le_bytes(tag) as u32)\n    }\n}\n\n/// To save space, the ROM likes to store memory pointers (which are 32-bit on\n/// the Cortex-M0+) using only the bottom 16-bits. The assumption is that the\n/// values they point at live in the first 64 KiB of ROM, and the ROM is mapped\n/// to address `0x0000_0000` and so 16-bits are always sufficient.\n///\n/// This functions grabs a 16-bit value from ROM and expands it out to a full 32-bit pointer.\nunsafe fn rom_hword_as_ptr(rom_address: *const u16) -> *const u32 {\n    let ptr: u16 = *rom_address;\n    ptr as *const u32\n}\n\nmacro_rules! declare_rom_function {\n    (\n        $(#[$outer:meta])*\n        fn $name:ident( $($argname:ident: $ty:ty),* ) -> $ret:ty\n        $lookup:block\n    ) => {\n        declare_rom_function!{\n            __internal ,\n            $(#[$outer])*\n            fn $name( $($argname: $ty),* ) -> $ret\n            $lookup\n        }\n    };\n\n    (\n        $(#[$outer:meta])*\n        unsafe fn $name:ident( $($argname:ident: $ty:ty),* ) -> $ret:ty\n        $lookup:block\n    ) => {\n        declare_rom_function!{\n            __internal unsafe ,\n            $(#[$outer])*\n            fn $name( $($argname: $ty),* ) -> $ret\n            $lookup\n        }\n    };\n\n    (\n        __internal\n        $( $maybe_unsafe:ident )? ,\n        $(#[$outer:meta])*\n        fn $name:ident( $($argname:ident: $ty:ty),* ) -> $ret:ty\n        $lookup:block\n    ) => {\n        #[doc = r\"Additional access for the `\"]\n        #[doc = stringify!($name)]\n        #[doc = r\"` ROM function.\"]\n        pub mod $name {\n            #[cfg(not(feature = \"rom-func-cache\"))]\n            pub(crate) fn outer_call() -> $( $maybe_unsafe )? extern \"C\" fn( $($argname: $ty),* ) -> $ret {\n                let p: *const u32 = $lookup;\n                unsafe {\n                    let func : $( $maybe_unsafe )? extern \"C\" fn( $($argname: $ty),* ) -> $ret\n                        = core::mem::transmute(p);\n                    func\n                }\n            }\n\n            /// Retrieve a function pointer.\n            #[cfg(not(feature = \"rom-func-cache\"))]\n            pub fn ptr() -> $( $maybe_unsafe )? extern \"C\" fn( $($argname: $ty),* ) -> $ret {\n                outer_call()\n            }\n\n            #[cfg(feature = \"rom-func-cache\")]\n            // unlike rp2040-hal we store a full word, containing the full function pointer.\n            // rp2040-hal saves two bytes by storing only the rom offset, at the cost of\n            // having to do an indirection and an atomic operation on every rom call.\n            static mut CACHE: $( $maybe_unsafe )? extern \"C\" fn( $($argname: $ty),* ) -> $ret\n                = trampoline;\n\n            #[cfg(feature = \"rom-func-cache\")]\n            $( $maybe_unsafe )? extern \"C\" fn trampoline( $($argname: $ty),* ) -> $ret {\n                use core::sync::atomic::{compiler_fence, Ordering};\n\n                let p: *const u32 = $lookup;\n                #[allow(unused_unsafe)]\n                unsafe {\n                    CACHE = core::mem::transmute(p);\n                    compiler_fence(Ordering::Release);\n                    CACHE($($argname),*)\n                }\n            }\n\n            #[cfg(feature = \"rom-func-cache\")]\n            pub(crate) fn outer_call() -> $( $maybe_unsafe )? extern \"C\" fn( $($argname: $ty),* ) -> $ret {\n                use core::sync::atomic::{compiler_fence, Ordering};\n\n                // This is safe because the lookup will always resolve\n                // to the same value.  So even if an interrupt or another\n                // core starts at the same time, it just repeats some\n                // work and eventually writes back the correct value.\n                //\n                // We easily get away with using only compiler fences here\n                // because RP2040 SRAM is not cached. If it were we'd need\n                // to make sure updates propagate quickly, or just take the\n                // hit and let each core resolve every function once.\n                compiler_fence(Ordering::Acquire);\n                unsafe {\n                    CACHE\n                }\n            }\n\n            /// Retrieve a function pointer.\n            #[cfg(feature = \"rom-func-cache\")]\n            pub fn ptr() -> $( $maybe_unsafe )? extern \"C\" fn( $($argname: $ty),* ) -> $ret {\n                use core::sync::atomic::{compiler_fence, Ordering};\n\n                // We can't just return the trampoline here because we need\n                // the actual resolved function address (e.x. flash operations\n                // can't reference a trampoline which itself is in flash).  We\n                // can still utilize the cache, but we have to make sure it has\n                // been resolved already.  Like the normal call path, we\n                // don't need anything stronger than fences because the\n                // final value always resolves to the same thing and SRAM\n                // itself is not cached.\n                compiler_fence(Ordering::Acquire);\n                #[allow(unused_unsafe)]\n                unsafe {\n                    // ROM is 16kB in size at 0x0, so anything outside is cached\n                    if CACHE as u32 >> 14 != 0 {\n                        let p: *const u32 = $lookup;\n                        CACHE = core::mem::transmute(p);\n                        compiler_fence(Ordering::Release);\n                    }\n                    CACHE\n                }\n            }\n        }\n\n        $(#[$outer])*\n        pub $( $maybe_unsafe )? extern \"C\" fn $name( $($argname: $ty),* ) -> $ret {\n            $name::outer_call()($($argname),*)\n        }\n    };\n}\n\nmacro_rules! rom_functions {\n    () => {};\n\n    (\n        $(#[$outer:meta])*\n        $c:literal fn $name:ident( $($argname:ident: $ty:ty),* ) -> $ret:ty;\n\n        $($rest:tt)*\n    ) => {\n        declare_rom_function! {\n            $(#[$outer])*\n            fn $name( $($argname: $ty),* ) -> $ret {\n                $crate::rom_data::inner::rom_table_lookup($crate::rom_data::inner::FUNC_TABLE, *$c)\n            }\n        }\n\n        rom_functions!($($rest)*);\n    };\n\n    (\n        $(#[$outer:meta])*\n        $c:literal unsafe fn $name:ident( $($argname:ident: $ty:ty),* ) -> $ret:ty;\n\n        $($rest:tt)*\n    ) => {\n        declare_rom_function! {\n            $(#[$outer])*\n            unsafe fn $name( $($argname: $ty),* ) -> $ret {\n                $crate::rom_data::inner::rom_table_lookup($crate::rom_data::inner::FUNC_TABLE, *$c)\n            }\n        }\n\n        rom_functions!($($rest)*);\n    };\n}\n\nrom_functions! {\n    /// Return a count of the number of 1 bits in value.\n    b\"P3\" fn popcount32(value: u32) -> u32;\n\n    /// Return the bits of value in the reverse order.\n    b\"R3\" fn reverse32(value: u32) -> u32;\n\n    /// Return the number of consecutive high order 0 bits of value. If value is zero, returns 32.\n    b\"L3\" fn clz32(value: u32) -> u32;\n\n    /// Return the number of consecutive low order 0 bits of value. If value is zero, returns 32.\n    b\"T3\" fn ctz32(value: u32) -> u32;\n\n    /// Resets the RP2040 and uses the watchdog facility to re-start in BOOTSEL mode:\n    ///   * gpio_activity_pin_mask is provided to enable an 'activity light' via GPIO attached LED\n    ///     for the USB Mass Storage Device:\n    ///     * 0 No pins are used as per cold boot.\n    ///     * Otherwise a single bit set indicating which GPIO pin should be set to output and\n    ///       raised whenever there is mass storage activity from the host.\n    ///  * disable_interface_mask may be used to control the exposed USB interfaces:\n    ///    * 0 To enable both interfaces (as per cold boot).\n    ///    * 1 To disable the USB Mass Storage Interface.\n    ///    * 2 to Disable the USB PICOBOOT Interface.\n    b\"UB\" fn reset_to_usb_boot(gpio_activity_pin_mask: u32, disable_interface_mask: u32) -> ();\n\n    /// Sets n bytes start at ptr to the value c and returns ptr\n    b\"MS\" unsafe fn memset(ptr: *mut u8, c: u8, n: u32) -> *mut u8;\n\n    /// Sets n bytes start at ptr to the value c and returns ptr.\n    ///\n    /// Note this is a slightly more efficient variant of _memset that may only\n    /// be used if ptr is word aligned.\n    // Note the datasheet does not match the actual ROM for the code here, see\n    // https://github.com/raspberrypi/pico-feedback/issues/217\n    b\"S4\" unsafe fn memset4(ptr: *mut u32, c: u8, n: u32) -> *mut u32;\n\n    /// Copies n bytes starting at src to dest and returns dest. The results are undefined if the\n    /// regions overlap.\n    b\"MC\" unsafe fn memcpy(dest: *mut u8, src: *const u8, n: u32) -> *mut u8;\n\n    /// Copies n bytes starting at src to dest and returns dest. The results are undefined if the\n    /// regions overlap.\n    ///\n    /// Note this is a slightly more efficient variant of _memcpy that may only be\n    /// used if dest and src are word aligned.\n    b\"C4\" unsafe fn memcpy44(dest: *mut u32, src: *const u32, n: u32) -> *mut u8;\n\n    /// Restore all QSPI pad controls to their default state, and connect the SSI to the QSPI pads.\n    b\"IF\" unsafe fn connect_internal_flash() -> ();\n\n    /// First set up the SSI for serial-mode operations, then issue the fixed XIP exit sequence.\n    ///\n    /// Note that the bootrom code uses the IO forcing logic to drive the CS pin, which must be\n    /// cleared before returning the SSI to XIP mode (e.g. by a call to _flash_flush_cache). This\n    /// function configures the SSI with a fixed SCK clock divisor of /6.\n    b\"EX\" unsafe fn flash_exit_xip() -> ();\n\n    /// Erase a count bytes, starting at addr (offset from start of flash). Optionally, pass a\n    /// block erase command e.g. D8h block erase, and the size of the block erased by this\n    /// command — this function will use the larger block erase where possible, for much higher\n    /// erase speed. addr must be aligned to a 4096-byte sector, and count must be a multiple of\n    /// 4096 bytes.\n    b\"RE\" unsafe fn flash_range_erase(addr: u32, count: usize, block_size: u32, block_cmd: u8) -> ();\n\n    /// Program data to a range of flash addresses starting at `addr` (and\n    /// offset from the start of flash) and `count` bytes in size. The value\n    /// `addr` must be aligned to a 256-byte boundary, and `count` must be a\n    /// multiple of 256.\n    b\"RP\" unsafe fn flash_range_program(addr: u32, data: *const u8, count: usize) -> ();\n\n    /// Flush and enable the XIP cache. Also clears the IO forcing on QSPI CSn, so that the SSI can\n    /// drive the flashchip select as normal.\n    b\"FC\" unsafe fn flash_flush_cache() -> ();\n\n    /// Configure the SSI to generate a standard 03h serial read command, with 24 address bits,\n    /// upon each XIP access. This is a very slow XIP configuration, but is very widely supported.\n    /// The debugger calls this function after performing a flash erase/programming operation, so\n    /// that the freshly-programmed code and data is visible to the debug host, without having to\n    /// know exactly what kind of flash device is connected.\n    b\"CX\" unsafe fn flash_enter_cmd_xip() -> ();\n\n    /// This is the method that is entered by core 1 on reset to wait to be launched by core 0.\n    /// There are few cases where you should call this method (resetting core 1 is much better).\n    /// This method does not return and should only ever be called on core 1.\n    b\"WV\" unsafe fn wait_for_vector() -> !;\n}\n\n// Various C intrinsics in the ROM\nintrinsics! {\n    #[alias = __popcountdi2]\n    extern \"C\" fn __popcountsi2(x: u32) -> u32 {\n        popcount32(x)\n    }\n\n    #[alias = __clzdi2]\n    extern \"C\" fn __clzsi2(x: u32) -> u32 {\n        clz32(x)\n    }\n\n    #[alias = __ctzdi2]\n    extern \"C\" fn __ctzsi2(x: u32) -> u32 {\n        ctz32(x)\n    }\n\n    // __rbit is only unofficial, but it show up in the ARM documentation,\n    // so may as well hook it up.\n    #[alias = __rbitl]\n    extern \"C\" fn __rbit(x: u32) -> u32 {\n        reverse32(x)\n    }\n\n    unsafe extern \"aapcs\" fn __aeabi_memset(dest: *mut u8, n: usize, c: i32) -> () {\n        // Different argument order\n        memset(dest, c as u8, n as u32);\n    }\n\n    #[alias = __aeabi_memset8]\n    unsafe extern \"aapcs\" fn __aeabi_memset4(dest: *mut u8, n: usize, c: i32) -> () {\n        // Different argument order\n        memset4(dest as *mut u32, c as u8, n as u32);\n    }\n\n    unsafe extern \"aapcs\" fn __aeabi_memclr(dest: *mut u8, n: usize) -> () {\n        memset(dest, 0, n as u32);\n    }\n\n    #[alias = __aeabi_memclr8]\n    unsafe extern \"aapcs\" fn __aeabi_memclr4(dest: *mut u8, n: usize) -> () {\n        memset4(dest as *mut u32, 0, n as u32);\n    }\n\n    unsafe extern \"aapcs\" fn __aeabi_memcpy(dest: *mut u8, src: *const u8, n: usize) -> () {\n        memcpy(dest, src, n as u32);\n    }\n\n    #[alias = __aeabi_memcpy8]\n    unsafe extern \"aapcs\" fn __aeabi_memcpy4(dest: *mut u8, src: *const u8, n: usize) -> () {\n        memcpy44(dest as *mut u32, src as *const u32, n as u32);\n    }\n}\n\nunsafe fn convert_str(s: *const u8) -> &'static str {\n    let mut end = s;\n    while *end != 0 {\n        end = end.add(1);\n    }\n    let s = core::slice::from_raw_parts(s, end.offset_from(s) as usize);\n    core::str::from_utf8_unchecked(s)\n}\n\n/// The version number of the rom.\npub fn rom_version_number() -> u8 {\n    unsafe { *VERSION_NUMBER }\n}\n\n/// The Raspberry Pi Trading Ltd copyright string.\npub fn copyright_string() -> &'static str {\n    let s: *const u8 = rom_table_lookup(DATA_TABLE, *b\"CR\");\n    unsafe { convert_str(s) }\n}\n\n/// The 8 most significant hex digits of the Bootrom git revision.\npub fn git_revision() -> u32 {\n    let s: *const u32 = rom_table_lookup(DATA_TABLE, *b\"GR\");\n    unsafe { *s }\n}\n\n/// The start address of the floating point library code and data.\n///\n/// This and fplib_end along with the individual function pointers in\n/// soft_float_table can be used to copy the floating point implementation into\n/// RAM if desired.\npub fn fplib_start() -> *const u8 {\n    rom_table_lookup(DATA_TABLE, *b\"FS\")\n}\n\n/// See Table 180 in the RP2040 datasheet for the contents of this table.\n#[cfg_attr(feature = \"rom-func-cache\", inline(never))]\npub fn soft_float_table() -> *const usize {\n    rom_table_lookup(DATA_TABLE, *b\"SF\")\n}\n\n/// The end address of the floating point library code and data.\npub fn fplib_end() -> *const u8 {\n    rom_table_lookup(DATA_TABLE, *b\"FE\")\n}\n\n/// This entry is only present in the V2 bootrom. See Table 182 in the RP2040 datasheet for the contents of this table.\n#[cfg_attr(feature = \"rom-func-cache\", inline(never))]\npub fn soft_double_table() -> *const usize {\n    if rom_version_number() < 2 {\n        panic!(\n            \"Double precision operations require V2 bootrom (found: V{})\",\n            rom_version_number()\n        );\n    }\n    rom_table_lookup(DATA_TABLE, *b\"SD\")\n}\n\n/// ROM functions using single-precision arithmetic (i.e. 'f32' in Rust terms)\npub mod float_funcs {\n\n    macro_rules! make_functions {\n        (\n            $(\n                $(#[$outer:meta])*\n                $offset:literal $name:ident (\n                    $( $aname:ident : $aty:ty ),*\n                ) -> $ret:ty;\n            )*\n        ) => {\n            $(\n                declare_rom_function! {\n                    $(#[$outer])*\n                    fn $name( $( $aname : $aty ),* ) -> $ret {\n                        let table: *const usize = $crate::rom_data::soft_float_table();\n                        unsafe {\n                            // This is the entry in the table. Our offset is given as a\n                            // byte offset, but we want the table index (each pointer in\n                            // the table is 4 bytes long)\n                            let entry: *const usize = table.offset($offset / 4);\n                            // Read the pointer from the table\n                            core::ptr::read(entry) as *const u32\n                        }\n                    }\n                }\n            )*\n        }\n    }\n\n    make_functions! {\n        /// Calculates `a + b`\n        0x00 fadd(a: f32, b: f32) -> f32;\n        /// Calculates `a - b`\n        0x04 fsub(a: f32, b: f32) -> f32;\n        /// Calculates `a * b`\n        0x08 fmul(a: f32, b: f32) -> f32;\n        /// Calculates `a / b`\n        0x0c fdiv(a: f32, b: f32) -> f32;\n\n        // 0x10 and 0x14 are deprecated\n\n        /// Calculates `sqrt(v)` (or return -Infinity if v is negative)\n        0x18 fsqrt(v: f32) -> f32;\n        /// Converts an f32 to a signed integer,\n        /// rounding towards -Infinity, and clamping the result to lie within the\n        /// range `-0x80000000` to `0x7FFFFFFF`\n        0x1c float_to_int(v: f32) -> i32;\n        /// Converts an f32 to an signed fixed point\n        /// integer representation where n specifies the position of the binary\n        /// point in the resulting fixed point representation, e.g.\n        /// `f(0.5f, 16) == 0x8000`. This method rounds towards -Infinity,\n        /// and clamps the resulting integer to lie within the range `0x00000000` to\n        /// `0xFFFFFFFF`\n        0x20 float_to_fix(v: f32, n: i32) -> i32;\n        /// Converts an f32 to an unsigned integer,\n        /// rounding towards -Infinity, and clamping the result to lie within the\n        /// range `0x00000000` to `0xFFFFFFFF`\n        0x24 float_to_uint(v: f32) -> u32;\n        /// Converts an f32 to an unsigned fixed point\n        /// integer representation where n specifies the position of the binary\n        /// point in the resulting fixed point representation, e.g.\n        /// `f(0.5f, 16) == 0x8000`. This method rounds towards -Infinity,\n        /// and clamps the resulting integer to lie within the range `0x00000000` to\n        /// `0xFFFFFFFF`\n        0x28 float_to_ufix(v: f32, n: i32) -> u32;\n        /// Converts a signed integer to the nearest\n        /// f32 value, rounding to even on tie\n        0x2c int_to_float(v: i32) -> f32;\n        /// Converts a signed fixed point integer\n        /// representation to the nearest f32 value, rounding to even on tie. `n`\n        /// specifies the position of the binary point in fixed point, so `f =\n        /// nearest(v/(2^n))`\n        0x30 fix_to_float(v: i32, n: i32) -> f32;\n        /// Converts an unsigned integer to the nearest\n        /// f32 value, rounding to even on tie\n        0x34 uint_to_float(v: u32) -> f32;\n        /// Converts an unsigned fixed point integer\n        /// representation to the nearest f32 value, rounding to even on tie. `n`\n        /// specifies the position of the binary point in fixed point, so `f =\n        /// nearest(v/(2^n))`\n        0x38 ufix_to_float(v: u32, n: i32) -> f32;\n        /// Calculates the cosine of `angle`. The value\n        /// of `angle` is in radians, and must be in the range `-1024` to `1024`\n        0x3c fcos(angle: f32) -> f32;\n        /// Calculates the sine of `angle`. The value of\n        /// `angle` is in radians, and must be in the range `-1024` to `1024`\n        0x40 fsin(angle: f32) -> f32;\n        /// Calculates the tangent of `angle`. The value\n        /// of `angle` is in radians, and must be in the range `-1024` to `1024`\n        0x44 ftan(angle: f32) -> f32;\n\n        // 0x48 is deprecated\n\n        /// Calculates the exponential value of `v`,\n        /// i.e. `e ** v`\n        0x4c fexp(v: f32) -> f32;\n        /// Calculates the natural logarithm of `v`. If `v <= 0` return -Infinity\n        0x50 fln(v: f32) -> f32;\n    }\n\n    macro_rules! make_functions_v2 {\n        (\n            $(\n                $(#[$outer:meta])*\n                $offset:literal $name:ident (\n                    $( $aname:ident : $aty:ty ),*\n                ) -> $ret:ty;\n            )*\n        ) => {\n            $(\n                declare_rom_function! {\n                    $(#[$outer])*\n                    fn $name( $( $aname : $aty ),* ) -> $ret {\n                        if $crate::rom_data::rom_version_number() < 2 {\n                            panic!(\n                                \"Floating point function requires V2 bootrom (found: V{})\",\n                                $crate::rom_data::rom_version_number()\n                            );\n                        }\n                        let table: *const usize = $crate::rom_data::soft_float_table();\n                        unsafe {\n                            // This is the entry in the table. Our offset is given as a\n                            // byte offset, but we want the table index (each pointer in\n                            // the table is 4 bytes long)\n                            let entry: *const usize = table.offset($offset / 4);\n                            // Read the pointer from the table\n                            core::ptr::read(entry) as *const u32\n                        }\n                    }\n                }\n            )*\n        }\n    }\n\n    // These are only on BootROM v2 or higher\n    make_functions_v2! {\n        /// Compares two floating point numbers, returning:\n        ///     • 0 if a == b\n        ///     • -1 if a < b\n        ///     • 1 if a > b\n        0x54 fcmp(a: f32, b: f32) -> i32;\n        /// Computes the arc tangent of `y/x` using the\n        /// signs of arguments to determine the correct quadrant\n        0x58 fatan2(y: f32, x: f32) -> f32;\n        /// Converts a signed 64-bit integer to the\n        /// nearest f32 value, rounding to even on tie\n        0x5c int64_to_float(v: i64) -> f32;\n        /// Converts a signed fixed point 64-bit integer\n        /// representation to the nearest f32 value, rounding to even on tie. `n`\n        /// specifies the position of the binary point in fixed point, so `f =\n        /// nearest(v/(2^n))`\n        0x60 fix64_to_float(v: i64, n: i32) -> f32;\n        /// Converts an unsigned 64-bit integer to the\n        /// nearest f32 value, rounding to even on tie\n        0x64 uint64_to_float(v: u64) -> f32;\n        /// Converts an unsigned fixed point 64-bit\n        /// integer representation to the nearest f32 value, rounding to even on\n        /// tie. `n` specifies the position of the binary point in fixed point, so\n        /// `f = nearest(v/(2^n))`\n        0x68 ufix64_to_float(v: u64, n: i32) -> f32;\n        /// Convert an f32 to a signed 64-bit integer, rounding towards -Infinity,\n        /// and clamping the result to lie within the range `-0x8000000000000000` to\n        /// `0x7FFFFFFFFFFFFFFF`\n        0x6c float_to_int64(v: f32) -> i64;\n        /// Converts an f32 to a signed fixed point\n        /// 64-bit integer representation where n specifies the position of the\n        /// binary point in the resulting fixed point representation - e.g. `f(0.5f,\n        /// 16) == 0x8000`. This method rounds towards -Infinity, and clamps the\n        /// resulting integer to lie within the range `-0x8000000000000000` to\n        /// `0x7FFFFFFFFFFFFFFF`\n        0x70 float_to_fix64(v: f32, n: i32) -> f32;\n        /// Converts an f32 to an unsigned 64-bit\n        /// integer, rounding towards -Infinity, and clamping the result to lie\n        /// within the range `0x0000000000000000` to `0xFFFFFFFFFFFFFFFF`\n        0x74 float_to_uint64(v: f32) -> u64;\n        /// Converts an f32 to an unsigned fixed point\n        /// 64-bit integer representation where n specifies the position of the\n        /// binary point in the resulting fixed point representation, e.g. `f(0.5f,\n        /// 16) == 0x8000`. This method rounds towards -Infinity, and clamps the\n        /// resulting integer to lie within the range `0x0000000000000000` to\n        /// `0xFFFFFFFFFFFFFFFF`\n        0x78 float_to_ufix64(v: f32, n: i32) -> u64;\n        /// Converts an f32 to an f64.\n        0x7c float_to_double(v: f32) -> f64;\n    }\n}\n\n/// Functions using double-precision arithmetic (i.e. 'f64' in Rust terms)\npub mod double_funcs {\n\n    macro_rules! make_double_funcs {\n        (\n            $(\n                $(#[$outer:meta])*\n                $offset:literal $name:ident (\n                    $( $aname:ident : $aty:ty ),*\n                ) -> $ret:ty;\n            )*\n        ) => {\n            $(\n                declare_rom_function! {\n                    $(#[$outer])*\n                    fn $name( $( $aname : $aty ),* ) -> $ret {\n                        let table: *const usize = $crate::rom_data::soft_double_table();\n                        unsafe {\n                            // This is the entry in the table. Our offset is given as a\n                            // byte offset, but we want the table index (each pointer in\n                            // the table is 4 bytes long)\n                            let entry: *const usize = table.offset($offset / 4);\n                            // Read the pointer from the table\n                            core::ptr::read(entry) as *const u32\n                        }\n                    }\n                }\n            )*\n        }\n    }\n\n    make_double_funcs! {\n        /// Calculates `a + b`\n        0x00 dadd(a: f64, b: f64) -> f64;\n        /// Calculates `a - b`\n        0x04 dsub(a: f64, b: f64) -> f64;\n        /// Calculates `a * b`\n        0x08 dmul(a: f64, b: f64) -> f64;\n        /// Calculates `a / b`\n        0x0c ddiv(a: f64, b: f64) -> f64;\n\n        // 0x10 and 0x14 are deprecated\n\n        /// Calculates `sqrt(v)` (or return -Infinity if v is negative)\n        0x18 dsqrt(v: f64) -> f64;\n        /// Converts an f64 to a signed integer,\n        /// rounding towards -Infinity, and clamping the result to lie within the\n        /// range `-0x80000000` to `0x7FFFFFFF`\n        0x1c double_to_int(v: f64) -> i32;\n        /// Converts an f64 to an signed fixed point\n        /// integer representation where n specifies the position of the binary\n        /// point in the resulting fixed point representation, e.g.\n        /// `f(0.5f, 16) == 0x8000`. This method rounds towards -Infinity,\n        /// and clamps the resulting integer to lie within the range `0x00000000` to\n        /// `0xFFFFFFFF`\n        0x20 double_to_fix(v: f64, n: i32) -> i32;\n        /// Converts an f64 to an unsigned integer,\n        /// rounding towards -Infinity, and clamping the result to lie within the\n        /// range `0x00000000` to `0xFFFFFFFF`\n        0x24 double_to_uint(v: f64) -> u32;\n        /// Converts an f64 to an unsigned fixed point\n        /// integer representation where n specifies the position of the binary\n        /// point in the resulting fixed point representation, e.g.\n        /// `f(0.5f, 16) == 0x8000`. This method rounds towards -Infinity,\n        /// and clamps the resulting integer to lie within the range `0x00000000` to\n        /// `0xFFFFFFFF`\n        0x28 double_to_ufix(v: f64, n: i32) -> u32;\n        /// Converts a signed integer to the nearest\n        /// double value, rounding to even on tie\n        0x2c int_to_double(v: i32) -> f64;\n        /// Converts a signed fixed point integer\n        /// representation to the nearest double value, rounding to even on tie. `n`\n        /// specifies the position of the binary point in fixed point, so `f =\n        /// nearest(v/(2^n))`\n        0x30 fix_to_double(v: i32, n: i32) -> f64;\n        /// Converts an unsigned integer to the nearest\n        /// double value, rounding to even on tie\n        0x34 uint_to_double(v: u32) -> f64;\n        /// Converts an unsigned fixed point integer\n        /// representation to the nearest double value, rounding to even on tie. `n`\n        /// specifies the position of the binary point in fixed point, so f =\n        /// nearest(v/(2^n))\n        0x38 ufix_to_double(v: u32, n: i32) -> f64;\n        /// Calculates the cosine of `angle`. The value\n        /// of `angle` is in radians, and must be in the range `-1024` to `1024`\n        0x3c dcos(angle: f64) -> f64;\n        /// Calculates the sine of `angle`. The value of\n        /// `angle` is in radians, and must be in the range `-1024` to `1024`\n        0x40 dsin(angle: f64) -> f64;\n        /// Calculates the tangent of `angle`. The value\n        /// of `angle` is in radians, and must be in the range `-1024` to `1024`\n        0x44 dtan(angle: f64) -> f64;\n\n        // 0x48 is deprecated\n\n        /// Calculates the exponential value of `v`,\n        /// i.e. `e ** v`\n        0x4c dexp(v: f64) -> f64;\n        /// Calculates the natural logarithm of v. If v <= 0 return -Infinity\n        0x50 dln(v: f64) -> f64;\n\n        // These are only on BootROM v2 or higher\n\n        /// Compares two floating point numbers, returning:\n        ///     • 0 if a == b\n        ///     • -1 if a < b\n        ///     • 1 if a > b\n        0x54 dcmp(a: f64, b: f64) -> i32;\n        /// Computes the arc tangent of `y/x` using the\n        /// signs of arguments to determine the correct quadrant\n        0x58 datan2(y: f64, x: f64) -> f64;\n        /// Converts a signed 64-bit integer to the\n        /// nearest double value, rounding to even on tie\n        0x5c int64_to_double(v: i64) -> f64;\n        /// Converts a signed fixed point 64-bit integer\n        /// representation to the nearest double value, rounding to even on tie. `n`\n        /// specifies the position of the binary point in fixed point, so `f =\n        /// nearest(v/(2^n))`\n        0x60 fix64_to_doubl(v: i64, n: i32) -> f64;\n        /// Converts an unsigned 64-bit integer to the\n        /// nearest double value, rounding to even on tie\n        0x64 uint64_to_double(v: u64) -> f64;\n        /// Converts an unsigned fixed point 64-bit\n        /// integer representation to the nearest double value, rounding to even on\n        /// tie. `n` specifies the position of the binary point in fixed point, so\n        /// `f = nearest(v/(2^n))`\n        0x68 ufix64_to_double(v: u64, n: i32) -> f64;\n        /// Convert an f64 to a signed 64-bit integer, rounding towards -Infinity,\n        /// and clamping the result to lie within the range `-0x8000000000000000` to\n        /// `0x7FFFFFFFFFFFFFFF`\n        0x6c double_to_int64(v: f64) -> i64;\n        /// Converts an f64 to a signed fixed point\n        /// 64-bit integer representation where n specifies the position of the\n        /// binary point in the resulting fixed point representation - e.g. `f(0.5f,\n        /// 16) == 0x8000`. This method rounds towards -Infinity, and clamps the\n        /// resulting integer to lie within the range `-0x8000000000000000` to\n        /// `0x7FFFFFFFFFFFFFFF`\n        0x70 double_to_fix64(v: f64, n: i32) -> i64;\n        /// Converts an f64 to an unsigned 64-bit\n        /// integer, rounding towards -Infinity, and clamping the result to lie\n        /// within the range `0x0000000000000000` to `0xFFFFFFFFFFFFFFFF`\n        0x74 double_to_uint64(v: f64) -> u64;\n        /// Converts an f64 to an unsigned fixed point\n        /// 64-bit integer representation where n specifies the position of the\n        /// binary point in the resulting fixed point representation, e.g. `f(0.5f,\n        /// 16) == 0x8000`. This method rounds towards -Infinity, and clamps the\n        /// resulting integer to lie within the range `0x0000000000000000` to\n        /// `0xFFFFFFFFFFFFFFFF`\n        0x78 double_to_ufix64(v: f64, n: i32) -> u64;\n        /// Converts an f64 to an f32\n        0x7c double_to_float(v: f64) -> f32;\n    }\n}\n"
  },
  {
    "path": "embassy-rp/src/rom_data/rp235x.rs",
    "content": "//! Functions and data from the RPI Bootrom.\n//!\n//! From [Section 5.4](https://rptl.io/rp2350-datasheet#section_bootrom) of the\n//! RP2350 datasheet:\n//!\n//! > Whilst some ROM space is dedicated to the implementation of the boot\n//! > sequence and USB/UART boot interfaces, the bootrom also contains public\n//! > functions that provide useful RP2350 functionality that may be useful for\n//! > any code or runtime running on the device\n\n// Credit: taken from `rp-hal` (also licensed Apache+MIT)\n// https://github.com/rp-rs/rp-hal/blob/main/rp235x-hal/src/rom_data.rs\n\n/// A bootrom function table code.\npub type RomFnTableCode = [u8; 2];\n\n/// This function searches for the tag which matches the mask.\ntype RomTableLookupFn = unsafe extern \"C\" fn(code: u32, mask: u32) -> usize;\n\n/// Pointer to the value lookup function supplied by the ROM.\n///\n/// This address is described at `5.5.1. Locating the API Functions`\n#[cfg(all(target_arch = \"arm\", target_os = \"none\"))]\nconst ROM_TABLE_LOOKUP_A2: *const u16 = 0x0000_0016 as _;\n\n/// Pointer to the value lookup function supplied by the ROM.\n///\n/// This address is described at `5.5.1. Locating the API Functions`\n#[cfg(all(target_arch = \"arm\", target_os = \"none\"))]\nconst ROM_TABLE_LOOKUP_A1: *const u32 = 0x0000_0018 as _;\n\n/// Pointer to the data lookup function supplied by the ROM.\n///\n/// On Arm, the same function is used to look up code and data.\n#[cfg(all(target_arch = \"arm\", target_os = \"none\"))]\nconst ROM_DATA_LOOKUP_A2: *const u16 = ROM_TABLE_LOOKUP_A2;\n\n/// Pointer to the data lookup function supplied by the ROM.\n///\n/// On Arm, the same function is used to look up code and data.\n#[cfg(all(target_arch = \"arm\", target_os = \"none\"))]\nconst ROM_DATA_LOOKUP_A1: *const u32 = ROM_TABLE_LOOKUP_A1;\n\n/// Pointer to the value lookup function supplied by the ROM.\n///\n/// This address is described at `5.5.1. Locating the API Functions`\n#[cfg(not(all(target_arch = \"arm\", target_os = \"none\")))]\nconst ROM_TABLE_LOOKUP_A2: *const u16 = 0x0000_7DFA as _;\n\n/// Pointer to the value lookup function supplied by the ROM.\n///\n/// This address is described at `5.5.1. Locating the API Functions`\n#[cfg(not(all(target_arch = \"arm\", target_os = \"none\")))]\nconst ROM_TABLE_LOOKUP_A1: *const u32 = 0x0000_7DF8 as _;\n\n/// Pointer to the data lookup function supplied by the ROM.\n///\n/// On RISC-V, a different function is used to look up data.\n#[cfg(not(all(target_arch = \"arm\", target_os = \"none\")))]\nconst ROM_DATA_LOOKUP_A2: *const u16 = 0x0000_7DF8 as _;\n\n/// Pointer to the data lookup function supplied by the ROM.\n///\n/// On RISC-V, a different function is used to look up data.\n#[cfg(not(all(target_arch = \"arm\", target_os = \"none\")))]\nconst ROM_DATA_LOOKUP_A1: *const u32 = 0x0000_7DF4 as _;\n\n/// Address of the version number of the ROM.\nconst VERSION_NUMBER: *const u8 = 0x0000_0013 as _;\n\n#[allow(unused)]\nmod rt_flags {\n    pub const FUNC_RISCV: u32 = 0x0001;\n    pub const FUNC_RISCV_FAR: u32 = 0x0003;\n    pub const FUNC_ARM_SEC: u32 = 0x0004;\n    // reserved for 32-bit pointer: 0x0008\n    pub const FUNC_ARM_NONSEC: u32 = 0x0010;\n    // reserved for 32-bit pointer: 0x0020\n    pub const DATA: u32 = 0x0040;\n    // reserved for 32-bit pointer: 0x0080\n    #[cfg(all(target_arch = \"arm\", target_os = \"none\"))]\n    pub const FUNC_ARM_SEC_RISCV: u32 = FUNC_ARM_SEC;\n    #[cfg(not(all(target_arch = \"arm\", target_os = \"none\")))]\n    pub const FUNC_ARM_SEC_RISCV: u32 = FUNC_RISCV;\n}\n\n/// Retrieve rom content from a table using a code.\npub fn rom_table_lookup(tag: RomFnTableCode, mask: u32) -> usize {\n    let tag = u16::from_le_bytes(tag) as u32;\n    unsafe {\n        let lookup_func = if rom_version_number() == 1 {\n            ROM_TABLE_LOOKUP_A1.read() as usize\n        } else {\n            ROM_TABLE_LOOKUP_A2.read() as usize\n        };\n        let lookup_func: RomTableLookupFn = core::mem::transmute(lookup_func);\n        lookup_func(tag, mask)\n    }\n}\n\n/// Retrieve rom data content from a table using a code.\npub fn rom_data_lookup(tag: RomFnTableCode, mask: u32) -> usize {\n    let tag = u16::from_le_bytes(tag) as u32;\n    unsafe {\n        let lookup_func = if rom_version_number() == 1 {\n            ROM_DATA_LOOKUP_A1.read() as usize\n        } else {\n            ROM_DATA_LOOKUP_A2.read() as usize\n        };\n        let lookup_func: RomTableLookupFn = core::mem::transmute(lookup_func);\n        lookup_func(tag, mask)\n    }\n}\n\nmacro_rules! declare_rom_function {\n    (\n        $(#[$outer:meta])*\n        fn $name:ident( $($argname:ident: $ty:ty),* ) -> $ret:ty\n        $lookup:block\n    ) => {\n        #[doc = r\"Additional access for the `\"]\n        #[doc = stringify!($name)]\n        #[doc = r\"` ROM function.\"]\n        pub mod $name {\n            /// Retrieve a function pointer.\n            #[cfg(not(feature = \"rom-func-cache\"))]\n            pub fn ptr() -> extern \"C\" fn( $($argname: $ty),* ) -> $ret {\n                let p: usize = $lookup;\n                unsafe {\n                    let func : extern \"C\" fn( $($argname: $ty),* ) -> $ret = core::mem::transmute(p);\n                    func\n                }\n            }\n\n            /// Retrieve a function pointer.\n            #[cfg(feature = \"rom-func-cache\")]\n            pub fn ptr() -> extern \"C\" fn( $($argname: $ty),* ) -> $ret {\n                use core::sync::atomic::{AtomicU16, Ordering};\n\n                // All pointers in the ROM fit in 16 bits, so we don't need a\n                // full width word to store the cached value.\n                static CACHED_PTR: AtomicU16 = AtomicU16::new(0);\n                // This is safe because the lookup will always resolve\n                // to the same value.  So even if an interrupt or another\n                // core starts at the same time, it just repeats some\n                // work and eventually writes back the correct value.\n                let p: usize = match CACHED_PTR.load(Ordering::Relaxed) {\n                    0 => {\n                        let raw: usize = $lookup;\n                        CACHED_PTR.store(raw as u16, Ordering::Relaxed);\n                        raw\n                    },\n                    val => val as usize,\n                };\n                unsafe {\n                    let func : extern \"C\" fn( $($argname: $ty),* ) -> $ret = core::mem::transmute(p);\n                    func\n                }\n            }\n        }\n\n        $(#[$outer])*\n        pub extern \"C\" fn $name( $($argname: $ty),* ) -> $ret {\n            $name::ptr()($($argname),*)\n        }\n    };\n\n    (\n        $(#[$outer:meta])*\n        unsafe fn $name:ident( $($argname:ident: $ty:ty),* ) -> $ret:ty\n        $lookup:block\n    ) => {\n        #[doc = r\"Additional access for the `\"]\n        #[doc = stringify!($name)]\n        #[doc = r\"` ROM function.\"]\n        pub mod $name {\n            /// Retrieve a function pointer.\n            #[cfg(not(feature = \"rom-func-cache\"))]\n            pub fn ptr() -> unsafe extern \"C\" fn( $($argname: $ty),* ) -> $ret {\n                let p: usize = $lookup;\n                unsafe {\n                    let func : unsafe extern \"C\" fn( $($argname: $ty),* ) -> $ret = core::mem::transmute(p);\n                    func\n                }\n            }\n\n            /// Retrieve a function pointer.\n            #[cfg(feature = \"rom-func-cache\")]\n            pub fn ptr() -> unsafe extern \"C\" fn( $($argname: $ty),* ) -> $ret {\n                use core::sync::atomic::{AtomicU16, Ordering};\n\n                // All pointers in the ROM fit in 16 bits, so we don't need a\n                // full width word to store the cached value.\n                static CACHED_PTR: AtomicU16 = AtomicU16::new(0);\n                // This is safe because the lookup will always resolve\n                // to the same value.  So even if an interrupt or another\n                // core starts at the same time, it just repeats some\n                // work and eventually writes back the correct value.\n                let p: usize = match CACHED_PTR.load(Ordering::Relaxed) {\n                    0 => {\n                        let raw: usize = $lookup;\n                        CACHED_PTR.store(raw as u16, Ordering::Relaxed);\n                        raw\n                    },\n                    val => val as usize,\n                };\n                unsafe {\n                    let func : unsafe extern \"C\" fn( $($argname: $ty),* ) -> $ret = core::mem::transmute(p);\n                    func\n                }\n            }\n        }\n\n        $(#[$outer])*\n        /// # Safety\n        ///\n        /// This is a low-level C function. It may be difficult to call safely from\n        /// Rust. If in doubt, check the rp235x datasheet for details and do your own\n        /// safety evaluation.\n        pub unsafe extern \"C\" fn $name( $($argname: $ty),* ) -> $ret {\n            $name::ptr()($($argname),*)\n        }\n    };\n}\n\n// **************** 5.5.7 Low-level Flash Commands ****************\n\ndeclare_rom_function! {\n    /// Restore all QSPI pad controls to their default state, and connect the\n    /// QMI peripheral to the QSPI pads.\n    ///\n    /// Supported architectures: ARM-S, RISC-V\n    unsafe fn connect_internal_flash() -> () {\n        crate::rom_data::rom_table_lookup(*b\"IF\", crate::rom_data::inner::rt_flags::FUNC_ARM_SEC_RISCV)\n    }\n}\n\ndeclare_rom_function! {\n    /// Initialise the QMI for serial operations (direct mode)\n    ///\n    /// Also initialise a basic XIP mode, where the QMI will perform 03h serial\n    /// read commands at low speed (CLKDIV=12) in response to XIP reads.\n    ///\n    /// Then, issue a sequence to the QSPI device on chip select 0, designed to\n    /// return it from continuous read mode (\"XIP mode\") and/or QPI mode to a\n    /// state where it will accept serial commands. This is necessary after\n    /// system reset to restore the QSPI device to a known state, because\n    /// resetting RP2350 does not reset attached QSPI devices. It is also\n    /// necessary when user code, having already performed some\n    /// continuous-read-mode or QPI-mode accesses, wishes to return the QSPI\n    /// device to a state where it will accept the serial erase and programming\n    /// commands issued by the bootrom’s flash access functions.\n    ///\n    /// If a GPIO for the secondary chip select is configured via FLASH_DEVINFO,\n    /// then the XIP exit sequence is also issued to chip select 1.\n    ///\n    /// The QSPI device should be accessible for XIP reads after calling this\n    /// function; the name flash_exit_xip refers to returning the QSPI device\n    /// from its XIP state to a serial command state.\n    ///\n    /// Supported architectures: ARM-S, RISC-V\n    unsafe fn flash_exit_xip() -> () {\n        crate::rom_data::rom_table_lookup(*b\"EX\", crate::rom_data::inner::rt_flags::FUNC_ARM_SEC_RISCV)\n    }\n}\n\ndeclare_rom_function! {\n    /// Erase count bytes, starting at addr (offset from start of flash).\n    ///\n    /// Optionally, pass a block erase command e.g. D8h block erase, and the\n    /// size of the block erased by this command — this function will use the\n    /// larger block erase where possible, for much higher erase speed. addr\n    /// must be aligned to a 4096-byte sector, and count must be a multiple of\n    /// 4096 bytes.\n    ///\n    /// This is a low-level flash API, and no validation of the arguments is\n    /// performed. See flash_op() for a higher-level API which checks alignment,\n    /// flash bounds and partition permissions, and can transparently apply a\n    /// runtime-to-storage address translation.\n    ///\n    /// The QSPI device must be in a serial command state before calling this\n    /// API, which can be achieved by calling connect_internal_flash() followed\n    /// by flash_exit_xip(). After the erase, the flash cache should be flushed\n    /// via flash_flush_cache() to ensure the modified flash data is visible to\n    /// cached XIP accesses.\n    ///\n    /// Finally, the original XIP mode should be restored by copying the saved\n    /// XIP setup function from bootram into SRAM, and executing it: the bootrom\n    /// provides a default function which restores the flash mode/clkdiv\n    /// discovered during flash scanning, and user programs can override this\n    /// with their own XIP setup function.\n    ///\n    /// For the duration of the erase operation, QMI is in direct mode (Section\n    /// 12.14.5) and attempting to access XIP from DMA, the debugger or the\n    /// other core will return a bus fault. XIP becomes accessible again once\n    /// the function returns.\n    ///\n    /// Supported architectures: ARM-S, RISC-V\n    unsafe fn flash_range_erase(addr: u32, count: usize, block_size: u32, block_cmd: u8) -> () {\n        crate::rom_data::rom_table_lookup(*b\"RE\", crate::rom_data::inner::rt_flags::FUNC_ARM_SEC_RISCV)\n    }\n}\n\ndeclare_rom_function! {\n    /// Program data to a range of flash storage addresses starting at addr\n    /// (offset from the start of flash) and count bytes in size.\n    ///\n    /// `addr` must be aligned to a 256-byte boundary, and count must be a\n    /// multiple of 256.\n    ///\n    /// This is a low-level flash API, and no validation of the arguments is\n    /// performed. See flash_op() for a higher-level API which checks alignment,\n    /// flash bounds and partition permissions, and can transparently apply a\n    /// runtime-to-storage address translation.\n    ///\n    /// The QSPI device must be in a serial command state before calling this\n    /// API — see notes on flash_range_erase().\n    ///\n    /// Supported architectures: ARM-S, RISC-V\n    unsafe fn flash_range_program(addr: u32, data: *const u8, count: usize) -> () {\n        crate::rom_data::rom_table_lookup(*b\"RP\", crate::rom_data::inner::rt_flags::FUNC_ARM_SEC_RISCV)\n    }\n}\n\ndeclare_rom_function! {\n    /// Flush the entire XIP cache, by issuing an invalidate by set/way\n    /// maintenance operation to every cache line (Section 4.4.1).\n    ///\n    /// This ensures that flash program/erase operations are visible to\n    /// subsequent cached XIP reads.\n    ///\n    /// Note that this unpins pinned cache lines, which may interfere with\n    /// cache-as-SRAM use of the XIP cache.\n    ///\n    /// No other operations are performed.\n    ///\n    /// Supported architectures: ARM-S, RISC-V\n    unsafe fn flash_flush_cache() -> () {\n        crate::rom_data::rom_table_lookup(*b\"FC\", crate::rom_data::inner::rt_flags::FUNC_ARM_SEC_RISCV)\n    }\n}\n\ndeclare_rom_function! {\n    /// Configure the QMI to generate a standard 03h serial read command, with\n    /// 24 address bits, upon each XIP access.\n    ///\n    /// This is a slow XIP configuration, but is widely supported. CLKDIV is set\n    /// to 12. The debugger may call this function to ensure that flash is\n    /// readable following a program/erase operation.\n    ///\n    /// Note that the same setup is performed by flash_exit_xip(), and the\n    /// RP2350 flash program/erase functions do not leave XIP in an inaccessible\n    /// state, so calls to this function are largely redundant. It is provided\n    /// for compatibility with RP2040.\n    ///\n    /// Supported architectures: ARM-S, RISC-V\n    unsafe fn flash_enter_cmd_xip() -> () {\n        crate::rom_data::rom_table_lookup(*b\"CX\", crate::rom_data::inner::rt_flags::FUNC_ARM_SEC_RISCV)\n    }\n}\n\ndeclare_rom_function! {\n    /// Configure QMI for one of a small menu of XIP read modes supported by the\n    /// bootrom. This mode is configured for both memory windows (both chip\n    /// selects), and the clock divisor is also applied to direct mode.\n    ///\n    /// The available modes are:\n    ///\n    /// * 0: `03h` serial read: serial address, serial data, no wait cycles\n    /// * 1: `0Bh` serial read: serial address, serial data, 8 wait cycles\n    /// * 2: `BBh` dual-IO read: dual address, dual data, 4 wait cycles\n    ///   (including MODE bits, which are driven to 0)\n    /// * 3: `EBh` quad-IO read: quad address, quad data, 6 wait cycles\n    ///   (including MODE bits, which are driven to 0)\n    ///\n    /// The XIP write command/format are not configured by this function. When\n    /// booting from flash, the bootrom tries each of these modes in turn, from\n    /// 3 down to 0. The first mode that is found to work is remembered, and a\n    /// default XIP setup function is written into bootram that calls this\n    /// function (flash_select_xip_read_mode) with the parameters discovered\n    /// during flash scanning. This can be called at any time to restore the\n    /// flash parameters discovered during flash boot.\n    ///\n    /// All XIP modes configured by the bootrom have an 8-bit serial command\n    /// prefix, so that the flash can remain in a serial command state, meaning\n    /// XIP accesses can be mixed more freely with program/erase serial\n    /// operations. This has a performance penalty, so users can perform their\n    /// own flash setup after flash boot using continuous read mode or QPI mode\n    /// to avoid or alleviate the command prefix cost.\n    ///\n    /// Supported architectures: ARM-S, RISC-V\n    unsafe fn flash_select_xip_read_mode(bootrom_xip_mode: u8, clkdiv: u8) -> () {\n        crate::rom_data::rom_table_lookup(*b\"XM\", crate::rom_data::inner::rt_flags::FUNC_ARM_SEC_RISCV)\n    }\n}\n\ndeclare_rom_function! {\n    /// Restore the QMI address translation registers, ATRANS0 through ATRANS7,\n    /// to their reset state. This makes the runtime- to-storage address map an\n    /// identity map, i.e. the mapped and unmapped address are equal, and the\n    /// entire space is fully mapped.\n    ///\n    /// See [Section 12.14.4](https://rptl.io/rp2350-datasheet#section_bootrom) of the RP2350\n    /// datasheet.\n    ///\n    /// Supported architectures: ARM-S, RISC-V\n    unsafe fn flash_reset_address_trans() -> () {\n        crate::rom_data::rom_table_lookup(*b\"RA\", crate::rom_data::inner::rt_flags::FUNC_ARM_SEC_RISCV)\n    }\n}\n\n// **************** High-level Flash Commands ****************\n\ndeclare_rom_function! {\n    /// Applies the address translation currently configured by QMI address\n    /// translation registers, ATRANS0 through ATRANS7.\n    ///\n    /// See [Section 12.14.4](https://rptl.io/rp2350-datasheet#section_bootrom) of the RP2350\n    /// datasheet.\n    ///\n    /// Translating an address outside of the XIP runtime address window, or\n    /// beyond the bounds of an ATRANSx_SIZE field, returns\n    /// BOOTROM_ERROR_INVALID_ADDRESS, which is not a valid flash storage\n    /// address. Otherwise, return the storage address which QMI would access\n    /// when presented with the runtime address addr. This is effectively a\n    /// virtual-to-physical address translation for QMI.\n    ///\n    /// Supported architectures: ARM-S, RISC-V\n    unsafe fn flash_runtime_to_storage_addr(addr: u32) -> i32 {\n        crate::rom_data::rom_table_lookup(*b\"FA\", crate::rom_data::inner::rt_flags::FUNC_ARM_SEC_RISCV)\n    }\n}\n\ndeclare_rom_function! {\n    /// Non-secure version of [flash_runtime_to_storage_addr()]\n    ///\n    /// Supported architectures: ARM-NS\n    #[cfg(all(target_arch = \"arm\", target_os = \"none\"))]\n    unsafe fn flash_runtime_to_storage_addr_ns(addr: u32) -> i32 {\n        crate::rom_data::rom_table_lookup(*b\"FA\", crate::rom_data::inner::rt_flags::FUNC_ARM_NONSEC)\n    }\n}\n\ndeclare_rom_function! {\n    /// Perform a flash read, erase, or program operation.\n    ///\n    /// Erase operations must be sector-aligned (4096 bytes) and sector-\n    /// multiple-sized, and program operations must be page-aligned (256 bytes)\n    /// and page-multiple-sized; misaligned erase and program operations will\n    /// return BOOTROM_ERROR_BAD_ALIGNMENT. The operation — erase, read, program\n    /// — is selected by the CFLASH_OP_BITS bitfield of the flags argument.\n    ///\n    /// See datasheet section 5.5.8.2 for more details.\n    ///\n    /// Supported architectures: ARM-S, RISC-V\n    unsafe fn flash_op(flags: u32, addr: u32, size_bytes: u32, buffer: *mut u8) -> i32 {\n        crate::rom_data::rom_table_lookup(*b\"FO\", crate::rom_data::inner::rt_flags::FUNC_ARM_SEC_RISCV)\n    }\n}\n\ndeclare_rom_function! {\n    /// Non-secure version of [flash_op()]\n    ///\n    /// Supported architectures: ARM-NS\n    #[cfg(all(target_arch = \"arm\", target_os = \"none\"))]\n    unsafe fn flash_op_ns(flags: u32, addr: u32, size_bytes: u32, buffer: *mut u8) -> i32 {\n        crate::rom_data::rom_table_lookup(*b\"FO\", crate::rom_data::inner::rt_flags::FUNC_ARM_NONSEC)\n    }\n}\n\n// **************** Security Related Functions ****************\n\ndeclare_rom_function! {\n    /// Allow or disallow the specific NS API (note all NS APIs default to\n    /// disabled).\n    ///\n    /// See datasheet section 5.5.9.1 for more details.\n    ///\n    /// Supported architectures: ARM-S\n    #[cfg(all(target_arch = \"arm\", target_os = \"none\"))]\n    unsafe fn set_ns_api_permission(ns_api_num: u32, allowed: u8) -> i32 {\n        crate::rom_data::rom_table_lookup(*b\"SP\", crate::rom_data::inner::rt_flags::FUNC_ARM_SEC)\n    }\n}\n\ndeclare_rom_function! {\n    /// Utility method that can be used by secure ARM code to validate a buffer\n    /// passed to it from Non-secure code.\n    ///\n    /// See datasheet section 5.5.9.2 for more details.\n    ///\n    /// Supported architectures: ARM-S, RISC-V\n    unsafe fn validate_ns_buffer() -> () {\n        crate::rom_data::rom_table_lookup(*b\"VB\", crate::rom_data::inner::rt_flags::FUNC_ARM_SEC_RISCV)\n    }\n}\n\n// **************** Miscellaneous Functions ****************\n\ndeclare_rom_function! {\n    /// Resets the RP2350 and uses the watchdog facility to restart.\n    ///\n    /// See datasheet section 5.5.10.1 for more details.\n    ///\n    /// Supported architectures: ARM-S, RISC-V\n    fn reboot(flags: u32, delay_ms: u32, p0: u32, p1: u32) -> i32 {\n        crate::rom_data::rom_table_lookup(*b\"RB\", crate::rom_data::inner::rt_flags::FUNC_ARM_SEC_RISCV)\n    }\n}\n\ndeclare_rom_function! {\n    /// Non-secure version of [reboot()]\n    ///\n    /// Supported architectures: ARM-NS\n    #[cfg(all(target_arch = \"arm\", target_os = \"none\"))]\n    fn reboot_ns(flags: u32, delay_ms: u32, p0: u32, p1: u32) -> i32 {\n        crate::rom_data::rom_table_lookup(*b\"RB\", crate::rom_data::inner::rt_flags::FUNC_ARM_NONSEC)\n    }\n}\n\ndeclare_rom_function! {\n    /// Resets internal bootrom state.\n    ///\n    /// See datasheet section 5.5.10.2 for more details.\n    ///\n    /// Supported architectures: ARM-S, RISC-V\n    unsafe fn bootrom_state_reset(flags: u32) -> () {\n        crate::rom_data::rom_table_lookup(*b\"SR\", crate::rom_data::inner::rt_flags::FUNC_ARM_SEC_RISCV)\n    }\n}\n\ndeclare_rom_function! {\n    /// Set a boot ROM callback.\n    ///\n    /// The only supported callback_number is 0 which sets the callback used for\n    /// the secure_call API.\n    ///\n    /// See datasheet section 5.5.10.3 for more details.\n    ///\n    /// Supported architectures: ARM-S, RISC-V\n    unsafe fn set_rom_callback(callback_number: i32, callback_fn: *const ()) -> i32 {\n        crate::rom_data::rom_table_lookup(*b\"RC\", crate::rom_data::inner::rt_flags::FUNC_ARM_SEC_RISCV)\n    }\n}\n\n// **************** System Information Functions ****************\n\ndeclare_rom_function! {\n    /// Fills a buffer with various system information.\n    ///\n    /// Note that this API is also used to return information over the PICOBOOT\n    /// interface.\n    ///\n    /// See datasheet section 5.5.11.1 for more details.\n    ///\n    /// Supported architectures: ARM-S, RISC-V\n    unsafe fn get_sys_info(out_buffer: *mut u32, out_buffer_word_size: usize, flags: u32) -> i32 {\n        crate::rom_data::rom_table_lookup(*b\"GS\", crate::rom_data::inner::rt_flags::FUNC_ARM_SEC_RISCV)\n    }\n}\n\ndeclare_rom_function! {\n    /// Non-secure version of [get_sys_info()]\n    ///\n    /// Supported architectures: ARM-NS\n    #[cfg(all(target_arch = \"arm\", target_os = \"none\"))]\n    unsafe fn get_sys_info_ns(out_buffer: *mut u32, out_buffer_word_size: usize, flags: u32) -> i32 {\n        crate::rom_data::rom_table_lookup(*b\"GS\", crate::rom_data::inner::rt_flags::FUNC_ARM_NONSEC)\n    }\n}\n\ndeclare_rom_function! {\n    /// Fills a buffer with information from the partition table.\n    ///\n    /// Note that this API is also used to return information over the PICOBOOT\n    /// interface.\n    ///\n    /// See datasheet section 5.5.11.2 for more details.\n    ///\n    /// Supported architectures: ARM-S, RISC-V\n    unsafe fn get_partition_table_info(out_buffer: *mut u32, out_buffer_word_size: usize, flags_and_partition: u32) -> i32 {\n        crate::rom_data::rom_table_lookup(*b\"GP\", crate::rom_data::inner::rt_flags::FUNC_ARM_SEC_RISCV)\n    }\n}\n\ndeclare_rom_function! {\n    /// Non-secure version of [get_partition_table_info()]\n    ///\n    /// Supported architectures: ARM-NS\n    #[cfg(all(target_arch = \"arm\", target_os = \"none\"))]\n    unsafe fn get_partition_table_info_ns(out_buffer: *mut u32, out_buffer_word_size: usize, flags_and_partition: u32) -> i32 {\n        crate::rom_data::rom_table_lookup(*b\"GP\", crate::rom_data::inner::rt_flags::FUNC_ARM_NONSEC)\n    }\n}\n\ndeclare_rom_function! {\n    /// Loads the current partition table from flash, if present.\n    ///\n    /// See datasheet section 5.5.11.3 for more details.\n    ///\n    /// Supported architectures: ARM-S, RISC-V\n    unsafe fn load_partition_table(workarea_base: *mut u8, workarea_size: usize, force_reload: bool) -> i32 {\n        crate::rom_data::rom_table_lookup(*b\"LP\", crate::rom_data::inner::rt_flags::FUNC_ARM_SEC_RISCV)\n    }\n}\n\ndeclare_rom_function! {\n    /// Writes data from a buffer into OTP, or reads data from OTP into a buffer.\n    ///\n    /// See datasheet section 5.5.11.4 for more details.\n    ///\n    /// Supported architectures: ARM-S, RISC-V\n    unsafe fn otp_access(buf: *mut u8, buf_len: usize, row_and_flags: u32) -> i32 {\n        crate::rom_data::rom_table_lookup(*b\"OA\", crate::rom_data::inner::rt_flags::FUNC_ARM_SEC_RISCV)\n    }\n}\n\ndeclare_rom_function! {\n    /// Non-secure version of [otp_access()]\n    ///\n    /// Supported architectures: ARM-NS\n    #[cfg(all(target_arch = \"arm\", target_os = \"none\"))]\n    unsafe fn otp_access_ns(buf: *mut u8, buf_len: usize, row_and_flags: u32) -> i32 {\n        crate::rom_data::rom_table_lookup(*b\"OA\", crate::rom_data::inner::rt_flags::FUNC_ARM_NONSEC)\n    }\n}\n\n// **************** Boot Related Functions ****************\n\ndeclare_rom_function! {\n    /// Determines which of the partitions has the \"better\" IMAGE_DEF. In the\n    /// case of executable images, this is the one that would be booted.\n    ///\n    /// See datasheet section 5.5.12.1 for more details.\n    ///\n    /// Supported architectures: ARM-S, RISC-V\n    unsafe fn pick_ab_parition(workarea_base: *mut u8, workarea_size: usize, partition_a_num: u32) -> i32 {\n        crate::rom_data::rom_table_lookup(*b\"AB\", crate::rom_data::inner::rt_flags::FUNC_ARM_SEC_RISCV)\n    }\n}\n\ndeclare_rom_function! {\n    /// Searches a memory region for a launchable image, and executes it if\n    /// possible.\n    ///\n    /// See datasheet section 5.5.12.2 for more details.\n    ///\n    /// Supported architectures: ARM-S, RISC-V\n    unsafe fn chain_image(workarea_base: *mut u8, workarea_size: usize, region_base: i32, region_size: u32) -> i32 {\n        crate::rom_data::rom_table_lookup(*b\"CI\", crate::rom_data::inner::rt_flags::FUNC_ARM_SEC_RISCV)\n    }\n}\n\ndeclare_rom_function! {\n    /// Perform an \"explicit\" buy of an executable launched via an IMAGE_DEF\n    /// which was \"explicit buy\" flagged.\n    ///\n    /// See datasheet section 5.5.12.3 for more details.\n    ///\n    /// Supported architectures: ARM-S, RISC-V\n    unsafe fn explicit_buy(buffer: *mut u8, buffer_size: u32) -> i32 {\n        crate::rom_data::rom_table_lookup(*b\"EB\", crate::rom_data::inner::rt_flags::FUNC_ARM_SEC_RISCV)\n    }\n}\n\ndeclare_rom_function! {\n    /// Not yet documented.\n    ///\n    /// See datasheet section 5.5.12.4 for more details.\n    ///\n    /// Supported architectures: ARM-S, RISC-V\n    unsafe fn get_uf2_target_partition(workarea_base: *mut u8, workarea_size: usize, family_id: u32, partition_out: *mut u32) -> i32 {\n        crate::rom_data::rom_table_lookup(*b\"GU\", crate::rom_data::inner::rt_flags::FUNC_ARM_SEC_RISCV)\n    }\n}\n\ndeclare_rom_function! {\n    /// Returns: The index of the B partition of partition A if a partition\n    /// table is present and loaded, and there is a partition A with a B\n    /// partition; otherwise returns BOOTROM_ERROR_NOT_FOUND.\n    ///\n    /// See datasheet section 5.5.12.5 for more details.\n    ///\n    /// Supported architectures: ARM-S, RISC-V\n    unsafe fn get_b_partition(partition_a: u32) -> i32 {\n        crate::rom_data::rom_table_lookup(*b\"GB\", crate::rom_data::inner::rt_flags::FUNC_ARM_SEC_RISCV)\n    }\n}\n\n// **************** Non-secure-specific Functions ****************\n\n// NB: The \"secure_call\" function should be here, but it doesn't have a fixed\n// function signature as it is designed to let you bounce into any secure\n// function from non-secure mode.\n\n// **************** RISC-V Functions ****************\n\ndeclare_rom_function! {\n    /// Set stack for RISC-V bootrom functions to use.\n    ///\n    /// See datasheet section 5.5.14.1 for more details.\n    ///\n    /// Supported architectures: RISC-V\n    #[cfg(not(all(target_arch = \"arm\", target_os = \"none\")))]\n    unsafe fn set_bootrom_stack(base_size: *mut u32) -> i32 {\n        crate::rom_data::rom_table_lookup(*b\"SS\", crate::rom_data::inner::rt_flags::FUNC_RISCV)\n    }\n}\n\n/// The version number of the rom.\npub fn rom_version_number() -> u8 {\n    unsafe { *VERSION_NUMBER }\n}\n\n/// The 8 most significant hex digits of the Bootrom git revision.\npub fn git_revision() -> u32 {\n    let ptr = rom_data_lookup(*b\"GR\", rt_flags::DATA) as *const u32;\n    unsafe { ptr.read() }\n}\n\n/// A pointer to the resident partition table info.\n///\n/// The resident partition table is the subset of the full partition table that\n/// is kept in memory, and used for flash permissions.\npub fn partition_table_pointer() -> *const u32 {\n    let ptr = rom_data_lookup(*b\"PT\", rt_flags::DATA) as *const *const u32;\n    unsafe { ptr.read() }\n}\n\n/// Determine if we are in secure mode\n///\n/// Returns `true` if we are in secure mode and `false` if we are in non-secure\n/// mode.\n#[cfg(all(target_arch = \"arm\", target_os = \"none\"))]\npub fn is_secure_mode() -> bool {\n    // Look at the start of ROM, which is always readable\n    #[allow(clippy::zero_ptr)]\n    let rom_base: *mut u32 = 0x0000_0000 as *mut u32;\n    // Use the 'tt' instruction to check the permissions for that address\n    let tt = cortex_m::asm::tt(rom_base);\n    // Is the secure bit set? => secure mode\n    (tt & (1 << 22)) != 0\n}\n\n/// Determine if we are in secure mode\n///\n/// Always returns `false` on RISC-V as it is impossible to determine if\n/// you are in Machine Mode or User Mode by design.\n#[cfg(not(all(target_arch = \"arm\", target_os = \"none\")))]\npub fn is_secure_mode() -> bool {\n    false\n}\n\n// These and the reset_to_usb_boot function are found from https://github.com/raspberrypi/pico-sdk/blob/master/src/rp2_common/pico_bootrom/bootrom.c#L35-L51\n// The following has just been translated to rust from the original c++\nconst BOOTSEL_FLAG_GPIO_PIN_SPECIFIED: u32 = 0x20;\nconst REBOOT2_FLAG_REBOOT_TYPE_BOOTSEL: u32 = 0x2;\nconst REBOOT2_FLAG_NO_RETURN_ON_SUCCESS: u32 = 0x100;\n\n/// Resets the RP235x and uses the watchdog facility to re-start in BOOTSEL mode:\n///   * gpio_activity_pin_mask is provided to enable an 'activity light' via GPIO attached LED\n///     for the USB Mass Storage Device:\n///     * 0 No pins are used as per cold boot.\n///     * Otherwise a single bit set indicating which GPIO pin should be set to output and\n///       raised whenever there is mass storage activity from the host.\n///  * disable_interface_mask may be used to control the exposed USB interfaces:\n///    * 0 To enable both interfaces (as per cold boot).\n///    * 1 To disable the USB Mass Storage Interface.\n///    * 2 to Disable the USB PICOBOOT Interface.\npub fn reset_to_usb_boot(mut usb_activity_gpio_pin_mask: u32, disable_interface_mask: u32) {\n    let mut flags = disable_interface_mask;\n\n    if usb_activity_gpio_pin_mask != 0 {\n        flags = flags | BOOTSEL_FLAG_GPIO_PIN_SPECIFIED;\n        usb_activity_gpio_pin_mask = usb_activity_gpio_pin_mask.trailing_zeros()\n    }\n\n    reboot(\n        REBOOT2_FLAG_REBOOT_TYPE_BOOTSEL | REBOOT2_FLAG_NO_RETURN_ON_SUCCESS,\n        10,\n        flags,\n        usb_activity_gpio_pin_mask,\n    );\n}\n"
  },
  {
    "path": "embassy-rp/src/rtc/conversions.rs",
    "content": "use crate::datetime::{DateTime, DayOfWeek, Error};\nuse crate::pac::rtc::regs::{Rtc0, Rtc1, Setup0, Setup1};\n\n#[cfg(not(feature = \"chrono\"))]\npub(super) fn day_of_week_from_u8(v: u8) -> Result<DayOfWeek, Error> {\n    Ok(match v {\n        0 => DayOfWeek::Sunday,\n        1 => DayOfWeek::Monday,\n        2 => DayOfWeek::Tuesday,\n        3 => DayOfWeek::Wednesday,\n        4 => DayOfWeek::Thursday,\n        5 => DayOfWeek::Friday,\n        6 => DayOfWeek::Saturday,\n        _ => return Err(Error::InvalidDayOfWeek),\n    })\n}\n\n#[cfg(feature = \"chrono\")]\npub(super) fn day_of_week_from_u8(v: u8) -> Result<DayOfWeek, ()> {\n    Ok(match v {\n        0 => DayOfWeek::Sun,\n        1 => DayOfWeek::Mon,\n        2 => DayOfWeek::Tue,\n        3 => DayOfWeek::Wed,\n        4 => DayOfWeek::Thu,\n        5 => DayOfWeek::Fri,\n        6 => DayOfWeek::Sat,\n        _ => return Err(()),\n    })\n}\n\npub(super) fn day_of_week_to_u8(dotw: DayOfWeek) -> u8 {\n    #[cfg(feature = \"chrono\")]\n    {\n        dotw.num_days_from_sunday() as u8\n    }\n    #[cfg(not(feature = \"chrono\"))]\n    {\n        dotw as u8\n    }\n}\n\npub(super) fn validate_datetime(dt: &DateTime) -> Result<(), Error> {\n    #[cfg(feature = \"chrono\")]\n    {\n        use chrono::Datelike;\n        if dt.year() < 0 || dt.year() > 4095 {\n            Err(Error::InvalidYear)\n        } else {\n            Ok(())\n        }\n    }\n    #[cfg(not(feature = \"chrono\"))]\n    {\n        if dt.year > 4095 {\n            Err(Error::InvalidYear)\n        } else if dt.month < 1 || dt.month > 12 {\n            Err(Error::InvalidMonth)\n        } else if dt.day < 1 || dt.day > 31 {\n            Err(Error::InvalidDay)\n        } else if dt.hour > 23 {\n            Err(Error::InvalidHour)\n        } else if dt.minute > 59 {\n            Err(Error::InvalidMinute)\n        } else if dt.second > 59 {\n            Err(Error::InvalidSecond)\n        } else {\n            Ok(())\n        }\n    }\n}\n\npub(super) fn write_setup_0(dt: &DateTime, w: &mut Setup0) {\n    #[cfg(feature = \"chrono\")]\n    {\n        use chrono::Datelike;\n        w.set_year(dt.year() as u16);\n        w.set_month(dt.month() as u8);\n        w.set_day(dt.day() as u8);\n    }\n    #[cfg(not(feature = \"chrono\"))]\n    {\n        w.set_year(dt.year);\n        w.set_month(dt.month);\n        w.set_day(dt.day);\n    }\n}\n\npub(super) fn write_setup_1(dt: &DateTime, w: &mut Setup1) {\n    #[cfg(feature = \"chrono\")]\n    {\n        use chrono::{Datelike, Timelike};\n        w.set_dotw(dt.weekday().num_days_from_sunday() as u8);\n        w.set_hour(dt.hour() as u8);\n        w.set_min(dt.minute() as u8);\n        w.set_sec(dt.second() as u8);\n    }\n    #[cfg(not(feature = \"chrono\"))]\n    {\n        w.set_dotw(dt.day_of_week as u8);\n        w.set_hour(dt.hour);\n        w.set_min(dt.minute);\n        w.set_sec(dt.second);\n    }\n}\n\npub(super) fn datetime_from_registers(rtc_0: Rtc0, rtc_1: Rtc1) -> Result<DateTime, Error> {\n    #[cfg(feature = \"chrono\")]\n    {\n        let year = rtc_1.year() as i32;\n        let month = rtc_1.month() as u32;\n        let day = rtc_1.day() as u32;\n\n        let hour = rtc_0.hour() as u32;\n        let minute = rtc_0.min() as u32;\n        let second = rtc_0.sec() as u32;\n\n        let date = chrono::NaiveDate::from_ymd_opt(year, month, day).ok_or(Error::InvalidDate)?;\n        let time = chrono::NaiveTime::from_hms_opt(hour, minute, second).ok_or(Error::InvalidTime)?;\n        Ok(DateTime::new(date, time))\n    }\n    #[cfg(not(feature = \"chrono\"))]\n    {\n        let year = rtc_1.year();\n        let month = rtc_1.month();\n        let day = rtc_1.day();\n\n        let day_of_week = rtc_0.dotw();\n        let hour = rtc_0.hour();\n        let minute = rtc_0.min();\n        let second = rtc_0.sec();\n\n        let day_of_week = day_of_week_from_u8(day_of_week)?;\n        Ok(DateTime {\n            year,\n            month,\n            day,\n            day_of_week,\n            hour,\n            minute,\n            second,\n        })\n    }\n}\n"
  },
  {
    "path": "embassy-rp/src/rtc/filter.rs",
    "content": "use super::DayOfWeek;\nuse crate::pac::rtc::regs::{IrqSetup0, IrqSetup1};\n\n/// A filter used for [`RealTimeClock::schedule_alarm`].\n///\n/// [`RealTimeClock::schedule_alarm`]: struct.RealTimeClock.html#method.schedule_alarm\n#[derive(Clone, Copy, Debug, PartialEq, Eq, Default)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub struct DateTimeFilter {\n    /// The year that this alarm should trigger on, `None` if the RTC alarm should not trigger on a year value.\n    pub year: Option<u16>,\n    /// The month that this alarm should trigger on, `None` if the RTC alarm should not trigger on a month value.\n    pub month: Option<u8>,\n    /// The day that this alarm should trigger on, `None` if the RTC alarm should not trigger on a day value.\n    pub day: Option<u8>,\n    /// The day of week that this alarm should trigger on, `None` if the RTC alarm should not trigger on a day of week value.\n    pub day_of_week: Option<DayOfWeek>,\n    /// The hour that this alarm should trigger on, `None` if the RTC alarm should not trigger on a hour value.\n    pub hour: Option<u8>,\n    /// The minute that this alarm should trigger on, `None` if the RTC alarm should not trigger on a minute value.\n    pub minute: Option<u8>,\n    /// The second that this alarm should trigger on, `None` if the RTC alarm should not trigger on a second value.\n    pub second: Option<u8>,\n}\n\nimpl DateTimeFilter {\n    /// Set a filter on the given year\n    pub fn year(mut self, year: u16) -> Self {\n        self.year = Some(year);\n        self\n    }\n    /// Set a filter on the given month\n    pub fn month(mut self, month: u8) -> Self {\n        self.month = Some(month);\n        self\n    }\n    /// Set a filter on the given day\n    pub fn day(mut self, day: u8) -> Self {\n        self.day = Some(day);\n        self\n    }\n    /// Set a filter on the given day of the week\n    pub fn day_of_week(mut self, day_of_week: DayOfWeek) -> Self {\n        self.day_of_week = Some(day_of_week);\n        self\n    }\n    /// Set a filter on the given hour\n    pub fn hour(mut self, hour: u8) -> Self {\n        self.hour = Some(hour);\n        self\n    }\n    /// Set a filter on the given minute\n    pub fn minute(mut self, minute: u8) -> Self {\n        self.minute = Some(minute);\n        self\n    }\n    /// Set a filter on the given second\n    pub fn second(mut self, second: u8) -> Self {\n        self.second = Some(second);\n        self\n    }\n}\n\n// register helper functions\nimpl DateTimeFilter {\n    pub(super) fn write_setup_0(&self, w: &mut IrqSetup0) {\n        if let Some(year) = self.year {\n            w.set_year_ena(true);\n\n            w.set_year(year);\n        }\n        if let Some(month) = self.month {\n            w.set_month_ena(true);\n            w.set_month(month);\n        }\n        if let Some(day) = self.day {\n            w.set_day_ena(true);\n            w.set_day(day);\n        }\n    }\n    pub(super) fn write_setup_1(&self, w: &mut IrqSetup1) {\n        if let Some(day_of_week) = self.day_of_week {\n            w.set_dotw_ena(true);\n            let bits = super::conversions::day_of_week_to_u8(day_of_week);\n\n            w.set_dotw(bits);\n        }\n        if let Some(hour) = self.hour {\n            w.set_hour_ena(true);\n            w.set_hour(hour);\n        }\n        if let Some(minute) = self.minute {\n            w.set_min_ena(true);\n            w.set_min(minute);\n        }\n        if let Some(second) = self.second {\n            w.set_sec_ena(true);\n            w.set_sec(second);\n        }\n    }\n}\n"
  },
  {
    "path": "embassy-rp/src/rtc/mod.rs",
    "content": "//! RTC driver.\nmod conversions;\nmod filter;\n\nuse core::future::poll_fn;\nuse core::sync::atomic::{AtomicBool, Ordering, compiler_fence};\nuse core::task::Poll;\n\nuse embassy_hal_internal::{Peri, PeripheralType};\nuse embassy_sync::waitqueue::AtomicWaker;\n\npub use self::filter::DateTimeFilter;\nuse crate::clocks::clk_rtc_freq;\npub use crate::datetime::{DateTime, DayOfWeek, Error as DateTimeError};\nuse crate::interrupt::typelevel::Binding;\nuse crate::interrupt::{self, InterruptExt};\n\n// Static waker for the interrupt handler\nstatic WAKER: AtomicWaker = AtomicWaker::new();\n// Static flag to indicate if an alarm has occurred\nstatic ALARM_OCCURRED: AtomicBool = AtomicBool::new(false);\n\n/// A reference to the real time clock of the system\npub struct Rtc<'d, T: Instance> {\n    inner: Peri<'d, T>,\n}\n\nimpl<'d, T: Instance> Rtc<'d, T> {\n    /// Create a new instance of the real time clock, with the given date as an initial value.\n    ///\n    /// # Errors\n    ///\n    /// Will return `RtcError::InvalidDateTime` if the datetime is not a valid range.\n    pub fn new(inner: Peri<'d, T>, _irq: impl Binding<interrupt::typelevel::RTC_IRQ, InterruptHandler>) -> Self {\n        // Set the RTC divider\n        inner.regs().clkdiv_m1().write(|w| w.set_clkdiv_m1(clk_rtc_freq() - 1));\n\n        // Setup the IRQ\n        // Clear any pending interrupts from the RTC_IRQ interrupt and enable it, so we do not have unexpected interrupts after initialization\n        interrupt::RTC_IRQ.unpend();\n        unsafe { interrupt::RTC_IRQ.enable() };\n\n        Self { inner }\n    }\n\n    /// Enable or disable the leap year check. The rp2040 chip will always add a Feb 29th on every year that is divisible by 4, but this may be incorrect (e.g. on century years). This function allows you to disable this check.\n    ///\n    /// Leap year checking is enabled by default.\n    pub fn set_leap_year_check(&mut self, leap_year_check_enabled: bool) {\n        self.inner.regs().ctrl().modify(|w| {\n            w.set_force_notleapyear(!leap_year_check_enabled);\n        });\n    }\n\n    /// Set the time from internal format\n    pub fn restore(&mut self, ymd: rp_pac::rtc::regs::Rtc1, hms: rp_pac::rtc::regs::Rtc0) {\n        // disable RTC while we configure it\n        self.inner.regs().ctrl().modify(|w| w.set_rtc_enable(false));\n        while self.inner.regs().ctrl().read().rtc_active() {\n            core::hint::spin_loop();\n        }\n\n        self.inner.regs().setup_0().write(|w| {\n            *w = rp_pac::rtc::regs::Setup0(ymd.0);\n        });\n        self.inner.regs().setup_1().write(|w| {\n            *w = rp_pac::rtc::regs::Setup1(hms.0);\n        });\n\n        // Load the new datetime and re-enable RTC\n        self.inner.regs().ctrl().write(|w| w.set_load(true));\n        self.inner.regs().ctrl().write(|w| w.set_rtc_enable(true));\n        while !self.inner.regs().ctrl().read().rtc_active() {\n            core::hint::spin_loop();\n        }\n    }\n\n    /// Get the time in internal format\n    pub fn save(&mut self) -> (rp_pac::rtc::regs::Rtc1, rp_pac::rtc::regs::Rtc0) {\n        let rtc_0: rp_pac::rtc::regs::Rtc0 = self.inner.regs().rtc_0().read();\n        let rtc_1 = self.inner.regs().rtc_1().read();\n        (rtc_1, rtc_0)\n    }\n\n    /// Checks to see if this Rtc is running\n    pub fn is_running(&self) -> bool {\n        self.inner.regs().ctrl().read().rtc_active()\n    }\n\n    /// Set the datetime to a new value.\n    ///\n    /// # Errors\n    ///\n    /// Will return `RtcError::InvalidDateTime` if the datetime is not a valid range.\n    pub fn set_datetime(&mut self, t: DateTime) -> Result<(), RtcError> {\n        conversions::validate_datetime(&t).map_err(RtcError::InvalidDateTime)?;\n\n        // disable RTC while we configure it\n        self.inner.regs().ctrl().modify(|w| w.set_rtc_enable(false));\n        while self.inner.regs().ctrl().read().rtc_active() {\n            core::hint::spin_loop();\n        }\n\n        self.inner.regs().setup_0().write(|w| {\n            conversions::write_setup_0(&t, w);\n        });\n        self.inner.regs().setup_1().write(|w| {\n            conversions::write_setup_1(&t, w);\n        });\n\n        // Load the new datetime and re-enable RTC\n        self.inner.regs().ctrl().write(|w| w.set_load(true));\n        self.inner.regs().ctrl().write(|w| w.set_rtc_enable(true));\n        while !self.inner.regs().ctrl().read().rtc_active() {\n            core::hint::spin_loop();\n        }\n        Ok(())\n    }\n\n    /// Return the current datetime.\n    ///\n    /// # Errors\n    ///\n    /// Will return an `RtcError::InvalidDateTime` if the stored value in the system is not a valid [`DayOfWeek`].\n    pub fn now(&self) -> Result<DateTime, RtcError> {\n        if !self.is_running() {\n            return Err(RtcError::NotRunning);\n        }\n\n        let rtc_0 = self.inner.regs().rtc_0().read();\n        let rtc_1 = self.inner.regs().rtc_1().read();\n\n        conversions::datetime_from_registers(rtc_0, rtc_1).map_err(RtcError::InvalidDateTime)\n    }\n\n    /// Disable the alarm that was scheduled with [`schedule_alarm`].\n    ///\n    /// [`schedule_alarm`]: #method.schedule_alarm\n    pub fn disable_alarm(&mut self) {\n        self.inner.regs().irq_setup_0().modify(|s| s.set_match_ena(false));\n\n        while self.inner.regs().irq_setup_0().read().match_active() {\n            core::hint::spin_loop();\n        }\n    }\n\n    /// Schedule an alarm. The `filter` determines at which point in time this alarm is set.\n    ///\n    /// Keep in mind that the filter only triggers on the specified time. If you want to schedule this alarm every minute, you have to call:\n    /// ```no_run\n    /// # #[cfg(feature = \"chrono\")]\n    /// # fn main() { }\n    /// # #[cfg(not(feature = \"chrono\"))]\n    /// # fn main() {\n    /// # use embassy_rp::rtc::{Rtc, DateTimeFilter};\n    /// # let mut real_time_clock: Rtc<embassy_rp::peripherals::RTC> = unsafe { core::mem::zeroed() };\n    /// let now = real_time_clock.now().unwrap();\n    /// real_time_clock.schedule_alarm(\n    ///     DateTimeFilter::default()\n    ///         .minute(if now.minute == 59 { 0 } else { now.minute + 1 })\n    /// );\n    /// # }\n    /// ```\n    pub fn schedule_alarm(&mut self, filter: DateTimeFilter) {\n        self.disable_alarm();\n\n        self.inner.regs().irq_setup_0().write(|w| {\n            filter.write_setup_0(w);\n        });\n        self.inner.regs().irq_setup_1().write(|w| {\n            filter.write_setup_1(w);\n        });\n\n        self.inner.regs().inte().modify(|w| w.set_rtc(true));\n\n        // Set the enable bit and check if it is set\n        self.inner.regs().irq_setup_0().modify(|w| w.set_match_ena(true));\n        while !self.inner.regs().irq_setup_0().read().match_active() {\n            core::hint::spin_loop();\n        }\n    }\n\n    /// Clear the interrupt. This should be called every time the `RTC_IRQ` interrupt is triggered,\n    /// or the next [`schedule_alarm`] will never fire.\n    ///\n    /// [`schedule_alarm`]: #method.schedule_alarm\n    pub fn clear_interrupt(&mut self) {\n        self.disable_alarm();\n    }\n\n    /// Check if an alarm is scheduled.\n    ///\n    /// This function checks if the RTC alarm is currently active. If it is, it returns the alarm configuration\n    /// as a [`DateTimeFilter`]. Otherwise, it returns `None`.\n    pub fn alarm_scheduled(&self) -> Option<DateTimeFilter> {\n        // Check if alarm is active\n        if !self.inner.regs().irq_setup_0().read().match_active() {\n            return None;\n        }\n\n        // Get values from both alarm registers\n        let irq_0 = self.inner.regs().irq_setup_0().read();\n        let irq_1 = self.inner.regs().irq_setup_1().read();\n\n        // Create a DateTimeFilter and populate it based on which fields are enabled\n        let mut filter = DateTimeFilter::default();\n\n        if irq_0.year_ena() {\n            filter.year = Some(irq_0.year());\n        }\n\n        if irq_0.month_ena() {\n            filter.month = Some(irq_0.month());\n        }\n\n        if irq_0.day_ena() {\n            filter.day = Some(irq_0.day());\n        }\n\n        if irq_1.dotw_ena() {\n            // Convert day of week value to DayOfWeek enum\n            let Ok(day_of_week) = conversions::day_of_week_from_u8(irq_1.dotw()) else {\n                return None; // Invalid day of week\n            };\n            filter.day_of_week = Some(day_of_week);\n        }\n\n        if irq_1.hour_ena() {\n            filter.hour = Some(irq_1.hour());\n        }\n\n        if irq_1.min_ena() {\n            filter.minute = Some(irq_1.min());\n        }\n\n        if irq_1.sec_ena() {\n            filter.second = Some(irq_1.sec());\n        }\n\n        Some(filter)\n    }\n\n    /// Wait for an RTC alarm to trigger.\n    ///\n    /// This function will wait until the RTC alarm is triggered. If the alarm is already triggered, it will return immediately.\n    /// If no alarm is scheduled, it will wait indefinitely until one is scheduled and triggered.\n    pub async fn wait_for_alarm(&mut self) {\n        poll_fn(|cx| {\n            WAKER.register(cx.waker());\n\n            // Atomically check and clear the alarm occurred flag to prevent race conditions\n            if critical_section::with(|_| {\n                let occurred = ALARM_OCCURRED.load(Ordering::SeqCst);\n                if occurred {\n                    ALARM_OCCURRED.store(false, Ordering::SeqCst);\n                }\n                occurred\n            }) {\n                // Clear the interrupt and disable the alarm\n                self.clear_interrupt();\n\n                compiler_fence(Ordering::SeqCst);\n                return Poll::Ready(());\n            } else {\n                return Poll::Pending;\n            }\n        })\n        .await;\n    }\n}\n\n/// Interrupt handler.\npub struct InterruptHandler {\n    _empty: (),\n}\n\nimpl crate::interrupt::typelevel::Handler<crate::interrupt::typelevel::RTC_IRQ> for InterruptHandler {\n    unsafe fn on_interrupt() {\n        // Disable the alarm first thing, to prevent unexpected re-entry\n        let rtc = crate::pac::RTC;\n        rtc.irq_setup_0().modify(|w| w.set_match_ena(false));\n\n        // Set the alarm occurred flag and wake the waker\n        ALARM_OCCURRED.store(true, Ordering::SeqCst);\n        WAKER.wake();\n    }\n}\n\n/// Errors that can occur on methods on [Rtc]\n#[derive(Clone, Debug, PartialEq, Eq)]\npub enum RtcError {\n    /// An invalid DateTime was given or stored on the hardware.\n    InvalidDateTime(DateTimeError),\n\n    /// The RTC clock is not running\n    NotRunning,\n}\n\ntrait SealedInstance {\n    fn regs(&self) -> crate::pac::rtc::Rtc;\n}\n\n/// RTC peripheral instance.\n#[allow(private_bounds)]\npub trait Instance: SealedInstance + PeripheralType {}\n\nimpl SealedInstance for crate::peripherals::RTC {\n    fn regs(&self) -> crate::pac::rtc::Rtc {\n        crate::pac::RTC\n    }\n}\nimpl Instance for crate::peripherals::RTC {}\n"
  },
  {
    "path": "embassy-rp/src/spi.rs",
    "content": "//! Serial Peripheral Interface\nuse core::marker::PhantomData;\n\nuse embassy_embedded_hal::SetConfig;\nuse embassy_futures::join::join;\nuse embassy_hal_internal::{Peri, PeripheralType};\npub use embedded_hal_02::spi::{Phase, Polarity};\n\nuse crate::dma::{Channel, ChannelInstance};\nuse crate::gpio::{AnyPin, Pin as GpioPin, SealedPin as _};\nuse crate::{dma, interrupt, pac, peripherals};\n\n/// SPI errors.\n#[derive(Debug, Clone, Copy, PartialEq, Eq)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\n#[non_exhaustive]\npub enum Error {\n    // No errors for now\n}\n\n/// SPI configuration.\n#[non_exhaustive]\n#[derive(Clone)]\npub struct Config {\n    /// Frequency.\n    pub frequency: u32,\n    /// Phase.\n    pub phase: Phase,\n    /// Polarity.\n    pub polarity: Polarity,\n}\n\nimpl Default for Config {\n    fn default() -> Self {\n        Self {\n            frequency: 1_000_000,\n            phase: Phase::CaptureOnFirstTransition,\n            polarity: Polarity::IdleLow,\n        }\n    }\n}\n\n/// SPI driver.\npub struct Spi<'d, T: Instance, M: Mode> {\n    inner: Peri<'d, T>,\n    tx_dma: Option<Channel<'d>>,\n    rx_dma: Option<Channel<'d>>,\n    phantom: PhantomData<(&'d mut T, M)>,\n}\n\nfn div_roundup(a: u32, b: u32) -> u32 {\n    (a + b - 1) / b\n}\n\nfn calc_prescs(freq: u32) -> (u8, u8) {\n    let clk_peri = crate::clocks::clk_peri_freq();\n\n    // final SPI frequency: spi_freq = clk_peri / presc / postdiv\n    // presc must be in 2..=254, and must be even\n    // postdiv must be in 1..=256\n\n    // divide extra by 2, so we get rid of the \"presc must be even\" requirement\n    let ratio = div_roundup(clk_peri, freq * 2);\n    if ratio > 127 * 256 {\n        panic!(\"Requested too low SPI frequency\");\n    }\n\n    let presc = div_roundup(ratio, 256);\n    let postdiv = if presc == 1 { ratio } else { div_roundup(ratio, presc) };\n\n    ((presc * 2) as u8, (postdiv - 1) as u8)\n}\n\nimpl<'d, T: Instance, M: Mode> Spi<'d, T, M> {\n    fn new_inner(\n        inner: Peri<'d, T>,\n        clk: Option<Peri<'d, AnyPin>>,\n        mosi: Option<Peri<'d, AnyPin>>,\n        miso: Option<Peri<'d, AnyPin>>,\n        cs: Option<Peri<'d, AnyPin>>,\n        tx_dma: Option<Channel<'d>>,\n        rx_dma: Option<Channel<'d>>,\n        config: Config,\n    ) -> Self {\n        Self::apply_config(&inner, &config);\n\n        let p = inner.regs();\n\n        // Always enable DREQ signals -- harmless if DMA is not listening\n        p.dmacr().write(|reg| {\n            reg.set_rxdmae(true);\n            reg.set_txdmae(true);\n        });\n\n        // finally, enable.\n        p.cr1().write(|w| w.set_sse(true));\n\n        if let Some(pin) = &clk {\n            pin.gpio().ctrl().write(|w| w.set_funcsel(1));\n            pin.pad_ctrl().write(|w| {\n                #[cfg(feature = \"_rp235x\")]\n                w.set_iso(false);\n                w.set_schmitt(true);\n                w.set_slewfast(false);\n                w.set_ie(true);\n                w.set_od(false);\n                w.set_pue(false);\n                w.set_pde(false);\n            });\n        }\n        if let Some(pin) = &mosi {\n            pin.gpio().ctrl().write(|w| w.set_funcsel(1));\n            pin.pad_ctrl().write(|w| {\n                #[cfg(feature = \"_rp235x\")]\n                w.set_iso(false);\n                w.set_schmitt(true);\n                w.set_slewfast(false);\n                w.set_ie(true);\n                w.set_od(false);\n                w.set_pue(false);\n                w.set_pde(false);\n            });\n        }\n        if let Some(pin) = &miso {\n            pin.gpio().ctrl().write(|w| w.set_funcsel(1));\n            pin.pad_ctrl().write(|w| {\n                #[cfg(feature = \"_rp235x\")]\n                w.set_iso(false);\n                w.set_schmitt(true);\n                w.set_slewfast(false);\n                w.set_ie(true);\n                w.set_od(false);\n                w.set_pue(false);\n                w.set_pde(false);\n            });\n        }\n        if let Some(pin) = &cs {\n            pin.gpio().ctrl().write(|w| w.set_funcsel(1));\n            pin.pad_ctrl().write(|w| {\n                #[cfg(feature = \"_rp235x\")]\n                w.set_iso(false);\n                w.set_schmitt(true);\n                w.set_slewfast(false);\n                w.set_ie(true);\n                w.set_od(false);\n                w.set_pue(false);\n                w.set_pde(false);\n            });\n        }\n        Self {\n            inner,\n            tx_dma,\n            rx_dma,\n            phantom: PhantomData,\n        }\n    }\n\n    /// Private function to apply SPI configuration (phase, polarity, frequency) settings.\n    ///\n    /// Driver should be disabled before making changes and re-enabled after the modifications\n    /// are applied.\n    fn apply_config(inner: &Peri<'d, T>, config: &Config) {\n        let p = inner.regs();\n        let (presc, postdiv) = calc_prescs(config.frequency);\n\n        p.cpsr().write(|w| w.set_cpsdvsr(presc));\n        p.cr0().write(|w| {\n            w.set_dss(0b0111); // 8bit\n            w.set_spo(config.polarity == Polarity::IdleHigh);\n            w.set_sph(config.phase == Phase::CaptureOnSecondTransition);\n            w.set_scr(postdiv);\n        });\n    }\n\n    /// Write data to SPI blocking execution until done.\n    pub fn blocking_write(&mut self, data: &[u8]) -> Result<(), Error> {\n        let p = self.inner.regs();\n        for &b in data {\n            while !p.sr().read().tnf() {}\n            p.dr().write(|w| w.set_data(b as _));\n            while !p.sr().read().rne() {}\n            let _ = p.dr().read();\n        }\n        self.flush()?;\n        Ok(())\n    }\n\n    /// Transfer data in place to SPI blocking execution until done.\n    pub fn blocking_transfer_in_place(&mut self, data: &mut [u8]) -> Result<(), Error> {\n        let p = self.inner.regs();\n        for b in data {\n            while !p.sr().read().tnf() {}\n            p.dr().write(|w| w.set_data(*b as _));\n            while !p.sr().read().rne() {}\n            *b = p.dr().read().data() as u8;\n        }\n        self.flush()?;\n        Ok(())\n    }\n\n    /// Read data from SPI blocking execution until done.\n    pub fn blocking_read(&mut self, data: &mut [u8]) -> Result<(), Error> {\n        let p = self.inner.regs();\n        for b in data {\n            while !p.sr().read().tnf() {}\n            p.dr().write(|w| w.set_data(0));\n            while !p.sr().read().rne() {}\n            *b = p.dr().read().data() as u8;\n        }\n        self.flush()?;\n        Ok(())\n    }\n\n    /// Transfer data to SPI blocking execution until done.\n    pub fn blocking_transfer(&mut self, read: &mut [u8], write: &[u8]) -> Result<(), Error> {\n        let p = self.inner.regs();\n        let len = read.len().max(write.len());\n        for i in 0..len {\n            let wb = write.get(i).copied().unwrap_or(0);\n            while !p.sr().read().tnf() {}\n            p.dr().write(|w| w.set_data(wb as _));\n            while !p.sr().read().rne() {}\n            let rb = p.dr().read().data() as u8;\n            if let Some(r) = read.get_mut(i) {\n                *r = rb;\n            }\n        }\n        self.flush()?;\n        Ok(())\n    }\n\n    /// Block execution until SPI is done.\n    pub fn flush(&mut self) -> Result<(), Error> {\n        let p = self.inner.regs();\n        while p.sr().read().bsy() {}\n        Ok(())\n    }\n\n    /// Set SPI frequency.\n    pub fn set_frequency(&mut self, freq: u32) {\n        let (presc, postdiv) = calc_prescs(freq);\n        let p = self.inner.regs();\n        // disable\n        p.cr1().write(|w| w.set_sse(false));\n\n        // change stuff\n        p.cpsr().write(|w| w.set_cpsdvsr(presc));\n        p.cr0().modify(|w| {\n            w.set_scr(postdiv);\n        });\n\n        // enable\n        p.cr1().write(|w| w.set_sse(true));\n    }\n\n    /// Set SPI config.\n    pub fn set_config(&mut self, config: &Config) {\n        let p = self.inner.regs();\n\n        // disable\n        p.cr1().write(|w| w.set_sse(false));\n\n        // change stuff\n        Self::apply_config(&self.inner, config);\n\n        // enable\n        p.cr1().write(|w| w.set_sse(true));\n    }\n}\n\nimpl<'d, T: Instance> Spi<'d, T, Blocking> {\n    /// Create an SPI driver in blocking mode.\n    pub fn new_blocking(\n        inner: Peri<'d, T>,\n        clk: Peri<'d, impl ClkPin<T> + 'd>,\n        mosi: Peri<'d, impl MosiPin<T> + 'd>,\n        miso: Peri<'d, impl MisoPin<T> + 'd>,\n        config: Config,\n    ) -> Self {\n        Self::new_inner(\n            inner,\n            Some(clk.into()),\n            Some(mosi.into()),\n            Some(miso.into()),\n            None,\n            None,\n            None,\n            config,\n        )\n    }\n\n    /// Create an SPI driver in blocking mode supporting writes only.\n    pub fn new_blocking_txonly(\n        inner: Peri<'d, T>,\n        clk: Peri<'d, impl ClkPin<T> + 'd>,\n        mosi: Peri<'d, impl MosiPin<T> + 'd>,\n        config: Config,\n    ) -> Self {\n        Self::new_inner(\n            inner,\n            Some(clk.into()),\n            Some(mosi.into()),\n            None,\n            None,\n            None,\n            None,\n            config,\n        )\n    }\n\n    /// Create an SPI driver in blocking mode supporting writes only, without SCK pin.\n    pub fn new_blocking_txonly_nosck(inner: Peri<'d, T>, mosi: Peri<'d, impl MosiPin<T> + 'd>, config: Config) -> Self {\n        Self::new_inner(inner, None, Some(mosi.into()), None, None, None, None, config)\n    }\n\n    /// Create an SPI driver in blocking mode supporting reads only.\n    pub fn new_blocking_rxonly(\n        inner: Peri<'d, T>,\n        clk: Peri<'d, impl ClkPin<T> + 'd>,\n        miso: Peri<'d, impl MisoPin<T> + 'd>,\n        config: Config,\n    ) -> Self {\n        Self::new_inner(\n            inner,\n            Some(clk.into()),\n            None,\n            Some(miso.into()),\n            None,\n            None,\n            None,\n            config,\n        )\n    }\n}\n\nimpl<'d, T: Instance> Spi<'d, T, Async> {\n    /// Create an SPI driver in async mode supporting DMA operations.\n    pub fn new<TxDma: ChannelInstance, RxDma: ChannelInstance>(\n        inner: Peri<'d, T>,\n        clk: Peri<'d, impl ClkPin<T> + 'd>,\n        mosi: Peri<'d, impl MosiPin<T> + 'd>,\n        miso: Peri<'d, impl MisoPin<T> + 'd>,\n        tx_dma: Peri<'d, TxDma>,\n        rx_dma: Peri<'d, RxDma>,\n        irq: impl interrupt::typelevel::Binding<TxDma::Interrupt, dma::InterruptHandler<TxDma>>\n        + interrupt::typelevel::Binding<RxDma::Interrupt, dma::InterruptHandler<RxDma>>\n        + 'd,\n        config: Config,\n    ) -> Self {\n        let tx_dma_ch = dma::Channel::new(tx_dma, irq);\n        let rx_dma_ch = dma::Channel::new(rx_dma, irq);\n        Self::new_inner(\n            inner,\n            Some(clk.into()),\n            Some(mosi.into()),\n            Some(miso.into()),\n            None,\n            Some(tx_dma_ch),\n            Some(rx_dma_ch),\n            config,\n        )\n    }\n\n    /// Create an SPI driver in async mode supporting DMA write operations only.\n    pub fn new_txonly<TxDma: ChannelInstance>(\n        inner: Peri<'d, T>,\n        clk: Peri<'d, impl ClkPin<T> + 'd>,\n        mosi: Peri<'d, impl MosiPin<T> + 'd>,\n        tx_dma: Peri<'d, TxDma>,\n        irq: impl interrupt::typelevel::Binding<TxDma::Interrupt, dma::InterruptHandler<TxDma>> + 'd,\n        config: Config,\n    ) -> Self {\n        let tx_dma_ch = dma::Channel::new(tx_dma, irq);\n        Self::new_inner(\n            inner,\n            Some(clk.into()),\n            Some(mosi.into()),\n            None,\n            None,\n            Some(tx_dma_ch),\n            None,\n            config,\n        )\n    }\n\n    /// Create an SPI driver in async mode supporting DMA write operations only,\n    /// without SCK pin.\n    pub fn new_txonly_nosck<TxDma: ChannelInstance>(\n        inner: Peri<'d, T>,\n        mosi: Peri<'d, impl MosiPin<T> + 'd>,\n        tx_dma: Peri<'d, TxDma>,\n        irq: impl interrupt::typelevel::Binding<TxDma::Interrupt, dma::InterruptHandler<TxDma>> + 'd,\n        config: Config,\n    ) -> Self {\n        let tx_dma_ch = dma::Channel::new(tx_dma, irq);\n        Self::new_inner(\n            inner,\n            None,\n            Some(mosi.into()),\n            None,\n            None,\n            Some(tx_dma_ch),\n            None,\n            config,\n        )\n    }\n\n    /// Create an SPI driver in async mode supporting DMA read operations only.\n    pub fn new_rxonly<TxDma: ChannelInstance, RxDma: ChannelInstance>(\n        inner: Peri<'d, T>,\n        clk: Peri<'d, impl ClkPin<T> + 'd>,\n        miso: Peri<'d, impl MisoPin<T> + 'd>,\n        tx_dma: Peri<'d, TxDma>,\n        rx_dma: Peri<'d, RxDma>,\n        irq: impl interrupt::typelevel::Binding<TxDma::Interrupt, dma::InterruptHandler<TxDma>>\n        + interrupt::typelevel::Binding<RxDma::Interrupt, dma::InterruptHandler<RxDma>>\n        + 'd,\n        config: Config,\n    ) -> Self {\n        let tx_dma_ch = dma::Channel::new(tx_dma, irq);\n        let rx_dma_ch = dma::Channel::new(rx_dma, irq);\n        Self::new_inner(\n            inner,\n            Some(clk.into()),\n            None,\n            Some(miso.into()),\n            None,\n            Some(tx_dma_ch),\n            Some(rx_dma_ch),\n            config,\n        )\n    }\n\n    /// Write data to SPI using DMA.\n    pub async fn write(&mut self, buffer: &[u8]) -> Result<(), Error> {\n        let tx_transfer = unsafe {\n            // If we don't assign future to a variable, the data register pointer\n            // is held across an await and makes the future non-Send.\n            self.tx_dma\n                .as_mut()\n                .unwrap()\n                .write(buffer, self.inner.regs().dr().as_ptr() as *mut _, T::TX_DREQ, false)\n        };\n        tx_transfer.await;\n\n        let p = self.inner.regs();\n        while p.sr().read().bsy() {}\n\n        // clear RX FIFO contents to prevent stale reads\n        while p.sr().read().rne() {\n            let _: u16 = p.dr().read().data();\n        }\n        // clear RX overrun interrupt\n        p.icr().write(|w| w.set_roric(true));\n\n        Ok(())\n    }\n\n    /// Read data from SPI using DMA.\n    pub async fn read(&mut self, buffer: &mut [u8]) -> Result<(), Error> {\n        // Start RX first. Transfer starts when TX starts, if RX\n        // is not started yet we might lose bytes.\n        let rx_transfer = unsafe {\n            // If we don't assign future to a variable, the data register pointer\n            // is held across an await and makes the future non-Send.\n            self.rx_dma\n                .as_mut()\n                .unwrap()\n                .read(self.inner.regs().dr().as_ptr() as *const _, buffer, T::RX_DREQ, false)\n        };\n\n        let tx_transfer = unsafe {\n            // If we don't assign future to a variable, the data register pointer\n            // is held across an await and makes the future non-Send.\n            self.tx_dma.as_mut().unwrap().write_zeros(\n                buffer.len(),\n                self.inner.regs().dr().as_ptr() as *mut u8,\n                T::TX_DREQ,\n            )\n        };\n        join(tx_transfer, rx_transfer).await;\n        Ok(())\n    }\n\n    /// Transfer data to SPI using DMA.\n    pub async fn transfer(&mut self, rx_buffer: &mut [u8], tx_buffer: &[u8]) -> Result<(), Error> {\n        self.transfer_inner(rx_buffer, tx_buffer).await\n    }\n\n    /// Transfer data in place to SPI using DMA.\n    pub async fn transfer_in_place(&mut self, words: &mut [u8]) -> Result<(), Error> {\n        self.transfer_inner(words, words).await\n    }\n\n    async fn transfer_inner(&mut self, rx: *mut [u8], tx: *const [u8]) -> Result<(), Error> {\n        // Start RX first. Transfer starts when TX starts, if RX\n        // is not started yet we might lose bytes.\n        let rx_transfer = unsafe {\n            // If we don't assign future to a variable, the data register pointer\n            // is held across an await and makes the future non-Send.\n            self.rx_dma\n                .as_mut()\n                .unwrap()\n                .read(self.inner.regs().dr().as_ptr() as *const _, rx, T::RX_DREQ, false)\n        };\n\n        let tx_ch = self.tx_dma.as_mut().unwrap();\n        // If we don't assign future to a variable, the data register pointer\n        // is held across an await and makes the future non-Send.\n        let tx_transfer = async {\n            let p = self.inner.regs();\n            unsafe {\n                tx_ch.write(tx, p.dr().as_ptr() as *mut _, T::TX_DREQ, false).await;\n\n                if rx.len() > tx.len() {\n                    let write_bytes_len = rx.len() - tx.len();\n                    // write dummy data\n                    // this will disable incrementation of the buffers\n                    tx_ch\n                        .write_zeros(write_bytes_len, p.dr().as_ptr() as *mut u8, T::TX_DREQ)\n                        .await\n                }\n            }\n        };\n        join(tx_transfer, rx_transfer).await;\n\n        // if tx > rx we should clear any overflow of the FIFO SPI buffer\n        if tx.len() > rx.len() {\n            let p = self.inner.regs();\n            while p.sr().read().bsy() {}\n\n            // clear RX FIFO contents to prevent stale reads\n            while p.sr().read().rne() {\n                let _: u16 = p.dr().read().data();\n            }\n            // clear RX overrun interrupt\n            p.icr().write(|w| w.set_roric(true));\n        }\n\n        Ok(())\n    }\n}\n\ntrait SealedMode {}\n\ntrait SealedInstance {\n    const TX_DREQ: pac::dma::vals::TreqSel;\n    const RX_DREQ: pac::dma::vals::TreqSel;\n\n    fn regs(&self) -> pac::spi::Spi;\n}\n\n/// Mode.\n#[allow(private_bounds)]\npub trait Mode: SealedMode {}\n\n/// SPI instance trait.\n#[allow(private_bounds)]\npub trait Instance: SealedInstance + PeripheralType {}\n\nmacro_rules! impl_instance {\n    ($type:ident, $irq:ident, $tx_dreq:expr, $rx_dreq:expr) => {\n        impl SealedInstance for peripherals::$type {\n            const TX_DREQ: pac::dma::vals::TreqSel = $tx_dreq;\n            const RX_DREQ: pac::dma::vals::TreqSel = $rx_dreq;\n\n            fn regs(&self) -> pac::spi::Spi {\n                pac::$type\n            }\n        }\n        impl Instance for peripherals::$type {}\n    };\n}\n\nimpl_instance!(\n    SPI0,\n    Spi0,\n    pac::dma::vals::TreqSel::SPI0_TX,\n    pac::dma::vals::TreqSel::SPI0_RX\n);\nimpl_instance!(\n    SPI1,\n    Spi1,\n    pac::dma::vals::TreqSel::SPI1_TX,\n    pac::dma::vals::TreqSel::SPI1_RX\n);\n\n/// CLK pin.\npub trait ClkPin<T: Instance>: GpioPin {}\n/// CS pin.\npub trait CsPin<T: Instance>: GpioPin {}\n/// MOSI pin.\npub trait MosiPin<T: Instance>: GpioPin {}\n/// MISO pin.\npub trait MisoPin<T: Instance>: GpioPin {}\n\nmacro_rules! impl_pin {\n    ($pin:ident, $instance:ident, $function:ident) => {\n        impl $function<peripherals::$instance> for peripherals::$pin {}\n    };\n}\n\nimpl_pin!(PIN_0, SPI0, MisoPin);\nimpl_pin!(PIN_1, SPI0, CsPin);\nimpl_pin!(PIN_2, SPI0, ClkPin);\nimpl_pin!(PIN_3, SPI0, MosiPin);\nimpl_pin!(PIN_4, SPI0, MisoPin);\nimpl_pin!(PIN_5, SPI0, CsPin);\nimpl_pin!(PIN_6, SPI0, ClkPin);\nimpl_pin!(PIN_7, SPI0, MosiPin);\nimpl_pin!(PIN_8, SPI1, MisoPin);\nimpl_pin!(PIN_9, SPI1, CsPin);\nimpl_pin!(PIN_10, SPI1, ClkPin);\nimpl_pin!(PIN_11, SPI1, MosiPin);\nimpl_pin!(PIN_12, SPI1, MisoPin);\nimpl_pin!(PIN_13, SPI1, CsPin);\nimpl_pin!(PIN_14, SPI1, ClkPin);\nimpl_pin!(PIN_15, SPI1, MosiPin);\nimpl_pin!(PIN_16, SPI0, MisoPin);\nimpl_pin!(PIN_17, SPI0, CsPin);\nimpl_pin!(PIN_18, SPI0, ClkPin);\nimpl_pin!(PIN_19, SPI0, MosiPin);\nimpl_pin!(PIN_20, SPI0, MisoPin);\nimpl_pin!(PIN_21, SPI0, CsPin);\nimpl_pin!(PIN_22, SPI0, ClkPin);\nimpl_pin!(PIN_23, SPI0, MosiPin);\nimpl_pin!(PIN_24, SPI1, MisoPin);\nimpl_pin!(PIN_25, SPI1, CsPin);\nimpl_pin!(PIN_26, SPI1, ClkPin);\nimpl_pin!(PIN_27, SPI1, MosiPin);\nimpl_pin!(PIN_28, SPI1, MisoPin);\nimpl_pin!(PIN_29, SPI1, CsPin);\n#[cfg(feature = \"rp235xb\")]\nimpl_pin!(PIN_30, SPI1, ClkPin);\n#[cfg(feature = \"rp235xb\")]\nimpl_pin!(PIN_31, SPI1, MosiPin);\n#[cfg(feature = \"rp235xb\")]\nimpl_pin!(PIN_32, SPI0, MisoPin);\n#[cfg(feature = \"rp235xb\")]\nimpl_pin!(PIN_33, SPI0, CsPin);\n#[cfg(feature = \"rp235xb\")]\nimpl_pin!(PIN_34, SPI0, ClkPin);\n#[cfg(feature = \"rp235xb\")]\nimpl_pin!(PIN_35, SPI0, MosiPin);\n#[cfg(feature = \"rp235xb\")]\nimpl_pin!(PIN_36, SPI0, MisoPin);\n#[cfg(feature = \"rp235xb\")]\nimpl_pin!(PIN_37, SPI0, CsPin);\n#[cfg(feature = \"rp235xb\")]\nimpl_pin!(PIN_38, SPI0, ClkPin);\n#[cfg(feature = \"rp235xb\")]\nimpl_pin!(PIN_39, SPI0, MosiPin);\n#[cfg(feature = \"rp235xb\")]\nimpl_pin!(PIN_40, SPI1, MisoPin);\n#[cfg(feature = \"rp235xb\")]\nimpl_pin!(PIN_41, SPI1, CsPin);\n#[cfg(feature = \"rp235xb\")]\nimpl_pin!(PIN_42, SPI1, ClkPin);\n#[cfg(feature = \"rp235xb\")]\nimpl_pin!(PIN_43, SPI1, MosiPin);\n#[cfg(feature = \"rp235xb\")]\nimpl_pin!(PIN_44, SPI1, MisoPin);\n#[cfg(feature = \"rp235xb\")]\nimpl_pin!(PIN_45, SPI1, CsPin);\n#[cfg(feature = \"rp235xb\")]\nimpl_pin!(PIN_46, SPI1, ClkPin);\n#[cfg(feature = \"rp235xb\")]\nimpl_pin!(PIN_47, SPI1, MosiPin);\n\nmacro_rules! impl_mode {\n    ($name:ident) => {\n        impl SealedMode for $name {}\n        impl Mode for $name {}\n    };\n}\n\n/// Blocking mode.\npub struct Blocking;\n/// Async mode.\npub struct Async;\n\nimpl_mode!(Blocking);\nimpl_mode!(Async);\n\n// ====================\n\nimpl<'d, T: Instance, M: Mode> embedded_hal_02::blocking::spi::Transfer<u8> for Spi<'d, T, M> {\n    type Error = Error;\n    fn transfer<'w>(&mut self, words: &'w mut [u8]) -> Result<&'w [u8], Self::Error> {\n        self.blocking_transfer_in_place(words)?;\n        Ok(words)\n    }\n}\n\nimpl<'d, T: Instance, M: Mode> embedded_hal_02::blocking::spi::Write<u8> for Spi<'d, T, M> {\n    type Error = Error;\n\n    fn write(&mut self, words: &[u8]) -> Result<(), Self::Error> {\n        self.blocking_write(words)\n    }\n}\n\nimpl embedded_hal_1::spi::Error for Error {\n    fn kind(&self) -> embedded_hal_1::spi::ErrorKind {\n        match *self {}\n    }\n}\n\nimpl<'d, T: Instance, M: Mode> embedded_hal_1::spi::ErrorType for Spi<'d, T, M> {\n    type Error = Error;\n}\n\nimpl<'d, T: Instance, M: Mode> embedded_hal_1::spi::SpiBus<u8> for Spi<'d, T, M> {\n    fn flush(&mut self) -> Result<(), Self::Error> {\n        Ok(())\n    }\n\n    fn read(&mut self, words: &mut [u8]) -> Result<(), Self::Error> {\n        self.blocking_transfer(words, &[])\n    }\n\n    fn write(&mut self, words: &[u8]) -> Result<(), Self::Error> {\n        self.blocking_write(words)\n    }\n\n    fn transfer(&mut self, read: &mut [u8], write: &[u8]) -> Result<(), Self::Error> {\n        self.blocking_transfer(read, write)\n    }\n\n    fn transfer_in_place(&mut self, words: &mut [u8]) -> Result<(), Self::Error> {\n        self.blocking_transfer_in_place(words)\n    }\n}\n\nimpl<'d, T: Instance> embedded_hal_async::spi::SpiBus<u8> for Spi<'d, T, Async> {\n    async fn flush(&mut self) -> Result<(), Self::Error> {\n        Ok(())\n    }\n\n    async fn write(&mut self, words: &[u8]) -> Result<(), Self::Error> {\n        self.write(words).await\n    }\n\n    async fn read(&mut self, words: &mut [u8]) -> Result<(), Self::Error> {\n        self.read(words).await\n    }\n\n    async fn transfer(&mut self, read: &mut [u8], write: &[u8]) -> Result<(), Self::Error> {\n        self.transfer(read, write).await\n    }\n\n    async fn transfer_in_place(&mut self, words: &mut [u8]) -> Result<(), Self::Error> {\n        self.transfer_in_place(words).await\n    }\n}\n\nimpl<'d, T: Instance, M: Mode> SetConfig for Spi<'d, T, M> {\n    type Config = Config;\n    type ConfigError = ();\n    fn set_config(&mut self, config: &Self::Config) -> Result<(), ()> {\n        self.set_config(config);\n\n        Ok(())\n    }\n}\n"
  },
  {
    "path": "embassy-rp/src/spinlock.rs",
    "content": "use crate::pac;\n\npub struct Spinlock<const N: usize>(core::marker::PhantomData<()>)\nwhere\n    Spinlock<N>: SpinlockValid;\n\nimpl<const N: usize> Spinlock<N>\nwhere\n    Spinlock<N>: SpinlockValid,\n{\n    /// Try to claim the spinlock. Will return `Some(Self)` if the lock is obtained, and `None` if the lock is\n    /// already in use somewhere else.\n    pub fn try_claim() -> Option<Self> {\n        let lock = pac::SIO.spinlock(N).read();\n        if lock > 0 {\n            Some(Self(core::marker::PhantomData))\n        } else {\n            None\n        }\n    }\n\n    /// Clear a locked spin-lock.\n    ///\n    /// # Safety\n    ///\n    /// Only call this function if you hold the spin-lock.\n    pub unsafe fn release() {\n        // Write (any value): release the lock\n        pac::SIO.spinlock(N).write_value(1);\n    }\n}\n\nimpl<const N: usize> Drop for Spinlock<N>\nwhere\n    Spinlock<N>: SpinlockValid,\n{\n    fn drop(&mut self) {\n        // This is safe because we own the object, and hence hold the lock.\n        unsafe { Self::release() }\n    }\n}\n\npub trait SpinlockValid {}\nimpl SpinlockValid for Spinlock<0> {}\nimpl SpinlockValid for Spinlock<1> {}\nimpl SpinlockValid for Spinlock<2> {}\nimpl SpinlockValid for Spinlock<3> {}\nimpl SpinlockValid for Spinlock<4> {}\nimpl SpinlockValid for Spinlock<5> {}\nimpl SpinlockValid for Spinlock<6> {}\nimpl SpinlockValid for Spinlock<7> {}\nimpl SpinlockValid for Spinlock<8> {}\nimpl SpinlockValid for Spinlock<9> {}\nimpl SpinlockValid for Spinlock<10> {}\nimpl SpinlockValid for Spinlock<11> {}\nimpl SpinlockValid for Spinlock<12> {}\nimpl SpinlockValid for Spinlock<13> {}\nimpl SpinlockValid for Spinlock<14> {}\nimpl SpinlockValid for Spinlock<15> {}\nimpl SpinlockValid for Spinlock<16> {}\nimpl SpinlockValid for Spinlock<17> {}\nimpl SpinlockValid for Spinlock<18> {}\nimpl SpinlockValid for Spinlock<19> {}\nimpl SpinlockValid for Spinlock<20> {}\nimpl SpinlockValid for Spinlock<21> {}\nimpl SpinlockValid for Spinlock<22> {}\nimpl SpinlockValid for Spinlock<23> {}\nimpl SpinlockValid for Spinlock<24> {}\nimpl SpinlockValid for Spinlock<25> {}\nimpl SpinlockValid for Spinlock<26> {}\nimpl SpinlockValid for Spinlock<27> {}\nimpl SpinlockValid for Spinlock<28> {}\nimpl SpinlockValid for Spinlock<29> {}\nimpl SpinlockValid for Spinlock<30> {}\nimpl SpinlockValid for Spinlock<31> {}\n"
  },
  {
    "path": "embassy-rp/src/spinlock_mutex.rs",
    "content": "//! Mutex implementation utilizing an hardware spinlock\n\nuse core::marker::PhantomData;\nuse core::sync::atomic::Ordering;\n\nuse embassy_sync::blocking_mutex::raw::RawMutex;\n\nuse crate::spinlock::{Spinlock, SpinlockValid};\n\n/// A mutex that allows borrowing data across executors and interrupts by utilizing an hardware spinlock\n///\n/// # Safety\n///\n/// This mutex is safe to share between different executors and interrupts.\npub struct SpinlockRawMutex<const N: usize> {\n    _phantom: PhantomData<()>,\n}\nunsafe impl<const N: usize> Send for SpinlockRawMutex<N> {}\nunsafe impl<const N: usize> Sync for SpinlockRawMutex<N> {}\n\nimpl<const N: usize> SpinlockRawMutex<N> {\n    /// Create a new `SpinlockRawMutex`.\n    pub const fn new() -> Self {\n        Self { _phantom: PhantomData }\n    }\n}\n\nunsafe impl<const N: usize> RawMutex for SpinlockRawMutex<N>\nwhere\n    Spinlock<N>: SpinlockValid,\n{\n    const INIT: Self = Self::new();\n\n    fn lock<R>(&self, f: impl FnOnce() -> R) -> R {\n        // Store the initial interrupt state in stack variable\n        let interrupts_active = cortex_m::register::primask::read().is_active();\n\n        // Spin until we get the lock\n        loop {\n            // Need to disable interrupts to ensure that we will not deadlock\n            // if an interrupt or higher prio locks the spinlock after we acquire the lock\n            cortex_m::interrupt::disable();\n            // Ensure the compiler doesn't re-order accesses and violate safety here\n            core::sync::atomic::compiler_fence(Ordering::SeqCst);\n            if let Some(lock) = Spinlock::<N>::try_claim() {\n                // We just acquired the lock.\n                // 1. Forget it, so we don't immediately unlock\n                core::mem::forget(lock);\n                break;\n            }\n            // We didn't get the lock, enable interrupts if they were enabled before we started\n            if interrupts_active {\n                // safety: interrupts are only enabled, if they had been enabled before\n                unsafe {\n                    cortex_m::interrupt::enable();\n                }\n            }\n        }\n\n        let retval = f();\n\n        // Ensure the compiler doesn't re-order accesses and violate safety here\n        core::sync::atomic::compiler_fence(Ordering::SeqCst);\n        // Release the spinlock to allow others to lock mutex again\n        // safety: this point is only reached a spinlock was acquired before\n        unsafe {\n            Spinlock::<N>::release();\n        }\n\n        // Re-enable interrupts if they were enabled before the mutex was locked\n        if interrupts_active {\n            // safety: interrupts are only enabled, if they had been enabled before\n            unsafe {\n                cortex_m::interrupt::enable();\n            }\n        }\n\n        retval\n    }\n}\n\npub mod blocking_mutex {\n    //! Mutex implementation utilizing an hardware spinlock\n    use embassy_sync::blocking_mutex::Mutex;\n\n    use crate::spinlock_mutex::SpinlockRawMutex;\n    /// A mutex that allows borrowing data across executors and interrupts by utilizing an hardware spinlock.\n    ///\n    /// # Safety\n    ///\n    /// This mutex is safe to share between different executors and interrupts.\n    pub type SpinlockMutex<const N: usize, T> = Mutex<SpinlockRawMutex<N>, T>;\n}\n"
  },
  {
    "path": "embassy-rp/src/time_driver.rs",
    "content": "//! Timer driver.\nuse core::cell::{Cell, RefCell};\n\nuse critical_section::CriticalSection;\nuse embassy_sync::blocking_mutex::Mutex;\nuse embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;\nuse embassy_time_driver::Driver;\nuse embassy_time_queue_utils::Queue;\n#[cfg(feature = \"rp2040\")]\nuse pac::TIMER;\n#[cfg(feature = \"_rp235x\")]\nuse pac::TIMER0 as TIMER;\n\nuse crate::interrupt::InterruptExt;\nuse crate::{interrupt, pac};\n\nstruct AlarmState {\n    timestamp: Cell<u64>,\n}\nunsafe impl Send for AlarmState {}\n\nstruct TimerDriver {\n    alarms: Mutex<CriticalSectionRawMutex, AlarmState>,\n    queue: Mutex<CriticalSectionRawMutex, RefCell<Queue>>,\n}\n\nembassy_time_driver::time_driver_impl!(static DRIVER: TimerDriver = TimerDriver{\n    alarms:  Mutex::const_new(CriticalSectionRawMutex::new(), AlarmState {\n        timestamp: Cell::new(0),\n    }),\n    queue: Mutex::new(RefCell::new(Queue::new()))\n});\n\nimpl Driver for TimerDriver {\n    fn now(&self) -> u64 {\n        loop {\n            let hi = TIMER.timerawh().read();\n            let lo = TIMER.timerawl().read();\n            let hi2 = TIMER.timerawh().read();\n            if hi == hi2 {\n                return (hi as u64) << 32 | (lo as u64);\n            }\n        }\n    }\n\n    fn schedule_wake(&self, at: u64, waker: &core::task::Waker) {\n        critical_section::with(|cs| {\n            let mut queue = self.queue.borrow(cs).borrow_mut();\n\n            if queue.schedule_wake(at, waker) {\n                let mut next = queue.next_expiration(self.now());\n                while !self.set_alarm(cs, next) {\n                    next = queue.next_expiration(self.now());\n                }\n            }\n        })\n    }\n}\n\nimpl TimerDriver {\n    fn set_alarm(&self, cs: CriticalSection, timestamp: u64) -> bool {\n        let n = 0;\n        let alarm = &self.alarms.borrow(cs);\n        alarm.timestamp.set(timestamp);\n\n        // Arm it.\n        // Note that we're not checking the high bits at all. This means the irq may fire early\n        // if the alarm is more than 72 minutes (2^32 us) in the future. This is OK, since on irq fire\n        // it is checked if the alarm time has passed.\n        TIMER.alarm(n).write_value(timestamp as u32);\n\n        let now = self.now();\n        if timestamp <= now {\n            // If alarm timestamp has passed the alarm will not fire.\n            // Disarm the alarm and return `false` to indicate that.\n            TIMER.armed().write(|w| w.set_armed(1 << n));\n\n            alarm.timestamp.set(u64::MAX);\n\n            false\n        } else {\n            true\n        }\n    }\n\n    fn check_alarm(&self) {\n        let n = 0;\n        critical_section::with(|cs| {\n            // clear the irq\n            TIMER.intr().write(|w| w.set_alarm(n, true));\n\n            let alarm = &self.alarms.borrow(cs);\n            let timestamp = alarm.timestamp.get();\n            if timestamp <= self.now() {\n                self.trigger_alarm(cs)\n            } else {\n                // Not elapsed, arm it again.\n                // This can happen if it was set more than 2^32 us in the future.\n                TIMER.alarm(n).write_value(timestamp as u32);\n            }\n        });\n    }\n\n    fn trigger_alarm(&self, cs: CriticalSection) {\n        let mut next = self.queue.borrow(cs).borrow_mut().next_expiration(self.now());\n        while !self.set_alarm(cs, next) {\n            next = self.queue.borrow(cs).borrow_mut().next_expiration(self.now());\n        }\n    }\n}\n\n/// safety: must be called exactly once at bootup\npub unsafe fn init() {\n    // init alarms\n    critical_section::with(|cs| {\n        let alarm = DRIVER.alarms.borrow(cs);\n        alarm.timestamp.set(u64::MAX);\n    });\n\n    // enable irq\n    TIMER.inte().write(|w| {\n        w.set_alarm(0, true);\n    });\n    #[cfg(feature = \"rp2040\")]\n    {\n        interrupt::TIMER_IRQ_0.enable();\n    }\n    #[cfg(feature = \"_rp235x\")]\n    {\n        interrupt::TIMER0_IRQ_0.enable();\n    }\n}\n\n#[cfg(all(feature = \"rt\", feature = \"rp2040\"))]\n#[interrupt]\nfn TIMER_IRQ_0() {\n    DRIVER.check_alarm()\n}\n\n#[cfg(all(feature = \"rt\", feature = \"_rp235x\"))]\n#[interrupt]\nfn TIMER0_IRQ_0() {\n    DRIVER.check_alarm()\n}\n"
  },
  {
    "path": "embassy-rp/src/trng.rs",
    "content": "//! True Random Number Generator (TRNG) driver.\n\nuse core::future::poll_fn;\nuse core::marker::PhantomData;\nuse core::ops::Not;\nuse core::task::Poll;\n\nuse embassy_hal_internal::{Peri, PeripheralType};\nuse embassy_sync::waitqueue::AtomicWaker;\n\nuse crate::interrupt::typelevel::{Binding, Interrupt};\nuse crate::peripherals::TRNG;\nuse crate::{interrupt, pac};\n\ntrait SealedInstance {\n    fn regs() -> pac::trng::Trng;\n    fn waker() -> &'static AtomicWaker;\n}\n\n/// TRNG peripheral instance.\n#[allow(private_bounds)]\npub trait Instance: SealedInstance + PeripheralType {\n    /// Interrupt for this peripheral.\n    type Interrupt: Interrupt;\n}\n\nimpl SealedInstance for TRNG {\n    fn regs() -> rp_pac::trng::Trng {\n        pac::TRNG\n    }\n\n    fn waker() -> &'static AtomicWaker {\n        static WAKER: AtomicWaker = AtomicWaker::new();\n        &WAKER\n    }\n}\n\nimpl Instance for TRNG {\n    type Interrupt = interrupt::typelevel::TRNG_IRQ;\n}\n\n#[derive(Copy, Clone, Debug)]\n#[allow(missing_docs)]\n/// TRNG ROSC Inverter chain length options.\npub enum InverterChainLength {\n    None = 0,\n    One,\n    Two,\n    Three,\n    Four,\n}\n\nimpl From<InverterChainLength> for u8 {\n    fn from(value: InverterChainLength) -> Self {\n        value as u8\n    }\n}\n\n/// Configuration for the TRNG.\n///\n/// - Three built in entropy checks\n/// - ROSC frequency controlled by selecting one of ROSC chain lengths\n/// - Sample period in terms of system clock ticks\n///\n///\n/// Default configuration is based on the following from documentation:\n///\n/// ----\n///\n/// RP2350 Datasheet 12.12.2\n///\n/// ...\n///\n/// When configuring the TRNG block, consider the following principles:\n/// • As average generation time increases, result quality increases and failed entropy checks decrease.\n/// • A low sample count decreases average generation time, but increases the chance of NIST test-failing results and\n/// failed entropy checks.\n/// For acceptable results with an average generation time of about 2 milliseconds, use ROSC chain length settings of 0 or\n/// 1 and sample count settings of 20-25.\n/// Larger sample count settings (e.g. 100) provide proportionately slower average generation times. These settings\n/// significantly reduce, but do not eliminate NIST test failures and entropy check failures. Results occasionally take an\n/// especially long time to generate.\n///\n/// ---\n///\n/// Note, Pico SDK and Bootrom don't use any of the entropy checks and sample the ROSC directly\n/// by setting the sample period to 0. Random data collected this way is then passed through\n/// either hardware accelerated SHA256 (Bootrom) or xoroshiro128** (version 1.0!).\n#[non_exhaustive]\n#[derive(Copy, Clone, Debug)]\npub struct Config {\n    /// Bypass TRNG autocorrelation test\n    pub disable_autocorrelation_test: bool,\n    /// Bypass CRNGT test\n    pub disable_crngt_test: bool,\n    /// When set, the Von-Neuman balancer is bypassed (including the\n    /// 32 consecutive bits test)\n    pub disable_von_neumann_balancer: bool,\n    /// Sets the number of rng_clk cycles between two consecutive\n    /// ring oscillator samples.\n    /// Note: If the von Neumann decorrelator is bypassed, the minimum value for\n    /// sample counter must not be less than seventeen\n    pub sample_count: u32,\n    /// Selects the number of inverters (out of four possible\n    /// selections) in the ring oscillator (the entropy source). Higher values select\n    /// longer inverter chain lengths.\n    pub inverter_chain_length: InverterChainLength,\n}\n\nimpl Default for Config {\n    fn default() -> Self {\n        Config {\n            // WARNING: Disabling these tests increases likelihood of poor rng results.\n            disable_autocorrelation_test: false,\n            disable_crngt_test: false,\n            disable_von_neumann_balancer: false,\n            sample_count: 25,\n            inverter_chain_length: InverterChainLength::One,\n        }\n    }\n}\n\n/// True Random Number Generator Driver for RP2350\n///\n/// This driver provides async and blocking options.\n///\n/// See [Config] for configuration details.\n///\n/// Usage example:\n/// ```no_run\n/// use embassy_executor::Spawner;\n/// use embassy_rp::trng::Trng;\n/// use embassy_rp::peripherals::TRNG;\n/// use embassy_rp::bind_interrupts;\n///\n/// bind_interrupts!(struct Irqs {\n///     TRNG_IRQ => embassy_rp::trng::InterruptHandler<TRNG>;\n/// });\n///\n/// #[embassy_executor::main]\n/// async fn main(spawner: Spawner) {\n///     let peripherals = embassy_rp::init(Default::default());\n///     let mut trng = Trng::new(peripherals.TRNG, Irqs, embassy_rp::trng::Config::default());\n///\n///     let mut randomness = [0u8; 58];\n///     loop {\n///         trng.fill_bytes(&mut randomness).await;\n///         assert_ne!(randomness, [0u8; 58]);\n///     }\n///}\n/// ```\npub struct Trng<'d, T: Instance> {\n    phantom: PhantomData<&'d mut T>,\n    config: Config,\n}\n\n/// 12.12.1. Overview\n/// On request, the TRNG block generates a block of 192 entropy bits generated by automatically processing a series of\n/// periodic samples from the TRNG block’s internal Ring Oscillator (ROSC).\nconst TRNG_BLOCK_SIZE_BITS: usize = 192;\nconst TRNG_BLOCK_SIZE_BYTES: usize = TRNG_BLOCK_SIZE_BITS / 8;\n\nimpl<'d, T: Instance> Trng<'d, T> {\n    /// Create a new TRNG driver.\n    pub fn new(_trng: Peri<'d, T>, _irq: impl Binding<T::Interrupt, InterruptHandler<T>> + 'd, config: Config) -> Self {\n        let trng = Trng {\n            phantom: PhantomData,\n            config: config,\n        };\n        trng.initialize_rng();\n        trng\n    }\n\n    fn start_rng(&self) {\n        let regs = T::regs();\n        let source_enable_register = regs.rnd_source_enable();\n        // Enable TRNG ROSC\n        source_enable_register.write(|w| w.set_rnd_src_en(true));\n    }\n\n    fn stop_rng(&self) {\n        let regs = T::regs();\n        let source_enable_register = regs.rnd_source_enable();\n        source_enable_register.write(|w| w.set_rnd_src_en(false));\n        let reset_bits_counter_register = regs.rst_bits_counter();\n        reset_bits_counter_register.write(|w| w.set_rst_bits_counter(true));\n    }\n\n    fn initialize_rng(&self) {\n        let regs = T::regs();\n\n        regs.rng_imr().write(|w| w.set_ehr_valid_int_mask(false));\n\n        let trng_config_register = regs.trng_config();\n        trng_config_register.write(|w| {\n            w.set_rnd_src_sel(self.config.inverter_chain_length.clone().into());\n        });\n\n        let sample_count_register = regs.sample_cnt1();\n        sample_count_register.write(|w| {\n            *w = self.config.sample_count;\n        });\n\n        let debug_control_register = regs.trng_debug_control();\n        debug_control_register.write(|w| {\n            w.set_auto_correlate_bypass(self.config.disable_autocorrelation_test);\n            w.set_trng_crngt_bypass(self.config.disable_crngt_test);\n            w.set_vnc_bypass(self.config.disable_von_neumann_balancer);\n        });\n    }\n\n    fn enable_irq(&self) {\n        unsafe { T::Interrupt::enable() }\n    }\n\n    fn disable_irq(&self) {\n        T::Interrupt::disable();\n    }\n\n    fn blocking_wait_for_successful_generation(&self) {\n        let regs = T::regs();\n\n        let trng_busy_register = regs.trng_busy();\n        let trng_valid_register = regs.trng_valid();\n\n        let mut success = false;\n        while success.not() {\n            while trng_busy_register.read().trng_busy() {}\n            if trng_valid_register.read().ehr_valid().not() {\n                if regs.rng_isr().read().autocorr_err() {\n                    regs.trng_sw_reset().write(|w| w.set_trng_sw_reset(true));\n                    // Fixed delay is required after TRNG soft reset. This read is sufficient.\n                    regs.trng_sw_reset().read();\n                    self.initialize_rng();\n                    self.start_rng();\n                } else {\n                    panic!(\"RNG not busy, but ehr is not valid!\")\n                }\n            } else {\n                success = true\n            }\n        }\n    }\n\n    fn read_ehr_registers_into_array(&mut self, buffer: &mut [u8; TRNG_BLOCK_SIZE_BYTES]) {\n        let regs = T::regs();\n        let ehr_data_regs = [\n            regs.ehr_data0(),\n            regs.ehr_data1(),\n            regs.ehr_data2(),\n            regs.ehr_data3(),\n            regs.ehr_data4(),\n            regs.ehr_data5(),\n        ];\n\n        for (i, reg) in ehr_data_regs.iter().enumerate() {\n            buffer[i * 4..i * 4 + 4].copy_from_slice(&reg.read().to_ne_bytes());\n        }\n    }\n\n    fn blocking_read_ehr_registers_into_array(&mut self, buffer: &mut [u8; TRNG_BLOCK_SIZE_BYTES]) {\n        self.blocking_wait_for_successful_generation();\n        self.read_ehr_registers_into_array(buffer);\n    }\n\n    /// Fill the buffer with random bytes, async version.\n    pub async fn fill_bytes(&mut self, destination: &mut [u8]) {\n        if destination.is_empty() {\n            return; // Nothing to fill\n        }\n\n        self.start_rng();\n        self.enable_irq();\n\n        let mut bytes_transferred = 0usize;\n        let mut buffer = [0u8; TRNG_BLOCK_SIZE_BYTES];\n\n        let regs = T::regs();\n\n        let trng_busy_register = regs.trng_busy();\n        let trng_valid_register = regs.trng_valid();\n\n        let waker = T::waker();\n\n        let destination_length = destination.len();\n\n        poll_fn(|context| {\n            waker.register(context.waker());\n            if bytes_transferred == destination_length {\n                self.stop_rng();\n                self.disable_irq();\n                Poll::Ready(())\n            } else {\n                if trng_busy_register.read().trng_busy() {\n                    Poll::Pending\n                } else {\n                    // If woken up and EHR is *not* valid, assume the trng has been reset and reinitialize, restart.\n                    if trng_valid_register.read().ehr_valid().not() {\n                        self.initialize_rng();\n                        self.start_rng();\n                        return Poll::Pending;\n                    }\n                    self.read_ehr_registers_into_array(&mut buffer);\n                    let remaining = destination_length - bytes_transferred;\n                    if remaining > TRNG_BLOCK_SIZE_BYTES {\n                        destination[bytes_transferred..bytes_transferred + TRNG_BLOCK_SIZE_BYTES]\n                            .copy_from_slice(&buffer);\n                        bytes_transferred += TRNG_BLOCK_SIZE_BYTES\n                    } else {\n                        destination[bytes_transferred..bytes_transferred + remaining]\n                            .copy_from_slice(&buffer[0..remaining]);\n                        bytes_transferred += remaining\n                    }\n                    if bytes_transferred == destination_length {\n                        self.stop_rng();\n                        self.disable_irq();\n                        Poll::Ready(())\n                    } else {\n                        Poll::Pending\n                    }\n                }\n            }\n        })\n        .await\n    }\n\n    /// Fill the buffer with random bytes, blocking version.\n    pub fn blocking_fill_bytes(&mut self, destination: &mut [u8]) {\n        if destination.is_empty() {\n            return; // Nothing to fill\n        }\n        self.start_rng();\n\n        let mut buffer = [0u8; TRNG_BLOCK_SIZE_BYTES];\n\n        for chunk in destination.chunks_mut(TRNG_BLOCK_SIZE_BYTES) {\n            self.blocking_wait_for_successful_generation();\n            self.blocking_read_ehr_registers_into_array(&mut buffer);\n            chunk.copy_from_slice(&buffer[..chunk.len()])\n        }\n        self.stop_rng()\n    }\n\n    /// Return a random u32, blocking.\n    pub fn blocking_next_u32(&mut self) -> u32 {\n        let regs = T::regs();\n        self.start_rng();\n        self.blocking_wait_for_successful_generation();\n        // 12.12.3 After successful generation, read the last result register, EHR_DATA[5] to\n        // clear all of the result registers.\n        let result = regs.ehr_data5().read();\n        self.stop_rng();\n        result\n    }\n\n    /// Return a random u64, blocking.\n    pub fn blocking_next_u64(&mut self) -> u64 {\n        let regs = T::regs();\n        self.start_rng();\n        self.blocking_wait_for_successful_generation();\n\n        let low = regs.ehr_data4().read() as u64;\n        // 12.12.3 After successful generation, read the last result register, EHR_DATA[5] to\n        // clear all of the result registers.\n        let result = (regs.ehr_data5().read() as u64) << 32 | low;\n        self.stop_rng();\n        result\n    }\n}\n\nimpl<'d, T: Instance> rand_core_06::RngCore for Trng<'d, T> {\n    fn next_u32(&mut self) -> u32 {\n        self.blocking_next_u32()\n    }\n\n    fn next_u64(&mut self) -> u64 {\n        self.blocking_next_u64()\n    }\n\n    fn fill_bytes(&mut self, dest: &mut [u8]) {\n        self.blocking_fill_bytes(dest);\n    }\n\n    fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), rand_core_06::Error> {\n        self.blocking_fill_bytes(dest);\n        Ok(())\n    }\n}\n\nimpl<'d, T: Instance> rand_core_06::CryptoRng for Trng<'d, T> {}\n\nimpl<'d, T: Instance> rand_core_09::RngCore for Trng<'d, T> {\n    fn next_u32(&mut self) -> u32 {\n        self.blocking_next_u32()\n    }\n\n    fn next_u64(&mut self) -> u64 {\n        self.blocking_next_u64()\n    }\n\n    fn fill_bytes(&mut self, dest: &mut [u8]) {\n        self.blocking_fill_bytes(dest);\n    }\n}\n\nimpl<'d, T: Instance> rand_core_09::CryptoRng for Trng<'d, T> {}\n\n/// TRNG interrupt handler.\npub struct InterruptHandler<T: Instance> {\n    _trng: PhantomData<T>,\n}\n\nimpl<T: Instance> interrupt::typelevel::Handler<T::Interrupt> for InterruptHandler<T> {\n    unsafe fn on_interrupt() {\n        let regs = T::regs();\n        let isr = regs.rng_isr().read();\n        if isr.ehr_valid() {\n            regs.rng_icr().write(|w| {\n                w.set_ehr_valid(true);\n            });\n            T::waker().wake();\n        } else if isr.crngt_err() {\n            warn!(\"TRNG CRNGT error! Increase sample count to reduce likelihood\");\n            regs.rng_icr().write(|w| {\n                w.set_crngt_err(true);\n            });\n        } else if isr.vn_err() {\n            warn!(\"TRNG Von-Neumann balancer error! Increase sample count to reduce likelihood\");\n            regs.rng_icr().write(|w| {\n                w.set_vn_err(true);\n            });\n        } else if isr.autocorr_err() {\n            // 12.12.5. List of Registers\n            // ...\n            // TRNG: RNG_ISR Register\n            // ...\n            // AUTOCORR_ERR: 1 indicates Autocorrelation test failed four times in a row.\n            // When set, RNG ceases functioning until next reset\n            warn!(\"TRNG Autocorrect error! Resetting TRNG. Increase sample count to reduce likelihood\");\n            regs.trng_sw_reset().write(|w| {\n                w.set_trng_sw_reset(true);\n            });\n            // Fixed delay is required after TRNG soft reset, this read is sufficient.\n            regs.trng_sw_reset().read();\n            // Wake up to reinitialize and restart the TRNG.\n            T::waker().wake();\n        }\n    }\n}\n"
  },
  {
    "path": "embassy-rp/src/uart/buffered.rs",
    "content": "//! Buffered UART driver.\nuse core::future::Future;\nuse core::slice;\nuse core::sync::atomic::{AtomicU8, Ordering};\n\nuse embassy_hal_internal::atomic_ring_buffer::RingBuffer;\n\nuse super::*;\n\npub struct State {\n    tx_waker: AtomicWaker,\n    tx_buf: RingBuffer,\n    rx_waker: AtomicWaker,\n    rx_buf: RingBuffer,\n    rx_error: AtomicU8,\n}\n\n// these must match bits 8..11 in UARTDR\nconst RXE_OVERRUN: u8 = 8;\nconst RXE_BREAK: u8 = 4;\nconst RXE_PARITY: u8 = 2;\nconst RXE_FRAMING: u8 = 1;\n\nimpl State {\n    pub const fn new() -> Self {\n        Self {\n            rx_buf: RingBuffer::new(),\n            tx_buf: RingBuffer::new(),\n            rx_waker: AtomicWaker::new(),\n            tx_waker: AtomicWaker::new(),\n            rx_error: AtomicU8::new(0),\n        }\n    }\n}\n\n/// Buffered UART driver.\npub struct BufferedUart {\n    pub(super) rx: BufferedUartRx,\n    pub(super) tx: BufferedUartTx,\n}\n\n/// Buffered UART RX handle.\npub struct BufferedUartRx {\n    pub(super) info: &'static Info,\n    pub(super) state: &'static State,\n}\n\n/// Buffered UART TX handle.\npub struct BufferedUartTx {\n    pub(super) info: &'static Info,\n    pub(super) state: &'static State,\n}\n\npub(super) fn init_buffers<'d>(\n    info: &Info,\n    state: &State,\n    tx_buffer: Option<&'d mut [u8]>,\n    rx_buffer: Option<&'d mut [u8]>,\n) {\n    if let Some(tx_buffer) = tx_buffer {\n        let len = tx_buffer.len();\n        unsafe { state.tx_buf.init(tx_buffer.as_mut_ptr(), len) };\n    }\n\n    if let Some(rx_buffer) = rx_buffer {\n        let len = rx_buffer.len();\n        unsafe { state.rx_buf.init(rx_buffer.as_mut_ptr(), len) };\n    }\n\n    // From the datasheet:\n    // \"The transmit interrupt is based on a transition through a level, rather\n    // than on the level itself. When the interrupt and the UART is enabled\n    // before any data is written to the transmit FIFO the interrupt is not set.\n    // The interrupt is only set, after written data leaves the single location\n    // of the transmit FIFO and it becomes empty.\"\n    //\n    // This means we can leave the interrupt enabled the whole time as long as\n    // we clear it after it happens. The downside is that the we manually have\n    // to pend the ISR when we want data transmission to start.\n    info.regs.uartimsc().write(|w| {\n        w.set_rxim(true);\n        w.set_rtim(true);\n        w.set_txim(true);\n    });\n\n    info.interrupt.unpend();\n    unsafe { info.interrupt.enable() };\n}\n\nimpl BufferedUart {\n    /// Create a buffered UART instance.\n    pub fn new<'d, T: Instance>(\n        _uart: Peri<'d, T>,\n        tx: Peri<'d, impl TxPin<T>>,\n        rx: Peri<'d, impl RxPin<T>>,\n        _irq: impl Binding<T::Interrupt, BufferedInterruptHandler<T>>,\n        tx_buffer: &'d mut [u8],\n        rx_buffer: &'d mut [u8],\n        config: Config,\n    ) -> Self {\n        super::Uart::<'d, Async>::init(T::info(), Some(tx.into()), Some(rx.into()), None, None, config);\n        init_buffers(T::info(), T::buffered_state(), Some(tx_buffer), Some(rx_buffer));\n\n        Self {\n            rx: BufferedUartRx {\n                info: T::info(),\n                state: T::buffered_state(),\n            },\n            tx: BufferedUartTx {\n                info: T::info(),\n                state: T::buffered_state(),\n            },\n        }\n    }\n\n    /// Create a buffered UART instance with flow control.\n    pub fn new_with_rtscts<'d, T: Instance>(\n        _uart: Peri<'d, T>,\n        tx: Peri<'d, impl TxPin<T>>,\n        rx: Peri<'d, impl RxPin<T>>,\n        rts: Peri<'d, impl RtsPin<T>>,\n        cts: Peri<'d, impl CtsPin<T>>,\n        _irq: impl Binding<T::Interrupt, BufferedInterruptHandler<T>>,\n        tx_buffer: &'d mut [u8],\n        rx_buffer: &'d mut [u8],\n        config: Config,\n    ) -> Self {\n        super::Uart::<'d, Async>::init(\n            T::info(),\n            Some(tx.into()),\n            Some(rx.into()),\n            Some(rts.into()),\n            Some(cts.into()),\n            config,\n        );\n        init_buffers(T::info(), T::buffered_state(), Some(tx_buffer), Some(rx_buffer));\n\n        Self {\n            rx: BufferedUartRx {\n                info: T::info(),\n                state: T::buffered_state(),\n            },\n            tx: BufferedUartTx {\n                info: T::info(),\n                state: T::buffered_state(),\n            },\n        }\n    }\n\n    /// Write to UART TX buffer blocking execution until done.\n    pub fn blocking_write(&mut self, buffer: &[u8]) -> Result<usize, Error> {\n        self.tx.blocking_write(buffer)\n    }\n\n    /// Flush UART TX blocking execution until done.\n    pub fn blocking_flush(&mut self) -> Result<(), Error> {\n        self.tx.blocking_flush()\n    }\n\n    /// Read from UART RX buffer blocking execution until done.\n    pub fn blocking_read(&mut self, buffer: &mut [u8]) -> Result<usize, Error> {\n        self.rx.blocking_read(buffer)\n    }\n\n    /// Check if UART is busy transmitting.\n    pub fn busy(&self) -> bool {\n        self.tx.busy()\n    }\n\n    /// Wait until TX is empty and send break condition.\n    pub async fn send_break(&mut self, bits: u32) {\n        self.tx.send_break(bits).await\n    }\n\n    /// sets baudrate on runtime\n    pub fn set_baudrate<'d>(&mut self, baudrate: u32) {\n        super::Uart::<'d, Async>::set_baudrate_inner(self.rx.info, baudrate);\n    }\n\n    /// Split into separate RX and TX handles.\n    pub fn split(self) -> (BufferedUartTx, BufferedUartRx) {\n        (self.tx, self.rx)\n    }\n\n    /// Split the Uart into a transmitter and receiver by mutable reference,\n    /// which is particularly useful when having two tasks correlating to\n    /// transmitting and receiving.\n    pub fn split_ref(&mut self) -> (&mut BufferedUartTx, &mut BufferedUartRx) {\n        (&mut self.tx, &mut self.rx)\n    }\n}\n\nimpl BufferedUartRx {\n    /// Create a new buffered UART RX.\n    pub fn new<'d, T: Instance>(\n        _uart: Peri<'d, T>,\n        _irq: impl Binding<T::Interrupt, BufferedInterruptHandler<T>>,\n        rx: Peri<'d, impl RxPin<T>>,\n        rx_buffer: &'d mut [u8],\n        config: Config,\n    ) -> Self {\n        super::Uart::<'d, Async>::init(T::info(), None, Some(rx.into()), None, None, config);\n        init_buffers(T::info(), T::buffered_state(), None, Some(rx_buffer));\n\n        Self {\n            info: T::info(),\n            state: T::buffered_state(),\n        }\n    }\n\n    /// Create a new buffered UART RX with flow control.\n    pub fn new_with_rts<'d, T: Instance>(\n        _uart: Peri<'d, T>,\n        _irq: impl Binding<T::Interrupt, BufferedInterruptHandler<T>>,\n        rx: Peri<'d, impl RxPin<T>>,\n        rts: Peri<'d, impl RtsPin<T>>,\n        rx_buffer: &'d mut [u8],\n        config: Config,\n    ) -> Self {\n        super::Uart::<'d, Async>::init(T::info(), None, Some(rx.into()), Some(rts.into()), None, config);\n        init_buffers(T::info(), T::buffered_state(), None, Some(rx_buffer));\n\n        Self {\n            info: T::info(),\n            state: T::buffered_state(),\n        }\n    }\n\n    fn read<'a>(\n        info: &'static Info,\n        state: &'static State,\n        buf: &'a mut [u8],\n    ) -> impl Future<Output = Result<usize, Error>> + 'a {\n        poll_fn(move |cx| {\n            if let Poll::Ready(r) = Self::try_read(info, state, buf) {\n                return Poll::Ready(r);\n            }\n            state.rx_waker.register(cx.waker());\n            Poll::Pending\n        })\n    }\n\n    fn get_rx_error(state: &State) -> Option<Error> {\n        let errs = critical_section::with(|_| {\n            let val = state.rx_error.load(Ordering::Relaxed);\n            state.rx_error.store(0, Ordering::Relaxed);\n            val\n        });\n        if errs & RXE_OVERRUN != 0 {\n            Some(Error::Overrun)\n        } else if errs & RXE_BREAK != 0 {\n            Some(Error::Break)\n        } else if errs & RXE_PARITY != 0 {\n            Some(Error::Parity)\n        } else if errs & RXE_FRAMING != 0 {\n            Some(Error::Framing)\n        } else {\n            None\n        }\n    }\n\n    fn try_read(info: &Info, state: &State, buf: &mut [u8]) -> Poll<Result<usize, Error>> {\n        if buf.is_empty() {\n            return Poll::Ready(Ok(0));\n        }\n\n        let mut rx_reader = unsafe { state.rx_buf.reader() };\n        let n = rx_reader.pop(|data| {\n            let n = data.len().min(buf.len());\n            buf[..n].copy_from_slice(&data[..n]);\n            n\n        });\n\n        let result = if n == 0 {\n            match Self::get_rx_error(state) {\n                None => return Poll::Pending,\n                Some(e) => Err(e),\n            }\n        } else {\n            Ok(n)\n        };\n\n        // (Re-)Enable the interrupt to receive more data in case it was\n        // disabled because the buffer was full or errors were detected.\n        info.regs.uartimsc().write_set(|w| {\n            w.set_rxim(true);\n            w.set_rtim(true);\n        });\n\n        Poll::Ready(result)\n    }\n\n    /// Read from UART RX buffer blocking execution until done.\n    pub fn blocking_read(&mut self, buf: &mut [u8]) -> Result<usize, Error> {\n        loop {\n            match Self::try_read(self.info, self.state, buf) {\n                Poll::Ready(res) => return res,\n                Poll::Pending => continue,\n            }\n        }\n    }\n\n    fn fill_buf<'a>(state: &'static State) -> impl Future<Output = Result<&'a [u8], Error>> {\n        poll_fn(move |cx| {\n            let mut rx_reader = unsafe { state.rx_buf.reader() };\n            let (p, n) = rx_reader.pop_buf();\n            let result = if n == 0 {\n                match Self::get_rx_error(state) {\n                    None => {\n                        state.rx_waker.register(cx.waker());\n                        return Poll::Pending;\n                    }\n                    Some(e) => Err(e),\n                }\n            } else {\n                let buf = unsafe { slice::from_raw_parts(p, n) };\n                Ok(buf)\n            };\n\n            Poll::Ready(result)\n        })\n    }\n\n    fn consume(info: &Info, state: &State, amt: usize) {\n        let mut rx_reader = unsafe { state.rx_buf.reader() };\n        rx_reader.pop_done(amt);\n\n        // (Re-)Enable the interrupt to receive more data in case it was\n        // disabled because the buffer was full or errors were detected.\n        info.regs.uartimsc().write_set(|w| {\n            w.set_rxim(true);\n            w.set_rtim(true);\n        });\n    }\n\n    /// we are ready to read if there is data in the buffer\n    fn read_ready(state: &State) -> Result<bool, Error> {\n        Ok(!state.rx_buf.is_empty())\n    }\n}\n\nimpl BufferedUartTx {\n    /// Create a new buffered UART TX.\n    pub fn new<'d, T: Instance>(\n        _uart: Peri<'d, T>,\n        _irq: impl Binding<T::Interrupt, BufferedInterruptHandler<T>>,\n        tx: Peri<'d, impl TxPin<T>>,\n        tx_buffer: &'d mut [u8],\n        config: Config,\n    ) -> Self {\n        super::Uart::<'d, Async>::init(T::info(), Some(tx.into()), None, None, None, config);\n        init_buffers(T::info(), T::buffered_state(), Some(tx_buffer), None);\n\n        Self {\n            info: T::info(),\n            state: T::buffered_state(),\n        }\n    }\n\n    /// Create a new buffered UART TX with flow control.\n    pub fn new_with_cts<'d, T: Instance>(\n        _uart: Peri<'d, T>,\n        _irq: impl Binding<T::Interrupt, BufferedInterruptHandler<T>>,\n        tx: Peri<'d, impl TxPin<T>>,\n        cts: Peri<'d, impl CtsPin<T>>,\n        tx_buffer: &'d mut [u8],\n        config: Config,\n    ) -> Self {\n        super::Uart::<'d, Async>::init(T::info(), Some(tx.into()), None, None, Some(cts.into()), config);\n        init_buffers(T::info(), T::buffered_state(), Some(tx_buffer), None);\n\n        Self {\n            info: T::info(),\n            state: T::buffered_state(),\n        }\n    }\n\n    fn write<'d>(\n        info: &'static Info,\n        state: &'static State,\n        buf: &'d [u8],\n    ) -> impl Future<Output = Result<usize, Error>> + 'd {\n        poll_fn(move |cx| {\n            if buf.is_empty() {\n                return Poll::Ready(Ok(0));\n            }\n\n            let mut tx_writer = unsafe { state.tx_buf.writer() };\n            let n = tx_writer.push(|data| {\n                let n = data.len().min(buf.len());\n                data[..n].copy_from_slice(&buf[..n]);\n                n\n            });\n            if n == 0 {\n                state.tx_waker.register(cx.waker());\n                return Poll::Pending;\n            }\n\n            // The TX interrupt only triggers when the there was data in the\n            // FIFO and the number of bytes drops below a threshold. When the\n            // FIFO was empty we have to manually pend the interrupt to shovel\n            // TX data from the buffer into the FIFO.\n            info.interrupt.pend();\n            Poll::Ready(Ok(n))\n        })\n    }\n\n    fn flush(state: &'static State) -> impl Future<Output = Result<(), Error>> {\n        poll_fn(move |cx| {\n            if !state.tx_buf.is_empty() {\n                state.tx_waker.register(cx.waker());\n                return Poll::Pending;\n            }\n\n            Poll::Ready(Ok(()))\n        })\n    }\n\n    /// Write to UART TX buffer blocking execution until done.\n    pub fn blocking_write(&mut self, buf: &[u8]) -> Result<usize, Error> {\n        if buf.is_empty() {\n            return Ok(0);\n        }\n\n        loop {\n            let mut tx_writer = unsafe { self.state.tx_buf.writer() };\n            let n = tx_writer.push(|data| {\n                let n = data.len().min(buf.len());\n                data[..n].copy_from_slice(&buf[..n]);\n                n\n            });\n\n            if n != 0 {\n                // The TX interrupt only triggers when the there was data in the\n                // FIFO and the number of bytes drops below a threshold. When the\n                // FIFO was empty we have to manually pend the interrupt to shovel\n                // TX data from the buffer into the FIFO.\n                self.info.interrupt.pend();\n                return Ok(n);\n            }\n        }\n    }\n\n    /// Flush UART TX blocking execution until done.\n    pub fn blocking_flush(&mut self) -> Result<(), Error> {\n        loop {\n            if self.state.tx_buf.is_empty() {\n                return Ok(());\n            }\n        }\n    }\n\n    /// Check if UART is busy.\n    pub fn busy(&self) -> bool {\n        self.info.regs.uartfr().read().busy()\n    }\n\n    /// Assert a break condition after waiting for the transmit buffers to empty,\n    /// for the specified number of bit times. This condition must be asserted\n    /// for at least two frame times to be effective, `bits` will adjusted\n    /// according to frame size, parity, and stop bit settings to ensure this.\n    ///\n    /// This method may block for a long amount of time since it has to wait\n    /// for the transmit fifo to empty, which may take a while on slow links.\n    pub async fn send_break(&mut self, bits: u32) {\n        let regs = self.info.regs;\n        let bits = bits.max({\n            let lcr = regs.uartlcr_h().read();\n            let width = lcr.wlen() as u32 + 5;\n            let parity = lcr.pen() as u32;\n            let stops = 1 + lcr.stp2() as u32;\n            2 * (1 + width + parity + stops)\n        });\n        let divx64 = (((regs.uartibrd().read().baud_divint() as u32) << 6)\n            + regs.uartfbrd().read().baud_divfrac() as u32) as u64;\n        let div_clk = clk_peri_freq() as u64 * 64;\n        let wait_usecs = (1_000_000 * bits as u64 * divx64 * 16 + div_clk - 1) / div_clk;\n\n        Self::flush(self.state).await.unwrap();\n        while self.busy() {}\n        regs.uartlcr_h().write_set(|w| w.set_brk(true));\n        Timer::after_micros(wait_usecs).await;\n        regs.uartlcr_h().write_clear(|w| w.set_brk(true));\n    }\n}\n\nimpl Drop for BufferedUartRx {\n    fn drop(&mut self) {\n        unsafe { self.state.rx_buf.deinit() }\n\n        // TX is inactive if the buffer is not available.\n        // We can now unregister the interrupt handler\n        if !self.state.tx_buf.is_available() {\n            self.info.interrupt.disable();\n        }\n    }\n}\n\nimpl Drop for BufferedUartTx {\n    fn drop(&mut self) {\n        unsafe { self.state.tx_buf.deinit() }\n\n        // RX is inactive if the buffer is not available.\n        // We can now unregister the interrupt handler\n        if !self.state.rx_buf.is_available() {\n            self.info.interrupt.disable();\n        }\n    }\n}\n\n/// Interrupt handler.\npub struct BufferedInterruptHandler<T: Instance> {\n    _uart: PhantomData<T>,\n}\n\nimpl<T: Instance> interrupt::typelevel::Handler<T::Interrupt> for BufferedInterruptHandler<T> {\n    unsafe fn on_interrupt() {\n        let r = T::info().regs;\n        if r.uartdmacr().read().rxdmae() {\n            return;\n        }\n\n        let s = T::buffered_state();\n\n        // Clear TX and error interrupt flags\n        // RX interrupt flags are cleared by reading from the FIFO.\n        let ris = r.uartris().read();\n        r.uarticr().write(|w| {\n            w.set_txic(ris.txris());\n            w.set_feic(ris.feris());\n            w.set_peic(ris.peris());\n            w.set_beic(ris.beris());\n            w.set_oeic(ris.oeris());\n        });\n\n        // Errors\n        if ris.feris() {\n            warn!(\"Framing error\");\n        }\n        if ris.peris() {\n            warn!(\"Parity error\");\n        }\n        if ris.beris() {\n            warn!(\"Break error\");\n        }\n        if ris.oeris() {\n            warn!(\"Overrun error\");\n        }\n\n        // RX\n        if s.rx_buf.is_available() {\n            let mut rx_writer = unsafe { s.rx_buf.writer() };\n            let rx_buf = rx_writer.push_slice();\n            let mut n_read = 0;\n            let mut error = false;\n            for rx_byte in rx_buf {\n                if r.uartfr().read().rxfe() {\n                    break;\n                }\n                let dr = r.uartdr().read();\n                if (dr.0 >> 8) != 0 {\n                    critical_section::with(|_| {\n                        let val = s.rx_error.load(Ordering::Relaxed);\n                        s.rx_error.store(val | ((dr.0 >> 8) as u8), Ordering::Relaxed);\n                    });\n                    error = true;\n                    // only fill the buffer with valid characters. the current character is fine\n                    // if the error is an overrun, but if we add it to the buffer we'll report\n                    // the overrun one character too late. drop it instead and pretend we were\n                    // a bit slower at draining the rx fifo than we actually were.\n                    // this is consistent with blocking uart error reporting.\n                    break;\n                }\n                *rx_byte = dr.data();\n                n_read += 1;\n            }\n            if n_read > 0 {\n                rx_writer.push_done(n_read);\n                s.rx_waker.wake();\n            } else if error {\n                s.rx_waker.wake();\n            }\n            // Disable any further RX interrupts when the buffer becomes full or\n            // errors have occurred. This lets us buffer additional errors in the\n            // fifo without needing more error storage locations, and most applications\n            // will want to do a full reset of their uart state anyway once an error\n            // has happened.\n            if s.rx_buf.is_full() || error {\n                r.uartimsc().write_clear(|w| {\n                    w.set_rxim(true);\n                    w.set_rtim(true);\n                });\n            }\n        }\n\n        // TX\n        if s.tx_buf.is_available() {\n            let mut tx_reader = unsafe { s.tx_buf.reader() };\n            let tx_buf = tx_reader.pop_slice();\n            let mut n_written = 0;\n            for tx_byte in tx_buf.iter_mut() {\n                if r.uartfr().read().txff() {\n                    break;\n                }\n                r.uartdr().write(|w| w.set_data(*tx_byte));\n                n_written += 1;\n            }\n            if n_written > 0 {\n                tx_reader.pop_done(n_written);\n                s.tx_waker.wake();\n            }\n            // The TX interrupt only triggers once when the FIFO threshold is\n            // crossed. No need to disable it when the buffer becomes empty\n            // as it does re-trigger anymore once we have cleared it.\n        }\n    }\n}\n\nimpl embedded_io::Error for Error {\n    fn kind(&self) -> embedded_io::ErrorKind {\n        embedded_io::ErrorKind::Other\n    }\n}\n\nimpl embedded_io_async::ErrorType for BufferedUart {\n    type Error = Error;\n}\n\nimpl embedded_io_async::ErrorType for BufferedUartRx {\n    type Error = Error;\n}\n\nimpl embedded_io_async::ErrorType for BufferedUartTx {\n    type Error = Error;\n}\n\nimpl embedded_io_async::Read for BufferedUart {\n    async fn read(&mut self, buf: &mut [u8]) -> Result<usize, Self::Error> {\n        BufferedUartRx::read(self.rx.info, self.rx.state, buf).await\n    }\n}\n\nimpl embedded_io_async::Read for BufferedUartRx {\n    async fn read(&mut self, buf: &mut [u8]) -> Result<usize, Self::Error> {\n        Self::read(self.info, self.state, buf).await\n    }\n}\n\nimpl embedded_io_async::ReadReady for BufferedUart {\n    fn read_ready(&mut self) -> Result<bool, Self::Error> {\n        BufferedUartRx::read_ready(self.rx.state)\n    }\n}\n\nimpl embedded_io_async::ReadReady for BufferedUartRx {\n    fn read_ready(&mut self) -> Result<bool, Self::Error> {\n        Self::read_ready(self.state)\n    }\n}\n\nimpl embedded_io_async::BufRead for BufferedUart {\n    async fn fill_buf(&mut self) -> Result<&[u8], Self::Error> {\n        BufferedUartRx::fill_buf(self.rx.state).await\n    }\n\n    fn consume(&mut self, amt: usize) {\n        BufferedUartRx::consume(self.rx.info, self.rx.state, amt)\n    }\n}\n\nimpl embedded_io_async::BufRead for BufferedUartRx {\n    async fn fill_buf(&mut self) -> Result<&[u8], Self::Error> {\n        Self::fill_buf(self.state).await\n    }\n\n    fn consume(&mut self, amt: usize) {\n        Self::consume(self.info, self.state, amt)\n    }\n}\n\nimpl embedded_io_async::Write for BufferedUart {\n    async fn write(&mut self, buf: &[u8]) -> Result<usize, Self::Error> {\n        BufferedUartTx::write(self.tx.info, self.tx.state, buf).await\n    }\n\n    async fn flush(&mut self) -> Result<(), Self::Error> {\n        BufferedUartTx::flush(self.tx.state).await\n    }\n}\n\nimpl embedded_io_async::Write for BufferedUartTx {\n    async fn write(&mut self, buf: &[u8]) -> Result<usize, Self::Error> {\n        Self::write(self.info, self.state, buf).await\n    }\n\n    async fn flush(&mut self) -> Result<(), Self::Error> {\n        Self::flush(self.state).await\n    }\n}\n\nimpl embedded_io::Read for BufferedUart {\n    fn read(&mut self, buf: &mut [u8]) -> Result<usize, Self::Error> {\n        self.rx.blocking_read(buf)\n    }\n}\n\nimpl embedded_io::Read for BufferedUartRx {\n    fn read(&mut self, buf: &mut [u8]) -> Result<usize, Self::Error> {\n        self.blocking_read(buf)\n    }\n}\n\nimpl embedded_io::Write for BufferedUart {\n    fn write(&mut self, buf: &[u8]) -> Result<usize, Self::Error> {\n        self.tx.blocking_write(buf)\n    }\n\n    fn flush(&mut self) -> Result<(), Self::Error> {\n        self.tx.blocking_flush()\n    }\n}\n\nimpl embedded_io::Write for BufferedUartTx {\n    fn write(&mut self, buf: &[u8]) -> Result<usize, Self::Error> {\n        self.blocking_write(buf)\n    }\n\n    fn flush(&mut self) -> Result<(), Self::Error> {\n        self.blocking_flush()\n    }\n}\n\nimpl embedded_hal_02::serial::Read<u8> for BufferedUartRx {\n    type Error = Error;\n\n    fn read(&mut self) -> Result<u8, nb::Error<Self::Error>> {\n        let r = self.info.regs;\n        if r.uartfr().read().rxfe() {\n            return Err(nb::Error::WouldBlock);\n        }\n\n        let dr = r.uartdr().read();\n\n        if dr.oe() {\n            Err(nb::Error::Other(Error::Overrun))\n        } else if dr.be() {\n            Err(nb::Error::Other(Error::Break))\n        } else if dr.pe() {\n            Err(nb::Error::Other(Error::Parity))\n        } else if dr.fe() {\n            Err(nb::Error::Other(Error::Framing))\n        } else {\n            Ok(dr.data())\n        }\n    }\n}\n\nimpl embedded_hal_02::blocking::serial::Write<u8> for BufferedUartTx {\n    type Error = Error;\n\n    fn bwrite_all(&mut self, mut buffer: &[u8]) -> Result<(), Self::Error> {\n        while !buffer.is_empty() {\n            match self.blocking_write(buffer) {\n                Ok(0) => panic!(\"zero-length write.\"),\n                Ok(n) => buffer = &buffer[n..],\n                Err(e) => return Err(e),\n            }\n        }\n        Ok(())\n    }\n\n    fn bflush(&mut self) -> Result<(), Self::Error> {\n        self.blocking_flush()\n    }\n}\n\nimpl embedded_hal_02::serial::Read<u8> for BufferedUart {\n    type Error = Error;\n\n    fn read(&mut self) -> Result<u8, nb::Error<Self::Error>> {\n        embedded_hal_02::serial::Read::read(&mut self.rx)\n    }\n}\n\nimpl embedded_hal_02::blocking::serial::Write<u8> for BufferedUart {\n    type Error = Error;\n\n    fn bwrite_all(&mut self, mut buffer: &[u8]) -> Result<(), Self::Error> {\n        while !buffer.is_empty() {\n            match self.blocking_write(buffer) {\n                Ok(0) => panic!(\"zero-length write.\"),\n                Ok(n) => buffer = &buffer[n..],\n                Err(e) => return Err(e),\n            }\n        }\n        Ok(())\n    }\n\n    fn bflush(&mut self) -> Result<(), Self::Error> {\n        self.blocking_flush()\n    }\n}\n\nimpl embedded_hal_nb::serial::ErrorType for BufferedUartRx {\n    type Error = Error;\n}\n\nimpl embedded_hal_nb::serial::ErrorType for BufferedUartTx {\n    type Error = Error;\n}\n\nimpl embedded_hal_nb::serial::ErrorType for BufferedUart {\n    type Error = Error;\n}\n\nimpl embedded_hal_nb::serial::Read for BufferedUartRx {\n    fn read(&mut self) -> nb::Result<u8, Self::Error> {\n        embedded_hal_02::serial::Read::read(self)\n    }\n}\n\nimpl embedded_hal_nb::serial::Write for BufferedUartTx {\n    fn write(&mut self, char: u8) -> nb::Result<(), Self::Error> {\n        self.blocking_write(&[char]).map(drop).map_err(nb::Error::Other)\n    }\n\n    fn flush(&mut self) -> nb::Result<(), Self::Error> {\n        self.blocking_flush().map_err(nb::Error::Other)\n    }\n}\n\nimpl embedded_hal_nb::serial::Read for BufferedUart {\n    fn read(&mut self) -> Result<u8, nb::Error<Self::Error>> {\n        embedded_hal_02::serial::Read::read(&mut self.rx)\n    }\n}\n\nimpl embedded_hal_nb::serial::Write for BufferedUart {\n    fn write(&mut self, char: u8) -> nb::Result<(), Self::Error> {\n        self.blocking_write(&[char]).map(drop).map_err(nb::Error::Other)\n    }\n\n    fn flush(&mut self) -> nb::Result<(), Self::Error> {\n        self.blocking_flush().map_err(nb::Error::Other)\n    }\n}\n"
  },
  {
    "path": "embassy-rp/src/uart/mod.rs",
    "content": "//! UART driver.\nuse core::future::poll_fn;\nuse core::marker::PhantomData;\nuse core::sync::atomic::{AtomicU16, Ordering};\nuse core::task::Poll;\n\nuse embassy_futures::select::{Either, select};\nuse embassy_hal_internal::{Peri, PeripheralType};\nuse embassy_sync::waitqueue::AtomicWaker;\nuse embassy_time::{Delay, Timer};\nuse pac::uart::regs::Uartris;\n\nuse crate::clocks::clk_peri_freq;\nuse crate::dma::{Channel, ChannelInstance};\nuse crate::gpio::{AnyPin, SealedPin};\nuse crate::interrupt::typelevel::{Binding, Interrupt as _};\nuse crate::interrupt::{Interrupt, InterruptExt};\nuse crate::pac::io::vals::{Inover, Outover};\nuse crate::{RegExt, dma, interrupt, pac, peripherals};\n\nmod buffered;\npub use buffered::{BufferedInterruptHandler, BufferedUart, BufferedUartRx, BufferedUartTx};\n\n/// Word length.\n#[derive(Clone, Copy, PartialEq, Eq, Debug)]\npub enum DataBits {\n    /// 5 bits.\n    DataBits5,\n    /// 6 bits.\n    DataBits6,\n    /// 7 bits.\n    DataBits7,\n    /// 8 bits.\n    DataBits8,\n}\n\nimpl DataBits {\n    fn bits(&self) -> u8 {\n        match self {\n            Self::DataBits5 => 0b00,\n            Self::DataBits6 => 0b01,\n            Self::DataBits7 => 0b10,\n            Self::DataBits8 => 0b11,\n        }\n    }\n}\n\n/// Parity bit.\n#[derive(Clone, Copy, PartialEq, Eq, Debug)]\npub enum Parity {\n    /// No parity.\n    ParityNone,\n    /// Even parity.\n    ParityEven,\n    /// Odd parity.\n    ParityOdd,\n}\n\n/// Stop bits.\n#[derive(Clone, Copy, PartialEq, Eq, Debug)]\npub enum StopBits {\n    #[doc = \"1 stop bit\"]\n    STOP1,\n    #[doc = \"2 stop bits\"]\n    STOP2,\n}\n\n/// UART config.\n#[non_exhaustive]\n#[derive(Clone, Copy, PartialEq, Eq, Debug)]\npub struct Config {\n    /// Baud rate.\n    pub baudrate: u32,\n    /// Word length.\n    pub data_bits: DataBits,\n    /// Stop bits.\n    pub stop_bits: StopBits,\n    /// Parity bit.\n    pub parity: Parity,\n    /// Invert the tx pin output\n    pub invert_tx: bool,\n    /// Invert the rx pin input\n    pub invert_rx: bool,\n    /// Invert the rts pin\n    pub invert_rts: bool,\n    /// Invert the cts pin\n    pub invert_cts: bool,\n}\n\nimpl Default for Config {\n    fn default() -> Self {\n        Self {\n            baudrate: 115200,\n            data_bits: DataBits::DataBits8,\n            stop_bits: StopBits::STOP1,\n            parity: Parity::ParityNone,\n            invert_rx: false,\n            invert_tx: false,\n            invert_rts: false,\n            invert_cts: false,\n        }\n    }\n}\n\n/// Serial error\n#[derive(Debug, Eq, PartialEq, Copy, Clone)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\n#[non_exhaustive]\npub enum Error {\n    /// Triggered when the FIFO (or shift-register) is overflowed.\n    Overrun,\n    /// Triggered when a break is received\n    Break,\n    /// Triggered when there is a parity mismatch between what's received and\n    /// our settings.\n    Parity,\n    /// Triggered when the received character didn't have a valid stop bit.\n    Framing,\n}\n\nimpl core::fmt::Display for Error {\n    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {\n        core::fmt::Debug::fmt(self, f)\n    }\n}\n\nimpl core::error::Error for Error {}\n\n/// Read To Break error\n#[derive(Debug, Eq, PartialEq, Copy, Clone)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\n#[non_exhaustive]\npub enum ReadToBreakError {\n    /// Read this many bytes, but never received a line break.\n    MissingBreak(usize),\n    /// Other, standard issue with the serial request\n    Other(Error),\n}\n\n/// Internal DMA state of UART RX.\npub struct DmaState {\n    rx_err_waker: AtomicWaker,\n    rx_errs: AtomicU16,\n}\n\n/// UART driver.\npub struct Uart<'d, M: Mode> {\n    tx: UartTx<'d, M>,\n    rx: UartRx<'d, M>,\n}\n\n/// UART TX driver.\npub struct UartTx<'d, M: Mode> {\n    info: &'static Info,\n    tx_dma: Option<dma::Channel<'d>>,\n    phantom: PhantomData<M>,\n}\n\n/// UART RX driver.\npub struct UartRx<'d, M: Mode> {\n    info: &'static Info,\n    dma_state: &'static DmaState,\n    rx_dma: Option<dma::Channel<'d>>,\n    phantom: PhantomData<M>,\n}\n\nimpl<'d, M: Mode> UartTx<'d, M> {\n    fn new_inner(info: &'static Info, tx_dma: Option<Channel<'d>>) -> Self {\n        Self {\n            info,\n            tx_dma,\n            phantom: PhantomData,\n        }\n    }\n\n    /// Transmit the provided buffer blocking execution until done.\n    pub fn blocking_write(&mut self, buffer: &[u8]) -> Result<(), Error> {\n        let r = self.info.regs;\n        for &b in buffer {\n            while r.uartfr().read().txff() {}\n            r.uartdr().write(|w| w.set_data(b));\n        }\n        Ok(())\n    }\n\n    /// Flush UART TX blocking execution until done.\n    pub fn blocking_flush(&mut self) -> Result<(), Error> {\n        while !self.info.regs.uartfr().read().txfe() {}\n        Ok(())\n    }\n\n    /// Check if UART is busy transmitting.\n    pub fn busy(&self) -> bool {\n        self.info.regs.uartfr().read().busy()\n    }\n\n    /// Assert a break condition after waiting for the transmit buffers to empty,\n    /// for the specified number of bit times. This condition must be asserted\n    /// for at least two frame times to be effective, `bits` will adjusted\n    /// according to frame size, parity, and stop bit settings to ensure this.\n    ///\n    /// This method may block for a long amount of time since it has to wait\n    /// for the transmit fifo to empty, which may take a while on slow links.\n    pub async fn send_break(&mut self, bits: u32) {\n        let regs = self.info.regs;\n        let bits = bits.max({\n            let lcr = regs.uartlcr_h().read();\n            let width = lcr.wlen() as u32 + 5;\n            let parity = lcr.pen() as u32;\n            let stops = 1 + lcr.stp2() as u32;\n            2 * (1 + width + parity + stops)\n        });\n        let divx64 = (((regs.uartibrd().read().baud_divint() as u32) << 6)\n            + regs.uartfbrd().read().baud_divfrac() as u32) as u64;\n        let div_clk = clk_peri_freq() as u64 * 64;\n        let wait_usecs = (1_000_000 * bits as u64 * divx64 * 16 + div_clk - 1) / div_clk;\n\n        self.blocking_flush().unwrap();\n        while self.busy() {}\n        regs.uartlcr_h().write_set(|w| w.set_brk(true));\n        Timer::after_micros(wait_usecs).await;\n        regs.uartlcr_h().write_clear(|w| w.set_brk(true));\n    }\n}\n\nimpl<'d> UartTx<'d, Blocking> {\n    /// Create a new UART TX instance for blocking mode operations.\n    pub fn new_blocking<T: Instance>(_uart: Peri<'d, T>, tx: Peri<'d, impl TxPin<T>>, config: Config) -> Self {\n        Uart::<Blocking>::init(T::info(), Some(tx.into()), None, None, None, config);\n        Self::new_inner(T::info(), None)\n    }\n\n    /// Convert this uart TX instance into a buffered uart using the provided\n    /// irq and transmit buffer.\n    pub fn into_buffered<T: Instance>(\n        self,\n        _irq: impl Binding<T::Interrupt, BufferedInterruptHandler<T>>,\n        tx_buffer: &'d mut [u8],\n    ) -> BufferedUartTx {\n        buffered::init_buffers(T::info(), T::buffered_state(), Some(tx_buffer), None);\n\n        BufferedUartTx {\n            info: T::info(),\n            state: T::buffered_state(),\n        }\n    }\n}\n\nimpl<'d> UartTx<'d, Async> {\n    /// Create a new DMA-enabled UART which can only send data\n    pub fn new<T: Instance, TxDma: ChannelInstance>(\n        _uart: Peri<'d, T>,\n        tx: Peri<'d, impl TxPin<T>>,\n        tx_dma: Peri<'d, TxDma>,\n        irq: impl crate::interrupt::typelevel::Binding<TxDma::Interrupt, crate::dma::InterruptHandler<TxDma>> + 'd,\n        config: Config,\n    ) -> Self {\n        Uart::<Async>::init(T::info(), Some(tx.into()), None, None, None, config);\n        Self::new_inner(T::info(), Some(Channel::new(tx_dma, irq)))\n    }\n\n    /// Write to UART TX from the provided buffer using DMA.\n    pub async fn write(&mut self, buffer: &[u8]) -> Result<(), Error> {\n        let transfer = unsafe {\n            self.info.regs.uartdmacr().write_set(|reg| {\n                reg.set_txdmae(true);\n            });\n            // If we don't assign future to a variable, the data register pointer\n            // is held across an await and makes the future non-Send.\n            self.tx_dma.as_mut().unwrap().write(\n                buffer,\n                self.info.regs.uartdr().as_ptr() as *mut _,\n                self.info.tx_dreq.into(),\n                false,\n            )\n        };\n        transfer.await;\n        Ok(())\n    }\n}\n\nimpl<'d, M: Mode> UartRx<'d, M> {\n    fn new_inner(\n        info: &'static Info,\n        dma_state: &'static DmaState,\n        has_irq: bool,\n        rx_dma: Option<dma::Channel<'d>>,\n    ) -> Self {\n        debug_assert_eq!(has_irq, rx_dma.is_some());\n        if has_irq {\n            // disable all error interrupts initially\n            info.regs.uartimsc().write(|w| w.0 = 0);\n            info.interrupt.unpend();\n            unsafe { info.interrupt.enable() };\n        }\n        Self {\n            info,\n            dma_state,\n            rx_dma,\n            phantom: PhantomData,\n        }\n    }\n\n    /// Read from UART RX blocking execution until done.\n    pub fn blocking_read(&mut self, mut buffer: &mut [u8]) -> Result<(), Error> {\n        while !buffer.is_empty() {\n            let received = self.drain_fifo(buffer).map_err(|(_i, e)| e)?;\n            buffer = &mut buffer[received..];\n        }\n        Ok(())\n    }\n\n    /// Returns Ok(len) if no errors occurred. Returns Err((len, err)) if an error was\n    /// encountered. In both cases, `len` is the number of *good* bytes copied into\n    /// `buffer`.\n    fn drain_fifo(&mut self, buffer: &mut [u8]) -> Result<usize, (usize, Error)> {\n        let r = self.info.regs;\n        for (i, b) in buffer.iter_mut().enumerate() {\n            if r.uartfr().read().rxfe() {\n                return Ok(i);\n            }\n\n            let dr = r.uartdr().read();\n\n            if dr.oe() {\n                return Err((i, Error::Overrun));\n            } else if dr.be() {\n                return Err((i, Error::Break));\n            } else if dr.pe() {\n                return Err((i, Error::Parity));\n            } else if dr.fe() {\n                return Err((i, Error::Framing));\n            } else {\n                *b = dr.data();\n            }\n        }\n        Ok(buffer.len())\n    }\n}\n\nimpl<'d, M: Mode> Drop for UartRx<'d, M> {\n    fn drop(&mut self) {\n        if self.rx_dma.is_some() {\n            self.info.interrupt.disable();\n            // clear dma flags. irq handlers use these to disambiguate among themselves.\n            self.info.regs.uartdmacr().write_clear(|reg| {\n                reg.set_rxdmae(true);\n                reg.set_txdmae(true);\n                reg.set_dmaonerr(true);\n            });\n        }\n    }\n}\n\nimpl<'d> UartRx<'d, Blocking> {\n    /// Create a new UART RX instance for blocking mode operations.\n    pub fn new_blocking<T: Instance>(_uart: Peri<'d, T>, rx: Peri<'d, impl RxPin<T>>, config: Config) -> Self {\n        Uart::<Blocking>::init(T::info(), None, Some(rx.into()), None, None, config);\n        Self::new_inner(T::info(), T::dma_state(), false, None)\n    }\n\n    /// Convert this uart RX instance into a buffered uart using the provided\n    /// irq and receive buffer.\n    pub fn into_buffered<T: Instance>(\n        self,\n        _irq: impl Binding<T::Interrupt, BufferedInterruptHandler<T>>,\n        rx_buffer: &'d mut [u8],\n    ) -> BufferedUartRx {\n        buffered::init_buffers(T::info(), T::buffered_state(), None, Some(rx_buffer));\n\n        BufferedUartRx {\n            info: T::info(),\n            state: T::buffered_state(),\n        }\n    }\n}\n\n/// Interrupt handler.\npub struct InterruptHandler<T: Instance> {\n    _uart: PhantomData<T>,\n}\n\nimpl<T: Instance> interrupt::typelevel::Handler<T::Interrupt> for InterruptHandler<T> {\n    unsafe fn on_interrupt() {\n        let uart = T::info().regs;\n        if !uart.uartdmacr().read().rxdmae() {\n            return;\n        }\n\n        let state = T::dma_state();\n        let errs = uart.uartris().read();\n        state.rx_errs.store(errs.0 as u16, Ordering::Relaxed);\n        state.rx_err_waker.wake();\n        // disable the error interrupts instead of clearing the flags. clearing the\n        // flags would allow the dma transfer to continue, potentially signaling\n        // completion before we can check for errors that happened *during* the transfer.\n        uart.uartimsc().write_clear(|w| w.0 = errs.0);\n    }\n}\n\nimpl<'d> UartRx<'d, Async> {\n    /// Create a new DMA-enabled UART which can only receive data\n    pub fn new<T: Instance, RxDma: ChannelInstance>(\n        _uart: Peri<'d, T>,\n        rx: Peri<'d, impl RxPin<T>>,\n        irq: impl Binding<T::Interrupt, InterruptHandler<T>>\n        + crate::interrupt::typelevel::Binding<RxDma::Interrupt, crate::dma::InterruptHandler<RxDma>>\n        + 'd,\n        rx_dma: Peri<'d, RxDma>,\n        config: Config,\n    ) -> Self {\n        Uart::<Async>::init(T::info(), None, Some(rx.into()), None, None, config);\n        Self::new_inner(T::info(), T::dma_state(), true, Some(Channel::new(rx_dma, irq)))\n    }\n\n    /// Read from UART RX into the provided buffer.\n    pub async fn read(&mut self, buffer: &mut [u8]) -> Result<(), Error> {\n        // clear error flags before we drain the fifo. errors that have accumulated\n        // in the flags will also be present in the fifo.\n        self.dma_state.rx_errs.store(0, Ordering::Relaxed);\n        self.info.regs.uarticr().write(|w| {\n            w.set_oeic(true);\n            w.set_beic(true);\n            w.set_peic(true);\n            w.set_feic(true);\n        });\n\n        // then drain the fifo. we need to read at most 32 bytes. errors that apply\n        // to fifo bytes will be reported directly.\n        let buffer = match {\n            let limit = buffer.len().min(32);\n            self.drain_fifo(&mut buffer[0..limit])\n        } {\n            Ok(len) if len < buffer.len() => &mut buffer[len..],\n            Ok(_) => return Ok(()),\n            Err((_i, e)) => return Err(e),\n        };\n\n        // start a dma transfer. if errors have happened in the interim some error\n        // interrupt flags will have been raised, and those will be picked up immediately\n        // by the interrupt handler.\n        self.info.regs.uartimsc().write_set(|w| {\n            w.set_oeim(true);\n            w.set_beim(true);\n            w.set_peim(true);\n            w.set_feim(true);\n        });\n        self.info.regs.uartdmacr().write_set(|reg| {\n            reg.set_rxdmae(true);\n            reg.set_dmaonerr(true);\n        });\n        let transfer = unsafe {\n            // If we don't assign future to a variable, the data register pointer\n            // is held across an await and makes the future non-Send.\n            self.rx_dma.as_mut().unwrap().read(\n                self.info.regs.uartdr().as_ptr() as *const _,\n                buffer,\n                self.info.rx_dreq.into(),\n                false,\n            )\n        };\n\n        // wait for either the transfer to complete or an error to happen.\n        let transfer_result = select(\n            transfer,\n            poll_fn(|cx| {\n                self.dma_state.rx_err_waker.register(cx.waker());\n                let rx_errs = critical_section::with(|_| {\n                    let val = self.dma_state.rx_errs.load(Ordering::Relaxed);\n                    self.dma_state.rx_errs.store(0, Ordering::Relaxed);\n                    val\n                });\n                match rx_errs {\n                    0 => Poll::Pending,\n                    e => Poll::Ready(Uartris(e as u32)),\n                }\n            }),\n        )\n        .await;\n\n        let errors = match transfer_result {\n            Either::First(()) => {\n                // We're here because the DMA finished, BUT if an error occurred on the LAST\n                // byte, then we may still need to grab the error state!\n                Uartris(critical_section::with(|_| {\n                    let val = self.dma_state.rx_errs.load(Ordering::Relaxed);\n                    self.dma_state.rx_errs.store(0, Ordering::Relaxed);\n                    val\n                }) as u32)\n            }\n            Either::Second(e) => {\n                // We're here because we errored, which means this is the error that\n                // was problematic.\n                e\n            }\n        };\n\n        // If we got no error, just return at this point\n        if errors.0 == 0 {\n            return Ok(());\n        }\n\n        // If we DID get an error, we need to figure out which one it was.\n        if errors.oeris() {\n            return Err(Error::Overrun);\n        } else if errors.beris() {\n            return Err(Error::Break);\n        } else if errors.peris() {\n            return Err(Error::Parity);\n        } else if errors.feris() {\n            return Err(Error::Framing);\n        }\n        unreachable!(\"unrecognized rx error\");\n    }\n\n    /// Read from the UART, waiting for a break.\n    ///\n    /// We read until one of the following occurs:\n    ///\n    /// * We read `buffer.len()` bytes without a break\n    ///     * returns `Err(ReadToBreakError::MissingBreak(buffer.len()))`\n    /// * We read `n` bytes then a break occurs\n    ///     * returns `Ok(n)`\n    /// * We encounter some error OTHER than a break\n    ///     * returns `Err(ReadToBreakError::Other(error))`\n    ///\n    /// **NOTE**: you MUST provide a buffer one byte larger than your largest expected\n    /// message to reliably detect the framing on one single call to `read_to_break()`.\n    ///\n    /// * If you expect a message of 20 bytes + break, and provide a 20-byte buffer:\n    ///     * The first call to `read_to_break()` will return `Err(ReadToBreakError::MissingBreak(20))`\n    ///     * The next call to `read_to_break()` will immediately return `Ok(0)`, from the \"stale\" break\n    /// * If you expect a message of 20 bytes + break, and provide a 21-byte buffer:\n    ///     * The first call to `read_to_break()` will return `Ok(20)`.\n    ///     * The next call to `read_to_break()` will work as expected\n    ///\n    /// **NOTE**: In the UART context, a break refers to a break condition (the line being held low for\n    /// for longer than a single character), not an ASCII line break.\n    pub async fn read_to_break(&mut self, buffer: &mut [u8]) -> Result<usize, ReadToBreakError> {\n        self.read_to_break_with_count(buffer, 0).await\n    }\n\n    /// Read from the UART, waiting for a break as soon as at least `min_count` bytes have been read.\n    ///\n    /// We read until one of the following occurs:\n    ///\n    /// * We read `buffer.len()` bytes without a break\n    ///     * returns `Err(ReadToBreakError::MissingBreak(buffer.len()))`\n    /// * We read `n > min_count` bytes then a break occurs\n    ///     * returns `Ok(n)`\n    /// * We encounter some error OTHER than a break\n    ///     * returns `Err(ReadToBreakError::Other(error))`\n    ///\n    /// If a break occurs before `min_count` bytes have been read, the break will be ignored and the read will continue\n    ///\n    /// **NOTE**: you MUST provide a buffer one byte larger than your largest expected\n    /// message to reliably detect the framing on one single call to `read_to_break()`.\n    ///\n    /// * If you expect a message of 20 bytes + break, and provide a 20-byte buffer:\n    ///     * The first call to `read_to_break()` will return `Err(ReadToBreakError::MissingBreak(20))`\n    ///     * The next call to `read_to_break()` will immediately return `Ok(0)`, from the \"stale\" line break\n    /// * If you expect a message of 20 bytes + break, and provide a 21-byte buffer:\n    ///     * The first call to `read_to_break()` will return `Ok(20)`.\n    ///     * The next call to `read_to_break()` will work as expected\n    ///\n    /// **NOTE**: In the UART context, a break refers to a break condition (the line being held low for\n    /// for longer than a single character), not an ASCII line break.\n    pub async fn read_to_break_with_count(\n        &mut self,\n        buffer: &mut [u8],\n        min_count: usize,\n    ) -> Result<usize, ReadToBreakError> {\n        // clear error flags before we drain the fifo. errors that have accumulated\n        // in the flags will also be present in the fifo.\n        self.dma_state.rx_errs.store(0, Ordering::Relaxed);\n        self.info.regs.uarticr().write(|w| {\n            w.set_oeic(true);\n            w.set_beic(true);\n            w.set_peic(true);\n            w.set_feic(true);\n        });\n\n        // then drain the fifo. we need to read at most 32 bytes. errors that apply\n        // to fifo bytes will be reported directly.\n        let mut sbuffer = match {\n            let limit = buffer.len().min(32);\n            self.drain_fifo(&mut buffer[0..limit])\n        } {\n            // Drained fifo, still some room left!\n            Ok(len) if len < buffer.len() => &mut buffer[len..],\n            // Drained (some/all of the fifo), no room left\n            Ok(len) => return Err(ReadToBreakError::MissingBreak(len)),\n            // We got a break WHILE draining the FIFO, return what we did get before the break\n            Err((len, Error::Break)) => {\n                if len < min_count && len < buffer.len() {\n                    &mut buffer[len..]\n                } else {\n                    return Ok(len);\n                }\n            }\n            // Some other error, just return the error\n            Err((_i, e)) => return Err(ReadToBreakError::Other(e)),\n        };\n\n        // start a dma transfer. if errors have happened in the interim some error\n        // interrupt flags will have been raised, and those will be picked up immediately\n        // by the interrupt handler.\n        self.info.regs.uartimsc().write_set(|w| {\n            w.set_oeim(true);\n            w.set_beim(true);\n            w.set_peim(true);\n            w.set_feim(true);\n        });\n        self.info.regs.uartdmacr().write_set(|reg| {\n            reg.set_rxdmae(true);\n            reg.set_dmaonerr(true);\n        });\n\n        loop {\n            let transfer = unsafe {\n                // If we don't assign future to a variable, the data register pointer\n                // is held across an await and makes the future non-Send.\n                self.rx_dma.as_mut().unwrap().read(\n                    self.info.regs.uartdr().as_ptr() as *const _,\n                    sbuffer,\n                    self.info.rx_dreq.into(),\n                    false,\n                )\n            };\n\n            // wait for either the transfer to complete or an error to happen.\n            let transfer_result = select(\n                transfer,\n                poll_fn(|cx| {\n                    self.dma_state.rx_err_waker.register(cx.waker());\n                    let rx_errs = critical_section::with(|_| {\n                        let val = self.dma_state.rx_errs.load(Ordering::Relaxed);\n                        self.dma_state.rx_errs.store(0, Ordering::Relaxed);\n                        val\n                    });\n                    match rx_errs {\n                        0 => Poll::Pending,\n                        e => Poll::Ready(Uartris(e as u32)),\n                    }\n                }),\n            )\n            .await;\n\n            // Figure out our error state\n            let errors = match transfer_result {\n                Either::First(()) => {\n                    // We're here because the DMA finished, BUT if an error occurred on the LAST\n                    // byte, then we may still need to grab the error state!\n                    Uartris(critical_section::with(|_| {\n                        let val = self.dma_state.rx_errs.load(Ordering::Relaxed);\n                        self.dma_state.rx_errs.store(0, Ordering::Relaxed);\n                        val\n                    }) as u32)\n                }\n                Either::Second(e) => {\n                    // We're here because we errored, which means this is the error that\n                    // was problematic.\n                    e\n                }\n            };\n\n            if errors.0 == 0 {\n                // No errors? That means we filled the buffer without a line break.\n                // For THIS function, that's a problem.\n                return Err(ReadToBreakError::MissingBreak(buffer.len()));\n            } else if errors.beris() {\n                // We got a Line Break! By this point, we've finished/aborted the DMA\n                // transaction, which means that we need to figure out where it left off\n                // by looking at the write_addr.\n                //\n                // First, we do a sanity check to make sure the write value is within the\n                // range of DMA we just did.\n                let sval = buffer.as_ptr() as usize;\n                let eval = sval + buffer.len();\n\n                // This is the address where the DMA would write to next\n                let next_addr = self.rx_dma.as_mut().unwrap().write_addr() as usize;\n\n                // If we DON'T end up inside the range, something has gone really wrong.\n                // Note that it's okay that `eval` is one past the end of the slice, as\n                // this is where the write pointer will end up at the end of a full\n                // transfer.\n                if (next_addr < sval) || (next_addr > eval) {\n                    unreachable!(\"UART DMA reported invalid `write_addr`\");\n                }\n\n                if (next_addr - sval) < min_count {\n                    sbuffer = &mut buffer[(next_addr - sval)..];\n                    continue;\n                }\n\n                let regs = self.info.regs;\n                let all_full = next_addr == eval;\n\n                // NOTE: This is off label usage of RSR! See the issue below for\n                // why I am not checking if there is an \"extra\" FIFO byte, and why\n                // I am checking RSR directly (it seems to report the status of the LAST\n                // POPPED value, rather than the NEXT TO POP value like the datasheet\n                // suggests!)\n                //\n                // issue: https://github.com/raspberrypi/pico-feedback/issues/367\n                let last_was_break = regs.uartrsr().read().be();\n\n                return match (all_full, last_was_break) {\n                    (true, true) | (false, _) => {\n                        // We got less than the full amount + a break, or the full amount\n                        // and the last byte was a break. Subtract the break off by adding one to sval.\n                        Ok(next_addr.saturating_sub(1 + sval))\n                    }\n                    (true, false) => {\n                        // We finished the whole DMA, and the last DMA'd byte was NOT a break\n                        // character. This is an error.\n                        //\n                        // NOTE: we COULD potentially return Ok(buffer.len()) here, since we\n                        // know a line break occured at SOME POINT after the DMA completed.\n                        //\n                        // However, we have no way of knowing if there was extra data BEFORE\n                        // that line break, so instead return an Err to signal to the caller\n                        // that there are \"leftovers\", and they'll catch the actual line break\n                        // on the next call.\n                        //\n                        // Doing it like this also avoids racyness: now whether you finished\n                        // the full read BEFORE the line break occurred or AFTER the line break\n                        // occurs, you still get `MissingBreak(buffer.len())` instead of sometimes\n                        // getting `Ok(buffer.len())` if you were \"late enough\" to observe the\n                        // line break.\n                        Err(ReadToBreakError::MissingBreak(buffer.len()))\n                    }\n                };\n            } else if errors.oeris() {\n                return Err(ReadToBreakError::Other(Error::Overrun));\n            } else if errors.peris() {\n                return Err(ReadToBreakError::Other(Error::Parity));\n            } else if errors.feris() {\n                return Err(ReadToBreakError::Other(Error::Framing));\n            }\n            unreachable!(\"unrecognized rx error\");\n        }\n    }\n}\n\nimpl<'d> Uart<'d, Blocking> {\n    /// Create a new UART without hardware flow control\n    pub fn new_blocking<T: Instance>(\n        uart: Peri<'d, T>,\n        tx: Peri<'d, impl TxPin<T>>,\n        rx: Peri<'d, impl RxPin<T>>,\n        config: Config,\n    ) -> Self {\n        Self::new_inner(uart, tx.into(), rx.into(), None, None, false, None, None, config)\n    }\n\n    /// Create a new UART with hardware flow control (RTS/CTS)\n    pub fn new_with_rtscts_blocking<T: Instance>(\n        uart: Peri<'d, T>,\n        tx: Peri<'d, impl TxPin<T>>,\n        rx: Peri<'d, impl RxPin<T>>,\n        rts: Peri<'d, impl RtsPin<T>>,\n        cts: Peri<'d, impl CtsPin<T>>,\n        config: Config,\n    ) -> Self {\n        Self::new_inner(\n            uart,\n            tx.into(),\n            rx.into(),\n            Some(rts.into()),\n            Some(cts.into()),\n            false,\n            None,\n            None,\n            config,\n        )\n    }\n\n    /// Convert this uart instance into a buffered uart using the provided\n    /// irq, transmit and receive buffers.\n    pub fn into_buffered<T: Instance>(\n        self,\n        _irq: impl Binding<T::Interrupt, BufferedInterruptHandler<T>>,\n        tx_buffer: &'d mut [u8],\n        rx_buffer: &'d mut [u8],\n    ) -> BufferedUart {\n        buffered::init_buffers(T::info(), T::buffered_state(), Some(tx_buffer), Some(rx_buffer));\n\n        BufferedUart {\n            rx: BufferedUartRx {\n                info: T::info(),\n                state: T::buffered_state(),\n            },\n            tx: BufferedUartTx {\n                info: T::info(),\n                state: T::buffered_state(),\n            },\n        }\n    }\n}\n\nimpl<'d> Uart<'d, Async> {\n    /// Create a new DMA enabled UART without hardware flow control\n    pub fn new<T: Instance, TxDma: ChannelInstance, RxDma: ChannelInstance>(\n        uart: Peri<'d, T>,\n        tx: Peri<'d, impl TxPin<T>>,\n        rx: Peri<'d, impl RxPin<T>>,\n        irq: impl Binding<T::Interrupt, InterruptHandler<T>>\n        + Binding<TxDma::Interrupt, dma::InterruptHandler<TxDma>>\n        + Binding<RxDma::Interrupt, dma::InterruptHandler<RxDma>>\n        + 'd,\n        tx_dma: Peri<'d, TxDma>,\n        rx_dma: Peri<'d, RxDma>,\n        config: Config,\n    ) -> Self {\n        let tx_dma_ch = dma::Channel::new(tx_dma, irq);\n        let rx_dma_ch = dma::Channel::new(rx_dma, irq);\n        Self::new_inner(\n            uart,\n            tx.into(),\n            rx.into(),\n            None,\n            None,\n            true,\n            Some(tx_dma_ch),\n            Some(rx_dma_ch),\n            config,\n        )\n    }\n\n    /// Create a new DMA enabled UART with hardware flow control (RTS/CTS)\n    pub fn new_with_rtscts<T: Instance, TxDma: ChannelInstance, RxDma: ChannelInstance>(\n        uart: Peri<'d, T>,\n        tx: Peri<'d, impl TxPin<T>>,\n        rx: Peri<'d, impl RxPin<T>>,\n        rts: Peri<'d, impl RtsPin<T>>,\n        cts: Peri<'d, impl CtsPin<T>>,\n        irq: impl Binding<T::Interrupt, InterruptHandler<T>>\n        + Binding<TxDma::Interrupt, dma::InterruptHandler<TxDma>>\n        + Binding<RxDma::Interrupt, dma::InterruptHandler<RxDma>>\n        + 'd,\n        tx_dma: Peri<'d, TxDma>,\n        rx_dma: Peri<'d, RxDma>,\n        config: Config,\n    ) -> Self {\n        let tx_dma_ch = dma::Channel::new(tx_dma, irq);\n        let rx_dma_ch = dma::Channel::new(rx_dma, irq);\n        Self::new_inner(\n            uart,\n            tx.into(),\n            rx.into(),\n            Some(rts.into()),\n            Some(cts.into()),\n            true,\n            Some(tx_dma_ch),\n            Some(rx_dma_ch),\n            config,\n        )\n    }\n}\n\nimpl<'d, M: Mode> Uart<'d, M> {\n    fn new_inner<T: Instance>(\n        _uart: Peri<'d, T>,\n        mut tx: Peri<'d, AnyPin>,\n        mut rx: Peri<'d, AnyPin>,\n        mut rts: Option<Peri<'d, AnyPin>>,\n        mut cts: Option<Peri<'d, AnyPin>>,\n        has_irq: bool,\n        tx_dma: Option<dma::Channel<'d>>,\n        rx_dma: Option<dma::Channel<'d>>,\n        config: Config,\n    ) -> Self {\n        Self::init(\n            T::info(),\n            Some(tx.reborrow()),\n            Some(rx.reborrow()),\n            rts.as_mut().map(|x| x.reborrow()),\n            cts.as_mut().map(|x| x.reborrow()),\n            config,\n        );\n\n        Self {\n            tx: UartTx::new_inner(T::info(), tx_dma),\n            rx: UartRx::new_inner(T::info(), T::dma_state(), has_irq, rx_dma),\n        }\n    }\n\n    fn init(\n        info: &Info,\n        tx: Option<Peri<'_, AnyPin>>,\n        rx: Option<Peri<'_, AnyPin>>,\n        rts: Option<Peri<'_, AnyPin>>,\n        cts: Option<Peri<'_, AnyPin>>,\n        config: Config,\n    ) {\n        let r = info.regs;\n        if let Some(pin) = &tx {\n            let funcsel = {\n                let pin_number = ((pin.gpio().as_ptr() as u32) & 0x1FF) / 8;\n                if (pin_number % 4) == 0 { 2 } else { 11 }\n            };\n            pin.gpio().ctrl().write(|w| {\n                w.set_funcsel(funcsel);\n                w.set_outover(if config.invert_tx {\n                    Outover::INVERT\n                } else {\n                    Outover::NORMAL\n                });\n            });\n            pin.pad_ctrl().write(|w| {\n                #[cfg(feature = \"_rp235x\")]\n                w.set_iso(false);\n                w.set_ie(true);\n            });\n        }\n        if let Some(pin) = &rx {\n            let funcsel = {\n                let pin_number = ((pin.gpio().as_ptr() as u32) & 0x1FF) / 8;\n                if ((pin_number - 1) % 4) == 0 { 2 } else { 11 }\n            };\n            pin.gpio().ctrl().write(|w| {\n                w.set_funcsel(funcsel);\n                w.set_inover(if config.invert_rx {\n                    Inover::INVERT\n                } else {\n                    Inover::NORMAL\n                });\n            });\n            pin.pad_ctrl().write(|w| {\n                #[cfg(feature = \"_rp235x\")]\n                w.set_iso(false);\n                w.set_ie(true);\n            });\n        }\n        if let Some(pin) = &cts {\n            pin.gpio().ctrl().write(|w| {\n                w.set_funcsel(2);\n                w.set_inover(if config.invert_cts {\n                    Inover::INVERT\n                } else {\n                    Inover::NORMAL\n                });\n            });\n            pin.pad_ctrl().write(|w| {\n                #[cfg(feature = \"_rp235x\")]\n                w.set_iso(false);\n                w.set_ie(true);\n            });\n        }\n        if let Some(pin) = &rts {\n            pin.gpio().ctrl().write(|w| {\n                w.set_funcsel(2);\n                w.set_outover(if config.invert_rts {\n                    Outover::INVERT\n                } else {\n                    Outover::NORMAL\n                });\n            });\n            pin.pad_ctrl().write(|w| {\n                #[cfg(feature = \"_rp235x\")]\n                w.set_iso(false);\n                w.set_ie(true);\n            });\n        }\n\n        Self::set_baudrate_inner(info, config.baudrate);\n\n        let (pen, eps) = match config.parity {\n            Parity::ParityNone => (false, false),\n            Parity::ParityOdd => (true, false),\n            Parity::ParityEven => (true, true),\n        };\n\n        r.uartlcr_h().write(|w| {\n            w.set_wlen(config.data_bits.bits());\n            w.set_stp2(config.stop_bits == StopBits::STOP2);\n            w.set_pen(pen);\n            w.set_eps(eps);\n            w.set_fen(true);\n        });\n\n        r.uartifls().write(|w| {\n            w.set_rxiflsel(0b100);\n            w.set_txiflsel(0b000);\n        });\n\n        r.uartcr().write(|w| {\n            w.set_uarten(true);\n            w.set_rxe(true);\n            w.set_txe(true);\n            w.set_ctsen(cts.is_some());\n            w.set_rtsen(rts.is_some());\n        });\n    }\n\n    fn lcr_modify<R>(info: &Info, f: impl FnOnce(&mut crate::pac::uart::regs::UartlcrH) -> R) -> R {\n        let r = info.regs;\n\n        // Notes from PL011 reference manual:\n        //\n        // - Before writing the LCR, if the UART is enabled it needs to be\n        //   disabled and any current TX + RX activity has to be completed\n        //\n        // - There is a BUSY flag which waits for the current TX char, but this is\n        //   OR'd with TX FIFO !FULL, so not usable when FIFOs are enabled and\n        //   potentially nonempty\n        //\n        // - FIFOs can't be set to disabled whilst a character is in progress\n        //   (else \"FIFO integrity is not guaranteed\")\n        //\n        // Combination of these means there is no general way to halt and poll for\n        // end of TX character, if FIFOs may be enabled. Either way, there is no\n        // way to poll for end of RX character.\n        //\n        // So, insert a 15 Baud period delay before changing the settings.\n        // 15 Baud is comfortably higher than start + max data + parity + stop.\n        // Anything else would require API changes to permit a non-enabled UART\n        // state after init() where settings can be changed safely.\n        let clk_base = crate::clocks::clk_peri_freq();\n\n        let cr = r.uartcr().read();\n        if cr.uarten() {\n            r.uartcr().modify(|w| {\n                w.set_uarten(false);\n                w.set_txe(false);\n                w.set_rxe(false);\n            });\n\n            // Note: Maximise precision here. Show working, the compiler will mop this up.\n            // Create a 16.6 fixed-point fractional division ratio; then scale to 32-bits.\n            let mut brdiv_ratio = 64 * r.uartibrd().read().0 + r.uartfbrd().read().0;\n            brdiv_ratio <<= 10;\n            // 3662 is ~(15 * 244.14) where 244.14 is 16e6 / 2^16\n            let scaled_freq = clk_base / 3662;\n            let wait_time_us = brdiv_ratio / scaled_freq;\n            embedded_hal_1::delay::DelayNs::delay_us(&mut Delay, wait_time_us);\n        }\n\n        let res = r.uartlcr_h().modify(f);\n\n        r.uartcr().write_value(cr);\n\n        res\n    }\n\n    /// sets baudrate on runtime\n    pub fn set_baudrate(&mut self, baudrate: u32) {\n        Self::set_baudrate_inner(self.tx.info, baudrate);\n    }\n\n    fn set_baudrate_inner(info: &Info, baudrate: u32) {\n        let r = info.regs;\n\n        let clk_base = crate::clocks::clk_peri_freq();\n\n        let baud_rate_div = (8 * clk_base) / baudrate;\n        let mut baud_ibrd = baud_rate_div >> 7;\n        let mut baud_fbrd = ((baud_rate_div & 0x7f) + 1) / 2;\n\n        if baud_ibrd == 0 {\n            baud_ibrd = 1;\n            baud_fbrd = 0;\n        } else if baud_ibrd >= 65535 {\n            baud_ibrd = 65535;\n            baud_fbrd = 0;\n        }\n\n        // Load PL011's baud divisor registers\n        r.uartibrd().write_value(pac::uart::regs::Uartibrd(baud_ibrd));\n        r.uartfbrd().write_value(pac::uart::regs::Uartfbrd(baud_fbrd));\n\n        Self::lcr_modify(info, |_| {});\n    }\n}\n\nimpl<'d, M: Mode> Uart<'d, M> {\n    /// Transmit the provided buffer blocking execution until done.\n    pub fn blocking_write(&mut self, buffer: &[u8]) -> Result<(), Error> {\n        self.tx.blocking_write(buffer)\n    }\n\n    /// Flush UART TX blocking execution until done.\n    pub fn blocking_flush(&mut self) -> Result<(), Error> {\n        self.tx.blocking_flush()\n    }\n\n    /// Read from UART RX blocking execution until done.\n    pub fn blocking_read(&mut self, buffer: &mut [u8]) -> Result<(), Error> {\n        self.rx.blocking_read(buffer)\n    }\n\n    /// Check if UART is busy transmitting.\n    pub fn busy(&self) -> bool {\n        self.tx.busy()\n    }\n\n    /// Wait until TX is empty and send break condition.\n    pub async fn send_break(&mut self, bits: u32) {\n        self.tx.send_break(bits).await\n    }\n\n    /// Split the Uart into a transmitter and receiver, which is particularly\n    /// useful when having two tasks correlating to transmitting and receiving.\n    pub fn split(self) -> (UartTx<'d, M>, UartRx<'d, M>) {\n        (self.tx, self.rx)\n    }\n\n    /// Split the Uart into a transmitter and receiver by mutable reference,\n    /// which is particularly useful when having two tasks correlating to\n    /// transmitting and receiving.\n    pub fn split_ref(&mut self) -> (&mut UartTx<'d, M>, &mut UartRx<'d, M>) {\n        (&mut self.tx, &mut self.rx)\n    }\n}\n\nimpl<'d> Uart<'d, Async> {\n    /// Write to UART TX from the provided buffer.\n    pub async fn write(&mut self, buffer: &[u8]) -> Result<(), Error> {\n        self.tx.write(buffer).await\n    }\n\n    /// Read from UART RX into the provided buffer.\n    pub async fn read(&mut self, buffer: &mut [u8]) -> Result<(), Error> {\n        self.rx.read(buffer).await\n    }\n\n    /// Read until the buffer is full or a line break occurs.\n    ///\n    /// See [`UartRx::read_to_break()`] for more details\n    pub async fn read_to_break<'a>(&mut self, buf: &'a mut [u8]) -> Result<usize, ReadToBreakError> {\n        self.rx.read_to_break(buf).await\n    }\n\n    /// Read until the buffer is full or a line break occurs after at least `min_count` bytes have been read.\n    ///\n    /// See [`UartRx::read_to_break_with_count()`] for more details\n    pub async fn read_to_break_with_count<'a>(\n        &mut self,\n        buf: &'a mut [u8],\n        min_count: usize,\n    ) -> Result<usize, ReadToBreakError> {\n        self.rx.read_to_break_with_count(buf, min_count).await\n    }\n}\n\nimpl<'d, M: Mode> embedded_hal_02::serial::Read<u8> for UartRx<'d, M> {\n    type Error = Error;\n    fn read(&mut self) -> Result<u8, nb::Error<Self::Error>> {\n        let r = self.info.regs;\n        if r.uartfr().read().rxfe() {\n            return Err(nb::Error::WouldBlock);\n        }\n\n        let dr = r.uartdr().read();\n\n        if dr.oe() {\n            Err(nb::Error::Other(Error::Overrun))\n        } else if dr.be() {\n            Err(nb::Error::Other(Error::Break))\n        } else if dr.pe() {\n            Err(nb::Error::Other(Error::Parity))\n        } else if dr.fe() {\n            Err(nb::Error::Other(Error::Framing))\n        } else {\n            Ok(dr.data())\n        }\n    }\n}\n\nimpl<'d, M: Mode> embedded_hal_02::serial::Write<u8> for UartTx<'d, M> {\n    type Error = Error;\n\n    fn write(&mut self, word: u8) -> Result<(), nb::Error<Self::Error>> {\n        let r = self.info.regs;\n        if r.uartfr().read().txff() {\n            return Err(nb::Error::WouldBlock);\n        }\n\n        r.uartdr().write(|w| w.set_data(word));\n        Ok(())\n    }\n\n    fn flush(&mut self) -> Result<(), nb::Error<Self::Error>> {\n        let r = self.info.regs;\n        if !r.uartfr().read().txfe() {\n            return Err(nb::Error::WouldBlock);\n        }\n        Ok(())\n    }\n}\n\nimpl<'d, M: Mode> embedded_hal_02::blocking::serial::Write<u8> for UartTx<'d, M> {\n    type Error = Error;\n\n    fn bwrite_all(&mut self, buffer: &[u8]) -> Result<(), Self::Error> {\n        self.blocking_write(buffer)\n    }\n\n    fn bflush(&mut self) -> Result<(), Self::Error> {\n        self.blocking_flush()\n    }\n}\n\nimpl<'d, M: Mode> embedded_hal_02::serial::Read<u8> for Uart<'d, M> {\n    type Error = Error;\n\n    fn read(&mut self) -> Result<u8, nb::Error<Self::Error>> {\n        embedded_hal_02::serial::Read::read(&mut self.rx)\n    }\n}\n\nimpl<'d, M: Mode> embedded_hal_02::serial::Write<u8> for Uart<'d, M> {\n    type Error = Error;\n\n    fn write(&mut self, word: u8) -> Result<(), nb::Error<Self::Error>> {\n        embedded_hal_02::serial::Write::write(&mut self.tx, word)\n    }\n\n    fn flush(&mut self) -> Result<(), nb::Error<Self::Error>> {\n        embedded_hal_02::serial::Write::flush(&mut self.tx)\n    }\n}\n\nimpl<'d, M: Mode> embedded_hal_02::blocking::serial::Write<u8> for Uart<'d, M> {\n    type Error = Error;\n\n    fn bwrite_all(&mut self, buffer: &[u8]) -> Result<(), Self::Error> {\n        self.blocking_write(buffer)\n    }\n\n    fn bflush(&mut self) -> Result<(), Self::Error> {\n        self.blocking_flush()\n    }\n}\n\nimpl embedded_hal_nb::serial::Error for Error {\n    fn kind(&self) -> embedded_hal_nb::serial::ErrorKind {\n        match *self {\n            Self::Framing => embedded_hal_nb::serial::ErrorKind::FrameFormat,\n            Self::Break => embedded_hal_nb::serial::ErrorKind::Other,\n            Self::Overrun => embedded_hal_nb::serial::ErrorKind::Overrun,\n            Self::Parity => embedded_hal_nb::serial::ErrorKind::Parity,\n        }\n    }\n}\n\nimpl<'d, M: Mode> embedded_hal_nb::serial::ErrorType for UartRx<'d, M> {\n    type Error = Error;\n}\n\nimpl<'d, M: Mode> embedded_hal_nb::serial::ErrorType for UartTx<'d, M> {\n    type Error = Error;\n}\n\nimpl<'d, M: Mode> embedded_hal_nb::serial::ErrorType for Uart<'d, M> {\n    type Error = Error;\n}\n\nimpl<'d, M: Mode> embedded_hal_nb::serial::Read for UartRx<'d, M> {\n    fn read(&mut self) -> nb::Result<u8, Self::Error> {\n        let r = self.info.regs;\n        if r.uartfr().read().rxfe() {\n            return Err(nb::Error::WouldBlock);\n        }\n\n        let dr = r.uartdr().read();\n\n        if dr.oe() {\n            Err(nb::Error::Other(Error::Overrun))\n        } else if dr.be() {\n            Err(nb::Error::Other(Error::Break))\n        } else if dr.pe() {\n            Err(nb::Error::Other(Error::Parity))\n        } else if dr.fe() {\n            Err(nb::Error::Other(Error::Framing))\n        } else {\n            Ok(dr.data())\n        }\n    }\n}\n\nimpl<'d, M: Mode> embedded_hal_nb::serial::Write for UartTx<'d, M> {\n    fn write(&mut self, char: u8) -> nb::Result<(), Self::Error> {\n        self.blocking_write(&[char]).map_err(nb::Error::Other)\n    }\n\n    fn flush(&mut self) -> nb::Result<(), Self::Error> {\n        self.blocking_flush().map_err(nb::Error::Other)\n    }\n}\n\nimpl<'d> embedded_io::ErrorType for UartTx<'d, Blocking> {\n    type Error = Error;\n}\n\nimpl<'d> embedded_io::Write for UartTx<'d, Blocking> {\n    fn write(&mut self, buf: &[u8]) -> Result<usize, Self::Error> {\n        self.blocking_write(buf).map(|_| buf.len())\n    }\n\n    fn flush(&mut self) -> Result<(), Self::Error> {\n        self.blocking_flush()\n    }\n}\n\nimpl<'d, M: Mode> embedded_hal_nb::serial::Read for Uart<'d, M> {\n    fn read(&mut self) -> Result<u8, nb::Error<Self::Error>> {\n        embedded_hal_02::serial::Read::read(&mut self.rx)\n    }\n}\n\nimpl<'d, M: Mode> embedded_hal_nb::serial::Write for Uart<'d, M> {\n    fn write(&mut self, char: u8) -> nb::Result<(), Self::Error> {\n        self.blocking_write(&[char]).map_err(nb::Error::Other)\n    }\n\n    fn flush(&mut self) -> nb::Result<(), Self::Error> {\n        self.blocking_flush().map_err(nb::Error::Other)\n    }\n}\n\nimpl<'d> embedded_io::ErrorType for Uart<'d, Blocking> {\n    type Error = Error;\n}\n\nimpl<'d> embedded_io::Write for Uart<'d, Blocking> {\n    fn write(&mut self, buf: &[u8]) -> Result<usize, Self::Error> {\n        self.blocking_write(buf).map(|_| buf.len())\n    }\n\n    fn flush(&mut self) -> Result<(), Self::Error> {\n        self.blocking_flush()\n    }\n}\n\nstruct Info {\n    regs: pac::uart::Uart,\n    tx_dreq: pac::dma::vals::TreqSel,\n    rx_dreq: pac::dma::vals::TreqSel,\n    interrupt: Interrupt,\n}\n\ntrait SealedMode {}\n\ntrait SealedInstance {\n    fn info() -> &'static Info;\n\n    fn buffered_state() -> &'static buffered::State;\n\n    fn dma_state() -> &'static DmaState;\n}\n\n/// UART mode.\n#[allow(private_bounds)]\npub trait Mode: SealedMode {}\n\nmacro_rules! impl_mode {\n    ($name:ident) => {\n        impl SealedMode for $name {}\n        impl Mode for $name {}\n    };\n}\n\n/// Blocking mode.\npub struct Blocking;\n/// Async mode.\npub struct Async;\n\nimpl_mode!(Blocking);\nimpl_mode!(Async);\n\n/// UART instance.\n#[allow(private_bounds)]\npub trait Instance: SealedInstance + PeripheralType {\n    /// Interrupt for this instance.\n    type Interrupt: interrupt::typelevel::Interrupt;\n}\n\nmacro_rules! impl_instance {\n    ($inst:ident, $irq:ident, $tx_dreq:expr, $rx_dreq:expr) => {\n        impl SealedInstance for peripherals::$inst {\n            fn info() -> &'static Info {\n                static INFO: Info = Info {\n                    regs: pac::$inst,\n                    tx_dreq: $tx_dreq,\n                    rx_dreq: $rx_dreq,\n                    interrupt: crate::interrupt::typelevel::$irq::IRQ,\n                };\n                &INFO\n            }\n\n            fn buffered_state() -> &'static buffered::State {\n                static STATE: buffered::State = buffered::State::new();\n                &STATE\n            }\n\n            fn dma_state() -> &'static DmaState {\n                static STATE: DmaState = DmaState {\n                    rx_err_waker: AtomicWaker::new(),\n                    rx_errs: AtomicU16::new(0),\n                };\n                &STATE\n            }\n        }\n        impl Instance for peripherals::$inst {\n            type Interrupt = crate::interrupt::typelevel::$irq;\n        }\n    };\n}\n\nimpl_instance!(\n    UART0,\n    UART0_IRQ,\n    pac::dma::vals::TreqSel::UART0_TX,\n    pac::dma::vals::TreqSel::UART0_RX\n);\nimpl_instance!(\n    UART1,\n    UART1_IRQ,\n    pac::dma::vals::TreqSel::UART1_TX,\n    pac::dma::vals::TreqSel::UART1_RX\n);\n\n/// Trait for TX pins.\npub trait TxPin<T: Instance>: crate::gpio::Pin {}\n/// Trait for RX pins.\npub trait RxPin<T: Instance>: crate::gpio::Pin {}\n/// Trait for Clear To Send (CTS) pins.\npub trait CtsPin<T: Instance>: crate::gpio::Pin {}\n/// Trait for Request To Send (RTS) pins.\npub trait RtsPin<T: Instance>: crate::gpio::Pin {}\n\nmacro_rules! impl_pin {\n    ($pin:ident, $instance:ident, $function:ident) => {\n        impl $function<peripherals::$instance> for peripherals::$pin {}\n    };\n}\n\nimpl_pin!(PIN_0, UART0, TxPin);\nimpl_pin!(PIN_1, UART0, RxPin);\nimpl_pin!(PIN_2, UART0, CtsPin);\nimpl_pin!(PIN_3, UART0, RtsPin);\nimpl_pin!(PIN_4, UART1, TxPin);\nimpl_pin!(PIN_5, UART1, RxPin);\nimpl_pin!(PIN_6, UART1, CtsPin);\nimpl_pin!(PIN_7, UART1, RtsPin);\nimpl_pin!(PIN_8, UART1, TxPin);\nimpl_pin!(PIN_9, UART1, RxPin);\nimpl_pin!(PIN_10, UART1, CtsPin);\nimpl_pin!(PIN_11, UART1, RtsPin);\nimpl_pin!(PIN_12, UART0, TxPin);\nimpl_pin!(PIN_13, UART0, RxPin);\nimpl_pin!(PIN_14, UART0, CtsPin);\nimpl_pin!(PIN_15, UART0, RtsPin);\nimpl_pin!(PIN_16, UART0, TxPin);\nimpl_pin!(PIN_17, UART0, RxPin);\nimpl_pin!(PIN_18, UART0, CtsPin);\nimpl_pin!(PIN_19, UART0, RtsPin);\nimpl_pin!(PIN_20, UART1, TxPin);\nimpl_pin!(PIN_21, UART1, RxPin);\nimpl_pin!(PIN_22, UART1, CtsPin);\nimpl_pin!(PIN_23, UART1, RtsPin);\nimpl_pin!(PIN_24, UART1, TxPin);\nimpl_pin!(PIN_25, UART1, RxPin);\nimpl_pin!(PIN_26, UART1, CtsPin);\nimpl_pin!(PIN_27, UART1, RtsPin);\nimpl_pin!(PIN_28, UART0, TxPin);\nimpl_pin!(PIN_29, UART0, RxPin);\n\n// Additional functions added by all 2350s\n#[cfg(feature = \"_rp235x\")]\nimpl_pin!(PIN_2, UART0, TxPin);\n#[cfg(feature = \"_rp235x\")]\nimpl_pin!(PIN_3, UART0, RxPin);\n#[cfg(feature = \"_rp235x\")]\nimpl_pin!(PIN_6, UART1, TxPin);\n#[cfg(feature = \"_rp235x\")]\nimpl_pin!(PIN_7, UART1, RxPin);\n#[cfg(feature = \"_rp235x\")]\nimpl_pin!(PIN_10, UART1, TxPin);\n#[cfg(feature = \"_rp235x\")]\nimpl_pin!(PIN_11, UART1, RxPin);\n#[cfg(feature = \"_rp235x\")]\nimpl_pin!(PIN_14, UART0, TxPin);\n#[cfg(feature = \"_rp235x\")]\nimpl_pin!(PIN_15, UART0, RxPin);\n#[cfg(feature = \"_rp235x\")]\nimpl_pin!(PIN_18, UART0, TxPin);\n#[cfg(feature = \"_rp235x\")]\nimpl_pin!(PIN_19, UART0, RxPin);\n#[cfg(feature = \"_rp235x\")]\nimpl_pin!(PIN_22, UART1, TxPin);\n#[cfg(feature = \"_rp235x\")]\nimpl_pin!(PIN_23, UART1, RxPin);\n#[cfg(feature = \"_rp235x\")]\nimpl_pin!(PIN_26, UART1, TxPin);\n#[cfg(feature = \"_rp235x\")]\nimpl_pin!(PIN_27, UART1, RxPin);\n\n// Additional pins added by larger 2350 packages.\n#[cfg(feature = \"rp235xb\")]\nimpl_pin!(PIN_30, UART0, CtsPin);\n#[cfg(feature = \"rp235xb\")]\nimpl_pin!(PIN_31, UART0, RtsPin);\n#[cfg(feature = \"rp235xb\")]\nimpl_pin!(PIN_32, UART0, TxPin);\n#[cfg(feature = \"rp235xb\")]\nimpl_pin!(PIN_33, UART0, RxPin);\n#[cfg(feature = \"rp235xb\")]\nimpl_pin!(PIN_34, UART0, CtsPin);\n#[cfg(feature = \"rp235xb\")]\nimpl_pin!(PIN_35, UART0, RtsPin);\n#[cfg(feature = \"rp235xb\")]\nimpl_pin!(PIN_36, UART1, TxPin);\n#[cfg(feature = \"rp235xb\")]\nimpl_pin!(PIN_37, UART1, RxPin);\n#[cfg(feature = \"rp235xb\")]\nimpl_pin!(PIN_38, UART1, CtsPin);\n#[cfg(feature = \"rp235xb\")]\nimpl_pin!(PIN_39, UART1, RtsPin);\n#[cfg(feature = \"rp235xb\")]\nimpl_pin!(PIN_40, UART1, TxPin);\n#[cfg(feature = \"rp235xb\")]\nimpl_pin!(PIN_41, UART1, RxPin);\n#[cfg(feature = \"rp235xb\")]\nimpl_pin!(PIN_42, UART1, CtsPin);\n#[cfg(feature = \"rp235xb\")]\nimpl_pin!(PIN_43, UART1, RtsPin);\n#[cfg(feature = \"rp235xb\")]\nimpl_pin!(PIN_44, UART0, TxPin);\n#[cfg(feature = \"rp235xb\")]\nimpl_pin!(PIN_45, UART0, RxPin);\n#[cfg(feature = \"rp235xb\")]\nimpl_pin!(PIN_46, UART0, CtsPin);\n#[cfg(feature = \"rp235xb\")]\nimpl_pin!(PIN_47, UART0, RtsPin);\n\n#[cfg(feature = \"rp235xb\")]\nimpl_pin!(PIN_30, UART0, TxPin);\n#[cfg(feature = \"rp235xb\")]\nimpl_pin!(PIN_31, UART0, RxPin);\n#[cfg(feature = \"rp235xb\")]\nimpl_pin!(PIN_34, UART0, TxPin);\n#[cfg(feature = \"rp235xb\")]\nimpl_pin!(PIN_35, UART0, RxPin);\n#[cfg(feature = \"rp235xb\")]\nimpl_pin!(PIN_38, UART1, TxPin);\n#[cfg(feature = \"rp235xb\")]\nimpl_pin!(PIN_39, UART1, RxPin);\n#[cfg(feature = \"rp235xb\")]\nimpl_pin!(PIN_42, UART1, TxPin);\n#[cfg(feature = \"rp235xb\")]\nimpl_pin!(PIN_43, UART1, RxPin);\n#[cfg(feature = \"rp235xb\")]\nimpl_pin!(PIN_46, UART0, TxPin);\n#[cfg(feature = \"rp235xb\")]\nimpl_pin!(PIN_47, UART0, RxPin);\n"
  },
  {
    "path": "embassy-rp/src/usb.rs",
    "content": "//! USB driver.\nuse core::future::poll_fn;\nuse core::marker::PhantomData;\nuse core::slice;\nuse core::sync::atomic::{Ordering, compiler_fence};\nuse core::task::Poll;\n\nuse embassy_hal_internal::PeripheralType;\nuse embassy_sync::waitqueue::AtomicWaker;\nuse embassy_usb_driver as driver;\nuse embassy_usb_driver::{\n    Direction, EndpointAddress, EndpointAllocError, EndpointError, EndpointInfo, EndpointType, Event, Unsupported,\n};\n\nuse crate::interrupt::typelevel::{Binding, Interrupt};\nuse crate::{Peri, RegExt, interrupt, pac, peripherals};\n\ntrait SealedInstance {\n    fn regs() -> crate::pac::usb::Usb;\n    fn dpram() -> crate::pac::usb_dpram::UsbDpram;\n}\n\n/// USB peripheral instance.\n#[allow(private_bounds)]\npub trait Instance: SealedInstance + PeripheralType + 'static {\n    /// Interrupt for this peripheral.\n    type Interrupt: interrupt::typelevel::Interrupt;\n}\n\nimpl crate::usb::SealedInstance for peripherals::USB {\n    fn regs() -> pac::usb::Usb {\n        pac::USB\n    }\n    fn dpram() -> crate::pac::usb_dpram::UsbDpram {\n        pac::USB_DPRAM\n    }\n}\n\nimpl crate::usb::Instance for peripherals::USB {\n    type Interrupt = crate::interrupt::typelevel::USBCTRL_IRQ;\n}\n\nconst EP_COUNT: usize = 16;\nconst EP_MEMORY_SIZE: usize = 4096;\nconst EP_MEMORY: *mut u8 = pac::USB_DPRAM.as_ptr() as *mut u8;\n\nstatic BUS_WAKER: AtomicWaker = AtomicWaker::new();\nstatic EP_IN_WAKERS: [AtomicWaker; EP_COUNT] = [const { AtomicWaker::new() }; EP_COUNT];\nstatic EP_OUT_WAKERS: [AtomicWaker; EP_COUNT] = [const { AtomicWaker::new() }; EP_COUNT];\n\nstruct EndpointBuffer<T: Instance> {\n    addr: u16,\n    len: u16,\n    _phantom: PhantomData<T>,\n}\n\nimpl<T: Instance> EndpointBuffer<T> {\n    const fn new(addr: u16, len: u16) -> Self {\n        Self {\n            addr,\n            len,\n            _phantom: PhantomData,\n        }\n    }\n\n    fn read(&mut self, buf: &mut [u8]) {\n        assert!(buf.len() <= self.len as usize);\n        compiler_fence(Ordering::SeqCst);\n        let mem = unsafe { slice::from_raw_parts(EP_MEMORY.add(self.addr as _), buf.len()) };\n        buf.copy_from_slice(mem);\n        compiler_fence(Ordering::SeqCst);\n    }\n\n    fn write(&mut self, buf: &[u8]) {\n        assert!(buf.len() <= self.len as usize);\n        compiler_fence(Ordering::SeqCst);\n        let mem = unsafe { slice::from_raw_parts_mut(EP_MEMORY.add(self.addr as _), buf.len()) };\n        mem.copy_from_slice(buf);\n        compiler_fence(Ordering::SeqCst);\n    }\n}\n\n#[derive(Debug, Clone, Copy)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\nstruct EndpointData {\n    ep_type: EndpointType, // only valid if used\n    max_packet_size: u16,\n    used: bool,\n}\n\nimpl EndpointData {\n    const fn new() -> Self {\n        Self {\n            ep_type: EndpointType::Bulk,\n            max_packet_size: 0,\n            used: false,\n        }\n    }\n}\n\n/// RP2040 USB driver handle.\npub struct Driver<'d, T: Instance> {\n    phantom: PhantomData<&'d mut T>,\n    ep_in: [EndpointData; EP_COUNT],\n    ep_out: [EndpointData; EP_COUNT],\n    ep_mem_free: u16, // first free address in EP mem, in bytes.\n}\n\nimpl<'d, T: Instance> Driver<'d, T> {\n    /// Create a new USB driver.\n    pub fn new(_usb: Peri<'d, T>, _irq: impl Binding<T::Interrupt, InterruptHandler<T>>) -> Self {\n        T::Interrupt::unpend();\n        unsafe { T::Interrupt::enable() };\n\n        let regs = T::regs();\n        unsafe {\n            // zero fill regs\n            let p = regs.as_ptr() as *mut u32;\n            for i in 0..0x9c / 4 {\n                p.add(i).write_volatile(0)\n            }\n\n            // zero fill epmem\n            let p = EP_MEMORY as *mut u32;\n            for i in 0..0x100 / 4 {\n                p.add(i).write_volatile(0)\n            }\n        }\n\n        regs.usb_muxing().write(|w| {\n            w.set_to_phy(true);\n            w.set_softcon(true);\n        });\n        regs.usb_pwr().write(|w| {\n            w.set_vbus_detect(true);\n            w.set_vbus_detect_override_en(true);\n        });\n        regs.main_ctrl().write(|w| {\n            w.set_controller_en(true);\n        });\n\n        // Initialize the bus so that it signals that power is available\n        BUS_WAKER.wake();\n\n        Self {\n            phantom: PhantomData,\n            ep_in: [EndpointData::new(); EP_COUNT],\n            ep_out: [EndpointData::new(); EP_COUNT],\n            ep_mem_free: 0x180, // data buffer region\n        }\n    }\n\n    fn alloc_endpoint<D: Dir>(\n        &mut self,\n        ep_type: EndpointType,\n        ep_addr: Option<EndpointAddress>,\n        max_packet_size: u16,\n        interval_ms: u8,\n    ) -> Result<Endpoint<'d, T, D>, driver::EndpointAllocError> {\n        trace!(\n            \"allocating type={:?} mps={:?} interval_ms={}, dir={:?}\",\n            ep_type,\n            max_packet_size,\n            interval_ms,\n            D::dir()\n        );\n\n        let alloc = match D::dir() {\n            Direction::Out => &mut self.ep_out,\n            Direction::In => &mut self.ep_in,\n        };\n\n        let index = if let Some(addr) = ep_addr {\n            // Use the specified endpoint address\n            let requested_index = addr.index();\n            if requested_index == 0 || requested_index >= EP_COUNT {\n                return Err(EndpointAllocError);\n            }\n            if alloc[requested_index].used {\n                return Err(EndpointAllocError);\n            }\n            Some((requested_index, &mut alloc[requested_index]))\n        } else {\n            // Find any available endpoint\n            alloc.iter_mut().enumerate().find(|(i, ep)| {\n                if *i == 0 {\n                    return false; // reserved for control pipe\n                }\n                !ep.used\n            })\n        };\n\n        let (index, ep) = index.ok_or(EndpointAllocError)?;\n        assert!(!ep.used);\n\n        // as per datasheet, the maximum buffer size is 64, except for isochronous\n        // endpoints, which are allowed to be up to 1023 bytes.\n        if (ep_type != EndpointType::Isochronous && max_packet_size > 64) || max_packet_size > 1023 {\n            warn!(\"max_packet_size too high: {}\", max_packet_size);\n            return Err(EndpointAllocError);\n        }\n\n        // ep mem addrs must be 64-byte aligned, so there's no point in trying\n        // to allocate smaller chunks to save memory.\n        let len = (max_packet_size + 63) / 64 * 64;\n\n        let addr = self.ep_mem_free;\n        if addr + len > EP_MEMORY_SIZE as u16 {\n            warn!(\"Endpoint memory full\");\n            return Err(EndpointAllocError);\n        }\n        self.ep_mem_free += len;\n\n        let buf = EndpointBuffer {\n            addr,\n            len,\n            _phantom: PhantomData,\n        };\n\n        trace!(\"  index={} addr={} len={}\", index, buf.addr, buf.len);\n\n        ep.ep_type = ep_type;\n        ep.used = true;\n        ep.max_packet_size = max_packet_size;\n\n        let ep_type_reg = match ep_type {\n            EndpointType::Bulk => pac::usb_dpram::vals::EpControlEndpointType::BULK,\n            EndpointType::Control => pac::usb_dpram::vals::EpControlEndpointType::CONTROL,\n            EndpointType::Interrupt => pac::usb_dpram::vals::EpControlEndpointType::INTERRUPT,\n            EndpointType::Isochronous => pac::usb_dpram::vals::EpControlEndpointType::ISOCHRONOUS,\n        };\n\n        match D::dir() {\n            Direction::Out => T::dpram().ep_out_control(index - 1).write(|w| {\n                w.set_enable(false);\n                w.set_buffer_address(addr);\n                w.set_interrupt_per_buff(true);\n                w.set_endpoint_type(ep_type_reg);\n            }),\n            Direction::In => T::dpram().ep_in_control(index - 1).write(|w| {\n                w.set_enable(false);\n                w.set_buffer_address(addr);\n                w.set_interrupt_per_buff(true);\n                w.set_endpoint_type(ep_type_reg);\n            }),\n        }\n\n        Ok(Endpoint {\n            _phantom: PhantomData,\n            info: EndpointInfo {\n                addr: EndpointAddress::from_parts(index, D::dir()),\n                ep_type,\n                max_packet_size,\n                interval_ms,\n            },\n            buf,\n        })\n    }\n}\n\n/// USB interrupt handler.\npub struct InterruptHandler<T: Instance> {\n    _uart: PhantomData<T>,\n}\n\nimpl<T: Instance> interrupt::typelevel::Handler<T::Interrupt> for InterruptHandler<T> {\n    unsafe fn on_interrupt() {\n        let regs = T::regs();\n        //let x = regs.istr().read().0;\n        //trace!(\"USB IRQ: {:08x}\", x);\n\n        let ints = regs.ints().read();\n\n        if ints.bus_reset() {\n            regs.inte().write_clear(|w| w.set_bus_reset(true));\n            BUS_WAKER.wake();\n        }\n        if ints.dev_resume_from_host() {\n            regs.inte().write_clear(|w| w.set_dev_resume_from_host(true));\n            BUS_WAKER.wake();\n        }\n        if ints.dev_suspend() {\n            regs.inte().write_clear(|w| w.set_dev_suspend(true));\n            BUS_WAKER.wake();\n        }\n        if ints.setup_req() {\n            regs.inte().write_clear(|w| w.set_setup_req(true));\n            EP_OUT_WAKERS[0].wake();\n        }\n\n        if ints.buff_status() {\n            let s = regs.buff_status().read();\n            regs.buff_status().write_value(s);\n\n            for i in 0..EP_COUNT {\n                if s.ep_in(i) {\n                    EP_IN_WAKERS[i].wake();\n                }\n                if s.ep_out(i) {\n                    EP_OUT_WAKERS[i].wake();\n                }\n            }\n        }\n    }\n}\n\nimpl<'d, T: Instance> driver::Driver<'d> for Driver<'d, T> {\n    type EndpointOut = Endpoint<'d, T, Out>;\n    type EndpointIn = Endpoint<'d, T, In>;\n    type ControlPipe = ControlPipe<'d, T>;\n    type Bus = Bus<'d, T>;\n\n    fn alloc_endpoint_in(\n        &mut self,\n        ep_type: EndpointType,\n        ep_addr: Option<EndpointAddress>,\n        max_packet_size: u16,\n        interval_ms: u8,\n    ) -> Result<Self::EndpointIn, driver::EndpointAllocError> {\n        self.alloc_endpoint(ep_type, ep_addr, max_packet_size, interval_ms)\n    }\n\n    fn alloc_endpoint_out(\n        &mut self,\n        ep_type: EndpointType,\n        ep_addr: Option<EndpointAddress>,\n        max_packet_size: u16,\n        interval_ms: u8,\n    ) -> Result<Self::EndpointOut, driver::EndpointAllocError> {\n        self.alloc_endpoint(ep_type, ep_addr, max_packet_size, interval_ms)\n    }\n\n    fn start(self, control_max_packet_size: u16) -> (Self::Bus, Self::ControlPipe) {\n        let regs = T::regs();\n        regs.inte().write(|w| {\n            w.set_bus_reset(true);\n            w.set_buff_status(true);\n            w.set_dev_resume_from_host(true);\n            w.set_dev_suspend(true);\n            w.set_setup_req(true);\n        });\n        regs.int_ep_ctrl().write(|w| {\n            w.set_int_ep_active(0xFFFE); // all EPs\n        });\n        regs.sie_ctrl().write(|w| {\n            w.set_ep0_int_1buf(true);\n            w.set_pullup_en(true);\n        });\n\n        trace!(\"enabled\");\n\n        (\n            Bus {\n                phantom: PhantomData,\n                inited: false,\n                ep_out: self.ep_out,\n            },\n            ControlPipe {\n                _phantom: PhantomData,\n                max_packet_size: control_max_packet_size,\n            },\n        )\n    }\n}\n\n/// Type representing the RP USB bus.\npub struct Bus<'d, T: Instance> {\n    phantom: PhantomData<&'d mut T>,\n    ep_out: [EndpointData; EP_COUNT],\n    inited: bool,\n}\n\nimpl<'d, T: Instance> driver::Bus for Bus<'d, T> {\n    async fn poll(&mut self) -> Event {\n        poll_fn(move |cx| {\n            BUS_WAKER.register(cx.waker());\n\n            // TODO: implement VBUS detection.\n            if !self.inited {\n                self.inited = true;\n                return Poll::Ready(Event::PowerDetected);\n            }\n\n            let regs = T::regs();\n            let siestatus = regs.sie_status().read();\n            let intrstatus = regs.intr().read();\n\n            if siestatus.resume() || intrstatus.dev_resume_from_host() {\n                regs.sie_status().write(|w| w.set_resume(true));\n                return Poll::Ready(Event::Resume);\n            }\n\n            if siestatus.bus_reset() {\n                regs.sie_status().write(|w| {\n                    w.set_bus_reset(true);\n                    w.set_setup_rec(true);\n                });\n                regs.buff_status().write(|w| w.0 = 0xFFFF_FFFF);\n                regs.addr_endp().write(|w| w.set_address(0));\n\n                for i in 1..EP_COUNT {\n                    T::dpram().ep_in_control(i - 1).modify(|w| w.set_enable(false));\n                    T::dpram().ep_out_control(i - 1).modify(|w| w.set_enable(false));\n                }\n\n                for w in &EP_IN_WAKERS {\n                    w.wake()\n                }\n                for w in &EP_OUT_WAKERS {\n                    w.wake()\n                }\n                return Poll::Ready(Event::Reset);\n            }\n\n            if siestatus.suspended() && intrstatus.dev_suspend() {\n                regs.sie_status().write(|w| w.set_suspended(true));\n                return Poll::Ready(Event::Suspend);\n            }\n\n            // no pending event. Reenable all irqs.\n            regs.inte().write_set(|w| {\n                w.set_bus_reset(true);\n                w.set_dev_resume_from_host(true);\n                w.set_dev_suspend(true);\n            });\n            Poll::Pending\n        })\n        .await\n    }\n\n    fn endpoint_set_stalled(&mut self, ep_addr: EndpointAddress, stalled: bool) {\n        let n = ep_addr.index();\n\n        if n == 0 {\n            T::regs().ep_stall_arm().modify(|w| {\n                if ep_addr.is_in() {\n                    w.set_ep0_in(stalled);\n                } else {\n                    w.set_ep0_out(stalled);\n                }\n            });\n        }\n\n        let ctrl = if ep_addr.is_in() {\n            T::dpram().ep_in_buffer_control(n)\n        } else {\n            T::dpram().ep_out_buffer_control(n)\n        };\n\n        ctrl.modify(|w| w.set_stall(stalled));\n\n        let wakers = if ep_addr.is_in() { &EP_IN_WAKERS } else { &EP_OUT_WAKERS };\n        wakers[n].wake();\n    }\n\n    fn endpoint_is_stalled(&mut self, ep_addr: EndpointAddress) -> bool {\n        let n = ep_addr.index();\n\n        let ctrl = if ep_addr.is_in() {\n            T::dpram().ep_in_buffer_control(n)\n        } else {\n            T::dpram().ep_out_buffer_control(n)\n        };\n\n        ctrl.read().stall()\n    }\n\n    fn endpoint_set_enabled(&mut self, ep_addr: EndpointAddress, enabled: bool) {\n        trace!(\"set_enabled {:?} {}\", ep_addr, enabled);\n        if ep_addr.index() == 0 {\n            return;\n        }\n\n        let n = ep_addr.index();\n        match ep_addr.direction() {\n            Direction::In => {\n                T::dpram().ep_in_control(n - 1).modify(|w| w.set_enable(enabled));\n                T::dpram().ep_in_buffer_control(ep_addr.index()).write(|w| {\n                    w.set_pid(0, true); // first packet is DATA0, but PID is flipped before\n                });\n                EP_IN_WAKERS[n].wake();\n            }\n            Direction::Out => {\n                T::dpram().ep_out_control(n - 1).modify(|w| w.set_enable(enabled));\n\n                T::dpram().ep_out_buffer_control(ep_addr.index()).write(|w| {\n                    w.set_pid(0, false);\n                    w.set_length(0, self.ep_out[n].max_packet_size);\n                });\n                cortex_m::asm::delay(12);\n                T::dpram().ep_out_buffer_control(ep_addr.index()).write(|w| {\n                    w.set_pid(0, false);\n                    w.set_length(0, self.ep_out[n].max_packet_size);\n                    w.set_available(0, true);\n                });\n                EP_OUT_WAKERS[n].wake();\n            }\n        }\n    }\n\n    async fn enable(&mut self) {}\n\n    async fn disable(&mut self) {}\n\n    async fn remote_wakeup(&mut self) -> Result<(), Unsupported> {\n        Err(Unsupported)\n    }\n}\n\ntrait Dir {\n    fn dir() -> Direction;\n}\n\n/// Type for In direction.\npub enum In {}\nimpl Dir for In {\n    fn dir() -> Direction {\n        Direction::In\n    }\n}\n\n/// Type for Out direction.\npub enum Out {}\nimpl Dir for Out {\n    fn dir() -> Direction {\n        Direction::Out\n    }\n}\n\n/// Endpoint for RP USB driver.\npub struct Endpoint<'d, T: Instance, D> {\n    _phantom: PhantomData<(&'d mut T, D)>,\n    info: EndpointInfo,\n    buf: EndpointBuffer<T>,\n}\n\nimpl<'d, T: Instance> driver::Endpoint for Endpoint<'d, T, In> {\n    fn info(&self) -> &EndpointInfo {\n        &self.info\n    }\n\n    async fn wait_enabled(&mut self) {\n        trace!(\"wait_enabled IN WAITING\");\n        let index = self.info.addr.index();\n        poll_fn(|cx| {\n            EP_IN_WAKERS[index].register(cx.waker());\n            let val = T::dpram().ep_in_control(self.info.addr.index() - 1).read();\n            if val.enable() { Poll::Ready(()) } else { Poll::Pending }\n        })\n        .await;\n        trace!(\"wait_enabled IN OK\");\n    }\n}\n\nimpl<'d, T: Instance> driver::Endpoint for Endpoint<'d, T, Out> {\n    fn info(&self) -> &EndpointInfo {\n        &self.info\n    }\n\n    async fn wait_enabled(&mut self) {\n        trace!(\"wait_enabled OUT WAITING\");\n        let index = self.info.addr.index();\n        poll_fn(|cx| {\n            EP_OUT_WAKERS[index].register(cx.waker());\n            let val = T::dpram().ep_out_control(self.info.addr.index() - 1).read();\n            if val.enable() { Poll::Ready(()) } else { Poll::Pending }\n        })\n        .await;\n        trace!(\"wait_enabled OUT OK\");\n    }\n}\n\nimpl<'d, T: Instance> driver::EndpointOut for Endpoint<'d, T, Out> {\n    async fn read(&mut self, buf: &mut [u8]) -> Result<usize, EndpointError> {\n        trace!(\"READ WAITING, buf.len() = {}\", buf.len());\n        let index = self.info.addr.index();\n        let val = poll_fn(|cx| {\n            EP_OUT_WAKERS[index].register(cx.waker());\n            let val = T::dpram().ep_out_buffer_control(index).read();\n            if val.available(0) {\n                Poll::Pending\n            } else {\n                Poll::Ready(val)\n            }\n        })\n        .await;\n\n        let rx_len = val.length(0) as usize;\n        if rx_len > buf.len() {\n            return Err(EndpointError::BufferOverflow);\n        }\n        self.buf.read(&mut buf[..rx_len]);\n\n        trace!(\"READ OK, rx_len = {}\", rx_len);\n\n        let pid = !val.pid(0);\n        T::dpram().ep_out_buffer_control(index).write(|w| {\n            w.set_pid(0, pid);\n            w.set_length(0, self.info.max_packet_size);\n        });\n        cortex_m::asm::delay(12);\n        T::dpram().ep_out_buffer_control(index).write(|w| {\n            w.set_pid(0, pid);\n            w.set_length(0, self.info.max_packet_size);\n            w.set_available(0, true);\n        });\n\n        Ok(rx_len)\n    }\n}\n\nimpl<'d, T: Instance> driver::EndpointIn for Endpoint<'d, T, In> {\n    async fn write(&mut self, buf: &[u8]) -> Result<(), EndpointError> {\n        if buf.len() > self.info.max_packet_size as usize {\n            return Err(EndpointError::BufferOverflow);\n        }\n\n        trace!(\"WRITE WAITING\");\n\n        let index = self.info.addr.index();\n        let val = poll_fn(|cx| {\n            EP_IN_WAKERS[index].register(cx.waker());\n            let val = T::dpram().ep_in_buffer_control(index).read();\n            if val.available(0) {\n                Poll::Pending\n            } else {\n                Poll::Ready(val)\n            }\n        })\n        .await;\n\n        self.buf.write(buf);\n\n        let pid = !val.pid(0);\n        T::dpram().ep_in_buffer_control(index).write(|w| {\n            w.set_pid(0, pid);\n            w.set_length(0, buf.len() as _);\n            w.set_full(0, true);\n        });\n        cortex_m::asm::delay(12);\n        T::dpram().ep_in_buffer_control(index).write(|w| {\n            w.set_pid(0, pid);\n            w.set_length(0, buf.len() as _);\n            w.set_full(0, true);\n            w.set_available(0, true);\n        });\n\n        trace!(\"WRITE OK\");\n\n        Ok(())\n    }\n}\n\n/// Control pipe for RP USB driver.\npub struct ControlPipe<'d, T: Instance> {\n    _phantom: PhantomData<&'d mut T>,\n    max_packet_size: u16,\n}\n\nimpl<'d, T: Instance> driver::ControlPipe for ControlPipe<'d, T> {\n    fn max_packet_size(&self) -> usize {\n        64\n    }\n\n    async fn setup(&mut self) -> [u8; 8] {\n        loop {\n            trace!(\"SETUP read waiting\");\n            let regs = T::regs();\n            regs.inte().write_set(|w| w.set_setup_req(true));\n\n            poll_fn(|cx| {\n                EP_OUT_WAKERS[0].register(cx.waker());\n                let regs = T::regs();\n                if regs.sie_status().read().setup_rec() {\n                    Poll::Ready(())\n                } else {\n                    Poll::Pending\n                }\n            })\n            .await;\n\n            let mut buf = [0; 8];\n            EndpointBuffer::<T>::new(0, 8).read(&mut buf);\n\n            let regs = T::regs();\n            regs.sie_status().write(|w| w.set_setup_rec(true));\n\n            // set PID to 0, so (after toggling) first DATA is PID 1\n            T::dpram().ep_in_buffer_control(0).write(|w| w.set_pid(0, false));\n            T::dpram().ep_out_buffer_control(0).write(|w| w.set_pid(0, false));\n\n            trace!(\"SETUP read ok\");\n            return buf;\n        }\n    }\n\n    async fn data_out(&mut self, buf: &mut [u8], first: bool, last: bool) -> Result<usize, EndpointError> {\n        let bufcontrol = T::dpram().ep_out_buffer_control(0);\n        let pid = !bufcontrol.read().pid(0);\n        bufcontrol.write(|w| {\n            w.set_length(0, self.max_packet_size);\n            w.set_pid(0, pid);\n        });\n        cortex_m::asm::delay(12);\n        bufcontrol.write(|w| {\n            w.set_length(0, self.max_packet_size);\n            w.set_pid(0, pid);\n            w.set_available(0, true);\n        });\n\n        trace!(\"control: data_out len={} first={} last={}\", buf.len(), first, last);\n        let val = poll_fn(|cx| {\n            EP_OUT_WAKERS[0].register(cx.waker());\n            let val = T::dpram().ep_out_buffer_control(0).read();\n            if val.available(0) {\n                Poll::Pending\n            } else {\n                Poll::Ready(val)\n            }\n        })\n        .await;\n\n        let rx_len = val.length(0) as _;\n        trace!(\"control data_out DONE, rx_len = {}\", rx_len);\n\n        if rx_len > buf.len() {\n            return Err(EndpointError::BufferOverflow);\n        }\n        EndpointBuffer::<T>::new(0x100, 64).read(&mut buf[..rx_len]);\n\n        Ok(rx_len)\n    }\n\n    async fn data_in(&mut self, data: &[u8], first: bool, last: bool) -> Result<(), EndpointError> {\n        trace!(\"control: data_in len={} first={} last={}\", data.len(), first, last);\n\n        if data.len() > 64 {\n            return Err(EndpointError::BufferOverflow);\n        }\n        EndpointBuffer::<T>::new(0x100, 64).write(data);\n\n        let bufcontrol = T::dpram().ep_in_buffer_control(0);\n        let pid = !bufcontrol.read().pid(0);\n        bufcontrol.write(|w| {\n            w.set_length(0, data.len() as _);\n            w.set_pid(0, pid);\n            w.set_full(0, true);\n        });\n        cortex_m::asm::delay(12);\n        bufcontrol.write(|w| {\n            w.set_length(0, data.len() as _);\n            w.set_pid(0, pid);\n            w.set_full(0, true);\n            w.set_available(0, true);\n        });\n\n        poll_fn(|cx| {\n            EP_IN_WAKERS[0].register(cx.waker());\n            let bufcontrol = T::dpram().ep_in_buffer_control(0);\n            if bufcontrol.read().available(0) {\n                Poll::Pending\n            } else {\n                Poll::Ready(())\n            }\n        })\n        .await;\n        trace!(\"control: data_in DONE\");\n\n        if last {\n            // prepare status phase right away.\n            let bufcontrol = T::dpram().ep_out_buffer_control(0);\n            bufcontrol.write(|w| {\n                w.set_length(0, 0);\n                w.set_pid(0, true);\n            });\n            cortex_m::asm::delay(12);\n            bufcontrol.write(|w| {\n                w.set_length(0, 0);\n                w.set_pid(0, true);\n                w.set_available(0, true);\n            });\n        }\n\n        Ok(())\n    }\n\n    async fn accept(&mut self) {\n        trace!(\"control: accept\");\n\n        let bufcontrol = T::dpram().ep_in_buffer_control(0);\n        bufcontrol.write(|w| {\n            w.set_length(0, 0);\n            w.set_pid(0, true);\n            w.set_full(0, true);\n        });\n        cortex_m::asm::delay(12);\n        bufcontrol.write(|w| {\n            w.set_length(0, 0);\n            w.set_pid(0, true);\n            w.set_full(0, true);\n            w.set_available(0, true);\n        });\n\n        // wait for completion before returning, needed so\n        // set_address() doesn't happen early.\n        poll_fn(|cx| {\n            EP_IN_WAKERS[0].register(cx.waker());\n            if bufcontrol.read().available(0) {\n                Poll::Pending\n            } else {\n                Poll::Ready(())\n            }\n        })\n        .await;\n    }\n\n    async fn reject(&mut self) {\n        trace!(\"control: reject\");\n\n        let regs = T::regs();\n        regs.ep_stall_arm().write_set(|w| {\n            w.set_ep0_in(true);\n            w.set_ep0_out(true);\n        });\n        T::dpram().ep_out_buffer_control(0).write(|w| w.set_stall(true));\n        T::dpram().ep_in_buffer_control(0).write(|w| w.set_stall(true));\n    }\n\n    async fn accept_set_address(&mut self, addr: u8) {\n        self.accept().await;\n\n        let regs = T::regs();\n        trace!(\"setting addr: {}\", addr);\n        regs.addr_endp().write(|w| w.set_address(addr))\n    }\n}\n"
  },
  {
    "path": "embassy-rp/src/watchdog.rs",
    "content": "//! Watchdog\n//!\n//! The watchdog is a countdown timer that can restart parts of the chip if it reaches zero. This can be used to restart the\n//! processor if software gets stuck in an infinite loop. The programmer must periodically write a value to the watchdog to\n//! stop it from reaching zero.\n//!\n//! Credit: based on `rp-hal` implementation (also licensed Apache+MIT)\n\nuse core::marker::PhantomData;\n\nuse embassy_time::Duration;\n\nuse crate::peripherals::WATCHDOG;\nuse crate::{Peri, pac};\n\n/// The reason for a system reset from the watchdog.\n#[derive(Debug, Copy, Clone, PartialEq, Eq)]\npub enum ResetReason {\n    /// The reset was forced.\n    Forced,\n    /// The watchdog was not fed in time.\n    TimedOut,\n}\n\n/// Watchdog peripheral\npub struct Watchdog {\n    phantom: PhantomData<WATCHDOG>,\n}\n\nimpl Watchdog {\n    /// Create a new `Watchdog`\n    pub fn new(_watchdog: Peri<'static, WATCHDOG>) -> Self {\n        Self { phantom: PhantomData }\n    }\n\n    /// Start tick generation on clk_tick which is driven from clk_ref.\n    ///\n    /// # Arguments\n    ///\n    /// * `cycles` - Total number of tick cycles before the next tick is generated.\n    ///   It is expected to be the frequency in MHz of clk_ref.\n    #[cfg(feature = \"rp2040\")]\n    pub fn enable_tick_generation(&mut self, cycles: u8) {\n        let watchdog = pac::WATCHDOG;\n        watchdog.tick().write(|w| {\n            w.set_enable(true);\n            w.set_cycles(cycles.into())\n        });\n    }\n\n    /// Defines whether or not the watchdog timer should be paused when processor(s) are in debug mode\n    /// or when JTAG is accessing bus fabric\n    pub fn pause_on_debug(&mut self, pause: bool) {\n        let watchdog = pac::WATCHDOG;\n        watchdog.ctrl().modify(|w| {\n            w.set_pause_dbg0(pause);\n            w.set_pause_dbg1(pause);\n            w.set_pause_jtag(pause);\n        })\n    }\n\n    fn load_counter(&self, counter: u32) {\n        let watchdog = pac::WATCHDOG;\n        watchdog.load().write_value(pac::watchdog::regs::Load(counter));\n    }\n\n    fn enable(&self, bit: bool) {\n        let watchdog = pac::WATCHDOG;\n        watchdog.ctrl().modify(|w| w.set_enable(bit))\n    }\n\n    // Configure which hardware will be reset by the watchdog\n    // (everything except ROSC, XOSC)\n    fn configure_wdog_reset_triggers(&self) {\n        let psm = pac::PSM;\n        psm.wdsel().write_value(pac::psm::regs::Wdsel(\n            0x0001ffff & !(0x01 << 0usize) & !(0x01 << 1usize),\n        ));\n    }\n\n    /// Feed the watchdog timer\n    pub fn feed(&mut self, timeout: Duration) {\n        #[cfg(feature = \"rp2040\")]\n        const MAX_PERIOD: u32 = 0xFFFFFF / 2;\n        #[cfg(feature = \"_rp235x\")]\n        const MAX_PERIOD: u32 = 0xFFFFFF;\n\n        let timeout_us = timeout.as_micros();\n        if timeout_us > (MAX_PERIOD) as u64 {\n            panic!(\"Period cannot exceed {} microseconds\", MAX_PERIOD);\n        }\n        let timeout_us = timeout_us as u32;\n\n        // Due to a logic error, the watchdog decrements by 2 and\n        // the load value must be compensated; see RP2040-E1\n        // This errata is fixed in the RP235x\n        let load_value = if cfg!(feature = \"rp2040\") {\n            timeout_us * 2\n        } else {\n            timeout_us\n        };\n\n        self.load_counter(load_value)\n    }\n\n    /// Start the watchdog timer\n    pub fn start(&mut self, initial_timeout: Duration) {\n        self.enable(false);\n        self.configure_wdog_reset_triggers();\n        self.feed(initial_timeout);\n        self.enable(true);\n    }\n\n    /// Stop the watchdog timer\n    pub fn stop(&mut self) {\n        self.enable(false);\n    }\n\n    /// Trigger a system reset\n    pub fn trigger_reset(&mut self) {\n        self.configure_wdog_reset_triggers();\n        self.pause_on_debug(false);\n        self.enable(true);\n        let watchdog = pac::WATCHDOG;\n        watchdog.ctrl().write(|w| {\n            w.set_trigger(true);\n        })\n    }\n\n    /// Store data in scratch register\n    pub fn set_scratch(&mut self, index: usize, value: u32) {\n        let watchdog = pac::WATCHDOG;\n        match index {\n            0 => watchdog.scratch0().write(|w| *w = value),\n            1 => watchdog.scratch1().write(|w| *w = value),\n            2 => watchdog.scratch2().write(|w| *w = value),\n            3 => watchdog.scratch3().write(|w| *w = value),\n            4 => watchdog.scratch4().write(|w| *w = value),\n            5 => watchdog.scratch5().write(|w| *w = value),\n            6 => watchdog.scratch6().write(|w| *w = value),\n            7 => watchdog.scratch7().write(|w| *w = value),\n            _ => panic!(\"Invalid watchdog scratch index\"),\n        }\n    }\n\n    /// Read data from scratch register\n    pub fn get_scratch(&mut self, index: usize) -> u32 {\n        let watchdog = pac::WATCHDOG;\n        match index {\n            0 => watchdog.scratch0().read(),\n            1 => watchdog.scratch1().read(),\n            2 => watchdog.scratch2().read(),\n            3 => watchdog.scratch3().read(),\n            4 => watchdog.scratch4().read(),\n            5 => watchdog.scratch5().read(),\n            6 => watchdog.scratch6().read(),\n            7 => watchdog.scratch7().read(),\n            _ => panic!(\"Invalid watchdog scratch index\"),\n        }\n    }\n\n    /// Get the reason for the last system reset, if it was caused by the watchdog.\n    pub fn reset_reason(&self) -> Option<ResetReason> {\n        let watchdog = pac::WATCHDOG;\n        let reason = watchdog.reason().read();\n        if reason.force() {\n            Some(ResetReason::Forced)\n        } else if reason.timer() {\n            Some(ResetReason::TimedOut)\n        } else {\n            None\n        }\n    }\n}\n"
  },
  {
    "path": "embassy-stm32/CHANGELOG.md",
    "content": "# Changelog for embassy-stm32\n\nAll notable changes to this project will be documented in this file.\n\nThe format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),\nand this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).\n\n<!-- next-header -->\n## Unreleased - ReleaseDate\n\nADC:\n- feat: stm32/adc: add `VrefInt::calibrated_value()` for additional chips\n\n## 0.6.0 - 2026-03-10\n\nADC:\n- feat: stm32/adc v2: add support for injected conversions\n- feat: stm32/adc v2: add support for triggered ADC conversions\n- feat: stm32/adc: add `read_latest()` to ring buffer\n- feat: stm32/adc: expose `set_alignment()` on `RingBufferedAdc`\n- fix: stm32/adc: align ring buffer reads to scan sequence length\n- feat: stm32/adc: add low-power improvements for v1/v3\n- feat: stm32/adc: add ADC4 circular DMA and temperature calibration for STM32WBA\n- fix: stm32/adc/v3: correct VREF_CALIB_MV for H5 and H7RS chips\n- fix: stm32/adc/g4: correct VREF_CALIB_MV\n- feat: stm32/adc: add `SealedSpecialConverter` for STM32L0 temperature\n- feat: stm32/adc: add triggers\n\nI2C:\n- fix: stm32/i2c v2: reset peripheral on BERR/ARLO in all paths (async DMA, blocking slave, blocking master)\n- fix: stm32/i2c v2: Fix async slave by using DMA completion instead of TC flag for buffer-full detection\n- change: stm32/i2c v2: slave `respond_to_write` and `respond_to_read` now return actual bytes transferred instead of buffer size (breaking change, matching v1 behavior)\n- fix: stm32/i2c v1: `write_read` was losing last write byte before RESTART due to not waiting for BTF\n- fix: stm32/i2c v1: slave: async `respond_to_write` and `respond_to_read` now return actual bytes transferred instead of buffer size\n\nI2S:\n- fix: stm32/i2s: fix frame misalignment after DMA ring buffer overrun recovery by adding alignment support to `ReadableDmaRingBuffer`\n- fix: stm32/i2s: defer I2SE enable from constructor to `start()` for proper slave frame synchronization\n- fix: stm32/i2s: Use correct PLLI2S clock source for STM32F2 and STM32F7 instead of APB clock\n- fix: stm32/spi-i2s: Add dedicated I2sSdPin trait for I2S data pins to unlock previously unavailable I2S pin configurations\n- feat: stm32/i2s: add `new_rxonly_nomck` and `new_full_duplex_nomck` constructors for use without master clock (e.g. slave mode)\n- feat: add I2S to STM32G4 except G414\n\nQEI:\n- feat: stm32/qei: add counter reset\n- feat: stm32/qei: add auto reload config\n\nDAC:\n- feat: stm32/dac: add sawtooth waveform for G4\n- feat: stm32/dac: add HRTIM DAC triggers\n- feat: stm32/dac: type-erase DAC\n- feat: stm32/dac: add trigger trait impl\n\nOPAMP:\n- feat: stm32/opamp: allow VINM0 as bias input\n\nNew peripheral drivers:\n- feat: stm32: add WWDG (window watchdog) driver\n- feat: stm32: add COMP (Comparator) driver for STM32WBA/U5\n\nDMA:\n- feat: stm32/dma: add memory-to-memory transfers for BDMA/GPDMA\n- feat: stm32/dma: add MDMA support for H7\n- feat: stm32/dma: add `set_alignment` pass-through to GPDMA `ReadableRingBuffer`\n- fix: stm32/xspi: fix DMA\n\nFlash:\n- feat: stm32/flash: add async support for L4\n- feat: stm32/flash: add async support for G0/G4\n- feat: stm32: support flash base address remapping\n\nFDCAN:\n- fix: stm32/fdcan: disable RCC when refcount reaches 0\n- fix: stm32/fdcan: avoid ISR spin on persistent bus errors\n- fix: stm32/fdcan: drain all RX FIFO frames per interrupt in buffered mode\n- change: stm32/can: remove `BusOFF`, `BusPassive`, `BusWarning` from bus error enum (breaking change)\n\nGPIO/EXTI:\n- feat: stm32/gpio,exti: add `from_flex`/`from_input` constructors for `Input` and `ExtiInput`\n\nSPI:\n- fix: stm32/spi: wait for TXE/BSY before disabling SPE\n\nTimer:\n- feat: stm32/timer: add `set_period`, improve PSC/ARR calculation\n- change: stm32/timer/input_capture: use timer word size for all outputs\n\nHRTIM:\n- change: stm32: Change HRTIM implementation to use stm32-hrtim driver for G474/484 and F334\n- feat: stm32/hrtim: add master timer\n\nSDMMC:\n- fix: stm32/sdmmc: add WFE support\n\nCrypto (WBA):\n- feat: stm32/wba: add AES, SAES, and PKA cryptographic drivers\n- feat: stm32/aes: add GMAC and CCM cipher modes\n- fix: stm32/aes: fix CBC/CTR cipher modes to match ST HAL behavior\n- fix: stm32/saes: fix multiple bugs in SAES driver for saes_v1a peripheral\n- feat: stm32/pka: add full ECC support with ECDSA sign/verify and ECDH\n- feat: stm32/pka: add Montgomery, arithmetic, and RSA operations\n\nLow-power:\n- feat: stm32/low-power: add WBA STOP mode support\n- feat: stm32/low-power: add STM32WLEx LPTIM time driver\n- feat: stm32/low-power: add STM32WL5x dual-core support\n\nSTM32F1:\n- feat: stm32f1: add config option to remap JTAG pins\n\nSTM32N6:\n- feat: stm32n6: add PLL3/4 support, configure all ICs and clock multiplexer\n- feat: stm32n6: add LTDC support\n- feat: stm32n6: rewrite RISAF access from raw pointer to PAC\n- feat: stm32n6: implement ClockCalculations for IC1 and IC2\n\nSTM32H7RS:\n- feat: stm32h7rs: add SYSCFG control for internal flash config\n- feat: stm32h7rs: enable PLL2 S/T channels\n\nQSPI:\n- feat: stm32/xspi: add Hexadeca-SPI method for dual DQS pins\n\nSMI:\n- feat: stm32: expose `smi::Instance` publicly\n\nRCC:\n- fix: stm32/rcc: allow linker to optimize out expensive PLL init functions in binaries where the PLL is not used (e.g. most bootloaders)\n\nUSB:\n- fix: don't put USB pins into alternate mode on chips where USB is an additional function\n\nMisc:\n- change: `gpio-init-analog` is now a non-default feature\n- fix: stm32/rng: panic if RNG clock not set\n\n- Upgrade embassy-sync to 0.8.0\n- Upgrade embassy-embedded-hal to 0.6.0\n- Upgrade embassy-usb-synopsys-otg to 0.3.2\n- Upgrade embassy-executor to 0.10.0\n- Upgrade cyw43 to 0.7.0\n\n## 0.5.0 - 2026-01-04\n- Add `receive_waveform` method in `InputCapture`, allowing asynchronous input capture with DMA.\n- fix: stm32: GPDMA driver reset ignored during channel configuration\n- fix: stm32: SPI driver SSOE and SSM manegment, add `nss_output_disable` to SPI Config\n- change: stm32: use typelevel timer type to allow dma for 32 bit timers\n- fix: fix incorrect handling of split interrupts in timer driver\n- feat: allow granular stop for regular usart\n- feat: Add continuous waveform method to SimplePWM\n- change: remove waveform timer method\n- change: low power: store stop mode for dma channels\n- fix: Fixed ADC4 enable() for WBA\n- feat: allow use of anyadcchannel for adc4\n- fix: fix incorrect logic for buffered usart transmission complete.\n- feat: add poll_for methods to exti\n- feat: implement stop for stm32wb.\n- change: rework hsem and add HIL test for some chips.\n- change: stm32/eth: ethernet no longer has a hard dependency on station management, and station management can be used independently ([#4871](https://github.com/embassy-rs/embassy/pull/4871))\n- feat: allow embassy_executor::main for low power\n- feat: Add waveform methods to ComplementaryPwm\n- fix: Avoid generating timer update events when updating the frequency ([#4890](https://github.com/embassy-rs/embassy/pull/4890))\n- chore: cleanup low-power add time\n- fix: Allow setting SAI peripheral `frame_length` to `256`\n- fix: flash erase on dual-bank STM32Gxxx\n- feat: Add support for STM32N657X0\n- feat: timer: Add 32-bit timer support to SimplePwm waveform_up method following waveform pattern ([#4717](https://github.com/embassy-rs/embassy/pull/4717))\n- feat: Add support for injected ADC measurements for g4 ([#4840](https://github.com/embassy-rs/embassy/pull/4840))\n- feat: Implement into_ring_buffered for g4 ([#4840](https://github.com/embassy-rs/embassy/pull/4840))\n- feat: Add support for 13-bit address and 16-bit data SDRAM chips\n- feat: stm32/hrtim add new_chx_with_config to provide pin configuration\n- fix flash erase on L4 & L5\n- fix: Fixed STM32H5 builds requiring time feature\n- feat: Derive Clone, Copy for QSPI Config\n- fix: stm32/i2c in master mode (blocking): subsequent transmissions failed after a NACK was received\n- feat: stm32/timer: add set_polarity functions for main and complementary outputs in complementary_pwm\n- Add I2S support for STM32F1, STM32C0, STM32F0, STM32F3, STM32F7, STM32G0, STM32WL, STM32H5, STM32H7RS\n- fix: STM32: Prevent dropped DacChannel from disabling Dac peripheral if another DacChannel is still in scope ([#4577](https://github.com/embassy-rs/embassy/pull/4577))\n- feat: Added support for more OctoSPI configurations (e.g. APS6408 RAM) ([#4581](https://github.com/embassy-rs/embassy/pull/4581))\n- fix: stm32/usart: fix bug with blocking flush in buffered uart ([#4648](https://github.com/embassy-rs/embassy/pull/4648))\n- fix: stm32/(ospi/hspi/xspi): Fix the alternate bytes register config sticking around for subsequent writes\n- feat: Configurable gpio speed for QSPI\n- feat: derive Clone, Copy and defmt::Format for all *SPI-related configs\n- fix: handle address and data-length errors in OSPI\n- feat: Allow OSPI/HSPI/XSPI DMA writes larger than 64kB using chunking\n- feat: More ADC enums for g0 PAC, API change for oversampling, allow separate sample times\n- feat: Add USB CRS sync support for STM32C071\n- fix: RTC register definition for STM32L4P5 and L4Q5 as they use v3 register map.\n- fix: Cut down the capabilities of the STM32L412 and L422 RTC as those are missing binary timer mode and underflow interrupt.\n- fix: Allow configuration of the internal pull up/down resistors on the pins for the Qei peripheral, as well as the Qei decoder mode.\n- feat: stm32/rcc/mco: Added support for IO driver strength when using Master Clock Out IO. This changes signature on Mco::new taking a McoConfig struct ([#4679](https://github.com/embassy-rs/embassy/pull/4679))\n- feat: derive Clone, Copy and defmt::Format for all SPI-related configs\n- feat: stm32/usart: add `eager_reads` option to control if buffered readers return as soon as possible or after more data is available ([#4668](https://github.com/embassy-rs/embassy/pull/4668))\n- feat: stm32/usart: add `de_assertion_time` and `de_deassertion_time` config options\n- change: stm32/uart: BufferedUartRx now returns all available bytes from the internal buffer\n- fix: Properly set the transfer size for OSPI/HSPI/XSPI transfers with word sizes other than 8 bits.\n- fix: stm32/adc: Calculate the ADC prescaler in a way that it allows for the max frequency to be reached\n- fix: Prevent a HardFault crash on STM32H5 devices by changing `uid()` to return `[u8; 12]` by value instead of a reference. (Fixes #2696)\n- change: timer: added output compare values\n- feat: timer: add ability to set master mode\n- fix: sdmmc: don't wait for DBCKEND flag on sdmmc_v2 devices as it never fires (Fixes #4723)\n- fix: usart: fix race condition in ringbuffered usart\n- feat: Add backup_sram::init() for H5 devices to access BKPSRAM\n- feat: stm32/i2c v1: Add I2C MultiMaster (Slave) support\n- feat: stm32/i2c v2: Add transaction() and blocking_transaction() methods with contract-compliant operation merging\n- feat: stm32/fdcan: add ability to control automatic recovery from bus off ([#4821](https://github.com/embassy-rs/embassy/pull/4821))\n- low-power: update rtc api to allow reconfig\n- adc: consolidate ringbuffer\n- feat: Added RTC low-power support for STM32WLEx ([#4716](https://github.com/embassy-rs/embassy/pull/4716))\n- feat: Added low-power support for STM32WL5x ([#5108](https://github.com/embassy-rs/embassy/pull/5108))\n- fix: Correct STM32WBA VREFBUFTRIM values\n- low_power: remove stop_with rtc and initialize in init if low-power feature enabled.\n- feat: stm32/dsi support zero parameter commands in `write_cmd` ([#4847](https://github.com/embassy-rs/embassy/pull/4847))\n- feat: stm32/spi: added support for slave mode ([#4388](https://github.com/embassy-rs/embassy/pull/4388))\n- chore: Updated stm32-metapac and stm32-data dependencies\n- adc: reogranize and cleanup somewhat. require sample_time to be passed on conversion\n- fix: stm32/i2c v2 slave: prevent misaligned reads, error false positives, and incorrect counts of bytes read/written\n- feat: add flash support for c0 family ([#4874](https://github.com/embassy-rs/embassy/pull/4874))\n- fix: fixing channel numbers on vbat and vddcore for adc on adc\n- adc: adding disable to vbat\n- feat: stm32/flash: add async support for h7 family\n- feat: exti brought in line with other drivers' interrupt rebinding system ([#4922](https://github.com/embassy-rs/embassy/pull/4922))\n- removal: ExtiInput no longer accepts AnyPin/AnyChannel; AnyChannel removed entirely\n- fix: build script ensures EXTI2_TSC is listed as the IRQ of EXTI2 even if the PAC doesn't\n- feat: stm32/lcd: added implementation\n- change: add error messages to can timing calculations ([#4961](https://github.com/embassy-rs/embassy/pull/4961))\n- feat: stm32/spi bidirectional mode\n- fix: stm32/i2c v2: add stop flag on stop received\n- stm32: Add blocking_listen for blocking I2C driver\n- fix: stm32l47*/stm32l48* adc analog pin setup\n- fix: keep stm32/sai: make NODIV independent of MCKDIV\n- fix: Source system clock from MSIS before (de)configuring PLLs on STM32U5\n- feat: adc: allow DMA reads to loop through enabled channels\n- chore: update to embedded-io 0.7\n\n## 0.4.0 - 2025-08-26\n\n- feat: stm32/sai: make NODIV independent of MCKDIV\n- fix: stm32/sai: fix WB MCKDIV\n- fix: stm32/i2c: pull-down was enabled instead of pull-none when no internal pull-up was needed.\n- feat: Improve blocking hash speed\n- fix: Fix vrefbuf building with log feature\n- fix: Fix performing a hash after performing a hmac\n- chore: Updated stm32-metapac and stm32-data dependencies\n- feat: stm32/adc/v3: allow DMA reads to loop through enable channels\n- fix: Fix XSPI not disabling alternate bytes when they were previously enabled\n- feat: stm32/adc/v3: added support for Continuous DMA configuration\n- fix: Fix stm32h7rs init when using external flash via XSPI\n- feat: Add Adc::new_with_clock() to configure analog clock\n- feat: Add GPDMA linked-list + ringbuffer support ([#3923](https://github.com/embassy-rs/embassy/pull/3923))\n- feat: Added support for STM32F1 peripheral pin remapping (AFIO) ([#4430](https://github.com/embassy-rs/embassy/pull/4430))\n\n## 0.3.0 - 2025-08-12\n\n- feat: Added VREFBUF voltage reference buffer driver ([#4524](https://github.com/embassy-rs/embassy/pull/4524))\n- feat: Added complementary PWM idle-state control methods ([#4522](https://github.com/embassy-rs/embassy/pull/4522))\n- feat: Added hardware oversampling support for ADC v3 ([#4279](https://github.com/embassy-rs/embassy/pull/4279))\n- feat: Added ADC4 support for STM32WBA devices\n- feat: Added USB OTG HS support for STM32WBA devices\n- feat: Added STM32C071 and STM32C051 RCC support\n- feat: Added PWM pin configuration options for different GPIO modes\n- feat: Added RTC low-power support for STM32WBA65 ([#4418](https://github.com/embassy-rs/embassy/pull/4418))\n- feat: Added eMMC support for SDMMC\n- feat: Added auto-calibration for MSI frequencies on U5 devices ([#4313](https://github.com/embassy-rs/embassy/pull/4313))\n- feat: Added DAC::new_unbuffered method ([#4183](https://github.com/embassy-rs/embassy/pull/4183))\n- feat: Added helper methods for low-power interrupt timer ([#4305](https://github.com/embassy-rs/embassy/pull/4305))\n- feat: Added ADC v1 analog watchdog implementation ([#4330](https://github.com/embassy-rs/embassy/pull/4330))\n- feat: Added OPAMP RCC initialization ([#4358](https://github.com/embassy-rs/embassy/pull/4358))\n- feat: Added const constructors for RCC Config structs ([#4231](https://github.com/embassy-rs/embassy/pull/4231))\n- feat: Added FDCAN/BXCAN RAII instance counters ([#4272](https://github.com/embassy-rs/embassy/pull/4272))\n- fix: Fixed I2C slave blocking read/write support ([#4454](https://github.com/embassy-rs/embassy/pull/4454))\n- fix: Fixed STM32WBA VDDIO2 configuration ([#4424](https://github.com/embassy-rs/embassy/pull/4424))\n- fix: Fixed timer break input 2 trait naming\n- fix: Fixed dead-time computation in complementary PWM\n- fix: Fixed get_max_duty off-by-one error for center-aligned mode ([#4302](https://github.com/embassy-rs/embassy/pull/4302))\n- fix: Fixed STM32C09x build issues\n- fix: Fixed STM32G0B0 build issues\n- fix: Fixed HSEM CPUID detection and added missing RCC initialization ([#4324](https://github.com/embassy-rs/embassy/pull/4324))\n- fix: Enable autoreload preload for complementary PWM ([#4303](https://github.com/embassy-rs/embassy/pull/4303))\n- fix: Fixed DMA packing/unpacking functionality\n- fix: Added missing fence on BDMA start operations\n- fix: Improve error handling for I2C v2 NACK conditions\n- fix: Renamed frequency parameters for consistency (freq -> frequency)\n- chore: Updated stm32-metapac and stm32-data dependencies\n- chore: Modify BufferedUart initialization to take pins before interrupts ([#3983](https://github.com/embassy-rs/embassy/pull/3983))\n- feat: Added a 'single-bank' and a 'dual-bank' feature so chips with configurable flash bank setups are be supported in embassy ([#4125](https://github.com/embassy-rs/embassy/pull/4125))\n- feat: Add automatic setting of remap bits when using alternate DMA channels on STM32F0 and STM32F3 devices ([#3653](https://github.com/embassy-rs/embassy/pull/3653))\n\n## 0.2.0 - 2025-01-10\n\nStarting 2025 strong with a release packed with new, exciting good stuff! 🚀\n\n### New chips\n\nThis release adds support for many newly-released STM32 chips.\n\n- STM32H7[RS] \"bootflash line\" ([#2898](https://github.com/embassy-rs/embassy/pull/2898))\n- STM32U0 ([#2809](https://github.com/embassy-rs/embassy/pull/2809) [#2813](https://github.com/embassy-rs/embassy/pull/2813))\n- STM32H5[23] ([#2892](https://github.com/embassy-rs/embassy/pull/2892))\n- STM32U5[FG] ([#2892](https://github.com/embassy-rs/embassy/pull/2892))\n- STM32WBA5[045] ([#2892](https://github.com/embassy-rs/embassy/pull/2892))\n\n### Simpler APIs with less generics\n\nMany HAL APIs have been simplified thanks to reducing the amount of generic parameters. This helps with creating arrays of pins or peripherals, and for calling the same code with different pins/peripherals without incurring in code size penalties d\n\nFor GPIO, the pins have been eliminated. `Output<'_, PA4>` is now `Output<'_>`.\n\nFor peripherals, both pins and DMA channels have been eliminated. Peripherals now have a \"mode\" generic param that specifies whether it's capable of async operation. For example, `I2c<'_, I2C2, NoDma, NoDma>` is now `I2c<'_, Blocking>` and `I2c<'_, I2C2, DMA2_CH1, DMA2_CH2>` is now `I2c<'_, Async>`.\n\n- Removed DMA channel generic params for UART ([#2821](https://github.com/embassy-rs/embassy/pull/2821)), I2C ([#2820](https://github.com/embassy-rs/embassy/pull/2820)), SPI ([#2819](https://github.com/embassy-rs/embassy/pull/2819)), QSPI ([#2982](https://github.com/embassy-rs/embassy/pull/2982)), OSPI ([#2941](https://github.com/embassy-rs/embassy/pull/2941)).\n- Removed peripheral generic params for GPIO ([#2471](https://github.com/embassy-rs/embassy/pull/2471)), UART ([#2836](https://github.com/embassy-rs/embassy/pull/2836)), I2C ([#2974](https://github.com/embassy-rs/embassy/pull/2974)), SPI ([#2835](https://github.com/embassy-rs/embassy/pull/2835))\n- Remove generics in CAN ([#3012](https://github.com/embassy-rs/embassy/pull/3012), [#3020](https://github.com/embassy-rs/embassy/pull/3020), [#3032](https://github.com/embassy-rs/embassy/pull/3032), [#3033](https://github.com/embassy-rs/embassy/pull/3033))\n\n### More complete and consistent RCC\n\nRCC support has been vastly expanded and improved.\n- The API is now consistent across all STM32 families. Previously in some families you'd configure the desired target frequencies for `sysclk` and the buses and `embassy-stm32` would try to calculate dividers and muxes to hit them as close as possible. This has proved to be intractable in the general case and hard to extend to more exotic RCC configurations. So, we have standardized on an API where the user specifies the settings for dividers and muxes directly. It's lower level but gices more control to the user, supports all edge case exotic configurations, and makes it easier to translate a configuration from the STM32CubeMX tool. ([Tracking issue](https://github.com/embassy-rs/embassy/issues/2515). [#2624](https://github.com/embassy-rs/embassy/pull/2624). F0, F1 [#2564](https://github.com/embassy-rs/embassy/pull/2564), F3 [#2560](https://github.com/embassy-rs/embassy/pull/2560), U5 [#2617](https://github.com/embassy-rs/embassy/pull/2617), [#3514](https://github.com/embassy-rs/embassy/pull/3514), [#3513](https://github.com/embassy-rs/embassy/pull/3513), G4 [#2579](https://github.com/embassy-rs/embassy/pull/2579), [#2618](https://github.com/embassy-rs/embassy/pull/2618), WBA [#2520](https://github.com/embassy-rs/embassy/pull/2520), G0, C0 ([#2656](https://github.com/embassy-rs/embassy/pull/2656)).\n- Added support for configuring all per-peripheral clock muxes (CCIPRx, DCKCFGRx registers) in `config.rcc.mux`. This was previously handled in an ad-hoc way in some drivers (e.g. USB) and not at all in others (causing e.g. wrong SPI frequency) ([#2521](https://github.com/embassy-rs/embassy/pull/2521), [#2583](https://github.com/embassy-rs/embassy/pull/2583), [#2634](https://github.com/embassy-rs/embassy/pull/2634), [#2626](https://github.com/embassy-rs/embassy/pull/2626), [#2815](https://github.com/embassy-rs/embassy/pull/2815), [#2517](https://github.com/embassy-rs/embassy/pull/2517)).\n- Switch to a safe configuration before configuring RCC. This helps avoid crashes when RCC has been already configured previously (for example by a bootloader). (F2, F4, F7 [#2829](https://github.com/embassy-rs/embassy/pull/2829), C0, F0, F1, F3, G0, G4, H5, H7[#3008](https://github.com/embassy-rs/embassy/pull/3008))\n- Some new nice features:\n    - Expose RCC enable and disable in public API. ([#2807](https://github.com/embassy-rs/embassy/pull/2807))\n    - Add `unchecked-overclocking` feature that disables all asserts, allowing running RCC out of spec. ([#3574](https://github.com/embassy-rs/embassy/pull/3574))\n- Many fixes:\n    - Workaround H5 errata that accidentally clears RAM on backup domain reset. ([#2616](https://github.com/embassy-rs/embassy/pull/2616))\n    - Reset RTC on L0 ([#2597](https://github.com/embassy-rs/embassy/pull/2597))\n    - Fix H7 to use correct unit in vco clock check ([#2537](https://github.com/embassy-rs/embassy/pull/2537))\n    - Fix incorrect D1CPRE max for STM32H7 RM0468 ([#2518](https://github.com/embassy-rs/embassy/pull/2518))\n    - WBA's high speed external clock has to run at 32 MHz ([#2511](https://github.com/embassy-rs/embassy/pull/2511))\n    - Take into account clock propagation delay to peripherals after enabling a clock. ([#2677](https://github.com/embassy-rs/embassy/pull/2677))\n    - Fix crash caused by using higher MSI range as sysclk on STM32WL ([#2786](https://github.com/embassy-rs/embassy/pull/2786))\n    - fix using HSI48 as SYSCLK on F0 devices with CRS ([#3652](https://github.com/embassy-rs/embassy/pull/3652))\n    - compute LSE and LSI frequency for STM32L and STM32U0 series ([#3554](https://github.com/embassy-rs/embassy/pull/3554))\n    - Add support for LSESYS, used to pass LSE clock to peripherals ([#3518](https://github.com/embassy-rs/embassy/pull/3518))\n    - H5: LSE low drive mode is not functional ([#2738](https://github.com/embassy-rs/embassy/pull/2738))\n\n### New peripheral drivers\n\n- Dual-core support. First core initializes RCC and writes a struct into shared memory that the second core uses, ensuring no conflicts. ([#3158](https://github.com/embassy-rs/embassy/pull/3158), [#3263](https://github.com/embassy-rs/embassy/pull/3263), [#3687](https://github.com/embassy-rs/embassy/pull/3687))\n- USB Type-C/USB Power Delivery Interface (UCPD) ([#2652](https://github.com/embassy-rs/embassy/pull/2652), [#2683](https://github.com/embassy-rs/embassy/pull/2683), [#2701](https://github.com/embassy-rs/embassy/pull/2701), [#2925](https://github.com/embassy-rs/embassy/pull/2925), [#3084](https://github.com/embassy-rs/embassy/pull/3084), [#3271](https://github.com/embassy-rs/embassy/pull/3271), [#3678](https://github.com/embassy-rs/embassy/pull/3678), [#3714](https://github.com/embassy-rs/embassy/pull/3714))\n- Touch sensing controller (TSC) ([#2853](https://github.com/embassy-rs/embassy/pull/2853), [#3111](https://github.com/embassy-rs/embassy/pull/3111), [#3163](https://github.com/embassy-rs/embassy/pull/3163), [#3274](https://github.com/embassy-rs/embassy/pull/3274))\n- Display Serial Interface (DSI) [#2903](https://github.com/embassy-rs/embassy/pull/2903), ([#3082](https://github.com/embassy-rs/embassy/pull/3082))\n- LCD/TFT Display Controller (LTDC) ([#3126](https://github.com/embassy-rs/embassy/pull/3126), [#3458](https://github.com/embassy-rs/embassy/pull/3458))\n- SPDIF receiver (SPDIFRX) ([#3280](https://github.com/embassy-rs/embassy/pull/3280))\n- CORDIC math accelerator ([#2697](https://github.com/embassy-rs/embassy/pull/2697))\n- Digital Temperature Sensor (DTS) ([#3717](https://github.com/embassy-rs/embassy/pull/3717))\n- HMAC accelerator ([#2565](https://github.com/embassy-rs/embassy/pull/2565))\n- Hash accelerator ([#2528](https://github.com/embassy-rs/embassy/pull/2528))\n- Crypto accelerator ([#2619](https://github.com/embassy-rs/embassy/pull/2619), [#2691](https://github.com/embassy-rs/embassy/pull/2691))\n- Semaphore (HSEM) ([#2777](https://github.com/embassy-rs/embassy/pull/2777), [#3161](https://github.com/embassy-rs/embassy/pull/3161))\n\n### Improvements to existing drivers\n\nGPIO:\n- Generate singletons only for pins that actually exist. ([#3738](https://github.com/embassy-rs/embassy/pull/3738))\n- Add `set_as_analog` to Flex ([#3017](https://github.com/embassy-rs/embassy/pull/3017))\n- Add `embedded-hal` v0.2 `InputPin` impls for `OutputOpenDrain`. ([#2716](https://github.com/embassy-rs/embassy/pull/2716))\n- Add a config option to make the VDDIO2 supply line valid ([#2737](https://github.com/embassy-rs/embassy/pull/2737))\n- Refactor AfType ([#3031](https://github.com/embassy-rs/embassy/pull/3031))\n- Gpiov1: Do not call set_speed for AFType::Input ([#2996](https://github.com/embassy-rs/embassy/pull/2996))\n\nUART:\n- Add embedded-io impls ([#2739](https://github.com/embassy-rs/embassy/pull/2739))\n- Add support for changing baud rate ([#3512](https://github.com/embassy-rs/embassy/pull/3512))\n- Add split_ref ([#3500](https://github.com/embassy-rs/embassy/pull/3500))\n- Add data bit selection ([#3595](https://github.com/embassy-rs/embassy/pull/3595))\n- Add RX Pull configuration option ([#3415](https://github.com/embassy-rs/embassy/pull/3415))\n- Add async flush ([#3379](https://github.com/embassy-rs/embassy/pull/3379))\n- Add support for sending breaks ([#3286](https://github.com/embassy-rs/embassy/pull/3286))\n- Disconnect pins on drop ([#3006](https://github.com/embassy-rs/embassy/pull/3006))\n- Half-duplex improvements\n    - Add half-duplex for all USART versions ([#2833](https://github.com/embassy-rs/embassy/pull/2833))\n    - configurable readback for half-duplex. ([#3679](https://github.com/embassy-rs/embassy/pull/3679))\n    - Convert uart half_duplex to use user configurable IO ([#3233](https://github.com/embassy-rs/embassy/pull/3233))\n    - Fix uart::flush with FIFO at Half-Duplex mode ([#2895](https://github.com/embassy-rs/embassy/pull/2895))\n    - Fix Half-Duplex sequential reads and writes ([#3089](https://github.com/embassy-rs/embassy/pull/3089))\n    - disable transmitter during during half-duplex flush ([#3299](https://github.com/embassy-rs/embassy/pull/3299))\n- Buffered UART improvements\n    - Add embedded-io ReadReady impls ([#3179](https://github.com/embassy-rs/embassy/pull/3179), [#3451](https://github.com/embassy-rs/embassy/pull/3451))\n    - Add constructors for RS485 ([#3441](https://github.com/embassy-rs/embassy/pull/3441))\n    - Fix RingBufferedUartRx hard-resetting DMA after initial error ([#3356](https://github.com/embassy-rs/embassy/pull/3356))\n    - Don't teardown during reconfigure ([#2989](https://github.com/embassy-rs/embassy/pull/2989))\n    - Wake receive task for each received byte ([#2722](https://github.com/embassy-rs/embassy/pull/2722))\n    - Fix dma and idle line detection in ringbuffereduartrx ([#3319](https://github.com/embassy-rs/embassy/pull/3319))\n\nSPI:\n- Add MISO pullup configuration option ([#2943](https://github.com/embassy-rs/embassy/pull/2943))\n- Add slew rate configuration options ([#3669](https://github.com/embassy-rs/embassy/pull/3669))\n- Fix blocking_write on nosck spi. ([#3035](https://github.com/embassy-rs/embassy/pull/3035))\n- Restrict txonly_nosck to SPIv1, it hangs in other versions. ([#3028](https://github.com/embassy-rs/embassy/pull/3028))\n- Fix non-u8 word sizes. ([#3363](https://github.com/embassy-rs/embassy/pull/3363))\n- Issue correct DMA word length when reading to prevent hang. ([#3362](https://github.com/embassy-rs/embassy/pull/3362))\n- Add proper rxonly support for spi_v3 and force tx dma stream requirements. ([#3007](https://github.com/embassy-rs/embassy/pull/3007))\n\nI2C:\n- Implement asynchronous transactions ([#2742](https://github.com/embassy-rs/embassy/pull/2742))\n- Implement blocking transactions ([#2713](https://github.com/embassy-rs/embassy/pull/2713))\n- Disconnect pins on drop ([#3006](https://github.com/embassy-rs/embassy/pull/3006))\n- Ensure bus is free before master-write operation ([#3104](https://github.com/embassy-rs/embassy/pull/3104))\n- Add workaround for STM32 i2cv1 errata ([#2887](https://github.com/embassy-rs/embassy/pull/2887))\n- Fix disabling pullup accidentally enabling pulldown ([#3410](https://github.com/embassy-rs/embassy/pull/3410))\n\nFlash:\n- Add L5 support ([#3423](https://github.com/embassy-rs/embassy/pull/3423))\n- Add H5 support ([#3305](https://github.com/embassy-rs/embassy/pull/3305))\n- add F2 support ([#3303](https://github.com/embassy-rs/embassy/pull/3303))\n- Add U5 support ([#2591](https://github.com/embassy-rs/embassy/pull/2591), [#2792](https://github.com/embassy-rs/embassy/pull/2792))\n- Add H50x support ([#2600](https://github.com/embassy-rs/embassy/pull/2600), [#2808](https://github.com/embassy-rs/embassy/pull/2808))\n- Fix flash erase on F3 ([#3744](https://github.com/embassy-rs/embassy/pull/3744))\n- Support G0 second flash bank ([#3711](https://github.com/embassy-rs/embassy/pull/3711))\n- F1, F3: wait for BSY flag to clear before flashing ([#3217](https://github.com/embassy-rs/embassy/pull/3217))\n- H7: enhance resilience to program sequence errors (pgserr) ([#2539](https://github.com/embassy-rs/embassy/pull/2539))\n\nADC:\n- Add `AnyAdcChannel` type. You can obtain it from a pin with `.degrade_adc()`. Useful for making arrays of ADC pins. ([#2985](https://github.com/embassy-rs/embassy/pull/2985))\n- Add L0 support ([#2544](https://github.com/embassy-rs/embassy/pull/2544))\n- Add U5 support ([#3688](https://github.com/embassy-rs/embassy/pull/3688))\n- Add H5 support ([#2613](https://github.com/embassy-rs/embassy/pull/2613), [#3557](https://github.com/embassy-rs/embassy/pull/3557))\n- Add G4 async support ([#3566](https://github.com/embassy-rs/embassy/pull/3566))\n- Add G4 support for calibrating differential inputs ([#3735](https://github.com/embassy-rs/embassy/pull/3735))\n- Add oversampling and differential support for G4 ([#3169](https://github.com/embassy-rs/embassy/pull/3169))\n- Add DMA support for ADC v2 ([#3116](https://github.com/embassy-rs/embassy/pull/3116))\n- Add DMA support for ADC v3 and v4 ([#3128](https://github.com/embassy-rs/embassy/pull/3128))\n- Unify naming `blocking_read` for blocking, `read` for async. ([#3148](https://github.com/embassy-rs/embassy/pull/3148))\n- Fix channel count for the STM32G4 ADCs. ([#2828](https://github.com/embassy-rs/embassy/pull/2828))\n- Fix blocking_delay_us() overflowing when sys freq is high ([#2825](https://github.com/embassy-rs/embassy/pull/2825))\n- Remove need for taking a `Delay` impl. ([#2797](https://github.com/embassy-rs/embassy/pull/2797))\n- H5: set OR.OP0 to 1 when ADCx_INP0 is selected, per RM ([#2776](https://github.com/embassy-rs/embassy/pull/2776))\n- Add oversampling support ([#3124](https://github.com/embassy-rs/embassy/pull/3124))\n- Adc averaging support for ADC v4. ([#3110](https://github.com/embassy-rs/embassy/pull/3110))\n- F2 ADC fixes ([#2513](https://github.com/embassy-rs/embassy/pull/2513))\n\nDAC:\n- Fix new_internal not setting mode as documented ([#2886](https://github.com/embassy-rs/embassy/pull/2886))\n\nOPAMP:\n- Add missing opamp external outputs for STM32G4 ([#3636](https://github.com/embassy-rs/embassy/pull/3636))\n- Add extra lifetime to opamp-using structs ([#3207](https://github.com/embassy-rs/embassy/pull/3207))\n- Make OpAmp usable in follower configuration for internal DAC channel ([#3021](https://github.com/embassy-rs/embassy/pull/3021))\n\nCAN:\n- Add FDCAN support. ([#2475](https://github.com/embassy-rs/embassy/pull/2475), [#2571](https://github.com/embassy-rs/embassy/pull/2571), [#2623](https://github.com/embassy-rs/embassy/pull/2623), [#2631](https://github.com/embassy-rs/embassy/pull/2631), [#2635](https://github.com/embassy-rs/embassy/pull/2635), [#2637](https://github.com/embassy-rs/embassy/pull/2637), [#2645](https://github.com/embassy-rs/embassy/pull/2645), [#2647](https://github.com/embassy-rs/embassy/pull/2647), [#2658](https://github.com/embassy-rs/embassy/pull/2658), [#2703](https://github.com/embassy-rs/embassy/pull/2703), [#3364](https://github.com/embassy-rs/embassy/pull/3364))\n- Simplify BXCAN API, make BXCAN and FDCAN APIs consistent. ([#2760](https://github.com/embassy-rs/embassy/pull/2760), [#2693](https://github.com/embassy-rs/embassy/pull/2693), [#2744](https://github.com/embassy-rs/embassy/pull/2744))\n- Add buffered mode support ([#2588](https://github.com/embassy-rs/embassy/pull/2588))\n- Add support for modifying the receiver filters from `BufferedCan`, `CanRx`, and `BufferedCanRx` ([#3733](https://github.com/embassy-rs/embassy/pull/3733))\n- Add support for optional FIFO scheduling for outgoing frames ([#2988](https://github.com/embassy-rs/embassy/pull/2988))\n- fdcan: Properties for common runtime get/set operations ([#2840](https://github.com/embassy-rs/embassy/pull/2840))\n- fdcan: implement bus-off recovery ([#2832](https://github.com/embassy-rs/embassy/pull/2832))\n- Add BXCAN sleep/wakeup functionality ([#2854](https://github.com/embassy-rs/embassy/pull/2854))\n- Fix BXCAN hangs ([#3468](https://github.com/embassy-rs/embassy/pull/3468))\n- add RTR flag if it is remote frame ([#3421](https://github.com/embassy-rs/embassy/pull/3421))\n- Fix log storm when no CAN is connected ([#3284](https://github.com/embassy-rs/embassy/pull/3284))\n- Fix error handling ([#2850](https://github.com/embassy-rs/embassy/pull/2850))\n- Give CAN a kick when writing into TX buffer via sender. ([#2646](https://github.com/embassy-rs/embassy/pull/2646))\n- Preseve the RTR flag in messages. ([#2745](https://github.com/embassy-rs/embassy/pull/2745))\n\nFMC:\n- Add 13bit address sdram constructors ([#3189](https://github.com/embassy-rs/embassy/pull/3189))\n\nxSPI:\n- Add OCTOSPI support ([#2672](https://github.com/embassy-rs/embassy/pull/2672))\n- Add OCTOSPIM support ([#3102](https://github.com/embassy-rs/embassy/pull/3102))\n- Add HEXADECASPI support ([#3667](https://github.com/embassy-rs/embassy/pull/3667))\n- Add memory mapping support for QSPI ([#3725](https://github.com/embassy-rs/embassy/pull/3725))\n- Add memory mapping support for OCTOSPI ([#3456](https://github.com/embassy-rs/embassy/pull/3456))\n- Add async support for QSPI ([#3475](https://github.com/embassy-rs/embassy/pull/3475))\n- Fix QSPI synchronous read operation hangs when FIFO is not full ([#3724](https://github.com/embassy-rs/embassy/pull/3724))\n- Stick to `blocking_*` naming convention for QSPI, OSPI ([#3661](https://github.com/embassy-rs/embassy/pull/3661))\n\nSDMMC:\n- Add `block-device-driver` impl for use with `embedded-fatfs` ([#2607](https://github.com/embassy-rs/embassy/pull/2607))\n- Allow cmd block to be passed in for sdmmc dma transfers ([#3188](https://github.com/embassy-rs/embassy/pull/3188))\n\nETH:\n- Fix reception of multicast packets ([#3488](https://github.com/embassy-rs/embassy/pull/3488), [#3707](https://github.com/embassy-rs/embassy/pull/3707))\n- Add support for executing custom SMI commands ([#3355](https://github.com/embassy-rs/embassy/pull/3355))\n- Add support for MII interface ([#2465](https://github.com/embassy-rs/embassy/pull/2465))\n\nUSB:\n- Assert correct clock on init. ([#2711](https://github.com/embassy-rs/embassy/pull/2711))\n- Set PWR_CR2 USV on STM32L4 ([#2605](https://github.com/embassy-rs/embassy/pull/2605))\n- USBD driver improvements:\n    - Add ISO endpoint support ([#3314](https://github.com/embassy-rs/embassy/pull/3314))\n    - Add support for L1. ([#2452](https://github.com/embassy-rs/embassy/pull/2452))\n    - set USB initialization delay to 1µs ([#3700](https://github.com/embassy-rs/embassy/pull/3700))\n- OTG driver improvements:\n    - Add ISO endpoint support ([#3314](https://github.com/embassy-rs/embassy/pull/3314))\n    - Add support for U595, U5A5 ([#3613](https://github.com/embassy-rs/embassy/pull/3613))\n    - Add support for STM32H7R/S ([#3337](https://github.com/embassy-rs/embassy/pull/3337))\n    - Add support for full-speed ULPI mode ([#3281](https://github.com/embassy-rs/embassy/pull/3281))\n    - Make max EP count configurable ([#2881](https://github.com/embassy-rs/embassy/pull/2881))\n    - fix corruption in CONTROL OUT transfers in stm32f4. ([#3565](https://github.com/embassy-rs/embassy/pull/3565))\n    - Extract Synopsys USB OTG driver to a separate crate ([#2871](https://github.com/embassy-rs/embassy/pull/2871))\n    - Add critical sections to avoid USB OTG corruption Errata ([#2823](https://github.com/embassy-rs/embassy/pull/2823))\n    - Fix support for OTG_HS in FS mode. ([#2805](https://github.com/embassy-rs/embassy/pull/2805))\n\nI2S:\n- Add SPIv3 support. ([#2992](https://github.com/embassy-rs/embassy/pull/2992))\n- Add full-duplex support. ([#2992](https://github.com/embassy-rs/embassy/pull/2992))\n- Add I2S ringbuffered DMA support ([#3023](https://github.com/embassy-rs/embassy/pull/3023))\n- Fix STM32F4 I2S clock calculations ([#3716](https://github.com/embassy-rs/embassy/pull/3716))\n\nSAI:\n- Add a function that waits for any SAI/ringbuffer write error ([#3545](https://github.com/embassy-rs/embassy/pull/3545))\n- Disallow start without an initial write ([#3541](https://github.com/embassy-rs/embassy/pull/3541))\n- Flush FIFO on init and disable ([#3538](https://github.com/embassy-rs/embassy/pull/3538))\n- Fix MCKDIV for SAI v3/v4 ([#2710](https://github.com/embassy-rs/embassy/pull/2710))\n- Pull down clock and data lines in receive mode ([#3326](https://github.com/embassy-rs/embassy/pull/3326))\n- Add function to check if SAI is muted ([#3282](https://github.com/embassy-rs/embassy/pull/3282))\n\nLow-power support:\n- Update `embassy-executor` to v0.7.\n- Add support for U0 ([#3556](https://github.com/embassy-rs/embassy/pull/3556))\n- Add support for U5 ([#3496](https://github.com/embassy-rs/embassy/pull/3496))\n- Add support for H5 ([#2877](https://github.com/embassy-rs/embassy/pull/2877))\n- Add support for L4 ([#3213](https://github.com/embassy-rs/embassy/pull/3213))\n- Fix low-power EXTI IRQ handler dropped edges ([#3404](https://github.com/embassy-rs/embassy/pull/3404))\n- Fix alarms not triggering in some cases ([#3592](https://github.com/embassy-rs/embassy/pull/3592))\n\nTimer:\n- Add Input Capture high-level driver ([#2912](https://github.com/embassy-rs/embassy/pull/2912))\n- Add PWM Input high-level driver ([#3014](https://github.com/embassy-rs/embassy/pull/3014))\n- Add support for splitting `SimplePwm` into channels ([#3317](https://github.com/embassy-rs/embassy/pull/3317))\n- Fix `SimplePwm` not enabling output pin in some stm32 families ([#2670](https://github.com/embassy-rs/embassy/pull/2670))\n- Add LPTIM low-level driver. ([#3310](https://github.com/embassy-rs/embassy/pull/3310))\n- Low-level TIM driver improvements:\n    - Simplify traits, convert from trait methods to struct. ([#2728](https://github.com/embassy-rs/embassy/pull/2728))\n    - Add `low_level::Timer::get_clock_frequency()` ([#2908](https://github.com/embassy-rs/embassy/pull/2908))\n    - Fix 32bit timer off by one ARR error ([#2876](https://github.com/embassy-rs/embassy/pull/2876))\n    - Avoid max_compare_value >= u16::MAX ([#3549](https://github.com/embassy-rs/embassy/pull/3549))\n\nDMA:\n- Add `AnyChannel` type. Similar to `AnyPin`, it allows representing any DMA channel at runtime without needing generics. ([#2606](https://github.com/embassy-rs/embassy/pull/2606))\n, Add support for BDMA on H7 ([#2606](https://github.com/embassy-rs/embassy/pull/2606))\n- Add async `stop()` function to BDMA, DMA ([#2757](https://github.com/embassy-rs/embassy/pull/2757))\n- Add configuration option for DMA Request Priority ([#2680](https://github.com/embassy-rs/embassy/pull/2680))\n- Rewrite DMA ringbuffers ([#3336](https://github.com/embassy-rs/embassy/pull/3336))\n- Enable half transfer IRQ when constructing a ReadableDmaRingBuffer ([#3093](https://github.com/embassy-rs/embassy/pull/3093))\n- Right-align `write_immediate()` in ring buffers ([#3588](https://github.com/embassy-rs/embassy/pull/3588))\n\n`embassy-time` driver:\n- Update to `embassy-time` v0.4, `embassy-time-driver` v0.2. ([#3593](https://github.com/embassy-rs/embassy/pull/3593))\n- Change preference order of `time-driver-any` to pick less-featureful timers first. ([#2570](https://github.com/embassy-rs/embassy/pull/2570))\n- Allow using more TIMx timers for the time driver  of TIM1 ([#2570](https://github.com/embassy-rs/embassy/pull/2570), [#2614](https://github.com/embassy-rs/embassy/pull/2614))\n- Correctly gate `time` feature of embassy-embedded-hal in embassy-stm32 ([#3359](https://github.com/embassy-rs/embassy/pull/3359))\n- adds timer-driver for tim21 and tim22 (on L0) ([#2450](https://github.com/embassy-rs/embassy/pull/2450))\n\nWDG:\n- Allow higher PSC value for iwdg_v3 ... ([#2628](https://github.com/embassy-rs/embassy/pull/2628))\n\nMisc:\n- Allow `bind_interrupts!` to accept conditional compilation attrs ([#3444](https://github.com/embassy-rs/embassy/pull/3444))\n\n## 0.1.0 - 2024-01-12\n\nFirst release.\n"
  },
  {
    "path": "embassy-stm32/Cargo.toml",
    "content": "[package]\nname = \"embassy-stm32\"\nversion = \"0.6.0\"\nedition = \"2024\"\nlicense = \"MIT OR Apache-2.0\"\ndescription = \"Embassy Hardware Abstraction Layer (HAL) for ST STM32 series microcontrollers\"\nkeywords = [\"embedded\", \"async\", \"stm32\", \"hal\", \"embedded-hal\"]\ncategories = [\"embedded\", \"hardware-support\", \"no-std\", \"asynchronous\"]\nrepository = \"https://github.com/embassy-rs/embassy\"\ndocumentation = \"https://docs.embassy.dev/embassy-stm32\"\n\n[package.metadata.embassy]\nbuild = [\n    {target = \"thumbv8m.main-none-eabihf\", features = [\"defmt\", \"dual-bank\", \"exti\", \"stm32l552ze\", \"time\", \"time-driver-any\"]},\n    {target = \"thumbv8m.main-none-eabihf\", features = [\"defmt\", \"dual-bank\", \"stm32l552ze\", \"time\", \"time-driver-any\"]},\n    {target = \"thumbv8m.main-none-eabihf\", features = [\"defmt\", \"dual-bank\", \"exti\", \"stm32l552ze\", \"time\"]},\n    {target = \"thumbv8m.main-none-eabihf\", features = [\"defmt\", \"dual-bank\", \"stm32l552ze\", \"time\"]},\n    {target = \"thumbv8m.main-none-eabihf\", features = [\"defmt\", \"dual-bank\", \"exti\", \"stm32l552ze\"]},\n    {target = \"thumbv8m.main-none-eabihf\", features = [\"defmt\", \"dual-bank\", \"stm32l552ze\"]},\n    {target = \"thumbv8m.main-none-eabihf\", features = [\"defmt\", \"single-bank\", \"stm32l552ze\"]},\n    {target = \"thumbv6m-none-eabi\", features = [\"defmt\", \"exti\", \"stm32c071rb\", \"time\", \"time-driver-any\"]},\n    {target = \"thumbv6m-none-eabi\", features = [\"defmt\", \"exti\", \"stm32c051f6\", \"time\", \"time-driver-any\"]},\n    {target = \"thumbv6m-none-eabi\", features = [\"defmt\", \"exti\", \"stm32c091gb\", \"time\", \"time-driver-any\"]},\n    {target = \"thumbv6m-none-eabi\", features = [\"defmt\", \"exti\", \"stm32c092rc\", \"time\", \"time-driver-any\"]},\n    {target = \"thumbv6m-none-eabi\", features = [\"defmt\", \"exti\", \"stm32f038f6\", \"time\", \"time-driver-any\"]},\n    {target = \"thumbv6m-none-eabi\", features = [\"defmt\", \"exti\", \"stm32f030c6\", \"time\", \"time-driver-any\"]},\n    {target = \"thumbv6m-none-eabi\", features = [\"defmt\", \"exti\", \"stm32f058t8\", \"time\", \"time-driver-any\"]},\n    {target = \"thumbv6m-none-eabi\", features = [\"defmt\", \"exti\", \"stm32f030r8\", \"time\", \"time-driver-any\"]},\n    {target = \"thumbv6m-none-eabi\", features = [\"defmt\", \"exti\", \"stm32f031k6\", \"time\", \"time-driver-any\"]},\n    {target = \"thumbv6m-none-eabi\", features = [\"defmt\", \"exti\", \"stm32f030rc\", \"time\", \"time-driver-any\"]},\n    {target = \"thumbv6m-none-eabi\", features = [\"defmt\", \"exti\", \"stm32f070f6\", \"time\", \"time-driver-any\"]},\n    {target = \"thumbv6m-none-eabi\", features = [\"defmt\", \"exti\", \"stm32f078vb\", \"time\", \"time-driver-any\"]},\n    {target = \"thumbv6m-none-eabi\", features = [\"defmt\", \"exti\", \"stm32f042g4\", \"time\", \"time-driver-any\"]},\n    {target = \"thumbv6m-none-eabi\", features = [\"defmt\", \"exti\", \"stm32f072c8\", \"time\", \"time-driver-any\"]},\n    {target = \"thumbv7em-none-eabi\", features = [\"defmt\", \"exti\", \"stm32f401ve\", \"time-driver-any\"]},\n    {target = \"thumbv7em-none-eabi\", features = [\"defmt\", \"exti\", \"stm32f405zg\", \"time-driver-any\"]},\n    {target = \"thumbv7em-none-eabi\", features = [\"defmt\", \"exti\", \"stm32f407zg\", \"time-driver-any\"]},\n    {target = \"thumbv7em-none-eabi\", features = [\"defmt\", \"exti\", \"stm32f401ve\", \"time\", \"time-driver-any\"]},\n    {target = \"thumbv7em-none-eabi\", features = [\"defmt\", \"exti\", \"stm32f405zg\", \"time\", \"time-driver-any\"]},\n    {target = \"thumbv7em-none-eabi\", features = [\"defmt\", \"exti\", \"stm32f407zg\", \"time\", \"time-driver-any\"]},\n    {target = \"thumbv7em-none-eabi\", features = [\"defmt\", \"exti\", \"stm32f410tb\", \"time\", \"time-driver-any\"]},\n    {target = \"thumbv7em-none-eabi\", features = [\"defmt\", \"exti\", \"stm32f411ce\", \"time\", \"time-driver-any\"]},\n    {target = \"thumbv7em-none-eabi\", features = [\"defmt\", \"exti\", \"stm32f412zg\", \"time\", \"time-driver-any\"]},\n    {target = \"thumbv7em-none-eabi\", features = [\"defmt\", \"exti\", \"stm32f413vh\", \"time\", \"time-driver-any\"]},\n    {target = \"thumbv7em-none-eabi\", features = [\"defmt\", \"exti\", \"stm32f415zg\", \"time\", \"time-driver-any\"]},\n    {target = \"thumbv7em-none-eabi\", features = [\"defmt\", \"exti\", \"stm32f417zg\", \"time\", \"time-driver-any\"]},\n    {target = \"thumbv7em-none-eabi\", features = [\"defmt\", \"exti\", \"stm32f423zh\", \"time\", \"time-driver-any\"]},\n    {target = \"thumbv7em-none-eabi\", features = [\"defmt\", \"exti\", \"stm32f427zi\", \"time\", \"time-driver-any\"]},\n    {target = \"thumbv7em-none-eabi\", features = [\"exti\", \"log\", \"stm32f429zi\", \"time\", \"time-driver-any\"]},\n    {target = \"thumbv7em-none-eabi\", features = [\"exti\", \"log\", \"stm32f437zi\", \"time\", \"time-driver-any\"]},\n    {target = \"thumbv7em-none-eabi\", features = [\"defmt\", \"exti\", \"stm32f439zi\", \"time\", \"time-driver-any\"]},\n    {target = \"thumbv7em-none-eabi\", features = [\"defmt\", \"exti\", \"stm32f446ze\", \"time\", \"time-driver-any\"]},\n    {target = \"thumbv7em-none-eabi\", features = [\"defmt\", \"exti\", \"stm32f469zi\", \"time\", \"time-driver-any\"]},\n    {target = \"thumbv7em-none-eabi\", features = [\"defmt\", \"exti\", \"stm32f479zi\", \"time\", \"time-driver-any\"]},\n    {target = \"thumbv7em-none-eabi\", features = [\"defmt\", \"exti\", \"stm32f730i8\", \"time\", \"time-driver-any\"]},\n    {target = \"thumbv7em-none-eabi\", features = [\"defmt\", \"exti\", \"stm32h753zi\", \"time\", \"time-driver-any\"]},\n    {target = \"thumbv7em-none-eabi\", features = [\"defmt\", \"exti\", \"stm32h735zg\", \"time\", \"time-driver-any\"]},\n    {target = \"thumbv7em-none-eabi\", features = [\"defmt\", \"exti\", \"split-pc2\", \"split-pc3\", \"stm32h755zi-cm7\", \"time\", \"time-driver-any\"]},\n    {target = \"thumbv7em-none-eabi\", features = [\"defmt\", \"exti\", \"stm32h725re\", \"time\", \"time-driver-any\"]},\n    {target = \"thumbv7em-none-eabi\", features = [\"defmt\", \"exti\", \"stm32h7b3ai\", \"time\", \"time-driver-any\"]},\n    {target = \"thumbv7em-none-eabi\", features = [\"defmt\", \"exti\", \"stm32h7b3ai\", \"time\", \"time-driver-tim1\"]},\n    {target = \"thumbv7em-none-eabi\", features = [\"defmt\", \"exti\", \"stm32h7r3z8\", \"time\", \"time-driver-tim1\"]},\n    {target = \"thumbv7em-none-eabi\", features = [\"defmt\", \"exti\", \"stm32h7r7a8\", \"time\", \"time-driver-tim1\"]},\n    {target = \"thumbv7em-none-eabi\", features = [\"defmt\", \"exti\", \"stm32h7s3a8\", \"time\", \"time-driver-tim1\"]},\n    {target = \"thumbv7em-none-eabi\", features = [\"defmt\", \"exti\", \"stm32h7s7z8\", \"time\", \"time-driver-tim1\"]},\n    {target = \"thumbv7em-none-eabi\", features = [\"defmt\", \"exti\", \"stm32l431cb\", \"time\", \"time-driver-any\"]},\n    {target = \"thumbv7em-none-eabi\", features = [\"defmt\", \"exti\", \"stm32l476vg\", \"time\", \"time-driver-any\"]},\n    {target = \"thumbv7em-none-eabi\", features = [\"defmt\", \"exti\", \"stm32l422cb\", \"time\", \"time-driver-any\"]},\n    {target = \"thumbv7em-none-eabi\", features = [\"defmt\", \"exti\", \"stm32l4p5ae\", \"time\", \"time-driver-any\", \"single-bank\"]},\n    {target = \"thumbv7em-none-eabi\", features = [\"defmt\", \"exti\", \"stm32l4q5zg\", \"time\", \"time-driver-any\", \"single-bank\"]},\n    {target = \"thumbv7em-none-eabi\", features = [\"defmt\", \"exti\", \"stm32l4r9vi\", \"time\", \"time-driver-any\", \"dual-bank\"]},\n    {target = \"thumbv7em-none-eabi\", features = [\"defmt\", \"exti\", \"stm32l4s7vi\", \"time\", \"time-driver-any\", \"dual-bank\"]},\n    {target = \"thumbv7em-none-eabi\", features = [\"defmt\", \"exti\", \"stm32wb15cc\", \"time\", \"time-driver-any\"]},\n    {target = \"thumbv6m-none-eabi\", features = [\"defmt\", \"exti\", \"stm32l072cz\", \"time\", \"time-driver-any\"]},\n    {target = \"thumbv6m-none-eabi\", features = [\"defmt\", \"exti\", \"stm32l041f6\", \"time\", \"time-driver-any\"]},\n    {target = \"thumbv6m-none-eabi\", features = [\"defmt\", \"exti\", \"stm32l051k8\", \"time\", \"time-driver-any\"]},\n    {target = \"thumbv6m-none-eabi\", features = [\"defmt\", \"exti\", \"low-power\", \"stm32l073cz\", \"time\", \"time-driver-any\"]},\n    {target = \"thumbv7m-none-eabi\", features = [\"defmt\", \"exti\", \"stm32l151cb-a\", \"time\", \"time-driver-any\"]},\n    {target = \"thumbv7em-none-eabi\", features = [\"defmt\", \"exti\", \"stm32f303c8\", \"time\", \"time-driver-any\"]},\n    {target = \"thumbv7em-none-eabi\", features = [\"defmt\", \"exti\", \"stm32f398ve\", \"time\", \"time-driver-any\"]},\n    {target = \"thumbv7em-none-eabi\", features = [\"defmt\", \"exti\", \"stm32f378cc\", \"time\", \"time-driver-any\"]},\n    {target = \"thumbv6m-none-eabi\", features = [\"defmt\", \"exti\", \"stm32g0b0ce\", \"time\", \"time-driver-any\"]},\n    {target = \"thumbv6m-none-eabi\", features = [\"defmt\", \"exti\", \"stm32g0c1ve\", \"time\", \"time-driver-any\"]},\n    {target = \"thumbv7m-none-eabi\", features = [\"defmt\", \"exti\", \"stm32f217zg\", \"time\", \"time-driver-any\"]},\n    {target = \"thumbv8m.main-none-eabihf\", features = [\"defmt\", \"dual-bank\", \"exti\", \"low-power\", \"stm32l552ze\", \"time\", \"time-driver-any\"]},\n    {target = \"thumbv6m-none-eabi\", features = [\"defmt\", \"exti\", \"stm32wl54jc-cm0p\", \"time\", \"time-driver-any\"]},\n    {target = \"thumbv7em-none-eabi\", features = [\"defmt\", \"exti\", \"stm32wle5jb\", \"time\", \"time-driver-any\"]},\n    {target = \"thumbv7em-none-eabi\", features = [\"defmt\", \"exti\", \"stm32g431kb\", \"time\", \"time-driver-any\"]},\n    {target = \"thumbv7em-none-eabi\", features = [\"defmt\", \"dual-bank\", \"exti\", \"stm32g474pe\", \"time\", \"time-driver-any\"]},\n    {target = \"thumbv7m-none-eabi\", features = [\"defmt\", \"exti\", \"stm32f107vc\", \"time\", \"time-driver-any\"]},\n    {target = \"thumbv7m-none-eabi\", features = [\"defmt\", \"exti\", \"stm32f103re\", \"time\", \"time-driver-any\"]},\n    {target = \"thumbv7m-none-eabi\", features = [\"defmt\", \"exti\", \"stm32f100c4\", \"time\", \"time-driver-any\"]},\n    {target = \"thumbv8m.main-none-eabihf\", features = [\"defmt\", \"exti\", \"stm32h503rb\", \"time\", \"time-driver-any\"]},\n    {target = \"thumbv8m.main-none-eabihf\", features = [\"defmt\", \"exti\", \"stm32h523cc\", \"time\", \"time-driver-any\"]},\n    {target = \"thumbv8m.main-none-eabihf\", features = [\"defmt\", \"exti\", \"stm32h562ag\", \"time\", \"time-driver-any\"]},\n    {target = \"thumbv8m.main-none-eabihf\", features = [\"defmt\", \"exti\", \"stm32wba50ke\", \"time\", \"time-driver-any\"]},\n    {target = \"thumbv8m.main-none-eabihf\", features = [\"defmt\", \"exti\", \"stm32wba55ug\", \"time\", \"time-driver-any\"]},\n    {target = \"thumbv8m.main-none-eabihf\", features = [\"defmt\", \"exti\", \"stm32wba62cg\", \"time\", \"time-driver-any\"]},\n    {target = \"thumbv8m.main-none-eabihf\", features = [\"defmt\", \"exti\", \"low-power\", \"stm32wba65ri\", \"time\", \"time-driver-any\"]},\n    {target = \"thumbv8m.main-none-eabihf\", features = [\"defmt\", \"exti\", \"stm32u385rg\", \"time\", \"time-driver-any\"]},\n    {target = \"thumbv8m.main-none-eabihf\", features = [\"defmt\", \"exti\", \"stm32u5f9zj\", \"time\", \"time-driver-any\"]},\n    {target = \"thumbv8m.main-none-eabihf\", features = [\"defmt\", \"exti\", \"stm32u5g9nj\", \"time\", \"time-driver-any\"]},\n    {target = \"thumbv8m.main-none-eabihf\", features = [\"defmt\", \"exti\", \"stm32n657x0\", \"time\", \"time-driver-any\"]},\n    {target = \"thumbv7em-none-eabi\", features = [\"defmt\", \"exti\", \"stm32wb35ce\", \"time\", \"time-driver-any\"]},\n    {target = \"thumbv7em-none-eabi\", features = [\"defmt\", \"exti\", \"low-power\", \"stm32wb55rg\", \"time\", \"time-driver-any\"]},\n    {target = \"thumbv7em-none-eabi\", features = [\"defmt\", \"exti\", \"low-power\", \"stm32wb55rg\", \"time\", \"time-driver-any\", \"executor-thread\"]},\n    {target = \"thumbv7em-none-eabi\", features = [\"defmt\", \"exti\", \"low-power\", \"stm32wb55rg\", \"time\", \"time-driver-any\", \"executor-thread\", \"executor-interrupt\"]},\n    {target = \"thumbv7em-none-eabi\", features = [\"defmt\", \"exti\", \"low-power\", \"stm32wb55rg\", \"time\", \"time-driver-any\", \"executor-interrupt\"]},\n    {target = \"thumbv6m-none-eabi\", features = [\"defmt\", \"exti\", \"stm32u031r8\", \"time\", \"time-driver-any\"]},\n    {target = \"thumbv6m-none-eabi\", features = [\"defmt\", \"exti\", \"stm32u073mb\", \"time\", \"time-driver-any\"]},\n    {target = \"thumbv6m-none-eabi\", features = [\"defmt\", \"exti\", \"stm32u083rc\", \"time\", \"time-driver-any\"]},\n]\n\n[package.metadata.embassy_docs]\nsrc_base = \"https://github.com/embassy-rs/embassy/blob/embassy-stm32-v$VERSION/embassy-stm32/src/\"\nsrc_base_git = \"https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-stm32/src/\"\n\nfeatures = [\"defmt\", \"unstable-pac\", \"exti\", \"time-driver-any\", \"time\"]\nflavors = [\n    { regex_feature = \"stm32f0.*\", target = \"thumbv6m-none-eabi\" },\n    { regex_feature = \"stm32f1.*\", target = \"thumbv7m-none-eabi\" },\n    { regex_feature = \"stm32f2.*\", target = \"thumbv7m-none-eabi\" },\n    { regex_feature = \"stm32f3.*\", target = \"thumbv7em-none-eabi\" },\n    { regex_feature = \"stm32f4[2367]..[ig]\", target = \"thumbv7em-none-eabi\", features = [\"low-power\", \"dual-bank\"] },\n    { regex_feature = \"stm32f4.*\", target = \"thumbv7em-none-eabi\", features = [\"low-power\", \"single-bank\"] },\n    { regex_feature = \"stm32f7[67]..[ig]\", target = \"thumbv7em-none-eabi\", features = [\"dual-bank\"] },\n    { regex_feature = \"stm32f7.*\", target = \"thumbv7em-none-eabi\" },\n    { regex_feature = \"stm32c0.*\", target = \"thumbv6m-none-eabi\" },\n    { regex_feature = \"stm32g0...c\", target = \"thumbv6m-none-eabi\", features = [\"dual-bank\"] },\n    { regex_feature = \"stm32g0.*\", target = \"thumbv6m-none-eabi\" },\n    { regex_feature = \"stm32g4[78].*\", target = \"thumbv7em-none-eabi\", features = [\"low-power\", \"dual-bank\"] },\n    { regex_feature = \"stm32g4.*\", target = \"thumbv7em-none-eabi\", features = [\"low-power\"] },\n    { regex_feature = \"stm32h5.*\", target = \"thumbv8m.main-none-eabihf\", features = [\"low-power\"] },\n    { regex_feature = \"stm32h7.*\", target = \"thumbv7em-none-eabi\" },\n    { regex_feature = \"stm32l0.*\", target = \"thumbv6m-none-eabi\", features = [\"low-power\"] },\n    { regex_feature = \"stm32l1.*\", target = \"thumbv7m-none-eabi\" },\n    { regex_feature = \"stm32l4[pqrs].*\", target = \"thumbv7em-none-eabi\", features = [\"dual-bank\"] },\n    { regex_feature = \"stm32l4.*\", target = \"thumbv7em-none-eabi\" },\n    { regex_feature = \"stm32l5...e\", target = \"thumbv8m.main-none-eabihf\", features = [\"low-power\", \"dual-bank\"] },\n    { regex_feature = \"stm32l5.*\", target = \"thumbv8m.main-none-eabihf\", features = [\"low-power\"] },\n    { regex_feature = \"stm32u0.*\", target = \"thumbv6m-none-eabi\" },\n    { regex_feature = \"stm32u3.*\", target = \"thumbv8m.main-none-eabihf\"},\n    { regex_feature = \"stm32u5.*\", target = \"thumbv8m.main-none-eabihf\" },\n    { regex_feature = \"stm32wb.*\", target = \"thumbv7em-none-eabi\" },\n    { regex_feature = \"stm32wba.*\", target = \"thumbv8m.main-none-eabihf\" },\n    { regex_feature = \"stm32wl.*\", target = \"thumbv7em-none-eabi\" },\n    { regex_feature = \"stm32n6.*\", target = \"thumbv8m.main-none-eabihf\" },\n]\n\n[package.metadata.docs.rs]\nfeatures = [\"defmt\", \"unstable-pac\", \"exti\", \"time-driver-any\", \"time\", \"stm32h755zi-cm7\", \"dual-bank\"]\nrustdoc-args = [\"--cfg\", \"docsrs\"]\n\n[dependencies]\nembassy-sync = { version = \"0.8.0\", path = \"../embassy-sync\" }\nembassy-time = { version = \"0.5.1\", path = \"../embassy-time\", optional = true }\nembassy-time-driver = { version = \"0.2.2\", path = \"../embassy-time-driver\", optional = true }\nembassy-time-queue-utils = { version = \"0.3.0\", path = \"../embassy-time-queue-utils\", optional = true }\nembassy-futures = { version = \"0.1.2\", path = \"../embassy-futures\" }\nembassy-hal-internal = { version = \"0.5.0\", path = \"../embassy-hal-internal\", features = [\"cortex-m\", \"prio-bits-4\"] }\nembassy-embedded-hal = { version = \"0.6.0\", path = \"../embassy-embedded-hal\", default-features = false }\nembassy-net-driver = { version = \"0.2.0\", path = \"../embassy-net-driver\" }\nembassy-usb-driver = { version = \"0.2.0\", path = \"../embassy-usb-driver\" }\nembassy-usb-synopsys-otg = { version = \"0.3.2\", path = \"../embassy-usb-synopsys-otg\" }\nembassy-executor = { version = \"0.10.0\", path = \"../embassy-executor\", optional = true }\ncyw43 = { version = \"0.7.0\", path = \"../cyw43\", optional = true }\n\nembedded-hal-02 = { package = \"embedded-hal\", version = \"0.2.6\", features = [\"unproven\"] }\nembedded-hal-1 = { package = \"embedded-hal\", version = \"1.0\" }\nembedded-hal-async = { version = \"1.0\" }\nembedded-hal-nb = { version = \"1.0\" }\nembedded-can = \"0.4\"\n\nembedded-storage = \"0.3.1\"\nembedded-storage-async = { version = \"0.4.1\" }\n\nrand-core-06 = { package = \"rand_core\", version = \"0.6\" }\nrand-core-09 = { package = \"rand_core\", version = \"0.9\" }\n\n\ndefmt = { version = \"1.0.1\", optional = true }\nlog = { version = \"0.4.14\", optional = true }\ncortex-m-rt = \">=0.6.15,<0.8\"\ncortex-m = \"0.7.6\"\nfutures-util = { version = \"0.3.30\", default-features = false }\nsdio-host = \"0.9.0\"\ncritical-section = \"1.1\"\n\nvcell = \"0.1.3\"\nnb = \"1.0.0\"\nstm32-hrtim = { version = \"0.2.0\", optional = true }\nstm32-fmc = \"0.4.0\"\ncfg-if = \"1.0.0\"\nembedded-io = { version = \"0.7.1\" }\nembedded-io-async = { version = \"0.7.0\" }\nchrono = { version = \"^0.4\", default-features = false, optional = true }\nbit_field = \"0.10.2\"\ntrait-set = \"0.3.0\"\ndocument-features = \"0.2.7\"\n\nstatic_assertions = { version = \"1.1\" }\nvolatile-register = { version = \"0.2.1\" }\nbitflags = \"2.10.0\"\n\nblock-device-driver = { version = \"0.2\" }\naligned = \"0.4.3\"\nheapless = \"0.9.1\"\n\nstm32-metapac = { version = \"21\" }\n# stm32-metapac = { git = \"https://github.com/embassy-rs/stm32-data-generated\", tag = \"stm32-data-21b543f6cab91a2966c0a4247e418d73a87a4ae7\" }\n\n[build-dependencies]\nstm32-metapac = { version = \"21\", default-features = false, features = [\"metadata\"]}\n# stm32-metapac = { git = \"https://github.com/embassy-rs/stm32-data-generated\", tag = \"stm32-data-21b543f6cab91a2966c0a4247e418d73a87a4ae7\", default-features = false, features = [\"metadata\"] }\n\nproc-macro2 = \"1.0.36\"\nquote = \"1.0.15\"\nregex = \"1.12.3\"\n\n[dev-dependencies]\ncritical-section = { version = \"1.1\", features = [\"std\"] }\nproptest = \"1.5.0\"\nproptest-state-machine = \"0.3.0\"\n\n\n[features]\ndefault = [\"rt\"]\n\n## Enable `stm32-metapac`'s `rt` feature\nrt = [\"stm32-metapac/rt\"]\n\n## Use [`defmt`](https://docs.rs/defmt/latest/defmt/) for logging\ndefmt = [\n    \"dep:defmt\",\n    \"embassy-sync/defmt\",\n    \"embassy-embedded-hal/defmt\",\n    \"embassy-hal-internal/defmt\",\n    \"embedded-io-async/defmt\",\n    \"embassy-usb-driver/defmt\",\n    \"embassy-net-driver/defmt\",\n    \"embassy-time?/defmt\",\n    \"embassy-usb-synopsys-otg/defmt\",\n    \"stm32-metapac/defmt\"\n]\n## Use log for logging\nlog = [\"dep:log\"]\n## Enable chrono support\nchrono = [\"dep:chrono\"]\n## Enable cyw\ncyw43 = [\"dep:cyw43\"]\n\nstm32-hrtim = [\"dep:stm32-hrtim\"]\n\nexti = []\nlow-power = [ \"time\", \"chrono\" ]\nlow-power-debug-with-sleep = [ \"low-power\" ]\nlow-power-defmt-flush = [ \"defmt\" ]\n\n_executor = [ \"dep:embassy-executor\", \"low-power\" ]\n\n## Enable the thread-mode low-power executor.\nexecutor-thread = [\"_executor\"]\n\n## Enable the interrupt-mode low-power executor.\nexecutor-interrupt = [\"_executor\"]\n\n## Automatically generate `memory.x` file based on the memory map from [`stm32-metapac`](https://docs.rs/stm32-metapac/)\nmemory-x = []\n\n## Use secure registers when TrustZone is enabled\ntrustzone-secure = []\n\n## Re-export stm32-metapac at `embassy_stm32::pac`.\n## This is unstable because semver-minor (non-breaking) releases of embassy-stm32 may major-bump (breaking) the stm32-metapac version.\n## If this is an issue for you, you're encouraged to directly depend on a fixed version of the PAC.\n## There are no plans to make this stable.\nunstable-pac = []\n\n## Enable this feature to disable the overclocking check.\n## DO NOT ENABLE THIS FEATURE UNLESS YOU KNOW WHAT YOU'RE DOING.\nunchecked-overclocking = []\n\n#! ## Time\n\n## Enables additional driver features that depend on embassy-time\ntime = [\"dep:embassy-time\", \"embassy-embedded-hal/time\"]\n\n# Features starting with `_` are for internal use only. They're not intended\n# to be enabled by other crates, and are not covered by semver guarantees.\n_time-driver = [\"dep:embassy-time-driver\", \"time\", \"dep:embassy-time-queue-utils\"]\n_lp-time-driver = [\"_time-driver\"]\n\n## Use any time driver\ntime-driver-any = [\"_time-driver\"]\n## Use TIM1 as time driver\ntime-driver-tim1 = [\"_time-driver\"]\n## Use TIM2 as time driver\ntime-driver-tim2 = [\"_time-driver\"]\n## Use TIM3 as time driver\ntime-driver-tim3 = [\"_time-driver\"]\n## Use TIM4 as time driver\ntime-driver-tim4 = [\"_time-driver\"]\n## Use TIM5 as time driver\ntime-driver-tim5 = [\"_time-driver\"]\n## Use TIM8 as time driver\ntime-driver-tim8 = [\"_time-driver\"]\n## Use TIM9 as time driver\ntime-driver-tim9 = [\"_time-driver\"]\n## Use TIM12 as time driver\ntime-driver-tim12 = [\"_time-driver\"]\n## Use TIM15 as time driver\ntime-driver-tim15 = [\"_time-driver\"]\n## Use TIM20 as time driver\ntime-driver-tim20 = [\"_time-driver\"]\n## Use TIM21 as time driver\ntime-driver-tim21 = [\"_time-driver\"]\n## Use TIM22 as time driver\ntime-driver-tim22 = [\"_time-driver\"]\n## Use TIM23 as time driver\ntime-driver-tim23 = [\"_time-driver\"]\n## Use TIM24 as time driver\ntime-driver-tim24 = [\"_time-driver\"]\n## Use LPTIM1 as time driver\ntime-driver-lptim1 = [\"_lp-time-driver\"]\n## Use LPTIM2 as time driver\ntime-driver-lptim2 = [\"_lp-time-driver\"]\n## Use LPTIM3 as time driver\ntime-driver-lptim3 = [\"_lp-time-driver\"]\n\n#! ## Analog Switch Pins (Pxy_C) on STM32H7 series\n#! Get `PXY` and `PXY_C` singletons. Digital impls are on `PXY`, Analog impls are on `PXY_C`\n#! If disabled, you get only the `PXY` singleton. It has both digital and analog impls.\n\n## Split PA0\nsplit-pa0 = [\"_split-pins-enabled\"]\n## Split PA1\nsplit-pa1 = [\"_split-pins-enabled\"]\n## Split PC2\nsplit-pc2 = [\"_split-pins-enabled\"]\n## Split PC3\nsplit-pc3 = [\"_split-pins-enabled\"]\n\ndual-bank = []\nsingle-bank = []\n\n# Set all GPIO pins (except SWD) to analog in init()\ngpio-init-analog = []\n\n## internal use only\n_split-pins-enabled = []\n\n## internal use only\n_dual-core = []\n_core-cm0p = []\n_core-cm4 = []\n_core-cm7 = []\n\n#! ## Chip-selection features\n#! Select your chip by specifying the model as a feature, e.g. `stm32c011d6`.\n#! Check the `Cargo.toml` for the latest list of supported chips.\n#!\n#! **Important:** Do not forget to adapt the target chip in your toolchain,\n#! e.g. in `.cargo/config.toml`.\n\nstm32c011d6 = [ \"stm32-metapac/stm32c011d6\" ]\nstm32c011f4 = [ \"stm32-metapac/stm32c011f4\" ]\nstm32c011f6 = [ \"stm32-metapac/stm32c011f6\" ]\nstm32c011j4 = [ \"stm32-metapac/stm32c011j4\" ]\nstm32c011j6 = [ \"stm32-metapac/stm32c011j6\" ]\nstm32c031c4 = [ \"stm32-metapac/stm32c031c4\" ]\nstm32c031c6 = [ \"stm32-metapac/stm32c031c6\" ]\nstm32c031f4 = [ \"stm32-metapac/stm32c031f4\" ]\nstm32c031f6 = [ \"stm32-metapac/stm32c031f6\" ]\nstm32c031g4 = [ \"stm32-metapac/stm32c031g4\" ]\nstm32c031g6 = [ \"stm32-metapac/stm32c031g6\" ]\nstm32c031k4 = [ \"stm32-metapac/stm32c031k4\" ]\nstm32c031k6 = [ \"stm32-metapac/stm32c031k6\" ]\nstm32c051c6 = [ \"stm32-metapac/stm32c051c6\" ]\nstm32c051c8 = [ \"stm32-metapac/stm32c051c8\" ]\nstm32c051d8 = [ \"stm32-metapac/stm32c051d8\" ]\nstm32c051f6 = [ \"stm32-metapac/stm32c051f6\" ]\nstm32c051f8 = [ \"stm32-metapac/stm32c051f8\" ]\nstm32c051g6 = [ \"stm32-metapac/stm32c051g6\" ]\nstm32c051g8 = [ \"stm32-metapac/stm32c051g8\" ]\nstm32c051k6 = [ \"stm32-metapac/stm32c051k6\" ]\nstm32c051k8 = [ \"stm32-metapac/stm32c051k8\" ]\nstm32c071c8 = [ \"stm32-metapac/stm32c071c8\" ]\nstm32c071cb = [ \"stm32-metapac/stm32c071cb\" ]\nstm32c071f8 = [ \"stm32-metapac/stm32c071f8\" ]\nstm32c071fb = [ \"stm32-metapac/stm32c071fb\" ]\nstm32c071g8 = [ \"stm32-metapac/stm32c071g8\" ]\nstm32c071gb = [ \"stm32-metapac/stm32c071gb\" ]\nstm32c071k8 = [ \"stm32-metapac/stm32c071k8\" ]\nstm32c071kb = [ \"stm32-metapac/stm32c071kb\" ]\nstm32c071r8 = [ \"stm32-metapac/stm32c071r8\" ]\nstm32c071rb = [ \"stm32-metapac/stm32c071rb\" ]\nstm32c091cb = [ \"stm32-metapac/stm32c091cb\" ]\nstm32c091cc = [ \"stm32-metapac/stm32c091cc\" ]\nstm32c091ec = [ \"stm32-metapac/stm32c091ec\" ]\nstm32c091fb = [ \"stm32-metapac/stm32c091fb\" ]\nstm32c091fc = [ \"stm32-metapac/stm32c091fc\" ]\nstm32c091gb = [ \"stm32-metapac/stm32c091gb\" ]\nstm32c091gc = [ \"stm32-metapac/stm32c091gc\" ]\nstm32c091kb = [ \"stm32-metapac/stm32c091kb\" ]\nstm32c091kc = [ \"stm32-metapac/stm32c091kc\" ]\nstm32c091rb = [ \"stm32-metapac/stm32c091rb\" ]\nstm32c091rc = [ \"stm32-metapac/stm32c091rc\" ]\nstm32c092cb = [ \"stm32-metapac/stm32c092cb\" ]\nstm32c092cc = [ \"stm32-metapac/stm32c092cc\" ]\nstm32c092ec = [ \"stm32-metapac/stm32c092ec\" ]\nstm32c092fb = [ \"stm32-metapac/stm32c092fb\" ]\nstm32c092fc = [ \"stm32-metapac/stm32c092fc\" ]\nstm32c092gb = [ \"stm32-metapac/stm32c092gb\" ]\nstm32c092gc = [ \"stm32-metapac/stm32c092gc\" ]\nstm32c092kb = [ \"stm32-metapac/stm32c092kb\" ]\nstm32c092kc = [ \"stm32-metapac/stm32c092kc\" ]\nstm32c092rb = [ \"stm32-metapac/stm32c092rb\" ]\nstm32c092rc = [ \"stm32-metapac/stm32c092rc\" ]\nstm32f030c6 = [ \"stm32-metapac/stm32f030c6\" ]\nstm32f030c8 = [ \"stm32-metapac/stm32f030c8\" ]\nstm32f030cc = [ \"stm32-metapac/stm32f030cc\" ]\nstm32f030f4 = [ \"stm32-metapac/stm32f030f4\" ]\nstm32f030k6 = [ \"stm32-metapac/stm32f030k6\" ]\nstm32f030r8 = [ \"stm32-metapac/stm32f030r8\" ]\nstm32f030rc = [ \"stm32-metapac/stm32f030rc\" ]\nstm32f031c4 = [ \"stm32-metapac/stm32f031c4\" ]\nstm32f031c6 = [ \"stm32-metapac/stm32f031c6\" ]\nstm32f031e6 = [ \"stm32-metapac/stm32f031e6\" ]\nstm32f031f4 = [ \"stm32-metapac/stm32f031f4\" ]\nstm32f031f6 = [ \"stm32-metapac/stm32f031f6\" ]\nstm32f031g4 = [ \"stm32-metapac/stm32f031g4\" ]\nstm32f031g6 = [ \"stm32-metapac/stm32f031g6\" ]\nstm32f031k4 = [ \"stm32-metapac/stm32f031k4\" ]\nstm32f031k6 = [ \"stm32-metapac/stm32f031k6\" ]\nstm32f038c6 = [ \"stm32-metapac/stm32f038c6\" ]\nstm32f038e6 = [ \"stm32-metapac/stm32f038e6\" ]\nstm32f038f6 = [ \"stm32-metapac/stm32f038f6\" ]\nstm32f038g6 = [ \"stm32-metapac/stm32f038g6\" ]\nstm32f038k6 = [ \"stm32-metapac/stm32f038k6\" ]\nstm32f042c4 = [ \"stm32-metapac/stm32f042c4\" ]\nstm32f042c6 = [ \"stm32-metapac/stm32f042c6\" ]\nstm32f042f4 = [ \"stm32-metapac/stm32f042f4\" ]\nstm32f042f6 = [ \"stm32-metapac/stm32f042f6\" ]\nstm32f042g4 = [ \"stm32-metapac/stm32f042g4\" ]\nstm32f042g6 = [ \"stm32-metapac/stm32f042g6\" ]\nstm32f042k4 = [ \"stm32-metapac/stm32f042k4\" ]\nstm32f042k6 = [ \"stm32-metapac/stm32f042k6\" ]\nstm32f042t6 = [ \"stm32-metapac/stm32f042t6\" ]\nstm32f048c6 = [ \"stm32-metapac/stm32f048c6\" ]\nstm32f048g6 = [ \"stm32-metapac/stm32f048g6\" ]\nstm32f048t6 = [ \"stm32-metapac/stm32f048t6\" ]\nstm32f051c4 = [ \"stm32-metapac/stm32f051c4\" ]\nstm32f051c6 = [ \"stm32-metapac/stm32f051c6\" ]\nstm32f051c8 = [ \"stm32-metapac/stm32f051c8\" ]\nstm32f051k4 = [ \"stm32-metapac/stm32f051k4\" ]\nstm32f051k6 = [ \"stm32-metapac/stm32f051k6\" ]\nstm32f051k8 = [ \"stm32-metapac/stm32f051k8\" ]\nstm32f051r4 = [ \"stm32-metapac/stm32f051r4\" ]\nstm32f051r6 = [ \"stm32-metapac/stm32f051r6\" ]\nstm32f051r8 = [ \"stm32-metapac/stm32f051r8\" ]\nstm32f051t8 = [ \"stm32-metapac/stm32f051t8\" ]\nstm32f058c8 = [ \"stm32-metapac/stm32f058c8\" ]\nstm32f058r8 = [ \"stm32-metapac/stm32f058r8\" ]\nstm32f058t8 = [ \"stm32-metapac/stm32f058t8\" ]\nstm32f070c6 = [ \"stm32-metapac/stm32f070c6\" ]\nstm32f070cb = [ \"stm32-metapac/stm32f070cb\" ]\nstm32f070f6 = [ \"stm32-metapac/stm32f070f6\" ]\nstm32f070rb = [ \"stm32-metapac/stm32f070rb\" ]\nstm32f071c8 = [ \"stm32-metapac/stm32f071c8\" ]\nstm32f071cb = [ \"stm32-metapac/stm32f071cb\" ]\nstm32f071rb = [ \"stm32-metapac/stm32f071rb\" ]\nstm32f071v8 = [ \"stm32-metapac/stm32f071v8\" ]\nstm32f071vb = [ \"stm32-metapac/stm32f071vb\" ]\nstm32f072c8 = [ \"stm32-metapac/stm32f072c8\" ]\nstm32f072cb = [ \"stm32-metapac/stm32f072cb\" ]\nstm32f072r8 = [ \"stm32-metapac/stm32f072r8\" ]\nstm32f072rb = [ \"stm32-metapac/stm32f072rb\" ]\nstm32f072v8 = [ \"stm32-metapac/stm32f072v8\" ]\nstm32f072vb = [ \"stm32-metapac/stm32f072vb\" ]\nstm32f078cb = [ \"stm32-metapac/stm32f078cb\" ]\nstm32f078rb = [ \"stm32-metapac/stm32f078rb\" ]\nstm32f078vb = [ \"stm32-metapac/stm32f078vb\" ]\nstm32f091cb = [ \"stm32-metapac/stm32f091cb\" ]\nstm32f091cc = [ \"stm32-metapac/stm32f091cc\" ]\nstm32f091rb = [ \"stm32-metapac/stm32f091rb\" ]\nstm32f091rc = [ \"stm32-metapac/stm32f091rc\" ]\nstm32f091vb = [ \"stm32-metapac/stm32f091vb\" ]\nstm32f091vc = [ \"stm32-metapac/stm32f091vc\" ]\nstm32f098cc = [ \"stm32-metapac/stm32f098cc\" ]\nstm32f098rc = [ \"stm32-metapac/stm32f098rc\" ]\nstm32f098vc = [ \"stm32-metapac/stm32f098vc\" ]\nstm32f100c4 = [ \"stm32-metapac/stm32f100c4\" ]\nstm32f100c6 = [ \"stm32-metapac/stm32f100c6\" ]\nstm32f100c8 = [ \"stm32-metapac/stm32f100c8\" ]\nstm32f100cb = [ \"stm32-metapac/stm32f100cb\" ]\nstm32f100r4 = [ \"stm32-metapac/stm32f100r4\" ]\nstm32f100r6 = [ \"stm32-metapac/stm32f100r6\" ]\nstm32f100r8 = [ \"stm32-metapac/stm32f100r8\" ]\nstm32f100rb = [ \"stm32-metapac/stm32f100rb\" ]\nstm32f100rc = [ \"stm32-metapac/stm32f100rc\" ]\nstm32f100rd = [ \"stm32-metapac/stm32f100rd\" ]\nstm32f100re = [ \"stm32-metapac/stm32f100re\" ]\nstm32f100v8 = [ \"stm32-metapac/stm32f100v8\" ]\nstm32f100vb = [ \"stm32-metapac/stm32f100vb\" ]\nstm32f100vc = [ \"stm32-metapac/stm32f100vc\" ]\nstm32f100vd = [ \"stm32-metapac/stm32f100vd\" ]\nstm32f100ve = [ \"stm32-metapac/stm32f100ve\" ]\nstm32f100zc = [ \"stm32-metapac/stm32f100zc\" ]\nstm32f100zd = [ \"stm32-metapac/stm32f100zd\" ]\nstm32f100ze = [ \"stm32-metapac/stm32f100ze\" ]\nstm32f101c4 = [ \"stm32-metapac/stm32f101c4\" ]\nstm32f101c6 = [ \"stm32-metapac/stm32f101c6\" ]\nstm32f101c8 = [ \"stm32-metapac/stm32f101c8\" ]\nstm32f101cb = [ \"stm32-metapac/stm32f101cb\" ]\nstm32f101r4 = [ \"stm32-metapac/stm32f101r4\" ]\nstm32f101r6 = [ \"stm32-metapac/stm32f101r6\" ]\nstm32f101r8 = [ \"stm32-metapac/stm32f101r8\" ]\nstm32f101rb = [ \"stm32-metapac/stm32f101rb\" ]\nstm32f101rc = [ \"stm32-metapac/stm32f101rc\" ]\nstm32f101rd = [ \"stm32-metapac/stm32f101rd\" ]\nstm32f101re = [ \"stm32-metapac/stm32f101re\" ]\nstm32f101rf = [ \"stm32-metapac/stm32f101rf\" ]\nstm32f101rg = [ \"stm32-metapac/stm32f101rg\" ]\nstm32f101t4 = [ \"stm32-metapac/stm32f101t4\" ]\nstm32f101t6 = [ \"stm32-metapac/stm32f101t6\" ]\nstm32f101t8 = [ \"stm32-metapac/stm32f101t8\" ]\nstm32f101tb = [ \"stm32-metapac/stm32f101tb\" ]\nstm32f101v8 = [ \"stm32-metapac/stm32f101v8\" ]\nstm32f101vb = [ \"stm32-metapac/stm32f101vb\" ]\nstm32f101vc = [ \"stm32-metapac/stm32f101vc\" ]\nstm32f101vd = [ \"stm32-metapac/stm32f101vd\" ]\nstm32f101ve = [ \"stm32-metapac/stm32f101ve\" ]\nstm32f101vf = [ \"stm32-metapac/stm32f101vf\" ]\nstm32f101vg = [ \"stm32-metapac/stm32f101vg\" ]\nstm32f101zc = [ \"stm32-metapac/stm32f101zc\" ]\nstm32f101zd = [ \"stm32-metapac/stm32f101zd\" ]\nstm32f101ze = [ \"stm32-metapac/stm32f101ze\" ]\nstm32f101zf = [ \"stm32-metapac/stm32f101zf\" ]\nstm32f101zg = [ \"stm32-metapac/stm32f101zg\" ]\nstm32f102c4 = [ \"stm32-metapac/stm32f102c4\" ]\nstm32f102c6 = [ \"stm32-metapac/stm32f102c6\" ]\nstm32f102c8 = [ \"stm32-metapac/stm32f102c8\" ]\nstm32f102cb = [ \"stm32-metapac/stm32f102cb\" ]\nstm32f102r4 = [ \"stm32-metapac/stm32f102r4\" ]\nstm32f102r6 = [ \"stm32-metapac/stm32f102r6\" ]\nstm32f102r8 = [ \"stm32-metapac/stm32f102r8\" ]\nstm32f102rb = [ \"stm32-metapac/stm32f102rb\" ]\nstm32f103c4 = [ \"stm32-metapac/stm32f103c4\" ]\nstm32f103c6 = [ \"stm32-metapac/stm32f103c6\" ]\nstm32f103c8 = [ \"stm32-metapac/stm32f103c8\" ]\nstm32f103cb = [ \"stm32-metapac/stm32f103cb\" ]\nstm32f103r4 = [ \"stm32-metapac/stm32f103r4\" ]\nstm32f103r6 = [ \"stm32-metapac/stm32f103r6\" ]\nstm32f103r8 = [ \"stm32-metapac/stm32f103r8\" ]\nstm32f103rb = [ \"stm32-metapac/stm32f103rb\" ]\nstm32f103rc = [ \"stm32-metapac/stm32f103rc\" ]\nstm32f103rd = [ \"stm32-metapac/stm32f103rd\" ]\nstm32f103re = [ \"stm32-metapac/stm32f103re\" ]\nstm32f103rf = [ \"stm32-metapac/stm32f103rf\" ]\nstm32f103rg = [ \"stm32-metapac/stm32f103rg\" ]\nstm32f103t4 = [ \"stm32-metapac/stm32f103t4\" ]\nstm32f103t6 = [ \"stm32-metapac/stm32f103t6\" ]\nstm32f103t8 = [ \"stm32-metapac/stm32f103t8\" ]\nstm32f103tb = [ \"stm32-metapac/stm32f103tb\" ]\nstm32f103v8 = [ \"stm32-metapac/stm32f103v8\" ]\nstm32f103vb = [ \"stm32-metapac/stm32f103vb\" ]\nstm32f103vc = [ \"stm32-metapac/stm32f103vc\" ]\nstm32f103vd = [ \"stm32-metapac/stm32f103vd\" ]\nstm32f103ve = [ \"stm32-metapac/stm32f103ve\" ]\nstm32f103vf = [ \"stm32-metapac/stm32f103vf\" ]\nstm32f103vg = [ \"stm32-metapac/stm32f103vg\" ]\nstm32f103zc = [ \"stm32-metapac/stm32f103zc\" ]\nstm32f103zd = [ \"stm32-metapac/stm32f103zd\" ]\nstm32f103ze = [ \"stm32-metapac/stm32f103ze\" ]\nstm32f103zf = [ \"stm32-metapac/stm32f103zf\" ]\nstm32f103zg = [ \"stm32-metapac/stm32f103zg\" ]\nstm32f105r8 = [ \"stm32-metapac/stm32f105r8\" ]\nstm32f105rb = [ \"stm32-metapac/stm32f105rb\" ]\nstm32f105rc = [ \"stm32-metapac/stm32f105rc\" ]\nstm32f105v8 = [ \"stm32-metapac/stm32f105v8\" ]\nstm32f105vb = [ \"stm32-metapac/stm32f105vb\" ]\nstm32f105vc = [ \"stm32-metapac/stm32f105vc\" ]\nstm32f107rb = [ \"stm32-metapac/stm32f107rb\" ]\nstm32f107rc = [ \"stm32-metapac/stm32f107rc\" ]\nstm32f107vb = [ \"stm32-metapac/stm32f107vb\" ]\nstm32f107vc = [ \"stm32-metapac/stm32f107vc\" ]\nstm32f205rb = [ \"stm32-metapac/stm32f205rb\" ]\nstm32f205rc = [ \"stm32-metapac/stm32f205rc\" ]\nstm32f205re = [ \"stm32-metapac/stm32f205re\" ]\nstm32f205rf = [ \"stm32-metapac/stm32f205rf\" ]\nstm32f205rg = [ \"stm32-metapac/stm32f205rg\" ]\nstm32f205vb = [ \"stm32-metapac/stm32f205vb\" ]\nstm32f205vc = [ \"stm32-metapac/stm32f205vc\" ]\nstm32f205ve = [ \"stm32-metapac/stm32f205ve\" ]\nstm32f205vf = [ \"stm32-metapac/stm32f205vf\" ]\nstm32f205vg = [ \"stm32-metapac/stm32f205vg\" ]\nstm32f205zc = [ \"stm32-metapac/stm32f205zc\" ]\nstm32f205ze = [ \"stm32-metapac/stm32f205ze\" ]\nstm32f205zf = [ \"stm32-metapac/stm32f205zf\" ]\nstm32f205zg = [ \"stm32-metapac/stm32f205zg\" ]\nstm32f207ic = [ \"stm32-metapac/stm32f207ic\" ]\nstm32f207ie = [ \"stm32-metapac/stm32f207ie\" ]\nstm32f207if = [ \"stm32-metapac/stm32f207if\" ]\nstm32f207ig = [ \"stm32-metapac/stm32f207ig\" ]\nstm32f207vc = [ \"stm32-metapac/stm32f207vc\" ]\nstm32f207ve = [ \"stm32-metapac/stm32f207ve\" ]\nstm32f207vf = [ \"stm32-metapac/stm32f207vf\" ]\nstm32f207vg = [ \"stm32-metapac/stm32f207vg\" ]\nstm32f207zc = [ \"stm32-metapac/stm32f207zc\" ]\nstm32f207ze = [ \"stm32-metapac/stm32f207ze\" ]\nstm32f207zf = [ \"stm32-metapac/stm32f207zf\" ]\nstm32f207zg = [ \"stm32-metapac/stm32f207zg\" ]\nstm32f215re = [ \"stm32-metapac/stm32f215re\" ]\nstm32f215rg = [ \"stm32-metapac/stm32f215rg\" ]\nstm32f215ve = [ \"stm32-metapac/stm32f215ve\" ]\nstm32f215vg = [ \"stm32-metapac/stm32f215vg\" ]\nstm32f215ze = [ \"stm32-metapac/stm32f215ze\" ]\nstm32f215zg = [ \"stm32-metapac/stm32f215zg\" ]\nstm32f217ie = [ \"stm32-metapac/stm32f217ie\" ]\nstm32f217ig = [ \"stm32-metapac/stm32f217ig\" ]\nstm32f217ve = [ \"stm32-metapac/stm32f217ve\" ]\nstm32f217vg = [ \"stm32-metapac/stm32f217vg\" ]\nstm32f217ze = [ \"stm32-metapac/stm32f217ze\" ]\nstm32f217zg = [ \"stm32-metapac/stm32f217zg\" ]\nstm32f301c6 = [ \"stm32-metapac/stm32f301c6\" ]\nstm32f301c8 = [ \"stm32-metapac/stm32f301c8\" ]\nstm32f301k6 = [ \"stm32-metapac/stm32f301k6\" ]\nstm32f301k8 = [ \"stm32-metapac/stm32f301k8\" ]\nstm32f301r6 = [ \"stm32-metapac/stm32f301r6\" ]\nstm32f301r8 = [ \"stm32-metapac/stm32f301r8\" ]\nstm32f302c6 = [ \"stm32-metapac/stm32f302c6\" ]\nstm32f302c8 = [ \"stm32-metapac/stm32f302c8\" ]\nstm32f302cb = [ \"stm32-metapac/stm32f302cb\" ]\nstm32f302cc = [ \"stm32-metapac/stm32f302cc\" ]\nstm32f302k6 = [ \"stm32-metapac/stm32f302k6\" ]\nstm32f302k8 = [ \"stm32-metapac/stm32f302k8\" ]\nstm32f302r6 = [ \"stm32-metapac/stm32f302r6\" ]\nstm32f302r8 = [ \"stm32-metapac/stm32f302r8\" ]\nstm32f302rb = [ \"stm32-metapac/stm32f302rb\" ]\nstm32f302rc = [ \"stm32-metapac/stm32f302rc\" ]\nstm32f302rd = [ \"stm32-metapac/stm32f302rd\" ]\nstm32f302re = [ \"stm32-metapac/stm32f302re\" ]\nstm32f302vb = [ \"stm32-metapac/stm32f302vb\" ]\nstm32f302vc = [ \"stm32-metapac/stm32f302vc\" ]\nstm32f302vd = [ \"stm32-metapac/stm32f302vd\" ]\nstm32f302ve = [ \"stm32-metapac/stm32f302ve\" ]\nstm32f302zd = [ \"stm32-metapac/stm32f302zd\" ]\nstm32f302ze = [ \"stm32-metapac/stm32f302ze\" ]\nstm32f303c6 = [ \"stm32-metapac/stm32f303c6\" ]\nstm32f303c8 = [ \"stm32-metapac/stm32f303c8\" ]\nstm32f303cb = [ \"stm32-metapac/stm32f303cb\" ]\nstm32f303cc = [ \"stm32-metapac/stm32f303cc\" ]\nstm32f303k6 = [ \"stm32-metapac/stm32f303k6\" ]\nstm32f303k8 = [ \"stm32-metapac/stm32f303k8\" ]\nstm32f303r6 = [ \"stm32-metapac/stm32f303r6\" ]\nstm32f303r8 = [ \"stm32-metapac/stm32f303r8\" ]\nstm32f303rb = [ \"stm32-metapac/stm32f303rb\" ]\nstm32f303rc = [ \"stm32-metapac/stm32f303rc\" ]\nstm32f303rd = [ \"stm32-metapac/stm32f303rd\" ]\nstm32f303re = [ \"stm32-metapac/stm32f303re\" ]\nstm32f303vb = [ \"stm32-metapac/stm32f303vb\" ]\nstm32f303vc = [ \"stm32-metapac/stm32f303vc\" ]\nstm32f303vd = [ \"stm32-metapac/stm32f303vd\" ]\nstm32f303ve = [ \"stm32-metapac/stm32f303ve\" ]\nstm32f303zd = [ \"stm32-metapac/stm32f303zd\" ]\nstm32f303ze = [ \"stm32-metapac/stm32f303ze\" ]\nstm32f318c8 = [ \"stm32-metapac/stm32f318c8\" ]\nstm32f318k8 = [ \"stm32-metapac/stm32f318k8\" ]\nstm32f328c8 = [ \"stm32-metapac/stm32f328c8\" ]\nstm32f334c4 = [ \"stm32-metapac/stm32f334c4\", \"stm32-hrtim\", \"stm32-hrtim/stm32f334\" ]\nstm32f334c6 = [ \"stm32-metapac/stm32f334c6\", \"stm32-hrtim\", \"stm32-hrtim/stm32f334\" ]\nstm32f334c8 = [ \"stm32-metapac/stm32f334c8\", \"stm32-hrtim\", \"stm32-hrtim/stm32f334\" ]\nstm32f334k4 = [ \"stm32-metapac/stm32f334k4\", \"stm32-hrtim\", \"stm32-hrtim/stm32f334\" ]\nstm32f334k6 = [ \"stm32-metapac/stm32f334k6\", \"stm32-hrtim\", \"stm32-hrtim/stm32f334\" ]\nstm32f334k8 = [ \"stm32-metapac/stm32f334k8\", \"stm32-hrtim\", \"stm32-hrtim/stm32f334\" ]\nstm32f334r6 = [ \"stm32-metapac/stm32f334r6\", \"stm32-hrtim\", \"stm32-hrtim/stm32f334\" ]\nstm32f334r8 = [ \"stm32-metapac/stm32f334r8\", \"stm32-hrtim\", \"stm32-hrtim/stm32f334\" ]\nstm32f358cc = [ \"stm32-metapac/stm32f358cc\" ]\nstm32f358rc = [ \"stm32-metapac/stm32f358rc\" ]\nstm32f358vc = [ \"stm32-metapac/stm32f358vc\" ]\nstm32f373c8 = [ \"stm32-metapac/stm32f373c8\" ]\nstm32f373cb = [ \"stm32-metapac/stm32f373cb\" ]\nstm32f373cc = [ \"stm32-metapac/stm32f373cc\" ]\nstm32f373r8 = [ \"stm32-metapac/stm32f373r8\" ]\nstm32f373rb = [ \"stm32-metapac/stm32f373rb\" ]\nstm32f373rc = [ \"stm32-metapac/stm32f373rc\" ]\nstm32f373v8 = [ \"stm32-metapac/stm32f373v8\" ]\nstm32f373vb = [ \"stm32-metapac/stm32f373vb\" ]\nstm32f373vc = [ \"stm32-metapac/stm32f373vc\" ]\nstm32f378cc = [ \"stm32-metapac/stm32f378cc\" ]\nstm32f378rc = [ \"stm32-metapac/stm32f378rc\" ]\nstm32f378vc = [ \"stm32-metapac/stm32f378vc\" ]\nstm32f398ve = [ \"stm32-metapac/stm32f398ve\" ]\nstm32f401cb = [ \"stm32-metapac/stm32f401cb\" ]\nstm32f401cc = [ \"stm32-metapac/stm32f401cc\" ]\nstm32f401cd = [ \"stm32-metapac/stm32f401cd\" ]\nstm32f401ce = [ \"stm32-metapac/stm32f401ce\" ]\nstm32f401rb = [ \"stm32-metapac/stm32f401rb\" ]\nstm32f401rc = [ \"stm32-metapac/stm32f401rc\" ]\nstm32f401rd = [ \"stm32-metapac/stm32f401rd\" ]\nstm32f401re = [ \"stm32-metapac/stm32f401re\" ]\nstm32f401vb = [ \"stm32-metapac/stm32f401vb\" ]\nstm32f401vc = [ \"stm32-metapac/stm32f401vc\" ]\nstm32f401vd = [ \"stm32-metapac/stm32f401vd\" ]\nstm32f401ve = [ \"stm32-metapac/stm32f401ve\" ]\nstm32f405oe = [ \"stm32-metapac/stm32f405oe\" ]\nstm32f405og = [ \"stm32-metapac/stm32f405og\" ]\nstm32f405rg = [ \"stm32-metapac/stm32f405rg\" ]\nstm32f405vg = [ \"stm32-metapac/stm32f405vg\" ]\nstm32f405zg = [ \"stm32-metapac/stm32f405zg\" ]\nstm32f407ie = [ \"stm32-metapac/stm32f407ie\" ]\nstm32f407ig = [ \"stm32-metapac/stm32f407ig\" ]\nstm32f407ve = [ \"stm32-metapac/stm32f407ve\" ]\nstm32f407vg = [ \"stm32-metapac/stm32f407vg\" ]\nstm32f407ze = [ \"stm32-metapac/stm32f407ze\" ]\nstm32f407zg = [ \"stm32-metapac/stm32f407zg\" ]\nstm32f410c8 = [ \"stm32-metapac/stm32f410c8\" ]\nstm32f410cb = [ \"stm32-metapac/stm32f410cb\" ]\nstm32f410r8 = [ \"stm32-metapac/stm32f410r8\" ]\nstm32f410rb = [ \"stm32-metapac/stm32f410rb\" ]\nstm32f410t8 = [ \"stm32-metapac/stm32f410t8\" ]\nstm32f410tb = [ \"stm32-metapac/stm32f410tb\" ]\nstm32f411cc = [ \"stm32-metapac/stm32f411cc\" ]\nstm32f411ce = [ \"stm32-metapac/stm32f411ce\" ]\nstm32f411rc = [ \"stm32-metapac/stm32f411rc\" ]\nstm32f411re = [ \"stm32-metapac/stm32f411re\" ]\nstm32f411vc = [ \"stm32-metapac/stm32f411vc\" ]\nstm32f411ve = [ \"stm32-metapac/stm32f411ve\" ]\nstm32f412ce = [ \"stm32-metapac/stm32f412ce\" ]\nstm32f412cg = [ \"stm32-metapac/stm32f412cg\" ]\nstm32f412re = [ \"stm32-metapac/stm32f412re\" ]\nstm32f412rg = [ \"stm32-metapac/stm32f412rg\" ]\nstm32f412ve = [ \"stm32-metapac/stm32f412ve\" ]\nstm32f412vg = [ \"stm32-metapac/stm32f412vg\" ]\nstm32f412ze = [ \"stm32-metapac/stm32f412ze\" ]\nstm32f412zg = [ \"stm32-metapac/stm32f412zg\" ]\nstm32f413cg = [ \"stm32-metapac/stm32f413cg\" ]\nstm32f413ch = [ \"stm32-metapac/stm32f413ch\" ]\nstm32f413mg = [ \"stm32-metapac/stm32f413mg\" ]\nstm32f413mh = [ \"stm32-metapac/stm32f413mh\" ]\nstm32f413rg = [ \"stm32-metapac/stm32f413rg\" ]\nstm32f413rh = [ \"stm32-metapac/stm32f413rh\" ]\nstm32f413vg = [ \"stm32-metapac/stm32f413vg\" ]\nstm32f413vh = [ \"stm32-metapac/stm32f413vh\" ]\nstm32f413zg = [ \"stm32-metapac/stm32f413zg\" ]\nstm32f413zh = [ \"stm32-metapac/stm32f413zh\" ]\nstm32f415og = [ \"stm32-metapac/stm32f415og\" ]\nstm32f415rg = [ \"stm32-metapac/stm32f415rg\" ]\nstm32f415vg = [ \"stm32-metapac/stm32f415vg\" ]\nstm32f415zg = [ \"stm32-metapac/stm32f415zg\" ]\nstm32f417ie = [ \"stm32-metapac/stm32f417ie\" ]\nstm32f417ig = [ \"stm32-metapac/stm32f417ig\" ]\nstm32f417ve = [ \"stm32-metapac/stm32f417ve\" ]\nstm32f417vg = [ \"stm32-metapac/stm32f417vg\" ]\nstm32f417ze = [ \"stm32-metapac/stm32f417ze\" ]\nstm32f417zg = [ \"stm32-metapac/stm32f417zg\" ]\nstm32f423ch = [ \"stm32-metapac/stm32f423ch\" ]\nstm32f423mh = [ \"stm32-metapac/stm32f423mh\" ]\nstm32f423rh = [ \"stm32-metapac/stm32f423rh\" ]\nstm32f423vh = [ \"stm32-metapac/stm32f423vh\" ]\nstm32f423zh = [ \"stm32-metapac/stm32f423zh\" ]\nstm32f427ag = [ \"stm32-metapac/stm32f427ag\" ]\nstm32f427ai = [ \"stm32-metapac/stm32f427ai\" ]\nstm32f427ig = [ \"stm32-metapac/stm32f427ig\" ]\nstm32f427ii = [ \"stm32-metapac/stm32f427ii\" ]\nstm32f427vg = [ \"stm32-metapac/stm32f427vg\" ]\nstm32f427vi = [ \"stm32-metapac/stm32f427vi\" ]\nstm32f427zg = [ \"stm32-metapac/stm32f427zg\" ]\nstm32f427zi = [ \"stm32-metapac/stm32f427zi\" ]\nstm32f429ag = [ \"stm32-metapac/stm32f429ag\" ]\nstm32f429ai = [ \"stm32-metapac/stm32f429ai\" ]\nstm32f429be = [ \"stm32-metapac/stm32f429be\" ]\nstm32f429bg = [ \"stm32-metapac/stm32f429bg\" ]\nstm32f429bi = [ \"stm32-metapac/stm32f429bi\" ]\nstm32f429ie = [ \"stm32-metapac/stm32f429ie\" ]\nstm32f429ig = [ \"stm32-metapac/stm32f429ig\" ]\nstm32f429ii = [ \"stm32-metapac/stm32f429ii\" ]\nstm32f429ne = [ \"stm32-metapac/stm32f429ne\" ]\nstm32f429ng = [ \"stm32-metapac/stm32f429ng\" ]\nstm32f429ni = [ \"stm32-metapac/stm32f429ni\" ]\nstm32f429ve = [ \"stm32-metapac/stm32f429ve\" ]\nstm32f429vg = [ \"stm32-metapac/stm32f429vg\" ]\nstm32f429vi = [ \"stm32-metapac/stm32f429vi\" ]\nstm32f429ze = [ \"stm32-metapac/stm32f429ze\" ]\nstm32f429zg = [ \"stm32-metapac/stm32f429zg\" ]\nstm32f429zi = [ \"stm32-metapac/stm32f429zi\" ]\nstm32f437ai = [ \"stm32-metapac/stm32f437ai\" ]\nstm32f437ig = [ \"stm32-metapac/stm32f437ig\" ]\nstm32f437ii = [ \"stm32-metapac/stm32f437ii\" ]\nstm32f437vg = [ \"stm32-metapac/stm32f437vg\" ]\nstm32f437vi = [ \"stm32-metapac/stm32f437vi\" ]\nstm32f437zg = [ \"stm32-metapac/stm32f437zg\" ]\nstm32f437zi = [ \"stm32-metapac/stm32f437zi\" ]\nstm32f439ai = [ \"stm32-metapac/stm32f439ai\" ]\nstm32f439bg = [ \"stm32-metapac/stm32f439bg\" ]\nstm32f439bi = [ \"stm32-metapac/stm32f439bi\" ]\nstm32f439ig = [ \"stm32-metapac/stm32f439ig\" ]\nstm32f439ii = [ \"stm32-metapac/stm32f439ii\" ]\nstm32f439ng = [ \"stm32-metapac/stm32f439ng\" ]\nstm32f439ni = [ \"stm32-metapac/stm32f439ni\" ]\nstm32f439vg = [ \"stm32-metapac/stm32f439vg\" ]\nstm32f439vi = [ \"stm32-metapac/stm32f439vi\" ]\nstm32f439zg = [ \"stm32-metapac/stm32f439zg\" ]\nstm32f439zi = [ \"stm32-metapac/stm32f439zi\" ]\nstm32f446mc = [ \"stm32-metapac/stm32f446mc\" ]\nstm32f446me = [ \"stm32-metapac/stm32f446me\" ]\nstm32f446rc = [ \"stm32-metapac/stm32f446rc\" ]\nstm32f446re = [ \"stm32-metapac/stm32f446re\" ]\nstm32f446vc = [ \"stm32-metapac/stm32f446vc\" ]\nstm32f446ve = [ \"stm32-metapac/stm32f446ve\" ]\nstm32f446zc = [ \"stm32-metapac/stm32f446zc\" ]\nstm32f446ze = [ \"stm32-metapac/stm32f446ze\" ]\nstm32f469ae = [ \"stm32-metapac/stm32f469ae\" ]\nstm32f469ag = [ \"stm32-metapac/stm32f469ag\" ]\nstm32f469ai = [ \"stm32-metapac/stm32f469ai\" ]\nstm32f469be = [ \"stm32-metapac/stm32f469be\" ]\nstm32f469bg = [ \"stm32-metapac/stm32f469bg\" ]\nstm32f469bi = [ \"stm32-metapac/stm32f469bi\" ]\nstm32f469ie = [ \"stm32-metapac/stm32f469ie\" ]\nstm32f469ig = [ \"stm32-metapac/stm32f469ig\" ]\nstm32f469ii = [ \"stm32-metapac/stm32f469ii\" ]\nstm32f469ne = [ \"stm32-metapac/stm32f469ne\" ]\nstm32f469ng = [ \"stm32-metapac/stm32f469ng\" ]\nstm32f469ni = [ \"stm32-metapac/stm32f469ni\" ]\nstm32f469ve = [ \"stm32-metapac/stm32f469ve\" ]\nstm32f469vg = [ \"stm32-metapac/stm32f469vg\" ]\nstm32f469vi = [ \"stm32-metapac/stm32f469vi\" ]\nstm32f469ze = [ \"stm32-metapac/stm32f469ze\" ]\nstm32f469zg = [ \"stm32-metapac/stm32f469zg\" ]\nstm32f469zi = [ \"stm32-metapac/stm32f469zi\" ]\nstm32f479ag = [ \"stm32-metapac/stm32f479ag\" ]\nstm32f479ai = [ \"stm32-metapac/stm32f479ai\" ]\nstm32f479bg = [ \"stm32-metapac/stm32f479bg\" ]\nstm32f479bi = [ \"stm32-metapac/stm32f479bi\" ]\nstm32f479ig = [ \"stm32-metapac/stm32f479ig\" ]\nstm32f479ii = [ \"stm32-metapac/stm32f479ii\" ]\nstm32f479ng = [ \"stm32-metapac/stm32f479ng\" ]\nstm32f479ni = [ \"stm32-metapac/stm32f479ni\" ]\nstm32f479vg = [ \"stm32-metapac/stm32f479vg\" ]\nstm32f479vi = [ \"stm32-metapac/stm32f479vi\" ]\nstm32f479zg = [ \"stm32-metapac/stm32f479zg\" ]\nstm32f479zi = [ \"stm32-metapac/stm32f479zi\" ]\nstm32f722ic = [ \"stm32-metapac/stm32f722ic\" ]\nstm32f722ie = [ \"stm32-metapac/stm32f722ie\" ]\nstm32f722rc = [ \"stm32-metapac/stm32f722rc\" ]\nstm32f722re = [ \"stm32-metapac/stm32f722re\" ]\nstm32f722vc = [ \"stm32-metapac/stm32f722vc\" ]\nstm32f722ve = [ \"stm32-metapac/stm32f722ve\" ]\nstm32f722zc = [ \"stm32-metapac/stm32f722zc\" ]\nstm32f722ze = [ \"stm32-metapac/stm32f722ze\" ]\nstm32f723ic = [ \"stm32-metapac/stm32f723ic\" ]\nstm32f723ie = [ \"stm32-metapac/stm32f723ie\" ]\nstm32f723vc = [ \"stm32-metapac/stm32f723vc\" ]\nstm32f723ve = [ \"stm32-metapac/stm32f723ve\" ]\nstm32f723zc = [ \"stm32-metapac/stm32f723zc\" ]\nstm32f723ze = [ \"stm32-metapac/stm32f723ze\" ]\nstm32f730i8 = [ \"stm32-metapac/stm32f730i8\" ]\nstm32f730r8 = [ \"stm32-metapac/stm32f730r8\" ]\nstm32f730v8 = [ \"stm32-metapac/stm32f730v8\" ]\nstm32f730z8 = [ \"stm32-metapac/stm32f730z8\" ]\nstm32f732ie = [ \"stm32-metapac/stm32f732ie\" ]\nstm32f732re = [ \"stm32-metapac/stm32f732re\" ]\nstm32f732ve = [ \"stm32-metapac/stm32f732ve\" ]\nstm32f732ze = [ \"stm32-metapac/stm32f732ze\" ]\nstm32f733ie = [ \"stm32-metapac/stm32f733ie\" ]\nstm32f733ve = [ \"stm32-metapac/stm32f733ve\" ]\nstm32f733ze = [ \"stm32-metapac/stm32f733ze\" ]\nstm32f745ie = [ \"stm32-metapac/stm32f745ie\" ]\nstm32f745ig = [ \"stm32-metapac/stm32f745ig\" ]\nstm32f745ve = [ \"stm32-metapac/stm32f745ve\" ]\nstm32f745vg = [ \"stm32-metapac/stm32f745vg\" ]\nstm32f745ze = [ \"stm32-metapac/stm32f745ze\" ]\nstm32f745zg = [ \"stm32-metapac/stm32f745zg\" ]\nstm32f746be = [ \"stm32-metapac/stm32f746be\" ]\nstm32f746bg = [ \"stm32-metapac/stm32f746bg\" ]\nstm32f746ie = [ \"stm32-metapac/stm32f746ie\" ]\nstm32f746ig = [ \"stm32-metapac/stm32f746ig\" ]\nstm32f746ne = [ \"stm32-metapac/stm32f746ne\" ]\nstm32f746ng = [ \"stm32-metapac/stm32f746ng\" ]\nstm32f746ve = [ \"stm32-metapac/stm32f746ve\" ]\nstm32f746vg = [ \"stm32-metapac/stm32f746vg\" ]\nstm32f746ze = [ \"stm32-metapac/stm32f746ze\" ]\nstm32f746zg = [ \"stm32-metapac/stm32f746zg\" ]\nstm32f750n8 = [ \"stm32-metapac/stm32f750n8\" ]\nstm32f750v8 = [ \"stm32-metapac/stm32f750v8\" ]\nstm32f750z8 = [ \"stm32-metapac/stm32f750z8\" ]\nstm32f756bg = [ \"stm32-metapac/stm32f756bg\" ]\nstm32f756ig = [ \"stm32-metapac/stm32f756ig\" ]\nstm32f756ng = [ \"stm32-metapac/stm32f756ng\" ]\nstm32f756vg = [ \"stm32-metapac/stm32f756vg\" ]\nstm32f756zg = [ \"stm32-metapac/stm32f756zg\" ]\nstm32f765bg = [ \"stm32-metapac/stm32f765bg\" ]\nstm32f765bi = [ \"stm32-metapac/stm32f765bi\" ]\nstm32f765ig = [ \"stm32-metapac/stm32f765ig\" ]\nstm32f765ii = [ \"stm32-metapac/stm32f765ii\" ]\nstm32f765ng = [ \"stm32-metapac/stm32f765ng\" ]\nstm32f765ni = [ \"stm32-metapac/stm32f765ni\" ]\nstm32f765vg = [ \"stm32-metapac/stm32f765vg\" ]\nstm32f765vi = [ \"stm32-metapac/stm32f765vi\" ]\nstm32f765zg = [ \"stm32-metapac/stm32f765zg\" ]\nstm32f765zi = [ \"stm32-metapac/stm32f765zi\" ]\nstm32f767bg = [ \"stm32-metapac/stm32f767bg\" ]\nstm32f767bi = [ \"stm32-metapac/stm32f767bi\" ]\nstm32f767ig = [ \"stm32-metapac/stm32f767ig\" ]\nstm32f767ii = [ \"stm32-metapac/stm32f767ii\" ]\nstm32f767ng = [ \"stm32-metapac/stm32f767ng\" ]\nstm32f767ni = [ \"stm32-metapac/stm32f767ni\" ]\nstm32f767vg = [ \"stm32-metapac/stm32f767vg\" ]\nstm32f767vi = [ \"stm32-metapac/stm32f767vi\" ]\nstm32f767zg = [ \"stm32-metapac/stm32f767zg\" ]\nstm32f767zi = [ \"stm32-metapac/stm32f767zi\" ]\nstm32f768ai = [ \"stm32-metapac/stm32f768ai\" ]\nstm32f769ag = [ \"stm32-metapac/stm32f769ag\" ]\nstm32f769ai = [ \"stm32-metapac/stm32f769ai\" ]\nstm32f769bg = [ \"stm32-metapac/stm32f769bg\" ]\nstm32f769bi = [ \"stm32-metapac/stm32f769bi\" ]\nstm32f769ig = [ \"stm32-metapac/stm32f769ig\" ]\nstm32f769ii = [ \"stm32-metapac/stm32f769ii\" ]\nstm32f769ng = [ \"stm32-metapac/stm32f769ng\" ]\nstm32f769ni = [ \"stm32-metapac/stm32f769ni\" ]\nstm32f777bi = [ \"stm32-metapac/stm32f777bi\" ]\nstm32f777ii = [ \"stm32-metapac/stm32f777ii\" ]\nstm32f777ni = [ \"stm32-metapac/stm32f777ni\" ]\nstm32f777vi = [ \"stm32-metapac/stm32f777vi\" ]\nstm32f777zi = [ \"stm32-metapac/stm32f777zi\" ]\nstm32f778ai = [ \"stm32-metapac/stm32f778ai\" ]\nstm32f779ai = [ \"stm32-metapac/stm32f779ai\" ]\nstm32f779bi = [ \"stm32-metapac/stm32f779bi\" ]\nstm32f779ii = [ \"stm32-metapac/stm32f779ii\" ]\nstm32f779ni = [ \"stm32-metapac/stm32f779ni\" ]\nstm32g030c6 = [ \"stm32-metapac/stm32g030c6\" ]\nstm32g030c8 = [ \"stm32-metapac/stm32g030c8\" ]\nstm32g030f6 = [ \"stm32-metapac/stm32g030f6\" ]\nstm32g030j6 = [ \"stm32-metapac/stm32g030j6\" ]\nstm32g030k6 = [ \"stm32-metapac/stm32g030k6\" ]\nstm32g030k8 = [ \"stm32-metapac/stm32g030k8\" ]\nstm32g031c4 = [ \"stm32-metapac/stm32g031c4\" ]\nstm32g031c6 = [ \"stm32-metapac/stm32g031c6\" ]\nstm32g031c8 = [ \"stm32-metapac/stm32g031c8\" ]\nstm32g031f4 = [ \"stm32-metapac/stm32g031f4\" ]\nstm32g031f6 = [ \"stm32-metapac/stm32g031f6\" ]\nstm32g031f8 = [ \"stm32-metapac/stm32g031f8\" ]\nstm32g031g4 = [ \"stm32-metapac/stm32g031g4\" ]\nstm32g031g6 = [ \"stm32-metapac/stm32g031g6\" ]\nstm32g031g8 = [ \"stm32-metapac/stm32g031g8\" ]\nstm32g031j4 = [ \"stm32-metapac/stm32g031j4\" ]\nstm32g031j6 = [ \"stm32-metapac/stm32g031j6\" ]\nstm32g031k4 = [ \"stm32-metapac/stm32g031k4\" ]\nstm32g031k6 = [ \"stm32-metapac/stm32g031k6\" ]\nstm32g031k8 = [ \"stm32-metapac/stm32g031k8\" ]\nstm32g031y8 = [ \"stm32-metapac/stm32g031y8\" ]\nstm32g041c6 = [ \"stm32-metapac/stm32g041c6\" ]\nstm32g041c8 = [ \"stm32-metapac/stm32g041c8\" ]\nstm32g041f6 = [ \"stm32-metapac/stm32g041f6\" ]\nstm32g041f8 = [ \"stm32-metapac/stm32g041f8\" ]\nstm32g041g6 = [ \"stm32-metapac/stm32g041g6\" ]\nstm32g041g8 = [ \"stm32-metapac/stm32g041g8\" ]\nstm32g041j6 = [ \"stm32-metapac/stm32g041j6\" ]\nstm32g041k6 = [ \"stm32-metapac/stm32g041k6\" ]\nstm32g041k8 = [ \"stm32-metapac/stm32g041k8\" ]\nstm32g041y8 = [ \"stm32-metapac/stm32g041y8\" ]\nstm32g050c6 = [ \"stm32-metapac/stm32g050c6\" ]\nstm32g050c8 = [ \"stm32-metapac/stm32g050c8\" ]\nstm32g050f6 = [ \"stm32-metapac/stm32g050f6\" ]\nstm32g050k6 = [ \"stm32-metapac/stm32g050k6\" ]\nstm32g050k8 = [ \"stm32-metapac/stm32g050k8\" ]\nstm32g051c6 = [ \"stm32-metapac/stm32g051c6\" ]\nstm32g051c8 = [ \"stm32-metapac/stm32g051c8\" ]\nstm32g051f6 = [ \"stm32-metapac/stm32g051f6\" ]\nstm32g051f8 = [ \"stm32-metapac/stm32g051f8\" ]\nstm32g051g6 = [ \"stm32-metapac/stm32g051g6\" ]\nstm32g051g8 = [ \"stm32-metapac/stm32g051g8\" ]\nstm32g051k6 = [ \"stm32-metapac/stm32g051k6\" ]\nstm32g051k8 = [ \"stm32-metapac/stm32g051k8\" ]\nstm32g061c6 = [ \"stm32-metapac/stm32g061c6\" ]\nstm32g061c8 = [ \"stm32-metapac/stm32g061c8\" ]\nstm32g061f6 = [ \"stm32-metapac/stm32g061f6\" ]\nstm32g061f8 = [ \"stm32-metapac/stm32g061f8\" ]\nstm32g061g6 = [ \"stm32-metapac/stm32g061g6\" ]\nstm32g061g8 = [ \"stm32-metapac/stm32g061g8\" ]\nstm32g061k6 = [ \"stm32-metapac/stm32g061k6\" ]\nstm32g061k8 = [ \"stm32-metapac/stm32g061k8\" ]\nstm32g070cb = [ \"stm32-metapac/stm32g070cb\" ]\nstm32g070kb = [ \"stm32-metapac/stm32g070kb\" ]\nstm32g070rb = [ \"stm32-metapac/stm32g070rb\" ]\nstm32g071c6 = [ \"stm32-metapac/stm32g071c6\" ]\nstm32g071c8 = [ \"stm32-metapac/stm32g071c8\" ]\nstm32g071cb = [ \"stm32-metapac/stm32g071cb\" ]\nstm32g071eb = [ \"stm32-metapac/stm32g071eb\" ]\nstm32g071g6 = [ \"stm32-metapac/stm32g071g6\" ]\nstm32g071g8 = [ \"stm32-metapac/stm32g071g8\" ]\nstm32g071gb = [ \"stm32-metapac/stm32g071gb\" ]\nstm32g071k6 = [ \"stm32-metapac/stm32g071k6\" ]\nstm32g071k8 = [ \"stm32-metapac/stm32g071k8\" ]\nstm32g071kb = [ \"stm32-metapac/stm32g071kb\" ]\nstm32g071r6 = [ \"stm32-metapac/stm32g071r6\" ]\nstm32g071r8 = [ \"stm32-metapac/stm32g071r8\" ]\nstm32g071rb = [ \"stm32-metapac/stm32g071rb\" ]\nstm32g081cb = [ \"stm32-metapac/stm32g081cb\" ]\nstm32g081eb = [ \"stm32-metapac/stm32g081eb\" ]\nstm32g081gb = [ \"stm32-metapac/stm32g081gb\" ]\nstm32g081kb = [ \"stm32-metapac/stm32g081kb\" ]\nstm32g081rb = [ \"stm32-metapac/stm32g081rb\" ]\nstm32g0b0ce = [ \"stm32-metapac/stm32g0b0ce\" ]\nstm32g0b0ke = [ \"stm32-metapac/stm32g0b0ke\" ]\nstm32g0b0re = [ \"stm32-metapac/stm32g0b0re\" ]\nstm32g0b0ve = [ \"stm32-metapac/stm32g0b0ve\" ]\nstm32g0b1cb = [ \"stm32-metapac/stm32g0b1cb\" ]\nstm32g0b1cc = [ \"stm32-metapac/stm32g0b1cc\" ]\nstm32g0b1ce = [ \"stm32-metapac/stm32g0b1ce\" ]\nstm32g0b1kb = [ \"stm32-metapac/stm32g0b1kb\" ]\nstm32g0b1kc = [ \"stm32-metapac/stm32g0b1kc\" ]\nstm32g0b1ke = [ \"stm32-metapac/stm32g0b1ke\" ]\nstm32g0b1mb = [ \"stm32-metapac/stm32g0b1mb\" ]\nstm32g0b1mc = [ \"stm32-metapac/stm32g0b1mc\" ]\nstm32g0b1me = [ \"stm32-metapac/stm32g0b1me\" ]\nstm32g0b1ne = [ \"stm32-metapac/stm32g0b1ne\" ]\nstm32g0b1rb = [ \"stm32-metapac/stm32g0b1rb\" ]\nstm32g0b1rc = [ \"stm32-metapac/stm32g0b1rc\" ]\nstm32g0b1re = [ \"stm32-metapac/stm32g0b1re\" ]\nstm32g0b1vb = [ \"stm32-metapac/stm32g0b1vb\" ]\nstm32g0b1vc = [ \"stm32-metapac/stm32g0b1vc\" ]\nstm32g0b1ve = [ \"stm32-metapac/stm32g0b1ve\" ]\nstm32g0c1cc = [ \"stm32-metapac/stm32g0c1cc\" ]\nstm32g0c1ce = [ \"stm32-metapac/stm32g0c1ce\" ]\nstm32g0c1kc = [ \"stm32-metapac/stm32g0c1kc\" ]\nstm32g0c1ke = [ \"stm32-metapac/stm32g0c1ke\" ]\nstm32g0c1mc = [ \"stm32-metapac/stm32g0c1mc\" ]\nstm32g0c1me = [ \"stm32-metapac/stm32g0c1me\" ]\nstm32g0c1ne = [ \"stm32-metapac/stm32g0c1ne\" ]\nstm32g0c1rc = [ \"stm32-metapac/stm32g0c1rc\" ]\nstm32g0c1re = [ \"stm32-metapac/stm32g0c1re\" ]\nstm32g0c1vc = [ \"stm32-metapac/stm32g0c1vc\" ]\nstm32g0c1ve = [ \"stm32-metapac/stm32g0c1ve\" ]\nstm32g431c6 = [ \"stm32-metapac/stm32g431c6\" ]\nstm32g431c8 = [ \"stm32-metapac/stm32g431c8\" ]\nstm32g431cb = [ \"stm32-metapac/stm32g431cb\" ]\nstm32g431k6 = [ \"stm32-metapac/stm32g431k6\" ]\nstm32g431k8 = [ \"stm32-metapac/stm32g431k8\" ]\nstm32g431kb = [ \"stm32-metapac/stm32g431kb\" ]\nstm32g431m6 = [ \"stm32-metapac/stm32g431m6\" ]\nstm32g431m8 = [ \"stm32-metapac/stm32g431m8\" ]\nstm32g431mb = [ \"stm32-metapac/stm32g431mb\" ]\nstm32g431r6 = [ \"stm32-metapac/stm32g431r6\" ]\nstm32g431r8 = [ \"stm32-metapac/stm32g431r8\" ]\nstm32g431rb = [ \"stm32-metapac/stm32g431rb\" ]\nstm32g431v6 = [ \"stm32-metapac/stm32g431v6\" ]\nstm32g431v8 = [ \"stm32-metapac/stm32g431v8\" ]\nstm32g431vb = [ \"stm32-metapac/stm32g431vb\" ]\nstm32g441cb = [ \"stm32-metapac/stm32g441cb\" ]\nstm32g441kb = [ \"stm32-metapac/stm32g441kb\" ]\nstm32g441mb = [ \"stm32-metapac/stm32g441mb\" ]\nstm32g441rb = [ \"stm32-metapac/stm32g441rb\" ]\nstm32g441vb = [ \"stm32-metapac/stm32g441vb\" ]\nstm32g471cc = [ \"stm32-metapac/stm32g471cc\" ]\nstm32g471ce = [ \"stm32-metapac/stm32g471ce\" ]\nstm32g471mc = [ \"stm32-metapac/stm32g471mc\" ]\nstm32g471me = [ \"stm32-metapac/stm32g471me\" ]\nstm32g471qc = [ \"stm32-metapac/stm32g471qc\" ]\nstm32g471qe = [ \"stm32-metapac/stm32g471qe\" ]\nstm32g471rc = [ \"stm32-metapac/stm32g471rc\" ]\nstm32g471re = [ \"stm32-metapac/stm32g471re\" ]\nstm32g471vc = [ \"stm32-metapac/stm32g471vc\" ]\nstm32g471ve = [ \"stm32-metapac/stm32g471ve\" ]\nstm32g473cb = [ \"stm32-metapac/stm32g473cb\" ]\nstm32g473cc = [ \"stm32-metapac/stm32g473cc\" ]\nstm32g473ce = [ \"stm32-metapac/stm32g473ce\" ]\nstm32g473mb = [ \"stm32-metapac/stm32g473mb\" ]\nstm32g473mc = [ \"stm32-metapac/stm32g473mc\" ]\nstm32g473me = [ \"stm32-metapac/stm32g473me\" ]\nstm32g473pb = [ \"stm32-metapac/stm32g473pb\" ]\nstm32g473pc = [ \"stm32-metapac/stm32g473pc\" ]\nstm32g473pe = [ \"stm32-metapac/stm32g473pe\" ]\nstm32g473qb = [ \"stm32-metapac/stm32g473qb\" ]\nstm32g473qc = [ \"stm32-metapac/stm32g473qc\" ]\nstm32g473qe = [ \"stm32-metapac/stm32g473qe\" ]\nstm32g473rb = [ \"stm32-metapac/stm32g473rb\" ]\nstm32g473rc = [ \"stm32-metapac/stm32g473rc\" ]\nstm32g473re = [ \"stm32-metapac/stm32g473re\" ]\nstm32g473vb = [ \"stm32-metapac/stm32g473vb\" ]\nstm32g473vc = [ \"stm32-metapac/stm32g473vc\" ]\nstm32g473ve = [ \"stm32-metapac/stm32g473ve\" ]\nstm32g474cb = [ \"stm32-metapac/stm32g474cb\", \"stm32-hrtim\", \"stm32-hrtim/stm32g474\" ]\nstm32g474cc = [ \"stm32-metapac/stm32g474cc\", \"stm32-hrtim\", \"stm32-hrtim/stm32g474\" ]\nstm32g474ce = [ \"stm32-metapac/stm32g474ce\", \"stm32-hrtim\", \"stm32-hrtim/stm32g474\" ]\nstm32g474mb = [ \"stm32-metapac/stm32g474mb\", \"stm32-hrtim\", \"stm32-hrtim/stm32g474\" ]\nstm32g474mc = [ \"stm32-metapac/stm32g474mc\", \"stm32-hrtim\", \"stm32-hrtim/stm32g474\" ]\nstm32g474me = [ \"stm32-metapac/stm32g474me\", \"stm32-hrtim\", \"stm32-hrtim/stm32g474\" ]\nstm32g474pb = [ \"stm32-metapac/stm32g474pb\", \"stm32-hrtim\", \"stm32-hrtim/stm32g474\" ]\nstm32g474pc = [ \"stm32-metapac/stm32g474pc\", \"stm32-hrtim\", \"stm32-hrtim/stm32g474\" ]\nstm32g474pe = [ \"stm32-metapac/stm32g474pe\", \"stm32-hrtim\", \"stm32-hrtim/stm32g474\" ]\nstm32g474qb = [ \"stm32-metapac/stm32g474qb\", \"stm32-hrtim\", \"stm32-hrtim/stm32g474\" ]\nstm32g474qc = [ \"stm32-metapac/stm32g474qc\", \"stm32-hrtim\", \"stm32-hrtim/stm32g474\" ]\nstm32g474qe = [ \"stm32-metapac/stm32g474qe\", \"stm32-hrtim\", \"stm32-hrtim/stm32g474\" ]\nstm32g474rb = [ \"stm32-metapac/stm32g474rb\", \"stm32-hrtim\", \"stm32-hrtim/stm32g474\" ]\nstm32g474rc = [ \"stm32-metapac/stm32g474rc\", \"stm32-hrtim\", \"stm32-hrtim/stm32g474\" ]\nstm32g474re = [ \"stm32-metapac/stm32g474re\", \"stm32-hrtim\", \"stm32-hrtim/stm32g474\" ]\nstm32g474vb = [ \"stm32-metapac/stm32g474vb\", \"stm32-hrtim\", \"stm32-hrtim/stm32g474\" ]\nstm32g474vc = [ \"stm32-metapac/stm32g474vc\", \"stm32-hrtim\", \"stm32-hrtim/stm32g474\" ]\nstm32g474ve = [ \"stm32-metapac/stm32g474ve\", \"stm32-hrtim\", \"stm32-hrtim/stm32g474\" ]\nstm32g483ce = [ \"stm32-metapac/stm32g483ce\" ]\nstm32g483me = [ \"stm32-metapac/stm32g483me\" ]\nstm32g483pe = [ \"stm32-metapac/stm32g483pe\" ]\nstm32g483qe = [ \"stm32-metapac/stm32g483qe\" ]\nstm32g483re = [ \"stm32-metapac/stm32g483re\" ]\nstm32g483ve = [ \"stm32-metapac/stm32g483ve\" ]\nstm32g484ce = [ \"stm32-metapac/stm32g484ce\", \"stm32-hrtim\", \"stm32-hrtim/stm32g484\" ]\nstm32g484me = [ \"stm32-metapac/stm32g484me\", \"stm32-hrtim\", \"stm32-hrtim/stm32g484\" ]\nstm32g484pe = [ \"stm32-metapac/stm32g484pe\", \"stm32-hrtim\", \"stm32-hrtim/stm32g484\" ]\nstm32g484qe = [ \"stm32-metapac/stm32g484qe\", \"stm32-hrtim\", \"stm32-hrtim/stm32g484\" ]\nstm32g484re = [ \"stm32-metapac/stm32g484re\", \"stm32-hrtim\", \"stm32-hrtim/stm32g484\" ]\nstm32g484ve = [ \"stm32-metapac/stm32g484ve\", \"stm32-hrtim\", \"stm32-hrtim/stm32g484\" ]\nstm32g491cc = [ \"stm32-metapac/stm32g491cc\" ]\nstm32g491ce = [ \"stm32-metapac/stm32g491ce\" ]\nstm32g491kc = [ \"stm32-metapac/stm32g491kc\" ]\nstm32g491ke = [ \"stm32-metapac/stm32g491ke\" ]\nstm32g491mc = [ \"stm32-metapac/stm32g491mc\" ]\nstm32g491me = [ \"stm32-metapac/stm32g491me\" ]\nstm32g491rc = [ \"stm32-metapac/stm32g491rc\" ]\nstm32g491re = [ \"stm32-metapac/stm32g491re\" ]\nstm32g491vc = [ \"stm32-metapac/stm32g491vc\" ]\nstm32g491ve = [ \"stm32-metapac/stm32g491ve\" ]\nstm32g4a1ce = [ \"stm32-metapac/stm32g4a1ce\" ]\nstm32g4a1ke = [ \"stm32-metapac/stm32g4a1ke\" ]\nstm32g4a1me = [ \"stm32-metapac/stm32g4a1me\" ]\nstm32g4a1re = [ \"stm32-metapac/stm32g4a1re\" ]\nstm32g4a1ve = [ \"stm32-metapac/stm32g4a1ve\" ]\nstm32h503cb = [ \"stm32-metapac/stm32h503cb\" ]\nstm32h503eb = [ \"stm32-metapac/stm32h503eb\" ]\nstm32h503kb = [ \"stm32-metapac/stm32h503kb\" ]\nstm32h503rb = [ \"stm32-metapac/stm32h503rb\" ]\nstm32h523cc = [ \"stm32-metapac/stm32h523cc\" ]\nstm32h523ce = [ \"stm32-metapac/stm32h523ce\" ]\nstm32h523he = [ \"stm32-metapac/stm32h523he\" ]\nstm32h523rc = [ \"stm32-metapac/stm32h523rc\" ]\nstm32h523re = [ \"stm32-metapac/stm32h523re\" ]\nstm32h523vc = [ \"stm32-metapac/stm32h523vc\" ]\nstm32h523ve = [ \"stm32-metapac/stm32h523ve\" ]\nstm32h523zc = [ \"stm32-metapac/stm32h523zc\" ]\nstm32h523ze = [ \"stm32-metapac/stm32h523ze\" ]\nstm32h533ce = [ \"stm32-metapac/stm32h533ce\" ]\nstm32h533he = [ \"stm32-metapac/stm32h533he\" ]\nstm32h533re = [ \"stm32-metapac/stm32h533re\" ]\nstm32h533ve = [ \"stm32-metapac/stm32h533ve\" ]\nstm32h533ze = [ \"stm32-metapac/stm32h533ze\" ]\nstm32h562ag = [ \"stm32-metapac/stm32h562ag\" ]\nstm32h562ai = [ \"stm32-metapac/stm32h562ai\" ]\nstm32h562ig = [ \"stm32-metapac/stm32h562ig\" ]\nstm32h562ii = [ \"stm32-metapac/stm32h562ii\" ]\nstm32h562rg = [ \"stm32-metapac/stm32h562rg\" ]\nstm32h562ri = [ \"stm32-metapac/stm32h562ri\" ]\nstm32h562vg = [ \"stm32-metapac/stm32h562vg\" ]\nstm32h562vi = [ \"stm32-metapac/stm32h562vi\" ]\nstm32h562zg = [ \"stm32-metapac/stm32h562zg\" ]\nstm32h562zi = [ \"stm32-metapac/stm32h562zi\" ]\nstm32h563ag = [ \"stm32-metapac/stm32h563ag\" ]\nstm32h563ai = [ \"stm32-metapac/stm32h563ai\" ]\nstm32h563ig = [ \"stm32-metapac/stm32h563ig\" ]\nstm32h563ii = [ \"stm32-metapac/stm32h563ii\" ]\nstm32h563mi = [ \"stm32-metapac/stm32h563mi\" ]\nstm32h563rg = [ \"stm32-metapac/stm32h563rg\" ]\nstm32h563ri = [ \"stm32-metapac/stm32h563ri\" ]\nstm32h563vg = [ \"stm32-metapac/stm32h563vg\" ]\nstm32h563vi = [ \"stm32-metapac/stm32h563vi\" ]\nstm32h563zg = [ \"stm32-metapac/stm32h563zg\" ]\nstm32h563zi = [ \"stm32-metapac/stm32h563zi\" ]\nstm32h573ai = [ \"stm32-metapac/stm32h573ai\" ]\nstm32h573ii = [ \"stm32-metapac/stm32h573ii\" ]\nstm32h573mi = [ \"stm32-metapac/stm32h573mi\" ]\nstm32h573ri = [ \"stm32-metapac/stm32h573ri\" ]\nstm32h573vi = [ \"stm32-metapac/stm32h573vi\" ]\nstm32h573zi = [ \"stm32-metapac/stm32h573zi\" ]\nstm32h723ve = [ \"stm32-metapac/stm32h723ve\" ]\nstm32h723vg = [ \"stm32-metapac/stm32h723vg\" ]\nstm32h723ze = [ \"stm32-metapac/stm32h723ze\" ]\nstm32h723zg = [ \"stm32-metapac/stm32h723zg\" ]\nstm32h725ae = [ \"stm32-metapac/stm32h725ae\" ]\nstm32h725ag = [ \"stm32-metapac/stm32h725ag\" ]\nstm32h725ie = [ \"stm32-metapac/stm32h725ie\" ]\nstm32h725ig = [ \"stm32-metapac/stm32h725ig\" ]\nstm32h725re = [ \"stm32-metapac/stm32h725re\" ]\nstm32h725rg = [ \"stm32-metapac/stm32h725rg\" ]\nstm32h725ve = [ \"stm32-metapac/stm32h725ve\" ]\nstm32h725vg = [ \"stm32-metapac/stm32h725vg\" ]\nstm32h725ze = [ \"stm32-metapac/stm32h725ze\" ]\nstm32h725zg = [ \"stm32-metapac/stm32h725zg\" ]\nstm32h730ab = [ \"stm32-metapac/stm32h730ab\" ]\nstm32h730ib = [ \"stm32-metapac/stm32h730ib\" ]\nstm32h730vb = [ \"stm32-metapac/stm32h730vb\" ]\nstm32h730zb = [ \"stm32-metapac/stm32h730zb\" ]\nstm32h733vg = [ \"stm32-metapac/stm32h733vg\" ]\nstm32h733zg = [ \"stm32-metapac/stm32h733zg\" ]\nstm32h735ag = [ \"stm32-metapac/stm32h735ag\" ]\nstm32h735ig = [ \"stm32-metapac/stm32h735ig\" ]\nstm32h735rg = [ \"stm32-metapac/stm32h735rg\" ]\nstm32h735vg = [ \"stm32-metapac/stm32h735vg\" ]\nstm32h735zg = [ \"stm32-metapac/stm32h735zg\" ]\nstm32h742ag = [ \"stm32-metapac/stm32h742ag\" ]\nstm32h742ai = [ \"stm32-metapac/stm32h742ai\" ]\nstm32h742bg = [ \"stm32-metapac/stm32h742bg\" ]\nstm32h742bi = [ \"stm32-metapac/stm32h742bi\" ]\nstm32h742ig = [ \"stm32-metapac/stm32h742ig\" ]\nstm32h742ii = [ \"stm32-metapac/stm32h742ii\" ]\nstm32h742vg = [ \"stm32-metapac/stm32h742vg\" ]\nstm32h742vi = [ \"stm32-metapac/stm32h742vi\" ]\nstm32h742xg = [ \"stm32-metapac/stm32h742xg\" ]\nstm32h742xi = [ \"stm32-metapac/stm32h742xi\" ]\nstm32h742zg = [ \"stm32-metapac/stm32h742zg\" ]\nstm32h742zi = [ \"stm32-metapac/stm32h742zi\" ]\nstm32h743ag = [ \"stm32-metapac/stm32h743ag\" ]\nstm32h743ai = [ \"stm32-metapac/stm32h743ai\" ]\nstm32h743bg = [ \"stm32-metapac/stm32h743bg\" ]\nstm32h743bi = [ \"stm32-metapac/stm32h743bi\" ]\nstm32h743ig = [ \"stm32-metapac/stm32h743ig\" ]\nstm32h743ii = [ \"stm32-metapac/stm32h743ii\" ]\nstm32h743vg = [ \"stm32-metapac/stm32h743vg\" ]\nstm32h743vi = [ \"stm32-metapac/stm32h743vi\" ]\nstm32h743xg = [ \"stm32-metapac/stm32h743xg\" ]\nstm32h743xi = [ \"stm32-metapac/stm32h743xi\" ]\nstm32h743zg = [ \"stm32-metapac/stm32h743zg\" ]\nstm32h743zi = [ \"stm32-metapac/stm32h743zi\" ]\nstm32h745bg-cm7 = [ \"stm32-metapac/stm32h745bg-cm7\", \"_dual-core\", \"_core-cm7\" ]\nstm32h745bg-cm4 = [ \"stm32-metapac/stm32h745bg-cm4\", \"_dual-core\", \"_core-cm4\" ]\nstm32h745bi-cm7 = [ \"stm32-metapac/stm32h745bi-cm7\", \"_dual-core\", \"_core-cm7\" ]\nstm32h745bi-cm4 = [ \"stm32-metapac/stm32h745bi-cm4\", \"_dual-core\", \"_core-cm4\" ]\nstm32h745ig-cm7 = [ \"stm32-metapac/stm32h745ig-cm7\", \"_dual-core\", \"_core-cm7\" ]\nstm32h745ig-cm4 = [ \"stm32-metapac/stm32h745ig-cm4\", \"_dual-core\", \"_core-cm4\" ]\nstm32h745ii-cm7 = [ \"stm32-metapac/stm32h745ii-cm7\", \"_dual-core\", \"_core-cm7\" ]\nstm32h745ii-cm4 = [ \"stm32-metapac/stm32h745ii-cm4\", \"_dual-core\", \"_core-cm4\" ]\nstm32h745xg-cm7 = [ \"stm32-metapac/stm32h745xg-cm7\", \"_dual-core\", \"_core-cm7\" ]\nstm32h745xg-cm4 = [ \"stm32-metapac/stm32h745xg-cm4\", \"_dual-core\", \"_core-cm4\" ]\nstm32h745xi-cm7 = [ \"stm32-metapac/stm32h745xi-cm7\", \"_dual-core\", \"_core-cm7\" ]\nstm32h745xi-cm4 = [ \"stm32-metapac/stm32h745xi-cm4\", \"_dual-core\", \"_core-cm4\" ]\nstm32h745zg-cm7 = [ \"stm32-metapac/stm32h745zg-cm7\", \"_dual-core\", \"_core-cm7\" ]\nstm32h745zg-cm4 = [ \"stm32-metapac/stm32h745zg-cm4\", \"_dual-core\", \"_core-cm4\" ]\nstm32h745zi-cm7 = [ \"stm32-metapac/stm32h745zi-cm7\", \"_dual-core\", \"_core-cm7\" ]\nstm32h745zi-cm4 = [ \"stm32-metapac/stm32h745zi-cm4\", \"_dual-core\", \"_core-cm4\" ]\nstm32h747ag-cm7 = [ \"stm32-metapac/stm32h747ag-cm7\", \"_dual-core\", \"_core-cm7\" ]\nstm32h747ag-cm4 = [ \"stm32-metapac/stm32h747ag-cm4\", \"_dual-core\", \"_core-cm4\" ]\nstm32h747ai-cm7 = [ \"stm32-metapac/stm32h747ai-cm7\", \"_dual-core\", \"_core-cm7\" ]\nstm32h747ai-cm4 = [ \"stm32-metapac/stm32h747ai-cm4\", \"_dual-core\", \"_core-cm4\" ]\nstm32h747bg-cm7 = [ \"stm32-metapac/stm32h747bg-cm7\", \"_dual-core\", \"_core-cm7\" ]\nstm32h747bg-cm4 = [ \"stm32-metapac/stm32h747bg-cm4\", \"_dual-core\", \"_core-cm4\" ]\nstm32h747bi-cm7 = [ \"stm32-metapac/stm32h747bi-cm7\", \"_dual-core\", \"_core-cm7\" ]\nstm32h747bi-cm4 = [ \"stm32-metapac/stm32h747bi-cm4\", \"_dual-core\", \"_core-cm4\" ]\nstm32h747ig-cm7 = [ \"stm32-metapac/stm32h747ig-cm7\", \"_dual-core\", \"_core-cm7\" ]\nstm32h747ig-cm4 = [ \"stm32-metapac/stm32h747ig-cm4\", \"_dual-core\", \"_core-cm4\" ]\nstm32h747ii-cm7 = [ \"stm32-metapac/stm32h747ii-cm7\", \"_dual-core\", \"_core-cm7\" ]\nstm32h747ii-cm4 = [ \"stm32-metapac/stm32h747ii-cm4\", \"_dual-core\", \"_core-cm4\" ]\nstm32h747xg-cm7 = [ \"stm32-metapac/stm32h747xg-cm7\", \"_dual-core\", \"_core-cm7\" ]\nstm32h747xg-cm4 = [ \"stm32-metapac/stm32h747xg-cm4\", \"_dual-core\", \"_core-cm4\" ]\nstm32h747xi-cm7 = [ \"stm32-metapac/stm32h747xi-cm7\", \"_dual-core\", \"_core-cm7\" ]\nstm32h747xi-cm4 = [ \"stm32-metapac/stm32h747xi-cm4\", \"_dual-core\", \"_core-cm4\" ]\nstm32h747zi-cm7 = [ \"stm32-metapac/stm32h747zi-cm7\", \"_dual-core\", \"_core-cm7\" ]\nstm32h747zi-cm4 = [ \"stm32-metapac/stm32h747zi-cm4\", \"_dual-core\", \"_core-cm4\" ]\nstm32h750ib = [ \"stm32-metapac/stm32h750ib\" ]\nstm32h750vb = [ \"stm32-metapac/stm32h750vb\" ]\nstm32h750xb = [ \"stm32-metapac/stm32h750xb\" ]\nstm32h750zb = [ \"stm32-metapac/stm32h750zb\" ]\nstm32h753ai = [ \"stm32-metapac/stm32h753ai\" ]\nstm32h753bi = [ \"stm32-metapac/stm32h753bi\" ]\nstm32h753ii = [ \"stm32-metapac/stm32h753ii\" ]\nstm32h753vi = [ \"stm32-metapac/stm32h753vi\" ]\nstm32h753xi = [ \"stm32-metapac/stm32h753xi\" ]\nstm32h753zi = [ \"stm32-metapac/stm32h753zi\" ]\nstm32h755bi-cm7 = [ \"stm32-metapac/stm32h755bi-cm7\", \"_dual-core\", \"_core-cm7\" ]\nstm32h755bi-cm4 = [ \"stm32-metapac/stm32h755bi-cm4\", \"_dual-core\", \"_core-cm4\" ]\nstm32h755ii-cm7 = [ \"stm32-metapac/stm32h755ii-cm7\", \"_dual-core\", \"_core-cm7\" ]\nstm32h755ii-cm4 = [ \"stm32-metapac/stm32h755ii-cm4\", \"_dual-core\", \"_core-cm4\" ]\nstm32h755xi-cm7 = [ \"stm32-metapac/stm32h755xi-cm7\", \"_dual-core\", \"_core-cm7\" ]\nstm32h755xi-cm4 = [ \"stm32-metapac/stm32h755xi-cm4\", \"_dual-core\", \"_core-cm4\" ]\nstm32h755zi-cm7 = [ \"stm32-metapac/stm32h755zi-cm7\", \"_dual-core\", \"_core-cm7\" ]\nstm32h755zi-cm4 = [ \"stm32-metapac/stm32h755zi-cm4\", \"_dual-core\", \"_core-cm4\" ]\nstm32h757ai-cm7 = [ \"stm32-metapac/stm32h757ai-cm7\", \"_dual-core\", \"_core-cm7\" ]\nstm32h757ai-cm4 = [ \"stm32-metapac/stm32h757ai-cm4\", \"_dual-core\", \"_core-cm4\" ]\nstm32h757bi-cm7 = [ \"stm32-metapac/stm32h757bi-cm7\", \"_dual-core\", \"_core-cm7\" ]\nstm32h757bi-cm4 = [ \"stm32-metapac/stm32h757bi-cm4\", \"_dual-core\", \"_core-cm4\" ]\nstm32h757ii-cm7 = [ \"stm32-metapac/stm32h757ii-cm7\", \"_dual-core\", \"_core-cm7\" ]\nstm32h757ii-cm4 = [ \"stm32-metapac/stm32h757ii-cm4\", \"_dual-core\", \"_core-cm4\" ]\nstm32h757xi-cm7 = [ \"stm32-metapac/stm32h757xi-cm7\", \"_dual-core\", \"_core-cm7\" ]\nstm32h757xi-cm4 = [ \"stm32-metapac/stm32h757xi-cm4\", \"_dual-core\", \"_core-cm4\" ]\nstm32h757zi-cm7 = [ \"stm32-metapac/stm32h757zi-cm7\", \"_dual-core\", \"_core-cm7\" ]\nstm32h757zi-cm4 = [ \"stm32-metapac/stm32h757zi-cm4\", \"_dual-core\", \"_core-cm4\" ]\nstm32h7a3ag = [ \"stm32-metapac/stm32h7a3ag\" ]\nstm32h7a3ai = [ \"stm32-metapac/stm32h7a3ai\" ]\nstm32h7a3ig = [ \"stm32-metapac/stm32h7a3ig\" ]\nstm32h7a3ii = [ \"stm32-metapac/stm32h7a3ii\" ]\nstm32h7a3lg = [ \"stm32-metapac/stm32h7a3lg\" ]\nstm32h7a3li = [ \"stm32-metapac/stm32h7a3li\" ]\nstm32h7a3ng = [ \"stm32-metapac/stm32h7a3ng\" ]\nstm32h7a3ni = [ \"stm32-metapac/stm32h7a3ni\" ]\nstm32h7a3qi = [ \"stm32-metapac/stm32h7a3qi\" ]\nstm32h7a3rg = [ \"stm32-metapac/stm32h7a3rg\" ]\nstm32h7a3ri = [ \"stm32-metapac/stm32h7a3ri\" ]\nstm32h7a3vg = [ \"stm32-metapac/stm32h7a3vg\" ]\nstm32h7a3vi = [ \"stm32-metapac/stm32h7a3vi\" ]\nstm32h7a3zg = [ \"stm32-metapac/stm32h7a3zg\" ]\nstm32h7a3zi = [ \"stm32-metapac/stm32h7a3zi\" ]\nstm32h7b0ab = [ \"stm32-metapac/stm32h7b0ab\" ]\nstm32h7b0ib = [ \"stm32-metapac/stm32h7b0ib\" ]\nstm32h7b0rb = [ \"stm32-metapac/stm32h7b0rb\" ]\nstm32h7b0vb = [ \"stm32-metapac/stm32h7b0vb\" ]\nstm32h7b0zb = [ \"stm32-metapac/stm32h7b0zb\" ]\nstm32h7b3ai = [ \"stm32-metapac/stm32h7b3ai\" ]\nstm32h7b3ii = [ \"stm32-metapac/stm32h7b3ii\" ]\nstm32h7b3li = [ \"stm32-metapac/stm32h7b3li\" ]\nstm32h7b3ni = [ \"stm32-metapac/stm32h7b3ni\" ]\nstm32h7b3qi = [ \"stm32-metapac/stm32h7b3qi\" ]\nstm32h7b3ri = [ \"stm32-metapac/stm32h7b3ri\" ]\nstm32h7b3vi = [ \"stm32-metapac/stm32h7b3vi\" ]\nstm32h7b3zi = [ \"stm32-metapac/stm32h7b3zi\" ]\nstm32h7r3a8 = [ \"stm32-metapac/stm32h7r3a8\" ]\nstm32h7r3i8 = [ \"stm32-metapac/stm32h7r3i8\" ]\nstm32h7r3l8 = [ \"stm32-metapac/stm32h7r3l8\" ]\nstm32h7r3r8 = [ \"stm32-metapac/stm32h7r3r8\" ]\nstm32h7r3v8 = [ \"stm32-metapac/stm32h7r3v8\" ]\nstm32h7r3z8 = [ \"stm32-metapac/stm32h7r3z8\" ]\nstm32h7r7a8 = [ \"stm32-metapac/stm32h7r7a8\" ]\nstm32h7r7i8 = [ \"stm32-metapac/stm32h7r7i8\" ]\nstm32h7r7l8 = [ \"stm32-metapac/stm32h7r7l8\" ]\nstm32h7r7z8 = [ \"stm32-metapac/stm32h7r7z8\" ]\nstm32h7s3a8 = [ \"stm32-metapac/stm32h7s3a8\" ]\nstm32h7s3i8 = [ \"stm32-metapac/stm32h7s3i8\" ]\nstm32h7s3l8 = [ \"stm32-metapac/stm32h7s3l8\" ]\nstm32h7s3r8 = [ \"stm32-metapac/stm32h7s3r8\" ]\nstm32h7s3v8 = [ \"stm32-metapac/stm32h7s3v8\" ]\nstm32h7s3z8 = [ \"stm32-metapac/stm32h7s3z8\" ]\nstm32h7s7a8 = [ \"stm32-metapac/stm32h7s7a8\" ]\nstm32h7s7i8 = [ \"stm32-metapac/stm32h7s7i8\" ]\nstm32h7s7l8 = [ \"stm32-metapac/stm32h7s7l8\" ]\nstm32h7s7z8 = [ \"stm32-metapac/stm32h7s7z8\" ]\nstm32l010c6 = [ \"stm32-metapac/stm32l010c6\" ]\nstm32l010f4 = [ \"stm32-metapac/stm32l010f4\" ]\nstm32l010k4 = [ \"stm32-metapac/stm32l010k4\" ]\nstm32l010k8 = [ \"stm32-metapac/stm32l010k8\" ]\nstm32l010r8 = [ \"stm32-metapac/stm32l010r8\" ]\nstm32l010rb = [ \"stm32-metapac/stm32l010rb\" ]\nstm32l011d3 = [ \"stm32-metapac/stm32l011d3\" ]\nstm32l011d4 = [ \"stm32-metapac/stm32l011d4\" ]\nstm32l011e3 = [ \"stm32-metapac/stm32l011e3\" ]\nstm32l011e4 = [ \"stm32-metapac/stm32l011e4\" ]\nstm32l011f3 = [ \"stm32-metapac/stm32l011f3\" ]\nstm32l011f4 = [ \"stm32-metapac/stm32l011f4\" ]\nstm32l011g3 = [ \"stm32-metapac/stm32l011g3\" ]\nstm32l011g4 = [ \"stm32-metapac/stm32l011g4\" ]\nstm32l011k3 = [ \"stm32-metapac/stm32l011k3\" ]\nstm32l011k4 = [ \"stm32-metapac/stm32l011k4\" ]\nstm32l021d4 = [ \"stm32-metapac/stm32l021d4\" ]\nstm32l021f4 = [ \"stm32-metapac/stm32l021f4\" ]\nstm32l021g4 = [ \"stm32-metapac/stm32l021g4\" ]\nstm32l021k4 = [ \"stm32-metapac/stm32l021k4\" ]\nstm32l031c4 = [ \"stm32-metapac/stm32l031c4\" ]\nstm32l031c6 = [ \"stm32-metapac/stm32l031c6\" ]\nstm32l031e4 = [ \"stm32-metapac/stm32l031e4\" ]\nstm32l031e6 = [ \"stm32-metapac/stm32l031e6\" ]\nstm32l031f4 = [ \"stm32-metapac/stm32l031f4\" ]\nstm32l031f6 = [ \"stm32-metapac/stm32l031f6\" ]\nstm32l031g4 = [ \"stm32-metapac/stm32l031g4\" ]\nstm32l031g6 = [ \"stm32-metapac/stm32l031g6\" ]\nstm32l031k4 = [ \"stm32-metapac/stm32l031k4\" ]\nstm32l031k6 = [ \"stm32-metapac/stm32l031k6\" ]\nstm32l041c4 = [ \"stm32-metapac/stm32l041c4\" ]\nstm32l041c6 = [ \"stm32-metapac/stm32l041c6\" ]\nstm32l041e6 = [ \"stm32-metapac/stm32l041e6\" ]\nstm32l041f6 = [ \"stm32-metapac/stm32l041f6\" ]\nstm32l041g6 = [ \"stm32-metapac/stm32l041g6\" ]\nstm32l041k6 = [ \"stm32-metapac/stm32l041k6\" ]\nstm32l051c6 = [ \"stm32-metapac/stm32l051c6\" ]\nstm32l051c8 = [ \"stm32-metapac/stm32l051c8\" ]\nstm32l051k6 = [ \"stm32-metapac/stm32l051k6\" ]\nstm32l051k8 = [ \"stm32-metapac/stm32l051k8\" ]\nstm32l051r6 = [ \"stm32-metapac/stm32l051r6\" ]\nstm32l051r8 = [ \"stm32-metapac/stm32l051r8\" ]\nstm32l051t6 = [ \"stm32-metapac/stm32l051t6\" ]\nstm32l051t8 = [ \"stm32-metapac/stm32l051t8\" ]\nstm32l052c6 = [ \"stm32-metapac/stm32l052c6\" ]\nstm32l052c8 = [ \"stm32-metapac/stm32l052c8\" ]\nstm32l052k6 = [ \"stm32-metapac/stm32l052k6\" ]\nstm32l052k8 = [ \"stm32-metapac/stm32l052k8\" ]\nstm32l052r6 = [ \"stm32-metapac/stm32l052r6\" ]\nstm32l052r8 = [ \"stm32-metapac/stm32l052r8\" ]\nstm32l052t6 = [ \"stm32-metapac/stm32l052t6\" ]\nstm32l052t8 = [ \"stm32-metapac/stm32l052t8\" ]\nstm32l053c6 = [ \"stm32-metapac/stm32l053c6\" ]\nstm32l053c8 = [ \"stm32-metapac/stm32l053c8\" ]\nstm32l053r6 = [ \"stm32-metapac/stm32l053r6\" ]\nstm32l053r8 = [ \"stm32-metapac/stm32l053r8\" ]\nstm32l062c8 = [ \"stm32-metapac/stm32l062c8\" ]\nstm32l062k8 = [ \"stm32-metapac/stm32l062k8\" ]\nstm32l063c8 = [ \"stm32-metapac/stm32l063c8\" ]\nstm32l063r8 = [ \"stm32-metapac/stm32l063r8\" ]\nstm32l071c8 = [ \"stm32-metapac/stm32l071c8\" ]\nstm32l071cb = [ \"stm32-metapac/stm32l071cb\" ]\nstm32l071cz = [ \"stm32-metapac/stm32l071cz\" ]\nstm32l071k8 = [ \"stm32-metapac/stm32l071k8\" ]\nstm32l071kb = [ \"stm32-metapac/stm32l071kb\" ]\nstm32l071kz = [ \"stm32-metapac/stm32l071kz\" ]\nstm32l071rb = [ \"stm32-metapac/stm32l071rb\" ]\nstm32l071rz = [ \"stm32-metapac/stm32l071rz\" ]\nstm32l071v8 = [ \"stm32-metapac/stm32l071v8\" ]\nstm32l071vb = [ \"stm32-metapac/stm32l071vb\" ]\nstm32l071vz = [ \"stm32-metapac/stm32l071vz\" ]\nstm32l072cb = [ \"stm32-metapac/stm32l072cb\" ]\nstm32l072cz = [ \"stm32-metapac/stm32l072cz\" ]\nstm32l072kb = [ \"stm32-metapac/stm32l072kb\" ]\nstm32l072kz = [ \"stm32-metapac/stm32l072kz\" ]\nstm32l072rb = [ \"stm32-metapac/stm32l072rb\" ]\nstm32l072rz = [ \"stm32-metapac/stm32l072rz\" ]\nstm32l072v8 = [ \"stm32-metapac/stm32l072v8\" ]\nstm32l072vb = [ \"stm32-metapac/stm32l072vb\" ]\nstm32l072vz = [ \"stm32-metapac/stm32l072vz\" ]\nstm32l073cb = [ \"stm32-metapac/stm32l073cb\" ]\nstm32l073cz = [ \"stm32-metapac/stm32l073cz\" ]\nstm32l073rb = [ \"stm32-metapac/stm32l073rb\" ]\nstm32l073rz = [ \"stm32-metapac/stm32l073rz\" ]\nstm32l073v8 = [ \"stm32-metapac/stm32l073v8\" ]\nstm32l073vb = [ \"stm32-metapac/stm32l073vb\" ]\nstm32l073vz = [ \"stm32-metapac/stm32l073vz\" ]\nstm32l081cb = [ \"stm32-metapac/stm32l081cb\" ]\nstm32l081cz = [ \"stm32-metapac/stm32l081cz\" ]\nstm32l081kz = [ \"stm32-metapac/stm32l081kz\" ]\nstm32l082cz = [ \"stm32-metapac/stm32l082cz\" ]\nstm32l082kb = [ \"stm32-metapac/stm32l082kb\" ]\nstm32l082kz = [ \"stm32-metapac/stm32l082kz\" ]\nstm32l083cb = [ \"stm32-metapac/stm32l083cb\" ]\nstm32l083cz = [ \"stm32-metapac/stm32l083cz\" ]\nstm32l083rb = [ \"stm32-metapac/stm32l083rb\" ]\nstm32l083rz = [ \"stm32-metapac/stm32l083rz\" ]\nstm32l083v8 = [ \"stm32-metapac/stm32l083v8\" ]\nstm32l083vb = [ \"stm32-metapac/stm32l083vb\" ]\nstm32l083vz = [ \"stm32-metapac/stm32l083vz\" ]\nstm32l100c6 = [ \"stm32-metapac/stm32l100c6\" ]\nstm32l100c6-a = [ \"stm32-metapac/stm32l100c6-a\" ]\nstm32l100r8 = [ \"stm32-metapac/stm32l100r8\" ]\nstm32l100r8-a = [ \"stm32-metapac/stm32l100r8-a\" ]\nstm32l100rb = [ \"stm32-metapac/stm32l100rb\" ]\nstm32l100rb-a = [ \"stm32-metapac/stm32l100rb-a\" ]\nstm32l100rc = [ \"stm32-metapac/stm32l100rc\" ]\nstm32l151c6 = [ \"stm32-metapac/stm32l151c6\" ]\nstm32l151c6-a = [ \"stm32-metapac/stm32l151c6-a\" ]\nstm32l151c8 = [ \"stm32-metapac/stm32l151c8\" ]\nstm32l151c8-a = [ \"stm32-metapac/stm32l151c8-a\" ]\nstm32l151cb = [ \"stm32-metapac/stm32l151cb\" ]\nstm32l151cb-a = [ \"stm32-metapac/stm32l151cb-a\" ]\nstm32l151cc = [ \"stm32-metapac/stm32l151cc\" ]\nstm32l151qc = [ \"stm32-metapac/stm32l151qc\" ]\nstm32l151qd = [ \"stm32-metapac/stm32l151qd\" ]\nstm32l151qe = [ \"stm32-metapac/stm32l151qe\" ]\nstm32l151r6 = [ \"stm32-metapac/stm32l151r6\" ]\nstm32l151r6-a = [ \"stm32-metapac/stm32l151r6-a\" ]\nstm32l151r8 = [ \"stm32-metapac/stm32l151r8\" ]\nstm32l151r8-a = [ \"stm32-metapac/stm32l151r8-a\" ]\nstm32l151rb = [ \"stm32-metapac/stm32l151rb\" ]\nstm32l151rb-a = [ \"stm32-metapac/stm32l151rb-a\" ]\nstm32l151rc = [ \"stm32-metapac/stm32l151rc\" ]\nstm32l151rc-a = [ \"stm32-metapac/stm32l151rc-a\" ]\nstm32l151rd = [ \"stm32-metapac/stm32l151rd\" ]\nstm32l151re = [ \"stm32-metapac/stm32l151re\" ]\nstm32l151uc = [ \"stm32-metapac/stm32l151uc\" ]\nstm32l151v8 = [ \"stm32-metapac/stm32l151v8\" ]\nstm32l151v8-a = [ \"stm32-metapac/stm32l151v8-a\" ]\nstm32l151vb = [ \"stm32-metapac/stm32l151vb\" ]\nstm32l151vb-a = [ \"stm32-metapac/stm32l151vb-a\" ]\nstm32l151vc = [ \"stm32-metapac/stm32l151vc\" ]\nstm32l151vc-a = [ \"stm32-metapac/stm32l151vc-a\" ]\nstm32l151vd = [ \"stm32-metapac/stm32l151vd\" ]\nstm32l151vd-x = [ \"stm32-metapac/stm32l151vd-x\" ]\nstm32l151ve = [ \"stm32-metapac/stm32l151ve\" ]\nstm32l151zc = [ \"stm32-metapac/stm32l151zc\" ]\nstm32l151zd = [ \"stm32-metapac/stm32l151zd\" ]\nstm32l151ze = [ \"stm32-metapac/stm32l151ze\" ]\nstm32l152c6 = [ \"stm32-metapac/stm32l152c6\" ]\nstm32l152c6-a = [ \"stm32-metapac/stm32l152c6-a\" ]\nstm32l152c8 = [ \"stm32-metapac/stm32l152c8\" ]\nstm32l152c8-a = [ \"stm32-metapac/stm32l152c8-a\" ]\nstm32l152cb = [ \"stm32-metapac/stm32l152cb\" ]\nstm32l152cb-a = [ \"stm32-metapac/stm32l152cb-a\" ]\nstm32l152cc = [ \"stm32-metapac/stm32l152cc\" ]\nstm32l152qc = [ \"stm32-metapac/stm32l152qc\" ]\nstm32l152qd = [ \"stm32-metapac/stm32l152qd\" ]\nstm32l152qe = [ \"stm32-metapac/stm32l152qe\" ]\nstm32l152r6 = [ \"stm32-metapac/stm32l152r6\" ]\nstm32l152r6-a = [ \"stm32-metapac/stm32l152r6-a\" ]\nstm32l152r8 = [ \"stm32-metapac/stm32l152r8\" ]\nstm32l152r8-a = [ \"stm32-metapac/stm32l152r8-a\" ]\nstm32l152rb = [ \"stm32-metapac/stm32l152rb\" ]\nstm32l152rb-a = [ \"stm32-metapac/stm32l152rb-a\" ]\nstm32l152rc = [ \"stm32-metapac/stm32l152rc\" ]\nstm32l152rc-a = [ \"stm32-metapac/stm32l152rc-a\" ]\nstm32l152rd = [ \"stm32-metapac/stm32l152rd\" ]\nstm32l152re = [ \"stm32-metapac/stm32l152re\" ]\nstm32l152uc = [ \"stm32-metapac/stm32l152uc\" ]\nstm32l152v8 = [ \"stm32-metapac/stm32l152v8\" ]\nstm32l152v8-a = [ \"stm32-metapac/stm32l152v8-a\" ]\nstm32l152vb = [ \"stm32-metapac/stm32l152vb\" ]\nstm32l152vb-a = [ \"stm32-metapac/stm32l152vb-a\" ]\nstm32l152vc = [ \"stm32-metapac/stm32l152vc\" ]\nstm32l152vc-a = [ \"stm32-metapac/stm32l152vc-a\" ]\nstm32l152vd = [ \"stm32-metapac/stm32l152vd\" ]\nstm32l152vd-x = [ \"stm32-metapac/stm32l152vd-x\" ]\nstm32l152ve = [ \"stm32-metapac/stm32l152ve\" ]\nstm32l152zc = [ \"stm32-metapac/stm32l152zc\" ]\nstm32l152zd = [ \"stm32-metapac/stm32l152zd\" ]\nstm32l152ze = [ \"stm32-metapac/stm32l152ze\" ]\nstm32l162qc = [ \"stm32-metapac/stm32l162qc\" ]\nstm32l162qd = [ \"stm32-metapac/stm32l162qd\" ]\nstm32l162rc = [ \"stm32-metapac/stm32l162rc\" ]\nstm32l162rc-a = [ \"stm32-metapac/stm32l162rc-a\" ]\nstm32l162rd = [ \"stm32-metapac/stm32l162rd\" ]\nstm32l162re = [ \"stm32-metapac/stm32l162re\" ]\nstm32l162vc = [ \"stm32-metapac/stm32l162vc\" ]\nstm32l162vc-a = [ \"stm32-metapac/stm32l162vc-a\" ]\nstm32l162vd = [ \"stm32-metapac/stm32l162vd\" ]\nstm32l162vd-x = [ \"stm32-metapac/stm32l162vd-x\" ]\nstm32l162ve = [ \"stm32-metapac/stm32l162ve\" ]\nstm32l162zc = [ \"stm32-metapac/stm32l162zc\" ]\nstm32l162zd = [ \"stm32-metapac/stm32l162zd\" ]\nstm32l162ze = [ \"stm32-metapac/stm32l162ze\" ]\nstm32l412c8 = [ \"stm32-metapac/stm32l412c8\" ]\nstm32l412cb = [ \"stm32-metapac/stm32l412cb\" ]\nstm32l412k8 = [ \"stm32-metapac/stm32l412k8\" ]\nstm32l412kb = [ \"stm32-metapac/stm32l412kb\" ]\nstm32l412r8 = [ \"stm32-metapac/stm32l412r8\" ]\nstm32l412rb = [ \"stm32-metapac/stm32l412rb\" ]\nstm32l412t8 = [ \"stm32-metapac/stm32l412t8\" ]\nstm32l412tb = [ \"stm32-metapac/stm32l412tb\" ]\nstm32l422cb = [ \"stm32-metapac/stm32l422cb\" ]\nstm32l422kb = [ \"stm32-metapac/stm32l422kb\" ]\nstm32l422rb = [ \"stm32-metapac/stm32l422rb\" ]\nstm32l422tb = [ \"stm32-metapac/stm32l422tb\" ]\nstm32l431cb = [ \"stm32-metapac/stm32l431cb\" ]\nstm32l431cc = [ \"stm32-metapac/stm32l431cc\" ]\nstm32l431kb = [ \"stm32-metapac/stm32l431kb\" ]\nstm32l431kc = [ \"stm32-metapac/stm32l431kc\" ]\nstm32l431rb = [ \"stm32-metapac/stm32l431rb\" ]\nstm32l431rc = [ \"stm32-metapac/stm32l431rc\" ]\nstm32l431vc = [ \"stm32-metapac/stm32l431vc\" ]\nstm32l432kb = [ \"stm32-metapac/stm32l432kb\" ]\nstm32l432kc = [ \"stm32-metapac/stm32l432kc\" ]\nstm32l433cb = [ \"stm32-metapac/stm32l433cb\" ]\nstm32l433cc = [ \"stm32-metapac/stm32l433cc\" ]\nstm32l433rb = [ \"stm32-metapac/stm32l433rb\" ]\nstm32l433rc = [ \"stm32-metapac/stm32l433rc\" ]\nstm32l433vc = [ \"stm32-metapac/stm32l433vc\" ]\nstm32l442kc = [ \"stm32-metapac/stm32l442kc\" ]\nstm32l443cc = [ \"stm32-metapac/stm32l443cc\" ]\nstm32l443rc = [ \"stm32-metapac/stm32l443rc\" ]\nstm32l443vc = [ \"stm32-metapac/stm32l443vc\" ]\nstm32l451cc = [ \"stm32-metapac/stm32l451cc\" ]\nstm32l451ce = [ \"stm32-metapac/stm32l451ce\" ]\nstm32l451rc = [ \"stm32-metapac/stm32l451rc\" ]\nstm32l451re = [ \"stm32-metapac/stm32l451re\" ]\nstm32l451vc = [ \"stm32-metapac/stm32l451vc\" ]\nstm32l451ve = [ \"stm32-metapac/stm32l451ve\" ]\nstm32l452cc = [ \"stm32-metapac/stm32l452cc\" ]\nstm32l452ce = [ \"stm32-metapac/stm32l452ce\" ]\nstm32l452rc = [ \"stm32-metapac/stm32l452rc\" ]\nstm32l452re = [ \"stm32-metapac/stm32l452re\" ]\nstm32l452vc = [ \"stm32-metapac/stm32l452vc\" ]\nstm32l452ve = [ \"stm32-metapac/stm32l452ve\" ]\nstm32l462ce = [ \"stm32-metapac/stm32l462ce\" ]\nstm32l462re = [ \"stm32-metapac/stm32l462re\" ]\nstm32l462ve = [ \"stm32-metapac/stm32l462ve\" ]\nstm32l471qe = [ \"stm32-metapac/stm32l471qe\" ]\nstm32l471qg = [ \"stm32-metapac/stm32l471qg\" ]\nstm32l471re = [ \"stm32-metapac/stm32l471re\" ]\nstm32l471rg = [ \"stm32-metapac/stm32l471rg\" ]\nstm32l471ve = [ \"stm32-metapac/stm32l471ve\" ]\nstm32l471vg = [ \"stm32-metapac/stm32l471vg\" ]\nstm32l471ze = [ \"stm32-metapac/stm32l471ze\" ]\nstm32l471zg = [ \"stm32-metapac/stm32l471zg\" ]\nstm32l475rc = [ \"stm32-metapac/stm32l475rc\" ]\nstm32l475re = [ \"stm32-metapac/stm32l475re\" ]\nstm32l475rg = [ \"stm32-metapac/stm32l475rg\" ]\nstm32l475vc = [ \"stm32-metapac/stm32l475vc\" ]\nstm32l475ve = [ \"stm32-metapac/stm32l475ve\" ]\nstm32l475vg = [ \"stm32-metapac/stm32l475vg\" ]\nstm32l476je = [ \"stm32-metapac/stm32l476je\" ]\nstm32l476jg = [ \"stm32-metapac/stm32l476jg\" ]\nstm32l476me = [ \"stm32-metapac/stm32l476me\" ]\nstm32l476mg = [ \"stm32-metapac/stm32l476mg\" ]\nstm32l476qe = [ \"stm32-metapac/stm32l476qe\" ]\nstm32l476qg = [ \"stm32-metapac/stm32l476qg\" ]\nstm32l476rc = [ \"stm32-metapac/stm32l476rc\" ]\nstm32l476re = [ \"stm32-metapac/stm32l476re\" ]\nstm32l476rg = [ \"stm32-metapac/stm32l476rg\" ]\nstm32l476vc = [ \"stm32-metapac/stm32l476vc\" ]\nstm32l476ve = [ \"stm32-metapac/stm32l476ve\" ]\nstm32l476vg = [ \"stm32-metapac/stm32l476vg\" ]\nstm32l476ze = [ \"stm32-metapac/stm32l476ze\" ]\nstm32l476zg = [ \"stm32-metapac/stm32l476zg\" ]\nstm32l486jg = [ \"stm32-metapac/stm32l486jg\" ]\nstm32l486qg = [ \"stm32-metapac/stm32l486qg\" ]\nstm32l486rg = [ \"stm32-metapac/stm32l486rg\" ]\nstm32l486vg = [ \"stm32-metapac/stm32l486vg\" ]\nstm32l486zg = [ \"stm32-metapac/stm32l486zg\" ]\nstm32l496ae = [ \"stm32-metapac/stm32l496ae\" ]\nstm32l496ag = [ \"stm32-metapac/stm32l496ag\" ]\nstm32l496qe = [ \"stm32-metapac/stm32l496qe\" ]\nstm32l496qg = [ \"stm32-metapac/stm32l496qg\" ]\nstm32l496re = [ \"stm32-metapac/stm32l496re\" ]\nstm32l496rg = [ \"stm32-metapac/stm32l496rg\" ]\nstm32l496ve = [ \"stm32-metapac/stm32l496ve\" ]\nstm32l496vg = [ \"stm32-metapac/stm32l496vg\" ]\nstm32l496wg = [ \"stm32-metapac/stm32l496wg\" ]\nstm32l496ze = [ \"stm32-metapac/stm32l496ze\" ]\nstm32l496zg = [ \"stm32-metapac/stm32l496zg\" ]\nstm32l4a6ag = [ \"stm32-metapac/stm32l4a6ag\" ]\nstm32l4a6qg = [ \"stm32-metapac/stm32l4a6qg\" ]\nstm32l4a6rg = [ \"stm32-metapac/stm32l4a6rg\" ]\nstm32l4a6vg = [ \"stm32-metapac/stm32l4a6vg\" ]\nstm32l4a6zg = [ \"stm32-metapac/stm32l4a6zg\" ]\nstm32l4p5ae = [ \"stm32-metapac/stm32l4p5ae\" ]\nstm32l4p5ag = [ \"stm32-metapac/stm32l4p5ag\" ]\nstm32l4p5ce = [ \"stm32-metapac/stm32l4p5ce\" ]\nstm32l4p5cg = [ \"stm32-metapac/stm32l4p5cg\" ]\nstm32l4p5qe = [ \"stm32-metapac/stm32l4p5qe\" ]\nstm32l4p5qg = [ \"stm32-metapac/stm32l4p5qg\" ]\nstm32l4p5re = [ \"stm32-metapac/stm32l4p5re\" ]\nstm32l4p5rg = [ \"stm32-metapac/stm32l4p5rg\" ]\nstm32l4p5ve = [ \"stm32-metapac/stm32l4p5ve\" ]\nstm32l4p5vg = [ \"stm32-metapac/stm32l4p5vg\" ]\nstm32l4p5ze = [ \"stm32-metapac/stm32l4p5ze\" ]\nstm32l4p5zg = [ \"stm32-metapac/stm32l4p5zg\" ]\nstm32l4q5ag = [ \"stm32-metapac/stm32l4q5ag\" ]\nstm32l4q5cg = [ \"stm32-metapac/stm32l4q5cg\" ]\nstm32l4q5qg = [ \"stm32-metapac/stm32l4q5qg\" ]\nstm32l4q5rg = [ \"stm32-metapac/stm32l4q5rg\" ]\nstm32l4q5vg = [ \"stm32-metapac/stm32l4q5vg\" ]\nstm32l4q5zg = [ \"stm32-metapac/stm32l4q5zg\" ]\nstm32l4r5ag = [ \"stm32-metapac/stm32l4r5ag\" ]\nstm32l4r5ai = [ \"stm32-metapac/stm32l4r5ai\" ]\nstm32l4r5qg = [ \"stm32-metapac/stm32l4r5qg\" ]\nstm32l4r5qi = [ \"stm32-metapac/stm32l4r5qi\" ]\nstm32l4r5vg = [ \"stm32-metapac/stm32l4r5vg\" ]\nstm32l4r5vi = [ \"stm32-metapac/stm32l4r5vi\" ]\nstm32l4r5zg = [ \"stm32-metapac/stm32l4r5zg\" ]\nstm32l4r5zi = [ \"stm32-metapac/stm32l4r5zi\" ]\nstm32l4r7ai = [ \"stm32-metapac/stm32l4r7ai\" ]\nstm32l4r7vi = [ \"stm32-metapac/stm32l4r7vi\" ]\nstm32l4r7zi = [ \"stm32-metapac/stm32l4r7zi\" ]\nstm32l4r9ag = [ \"stm32-metapac/stm32l4r9ag\" ]\nstm32l4r9ai = [ \"stm32-metapac/stm32l4r9ai\" ]\nstm32l4r9vg = [ \"stm32-metapac/stm32l4r9vg\" ]\nstm32l4r9vi = [ \"stm32-metapac/stm32l4r9vi\" ]\nstm32l4r9zg = [ \"stm32-metapac/stm32l4r9zg\" ]\nstm32l4r9zi = [ \"stm32-metapac/stm32l4r9zi\" ]\nstm32l4s5ai = [ \"stm32-metapac/stm32l4s5ai\" ]\nstm32l4s5qi = [ \"stm32-metapac/stm32l4s5qi\" ]\nstm32l4s5vi = [ \"stm32-metapac/stm32l4s5vi\" ]\nstm32l4s5zi = [ \"stm32-metapac/stm32l4s5zi\" ]\nstm32l4s7ai = [ \"stm32-metapac/stm32l4s7ai\" ]\nstm32l4s7vi = [ \"stm32-metapac/stm32l4s7vi\" ]\nstm32l4s7zi = [ \"stm32-metapac/stm32l4s7zi\" ]\nstm32l4s9ai = [ \"stm32-metapac/stm32l4s9ai\" ]\nstm32l4s9vi = [ \"stm32-metapac/stm32l4s9vi\" ]\nstm32l4s9zi = [ \"stm32-metapac/stm32l4s9zi\" ]\nstm32l552cc = [ \"stm32-metapac/stm32l552cc\" ]\nstm32l552ce = [ \"stm32-metapac/stm32l552ce\" ]\nstm32l552me = [ \"stm32-metapac/stm32l552me\" ]\nstm32l552qc = [ \"stm32-metapac/stm32l552qc\" ]\nstm32l552qe = [ \"stm32-metapac/stm32l552qe\" ]\nstm32l552rc = [ \"stm32-metapac/stm32l552rc\" ]\nstm32l552re = [ \"stm32-metapac/stm32l552re\" ]\nstm32l552vc = [ \"stm32-metapac/stm32l552vc\" ]\nstm32l552ve = [ \"stm32-metapac/stm32l552ve\" ]\nstm32l552zc = [ \"stm32-metapac/stm32l552zc\" ]\nstm32l552ze = [ \"stm32-metapac/stm32l552ze\" ]\nstm32l562ce = [ \"stm32-metapac/stm32l562ce\" ]\nstm32l562me = [ \"stm32-metapac/stm32l562me\" ]\nstm32l562qe = [ \"stm32-metapac/stm32l562qe\" ]\nstm32l562re = [ \"stm32-metapac/stm32l562re\" ]\nstm32l562ve = [ \"stm32-metapac/stm32l562ve\" ]\nstm32l562ze = [ \"stm32-metapac/stm32l562ze\" ]\nstm32n645a0 = [ \"stm32-metapac/stm32n645a0\" ]\nstm32n645b0 = [ \"stm32-metapac/stm32n645b0\" ]\nstm32n645i0 = [ \"stm32-metapac/stm32n645i0\" ]\nstm32n645l0 = [ \"stm32-metapac/stm32n645l0\" ]\nstm32n645x0 = [ \"stm32-metapac/stm32n645x0\" ]\nstm32n645z0 = [ \"stm32-metapac/stm32n645z0\" ]\nstm32n647a0 = [ \"stm32-metapac/stm32n647a0\" ]\nstm32n647b0 = [ \"stm32-metapac/stm32n647b0\" ]\nstm32n647i0 = [ \"stm32-metapac/stm32n647i0\" ]\nstm32n647l0 = [ \"stm32-metapac/stm32n647l0\" ]\nstm32n647x0 = [ \"stm32-metapac/stm32n647x0\" ]\nstm32n647z0 = [ \"stm32-metapac/stm32n647z0\" ]\nstm32n655a0 = [ \"stm32-metapac/stm32n655a0\" ]\nstm32n655b0 = [ \"stm32-metapac/stm32n655b0\" ]\nstm32n655i0 = [ \"stm32-metapac/stm32n655i0\" ]\nstm32n655l0 = [ \"stm32-metapac/stm32n655l0\" ]\nstm32n655x0 = [ \"stm32-metapac/stm32n655x0\" ]\nstm32n655z0 = [ \"stm32-metapac/stm32n655z0\" ]\nstm32n657a0 = [ \"stm32-metapac/stm32n657a0\" ]\nstm32n657b0 = [ \"stm32-metapac/stm32n657b0\" ]\nstm32n657i0 = [ \"stm32-metapac/stm32n657i0\" ]\nstm32n657l0 = [ \"stm32-metapac/stm32n657l0\" ]\nstm32n657x0 = [ \"stm32-metapac/stm32n657x0\" ]\nstm32n657z0 = [ \"stm32-metapac/stm32n657z0\" ]\nstm32u031c6 = [ \"stm32-metapac/stm32u031c6\" ]\nstm32u031c8 = [ \"stm32-metapac/stm32u031c8\" ]\nstm32u031f4 = [ \"stm32-metapac/stm32u031f4\" ]\nstm32u031f6 = [ \"stm32-metapac/stm32u031f6\" ]\nstm32u031f8 = [ \"stm32-metapac/stm32u031f8\" ]\nstm32u031g6 = [ \"stm32-metapac/stm32u031g6\" ]\nstm32u031g8 = [ \"stm32-metapac/stm32u031g8\" ]\nstm32u031k4 = [ \"stm32-metapac/stm32u031k4\" ]\nstm32u031k6 = [ \"stm32-metapac/stm32u031k6\" ]\nstm32u031k8 = [ \"stm32-metapac/stm32u031k8\" ]\nstm32u031r6 = [ \"stm32-metapac/stm32u031r6\" ]\nstm32u031r8 = [ \"stm32-metapac/stm32u031r8\" ]\nstm32u073c8 = [ \"stm32-metapac/stm32u073c8\" ]\nstm32u073cb = [ \"stm32-metapac/stm32u073cb\" ]\nstm32u073cc = [ \"stm32-metapac/stm32u073cc\" ]\nstm32u073h8 = [ \"stm32-metapac/stm32u073h8\" ]\nstm32u073hb = [ \"stm32-metapac/stm32u073hb\" ]\nstm32u073hc = [ \"stm32-metapac/stm32u073hc\" ]\nstm32u073k8 = [ \"stm32-metapac/stm32u073k8\" ]\nstm32u073kb = [ \"stm32-metapac/stm32u073kb\" ]\nstm32u073kc = [ \"stm32-metapac/stm32u073kc\" ]\nstm32u073m8 = [ \"stm32-metapac/stm32u073m8\" ]\nstm32u073mb = [ \"stm32-metapac/stm32u073mb\" ]\nstm32u073mc = [ \"stm32-metapac/stm32u073mc\" ]\nstm32u073r8 = [ \"stm32-metapac/stm32u073r8\" ]\nstm32u073rb = [ \"stm32-metapac/stm32u073rb\" ]\nstm32u073rc = [ \"stm32-metapac/stm32u073rc\" ]\nstm32u083cc = [ \"stm32-metapac/stm32u083cc\" ]\nstm32u083hc = [ \"stm32-metapac/stm32u083hc\" ]\nstm32u083kc = [ \"stm32-metapac/stm32u083kc\" ]\nstm32u083mc = [ \"stm32-metapac/stm32u083mc\" ]\nstm32u083rc = [ \"stm32-metapac/stm32u083rc\" ]\nstm32u375ce = [ \"stm32-metapac/stm32u375ce\" ]\nstm32u375cg = [ \"stm32-metapac/stm32u375cg\" ]\nstm32u375ke = [ \"stm32-metapac/stm32u375ke\" ]\nstm32u375kg = [ \"stm32-metapac/stm32u375kg\" ]\nstm32u375re = [ \"stm32-metapac/stm32u375re\" ]\nstm32u375rg = [ \"stm32-metapac/stm32u375rg\" ]\nstm32u375ve = [ \"stm32-metapac/stm32u375ve\" ]\nstm32u375vg = [ \"stm32-metapac/stm32u375vg\" ]\nstm32u385cg = [ \"stm32-metapac/stm32u385cg\" ]\nstm32u385kg = [ \"stm32-metapac/stm32u385kg\" ]\nstm32u385rg = [ \"stm32-metapac/stm32u385rg\" ]\nstm32u385vg = [ \"stm32-metapac/stm32u385vg\" ]\nstm32u535cb = [ \"stm32-metapac/stm32u535cb\" ]\nstm32u535cc = [ \"stm32-metapac/stm32u535cc\" ]\nstm32u535ce = [ \"stm32-metapac/stm32u535ce\" ]\nstm32u535je = [ \"stm32-metapac/stm32u535je\" ]\nstm32u535nc = [ \"stm32-metapac/stm32u535nc\" ]\nstm32u535ne = [ \"stm32-metapac/stm32u535ne\" ]\nstm32u535rb = [ \"stm32-metapac/stm32u535rb\" ]\nstm32u535rc = [ \"stm32-metapac/stm32u535rc\" ]\nstm32u535re = [ \"stm32-metapac/stm32u535re\" ]\nstm32u535vc = [ \"stm32-metapac/stm32u535vc\" ]\nstm32u535ve = [ \"stm32-metapac/stm32u535ve\" ]\nstm32u545ce = [ \"stm32-metapac/stm32u545ce\" ]\nstm32u545je = [ \"stm32-metapac/stm32u545je\" ]\nstm32u545ne = [ \"stm32-metapac/stm32u545ne\" ]\nstm32u545re = [ \"stm32-metapac/stm32u545re\" ]\nstm32u545ve = [ \"stm32-metapac/stm32u545ve\" ]\nstm32u575ag = [ \"stm32-metapac/stm32u575ag\" ]\nstm32u575ai = [ \"stm32-metapac/stm32u575ai\" ]\nstm32u575cg = [ \"stm32-metapac/stm32u575cg\" ]\nstm32u575ci = [ \"stm32-metapac/stm32u575ci\" ]\nstm32u575og = [ \"stm32-metapac/stm32u575og\" ]\nstm32u575oi = [ \"stm32-metapac/stm32u575oi\" ]\nstm32u575qg = [ \"stm32-metapac/stm32u575qg\" ]\nstm32u575qi = [ \"stm32-metapac/stm32u575qi\" ]\nstm32u575rg = [ \"stm32-metapac/stm32u575rg\" ]\nstm32u575ri = [ \"stm32-metapac/stm32u575ri\" ]\nstm32u575vg = [ \"stm32-metapac/stm32u575vg\" ]\nstm32u575vi = [ \"stm32-metapac/stm32u575vi\" ]\nstm32u575zg = [ \"stm32-metapac/stm32u575zg\" ]\nstm32u575zi = [ \"stm32-metapac/stm32u575zi\" ]\nstm32u585ai = [ \"stm32-metapac/stm32u585ai\" ]\nstm32u585ci = [ \"stm32-metapac/stm32u585ci\" ]\nstm32u585oi = [ \"stm32-metapac/stm32u585oi\" ]\nstm32u585qi = [ \"stm32-metapac/stm32u585qi\" ]\nstm32u585ri = [ \"stm32-metapac/stm32u585ri\" ]\nstm32u585vi = [ \"stm32-metapac/stm32u585vi\" ]\nstm32u585zi = [ \"stm32-metapac/stm32u585zi\" ]\nstm32u595ai = [ \"stm32-metapac/stm32u595ai\" ]\nstm32u595aj = [ \"stm32-metapac/stm32u595aj\" ]\nstm32u595qi = [ \"stm32-metapac/stm32u595qi\" ]\nstm32u595qj = [ \"stm32-metapac/stm32u595qj\" ]\nstm32u595ri = [ \"stm32-metapac/stm32u595ri\" ]\nstm32u595rj = [ \"stm32-metapac/stm32u595rj\" ]\nstm32u595vi = [ \"stm32-metapac/stm32u595vi\" ]\nstm32u595vj = [ \"stm32-metapac/stm32u595vj\" ]\nstm32u595zi = [ \"stm32-metapac/stm32u595zi\" ]\nstm32u595zj = [ \"stm32-metapac/stm32u595zj\" ]\nstm32u599bj = [ \"stm32-metapac/stm32u599bj\" ]\nstm32u599ni = [ \"stm32-metapac/stm32u599ni\" ]\nstm32u599nj = [ \"stm32-metapac/stm32u599nj\" ]\nstm32u599vi = [ \"stm32-metapac/stm32u599vi\" ]\nstm32u599vj = [ \"stm32-metapac/stm32u599vj\" ]\nstm32u599zi = [ \"stm32-metapac/stm32u599zi\" ]\nstm32u599zj = [ \"stm32-metapac/stm32u599zj\" ]\nstm32u5a5aj = [ \"stm32-metapac/stm32u5a5aj\" ]\nstm32u5a5qi = [ \"stm32-metapac/stm32u5a5qi\" ]\nstm32u5a5qj = [ \"stm32-metapac/stm32u5a5qj\" ]\nstm32u5a5rj = [ \"stm32-metapac/stm32u5a5rj\" ]\nstm32u5a5vj = [ \"stm32-metapac/stm32u5a5vj\" ]\nstm32u5a5zj = [ \"stm32-metapac/stm32u5a5zj\" ]\nstm32u5a9bj = [ \"stm32-metapac/stm32u5a9bj\" ]\nstm32u5a9nj = [ \"stm32-metapac/stm32u5a9nj\" ]\nstm32u5a9vj = [ \"stm32-metapac/stm32u5a9vj\" ]\nstm32u5a9zj = [ \"stm32-metapac/stm32u5a9zj\" ]\nstm32u5f7vi = [ \"stm32-metapac/stm32u5f7vi\" ]\nstm32u5f7vj = [ \"stm32-metapac/stm32u5f7vj\" ]\nstm32u5f9bj = [ \"stm32-metapac/stm32u5f9bj\" ]\nstm32u5f9nj = [ \"stm32-metapac/stm32u5f9nj\" ]\nstm32u5f9vi = [ \"stm32-metapac/stm32u5f9vi\" ]\nstm32u5f9vj = [ \"stm32-metapac/stm32u5f9vj\" ]\nstm32u5f9zi = [ \"stm32-metapac/stm32u5f9zi\" ]\nstm32u5f9zj = [ \"stm32-metapac/stm32u5f9zj\" ]\nstm32u5g7vj = [ \"stm32-metapac/stm32u5g7vj\" ]\nstm32u5g9bj = [ \"stm32-metapac/stm32u5g9bj\" ]\nstm32u5g9nj = [ \"stm32-metapac/stm32u5g9nj\" ]\nstm32u5g9vj = [ \"stm32-metapac/stm32u5g9vj\" ]\nstm32u5g9zj = [ \"stm32-metapac/stm32u5g9zj\" ]\nstm32wb10cc = [ \"stm32-metapac/stm32wb10cc\" ]\nstm32wb15cc = [ \"stm32-metapac/stm32wb15cc\" ]\nstm32wb30ce = [ \"stm32-metapac/stm32wb30ce\" ]\nstm32wb35cc = [ \"stm32-metapac/stm32wb35cc\" ]\nstm32wb35ce = [ \"stm32-metapac/stm32wb35ce\" ]\nstm32wb50cg = [ \"stm32-metapac/stm32wb50cg\" ]\nstm32wb55cc = [ \"stm32-metapac/stm32wb55cc\" ]\nstm32wb55ce = [ \"stm32-metapac/stm32wb55ce\" ]\nstm32wb55cg = [ \"stm32-metapac/stm32wb55cg\" ]\nstm32wb55rc = [ \"stm32-metapac/stm32wb55rc\" ]\nstm32wb55re = [ \"stm32-metapac/stm32wb55re\" ]\nstm32wb55rg = [ \"stm32-metapac/stm32wb55rg\" ]\nstm32wb55vc = [ \"stm32-metapac/stm32wb55vc\" ]\nstm32wb55ve = [ \"stm32-metapac/stm32wb55ve\" ]\nstm32wb55vg = [ \"stm32-metapac/stm32wb55vg\" ]\nstm32wb55vy = [ \"stm32-metapac/stm32wb55vy\" ]\nstm32wba50ke = [ \"stm32-metapac/stm32wba50ke\" ]\nstm32wba50kg = [ \"stm32-metapac/stm32wba50kg\" ]\nstm32wba52ce = [ \"stm32-metapac/stm32wba52ce\" ]\nstm32wba52cg = [ \"stm32-metapac/stm32wba52cg\" ]\nstm32wba52ke = [ \"stm32-metapac/stm32wba52ke\" ]\nstm32wba52kg = [ \"stm32-metapac/stm32wba52kg\" ]\nstm32wba54ce = [ \"stm32-metapac/stm32wba54ce\" ]\nstm32wba54cg = [ \"stm32-metapac/stm32wba54cg\" ]\nstm32wba54ke = [ \"stm32-metapac/stm32wba54ke\" ]\nstm32wba54kg = [ \"stm32-metapac/stm32wba54kg\" ]\nstm32wba55ce = [ \"stm32-metapac/stm32wba55ce\" ]\nstm32wba55cg = [ \"stm32-metapac/stm32wba55cg\" ]\nstm32wba55he = [ \"stm32-metapac/stm32wba55he\" ]\nstm32wba55hg = [ \"stm32-metapac/stm32wba55hg\" ]\nstm32wba55ue = [ \"stm32-metapac/stm32wba55ue\" ]\nstm32wba55ug = [ \"stm32-metapac/stm32wba55ug\" ]\nstm32wba62cg = [ \"stm32-metapac/stm32wba62cg\" ]\nstm32wba62ci = [ \"stm32-metapac/stm32wba62ci\" ]\nstm32wba62mg = [ \"stm32-metapac/stm32wba62mg\" ]\nstm32wba62mi = [ \"stm32-metapac/stm32wba62mi\" ]\nstm32wba62pg = [ \"stm32-metapac/stm32wba62pg\" ]\nstm32wba62pi = [ \"stm32-metapac/stm32wba62pi\" ]\nstm32wba63cg = [ \"stm32-metapac/stm32wba63cg\" ]\nstm32wba63ci = [ \"stm32-metapac/stm32wba63ci\" ]\nstm32wba64cg = [ \"stm32-metapac/stm32wba64cg\" ]\nstm32wba64ci = [ \"stm32-metapac/stm32wba64ci\" ]\nstm32wba65cg = [ \"stm32-metapac/stm32wba65cg\" ]\nstm32wba65ci = [ \"stm32-metapac/stm32wba65ci\" ]\nstm32wba65mg = [ \"stm32-metapac/stm32wba65mg\" ]\nstm32wba65mi = [ \"stm32-metapac/stm32wba65mi\" ]\nstm32wba65pg = [ \"stm32-metapac/stm32wba65pg\" ]\nstm32wba65pi = [ \"stm32-metapac/stm32wba65pi\" ]\nstm32wba65rg = [ \"stm32-metapac/stm32wba65rg\" ]\nstm32wba65ri = [ \"stm32-metapac/stm32wba65ri\" ]\nstm32wl54cc-cm4 = [ \"stm32-metapac/stm32wl54cc-cm4\", \"_dual-core\", \"_core-cm4\" ]\nstm32wl54cc-cm0p = [ \"stm32-metapac/stm32wl54cc-cm0p\", \"_dual-core\", \"_core-cm0p\" ]\nstm32wl54jc-cm4 = [ \"stm32-metapac/stm32wl54jc-cm4\", \"_dual-core\", \"_core-cm4\" ]\nstm32wl54jc-cm0p = [ \"stm32-metapac/stm32wl54jc-cm0p\", \"_dual-core\", \"_core-cm0p\" ]\nstm32wl55cc-cm4 = [ \"stm32-metapac/stm32wl55cc-cm4\", \"_dual-core\", \"_core-cm4\" ]\nstm32wl55cc-cm0p = [ \"stm32-metapac/stm32wl55cc-cm0p\", \"_dual-core\", \"_core-cm0p\" ]\nstm32wl55jc-cm4 = [ \"stm32-metapac/stm32wl55jc-cm4\", \"_dual-core\", \"_core-cm4\" ]\nstm32wl55jc-cm0p = [ \"stm32-metapac/stm32wl55jc-cm0p\", \"_dual-core\", \"_core-cm0p\" ]\nstm32wle4c8 = [ \"stm32-metapac/stm32wle4c8\" ]\nstm32wle4cb = [ \"stm32-metapac/stm32wle4cb\" ]\nstm32wle4cc = [ \"stm32-metapac/stm32wle4cc\" ]\nstm32wle4j8 = [ \"stm32-metapac/stm32wle4j8\" ]\nstm32wle4jb = [ \"stm32-metapac/stm32wle4jb\" ]\nstm32wle4jc = [ \"stm32-metapac/stm32wle4jc\" ]\nstm32wle5c8 = [ \"stm32-metapac/stm32wle5c8\" ]\nstm32wle5cb = [ \"stm32-metapac/stm32wle5cb\" ]\nstm32wle5cc = [ \"stm32-metapac/stm32wle5cc\" ]\nstm32wle5j8 = [ \"stm32-metapac/stm32wle5j8\" ]\nstm32wle5jb = [ \"stm32-metapac/stm32wle5jb\" ]\nstm32wle5jc = [ \"stm32-metapac/stm32wle5jc\" ]\n"
  },
  {
    "path": "embassy-stm32/README.md",
    "content": "# Embassy STM32 HAL\n\nThe embassy-stm32 HAL aims to provide a safe, idiomatic hardware abstraction layer for all STM32 families. The HAL implements both blocking and async APIs for many peripherals. Where appropriate, traits from both blocking and asynchronous versions of [embedded-hal](https://docs.rs/embedded-hal/latest/embedded_hal/) v0.2 and v1.0 are implemented, as well as serial traits from embedded-io\\[-async].\n\n* [embassy-stm32 on crates.io](https://crates.io/crates/embassy-stm32)\n* [Documentation](https://docs.embassy.dev/embassy-stm32/) (**Important:** use docs.embassy.dev rather than docs.rs to see the specific docs for the chip you’re using!)\n* [Source](https://github.com/embassy-rs/embassy/tree/main/embassy-stm32)\n* [Examples](https://github.com/embassy-rs/embassy/tree/main/examples)\n\n## embassy-stm32 supports all STM32 chip families\n\nSTM32 microcontrollers come in many families and flavors, and supporting all of them is a big undertaking. Embassy takes advantage of the fact that the STM32 peripheral versions are shared across chip families. For example, instead of re-implementing the SPI peripheral for every STM32 chip family, embassy has a single SPI implementation that depends on code-generated register types that are identical for STM32 families with the same version of a given peripheral.\n\nIn practice, this works as follows:\n\n1. You tell the compiler which chip you’re using with a feature flag\n1. The stm32-metapac module generates register types for that chip at compile time, based on data from the stm32-data module\n1. The embassy-stm32 HAL picks the correct implementation each peripheral based on automatically-generated feature flags, and applies any other tweaks which are required for the HAL to work on that chip\n\nBe aware that, while embassy-stm32 strives to consistently support all peripherals across all chips, this approach can lead to slightly different APIs and capabilities being available on different families. Check the [documentation](https://docs.embassy.dev/embassy-stm32/) for the specific chip you’re using to confirm exactly what’s available.\n\n## Embedded-hal\n\nThe `embassy-stm32` HAL implements the traits from [embedded-hal](https://crates.io/crates/embedded-hal) (v0.2 and 1.0) and [embedded-hal-async](https://crates.io/crates/embedded-hal-async), as well as [embedded-io](https://crates.io/crates/embedded-io) and [embedded-io-async](https://crates.io/crates/embedded-io-async).\n\n## `embassy-time` time driver\nIf a `time-driver-*` feature is enabled, embassy-stm32 provides a time driver for use with [embassy-time](https://docs.embassy.dev/embassy-time/). You can pick which hardware timer is used for this internally via the `time-driver-tim*` features, or let embassy pick with `time-driver-any`.\n\nembassy-time has a default tick rate of 1MHz, which is fast enough to cause problems with the 16-bit timers currently supported by the embassy-stm32 time driver (specifically, if a critical section delays an IRQ by more than 32ms). To avoid this, it’s recommended to pick a lower tick rate. 32.768kHz is a reasonable default for many purposes.\n\n## Interoperability\n\nThis crate can run on any executor.\n\nOptionally, some features requiring [`embassy-time`](https://crates.io/crates/embassy-time) can be activated with the `time` feature. If you enable it,\nyou must link an `embassy-time` driver in your project.\n\nThe `low-power` feature integrates specifically with `embassy-executor`, it can't be used on other executors for now.\n"
  },
  {
    "path": "embassy-stm32/build.rs",
    "content": "use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet};\nuse std::fmt::Write as _;\nuse std::io::Write;\nuse std::path::{Path, PathBuf};\nuse std::process::Command;\nuse std::{env, fs};\n\nuse proc_macro2::{Ident, TokenStream};\nuse quote::{format_ident, quote};\nuse regex::Regex;\nuse stm32_metapac::metadata::ir::BitOffset;\nuse stm32_metapac::metadata::{\n    ALL_CHIPS, ALL_PERIPHERAL_VERSIONS, METADATA, MemoryRegion, MemoryRegionKind, Peripheral, PeripheralRccKernelClock,\n    PeripheralRccRegister, PeripheralRegisters, StopMode,\n};\n\n#[path = \"./build_common.rs\"]\nmod common;\n\n/// Helper function to handle peripheral versions with underscores.\n/// For a version like \"v1_foo_bar\", this generates all prefix combinations:\n/// - \"kind_v1\"\n/// - \"kind_v1_foo\"\n/// - \"kind_v1_foo_bar\"\nfn foreach_version_cfg(\n    cfgs: &mut common::CfgSet,\n    kind: &str,\n    version: &str,\n    mut cfg_fn: impl FnMut(&mut common::CfgSet, &str),\n) {\n    let parts: Vec<&str> = version.split('_').collect();\n\n    // Generate all possible prefix combinations\n    for i in 1..=parts.len() {\n        let partial_version = parts[0..i].join(\"_\");\n        let cfg_name = format!(\"{}_{}\", kind, partial_version);\n        cfg_fn(cfgs, &cfg_name);\n    }\n}\n\nfn main() {\n    let mut cfgs = common::CfgSet::new();\n    common::set_target_cfgs(&mut cfgs);\n\n    let chip_name = match env::vars()\n        .map(|(a, _)| a)\n        .filter(|x| x.starts_with(\"CARGO_FEATURE_STM32\") && x != \"CARGO_FEATURE_STM32_HRTIM\")\n        .get_one()\n    {\n        Ok(x) => x,\n        Err(GetOneError::None) => panic!(\"No stm32xx Cargo feature enabled\"),\n        Err(GetOneError::Multiple) => panic!(\"Multiple stm32xx Cargo features enabled\"),\n    }\n    .strip_prefix(\"CARGO_FEATURE_\")\n    .unwrap()\n    .to_ascii_lowercase();\n\n    eprintln!(\"chip: {chip_name}\");\n\n    for p in METADATA.peripherals {\n        if let Some(r) = &p.registers {\n            cfgs.enable(r.kind);\n            foreach_version_cfg(&mut cfgs, r.kind, r.version, |cfgs, cfg_name| {\n                cfgs.enable(cfg_name);\n            });\n        }\n    }\n\n    for &(kind, versions) in ALL_PERIPHERAL_VERSIONS.iter() {\n        cfgs.declare(kind);\n        for &version in versions.iter() {\n            foreach_version_cfg(&mut cfgs, kind, version, |cfgs, cfg_name| {\n                cfgs.declare(cfg_name);\n            });\n        }\n    }\n\n    // ========\n    // Select the memory variant to use\n    let dual_bank_selected = env::var(\"CARGO_FEATURE_DUAL_BANK\").is_ok();\n    let memory = {\n        let single_bank_selected = env::var(\"CARGO_FEATURE_SINGLE_BANK\").is_ok();\n\n        let single_bank_memory = METADATA.memory.iter().find(|mem| {\n            mem.iter().any(|region| region.name.contains(\"BANK_1\"))\n                && !mem.iter().any(|region| region.name.contains(\"BANK_2\"))\n        });\n\n        let dual_bank_memory = METADATA.memory.iter().find(|mem| {\n            mem.iter().any(|region| region.name.contains(\"BANK_1\"))\n                && mem.iter().any(|region| region.name.contains(\"BANK_2\"))\n        });\n\n        cfgs.set(\n            \"bank_setup_configurable\",\n            single_bank_memory.is_some() && dual_bank_memory.is_some(),\n        );\n\n        match (single_bank_selected, dual_bank_selected) {\n            (true, true) => panic!(\"Both 'single-bank' and 'dual-bank' features enabled\"),\n            (true, false) => {\n                single_bank_memory.expect(\"The 'single-bank' feature is not supported on this dual bank chip\")\n            }\n            (false, true) => {\n                dual_bank_memory.expect(\"The 'dual-bank' feature is not supported on this single bank chip\")\n            }\n            (false, false) => {\n                if METADATA.memory.len() != 1 {\n                    panic!(\n                        \"Chip supports single and dual bank configuration. No Cargo feature to select one is enabled. Use the 'single-bank' or 'dual-bank' feature to make your selection\"\n                    )\n                }\n                METADATA.memory[0]\n            }\n        }\n    };\n\n    let has_bkpsram = memory.iter().any(|m| m.name == \"BKPSRAM\");\n\n    // ========\n    // Generate singletons\n\n    let mut singletons: Vec<String> = Vec::new();\n\n    // Generate one singleton per pin\n    for p in METADATA.pins {\n        singletons.push(p.name.to_string());\n    }\n\n    cfgs.declare(\"backup_sram\");\n\n    if has_bkpsram {\n        singletons.push(\"BKPSRAM\".to_string());\n        cfgs.enable(\"backup_sram\")\n    }\n\n    // compile a map of peripherals\n    let peripheral_map: BTreeMap<&str, &Peripheral> = METADATA.peripherals.iter().map(|p| (p.name, p)).collect();\n\n    // generate one singleton per peripheral (with many exceptions...)\n    for p in METADATA.peripherals {\n        if let Some(r) = &p.registers {\n            if r.kind == \"adccommon\"\n                || r.kind == \"sai\"\n                || r.kind == \"ucpd\"\n                || r.kind == \"otg\"\n                || r.kind == \"octospi\"\n                || r.kind == \"xspi\"\n            {\n                // TODO: should we emit this for all peripherals? if so, we will need a list of all\n                // possible peripherals across all chips, so that we can declare the configs\n                // (replacing the hard-coded list of `peri_*` cfgs below)\n                cfgs.enable(format!(\"peri_{}\", p.name.to_ascii_lowercase()));\n            }\n\n            match r.kind {\n                // handled above\n                \"gpio\" => {}\n\n                // No singleton for these, the HAL handles them specially.\n                \"exti\" => {}\n\n                // We *shouldn't* have singletons for these, but the HAL currently requires\n                // singletons, for using with RccPeripheral to enable/disable clocks to them.\n                \"rcc\" => {\n                    for pin in p.pins {\n                        if pin.signal.starts_with(\"MCO\") {\n                            let name = pin.signal.replace('_', \"\").to_string();\n                            if !singletons.contains(&name) {\n                                cfgs.enable(name.to_ascii_lowercase());\n                                singletons.push(name);\n                            }\n                        }\n                    }\n                    singletons.push(p.name.to_string());\n                }\n\n                \"eth\" => {\n                    singletons.push(p.name.to_string());\n                    singletons.push(\"ETH_SMA\".to_string());\n                }\n                //\"dbgmcu\" => {}\n                //\"syscfg\" => {}\n                //\"dma\" => {}\n                //\"bdma\" => {}\n                //\"dmamux\" => {}\n\n                // For other peripherals, one singleton per peri\n                _ => singletons.push(p.name.to_string()),\n            }\n        }\n    }\n\n    cfgs.declare_all(&[\n        \"peri_adc1_common\",\n        \"peri_adc3_common\",\n        \"peri_adc12_common\",\n        \"peri_adc34_common\",\n        \"peri_sai1\",\n        \"peri_sai2\",\n        \"peri_sai3\",\n        \"peri_sai4\",\n        \"peri_ucpd1\",\n        \"peri_ucpd2\",\n        \"peri_usb_otg_fs\",\n        \"peri_usb_otg_hs\",\n        \"peri_octospi2\",\n        \"peri_xspi2\",\n    ]);\n    cfgs.declare_all(&[\"mco\", \"mco1\", \"mco2\"]);\n\n    // One singleton per EXTI line\n    for pin_num in 0..16 {\n        singletons.push(format!(\"EXTI{}\", pin_num));\n    }\n\n    // One singleton per DMA channel\n    for c in METADATA.dma_channels {\n        singletons.push(c.name.to_string());\n    }\n\n    let mut pin_set = std::collections::HashSet::new();\n    for p in METADATA.peripherals {\n        for pin in p.pins {\n            pin_set.insert(pin.pin);\n        }\n    }\n\n    struct SplitFeature {\n        feature_name: String,\n        pin_name_with_c: String,\n        #[cfg(feature = \"_split-pins-enabled\")]\n        pin_name_without_c: String,\n    }\n\n    // Extra analog switch pins available on most H7 chips\n    let split_features: Vec<SplitFeature> = vec![\n        #[cfg(feature = \"split-pa0\")]\n        SplitFeature {\n            feature_name: \"split-pa0\".to_string(),\n            pin_name_with_c: \"PA0_C\".to_string(),\n            pin_name_without_c: \"PA0\".to_string(),\n        },\n        #[cfg(feature = \"split-pa1\")]\n        SplitFeature {\n            feature_name: \"split-pa1\".to_string(),\n            pin_name_with_c: \"PA1_C\".to_string(),\n            pin_name_without_c: \"PA1\".to_string(),\n        },\n        #[cfg(feature = \"split-pc2\")]\n        SplitFeature {\n            feature_name: \"split-pc2\".to_string(),\n            pin_name_with_c: \"PC2_C\".to_string(),\n            pin_name_without_c: \"PC2\".to_string(),\n        },\n        #[cfg(feature = \"split-pc3\")]\n        SplitFeature {\n            feature_name: \"split-pc3\".to_string(),\n            pin_name_with_c: \"PC3_C\".to_string(),\n            pin_name_without_c: \"PC3\".to_string(),\n        },\n    ];\n\n    for split_feature in &split_features {\n        if pin_set.contains(split_feature.pin_name_with_c.as_str()) {\n            singletons.push(split_feature.pin_name_with_c.clone());\n        } else {\n            panic!(\n                \"'{}' feature invalid for this chip! No pin '{}' found.\\n\n                Found pins: {:#?}\",\n                split_feature.feature_name, split_feature.pin_name_with_c, pin_set\n            )\n        }\n    }\n\n    // ========\n    // Handle time-driver-XXXX features.\n\n    let time_driver = match env::vars()\n        .map(|(a, _)| a)\n        .filter(|x| x.starts_with(\"CARGO_FEATURE_TIME_DRIVER_\"))\n        .get_one()\n    {\n        Ok(x) => Some(\n            x.strip_prefix(\"CARGO_FEATURE_TIME_DRIVER_\")\n                .unwrap()\n                .to_ascii_lowercase(),\n        ),\n        Err(GetOneError::None) => None,\n        Err(GetOneError::Multiple) => panic!(\"Multiple time-driver-xxx Cargo features enabled\"),\n    };\n\n    let time_driver_singleton = match time_driver.as_ref().map(|x| x.as_ref()) {\n        None => \"\",\n        Some(\"tim1\") => \"TIM1\",\n        Some(\"tim2\") => \"TIM2\",\n        Some(\"tim3\") => \"TIM3\",\n        Some(\"tim4\") => \"TIM4\",\n        Some(\"tim5\") => \"TIM5\",\n        Some(\"tim8\") => \"TIM8\",\n        Some(\"tim9\") => \"TIM9\",\n        Some(\"tim12\") => \"TIM12\",\n        Some(\"tim15\") => \"TIM15\",\n        Some(\"tim20\") => \"TIM20\",\n        Some(\"tim21\") => \"TIM21\",\n        Some(\"tim22\") => \"TIM22\",\n        Some(\"tim23\") => \"TIM23\",\n        Some(\"tim24\") => \"TIM24\",\n        Some(\"lptim1\") => \"LPTIM1\",\n        Some(\"lptim2\") => \"LPTIM2\",\n        Some(\"lptim3\") => \"LPTIM3\",\n        Some(\"any\") => {\n            // Order of TIM candidators:\n            // 1. 2CH -> 2CH_CMP -> GP16 -> GP32 -> ADV\n            // 2. In same catagory: larger TIM number first\n            [\n                \"TIM22\", \"TIM21\", \"TIM12\", \"TIM9\",  // 2CH\n                \"TIM15\", // 2CH_CMP\n                \"TIM19\", \"TIM4\", \"TIM3\", // GP16\n                \"TIM24\", \"TIM23\", \"TIM5\", \"TIM2\", // GP32\n                \"TIM20\", \"TIM8\", \"TIM1\", //ADV\n            ]\n            .iter()\n            .find(|tim| singletons.contains(&tim.to_string())).expect(\"time-driver-any requested, but the chip doesn't have TIM1, TIM2, TIM3, TIM4, TIM5, TIM8, TIM9, TIM12, TIM15, TIM20, TIM21, TIM22, TIM23 or TIM24.\")\n        }\n        _ => panic!(\"unknown time_driver {:?}\", time_driver),\n    };\n\n    let time_driver_irq_decl = if !time_driver_singleton.is_empty() {\n        cfgs.enable(format!(\"time_driver_{}\", time_driver_singleton.to_lowercase()));\n\n        let Some(p) = peripheral_map.get(time_driver_singleton) else {\n            panic!(\"Tried to select {time_driver_singleton}, which is not available on this device\");\n        };\n        let irqs: BTreeSet<_> = p\n            .interrupts\n            .iter()\n            .filter(|i| i.signal == \"CC\" || i.signal == \"UP\" || i.signal == \"GLOBAL\")\n            .map(|i| i.interrupt.to_ascii_uppercase())\n            .collect();\n\n        irqs.iter()\n            .map(|i| {\n                let irq = format_ident!(\"{}\", i);\n                quote! {\n                    #[cfg(feature = \"rt\")]\n                    #[interrupt]\n                    fn #irq() {\n                        crate::time_driver::get_driver().on_interrupt();\n                    }\n                }\n            })\n            .collect()\n    } else {\n        TokenStream::new()\n    };\n\n    for tim in [\n        \"lptim1\", \"lptim2\", \"lptim3\", \"tim1\", \"tim2\", \"tim3\", \"tim4\", \"tim5\", \"tim8\", \"tim9\", \"tim12\", \"tim15\",\n        \"tim20\", \"tim21\", \"tim22\", \"tim23\", \"tim24\",\n    ] {\n        cfgs.declare(format!(\"time_driver_{}\", tim));\n    }\n\n    // ========\n    // Write singletons\n\n    let mut g = TokenStream::new();\n\n    let singleton_tokens: Vec<_> = singletons.iter().map(|s| format_ident!(\"{}\", s)).collect();\n\n    g.extend(quote! {\n        embassy_hal_internal::peripherals_definition!(#(#singleton_tokens),*);\n    });\n\n    let singleton_tokens: Vec<_> = singletons\n        .iter()\n        .filter(|s| *s != &time_driver_singleton.to_string())\n        .map(|s| format_ident!(\"{}\", s))\n        .collect();\n\n    g.extend(quote! {\n        embassy_hal_internal::peripherals_struct!(#(#singleton_tokens),*);\n    });\n\n    // ========\n    // Generate interrupt declarations\n\n    let mut exti2_tsc_shared_int_present: Option<stm32_metapac::metadata::Interrupt> = None;\n    let mut irqs = Vec::new();\n    for irq in METADATA.interrupts {\n        // The PAC doesn't ensure this is listed as the IRQ of EXTI2, so we must do so\n        if irq.name == \"EXTI2_TSC\" {\n            exti2_tsc_shared_int_present = Some(irq.clone())\n        }\n        irqs.push(format_ident!(\"{}\", irq.name));\n    }\n\n    g.extend(quote! {\n        embassy_hal_internal::interrupt_mod!(\n            #(\n                #irqs,\n            )*\n        );\n    });\n\n    g.extend(time_driver_irq_decl);\n\n    // ========\n    // Generate FLASH regions\n    cfgs.declare(\"flash\");\n    let mut has_flash = false;\n    if !chip_name.starts_with(\"stm32n6\") {\n        cfgs.enable(\"flash\");\n        has_flash = true;\n\n        let mut flash_regions = TokenStream::new();\n        let flash_memory_regions: Vec<_> = memory\n            .iter()\n            .filter(|x| x.kind == MemoryRegionKind::Flash && x.settings.is_some())\n            .collect();\n\n        let check_fb_mode = dual_bank_selected\n            && METADATA.peripherals.iter().any(|p| {\n                p.name == \"SYSCFG\"\n                    && p.registers.as_ref().is_some_and(|r| {\n                        r.ir.fieldsets\n                            .iter()\n                            .any(|f| f.name == \"Memrmp\" && f.fields.iter().any(|f| f.name == \"fb_mode\"))\n                    })\n            });\n\n        let mut bank_1_base = None;\n        let mut bank_2_base = None;\n        let mut otp_base = None;\n        for region in flash_memory_regions.iter() {\n            if region.name == \"BANK_1\" || region.name == \"BANK_1_REGION_1\" {\n                bank_1_base = Some(region.address);\n            } else if region.name == \"BANK_2\" || region.name == \"BANK_2_REGION_1\" {\n                bank_2_base = Some(region.address);\n            } else if region.name == \"OTP\" {\n                otp_base = Some(region.address);\n            }\n        }\n        let bank_1 = bank_1_base\n            .map(|a| quote!(#a))\n            .unwrap_or_else(|| quote!(panic!(\"Bank 1 not present\")));\n        let bank_2 = bank_2_base\n            .map(|a| quote!(#a))\n            .unwrap_or_else(|| quote!(panic!(\"Bank 2 not present\")));\n        let otp = otp_base\n            .map(|a| quote!(#a))\n            .unwrap_or_else(|| quote!(panic!(\"OTP not present\")));\n\n        let (swap_check, bank1, bank2) = if check_fb_mode {\n            (\n                quote! { let is_swapped = crate::pac::SYSCFG.memrmp().read().fb_mode(); },\n                quote! { if is_swapped { #bank_2 } else { #bank_1 } },\n                quote! { if is_swapped { #bank_1 } else { #bank_2 } },\n            )\n        } else {\n            (quote! {}, quote! { #bank_1 }, quote! { #bank_2 })\n        };\n\n        flash_regions.extend(quote! {\n            impl crate::flash::FlashBank {\n                /// Absolute base address.\n                pub fn base(&self) -> u32 {\n                    #swap_check\n                    match self {\n                        crate::flash::FlashBank::Bank1 => #bank1,\n                        crate::flash::FlashBank::Bank2 => #bank2,\n                        crate::flash::FlashBank::Otp => #otp,\n                    }\n                }\n            }\n        });\n\n        for region in flash_memory_regions.iter() {\n            let region_name = format_ident!(\"{}\", get_flash_region_name(region.name));\n            let (bank_variant, base) = if region.name.starts_with(\"BANK_1\") {\n                (\"Bank1\", bank_1_base.unwrap())\n            } else if region.name.starts_with(\"BANK_2\") {\n                (\"Bank2\", bank_2_base.unwrap())\n            } else if region.name == \"OTP\" {\n                (\"Otp\", otp_base.unwrap())\n            } else {\n                continue;\n            };\n            let bank_variant = format_ident!(\"{bank_variant}\");\n            let offset = region.address - base;\n            let size = region.size;\n            let settings = region.settings.as_ref().unwrap();\n            let erase_size = settings.erase_size;\n            let write_size = settings.write_size;\n            let erase_value = settings.erase_value;\n\n            flash_regions.extend(quote! {\n                pub const #region_name: crate::flash::FlashRegion = crate::flash::FlashRegion {\n                    bank: crate::flash::FlashBank::#bank_variant,\n                    offset: #offset,\n                    size: #size,\n                    erase_size: #erase_size,\n                    write_size: #write_size,\n                    erase_value: #erase_value,\n                    _ensure_internal: (),\n                };\n            });\n\n            let region_type = format_ident!(\"{}\", get_flash_region_type_name(region.name));\n            flash_regions.extend(quote! {\n            #[cfg(flash)]\n            pub struct #region_type<'d, MODE = crate::flash::Async>(pub &'static crate::flash::FlashRegion, pub(crate) embassy_hal_internal::Peri<'d, crate::peripherals::FLASH>, pub(crate) core::marker::PhantomData<MODE>);\n        });\n        }\n\n        let (fields, (inits, region_names)): (Vec<TokenStream>, (Vec<TokenStream>, Vec<Ident>)) = flash_memory_regions\n            .iter()\n            .map(|f| {\n                let region_name = get_flash_region_name(f.name);\n                let field_name = format_ident!(\"{}\", region_name.to_lowercase());\n                let field_type = format_ident!(\"{}\", get_flash_region_type_name(f.name));\n                let field = quote! {\n                    pub #field_name: #field_type<'d, MODE>\n                };\n                let region_name = format_ident!(\"{}\", region_name);\n                let init = quote! {\n                    #field_name: #field_type(&#region_name, unsafe { p.clone_unchecked()}, core::marker::PhantomData)\n                };\n\n                (field, (init, region_name))\n            })\n            .unzip();\n\n        let regions_len = flash_memory_regions.len();\n        flash_regions.extend(quote! {\n            #[cfg(flash)]\n            pub struct FlashLayout<'d, MODE = crate::flash::Async> {\n                #(#fields),*,\n                _mode: core::marker::PhantomData<MODE>,\n            }\n\n            #[cfg(flash)]\n            impl<'d, MODE> FlashLayout<'d, MODE> {\n                pub(crate) fn new(p: embassy_hal_internal::Peri<'d, crate::peripherals::FLASH>) -> Self {\n                    Self {\n                        #(#inits),*,\n                        _mode: core::marker::PhantomData,\n                    }\n                }\n            }\n\n            pub const FLASH_REGIONS: [&crate::flash::FlashRegion; #regions_len] = [\n                #(&#region_names),*\n            ];\n        });\n\n        let max_erase_size = flash_memory_regions\n            .iter()\n            .map(|region| region.settings.as_ref().unwrap().erase_size)\n            .max()\n            .unwrap();\n\n        g.extend(quote! { pub const MAX_ERASE_SIZE: usize = #max_erase_size as usize; });\n\n        g.extend(quote! { pub mod flash_regions { #flash_regions } });\n    }\n\n    // ========\n    // Extract the rcc registers\n\n    let rcc_registers = METADATA\n        .peripherals\n        .iter()\n        .filter_map(|p| p.registers.as_ref())\n        .find(|r| r.kind == \"rcc\")\n        .unwrap();\n    let rcc_block = rcc_registers.ir.blocks.iter().find(|b| b.name == \"Rcc\").unwrap();\n\n    // ========\n    // Generate RccPeripheral impls\n\n    // count how many times each xxENR field is used, to enable refcounting if used more than once.\n    let mut rcc_field_count: HashMap<_, usize> = HashMap::new();\n    for p in METADATA.peripherals {\n        if let Some(rcc) = &p.rcc {\n            let en = rcc.enable.as_ref().unwrap();\n            *rcc_field_count.entry((en.register, en.field)).or_insert(0) += 1;\n        }\n    }\n\n    struct ClockGen<'a> {\n        rcc_registers: &'a PeripheralRegisters,\n        chained_muxes: HashMap<&'a str, &'a PeripheralRccRegister>,\n\n        clock_names: BTreeSet<String>,\n        muxes: BTreeSet<(Ident, Ident, Ident)>,\n    }\n\n    let mut clock_gen = ClockGen {\n        rcc_registers,\n        chained_muxes: HashMap::new(),\n\n        clock_names: BTreeSet::new(),\n        muxes: BTreeSet::new(),\n    };\n    if chip_name.starts_with(\"stm32h5\") {\n        clock_gen.chained_muxes.insert(\n            \"PER\",\n            &PeripheralRccRegister {\n                register: \"CCIPR5\",\n                field: \"PERSEL\",\n            },\n        );\n    }\n\n    if chip_name.starts_with(\"stm32h7r\") || chip_name.starts_with(\"stm32h7s\") {\n        clock_gen.chained_muxes.insert(\n            \"PER\",\n            &PeripheralRccRegister {\n                register: \"AHBPERCKSELR\",\n                field: \"PERSEL\",\n            },\n        );\n    } else if chip_name.starts_with(\"stm32h7\") {\n        clock_gen.chained_muxes.insert(\n            \"PER\",\n            &PeripheralRccRegister {\n                register: \"D1CCIPR\",\n                field: \"PERSEL\",\n            },\n        );\n    }\n    if chip_name.starts_with(\"stm32u5\") || chip_name.starts_with(\"stm32U3\") {\n        clock_gen.chained_muxes.insert(\n            \"ICLK\",\n            &PeripheralRccRegister {\n                register: \"CCIPR1\",\n                field: \"ICLKSEL\",\n            },\n        );\n    }\n    if chip_name.starts_with(\"stm32wb\") && !chip_name.starts_with(\"stm32wba\") {\n        clock_gen.chained_muxes.insert(\n            \"CLK48\",\n            &PeripheralRccRegister {\n                register: \"CCIPR\",\n                field: \"CLK48SEL\",\n            },\n        );\n        clock_gen.chained_muxes.insert(\n            \"RFWKP\",\n            &PeripheralRccRegister {\n                register: \"CSR\",\n                field: \"RFWKPSEL\",\n            },\n        );\n    }\n    if chip_name.starts_with(\"stm32f7\") {\n        clock_gen.chained_muxes.insert(\n            \"CLK48\",\n            &PeripheralRccRegister {\n                register: \"DCKCFGR2\",\n                field: \"CLK48SEL\",\n            },\n        );\n    }\n    if chip_name.starts_with(\"stm32f4\") && !chip_name.starts_with(\"stm32f410\") {\n        clock_gen.chained_muxes.insert(\n            \"CLK48\",\n            &PeripheralRccRegister {\n                register: \"DCKCFGR\",\n                field: \"CLK48SEL\",\n            },\n        );\n    }\n\n    impl<'a> ClockGen<'a> {\n        fn parse_mul_div(name: &str) -> (&str, Frac) {\n            if name == \"hse_div_rtcpre\" {\n                return (name, Frac { num: 1, denom: 1 });\n            }\n\n            if let Some(i) = name.find(\"_div_\") {\n                let n = &name[..i];\n                let val: u32 = name[i + 5..].parse().unwrap();\n                (n, Frac { num: 1, denom: val })\n            } else if let Some(i) = name.find(\"_mul_\") {\n                let n = &name[..i];\n                let val: u32 = name[i + 5..].parse().unwrap();\n                (n, Frac { num: val, denom: 1 })\n            } else {\n                (name, Frac { num: 1, denom: 1 })\n            }\n        }\n\n        fn gen_clock(&mut self, peripheral: &str, name: &str) -> TokenStream {\n            let name = name.to_ascii_lowercase();\n            let (name, frac) = Self::parse_mul_div(&name);\n            let clock_name = format_ident!(\"{}\", name);\n            self.clock_names.insert(name.to_string());\n\n            let mut muldiv = quote!();\n            if frac.num != 1 {\n                let val = frac.num;\n                muldiv.extend(quote!(* #val));\n            }\n            if frac.denom != 1 {\n                let val = frac.denom;\n                muldiv.extend(quote!(/ #val));\n            }\n            quote!(unsafe {\n                unwrap!(\n                    crate::rcc::get_freqs().#clock_name.to_hertz(),\n                    \"peripheral '{}' is configured to use the '{}' clock, which is not running. \\\n                    Either enable it in 'config.rcc' or change 'config.rcc.mux' to use another clock\",\n                    #peripheral,\n                    #name\n                )\n                #muldiv\n            })\n        }\n\n        fn gen_mux(&mut self, peripheral: &str, mux: &PeripheralRccRegister) -> TokenStream {\n            let ir = &self.rcc_registers.ir;\n            let fieldset_name = mux.register.to_ascii_lowercase();\n            let fieldset = ir\n                .fieldsets\n                .iter()\n                .find(|i| i.name.eq_ignore_ascii_case(&fieldset_name))\n                .unwrap();\n            let field_name = mux.field.to_ascii_lowercase();\n            let field = fieldset.fields.iter().find(|i| i.name == field_name).unwrap();\n            let enum_name = field.enumm.unwrap();\n            let enumm = ir.enums.iter().find(|i| i.name == enum_name).unwrap();\n\n            let fieldset_name = format_ident!(\"{}\", fieldset_name);\n            let field_name = format_ident!(\"{}\", field_name);\n            let enum_name = format_ident!(\"{}\", enum_name);\n\n            self.muxes\n                .insert((fieldset_name.clone(), field_name.clone(), enum_name.clone()));\n\n            let mut match_arms = TokenStream::new();\n\n            for v in enumm.variants.iter().filter(|v| v.name != \"DISABLE\") {\n                let variant_name = format_ident!(\"{}\", v.name);\n                let expr = if let Some(mux) = self.chained_muxes.get(&v.name) {\n                    self.gen_mux(peripheral, mux)\n                } else {\n                    self.gen_clock(peripheral, v.name)\n                };\n                match_arms.extend(quote! {\n                    crate::pac::rcc::vals::#enum_name::#variant_name => #expr,\n                });\n            }\n\n            quote! {\n                match crate::pac::RCC.#fieldset_name().read().#field_name() {\n                    #match_arms\n                    #[allow(unreachable_patterns)]\n                    _ => panic!(\n                        \"attempted to use peripheral '{}' but its clock mux is not set to a valid \\\n                         clock. Change 'config.rcc.mux' to another clock.\",\n                        #peripheral\n                    )\n                }\n            }\n        }\n    }\n\n    let mut refcount_idxs = HashMap::new();\n\n    for p in METADATA.peripherals {\n        if !singletons.contains(&p.name.to_string()) {\n            continue;\n        }\n\n        if let Some(rcc) = &p.rcc {\n            let rst_reg = rcc.reset.as_ref();\n            let en_reg = rcc.enable.as_ref().unwrap();\n            let pname = format_ident!(\"{}\", p.name);\n\n            let get_offset_and_bit = |reg: &PeripheralRccRegister| -> TokenStream {\n                let reg_offset = rcc_block\n                    .items\n                    .iter()\n                    .find(|i| i.name.eq_ignore_ascii_case(reg.register))\n                    .unwrap()\n                    .byte_offset;\n                let reg_offset: u8 = (reg_offset / 4).try_into().unwrap();\n\n                let bit_offset = &rcc_registers\n                    .ir\n                    .fieldsets\n                    .iter()\n                    .find(|i| i.name.eq_ignore_ascii_case(reg.register))\n                    .unwrap()\n                    .fields\n                    .iter()\n                    .find(|i| i.name.eq_ignore_ascii_case(reg.field))\n                    .unwrap()\n                    .bit_offset;\n                let BitOffset::Regular(bit_offset) = bit_offset else {\n                    panic!(\"cursed bit offset\")\n                };\n                let bit_offset: u8 = bit_offset.offset.try_into().unwrap();\n\n                quote! { (#reg_offset, #bit_offset) }\n            };\n\n            let reset_offset_and_bit = match rst_reg {\n                Some(rst_reg) => {\n                    let reset_offset_and_bit = get_offset_and_bit(rst_reg);\n                    quote! { Some(#reset_offset_and_bit) }\n                }\n                None => quote! { None },\n            };\n            let enable_offset_and_bit = get_offset_and_bit(en_reg);\n\n            let needs_refcount = *rcc_field_count.get(&(en_reg.register, en_reg.field)).unwrap() > 1;\n            let refcount_idx = if needs_refcount {\n                let next_refcount_idx = refcount_idxs.len() as u8;\n                let refcount_idx = *refcount_idxs\n                    .entry((en_reg.register, en_reg.field))\n                    .or_insert(next_refcount_idx);\n                quote! { Some(#refcount_idx) }\n            } else {\n                quote! { None }\n            };\n\n            let clock_frequency = match &rcc.kernel_clock {\n                PeripheralRccKernelClock::Mux(mux) => clock_gen.gen_mux(p.name, mux),\n                PeripheralRccKernelClock::Clock(clock) => clock_gen.gen_clock(p.name, clock),\n            };\n\n            let bus_clock_frequency = clock_gen.gen_clock(p.name, &rcc.bus_clock);\n\n            // A refcount leak can result if the same field is shared by peripherals with different stop modes\n            // This condition should be checked in stm32-data\n            let stop_mode = match rcc.stop_mode {\n                StopMode::Standby => quote! { crate::rcc::StopMode::Standby },\n                StopMode::Stop2 => quote! { crate::rcc::StopMode::Stop2 },\n                StopMode::Stop1 => quote! { crate::rcc::StopMode::Stop1 },\n            };\n\n            g.extend(quote! {\n                impl crate::rcc::SealedRccPeripheral for peripherals::#pname {\n                    fn frequency() -> crate::time::Hertz {\n                        #clock_frequency\n                    }\n                    fn bus_frequency() -> crate::time::Hertz {\n                        #bus_clock_frequency\n                    }\n\n                    const RCC_INFO: crate::rcc::RccInfo = unsafe {\n                        crate::rcc::RccInfo::new(\n                            #reset_offset_and_bit,\n                            #enable_offset_and_bit,\n                            #refcount_idx,\n                            #[cfg(feature = \"low-power\")]\n                            #stop_mode,\n                        )\n                    };\n                }\n\n                impl crate::rcc::RccPeripheral for peripherals::#pname {}\n            });\n        }\n    }\n\n    g.extend({\n        let refcounts_len = refcount_idxs.len();\n        let refcount_zeros: TokenStream = refcount_idxs.iter().map(|_| quote! { 0u8, }).collect();\n        quote! {\n            pub(crate) static mut REFCOUNTS: [u8; #refcounts_len] = [#refcount_zeros];\n        }\n    });\n\n    let struct_fields: Vec<_> = clock_gen\n        .muxes\n        .iter()\n        .map(|(_fieldset, fieldname, enum_name)| {\n            quote! {\n                pub #fieldname: #enum_name\n            }\n        })\n        .collect();\n\n    let mut inits = TokenStream::new();\n    for fieldset in clock_gen\n        .muxes\n        .iter()\n        .map(|(f, _, _)| f)\n        .collect::<BTreeSet<_>>()\n        .into_iter()\n    {\n        let setters: Vec<_> = clock_gen\n            .muxes\n            .iter()\n            .filter(|(f, _, _)| f == fieldset)\n            .map(|(_, fieldname, _)| {\n                let setter = format_ident!(\"set_{}\", fieldname);\n                quote! {\n                    w.#setter(self.#fieldname);\n                }\n            })\n            .collect();\n\n        inits.extend(quote! {\n            crate::pac::RCC.#fieldset().modify(|w| {\n                #(#setters)*\n            });\n        })\n    }\n\n    let enum_names: BTreeSet<_> = clock_gen.muxes.iter().map(|(_, _, enum_name)| enum_name).collect();\n\n    g.extend(quote! {\n        pub mod mux {\n            #(pub use crate::pac::rcc::vals::#enum_names as #enum_names; )*\n\n            #[derive(Clone, Copy)]\n            #[non_exhaustive]\n            pub struct ClockMux {\n                #( #struct_fields, )*\n            }\n\n            impl ClockMux {\n                pub(crate) const fn default() -> Self {\n                    // safety: zero value is valid for all PAC enums.\n                    unsafe { ::core::mem::zeroed() }\n                }\n            }\n\n            impl Default for ClockMux {\n                fn default() -> Self {\n                    Self::default()\n                }\n            }\n\n            impl ClockMux {\n                pub(crate) fn init(&self) {\n                    #inits\n                }\n            }\n        }\n    });\n\n    // Generate RCC\n    clock_gen.clock_names.insert(\"sys\".to_string());\n    clock_gen.clock_names.insert(\"rtc\".to_string());\n\n    // STM32F2/F4/F7 SPI in I2S mode receives a clock input from the dedicated I2S PLL.\n    // For this, there is an additional clock MUX, which is not present in other\n    // peripherals and does not fit the current RCC structure of stm32-data.\n    if (chip_name.starts_with(\"stm32f4\") && !chip_name.starts_with(\"stm32f410\"))\n        || chip_name.starts_with(\"stm32f2\")\n        || chip_name.starts_with(\"stm32f7\")\n    {\n        clock_gen.clock_names.insert(\"plli2s1_p\".to_string());\n        clock_gen.clock_names.insert(\"plli2s1_q\".to_string());\n        clock_gen.clock_names.insert(\"plli2s1_r\".to_string());\n    }\n\n    let clock_idents: Vec<_> = clock_gen.clock_names.iter().map(|n| format_ident!(\"{}\", n)).collect();\n    g.extend(quote! {\n        #[derive(Clone, Copy, Debug)]\n        #[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\n        #[repr(C)]\n        pub struct Clocks {\n            #(\n                pub #clock_idents: crate::time::MaybeHertz,\n            )*\n        }\n    });\n\n    let clocks_macro = quote!(\n        macro_rules! set_clocks {\n            ($($(#[$m:meta])* $k:ident: $v:expr,)*) => {\n                {\n                    #[allow(unused)]\n                    struct Temp {\n                        $($(#[$m])* $k: Option<crate::time::Hertz>,)*\n                    }\n                    let all = Temp {\n                        $($(#[$m])* $k: $v,)*\n                    };\n                    crate::rcc::set_freqs(crate::rcc::Clocks {\n                        #( #clock_idents: all.#clock_idents.into(), )*\n                    });\n                }\n            };\n        }\n    );\n\n    // ========\n    // Generate fns to enable GPIO, DMA in RCC\n\n    for kind in [\"mdma\", \"dma\", \"bdma\", \"dmamux\", \"gpdma\", \"gpio\"] {\n        let mut gg = TokenStream::new();\n\n        for p in METADATA.peripherals {\n            if p.registers.is_some() && p.registers.as_ref().unwrap().kind == kind {\n                if let Some(rcc) = &p.rcc {\n                    let en = rcc.enable.as_ref().unwrap();\n                    let en_reg = format_ident!(\"{}\", en.register.to_ascii_lowercase());\n                    let set_en_field = format_ident!(\"set_{}\", en.field.to_ascii_lowercase());\n                    gg.extend(quote! {\n                        crate::pac::RCC.#en_reg().modify(|w| w.#set_en_field(true));\n                    });\n                    // enable for both cores or if the primary core goes in stop mode devices become unavailable!\n                    // particularly problematic for GPIOs and DMA\n                    if chip_name.starts_with(\"stm32wl5\") {\n                        // second core clock enable registers start with \"c2\"\n                        let en_reg = format_ident!(\"c2{}\", en.register.to_ascii_lowercase());\n                        gg.extend(quote! {\n                            crate::pac::RCC.#en_reg().modify(|w| w.#set_en_field(true));\n                        });\n                    }\n                }\n            }\n        }\n\n        if cfg!(feature = \"gpio-init-analog\") && kind == \"gpio\" {\n            gg.extend(quote! {init_gpio_analog();});\n        }\n\n        let fname = format_ident!(\"init_{}\", kind);\n        g.extend(quote! {\n            pub unsafe fn #fname(){\n                #gg\n            }\n        })\n    }\n\n    // ========\n    // Generate pin_trait_impl!\n\n    #[rustfmt::skip]\n    let signals: HashMap<_, _> = [\n        // (kind, signal) => trait\n        ((\"ucpd\", \"CC1\"), quote!(crate::ucpd::Cc1Pin)),\n        ((\"ucpd\", \"CC2\"), quote!(crate::ucpd::Cc2Pin)),\n        ((\"usart\", \"TX\"), quote!(crate::usart::TxPin)),\n        ((\"usart\", \"RX\"), quote!(crate::usart::RxPin)),\n        ((\"usart\", \"CTS\"), quote!(crate::usart::CtsPin)),\n        ((\"usart\", \"RTS\"), quote!(crate::usart::RtsPin)),\n        ((\"usart\", \"CK\"), quote!(crate::usart::CkPin)),\n        ((\"usart\", \"DE\"), quote!(crate::usart::DePin)),\n        ((\"lpuart\", \"TX\"), quote!(crate::usart::TxPin)),\n        ((\"lpuart\", \"RX\"), quote!(crate::usart::RxPin)),\n        ((\"lpuart\", \"CTS\"), quote!(crate::usart::CtsPin)),\n        ((\"lpuart\", \"RTS\"), quote!(crate::usart::RtsPin)),\n        ((\"lpuart\", \"CK\"), quote!(crate::usart::CkPin)),\n        ((\"lpuart\", \"DE\"), quote!(crate::usart::DePin)),\n        ((\"sai\", \"SCK_A\"), quote!(crate::sai::SckPin<A>)),\n        ((\"sai\", \"SCK_B\"), quote!(crate::sai::SckPin<B>)),\n        ((\"sai\", \"FS_A\"), quote!(crate::sai::FsPin<A>)),\n        ((\"sai\", \"FS_B\"), quote!(crate::sai::FsPin<B>)),\n        ((\"sai\", \"SD_A\"), quote!(crate::sai::SdPin<A>)),\n        ((\"sai\", \"SD_B\"), quote!(crate::sai::SdPin<B>)),\n        ((\"sai\", \"MCLK_A\"), quote!(crate::sai::MclkPin<A>)),\n        ((\"sai\", \"MCLK_B\"), quote!(crate::sai::MclkPin<B>)),\n        ((\"sai\", \"WS\"), quote!(crate::sai::WsPin)),\n        ((\"spi\", \"SCK\"), quote!(crate::spi::SckPin)),\n        ((\"spi\", \"MOSI\"), quote!(crate::spi::MosiPin)),\n        ((\"spi\", \"MISO\"), quote!(crate::spi::MisoPin)),\n        ((\"spi\", \"NSS\"), quote!(crate::spi::CsPin)),\n        ((\"spi\", \"I2S_MCK\"), quote!(crate::spi::MckPin)),\n        ((\"spi\", \"I2S_CK\"), quote!(crate::spi::CkPin)),\n        ((\"spi\", \"I2S_WS\"), quote!(crate::spi::WsPin)),\n        ((\"spi\", \"I2S_SD\"), quote!(crate::spi::I2sSdPin)),\n        ((\"spi\", \"I2S_SDI\"), quote!(crate::spi::I2sSdPin)),\n        ((\"spi\", \"I2S_SDO\"), quote!(crate::spi::I2sSdPin)),\n        ((\"i2c\", \"SDA\"), quote!(crate::i2c::SdaPin)),\n        ((\"i2c\", \"SCL\"), quote!(crate::i2c::SclPin)),\n        ((\"rcc\", \"MCO_1\"), quote!(crate::rcc::McoPin)),\n        ((\"rcc\", \"MCO_2\"), quote!(crate::rcc::McoPin)),\n        ((\"rcc\", \"MCO\"), quote!(crate::rcc::McoPin)),\n        ((\"dcmi\", \"D0\"), quote!(crate::dcmi::D0Pin)),\n        ((\"dcmi\", \"D1\"), quote!(crate::dcmi::D1Pin)),\n        ((\"dcmi\", \"D2\"), quote!(crate::dcmi::D2Pin)),\n        ((\"dcmi\", \"D3\"), quote!(crate::dcmi::D3Pin)),\n        ((\"dcmi\", \"D4\"), quote!(crate::dcmi::D4Pin)),\n        ((\"dcmi\", \"D5\"), quote!(crate::dcmi::D5Pin)),\n        ((\"dcmi\", \"D6\"), quote!(crate::dcmi::D6Pin)),\n        ((\"dcmi\", \"D7\"), quote!(crate::dcmi::D7Pin)),\n        ((\"dcmi\", \"D8\"), quote!(crate::dcmi::D8Pin)),\n        ((\"dcmi\", \"D9\"), quote!(crate::dcmi::D9Pin)),\n        ((\"dcmi\", \"D10\"), quote!(crate::dcmi::D10Pin)),\n        ((\"dcmi\", \"D11\"), quote!(crate::dcmi::D11Pin)),\n        ((\"dcmi\", \"D12\"), quote!(crate::dcmi::D12Pin)),\n        ((\"dcmi\", \"D13\"), quote!(crate::dcmi::D13Pin)),\n        ((\"dcmi\", \"HSYNC\"), quote!(crate::dcmi::HSyncPin)),\n        ((\"dcmi\", \"VSYNC\"), quote!(crate::dcmi::VSyncPin)),\n        ((\"dcmi\", \"PIXCLK\"), quote!(crate::dcmi::PixClkPin)),\n        ((\"dsihost\", \"TE\"), quote!(crate::dsihost::TePin)),\n        ((\"ltdc\", \"CLK\"), quote!(crate::ltdc::ClkPin)),\n        ((\"ltdc\", \"HSYNC\"), quote!(crate::ltdc::HsyncPin)),\n        ((\"ltdc\", \"VSYNC\"), quote!(crate::ltdc::VsyncPin)),\n        ((\"ltdc\", \"DE\"), quote!(crate::ltdc::DePin)),\n        ((\"ltdc\", \"R0\"), quote!(crate::ltdc::R0Pin)),\n        ((\"ltdc\", \"R1\"), quote!(crate::ltdc::R1Pin)),\n        ((\"ltdc\", \"R2\"), quote!(crate::ltdc::R2Pin)),\n        ((\"ltdc\", \"R3\"), quote!(crate::ltdc::R3Pin)),\n        ((\"ltdc\", \"R4\"), quote!(crate::ltdc::R4Pin)),\n        ((\"ltdc\", \"R5\"), quote!(crate::ltdc::R5Pin)),\n        ((\"ltdc\", \"R6\"), quote!(crate::ltdc::R6Pin)),\n        ((\"ltdc\", \"R7\"), quote!(crate::ltdc::R7Pin)),\n        ((\"ltdc\", \"G0\"), quote!(crate::ltdc::G0Pin)),\n        ((\"ltdc\", \"G1\"), quote!(crate::ltdc::G1Pin)),\n        ((\"ltdc\", \"G2\"), quote!(crate::ltdc::G2Pin)),\n        ((\"ltdc\", \"G3\"), quote!(crate::ltdc::G3Pin)),\n        ((\"ltdc\", \"G4\"), quote!(crate::ltdc::G4Pin)),\n        ((\"ltdc\", \"G5\"), quote!(crate::ltdc::G5Pin)),\n        ((\"ltdc\", \"G6\"), quote!(crate::ltdc::G6Pin)),\n        ((\"ltdc\", \"G7\"), quote!(crate::ltdc::G7Pin)),\n        ((\"ltdc\", \"B0\"), quote!(crate::ltdc::B0Pin)),\n        ((\"ltdc\", \"B1\"), quote!(crate::ltdc::B1Pin)),\n        ((\"ltdc\", \"B2\"), quote!(crate::ltdc::B2Pin)),\n        ((\"ltdc\", \"B3\"), quote!(crate::ltdc::B3Pin)),\n        ((\"ltdc\", \"B4\"), quote!(crate::ltdc::B4Pin)),\n        ((\"ltdc\", \"B5\"), quote!(crate::ltdc::B5Pin)),\n        ((\"ltdc\", \"B6\"), quote!(crate::ltdc::B6Pin)),\n        ((\"ltdc\", \"B7\"), quote!(crate::ltdc::B7Pin)),\n        ((\"usb\", \"DP\"), quote!(crate::usb::DpPin)),\n        ((\"usb\", \"DM\"), quote!(crate::usb::DmPin)),\n        ((\"usb\", \"SOF\"), quote!(crate::usb::SofPin)),\n        ((\"otg\", \"DP\"), quote!(crate::usb::DpPin)),\n        ((\"otg\", \"DM\"), quote!(crate::usb::DmPin)),\n        ((\"otg\", \"ULPI_CK\"), quote!(crate::usb::UlpiClkPin)),\n        ((\"otg\", \"ULPI_DIR\"), quote!(crate::usb::UlpiDirPin)),\n        ((\"otg\", \"ULPI_NXT\"), quote!(crate::usb::UlpiNxtPin)),\n        ((\"otg\", \"ULPI_STP\"), quote!(crate::usb::UlpiStpPin)),\n        ((\"otg\", \"ULPI_D0\"), quote!(crate::usb::UlpiD0Pin)),\n        ((\"otg\", \"ULPI_D1\"), quote!(crate::usb::UlpiD1Pin)),\n        ((\"otg\", \"ULPI_D2\"), quote!(crate::usb::UlpiD2Pin)),\n        ((\"otg\", \"ULPI_D3\"), quote!(crate::usb::UlpiD3Pin)),\n        ((\"otg\", \"ULPI_D4\"), quote!(crate::usb::UlpiD4Pin)),\n        ((\"otg\", \"ULPI_D5\"), quote!(crate::usb::UlpiD5Pin)),\n        ((\"otg\", \"ULPI_D6\"), quote!(crate::usb::UlpiD6Pin)),\n        ((\"otg\", \"ULPI_D7\"), quote!(crate::usb::UlpiD7Pin)),\n        ((\"can\", \"TX\"), quote!(crate::can::TxPin)),\n        ((\"can\", \"RX\"), quote!(crate::can::RxPin)),\n        ((\"eth\", \"REF_CLK\"), quote!(crate::eth::RefClkPin)),\n        ((\"eth\", \"RX_CLK\"), quote!(crate::eth::RXClkPin)),\n        ((\"eth\", \"TX_CLK\"), quote!(crate::eth::TXClkPin)),\n        ((\"eth\", \"MDIO\"), quote!(crate::eth::MDIOPin)),\n        ((\"eth\", \"MDC\"), quote!(crate::eth::MDCPin)),\n        ((\"eth\", \"CRS_DV\"), quote!(crate::eth::CRSPin)),\n        ((\"eth\", \"RX_DV\"), quote!(crate::eth::RXDVPin)),\n        ((\"eth\", \"RXD0\"), quote!(crate::eth::RXD0Pin)),\n        ((\"eth\", \"RXD1\"), quote!(crate::eth::RXD1Pin)),\n        ((\"eth\", \"RXD2\"), quote!(crate::eth::RXD2Pin)),\n        ((\"eth\", \"RXD3\"), quote!(crate::eth::RXD3Pin)),\n        ((\"eth\", \"TXD0\"), quote!(crate::eth::TXD0Pin)),\n        ((\"eth\", \"TXD1\"), quote!(crate::eth::TXD1Pin)),\n        ((\"eth\", \"TXD2\"), quote!(crate::eth::TXD2Pin)),\n        ((\"eth\", \"TXD3\"), quote!(crate::eth::TXD3Pin)),\n        ((\"eth\", \"TX_EN\"), quote!(crate::eth::TXEnPin)),\n        ((\"fmc\", \"A0\"), quote!(crate::fmc::A0Pin)),\n        ((\"fmc\", \"A1\"), quote!(crate::fmc::A1Pin)),\n        ((\"fmc\", \"A2\"), quote!(crate::fmc::A2Pin)),\n        ((\"fmc\", \"A3\"), quote!(crate::fmc::A3Pin)),\n        ((\"fmc\", \"A4\"), quote!(crate::fmc::A4Pin)),\n        ((\"fmc\", \"A5\"), quote!(crate::fmc::A5Pin)),\n        ((\"fmc\", \"A6\"), quote!(crate::fmc::A6Pin)),\n        ((\"fmc\", \"A7\"), quote!(crate::fmc::A7Pin)),\n        ((\"fmc\", \"A8\"), quote!(crate::fmc::A8Pin)),\n        ((\"fmc\", \"A9\"), quote!(crate::fmc::A9Pin)),\n        ((\"fmc\", \"A10\"), quote!(crate::fmc::A10Pin)),\n        ((\"fmc\", \"A11\"), quote!(crate::fmc::A11Pin)),\n        ((\"fmc\", \"A12\"), quote!(crate::fmc::A12Pin)),\n        ((\"fmc\", \"A13\"), quote!(crate::fmc::A13Pin)),\n        ((\"fmc\", \"A14\"), quote!(crate::fmc::A14Pin)),\n        ((\"fmc\", \"A15\"), quote!(crate::fmc::A15Pin)),\n        ((\"fmc\", \"A16\"), quote!(crate::fmc::A16Pin)),\n        ((\"fmc\", \"A17\"), quote!(crate::fmc::A17Pin)),\n        ((\"fmc\", \"A18\"), quote!(crate::fmc::A18Pin)),\n        ((\"fmc\", \"A19\"), quote!(crate::fmc::A19Pin)),\n        ((\"fmc\", \"A20\"), quote!(crate::fmc::A20Pin)),\n        ((\"fmc\", \"A21\"), quote!(crate::fmc::A21Pin)),\n        ((\"fmc\", \"A22\"), quote!(crate::fmc::A22Pin)),\n        ((\"fmc\", \"A23\"), quote!(crate::fmc::A23Pin)),\n        ((\"fmc\", \"A24\"), quote!(crate::fmc::A24Pin)),\n        ((\"fmc\", \"A25\"), quote!(crate::fmc::A25Pin)),\n        ((\"fmc\", \"D0\"), quote!(crate::fmc::D0Pin)),\n        ((\"fmc\", \"D1\"), quote!(crate::fmc::D1Pin)),\n        ((\"fmc\", \"D2\"), quote!(crate::fmc::D2Pin)),\n        ((\"fmc\", \"D3\"), quote!(crate::fmc::D3Pin)),\n        ((\"fmc\", \"D4\"), quote!(crate::fmc::D4Pin)),\n        ((\"fmc\", \"D5\"), quote!(crate::fmc::D5Pin)),\n        ((\"fmc\", \"D6\"), quote!(crate::fmc::D6Pin)),\n        ((\"fmc\", \"D7\"), quote!(crate::fmc::D7Pin)),\n        ((\"fmc\", \"D8\"), quote!(crate::fmc::D8Pin)),\n        ((\"fmc\", \"D9\"), quote!(crate::fmc::D9Pin)),\n        ((\"fmc\", \"D10\"), quote!(crate::fmc::D10Pin)),\n        ((\"fmc\", \"D11\"), quote!(crate::fmc::D11Pin)),\n        ((\"fmc\", \"D12\"), quote!(crate::fmc::D12Pin)),\n        ((\"fmc\", \"D13\"), quote!(crate::fmc::D13Pin)),\n        ((\"fmc\", \"D14\"), quote!(crate::fmc::D14Pin)),\n        ((\"fmc\", \"D15\"), quote!(crate::fmc::D15Pin)),\n        ((\"fmc\", \"D16\"), quote!(crate::fmc::D16Pin)),\n        ((\"fmc\", \"D17\"), quote!(crate::fmc::D17Pin)),\n        ((\"fmc\", \"D18\"), quote!(crate::fmc::D18Pin)),\n        ((\"fmc\", \"D19\"), quote!(crate::fmc::D19Pin)),\n        ((\"fmc\", \"D20\"), quote!(crate::fmc::D20Pin)),\n        ((\"fmc\", \"D21\"), quote!(crate::fmc::D21Pin)),\n        ((\"fmc\", \"D22\"), quote!(crate::fmc::D22Pin)),\n        ((\"fmc\", \"D23\"), quote!(crate::fmc::D23Pin)),\n        ((\"fmc\", \"D24\"), quote!(crate::fmc::D24Pin)),\n        ((\"fmc\", \"D25\"), quote!(crate::fmc::D25Pin)),\n        ((\"fmc\", \"D26\"), quote!(crate::fmc::D26Pin)),\n        ((\"fmc\", \"D27\"), quote!(crate::fmc::D27Pin)),\n        ((\"fmc\", \"D28\"), quote!(crate::fmc::D28Pin)),\n        ((\"fmc\", \"D29\"), quote!(crate::fmc::D29Pin)),\n        ((\"fmc\", \"D30\"), quote!(crate::fmc::D30Pin)),\n        ((\"fmc\", \"D31\"), quote!(crate::fmc::D31Pin)),\n        ((\"fmc\", \"DA0\"), quote!(crate::fmc::DA0Pin)),\n        ((\"fmc\", \"DA1\"), quote!(crate::fmc::DA1Pin)),\n        ((\"fmc\", \"DA2\"), quote!(crate::fmc::DA2Pin)),\n        ((\"fmc\", \"DA3\"), quote!(crate::fmc::DA3Pin)),\n        ((\"fmc\", \"DA4\"), quote!(crate::fmc::DA4Pin)),\n        ((\"fmc\", \"DA5\"), quote!(crate::fmc::DA5Pin)),\n        ((\"fmc\", \"DA6\"), quote!(crate::fmc::DA6Pin)),\n        ((\"fmc\", \"DA7\"), quote!(crate::fmc::DA7Pin)),\n        ((\"fmc\", \"DA8\"), quote!(crate::fmc::DA8Pin)),\n        ((\"fmc\", \"DA9\"), quote!(crate::fmc::DA9Pin)),\n        ((\"fmc\", \"DA10\"), quote!(crate::fmc::DA10Pin)),\n        ((\"fmc\", \"DA11\"), quote!(crate::fmc::DA11Pin)),\n        ((\"fmc\", \"DA12\"), quote!(crate::fmc::DA12Pin)),\n        ((\"fmc\", \"DA13\"), quote!(crate::fmc::DA13Pin)),\n        ((\"fmc\", \"DA14\"), quote!(crate::fmc::DA14Pin)),\n        ((\"fmc\", \"DA15\"), quote!(crate::fmc::DA15Pin)),\n        ((\"fmc\", \"SDNWE\"), quote!(crate::fmc::SDNWEPin)),\n        ((\"fmc\", \"SDNCAS\"), quote!(crate::fmc::SDNCASPin)),\n        ((\"fmc\", \"SDNRAS\"), quote!(crate::fmc::SDNRASPin)),\n        ((\"fmc\", \"SDNE0\"), quote!(crate::fmc::SDNE0Pin)),\n        ((\"fmc\", \"SDNE1\"), quote!(crate::fmc::SDNE1Pin)),\n        ((\"fmc\", \"SDCKE0\"), quote!(crate::fmc::SDCKE0Pin)),\n        ((\"fmc\", \"SDCKE1\"), quote!(crate::fmc::SDCKE1Pin)),\n        ((\"fmc\", \"SDCLK\"), quote!(crate::fmc::SDCLKPin)),\n        ((\"fmc\", \"NBL0\"), quote!(crate::fmc::NBL0Pin)),\n        ((\"fmc\", \"NBL1\"), quote!(crate::fmc::NBL1Pin)),\n        ((\"fmc\", \"NBL2\"), quote!(crate::fmc::NBL2Pin)),\n        ((\"fmc\", \"NBL3\"), quote!(crate::fmc::NBL3Pin)),\n        ((\"fmc\", \"INT\"), quote!(crate::fmc::INTPin)),\n        ((\"fmc\", \"NL\"), quote!(crate::fmc::NLPin)),\n        ((\"fmc\", \"NWAIT\"), quote!(crate::fmc::NWaitPin)),\n        ((\"fmc\", \"NE1\"), quote!(crate::fmc::NE1Pin)),\n        ((\"fmc\", \"NE2\"), quote!(crate::fmc::NE2Pin)),\n        ((\"fmc\", \"NE3\"), quote!(crate::fmc::NE3Pin)),\n        ((\"fmc\", \"NE4\"), quote!(crate::fmc::NE4Pin)),\n        ((\"fmc\", \"NCE\"), quote!(crate::fmc::NCEPin)),\n        ((\"fmc\", \"NOE\"), quote!(crate::fmc::NOEPin)),\n        ((\"fmc\", \"NWE\"), quote!(crate::fmc::NWEPin)),\n        ((\"fmc\", \"CLK\"), quote!(crate::fmc::ClkPin)),\n        ((\"fmc\", \"BA0\"), quote!(crate::fmc::BA0Pin)),\n        ((\"fmc\", \"BA1\"), quote!(crate::fmc::BA1Pin)),\n        ((\"timer\", \"CH1\"), quote!(crate::timer::TimerPin<Ch1>)),\n        ((\"timer\", \"CH1N\"), quote!(crate::timer::TimerComplementaryPin<Ch1>)),\n        ((\"timer\", \"CH2\"), quote!(crate::timer::TimerPin<Ch2>)),\n        ((\"timer\", \"CH2N\"), quote!(crate::timer::TimerComplementaryPin<Ch2>)),\n        ((\"timer\", \"CH3\"), quote!(crate::timer::TimerPin<Ch3>)),\n        ((\"timer\", \"CH3N\"), quote!(crate::timer::TimerComplementaryPin<Ch3>)),\n        ((\"timer\", \"CH4\"), quote!(crate::timer::TimerPin<Ch4>)),\n        ((\"timer\", \"CH4N\"), quote!(crate::timer::TimerComplementaryPin<Ch4>)),\n        ((\"timer\", \"ETR\"), quote!(crate::timer::ExternalTriggerPin)),\n        ((\"timer\", \"BKIN\"), quote!(crate::timer::BreakInputPin<BkIn1>)),\n        ((\"timer\", \"BKIN_COMP1\"), quote!(crate::timer::BreakInputComparator1Pin<BkIn1>)),\n        ((\"timer\", \"BKIN_COMP2\"), quote!(crate::timer::BreakInputComparator2Pin<BkIn1>)),\n        ((\"timer\", \"BKIN2\"), quote!(crate::timer::BreakInputPin<BkIn2>)),\n        ((\"timer\", \"BKIN2_COMP1\"), quote!(crate::timer::BreakInputComparator1Pin<BkIn2>)),\n        ((\"timer\", \"BKIN2_COMP2\"), quote!(crate::timer::BreakInputComparator2Pin<BkIn2>)),\n        ((\"hrtim\", \"CHA1\"), quote!(crate::hrtim::ChannelAPin)),\n        ((\"hrtim\", \"CHA2\"), quote!(crate::hrtim::ChannelAComplementaryPin)),\n        ((\"hrtim\", \"CHB1\"), quote!(crate::hrtim::ChannelBPin)),\n        ((\"hrtim\", \"CHB2\"), quote!(crate::hrtim::ChannelBComplementaryPin)),\n        ((\"hrtim\", \"CHC1\"), quote!(crate::hrtim::ChannelCPin)),\n        ((\"hrtim\", \"CHC2\"), quote!(crate::hrtim::ChannelCComplementaryPin)),\n        ((\"hrtim\", \"CHD1\"), quote!(crate::hrtim::ChannelDPin)),\n        ((\"hrtim\", \"CHD2\"), quote!(crate::hrtim::ChannelDComplementaryPin)),\n        ((\"hrtim\", \"CHE1\"), quote!(crate::hrtim::ChannelEPin)),\n        ((\"hrtim\", \"CHE2\"), quote!(crate::hrtim::ChannelEComplementaryPin)),\n        ((\"hrtim\", \"CHF1\"), quote!(crate::hrtim::ChannelFPin)),\n        ((\"hrtim\", \"CHF2\"), quote!(crate::hrtim::ChannelFComplementaryPin)),\n        ((\"lptim\", \"CH1\"), quote!(crate::lptim::Channel1Pin)),\n        ((\"lptim\", \"CH2\"), quote!(crate::lptim::Channel2Pin)),\n        ((\"lptim\", \"OUT\"), quote!(crate::lptim::OutputPin)),\n        ((\"sdmmc\", \"CK\"), quote!(crate::sdmmc::CkPin)),\n        ((\"sdmmc\", \"CMD\"), quote!(crate::sdmmc::CmdPin)),\n        ((\"sdmmc\", \"D0\"), quote!(crate::sdmmc::D0Pin)),\n        ((\"sdmmc\", \"D1\"), quote!(crate::sdmmc::D1Pin)),\n        ((\"sdmmc\", \"D2\"), quote!(crate::sdmmc::D2Pin)),\n        ((\"sdmmc\", \"D3\"), quote!(crate::sdmmc::D3Pin)),\n        ((\"sdmmc\", \"D4\"), quote!(crate::sdmmc::D4Pin)),\n        ((\"sdmmc\", \"D5\"), quote!(crate::sdmmc::D5Pin)),\n        ((\"sdmmc\", \"D6\"), quote!(crate::sdmmc::D6Pin)),\n        ((\"sdmmc\", \"D7\"), quote!(crate::sdmmc::D7Pin)),\n        ((\"sdmmc\", \"D8\"), quote!(crate::sdmmc::D8Pin)),\n        ((\"quadspi\", \"BK1_IO0\"), quote!(crate::qspi::BK1D0Pin)),\n        ((\"quadspi\", \"BK1_IO1\"), quote!(crate::qspi::BK1D1Pin)),\n        ((\"quadspi\", \"BK1_IO2\"), quote!(crate::qspi::BK1D2Pin)),\n        ((\"quadspi\", \"BK1_IO3\"), quote!(crate::qspi::BK1D3Pin)),\n        ((\"quadspi\", \"BK1_NCS\"), quote!(crate::qspi::BK1NSSPin)),\n        ((\"quadspi\", \"BK2_IO0\"), quote!(crate::qspi::BK2D0Pin)),\n        ((\"quadspi\", \"BK2_IO1\"), quote!(crate::qspi::BK2D1Pin)),\n        ((\"quadspi\", \"BK2_IO2\"), quote!(crate::qspi::BK2D2Pin)),\n        ((\"quadspi\", \"BK2_IO3\"), quote!(crate::qspi::BK2D3Pin)),\n        ((\"quadspi\", \"BK2_NCS\"), quote!(crate::qspi::BK2NSSPin)),\n        ((\"quadspi\", \"CLK\"), quote!(crate::qspi::SckPin)),\n        ((\"octospi\", \"IO0\"), quote!(crate::ospi::D0Pin)),\n        ((\"octospi\", \"IO1\"), quote!(crate::ospi::D1Pin)),\n        ((\"octospi\", \"IO2\"), quote!(crate::ospi::D2Pin)),\n        ((\"octospi\", \"IO3\"), quote!(crate::ospi::D3Pin)),\n        ((\"octospi\", \"IO4\"), quote!(crate::ospi::D4Pin)),\n        ((\"octospi\", \"IO5\"), quote!(crate::ospi::D5Pin)),\n        ((\"octospi\", \"IO6\"), quote!(crate::ospi::D6Pin)),\n        ((\"octospi\", \"IO7\"), quote!(crate::ospi::D7Pin)),\n        ((\"octospi\", \"DQS\"), quote!(crate::ospi::DQSPin)),\n        ((\"octospi\", \"NCS\"), quote!(crate::ospi::NSSPin)),\n        ((\"octospi\", \"CLK\"), quote!(crate::ospi::SckPin)),\n        ((\"octospi\", \"NCLK\"), quote!(crate::ospi::NckPin)),\n        ((\"octospim\", \"P1_IO0\"), quote!(crate::ospi::D0Pin)),\n        ((\"octospim\", \"P1_IO1\"), quote!(crate::ospi::D1Pin)),\n        ((\"octospim\", \"P1_IO2\"), quote!(crate::ospi::D2Pin)),\n        ((\"octospim\", \"P1_IO3\"), quote!(crate::ospi::D3Pin)),\n        ((\"octospim\", \"P1_IO4\"), quote!(crate::ospi::D4Pin)),\n        ((\"octospim\", \"P1_IO5\"), quote!(crate::ospi::D5Pin)),\n        ((\"octospim\", \"P1_IO6\"), quote!(crate::ospi::D6Pin)),\n        ((\"octospim\", \"P1_IO7\"), quote!(crate::ospi::D7Pin)),\n        ((\"octospim\", \"P1_DQS\"), quote!(crate::ospi::DQSPin)),\n        ((\"octospim\", \"P1_NCS\"), quote!(crate::ospi::NSSPin)),\n        ((\"octospim\", \"P1_CLK\"), quote!(crate::ospi::SckPin)),\n        ((\"octospim\", \"P1_NCLK\"), quote!(crate::ospi::NckPin)),\n        ((\"octospim\", \"P2_IO0\"), quote!(crate::ospi::D0Pin)),\n        ((\"octospim\", \"P2_IO1\"), quote!(crate::ospi::D1Pin)),\n        ((\"octospim\", \"P2_IO2\"), quote!(crate::ospi::D2Pin)),\n        ((\"octospim\", \"P2_IO3\"), quote!(crate::ospi::D3Pin)),\n        ((\"octospim\", \"P2_IO4\"), quote!(crate::ospi::D4Pin)),\n        ((\"octospim\", \"P2_IO5\"), quote!(crate::ospi::D5Pin)),\n        ((\"octospim\", \"P2_IO6\"), quote!(crate::ospi::D6Pin)),\n        ((\"octospim\", \"P2_IO7\"), quote!(crate::ospi::D7Pin)),\n        ((\"octospim\", \"P2_DQS\"), quote!(crate::ospi::DQSPin)),\n        ((\"octospim\", \"P2_NCS\"), quote!(crate::ospi::NSSPin)),\n        ((\"octospim\", \"P2_CLK\"), quote!(crate::ospi::SckPin)),\n        ((\"octospim\", \"P2_NCLK\"), quote!(crate::ospi::NckPin)),\n        ((\"xspi\", \"IO0\"), quote!(crate::xspi::D0Pin)),\n        ((\"xspi\", \"IO1\"), quote!(crate::xspi::D1Pin)),\n        ((\"xspi\", \"IO2\"), quote!(crate::xspi::D2Pin)),\n        ((\"xspi\", \"IO3\"), quote!(crate::xspi::D3Pin)),\n        ((\"xspi\", \"IO4\"), quote!(crate::xspi::D4Pin)),\n        ((\"xspi\", \"IO5\"), quote!(crate::xspi::D5Pin)),\n        ((\"xspi\", \"IO6\"), quote!(crate::xspi::D6Pin)),\n        ((\"xspi\", \"IO7\"), quote!(crate::xspi::D7Pin)),\n        ((\"xspi\", \"IO8\"), quote!(crate::xspi::D8Pin)),\n        ((\"xspi\", \"IO9\"), quote!(crate::xspi::D9Pin)),\n        ((\"xspi\", \"IO10\"), quote!(crate::xspi::D10Pin)),\n        ((\"xspi\", \"IO11\"), quote!(crate::xspi::D11Pin)),\n        ((\"xspi\", \"IO12\"), quote!(crate::xspi::D12Pin)),\n        ((\"xspi\", \"IO13\"), quote!(crate::xspi::D13Pin)),\n        ((\"xspi\", \"IO14\"), quote!(crate::xspi::D14Pin)),\n        ((\"xspi\", \"IO15\"), quote!(crate::xspi::D15Pin)),\n        ((\"xspi\", \"DQS0\"), quote!(crate::xspi::DQS0Pin)),\n        ((\"xspi\", \"DQS1\"), quote!(crate::xspi::DQS1Pin)),\n        ((\"xspi\", \"NCS1\"), quote!(crate::xspi::NCSPin)),\n        ((\"xspi\", \"NCS2\"), quote!(crate::xspi::NCSPin)),\n        ((\"xspi\", \"CLK\"), quote!(crate::xspi::CLKPin)),\n        ((\"xspi\", \"NCLK\"), quote!(crate::xspi::NCLKPin)),\n        ((\"xspim\", \"P1_IO0\"), quote!(crate::xspi::D0Pin)),\n        ((\"xspim\", \"P1_IO1\"), quote!(crate::xspi::D1Pin)),\n        ((\"xspim\", \"P1_IO2\"), quote!(crate::xspi::D2Pin)),\n        ((\"xspim\", \"P1_IO3\"), quote!(crate::xspi::D3Pin)),\n        ((\"xspim\", \"P1_IO4\"), quote!(crate::xspi::D4Pin)),\n        ((\"xspim\", \"P1_IO5\"), quote!(crate::xspi::D5Pin)),\n        ((\"xspim\", \"P1_IO6\"), quote!(crate::xspi::D6Pin)),\n        ((\"xspim\", \"P1_IO7\"), quote!(crate::xspi::D7Pin)),\n        ((\"xspim\", \"P1_IO8\"), quote!(crate::xspi::D8Pin)),\n        ((\"xspim\", \"P1_IO9\"), quote!(crate::xspi::D9Pin)),\n        ((\"xspim\", \"P1_IO10\"), quote!(crate::xspi::D10Pin)),\n        ((\"xspim\", \"P1_IO11\"), quote!(crate::xspi::D11Pin)),\n        ((\"xspim\", \"P1_IO12\"), quote!(crate::xspi::D12Pin)),\n        ((\"xspim\", \"P1_IO13\"), quote!(crate::xspi::D13Pin)),\n        ((\"xspim\", \"P1_IO14\"), quote!(crate::xspi::D14Pin)),\n        ((\"xspim\", \"P1_IO15\"), quote!(crate::xspi::D15Pin)),\n        ((\"xspim\", \"P1_DQS0\"), quote!(crate::xspi::DQS0Pin)),\n        ((\"xspim\", \"P1_DQS1\"), quote!(crate::xspi::DQS1Pin)),\n        ((\"xspim\", \"P1_NCS1\"), quote!(crate::xspi::NCSPin)),\n        ((\"xspim\", \"P1_NCS2\"), quote!(crate::xspi::NCSPin)),\n        ((\"xspim\", \"P1_CLK\"), quote!(crate::xspi::CLKPin)),\n        ((\"xspim\", \"P1_NCLK\"), quote!(crate::xspi::NCLKPin)),\n        ((\"xspim\", \"P2_IO0\"), quote!(crate::xspi::D0Pin)),\n        ((\"xspim\", \"P2_IO1\"), quote!(crate::xspi::D1Pin)),\n        ((\"xspim\", \"P2_IO2\"), quote!(crate::xspi::D2Pin)),\n        ((\"xspim\", \"P2_IO3\"), quote!(crate::xspi::D3Pin)),\n        ((\"xspim\", \"P2_IO4\"), quote!(crate::xspi::D4Pin)),\n        ((\"xspim\", \"P2_IO5\"), quote!(crate::xspi::D5Pin)),\n        ((\"xspim\", \"P2_IO6\"), quote!(crate::xspi::D6Pin)),\n        ((\"xspim\", \"P2_IO7\"), quote!(crate::xspi::D7Pin)),\n        ((\"xspim\", \"P2_IO8\"), quote!(crate::xspi::D8Pin)),\n        ((\"xspim\", \"P2_IO9\"), quote!(crate::xspi::D9Pin)),\n        ((\"xspim\", \"P2_IO10\"), quote!(crate::xspi::D10Pin)),\n        ((\"xspim\", \"P2_IO11\"), quote!(crate::xspi::D11Pin)),\n        ((\"xspim\", \"P2_IO12\"), quote!(crate::xspi::D12Pin)),\n        ((\"xspim\", \"P2_IO13\"), quote!(crate::xspi::D13Pin)),\n        ((\"xspim\", \"P2_IO14\"), quote!(crate::xspi::D14Pin)),\n        ((\"xspim\", \"P2_IO15\"), quote!(crate::xspi::D15Pin)),\n        ((\"xspim\", \"P2_DQS0\"), quote!(crate::xspi::DQS0Pin)),\n        ((\"xspim\", \"P2_DQS1\"), quote!(crate::xspi::DQS1Pin)),\n        ((\"xspim\", \"P2_NCS1\"), quote!(crate::xspi::NCSPin)),\n        ((\"xspim\", \"P2_NCS2\"), quote!(crate::xspi::NCSPin)),\n        ((\"xspim\", \"P2_CLK\"), quote!(crate::xspi::CLKPin)),\n        ((\"xspim\", \"P2_NCLK\"), quote!(crate::xspi::NCLKPin)),\n        ((\"hspi\", \"IO0\"), quote!(crate::hspi::D0Pin)),\n        ((\"hspi\", \"IO1\"), quote!(crate::hspi::D1Pin)),\n        ((\"hspi\", \"IO2\"), quote!(crate::hspi::D2Pin)),\n        ((\"hspi\", \"IO3\"), quote!(crate::hspi::D3Pin)),\n        ((\"hspi\", \"IO4\"), quote!(crate::hspi::D4Pin)),\n        ((\"hspi\", \"IO5\"), quote!(crate::hspi::D5Pin)),\n        ((\"hspi\", \"IO6\"), quote!(crate::hspi::D6Pin)),\n        ((\"hspi\", \"IO7\"), quote!(crate::hspi::D7Pin)),\n        ((\"hspi\", \"IO8\"), quote!(crate::hspi::D8Pin)),\n        ((\"hspi\", \"IO9\"), quote!(crate::hspi::D9Pin)),\n        ((\"hspi\", \"IO10\"), quote!(crate::hspi::D10Pin)),\n        ((\"hspi\", \"IO11\"), quote!(crate::hspi::D11Pin)),\n        ((\"hspi\", \"IO12\"), quote!(crate::hspi::D12Pin)),\n        ((\"hspi\", \"IO13\"), quote!(crate::hspi::D13Pin)),\n        ((\"hspi\", \"IO14\"), quote!(crate::hspi::D14Pin)),\n        ((\"hspi\", \"IO15\"), quote!(crate::hspi::D15Pin)),\n        ((\"hspi\", \"DQS0\"), quote!(crate::hspi::DQS0Pin)),\n        ((\"hspi\", \"DQS1\"), quote!(crate::hspi::DQS1Pin)),\n        ((\"hspi\", \"NCS\"), quote!(crate::hspi::NSSPin)),\n        ((\"hspi\", \"CLK\"), quote!(crate::hspi::SckPin)),\n        ((\"hspi\", \"NCLK\"), quote!(crate::hspi::NckPin)),\n        ((\"tsc\", \"G1_IO1\"), quote!(crate::tsc::G1IO1Pin)),\n        ((\"tsc\", \"G1_IO2\"), quote!(crate::tsc::G1IO2Pin)),\n        ((\"tsc\", \"G1_IO3\"), quote!(crate::tsc::G1IO3Pin)),\n        ((\"tsc\", \"G1_IO4\"), quote!(crate::tsc::G1IO4Pin)),\n        ((\"tsc\", \"G2_IO1\"), quote!(crate::tsc::G2IO1Pin)),\n        ((\"tsc\", \"G2_IO2\"), quote!(crate::tsc::G2IO2Pin)),\n        ((\"tsc\", \"G2_IO3\"), quote!(crate::tsc::G2IO3Pin)),\n        ((\"tsc\", \"G2_IO4\"), quote!(crate::tsc::G2IO4Pin)),\n        ((\"tsc\", \"G3_IO1\"), quote!(crate::tsc::G3IO1Pin)),\n        ((\"tsc\", \"G3_IO2\"), quote!(crate::tsc::G3IO2Pin)),\n        ((\"tsc\", \"G3_IO3\"), quote!(crate::tsc::G3IO3Pin)),\n        ((\"tsc\", \"G3_IO4\"), quote!(crate::tsc::G3IO4Pin)),\n        ((\"tsc\", \"G4_IO1\"), quote!(crate::tsc::G4IO1Pin)),\n        ((\"tsc\", \"G4_IO2\"), quote!(crate::tsc::G4IO2Pin)),\n        ((\"tsc\", \"G4_IO3\"), quote!(crate::tsc::G4IO3Pin)),\n        ((\"tsc\", \"G4_IO4\"), quote!(crate::tsc::G4IO4Pin)),\n        ((\"tsc\", \"G5_IO1\"), quote!(crate::tsc::G5IO1Pin)),\n        ((\"tsc\", \"G5_IO2\"), quote!(crate::tsc::G5IO2Pin)),\n        ((\"tsc\", \"G5_IO3\"), quote!(crate::tsc::G5IO3Pin)),\n        ((\"tsc\", \"G5_IO4\"), quote!(crate::tsc::G5IO4Pin)),\n        ((\"tsc\", \"G6_IO1\"), quote!(crate::tsc::G6IO1Pin)),\n        ((\"tsc\", \"G6_IO2\"), quote!(crate::tsc::G6IO2Pin)),\n        ((\"tsc\", \"G6_IO3\"), quote!(crate::tsc::G6IO3Pin)),\n        ((\"tsc\", \"G6_IO4\"), quote!(crate::tsc::G6IO4Pin)),\n        ((\"tsc\", \"G7_IO1\"), quote!(crate::tsc::G7IO1Pin)),\n        ((\"tsc\", \"G7_IO2\"), quote!(crate::tsc::G7IO2Pin)),\n        ((\"tsc\", \"G7_IO3\"), quote!(crate::tsc::G7IO3Pin)),\n        ((\"tsc\", \"G7_IO4\"), quote!(crate::tsc::G7IO4Pin)),\n        ((\"tsc\", \"G8_IO1\"), quote!(crate::tsc::G8IO1Pin)),\n        ((\"tsc\", \"G8_IO2\"), quote!(crate::tsc::G8IO2Pin)),\n        ((\"tsc\", \"G8_IO3\"), quote!(crate::tsc::G8IO3Pin)),\n        ((\"tsc\", \"G8_IO4\"), quote!(crate::tsc::G8IO4Pin)),\n        ((\"lcd\", \"SEG\"), quote!(crate::lcd::SegPin)),\n        ((\"lcd\", \"COM\"), quote!(crate::lcd::ComPin)),\n        ((\"lcd\", \"VLCD\"), quote!(crate::lcd::VlcdPin)),\n        ((\"dac\", \"OUT1\"), quote!(crate::dac::DacPin<Ch1>)),\n        ((\"dac\", \"OUT2\"), quote!(crate::dac::DacPin<Ch2>)),\n    ].into();\n\n    // On some families the USB DM/DP signals are present as alternate functions,\n    // on other as additional functions where GPIO should be left in Analog mode.\n    cfgs.declare(\"usb_alternate_function\");\n\n    for p in METADATA.peripherals {\n        #[cfg(not(feature = \"stm32-hrtim\"))]\n        if let Some(reg) = &p.registers\n            && reg.kind == \"hrtim\"\n        {\n            // Only enable the hrtim peripheral if the stm32-hrtim feature is active\n            continue;\n        }\n        if let Some(regs) = &p.registers {\n            let mut adc_pairs: BTreeMap<u8, (Option<Ident>, Option<Ident>)> = BTreeMap::new();\n            let mut seen_lcd_seg_pins = HashSet::new();\n\n            for pin in p.pins {\n                let mut key = (regs.kind, pin.signal);\n\n                // LCD is special. There are so many pins!\n                if regs.kind == \"lcd\" {\n                    key.1 = pin.signal.trim_end_matches(char::is_numeric);\n\n                    if key.1 == \"SEG\" && !seen_lcd_seg_pins.insert(pin.pin) {\n                        // LCD has SEG pins multiplexed in the peripheral\n                        // This means we can see them twice. We need to skip those so we're not impl'ing the trait twice\n                        continue;\n                    }\n                }\n\n                if let Some(tr) = signals.get(&key) {\n                    let mut peri = format_ident!(\"{}\", p.name);\n\n                    let pin_name = {\n                        // If we encounter a _C pin but the split_feature for this pin is not enabled, skip it\n                        if pin.pin.ends_with(\"_C\") && !split_features.iter().any(|x| x.pin_name_with_c == pin.pin) {\n                            continue;\n                        }\n\n                        format_ident!(\"{}\", pin.pin)\n                    };\n\n                    let af = pin.af.unwrap_or(0);\n\n                    // MCO is special\n                    if pin.signal.starts_with(\"MCO\") {\n                        peri = format_ident!(\"{}\", pin.signal.replace('_', \"\"));\n                    }\n\n                    // OCTOSPIM is special\n                    if p.name == \"OCTOSPIM\" {\n                        // Some chips have OCTOSPIM but not OCTOSPI2.\n                        if METADATA.peripherals.iter().any(|p| p.name == \"OCTOSPI2\") {\n                            peri = format_ident!(\"{}\", \"OCTOSPI2\");\n                            g.extend(quote! {\n                                pin_trait_impl!(#tr, #peri, #pin_name, #af);\n                            });\n                        }\n                        peri = format_ident!(\"{}\", \"OCTOSPI1\");\n                    }\n\n                    // XSPIM  is special\n                    if p.name == \"XSPIM\" {\n                        if pin.signal.starts_with(\"P1\") {\n                            peri = format_ident!(\"{}\", \"XSPI1\");\n                        } else if pin.signal.starts_with(\"P2\") {\n                            peri = format_ident!(\"{}\", \"XSPI2\");\n                        } else {\n                            panic! {\"malformed XSPIM pin: {:?}\", pin}\n                        }\n                    }\n\n                    // MDIO and MDC are special\n                    if pin.signal == \"MDIO\" || pin.signal == \"MDC\" {\n                        peri = format_ident!(\"{}\", \"ETH_SMA\");\n                    }\n\n                    // XSPI NCS pin to CSSEL mapping\n                    if pin.signal.ends_with(\"NCS1\") {\n                        g.extend(quote! {\n                            sel_trait_impl!(crate::xspi::NCSEither, #peri, #pin_name, 0);\n                        })\n                    }\n                    if pin.signal.ends_with(\"NCS2\") {\n                        g.extend(quote! {\n                            sel_trait_impl!(crate::xspi::NCSEither, #peri, #pin_name, 1);\n                        })\n                    }\n\n                    // Many families have USB as an additional function, not an\n                    // alternate function, where the pin must be left in analog\n                    // mode and enabling AF will break USB.\n                    if p.name.starts_with(\"USB\") && (pin.signal == \"DM\" || pin.signal == \"DP\") {\n                        if pin.af.is_some() {\n                            cfgs.enable(\"usb_alternate_function\");\n                        }\n                    }\n\n                    let pin_trait_impl = if let Some(afio) = &p.afio {\n                        let values = afio\n                            .values\n                            .iter()\n                            .filter(|v| v.pins.contains(&pin.pin))\n                            .map(|v| v.value)\n                            .collect::<Vec<_>>();\n\n                        if values.is_empty() {\n                            None\n                        } else {\n                            let reg = format_ident!(\"{}\", afio.register.to_lowercase());\n                            let setter = format_ident!(\"set_{}\", afio.field.to_lowercase());\n                            let type_and_values = if is_bool_field(\"AFIO\", afio.register, afio.field) {\n                                let values = values.iter().map(|&v| v > 0);\n                                quote!(AfioRemapBool, [#(#values),*])\n                            } else {\n                                quote!(AfioRemap, [#(#values),*])\n                            };\n\n                            Some(quote! {\n                                pin_trait_afio_impl!(#tr, #peri, #pin_name, {#reg, #setter, #type_and_values});\n                            })\n                        }\n                    } else {\n                        let peripherals_with_afio = [\n                            \"CAN\",\n                            \"CEC\",\n                            \"ETH\",\n                            \"I2C\",\n                            \"SPI\",\n                            \"SUBGHZSPI\",\n                            \"USART\",\n                            \"UART\",\n                            \"LPUART\",\n                            \"TIM\",\n                        ];\n                        let not_applicable = if peripherals_with_afio.iter().any(|&x| p.name.starts_with(x)) {\n                            quote!(, crate::gpio::AfioRemapNotApplicable)\n                        } else {\n                            quote!()\n                        };\n\n                        Some(quote!(pin_trait_impl!(#tr, #peri, #pin_name, #af #not_applicable);))\n                    };\n\n                    g.extend(pin_trait_impl);\n                }\n\n                // ADC is special\n                if regs.kind == \"adc\" {\n                    if p.rcc.is_none() {\n                        continue;\n                    }\n\n                    let peri = format_ident!(\"{}\", p.name);\n                    let pin_name = {\n                        // If we encounter a _C pin but the split_feature for this pin is not enabled, skip it\n                        if pin.pin.ends_with(\"_C\") && !split_features.iter().any(|x| x.pin_name_with_c == pin.pin) {\n                            continue;\n                        }\n                        format_ident!(\"{}\", pin.pin)\n                    };\n\n                    // H7 has differential voltage measurements.\n                    let ch = parse_adc_pin_signal(pin.signal);\n                    if let Some((ch, false)) = ch {\n                        adc_pairs.entry(ch).or_insert((None, None)).0.replace(pin_name.clone());\n\n                        g.extend(quote! {\n                        impl_adc_pin!( #peri, #pin_name, #ch);\n                        })\n                    }\n                    if let Some((ch, true)) = ch {\n                        adc_pairs.entry(ch).or_insert((None, None)).1.replace(pin_name.clone());\n                    }\n                }\n\n                if regs.kind == \"opamp\" {\n                    let peri = format_ident!(\"{}\", p.name);\n                    let pin_name = format_ident!(\"{}\", pin.pin);\n                    if let Some(ch_str) = pin.signal.strip_prefix(\"VINP\") {\n                        // Impl NonInvertingPin for VINP0, VINP1 etc.\n                        if let Ok(ch) = ch_str.parse::<u8>() {\n                            g.extend(quote! {\n                                impl_opamp_vp_pin!( #peri, #pin_name, #ch );\n                            });\n                        }\n                    } else if let Some(ch_str) = pin.signal.strip_prefix(\"VINM\") {\n                        if let Ok(ch) = ch_str.parse::<u8>() {\n                            // Impl BiasPin for VINM0\n                            if ch == 0 {\n                                g.extend(quote! {\n                                    impl_opamp_bias_pin!( #peri, #pin_name, #ch);\n                                });\n                            }\n\n                            // Impl InvertingPin for VINM0, VINM1 etc.\n                            g.extend(quote! {\n                                impl_opamp_vn_pin!( #peri, #pin_name, #ch);\n                            });\n                        }\n                    } else if pin.signal == \"VOUT\" {\n                        // Impl OutputPin for the VOUT pin\n                        g.extend(quote! {\n                            impl_opamp_vout_pin!( #peri, #pin_name );\n                        });\n\n                        for adc in METADATA.peripherals {\n                            let Some(adc_regs) = &adc.registers else {\n                                continue;\n                            };\n                            if adc_regs.kind != \"adc\" || adc.rcc.is_none() {\n                                continue;\n                            }\n\n                            let adc_peri = format_ident!(\"{}\", adc.name);\n                            for adc_pin in adc.pins {\n                                if adc_pin.pin != pin.pin {\n                                    continue;\n                                }\n\n                                if let Some((ch, false)) = parse_adc_pin_signal(adc_pin.signal) {\n                                    g.extend(quote! {\n                                        impl_opamp_external_output!( #peri, #adc_peri, #ch );\n                                    });\n                                }\n                            }\n                        }\n                    }\n                }\n\n                if regs.kind == \"comp\" {\n                    let peri = format_ident!(\"{}\", p.name);\n                    let pin_name = format_ident!(\"{}\", pin.pin);\n                    // Check if this peripheral has numbered signals (e.g. INP0/INP1 from extra YAML).\n                    // If so, skip bare INP/INM to avoid duplicate trait impls.\n                    let has_numbered = p.pins.iter().any(|s| s.signal.starts_with(\"INP\") && s.signal.len() > 3);\n                    if let Some(ch_str) = pin.signal.strip_prefix(\"INP\") {\n                        let ch: u8 = match ch_str.parse() {\n                            Ok(ch) => ch,\n                            Err(_) if !has_numbered => 0, // bare \"INP\" on chips without numbered signals\n                            Err(_) => continue,           // skip bare \"INP\" when numbered signals exist\n                        };\n                        g.extend(quote! {\n                            impl_comp_inp_pin!( #peri, #pin_name, #ch );\n                        });\n                    } else if let Some(ch_str) = pin.signal.strip_prefix(\"INM\") {\n                        let ch: u8 = match ch_str.parse() {\n                            Ok(ch) => ch,\n                            Err(_) if !has_numbered => 0,\n                            Err(_) => continue,\n                        };\n                        g.extend(quote! {\n                            impl_comp_inm_pin!( #peri, #pin_name, #ch );\n                        });\n                    }\n                }\n\n                if regs.kind == \"spdifrx\" {\n                    let peri = format_ident!(\"{}\", p.name);\n                    let pin_name = format_ident!(\"{}\", pin.pin);\n                    let af = pin.af.unwrap_or(0);\n                    let sel: u8 = pin.signal.strip_prefix(\"IN\").unwrap().parse().unwrap();\n\n                    g.extend(quote! {\n                    impl_spdifrx_pin!( #peri, #pin_name, #af, #sel);\n                    })\n                }\n            }\n\n            {\n                let peri = format_ident!(\"{}\", p.name);\n\n                for (ch, (pin, npin)) in adc_pairs {\n                    let (pin_name, npin_name) = match (pin, npin) {\n                        (Some(pin), Some(npin)) => (pin, npin),\n                        _ => {\n                            continue;\n                        }\n                    };\n\n                    g.extend(quote! {\n                    impl_adc_pair!( #peri, #pin_name, #npin_name, #ch);\n                    })\n                }\n            }\n        }\n    }\n\n    // ========\n    // Generate dma_trait_impl!\n\n    let mut signals: HashMap<_, _> = [\n        // (kind, signal) => trait\n        ((\"adc\", \"ADC\"), quote!(crate::adc::RxDma)),\n        ((\"adc\", \"ADC1\"), quote!(crate::adc::RxDma)),\n        ((\"adc\", \"ADC2\"), quote!(crate::adc::RxDma)),\n        ((\"adc\", \"ADC3\"), quote!(crate::adc::RxDma)),\n        ((\"ucpd\", \"RX\"), quote!(crate::ucpd::RxDma)),\n        ((\"ucpd\", \"TX\"), quote!(crate::ucpd::TxDma)),\n        ((\"usart\", \"RX\"), quote!(crate::usart::RxDma)),\n        ((\"usart\", \"TX\"), quote!(crate::usart::TxDma)),\n        ((\"lpuart\", \"RX\"), quote!(crate::usart::RxDma)),\n        ((\"lpuart\", \"TX\"), quote!(crate::usart::TxDma)),\n        ((\"sai\", \"A\"), quote!(crate::sai::Dma<A>)),\n        ((\"sai\", \"B\"), quote!(crate::sai::Dma<B>)),\n        ((\"spi\", \"RX\"), quote!(crate::spi::RxDma)),\n        ((\"spi\", \"TX\"), quote!(crate::spi::TxDma)),\n        ((\"spdifrx\", \"RX\"), quote!(crate::spdifrx::Dma)),\n        ((\"i2c\", \"RX\"), quote!(crate::i2c::RxDma)),\n        ((\"i2c\", \"TX\"), quote!(crate::i2c::TxDma)),\n        ((\"dcmi\", \"DCMI\"), quote!(crate::dcmi::FrameDma)),\n        ((\"dcmi\", \"PSSI\"), quote!(crate::dcmi::FrameDma)),\n        // SDMMCv1 uses the same channel for both directions, so just implement for RX\n        ((\"sdmmc\", \"RX\"), quote!(crate::sdmmc::SdmmcDma)),\n        ((\"quadspi\", \"QUADSPI\"), quote!(crate::qspi::QuadDma)),\n        ((\"quadspi\", \"FIFO\"), quote!(crate::qspi::QuadDma)),\n        ((\"octospi\", \"OCTOSPI1\"), quote!(crate::ospi::OctoDma)),\n        ((\"hspi\", \"HSPI1\"), quote!(crate::hspi::HspiDma)),\n        ((\"dac\", \"CH1\"), quote!(crate::dac::Dma<Ch1>)),\n        ((\"dac\", \"CH2\"), quote!(crate::dac::Dma<Ch2>)),\n        ((\"timer\", \"UP\"), quote!(crate::timer::UpDma)),\n        ((\"hash\", \"IN\"), quote!(crate::hash::Dma)),\n        ((\"cryp\", \"IN\"), quote!(crate::cryp::DmaIn)),\n        ((\"cryp\", \"OUT\"), quote!(crate::cryp::DmaOut)),\n        ((\"timer\", \"CH1\"), quote!(crate::timer::Dma<Ch1>)),\n        ((\"timer\", \"CH2\"), quote!(crate::timer::Dma<Ch2>)),\n        ((\"timer\", \"CH3\"), quote!(crate::timer::Dma<Ch3>)),\n        ((\"timer\", \"CH4\"), quote!(crate::timer::Dma<Ch4>)),\n        ((\"cordic\", \"WRITE\"), quote!(crate::cordic::WriteDma)),\n        ((\"cordic\", \"READ\"), quote!(crate::cordic::ReadDma)),\n        ((\"xspi\", \"RX\"), quote!(crate::xspi::XDma)),\n        ((\"xspi\", \"RX\"), quote!(crate::xspi::XDma)),\n    ]\n    .into();\n\n    // ========\n    // Generate trigger_trait_impl!\n\n    let triggers: HashMap<_, _> = [\n        // (kind, signal) => trait\n        ((\"dac\", \"DAC_CHX_TRG\"), quote!(crate::dac::ChannelTrigger)),\n        ((\"dac\", \"DAC_INC_CHX_TRG\"), quote!(crate::dac::ChannelIncTrigger)),\n        ((\"adc\", \"ADC_EXT_TRG\"), quote!(crate::adc::RegularTrigger)),\n        ((\"adc\", \"ADC_JEXT_TRG\"), quote!(crate::adc::InjectedTrigger)),\n    ]\n    .into();\n\n    let mut trigger_list: BTreeSet<&str> = BTreeSet::new();\n\n    let trigger_expr = Regex::new(r\"(?m)(.+?)(\\d+)\").unwrap();\n\n    if chip_name.starts_with(\"stm32u5\") {\n        signals.insert((\"adc\", \"ADC4\"), quote!(crate::adc::RxDma));\n    } else {\n        signals.insert((\"adc\", \"ADC4\"), quote!(crate::adc::RxDma));\n    }\n\n    if chip_name.starts_with(\"stm32wba\") {\n        signals.insert((\"adc\", \"ADC4\"), quote!(crate::adc::RxDma));\n    }\n\n    if chip_name.starts_with(\"stm32g4\") {\n        let line_number = chip_name.chars().skip(8).next().unwrap();\n        if line_number == '3' || line_number == '4' {\n            signals.insert((\"adc\", \"ADC5\"), quote!(crate::adc::RxDma));\n        }\n    }\n\n    for p in METADATA.peripherals {\n        if let Some(regs) = &p.registers {\n            for trigger in p.triggers {\n                let matches = trigger_expr.captures(trigger.signal).unwrap();\n                let signal = &matches[1];\n                let idx: u8 = (&matches[2]).parse().unwrap();\n\n                trigger_list.insert(trigger.source);\n\n                if let Some(tr) = triggers.get(&(regs.kind, signal)) {\n                    let peri = format_ident!(\"{}\", p.name);\n                    let source = format_ident!(\"{}\", trigger.source);\n                    let idx = quote!(#idx);\n\n                    g.extend(quote! {\n                        trigger_trait_impl!(#tr, #peri, #source, #idx);\n                    });\n                }\n            }\n\n            let mut dupe = HashSet::new();\n            for ch in p.dma_channels {\n                if let Some(tr) = signals.get(&(regs.kind, ch.signal)) {\n                    let peri = format_ident!(\"{}\", p.name);\n\n                    let channels = if let Some(channel) = &ch.channel {\n                        // Chip with DMA/BDMA, without DMAMUX\n                        vec![*channel]\n                    } else if let Some(dmamux) = &ch.dmamux {\n                        // Chip with DMAMUX\n                        METADATA\n                            .dma_channels\n                            .iter()\n                            .filter(|ch| ch.dmamux == Some(*dmamux))\n                            .map(|ch| ch.name)\n                            .collect()\n                    } else if let Some(dma) = &ch.dma {\n                        // Chip with GPDMA\n                        METADATA\n                            .dma_channels\n                            .iter()\n                            .filter(|ch| ch.dma == *dma)\n                            .map(|ch| ch.name)\n                            .collect()\n                    } else {\n                        unreachable!();\n                    };\n\n                    for channel in channels {\n                        // Some chips have multiple request numbers for the same (peri, signal, channel) combos.\n                        // Ignore the dupes, picking the first one. Otherwise this causes conflicting trait impls\n                        let key = (ch.signal, channel.to_string());\n                        if !dupe.insert(key) {\n                            continue;\n                        }\n\n                        let request = if let Some(request) = ch.request {\n                            let request = request as u8;\n                            quote!(#request)\n                        } else {\n                            quote!(())\n                        };\n\n                        let mut remap = quote!();\n                        for remap_info in ch.remap {\n                            let register = format_ident!(\"{}\", remap_info.register.to_lowercase());\n                            let setter = format_ident!(\"set_{}\", remap_info.field.to_lowercase());\n\n                            let value = if is_bool_field(\"SYSCFG\", &remap_info.register, &remap_info.field) {\n                                let bool_value = format_ident!(\"{}\", remap_info.value > 0);\n                                quote!(#bool_value)\n                            } else {\n                                let value = remap_info.value;\n                                quote!(#value.into())\n                            };\n\n                            remap.extend(quote!(crate::pac::SYSCFG.#register().modify(|w| w.#setter(#value));));\n                        }\n\n                        let channel = format_ident!(\"{}\", channel);\n                        g.extend(quote! {\n                            dma_trait_impl!(#tr, #peri, #channel, #request, {#remap});\n                        });\n                    }\n                }\n            }\n        }\n    }\n\n    // ========\n    // Generate Triggers mod\n    {\n        let triggers_mod: TokenStream = trigger_list\n            .iter()\n            .map(|trigger| {\n                let trigger = format_ident!(\"{}\", trigger);\n\n                quote! {\n                    #[allow(non_camel_case_types)]\n                    pub struct #trigger;\n                }\n            })\n            .collect();\n\n        g.extend(quote! {\n            pub mod triggers {\n                #triggers_mod\n            }\n        });\n    }\n\n    // ========\n    // Generate Div/Mul impls for RCC and ADC prescalers/dividers/multipliers.\n    for (kind, psc_enums) in [\"rcc\", \"adc\", \"adccommon\"].iter().filter_map(|kind| {\n        METADATA\n            .peripherals\n            .iter()\n            .filter_map(|p| p.registers.as_ref())\n            .find(|r| r.kind == *kind)\n            .map(|r| (*kind, r.ir.enums))\n    }) {\n        for e in psc_enums.iter() {\n            fn is_adc_name(e: &str) -> bool {\n                match e {\n                    \"Presc\" | \"Adc4Presc\" | \"Adcpre\" => true,\n                    _ => false,\n                }\n            }\n\n            fn is_rcc_name(e: &str) -> bool {\n                match e {\n                    \"Pllp\" | \"Pllq\" | \"Pllr\" | \"Plldivst\" | \"Pllm\" | \"Plln\" | \"Prediv1\" | \"Prediv2\" | \"Hpre5\" => true,\n                    \"Timpre\" | \"Pllrclkpre\" => false,\n                    e if e.ends_with(\"pre\") || e.ends_with(\"pres\") || e.ends_with(\"div\") || e.ends_with(\"mul\") => true,\n                    _ => false,\n                }\n            }\n\n            fn parse_num(n: &str) -> Result<Frac, ()> {\n                for prefix in [\"DIV\", \"MUL\"] {\n                    if let Some(n) = n.strip_prefix(prefix) {\n                        let exponent = n.find('_').map(|e| n.len() - 1 - e).unwrap_or(0) as u32;\n                        let mantissa = n.replace('_', \"\").parse().map_err(|_| ())?;\n                        let f = Frac {\n                            num: mantissa,\n                            denom: 10u32.pow(exponent),\n                        };\n                        return Ok(f.simplify());\n                    }\n                }\n                Err(())\n            }\n\n            if (kind == \"rcc\" && is_rcc_name(e.name)) || ((kind == \"adccommon\" || kind == \"adc\") && is_adc_name(e.name))\n            {\n                let kind = format_ident!(\"{}\", kind);\n                let enum_name = format_ident!(\"{}\", e.name);\n                let mut nums = Vec::new();\n                let mut denoms = Vec::new();\n                for v in e.variants {\n                    let Ok(val) = parse_num(v.name) else {\n                        panic!(\"could not parse mul/div. enum={} variant={}\", e.name, v.name)\n                    };\n                    let variant_name = format_ident!(\"{}\", v.name);\n                    let variant = quote!(crate::pac::#kind::vals::#enum_name::#variant_name);\n                    let num = val.num;\n                    let denom = val.denom;\n                    nums.push(quote!(#variant => #num,));\n                    denoms.push(quote!(#variant => #denom,));\n                }\n\n                g.extend(quote! {\n                    impl crate::time::Prescaler for crate::pac::#kind::vals::#enum_name {\n                        fn num(&self) -> u32 {\n                            match *self {\n                                #(#nums)*\n                                #[allow(unreachable_patterns)]\n                                _ => unreachable!(),\n                            }\n                        }\n\n                        fn denom(&self) -> u32 {\n                            match *self {\n                                #(#denoms)*\n                                #[allow(unreachable_patterns)]\n                                _ => unreachable!(),\n                            }\n                        }\n                    }\n                });\n            }\n        }\n    }\n\n    // ========\n    // Write peripheral_interrupts module.\n    let mut mt = TokenStream::new();\n    for p in METADATA.peripherals {\n        let mut pt = TokenStream::new();\n\n        let mut exti2_tsc_injected = false;\n        if let Some(ref irq) = exti2_tsc_shared_int_present\n            && p.name == \"EXTI\"\n        {\n            exti2_tsc_injected = true;\n            let iname = format_ident!(\"{}\", irq.name);\n            let sname = format_ident!(\"{}\", \"EXTI2\");\n            pt.extend(quote!(pub type #sname = crate::interrupt::typelevel::#iname;));\n        }\n        for irq in p.interrupts {\n            if exti2_tsc_injected && irq.signal == \"EXTI2\" {\n                continue;\n            }\n            let iname = format_ident!(\"{}\", irq.interrupt);\n            let sname = format_ident!(\"{}\", irq.signal);\n            pt.extend(quote!(pub type #sname = crate::interrupt::typelevel::#iname;));\n        }\n\n        let pname = format_ident!(\"{}\", p.name);\n        mt.extend(quote!(pub mod #pname { #pt }));\n    }\n    g.extend(quote!(#[allow(non_camel_case_types)] pub mod peripheral_interrupts { #mt }));\n\n    // ========\n    // Write foreach_foo! macrotables\n\n    let mut flash_regions_table: Vec<Vec<String>> = Vec::new();\n    let mut interrupts_table: Vec<Vec<String>> = Vec::new();\n    let mut peripherals_table: Vec<Vec<String>> = Vec::new();\n    let mut pins_table: Vec<Vec<String>> = Vec::new();\n    let mut adc_table: Vec<Vec<String>> = Vec::new();\n\n    for m in memory\n        .iter()\n        .filter(|m| m.kind == MemoryRegionKind::Flash && m.settings.is_some())\n    {\n        let settings = m.settings.as_ref().unwrap();\n        let row = vec![\n            get_flash_region_type_name(m.name),\n            settings.write_size.to_string(),\n            settings.erase_size.to_string(),\n        ];\n        flash_regions_table.push(row);\n    }\n\n    let gpio_base = peripheral_map.get(\"GPIOA\").unwrap().address as u32;\n    let gpio_stride = 0x400;\n    let mut init_gpio_analog = TokenStream::new();\n\n    for pin in METADATA.pins {\n        let port_letter = pin.name.chars().nth(1).unwrap();\n        let pname = format!(\"GPIO{}\", port_letter);\n        let p = METADATA.peripherals.iter().find(|p| p.name == pname).unwrap();\n        assert_eq!(0, (p.address as u32 - gpio_base) % gpio_stride);\n        let port_num = (p.address as u32 - gpio_base) / gpio_stride;\n        let pin_num: u32 = pin.name[2..].parse().unwrap();\n\n        let port_num = if chip_name.starts_with(\"stm32n6\") && port_num > 7 {\n            port_num - 5 // Ports I-M are not present\n        } else {\n            port_num\n        };\n\n        pins_table.push(vec![\n            pin.name.to_string(),\n            p.name.to_string(),\n            port_num.to_string(),\n            pin_num.to_string(),\n            format!(\"EXTI{}\", pin_num),\n        ]);\n\n        // set all GPIOs to analog mode except for PA13 and PA14 which are SWDIO and SWDCLK\n        let pin_port = (port_num * 16 + pin_num) as u8;\n        if pin.name != \"PA13\" && pin.name != \"PA14\" {\n            init_gpio_analog.extend(quote! {\n                crate::gpio::set_as_analog(#pin_port);\n            });\n        }\n\n        // If we have the split pins, we need to do a little extra work:\n        // Add the \"_C\" variant to the table. The solution is not optimal, though.\n        // Adding them only when the corresponding GPIOx also appears.\n        // This should avoid unintended side-effects as much as possible.\n        #[cfg(feature = \"_split-pins-enabled\")]\n        for split_feature in &split_features {\n            if split_feature.pin_name_without_c == pin.name {\n                pins_table.push(vec![\n                    split_feature.pin_name_with_c.to_string(),\n                    p.name.to_string(),\n                    port_num.to_string(),\n                    pin_num.to_string(),\n                    format!(\"EXTI{}\", pin_num),\n                ]);\n            }\n        }\n    }\n\n    if cfg!(feature = \"gpio-init-analog\") {\n        g.extend(quote! {\n            fn init_gpio_analog() {\n                #init_gpio_analog\n            }\n        });\n    }\n\n    for p in METADATA.peripherals {\n        if let Some(regs) = &p.registers {\n            if regs.kind == \"adc\" {\n                let adc_num = p.name.strip_prefix(\"ADC\").unwrap();\n                let mut adc_common = None;\n                for p2 in METADATA.peripherals {\n                    if let Some(common_nums) = p2.name.strip_prefix(\"ADC\").and_then(|s| s.strip_suffix(\"_COMMON\")) {\n                        if common_nums.contains(adc_num) {\n                            adc_common = Some(p2);\n                        }\n                    }\n                }\n                let adc_common = adc_common.map(|p| p.name).unwrap_or(\"none\");\n                let row = vec![p.name.to_string(), adc_common.to_string(), \"adc\".to_string()];\n                adc_table.push(row);\n            }\n\n            for irq in p.interrupts {\n                let row = vec![\n                    p.name.to_string(),\n                    regs.kind.to_string(),\n                    regs.block.to_string(),\n                    irq.signal.to_string(),\n                    irq.interrupt.to_ascii_uppercase(),\n                ];\n                interrupts_table.push(row)\n            }\n\n            let row = vec![regs.kind.to_string(), p.name.to_string()];\n            peripherals_table.push(row);\n        }\n    }\n\n    let mut dmas = TokenStream::new();\n    let has_dmamux = METADATA\n        .peripherals\n        .iter()\n        .flat_map(|p| &p.registers)\n        .any(|p| p.kind == \"dmamux\");\n\n    let mut dma_irqs: BTreeMap<&str, Vec<String>> = BTreeMap::new();\n\n    for p in METADATA.peripherals {\n        if let Some(r) = &p.registers {\n            match r.kind {\n                \"dma\" | \"bdma\" | \"gpdma\" | \"lpdma\" => {\n                    for irq in p.interrupts {\n                        let ch_name = format!(\"{}_{}\", p.name, irq.signal);\n                        let ch = METADATA.dma_channels.iter().find(|c| c.name == ch_name);\n\n                        if ch.is_none() {\n                            continue;\n                        }\n\n                        dma_irqs.entry(irq.interrupt).or_default().push(ch_name);\n                    }\n                }\n                \"mdma\" => {\n                    for irq in p.interrupts {\n                        for c in METADATA.dma_channels.iter().filter(|c| c.name.starts_with(\"MDMA\")) {\n                            dma_irqs.entry(irq.interrupt).or_default().push(c.name.to_string());\n                        }\n                    }\n                }\n                _ => (),\n            }\n        }\n    }\n\n    // Build a map from DMA channel name to its interrupt name.\n    // This is used to generate the interrupt type for each DMA channel.\n    let mut dma_ch_to_irq: BTreeMap<&str, Vec<String>> = BTreeMap::new();\n\n    for (irq, channels) in &dma_irqs {\n        for channel in channels {\n            dma_ch_to_irq.entry(channel).or_default().push(irq.to_string());\n        }\n    }\n\n    for (ch_idx, ch) in METADATA.dma_channels.iter().enumerate() {\n        let dma_peri = peripheral_map.get(ch.dma).unwrap();\n        let stop_mode = dma_peri\n            .rcc\n            .as_ref()\n            .map(|rcc| rcc.stop_mode.clone())\n            .unwrap_or_default();\n\n        let stop_mode = match stop_mode {\n            StopMode::Standby => quote! { Standby },\n            StopMode::Stop2 => quote! { Stop2 },\n            StopMode::Stop1 => quote! { Stop1 },\n        };\n\n        let name = format_ident!(\"{}\", ch.name);\n        let idx = ch_idx as u8;\n\n        // Get the interrupt type for this DMA channel\n        let irq_name = dma_ch_to_irq\n            .get(ch.name)\n            .and_then(|v| v.first())\n            .unwrap_or_else(|| panic!(\"failed to find dma interrupt for channel {}\", ch.name));\n        let irq_ident = format_ident!(\"{}\", irq_name);\n        let irq_type = quote!(crate::interrupt::typelevel::#irq_ident);\n\n        #[cfg(feature = \"_dual-core\")]\n        let irq_pac = quote!(crate::pac::Interrupt::#irq_ident);\n\n        g.extend(quote!(dma_channel_impl!(#name, #idx, #irq_type);));\n\n        let dma = format_ident!(\"{}\", ch.dma);\n        let ch_num = ch.channel as usize;\n        let bi = dma_peri.registers.as_ref().unwrap();\n\n        let dma_info = match bi.kind {\n            \"dma\" => quote!(crate::dma::DmaInfo::Dma(crate::pac::#dma)),\n            \"bdma\" => quote!(crate::dma::DmaInfo::Bdma(crate::pac::#dma)),\n            \"gpdma\" => quote!(crate::pac::#dma),\n            \"mdma\" => quote!(crate::dma::DmaInfo::Mdma(crate::pac::#dma)),\n            \"lpdma\" => {\n                quote!(unsafe { crate::pac::gpdma::Gpdma::from_ptr(crate::pac::#dma.as_ptr())})\n            }\n            _ => panic!(\"bad dma channel kind {}\", bi.kind),\n        };\n\n        let dmamux = if has_dmamux {\n            match &ch.dmamux {\n                Some(dmamux) => {\n                    let dmamux = format_ident!(\"{}\", dmamux);\n                    let num = ch.dmamux_channel.unwrap() as usize;\n                    quote! {\n                        dmamux: Some(crate::dma::DmamuxInfo {\n                            mux: crate::pac::#dmamux,\n                            num: #num,\n                        }),\n                    }\n                }\n                None => quote!(dmamux: None),\n            }\n        } else {\n            quote!()\n        };\n\n        #[cfg(not(feature = \"_dual-core\"))]\n        dmas.extend(quote! {\n            crate::dma::ChannelInfo {\n                dma: #dma_info,\n                num: #ch_num,\n                #[cfg(feature = \"low-power\")]\n                stop_mode: crate::rcc::StopMode::#stop_mode,\n                #dmamux\n            },\n        });\n        #[cfg(feature = \"_dual-core\")]\n        dmas.extend(quote! {\n            crate::dma::ChannelInfo {\n                dma: #dma_info,\n                num: #ch_num,\n                irq: #irq_pac,\n                #[cfg(feature = \"low-power\")]\n                stop_mode: crate::rcc::StopMode::#stop_mode,\n                #dmamux\n            },\n        });\n    }\n\n    g.extend(quote! {\n        pub(crate) const DMA_CHANNELS: &[crate::dma::ChannelInfo] = &[#dmas];\n    });\n\n    // ========\n    // Generate gpio_block() function\n\n    let gpio_base = METADATA.peripherals.iter().find(|p| p.name == \"GPIOA\").unwrap().address as usize;\n    let gpio_stride = 0x400 as usize;\n\n    for p in METADATA.peripherals {\n        if let Some(bi) = &p.registers {\n            if bi.kind == \"gpio\" {\n                assert_eq!(0, (p.address as usize - gpio_base) % gpio_stride);\n            }\n        }\n    }\n\n    g.extend(quote!(\n        pub const fn gpio_block(port_num: usize) -> crate::pac::gpio::Gpio {\n            #[cfg(stm32n6)]\n            let port_num = if port_num > 7 {\n                port_num + 5 // Ports I-M are not present\n            } else {\n                port_num\n            };\n\n            unsafe { crate::pac::gpio::Gpio::from_ptr((#gpio_base + #gpio_stride*port_num) as _) }\n        }\n    ));\n\n    // ========\n    // Generate backup sram constants\n    if let Some(m) = memory.iter().find(|m| m.name == \"BKPSRAM\") {\n        let bkpsram_base = m.address as usize;\n        let bkpsram_size = m.size as usize;\n\n        g.extend(quote!(\n            pub const BKPSRAM_BASE: usize = #bkpsram_base;\n            pub const BKPSRAM_SIZE: usize = #bkpsram_size;\n        ));\n    }\n\n    // Generate constants identifying Tighly Coupled Ram regions\n    if let Some(m) = memory.iter().find(|m| m.name == \"ITCM\") {\n        let start = m.address;\n        let end = m.address + m.size;\n\n        g.extend(quote!(\n            pub const MEMORY_REGION_ITCM: core::ops::Range<u32> = #start..#end;\n        ));\n    }\n\n    if let Some(m) = memory.iter().find(|m| m.name == \"DTCM\") {\n        let start = m.address;\n        let end = m.address + m.size;\n\n        g.extend(quote!(\n            pub const MEMORY_REGION_DTCM: core::ops::Range<u32> = #start..#end;\n        ));\n    }\n\n    // ========\n    // Generate flash constants\n\n    if has_flash {\n        let flash_regions: Vec<&MemoryRegion> = memory\n            .iter()\n            .filter(|x| x.kind == MemoryRegionKind::Flash && x.name.starts_with(\"BANK_\"))\n            .collect();\n        let first_flash = flash_regions.iter().min_by_key(|region| region.address).unwrap();\n        let total_flash_size = flash_regions\n            .iter()\n            .map(|x| x.size)\n            .reduce(|acc, item| acc + item)\n            .unwrap();\n        let write_sizes: HashSet<_> = flash_regions\n            .iter()\n            .map(|r| r.settings.as_ref().unwrap().write_size)\n            .collect();\n        assert_eq!(1, write_sizes.len());\n\n        let flash_base = first_flash.address as usize;\n        let total_flash_size = total_flash_size as usize;\n        let write_size = (*write_sizes.iter().next().unwrap()) as usize;\n\n        g.extend(quote!(\n            pub const FLASH_BASE: usize = #flash_base;\n            pub const FLASH_SIZE: usize = #total_flash_size;\n            pub const WRITE_SIZE: usize = #write_size;\n        ));\n    }\n\n    // ========\n    // Generate EEPROM constants\n\n    cfgs.declare(\"eeprom\");\n\n    let eeprom_memory_regions: Vec<&MemoryRegion> =\n        memory.iter().filter(|x| x.kind == MemoryRegionKind::Eeprom).collect();\n\n    if !eeprom_memory_regions.is_empty() {\n        cfgs.enable(\"eeprom\");\n\n        let mut sorted_eeprom_regions = eeprom_memory_regions.clone();\n        sorted_eeprom_regions.sort_by_key(|r| r.address);\n\n        let first_eeprom_address = sorted_eeprom_regions[0].address;\n        let mut total_eeprom_size = 0;\n        let mut current_expected_address = first_eeprom_address;\n\n        for region in sorted_eeprom_regions.iter() {\n            if region.address != current_expected_address {\n                // For STM32L0 and STM32L1, EEPROM regions (if multiple) are expected to be contiguous.\n                // If they are not, this indicates an issue with the chip metadata or an unsupported configuration.\n                panic!(\n                    \"EEPROM regions for chip {} are not contiguous, which is unexpected for L0/L1 series. \\\n                    First region: '{}' at {:#X}. Found next non-contiguous region: '{}' at {:#X}. \\\n                    Please verify chip metadata. Embassy currently assumes contiguous EEPROM for these series.\",\n                    chip_name, sorted_eeprom_regions[0].name, first_eeprom_address, region.name, region.address\n                );\n            }\n            total_eeprom_size += region.size;\n            current_expected_address += region.size;\n        }\n\n        let eeprom_base_usize = first_eeprom_address as usize;\n        let total_eeprom_size_usize = total_eeprom_size as usize;\n\n        g.extend(quote! {\n            pub const EEPROM_BASE: usize = #eeprom_base_usize;\n            pub const EEPROM_SIZE: usize = #total_eeprom_size_usize;\n        });\n    }\n\n    // ========\n    // Generate macro-tables\n\n    for irq in METADATA.interrupts {\n        let name = irq.name.to_ascii_uppercase();\n        interrupts_table.push(vec![name.clone()]);\n        if name.contains(\"EXTI\") {\n            interrupts_table.push(vec![\"EXTI\".to_string(), name.clone()]);\n        }\n    }\n\n    let mut m = clocks_macro.to_string();\n\n    // DO NOT ADD more macros like these.\n    // These turned to be a bad idea!\n    // Instead, make build.rs generate the final code.\n    make_table(&mut m, \"foreach_flash_region\", &flash_regions_table);\n    make_table(&mut m, \"foreach_interrupt\", &interrupts_table);\n    make_table(&mut m, \"foreach_peripheral\", &peripherals_table);\n    make_table(&mut m, \"foreach_pin\", &pins_table);\n    make_table(&mut m, \"foreach_adc\", &adc_table);\n\n    let out_dir = &PathBuf::from(env::var_os(\"OUT_DIR\").unwrap());\n    let out_file = out_dir.join(\"_macros.rs\").to_string_lossy().to_string();\n    fs::write(&out_file, m).unwrap();\n    rustfmt(&out_file);\n\n    // ========\n    // Write generated.rs\n\n    let out_file = out_dir.join(\"_generated.rs\").to_string_lossy().to_string();\n    fs::write(&out_file, g.to_string()).unwrap();\n    rustfmt(&out_file);\n\n    // ========\n    // Configs for multicore and for targeting groups of chips\n\n    fn get_chip_cfgs(chip_name: &str) -> Vec<String> {\n        let mut cfgs = Vec::new();\n\n        // Multicore\n\n        let mut s = chip_name.split('_');\n        let mut chip_name: String = s.next().unwrap().to_string();\n        let core_name = if let Some(c) = s.next() {\n            if !c.starts_with(\"CM\") {\n                chip_name.push('_');\n                chip_name.push_str(c);\n                None\n            } else {\n                Some(c)\n            }\n        } else {\n            None\n        };\n\n        if let Some(core) = core_name {\n            cfgs.push(format!(\"{}_{}\", &chip_name[..chip_name.len() - 2], core));\n        }\n\n        // Configs for targeting groups of chips\n        if &chip_name[..8] == \"stm32wba\" {\n            cfgs.push(chip_name[..8].to_owned()); // stm32wba\n            cfgs.push(chip_name[..10].to_owned()); // stm32wba52\n            cfgs.push(format!(\"package_{}\", &chip_name[10..11]));\n            cfgs.push(format!(\"flashsize_{}\", &chip_name[11..12]));\n        } else {\n            if &chip_name[..8] == \"stm32h7r\" || &chip_name[..8] == \"stm32h7s\" {\n                cfgs.push(\"stm32h7rs\".to_owned());\n            } else {\n                cfgs.push(chip_name[..7].to_owned()); // stm32f4\n            }\n            cfgs.push(chip_name[..9].to_owned()); // stm32f429\n            cfgs.push(format!(\"{}x\", &chip_name[..8])); // stm32f42x\n            cfgs.push(format!(\"{}x{}\", &chip_name[..7], &chip_name[8..9])); // stm32f4x9\n            cfgs.push(format!(\"package_{}\", &chip_name[9..10]));\n            cfgs.push(format!(\"flashsize_{}\", &chip_name[10..11]));\n        }\n\n        // Mark the L4+ chips as they have many differences to regular L4.\n        if &chip_name[..7] == \"stm32l4\" {\n            if \"pqrs\".contains(&chip_name[7..8]) {\n                cfgs.push(\"stm32l4_plus\".to_owned());\n            } else {\n                cfgs.push(\"stm32l4_nonplus\".to_owned());\n            }\n        }\n\n        cfgs\n    }\n\n    cfgs.enable_all(&get_chip_cfgs(&chip_name));\n    for &chip_name in ALL_CHIPS.iter() {\n        cfgs.declare_all(&get_chip_cfgs(&chip_name.to_ascii_lowercase()));\n    }\n\n    println!(\"cargo:rerun-if-changed=build.rs\");\n\n    if cfg!(feature = \"memory-x\") {\n        gen_memory_x(memory, out_dir);\n        println!(\"cargo:rustc-link-search={}\", out_dir.display());\n    }\n}\n\nenum GetOneError {\n    None,\n    Multiple,\n}\n\ntrait IteratorExt: Iterator {\n    fn get_one(self) -> Result<Self::Item, GetOneError>;\n}\n\nimpl<T: Iterator> IteratorExt for T {\n    fn get_one(mut self) -> Result<Self::Item, GetOneError> {\n        match self.next() {\n            None => Err(GetOneError::None),\n            Some(res) => match self.next() {\n                Some(_) => Err(GetOneError::Multiple),\n                None => Ok(res),\n            },\n        }\n    }\n}\n\nfn make_table(out: &mut String, name: &str, data: &Vec<Vec<String>>) {\n    write!(\n        out,\n        \"#[allow(unused)]\nmacro_rules! {} {{\n    ($($pat:tt => $code:tt;)*) => {{\n        macro_rules! __{}_inner {{\n            $(($pat) => $code;)*\n            ($_:tt) => {{}}\n        }}\n\",\n        name, name\n    )\n    .unwrap();\n\n    for row in data {\n        writeln!(out, \"        __{}_inner!(({}));\", name, row.join(\",\")).unwrap();\n    }\n\n    write!(\n        out,\n        \"    }};\n}}\"\n    )\n    .unwrap();\n}\n\nfn get_flash_region_name(name: &str) -> String {\n    let name = name.replace(\"BANK_\", \"BANK\").replace(\"REGION_\", \"REGION\");\n    if name.contains(\"REGION\") {\n        name\n    } else {\n        name + \"_REGION\"\n    }\n}\n\nfn get_flash_region_type_name(name: &str) -> String {\n    get_flash_region_name(name)\n        .replace(\"BANK\", \"Bank\")\n        .replace(\"REGION\", \"Region\")\n        .replace('_', \"\")\n}\n\n/// rustfmt a given path.\n/// Failures are logged to stderr and ignored.\nfn rustfmt(path: impl AsRef<Path>) {\n    let path = path.as_ref();\n    match Command::new(\"rustfmt\").args([path]).output() {\n        Err(e) => {\n            eprintln!(\"failed to exec rustfmt {:?}: {:?}\", path, e);\n        }\n        Ok(out) => {\n            if !out.status.success() {\n                eprintln!(\"rustfmt {:?} failed:\", path);\n                eprintln!(\"=== STDOUT:\");\n                std::io::stderr().write_all(&out.stdout).unwrap();\n                eprintln!(\"=== STDERR:\");\n                std::io::stderr().write_all(&out.stderr).unwrap();\n            }\n        }\n    }\n}\n\nfn gen_memory_x(memory: &[MemoryRegion], out_dir: &Path) {\n    let mut memory_x = String::new();\n\n    let flash = get_memory_range(memory, MemoryRegionKind::Flash);\n    let ram = get_memory_range(memory, MemoryRegionKind::Ram);\n\n    write!(memory_x, \"MEMORY\\n{{\\n\").unwrap();\n    writeln!(\n        memory_x,\n        \"    FLASH : ORIGIN = 0x{:08x}, LENGTH = {:>4}K /* {} */\",\n        flash.0,\n        flash.1 / 1024,\n        flash.2\n    )\n    .unwrap();\n    writeln!(\n        memory_x,\n        \"    RAM   : ORIGIN = 0x{:08x}, LENGTH = {:>4}K /* {} */\",\n        ram.0,\n        ram.1 / 1024,\n        ram.2\n    )\n    .unwrap();\n    write!(memory_x, \"}}\").unwrap();\n\n    std::fs::write(out_dir.join(\"memory.x\"), memory_x.as_bytes()).unwrap();\n}\n\nfn get_memory_range(memory: &[MemoryRegion], kind: MemoryRegionKind) -> (u32, u32, String) {\n    let mut mems: Vec<_> = memory.iter().filter(|m| m.kind == kind && m.size != 0).collect();\n    mems.sort_by_key(|m| m.address);\n\n    let mut start = u32::MAX;\n    let mut end = u32::MAX;\n    let mut names = Vec::new();\n    let mut best: Option<(u32, u32, String)> = None;\n    for m in mems {\n        if !mem_filter(&METADATA.name, &m.name) {\n            continue;\n        }\n\n        if m.address != end {\n            names = Vec::new();\n            start = m.address;\n            end = m.address;\n        }\n\n        end += m.size;\n        names.push(m.name.to_string());\n\n        if best.is_none() || end - start > best.as_ref().unwrap().1 {\n            best = Some((start, end - start, names.join(\" + \")));\n        }\n    }\n\n    best.unwrap()\n}\n\nfn mem_filter(chip: &str, region: &str) -> bool {\n    // in STM32WB, SRAM2a/SRAM2b are reserved for the radio core.\n    if chip.starts_with(\"STM32WB\")\n        && !chip.starts_with(\"STM32WBA\")\n        && !chip.starts_with(\"STM32WB0\")\n        && region.starts_with(\"SRAM2\")\n    {\n        return false;\n    }\n\n    if region.starts_with(\"SDRAM_\") || region.starts_with(\"FMC_\") || region.starts_with(\"OCTOSPI_\") {\n        return false;\n    }\n\n    true\n}\n\nfn parse_adc_pin_signal(signal: &str) -> Option<(u8, bool)> {\n    if signal.starts_with(\"INP\") {\n        Some((signal.strip_prefix(\"INP\").unwrap().parse().unwrap(), false))\n    } else if signal.starts_with(\"INN\") {\n        Some((signal.strip_prefix(\"INN\").unwrap().parse().unwrap(), true))\n    } else if signal.starts_with(\"IN\") && signal.ends_with('b') {\n        // We number STM32L1 ADC bank 1 as 0..=31, bank 2 as 32..=63.\n        let signal = signal.strip_prefix(\"IN\").unwrap().strip_suffix('b').unwrap();\n        Some((32u8 + signal.parse::<u8>().unwrap(), false))\n    } else if signal.starts_with(\"IN\") {\n        Some((signal.strip_prefix(\"IN\").unwrap().parse().unwrap(), false))\n    } else {\n        None\n    }\n}\n\n#[derive(Copy, Clone, Debug)]\nstruct Frac {\n    num: u32,\n    denom: u32,\n}\n\nimpl Frac {\n    fn simplify(self) -> Self {\n        let d = gcd(self.num, self.denom);\n        Self {\n            num: self.num / d,\n            denom: self.denom / d,\n        }\n    }\n}\n\nfn gcd(a: u32, b: u32) -> u32 {\n    if b == 0 {\n        return a;\n    }\n    gcd(b, a % b)\n}\n\nfn is_bool_field(peripheral: &str, register: &str, field: &str) -> bool {\n    let field_metadata = METADATA\n        .peripherals\n        .iter()\n        .filter(|p| p.name == peripheral)\n        .flat_map(|p| p.registers.as_ref().unwrap().ir.fieldsets.iter())\n        .filter(|f| f.name.eq_ignore_ascii_case(register))\n        .flat_map(|f| f.fields.iter())\n        .find(|f| f.name.eq_ignore_ascii_case(field))\n        .unwrap();\n\n    field_metadata.bit_size == 1\n}\n"
  },
  {
    "path": "embassy-stm32/build_common.rs",
    "content": "// NOTE: this file is copy-pasted between several Embassy crates, because there is no\n// straightforward way to share this code:\n// - it cannot be placed into the root of the repo and linked from each build.rs using `#[path =\n// \"../build_common.rs\"]`, because `cargo publish` requires that all files published with a crate\n// reside in the crate's directory,\n// - it cannot be symlinked from `embassy-xxx/build_common.rs` to `../build_common.rs`, because\n// symlinks don't work on Windows.\n\nuse std::collections::HashSet;\nuse std::env;\n\n/// Helper for emitting cargo instruction for enabling configs (`cargo:rustc-cfg=X`) and declaring\n/// them (`cargo:rust-check-cfg=cfg(X)`).\n#[derive(Debug)]\npub struct CfgSet {\n    enabled: HashSet<String>,\n    declared: HashSet<String>,\n}\n\nimpl CfgSet {\n    pub fn new() -> Self {\n        Self {\n            enabled: HashSet::new(),\n            declared: HashSet::new(),\n        }\n    }\n\n    /// Enable a config, which can then be used in `#[cfg(...)]` for conditional compilation.\n    ///\n    /// All configs that can potentially be enabled should be unconditionally declared using\n    /// [`Self::declare()`].\n    pub fn enable(&mut self, cfg: impl AsRef<str>) {\n        if self.enabled.insert(cfg.as_ref().to_owned()) {\n            println!(\"cargo:rustc-cfg={}\", cfg.as_ref());\n        }\n    }\n\n    pub fn enable_all(&mut self, cfgs: &[impl AsRef<str>]) {\n        for cfg in cfgs.iter() {\n            self.enable(cfg.as_ref());\n        }\n    }\n\n    /// Declare a valid config for conditional compilation, without enabling it.\n    ///\n    /// This enables rustc to check that the configs in `#[cfg(...)]` attributes are valid.\n    pub fn declare(&mut self, cfg: impl AsRef<str>) {\n        if self.declared.insert(cfg.as_ref().to_owned()) {\n            println!(\"cargo:rustc-check-cfg=cfg({})\", cfg.as_ref());\n        }\n    }\n\n    pub fn declare_all(&mut self, cfgs: &[impl AsRef<str>]) {\n        for cfg in cfgs.iter() {\n            self.declare(cfg.as_ref());\n        }\n    }\n\n    pub fn set(&mut self, cfg: impl Into<String>, enable: bool) {\n        let cfg = cfg.into();\n        if enable {\n            self.enable(cfg.clone());\n        }\n        self.declare(cfg);\n    }\n}\n\n/// Sets configs that describe the target platform.\npub fn set_target_cfgs(cfgs: &mut CfgSet) {\n    let target = env::var(\"TARGET\").unwrap();\n\n    if target.starts_with(\"thumbv6m-\") {\n        cfgs.enable_all(&[\"cortex_m\", \"armv6m\"]);\n    } else if target.starts_with(\"thumbv7m-\") {\n        cfgs.enable_all(&[\"cortex_m\", \"armv7m\"]);\n    } else if target.starts_with(\"thumbv7em-\") {\n        cfgs.enable_all(&[\"cortex_m\", \"armv7m\", \"armv7em\"]);\n    } else if target.starts_with(\"thumbv8m.base\") {\n        cfgs.enable_all(&[\"cortex_m\", \"armv8m\", \"armv8m_base\"]);\n    } else if target.starts_with(\"thumbv8m.main\") {\n        cfgs.enable_all(&[\"cortex_m\", \"armv8m\", \"armv8m_main\"]);\n    }\n    cfgs.declare_all(&[\n        \"cortex_m\",\n        \"armv6m\",\n        \"armv7m\",\n        \"armv7em\",\n        \"armv8m\",\n        \"armv8m_base\",\n        \"armv8m_main\",\n    ]);\n\n    cfgs.set(\"has_fpu\", target.ends_with(\"-eabihf\"));\n}\n"
  },
  {
    "path": "embassy-stm32/src/adc/adc4.rs",
    "content": "#[cfg(stm32u5)]\nuse pac::adc::vals::{Adc4Dmacfg as Dmacfg, Adc4Exten as Exten, Adc4OversamplingRatio as OversamplingRatio};\n#[allow(unused)]\n#[cfg(stm32wba)]\nuse pac::adc::vals::{Chselrmod, Cont, Dmacfg, Exten, OversamplingRatio, Ovss, Smpsel};\n\nuse super::blocking_delay_us;\nuse crate::adc::{AdcRegs, ConversionMode, Instance};\n#[cfg(stm32u5)]\npub use crate::pac::adc::regs::Adc4Chselrmod0 as Chselr;\n#[cfg(stm32wba)]\npub use crate::pac::adc::regs::Chselr;\n#[cfg(stm32u5)]\npub use crate::pac::adc::vals::{Adc4Presc as Presc, Adc4Res as Resolution, Adc4SampleTime as SampleTime};\n#[cfg(stm32wba)]\npub use crate::pac::adc::vals::{Presc, Res as Resolution, SampleTime};\nuse crate::time::Hertz;\nuse crate::{Peri, pac, rcc};\n\nconst MAX_ADC_CLK_FREQ: Hertz = Hertz::mhz(55);\n\n/// Default VREF voltage used for sample conversion to millivolts.\npub const VREF_DEFAULT_MV: u32 = 3300;\n/// VREF voltage used for factory calibration of VREFINTCAL and TSCAL registers (3.0V).\npub const VREF_CALIB_MV: u32 = 3000;\n\n/// Temperature at which TS_CAL1 was measured (30°C).\npub const TS_CAL1_TEMP_C: i32 = 30;\n/// Temperature at which TS_CAL2 was measured (130°C).\npub const TS_CAL2_TEMP_C: i32 = 130;\n\n/// Factory calibration values read from the DESIG peripheral.\n///\n/// These values are programmed during manufacturing and can be used\n/// for accurate temperature and voltage measurements.\n#[derive(Clone, Copy, Debug)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub struct Calibration {\n    /// Temperature sensor calibration value at 30°C (12-bit).\n    pub ts_cal1: u16,\n    /// Temperature sensor calibration value at 130°C (12-bit).\n    pub ts_cal2: u16,\n    /// Internal voltage reference calibration value (12-bit).\n    /// Measured at VDDA = 3.0V.\n    pub vrefint_cal: u16,\n}\n\nimpl Calibration {\n    /// Read factory calibration values from the DESIG and VREFINTCAL peripherals.\n    ///\n    /// These values are unique to each chip and were measured during manufacturing\n    /// at VDDA = 3.0V.\n    #[cfg(stm32wba)]\n    pub fn read() -> Self {\n        Self {\n            ts_cal1: pac::DESIG.tscal1r().read().ts_cal1(),\n            ts_cal2: pac::DESIG.tscal2r().read().ts_cal2(),\n            vrefint_cal: pac::VREFINTCAL.data().read().vrefint_cal(),\n        }\n    }\n\n    /// Convert a temperature sensor ADC reading to temperature in millidegrees Celsius.\n    ///\n    /// This function applies VDDA compensation using the VREFINT reading to account\n    /// for differences between the actual supply voltage and the 3.0V calibration voltage.\n    ///\n    /// # Arguments\n    /// * `ts_data` - Raw ADC reading from the temperature sensor channel\n    /// * `vrefint_data` - Raw ADC reading from the VREFINT channel (for VDDA compensation)\n    ///\n    /// # Returns\n    /// Temperature in millidegrees Celsius (e.g., 25000 = 25.000°C)\n    ///\n    /// # Example\n    /// ```ignore\n    /// let cal = Calibration::read();\n    /// let temp_mc = cal.convert_to_millicelsius(temp_adc_reading, vrefint_adc_reading);\n    /// let temp_c = temp_mc / 1000;\n    /// let temp_frac = (temp_mc % 1000).unsigned_abs();\n    /// info!(\"Temperature: {}.{:03} C\", temp_c, temp_frac);\n    /// ```\n    pub fn convert_to_millicelsius(&self, ts_data: u32, vrefint_data: u32) -> i32 {\n        // Compensate TS_DATA for actual VDDA vs calibration VDDA (3.0V)\n        // TS_DATA_compensated = TS_DATA * VREFINT_CAL / VREFINT_DATA\n        let ts_data_comp = if vrefint_data > 0 {\n            (ts_data * self.vrefint_cal as u32) / vrefint_data\n        } else {\n            ts_data\n        };\n\n        // Use i32 for signed arithmetic (temperature can be negative)\n        let ts_data_comp = ts_data_comp as i32;\n        let ts_cal1 = self.ts_cal1 as i32;\n        let ts_cal2 = self.ts_cal2 as i32;\n\n        // Calculate temperature in millidegrees\n        // Temp_mC = TS_CAL1_TEMP * 1000 + (TS_CAL2_TEMP - TS_CAL1_TEMP) * 1000 * (TS_DATA - TS_CAL1) / (TS_CAL2 - TS_CAL1)\n        let delta_temp = (TS_CAL2_TEMP_C - TS_CAL1_TEMP_C) * 1000; // 100000 millidegrees\n        let delta_cal = ts_cal2 - ts_cal1;\n\n        if delta_cal == 0 {\n            // Avoid division by zero - return raw estimate\n            return ts_data_comp * 10;\n        }\n\n        TS_CAL1_TEMP_C * 1000 + (delta_temp * (ts_data_comp - ts_cal1)) / delta_cal\n    }\n\n    /// Calculate the actual VDDA voltage in millivolts using VREFINT.\n    ///\n    /// The formula is: VDDA = 3000mV × VREFINT_CAL / VREFINT_DATA\n    ///\n    /// # Arguments\n    /// * `vrefint_data` - Raw ADC reading from the VREFINT channel\n    ///\n    /// # Returns\n    /// Actual VDDA voltage in millivolts\n    pub fn calculate_vdda_mv(&self, vrefint_data: u32) -> u32 {\n        if vrefint_data > 0 {\n            (VREF_CALIB_MV * self.vrefint_cal as u32) / vrefint_data\n        } else {\n            VREF_DEFAULT_MV\n        }\n    }\n}\n\nimpl super::SealedSpecialConverter<super::VrefInt> for crate::peripherals::ADC4 {\n    const CHANNEL: u8 = 0;\n}\n\nimpl super::SealedSpecialConverter<super::Temperature> for crate::peripherals::ADC4 {\n    const CHANNEL: u8 = 13;\n}\n\nimpl super::SealedSpecialConverter<super::Vcore> for crate::peripherals::ADC4 {\n    const CHANNEL: u8 = 12;\n}\n\nimpl super::SealedSpecialConverter<super::Vbat> for crate::peripherals::ADC4 {\n    const CHANNEL: u8 = 14;\n}\n\nimpl super::SealedSpecialConverter<super::Dac> for crate::peripherals::ADC4 {\n    const CHANNEL: u8 = 21;\n}\n\n#[derive(Copy, Clone)]\npub enum DacChannel {\n    OUT1,\n    OUT2,\n}\n\n/// Number of samples used for averaging.\n#[derive(Copy, Clone, Debug)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub enum Averaging {\n    Disabled,\n    Samples2,\n    Samples4,\n    Samples8,\n    Samples16,\n    Samples32,\n    Samples64,\n    Samples128,\n    Samples256,\n}\n\npub const fn resolution_to_max_count(res: Resolution) -> u32 {\n    match res {\n        Resolution::BITS12 => (1 << 12) - 1,\n        Resolution::BITS10 => (1 << 10) - 1,\n        Resolution::BITS8 => (1 << 8) - 1,\n        Resolution::BITS6 => (1 << 6) - 1,\n        #[allow(unreachable_patterns)]\n        _ => core::unreachable!(),\n    }\n}\n\nfn from_ker_ck(frequency: Hertz) -> Presc {\n    let raw_prescaler = rcc::raw_prescaler(frequency.0, MAX_ADC_CLK_FREQ.0);\n    match raw_prescaler {\n        0 => Presc::DIV1,\n        1 => Presc::DIV2,\n        2..=3 => Presc::DIV4,\n        4..=5 => Presc::DIV6,\n        6..=7 => Presc::DIV8,\n        8..=9 => Presc::DIV10,\n        10..=11 => Presc::DIV12,\n        _ => unimplemented!(),\n    }\n}\n\nimpl AdcRegs for crate::pac::adc::Adc4 {\n    fn data(&self) -> *mut u16 {\n        crate::pac::adc::Adc4::dr(*self).as_ptr() as *mut u16\n    }\n\n    fn enable(&self) {\n        if !self.cr().read().aden() || !self.isr().read().adrdy() {\n            self.isr().write(|w| w.set_adrdy(true));\n            self.cr().modify(|w| w.set_aden(true));\n            while !self.isr().read().adrdy() {}\n        }\n    }\n\n    fn start(&self) {\n        // Start conversion\n        self.cr().modify(|reg| {\n            reg.set_adstart(true);\n        });\n    }\n\n    fn stop(&self) {\n        let cr = self.cr().read();\n        if cr.adstart() {\n            self.cr().modify(|w| w.set_adstp(true));\n            while self.cr().read().adstart() {}\n        }\n\n        if cr.aden() || cr.adstart() {\n            self.cr().modify(|w| w.set_addis(true));\n            while self.cr().read().aden() {}\n        }\n\n        // Reset configuration.\n        self.cfgr1().modify(|reg| {\n            reg.set_dmaen(false);\n        });\n    }\n\n    fn configure_dma(&self, conversion_mode: ConversionMode) {\n        // Clear overrun and conversion flags\n        self.isr().modify(|reg| {\n            reg.set_ovr(true);\n            reg.set_eos(true);\n            reg.set_eoc(true);\n        });\n\n        match conversion_mode {\n            ConversionMode::Singular => {\n                self.cfgr1().modify(|reg| {\n                    reg.set_dmaen(true);\n                    reg.set_dmacfg(Dmacfg::ONE_SHOT);\n                    reg.set_discen(false);\n                    #[cfg(stm32u5)]\n                    {\n                        reg.set_cont(false);\n                        reg.set_chselrmod(false);\n                    }\n                    #[cfg(stm32wba)]\n                    {\n                        reg.set_cont(Cont::SINGLE);\n                        reg.set_chselrmod(Chselrmod::ENABLE_INPUT);\n                    }\n                });\n            }\n            #[cfg(any(adc_v2, adc_g4, adc_v3, adc_g0, adc_u0))]\n            ConversionMode::Repeated(_) => unreachable!(),\n            #[cfg(stm32wba)]\n            ConversionMode::Repeated(_mode) => {\n                // Configure for circular DMA with continuous conversion\n                self.cfgr1().modify(|reg| {\n                    reg.set_dmaen(true);\n                    reg.set_dmacfg(Dmacfg::CIRCULAR); // Enable circular DMA mode\n                    reg.set_cont(Cont::CONTINUOUS); // Enable continuous conversion\n                    reg.set_discen(false); // Disable discontinuous mode\n                    reg.set_chselrmod(Chselrmod::ENABLE_INPUT);\n                });\n            }\n        }\n    }\n\n    fn configure_sequence(&self, sequence: impl ExactSizeIterator<Item = ((u8, bool), SampleTime)>) {\n        let mut prev_channel: i16 = -1;\n        #[cfg(stm32wba)]\n        self.chselr().write_value(Chselr(0_u32));\n        #[cfg(stm32u5)]\n        self.chselrmod0().write_value(Chselr(0_u32));\n\n        #[cfg(stm32wba)]\n        let mut first_sample_time: Option<SampleTime> = None;\n\n        for (_i, ((channel, _), sample_time)) in sequence.enumerate() {\n            // For STM32WBA: SMPR only has 2 sample time slots (SMP1, SMP2).\n            // We use SMP1 for all channels with the first channel's sample time.\n            // For STM32U5: Each channel can have its own sample time.\n            #[cfg(stm32u5)]\n            self.smpr().modify(|w| {\n                w.set_smp(_i, sample_time);\n            });\n\n            #[cfg(stm32wba)]\n            {\n                // Set SMP1 (index 0) with the first channel's sample time, use it for all channels\n                if first_sample_time.is_none() {\n                    first_sample_time = Some(sample_time);\n                    self.smpr().modify(|w| {\n                        w.set_smp(0, sample_time); // Index 0 = SMP1\n                    });\n                }\n                // Set SMPSEL for this channel to use SMP1\n                self.smpr().modify(|w| {\n                    w.set_smpsel(channel as usize, Smpsel::SMP1);\n                });\n            }\n\n            let channel_num = channel;\n            if channel_num as i16 <= prev_channel {\n                return;\n            };\n            prev_channel = channel_num as i16;\n\n            #[cfg(stm32wba)]\n            self.chselr().modify(|w| {\n                w.set_chsel0(channel as usize, true);\n            });\n            #[cfg(stm32u5)]\n            self.chselrmod0().modify(|w| {\n                w.set_chsel(channel as usize, true);\n            });\n        }\n    }\n\n    fn convert(&self) {\n        // Reset interrupts\n        self.isr().modify(|reg| {\n            reg.set_eos(true);\n            reg.set_eoc(true);\n        });\n\n        // Start conversion\n        self.cr().modify(|reg| {\n            reg.set_adstart(true);\n        });\n\n        while !self.isr().read().eos() {\n            // spin\n        }\n    }\n}\n\nimpl<'d, T: Instance<Regs = crate::pac::adc::Adc4>> super::Adc<'d, T> {\n    /// Create a new ADC driver.\n    pub fn new_adc4(adc: Peri<'d, T>) -> Self {\n        rcc::enable_and_reset::<T>();\n        let prescaler = from_ker_ck(T::frequency());\n\n        T::regs().ccr().modify(|w| w.set_presc(prescaler));\n\n        let frequency = T::frequency() / prescaler;\n        info!(\"ADC4 frequency set to {}\", frequency);\n\n        if frequency > MAX_ADC_CLK_FREQ {\n            panic!(\n                \"Maximal allowed frequency for ADC4 is {} MHz and it varies with different packages, refer to ST docs for more information.\",\n                MAX_ADC_CLK_FREQ.0 / 1_000_000\n            );\n        }\n\n        T::regs().isr().modify(|w| {\n            w.set_ldordy(true);\n        });\n        T::regs().cr().modify(|w| {\n            w.set_advregen(true);\n        });\n        while !T::regs().isr().read().ldordy() {}\n\n        T::regs().isr().modify(|w| {\n            w.set_ldordy(true);\n        });\n\n        T::regs().cr().modify(|w| w.set_adcal(true));\n        while T::regs().cr().read().adcal() {}\n        T::regs().isr().modify(|w| w.set_eocal(true));\n\n        blocking_delay_us(1);\n\n        T::regs().enable();\n\n        // single conversion mode, software trigger\n        T::regs().cfgr1().modify(|w| {\n            #[cfg(stm32u5)]\n            w.set_cont(false);\n            #[cfg(stm32wba)]\n            w.set_cont(Cont::SINGLE);\n            w.set_discen(false);\n            w.set_exten(Exten::DISABLED);\n            #[cfg(stm32u5)]\n            w.set_chselrmod(false);\n            #[cfg(stm32wba)]\n            w.set_chselrmod(Chselrmod::ENABLE_INPUT);\n        });\n\n        // only use one channel at the moment\n        T::regs().smpr().modify(|w| {\n            #[cfg(stm32u5)]\n            for i in 0..24 {\n                w.set_smpsel(i, false);\n            }\n            #[cfg(stm32wba)]\n            for i in 0..14 {\n                w.set_smpsel(i, Smpsel::SMP1);\n            }\n        });\n\n        Self { adc }\n    }\n\n    /// Enable reading the voltage reference internal channel.\n    pub fn enable_vrefint_adc4(&self) -> super::VrefInt {\n        T::regs().ccr().modify(|w| {\n            w.set_vrefen(true);\n        });\n\n        super::VrefInt {}\n    }\n\n    /// Enable reading the temperature internal channel.\n    pub fn enable_temperature_adc4(&self) -> super::Temperature {\n        T::regs().ccr().modify(|w| {\n            w.set_vsensesel(true);\n        });\n\n        super::Temperature {}\n    }\n\n    /// Enable reading the vbat internal channel.\n    #[cfg(stm32u5)]\n    pub fn enable_vbat_adc4(&self) -> super::Vbat {\n        T::regs().ccr().modify(|w| {\n            w.set_vbaten(true);\n        });\n\n        super::Vbat {}\n    }\n\n    /// Enable reading the vbat internal channel.\n    pub fn enable_vcore_adc4(&self) -> super::Vcore {\n        super::Vcore {}\n    }\n\n    /// Enable reading the vbat internal channel.\n    #[cfg(stm32u5)]\n    pub fn enable_dac_channel_adc4(&self, dac: DacChannel) -> super::Dac {\n        let mux;\n        match dac {\n            DacChannel::OUT1 => mux = false,\n            DacChannel::OUT2 => mux = true,\n        }\n        T::regs().or().modify(|w| w.set_chn21sel(mux));\n        super::Dac {}\n    }\n\n    /// Set the ADC resolution.\n    pub fn set_resolution_adc4(&mut self, resolution: Resolution) {\n        T::regs().cfgr1().modify(|w| w.set_res(resolution.into()));\n    }\n\n    /// Set hardware averaging.\n    #[cfg(stm32u5)]\n    pub fn set_averaging_adc4(&mut self, averaging: Averaging) {\n        let (enable, samples, right_shift) = match averaging {\n            Averaging::Disabled => (false, OversamplingRatio::OVERSAMPLE2X, 0),\n            Averaging::Samples2 => (true, OversamplingRatio::OVERSAMPLE2X, 1),\n            Averaging::Samples4 => (true, OversamplingRatio::OVERSAMPLE4X, 2),\n            Averaging::Samples8 => (true, OversamplingRatio::OVERSAMPLE8X, 3),\n            Averaging::Samples16 => (true, OversamplingRatio::OVERSAMPLE16X, 4),\n            Averaging::Samples32 => (true, OversamplingRatio::OVERSAMPLE32X, 5),\n            Averaging::Samples64 => (true, OversamplingRatio::OVERSAMPLE64X, 6),\n            Averaging::Samples128 => (true, OversamplingRatio::OVERSAMPLE128X, 7),\n            Averaging::Samples256 => (true, OversamplingRatio::OVERSAMPLE256X, 8),\n        };\n\n        T::regs().cfgr2().modify(|w| {\n            w.set_ovsr(samples);\n            w.set_ovss(right_shift);\n            w.set_ovse(enable)\n        })\n    }\n    #[cfg(stm32wba)]\n    pub fn set_averaging_adc4(&mut self, averaging: Averaging) {\n        let (enable, samples, right_shift) = match averaging {\n            Averaging::Disabled => (false, OversamplingRatio::OVERSAMPLE2X, Ovss::SHIFT0),\n            Averaging::Samples2 => (true, OversamplingRatio::OVERSAMPLE2X, Ovss::SHIFT1),\n            Averaging::Samples4 => (true, OversamplingRatio::OVERSAMPLE4X, Ovss::SHIFT2),\n            Averaging::Samples8 => (true, OversamplingRatio::OVERSAMPLE8X, Ovss::SHIFT3),\n            Averaging::Samples16 => (true, OversamplingRatio::OVERSAMPLE16X, Ovss::SHIFT4),\n            Averaging::Samples32 => (true, OversamplingRatio::OVERSAMPLE32X, Ovss::SHIFT5),\n            Averaging::Samples64 => (true, OversamplingRatio::OVERSAMPLE64X, Ovss::SHIFT6),\n            Averaging::Samples128 => (true, OversamplingRatio::OVERSAMPLE128X, Ovss::SHIFT7),\n            Averaging::Samples256 => (true, OversamplingRatio::OVERSAMPLE256X, Ovss::SHIFT8),\n        };\n\n        T::regs().cfgr2().modify(|w| {\n            w.set_ovsr(samples);\n            w.set_ovss(right_shift);\n            w.set_ovse(enable)\n        })\n    }\n}\n"
  },
  {
    "path": "embassy-stm32/src/adc/c0.rs",
    "content": "#[allow(unused)]\nuse pac::adc::vals::{Adstp, Align, Ckmode, Dmacfg, Exten, Ovrmod, Ovsr};\nuse pac::adccommon::vals::Presc;\nuse stm32_metapac::adc::vals::{SampleTime, Scandir};\n\nuse super::{Adc, Instance, Resolution, blocking_delay_us};\nuse crate::adc::{AdcRegs, ConversionMode};\nuse crate::time::Hertz;\nuse crate::{Peri, pac, rcc};\n\n/// Default VREF voltage used for sample conversion to millivolts.\npub const VREF_DEFAULT_MV: u32 = 3300;\n/// VREF voltage used for factory calibration of VREFINTCAL register.\npub const VREF_CALIB_MV: u32 = 3300;\n\nconst MAX_ADC_CLK_FREQ: Hertz = Hertz::mhz(25);\n\nconst TIME_ADC_VOLTAGE_REGUALTOR_STARTUP_US: u32 = 20;\n\nconst CHSELR_SQ_SIZE: usize = 8;\nconst CHSELR_SQ_MAX_CHANNEL: u8 = 14;\nconst CHSELR_SQ_SEQUENCE_END_MARKER: u8 = 0b1111;\n\nimpl<T: Instance> super::SealedSpecialConverter<super::VrefInt> for T {\n    const CHANNEL: u8 = 10;\n}\n\nimpl<T: Instance> super::SealedSpecialConverter<super::Temperature> for T {\n    const CHANNEL: u8 = 9;\n}\n\nfn from_ker_ck(frequency: Hertz) -> Presc {\n    let raw_prescaler = rcc::raw_prescaler(frequency.0, MAX_ADC_CLK_FREQ.0);\n    match raw_prescaler {\n        0 => Presc::DIV1,\n        1 => Presc::DIV2,\n        2..=3 => Presc::DIV4,\n        4..=5 => Presc::DIV6,\n        6..=7 => Presc::DIV8,\n        8..=9 => Presc::DIV10,\n        10..=11 => Presc::DIV12,\n        _ => unimplemented!(),\n    }\n}\n\nimpl AdcRegs for crate::pac::adc::Adc {\n    fn data(&self) -> *mut u16 {\n        crate::pac::adc::Adc::dr(*self).as_ptr() as *mut u16\n    }\n\n    fn enable(&self) {\n        if !self.cr().read().aden() {\n            self.isr().modify(|w| w.set_adrdy(true));\n            self.cr().modify(|w| w.set_aden(true));\n            // ADRDY is \"ADC ready\". Wait until it will be True.\n            while !self.isr().read().adrdy() {}\n        }\n    }\n\n    fn start(&self) {\n        // Start conversion\n        self.cr().modify(|reg| {\n            reg.set_adstart(true);\n        });\n    }\n\n    fn stop(&self) {\n        if self.cr().read().adstart() && !self.cr().read().addis() {\n            self.cr().modify(|reg| {\n                reg.set_adstp(Adstp::STOP);\n            });\n            while self.cr().read().adstart() {}\n        }\n\n        // Reset configuration.\n        self.cfgr1().modify(|reg| {\n            reg.set_cont(false);\n            reg.set_dmacfg(Dmacfg::from_bits(0));\n            reg.set_dmaen(false);\n        });\n    }\n\n    fn configure_dma(&self, conversion_mode: super::ConversionMode) {\n        // Enable overrun control, so no new DMA requests will be generated until\n        // previous DR values is read.\n        self.isr().modify(|reg| {\n            reg.set_ovr(true);\n        });\n\n        match conversion_mode {\n            ConversionMode::Singular => {\n                // Set continuous mode with oneshot dma.\n                self.cfgr1().modify(|reg| {\n                    reg.set_discen(false);\n                    reg.set_cont(true);\n                    reg.set_dmacfg(Dmacfg::DMA_ONE_SHOT);\n                    reg.set_dmaen(true);\n                    reg.set_ovrmod(Ovrmod::PRESERVE);\n                });\n            }\n            ConversionMode::Repeated(trigger) => {\n                match trigger.signal {\n                    u8::MAX => {\n                        // continuous conversions\n                        self.cfgr1().modify(|reg| {\n                            reg.set_discen(false);\n                            reg.set_cont(true);\n                            reg.set_dmacfg(Dmacfg::DMA_CIRCULAR);\n                            reg.set_dmaen(true);\n                            reg.set_ovrmod(Ovrmod::OVERWRITE);\n                        });\n                    }\n                    _ => {\n                        self.cfgr1().modify(|reg| {\n                            reg.set_discen(false);\n                            reg.set_cont(false); // Triggered, not continuous\n                            reg.set_dmacfg(Dmacfg::DMA_CIRCULAR);\n                            reg.set_dmaen(true);\n                            reg.set_ovrmod(Ovrmod::OVERWRITE);\n                            // Configure trigger edge (rising, falling, or both)\n                            reg.set_exten(trigger.edge);\n                            reg.set_extsel(trigger.signal);\n                        });\n\n                        // Regular conversions uses DMA so no need to generate interrupt\n                        self.ier().modify(|r| r.set_eosie(false));\n                    }\n                }\n            }\n        }\n    }\n\n    fn configure_sequence(&self, sequence: impl ExactSizeIterator<Item = ((u8, bool), Self::SampleTime)>) {\n        // TODO: get sequencer working\n        let mut needs_hw = sequence.len() == 1 || sequence.len() > CHSELR_SQ_SIZE || true;\n        let mut is_ordered_up = true;\n        let mut is_ordered_down = true;\n\n        let sequence_len = sequence.len();\n        let mut hw_channel_selection: u32 = 0;\n        let mut last_channel: u8 = 0;\n        let mut sample_time: Self::SampleTime = SampleTime::CYCLES2_5;\n\n        self.chselr_sq().write(|w| {\n            for (i, ((channel, _), _sample_time)) in sequence.enumerate() {\n                assert!(\n                    sample_time == _sample_time || i == 0,\n                    \"C0 only supports one sample time for the sequence.\"\n                );\n\n                sample_time = _sample_time;\n                needs_hw = needs_hw || channel > CHSELR_SQ_MAX_CHANNEL;\n                is_ordered_up = is_ordered_up && (channel > last_channel || i == 0);\n                is_ordered_down = is_ordered_down && (channel < last_channel || i == 0);\n                hw_channel_selection += 1 << channel;\n                last_channel = channel;\n\n                if !needs_hw {\n                    w.set_sq(i, channel);\n                }\n            }\n\n            for i in sequence_len..CHSELR_SQ_SIZE {\n                w.set_sq(i, CHSELR_SQ_SEQUENCE_END_MARKER);\n            }\n        });\n\n        if needs_hw {\n            assert!(\n                sequence_len <= CHSELR_SQ_SIZE || is_ordered_up || is_ordered_down,\n                \"Sequencer is required because of unordered channels, but read set cannot be more than {} in size.\",\n                CHSELR_SQ_SIZE\n            );\n            assert!(\n                sequence_len > CHSELR_SQ_SIZE || is_ordered_up || is_ordered_down,\n                \"Sequencer is required because of unordered channels, but only support HW channels smaller than {}.\",\n                CHSELR_SQ_MAX_CHANNEL\n            );\n\n            // Set required channels for multi-convert.\n            unsafe { (self.chselr().as_ptr() as *mut u32).write_volatile(hw_channel_selection) }\n        }\n\n        self.smpr().modify(|w| {\n            w.smpsel(0);\n            w.set_smp1(sample_time);\n        });\n\n        self.cfgr1().modify(|reg| {\n            reg.set_chselrmod(!needs_hw);\n            reg.set_align(Align::RIGHT);\n            reg.set_scandir(if is_ordered_up { Scandir::UP } else { Scandir::BACK });\n        });\n\n        // Trigger and wait for the channel selection procedure to complete.\n        self.isr().modify(|w| w.set_ccrdy(false));\n        while !self.isr().read().ccrdy() {}\n    }\n\n    fn convert(&self) {\n        // Set single conversion mode.\n        self.cfgr1().modify(|w| w.set_cont(false));\n\n        // Start conversion\n        self.cr().modify(|reg| {\n            reg.set_adstart(true);\n        });\n\n        // Waiting for End Of Conversion (EOC).\n        while !self.isr().read().eoc() {}\n    }\n}\n\nimpl<'d, T: Instance<Regs = crate::pac::adc::Adc>> Adc<'d, T> {\n    /// Create a new ADC driver.\n    pub fn new(adc: Peri<'d, T>, resolution: Resolution) -> Self {\n        rcc::enable_and_reset::<T>();\n\n        T::regs().cfgr2().modify(|w| w.set_ckmode(Ckmode::SYSCLK));\n\n        let prescaler = from_ker_ck(T::frequency());\n        T::common_regs().ccr().modify(|w| w.set_presc(prescaler));\n\n        let frequency = T::frequency() / prescaler;\n        debug!(\"ADC frequency set to {}\", frequency);\n\n        if frequency > MAX_ADC_CLK_FREQ {\n            panic!(\n                \"Maximal allowed frequency for the ADC is {} MHz and it varies with different packages, refer to ST docs for more information.\",\n                MAX_ADC_CLK_FREQ.0 / 1_000_000\n            );\n        }\n\n        T::regs().cr().modify(|reg| {\n            reg.set_advregen(true);\n        });\n\n        // \"The software must wait for the ADC voltage regulator startup time.\"\n        // See datasheet for the value.\n        blocking_delay_us(TIME_ADC_VOLTAGE_REGUALTOR_STARTUP_US as u64 + 1);\n\n        T::regs().cfgr1().modify(|reg| reg.set_res(resolution));\n\n        // We have to make sure AUTOFF is OFF, but keep its value after calibration.\n        let autoff_value = T::regs().cfgr1().read().autoff();\n        T::regs().cfgr1().modify(|w| w.set_autoff(false));\n\n        T::regs().cr().modify(|w| w.set_adcal(true));\n\n        // \"ADCAL bit stays at 1 during all the calibration sequence.\"\n        // \"It is then cleared by hardware as soon the calibration completes.\"\n        while T::regs().cr().read().adcal() {}\n\n        debug!(\"ADC calibration value: {}.\", T::regs().dr().read().data());\n\n        T::regs().cfgr1().modify(|w| w.set_autoff(autoff_value));\n\n        T::regs().enable();\n\n        // single conversion mode, software trigger\n        T::regs().cfgr1().modify(|w| {\n            w.set_cont(false);\n            w.set_exten(Exten::DISABLED);\n            w.set_align(Align::RIGHT);\n        });\n\n        Self { adc }\n    }\n\n    /// Enable reading the voltage reference internal channel.\n    pub fn enable_vrefint(&self) -> super::VrefInt {\n        T::common_regs().ccr().modify(|reg| {\n            reg.set_vrefen(true);\n        });\n\n        super::VrefInt {}\n    }\n\n    /// Enable reading the temperature internal channel.\n    pub fn enable_temperature(&self) -> super::Temperature {\n        debug!(\"Ensure that sample time is set to more than temperature sensor T_start from the datasheet!\");\n        T::common_regs().ccr().modify(|reg| {\n            reg.set_tsen(true);\n        });\n\n        super::Temperature {}\n    }\n}\n"
  },
  {
    "path": "embassy-stm32/src/adc/f1.rs",
    "content": "use core::future::poll_fn;\nuse core::marker::PhantomData;\nuse core::task::Poll;\n\nuse super::blocking_delay_us;\nuse crate::adc::{Adc, AdcChannel, Instance, SampleTime, VrefInt};\nuse crate::interrupt::typelevel::Interrupt;\nuse crate::interrupt::{self};\nuse crate::time::Hertz;\nuse crate::{Peri, rcc};\n\npub const VDDA_CALIB_MV: u32 = 3300;\npub const ADC_MAX: u32 = (1 << 12) - 1;\n// No calibration data for F103, voltage should be 1.2v\npub const VREF_INT: u32 = 1200;\n\n/// Interrupt handler.\npub struct InterruptHandler<T: Instance> {\n    _phantom: PhantomData<T>,\n}\n\nimpl<T: Instance> interrupt::typelevel::Handler<T::Interrupt> for InterruptHandler<T> {\n    unsafe fn on_interrupt() {\n        if T::regs().sr().read().eoc() {\n            T::regs().cr1().modify(|w| w.set_eocie(false)); // End of Convert interrupt disable\n            T::state().waker.wake();\n        }\n    }\n}\n\nimpl<T: Instance> super::SealedSpecialConverter<VrefInt> for T {\n    const CHANNEL: u8 = 17;\n}\n\nimpl<T: Instance> super::SealedSpecialConverter<super::Temperature> for T {\n    const CHANNEL: u8 = 16;\n}\n\nimpl<'d, T: Instance> Adc<'d, T> {\n    pub fn new(adc: Peri<'d, T>) -> Self {\n        rcc::enable_and_reset::<T>();\n        T::regs().cr2().modify(|reg| reg.set_adon(true));\n\n        // 11.4: Before starting a calibration, the ADC must have been in power-on state (ADON bit = ‘1’)\n        // for at least two ADC clock cycles.\n        blocking_delay_us((1_000_000 * 2) / Self::freq().0 as u64 + 1);\n\n        // Reset calibration\n        T::regs().cr2().modify(|reg| reg.set_rstcal(true));\n        while T::regs().cr2().read().rstcal() {\n            // spin\n        }\n\n        // Calibrate\n        T::regs().cr2().modify(|reg| reg.set_cal(true));\n        while T::regs().cr2().read().cal() {\n            // spin\n        }\n\n        // One cycle after calibration\n        blocking_delay_us((1_000_000 * 1) / Self::freq().0 as u64 + 1);\n\n        T::Interrupt::unpend();\n        unsafe { T::Interrupt::enable() };\n\n        Self { adc }\n    }\n\n    fn freq() -> Hertz {\n        T::frequency()\n    }\n\n    pub fn sample_time_for_us(&self, us: u32) -> SampleTime {\n        match us * Self::freq().0 / 1_000_000 {\n            0..=1 => SampleTime::CYCLES1_5,\n            2..=7 => SampleTime::CYCLES7_5,\n            8..=13 => SampleTime::CYCLES13_5,\n            14..=28 => SampleTime::CYCLES28_5,\n            29..=41 => SampleTime::CYCLES41_5,\n            42..=55 => SampleTime::CYCLES55_5,\n            56..=71 => SampleTime::CYCLES71_5,\n            _ => SampleTime::CYCLES239_5,\n        }\n    }\n\n    pub fn enable_vref(&self) -> super::VrefInt {\n        T::regs().cr2().modify(|reg| {\n            reg.set_tsvrefe(true);\n        });\n        super::VrefInt {}\n    }\n\n    pub fn enable_temperature(&self) -> super::Temperature {\n        T::regs().cr2().modify(|reg| {\n            reg.set_tsvrefe(true);\n        });\n        super::Temperature {}\n    }\n\n    /// Perform a single conversion.\n    async fn convert(&mut self) -> u16 {\n        T::regs().cr2().modify(|reg| {\n            reg.set_adon(true);\n            reg.set_swstart(true);\n        });\n        T::regs().cr1().modify(|w| w.set_eocie(true));\n\n        poll_fn(|cx| {\n            T::state().waker.register(cx.waker());\n\n            if !T::regs().cr2().read().swstart() && T::regs().sr().read().eoc() {\n                Poll::Ready(())\n            } else {\n                Poll::Pending\n            }\n        })\n        .await;\n\n        T::regs().dr().read().0 as u16\n    }\n\n    pub async fn read(&mut self, channel: &mut impl AdcChannel<T>, sample_time: SampleTime) -> u16 {\n        Self::set_channel_sample_time(channel.channel(), sample_time);\n        T::regs().cr1().modify(|reg| {\n            reg.set_scan(false);\n            reg.set_discen(false);\n        });\n        T::regs().sqr1().modify(|reg| reg.set_l(0));\n\n        T::regs().cr2().modify(|reg| {\n            reg.set_cont(false);\n            reg.set_exttrig(true);\n            reg.set_swstart(false);\n            reg.set_extsel(7); // SWSTART\n        });\n\n        // Configure the channel to sample\n        T::regs().sqr3().write(|reg| reg.set_sq(0, channel.channel()));\n        self.convert().await\n    }\n\n    fn set_channel_sample_time(ch: u8, sample_time: SampleTime) {\n        let sample_time = sample_time.into();\n        if ch <= 9 {\n            T::regs().smpr2().modify(|reg| reg.set_smp(ch as _, sample_time));\n        } else {\n            T::regs().smpr1().modify(|reg| reg.set_smp((ch - 10) as _, sample_time));\n        }\n    }\n}\n\nimpl<'d, T: Instance> Drop for Adc<'d, T> {\n    fn drop(&mut self) {\n        T::regs().cr2().modify(|reg| reg.set_adon(false));\n\n        rcc::disable::<T>();\n    }\n}\n"
  },
  {
    "path": "embassy-stm32/src/adc/f3.rs",
    "content": "use core::future::poll_fn;\nuse core::marker::PhantomData;\nuse core::task::Poll;\n\nuse super::blocking_delay_us;\nuse crate::adc::{Adc, AdcChannel, Instance, SampleTime, VrefInt};\nuse crate::interrupt::typelevel::Interrupt;\nuse crate::time::Hertz;\nuse crate::{Peri, interrupt, rcc};\n\npub const VDDA_CALIB_MV: u32 = 3300;\npub const ADC_MAX: u32 = (1 << 12) - 1;\npub const VREF_INT: u32 = 1230;\n\n/// Interrupt handler.\npub struct InterruptHandler<T: Instance> {\n    _phantom: PhantomData<T>,\n}\n\nimpl<T: Instance> interrupt::typelevel::Handler<T::Interrupt> for InterruptHandler<T> {\n    unsafe fn on_interrupt() {\n        if T::regs().isr().read().eoc() {\n            T::regs().ier().modify(|w| w.set_eocie(false));\n        } else {\n            return;\n        }\n\n        T::state().waker.wake();\n    }\n}\n\nimpl<T: Instance> super::SealedSpecialConverter<VrefInt> for T {\n    const CHANNEL: u8 = 18;\n}\n\nimpl<T: Instance> super::SealedSpecialConverter<super::Temperature> for T {\n    const CHANNEL: u8 = 16;\n}\n\nimpl<'d, T: Instance> Adc<'d, T> {\n    pub fn new(\n        adc: Peri<'d, T>,\n        _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd,\n    ) -> Self {\n        use crate::pac::adc::vals;\n\n        rcc::enable_and_reset::<T>();\n\n        // Enable the adc regulator\n        T::regs().cr().modify(|w| w.set_advregen(vals::Advregen::INTERMEDIATE));\n        T::regs().cr().modify(|w| w.set_advregen(vals::Advregen::ENABLED));\n\n        // Wait for the regulator to stabilize\n        blocking_delay_us(10);\n\n        assert!(!T::regs().cr().read().aden());\n\n        // Begin calibration\n        T::regs().cr().modify(|w| w.set_adcaldif(false));\n        T::regs().cr().modify(|w| w.set_adcal(true));\n\n        while T::regs().cr().read().adcal() {}\n\n        // Wait more than 4 clock cycles after adcal is cleared (RM0364 p. 223).\n        blocking_delay_us((1_000_000 * 4) / Self::freq().0 as u64 + 1);\n\n        // Enable the adc\n        T::regs().cr().modify(|w| w.set_aden(true));\n\n        // Wait until the adc is ready\n        while !T::regs().isr().read().adrdy() {}\n\n        T::Interrupt::unpend();\n        unsafe {\n            T::Interrupt::enable();\n        }\n\n        Self { adc }\n    }\n\n    fn freq() -> Hertz {\n        <T as crate::rcc::SealedRccPeripheral>::frequency()\n    }\n\n    pub fn sample_time_for_us(&self, us: u32) -> SampleTime {\n        match us * Self::freq().0 / 1_000_000 {\n            0..=1 => SampleTime::CYCLES1_5,\n            2..=4 => SampleTime::CYCLES4_5,\n            5..=7 => SampleTime::CYCLES7_5,\n            8..=19 => SampleTime::CYCLES19_5,\n            20..=61 => SampleTime::CYCLES61_5,\n            62..=181 => SampleTime::CYCLES181_5,\n            _ => SampleTime::CYCLES601_5,\n        }\n    }\n\n    pub fn enable_vref(&self) -> super::VrefInt {\n        T::common_regs().ccr().modify(|w| w.set_vrefen(true));\n\n        super::VrefInt {}\n    }\n\n    pub fn enable_temperature(&self) -> super::Temperature {\n        T::common_regs().ccr().modify(|w| w.set_tsen(true));\n\n        super::Temperature {}\n    }\n\n    /// Perform a single conversion.\n    async fn convert(&mut self) -> u16 {\n        T::regs().isr().write(|_| {});\n        T::regs().ier().modify(|w| w.set_eocie(true));\n        T::regs().cr().modify(|w| w.set_adstart(true));\n\n        poll_fn(|cx| {\n            T::state().waker.register(cx.waker());\n\n            if T::regs().isr().read().eoc() {\n                Poll::Ready(())\n            } else {\n                Poll::Pending\n            }\n        })\n        .await;\n\n        T::regs().isr().write(|_| {});\n\n        T::regs().dr().read().rdata()\n    }\n\n    pub async fn read(&mut self, channel: &mut impl AdcChannel<T>, sample_time: SampleTime) -> u16 {\n        Self::set_channel_sample_time(channel.channel(), sample_time);\n\n        // Configure the channel to sample\n        T::regs().sqr1().write(|w| w.set_sq(0, channel.channel()));\n        self.convert().await\n    }\n\n    fn set_channel_sample_time(ch: u8, sample_time: SampleTime) {\n        let sample_time = sample_time.into();\n        if ch <= 9 {\n            T::regs().smpr1().modify(|reg| reg.set_smp(ch as _, sample_time));\n        } else {\n            T::regs().smpr2().modify(|reg| reg.set_smp((ch - 10) as _, sample_time));\n        }\n    }\n}\n\nimpl<'d, T: Instance> Drop for Adc<'d, T> {\n    fn drop(&mut self) {\n        use crate::pac::adc::vals;\n\n        T::regs().cr().modify(|w| w.set_adstp(true));\n\n        while T::regs().cr().read().adstp() {}\n\n        T::regs().cr().modify(|w| w.set_addis(true));\n\n        while T::regs().cr().read().aden() {}\n\n        // Disable the adc regulator\n        T::regs().cr().modify(|w| w.set_advregen(vals::Advregen::INTERMEDIATE));\n        T::regs().cr().modify(|w| w.set_advregen(vals::Advregen::DISABLED));\n\n        rcc::disable::<T>();\n    }\n}\n"
  },
  {
    "path": "embassy-stm32/src/adc/f3_v1_1.rs",
    "content": "use core::future::poll_fn;\nuse core::marker::PhantomData;\nuse core::task::Poll;\n\nuse embassy_futures::yield_now;\nuse embassy_time::Instant;\n\nuse super::Resolution;\nuse crate::adc::{Adc, AdcChannel, Instance, SampleTime};\nuse crate::interrupt::typelevel::Interrupt;\nuse crate::time::Hertz;\nuse crate::{Peri, interrupt, rcc};\n\nconst ADC_FREQ: Hertz = crate::rcc::HSI_FREQ;\n\npub const VDDA_CALIB_MV: u32 = 3300;\npub const ADC_MAX: u32 = (1 << 12) - 1;\npub const VREF_INT: u32 = 1230;\n\n#[derive(Copy, Clone)]\npub enum AdcPowerMode {\n    AlwaysOn,\n    DelayOff,\n    IdleOff,\n    DelayIdleOff,\n}\n\n#[derive(Copy, Clone)]\npub enum Prescaler {\n    Div1,\n    Div2,\n    Div3,\n    Div4,\n}\n\n/// Interrupt handler.\npub struct InterruptHandler<T: Instance> {\n    _phantom: PhantomData<T>,\n}\n\nimpl<T: Instance> interrupt::typelevel::Handler<T::Interrupt> for InterruptHandler<T> {\n    unsafe fn on_interrupt() {\n        if T::regs().sr().read().eoc() {\n            T::regs().cr1().modify(|w| w.set_eocie(false));\n        } else {\n            return;\n        }\n\n        T::state().waker.wake();\n    }\n}\n\nfn update_vref<T: Instance>(op: i8) {\n    static VREF_STATUS: core::sync::atomic::AtomicU8 = core::sync::atomic::AtomicU8::new(0);\n\n    if op > 0 {\n        if VREF_STATUS.fetch_add(1, core::sync::atomic::Ordering::SeqCst) == 0 {\n            T::regs().ccr().modify(|w| w.set_tsvrefe(true));\n        }\n    } else {\n        if VREF_STATUS.fetch_sub(1, core::sync::atomic::Ordering::SeqCst) == 1 {\n            T::regs().ccr().modify(|w| w.set_tsvrefe(false));\n        }\n    }\n}\n\npub struct Vref<T: Instance>(core::marker::PhantomData<T>);\nimpl<T: Instance> AdcChannel<T> for Vref<T> {}\nimpl<T: Instance> super::SealedAdcChannel<T> for Vref<T> {\n    fn channel(&self) -> u8 {\n        17\n    }\n}\n\nimpl<T: Instance> Vref<T> {\n    /// The value that vref would be if vdda was at 3000mv\n    pub fn calibrated_value(&self) -> u16 {\n        crate::pac::VREFINTCAL.data().read()\n    }\n\n    pub async fn calibrate(&mut self, adc: &mut Adc<'_, T>) -> Calibration {\n        let vref_val = adc.read(self, SampleTime::from(0)).await;\n        Calibration {\n            vref_cal: self.calibrated_value(),\n            vref_val,\n        }\n    }\n}\n\npub struct Calibration {\n    vref_cal: u16,\n    vref_val: u16,\n}\n\nimpl Calibration {\n    /// The millivolts that the calibration value was measured at\n    pub const CALIBRATION_UV: u32 = 3_000_000;\n\n    /// Returns the measured VddA in microvolts (uV)\n    pub fn vdda_uv(&self) -> u32 {\n        (Self::CALIBRATION_UV * self.vref_cal as u32) / self.vref_val as u32\n    }\n\n    /// Returns the measured VddA as an f32\n    pub fn vdda_f32(&self) -> f32 {\n        (Self::CALIBRATION_UV as f32 / 1_000.0) * (self.vref_cal as f32 / self.vref_val as f32)\n    }\n\n    /// Returns a calibrated voltage value as in microvolts (uV)\n    pub fn cal_uv(&self, raw: u16, resolution: super::Resolution) -> u32 {\n        (self.vdda_uv() / super::resolution_to_max_count(resolution)) * raw as u32\n    }\n\n    /// Returns a calibrated voltage value as an f32\n    pub fn cal_f32(&self, raw: u16, resolution: super::Resolution) -> f32 {\n        raw as f32 * self.vdda_f32() / super::resolution_to_max_count(resolution) as f32\n    }\n}\n\nimpl<T: Instance> Drop for Vref<T> {\n    fn drop(&mut self) {\n        update_vref::<T>(-1)\n    }\n}\n\npub struct Temperature<T: Instance>(core::marker::PhantomData<T>);\nimpl<T: Instance> AdcChannel<T> for Temperature<T> {}\nimpl<T: Instance> super::SealedAdcChannel<T> for Temperature<T> {\n    fn channel(&self) -> u8 {\n        16\n    }\n}\n\nimpl<T: Instance> Drop for Temperature<T> {\n    fn drop(&mut self) {\n        update_vref::<T>(-1)\n    }\n}\n\nimpl<'d, T: Instance> Adc<'d, T> {\n    pub fn new(\n        adc: Peri<'d, T>,\n        _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd,\n    ) -> Self {\n        rcc::enable_and_reset::<T>();\n\n        //let r = T::regs();\n        //r.cr2().write(|w| w.set_align(true));\n\n        T::Interrupt::unpend();\n        unsafe {\n            T::Interrupt::enable();\n        }\n\n        Self { adc }\n    }\n\n    fn freq() -> Hertz {\n        let div = T::regs().ccr().read().adcpre() + 1;\n        ADC_FREQ / div as u32\n    }\n\n    pub async fn set_resolution(&mut self, res: Resolution) {\n        let was_on = Self::is_on();\n        if was_on {\n            self.stop_adc().await;\n        }\n\n        T::regs().cr1().modify(|w| w.set_res(res.into()));\n\n        if was_on {\n            self.start_adc().await;\n        }\n    }\n\n    pub fn resolution(&self) -> Resolution {\n        T::regs().cr1().read().res()\n    }\n\n    pub fn enable_vref(&self) -> Vref<T> {\n        update_vref::<T>(1);\n\n        Vref(core::marker::PhantomData)\n    }\n\n    pub fn enable_temperature(&self) -> Temperature<T> {\n        T::regs().ccr().modify(|w| w.set_tsvrefe(true));\n\n        Temperature::<T>(core::marker::PhantomData)\n    }\n\n    /// Perform a single conversion.\n    async fn convert(&mut self) -> u16 {\n        let was_on = Self::is_on();\n\n        if !was_on {\n            self.start_adc().await;\n        }\n\n        self.wait_sample_ready().await;\n\n        T::regs().sr().write(|_| {});\n        T::regs().cr1().modify(|w| {\n            w.set_eocie(true);\n            w.set_scan(false);\n        });\n        T::regs().cr2().modify(|w| {\n            w.set_swstart(true);\n            w.set_cont(false);\n        }); // swstart cleared by HW\n\n        let res = poll_fn(|cx| {\n            T::state().waker.register(cx.waker());\n\n            if T::regs().sr().read().eoc() {\n                let res = T::regs().dr().read().rdata();\n                Poll::Ready(res)\n            } else {\n                Poll::Pending\n            }\n        })\n        .await;\n\n        if !was_on {\n            self.stop_adc().await;\n        }\n\n        res\n    }\n\n    #[inline(always)]\n    fn is_on() -> bool {\n        T::regs().sr().read().adons() || T::regs().cr2().read().adon()\n    }\n\n    pub async fn start_adc(&self) {\n        //defmt::trace!(\"Turn ADC on\");\n        T::regs().cr2().modify(|w| w.set_adon(true));\n        //defmt::trace!(\"Waiting for ADC to turn on\");\n\n        let mut t = Instant::now();\n\n        while !T::regs().sr().read().adons() {\n            yield_now().await;\n            if t.elapsed() > embassy_time::Duration::from_millis(1000) {\n                t = Instant::now();\n                //defmt::trace!(\"ADC still not on\");\n            }\n        }\n\n        //defmt::trace!(\"ADC on\");\n    }\n\n    pub async fn stop_adc(&self) {\n        if T::regs().cr2().read().adon() {\n            //defmt::trace!(\"ADC should be on, wait for it to start\");\n            while !T::regs().csr().read().adons1() {\n                yield_now().await;\n            }\n        }\n\n        //defmt::trace!(\"Turn ADC off\");\n\n        T::regs().cr2().modify(|w| w.set_adon(false));\n\n        //defmt::trace!(\"Waiting for ADC to turn off\");\n\n        while T::regs().csr().read().adons1() {\n            yield_now().await;\n        }\n    }\n\n    pub async fn read(&mut self, channel: &mut impl AdcChannel<T>, sample_time: SampleTime) -> u16 {\n        self.set_sample_time(channel, sample_time).await;\n        self.set_sample_sequence(&[channel.channel()]).await;\n        self.convert().await\n    }\n\n    async fn wait_sample_ready(&self) {\n        //trace!(\"Waiting for sample channel to be ready\");\n        while T::regs().sr().read().rcnr() {\n            yield_now().await;\n        }\n    }\n\n    pub async fn set_sample_time(&mut self, channel: &mut impl AdcChannel<T>, sample_time: SampleTime) {\n        if Self::get_channel_sample_time(channel.channel()) != sample_time {\n            self.stop_adc().await;\n            unsafe {\n                Self::set_channel_sample_time(channel.channel(), sample_time);\n            }\n            self.start_adc().await;\n        }\n    }\n\n    pub fn get_sample_time(&self, channel: &impl AdcChannel<T>) -> SampleTime {\n        Self::get_channel_sample_time(channel.channel())\n    }\n\n    /// Sets the channel sample time\n    ///\n    /// ## SAFETY:\n    /// - ADON == 0 i.e ADC must not be enabled when this is called.\n    unsafe fn set_channel_sample_time(ch: u8, sample_time: SampleTime) {\n        let sample_time = sample_time.into();\n\n        match ch {\n            0..=9 => T::regs().smpr3().modify(|reg| reg.set_smp(ch as _, sample_time)),\n            10..=19 => T::regs()\n                .smpr2()\n                .modify(|reg| reg.set_smp(ch as usize - 10, sample_time)),\n            20..=29 => T::regs()\n                .smpr1()\n                .modify(|reg| reg.set_smp(ch as usize - 20, sample_time)),\n            30..=31 => T::regs()\n                .smpr0()\n                .modify(|reg| reg.set_smp(ch as usize - 30, sample_time)),\n            _ => panic!(\"Invalid channel to sample\"),\n        }\n    }\n\n    fn get_channel_sample_time(ch: u8) -> SampleTime {\n        match ch {\n            0..=9 => T::regs().smpr3().read().smp(ch as _),\n            10..=19 => T::regs().smpr2().read().smp(ch as usize - 10),\n            20..=29 => T::regs().smpr1().read().smp(ch as usize - 20),\n            30..=31 => T::regs().smpr0().read().smp(ch as usize - 30),\n            _ => panic!(\"Invalid channel to sample\"),\n        }\n        .into()\n    }\n\n    /// Sets the sequence to sample the ADC. Must be less than 28 elements.\n    async fn set_sample_sequence(&self, sequence: &[u8]) {\n        assert!(sequence.len() <= 28);\n        let mut iter = sequence.iter();\n        T::regs().sqr1().modify(|w| w.set_l((sequence.len() - 1) as _));\n        for (idx, ch) in iter.by_ref().take(6).enumerate() {\n            T::regs().sqr5().modify(|w| w.set_sq(idx, *ch));\n        }\n        for (idx, ch) in iter.by_ref().take(6).enumerate() {\n            T::regs().sqr4().modify(|w| w.set_sq(idx, *ch));\n        }\n        for (idx, ch) in iter.by_ref().take(6).enumerate() {\n            T::regs().sqr3().modify(|w| w.set_sq(idx, *ch));\n        }\n        for (idx, ch) in iter.by_ref().take(6).enumerate() {\n            T::regs().sqr2().modify(|w| w.set_sq(idx, *ch));\n        }\n        for (idx, ch) in iter.by_ref().take(4).enumerate() {\n            T::regs().sqr1().modify(|w| w.set_sq(idx, *ch));\n        }\n    }\n\n    fn get_res_clks(res: Resolution) -> u32 {\n        match res {\n            Resolution::BITS12 => 12,\n            Resolution::BITS10 => 11,\n            Resolution::BITS8 => 9,\n            Resolution::BITS6 => 7,\n        }\n    }\n\n    fn get_sample_time_clks(sample_time: SampleTime) -> u32 {\n        match sample_time {\n            SampleTime::CYCLES4 => 4,\n            SampleTime::CYCLES9 => 9,\n            SampleTime::CYCLES16 => 16,\n            SampleTime::CYCLES24 => 24,\n            SampleTime::CYCLES48 => 48,\n            SampleTime::CYCLES96 => 96,\n            SampleTime::CYCLES192 => 192,\n            SampleTime::CYCLES384 => 384,\n        }\n    }\n\n    pub fn sample_time_for_us(&self, us: u32) -> SampleTime {\n        let res_clks = Self::get_res_clks(self.resolution());\n        let us_clks = us * Self::freq().0 / 1_000_000;\n        let clks = us_clks.saturating_sub(res_clks);\n        match clks {\n            0..=4 => SampleTime::CYCLES4,\n            5..=9 => SampleTime::CYCLES9,\n            10..=16 => SampleTime::CYCLES16,\n            17..=24 => SampleTime::CYCLES24,\n            25..=48 => SampleTime::CYCLES48,\n            49..=96 => SampleTime::CYCLES96,\n            97..=192 => SampleTime::CYCLES192,\n            193.. => SampleTime::CYCLES384,\n        }\n    }\n\n    pub fn us_for_cfg(&self, res: Resolution, sample_time: SampleTime) -> u32 {\n        let res_clks = Self::get_res_clks(res);\n        let sample_clks = Self::get_sample_time_clks(sample_time);\n        (res_clks + sample_clks) * 1_000_000 / Self::freq().0\n    }\n}\n\nimpl<'d, T: Instance> Drop for Adc<'d, T> {\n    fn drop(&mut self) {\n        while !T::regs().sr().read().adons() {}\n\n        T::regs().cr2().modify(|w| w.set_adon(false));\n\n        rcc::disable::<T>();\n    }\n}\n"
  },
  {
    "path": "embassy-stm32/src/adc/g4.rs",
    "content": "#[cfg(stm32g4)]\nuse pac::adc::regs::Difsel as DifselReg;\n#[allow(unused)]\n#[cfg(stm32g4)]\npub use pac::adc::vals::{Adcaldif, Adstp, Difsel, Dmacfg, Dmaen, Exten, Rovsm, Trovs};\n#[allow(unused)]\n#[cfg(stm32h7)]\nuse pac::adc::vals::{Adcaldif, Difsel, Exten};\npub use pac::adccommon::vals::{Dual, Presc};\n\nuse super::{Adc, AnyAdcChannel, ConversionMode, Instance, Resolution, RxDma, SampleTime, blocking_delay_us};\nuse crate::adc::{AdcRegs, BasicAdcRegs, InjectedTrigger, RegularTrigger, SealedAdcChannel};\nuse crate::pac::adc::regs::{Smpr, Smpr2, Sqr1, Sqr2, Sqr3, Sqr4};\nuse crate::time::Hertz;\nuse crate::{Peri, pac, rcc};\n\nmod injected;\npub use injected::InjectedAdc;\n\n/// Default VREF voltage used for sample conversion to millivolts.\npub const VREF_DEFAULT_MV: u32 = 3300;\n/// VREF voltage used for factory calibration of VREFINTCAL register.\npub const VREF_CALIB_MV: u32 = 3000;\n\nconst NR_INJECTED_RANKS: usize = 4;\n\n/// Max single ADC operation clock frequency\n#[cfg(stm32g4)]\nconst MAX_ADC_CLK_FREQ: Hertz = Hertz::mhz(60);\n#[cfg(stm32h7)]\nconst MAX_ADC_CLK_FREQ: Hertz = Hertz::mhz(50);\n\nfn from_ker_ck(frequency: Hertz) -> Presc {\n    let raw_prescaler = rcc::raw_prescaler(frequency.0, MAX_ADC_CLK_FREQ.0);\n    match raw_prescaler {\n        0 => Presc::DIV1,\n        1 => Presc::DIV2,\n        2..=3 => Presc::DIV4,\n        4..=5 => Presc::DIV6,\n        6..=7 => Presc::DIV8,\n        8..=9 => Presc::DIV10,\n        10..=11 => Presc::DIV12,\n        _ => unimplemented!(),\n    }\n}\n\n/// ADC configuration\n#[derive(Default)]\npub struct AdcConfig {\n    pub dual_mode: Option<Dual>,\n    pub resolution: Option<Resolution>,\n    #[cfg(stm32g4)]\n    pub oversampling_shift: Option<u8>,\n    #[cfg(stm32g4)]\n    pub oversampling_ratio: Option<u8>,\n    #[cfg(stm32g4)]\n    pub oversampling_mode: Option<(Rovsm, Trovs, bool)>,\n}\n\nimpl super::AdcRegs for crate::pac::adc::Adc {\n    fn data(&self) -> *mut u16 {\n        crate::pac::adc::Adc::dr(*self).as_ptr() as *mut u16\n    }\n\n    fn enable(&self) {\n        // Make sure bits are off\n        while self.cr().read().addis() {\n            // spin\n        }\n\n        if !self.cr().read().aden() {\n            // Enable ADC\n            self.isr().modify(|reg| {\n                reg.set_adrdy(true);\n            });\n            self.cr().modify(|reg| {\n                reg.set_aden(true);\n            });\n\n            while !self.isr().read().adrdy() {\n                // spin\n            }\n        }\n    }\n\n    fn start(&self) {\n        self.cr().modify(|reg| {\n            reg.set_adstart(true);\n        });\n    }\n\n    fn stop(&self) {\n        if self.cr().read().adstart() && !self.cr().read().addis() {\n            self.cr().modify(|reg| {\n                reg.set_adstp(Adstp::STOP);\n            });\n            // The software must poll ADSTART until the bit is reset before assuming the\n            // ADC is completely stopped\n            while self.cr().read().adstart() {}\n        }\n\n        // Disable dma control and continuous conversion, if enabled\n        self.cfgr().modify(|reg| {\n            reg.set_cont(false);\n            reg.set_dmaen(Dmaen::DISABLE);\n        });\n    }\n\n    fn convert(&self) {\n        self.isr().modify(|reg| {\n            reg.set_eos(true);\n            reg.set_eoc(true);\n        });\n\n        // Start conversion\n        self.cr().modify(|reg| {\n            reg.set_adstart(true);\n        });\n\n        while !self.isr().read().eos() {\n            // spin\n        }\n    }\n\n    fn configure_dma(&self, conversion_mode: ConversionMode) {\n        self.isr().modify(|reg| {\n            reg.set_ovr(true);\n        });\n\n        self.cfgr().modify(|reg| {\n            reg.set_discen(false); // Convert all channels for each trigger\n            reg.set_dmacfg(match conversion_mode {\n                ConversionMode::Singular => Dmacfg::ONE_SHOT,\n                ConversionMode::Repeated(_) => Dmacfg::CIRCULAR,\n            });\n            reg.set_dmaen(Dmaen::ENABLE);\n        });\n\n        if let ConversionMode::Repeated(trigger) = conversion_mode {\n            match trigger.signal {\n                u8::MAX => {\n                    // continuous conversions\n                    self.cfgr().modify(|reg| {\n                        reg.set_cont(true);\n                    });\n                }\n                _ => {\n                    self.cfgr().modify(|r| {\n                        r.set_cont(false); // New trigger is needed for each sample to be read\n                    });\n\n                    self.cfgr().modify(|r| {\n                        r.set_extsel(trigger.signal);\n                        r.set_exten(trigger.edge);\n                    });\n\n                    // Regular conversions uses DMA so no need to generate interrupt\n                    self.ier().modify(|r| r.set_eosie(false));\n                }\n            }\n        }\n    }\n\n    fn configure_sequence(&self, sequence: impl ExactSizeIterator<Item = ((u8, bool), SampleTime)>) {\n        self.cr().modify(|w| w.set_aden(false));\n\n        #[cfg(stm32g4)]\n        let mut difsel = DifselReg::default();\n        let mut smpr = Smpr::default();\n        let mut smpr2 = Smpr2::default();\n        let mut sqr1 = Sqr1::default();\n        let mut sqr2 = Sqr2::default();\n        let mut sqr3 = Sqr3::default();\n        let mut sqr4 = Sqr4::default();\n\n        // Set sequence length\n        sqr1.set_l(sequence.len() as u8 - 1);\n\n        // Configure channels and ranks\n        for (_i, ((ch, is_differential), sample_time)) in sequence.enumerate() {\n            let sample_time = sample_time.into();\n            if ch <= 9 {\n                smpr.set_smp(ch as _, sample_time);\n            } else {\n                smpr2.set_smp((ch - 10) as _, sample_time);\n            }\n\n            match _i {\n                0..=3 => {\n                    sqr1.set_sq(_i, ch);\n                }\n                4..=8 => {\n                    sqr2.set_sq(_i - 4, ch);\n                }\n                9..=13 => {\n                    sqr3.set_sq(_i - 9, ch);\n                }\n                14..=15 => {\n                    sqr4.set_sq(_i - 14, ch);\n                }\n                _ => unreachable!(),\n            }\n\n            #[cfg(stm32g4)]\n            {\n                if ch < 18 {\n                    difsel.set_difsel(\n                        ch.into(),\n                        if is_differential {\n                            Difsel::DIFFERENTIAL\n                        } else {\n                            Difsel::SINGLE_ENDED\n                        },\n                    );\n                }\n            }\n        }\n\n        self.smpr().write_value(smpr);\n        self.smpr2().write_value(smpr2);\n        self.sqr1().write_value(sqr1);\n        self.sqr2().write_value(sqr2);\n        self.sqr3().write_value(sqr3);\n        self.sqr4().write_value(sqr4);\n        #[cfg(stm32g4)]\n        self.difsel().write_value(difsel);\n    }\n}\n\nimpl<'d, T: Instance<Regs = crate::pac::adc::Adc>> Adc<'d, T> {\n    /// Create a new ADC driver.\n    pub fn new(adc: Peri<'d, T>, config: AdcConfig) -> Self {\n        rcc::enable_and_reset::<T>();\n\n        let prescaler = from_ker_ck(T::frequency());\n\n        T::common_regs().ccr().modify(|w| w.set_presc(prescaler));\n\n        let frequency = T::frequency() / prescaler;\n        trace!(\"ADC frequency set to {}\", frequency);\n\n        if frequency > MAX_ADC_CLK_FREQ {\n            panic!(\n                \"Maximal allowed frequency for the ADC is {} MHz and it varies with different packages, refer to ST docs for more information.\",\n                MAX_ADC_CLK_FREQ.0 / 1_000_000\n            );\n        }\n\n        T::regs().cr().modify(|reg| {\n            reg.set_deeppwd(false);\n            reg.set_advregen(true);\n        });\n\n        blocking_delay_us(20);\n\n        T::regs().difsel().modify(|w| {\n            for n in 0..18 {\n                w.set_difsel(n, Difsel::SINGLE_ENDED);\n            }\n        });\n\n        T::regs().cr().modify(|w| {\n            w.set_adcaldif(Adcaldif::SINGLE_ENDED);\n        });\n\n        T::regs().cr().modify(|w| w.set_adcal(true));\n\n        while T::regs().cr().read().adcal() {}\n\n        blocking_delay_us(20);\n\n        T::regs().cr().modify(|w| {\n            w.set_adcaldif(Adcaldif::DIFFERENTIAL);\n        });\n\n        T::regs().cr().modify(|w| w.set_adcal(true));\n\n        while T::regs().cr().read().adcal() {}\n\n        blocking_delay_us(20);\n\n        T::regs().enable();\n\n        // single conversion mode, software trigger\n        T::regs().cfgr().modify(|w| {\n            w.set_cont(false);\n            w.set_exten(Exten::DISABLED);\n        });\n\n        if let Some(dual) = config.dual_mode {\n            T::common_regs().ccr().modify(|reg| {\n                reg.set_dual(dual);\n            })\n        }\n\n        if let Some(resolution) = config.resolution {\n            T::regs().cfgr().modify(|reg| reg.set_res(resolution.into()));\n        }\n\n        #[cfg(stm32g4)]\n        if let Some(shift) = config.oversampling_shift {\n            T::regs().cfgr2().modify(|reg| reg.set_ovss(shift));\n        }\n\n        #[cfg(stm32g4)]\n        if let Some(ratio) = config.oversampling_ratio {\n            T::regs().cfgr2().modify(|reg| reg.set_ovsr(ratio));\n        }\n\n        #[cfg(stm32g4)]\n        if let Some((mode, trig_mode, enable)) = config.oversampling_mode {\n            T::regs().cfgr2().modify(|reg| reg.set_trovs(trig_mode));\n            T::regs().cfgr2().modify(|reg| reg.set_rovsm(mode));\n            T::regs().cfgr2().modify(|reg| reg.set_rovse(enable));\n        }\n\n        Self { adc }\n    }\n\n    /// Enable reading the voltage reference internal channel.\n    pub fn enable_vrefint(&self) -> super::VrefInt\n    where\n        T: super::SpecialConverter<super::VrefInt>,\n    {\n        T::common_regs().ccr().modify(|reg| {\n            reg.set_vrefen(true);\n        });\n\n        super::VrefInt {}\n    }\n\n    /// Enable reading the temperature internal channel.\n    pub fn enable_temperature(&self) -> super::Temperature\n    where\n        T: super::SpecialConverter<super::Temperature>,\n    {\n        T::common_regs().ccr().modify(|reg| {\n            reg.set_vsenseen(true);\n        });\n\n        super::Temperature {}\n    }\n\n    /// Enable reading the vbat internal channel.\n    pub fn enable_vbat(&self) -> super::Vbat\n    where\n        T: super::SpecialConverter<super::Vbat>,\n    {\n        T::common_regs().ccr().modify(|reg| {\n            reg.set_vbaten(true);\n        });\n\n        super::Vbat {}\n    }\n\n    // Reads that are not implemented as INJECTED in \"blocking_read\"\n    // #[cfg(stm32g4)]\n    // pub fn enalble_injected_oversampling_mode(&mut self, enable: bool) {\n    //     T::regs().cfgr2().modify(|reg| reg.set_jovse(enable));\n    // }\n\n    // #[cfg(stm32g4)]\n    // pub fn enable_oversampling_regular_injected_mode(&mut self, enable: bool) {\n    //     // the regularoversampling mode is forced to resumed mode (ROVSM bit ignored),\n    //     T::regs().cfgr2().modify(|reg| reg.set_rovse(enable));\n    //     T::regs().cfgr2().modify(|reg| reg.set_jovse(enable));\n    // }\n\n    /// Configures the ADC for injected conversions.\n    ///\n    /// Injected conversions are separate from the regular conversion sequence and are typically\n    /// triggered by software or an external event. This method sets up a fixed-length sequence of\n    /// injected channels with specified sample times, the trigger source, and whether the end-of-sequence\n    /// interrupt should be enabled.\n    ///\n    /// # Parameters\n    /// - `sequence`: An array of tuples containing the ADC channels and their sample times. The length\n    ///   `N` determines the number of injected ranks to configure (maximum 4 for STM32).\n    /// - `trigger`: The trigger source that starts the injected conversion sequence.\n    /// - `interrupt`: If `true`, enables the end-of-sequence (JEOS) interrupt for injected conversions.\n    ///\n    /// # Returns\n    /// An `InjectedAdc<T, N>` instance that represents the configured injected sequence. The returned\n    /// type encodes the sequence length `N` in its type, ensuring that reads return exactly `N` samples.\n    ///\n    /// # Panics\n    /// This function will panic if:\n    /// - `sequence` is empty.\n    /// - `sequence` length exceeds the maximum number of injected ranks (`NR_INJECTED_RANKS`).\n    ///\n    /// # Notes\n    /// - Injected conversions can run independently of regular ADC conversions.\n    /// - The order of channels in `sequence` determines the rank order in the injected sequence.\n    /// - Accessing samples beyond `N` will result in a panic; use the returned type\n    ///   `InjectedAdc<T, N>` to enforce bounds at compile time.\n    pub fn setup_injected_conversions<'a, const N: usize>(\n        self,\n        sequence: [(AnyAdcChannel<'a, T>, SampleTime); N],\n        trigger: impl InjectedTrigger<T>,\n        edge: Exten,\n        interrupt: bool,\n    ) -> InjectedAdc<'a, T, N> {\n        assert!(N != 0, \"Read sequence cannot be empty\");\n        assert!(\n            N <= NR_INJECTED_RANKS,\n            \"Read sequence cannot be more than {} in length\",\n            NR_INJECTED_RANKS\n        );\n\n        T::regs().enable();\n\n        T::regs().jsqr().modify(|w| w.set_jl(N as u8 - 1));\n\n        for (n, (channel, sample_time)) in sequence.iter().enumerate() {\n            let sample_time = sample_time.clone().into();\n            if channel.channel() <= 9 {\n                T::regs()\n                    .smpr()\n                    .modify(|reg| reg.set_smp(channel.channel() as _, sample_time));\n            } else {\n                T::regs()\n                    .smpr2()\n                    .modify(|reg| reg.set_smp((channel.channel() - 10) as _, sample_time));\n            }\n\n            let idx = match n {\n                0..=3 => n,\n                4..=8 => n - 4,\n                9..=13 => n - 9,\n                14..=15 => n - 14,\n                _ => unreachable!(),\n            };\n\n            T::regs().jsqr().modify(|w| w.set_jsq(idx, channel.channel()));\n        }\n\n        T::regs().cfgr().modify(|reg| reg.set_jdiscen(false));\n\n        // Set external trigger for injected conversion sequence\n        // Possible trigger values are seen in Table 167 in RM0440 Rev 9\n        T::regs().jsqr().modify(|r| {\n            r.set_jextsel(trigger.signal());\n            r.set_jexten(edge);\n        });\n\n        // Enable end of injected sequence interrupt\n        T::regs().ier().modify(|r| r.set_jeosie(interrupt));\n\n        Self::start_injected_conversions();\n\n        InjectedAdc::new(sequence) // InjectedAdc<'a, T, N> now borrows the channels\n    }\n\n    /// Configures ADC for both regular conversions with a ring-buffered DMA and injected conversions.\n    ///\n    /// # Parameters\n    /// - `dma`: The DMA peripheral to use for the ring-buffered ADC transfers.\n    /// - `dma_buf`: The buffer to store DMA-transferred samples for regular conversions.\n    /// - `regular_sequence`: The sequence of channels and their sample times for regular conversions.\n    /// - `regular_conversion_mode`: The mode for regular conversions (e.g., continuous or triggered).\n    /// - `injected_sequence`: An array of channels and sample times for injected conversions (length `N`).\n    /// - `injected_trigger`: The trigger source for injected conversions.\n    /// - `injected_interrupt`: Whether to enable the end-of-sequence interrupt for injected conversions.\n    ///\n    /// Injected conversions are typically used with interrupts. If ADC1 and ADC2 are used in dual mode,\n    /// it is recommended to enable interrupts only for the ADC whose sequence takes the longest to complete.\n    ///\n    /// # Returns\n    /// A tuple containing:\n    /// 1. `RingBufferedAdc<'a, T>` — the configured ADC for regular conversions using DMA.\n    /// 2. `InjectedAdc<T, N>` — the configured ADC for injected conversions.\n    ///\n    /// # Safety\n    /// This function is `unsafe` because it clones the ADC peripheral handle unchecked. Both the\n    /// `RingBufferedAdc` and `InjectedAdc` take ownership of the handle and drop it independently.\n    /// Ensure no other code concurrently accesses the same ADC instance in a conflicting way.\n    pub fn into_ring_buffered_and_injected<'a, 'b, const N: usize, D: RxDma<T>>(\n        self,\n        dma: Peri<'a, D>,\n        dma_buf: &'a mut [u16],\n        _irq: impl crate::interrupt::typelevel::Binding<D::Interrupt, crate::dma::InterruptHandler<D>> + 'a,\n        regular_sequence: impl ExactSizeIterator<Item = (AnyAdcChannel<'b, T>, <T::Regs as BasicAdcRegs>::SampleTime)>,\n        regular_trigger: impl RegularTrigger<T>,\n        regular_edge: Exten,\n        injected_sequence: [(AnyAdcChannel<'b, T>, SampleTime); N],\n        injected_trigger: impl InjectedTrigger<T>,\n        injected_edge: Exten,\n        injected_interrupt: bool,\n    ) -> (super::RingBufferedAdc<'a, T>, InjectedAdc<'b, T, N>) {\n        unsafe {\n            (\n                Self {\n                    adc: self.adc.clone_unchecked(),\n                }\n                .into_ring_buffered(\n                    dma,\n                    dma_buf,\n                    _irq,\n                    regular_sequence,\n                    regular_trigger,\n                    regular_edge,\n                ),\n                Self {\n                    adc: self.adc.clone_unchecked(),\n                }\n                .setup_injected_conversions(\n                    injected_sequence,\n                    injected_trigger,\n                    injected_edge,\n                    injected_interrupt,\n                ),\n            )\n        }\n    }\n\n    /// Stop injected conversions\n    pub(super) fn stop_injected_conversions() {\n        if T::regs().cr().read().adstart() && !T::regs().cr().read().addis() {\n            T::regs().cr().modify(|reg| {\n                reg.set_jadstp(Adstp::STOP);\n            });\n            // The software must poll JADSTART until the bit is reset before assuming the\n            // ADC is completely stopped\n            while T::regs().cr().read().jadstart() {}\n        }\n    }\n\n    /// Start injected ADC conversion\n    pub(super) fn start_injected_conversions() {\n        T::regs().cr().modify(|reg| {\n            reg.set_jadstart(true);\n        });\n    }\n}\n\nimpl<'a, T: Instance<Regs = crate::pac::adc::Adc>, const N: usize> InjectedAdc<'a, T, N> {\n    /// Read sampled data from all injected ADC injected ranks\n    /// Clear the JEOS flag to allow a new injected sequence\n    pub(super) fn read_injected_data() -> [u16; N] {\n        let mut data = [0u16; N];\n        for i in 0..N {\n            data[i] = T::regs().jdr(i).read().jdata();\n        }\n\n        // Clear JEOS by writing 1\n        T::regs().isr().modify(|r| r.set_jeos(true));\n        data\n    }\n}\n\n#[cfg(stm32g4)]\nmod g4 {\n    use crate::adc::{SealedSpecialConverter, Temperature, Vbat, VrefInt};\n\n    impl SealedSpecialConverter<Temperature> for crate::peripherals::ADC1 {\n        const CHANNEL: u8 = 16;\n    }\n\n    impl SealedSpecialConverter<VrefInt> for crate::peripherals::ADC1 {\n        const CHANNEL: u8 = 18;\n    }\n\n    impl SealedSpecialConverter<Vbat> for crate::peripherals::ADC1 {\n        const CHANNEL: u8 = 17;\n    }\n\n    #[cfg(peri_adc3_common)]\n    impl SealedSpecialConverter<VrefInt> for crate::peripherals::ADC3 {\n        const CHANNEL: u8 = 18;\n    }\n\n    #[cfg(peri_adc3_common)]\n    impl SealedSpecialConverter<Vbat> for crate::peripherals::ADC3 {\n        const CHANNEL: u8 = 17;\n    }\n\n    #[cfg(not(stm32g4x1))]\n    impl SealedSpecialConverter<VrefInt> for crate::peripherals::ADC4 {\n        const CHANNEL: u8 = 18;\n    }\n\n    #[cfg(not(stm32g4x1))]\n    impl SealedSpecialConverter<Temperature> for crate::peripherals::ADC5 {\n        const CHANNEL: u8 = 4;\n    }\n\n    #[cfg(not(stm32g4x1))]\n    impl SealedSpecialConverter<VrefInt> for crate::peripherals::ADC5 {\n        const CHANNEL: u8 = 18;\n    }\n\n    #[cfg(not(stm32g4x1))]\n    impl SealedSpecialConverter<Vbat> for crate::peripherals::ADC5 {\n        const CHANNEL: u8 = 17;\n    }\n}\n\n// TODO this should look at each ADC individually and impl the correct channels\n#[cfg(stm32h7)]\nmod h7 {\n    impl<T: Instance> SealedSpecialConverter<Temperature> for T {\n        const CHANNEL: u8 = 18;\n    }\n    impl<T: Instance> SealedSpecialConverter<VrefInt> for T {\n        const CHANNEL: u8 = 19;\n    }\n    impl<T: Instance> SealedSpecialConverter<Vbat> for T {\n        // TODO this should be 14 for H7a/b/35\n        const CHANNEL: u8 = 17;\n    }\n}\n"
  },
  {
    "path": "embassy-stm32/src/adc/injected.rs",
    "content": "use core::marker::PhantomData;\nuse core::sync::atomic::{Ordering, compiler_fence};\n\n#[allow(unused_imports)]\nuse embassy_hal_internal::Peri;\n\nuse super::{AdcRegs, AnyAdcChannel, SampleTime};\nuse crate::adc::Adc;\n#[allow(unused_imports)]\nuse crate::adc::Instance;\n\n/// Injected ADC sequence with owned channels.\npub struct InjectedAdc<'a, T: Instance<Regs = crate::pac::adc::Adc>, const N: usize> {\n    _channels: [(AnyAdcChannel<'a, T>, SampleTime); N],\n    _phantom: PhantomData<T>,\n}\n\nimpl<'a, T: Instance<Regs = crate::pac::adc::Adc>, const N: usize> InjectedAdc<'a, T, N> {\n    pub(crate) fn new(channels: [(AnyAdcChannel<'a, T>, SampleTime); N]) -> Self {\n        Self {\n            _channels: channels,\n            _phantom: PhantomData,\n        }\n    }\n\n    pub fn stop_injected_conversions(&mut self) {\n        Adc::<T>::stop_injected_conversions()\n    }\n\n    pub fn start_injected_conversions(&mut self) {\n        Adc::<T>::start_injected_conversions()\n    }\n\n    pub fn read_injected_samples(&mut self) -> [u16; N] {\n        InjectedAdc::<T, N>::read_injected_data()\n    }\n}\n\nimpl<'a, T: Instance<Regs = crate::pac::adc::Adc>, const N: usize> Drop for InjectedAdc<'a, T, N> {\n    fn drop(&mut self) {\n        T::regs().stop();\n        compiler_fence(Ordering::SeqCst);\n    }\n}\n"
  },
  {
    "path": "embassy-stm32/src/adc/mod.rs",
    "content": "//! Analog to Digital Converter (ADC)\n\n#![macro_use]\n#![allow(missing_docs)] // TODO\n#![cfg_attr(adc_f3v3, allow(unused))]\n\n#[cfg(not(any(adc_f3v3, adc_wba)))]\n#[cfg_attr(adc_f1, path = \"f1.rs\")]\n#[cfg_attr(adc_f3v1, path = \"f3.rs\")]\n#[cfg_attr(adc_f3v2, path = \"f3_v1_1.rs\")]\n#[cfg_attr(adc_v1, path = \"v1.rs\")]\n#[cfg_attr(adc_l0, path = \"v1.rs\")]\n#[cfg_attr(adc_v2, path = \"v2.rs\")]\n#[cfg_attr(any(adc_v3, adc_g0, adc_h5, adc_h7rs, adc_u0), path = \"v3.rs\")]\n#[cfg_attr(any(adc_v4, adc_u5, adc_u3), path = \"v4.rs\")]\n#[cfg_attr(adc_g4, path = \"g4.rs\")]\n#[cfg_attr(adc_c0, path = \"c0.rs\")]\nmod _version;\n\n#[cfg(any(adc_v2, adc_g4, adc_v3, adc_g0, adc_u0, adc_wba, adc_c0))]\nmod ringbuffered;\n\nuse core::marker::PhantomData;\n\n#[allow(unused)]\n#[cfg(not(any(adc_f3v3, adc_wba)))]\npub use _version::*;\n#[allow(unused)]\nuse embassy_hal_internal::PeripheralType;\n#[cfg(any(adc_f1, adc_f3v1, adc_v1, adc_l0, adc_f3v2))]\nuse embassy_sync::waitqueue::AtomicWaker;\n#[cfg(any(adc_v2, adc_g4, adc_v3, adc_g0, adc_u0, adc_wba, adc_c0))]\npub use ringbuffered::RingBufferedAdc;\n\n#[cfg(adc_u5)]\nuse crate::pac::adc::vals::Adc4SampleTime;\n#[cfg(adc_wba)]\nuse crate::pac::adc::vals::SampleTime as Adc4SampleTime;\n\n#[cfg(any(adc_u5, adc_wba))]\n#[path = \"adc4.rs\"]\npub mod adc4;\n\n#[allow(unused)]\npub(self) use crate::block_for_us as blocking_delay_us;\npub use crate::pac::adc::vals;\n#[cfg(any(adc_v2, adc_g4, adc_g0, adc_c0))]\npub use crate::pac::adc::vals::Exten;\n#[cfg(not(any(adc_f1, adc_f3v3)))]\npub use crate::pac::adc::vals::Res as Resolution;\npub use crate::pac::adc::vals::SampleTime;\nuse crate::peripherals;\n\ndma_trait!(RxDma, Instance);\n\n/// Continuous Trigger\npub struct CONTINUOUS;\n\nimpl<T: Instance> RegularTrigger<T> for CONTINUOUS {\n    fn signal(&self) -> u8 {\n        u8::MAX\n    }\n}\n\n/// Analog to Digital driver.\npub struct Adc<'d, T: Instance> {\n    #[allow(unused)]\n    adc: crate::Peri<'d, T>,\n}\n\n#[cfg(any(adc_f1, adc_f3v1, adc_v1, adc_l0, adc_f3v2))]\npub struct State {\n    pub waker: AtomicWaker,\n}\n\n#[cfg(any(adc_f1, adc_f3v1, adc_v1, adc_l0, adc_f3v2))]\nimpl State {\n    pub const fn new() -> Self {\n        Self {\n            waker: AtomicWaker::new(),\n        }\n    }\n}\n\n#[cfg(any(adc_f1, adc_f3v1, adc_f3v2, adc_v1, adc_l0))]\ntrait_set::trait_set! {\n    pub trait DefaultInstance = Instance;\n}\n\n#[cfg(any(\n    adc_v2, adc_v3, adc_g0, adc_h5, adc_h7rs, adc_u0, adc_v4, adc_u3, adc_u5, adc_g4, adc_c0\n))]\ntrait_set::trait_set! {\n    pub trait DefaultInstance = Instance<Regs = crate::pac::adc::Adc>;\n}\n\n#[cfg(adc_wba)]\ntrait_set::trait_set! {\n    pub trait DefaultInstance = Instance<Regs = crate::pac::adc::Adc4>;\n}\n\npub trait BasicAdcRegs {\n    type SampleTime;\n}\n\n#[cfg(any(\n    adc_v2, adc_v3, adc_g0, adc_h5, adc_h7rs, adc_u0, adc_v4, adc_u3, adc_u5, adc_wba, adc_g4, adc_c0\n))]\ntrait AdcRegs: BasicAdcRegs {\n    fn enable(&self);\n    fn start(&self);\n    fn stop(&self);\n    fn convert(&self);\n    fn configure_dma(&self, conversion_mode: ConversionMode);\n    fn configure_sequence(&self, sequence: impl ExactSizeIterator<Item = ((u8, bool), Self::SampleTime)>);\n    fn data(&self) -> *mut u16;\n}\n\n#[allow(private_bounds)]\npub trait BasicInstance {\n    #[cfg(any(\n        adc_v2, adc_v3, adc_g0, adc_h5, adc_h7rs, adc_u0, adc_v4, adc_u3, adc_u5, adc_wba, adc_g4, adc_c0\n    ))]\n    type Regs: AdcRegs;\n}\n\ntrait SealedInstance: BasicInstance {\n    #[cfg(any(adc_f1, adc_f3v1, adc_f3v2, adc_v1, adc_l0))]\n    fn regs() -> crate::pac::adc::Adc;\n    #[cfg(any(\n        adc_v2, adc_v3, adc_g0, adc_h5, adc_h7rs, adc_u0, adc_v4, adc_u3, adc_u5, adc_wba, adc_g4, adc_c0\n    ))]\n    fn regs() -> Self::Regs;\n    #[cfg(not(any(adc_f1, adc_v1, adc_l0, adc_f3v3, adc_f3v2, adc_g0)))]\n    #[allow(unused)]\n    fn common_regs() -> crate::pac::adccommon::AdcCommon;\n    #[cfg(any(adc_f1, adc_f3v1, adc_v1, adc_l0, adc_f3v2))]\n    fn state() -> &'static State;\n}\n\npub(crate) trait SealedAdcChannel<T> {\n    #[cfg(any(adc_v1, adc_c0, adc_l0, adc_v2, adc_g4, adc_v3, adc_v4, adc_u5, adc_u3, adc_wba))]\n    fn setup(&mut self) {}\n\n    #[allow(unused)]\n    fn channel(&self) -> u8;\n\n    #[allow(unused)]\n    fn is_differential(&self) -> bool {\n        false\n    }\n}\n\n#[cfg(any(adc_c0, adc_v3, adc_g0, adc_h5, adc_h7rs, adc_u0, adc_v4, adc_u5, adc_u3))]\n/// Number of samples used for averaging.\n#[derive(Copy, Clone, Debug)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub enum Averaging {\n    Disabled,\n    Samples2,\n    Samples4,\n    Samples8,\n    Samples16,\n    Samples32,\n    Samples64,\n    Samples128,\n    Samples256,\n    #[cfg(any(adc_c0, adc_v4, adc_u5, adc_u3))]\n    Samples512,\n    #[cfg(any(adc_c0, adc_v4, adc_u5, adc_u3))]\n    Samples1024,\n}\n\n#[cfg(any(adc_v2, adc_g4, adc_v3, adc_g0, adc_u0, adc_wba, adc_c0))]\npub(crate) struct Trigger {\n    #[cfg(any(adc_v2, adc_g4, adc_g0, adc_c0))]\n    signal: u8,\n    #[cfg(any(adc_v2, adc_g4, adc_g0, adc_c0))]\n    edge: Exten,\n}\n\n#[cfg(any(\n    adc_v2, adc_g4, adc_v3, adc_g0, adc_h5, adc_h7rs, adc_u0, adc_v4, adc_u5, adc_u3, adc_wba, adc_c0\n))]\npub(crate) enum ConversionMode {\n    // Should match the cfg on \"read\" below\n    #[cfg(any(\n        adc_g4, adc_v3, adc_g0, adc_h5, adc_h7rs, adc_u0, adc_v4, adc_u5, adc_u3, adc_wba, adc_c0\n    ))]\n    Singular,\n    // Should match the cfg on \"into_ring_buffered\" below\n    #[cfg(any(adc_v2, adc_g4, adc_v3, adc_g0, adc_u0, adc_wba, adc_c0))]\n    Repeated(Trigger),\n}\n\nimpl<'d, T: Instance> Adc<'d, T> {\n    #[cfg(any(\n        adc_v2, adc_g4, adc_v3, adc_g0, adc_h5, adc_h7rs, adc_u0, adc_u3, adc_u5, adc_v3, adc_v4, adc_wba, adc_c0\n    ))]\n    /// Read an ADC pin.\n    pub fn blocking_read(\n        &mut self,\n        channel: &mut impl AdcChannel<T>,\n        sample_time: <T::Regs as BasicAdcRegs>::SampleTime,\n    ) -> u16 {\n        #[cfg(any(adc_v1, adc_c0, adc_l0, adc_v2, adc_g4, adc_v3, adc_v4, adc_u3, adc_u5, adc_wba))]\n        channel.setup();\n\n        // Ensure no conversions are ongoing\n        T::regs().stop();\n        #[cfg(any(adc_v2, adc_v3, adc_g0, adc_h7rs, adc_u0, adc_u3, adc_u5, adc_wba))]\n        T::regs().enable();\n        T::regs().configure_sequence([((channel.channel(), channel.is_differential()), sample_time)].into_iter());\n\n        // On chips with differential channels, enable after configure_sequence to allow setting differential channels\n        //\n        // TODO: If hardware allows, enable after configure_sequence on all chips\n        #[cfg(any(adc_g4, adc_h5, adc_c0))]\n        T::regs().enable();\n        T::regs().convert();\n\n        unsafe { core::ptr::read_volatile(T::regs().data()) }\n    }\n\n    #[cfg(any(\n        adc_g4, adc_v3, adc_g0, adc_h5, adc_h7rs, adc_u0, adc_v4, adc_u5, adc_u3, adc_wba, adc_c0\n    ))]\n    /// Read one or multiple ADC regular channels using DMA.\n    ///\n    /// `readings` must have a length that is a multiple of the length of the `sequence` iterator.\n    ///\n    /// Example\n    /// ```rust,ignore\n    /// use embassy_stm32::adc::{Adc, AdcChannel}\n    ///\n    /// let mut adc = Adc::new(p.ADC1);\n    /// let mut adc_pin0 = p.PA0.into();\n    /// let mut adc_pin1 = p.PA1.into();\n    /// let mut measurements = [0u16; 2];\n    ///\n    /// adc.read(\n    ///     p.DMA1_CH2.reborrow(),\n    ///     Irqs,\n    ///     [\n    ///         (&mut *adc_pin0, SampleTime::CYCLES160_5),\n    ///         (&mut *adc_pin1, SampleTime::CYCLES160_5),\n    ///     ]\n    ///     .into_iter(),\n    ///     &mut measurements,\n    /// )\n    /// .await;\n    /// defmt::info!(\"measurements: {}\", measurements);\n    /// ```\n    ///\n    /// Note: This is not very efficient as the ADC needs to be reconfigured for each read. Use\n    /// `into_ring_buffered`, `into_ring_buffered_and_injected`\n    ///\n    /// Note: Depending on hardware limitations, this method may require channels to be passed\n    /// in order or require the sequence to have the same sample time for all channnels, depending\n    /// on the number and properties of the channels in the sequence. This method will panic if\n    /// the hardware cannot deliver the requested configuration.\n    pub async fn read<'a, 'b: 'a, D: RxDma<T>>(\n        &mut self,\n        rx_dma: embassy_hal_internal::Peri<'a, D>,\n        irq: impl crate::interrupt::typelevel::Binding<D::Interrupt, crate::dma::InterruptHandler<D>> + 'a,\n        sequence: impl ExactSizeIterator<Item = (&'a mut AnyAdcChannel<'b, T>, <T::Regs as BasicAdcRegs>::SampleTime)>,\n        readings: &mut [u16],\n    ) {\n        let _scoped_wake_guard = <T as crate::rcc::SealedRccPeripheral>::RCC_INFO.wake_guard();\n\n        assert!(sequence.len() != 0, \"Asynchronous read sequence cannot be empty\");\n        assert!(\n            readings.len() % sequence.len() == 0,\n            \"Readings length must be a multiple of sequence length\"\n        );\n        assert!(\n            sequence.len() <= 16,\n            \"Asynchronous read sequence cannot be more than 16 in length\"\n        );\n\n        // Ensure no conversions are ongoing\n        T::regs().stop();\n        #[cfg(any(adc_g0, adc_v3, adc_h7rs, adc_u0, adc_v4, adc_u3, adc_u5, adc_wba))]\n        T::regs().enable();\n\n        T::regs().configure_sequence(\n            sequence.map(|(channel, sample_time)| ((channel.channel, channel.is_differential), sample_time)),\n        );\n\n        // On chips with differential channels, enable after configure_sequence to allow setting differential channels\n        //\n        // TODO: If hardware allows, enable after configure_sequence on all chips\n        #[cfg(any(adc_g4, adc_h5, adc_c0))]\n        T::regs().enable();\n        T::regs().configure_dma(ConversionMode::Singular);\n\n        let request = rx_dma.request();\n        let mut dma_channel = crate::dma::Channel::new(rx_dma, irq);\n        let transfer = unsafe { dma_channel.read(request, T::regs().data(), readings, Default::default()) };\n\n        T::regs().start();\n\n        // Wait for conversion sequence to finish.\n        transfer.await;\n\n        // Ensure conversions are finished.\n        T::regs().stop();\n    }\n\n    #[cfg(any(adc_v2, adc_g4, adc_v3, adc_g0, adc_u0, adc_wba, adc_c0))]\n    /// Configures the ADC to use a DMA ring buffer for continuous data acquisition.\n    ///\n    /// Use the [`Self::read`] method to retrieve measurements from the DMA ring buffer. The read buffer\n    /// should be exactly half the size of `dma_buf`. When using triggered mode, it is recommended\n    /// to configure `dma_buf` as a double buffer so that one half can be read while the other half\n    /// is being filled by the DMA, preventing data loss. The trigger period of the ADC effectively\n    /// defines the period at which the buffer should be read.\n    ///\n    /// If continous conversion mode is selected, the provided `dma_buf` must be large enough to prevent\n    /// DMA buffer overruns. Its length should be a multiple of the number of ADC channels being measured.\n    /// For example, if 3 channels are measured and you want to store 40 samples per channel,\n    /// the buffer length should be `3 * 40 = 120`.\n    ///\n    /// # Parameters\n    /// - `dma`: The DMA peripheral used to transfer ADC data into the buffer.\n    /// - `dma_buf`: The buffer where DMA stores ADC samples.\n    /// - `regular_sequence`: Sequence of channels and sample times for regular ADC conversions.\n    /// - `regular_conversion_mode`: Mode for regular conversions (continuous or triggered).\n    ///\n    /// # Returns\n    /// A `RingBufferedAdc<'a, T>` instance configured for continuous DMA-based sampling.\n    ///\n    /// Note: Depending on hardware limitations, this method may require channels to be passed\n    /// in order or require the sequence to have the same sample time for all channnels, depending\n    /// on the number and properties of the channels in the sequence. This method will panic if\n    /// the hardware cannot deliver the requested configuration.\n    pub fn into_ring_buffered<'a, 'b, D: RxDma<T>>(\n        self,\n        dma: embassy_hal_internal::Peri<'a, D>,\n        dma_buf: &'a mut [u16],\n        irq: impl crate::interrupt::typelevel::Binding<D::Interrupt, crate::dma::InterruptHandler<D>> + 'a,\n        sequence: impl ExactSizeIterator<Item = (AnyAdcChannel<'b, T>, <T::Regs as BasicAdcRegs>::SampleTime)>,\n        _trigger: impl RegularTrigger<T>,\n        #[cfg(any(adc_v2, adc_g4, adc_g0, adc_c0))] edge: Exten,\n    ) -> RingBufferedAdc<'a, T> {\n        let sequence_len = sequence.len();\n        assert!(!dma_buf.is_empty() && dma_buf.len() <= 0xFFFF);\n        assert!(sequence_len != 0, \"Asynchronous read sequence cannot be empty\");\n        assert!(\n            sequence_len <= 16,\n            \"Asynchronous read sequence cannot be more than 16 in length\"\n        );\n        assert!(\n            dma_buf.len() % sequence_len == 0,\n            \"DMA buffer length must be a multiple of the scan sequence length\"\n        );\n        // Ensure no conversions are ongoing\n        T::regs().stop();\n        #[cfg(any(adc_g0, adc_v3, adc_h7rs, adc_u0, adc_v4, adc_u5, adc_wba, adc_c0))]\n        T::regs().enable();\n\n        T::regs().configure_sequence(\n            sequence.map(|(channel, sample_time)| ((channel.channel, channel.is_differential), sample_time)),\n        );\n\n        // On chips with differential channels, enable after configure_sequence to allow setting differential channels\n        //\n        // TODO: If hardware allows, enable after configure_sequence on all chips\n        #[cfg(any(adc_g4, adc_h5))]\n        T::regs().enable();\n        T::regs().configure_dma(ConversionMode::Repeated(Trigger {\n            #[cfg(any(adc_v2, adc_g4, adc_g0, adc_c0))]\n            signal: _trigger.signal(),\n            #[cfg(any(adc_v2, adc_g4, adc_g0, adc_c0))]\n            edge,\n        }));\n\n        core::mem::forget(self);\n\n        RingBufferedAdc::new(dma, irq, dma_buf, sequence_len)\n    }\n}\n\npub(self) trait SpecialChannel {}\n\n/// Implemented for ADCs that have a special channel\ntrait SealedSpecialConverter<T: SpecialChannel + Sized> {\n    const CHANNEL: u8;\n}\n\n#[allow(private_bounds)]\npub trait SpecialConverter<T: SpecialChannel + Sized>: SealedSpecialConverter<T> {}\n\nimpl<C: SpecialChannel + Sized, T: SealedSpecialConverter<C>> SpecialConverter<C> for T {}\n\nimpl<C: SpecialChannel, T: Instance + SealedSpecialConverter<C>> AdcChannel<T> for C {}\nimpl<C: SpecialChannel, T: Instance + SealedSpecialConverter<C>> SealedAdcChannel<T> for C {\n    fn channel(&self) -> u8 {\n        T::CHANNEL\n    }\n}\n\npub struct VrefInt;\nimpl SpecialChannel for VrefInt {}\n\nimpl VrefInt {\n    #[cfg(any(\n        stm32f0,\n        stm32f3,\n        stm32f7,\n        stm32g0,\n        stm32g4,\n        stm32l0,\n        stm32l1,\n        stm32l4,\n        stm32l4_plus,\n        stm32l5,\n        stm32n6,\n        stm32l5,\n        stm32wb,\n        stm32wl\n    ))]\n    /// The value that vref would be if vdda was at the factory calibration voltage `VREF_CALIB_MV`.\n    pub fn calibrated_value(&self) -> u16 {\n        crate::pac::VREFINTCAL.data().read()\n    }\n}\n\n/// Internal temperature channel.\npub struct Temperature;\nimpl SpecialChannel for Temperature {}\n\n/// Internal battery voltage channel.\npub struct Vbat;\nimpl SpecialChannel for Vbat {}\n\n/// Vcore channel.\npub struct Vcore;\nimpl SpecialChannel for Vcore {}\n\n/// Internal dac channel.\npub struct Dac;\nimpl SpecialChannel for Dac {}\n\n/// ADC instance.\n#[cfg(not(any(\n    adc_f1, adc_v1, adc_l0, adc_v2, adc_v3, adc_v4, adc_g4, adc_f3v1, adc_f3v2, adc_g0, adc_u0, adc_h5, adc_h7rs,\n    adc_u5, adc_u3, adc_c0, adc_wba,\n)))]\n#[allow(private_bounds)]\npub trait Instance: SealedInstance + crate::PeripheralType {\n    type Interrupt: crate::interrupt::typelevel::Interrupt;\n}\n/// ADC instance.\n#[cfg(any(\n    adc_f1, adc_v1, adc_l0, adc_v2, adc_v3, adc_v4, adc_g4, adc_f3v1, adc_f3v2, adc_g0, adc_u0, adc_h5, adc_h7rs,\n    adc_u5, adc_u3, adc_c0, adc_wba,\n))]\n#[allow(private_bounds)]\npub trait Instance: SealedInstance + crate::PeripheralType + crate::rcc::RccPeripheral {\n    type Interrupt: crate::interrupt::typelevel::Interrupt;\n}\n\n/// ADC channel.\n#[allow(private_bounds)]\npub trait AdcChannel<T>: SealedAdcChannel<T> + Sized {\n    #[allow(unused_mut)]\n    fn degrade_adc<'a>(mut self) -> AnyAdcChannel<'a, T>\n    where\n        Self: 'a,\n    {\n        #[cfg(any(adc_v1, adc_l0, adc_v2, adc_g4, adc_v3, adc_v4, adc_u3, adc_u5, adc_wba))]\n        self.setup();\n\n        AnyAdcChannel {\n            channel: self.channel(),\n            is_differential: self.is_differential(),\n            _phantom: PhantomData,\n        }\n    }\n}\n\n/// A type-erased channel for a given ADC instance.\n///\n/// This is useful in scenarios where you need the ADC channels to have the same type, such as\n/// storing them in an array.\npub struct AnyAdcChannel<'a, T> {\n    channel: u8,\n    is_differential: bool,\n    _phantom: PhantomData<&'a mut T>,\n}\nimpl<T: Instance> AdcChannel<T> for AnyAdcChannel<'_, T> {}\nimpl<T: Instance> SealedAdcChannel<T> for AnyAdcChannel<'_, T> {\n    fn channel(&self) -> u8 {\n        self.channel\n    }\n\n    fn is_differential(&self) -> bool {\n        self.is_differential\n    }\n}\n\nimpl<T> AnyAdcChannel<'_, T> {\n    #[allow(unused)]\n    pub fn get_hw_channel(&self) -> u8 {\n        self.channel\n    }\n}\n\n#[cfg(not(adc_wba))]\nimpl BasicAdcRegs for crate::pac::adc::Adc {\n    type SampleTime = SampleTime;\n}\n\n#[cfg(any(adc_wba, adc_u5))]\nimpl BasicAdcRegs for crate::pac::adc::Adc4 {\n    type SampleTime = Adc4SampleTime;\n}\n\ntrigger_trait!(RegularTrigger, Instance);\ntrigger_trait!(InjectedTrigger, Instance);\n\n#[cfg(adc_wba)]\nforeach_adc!(\n    (ADC4, $common_inst:ident, $clock:ident) => {\n        impl crate::adc::BasicInstance for peripherals::ADC4 {\n            type Regs = crate::pac::adc::Adc4;\n        }\n\n        impl crate::adc::SealedInstance for peripherals::ADC4 {\n            fn regs() -> Self::Regs {\n                crate::pac::ADC4\n            }\n\n            fn common_regs() -> crate::pac::adccommon::AdcCommon {\n                return crate::pac::$common_inst\n            }\n        }\n\n        impl crate::adc::Instance for peripherals::ADC4 {\n            type Interrupt = crate::_generated::peripheral_interrupts::ADC4::GLOBAL;\n        }\n    };\n\n    ($inst:ident, $common_inst:ident, $clock:ident) => {\n        impl crate::adc::SealedInstance for peripherals::$inst {\n            fn regs() -> crate::pac::adc::Adc {\n                crate::pac::$inst\n            }\n\n            fn common_regs() -> crate::pac::adccommon::AdcCommon {\n                return crate::pac::$common_inst\n            }\n        }\n\n        impl crate::adc::Instance for peripherals::$inst {\n            type Interrupt = crate::_generated::peripheral_interrupts::$inst::GLOBAL;\n        }\n    };\n);\n\n#[cfg(adc_u5)]\nforeach_adc!(\n    (ADC4, $common_inst:ident, $clock:ident) => {\n        impl crate::adc::BasicInstance for peripherals::ADC4 {\n            type Regs = crate::pac::adc::Adc4;\n        }\n\n        impl crate::adc::SealedInstance for peripherals::ADC4 {\n            fn regs() -> Self::Regs {\n                crate::pac::ADC4\n            }\n\n            fn common_regs() -> crate::pac::adccommon::AdcCommon {\n                return crate::pac::$common_inst\n            }\n        }\n\n        impl crate::adc::Instance for peripherals::ADC4 {\n            type Interrupt = crate::_generated::peripheral_interrupts::ADC4::GLOBAL;\n        }\n    };\n\n    ($inst:ident, $common_inst:ident, $clock:ident) => {\n        impl crate::adc::BasicInstance for peripherals::$inst {\n            type Regs = crate::pac::adc::Adc;\n        }\n\n        impl crate::adc::SealedInstance for peripherals::$inst {\n            fn regs() -> Self::Regs {\n                crate::pac::$inst\n            }\n\n            fn common_regs() -> crate::pac::adccommon::AdcCommon {\n                return crate::pac::$common_inst\n            }\n        }\n\n        impl crate::adc::Instance for peripherals::$inst {\n            type Interrupt = crate::_generated::peripheral_interrupts::$inst::GLOBAL;\n        }\n    };\n);\n\n#[cfg(not(any(adc_u5, adc_wba)))]\nforeach_adc!(\n    ($inst:ident, $common_inst:ident, $clock:ident) => {\n        impl crate::adc::BasicInstance for peripherals::$inst {\n            #[cfg(any(\n                adc_v2, adc_v3, adc_g0, adc_h5, adc_h7rs, adc_u0, adc_v4, adc_u3, adc_u5, adc_wba, adc_g4, adc_c0\n            ))]\n            type Regs = crate::pac::adc::Adc;\n        }\n\n        impl crate::adc::SealedInstance for peripherals::$inst {\n            #[cfg(any(\n                adc_v2, adc_v3, adc_g0, adc_h5, adc_h7rs, adc_u0, adc_v4, adc_u3, adc_u5, adc_wba, adc_g4, adc_c0\n            ))]\n            fn regs() -> Self::Regs {\n                crate::pac::$inst\n            }\n\n            #[cfg(any(adc_f1, adc_f3v1, adc_f3v2, adc_v1, adc_l0))]\n            fn regs() -> crate::pac::adc::Adc {\n                crate::pac::$inst\n            }\n\n            #[cfg(not(any(adc_f1, adc_v1, adc_l0, adc_f3v3, adc_f3v2, adc_g0, adc_u5, adc_wba)))]\n            fn common_regs() -> crate::pac::adccommon::AdcCommon {\n                return crate::pac::$common_inst\n            }\n\n            #[cfg(any(adc_f1, adc_f3v1, adc_v1, adc_l0, adc_f3v2))]\n            fn state() -> &'static State {\n                static STATE: State = State::new();\n                &STATE\n            }\n        }\n\n        impl crate::adc::Instance for peripherals::$inst {\n            type Interrupt = crate::_generated::peripheral_interrupts::$inst::GLOBAL;\n        }\n    };\n);\n\nmacro_rules! impl_adc_pin {\n    ($inst:ident, $pin:ident, $ch:expr) => {\n        impl crate::adc::AdcChannel<peripherals::$inst> for crate::Peri<'_, crate::peripherals::$pin> {}\n        impl crate::adc::SealedAdcChannel<peripherals::$inst> for crate::Peri<'_, crate::peripherals::$pin> {\n            #[cfg(any(\n                adc_v1, adc_c0, adc_l0, adc_v2, adc_g4, adc_v3, adc_v4, adc_u3, adc_u5, adc_wba\n            ))]\n            fn setup(&mut self) {\n                <crate::peripherals::$pin as crate::gpio::SealedPin>::set_as_analog(self);\n            }\n\n            fn channel(&self) -> u8 {\n                $ch\n            }\n        }\n    };\n}\n\n#[allow(unused_macros)]\nmacro_rules! impl_adc_pair {\n    ($inst:ident, $pin:ident, $npin:ident, $ch:expr) => {\n        impl crate::adc::AdcChannel<peripherals::$inst>\n            for (\n                crate::Peri<'_, crate::peripherals::$pin>,\n                crate::Peri<'_, crate::peripherals::$npin>,\n            )\n        {\n        }\n        impl crate::adc::SealedAdcChannel<peripherals::$inst>\n            for (\n                crate::Peri<'_, crate::peripherals::$pin>,\n                crate::Peri<'_, crate::peripherals::$npin>,\n            )\n        {\n            #[cfg(any(\n                adc_v1, adc_c0, adc_l0, adc_v2, adc_g4, adc_v3, adc_v4, adc_u3, adc_u5, adc_wba\n            ))]\n            fn setup(&mut self) {\n                <crate::peripherals::$pin as crate::gpio::SealedPin>::set_as_analog(&mut self.0);\n                <crate::peripherals::$npin as crate::gpio::SealedPin>::set_as_analog(&mut self.1);\n            }\n\n            fn channel(&self) -> u8 {\n                $ch\n            }\n\n            fn is_differential(&self) -> bool {\n                true\n            }\n        }\n    };\n}\n\n/// Get the maximum reading value for this resolution.\n///\n/// This is `2**n - 1`.\n#[cfg(not(any(adc_f1, adc_f3v3)))]\npub const fn resolution_to_max_count(res: Resolution) -> u32 {\n    match res {\n        #[cfg(adc_v4)]\n        Resolution::BITS16 => (1 << 16) - 1,\n        #[cfg(any(adc_v4, adc_u5))]\n        Resolution::BITS14 => (1 << 14) - 1,\n        #[cfg(adc_v4)]\n        Resolution::BITS14V => (1 << 14) - 1,\n        #[cfg(adc_v4)]\n        Resolution::BITS12V => (1 << 12) - 1,\n        Resolution::BITS12 => (1 << 12) - 1,\n        Resolution::BITS10 => (1 << 10) - 1,\n        Resolution::BITS8 => (1 << 8) - 1,\n        #[cfg(any(adc_v1, adc_v2, adc_v3, adc_l0, adc_c0, adc_g0, adc_f3v1, adc_f3v2, adc_h5))]\n        Resolution::BITS6 => (1 << 6) - 1,\n        #[allow(unreachable_patterns)]\n        _ => core::unreachable!(),\n    }\n}\n"
  },
  {
    "path": "embassy-stm32/src/adc/ringbuffered.rs",
    "content": "use core::marker::PhantomData;\nuse core::sync::atomic::{Ordering, compiler_fence};\n\n#[allow(unused_imports)]\nuse embassy_hal_internal::Peri;\n\nuse super::AdcRegs;\n#[allow(unused_imports)]\nuse crate::adc::{Instance, RxDma};\nuse crate::dma::Channel;\n#[allow(unused_imports)]\nuse crate::dma::{ReadableRingBuffer, TransferOptions};\nuse crate::rcc;\n\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub struct OverrunError;\n\npub struct RingBufferedAdc<'d, T: Instance> {\n    _phantom: PhantomData<T>,\n    ring_buf: ReadableRingBuffer<'d, u16>,\n}\n\nimpl<'d, T: Instance> RingBufferedAdc<'d, T> {\n    pub(crate) fn new<D: RxDma<T>>(\n        dma: Peri<'d, D>,\n        irq: impl crate::interrupt::typelevel::Binding<D::Interrupt, crate::dma::InterruptHandler<D>> + 'd,\n        dma_buf: &'d mut [u16],\n        sequence_len: usize,\n    ) -> Self {\n        // DMA side setup - configuration differs between DMA/BDMA and GPDMA\n        // For DMA/BDMA: use circular mode via TransferOptions\n        // For GPDMA: circular mode is achieved via linked-list ping-pong\n        #[cfg(not(gpdma))]\n        let opts = TransferOptions {\n            half_transfer_ir: true,\n            circular: true,\n            ..Default::default()\n        };\n\n        #[cfg(gpdma)]\n        let opts = TransferOptions {\n            half_transfer_ir: true,\n            ..Default::default()\n        };\n\n        // Safety: we forget the struct before this function returns.\n        let request = dma.request();\n\n        let mut ring_buf =\n            unsafe { ReadableRingBuffer::new(Channel::new(dma, irq), request, T::regs().data(), dma_buf, opts) };\n\n        // Align reads to the scan sequence boundary so that channel assignments\n        // never shift after an overrun recovery.\n        ring_buf.set_alignment(sequence_len);\n\n        Self {\n            _phantom: PhantomData,\n            ring_buf,\n        }\n    }\n\n    /// Turns on ADC if it is not already turned on and starts continuous DMA transfer.\n    pub fn start(&mut self) {\n        compiler_fence(Ordering::SeqCst);\n        self.ring_buf.start();\n\n        T::regs().start();\n    }\n\n    pub fn stop(&mut self) {\n        self.ring_buf.request_pause();\n\n        compiler_fence(Ordering::SeqCst);\n    }\n\n    pub fn clear(&mut self) {\n        self.ring_buf.clear();\n    }\n\n    /// See [`ReadableDmaRingBuffer::set_alignment`] for details.\n    pub fn set_alignment(&mut self, alignment: usize) {\n        self.ring_buf.set_alignment(alignment);\n    }\n\n    /// Reads measurements from the DMA ring buffer.\n    ///\n    /// This method fills the provided `measurements` array with ADC readings from the DMA buffer.\n    /// The length of the `measurements` array should be exactly half of the DMA buffer length.\n    /// Because interrupts are only generated if half or full DMA transfer completes.\n    ///\n    /// Each call to `read` will populate the `measurements` array in the same order as the channels\n    /// defined with `sequence`. There will be many sequences worth of measurements in this array\n    /// because it only returns if at least half of the DMA buffer is filled. For example if 2\n    /// channels are sampled `measurements` contain: `[sq0 sq1 sq0 sq1 sq0 sq1 ..]`.\n    ///\n    /// Note that the ADC Datarate can be very fast, it is suggested to use DMA mode inside tightly\n    /// running tasks. Otherwise, you'll see constant Overrun errors occurring, this means that\n    /// you're sampling too quickly for the task to handle, and you may need to increase the buffer size.\n    /// Example:\n    /// ```rust,ignore\n    /// const DMA_BUF_LEN: usize = 120;\n    /// use embassy_stm32::adc::{Adc, AdcChannel}\n    ///\n    /// let mut adc = Adc::new(p.ADC1);\n    /// let mut adc_pin0 = p.PA0.degrade_adc();\n    /// let mut adc_pin1 = p.PA1.degrade_adc();\n    /// let adc_dma_buf = [0u16; DMA_BUF_LEN];\n    ///\n    /// let mut ring_buffered_adc: RingBufferedAdc<embassy_stm32::peripherals::ADC1> = adc.into_ring_buffered(\n    ///     p.DMA2_CH0,\n    ///      adc_dma_buf, [\n    ///         (&mut *adc_pin0, SampleTime::CYCLES160_5),\n    ///         (&mut *adc_pin1, SampleTime::CYCLES160_5),\n    ///     ].into_iter());\n    ///\n    ///\n    /// let mut measurements = [0u16; DMA_BUF_LEN / 2];\n    /// loop {\n    ///     match ring_buffered_adc.read(&mut measurements).await {\n    ///         Ok(_) => {\n    ///             defmt::info!(\"adc1: {}\", measurements);\n    ///         }\n    ///         Err(e) => {\n    ///             defmt::warn!(\"Error: {:?}\", e);\n    ///         }\n    ///     }\n    /// }\n    /// ```\n    ///\n    ///\n    /// [`teardown_adc`]: #method.teardown_adc\n    /// [`start_continuous_sampling`]: #method.start_continuous_sampling\n    pub async fn read(&mut self, measurements: &mut [u16]) -> Result<usize, OverrunError> {\n        assert_eq!(\n            self.ring_buf.capacity() / 2,\n            measurements.len(),\n            \"Buffer size must be half the size of the ring buffer\"\n        );\n\n        if !self.ring_buf.is_running() {\n            self.start();\n        }\n\n        //        #[cfg(adc_v2)]\n        //        {\n        //            // Clear overrun flag if set.\n        //            if T::regs().sr().read().ovr() {\n        //                self.stop();\n        //\n        //                return Err(OverrunError);\n        //            }\n        //        }\n\n        self.ring_buf.read_exact(measurements).await.map_err(|_| OverrunError)\n    }\n\n    /// Read the most recent ADC measurements, discarding any older data.\n    ///\n    /// Returns the number of samples actually read into `measurements`. Unlike [`read`](Self::read),\n    /// this method **never returns an overrun error**. If the DMA has lapped the consumer\n    /// (e.g. because the task was not scheduled quickly enough), old data is silently\n    /// discarded and only the most recent samples are returned.\n    ///\n    /// This is ideal for use cases like ADC oversampling where the consumer only cares about\n    /// the latest values and stale data can be safely ignored.\n    pub fn read_latest(&mut self, measurements: &mut [u16]) -> usize {\n        if !self.ring_buf.is_running() {\n            self.start();\n        }\n\n        self.ring_buf.read_latest(measurements)\n    }\n\n    /// Read bytes that are readily available in the ring buffer.\n    /// If no bytes are currently available in the buffer the call waits until the some\n    /// bytes are available (at least one byte and at most half the buffer size)\n    ///\n    /// Background receive is started if `start_continuous_sampling()` has not been previously called.\n    ///\n    /// Receive in the background is terminated if an error is returned.\n    /// It must then manually be started again by calling `start_continuous_sampling()` or by re-calling `blocking_read()`.\n    pub fn blocking_read(&mut self, buf: &mut [u16]) -> Result<usize, OverrunError> {\n        if !self.ring_buf.is_running() {\n            self.start();\n        }\n\n        //        #[cfg(adc_v2)]\n        //        {\n        //            // Clear overrun flag if set.\n        //            if T::regs().sr().read().ovr() {\n        //                self.stop();\n        //\n        //                return Err(OverrunError);\n        //            }\n        //        }\n\n        loop {\n            match self.ring_buf.read(buf) {\n                Ok((0, _)) => {}\n                Ok((len, _)) => {\n                    return Ok(len);\n                }\n                Err(_) => {\n                    self.ring_buf.request_pause();\n\n                    return Err(OverrunError);\n                }\n            }\n        }\n    }\n}\n\nimpl<T: Instance> Drop for RingBufferedAdc<'_, T> {\n    fn drop(&mut self) {\n        T::regs().stop();\n\n        compiler_fence(Ordering::SeqCst);\n\n        self.ring_buf.request_pause();\n        rcc::disable::<T>();\n    }\n}\n"
  },
  {
    "path": "embassy-stm32/src/adc/v1.rs",
    "content": "use core::future::poll_fn;\nuse core::marker::PhantomData;\nuse core::task::Poll;\n\n#[cfg(adc_l0)]\nuse stm32_metapac::adc::vals::Ckmode;\n\n#[cfg(not(adc_l0))]\nuse super::Vbat;\nuse super::{Temperature, VrefInt, blocking_delay_us};\nuse crate::adc::{Adc, AdcChannel, Instance, Resolution, SampleTime};\nuse crate::interrupt::typelevel::Interrupt;\nuse crate::{Peri, interrupt, rcc};\n\nmod watchdog_v1;\npub use watchdog_v1::WatchdogChannels;\n\npub const VDDA_CALIB_MV: u32 = 3300;\npub const VREF_INT: u32 = 1230;\n\n/// Interrupt handler.\npub struct InterruptHandler<T: Instance> {\n    _phantom: PhantomData<T>,\n}\n\nimpl<T: Instance> interrupt::typelevel::Handler<T::Interrupt> for InterruptHandler<T> {\n    unsafe fn on_interrupt() {\n        let isr = T::regs().isr().read();\n        let ier = T::regs().ier().read();\n        if ier.eocie() && isr.eoc() {\n            // eocie is set during adc.read()\n            T::regs().ier().modify(|w| w.set_eocie(false));\n        } else if ier.awdie() && isr.awd() {\n            // awdie is set during adc.monitor_watchdog()\n            T::regs().cr().read().set_adstp(true);\n            T::regs().ier().modify(|w| w.set_awdie(false));\n        } else {\n            return;\n        }\n\n        T::state().waker.wake();\n    }\n}\n\n#[cfg(not(adc_l0))]\nimpl super::SealedSpecialConverter<super::Vbat> for crate::peripherals::ADC1 {\n    const CHANNEL: u8 = 18;\n}\n\nimpl super::SealedSpecialConverter<super::VrefInt> for crate::peripherals::ADC1 {\n    const CHANNEL: u8 = 17;\n}\n\n#[cfg(adc_l0)]\nimpl super::SealedSpecialConverter<super::Temperature> for crate::peripherals::ADC1 {\n    const CHANNEL: u8 = 18;\n}\n\n#[cfg(not(adc_l0))]\nimpl super::SealedSpecialConverter<super::Temperature> for crate::peripherals::ADC1 {\n    const CHANNEL: u8 = 16;\n}\n\nimpl<'d, T: Instance> Adc<'d, T> {\n    pub fn new(\n        adc: Peri<'d, T>,\n        _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd,\n    ) -> Self {\n        rcc::enable_and_reset_without_stop::<T>();\n\n        // Delay 1μs when using HSI14 as the ADC clock.\n        //\n        // Table 57. ADC characteristics\n        // tstab = 14 * 1/fadc\n        blocking_delay_us(1);\n\n        // set default PCKL/2 on L0s because HSI is disabled in the default clock config\n        #[cfg(adc_l0)]\n        T::regs().cfgr2().modify(|reg| reg.set_ckmode(Ckmode::PCLK_DIV2));\n\n        // A.7.1 ADC calibration code example\n        T::regs().cfgr1().modify(|reg| reg.set_dmaen(false));\n\n        #[cfg(adc_l0)]\n        let auto_off = T::regs().cfgr1().read().autoff();\n        #[cfg(adc_l0)]\n        T::regs().cfgr1().modify(|reg| reg.set_autoff(false));\n\n        T::regs().cr().modify(|reg| reg.set_adcal(true));\n\n        #[cfg(adc_l0)]\n        while !T::regs().isr().read().eocal() {}\n\n        #[cfg(not(adc_l0))]\n        while T::regs().cr().read().adcal() {}\n\n        #[cfg(adc_l0)]\n        T::regs().cfgr1().modify(|reg| reg.set_autoff(auto_off));\n\n        let s = Self { adc };\n\n        s.enable();\n\n        T::Interrupt::unpend();\n        unsafe {\n            T::Interrupt::enable();\n        }\n\n        s\n    }\n\n    fn enable(&self) {\n        #[cfg(adc_l0)]\n        if T::regs().cfgr1().read().autoff() {\n            // In AUTOFF mode the ADC wakes automatically when conversion starts,\n            // so waiting for ADRDY here can stall instead of helping.\n            return;\n        }\n\n        // A.7.2 ADC enable sequence code example\n\n        while T::regs().cr().read().addis() {}\n\n        if !T::regs().cr().read().aden() {\n            if T::regs().isr().read().adrdy() {\n                T::regs().isr().modify(|reg| reg.set_adrdy(true));\n            }\n            T::regs().cr().modify(|reg| reg.set_aden(true));\n            while !T::regs().isr().read().adrdy() {\n                // ES0233, 2.4.3 ADEN bit cannot be set immediately after the ADC calibration\n                // Workaround: keep setting ADEN until ADRDY goes high.\n                T::regs().cr().modify(|reg| reg.set_aden(true));\n            }\n        }\n    }\n\n    /// Power down the ADC.\n    ///\n    /// This stops ADC operation and powers down ADC-specific circuitry.\n    /// Later reads will enable the ADC again, but internal measurement paths\n    /// such as VREFINT or temperature sensing may need to be re-enabled.\n    pub fn power_down(&mut self) {\n        self.stop_watchdog();\n        Self::teardown_adc();\n    }\n\n    #[cfg(not(adc_l0))]\n    pub fn enable_vbat(&self) -> Vbat {\n        // SMP must be ≥ 56 ADC clock cycles when using HSI14.\n        //\n        // 6.3.20 Vbat monitoring characteristics\n        // ts_vbat ≥ 4μs\n        T::regs().ccr().modify(|reg| reg.set_vbaten(true));\n        Vbat\n    }\n\n    pub fn enable_vref(&self) -> VrefInt {\n        // Table 28. Embedded internal reference voltage\n        // tstart = 10μs\n        T::regs().ccr().modify(|reg| reg.set_vrefen(true));\n        blocking_delay_us(10);\n        VrefInt\n    }\n\n    pub fn enable_temperature(&self) -> Temperature {\n        // SMP must be ≥ 56 ADC clock cycles when using HSI14.\n        //\n        // 6.3.19 Temperature sensor characteristics\n        // tstart ≤ 10μs\n        // ts_temp ≥ 4μs\n        T::regs().ccr().modify(|reg| reg.set_tsen(true));\n        blocking_delay_us(10);\n        Temperature\n    }\n\n    #[cfg(adc_l0)]\n    pub fn enable_auto_off(&self) {\n        T::regs().cfgr1().modify(|reg| reg.set_autoff(true));\n    }\n\n    #[cfg(adc_l0)]\n    pub fn disable_auto_off(&self) {\n        T::regs().cfgr1().modify(|reg| reg.set_autoff(false));\n    }\n\n    pub fn set_resolution(&mut self, resolution: Resolution) {\n        T::regs().cfgr1().modify(|reg| reg.set_res(resolution.into()));\n    }\n\n    #[cfg(adc_l0)]\n    pub fn set_ckmode(&mut self, ckmode: Ckmode) {\n        // set ADC clock mode\n        T::regs().cfgr2().modify(|reg| reg.set_ckmode(ckmode));\n    }\n\n    pub async fn read(&mut self, channel: &mut impl AdcChannel<T>, sample_time: SampleTime) -> u16 {\n        let _scoped_wake_guard = <T as crate::rcc::SealedRccPeripheral>::RCC_INFO.wake_guard();\n        self.enable();\n\n        let ch_num = channel.channel();\n        channel.setup();\n\n        // A.7.5 Single conversion sequence code example - Software trigger\n        T::regs().chselr().write(|reg| reg.set_chsel_x(ch_num as usize, true));\n        T::regs().smpr().modify(|reg| reg.set_smp(sample_time.into()));\n\n        self.convert().await\n    }\n\n    async fn convert(&mut self) -> u16 {\n        T::regs().isr().modify(|reg| {\n            reg.set_eoc(true);\n            reg.set_eosmp(true);\n        });\n\n        T::regs().ier().modify(|w| w.set_eocie(true));\n        T::regs().cr().modify(|reg| reg.set_adstart(true));\n\n        poll_fn(|cx| {\n            T::state().waker.register(cx.waker());\n\n            if T::regs().isr().read().eoc() {\n                Poll::Ready(())\n            } else {\n                Poll::Pending\n            }\n        })\n        .await;\n\n        T::regs().dr().read().data()\n    }\n\n    fn teardown_adc() {\n        // A.7.3 ADC disable code example\n        if T::regs().cr().read().adstart() {\n            T::regs().cr().modify(|reg| reg.set_adstp(true));\n            while T::regs().cr().read().adstp() {}\n        }\n\n        if T::regs().cr().read().aden() {\n            T::regs().cr().modify(|reg| reg.set_addis(true));\n            while T::regs().cr().read().aden() {}\n        }\n\n        T::regs().ccr().modify(|reg| {\n            reg.set_vrefen(false);\n            reg.set_tsen(false);\n            #[cfg(not(adc_l0))]\n            reg.set_vbaten(false);\n        });\n\n        if T::regs().isr().read().adrdy() {\n            T::regs().isr().modify(|reg| reg.set_adrdy(true));\n        }\n\n        #[cfg(adc_l0)]\n        T::regs().cr().modify(|reg| reg.set_advregen(false));\n    }\n}\n\nimpl<'d, T: Instance> Drop for Adc<'d, T> {\n    fn drop(&mut self) {\n        self.stop_watchdog();\n        Self::teardown_adc();\n        Self::teardown_awd();\n\n        <T as crate::rcc::SealedRccPeripheral>::RCC_INFO.disable_without_stop();\n    }\n}\n"
  },
  {
    "path": "embassy-stm32/src/adc/v2.rs",
    "content": "use core::mem;\nuse core::sync::atomic::{Ordering, compiler_fence};\n\nuse super::{AnyAdcChannel, ConversionMode, Temperature, Vbat, VrefInt, blocking_delay_us};\nuse crate::adc::{\n    Adc, AdcRegs, BasicAdcRegs, InjectedTrigger, Instance, RegularTrigger, Resolution, RxDma, SampleTime,\n    SealedAdcChannel,\n};\nuse crate::pac::adc::vals;\npub use crate::pac::adccommon::vals::Adcpre;\nuse crate::time::Hertz;\nuse crate::{Peri, rcc};\n\nmod injected;\npub use injected::InjectedAdc;\n\nfn clear_interrupt_flags(r: crate::pac::adc::Adc) {\n    r.sr().modify(|regs| {\n        regs.set_eoc(false);\n        regs.set_ovr(false);\n    });\n}\n\n/// Default VREF voltage used for sample conversion to millivolts.\npub const VREF_DEFAULT_MV: u32 = 3300;\n/// VREF voltage used for factory calibration of VREFINTCAL register.\npub const VREF_CALIB_MV: u32 = 3300;\n\nconst NR_INJECTED_RANKS: usize = 4;\n\nimpl super::SealedSpecialConverter<super::VrefInt> for crate::peripherals::ADC1 {\n    const CHANNEL: u8 = 17;\n}\n\n#[cfg(any(stm32f2, stm32f40x, stm32f41x))]\nimpl super::SealedSpecialConverter<super::Temperature> for crate::peripherals::ADC1 {\n    const CHANNEL: u8 = 16;\n}\n\n#[cfg(not(any(stm32f2, stm32f40x, stm32f41x)))]\nimpl super::SealedSpecialConverter<super::Temperature> for crate::peripherals::ADC1 {\n    const CHANNEL: u8 = 18;\n}\n\nimpl super::SealedSpecialConverter<super::Vbat> for crate::peripherals::ADC1 {\n    const CHANNEL: u8 = 18;\n}\n\nimpl VrefInt {\n    /// Time needed for internal voltage reference to stabilize\n    pub fn start_time_us() -> u32 {\n        10\n    }\n}\n\nimpl Temperature {\n    /// Time needed for temperature sensor readings to stabilize\n    pub fn start_time_us() -> u32 {\n        10\n    }\n}\n\nfn from_pclk2(freq: Hertz) -> Adcpre {\n    // Datasheet for F2 specifies min frequency 0.6 MHz, and max 30 MHz (with VDDA 2.4-3.6V).\n    #[cfg(stm32f2)]\n    const MAX_FREQUENCY: Hertz = Hertz(30_000_000);\n    // Datasheet for both F4 and F7 specifies min frequency 0.6 MHz, typ freq. 30 MHz and max 36 MHz.\n    #[cfg(not(stm32f2))]\n    const MAX_FREQUENCY: Hertz = Hertz(36_000_000);\n    let raw_div = rcc::raw_prescaler(freq.0, MAX_FREQUENCY.0);\n    match raw_div {\n        0..=1 => Adcpre::DIV2,\n        2..=3 => Adcpre::DIV4,\n        4..=5 => Adcpre::DIV6,\n        6..=7 => Adcpre::DIV8,\n        _ => panic!(\"Selected PCLK2 frequency is too high for ADC with largest possible prescaler.\"),\n    }\n}\n\n/// ADC configuration\n#[derive(Default)]\npub struct AdcConfig {\n    pub resolution: Option<Resolution>,\n}\n\nimpl super::AdcRegs for crate::pac::adc::Adc {\n    fn data(&self) -> *mut u16 {\n        crate::pac::adc::Adc::dr(*self).as_ptr() as *mut u16\n    }\n\n    fn enable(&self) {\n        self.cr2().modify(|reg| {\n            reg.set_adon(true);\n        });\n\n        blocking_delay_us(3);\n    }\n\n    fn start(&self) {\n        // Begin ADC conversions\n        self.cr2().modify(|reg| {\n            reg.set_swstart(true);\n        });\n    }\n\n    fn stop(&self) {\n        let r = self;\n\n        // Stop ADC\n        r.cr2().modify(|reg| {\n            // Stop ADC\n            reg.set_swstart(false);\n            // Stop ADC\n            reg.set_adon(false);\n            // Stop DMA\n            reg.set_dma(false);\n        });\n\n        r.cr1().modify(|w| {\n            // Disable interrupt for end of conversion\n            w.set_eocie(false);\n            // Disable interrupt for overrun\n            w.set_ovrie(false);\n        });\n\n        clear_interrupt_flags(*r);\n\n        compiler_fence(Ordering::SeqCst);\n    }\n\n    fn convert(&self) {\n        // clear end of conversion flag\n        self.sr().modify(|reg| {\n            reg.set_eoc(false);\n        });\n\n        // Start conversion\n        self.cr2().modify(|reg| {\n            reg.set_swstart(true);\n        });\n\n        while self.sr().read().strt() == false {\n            // spin //wait for actual start\n        }\n        while self.sr().read().eoc() == false {\n            // spin //wait for finish\n        }\n    }\n\n    fn configure_dma(&self, conversion_mode: ConversionMode) {\n        match conversion_mode {\n            ConversionMode::Repeated(trigger) => {\n                let r = self;\n                // Clear all interrupts\n                r.sr().modify(|regs| {\n                    regs.set_eoc(false);\n                    regs.set_ovr(false);\n                    regs.set_strt(false);\n                });\n\n                r.cr1().modify(|w| {\n                    // Enable interrupt for end of conversion\n                    w.set_eocie(true);\n                    // Enable interrupt for overrun\n                    w.set_ovrie(true);\n                    // Scanning conversions of multiple channels\n                    w.set_scan(true);\n                    // Continuous conversion mode\n                    w.set_discen(false);\n                });\n\n                r.cr2().modify(|w| {\n                    // Enable DMA mode\n                    w.set_dma(true);\n                    // DMA requests are issues as long as DMA=1 and data are converted.\n                    w.set_dds(vals::Dds::CONTINUOUS);\n                    // EOC flag is set at the end of each conversion.\n                    w.set_eocs(vals::Eocs::EACH_CONVERSION);\n                });\n\n                match trigger.signal {\n                    u8::MAX => {\n                        // continuous conversion\n                        r.cr2().modify(|w| {\n                            // Enable continuous conversions\n                            w.set_cont(true);\n                        });\n                    }\n                    _ => {\n                        r.cr2().modify(|w| {\n                            // Disable continuous conversions\n                            w.set_cont(false);\n                            // Trigger detection edge\n                            w.set_exten(trigger.edge);\n                            // Trigger channel\n                            w.set_extsel(trigger.signal);\n                        })\n                    }\n                };\n            }\n        }\n    }\n\n    fn configure_sequence(&self, sequence: impl ExactSizeIterator<Item = ((u8, bool), SampleTime)>) {\n        self.cr2().modify(|reg| {\n            reg.set_adon(true);\n        });\n\n        // Check the sequence is long enough\n        self.sqr1().modify(|r| {\n            r.set_l((sequence.len() - 1).try_into().unwrap());\n        });\n\n        for (i, ((ch, _), sample_time)) in sequence.enumerate() {\n            // Set the channel in the right sequence field.\n            self.sqr3().modify(|w| w.set_sq(i, ch));\n\n            let sample_time = sample_time.into();\n            if ch <= 9 {\n                self.smpr2().modify(|reg| reg.set_smp(ch as _, sample_time));\n            } else {\n                self.smpr1().modify(|reg| reg.set_smp((ch - 10) as _, sample_time));\n            }\n        }\n    }\n}\n\nimpl<'d, T> Adc<'d, T>\nwhere\n    T: Instance<Regs = crate::pac::adc::Adc>,\n{\n    pub fn new(adc: Peri<'d, T>) -> Self {\n        Self::new_with_config(adc, Default::default())\n    }\n\n    pub fn new_with_config(adc: Peri<'d, T>, config: AdcConfig) -> Self {\n        rcc::enable_and_reset::<T>();\n\n        let presc = from_pclk2(T::frequency());\n        T::common_regs().ccr().modify(|w| w.set_adcpre(presc));\n        T::regs().enable();\n\n        if let Some(resolution) = config.resolution {\n            T::regs().cr1().modify(|reg| reg.set_res(resolution.into()));\n        }\n\n        Self { adc }\n    }\n\n    /// Enables internal voltage reference and returns [VrefInt], which can be used in\n    /// [Adc::read_internal()] to perform conversion.\n    pub fn enable_vrefint(&self) -> VrefInt {\n        T::common_regs().ccr().modify(|reg| {\n            reg.set_tsvrefe(true);\n        });\n\n        VrefInt {}\n    }\n\n    /// Enables internal temperature sensor and returns [Temperature], which can be used in\n    /// [Adc::read_internal()] to perform conversion.\n    ///\n    /// On STM32F42 and STM32F43 this can not be used together with [Vbat]. If both are enabled,\n    /// temperature sensor will return vbat value.\n    pub fn enable_temperature(&self) -> Temperature {\n        T::common_regs().ccr().modify(|reg| {\n            reg.set_tsvrefe(true);\n        });\n\n        Temperature {}\n    }\n\n    /// Enables vbat input and returns [Vbat], which can be used in\n    /// [Adc::read_internal()] to perform conversion.\n    pub fn enable_vbat(&self) -> Vbat {\n        T::common_regs().ccr().modify(|reg| {\n            reg.set_vbate(true);\n        });\n\n        Vbat {}\n    }\n    /// Configures the ADC for injected conversions.\n    ///\n    /// Injected conversions are separate from the regular conversion sequence and are typically\n    /// triggered by software or an external event. This method sets up a fixed-length sequence of\n    /// injected channels with specified sample times, the trigger source, and whether the end-of-sequence\n    /// interrupt should be enabled.\n    ///\n    /// # Parameters\n    /// - `sequence`: An array of tuples containing the ADC channels and their sample times. The length\n    ///   `N` determines the number of injected ranks to configure (maximum 4 for STM32).\n    /// - `trigger`: The trigger source that starts the injected conversion sequence.\n    /// - `interrupt`: If `true`, enables the end-of-sequence (JEOS) interrupt for injected conversions.\n    ///\n    /// # Returns\n    /// An `InjectedAdc<T, N>` instance that represents the configured injected sequence. The returned\n    /// type encodes the sequence length `N` in its type, ensuring that reads return exactly `N` samples.\n    ///\n    /// # Panics\n    /// This function will panic if:\n    /// - `sequence` is empty.\n    /// - `sequence` length exceeds the maximum number of injected ranks (`NR_INJECTED_RANKS`).\n    ///\n    /// # Notes\n    /// - Injected conversions can run independently of regular ADC conversions.\n    /// - The order of channels in `sequence` determines the rank order in the injected sequence.\n    /// - Accessing samples beyond `N` will result in a panic; use the returned type\n    ///   `InjectedAdc<T, N>` to enforce bounds at compile time.\n    pub fn setup_injected_conversions<'a, const N: usize>(\n        self,\n        sequence: [(AnyAdcChannel<'a, T>, SampleTime); N],\n        trigger: impl InjectedTrigger<T>,\n        edge: vals::Exten,\n        interrupt: bool,\n    ) -> InjectedAdc<'a, T, N> {\n        assert!(N != 0, \"Read sequence cannot be empty\");\n        assert!(\n            N <= NR_INJECTED_RANKS,\n            \"Read sequence cannot be more than {} in length\",\n            NR_INJECTED_RANKS\n        );\n\n        T::regs().enable();\n\n        T::regs().cr1().modify(|w| w.set_jauto(false));\n        // Set injected sequence length\n        T::regs().jsqr().modify(|w| w.set_jl(N as u8 - 1));\n\n        for (n, (channel, sample_time)) in sequence.iter().enumerate() {\n            let sample_time = sample_time.clone().into();\n            if channel.channel() <= 9 {\n                T::regs()\n                    .smpr2()\n                    .modify(|reg| reg.set_smp(channel.channel() as _, sample_time));\n            } else {\n                T::regs()\n                    .smpr1()\n                    .modify(|reg| reg.set_smp((channel.channel() - 10) as _, sample_time));\n            }\n\n            // On adc_v2/F4, injected JSQ rank field placement depends on the\n            // programmed sequence length (JL). ST's HAL uses:\n            //   shift = 5 * ((rank + 3) - sequence_len)\n            // with rank starting at 1.\n            let idx = n + (4 - N);\n\n            T::regs().jsqr().modify(|w| w.set_jsq(idx, channel.channel()));\n        }\n\n        T::regs().cr1().modify(|w| {\n            w.set_scan(true);\n            w.set_jdiscen(false);\n            w.set_jeocie(interrupt);\n        });\n        T::regs().cr2().modify(|w| {\n            w.set_jextsel(trigger.signal());\n            w.set_jexten(edge);\n        });\n\n        Self::start_injected_conversions();\n\n        mem::forget(self);\n\n        InjectedAdc::new(sequence) // InjectedAdc<'a, T, N> now borrows the channels\n    }\n\n    /// Configures ADC for both regular conversions with a ring-buffered DMA and injected conversions.\n    ///\n    /// # Parameters\n    /// - `dma`: The DMA peripheral to use for the ring-buffered ADC transfers.\n    /// - `dma_buf`: The buffer to store DMA-transferred samples for regular conversions.\n    /// - `regular_sequence`: The sequence of channels and their sample times for regular conversions.\n    /// - `regular_conversion_mode`: The mode for regular conversions (e.g., continuous or triggered).\n    /// - `injected_sequence`: An array of channels and sample times for injected conversions (length `N`).\n    /// - `injected_trigger`: The trigger source for injected conversions.\n    /// - `injected_interrupt`: Whether to enable the end-of-sequence interrupt for injected conversions.\n    ///\n    /// Injected conversions are typically used with interrupts. If ADC1 and ADC2 are used in dual mode,\n    /// it is recommended to enable interrupts only for the ADC whose sequence takes the longest to complete.\n    ///\n    /// # Returns\n    /// A tuple containing:\n    /// 1. `RingBufferedAdc<'a, T>` — the configured ADC for regular conversions using DMA.\n    /// 2. `InjectedAdc<T, N>` — the configured ADC for injected conversions.\n    ///\n    /// # Safety\n    /// This function is `unsafe` because it clones the ADC peripheral handle unchecked. Both the\n    /// `RingBufferedAdc` and `InjectedAdc` take ownership of the handle and drop it independently.\n    /// Ensure no other code concurrently accesses the same ADC instance in a conflicting way.\n    pub fn into_ring_buffered_and_injected<'a, 'b, const N: usize, D: RxDma<T>>(\n        self,\n        dma: Peri<'a, D>,\n        dma_buf: &'a mut [u16],\n        _irq: impl crate::interrupt::typelevel::Binding<D::Interrupt, crate::dma::InterruptHandler<D>> + 'a,\n        regular_sequence: impl ExactSizeIterator<Item = (AnyAdcChannel<'b, T>, <T::Regs as BasicAdcRegs>::SampleTime)>,\n        regular_trigger: impl RegularTrigger<T>,\n        regular_edge: vals::Exten,\n        injected_sequence: [(AnyAdcChannel<'b, T>, SampleTime); N],\n        injected_trigger: impl InjectedTrigger<T>,\n        injected_edge: vals::Exten,\n        injected_interrupt: bool,\n    ) -> (super::RingBufferedAdc<'a, T>, InjectedAdc<'b, T, N>) {\n        unsafe {\n            (\n                Self {\n                    adc: self.adc.clone_unchecked(),\n                }\n                .into_ring_buffered(\n                    dma,\n                    dma_buf,\n                    _irq,\n                    regular_sequence,\n                    regular_trigger,\n                    regular_edge,\n                ),\n                Self {\n                    adc: self.adc.clone_unchecked(),\n                }\n                .setup_injected_conversions(\n                    injected_sequence,\n                    injected_trigger,\n                    injected_edge,\n                    injected_interrupt,\n                ),\n            )\n        }\n    }\n\n    /// Stop injected conversions\n    pub(super) fn stop_injected_conversions() {\n        // No true \"abort injected conversion\" primitive on adc_v2.\n        // Best practical stop: disable external injected triggering.\n        T::regs().cr2().modify(|w| w.set_jexten(vals::Exten::DISABLED));\n        T::regs().cr1().modify(|w| w.set_jeocie(false));\n        T::regs().sr().modify(|w| {\n            w.set_jeoc(false);\n            w.set_jstrt(false);\n        });\n    }\n    /// Start injected ADC conversion\n    pub(super) fn start_injected_conversions() {\n        T::regs().sr().modify(|w| {\n            w.set_jeoc(false);\n            w.set_jstrt(false);\n        });\n\n        // On STM32F4 adc_v2, externally-triggered injected conversions are armed\n        // by JEXTEN and start on the next trigger event. JSWSTART is only valid\n        // for pure software-triggered injected conversions.\n        if T::regs().cr2().read().jexten() == vals::Exten::DISABLED {\n            T::regs().cr2().modify(|w| w.set_jswstart(true));\n        }\n    }\n}\nimpl<'a, T: Instance<Regs = crate::pac::adc::Adc>, const N: usize> InjectedAdc<'a, T, N> {\n    /// Read sampled data from all injected ADC injected ranks\n    /// Clear the JEOC and JSTRT flags to allow a new injected sequence\n    pub(super) fn read_injected_data() -> [u16; N] {\n        let mut data = [0u16; N];\n        for i in 0..N {\n            data[i] = T::regs().jdr(i).read().jdata();\n        }\n\n        // Clear JEOC and JSTRT\n        T::regs().sr().modify(|w| {\n            w.set_jeoc(false);\n            w.set_jstrt(false);\n        });\n        data\n    }\n}\n\nimpl<'d, T: Instance> Drop for Adc<'d, T> {\n    fn drop(&mut self) {\n        T::regs().stop();\n\n        rcc::disable::<T>();\n    }\n}\n"
  },
  {
    "path": "embassy-stm32/src/adc/v3.rs",
    "content": "use cfg_if::cfg_if;\n#[cfg(adc_g0)]\nuse heapless::Vec;\n#[cfg(adc_g0)]\nuse pac::adc::vals::Ckmode;\nuse pac::adc::vals::Dmacfg;\n#[cfg(adc_v3)]\nuse pac::adc::vals::{OversamplingRatio, OversamplingShift, Rovsm, Trovs};\n#[cfg(adc_g0)]\npub use pac::adc::vals::{Ovsr, Ovss, Presc};\n\n#[allow(unused_imports)]\nuse super::SealedAdcChannel;\nuse super::{Adc, Averaging, Instance, Resolution, SampleTime, Temperature, Vbat, VrefInt, blocking_delay_us};\nuse crate::adc::ConversionMode;\nuse crate::{Peri, pac, rcc};\n\n/// Default VREF voltage used for sample conversion to millivolts.\npub const VREF_DEFAULT_MV: u32 = 3300;\n#[cfg(any(adc_v3, adc_g0, adc_u0))]\n/// VREF voltage used for factory calibration of VREFINTCAL register.\npub const VREF_CALIB_MV: u32 = 3000;\n#[cfg(any(adc_h5, adc_h7rs))]\n/// VREF voltage used for factory calibration of VREFINTCAL register.\npub const VREF_CALIB_MV: u32 = 3300;\n\n#[cfg(adc_g0)]\n/// The number of variants in Smpsel\n// TODO: Use [#![feature(variant_count)]](https://github.com/rust-lang/rust/issues/73662) when stable\nconst SAMPLE_TIMES_CAPACITY: usize = 2;\n\n#[cfg(adc_g0)]\nimpl<T: Instance> super::SealedSpecialConverter<super::VrefInt> for T {\n    const CHANNEL: u8 = 13;\n}\n#[cfg(any(adc_h5, adc_h7rs))]\nimpl<T: Instance> super::SealedSpecialConverter<super::VrefInt> for T {\n    const CHANNEL: u8 = 17;\n}\n#[cfg(adc_u0)]\nimpl<T: Instance> super::SealedSpecialConverter<super::VrefInt> for T {\n    const CHANNEL: u8 = 12;\n}\n#[cfg(not(any(adc_g0, adc_h5, adc_h7rs, adc_u0)))]\nimpl<T: Instance> super::SealedSpecialConverter<super::VrefInt> for T {\n    const CHANNEL: u8 = 0;\n}\n\n#[cfg(adc_g0)]\nimpl<T: Instance> super::SealedSpecialConverter<super::Temperature> for T {\n    const CHANNEL: u8 = 12;\n}\n#[cfg(any(adc_h5, adc_h7rs))]\nimpl<T: Instance> super::SealedSpecialConverter<super::Temperature> for T {\n    const CHANNEL: u8 = 16;\n}\n#[cfg(adc_u0)]\nimpl<T: Instance> super::SealedSpecialConverter<super::Temperature> for T {\n    const CHANNEL: u8 = 11;\n}\n#[cfg(not(any(adc_g0, adc_h5, adc_h7rs, adc_u0)))]\nimpl<T: Instance> super::SealedSpecialConverter<super::Temperature> for T {\n    const CHANNEL: u8 = 17;\n}\n\n#[cfg(adc_g0)]\nimpl<T: Instance> super::SealedSpecialConverter<super::Vbat> for T {\n    const CHANNEL: u8 = 14;\n}\n#[cfg(any(adc_h5, adc_h7rs))]\nimpl<T: Instance> super::SealedSpecialConverter<super::Vbat> for T {\n    const CHANNEL: u8 = 16;\n}\n#[cfg(adc_u0)]\nimpl<T: Instance> super::SealedSpecialConverter<super::Vbat> for T {\n    const CHANNEL: u8 = 13;\n}\n#[cfg(not(any(adc_g0, adc_h5, adc_h7rs, adc_u0)))]\nimpl<T: Instance> super::SealedSpecialConverter<super::Vbat> for T {\n    const CHANNEL: u8 = 18;\n}\n\ncfg_if! {\n    if #[cfg(any(adc_h5, adc_h7rs))] {\n        pub struct VddCore;\n        impl<T: Instance> super::AdcChannel<T> for VddCore {}\n        impl<T: Instance> super::SealedAdcChannel<T> for VddCore {\n            fn channel(&self) -> u8 {\n                17\n            }\n        }\n    }\n}\n\ncfg_if! {\n    if #[cfg(adc_u0)] {\n        pub struct DacOut;\n        impl<T: Instance> super::AdcChannel<T> for DacOut {}\n        impl<T: Instance> super::SealedAdcChannel<T> for DacOut {\n            fn channel(&self) -> u8 {\n                19\n            }\n        }\n    }\n}\n\ncfg_if! { if #[cfg(adc_g0)] {\n\n/// Synchronous PCLK prescaler\npub enum CkModePclk {\n    DIV1,\n    DIV2,\n    DIV4,\n}\n\n/// The analog clock is either the synchronous prescaled PCLK or\n/// the asynchronous prescaled ADCCLK configured by the RCC mux.\n/// The data sheet states the maximum analog clock frequency -\n/// for STM32WL55CC it is 36 MHz.\npub enum Clock {\n    Sync { div: CkModePclk },\n    Async { div: Presc },\n}\n\n}}\n\n#[cfg(adc_u0)]\ntype Ovss = u8;\n#[cfg(adc_u0)]\ntype Ovsr = u8;\n#[cfg(adc_v3)]\ntype Ovss = OversamplingShift;\n#[cfg(adc_v3)]\ntype Ovsr = OversamplingRatio;\n\n/// Adc configuration\n#[derive(Default)]\npub struct AdcConfig {\n    #[cfg(any(adc_u0, adc_g0, adc_v3))]\n    pub oversampling_shift: Option<Ovss>,\n    #[cfg(any(adc_u0, adc_g0, adc_v3))]\n    pub oversampling_ratio: Option<Ovsr>,\n    #[cfg(any(adc_u0, adc_g0))]\n    pub oversampling_enable: Option<bool>,\n    #[cfg(adc_v3)]\n    pub oversampling_mode: Option<(Rovsm, Trovs, bool)>,\n    #[cfg(adc_g0)]\n    pub clock: Option<Clock>,\n    pub resolution: Option<Resolution>,\n    pub averaging: Option<Averaging>,\n}\n\nimpl super::AdcRegs for crate::pac::adc::Adc {\n    fn data(&self) -> *mut u16 {\n        crate::pac::adc::Adc::dr(*self).as_ptr() as *mut u16\n    }\n\n    // Enable ADC only when it is not already running.\n    fn enable(&self) {\n        #[cfg(adc_u0)]\n        if self.cfgr1().read().autoff() {\n            // In AUTOFF mode the ADC wakes automatically when conversion starts,\n            // so waiting for ADRDY here can stall instead of helping.\n            return;\n        }\n\n        // Make sure bits are off\n        while self.cr().read().addis() {\n            // spin\n        }\n\n        if !self.cr().read().aden() {\n            // Enable ADC\n            self.isr().modify(|reg| {\n                reg.set_adrdy(true);\n            });\n            self.cr().modify(|reg| {\n                reg.set_aden(true);\n            });\n\n            while !self.isr().read().adrdy() {\n                // spin\n            }\n        }\n    }\n\n    fn start(&self) {\n        self.cr().modify(|reg| {\n            reg.set_adstart(true);\n        });\n    }\n\n    fn stop(&self) {\n        // Ensure conversions are finished.\n        if self.cr().read().adstart() && !self.cr().read().addis() {\n            self.cr().modify(|reg| {\n                reg.set_adstp(true);\n            });\n            while self.cr().read().adstart() {}\n        }\n\n        // Reset configuration.\n        #[cfg(not(any(adc_g0, adc_u0)))]\n        self.cfgr().modify(|reg| {\n            reg.set_cont(false);\n            reg.set_dmaen(false);\n        });\n        #[cfg(any(adc_g0, adc_u0))]\n        self.cfgr1().modify(|reg| {\n            reg.set_cont(false);\n            reg.set_dmaen(false);\n        });\n    }\n\n    /// Perform a single conversion.\n    fn convert(&self) {\n        // Some models are affected by an erratum:\n        // If we perform conversions slower than 1 kHz, the first read ADC value can be\n        // corrupted, so we discard it and measure again.\n        //\n        // STM32L471xx: Section 2.7.3\n        // STM32G4: Section 2.7.3\n        #[cfg(any(rcc_l4, rcc_g4))]\n        let len = 2;\n\n        #[cfg(not(any(rcc_l4, rcc_g4)))]\n        let len = 1;\n\n        for _ in 0..len {\n            self.isr().modify(|reg| {\n                reg.set_eos(true);\n                reg.set_eoc(true);\n            });\n\n            // Start conversion\n            self.cr().modify(|reg| {\n                reg.set_adstart(true);\n            });\n\n            while !self.isr().read().eos() {\n                // spin\n            }\n        }\n    }\n\n    fn configure_dma(&self, conversion_mode: ConversionMode) {\n        // Set continuous mode with oneshot dma.\n        // Clear overrun flag before starting transfer.\n        self.isr().modify(|reg| {\n            reg.set_ovr(true);\n        });\n\n        #[cfg(not(any(adc_g0, adc_u0)))]\n        let regs = self.cfgr();\n\n        #[cfg(any(adc_g0, adc_u0))]\n        let regs = self.cfgr1();\n\n        match conversion_mode {\n            ConversionMode::Singular => {\n                regs.modify(|reg| {\n                    reg.set_discen(false);\n                    reg.set_cont(true);\n                    reg.set_dmacfg(Dmacfg::ONE_SHOT);\n                    reg.set_dmaen(true);\n                });\n            }\n            #[cfg(any(adc_v3, adc_g0, adc_u0))]\n            ConversionMode::Repeated(trigger) => {\n                #[cfg(not(adc_g0))]\n                {\n                    let _ = trigger; // Suppress unused variable warning\n                    // For non-G0 variants, only continuous conversions are supported\n                    regs.modify(|reg| {\n                        reg.set_discen(false);\n                        reg.set_cont(true);\n                        reg.set_dmacfg(Dmacfg::CIRCULAR);\n                        reg.set_dmaen(true);\n                    });\n                }\n                #[cfg(adc_g0)]\n                match trigger.signal {\n                    u8::MAX => {\n                        // continuous conversions\n                        regs.modify(|reg| {\n                            reg.set_discen(false);\n                            reg.set_cont(true);\n                            reg.set_dmacfg(Dmacfg::CIRCULAR);\n                            reg.set_dmaen(true);\n                        });\n                    }\n                    _ => {\n                        regs.modify(|reg| {\n                            reg.set_discen(false);\n                            reg.set_cont(false); // New trigger is needed for each sample to be read\n                            reg.set_dmacfg(Dmacfg::CIRCULAR);\n                            reg.set_dmaen(true);\n                            // Configure trigger edge (rising, falling, or both)\n                            reg.set_exten(trigger.edge);\n                            reg.set_extsel(trigger.signal.into());\n                        });\n\n                        // Regular conversions uses DMA so no need to generate interrupt\n                        self.ier().modify(|r| r.set_eosie(false));\n                    }\n                }\n            }\n        }\n    }\n\n    fn configure_sequence(&self, sequence: impl ExactSizeIterator<Item = ((u8, bool), SampleTime)>) {\n        #[cfg(adc_h5)]\n        self.cr().modify(|w| w.set_aden(false));\n\n        // Set sequence length\n        #[cfg(not(any(adc_g0, adc_u0)))]\n        self.sqr1().modify(|w| {\n            w.set_l(sequence.len() as u8 - 1);\n        });\n\n        #[cfg(adc_g0)]\n        {\n            let mut sample_times = Vec::<SampleTime, SAMPLE_TIMES_CAPACITY>::new();\n\n            self.chselr().write(|chselr| {\n                self.smpr().write(|smpr| {\n                    for ((channel, _), sample_time) in sequence {\n                        chselr.set_chsel(channel.into(), true);\n                        if let Some(i) = sample_times.iter().position(|&t| t == sample_time) {\n                            smpr.set_smpsel(channel.into(), (i as u8).into());\n                        } else {\n                            smpr.set_sample_time(sample_times.len(), sample_time);\n                            if let Err(_) = sample_times.push(sample_time) {\n                                panic!(\n                                    \"Implementation is limited to {} unique sample times among all channels.\",\n                                    SAMPLE_TIMES_CAPACITY\n                                );\n                            }\n                        }\n                    }\n                })\n            });\n        }\n        #[cfg(not(adc_g0))]\n        {\n            #[cfg(adc_u0)]\n            let mut channel_mask = 0;\n\n            #[cfg(adc_h5)]\n            let mut difsel = 0u32;\n\n            // Configure channels and ranks\n            for (_i, ((channel, _is_differential), sample_time)) in sequence.enumerate() {\n                // RM0492, RM0481, etc.\n                // \"This option bit must be set to 1 when ADCx_INP0 or ADCx_INN1 channel is selected.\"\n                #[cfg(any(adc_h5, adc_h7rs))]\n                if channel == 0 {\n                    self.or().modify(|reg| reg.set_op0(true));\n                }\n\n                // Configure channel\n                cfg_if! {\n                    if #[cfg(adc_u0)] {\n                        // On G0 and U6 all channels use the same sampling time.\n                        self.smpr().modify(|reg| reg.set_smp1(sample_time.into()));\n                    } else if #[cfg(any(adc_h5, adc_h7rs))] {\n                        match channel {\n                            0..=9 => self.smpr1().modify(|w| w.set_smp(channel as usize % 10, sample_time.into())),\n                            _ => self.smpr2().modify(|w| w.set_smp(channel as usize % 10, sample_time.into())),\n                        }\n                    } else {\n                        let sample_time = sample_time.into();\n                        self\n                            .smpr(channel as usize / 10)\n                            .modify(|reg| reg.set_smp(channel as usize % 10, sample_time));\n                    }\n                }\n\n                #[cfg(stm32h7)]\n                {\n                    use crate::pac::adc::vals::Pcsel;\n\n                    self.cfgr2().modify(|w| w.set_lshift(0));\n                    self.pcsel()\n                        .write(|w| w.set_pcsel(channel.channel() as _, Pcsel::PRESELECTED));\n                }\n\n                // Each channel is sampled according to sequence\n                #[cfg(not(any(adc_g0, adc_u0)))]\n                match _i {\n                    0..=3 => {\n                        self.sqr1().modify(|w| {\n                            w.set_sq(_i, channel);\n                        });\n                    }\n                    4..=8 => {\n                        self.sqr2().modify(|w| {\n                            w.set_sq(_i - 4, channel);\n                        });\n                    }\n                    9..=13 => {\n                        self.sqr3().modify(|w| {\n                            w.set_sq(_i - 9, channel);\n                        });\n                    }\n                    14..=15 => {\n                        self.sqr4().modify(|w| {\n                            w.set_sq(_i - 14, channel);\n                        });\n                    }\n                    _ => unreachable!(),\n                }\n\n                #[cfg(adc_h5)]\n                {\n                    difsel |= (_is_differential as u32) << channel;\n                }\n\n                #[cfg(adc_u0)]\n                {\n                    channel_mask |= 1 << channel;\n                }\n            }\n\n            #[cfg(adc_h5)]\n            self.difsel().write(|w| w.set_difsel(difsel));\n\n            // On G0 and U0 enabled channels are sampled from 0 to last channel.\n            // It is possible to add up to 8 sequences if CHSELRMOD = 1.\n            // However for supporting more than 8 channels alternative CHSELRMOD = 0 approach is used.\n            #[cfg(adc_u0)]\n            self.chselr().modify(|reg| {\n                reg.set_chsel(channel_mask);\n            });\n        }\n    }\n}\n\nimpl<'d, T: Instance<Regs = crate::pac::adc::Adc>> Adc<'d, T> {\n    /// Enable the voltage regulator\n    fn init_regulator() {\n        rcc::enable_and_reset_without_stop::<T>();\n        T::regs().cr().modify(|reg| {\n            #[cfg(not(any(adc_g0, adc_u0)))]\n            reg.set_deeppwd(false);\n            reg.set_advregen(true);\n        });\n\n        // If this is false then each ADC_CHSELR bit enables an input channel.\n        // This is the reset value, so has no effect.\n        #[cfg(any(adc_g0, adc_u0))]\n        T::regs().cfgr1().modify(|reg| {\n            reg.set_chselrmod(false);\n        });\n\n        blocking_delay_us(20);\n    }\n\n    /// Calibrate to remove conversion offset\n    fn init_calibrate() {\n        #[cfg(adc_u0)]\n        let auto_off = T::regs().cfgr1().read().autoff();\n        #[cfg(adc_u0)]\n        T::regs().cfgr1().modify(|reg| {\n            reg.set_autoff(false);\n        });\n\n        T::regs().cr().modify(|reg| {\n            reg.set_adcal(true);\n        });\n\n        while T::regs().cr().read().adcal() {\n            // spin\n        }\n\n        #[cfg(adc_u0)]\n        T::regs().cfgr1().modify(|reg| {\n            reg.set_autoff(auto_off);\n        });\n\n        blocking_delay_us(1);\n    }\n\n    /// Initialize the ADC leaving any analog clock at reset value.\n    /// For G0 and WL, this is the async clock without prescaler.\n    pub fn new(adc: Peri<'d, T>) -> Self {\n        Self::init_regulator();\n        Self::init_calibrate();\n        Self { adc }\n    }\n\n    pub fn new_with_config(adc: Peri<'d, T>, config: AdcConfig) -> Self {\n        #[cfg(not(adc_g0))]\n        let s = Self::new(adc);\n\n        #[cfg(adc_g0)]\n        let s = match config.clock {\n            Some(clock) => Self::new_with_clock(adc, clock),\n            None => Self::new(adc),\n        };\n\n        #[cfg(any(adc_g0, adc_u0, adc_v3))]\n        if let Some(shift) = config.oversampling_shift {\n            T::regs().cfgr2().modify(|reg| reg.set_ovss(shift));\n        }\n\n        #[cfg(any(adc_g0, adc_u0, adc_v3))]\n        if let Some(ratio) = config.oversampling_ratio {\n            T::regs().cfgr2().modify(|reg| reg.set_ovsr(ratio));\n        }\n\n        #[cfg(any(adc_g0, adc_u0))]\n        if let Some(enable) = config.oversampling_enable {\n            T::regs().cfgr2().modify(|reg| reg.set_ovse(enable));\n        }\n\n        #[cfg(adc_v3)]\n        if let Some((mode, trig_mode, enable)) = config.oversampling_mode {\n            T::regs().cfgr2().modify(|reg| reg.set_trovs(trig_mode));\n            T::regs().cfgr2().modify(|reg| reg.set_rovsm(mode));\n            T::regs().cfgr2().modify(|reg| reg.set_rovse(enable));\n        }\n\n        if let Some(resolution) = config.resolution {\n            #[cfg(not(any(adc_g0, adc_u0)))]\n            T::regs().cfgr().modify(|reg| reg.set_res(resolution.into()));\n            #[cfg(any(adc_g0, adc_u0))]\n            T::regs().cfgr1().modify(|reg| reg.set_res(resolution.into()));\n        }\n\n        if let Some(averaging) = config.averaging {\n            let (enable, samples, right_shift) = match averaging {\n                Averaging::Disabled => (false, 0, 0),\n                Averaging::Samples2 => (true, 0, 1),\n                Averaging::Samples4 => (true, 1, 2),\n                Averaging::Samples8 => (true, 2, 3),\n                Averaging::Samples16 => (true, 3, 4),\n                Averaging::Samples32 => (true, 4, 5),\n                Averaging::Samples64 => (true, 5, 6),\n                Averaging::Samples128 => (true, 6, 7),\n                Averaging::Samples256 => (true, 7, 8),\n            };\n            T::regs().cfgr2().modify(|reg| {\n                #[cfg(not(any(adc_g0, adc_u0)))]\n                reg.set_rovse(enable);\n                #[cfg(any(adc_g0, adc_u0))]\n                reg.set_ovse(enable);\n                #[cfg(any(adc_h5, adc_h7rs))]\n                reg.set_ovsr(samples.into());\n                #[cfg(not(any(adc_h5, adc_h7rs)))]\n                reg.set_ovsr(samples.into());\n                reg.set_ovss(right_shift.into());\n            })\n        }\n\n        s\n    }\n\n    #[cfg(adc_g0)]\n    /// Initialize ADC with explicit clock for the analog ADC\n    pub fn new_with_clock(adc: Peri<'d, T>, clock: Clock) -> Self {\n        Self::init_regulator();\n\n        #[cfg(any(stm32wl5x))]\n        {\n            // Reset value 0 is actually _No clock selected_ in the STM32WL5x reference manual\n            let async_clock_available = pac::RCC.ccipr().read().adcsel() != pac::rcc::vals::Adcsel::_RESERVED_0;\n            match clock {\n                Clock::Async { div: _ } => {\n                    assert!(async_clock_available);\n                }\n                Clock::Sync { div: _ } => {\n                    if async_clock_available {\n                        warn!(\"Not using configured ADC clock\");\n                    }\n                }\n            }\n        }\n        match clock {\n            Clock::Async { div } => T::regs().ccr().modify(|reg| reg.set_presc(div)),\n            Clock::Sync { div } => T::regs().cfgr2().modify(|reg| {\n                reg.set_ckmode(match div {\n                    CkModePclk::DIV1 => Ckmode::PCLK,\n                    CkModePclk::DIV2 => Ckmode::PCLK_DIV2,\n                    CkModePclk::DIV4 => Ckmode::PCLK_DIV4,\n                })\n            }),\n        }\n\n        Self::init_calibrate();\n\n        Self { adc }\n    }\n\n    /// Power down the ADC.\n    ///\n    /// This stops ADC operation and may reduce power consumption.\n    /// A later read will enable it automatically.\n    pub fn power_down(&mut self) {\n        super::AdcRegs::stop(&T::regs());\n\n        if T::regs().cr().read().aden() {\n            T::regs().cr().modify(|reg| {\n                reg.set_addis(true);\n            });\n            while T::regs().cr().read().aden() {}\n        }\n    }\n\n    #[cfg(adc_u0)]\n    pub fn enable_auto_off(&self) {\n        T::regs().cfgr1().modify(|reg| {\n            reg.set_autoff(true);\n        });\n    }\n\n    #[cfg(adc_u0)]\n    pub fn disable_auto_off(&self) {\n        T::regs().cfgr1().modify(|reg| {\n            reg.set_autoff(false);\n        });\n    }\n\n    pub fn enable_vrefint(&self) -> VrefInt {\n        #[cfg(not(any(adc_g0, adc_u0)))]\n        T::common_regs().ccr().modify(|reg| {\n            reg.set_vrefen(true);\n        });\n        #[cfg(any(adc_g0, adc_u0))]\n        T::regs().ccr().modify(|reg| {\n            reg.set_vrefen(true);\n        });\n\n        // \"Table 24. Embedded internal voltage reference\" states that it takes a maximum of 12 us\n        // to stabilize the internal voltage reference.\n        blocking_delay_us(15);\n\n        VrefInt {}\n    }\n\n    pub fn enable_temperature(&self) -> Temperature {\n        cfg_if! {\n            if #[cfg(any(adc_g0, adc_u0))] {\n                T::regs().ccr().modify(|reg| {\n                    reg.set_tsen(true);\n                });\n            } else if #[cfg(any(adc_h5, adc_h7rs))] {\n                T::common_regs().ccr().modify(|reg| {\n                    reg.set_tsen(true);\n                });\n            } else {\n                T::common_regs().ccr().modify(|reg| {\n                    reg.set_ch17sel(true);\n                });\n            }\n        }\n\n        Temperature {}\n    }\n\n    pub fn enable_vbat(&self) -> Vbat {\n        cfg_if! {\n            if #[cfg(any(adc_g0, adc_u0))] {\n                T::regs().ccr().modify(|reg| {\n                    reg.set_vbaten(true);\n                });\n            } else if #[cfg(any(adc_h5, adc_h7rs))] {\n                T::common_regs().ccr().modify(|reg| {\n                    reg.set_vbaten(true);\n                });\n            } else {\n                T::common_regs().ccr().modify(|reg| {\n                    reg.set_ch18sel(true);\n                });\n            }\n        }\n\n        Vbat {}\n    }\n\n    pub fn disable_vbat(&self) {\n        cfg_if! {\n            if #[cfg(any(adc_g0, adc_u0))] {\n                T::regs().ccr().modify(|reg| {\n                    reg.set_vbaten(false);\n                });\n            } else if #[cfg(any(adc_h5, adc_h7rs))] {\n                T::common_regs().ccr().modify(|reg| {\n                    reg.set_vbaten(false);\n                });\n            } else {\n                T::common_regs().ccr().modify(|reg| {\n                    reg.set_ch18sel(false);\n                });\n            }\n        }\n    }\n\n    /*\n    /// Convert a raw sample from the `Temperature` to deg C\n    pub fn to_degrees_centigrade(sample: u16) -> f32 {\n        (130.0 - 30.0) / (VtempCal130::get().read() as f32 - VtempCal30::get().read() as f32)\n            * (sample as f32 - VtempCal30::get().read() as f32)\n            + 30.0\n    }\n     */\n}\n\nimpl<'d, T: Instance> Drop for Adc<'d, T> {\n    fn drop(&mut self) {\n        super::AdcRegs::stop(&T::regs());\n        <T as crate::rcc::SealedRccPeripheral>::RCC_INFO.disable_without_stop();\n    }\n}\n"
  },
  {
    "path": "embassy-stm32/src/adc/v4.rs",
    "content": "#[cfg(not(stm32u3))]\nuse pac::adc::vals::Difsel;\n#[cfg(not(any(stm32u5, stm32u3)))]\nuse pac::adc::vals::{Adcaldif, Boost};\n#[allow(unused)]\nuse pac::adc::vals::{Adstp, Dmngt, Exten, Pcsel};\n#[cfg(not(stm32u3))]\nuse pac::adccommon::vals::Presc;\n\nuse super::{Adc, Averaging, Instance, Resolution, SampleTime, Temperature, Vbat, VrefInt, blocking_delay_us};\n#[cfg(any(stm32u5, stm32u3))]\nuse crate::adc::DefaultInstance;\nuse crate::adc::{AdcRegs, ConversionMode};\nuse crate::time::Hertz;\nuse crate::{Peri, pac, rcc};\n\n/// Default VREF voltage used for sample conversion to millivolts.\npub const VREF_DEFAULT_MV: u32 = 3300;\n/// VREF voltage used for factory calibration of VREFINTCAL register.\npub const VREF_CALIB_MV: u32 = 3300;\n\n/// Max single ADC operation clock frequency\n#[cfg(stm32g4)]\nconst MAX_ADC_CLK_FREQ: Hertz = Hertz::mhz(60);\n#[cfg(stm32h7)]\nconst MAX_ADC_CLK_FREQ: Hertz = Hertz::mhz(50);\n#[cfg(stm32u5)]\nconst MAX_ADC_CLK_FREQ: Hertz = Hertz::mhz(55);\n#[cfg(stm32u3)]\nconst MAX_ADC_CLK_FREQ: Hertz = Hertz::mhz(48);\n\n#[cfg(stm32g4)]\nimpl<T: Instance> super::SealedSpecialConverter<super::VrefInt> for T {\n    const CHANNEL: u8 = 18;\n}\n#[cfg(stm32g4)]\nimpl<T: Instance> super::SealedSpecialConverter<super::Temperature> for T {\n    const CHANNEL: u8 = 16;\n}\n\n#[cfg(stm32h7)]\nimpl<T: Instance> super::SealedSpecialConverter<super::VrefInt> for T {\n    const CHANNEL: u8 = 19;\n}\n#[cfg(stm32h7)]\nimpl<T: Instance> super::SealedSpecialConverter<super::Temperature> for T {\n    const CHANNEL: u8 = 18;\n}\n\n// TODO this should be 14 for H7a/b/35\n#[cfg(not(any(stm32u5, stm32u3)))]\nimpl<T: Instance> super::SealedSpecialConverter<super::Vbat> for T {\n    const CHANNEL: u8 = 17;\n}\n\n#[cfg(any(stm32u5, stm32u3))]\nimpl<T: DefaultInstance> super::SealedSpecialConverter<super::VrefInt> for T {\n    const CHANNEL: u8 = 0;\n}\n#[cfg(stm32u5)]\nimpl<T: DefaultInstance> super::SealedSpecialConverter<super::Temperature> for T {\n    const CHANNEL: u8 = 19;\n}\n#[cfg(stm32u5)]\nimpl<T: DefaultInstance> super::SealedSpecialConverter<super::Vbat> for T {\n    const CHANNEL: u8 = 18;\n}\n\n#[cfg(stm32u3)]\nimpl<T: DefaultInstance> super::SealedSpecialConverter<super::Vbat> for T {\n    const CHANNEL: u8 = 16;\n}\n\n#[cfg(stm32u3)]\nimpl<T: DefaultInstance> super::SealedSpecialConverter<super::Temperature> for T {\n    const CHANNEL: u8 = 17;\n}\n\n#[cfg(not(stm32u3))]\nfn from_ker_ck(frequency: Hertz) -> Presc {\n    let raw_prescaler = rcc::raw_prescaler(frequency.0, MAX_ADC_CLK_FREQ.0);\n    match raw_prescaler {\n        0 => Presc::DIV1,\n        1 => Presc::DIV2,\n        2..=3 => Presc::DIV4,\n        4..=5 => Presc::DIV6,\n        6..=7 => Presc::DIV8,\n        8..=9 => Presc::DIV10,\n        10..=11 => Presc::DIV12,\n        _ => unimplemented!(),\n    }\n}\n\n/// Adc configuration\n#[derive(Default)]\npub struct AdcConfig {\n    pub resolution: Option<Resolution>,\n    pub averaging: Option<Averaging>,\n}\n\nimpl AdcRegs for crate::pac::adc::Adc {\n    fn data(&self) -> *mut u16 {\n        crate::pac::adc::Adc::dr(*self).as_ptr() as *mut u16\n    }\n\n    fn enable(&self) {\n        if !self.cr().read().aden() {\n            self.isr().write(|w| w.set_adrdy(true));\n            self.cr().modify(|w| w.set_aden(true));\n            while !self.isr().read().adrdy() {}\n            self.isr().write(|w| w.set_adrdy(true));\n        }\n    }\n\n    fn start(&self) {\n        // Start conversion\n        self.cr().modify(|reg| {\n            reg.set_adstart(true);\n        });\n    }\n\n    fn stop(&self) {\n        if self.cr().read().adstart() && !self.cr().read().addis() {\n            self.cr().modify(|reg| {\n                reg.set_adstp(Adstp::STOP);\n            });\n            while self.cr().read().adstart() {}\n        }\n\n        // Reset configuration.\n        self.cfgr().modify(|reg| {\n            reg.set_cont(false);\n            reg.set_dmngt(Dmngt::from_bits(0));\n        });\n    }\n\n    fn convert(&self) {\n        self.isr().modify(|reg| {\n            reg.set_eos(true);\n            reg.set_eoc(true);\n        });\n\n        // Start conversion\n        self.cr().modify(|reg| {\n            reg.set_adstart(true);\n        });\n\n        while !self.isr().read().eos() {\n            // spin\n        }\n    }\n\n    fn configure_dma(&self, conversion_mode: ConversionMode) {\n        match conversion_mode {\n            ConversionMode::Singular => {\n                self.isr().modify(|reg| {\n                    reg.set_ovr(true);\n                });\n                self.cfgr().modify(|reg| {\n                    reg.set_cont(true);\n                    reg.set_dmngt(Dmngt::DMA_ONE_SHOT);\n                });\n            }\n            #[cfg(any(adc_v2, adc_g4, adc_v3, adc_g0, adc_u0))]\n            _ => unreachable!(),\n        }\n    }\n\n    fn configure_sequence(&self, sequence: impl ExactSizeIterator<Item = ((u8, bool), SampleTime)>) {\n        // Set sequence length\n        self.sqr1().modify(|w| {\n            w.set_l(sequence.len() as u8 - 1);\n        });\n\n        // Configure channels and ranks\n        for (i, ((channel, _), sample_time)) in sequence.enumerate() {\n            let sample_time = sample_time.into();\n            if channel <= 9 {\n                self.smpr(0).modify(|reg| reg.set_smp(channel as _, sample_time));\n            } else {\n                self.smpr(1).modify(|reg| reg.set_smp((channel - 10) as _, sample_time));\n            }\n\n            #[cfg(any(stm32h7, stm32u5, stm32u3))]\n            {\n                self.cfgr2().modify(|w| w.set_lshift(0));\n                self.pcsel().modify(|w| w.set_pcsel(channel as _, Pcsel::PRESELECTED));\n            }\n\n            match i {\n                0..=3 => {\n                    self.sqr1().modify(|w| {\n                        w.set_sq(i, channel);\n                    });\n                }\n                4..=8 => {\n                    self.sqr2().modify(|w| {\n                        w.set_sq(i - 4, channel);\n                    });\n                }\n                9..=13 => {\n                    self.sqr3().modify(|w| {\n                        w.set_sq(i - 9, channel);\n                    });\n                }\n                14..=15 => {\n                    self.sqr4().modify(|w| {\n                        w.set_sq(i - 14, channel);\n                    });\n                }\n                _ => unreachable!(),\n            }\n        }\n    }\n}\n\nimpl<'d, T: Instance<Regs = crate::pac::adc::Adc>> Adc<'d, T> {\n    pub fn new_with_config(adc: Peri<'d, T>, config: AdcConfig) -> Self {\n        let s = Self::new(adc);\n\n        // Set the ADC resolution.\n        if let Some(resolution) = config.resolution {\n            T::regs().cfgr().modify(|reg| reg.set_res(resolution.into()));\n        }\n\n        // Set hardware averaging.\n        if let Some(averaging) = config.averaging {\n            let (enable, samples, right_shift) = match averaging {\n                Averaging::Disabled => (false, 0, 0),\n                Averaging::Samples2 => (true, 1, 1),\n                Averaging::Samples4 => (true, 3, 2),\n                Averaging::Samples8 => (true, 7, 3),\n                Averaging::Samples16 => (true, 15, 4),\n                Averaging::Samples32 => (true, 31, 5),\n                Averaging::Samples64 => (true, 63, 6),\n                Averaging::Samples128 => (true, 127, 7),\n                Averaging::Samples256 => (true, 255, 8),\n                Averaging::Samples512 => (true, 511, 9),\n                Averaging::Samples1024 => (true, 1023, 10),\n            };\n\n            T::regs().cfgr2().modify(|reg| {\n                reg.set_rovse(enable);\n                reg.set_ovsr(samples);\n                reg.set_ovss(right_shift);\n            })\n        }\n\n        s\n    }\n\n    /// Create a new ADC driver.\n    pub fn new(adc: Peri<'d, T>) -> Self {\n        rcc::enable_and_reset::<T>();\n\n        #[cfg(not(stm32u3))]\n        let prescaler = from_ker_ck(T::frequency());\n        #[cfg(not(stm32u3))]\n        T::common_regs().ccr().modify(|w| w.set_presc(prescaler));\n        #[cfg(not(stm32u3))]\n        let frequency = T::frequency() / prescaler;\n\n        #[cfg(stm32u3)]\n        let frequency = T::frequency();\n\n        info!(\"ADC frequency set to {}\", frequency);\n\n        if frequency > MAX_ADC_CLK_FREQ {\n            panic!(\n                \"Maximal allowed frequency for the ADC is {} MHz and it varies with different packages, refer to ST docs for more information.\",\n                MAX_ADC_CLK_FREQ.0 / 1_000_000\n            );\n        }\n\n        #[cfg(stm32h7)]\n        {\n            let boost = if frequency < Hertz::khz(6_250) {\n                Boost::LT6_25\n            } else if frequency < Hertz::khz(12_500) {\n                Boost::LT12_5\n            } else if frequency < Hertz::mhz(25) {\n                Boost::LT25\n            } else {\n                Boost::LT50\n            };\n            T::regs().cr().modify(|w| w.set_boost(boost));\n        }\n\n        T::regs().cr().modify(|reg| {\n            reg.set_deeppwd(false);\n            reg.set_advregen(true);\n        });\n\n        blocking_delay_us(10);\n\n        #[cfg(not(stm32u3))]\n        T::regs().difsel().modify(|w| {\n            for n in 0..20 {\n                w.set_difsel(n, Difsel::SINGLE_ENDED);\n            }\n        });\n\n        #[cfg(not(stm32u3))]\n        T::regs().cr().modify(|w| {\n            #[cfg(not(adc_u5))]\n            w.set_adcaldif(Adcaldif::SINGLE_ENDED);\n            w.set_adcallin(true);\n        });\n\n        T::regs().cr().modify(|w| w.set_adcal(true));\n\n        while T::regs().cr().read().adcal() {}\n\n        blocking_delay_us(1);\n\n        T::regs().enable();\n\n        // single conversion mode, software trigger\n        T::regs().cfgr().modify(|w| {\n            w.set_cont(false);\n            w.set_exten(Exten::DISABLED);\n        });\n\n        Self { adc }\n    }\n\n    /// Enable reading the voltage reference internal channel.\n    pub fn enable_vrefint(&self) -> VrefInt {\n        T::common_regs().ccr().modify(|reg| {\n            reg.set_vrefen(true);\n        });\n\n        VrefInt {}\n    }\n\n    /// Enable reading the temperature internal channel.\n    pub fn enable_temperature(&self) -> Temperature {\n        T::common_regs().ccr().modify(|reg| {\n            reg.set_vsenseen(true);\n        });\n\n        Temperature {}\n    }\n\n    /// Enable reading the vbat internal channel.\n    pub fn enable_vbat(&self) -> Vbat {\n        T::common_regs().ccr().modify(|reg| {\n            reg.set_vbaten(true);\n        });\n\n        Vbat {}\n    }\n}\n"
  },
  {
    "path": "embassy-stm32/src/adc/watchdog_v1.rs",
    "content": "use core::future::poll_fn;\nuse core::task::Poll;\n\nuse stm32_metapac::adc::vals::{Align, Awdsgl, Res, SampleTime};\n\nuse crate::adc::{Adc, AdcChannel, Instance};\n\n/// This enum is passed into `Adc::init_watchdog` to specify the channels for the watchdog to monitor\npub enum WatchdogChannels {\n    // Single channel identified by index\n    Single(u8),\n    // Multiple channels identified by mask\n    Multiple(u16),\n}\n\nimpl WatchdogChannels {\n    pub fn from_channel<T>(channel: &impl AdcChannel<T>) -> Self {\n        Self::Single(channel.channel())\n    }\n\n    pub fn add_channel<T>(self, channel: &impl AdcChannel<T>) -> Self {\n        WatchdogChannels::Multiple(\n            (match self {\n                WatchdogChannels::Single(ch) => 1 << ch,\n                WatchdogChannels::Multiple(ch) => ch,\n            }) | 1 << channel.channel(),\n        )\n    }\n}\n\nimpl<'d, T: Instance> Adc<'d, T> {\n    /// Configure the analog window watchdog to monitor one or more ADC channels\n    ///\n    /// `high_threshold` and `low_threshold` are expressed in the same way as ADC results. The format\n    /// depends on the values of CFGR1.ALIGN and CFGR1.RES.\n    pub fn init_watchdog(&mut self, channels: WatchdogChannels, low_threshold: u16, high_threshold: u16) {\n        Self::stop_awd();\n\n        match channels {\n            WatchdogChannels::Single(ch) => {\n                T::regs().chselr().modify(|w| {\n                    w.set_chsel_x(ch.into(), true);\n                });\n                T::regs().cfgr1().modify(|w| {\n                    w.set_awdch(ch);\n                    w.set_awdsgl(Awdsgl::SINGLE_CHANNEL)\n                });\n            }\n            WatchdogChannels::Multiple(ch) => {\n                T::regs().chselr().modify(|w| w.0 = ch.into());\n                T::regs().cfgr1().modify(|w| {\n                    w.set_awdch(0);\n                    w.set_awdsgl(Awdsgl::ALL_CHANNELS)\n                });\n            }\n        }\n\n        Self::set_watchdog_thresholds(low_threshold, high_threshold);\n        Self::setup_awd();\n    }\n\n    /// Monitor the voltage on the selected channels; return when it crosses the thresholds.\n    ///\n    /// ```rust,ignore\n    /// // Wait for pin to go high\n    /// adc.init_watchdog(WatchdogChannels::from_channel(&pin), 0, 0x07F);\n    /// let v_high = adc.monitor_watchdog().await;\n    /// info!(\"ADC sample is high {}\", v_high);\n    /// ```\n    pub async fn monitor_watchdog(&mut self, sample_time: SampleTime) -> u16 {\n        let _scoped_wake_guard = <T as crate::rcc::SealedRccPeripheral>::RCC_INFO.wake_guard();\n        self.enable();\n\n        assert!(\n            match T::regs().cfgr1().read().awdsgl() {\n                Awdsgl::SINGLE_CHANNEL => T::regs().cfgr1().read().awdch() != 0,\n                Awdsgl::ALL_CHANNELS => T::regs().cfgr1().read().awdch() == 0,\n            },\n            \"`set_channel` should be called before `monitor`\",\n        );\n        assert!(T::regs().chselr().read().0 != 0);\n        T::regs().smpr().modify(|reg| reg.set_smp(sample_time.into()));\n        Self::start_awd();\n\n        let sample = poll_fn(|cx| {\n            T::state().waker.register(cx.waker());\n\n            if T::regs().isr().read().awd() {\n                Poll::Ready(T::regs().dr().read().data())\n            } else {\n                Poll::Pending\n            }\n        })\n        .await;\n\n        self.stop_watchdog();\n        sample\n    }\n\n    /// Stop monitoring the selected channels\n    pub fn stop_watchdog(&mut self) {\n        Self::stop_awd();\n    }\n\n    fn set_watchdog_thresholds(low_threshold: u16, high_threshold: u16) {\n        // This function takes `high_threshold` and `low_threshold` in the same alignment and resolution\n        // as ADC results, and programs them into ADC_DR. Because ADC_DR is always right-aligned on 12 bits,\n        // some bit-shifting may be necessary. See more in table 47 §13.7.1 Analog Watchdog Comparison\n\n        // Verify that the thresholds are in the correct bit positions according to alignment and resolution\n        let threshold_mask = match (T::regs().cfgr1().read().align(), T::regs().cfgr1().read().res()) {\n            (Align::LEFT, Res::BITS6) => 0x00FC,\n            (Align::LEFT, Res::BITS8) => 0xFF00,\n            (Align::LEFT, Res::BITS10) => 0xFFC0,\n            (Align::LEFT, Res::BITS12) => 0xFFF0,\n            (Align::RIGHT, Res::BITS6) => 0x003F,\n            (Align::RIGHT, Res::BITS8) => 0x00FF,\n            (Align::RIGHT, Res::BITS10) => 0x03FF,\n            (Align::RIGHT, Res::BITS12) => 0x0FFF,\n        };\n        assert!(\n            high_threshold & !threshold_mask == 0,\n            \"High threshold {:x} is invalid — only bits {:x} are allowed\",\n            high_threshold,\n            threshold_mask\n        );\n        assert!(\n            low_threshold & !threshold_mask == 0,\n            \"Low threshold {:x} is invalid — only bits {:x} are allowed\",\n            low_threshold,\n            threshold_mask\n        );\n\n        T::regs().tr().modify(|w| {\n            w.set_lt(low_threshold << threshold_mask.leading_zeros() >> 4);\n            w.set_ht(high_threshold << threshold_mask.leading_zeros() >> 4);\n        })\n    }\n\n    fn setup_awd() {\n        // Configure AWD\n        assert!(!T::regs().cr().read().adstart());\n        T::regs().cfgr1().modify(|w| w.set_awden(true));\n    }\n\n    fn start_awd() {\n        // Clear AWD interrupt flag\n        while T::regs().isr().read().awd() {\n            T::regs().isr().modify(|regs| {\n                regs.set_awd(true);\n            })\n        }\n\n        // Enable AWD interrupt\n        assert!(!T::regs().cr().read().adstart());\n        T::regs().ier().modify(|w| {\n            w.set_eocie(false);\n            w.set_awdie(true)\n        });\n\n        // Start conversion\n        T::regs().cfgr1().modify(|w| w.set_cont(true));\n        T::regs().cr().modify(|w| w.set_adstart(true));\n    }\n\n    fn stop_awd() {\n        // Stop conversion\n        while T::regs().cr().read().addis() {}\n        if T::regs().cr().read().adstart() {\n            T::regs().cr().write(|x| x.set_adstp(true));\n            while T::regs().cr().read().adstp() {}\n        }\n        T::regs().cfgr1().modify(|w| w.set_cont(false));\n\n        // Disable AWD interrupt\n        assert!(!T::regs().cr().read().adstart());\n        T::regs().ier().modify(|w| w.set_awdie(false));\n\n        // Clear AWD interrupt flag\n        while T::regs().isr().read().awd() {\n            T::regs().isr().modify(|regs| {\n                regs.set_awd(true);\n            })\n        }\n    }\n\n    pub(crate) fn teardown_awd() {\n        Self::stop_awd();\n        T::regs().cfgr1().modify(|w| w.set_awden(false));\n    }\n}\n"
  },
  {
    "path": "embassy-stm32/src/aes/mod.rs",
    "content": "//! Advanced Encryption Standard (AES) hardware accelerator\n//!\n//! This module provides support for the AES v3b hardware accelerator peripheral\n//! found on STM32WBA series microcontrollers.\n//!\n//! # Supported Cipher Modes\n//!\n//! | Mode | Padding | Auth | Use Case |\n//! |------|---------|------|----------|\n//! | ECB  | Required | No  | Keys only (not recommended for data) |\n//! | CBC  | Required | No  | File/disk encryption |\n//! | CTR  | No | No  | Streaming data, random access |\n//! | GCM  | No | Yes | **Recommended** - Modern applications |\n//! | CCM  | No | Yes | Resource-constrained devices |\n//!\n//! # Key Sizes\n//!\n//! - 128-bit (16 bytes)\n//! - 256-bit (32 bytes)\n//! - Note: 192-bit keys are NOT supported on this hardware\n//!\n//! # Examples\n//!\n//! ## Basic ECB Mode (Block Cipher)\n//!\n//! ```no_run\n//! use embassy_stm32::aes::{Aes, AesEcb, Direction};\n//! use embassy_stm32::{bind_interrupts, peripherals};\n//!\n//! bind_interrupts!(struct Irqs {\n//!     AES => embassy_stm32::aes::InterruptHandler<peripherals::AES>;\n//! });\n//!\n//! let key = [0u8; 16];  // 128-bit key\n//! let cipher = AesEcb::new(&key);\n//!\n//! let mut aes = Aes::new_blocking(p.AES, Irqs);\n//! let mut ctx = aes.start(&cipher, Direction::Encrypt);\n//!\n//! let plaintext = [0u8; 16];\n//! let mut ciphertext = [0u8; 16];\n//! aes.payload_blocking(&mut ctx, &plaintext, &mut ciphertext, true);\n//! aes.finish_blocking(ctx);\n//! ```\n//!\n//! ## CBC Mode (With IV)\n//!\n//! ```no_run\n//! use embassy_stm32::aes::{Aes, AesCbc, Direction};\n//!\n//! let key = [0u8; 16];\n//! let iv = [0u8; 16];  // Random IV, unique per message\n//! let cipher = AesCbc::new(&key, &iv);\n//!\n//! let mut ctx = aes.start(&cipher, Direction::Encrypt);\n//! // Process multiple blocks\n//! aes.payload_blocking(&mut ctx, &plaintext, &mut ciphertext, true);\n//! aes.finish_blocking(ctx);\n//! ```\n//!\n//! ## CTR Mode (Stream Cipher - No Padding)\n//!\n//! ```no_run\n//! use embassy_stm32::aes::{Aes, AesCtr, Direction};\n//!\n//! let key = [0u8; 16];\n//! let counter = [0u8; 16];  // Nonce + counter\n//! let cipher = AesCtr::new(&key, &counter);\n//!\n//! let mut ctx = aes.start(&cipher, Direction::Encrypt);\n//! // Can process any length data (no padding needed)\n//! let partial_data = [0u8; 13]; // Not block-aligned - OK for CTR!\n//! let mut output = [0u8; 13];\n//! aes.payload_blocking(&mut ctx, &partial_data, &mut output, true);\n//! aes.finish_blocking(ctx);\n//! ```\n//!\n//! ## GCM Mode (Authenticated Encryption - Recommended)\n//!\n//! ```no_run\n//! use embassy_stm32::aes::{Aes, AesGcm, Direction};\n//!\n//! let key = [0u8; 16];\n//! let iv = [0u8; 12];  // 96-bit nonce (12 bytes)\n//! let cipher = AesGcm::new(&key, &iv);\n//!\n//! let mut ctx = aes.start(&cipher, Direction::Encrypt);\n//!\n//! // Process Additional Authenticated Data (AAD) - optional\n//! let aad = b\"metadata that will be authenticated but not encrypted\";\n//! aes.aad_blocking(&mut ctx, aad, true);\n//!\n//! // Encrypt payload\n//! aes.payload_blocking(&mut ctx, &plaintext, &mut ciphertext, true);\n//!\n//! // Get authentication tag\n//! if let Ok(Some(tag)) = aes.finish_blocking(ctx) {\n//!     // Send tag with ciphertext for verification\n//! }\n//! ```\n//!\n//! # Security Best Practices\n//!\n//! ## Key Management\n//! - Use hardware RNG for key generation\n//! - Never hardcode keys in production\n//! - Consider using SAES hardware key derivation\n//! - Never reuse keys inappropriately\n//!\n//! ## IV/Nonce Requirements\n//! - **CBC**: Random, unique per message\n//! - **CTR**: Must NEVER repeat with same key (use counter)\n//! - **GCM**: 96-bit (12 bytes), unique per message\n//! - **CRITICAL**: IV reuse is catastrophic in CTR/GCM modes\n//!\n//! ## Mode Selection\n//! - **Use GCM** for new applications (provides authentication)\n//! - **Use CTR** for streaming or arbitrary-length data\n//! - **Avoid ECB** for anything except encrypting random keys\n//! - **CBC/CTR alone** don't provide authentication - consider GCM or add HMAC\n//!\n//! # Hardware Capabilities\n//!\n//! **AES v3b (STM32WBA)**:\n//! - Block size: 16 bytes (128 bits)\n//! - Key sizes: 128-bit, 256-bit\n//! - DMA support: Yes (async mode)\n//! - Interrupt support: Yes\n//! - Suspend/resume: Yes (for GCM/CCM)\n//!\n//! # Performance\n//!\n//! Hardware acceleration provides significant speed improvements over software:\n//! - ~10-20× faster than pure software implementation\n//! - Constant-time operation (side-channel resistant)\n//! - Low CPU overhead\n//!\n//! # See Also\n//!\n//! - [`saes`](crate::saes) - Secure AES with hardware key derivation\n//! - [`pka`](crate::pka) - Public Key Accelerator (ECDSA verification)\n//! - Examples: `examples/stm32wba/src/bin/aes_*.rs`\n\nuse core::future::poll_fn;\nuse core::marker::PhantomData;\nuse core::task::Poll;\n\nuse embassy_hal_internal::{Peri, PeripheralType};\nuse embassy_sync::waitqueue::AtomicWaker;\n\nuse crate::dma::ChannelAndRequest;\nuse crate::interrupt::typelevel::Interrupt;\nuse crate::mode::{Async, Blocking, Mode};\nuse crate::{interrupt, pac, peripherals, rcc};\n\nconst AES_BLOCK_SIZE: usize = 16; // 128 bits\n\nstatic AES_WAKER: AtomicWaker = AtomicWaker::new();\n\n/// AES interrupt handler.\npub struct InterruptHandler<T: Instance> {\n    _phantom: PhantomData<T>,\n}\n\nimpl<T: Instance> interrupt::typelevel::Handler<T::Interrupt> for InterruptHandler<T> {\n    unsafe fn on_interrupt() {\n        let sr = T::regs().sr().read();\n\n        // Wake on completion flag\n        if sr.ccf() {\n            // Clear all interrupt flags by writing 1s to ICR\n            T::regs().icr().write(|w| w.0 = 0xFFFF_FFFF);\n            AES_WAKER.wake();\n        }\n\n        // Clear error flags if any\n        if sr.rderr() || sr.wrerr() {\n            T::regs().icr().write(|w| w.0 = 0xFFFF_FFFF);\n        }\n    }\n}\n\n/// AES error\n#[derive(Debug, PartialEq, Eq, Clone, Copy)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub enum Error {\n    /// Invalid key size\n    KeyError,\n    /// Read error - unexpected output read during computation\n    ReadError,\n    /// Write error - unexpected input write during output phase\n    WriteError,\n    /// Invalid configuration\n    ConfigError,\n}\n\n/// AES cipher direction\n#[derive(Debug, Clone, Copy, PartialEq, Eq)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub enum Direction {\n    /// Encryption mode\n    Encrypt = 0,\n    /// Decryption mode (MODE = 2)\n    Decrypt = 2,\n}\n\n/// AES key size\n#[derive(Debug, Clone, Copy, PartialEq, Eq)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub enum KeySize {\n    /// 128-bit key\n    Bits128 = 0,\n    /// 256-bit key\n    Bits256 = 1,\n}\n\n/// This trait encapsulates all cipher-specific behavior.\npub trait Cipher<'c> {\n    /// Processing block size (always 16 bytes for AES).\n    const BLOCK_SIZE: usize = AES_BLOCK_SIZE;\n\n    /// Indicates whether the cipher requires the application to provide padding.\n    const REQUIRES_PADDING: bool = false;\n\n    /// Returns the symmetric key.\n    fn key(&self) -> &[u8];\n\n    /// Returns the initialization vector.\n    fn iv(&self) -> &[u8];\n\n    /// Returns the key size.\n    fn key_size(&self) -> KeySize {\n        match self.key().len() {\n            16 => KeySize::Bits128,\n            32 => KeySize::Bits256,\n            _ => panic!(\"Invalid key size\"),\n        }\n    }\n\n    /// Sets the cipher mode (CHMOD field).\n    fn set_mode(&self, p: pac::aes::Aes);\n\n    /// Returns the data type setting for this cipher mode.\n    /// - 0 = NO_SWAP (32-bit words, no swapping) - Default for all modes\n    ///\n    /// Note: The ST HAL uses different DATATYPE values (BYTE_SWAP for CBC, BIT_SWAP for CTR)\n    /// with pre-swapped test vectors. This driver uses NO_SWAP consistently with big-endian\n    /// byte conversion (from_be_bytes/to_be_bytes) for direct NIST test vector compatibility.\n    fn datatype(&self) -> u8 {\n        0 // NO_SWAP for all modes - handles NIST vectors correctly with from_be_bytes/to_be_bytes\n    }\n\n    /// Returns the raw CHMOD field value for this cipher mode.\n    /// Used by peripheral drivers (e.g. SAES) that need the raw mode bits without a PAC type.\n    fn chmod_bits(&self) -> u8 {\n        0 // ECB default\n    }\n\n    /// Performs any key preparation within the processor, if necessary.\n    fn prepare_key(&self, _p: pac::aes::Aes, _dir: Direction) {}\n\n    /// Performs any cipher-specific initialization.\n    fn init_phase_blocking<T: Instance, M: Mode>(&self, _p: pac::aes::Aes, _aes: &Aes<T, M>) {}\n\n    /// Performs any cipher-specific initialization (async).\n    async fn init_phase<T: Instance>(&self, _p: pac::aes::Aes, _aes: &mut Aes<'_, T, Async>) {}\n\n    /// Called prior to processing the last data block for cipher-specific operations.\n    fn pre_final(&self, _p: pac::aes::Aes, _dir: Direction, _padding_len: usize) -> [u32; 4] {\n        [0; 4]\n    }\n\n    /// Called after processing the last data block for cipher-specific operations.\n    fn post_final_blocking<T: Instance, M: Mode>(\n        &self,\n        _p: pac::aes::Aes,\n        _aes: &Aes<T, M>,\n        _dir: Direction,\n        _int_data: &mut [u8; AES_BLOCK_SIZE],\n        _temp1: [u32; 4],\n        _padding_mask: [u8; 16],\n    ) {\n    }\n\n    /// Called after processing the last data block for cipher-specific operations (async).\n    async fn post_final<T: Instance>(\n        &self,\n        _p: pac::aes::Aes,\n        _aes: &mut Aes<'_, T, Async>,\n        _dir: Direction,\n        _int_data: &mut [u8; AES_BLOCK_SIZE],\n        _temp1: [u32; 4],\n        _padding_mask: [u8; 16],\n    ) {\n    }\n\n    /// Returns the AAD header block as required by the cipher (for authenticated modes).\n    fn get_header_block(&self) -> &[u8] {\n        [0; 0].as_slice()\n    }\n\n    /// Indicates whether this cipher mode uses GCM/CCM phases (init, header, payload, final).\n    fn uses_gcm_phases(&self) -> bool {\n        false\n    }\n\n    /// Indicates whether this is CCM mode (which has different final phase handling).\n    /// CCM doesn't use a length block in the final phase, unlike GCM.\n    fn is_ccm_mode(&self) -> bool {\n        false\n    }\n\n    /// Returns the pre-computed B0 block for CCM mode (None for other modes).\n    fn ccm_b0(&self) -> Option<&[u8; 16]> {\n        None\n    }\n\n    /// Returns the formatted AAD length prefix for CCM mode.\n    /// CCM requires AAD to be prefixed with its length encoding.\n    fn ccm_format_aad_header(&self, aad_len: usize) -> ([u8; 10], usize) {\n        // Default implementation - returns empty for non-CCM modes\n        // Format: if aad_len < 2^16-2^8: 2 bytes\n        //         if aad_len < 2^32: 0xFFFE + 4 bytes\n        //         else: 0xFFFF + 8 bytes\n        let mut header = [0u8; 10];\n        let len = if aad_len == 0 {\n            0\n        } else if aad_len < (1 << 16) - (1 << 8) {\n            header[0] = (aad_len >> 8) as u8;\n            header[1] = aad_len as u8;\n            2\n        } else if aad_len < (1u64 << 32) as usize {\n            header[0] = 0xFF;\n            header[1] = 0xFE;\n            header[2..6].copy_from_slice(&(aad_len as u32).to_be_bytes());\n            6\n        } else {\n            header[0] = 0xFF;\n            header[1] = 0xFF;\n            header[2..10].copy_from_slice(&(aad_len as u64).to_be_bytes());\n            10\n        };\n        (header, len)\n    }\n}\n\n/// This trait enables restriction of ciphers to specific key sizes.\npub trait CipherSized {}\n\n/// This trait enables restriction of initialization vectors to sizes compatible with a cipher mode.\npub trait IVSized {}\n\n/// This trait enables restriction of a header phase to authenticated ciphers only.\npub trait CipherAuthenticated<const TAG_SIZE: usize> {\n    /// Defines the authentication tag size.\n    const TAG_SIZE: usize = TAG_SIZE;\n}\n\n/// AES-ECB Cipher Mode\npub struct AesEcb<'c, const KEY_SIZE: usize> {\n    iv: &'c [u8; 0],\n    key: &'c [u8; KEY_SIZE],\n}\n\nimpl<'c, const KEY_SIZE: usize> AesEcb<'c, KEY_SIZE> {\n    /// Constructs a new AES-ECB cipher for a cryptographic operation.\n    pub fn new(key: &'c [u8; KEY_SIZE]) -> Self {\n        Self { key, iv: &[0; 0] }\n    }\n}\n\nimpl<'c, const KEY_SIZE: usize> Cipher<'c> for AesEcb<'c, KEY_SIZE> {\n    const REQUIRES_PADDING: bool = true;\n\n    fn key(&self) -> &[u8] {\n        self.key\n    }\n\n    fn iv(&self) -> &[u8] {\n        self.iv\n    }\n\n    fn set_mode(&self, p: pac::aes::Aes) {\n        // ECB mode: CHMOD = 0\n        p.cr().modify(|w| {\n            w.set_chmod(pac::aes::vals::Chmod::from_bits(0));\n        });\n    }\n\n    fn chmod_bits(&self) -> u8 {\n        0\n    }\n\n    fn prepare_key(&self, p: pac::aes::Aes, dir: Direction) {\n        // For ECB decryption, need to prepare key (RM Section 26.4.6 steps 2-7)\n        if dir == Direction::Decrypt {\n            // Step 2: Set MODE to key derivation (MODE[1:0] = 0x1)\n            p.cr().modify(|w| w.set_mode(pac::aes::vals::Mode::from_bits(1)));\n            // Step 5: Enable AES - peripheral starts key preparation\n            p.cr().modify(|w| w.set_en(true));\n            // Step 6: Wait for CCF flag\n            while !p.sr().read().ccf() {}\n            // Step 7: Clear CCF flag - AES disables automatically\n            p.icr().write(|w| w.0 = 0xFFFF_FFFF);\n            // Note: Peripheral is now disabled, caller will reconfigure and re-enable\n        }\n    }\n}\n\nimpl<'c> CipherSized for AesEcb<'c, { 128 / 8 }> {}\nimpl<'c> CipherSized for AesEcb<'c, { 256 / 8 }> {}\nimpl<'c, const KEY_SIZE: usize> IVSized for AesEcb<'c, KEY_SIZE> {}\n\n/// AES-CBC Cipher Mode\npub struct AesCbc<'c, const KEY_SIZE: usize> {\n    iv: &'c [u8; 16],\n    key: &'c [u8; KEY_SIZE],\n}\n\nimpl<'c, const KEY_SIZE: usize> AesCbc<'c, KEY_SIZE> {\n    /// Constructs a new AES-CBC cipher for a cryptographic operation.\n    pub fn new(key: &'c [u8; KEY_SIZE], iv: &'c [u8; 16]) -> Self {\n        Self { key, iv }\n    }\n}\n\nimpl<'c, const KEY_SIZE: usize> Cipher<'c> for AesCbc<'c, KEY_SIZE> {\n    const REQUIRES_PADDING: bool = true;\n\n    fn key(&self) -> &[u8] {\n        self.key\n    }\n\n    fn iv(&self) -> &[u8] {\n        self.iv\n    }\n\n    fn set_mode(&self, p: pac::aes::Aes) {\n        // CBC mode: CHMOD = 1\n        p.cr().modify(|w| {\n            w.set_chmod(pac::aes::vals::Chmod::from_bits(1));\n        });\n    }\n\n    fn chmod_bits(&self) -> u8 {\n        1\n    }\n\n    // Uses default datatype() = 0 (NO_SWAP) for NIST vector compatibility\n\n    fn prepare_key(&self, p: pac::aes::Aes, dir: Direction) {\n        // For CBC/ECB decryption, need to prepare key (RM Section 26.4.6 steps 2-7)\n        if dir == Direction::Decrypt {\n            // Step 2: Set MODE to key derivation (MODE[1:0] = 0x1)\n            p.cr().modify(|w| w.set_mode(pac::aes::vals::Mode::from_bits(1)));\n            // Step 5: Enable AES - peripheral starts key preparation\n            p.cr().modify(|w| w.set_en(true));\n            // Step 6: Wait for CCF flag\n            while !p.sr().read().ccf() {}\n            // Step 7: Clear CCF flag - AES disables automatically\n            p.icr().write(|w| w.0 = 0xFFFF_FFFF);\n            // Note: Peripheral is now disabled, caller will reconfigure and re-enable\n        }\n    }\n}\n\nimpl<'c> CipherSized for AesCbc<'c, { 128 / 8 }> {}\nimpl<'c> CipherSized for AesCbc<'c, { 256 / 8 }> {}\nimpl<'c, const KEY_SIZE: usize> IVSized for AesCbc<'c, KEY_SIZE> {}\n\n/// AES-CTR Cipher Mode\npub struct AesCtr<'c, const KEY_SIZE: usize> {\n    iv: &'c [u8; 16],\n    key: &'c [u8; KEY_SIZE],\n}\n\nimpl<'c, const KEY_SIZE: usize> AesCtr<'c, KEY_SIZE> {\n    /// Constructs a new AES-CTR cipher for a cryptographic operation.\n    pub fn new(key: &'c [u8; KEY_SIZE], iv: &'c [u8; 16]) -> Self {\n        Self { key, iv }\n    }\n}\n\nimpl<'c, const KEY_SIZE: usize> Cipher<'c> for AesCtr<'c, KEY_SIZE> {\n    const REQUIRES_PADDING: bool = false; // Stream cipher, no padding needed\n\n    fn key(&self) -> &[u8] {\n        self.key\n    }\n\n    fn iv(&self) -> &[u8] {\n        self.iv\n    }\n\n    fn set_mode(&self, p: pac::aes::Aes) {\n        // CTR mode: CHMOD = 2\n        p.cr().modify(|w| {\n            w.set_chmod(pac::aes::vals::Chmod::from_bits(2));\n        });\n    }\n\n    fn chmod_bits(&self) -> u8 {\n        2\n    }\n\n    // Uses default datatype() = 0 (NO_SWAP) for NIST vector compatibility\n}\n\nimpl<'c> CipherSized for AesCtr<'c, { 128 / 8 }> {}\nimpl<'c> CipherSized for AesCtr<'c, { 256 / 8 }> {}\nimpl<'c, const KEY_SIZE: usize> IVSized for AesCtr<'c, KEY_SIZE> {}\n\n/// AES-GCM Cipher Mode\npub struct AesGcm<'c, const KEY_SIZE: usize> {\n    key: &'c [u8; KEY_SIZE],\n    iv: [u8; 16],\n}\n\nimpl<'c, const KEY_SIZE: usize> AesGcm<'c, KEY_SIZE> {\n    /// Constructs a new AES-GCM cipher for a cryptographic operation.\n    /// The IV should be 12 bytes long (96 bits).\n    pub fn new(key: &'c [u8; KEY_SIZE], iv: &'c [u8; 12]) -> Self {\n        let mut iv_full = [0u8; 16];\n        iv_full[..12].copy_from_slice(iv);\n        iv_full[15] = 2; // Initial counter value\n        Self { key, iv: iv_full }\n    }\n}\n\nimpl<'c, const KEY_SIZE: usize> Cipher<'c> for AesGcm<'c, KEY_SIZE> {\n    const REQUIRES_PADDING: bool = false;\n\n    fn key(&self) -> &[u8] {\n        self.key\n    }\n\n    fn iv(&self) -> &[u8] {\n        &self.iv\n    }\n\n    fn set_mode(&self, p: pac::aes::Aes) {\n        // GCM mode: CHMOD = 3\n        p.cr().modify(|w| {\n            w.set_chmod(pac::aes::vals::Chmod::from_bits(3));\n        });\n    }\n\n    fn chmod_bits(&self) -> u8 {\n        3\n    }\n\n    fn init_phase_blocking<T: Instance, M: Mode>(&self, p: pac::aes::Aes, _aes: &Aes<T, M>) {\n        // GCMPH was already set to 0 in start() before key loading\n        // Enable EN to start hash key (H) calculation (RM step 6)\n        p.cr().modify(|w| w.set_en(true));\n\n        // Wait for CCF (hash key calculation complete - RM step 7)\n        while !p.sr().read().ccf() {}\n\n        // Clear completion flag (RM step 8)\n        p.icr().write(|w| w.0 = 0xFFFF_FFFF);\n\n        // ST HAL does NOT disable EN after init phase\n        // Leave peripheral enabled for next phase transition\n    }\n\n    async fn init_phase<T: Instance>(&self, p: pac::aes::Aes, _aes: &mut Aes<'_, T, Async>) {\n        // Set GCM phase to init (GCMPH = 0)\n        p.cr().modify(|w| w.set_gcmph(pac::aes::vals::Gcmph::from_bits(0)));\n        p.cr().modify(|w| w.set_en(true));\n        // Wait for completion\n        poll_fn(|cx| {\n            if p.sr().read().ccf() {\n                Poll::Ready(())\n            } else {\n                AES_WAKER.register(cx.waker());\n                // Re-check after registering waker\n                if p.sr().read().ccf() {\n                    Poll::Ready(())\n                } else {\n                    Poll::Pending\n                }\n            }\n        })\n        .await;\n    }\n\n    fn pre_final(&self, p: pac::aes::Aes, _dir: Direction, padding_len: usize) -> [u32; 4] {\n        // Set number of padding bytes for partial block\n        if padding_len > 0 {\n            p.cr().modify(|w| w.set_npblb(padding_len as u8));\n        }\n        [0; 4]\n    }\n\n    fn uses_gcm_phases(&self) -> bool {\n        true\n    }\n}\n\nimpl<'c> CipherSized for AesGcm<'c, { 128 / 8 }> {}\nimpl<'c> CipherSized for AesGcm<'c, { 256 / 8 }> {}\nimpl<'c, const KEY_SIZE: usize> IVSized for AesGcm<'c, KEY_SIZE> {}\nimpl<'c, const KEY_SIZE: usize> CipherAuthenticated<16> for AesGcm<'c, KEY_SIZE> {}\n\n/// AES-GMAC Cipher Mode (Galois Message Authentication Code)\n///\n/// GMAC provides message authentication without encryption. Use this when you need\n/// to authenticate data integrity without confidentiality - the data remains in\n/// plaintext but any tampering will be detected.\n///\n/// # Use Cases\n/// - Authenticating packet headers in network protocols\n/// - Verifying integrity of publicly-readable metadata\n/// - Any scenario requiring authentication without encryption\n///\n/// # Example\n/// ```no_run\n/// let key = [0u8; 16];\n/// let iv = [0u8; 12];  // 96-bit nonce\n/// let cipher = AesGmac::new(&key, &iv);\n///\n/// let mut ctx = aes.start(&cipher, Direction::Encrypt);\n/// aes.aad_blocking(&mut ctx, &header_data, true);\n/// if let Ok(Some(tag)) = aes.finish_blocking(ctx) {\n///     // Use tag to verify integrity\n/// }\n/// ```\npub struct AesGmac<'c, const KEY_SIZE: usize> {\n    key: &'c [u8; KEY_SIZE],\n    iv: [u8; 16],\n}\n\nimpl<'c, const KEY_SIZE: usize> AesGmac<'c, KEY_SIZE> {\n    /// Constructs a new AES-GMAC cipher for message authentication.\n    /// The IV should be 12 bytes long (96 bits) and unique per message.\n    pub fn new(key: &'c [u8; KEY_SIZE], iv: &'c [u8; 12]) -> Self {\n        let mut iv_full = [0u8; 16];\n        iv_full[..12].copy_from_slice(iv);\n        iv_full[15] = 2; // Initial counter value (same as GCM)\n        Self { key, iv: iv_full }\n    }\n}\n\nimpl<'c, const KEY_SIZE: usize> Cipher<'c> for AesGmac<'c, KEY_SIZE> {\n    const REQUIRES_PADDING: bool = false;\n\n    fn key(&self) -> &[u8] {\n        self.key\n    }\n\n    fn iv(&self) -> &[u8] {\n        &self.iv\n    }\n\n    fn set_mode(&self, p: pac::aes::Aes) {\n        // GMAC uses the same hardware mode as GCM: CHMOD = 3\n        p.cr().modify(|w| {\n            w.set_chmod(pac::aes::vals::Chmod::from_bits(3));\n        });\n    }\n\n    fn chmod_bits(&self) -> u8 {\n        3\n    }\n\n    fn init_phase_blocking<T: Instance, M: Mode>(&self, p: pac::aes::Aes, _aes: &Aes<T, M>) {\n        // Same init phase as GCM - compute hash key H\n        p.cr().modify(|w| w.set_en(true));\n        while !p.sr().read().ccf() {}\n        p.icr().write(|w| w.0 = 0xFFFF_FFFF);\n    }\n\n    async fn init_phase<T: Instance>(&self, p: pac::aes::Aes, _aes: &mut Aes<'_, T, Async>) {\n        p.cr().modify(|w| w.set_gcmph(pac::aes::vals::Gcmph::from_bits(0)));\n        p.cr().modify(|w| w.set_en(true));\n        poll_fn(|cx| {\n            if p.sr().read().ccf() {\n                Poll::Ready(())\n            } else {\n                AES_WAKER.register(cx.waker());\n                if p.sr().read().ccf() {\n                    Poll::Ready(())\n                } else {\n                    Poll::Pending\n                }\n            }\n        })\n        .await;\n    }\n\n    fn uses_gcm_phases(&self) -> bool {\n        true\n    }\n}\n\nimpl<'c> CipherSized for AesGmac<'c, { 128 / 8 }> {}\nimpl<'c> CipherSized for AesGmac<'c, { 256 / 8 }> {}\nimpl<'c, const KEY_SIZE: usize> IVSized for AesGmac<'c, KEY_SIZE> {}\nimpl<'c, const KEY_SIZE: usize> CipherAuthenticated<16> for AesGmac<'c, KEY_SIZE> {}\n\n/// AES-CCM Cipher Mode (Counter with CBC-MAC)\npub struct AesCcm<'c, const KEY_SIZE: usize, const IV_SIZE: usize, const TAG_SIZE: usize> {\n    key: &'c [u8; KEY_SIZE],\n    iv: [u8; 16],\n    #[allow(dead_code)] // Stored for potential future use in verification\n    aad_len: usize,\n    #[allow(dead_code)] // Stored for potential future use in verification\n    payload_len: usize,\n}\n\nimpl<'c, const KEY_SIZE: usize, const IV_SIZE: usize, const TAG_SIZE: usize> AesCcm<'c, KEY_SIZE, IV_SIZE, TAG_SIZE> {\n    /// Constructs a new AES-CCM cipher for a cryptographic operation.\n    /// - `key`: The encryption key (16 or 32 bytes)\n    /// - `iv`: The nonce/IV (7-13 bytes recommended, typically 12 bytes)\n    /// - `aad_len`: Length of additional authenticated data (known in advance)\n    /// - `payload_len`: Length of payload data (known in advance)\n    ///\n    /// Note: CCM requires knowing the data lengths in advance.\n    pub fn new(key: &'c [u8; KEY_SIZE], iv: &'c [u8; IV_SIZE], aad_len: usize, payload_len: usize) -> Self {\n        // Validate IV size (7-13 bytes per NIST SP 800-38C)\n        assert!(IV_SIZE >= 7 && IV_SIZE <= 13, \"CCM IV must be 7-13 bytes\");\n        // Validate tag size (4, 6, 8, 10, 12, 14, or 16 bytes)\n        assert!(\n            TAG_SIZE >= 4 && TAG_SIZE <= 16 && TAG_SIZE % 2 == 0,\n            \"CCM tag must be 4-16 bytes and even\"\n        );\n\n        let mut iv_full = [0u8; 16];\n        // Format B0 block for CCM\n        let l = 15 - IV_SIZE; // l = size of length field\n        iv_full[0] = ((l - 1) as u8) | ((((TAG_SIZE - 2) / 2) as u8) << 3);\n        if aad_len > 0 {\n            iv_full[0] |= 0x40; // Set Adata flag\n        }\n        iv_full[1..1 + IV_SIZE].copy_from_slice(iv);\n\n        // Encode payload length in the last l bytes (as big-endian)\n        // Use u64 to ensure consistent handling on 32-bit and 64-bit platforms\n        let payload_bytes = (payload_len as u64).to_be_bytes();\n        let offset = 16 - l;\n        iv_full[offset..].copy_from_slice(&payload_bytes[8 - l..]);\n\n        Self {\n            key,\n            iv: iv_full,\n            aad_len,\n            payload_len,\n        }\n    }\n}\n\nimpl<'c, const KEY_SIZE: usize, const IV_SIZE: usize, const TAG_SIZE: usize> Cipher<'c>\n    for AesCcm<'c, KEY_SIZE, IV_SIZE, TAG_SIZE>\n{\n    const REQUIRES_PADDING: bool = false;\n\n    fn key(&self) -> &[u8] {\n        self.key\n    }\n\n    fn iv(&self) -> &[u8] {\n        &self.iv\n    }\n\n    fn set_mode(&self, p: pac::aes::Aes) {\n        // CCM mode: CHMOD = 4 (0b100) per RM0493\n        p.cr().modify(|w| {\n            w.set_chmod(pac::aes::vals::Chmod::from_bits(4));\n        });\n    }\n\n    fn chmod_bits(&self) -> u8 {\n        4\n    }\n\n    fn init_phase_blocking<T: Instance, M: Mode>(&self, p: pac::aes::Aes, _aes: &Aes<T, M>) {\n        // GCMPH was already set to 0 in start() before key loading\n        // Enable EN to start initialization\n        p.cr().modify(|w| w.set_en(true));\n        // Wait for CCF\n        while !p.sr().read().ccf() {}\n        // Clear completion flag\n        p.icr().write(|w| w.0 = 0xFFFF_FFFF);\n        // ST HAL does NOT disable EN after init phase\n        // Leave peripheral enabled for next phase transition\n    }\n\n    async fn init_phase<T: Instance>(&self, p: pac::aes::Aes, _aes: &mut Aes<'_, T, Async>) {\n        // Set CCM phase to init (GCMPH = 0)\n        p.cr().modify(|w| w.set_gcmph(pac::aes::vals::Gcmph::from_bits(0)));\n        p.cr().modify(|w| w.set_en(true));\n        // Wait for completion\n        poll_fn(|cx| {\n            if p.sr().read().ccf() {\n                Poll::Ready(())\n            } else {\n                AES_WAKER.register(cx.waker());\n                // Re-check after registering waker\n                if p.sr().read().ccf() {\n                    Poll::Ready(())\n                } else {\n                    Poll::Pending\n                }\n            }\n        })\n        .await;\n    }\n\n    fn pre_final(&self, p: pac::aes::Aes, _dir: Direction, padding_len: usize) -> [u32; 4] {\n        // Set number of padding bytes for partial block\n        if padding_len > 0 {\n            p.cr().modify(|w| w.set_npblb(padding_len as u8));\n        }\n        [0; 4]\n    }\n\n    fn uses_gcm_phases(&self) -> bool {\n        true\n    }\n\n    fn is_ccm_mode(&self) -> bool {\n        true\n    }\n\n    fn ccm_b0(&self) -> Option<&[u8; 16]> {\n        Some(&self.iv)\n    }\n}\n\nimpl<'c, const IV_SIZE: usize, const TAG_SIZE: usize> CipherSized for AesCcm<'c, { 128 / 8 }, IV_SIZE, TAG_SIZE> {}\nimpl<'c, const IV_SIZE: usize, const TAG_SIZE: usize> CipherSized for AesCcm<'c, { 256 / 8 }, IV_SIZE, TAG_SIZE> {}\nimpl<'c, const KEY_SIZE: usize, const IV_SIZE: usize, const TAG_SIZE: usize> IVSized\n    for AesCcm<'c, KEY_SIZE, IV_SIZE, TAG_SIZE>\n{\n}\nimpl<'c, const KEY_SIZE: usize, const IV_SIZE: usize, const TAG_SIZE: usize> CipherAuthenticated<TAG_SIZE>\n    for AesCcm<'c, KEY_SIZE, IV_SIZE, TAG_SIZE>\n{\n}\n\n/// Stores the state of the AES peripheral for suspending/resuming operations.\n#[derive(Clone)]\npub struct Context<'c, C: Cipher<'c>> {\n    /// The cipher configuration\n    pub cipher: &'c C,\n    /// Encryption or decryption direction\n    pub dir: Direction,\n    /// Whether the last block has been processed\n    pub last_block_processed: bool,\n    /// Whether this is a GCM/CCM authenticated mode\n    pub is_gcm_ccm: bool,\n    // For GCM/CCM authenticated modes\n    /// Whether the header (AAD) has been processed\n    pub header_processed: bool,\n    /// Total length of additional authenticated data\n    pub header_len: u64,\n    /// Total length of payload data\n    pub payload_len: u64,\n    /// Buffer for partial AAD blocks\n    pub aad_buffer: [u8; 16],\n    /// Number of bytes in AAD buffer\n    pub aad_buffer_len: usize,\n    // Hardware state\n    /// Control register state\n    pub cr: u32,\n    /// Initialization vector state\n    pub iv: [u32; 4],\n    /// Suspend registers for GCM/CCM\n    pub suspr: [u32; 8],\n}\n\n/// AES driver.\npub struct Aes<'d, T: Instance, M: Mode> {\n    _peripheral: Peri<'d, T>,\n    _phantom: PhantomData<M>,\n    #[allow(dead_code)] // Reserved for future async/DMA implementation\n    dma_in: Option<ChannelAndRequest<'d>>,\n    #[allow(dead_code)] // Reserved for future async/DMA implementation\n    dma_out: Option<ChannelAndRequest<'d>>,\n}\n\nimpl<'d, T: Instance> Aes<'d, T, Blocking> {\n    /// Instantiates, resets, and enables the AES peripheral.\n    pub fn new_blocking(\n        peripheral: Peri<'d, T>,\n        _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd,\n    ) -> Self {\n        rcc::enable_and_reset::<T>();\n        let instance = Self {\n            _peripheral: peripheral,\n            _phantom: PhantomData,\n            dma_in: None,\n            dma_out: None,\n        };\n\n        T::Interrupt::unpend();\n        unsafe { T::Interrupt::enable() };\n\n        instance\n    }\n}\n\nimpl<'d, T: Instance> Aes<'d, T, Async> {\n    /// Instantiates, resets, and enables the AES peripheral with DMA support.\n    pub fn new<D1: DmaIn<T>, D2: DmaOut<T>>(\n        peripheral: Peri<'d, T>,\n        dma_in: Peri<'d, D1>,\n        dma_out: Peri<'d, D2>,\n        _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>>\n        + interrupt::typelevel::Binding<D1::Interrupt, crate::dma::InterruptHandler<D1>>\n        + interrupt::typelevel::Binding<D2::Interrupt, crate::dma::InterruptHandler<D2>>\n        + 'd,\n    ) -> Self {\n        rcc::enable_and_reset::<T>();\n\n        let instance = Self {\n            _peripheral: peripheral,\n            _phantom: PhantomData,\n            dma_in: new_dma!(dma_in, _irq),\n            dma_out: new_dma!(dma_out, _irq),\n        };\n\n        T::Interrupt::unpend();\n        unsafe { T::Interrupt::enable() };\n\n        instance\n    }\n}\n\nimpl<'d, T: Instance, M: Mode> Aes<'d, T, M> {\n    /// Starts a new cipher operation and returns the context.\n    pub fn start<'c, C>(&mut self, cipher: &'c C, dir: Direction) -> Context<'c, C>\n    where\n        C: Cipher<'c> + CipherSized + IVSized,\n    {\n        let p = T::regs();\n\n        // Disable the peripheral\n        p.cr().modify(|w| w.set_en(false));\n\n        // Configure data type based on cipher mode (NO_SWAP, BYTE_SWAP, or BIT_SWAP)\n        p.cr()\n            .modify(|w| w.set_datatype(pac::aes::vals::Datatype::from_bits(cipher.datatype())));\n\n        // Configure key size (false = 128-bit, true = 256-bit)\n        let keysize = cipher.key_size();\n        p.cr().modify(|w| w.set_keysize(keysize == KeySize::Bits256));\n\n        // Set direction\n        p.cr()\n            .modify(|w| w.set_mode(pac::aes::vals::Mode::from_bits(dir as u8)));\n\n        // Set cipher mode\n        cipher.set_mode(p);\n\n        // Check if this is an authenticated mode that uses GCM/CCM phases\n        let is_gcm_ccm = cipher.uses_gcm_phases();\n\n        // For GCM/CCM, set GCMPH to init BEFORE loading key (per RM step 2)\n        if is_gcm_ccm {\n            p.cr().modify(|w| w.set_gcmph(pac::aes::vals::Gcmph::from_bits(0)));\n        }\n\n        // For ECB/CBC decryption, need key preparation first\n        let needs_key_prep = dir == Direction::Decrypt && !is_gcm_ccm && cipher.key().len() > 0;\n\n        if is_gcm_ccm {\n            // GCM/CCM mode (RM 26.4.8): Load key first, then IV\n            self.load_key(cipher.key());\n            if !cipher.iv().is_empty() {\n                self.load_iv(cipher.iv());\n            }\n        } else if needs_key_prep {\n            // ECB/CBC decryption (RM 26.4.6): Key prep, then IV\n            self.load_key(cipher.key());\n            cipher.prepare_key(p, dir);\n\n            // Step 8: Select cipher mode and decryption mode (keep other params)\n            p.cr()\n                .modify(|w| w.set_mode(pac::aes::vals::Mode::from_bits(dir as u8)));\n            cipher.set_mode(p); // Set CHMOD\n\n            // Step 9: Write IV (for CBC decryption, AFTER key preparation)\n            if !cipher.iv().is_empty() {\n                self.load_iv(cipher.iv());\n            }\n        } else {\n            // ECB/CBC/CTR encryption (RM 26.4.5): IV first, then key\n            if !cipher.iv().is_empty() {\n                self.load_iv(cipher.iv());\n            }\n            self.load_key(cipher.key());\n        }\n\n        // Perform init phase for GCM/CCM modes (RM step 6-8)\n        // This calculates the hash key H needed for GCM authentication\n        if is_gcm_ccm {\n            cipher.init_phase_blocking(p, self);\n        } else {\n            // For non-GCM/CCM modes, just enable the peripheral\n            p.cr().modify(|w| w.set_en(true));\n        }\n\n        // Create context (peripheral is now enabled, safe to read registers)\n        Context {\n            cipher,\n            dir,\n            last_block_processed: false,\n            is_gcm_ccm,\n            header_processed: false,\n            header_len: 0,\n            payload_len: 0,\n            aad_buffer: [0; 16],\n            aad_buffer_len: 0,\n            cr: p.cr().read().0,\n            iv: [p.ivr(0).read(), p.ivr(1).read(), p.ivr(2).read(), p.ivr(3).read()],\n            suspr: [0; 8],\n        }\n    }\n\n    /// Process authenticated additional data (AAD) for GCM/CCM modes.\n    /// Must be called after `start` and before `payload_blocking`.\n    /// Set `last` to true for the final AAD block.\n    pub fn aad_blocking<'c, C>(&mut self, ctx: &mut Context<'c, C>, aad: &[u8], last: bool) -> Result<(), Error>\n    where\n        C: Cipher<'c> + CipherAuthenticated<16>,\n    {\n        let p = T::regs();\n\n        if ctx.header_processed && last {\n            return Ok(());\n        }\n\n        // Set GCM phase to header (GCMPH = 1)\n        p.cr().modify(|w| w.set_gcmph(pac::aes::vals::Gcmph::from_bits(1)));\n        // Enable the peripheral for header phase\n        p.cr().modify(|w| w.set_en(true));\n\n        let mut aad_remaining = aad.len();\n        let mut aad_index = 0;\n\n        // Process buffered AAD first if any\n        if ctx.aad_buffer_len > 0 {\n            let space_available = 16 - ctx.aad_buffer_len;\n            let to_copy = core::cmp::min(space_available, aad_remaining);\n            ctx.aad_buffer[ctx.aad_buffer_len..ctx.aad_buffer_len + to_copy].copy_from_slice(&aad[..to_copy]);\n            ctx.aad_buffer_len += to_copy;\n            aad_index += to_copy;\n            aad_remaining -= to_copy;\n\n            if ctx.aad_buffer_len == 16 {\n                self.write_block_blocking(&ctx.aad_buffer)?;\n                // Wait for CCF (block processed) - no read in header phase\n                while !p.sr().read().ccf() {}\n                p.icr().write(|w| w.0 = 0xFFFF_FFFF);\n                ctx.header_len += 16;\n                ctx.aad_buffer_len = 0;\n            }\n        }\n\n        // Process complete blocks\n        while aad_remaining >= 16 {\n            self.write_block_blocking(&aad[aad_index..aad_index + 16])?;\n            // Wait for CCF (block processed) - no read in header phase\n            while !p.sr().read().ccf() {}\n            p.icr().write(|w| w.0 = 0xFFFF_FFFF);\n            ctx.header_len += 16;\n            aad_index += 16;\n            aad_remaining -= 16;\n        }\n\n        // Buffer any remaining partial block\n        if aad_remaining > 0 {\n            ctx.aad_buffer[..aad_remaining].copy_from_slice(&aad[aad_index..aad_index + aad_remaining]);\n            ctx.aad_buffer_len = aad_remaining;\n        }\n\n        // If this is the last AAD block, pad and process\n        if last {\n            if ctx.aad_buffer_len > 0 {\n                // Pad with zeros (per GCM spec, AAD is zero-padded to 16-byte boundary)\n                for i in ctx.aad_buffer_len..16 {\n                    ctx.aad_buffer[i] = 0;\n                }\n                // Note: Do NOT set NPBLB for header phase - NPBLB is only for payload phase\n                self.write_block_blocking(&ctx.aad_buffer)?;\n                // Wait for CCF (block processed)\n                while !p.sr().read().ccf() {}\n                p.icr().write(|w| w.0 = 0xFFFF_FFFF);\n                ctx.header_len += ctx.aad_buffer_len as u64;\n                ctx.aad_buffer_len = 0;\n            }\n            ctx.header_processed = true;\n        }\n\n        Ok(())\n    }\n\n    /// Process payload data in blocking mode.\n    ///\n    /// Set `last` to true for the final block. Intermediate chunks (`last=false`)\n    /// must be block-aligned (16 bytes). Only the final chunk can be a partial block.\n    pub fn payload_blocking<'c, C>(\n        &mut self,\n        ctx: &mut Context<'c, C>,\n        input: &[u8],\n        output: &mut [u8],\n        last: bool,\n    ) -> Result<(), Error>\n    where\n        C: Cipher<'c>,\n    {\n        let p = T::regs();\n\n        if output.len() < input.len() {\n            return Err(Error::ConfigError);\n        }\n\n        // For GCM/CCM, switch to payload phase\n        if ctx.is_gcm_ccm {\n            let header_was_skipped = !ctx.header_processed;\n            if header_was_skipped {\n                // No AAD provided, mark header as done\n                ctx.header_processed = true;\n            }\n            // Set GCM phase to payload (per RM step 11a)\n            // ST HAL shows: just change GCMPH, DON'T disable EN between phases\n            p.cr().modify(|w| w.set_gcmph(pac::aes::vals::Gcmph::from_bits(2)));\n            // Reset NPBLB to 0 (per ST HAL)\n            p.cr().modify(|w| w.set_npblb(0));\n            // Only enable if header was skipped (per RM step 11b)\n            if header_was_skipped {\n                p.cr().modify(|w| w.set_en(true));\n            }\n            // If header was processed, EN is already enabled - don't touch it\n        }\n\n        let block_size = C::BLOCK_SIZE;\n        let mut processed = 0;\n\n        // Ensure proper block alignment for intermediate chunks (all modes).\n        // Only the final chunk (last=true) can be partial. This applies to all modes\n        // including CTR, as keystream buffering is not implemented.\n        if !last && input.len() % block_size != 0 {\n            return Err(Error::ConfigError);\n        }\n\n        // Process complete blocks\n        let complete_blocks = if last {\n            input.len() / block_size\n        } else {\n            input.len() / block_size\n        };\n\n        for _ in 0..complete_blocks {\n            let block = &input[processed..processed + block_size];\n            let out_block = &mut output[processed..processed + block_size];\n            self.write_block_blocking(block)?;\n            self.read_block_blocking(out_block)?;\n            processed += block_size;\n            ctx.payload_len += block_size as u64;\n        }\n\n        // Handle partial block if last\n        if last && processed < input.len() {\n            if C::REQUIRES_PADDING {\n                return Err(Error::ConfigError); // Padding modes don't support partial blocks\n            }\n\n            let remaining = input.len() - processed;\n            let mut partial_block = [0u8; 16];\n            partial_block[..remaining].copy_from_slice(&input[processed..]);\n\n            // Set NPBLB (Number of Padding Bytes in Last Block)\n            // Per ST HAL:\n            // - GCM: NPBLB is set for both encryption and decryption\n            // - CCM: NPBLB is ONLY set for decryption (NOT for encryption)\n            // NPBLB = 16 - valid_bytes, e.g., for 13 valid bytes, NPBLB = 3\n            let is_ccm = ctx.cipher.is_ccm_mode();\n            let should_set_npblb = if is_ccm {\n                ctx.dir == Direction::Decrypt // CCM: only for decryption\n            } else {\n                true // GCM: always set NPBLB\n            };\n            if should_set_npblb {\n                let npblb = (16 - remaining) as u8;\n                p.cr().modify(|w| w.set_npblb(npblb));\n            }\n\n            self.write_block_blocking(&partial_block)?;\n            self.read_block_blocking(&mut partial_block)?;\n\n            output[processed..processed + remaining].copy_from_slice(&partial_block[..remaining]);\n            ctx.payload_len += remaining as u64;\n        }\n\n        if last {\n            ctx.last_block_processed = true;\n        }\n\n        Ok(())\n    }\n\n    /// Finishes the cipher operation and returns the authentication tag (for GCM/CCM).\n    pub fn finish_blocking<'c, C>(&mut self, ctx: Context<'c, C>) -> Result<Option<[u8; 16]>, Error>\n    where\n        C: Cipher<'c>,\n    {\n        let p = T::regs();\n\n        // For GCM/CCM, perform final phase to get tag\n        if ctx.is_gcm_ccm {\n            // Wait for BUSY flag to clear before modifying CR (per ST HAL)\n            while p.sr().read().busy() {}\n\n            // Set GCM/CCM phase to final (GCMPH = 3)\n            p.cr().modify(|w| w.set_gcmph(pac::aes::vals::Gcmph::from_bits(3)));\n\n            // GCM and CCM have different final phase handling:\n            // - GCM: Write length block (64-bit header len || 64-bit payload len in bits)\n            // - CCM: Enable peripheral to trigger final tag computation\n            if ctx.cipher.is_ccm_mode() {\n                // CCM: Just enable the peripheral to trigger final computation\n                // Per ST HAL HAL_CRYPEx_AESCCM_GenerateAuthTAG\n                p.cr().modify(|w| w.set_en(true));\n            } else {\n                // GCM: Write lengths (in bits) as final block per GCM spec\n                // ST HAL writes: DINR=0, DINR=header_bits, DINR=0, DINR=payload_bits\n                let header_bits = (ctx.header_len * 8) as u32;\n                let payload_bits = (ctx.payload_len * 8) as u32;\n\n                p.dinr().write_value(0);\n                p.dinr().write_value(header_bits);\n                p.dinr().write_value(0);\n                p.dinr().write_value(payload_bits);\n            }\n\n            // Wait for CCF flag\n            while !p.sr().read().ccf() {}\n\n            // Read the authentication tag\n            // With NO_SWAP mode, use big-endian byte order for consistency\n            let mut tag = [0u8; 16];\n            for i in 0..4 {\n                let word = p.doutr().read();\n                tag[i * 4..i * 4 + 4].copy_from_slice(&word.to_be_bytes());\n            }\n\n            // Clear CCF flag\n            p.icr().write(|w| w.0 = 0xFFFF_FFFF);\n\n            // Disable peripheral\n            p.cr().modify(|w| w.set_en(false));\n\n            Ok(Some(tag))\n        } else {\n            // For non-authenticated modes, just disable\n            p.cr().modify(|w| w.set_en(false));\n            Ok(None)\n        }\n    }\n\n    /// Load key into AES peripheral.\n    fn load_key(&mut self, key: &[u8]) {\n        let p = T::regs();\n\n        // Keys are loaded as 32-bit words\n        // NIST vectors are in big-endian byte order, form words accordingly\n        let key_words = key.len() / 4;\n        for i in 0..key_words {\n            let word = u32::from_be_bytes([key[i * 4], key[i * 4 + 1], key[i * 4 + 2], key[i * 4 + 3]]);\n            p.keyr(key_words - 1 - i).write_value(word); // Reverse order\n        }\n    }\n\n    /// Load IV into AES peripheral.\n    fn load_iv(&mut self, iv: &[u8]) {\n        if iv.is_empty() {\n            return;\n        }\n\n        let p = T::regs();\n\n        // IV is loaded as 32-bit words in reverse register order (per ST HAL):\n        // IVR3 = first word (bytes 0-3), IVR2 = second word, IVR1 = third, IVR0 = fourth\n        let iv_words = core::cmp::min(iv.len(), 16) / 4;\n        for i in 0..iv_words {\n            let word = u32::from_be_bytes([iv[i * 4], iv[i * 4 + 1], iv[i * 4 + 2], iv[i * 4 + 3]]);\n            p.ivr(iv_words - 1 - i).write_value(word); // Reverse order like ST HAL\n        }\n    }\n\n    /// Write a block to the AES peripheral (blocking).\n    /// Uses big-endian byte order for NIST test vector compatibility with NO_SWAP mode.\n    fn write_block_blocking(&mut self, block: &[u8]) -> Result<(), Error> {\n        let p = T::regs();\n\n        // Write all 4 words of the block\n        // Use big-endian byte order with NO_SWAP datatype for NIST vector compatibility\n        for i in 0..4 {\n            if p.sr().read().wrerr() {\n                p.icr().write(|w| w.0 = 0xFFFF_FFFF);\n                return Err(Error::WriteError);\n            }\n\n            let word = u32::from_be_bytes([block[i * 4], block[i * 4 + 1], block[i * 4 + 2], block[i * 4 + 3]]);\n            p.dinr().write_value(word);\n        }\n\n        Ok(())\n    }\n\n    /// Read a block from the AES peripheral (blocking).\n    /// Uses big-endian byte order for NIST test vector compatibility with NO_SWAP mode.\n    fn read_block_blocking(&mut self, block: &mut [u8]) -> Result<(), Error> {\n        let p = T::regs();\n\n        // Wait for computation complete\n        while !p.sr().read().ccf() {}\n\n        // Check for errors before reading\n        let sr = p.sr().read();\n        if sr.rderr() {\n            p.icr().write(|w| w.0 = 0xFFFF_FFFF);\n            return Err(Error::ReadError);\n        }\n\n        // Read as 32-bit words and convert to big-endian byte arrays\n        // With NO_SWAP datatype, use big-endian for NIST vector compatibility\n        for i in 0..4 {\n            let word = p.doutr().read();\n            let bytes = word.to_be_bytes();\n            block[i * 4..i * 4 + 4].copy_from_slice(&bytes);\n        }\n\n        // Clear flags after successful read\n        p.icr().write(|w| w.0 = 0xFFFF_FFFF);\n\n        Ok(())\n    }\n}\n\ntrait SealedInstance {\n    fn regs() -> pac::aes::Aes;\n}\n\n/// AES instance trait.\n#[allow(private_bounds)]\npub trait Instance: SealedInstance + PeripheralType + crate::rcc::RccPeripheral + 'static + Send {\n    /// Interrupt for this AES instance.\n    type Interrupt: interrupt::typelevel::Interrupt;\n}\n\nforeach_interrupt!(\n    ($inst:ident, aes, AES, GLOBAL, $irq:ident) => {\n        impl Instance for peripherals::$inst {\n            type Interrupt = crate::interrupt::typelevel::$irq;\n        }\n\n        impl SealedInstance for peripherals::$inst {\n            fn regs() -> crate::pac::aes::Aes {\n                crate::pac::$inst\n            }\n        }\n    };\n);\n\ndma_trait!(DmaIn, Instance);\ndma_trait!(DmaOut, Instance);\n"
  },
  {
    "path": "embassy-stm32/src/backup_sram.rs",
    "content": "//! Battary backed SRAM\n\nuse core::slice;\n\nuse embassy_hal_internal::Peri;\n\nuse crate::_generated::{BKPSRAM_BASE, BKPSRAM_SIZE};\nuse crate::peripherals::BKPSRAM;\n\n/// Struct used to initilize backup sram\npub struct BackupMemory {}\n\nimpl BackupMemory {\n    /// Setup battery backed sram\n    ///\n    /// Returns slice to sram and whether the sram was retained\n    pub fn new(_backup_sram: Peri<'static, BKPSRAM>) -> (&'static mut [u8], bool) {\n        // Assert bksram has been enabled in rcc\n        assert!(crate::pac::PWR.bdcr().read().bren() == crate::pac::pwr::vals::Retention::PRESERVED);\n\n        unsafe {\n            (\n                slice::from_raw_parts_mut(BKPSRAM_BASE as *mut u8, BKPSRAM_SIZE),\n                critical_section::with(|_| crate::rcc::BKSRAM_RETAINED),\n            )\n        }\n    }\n}\n"
  },
  {
    "path": "embassy-stm32/src/can/bxcan/filter.rs",
    "content": "//! Filter bank API.\n\nuse core::marker::PhantomData;\n\nuse super::{ExtendedId, Fifo, Id, StandardId};\n\nconst F32_RTR: u32 = 0b010; // set the RTR bit to match remote frames\nconst F32_IDE: u32 = 0b100; // set the IDE bit to match extended identifiers\nconst F16_RTR: u16 = 0b10000;\nconst F16_IDE: u16 = 0b01000;\n\n/// A 16-bit filter list entry.\n///\n/// This can match data and remote frames using standard IDs.\n#[derive(Debug, Copy, Clone, Eq, PartialEq)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub struct ListEntry16(u16);\n\n/// A 32-bit filter list entry.\n///\n/// This can match data and remote frames using extended or standard IDs.\n#[derive(Debug, Copy, Clone, Eq, PartialEq)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub struct ListEntry32(u32);\n\n/// A 16-bit identifier mask.\n#[derive(Debug, Copy, Clone)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub struct Mask16 {\n    id: u16,\n    mask: u16,\n}\n\n/// A 32-bit identifier mask.\n#[derive(Debug, Copy, Clone)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub struct Mask32 {\n    id: u32,\n    mask: u32,\n}\n\nimpl ListEntry16 {\n    /// Creates a filter list entry that accepts data frames with the given standard ID.\n    ///\n    /// This entry will *not* accept remote frames with the same ID.\n    pub fn data_frames_with_id(id: StandardId) -> Self {\n        Self(id.as_raw() << 5)\n    }\n\n    /// Creates a filter list entry that accepts remote frames with the given standard ID.\n    pub fn remote_frames_with_id(id: StandardId) -> Self {\n        Self(id.as_raw() << 5 | F16_RTR)\n    }\n}\n\nimpl ListEntry32 {\n    /// Creates a filter list entry that accepts data frames with the given ID.\n    ///\n    /// This entry will *not* accept remote frames with the same ID.\n    ///\n    /// The filter will only accept *either* standard *or* extended frames, depending on `id`.\n    pub fn data_frames_with_id(id: impl Into<Id>) -> Self {\n        match id.into() {\n            Id::Standard(id) => Self(u32::from(id.as_raw()) << 21),\n            Id::Extended(id) => Self(id.as_raw() << 3 | F32_IDE),\n        }\n    }\n\n    /// Creates a filter list entry that accepts remote frames with the given ID.\n    pub fn remote_frames_with_id(id: impl Into<Id>) -> Self {\n        match id.into() {\n            Id::Standard(id) => Self(u32::from(id.as_raw()) << 21 | F32_RTR),\n            Id::Extended(id) => Self(id.as_raw() << 3 | F32_IDE | F32_RTR),\n        }\n    }\n}\n\nimpl Mask16 {\n    /// Creates a 16-bit identifier mask that accepts all frames.\n    ///\n    /// This will accept both standard and extended data and remote frames with any ID.\n    pub fn accept_all() -> Self {\n        Self { id: 0, mask: 0 }\n    }\n\n    /// Creates a 16-bit identifier mask that accepts all frames with the given standard\n    /// ID and mask combination.\n    ///\n    /// Filter logic: `frame_accepted = (incoming_id & mask) == (id & mask)`\n    ///\n    /// A mask of all all ones (`0x7FF`) matches an exact ID, a mask of 0 matches all IDs.\n    ///\n    /// Both data and remote frames with `id` will be accepted. Any extended frames will be\n    /// rejected.\n    pub fn frames_with_std_id(id: StandardId, mask: StandardId) -> Self {\n        Self {\n            id: id.as_raw() << 5,\n            mask: mask.as_raw() << 5 | F16_IDE, // also require IDE = 0\n        }\n    }\n\n    /// Make the filter accept data frames only.\n    pub fn data_frames_only(&mut self) -> &mut Self {\n        self.id &= !F16_RTR; // RTR = 0\n        self.mask |= F16_RTR;\n        self\n    }\n\n    /// Make the filter accept remote frames only.\n    pub fn remote_frames_only(&mut self) -> &mut Self {\n        self.id |= F16_RTR; // RTR = 1\n        self.mask |= F16_RTR;\n        self\n    }\n}\n\nimpl Mask32 {\n    /// Creates a 32-bit identifier mask that accepts all frames.\n    ///\n    /// This will accept both standard and extended data and remote frames with any ID.\n    pub fn accept_all() -> Self {\n        Self { id: 0, mask: 0 }\n    }\n\n    /// Creates a 32-bit identifier mask that accepts all frames with the given extended\n    /// ID and mask combination.\n    ///\n    /// Filter logic: `frame_accepted = (incoming_id & mask) == (id & mask)`\n    ///\n    /// A mask of all all ones (`0x1FFF_FFFF`) matches an exact ID, a mask of 0 matches all IDs.\n    ///\n    /// Both data and remote frames with `id` will be accepted. Standard frames will be rejected.\n    pub fn frames_with_ext_id(id: ExtendedId, mask: ExtendedId) -> Self {\n        Self {\n            id: id.as_raw() << 3 | F32_IDE,\n            mask: mask.as_raw() << 3 | F32_IDE, // also require IDE = 1\n        }\n    }\n\n    /// Creates a 32-bit identifier mask that accepts all frames with the given standard\n    /// ID and mask combination.\n    ///\n    /// Filter logic: `frame_accepted = (incoming_id & mask) == (id & mask)`\n    ///\n    /// A mask of all all ones (`0x7FF`) matches the exact ID, a mask of 0 matches all IDs.\n    ///\n    /// Both data and remote frames with `id` will be accepted. Extended frames will be rejected.\n    pub fn frames_with_std_id(id: StandardId, mask: StandardId) -> Self {\n        Self {\n            id: u32::from(id.as_raw()) << 21,\n            mask: u32::from(mask.as_raw()) << 21 | F32_IDE, // also require IDE = 0\n        }\n    }\n\n    /// Make the filter accept data frames only.\n    pub fn data_frames_only(&mut self) -> &mut Self {\n        self.id &= !F32_RTR; // RTR = 0\n        self.mask |= F32_RTR;\n        self\n    }\n\n    /// Make the filter accept remote frames only.\n    pub fn remote_frames_only(&mut self) -> &mut Self {\n        self.id |= F32_RTR; // RTR = 1\n        self.mask |= F32_RTR;\n        self\n    }\n}\n\n/// The configuration of a filter bank.\n#[derive(Debug, Copy, Clone)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub enum BankConfig {\n    /// Specify up to 4 exact standard CAN ID's.\n    List16([ListEntry16; 4]),\n    /// Specify up to 2 exact standard or extended CAN ID's.\n    List32([ListEntry32; 2]),\n    /// Specify up to 2 standard ID's with masks.\n    Mask16([Mask16; 2]),\n    /// Specify a single extended ID with mask.\n    Mask32(Mask32),\n}\n\nimpl From<[ListEntry16; 4]> for BankConfig {\n    #[inline]\n    fn from(entries: [ListEntry16; 4]) -> Self {\n        Self::List16(entries)\n    }\n}\n\nimpl From<[ListEntry32; 2]> for BankConfig {\n    #[inline]\n    fn from(entries: [ListEntry32; 2]) -> Self {\n        Self::List32(entries)\n    }\n}\n\nimpl From<[Mask16; 2]> for BankConfig {\n    #[inline]\n    fn from(entries: [Mask16; 2]) -> Self {\n        Self::Mask16(entries)\n    }\n}\n\nimpl From<Mask32> for BankConfig {\n    #[inline]\n    fn from(filter: Mask32) -> Self {\n        Self::Mask32(filter)\n    }\n}\n\n/// Interface to the filter banks of a CAN peripheral.\npub struct MasterFilters<'a> {\n    /// Number of assigned filter banks.\n    ///\n    /// On chips with splittable filter banks, this value can be dynamic.\n    bank_count: u8,\n    _phantom: PhantomData<&'a ()>,\n    info: &'static crate::can::Info,\n}\n\n// NOTE: This type mutably borrows the CAN instance and has unique access to the registers while it\n// exists.\nimpl MasterFilters<'_> {\n    pub(crate) unsafe fn new(info: &'static crate::can::Info) -> Self {\n        // Enable initialization mode.\n        info.regs.0.fmr().modify(|reg| reg.set_finit(true));\n\n        // Read the filter split value.\n        let bank_count = info.regs.0.fmr().read().can2sb();\n\n        // (Reset value of CAN2SB is 0x0E, 14, which, in devices with 14 filter banks, assigns all\n        // of them to the master peripheral, and in devices with 28, assigns them 50/50 to\n        // master/slave instances)\n\n        Self {\n            bank_count,\n            _phantom: PhantomData,\n            info,\n        }\n    }\n\n    fn banks_imm(&self) -> FilterBanks {\n        FilterBanks {\n            start_idx: 0,\n            bank_count: self.bank_count,\n            info: self.info,\n        }\n    }\n\n    /// Returns the number of filter banks currently assigned to this instance.\n    ///\n    /// Chips with splittable filter banks may start out with some banks assigned to the master\n    /// instance and some assigned to the slave instance.\n    pub fn num_banks(&self) -> u8 {\n        self.bank_count\n    }\n\n    /// Disables all enabled filter banks.\n    ///\n    /// This causes all incoming frames to be disposed.\n    pub fn clear(&mut self) -> &mut Self {\n        self.banks_imm().clear();\n        self\n    }\n\n    /// Disables a filter bank.\n    ///\n    /// If `index` is out of bounds, this will panic.\n    pub fn disable_bank(&mut self, index: u8) -> &mut Self {\n        self.banks_imm().disable(index);\n        self\n    }\n\n    /// Configures a filter bank according to `config` and enables it.\n    ///\n    /// Each filter bank is associated with one of the two RX FIFOs, configured by the [`Fifo`]\n    /// passed to this function. In the event that both FIFOs are configured to accept an incoming\n    /// frame, the accepting filter bank with the lowest index wins. The FIFO state is ignored, so\n    /// if the FIFO is full, it will overflow, even if the other FIFO is also configured to accept\n    /// the frame.\n    ///\n    /// # Parameters\n    ///\n    /// - `index`: the filter index.\n    /// - `fifo`: the receive FIFO the filter should pass accepted messages to.\n    /// - `config`: the filter configuration.\n    pub fn enable_bank(&mut self, index: u8, fifo: Fifo, config: impl Into<BankConfig>) -> &mut Self {\n        self.banks_imm().enable(index, fifo, config.into());\n        self\n    }\n}\n\nimpl MasterFilters<'_> {\n    /// Sets the index at which the filter banks owned by the slave peripheral start.\n    pub fn set_split(&mut self, split_index: u8) -> &mut Self {\n        assert!(split_index <= self.info.num_filter_banks);\n        self.info.regs.0.fmr().modify(|reg| reg.set_can2sb(split_index));\n        self.bank_count = split_index;\n        self\n    }\n\n    /// Accesses the filters assigned to the slave peripheral.\n    pub fn slave_filters(&mut self) -> SlaveFilters<'_> {\n        // NB: This mutably borrows `self`, so it has full access to the filter bank registers.\n        SlaveFilters {\n            start_idx: self.bank_count,\n            bank_count: self.info.num_filter_banks - self.bank_count,\n            _phantom: PhantomData,\n            info: self.info,\n        }\n    }\n}\n\nimpl Drop for MasterFilters<'_> {\n    #[inline]\n    fn drop(&mut self) {\n        // Leave initialization mode.\n        self.info.regs.0.fmr().modify(|regs| regs.set_finit(false));\n    }\n}\n\n/// Interface to the filter banks assigned to a slave peripheral.\npub struct SlaveFilters<'a> {\n    start_idx: u8,\n    bank_count: u8,\n    _phantom: PhantomData<&'a ()>,\n    info: &'static crate::can::Info,\n}\n\nimpl SlaveFilters<'_> {\n    fn banks_imm(&self) -> FilterBanks {\n        FilterBanks {\n            start_idx: self.start_idx,\n            bank_count: self.bank_count,\n            info: self.info,\n        }\n    }\n\n    /// Returns the number of filter banks currently assigned to this instance.\n    ///\n    /// Chips with splittable filter banks may start out with some banks assigned to the master\n    /// instance and some assigned to the slave instance.\n    pub fn num_banks(&self) -> u8 {\n        self.bank_count\n    }\n\n    /// Disables all enabled filter banks.\n    ///\n    /// This causes all incoming frames to be disposed.\n    pub fn clear(&mut self) -> &mut Self {\n        self.banks_imm().clear();\n        self\n    }\n\n    /// Disables a filter bank.\n    ///\n    /// If `index` is out of bounds, this will panic.\n    pub fn disable_bank(&mut self, index: u8) -> &mut Self {\n        self.banks_imm().disable(index);\n        self\n    }\n\n    /// Configures a filter bank according to `config` and enables it.\n    ///\n    /// # Parameters\n    ///\n    /// - `index`: the filter index.\n    /// - `fifo`: the receive FIFO the filter should pass accepted messages to.\n    /// - `config`: the filter configuration.\n    pub fn enable_bank(&mut self, index: u8, fifo: Fifo, config: impl Into<BankConfig>) -> &mut Self {\n        self.banks_imm().enable(index, fifo, config.into());\n        self\n    }\n}\n\nstruct FilterBanks {\n    start_idx: u8,\n    bank_count: u8,\n    info: &'static crate::can::Info,\n}\n\nimpl FilterBanks {\n    fn clear(&mut self) {\n        let mask = filter_bitmask(self.start_idx, self.bank_count);\n\n        self.info.regs.0.fa1r().modify(|reg| {\n            for i in 0..28usize {\n                if (0x01u32 << i) & mask != 0 {\n                    reg.set_fact(i, false);\n                }\n            }\n        });\n    }\n\n    fn assert_bank_index(&self, index: u8) {\n        assert!((self.start_idx..self.start_idx + self.bank_count).contains(&index));\n    }\n\n    fn disable(&mut self, index: u8) {\n        self.assert_bank_index(index);\n        self.info\n            .regs\n            .0\n            .fa1r()\n            .modify(|reg| reg.set_fact(index as usize, false))\n    }\n\n    fn enable(&mut self, index: u8, fifo: Fifo, config: BankConfig) {\n        self.assert_bank_index(index);\n\n        // Configure mode.\n        let mode = matches!(config, BankConfig::List16(_) | BankConfig::List32(_));\n        self.info.regs.0.fm1r().modify(|reg| reg.set_fbm(index as usize, mode));\n\n        // Configure scale.\n        let scale = matches!(config, BankConfig::List32(_) | BankConfig::Mask32(_));\n        self.info.regs.0.fs1r().modify(|reg| reg.set_fsc(index as usize, scale));\n\n        // Configure filter register.\n        let (fxr1, fxr2);\n        match config {\n            BankConfig::List16([a, b, c, d]) => {\n                fxr1 = (u32::from(b.0) << 16) | u32::from(a.0);\n                fxr2 = (u32::from(d.0) << 16) | u32::from(c.0);\n            }\n            BankConfig::List32([a, b]) => {\n                fxr1 = a.0;\n                fxr2 = b.0;\n            }\n            BankConfig::Mask16([a, b]) => {\n                fxr1 = (u32::from(a.mask) << 16) | u32::from(a.id);\n                fxr2 = (u32::from(b.mask) << 16) | u32::from(b.id);\n            }\n            BankConfig::Mask32(a) => {\n                fxr1 = a.id;\n                fxr2 = a.mask;\n            }\n        };\n        let bank = self.info.regs.0.fb(index as usize);\n        bank.fr1().write(|w| w.0 = fxr1);\n        bank.fr2().write(|w| w.0 = fxr2);\n\n        // Assign to the right FIFO\n        self.info.regs.0.ffa1r().modify(|reg| {\n            reg.set_ffa(\n                index as usize,\n                match fifo {\n                    Fifo::Fifo0 => false,\n                    Fifo::Fifo1 => true,\n                },\n            )\n        });\n\n        // Set active.\n        self.info.regs.0.fa1r().modify(|reg| reg.set_fact(index as usize, true))\n    }\n}\n\n/// Computes a bitmask for per-filter-bank registers that only includes filters in the given range.\nfn filter_bitmask(start_idx: u8, bank_count: u8) -> u32 {\n    let count_mask = (1 << bank_count) - 1; // `bank_count` 1-bits\n    count_mask << start_idx\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n\n    #[test]\n    fn test_filter_bitmask() {\n        assert_eq!(filter_bitmask(0, 1), 0x1);\n        assert_eq!(filter_bitmask(1, 1), 0b10);\n        assert_eq!(filter_bitmask(0, 4), 0xf);\n        assert_eq!(filter_bitmask(1, 3), 0xe);\n        assert_eq!(filter_bitmask(8, 1), 0x100);\n        assert_eq!(filter_bitmask(8, 4), 0xf00);\n    }\n}\n"
  },
  {
    "path": "embassy-stm32/src/can/bxcan/mod.rs",
    "content": "pub mod filter;\nmod registers;\n\nuse core::future::poll_fn;\nuse core::marker::PhantomData;\nuse core::task::Poll;\n\nuse embassy_hal_internal::PeripheralType;\nuse embassy_hal_internal::interrupt::InterruptExt;\nuse embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;\nuse embassy_sync::channel::Channel;\nuse embassy_sync::waitqueue::AtomicWaker;\npub use embedded_can::{ExtendedId, Id, StandardId};\n\nuse self::filter::MasterFilters;\nuse self::registers::{Registers, RxFifo};\npub use super::common::{BufferedCanReceiver, BufferedCanSender};\nuse super::common::{InfoRef, RxInfoRef, TxInfoRef};\nuse super::frame::{Envelope, Frame};\nuse super::util;\nuse crate::can::enums::{BusError, RefCountOp, TryReadError};\nuse crate::gpio::{AfType, OutputType, Pull, Speed};\nuse crate::interrupt::typelevel::Interrupt;\nuse crate::rcc::{self, RccPeripheral};\nuse crate::{Peri, interrupt, peripherals};\n\n/// Interrupt handler.\npub struct TxInterruptHandler<T: Instance> {\n    _phantom: PhantomData<T>,\n}\n\nimpl<T: Instance> interrupt::typelevel::Handler<T::TXInterrupt> for TxInterruptHandler<T> {\n    unsafe fn on_interrupt() {\n        T::regs().tsr().write(|v| {\n            v.set_rqcp(0, true);\n            v.set_rqcp(1, true);\n            v.set_rqcp(2, true);\n        });\n        T::info().state.lock(|state| {\n            state.borrow().tx_mode.on_interrupt::<T>();\n        });\n    }\n}\n\n/// RX0 interrupt handler.\npub struct Rx0InterruptHandler<T: Instance> {\n    _phantom: PhantomData<T>,\n}\n\nimpl<T: Instance> interrupt::typelevel::Handler<T::RX0Interrupt> for Rx0InterruptHandler<T> {\n    unsafe fn on_interrupt() {\n        T::info().state.lock(|state| {\n            state.borrow().rx_mode.on_interrupt::<T>(RxFifo::Fifo0);\n        });\n    }\n}\n\n/// RX1 interrupt handler.\npub struct Rx1InterruptHandler<T: Instance> {\n    _phantom: PhantomData<T>,\n}\n\nimpl<T: Instance> interrupt::typelevel::Handler<T::RX1Interrupt> for Rx1InterruptHandler<T> {\n    unsafe fn on_interrupt() {\n        T::info().state.lock(|state| {\n            state.borrow().rx_mode.on_interrupt::<T>(RxFifo::Fifo1);\n        });\n    }\n}\n\n/// SCE interrupt handler.\npub struct SceInterruptHandler<T: Instance> {\n    _phantom: PhantomData<T>,\n}\n\nimpl<T: Instance> interrupt::typelevel::Handler<T::SCEInterrupt> for SceInterruptHandler<T> {\n    unsafe fn on_interrupt() {\n        let msr = T::regs().msr();\n        let msr_val = msr.read();\n\n        if msr_val.slaki() {\n            msr.modify(|m| m.set_slaki(true));\n            T::info().state.lock(|state| {\n                state.borrow().err_waker.wake();\n            });\n        } else if msr_val.erri() {\n            // Disable the interrupt, but don't acknowledge the error, so that it can be\n            // forwarded off the bus message consumer. If we don't provide some way for\n            // downstream code to determine that it has already provided this bus error instance\n            // to the bus message consumer, we are doomed to re-provide a single error instance for\n            // an indefinite amount of time.\n            let ier = T::regs().ier();\n            ier.modify(|i| i.set_errie(false));\n            T::info().state.lock(|state| {\n                state.borrow().err_waker.wake();\n            });\n        }\n    }\n}\n\n/// Configuration proxy returned by [`Can::modify_config`].\npub struct CanConfig<'a> {\n    phantom: PhantomData<&'a ()>,\n    info: InfoRef,\n    periph_clock: crate::time::Hertz,\n}\n\nimpl CanConfig<'_> {\n    /// Configures the bit timings.\n    ///\n    /// You can use <http://www.bittiming.can-wiki.info/> to calculate the `btr` parameter. Enter\n    /// parameters as follows:\n    ///\n    /// - *Clock Rate*: The input clock speed to the CAN peripheral (*not* the CPU clock speed).\n    ///   This is the clock rate of the peripheral bus the CAN peripheral is attached to (eg. APB1).\n    /// - *Sample Point*: Should normally be left at the default value of 87.5%.\n    /// - *SJW*: Should normally be left at the default value of 1.\n    ///\n    /// Then copy the `CAN_BUS_TIME` register value from the table and pass it as the `btr`\n    /// parameter to this method.\n    pub fn set_bit_timing(self, bt: crate::can::util::NominalBitTiming) -> Self {\n        self.info.regs.set_bit_timing(bt);\n        self\n    }\n\n    /// Configure the CAN bit rate.\n    ///\n    /// This is a helper that internally calls `set_bit_timing()`[Self::set_bit_timing].\n    pub fn set_bitrate(self, bitrate: u32) -> Self {\n        let bit_timing = util::calc_can_timings(self.periph_clock, bitrate).unwrap();\n        self.set_bit_timing(bit_timing)\n    }\n\n    /// Enables or disables loopback mode: Internally connects the TX and RX\n    /// signals together.\n    pub fn set_loopback(self, enabled: bool) -> Self {\n        self.info.regs.set_loopback(enabled);\n        self\n    }\n\n    /// Enables or disables silent mode: Disconnects the TX signal from the pin.\n    pub fn set_silent(self, enabled: bool) -> Self {\n        self.info.regs.set_silent(enabled);\n        self\n    }\n\n    /// Enables or disables automatic retransmission of frames.\n    ///\n    /// If this is enabled, the CAN peripheral will automatically try to retransmit each frame\n    /// until it can be sent. Otherwise, it will try only once to send each frame.\n    ///\n    /// Automatic retransmission is enabled by default.\n    pub fn set_automatic_retransmit(self, enabled: bool) -> Self {\n        self.info.regs.set_automatic_retransmit(enabled);\n        self\n    }\n}\n\nimpl Drop for CanConfig<'_> {\n    #[inline]\n    fn drop(&mut self) {\n        self.info.regs.leave_init_mode();\n    }\n}\n\n/// CAN driver\npub struct Can<'d> {\n    phantom: PhantomData<&'d ()>,\n    info: InfoRef,\n    periph_clock: crate::time::Hertz,\n}\n\n/// Error returned by `try_write`\n#[derive(Debug)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub enum TryWriteError {\n    /// All transmit mailboxes are full\n    Full,\n}\n\nimpl<'d> Can<'d> {\n    /// Creates a new Bxcan instance, keeping the peripheral in sleep mode.\n    /// You must call [Can::enable_non_blocking] to use the peripheral.\n    pub fn new<T: Instance, #[cfg(afio)] A>(\n        _peri: Peri<'d, T>,\n        rx: Peri<'d, if_afio!(impl RxPin<T, A>)>,\n        tx: Peri<'d, if_afio!(impl TxPin<T, A>)>,\n        _irqs: impl interrupt::typelevel::Binding<T::TXInterrupt, TxInterruptHandler<T>>\n        + interrupt::typelevel::Binding<T::RX0Interrupt, Rx0InterruptHandler<T>>\n        + interrupt::typelevel::Binding<T::RX1Interrupt, Rx1InterruptHandler<T>>\n        + interrupt::typelevel::Binding<T::SCEInterrupt, SceInterruptHandler<T>>\n        + 'd,\n    ) -> Self {\n        let info = T::info();\n        let regs = &T::info().regs;\n\n        set_as_af!(rx, AfType::input(Pull::None));\n        set_as_af!(tx, AfType::output(OutputType::PushPull, Speed::VeryHigh));\n\n        rcc::enable_and_reset::<T>();\n\n        {\n            regs.0.ier().write(|w| {\n                w.set_errie(true);\n                w.set_fmpie(0, true);\n                w.set_fmpie(1, true);\n                w.set_tmeie(true);\n                w.set_bofie(true);\n                w.set_epvie(true);\n                w.set_ewgie(true);\n                w.set_lecie(true);\n            });\n\n            regs.0.mcr().write(|w| {\n                // Enable timestamps on rx messages\n\n                w.set_ttcm(true);\n            });\n        }\n\n        unsafe {\n            info.tx_interrupt.unpend();\n            info.tx_interrupt.enable();\n            info.rx0_interrupt.unpend();\n            info.rx0_interrupt.enable();\n            info.rx1_interrupt.unpend();\n            info.rx1_interrupt.enable();\n            info.sce_interrupt.unpend();\n            info.sce_interrupt.enable();\n        }\n\n        set_as_af!(rx, AfType::input(Pull::None));\n        set_as_af!(tx, AfType::output(OutputType::PushPull, Speed::VeryHigh));\n\n        Registers(T::regs()).leave_init_mode();\n\n        Self {\n            phantom: PhantomData,\n            info: InfoRef::new(T::info()),\n            periph_clock: T::frequency(),\n        }\n    }\n\n    /// Set CAN bit rate.\n    pub fn set_bitrate(&mut self, bitrate: u32) {\n        let bit_timing = util::calc_can_timings(self.periph_clock, bitrate).unwrap();\n        self.modify_config().set_bit_timing(bit_timing);\n    }\n\n    /// Configure bit timings and silent/loop-back mode.\n    ///\n    /// Calling this method will enter initialization mode. You must enable the peripheral\n    /// again afterwards with [`enable`](Self::enable).\n    pub fn modify_config(&mut self) -> CanConfig<'_> {\n        self.info.regs.enter_init_mode();\n\n        CanConfig {\n            phantom: self.phantom,\n            info: InfoRef::new(&self.info),\n            periph_clock: self.periph_clock,\n        }\n    }\n\n    /// Enables the peripheral and synchronizes with the bus.\n    ///\n    /// This will wait for 11 consecutive recessive bits (bus idle state).\n    /// Contrary to enable method from bxcan library, this will not freeze the executor while waiting.\n    pub async fn enable(&mut self) {\n        while self.info.regs.enable_non_blocking().is_err() {\n            // SCE interrupt is only generated for entering sleep mode, but not leaving.\n            // Yield to allow other tasks to execute while can bus is initializing.\n            embassy_futures::yield_now().await;\n        }\n    }\n\n    /// Enables or disables the peripheral from automatically wakeup when a SOF is detected on the bus\n    /// while the peripheral is in sleep mode\n    pub fn set_automatic_wakeup(&mut self, enabled: bool) {\n        self.info.regs.set_automatic_wakeup(enabled);\n    }\n\n    /// Manually wake the peripheral from sleep mode.\n    ///\n    /// Waking the peripheral manually does not trigger a wake-up interrupt.\n    /// This will wait until the peripheral has acknowledged it has awoken from sleep mode\n    pub fn wakeup(&mut self) {\n        self.info.regs.wakeup()\n    }\n\n    /// Check if the peripheral is currently in sleep mode\n    pub fn is_sleeping(&self) -> bool {\n        self.info.regs.0.msr().read().slak()\n    }\n\n    /// Put the peripheral in sleep mode\n    ///\n    /// When the peripherial is in sleep mode, messages can still be queued for transmission\n    /// and any previously received messages can be read from the receive FIFOs, however\n    /// no messages will be transmitted and no additional messages will be received.\n    ///\n    /// If the peripheral has automatic wakeup enabled, when a Start-of-Frame is detected\n    /// the peripheral will automatically wake and receive the incoming message.\n    pub async fn sleep(&mut self) {\n        self.info.regs.0.ier().modify(|i| i.set_slkie(true));\n        self.info.regs.0.mcr().modify(|m| m.set_sleep(true));\n\n        poll_fn(|cx| {\n            self.info.state.lock(|s| {\n                s.borrow().err_waker.register(cx.waker());\n            });\n            if self.is_sleeping() {\n                Poll::Ready(())\n            } else {\n                Poll::Pending\n            }\n        })\n        .await;\n\n        self.info.regs.0.ier().modify(|i| i.set_slkie(false));\n    }\n\n    /// Enable FIFO scheduling of outgoing frames.\n    ///\n    /// If this is enabled, frames will be transmitted in the order that they are passed to\n    /// [`write()`][Self::write] or [`try_write()`][Self::try_write()].\n    ///\n    /// If this is disabled, frames are transmitted in order of priority.\n    ///\n    /// FIFO scheduling is disabled by default.\n    pub fn set_tx_fifo_scheduling(&mut self, enabled: bool) {\n        self.info.regs.set_tx_fifo_scheduling(enabled)\n    }\n\n    /// Checks if FIFO scheduling of outgoing frames is enabled.\n    pub fn tx_fifo_scheduling_enabled(&self) -> bool {\n        self.info.regs.tx_fifo_scheduling_enabled()\n    }\n\n    /// Queues the message to be sent.\n    ///\n    /// If the TX queue is full, this will wait until there is space, therefore exerting backpressure.\n    pub async fn write(&mut self, frame: &Frame) -> TransmitStatus {\n        self.split().0.write(frame).await\n    }\n\n    /// Attempts to transmit a frame without blocking.\n    ///\n    /// Returns [Err(TryWriteError::Full)] if the frame can not be queued for transmission now.\n    ///\n    /// If FIFO scheduling is enabled, any empty mailbox will be used.\n    ///\n    /// Otherwise, the frame will only be accepted if there is no frame with the same priority already queued.\n    /// This is done to work around a hardware limitation that could lead to out-of-order delivery\n    /// of frames with the same priority.\n    pub fn try_write(&mut self, frame: &Frame) -> Result<TransmitStatus, TryWriteError> {\n        self.split().0.try_write(frame)\n    }\n\n    /// Waits for a specific transmit mailbox to become empty\n    pub async fn flush(&self, mb: Mailbox) {\n        CanTx {\n            _phantom: PhantomData,\n            info: TxInfoRef::new(&self.info),\n        }\n        .flush_inner(mb)\n        .await;\n    }\n\n    /// Waits until any of the transmit mailboxes become empty\n    ///\n    /// Note that [`Self::try_write()`] may fail with [`TryWriteError::Full`],\n    /// even after the future returned by this function completes.\n    /// This will happen if FIFO scheduling of outgoing frames is not enabled,\n    /// and a frame with equal priority is already queued for transmission.\n    pub async fn flush_any(&self) {\n        CanTx {\n            _phantom: PhantomData,\n            info: TxInfoRef::new(&self.info),\n        }\n        .flush_any_inner()\n        .await\n    }\n\n    /// Waits until all of the transmit mailboxes become empty\n    pub async fn flush_all(&self) {\n        CanTx {\n            _phantom: PhantomData,\n            info: TxInfoRef::new(&self.info),\n        }\n        .flush_all_inner()\n        .await\n    }\n\n    /// Attempts to abort the sending of a frame that is pending in a mailbox.\n    ///\n    /// If there is no frame in the provided mailbox, or its transmission succeeds before it can be\n    /// aborted, this function has no effect and returns `false`.\n    ///\n    /// If there is a frame in the provided mailbox, and it is canceled successfully, this function\n    /// returns `true`.\n    pub fn abort(&mut self, mailbox: Mailbox) -> bool {\n        self.info.regs.abort(mailbox)\n    }\n\n    /// Returns `true` if no frame is pending for transmission.\n    pub fn is_transmitter_idle(&self) -> bool {\n        self.info.regs.is_idle()\n    }\n\n    /// Read a CAN frame.\n    ///\n    /// If no CAN frame is in the RX buffer, this will wait until there is one.\n    ///\n    /// Returns a tuple of the time the message was received and the message frame\n    pub async fn read(&mut self) -> Result<Envelope, BusError> {\n        RxMode::read(&self.info).await\n    }\n\n    /// Attempts to read a CAN frame without blocking.\n    ///\n    /// Returns [Err(TryReadError::Empty)] if there are no frames in the rx queue.\n    pub fn try_read(&mut self) -> Result<Envelope, TryReadError> {\n        RxMode::try_read(&self.info)\n    }\n\n    /// Waits while receive queue is empty.\n    pub async fn wait_not_empty(&mut self) {\n        RxMode::wait_not_empty(&self.info).await\n    }\n\n    /// Split the CAN driver into transmit and receive halves.\n    ///\n    /// Useful for doing separate transmit/receive tasks.\n    pub fn split<'c>(&'c mut self) -> (CanTx<'d>, CanRx<'d>) {\n        (\n            CanTx {\n                _phantom: PhantomData,\n                info: TxInfoRef::new(&self.info),\n            },\n            CanRx {\n                _phantom: PhantomData,\n                info: RxInfoRef::new(&self.info),\n            },\n        )\n    }\n\n    /// Return a buffered instance of driver. User must supply Buffers\n    pub fn buffered<'c, const TX_BUF_SIZE: usize, const RX_BUF_SIZE: usize>(\n        &'c mut self,\n        txb: &'static mut TxBuf<TX_BUF_SIZE>,\n        rxb: &'static mut RxBuf<RX_BUF_SIZE>,\n    ) -> BufferedCan<'d, TX_BUF_SIZE, RX_BUF_SIZE> {\n        let (tx, rx) = self.split();\n        BufferedCan {\n            tx: tx.buffered(txb),\n            rx: rx.buffered(rxb),\n        }\n    }\n}\n\nimpl<'d> Can<'d> {\n    /// Accesses the filter banks owned by this CAN peripheral.\n    ///\n    /// To modify filters of a slave peripheral, `modify_filters` has to be called on the master\n    /// peripheral instead.\n    pub fn modify_filters(&mut self) -> MasterFilters<'_> {\n        unsafe { MasterFilters::new(&self.info) }\n    }\n}\n\n/// Buffered CAN driver.\npub struct BufferedCan<'d, const TX_BUF_SIZE: usize, const RX_BUF_SIZE: usize> {\n    tx: BufferedCanTx<'d, TX_BUF_SIZE>,\n    rx: BufferedCanRx<'d, RX_BUF_SIZE>,\n}\n\nimpl<'d, const TX_BUF_SIZE: usize, const RX_BUF_SIZE: usize> BufferedCan<'d, TX_BUF_SIZE, RX_BUF_SIZE> {\n    /// Async write frame to TX buffer.\n    pub async fn write(&mut self, frame: &Frame) {\n        self.tx.write(frame).await\n    }\n\n    /// Returns a sender that can be used for sending CAN frames.\n    pub fn writer(&self) -> BufferedCanSender {\n        self.tx.writer()\n    }\n\n    /// Async read frame from RX buffer.\n    pub async fn read(&mut self) -> Result<Envelope, BusError> {\n        self.rx.read().await\n    }\n\n    /// Attempts to read a CAN frame without blocking.\n    ///\n    /// Returns [Err(TryReadError::Empty)] if there are no frames in the rx queue.\n    pub fn try_read(&mut self) -> Result<Envelope, TryReadError> {\n        self.rx.try_read()\n    }\n\n    /// Waits while receive queue is empty.\n    pub async fn wait_not_empty(&mut self) {\n        self.rx.wait_not_empty().await\n    }\n\n    /// Returns a receiver that can be used for receiving CAN frames. Note, each CAN frame will only be received by one receiver.\n    pub fn reader(&self) -> BufferedCanReceiver {\n        self.rx.reader()\n    }\n\n    /// Accesses the filter banks owned by this CAN peripheral.\n    ///\n    /// To modify filters of a slave peripheral, `modify_filters` has to be called on the master\n    /// peripheral instead.\n    pub fn modify_filters(&mut self) -> MasterFilters<'_> {\n        self.rx.modify_filters()\n    }\n}\n\n/// CAN driver, transmit half.\npub struct CanTx<'d> {\n    _phantom: PhantomData<&'d ()>,\n    info: TxInfoRef,\n}\n\nimpl<'d> CanTx<'d> {\n    /// Queues the message to be sent.\n    ///\n    /// If the TX queue is full, this will wait until there is space, therefore exerting backpressure.\n    pub async fn write(&mut self, frame: &Frame) -> TransmitStatus {\n        poll_fn(|cx| {\n            self.info.state.lock(|s| {\n                s.borrow().tx_mode.register(cx.waker());\n            });\n            if let Ok(status) = self.info.regs.transmit(frame) {\n                return Poll::Ready(status);\n            }\n\n            Poll::Pending\n        })\n        .await\n    }\n\n    /// Attempts to transmit a frame without blocking.\n    ///\n    /// Returns [Err(TryWriteError::Full)] if the frame can not be queued for transmission now.\n    ///\n    /// If FIFO scheduling is enabled, any empty mailbox will be used.\n    ///\n    /// Otherwise, the frame will only be accepted if there is no frame with the same priority already queued.\n    /// This is done to work around a hardware limitation that could lead to out-of-order delivery\n    /// of frames with the same priority.\n    pub fn try_write(&mut self, frame: &Frame) -> Result<TransmitStatus, TryWriteError> {\n        self.info.regs.transmit(frame).map_err(|_| TryWriteError::Full)\n    }\n\n    async fn flush_inner(&self, mb: Mailbox) {\n        poll_fn(|cx| {\n            self.info.state.lock(|s| {\n                s.borrow().tx_mode.register(cx.waker());\n            });\n            if self.info.regs.0.tsr().read().tme(mb.index()) {\n                return Poll::Ready(());\n            }\n\n            Poll::Pending\n        })\n        .await;\n    }\n\n    /// Waits for a specific transmit mailbox to become empty\n    pub async fn flush(&self, mb: Mailbox) {\n        self.flush_inner(mb).await\n    }\n\n    async fn flush_any_inner(&self) {\n        poll_fn(|cx| {\n            self.info.state.lock(|s| {\n                s.borrow().tx_mode.register(cx.waker());\n            });\n\n            let tsr = self.info.regs.0.tsr().read();\n            if tsr.tme(Mailbox::Mailbox0.index())\n                || tsr.tme(Mailbox::Mailbox1.index())\n                || tsr.tme(Mailbox::Mailbox2.index())\n            {\n                return Poll::Ready(());\n            }\n\n            Poll::Pending\n        })\n        .await;\n    }\n\n    /// Waits until any of the transmit mailboxes become empty\n    ///\n    /// Note that [`Self::try_write()`] may fail with [`TryWriteError::Full`],\n    /// even after the future returned by this function completes.\n    /// This will happen if FIFO scheduling of outgoing frames is not enabled,\n    /// and a frame with equal priority is already queued for transmission.\n    pub async fn flush_any(&self) {\n        self.flush_any_inner().await\n    }\n\n    async fn flush_all_inner(&self) {\n        poll_fn(|cx| {\n            self.info.state.lock(|s| {\n                s.borrow().tx_mode.register(cx.waker());\n            });\n\n            let tsr = self.info.regs.0.tsr().read();\n            if tsr.tme(Mailbox::Mailbox0.index())\n                && tsr.tme(Mailbox::Mailbox1.index())\n                && tsr.tme(Mailbox::Mailbox2.index())\n            {\n                return Poll::Ready(());\n            }\n\n            Poll::Pending\n        })\n        .await;\n    }\n\n    /// Waits until all of the transmit mailboxes become empty\n    pub async fn flush_all(&self) {\n        self.flush_all_inner().await\n    }\n\n    /// Attempts to abort the sending of a frame that is pending in a mailbox.\n    ///\n    /// If there is no frame in the provided mailbox, or its transmission succeeds before it can be\n    /// aborted, this function has no effect and returns `false`.\n    ///\n    /// If there is a frame in the provided mailbox, and it is canceled successfully, this function\n    /// returns `true`.\n    pub fn abort(&mut self, mailbox: Mailbox) -> bool {\n        self.info.regs.abort(mailbox)\n    }\n\n    /// Returns `true` if no frame is pending for transmission.\n    pub fn is_idle(&self) -> bool {\n        self.info.regs.is_idle()\n    }\n\n    /// Return a buffered instance of driver. User must supply Buffers\n    pub fn buffered<const TX_BUF_SIZE: usize>(\n        self,\n        txb: &'static mut TxBuf<TX_BUF_SIZE>,\n    ) -> BufferedCanTx<'d, TX_BUF_SIZE> {\n        BufferedCanTx::new(&self.info, self, txb)\n    }\n}\n\n/// User supplied buffer for TX buffering\npub type TxBuf<const BUF_SIZE: usize> = Channel<CriticalSectionRawMutex, Frame, BUF_SIZE>;\n\n/// Buffered CAN driver, transmit half.\npub struct BufferedCanTx<'d, const TX_BUF_SIZE: usize> {\n    info: TxInfoRef,\n    _tx: CanTx<'d>,\n    tx_buf: &'static TxBuf<TX_BUF_SIZE>,\n}\n\nimpl<'d, const TX_BUF_SIZE: usize> BufferedCanTx<'d, TX_BUF_SIZE> {\n    fn new(info: &'static Info, _tx: CanTx<'d>, tx_buf: &'static TxBuf<TX_BUF_SIZE>) -> Self {\n        Self {\n            info: TxInfoRef::new(info),\n            _tx,\n            tx_buf,\n        }\n        .setup()\n    }\n\n    fn setup(self) -> Self {\n        // We don't want interrupts being processed while we change modes.\n        critical_section::with(|_| {\n            let tx_inner = super::common::ClassicBufferedTxInner {\n                tx_receiver: self.tx_buf.receiver().into(),\n            };\n            self.info.state.lock(|s| {\n                s.borrow_mut().tx_mode = TxMode::Buffered(tx_inner);\n            });\n        });\n        self\n    }\n\n    /// Async write frame to TX buffer.\n    pub async fn write(&mut self, frame: &Frame) {\n        self.tx_buf.send(*frame).await;\n        let waker = self.info.tx_waker;\n        waker(); // Wake for Tx\n    }\n\n    /// Returns a sender that can be used for sending CAN frames.\n    pub fn writer(&self) -> BufferedCanSender {\n        BufferedCanSender {\n            tx_buf: self.tx_buf.sender().into(),\n            info: TxInfoRef::new(&self.info),\n        }\n    }\n}\n\n/// CAN driver, receive half.\n#[allow(dead_code)]\npub struct CanRx<'d> {\n    _phantom: PhantomData<&'d ()>,\n    info: RxInfoRef,\n}\n\nimpl<'d> CanRx<'d> {\n    /// Read a CAN frame.\n    ///\n    /// If no CAN frame is in the RX buffer, this will wait until there is one.\n    ///\n    /// Returns a tuple of the time the message was received and the message frame\n    pub async fn read(&mut self) -> Result<Envelope, BusError> {\n        RxMode::read(&self.info).await\n    }\n\n    /// Attempts to read a CAN frame without blocking.\n    ///\n    /// Returns [Err(TryReadError::Empty)] if there are no frames in the rx queue.\n    pub fn try_read(&mut self) -> Result<Envelope, TryReadError> {\n        RxMode::try_read(&self.info)\n    }\n\n    /// Waits while receive queue is empty.\n    pub async fn wait_not_empty(&mut self) {\n        RxMode::wait_not_empty(&self.info).await\n    }\n\n    /// Return a buffered instance of driver. User must supply Buffers\n    pub fn buffered<const RX_BUF_SIZE: usize>(\n        self,\n        rxb: &'static mut RxBuf<RX_BUF_SIZE>,\n    ) -> BufferedCanRx<'d, RX_BUF_SIZE> {\n        BufferedCanRx::new(&self.info, self, rxb)\n    }\n\n    /// Accesses the filter banks owned by this CAN peripheral.\n    ///\n    /// To modify filters of a slave peripheral, `modify_filters` has to be called on the master\n    /// peripheral instead.\n    pub fn modify_filters(&mut self) -> MasterFilters<'_> {\n        unsafe { MasterFilters::new(&self.info) }\n    }\n}\n\n/// User supplied buffer for RX Buffering\npub type RxBuf<const BUF_SIZE: usize> = Channel<CriticalSectionRawMutex, Result<Envelope, BusError>, BUF_SIZE>;\n\n/// CAN driver, receive half in Buffered mode.\npub struct BufferedCanRx<'d, const RX_BUF_SIZE: usize> {\n    info: RxInfoRef,\n    rx: CanRx<'d>,\n    rx_buf: &'static RxBuf<RX_BUF_SIZE>,\n}\n\nimpl<'d, const RX_BUF_SIZE: usize> BufferedCanRx<'d, RX_BUF_SIZE> {\n    fn new(info: &'static Info, rx: CanRx<'d>, rx_buf: &'static RxBuf<RX_BUF_SIZE>) -> Self {\n        BufferedCanRx {\n            info: RxInfoRef::new(info),\n            rx,\n            rx_buf,\n        }\n        .setup()\n    }\n\n    fn setup(self) -> Self {\n        // We don't want interrupts being processed while we change modes.\n        critical_section::with(|_| {\n            let rx_inner = super::common::ClassicBufferedRxInner {\n                rx_sender: self.rx_buf.sender().into(),\n            };\n            self.info.state.lock(|s| {\n                s.borrow_mut().rx_mode = RxMode::Buffered(rx_inner);\n            });\n        });\n        self\n    }\n\n    /// Async read frame from RX buffer.\n    pub async fn read(&mut self) -> Result<Envelope, BusError> {\n        self.rx_buf.receive().await\n    }\n\n    /// Attempts to read a CAN frame without blocking.\n    ///\n    /// Returns [Err(TryReadError::Empty)] if there are no frames in the rx queue.\n    pub fn try_read(&mut self) -> Result<Envelope, TryReadError> {\n        self.info.state.lock(|s| match &s.borrow().rx_mode {\n            RxMode::Buffered(_) => {\n                if let Ok(result) = self.rx_buf.try_receive() {\n                    match result {\n                        Ok(envelope) => Ok(envelope),\n                        Err(e) => Err(TryReadError::BusError(e)),\n                    }\n                } else {\n                    if let Some(err) = self.info.regs.curr_error() {\n                        return Err(TryReadError::BusError(err));\n                    } else {\n                        Err(TryReadError::Empty)\n                    }\n                }\n            }\n            _ => {\n                panic!(\"Bad Mode\")\n            }\n        })\n    }\n\n    /// Waits while receive queue is empty.\n    pub async fn wait_not_empty(&mut self) {\n        poll_fn(|cx| self.rx_buf.poll_ready_to_receive(cx)).await\n    }\n\n    /// Returns a receiver that can be used for receiving CAN frames. Note, each CAN frame will only be received by one receiver.\n    pub fn reader(&self) -> BufferedCanReceiver {\n        BufferedCanReceiver {\n            rx_buf: self.rx_buf.receiver().into(),\n            info: RxInfoRef::new(&self.info),\n        }\n    }\n\n    /// Accesses the filter banks owned by this CAN peripheral.\n    ///\n    /// To modify filters of a slave peripheral, `modify_filters` has to be called on the master\n    /// peripheral instead.\n    pub fn modify_filters(&mut self) -> MasterFilters<'_> {\n        self.rx.modify_filters()\n    }\n}\n\nimpl Drop for Can<'_> {\n    fn drop(&mut self) {\n        // Cannot call `free()` because it moves the instance.\n        // Manually reset the peripheral.\n        self.info.regs.0.mcr().write(|w| w.set_reset(true));\n        self.info.regs.enter_init_mode();\n        self.info.regs.leave_init_mode();\n        //rcc::disable::<T>();\n    }\n}\n\n/// Identifies one of the two receive FIFOs.\n#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub enum Fifo {\n    /// First receive FIFO\n    Fifo0 = 0,\n    /// Second receive FIFO\n    Fifo1 = 1,\n}\n\n/// Identifies one of the three transmit mailboxes.\n#[derive(Debug, Copy, Clone, Ord, PartialOrd, Eq, PartialEq)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub enum Mailbox {\n    /// Transmit mailbox 0\n    Mailbox0 = 0,\n    /// Transmit mailbox 1\n    Mailbox1 = 1,\n    /// Transmit mailbox 2\n    Mailbox2 = 2,\n}\n\n/// Contains information about a frame enqueued for transmission via [`Can::transmit`] or\n/// [`Tx::transmit`].\npub struct TransmitStatus {\n    dequeued_frame: Option<Frame>,\n    mailbox: Mailbox,\n}\n\nimpl TransmitStatus {\n    /// Returns the lower-priority frame that was dequeued to make space for the new frame.\n    #[inline]\n    pub fn dequeued_frame(&self) -> Option<&Frame> {\n        self.dequeued_frame.as_ref()\n    }\n\n    /// Returns the [`Mailbox`] the frame was enqueued in.\n    #[inline]\n    pub fn mailbox(&self) -> Mailbox {\n        self.mailbox\n    }\n}\n\npub(crate) enum RxMode {\n    NonBuffered(AtomicWaker),\n    Buffered(super::common::ClassicBufferedRxInner),\n}\n\nimpl RxMode {\n    pub fn on_interrupt<T: Instance>(&self, fifo: RxFifo) {\n        match self {\n            Self::NonBuffered(waker) => {\n                // Disable interrupts until read\n                let fifo_idx = match fifo {\n                    RxFifo::Fifo0 => 0usize,\n                    RxFifo::Fifo1 => 1usize,\n                };\n                T::regs().ier().modify(|w| {\n                    w.set_fmpie(fifo_idx, false);\n                });\n                waker.wake();\n            }\n            Self::Buffered(buf) => {\n                loop {\n                    match Registers(T::regs()).receive_fifo(fifo) {\n                        Some(envelope) => {\n                            // NOTE: consensus was reached that if rx_queue is full, packets should be dropped\n                            let _ = buf.rx_sender.try_send(Ok(envelope));\n                        }\n                        None => return,\n                    };\n                }\n            }\n        }\n    }\n\n    pub(crate) async fn read(info: &Info) -> Result<Envelope, BusError> {\n        poll_fn(|cx| {\n            info.state.lock(|state| {\n                let state = state.borrow();\n                state.err_waker.register(cx.waker());\n                match &state.rx_mode {\n                    Self::NonBuffered(waker) => {\n                        waker.register(cx.waker());\n                    }\n                    _ => {\n                        panic!(\"Bad Mode\")\n                    }\n                }\n            });\n            match RxMode::try_read(info) {\n                Ok(result) => Poll::Ready(Ok(result)),\n                Err(TryReadError::Empty) => Poll::Pending,\n                Err(TryReadError::BusError(be)) => Poll::Ready(Err(be)),\n            }\n        })\n        .await\n    }\n    pub(crate) fn try_read(info: &Info) -> Result<Envelope, TryReadError> {\n        info.state.lock(|state| match state.borrow().rx_mode {\n            Self::NonBuffered(_) => {\n                let registers = &info.regs;\n                if let Some(msg) = registers.receive_fifo(RxFifo::Fifo0) {\n                    registers.0.ier().modify(|w| {\n                        w.set_fmpie(0, true);\n                    });\n                    Ok(msg)\n                } else if let Some(msg) = registers.receive_fifo(RxFifo::Fifo1) {\n                    registers.0.ier().modify(|w| {\n                        w.set_fmpie(1, true);\n                    });\n                    Ok(msg)\n                } else if let Some(err) = registers.curr_error() {\n                    Err(TryReadError::BusError(err))\n                } else {\n                    registers.0.ier().modify(|w| {\n                        w.set_fmpie(0, true);\n                        w.set_fmpie(1, true);\n                    });\n                    Err(TryReadError::Empty)\n                }\n            }\n            _ => {\n                panic!(\"Bad Mode\")\n            }\n        })\n    }\n    pub(crate) async fn wait_not_empty(info: &Info) {\n        poll_fn(|cx| {\n            info.state.lock(|s| {\n                let state = s.borrow();\n                match &state.rx_mode {\n                    Self::NonBuffered(waker) => {\n                        waker.register(cx.waker());\n                    }\n                    _ => {\n                        panic!(\"Bad Mode\")\n                    }\n                }\n            });\n            if info.regs.receive_frame_available() {\n                Poll::Ready(())\n            } else {\n                Poll::Pending\n            }\n        })\n        .await\n    }\n}\n\npub(crate) enum TxMode {\n    NonBuffered(AtomicWaker),\n    Buffered(super::common::ClassicBufferedTxInner),\n}\n\nimpl TxMode {\n    pub fn buffer_free<T: Instance>(&self) -> bool {\n        let tsr = T::regs().tsr().read();\n        tsr.tme(Mailbox::Mailbox0.index()) || tsr.tme(Mailbox::Mailbox1.index()) || tsr.tme(Mailbox::Mailbox2.index())\n    }\n    pub fn on_interrupt<T: Instance>(&self) {\n        T::info().state.lock(|state| {\n            let tx_mode = &state.borrow().tx_mode;\n\n            match tx_mode {\n                TxMode::NonBuffered(waker) => waker.wake(),\n                TxMode::Buffered(buf) => {\n                    while self.buffer_free::<T>() {\n                        match buf.tx_receiver.try_receive() {\n                            Ok(frame) => {\n                                _ = Registers(T::regs()).transmit(&frame);\n                            }\n                            Err(_) => {\n                                break;\n                            }\n                        }\n                    }\n                }\n            }\n        });\n    }\n\n    fn register(&self, arg: &core::task::Waker) {\n        match self {\n            TxMode::NonBuffered(waker) => {\n                waker.register(arg);\n            }\n            _ => {\n                panic!(\"Bad mode\");\n            }\n        }\n    }\n}\n\npub(crate) struct State {\n    pub(crate) rx_mode: RxMode,\n    pub(crate) tx_mode: TxMode,\n    pub err_waker: AtomicWaker,\n    receiver_instance_count: usize,\n    sender_instance_count: usize,\n}\n\nimpl State {\n    pub const fn new() -> Self {\n        Self {\n            rx_mode: RxMode::NonBuffered(AtomicWaker::new()),\n            tx_mode: TxMode::NonBuffered(AtomicWaker::new()),\n            err_waker: AtomicWaker::new(),\n            receiver_instance_count: 1,\n            sender_instance_count: 1,\n        }\n    }\n}\n\ntype SharedState = embassy_sync::blocking_mutex::Mutex<CriticalSectionRawMutex, core::cell::RefCell<State>>;\npub(crate) struct Info {\n    regs: Registers,\n    tx_interrupt: crate::interrupt::Interrupt,\n    rx0_interrupt: crate::interrupt::Interrupt,\n    rx1_interrupt: crate::interrupt::Interrupt,\n    sce_interrupt: crate::interrupt::Interrupt,\n    pub(crate) tx_waker: fn(),\n    state: SharedState,\n\n    /// The total number of filter banks available to the instance.\n    ///\n    /// This is usually either 14 or 28, and should be specified in the chip's reference manual or datasheet.\n    num_filter_banks: u8,\n}\n\nimpl Info {\n    pub(crate) fn adjust_reference_counter(&self, val: RefCountOp) {\n        self.state.lock(|s| {\n            let mut mut_state = s.borrow_mut();\n            match val {\n                RefCountOp::NotifySenderCreated => {\n                    mut_state.sender_instance_count += 1;\n                }\n                RefCountOp::NotifySenderDestroyed => {\n                    mut_state.sender_instance_count -= 1;\n                    if 0 == mut_state.sender_instance_count {\n                        (*mut_state).tx_mode = TxMode::NonBuffered(embassy_sync::waitqueue::AtomicWaker::new());\n                    }\n                }\n                RefCountOp::NotifyReceiverCreated => {\n                    mut_state.receiver_instance_count += 1;\n                }\n                RefCountOp::NotifyReceiverDestroyed => {\n                    mut_state.receiver_instance_count -= 1;\n                    if 0 == mut_state.receiver_instance_count {\n                        (*mut_state).rx_mode = RxMode::NonBuffered(embassy_sync::waitqueue::AtomicWaker::new());\n                    }\n                }\n            }\n        });\n    }\n}\n\ntrait SealedInstance {\n    fn info() -> &'static Info;\n    fn regs() -> crate::pac::can::Can;\n}\n\n/// CAN instance trait.\n#[allow(private_bounds)]\npub trait Instance: SealedInstance + PeripheralType + RccPeripheral + 'static {\n    /// TX interrupt for this instance.\n    type TXInterrupt: crate::interrupt::typelevel::Interrupt;\n    /// RX0 interrupt for this instance.\n    type RX0Interrupt: crate::interrupt::typelevel::Interrupt;\n    /// RX1 interrupt for this instance.\n    type RX1Interrupt: crate::interrupt::typelevel::Interrupt;\n    /// SCE interrupt for this instance.\n    type SCEInterrupt: crate::interrupt::typelevel::Interrupt;\n}\n\n/// A bxCAN instance that owns filter banks.\n///\n/// In master-slave-instance setups, only the master instance owns the filter banks, and needs to\n/// split some of them off for use by the slave instance. In that case, the master instance should\n/// implement [`FilterOwner`] and [`MasterInstance`], while the slave instance should only implement\n/// [`Instance`].\n///\n/// In single-instance configurations, the instance owns all filter banks and they can not be split\n/// off. In that case, the instance should implement [`Instance`] and [`FilterOwner`].\n///\n/// # Safety\n///\n/// This trait must only be implemented if the instance does, in fact, own its associated filter\n/// banks, and `NUM_FILTER_BANKS` must be correct.\npub unsafe trait FilterOwner: Instance {\n    /// The total number of filter banks available to the instance.\n    ///\n    /// This is usually either 14 or 28, and should be specified in the chip's reference manual or datasheet.\n    const NUM_FILTER_BANKS: u8;\n}\n\n/// A bxCAN master instance that shares filter banks with a slave instance.\n///\n/// In master-slave-instance setups, this trait should be implemented for the master instance.\n///\n/// # Safety\n///\n/// This trait must only be implemented when there is actually an associated slave instance.\npub unsafe trait MasterInstance: FilterOwner {}\n\nforeach_peripheral!(\n    (can, $inst:ident) => {\n        impl SealedInstance for peripherals::$inst {\n\n            fn info() -> &'static Info {\n                static INFO: Info = Info {\n                    regs: Registers(crate::pac::$inst),\n                    tx_interrupt: crate::_generated::peripheral_interrupts::$inst::TX::IRQ,\n                    rx0_interrupt: crate::_generated::peripheral_interrupts::$inst::RX0::IRQ,\n                    rx1_interrupt: crate::_generated::peripheral_interrupts::$inst::RX1::IRQ,\n                    sce_interrupt: crate::_generated::peripheral_interrupts::$inst::SCE::IRQ,\n                    tx_waker: crate::_generated::peripheral_interrupts::$inst::TX::pend,\n                    num_filter_banks: peripherals::$inst::NUM_FILTER_BANKS,\n                    state: embassy_sync::blocking_mutex::Mutex::new(core::cell::RefCell::new(State::new())),\n                };\n                &INFO\n            }\n            fn regs() -> crate::pac::can::Can {\n                crate::pac::$inst\n            }\n        }\n\n        impl Instance for peripherals::$inst {\n            type TXInterrupt = crate::_generated::peripheral_interrupts::$inst::TX;\n            type RX0Interrupt = crate::_generated::peripheral_interrupts::$inst::RX0;\n            type RX1Interrupt = crate::_generated::peripheral_interrupts::$inst::RX1;\n            type SCEInterrupt = crate::_generated::peripheral_interrupts::$inst::SCE;\n        }\n    };\n);\n\nforeach_peripheral!(\n    (can, CAN) => {\n        unsafe impl FilterOwner for peripherals::CAN {\n            const NUM_FILTER_BANKS: u8 = 14;\n        }\n    };\n    // CAN1 and CAN2 is a combination of master and slave instance.\n    // CAN1 owns the filter bank and needs to be enabled in order\n    // for CAN2 to receive messages.\n    (can, CAN1) => {\n        cfg_if::cfg_if! {\n            if #[cfg(all(\n                any(stm32l4, stm32f72x, stm32f73x),\n                not(any(stm32l49x, stm32l4ax))\n            ))] {\n                // Most L4 devices and some F7 devices use the name \"CAN1\"\n                // even if there is no \"CAN2\" peripheral.\n                unsafe impl FilterOwner for peripherals::CAN1 {\n                    const NUM_FILTER_BANKS: u8 = 14;\n                }\n            } else {\n                unsafe impl FilterOwner for peripherals::CAN1 {\n                    const NUM_FILTER_BANKS: u8 = 28;\n                }\n                unsafe impl MasterInstance for peripherals::CAN1 {}\n            }\n        }\n    };\n    (can, CAN2) => {\n        unsafe impl FilterOwner for peripherals::CAN2 {\n            const NUM_FILTER_BANKS: u8 = 0;\n        }\n    };\n    (can, CAN3) => {\n        unsafe impl FilterOwner for peripherals::CAN3 {\n            const NUM_FILTER_BANKS: u8 = 14;\n        }\n    };\n);\n\npin_trait!(RxPin, Instance, @A);\npin_trait!(TxPin, Instance, @A);\n\ntrait Index {\n    fn index(&self) -> usize;\n}\n\nimpl Index for Mailbox {\n    fn index(&self) -> usize {\n        match self {\n            Mailbox::Mailbox0 => 0,\n            Mailbox::Mailbox1 => 1,\n            Mailbox::Mailbox2 => 2,\n        }\n    }\n}\n"
  },
  {
    "path": "embassy-stm32/src/can/bxcan/registers.rs",
    "content": "use core::cmp::Ordering;\nuse core::convert::Infallible;\n\npub use embedded_can::{ExtendedId, Id, StandardId};\nuse stm32_metapac::can::vals::{Lec, Rtr};\n\nuse super::{Mailbox, TransmitStatus};\nuse crate::can::enums::BusError;\nuse crate::can::frame::{Envelope, Frame, Header};\n\npub(crate) struct Registers(pub crate::pac::can::Can);\n\nimpl Registers {\n    pub fn enter_init_mode(&self) {\n        self.0.mcr().modify(|reg| {\n            reg.set_sleep(false);\n            reg.set_inrq(true);\n        });\n        loop {\n            let msr = self.0.msr().read();\n            if !msr.slak() && msr.inak() {\n                break;\n            }\n        }\n    }\n\n    // Leaves initialization mode, enters sleep mode.\n    pub fn leave_init_mode(&self) {\n        self.0.mcr().modify(|reg| {\n            reg.set_sleep(true);\n            reg.set_inrq(false);\n        });\n        loop {\n            let msr = self.0.msr().read();\n            if msr.slak() && !msr.inak() {\n                break;\n            }\n        }\n    }\n\n    pub fn set_bit_timing(&self, bt: crate::can::util::NominalBitTiming) {\n        let prescaler = u16::from(bt.prescaler) & 0x1FF;\n        let seg1 = u8::from(bt.seg1);\n        let seg2 = u8::from(bt.seg2) & 0x7F;\n        let sync_jump_width = u8::from(bt.sync_jump_width) & 0x7F;\n        self.0.btr().modify(|reg| {\n            reg.set_brp(prescaler - 1);\n            reg.set_ts(0, seg1 - 1);\n            reg.set_ts(1, seg2 - 1);\n            reg.set_sjw(sync_jump_width - 1);\n        });\n    }\n\n    /// Enables or disables silent mode: Disconnects the TX signal from the pin.\n    pub fn set_silent(&self, enabled: bool) {\n        let mode = match enabled {\n            false => stm32_metapac::can::vals::Silm::NORMAL,\n            true => stm32_metapac::can::vals::Silm::SILENT,\n        };\n        self.0.btr().modify(|reg| reg.set_silm(mode));\n    }\n\n    /// Enables or disables automatic retransmission of messages.\n    ///\n    /// If this is enabled, the CAN peripheral will automatically try to retransmit each frame\n    /// until it can be sent. Otherwise, it will try only once to send each frame.\n    ///\n    /// Automatic retransmission is enabled by default.\n    pub fn set_automatic_retransmit(&self, enabled: bool) {\n        self.0.mcr().modify(|reg| reg.set_nart(enabled));\n    }\n\n    /// Enables or disables loopback mode: Internally connects the TX and RX\n    /// signals together.\n    pub fn set_loopback(&self, enabled: bool) {\n        self.0.btr().modify(|reg| reg.set_lbkm(enabled));\n    }\n\n    /// Configures the automatic wake-up feature.\n    ///\n    /// This is turned off by default.\n    ///\n    /// When turned on, an incoming frame will cause the peripheral to wake up from sleep and\n    /// receive the frame. If enabled, [`Interrupt::Wakeup`] will also be triggered by the incoming\n    /// frame.\n    #[allow(dead_code)]\n    pub fn set_automatic_wakeup(&self, enabled: bool) {\n        self.0.mcr().modify(|reg| reg.set_awum(enabled));\n    }\n\n    /// Leaves initialization mode and enables the peripheral (non-blocking version).\n    ///\n    /// Usually, it is recommended to call [`CanConfig::enable`] instead. This method is only needed\n    /// if you want non-blocking initialization.\n    ///\n    /// If this returns [`WouldBlock`][nb::Error::WouldBlock], the peripheral will enable itself\n    /// in the background. The peripheral is enabled and ready to use when this method returns\n    /// successfully.\n    pub fn enable_non_blocking(&self) -> nb::Result<(), Infallible> {\n        let msr = self.0.msr().read();\n        if msr.slak() {\n            self.0.mcr().modify(|reg| {\n                reg.set_abom(true);\n                reg.set_sleep(false);\n            });\n            Err(nb::Error::WouldBlock)\n        } else {\n            Ok(())\n        }\n    }\n\n    /// Puts the peripheral in a sleep mode to save power.\n    ///\n    /// While in sleep mode, an incoming CAN frame will trigger [`Interrupt::Wakeup`] if enabled.\n    #[allow(dead_code)]\n    pub fn sleep(&mut self) {\n        self.0.mcr().modify(|reg| {\n            reg.set_sleep(true);\n            reg.set_inrq(false);\n        });\n        loop {\n            let msr = self.0.msr().read();\n            if msr.slak() && !msr.inak() {\n                break;\n            }\n        }\n    }\n\n    /// Wakes up from sleep mode.\n    ///\n    /// Note that this will not trigger [`Interrupt::Wakeup`], only reception of an incoming CAN\n    /// frame will cause that interrupt.\n    #[allow(dead_code)]\n    pub fn wakeup(&self) {\n        self.0.mcr().modify(|reg| {\n            reg.set_sleep(false);\n            reg.set_inrq(false);\n        });\n        loop {\n            let msr = self.0.msr().read();\n            if !msr.slak() && !msr.inak() {\n                break;\n            }\n        }\n    }\n\n    pub fn curr_error(&self) -> Option<BusError> {\n        if !self.0.msr().read().erri() {\n            // This ensures that once a single error instance has\n            // been acknowledged and forwared to the bus message consumer\n            // we don't continue to re-forward the same error occurrance for an\n            // in-definite amount of time.\n            return None;\n        }\n\n        // Since we have not already acknowledge the error, and the interrupt was\n        // disabled in the ISR, we will acknowledge the current error and re-enable the interrupt\n        // so futher errors are captured\n        self.0.msr().modify(|m| m.set_erri(true));\n        self.0.ier().modify(|i| i.set_errie(true));\n\n        let err = self.0.esr().read();\n        if err.lec() != Lec::NO_ERROR {\n            return Some(match err.lec() {\n                Lec::STUFF => BusError::Stuff,\n                Lec::FORM => BusError::Form,\n                Lec::ACK => BusError::Acknowledge,\n                Lec::BIT_RECESSIVE => BusError::BitRecessive,\n                Lec::BIT_DOMINANT => BusError::BitDominant,\n                Lec::CRC => BusError::Crc,\n                Lec::CUSTOM => BusError::Software,\n                Lec::NO_ERROR => unreachable!(),\n            });\n        }\n        None\n    }\n\n    /// Enables or disables FIFO scheduling of outgoing mailboxes.\n    ///\n    /// If this is enabled, mailboxes are scheduled based on the time when the transmit request bit of the mailbox was set.\n    ///\n    /// If this is disabled, mailboxes are scheduled based on the priority of the frame in the mailbox.\n    pub fn set_tx_fifo_scheduling(&self, enabled: bool) {\n        self.0.mcr().modify(|w| w.set_txfp(enabled))\n    }\n\n    /// Checks if FIFO scheduling of outgoing mailboxes is enabled.\n    pub fn tx_fifo_scheduling_enabled(&self) -> bool {\n        self.0.mcr().read().txfp()\n    }\n\n    /// Puts a CAN frame in a transmit mailbox for transmission on the bus.\n    ///\n    /// The behavior of this function depends on wheter or not FIFO scheduling is enabled.\n    /// See [`Self::set_tx_fifo_scheduling()`] and [`Self::tx_fifo_scheduling_enabled()`].\n    ///\n    /// # Priority based scheduling\n    ///\n    /// If FIFO scheduling is disabled, frames are transmitted to the bus based on their\n    /// priority (see [`FramePriority`]). Transmit order is preserved for frames with identical\n    /// priority.\n    ///\n    /// If all transmit mailboxes are full, and `frame` has a higher priority than the\n    /// lowest-priority message in the transmit mailboxes, transmission of the enqueued frame is\n    /// cancelled and `frame` is enqueued instead. The frame that was replaced is returned as\n    /// [`TransmitStatus::dequeued_frame`].\n    ///\n    /// # FIFO scheduling\n    ///\n    /// If FIFO scheduling is enabled, frames are transmitted in the order that they are passed to this function.\n    ///\n    /// If all transmit mailboxes are full, this function returns [`nb::Error::WouldBlock`].\n    pub fn transmit(&self, frame: &Frame) -> nb::Result<TransmitStatus, Infallible> {\n        // Check if FIFO scheduling is enabled.\n        let fifo_scheduling = self.0.mcr().read().txfp();\n\n        // Get the index of the next free mailbox or the one with the lowest priority.\n        let tsr = self.0.tsr().read();\n        let idx = tsr.code() as usize;\n\n        let frame_is_pending = !tsr.tme(0) || !tsr.tme(1) || !tsr.tme(2);\n        let all_frames_are_pending = !tsr.tme(0) && !tsr.tme(1) && !tsr.tme(2);\n\n        let pending_frame;\n        if fifo_scheduling && all_frames_are_pending {\n            // FIFO scheduling is enabled and all mailboxes are full.\n            // We will not drop a lower priority frame, we just report WouldBlock.\n            return Err(nb::Error::WouldBlock);\n        } else if !fifo_scheduling && frame_is_pending {\n            // Priority scheduling is enabled and alteast one mailbox is full.\n            //\n            // In this mode, the peripheral transmits high priority frames first.\n            // Frames with identical priority should be transmitted in FIFO order,\n            // but the controller schedules pending frames of same priority based on the\n            // mailbox index. As a workaround check all pending mailboxes and only accept\n            // frames with a different priority.\n            self.check_priority(0, frame.id().into())?;\n            self.check_priority(1, frame.id().into())?;\n            self.check_priority(2, frame.id().into())?;\n\n            if all_frames_are_pending {\n                // No free mailbox is available. This can only happen when three frames with\n                // ascending priority (descending IDs) were requested for transmission and all\n                // of them are blocked by bus traffic with even higher priority.\n                // To prevent a priority inversion abort and replace the lowest priority frame.\n                pending_frame = self.read_pending_mailbox(idx);\n            } else {\n                // There was a free mailbox.\n                pending_frame = None;\n            }\n        } else {\n            // Either we have FIFO scheduling and at-least one free mailbox,\n            // or we have priority scheduling and all mailboxes are free.\n            // No further checks are needed.\n            pending_frame = None\n        }\n\n        self.write_mailbox(idx, frame);\n\n        let mailbox = match idx {\n            0 => Mailbox::Mailbox0,\n            1 => Mailbox::Mailbox1,\n            2 => Mailbox::Mailbox2,\n            _ => unreachable!(),\n        };\n        Ok(TransmitStatus {\n            dequeued_frame: pending_frame,\n            mailbox,\n        })\n    }\n\n    /// Returns `Ok` when the mailbox is free or if it contains pending frame with a\n    /// different priority from the identifier `id`.\n    fn check_priority(&self, idx: usize, id: IdReg) -> nb::Result<(), Infallible> {\n        // Read the pending frame's id to check its priority.\n        assert!(idx < 3);\n        let tir = &self.0.tx(idx).tir().read();\n\n        // Check the priority by comparing the identifiers. But first make sure the\n        // frame has not finished the transmission (`TXRQ` == 0) in the meantime.\n        if tir.txrq() && id == IdReg::from_register(tir.0) {\n            // There's a mailbox whose priority is equal to the priority of the new frame.\n            return Err(nb::Error::WouldBlock);\n        }\n\n        Ok(())\n    }\n\n    fn write_mailbox(&self, idx: usize, frame: &Frame) {\n        debug_assert!(idx < 3);\n\n        let mb = self.0.tx(idx);\n        mb.tdtr().write(|w| w.set_dlc(frame.header().len() as u8));\n\n        mb.tdlr()\n            .write(|w| w.0 = u32::from_ne_bytes(unwrap!(frame.raw_data()[0..4].try_into())));\n        mb.tdhr()\n            .write(|w| w.0 = u32::from_ne_bytes(unwrap!(frame.raw_data()[4..8].try_into())));\n        let id: IdReg = frame.id().into();\n        mb.tir().write(|w| {\n            w.0 = id.0;\n            w.set_txrq(true);\n            if frame.header().rtr() {\n                w.set_rtr(Rtr::REMOTE);\n            }\n        });\n    }\n\n    fn read_pending_mailbox(&self, idx: usize) -> Option<Frame> {\n        if self.abort_by_index(idx) {\n            debug_assert!(idx < 3);\n\n            let mb = self.0.tx(idx);\n\n            let id = IdReg(mb.tir().read().0);\n            let mut data = [0xff; 8];\n            data[0..4].copy_from_slice(&mb.tdlr().read().0.to_ne_bytes());\n            data[4..8].copy_from_slice(&mb.tdhr().read().0.to_ne_bytes());\n            let len = mb.tdtr().read().dlc();\n\n            Some(unwrap!(Frame::new(Header::new(id.id(), len, id.rtr()), &data)))\n        } else {\n            // Abort request failed because the frame was already sent (or being sent) on\n            // the bus. All mailboxes are now free. This can happen for small prescaler\n            // values (e.g. 1MBit/s bit timing with a source clock of 8MHz) or when an ISR\n            // has preempted the execution.\n            None\n        }\n    }\n\n    /// Tries to abort a pending frame. Returns `true` when aborted.\n    fn abort_by_index(&self, idx: usize) -> bool {\n        self.0.tsr().write(|reg| reg.set_abrq(idx, true));\n\n        // Wait for the abort request to be finished.\n        loop {\n            let tsr = self.0.tsr().read();\n            if false == tsr.abrq(idx) {\n                break tsr.txok(idx) == false;\n            }\n        }\n    }\n\n    /// Attempts to abort the sending of a frame that is pending in a mailbox.\n    ///\n    /// If there is no frame in the provided mailbox, or its transmission succeeds before it can be\n    /// aborted, this function has no effect and returns `false`.\n    ///\n    /// If there is a frame in the provided mailbox, and it is canceled successfully, this function\n    /// returns `true`.\n    pub fn abort(&self, mailbox: Mailbox) -> bool {\n        // If the mailbox is empty, the value of TXOKx depends on what happened with the previous\n        // frame in that mailbox. Only call abort_by_index() if the mailbox is not empty.\n        let tsr = self.0.tsr().read();\n        let mailbox_empty = match mailbox {\n            Mailbox::Mailbox0 => tsr.tme(0),\n            Mailbox::Mailbox1 => tsr.tme(1),\n            Mailbox::Mailbox2 => tsr.tme(2),\n        };\n        if mailbox_empty {\n            false\n        } else {\n            self.abort_by_index(mailbox as usize)\n        }\n    }\n\n    /// Returns `true` if no frame is pending for transmission.\n    pub fn is_idle(&self) -> bool {\n        let tsr = self.0.tsr().read();\n        tsr.tme(0) && tsr.tme(1) && tsr.tme(2)\n    }\n\n    pub fn receive_frame_available(&self) -> bool {\n        if self.0.rfr(0).read().fmp() != 0 {\n            true\n        } else if self.0.rfr(1).read().fmp() != 0 {\n            true\n        } else {\n            false\n        }\n    }\n\n    pub fn receive_fifo(&self, fifo: RxFifo) -> Option<Envelope> {\n        // Generate timestamp as early as possible\n        #[cfg(feature = \"time\")]\n        let ts = embassy_time::Instant::now();\n\n        use crate::pac::can::vals::Ide;\n\n        let fifo_idx = match fifo {\n            RxFifo::Fifo0 => 0usize,\n            RxFifo::Fifo1 => 1usize,\n        };\n        let rfr = self.0.rfr(fifo_idx);\n        let fifo = self.0.rx(fifo_idx);\n\n        // If there are no pending messages, there is nothing to do\n        if rfr.read().fmp() == 0 {\n            return None;\n        }\n\n        let rir = fifo.rir().read();\n        let id: embedded_can::Id = if rir.ide() == Ide::STANDARD {\n            unwrap!(embedded_can::StandardId::new(rir.stid())).into()\n        } else {\n            let stid = (rir.stid() & 0x7FF) as u32;\n            let exid = rir.exid() & 0x3FFFF;\n            let id = (stid << 18) | (exid);\n            unwrap!(embedded_can::ExtendedId::new(id)).into()\n        };\n        let rdtr = fifo.rdtr().read();\n        let data_len = rdtr.dlc();\n        let rtr = rir.rtr() == stm32_metapac::can::vals::Rtr::REMOTE;\n\n        #[cfg(not(feature = \"time\"))]\n        let ts = rdtr.time();\n\n        let mut data: [u8; 8] = [0; 8];\n        data[0..4].copy_from_slice(&fifo.rdlr().read().0.to_ne_bytes());\n        data[4..8].copy_from_slice(&fifo.rdhr().read().0.to_ne_bytes());\n\n        let frame = unwrap!(Frame::new(Header::new(id, data_len, rtr), &data));\n        let envelope = Envelope { ts, frame };\n\n        rfr.modify(|v| v.set_rfom(true));\n\n        Some(envelope)\n    }\n}\n\n/// Identifier of a CAN message.\n///\n/// Can be either a standard identifier (11bit, Range: 0..0x3FF) or a\n/// extendended identifier (29bit , Range: 0..0x1FFFFFFF).\n///\n/// The `Ord` trait can be used to determine the frame’s priority this ID\n/// belongs to.\n/// Lower identifier values have a higher priority. Additionally standard frames\n/// have a higher priority than extended frames and data frames have a higher\n/// priority than remote frames.\n#[derive(Clone, Copy, Debug, PartialEq, Eq)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub(crate) struct IdReg(u32);\n\nimpl IdReg {\n    const STANDARD_SHIFT: u32 = 21;\n\n    const EXTENDED_SHIFT: u32 = 3;\n\n    const IDE_MASK: u32 = 0x0000_0004;\n\n    const RTR_MASK: u32 = 0x0000_0002;\n\n    /// Creates a new standard identifier (11bit, Range: 0..0x7FF)\n    ///\n    /// Panics for IDs outside the allowed range.\n    fn new_standard(id: StandardId) -> Self {\n        Self(u32::from(id.as_raw()) << Self::STANDARD_SHIFT)\n    }\n\n    /// Creates a new extendended identifier (29bit , Range: 0..0x1FFFFFFF).\n    ///\n    /// Panics for IDs outside the allowed range.\n    fn new_extended(id: ExtendedId) -> IdReg {\n        Self(id.as_raw() << Self::EXTENDED_SHIFT | Self::IDE_MASK)\n    }\n\n    fn from_register(reg: u32) -> IdReg {\n        Self(reg & 0xFFFF_FFFE)\n    }\n\n    /// Returns the identifier.\n    fn to_id(self) -> Id {\n        if self.is_extended() {\n            Id::Extended(unsafe { ExtendedId::new_unchecked(self.0 >> Self::EXTENDED_SHIFT) })\n        } else {\n            Id::Standard(unsafe { StandardId::new_unchecked((self.0 >> Self::STANDARD_SHIFT) as u16) })\n        }\n    }\n\n    /// Returns the identifier.\n    fn id(self) -> embedded_can::Id {\n        if self.is_extended() {\n            unwrap!(embedded_can::ExtendedId::new(self.0 >> Self::EXTENDED_SHIFT)).into()\n        } else {\n            unwrap!(embedded_can::StandardId::new((self.0 >> Self::STANDARD_SHIFT) as u16)).into()\n        }\n    }\n\n    /// Returns `true` if the identifier is an extended identifier.\n    fn is_extended(self) -> bool {\n        self.0 & Self::IDE_MASK != 0\n    }\n\n    /// Returns `true` if the identifer is part of a remote frame (RTR bit set).\n    fn rtr(self) -> bool {\n        self.0 & Self::RTR_MASK != 0\n    }\n}\n\nimpl From<&embedded_can::Id> for IdReg {\n    fn from(eid: &embedded_can::Id) -> Self {\n        match eid {\n            embedded_can::Id::Standard(id) => IdReg::new_standard(StandardId::new(id.as_raw()).unwrap()),\n            embedded_can::Id::Extended(id) => IdReg::new_extended(ExtendedId::new(id.as_raw()).unwrap()),\n        }\n    }\n}\n\nimpl From<IdReg> for embedded_can::Id {\n    fn from(idr: IdReg) -> Self {\n        idr.id()\n    }\n}\n\n/// `IdReg` is ordered by priority.\nimpl Ord for IdReg {\n    fn cmp(&self, other: &Self) -> Ordering {\n        // When the IDs match, data frames have priority over remote frames.\n        let rtr = self.rtr().cmp(&other.rtr()).reverse();\n\n        let id_a = self.to_id();\n        let id_b = other.to_id();\n        match (id_a, id_b) {\n            (Id::Standard(a), Id::Standard(b)) => {\n                // Lower IDs have priority over higher IDs.\n                a.as_raw().cmp(&b.as_raw()).reverse().then(rtr)\n            }\n            (Id::Extended(a), Id::Extended(b)) => a.as_raw().cmp(&b.as_raw()).reverse().then(rtr),\n            (Id::Standard(a), Id::Extended(b)) => {\n                // Standard frames have priority over extended frames if their Base IDs match.\n                a.as_raw()\n                    .cmp(&b.standard_id().as_raw())\n                    .reverse()\n                    .then(Ordering::Greater)\n            }\n            (Id::Extended(a), Id::Standard(b)) => {\n                a.standard_id().as_raw().cmp(&b.as_raw()).reverse().then(Ordering::Less)\n            }\n        }\n    }\n}\n\nimpl PartialOrd for IdReg {\n    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {\n        Some(self.cmp(other))\n    }\n}\n\n#[derive(Debug, Copy, Clone, Eq, PartialEq)]\npub(crate) enum RxFifo {\n    Fifo0,\n    Fifo1,\n}\n"
  },
  {
    "path": "embassy-stm32/src/can/common.rs",
    "content": "use embassy_sync::channel::{SendDynamicReceiver, SendDynamicSender};\n\nuse super::enums::*;\nuse super::frame::*;\n\npub(crate) struct ClassicBufferedRxInner {\n    pub rx_sender: SendDynamicSender<'static, Result<Envelope, BusError>>,\n}\npub(crate) struct ClassicBufferedTxInner {\n    pub tx_receiver: SendDynamicReceiver<'static, Frame>,\n}\n\n#[cfg(any(can_fdcan_v1, can_fdcan_h7))]\n\npub(crate) struct FdBufferedRxInner {\n    pub rx_sender: SendDynamicSender<'static, Result<FdEnvelope, BusError>>,\n}\n\n#[cfg(any(can_fdcan_v1, can_fdcan_h7))]\npub(crate) struct FdBufferedTxInner {\n    pub tx_receiver: SendDynamicReceiver<'static, FdFrame>,\n}\n\n/// Sender that can be used for sending CAN frames.\npub struct BufferedSender<'ch, FRAME> {\n    pub(crate) tx_buf: embassy_sync::channel::SendDynamicSender<'ch, FRAME>,\n    pub(crate) info: TxInfoRef,\n}\n\nimpl<'ch, FRAME> BufferedSender<'ch, FRAME> {\n    /// Async write frame to TX buffer.\n    pub fn try_write(&mut self, frame: FRAME) -> Result<(), embassy_sync::channel::TrySendError<FRAME>> {\n        self.tx_buf.try_send(frame)?;\n        (self.info.tx_waker)();\n        Ok(())\n    }\n\n    /// Async write frame to TX buffer.\n    pub async fn write(&mut self, frame: FRAME) {\n        self.tx_buf.send(frame).await;\n        (self.info.tx_waker)();\n    }\n\n    /// Allows a poll_fn to poll until the channel is ready to write\n    pub fn poll_ready_to_send(&self, cx: &mut core::task::Context<'_>) -> core::task::Poll<()> {\n        self.tx_buf.poll_ready_to_send(cx)\n    }\n}\n\nimpl<'ch, FRAME> Clone for BufferedSender<'ch, FRAME> {\n    fn clone(&self) -> Self {\n        Self {\n            tx_buf: self.tx_buf,\n            info: TxInfoRef::new(&self.info),\n        }\n    }\n}\n\n/// Sender that can be used for sending Classic CAN frames.\npub type BufferedCanSender = BufferedSender<'static, Frame>;\n\n/// Receiver that can be used for receiving CAN frames. Note, each CAN frame will only be received by one receiver.\npub struct BufferedReceiver<'ch, ENVELOPE> {\n    pub(crate) rx_buf: embassy_sync::channel::SendDynamicReceiver<'ch, Result<ENVELOPE, BusError>>,\n    pub(crate) info: RxInfoRef,\n}\n\nimpl<'ch, ENVELOPE> BufferedReceiver<'ch, ENVELOPE> {\n    /// Receive the next frame.\n    ///\n    /// See [`Channel::receive()`].\n    pub fn receive(&self) -> embassy_sync::channel::DynamicReceiveFuture<'_, Result<ENVELOPE, BusError>> {\n        self.rx_buf.receive()\n    }\n\n    /// Attempt to immediately receive the next frame.\n    ///\n    /// See [`Channel::try_receive()`]\n    pub fn try_receive(&self) -> Result<Result<ENVELOPE, BusError>, embassy_sync::channel::TryReceiveError> {\n        self.rx_buf.try_receive()\n    }\n\n    /// Allows a poll_fn to poll until the channel is ready to receive\n    ///\n    /// See [`Channel::poll_ready_to_receive()`]\n    pub fn poll_ready_to_receive(&self, cx: &mut core::task::Context<'_>) -> core::task::Poll<()> {\n        self.rx_buf.poll_ready_to_receive(cx)\n    }\n\n    /// Poll the channel for the next frame\n    ///\n    /// See [`Channel::poll_receive()`]\n    pub fn poll_receive(&self, cx: &mut core::task::Context<'_>) -> core::task::Poll<Result<ENVELOPE, BusError>> {\n        self.rx_buf.poll_receive(cx)\n    }\n}\n\nimpl<'ch, ENVELOPE> Clone for BufferedReceiver<'ch, ENVELOPE> {\n    fn clone(&self) -> Self {\n        Self {\n            rx_buf: self.rx_buf,\n            info: RxInfoRef::new(&self.info),\n        }\n    }\n}\n\n/// A BufferedCanReceiver for Classic CAN frames.\npub type BufferedCanReceiver = BufferedReceiver<'static, Envelope>;\n\n/// Provides a reference to the driver internals and implements RAII for the internal reference\n/// counting. Each type that can operate on the driver should contain either InfoRef\n/// or the similar TxInfoRef or RxInfoRef. The new method and the Drop impl will automatically\n/// call the reference counting function. Like this, the reference counting function does not\n/// need to be called manually for each type.\npub(crate) struct InfoRef {\n    info: &'static super::Info,\n}\nimpl InfoRef {\n    pub(crate) fn new(info: &'static super::Info) -> Self {\n        info.adjust_reference_counter(RefCountOp::NotifyReceiverCreated);\n        info.adjust_reference_counter(RefCountOp::NotifySenderCreated);\n        Self { info }\n    }\n}\n\nimpl Drop for InfoRef {\n    fn drop(&mut self) {\n        self.info.adjust_reference_counter(RefCountOp::NotifyReceiverDestroyed);\n        self.info.adjust_reference_counter(RefCountOp::NotifySenderDestroyed);\n    }\n}\n\nimpl core::ops::Deref for InfoRef {\n    type Target = &'static super::Info;\n\n    fn deref(&self) -> &Self::Target {\n        &self.info\n    }\n}\n\n/// Provides a reference to the driver internals and implements RAII for the internal reference\n/// counting for Tx only types.\n/// See InfoRef for further doc.\npub(crate) struct TxInfoRef {\n    info: &'static super::Info,\n}\n\nimpl TxInfoRef {\n    pub(crate) fn new(info: &'static super::Info) -> Self {\n        info.adjust_reference_counter(RefCountOp::NotifySenderCreated);\n        Self { info }\n    }\n}\n\nimpl Drop for TxInfoRef {\n    fn drop(&mut self) {\n        self.info.adjust_reference_counter(RefCountOp::NotifySenderDestroyed);\n    }\n}\n\nimpl core::ops::Deref for TxInfoRef {\n    type Target = &'static super::Info;\n\n    fn deref(&self) -> &Self::Target {\n        &self.info\n    }\n}\n\n/// Provides a reference to the driver internals and implements RAII for the internal reference\n/// counting for Rx only types.\n/// See InfoRef for further doc.\npub(crate) struct RxInfoRef {\n    info: &'static super::Info,\n}\n\nimpl RxInfoRef {\n    pub(crate) fn new(info: &'static super::Info) -> Self {\n        info.adjust_reference_counter(RefCountOp::NotifyReceiverCreated);\n        Self { info }\n    }\n}\n\nimpl Drop for RxInfoRef {\n    fn drop(&mut self) {\n        self.info.adjust_reference_counter(RefCountOp::NotifyReceiverDestroyed);\n    }\n}\n\nimpl core::ops::Deref for RxInfoRef {\n    type Target = &'static super::Info;\n\n    fn deref(&self) -> &Self::Target {\n        &self.info\n    }\n}\n"
  },
  {
    "path": "embassy-stm32/src/can/enums.rs",
    "content": "//! Enums shared between CAN controller types.\n\n/// Bus error\n#[derive(Debug)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub enum BusError {\n    /// Bit stuffing error - more than 5 equal bits\n    Stuff,\n    /// Form error - A fixed format part of a received message has wrong format\n    Form,\n    /// The message transmitted by the FDCAN was not acknowledged by another node.\n    Acknowledge,\n    /// Bit0Error: During the transmission of a message the device wanted to send a dominant level\n    /// but the monitored bus value was recessive.\n    BitRecessive,\n    /// Bit1Error: During the transmission of a message the device wanted to send a recessive level\n    /// but the monitored bus value was dominant.\n    BitDominant,\n    /// The CRC check sum of a received message was incorrect. The CRC of an\n    /// incoming message does not match with the CRC calculated from the received data.\n    Crc,\n    /// A software error occured. Exclusive to BXCAN.\n    Software,\n}\n\n/// Bus error modes.\n///\n/// Contrary to the `BusError` enum which also includes last-seen acute protocol\n/// errors, this enum includes only the mutually exclusive bus error modes.\n#[derive(Debug)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub enum BusErrorMode {\n    /// Error active mode (default). Controller will transmit an active error\n    /// frame upon protocol error.\n    ErrorActive,\n    /// Error passive mode. An error counter exceeded 127. Controller will\n    /// transmit a passive error frame upon protocol error.\n    ErrorPassive,\n    /// Bus off mode. The transmit error counter exceeded 255. Controller is not\n    /// participating in bus traffic.\n    BusOff,\n}\n\n/// Frame Create Errors\n#[derive(Debug)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub enum FrameCreateError {\n    /// Data in header does not match supplied.\n    NotEnoughData,\n    /// Invalid data length not 0-8 for Classic packet or valid for FD.\n    InvalidDataLength,\n    /// Invalid ID.\n    InvalidCanId,\n}\n\n/// Error returned by `try_read`\n#[derive(Debug)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub enum TryReadError {\n    /// Bus error\n    BusError(BusError),\n    /// Receive buffer is empty\n    Empty,\n}\n\n/// Internal Operation\n#[derive(Debug)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub enum RefCountOp {\n    /// Notify receiver created\n    NotifyReceiverCreated,\n    /// Notify receiver destroyed\n    NotifyReceiverDestroyed,\n    /// Notify sender created\n    NotifySenderCreated,\n    /// Notify sender destroyed\n    NotifySenderDestroyed,\n}\n\n/// Error returned when calculating the can timing fails\n#[derive(Debug)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub enum TimingCalcError {\n    /// Bitrate is lower than 1000\n    BitrateTooLow {\n        /// The set bitrate\n        bitrate: u32,\n    },\n    /// No solution possible\n    NoSolution {\n        /// The sum of BS1 and BS2\n        bs1_bs2_sum: u8,\n    },\n    /// Prescaler is not 1 < prescaler < 1024\n    InvalidPrescaler {\n        /// The calculated prescaler value\n        prescaler: u32,\n    },\n    /// BS1 or BS2 are not in the range 0 < BSx < BSx_MAX\n    BSNotInRange {\n        /// The value of BS1\n        bs1: u8,\n        /// The value of BS2\n        bs2: u8,\n    },\n    /// Final bitrate doesn't match the requested bitrate\n    NoMatch {\n        /// The requested bitrate\n        requested: u32,\n        /// The calculated bitrate\n        final_calculated: u32,\n    },\n    /// core::num::NonZeroUxx::new error\n    CoreNumNew,\n}\n"
  },
  {
    "path": "embassy-stm32/src/can/fd/config.rs",
    "content": "//! Configuration for FDCAN Module\n// Note: This file is copied and modified from fdcan crate by Richard Meadows\n\nuse core::num::{NonZeroU8, NonZeroU16};\n\n/// Configures the bit timings.\n///\n/// You can use <http://www.bittiming.can-wiki.info/> to calculate the `btr` parameter. Enter\n/// parameters as follows:\n///\n/// - *Clock Rate*: The input clock speed to the CAN peripheral (*not* the CPU clock speed).\n///   This is the clock rate of the peripheral bus the CAN peripheral is attached to (eg. APB1).\n/// - *Sample Point*: Should normally be left at the default value of 87.5%.\n/// - *SJW*: Should normally be left at the default value of 1.\n///\n/// Then copy the `CAN_BUS_TIME` register value from the table and pass it as the `btr`\n/// parameter to this method.\n#[derive(Clone, Copy, Debug)]\npub struct NominalBitTiming {\n    /// Value by which the oscillator frequency is divided for generating the bit time quanta. The bit\n    /// time is built up from a multiple of this quanta. Valid values are 1 to 512.\n    pub prescaler: NonZeroU16,\n    /// Valid values are 1 to 128.\n    pub seg1: NonZeroU8,\n    /// Valid values are 1 to 255.\n    pub seg2: NonZeroU8,\n    /// Valid values are 1 to 128.\n    pub sync_jump_width: NonZeroU8,\n}\nimpl NominalBitTiming {\n    #[inline]\n    pub(crate) fn nbrp(&self) -> u16 {\n        u16::from(self.prescaler) & 0x1FF\n    }\n    #[inline]\n    pub(crate) fn ntseg1(&self) -> u8 {\n        u8::from(self.seg1)\n    }\n    #[inline]\n    pub(crate) fn ntseg2(&self) -> u8 {\n        u8::from(self.seg2) & 0x7F\n    }\n    #[inline]\n    pub(crate) fn nsjw(&self) -> u8 {\n        u8::from(self.sync_jump_width) & 0x7F\n    }\n}\n\nimpl Default for NominalBitTiming {\n    #[inline]\n    fn default() -> Self {\n        // Kernel Clock 8MHz, Bit rate: 500kbit/s. Corresponds to a NBTP\n        // register value of 0x0600_0A03\n        Self {\n            prescaler: NonZeroU16::new(1).unwrap(),\n            seg1: NonZeroU8::new(11).unwrap(),\n            seg2: NonZeroU8::new(4).unwrap(),\n            sync_jump_width: NonZeroU8::new(4).unwrap(),\n        }\n    }\n}\n\n/// Configures the data bit timings for the FdCan Variable Bitrates.\n/// This is not used when frame_transmit is set to anything other than AllowFdCanAndBRS.\n#[derive(Clone, Copy, Debug)]\npub struct DataBitTiming {\n    /// Tranceiver Delay Compensation\n    pub transceiver_delay_compensation: bool,\n    ///  The value by which the oscillator frequency is divided to generate the bit time quanta. The bit\n    ///  time is built up from a multiple of this quanta. Valid values for the Baud Rate Prescaler are 1\n    ///  to 31.\n    pub prescaler: NonZeroU16,\n    /// Valid values are 1 to 31.\n    pub seg1: NonZeroU8,\n    /// Valid values are 1 to 15.\n    pub seg2: NonZeroU8,\n    /// Must always be smaller than DTSEG2, valid values are 1 to 15.\n    pub sync_jump_width: NonZeroU8,\n}\nimpl DataBitTiming {\n    // #[inline]\n    // fn tdc(&self) -> u8 {\n    //     let tsd = self.transceiver_delay_compensation as u8;\n    //     //TODO: stm32g4 does not export the TDC field\n    //     todo!()\n    // }\n    #[inline]\n    pub(crate) fn dbrp(&self) -> u8 {\n        (u16::from(self.prescaler) & 0x001F) as u8\n    }\n    #[inline]\n    pub(crate) fn dtseg1(&self) -> u8 {\n        u8::from(self.seg1) & 0x1F\n    }\n    #[inline]\n    pub(crate) fn dtseg2(&self) -> u8 {\n        u8::from(self.seg2) & 0x0F\n    }\n    #[inline]\n    pub(crate) fn dsjw(&self) -> u8 {\n        u8::from(self.sync_jump_width) & 0x0F\n    }\n}\n\nimpl Default for DataBitTiming {\n    #[inline]\n    fn default() -> Self {\n        // Kernel Clock 8MHz, Bit rate: 500kbit/s. Corresponds to a DBTP\n        // register value of 0x0000_0A33\n        Self {\n            transceiver_delay_compensation: false,\n            prescaler: NonZeroU16::new(1).unwrap(),\n            seg1: NonZeroU8::new(11).unwrap(),\n            seg2: NonZeroU8::new(4).unwrap(),\n            sync_jump_width: NonZeroU8::new(4).unwrap(),\n        }\n    }\n}\n\n/// Configures which modes to use\n/// Individual headers can contain a desire to be send via FdCan\n/// or use Bit rate switching. But if this general setting does not allow\n/// that, only classic CAN is used instead.\n#[derive(Clone, Copy, Debug)]\npub enum FrameTransmissionConfig {\n    /// Only allow Classic CAN message Frames\n    ClassicCanOnly,\n    /// Allow (non-brs) FdCAN Message Frames\n    AllowFdCan,\n    /// Allow FdCAN Message Frames and allow Bit Rate Switching\n    AllowFdCanAndBRS,\n}\n\n///\n#[derive(Clone, Copy, Debug)]\npub enum ClockDivider {\n    /// Divide by 1\n    _1 = 0b0000,\n    /// Divide by 2\n    _2 = 0b0001,\n    /// Divide by 4\n    _4 = 0b0010,\n    /// Divide by 6\n    _6 = 0b0011,\n    /// Divide by 8\n    _8 = 0b0100,\n    /// Divide by 10\n    _10 = 0b0101,\n    /// Divide by 12\n    _12 = 0b0110,\n    /// Divide by 14\n    _14 = 0b0111,\n    /// Divide by 16\n    _16 = 0b1000,\n    /// Divide by 18\n    _18 = 0b1001,\n    /// Divide by 20\n    _20 = 0b1010,\n    /// Divide by 22\n    _22 = 0b1011,\n    /// Divide by 24\n    _24 = 0b1100,\n    /// Divide by 26\n    _26 = 0b1101,\n    /// Divide by 28\n    _28 = 0b1110,\n    /// Divide by 30\n    _30 = 0b1111,\n}\n\n/// Prescaler of the Timestamp counter\n#[derive(Clone, Copy, Debug)]\npub enum TimestampPrescaler {\n    /// 1\n    _1 = 1,\n    /// 2\n    _2 = 2,\n    /// 3\n    _3 = 3,\n    /// 4\n    _4 = 4,\n    /// 5\n    _5 = 5,\n    /// 6\n    _6 = 6,\n    /// 7\n    _7 = 7,\n    /// 8\n    _8 = 8,\n    /// 9\n    _9 = 9,\n    /// 10\n    _10 = 10,\n    /// 11\n    _11 = 11,\n    /// 12\n    _12 = 12,\n    /// 13\n    _13 = 13,\n    /// 14\n    _14 = 14,\n    /// 15\n    _15 = 15,\n    /// 16\n    _16 = 16,\n}\n\n/// Selects the source of the Timestamp counter\n#[derive(Clone, Copy, Debug)]\npub enum TimestampSource {\n    /// The Timestamp counter is disabled\n    None,\n    /// Using the FdCan input clock as the Timstamp counter's source,\n    /// and using a specific prescaler\n    Prescaler(TimestampPrescaler),\n    /// Using TIM3 as a source\n    FromTIM3,\n}\n\n/// How to handle frames in the global filter\n#[derive(Clone, Copy, Debug)]\npub enum NonMatchingFilter {\n    /// Frames will go to Fifo0 when they do no match any specific filter\n    IntoRxFifo0 = 0b00,\n    /// Frames will go to Fifo1 when they do no match any specific filter\n    IntoRxFifo1 = 0b01,\n    /// Frames will be rejected when they do not match any specific filter\n    Reject = 0b11,\n}\n\n/// How to handle frames which do not match a specific filter\n#[derive(Clone, Copy, Debug)]\npub struct GlobalFilter {\n    /// How to handle non-matching standard frames\n    pub handle_standard_frames: NonMatchingFilter,\n\n    /// How to handle non-matching extended frames\n    pub handle_extended_frames: NonMatchingFilter,\n\n    /// How to handle remote standard frames\n    pub reject_remote_standard_frames: bool,\n\n    /// How to handle remote extended frames\n    pub reject_remote_extended_frames: bool,\n}\nimpl GlobalFilter {\n    /// Reject all non-matching and remote frames\n    pub const fn reject_all() -> Self {\n        Self {\n            handle_standard_frames: NonMatchingFilter::Reject,\n            handle_extended_frames: NonMatchingFilter::Reject,\n            reject_remote_standard_frames: true,\n            reject_remote_extended_frames: true,\n        }\n    }\n\n    /// How to handle non-matching standard frames\n    pub const fn set_handle_standard_frames(mut self, filter: NonMatchingFilter) -> Self {\n        self.handle_standard_frames = filter;\n        self\n    }\n    /// How to handle non-matching exteded frames\n    pub const fn set_handle_extended_frames(mut self, filter: NonMatchingFilter) -> Self {\n        self.handle_extended_frames = filter;\n        self\n    }\n    /// How to handle remote standard frames\n    pub const fn set_reject_remote_standard_frames(mut self, filter: bool) -> Self {\n        self.reject_remote_standard_frames = filter;\n        self\n    }\n    /// How to handle remote extended frames\n    pub const fn set_reject_remote_extended_frames(mut self, filter: bool) -> Self {\n        self.reject_remote_extended_frames = filter;\n        self\n    }\n}\nimpl Default for GlobalFilter {\n    #[inline]\n    fn default() -> Self {\n        Self {\n            handle_standard_frames: NonMatchingFilter::IntoRxFifo0,\n            handle_extended_frames: NonMatchingFilter::IntoRxFifo0,\n            reject_remote_standard_frames: false,\n            reject_remote_extended_frames: false,\n        }\n    }\n}\n\n/// TX buffer operation mode\n#[derive(Clone, Copy, PartialEq, Eq, Debug)]\npub enum TxBufferMode {\n    /// TX FIFO operation - In this mode CAN frames are trasmitted strictly in write order.\n    Fifo,\n    /// TX priority queue operation - In this mode CAN frames are transmitted according to CAN priority.\n    Priority,\n}\n\nimpl From<TxBufferMode> for crate::pac::can::vals::Tfqm {\n    fn from(value: TxBufferMode) -> Self {\n        match value {\n            TxBufferMode::Priority => Self::QUEUE,\n            TxBufferMode::Fifo => Self::FIFO,\n        }\n    }\n}\n\nimpl From<crate::pac::can::vals::Tfqm> for TxBufferMode {\n    fn from(value: crate::pac::can::vals::Tfqm) -> Self {\n        match value {\n            crate::pac::can::vals::Tfqm::QUEUE => Self::Priority,\n            crate::pac::can::vals::Tfqm::FIFO => Self::Fifo,\n        }\n    }\n}\n\n/// FdCan Config Struct\n#[derive(Clone, Copy, Debug)]\npub struct FdCanConfig {\n    /// Nominal Bit Timings\n    pub nbtr: NominalBitTiming,\n    /// (Variable) Data Bit Timings\n    pub dbtr: DataBitTiming,\n    /// Enables or disables automatic retransmission of messages\n    ///\n    /// If this is enabled, the CAN peripheral will automatically try to retransmit each frame\n    /// util it can be sent. Otherwise, it will try only once to send each frame.\n    ///\n    /// Automatic retransmission is enabled by default.\n    pub automatic_retransmit: bool,\n    /// The transmit pause feature is intended for use in CAN systems where the CAN message\n    /// identifiers are permanently specified to specific values and cannot easily be changed.\n    ///\n    /// These message identifiers can have a higher CAN arbitration priority than other defined\n    /// messages, while in a specific application their relative arbitration priority must be inverse.\n    ///\n    /// This may lead to a case where one ECU sends a burst of CAN messages that cause\n    /// another ECU CAN messages to be delayed because that other messages have a lower\n    /// CAN arbitration priority.\n    pub transmit_pause: bool,\n    /// Enabled or disables the pausing between transmissions\n    ///\n    /// This feature looses up burst transmissions coming from a single node and it protects against\n    /// \"babbling idiot\" scenarios where the application program erroneously requests too many\n    /// transmissions.\n    pub frame_transmit: FrameTransmissionConfig,\n    /// Non Isoe Mode\n    /// If this is set, the FDCAN uses the CAN FD frame format as specified by the Bosch CAN\n    /// FD Specification V1.0.\n    pub non_iso_mode: bool,\n    /// Edge Filtering: Two consecutive dominant tq required to detect an edge for hard synchronization\n    pub edge_filtering: bool,\n    /// Enables protocol exception handling\n    pub protocol_exception_handling: bool,\n    /// Sets the general clock divider for this FdCAN instance\n    pub clock_divider: ClockDivider,\n    /// Sets the timestamp source\n    pub timestamp_source: TimestampSource,\n    /// Configures the Global Filter\n    pub global_filter: GlobalFilter,\n    /// TX buffer mode (FIFO or priority queue)\n    pub tx_buffer_mode: TxBufferMode,\n    /// Automatic recovery from bus off state\n    pub automatic_bus_off_recovery: bool,\n}\n\nimpl FdCanConfig {\n    /// Configures the bit timings.\n    #[inline]\n    pub const fn set_nominal_bit_timing(mut self, btr: NominalBitTiming) -> Self {\n        self.nbtr = btr;\n        self\n    }\n\n    /// Configures the bit timings.\n    #[inline]\n    pub const fn set_data_bit_timing(mut self, btr: DataBitTiming) -> Self {\n        self.dbtr = btr;\n        self\n    }\n\n    /// Enables or disables automatic retransmission of messages\n    ///\n    /// If this is enabled, the CAN peripheral will automatically try to retransmit each frame\n    /// util it can be sent. Otherwise, it will try only once to send each frame.\n    ///\n    /// Automatic retransmission is enabled by default.\n    #[inline]\n    pub const fn set_automatic_retransmit(mut self, enabled: bool) -> Self {\n        self.automatic_retransmit = enabled;\n        self\n    }\n\n    /// Enabled or disables the pausing between transmissions\n    ///\n    /// This feature looses up burst transmissions coming from a single node and it protects against\n    /// \"babbling idiot\" scenarios where the application program erroneously requests too many\n    /// transmissions.\n    #[inline]\n    pub const fn set_transmit_pause(mut self, enabled: bool) -> Self {\n        self.transmit_pause = enabled;\n        self\n    }\n\n    /// If this is set, the FDCAN uses the CAN FD frame format as specified by the Bosch CAN\n    /// FD Specification V1.0.\n    #[inline]\n    pub const fn set_non_iso_mode(mut self, enabled: bool) -> Self {\n        self.non_iso_mode = enabled;\n        self\n    }\n\n    /// Two consecutive dominant tq required to detect an edge for hard synchronization\n    #[inline]\n    pub const fn set_edge_filtering(mut self, enabled: bool) -> Self {\n        self.edge_filtering = enabled;\n        self\n    }\n\n    /// Sets the allowed transmission types for messages.\n    #[inline]\n    pub const fn set_frame_transmit(mut self, fts: FrameTransmissionConfig) -> Self {\n        self.frame_transmit = fts;\n        self\n    }\n\n    /// Enables protocol exception handling\n    #[inline]\n    pub const fn set_protocol_exception_handling(mut self, peh: bool) -> Self {\n        self.protocol_exception_handling = peh;\n        self\n    }\n\n    /// Sets the general clock divider for this FdCAN instance\n    #[inline]\n    pub const fn set_clock_divider(mut self, div: ClockDivider) -> Self {\n        self.clock_divider = div;\n        self\n    }\n\n    /// Sets the timestamp source\n    #[inline]\n    pub const fn set_timestamp_source(mut self, tss: TimestampSource) -> Self {\n        self.timestamp_source = tss;\n        self\n    }\n\n    /// Sets the global filter settings\n    #[inline]\n    pub const fn set_global_filter(mut self, filter: GlobalFilter) -> Self {\n        self.global_filter = filter;\n        self\n    }\n\n    /// Sets the TX buffer mode (FIFO or priority queue)\n    #[inline]\n    pub const fn set_tx_buffer_mode(mut self, txbm: TxBufferMode) -> Self {\n        self.tx_buffer_mode = txbm;\n        self\n    }\n\n    /// Enables or disables automatic recovery from bus off state\n    ///\n    /// Automatic recovery is performed by clearing the INIT bit in the CCCR register if\n    /// the BO bit is active in the IR register in the IT0 interrupt.\n    #[inline]\n    pub const fn set_automatic_bus_off_recovery(mut self, enabled: bool) -> Self {\n        self.automatic_bus_off_recovery = enabled;\n        self\n    }\n}\n\nimpl Default for FdCanConfig {\n    #[inline]\n    fn default() -> Self {\n        Self {\n            nbtr: NominalBitTiming::default(),\n            dbtr: DataBitTiming::default(),\n            automatic_retransmit: true,\n            transmit_pause: false,\n            frame_transmit: FrameTransmissionConfig::ClassicCanOnly,\n            non_iso_mode: false,\n            edge_filtering: false,\n            protocol_exception_handling: true,\n            clock_divider: ClockDivider::_1,\n            timestamp_source: TimestampSource::None,\n            global_filter: GlobalFilter::default(),\n            tx_buffer_mode: TxBufferMode::Priority,\n            automatic_bus_off_recovery: true,\n        }\n    }\n}\n"
  },
  {
    "path": "embassy-stm32/src/can/fd/filter.rs",
    "content": "//! Definition of Filter structs for FDCAN Module\n// Note: This file is copied and modified from fdcan crate by Richard Meadows\n\nuse embedded_can::{ExtendedId, StandardId};\n\nuse crate::can::fd::message_ram;\npub use crate::can::fd::message_ram::{EXTENDED_FILTER_MAX, STANDARD_FILTER_MAX};\n\n/// A Standard Filter\npub type StandardFilter = Filter<StandardId, u16>;\n/// An Extended Filter\npub type ExtendedFilter = Filter<ExtendedId, u32>;\n\nimpl Default for StandardFilter {\n    fn default() -> Self {\n        StandardFilter::disable()\n    }\n}\nimpl Default for ExtendedFilter {\n    fn default() -> Self {\n        ExtendedFilter::disable()\n    }\n}\n\nimpl StandardFilter {\n    /// Accept all messages in FIFO 0\n    pub fn accept_all_into_fifo0() -> StandardFilter {\n        StandardFilter {\n            filter: FilterType::BitMask { filter: 0x0, mask: 0x0 },\n            action: Action::StoreInFifo0,\n        }\n    }\n\n    /// Accept all messages in FIFO 1\n    pub fn accept_all_into_fifo1() -> StandardFilter {\n        StandardFilter {\n            filter: FilterType::BitMask { filter: 0x0, mask: 0x0 },\n            action: Action::StoreInFifo1,\n        }\n    }\n\n    /// Reject all messages\n    pub fn reject_all() -> StandardFilter {\n        StandardFilter {\n            filter: FilterType::BitMask { filter: 0x0, mask: 0x0 },\n            action: Action::Reject,\n        }\n    }\n\n    /// Disable the filter\n    pub fn disable() -> StandardFilter {\n        StandardFilter {\n            filter: FilterType::Disabled,\n            action: Action::Disable,\n        }\n    }\n}\n\nimpl ExtendedFilter {\n    /// Accept all messages in FIFO 0\n    pub fn accept_all_into_fifo0() -> ExtendedFilter {\n        ExtendedFilter {\n            filter: FilterType::BitMask { filter: 0x0, mask: 0x0 },\n            action: Action::StoreInFifo0,\n        }\n    }\n\n    /// Accept all messages in FIFO 1\n    pub fn accept_all_into_fifo1() -> ExtendedFilter {\n        ExtendedFilter {\n            filter: FilterType::BitMask { filter: 0x0, mask: 0x0 },\n            action: Action::StoreInFifo1,\n        }\n    }\n\n    /// Reject all messages\n    pub fn reject_all() -> ExtendedFilter {\n        ExtendedFilter {\n            filter: FilterType::BitMask { filter: 0x0, mask: 0x0 },\n            action: Action::Reject,\n        }\n    }\n\n    /// Disable the filter\n    pub fn disable() -> ExtendedFilter {\n        ExtendedFilter {\n            filter: FilterType::Disabled,\n            action: Action::Disable,\n        }\n    }\n}\n\n/// Filter Type\n#[derive(Clone, Copy, Debug)]\npub enum FilterType<ID, UNIT>\nwhere\n    ID: Copy + Clone + core::fmt::Debug,\n    UNIT: Copy + Clone + core::fmt::Debug,\n{\n    /// Match with a range between two messages\n    Range {\n        /// First Id of the range\n        from: ID,\n        /// Last Id of the range\n        to: ID,\n    },\n    /// Match with a bitmask\n    BitMask {\n        /// Filter of the bitmask\n        filter: UNIT,\n        /// Mask of the bitmask\n        mask: UNIT,\n    },\n    /// Match with a single ID\n    DedicatedSingle(ID),\n    /// Match with one of two ID's\n    DedicatedDual(ID, ID),\n    /// Filter is disabled\n    Disabled,\n}\nimpl<ID, UNIT> From<FilterType<ID, UNIT>> for message_ram::enums::FilterType\nwhere\n    ID: Copy + Clone + core::fmt::Debug,\n    UNIT: Copy + Clone + core::fmt::Debug,\n{\n    fn from(f: FilterType<ID, UNIT>) -> Self {\n        match f {\n            FilterType::Range { to: _, from: _ } => Self::RangeFilter,\n            FilterType::BitMask { filter: _, mask: _ } => Self::ClassicFilter,\n            FilterType::DedicatedSingle(_) => Self::DualIdFilter,\n            FilterType::DedicatedDual(_, _) => Self::DualIdFilter,\n            FilterType::Disabled => Self::FilterDisabled,\n        }\n    }\n}\n\n/// Filter Action\n#[derive(Clone, Copy, Debug)]\npub enum Action {\n    /// No Action\n    Disable = 0b000,\n    /// Store an matching message in FIFO 0\n    StoreInFifo0 = 0b001,\n    /// Store an matching message in FIFO 1\n    StoreInFifo1 = 0b010,\n    /// Reject an matching message\n    Reject = 0b011,\n    /// Flag a matching message (But not store?!?)\n    FlagHighPrio = 0b100,\n    /// Flag a matching message as a High Priority message and store it in FIFO 0\n    FlagHighPrioAndStoreInFifo0 = 0b101,\n    /// Flag a matching message as a High Priority message and store it in FIFO 1\n    FlagHighPrioAndStoreInFifo1 = 0b110,\n}\nimpl From<Action> for message_ram::enums::FilterElementConfig {\n    fn from(a: Action) -> Self {\n        match a {\n            Action::Disable => Self::DisableFilterElement,\n            Action::StoreInFifo0 => Self::StoreInFifo0,\n            Action::StoreInFifo1 => Self::StoreInFifo1,\n            Action::Reject => Self::Reject,\n            Action::FlagHighPrio => Self::SetPriority,\n            Action::FlagHighPrioAndStoreInFifo0 => Self::SetPriorityAndStoreInFifo0,\n            Action::FlagHighPrioAndStoreInFifo1 => Self::SetPriorityAndStoreInFifo1,\n        }\n    }\n}\n\n/// Filter\n#[derive(Clone, Copy, Debug)]\npub struct Filter<ID, UNIT>\nwhere\n    ID: Copy + Clone + core::fmt::Debug,\n    UNIT: Copy + Clone + core::fmt::Debug,\n{\n    /// How to match an incoming message\n    pub filter: FilterType<ID, UNIT>,\n    /// What to do with a matching message\n    pub action: Action,\n}\n\n/// Standard Filter Slot\n#[derive(Debug, Copy, Clone, Eq, PartialEq)]\npub enum StandardFilterSlot {\n    /// 0\n    _0 = 0,\n    /// 1\n    _1 = 1,\n    /// 2\n    _2 = 2,\n    /// 3\n    _3 = 3,\n    /// 4\n    _4 = 4,\n    /// 5\n    _5 = 5,\n    /// 6\n    _6 = 6,\n    /// 7\n    _7 = 7,\n    /// 8\n    _8 = 8,\n    /// 9\n    _9 = 9,\n    /// 10\n    _10 = 10,\n    /// 11\n    _11 = 11,\n    /// 12\n    _12 = 12,\n    /// 13\n    _13 = 13,\n    /// 14\n    _14 = 14,\n    /// 15\n    _15 = 15,\n    /// 16\n    _16 = 16,\n    /// 17\n    _17 = 17,\n    /// 18\n    _18 = 18,\n    /// 19\n    _19 = 19,\n    /// 20\n    _20 = 20,\n    /// 21\n    _21 = 21,\n    /// 22\n    _22 = 22,\n    /// 23\n    _23 = 23,\n    /// 24\n    _24 = 24,\n    /// 25\n    _25 = 25,\n    /// 26\n    _26 = 26,\n    /// 27\n    _27 = 27,\n}\nimpl From<u8> for StandardFilterSlot {\n    fn from(u: u8) -> Self {\n        match u {\n            0 => StandardFilterSlot::_0,\n            1 => StandardFilterSlot::_1,\n            2 => StandardFilterSlot::_2,\n            3 => StandardFilterSlot::_3,\n            4 => StandardFilterSlot::_4,\n            5 => StandardFilterSlot::_5,\n            6 => StandardFilterSlot::_6,\n            7 => StandardFilterSlot::_7,\n            8 => StandardFilterSlot::_8,\n            9 => StandardFilterSlot::_9,\n            10 => StandardFilterSlot::_10,\n            11 => StandardFilterSlot::_11,\n            12 => StandardFilterSlot::_12,\n            13 => StandardFilterSlot::_13,\n            14 => StandardFilterSlot::_14,\n            15 => StandardFilterSlot::_15,\n            16 => StandardFilterSlot::_16,\n            17 => StandardFilterSlot::_17,\n            18 => StandardFilterSlot::_18,\n            19 => StandardFilterSlot::_19,\n            20 => StandardFilterSlot::_20,\n            21 => StandardFilterSlot::_21,\n            22 => StandardFilterSlot::_22,\n            23 => StandardFilterSlot::_23,\n            24 => StandardFilterSlot::_24,\n            25 => StandardFilterSlot::_25,\n            26 => StandardFilterSlot::_26,\n            27 => StandardFilterSlot::_27,\n            _ => panic!(\"Standard Filter Slot Too High!\"),\n        }\n    }\n}\n\n/// Extended Filter Slot\n#[derive(Debug, Copy, Clone, Eq, PartialEq)]\npub enum ExtendedFilterSlot {\n    /// 0\n    _0 = 0,\n    /// 1\n    _1 = 1,\n    /// 2\n    _2 = 2,\n    /// 3\n    _3 = 3,\n    /// 4\n    _4 = 4,\n    /// 5\n    _5 = 5,\n    /// 6\n    _6 = 6,\n    /// 7\n    _7 = 7,\n}\nimpl From<u8> for ExtendedFilterSlot {\n    fn from(u: u8) -> Self {\n        match u {\n            0 => ExtendedFilterSlot::_0,\n            1 => ExtendedFilterSlot::_1,\n            2 => ExtendedFilterSlot::_2,\n            3 => ExtendedFilterSlot::_3,\n            4 => ExtendedFilterSlot::_4,\n            5 => ExtendedFilterSlot::_5,\n            6 => ExtendedFilterSlot::_6,\n            7 => ExtendedFilterSlot::_7,\n            _ => panic!(\"Extended Filter Slot Too High!\"), // Should be unreachable\n        }\n    }\n}\n\n/// Enum over both Standard and Extended Filter ID's\n#[derive(Debug, Copy, Clone, Eq, PartialEq)]\npub enum FilterId {\n    /// Standard Filter Slots\n    Standard(StandardFilterSlot),\n    /// Extended Filter Slots\n    Extended(ExtendedFilterSlot),\n}\n\npub(crate) trait ActivateFilter<ID, UNIT>\nwhere\n    ID: Copy + Clone + core::fmt::Debug,\n    UNIT: Copy + Clone + core::fmt::Debug,\n{\n    fn activate(&mut self, f: Filter<ID, UNIT>);\n    // fn read(&self) -> Filter<ID, UNIT>;\n}\n\nimpl ActivateFilter<StandardId, u16> for message_ram::StandardFilter {\n    fn activate(&mut self, f: Filter<StandardId, u16>) {\n        let sft = f.filter.into();\n\n        let (sfid1, sfid2) = match f.filter {\n            FilterType::Range { to, from } => (to.as_raw(), from.as_raw()),\n            FilterType::DedicatedSingle(id) => (id.as_raw(), id.as_raw()),\n            FilterType::DedicatedDual(id1, id2) => (id1.as_raw(), id2.as_raw()),\n            FilterType::BitMask { filter, mask } => (filter, mask),\n            FilterType::Disabled => (0x0, 0x0),\n        };\n        let sfec = f.action.into();\n        self.write(|w| {\n            unsafe { w.sfid1().bits(sfid1).sfid2().bits(sfid2) }\n                .sft()\n                .set_filter_type(sft)\n                .sfec()\n                .set_filter_element_config(sfec)\n        });\n    }\n    // fn read(&self) -> Filter<StandardId, u16> {\n    //     todo!()\n    // }\n}\nimpl ActivateFilter<ExtendedId, u32> for message_ram::ExtendedFilter {\n    fn activate(&mut self, f: Filter<ExtendedId, u32>) {\n        let eft = f.filter.into();\n\n        let (efid1, efid2) = match f.filter {\n            FilterType::Range { to, from } => (to.as_raw(), from.as_raw()),\n            FilterType::DedicatedSingle(id) => (id.as_raw(), id.as_raw()),\n            FilterType::DedicatedDual(id1, id2) => (id1.as_raw(), id2.as_raw()),\n            FilterType::BitMask { filter, mask } => (filter, mask),\n            FilterType::Disabled => (0x0, 0x0),\n        };\n        let efec = f.action.into();\n        self.write(|w| {\n            unsafe { w.efid1().bits(efid1).efid2().bits(efid2) }\n                .eft()\n                .set_filter_type(eft)\n                .efec()\n                .set_filter_element_config(efec)\n        });\n    }\n    // fn read(&self) -> Filter<ExtendedId, u32> {\n    //     todo!()\n    // }\n}\n"
  },
  {
    "path": "embassy-stm32/src/can/fd/message_ram/common.rs",
    "content": "// Note: This file is copied and modified from fdcan crate by Richard Meadows\n#![allow(non_camel_case_types)]\n#![allow(non_snake_case)]\n#![allow(unused)]\n\nuse super::enums::{\n    BitRateSwitching, ErrorStateIndicator, FilterElementConfig, FilterType, FrameFormat, IdType,\n    RemoteTransmissionRequest,\n};\nuse super::generic;\n\n#[doc = \"Reader of field `ID`\"]\npub type ID_R = generic::R<u32, u32>;\n\n#[doc = \"Reader of field `RTR`\"]\npub type RTR_R = generic::R<bool, RemoteTransmissionRequest>;\nimpl RTR_R {\n    pub fn rtr(&self) -> RemoteTransmissionRequest {\n        match self.bits {\n            false => RemoteTransmissionRequest::TransmitDataFrame,\n            true => RemoteTransmissionRequest::TransmitRemoteFrame,\n        }\n    }\n    pub fn is_transmit_remote_frame(&self) -> bool {\n        *self == RemoteTransmissionRequest::TransmitRemoteFrame\n    }\n    pub fn is_transmit_data_frame(&self) -> bool {\n        *self == RemoteTransmissionRequest::TransmitDataFrame\n    }\n}\n\n#[doc = \"Reader of field `XTD`\"]\npub type XTD_R = generic::R<bool, IdType>;\nimpl XTD_R {\n    pub fn id_type(&self) -> IdType {\n        match self.bits() {\n            false => IdType::StandardId,\n            true => IdType::ExtendedId,\n        }\n    }\n    pub fn is_standard_id(&self) -> bool {\n        *self == IdType::StandardId\n    }\n    pub fn is_exteded_id(&self) -> bool {\n        *self == IdType::ExtendedId\n    }\n}\n\n#[doc = \"Reader of field `ESI`\"]\npub type ESI_R = generic::R<bool, ErrorStateIndicator>;\nimpl ESI_R {\n    pub fn error_state(&self) -> ErrorStateIndicator {\n        match self.bits() {\n            false => ErrorStateIndicator::ErrorActive,\n            true => ErrorStateIndicator::ErrorPassive,\n        }\n    }\n    pub fn is_error_active(&self) -> bool {\n        *self == ErrorStateIndicator::ErrorActive\n    }\n    pub fn is_error_passive(&self) -> bool {\n        *self == ErrorStateIndicator::ErrorPassive\n    }\n}\n\n#[doc = \"Reader of field `DLC`\"]\npub type DLC_R = generic::R<u8, u8>;\n\n#[doc = \"Reader of field `BRS`\"]\npub type BRS_R = generic::R<bool, BitRateSwitching>;\nimpl BRS_R {\n    pub fn bit_rate_switching(&self) -> BitRateSwitching {\n        match self.bits() {\n            true => BitRateSwitching::WithBRS,\n            false => BitRateSwitching::WithoutBRS,\n        }\n    }\n    pub fn is_with_brs(&self) -> bool {\n        *self == BitRateSwitching::WithBRS\n    }\n    pub fn is_without_brs(&self) -> bool {\n        *self == BitRateSwitching::WithoutBRS\n    }\n}\n\n#[doc = \"Reader of field `FDF`\"]\npub type FDF_R = generic::R<bool, FrameFormat>;\nimpl FDF_R {\n    pub fn frame_format(&self) -> FrameFormat {\n        match self.bits() {\n            false => FrameFormat::Classic,\n            true => FrameFormat::Fdcan,\n        }\n    }\n    pub fn is_classic_format(&self) -> bool {\n        *self == FrameFormat::Classic\n    }\n    pub fn is_fdcan_format(&self) -> bool {\n        *self == FrameFormat::Fdcan\n    }\n}\n\n#[doc = \"Reader of field `(X|S)FT`\"]\npub type ESFT_R = generic::R<u8, FilterType>;\nimpl ESFT_R {\n    #[doc = r\"Gets the Filtertype\"]\n    #[inline(always)]\n    pub fn to_filter_type(&self) -> FilterType {\n        match self.bits() {\n            0b00 => FilterType::RangeFilter,\n            0b01 => FilterType::DualIdFilter,\n            0b10 => FilterType::ClassicFilter,\n            0b11 => FilterType::FilterDisabled,\n            _ => unreachable!(),\n        }\n    }\n}\n\n#[doc = \"Reader of field `(E|S)FEC`\"]\npub type ESFEC_R = generic::R<u8, FilterElementConfig>;\nimpl ESFEC_R {\n    pub fn to_filter_element_config(&self) -> FilterElementConfig {\n        match self.bits() {\n            0b000 => FilterElementConfig::DisableFilterElement,\n            0b001 => FilterElementConfig::StoreInFifo0,\n            0b010 => FilterElementConfig::StoreInFifo1,\n            0b011 => FilterElementConfig::Reject,\n            0b100 => FilterElementConfig::SetPriority,\n            0b101 => FilterElementConfig::SetPriorityAndStoreInFifo0,\n            0b110 => FilterElementConfig::SetPriorityAndStoreInFifo1,\n            _ => unimplemented!(),\n        }\n    }\n}\n"
  },
  {
    "path": "embassy-stm32/src/can/fd/message_ram/enums.rs",
    "content": "// Note: This file is copied and modified from fdcan crate by Richard Meadows\n\n/// Datalength is the message length generalised over\n/// the Standard (Classic) and FDCAN message types\n\n#[derive(Clone, Copy, Debug, PartialEq)]\npub enum DataLength {\n    Classic(u8),\n    Fdcan(u8),\n}\nimpl DataLength {\n    /// Creates a DataLength type\n    ///\n    /// Uses the byte length and Type of frame as input\n    pub fn new(len: u8, ff: FrameFormat) -> DataLength {\n        match ff {\n            FrameFormat::Classic => match len {\n                0..=8 => DataLength::Classic(len),\n                _ => panic!(\"DataLength > 8\"),\n            },\n            FrameFormat::Fdcan => match len {\n                0..=64 => DataLength::Fdcan(len),\n                _ => panic!(\"DataLength > 64\"),\n            },\n        }\n    }\n    /// Specialised function to create classic frames\n    pub fn new_classic(len: u8) -> DataLength {\n        Self::new(len, FrameFormat::Classic)\n    }\n    /// Specialised function to create FDCAN frames\n    pub fn new_fdcan(len: u8) -> DataLength {\n        Self::new(len, FrameFormat::Fdcan)\n    }\n\n    /// returns the length in bytes\n    pub fn len(&self) -> u8 {\n        match self {\n            DataLength::Classic(l) | DataLength::Fdcan(l) => *l,\n        }\n    }\n\n    pub(crate) fn dlc(&self) -> u8 {\n        match self {\n            DataLength::Classic(l) => *l,\n            // See RM0433 Rev 7 Table 475. DLC coding\n            DataLength::Fdcan(l) => match l {\n                0..=8 => *l,\n                9..=12 => 9,\n                13..=16 => 10,\n                17..=20 => 11,\n                21..=24 => 12,\n                25..=32 => 13,\n                33..=48 => 14,\n                49..=64 => 15,\n                _ => panic!(\"DataLength > 64\"),\n            },\n        }\n    }\n}\nimpl From<DataLength> for FrameFormat {\n    fn from(dl: DataLength) -> FrameFormat {\n        match dl {\n            DataLength::Classic(_) => FrameFormat::Classic,\n            DataLength::Fdcan(_) => FrameFormat::Fdcan,\n        }\n    }\n}\n\n/// Wheter or not to generate an Tx Event\n#[derive(Clone, Copy, Debug, PartialEq)]\npub enum Event {\n    /// Do not generate an Tx Event\n    NoEvent,\n    /// Generate an Tx Event with a specified ID\n    Event(u8),\n}\n\nimpl From<Event> for EventControl {\n    fn from(e: Event) -> Self {\n        match e {\n            Event::NoEvent => EventControl::DoNotStore,\n            Event::Event(_) => EventControl::Store,\n        }\n    }\n}\n\nimpl From<Option<u8>> for Event {\n    fn from(mm: Option<u8>) -> Self {\n        match mm {\n            None => Event::NoEvent,\n            Some(mm) => Event::Event(mm),\n        }\n    }\n}\n\nimpl From<Event> for Option<u8> {\n    fn from(e: Event) -> Option<u8> {\n        match e {\n            Event::NoEvent => None,\n            Event::Event(mm) => Some(mm),\n        }\n    }\n}\n\n/// TODO\n#[derive(Clone, Copy, Debug, PartialEq)]\npub enum ErrorStateIndicator {\n    /// TODO\n    ErrorActive = 0,\n    /// TODO\n    ErrorPassive = 1,\n}\nimpl From<ErrorStateIndicator> for bool {\n    #[inline(always)]\n    fn from(e: ErrorStateIndicator) -> Self {\n        e as u8 != 0\n    }\n}\n\n/// Type of frame, standard (classic) or FdCAN\n#[derive(Clone, Copy, Debug, PartialEq)]\npub enum FrameFormat {\n    Classic = 0,\n    Fdcan = 1,\n}\nimpl From<FrameFormat> for bool {\n    #[inline(always)]\n    fn from(e: FrameFormat) -> Self {\n        e as u8 != 0\n    }\n}\n\n/// Type of Id, Standard or Extended\n#[derive(Clone, Copy, Debug, PartialEq)]\npub enum IdType {\n    /// Standard ID\n    StandardId = 0,\n    /// Extended ID\n    ExtendedId = 1,\n}\nimpl From<IdType> for bool {\n    #[inline(always)]\n    fn from(e: IdType) -> Self {\n        e as u8 != 0\n    }\n}\n\n/// Whether the frame contains data or requests data\n#[derive(Clone, Copy, Debug, PartialEq)]\npub enum RemoteTransmissionRequest {\n    /// Frame contains data\n    TransmitDataFrame = 0,\n    /// frame does not contain data\n    TransmitRemoteFrame = 1,\n}\nimpl From<RemoteTransmissionRequest> for bool {\n    #[inline(always)]\n    fn from(e: RemoteTransmissionRequest) -> Self {\n        e as u8 != 0\n    }\n}\n\n/// Whether BitRateSwitching should be or was enabled\n#[derive(Clone, Copy, Debug, PartialEq)]\npub enum BitRateSwitching {\n    /// disable bit rate switching\n    WithoutBRS = 0,\n    /// enable bit rate switching\n    WithBRS = 1,\n}\nimpl From<BitRateSwitching> for bool {\n    #[inline(always)]\n    fn from(e: BitRateSwitching) -> Self {\n        e as u8 != 0\n    }\n}\n\n/// Whether to store transmit Events\n#[derive(Clone, Copy, Debug, PartialEq)]\npub enum EventControl {\n    /// do not store an tx event\n    DoNotStore,\n    /// store transmit events\n    Store,\n}\nimpl From<EventControl> for bool {\n    #[inline(always)]\n    fn from(e: EventControl) -> Self {\n        e as u8 != 0\n    }\n}\n\n/// If an received message matched any filters\n#[derive(Clone, Copy, Debug, PartialEq)]\npub enum FilterFrameMatch {\n    /// This did match filter <id>\n    DidMatch(u8),\n    /// This received frame did not match any specific filters\n    DidNotMatch,\n}\n\n/// Type of filter to be used\n#[derive(Clone, Copy, Debug, PartialEq)]\npub enum FilterType {\n    /// Filter uses the range between two id's\n    RangeFilter = 0b00,\n    /// The filter matches on two specific id's (or one ID checked twice)\n    DualIdFilter = 0b01,\n    /// Filter is using a bitmask\n    ClassicFilter = 0b10,\n    /// Filter is disabled\n    FilterDisabled = 0b11,\n}\n\n#[derive(Clone, Copy, Debug, PartialEq)]\npub enum FilterElementConfig {\n    /// Filter is disabled\n    DisableFilterElement = 0b000,\n    /// Store a matching message in FIFO 0\n    StoreInFifo0 = 0b001,\n    /// Store a matching message in FIFO 1\n    StoreInFifo1 = 0b010,\n    /// Reject a matching message\n    Reject = 0b011,\n    /// Flag that a priority message has been received, *But do note store!*??\n    SetPriority = 0b100,\n    /// Flag and store message in FIFO 0\n    SetPriorityAndStoreInFifo0 = 0b101,\n    /// Flag and store message in FIFO 1\n    SetPriorityAndStoreInFifo1 = 0b110,\n    //_Unused = 0b111,\n}\n"
  },
  {
    "path": "embassy-stm32/src/can/fd/message_ram/extended_filter.rs",
    "content": "// Note: This file is copied and modified from fdcan crate by Richard Meadows\n\n#![allow(non_camel_case_types)]\n#![allow(non_snake_case)]\n#![allow(unused)]\n\nuse super::common::{ESFEC_R, ESFT_R};\nuse super::enums::{FilterElementConfig, FilterType};\nuse super::generic;\n\n#[doc = \"Reader of register ExtendedFilter\"]\npub(crate) type R = generic::R<super::ExtendedFilterType, super::ExtendedFilter>;\n#[doc = \"Writer for register ExtendedFilter\"]\npub(crate) type W = generic::W<super::ExtendedFilterType, super::ExtendedFilter>;\n#[doc = \"Register ExtendedFilter `reset()`'s\"]\nimpl generic::ResetValue for super::ExtendedFilter {\n    type Type = super::ExtendedFilterType;\n    #[inline(always)]\n    fn reset_value() -> Self::Type {\n        // Sets filter element to Disabled\n        [0x0, 0x0]\n    }\n}\n\n#[doc = \"Reader of field `EFID2`\"]\npub(crate) type EFID2_R = generic::R<u32, u32>;\n#[doc = \"Write proxy for field `EFID2`\"]\npub(crate) struct EFID2_W<'a> {\n    w: &'a mut W,\n}\nimpl<'a> EFID2_W<'a> {\n    #[doc = r\"Writes raw bits to the field\"]\n    #[inline(always)]\n    pub unsafe fn bits(self, value: u32) -> &'a mut W {\n        self.w.bits[1] = (self.w.bits[1] & !(0x1FFFFFFF)) | ((value as u32) & 0x1FFFFFFF);\n        self.w\n    }\n}\n\n#[doc = \"Reader of field `EFID1`\"]\npub(crate) type EFID1_R = generic::R<u32, u32>;\n#[doc = \"Write proxy for field `EFID1`\"]\npub(crate) struct EFID1_W<'a> {\n    w: &'a mut W,\n}\nimpl<'a> EFID1_W<'a> {\n    #[doc = r\"Writes raw bits to the field\"]\n    #[inline(always)]\n    pub unsafe fn bits(self, value: u32) -> &'a mut W {\n        self.w.bits[0] = (self.w.bits[0] & !(0x1FFFFFFF)) | ((value as u32) & 0x1FFFFFFF);\n        self.w\n    }\n}\n\n#[doc = \"Write proxy for field `EFEC`\"]\npub(crate) struct EFEC_W<'a> {\n    w: &'a mut W,\n}\nimpl<'a> EFEC_W<'a> {\n    #[doc = r\"Writes raw bits to the field\"]\n    #[inline(always)]\n    pub unsafe fn bits(self, value: u8) -> &'a mut W {\n        self.w.bits[0] = (self.w.bits[0] & !(0x07 << 29)) | (((value as u32) & 0x07) << 29);\n        self.w\n    }\n    #[doc = r\"Sets the field according to FilterElementConfig\"]\n    #[inline(always)]\n    pub fn set_filter_element_config(self, fec: FilterElementConfig) -> &'a mut W {\n        //SAFETY: FilterElementConfig only be valid options\n        unsafe { self.bits(fec as u8) }\n    }\n}\n\n#[doc = \"Write proxy for field `EFT`\"]\npub(crate) struct EFT_W<'a> {\n    w: &'a mut W,\n}\nimpl<'a> EFT_W<'a> {\n    #[doc = r\"Sets the field according the FilterType\"]\n    #[inline(always)]\n    pub fn set_filter_type(self, filter: FilterType) -> &'a mut W {\n        //SAFETY: FilterType only be valid options\n        unsafe { self.bits(filter as u8) }\n    }\n    #[doc = r\"Writes raw bits to the field\"]\n    #[inline(always)]\n    pub unsafe fn bits(self, value: u8) -> &'a mut W {\n        self.w.bits[1] = (self.w.bits[1] & !(0x03 << 30)) | (((value as u32) & 0x03) << 30);\n        self.w\n    }\n}\n\nimpl R {\n    #[doc = \"Byte 0 - Bits 0:28 - EFID1\"]\n    #[inline(always)]\n    pub fn sfid1(&self) -> EFID1_R {\n        EFID1_R::new(((self.bits[0]) & 0x1FFFFFFF) as u32)\n    }\n    #[doc = \"Byte 0 - Bits 29:31 - EFEC\"]\n    #[inline(always)]\n    pub fn efec(&self) -> ESFEC_R {\n        ESFEC_R::new(((self.bits[0] >> 29) & 0x07) as u8)\n    }\n    #[doc = \"Byte 1 - Bits 0:28 - EFID2\"]\n    #[inline(always)]\n    pub fn sfid2(&self) -> EFID2_R {\n        EFID2_R::new(((self.bits[1]) & 0x1FFFFFFF) as u32)\n    }\n    #[doc = \"Byte 1 - Bits 30:31 - EFT\"]\n    #[inline(always)]\n    pub fn eft(&self) -> ESFT_R {\n        ESFT_R::new(((self.bits[1] >> 30) & 0x03) as u8)\n    }\n}\nimpl W {\n    #[doc = \"Byte 0 - Bits 0:28 - EFID1\"]\n    #[inline(always)]\n    pub fn efid1(&mut self) -> EFID1_W<'_> {\n        EFID1_W { w: self }\n    }\n    #[doc = \"Byte 0 - Bits 29:31 - EFEC\"]\n    #[inline(always)]\n    pub fn efec(&mut self) -> EFEC_W<'_> {\n        EFEC_W { w: self }\n    }\n    #[doc = \"Byte 1 - Bits 0:28 - EFID2\"]\n    #[inline(always)]\n    pub fn efid2(&mut self) -> EFID2_W<'_> {\n        EFID2_W { w: self }\n    }\n    #[doc = \"Byte 1 - Bits 30:31 - EFT\"]\n    #[inline(always)]\n    pub fn eft(&mut self) -> EFT_W<'_> {\n        EFT_W { w: self }\n    }\n}\n"
  },
  {
    "path": "embassy-stm32/src/can/fd/message_ram/generic.rs",
    "content": "// Note: This file is copied and modified from fdcan crate by Richard Meadows\n\nuse core::marker;\n\n///This trait shows that register has `read` method\n///\n///Registers marked with `Writable` can be also `modify`'ed\npub trait Readable {}\n\n///This trait shows that register has `write`, `write_with_zero` and `reset` method\n///\n///Registers marked with `Readable` can be also `modify`'ed\npub trait Writable {}\n\n///Reset value of the register\n///\n///This value is initial value for `write` method.\n///It can be also directly writed to register by `reset` method.\npub trait ResetValue {\n    ///Register size\n    type Type;\n    ///Reset value of the register\n    fn reset_value() -> Self::Type;\n}\n\n///This structure provides volatile access to register\npub struct Reg<U, REG> {\n    register: vcell::VolatileCell<U>,\n    _marker: marker::PhantomData<REG>,\n}\n\nunsafe impl<U: Send, REG> Send for Reg<U, REG> {}\n\nimpl<U, REG> Reg<U, REG>\nwhere\n    Self: Readable,\n    U: Copy,\n{\n    ///Reads the contents of `Readable` register\n    ///\n    ///You can read the contents of a register in such way:\n    ///```ignore\n    ///let bits = periph.reg.read().bits();\n    ///```\n    ///or get the content of a particular field of a register.\n    ///```ignore\n    ///let reader = periph.reg.read();\n    ///let bits = reader.field1().bits();\n    ///let flag = reader.field2().bit_is_set();\n    ///```\n    #[inline(always)]\n    pub fn read(&self) -> R<U, Self> {\n        R {\n            bits: self.register.get(),\n            _reg: marker::PhantomData,\n        }\n    }\n}\n\nimpl<U, REG> Reg<U, REG>\nwhere\n    Self: ResetValue<Type = U> + Writable,\n    U: Copy,\n{\n    ///Writes the reset value to `Writable` register\n    ///\n    ///Resets the register to its initial state\n    #[inline(always)]\n    pub fn reset(&self) {\n        self.register.set(Self::reset_value())\n    }\n}\n\nimpl<U, REG> Reg<U, REG>\nwhere\n    Self: ResetValue<Type = U> + Writable,\n    U: Copy,\n{\n    ///Writes bits to `Writable` register\n    ///\n    ///You can write raw bits into a register:\n    ///```ignore\n    ///periph.reg.write(|w| unsafe { w.bits(rawbits) });\n    ///```\n    ///or write only the fields you need:\n    ///```ignore\n    ///periph.reg.write(|w| w\n    ///    .field1().bits(newfield1bits)\n    ///    .field2().set_bit()\n    ///    .field3().variant(VARIANT)\n    ///);\n    ///```\n    ///Other fields will have reset value.\n    #[inline(always)]\n    pub fn write<F>(&self, f: F)\n    where\n        F: FnOnce(&mut W<U, Self>) -> &mut W<U, Self>,\n    {\n        self.register.set(\n            f(&mut W {\n                bits: Self::reset_value(),\n                _reg: marker::PhantomData,\n            })\n            .bits,\n        );\n    }\n}\n\n///Register/field reader\n///\n///Result of the [`read`](Reg::read) method of a register.\n///Also it can be used in the [`modify`](Reg::read) method\npub struct R<U, T> {\n    pub(crate) bits: U,\n    _reg: marker::PhantomData<T>,\n}\n\nimpl<U, T> R<U, T>\nwhere\n    U: Copy,\n{\n    ///Create new instance of reader\n    #[inline(always)]\n    pub(crate) fn new(bits: U) -> Self {\n        Self {\n            bits,\n            _reg: marker::PhantomData,\n        }\n    }\n    ///Read raw bits from register/field\n    #[inline(always)]\n    pub fn bits(&self) -> U {\n        self.bits\n    }\n}\n\nimpl<U, T, FI> PartialEq<FI> for R<U, T>\nwhere\n    U: PartialEq,\n    FI: Copy + Into<U>,\n{\n    #[inline(always)]\n    fn eq(&self, other: &FI) -> bool {\n        self.bits.eq(&(*other).into())\n    }\n}\n\nimpl<FI> R<bool, FI> {\n    ///Value of the field as raw bits\n    #[inline(always)]\n    pub fn bit(&self) -> bool {\n        self.bits\n    }\n    ///Returns `true` if the bit is clear (0)\n    #[inline(always)]\n    pub fn bit_is_clear(&self) -> bool {\n        !self.bit()\n    }\n}\n\n///Register writer\n///\n///Used as an argument to the closures in the [`write`](Reg::write) and [`modify`](Reg::modify) methods of the register\npub struct W<U, REG> {\n    ///Writable bits\n    pub(crate) bits: U,\n    _reg: marker::PhantomData<REG>,\n}\n"
  },
  {
    "path": "embassy-stm32/src/can/fd/message_ram/mod.rs",
    "content": "// Note: This file is copied and modified from fdcan crate by Richard Meadows\n\nuse volatile_register::RW;\n\npub(crate) mod common;\npub(crate) mod enums;\npub(crate) mod generic;\n\n/// Number of Receive Fifos configured by this module\npub const RX_FIFOS_MAX: u8 = 2;\n/// Number of Receive Messages per RxFifo configured by this module\npub const RX_FIFO_MAX: u8 = 3;\n/// Number of Transmit Messages configured by this module\npub const TX_FIFO_MAX: u8 = 3;\n/// Number of Transmit Events configured by this module\npub const TX_EVENT_MAX: u8 = 3;\n/// Number of Standard Filters configured by this module\npub const STANDARD_FILTER_MAX: u8 = 28;\n/// Number of Extended Filters configured by this module\npub const EXTENDED_FILTER_MAX: u8 = 8;\n\n/// MessageRam Overlay\n#[repr(C)]\npub struct RegisterBlock {\n    pub(crate) filters: Filters,\n    pub(crate) receive: [Receive; RX_FIFOS_MAX as usize],\n    pub(crate) transmit: Transmit,\n}\nimpl RegisterBlock {\n    pub fn reset(&mut self) {\n        self.filters.reset();\n        self.receive[0].reset();\n        self.receive[1].reset();\n        self.transmit.reset();\n    }\n}\n\n#[repr(C)]\npub(crate) struct Filters {\n    pub(crate) flssa: [StandardFilter; STANDARD_FILTER_MAX as usize],\n    pub(crate) flesa: [ExtendedFilter; EXTENDED_FILTER_MAX as usize],\n}\nimpl Filters {\n    pub fn reset(&mut self) {\n        for sf in &mut self.flssa {\n            sf.reset();\n        }\n        for ef in &mut self.flesa {\n            ef.reset();\n        }\n    }\n}\n\n#[repr(C)]\npub(crate) struct Receive {\n    pub(crate) fxsa: [RxFifoElement; RX_FIFO_MAX as usize],\n}\nimpl Receive {\n    pub fn reset(&mut self) {\n        for fe in &mut self.fxsa {\n            fe.reset();\n        }\n    }\n}\n\n#[repr(C)]\npub(crate) struct Transmit {\n    pub(crate) efsa: [TxEventElement; TX_EVENT_MAX as usize],\n    pub(crate) tbsa: [TxBufferElement; TX_FIFO_MAX as usize],\n}\nimpl Transmit {\n    pub fn reset(&mut self) {\n        for ee in &mut self.efsa {\n            ee.reset();\n        }\n        for be in &mut self.tbsa {\n            be.reset();\n        }\n    }\n}\n\npub(crate) mod standard_filter;\npub(crate) type StandardFilterType = u32;\npub(crate) type StandardFilter = generic::Reg<StandardFilterType, _StandardFilter>;\npub(crate) struct _StandardFilter;\nimpl generic::Readable for StandardFilter {}\nimpl generic::Writable for StandardFilter {}\n\npub(crate) mod extended_filter;\npub(crate) type ExtendedFilterType = [u32; 2];\npub(crate) type ExtendedFilter = generic::Reg<ExtendedFilterType, _ExtendedFilter>;\npub(crate) struct _ExtendedFilter;\nimpl generic::Readable for ExtendedFilter {}\nimpl generic::Writable for ExtendedFilter {}\n\npub(crate) mod txevent_element;\npub(crate) type TxEventElementType = [u32; 2];\npub(crate) type TxEventElement = generic::Reg<TxEventElementType, _TxEventElement>;\npub(crate) struct _TxEventElement;\nimpl generic::Readable for TxEventElement {}\nimpl generic::Writable for TxEventElement {}\n\npub(crate) mod rxfifo_element;\n#[repr(C)]\npub(crate) struct RxFifoElement {\n    pub(crate) header: RxFifoElementHeader,\n    pub(crate) data: [RW<u32>; 16],\n}\nimpl RxFifoElement {\n    pub(crate) fn reset(&mut self) {\n        self.header.reset();\n        for byte in self.data.iter_mut() {\n            unsafe { byte.write(0) };\n        }\n    }\n}\npub(crate) type RxFifoElementHeaderType = [u32; 2];\npub(crate) type RxFifoElementHeader = generic::Reg<RxFifoElementHeaderType, _RxFifoElement>;\npub(crate) struct _RxFifoElement;\nimpl generic::Readable for RxFifoElementHeader {}\nimpl generic::Writable for RxFifoElementHeader {}\n\npub(crate) mod txbuffer_element;\n#[repr(C)]\npub(crate) struct TxBufferElement {\n    pub(crate) header: TxBufferElementHeader,\n    pub(crate) data: [RW<u32>; 16],\n}\nimpl TxBufferElement {\n    pub(crate) fn reset(&mut self) {\n        self.header.reset();\n        for byte in self.data.iter_mut() {\n            unsafe { byte.write(0) };\n        }\n    }\n}\npub(crate) type TxBufferElementHeader = generic::Reg<TxBufferElementHeaderType, _TxBufferElement>;\npub(crate) type TxBufferElementHeaderType = [u32; 2];\npub(crate) struct _TxBufferElement;\nimpl generic::Readable for TxBufferElementHeader {}\nimpl generic::Writable for TxBufferElementHeader {}\n\n// Ensure the RegisterBlock is the same size as on pg 1957 of RM0440.\nstatic_assertions::assert_eq_size!(Filters, [u32; 28 + 16]);\nstatic_assertions::assert_eq_size!(Receive, [u32; 54]);\nstatic_assertions::assert_eq_size!(Transmit, [u32; 6 + 54]);\nstatic_assertions::assert_eq_size!(\n    RegisterBlock,\n    [u32; 28 /*Standard Filters*/ +16 /*Extended Filters*/ +54 /*RxFifo0*/ +54 /*RxFifo1*/ +6 /*TxEvent*/ +54 /*TxFifo */]\n);\n"
  },
  {
    "path": "embassy-stm32/src/can/fd/message_ram/rxfifo_element.rs",
    "content": "// Note: This file is copied and modified from fdcan crate by Richard Meadows\n\n#![allow(non_camel_case_types)]\n#![allow(non_snake_case)]\n#![allow(unused)]\n\nuse super::common::{BRS_R, DLC_R, ESI_R, FDF_R, ID_R, RTR_R, XTD_R};\nuse super::enums::{DataLength, FilterFrameMatch, FrameFormat};\nuse super::generic;\n\n#[doc = \"Reader of register RxFifoElement\"]\npub(crate) type R = generic::R<super::RxFifoElementHeaderType, super::RxFifoElementHeader>;\n// #[doc = \"Writer for register ExtendedFilter\"]\n// pub(crate) type W = generic::W<super::RxFifoElementHeaderType, super::RxFifoElementHeader>;\n#[doc = \"Register ExtendedFilter `reset()`'s\"]\nimpl generic::ResetValue for super::RxFifoElementHeader {\n    type Type = super::RxFifoElementHeaderType;\n    #[inline(always)]\n    fn reset_value() -> Self::Type {\n        [0x0, 0x0]\n    }\n}\n\n#[doc = \"Reader of field `RXTS`\"]\npub(crate) type RXTS_R = generic::R<u16, u16>;\n\n#[doc = \"Reader of field `FIDX`\"]\npub(crate) type FIDX_R = generic::R<u8, u8>;\n\npub(crate) struct _ANMF;\n#[doc = \"Reader of field `ANMF`\"]\npub(crate) type ANMF_R = generic::R<bool, _ANMF>;\nimpl ANMF_R {\n    pub fn is_matching_frame(&self) -> bool {\n        self.bit_is_clear()\n    }\n}\n\nimpl R {\n    #[doc = \"Byte 0 - Bits 0:28 - ID\"]\n    #[inline(always)]\n    pub fn id(&self) -> ID_R {\n        ID_R::new(((self.bits[0]) & 0x1FFFFFFF) as u32)\n    }\n    #[doc = \"Byte 0 - Bit 29 - RTR\"]\n    #[inline(always)]\n    pub fn rtr(&self) -> RTR_R {\n        RTR_R::new(((self.bits[0] >> 29) & 0x01) != 0)\n    }\n    #[doc = \"Byte 0 - Bit 30 - XTD\"]\n    #[inline(always)]\n    pub fn xtd(&self) -> XTD_R {\n        XTD_R::new(((self.bits[0] >> 30) & 0x01) != 0)\n    }\n    #[doc = \"Byte 0 - Bit 30 - ESI\"]\n    #[inline(always)]\n    pub fn esi(&self) -> ESI_R {\n        ESI_R::new(((self.bits[0] >> 31) & 0x01) != 0)\n    }\n    #[doc = \"Byte 1 - Bits 0:15 - RXTS\"]\n    #[inline(always)]\n    pub fn txts(&self) -> RXTS_R {\n        RXTS_R::new(((self.bits[1]) & 0xFFFF) as u16)\n    }\n    #[doc = \"Byte 1 - Bits 16:19 - DLC\"]\n    #[inline(always)]\n    pub fn dlc(&self) -> DLC_R {\n        DLC_R::new(((self.bits[1] >> 16) & 0x0F) as u8)\n    }\n    #[doc = \"Byte 1 - Bits 20 - BRS\"]\n    #[inline(always)]\n    pub fn brs(&self) -> BRS_R {\n        BRS_R::new(((self.bits[1] >> 20) & 0x01) != 0)\n    }\n    #[doc = \"Byte 1 - Bits 20 - FDF\"]\n    #[inline(always)]\n    pub fn fdf(&self) -> FDF_R {\n        FDF_R::new(((self.bits[1] >> 21) & 0x01) != 0)\n    }\n    #[doc = \"Byte 1 - Bits 24:30 - FIDX\"]\n    #[inline(always)]\n    pub fn fidx(&self) -> FIDX_R {\n        FIDX_R::new(((self.bits[1] >> 24) & 0xFF) as u8)\n    }\n    #[doc = \"Byte 1 - Bits 31 - ANMF\"]\n    #[inline(always)]\n    pub fn anmf(&self) -> ANMF_R {\n        ANMF_R::new(((self.bits[1] >> 31) & 0x01) != 0)\n    }\n    pub fn to_data_length(&self) -> DataLength {\n        let dlc = self.dlc().bits();\n        let ff = self.fdf().frame_format();\n        let len = if ff == FrameFormat::Fdcan {\n            // See RM0433 Rev 7 Table 475. DLC coding\n            match dlc {\n                0..=8 => dlc,\n                9 => 12,\n                10 => 16,\n                11 => 20,\n                12 => 24,\n                13 => 32,\n                14 => 48,\n                15 => 64,\n                _ => panic!(\"DLC > 15\"),\n            }\n        } else {\n            match dlc {\n                0..=8 => dlc,\n                9..=15 => 8,\n                _ => panic!(\"DLC > 15\"),\n            }\n        };\n        DataLength::new(len, ff)\n    }\n    pub fn to_filter_match(&self) -> FilterFrameMatch {\n        if self.anmf().is_matching_frame() {\n            FilterFrameMatch::DidMatch(self.fidx().bits())\n        } else {\n            FilterFrameMatch::DidNotMatch\n        }\n    }\n}\n"
  },
  {
    "path": "embassy-stm32/src/can/fd/message_ram/standard_filter.rs",
    "content": "// Note: This file is copied and modified from fdcan crate by Richard Meadows\n\n#![allow(non_camel_case_types)]\n#![allow(non_snake_case)]\n#![allow(unused)]\n\nuse super::common::{ESFEC_R, ESFT_R};\nuse super::enums::{FilterElementConfig, FilterType};\nuse super::generic;\n\n#[doc = \"Reader of register StandardFilter\"]\npub(crate) type R = generic::R<super::StandardFilterType, super::StandardFilter>;\n#[doc = \"Writer for register StandardFilter\"]\npub(crate) type W = generic::W<super::StandardFilterType, super::StandardFilter>;\n#[doc = \"Register StandardFilter `reset()`'s with value 0xC0000\"]\nimpl generic::ResetValue for super::StandardFilter {\n    type Type = super::StandardFilterType;\n    #[inline(always)]\n    fn reset_value() -> Self::Type {\n        // Sets filter element to Disabled\n        0xC000\n    }\n}\n\n#[doc = \"Reader of field `SFID2`\"]\npub(crate) type SFID2_R = generic::R<u16, u16>;\n#[doc = \"Write proxy for field `SFID2`\"]\npub(crate) struct SFID2_W<'a> {\n    w: &'a mut W,\n}\nimpl<'a> SFID2_W<'a> {\n    #[doc = r\"Writes raw bits to the field\"]\n    #[inline(always)]\n    pub unsafe fn bits(self, value: u16) -> &'a mut W {\n        self.w.bits = (self.w.bits & !(0x07ff)) | ((value as u32) & 0x07ff);\n        self.w\n    }\n}\n\n#[doc = \"Reader of field `SFID1`\"]\npub(crate) type SFID1_R = generic::R<u16, u16>;\n#[doc = \"Write proxy for field `SFID1`\"]\npub(crate) struct SFID1_W<'a> {\n    w: &'a mut W,\n}\nimpl<'a> SFID1_W<'a> {\n    #[doc = r\"Writes raw bits to the field\"]\n    #[inline(always)]\n    pub unsafe fn bits(self, value: u16) -> &'a mut W {\n        self.w.bits = (self.w.bits & !(0x07ff << 16)) | (((value as u32) & 0x07ff) << 16);\n        self.w\n    }\n}\n\n#[doc = \"Write proxy for field `SFEC`\"]\npub(crate) struct SFEC_W<'a> {\n    w: &'a mut W,\n}\nimpl<'a> SFEC_W<'a> {\n    #[doc = r\"Writes raw bits to the field\"]\n    #[inline(always)]\n    pub unsafe fn bits(self, value: u8) -> &'a mut W {\n        self.w.bits = (self.w.bits & !(0x07 << 27)) | (((value as u32) & 0x07) << 27);\n        self.w\n    }\n    #[doc = r\"Sets the field according to FilterElementConfig\"]\n    #[inline(always)]\n    pub fn set_filter_element_config(self, fec: FilterElementConfig) -> &'a mut W {\n        //SAFETY: FilterElementConfig only be valid options\n        unsafe { self.bits(fec as u8) }\n    }\n}\n\n#[doc = \"Write proxy for field `SFT`\"]\npub(crate) struct SFT_W<'a> {\n    w: &'a mut W,\n}\nimpl<'a> SFT_W<'a> {\n    #[doc = r\"Sets the field according the FilterType\"]\n    #[inline(always)]\n    pub fn set_filter_type(self, filter: FilterType) -> &'a mut W {\n        //SAFETY: FilterType only be valid options\n        unsafe { self.bits(filter as u8) }\n    }\n    #[doc = r\"Writes raw bits to the field\"]\n    #[inline(always)]\n    pub unsafe fn bits(self, value: u8) -> &'a mut W {\n        self.w.bits = (self.w.bits & !(0x03 << 30)) | (((value as u32) & 0x03) << 30);\n        self.w\n    }\n}\n\nimpl R {\n    #[doc = \"Bits 0:10 - SFID2\"]\n    #[inline(always)]\n    pub fn sfid2(&self) -> SFID2_R {\n        SFID2_R::new((self.bits & 0x07ff) as u16)\n    }\n    #[doc = \"Bits 16:26 - SFID1\"]\n    #[inline(always)]\n    pub fn sfid1(&self) -> SFID1_R {\n        SFID1_R::new(((self.bits >> 16) & 0x07ff) as u16)\n    }\n    #[doc = \"Bits 27:29 - SFEC\"]\n    #[inline(always)]\n    pub fn sfec(&self) -> ESFEC_R {\n        ESFEC_R::new(((self.bits >> 27) & 0x07) as u8)\n    }\n    #[doc = \"Bits 30:31 - SFT\"]\n    #[inline(always)]\n    pub fn sft(&self) -> ESFT_R {\n        ESFT_R::new(((self.bits >> 30) & 0x03) as u8)\n    }\n}\nimpl W {\n    #[doc = \"Bits 0:10 - SFID2\"]\n    #[inline(always)]\n    pub fn sfid2(&mut self) -> SFID2_W<'_> {\n        SFID2_W { w: self }\n    }\n    #[doc = \"Bits 16:26 - SFID1\"]\n    #[inline(always)]\n    pub fn sfid1(&mut self) -> SFID1_W<'_> {\n        SFID1_W { w: self }\n    }\n    #[doc = \"Bits 27:29 - SFEC\"]\n    #[inline(always)]\n    pub fn sfec(&mut self) -> SFEC_W<'_> {\n        SFEC_W { w: self }\n    }\n    #[doc = \"Bits 30:31 - SFT\"]\n    #[inline(always)]\n    pub fn sft(&mut self) -> SFT_W<'_> {\n        SFT_W { w: self }\n    }\n}\n"
  },
  {
    "path": "embassy-stm32/src/can/fd/message_ram/txbuffer_element.rs",
    "content": "// Note: This file is copied and modified from fdcan crate by Richard Meadows\n\n#![allow(non_camel_case_types)]\n#![allow(non_snake_case)]\n#![allow(unused)]\n\nuse super::common::{BRS_R, DLC_R, ESI_R, FDF_R, ID_R, RTR_R, XTD_R};\nuse super::enums::{\n    BitRateSwitching, DataLength, ErrorStateIndicator, Event, EventControl, FrameFormat, IdType,\n    RemoteTransmissionRequest,\n};\nuse super::generic;\n\n#[doc = \"Reader of register TxBufferElement\"]\npub(crate) type R = generic::R<super::TxBufferElementHeaderType, super::TxBufferElementHeader>;\n#[doc = \"Writer for register TxBufferElement\"]\npub(crate) type W = generic::W<super::TxBufferElementHeaderType, super::TxBufferElementHeader>;\nimpl generic::ResetValue for super::TxBufferElementHeader {\n    type Type = super::TxBufferElementHeaderType;\n\n    #[allow(dead_code)]\n    #[inline(always)]\n    fn reset_value() -> Self::Type {\n        [0; 2]\n    }\n}\n\n#[doc = \"Write proxy for field `ESI`\"]\npub(crate) struct ESI_W<'a> {\n    w: &'a mut W,\n}\nimpl<'a> ESI_W<'a> {\n    #[doc = r\"Writes `variant` to the field\"]\n    #[inline(always)]\n    #[allow(dead_code)]\n    pub fn set_error_indicator(self, esi: ErrorStateIndicator) -> &'a mut W {\n        self.bit(esi as u8 != 0)\n    }\n\n    #[doc = r\"Sets the field bit\"]\n    #[inline(always)]\n    #[allow(dead_code)]\n    pub fn set_bit(self) -> &'a mut W {\n        self.bit(true)\n    }\n    #[doc = r\"Clears the field bit\"]\n    #[inline(always)]\n    #[allow(dead_code)]\n    pub fn clear_bit(self) -> &'a mut W {\n        self.bit(false)\n    }\n    #[doc = r\"Writes raw bits to the field\"]\n    #[inline(always)]\n    #[allow(dead_code)]\n    pub fn bit(self, value: bool) -> &'a mut W {\n        self.w.bits[0] = (self.w.bits[0] & !(0x01 << 31)) | (((value as u32) & 0x01) << 31);\n        self.w\n    }\n}\n\n#[doc = \"Write proxy for field `XTD`\"]\npub(crate) struct XTD_W<'a> {\n    w: &'a mut W,\n}\nimpl<'a> XTD_W<'a> {\n    #[doc = r\"Writes `variant` to the field\"]\n    #[inline(always)]\n    #[allow(dead_code)]\n    pub fn set_id_type(self, idt: IdType) -> &'a mut W {\n        self.bit(idt as u8 != 0)\n    }\n\n    #[doc = r\"Sets the field bit\"]\n    #[inline(always)]\n    #[allow(dead_code)]\n    pub fn set_bit(self) -> &'a mut W {\n        self.bit(true)\n    }\n    #[doc = r\"Clears the field bit\"]\n    #[inline(always)]\n    #[allow(dead_code)]\n    pub fn clear_bit(self) -> &'a mut W {\n        self.bit(false)\n    }\n    #[doc = r\"Writes raw bits to the field\"]\n    #[inline(always)]\n    #[allow(dead_code)]\n    pub fn bit(self, value: bool) -> &'a mut W {\n        self.w.bits[0] = (self.w.bits[0] & !(0x01 << 30)) | (((value as u32) & 0x01) << 30);\n        self.w\n    }\n}\n\n#[doc = \"Write proxy for field `RTR`\"]\npub(crate) struct RTR_W<'a> {\n    w: &'a mut W,\n}\nimpl<'a> RTR_W<'a> {\n    #[doc = r\"Writes `variant` to the field\"]\n    #[inline(always)]\n    #[allow(dead_code)]\n    pub fn set_rtr(self, rtr: RemoteTransmissionRequest) -> &'a mut W {\n        self.bit(rtr as u8 != 0)\n    }\n\n    #[doc = r\"Sets the field bit\"]\n    #[inline(always)]\n    #[allow(dead_code)]\n    pub fn set_bit(self) -> &'a mut W {\n        self.bit(true)\n    }\n    #[doc = r\"Clears the field bit\"]\n    #[inline(always)]\n    #[allow(dead_code)]\n    pub fn clear_bit(self) -> &'a mut W {\n        self.bit(false)\n    }\n    #[doc = r\"Writes raw bits to the field\"]\n    #[inline(always)]\n    #[allow(dead_code)]\n    pub fn bit(self, value: bool) -> &'a mut W {\n        self.w.bits[0] = (self.w.bits[0] & !(0x01 << 29)) | (((value as u32) & 0x01) << 29);\n        self.w\n    }\n}\n\n#[doc = \"Write proxy for field `ID`\"]\npub(crate) struct ID_W<'a> {\n    w: &'a mut W,\n}\nimpl<'a> ID_W<'a> {\n    #[doc = r\"Writes raw bits to the field\"]\n    #[inline(always)]\n    #[allow(dead_code)]\n    pub unsafe fn bits(self, value: u32) -> &'a mut W {\n        self.w.bits[0] = (self.w.bits[0] & !(0x1FFFFFFF)) | ((value as u32) & 0x1FFFFFFF);\n        self.w\n    }\n}\n\n#[doc = \"Write proxy for field `DLC`\"]\npub(crate) struct DLC_W<'a> {\n    w: &'a mut W,\n}\nimpl<'a> DLC_W<'a> {\n    #[doc = r\"Writes raw bits to the field\"]\n    #[inline(always)]\n    #[allow(dead_code)]\n    pub unsafe fn bits(self, value: u8) -> &'a mut W {\n        self.w.bits[1] = (self.w.bits[1] & !(0x0F << 16)) | (((value as u32) & 0x0F) << 16);\n        self.w\n    }\n}\n\n#[doc = \"Write proxy for field `BRS`\"]\npub(crate) struct BRS_W<'a> {\n    w: &'a mut W,\n}\nimpl<'a> BRS_W<'a> {\n    #[doc = r\"Writes `variant` to the field\"]\n    #[inline(always)]\n    #[allow(dead_code)]\n    pub fn set_brs(self, brs: BitRateSwitching) -> &'a mut W {\n        self.bit(brs as u8 != 0)\n    }\n\n    #[doc = r\"Sets the field bit\"]\n    #[inline(always)]\n    #[allow(dead_code)]\n    pub fn set_bit(self) -> &'a mut W {\n        self.bit(true)\n    }\n    #[doc = r\"Clears the field bit\"]\n    #[inline(always)]\n    #[allow(dead_code)]\n    pub fn clear_bit(self) -> &'a mut W {\n        self.bit(false)\n    }\n    #[doc = r\"Writes raw bits to the field\"]\n    #[inline(always)]\n    #[allow(dead_code)]\n    pub fn bit(self, value: bool) -> &'a mut W {\n        self.w.bits[1] = (self.w.bits[1] & !(0x01 << 20)) | (((value as u32) & 0x01) << 20);\n        self.w\n    }\n}\n\n#[doc = \"Write proxy for field `FDF`\"]\npub(crate) struct FDF_W<'a> {\n    w: &'a mut W,\n}\nimpl<'a> FDF_W<'a> {\n    #[doc = r\"Writes `variant` to the field\"]\n    #[inline(always)]\n    #[allow(dead_code)]\n    pub fn set_format(self, fdf: FrameFormat) -> &'a mut W {\n        self.bit(fdf as u8 != 0)\n    }\n\n    #[doc = r\"Sets the field bit\"]\n    #[inline(always)]\n    #[allow(dead_code)]\n    pub fn set_bit(self) -> &'a mut W {\n        self.bit(true)\n    }\n    #[doc = r\"Clears the field bit\"]\n    #[inline(always)]\n    #[allow(dead_code)]\n    pub fn clear_bit(self) -> &'a mut W {\n        self.bit(false)\n    }\n    #[doc = r\"Writes raw bits to the field\"]\n    #[inline(always)]\n    #[allow(dead_code)]\n    pub fn bit(self, value: bool) -> &'a mut W {\n        self.w.bits[1] = (self.w.bits[1] & !(0x01 << 21)) | (((value as u32) & 0x01) << 21);\n        self.w\n    }\n}\n\n#[doc = \"Reader of field `EFC`\"]\npub(crate) type EFC_R = generic::R<bool, EventControl>;\nimpl EFC_R {\n    pub fn to_event_control(&self) -> EventControl {\n        match self.bit() {\n            false => EventControl::DoNotStore,\n            true => EventControl::Store,\n        }\n    }\n}\n#[doc = \"Write proxy for field `EFC`\"]\npub(crate) struct EFC_W<'a> {\n    w: &'a mut W,\n}\nimpl<'a> EFC_W<'a> {\n    #[doc = r\"Writes `variant` to the field\"]\n    #[inline(always)]\n    #[allow(dead_code)]\n    pub fn set_event_control(self, efc: EventControl) -> &'a mut W {\n        self.bit(match efc {\n            EventControl::DoNotStore => false,\n            EventControl::Store => true,\n        })\n    }\n\n    #[doc = r\"Sets the field bit\"]\n    #[inline(always)]\n    #[allow(dead_code)]\n    pub fn set_bit(self) -> &'a mut W {\n        self.bit(true)\n    }\n    #[doc = r\"Clears the field bit\"]\n    #[inline(always)]\n    #[allow(dead_code)]\n    pub fn clear_bit(self) -> &'a mut W {\n        self.bit(false)\n    }\n    #[doc = r\"Writes raw bits to the field\"]\n    #[inline(always)]\n    #[allow(dead_code)]\n    pub fn bit(self, value: bool) -> &'a mut W {\n        self.w.bits[1] = (self.w.bits[1] & !(0x01 << 23)) | (((value as u32) & 0x01) << 23);\n        self.w\n    }\n}\n\nstruct Marker(u8);\nimpl From<Event> for Marker {\n    fn from(e: Event) -> Marker {\n        match e {\n            Event::NoEvent => Marker(0),\n            Event::Event(mm) => Marker(mm),\n        }\n    }\n}\n\n#[doc = \"Reader of field `MM`\"]\npub(crate) type MM_R = generic::R<u8, u8>;\n#[doc = \"Write proxy for field `MM`\"]\npub(crate) struct MM_W<'a> {\n    w: &'a mut W,\n}\nimpl<'a> MM_W<'a> {\n    #[doc = r\"Writes raw bits to the field\"]\n    #[inline(always)]\n    pub unsafe fn bits(self, value: u8) -> &'a mut W {\n        self.w.bits[1] = (self.w.bits[1] & !(0x7F << 24)) | (((value as u32) & 0x7F) << 24);\n        self.w\n    }\n\n    fn set_message_marker(self, mm: Marker) -> &'a mut W {\n        unsafe { self.bits(mm.0) }\n    }\n}\n\nimpl R {\n    #[doc = \"Byte 0 - Bits 0:28 - ID\"]\n    #[inline(always)]\n    pub fn id(&self) -> ID_R {\n        ID_R::new(((self.bits[0]) & 0x1FFFFFFF) as u32)\n    }\n    #[doc = \"Byte 0 - Bit 29 - RTR\"]\n    #[inline(always)]\n    pub fn rtr(&self) -> RTR_R {\n        RTR_R::new(((self.bits[0] >> 29) & 0x01) != 0)\n    }\n    #[doc = \"Byte 0 - Bit 30 - XTD\"]\n    #[inline(always)]\n    pub fn xtd(&self) -> XTD_R {\n        XTD_R::new(((self.bits[0] >> 30) & 0x01) != 0)\n    }\n    #[doc = \"Byte 0 - Bit 30 - ESI\"]\n    #[inline(always)]\n    pub fn esi(&self) -> ESI_R {\n        ESI_R::new(((self.bits[0] >> 31) & 0x01) != 0)\n    }\n    #[doc = \"Byte 1 - Bits 16:19 - DLC\"]\n    #[inline(always)]\n    pub fn dlc(&self) -> DLC_R {\n        DLC_R::new(((self.bits[1] >> 16) & 0x0F) as u8)\n    }\n    #[doc = \"Byte 1 - Bits 20 - BRS\"]\n    #[inline(always)]\n    pub fn brs(&self) -> BRS_R {\n        BRS_R::new(((self.bits[1] >> 20) & 0x01) != 0)\n    }\n    #[doc = \"Byte 1 - Bits 20 - FDF\"]\n    #[inline(always)]\n    pub fn fdf(&self) -> FDF_R {\n        FDF_R::new(((self.bits[1] >> 21) & 0x01) != 0)\n    }\n    #[doc = \"Byte 1 - Bits 23 - EFC\"]\n    #[inline(always)]\n    pub fn efc(&self) -> EFC_R {\n        EFC_R::new(((self.bits[1] >> 23) & 0x01) != 0)\n    }\n    #[doc = \"Byte 1 - Bits 24:31 - MM\"]\n    #[inline(always)]\n    pub fn mm(&self) -> MM_R {\n        MM_R::new(((self.bits[1] >> 24) & 0xFF) as u8)\n    }\n    pub fn to_data_length(&self) -> DataLength {\n        let dlc = self.dlc().bits();\n        let ff = self.fdf().frame_format();\n        let len = if ff == FrameFormat::Fdcan {\n            // See RM0433 Rev 7 Table 475. DLC coding\n            match dlc {\n                0..=8 => dlc,\n                9 => 12,\n                10 => 16,\n                11 => 20,\n                12 => 24,\n                13 => 32,\n                14 => 48,\n                15 => 64,\n                _ => panic!(\"DLC > 15\"),\n            }\n        } else {\n            match dlc {\n                0..=8 => dlc,\n                9..=15 => 8,\n                _ => panic!(\"DLC > 15\"),\n            }\n        };\n        DataLength::new(len, ff)\n    }\n    pub fn to_event(&self) -> Event {\n        let mm = self.mm().bits();\n        let efc = self.efc().to_event_control();\n        match efc {\n            EventControl::DoNotStore => Event::NoEvent,\n            EventControl::Store => Event::Event(mm),\n        }\n    }\n}\nimpl W {\n    #[doc = \"Byte 0 - Bits 0:28 - ID\"]\n    #[inline(always)]\n    pub fn id(&mut self) -> ID_W<'_> {\n        ID_W { w: self }\n    }\n    #[doc = \"Byte 0 - Bit 29 - RTR\"]\n    #[inline(always)]\n    pub fn rtr(&mut self) -> RTR_W<'_> {\n        RTR_W { w: self }\n    }\n    #[doc = \"Byte 0 - Bit 30 - XTD\"]\n    #[inline(always)]\n    pub fn xtd(&mut self) -> XTD_W<'_> {\n        XTD_W { w: self }\n    }\n    #[doc = \"Byte 0 - Bit 31 - ESI\"]\n    #[inline(always)]\n    pub fn esi(&mut self) -> ESI_W<'_> {\n        ESI_W { w: self }\n    }\n    #[doc = \"Byte 1 - Bit 16:19 - DLC\"]\n    #[inline(always)]\n    pub fn dlc(&mut self) -> DLC_W<'_> {\n        DLC_W { w: self }\n    }\n    #[doc = \"Byte 1 - Bit 20 - BRS\"]\n    #[inline(always)]\n    pub fn brs(&mut self) -> BRS_W<'_> {\n        BRS_W { w: self }\n    }\n    #[doc = \"Byte 1 - Bit 21 - FDF\"]\n    #[inline(always)]\n    pub fn fdf(&mut self) -> FDF_W<'_> {\n        FDF_W { w: self }\n    }\n    #[doc = \"Byte 1 - Bit 23 - EFC\"]\n    #[inline(always)]\n    pub fn efc(&mut self) -> EFC_W<'_> {\n        EFC_W { w: self }\n    }\n    #[doc = \"Byte 1 - Bit 24:31 - MM\"]\n    #[inline(always)]\n    pub fn mm(&mut self) -> MM_W<'_> {\n        MM_W { w: self }\n    }\n    #[doc = \"Convenience function for setting the data length and frame format\"]\n    #[inline(always)]\n    pub fn set_len(&mut self, dl: impl Into<DataLength>) -> &mut Self {\n        let dl: DataLength = dl.into();\n        self.fdf().set_format(dl.into());\n        unsafe { self.dlc().bits(dl.dlc()) }\n    }\n    pub fn set_event(&mut self, event: Event) -> &mut Self {\n        self.mm().set_message_marker(event.into());\n        self.efc().set_event_control(event.into())\n    }\n}\n"
  },
  {
    "path": "embassy-stm32/src/can/fd/message_ram/txevent_element.rs",
    "content": "// Note: This file is copied and modified from fdcan crate by Richard Meadows\n\n#![allow(non_camel_case_types)]\n#![allow(non_snake_case)]\n#![allow(unused)]\n\nuse super::common::{BRS_R, DLC_R, ESI_R, RTR_R, XTD_R};\nuse super::generic;\n\n#[doc = \"Reader of register TxEventElement\"]\npub(crate) type R = generic::R<super::TxEventElementType, super::TxEventElement>;\n// #[doc = \"Writer for register TxEventElement\"]\n// pub(crate) type W = generic::W<super::TxEventElementType, super::TxEventElement>;\n#[doc = \"Register TxEventElement `reset()`'s\"]\nimpl generic::ResetValue for super::TxEventElement {\n    type Type = super::TxEventElementType;\n    #[inline(always)]\n    fn reset_value() -> Self::Type {\n        [0, 0]\n    }\n}\n\n#[doc = \"Reader of field `ID`\"]\npub(crate) type ID_R = generic::R<u32, u32>;\n\n#[doc = \"Reader of field `TXTS`\"]\npub(crate) type TXTS_R = generic::R<u16, u16>;\n\n#[derive(Clone, Copy, Debug, PartialEq)]\npub(crate) enum DataLengthFormat {\n    StandardLength = 0,\n    FDCANLength = 1,\n}\nimpl From<DataLengthFormat> for bool {\n    #[inline(always)]\n    fn from(dlf: DataLengthFormat) -> Self {\n        dlf as u8 != 0\n    }\n}\n\n#[doc = \"Reader of field `EDL`\"]\npub(crate) type EDL_R = generic::R<bool, DataLengthFormat>;\nimpl EDL_R {\n    pub fn data_length_format(&self) -> DataLengthFormat {\n        match self.bits() {\n            false => DataLengthFormat::StandardLength,\n            true => DataLengthFormat::FDCANLength,\n        }\n    }\n    pub fn is_standard_length(&self) -> bool {\n        *self == DataLengthFormat::StandardLength\n    }\n    pub fn is_fdcan_length(&self) -> bool {\n        *self == DataLengthFormat::FDCANLength\n    }\n}\n\n#[derive(Clone, Copy, Debug, PartialEq)]\npub(crate) enum EventType {\n    //_Reserved = 0b00,\n    TxEvent = 0b01,\n    TxDespiteAbort = 0b10,\n    //_Reserved = 0b10,\n}\n\n#[doc = \"Reader of field `EFC`\"]\npub(crate) type EFC_R = generic::R<u8, EventType>;\nimpl EFC_R {\n    pub fn event_type(&self) -> EventType {\n        match self.bits() {\n            0b01 => EventType::TxEvent,\n            0b10 => EventType::TxDespiteAbort,\n            _ => unimplemented!(),\n        }\n    }\n    pub fn is_tx_event(&self) -> bool {\n        self.event_type() == EventType::TxEvent\n    }\n    pub fn is_despite_abort(&self) -> bool {\n        self.event_type() == EventType::TxDespiteAbort\n    }\n}\n\n#[doc = \"Reader of field `MM`\"]\npub(crate) type MM_R = generic::R<u8, u8>;\n\nimpl R {\n    #[doc = \"Byte 0 - Bits 0:28 - ID\"]\n    #[inline(always)]\n    pub fn id(&self) -> ID_R {\n        ID_R::new(((self.bits[0]) & 0x1FFFFFFF) as u32)\n    }\n    #[doc = \"Byte 0 - Bit 29 - RTR\"]\n    #[inline(always)]\n    pub fn rtr(&self) -> RTR_R {\n        RTR_R::new(((self.bits[0] >> 29) & 0x01) != 0)\n    }\n    #[doc = \"Byte 0 - Bit 30 - XTD\"]\n    #[inline(always)]\n    pub fn xtd(&self) -> XTD_R {\n        XTD_R::new(((self.bits[0] >> 30) & 0x01) != 0)\n    }\n    #[doc = \"Byte 0 - Bit 30 - ESI\"]\n    #[inline(always)]\n    pub fn esi(&self) -> ESI_R {\n        ESI_R::new(((self.bits[0] >> 31) & 0x01) != 0)\n    }\n    #[doc = \"Byte 1 - Bits 0:15 - TXTS\"]\n    #[inline(always)]\n    pub fn txts(&self) -> TXTS_R {\n        TXTS_R::new(((self.bits[1]) & 0xFFFF) as u16)\n    }\n    #[doc = \"Byte 1 - Bits 16:19 - DLC\"]\n    #[inline(always)]\n    pub fn dlc(&self) -> DLC_R {\n        DLC_R::new(((self.bits[1] >> 16) & 0x0F) as u8)\n    }\n    #[doc = \"Byte 1 - Bits 20 - BRS\"]\n    #[inline(always)]\n    pub fn brs(&self) -> BRS_R {\n        BRS_R::new(((self.bits[1] >> 20) & 0x01) != 0)\n    }\n    #[doc = \"Byte 1 - Bits 21 - EDL\"]\n    #[inline(always)]\n    pub fn edl(&self) -> EDL_R {\n        EDL_R::new(((self.bits[1] >> 21) & 0x01) != 0)\n    }\n    #[doc = \"Byte 1 - Bits 22:23 - EFC\"]\n    #[inline(always)]\n    pub fn efc(&self) -> EFC_R {\n        EFC_R::new(((self.bits[1] >> 22) & 0x03) as u8)\n    }\n    #[doc = \"Byte 1 - Bits 24:31 - MM\"]\n    #[inline(always)]\n    pub fn mm(&self) -> MM_R {\n        MM_R::new(((self.bits[1] >> 24) & 0xFF) as u8)\n    }\n}\n"
  },
  {
    "path": "embassy-stm32/src/can/fd/mod.rs",
    "content": "//! Module containing that which is specific to fdcan hardware variant\n\npub mod config;\npub mod filter;\npub(crate) mod message_ram;\npub(crate) mod peripheral;\n"
  },
  {
    "path": "embassy-stm32/src/can/fd/peripheral.rs",
    "content": "// Note: This file is copied and modified from fdcan crate by Richard Meadows\n\nuse core::convert::Infallible;\nuse core::slice;\n\nuse cfg_if::cfg_if;\n\nuse crate::can::enums::*;\nuse crate::can::fd::config::*;\nuse crate::can::fd::message_ram::enums::*;\nuse crate::can::fd::message_ram::{RegisterBlock, RxFifoElement, TxBufferElement};\nuse crate::can::frame::*;\n\n/// Loopback Mode\n#[derive(Clone, Copy, Debug)]\nenum LoopbackMode {\n    None,\n    Internal,\n    External,\n}\n\npub struct Registers {\n    pub regs: crate::pac::can::Fdcan,\n    pub msgram: crate::pac::fdcanram::Fdcanram,\n    #[allow(dead_code)]\n    pub msg_ram_offset: usize,\n}\n\nimpl Registers {\n    fn tx_buffer_element(&self, bufidx: usize) -> &mut TxBufferElement {\n        &mut self.msg_ram_mut().transmit.tbsa[bufidx]\n    }\n    pub fn msg_ram_mut(&self) -> &mut RegisterBlock {\n        #[cfg(can_fdcan_h7)]\n        let ptr = self.msgram.ram(self.msg_ram_offset / 4).as_ptr() as *mut RegisterBlock;\n\n        #[cfg(not(can_fdcan_h7))]\n        let ptr = self.msgram.as_ptr() as *mut RegisterBlock;\n\n        unsafe { &mut (*ptr) }\n    }\n\n    fn rx_fifo_element(&self, fifonr: usize, bufnum: usize) -> &mut RxFifoElement {\n        &mut self.msg_ram_mut().receive[fifonr].fxsa[bufnum]\n    }\n\n    pub fn read<F: CanHeader>(&self, fifonr: usize) -> Option<(F, u16)> {\n        // Fill level - do we have a msg?\n        if self.regs.rxfs(fifonr).read().ffl() < 1 {\n            return None;\n        }\n\n        let read_idx = self.regs.rxfs(fifonr).read().fgi();\n        let mailbox = self.rx_fifo_element(fifonr, read_idx as usize);\n\n        let mut buffer = [0u8; 64];\n        let maybe_header = extract_frame(mailbox, &mut buffer);\n\n        // Clear FIFO, reduces count and increments read buf\n        self.regs.rxfa(fifonr).modify(|w| w.set_fai(read_idx));\n\n        match maybe_header {\n            Some((header, ts)) => {\n                let data = &buffer[0..header.len() as usize];\n                match F::from_header(header, data) {\n                    Ok(frame) => Some((frame, ts)),\n                    Err(_) => None,\n                }\n            }\n            None => None,\n        }\n    }\n\n    #[cfg(feature = \"time\")]\n    pub fn calc_timestamp(&self, ns_per_timer_tick: u64, ts_val: u16) -> Timestamp {\n        let now_embassy = embassy_time::Instant::now();\n        if ns_per_timer_tick == 0 {\n            return now_embassy;\n        }\n        let cantime = { self.regs.tscv().read().tsc() };\n        let delta = cantime.overflowing_sub(ts_val).0 as u64;\n        let ns = ns_per_timer_tick * delta as u64;\n        now_embassy - embassy_time::Duration::from_nanos(ns)\n    }\n\n    #[cfg(not(feature = \"time\"))]\n    pub fn calc_timestamp(&self, _ns_per_timer_tick: u64, ts_val: u16) -> Timestamp {\n        ts_val\n    }\n\n    pub fn put_tx_frame(&self, bufidx: usize, header: &Header, buffer: &[u8]) {\n        let mailbox = self.tx_buffer_element(bufidx);\n        mailbox.reset();\n        put_tx_header(mailbox, header);\n        put_tx_data(mailbox, buffer);\n\n        // Set <idx as Mailbox> as ready to transmit\n        self.regs.txbar().modify(|w| w.set_ar(bufidx, true));\n    }\n\n    fn reg_to_error(value: u8) -> Option<BusError> {\n        match value {\n            // 0b000 => None,\n            0b001 => Some(BusError::Stuff),\n            0b010 => Some(BusError::Form),\n            0b011 => Some(BusError::Acknowledge),\n            0b100 => Some(BusError::BitRecessive),\n            0b101 => Some(BusError::BitDominant),\n            0b110 => Some(BusError::Crc),\n            // 0b111 => Some(BusError::NoError),\n            _ => None,\n        }\n    }\n\n    pub fn curr_error(&self) -> Option<BusError> {\n        let err = { self.regs.psr().read() };\n        cfg_if! {\n            if #[cfg(can_fdcan_h7)] {\n                let lec = err.lec();\n            } else {\n                let lec = err.lec().to_bits();\n            }\n        }\n        if let Some(err) = Self::reg_to_error(lec) {\n            return Some(err);\n        }\n        None\n    }\n    /// Returns if the tx queue is able to accept new messages without having to cancel an existing one\n    #[inline]\n    pub fn tx_queue_is_full(&self) -> bool {\n        self.regs.txfqs().read().tfqf()\n    }\n\n    /// Returns the current TX buffer operation mode (queue or FIFO)\n    #[inline]\n    pub fn tx_queue_mode(&self) -> TxBufferMode {\n        self.regs.txbc().read().tfqm().into()\n    }\n\n    #[inline]\n    pub fn has_pending_frame(&self, idx: usize) -> bool {\n        self.regs.txbrp().read().trp(idx)\n    }\n\n    /// Returns `Ok` when the mailbox is free or if it contains pending frame with a\n    /// lower priority (higher ID) than the identifier `id`.\n    #[inline]\n    pub fn is_available(&self, bufidx: usize, id: &embedded_can::Id) -> bool {\n        if self.has_pending_frame(bufidx) {\n            let mailbox = self.tx_buffer_element(bufidx);\n\n            let header_reg = mailbox.header.read();\n            let old_id = make_id(header_reg.id().bits(), header_reg.xtd().bits());\n\n            *id > old_id\n        } else {\n            true\n        }\n    }\n\n    /// Attempts to abort the sending of a frame that is pending in a mailbox.\n    ///\n    /// If there is no frame in the provided mailbox, or its transmission succeeds before it can be\n    /// aborted, this function has no effect and returns `false`.\n    ///\n    /// If there is a frame in the provided mailbox, and it is canceled successfully, this function\n    /// returns `true`.\n    #[inline]\n    pub fn abort(&self, bufidx: usize) -> bool {\n        let can = self.regs;\n\n        // Check if there is a request pending to abort\n        if self.has_pending_frame(bufidx) {\n            // Abort Request\n            can.txbcr().write(|w| w.set_cr(bufidx, true));\n\n            // Wait for the abort request to be finished.\n            loop {\n                if can.txbcf().read().cf(bufidx) {\n                    // Return false when a transmission has occured\n                    break can.txbto().read().to(bufidx) == false;\n                }\n            }\n        } else {\n            false\n        }\n    }\n\n    #[inline]\n    fn abort_pending_mailbox<F: embedded_can::Frame>(&self, bufidx: usize) -> Option<F> {\n        if self.abort(bufidx) {\n            let mailbox = self.tx_buffer_element(bufidx);\n\n            let header_reg = mailbox.header.read();\n            let id = make_id(header_reg.id().bits(), header_reg.xtd().bits());\n\n            let len = match header_reg.to_data_length() {\n                DataLength::Fdcan(len) => len,\n                DataLength::Classic(len) => len,\n            };\n            if len as usize > 8 {\n                return None;\n            }\n\n            let mut data = [0u8; 64];\n            data_from_tx_buffer(&mut data, mailbox, len as usize);\n\n            if header_reg.rtr().bit() {\n                F::new_remote(id, len as usize)\n            } else {\n                F::new(id, &data[0..(len as usize)])\n            }\n        } else {\n            // Abort request failed because the frame was already sent (or being sent) on\n            // the bus. All mailboxes are now free. This can happen for small prescaler\n            // values (e.g. 1MBit/s bit timing with a source clock of 8MHz) or when an ISR\n            // has preempted the execution.\n            None\n        }\n    }\n\n    pub fn write<F: embedded_can::Frame + CanHeader>(&self, frame: &F) -> nb::Result<Option<F>, Infallible> {\n        let (idx, pending_frame) = if self.tx_queue_is_full() {\n            if self.tx_queue_mode() == TxBufferMode::Fifo {\n                // Does not make sense to cancel a pending frame when using FIFO\n                return Err(nb::Error::WouldBlock);\n            }\n            // If the queue is full,\n            // Discard the first slot with a lower priority message\n            let id = frame.header().id();\n            if self.is_available(0, id) {\n                (0, self.abort_pending_mailbox(0))\n            } else if self.is_available(1, id) {\n                (1, self.abort_pending_mailbox(1))\n            } else if self.is_available(2, id) {\n                (2, self.abort_pending_mailbox(2))\n            } else {\n                // For now we bail when there is no lower priority slot available\n                // Can this lead to priority inversion?\n                return Err(nb::Error::WouldBlock);\n            }\n        } else {\n            // Read the Write Pointer\n            let idx = self.regs.txfqs().read().tfqpi();\n\n            (idx, None)\n        };\n\n        self.put_tx_frame(idx as usize, frame.header(), frame.data());\n\n        Ok(pending_frame)\n    }\n\n    #[inline]\n    fn reset_msg_ram(&self) {\n        self.msg_ram_mut().reset();\n    }\n\n    #[inline]\n    fn enter_init_mode(&self) {\n        self.regs.cccr().modify(|w| w.set_init(true));\n        while false == self.regs.cccr().read().init() {}\n        self.regs.cccr().modify(|w| w.set_cce(true));\n    }\n\n    /// Enables or disables loopback mode: Internally connects the TX and RX\n    /// signals together.\n    #[inline]\n    fn set_loopback_mode(&self, mode: LoopbackMode) {\n        let (test, mon, lbck) = match mode {\n            LoopbackMode::None => (false, false, false),\n            LoopbackMode::Internal => (true, true, true),\n            LoopbackMode::External => (true, false, true),\n        };\n\n        self.set_test_mode(test);\n        self.set_bus_monitoring_mode(mon);\n\n        self.regs.test().modify(|w| w.set_lbck(lbck));\n    }\n\n    /// Enables or disables silent mode: Disconnects the TX signal from the pin.\n    #[inline]\n    fn set_bus_monitoring_mode(&self, enabled: bool) {\n        self.regs.cccr().modify(|w| w.set_mon(enabled));\n    }\n\n    #[inline]\n    fn set_restricted_operations(&self, enabled: bool) {\n        self.regs.cccr().modify(|w| w.set_asm(enabled));\n    }\n\n    #[inline]\n    fn set_normal_operations(&self, _enabled: bool) {\n        self.set_loopback_mode(LoopbackMode::None);\n    }\n\n    #[inline]\n    fn set_test_mode(&self, enabled: bool) {\n        self.regs.cccr().modify(|w| w.set_test(enabled));\n    }\n\n    #[inline]\n    fn set_power_down_mode(&self, enabled: bool) {\n        self.regs.cccr().modify(|w| w.set_csr(enabled));\n        while self.regs.cccr().read().csa() != enabled {}\n    }\n\n    /// Moves out of PoweredDownMode and into ConfigMode\n    #[inline]\n    pub fn into_config_mode(self, _config: FdCanConfig) {\n        self.set_power_down_mode(false);\n        self.enter_init_mode();\n        self.reset_msg_ram();\n\n        // check the FDCAN core matches our expections\n        assert!(\n            self.regs.crel().read().rel() == 3,\n            \"Expected FDCAN core major release 3\"\n        );\n        assert!(\n            self.regs.endn().read().etv() == 0x87654321_u32,\n            \"Error reading endianness test value from FDCAN core\"\n        );\n\n        /*\n        for fid in 0..crate::can::message_ram::STANDARD_FILTER_MAX {\n            self.set_standard_filter((fid as u8).into(), StandardFilter::disable());\n        }\n        for fid in 0..Ecrate::can::message_ram::XTENDED_FILTER_MAX {\n            self.set_extended_filter(fid.into(), ExtendedFilter::disable());\n        }\n        */\n    }\n\n    /// Applies the settings of a new FdCanConfig See [`FdCanConfig`]\n    #[inline]\n    pub fn apply_config(&self, config: FdCanConfig) {\n        self.set_tx_buffer_mode(config.tx_buffer_mode);\n\n        // set standard filters list size to 28\n        // set extended filters list size to 8\n        // REQUIRED: we use the memory map as if these settings are set\n        // instead of re-calculating them.\n        #[cfg(not(can_fdcan_h7))]\n        {\n            self.regs.rxgfc().modify(|w| {\n                w.set_lss(crate::can::fd::message_ram::STANDARD_FILTER_MAX);\n                w.set_lse(crate::can::fd::message_ram::EXTENDED_FILTER_MAX);\n            });\n        }\n        #[cfg(can_fdcan_h7)]\n        {\n            self.regs\n                .sidfc()\n                .modify(|w| w.set_lss(crate::can::fd::message_ram::STANDARD_FILTER_MAX));\n            self.regs\n                .xidfc()\n                .modify(|w| w.set_lse(crate::can::fd::message_ram::EXTENDED_FILTER_MAX));\n        }\n\n        self.configure_msg_ram();\n\n        // Enable timestamping\n        #[cfg(not(can_fdcan_h7))]\n        self.regs\n            .tscc()\n            .write(|w| w.set_tss(stm32_metapac::can::vals::Tss::INCREMENT));\n        #[cfg(can_fdcan_h7)]\n        self.regs.tscc().write(|w| w.set_tss(0x01));\n\n        // this isn't really documented in the reference manual\n        // but corresponding txbtie bit has to be set for the TC (TxComplete) interrupt to fire\n        self.regs.txbtie().write(|w| w.0 = 0xffff_ffff);\n        self.regs.ie().modify(|w| {\n            w.set_rfne(0, true); // Rx Fifo 0 New Msg\n            w.set_rfne(1, true); // Rx Fifo 1 New Msg\n            w.set_tce(true); //  Tx Complete\n            w.set_boe(true); // Bus-Off Status Changed\n        });\n        self.regs.ile().modify(|w| {\n            w.set_eint0(true); // Interrupt Line 0\n            w.set_eint1(true); // Interrupt Line 1\n        });\n\n        self.set_data_bit_timing(config.dbtr);\n        self.set_nominal_bit_timing(config.nbtr);\n        self.set_automatic_retransmit(config.automatic_retransmit);\n        self.set_transmit_pause(config.transmit_pause);\n        self.set_frame_transmit(config.frame_transmit);\n        //self.set_interrupt_line_config(config.interrupt_line_config);\n        self.set_non_iso_mode(config.non_iso_mode);\n        self.set_edge_filtering(config.edge_filtering);\n        self.set_protocol_exception_handling(config.protocol_exception_handling);\n        self.set_global_filter(config.global_filter);\n    }\n\n    #[inline]\n    fn leave_init_mode(&self, config: FdCanConfig) {\n        self.apply_config(config);\n\n        self.regs.cccr().modify(|w| w.set_cce(false));\n        self.regs.cccr().modify(|w| w.set_init(false));\n        while self.regs.cccr().read().init() == true {}\n    }\n\n    /// Moves out of ConfigMode and into specified mode\n    #[inline]\n    pub fn into_mode(&self, config: FdCanConfig, mode: crate::can::_version::OperatingMode) {\n        match mode {\n            crate::can::OperatingMode::InternalLoopbackMode => self.set_loopback_mode(LoopbackMode::Internal),\n            crate::can::OperatingMode::ExternalLoopbackMode => self.set_loopback_mode(LoopbackMode::External),\n            crate::can::OperatingMode::NormalOperationMode => self.set_normal_operations(true),\n            crate::can::OperatingMode::RestrictedOperationMode => self.set_restricted_operations(true),\n            crate::can::OperatingMode::BusMonitoringMode => self.set_bus_monitoring_mode(true),\n        }\n        self.leave_init_mode(config);\n    }\n\n    /// Configures the bit timings.\n    ///\n    /// You can use <http://www.bittiming.can-wiki.info/> to calculate the `btr` parameter. Enter\n    /// parameters as follows:\n    ///\n    /// - *Clock Rate*: The input clock speed to the CAN peripheral (*not* the CPU clock speed).\n    ///   This is the clock rate of the peripheral bus the CAN peripheral is attached to (eg. APB1).\n    /// - *Sample Point*: Should normally be left at the default value of 87.5%.\n    /// - *SJW*: Should normally be left at the default value of 1.\n    ///\n    /// Then copy the `CAN_BUS_TIME` register value from the table and pass it as the `btr`\n    /// parameter to this method.\n    #[inline]\n    pub fn set_nominal_bit_timing(&self, btr: NominalBitTiming) {\n        self.regs.nbtp().write(|w| {\n            w.set_nbrp(btr.nbrp() - 1);\n            w.set_ntseg1(btr.ntseg1() - 1);\n            w.set_ntseg2(btr.ntseg2() - 1);\n            w.set_nsjw(btr.nsjw() - 1);\n        });\n    }\n\n    /// Configures the data bit timings for the FdCan Variable Bitrates.\n    /// This is not used when frame_transmit is set to anything other than AllowFdCanAndBRS.\n    #[inline]\n    pub fn set_data_bit_timing(&self, btr: DataBitTiming) {\n        self.regs.dbtp().write(|w| {\n            w.set_dbrp(btr.dbrp() - 1);\n            w.set_dtseg1(btr.dtseg1() - 1);\n            w.set_dtseg2(btr.dtseg2() - 1);\n            w.set_dsjw(btr.dsjw() - 1);\n        });\n    }\n\n    /// Enables or disables automatic retransmission of messages\n    ///\n    /// If this is enabled, the CAN peripheral will automatically try to retransmit each frame\n    /// util it can be sent. Otherwise, it will try only once to send each frame.\n    ///\n    /// Automatic retransmission is enabled by default.\n    #[inline]\n    pub fn set_automatic_retransmit(&self, enabled: bool) {\n        self.regs.cccr().modify(|w| w.set_dar(!enabled));\n    }\n\n    /// Configures the transmit pause feature. See\n    /// [`FdCanConfig::set_transmit_pause`]\n    #[inline]\n    pub fn set_transmit_pause(&self, enabled: bool) {\n        self.regs.cccr().modify(|w| w.set_txp(enabled));\n    }\n\n    /// Configures non-iso mode. See [`FdCanConfig::set_non_iso_mode`]\n    #[inline]\n    pub fn set_non_iso_mode(&self, enabled: bool) {\n        self.regs.cccr().modify(|w| w.set_niso(enabled));\n    }\n\n    /// Configures edge filtering. See [`FdCanConfig::set_edge_filtering`]\n    #[inline]\n    pub fn set_edge_filtering(&self, enabled: bool) {\n        self.regs.cccr().modify(|w| w.set_efbi(enabled));\n    }\n\n    /// Configures TX Buffer Mode\n    #[inline]\n    pub fn set_tx_buffer_mode(&self, tbm: TxBufferMode) {\n        self.regs.txbc().write(|w| w.set_tfqm(tbm.into()));\n    }\n\n    /// Configures frame transmission mode. See\n    /// [`FdCanConfig::set_frame_transmit`]\n    #[inline]\n    pub fn set_frame_transmit(&self, fts: FrameTransmissionConfig) {\n        let (fdoe, brse) = match fts {\n            FrameTransmissionConfig::ClassicCanOnly => (false, false),\n            FrameTransmissionConfig::AllowFdCan => (true, false),\n            FrameTransmissionConfig::AllowFdCanAndBRS => (true, true),\n        };\n\n        self.regs.cccr().modify(|w| {\n            w.set_fdoe(fdoe);\n            #[cfg(can_fdcan_h7)]\n            w.set_bse(brse);\n            #[cfg(not(can_fdcan_h7))]\n            w.set_brse(brse);\n        });\n    }\n\n    /// Sets the protocol exception handling on/off\n    #[inline]\n    pub fn set_protocol_exception_handling(&self, enabled: bool) {\n        self.regs.cccr().modify(|w| w.set_pxhd(!enabled));\n    }\n\n    /// Configures and resets the timestamp counter\n    #[inline]\n    #[allow(unused)]\n    pub fn set_timestamp_counter_source(&self, select: TimestampSource) {\n        #[cfg(can_fdcan_h7)]\n        let (tcp, tss) = match select {\n            TimestampSource::None => (0, 0),\n            TimestampSource::Prescaler(p) => (p as u8, 1),\n            TimestampSource::FromTIM3 => (0, 2),\n        };\n\n        #[cfg(not(can_fdcan_h7))]\n        let (tcp, tss) = match select {\n            TimestampSource::None => (0, stm32_metapac::can::vals::Tss::ZERO),\n            TimestampSource::Prescaler(p) => (p as u8, stm32_metapac::can::vals::Tss::INCREMENT),\n            TimestampSource::FromTIM3 => (0, stm32_metapac::can::vals::Tss::EXTERNAL),\n        };\n\n        self.regs.tscc().write(|w| {\n            w.set_tcp(tcp);\n            w.set_tss(tss);\n        });\n    }\n\n    #[cfg(not(can_fdcan_h7))]\n    /// Configures the global filter settings\n    #[inline]\n    pub fn set_global_filter(&self, filter: GlobalFilter) {\n        let anfs = match filter.handle_standard_frames {\n            crate::can::fd::config::NonMatchingFilter::IntoRxFifo0 => stm32_metapac::can::vals::Anfs::ACCEPT_FIFO_0,\n            crate::can::fd::config::NonMatchingFilter::IntoRxFifo1 => stm32_metapac::can::vals::Anfs::ACCEPT_FIFO_1,\n            crate::can::fd::config::NonMatchingFilter::Reject => stm32_metapac::can::vals::Anfs::REJECT,\n        };\n        let anfe = match filter.handle_extended_frames {\n            crate::can::fd::config::NonMatchingFilter::IntoRxFifo0 => stm32_metapac::can::vals::Anfe::ACCEPT_FIFO_0,\n            crate::can::fd::config::NonMatchingFilter::IntoRxFifo1 => stm32_metapac::can::vals::Anfe::ACCEPT_FIFO_1,\n            crate::can::fd::config::NonMatchingFilter::Reject => stm32_metapac::can::vals::Anfe::REJECT,\n        };\n\n        self.regs.rxgfc().modify(|w| {\n            w.set_anfs(anfs);\n            w.set_anfe(anfe);\n            w.set_rrfs(filter.reject_remote_standard_frames);\n            w.set_rrfe(filter.reject_remote_extended_frames);\n        });\n    }\n\n    #[cfg(can_fdcan_h7)]\n    /// Configures the global filter settings\n    #[inline]\n    pub fn set_global_filter(&self, filter: GlobalFilter) {\n        let anfs = match filter.handle_standard_frames {\n            crate::can::fd::config::NonMatchingFilter::IntoRxFifo0 => 0,\n            crate::can::fd::config::NonMatchingFilter::IntoRxFifo1 => 1,\n            crate::can::fd::config::NonMatchingFilter::Reject => 2,\n        };\n\n        let anfe = match filter.handle_extended_frames {\n            crate::can::fd::config::NonMatchingFilter::IntoRxFifo0 => 0,\n            crate::can::fd::config::NonMatchingFilter::IntoRxFifo1 => 1,\n            crate::can::fd::config::NonMatchingFilter::Reject => 2,\n        };\n\n        self.regs.gfc().modify(|w| {\n            w.set_anfs(anfs);\n            w.set_anfe(anfe);\n            w.set_rrfs(filter.reject_remote_standard_frames);\n            w.set_rrfe(filter.reject_remote_extended_frames);\n        });\n    }\n\n    #[cfg(not(can_fdcan_h7))]\n    fn configure_msg_ram(&self) {}\n\n    #[cfg(can_fdcan_h7)]\n    fn configure_msg_ram(&self) {\n        let r = self.regs;\n\n        use crate::can::fd::message_ram::*;\n        //use fdcan::message_ram::*;\n        let mut offset_words = (self.msg_ram_offset / 4) as u16;\n\n        // 11-bit filter\n        r.sidfc().modify(|w| w.set_flssa(offset_words));\n        offset_words += STANDARD_FILTER_MAX as u16;\n\n        // 29-bit filter\n        r.xidfc().modify(|w| w.set_flesa(offset_words));\n        offset_words += 2 * EXTENDED_FILTER_MAX as u16;\n\n        // Rx FIFO 0 and 1\n        for i in 0..=1 {\n            r.rxfc(i).modify(|w| {\n                w.set_fsa(offset_words);\n                w.set_fs(RX_FIFO_MAX);\n                w.set_fwm(RX_FIFO_MAX);\n            });\n            offset_words += 18 * RX_FIFO_MAX as u16;\n        }\n\n        // Rx buffer - see below\n        // Tx event FIFO\n        r.txefc().modify(|w| {\n            w.set_efsa(offset_words);\n            w.set_efs(TX_EVENT_MAX);\n            w.set_efwm(TX_EVENT_MAX);\n        });\n        offset_words += 2 * TX_EVENT_MAX as u16;\n\n        // Tx buffers\n        r.txbc().modify(|w| {\n            w.set_tbsa(offset_words);\n            w.set_tfqs(TX_FIFO_MAX);\n        });\n        offset_words += 18 * TX_FIFO_MAX as u16;\n\n        // Rx Buffer - not used\n        r.rxbc().modify(|w| {\n            w.set_rbsa(offset_words);\n        });\n\n        // TX event FIFO?\n        // Trigger memory?\n\n        // Set the element sizes to 16 bytes\n        r.rxesc().modify(|w| {\n            w.set_rbds(0b111);\n            for i in 0..=1 {\n                w.set_fds(i, 0b111);\n            }\n        });\n        r.txesc().modify(|w| {\n            w.set_tbds(0b111);\n        })\n    }\n}\n\nfn make_id(id: u32, extended: bool) -> embedded_can::Id {\n    if extended {\n        embedded_can::Id::from(unsafe { embedded_can::ExtendedId::new_unchecked(id & 0x1FFFFFFF) })\n    } else {\n        // A standard identifier is stored into ID[28:18].\n        embedded_can::Id::from(unsafe { embedded_can::StandardId::new_unchecked(((id >> 18) & 0x000007FF) as u16) })\n    }\n}\n\nfn put_tx_header(mailbox: &mut TxBufferElement, header: &Header) {\n    let (id, id_type) = match header.id() {\n        // A standard identifier has to be written to ID[28:18].\n        embedded_can::Id::Standard(id) => ((id.as_raw() as u32) << 18, IdType::StandardId),\n        embedded_can::Id::Extended(id) => (id.as_raw() as u32, IdType::ExtendedId),\n    };\n\n    // Use FDCAN only for DLC > 8. FDCAN users can revise this if required.\n    let frame_format = if header.len() > 8 || header.fdcan() {\n        FrameFormat::Fdcan\n    } else {\n        FrameFormat::Classic\n    };\n    let brs = (frame_format == FrameFormat::Fdcan) && header.bit_rate_switching();\n\n    mailbox.header.write(|w| {\n        unsafe { w.id().bits(id) }\n            .rtr()\n            .bit(header.len() == 0 && header.rtr())\n            .xtd()\n            .set_id_type(id_type)\n            .set_len(DataLength::new(header.len(), frame_format))\n            .set_event(Event::NoEvent)\n            .fdf()\n            .set_format(frame_format)\n            .brs()\n            .bit(brs)\n        //esi.set_error_indicator(//TODO//)\n    });\n}\n\nfn put_tx_data(mailbox: &mut TxBufferElement, buffer: &[u8]) {\n    let mut lbuffer = [0_u32; 16];\n    let len = buffer.len();\n    let data = unsafe { slice::from_raw_parts_mut(lbuffer.as_mut_ptr() as *mut u8, len) };\n    data[..len].copy_from_slice(&buffer[..len]);\n    let data_len = ((len) + 3) / 4;\n    for (register, byte) in mailbox.data.iter_mut().zip(lbuffer[..data_len].iter()) {\n        unsafe { register.write(*byte) };\n    }\n}\n\nfn data_from_fifo(buffer: &mut [u8], mailbox: &RxFifoElement, len: usize) {\n    for (i, register) in mailbox.data.iter().enumerate() {\n        let register_value = register.read();\n        let register_bytes = unsafe { slice::from_raw_parts(&register_value as *const u32 as *const u8, 4) };\n        let num_bytes = (len) - i * 4;\n        if num_bytes <= 4 {\n            buffer[i * 4..i * 4 + num_bytes].copy_from_slice(&register_bytes[..num_bytes]);\n            break;\n        }\n        buffer[i * 4..(i + 1) * 4].copy_from_slice(register_bytes);\n    }\n}\n\nfn data_from_tx_buffer(buffer: &mut [u8], mailbox: &TxBufferElement, len: usize) {\n    for (i, register) in mailbox.data.iter().enumerate() {\n        let register_value = register.read();\n        let register_bytes = unsafe { slice::from_raw_parts(&register_value as *const u32 as *const u8, 4) };\n        let num_bytes = (len) - i * 4;\n        if num_bytes <= 4 {\n            buffer[i * 4..i * 4 + num_bytes].copy_from_slice(&register_bytes[..num_bytes]);\n            break;\n        }\n        buffer[i * 4..(i + 1) * 4].copy_from_slice(register_bytes);\n    }\n}\n\nfn extract_frame(mailbox: &RxFifoElement, buffer: &mut [u8]) -> Option<(Header, u16)> {\n    let header_reg = mailbox.header.read();\n\n    let id = make_id(header_reg.id().bits(), header_reg.xtd().bits());\n    let dlc = header_reg.to_data_length().len();\n    let len = dlc as usize;\n    let timestamp = header_reg.txts().bits;\n    if len > buffer.len() {\n        return None;\n    }\n    data_from_fifo(buffer, mailbox, len);\n    let header = if header_reg.fdf().bits {\n        Header::new_fd(id, dlc, header_reg.rtr().bits(), header_reg.brs().bits())\n    } else {\n        Header::new(id, dlc, header_reg.rtr().bits())\n    };\n    Some((header, timestamp))\n}\n"
  },
  {
    "path": "embassy-stm32/src/can/fdcan.rs",
    "content": "#[allow(unused_variables)]\nuse core::future::poll_fn;\nuse core::marker::PhantomData;\nuse core::task::Poll;\n\nuse embassy_hal_internal::PeripheralType;\nuse embassy_hal_internal::interrupt::InterruptExt;\nuse embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;\nuse embassy_sync::channel::Channel;\nuse embassy_sync::waitqueue::AtomicWaker;\n\nuse crate::can::fd::peripheral::Registers;\nuse crate::gpio::{AfType, OutputType, Pull, SealedPin as _, Speed};\nuse crate::interrupt::typelevel::Interrupt;\nuse crate::rcc::{self, RccInfo, RccPeripheral, SealedRccPeripheral};\nuse crate::{Peri, interrupt, peripherals};\n\npub(crate) mod fd;\n\nuse self::fd::config::*;\nuse self::fd::filter::*;\npub use self::fd::{config, filter};\npub use super::common::{BufferedCanReceiver, BufferedCanSender};\nuse super::common::{InfoRef, RxInfoRef, TxInfoRef};\nuse super::enums::*;\nuse super::frame::*;\nuse super::util;\n\n/// Timestamp for incoming packets. Use Embassy time when enabled.\n#[cfg(feature = \"time\")]\npub type Timestamp = embassy_time::Instant;\n\n/// Timestamp for incoming packets.\n#[cfg(not(feature = \"time\"))]\npub type Timestamp = u16;\n\n/// Interrupt handler channel 0.\npub struct IT0InterruptHandler<T: Instance> {\n    _phantom: PhantomData<T>,\n}\n\n// We use IT0 for everything currently\nimpl<T: Instance> interrupt::typelevel::Handler<T::IT0Interrupt> for IT0InterruptHandler<T> {\n    unsafe fn on_interrupt() {\n        let regs = T::registers().regs;\n\n        let ir = regs.ir().read();\n\n        if ir.tc() {\n            regs.ir().write(|w| w.set_tc(true));\n        }\n        if ir.tefn() {\n            regs.ir().write(|w| w.set_tefn(true));\n        }\n\n        let recover_from_bo = T::info().state.lock(|s| {\n            let state = s.borrow_mut();\n            match &state.tx_mode {\n                TxMode::NonBuffered(waker) => waker.wake(),\n                TxMode::ClassicBuffered(buf) => {\n                    if !T::registers().tx_queue_is_full() {\n                        match buf.tx_receiver.try_receive() {\n                            Ok(frame) => {\n                                _ = T::registers().write(&frame);\n                            }\n                            Err(_) => {}\n                        }\n                    }\n                }\n                TxMode::FdBuffered(buf) => {\n                    if !T::registers().tx_queue_is_full() {\n                        match buf.tx_receiver.try_receive() {\n                            Ok(frame) => {\n                                _ = T::registers().write(&frame);\n                            }\n                            Err(_) => {}\n                        }\n                    }\n                }\n            }\n\n            if ir.rfn(0) {\n                state.rx_mode.on_interrupt::<T>(0, state.ns_per_timer_tick);\n            }\n            if ir.rfn(1) {\n                state.rx_mode.on_interrupt::<T>(1, state.ns_per_timer_tick);\n            }\n\n            state.automatic_bus_off_recovery\n        });\n\n        if ir.bo() {\n            regs.ir().write(|w| w.set_bo(true));\n            if let Some(true) = recover_from_bo\n                && regs.psr().read().bo()\n            {\n                // Initiate bus-off recovery sequence by resetting CCCR.INIT\n                regs.cccr().modify(|w| w.set_init(false));\n            }\n        }\n    }\n}\n\n/// Interrupt handler channel 1.\npub struct IT1InterruptHandler<T: Instance> {\n    _phantom: PhantomData<T>,\n}\n\nimpl<T: Instance> interrupt::typelevel::Handler<T::IT1Interrupt> for IT1InterruptHandler<T> {\n    unsafe fn on_interrupt() {}\n}\n\n#[derive(Debug, Copy, Clone, Eq, PartialEq)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\n/// Different operating modes\npub enum OperatingMode {\n    //PoweredDownMode,\n    //ConfigMode,\n    /// This mode can be used for a “Hot Selftest”, meaning the FDCAN can be tested without\n    /// affecting a running CAN system connected to the FDCAN_TX and FDCAN_RX pins. In this\n    /// mode, FDCAN_RX pin is disconnected from the FDCAN and FDCAN_TX pin is held\n    /// recessive.\n    InternalLoopbackMode,\n    /// This mode is provided for hardware self-test. To be independent from external stimulation,\n    /// the FDCAN ignores acknowledge errors (recessive bit sampled in the acknowledge slot of a\n    /// data / remote frame) in Loop Back mode. In this mode the FDCAN performs an internal\n    /// feedback from its transmit output to its receive input. The actual value of the FDCAN_RX\n    /// input pin is disregarded by the FDCAN. The transmitted messages can be monitored at the\n    /// FDCAN_TX transmit pin.\n    ExternalLoopbackMode,\n    /// The normal use of the Fdcan instance after configurations\n    NormalOperationMode,\n    /// In Restricted operation mode the node is able to receive data and remote frames and to give\n    /// acknowledge to valid frames, but it does not send data frames, remote frames, active error\n    /// frames, or overload frames. In case of an error condition or overload condition, it does not\n    /// send dominant bits, instead it waits for the occurrence of bus idle condition to resynchronize\n    /// itself to the CAN communication. The error counters for transmit and receive are frozen while\n    /// error logging (can_errors) is active. TODO: automatically enter in this mode?\n    RestrictedOperationMode,\n    ///  In Bus monitoring mode (for more details refer to ISO11898-1, 10.12 Bus monitoring),\n    /// the FDCAN is able to receive valid data frames and valid remote frames, but cannot start a\n    /// transmission. In this mode, it sends only recessive bits on the CAN bus. If the FDCAN is\n    /// required to send a dominant bit (ACK bit, overload flag, active error flag), the bit is\n    /// rerouted internally so that the FDCAN can monitor it, even if the CAN bus remains in recessive\n    /// state. In Bus monitoring mode the TXBRP register is held in reset state. The Bus monitoring\n    /// mode can be used to analyze the traffic on a CAN bus without affecting it by the transmission\n    /// of dominant bits.\n    BusMonitoringMode,\n    //TestMode,\n}\n\nfn calc_ns_per_timer_tick(\n    info: &'static Info,\n    freq: crate::time::Hertz,\n    mode: crate::can::fd::config::FrameTransmissionConfig,\n) -> u64 {\n    match mode {\n        // Use timestamp from Rx FIFO to adjust timestamp reported to user\n        crate::can::fd::config::FrameTransmissionConfig::ClassicCanOnly => {\n            let prescale: u64 = ({ info.regs.regs.nbtp().read().nbrp() } + 1) as u64\n                * ({ info.regs.regs.tscc().read().tcp() } + 1) as u64;\n            1_000_000_000 as u64 / (freq.0 as u64 * prescale)\n        }\n        // For VBR this is too hard because the FDCAN timer switches clock rate you need to configure to use\n        // timer3 instead which is too hard to do from this module.\n        _ => 0,\n    }\n}\n\n/// FDCAN Configuration instance instance\n/// Create instance of this first\npub struct CanConfigurator<'d> {\n    _phantom: PhantomData<&'d ()>,\n    config: crate::can::fd::config::FdCanConfig,\n    /// Reference to internals.\n    properties: Properties,\n    periph_clock: crate::time::Hertz,\n    info: InfoRef,\n}\n\nimpl<'d> CanConfigurator<'d> {\n    /// Creates a new Fdcan instance, keeping the peripheral in sleep mode.\n    /// You must call [Fdcan::enable_non_blocking] to use the peripheral.\n    pub fn new<T: Instance>(\n        _peri: Peri<'d, T>,\n        rx: Peri<'d, impl RxPin<T>>,\n        tx: Peri<'d, impl TxPin<T>>,\n        _irqs: impl interrupt::typelevel::Binding<T::IT0Interrupt, IT0InterruptHandler<T>>\n        + interrupt::typelevel::Binding<T::IT1Interrupt, IT1InterruptHandler<T>>\n        + 'd,\n    ) -> CanConfigurator<'d> {\n        set_as_af!(rx, AfType::input(Pull::None));\n        set_as_af!(tx, AfType::output(OutputType::PushPull, Speed::VeryHigh));\n\n        rcc::enable_and_reset::<T>();\n\n        let info = T::info();\n        T::info().state.lock(|s| {\n            s.borrow_mut().tx_pin_port = Some(tx.pin_port());\n            s.borrow_mut().rx_pin_port = Some(rx.pin_port());\n        });\n\n        let mut config = crate::can::fd::config::FdCanConfig::default();\n        config.timestamp_source = TimestampSource::Prescaler(TimestampPrescaler::_1);\n        T::registers().into_config_mode(config);\n\n        unsafe {\n            T::IT0Interrupt::unpend(); // Not unsafe\n            T::IT0Interrupt::enable();\n\n            T::IT1Interrupt::unpend(); // Not unsafe\n            T::IT1Interrupt::enable();\n        }\n        Self {\n            _phantom: PhantomData,\n            config,\n            properties: Properties::new(T::info()),\n            periph_clock: T::frequency(),\n            info: InfoRef::new(info),\n        }\n    }\n\n    /// Get driver properties\n    pub fn properties(&self) -> &Properties {\n        &self.properties\n    }\n\n    /// Get configuration\n    pub fn config(&self) -> crate::can::fd::config::FdCanConfig {\n        return self.config;\n    }\n\n    /// Set configuration\n    pub fn set_config(&mut self, config: crate::can::fd::config::FdCanConfig) {\n        self.config = config;\n    }\n\n    /// Configures the bit timings calculated from supplied bitrate.\n    pub fn set_bitrate(&mut self, bitrate: u32) {\n        let bit_timing = util::calc_can_timings(self.periph_clock, bitrate).unwrap();\n\n        let nbtr = crate::can::fd::config::NominalBitTiming {\n            sync_jump_width: bit_timing.sync_jump_width,\n            prescaler: bit_timing.prescaler,\n            seg1: bit_timing.seg1,\n            seg2: bit_timing.seg2,\n        };\n        self.config = self.config.set_nominal_bit_timing(nbtr);\n    }\n\n    /// Configures the bit timings for VBR data calculated from supplied bitrate. This also sets confit to allow can FD and VBR\n    pub fn set_fd_data_bitrate(&mut self, bitrate: u32, transceiver_delay_compensation: bool) {\n        let bit_timing = util::calc_can_timings(self.periph_clock, bitrate).unwrap();\n        // Note, used existing calcluation for normal(non-VBR) bitrate, appears to work for 250k/1M\n        let nbtr = crate::can::fd::config::DataBitTiming {\n            transceiver_delay_compensation,\n            sync_jump_width: bit_timing.sync_jump_width,\n            prescaler: bit_timing.prescaler,\n            seg1: bit_timing.seg1,\n            seg2: bit_timing.seg2,\n        };\n        self.config.frame_transmit = FrameTransmissionConfig::AllowFdCanAndBRS;\n        self.config = self.config.set_data_bit_timing(nbtr);\n    }\n\n    /// Start in mode.\n    pub fn start(self, mode: OperatingMode) -> Can<'d> {\n        let ns_per_timer_tick = calc_ns_per_timer_tick(&self.info, self.periph_clock, self.config.frame_transmit);\n        self.info.state.lock(|s| {\n            let mut state = s.borrow_mut();\n            state.ns_per_timer_tick = ns_per_timer_tick;\n            state.automatic_bus_off_recovery = Some(self.config.automatic_bus_off_recovery);\n        });\n        self.info.regs.into_mode(self.config, mode);\n        Can {\n            _phantom: PhantomData,\n            config: self.config,\n            _mode: mode,\n            properties: Properties::new(&self.info),\n            info: InfoRef::new(&self.info),\n        }\n    }\n\n    /// Start, entering mode. Does same as start(mode)\n    pub fn into_normal_mode(self) -> Can<'d> {\n        self.start(OperatingMode::NormalOperationMode)\n    }\n\n    /// Start, entering mode. Does same as start(mode)\n    pub fn into_internal_loopback_mode(self) -> Can<'d> {\n        self.start(OperatingMode::InternalLoopbackMode)\n    }\n\n    /// Start, entering mode. Does same as start(mode)\n    pub fn into_external_loopback_mode(self) -> Can<'d> {\n        self.start(OperatingMode::ExternalLoopbackMode)\n    }\n}\n\n/// FDCAN Instance\npub struct Can<'d> {\n    _phantom: PhantomData<&'d ()>,\n    config: crate::can::fd::config::FdCanConfig,\n    _mode: OperatingMode,\n    properties: Properties,\n    info: InfoRef,\n}\n\nimpl<'d> Can<'d> {\n    /// Get driver properties\n    pub fn properties(&self) -> &Properties {\n        &self.properties\n    }\n\n    /// Flush one of the TX mailboxes.\n    pub async fn flush(&self, idx: usize) {\n        poll_fn(|cx| {\n            self.info.state.lock(|s| {\n                s.borrow_mut().tx_mode.register(cx.waker());\n            });\n\n            if idx > 3 {\n                panic!(\"Bad mailbox\");\n            }\n            let idx = 1 << idx;\n            if !self.info.regs.regs.txbrp().read().trp(idx) {\n                return Poll::Ready(());\n            }\n\n            Poll::Pending\n        })\n        .await;\n    }\n\n    /// Queues the message to be sent but exerts backpressure.  If a lower-priority\n    /// frame is dropped from the mailbox, it is returned.  If no lower-priority frames\n    /// can be replaced, this call asynchronously waits for a frame to be successfully\n    /// transmitted, then tries again.\n    pub async fn write(&mut self, frame: &Frame) -> Option<Frame> {\n        TxMode::write(&self.info, frame).await\n    }\n\n    /// Returns the next received message frame\n    pub async fn read(&mut self) -> Result<Envelope, BusError> {\n        RxMode::read_classic(&self.info).await\n    }\n\n    /// Queues the message to be sent but exerts backpressure.  If a lower-priority\n    /// frame is dropped from the mailbox, it is returned.  If no lower-priority frames\n    /// can be replaced, this call asynchronously waits for a frame to be successfully\n    /// transmitted, then tries again.\n    pub async fn write_fd(&mut self, frame: &FdFrame) -> Option<FdFrame> {\n        TxMode::write_fd(&self.info, frame).await\n    }\n\n    /// Returns the next received message frame\n    pub async fn read_fd(&mut self) -> Result<FdEnvelope, BusError> {\n        RxMode::read_fd(&self.info).await\n    }\n\n    /// Split instance into separate portions: Tx(write), Rx(read), common properties\n    pub fn split(self) -> (CanTx<'d>, CanRx<'d>, Properties) {\n        (\n            CanTx {\n                _phantom: PhantomData,\n                config: self.config,\n                _mode: self._mode,\n                info: TxInfoRef::new(&self.info),\n            },\n            CanRx {\n                _phantom: PhantomData,\n                _mode: self._mode,\n                info: RxInfoRef::new(&self.info),\n            },\n            Properties {\n                info: self.properties.info,\n            },\n        )\n    }\n    /// Join split rx and tx portions back together\n    pub fn join(tx: CanTx<'d>, rx: CanRx<'d>) -> Self {\n        Can {\n            _phantom: PhantomData,\n            config: tx.config,\n            _mode: rx._mode,\n            properties: Properties::new(&tx.info),\n            info: InfoRef::new(&tx.info),\n        }\n    }\n\n    /// Return a buffered instance of driver without CAN FD support. User must supply Buffers\n    pub fn buffered<const TX_BUF_SIZE: usize, const RX_BUF_SIZE: usize>(\n        self,\n        tx_buf: &'static mut TxBuf<TX_BUF_SIZE>,\n        rxb: &'static mut RxBuf<RX_BUF_SIZE>,\n    ) -> BufferedCan<'d, TX_BUF_SIZE, RX_BUF_SIZE> {\n        BufferedCan::new(&self.info, self._mode, tx_buf, rxb)\n    }\n\n    /// Return a buffered instance of driver with CAN FD support. User must supply Buffers\n    pub fn buffered_fd<const TX_BUF_SIZE: usize, const RX_BUF_SIZE: usize>(\n        self,\n        tx_buf: &'static mut TxFdBuf<TX_BUF_SIZE>,\n        rxb: &'static mut RxFdBuf<RX_BUF_SIZE>,\n    ) -> BufferedCanFd<'d, TX_BUF_SIZE, RX_BUF_SIZE> {\n        BufferedCanFd::new(&self.info, self._mode, tx_buf, rxb)\n    }\n}\n\n/// User supplied buffer for RX Buffering\npub type RxBuf<const BUF_SIZE: usize> = Channel<CriticalSectionRawMutex, Result<Envelope, BusError>, BUF_SIZE>;\n\n/// User supplied buffer for TX buffering\npub type TxBuf<const BUF_SIZE: usize> = Channel<CriticalSectionRawMutex, Frame, BUF_SIZE>;\n\n/// Buffered FDCAN Instance\npub struct BufferedCan<'d, const TX_BUF_SIZE: usize, const RX_BUF_SIZE: usize> {\n    _phantom: PhantomData<&'d ()>,\n    _mode: OperatingMode,\n    tx_buf: &'static TxBuf<TX_BUF_SIZE>,\n    rx_buf: &'static RxBuf<RX_BUF_SIZE>,\n    properties: Properties,\n    info: InfoRef,\n}\n\nimpl<'c, 'd, const TX_BUF_SIZE: usize, const RX_BUF_SIZE: usize> BufferedCan<'d, TX_BUF_SIZE, RX_BUF_SIZE> {\n    fn new(\n        info: &'static Info,\n        _mode: OperatingMode,\n        tx_buf: &'static TxBuf<TX_BUF_SIZE>,\n        rx_buf: &'static RxBuf<RX_BUF_SIZE>,\n    ) -> Self {\n        BufferedCan {\n            _phantom: PhantomData,\n            _mode,\n            tx_buf,\n            rx_buf,\n            properties: Properties::new(info),\n            info: InfoRef::new(info),\n        }\n        .setup()\n    }\n\n    /// Get driver properties\n    pub fn properties(&self) -> &Properties {\n        &self.properties\n    }\n\n    fn setup(self) -> Self {\n        // We don't want interrupts being processed while we change modes.\n        self.info.state.lock(|s| {\n            let rx_inner = super::common::ClassicBufferedRxInner {\n                rx_sender: self.rx_buf.sender().into(),\n            };\n            let tx_inner = super::common::ClassicBufferedTxInner {\n                tx_receiver: self.tx_buf.receiver().into(),\n            };\n            s.borrow_mut().rx_mode = RxMode::ClassicBuffered(rx_inner);\n            s.borrow_mut().tx_mode = TxMode::ClassicBuffered(tx_inner);\n        });\n        self\n    }\n\n    /// Async write frame to TX buffer.\n    pub async fn write(&mut self, frame: Frame) {\n        self.tx_buf.send(frame).await;\n        self.info.interrupt0.pend(); // Wake for Tx\n        //T::IT0Interrupt::pend(); // Wake for Tx\n    }\n\n    /// Async read frame from RX buffer.\n    pub async fn read(&mut self) -> Result<Envelope, BusError> {\n        self.rx_buf.receive().await\n    }\n\n    /// Returns a sender that can be used for sending CAN frames.\n    pub fn writer(&self) -> BufferedCanSender {\n        BufferedCanSender {\n            tx_buf: self.tx_buf.sender().into(),\n            info: TxInfoRef::new(&self.info),\n        }\n    }\n\n    /// Returns a receiver that can be used for receiving CAN frames. Note, each CAN frame will only be received by one receiver.\n    pub fn reader(&self) -> BufferedCanReceiver {\n        BufferedCanReceiver {\n            rx_buf: self.rx_buf.receiver().into(),\n            info: RxInfoRef::new(&self.info),\n        }\n    }\n}\n\n/// User supplied buffer for RX Buffering\npub type RxFdBuf<const BUF_SIZE: usize> = Channel<CriticalSectionRawMutex, Result<FdEnvelope, BusError>, BUF_SIZE>;\n\n/// User supplied buffer for TX buffering\npub type TxFdBuf<const BUF_SIZE: usize> = Channel<CriticalSectionRawMutex, FdFrame, BUF_SIZE>;\n\n/// Sender that can be used for sending Classic CAN frames.\npub type BufferedFdCanSender = super::common::BufferedSender<'static, FdFrame>;\n\n/// Receiver that can be used for receiving CAN frames. Note, each CAN frame will only be received by one receiver.\npub type BufferedFdCanReceiver = super::common::BufferedReceiver<'static, FdEnvelope>;\n\n/// Buffered FDCAN Instance\npub struct BufferedCanFd<'d, const TX_BUF_SIZE: usize, const RX_BUF_SIZE: usize> {\n    _phantom: PhantomData<&'d ()>,\n    _mode: OperatingMode,\n    tx_buf: &'static TxFdBuf<TX_BUF_SIZE>,\n    rx_buf: &'static RxFdBuf<RX_BUF_SIZE>,\n    properties: Properties,\n    info: InfoRef,\n}\n\nimpl<'c, 'd, const TX_BUF_SIZE: usize, const RX_BUF_SIZE: usize> BufferedCanFd<'d, TX_BUF_SIZE, RX_BUF_SIZE> {\n    fn new(\n        info: &'static Info,\n        _mode: OperatingMode,\n        tx_buf: &'static TxFdBuf<TX_BUF_SIZE>,\n        rx_buf: &'static RxFdBuf<RX_BUF_SIZE>,\n    ) -> Self {\n        BufferedCanFd {\n            _phantom: PhantomData,\n            _mode,\n            tx_buf,\n            rx_buf,\n            properties: Properties::new(info),\n            info: InfoRef::new(info),\n        }\n        .setup()\n    }\n\n    /// Get driver properties\n    pub fn properties(&self) -> &Properties {\n        &self.properties\n    }\n\n    fn setup(self) -> Self {\n        // We don't want interrupts being processed while we change modes.\n        self.info.state.lock(|s| {\n            let rx_inner = super::common::FdBufferedRxInner {\n                rx_sender: self.rx_buf.sender().into(),\n            };\n            let tx_inner = super::common::FdBufferedTxInner {\n                tx_receiver: self.tx_buf.receiver().into(),\n            };\n            s.borrow_mut().rx_mode = RxMode::FdBuffered(rx_inner);\n            s.borrow_mut().tx_mode = TxMode::FdBuffered(tx_inner);\n        });\n        self\n    }\n\n    /// Async write frame to TX buffer.\n    pub async fn write(&mut self, frame: FdFrame) {\n        self.tx_buf.send(frame).await;\n        self.info.interrupt0.pend(); // Wake for Tx\n        //T::IT0Interrupt::pend(); // Wake for Tx\n    }\n\n    /// Async read frame from RX buffer.\n    pub async fn read(&mut self) -> Result<FdEnvelope, BusError> {\n        self.rx_buf.receive().await\n    }\n\n    /// Returns a sender that can be used for sending CAN frames.\n    pub fn writer(&self) -> BufferedFdCanSender {\n        BufferedFdCanSender {\n            tx_buf: self.tx_buf.sender().into(),\n            info: TxInfoRef::new(&self.info),\n        }\n    }\n\n    /// Returns a receiver that can be used for receiving CAN frames. Note, each CAN frame will only be received by one receiver.\n    pub fn reader(&self) -> BufferedFdCanReceiver {\n        BufferedFdCanReceiver {\n            rx_buf: self.rx_buf.receiver().into(),\n            info: RxInfoRef::new(&self.info),\n        }\n    }\n}\n\n/// FDCAN Rx only Instance\npub struct CanRx<'d> {\n    _phantom: PhantomData<&'d ()>,\n    _mode: OperatingMode,\n    info: RxInfoRef,\n}\n\nimpl<'d> CanRx<'d> {\n    /// Returns the next received message frame\n    pub async fn read(&mut self) -> Result<Envelope, BusError> {\n        RxMode::read_classic(&self.info).await\n    }\n\n    /// Returns the next received message frame\n    pub async fn read_fd(&mut self) -> Result<FdEnvelope, BusError> {\n        RxMode::read_fd(&self.info).await\n    }\n}\n\n/// FDCAN Tx only Instance\npub struct CanTx<'d> {\n    _phantom: PhantomData<&'d ()>,\n    config: crate::can::fd::config::FdCanConfig,\n    _mode: OperatingMode,\n    info: TxInfoRef,\n}\n\nimpl<'c, 'd> CanTx<'d> {\n    /// Queues the message to be sent but exerts backpressure.  If a lower-priority\n    /// frame is dropped from the mailbox, it is returned.  If no lower-priority frames\n    /// can be replaced, this call asynchronously waits for a frame to be successfully\n    /// transmitted, then tries again.\n    pub async fn write(&mut self, frame: &Frame) -> Option<Frame> {\n        TxMode::write(&self.info, frame).await\n    }\n\n    /// Queues the message to be sent but exerts backpressure.  If a lower-priority\n    /// frame is dropped from the mailbox, it is returned.  If no lower-priority frames\n    /// can be replaced, this call asynchronously waits for a frame to be successfully\n    /// transmitted, then tries again.\n    pub async fn write_fd(&mut self, frame: &FdFrame) -> Option<FdFrame> {\n        TxMode::write_fd(&self.info, frame).await\n    }\n}\n\nenum RxMode {\n    NonBuffered(AtomicWaker),\n    ClassicBuffered(super::common::ClassicBufferedRxInner),\n    FdBuffered(super::common::FdBufferedRxInner),\n}\n\nimpl RxMode {\n    fn register(&self, arg: &core::task::Waker) {\n        match self {\n            RxMode::NonBuffered(waker) => waker.register(arg),\n            _ => {\n                panic!(\"Bad Mode\")\n            }\n        }\n    }\n\n    fn on_interrupt<T: Instance>(&self, fifonr: usize, ns_per_timer_tick: u64) {\n        match self {\n            RxMode::NonBuffered(waker) => {\n                T::registers().regs.ir().write(|w| w.set_rfn(fifonr, true));\n                waker.wake();\n            }\n            RxMode::ClassicBuffered(buf) => {\n                T::registers().regs.ir().write(|w| w.set_rfn(fifonr, true));\n                loop {\n                    match self.try_read::<T>(ns_per_timer_tick) {\n                        Some(Ok(envelope)) => {\n                            let _ = buf.rx_sender.try_send(Ok(envelope));\n                        }\n                        Some(Err(err)) => {\n                            // bus error states can persist; emit once and return to avoid\n                            // spinning forever in interrupt context when no frames are available\n                            let _ = buf.rx_sender.try_send(Err(err));\n                            break;\n                        }\n                        None => break,\n                    }\n                }\n            }\n            RxMode::FdBuffered(buf) => {\n                T::registers().regs.ir().write(|w| w.set_rfn(fifonr, true));\n                loop {\n                    match self.try_read_fd::<T>(ns_per_timer_tick) {\n                        Some(Ok(envelope)) => {\n                            let _ = buf.rx_sender.try_send(Ok(envelope));\n                        }\n                        Some(Err(err)) => {\n                            // bus error states can persist; emit once and return to avoid\n                            // spinning forever in interrupt context when no frames are available\n                            let _ = buf.rx_sender.try_send(Err(err));\n                            break;\n                        }\n                        None => break,\n                    }\n                }\n            }\n        }\n    }\n\n    //async fn read_classic<T: Instance>(&self) -> Result<Envelope, BusError> {\n    fn try_read<T: Instance>(&self, ns_per_timer_tick: u64) -> Option<Result<Envelope, BusError>> {\n        if let Some((frame, ts)) = T::registers().read(0) {\n            let ts = T::registers().calc_timestamp(ns_per_timer_tick, ts);\n            Some(Ok(Envelope { ts, frame }))\n        } else if let Some((frame, ts)) = T::registers().read(1) {\n            let ts = T::registers().calc_timestamp(ns_per_timer_tick, ts);\n            Some(Ok(Envelope { ts, frame }))\n        } else if let Some(err) = T::registers().curr_error() {\n            // TODO: this is probably wrong\n            Some(Err(err))\n        } else {\n            None\n        }\n    }\n\n    fn try_read_fd<T: Instance>(&self, ns_per_timer_tick: u64) -> Option<Result<FdEnvelope, BusError>> {\n        if let Some((frame, ts)) = T::registers().read(0) {\n            let ts = T::registers().calc_timestamp(ns_per_timer_tick, ts);\n            Some(Ok(FdEnvelope { ts, frame }))\n        } else if let Some((frame, ts)) = T::registers().read(1) {\n            let ts = T::registers().calc_timestamp(ns_per_timer_tick, ts);\n            Some(Ok(FdEnvelope { ts, frame }))\n        } else if let Some(err) = T::registers().curr_error() {\n            // TODO: this is probably wrong\n            Some(Err(err))\n        } else {\n            None\n        }\n    }\n\n    fn read<F: CanHeader>(info: &'static Info, ns_per_timer_tick: u64) -> Option<Result<(F, Timestamp), BusError>> {\n        if let Some((msg, ts)) = info.regs.read(0) {\n            let ts = info.regs.calc_timestamp(ns_per_timer_tick, ts);\n            Some(Ok((msg, ts)))\n        } else if let Some((msg, ts)) = info.regs.read(1) {\n            let ts = info.regs.calc_timestamp(ns_per_timer_tick, ts);\n            Some(Ok((msg, ts)))\n        } else if let Some(err) = info.regs.curr_error() {\n            // TODO: this is probably wrong\n            Some(Err(err))\n        } else {\n            None\n        }\n    }\n\n    async fn read_async<F: CanHeader>(info: &'static Info) -> Result<(F, Timestamp), BusError> {\n        poll_fn(move |cx| {\n            let ns_per_timer_tick = info.state.lock(|s| {\n                let state = s.borrow_mut();\n                state.err_waker.register(cx.waker());\n                state.rx_mode.register(cx.waker());\n                state.ns_per_timer_tick\n            });\n            match RxMode::read::<_>(info, ns_per_timer_tick) {\n                Some(result) => Poll::Ready(result),\n                None => Poll::Pending,\n            }\n        })\n        .await\n    }\n\n    async fn read_classic(info: &'static Info) -> Result<Envelope, BusError> {\n        match RxMode::read_async::<_>(info).await {\n            Ok((frame, ts)) => Ok(Envelope { ts, frame }),\n            Err(e) => Err(e),\n        }\n    }\n\n    async fn read_fd(info: &'static Info) -> Result<FdEnvelope, BusError> {\n        match RxMode::read_async::<_>(info).await {\n            Ok((frame, ts)) => Ok(FdEnvelope { ts, frame }),\n            Err(e) => Err(e),\n        }\n    }\n}\n\nenum TxMode {\n    NonBuffered(AtomicWaker),\n    ClassicBuffered(super::common::ClassicBufferedTxInner),\n    FdBuffered(super::common::FdBufferedTxInner),\n}\n\nimpl TxMode {\n    fn register(&self, arg: &core::task::Waker) {\n        match self {\n            TxMode::NonBuffered(waker) => {\n                waker.register(arg);\n            }\n            _ => {\n                panic!(\"Bad mode\");\n            }\n        }\n    }\n\n    /// Queues the message to be sent but exerts backpressure.  If a lower-priority\n    /// frame is dropped from the mailbox, it is returned.  If no lower-priority frames\n    /// can be replaced, this call asynchronously waits for a frame to be successfully\n    /// transmitted, then tries again.\n    async fn write_generic<F: embedded_can::Frame + CanHeader>(info: &'static Info, frame: &F) -> Option<F> {\n        poll_fn(|cx| {\n            info.state.lock(|s| {\n                s.borrow_mut().tx_mode.register(cx.waker());\n            });\n\n            if let Ok(dropped) = info.regs.write(frame) {\n                return Poll::Ready(dropped);\n            }\n\n            // Couldn't replace any lower priority frames.  Need to wait for some mailboxes\n            // to clear.\n            Poll::Pending\n        })\n        .await\n    }\n\n    /// Queues the message to be sent but exerts backpressure.  If a lower-priority\n    /// frame is dropped from the mailbox, it is returned.  If no lower-priority frames\n    /// can be replaced, this call asynchronously waits for a frame to be successfully\n    /// transmitted, then tries again.\n    async fn write(info: &'static Info, frame: &Frame) -> Option<Frame> {\n        TxMode::write_generic::<_>(info, frame).await\n    }\n\n    /// Queues the message to be sent but exerts backpressure.  If a lower-priority\n    /// frame is dropped from the mailbox, it is returned.  If no lower-priority frames\n    /// can be replaced, this call asynchronously waits for a frame to be successfully\n    /// transmitted, then tries again.\n    async fn write_fd(info: &'static Info, frame: &FdFrame) -> Option<FdFrame> {\n        TxMode::write_generic::<_>(info, frame).await\n    }\n}\n\n/// Common driver properties, including filters and error counters\npub struct Properties {\n    info: &'static Info,\n    // phantom pointer to ensure !Sync\n    //instance: PhantomData<*const T>,\n}\n\nimpl Properties {\n    fn new(info: &'static Info) -> Self {\n        Self {\n            info,\n            //instance: Default::default(),\n        }\n    }\n\n    /// Set a standard address CAN filter in the specified slot in FDCAN memory.\n    #[inline]\n    pub fn set_standard_filter(&self, slot: StandardFilterSlot, filter: StandardFilter) {\n        self.info.regs.msg_ram_mut().filters.flssa[slot as usize].activate(filter);\n    }\n\n    /// Set the full array of standard address CAN filters in FDCAN memory.\n    /// Overwrites all standard address filters in memory.\n    pub fn set_standard_filters(&self, filters: &[StandardFilter; STANDARD_FILTER_MAX as usize]) {\n        for (i, f) in filters.iter().enumerate() {\n            self.info.regs.msg_ram_mut().filters.flssa[i].activate(*f);\n        }\n    }\n\n    /// Set an extended address CAN filter in the specified slot in FDCAN memory.\n    #[inline]\n    pub fn set_extended_filter(&self, slot: ExtendedFilterSlot, filter: ExtendedFilter) {\n        self.info.regs.msg_ram_mut().filters.flesa[slot as usize].activate(filter);\n    }\n\n    /// Set the full array of extended address CAN filters in FDCAN memory.\n    /// Overwrites all extended address filters in memory.\n    pub fn set_extended_filters(&self, filters: &[ExtendedFilter; EXTENDED_FILTER_MAX as usize]) {\n        for (i, f) in filters.iter().enumerate() {\n            self.info.regs.msg_ram_mut().filters.flesa[i].activate(*f);\n        }\n    }\n\n    /// Get the CAN RX error counter\n    pub fn rx_error_count(&self) -> u8 {\n        self.info.regs.regs.ecr().read().rec()\n    }\n\n    /// Get the CAN TX error counter\n    pub fn tx_error_count(&self) -> u8 {\n        self.info.regs.regs.ecr().read().tec()\n    }\n\n    /// Get the current bus error mode\n    pub fn bus_error_mode(&self) -> BusErrorMode {\n        // This read will clear LEC and DLEC. This is not ideal, but protocol\n        // error reporting in this driver should have a big ol' FIXME on it\n        // anyway!\n        let psr = self.info.regs.regs.psr().read();\n        match (psr.bo(), psr.ep()) {\n            (false, false) => BusErrorMode::ErrorActive,\n            (false, true) => BusErrorMode::ErrorPassive,\n            (true, _) => BusErrorMode::BusOff,\n        }\n    }\n}\n\nstruct State {\n    pub rx_mode: RxMode,\n    pub tx_mode: TxMode,\n    pub ns_per_timer_tick: u64,\n    receiver_instance_count: usize,\n    sender_instance_count: usize,\n    tx_pin_port: Option<u8>,\n    rx_pin_port: Option<u8>,\n    automatic_bus_off_recovery: Option<bool>, // controlled by CanConfigurator::start()\n    pub err_waker: AtomicWaker,\n}\n\nimpl State {\n    const fn new() -> Self {\n        Self {\n            rx_mode: RxMode::NonBuffered(AtomicWaker::new()),\n            tx_mode: TxMode::NonBuffered(AtomicWaker::new()),\n            ns_per_timer_tick: 0,\n            err_waker: AtomicWaker::new(),\n            receiver_instance_count: 0,\n            sender_instance_count: 0,\n            tx_pin_port: None,\n            rx_pin_port: None,\n            automatic_bus_off_recovery: None,\n        }\n    }\n}\n\ntype SharedState = embassy_sync::blocking_mutex::Mutex<CriticalSectionRawMutex, core::cell::RefCell<State>>;\npub(crate) struct Info {\n    regs: Registers,\n    interrupt0: crate::interrupt::Interrupt,\n    _interrupt1: crate::interrupt::Interrupt,\n    pub(crate) tx_waker: fn(),\n    state: SharedState,\n    rcc_info: RccInfo,\n}\n\nimpl Info {\n    pub(crate) fn adjust_reference_counter(&self, val: RefCountOp) {\n        self.state.lock(|s| {\n            let mut mut_state = s.borrow_mut();\n            match val {\n                RefCountOp::NotifySenderCreated => {\n                    mut_state.sender_instance_count += 1;\n                }\n                RefCountOp::NotifySenderDestroyed => {\n                    mut_state.sender_instance_count -= 1;\n                    if 0 == mut_state.sender_instance_count {\n                        (*mut_state).tx_mode = TxMode::NonBuffered(embassy_sync::waitqueue::AtomicWaker::new());\n                    }\n                }\n                RefCountOp::NotifyReceiverCreated => {\n                    mut_state.receiver_instance_count += 1;\n                }\n                RefCountOp::NotifyReceiverDestroyed => {\n                    mut_state.receiver_instance_count -= 1;\n                    if 0 == mut_state.receiver_instance_count {\n                        (*mut_state).rx_mode = RxMode::NonBuffered(embassy_sync::waitqueue::AtomicWaker::new());\n                    }\n                }\n            }\n            if mut_state.sender_instance_count == 0 && mut_state.receiver_instance_count == 0 {\n                unsafe {\n                    let tx_pin = crate::gpio::AnyPin::steal(mut_state.tx_pin_port.unwrap());\n                    tx_pin.set_as_disconnected();\n                    let rx_pin = crate::gpio::AnyPin::steal(mut_state.rx_pin_port.unwrap());\n                    rx_pin.set_as_disconnected();\n                    self.interrupt0.disable();\n                    self.rcc_info.disable();\n                }\n            }\n        });\n    }\n}\n\ntrait SealedInstance {\n    const MSG_RAM_OFFSET: usize;\n\n    fn info() -> &'static Info;\n    fn registers() -> crate::can::fd::peripheral::Registers;\n}\n\n/// Instance trait\n#[allow(private_bounds)]\npub trait Instance: SealedInstance + PeripheralType + RccPeripheral + 'static {\n    /// Interrupt 0\n    type IT0Interrupt: crate::interrupt::typelevel::Interrupt;\n    /// Interrupt 1\n    type IT1Interrupt: crate::interrupt::typelevel::Interrupt;\n}\n\n/// Fdcan Instance struct\npub struct FdcanInstance<'a, T: Instance>(Peri<'a, T>);\n\nmacro_rules! impl_fdcan {\n    ($inst:ident,\n        //$irq0:ident, $irq1:ident,\n        $msg_ram_inst:ident, $msg_ram_offset:literal) => {\n        impl SealedInstance for peripherals::$inst {\n            const MSG_RAM_OFFSET: usize = $msg_ram_offset;\n\n            fn info() -> &'static Info {\n\n                static INFO: Info = Info {\n                    regs: Registers{regs: crate::pac::$inst, msgram: crate::pac::$msg_ram_inst, msg_ram_offset: $msg_ram_offset},\n                    interrupt0: crate::_generated::peripheral_interrupts::$inst::IT0::IRQ,\n                    _interrupt1: crate::_generated::peripheral_interrupts::$inst::IT1::IRQ,\n                    tx_waker: crate::_generated::peripheral_interrupts::$inst::IT0::pend,\n                    state: embassy_sync::blocking_mutex::Mutex::new(core::cell::RefCell::new(State::new())),\n                    rcc_info: crate::peripherals::$inst::RCC_INFO,\n                };\n                &INFO\n            }\n            fn registers() -> Registers {\n                Registers{regs: crate::pac::$inst, msgram: crate::pac::$msg_ram_inst, msg_ram_offset: Self::MSG_RAM_OFFSET}\n            }\n\n        }\n\n        #[allow(non_snake_case)]\n        pub(crate) mod $inst {\n\n            foreach_interrupt!(\n                ($inst,can,FDCAN,IT0,$irq:ident) => {\n                    pub type Interrupt0 = crate::interrupt::typelevel::$irq;\n                };\n                ($inst,can,FDCAN,IT1,$irq:ident) => {\n                    pub type Interrupt1 = crate::interrupt::typelevel::$irq;\n                };\n            );\n        }\n        impl Instance for peripherals::$inst {\n            type IT0Interrupt = $inst::Interrupt0;\n            type IT1Interrupt = $inst::Interrupt1;\n        }\n    };\n\n    ($inst:ident, $msg_ram_inst:ident) => {\n        impl_fdcan!($inst, $msg_ram_inst, 0);\n    };\n}\n\n#[cfg(not(can_fdcan_h7))]\nforeach_peripheral!(\n    (can, FDCAN) => { impl_fdcan!(FDCAN, FDCANRAM); };\n    (can, FDCAN1) => { impl_fdcan!(FDCAN1, FDCANRAM1); };\n    (can, FDCAN2) => { impl_fdcan!(FDCAN2, FDCANRAM2); };\n    (can, FDCAN3) => { impl_fdcan!(FDCAN3, FDCANRAM3); };\n);\n\n#[cfg(can_fdcan_h7)]\nforeach_peripheral!(\n    (can, FDCAN1) => { impl_fdcan!(FDCAN1, FDCANRAM, 0x0000); };\n    (can, FDCAN2) => { impl_fdcan!(FDCAN2, FDCANRAM, 0x0C00); };\n    (can, FDCAN3) => { impl_fdcan!(FDCAN3, FDCANRAM, 0x1800); };\n);\n\npin_trait!(RxPin, Instance);\npin_trait!(TxPin, Instance);\n"
  },
  {
    "path": "embassy-stm32/src/can/frame.rs",
    "content": "//! Definition for CAN Frames\nuse bit_field::BitField;\n\nuse crate::can::enums::FrameCreateError;\n\n/// Calculate proper timestamp when available.\n#[cfg(feature = \"time\")]\npub type Timestamp = embassy_time::Instant;\n\n/// Raw register timestamp\n#[cfg(not(feature = \"time\"))]\npub type Timestamp = u16;\n\n/// CAN Header, without meta data\n#[derive(Debug, Copy, Clone)]\npub struct Header {\n    id: embedded_can::Id,\n    len: u8,\n    flags: u8,\n}\n\n#[cfg(feature = \"defmt\")]\nimpl defmt::Format for Header {\n    fn format(&self, fmt: defmt::Formatter<'_>) {\n        match self.id() {\n            embedded_can::Id::Standard(id) => {\n                defmt::write!(fmt, \"Can Standard ID={:x} len={}\", id.as_raw(), self.len,)\n            }\n            embedded_can::Id::Extended(id) => {\n                defmt::write!(fmt, \"Can Extended ID={:x} len={}\", id.as_raw(), self.len,)\n            }\n        }\n    }\n}\n\nimpl Header {\n    const FLAG_RTR: usize = 0; // Remote\n    const FLAG_FDCAN: usize = 1; // FDCan vs Classic CAN\n    const FLAG_BRS: usize = 2; // Bit-rate switching, ignored for Classic CAN\n\n    /// Create new CAN Header\n    pub fn new(id: embedded_can::Id, len: u8, rtr: bool) -> Header {\n        let mut flags = 0u8;\n        flags.set_bit(Self::FLAG_RTR, rtr);\n        Header { id, len, flags }\n    }\n\n    /// Create new CAN FD Header\n    pub fn new_fd(id: embedded_can::Id, len: u8, rtr: bool, brs: bool) -> Header {\n        let mut flags = 0u8;\n        flags.set_bit(Self::FLAG_RTR, rtr);\n        flags.set_bit(Self::FLAG_FDCAN, true);\n        flags.set_bit(Self::FLAG_BRS, brs);\n        Header { id, len, flags }\n    }\n\n    /// Return ID\n    pub fn id(&self) -> &embedded_can::Id {\n        &self.id\n    }\n\n    /// Get mutable reference to ID\n    pub fn id_mut(&mut self) -> &mut embedded_can::Id {\n        &mut self.id\n    }\n\n    /// Return length as u8\n    pub fn len(&self) -> u8 {\n        self.len\n    }\n\n    /// Is remote frame\n    pub fn rtr(&self) -> bool {\n        self.flags.get_bit(Self::FLAG_RTR)\n    }\n\n    /// Request/is FDCAN frame\n    pub fn fdcan(&self) -> bool {\n        self.flags.get_bit(Self::FLAG_FDCAN)\n    }\n\n    /// Request/is Flexible Data Rate\n    pub fn bit_rate_switching(&self) -> bool {\n        self.flags.get_bit(Self::FLAG_BRS)\n    }\n\n    /// Get priority of frame\n    pub(crate) fn priority(&self) -> u32 {\n        match self.id() {\n            embedded_can::Id::Standard(id) => (id.as_raw() as u32) << 18,\n            embedded_can::Id::Extended(id) => id.as_raw(),\n        }\n    }\n}\n\n/// Trait for FDCAN frame types, providing ability to construct from a Header\n/// and to retrieve the Header from a frame\npub trait CanHeader: Sized {\n    /// Construct frame from header and payload\n    fn from_header(header: Header, data: &[u8]) -> Result<Self, FrameCreateError>;\n\n    /// Get this frame's header struct\n    fn header(&self) -> &Header;\n}\n\n/// Payload of a classic CAN data frame.\n///\n/// Contains 0 to 8 Bytes of data.\n#[derive(Debug, Copy, Clone)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub struct ClassicData {\n    pub(crate) bytes: [u8; 8],\n}\n\nimpl ClassicData {\n    /// Creates a data payload from a raw byte slice.\n    ///\n    /// Returns `FrameCreateError` if `data` is more than 8 bytes (which is the maximum).\n    pub fn new(data: &[u8]) -> Result<Self, FrameCreateError> {\n        if data.len() > 8 {\n            return Err(FrameCreateError::InvalidDataLength);\n        }\n\n        let mut bytes = [0; 8];\n        bytes[..data.len()].copy_from_slice(data);\n\n        Ok(Self { bytes })\n    }\n\n    /// Raw read access to data.\n    pub fn raw(&self) -> &[u8] {\n        &self.bytes\n    }\n\n    /// Raw mutable read access to data.\n    pub fn raw_mut(&mut self) -> &mut [u8] {\n        &mut self.bytes\n    }\n\n    /// Checks if the length can be encoded in FDCAN DLC field.\n    pub const fn is_valid_len(len: usize) -> bool {\n        match len {\n            0..=8 => true,\n            _ => false,\n        }\n    }\n\n    /// Creates an empty data payload containing 0 bytes.\n    #[inline]\n    pub const fn empty() -> Self {\n        Self { bytes: [0; 8] }\n    }\n}\n\n/// Frame with up to 8 bytes of data payload as per Classic(non-FD) CAN\n/// For CAN-FD support use FdFrame\n#[derive(Debug, Copy, Clone)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub struct Frame {\n    can_header: Header,\n    data: ClassicData,\n}\n\nimpl Frame {\n    /// Create a new CAN classic Frame\n    pub fn new(can_header: Header, raw_data: &[u8]) -> Result<Self, FrameCreateError> {\n        let data = ClassicData::new(raw_data)?;\n        Ok(Frame { can_header, data: data })\n    }\n\n    /// Creates a new data frame.\n    pub fn new_data(id: impl Into<embedded_can::Id>, data: &[u8]) -> Result<Self, FrameCreateError> {\n        let eid: embedded_can::Id = id.into();\n        let header = Header::new(eid, data.len() as u8, false);\n        Self::new(header, data)\n    }\n\n    /// Create new extended frame\n    pub fn new_extended(raw_id: u32, raw_data: &[u8]) -> Result<Self, FrameCreateError> {\n        if let Some(id) = embedded_can::ExtendedId::new(raw_id) {\n            Self::new(Header::new(id.into(), raw_data.len() as u8, false), raw_data)\n        } else {\n            Err(FrameCreateError::InvalidCanId)\n        }\n    }\n\n    /// Create new standard frame\n    pub fn new_standard(raw_id: u16, raw_data: &[u8]) -> Result<Self, FrameCreateError> {\n        if let Some(id) = embedded_can::StandardId::new(raw_id) {\n            Self::new(Header::new(id.into(), raw_data.len() as u8, false), raw_data)\n        } else {\n            Err(FrameCreateError::InvalidCanId)\n        }\n    }\n\n    /// Create new remote frame\n    pub fn new_remote(id: impl Into<embedded_can::Id>, len: usize) -> Result<Self, FrameCreateError> {\n        if len <= 8usize {\n            Self::new(Header::new(id.into(), len as u8, true), &[0; 8])\n        } else {\n            Err(FrameCreateError::InvalidDataLength)\n        }\n    }\n\n    /// Get reference to data\n    pub fn header(&self) -> &Header {\n        &self.can_header\n    }\n\n    /// Return ID\n    pub fn id(&self) -> &embedded_can::Id {\n        &self.can_header.id\n    }\n\n    /// Get mutable reference to ID\n    pub fn id_mut(&mut self) -> &mut embedded_can::Id {\n        &mut self.can_header.id\n    }\n\n    /// Get reference to data\n    pub fn data(&self) -> &[u8] {\n        &self.data.raw()[..self.can_header.len as usize]\n    }\n\n    /// Get reference to underlying 8-byte raw data buffer, some bytes on the tail might be undefined.\n    pub fn raw_data(&self) -> &[u8] {\n        self.data.raw()\n    }\n\n    /// Get mutable reference to data\n    pub fn data_mut(&mut self) -> &mut [u8] {\n        &mut self.data.raw_mut()[..self.can_header.len as usize]\n    }\n\n    /// Get priority of frame\n    pub fn priority(&self) -> u32 {\n        self.header().priority()\n    }\n}\n\nimpl embedded_can::Frame for Frame {\n    fn new(id: impl Into<embedded_can::Id>, raw_data: &[u8]) -> Option<Self> {\n        let frameopt = Frame::new(Header::new(id.into(), raw_data.len() as u8, false), raw_data);\n        match frameopt {\n            Ok(frame) => Some(frame),\n            Err(_) => None,\n        }\n    }\n    fn new_remote(id: impl Into<embedded_can::Id>, len: usize) -> Option<Self> {\n        if len <= 8 {\n            let frameopt = Frame::new(Header::new(id.into(), len as u8, true), &[0; 8]);\n            match frameopt {\n                Ok(frame) => Some(frame),\n                Err(_) => None,\n            }\n        } else {\n            None\n        }\n    }\n    fn is_extended(&self) -> bool {\n        match self.can_header.id {\n            embedded_can::Id::Extended(_) => true,\n            embedded_can::Id::Standard(_) => false,\n        }\n    }\n    fn is_remote_frame(&self) -> bool {\n        self.can_header.rtr()\n    }\n    fn id(&self) -> embedded_can::Id {\n        self.can_header.id\n    }\n    fn dlc(&self) -> usize {\n        self.can_header.len as usize\n    }\n    fn data(&self) -> &[u8] {\n        &self.data()\n    }\n}\n\nimpl CanHeader for Frame {\n    fn from_header(header: Header, data: &[u8]) -> Result<Self, FrameCreateError> {\n        Self::new(header, data)\n    }\n\n    fn header(&self) -> &Header {\n        self.header()\n    }\n}\n\n/// Contains CAN frame and additional metadata.\n///\n/// Timestamp is available if `time` feature is enabled.\n/// For CAN-FD support use FdEnvelope\n#[derive(Debug, Clone)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub struct Envelope {\n    /// Reception time.\n    pub ts: Timestamp,\n    /// The actual CAN frame.\n    pub frame: Frame,\n}\n\nimpl Envelope {\n    /// Convert into a tuple\n    pub fn parts(self) -> (Frame, Timestamp) {\n        (self.frame, self.ts)\n    }\n}\n\n/// Payload of a (FD)CAN data frame.\n///\n/// Contains 0 to 64 Bytes of data.\n#[derive(Debug, Copy, Clone)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub struct FdData {\n    pub(crate) bytes: [u8; 64],\n}\n\nimpl FdData {\n    /// Creates a data payload from a raw byte slice.\n    ///\n    /// Returns `None` if `data` is more than 64 bytes (which is the maximum) or\n    /// cannot be represented with an FDCAN DLC.\n    pub fn new(data: &[u8]) -> Result<Self, FrameCreateError> {\n        if !FdData::is_valid_len(data.len()) {\n            return Err(FrameCreateError::InvalidDataLength);\n        }\n\n        let mut bytes = [0; 64];\n        bytes[..data.len()].copy_from_slice(data);\n\n        Ok(Self { bytes })\n    }\n\n    /// Raw read access to data.\n    pub fn raw(&self) -> &[u8] {\n        &self.bytes\n    }\n\n    /// Raw mutable read access to data.\n    pub fn raw_mut(&mut self) -> &mut [u8] {\n        &mut self.bytes\n    }\n\n    /// Checks if the length can be encoded in FDCAN DLC field.\n    pub const fn is_valid_len(len: usize) -> bool {\n        match len {\n            0..=8 => true,\n            12 => true,\n            16 => true,\n            20 => true,\n            24 => true,\n            32 => true,\n            48 => true,\n            64 => true,\n            _ => false,\n        }\n    }\n\n    /// Creates an empty data payload containing 0 bytes.\n    #[inline]\n    pub const fn empty() -> Self {\n        Self { bytes: [0; 64] }\n    }\n}\n\n/// Frame with up to 8 bytes of data payload as per Fd CAN\n#[derive(Debug, Copy, Clone)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub struct FdFrame {\n    can_header: Header,\n    data: FdData,\n}\n\nimpl FdFrame {\n    /// Create a new CAN classic Frame\n    pub fn new(can_header: Header, raw_data: &[u8]) -> Result<Self, FrameCreateError> {\n        let data = FdData::new(raw_data)?;\n        Ok(FdFrame { can_header, data })\n    }\n\n    /// Create new extended frame\n    pub fn new_extended(raw_id: u32, raw_data: &[u8]) -> Result<Self, FrameCreateError> {\n        if let Some(id) = embedded_can::ExtendedId::new(raw_id) {\n            Self::new(Header::new(id.into(), raw_data.len() as u8, false), raw_data)\n        } else {\n            Err(FrameCreateError::InvalidCanId)\n        }\n    }\n\n    /// Create new standard frame\n    pub fn new_standard(raw_id: u16, raw_data: &[u8]) -> Result<Self, FrameCreateError> {\n        if let Some(id) = embedded_can::StandardId::new(raw_id) {\n            Self::new(Header::new(id.into(), raw_data.len() as u8, false), raw_data)\n        } else {\n            Err(FrameCreateError::InvalidCanId)\n        }\n    }\n\n    /// Create new remote frame\n    pub fn new_remote(id: impl Into<embedded_can::Id>, len: usize) -> Result<Self, FrameCreateError> {\n        if len <= 8 {\n            Self::new(Header::new(id.into(), len as u8, true), &[0; 8])\n        } else {\n            Err(FrameCreateError::InvalidDataLength)\n        }\n    }\n\n    /// Get reference to data\n    pub fn header(&self) -> &Header {\n        &self.can_header\n    }\n\n    /// Return ID\n    pub fn id(&self) -> &embedded_can::Id {\n        &self.can_header.id\n    }\n\n    /// Get reference to data\n    pub fn data(&self) -> &[u8] {\n        &self.data.raw()[..self.can_header.len as usize]\n    }\n\n    /// Get mutable reference to data\n    pub fn data_mut(&mut self) -> &mut [u8] {\n        &mut self.data.raw_mut()[..self.can_header.len as usize]\n    }\n}\n\nimpl embedded_can::Frame for FdFrame {\n    fn new(id: impl Into<embedded_can::Id>, raw_data: &[u8]) -> Option<Self> {\n        match FdFrame::new(Header::new_fd(id.into(), raw_data.len() as u8, false, true), raw_data) {\n            Ok(frame) => Some(frame),\n            Err(_) => None,\n        }\n    }\n    fn new_remote(id: impl Into<embedded_can::Id>, len: usize) -> Option<Self> {\n        if len <= 8 {\n            match FdFrame::new(Header::new_fd(id.into(), len as u8, true, true), &[0; 64]) {\n                Ok(frame) => Some(frame),\n                Err(_) => None,\n            }\n        } else {\n            None\n        }\n    }\n    fn is_extended(&self) -> bool {\n        match self.can_header.id {\n            embedded_can::Id::Extended(_) => true,\n            embedded_can::Id::Standard(_) => false,\n        }\n    }\n    fn is_remote_frame(&self) -> bool {\n        self.can_header.rtr()\n    }\n    fn id(&self) -> embedded_can::Id {\n        self.can_header.id\n    }\n    // Returns length in bytes even for CANFD packets which embedded-can does not really mention.\n    fn dlc(&self) -> usize {\n        self.can_header.len as usize\n    }\n    fn data(&self) -> &[u8] {\n        &self.data()\n    }\n}\n\nimpl CanHeader for FdFrame {\n    fn from_header(header: Header, data: &[u8]) -> Result<Self, FrameCreateError> {\n        Self::new(header, data)\n    }\n\n    fn header(&self) -> &Header {\n        self.header()\n    }\n}\n\n/// Contains CAN FD frame and additional metadata.\n///\n/// Timestamp is available if `time` feature is enabled.\n#[derive(Debug, Clone)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub struct FdEnvelope {\n    /// Reception time.\n    pub ts: Timestamp,\n\n    /// The actual CAN frame.\n    pub frame: FdFrame,\n}\n\nimpl FdEnvelope {\n    /// Convert into a tuple\n    pub fn parts(self) -> (FdFrame, Timestamp) {\n        (self.frame, self.ts)\n    }\n}\n"
  },
  {
    "path": "embassy-stm32/src/can/mod.rs",
    "content": "//! Controller Area Network (CAN)\n#![macro_use]\n\n#[cfg_attr(can_bxcan, path = \"bxcan/mod.rs\")]\n#[cfg_attr(any(can_fdcan_v1, can_fdcan_h7), path = \"fdcan.rs\")]\nmod _version;\npub use _version::*;\n\nmod common;\npub mod enums;\npub mod frame;\npub mod util;\n\npub use frame::Frame;\n"
  },
  {
    "path": "embassy-stm32/src/can/util.rs",
    "content": "//! Utility functions shared between CAN controller types.\n\nuse core::num::{NonZeroU8, NonZeroU16};\n\nuse crate::can::enums::TimingCalcError;\n\n/// Shared struct to represent bit timings used by calc_can_timings.\n#[derive(Clone, Copy, Debug)]\npub struct NominalBitTiming {\n    /// Value by which the oscillator frequency is divided for generating the bit time quanta. The bit\n    /// time is built up from a multiple of this quanta. Valid values are 1 to 512.\n    pub prescaler: NonZeroU16,\n    /// Valid values are 1 to 128.\n    pub seg1: NonZeroU8,\n    /// Valid values are 1 to 255.\n    pub seg2: NonZeroU8,\n    /// Valid values are 1 to 128.\n    pub sync_jump_width: NonZeroU8,\n}\n\n/// Calculate nominal CAN bit timing based on CAN bitrate and periphial clock frequency\npub fn calc_can_timings(\n    periph_clock: crate::time::Hertz,\n    can_bitrate: u32,\n) -> Result<NominalBitTiming, TimingCalcError> {\n    const BS1_MAX: u8 = 16;\n    const BS2_MAX: u8 = 8;\n    const MAX_SAMPLE_POINT_PERMILL: u16 = 900;\n\n    let periph_clock = periph_clock.0;\n\n    if can_bitrate < 1000 {\n        return Err(TimingCalcError::BitrateTooLow { bitrate: can_bitrate });\n    }\n\n    // Ref. \"Automatic Baudrate Detection in CANopen Networks\", U. Koppe, MicroControl GmbH & Co. KG\n    //      CAN in Automation, 2003\n    //\n    // According to the source, optimal quanta per bit are:\n    //   Bitrate        Optimal Maximum\n    //   1000 kbps      8       10\n    //   500  kbps      16      17\n    //   250  kbps      16      17\n    //   125  kbps      16      17\n    let max_quanta_per_bit: u8 = if can_bitrate >= 1_000_000 { 10 } else { 17 };\n\n    // Computing (prescaler * BS):\n    //   BITRATE = 1 / (PRESCALER * (1 / PCLK) * (1 + BS1 + BS2))       -- See the Reference Manual\n    //   BITRATE = PCLK / (PRESCALER * (1 + BS1 + BS2))                 -- Simplified\n    // let:\n    //   BS = 1 + BS1 + BS2                                             -- Number of time quanta per bit\n    //   PRESCALER_BS = PRESCALER * BS\n    // ==>\n    //   PRESCALER_BS = PCLK / BITRATE\n    let prescaler_bs = periph_clock / can_bitrate;\n\n    // Searching for such prescaler value so that the number of quanta per bit is highest.\n    let mut bs1_bs2_sum = max_quanta_per_bit - 1;\n    while (prescaler_bs % (1 + bs1_bs2_sum) as u32) != 0 {\n        if bs1_bs2_sum <= 2 {\n            return Err(TimingCalcError::NoSolution { bs1_bs2_sum }); // No solution\n        }\n        bs1_bs2_sum -= 1;\n    }\n\n    let prescaler = prescaler_bs / (1 + bs1_bs2_sum) as u32;\n    if (prescaler < 1) || (prescaler > 1024) {\n        return Err(TimingCalcError::InvalidPrescaler { prescaler }); // No solution\n    }\n\n    // Now we have a constraint: (BS1 + BS2) == bs1_bs2_sum.\n    // We need to find such values so that the sample point is as close as possible to the optimal value,\n    // which is 87.5%, which is 7/8.\n    //\n    //   Solve[(1 + bs1)/(1 + bs1 + bs2) == 7/8, bs2]  (* Where 7/8 is 0.875, the recommended sample point location *)\n    //   {{bs2 -> (1 + bs1)/7}}\n    //\n    // Hence:\n    //   bs2 = (1 + bs1) / 7\n    //   bs1 = (7 * bs1_bs2_sum - 1) / 8\n    //\n    // Sample point location can be computed as follows:\n    //   Sample point location = (1 + bs1) / (1 + bs1 + bs2)\n    //\n    // Since the optimal solution is so close to the maximum, we prepare two solutions, and then pick the best one:\n    //   - With rounding to nearest\n    //   - With rounding to zero\n    let mut bs1 = ((7 * bs1_bs2_sum - 1) + 4) / 8; // Trying rounding to nearest first\n    let mut bs2 = bs1_bs2_sum - bs1;\n    core::assert!(bs1_bs2_sum > bs1);\n\n    let sample_point_permill = 1000 * ((1 + bs1) / (1 + bs1 + bs2)) as u16;\n    if sample_point_permill > MAX_SAMPLE_POINT_PERMILL {\n        // Nope, too far; now rounding to zero\n        bs1 = (7 * bs1_bs2_sum - 1) / 8;\n        bs2 = bs1_bs2_sum - bs1;\n    }\n\n    // Check is BS1 and BS2 are in range\n    if (bs1 < 1) || (bs1 > BS1_MAX) || (bs2 < 1) || (bs2 > BS2_MAX) {\n        return Err(TimingCalcError::BSNotInRange { bs1, bs2 });\n    }\n\n    let calculated = periph_clock / (prescaler * (1 + bs1 + bs2) as u32);\n    // Check if final bitrate matches the requested\n    if can_bitrate != calculated {\n        return Err(TimingCalcError::NoMatch {\n            requested: can_bitrate,\n            final_calculated: calculated,\n        });\n    }\n\n    // One is recommended by DS-015, CANOpen, and DeviceNet\n    let sync_jump_width = core::num::NonZeroU8::new(1).ok_or(TimingCalcError::CoreNumNew)?;\n\n    let seg1 = core::num::NonZeroU8::new(bs1).ok_or(TimingCalcError::CoreNumNew)?;\n    let seg2 = core::num::NonZeroU8::new(bs2).ok_or(TimingCalcError::CoreNumNew)?;\n    let nz_prescaler = core::num::NonZeroU16::new(prescaler as u16).ok_or(TimingCalcError::CoreNumNew)?;\n\n    Ok(NominalBitTiming {\n        sync_jump_width,\n        prescaler: nz_prescaler,\n        seg1,\n        seg2,\n    })\n}\n"
  },
  {
    "path": "embassy-stm32/src/comp.rs",
    "content": "//! Analog Comparator (COMP)\n//!\n//! This driver supports chips with the comp_u5 peripheral version\n//! (STM32WBA and STM32U5 series) and comp_v2 (STM32G4 series).\n#![macro_use]\n\nuse core::future::poll_fn;\nuse core::marker::PhantomData;\nuse core::task::Poll;\n\nuse embassy_hal_internal::PeripheralType;\nuse embassy_sync::waitqueue::AtomicWaker;\nuse stm32_metapac::comp::vals;\n\nuse crate::interrupt::typelevel::{Binding, Interrupt};\nuse crate::rcc::RccInfo;\nuse crate::{Peri, interrupt};\n\n/// Power mode for the comparator.\n#[derive(Clone, Copy, Debug, PartialEq, Eq)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub enum PowerMode {\n    /// High speed / full power.\n    HighSpeed,\n    /// Medium speed / medium power.\n    MediumSpeed,\n    /// Ultra-low power / very low speed.\n    UltraLowPower,\n}\n\n/// Hysteresis level.\n#[derive(Clone, Copy, Debug, PartialEq, Eq)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\n#[cfg(comp_u5)]\npub enum Hysteresis {\n    /// No hysteresis.\n    None,\n    /// Low hysteresis.\n    Low,\n    /// Medium hysteresis.\n    Medium,\n    /// High hysteresis.\n    High,\n}\n\n/// Hysteresis level.\n#[derive(Clone, Copy, Debug, PartialEq, Eq)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\n#[cfg(comp_v2)]\npub enum Hysteresis {\n    /// No hysteresis.\n    None,\n    /// 10mV hysteresis.\n    Hyst10M,\n    /// 20mV hysteresis.\n    Hyst20M,\n    /// 30mV hysteresis.\n    Hyst30M,\n    /// 40mV hysteresis.\n    Hyst40M,\n    /// 50mV hysteresis.\n    Hyst50M,\n    /// 60mV hysteresis.\n    Hyst60M,\n    /// 70mV hysteresis.\n    Hyst70M,\n}\n\n/// Output polarity.\n#[derive(Clone, Copy, Debug, PartialEq, Eq)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub enum OutputPolarity {\n    /// Output is not inverted.\n    NotInverted,\n    /// Output is inverted.\n    Inverted,\n}\n\n/// Inverting input selection.\n#[derive(Clone, Copy, Debug, PartialEq, Eq)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub enum InvertingInput {\n    /// 1/4 of VrefInt.\n    OneQuarterVref,\n    /// 1/2 of VrefInt.\n    HalfVref,\n    /// 3/4 of VrefInt.\n    ThreeQuarterVref,\n    /// VrefInt.\n    Vref,\n    /// DAC channel 1 output.\n    Dac1,\n    /// DAC channel 2 output.\n    Dac2,\n    /// External IO pin (INM1).\n    InputPin,\n    /// External IO pin (INM2).\n    #[cfg(comp_v2)]\n    InputPin2,\n}\n\n/// Blanking source selection.\n///\n/// Blanking allows masking the comparator output during specific timer events\n/// to avoid false triggering during switching noise.\n#[derive(Clone, Copy, Debug, PartialEq, Eq, Default)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub enum BlankingSource {\n    /// No blanking.\n    #[default]\n    None,\n    /// Timer blanking source 1 (check datasheet for specific timer mapping).\n    Blank1,\n    /// Timer blanking source 2 (check datasheet for specific timer mapping).\n    Blank2,\n    /// Timer blanking source 3 (check datasheet for specific timer mapping).\n    Blank3,\n}\n\n/// Window mode configuration.\n///\n/// Window mode allows two comparators to work together to detect if a signal\n/// is within a voltage window defined by the two comparators' thresholds.\n#[derive(Clone, Copy, Debug, PartialEq, Eq, Default)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub enum WindowMode {\n    /// Window mode disabled. Each comparator works independently.\n    #[default]\n    Disabled,\n    /// Window mode enabled. This comparator uses the non-inverting input\n    /// from the other comparator in the pair (COMP1/COMP2).\n    Enabled,\n}\n\n/// Window output mode for window comparisons.\n#[derive(Clone, Copy, Debug, PartialEq, Eq, Default)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub enum WindowOutput {\n    /// Output is the comparator's own value.\n    #[default]\n    OwnValue,\n    /// Output is XOR of both comparators in the pair (for window detection).\n    XorValue,\n}\n\n/// Configuration for the comparator.\n#[derive(Clone, Copy, Debug)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub struct Config {\n    /// Power mode.\n    pub power_mode: PowerMode,\n    /// Hysteresis level.\n    pub hysteresis: Hysteresis,\n    /// Output polarity.\n    pub output_polarity: OutputPolarity,\n    /// Inverting input selection.\n    pub inverting_input: InvertingInput,\n    /// Blanking source selection.\n    pub blanking_source: BlankingSource,\n    /// Window mode configuration.\n    pub window_mode: WindowMode,\n    /// Window output mode.\n    pub window_output: WindowOutput,\n}\n\nimpl Default for Config {\n    fn default() -> Self {\n        Self {\n            power_mode: PowerMode::HighSpeed,\n            hysteresis: Hysteresis::None,\n            output_polarity: OutputPolarity::NotInverted,\n            inverting_input: InvertingInput::HalfVref,\n            blanking_source: BlankingSource::None,\n            window_mode: WindowMode::Disabled,\n            window_output: WindowOutput::OwnValue,\n        }\n    }\n}\n\n/// Comparator state for async operations.\npub struct State {\n    waker: AtomicWaker,\n}\n\nimpl State {\n    /// Create a new state.\n    pub const fn new() -> Self {\n        Self {\n            waker: AtomicWaker::new(),\n        }\n    }\n}\n\n/// Interrupt handler for COMP.\npub struct InterruptHandler<T: Instance> {\n    _phantom: PhantomData<T>,\n}\n\nimpl<T: Instance> interrupt::typelevel::Handler<T::Interrupt> for InterruptHandler<T> {\n    unsafe fn on_interrupt() {\n        // The COMP interrupt is triggered on output transition.\n        // We disable the EXTI interrupt and wake the waker.\n        // The async code will re-enable the interrupt when needed.\n        T::disable_exti_interrupt();\n        T::state().waker.wake();\n    }\n}\n\n/// Comparator driver.\npub struct Comp<'d, T: Instance> {\n    _peri: Peri<'d, T>,\n}\n\nimpl<'d, T: Instance> Comp<'d, T> {\n    /// Create a new comparator driver.\n    ///\n    /// The comparator is configured but not enabled. Use [`enable`](Self::enable) to enable it.\n    ///\n    /// The non-inverting input is connected to the provided pin. The inverting input\n    /// is configured via the `config.inverting_input` parameter.\n    pub fn new(\n        peri: Peri<'d, T>,\n        inp: Peri<'_, impl InputPlusPin<T> + crate::gpio::Pin>,\n        _irq: impl Binding<T::Interrupt, InterruptHandler<T>>,\n        config: Config,\n    ) -> Self {\n        T::info().rcc.enable_and_reset();\n        inp.set_as_analog();\n\n        Self::configure(inp.channel(), config);\n\n        T::Interrupt::unpend();\n        unsafe { T::Interrupt::enable() };\n\n        Self { _peri: peri }\n    }\n\n    /// Create a new comparator driver with an external inverting input pin.\n    ///\n    /// The comparator is configured but not enabled. Use [`enable`](Self::enable) to enable it.\n    ///\n    /// Both non-inverting and inverting inputs are connected to the provided pins.\n    /// The `config.inverting_input` parameter is ignored; the pin determines the input.\n    pub fn new_with_input_minus_pin(\n        peri: Peri<'d, T>,\n        inp: Peri<'_, impl InputPlusPin<T> + crate::gpio::Pin>,\n        inm: Peri<'_, impl InputMinusPin<T> + crate::gpio::Pin>,\n        _irq: impl Binding<T::Interrupt, InterruptHandler<T>>,\n        config: Config,\n    ) -> Self {\n        T::info().rcc.enable_and_reset();\n        inp.set_as_analog();\n        inm.set_as_analog();\n\n        // Configure with the pin's channel\n        Self::configure_with_input_minus_pin(inp.channel(), inm.channel(), config);\n\n        T::Interrupt::unpend();\n        unsafe { T::Interrupt::enable() };\n\n        Self { _peri: peri }\n    }\n\n    fn configure_raw(inp_channel: u8, inmsel: vals::Inm, config: Config) {\n        #[cfg(comp_u5)]\n        let pwrmode = match config.power_mode {\n            PowerMode::HighSpeed => vals::PowerMode::HIGH_SPEED,\n            PowerMode::MediumSpeed => vals::PowerMode::MEDIUM_SPEED,\n            PowerMode::UltraLowPower => vals::PowerMode::ULTRA_LOW,\n        };\n\n        #[cfg(comp_v2)]\n        let hyst = match config.hysteresis {\n            Hysteresis::None => vals::Hysteresis::NONE,\n            Hysteresis::Hyst10M => vals::Hysteresis::HYST10M,\n            Hysteresis::Hyst20M => vals::Hysteresis::HYST20M,\n            Hysteresis::Hyst30M => vals::Hysteresis::HYST30M,\n            Hysteresis::Hyst40M => vals::Hysteresis::HYST40M,\n            Hysteresis::Hyst50M => vals::Hysteresis::HYST50M,\n            Hysteresis::Hyst60M => vals::Hysteresis::HYST60M,\n            Hysteresis::Hyst70M => vals::Hysteresis::HYST70M,\n        };\n\n        #[cfg(comp_u5)]\n        let hyst = match config.hysteresis {\n            Hysteresis::None => vals::Hysteresis::NONE,\n            Hysteresis::Low => vals::Hysteresis::LOW,\n            Hysteresis::Medium => vals::Hysteresis::MEDIUM,\n            Hysteresis::High => vals::Hysteresis::HIGH,\n        };\n\n        let polarity = match config.output_polarity {\n            OutputPolarity::NotInverted => vals::Polarity::NOT_INVERTED,\n            OutputPolarity::Inverted => vals::Polarity::INVERTED,\n        };\n\n        let blanksel = match config.blanking_source {\n            BlankingSource::None => vals::Blanking::NO_BLANKING,\n            BlankingSource::Blank1 => vals::Blanking::BLANK1,\n            BlankingSource::Blank2 => vals::Blanking::BLANK2,\n            BlankingSource::Blank3 => vals::Blanking::BLANK3,\n        };\n\n        #[cfg(comp_u5)]\n        let winmode = match config.window_mode {\n            WindowMode::Disabled => vals::WindowMode::THIS_INPSEL,\n            WindowMode::Enabled => vals::WindowMode::OTHER_INPSEL,\n        };\n\n        #[cfg(comp_u5)]\n        let winout = match config.window_output {\n            WindowOutput::OwnValue => vals::WindowOut::COMP1_VALUE,\n            WindowOutput::XorValue => vals::WindowOut::COMP1_VALUE_XOR_COMP2_VALUE,\n        };\n\n        #[cfg(comp_v2)]\n        let inp_channel = inp_channel != 0;\n\n        T::regs().csr().modify(|w| {\n            w.set_inpsel(inp_channel);\n            w.set_inmsel(inmsel);\n            w.set_hyst(hyst);\n            w.set_polarity(polarity);\n            w.set_blanksel(blanksel);\n\n            // G4 COMP needs SCALEN/BRGEN bits to enable internal voltage references.\n            // SCALEN enables the Vrefint scaler, BRGEN enables the bridge resistor divider.\n            #[cfg(comp_v2)]\n            {\n                w.set_scalen(matches!(\n                    inmsel,\n                    vals::Inm::QUARTER_VREF | vals::Inm::HALF_VREF | vals::Inm::THREE_QUARTER_VREF | vals::Inm::VREF\n                ));\n                w.set_brgen(matches!(\n                    inmsel,\n                    vals::Inm::QUARTER_VREF | vals::Inm::HALF_VREF | vals::Inm::THREE_QUARTER_VREF\n                ));\n            }\n\n            w.set_en(true);\n            #[cfg(comp_u5)]\n            {\n                w.set_pwrmode(pwrmode);\n                w.set_winmode(winmode);\n                w.set_winout(winout);\n            }\n        });\n    }\n\n    fn configure(inp_channel: u8, config: Config) {\n        let inmsel = match config.inverting_input {\n            InvertingInput::OneQuarterVref => vals::Inm::QUARTER_VREF,\n            InvertingInput::HalfVref => vals::Inm::HALF_VREF,\n            InvertingInput::ThreeQuarterVref => vals::Inm::THREE_QUARTER_VREF,\n            InvertingInput::Vref => vals::Inm::VREF,\n            #[cfg(comp_u5)]\n            InvertingInput::Dac1 => vals::Inm::DAC1,\n            #[cfg(comp_u5)]\n            InvertingInput::Dac2 => vals::Inm::DAC2,\n            #[cfg(comp_v2)]\n            InvertingInput::Dac1 => vals::Inm::DACA,\n            #[cfg(comp_v2)]\n            InvertingInput::Dac2 => vals::Inm::DACB,\n\n            InvertingInput::InputPin => vals::Inm::INM1,\n            #[cfg(comp_v2)]\n            InvertingInput::InputPin2 => vals::Inm::INM2,\n        };\n\n        Self::configure_raw(inp_channel, inmsel, config);\n    }\n\n    fn configure_with_input_minus_pin(inp_channel: u8, inm_channel: u8, config: Config) {\n        // Map the channel to the INM enum value\n        // INM1 = 0x06, INM2 = 0x07\n        let inmsel = vals::Inm::from_bits(0x06 + inm_channel);\n\n        Self::configure_raw(inp_channel, inmsel, config)\n    }\n\n    /// Enable the comparator.\n    pub fn enable(&mut self) {\n        T::regs().csr().modify(|w| {\n            w.set_en(true);\n        });\n    }\n\n    /// Disable the comparator.\n    pub fn disable(&mut self) {\n        T::regs().csr().modify(|w| {\n            w.set_en(false);\n        });\n    }\n\n    /// Check if the comparator is enabled.\n    pub fn is_enabled(&self) -> bool {\n        T::regs().csr().read().en()\n    }\n\n    /// Get the current output level.\n    ///\n    /// Returns `true` if the non-inverting input is higher than the inverting input\n    /// (or the opposite if polarity is inverted).\n    pub fn output_level(&self) -> bool {\n        T::regs().csr().read().value()\n    }\n\n    /// Set the blanking source.\n    pub fn set_blanking_source(&mut self, source: BlankingSource) {\n        let blanksel = match source {\n            BlankingSource::None => vals::Blanking::NO_BLANKING,\n            BlankingSource::Blank1 => vals::Blanking::BLANK1,\n            BlankingSource::Blank2 => vals::Blanking::BLANK2,\n            BlankingSource::Blank3 => vals::Blanking::BLANK3,\n        };\n\n        T::regs().csr().modify(|w| {\n            w.set_blanksel(blanksel);\n        });\n    }\n\n    /// Wait for the comparator output to go high.\n    ///\n    /// This method enables the comparator if it's not already enabled,\n    /// then waits asynchronously for the output to transition high.\n    /// If the output is already high, it returns immediately.\n    pub async fn wait_for_high(&mut self) {\n        self.enable();\n\n        if self.output_level() {\n            return;\n        }\n\n        self.wait_for_rising_edge().await;\n    }\n\n    /// Wait for the comparator output to go low.\n    ///\n    /// This method enables the comparator if it's not already enabled,\n    /// then waits asynchronously for the output to transition low.\n    /// If the output is already low, it returns immediately.\n    pub async fn wait_for_low(&mut self) {\n        self.enable();\n\n        if !self.output_level() {\n            return;\n        }\n\n        self.wait_for_falling_edge().await;\n    }\n\n    /// Wait for a rising edge on the comparator output.\n    ///\n    /// This method waits asynchronously for the output to transition from low to high.\n    pub async fn wait_for_rising_edge(&mut self) {\n        self.enable();\n\n        // Configure EXTI for rising edge\n        T::configure_exti(true, false);\n\n        poll_fn(|cx| {\n            T::state().waker.register(cx.waker());\n\n            // Check if interrupt already fired (IMR was cleared by handler)\n            if !T::is_exti_interrupt_enabled() {\n                return Poll::Ready(());\n            }\n\n            Poll::Pending\n        })\n        .await;\n    }\n\n    /// Wait for a falling edge on the comparator output.\n    ///\n    /// This method waits asynchronously for the output to transition from high to low.\n    pub async fn wait_for_falling_edge(&mut self) {\n        self.enable();\n\n        // Configure EXTI for falling edge\n        T::configure_exti(false, true);\n\n        poll_fn(|cx| {\n            T::state().waker.register(cx.waker());\n\n            // Check if interrupt already fired (IMR was cleared by handler)\n            if !T::is_exti_interrupt_enabled() {\n                return Poll::Ready(());\n            }\n\n            Poll::Pending\n        })\n        .await;\n    }\n\n    /// Wait for any edge (rising or falling) on the comparator output.\n    ///\n    /// This method waits asynchronously for any output transition.\n    pub async fn wait_for_any_edge(&mut self) {\n        self.enable();\n\n        // Configure EXTI for both edges\n        T::configure_exti(true, true);\n\n        poll_fn(|cx| {\n            T::state().waker.register(cx.waker());\n\n            // Check if interrupt already fired (IMR was cleared by handler)\n            if !T::is_exti_interrupt_enabled() {\n                return Poll::Ready(());\n            }\n\n            Poll::Pending\n        })\n        .await;\n    }\n}\n\nimpl<'d, T: Instance> Drop for Comp<'d, T> {\n    fn drop(&mut self) {\n        T::regs().csr().modify(|w| {\n            w.set_en(false);\n        });\n        T::disable_exti_interrupt();\n        T::info().rcc.disable();\n    }\n}\n\npub(crate) struct Info {\n    rcc: RccInfo,\n}\n\npub(crate) trait SealedInstance {\n    fn info() -> &'static Info;\n    fn regs() -> crate::pac::comp::Comp;\n    fn state() -> &'static State;\n    fn exti_line() -> u8;\n    fn configure_exti(rising: bool, falling: bool);\n    fn enable_exti_interrupt();\n    fn disable_exti_interrupt();\n    fn is_exti_interrupt_enabled() -> bool;\n    fn clear_exti_pending();\n}\n\npub(crate) trait SealedInputPlusPin<T: Instance> {\n    fn channel(&self) -> u8;\n}\n\npub(crate) trait SealedInputMinusPin<T: Instance> {\n    fn channel(&self) -> u8;\n}\n\n/// Comparator instance trait.\n#[allow(private_bounds)]\npub trait Instance: SealedInstance + PeripheralType + 'static {\n    /// Interrupt type for this instance.\n    type Interrupt: Interrupt;\n}\n\n/// Non-inverting input pin trait.\n#[allow(private_bounds)]\npub trait InputPlusPin<T: Instance>: SealedInputPlusPin<T> {}\n\n/// Inverting input pin trait.\n#[allow(private_bounds)]\npub trait InputMinusPin<T: Instance>: SealedInputMinusPin<T> {}\n\nmacro_rules! impl_comp {\n    ($inst:ident, $exti_line:expr) => {\n        impl SealedInstance for crate::peripherals::$inst {\n            fn info() -> &'static Info {\n                use crate::rcc::SealedRccPeripheral;\n                static INFO: Info = Info {\n                    rcc: crate::peripherals::$inst::RCC_INFO,\n                };\n                &INFO\n            }\n\n            fn regs() -> crate::pac::comp::Comp {\n                crate::pac::$inst\n            }\n\n            fn state() -> &'static State {\n                static STATE: State = State::new();\n                &STATE\n            }\n\n            fn exti_line() -> u8 {\n                $exti_line\n            }\n\n            fn configure_exti(rising: bool, falling: bool) {\n                use crate::pac::EXTI;\n\n                let line = Self::exti_line() as usize;\n\n                critical_section::with(|_| {\n                    // Configure rising/falling edge triggers\n                    EXTI.rtsr(0).modify(|w| w.set_line(line, rising));\n                    EXTI.ftsr(0).modify(|w| w.set_line(line, falling));\n\n                    // Clear any pending interrupt\n                    Self::clear_exti_pending();\n\n                    // Enable the interrupt\n                    Self::enable_exti_interrupt();\n                });\n            }\n\n            fn enable_exti_interrupt() {\n                use crate::pac::EXTI;\n                let line = Self::exti_line() as usize;\n\n                #[cfg(any(\n                    exti_c0, exti_g0, exti_u0, exti_l5, exti_u5, exti_u3, exti_h5, exti_h50, exti_n6\n                ))]\n                EXTI.imr(0).modify(|w| w.set_line(line, true));\n\n                #[cfg(not(any(\n                    exti_c0, exti_g0, exti_u0, exti_l5, exti_u5, exti_u3, exti_h5, exti_h50, exti_n6\n                )))]\n                EXTI.imr(0).modify(|w| w.set_line(line, true));\n            }\n\n            fn disable_exti_interrupt() {\n                use crate::pac::EXTI;\n                let line = Self::exti_line() as usize;\n                EXTI.imr(0).modify(|w| w.set_line(line, false));\n            }\n\n            fn is_exti_interrupt_enabled() -> bool {\n                use crate::pac::EXTI;\n                let line = Self::exti_line() as usize;\n                EXTI.imr(0).read().line(line)\n            }\n\n            fn clear_exti_pending() {\n                use crate::pac::EXTI;\n                let line = Self::exti_line() as usize;\n\n                #[cfg(not(any(\n                    exti_c0, exti_g0, exti_u0, exti_l5, exti_u5, exti_u3, exti_h5, exti_h50, exti_n6\n                )))]\n                EXTI.pr(0).write(|w| w.set_line(line, true));\n\n                #[cfg(any(\n                    exti_c0, exti_g0, exti_u0, exti_l5, exti_u5, exti_u3, exti_h5, exti_h50, exti_n6\n                ))]\n                {\n                    EXTI.rpr(0).write(|w| w.set_line(line, true));\n                    EXTI.fpr(0).write(|w| w.set_line(line, true));\n                }\n            }\n        }\n\n        impl Instance for crate::peripherals::$inst {\n            type Interrupt = crate::_generated::peripheral_interrupts::$inst::WKUP;\n        }\n    };\n}\n\n#[cfg(comp_u5)]\nforeach_peripheral! {\n    (comp, COMP1) => {\n        impl_comp!(COMP1, 17);\n    };\n    (comp, COMP2) => {\n        impl_comp!(COMP2, 18);\n    };\n}\n\n#[cfg(comp_v2)]\nforeach_peripheral! {\n    (comp, COMP1) => {\n        impl_comp!(COMP1, 21);\n    };\n    (comp, COMP2) => {\n        impl_comp!(COMP2, 22);\n    };\n    (comp, COMP3) => {\n        impl_comp!(COMP3, 29);\n    };\n    (comp, COMP4) => {\n        impl_comp!(COMP4, 30);\n    };\n    (comp, COMP5) => {\n        impl_comp!(COMP5, 31);\n    };\n    (comp, COMP6) => {\n        impl_comp!(COMP6, 32);\n    };\n    (comp, COMP7) => {\n        impl_comp!(COMP7, 33);\n    };\n}\n\n#[allow(unused_macros)]\nmacro_rules! impl_comp_inp_pin {\n    ($inst:ident, $pin:ident, $ch:expr) => {\n        impl crate::comp::InputPlusPin<crate::peripherals::$inst> for crate::peripherals::$pin {}\n        impl crate::comp::SealedInputPlusPin<crate::peripherals::$inst> for crate::peripherals::$pin {\n            fn channel(&self) -> u8 {\n                $ch\n            }\n        }\n    };\n}\n\n#[allow(unused_macros)]\nmacro_rules! impl_comp_inm_pin {\n    ($inst:ident, $pin:ident, $ch:expr) => {\n        impl crate::comp::InputMinusPin<crate::peripherals::$inst> for crate::peripherals::$pin {}\n        impl crate::comp::SealedInputMinusPin<crate::peripherals::$inst> for crate::peripherals::$pin {\n            fn channel(&self) -> u8 {\n                $ch\n            }\n        }\n    };\n}\n\n// COMP pin implementations are generated by build.rs from stm32-data.\n// Channel numbers (INP0/INP1, INM0/INM1) come from data/extra/STM32G4.yaml\n// in the stm32-data repository.\n"
  },
  {
    "path": "embassy-stm32/src/cordic/enums.rs",
    "content": "/// CORDIC function\n#[allow(missing_docs)]\n#[derive(Debug, Clone, Copy)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub enum Function {\n    Cos = 0,\n    Sin,\n    Phase,\n    Modulus,\n    Arctan,\n    Cosh,\n    Sinh,\n    Arctanh,\n    Ln,\n    Sqrt,\n}\n\n/// CORDIC precision\n#[allow(missing_docs)]\n#[derive(Debug, Clone, Copy, Default)]\npub enum Precision {\n    Iters4 = 1,\n    Iters8,\n    Iters12,\n    Iters16,\n    Iters20,\n    #[default]\n    Iters24, // this value is recommended by Reference Manual\n    Iters28,\n    Iters32,\n    Iters36,\n    Iters40,\n    Iters44,\n    Iters48,\n    Iters52,\n    Iters56,\n    Iters60,\n}\n\n/// CORDIC scale\n#[allow(missing_docs)]\n#[derive(Debug, Clone, Copy, Default, PartialEq)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub enum Scale {\n    #[default]\n    Arg1Res1 = 0,\n    Arg1o2Res2,\n    Arg1o4Res4,\n    Arg1o8Res8,\n    Arg1o16Res16,\n    Arg1o32Res32,\n    Arg1o64Res64,\n    Arg1o128Res128,\n}\n\n/// CORDIC argument/result register access count\n#[allow(missing_docs)]\n#[derive(Debug, Clone, Copy, Default)]\npub enum AccessCount {\n    #[default]\n    One,\n    Two,\n}\n\n/// CORDIC argument/result data width\n#[allow(missing_docs)]\n#[derive(Clone, Copy)]\npub enum Width {\n    Bits32,\n    Bits16,\n}\n"
  },
  {
    "path": "embassy-stm32/src/cordic/errors.rs",
    "content": "use super::{Function, Scale};\n\n/// Error for [Cordic](super::Cordic)\n#[derive(Debug)]\npub enum CordicError {\n    /// Config error\n    ConfigError(ConfigError),\n    /// Argument length is incorrect\n    ArgumentLengthIncorrect,\n    /// Result buffer length error\n    ResultLengthNotEnough,\n    /// Input value is out of range for Q1.x format\n    NumberOutOfRange(NumberOutOfRange),\n    /// Argument error\n    ArgError(ArgError),\n}\n\nimpl From<ConfigError> for CordicError {\n    fn from(value: ConfigError) -> Self {\n        Self::ConfigError(value)\n    }\n}\n\nimpl From<NumberOutOfRange> for CordicError {\n    fn from(value: NumberOutOfRange) -> Self {\n        Self::NumberOutOfRange(value)\n    }\n}\n\nimpl From<ArgError> for CordicError {\n    fn from(value: ArgError) -> Self {\n        Self::ArgError(value)\n    }\n}\n\n#[cfg(feature = \"defmt\")]\nimpl defmt::Format for CordicError {\n    fn format(&self, fmt: defmt::Formatter) {\n        use CordicError::*;\n\n        match self {\n            ConfigError(e) => defmt::write!(fmt, \"{}\", e),\n            ResultLengthNotEnough => defmt::write!(fmt, \"Output buffer length is not long enough\"),\n            ArgumentLengthIncorrect => defmt::write!(fmt, \"Argument length incorrect\"),\n            NumberOutOfRange(e) => defmt::write!(fmt, \"{}\", e),\n            ArgError(e) => defmt::write!(fmt, \"{}\", e),\n        }\n    }\n}\n\n/// Error during parsing [Cordic::Config](super::Config)\n#[allow(dead_code)]\n#[derive(Debug)]\npub struct ConfigError {\n    pub(super) func: Function,\n    pub(super) scale_range: [u8; 2],\n}\n\n#[cfg(feature = \"defmt\")]\nimpl defmt::Format for ConfigError {\n    fn format(&self, fmt: defmt::Formatter) {\n        defmt::write!(fmt, \"For FUNCTION: {},\", self.func);\n\n        if self.scale_range[0] == self.scale_range[1] {\n            defmt::write!(fmt, \" SCALE value should be {}\", self.scale_range[0])\n        } else {\n            defmt::write!(\n                fmt,\n                \" SCALE value should be {} <= SCALE <= {}\",\n                self.scale_range[0],\n                self.scale_range[1]\n            )\n        }\n    }\n}\n\n/// Input value is out of range for Q1.x format\n#[allow(missing_docs)]\n#[derive(Debug)]\npub enum NumberOutOfRange {\n    BelowLowerBound,\n    AboveUpperBound,\n}\n\n#[cfg(feature = \"defmt\")]\nimpl defmt::Format for NumberOutOfRange {\n    fn format(&self, fmt: defmt::Formatter) {\n        use NumberOutOfRange::*;\n\n        match self {\n            BelowLowerBound => defmt::write!(fmt, \"input value should be equal or greater than -1\"),\n            AboveUpperBound => defmt::write!(fmt, \"input value should be equal or less than 1\"),\n        }\n    }\n}\n\n/// Error on checking input arguments\n#[allow(dead_code)]\n#[derive(Debug)]\npub struct ArgError {\n    pub(super) func: Function,\n    pub(super) scale: Option<Scale>,\n    pub(super) arg_range: [f32; 2], // only for debug display, f32 is ok\n    pub(super) inclusive_upper_bound: bool,\n    pub(super) arg_type: ArgType,\n}\n\n#[cfg(feature = \"defmt\")]\nimpl defmt::Format for ArgError {\n    fn format(&self, fmt: defmt::Formatter) {\n        defmt::write!(fmt, \"For FUNCTION: {},\", self.func);\n\n        if let Some(scale) = self.scale {\n            defmt::write!(fmt, \" when SCALE is {},\", scale);\n        }\n\n        defmt::write!(fmt, \" {} should be\", self.arg_type);\n\n        if self.inclusive_upper_bound {\n            defmt::write!(\n                fmt,\n                \" {} <= {} <= {}\",\n                self.arg_range[0],\n                self.arg_type,\n                self.arg_range[1]\n            )\n        } else {\n            defmt::write!(\n                fmt,\n                \" {} <= {} < {}\",\n                self.arg_range[0],\n                self.arg_type,\n                self.arg_range[1]\n            )\n        };\n    }\n}\n\n#[derive(Debug)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub(super) enum ArgType {\n    Arg1,\n    Arg2,\n}\n"
  },
  {
    "path": "embassy-stm32/src/cordic/mod.rs",
    "content": "//! Coordinate Rotation Digital Computer (CORDIC)\n\nuse embassy_hal_internal::drop::OnDrop;\nuse embassy_hal_internal::{Peri, PeripheralType};\n\nuse crate::pac::cordic::vals;\nuse crate::{dma, peripherals, rcc};\n\nmod enums;\npub use enums::*;\n\nmod errors;\npub use errors::*;\n\npub mod utils;\n\n/// CORDIC driver\npub struct Cordic<'d, T: Instance> {\n    peri: Peri<'d, T>,\n    config: Config,\n}\n\n/// Cordic instance\ntrait SealedInstance {\n    /// Get access to CORDIC registers\n    fn regs() -> crate::pac::cordic::Cordic;\n\n    /// Read RRDY flag\n    fn ready_to_read(&self) -> bool {\n        Self::regs().csr().read().rrdy()\n    }\n\n    /// Write value to WDATA\n    fn write_argument(&self, arg: u32) {\n        Self::regs().wdata().write_value(arg)\n    }\n\n    /// Read value from RDATA\n    fn read_result(&self) -> u32 {\n        Self::regs().rdata().read()\n    }\n}\n\n/// CORDIC instance trait\n#[allow(private_bounds)]\npub trait Instance: SealedInstance + PeripheralType + crate::rcc::RccPeripheral {}\n\n/// CORDIC configuration\n#[derive(Debug)]\npub struct Config {\n    function: Function,\n    precision: Precision,\n    scale: Scale,\n    arg_count: AccessCount,\n    res_count: AccessCount,\n}\n\nimpl Config {\n    /// Create a config for Cordic driver\n    ///\n    /// `arg_count` defaults to `AccessCount::One` and `res_count` defaults to `AccessCount::Two`.\n    /// Use the builder methods [`Self::arg_count`] and [`Self::res_count`] to override.\n    pub fn new(function: Function, precision: Precision, scale: Scale) -> Result<Self, CordicError> {\n        let config = Self {\n            function,\n            precision,\n            scale,\n            arg_count: AccessCount::One,\n            res_count: AccessCount::Two,\n        };\n\n        config.check_scale()?;\n\n        Ok(config)\n    }\n\n    /// Set the argument access count.\n    ///\n    /// `AccessCount::One`: each WDATA write provides one argument (ARG1), reusing the previous ARG2.\n    /// `AccessCount::Two`: arguments are written in pairs (ARG1 then ARG2) to WDATA.\n    pub fn arg_count(mut self, arg_count: AccessCount) -> Self {\n        self.arg_count = arg_count;\n        self\n    }\n\n    /// Set the result access count.\n    ///\n    /// `AccessCount::One`: each calculation produces one RDATA read (primary result only).\n    /// `AccessCount::Two`: each calculation produces two RDATA reads (primary + secondary).\n    pub fn res_count(mut self, res_count: AccessCount) -> Self {\n        self.res_count = res_count;\n        self\n    }\n\n    fn check_scale(&self) -> Result<(), ConfigError> {\n        use Function::*;\n\n        let scale_raw = self.scale as u8;\n\n        let err_range = match self.function {\n            Cos | Sin | Phase | Modulus if !(0..=0).contains(&scale_raw) => Some([0, 0]),\n\n            Arctan if !(0..=7).contains(&scale_raw) => Some([0, 7]),\n\n            Cosh | Sinh | Arctanh if !(1..=1).contains(&scale_raw) => Some([1, 1]),\n\n            Ln if !(1..=4).contains(&scale_raw) => Some([1, 4]),\n\n            Sqrt if !(0..=2).contains(&scale_raw) => Some([0, 2]),\n\n            Cos | Sin | Phase | Modulus | Arctan | Cosh | Sinh | Arctanh | Ln | Sqrt => None,\n        };\n\n        if let Some(range) = err_range {\n            Err(ConfigError {\n                func: self.function,\n                scale_range: range,\n            })\n        } else {\n            Ok(())\n        }\n    }\n}\n\n// common method\nimpl<'d, T: Instance> Cordic<'d, T> {\n    /// Create a Cordic driver instance\n    pub fn new(peri: Peri<'d, T>, config: Config) -> Self {\n        rcc::enable_and_reset::<T>();\n\n        let mut instance = Self { peri, config };\n\n        instance.reconfigure();\n\n        instance\n    }\n\n    /// Set a new config for Cordic driver.\n    ///\n    /// This calls [`Self::reconfigure`], which resets ARG2 to +1.\n    /// To change only `arg_count`/`res_count` without resetting ARG2,\n    /// use [`Self::set_access_counts`] instead.\n    pub fn set_config(&mut self, config: Config) {\n        self.config = config;\n        self.reconfigure();\n    }\n\n    /// Change `arg_count` and `res_count` without resetting ARG2.\n    ///\n    /// This is a lightweight CSR update for switching between 1-arg and 2-arg\n    /// modes within the same function configuration (e.g. after an initial\n    /// 2-arg call sets ARG2, switch to 1-arg mode for the hot loop).\n    pub fn set_access_counts(&mut self, arg_count: AccessCount, res_count: AccessCount) {\n        self.config.arg_count = arg_count;\n        self.config.res_count = res_count;\n        T::regs().csr().modify(|v| {\n            v.set_nargs(match arg_count {\n                AccessCount::One => vals::Num::NUM1,\n                AccessCount::Two => vals::Num::NUM2,\n            });\n            v.set_nres(match res_count {\n                AccessCount::One => vals::Num::NUM1,\n                AccessCount::Two => vals::Num::NUM2,\n            });\n        });\n    }\n\n    fn clean_rrdy_flag(&mut self) {\n        while self.peri.ready_to_read() {\n            self.peri.read_result();\n        }\n    }\n\n    /// Disable IRQ and DMA, clean RRDY, and set ARG2 to +1 (0x7FFFFFFF)\n    pub fn reconfigure(&mut self) {\n        // Disable IRQ and DMA first\n        T::regs().csr().modify(|v| {\n            v.set_ien(false);\n            v.set_dmaren(false);\n            v.set_dmawen(false);\n        });\n        self.clean_rrdy_flag();\n\n        // Reset ARG2 to +1: configure for 2-arg Cos with minimal precision, feed dummy args.\n        T::regs().csr().modify(|v| {\n            v.set_func(vals::Func::from_bits(Function::Cos as u8));\n            v.set_precision(vals::Precision::from_bits(Precision::Iters4 as u8));\n            v.set_scale(vals::Scale::from_bits(Scale::Arg1Res1 as u8));\n            v.set_nargs(vals::Num::NUM2);\n            v.set_argsize(vals::Size::BITS32);\n            v.set_ressize(vals::Size::BITS32);\n        });\n        self.peri.write_argument(0x0u32);\n        self.peri.write_argument(0x7FFFFFFFu32);\n        self.clean_rrdy_flag();\n\n        // Apply full user configuration (func, precision, scale, data interface).\n        T::regs().csr().modify(|v| {\n            v.set_func(vals::Func::from_bits(self.config.function as u8));\n            v.set_precision(vals::Precision::from_bits(self.config.precision as u8));\n            v.set_scale(vals::Scale::from_bits(self.config.scale as u8));\n            v.set_nargs(match self.config.arg_count {\n                AccessCount::One => vals::Num::NUM1,\n                AccessCount::Two => vals::Num::NUM2,\n            });\n            v.set_nres(match self.config.res_count {\n                AccessCount::One => vals::Num::NUM1,\n                AccessCount::Two => vals::Num::NUM2,\n            });\n            v.set_argsize(vals::Size::BITS32);\n            v.set_ressize(vals::Size::BITS32);\n        });\n\n        // Changing NRES or other CSR fields above can re-assert RRDY if secondary\n        // results from the dummy calc were not fully drained. Clean it again.\n        self.clean_rrdy_flag();\n    }\n}\n\nimpl<'d, T: Instance> Drop for Cordic<'d, T> {\n    fn drop(&mut self) {\n        rcc::disable::<T>();\n    }\n}\n\n// q1.31 related\nimpl<'d, T: Instance> Cordic<'d, T> {\n    /// Run a blocking CORDIC calculation in q1.31 format.\n    ///\n    /// Uses `arg_count` and `res_count` from the current [`Config`].\n    /// If `arg_count` is `One`, ARG2 must have been set to the desired value\n    /// beforehand (e.g. via a prior `Two`-arg call or [`Self::reconfigure`]).\n    #[inline]\n    pub fn blocking_calc_32bit(&mut self, arg: &[u32], res: &mut [u32]) -> Result<usize, CordicError> {\n        if arg.is_empty() {\n            return Ok(0);\n        }\n\n        let arg1_only = matches!(self.config.arg_count, AccessCount::One);\n        let res1_only = matches!(self.config.res_count, AccessCount::One);\n\n        let res_cnt = Self::check_arg_res_length_32bit(arg.len(), res.len(), arg1_only, res1_only)?;\n\n        let mut cnt = 0;\n\n        match arg1_only {\n            true => {\n                // To use cordic preload function, the first value is special.\n                // It is loaded to CORDIC WDATA register out side of loop\n                let first_value = arg[0];\n\n                // preload 1st value to CORDIC, to start the CORDIC calc\n                self.peri.write_argument(first_value);\n\n                for &arg1 in &arg[1..] {\n                    // preload arg1 (for next calc)\n                    self.peri.write_argument(arg1);\n\n                    // then read current result out\n                    res[cnt] = self.peri.read_result();\n                    cnt += 1;\n                    if !res1_only {\n                        res[cnt] = self.peri.read_result();\n                        cnt += 1;\n                    }\n                }\n\n                // read the last result\n                res[cnt] = self.peri.read_result();\n                cnt += 1;\n                if !res1_only {\n                    res[cnt] = self.peri.read_result();\n                    // cnt += 1;\n                }\n            }\n            false => {\n                // To use cordic preload function, the first and last value is special.\n                // They are load to CORDIC WDATA register out side of loop\n                let first_value = arg[0];\n                let last_value = arg[arg.len() - 1];\n\n                let paired_args = &arg[1..arg.len() - 1];\n\n                // preload 1st value to CORDIC\n                self.peri.write_argument(first_value);\n\n                for args in paired_args.chunks(2) {\n                    let arg2 = args[0];\n                    let arg1 = args[1];\n\n                    // load arg2 (for current calc) first, to start the CORDIC calc\n                    self.peri.write_argument(arg2);\n\n                    // preload arg1 (for next calc)\n                    self.peri.write_argument(arg1);\n\n                    // then read current result out\n                    res[cnt] = self.peri.read_result();\n                    cnt += 1;\n                    if !res1_only {\n                        res[cnt] = self.peri.read_result();\n                        cnt += 1;\n                    }\n                }\n\n                // load last value to CORDIC, and finish the calculation\n                self.peri.write_argument(last_value);\n                res[cnt] = self.peri.read_result();\n                cnt += 1;\n                if !res1_only {\n                    res[cnt] = self.peri.read_result();\n                    // cnt += 1;\n                }\n            }\n        }\n\n        // at this point cnt should be equal to res_cnt\n\n        Ok(res_cnt)\n    }\n\n    /// Run an async CORDIC calculation in q1.31 format.\n    ///\n    /// Uses `arg_count` and `res_count` from the current [`Config`].\n    /// If `arg_count` is `One`, ARG2 must have been set to the desired value\n    /// beforehand (e.g. via a prior `Two`-arg call or [`Self::reconfigure`]).\n    #[inline]\n    pub async fn async_calc_32bit<'a, W, R>(\n        &mut self,\n        mut write_dma: Peri<'a, W>,\n        mut read_dma: Peri<'a, R>,\n        irq: impl crate::interrupt::typelevel::Binding<W::Interrupt, crate::dma::InterruptHandler<W>>\n        + crate::interrupt::typelevel::Binding<R::Interrupt, crate::dma::InterruptHandler<R>>\n        + 'a,\n        arg: &[u32],\n        res: &mut [u32],\n    ) -> Result<usize, CordicError>\n    where\n        W: WriteDma<T>,\n        R: ReadDma<T>,\n    {\n        if arg.is_empty() {\n            return Ok(0);\n        }\n\n        let arg1_only = matches!(self.config.arg_count, AccessCount::One);\n        let res1_only = matches!(self.config.res_count, AccessCount::One);\n\n        let res_cnt = Self::check_arg_res_length_32bit(arg.len(), res.len(), arg1_only, res1_only)?;\n\n        let active_res_buf = &mut res[..res_cnt];\n\n        let write_req = write_dma.request();\n        let read_req = read_dma.request();\n\n        // DMAWEN and DMAREN must be set in separate register writes;\n        // setting both in a single write hangs the CORDIC+DMA on STM32H563.\n        T::regs().csr().modify(|v| v.set_dmawen(true));\n        T::regs().csr().modify(|v| v.set_dmaren(true));\n\n        // Same H563 constraint: clear DMAWEN and DMAREN in separate writes.\n        let _on_drop = OnDrop::new(|| {\n            T::regs().csr().modify(|v| v.set_dmawen(false));\n            T::regs().csr().modify(|v| v.set_dmaren(false));\n        });\n\n        unsafe {\n            let mut write_channel = dma::Channel::new(write_dma.reborrow(), irq);\n            let write_transfer =\n                write_channel.write(write_req, arg, T::regs().wdata().as_ptr() as *mut _, Default::default());\n\n            let mut read_channel = dma::Channel::new(read_dma.reborrow(), irq);\n            let read_transfer = read_channel.read(\n                read_req,\n                T::regs().rdata().as_ptr() as *mut _,\n                active_res_buf,\n                Default::default(),\n            );\n\n            embassy_futures::join::join(write_transfer, read_transfer).await;\n        }\n\n        Ok(res_cnt)\n    }\n\n    fn check_arg_res_length_32bit(\n        arg_len: usize,\n        res_len: usize,\n        arg1_only: bool,\n        res1_only: bool,\n    ) -> Result<usize, CordicError> {\n        if !arg1_only && arg_len % 2 != 0 {\n            return Err(CordicError::ArgumentLengthIncorrect);\n        }\n\n        let mut minimal_res_length = arg_len;\n\n        if !res1_only {\n            minimal_res_length *= 2;\n        }\n\n        if !arg1_only {\n            minimal_res_length /= 2\n        }\n\n        if minimal_res_length > res_len {\n            return Err(CordicError::ResultLengthNotEnough);\n        }\n\n        Ok(minimal_res_length)\n    }\n}\n\n// q1.15 related\nimpl<'d, T: Instance> Cordic<'d, T> {\n    /// Run a blocking CORDIC calculation in q1.15 format.\n    ///\n    /// In q1.15 mode, each WDATA write / RDATA read contains two packed 16-bit values,\n    /// so `nargs` and `nres` are always 1 (one register access = two values).\n    ///\n    /// After this call, the CSR is restored to the 32-bit state from the current [`Config`].\n    #[inline]\n    pub fn blocking_calc_16bit(&mut self, arg: &[u32], res: &mut [u32]) -> Result<usize, CordicError> {\n        if arg.is_empty() {\n            return Ok(0);\n        }\n\n        if arg.len() > res.len() {\n            return Err(CordicError::ResultLengthNotEnough);\n        }\n\n        let res_cnt = arg.len();\n\n        // In q1.15 mode, 1 write/read to access 2 arguments/results\n        T::regs().csr().modify(|v| {\n            v.set_nargs(vals::Num::NUM1);\n            v.set_nres(vals::Num::NUM1);\n            v.set_argsize(vals::Size::BITS16);\n            v.set_ressize(vals::Size::BITS16);\n        });\n\n        // To use cordic preload function, the first value is special.\n        // It is loaded to CORDIC WDATA register out side of loop\n        let first_value = arg[0];\n\n        // preload 1st value to CORDIC, to start the CORDIC calc\n        self.peri.write_argument(first_value);\n\n        let mut cnt = 0;\n\n        for &arg_val in &arg[1..] {\n            // preload arg_val (for next calc)\n            self.peri.write_argument(arg_val);\n\n            // then read current result out\n            res[cnt] = self.peri.read_result();\n            cnt += 1;\n        }\n\n        // read last result out\n        res[cnt] = self.peri.read_result();\n        // cnt += 1;\n\n        // Restore CSR to 32-bit state matching current Config\n        self.restore_csr_from_config();\n\n        Ok(res_cnt)\n    }\n\n    /// Run an async CORDIC calculation in q1.15 format.\n    ///\n    /// In q1.15 mode, each WDATA write / RDATA read contains two packed 16-bit values,\n    /// so `nargs` and `nres` are always 1 (one register access = two values).\n    ///\n    /// After this call, the CSR is restored to the 32-bit state from the current [`Config`].\n    #[inline]\n    pub async fn async_calc_16bit<'a, W, R>(\n        &mut self,\n        mut write_dma: Peri<'a, W>,\n        mut read_dma: Peri<'a, R>,\n        irq: impl crate::interrupt::typelevel::Binding<W::Interrupt, crate::dma::InterruptHandler<W>>\n        + crate::interrupt::typelevel::Binding<R::Interrupt, crate::dma::InterruptHandler<R>>\n        + 'a,\n        arg: &[u32],\n        res: &mut [u32],\n    ) -> Result<usize, CordicError>\n    where\n        W: WriteDma<T>,\n        R: ReadDma<T>,\n    {\n        if arg.is_empty() {\n            return Ok(0);\n        }\n\n        if arg.len() > res.len() {\n            return Err(CordicError::ResultLengthNotEnough);\n        }\n\n        let res_cnt = arg.len();\n\n        let active_res_buf = &mut res[..res_cnt];\n\n        // In q1.15 mode, 1 write/read to access 2 arguments/results\n        T::regs().csr().modify(|v| {\n            v.set_nargs(vals::Num::NUM1);\n            v.set_nres(vals::Num::NUM1);\n            v.set_argsize(vals::Size::BITS16);\n            v.set_ressize(vals::Size::BITS16);\n        });\n\n        let write_req = write_dma.request();\n        let read_req = read_dma.request();\n\n        // DMAWEN and DMAREN must be set in separate register writes;\n        // setting both in a single write hangs the CORDIC+DMA on STM32H563.\n        T::regs().csr().modify(|v| v.set_dmawen(true));\n        T::regs().csr().modify(|v| v.set_dmaren(true));\n\n        // Same H563 constraint: clear DMAWEN and DMAREN in separate writes.\n        let _on_drop = OnDrop::new(|| {\n            T::regs().csr().modify(|v| v.set_dmawen(false));\n            T::regs().csr().modify(|v| v.set_dmaren(false));\n        });\n\n        unsafe {\n            let mut write_channel = dma::Channel::new(write_dma.reborrow(), irq);\n            let write_transfer =\n                write_channel.write(write_req, arg, T::regs().wdata().as_ptr() as *mut _, Default::default());\n\n            let mut read_channel = dma::Channel::new(read_dma.reborrow(), irq);\n            let read_transfer = read_channel.read(\n                read_req,\n                T::regs().rdata().as_ptr() as *mut _,\n                active_res_buf,\n                Default::default(),\n            );\n\n            embassy_futures::join::join(write_transfer, read_transfer).await;\n        }\n\n        // Restore CSR to 32-bit state matching current Config\n        self.restore_csr_from_config();\n\n        Ok(res_cnt)\n    }\n\n    fn restore_csr_from_config(&self) {\n        T::regs().csr().modify(|v| {\n            v.set_nargs(match self.config.arg_count {\n                AccessCount::One => vals::Num::NUM1,\n                AccessCount::Two => vals::Num::NUM2,\n            });\n            v.set_nres(match self.config.res_count {\n                AccessCount::One => vals::Num::NUM1,\n                AccessCount::Two => vals::Num::NUM2,\n            });\n            v.set_argsize(vals::Size::BITS32);\n            v.set_ressize(vals::Size::BITS32);\n        });\n    }\n}\n\nmacro_rules! check_arg_value {\n    ($func_arg1_name:ident, $func_arg2_name:ident, $float_type:ty) => {\n        impl<'d, T: Instance> Cordic<'d, T> {\n            /// check input value ARG1, SCALE and FUNCTION are compatible with each other\n            pub fn $func_arg1_name(&self, arg: $float_type) -> Result<(), ArgError> {\n                let config = &self.config;\n\n                use Function::*;\n\n                struct Arg1ErrInfo {\n                    scale: Option<Scale>,\n                    range: [f32; 2], // f32 is ok, it only used in error display\n                    inclusive_upper_bound: bool,\n                }\n\n                let err_info = match config.function {\n                    Cos | Sin | Phase | Modulus | Arctan if !(-1.0..=1.0).contains(arg) => Some(Arg1ErrInfo {\n                        scale: None,\n                        range: [-1.0, 1.0],\n                        inclusive_upper_bound: true,\n                    }),\n\n                    Cosh | Sinh if !(-0.559..=0.559).contains(arg) => Some(Arg1ErrInfo {\n                        scale: None,\n                        range: [-0.559, 0.559],\n                        inclusive_upper_bound: true,\n                    }),\n\n                    Arctanh if !(-0.403..=0.403).contains(arg) => Some(Arg1ErrInfo {\n                        scale: None,\n                        range: [-0.403, 0.403],\n                        inclusive_upper_bound: true,\n                    }),\n\n                    Ln => match config.scale {\n                        Scale::Arg1o2Res2 if !(0.0535..0.5).contains(arg) => Some(Arg1ErrInfo {\n                            scale: Some(Scale::Arg1o2Res2),\n                            range: [0.0535, 0.5],\n                            inclusive_upper_bound: false,\n                        }),\n                        Scale::Arg1o4Res4 if !(0.25..0.75).contains(arg) => Some(Arg1ErrInfo {\n                            scale: Some(Scale::Arg1o4Res4),\n                            range: [0.25, 0.75],\n                            inclusive_upper_bound: false,\n                        }),\n                        Scale::Arg1o8Res8 if !(0.375..0.875).contains(arg) => Some(Arg1ErrInfo {\n                            scale: Some(Scale::Arg1o8Res8),\n                            range: [0.375, 0.875],\n                            inclusive_upper_bound: false,\n                        }),\n                        Scale::Arg1o16Res16 if !(0.4375..0.584).contains(arg) => Some(Arg1ErrInfo {\n                            scale: Some(Scale::Arg1o16Res16),\n                            range: [0.4375, 0.584],\n                            inclusive_upper_bound: false,\n                        }),\n\n                        Scale::Arg1o2Res2 | Scale::Arg1o4Res4 | Scale::Arg1o8Res8 | Scale::Arg1o16Res16 => None,\n\n                        _ => unreachable!(),\n                    },\n\n                    Sqrt => match config.scale {\n                        Scale::Arg1Res1 if !(0.027..0.75).contains(arg) => Some(Arg1ErrInfo {\n                            scale: Some(Scale::Arg1Res1),\n                            range: [0.027, 0.75],\n                            inclusive_upper_bound: false,\n                        }),\n                        Scale::Arg1o2Res2 if !(0.375..0.875).contains(arg) => Some(Arg1ErrInfo {\n                            scale: Some(Scale::Arg1o2Res2),\n                            range: [0.375, 0.875],\n                            inclusive_upper_bound: false,\n                        }),\n                        Scale::Arg1o4Res4 if !(0.4375..0.584).contains(arg) => Some(Arg1ErrInfo {\n                            scale: Some(Scale::Arg1o4Res4),\n                            range: [0.4375, 0.584],\n                            inclusive_upper_bound: false,\n                        }),\n                        Scale::Arg1Res1 | Scale::Arg1o2Res2 | Scale::Arg1o4Res4 => None,\n                        _ => unreachable!(),\n                    },\n\n                    Cos | Sin | Phase | Modulus | Arctan | Cosh | Sinh | Arctanh => None,\n                };\n\n                if let Some(err) = err_info {\n                    return Err(ArgError {\n                        func: config.function,\n                        scale: err.scale,\n                        arg_range: err.range,\n                        inclusive_upper_bound: err.inclusive_upper_bound,\n                        arg_type: ArgType::Arg1,\n                    });\n                }\n\n                Ok(())\n            }\n\n            /// check input value ARG2 and FUNCTION are compatible with each other\n            pub fn $func_arg2_name(&self, arg: $float_type) -> Result<(), ArgError> {\n                let config = &self.config;\n\n                use Function::*;\n\n                struct Arg2ErrInfo {\n                    range: [f32; 2], // f32 is ok, it only used in error display\n                }\n\n                let err_info = match config.function {\n                    Cos | Sin if !(0.0..=1.0).contains(arg) => Some(Arg2ErrInfo { range: [0.0, 1.0] }),\n\n                    Phase | Modulus if !(-1.0..=1.0).contains(arg) => Some(Arg2ErrInfo { range: [-1.0, 1.0] }),\n\n                    Cos | Sin | Phase | Modulus | Arctan | Cosh | Sinh | Arctanh | Ln | Sqrt => None,\n                };\n\n                if let Some(err) = err_info {\n                    return Err(ArgError {\n                        func: config.function,\n                        scale: None,\n                        arg_range: err.range,\n                        inclusive_upper_bound: true,\n                        arg_type: ArgType::Arg2,\n                    });\n                }\n\n                Ok(())\n            }\n        }\n    };\n}\n\ncheck_arg_value!(check_f64_arg1, check_f64_arg2, &f64);\ncheck_arg_value!(check_f32_arg1, check_f32_arg2, &f32);\n\nforeach_interrupt!(\n    ($inst:ident, cordic, $block:ident, GLOBAL, $irq:ident) => {\n        impl Instance for peripherals::$inst {\n        }\n\n        impl SealedInstance for peripherals::$inst {\n            fn regs() -> crate::pac::cordic::Cordic {\n                crate::pac::$inst\n            }\n        }\n    };\n);\n\ndma_trait!(WriteDma, Instance);\ndma_trait!(ReadDma, Instance);\n"
  },
  {
    "path": "embassy-stm32/src/cordic/utils.rs",
    "content": "//! Common math utils\nuse super::errors::NumberOutOfRange;\n\nmacro_rules! floating_fixed_convert {\n    ($f_to_q:ident, $q_to_f:ident, $unsigned_bin_typ:ty, $signed_bin_typ:ty, $float_ty:ty, $offset:literal, $min_positive:literal) => {\n        /// convert float point to fixed point format\n        pub fn $f_to_q(value: $float_ty) -> Result<$unsigned_bin_typ, NumberOutOfRange> {\n            const MIN_POSITIVE: $float_ty = <$float_ty>::from_bits($min_positive);\n\n            if value < -1.0 {\n                return Err(NumberOutOfRange::BelowLowerBound)\n            }\n\n            if value > 1.0 {\n                return Err(NumberOutOfRange::AboveUpperBound)\n            }\n\n\n            let value = if 1.0 - MIN_POSITIVE < value && value <= 1.0 {\n                // make a exception for value between (1.0^{-x} , 1.0] float point,\n                // convert it to max representable value of q1.x format\n                (1.0 as $float_ty) - MIN_POSITIVE\n            } else {\n                value\n            };\n\n            // It's necessary to cast the float value to signed integer, before convert it to a unsigned value.\n            // Since value from register is actually a \"signed value\", a \"as\" cast will keep original binary format but mark it as a unsigned value for register writing.\n            // see https://doc.rust-lang.org/reference/expressions/operator-expr.html#numeric-cast\n            Ok((value * ((1 as $unsigned_bin_typ << $offset) as $float_ty)) as $signed_bin_typ as $unsigned_bin_typ)\n        }\n\n        #[inline(always)]\n        /// convert fixed point to float point format\n        pub fn $q_to_f(value: $unsigned_bin_typ) -> $float_ty {\n            // It's necessary to cast the unsigned integer to signed integer, before convert it to a float value.\n            // Since value from register is actually a \"signed value\", a \"as\" cast will keep original binary format but mark it as a signed value.\n            // see https://doc.rust-lang.org/reference/expressions/operator-expr.html#numeric-cast\n            (value as $signed_bin_typ as $float_ty) / ((1 as $unsigned_bin_typ << $offset) as $float_ty)\n        }\n    };\n}\n\nfloating_fixed_convert!(\n    f64_to_q1_31,\n    q1_31_to_f64,\n    u32,\n    i32,\n    f64,\n    31,\n    0x3E00_0000_0000_0000u64 // binary form of 1f64^(-31)\n);\n\nfloating_fixed_convert!(\n    f32_to_q1_15,\n    q1_15_to_f32,\n    u16,\n    i16,\n    f32,\n    15,\n    0x3800_0000u32 // binary form of 1f32^(-15)\n);\n"
  },
  {
    "path": "embassy-stm32/src/cpu.rs",
    "content": "//! Multicore utilities.\n/// The enum values are identical to the bus master IDs / core Ids defined for each\n/// chip family (i.e. stm32h747 see rm0399 table 95)\n#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash)]\n#[repr(u8)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub enum CoreId {\n    #[cfg(any(stm32h745, stm32h747, stm32h755, stm32h757))]\n    /// Cortex-M7, core 1.\n    Core0 = 0x3,\n\n    #[cfg(any(stm32h745, stm32h747, stm32h755, stm32h757))]\n    /// Cortex-M4, core 2.\n    Core1 = 0x1,\n\n    #[cfg(not(any(stm32h745, stm32h747, stm32h755, stm32h757)))]\n    /// Cortex-M4, core 1\n    Core0 = 0x4,\n\n    #[cfg(any(stm32wb, stm32wl))]\n    /// Cortex-M0+, core 2.\n    Core1 = 0x8,\n}\n\nimpl CoreId {\n    /// Get the current core id\n    /// This code assume that it is only executed on a Cortex-M M0+, M4 or M7 core.\n    pub fn current() -> Self {\n        let cpuid = unsafe { cortex_m::peripheral::CPUID::PTR.read_volatile().base.read() };\n        match (cpuid & 0x000000F0) >> 4 {\n            #[cfg(any(stm32wb, stm32wl))]\n            0x0 => CoreId::Core1,\n\n            #[cfg(not(any(stm32h745, stm32h747, stm32h755, stm32h757)))]\n            0x4 => CoreId::Core0,\n\n            #[cfg(any(stm32h745, stm32h747, stm32h755, stm32h757))]\n            0x4 => CoreId::Core1,\n\n            #[cfg(any(stm32h745, stm32h747, stm32h755, stm32h757))]\n            0x7 => CoreId::Core0,\n            _ => panic!(\"Unknown Cortex-M core\"),\n        }\n    }\n\n    #[cfg(any(stm32h745, stm32h747, stm32h755, stm32h757, stm32wb, stm32wl))]\n    /// Get the other core id\n    pub const fn other(&self) -> Self {\n        match &self {\n            Self::Core0 => Self::Core1,\n            Self::Core1 => Self::Core0,\n        }\n    }\n\n    /// Translates the core ID to an index into the interrupt registers.\n    pub const fn to_index(&self) -> usize {\n        match &self {\n            CoreId::Core0 => 0,\n            #[cfg(any(stm32h745, stm32h747, stm32h755, stm32h757, stm32wb, stm32wl))]\n            CoreId::Core1 => 1,\n        }\n    }\n}\n"
  },
  {
    "path": "embassy-stm32/src/crc/mod.rs",
    "content": "//! Cyclic Redundancy Check (CRC)\n#[cfg_attr(crc_v1, path = \"v1.rs\")]\n#[cfg_attr(crc_v2, path = \"v2v3.rs\")]\n#[cfg_attr(crc_v3, path = \"v2v3.rs\")]\nmod _version;\n\npub use _version::*;\n"
  },
  {
    "path": "embassy-stm32/src/crc/v1.rs",
    "content": "use crate::pac::CRC as PAC_CRC;\nuse crate::peripherals::CRC;\nuse crate::{Peri, rcc};\n\n/// CRC driver.\npub struct Crc<'d> {\n    _peri: Peri<'d, CRC>,\n}\n\nimpl<'d> Crc<'d> {\n    /// Instantiates the CRC32 peripheral and initializes it to default values.\n    pub fn new(peripheral: Peri<'d, CRC>) -> Self {\n        // Note: enable and reset come from RccPeripheral.\n        // enable CRC clock in RCC.\n        rcc::enable_and_reset::<CRC>();\n        let mut instance = Self { _peri: peripheral };\n        instance.reset();\n        instance\n    }\n\n    /// Resets the CRC unit to default value (0xFFFF_FFFF)\n    pub fn reset(&mut self) {\n        PAC_CRC.cr().write(|w| w.set_reset(true));\n    }\n\n    /// Feeds a word into the CRC peripheral. Returns the computed CRC.\n    pub fn feed_word(&mut self, word: u32) -> u32 {\n        // write a single byte to the device, and return the result\n        PAC_CRC.dr().write_value(word);\n        self.read()\n    }\n\n    /// Feeds a slice of words into the CRC peripheral. Returns the computed CRC.\n    pub fn feed_words(&mut self, words: &[u32]) -> u32 {\n        for word in words {\n            PAC_CRC.dr().write_value(*word);\n        }\n\n        self.read()\n    }\n\n    /// Read the CRC result value.\n    pub fn read(&self) -> u32 {\n        PAC_CRC.dr().read()\n    }\n}\n"
  },
  {
    "path": "embassy-stm32/src/crc/v2v3.rs",
    "content": "use crate::pac::CRC as PAC_CRC;\nuse crate::pac::crc::vals;\nuse crate::peripherals::CRC;\nuse crate::{Peri, rcc};\n\n/// CRC driver.\npub struct Crc<'d> {\n    _peripheral: Peri<'d, CRC>,\n    _config: Config,\n}\n\n/// CRC configuration error\n#[derive(Debug)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub enum ConfigError {\n    /// The selected polynomial is invalid.\n    InvalidPolynomial,\n}\n\n/// CRC configuration\npub struct Config {\n    reverse_in: InputReverseConfig,\n    reverse_out: bool,\n    #[cfg(crc_v3)]\n    poly_size: PolySize,\n    crc_init_value: u32,\n    #[cfg(crc_v3)]\n    crc_poly: u32,\n}\n\n/// Input reverse configuration.\npub enum InputReverseConfig {\n    /// Don't reverse anything\n    None,\n    /// Reverse bytes\n    Byte,\n    /// Reverse 16-bit halfwords\n    Halfword,\n    /// Reverse 32-bit words\n    Word,\n}\n\nimpl Config {\n    /// Create a new CRC config.\n    pub fn new(\n        reverse_in: InputReverseConfig,\n        reverse_out: bool,\n        #[cfg(crc_v3)] poly_size: PolySize,\n        crc_init_value: u32,\n        #[cfg(crc_v3)] crc_poly: u32,\n    ) -> Result<Self, ConfigError> {\n        // As Per RM0091 (DocID018940 Rev 9), Even polynomials are not supported.\n        #[cfg(crc_v3)]\n        if crc_poly % 2 == 0 {\n            return Err(ConfigError::InvalidPolynomial);\n        }\n        Ok(Config {\n            reverse_in,\n            reverse_out,\n            #[cfg(crc_v3)]\n            poly_size,\n            crc_init_value,\n            #[cfg(crc_v3)]\n            crc_poly,\n        })\n    }\n}\n\n/// Polynomial size\n#[cfg(crc_v3)]\n#[allow(missing_docs)]\npub enum PolySize {\n    Width7,\n    Width8,\n    Width16,\n    Width32,\n}\n\nimpl<'d> Crc<'d> {\n    /// Instantiates the CRC32 peripheral and initializes it to default values.\n    pub fn new(peripheral: Peri<'d, CRC>, config: Config) -> Self {\n        // Note: enable and reset come from RccPeripheral.\n        // reset to default values and enable CRC clock in RCC.\n        rcc::enable_and_reset::<CRC>();\n        let mut instance = Self {\n            _peripheral: peripheral,\n            _config: config,\n        };\n        instance.reconfigure();\n        instance.reset();\n        instance\n    }\n\n    /// Reset the CRC engine.\n    pub fn reset(&mut self) {\n        PAC_CRC.cr().modify(|w| w.set_reset(true));\n    }\n\n    /// Reconfigures the CRC peripheral. Doesn't reset.\n    fn reconfigure(&mut self) {\n        // Init CRC value\n        PAC_CRC.init().write_value(self._config.crc_init_value);\n        #[cfg(crc_v3)]\n        PAC_CRC.pol().write_value(self._config.crc_poly);\n\n        // configure CR components\n        // (reverse I/O, polysize, poly)\n        PAC_CRC.cr().write(|w| {\n            // configure reverse output\n            w.set_rev_out(match self._config.reverse_out {\n                true => vals::RevOut::REVERSED,\n                false => vals::RevOut::NORMAL,\n            });\n            // configure reverse input\n            w.set_rev_in(match self._config.reverse_in {\n                InputReverseConfig::None => vals::RevIn::NORMAL,\n                InputReverseConfig::Byte => vals::RevIn::BYTE,\n                InputReverseConfig::Halfword => vals::RevIn::HALF_WORD,\n                InputReverseConfig::Word => vals::RevIn::WORD,\n            });\n            // configure the polynomial.\n            #[cfg(crc_v3)]\n            w.set_polysize(match self._config.poly_size {\n                PolySize::Width7 => vals::Polysize::POLYSIZE7,\n                PolySize::Width8 => vals::Polysize::POLYSIZE8,\n                PolySize::Width16 => vals::Polysize::POLYSIZE16,\n                PolySize::Width32 => vals::Polysize::POLYSIZE32,\n            });\n        });\n    }\n\n    /// Read the CRC result value.\n    pub fn read(&self) -> u32 {\n        PAC_CRC.dr32().read()\n    }\n\n    /// Feeds a byte into the CRC peripheral.\n    pub fn feed_byte(&mut self, byte: u8) {\n        PAC_CRC.dr8().write_value(byte);\n    }\n\n    /// Feeds a slice of bytes into the CRC peripheral.\n    pub fn feed_bytes(&mut self, bytes: &[u8]) {\n        for byte in bytes {\n            PAC_CRC.dr8().write_value(*byte);\n        }\n    }\n\n    /// Feeds a halfword into the CRC peripheral.\n    pub fn feed_halfword(&mut self, halfword: u16) {\n        PAC_CRC.dr16().write_value(halfword);\n    }\n\n    /// Feeds a slice of halfwords into the CRC peripheral.\n    pub fn feed_halfwords(&mut self, halfwords: &[u16]) {\n        for halfword in halfwords {\n            PAC_CRC.dr16().write_value(*halfword);\n        }\n    }\n\n    /// Feeds a word into the CRC peripheral.\n    pub fn feed_word(&mut self, word: u32) {\n        PAC_CRC.dr32().write_value(word as u32);\n    }\n\n    /// Feeds a slice of words into the CRC peripheral.\n    pub fn feed_words(&mut self, words: &[u32]) {\n        for word in words {\n            PAC_CRC.dr32().write_value(*word as u32);\n        }\n    }\n}\n"
  },
  {
    "path": "embassy-stm32/src/cryp/mod.rs",
    "content": "//! Crypto Accelerator (CRYP)\n#[cfg(any(cryp_v2, cryp_v3, cryp_v4))]\nuse core::cmp::min;\nuse core::marker::PhantomData;\n\nuse embassy_hal_internal::{Peri, PeripheralType};\nuse embassy_sync::waitqueue::AtomicWaker;\n\nuse crate::dma::{ChannelAndRequest, TransferOptions};\nuse crate::interrupt::typelevel::Interrupt;\nuse crate::mode::{Async, Blocking, Mode};\nuse crate::{interrupt, pac, peripherals, rcc};\n\nconst DES_BLOCK_SIZE: usize = 8; // 64 bits\nconst AES_BLOCK_SIZE: usize = 16; // 128 bits\n\nstatic CRYP_WAKER: AtomicWaker = AtomicWaker::new();\n\n/// CRYP interrupt handler.\npub struct InterruptHandler<T: Instance> {\n    _phantom: PhantomData<T>,\n}\n\nimpl<T: Instance> interrupt::typelevel::Handler<T::Interrupt> for InterruptHandler<T> {\n    unsafe fn on_interrupt() {\n        let bits = T::regs().misr().read();\n        if bits.inmis() {\n            T::regs().imscr().modify(|w| w.set_inim(false));\n            CRYP_WAKER.wake();\n        }\n        if bits.outmis() {\n            T::regs().imscr().modify(|w| w.set_outim(false));\n            CRYP_WAKER.wake();\n        }\n    }\n}\n\n/// This trait encapsulates all cipher-specific behavior/\npub trait Cipher<'c> {\n    /// Processing block size. Determined by the processor and the algorithm.\n    const BLOCK_SIZE: usize;\n\n    /// Indicates whether the cipher requires the application to provide padding.\n    /// If `true`, no partial blocks will be accepted (a panic will occur).\n    const REQUIRES_PADDING: bool = false;\n\n    /// Returns the symmetric key.\n    fn key(&self) -> &[u8];\n\n    /// Returns the initialization vector.\n    fn iv(&self) -> &[u8];\n\n    /// Sets the processor algorithm mode according to the associated cipher.\n    fn set_algomode(&self, p: pac::cryp::Cryp);\n\n    /// Performs any key preparation within the processor, if necessary.\n    fn prepare_key(&self, _p: pac::cryp::Cryp) {}\n\n    /// Performs any cipher-specific initialization.\n    fn init_phase_blocking<T: Instance, M: Mode>(&self, _p: pac::cryp::Cryp, _cryp: &Cryp<T, M>) {}\n\n    /// Performs any cipher-specific initialization.\n    async fn init_phase<T: Instance>(&self, _p: pac::cryp::Cryp, _cryp: &mut Cryp<'_, T, Async>) {}\n\n    /// Called prior to processing the last data block for cipher-specific operations.\n    fn pre_final(&self, _p: pac::cryp::Cryp, _dir: Direction, _padding_len: usize) -> [u32; 4] {\n        return [0; 4];\n    }\n\n    /// Called after processing the last data block for cipher-specific operations.\n    fn post_final_blocking<T: Instance, M: Mode>(\n        &self,\n        _p: pac::cryp::Cryp,\n        _cryp: &Cryp<T, M>,\n        _dir: Direction,\n        _int_data: &mut [u8; AES_BLOCK_SIZE],\n        _temp1: [u32; 4],\n        _padding_mask: [u8; 16],\n    ) {\n    }\n\n    /// Called after processing the last data block for cipher-specific operations.\n    async fn post_final<T: Instance>(\n        &self,\n        _p: pac::cryp::Cryp,\n        _cryp: &mut Cryp<'_, T, Async>,\n        _dir: Direction,\n        _int_data: &mut [u8; AES_BLOCK_SIZE],\n        _temp1: [u32; 4],\n        _padding_mask: [u8; 16],\n    ) {\n    }\n\n    /// Returns the AAD header block as required by the cipher.\n    fn get_header_block(&self) -> &[u8] {\n        return [0; 0].as_slice();\n    }\n}\n\n/// This trait enables restriction of ciphers to specific key sizes.\npub trait CipherSized {}\n\n/// This trait enables restriction of initialization vectors to sizes compatibile with a cipher mode.\npub trait IVSized {}\n\n/// This trait enables restriction of a header phase to authenticated ciphers only.\npub trait CipherAuthenticated<const TAG_SIZE: usize> {\n    /// Defines the authentication tag size.\n    const TAG_SIZE: usize = TAG_SIZE;\n}\n\n/// TDES-ECB Cipher Mode\npub struct TdesEcb<'c, const KEY_SIZE: usize> {\n    iv: &'c [u8; 0],\n    key: &'c [u8; KEY_SIZE],\n}\n\nimpl<'c, const KEY_SIZE: usize> TdesEcb<'c, KEY_SIZE> {\n    /// Constructs a new AES-ECB cipher for a cryptographic operation.\n    pub fn new(key: &'c [u8; KEY_SIZE]) -> Self {\n        return Self { key: key, iv: &[0; 0] };\n    }\n}\n\nimpl<'c, const KEY_SIZE: usize> Cipher<'c> for TdesEcb<'c, KEY_SIZE> {\n    const BLOCK_SIZE: usize = DES_BLOCK_SIZE;\n    const REQUIRES_PADDING: bool = true;\n\n    fn key(&self) -> &'c [u8] {\n        self.key\n    }\n\n    fn iv(&self) -> &'c [u8] {\n        self.iv\n    }\n\n    fn set_algomode(&self, p: pac::cryp::Cryp) {\n        #[cfg(cryp_v1)]\n        {\n            p.cr().modify(|w| w.set_algomode(0));\n        }\n        #[cfg(any(cryp_v2, cryp_v3, cryp_v4))]\n        {\n            p.cr().modify(|w| w.set_algomode0(0));\n            p.cr().modify(|w| w.set_algomode3(false));\n        }\n    }\n}\n\nimpl<'c> CipherSized for TdesEcb<'c, { 112 / 8 }> {}\nimpl<'c> CipherSized for TdesEcb<'c, { 168 / 8 }> {}\nimpl<'c, const KEY_SIZE: usize> IVSized for TdesEcb<'c, KEY_SIZE> {}\n\n/// TDES-CBC Cipher Mode\npub struct TdesCbc<'c, const KEY_SIZE: usize> {\n    iv: &'c [u8; 8],\n    key: &'c [u8; KEY_SIZE],\n}\n\nimpl<'c, const KEY_SIZE: usize> TdesCbc<'c, KEY_SIZE> {\n    /// Constructs a new TDES-CBC cipher for a cryptographic operation.\n    pub fn new(key: &'c [u8; KEY_SIZE], iv: &'c [u8; 8]) -> Self {\n        return Self { key: key, iv: iv };\n    }\n}\n\nimpl<'c, const KEY_SIZE: usize> Cipher<'c> for TdesCbc<'c, KEY_SIZE> {\n    const BLOCK_SIZE: usize = DES_BLOCK_SIZE;\n    const REQUIRES_PADDING: bool = true;\n\n    fn key(&self) -> &'c [u8] {\n        self.key\n    }\n\n    fn iv(&self) -> &'c [u8] {\n        self.iv\n    }\n\n    fn set_algomode(&self, p: pac::cryp::Cryp) {\n        #[cfg(cryp_v1)]\n        {\n            p.cr().modify(|w| w.set_algomode(1));\n        }\n        #[cfg(any(cryp_v2, cryp_v3, cryp_v4))]\n        {\n            p.cr().modify(|w| w.set_algomode0(1));\n            p.cr().modify(|w| w.set_algomode3(false));\n        }\n    }\n}\n\nimpl<'c> CipherSized for TdesCbc<'c, { 112 / 8 }> {}\nimpl<'c> CipherSized for TdesCbc<'c, { 168 / 8 }> {}\nimpl<'c, const KEY_SIZE: usize> IVSized for TdesCbc<'c, KEY_SIZE> {}\n\n/// DES-ECB Cipher Mode\npub struct DesEcb<'c, const KEY_SIZE: usize> {\n    iv: &'c [u8; 0],\n    key: &'c [u8; KEY_SIZE],\n}\n\nimpl<'c, const KEY_SIZE: usize> DesEcb<'c, KEY_SIZE> {\n    /// Constructs a new AES-ECB cipher for a cryptographic operation.\n    pub fn new(key: &'c [u8; KEY_SIZE]) -> Self {\n        return Self { key: key, iv: &[0; 0] };\n    }\n}\n\nimpl<'c, const KEY_SIZE: usize> Cipher<'c> for DesEcb<'c, KEY_SIZE> {\n    const BLOCK_SIZE: usize = DES_BLOCK_SIZE;\n    const REQUIRES_PADDING: bool = true;\n\n    fn key(&self) -> &'c [u8] {\n        self.key\n    }\n\n    fn iv(&self) -> &'c [u8] {\n        self.iv\n    }\n\n    fn set_algomode(&self, p: pac::cryp::Cryp) {\n        #[cfg(cryp_v1)]\n        {\n            p.cr().modify(|w| w.set_algomode(2));\n        }\n        #[cfg(any(cryp_v2, cryp_v3, cryp_v4))]\n        {\n            p.cr().modify(|w| w.set_algomode0(2));\n            p.cr().modify(|w| w.set_algomode3(false));\n        }\n    }\n}\n\nimpl<'c> CipherSized for DesEcb<'c, { 56 / 8 }> {}\nimpl<'c, const KEY_SIZE: usize> IVSized for DesEcb<'c, KEY_SIZE> {}\n\n/// DES-CBC Cipher Mode\npub struct DesCbc<'c, const KEY_SIZE: usize> {\n    iv: &'c [u8; 8],\n    key: &'c [u8; KEY_SIZE],\n}\n\nimpl<'c, const KEY_SIZE: usize> DesCbc<'c, KEY_SIZE> {\n    /// Constructs a new AES-CBC cipher for a cryptographic operation.\n    pub fn new(key: &'c [u8; KEY_SIZE], iv: &'c [u8; 8]) -> Self {\n        return Self { key: key, iv: iv };\n    }\n}\n\nimpl<'c, const KEY_SIZE: usize> Cipher<'c> for DesCbc<'c, KEY_SIZE> {\n    const BLOCK_SIZE: usize = DES_BLOCK_SIZE;\n    const REQUIRES_PADDING: bool = true;\n\n    fn key(&self) -> &'c [u8] {\n        self.key\n    }\n\n    fn iv(&self) -> &'c [u8] {\n        self.iv\n    }\n\n    fn set_algomode(&self, p: pac::cryp::Cryp) {\n        #[cfg(cryp_v1)]\n        {\n            p.cr().modify(|w| w.set_algomode(3));\n        }\n        #[cfg(any(cryp_v2, cryp_v3, cryp_v4))]\n        {\n            p.cr().modify(|w| w.set_algomode0(3));\n            p.cr().modify(|w| w.set_algomode3(false));\n        }\n    }\n}\n\nimpl<'c> CipherSized for DesCbc<'c, { 56 / 8 }> {}\nimpl<'c, const KEY_SIZE: usize> IVSized for DesCbc<'c, KEY_SIZE> {}\n\n/// AES-ECB Cipher Mode\npub struct AesEcb<'c, const KEY_SIZE: usize> {\n    iv: &'c [u8; 0],\n    key: &'c [u8; KEY_SIZE],\n}\n\nimpl<'c, const KEY_SIZE: usize> AesEcb<'c, KEY_SIZE> {\n    /// Constructs a new AES-ECB cipher for a cryptographic operation.\n    pub fn new(key: &'c [u8; KEY_SIZE]) -> Self {\n        return Self { key: key, iv: &[0; 0] };\n    }\n}\n\nimpl<'c, const KEY_SIZE: usize> Cipher<'c> for AesEcb<'c, KEY_SIZE> {\n    const BLOCK_SIZE: usize = AES_BLOCK_SIZE;\n    const REQUIRES_PADDING: bool = true;\n\n    fn key(&self) -> &'c [u8] {\n        self.key\n    }\n\n    fn iv(&self) -> &'c [u8] {\n        self.iv\n    }\n\n    fn prepare_key(&self, p: pac::cryp::Cryp) {\n        #[cfg(cryp_v1)]\n        {\n            p.cr().modify(|w| w.set_algomode(7));\n        }\n        #[cfg(any(cryp_v2, cryp_v3, cryp_v4))]\n        {\n            p.cr().modify(|w| w.set_algomode0(7));\n            p.cr().modify(|w| w.set_algomode3(false));\n        }\n        p.cr().modify(|w| w.set_crypen(true));\n        while p.sr().read().busy() {}\n    }\n\n    fn set_algomode(&self, p: pac::cryp::Cryp) {\n        #[cfg(cryp_v1)]\n        {\n            p.cr().modify(|w| w.set_algomode(2));\n        }\n        #[cfg(any(cryp_v2, cryp_v3, cryp_v4))]\n        {\n            p.cr().modify(|w| w.set_algomode0(2));\n            p.cr().modify(|w| w.set_algomode3(false));\n        }\n    }\n}\n\nimpl<'c> CipherSized for AesEcb<'c, { 128 / 8 }> {}\nimpl<'c> CipherSized for AesEcb<'c, { 192 / 8 }> {}\nimpl<'c> CipherSized for AesEcb<'c, { 256 / 8 }> {}\nimpl<'c, const KEY_SIZE: usize> IVSized for AesEcb<'c, KEY_SIZE> {}\n\n/// AES-CBC Cipher Mode\npub struct AesCbc<'c, const KEY_SIZE: usize> {\n    iv: &'c [u8; 16],\n    key: &'c [u8; KEY_SIZE],\n}\n\nimpl<'c, const KEY_SIZE: usize> AesCbc<'c, KEY_SIZE> {\n    /// Constructs a new AES-CBC cipher for a cryptographic operation.\n    pub fn new(key: &'c [u8; KEY_SIZE], iv: &'c [u8; 16]) -> Self {\n        return Self { key: key, iv: iv };\n    }\n}\n\nimpl<'c, const KEY_SIZE: usize> Cipher<'c> for AesCbc<'c, KEY_SIZE> {\n    const BLOCK_SIZE: usize = AES_BLOCK_SIZE;\n    const REQUIRES_PADDING: bool = true;\n\n    fn key(&self) -> &'c [u8] {\n        self.key\n    }\n\n    fn iv(&self) -> &'c [u8] {\n        self.iv\n    }\n\n    fn prepare_key(&self, p: pac::cryp::Cryp) {\n        #[cfg(cryp_v1)]\n        {\n            p.cr().modify(|w| w.set_algomode(7));\n        }\n        #[cfg(any(cryp_v2, cryp_v3, cryp_v4))]\n        {\n            p.cr().modify(|w| w.set_algomode0(7));\n            p.cr().modify(|w| w.set_algomode3(false));\n        }\n        p.cr().modify(|w| w.set_crypen(true));\n        while p.sr().read().busy() {}\n    }\n\n    fn set_algomode(&self, p: pac::cryp::Cryp) {\n        #[cfg(cryp_v1)]\n        {\n            p.cr().modify(|w| w.set_algomode(5));\n        }\n        #[cfg(any(cryp_v2, cryp_v3, cryp_v4))]\n        {\n            p.cr().modify(|w| w.set_algomode0(5));\n            p.cr().modify(|w| w.set_algomode3(false));\n        }\n    }\n}\n\nimpl<'c> CipherSized for AesCbc<'c, { 128 / 8 }> {}\nimpl<'c> CipherSized for AesCbc<'c, { 192 / 8 }> {}\nimpl<'c> CipherSized for AesCbc<'c, { 256 / 8 }> {}\nimpl<'c, const KEY_SIZE: usize> IVSized for AesCbc<'c, KEY_SIZE> {}\n\n/// AES-CTR Cipher Mode\npub struct AesCtr<'c, const KEY_SIZE: usize> {\n    iv: &'c [u8; 16],\n    key: &'c [u8; KEY_SIZE],\n}\n\nimpl<'c, const KEY_SIZE: usize> AesCtr<'c, KEY_SIZE> {\n    /// Constructs a new AES-CTR cipher for a cryptographic operation.\n    pub fn new(key: &'c [u8; KEY_SIZE], iv: &'c [u8; 16]) -> Self {\n        return Self { key: key, iv: iv };\n    }\n}\n\nimpl<'c, const KEY_SIZE: usize> Cipher<'c> for AesCtr<'c, KEY_SIZE> {\n    const BLOCK_SIZE: usize = AES_BLOCK_SIZE;\n\n    fn key(&self) -> &'c [u8] {\n        self.key\n    }\n\n    fn iv(&self) -> &'c [u8] {\n        self.iv\n    }\n\n    fn set_algomode(&self, p: pac::cryp::Cryp) {\n        #[cfg(cryp_v1)]\n        {\n            p.cr().modify(|w| w.set_algomode(6));\n        }\n        #[cfg(any(cryp_v2, cryp_v3, cryp_v4))]\n        {\n            p.cr().modify(|w| w.set_algomode0(6));\n            p.cr().modify(|w| w.set_algomode3(false));\n        }\n    }\n}\n\nimpl<'c> CipherSized for AesCtr<'c, { 128 / 8 }> {}\nimpl<'c> CipherSized for AesCtr<'c, { 192 / 8 }> {}\nimpl<'c> CipherSized for AesCtr<'c, { 256 / 8 }> {}\nimpl<'c, const KEY_SIZE: usize> IVSized for AesCtr<'c, KEY_SIZE> {}\n\n#[cfg(any(cryp_v2, cryp_v3, cryp_v4))]\n///AES-GCM Cipher Mode\npub struct AesGcm<'c, const KEY_SIZE: usize> {\n    iv: [u8; 16],\n    key: &'c [u8; KEY_SIZE],\n}\n\n#[cfg(any(cryp_v2, cryp_v3, cryp_v4))]\nimpl<'c, const KEY_SIZE: usize> AesGcm<'c, KEY_SIZE> {\n    /// Constucts a new AES-GCM cipher for a cryptographic operation.\n    pub fn new(key: &'c [u8; KEY_SIZE], iv: &'c [u8; 12]) -> Self {\n        let mut new_gcm = Self { key: key, iv: [0; 16] };\n        new_gcm.iv[..12].copy_from_slice(iv);\n        new_gcm.iv[15] = 2;\n        new_gcm\n    }\n}\n\n#[cfg(any(cryp_v2, cryp_v3, cryp_v4))]\nimpl<'c, const KEY_SIZE: usize> Cipher<'c> for AesGcm<'c, KEY_SIZE> {\n    const BLOCK_SIZE: usize = AES_BLOCK_SIZE;\n\n    fn key(&self) -> &'c [u8] {\n        self.key\n    }\n\n    fn iv(&self) -> &[u8] {\n        self.iv.as_slice()\n    }\n\n    fn set_algomode(&self, p: pac::cryp::Cryp) {\n        p.cr().modify(|w| w.set_algomode0(0));\n        p.cr().modify(|w| w.set_algomode3(true));\n    }\n\n    fn init_phase_blocking<T: Instance, M: Mode>(&self, p: pac::cryp::Cryp, _cryp: &Cryp<T, M>) {\n        p.cr().modify(|w| w.set_gcm_ccmph(0));\n        p.cr().modify(|w| w.set_crypen(true));\n        while p.cr().read().crypen() {}\n    }\n\n    async fn init_phase<T: Instance>(&self, p: pac::cryp::Cryp, _cryp: &mut Cryp<'_, T, Async>) {\n        p.cr().modify(|w| w.set_gcm_ccmph(0));\n        p.cr().modify(|w| w.set_crypen(true));\n        while p.cr().read().crypen() {}\n    }\n\n    #[cfg(cryp_v2)]\n    fn pre_final(&self, p: pac::cryp::Cryp, dir: Direction, _padding_len: usize) -> [u32; 4] {\n        //Handle special GCM partial block process.\n        if dir == Direction::Encrypt {\n            p.cr().modify(|w| w.set_crypen(false));\n            p.cr().modify(|w| w.set_algomode3(false));\n            p.cr().modify(|w| w.set_algomode0(6));\n            let iv1r = p.csgcmccmr(7).read() - 1;\n            p.init(1).ivrr().write_value(iv1r);\n            p.cr().modify(|w| w.set_crypen(true));\n        }\n        [0; 4]\n    }\n\n    #[cfg(any(cryp_v3, cryp_v4))]\n    fn pre_final(&self, p: pac::cryp::Cryp, _dir: Direction, padding_len: usize) -> [u32; 4] {\n        //Handle special GCM partial block process.\n        p.cr().modify(|w| w.set_npblb(padding_len as u8));\n        [0; 4]\n    }\n\n    #[cfg(cryp_v2)]\n    fn post_final_blocking<T: Instance, M: Mode>(\n        &self,\n        p: pac::cryp::Cryp,\n        cryp: &Cryp<T, M>,\n        dir: Direction,\n        int_data: &mut [u8; AES_BLOCK_SIZE],\n        _temp1: [u32; 4],\n        padding_mask: [u8; AES_BLOCK_SIZE],\n    ) {\n        if dir == Direction::Encrypt {\n            //Handle special GCM partial block process.\n            p.cr().modify(|w| w.set_crypen(false));\n            p.cr().modify(|w| w.set_algomode3(true));\n            p.cr().modify(|w| w.set_algomode0(0));\n            for i in 0..AES_BLOCK_SIZE {\n                int_data[i] = int_data[i] & padding_mask[i];\n            }\n            p.cr().modify(|w| w.set_crypen(true));\n            p.cr().modify(|w| w.set_gcm_ccmph(3));\n\n            cryp.write_bytes_blocking(Self::BLOCK_SIZE, int_data);\n            cryp.read_bytes_blocking(Self::BLOCK_SIZE, int_data);\n        }\n    }\n\n    #[cfg(cryp_v2)]\n    async fn post_final<T: Instance>(\n        &self,\n        p: pac::cryp::Cryp,\n        cryp: &mut Cryp<'_, T, Async>,\n        dir: Direction,\n        int_data: &mut [u8; AES_BLOCK_SIZE],\n        _temp1: [u32; 4],\n        padding_mask: [u8; AES_BLOCK_SIZE],\n    ) {\n        if dir == Direction::Encrypt {\n            // Handle special GCM partial block process.\n            p.cr().modify(|w| w.set_crypen(false));\n            p.cr().modify(|w| w.set_algomode3(true));\n            p.cr().modify(|w| w.set_algomode0(0));\n            for i in 0..AES_BLOCK_SIZE {\n                int_data[i] = int_data[i] & padding_mask[i];\n            }\n            p.cr().modify(|w| w.set_crypen(true));\n            p.cr().modify(|w| w.set_gcm_ccmph(3));\n\n            let mut out_data: [u8; AES_BLOCK_SIZE] = [0; AES_BLOCK_SIZE];\n\n            let read = Cryp::<T, Async>::read_bytes(cryp.outdma.as_mut().unwrap(), Self::BLOCK_SIZE, &mut out_data);\n            let write = Cryp::<T, Async>::write_bytes(cryp.indma.as_mut().unwrap(), Self::BLOCK_SIZE, int_data);\n\n            embassy_futures::join::join(read, write).await;\n\n            int_data.copy_from_slice(&out_data);\n        }\n    }\n}\n\n#[cfg(any(cryp_v2, cryp_v3, cryp_v4))]\nimpl<'c> CipherSized for AesGcm<'c, { 128 / 8 }> {}\n#[cfg(any(cryp_v2, cryp_v3, cryp_v4))]\nimpl<'c> CipherSized for AesGcm<'c, { 192 / 8 }> {}\n#[cfg(any(cryp_v2, cryp_v3, cryp_v4))]\nimpl<'c> CipherSized for AesGcm<'c, { 256 / 8 }> {}\n#[cfg(any(cryp_v2, cryp_v3, cryp_v4))]\nimpl<'c, const KEY_SIZE: usize> CipherAuthenticated<16> for AesGcm<'c, KEY_SIZE> {}\n#[cfg(any(cryp_v2, cryp_v3, cryp_v4))]\nimpl<'c, const KEY_SIZE: usize> IVSized for AesGcm<'c, KEY_SIZE> {}\n\n#[cfg(any(cryp_v2, cryp_v3, cryp_v4))]\n/// AES-GMAC Cipher Mode\npub struct AesGmac<'c, const KEY_SIZE: usize> {\n    iv: [u8; 16],\n    key: &'c [u8; KEY_SIZE],\n}\n\n#[cfg(any(cryp_v2, cryp_v3, cryp_v4))]\nimpl<'c, const KEY_SIZE: usize> AesGmac<'c, KEY_SIZE> {\n    /// Constructs a new AES-GMAC cipher for a cryptographic operation.\n    pub fn new(key: &'c [u8; KEY_SIZE], iv: &'c [u8; 12]) -> Self {\n        let mut new_gmac = Self { key: key, iv: [0; 16] };\n        new_gmac.iv[..12].copy_from_slice(iv);\n        new_gmac.iv[15] = 2;\n        new_gmac\n    }\n}\n\n#[cfg(any(cryp_v2, cryp_v3, cryp_v4))]\nimpl<'c, const KEY_SIZE: usize> Cipher<'c> for AesGmac<'c, KEY_SIZE> {\n    const BLOCK_SIZE: usize = AES_BLOCK_SIZE;\n\n    fn key(&self) -> &'c [u8] {\n        self.key\n    }\n\n    fn iv(&self) -> &[u8] {\n        self.iv.as_slice()\n    }\n\n    fn set_algomode(&self, p: pac::cryp::Cryp) {\n        p.cr().modify(|w| w.set_algomode0(0));\n        p.cr().modify(|w| w.set_algomode3(true));\n    }\n\n    fn init_phase_blocking<T: Instance, M: Mode>(&self, p: pac::cryp::Cryp, _cryp: &Cryp<T, M>) {\n        p.cr().modify(|w| w.set_gcm_ccmph(0));\n        p.cr().modify(|w| w.set_crypen(true));\n        while p.cr().read().crypen() {}\n    }\n\n    async fn init_phase<T: Instance>(&self, p: pac::cryp::Cryp, _cryp: &mut Cryp<'_, T, Async>) {\n        p.cr().modify(|w| w.set_gcm_ccmph(0));\n        p.cr().modify(|w| w.set_crypen(true));\n        while p.cr().read().crypen() {}\n    }\n\n    #[cfg(cryp_v2)]\n    fn pre_final(&self, p: pac::cryp::Cryp, dir: Direction, _padding_len: usize) -> [u32; 4] {\n        //Handle special GCM partial block process.\n        if dir == Direction::Encrypt {\n            p.cr().modify(|w| w.set_crypen(false));\n            p.cr().modify(|w| w.set_algomode3(false));\n            p.cr().modify(|w| w.set_algomode0(6));\n            let iv1r = p.csgcmccmr(7).read() - 1;\n            p.init(1).ivrr().write_value(iv1r);\n            p.cr().modify(|w| w.set_crypen(true));\n        }\n        [0; 4]\n    }\n\n    #[cfg(any(cryp_v3, cryp_v4))]\n    fn pre_final(&self, p: pac::cryp::Cryp, _dir: Direction, padding_len: usize) -> [u32; 4] {\n        //Handle special GCM partial block process.\n        p.cr().modify(|w| w.set_npblb(padding_len as u8));\n        [0; 4]\n    }\n\n    #[cfg(cryp_v2)]\n    fn post_final_blocking<T: Instance, M: Mode>(\n        &self,\n        p: pac::cryp::Cryp,\n        cryp: &Cryp<T, M>,\n        dir: Direction,\n        int_data: &mut [u8; AES_BLOCK_SIZE],\n        _temp1: [u32; 4],\n        padding_mask: [u8; AES_BLOCK_SIZE],\n    ) {\n        if dir == Direction::Encrypt {\n            //Handle special GCM partial block process.\n            p.cr().modify(|w| w.set_crypen(false));\n            p.cr().modify(|w| w.set_algomode3(true));\n            p.cr().modify(|w| w.set_algomode0(0));\n            for i in 0..AES_BLOCK_SIZE {\n                int_data[i] = int_data[i] & padding_mask[i];\n            }\n            p.cr().modify(|w| w.set_crypen(true));\n            p.cr().modify(|w| w.set_gcm_ccmph(3));\n\n            cryp.write_bytes_blocking(Self::BLOCK_SIZE, int_data);\n            cryp.read_bytes_blocking(Self::BLOCK_SIZE, int_data);\n        }\n    }\n\n    #[cfg(cryp_v2)]\n    async fn post_final<T: Instance>(\n        &self,\n        p: pac::cryp::Cryp,\n        cryp: &mut Cryp<'_, T, Async>,\n        dir: Direction,\n        int_data: &mut [u8; AES_BLOCK_SIZE],\n        _temp1: [u32; 4],\n        padding_mask: [u8; AES_BLOCK_SIZE],\n    ) {\n        if dir == Direction::Encrypt {\n            // Handle special GCM partial block process.\n            p.cr().modify(|w| w.set_crypen(false));\n            p.cr().modify(|w| w.set_algomode3(true));\n            p.cr().modify(|w| w.set_algomode0(0));\n            for i in 0..AES_BLOCK_SIZE {\n                int_data[i] = int_data[i] & padding_mask[i];\n            }\n            p.cr().modify(|w| w.set_crypen(true));\n            p.cr().modify(|w| w.set_gcm_ccmph(3));\n\n            let mut out_data: [u8; AES_BLOCK_SIZE] = [0; AES_BLOCK_SIZE];\n\n            let read = Cryp::<T, Async>::read_bytes(cryp.outdma.as_mut().unwrap(), Self::BLOCK_SIZE, &mut out_data);\n            let write = Cryp::<T, Async>::write_bytes(cryp.indma.as_mut().unwrap(), Self::BLOCK_SIZE, int_data);\n\n            embassy_futures::join::join(read, write).await;\n        }\n    }\n}\n\n#[cfg(any(cryp_v2, cryp_v3, cryp_v4))]\nimpl<'c> CipherSized for AesGmac<'c, { 128 / 8 }> {}\n#[cfg(any(cryp_v2, cryp_v3, cryp_v4))]\nimpl<'c> CipherSized for AesGmac<'c, { 192 / 8 }> {}\n#[cfg(any(cryp_v2, cryp_v3, cryp_v4))]\nimpl<'c> CipherSized for AesGmac<'c, { 256 / 8 }> {}\n#[cfg(any(cryp_v2, cryp_v3, cryp_v4))]\nimpl<'c, const KEY_SIZE: usize> CipherAuthenticated<16> for AesGmac<'c, KEY_SIZE> {}\n#[cfg(any(cryp_v2, cryp_v3, cryp_v4))]\nimpl<'c, const KEY_SIZE: usize> IVSized for AesGmac<'c, KEY_SIZE> {}\n\n#[cfg(any(cryp_v2, cryp_v3, cryp_v4))]\n/// AES-CCM Cipher Mode\npub struct AesCcm<'c, const KEY_SIZE: usize, const TAG_SIZE: usize, const IV_SIZE: usize> {\n    key: &'c [u8; KEY_SIZE],\n    aad_header: [u8; 6],\n    aad_header_len: usize,\n    block0: [u8; 16],\n    ctr: [u8; 16],\n}\n\n#[cfg(any(cryp_v2, cryp_v3, cryp_v4))]\nimpl<'c, const KEY_SIZE: usize, const TAG_SIZE: usize, const IV_SIZE: usize> AesCcm<'c, KEY_SIZE, TAG_SIZE, IV_SIZE> {\n    /// Constructs a new AES-CCM cipher for a cryptographic operation.\n    pub fn new(key: &'c [u8; KEY_SIZE], iv: &'c [u8; IV_SIZE], aad_len: usize, payload_len: usize) -> Self {\n        let mut aad_header: [u8; 6] = [0; 6];\n        let mut aad_header_len = 0;\n        let mut block0: [u8; 16] = [0; 16];\n        if aad_len != 0 {\n            if aad_len < 65280 {\n                aad_header[0] = (aad_len >> 8) as u8 & 0xFF;\n                aad_header[1] = aad_len as u8 & 0xFF;\n                aad_header_len = 2;\n            } else {\n                aad_header[0] = 0xFF;\n                aad_header[1] = 0xFE;\n                let aad_len_bytes: [u8; 4] = (aad_len as u32).to_be_bytes();\n                aad_header[2] = aad_len_bytes[0];\n                aad_header[3] = aad_len_bytes[1];\n                aad_header[4] = aad_len_bytes[2];\n                aad_header[5] = aad_len_bytes[3];\n                aad_header_len = 6;\n            }\n        }\n        let total_aad_len = aad_header_len + aad_len;\n        let mut aad_padding_len = 16 - (total_aad_len % 16);\n        if aad_padding_len == 16 {\n            aad_padding_len = 0;\n        }\n        aad_header_len += aad_padding_len;\n        let total_aad_len_padded = aad_header_len + aad_len;\n        if total_aad_len_padded > 0 {\n            block0[0] = 0x40;\n        }\n        block0[0] |= ((((TAG_SIZE as u8) - 2) >> 1) & 0x07) << 3;\n        block0[0] |= ((15 - (iv.len() as u8)) - 1) & 0x07;\n        block0[1..1 + iv.len()].copy_from_slice(iv);\n        let payload_len_bytes: [u8; 4] = (payload_len as u32).to_be_bytes();\n        if iv.len() <= 11 {\n            block0[12] = payload_len_bytes[0];\n        } else if payload_len_bytes[0] > 0 {\n            panic!(\"Message is too large for given IV size.\");\n        }\n        if iv.len() <= 12 {\n            block0[13] = payload_len_bytes[1];\n        } else if payload_len_bytes[1] > 0 {\n            panic!(\"Message is too large for given IV size.\");\n        }\n        block0[14] = payload_len_bytes[2];\n        block0[15] = payload_len_bytes[3];\n        let mut ctr: [u8; 16] = [0; 16];\n        ctr[0] = block0[0] & 0x07;\n        ctr[1..1 + iv.len()].copy_from_slice(&block0[1..1 + iv.len()]);\n        ctr[15] = 0x01;\n\n        return Self {\n            key: key,\n            aad_header: aad_header,\n            aad_header_len: aad_header_len,\n            block0: block0,\n            ctr: ctr,\n        };\n    }\n}\n\n#[cfg(any(cryp_v2, cryp_v3, cryp_v4))]\nimpl<'c, const KEY_SIZE: usize, const TAG_SIZE: usize, const IV_SIZE: usize> Cipher<'c>\n    for AesCcm<'c, KEY_SIZE, TAG_SIZE, IV_SIZE>\n{\n    const BLOCK_SIZE: usize = AES_BLOCK_SIZE;\n\n    fn key(&self) -> &'c [u8] {\n        self.key\n    }\n\n    fn iv(&self) -> &[u8] {\n        self.ctr.as_slice()\n    }\n\n    fn set_algomode(&self, p: pac::cryp::Cryp) {\n        p.cr().modify(|w| w.set_algomode0(1));\n        p.cr().modify(|w| w.set_algomode3(true));\n    }\n\n    fn init_phase_blocking<T: Instance, M: Mode>(&self, p: pac::cryp::Cryp, cryp: &Cryp<T, M>) {\n        p.cr().modify(|w| w.set_gcm_ccmph(0));\n\n        cryp.write_bytes_blocking(Self::BLOCK_SIZE, &self.block0);\n\n        p.cr().modify(|w| w.set_crypen(true));\n        while p.cr().read().crypen() {}\n    }\n\n    async fn init_phase<T: Instance>(&self, p: pac::cryp::Cryp, cryp: &mut Cryp<'_, T, Async>) {\n        p.cr().modify(|w| w.set_gcm_ccmph(0));\n\n        Cryp::<T, Async>::write_bytes(cryp.indma.as_mut().unwrap(), Self::BLOCK_SIZE, &self.block0).await;\n\n        p.cr().modify(|w| w.set_crypen(true));\n        while p.cr().read().crypen() {}\n    }\n\n    fn get_header_block(&self) -> &[u8] {\n        return &self.aad_header[0..self.aad_header_len];\n    }\n\n    #[cfg(cryp_v2)]\n    fn pre_final(&self, p: pac::cryp::Cryp, dir: Direction, _padding_len: usize) -> [u32; 4] {\n        //Handle special CCM partial block process.\n        let mut temp1 = [0; 4];\n        if dir == Direction::Decrypt {\n            p.cr().modify(|w| w.set_crypen(false));\n            let iv1temp = p.init(1).ivrr().read();\n            temp1[0] = p.csgcmccmr(0).read().swap_bytes();\n            temp1[1] = p.csgcmccmr(1).read().swap_bytes();\n            temp1[2] = p.csgcmccmr(2).read().swap_bytes();\n            temp1[3] = p.csgcmccmr(3).read().swap_bytes();\n            p.init(1).ivrr().write_value(iv1temp);\n            p.cr().modify(|w| w.set_algomode3(false));\n            p.cr().modify(|w| w.set_algomode0(6));\n            p.cr().modify(|w| w.set_crypen(true));\n        }\n        return temp1;\n    }\n\n    #[cfg(any(cryp_v3, cryp_v4))]\n    fn pre_final(&self, p: pac::cryp::Cryp, _dir: Direction, padding_len: usize) -> [u32; 4] {\n        //Handle special GCM partial block process.\n        p.cr().modify(|w| w.set_npblb(padding_len as u8));\n        [0; 4]\n    }\n\n    #[cfg(cryp_v2)]\n    fn post_final_blocking<T: Instance, M: Mode>(\n        &self,\n        p: pac::cryp::Cryp,\n        cryp: &Cryp<T, M>,\n        dir: Direction,\n        int_data: &mut [u8; AES_BLOCK_SIZE],\n        temp1: [u32; 4],\n        padding_mask: [u8; 16],\n    ) {\n        if dir == Direction::Decrypt {\n            //Handle special CCM partial block process.\n            let mut temp2 = [0; 4];\n            temp2[0] = p.csgcmccmr(0).read().swap_bytes();\n            temp2[1] = p.csgcmccmr(1).read().swap_bytes();\n            temp2[2] = p.csgcmccmr(2).read().swap_bytes();\n            temp2[3] = p.csgcmccmr(3).read().swap_bytes();\n            p.cr().modify(|w| w.set_algomode3(true));\n            p.cr().modify(|w| w.set_algomode0(1));\n            p.cr().modify(|w| w.set_gcm_ccmph(3));\n            // Header phase\n            p.cr().modify(|w| w.set_gcm_ccmph(1));\n            for i in 0..AES_BLOCK_SIZE {\n                int_data[i] = int_data[i] & padding_mask[i];\n            }\n            let mut in_data: [u32; 4] = [0; 4];\n            for i in 0..in_data.len() {\n                let mut int_bytes: [u8; 4] = [0; 4];\n                int_bytes.copy_from_slice(&int_data[(i * 4)..(i * 4) + 4]);\n                let int_word = u32::from_le_bytes(int_bytes);\n                in_data[i] = int_word;\n                in_data[i] = in_data[i] ^ temp1[i] ^ temp2[i];\n            }\n            cryp.write_words_blocking(Self::BLOCK_SIZE, &in_data);\n        }\n    }\n\n    #[cfg(cryp_v2)]\n    async fn post_final<T: Instance>(\n        &self,\n        p: pac::cryp::Cryp,\n        cryp: &mut Cryp<'_, T, Async>,\n        dir: Direction,\n        int_data: &mut [u8; AES_BLOCK_SIZE],\n        temp1: [u32; 4],\n        padding_mask: [u8; 16],\n    ) {\n        if dir == Direction::Decrypt {\n            //Handle special CCM partial block process.\n            let mut temp2 = [0; 4];\n            temp2[0] = p.csgcmccmr(0).read().swap_bytes();\n            temp2[1] = p.csgcmccmr(1).read().swap_bytes();\n            temp2[2] = p.csgcmccmr(2).read().swap_bytes();\n            temp2[3] = p.csgcmccmr(3).read().swap_bytes();\n            p.cr().modify(|w| w.set_algomode3(true));\n            p.cr().modify(|w| w.set_algomode0(1));\n            p.cr().modify(|w| w.set_gcm_ccmph(3));\n            // Header phase\n            p.cr().modify(|w| w.set_gcm_ccmph(1));\n            for i in 0..AES_BLOCK_SIZE {\n                int_data[i] = int_data[i] & padding_mask[i];\n            }\n            let mut in_data: [u32; 4] = [0; 4];\n            for i in 0..in_data.len() {\n                let mut int_bytes: [u8; 4] = [0; 4];\n                int_bytes.copy_from_slice(&int_data[(i * 4)..(i * 4) + 4]);\n                let int_word = u32::from_le_bytes(int_bytes);\n                in_data[i] = int_word;\n                in_data[i] = in_data[i] ^ temp1[i] ^ temp2[i];\n            }\n            Cryp::<T, Async>::write_words(cryp.indma.as_mut().unwrap(), Self::BLOCK_SIZE, &in_data).await;\n        }\n    }\n}\n\n#[cfg(any(cryp_v2, cryp_v3, cryp_v4))]\nimpl<'c, const TAG_SIZE: usize, const IV_SIZE: usize> CipherSized for AesCcm<'c, { 128 / 8 }, TAG_SIZE, IV_SIZE> {}\n#[cfg(any(cryp_v2, cryp_v3, cryp_v4))]\nimpl<'c, const TAG_SIZE: usize, const IV_SIZE: usize> CipherSized for AesCcm<'c, { 192 / 8 }, TAG_SIZE, IV_SIZE> {}\n#[cfg(any(cryp_v2, cryp_v3, cryp_v4))]\nimpl<'c, const TAG_SIZE: usize, const IV_SIZE: usize> CipherSized for AesCcm<'c, { 256 / 8 }, TAG_SIZE, IV_SIZE> {}\n#[cfg(any(cryp_v2, cryp_v3, cryp_v4))]\nimpl<'c, const KEY_SIZE: usize, const IV_SIZE: usize> CipherAuthenticated<4> for AesCcm<'c, KEY_SIZE, 4, IV_SIZE> {}\n#[cfg(any(cryp_v2, cryp_v3, cryp_v4))]\nimpl<'c, const KEY_SIZE: usize, const IV_SIZE: usize> CipherAuthenticated<6> for AesCcm<'c, KEY_SIZE, 6, IV_SIZE> {}\n#[cfg(any(cryp_v2, cryp_v3, cryp_v4))]\nimpl<'c, const KEY_SIZE: usize, const IV_SIZE: usize> CipherAuthenticated<8> for AesCcm<'c, KEY_SIZE, 8, IV_SIZE> {}\n#[cfg(any(cryp_v2, cryp_v3, cryp_v4))]\nimpl<'c, const KEY_SIZE: usize, const IV_SIZE: usize> CipherAuthenticated<10> for AesCcm<'c, KEY_SIZE, 10, IV_SIZE> {}\n#[cfg(any(cryp_v2, cryp_v3, cryp_v4))]\nimpl<'c, const KEY_SIZE: usize, const IV_SIZE: usize> CipherAuthenticated<12> for AesCcm<'c, KEY_SIZE, 12, IV_SIZE> {}\n#[cfg(any(cryp_v2, cryp_v3, cryp_v4))]\nimpl<'c, const KEY_SIZE: usize, const IV_SIZE: usize> CipherAuthenticated<14> for AesCcm<'c, KEY_SIZE, 14, IV_SIZE> {}\n#[cfg(any(cryp_v2, cryp_v3, cryp_v4))]\nimpl<'c, const KEY_SIZE: usize, const IV_SIZE: usize> CipherAuthenticated<16> for AesCcm<'c, KEY_SIZE, 16, IV_SIZE> {}\n#[cfg(any(cryp_v2, cryp_v3, cryp_v4))]\nimpl<'c, const KEY_SIZE: usize, const TAG_SIZE: usize> IVSized for AesCcm<'c, KEY_SIZE, TAG_SIZE, 7> {}\n#[cfg(any(cryp_v2, cryp_v3, cryp_v4))]\nimpl<'c, const KEY_SIZE: usize, const TAG_SIZE: usize> IVSized for AesCcm<'c, KEY_SIZE, TAG_SIZE, 8> {}\n#[cfg(any(cryp_v2, cryp_v3, cryp_v4))]\nimpl<'c, const KEY_SIZE: usize, const TAG_SIZE: usize> IVSized for AesCcm<'c, KEY_SIZE, TAG_SIZE, 9> {}\n#[cfg(any(cryp_v2, cryp_v3, cryp_v4))]\nimpl<'c, const KEY_SIZE: usize, const TAG_SIZE: usize> IVSized for AesCcm<'c, KEY_SIZE, TAG_SIZE, 10> {}\n#[cfg(any(cryp_v2, cryp_v3, cryp_v4))]\nimpl<'c, const KEY_SIZE: usize, const TAG_SIZE: usize> IVSized for AesCcm<'c, KEY_SIZE, TAG_SIZE, 11> {}\n#[cfg(any(cryp_v2, cryp_v3, cryp_v4))]\nimpl<'c, const KEY_SIZE: usize, const TAG_SIZE: usize> IVSized for AesCcm<'c, KEY_SIZE, TAG_SIZE, 12> {}\n#[cfg(any(cryp_v2, cryp_v3, cryp_v4))]\nimpl<'c, const KEY_SIZE: usize, const TAG_SIZE: usize> IVSized for AesCcm<'c, KEY_SIZE, TAG_SIZE, 13> {}\n\n#[allow(dead_code)]\n/// Holds the state information for a cipher operation.\n/// Allows suspending/resuming of cipher operations.\npub struct Context<'c, C: Cipher<'c> + CipherSized> {\n    phantom_data: PhantomData<&'c C>,\n    cipher: &'c C,\n    dir: Direction,\n    last_block_processed: bool,\n    header_processed: bool,\n    aad_complete: bool,\n    cr: u32,\n    iv: [u32; 4],\n    csgcmccm: [u32; 8],\n    csgcm: [u32; 8],\n    header_len: u64,\n    payload_len: u64,\n    aad_buffer: [u8; 16],\n    aad_buffer_len: usize,\n}\n\n/// Selects whether the crypto processor operates in encryption or decryption mode.\n#[derive(PartialEq, Clone, Copy)]\npub enum Direction {\n    /// Encryption mode\n    Encrypt,\n    /// Decryption mode\n    Decrypt,\n}\n\n/// Crypto Accelerator Driver\npub struct Cryp<'d, T: Instance, M: Mode> {\n    _peripheral: Peri<'d, T>,\n    _phantom: PhantomData<M>,\n    indma: Option<ChannelAndRequest<'d>>,\n    outdma: Option<ChannelAndRequest<'d>>,\n}\n\nimpl<'d, T: Instance> Cryp<'d, T, Blocking> {\n    /// Create a new CRYP driver in blocking mode.\n    pub fn new_blocking(\n        peri: Peri<'d, T>,\n        _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd,\n    ) -> Self {\n        rcc::enable_and_reset::<T>();\n        let instance = Self {\n            _peripheral: peri,\n            _phantom: PhantomData,\n            indma: None,\n            outdma: None,\n        };\n\n        T::Interrupt::unpend();\n        unsafe { T::Interrupt::enable() };\n\n        instance\n    }\n}\n\nimpl<'d, T: Instance, M: Mode> Cryp<'d, T, M> {\n    /// Start a new encrypt or decrypt operation for the given cipher.\n    pub fn start_blocking<'c, C: Cipher<'c> + CipherSized + IVSized>(\n        &self,\n        cipher: &'c C,\n        dir: Direction,\n    ) -> Context<'c, C> {\n        let mut ctx: Context<'c, C> = Context {\n            dir,\n            last_block_processed: false,\n            cr: 0,\n            iv: [0; 4],\n            csgcmccm: [0; 8],\n            csgcm: [0; 8],\n            aad_complete: false,\n            header_len: 0,\n            payload_len: 0,\n            cipher: cipher,\n            phantom_data: PhantomData,\n            header_processed: false,\n            aad_buffer: [0; 16],\n            aad_buffer_len: 0,\n        };\n\n        T::regs().cr().modify(|w| w.set_crypen(false));\n\n        let key = ctx.cipher.key();\n\n        if key.len() == (128 / 8) {\n            T::regs().cr().modify(|w| w.set_keysize(0));\n        } else if key.len() == (192 / 8) {\n            T::regs().cr().modify(|w| w.set_keysize(1));\n        } else if key.len() == (256 / 8) {\n            T::regs().cr().modify(|w| w.set_keysize(2));\n        }\n\n        self.load_key(key);\n\n        // Set data type to 8-bit. This will match software implementations.\n        T::regs().cr().modify(|w| w.set_datatype(2));\n\n        ctx.cipher.prepare_key(T::regs());\n\n        ctx.cipher.set_algomode(T::regs());\n\n        // Set encrypt/decrypt\n        if dir == Direction::Encrypt {\n            T::regs().cr().modify(|w| w.set_algodir(false));\n        } else {\n            T::regs().cr().modify(|w| w.set_algodir(true));\n        }\n\n        // Load the IV into the registers.\n        let iv = ctx.cipher.iv();\n        let mut full_iv: [u8; 16] = [0; 16];\n        full_iv[0..iv.len()].copy_from_slice(iv);\n        let mut iv_idx = 0;\n        let mut iv_word: [u8; 4] = [0; 4];\n        iv_word.copy_from_slice(&full_iv[iv_idx..iv_idx + 4]);\n        iv_idx += 4;\n        T::regs().init(0).ivlr().write_value(u32::from_be_bytes(iv_word));\n        iv_word.copy_from_slice(&full_iv[iv_idx..iv_idx + 4]);\n        iv_idx += 4;\n        T::regs().init(0).ivrr().write_value(u32::from_be_bytes(iv_word));\n        iv_word.copy_from_slice(&full_iv[iv_idx..iv_idx + 4]);\n        iv_idx += 4;\n        T::regs().init(1).ivlr().write_value(u32::from_be_bytes(iv_word));\n        iv_word.copy_from_slice(&full_iv[iv_idx..iv_idx + 4]);\n        T::regs().init(1).ivrr().write_value(u32::from_be_bytes(iv_word));\n\n        // Flush in/out FIFOs\n        T::regs().cr().modify(|w| w.fflush());\n\n        ctx.cipher.init_phase_blocking(T::regs(), self);\n\n        self.store_context(&mut ctx);\n\n        ctx\n    }\n\n    #[cfg(any(cryp_v2, cryp_v3, cryp_v4))]\n    /// Controls the header phase of cipher processing.\n    /// This function is only valid for authenticated ciphers including GCM, CCM, and GMAC.\n    /// All additional associated data (AAD) must be supplied to this function prior to starting the payload phase with `payload_blocking`.\n    /// The AAD must be supplied in multiples of the block size (128-bits for AES, 64-bits for DES), except when supplying the last block.\n    /// When supplying the last block of AAD, `last_aad_block` must be `true`.\n    pub fn aad_blocking<\n        'c,\n        const TAG_SIZE: usize,\n        C: Cipher<'c> + CipherSized + IVSized + CipherAuthenticated<TAG_SIZE>,\n    >(\n        &self,\n        ctx: &mut Context<'c, C>,\n        aad: &[u8],\n        last_aad_block: bool,\n    ) {\n        self.load_context(ctx);\n\n        // Perform checks for correctness.\n        if ctx.aad_complete {\n            panic!(\"Cannot update AAD after starting payload!\")\n        }\n\n        ctx.header_len += aad.len() as u64;\n\n        // Header phase\n        T::regs().cr().modify(|w| w.set_crypen(false));\n        T::regs().cr().modify(|w| w.set_gcm_ccmph(1));\n        T::regs().cr().modify(|w| w.set_crypen(true));\n\n        // First write the header B1 block if not yet written.\n        if !ctx.header_processed {\n            ctx.header_processed = true;\n            let header = ctx.cipher.get_header_block();\n            ctx.aad_buffer[0..header.len()].copy_from_slice(header);\n            ctx.aad_buffer_len += header.len();\n        }\n\n        // Fill the header block to make a full block.\n        let len_to_copy = min(aad.len(), C::BLOCK_SIZE - ctx.aad_buffer_len);\n        ctx.aad_buffer[ctx.aad_buffer_len..ctx.aad_buffer_len + len_to_copy].copy_from_slice(&aad[..len_to_copy]);\n        ctx.aad_buffer_len += len_to_copy;\n        ctx.aad_buffer[ctx.aad_buffer_len..].fill(0);\n        let mut aad_len_remaining = aad.len() - len_to_copy;\n\n        if ctx.aad_buffer_len < C::BLOCK_SIZE {\n            // The buffer isn't full and this is the last buffer, so process it as is (already padded).\n            if last_aad_block {\n                self.write_bytes_blocking(C::BLOCK_SIZE, &ctx.aad_buffer);\n                // Block until input FIFO is empty.\n                while !T::regs().sr().read().ifem() {}\n\n                // Switch to payload phase.\n                ctx.aad_complete = true;\n                T::regs().cr().modify(|w| w.set_crypen(false));\n                T::regs().cr().modify(|w| w.set_gcm_ccmph(2));\n                T::regs().cr().modify(|w| w.fflush());\n            } else {\n                // Just return because we don't yet have a full block to process.\n                return;\n            }\n        } else {\n            // Load the full block from the buffer.\n            self.write_bytes_blocking(C::BLOCK_SIZE, &ctx.aad_buffer);\n            // Block until input FIFO is empty.\n            while !T::regs().sr().read().ifem() {}\n        }\n\n        // Handle a partial block that is passed in.\n        ctx.aad_buffer_len = 0;\n        let leftovers = aad_len_remaining % C::BLOCK_SIZE;\n        ctx.aad_buffer[..leftovers].copy_from_slice(&aad[aad.len() - leftovers..aad.len()]);\n        ctx.aad_buffer_len += leftovers;\n        ctx.aad_buffer[ctx.aad_buffer_len..].fill(0);\n        aad_len_remaining -= leftovers;\n        assert_eq!(aad_len_remaining % C::BLOCK_SIZE, 0);\n\n        // Load full data blocks into core.\n        let num_full_blocks = aad_len_remaining / C::BLOCK_SIZE;\n        let start_index = len_to_copy;\n        let end_index = start_index + (C::BLOCK_SIZE * num_full_blocks);\n        self.write_bytes_blocking(C::BLOCK_SIZE, &aad[start_index..end_index]);\n\n        if last_aad_block {\n            if leftovers > 0 {\n                self.write_bytes_blocking(C::BLOCK_SIZE, &ctx.aad_buffer);\n            }\n            // Switch to payload phase.\n            ctx.aad_complete = true;\n            T::regs().cr().modify(|w| w.set_crypen(false));\n            T::regs().cr().modify(|w| w.set_gcm_ccmph(2));\n            T::regs().cr().modify(|w| w.fflush());\n        }\n\n        self.store_context(ctx);\n    }\n\n    /// Performs encryption/decryption on the provided context.\n    /// The context determines algorithm, mode, and state of the crypto accelerator.\n    /// When the last piece of data is supplied, `last_block` should be `true`.\n    /// This function panics under various mismatches of parameters.\n    /// Output buffer must be at least as long as the input buffer.\n    /// Data must be a multiple of block size (128-bits for AES, 64-bits for DES) for CBC and ECB modes.\n    /// Padding or ciphertext stealing must be managed by the application for these modes.\n    /// Data must also be a multiple of block size unless `last_block` is `true`.\n    pub fn payload_blocking<'c, C: Cipher<'c> + CipherSized + IVSized>(\n        &self,\n        ctx: &mut Context<'c, C>,\n        input: &[u8],\n        output: &mut [u8],\n        last_block: bool,\n    ) {\n        self.load_context(ctx);\n\n        let last_block_remainder = input.len() % C::BLOCK_SIZE;\n\n        // Perform checks for correctness.\n        if !ctx.aad_complete && ctx.header_len > 0 {\n            panic!(\"Additional associated data must be processed first!\");\n        } else if !ctx.aad_complete {\n            #[cfg(any(cryp_v2, cryp_v3, cryp_v4))]\n            {\n                ctx.aad_complete = true;\n                T::regs().cr().modify(|w| w.set_crypen(false));\n                T::regs().cr().modify(|w| w.set_gcm_ccmph(2));\n                T::regs().cr().modify(|w| w.fflush());\n                T::regs().cr().modify(|w| w.set_crypen(true));\n            }\n        }\n        if ctx.last_block_processed {\n            panic!(\"The last block has already been processed!\");\n        }\n        if input.len() > output.len() {\n            panic!(\"Output buffer length must match input length.\");\n        }\n        if !last_block {\n            if last_block_remainder != 0 {\n                panic!(\"Input length must be a multiple of {} bytes.\", C::BLOCK_SIZE);\n            }\n        }\n        if C::REQUIRES_PADDING {\n            if last_block_remainder != 0 {\n                panic!(\n                    \"Input must be a multiple of {} bytes in ECB and CBC modes. Consider padding or ciphertext stealing.\",\n                    C::BLOCK_SIZE\n                );\n            }\n        }\n        if last_block {\n            ctx.last_block_processed = true;\n        }\n\n        // Load data into core, block by block.\n        let num_full_blocks = input.len() / C::BLOCK_SIZE;\n        for block in 0..num_full_blocks {\n            let index = block * C::BLOCK_SIZE;\n            // Write block in\n            self.write_bytes_blocking(C::BLOCK_SIZE, &input[index..index + C::BLOCK_SIZE]);\n            // Read block out\n            self.read_bytes_blocking(C::BLOCK_SIZE, &mut output[index..index + C::BLOCK_SIZE]);\n        }\n\n        // Handle the final block, which is incomplete.\n        if last_block_remainder > 0 {\n            let padding_len = C::BLOCK_SIZE - last_block_remainder;\n            let temp1 = ctx.cipher.pre_final(T::regs(), ctx.dir, padding_len);\n\n            let mut intermediate_data: [u8; AES_BLOCK_SIZE] = [0; AES_BLOCK_SIZE];\n            let mut last_block: [u8; AES_BLOCK_SIZE] = [0; AES_BLOCK_SIZE];\n            last_block[..last_block_remainder].copy_from_slice(&input[input.len() - last_block_remainder..input.len()]);\n            self.write_bytes_blocking(C::BLOCK_SIZE, &last_block);\n            self.read_bytes_blocking(C::BLOCK_SIZE, &mut intermediate_data);\n\n            // Handle the last block depending on mode.\n            let output_len = output.len();\n            output[output_len - last_block_remainder..output_len]\n                .copy_from_slice(&intermediate_data[0..last_block_remainder]);\n\n            let mut mask: [u8; 16] = [0; 16];\n            mask[..last_block_remainder].fill(0xFF);\n            ctx.cipher\n                .post_final_blocking(T::regs(), self, ctx.dir, &mut intermediate_data, temp1, mask);\n        }\n\n        ctx.payload_len += input.len() as u64;\n\n        self.store_context(ctx);\n    }\n\n    #[cfg(any(cryp_v2, cryp_v3, cryp_v4))]\n    /// Generates an authentication tag for authenticated ciphers including GCM, CCM, and GMAC.\n    /// Called after the all data has been encrypted/decrypted by `payload`.\n    pub fn finish_blocking<\n        'c,\n        const TAG_SIZE: usize,\n        C: Cipher<'c> + CipherSized + IVSized + CipherAuthenticated<TAG_SIZE>,\n    >(\n        &self,\n        mut ctx: Context<'c, C>,\n    ) -> [u8; TAG_SIZE] {\n        self.load_context(&mut ctx);\n\n        T::regs().cr().modify(|w| w.set_crypen(false));\n        T::regs().cr().modify(|w| w.set_gcm_ccmph(3));\n        T::regs().cr().modify(|w| w.set_crypen(true));\n\n        let headerlen1: u32 = ((ctx.header_len * 8) >> 32) as u32;\n        let headerlen2: u32 = (ctx.header_len * 8) as u32;\n        let payloadlen1: u32 = ((ctx.payload_len * 8) >> 32) as u32;\n        let payloadlen2: u32 = (ctx.payload_len * 8) as u32;\n\n        #[cfg(cryp_v2)]\n        let footer: [u32; 4] = [\n            headerlen1.swap_bytes(),\n            headerlen2.swap_bytes(),\n            payloadlen1.swap_bytes(),\n            payloadlen2.swap_bytes(),\n        ];\n        #[cfg(any(cryp_v3, cryp_v4))]\n        let footer: [u32; 4] = [headerlen1, headerlen2, payloadlen1, payloadlen2];\n\n        self.write_words_blocking(C::BLOCK_SIZE, &footer);\n\n        while !T::regs().sr().read().ofne() {}\n\n        let mut full_tag: [u8; 16] = [0; 16];\n        self.read_bytes_blocking(C::BLOCK_SIZE, &mut full_tag);\n        let mut tag: [u8; TAG_SIZE] = [0; TAG_SIZE];\n        tag.copy_from_slice(&full_tag[0..TAG_SIZE]);\n\n        T::regs().cr().modify(|w| w.set_crypen(false));\n\n        tag\n    }\n\n    fn load_key(&self, key: &[u8]) {\n        // Load the key into the registers.\n        let mut keyidx = 0;\n        let mut keyword: [u8; 4] = [0; 4];\n        let keylen = key.len() * 8;\n        if keylen > 192 {\n            keyword.copy_from_slice(&key[keyidx..keyidx + 4]);\n            keyidx += 4;\n            T::regs().key(0).klr().write_value(u32::from_be_bytes(keyword));\n            keyword.copy_from_slice(&key[keyidx..keyidx + 4]);\n            keyidx += 4;\n            T::regs().key(0).krr().write_value(u32::from_be_bytes(keyword));\n        }\n        if keylen > 128 {\n            keyword.copy_from_slice(&key[keyidx..keyidx + 4]);\n            keyidx += 4;\n            T::regs().key(1).klr().write_value(u32::from_be_bytes(keyword));\n            keyword.copy_from_slice(&key[keyidx..keyidx + 4]);\n            keyidx += 4;\n            T::regs().key(1).krr().write_value(u32::from_be_bytes(keyword));\n        }\n        if keylen > 64 {\n            keyword.copy_from_slice(&key[keyidx..keyidx + 4]);\n            keyidx += 4;\n            T::regs().key(2).klr().write_value(u32::from_be_bytes(keyword));\n            keyword.copy_from_slice(&key[keyidx..keyidx + 4]);\n            keyidx += 4;\n            T::regs().key(2).krr().write_value(u32::from_be_bytes(keyword));\n        }\n        keyword.copy_from_slice(&key[keyidx..keyidx + 4]);\n        keyidx += 4;\n        T::regs().key(3).klr().write_value(u32::from_be_bytes(keyword));\n        keyword = [0; 4];\n        keyword[0..key.len() - keyidx].copy_from_slice(&key[keyidx..key.len()]);\n        T::regs().key(3).krr().write_value(u32::from_be_bytes(keyword));\n    }\n\n    fn store_context<'c, C: Cipher<'c> + CipherSized>(&self, ctx: &mut Context<'c, C>) {\n        // Wait for data block processing to finish.\n        while !T::regs().sr().read().ifem() {}\n        while T::regs().sr().read().ofne() {}\n        while T::regs().sr().read().busy() {}\n\n        // Disable crypto processor.\n        T::regs().cr().modify(|w| w.set_crypen(false));\n\n        // Save the peripheral state.\n        ctx.cr = T::regs().cr().read().0;\n        ctx.iv[0] = T::regs().init(0).ivlr().read();\n        ctx.iv[1] = T::regs().init(0).ivrr().read();\n        ctx.iv[2] = T::regs().init(1).ivlr().read();\n        ctx.iv[3] = T::regs().init(1).ivrr().read();\n\n        #[cfg(any(cryp_v2, cryp_v3, cryp_v4))]\n        for i in 0..8 {\n            ctx.csgcmccm[i] = T::regs().csgcmccmr(i).read();\n            ctx.csgcm[i] = T::regs().csgcmr(i).read();\n        }\n    }\n\n    fn load_context<'c, C: Cipher<'c> + CipherSized>(&self, ctx: &Context<'c, C>) {\n        // Reload state registers.\n        T::regs().cr().write(|w| w.0 = ctx.cr);\n        T::regs().init(0).ivlr().write_value(ctx.iv[0]);\n        T::regs().init(0).ivrr().write_value(ctx.iv[1]);\n        T::regs().init(1).ivlr().write_value(ctx.iv[2]);\n        T::regs().init(1).ivrr().write_value(ctx.iv[3]);\n\n        #[cfg(any(cryp_v2, cryp_v3, cryp_v4))]\n        for i in 0..8 {\n            T::regs().csgcmccmr(i).write_value(ctx.csgcmccm[i]);\n            T::regs().csgcmr(i).write_value(ctx.csgcm[i]);\n        }\n        self.load_key(ctx.cipher.key());\n\n        // Prepare key if applicable.\n        ctx.cipher.prepare_key(T::regs());\n        T::regs().cr().write(|w| w.0 = ctx.cr);\n\n        // Enable crypto processor.\n        T::regs().cr().modify(|w| w.set_crypen(true));\n    }\n\n    fn write_bytes_blocking(&self, block_size: usize, blocks: &[u8]) {\n        // Ensure input is a multiple of block size.\n        assert_eq!(blocks.len() % block_size, 0);\n        let mut index = 0;\n        let end_index = blocks.len();\n        while index < end_index {\n            let mut in_word: [u8; 4] = [0; 4];\n            in_word.copy_from_slice(&blocks[index..index + 4]);\n            T::regs().din().write_value(u32::from_ne_bytes(in_word));\n            index += 4;\n            if index % block_size == 0 {\n                // Block until input FIFO is empty.\n                while !T::regs().sr().read().ifem() {}\n            }\n        }\n    }\n\n    #[cfg(any(cryp_v2, cryp_v3, cryp_v4))]\n    fn write_words_blocking(&self, block_size: usize, blocks: &[u32]) {\n        assert_eq!((blocks.len() * 4) % block_size, 0);\n        let mut byte_counter: usize = 0;\n        for word in blocks {\n            T::regs().din().write_value(*word);\n            byte_counter += 4;\n            if byte_counter % block_size == 0 {\n                // Block until input FIFO is empty.\n                while !T::regs().sr().read().ifem() {}\n            }\n        }\n    }\n\n    fn read_bytes_blocking(&self, block_size: usize, blocks: &mut [u8]) {\n        // Block until there is output to read.\n        while !T::regs().sr().read().ofne() {}\n        // Ensure input is a multiple of block size.\n        assert_eq!(blocks.len() % block_size, 0);\n        // Read block out\n        let mut index = 0;\n        let end_index = blocks.len();\n        while index < end_index {\n            let out_word: u32 = T::regs().dout().read();\n            blocks[index..index + 4].copy_from_slice(u32::to_ne_bytes(out_word).as_slice());\n            index += 4;\n        }\n    }\n}\n\nimpl<'d, T: Instance> Cryp<'d, T, Async> {\n    /// Create a new CRYP driver.\n    pub fn new<D1: DmaIn<T>, D2: DmaOut<T>>(\n        peri: Peri<'d, T>,\n        indma: Peri<'d, D1>,\n        outdma: Peri<'d, D2>,\n        _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>>\n        + interrupt::typelevel::Binding<D1::Interrupt, crate::dma::InterruptHandler<D1>>\n        + interrupt::typelevel::Binding<D2::Interrupt, crate::dma::InterruptHandler<D2>>\n        + 'd,\n    ) -> Self {\n        rcc::enable_and_reset::<T>();\n        let instance = Self {\n            _peripheral: peri,\n            _phantom: PhantomData,\n            indma: new_dma!(indma, _irq),\n            outdma: new_dma!(outdma, _irq),\n        };\n\n        T::Interrupt::unpend();\n        unsafe { T::Interrupt::enable() };\n\n        instance\n    }\n\n    /// Start a new encrypt or decrypt operation for the given cipher.\n    pub async fn start<'c, C: Cipher<'c> + CipherSized + IVSized>(\n        &mut self,\n        cipher: &'c C,\n        dir: Direction,\n    ) -> Context<'c, C> {\n        let mut ctx: Context<'c, C> = Context {\n            dir,\n            last_block_processed: false,\n            cr: 0,\n            iv: [0; 4],\n            csgcmccm: [0; 8],\n            csgcm: [0; 8],\n            aad_complete: false,\n            header_len: 0,\n            payload_len: 0,\n            cipher: cipher,\n            phantom_data: PhantomData,\n            header_processed: false,\n            aad_buffer: [0; 16],\n            aad_buffer_len: 0,\n        };\n\n        T::regs().cr().modify(|w| w.set_crypen(false));\n\n        let key = ctx.cipher.key();\n\n        if key.len() == (128 / 8) {\n            T::regs().cr().modify(|w| w.set_keysize(0));\n        } else if key.len() == (192 / 8) {\n            T::regs().cr().modify(|w| w.set_keysize(1));\n        } else if key.len() == (256 / 8) {\n            T::regs().cr().modify(|w| w.set_keysize(2));\n        }\n\n        self.load_key(key);\n\n        // Set data type to 8-bit. This will match software implementations.\n        T::regs().cr().modify(|w| w.set_datatype(2));\n\n        ctx.cipher.prepare_key(T::regs());\n\n        ctx.cipher.set_algomode(T::regs());\n\n        // Set encrypt/decrypt\n        if dir == Direction::Encrypt {\n            T::regs().cr().modify(|w| w.set_algodir(false));\n        } else {\n            T::regs().cr().modify(|w| w.set_algodir(true));\n        }\n\n        // Load the IV into the registers.\n        let iv = ctx.cipher.iv();\n        let mut full_iv: [u8; 16] = [0; 16];\n        full_iv[0..iv.len()].copy_from_slice(iv);\n        let mut iv_idx = 0;\n        let mut iv_word: [u8; 4] = [0; 4];\n        iv_word.copy_from_slice(&full_iv[iv_idx..iv_idx + 4]);\n        iv_idx += 4;\n        T::regs().init(0).ivlr().write_value(u32::from_be_bytes(iv_word));\n        iv_word.copy_from_slice(&full_iv[iv_idx..iv_idx + 4]);\n        iv_idx += 4;\n        T::regs().init(0).ivrr().write_value(u32::from_be_bytes(iv_word));\n        iv_word.copy_from_slice(&full_iv[iv_idx..iv_idx + 4]);\n        iv_idx += 4;\n        T::regs().init(1).ivlr().write_value(u32::from_be_bytes(iv_word));\n        iv_word.copy_from_slice(&full_iv[iv_idx..iv_idx + 4]);\n        T::regs().init(1).ivrr().write_value(u32::from_be_bytes(iv_word));\n\n        // Flush in/out FIFOs\n        T::regs().cr().modify(|w| w.fflush());\n\n        ctx.cipher.init_phase(T::regs(), self).await;\n\n        self.store_context(&mut ctx);\n\n        ctx\n    }\n\n    #[cfg(any(cryp_v2, cryp_v3, cryp_v4))]\n    /// Controls the header phase of cipher processing.\n    /// This function is only valid for authenticated ciphers including GCM, CCM, and GMAC.\n    /// All additional associated data (AAD) must be supplied to this function prior to starting the payload phase with `payload`.\n    /// The AAD must be supplied in multiples of the block size (128-bits for AES, 64-bits for DES), except when supplying the last block.\n    /// When supplying the last block of AAD, `last_aad_block` must be `true`.\n    pub async fn aad<\n        'c,\n        const TAG_SIZE: usize,\n        C: Cipher<'c> + CipherSized + IVSized + CipherAuthenticated<TAG_SIZE>,\n    >(\n        &mut self,\n        ctx: &mut Context<'c, C>,\n        aad: &[u8],\n        last_aad_block: bool,\n    ) {\n        self.load_context(ctx);\n\n        // Perform checks for correctness.\n        if ctx.aad_complete {\n            panic!(\"Cannot update AAD after starting payload!\")\n        }\n\n        ctx.header_len += aad.len() as u64;\n\n        // Header phase\n        T::regs().cr().modify(|w| w.set_crypen(false));\n        T::regs().cr().modify(|w| w.set_gcm_ccmph(1));\n        T::regs().cr().modify(|w| w.set_crypen(true));\n\n        // First write the header B1 block if not yet written.\n        if !ctx.header_processed {\n            ctx.header_processed = true;\n            let header = ctx.cipher.get_header_block();\n            ctx.aad_buffer[0..header.len()].copy_from_slice(header);\n            ctx.aad_buffer_len += header.len();\n        }\n\n        // Fill the header block to make a full block.\n        let len_to_copy = min(aad.len(), C::BLOCK_SIZE - ctx.aad_buffer_len);\n        ctx.aad_buffer[ctx.aad_buffer_len..ctx.aad_buffer_len + len_to_copy].copy_from_slice(&aad[..len_to_copy]);\n        ctx.aad_buffer_len += len_to_copy;\n        ctx.aad_buffer[ctx.aad_buffer_len..].fill(0);\n        let mut aad_len_remaining = aad.len() - len_to_copy;\n\n        if ctx.aad_buffer_len < C::BLOCK_SIZE {\n            // The buffer isn't full and this is the last buffer, so process it as is (already padded).\n            if last_aad_block {\n                Self::write_bytes(self.indma.as_mut().unwrap(), C::BLOCK_SIZE, &ctx.aad_buffer).await;\n                assert_eq!(T::regs().sr().read().ifem(), true);\n\n                // Switch to payload phase.\n                ctx.aad_complete = true;\n                T::regs().cr().modify(|w| w.set_crypen(false));\n                T::regs().cr().modify(|w| w.set_gcm_ccmph(2));\n                T::regs().cr().modify(|w| w.fflush());\n            } else {\n                // Just return because we don't yet have a full block to process.\n                return;\n            }\n        } else {\n            // Load the full block from the buffer.\n            Self::write_bytes(self.indma.as_mut().unwrap(), C::BLOCK_SIZE, &ctx.aad_buffer).await;\n            assert_eq!(T::regs().sr().read().ifem(), true);\n        }\n\n        // Handle a partial block that is passed in.\n        ctx.aad_buffer_len = 0;\n        let leftovers = aad_len_remaining % C::BLOCK_SIZE;\n        ctx.aad_buffer[..leftovers].copy_from_slice(&aad[aad.len() - leftovers..aad.len()]);\n        ctx.aad_buffer_len += leftovers;\n        ctx.aad_buffer[ctx.aad_buffer_len..].fill(0);\n        aad_len_remaining -= leftovers;\n        assert_eq!(aad_len_remaining % C::BLOCK_SIZE, 0);\n\n        // Load full data blocks into core.\n        let num_full_blocks = aad_len_remaining / C::BLOCK_SIZE;\n        let start_index = len_to_copy;\n        let end_index = start_index + (C::BLOCK_SIZE * num_full_blocks);\n        Self::write_bytes(\n            self.indma.as_mut().unwrap(),\n            C::BLOCK_SIZE,\n            &aad[start_index..end_index],\n        )\n        .await;\n\n        if last_aad_block {\n            if leftovers > 0 {\n                Self::write_bytes(self.indma.as_mut().unwrap(), C::BLOCK_SIZE, &ctx.aad_buffer).await;\n                assert_eq!(T::regs().sr().read().ifem(), true);\n            }\n            // Switch to payload phase.\n            ctx.aad_complete = true;\n            T::regs().cr().modify(|w| w.set_crypen(false));\n            T::regs().cr().modify(|w| w.set_gcm_ccmph(2));\n            T::regs().cr().modify(|w| w.fflush());\n        }\n\n        self.store_context(ctx);\n    }\n\n    /// Performs encryption/decryption on the provided context.\n    /// The context determines algorithm, mode, and state of the crypto accelerator.\n    /// When the last piece of data is supplied, `last_block` should be `true`.\n    /// This function panics under various mismatches of parameters.\n    /// Output buffer must be at least as long as the input buffer.\n    /// Data must be a multiple of block size (128-bits for AES, 64-bits for DES) for CBC and ECB modes.\n    /// Padding or ciphertext stealing must be managed by the application for these modes.\n    /// Data must also be a multiple of block size unless `last_block` is `true`.\n    pub async fn payload<'c, C: Cipher<'c> + CipherSized + IVSized>(\n        &mut self,\n        ctx: &mut Context<'c, C>,\n        input: &[u8],\n        output: &mut [u8],\n        last_block: bool,\n    ) {\n        self.load_context(ctx);\n\n        let last_block_remainder = input.len() % C::BLOCK_SIZE;\n\n        // Perform checks for correctness.\n        if !ctx.aad_complete && ctx.header_len > 0 {\n            panic!(\"Additional associated data must be processed first!\");\n        } else if !ctx.aad_complete {\n            #[cfg(any(cryp_v2, cryp_v3, cryp_v4))]\n            {\n                ctx.aad_complete = true;\n                T::regs().cr().modify(|w| w.set_crypen(false));\n                T::regs().cr().modify(|w| w.set_gcm_ccmph(2));\n                T::regs().cr().modify(|w| w.fflush());\n                T::regs().cr().modify(|w| w.set_crypen(true));\n            }\n        }\n        if ctx.last_block_processed {\n            panic!(\"The last block has already been processed!\");\n        }\n        if input.len() > output.len() {\n            panic!(\"Output buffer length must match input length.\");\n        }\n        if !last_block {\n            if last_block_remainder != 0 {\n                panic!(\"Input length must be a multiple of {} bytes.\", C::BLOCK_SIZE);\n            }\n        }\n        if C::REQUIRES_PADDING {\n            if last_block_remainder != 0 {\n                panic!(\n                    \"Input must be a multiple of {} bytes in ECB and CBC modes. Consider padding or ciphertext stealing.\",\n                    C::BLOCK_SIZE\n                );\n            }\n        }\n        if last_block {\n            ctx.last_block_processed = true;\n        }\n\n        // Load data into core, block by block.\n        let num_full_blocks = input.len() / C::BLOCK_SIZE;\n        for block in 0..num_full_blocks {\n            let index = block * C::BLOCK_SIZE;\n            // Read block out\n            let read = Self::read_bytes(\n                self.outdma.as_mut().unwrap(),\n                C::BLOCK_SIZE,\n                &mut output[index..index + C::BLOCK_SIZE],\n            );\n            // Write block in\n            let write = Self::write_bytes(\n                self.indma.as_mut().unwrap(),\n                C::BLOCK_SIZE,\n                &input[index..index + C::BLOCK_SIZE],\n            );\n            embassy_futures::join::join(read, write).await;\n        }\n\n        // Handle the final block, which is incomplete.\n        if last_block_remainder > 0 {\n            let padding_len = C::BLOCK_SIZE - last_block_remainder;\n            let temp1 = ctx.cipher.pre_final(T::regs(), ctx.dir, padding_len);\n\n            let mut intermediate_data: [u8; AES_BLOCK_SIZE] = [0; AES_BLOCK_SIZE];\n            let mut last_block: [u8; AES_BLOCK_SIZE] = [0; AES_BLOCK_SIZE];\n            last_block[..last_block_remainder].copy_from_slice(&input[input.len() - last_block_remainder..input.len()]);\n            let read = Self::read_bytes(self.outdma.as_mut().unwrap(), C::BLOCK_SIZE, &mut intermediate_data);\n            let write = Self::write_bytes(self.indma.as_mut().unwrap(), C::BLOCK_SIZE, &last_block);\n            embassy_futures::join::join(read, write).await;\n\n            // Handle the last block depending on mode.\n            let output_len = output.len();\n            output[output_len - last_block_remainder..output_len]\n                .copy_from_slice(&intermediate_data[0..last_block_remainder]);\n\n            let mut mask: [u8; 16] = [0; 16];\n            mask[..last_block_remainder].fill(0xFF);\n            ctx.cipher\n                .post_final(T::regs(), self, ctx.dir, &mut intermediate_data, temp1, mask)\n                .await;\n        }\n\n        ctx.payload_len += input.len() as u64;\n\n        self.store_context(ctx);\n    }\n\n    #[cfg(any(cryp_v2, cryp_v3, cryp_v4))]\n    // Generates an authentication tag for authenticated ciphers including GCM, CCM, and GMAC.\n    /// Called after the all data has been encrypted/decrypted by `payload`.\n    pub async fn finish<\n        'c,\n        const TAG_SIZE: usize,\n        C: Cipher<'c> + CipherSized + IVSized + CipherAuthenticated<TAG_SIZE>,\n    >(\n        &mut self,\n        mut ctx: Context<'c, C>,\n    ) -> [u8; TAG_SIZE] {\n        self.load_context(&mut ctx);\n\n        T::regs().cr().modify(|w| w.set_crypen(false));\n        T::regs().cr().modify(|w| w.set_gcm_ccmph(3));\n        T::regs().cr().modify(|w| w.set_crypen(true));\n\n        let headerlen1: u32 = ((ctx.header_len * 8) >> 32) as u32;\n        let headerlen2: u32 = (ctx.header_len * 8) as u32;\n        let payloadlen1: u32 = ((ctx.payload_len * 8) >> 32) as u32;\n        let payloadlen2: u32 = (ctx.payload_len * 8) as u32;\n\n        #[cfg(cryp_v2)]\n        let footer: [u32; 4] = [\n            headerlen1.swap_bytes(),\n            headerlen2.swap_bytes(),\n            payloadlen1.swap_bytes(),\n            payloadlen2.swap_bytes(),\n        ];\n        #[cfg(any(cryp_v3, cryp_v4))]\n        let footer: [u32; 4] = [headerlen1, headerlen2, payloadlen1, payloadlen2];\n\n        let write = Self::write_words(self.indma.as_mut().unwrap(), C::BLOCK_SIZE, &footer);\n\n        let mut full_tag: [u8; 16] = [0; 16];\n        let read = Self::read_bytes(self.outdma.as_mut().unwrap(), C::BLOCK_SIZE, &mut full_tag);\n\n        embassy_futures::join::join(read, write).await;\n\n        let mut tag: [u8; TAG_SIZE] = [0; TAG_SIZE];\n        tag.copy_from_slice(&full_tag[0..TAG_SIZE]);\n\n        T::regs().cr().modify(|w| w.set_crypen(false));\n\n        tag\n    }\n\n    async fn write_bytes(dma: &mut ChannelAndRequest<'d>, block_size: usize, blocks: &[u8]) {\n        if blocks.len() == 0 {\n            return;\n        }\n        // Ensure input is a multiple of block size.\n        assert_eq!(blocks.len() % block_size, 0);\n        // Configure DMA to transfer input to crypto core.\n        let dst_ptr: *mut u32 = T::regs().din().as_ptr();\n        let options = TransferOptions {\n            priority: crate::dma::Priority::High,\n            ..Default::default()\n        };\n        let dma_transfer = unsafe { dma.write_raw(blocks, dst_ptr, options) };\n        T::regs().dmacr().modify(|w| w.set_dien(true));\n        // Wait for the transfer to complete.\n        dma_transfer.await;\n    }\n\n    #[cfg(any(cryp_v2, cryp_v3, cryp_v4))]\n    async fn write_words(dma: &mut ChannelAndRequest<'d>, block_size: usize, blocks: &[u32]) {\n        if blocks.len() == 0 {\n            return;\n        }\n        // Ensure input is a multiple of block size.\n        assert_eq!((blocks.len() * 4) % block_size, 0);\n        // Configure DMA to transfer input to crypto core.\n        let dst_ptr: *mut u32 = T::regs().din().as_ptr();\n        let options = TransferOptions {\n            priority: crate::dma::Priority::High,\n            ..Default::default()\n        };\n        let dma_transfer = unsafe { dma.write_raw(blocks, dst_ptr, options) };\n        T::regs().dmacr().modify(|w| w.set_dien(true));\n        // Wait for the transfer to complete.\n        dma_transfer.await;\n    }\n\n    async fn read_bytes(dma: &mut ChannelAndRequest<'d>, block_size: usize, blocks: &mut [u8]) {\n        if blocks.len() == 0 {\n            return;\n        }\n        // Ensure input is a multiple of block size.\n        assert_eq!(blocks.len() % block_size, 0);\n        // Configure DMA to get output from crypto core.\n        let src_ptr = T::regs().dout().as_ptr();\n        let options = TransferOptions {\n            priority: crate::dma::Priority::VeryHigh,\n            ..Default::default()\n        };\n        let dma_transfer = unsafe { dma.read_raw(src_ptr, blocks, options) };\n        T::regs().dmacr().modify(|w| w.set_doen(true));\n        // Wait for the transfer to complete.\n        dma_transfer.await;\n    }\n}\n\ntrait SealedInstance {\n    fn regs() -> pac::cryp::Cryp;\n}\n\n/// CRYP instance trait.\n#[allow(private_bounds)]\npub trait Instance: SealedInstance + PeripheralType + crate::rcc::RccPeripheral + 'static + Send {\n    /// Interrupt for this CRYP instance.\n    type Interrupt: interrupt::typelevel::Interrupt;\n}\n\nforeach_interrupt!(\n    ($inst:ident, cryp, CRYP, GLOBAL, $irq:ident) => {\n        impl Instance for peripherals::$inst {\n            type Interrupt = crate::interrupt::typelevel::$irq;\n        }\n\n        impl SealedInstance for peripherals::$inst {\n            fn regs() -> crate::pac::cryp::Cryp {\n                crate::pac::$inst\n            }\n        }\n    };\n);\n\ndma_trait!(DmaIn, Instance);\ndma_trait!(DmaOut, Instance);\n"
  },
  {
    "path": "embassy-stm32/src/dac/mod.rs",
    "content": "//! Digital to Analog Converter (DAC)\n#![macro_use]\n\nuse core::marker::PhantomData;\n\n#[cfg(stm32g4)]\nuse dac::vals;\nuse embassy_hal_internal::PeripheralType;\nuse embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;\n\nuse crate::dma::ChannelAndRequest;\nuse crate::mode::{Async, Blocking, Mode as PeriMode};\n#[cfg(any(dac_v3, dac_v4, dac_v5, dac_v6, dac_v7))]\nuse crate::pac::dac;\nuse crate::pac::dac::Dac as Regs;\nuse crate::rcc::{self, RccInfo, RccPeripheral, SealedRccPeripheral};\nuse crate::time::Hertz;\nuse crate::{Peri, peripherals};\n\n/// Software trigger\npub struct SOFTWARE;\n\n/// Sawtooth waveform step direction\n#[cfg(stm32g4)]\n#[derive(Debug, Copy, Clone, Eq, PartialEq)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub enum StepDirection {\n    /// Increment dac value every step trigger\n    Increment,\n\n    /// Decrement dac value every step trigger\n    Decrement,\n}\n\n#[cfg(stm32g4)]\nimpl<T: Instance> ChannelIncTrigger<T> for SOFTWARE {\n    fn signal(&self) -> u8 {\n        0\n    }\n}\n\nimpl<T: Instance> ChannelTrigger<T> for SOFTWARE {\n    fn signal(&self) -> u8 {\n        #[cfg(any(\n            stm32l4_plus,\n            stm32l5,\n            stm32u5,\n            stm32u3,\n            stm32h7,\n            stm32h5,\n            stm32g0,\n            stm32u0,\n            stm32g4,\n            stm32wl\n        ))]\n        const SOFTWARE_TRIG: u8 = 0;\n\n        #[cfg(not(any(\n            stm32l4_plus,\n            stm32l5,\n            stm32u5,\n            stm32u3,\n            stm32h7,\n            stm32h5,\n            stm32g0,\n            stm32u0,\n            stm32g4,\n            stm32wl\n        )))]\n        const SOFTWARE_TRIG: u8 = 7;\n\n        SOFTWARE_TRIG\n    }\n}\n\n/// Operating mode for DAC channel\n#[cfg(any(dac_v3, dac_v4, dac_v5, dac_v6, dac_v7))]\n#[derive(Debug, Copy, Clone, Eq, PartialEq)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub enum Mode {\n    /// Normal mode, channel is connected to external pin with buffer enabled.\n    NormalExternalBuffered,\n    /// Normal mode, channel is connected to external pin and internal peripherals\n    /// with buffer enabled.\n    NormalBothBuffered,\n    /// Normal mode, channel is connected to external pin with buffer disabled.\n    NormalExternalUnbuffered,\n    /// Normal mode, channel is connected to internal peripherals with buffer disabled.\n    NormalInternalUnbuffered,\n    /// Sample-and-hold mode, channel is connected to external pin with buffer enabled.\n    SampleHoldExternalBuffered,\n    /// Sample-and-hold mode, channel is connected to external pin and internal peripherals\n    /// with buffer enabled.\n    SampleHoldBothBuffered,\n    /// Sample-and-hold mode, channel is connected to external pin and internal peripherals\n    /// with buffer disabled.\n    SampleHoldBothUnbuffered,\n    /// Sample-and-hold mode, channel is connected to internal peripherals with buffer disabled.\n    SampleHoldInternalUnbuffered,\n}\n\n#[cfg(any(dac_v3, dac_v4, dac_v5, dac_v6, dac_v7))]\nimpl Mode {\n    fn mode(&self) -> dac::vals::Mode {\n        match self {\n            Mode::NormalExternalBuffered => dac::vals::Mode::NORMAL_EXT_BUFEN,\n            Mode::NormalBothBuffered => dac::vals::Mode::NORMAL_EXT_INT_BUFEN,\n            Mode::NormalExternalUnbuffered => dac::vals::Mode::NORMAL_EXT_BUFDIS,\n            Mode::NormalInternalUnbuffered => dac::vals::Mode::NORMAL_INT_BUFDIS,\n            Mode::SampleHoldExternalBuffered => dac::vals::Mode::SAMPHOLD_EXT_BUFEN,\n            Mode::SampleHoldBothBuffered => dac::vals::Mode::SAMPHOLD_EXT_INT_BUFEN,\n            Mode::SampleHoldBothUnbuffered => dac::vals::Mode::SAMPHOLD_EXT_INT_BUFDIS,\n            Mode::SampleHoldInternalUnbuffered => dac::vals::Mode::SAMPHOLD_INT_BUFDIS,\n        }\n    }\n}\n\n#[derive(Debug, Copy, Clone, Eq, PartialEq)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\n/// Single 8 or 12 bit value that can be output by the DAC.\n///\n/// 12-bit values outside the permitted range are silently truncated.\npub enum Value {\n    /// 8 bit value\n    Bit8(u8),\n    /// 12 bit value stored in a u16, left-aligned\n    Bit12Left(u16),\n    /// 12 bit value stored in a u16, right-aligned\n    Bit12Right(u16),\n}\n\n#[derive(Debug, Copy, Clone, Eq, PartialEq)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\n/// Dual 8 or 12 bit values that can be output by the DAC channels 1 and 2 simultaneously.\n///\n/// 12-bit values outside the permitted range are silently truncated.\npub enum DualValue {\n    /// 8 bit value\n    Bit8(u8, u8),\n    /// 12 bit value stored in a u16, left-aligned\n    Bit12Left(u16, u16),\n    /// 12 bit value stored in a u16, right-aligned\n    Bit12Right(u16, u16),\n}\n\n#[derive(Debug, Copy, Clone, Eq, PartialEq)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\n/// Array variant of [`Value`].\npub enum ValueArray<'a> {\n    /// 8 bit values\n    Bit8(&'a [u8]),\n    /// 12 bit value stored in a u16, left-aligned\n    Bit12Left(&'a [u16]),\n    /// 12 bit values stored in a u16, right-aligned\n    Bit12Right(&'a [u16]),\n}\n\n#[derive(Debug)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\nenum ChannelEvent {\n    Enable,\n    Disable,\n}\n\nstruct InnerState {\n    channel_count: usize,\n}\n\ntype SharedState = embassy_sync::blocking_mutex::Mutex<CriticalSectionRawMutex, core::cell::RefCell<InnerState>>;\nstruct State {\n    state: SharedState,\n}\n\nimpl State {\n    /// Adjusts the channel count in response to a `ChannelEvent`, returning the updated value.\n    pub fn adjust_channel_count(&self, event: ChannelEvent) -> usize {\n        self.state.lock(|state| {\n            {\n                let mut mut_state = state.borrow_mut();\n                match event {\n                    ChannelEvent::Enable => {\n                        mut_state.channel_count += 1;\n                    }\n                    ChannelEvent::Disable => {\n                        mut_state.channel_count -= 1;\n                    }\n                };\n            }\n            state.borrow().channel_count\n        })\n    }\n}\n/// Driver for a single DAC channel.\n///\n/// If you want to use both channels, either together or independently,\n/// create a [`Dac`] first and use it to access each channel.\npub struct DacChannel<'d, M: PeriMode> {\n    phantom: PhantomData<&'d mut M>,\n    #[allow(unused)]\n    dma: Option<ChannelAndRequest<'d>>,\n    info: &'static Info,\n    state: &'static State,\n    _ker_clk: Hertz,\n    idx: usize,\n}\n\nimpl<'d> DacChannel<'d, Async> {\n    /// Create a new `DacChannel` instance, consuming the underlying DAC peripheral.\n    ///\n    /// The channel is enabled on creation and begin to drive the output pin.\n    /// Note that some methods, such as `set_trigger()` and `set_mode()`, will\n    /// disable the channel; you must re-enable it with `enable()`.\n    pub fn new<T: Instance, C: Channel, D: Dma<T, C>>(\n        peri: Peri<'d, T>,\n        dma: Peri<'d, D>,\n        _irq: impl crate::interrupt::typelevel::Binding<D::Interrupt, crate::dma::InterruptHandler<D>> + 'd,\n        pin: Peri<'d, impl DacPin<T, C>>,\n    ) -> Self {\n        pin.set_as_analog();\n        Self::new_inner::<T, C>(\n            peri,\n            None,\n            new_dma!(dma, _irq),\n            #[cfg(any(dac_v3, dac_v4, dac_v5, dac_v6, dac_v7))]\n            Mode::NormalExternalBuffered,\n            #[cfg(stm32g4)]\n            vals::Wave::DISABLED,\n            #[cfg(stm32g4)]\n            None,\n        )\n    }\n\n    /// Create a new `DacChannel` instance, consuming the underlying DAC peripheral.\n    ///\n    /// The channel is enabled on creation and begin to drive the output pin.\n    /// Note that some methods, such as `set_trigger()` and `set_mode()`, will\n    /// disable the channel; you must re-enable it with `enable()`.\n    pub fn new_triggered<T: Instance, C: Channel, D: Dma<T, C>>(\n        peri: Peri<'d, T>,\n        dma: Peri<'d, D>,\n        trigger: impl ChannelTrigger<T>,\n        _irq: impl crate::interrupt::typelevel::Binding<D::Interrupt, crate::dma::InterruptHandler<D>> + 'd,\n        pin: Peri<'d, impl DacPin<T, C>>,\n    ) -> Self {\n        pin.set_as_analog();\n        Self::new_inner::<T, C>(\n            peri,\n            Some(trigger.signal()),\n            new_dma!(dma, _irq),\n            #[cfg(any(dac_v3, dac_v4, dac_v5, dac_v6, dac_v7))]\n            Mode::NormalExternalBuffered,\n            #[cfg(stm32g4)]\n            vals::Wave::DISABLED,\n            #[cfg(stm32g4)]\n            None,\n        )\n    }\n\n    /// Create a new `DacChannel` instance where the external output pin is not used,\n    /// so the DAC can only be used to generate internal signals.\n    /// The GPIO pin is therefore available to be used for other functions.\n    ///\n    /// The channel is set to [`Mode::NormalInternalUnbuffered`] and enabled on creation.\n    /// Note that some methods, such as `set_trigger()` and `set_mode()`, will disable the\n    /// channel; you must re-enable it with `enable()`.\n    #[cfg(all(any(dac_v3, dac_v4, dac_v5, dac_v6, dac_v7), not(any(stm32h56x, stm32h57x))))]\n    pub fn new_internal<T: Instance, C: Channel, D: Dma<T, C>>(\n        peri: Peri<'d, T>,\n        dma: Peri<'d, D>,\n        _irq: impl crate::interrupt::typelevel::Binding<D::Interrupt, crate::dma::InterruptHandler<D>> + 'd,\n    ) -> Self {\n        Self::new_inner::<T, C>(\n            peri,\n            None,\n            new_dma!(dma, _irq),\n            Mode::NormalInternalUnbuffered,\n            #[cfg(stm32g4)]\n            vals::Wave::DISABLED,\n            #[cfg(stm32g4)]\n            None,\n        )\n    }\n\n    /// Create a new `DacChannel` instance where the external output pin is not used,\n    /// so the DAC can only be used to generate internal signals.\n    /// The GPIO pin is therefore available to be used for other functions.\n    ///\n    /// The channel is set to [`Mode::NormalInternalUnbuffered`] and enabled on creation.\n    /// Note that some methods, such as `set_trigger()` and `set_mode()`, will disable the\n    /// channel; you must re-enable it with `enable()`.\n    #[cfg(all(any(dac_v3, dac_v4, dac_v5, dac_v6, dac_v7), not(any(stm32h56x, stm32h57x))))]\n    pub fn new_triggered_internal<T: Instance, C: Channel, D: Dma<T, C>>(\n        peri: Peri<'d, T>,\n        dma: Peri<'d, D>,\n        trigger: impl ChannelTrigger<T>,\n        _irq: impl crate::interrupt::typelevel::Binding<D::Interrupt, crate::dma::InterruptHandler<D>> + 'd,\n    ) -> Self {\n        Self::new_inner::<T, C>(\n            peri,\n            Some(trigger.signal()),\n            new_dma!(dma, _irq),\n            Mode::NormalInternalUnbuffered,\n            #[cfg(stm32g4)]\n            vals::Wave::DISABLED,\n            #[cfg(stm32g4)]\n            None,\n        )\n    }\n\n    /// Write `data` to this channel via DMA.\n    ///\n    /// To prevent delays or glitches when outputing a periodic waveform, the `circular`\n    /// flag can be set. This configures a circular DMA transfer that continually outputs\n    /// `data`. Note that for performance reasons in circular mode the transfer-complete\n    /// interrupt is disabled.\n    #[cfg(not(gpdma))]\n    pub async fn write(&mut self, data: ValueArray<'_>, circular: bool) {\n        // Enable DAC and DMA\n        self.info.regs.cr().modify(|w| {\n            w.set_en(self.idx, true);\n            w.set_dmaen(self.idx, true);\n        });\n\n        let dma = self.dma.as_mut().unwrap();\n\n        let tx_options = crate::dma::TransferOptions {\n            circular,\n            half_transfer_ir: false,\n            complete_transfer_ir: !circular,\n            ..Default::default()\n        };\n\n        // Initiate the correct type of DMA transfer depending on what data is passed\n        let tx_f = match data {\n            ValueArray::Bit8(buf) => unsafe {\n                dma.write(buf, self.info.regs.dhr8r(self.idx).as_ptr() as *mut u8, tx_options)\n            },\n            ValueArray::Bit12Left(buf) => unsafe {\n                dma.write(buf, self.info.regs.dhr12l(self.idx).as_ptr() as *mut u16, tx_options)\n            },\n            ValueArray::Bit12Right(buf) => unsafe {\n                dma.write(buf, self.info.regs.dhr12r(self.idx).as_ptr() as *mut u16, tx_options)\n            },\n        };\n\n        tx_f.await;\n\n        self.info.regs.cr().modify(|w| {\n            w.set_en(self.idx, false);\n            w.set_dmaen(self.idx, false);\n        });\n    }\n}\n\nimpl<'d> DacChannel<'d, Blocking> {\n    /// Create a new `DacChannel` instance, consuming the underlying DAC peripheral.\n    ///\n    /// The channel is enabled on creation and begin to drive the output pin.\n    /// Note that some methods, such as `set_trigger()` and `set_mode()`, will\n    /// disable the channel; you must re-enable it with `enable()`.\n    ///\n    /// By default, triggering is disabled, but it can be enabled using\n    /// [`DacChannel::set_trigger()`].\n    pub fn new_blocking<T: Instance, C: Channel>(peri: Peri<'d, T>, pin: Peri<'d, impl DacPin<T, C>>) -> Self {\n        pin.set_as_analog();\n        Self::new_inner::<T, C>(\n            peri,\n            None,\n            None,\n            #[cfg(any(dac_v3, dac_v4, dac_v5, dac_v6, dac_v7))]\n            Mode::NormalExternalBuffered,\n            #[cfg(stm32g4)]\n            vals::Wave::DISABLED,\n            #[cfg(stm32g4)]\n            None,\n        )\n    }\n\n    /// Create a new `DacChannel` instance where the external output pin is not used,\n    /// so the DAC can only be used to generate internal signals.\n    /// The GPIO pin is therefore available to be used for other functions.\n    ///\n    /// The channel is set to [`Mode::NormalInternalUnbuffered`] and enabled on creation.\n    /// Note that some methods, such as `set_trigger()` and `set_mode()`, will disable the\n    /// channel; you must re-enable it with `enable()`.\n    ///\n    /// By default, triggering is disabled, but it can be enabled using\n    /// [`DacChannel::set_trigger()`].\n    #[cfg(all(any(dac_v3, dac_v4, dac_v5, dac_v6, dac_v7), not(any(stm32h56x, stm32h57x))))]\n    pub fn new_internal_blocking<T: Instance, C: Channel>(peri: Peri<'d, T>) -> Self {\n        Self::new_inner::<T, C>(\n            peri,\n            None,\n            None,\n            Mode::NormalInternalUnbuffered,\n            #[cfg(stm32g4)]\n            vals::Wave::DISABLED,\n            #[cfg(stm32g4)]\n            None,\n        )\n    }\n\n    /// Create a new `DacChannel` instance with sawtooth mode enabled\n    ///\n    /// See [Self::set_sawtooth_reset_value], [Self::set_sawtooth_step_value] and [Self::set_sawtooth_step_direction]\n    /// for setting the reset value, step size and -direction.\n    ///\n    /// This method disables the channel, so you may need to re-enable afterwards.\n    #[cfg(stm32g4)]\n    pub fn new_sawtooth<T: Instance, C: Channel>(\n        peri: Peri<'d, T>,\n        reset_trigger: impl ChannelTrigger<T>,\n        step_trigger: impl ChannelIncTrigger<T>,\n        pin: Peri<'d, impl DacPin<T, C>>,\n    ) -> Self {\n        pin.set_as_analog();\n        Self::new_inner::<T, C>(\n            peri,\n            Some(reset_trigger.signal()),\n            None,\n            Mode::NormalExternalBuffered,\n            vals::Wave::SAWTOOTH,\n            Some(step_trigger.signal()),\n        )\n    }\n\n    /// Create a new `DacChannel` instance with sawtooth mode enabled where the external output pin is not used,\n    /// so the DAC can only be used to generate internal signals.\n    /// The GPIO pin is therefore available to be used for other functions.\n    ///\n    /// See [Self::set_sawtooth_reset_value], [Self::set_sawtooth_step_value] and [Self::set_sawtooth_step_direction]\n    /// for setting the reset value, step size and -direction.\n    ///\n    /// This method disables the channel, so you may need to re-enable afterwards.\n    #[cfg(stm32g4)]\n    pub fn new_sawtooth_internal<T: Instance, C: Channel>(\n        peri: Peri<'d, T>,\n        reset_trigger: impl ChannelTrigger<T>,\n        step_trigger: impl ChannelIncTrigger<T>,\n    ) -> Self {\n        Self::new_inner::<T, C>(\n            peri,\n            Some(reset_trigger.signal()),\n            None,\n            Mode::NormalInternalUnbuffered,\n            vals::Wave::SAWTOOTH,\n            Some(step_trigger.signal()),\n        )\n    }\n}\n\nimpl<'d, M: PeriMode> DacChannel<'d, M> {\n    fn new_inner<T: Instance, C: Channel>(\n        _peri: Peri<'d, T>,\n        trigger: Option<u8>,\n        dma: Option<ChannelAndRequest<'d>>,\n        #[cfg(any(dac_v3, dac_v4, dac_v5, dac_v6, dac_v7))] mode: Mode,\n        #[cfg(stm32g4)] wave: dac::vals::Wave,\n        #[cfg(stm32g4)] inc_trigger: Option<u8>,\n    ) -> Self {\n        rcc::enable_and_reset::<T>();\n        let mut dac = Self {\n            phantom: PhantomData,\n            info: T::info(),\n            state: T::state(),\n            _ker_clk: T::frequency(),\n            idx: C::IDX,\n            dma,\n        };\n        #[cfg(any(dac_v5, dac_v6, dac_v7))]\n        dac.set_hfsel();\n        #[cfg(any(dac_v3, dac_v4, dac_v5, dac_v6, dac_v7))]\n        dac.set_mode(mode);\n\n        #[cfg(stm32g4)]\n        dac.set_wave(wave);\n        trigger.map(|idx| {\n            dac.info.regs.cr().modify(|reg| {\n                reg.set_tsel(dac.idx, idx);\n            });\n\n            // Set in case Sawtooth wave form is used\n            #[cfg(stm32g4)]\n            dac.info.regs.stmodr().modify(|reg| {\n                reg.set_strsttrigsel(dac.idx, idx);\n            });\n        });\n        #[cfg(stm32g4)]\n        inc_trigger.map(|idx| {\n            dac.info.regs.stmodr().modify(|reg| {\n                reg.set_stinctrigsel(dac.idx, idx);\n            })\n        });\n        dac.enable();\n        dac\n    }\n\n    /// Enable or disable this channel.\n    pub fn set_enable(&mut self, on: bool) {\n        critical_section::with(|_| {\n            self.info.regs.cr().modify(|reg| {\n                reg.set_en(self.idx, on);\n            });\n        });\n        let event = if on {\n            ChannelEvent::Enable\n        } else {\n            ChannelEvent::Disable\n        };\n        let channel_count = self.state.adjust_channel_count(event);\n        // Disable the DAC only if no more channels are using it.\n        if channel_count == 0 {\n            self.info.rcc.disable();\n        }\n    }\n\n    /// Enable this channel.\n    pub fn enable(&mut self) {\n        self.set_enable(true)\n    }\n\n    /// Disable this channel.\n    pub fn disable(&mut self) {\n        self.set_enable(false)\n    }\n\n    /// Enable or disable triggering for this channel.\n    pub fn set_triggering(&mut self, on: bool) {\n        critical_section::with(|_| {\n            self.info.regs.cr().modify(|reg| {\n                reg.set_ten(self.idx, on);\n            });\n        });\n    }\n\n    /// Software trigger this channel.\n    ///\n    /// NOTE: In sawtooth mode, this only works with [SOFTWARE] as reset_trigger source\n    pub fn trigger(&mut self) {\n        self.info.regs.swtrigr().write(|reg| {\n            reg.set_swtrig(self.idx, true);\n        });\n    }\n\n    /// Software trigger this channels sawtooth waveform step\n    ///\n    /// NOTE: This only works with [SOFTWARE] as reset_trigger source\n    #[cfg(stm32g4)]\n    pub fn trigger_step(&mut self) {\n        self.info.regs.swtrigr().write(|reg| {\n            reg.set_swtrigb(self.idx, true);\n        });\n    }\n\n    /// Set mode of this channel.\n    ///\n    /// This method disables the channel, so you may need to re-enable afterwards.\n    #[cfg(any(dac_v3, dac_v4, dac_v5, dac_v6, dac_v7))]\n    pub fn set_mode(&mut self, mode: Mode) {\n        critical_section::with(|_| {\n            self.info.regs.cr().modify(|reg| {\n                reg.set_en(self.idx, false);\n            });\n            self.info.regs.mcr().modify(|reg| {\n                reg.set_mode(self.idx, mode.mode());\n            });\n        });\n    }\n\n    /// Set mode of this channel.\n    ///\n    /// This method disables the channel, so you may need to re-enable afterwards.\n    #[cfg(stm32g4)]\n    fn set_wave(&mut self, wave: dac::vals::Wave) {\n        critical_section::with(|_| {\n            self.info.regs.cr().modify(|reg| {\n                reg.set_en(self.idx, false);\n                reg.set_wave(self.idx, wave);\n            });\n        });\n    }\n\n    /// Write a new value to this channel.\n    ///\n    /// If triggering is not enabled, the new value is immediately output; otherwise,\n    /// it will be output after the next trigger.\n    pub fn set(&mut self, value: Value) {\n        match value {\n            Value::Bit8(v) => self.info.regs.dhr8r(self.idx).write(|reg| reg.set_dhr(v)),\n            Value::Bit12Left(v) => self.info.regs.dhr12l(self.idx).write(|reg| reg.set_dhr(v)),\n            Value::Bit12Right(v) => self.info.regs.dhr12r(self.idx).write(|reg| reg.set_dhr(v)),\n        }\n    }\n\n    /// Read the current output value of the DAC.\n    pub fn read(&self) -> u16 {\n        self.info.regs.dor(self.idx).read().dor()\n    }\n\n    /// Set sawtooth reset value set on every reset trigger\n    ///\n    /// This is only used when the channel is in sawtooth waveform mode\n    #[cfg(stm32g4)]\n    pub fn set_sawtooth_reset_value(&mut self, value: u16) {\n        self.info.regs.str(self.idx).modify(|reg| reg.set_rstdata(value));\n    }\n\n    /// Set sawtooth step value (12.4 bit format)\n    ///\n    /// See [Self::set_sawtooth_step_direction] for setting the step direction\n    /// and [Self::set_sawtooth_mode] for setting sawtooth mode.\n    ///\n    /// NOTE: This is only used when the channel is in sawtooth waveform mode\n    #[cfg(stm32g4)]\n    pub fn set_sawtooth_step_value(&mut self, value: u16) {\n        self.info.regs.str(self.idx).modify(|reg| reg.set_incdata(value));\n    }\n\n    /// Set sawtooth step direction\n    ///\n    /// See [Self::set_sawtooth_step_value] for setting the step value\n    /// and [Self::set_sawtooth_mode] for setting sawtooth mode.\n    ///\n    /// NOTE: This is only used when the channel is in sawtooth waveform mode\n    #[cfg(stm32g4)]\n    pub fn set_sawtooth_step_direction(&mut self, value: StepDirection) {\n        self.info\n            .regs\n            .str(self.idx)\n            .modify(|reg| reg.set_dir(matches!(value, StepDirection::Increment)));\n    }\n\n    /// Set HFSEL as appropriate for the current peripheral clock frequency.\n    #[cfg(dac_v5)]\n    fn set_hfsel(&mut self) {\n        if self._ker_clk >= crate::time::mhz(80) {\n            critical_section::with(|_| {\n                self.info.regs.cr().modify(|reg| {\n                    reg.set_hfsel(true);\n                });\n            });\n        }\n    }\n\n    /// Set HFSEL as appropriate for the current peripheral clock frequency.\n    #[cfg(any(dac_v6, dac_v7))]\n    fn set_hfsel(&mut self) {\n        if self._ker_clk >= crate::time::mhz(160) {\n            critical_section::with(|_| {\n                self.info.regs.mcr().modify(|reg| {\n                    reg.set_hfsel(0b10);\n                });\n            });\n        } else if self._ker_clk >= crate::time::mhz(80) {\n            critical_section::with(|_| {\n                self.info.regs.mcr().modify(|reg| {\n                    reg.set_hfsel(0b01);\n                });\n            });\n        }\n    }\n}\n\nimpl<'d, M: PeriMode> Drop for DacChannel<'d, M> {\n    fn drop(&mut self) {\n        self.disable();\n    }\n}\n\n/// DAC driver.\n///\n/// Use this struct when you want to use both channels, either together or independently.\n///\n/// # Example\n///\n/// ```ignore\n/// // Pins may need to be changed for your specific device.\n/// let (dac_ch1, dac_ch2) = embassy_stm32::dac::Dac::new_blocking(p.DAC1, p.PA4, p.PA5).split();\n/// ```\npub struct Dac<'d, M: PeriMode> {\n    info: &'static Info,\n    ch1: DacChannel<'d, M>,\n    ch2: DacChannel<'d, M>,\n}\n\nimpl<'d> Dac<'d, Async> {\n    /// Create a new `Dac` instance, consuming the underlying DAC peripheral.\n    ///\n    /// This struct allows you to access both channels of the DAC, where available. You can either\n    /// call `split()` to obtain separate `DacChannel`s, or use methods on `Dac` to use\n    /// the two channels together.\n    ///\n    /// The channels are enabled on creation and begin to drive their output pins.\n    /// Note that some methods, such as `set_trigger()` and `set_mode()`, will\n    /// disable the channel; you must re-enable them with `enable()`.\n    ///\n    /// By default, triggering is disabled, but it can be enabled using the `set_trigger()`\n    /// method on the underlying channels.\n    pub fn new<T: Instance, D1: Dma<T, Ch1>, D2: Dma<T, Ch2>>(\n        peri: Peri<'d, T>,\n        dma_ch1: Peri<'d, D1>,\n        dma_ch2: Peri<'d, D2>,\n        _irq: impl crate::interrupt::typelevel::Binding<D1::Interrupt, crate::dma::InterruptHandler<D1>>\n        + crate::interrupt::typelevel::Binding<D2::Interrupt, crate::dma::InterruptHandler<D2>>\n        + 'd,\n        pin_ch1: Peri<'d, impl DacPin<T, Ch1> + crate::gpio::Pin>,\n        pin_ch2: Peri<'d, impl DacPin<T, Ch2> + crate::gpio::Pin>,\n    ) -> Self {\n        pin_ch1.set_as_analog();\n        pin_ch2.set_as_analog();\n        Self::new_inner(\n            peri,\n            None,\n            None,\n            new_dma!(dma_ch1, _irq),\n            new_dma!(dma_ch2, _irq),\n            #[cfg(any(dac_v3, dac_v4, dac_v5, dac_v6, dac_v7))]\n            Mode::NormalExternalBuffered,\n        )\n    }\n\n    /// Create a new `Dac` instance, consuming the underlying DAC peripheral.\n    ///\n    /// This struct allows you to access both channels of the DAC, where available. You can either\n    /// call `split()` to obtain separate `DacChannel`s, or use methods on `Dac` to use\n    /// the two channels together.\n    ///\n    /// The channels are enabled on creation and begin to drive their output pins.\n    /// Note that some methods, such as `set_trigger()` and `set_mode()`, will\n    /// disable the channel; you must re-enable them with `enable()`.\n    ///\n    /// By default, triggering is disabled, but it can be enabled using the `set_trigger()`\n    /// method on the underlying channels.\n    pub fn new_triggered<T: Instance, D1: Dma<T, Ch1>, D2: Dma<T, Ch2>>(\n        peri: Peri<'d, T>,\n        dma_ch1: Peri<'d, D1>,\n        dma_ch2: Peri<'d, D2>,\n        trigger_ch1: impl ChannelTrigger<T>,\n        trigger_ch2: impl ChannelTrigger<T>,\n        _irq: impl crate::interrupt::typelevel::Binding<D1::Interrupt, crate::dma::InterruptHandler<D1>>\n        + crate::interrupt::typelevel::Binding<D2::Interrupt, crate::dma::InterruptHandler<D2>>\n        + 'd,\n        pin_ch1: Peri<'d, impl DacPin<T, Ch1> + crate::gpio::Pin>,\n        pin_ch2: Peri<'d, impl DacPin<T, Ch2> + crate::gpio::Pin>,\n    ) -> Self {\n        pin_ch1.set_as_analog();\n        pin_ch2.set_as_analog();\n\n        Self::new_inner(\n            peri,\n            Some(trigger_ch1.signal()),\n            Some(trigger_ch2.signal()),\n            new_dma!(dma_ch1, _irq),\n            new_dma!(dma_ch2, _irq),\n            #[cfg(any(dac_v3, dac_v4, dac_v5, dac_v6, dac_v7))]\n            Mode::NormalExternalBuffered,\n        )\n    }\n\n    /// Create a new `Dac` instance with external output pins and unbuffered mode.\n    ///\n    /// This function consumes the underlying DAC peripheral and allows access to both channels.\n    /// The channels are configured for external output with the buffer disabled.\n    ///\n    /// The channels are enabled on creation and begin to drive their output pins.\n    /// Note that some methods, such as `set_trigger()` and `set_mode()`, will\n    /// disable the channel; you must re-enable them with `enable()`.\n    ///\n    /// By default, triggering is disabled, but it can be enabled using the `set_trigger()`\n    /// method on the underlying channels.\n    ///\n    /// # Arguments\n    ///\n    /// * `peri` - The DAC peripheral instance.\n    /// * `dma_ch1` - The DMA channel for DAC channel 1.\n    /// * `dma_ch2` - The DMA channel for DAC channel 2.\n    /// * `_irq` - The interrupt binding for DMA channels 1 and 2.\n    /// * `pin_ch1` - The GPIO pin for DAC channel 1 output.\n    /// * `pin_ch2` - The GPIO pin for DAC channel 2 output.\n    ///\n    /// # Returns\n    ///\n    /// A new `Dac` instance in unbuffered mode.\n    pub fn new_unbuffered<T: Instance, D1: Dma<T, Ch1>, D2: Dma<T, Ch2>>(\n        peri: Peri<'d, T>,\n        dma_ch1: Peri<'d, D1>,\n        dma_ch2: Peri<'d, D2>,\n        _irq: impl crate::interrupt::typelevel::Binding<D1::Interrupt, crate::dma::InterruptHandler<D1>>\n        + crate::interrupt::typelevel::Binding<D2::Interrupt, crate::dma::InterruptHandler<D2>>\n        + 'd,\n        pin_ch1: Peri<'d, impl DacPin<T, Ch1> + crate::gpio::Pin>,\n        pin_ch2: Peri<'d, impl DacPin<T, Ch2> + crate::gpio::Pin>,\n    ) -> Self {\n        pin_ch1.set_as_analog();\n        pin_ch2.set_as_analog();\n        Self::new_inner(\n            peri,\n            None,\n            None,\n            new_dma!(dma_ch1, _irq),\n            new_dma!(dma_ch2, _irq),\n            #[cfg(any(dac_v3, dac_v4, dac_v5, dac_v6, dac_v7))]\n            Mode::NormalExternalUnbuffered,\n        )\n    }\n\n    /// Create a new `Dac` instance, consuming the underlying DAC peripheral.\n    ///\n    /// This struct allows you to access both channels of the DAC, where available. You can either\n    /// call `split()` to obtain separate `DacChannel`s, or use methods on `Dac` to use\n    /// the two channels together.\n    ///\n    /// The channels are enabled on creation and begin to drive their output pins.\n    /// Note that some methods, such as `set_trigger()` and `set_mode()`, will\n    /// disable the channel; you must re-enable them with `enable()`.\n    ///\n    /// By default, triggering is disabled, but it can be enabled using the `set_trigger()`\n    /// method on the underlying channels.\n    pub fn new_triggered_unbuffered<T: Instance, D1: Dma<T, Ch1>, D2: Dma<T, Ch2>>(\n        peri: Peri<'d, T>,\n        dma_ch1: Peri<'d, D1>,\n        dma_ch2: Peri<'d, D2>,\n        trigger_ch1: impl ChannelTrigger<T>,\n        trigger_ch2: impl ChannelTrigger<T>,\n        _irq: impl crate::interrupt::typelevel::Binding<D1::Interrupt, crate::dma::InterruptHandler<D1>>\n        + crate::interrupt::typelevel::Binding<D2::Interrupt, crate::dma::InterruptHandler<D2>>\n        + 'd,\n        pin_ch1: Peri<'d, impl DacPin<T, Ch1> + crate::gpio::Pin>,\n        pin_ch2: Peri<'d, impl DacPin<T, Ch2> + crate::gpio::Pin>,\n    ) -> Self {\n        pin_ch1.set_as_analog();\n        pin_ch2.set_as_analog();\n\n        Self::new_inner(\n            peri,\n            Some(trigger_ch1.signal()),\n            Some(trigger_ch2.signal()),\n            new_dma!(dma_ch1, _irq),\n            new_dma!(dma_ch2, _irq),\n            #[cfg(any(dac_v3, dac_v4, dac_v5, dac_v6, dac_v7))]\n            Mode::NormalExternalUnbuffered,\n        )\n    }\n\n    /// Create a new `Dac` instance where the external output pins are not used,\n    /// so the DAC can only be used to generate internal signals but the GPIO\n    /// pins remain available for other functions.\n    ///\n    /// This struct allows you to access both channels of the DAC, where available. You can either\n    /// call `split()` to obtain separate `DacChannel`s, or use methods on `Dac` to use the two\n    /// channels together.\n    ///\n    /// The channels are set to [`Mode::NormalInternalUnbuffered`] and enabled on creation.\n    /// Note that some methods, such as `set_trigger()` and `set_mode()`, will disable the\n    /// channel; you must re-enable them with `enable()`.\n    ///\n    /// By default, triggering is disabled, but it can be enabled using the `set_trigger()`\n    /// method on the underlying channels.\n    #[cfg(all(any(dac_v3, dac_v4, dac_v5, dac_v6, dac_v7), not(any(stm32h56x, stm32h57x))))]\n    pub fn new_internal<T: Instance, D1: Dma<T, Ch1>, D2: Dma<T, Ch2>>(\n        peri: Peri<'d, T>,\n        dma_ch1: Peri<'d, D1>,\n        dma_ch2: Peri<'d, D2>,\n        _irq: impl crate::interrupt::typelevel::Binding<D1::Interrupt, crate::dma::InterruptHandler<D1>>\n        + crate::interrupt::typelevel::Binding<D2::Interrupt, crate::dma::InterruptHandler<D2>>\n        + 'd,\n    ) -> Self {\n        Self::new_inner(\n            peri,\n            None,\n            None,\n            new_dma!(dma_ch1, _irq),\n            new_dma!(dma_ch2, _irq),\n            Mode::NormalInternalUnbuffered,\n        )\n    }\n\n    /// Create a new `Dac` instance where the external output pins are not used,\n    /// so the DAC can only be used to generate internal signals but the GPIO\n    /// pins remain available for other functions.\n    ///\n    /// This struct allows you to access both channels of the DAC, where available. You can either\n    /// call `split()` to obtain separate `DacChannel`s, or use methods on `Dac` to use the two\n    /// channels together.\n    ///\n    /// The channels are set to [`Mode::NormalInternalUnbuffered`] and enabled on creation.\n    /// Note that some methods, such as `set_trigger()` and `set_mode()`, will disable the\n    /// channel; you must re-enable them with `enable()`.\n    ///\n    /// By default, triggering is disabled, but it can be enabled using the `set_trigger()`\n    /// method on the underlying channels.\n    #[cfg(all(any(dac_v3, dac_v4, dac_v5, dac_v6, dac_v7), not(any(stm32h56x, stm32h57x))))]\n    pub fn new_triggered_internal<T: Instance, D1: Dma<T, Ch1>, D2: Dma<T, Ch2>>(\n        peri: Peri<'d, T>,\n        trigger_ch1: impl ChannelTrigger<T>,\n        trigger_ch2: impl ChannelTrigger<T>,\n        dma_ch1: Peri<'d, D1>,\n        dma_ch2: Peri<'d, D2>,\n        _irq: impl crate::interrupt::typelevel::Binding<D1::Interrupt, crate::dma::InterruptHandler<D1>>\n        + crate::interrupt::typelevel::Binding<D2::Interrupt, crate::dma::InterruptHandler<D2>>\n        + 'd,\n    ) -> Self {\n        Self::new_inner(\n            peri,\n            Some(trigger_ch1.signal()),\n            Some(trigger_ch2.signal()),\n            new_dma!(dma_ch1, _irq),\n            new_dma!(dma_ch2, _irq),\n            Mode::NormalInternalUnbuffered,\n        )\n    }\n}\n\nimpl<'d> Dac<'d, Blocking> {\n    /// Create a new `Dac` instance, consuming the underlying DAC peripheral.\n    ///\n    /// This struct allows you to access both channels of the DAC, where available. You can either\n    /// call `split()` to obtain separate `DacChannel`s, or use methods on `Dac` to use\n    /// the two channels together.\n    ///\n    /// The channels are enabled on creation and begin to drive their output pins.\n    /// Note that some methods, such as `set_trigger()` and `set_mode()`, will\n    /// disable the channel; you must re-enable them with `enable()`.\n    ///\n    /// By default, triggering is disabled, but it can be enabled using the `set_trigger()`\n    /// method on the underlying channels.\n    pub fn new_blocking<T: Instance>(\n        peri: Peri<'d, T>,\n        pin_ch1: Peri<'d, impl DacPin<T, Ch1> + crate::gpio::Pin>,\n        pin_ch2: Peri<'d, impl DacPin<T, Ch2> + crate::gpio::Pin>,\n    ) -> Self {\n        pin_ch1.set_as_analog();\n        pin_ch2.set_as_analog();\n        Self::new_inner(\n            peri,\n            None,\n            None,\n            None,\n            None,\n            #[cfg(any(dac_v3, dac_v4, dac_v5, dac_v6, dac_v7))]\n            Mode::NormalExternalBuffered,\n        )\n    }\n\n    /// Create a new `Dac` instance where the external output pins are not used,\n    /// so the DAC can only be used to generate internal signals but the GPIO\n    /// pins remain available for other functions.\n    ///\n    /// This struct allows you to access both channels of the DAC, where available. You can either\n    /// call `split()` to obtain separate `DacChannel`s, or use methods on `Dac` to use the two\n    /// channels together.\n    ///\n    /// The channels are set to [`Mode::NormalInternalUnbuffered`] and enabled on creation.\n    /// Note that some methods, such as `set_trigger()` and `set_mode()`, will disable the\n    /// channel; you must re-enable them with `enable()`.\n    ///\n    /// By default, triggering is disabled, but it can be enabled using the `set_trigger()`\n    /// method on the underlying channels.\n    #[cfg(all(any(dac_v3, dac_v4, dac_v5, dac_v6, dac_v7), not(any(stm32h56x, stm32h57x))))]\n    pub fn new_internal<T: Instance>(peri: Peri<'d, T>) -> Self {\n        Self::new_inner(peri, None, None, None, None, Mode::NormalInternalUnbuffered)\n    }\n}\n\nimpl<'d, M: PeriMode> Dac<'d, M> {\n    fn new_inner<T: Instance>(\n        _peri: Peri<'d, T>,\n        trigger_ch1: Option<u8>,\n        trigger_ch2: Option<u8>,\n        dma_ch1: Option<ChannelAndRequest<'d>>,\n        dma_ch2: Option<ChannelAndRequest<'d>>,\n        #[cfg(any(dac_v3, dac_v4, dac_v5, dac_v6, dac_v7))] mode: Mode,\n    ) -> Self {\n        rcc::enable_and_reset::<T>();\n\n        let mut ch1 = DacChannel {\n            phantom: PhantomData,\n            info: T::info(),\n            state: T::state(),\n            _ker_clk: T::frequency(),\n            idx: Ch1::IDX,\n            dma: dma_ch1,\n        };\n        #[cfg(any(dac_v5, dac_v6, dac_v7))]\n        ch1.set_hfsel();\n        #[cfg(any(dac_v3, dac_v4, dac_v5, dac_v6, dac_v7))]\n        ch1.set_mode(mode);\n        trigger_ch1.map(|idx| {\n            T::info().regs.cr().modify(|reg| {\n                reg.set_tsel(0, idx);\n            });\n        });\n        ch1.enable();\n\n        let mut ch2 = DacChannel {\n            phantom: PhantomData,\n            info: T::info(),\n            state: T::state(),\n            _ker_clk: T::frequency(),\n            idx: Ch2::IDX,\n            dma: dma_ch2,\n        };\n        #[cfg(any(dac_v5, dac_v6, dac_v7))]\n        ch2.set_hfsel();\n        #[cfg(any(dac_v3, dac_v4, dac_v5, dac_v6, dac_v7))]\n        ch2.set_mode(mode);\n        trigger_ch2.map(|idx| {\n            T::info().regs.cr().modify(|reg| {\n                reg.set_tsel(1, idx);\n            });\n        });\n        ch2.enable();\n\n        Self {\n            info: T::info(),\n            ch1,\n            ch2,\n        }\n    }\n\n    /// Split this `Dac` into separate channels.\n    ///\n    /// You can access and move the channels around separately after splitting.\n    pub fn split(self) -> (DacChannel<'d, M>, DacChannel<'d, M>) {\n        (self.ch1, self.ch2)\n    }\n\n    /// Temporarily access channel 1.\n    pub fn ch1(&mut self) -> &mut DacChannel<'d, M> {\n        &mut self.ch1\n    }\n\n    /// Temporarily access channel 2.\n    pub fn ch2(&mut self) -> &mut DacChannel<'d, M> {\n        &mut self.ch2\n    }\n\n    /// Simultaneously update channels 1 and 2 with a new value.\n    ///\n    /// If triggering is not enabled, the new values are immediately output;\n    /// otherwise, they will be output after the next trigger.\n    pub fn set(&mut self, values: DualValue) {\n        match values {\n            DualValue::Bit8(v1, v2) => self.info.regs.dhr8rd().write(|reg| {\n                reg.set_dhr(0, v1);\n                reg.set_dhr(1, v2);\n            }),\n            DualValue::Bit12Left(v1, v2) => self.info.regs.dhr12ld().write(|reg| {\n                reg.set_dhr(0, v1);\n                reg.set_dhr(1, v2);\n            }),\n            DualValue::Bit12Right(v1, v2) => self.info.regs.dhr12rd().write(|reg| {\n                reg.set_dhr(0, v1);\n                reg.set_dhr(1, v2);\n            }),\n        }\n    }\n}\n\ntrait SealedInstance {\n    fn info() -> &'static Info;\n\n    fn state() -> &'static State {\n        static STATE: State = State {\n            state: embassy_sync::blocking_mutex::Mutex::new(core::cell::RefCell::new(InnerState { channel_count: 0 })),\n        };\n        &STATE\n    }\n}\n\n/// DAC instance.\n#[allow(private_bounds)]\npub trait Instance: SealedInstance + PeripheralType + RccPeripheral + 'static {}\n\n/// Channel 1 marker type.\npub enum Ch1 {}\n/// Channel 2 marker type.\npub enum Ch2 {}\n\ntrait SealedChannel {\n    const IDX: usize;\n}\n/// DAC channel trait.\n#[allow(private_bounds)]\npub trait Channel: SealedChannel {}\n\nimpl SealedChannel for Ch1 {\n    const IDX: usize = 0;\n}\nimpl SealedChannel for Ch2 {\n    const IDX: usize = 1;\n}\nimpl Channel for Ch1 {}\nimpl Channel for Ch2 {}\n\ntrigger_trait!(ChannelTrigger, Instance);\ntrigger_trait!(ChannelIncTrigger, Instance);\ndma_trait!(Dma, Instance, Channel);\npin_trait!(DacPin, Instance, Channel);\n\nstruct Info {\n    regs: Regs,\n    rcc: RccInfo,\n}\n\nforeach_peripheral!(\n    (dac, $inst:ident) => {\n        impl crate::dac::SealedInstance for peripherals::$inst {\n            fn info() -> &'static Info {\n                static INFO: Info = Info {\n                    regs: unsafe { Regs::from_ptr(crate::pac::$inst.as_ptr()) },\n                    rcc: crate::peripherals::$inst::RCC_INFO,\n                };\n                &INFO\n            }\n        }\n\n        impl crate::dac::Instance for peripherals::$inst {}\n    };\n);\n"
  },
  {
    "path": "embassy-stm32/src/dcmi.rs",
    "content": "//! Digital Camera Interface (DCMI)\nuse core::future::poll_fn;\nuse core::marker::PhantomData;\nuse core::task::Poll;\n\nuse embassy_hal_internal::PeripheralType;\nuse embassy_sync::waitqueue::AtomicWaker;\n\nuse crate::dma::ChannelAndRequest;\nuse crate::gpio::{AfType, Pull};\nuse crate::interrupt::typelevel::Interrupt;\nuse crate::{Peri, interrupt, rcc};\n\n/// Interrupt handler.\npub struct InterruptHandler<T: Instance> {\n    _phantom: PhantomData<T>,\n}\n\nimpl<T: Instance> interrupt::typelevel::Handler<T::Interrupt> for InterruptHandler<T> {\n    unsafe fn on_interrupt() {\n        let ris = crate::pac::DCMI.ris().read();\n        if ris.err_ris() {\n            trace!(\"DCMI IRQ: Error.\");\n            crate::pac::DCMI.ier().modify(|ier| ier.set_err_ie(false));\n        }\n        if ris.ovr_ris() {\n            trace!(\"DCMI IRQ: Overrun.\");\n            crate::pac::DCMI.ier().modify(|ier| ier.set_ovr_ie(false));\n        }\n        if ris.frame_ris() {\n            trace!(\"DCMI IRQ: Frame captured.\");\n            crate::pac::DCMI.ier().modify(|ier| ier.set_frame_ie(false));\n        }\n        STATE.waker.wake();\n    }\n}\n\n/// The level on the VSync pin when the data is not valid on the parallel interface.\n#[allow(missing_docs)]\n#[derive(Clone, Copy, PartialEq)]\npub enum VSyncDataInvalidLevel {\n    Low,\n    High,\n}\n\n/// The level on the VSync pin when the data is not valid on the parallel interface.\n#[allow(missing_docs)]\n#[derive(Clone, Copy, PartialEq)]\npub enum HSyncDataInvalidLevel {\n    Low,\n    High,\n}\n\n#[derive(Clone, Copy, PartialEq)]\n#[allow(missing_docs)]\npub enum PixelClockPolarity {\n    RisingEdge,\n    FallingEdge,\n}\n\nstruct State {\n    waker: AtomicWaker,\n}\n\nimpl State {\n    const fn new() -> State {\n        State {\n            waker: AtomicWaker::new(),\n        }\n    }\n}\n\nstatic STATE: State = State::new();\n\n/// DCMI error.\n#[derive(Debug, Eq, PartialEq, Copy, Clone)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\n#[non_exhaustive]\npub enum Error {\n    /// Overrun error: the hardware generated data faster than we could read it.\n    Overrun,\n    /// Internal peripheral error.\n    PeripheralError,\n}\n\n/// DCMI configuration.\n#[non_exhaustive]\npub struct Config {\n    /// VSYNC level.\n    pub vsync_level: VSyncDataInvalidLevel,\n    /// HSYNC level.\n    pub hsync_level: HSyncDataInvalidLevel,\n    /// PIXCLK polarity.\n    pub pixclk_polarity: PixelClockPolarity,\n}\n\nimpl Default for Config {\n    fn default() -> Self {\n        Self {\n            vsync_level: VSyncDataInvalidLevel::High,\n            hsync_level: HSyncDataInvalidLevel::Low,\n            pixclk_polarity: PixelClockPolarity::RisingEdge,\n        }\n    }\n}\n\nmacro_rules! config_pins {\n    ($($pin:ident),*) => {\n                critical_section::with(|_| {\n            $(\n                set_as_af!($pin, AfType::input(Pull::None));\n            )*\n        })\n    };\n}\n\n/// DCMI driver.\npub struct Dcmi<'d, T: Instance> {\n    inner: Peri<'d, T>,\n    dma: ChannelAndRequest<'d>,\n}\n\nimpl<'d, T> Dcmi<'d, T>\nwhere\n    T: Instance,\n{\n    /// Create a new DCMI driver with 8 data bits.\n    pub fn new_8bit<D: FrameDma<T>>(\n        peri: Peri<'d, T>,\n        dma: Peri<'d, D>,\n        _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>>\n        + interrupt::typelevel::Binding<D::Interrupt, crate::dma::InterruptHandler<D>>\n        + 'd,\n        d0: Peri<'d, impl D0Pin<T>>,\n        d1: Peri<'d, impl D1Pin<T>>,\n        d2: Peri<'d, impl D2Pin<T>>,\n        d3: Peri<'d, impl D3Pin<T>>,\n        d4: Peri<'d, impl D4Pin<T>>,\n        d5: Peri<'d, impl D5Pin<T>>,\n        d6: Peri<'d, impl D6Pin<T>>,\n        d7: Peri<'d, impl D7Pin<T>>,\n        v_sync: Peri<'d, impl VSyncPin<T>>,\n        h_sync: Peri<'d, impl HSyncPin<T>>,\n        pixclk: Peri<'d, impl PixClkPin<T>>,\n        config: Config,\n    ) -> Self {\n        config_pins!(d0, d1, d2, d3, d4, d5, d6, d7);\n        config_pins!(v_sync, h_sync, pixclk);\n\n        Self::new_inner(peri, dma, _irq, config, false, 0b00)\n    }\n\n    /// Create a new DCMI driver with 10 data bits.\n    pub fn new_10bit<D: FrameDma<T>>(\n        peri: Peri<'d, T>,\n        dma: Peri<'d, D>,\n        _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>>\n        + interrupt::typelevel::Binding<D::Interrupt, crate::dma::InterruptHandler<D>>\n        + 'd,\n        d0: Peri<'d, impl D0Pin<T>>,\n        d1: Peri<'d, impl D1Pin<T>>,\n        d2: Peri<'d, impl D2Pin<T>>,\n        d3: Peri<'d, impl D3Pin<T>>,\n        d4: Peri<'d, impl D4Pin<T>>,\n        d5: Peri<'d, impl D5Pin<T>>,\n        d6: Peri<'d, impl D6Pin<T>>,\n        d7: Peri<'d, impl D7Pin<T>>,\n        d8: Peri<'d, impl D8Pin<T>>,\n        d9: Peri<'d, impl D9Pin<T>>,\n        v_sync: Peri<'d, impl VSyncPin<T>>,\n        h_sync: Peri<'d, impl HSyncPin<T>>,\n        pixclk: Peri<'d, impl PixClkPin<T>>,\n        config: Config,\n    ) -> Self {\n        config_pins!(d0, d1, d2, d3, d4, d5, d6, d7, d8, d9);\n        config_pins!(v_sync, h_sync, pixclk);\n\n        Self::new_inner(peri, dma, _irq, config, false, 0b01)\n    }\n\n    /// Create a new DCMI driver with 12 data bits.\n    pub fn new_12bit<D: FrameDma<T>>(\n        peri: Peri<'d, T>,\n        dma: Peri<'d, D>,\n        _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>>\n        + interrupt::typelevel::Binding<D::Interrupt, crate::dma::InterruptHandler<D>>\n        + 'd,\n        d0: Peri<'d, impl D0Pin<T>>,\n        d1: Peri<'d, impl D1Pin<T>>,\n        d2: Peri<'d, impl D2Pin<T>>,\n        d3: Peri<'d, impl D3Pin<T>>,\n        d4: Peri<'d, impl D4Pin<T>>,\n        d5: Peri<'d, impl D5Pin<T>>,\n        d6: Peri<'d, impl D6Pin<T>>,\n        d7: Peri<'d, impl D7Pin<T>>,\n        d8: Peri<'d, impl D8Pin<T>>,\n        d9: Peri<'d, impl D9Pin<T>>,\n        d10: Peri<'d, impl D10Pin<T>>,\n        d11: Peri<'d, impl D11Pin<T>>,\n        v_sync: Peri<'d, impl VSyncPin<T>>,\n        h_sync: Peri<'d, impl HSyncPin<T>>,\n        pixclk: Peri<'d, impl PixClkPin<T>>,\n        config: Config,\n    ) -> Self {\n        config_pins!(d0, d1, d2, d3, d4, d5, d6, d7, d8, d9, d10, d11);\n        config_pins!(v_sync, h_sync, pixclk);\n\n        Self::new_inner(peri, dma, _irq, config, false, 0b10)\n    }\n\n    /// Create a new DCMI driver with 14 data bits.\n    pub fn new_14bit<D: FrameDma<T>>(\n        peri: Peri<'d, T>,\n        dma: Peri<'d, D>,\n        _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>>\n        + interrupt::typelevel::Binding<D::Interrupt, crate::dma::InterruptHandler<D>>\n        + 'd,\n        d0: Peri<'d, impl D0Pin<T>>,\n        d1: Peri<'d, impl D1Pin<T>>,\n        d2: Peri<'d, impl D2Pin<T>>,\n        d3: Peri<'d, impl D3Pin<T>>,\n        d4: Peri<'d, impl D4Pin<T>>,\n        d5: Peri<'d, impl D5Pin<T>>,\n        d6: Peri<'d, impl D6Pin<T>>,\n        d7: Peri<'d, impl D7Pin<T>>,\n        d8: Peri<'d, impl D8Pin<T>>,\n        d9: Peri<'d, impl D9Pin<T>>,\n        d10: Peri<'d, impl D10Pin<T>>,\n        d11: Peri<'d, impl D11Pin<T>>,\n        d12: Peri<'d, impl D12Pin<T>>,\n        d13: Peri<'d, impl D13Pin<T>>,\n        v_sync: Peri<'d, impl VSyncPin<T>>,\n        h_sync: Peri<'d, impl HSyncPin<T>>,\n        pixclk: Peri<'d, impl PixClkPin<T>>,\n        config: Config,\n    ) -> Self {\n        config_pins!(d0, d1, d2, d3, d4, d5, d6, d7, d8, d9, d10, d11, d12, d13);\n        config_pins!(v_sync, h_sync, pixclk);\n\n        Self::new_inner(peri, dma, _irq, config, false, 0b11)\n    }\n\n    /// Create a new DCMI driver with 8 data bits, with embedded synchronization.\n    pub fn new_es_8bit<D: FrameDma<T>>(\n        peri: Peri<'d, T>,\n        dma: Peri<'d, D>,\n        _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>>\n        + interrupt::typelevel::Binding<D::Interrupt, crate::dma::InterruptHandler<D>>\n        + 'd,\n        d0: Peri<'d, impl D0Pin<T>>,\n        d1: Peri<'d, impl D1Pin<T>>,\n        d2: Peri<'d, impl D2Pin<T>>,\n        d3: Peri<'d, impl D3Pin<T>>,\n        d4: Peri<'d, impl D4Pin<T>>,\n        d5: Peri<'d, impl D5Pin<T>>,\n        d6: Peri<'d, impl D6Pin<T>>,\n        d7: Peri<'d, impl D7Pin<T>>,\n        pixclk: Peri<'d, impl PixClkPin<T>>,\n        config: Config,\n    ) -> Self {\n        config_pins!(d0, d1, d2, d3, d4, d5, d6, d7);\n        config_pins!(pixclk);\n\n        Self::new_inner(peri, dma, _irq, config, true, 0b00)\n    }\n\n    /// Create a new DCMI driver with 10 data bits, with embedded synchronization.\n    pub fn new_es_10bit<D: FrameDma<T>>(\n        peri: Peri<'d, T>,\n        dma: Peri<'d, D>,\n        _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>>\n        + interrupt::typelevel::Binding<D::Interrupt, crate::dma::InterruptHandler<D>>\n        + 'd,\n        d0: Peri<'d, impl D0Pin<T>>,\n        d1: Peri<'d, impl D1Pin<T>>,\n        d2: Peri<'d, impl D2Pin<T>>,\n        d3: Peri<'d, impl D3Pin<T>>,\n        d4: Peri<'d, impl D4Pin<T>>,\n        d5: Peri<'d, impl D5Pin<T>>,\n        d6: Peri<'d, impl D6Pin<T>>,\n        d7: Peri<'d, impl D7Pin<T>>,\n        d8: Peri<'d, impl D8Pin<T>>,\n        d9: Peri<'d, impl D9Pin<T>>,\n        pixclk: Peri<'d, impl PixClkPin<T>>,\n        config: Config,\n    ) -> Self {\n        config_pins!(d0, d1, d2, d3, d4, d5, d6, d7, d8, d9);\n        config_pins!(pixclk);\n\n        Self::new_inner(peri, dma, _irq, config, true, 0b01)\n    }\n\n    /// Create a new DCMI driver with 12 data bits, with embedded synchronization.\n    pub fn new_es_12bit<D: FrameDma<T>>(\n        peri: Peri<'d, T>,\n        dma: Peri<'d, D>,\n        _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>>\n        + interrupt::typelevel::Binding<D::Interrupt, crate::dma::InterruptHandler<D>>\n        + 'd,\n        d0: Peri<'d, impl D0Pin<T>>,\n        d1: Peri<'d, impl D1Pin<T>>,\n        d2: Peri<'d, impl D2Pin<T>>,\n        d3: Peri<'d, impl D3Pin<T>>,\n        d4: Peri<'d, impl D4Pin<T>>,\n        d5: Peri<'d, impl D5Pin<T>>,\n        d6: Peri<'d, impl D6Pin<T>>,\n        d7: Peri<'d, impl D7Pin<T>>,\n        d8: Peri<'d, impl D8Pin<T>>,\n        d9: Peri<'d, impl D9Pin<T>>,\n        d10: Peri<'d, impl D10Pin<T>>,\n        d11: Peri<'d, impl D11Pin<T>>,\n        pixclk: Peri<'d, impl PixClkPin<T>>,\n        config: Config,\n    ) -> Self {\n        config_pins!(d0, d1, d2, d3, d4, d5, d6, d7, d8, d9, d10, d11);\n        config_pins!(pixclk);\n\n        Self::new_inner(peri, dma, _irq, config, true, 0b10)\n    }\n\n    /// Create a new DCMI driver with 14 data bits, with embedded synchronization.\n    pub fn new_es_14bit<D: FrameDma<T>>(\n        peri: Peri<'d, T>,\n        dma: Peri<'d, D>,\n        _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>>\n        + interrupt::typelevel::Binding<D::Interrupt, crate::dma::InterruptHandler<D>>\n        + 'd,\n        d0: Peri<'d, impl D0Pin<T>>,\n        d1: Peri<'d, impl D1Pin<T>>,\n        d2: Peri<'d, impl D2Pin<T>>,\n        d3: Peri<'d, impl D3Pin<T>>,\n        d4: Peri<'d, impl D4Pin<T>>,\n        d5: Peri<'d, impl D5Pin<T>>,\n        d6: Peri<'d, impl D6Pin<T>>,\n        d7: Peri<'d, impl D7Pin<T>>,\n        d8: Peri<'d, impl D8Pin<T>>,\n        d9: Peri<'d, impl D9Pin<T>>,\n        d10: Peri<'d, impl D10Pin<T>>,\n        d11: Peri<'d, impl D11Pin<T>>,\n        d12: Peri<'d, impl D12Pin<T>>,\n        d13: Peri<'d, impl D13Pin<T>>,\n        pixclk: Peri<'d, impl PixClkPin<T>>,\n        config: Config,\n    ) -> Self {\n        config_pins!(d0, d1, d2, d3, d4, d5, d6, d7, d8, d9, d10, d11, d12, d13);\n        config_pins!(pixclk);\n\n        Self::new_inner(peri, dma, _irq, config, true, 0b11)\n    }\n\n    fn new_inner<D: FrameDma<T>>(\n        peri: Peri<'d, T>,\n        dma: Peri<'d, D>,\n        irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>>\n        + interrupt::typelevel::Binding<D::Interrupt, crate::dma::InterruptHandler<D>>\n        + 'd,\n        config: Config,\n        use_embedded_synchronization: bool,\n        edm: u8,\n    ) -> Self {\n        rcc::enable_and_reset::<T>();\n\n        peri.regs().cr().modify(|r| {\n            r.set_cm(true); // disable continuous mode (snapshot mode)\n            r.set_ess(use_embedded_synchronization);\n            r.set_pckpol(config.pixclk_polarity == PixelClockPolarity::RisingEdge);\n            r.set_vspol(config.vsync_level == VSyncDataInvalidLevel::High);\n            r.set_hspol(config.hsync_level == HSyncDataInvalidLevel::High);\n            r.set_fcrc(0x00); // capture every frame\n            r.set_edm(edm); // extended data mode\n        });\n\n        T::Interrupt::unpend();\n        unsafe { T::Interrupt::enable() };\n\n        Self {\n            inner: peri,\n            dma: new_dma!(dma, irq).unwrap(),\n        }\n    }\n\n    fn toggle(enable: bool) {\n        crate::pac::DCMI.cr().modify(|r| {\n            r.set_enable(enable);\n            r.set_capture(enable);\n        })\n    }\n\n    fn enable_irqs() {\n        crate::pac::DCMI.ier().modify(|r| {\n            r.set_err_ie(true);\n            r.set_ovr_ie(true);\n            r.set_frame_ie(true);\n        });\n    }\n\n    fn clear_interrupt_flags() {\n        crate::pac::DCMI.icr().write(|r| {\n            r.set_ovr_isc(true);\n            r.set_err_isc(true);\n            r.set_frame_isc(true);\n        })\n    }\n\n    /// This method starts the capture and finishes when both the dma transfer and DCMI finish the frame transfer.\n    /// The implication is that the input buffer size must be exactly the size of the captured frame.\n    pub async fn capture(&mut self, buffer: &mut [u32]) -> Result<(), Error> {\n        let r = self.inner.regs();\n        let src = r.dr().as_ptr() as *mut u32;\n        let dma_read = unsafe { self.dma.channel.read(self.dma.request, src, buffer, Default::default()) };\n\n        Self::clear_interrupt_flags();\n        Self::enable_irqs();\n\n        Self::toggle(true);\n\n        let result = poll_fn(|cx| {\n            STATE.waker.register(cx.waker());\n\n            let ris = crate::pac::DCMI.ris().read();\n            if ris.err_ris() {\n                crate::pac::DCMI.icr().write(|r| r.set_err_isc(true));\n                Poll::Ready(Err(Error::PeripheralError))\n            } else if ris.ovr_ris() {\n                crate::pac::DCMI.icr().write(|r| r.set_ovr_isc(true));\n                Poll::Ready(Err(Error::Overrun))\n            } else if ris.frame_ris() {\n                crate::pac::DCMI.icr().write(|r| r.set_frame_isc(true));\n                Poll::Ready(Ok(()))\n            } else {\n                Poll::Pending\n            }\n        });\n\n        let (_, result) = embassy_futures::join::join(dma_read, result).await;\n\n        Self::toggle(false);\n\n        result\n    }\n}\n\ntrait SealedInstance: crate::rcc::RccPeripheral {\n    fn regs(&self) -> crate::pac::dcmi::Dcmi;\n}\n\n/// DCMI instance.\n#[allow(private_bounds)]\npub trait Instance: SealedInstance + PeripheralType + 'static {\n    /// Interrupt for this instance.\n    type Interrupt: interrupt::typelevel::Interrupt;\n}\n\npin_trait!(D0Pin, Instance);\npin_trait!(D1Pin, Instance);\npin_trait!(D2Pin, Instance);\npin_trait!(D3Pin, Instance);\npin_trait!(D4Pin, Instance);\npin_trait!(D5Pin, Instance);\npin_trait!(D6Pin, Instance);\npin_trait!(D7Pin, Instance);\npin_trait!(D8Pin, Instance);\npin_trait!(D9Pin, Instance);\npin_trait!(D10Pin, Instance);\npin_trait!(D11Pin, Instance);\npin_trait!(D12Pin, Instance);\npin_trait!(D13Pin, Instance);\npin_trait!(HSyncPin, Instance);\npin_trait!(VSyncPin, Instance);\npin_trait!(PixClkPin, Instance);\n\n// allow unused as U5 sources do not contain interrupt nor dma data\n#[allow(unused)]\nmacro_rules! impl_peripheral {\n    ($inst:ident, $irq:ident) => {\n        impl SealedInstance for crate::peripherals::$inst {\n            fn regs(&self) -> crate::pac::dcmi::Dcmi {\n                crate::pac::$inst\n            }\n        }\n\n        impl Instance for crate::peripherals::$inst {\n            type Interrupt = crate::interrupt::typelevel::$irq;\n        }\n    };\n}\n\nforeach_interrupt! {\n    ($inst:ident, dcmi, $block:ident, GLOBAL, $irq:ident) => {\n        impl_peripheral!($inst, $irq);\n    };\n}\n\ndma_trait!(FrameDma, Instance);\n"
  },
  {
    "path": "embassy-stm32/src/dma/dma_bdma.rs",
    "content": "use core::future::{Future, poll_fn};\nuse core::pin::Pin;\nuse core::sync::atomic::{AtomicUsize, Ordering, compiler_fence, fence};\nuse core::task::{Context, Poll, Waker};\n\nuse embassy_sync::waitqueue::AtomicWaker;\n\nuse super::ringbuffer::{DmaCtrl, Error, ReadableDmaRingBuffer, WritableDmaRingBuffer};\nuse super::word::{Word, WordSize};\nuse super::{Channel, Dir, Increment, Request, STATE, info};\nuse crate::interrupt::typelevel::Interrupt;\nuse crate::rcc::WakeGuard;\nuse crate::{interrupt, pac};\n\npub(crate) unsafe fn on_irq(id: u8) {\n    let info = info(id);\n    let state = &STATE[id as usize];\n    match info.dma {\n        #[cfg(dma)]\n        DmaInfo::Dma(r) => {\n            let cr = r.st(info.num).cr();\n            let isr = r.isr(info.num / 4).read();\n\n            if isr.teif(info.num % 4) {\n                panic!(\"DMA: error on DMA@{:08x} channel {}\", r.as_ptr() as u32, info.num);\n            }\n\n            if isr.htif(info.num % 4) && cr.read().htie() {\n                // Acknowledge half transfer complete interrupt\n                r.ifcr(info.num / 4).write(|w| w.set_htif(info.num % 4, true));\n            } else if isr.tcif(info.num % 4) && cr.read().tcie() {\n                // Acknowledge  transfer complete interrupt\n                r.ifcr(info.num / 4).write(|w| w.set_tcif(info.num % 4, true));\n                state.complete_count.fetch_add(1, Ordering::Release);\n            } else {\n                return;\n            }\n            state.waker.wake();\n        }\n        #[cfg(bdma)]\n        DmaInfo::Bdma(r) => {\n            let isr = r.isr().read();\n            let cr = r.ch(info.num).cr();\n\n            if isr.teif(info.num) {\n                panic!(\"DMA: error on BDMA@{:08x} channel {}\", r.as_ptr() as u32, info.num);\n            }\n\n            if isr.htif(info.num) && cr.read().htie() {\n                // Acknowledge half transfer complete interrupt\n                r.ifcr().write(|w| w.set_htif(info.num, true));\n            } else if isr.tcif(info.num) && cr.read().tcie() {\n                // Acknowledge transfer complete interrupt\n                r.ifcr().write(|w| w.set_tcif(info.num, true));\n                #[cfg(not(armv6m))]\n                state.complete_count.fetch_add(1, Ordering::Release);\n                #[cfg(armv6m)]\n                critical_section::with(|_| {\n                    let x = state.complete_count.load(Ordering::Relaxed);\n                    state.complete_count.store(x + 1, Ordering::Release);\n                })\n            } else {\n                return;\n            }\n\n            state.waker.wake();\n        }\n        #[cfg(mdma)]\n        DmaInfo::Mdma(r) => {\n            // If our bit in gisr0 is not set, then the interrupt is not for this channel\n            if !r.gisr0().read().gif(info.num) {\n                return;\n            }\n\n            let isr = r.ch(info.num).isr();\n            let ifcr = r.ch(info.num).ifcr();\n\n            if isr.read().teif() {\n                panic!(\"DMA: error on MDMA@{:08x} channel {}\", r.as_ptr() as u32, info.num);\n            }\n\n            if isr.read().ctcif() {\n                // Channel Transfer complete\n                state.complete_count.fetch_add(1, Ordering::Release);\n                ifcr.write(|w| w.set_cctcif(true));\n            }\n\n            state.waker.wake();\n        }\n    }\n}\n\npub(crate) struct ChannelInfo {\n    pub(crate) dma: DmaInfo,\n    pub(crate) num: usize,\n    #[cfg(feature = \"_dual-core\")]\n    pub(crate) irq: pac::Interrupt,\n    #[cfg(dmamux)]\n    pub(crate) dmamux: Option<super::DmamuxInfo>,\n    #[cfg(feature = \"low-power\")]\n    pub(crate) stop_mode: crate::rcc::StopMode,\n}\n\nimpl ChannelInfo {\n    fn wake_guard(&self) -> WakeGuard {\n        WakeGuard::new(\n            #[cfg(feature = \"low-power\")]\n            self.stop_mode,\n        )\n    }\n}\n\n#[derive(Clone, Copy)]\npub(crate) enum DmaInfo {\n    #[cfg(dma)]\n    Dma(pac::dma::Dma),\n    #[cfg(bdma)]\n    Bdma(pac::bdma::Dma),\n    #[cfg(mdma)]\n    Mdma(pac::mdma::Mdma),\n}\n\n/// DMA transfer options.\n#[derive(Debug, Copy, Clone, PartialEq, Eq)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\n#[non_exhaustive]\npub struct TransferOptions {\n    /// Peripheral burst transfer configuration\n    #[cfg(any(dma, mdma))]\n    pub pburst: Burst,\n    /// Memory burst transfer configuration\n    #[cfg(any(dma, mdma))]\n    pub mburst: Burst,\n    /// Flow control configuration\n    #[cfg(dma)]\n    pub flow_ctrl: FlowControl,\n    /// FIFO threshold for DMA FIFO mode. If none, direct mode is used.\n    #[cfg(dma)]\n    pub fifo_threshold: Option<FifoThreshold>,\n    /// Request priority level\n    pub priority: Priority,\n    /// Enable circular DMA\n    ///\n    /// Note:\n    /// If you enable circular mode manually, you may want to build and `.await` the `Transfer` in a separate task.\n    /// Since DMA in circular mode need manually stop, `.await` in current task would block the task forever.\n    pub circular: bool,\n    /// Enable half transfer interrupt\n    pub half_transfer_ir: bool,\n    /// Enable transfer complete interrupt\n    pub complete_transfer_ir: bool,\n    #[cfg(mdma)]\n    /// Max bytes to transfer at once, 1-64\n    pub buffer_size: u8,\n    #[cfg(mdma)]\n    /// Swap bytes in each half-word\n    pub byte_swap: bool,\n    #[cfg(mdma)]\n    /// Swap half-words in each word\n    pub half_word_swap: bool,\n    #[cfg(mdma)]\n    /// Swap words in each double-word\n    pub word_swap: bool,\n}\n\nimpl Default for TransferOptions {\n    fn default() -> Self {\n        Self {\n            #[cfg(dma)]\n            pburst: Burst::Single,\n            #[cfg(dma)]\n            mburst: Burst::Single,\n            #[cfg(dma)]\n            flow_ctrl: FlowControl::Dma,\n            #[cfg(dma)]\n            fifo_threshold: None,\n            priority: Priority::VeryHigh,\n            circular: false,\n            half_transfer_ir: false,\n            complete_transfer_ir: true,\n            #[cfg(mdma)]\n            buffer_size: MDMA_MAX_BUFFER,\n            #[cfg(mdma)]\n            byte_swap: false,\n            #[cfg(mdma)]\n            half_word_swap: false,\n            #[cfg(mdma)]\n            word_swap: false,\n        }\n    }\n}\n\n/// DMA request priority\n#[derive(Debug, Copy, Clone, PartialEq, Eq)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub enum Priority {\n    /// Low Priority\n    Low,\n    /// Medium Priority\n    Medium,\n    /// High Priority\n    High,\n    /// Very High Priority\n    VeryHigh,\n}\n\n#[cfg(dma)]\nimpl From<Priority> for pac::dma::vals::Pl {\n    fn from(value: Priority) -> Self {\n        match value {\n            Priority::Low => pac::dma::vals::Pl::LOW,\n            Priority::Medium => pac::dma::vals::Pl::MEDIUM,\n            Priority::High => pac::dma::vals::Pl::HIGH,\n            Priority::VeryHigh => pac::dma::vals::Pl::VERY_HIGH,\n        }\n    }\n}\n\n#[cfg(bdma)]\nimpl From<Priority> for pac::bdma::vals::Pl {\n    fn from(value: Priority) -> Self {\n        match value {\n            Priority::Low => pac::bdma::vals::Pl::LOW,\n            Priority::Medium => pac::bdma::vals::Pl::MEDIUM,\n            Priority::High => pac::bdma::vals::Pl::HIGH,\n            Priority::VeryHigh => pac::bdma::vals::Pl::VERY_HIGH,\n        }\n    }\n}\n\n#[cfg(dma)]\npub use dma_only::*;\n#[cfg(dma)]\nmod dma_only {\n    use pac::dma::vals;\n\n    use super::*;\n\n    impl From<WordSize> for vals::Size {\n        fn from(raw: WordSize) -> Self {\n            match raw {\n                WordSize::OneByte => Self::BITS8,\n                WordSize::TwoBytes => Self::BITS16,\n                WordSize::FourBytes => Self::BITS32,\n                WordSize::EightBytes => unimplemented!(),\n            }\n        }\n    }\n\n    impl From<Dir> for vals::Dir {\n        fn from(raw: Dir) -> Self {\n            match raw {\n                Dir::MemoryToPeripheral => Self::MEMORY_TO_PERIPHERAL,\n                Dir::PeripheralToMemory => Self::PERIPHERAL_TO_MEMORY,\n                Dir::MemoryToMemory => Self::MEMORY_TO_MEMORY,\n            }\n        }\n    }\n\n    /// DMA transfer burst setting.\n    #[derive(Debug, Copy, Clone, PartialEq, Eq)]\n    #[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\n    pub enum Burst {\n        /// Single transfer\n        Single,\n        /// Incremental burst of 4 beats\n        Incr4,\n        /// Incremental burst of 8 beats\n        Incr8,\n        /// Incremental burst of 16 beats\n        Incr16,\n        /// Incremental burst of 32 beats\n        Incr32,\n        /// Incremental burst of 64 beats\n        Incr64,\n        /// Incremental burst of 128 beats\n        Incr128,\n        /// Incremental burst of 256 beats\n        Incr256,\n    }\n\n    impl From<Burst> for vals::Burst {\n        fn from(burst: Burst) -> Self {\n            match burst {\n                Burst::Single => vals::Burst::SINGLE,\n                Burst::Incr4 => vals::Burst::INCR4,\n                Burst::Incr8 => vals::Burst::INCR8,\n                Burst::Incr16 => vals::Burst::INCR16,\n                _ => unimplemented!(\"invalid burst size\"),\n            }\n        }\n    }\n\n    /// DMA flow control setting.\n    #[derive(Debug, Copy, Clone, PartialEq, Eq)]\n    #[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\n    pub enum FlowControl {\n        /// Flow control by DMA\n        Dma,\n        /// Flow control by peripheral\n        Peripheral,\n    }\n\n    impl From<FlowControl> for vals::Pfctrl {\n        fn from(flow: FlowControl) -> Self {\n            match flow {\n                FlowControl::Dma => vals::Pfctrl::DMA,\n                FlowControl::Peripheral => vals::Pfctrl::PERIPHERAL,\n            }\n        }\n    }\n\n    /// DMA FIFO threshold.\n    #[derive(Debug, Copy, Clone, PartialEq, Eq)]\n    #[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\n    pub enum FifoThreshold {\n        /// 1/4 full FIFO\n        Quarter,\n        /// 1/2 full FIFO\n        Half,\n        /// 3/4 full FIFO\n        ThreeQuarters,\n        /// Full FIFO\n        Full,\n    }\n\n    impl From<FifoThreshold> for vals::Fth {\n        fn from(value: FifoThreshold) -> Self {\n            match value {\n                FifoThreshold::Quarter => vals::Fth::QUARTER,\n                FifoThreshold::Half => vals::Fth::HALF,\n                FifoThreshold::ThreeQuarters => vals::Fth::THREE_QUARTERS,\n                FifoThreshold::Full => vals::Fth::FULL,\n            }\n        }\n    }\n}\n\n#[cfg(mdma)]\npub use mdma_only::*;\n#[cfg(mdma)]\nmod mdma_only {\n    use pac::mdma::vals;\n\n    use super::*;\n\n    /// Maximum Buffer Size\n    pub const MDMA_MAX_BUFFER: u8 = 0x40;\n\n    /// Max Block Size (bytes)\n    pub const MDMA_MAX_BLOCK: usize = 0x10000;\n\n    /// Max Number of Blocks\n    pub const MDMA_MAX_BLOCK_COUNT: usize = 0x1000;\n\n    impl From<WordSize> for vals::Wordsize {\n        fn from(raw: WordSize) -> Self {\n            match raw {\n                WordSize::OneByte => Self::BYTE,\n                WordSize::TwoBytes => Self::HALF_WORD,\n                WordSize::FourBytes => Self::WORD,\n                WordSize::EightBytes => Self::DOUBLE_WORD,\n            }\n        }\n    }\n\n    impl From<Burst> for vals::Burst {\n        fn from(burst: Burst) -> Self {\n            match burst {\n                Burst::Single => vals::Burst::SINGLE,\n                Burst::Incr4 => vals::Burst::INCR4,\n                Burst::Incr8 => vals::Burst::INCR8,\n                Burst::Incr16 => vals::Burst::INCR16,\n                Burst::Incr32 => vals::Burst::INCR32,\n                Burst::Incr64 => vals::Burst::INCR64,\n                Burst::Incr128 => vals::Burst::INCR128,\n                Burst::Incr256 => vals::Burst::INCR256,\n            }\n        }\n    }\n\n    impl From<Priority> for pac::mdma::vals::Pl {\n        fn from(value: Priority) -> Self {\n            match value {\n                Priority::Low => pac::mdma::vals::Pl::LOW,\n                Priority::Medium => pac::mdma::vals::Pl::MEDIUM,\n                Priority::High => pac::mdma::vals::Pl::HIGH,\n                Priority::VeryHigh => pac::mdma::vals::Pl::VERY_HIGH,\n            }\n        }\n    }\n}\n\n#[cfg(bdma)]\nmod bdma_only {\n    use pac::bdma::vals;\n\n    use super::*;\n\n    impl From<WordSize> for vals::Size {\n        fn from(raw: WordSize) -> Self {\n            match raw {\n                WordSize::OneByte => Self::BITS8,\n                WordSize::TwoBytes => Self::BITS16,\n                WordSize::FourBytes => Self::BITS32,\n                WordSize::EightBytes => unimplemented!(),\n            }\n        }\n    }\n\n    impl From<Dir> for vals::Dir {\n        fn from(raw: Dir) -> Self {\n            match raw {\n                Dir::MemoryToPeripheral => Self::FROM_MEMORY,\n                Dir::PeripheralToMemory => Self::FROM_PERIPHERAL,\n                Dir::MemoryToMemory => Self::FROM_MEMORY,\n            }\n        }\n    }\n}\n\npub(crate) struct ChannelState {\n    waker: AtomicWaker,\n    complete_count: AtomicUsize,\n}\n\nimpl ChannelState {\n    pub(crate) const NEW: Self = Self {\n        waker: AtomicWaker::new(),\n        complete_count: AtomicUsize::new(0),\n    };\n}\n\n/// safety: must be called only once\npub(crate) unsafe fn init(\n    cs: critical_section::CriticalSection,\n    #[cfg(dma)] dma_priority: interrupt::Priority,\n    #[cfg(bdma)] bdma_priority: interrupt::Priority,\n    #[cfg(mdma)] mdma_priority: interrupt::Priority,\n) {\n    foreach_interrupt! {\n        ($peri:ident, dma, $block:ident, $signal_name:ident, $irq:ident) => {\n            crate::interrupt::typelevel::$irq::set_priority_with_cs(cs, dma_priority);\n            #[cfg(not(feature = \"_dual-core\"))]\n            crate::interrupt::typelevel::$irq::enable();\n        };\n        ($peri:ident, bdma, $block:ident, $signal_name:ident, $irq:ident) => {\n            crate::interrupt::typelevel::$irq::set_priority_with_cs(cs, bdma_priority);\n            #[cfg(not(feature = \"_dual-core\"))]\n            crate::interrupt::typelevel::$irq::enable();\n        };\n    }\n    #[cfg(mdma)]\n    foreach_interrupt! {\n        ($peri:ident, mdma, $block:ident, $signal_name:ident, $irq:ident) => {\n            crate::interrupt::typelevel::$irq::set_priority_with_cs(cs, mdma_priority);\n            #[cfg(not(feature = \"_dual-core\"))]\n            crate::interrupt::typelevel::$irq::enable();\n        };\n    }\n    crate::_generated::init_dma();\n    crate::_generated::init_bdma();\n    #[cfg(mdma)]\n    crate::_generated::init_mdma();\n}\n\nimpl<'d> Channel<'d> {\n    fn info(&self) -> &'static super::ChannelInfo {\n        super::info(self.id)\n    }\n\n    unsafe fn configure(\n        &self,\n        _request: Request,\n        dir: Dir,\n        peri_addr: *const u32,\n        mem_addr: *mut u32,\n        mem_len: usize,\n        incr_mem: Increment,\n        mem_size: WordSize,\n        peri_size: WordSize,\n        options: TransferOptions,\n    ) {\n        // \"Preceding reads and writes cannot be moved past subsequent writes.\"\n        fence(Ordering::SeqCst);\n\n        let info = self.info();\n        #[cfg(feature = \"_dual-core\")]\n        {\n            use embassy_hal_internal::interrupt::InterruptExt as _;\n            info.irq.enable();\n        }\n\n        #[cfg(dmamux)]\n        if let Some(ref dmamux) = info.dmamux {\n            super::dmamux::configure_dmamux(dmamux, _request);\n        }\n\n        match self.info().dma {\n            #[cfg(dma)]\n            DmaInfo::Dma(r) => {\n                assert!(mem_len > 0 && mem_len <= 0xFFFF);\n                let state: &ChannelState = &STATE[self.id as usize];\n                let ch = r.st(info.num);\n\n                state.complete_count.store(0, Ordering::Release);\n                self.clear_irqs();\n\n                // NDTR is the number of transfers in the *peripheral* word size.\n                // ex: if mem_size=1, peri_size=4 and ndtr=3 it'll do 12 mem transfers, 3 peri transfers.\n                let ndtr = match (mem_size, peri_size) {\n                    (WordSize::FourBytes, WordSize::OneByte) => mem_len * 4,\n                    (WordSize::FourBytes, WordSize::TwoBytes) | (WordSize::TwoBytes, WordSize::OneByte) => mem_len * 2,\n                    (WordSize::FourBytes, WordSize::FourBytes)\n                    | (WordSize::TwoBytes, WordSize::TwoBytes)\n                    | (WordSize::OneByte, WordSize::OneByte) => mem_len,\n                    (WordSize::TwoBytes, WordSize::FourBytes) | (WordSize::OneByte, WordSize::TwoBytes) => {\n                        assert!(mem_len % 2 == 0);\n                        mem_len / 2\n                    }\n                    (WordSize::OneByte, WordSize::FourBytes) => {\n                        assert!(mem_len % 4 == 0);\n                        mem_len / 4\n                    }\n                    (WordSize::EightBytes, _) | (_, WordSize::EightBytes) => unimplemented!(\"invalid word size\"),\n                };\n\n                assert!(ndtr > 0 && ndtr <= 0xFFFF);\n\n                ch.par().write_value(peri_addr as u32);\n                ch.m0ar().write_value(mem_addr as u32);\n                ch.ndtr().write_value(pac::dma::regs::Ndtr(ndtr as _));\n                ch.fcr().write(|w| {\n                    if let Some(fth) = options.fifo_threshold {\n                        // FIFO mode\n                        w.set_dmdis(pac::dma::vals::Dmdis::DISABLED);\n                        w.set_fth(fth.into());\n                    } else if mem_size != peri_size {\n                        // force FIFO mode if msize != psize\n                        // packing/unpacking doesn't work in direct mode.\n                        w.set_dmdis(pac::dma::vals::Dmdis::DISABLED);\n                        w.set_fth(FifoThreshold::Half.into());\n                    } else {\n                        // Direct mode\n                        w.set_dmdis(pac::dma::vals::Dmdis::ENABLED);\n                    }\n                });\n                ch.cr().write(|w| {\n                    w.set_dir(dir.into());\n                    w.set_msize(mem_size.into());\n                    w.set_psize(peri_size.into());\n                    w.set_pl(options.priority.into());\n                    match incr_mem {\n                        Increment::None => {\n                            w.set_minc(false);\n                            w.set_pinc(false);\n                        }\n                        Increment::Peripheral => {\n                            w.set_minc(false);\n                            w.set_pinc(true);\n                        }\n                        Increment::Memory => {\n                            w.set_minc(true);\n                            w.set_pinc(false);\n                        }\n                        Increment::Both => {\n                            w.set_minc(true);\n                            w.set_pinc(true);\n                        }\n                    }\n                    w.set_teie(true);\n                    w.set_htie(options.half_transfer_ir);\n                    w.set_tcie(options.complete_transfer_ir);\n                    w.set_circ(options.circular);\n                    #[cfg(dma_v1)]\n                    w.set_trbuff(true);\n                    #[cfg(dma_v2)]\n                    w.set_chsel(_request);\n                    w.set_pburst(options.pburst.into());\n                    w.set_mburst(options.mburst.into());\n                    w.set_pfctrl(options.flow_ctrl.into());\n                    w.set_en(false); // don't start yet\n                });\n            }\n            #[cfg(bdma)]\n            DmaInfo::Bdma(r) => {\n                assert!(mem_len > 0 && mem_len <= 0xFFFF);\n\n                #[cfg(bdma_v2)]\n                critical_section::with(|_| r.cselr().modify(|w| w.set_cs(info.num, _request)));\n\n                let state: &ChannelState = &STATE[self.id as usize];\n                let ch = r.ch(info.num);\n\n                state.complete_count.store(0, Ordering::Release);\n                self.clear_irqs();\n\n                ch.par().write_value(peri_addr as u32);\n                ch.mar().write_value(mem_addr as u32);\n                ch.ndtr().write(|w| w.set_ndt(mem_len as u16));\n                ch.cr().write(|w| {\n                    w.set_psize(peri_size.into());\n                    w.set_msize(mem_size.into());\n                    match incr_mem {\n                        Increment::None => {\n                            w.set_minc(false);\n                            w.set_pinc(false);\n                        }\n                        Increment::Peripheral => {\n                            w.set_minc(false);\n                            w.set_pinc(true);\n                        }\n                        Increment::Memory => {\n                            w.set_minc(true);\n                            w.set_pinc(false);\n                        }\n                        Increment::Both => {\n                            w.set_minc(true);\n                            w.set_pinc(true);\n                        }\n                    }\n                    w.set_dir(dir.into());\n                    w.set_teie(true);\n                    w.set_tcie(options.complete_transfer_ir);\n                    w.set_htie(options.half_transfer_ir);\n                    w.set_circ(options.circular);\n                    w.set_pl(options.priority.into());\n                    w.set_en(false); // don't start yet\n                });\n            }\n            #[cfg(mdma)]\n            DmaInfo::Mdma(r) => {\n                use pac::mdma::vals::*;\n\n                use crate::_generated::{MEMORY_REGION_DTCM, MEMORY_REGION_ITCM};\n\n                assert!(mem_len > 0 && mem_len <= MDMA_MAX_BLOCK * MDMA_MAX_BLOCK_COUNT);\n\n                // Circular mode is not supported\n                assert!(!options.circular);\n\n                let state: &ChannelState = &STATE[self.id as usize];\n                let ch = r.ch(info.num);\n\n                state.complete_count.store(0, Ordering::Release);\n                self.clear_irqs();\n\n                match dir {\n                    Dir::MemoryToPeripheral => {\n                        ch.sar().write(|w| w.set_sar(mem_addr as u32));\n                        ch.dar().write(|w| w.set_dar(peri_addr as u32));\n                    }\n                    _ => {\n                        ch.sar().write(|w| w.set_sar(peri_addr as u32));\n                        ch.dar().write(|w| w.set_dar(mem_addr as u32));\n                    }\n                };\n\n                // Find the best block size/count. This is essentially a factorisation problem\n                // So it's best to avoid large prime number transfer sizes.\n                let mut block_count = mem_len.div_ceil(MDMA_MAX_BLOCK);\n                let mut block_size = mem_len.div_ceil(block_count);\n\n                loop {\n                    // Everything matches up so we're good to go\n                    if block_count * block_size == mem_len {\n                        break;\n                    }\n\n                    // Try a higher block count, lower block size\n                    block_count += 1;\n                    block_size = mem_len.div_ceil(block_count);\n\n                    if block_count > MDMA_MAX_BLOCK_COUNT {\n                        panic!(\"MDMA: max block count hit\");\n                    }\n                }\n\n                let (sinc, dinc) = match (incr_mem, dir) {\n                    (Increment::None, _) => (Incmode::FIXED, Incmode::FIXED),\n                    (Increment::Both, _) => (Incmode::INCREMENT, Incmode::INCREMENT),\n                    (_, Dir::MemoryToMemory) => (Incmode::INCREMENT, Incmode::INCREMENT),\n                    (Increment::Peripheral, Dir::PeripheralToMemory) => (Incmode::INCREMENT, Incmode::FIXED),\n                    (Increment::Peripheral, Dir::MemoryToPeripheral) => (Incmode::FIXED, Incmode::INCREMENT),\n                    (Increment::Memory, Dir::PeripheralToMemory) => (Incmode::FIXED, Incmode::INCREMENT),\n                    (Increment::Memory, Dir::MemoryToPeripheral) => (Incmode::INCREMENT, Incmode::FIXED),\n                };\n\n                ch.tcr().write(|w| {\n                    w.set_tlen((options.buffer_size - 1) as u8);\n                    match dir {\n                        Dir::MemoryToPeripheral => {\n                            w.set_sincos(mem_size.into());\n                            w.set_ssize(mem_size.into());\n                            w.set_sinc(sinc);\n                            w.set_dincos(peri_size.into());\n                            w.set_dsize(peri_size.into());\n                            w.set_dinc(dinc);\n                        }\n                        _ => {\n                            w.set_sincos(peri_size.into());\n                            w.set_ssize(peri_size.into());\n                            w.set_sinc(sinc);\n                            w.set_dincos(mem_size.into());\n                            w.set_dsize(mem_size.into());\n                            w.set_dinc(dinc);\n                        }\n                    };\n                    if dir == Dir::MemoryToMemory {\n                        w.set_swrm(dir == Dir::MemoryToMemory);\n                        w.set_trgm(Trgm::REPEATED);\n                    }\n                });\n\n                ch.bndtr().write(|w| {\n                    w.set_bndt(block_size as u32);\n                    w.set_brc(block_count as u16 - 1);\n                });\n\n                ch.brur().write(|w| {\n                    w.set_suv(0);\n                    w.set_duv(0);\n                });\n\n                let get_bus = |addr: u32| {\n                    if MEMORY_REGION_ITCM.contains(&addr) || MEMORY_REGION_DTCM.contains(&addr) {\n                        Bus::AHB\n                    } else {\n                        Bus::SYSTEM\n                    }\n                };\n\n                let mem_bus = get_bus(mem_addr as u32);\n                let peri_bus = get_bus(peri_addr as u32);\n\n                ch.tbr().write(|w| {\n                    match dir {\n                        Dir::MemoryToPeripheral => {\n                            w.set_sbus(mem_bus);\n                            w.set_dbus(peri_bus);\n                        }\n                        _ => {\n                            w.set_sbus(peri_bus);\n                            w.set_dbus(mem_bus);\n                        }\n                    };\n\n                    w.set_tsel(_request);\n                });\n\n                ch.cr().write(|w| {\n                    w.set_teie(true);\n                    w.set_ctcie(true);\n                    w.set_tcie(false);\n                    w.set_btie(false);\n                    w.set_brtie(false);\n                    w.set_pl(options.priority.into());\n                    w.set_bex(options.byte_swap);\n                    w.set_wex(options.word_swap);\n                    w.set_hex(options.half_word_swap);\n                    w.set_en(false); // don't start yet\n                });\n            }\n        }\n    }\n\n    fn start(&self) {\n        let info = self.info();\n        match self.info().dma {\n            #[cfg(dma)]\n            DmaInfo::Dma(r) => {\n                let ch = r.st(info.num);\n                ch.cr().modify(|w| w.set_en(true))\n            }\n            #[cfg(bdma)]\n            DmaInfo::Bdma(r) => {\n                let ch = r.ch(info.num);\n                ch.cr().modify(|w| w.set_en(true));\n            }\n            #[cfg(mdma)]\n            DmaInfo::Mdma(r) => {\n                let ch = r.ch(info.num);\n\n                let swrm = ch.tcr().read().swrm();\n\n                ch.cr().modify(|w| {\n                    w.set_en(true);\n                    if swrm {\n                        w.set_swrq(true);\n                    }\n                });\n            }\n        }\n    }\n\n    fn clear_irqs(&self) {\n        let info = self.info();\n        match self.info().dma {\n            #[cfg(dma)]\n            DmaInfo::Dma(r) => {\n                let isrn = info.num / 4;\n                let isrbit = info.num % 4;\n\n                r.ifcr(isrn).write(|w| {\n                    w.set_htif(isrbit, true);\n                    w.set_tcif(isrbit, true);\n                    w.set_teif(isrbit, true);\n                });\n            }\n            #[cfg(bdma)]\n            DmaInfo::Bdma(r) => {\n                r.ifcr().write(|w| {\n                    w.set_htif(info.num, true);\n                    w.set_tcif(info.num, true);\n                    w.set_teif(info.num, true);\n                });\n            }\n            #[cfg(mdma)]\n            DmaInfo::Mdma(r) => {\n                let ch = r.ch(info.num);\n                ch.ifcr().write(|w| {\n                    w.set_cbrtif(true);\n                    w.set_cbtif(true);\n                    w.set_cctcif(true);\n                    w.set_cltcif(true);\n                    w.set_cteif(true);\n                });\n            }\n        }\n    }\n\n    fn request_pause(&self) {\n        let info = self.info();\n        match self.info().dma {\n            #[cfg(dma)]\n            DmaInfo::Dma(r) => {\n                // Disable the channel without overwriting the existing configuration\n                r.st(info.num).cr().modify(|w| {\n                    w.set_en(false);\n                });\n            }\n            #[cfg(bdma)]\n            DmaInfo::Bdma(r) => {\n                // Disable the channel without overwriting the existing configuration\n                r.ch(info.num).cr().modify(|w| {\n                    w.set_en(false);\n                });\n            }\n            #[cfg(mdma)]\n            DmaInfo::Mdma(r) => {\n                // Disable the channel without overwriting the existing configuration\n                r.ch(info.num).cr().modify(|w| {\n                    w.set_en(false);\n                });\n            }\n        }\n    }\n\n    fn request_resume(&self) {\n        self.start()\n    }\n\n    fn request_reset(&self) {\n        let info = self.info();\n        match self.info().dma {\n            #[cfg(dma)]\n            DmaInfo::Dma(r) => {\n                // Disable the channel. Keep the IEs enabled so the irqs still fire.\n                r.st(info.num).cr().write(|w| {\n                    w.set_teie(true);\n                    w.set_tcie(true);\n                });\n            }\n            #[cfg(bdma)]\n            DmaInfo::Bdma(r) => {\n                // Disable the channel. Keep the IEs enabled so the irqs still fire.\n                r.ch(info.num).cr().write(|w| {\n                    w.set_teie(true);\n                    w.set_tcie(true);\n                });\n            }\n            #[cfg(mdma)]\n            DmaInfo::Mdma(r) => {\n                // Disable the channel. Keep the IEs enabled so the irqs still fire.\n                r.ch(info.num).cr().modify(|m| {\n                    m.set_en(false);\n                });\n            }\n        }\n\n        while self.is_running() {}\n    }\n\n    fn is_running(&self) -> bool {\n        let info = self.info();\n        match self.info().dma {\n            #[cfg(dma)]\n            DmaInfo::Dma(r) => r.st(info.num).cr().read().en(),\n            #[cfg(bdma)]\n            DmaInfo::Bdma(r) => {\n                let state: &ChannelState = &STATE[self.id as usize];\n                let ch = r.ch(info.num);\n                let en = ch.cr().read().en();\n                let circular = ch.cr().read().circ();\n                let tcif = state.complete_count.load(Ordering::Acquire) != 0;\n                en && (circular || !tcif)\n            }\n            #[cfg(mdma)]\n            DmaInfo::Mdma(r) => r.ch(info.num).cr().read().en(),\n        }\n    }\n\n    fn get_remaining_transfers(&self) -> u32 {\n        let info = self.info();\n        match self.info().dma {\n            #[cfg(dma)]\n            DmaInfo::Dma(r) => r.st(info.num).ndtr().read().ndt() as u32,\n            #[cfg(bdma)]\n            DmaInfo::Bdma(r) => r.ch(info.num).ndtr().read().ndt() as u32,\n            #[cfg(mdma)]\n            DmaInfo::Mdma(r) => r.ch(info.num).bndtr().read().bndt(),\n        }\n    }\n\n    fn disable_circular_mode(&self) {\n        let info = self.info();\n        match self.info().dma {\n            #[cfg(dma)]\n            DmaInfo::Dma(regs) => regs.st(info.num).cr().modify(|w| {\n                w.set_circ(false);\n            }),\n            #[cfg(bdma)]\n            DmaInfo::Bdma(regs) => regs.ch(info.num).cr().modify(|w| {\n                w.set_circ(false);\n            }),\n            #[cfg(mdma)]\n            DmaInfo::Mdma(_regs) => (),\n        }\n    }\n\n    fn poll_stop(&self) -> Poll<()> {\n        compiler_fence(Ordering::SeqCst);\n\n        if !self.is_running() {\n            fence(Ordering::Acquire);\n\n            Poll::Ready(())\n        } else {\n            Poll::Pending\n        }\n    }\n\n    /// Create a memory DMA transfer (memory to memory), using raw pointers.\n    pub unsafe fn transfer<'a, MW: Word, PW: Word>(\n        &'a mut self,\n        request: Request,\n        buf: *const [PW],\n        dest_addr: *mut MW,\n        options: TransferOptions,\n    ) -> Transfer<'a> {\n        self.transfer_raw(request, buf as *const PW, buf.len(), dest_addr, options)\n    }\n\n    /// Create a memory DMA transfer (memory to memory), using raw pointers.\n    pub unsafe fn transfer_raw<'a, MW: Word, PW: Word>(\n        &'a mut self,\n        request: Request,\n        src_addr: *const MW,\n        src_size: usize,\n        dest_addr: *mut PW,\n        options: TransferOptions,\n    ) -> Transfer<'a> {\n        self.configure(\n            request,\n            Dir::MemoryToMemory,\n            src_addr as *mut u32,\n            dest_addr as *mut u32,\n            src_size,\n            Increment::Both,\n            MW::size(),\n            PW::size(),\n            options,\n        );\n        self.start();\n        Transfer {\n            _wake_guard: self.info().wake_guard(),\n            channel: self.reborrow(),\n        }\n    }\n\n    /// Create a read DMA transfer (peripheral to memory).\n    pub unsafe fn read<'a, W: Word>(\n        &'a mut self,\n        request: Request,\n        peri_addr: *mut W,\n        buf: &'a mut [W],\n        options: TransferOptions,\n    ) -> Transfer<'a> {\n        self.read_raw(request, peri_addr, buf, options)\n    }\n\n    /// Create a read DMA transfer (peripheral to memory), using raw pointers.\n    pub unsafe fn read_raw<'a, MW: Word, PW: Word>(\n        &'a mut self,\n        request: Request,\n        peri_addr: *mut PW,\n        buf: *mut [MW],\n        options: TransferOptions,\n    ) -> Transfer<'a> {\n        let mem_len = buf.len();\n\n        self.configure(\n            request,\n            Dir::PeripheralToMemory,\n            peri_addr as *const u32,\n            buf as *mut MW as *mut u32,\n            mem_len,\n            Increment::Memory,\n            MW::size(),\n            PW::size(),\n            options,\n        );\n        self.start();\n        Transfer {\n            _wake_guard: self.info().wake_guard(),\n            channel: self.reborrow(),\n        }\n    }\n\n    /// Create a write DMA transfer (memory to peripheral).\n    pub unsafe fn write<'a, MW: Word, PW: Word>(\n        &'a mut self,\n        request: Request,\n        buf: &'a [MW],\n        peri_addr: *mut PW,\n        options: TransferOptions,\n    ) -> Transfer<'a> {\n        self.write_raw(request, buf, peri_addr, options)\n    }\n\n    /// Create a write DMA transfer (memory to peripheral), using raw pointers.\n    pub unsafe fn write_raw<'a, MW: Word, PW: Word>(\n        &'a mut self,\n        request: Request,\n        buf: *const [MW],\n        peri_addr: *mut PW,\n        options: TransferOptions,\n    ) -> Transfer<'a> {\n        let mem_len = buf.len();\n\n        self.configure(\n            request,\n            Dir::MemoryToPeripheral,\n            peri_addr as *const u32,\n            buf as *const MW as *mut u32,\n            mem_len,\n            Increment::Memory,\n            MW::size(),\n            PW::size(),\n            options,\n        );\n        self.start();\n        Transfer {\n            _wake_guard: self.info().wake_guard(),\n            channel: self.reborrow(),\n        }\n    }\n\n    /// Create a write DMA transfer (memory to peripheral), writing the same value repeatedly.\n    pub unsafe fn write_repeated<'a, W: Word>(\n        &'a mut self,\n        request: Request,\n        repeated: &'a W,\n        count: usize,\n        peri_addr: *mut W,\n        options: TransferOptions,\n    ) -> Transfer<'a> {\n        assert!(count > 0 && count <= 0xFFFF);\n\n        self.configure(\n            request,\n            Dir::MemoryToPeripheral,\n            peri_addr as *const u32,\n            repeated as *const W as *mut u32,\n            count,\n            Increment::None,\n            W::size(),\n            W::size(),\n            options,\n        );\n        self.start();\n        Transfer {\n            _wake_guard: self.info().wake_guard(),\n            channel: self.reborrow(),\n        }\n    }\n}\n\n/// DMA transfer.\n#[must_use = \"futures do nothing unless you `.await` or poll them\"]\npub struct Transfer<'a> {\n    channel: Channel<'a>,\n    _wake_guard: WakeGuard,\n}\n\nimpl<'a> Transfer<'a> {\n    /// Request the transfer to pause, keeping the existing configuration for this channel.\n    ///\n    /// To resume the transfer, call [`request_resume`](Self::request_resume) again.\n    /// This doesn't immediately stop the transfer, you have to wait until [`is_running`](Self::is_running) returns false.\n    pub fn request_pause(&mut self) {\n        self.channel.request_pause()\n    }\n\n    /// Request the transfer to resume after having been paused.\n    pub fn request_resume(&mut self) {\n        self.channel.request_resume()\n    }\n\n    /// Request the DMA to reset.\n    ///\n    /// The configuration for this channel will **not be preserved**. If you need to restart the transfer\n    /// at a later point with the same configuration, see [`request_pause`](Self::request_pause) instead.\n    pub fn request_reset(&mut self) {\n        self.channel.request_reset()\n    }\n\n    /// Return whether this transfer is still running.\n    ///\n    /// If this returns `false`, it can be because either the transfer finished, or\n    /// it was requested to stop early with [`request_pause`](Self::request_pause).\n    pub fn is_running(&mut self) -> bool {\n        self.channel.is_running()\n    }\n\n    /// Gets the total remaining transfers for the channel\n    /// Note: this will be zero for transfers that completed without cancellation.\n    pub fn get_remaining_transfers(&self) -> u32 {\n        self.channel.get_remaining_transfers()\n    }\n\n    /// Blocking wait until the transfer finishes.\n    pub fn blocking_wait(mut self) {\n        while self.is_running() {}\n\n        // \"Subsequent reads and writes cannot be moved ahead of preceding reads.\"\n        fence(Ordering::SeqCst);\n\n        core::mem::forget(self);\n    }\n\n    pub(crate) unsafe fn unchecked_extend_lifetime(self) -> Transfer<'static> {\n        unsafe { core::mem::transmute(self) }\n    }\n}\n\nimpl<'a> Drop for Transfer<'a> {\n    fn drop(&mut self) {\n        self.request_reset();\n        while self.is_running() {}\n\n        // \"Subsequent reads and writes cannot be moved ahead of preceding reads.\"\n        fence(Ordering::SeqCst);\n    }\n}\n\nimpl<'a> Unpin for Transfer<'a> {}\nimpl<'a> Future for Transfer<'a> {\n    type Output = ();\n    fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {\n        let state: &ChannelState = &STATE[self.channel.id as usize];\n\n        state.waker.register(cx.waker());\n\n        compiler_fence(Ordering::SeqCst);\n        if self.is_running() {\n            Poll::Pending\n        } else {\n            fence(Ordering::Acquire);\n\n            Poll::Ready(())\n        }\n    }\n}\n\n// ==============================\n\nstruct DmaCtrlImpl<'a>(Channel<'a>);\n\nimpl<'a> DmaCtrl for DmaCtrlImpl<'a> {\n    fn get_remaining_transfers(&self) -> usize {\n        self.0.get_remaining_transfers() as _\n    }\n\n    fn reset_complete_count(&mut self) -> usize {\n        let state = &STATE[self.0.id as usize];\n        #[cfg(not(armv6m))]\n        return state.complete_count.swap(0, Ordering::AcqRel);\n        #[cfg(armv6m)]\n        return critical_section::with(|_| {\n            let x = state.complete_count.load(Ordering::Acquire);\n            state.complete_count.store(0, Ordering::Release);\n            x\n        });\n    }\n\n    fn set_waker(&mut self, waker: &Waker) {\n        STATE[self.0.id as usize].waker.register(waker);\n    }\n}\n\n/// Ringbuffer for receiving data using DMA circular mode.\npub struct ReadableRingBuffer<'a, W: Word> {\n    channel: Channel<'a>,\n    _wake_guard: WakeGuard,\n    ringbuf: ReadableDmaRingBuffer<'a, W>,\n}\n\nimpl<'a, W: Word> ReadableRingBuffer<'a, W> {\n    /// Create a new ring buffer.\n    pub unsafe fn new(\n        channel: Channel<'a>,\n        _request: Request,\n        peri_addr: *mut W,\n        buffer: &'a mut [W],\n        mut options: TransferOptions,\n    ) -> Self {\n        let channel: Channel<'a> = channel.into();\n\n        let buffer_ptr = buffer.as_mut_ptr();\n        let len = buffer.len();\n        let dir = Dir::PeripheralToMemory;\n        let data_size = W::size();\n\n        options.half_transfer_ir = true;\n        options.complete_transfer_ir = true;\n        options.circular = true;\n\n        channel.configure(\n            _request,\n            dir,\n            peri_addr as *mut u32,\n            buffer_ptr as *mut u32,\n            len,\n            Increment::Memory,\n            data_size,\n            data_size,\n            options,\n        );\n\n        Self {\n            _wake_guard: channel.info().wake_guard(),\n            channel,\n            ringbuf: ReadableDmaRingBuffer::new(buffer),\n        }\n    }\n\n    /// Start the ring buffer operation.\n    ///\n    /// You must call this after creating it for it to work.\n    pub fn start(&mut self) {\n        self.channel.start();\n    }\n\n    /// Set the frame alignment for the ring buffer.\n    ///\n    /// See [`ReadableDmaRingBuffer::set_alignment`] for details.\n    pub fn set_alignment(&mut self, alignment: usize) {\n        self.ringbuf.set_alignment(alignment);\n    }\n\n    /// Clear all data in the ring buffer.\n    pub fn clear(&mut self) {\n        self.ringbuf.reset(&mut DmaCtrlImpl(self.channel.reborrow()));\n    }\n\n    /// Read elements from the ring buffer\n    /// Return a tuple of the length read and the length remaining in the buffer\n    /// If not all of the elements were read, then there will be some elements in the buffer remaining\n    /// The length remaining is the capacity, ring_buf.len(), less the elements remaining after the read\n    /// Error is returned if the portion to be read was overwritten by the DMA controller.\n    pub fn read(&mut self, buf: &mut [W]) -> Result<(usize, usize), Error> {\n        self.ringbuf.read(&mut DmaCtrlImpl(self.channel.reborrow()), buf)\n    }\n\n    /// Read an exact number of elements from the ringbuffer.\n    ///\n    /// Returns the remaining number of elements available for immediate reading.\n    /// Error is returned if the portion to be read was overwritten by the DMA controller.\n    ///\n    /// Async/Wake Behavior:\n    /// The underlying DMA peripheral only can wake us when its buffer pointer has reached the halfway point,\n    /// and when it wraps around. This means that when called with a buffer of length 'M', when this\n    /// ring buffer was created with a buffer of size 'N':\n    /// - If M equals N/2 or N/2 divides evenly into M, this function will return every N/2 elements read on the DMA source.\n    /// - Otherwise, this function may need up to N/2 extra elements to arrive before returning.\n    pub async fn read_exact(&mut self, buffer: &mut [W]) -> Result<usize, Error> {\n        self.ringbuf\n            .read_exact(&mut DmaCtrlImpl(self.channel.reborrow()), buffer)\n            .await\n    }\n\n    /// The current length of the ringbuffer\n    pub fn len(&mut self) -> Result<usize, Error> {\n        Ok(self.ringbuf.len(&mut DmaCtrlImpl(self.channel.reborrow()))?)\n    }\n\n    /// Read the most recent elements from the ring buffer, discarding any older data.\n    ///\n    /// Returns the number of elements actually read into `buf`. Unlike [`read`](Self::read),\n    /// this method **never returns an overrun error**. If the DMA has lapped the read pointer,\n    /// old data is silently discarded and only the most recent samples are returned.\n    ///\n    /// This is ideal for use cases like ADC sampling where the consumer only cares about\n    /// the latest values.\n    pub fn read_latest(&mut self, buf: &mut [W]) -> usize {\n        self.ringbuf.read_latest(&mut DmaCtrlImpl(self.channel.reborrow()), buf)\n    }\n\n    /// The capacity of the ringbuffer\n    pub const fn capacity(&self) -> usize {\n        self.ringbuf.cap()\n    }\n\n    /// Set a waker to be woken when at least one byte is received.\n    pub fn set_waker(&mut self, waker: &Waker) {\n        DmaCtrlImpl(self.channel.reborrow()).set_waker(waker);\n    }\n\n    /// Request the transfer to pause, keeping the existing configuration for this channel.\n    /// To restart the transfer, call [`start`](Self::start) again.\n    ///\n    /// This doesn't immediately stop the transfer, you have to wait until [`is_running`](Self::is_running) returns false.\n    pub fn request_pause(&mut self) {\n        self.channel.request_pause()\n    }\n\n    /// Request the transfer to resume after having been paused.\n    pub fn request_resume(&mut self) {\n        self.channel.request_resume()\n    }\n\n    /// Request the DMA to reset.\n    ///\n    /// The configuration for this channel will **not be preserved**. If you need to restart the transfer\n    /// at a later point with the same configuration, see [`request_pause`](Self::request_pause) instead.\n    pub fn request_reset(&mut self) {\n        self.channel.request_reset()\n    }\n\n    /// Return whether DMA is still running.\n    ///\n    /// If this returns `false`, it can be because either the transfer finished, or\n    /// it was requested to stop early with [`request_reset`](Self::request_reset).\n    pub fn is_running(&mut self) -> bool {\n        self.channel.is_running()\n    }\n\n    /// Stop the DMA transfer and await until the buffer is full.\n    ///\n    /// This disables the DMA transfer's circular mode so that the transfer\n    /// stops when the buffer is full.\n    ///\n    /// This is designed to be used with streaming input data such as the\n    /// I2S/SAI or ADC.\n    ///\n    /// When using the UART, you probably want `request_reset()`.\n    pub async fn stop(&mut self) {\n        self.channel.disable_circular_mode();\n        //wait until cr.susp reads as true\n        poll_fn(|cx| {\n            self.set_waker(cx.waker());\n            self.channel.poll_stop()\n        })\n        .await\n    }\n}\n\nimpl<'a, W: Word> Drop for ReadableRingBuffer<'a, W> {\n    fn drop(&mut self) {\n        self.request_reset();\n        while self.is_running() {}\n\n        // \"Subsequent reads and writes cannot be moved ahead of preceding reads.\"\n        fence(Ordering::SeqCst);\n    }\n}\n\n/// Ringbuffer for writing data using DMA circular mode.\npub struct WritableRingBuffer<'a, W: Word> {\n    channel: Channel<'a>,\n    _wake_guard: WakeGuard,\n    ringbuf: WritableDmaRingBuffer<'a, W>,\n}\n\nimpl<'a, W: Word> WritableRingBuffer<'a, W> {\n    /// Create a new ring buffer.\n    pub unsafe fn new(\n        channel: Channel<'a>,\n        _request: Request,\n        peri_addr: *mut W,\n        buffer: &'a mut [W],\n        mut options: TransferOptions,\n    ) -> Self {\n        let channel: Channel<'a> = channel.into();\n\n        let len = buffer.len();\n        let dir = Dir::MemoryToPeripheral;\n        let data_size = W::size();\n        let buffer_ptr = buffer.as_mut_ptr();\n\n        options.half_transfer_ir = true;\n        options.complete_transfer_ir = true;\n        options.circular = true;\n\n        channel.configure(\n            _request,\n            dir,\n            peri_addr as *mut u32,\n            buffer_ptr as *mut u32,\n            len,\n            Increment::Memory,\n            data_size,\n            data_size,\n            options,\n        );\n\n        Self {\n            _wake_guard: channel.info().wake_guard(),\n            channel,\n            ringbuf: WritableDmaRingBuffer::new(buffer),\n        }\n    }\n\n    /// Start the ring buffer operation.\n    ///\n    /// You must call this after creating it for it to work.\n    pub fn start(&mut self) {\n        self.channel.start();\n    }\n\n    /// Clear all data in the ring buffer.\n    pub fn clear(&mut self) {\n        self.ringbuf.reset(&mut DmaCtrlImpl(self.channel.reborrow()));\n    }\n\n    /// Write elements directly to the raw buffer.\n    /// This can be used to fill the buffer before starting the DMA transfer.\n    pub fn write_immediate(&mut self, buf: &[W]) -> Result<(usize, usize), Error> {\n        self.ringbuf.write_immediate(buf)\n    }\n\n    /// Write elements from the ring buffer\n    /// Return a tuple of the length written and the length remaining in the buffer\n    pub fn write(&mut self, buf: &[W]) -> Result<(usize, usize), Error> {\n        self.ringbuf.write(&mut DmaCtrlImpl(self.channel.reborrow()), buf)\n    }\n\n    /// Write an exact number of elements to the ringbuffer.\n    pub async fn write_exact(&mut self, buffer: &[W]) -> Result<usize, Error> {\n        self.ringbuf\n            .write_exact(&mut DmaCtrlImpl(self.channel.reborrow()), buffer)\n            .await\n    }\n\n    /// Wait for any ring buffer write error.\n    pub async fn wait_write_error(&mut self) -> Result<usize, Error> {\n        self.ringbuf\n            .wait_write_error(&mut DmaCtrlImpl(self.channel.reborrow()))\n            .await\n    }\n\n    /// The current length of the ringbuffer\n    pub fn len(&mut self) -> Result<usize, Error> {\n        Ok(self.ringbuf.len(&mut DmaCtrlImpl(self.channel.reborrow()))?)\n    }\n\n    /// The capacity of the ringbuffer\n    pub const fn capacity(&self) -> usize {\n        self.ringbuf.cap()\n    }\n\n    /// Set a waker to be woken when at least one byte is received.\n    pub fn set_waker(&mut self, waker: &Waker) {\n        DmaCtrlImpl(self.channel.reborrow()).set_waker(waker);\n    }\n\n    /// Request the DMA to stop.\n    /// The configuration for this channel will **not be preserved**. If you need to restart the transfer\n    /// at a later point with the same configuration, see [`request_pause`](Self::request_pause) instead.\n    ///\n    /// This doesn't immediately stop the transfer, you have to wait until [`is_running`](Self::is_running) returns false.\n    pub fn request_reset(&mut self) {\n        self.channel.request_reset()\n    }\n\n    /// Request the transfer to pause, keeping the existing configuration for this channel.\n    /// To restart the transfer, call [`start`](Self::start) again.\n    ///\n    /// This doesn't immediately stop the transfer, you have to wait until [`is_running`](Self::is_running) returns false.\n    pub fn request_pause(&mut self) {\n        self.channel.request_pause()\n    }\n\n    /// Return whether DMA is still running.\n    ///\n    /// If this returns `false`, it can be because either the transfer finished, or\n    /// it was requested to stop early with [`request_reset`](Self::request_reset).\n    pub fn is_running(&mut self) -> bool {\n        self.channel.is_running()\n    }\n\n    /// Stop the DMA transfer and await until the buffer is empty.\n    ///\n    /// This disables the DMA transfer's circular mode so that the transfer\n    /// stops when all available data has been written.\n    ///\n    /// This is designed to be used with streaming output data such as the\n    /// I2S/SAI or DAC.\n    pub async fn stop(&mut self) {\n        self.channel.disable_circular_mode();\n        //wait until cr.susp reads as true\n        poll_fn(|cx| {\n            self.set_waker(cx.waker());\n            self.channel.poll_stop()\n        })\n        .await\n    }\n}\n\nimpl<'a, W: Word> Drop for WritableRingBuffer<'a, W> {\n    fn drop(&mut self) {\n        self.request_reset();\n        while self.is_running() {}\n\n        // \"Subsequent reads and writes cannot be moved ahead of preceding reads.\"\n        fence(Ordering::SeqCst);\n    }\n}\n"
  },
  {
    "path": "embassy-stm32/src/dma/dmamux.rs",
    "content": "#![macro_use]\n\nuse crate::pac;\n\npub(crate) struct DmamuxInfo {\n    pub(crate) mux: pac::dmamux::Dmamux,\n    pub(crate) num: usize,\n}\n\npub(crate) fn configure_dmamux(info: &DmamuxInfo, request: u8) {\n    let ch_mux_regs = info.mux.ccr(info.num);\n    ch_mux_regs.write(|reg| {\n        reg.set_nbreq(0);\n        reg.set_dmareq_id(request);\n    });\n\n    ch_mux_regs.modify(|reg| {\n        reg.set_ege(true);\n    });\n}\n\n/// safety: must be called only once\npub(crate) unsafe fn init(_cs: critical_section::CriticalSection) {\n    crate::_generated::init_dmamux();\n}\n"
  },
  {
    "path": "embassy-stm32/src/dma/gpdma/linked_list.rs",
    "content": "//! Implementation of the GPDMA linked list and linked list items.\n#![macro_use]\n\nuse stm32_metapac::gpdma::regs;\nuse stm32_metapac::gpdma::vals::Dreq;\n\nuse crate::dma::word::{Word, WordSize};\nuse crate::dma::{Dir, Request};\n\n/// The mode in which to run the linked list.\n#[derive(Debug)]\npub enum RunMode {\n    /// List items are not linked together.\n    Unlinked,\n    /// The list is linked sequentially and only run once.\n    Once,\n    /// The list is linked sequentially, and the end of the list is linked to the beginning.\n    Circular,\n}\n\n/// A linked-list item for linear GPDMA transfers.\n///\n/// Also works for 2D-capable GPDMA channels, but does not use 2D capabilities.\n#[derive(Debug, Copy, Clone, Default)]\n#[repr(C)]\npub struct LinearItem {\n    /// Transfer register 1.\n    pub tr1: regs::ChTr1,\n    /// Transfer register 2.\n    pub tr2: regs::ChTr2,\n    /// Block register 2.\n    pub br1: regs::ChBr1,\n    /// Source address register.\n    pub sar: u32,\n    /// Destination address register.\n    pub dar: u32,\n    /// Linked-list address register.\n    pub llr: regs::ChLlr,\n}\n\nimpl LinearItem {\n    /// Create a new read DMA transfer (peripheral to memory).\n    pub unsafe fn new_read<'d, W: Word>(request: Request, peri_addr: *mut W, buf: &'d mut [W]) -> Self {\n        Self::new_inner(\n            request,\n            Dir::PeripheralToMemory,\n            peri_addr as *const u32,\n            buf as *mut [W] as *mut W as *mut u32,\n            buf.len(),\n            true,\n            W::size(),\n            W::size(),\n        )\n    }\n\n    /// Create a new write DMA transfer (memory to peripheral).\n    pub unsafe fn new_write<'d, MW: Word, PW: Word>(request: Request, buf: &'d [MW], peri_addr: *mut PW) -> Self {\n        Self::new_inner(\n            request,\n            Dir::MemoryToPeripheral,\n            peri_addr as *const u32,\n            buf as *const [MW] as *const MW as *mut u32,\n            buf.len(),\n            true,\n            MW::size(),\n            PW::size(),\n        )\n    }\n\n    unsafe fn new_inner(\n        request: Request,\n        dir: Dir,\n        peri_addr: *const u32,\n        mem_addr: *mut u32,\n        mem_len: usize,\n        incr_mem: bool,\n        data_size: WordSize,\n        dst_size: WordSize,\n    ) -> Self {\n        // BNDT is specified as bytes, not as number of transfers.\n        let Ok(bndt) = (mem_len * data_size.bytes()).try_into() else {\n            panic!(\"DMA transfers may not be larger than 65535 bytes.\");\n        };\n\n        let mut br1 = regs::ChBr1(0);\n        br1.set_bndt(bndt);\n\n        let mut tr1 = regs::ChTr1(0);\n        tr1.set_sdw(data_size.into());\n        tr1.set_ddw(dst_size.into());\n        tr1.set_sinc(dir == Dir::MemoryToPeripheral && incr_mem);\n        tr1.set_dinc(dir == Dir::PeripheralToMemory && incr_mem);\n\n        let mut tr2 = regs::ChTr2(0);\n        tr2.set_dreq(match dir {\n            Dir::MemoryToPeripheral => Dreq::DESTINATION_PERIPHERAL,\n            Dir::PeripheralToMemory => Dreq::SOURCE_PERIPHERAL,\n            Dir::MemoryToMemory => panic!(\"memory-to-memory transfers are not valid for LinearItem\"),\n        });\n        tr2.set_reqsel(request);\n\n        let (sar, dar) = match dir {\n            Dir::MemoryToPeripheral => (mem_addr as _, peri_addr as _),\n            Dir::PeripheralToMemory => (peri_addr as _, mem_addr as _),\n            Dir::MemoryToMemory => panic!(\"memory-to-memory transfers are not valid for LinearItem\"),\n        };\n\n        let llr = regs::ChLlr(0);\n\n        Self {\n            tr1,\n            tr2,\n            br1,\n            sar,\n            dar,\n            llr,\n        }\n    }\n\n    /// Link to the next linear item at the given address.\n    ///\n    /// Enables channel update bits.\n    fn link_to(&mut self, next: u16) {\n        let mut llr = regs::ChLlr(0);\n\n        llr.set_ut1(true);\n        llr.set_ut2(true);\n        llr.set_ub1(true);\n        llr.set_usa(true);\n        llr.set_uda(true);\n        llr.set_ull(true);\n\n        // Lower two bits are ignored: 32 bit aligned.\n        llr.set_la(next >> 2);\n\n        self.llr = llr;\n    }\n\n    /// Unlink the next linear item.\n    ///\n    /// Disables channel update bits.\n    fn unlink(&mut self) {\n        self.llr = regs::ChLlr(0);\n    }\n\n    /// The item's transfer count in number of words.\n    fn transfer_count(&self) -> usize {\n        let word_size: WordSize = self.tr1.ddw().into();\n        self.br1.bndt() as usize / word_size.bytes()\n    }\n}\n\n/// A table of linked list items.\n#[repr(C)]\npub struct Table<const ITEM_COUNT: usize> {\n    /// The items.\n    pub items: [LinearItem; ITEM_COUNT],\n}\n\nimpl<const ITEM_COUNT: usize> Table<ITEM_COUNT> {\n    /// Create a new table.\n    pub fn new(items: [LinearItem; ITEM_COUNT]) -> Self {\n        assert!(!items.is_empty());\n\n        Self { items }\n    }\n\n    /// Create a ping-pong linked-list table.\n    ///\n    /// This uses two linked-list items, one for each half of the buffer.\n    pub unsafe fn new_ping_pong<W: Word>(\n        request: Request,\n        peri_addr: *mut W,\n        buffer: &mut [W],\n        direction: Dir,\n    ) -> Table<2> {\n        // Buffer halves should be the same length.\n        let half_len = buffer.len() / 2;\n        assert_eq!(half_len * 2, buffer.len());\n\n        let items = match direction {\n            Dir::MemoryToPeripheral => [\n                LinearItem::new_write(request, &mut buffer[..half_len], peri_addr),\n                LinearItem::new_write(request, &mut buffer[half_len..], peri_addr),\n            ],\n            Dir::PeripheralToMemory => [\n                LinearItem::new_read(request, peri_addr, &mut buffer[..half_len]),\n                LinearItem::new_read(request, peri_addr, &mut buffer[half_len..]),\n            ],\n            Dir::MemoryToMemory => panic!(\"memory-to-memory transfers are not valid for LinearItem\"),\n        };\n\n        Table::new(items)\n    }\n\n    /// Link the table as given by the run mode.\n    pub fn link(&mut self, run_mode: RunMode) {\n        if matches!(run_mode, RunMode::Once | RunMode::Circular) {\n            self.link_sequential();\n        }\n\n        if matches!(run_mode, RunMode::Circular) {\n            self.link_repeat();\n        }\n    }\n\n    /// The number of linked list items.\n    pub fn len(&self) -> usize {\n        self.items.len()\n    }\n\n    /// The total transfer count of the table in number of words.\n    pub fn transfer_count(&self) -> usize {\n        let mut count = 0;\n        for item in self.items {\n            count += item.transfer_count() as usize\n        }\n\n        count\n    }\n\n    /// Link items of given indices together: first -> second.\n    pub fn link_indices(&mut self, first: usize, second: usize) {\n        assert!(first < self.len());\n        assert!(second < self.len());\n\n        let second_item = self.offset_address(second);\n        self.items[first].link_to(second_item);\n    }\n\n    /// Link items sequentially.\n    pub fn link_sequential(&mut self) {\n        if self.len() > 1 {\n            for index in 0..(self.items.len() - 1) {\n                let next = self.offset_address(index + 1);\n                self.items[index].link_to(next);\n            }\n        }\n    }\n\n    /// Link last to first item.\n    pub fn link_repeat(&mut self) {\n        let first_address = self.offset_address(0);\n        self.items.last_mut().unwrap().link_to(first_address);\n    }\n\n    /// Unlink all items.\n    pub fn unlink(&mut self) {\n        for item in self.items.iter_mut() {\n            item.unlink();\n        }\n    }\n\n    /// Linked list base address (upper 16 address bits).\n    pub fn base_address(&self) -> u16 {\n        ((&raw const self.items as u32) >> 16) as _\n    }\n\n    /// Linked list offset address (lower 16 address bits) at the selected index.\n    pub fn offset_address(&self, index: usize) -> u16 {\n        assert!(self.items.len() > index);\n\n        let address = &raw const self.items[index] as _;\n\n        // Ensure 32 bit address alignment.\n        assert_eq!(address & 0b11, 0);\n\n        address\n    }\n}\n"
  },
  {
    "path": "embassy-stm32/src/dma/gpdma/mod.rs",
    "content": "#![macro_use]\n\nuse core::future::Future;\nuse core::pin::Pin;\nuse core::sync::atomic::{AtomicUsize, Ordering, compiler_fence, fence};\nuse core::task::{Context, Poll};\n\nuse embassy_sync::waitqueue::AtomicWaker;\nuse linked_list::Table;\n\nuse super::word::{Word, WordSize};\nuse super::{Channel, Dir, Request, STATE};\nuse crate::interrupt::typelevel::Interrupt;\nuse crate::pac;\nuse crate::pac::gpdma::vals;\nuse crate::rcc::WakeGuard;\n\npub mod linked_list;\npub mod ringbuffered;\n\npub(crate) struct ChannelInfo {\n    pub(crate) dma: pac::gpdma::Gpdma,\n    pub(crate) num: usize,\n    #[cfg(feature = \"_dual-core\")]\n    pub(crate) irq: pac::Interrupt,\n    #[cfg(feature = \"low-power\")]\n    pub(crate) stop_mode: crate::rcc::StopMode,\n}\n\nimpl ChannelInfo {\n    fn wake_guard(&self) -> WakeGuard {\n        WakeGuard::new(\n            #[cfg(feature = \"low-power\")]\n            self.stop_mode,\n        )\n    }\n}\n\n/// DMA request priority\n#[derive(Debug, Copy, Clone, PartialEq, Eq)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub enum Priority {\n    /// Low Priority\n    Low,\n    /// Medium Priority\n    Medium,\n    /// High Priority\n    High,\n    /// Very High Priority\n    VeryHigh,\n}\n\nimpl From<Priority> for pac::gpdma::vals::Prio {\n    fn from(value: Priority) -> Self {\n        match value {\n            Priority::Low => pac::gpdma::vals::Prio::LOW_WITH_LOWH_WEIGHT,\n            Priority::Medium => pac::gpdma::vals::Prio::LOW_WITH_MID_WEIGHT,\n            Priority::High => pac::gpdma::vals::Prio::LOW_WITH_HIGH_WEIGHT,\n            Priority::VeryHigh => pac::gpdma::vals::Prio::HIGH,\n        }\n    }\n}\n\n/// GPDMA transfer options.\n#[derive(Debug, Copy, Clone, PartialEq, Eq)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\n#[non_exhaustive]\npub struct TransferOptions {\n    /// Request priority level.\n    pub priority: Priority,\n    /// Enable half transfer interrupt.\n    pub half_transfer_ir: bool,\n    /// Enable transfer complete interrupt.\n    pub complete_transfer_ir: bool,\n}\n\nimpl Default for TransferOptions {\n    fn default() -> Self {\n        Self {\n            priority: Priority::VeryHigh,\n            half_transfer_ir: false,\n            complete_transfer_ir: true,\n        }\n    }\n}\n\nimpl From<WordSize> for vals::Dw {\n    fn from(raw: WordSize) -> Self {\n        match raw {\n            WordSize::OneByte => Self::BYTE,\n            WordSize::TwoBytes => Self::HALF_WORD,\n            WordSize::FourBytes => Self::WORD,\n            _ => panic!(\"Invalid word size\"),\n        }\n    }\n}\n\nimpl From<vals::Dw> for WordSize {\n    fn from(raw: vals::Dw) -> Self {\n        match raw {\n            vals::Dw::BYTE => Self::OneByte,\n            vals::Dw::HALF_WORD => Self::TwoBytes,\n            vals::Dw::WORD => Self::FourBytes,\n            _ => panic!(\"Invalid word size\"),\n        }\n    }\n}\n\npub(crate) struct LLiState {\n    /// The number of linked-list items.\n    count: AtomicUsize,\n    /// The index of the current linked-list item.\n    index: AtomicUsize,\n    /// The total transfer count of all linked-list items in number of words.\n    transfer_count: AtomicUsize,\n}\n\npub(crate) struct ChannelState {\n    waker: AtomicWaker,\n    complete_count: AtomicUsize,\n    lli_state: LLiState,\n}\n\nimpl ChannelState {\n    pub(crate) const NEW: Self = Self {\n        waker: AtomicWaker::new(),\n        complete_count: AtomicUsize::new(0),\n\n        lli_state: LLiState {\n            count: AtomicUsize::new(0),\n            index: AtomicUsize::new(0),\n            transfer_count: AtomicUsize::new(0),\n        },\n    };\n}\n\n/// safety: must be called only once\npub(crate) unsafe fn init(cs: critical_section::CriticalSection, irq_priority: crate::interrupt::Priority) {\n    foreach_interrupt! {\n        ($peri:ident, gpdma, $block:ident, $signal_name:ident, $irq:ident) => {\n            crate::interrupt::typelevel::$irq::set_priority_with_cs(cs, irq_priority);\n            #[cfg(not(feature = \"_dual-core\"))]\n            crate::interrupt::typelevel::$irq::enable();\n        };\n    }\n    crate::_generated::init_gpdma();\n}\n\npub(crate) unsafe fn on_irq(id: u8) {\n    let info = super::info(id);\n    #[cfg(feature = \"_dual-core\")]\n    {\n        use embassy_hal_internal::interrupt::InterruptExt as _;\n        info.irq.enable();\n    }\n\n    let state = &STATE[id as usize];\n\n    let ch = info.dma.ch(info.num);\n    let sr = ch.sr().read();\n\n    if sr.dtef() {\n        panic!(\n            \"DMA: data transfer error on DMA@{:08x} channel {}\",\n            info.dma.as_ptr() as u32,\n            info.num\n        );\n    }\n    if sr.usef() {\n        panic!(\n            \"DMA: user settings error on DMA@{:08x} channel {}\",\n            info.dma.as_ptr() as u32,\n            info.num\n        );\n    }\n    if sr.ulef() {\n        panic!(\n            \"DMA: link transfer error on DMA@{:08x} channel {}\",\n            info.dma.as_ptr() as u32,\n            info.num\n        );\n    }\n\n    if sr.htf() {\n        ch.fcr().write(|w| w.set_htf(true));\n    }\n\n    if sr.tcf() {\n        ch.fcr().write(|w| w.set_tcf(true));\n\n        let lli_count = state.lli_state.count.load(Ordering::Acquire);\n        let complete = if lli_count > 0 {\n            let next_lli_index = state.lli_state.index.load(Ordering::Acquire) + 1;\n            let complete = next_lli_index >= lli_count;\n\n            state\n                .lli_state\n                .index\n                .store(if complete { 0 } else { next_lli_index }, Ordering::Release);\n\n            complete\n        } else {\n            true\n        };\n\n        if complete {\n            state.complete_count.fetch_add(1, Ordering::Release);\n        }\n    }\n\n    if sr.suspf() {\n        // Disable all xxIEs to prevent the irq from firing again.\n        ch.cr().write(|_| {});\n    }\n    state.waker.wake();\n}\n\nimpl<'d> Channel<'d> {\n    fn info(&self) -> &'static super::ChannelInfo {\n        super::info(self.id)\n    }\n\n    fn get_remaining_transfers(&self) -> u16 {\n        let info = self.info();\n        let ch = info.dma.ch(info.num);\n        let word_size: WordSize = ch.tr1().read().ddw().into();\n\n        ch.br1().read().bndt() / word_size.bytes() as u16\n    }\n\n    unsafe fn configure(\n        &self,\n        request: Request,\n        dir: Dir,\n        peri_addr: *const u32,\n        mem_addr: *mut u32,\n        mem_len: usize,\n        incr_mem: bool,\n        data_size: WordSize,\n        dst_size: WordSize,\n        options: TransferOptions,\n    ) {\n        // BNDT is specified as bytes, not as number of transfers.\n        let Ok(bndt) = (mem_len * data_size.bytes()).try_into() else {\n            panic!(\"DMA transfers may not be larger than 65535 bytes.\");\n        };\n\n        let info = self.info();\n        let ch = info.dma.ch(info.num);\n\n        // \"Preceding reads and writes cannot be moved past subsequent writes.\"\n        fence(Ordering::SeqCst);\n\n        if ch.cr().read().en() {\n            ch.cr().modify(|w| w.set_susp(true));\n            while !ch.sr().read().suspf() {}\n        }\n\n        ch.cr().write(|w| w.set_reset(true));\n        ch.fcr().write(|w| {\n            // Clear all irqs\n            w.set_dtef(true);\n            w.set_htf(true);\n            w.set_suspf(true);\n            w.set_tcf(true);\n            w.set_tof(true);\n            w.set_ulef(true);\n            w.set_usef(true);\n        });\n        ch.llr().write(|_| {}); // no linked list\n        ch.tr1().write(|w| {\n            w.set_sdw(data_size.into());\n            w.set_ddw(dst_size.into());\n            w.set_sinc(dir == Dir::MemoryToPeripheral && incr_mem);\n            w.set_dinc(dir == Dir::PeripheralToMemory && incr_mem);\n            w.set_dap(match dir {\n                Dir::MemoryToPeripheral => vals::Ap::PORT1, // Destination is peripheral on AHB for HPDMA\n                Dir::PeripheralToMemory => vals::Ap::PORT0, // Destination is memory on AXI for HPDMA\n                Dir::MemoryToMemory => panic!(\"memory-to-memory transfers not implemented for GPDMA\"),\n            });\n            w.set_sap(match dir {\n                Dir::MemoryToPeripheral => vals::Ap::PORT0, // Source is memory on AXI for HPDMA\n                Dir::PeripheralToMemory => vals::Ap::PORT1, // Source is peripheral on AHB for HPDMA\n                Dir::MemoryToMemory => panic!(\"memory-to-memory transfers not implemented for GPDMA\"),\n            });\n        });\n        ch.tr2().write(|w| {\n            w.set_dreq(match dir {\n                Dir::MemoryToPeripheral => vals::Dreq::DESTINATION_PERIPHERAL,\n                Dir::PeripheralToMemory => vals::Dreq::SOURCE_PERIPHERAL,\n                Dir::MemoryToMemory => panic!(\"memory-to-memory transfers not implemented for GPDMA\"),\n            });\n            w.set_reqsel(request);\n        });\n        ch.tr3().write(|_| {}); // no address offsets.\n        ch.br1().write(|w| w.set_bndt(bndt));\n\n        match dir {\n            Dir::MemoryToPeripheral => {\n                ch.sar().write_value(mem_addr as _);\n                ch.dar().write_value(peri_addr as _);\n            }\n            Dir::PeripheralToMemory => {\n                ch.sar().write_value(peri_addr as _);\n                ch.dar().write_value(mem_addr as _);\n            }\n            Dir::MemoryToMemory => panic!(\"memory-to-memory transfers not implemented for GPDMA\"),\n        }\n\n        ch.cr().write(|w| {\n            w.set_prio(options.priority.into());\n            w.set_htie(options.half_transfer_ir);\n            w.set_tcie(options.complete_transfer_ir);\n            w.set_useie(true);\n            w.set_dteie(true);\n            w.set_suspie(true);\n        });\n\n        let state = &STATE[self.id as usize];\n        state.lli_state.count.store(0, Ordering::Relaxed);\n        state.lli_state.index.store(0, Ordering::Relaxed);\n        state.lli_state.transfer_count.store(0, Ordering::Relaxed)\n    }\n\n    /// Configure a linked-list transfer.\n    unsafe fn configure_linked_list<const ITEM_COUNT: usize>(\n        &self,\n        table: &Table<ITEM_COUNT>,\n        options: TransferOptions,\n    ) {\n        let info = self.info();\n        let ch = info.dma.ch(info.num);\n\n        // \"Preceding reads and writes cannot be moved past subsequent writes.\"\n        fence(Ordering::SeqCst);\n\n        ch.cr().write(|w| w.set_reset(true));\n        ch.fcr().write(|w| {\n            // Clear all irqs\n            w.set_dtef(true);\n            w.set_htf(true);\n            w.set_suspf(true);\n            w.set_tcf(true);\n            w.set_tof(true);\n            w.set_ulef(true);\n            w.set_usef(true);\n        });\n        ch.lbar().write(|reg| reg.set_lba(table.base_address()));\n\n        // Empty LLI0.\n        ch.br1().write(|w| w.set_bndt(0));\n\n        // Enable all linked-list field updates.\n        ch.llr().write(|w| {\n            w.set_ut1(true);\n            w.set_ut2(true);\n            w.set_ub1(true);\n            w.set_usa(true);\n            w.set_uda(true);\n            w.set_ull(true);\n\n            // Lower two bits are ignored: 32 bit aligned.\n            w.set_la(table.offset_address(0) >> 2);\n        });\n\n        ch.tr3().write(|_| {}); // no address offsets.\n\n        ch.cr().write(|w| {\n            w.set_prio(options.priority.into());\n            w.set_htie(options.half_transfer_ir);\n            w.set_tcie(options.complete_transfer_ir);\n            w.set_useie(true);\n            w.set_uleie(true);\n            w.set_dteie(true);\n            w.set_suspie(true);\n        });\n\n        let state = &STATE[self.id as usize];\n        state.lli_state.count.store(ITEM_COUNT, Ordering::Relaxed);\n        state.lli_state.index.store(0, Ordering::Relaxed);\n        state\n            .lli_state\n            .transfer_count\n            .store(table.transfer_count(), Ordering::Relaxed)\n    }\n\n    fn start(&self) {\n        let info = self.info();\n        let ch = info.dma.ch(info.num);\n\n        ch.cr().modify(|w| w.set_en(true));\n    }\n\n    fn request_pause(&self) {\n        let info = self.info();\n        let ch = info.dma.ch(info.num);\n\n        ch.cr().modify(|w| w.set_susp(true))\n    }\n\n    fn request_resume(&self) {\n        let info = self.info();\n        let ch = info.dma.ch(info.num);\n\n        ch.cr().modify(|w| w.set_susp(false));\n    }\n\n    fn request_reset(&self) {\n        let info = self.info();\n        let ch = info.dma.ch(info.num);\n\n        self.request_pause();\n        while self.is_running() {}\n\n        ch.cr().modify(|w| w.set_reset(true));\n    }\n\n    fn is_running(&self) -> bool {\n        let info = self.info();\n        let ch = info.dma.ch(info.num);\n\n        let sr = ch.sr().read();\n\n        !sr.suspf() && !sr.idlef()\n    }\n\n    fn poll_stop(&self) -> Poll<()> {\n        compiler_fence(Ordering::SeqCst);\n\n        if !self.is_running() {\n            fence(Ordering::Acquire);\n\n            Poll::Ready(())\n        } else {\n            Poll::Pending\n        }\n    }\n\n    /// Create a read DMA transfer (peripheral to memory).\n    pub unsafe fn read<'a, W: Word>(\n        &'a mut self,\n        request: Request,\n        peri_addr: *mut W,\n        buf: &'a mut [W],\n        options: TransferOptions,\n    ) -> Transfer<'a> {\n        self.read_raw(request, peri_addr, buf, options)\n    }\n\n    /// Create a read DMA transfer (peripheral to memory), using raw pointers.\n    pub unsafe fn read_raw<'a, MW: Word, PW: Word>(\n        &'a mut self,\n        request: Request,\n        peri_addr: *mut PW,\n        buf: *mut [MW],\n        options: TransferOptions,\n    ) -> Transfer<'a> {\n        let mem_len = buf.len();\n        assert!(mem_len > 0 && mem_len <= 0xFFFF);\n\n        self.configure(\n            request,\n            Dir::PeripheralToMemory,\n            peri_addr as *const u32,\n            buf as *mut MW as *mut u32,\n            mem_len,\n            true,\n            PW::size(),\n            MW::size(),\n            options,\n        );\n        self.start();\n\n        Transfer {\n            _wake_guard: self.info().wake_guard(),\n            channel: self.reborrow(),\n        }\n    }\n\n    /// Create a write DMA transfer (memory to peripheral).\n    pub unsafe fn write<'a, MW: Word, PW: Word>(\n        &'a mut self,\n        request: Request,\n        buf: &'a [MW],\n        peri_addr: *mut PW,\n        options: TransferOptions,\n    ) -> Transfer<'a> {\n        self.write_raw(request, buf, peri_addr, options)\n    }\n\n    /// Create a write DMA transfer (memory to peripheral), using raw pointers.\n    pub unsafe fn write_raw<'a, MW: Word, PW: Word>(\n        &'a mut self,\n        request: Request,\n        buf: *const [MW],\n        peri_addr: *mut PW,\n        options: TransferOptions,\n    ) -> Transfer<'a> {\n        let mem_len = buf.len();\n        assert!(mem_len > 0 && mem_len <= 0xFFFF);\n\n        self.configure(\n            request,\n            Dir::MemoryToPeripheral,\n            peri_addr as *const u32,\n            buf as *const MW as *mut u32,\n            mem_len,\n            true,\n            MW::size(),\n            PW::size(),\n            options,\n        );\n        self.start();\n\n        Transfer {\n            _wake_guard: self.info().wake_guard(),\n            channel: self.reborrow(),\n        }\n    }\n\n    /// Create a write DMA transfer (memory to peripheral), writing the same value repeatedly.\n    pub unsafe fn write_repeated<'a, MW: Word, PW: Word>(\n        &'a mut self,\n        request: Request,\n        repeated: &'a MW,\n        count: usize,\n        peri_addr: *mut PW,\n        options: TransferOptions,\n    ) -> Transfer<'a> {\n        assert!(count > 0 && count <= 0xFFFF);\n\n        self.configure(\n            request,\n            Dir::MemoryToPeripheral,\n            peri_addr as *const u32,\n            repeated as *const MW as *mut u32,\n            count,\n            false,\n            MW::size(),\n            PW::size(),\n            options,\n        );\n        self.start();\n\n        Transfer {\n            _wake_guard: self.info().wake_guard(),\n            channel: self.reborrow(),\n        }\n    }\n\n    /// Create a linked-list DMA transfer.\n    pub unsafe fn linked_list<'a, const ITEM_COUNT: usize>(\n        &'a mut self,\n        table: Table<ITEM_COUNT>,\n        options: TransferOptions,\n    ) -> LinkedListTransfer<'a, ITEM_COUNT> {\n        self.configure_linked_list(&table, options);\n        self.start();\n\n        LinkedListTransfer {\n            _wake_guard: self.info().wake_guard(),\n            channel: self.reborrow(),\n        }\n    }\n}\n\n/// Linked-list DMA transfer.\n#[must_use = \"futures do nothing unless you `.await` or poll them\"]\npub struct LinkedListTransfer<'a, const ITEM_COUNT: usize> {\n    channel: Channel<'a>,\n    _wake_guard: WakeGuard,\n}\n\nimpl<'a, const ITEM_COUNT: usize> LinkedListTransfer<'a, ITEM_COUNT> {\n    /// Request the transfer to pause, keeping the existing configuration for this channel.\n    ///\n    /// To resume the transfer, call [`request_resume`](Self::request_resume) again.\n    /// This doesn't immediately stop the transfer, you have to wait until [`is_running`](Self::is_running) returns false.\n    pub fn request_pause(&mut self) {\n        self.channel.request_pause()\n    }\n\n    /// Request the transfer to resume after having been paused.\n    pub fn request_resume(&mut self) {\n        self.channel.request_resume()\n    }\n\n    /// Request the DMA to reset.\n    ///\n    /// The configuration for this channel will **not be preserved**. If you need to restart the transfer\n    /// at a later point with the same configuration, see [`request_pause`](Self::request_pause) instead.\n    pub fn request_reset(&mut self) {\n        self.channel.request_reset()\n    }\n\n    /// Return whether this transfer is still running.\n    ///\n    /// If this returns `false`, it can be because either the transfer finished, or\n    /// it was requested to stop early with [`request_pause`](Self::request_pause).\n    pub fn is_running(&mut self) -> bool {\n        self.channel.is_running()\n    }\n\n    /// Gets the total remaining transfers for the channel\n    /// Note: this will be zero for transfers that completed without cancellation.\n    pub fn get_remaining_transfers(&self) -> u16 {\n        self.channel.get_remaining_transfers()\n    }\n\n    /// Blocking wait until the transfer finishes.\n    pub fn blocking_wait(mut self) {\n        while self.is_running() {}\n\n        // \"Subsequent reads and writes cannot be moved ahead of preceding reads.\"\n        fence(Ordering::SeqCst);\n\n        core::mem::forget(self);\n    }\n}\n\nimpl<'a, const ITEM_COUNT: usize> Drop for LinkedListTransfer<'a, ITEM_COUNT> {\n    fn drop(&mut self) {\n        self.request_reset();\n\n        // \"Subsequent reads and writes cannot be moved ahead of preceding reads.\"\n        fence(Ordering::SeqCst);\n    }\n}\n\nimpl<'a, const ITEM_COUNT: usize> Unpin for LinkedListTransfer<'a, ITEM_COUNT> {}\nimpl<'a, const ITEM_COUNT: usize> Future for LinkedListTransfer<'a, ITEM_COUNT> {\n    type Output = ();\n    fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {\n        let state = &STATE[self.channel.id as usize];\n        state.waker.register(cx.waker());\n\n        if self.is_running() {\n            Poll::Pending\n        } else {\n            fence(Ordering::Acquire);\n\n            Poll::Ready(())\n        }\n    }\n}\n\n/// DMA transfer.\n#[must_use = \"futures do nothing unless you `.await` or poll them\"]\npub struct Transfer<'a> {\n    channel: Channel<'a>,\n    _wake_guard: WakeGuard,\n}\n\nimpl<'a> Transfer<'a> {\n    /// Request the transfer to pause, keeping the existing configuration for this channel.\n    /// To restart the transfer, call [`start`](Self::start) again.\n    ///\n    /// This doesn't immediately stop the transfer, you have to wait until [`is_running`](Self::is_running) returns false.\n    pub fn request_pause(&mut self) {\n        self.channel.request_pause()\n    }\n\n    /// Request the transfer to resume after being suspended.\n    pub fn request_resume(&mut self) {\n        self.channel.request_resume()\n    }\n\n    /// Request the DMA to reset.\n    ///\n    /// The configuration for this channel will **not be preserved**. If you need to restart the transfer\n    /// at a later point with the same configuration, see [`request_pause`](Self::request_pause) instead.\n    pub fn request_reset(&mut self) {\n        self.channel.request_reset()\n    }\n\n    /// Return whether this transfer is still running.\n    ///\n    /// If this returns `false`, it can be because either the transfer finished, or\n    /// it was requested to stop early with [`request_pause`](Self::request_pause).\n    pub fn is_running(&mut self) -> bool {\n        self.channel.is_running()\n    }\n\n    /// Gets the total remaining transfers for the channel\n    /// Note: this will be zero for transfers that completed without cancellation.\n    pub fn get_remaining_transfers(&self) -> u16 {\n        self.channel.get_remaining_transfers()\n    }\n\n    /// Blocking wait until the transfer finishes.\n    pub fn blocking_wait(mut self) {\n        while self.is_running() {}\n\n        // \"Subsequent reads and writes cannot be moved ahead of preceding reads.\"\n        fence(Ordering::SeqCst);\n\n        core::mem::forget(self);\n    }\n\n    pub(crate) unsafe fn unchecked_extend_lifetime(self) -> Transfer<'static> {\n        unsafe { core::mem::transmute(self) }\n    }\n}\n\nimpl<'a> Drop for Transfer<'a> {\n    fn drop(&mut self) {\n        self.request_pause();\n        while self.is_running() {}\n\n        // \"Subsequent reads and writes cannot be moved ahead of preceding reads.\"\n        fence(Ordering::SeqCst);\n    }\n}\n\nimpl<'a> Unpin for Transfer<'a> {}\nimpl<'a> Future for Transfer<'a> {\n    type Output = ();\n    fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {\n        let state = &STATE[self.channel.id as usize];\n        state.waker.register(cx.waker());\n\n        compiler_fence(Ordering::SeqCst);\n        if self.is_running() {\n            Poll::Pending\n        } else {\n            fence(Ordering::Acquire);\n\n            Poll::Ready(())\n        }\n    }\n}\n"
  },
  {
    "path": "embassy-stm32/src/dma/gpdma/ringbuffered.rs",
    "content": "//! GPDMA ring buffer implementation.\n//!\n//! FIXME: Add request_pause functionality?\n//! FIXME: Stop the DMA, if a user does not queue new transfers (chain of linked-list items ends automatically).\nuse core::future::poll_fn;\nuse core::sync::atomic::{Ordering, fence};\nuse core::task::Waker;\n\nuse super::{Channel, STATE, TransferOptions};\nuse crate::dma::gpdma::linked_list::{RunMode, Table};\nuse crate::dma::ringbuffer::{DmaCtrl, Error, ReadableDmaRingBuffer, WritableDmaRingBuffer};\nuse crate::dma::word::Word;\nuse crate::dma::{Dir, Request};\nuse crate::rcc::WakeGuard;\n\nstruct DmaCtrlImpl<'a>(Channel<'a>);\n\nimpl<'a> DmaCtrl for DmaCtrlImpl<'a> {\n    fn get_remaining_transfers(&self) -> usize {\n        let state = &STATE[self.0.id as usize];\n        let current_remaining = self.0.get_remaining_transfers() as usize;\n\n        let lli_count = state.lli_state.count.load(Ordering::Acquire);\n\n        if lli_count > 0 {\n            // In linked-list mode, the remaining transfers are the sum of the full lengths of LLIs that follow,\n            // and the remaining transfers for the current LLI.\n            let lli_index = state.lli_state.index.load(Ordering::Acquire);\n            let single_transfer_count = state.lli_state.transfer_count.load(Ordering::Acquire) / lli_count;\n\n            (lli_count - lli_index - 1) * single_transfer_count + current_remaining\n        } else {\n            // No linked-list mode.\n            current_remaining\n        }\n    }\n\n    fn reset_complete_count(&mut self) -> usize {\n        let state = &STATE[self.0.id as usize];\n\n        state.complete_count.swap(0, Ordering::AcqRel)\n    }\n\n    fn set_waker(&mut self, waker: &Waker) {\n        STATE[self.0.id as usize].waker.register(waker);\n    }\n}\n\n/// Ringbuffer for receiving data using GPDMA linked-list mode.\npub struct ReadableRingBuffer<'a, W: Word> {\n    channel: Channel<'a>,\n    _wake_guard: WakeGuard,\n    ringbuf: ReadableDmaRingBuffer<'a, W>,\n    table: Table<2>,\n    options: TransferOptions,\n}\n\nimpl<'a, W: Word> ReadableRingBuffer<'a, W> {\n    /// Create a new ring buffer.\n    ///\n    /// Transfer options are applied to the individual linked list items.\n    pub unsafe fn new(\n        channel: Channel<'a>,\n        request: Request,\n        peri_addr: *mut W,\n        buffer: &'a mut [W],\n        options: TransferOptions,\n    ) -> Self {\n        let table = Table::<2>::new_ping_pong::<W>(request, peri_addr, buffer, Dir::PeripheralToMemory);\n\n        Self {\n            _wake_guard: channel.info().wake_guard(),\n            channel,\n            ringbuf: ReadableDmaRingBuffer::new(buffer),\n            table,\n            options,\n        }\n    }\n\n    /// Start the ring buffer operation.\n    pub fn start(&mut self) {\n        // Apply the default configuration to the channel.\n        unsafe { self.channel.configure_linked_list(&self.table, self.options) };\n        self.table.link(RunMode::Circular);\n        self.channel.start();\n    }\n\n    /// Set the frame alignment for the ring buffer.\n    ///\n    /// See [`ReadableDmaRingBuffer::set_alignment`] for details.\n    pub fn set_alignment(&mut self, alignment: usize) {\n        self.ringbuf.set_alignment(alignment);\n    }\n\n    /// Clear all data in the ring buffer.\n    pub fn clear(&mut self) {\n        self.ringbuf.reset(&mut DmaCtrlImpl(self.channel.reborrow()));\n    }\n\n    /// Read elements from the ring buffer\n    /// Return a tuple of the length read and the length remaining in the buffer\n    /// If not all of the elements were read, then there will be some elements in the buffer remaining\n    /// The length remaining is the capacity, ring_buf.len(), less the elements remaining after the read\n    /// Error is returned if the portion to be read was overwritten by the DMA controller.\n    pub fn read(&mut self, buf: &mut [W]) -> Result<(usize, usize), Error> {\n        self.ringbuf.read(&mut DmaCtrlImpl(self.channel.reborrow()), buf)\n    }\n\n    /// Read an exact number of elements from the ringbuffer.\n    ///\n    /// Returns the remaining number of elements available for immediate reading.\n    /// Error is returned if the portion to be read was overwritten by the DMA controller.\n    ///\n    /// Async/Wake Behavior:\n    /// The underlying DMA peripheral only can wake us when its buffer pointer has reached the halfway point,\n    /// and when it wraps around. This means that when called with a buffer of length 'M', when this\n    /// ring buffer was created with a buffer of size 'N':\n    /// - If M equals N/2 or N/2 divides evenly into M, this function will return every N/2 elements read on the DMA source.\n    /// - Otherwise, this function may need up to N/2 extra elements to arrive before returning.\n    pub async fn read_exact(&mut self, buffer: &mut [W]) -> Result<usize, Error> {\n        self.ringbuf\n            .read_exact(&mut DmaCtrlImpl(self.channel.reborrow()), buffer)\n            .await\n    }\n\n    /// The current length of the ringbuffer\n    pub fn len(&mut self) -> Result<usize, Error> {\n        Ok(self.ringbuf.len(&mut DmaCtrlImpl(self.channel.reborrow()))?)\n    }\n\n    /// Read the most recent elements from the ring buffer, discarding any older data.\n    ///\n    /// Returns the number of elements actually read into `buf`. Unlike [`read`](Self::read),\n    /// this method **never returns an overrun error**. If the DMA has lapped the read pointer,\n    /// old data is silently discarded and only the most recent samples are returned.\n    ///\n    /// This is ideal for use cases like ADC sampling where the consumer only cares about\n    /// the latest values.\n    pub fn read_latest(&mut self, buf: &mut [W]) -> usize {\n        self.ringbuf.read_latest(&mut DmaCtrlImpl(self.channel.reborrow()), buf)\n    }\n\n    /// The capacity of the ringbuffer\n    pub const fn capacity(&self) -> usize {\n        self.ringbuf.cap()\n    }\n\n    /// Set a waker to be woken when at least one byte is received.\n    pub fn set_waker(&mut self, waker: &Waker) {\n        DmaCtrlImpl(self.channel.reborrow()).set_waker(waker);\n    }\n\n    /// Request the transfer to pause, keeping the existing configuration for this channel.\n    ///\n    /// To resume the transfer, call [`request_resume`](Self::request_resume) again.\n    /// This doesn't immediately stop the transfer, you have to wait until [`is_running`](Self::is_running) returns false.\n    pub fn request_pause(&mut self) {\n        self.channel.request_pause()\n    }\n\n    /// Request the transfer to resume after having been paused.\n    pub fn request_resume(&mut self) {\n        self.channel.request_resume()\n    }\n\n    /// Request the DMA to reset.\n    ///\n    /// The configuration for this channel will **not be preserved**. If you need to restart the transfer\n    /// at a later point with the same configuration, see [`request_pause`](Self::request_pause) instead.\n    pub fn request_reset(&mut self) {\n        self.channel.request_reset()\n    }\n\n    /// Return whether this transfer is still running.\n    ///\n    /// If this returns `false`, it can be because either the transfer finished, or\n    /// it was requested to stop early with [`request_pause`](Self::request_pause).\n    pub fn is_running(&mut self) -> bool {\n        self.channel.is_running()\n    }\n\n    /// Stop the DMA transfer and await until the buffer is full.\n    ///\n    /// This disables the DMA transfer's circular mode so that the transfer\n    /// stops when the buffer is full.\n    ///\n    /// This is designed to be used with streaming input data such as the\n    /// I2S/SAI or ADC.\n    pub async fn stop(&mut self) {\n        // wait until cr.susp reads as true\n        poll_fn(|cx| {\n            self.set_waker(cx.waker());\n            self.channel.poll_stop()\n        })\n        .await\n    }\n}\n\nimpl<'a, W: Word> Drop for ReadableRingBuffer<'a, W> {\n    fn drop(&mut self) {\n        self.request_pause();\n        while self.is_running() {}\n\n        // \"Subsequent reads and writes cannot be moved ahead of preceding reads.\"\n        fence(Ordering::SeqCst);\n    }\n}\n\n/// Ringbuffer for writing data using GPDMA linked-list mode.\npub struct WritableRingBuffer<'a, W: Word> {\n    channel: Channel<'a>,\n    _wake_guard: WakeGuard,\n    ringbuf: WritableDmaRingBuffer<'a, W>,\n    table: Table<2>,\n    options: TransferOptions,\n}\n\nimpl<'a, W: Word> WritableRingBuffer<'a, W> {\n    /// Create a new ring buffer.\n    ///\n    /// Transfer options are applied to the individual linked list items.\n    pub unsafe fn new(\n        channel: Channel<'a>,\n        request: Request,\n        peri_addr: *mut W,\n        buffer: &'a mut [W],\n        options: TransferOptions,\n    ) -> Self {\n        let table = Table::<2>::new_ping_pong::<W>(request, peri_addr, buffer, Dir::MemoryToPeripheral);\n\n        Self {\n            _wake_guard: channel.info().wake_guard(),\n            channel,\n            ringbuf: WritableDmaRingBuffer::new(buffer),\n            table,\n            options,\n        }\n    }\n\n    /// Start the ring buffer operation.\n    pub fn start(&mut self) {\n        // Apply the default configuration to the channel.\n        unsafe { self.channel.configure_linked_list(&self.table, self.options) };\n        self.table.link(RunMode::Circular);\n\n        self.channel.start();\n    }\n\n    /// Clear all data in the ring buffer.\n    pub fn clear(&mut self) {\n        self.ringbuf.reset(&mut DmaCtrlImpl(self.channel.reborrow()));\n    }\n\n    /// Write elements directly to the raw buffer.\n    /// This can be used to fill the buffer before starting the DMA transfer.\n    pub fn write_immediate(&mut self, buf: &[W]) -> Result<(usize, usize), Error> {\n        self.ringbuf.write_immediate(buf)\n    }\n\n    /// Write elements from the ring buffer\n    /// Return a tuple of the length written and the length remaining in the buffer\n    pub fn write(&mut self, buf: &[W]) -> Result<(usize, usize), Error> {\n        self.ringbuf.write(&mut DmaCtrlImpl(self.channel.reborrow()), buf)\n    }\n\n    /// Write an exact number of elements to the ringbuffer.\n    pub async fn write_exact(&mut self, buffer: &[W]) -> Result<usize, Error> {\n        self.ringbuf\n            .write_exact(&mut DmaCtrlImpl(self.channel.reborrow()), buffer)\n            .await\n    }\n\n    /// Wait for any ring buffer write error.\n    pub async fn wait_write_error(&mut self) -> Result<usize, Error> {\n        self.ringbuf\n            .wait_write_error(&mut DmaCtrlImpl(self.channel.reborrow()))\n            .await\n    }\n\n    /// The current length of the ringbuffer\n    pub fn len(&mut self) -> Result<usize, Error> {\n        Ok(self.ringbuf.len(&mut DmaCtrlImpl(self.channel.reborrow()))?)\n    }\n\n    /// The capacity of the ringbuffer\n    pub const fn capacity(&self) -> usize {\n        self.ringbuf.cap()\n    }\n\n    /// Set a waker to be woken when at least one byte is received.\n    pub fn set_waker(&mut self, waker: &Waker) {\n        DmaCtrlImpl(self.channel.reborrow()).set_waker(waker);\n    }\n\n    /// Request the DMA to suspend.\n    ///\n    /// To resume the transfer, call [`request_resume`](Self::request_resume) again.\n    ///\n    /// This doesn't immediately stop the transfer, you have to wait until [`is_running`](Self::is_running) returns false.\n    pub fn request_pause(&mut self) {\n        self.channel.request_pause()\n    }\n\n    /// Request the DMA to resume transfers after being suspended.\n    pub fn request_resume(&mut self) {\n        self.channel.request_resume()\n    }\n\n    /// Request the DMA to reset.\n    ///\n    /// The configuration for this channel will **not be preserved**. If you need to restart the transfer\n    /// at a later point with the same configuration, see [`request_pause`](Self::request_pause) instead.\n    pub fn request_reset(&mut self) {\n        self.channel.request_reset()\n    }\n\n    /// Return whether DMA is still running.\n    ///\n    /// If this returns `false`, it can be because either the transfer finished, or\n    /// it was requested to stop early with [`request_stop`](Self::request_stop).\n    pub fn is_running(&mut self) -> bool {\n        self.channel.is_running()\n    }\n\n    /// Stop the DMA transfer and await until the buffer is full.\n    ///\n    /// This disables the DMA transfer's circular mode so that the transfer\n    /// stops when the buffer is full.\n    ///\n    /// This is designed to be used with streaming input data such as the\n    /// I2S/SAI or ADC.\n    ///\n    /// When using the UART, you probably want `request_stop()`.\n    pub async fn stop(&mut self) {\n        // wait until cr.susp reads as true\n        poll_fn(|cx| {\n            self.set_waker(cx.waker());\n            self.channel.poll_stop()\n        })\n        .await\n    }\n}\n\nimpl<'a, W: Word> Drop for WritableRingBuffer<'a, W> {\n    fn drop(&mut self) {\n        self.request_pause();\n        while self.is_running() {}\n\n        // \"Subsequent reads and writes cannot be moved ahead of preceding reads.\"\n        fence(Ordering::SeqCst);\n    }\n}\n"
  },
  {
    "path": "embassy-stm32/src/dma/mod.rs",
    "content": "//! Direct Memory Access (DMA)\n#![macro_use]\n\n#[cfg(any(bdma, dma, mdma))]\nmod dma_bdma;\n\n#[cfg(any(bdma, dma, mdma))]\npub use dma_bdma::*;\n\n#[cfg(gpdma)]\npub(crate) mod gpdma;\n#[cfg(gpdma)]\npub use gpdma::ringbuffered::*;\n#[cfg(gpdma)]\npub use gpdma::*;\n\n#[cfg(dmamux)]\nmod dmamux;\n#[cfg(dmamux)]\npub(crate) use dmamux::*;\n\nmod util;\npub(crate) use util::*;\n\npub(crate) mod ringbuffer;\npub mod word;\n\nuse core::marker::PhantomData;\n\nuse embassy_hal_internal::{Peri, PeripheralType};\n\nuse crate::interrupt;\n\n/// The direction of a DMA transfer.\n#[derive(Debug, Copy, Clone, PartialEq, Eq)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub enum Dir {\n    /// Transfer from memory to a peripheral.\n    MemoryToPeripheral,\n    /// Transfer from a peripheral to memory.\n    PeripheralToMemory,\n    /// Transfer from memory to another memory address.\n    MemoryToMemory,\n}\n\n/// Which pointer in the transfer to increment.\n#[derive(Debug, Copy, Clone, PartialEq, Eq)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub enum Increment {\n    /// DMA will not increment either of the addresses.\n    None,\n    /// DMA will increment the peripheral address.\n    Peripheral,\n    /// DMA will increment the memory address.\n    Memory,\n    /// DMA will increment both peripheral and memory addresses simultaneously.\n    Both,\n}\n\n/// DMA request type alias. (also known as DMA channel number in some chips)\n#[cfg(any(dma_v2, bdma_v2, gpdma, dmamux))]\npub type Request = u8;\n/// DMA request type alias. (also known as DMA channel number in some chips)\n#[cfg(not(any(dma_v2, bdma_v2, gpdma, dmamux)))]\npub type Request = ();\n\n/// DMA channel driver\npub struct Channel<'d> {\n    pub(crate) id: u8,\n    phantom: PhantomData<&'d ()>,\n}\n\nimpl<'d> Channel<'d> {\n    /// Create a new DMA channel driver.\n    pub fn new<T: ChannelInstance>(\n        _ch: Peri<'d, T>,\n        _irq: impl interrupt::typelevel::Binding<T::Interrupt, crate::dma::InterruptHandler<T>> + 'd,\n    ) -> Self {\n        Self {\n            id: T::ID,\n            phantom: PhantomData,\n        }\n    }\n\n    /// Reborrow the channel, allowing it to be used in multiple places.\n    pub fn reborrow(&mut self) -> Channel<'_> {\n        Channel {\n            id: self.id,\n            phantom: PhantomData,\n        }\n    }\n\n    pub(crate) unsafe fn clone_unchecked(&self) -> Channel<'d> {\n        Channel {\n            id: self.id,\n            phantom: PhantomData,\n        }\n    }\n}\n\npub(crate) trait SealedChannelInstance {\n    const ID: u8;\n}\n\n/// DMA channel.\n#[allow(private_bounds)]\npub trait ChannelInstance: SealedChannelInstance + PeripheralType + 'static {\n    /// The interrupt type for this DMA channel.\n    type Interrupt: interrupt::typelevel::Interrupt;\n}\n\n/// DMA interrupt handler.\n#[allow(private_bounds)]\npub struct InterruptHandler<T: ChannelInstance> {\n    _phantom: PhantomData<T>,\n}\n\nimpl<T: ChannelInstance> interrupt::typelevel::Handler<T::Interrupt> for InterruptHandler<T> {\n    unsafe fn on_interrupt() {\n        on_irq(T::ID)\n    }\n}\n\nmacro_rules! dma_channel_impl {\n    ($channel_peri:ident, $index:expr, $irq:ty) => {\n        impl crate::dma::SealedChannelInstance for crate::peripherals::$channel_peri {\n            const ID: u8 = $index;\n        }\n\n        impl crate::dma::ChannelInstance for crate::peripherals::$channel_peri {\n            type Interrupt = $irq;\n        }\n    };\n}\n\nconst CHANNEL_COUNT: usize = crate::_generated::DMA_CHANNELS.len();\nstatic STATE: [ChannelState; CHANNEL_COUNT] = [ChannelState::NEW; CHANNEL_COUNT];\n\npub(crate) fn info(id: u8) -> &'static ChannelInfo {\n    &crate::_generated::DMA_CHANNELS[id as usize]\n}\n\n// safety: must be called only once at startup\npub(crate) unsafe fn init(\n    cs: critical_section::CriticalSection,\n    #[cfg(bdma)] bdma_priority: interrupt::Priority,\n    #[cfg(dma)] dma_priority: interrupt::Priority,\n    #[cfg(gpdma)] gpdma_priority: interrupt::Priority,\n    #[cfg(mdma)] mdma_priority: interrupt::Priority,\n) {\n    #[cfg(any(dma, bdma))]\n    dma_bdma::init(\n        cs,\n        #[cfg(dma)]\n        dma_priority,\n        #[cfg(bdma)]\n        bdma_priority,\n        #[cfg(mdma)]\n        mdma_priority,\n    );\n    #[cfg(gpdma)]\n    gpdma::init(cs, gpdma_priority);\n    #[cfg(dmamux)]\n    dmamux::init(cs);\n}\n"
  },
  {
    "path": "embassy-stm32/src/dma/ringbuffer/mod.rs",
    "content": "use core::future::poll_fn;\nuse core::sync::atomic::{Ordering, fence};\nuse core::task::{Poll, Waker};\n\nuse crate::dma::word::Word;\n\npub trait DmaCtrl {\n    /// Get the NDTR register value, i.e. the space left in the underlying\n    /// buffer until the dma writer wraps.\n    fn get_remaining_transfers(&self) -> usize;\n\n    /// Reset the transfer completed counter to 0 and return the value just prior to the reset.\n    fn reset_complete_count(&mut self) -> usize;\n\n    /// Set the waker for a running poll_fn\n    fn set_waker(&mut self, waker: &Waker);\n}\n\n#[derive(Debug, PartialEq)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub enum Error {\n    Overrun,\n    /// the newly read DMA positions don't make sense compared to the previous\n    /// ones. This can usually only occur due to wrong Driver implementation, if\n    /// the driver author (or the user using raw metapac code) directly resets\n    /// the channel for instance.\n    DmaUnsynced,\n}\n\n#[derive(Debug, Clone, Copy, Default)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\nstruct DmaIndex {\n    complete_count: usize,\n    pos: usize,\n}\n\nimpl DmaIndex {\n    fn reset(&mut self) {\n        self.pos = 0;\n        self.complete_count = 0;\n    }\n\n    fn as_index(&self, cap: usize, offset: usize) -> usize {\n        (self.pos + offset) % cap\n    }\n\n    fn dma_sync(&mut self, cap: usize, dma: &mut impl DmaCtrl) {\n        // Important!\n        // The ordering of the first two lines matters!\n        // If changed, the code will detect a wrong +capacity\n        // jump at wrap-around.\n        let count_diff = dma.reset_complete_count();\n        let pos = cap - dma.get_remaining_transfers();\n        self.pos = if pos < self.pos && count_diff == 0 {\n            cap - 1\n        } else {\n            pos\n        };\n\n        self.complete_count += count_diff;\n    }\n\n    fn advance(&mut self, cap: usize, steps: usize) {\n        let next = self.pos + steps;\n        self.complete_count += next / cap;\n        self.pos = next % cap;\n    }\n\n    fn normalize(lhs: &mut DmaIndex, rhs: &mut DmaIndex) {\n        let min_count = lhs.complete_count.min(rhs.complete_count);\n        lhs.complete_count -= min_count;\n        rhs.complete_count -= min_count;\n    }\n\n    fn diff(&self, cap: usize, rhs: &DmaIndex) -> isize {\n        (self.complete_count * cap + self.pos) as isize - (rhs.complete_count * cap + rhs.pos) as isize\n    }\n}\n\npub struct ReadableDmaRingBuffer<'a, W: Word> {\n    dma_buf: &'a mut [W],\n    write_index: DmaIndex,\n    read_index: DmaIndex,\n    alignment: usize,\n}\n\nimpl<'a, W: Word> ReadableDmaRingBuffer<'a, W> {\n    /// Construct an empty buffer.\n    pub fn new(dma_buf: &'a mut [W]) -> Self {\n        Self {\n            dma_buf,\n            write_index: Default::default(),\n            read_index: Default::default(),\n            alignment: 1,\n        }\n    }\n\n    /// Set the frame alignment for the ring buffer.\n    ///\n    /// When set to a value > 1, the ring buffer will automatically discard partial\n    /// frames after overrun recovery to maintain alignment. This is critical for\n    /// protocols like I2S where each frame consists of multiple DMA transfers\n    /// (e.g. 4 half-words for stereo 32-bit I2S) and reading from a mid-frame\n    /// position produces garbage data.\n    ///\n    /// The DMA buffer length must be a multiple of the alignment value.\n    pub fn set_alignment(&mut self, alignment: usize) {\n        let alignment = alignment.max(1);\n        assert!(\n            self.cap() % alignment == 0,\n            \"DMA buffer length must be a multiple of the alignment value\"\n        );\n        self.alignment = alignment;\n    }\n\n    /// Reset the ring buffer to its initial state.\n    pub fn reset(&mut self, dma: &mut impl DmaCtrl) {\n        dma.reset_complete_count();\n        self.write_index.reset();\n        self.write_index.dma_sync(self.cap(), dma);\n        self.read_index = self.write_index;\n    }\n\n    /// Get the full ringbuffer capacity.\n    pub const fn cap(&self) -> usize {\n        self.dma_buf.len()\n    }\n\n    /// Get the available readable dma samples.\n    pub fn len(&mut self, dma: &mut impl DmaCtrl) -> Result<usize, Error> {\n        self.write_index.dma_sync(self.cap(), dma);\n        DmaIndex::normalize(&mut self.write_index, &mut self.read_index);\n\n        let diff = self.write_index.diff(self.cap(), &self.read_index);\n\n        if diff < 0 {\n            Err(Error::DmaUnsynced)\n        } else if diff > self.cap() as isize {\n            Err(Error::Overrun)\n        } else {\n            Ok(diff as usize)\n        }\n    }\n\n    /// Read elements from the ring buffer.\n    ///\n    /// Return a tuple of the length read and the length remaining in the buffer\n    /// If not all of the elements were read, then there will be some elements in the buffer remaining\n    /// The length remaining is the capacity, ring_buf.len(), less the elements remaining after the read\n    /// Error is returned if the portion to be read was overwritten by the DMA controller,\n    /// in which case the rinbuffer will automatically reset itself.\n    pub fn read(&mut self, dma: &mut impl DmaCtrl, buf: &mut [W]) -> Result<(usize, usize), Error> {\n        self.read_raw(dma, buf).inspect_err(|_e| {\n            self.reset(dma);\n        })\n    }\n\n    /// Read an exact number of elements from the ringbuffer.\n    ///\n    /// Returns the remaining number of elements available for immediate reading.\n    /// Error is returned if the portion to be read was overwritten by the DMA controller.\n    ///\n    /// Async/Wake Behavior:\n    /// The underlying DMA peripheral only can wake us when its buffer pointer has reached the halfway point,\n    /// and when it wraps around. This means that when called with a buffer of length 'M', when this\n    /// ring buffer was created with a buffer of size 'N':\n    /// - If M equals N/2 or N/2 divides evenly into M, this function will return every N/2 elements read on the DMA source.\n    /// - Otherwise, this function may need up to N/2 extra elements to arrive before returning.\n    pub async fn read_exact(&mut self, dma: &mut impl DmaCtrl, buffer: &mut [W]) -> Result<usize, Error> {\n        let mut read_data = 0;\n        let buffer_len = buffer.len();\n\n        poll_fn(|cx| {\n            dma.set_waker(cx.waker());\n\n            match self.read(dma, &mut buffer[read_data..buffer_len]) {\n                Ok((len, remaining)) => {\n                    read_data += len;\n                    if read_data == buffer_len {\n                        Poll::Ready(Ok(remaining))\n                    } else {\n                        Poll::Pending\n                    }\n                }\n                Err(e) => Poll::Ready(Err(e)),\n            }\n        })\n        .await\n    }\n\n    fn read_raw(&mut self, dma: &mut impl DmaCtrl, buf: &mut [W]) -> Result<(usize, usize), Error> {\n        fence(Ordering::Acquire);\n\n        let mut available = self.len(dma)?;\n\n        // Skip misaligned samples to maintain frame alignment after overrun recovery.\n        // DMA always starts at buffer position 0 (frame-aligned) and advances\n        // sequentially, so position N is frame-aligned when N % alignment == 0.\n        if self.alignment > 1 {\n            let misalignment = self.read_index.pos % self.alignment;\n            if misalignment != 0 {\n                let skip = self.alignment - misalignment;\n                if available >= skip {\n                    self.read_index.advance(self.cap(), skip);\n                    available -= skip;\n                } else {\n                    return Ok((0, available));\n                }\n            }\n        }\n\n        let mut readable = available.min(buf.len());\n        // Round down to alignment so read_index always lands on a frame boundary.\n        if self.alignment > 1 {\n            readable -= readable % self.alignment;\n        }\n        for i in 0..readable {\n            buf[i] = self.read_buf(i);\n        }\n        let remaining = self.len(dma)?;\n        self.read_index.advance(self.cap(), readable);\n        Ok((readable, remaining - readable))\n    }\n\n    /// Read the most recent elements from the ring buffer, discarding any older data.\n    ///\n    /// Returns the number of elements actually read into `buf`. This may be less than\n    /// `buf.len()` if fewer samples are available (e.g. the DMA has not yet filled enough\n    /// data since the last read).\n    ///\n    /// Unlike [`read`], this method **never returns an overrun error**. If the DMA has\n    /// lapped the read pointer, the read pointer is silently advanced to catch up, and\n    /// only the most recent data is returned. This makes it ideal for use cases like ADC\n    /// sampling where the consumer only cares about the latest values and old data can\n    /// be safely discarded.\n    ///\n    /// If an `alignment` has been set, the returned count is rounded down to a multiple\n    /// of the alignment and reading starts from a frame-aligned position.\n    pub fn read_latest(&mut self, dma: &mut impl DmaCtrl, buf: &mut [W]) -> usize {\n        fence(Ordering::Acquire);\n\n        self.write_index.dma_sync(self.cap(), dma);\n        DmaIndex::normalize(&mut self.write_index, &mut self.read_index);\n\n        let diff = self.write_index.diff(self.cap(), &self.read_index);\n\n        // On overrun or desync, reset the read pointer to the current write position.\n        // This means zero samples are available right now, but the next call will\n        // return fresh data without any error.\n        let available = if diff <= 0 || diff > self.cap() as isize {\n            self.read_index = self.write_index;\n            0\n        } else {\n            diff as usize\n        };\n\n        let mut to_read = available.min(buf.len());\n        let mut front_skip = available - to_read;\n\n        // Respect frame alignment. Because read_latest reads the NEWEST data\n        // (skip at the front, read at the tail), reducing to_read moves the\n        // start forward. We must compute front_skip explicitly so the read\n        // window starts at an aligned buffer position.\n        if self.alignment > 1 {\n            // Discard any partial frame at the end of available data.\n            let end_pos = self.read_index.as_index(self.cap(), available);\n            let tail = end_pos % self.alignment;\n            let aligned_available = available.saturating_sub(tail);\n\n            to_read = aligned_available.min(buf.len());\n            to_read -= to_read % self.alignment;\n            front_skip = aligned_available - to_read;\n        }\n\n        // Advance past old data to the aligned start position.\n        if front_skip > 0 {\n            self.read_index.advance(self.cap(), front_skip);\n        }\n\n        for i in 0..to_read {\n            buf[i] = self.read_buf(i);\n        }\n\n        // Advance past what we read plus any trailing partial frame.\n        self.read_index.advance(self.cap(), available - front_skip);\n\n        to_read\n    }\n\n    fn read_buf(&self, offset: usize) -> W {\n        unsafe {\n            core::ptr::read_volatile(\n                self.dma_buf\n                    .as_ptr()\n                    .offset(self.read_index.as_index(self.cap(), offset) as isize),\n            )\n        }\n    }\n}\n\npub struct WritableDmaRingBuffer<'a, W: Word> {\n    dma_buf: &'a mut [W],\n    read_index: DmaIndex,\n    write_index: DmaIndex,\n}\n\nimpl<'a, W: Word> WritableDmaRingBuffer<'a, W> {\n    /// Construct a ringbuffer filled with the given buffer data.\n    pub fn new(dma_buf: &'a mut [W]) -> Self {\n        let len = dma_buf.len();\n        Self {\n            dma_buf,\n            read_index: Default::default(),\n            write_index: DmaIndex {\n                complete_count: 0,\n                pos: len,\n            },\n        }\n    }\n\n    /// Reset the ring buffer to its initial state. The buffer after the reset will be full.\n    pub fn reset(&mut self, dma: &mut impl DmaCtrl) {\n        dma.reset_complete_count();\n        self.read_index.reset();\n        self.read_index.dma_sync(self.cap(), dma);\n        self.write_index = self.read_index;\n        self.write_index.advance(self.cap(), self.cap());\n    }\n\n    /// Get the remaining writable dma samples.\n    pub fn len(&mut self, dma: &mut impl DmaCtrl) -> Result<usize, Error> {\n        self.read_index.dma_sync(self.cap(), dma);\n        DmaIndex::normalize(&mut self.read_index, &mut self.write_index);\n\n        let diff = self.write_index.diff(self.cap(), &self.read_index);\n\n        if diff < 0 {\n            Err(Error::Overrun)\n        } else if diff > self.cap() as isize {\n            Err(Error::DmaUnsynced)\n        } else {\n            Ok(self.cap().saturating_sub(diff as usize))\n        }\n    }\n\n    /// Get the full ringbuffer capacity.\n    pub const fn cap(&self) -> usize {\n        self.dma_buf.len()\n    }\n\n    /// Append data to the ring buffer.\n    /// Returns a tuple of the data written and the remaining write capacity in the buffer.\n    /// Error is returned if the portion to be written was previously read by the DMA controller.\n    /// In this case, the ringbuffer will automatically reset itself, giving a full buffer worth of\n    /// leeway between the write index and the DMA.\n    pub fn write(&mut self, dma: &mut impl DmaCtrl, buf: &[W]) -> Result<(usize, usize), Error> {\n        self.write_raw(dma, buf).inspect_err(|_e| {\n            self.reset(dma);\n        })\n    }\n\n    /// Write elements directly to the buffer.\n    ///\n    /// Subsequent writes will overwrite the content of the buffer, so it is not useful to call this more than once.\n    /// Data is aligned towards the end of the buffer.\n    ///\n    /// In case of success, returns the written length, and the empty space in front of the written block.\n    /// Fails if the data to write exceeds the buffer capacity.\n    pub fn write_immediate(&mut self, buf: &[W]) -> Result<(usize, usize), Error> {\n        fence(Ordering::Release);\n\n        if buf.len() > self.cap() {\n            return Err(Error::Overrun);\n        }\n\n        let start = self.cap() - buf.len();\n        for (i, data) in buf.iter().enumerate() {\n            self.write_buf(start + i, *data)\n        }\n        let written = buf.len().min(self.cap());\n        Ok((written, self.cap() - written))\n    }\n\n    /// Wait for any ring buffer write error.\n    pub async fn wait_write_error(&mut self, dma: &mut impl DmaCtrl) -> Result<usize, Error> {\n        poll_fn(|cx| {\n            dma.set_waker(cx.waker());\n\n            match self.len(dma) {\n                Ok(_) => Poll::Pending,\n                Err(e) => Poll::Ready(Err(e)),\n            }\n        })\n        .await\n    }\n\n    /// Write an exact number of elements to the ringbuffer.\n    ///\n    /// Returns the remaining write capacity in the buffer.\n    #[allow(dead_code)]\n    pub async fn write_exact(&mut self, dma: &mut impl DmaCtrl, buffer: &[W]) -> Result<usize, Error> {\n        let mut written_len = 0;\n        let buffer_len = buffer.len();\n\n        poll_fn(|cx| {\n            dma.set_waker(cx.waker());\n\n            match self.write(dma, &buffer[written_len..buffer_len]) {\n                Ok((len, remaining)) => {\n                    written_len += len;\n                    if written_len == buffer_len {\n                        Poll::Ready(Ok(remaining))\n                    } else {\n                        Poll::Pending\n                    }\n                }\n                Err(e) => Poll::Ready(Err(e)),\n            }\n        })\n        .await\n    }\n\n    fn write_raw(&mut self, dma: &mut impl DmaCtrl, buf: &[W]) -> Result<(usize, usize), Error> {\n        fence(Ordering::Release);\n\n        let writable = self.len(dma)?.min(buf.len());\n        for i in 0..writable {\n            self.write_buf(i, buf[i]);\n        }\n        let available = self.len(dma)?;\n        self.write_index.advance(self.cap(), writable);\n        Ok((writable, available - writable))\n    }\n\n    fn write_buf(&mut self, offset: usize, value: W) {\n        unsafe {\n            core::ptr::write_volatile(\n                self.dma_buf\n                    .as_mut_ptr()\n                    .offset(self.write_index.as_index(self.cap(), offset) as isize),\n                value,\n            )\n        }\n    }\n}\n\n#[cfg(test)]\nmod tests;\n"
  },
  {
    "path": "embassy-stm32/src/dma/ringbuffer/tests/mod.rs",
    "content": "use std::{cell, vec};\n\nuse super::*;\n\n#[allow(unused)]\n#[derive(PartialEq, Debug)]\nenum TestCircularTransferRequest {\n    ResetCompleteCount(usize),\n    PositionRequest(usize),\n}\n\n#[allow(unused)]\nstruct TestCircularTransfer {\n    len: usize,\n    requests: cell::RefCell<vec::Vec<TestCircularTransferRequest>>,\n}\n\nimpl DmaCtrl for TestCircularTransfer {\n    fn get_remaining_transfers(&self) -> usize {\n        match self.requests.borrow_mut().pop().unwrap() {\n            TestCircularTransferRequest::PositionRequest(pos) => {\n                let len = self.len;\n\n                assert!(len >= pos);\n\n                len - pos\n            }\n            _ => unreachable!(),\n        }\n    }\n\n    fn reset_complete_count(&mut self) -> usize {\n        match self.requests.get_mut().pop().unwrap() {\n            TestCircularTransferRequest::ResetCompleteCount(complete_count) => complete_count,\n            _ => unreachable!(),\n        }\n    }\n\n    fn set_waker(&mut self, _waker: &Waker) {}\n}\n\nimpl TestCircularTransfer {\n    #[allow(unused)]\n    pub fn new(len: usize) -> Self {\n        Self {\n            requests: cell::RefCell::new(vec![]),\n            len,\n        }\n    }\n\n    #[allow(unused)]\n    pub fn setup(&self, mut requests: vec::Vec<TestCircularTransferRequest>) {\n        requests.reverse();\n        self.requests.replace(requests);\n    }\n}\n\nconst CAP: usize = 16;\n\n#[test]\nfn dma_index_as_index_returns_index_mod_cap_by_default() {\n    let index = DmaIndex::default();\n    assert_eq!(index.as_index(CAP, 0), 0);\n    assert_eq!(index.as_index(CAP, 1), 1);\n    assert_eq!(index.as_index(CAP, 2), 2);\n    assert_eq!(index.as_index(CAP, 3), 3);\n    assert_eq!(index.as_index(CAP, 4), 4);\n    assert_eq!(index.as_index(CAP, CAP), 0);\n    assert_eq!(index.as_index(CAP, CAP + 1), 1);\n}\n\n#[test]\nfn dma_index_advancing_increases_as_index() {\n    let mut index = DmaIndex::default();\n    assert_eq!(index.as_index(CAP, 0), 0);\n    index.advance(CAP, 1);\n    assert_eq!(index.as_index(CAP, 0), 1);\n    index.advance(CAP, 1);\n    assert_eq!(index.as_index(CAP, 0), 2);\n    index.advance(CAP, 1);\n    assert_eq!(index.as_index(CAP, 0), 3);\n    index.advance(CAP, 1);\n    assert_eq!(index.as_index(CAP, 0), 4);\n    index.advance(CAP, CAP - 4);\n    assert_eq!(index.as_index(CAP, 0), 0);\n    index.advance(CAP, 1);\n    assert_eq!(index.as_index(CAP, 0), 1);\n}\n\n/// Test that after an overrun recovery lands on a misaligned position,\n/// subsequent reads skip forward to the next frame boundary.\n#[test]\nfn alignment_skip_after_overrun() {\n    // Buffer of 16 u8s, alignment of 4 (simulating stereo 32-bit I2S frames).\n    let mut dma_buf = [0u8; CAP];\n    // Fill buffer with recognizable pattern: each frame starts with its frame index.\n    for i in 0..CAP {\n        dma_buf[i] = i as u8;\n    }\n    let mut ringbuf = ReadableDmaRingBuffer::new(&mut dma_buf);\n    ringbuf.set_alignment(4);\n\n    // Simulate DMA having written 10 samples (2.5 frames) and an overrun\n    // that resets the read index to the current DMA write position.\n    let mut dma = TestCircularTransfer::new(CAP);\n\n    // reset() calls: reset_complete_count, then dma_sync (reset_complete_count + get_remaining_transfers)\n    dma.setup(vec![\n        TestCircularTransferRequest::ResetCompleteCount(0),\n        TestCircularTransferRequest::ResetCompleteCount(0),\n        TestCircularTransferRequest::PositionRequest(5), // DMA at position 5 (misaligned)\n    ]);\n    ringbuf.reset(&mut dma);\n\n    // read_index is now at position 5 (mid-frame). Advance DMA to position 14.\n    let mut read_buf = [0u8; 8];\n    dma.setup(vec![\n        // read_raw calls len() which calls dma_sync (reset_complete_count + get_remaining_transfers)\n        TestCircularTransferRequest::ResetCompleteCount(0),\n        TestCircularTransferRequest::PositionRequest(14),\n        // len() is called again after reading\n        TestCircularTransferRequest::ResetCompleteCount(0),\n        TestCircularTransferRequest::PositionRequest(14),\n    ]);\n\n    let (read, _remaining) = ringbuf.read(&mut dma, &mut read_buf).unwrap();\n\n    // 9 samples available (pos 5..14), alignment skip of 3 (to pos 8),\n    // leaves 6 samples, rounded down to nearest frame (4).\n    assert_eq!(read, 4);\n    // Data should start at buffer position 8 (the next aligned frame boundary).\n    assert_eq!(&read_buf[..read], &[8, 9, 10, 11]);\n}\n\n/// Test that reads from an already-aligned position are unaffected by alignment setting.\n#[test]\nfn alignment_no_skip_when_aligned() {\n    let mut dma_buf = [0u8; CAP];\n    for i in 0..CAP {\n        dma_buf[i] = i as u8;\n    }\n    let mut ringbuf = ReadableDmaRingBuffer::new(&mut dma_buf);\n    ringbuf.set_alignment(4);\n\n    let mut dma = TestCircularTransfer::new(CAP);\n\n    // Reset with DMA at position 8 (already frame-aligned).\n    dma.setup(vec![\n        TestCircularTransferRequest::ResetCompleteCount(0),\n        TestCircularTransferRequest::ResetCompleteCount(0),\n        TestCircularTransferRequest::PositionRequest(8),\n    ]);\n    ringbuf.reset(&mut dma);\n\n    // Advance DMA to position 16 (= 0 wrapped).\n    let mut read_buf = [0u8; 8];\n    dma.setup(vec![\n        TestCircularTransferRequest::ResetCompleteCount(1),\n        TestCircularTransferRequest::PositionRequest(0),\n        TestCircularTransferRequest::ResetCompleteCount(0),\n        TestCircularTransferRequest::PositionRequest(0),\n    ]);\n\n    let (read, _remaining) = ringbuf.read(&mut dma, &mut read_buf).unwrap();\n\n    // All 8 samples readable with no skip needed.\n    assert_eq!(read, 8);\n    assert_eq!(&read_buf[..read], &[8, 9, 10, 11, 12, 13, 14, 15]);\n}\n\n/// Test that a non-frame-aligned user buffer gets rounded down so read_index stays aligned.\n#[test]\nfn alignment_rounds_down_read_length() {\n    let mut dma_buf = [0u8; CAP];\n    for i in 0..CAP {\n        dma_buf[i] = i as u8;\n    }\n    let mut ringbuf = ReadableDmaRingBuffer::new(&mut dma_buf);\n    ringbuf.set_alignment(4);\n\n    let mut dma = TestCircularTransfer::new(CAP);\n\n    // Reset at position 0 (aligned), DMA at position 12.\n    dma.setup(vec![\n        TestCircularTransferRequest::ResetCompleteCount(0),\n        TestCircularTransferRequest::ResetCompleteCount(0),\n        TestCircularTransferRequest::PositionRequest(0),\n    ]);\n    ringbuf.reset(&mut dma);\n\n    // User buffer of 5 (not a multiple of 4). Should only read 4.\n    let mut read_buf = [0u8; 5];\n    dma.setup(vec![\n        TestCircularTransferRequest::ResetCompleteCount(0),\n        TestCircularTransferRequest::PositionRequest(12),\n        TestCircularTransferRequest::ResetCompleteCount(0),\n        TestCircularTransferRequest::PositionRequest(12),\n    ]);\n\n    let (read, _remaining) = ringbuf.read(&mut dma, &mut read_buf).unwrap();\n\n    // Rounded down from 5 to 4 to maintain frame alignment.\n    assert_eq!(read, 4);\n    assert_eq!(&read_buf[..read], &[0, 1, 2, 3]);\n}\n\n/// Test that set_alignment panics when buffer length is not a multiple of alignment.\n#[test]\n#[should_panic(expected = \"DMA buffer length must be a multiple of the alignment value\")]\nfn alignment_rejects_non_multiple() {\n    let mut dma_buf = [0u8; CAP]; // CAP = 16\n    let mut ringbuf = ReadableDmaRingBuffer::new(&mut dma_buf);\n    ringbuf.set_alignment(3); // 16 % 3 != 0 → panic\n}\n\n// ── read_latest tests ──────────────────────────────────────────────────\n\n/// read_latest returns the most recent elements when more data is available\n/// than the user buffer can hold.\n#[test]\nfn read_latest_returns_most_recent_data() {\n    let mut dma_buf = [0u8; CAP];\n    for i in 0..CAP {\n        dma_buf[i] = i as u8;\n    }\n    let mut ringbuf = ReadableDmaRingBuffer::new(&mut dma_buf);\n    let mut dma = TestCircularTransfer::new(CAP);\n\n    // Reset at position 0.\n    dma.setup(vec![\n        TestCircularTransferRequest::ResetCompleteCount(0),\n        TestCircularTransferRequest::ResetCompleteCount(0),\n        TestCircularTransferRequest::PositionRequest(0),\n    ]);\n    ringbuf.reset(&mut dma);\n\n    // DMA has written 12 samples (positions 0..12). User buffer holds 4.\n    // read_latest should return the 4 most recent: [8, 9, 10, 11].\n    let mut read_buf = [0u8; 4];\n    dma.setup(vec![\n        TestCircularTransferRequest::ResetCompleteCount(0),\n        TestCircularTransferRequest::PositionRequest(12),\n    ]);\n\n    let n = ringbuf.read_latest(&mut dma, &mut read_buf);\n    assert_eq!(n, 4);\n    assert_eq!(&read_buf[..n], &[8, 9, 10, 11]);\n}\n\n/// read_latest never errors on overrun — it just resets and returns 0.\n#[test]\nfn read_latest_recovers_from_overrun() {\n    let mut dma_buf = [0u8; CAP];\n    for i in 0..CAP {\n        dma_buf[i] = i as u8;\n    }\n    let mut ringbuf = ReadableDmaRingBuffer::new(&mut dma_buf);\n    let mut dma = TestCircularTransfer::new(CAP);\n\n    // Reset at position 0.\n    dma.setup(vec![\n        TestCircularTransferRequest::ResetCompleteCount(0),\n        TestCircularTransferRequest::ResetCompleteCount(0),\n        TestCircularTransferRequest::PositionRequest(0),\n    ]);\n    ringbuf.reset(&mut dma);\n\n    // Simulate overrun: DMA has wrapped more than once (complete_count=2, pos=5).\n    // diff = (2*16 + 5) - 0 = 37 > 16 → overrun.\n    let mut read_buf = [0u8; 8];\n    dma.setup(vec![\n        TestCircularTransferRequest::ResetCompleteCount(2),\n        TestCircularTransferRequest::PositionRequest(5),\n    ]);\n\n    // Should not panic or error — just returns 0 (reset to catch up).\n    let n = ringbuf.read_latest(&mut dma, &mut read_buf);\n    assert_eq!(n, 0);\n\n    // Next call after overrun recovery should return fresh data normally.\n    dma.setup(vec![\n        TestCircularTransferRequest::ResetCompleteCount(0),\n        TestCircularTransferRequest::PositionRequest(13),\n    ]);\n\n    let n = ringbuf.read_latest(&mut dma, &mut read_buf);\n    assert_eq!(n, 8);\n    assert_eq!(&read_buf[..n], &[5, 6, 7, 8, 9, 10, 11, 12]);\n}\n\n/// read_latest returns 0 when no new data is available.\n#[test]\nfn read_latest_returns_zero_when_empty() {\n    let mut dma_buf = [0u8; CAP];\n    let mut ringbuf = ReadableDmaRingBuffer::new(&mut dma_buf);\n    let mut dma = TestCircularTransfer::new(CAP);\n\n    // Reset at position 4.\n    dma.setup(vec![\n        TestCircularTransferRequest::ResetCompleteCount(0),\n        TestCircularTransferRequest::ResetCompleteCount(0),\n        TestCircularTransferRequest::PositionRequest(4),\n    ]);\n    ringbuf.reset(&mut dma);\n\n    // DMA is still at position 4 — nothing new.\n    let mut read_buf = [0u8; 8];\n    dma.setup(vec![\n        TestCircularTransferRequest::ResetCompleteCount(0),\n        TestCircularTransferRequest::PositionRequest(4),\n    ]);\n\n    let n = ringbuf.read_latest(&mut dma, &mut read_buf);\n    assert_eq!(n, 0);\n}\n\n/// read_latest respects alignment: skips to frame boundary, rounds down length.\n#[test]\nfn read_latest_with_alignment() {\n    let mut dma_buf = [0u8; CAP];\n    for i in 0..CAP {\n        dma_buf[i] = i as u8;\n    }\n    let mut ringbuf = ReadableDmaRingBuffer::new(&mut dma_buf);\n    ringbuf.set_alignment(4);\n    let mut dma = TestCircularTransfer::new(CAP);\n\n    // Reset at position 0 (aligned).\n    dma.setup(vec![\n        TestCircularTransferRequest::ResetCompleteCount(0),\n        TestCircularTransferRequest::ResetCompleteCount(0),\n        TestCircularTransferRequest::PositionRequest(0),\n    ]);\n    ringbuf.reset(&mut dma);\n\n    // DMA at position 14. 14 samples available, user buffer holds 6.\n    // Tail = 14 % 4 = 2 (partial frame discarded), aligned_available = 12.\n    // to_read = min(12, 6) = 6, rounded down to 4.\n    // front_skip = 12 - 4 = 8. Read starts at position 8 (aligned): [8, 9, 10, 11].\n    let mut read_buf = [0u8; 6];\n    dma.setup(vec![\n        TestCircularTransferRequest::ResetCompleteCount(0),\n        TestCircularTransferRequest::PositionRequest(14),\n    ]);\n\n    let n = ringbuf.read_latest(&mut dma, &mut read_buf);\n    assert_eq!(n, 4);\n    assert_eq!(&read_buf[..n], &[8, 9, 10, 11]);\n}\n\n/// read_latest reads all available data when buffer is large enough.\n#[test]\nfn read_latest_reads_all_when_buffer_is_large() {\n    let mut dma_buf = [0u8; CAP];\n    for i in 0..CAP {\n        dma_buf[i] = i as u8;\n    }\n    let mut ringbuf = ReadableDmaRingBuffer::new(&mut dma_buf);\n    let mut dma = TestCircularTransfer::new(CAP);\n\n    // Reset at position 0.\n    dma.setup(vec![\n        TestCircularTransferRequest::ResetCompleteCount(0),\n        TestCircularTransferRequest::ResetCompleteCount(0),\n        TestCircularTransferRequest::PositionRequest(0),\n    ]);\n    ringbuf.reset(&mut dma);\n\n    // DMA has written 6 samples. User buffer holds 16. Should read exactly 6.\n    let mut read_buf = [0u8; CAP];\n    dma.setup(vec![\n        TestCircularTransferRequest::ResetCompleteCount(0),\n        TestCircularTransferRequest::PositionRequest(6),\n    ]);\n\n    let n = ringbuf.read_latest(&mut dma, &mut read_buf);\n    assert_eq!(n, 6);\n    assert_eq!(&read_buf[..n], &[0, 1, 2, 3, 4, 5]);\n}\n\n/// read_latest handles wrap-around correctly.\n#[test]\nfn read_latest_wraps_around() {\n    let mut dma_buf = [0u8; CAP];\n    for i in 0..CAP {\n        dma_buf[i] = i as u8;\n    }\n    let mut ringbuf = ReadableDmaRingBuffer::new(&mut dma_buf);\n    let mut dma = TestCircularTransfer::new(CAP);\n\n    // Reset at position 12.\n    dma.setup(vec![\n        TestCircularTransferRequest::ResetCompleteCount(0),\n        TestCircularTransferRequest::ResetCompleteCount(0),\n        TestCircularTransferRequest::PositionRequest(12),\n    ]);\n    ringbuf.reset(&mut dma);\n\n    // DMA wraps around: complete_count=1, position=4.\n    // Available = (1*16+4) - (0*16+12) = 8.\n    // User buffer holds 4 → reads latest 4: positions 0..4 after wrap.\n    let mut read_buf = [0u8; 4];\n    dma.setup(vec![\n        TestCircularTransferRequest::ResetCompleteCount(1),\n        TestCircularTransferRequest::PositionRequest(4),\n    ]);\n\n    let n = ringbuf.read_latest(&mut dma, &mut read_buf);\n    assert_eq!(n, 4);\n    // After wrap, positions 0..4 in the buffer hold [0, 1, 2, 3]\n    assert_eq!(&read_buf[..n], &[0, 1, 2, 3]);\n}\n\nmod prop_test;\n"
  },
  {
    "path": "embassy-stm32/src/dma/ringbuffer/tests/prop_test/mod.rs",
    "content": "use std::task::Waker;\n\nuse proptest::prop_oneof;\nuse proptest::strategy::{self, BoxedStrategy, Strategy as _};\nuse proptest_state_machine::{ReferenceStateMachine, StateMachineTest, prop_state_machine};\n\nuse super::*;\n\nconst CAP: usize = 128;\n\n#[derive(Debug, Default)]\nstruct DmaMock {\n    pos: usize,\n    wraps: usize,\n}\n\nimpl DmaMock {\n    pub fn advance(&mut self, steps: usize) {\n        let next = self.pos + steps;\n        self.pos = next % CAP;\n        self.wraps += next / CAP;\n    }\n}\n\nimpl DmaCtrl for DmaMock {\n    fn get_remaining_transfers(&self) -> usize {\n        CAP - self.pos\n    }\n\n    fn reset_complete_count(&mut self) -> usize {\n        core::mem::replace(&mut self.wraps, 0)\n    }\n\n    fn set_waker(&mut self, _waker: &Waker) {}\n}\n\n#[derive(Debug, Clone)]\nenum Status {\n    Available(usize),\n    Failed,\n}\n\nimpl Status {\n    pub fn new(capacity: usize) -> Self {\n        Self::Available(capacity)\n    }\n}\n\nmod reader;\nmod writer;\n"
  },
  {
    "path": "embassy-stm32/src/dma/ringbuffer/tests/prop_test/reader.rs",
    "content": "use core::fmt::Debug;\n\nuse super::*;\n\n#[derive(Debug, Clone)]\nenum ReaderTransition {\n    Write(usize),\n    Reset,\n    ReadUpTo(usize),\n}\n\nstruct ReaderSM;\n\nimpl ReferenceStateMachine for ReaderSM {\n    type State = Status;\n    type Transition = ReaderTransition;\n\n    fn init_state() -> BoxedStrategy<Self::State> {\n        strategy::Just(Status::new(0)).boxed()\n    }\n\n    fn transitions(_state: &Self::State) -> BoxedStrategy<Self::Transition> {\n        prop_oneof![\n            (1..50_usize).prop_map(ReaderTransition::Write),\n            (1..50_usize).prop_map(ReaderTransition::ReadUpTo),\n            strategy::Just(ReaderTransition::Reset),\n        ]\n        .boxed()\n    }\n\n    fn apply(status: Self::State, transition: &Self::Transition) -> Self::State {\n        match (status, transition) {\n            (_, ReaderTransition::Reset) => Status::Available(0),\n            (Status::Available(x), ReaderTransition::Write(y)) => {\n                if x + y > CAP {\n                    Status::Failed\n                } else {\n                    Status::Available(x + y)\n                }\n            }\n            (Status::Failed, ReaderTransition::Write(_)) => Status::Failed,\n            (Status::Available(x), ReaderTransition::ReadUpTo(y)) => Status::Available(x.saturating_sub(*y)),\n            (Status::Failed, ReaderTransition::ReadUpTo(_)) => Status::Available(0),\n        }\n    }\n}\n\nstruct ReaderSut {\n    status: Status,\n    buffer: *mut [u8],\n    producer: DmaMock,\n    consumer: ReadableDmaRingBuffer<'static, u8>,\n}\n\nimpl Debug for ReaderSut {\n    fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {\n        <DmaMock as Debug>::fmt(&self.producer, f)\n    }\n}\n\nstruct ReaderTest;\n\nimpl StateMachineTest for ReaderTest {\n    type SystemUnderTest = ReaderSut;\n    type Reference = ReaderSM;\n\n    fn init_test(ref_status: &<Self::Reference as ReferenceStateMachine>::State) -> Self::SystemUnderTest {\n        let buffer = Box::into_raw(Box::new([0; CAP]));\n        ReaderSut {\n            status: ref_status.clone(),\n            buffer,\n            producer: DmaMock::default(),\n            consumer: ReadableDmaRingBuffer::new(unsafe { &mut *buffer }),\n        }\n    }\n\n    fn teardown(state: Self::SystemUnderTest) {\n        unsafe {\n            let _ = Box::from_raw(state.buffer);\n        };\n    }\n\n    fn apply(\n        mut sut: Self::SystemUnderTest,\n        ref_state: &<Self::Reference as ReferenceStateMachine>::State,\n        transition: <Self::Reference as ReferenceStateMachine>::Transition,\n    ) -> Self::SystemUnderTest {\n        match transition {\n            ReaderTransition::Write(x) => sut.producer.advance(x),\n            ReaderTransition::Reset => {\n                sut.consumer.reset(&mut sut.producer);\n            }\n            ReaderTransition::ReadUpTo(x) => {\n                let status = sut.status;\n                let ReaderSut {\n                    ref mut producer,\n                    ref mut consumer,\n                    ..\n                } = sut;\n                let mut buf = vec![0; x];\n                let res = consumer.read(producer, &mut buf);\n                match status {\n                    Status::Available(n) => {\n                        let readable = x.min(n);\n\n                        assert_eq!(res.unwrap().0, readable);\n                    }\n                    Status::Failed => assert!(res.is_err()),\n                }\n            }\n        }\n\n        ReaderSut {\n            status: ref_state.clone(),\n            ..sut\n        }\n    }\n}\n\nprop_state_machine! {\n    #[test]\n    fn reader_state_test(sequential 1..20 => ReaderTest);\n}\n"
  },
  {
    "path": "embassy-stm32/src/dma/ringbuffer/tests/prop_test/writer.rs",
    "content": "use core::fmt::Debug;\n\nuse super::*;\n\n#[derive(Debug, Clone)]\nenum WriterTransition {\n    Read(usize),\n    WriteUpTo(usize),\n    Reset,\n}\n\nstruct WriterSM;\n\nimpl ReferenceStateMachine for WriterSM {\n    type State = Status;\n    type Transition = WriterTransition;\n\n    fn init_state() -> BoxedStrategy<Self::State> {\n        strategy::Just(Status::new(CAP)).boxed()\n    }\n\n    fn transitions(_state: &Self::State) -> BoxedStrategy<Self::Transition> {\n        prop_oneof![\n            (1..50_usize).prop_map(WriterTransition::Read),\n            (1..50_usize).prop_map(WriterTransition::WriteUpTo),\n            strategy::Just(WriterTransition::Reset),\n        ]\n        .boxed()\n    }\n\n    fn apply(status: Self::State, transition: &Self::Transition) -> Self::State {\n        match (status, transition) {\n            (_, WriterTransition::Reset) => Status::Available(CAP),\n            (Status::Available(x), WriterTransition::Read(y)) => {\n                if x < *y {\n                    Status::Failed\n                } else {\n                    Status::Available(x - y)\n                }\n            }\n            (Status::Failed, WriterTransition::Read(_)) => Status::Failed,\n            (Status::Available(x), WriterTransition::WriteUpTo(y)) => Status::Available((x + *y).min(CAP)),\n            (Status::Failed, WriterTransition::WriteUpTo(_)) => Status::Available(CAP),\n        }\n    }\n}\n\nstruct WriterSut {\n    status: Status,\n    buffer: *mut [u8],\n    producer: WritableDmaRingBuffer<'static, u8>,\n    consumer: DmaMock,\n}\n\nimpl Debug for WriterSut {\n    fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {\n        <DmaMock as Debug>::fmt(&self.consumer, f)\n    }\n}\n\nstruct WriterTest;\n\nimpl StateMachineTest for WriterTest {\n    type SystemUnderTest = WriterSut;\n    type Reference = WriterSM;\n\n    fn init_test(ref_status: &<Self::Reference as ReferenceStateMachine>::State) -> Self::SystemUnderTest {\n        let buffer = Box::into_raw(Box::new([0; CAP]));\n        WriterSut {\n            status: ref_status.clone(),\n            buffer,\n            producer: WritableDmaRingBuffer::new(unsafe { &mut *buffer }),\n            consumer: DmaMock::default(),\n        }\n    }\n\n    fn teardown(state: Self::SystemUnderTest) {\n        unsafe {\n            let _ = Box::from_raw(state.buffer);\n        };\n    }\n\n    fn apply(\n        mut sut: Self::SystemUnderTest,\n        ref_status: &<Self::Reference as ReferenceStateMachine>::State,\n        transition: <Self::Reference as ReferenceStateMachine>::Transition,\n    ) -> Self::SystemUnderTest {\n        match transition {\n            WriterTransition::Read(x) => sut.consumer.advance(x),\n            WriterTransition::Reset => {\n                sut.producer.reset(&mut sut.consumer);\n            }\n            WriterTransition::WriteUpTo(x) => {\n                let status = sut.status;\n                let WriterSut {\n                    ref mut producer,\n                    ref mut consumer,\n                    ..\n                } = sut;\n                let mut buf = vec![0; x];\n                let res = producer.write(consumer, &mut buf);\n                match status {\n                    Status::Available(n) => {\n                        let writable = x.min(CAP - n.min(CAP));\n                        assert_eq!(res.unwrap().0, writable);\n                    }\n                    Status::Failed => assert!(res.is_err()),\n                }\n            }\n        }\n\n        WriterSut {\n            status: ref_status.clone(),\n            ..sut\n        }\n    }\n}\n\nprop_state_machine! {\n    #[test]\n    fn writer_state_test(sequential 1..20 => WriterTest);\n}\n"
  },
  {
    "path": "embassy-stm32/src/dma/util.rs",
    "content": "use super::word::Word;\nuse super::{Channel, Request, Transfer, TransferOptions};\nuse crate::Peri;\nuse crate::dma::{ChannelInstance, InterruptHandler};\nuse crate::interrupt::typelevel::Binding;\n\n/// Convenience wrapper, contains a channel and a request number.\n///\n/// Commonly used in peripheral drivers that own DMA channels.\npub(crate) struct ChannelAndRequest<'d> {\n    pub channel: Channel<'d>,\n    pub request: Request,\n}\n\nimpl<'d> ChannelAndRequest<'d> {\n    pub fn new<T: ChannelInstance>(\n        ch: Peri<'d, T>,\n        irqs: impl Binding<T::Interrupt, InterruptHandler<T>> + 'd,\n        request: Request,\n    ) -> Self {\n        Self {\n            channel: Channel::new(ch, irqs),\n            request,\n        }\n    }\n\n    pub unsafe fn read<'a, W: Word>(\n        &'a mut self,\n        peri_addr: *mut W,\n        buf: &'a mut [W],\n        options: TransferOptions,\n    ) -> Transfer<'a> {\n        self.channel.read(self.request, peri_addr, buf, options)\n    }\n\n    pub unsafe fn read_raw<'a, MW: Word, PW: Word>(\n        &'a mut self,\n        peri_addr: *mut PW,\n        buf: *mut [MW],\n        options: TransferOptions,\n    ) -> Transfer<'a> {\n        self.channel.read_raw(self.request, peri_addr, buf, options)\n    }\n\n    pub unsafe fn write<'a, W: Word>(\n        &'a mut self,\n        buf: &'a [W],\n        peri_addr: *mut W,\n        options: TransferOptions,\n    ) -> Transfer<'a> {\n        self.channel.write(self.request, buf, peri_addr, options)\n    }\n\n    pub unsafe fn write_raw<'a, MW: Word, PW: Word>(\n        &'a mut self,\n        buf: *const [MW],\n        peri_addr: *mut PW,\n        options: TransferOptions,\n    ) -> Transfer<'a> {\n        self.channel.write_raw(self.request, buf, peri_addr, options)\n    }\n\n    #[allow(dead_code)]\n    pub unsafe fn write_repeated<'a, W: Word>(\n        &'a mut self,\n        repeated: &'a W,\n        count: usize,\n        peri_addr: *mut W,\n        options: TransferOptions,\n    ) -> Transfer<'a> {\n        self.channel\n            .write_repeated(self.request, repeated, count, peri_addr, options)\n    }\n\n    /// Reborrow the channel and request, allowing it to be used in multiple places.\n    #[allow(dead_code)]\n    pub fn reborrow(&mut self) -> ChannelAndRequest<'_> {\n        ChannelAndRequest {\n            channel: self.channel.reborrow(),\n            request: self.request,\n        }\n    }\n\n    #[allow(dead_code)]\n    pub(crate) unsafe fn clone_unchecked(&self) -> ChannelAndRequest<'d> {\n        ChannelAndRequest {\n            channel: self.channel.clone_unchecked(),\n            request: self.request,\n        }\n    }\n}\n"
  },
  {
    "path": "embassy-stm32/src/dma/word.rs",
    "content": "//! DMA word sizes.\n\n#[allow(missing_docs)]\n#[derive(Debug, Copy, Clone, PartialEq, Eq)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub enum WordSize {\n    OneByte,\n    TwoBytes,\n    FourBytes,\n    EightBytes,\n}\n\nimpl WordSize {\n    /// Amount of bytes of this word size.\n    pub fn bytes(&self) -> usize {\n        match self {\n            Self::OneByte => 1,\n            Self::TwoBytes => 2,\n            Self::FourBytes => 4,\n            Self::EightBytes => 8,\n        }\n    }\n}\n\ntrait SealedWord {}\n\n/// DMA word trait.\n///\n/// This is implemented for u8, u16, u32, etc.\n#[allow(private_bounds)]\npub trait Word: SealedWord + Default + Copy + 'static {\n    /// Word size\n    fn size() -> WordSize;\n    /// Amount of bits of this word size.\n    fn bits() -> usize;\n    /// Maximum value of this type.\n    fn max() -> usize {\n        (1 << Self::bits()) - 1\n    }\n}\n\nmacro_rules! impl_word {\n    (_, $T:ident, $bits:literal, $size:ident) => {\n        impl SealedWord for $T {}\n        impl Word for $T {\n            fn bits() -> usize {\n                $bits\n            }\n            fn size() -> WordSize {\n                WordSize::$size\n            }\n        }\n    };\n    ($T:ident, $uX:ident, $bits:literal, $size:ident) => {\n        #[repr(transparent)]\n        #[derive(Copy, Clone, Default)]\n        #[doc = concat!(stringify!($T), \" word size\")]\n        pub struct $T(pub $uX);\n        impl_word!(_, $T, $bits, $size);\n    };\n}\n\nimpl_word!(U1, u8, 1, OneByte);\nimpl_word!(U2, u8, 2, OneByte);\nimpl_word!(U3, u8, 3, OneByte);\nimpl_word!(U4, u8, 4, OneByte);\nimpl_word!(U5, u8, 5, OneByte);\nimpl_word!(U6, u8, 6, OneByte);\nimpl_word!(U7, u8, 7, OneByte);\nimpl_word!(_, u8, 8, OneByte);\nimpl_word!(U9, u16, 9, TwoBytes);\nimpl_word!(U10, u16, 10, TwoBytes);\nimpl_word!(U11, u16, 11, TwoBytes);\nimpl_word!(U12, u16, 12, TwoBytes);\nimpl_word!(U13, u16, 13, TwoBytes);\nimpl_word!(U14, u16, 14, TwoBytes);\nimpl_word!(U15, u16, 15, TwoBytes);\nimpl_word!(_, u16, 16, TwoBytes);\nimpl_word!(U17, u32, 17, FourBytes);\nimpl_word!(U18, u32, 18, FourBytes);\nimpl_word!(U19, u32, 19, FourBytes);\nimpl_word!(U20, u32, 20, FourBytes);\nimpl_word!(U21, u32, 21, FourBytes);\nimpl_word!(U22, u32, 22, FourBytes);\nimpl_word!(U23, u32, 23, FourBytes);\nimpl_word!(U24, u32, 24, FourBytes);\nimpl_word!(U25, u32, 25, FourBytes);\nimpl_word!(U26, u32, 26, FourBytes);\nimpl_word!(U27, u32, 27, FourBytes);\nimpl_word!(U28, u32, 28, FourBytes);\nimpl_word!(U29, u32, 29, FourBytes);\nimpl_word!(U30, u32, 30, FourBytes);\nimpl_word!(U31, u32, 31, FourBytes);\nimpl_word!(_, u32, 32, FourBytes);\nimpl_word!(_, u64, 64, EightBytes);\n"
  },
  {
    "path": "embassy-stm32/src/dsihost.rs",
    "content": "//! DSI HOST\n\nuse core::marker::PhantomData;\n\nuse embassy_hal_internal::PeripheralType;\n\n//use crate::gpio::{AnyPin, SealedPin};\nuse crate::block_for_us;\nuse crate::gpio::{AfType, Flex, OutputType, Speed};\nuse crate::rcc::{self, RccPeripheral};\nuse crate::{Peri, peripherals};\n\n/// PacketTypes extracted from CubeMX\n#[repr(u8)]\n#[allow(dead_code)]\npub enum PacketType {\n    /// DCS short write, no parameters\n    DcsShortPktWriteP0,\n    /// DCS short write, one parameter\n    DcsShortPktWriteP1,\n    /// Generic short write, no parameters\n    GenShortPktWriteP0,\n    /// Generic short write, one parameter\n    GenShortPktWriteP1,\n    /// Generic short write, two parameters\n    GenShortPktWriteP2,\n    /// DCS long write\n    DcsLongPktWrite,\n    /// Generic long write\n    GenLongPktWrite,\n    /// DCS short read\n    DcsShortPktRead(u8),\n    /// Generic short read, no parameters\n    GenShortPktReadP0,\n    /// Generic short read, one parameter\n    GenShortPktReadP1(u8),\n    /// Generic short read, two parameters\n    GenShortPktReadP2(u8, u8),\n    /// Used to set the maximum return packet size for reading data\n    MaxReturnPktSize,\n}\n\nimpl From<PacketType> for u8 {\n    fn from(packet_type: PacketType) -> u8 {\n        match packet_type {\n            PacketType::DcsShortPktWriteP0 => 0x05,\n            PacketType::DcsShortPktWriteP1 => 0x15,\n            PacketType::GenShortPktWriteP0 => 0x03,\n            PacketType::GenShortPktWriteP1 => 0x13,\n            PacketType::GenShortPktWriteP2 => 0x23,\n            PacketType::DcsLongPktWrite => 0x39,\n            PacketType::GenLongPktWrite => 0x29,\n            PacketType::DcsShortPktRead(_) => 0x06,\n            PacketType::GenShortPktReadP0 => 0x04,\n            PacketType::GenShortPktReadP1(_) => 0x14,\n            PacketType::GenShortPktReadP2(_, _) => 0x24,\n            PacketType::MaxReturnPktSize => 0x37,\n        }\n    }\n}\n\n/// DSIHOST driver.\npub struct DsiHost<'d, T: Instance> {\n    _peri: PhantomData<&'d mut T>,\n    _te: Flex<'d>,\n}\n\nimpl<'d, T: Instance> DsiHost<'d, T> {\n    /// Note: Full-Duplex modes are not supported at this time\n    pub fn new(_peri: Peri<'d, T>, te: Peri<'d, impl TePin<T>>) -> Self {\n        rcc::enable_and_reset::<T>();\n\n        // Set Tearing Enable pin according to CubeMx example\n        set_as_af!(te, AfType::output(OutputType::PushPull, Speed::Low));\n        /*\n                T::regs().wcr().modify(|w| {\n                    w.set_dsien(true);\n                });\n        */\n        Self {\n            _peri: PhantomData,\n            _te: Flex::new(te),\n        }\n    }\n\n    /// Get the DSIHOST hardware version. Found in the reference manual for comparison.\n    pub fn get_version(&self) -> u32 {\n        T::regs().vr().read().version()\n    }\n\n    /// Set the enable bit in the control register and assert that it has been enabled\n    pub fn enable(&mut self) {\n        T::regs().cr().modify(|w| w.set_en(true));\n        assert!(T::regs().cr().read().en())\n    }\n\n    /// Unset the enable bit in the control register and assert that it has been disabled\n    pub fn disable(&mut self) {\n        T::regs().cr().modify(|w| w.set_en(false));\n        assert!(!T::regs().cr().read().en())\n    }\n\n    /// Set the DSI enable bit in the wrapper control register and assert that it has been enabled\n    pub fn enable_wrapper_dsi(&mut self) {\n        T::regs().wcr().modify(|w| w.set_dsien(true));\n        assert!(T::regs().wcr().read().dsien())\n    }\n\n    /// Unset the DSI enable bit in the wrapper control register and assert that it has been disabled\n    pub fn disable_wrapper_dsi(&mut self) {\n        T::regs().wcr().modify(|w| w.set_dsien(false));\n        assert!(!T::regs().wcr().read().dsien())\n    }\n\n    /// DCS or Generic short/long write command\n    pub fn write_cmd(&mut self, channel_id: u8, address: u8, data: &[u8]) -> Result<(), Error> {\n        match data.len() {\n            0 => self.short_write(channel_id, PacketType::DcsShortPktWriteP0, address, 0),\n            1 => self.short_write(channel_id, PacketType::DcsShortPktWriteP1, address, data[0]),\n            _ => self.long_write(\n                channel_id,\n                PacketType::DcsLongPktWrite, // FIXME: This might be a generic long packet, as well...\n                address,\n                data,\n            ),\n        }\n    }\n\n    fn short_write(&mut self, channel_id: u8, packet_type: PacketType, param1: u8, param2: u8) -> Result<(), Error> {\n        #[cfg(feature = \"defmt\")]\n        defmt::debug!(\"short_write: BEGIN wait for command fifo empty\");\n\n        // Wait for Command FIFO empty\n        self.wait_command_fifo_empty()?;\n        #[cfg(feature = \"defmt\")]\n        defmt::debug!(\"short_write: END wait for command fifo empty\");\n\n        // Configure the packet to send a short DCS command with 0 or 1 parameters\n        // Update the DSI packet header with new information\n        self.config_packet_header(channel_id, packet_type, param1, param2);\n\n        self.wait_command_fifo_empty()?;\n\n        let status = T::regs().isr1().read().0;\n        if status != 0 {\n            error!(\"ISR1 after short_write(): {:b}\", status);\n        }\n        Ok(())\n    }\n\n    fn config_packet_header(&mut self, channel_id: u8, packet_type: PacketType, param1: u8, param2: u8) {\n        T::regs().ghcr().write(|w| {\n            w.set_dt(packet_type.into());\n            w.set_vcid(channel_id);\n            w.set_wclsb(param1);\n            w.set_wcmsb(param2);\n        });\n    }\n\n    /// Write long DCS or long Generic command.\n    ///\n    /// `params` is expected to contain at least 2 elements. Use [`short_write`] for a single element.\n    fn long_write(&mut self, channel_id: u8, packet_type: PacketType, address: u8, data: &[u8]) -> Result<(), Error> {\n        // Must be a long packet if we do the long write, obviously.\n        assert!(matches!(\n            packet_type,\n            PacketType::DcsLongPktWrite | PacketType::GenLongPktWrite\n        ));\n\n        // params needs to have at least 2 elements, otherwise short_write should be used\n        assert!(data.len() >= 2);\n\n        #[cfg(feature = \"defmt\")]\n        defmt::debug!(\"long_write: BEGIN wait for command fifo empty\");\n\n        self.wait_command_fifo_empty()?;\n\n        #[cfg(feature = \"defmt\")]\n        defmt::debug!(\"long_write: DONE wait for command fifo empty\");\n\n        // Note: CubeMX example \"NbParams\" is always one LESS than params.len()\n        // DCS code (last element of params) must be on payload byte 1 and if we have only 2 more params,\n        // then they must go into data2 and data3\n        T::regs().gpdr().write(|w| {\n            // data[2] may or may not exist.\n            if let Some(x) = data.get(2) {\n                w.set_data4(*x);\n            }\n            // data[0] and [1] have to exist if long_write is called.\n            w.set_data3(data[1]);\n            w.set_data2(data[0]);\n\n            // DCS Code\n            w.set_data1(address);\n        });\n\n        self.wait_command_fifo_empty()?;\n\n        // These steps are only necessary if more than 1x 4 bytes need to go into the FIFO\n        if data.len() >= 4 {\n            // Generate an iterator that iterates over chunks of exactly 4 bytes\n            let iter = data[3..data.len()].chunks_exact(4);\n            // Obtain remainder before consuming iter\n            let remainder = iter.remainder();\n\n            // Keep filling the buffer with remaining data\n            for param in iter {\n                self.wait_command_fifo_not_full()?;\n                T::regs().gpdr().write(|w| {\n                    w.set_data4(param[3]);\n                    w.set_data3(param[2]);\n                    w.set_data2(param[1]);\n                    w.set_data1(param[0]);\n                });\n\n                self.wait_command_fifo_empty().unwrap();\n            }\n\n            // If the remaining data was not devisible by 4 we get a remainder\n            if remainder.len() >= 1 {\n                self.wait_command_fifo_not_full()?;\n                T::regs().gpdr().write(|w| {\n                    if let Some(x) = remainder.get(2) {\n                        w.set_data3(*x);\n                    }\n                    if let Some(x) = remainder.get(1) {\n                        w.set_data2(*x);\n                    }\n                    w.set_data1(remainder[0]);\n                });\n                self.wait_command_fifo_empty().unwrap();\n            }\n        }\n        // Configure the packet to send a long DCS command\n        self.config_packet_header(\n            channel_id,\n            packet_type,\n            ((data.len() + 1) & 0x00FF) as u8,        // +1 to account for address byte\n            (((data.len() + 1) & 0xFF00) >> 8) as u8, // +1 to account for address byte\n        );\n\n        self.wait_command_fifo_empty()?;\n\n        let status = T::regs().isr1().read().0;\n        if status != 0 {\n            error!(\"ISR1 after long_write(): {:b}\", status);\n        }\n        Ok(())\n    }\n\n    /// Read DSI Register\n    pub fn read(\n        &mut self,\n        channel_id: u8,\n        packet_type: PacketType,\n        read_size: u16,\n        data: &mut [u8],\n    ) -> Result<(), Error> {\n        if data.len() != read_size as usize {\n            return Err(Error::InvalidReadSize);\n        }\n\n        // Set the maximum return packet size\n        self.short_write(\n            channel_id,\n            PacketType::MaxReturnPktSize,\n            (read_size & 0xFF) as u8,\n            ((read_size & 0xFF00) >> 8) as u8,\n        )?;\n\n        // Set the packet header according to the packet_type\n        use PacketType::*;\n        match packet_type {\n            DcsShortPktRead(cmd) => self.config_packet_header(channel_id, packet_type, cmd, 0),\n            GenShortPktReadP0 => self.config_packet_header(channel_id, packet_type, 0, 0),\n            GenShortPktReadP1(param1) => self.config_packet_header(channel_id, packet_type, param1, 0),\n            GenShortPktReadP2(param1, param2) => self.config_packet_header(channel_id, packet_type, param1, param2),\n            _ => return Err(Error::InvalidPacketType),\n        }\n\n        self.wait_read_not_busy()?;\n\n        // Obtain chunks of 32-bit so the entire FIFO data register can be read\n        for bytes in data.chunks_exact_mut(4) {\n            self.wait_payload_read_fifo_not_empty()?;\n\n            // Only perform a single read on the entire register to avoid unintended side-effects\n            let gpdr = T::regs().gpdr().read();\n            bytes[0] = gpdr.data1();\n            bytes[1] = gpdr.data2();\n            bytes[2] = gpdr.data3();\n            bytes[3] = gpdr.data4();\n        }\n\n        // Collect the remaining chunks and read the corresponding number of bytes from the FIFO\n        let remainder = data.chunks_exact_mut(4).into_remainder();\n        if !remainder.is_empty() {\n            self.wait_payload_read_fifo_not_empty()?;\n            // Only perform a single read on the entire register to avoid unintended side-effects\n            let gpdr = T::regs().gpdr().read();\n            if let Some(x) = remainder.get_mut(0) {\n                *x = gpdr.data1()\n            }\n            if let Some(x) = remainder.get_mut(1) {\n                *x = gpdr.data2()\n            }\n            if let Some(x) = remainder.get_mut(2) {\n                *x = gpdr.data3()\n            }\n        }\n\n        /*\n        // Used this to check whether there are read errors. Does not seem like it.\n        if !self.read_busy() {\n            defmt::debug!(\"Read not busy!\");\n            if self.packet_size_error() {\n                return Err(Error::ReadError);\n            }\n        }\n        */\n        Ok(())\n    }\n\n    fn wait_command_fifo_empty(&self) -> Result<(), Error> {\n        for _ in 1..1000 {\n            // Wait for Command FIFO empty\n            if T::regs().gpsr().read().cmdfe() {\n                return Ok(());\n            }\n            block_for_us(1_000);\n        }\n        Err(Error::FifoTimeout)\n    }\n\n    fn wait_command_fifo_not_full(&self) -> Result<(), Error> {\n        for _ in 1..1000 {\n            // Wait for Command FIFO not empty\n            if !T::regs().gpsr().read().cmdff() {\n                return Ok(());\n            }\n            block_for_us(1_000);\n        }\n        Err(Error::FifoTimeout)\n    }\n\n    fn wait_read_not_busy(&self) -> Result<(), Error> {\n        for _ in 1..1000 {\n            // Wait for read not busy\n            if !self.read_busy() {\n                return Ok(());\n            }\n            block_for_us(1_000);\n        }\n        Err(Error::ReadTimeout)\n    }\n\n    fn wait_payload_read_fifo_not_empty(&self) -> Result<(), Error> {\n        for _ in 1..1000 {\n            // Wait for payload read FIFO not empty\n            if !T::regs().gpsr().read().prdfe() {\n                return Ok(());\n            }\n            block_for_us(1_000);\n        }\n        Err(Error::FifoTimeout)\n    }\n\n    fn _packet_size_error(&self) -> bool {\n        T::regs().isr1().read().pse()\n    }\n\n    fn read_busy(&self) -> bool {\n        T::regs().gpsr().read().rcb()\n    }\n}\n\n/// Possible Error Types for DSI HOST\n#[non_exhaustive]\n#[derive(Debug, PartialEq, Eq, Clone, Copy)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub enum Error {\n    /// Waiting for FIFO empty flag timed out\n    FifoTimeout,\n    /// The specified `PacketType` is invalid for the selected operation\n    InvalidPacketType,\n    /// Provided read size does not match the read buffer length\n    InvalidReadSize,\n    /// Error during read\n    ReadError,\n    /// Read operation timed out\n    ReadTimeout,\n}\n\nimpl<'d, T: Instance> Drop for DsiHost<'d, T> {\n    fn drop(&mut self) {}\n}\n\ntrait SealedInstance: crate::rcc::SealedRccPeripheral {\n    fn regs() -> crate::pac::dsihost::Dsihost;\n}\n\n/// DSI instance trait.\n#[allow(private_bounds)]\npub trait Instance: SealedInstance + PeripheralType + RccPeripheral + 'static {}\n\npin_trait!(TePin, Instance);\n\nforeach_peripheral!(\n    (dsihost, $inst:ident) => {\n        impl crate::dsihost::SealedInstance for peripherals::$inst {\n            fn regs() -> crate::pac::dsihost::Dsihost {\n                crate::pac::$inst\n            }\n        }\n\n        impl crate::dsihost::Instance for peripherals::$inst {}\n    };\n);\n"
  },
  {
    "path": "embassy-stm32/src/dts/mod.rs",
    "content": "//! Digital Temperature Sensor (DTS)\n\nuse core::future::poll_fn;\nuse core::sync::atomic::{Ordering, compiler_fence};\nuse core::task::Poll;\n\nuse embassy_hal_internal::Peri;\nuse embassy_sync::waitqueue::AtomicWaker;\n\nuse crate::interrupt::InterruptExt;\nuse crate::peripherals::DTS;\nuse crate::time::Hertz;\nuse crate::{interrupt, pac, rcc};\n\nmod tsel;\npub use tsel::TriggerSel;\n\n#[allow(missing_docs)]\n#[derive(Eq, PartialEq, Ord, PartialOrd, Clone, Copy, Debug)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub enum SampleTime {\n    ClockCycles1 = 1,\n    ClockCycles2 = 2,\n    ClockCycles3 = 3,\n    ClockCycles4 = 4,\n    ClockCycles5 = 5,\n    ClockCycles6 = 6,\n    ClockCycles7 = 7,\n    ClockCycles8 = 8,\n    ClockCycles9 = 9,\n    ClockCycles10 = 10,\n    ClockCycles11 = 11,\n    ClockCycles12 = 12,\n    ClockCycles13 = 13,\n    ClockCycles14 = 14,\n    ClockCycles15 = 15,\n}\n\n#[non_exhaustive]\n#[derive(Clone, Copy, PartialEq, Eq, Debug)]\n/// Config\npub struct Config {\n    /// Sample time\n    pub sample_time: SampleTime,\n    /// Trigger selection\n    pub trigger: TriggerSel,\n}\n\nimpl Default for Config {\n    fn default() -> Self {\n        Self {\n            sample_time: SampleTime::ClockCycles1,\n            trigger: TriggerSel::Software,\n        }\n    }\n}\n\n/// The read-only factory calibration values used for converting a\n/// measurement to a temperature.\n#[derive(Clone, Debug)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub struct FactoryCalibration {\n    /// The calibration temperature in degrees Celsius.\n    pub t0: u8,\n    /// The frequency at the calibration temperature.\n    pub fmt0: Hertz,\n    /// The ramp coefficient in Hertz per degree Celsius.\n    pub ramp_coeff: u16,\n}\n\nconst MAX_DTS_CLK_FREQ: Hertz = Hertz::mhz(1);\n\n/// Digital temperature sensor driver.\npub struct Dts<'d> {\n    _peri: Peri<'d, DTS>,\n}\n\nstatic WAKER: AtomicWaker = AtomicWaker::new();\n\nimpl<'d> Dts<'d> {\n    /// Create a new temperature sensor driver.\n    pub fn new(\n        _peri: Peri<'d, DTS>,\n        _irq: impl interrupt::typelevel::Binding<interrupt::typelevel::DTS, InterruptHandler> + 'd,\n        config: Config,\n    ) -> Self {\n        rcc::enable_and_reset::<DTS>();\n\n        let prescaler = rcc::frequency::<DTS>() / MAX_DTS_CLK_FREQ;\n\n        if prescaler > 127 {\n            panic!(\"DTS PCLK frequency must be less than 127 MHz.\");\n        }\n\n        Self::regs().cfgr1().modify(|w| {\n            w.set_refclk_sel(false);\n            w.set_hsref_clk_div(prescaler as u8);\n            w.set_q_meas_opt(false);\n            // Software trigger\n            w.set_intrig_sel(0);\n            w.set_smp_time(config.sample_time as u8);\n            w.set_intrig_sel(config.trigger as u8);\n            w.set_start(true);\n            w.set_en(true);\n        });\n\n        interrupt::DTS.unpend();\n        unsafe { interrupt::DTS.enable() };\n\n        Self { _peri }\n    }\n\n    /// Reconfigure the driver.\n    pub fn set_config(&mut self, config: &Config) {\n        Self::regs().cfgr1().modify(|w| {\n            w.set_smp_time(config.sample_time as u8);\n            w.set_intrig_sel(config.trigger as u8);\n        });\n    }\n\n    /// Get the read-only factory calibration values used for converting a\n    /// measurement to a temperature.\n    pub fn factory_calibration() -> FactoryCalibration {\n        let t0valr1 = Self::regs().t0valr1().read();\n        let t0 = match t0valr1.t0() {\n            0 => 30,\n            1 => 130,\n            _ => unimplemented!(),\n        };\n        let fmt0 = Hertz::hz(t0valr1.fmt0() as u32 * 100);\n\n        let ramp_coeff = Self::regs().rampvalr().read().ramp_coeff();\n\n        FactoryCalibration { t0, fmt0, ramp_coeff }\n    }\n\n    /// Perform an asynchronous temperature measurement. The returned future can\n    /// be awaited to obtain the measurement.\n    ///\n    /// The future returned waits for the next measurement to complete.\n    ///\n    /// # Example\n    ///\n    /// ```no_run\n    /// use embassy_stm32::{bind_interrupts, dts};\n    /// use embassy_stm32::dts::Dts;\n    ///\n    /// bind_interrupts!(struct Irqs {\n    ///     DTS => temp::InterruptHandler;\n    /// });\n    ///\n    /// # async {\n    /// # let p: embassy_stm32::Peripherals = todo!();\n    /// let mut dts = Dts::new(p.DTS, Irqs, Default::default());\n    /// let v: u16 = dts.read().await;\n    /// # };\n    /// ```\n    pub async fn read(&mut self) -> u16 {\n        let r = Self::regs();\n\n        r.itenr().modify(|w| w.set_iteen(true));\n\n        poll_fn(|cx| {\n            WAKER.register(cx.waker());\n            if r.itenr().read().iteen() {\n                Poll::Pending\n            } else {\n                Poll::Ready(r.dr().read().mfreq())\n            }\n        })\n        .await\n    }\n\n    /// Returns the last measurement made, if any.\n    ///\n    /// There is no guarantee that the measurement is recent or that a\n    /// measurement has ever completed.\n    pub fn read_immediate(&mut self) -> u16 {\n        Self::regs().dr().read().mfreq()\n    }\n\n    fn regs() -> pac::dts::Dts {\n        pac::DTS\n    }\n}\n\nimpl<'d> Drop for Dts<'d> {\n    fn drop(&mut self) {\n        Self::regs().cfgr1().modify(|w| w.set_en(false));\n        rcc::disable::<DTS>();\n    }\n}\n\n/// Interrupt handler.\npub struct InterruptHandler {\n    _private: (),\n}\n\nimpl interrupt::typelevel::Handler<interrupt::typelevel::DTS> for InterruptHandler {\n    unsafe fn on_interrupt() {\n        let r = pac::DTS;\n        let (sr, itenr) = (r.sr().read(), r.itenr().read());\n\n        if (itenr.iteen() && sr.itef()) || (itenr.aiteen() && sr.aitef()) {\n            r.itenr().modify(|w| {\n                w.set_iteen(false);\n                w.set_aiteen(false);\n            });\n            r.icifr().modify(|w| {\n                w.set_citef(true);\n                w.set_caitef(true);\n            });\n        } else if (itenr.itlen() && sr.itlf()) || (itenr.aitlen() && sr.aitlf()) {\n            r.itenr().modify(|w| {\n                w.set_itlen(false);\n                w.set_aitlen(false);\n            });\n            r.icifr().modify(|w| {\n                w.set_citlf(true);\n                w.set_caitlf(true);\n            });\n        } else if (itenr.ithen() && sr.ithf()) || (itenr.aithen() && sr.aithf()) {\n            r.itenr().modify(|w| {\n                w.set_ithen(false);\n                w.set_aithen(false);\n            });\n            r.icifr().modify(|w| {\n                w.set_cithf(true);\n                w.set_caithf(true);\n            });\n        } else {\n            return;\n        }\n\n        compiler_fence(Ordering::SeqCst);\n        WAKER.wake();\n    }\n}\n"
  },
  {
    "path": "embassy-stm32/src/dts/tsel.rs",
    "content": "/// Trigger selection for H5\n#[cfg(stm32h5)]\n#[derive(Debug, Copy, Clone, Eq, PartialEq)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub enum TriggerSel {\n    /// Software triggering. Performs continuous measurements.\n    Software = 0,\n    /// LPTIM1 CH1\n    Lptim1 = 1,\n    /// LPTIM2 CH1\n    Lptim2 = 2,\n    /// LPTIM3 CH1\n    #[cfg(not(stm32h503))]\n    Lptim3 = 3,\n    /// EXTI13\n    Exti13 = 4,\n}\n\n/// Trigger selection for H7, except for H7R and H7S\n#[cfg(stm32h7)]\n#[derive(Debug, Copy, Clone, Eq, PartialEq)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub enum TriggerSel {\n    /// Software triggering. Performs continuous measurements.\n    Software = 0,\n    /// LPTIM1 OUT\n    Lptim1 = 1,\n    /// LPTIM2 OUT\n    Lptim2 = 2,\n    /// LPTIM3 OUT\n    Lptim3 = 3,\n    /// EXTI13\n    Exti13 = 4,\n}\n\n/// Trigger selection for H7R and H7S\n#[cfg(stm32h7rs)]\n#[derive(Debug, Copy, Clone, Eq, PartialEq)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub enum TriggerSel {\n    /// Software triggering. Performs continuous measurements.\n    Software = 0,\n    /// LPTIM4 OUT\n    Lptim4 = 1,\n    /// LPTIM2 CH1\n    Lptim2 = 2,\n    /// LPTIM3 CH1\n    Lptim3 = 3,\n    /// EXTI13\n    Exti13 = 4,\n}\n\n/// Trigger selection for N6\n#[cfg(stm32n6)]\n#[derive(Debug, Copy, Clone, Eq, PartialEq)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub enum TriggerSel {\n    /// Software triggering. Performs continuous measurements.\n    Software = 0,\n    /// LPTIM4 OUT\n    Lptim4 = 1,\n    /// LPTIM2 CH1\n    Lptim2 = 2,\n    /// LPTIM3 CH1\n    Lptim3 = 3,\n    /// EXTI13\n    Exti13 = 4,\n}\n"
  },
  {
    "path": "embassy-stm32/src/eth/generic_phy.rs",
    "content": "//! Generic SMI Ethernet PHY\n\nuse core::task::Context;\n\n#[cfg(feature = \"time\")]\nuse embassy_time::{Duration, Timer};\n#[cfg(feature = \"time\")]\nuse futures_util::FutureExt;\n\nuse super::{Phy, StationManagement};\nuse crate::block_for_us as blocking_delay_us;\n\n#[allow(dead_code)]\nmod phy_consts {\n    pub const PHY_REG_BCR: u8 = 0x00;\n    pub const PHY_REG_BSR: u8 = 0x01;\n    pub const PHY_REG_ID1: u8 = 0x02;\n    pub const PHY_REG_ID2: u8 = 0x03;\n    pub const PHY_REG_ANTX: u8 = 0x04;\n    pub const PHY_REG_ANRX: u8 = 0x05;\n    pub const PHY_REG_ANEXP: u8 = 0x06;\n    pub const PHY_REG_ANNPTX: u8 = 0x07;\n    pub const PHY_REG_ANNPRX: u8 = 0x08;\n    pub const PHY_REG_CTL: u8 = 0x0D; // Ethernet PHY Register Control\n    pub const PHY_REG_ADDAR: u8 = 0x0E; // Ethernet PHY Address or Data\n\n    pub const PHY_REG_WUCSR: u16 = 0x8010;\n\n    pub const PHY_REG_BCR_COLTEST: u16 = 1 << 7;\n    pub const PHY_REG_BCR_FD: u16 = 1 << 8;\n    pub const PHY_REG_BCR_ANRST: u16 = 1 << 9;\n    pub const PHY_REG_BCR_ISOLATE: u16 = 1 << 10;\n    pub const PHY_REG_BCR_POWERDN: u16 = 1 << 11;\n    pub const PHY_REG_BCR_AN: u16 = 1 << 12;\n    pub const PHY_REG_BCR_100M: u16 = 1 << 13;\n    pub const PHY_REG_BCR_LOOPBACK: u16 = 1 << 14;\n    pub const PHY_REG_BCR_RESET: u16 = 1 << 15;\n\n    pub const PHY_REG_BSR_JABBER: u16 = 1 << 1;\n    pub const PHY_REG_BSR_UP: u16 = 1 << 2;\n    pub const PHY_REG_BSR_FAULT: u16 = 1 << 4;\n    pub const PHY_REG_BSR_ANDONE: u16 = 1 << 5;\n}\nuse self::phy_consts::*;\n\n/// Generic SMI Ethernet PHY implementation\npub struct GenericPhy<SM: StationManagement> {\n    phy_addr: u8,\n    sm: SM,\n    #[cfg(feature = \"time\")]\n    poll_interval: Duration,\n}\n\nimpl<SM: StationManagement> GenericPhy<SM> {\n    /// Construct the PHY. It assumes the address `phy_addr` in the SMI communication\n    ///\n    /// # Panics\n    /// `phy_addr` must be in range `0..32`\n    pub fn new(sm: SM, phy_addr: u8) -> Self {\n        assert!(phy_addr < 32);\n        Self {\n            phy_addr,\n            sm,\n            #[cfg(feature = \"time\")]\n            poll_interval: Duration::from_millis(500),\n        }\n    }\n\n    /// Construct the PHY. Try to probe all addresses from 0 to 31 during initialization\n    ///\n    /// # Panics\n    /// Initialization panics if PHY didn't respond on any address\n    pub fn new_auto(sm: SM) -> Self {\n        Self {\n            sm,\n            phy_addr: 0xFF,\n            #[cfg(feature = \"time\")]\n            poll_interval: Duration::from_millis(500),\n        }\n    }\n}\n\nimpl<SM: StationManagement> Phy for GenericPhy<SM> {\n    fn phy_reset(&mut self) {\n        // Detect SMI address\n        if self.phy_addr == 0xFF {\n            for addr in 0..32 {\n                self.sm.smi_write(addr, PHY_REG_BCR, PHY_REG_BCR_RESET);\n                for _ in 0..10 {\n                    if self.sm.smi_read(addr, PHY_REG_BCR) & PHY_REG_BCR_RESET != PHY_REG_BCR_RESET {\n                        trace!(\"Found ETH PHY on address {}\", addr);\n                        self.phy_addr = addr;\n                        return;\n                    }\n                    // Give PHY a total of 100ms to respond\n                    blocking_delay_us(10000);\n                }\n            }\n            panic!(\"PHY did not respond\");\n        }\n\n        self.sm.smi_write(self.phy_addr, PHY_REG_BCR, PHY_REG_BCR_RESET);\n        while self.sm.smi_read(self.phy_addr, PHY_REG_BCR) & PHY_REG_BCR_RESET == PHY_REG_BCR_RESET {}\n    }\n\n    fn phy_init(&mut self) {\n        // Clear WU CSR\n        self.smi_write_ext(PHY_REG_WUCSR, 0);\n\n        // Enable auto-negotiation\n        self.sm.smi_write(\n            self.phy_addr,\n            PHY_REG_BCR,\n            PHY_REG_BCR_AN | PHY_REG_BCR_ANRST | PHY_REG_BCR_100M,\n        );\n    }\n\n    fn poll_link(&mut self, cx: &mut Context) -> bool {\n        #[cfg(not(feature = \"time\"))]\n        cx.waker().wake_by_ref();\n\n        #[cfg(feature = \"time\")]\n        let _ = Timer::after(self.poll_interval).poll_unpin(cx);\n\n        let bsr = self.sm.smi_read(self.phy_addr, PHY_REG_BSR);\n\n        // No link without autonegotiate\n        if bsr & PHY_REG_BSR_ANDONE == 0 {\n            return false;\n        }\n        // No link if link is down\n        if bsr & PHY_REG_BSR_UP == 0 {\n            return false;\n        }\n\n        // Got link\n        true\n    }\n}\n\n/// Public functions for the PHY\nimpl<SM: StationManagement> GenericPhy<SM> {\n    /// Set the SMI polling interval.\n    #[cfg(feature = \"time\")]\n    pub fn set_poll_interval(&mut self, poll_interval: Duration) {\n        self.poll_interval = poll_interval\n    }\n\n    // Writes a value to an extended PHY register in MMD address space\n    fn smi_write_ext(&mut self, reg_addr: u16, reg_data: u16) {\n        self.sm.smi_write(self.phy_addr, PHY_REG_CTL, 0x0003); // set address\n        self.sm.smi_write(self.phy_addr, PHY_REG_ADDAR, reg_addr);\n        self.sm.smi_write(self.phy_addr, PHY_REG_CTL, 0x4003); // set data\n        self.sm.smi_write(self.phy_addr, PHY_REG_ADDAR, reg_data);\n    }\n\n    /// Access the underlying station management.\n    pub fn station_management(&mut self) -> &mut SM {\n        &mut self.sm\n    }\n}\n"
  },
  {
    "path": "embassy-stm32/src/eth/mod.rs",
    "content": "//! Ethernet (ETH)\n#![macro_use]\n\n#[cfg_attr(any(eth_v1a, eth_v1b, eth_v1c), path = \"v1/mod.rs\")]\n#[cfg_attr(eth_v2, path = \"v2/mod.rs\")]\nmod _version;\nmod generic_phy;\nmod sma;\n\nuse core::mem::MaybeUninit;\nuse core::task::Context;\n\nuse embassy_hal_internal::PeripheralType;\nuse embassy_net_driver::{Capabilities, HardwareAddress, LinkState};\nuse embassy_sync::waitqueue::AtomicWaker;\n\npub use self::_version::{InterruptHandler, *};\npub use self::generic_phy::*;\npub use self::sma::{Instance as SmaInstance, Sma, StationManagement};\nuse crate::rcc::RccPeripheral;\n\n#[allow(unused)]\nconst MTU: usize = 1514;\nconst TX_BUFFER_SIZE: usize = 1514;\nconst RX_BUFFER_SIZE: usize = 1536;\n\n#[repr(C, align(8))]\n#[derive(Copy, Clone)]\npub(crate) struct Packet<const N: usize>([u8; N]);\n\n/// Ethernet packet queue.\n///\n/// This struct owns the memory used for reading and writing packets.\n///\n/// `TX` is the number of packets in the transmit queue, `RX` in the receive\n/// queue. A bigger queue allows the hardware to receive more packets while the\n/// CPU is busy doing other things, which may increase performance (especially for RX)\n/// at the cost of more RAM usage.\npub struct PacketQueue<const TX: usize, const RX: usize> {\n    tx_desc: [TDes; TX],\n    rx_desc: [RDes; RX],\n    tx_buf: [Packet<TX_BUFFER_SIZE>; TX],\n    rx_buf: [Packet<RX_BUFFER_SIZE>; RX],\n}\n\nimpl<const TX: usize, const RX: usize> PacketQueue<TX, RX> {\n    /// Create a new packet queue.\n    pub const fn new() -> Self {\n        Self {\n            tx_desc: [const { TDes::new() }; TX],\n            rx_desc: [const { RDes::new() }; RX],\n            tx_buf: [Packet([0; TX_BUFFER_SIZE]); TX],\n            rx_buf: [Packet([0; RX_BUFFER_SIZE]); RX],\n        }\n    }\n\n    /// Initialize a packet queue in-place.\n    ///\n    /// This can be helpful to avoid accidentally stack-allocating the packet queue in the stack. The\n    /// Rust compiler can sometimes be a bit dumb when working with large owned values: if you call `new()`\n    /// and then store the returned PacketQueue in its final place (like a `static`), the compiler might\n    /// place it temporarily on the stack then move it. Since this struct is quite big, it may result\n    /// in a stack overflow.\n    ///\n    /// With this function, you can create an uninitialized `static` with type `MaybeUninit<PacketQueue<...>>`\n    /// and initialize it in-place, guaranteeing no stack usage.\n    ///\n    /// After calling this function, calling `assume_init` on the MaybeUninit is guaranteed safe.\n    pub fn init(this: &mut MaybeUninit<Self>) {\n        unsafe {\n            this.as_mut_ptr().write_bytes(0u8, 1);\n        }\n    }\n}\n\nstatic WAKER: AtomicWaker = AtomicWaker::new();\n\nimpl<'d, T: Instance, P: Phy> embassy_net_driver::Driver for Ethernet<'d, T, P> {\n    type RxToken<'a>\n        = RxToken<'a, 'd>\n    where\n        Self: 'a;\n    type TxToken<'a>\n        = TxToken<'a, 'd>\n    where\n        Self: 'a;\n\n    fn receive(&mut self, cx: &mut Context) -> Option<(Self::RxToken<'_>, Self::TxToken<'_>)> {\n        WAKER.register(cx.waker());\n        if self.rx.available().is_some() && self.tx.available().is_some() {\n            Some((RxToken { rx: &mut self.rx }, TxToken { tx: &mut self.tx }))\n        } else {\n            None\n        }\n    }\n\n    fn transmit(&mut self, cx: &mut Context) -> Option<Self::TxToken<'_>> {\n        WAKER.register(cx.waker());\n        if self.tx.available().is_some() {\n            Some(TxToken { tx: &mut self.tx })\n        } else {\n            None\n        }\n    }\n\n    fn capabilities(&self) -> Capabilities {\n        let mut caps = Capabilities::default();\n        caps.max_transmission_unit = MTU;\n        caps.max_burst_size = Some(self.tx.len());\n        caps\n    }\n\n    fn link_state(&mut self, cx: &mut Context) -> LinkState {\n        if self.phy.poll_link(cx) {\n            LinkState::Up\n        } else {\n            LinkState::Down\n        }\n    }\n\n    fn hardware_address(&self) -> HardwareAddress {\n        HardwareAddress::Ethernet(self.mac_addr)\n    }\n}\n\n/// `embassy-net` RX token.\npub struct RxToken<'a, 'd> {\n    rx: &'a mut RDesRing<'d>,\n}\n\nimpl<'a, 'd> embassy_net_driver::RxToken for RxToken<'a, 'd> {\n    fn consume<R, F>(self, f: F) -> R\n    where\n        F: FnOnce(&mut [u8]) -> R,\n    {\n        // NOTE(unwrap): we checked the queue wasn't full when creating the token.\n        let pkt = unwrap!(self.rx.available());\n        let r = f(pkt);\n        self.rx.pop_packet();\n        r\n    }\n}\n\n/// `embassy-net` TX token.\npub struct TxToken<'a, 'd> {\n    tx: &'a mut TDesRing<'d>,\n}\n\nimpl<'a, 'd> embassy_net_driver::TxToken for TxToken<'a, 'd> {\n    fn consume<R, F>(self, len: usize, f: F) -> R\n    where\n        F: FnOnce(&mut [u8]) -> R,\n    {\n        // NOTE(unwrap): we checked the queue wasn't full when creating the token.\n        let pkt = unwrap!(self.tx.available());\n        let r = f(&mut pkt[..len]);\n        self.tx.transmit(len);\n        r\n    }\n}\n\n/// Trait for an Ethernet PHY\npub trait Phy {\n    /// Reset PHY and wait for it to come out of reset.\n    fn phy_reset(&mut self);\n    /// PHY initialisation.\n    fn phy_init(&mut self);\n    /// Poll link to see if it is up and FD with 100Mbps\n    fn poll_link(&mut self, cx: &mut Context) -> bool;\n}\n\nimpl<'d, T: Instance, P: Phy> Ethernet<'d, T, P> {\n    /// Access the user-supplied `Phy`.\n    pub fn phy(&self) -> &P {\n        &self.phy\n    }\n\n    /// Mutably access the user-supplied `Phy`.\n    pub fn phy_mut(&mut self) -> &mut P {\n        &mut self.phy\n    }\n}\n\ntrait SealedInstance {\n    fn regs() -> crate::pac::eth::Eth;\n}\n\n/// Ethernet instance.\n#[allow(private_bounds)]\npub trait Instance: SealedInstance + PeripheralType + RccPeripheral + Send + 'static {}\n\nimpl SealedInstance for crate::peripherals::ETH {\n    fn regs() -> crate::pac::eth::Eth {\n        crate::pac::ETH\n    }\n}\nimpl Instance for crate::peripherals::ETH {}\n\npin_trait!(RXClkPin, Instance, @A);\npin_trait!(TXClkPin, Instance, @A);\npin_trait!(RefClkPin, Instance, @A);\npin_trait!(MDIOPin, sma::Instance, @A);\npin_trait!(MDCPin, sma::Instance, @A);\npin_trait!(RXDVPin, Instance, @A);\npin_trait!(CRSPin, Instance, @A);\npin_trait!(RXD0Pin, Instance, @A);\npin_trait!(RXD1Pin, Instance, @A);\npin_trait!(RXD2Pin, Instance, @A);\npin_trait!(RXD3Pin, Instance, @A);\npin_trait!(TXD0Pin, Instance, @A);\npin_trait!(TXD1Pin, Instance, @A);\npin_trait!(TXD2Pin, Instance, @A);\npin_trait!(TXD3Pin, Instance, @A);\npin_trait!(TXEnPin, Instance, @A);\n"
  },
  {
    "path": "embassy-stm32/src/eth/sma/mod.rs",
    "content": "//! Station Management Agent (also known as MDIO or SMI).\n\n#![macro_use]\n\n#[cfg_attr(eth_v2, path = \"v2.rs\")]\n#[cfg_attr(any(eth_v1a, eth_v1b, eth_v1c), path = \"v1.rs\")]\nmod _version;\n\nuse embassy_hal_internal::PeripheralType;\nuse stm32_metapac::common::{RW, Reg};\n\npub use self::_version::*;\n\n/// Station Management Interface (SMI).\npub trait StationManagement {\n    /// Read a register over SMI.\n    fn smi_read(&mut self, phy_addr: u8, reg: u8) -> u16;\n    /// Write a register over SMI.\n    fn smi_write(&mut self, phy_addr: u8, reg: u8, val: u16);\n}\n\ntrait SealedInstance {\n    fn regs() -> (Reg<AddressRegister, RW>, Reg<DataRegister, RW>);\n}\n\n/// MDIO instance.\n#[allow(private_bounds)]\npub trait Instance: SealedInstance + PeripheralType + Send + 'static {}\n\nimpl SealedInstance for crate::peripherals::ETH_SMA {\n    fn regs() -> (Reg<AddressRegister, RW>, Reg<DataRegister, RW>) {\n        let mac = crate::pac::ETH.ethernet_mac();\n\n        #[cfg(any(eth_v1a, eth_v1b, eth_v1c))]\n        return (mac.macmiiar(), mac.macmiidr());\n\n        #[cfg(eth_v2)]\n        return (mac.macmdioar(), mac.macmdiodr());\n    }\n}\n\nimpl Instance for crate::peripherals::ETH_SMA {}\n"
  },
  {
    "path": "embassy-stm32/src/eth/sma/v1.rs",
    "content": "use embassy_hal_internal::Peri;\npub(crate) use regs::{Macmiiar as AddressRegister, Macmiidr as DataRegister};\nuse stm32_metapac::eth::regs;\nuse stm32_metapac::eth::vals::{Cr, MbProgress, Mw};\n\nuse super::{Instance, StationManagement};\nuse crate::eth::{MDCPin, MDIOPin};\nuse crate::gpio::{AfType, Flex, OutputType, Speed};\n\n/// Station Management Agent.\n///\n/// This peripheral is used for SMI reads and writes to the connected\n/// ethernet PHY/device(s).\npub struct Sma<'d, T: Instance> {\n    _peri: Peri<'d, T>,\n    clock_range: Cr,\n    _pins: [Flex<'d>; 2],\n}\n\nimpl<'d, T: Instance> Sma<'d, T> {\n    /// Create a new instance of this peripheral.\n    pub fn new<#[cfg(afio)] A>(\n        peri: Peri<'d, T>,\n        mdio: Peri<'d, if_afio!(impl MDIOPin<T, A>)>,\n        mdc: Peri<'d, if_afio!(impl MDCPin<T, A>)>,\n    ) -> Self {\n        // Enable necessary clocks.\n        critical_section::with(|_| {\n            #[cfg(eth_v1a)]\n            let reg = crate::pac::RCC.ahbenr();\n\n            #[cfg(any(eth_v1b, eth_v1c))]\n            let reg = crate::pac::RCC.ahb1enr();\n\n            reg.modify(|w| {\n                w.set_ethen(true);\n            })\n        });\n\n        let hclk = unsafe { crate::rcc::get_freqs().hclk1.to_hertz() };\n        let hclk = unwrap!(hclk, \"SMA requires HCLK to be enabled, but it was not.\");\n        let hclk_mhz = hclk.0 / 1_000_000;\n\n        // Set the MDC clock frequency in the range 1MHz - 2.5MHz\n        let clock_range = match hclk_mhz {\n            0..=24 => panic!(\"Invalid HCLK frequency - should be at least 25 MHz.\"),\n            25..=34 => Cr::CR_20_35,     // Divide by 16\n            35..=59 => Cr::CR_35_60,     // Divide by 26\n            60..=99 => Cr::CR_60_100,    // Divide by 42\n            100..=149 => Cr::CR_100_150, // Divide by 62\n            150..=216 => Cr::CR_150_168, // Divide by 102\n            _ => {\n                panic!(\"HCLK results in MDC clock > 2.5MHz even for the highest CSR clock divider\")\n            }\n        };\n\n        Self {\n            _peri: peri,\n            clock_range,\n            _pins: [\n                new_pin!(mdio, AfType::output(OutputType::PushPull, Speed::VeryHigh)).unwrap(),\n                new_pin!(mdc, AfType::output(OutputType::PushPull, Speed::VeryHigh)).unwrap(),\n            ],\n        }\n    }\n}\n\nimpl<T: Instance> StationManagement for Sma<'_, T> {\n    fn smi_read(&mut self, phy_addr: u8, reg: u8) -> u16 {\n        let (macmiiar, macmiidr) = T::regs();\n\n        macmiiar.modify(|w| {\n            w.set_pa(phy_addr);\n            w.set_mr(reg);\n            w.set_mw(Mw::READ); // read operation\n            w.set_cr(self.clock_range);\n            w.set_mb(MbProgress::BUSY); // indicate that operation is in progress\n        });\n        while macmiiar.read().mb() == MbProgress::BUSY {}\n        macmiidr.read().md()\n    }\n\n    fn smi_write(&mut self, phy_addr: u8, reg: u8, val: u16) {\n        let (macmiiar, macmiidr) = T::regs();\n\n        macmiidr.write(|w| w.set_md(val));\n        macmiiar.modify(|w| {\n            w.set_pa(phy_addr);\n            w.set_mr(reg);\n            w.set_mw(Mw::WRITE); // write\n            w.set_cr(self.clock_range);\n            w.set_mb(MbProgress::BUSY);\n        });\n        while macmiiar.read().mb() == MbProgress::BUSY {}\n    }\n}\n"
  },
  {
    "path": "embassy-stm32/src/eth/sma/v2.rs",
    "content": "use embassy_hal_internal::Peri;\npub(crate) use regs::{Macmdioar as AddressRegister, Macmdiodr as DataRegister};\nuse stm32_metapac::eth::regs;\n\nuse super::{Instance, StationManagement};\nuse crate::eth::{MDCPin, MDIOPin};\nuse crate::gpio::{AfType, Flex, OutputType, Speed};\n\n/// Station Management Agent.\n///\n/// This peripheral is used for SMI reads and writes to the connected\n/// ethernet PHY/device(s).\npub struct Sma<'d, T: Instance> {\n    _peri: Peri<'d, T>,\n    _pins: [Flex<'d>; 2],\n    clock_range: u8,\n}\n\nimpl<'d, T: Instance> Sma<'d, T> {\n    /// Create a new instance of this peripheral.\n    pub fn new(peri: Peri<'d, T>, mdio: Peri<'d, impl MDIOPin<T>>, mdc: Peri<'d, impl MDCPin<T>>) -> Self {\n        // Enable necessary clocks.\n        critical_section::with(|_| {\n            crate::pac::RCC.ahb1enr().modify(|w| {\n                w.set_ethen(true);\n            })\n        });\n\n        let hclk = unsafe { crate::rcc::get_freqs().hclk1.to_hertz() };\n        let hclk = unwrap!(hclk, \"SMA requires HCLK to be enabled, but it was not.\");\n        let hclk_mhz = hclk.0 / 1_000_000;\n\n        // Set the MDC clock frequency in the range 1MHz - 2.5MHz\n        let clock_range = match hclk_mhz {\n            0..=34 => 2,    // Divide by 16\n            35..=59 => 3,   // Divide by 26\n            60..=99 => 0,   // Divide by 42\n            100..=149 => 1, // Divide by 62\n            150..=249 => 4, // Divide by 102\n            250..=310 => 5, // Divide by 124\n            _ => {\n                panic!(\"HCLK results in MDC clock > 2.5MHz even for the highest CSR clock divider\")\n            }\n        };\n\n        Self {\n            _peri: peri,\n            clock_range,\n            _pins: [\n                new_pin!(mdio, AfType::output(OutputType::PushPull, Speed::VeryHigh)).unwrap(),\n                new_pin!(mdc, AfType::output(OutputType::PushPull, Speed::VeryHigh)).unwrap(),\n            ],\n        }\n    }\n}\n\nimpl<T: Instance> StationManagement for Sma<'_, T> {\n    fn smi_read(&mut self, phy_addr: u8, reg: u8) -> u16 {\n        let (macmdioar, macmdiodr) = T::regs();\n\n        macmdioar.modify(|w| {\n            w.set_pa(phy_addr);\n            w.set_rda(reg);\n            w.set_goc(0b11); // read\n            w.set_cr(self.clock_range);\n            w.set_mb(true);\n        });\n\n        while macmdioar.read().mb() {}\n\n        macmdiodr.read().md()\n    }\n\n    fn smi_write(&mut self, phy_addr: u8, reg: u8, val: u16) {\n        let (macmdioar, macmdiodr) = T::regs();\n\n        macmdiodr.write(|w| w.set_md(val));\n        macmdioar.modify(|w| {\n            w.set_pa(phy_addr);\n            w.set_rda(reg);\n            w.set_goc(0b01); // write\n            w.set_cr(self.clock_range);\n            w.set_mb(true);\n        });\n\n        while macmdioar.read().mb() {}\n    }\n}\n"
  },
  {
    "path": "embassy-stm32/src/eth/v1/mod.rs",
    "content": "// The v1c ethernet driver was ported to embassy from the awesome stm32-eth project (https://github.com/stm32-rs/stm32-eth).\n\nmod rx_desc;\nmod tx_desc;\n\nuse core::sync::atomic::{Ordering, fence};\n\nuse embassy_hal_internal::Peri;\nuse stm32_metapac::eth::vals::{Apcs, Dm, DmaomrSr, Fes, Ftf, Ifg, Pbl, Rsf, St, Tsf};\n\npub(crate) use self::rx_desc::{RDes, RDesRing};\npub(crate) use self::tx_desc::{TDes, TDesRing};\nuse super::*;\n#[cfg(eth_v1a)]\nuse crate::gpio::Pull;\nuse crate::gpio::{AfType, Flex, OutputType, Speed};\nuse crate::interrupt;\nuse crate::interrupt::InterruptExt;\n#[cfg(eth_v1a)]\nuse crate::pac::AFIO;\n#[cfg(any(eth_v1b, eth_v1c))]\nuse crate::pac::SYSCFG;\nuse crate::pac::{ETH, RCC};\n\n/// Interrupt handler.\npub struct InterruptHandler {}\n\nimpl interrupt::typelevel::Handler<interrupt::typelevel::ETH> for InterruptHandler {\n    unsafe fn on_interrupt() {\n        WAKER.wake();\n\n        // TODO: Check and clear more flags\n        let dma = ETH.ethernet_dma();\n\n        dma.dmasr().modify(|w| {\n            w.set_ts(true);\n            w.set_rs(true);\n            w.set_nis(true);\n        });\n        // Delay two peripheral's clock\n        dma.dmasr().read();\n        dma.dmasr().read();\n    }\n}\n\n/// Ethernet driver.\npub struct Ethernet<'d, T: Instance, P: Phy> {\n    _peri: Peri<'d, T>,\n    pub(crate) tx: TDesRing<'d>,\n    pub(crate) rx: RDesRing<'d>,\n\n    _pins: Pins<'d>,\n    pub(crate) phy: P,\n    pub(crate) mac_addr: [u8; 6],\n}\n\n/// Pins of ethernet driver.\nenum Pins<'d> {\n    #[allow(unused)]\n    Rmii([Flex<'d>; 7]),\n    #[allow(unused)]\n    Mii([Flex<'d>; 12]),\n}\n\n#[cfg(eth_v1a)]\nmacro_rules! config_in_pins {\n    ($($pin:ident),*) => {\n        critical_section::with(|_| {\n            $(\n                // TODO properly create a set_as_input function\n                set_as_af!($pin, AfType::input(Pull::None));\n            )*\n        })\n    }\n}\n\n#[cfg(eth_v1a)]\nmacro_rules! config_af_pins {\n    ($($pin:ident),*) => {\n        critical_section::with(|_| {\n            $(\n                set_as_af!($pin, AfType::output(OutputType::PushPull, Speed::VeryHigh));\n            )*\n        })\n    };\n}\n\n#[cfg(any(eth_v1b, eth_v1c))]\nmacro_rules! config_pins {\n    ($($pin:ident),*) => {\n        critical_section::with(|_| {\n            $(\n                set_as_af!($pin, AfType::output(OutputType::PushPull, Speed::VeryHigh));\n            )*\n        })\n    };\n}\n\nimpl<'d, T: Instance, SMA: sma::Instance> Ethernet<'d, T, GenericPhy<Sma<'d, SMA>>> {\n    /// Create a new RMII ethernet driver using 7 pins.\n    ///\n    /// This function uses a [`GenericPhy::new_auto`] as PHY, created using the\n    /// provided [`SMA`](sma::Instance), and MDIO and MDC pins.\n    ///\n    /// See [`Ethernet::new_with_phy`] for creating an RMII ethernet\n    /// river with a non-standard PHY.\n    ///\n    /// safety: the returned instance is not leak-safe\n    pub fn new<const TX: usize, const RX: usize, #[cfg(afio)] A>(\n        queue: &'d mut PacketQueue<TX, RX>,\n        peri: Peri<'d, T>,\n        irq: impl interrupt::typelevel::Binding<interrupt::typelevel::ETH, InterruptHandler> + 'd,\n        ref_clk: Peri<'d, if_afio!(impl RefClkPin<T, A>)>,\n        crs: Peri<'d, if_afio!(impl CRSPin<T, A>)>,\n        rx_d0: Peri<'d, if_afio!(impl RXD0Pin<T, A>)>,\n        rx_d1: Peri<'d, if_afio!(impl RXD1Pin<T, A>)>,\n        tx_d0: Peri<'d, if_afio!(impl TXD0Pin<T, A>)>,\n        tx_d1: Peri<'d, if_afio!(impl TXD1Pin<T, A>)>,\n        tx_en: Peri<'d, if_afio!(impl TXEnPin<T, A>)>,\n        mac_addr: [u8; 6],\n        sma: Peri<'d, SMA>,\n        mdio: Peri<'d, if_afio!(impl MDIOPin<SMA, A>)>,\n        mdc: Peri<'d, if_afio!(impl MDCPin<SMA, A>)>,\n    ) -> Self {\n        let sma = Sma::new(sma, mdio, mdc);\n        let phy = GenericPhy::new_auto(sma);\n\n        Self::new_with_phy(\n            queue, peri, irq, ref_clk, crs, rx_d0, rx_d1, tx_d0, tx_d1, tx_en, mac_addr, phy,\n        )\n    }\n\n    /// Create a new MII ethernet driver using 14 pins.\n    ///\n    /// This function uses a [`GenericPhy::new_auto`] as PHY, created using the\n    /// provided [`SMA`](sma::Instance), and MDIO and MDC pins.\n    ///\n    /// See [`Ethernet::new_mii_with_phy`] for creating an RMII ethernet\n    /// river with a non-standard PHY.\n    pub fn new_mii<const TX: usize, const RX: usize, #[cfg(afio)] A>(\n        queue: &'d mut PacketQueue<TX, RX>,\n        peri: Peri<'d, T>,\n        irq: impl interrupt::typelevel::Binding<interrupt::typelevel::ETH, InterruptHandler> + 'd,\n        rx_clk: Peri<'d, if_afio!(impl RXClkPin<T, A>)>,\n        tx_clk: Peri<'d, if_afio!(impl TXClkPin<T, A>)>,\n        rxdv: Peri<'d, if_afio!(impl RXDVPin<T, A>)>,\n        rx_d0: Peri<'d, if_afio!(impl RXD0Pin<T, A>)>,\n        rx_d1: Peri<'d, if_afio!(impl RXD1Pin<T, A>)>,\n        rx_d2: Peri<'d, if_afio!(impl RXD2Pin<T, A>)>,\n        rx_d3: Peri<'d, if_afio!(impl RXD3Pin<T, A>)>,\n        tx_d0: Peri<'d, if_afio!(impl TXD0Pin<T, A>)>,\n        tx_d1: Peri<'d, if_afio!(impl TXD1Pin<T, A>)>,\n        tx_d2: Peri<'d, if_afio!(impl TXD2Pin<T, A>)>,\n        tx_d3: Peri<'d, if_afio!(impl TXD3Pin<T, A>)>,\n        tx_en: Peri<'d, if_afio!(impl TXEnPin<T, A>)>,\n        mac_addr: [u8; 6],\n        sma: Peri<'d, SMA>,\n        mdio: Peri<'d, if_afio!(impl MDIOPin<SMA, A>)>,\n        mdc: Peri<'d, if_afio!(impl MDCPin<SMA, A>)>,\n    ) -> Self {\n        let sma = Sma::new(sma, mdio, mdc);\n        let phy = GenericPhy::new_auto(sma);\n\n        Self::new_mii_with_phy(\n            queue, peri, irq, rx_clk, tx_clk, rxdv, rx_d0, rx_d1, rx_d2, rx_d3, tx_d0, tx_d1, tx_d2, tx_d3, tx_en,\n            mac_addr, phy,\n        )\n    }\n}\n\nimpl<'d, T: Instance, P: Phy> Ethernet<'d, T, P> {\n    /// safety: the returned instance is not leak-safe\n    pub fn new_with_phy<const TX: usize, const RX: usize, #[cfg(afio)] A>(\n        queue: &'d mut PacketQueue<TX, RX>,\n        peri: Peri<'d, T>,\n        irq: impl interrupt::typelevel::Binding<interrupt::typelevel::ETH, InterruptHandler> + 'd,\n        ref_clk: Peri<'d, if_afio!(impl RefClkPin<T, A>)>,\n        crs: Peri<'d, if_afio!(impl CRSPin<T, A>)>,\n        rx_d0: Peri<'d, if_afio!(impl RXD0Pin<T, A>)>,\n        rx_d1: Peri<'d, if_afio!(impl RXD1Pin<T, A>)>,\n        tx_d0: Peri<'d, if_afio!(impl TXD0Pin<T, A>)>,\n        tx_d1: Peri<'d, if_afio!(impl TXD1Pin<T, A>)>,\n        tx_en: Peri<'d, if_afio!(impl TXEnPin<T, A>)>,\n        mac_addr: [u8; 6],\n        phy: P,\n    ) -> Self {\n        #[cfg(eth_v1a)]\n        {\n            config_in_pins!(ref_clk, rx_d0, rx_d1);\n            config_af_pins!(tx_d0, tx_d1, tx_en);\n        }\n\n        #[cfg(any(eth_v1b, eth_v1c))]\n        config_pins!(ref_clk, crs, rx_d0, rx_d1, tx_d0, tx_d1, tx_en);\n\n        let pins = Pins::Rmii([\n            Flex::new(ref_clk),\n            Flex::new(crs),\n            Flex::new(rx_d0),\n            Flex::new(rx_d1),\n            Flex::new(tx_d0),\n            Flex::new(tx_d1),\n            Flex::new(tx_en),\n        ]);\n\n        Self::new_inner(queue, peri, irq, pins, phy, mac_addr, true)\n    }\n\n    fn new_inner<const TX: usize, const RX: usize>(\n        queue: &'d mut PacketQueue<TX, RX>,\n        peri: Peri<'d, T>,\n        _irq: impl interrupt::typelevel::Binding<interrupt::typelevel::ETH, InterruptHandler> + 'd,\n        pins: Pins<'d>,\n        phy: P,\n        mac_addr: [u8; 6],\n        rmii_mii_sel: bool,\n    ) -> Self {\n        // Enable the necessary Clocks\n        #[cfg(eth_v1a)]\n        critical_section::with(|_| {\n            RCC.apb2enr().modify(|w| w.set_afioen(true));\n\n            // Select (R)MII (Reduced Media Independent Interface)\n            // Must be done prior to enabling peripheral clock\n            AFIO.mapr().modify(|w| {\n                w.set_mii_rmii_sel(rmii_mii_sel);\n                w.set_swj_cfg(crate::pac::afio::vals::SwjCfg::NO_OP);\n            });\n\n            RCC.ahbenr().modify(|w| {\n                w.set_ethen(true);\n                w.set_ethtxen(true);\n                w.set_ethrxen(true);\n            });\n        });\n\n        #[cfg(any(eth_v1b, eth_v1c))]\n        critical_section::with(|_| {\n            RCC.ahb1enr().modify(|w| {\n                w.set_ethen(true);\n                w.set_ethtxen(true);\n                w.set_ethrxen(true);\n            });\n\n            // (R)MII ((Reduced) Media Independent Interface)\n            SYSCFG.pmc().modify(|w| w.set_mii_rmii_sel(rmii_mii_sel));\n        });\n\n        let dma = T::regs().ethernet_dma();\n        let mac = T::regs().ethernet_mac();\n\n        // Reset and wait\n        dma.dmabmr().modify(|w| w.set_sr(true));\n        while dma.dmabmr().read().sr() {}\n\n        mac.maccr().modify(|w| {\n            w.set_ifg(Ifg::IFG96); // inter frame gap 96 bit times\n            w.set_apcs(Apcs::STRIP); // automatic padding and crc stripping\n            w.set_fes(Fes::FES100); // fast ethernet speed\n            w.set_dm(Dm::FULL_DUPLEX); // full duplex\n            // TODO: Carrier sense ? ECRSFD\n        });\n\n        // Set the mac to pass all multicast packets\n        mac.macffr().modify(|w| {\n            w.set_pam(true);\n        });\n\n        // Note: Writing to LR triggers synchronisation of both LR and HR into the MAC core,\n        // so the LR write must happen after the HR write.\n        mac.maca0hr()\n            .modify(|w| w.set_maca0h(u16::from(mac_addr[4]) | (u16::from(mac_addr[5]) << 8)));\n        mac.maca0lr().write(|w| {\n            w.set_maca0l(\n                u32::from(mac_addr[0])\n                    | (u32::from(mac_addr[1]) << 8)\n                    | (u32::from(mac_addr[2]) << 16)\n                    | (u32::from(mac_addr[3]) << 24),\n            )\n        });\n\n        // pause time\n        mac.macfcr().modify(|w| w.set_pt(0x100));\n\n        // Transfer and Forward, Receive and Forward\n        dma.dmaomr().modify(|w| {\n            w.set_tsf(Tsf::STORE_FORWARD);\n            w.set_rsf(Rsf::STORE_FORWARD);\n        });\n\n        dma.dmabmr().modify(|w| {\n            w.set_pbl(Pbl::PBL32) // programmable burst length - 32 ?\n        });\n\n        // TODO MTU size setting not found for v1 ethernet, check if correct\n\n        let mut this = Self {\n            _peri: peri,\n            _pins: pins,\n            phy: phy,\n            mac_addr,\n            tx: TDesRing::new(&mut queue.tx_desc, &mut queue.tx_buf),\n            rx: RDesRing::new(&mut queue.rx_desc, &mut queue.rx_buf),\n        };\n\n        fence(Ordering::SeqCst);\n\n        let mac = T::regs().ethernet_mac();\n        let dma = T::regs().ethernet_dma();\n\n        mac.maccr().modify(|w| {\n            w.set_re(true);\n            w.set_te(true);\n        });\n        dma.dmaomr().modify(|w| {\n            w.set_ftf(Ftf::FLUSH); // flush transmit fifo (queue)\n            w.set_st(St::STARTED); // start transmitting channel\n            w.set_sr(DmaomrSr::STARTED); // start receiving channel\n        });\n\n        this.rx.demand_poll();\n\n        // Enable interrupts\n        dma.dmaier().modify(|w| {\n            w.set_nise(true);\n            w.set_rie(true);\n            w.set_tie(true);\n        });\n\n        this.phy.phy_reset();\n        this.phy.phy_init();\n\n        interrupt::ETH.unpend();\n        unsafe { interrupt::ETH.enable() };\n\n        this\n    }\n\n    /// Create a new MII ethernet driver using 12 pins.\n    pub fn new_mii_with_phy<const TX: usize, const RX: usize, #[cfg(afio)] A>(\n        queue: &'d mut PacketQueue<TX, RX>,\n        peri: Peri<'d, T>,\n        irq: impl interrupt::typelevel::Binding<interrupt::typelevel::ETH, InterruptHandler> + 'd,\n        rx_clk: Peri<'d, if_afio!(impl RXClkPin<T, A>)>,\n        tx_clk: Peri<'d, if_afio!(impl TXClkPin<T, A>)>,\n        rxdv: Peri<'d, if_afio!(impl RXDVPin<T, A>)>,\n        rx_d0: Peri<'d, if_afio!(impl RXD0Pin<T, A>)>,\n        rx_d1: Peri<'d, if_afio!(impl RXD1Pin<T, A>)>,\n        rx_d2: Peri<'d, if_afio!(impl RXD2Pin<T, A>)>,\n        rx_d3: Peri<'d, if_afio!(impl RXD3Pin<T, A>)>,\n        tx_d0: Peri<'d, if_afio!(impl TXD0Pin<T, A>)>,\n        tx_d1: Peri<'d, if_afio!(impl TXD1Pin<T, A>)>,\n        tx_d2: Peri<'d, if_afio!(impl TXD2Pin<T, A>)>,\n        tx_d3: Peri<'d, if_afio!(impl TXD3Pin<T, A>)>,\n        tx_en: Peri<'d, if_afio!(impl TXEnPin<T, A>)>,\n        mac_addr: [u8; 6],\n        phy: P,\n    ) -> Self {\n        #[cfg(eth_v1a)]\n        {\n            config_in_pins!(rx_clk, tx_clk, rx_d0, rx_d1, rx_d2, rx_d3, rxdv);\n            config_af_pins!(tx_d0, tx_d1, tx_d2, tx_d3, tx_en);\n        }\n\n        #[cfg(any(eth_v1b, eth_v1c))]\n        config_pins!(\n            rx_clk, tx_clk, rxdv, rx_d0, rx_d1, rx_d2, rx_d3, tx_d0, tx_d1, tx_d2, tx_d3, tx_en\n        );\n\n        let pins = Pins::Mii([\n            Flex::new(rx_clk),\n            Flex::new(tx_clk),\n            Flex::new(rxdv),\n            Flex::new(rx_d0),\n            Flex::new(rx_d1),\n            Flex::new(rx_d2),\n            Flex::new(rx_d3),\n            Flex::new(tx_d0),\n            Flex::new(tx_d1),\n            Flex::new(tx_d2),\n            Flex::new(tx_d3),\n            Flex::new(tx_en),\n        ]);\n\n        Self::new_inner(queue, peri, irq, pins, phy, mac_addr, false)\n    }\n}\n\nimpl<'d, T: Instance, P: Phy> Drop for Ethernet<'d, T, P> {\n    fn drop(&mut self) {\n        let dma = T::regs().ethernet_dma();\n        let mac = T::regs().ethernet_mac();\n\n        // Disable the TX DMA and wait for any previous transmissions to be completed\n        dma.dmaomr().modify(|w| w.set_st(St::STOPPED));\n\n        // Disable MAC transmitter and receiver\n        mac.maccr().modify(|w| {\n            w.set_re(false);\n            w.set_te(false);\n        });\n\n        dma.dmaomr().modify(|w| w.set_sr(DmaomrSr::STOPPED));\n    }\n}\n"
  },
  {
    "path": "embassy-stm32/src/eth/v1/rx_desc.rs",
    "content": "use core::sync::atomic::{Ordering, compiler_fence, fence};\n\nuse stm32_metapac::eth::vals::{Rpd, Rps};\nuse vcell::VolatileCell;\n\nuse crate::eth::RX_BUFFER_SIZE;\nuse crate::pac::ETH;\n\nmod rx_consts {\n    /// Owned by DMA engine\n    pub const RXDESC_0_OWN: u32 = 1 << 31;\n    /// First descriptor\n    pub const RXDESC_0_FS: u32 = 1 << 9;\n    /// Last descriptor\n    pub const RXDESC_0_LS: u32 = 1 << 8;\n    /// Error summary\n    pub const RXDESC_0_ES: u32 = 1 << 15;\n    /// Frame length\n    pub const RXDESC_0_FL_MASK: u32 = 0x3FFF;\n    pub const RXDESC_0_FL_SHIFT: usize = 16;\n\n    pub const RXDESC_1_RBS_MASK: u32 = 0x0fff;\n    /// Second address chained\n    pub const RXDESC_1_RCH: u32 = 1 << 14;\n    /// End Of Ring\n    pub const RXDESC_1_RER: u32 = 1 << 15;\n}\n\nuse rx_consts::*;\n\nuse super::Packet;\n\n/// Receive Descriptor representation\n///\n/// * rdes0: OWN and Status\n/// * rdes1: allocated buffer length\n/// * rdes2: data buffer address\n/// * rdes3: next descriptor address\n#[repr(C)]\npub(crate) struct RDes {\n    rdes0: VolatileCell<u32>,\n    rdes1: VolatileCell<u32>,\n    rdes2: VolatileCell<u32>,\n    rdes3: VolatileCell<u32>,\n}\n\nimpl RDes {\n    pub const fn new() -> Self {\n        Self {\n            rdes0: VolatileCell::new(0),\n            rdes1: VolatileCell::new(0),\n            rdes2: VolatileCell::new(0),\n            rdes3: VolatileCell::new(0),\n        }\n    }\n\n    /// Return true if this RDes is acceptable to us\n    #[inline(always)]\n    fn valid(&self) -> bool {\n        // Write-back descriptor is valid if:\n        //\n        // Contains first buffer of packet AND contains last buf of\n        // packet AND no errors\n        (self.rdes0.get() & (RXDESC_0_ES | RXDESC_0_FS | RXDESC_0_LS)) == (RXDESC_0_FS | RXDESC_0_LS)\n    }\n\n    /// Return true if this RDes is not currently owned by the DMA\n    #[inline(always)]\n    fn available(&self) -> bool {\n        self.rdes0.get() & RXDESC_0_OWN == 0 // Owned by us\n    }\n\n    /// Configures the reception buffer address and length and passed descriptor ownership to the DMA\n    #[inline(always)]\n    fn set_ready(&self, buf: *mut u8) {\n        self.rdes1\n            .set(self.rdes1.get() | (RX_BUFFER_SIZE as u32) & RXDESC_1_RBS_MASK);\n        self.rdes2.set(buf as u32);\n\n        // \"Preceding reads and writes cannot be moved past subsequent writes.\"\n        fence(Ordering::Release);\n\n        compiler_fence(Ordering::Release);\n\n        self.rdes0.set(self.rdes0.get() | RXDESC_0_OWN);\n\n        // Used to flush the store buffer as fast as possible to make the buffer available for the\n        // DMA.\n        fence(Ordering::SeqCst);\n    }\n\n    // points to next descriptor (RCH)\n    #[inline(always)]\n    fn set_buffer2(&self, buffer: *const u8) {\n        self.rdes3.set(buffer as u32);\n    }\n\n    #[inline(always)]\n    fn set_end_of_ring(&self) {\n        self.rdes1.set(self.rdes1.get() | RXDESC_1_RER);\n    }\n\n    #[inline(always)]\n    fn packet_len(&self) -> usize {\n        ((self.rdes0.get() >> RXDESC_0_FL_SHIFT) & RXDESC_0_FL_MASK) as usize\n    }\n\n    fn setup(&self, next: Option<&Self>, buf: *mut u8) {\n        // Defer this initialization to this function, so we can have `RingEntry` on bss.\n        self.rdes1.set(self.rdes1.get() | RXDESC_1_RCH);\n\n        match next {\n            Some(next) => self.set_buffer2(next as *const _ as *const u8),\n            None => {\n                self.set_buffer2(0 as *const u8);\n                self.set_end_of_ring();\n            }\n        }\n\n        self.set_ready(buf);\n    }\n}\n\n/// Running state of the `RxRing`\n#[derive(PartialEq, Eq, Debug)]\npub enum RunningState {\n    Unknown,\n    Stopped,\n    Running,\n}\n\n/// Rx ring of descriptors and packets\npub(crate) struct RDesRing<'a> {\n    descriptors: &'a mut [RDes],\n    buffers: &'a mut [Packet<RX_BUFFER_SIZE>],\n    index: usize,\n}\n\nimpl<'a> RDesRing<'a> {\n    pub(crate) fn new(descriptors: &'a mut [RDes], buffers: &'a mut [Packet<RX_BUFFER_SIZE>]) -> Self {\n        assert!(descriptors.len() > 1);\n        assert!(descriptors.len() == buffers.len());\n\n        for (i, entry) in descriptors.iter().enumerate() {\n            entry.setup(descriptors.get(i + 1), buffers[i].0.as_mut_ptr());\n        }\n\n        // Register rx descriptor start\n        ETH.ethernet_dma()\n            .dmardlar()\n            .write(|w| w.0 = descriptors.as_ptr() as u32);\n        // We already have fences in `set_owned`, which is called in `setup`\n\n        Self {\n            descriptors,\n            buffers,\n            index: 0,\n        }\n    }\n\n    pub(crate) fn demand_poll(&self) {\n        ETH.ethernet_dma().dmarpdr().write(|w| w.set_rpd(Rpd::POLL));\n    }\n\n    /// Get current `RunningState`\n    fn running_state(&self) -> RunningState {\n        match ETH.ethernet_dma().dmasr().read().rps() {\n            //  Reset or Stop Receive Command issued\n            Rps::STOPPED => RunningState::Stopped,\n            //  Fetching receive transfer descriptor\n            Rps::RUNNING_FETCHING => RunningState::Running,\n            //  Waiting for receive packet\n            Rps::RUNNING_WAITING => RunningState::Running,\n            //  Receive descriptor unavailable\n            Rps::SUSPENDED => RunningState::Stopped,\n            //  Closing receive descriptor\n            Rps::_RESERVED_5 => RunningState::Running,\n            //  Transferring the receive packet data from receive buffer to host memory\n            Rps::RUNNING_WRITING => RunningState::Running,\n            _ => RunningState::Unknown,\n        }\n    }\n\n    /// Get a received packet if any, or None.\n    pub(crate) fn available(&mut self) -> Option<&mut [u8]> {\n        if self.running_state() != RunningState::Running {\n            self.demand_poll();\n        }\n\n        // Not sure if the contents of the write buffer on the M7 can affects reads, so we are using\n        // a DMB here just in case, it also serves as a hint to the compiler that we're syncing the\n        // buffer (I think .-.)\n        fence(Ordering::SeqCst);\n\n        // We might have to process many packets, in case some have been rx'd but are invalid.\n        loop {\n            let descriptor = &mut self.descriptors[self.index];\n            if !descriptor.available() {\n                return None;\n            }\n\n            // If packet is invalid, pop it and try again.\n            if !descriptor.valid() {\n                warn!(\"invalid packet: {:08x}\", descriptor.rdes0.get());\n                self.pop_packet();\n                continue;\n            }\n\n            break;\n        }\n\n        let descriptor = &mut self.descriptors[self.index];\n        let len = descriptor.packet_len();\n        return Some(&mut self.buffers[self.index].0[..len]);\n    }\n\n    /// Pop the packet previously returned by `available`.\n    pub(crate) fn pop_packet(&mut self) {\n        let descriptor = &mut self.descriptors[self.index];\n        assert!(descriptor.available());\n\n        self.descriptors[self.index].set_ready(self.buffers[self.index].0.as_mut_ptr());\n\n        self.demand_poll();\n\n        // Increment index.\n        self.index += 1;\n        if self.index == self.descriptors.len() {\n            self.index = 0\n        }\n    }\n}\n"
  },
  {
    "path": "embassy-stm32/src/eth/v1/tx_desc.rs",
    "content": "use core::sync::atomic::{Ordering, compiler_fence, fence};\n\nuse vcell::VolatileCell;\n\nuse crate::eth::TX_BUFFER_SIZE;\nuse crate::pac::ETH;\n\n/// Transmit and Receive Descriptor fields\n#[allow(dead_code)]\nmod tx_consts {\n    pub const TXDESC_0_OWN: u32 = 1 << 31;\n    pub const TXDESC_0_IOC: u32 = 1 << 30;\n    // First segment of frame\n    pub const TXDESC_0_FS: u32 = 1 << 28;\n    // Last segment of frame\n    pub const TXDESC_0_LS: u32 = 1 << 29;\n    // Transmit end of ring\n    pub const TXDESC_0_TER: u32 = 1 << 21;\n    // Second address chained\n    pub const TXDESC_0_TCH: u32 = 1 << 20;\n    // Error status\n    pub const TXDESC_0_ES: u32 = 1 << 15;\n\n    // Transmit buffer size\n    pub const TXDESC_1_TBS_SHIFT: usize = 0;\n    pub const TXDESC_1_TBS_MASK: u32 = 0x0fff << TXDESC_1_TBS_SHIFT;\n}\nuse tx_consts::*;\n\nuse super::Packet;\n\n/// Transmit Descriptor representation\n///\n/// * tdes0: control\n/// * tdes1: buffer lengths\n/// * tdes2: data buffer address\n/// * tdes3: next descriptor address\n#[repr(C)]\npub(crate) struct TDes {\n    tdes0: VolatileCell<u32>,\n    tdes1: VolatileCell<u32>,\n    tdes2: VolatileCell<u32>,\n    tdes3: VolatileCell<u32>,\n}\n\nimpl TDes {\n    pub const fn new() -> Self {\n        Self {\n            tdes0: VolatileCell::new(0),\n            tdes1: VolatileCell::new(0),\n            tdes2: VolatileCell::new(0),\n            tdes3: VolatileCell::new(0),\n        }\n    }\n\n    /// Return true if this TDes is not currently owned by the DMA\n    fn available(&self) -> bool {\n        (self.tdes0.get() & TXDESC_0_OWN) == 0\n    }\n\n    /// Pass ownership to the DMA engine\n    fn set_owned(&mut self) {\n        // \"Preceding reads and writes cannot be moved past subsequent writes.\"\n        fence(Ordering::Release);\n\n        compiler_fence(Ordering::Release);\n        self.tdes0.set(self.tdes0.get() | TXDESC_0_OWN);\n\n        // Used to flush the store buffer as fast as possible to make the buffer available for the\n        // DMA.\n        fence(Ordering::SeqCst);\n    }\n\n    fn set_buffer1(&self, buffer: *const u8) {\n        self.tdes2.set(buffer as u32);\n    }\n\n    fn set_buffer1_len(&self, len: usize) {\n        self.tdes1\n            .set((self.tdes1.get() & !TXDESC_1_TBS_MASK) | ((len as u32) << TXDESC_1_TBS_SHIFT));\n    }\n\n    // points to next descriptor (RCH)\n    fn set_buffer2(&self, buffer: *const u8) {\n        self.tdes3.set(buffer as u32);\n    }\n\n    fn set_end_of_ring(&self) {\n        self.tdes0.set(self.tdes0.get() | TXDESC_0_TER);\n    }\n\n    // set up as a part fo the ring buffer - configures the tdes\n    fn setup(&self, next: Option<&Self>) {\n        // Defer this initialization to this function, so we can have `RingEntry` on bss.\n        self.tdes0.set(TXDESC_0_TCH | TXDESC_0_IOC | TXDESC_0_FS | TXDESC_0_LS);\n        match next {\n            Some(next) => self.set_buffer2(next as *const TDes as *const u8),\n            None => {\n                self.set_buffer2(0 as *const u8);\n                self.set_end_of_ring();\n            }\n        }\n    }\n}\n\npub(crate) struct TDesRing<'a> {\n    descriptors: &'a mut [TDes],\n    buffers: &'a mut [Packet<TX_BUFFER_SIZE>],\n    index: usize,\n}\n\nimpl<'a> TDesRing<'a> {\n    /// Initialise this TDesRing. Assume TDesRing is corrupt\n    pub(crate) fn new(descriptors: &'a mut [TDes], buffers: &'a mut [Packet<TX_BUFFER_SIZE>]) -> Self {\n        assert!(descriptors.len() > 0);\n        assert!(descriptors.len() == buffers.len());\n\n        for (i, entry) in descriptors.iter().enumerate() {\n            entry.setup(descriptors.get(i + 1));\n        }\n\n        // Register txdescriptor start\n        ETH.ethernet_dma()\n            .dmatdlar()\n            .write(|w| w.0 = descriptors.as_ptr() as u32);\n\n        Self {\n            descriptors,\n            buffers,\n            index: 0,\n        }\n    }\n\n    pub(crate) fn len(&self) -> usize {\n        self.descriptors.len()\n    }\n\n    /// Return the next available packet buffer for transmitting, or None\n    pub(crate) fn available(&mut self) -> Option<&mut [u8]> {\n        let descriptor = &mut self.descriptors[self.index];\n        if descriptor.available() {\n            Some(&mut self.buffers[self.index].0)\n        } else {\n            None\n        }\n    }\n\n    /// Transmit the packet written in a buffer returned by `available`.\n    pub(crate) fn transmit(&mut self, len: usize) {\n        let descriptor = &mut self.descriptors[self.index];\n        assert!(descriptor.available());\n\n        descriptor.set_buffer1(self.buffers[self.index].0.as_ptr());\n        descriptor.set_buffer1_len(len);\n\n        descriptor.set_owned();\n\n        // Ensure changes to the descriptor are committed before DMA engine sees tail pointer store.\n        // This will generate an DMB instruction.\n        // \"Preceding reads and writes cannot be moved past subsequent writes.\"\n        fence(Ordering::Release);\n\n        // Move the index to the next descriptor\n        self.index += 1;\n        if self.index == self.descriptors.len() {\n            self.index = 0\n        }\n        // Request the DMA engine to poll the latest tx descriptor\n        ETH.ethernet_dma().dmatpdr().modify(|w| w.0 = 1)\n    }\n}\n"
  },
  {
    "path": "embassy-stm32/src/eth/v2/descriptors.rs",
    "content": "use core::sync::atomic::{Ordering, fence};\n\nuse vcell::VolatileCell;\n\nuse crate::eth::{Packet, RX_BUFFER_SIZE, TX_BUFFER_SIZE};\nuse crate::pac::ETH;\n\n/// Transmit and Receive Descriptor fields\n#[allow(dead_code)]\nmod emac_consts {\n    pub const EMAC_DES3_OWN: u32 = 0x8000_0000;\n    pub const EMAC_DES3_CTXT: u32 = 0x4000_0000;\n    pub const EMAC_DES3_FD: u32 = 0x2000_0000;\n    pub const EMAC_DES3_LD: u32 = 0x1000_0000;\n    pub const EMAC_DES3_ES: u32 = 0x0000_8000;\n    pub const EMAC_DES0_BUF1AP: u32 = 0xFFFF_FFFF;\n\n    pub const EMAC_TDES2_IOC: u32 = 0x8000_0000;\n    pub const EMAC_TDES2_B1L: u32 = 0x0000_3FFF;\n\n    pub const EMAC_RDES3_IOC: u32 = 0x4000_0000;\n    pub const EMAC_RDES3_PL: u32 = 0x0000_7FFF;\n    pub const EMAC_RDES3_BUF1V: u32 = 0x0100_0000;\n    pub const EMAC_RDES3_PKTLEN: u32 = 0x0000_7FFF;\n}\nuse emac_consts::*;\n\n/// Transmit Descriptor representation\n///\n/// * tdes0: transmit buffer address\n/// * tdes1:\n/// * tdes2: buffer lengths\n/// * tdes3: control and payload/frame length\n#[repr(C)]\npub(crate) struct TDes {\n    tdes0: VolatileCell<u32>,\n    tdes1: VolatileCell<u32>,\n    tdes2: VolatileCell<u32>,\n    tdes3: VolatileCell<u32>,\n}\n\nimpl TDes {\n    pub const fn new() -> Self {\n        Self {\n            tdes0: VolatileCell::new(0),\n            tdes1: VolatileCell::new(0),\n            tdes2: VolatileCell::new(0),\n            tdes3: VolatileCell::new(0),\n        }\n    }\n\n    /// Return true if this TDes is not currently owned by the DMA\n    fn available(&self) -> bool {\n        self.tdes3.get() & EMAC_DES3_OWN == 0\n    }\n}\n\npub(crate) struct TDesRing<'a> {\n    descriptors: &'a mut [TDes],\n    buffers: &'a mut [Packet<TX_BUFFER_SIZE>],\n    index: usize,\n}\n\nimpl<'a> TDesRing<'a> {\n    /// Initialise this TDesRing. Assume TDesRing is corrupt.\n    pub fn new(descriptors: &'a mut [TDes], buffers: &'a mut [Packet<TX_BUFFER_SIZE>]) -> Self {\n        assert!(descriptors.len() > 0);\n        assert!(descriptors.len() == buffers.len());\n\n        for td in descriptors.iter_mut() {\n            *td = TDes::new();\n        }\n\n        // Initialize the pointers in the DMA engine. (There will be a memory barrier later\n        // before the DMA engine is enabled.)\n        let dma = ETH.ethernet_dma();\n        dma.dmactx_dlar().write(|w| w.0 = descriptors.as_mut_ptr() as u32);\n        dma.dmactx_rlr().write(|w| w.set_tdrl((descriptors.len() as u16) - 1));\n        dma.dmactx_dtpr().write(|w| w.0 = 0);\n\n        Self {\n            descriptors,\n            buffers,\n            index: 0,\n        }\n    }\n\n    pub(crate) fn len(&self) -> usize {\n        self.descriptors.len()\n    }\n\n    /// Return the next available packet buffer for transmitting, or None\n    pub(crate) fn available(&mut self) -> Option<&mut [u8]> {\n        let d = &mut self.descriptors[self.index];\n        if d.available() {\n            Some(&mut self.buffers[self.index].0)\n        } else {\n            None\n        }\n    }\n\n    /// Transmit the packet written in a buffer returned by `available`.\n    pub(crate) fn transmit(&mut self, len: usize) {\n        let td = &mut self.descriptors[self.index];\n        assert!(td.available());\n        assert!(len as u32 <= EMAC_TDES2_B1L);\n\n        // Read format\n        td.tdes0.set(self.buffers[self.index].0.as_ptr() as u32);\n        td.tdes2.set(len as u32 & EMAC_TDES2_B1L | EMAC_TDES2_IOC);\n\n        // FD: Contains first buffer of packet\n        // LD: Contains last buffer of packet\n        // Give the DMA engine ownership\n        td.tdes3.set(EMAC_DES3_FD | EMAC_DES3_LD | EMAC_DES3_OWN);\n\n        // Ensure changes to the descriptor are committed before DMA engine sees tail pointer store.\n        // This will generate an DMB instruction.\n        // \"Preceding reads and writes cannot be moved past subsequent writes.\"\n        fence(Ordering::Release);\n\n        // signal DMA it can try again.\n        // See issue #2129\n        ETH.ethernet_dma().dmactx_dtpr().write(|w| w.0 = &td as *const _ as u32);\n\n        self.index = (self.index + 1) % self.descriptors.len();\n    }\n}\n\n/// Receive Descriptor representation\n///\n/// * rdes0: receive buffer address\n/// * rdes1:\n/// * rdes2:\n/// * rdes3: OWN and Status\n#[repr(C)]\npub(crate) struct RDes {\n    rdes0: VolatileCell<u32>,\n    rdes1: VolatileCell<u32>,\n    rdes2: VolatileCell<u32>,\n    rdes3: VolatileCell<u32>,\n}\n\nimpl RDes {\n    pub const fn new() -> Self {\n        Self {\n            rdes0: VolatileCell::new(0),\n            rdes1: VolatileCell::new(0),\n            rdes2: VolatileCell::new(0),\n            rdes3: VolatileCell::new(0),\n        }\n    }\n\n    /// Return true if this RDes is acceptable to us\n    #[inline(always)]\n    fn valid(&self) -> bool {\n        // Write-back descriptor is valid if:\n        //\n        // Contains first buffer of packet AND contains last buf of\n        // packet AND no errors AND not a context descriptor\n        self.rdes3.get() & (EMAC_DES3_FD | EMAC_DES3_LD | EMAC_DES3_ES | EMAC_DES3_CTXT)\n            == (EMAC_DES3_FD | EMAC_DES3_LD)\n    }\n\n    /// Return true if this RDes is not currently owned by the DMA\n    #[inline(always)]\n    fn available(&self) -> bool {\n        self.rdes3.get() & EMAC_DES3_OWN == 0 // Owned by us\n    }\n\n    #[inline(always)]\n    fn set_ready(&mut self, buf: *mut u8) {\n        self.rdes0.set(buf as u32);\n        self.rdes3.set(EMAC_RDES3_BUF1V | EMAC_RDES3_IOC | EMAC_DES3_OWN);\n    }\n}\n\n/// Rx ring of descriptors and packets\npub(crate) struct RDesRing<'a> {\n    descriptors: &'a mut [RDes],\n    buffers: &'a mut [Packet<RX_BUFFER_SIZE>],\n    index: usize,\n}\n\nimpl<'a> RDesRing<'a> {\n    pub(crate) fn new(descriptors: &'a mut [RDes], buffers: &'a mut [Packet<RX_BUFFER_SIZE>]) -> Self {\n        assert!(descriptors.len() > 1);\n        assert!(descriptors.len() == buffers.len());\n\n        for (i, desc) in descriptors.iter_mut().enumerate() {\n            *desc = RDes::new();\n            desc.set_ready(buffers[i].0.as_mut_ptr());\n        }\n\n        let dma = ETH.ethernet_dma();\n        dma.dmacrx_dlar().write(|w| w.0 = descriptors.as_mut_ptr() as u32);\n        dma.dmacrx_rlr().write(|w| w.set_rdrl((descriptors.len() as u16) - 1));\n        dma.dmacrx_dtpr().write(|w| w.0 = 0);\n\n        Self {\n            descriptors,\n            buffers,\n            index: 0,\n        }\n    }\n\n    /// Get a received packet if any, or None.\n    pub(crate) fn available(&mut self) -> Option<&mut [u8]> {\n        // Not sure if the contents of the write buffer on the M7 can affects reads, so we are using\n        // a DMB here just in case, it also serves as a hint to the compiler that we're syncing the\n        // buffer (I think .-.)\n        fence(Ordering::SeqCst);\n\n        // We might have to process many packets, in case some have been rx'd but are invalid.\n        loop {\n            let descriptor = &mut self.descriptors[self.index];\n            if !descriptor.available() {\n                return None;\n            }\n\n            // If packet is invalid, pop it and try again.\n            if !descriptor.valid() {\n                warn!(\"invalid packet: {:08x}\", descriptor.rdes0.get());\n                self.pop_packet();\n                continue;\n            }\n\n            break;\n        }\n\n        let descriptor = &mut self.descriptors[self.index];\n        let len = (descriptor.rdes3.get() & EMAC_RDES3_PKTLEN) as usize;\n        return Some(&mut self.buffers[self.index].0[..len]);\n    }\n\n    /// Pop the packet previously returned by `available`.\n    pub(crate) fn pop_packet(&mut self) {\n        let rd = &mut self.descriptors[self.index];\n        assert!(rd.available());\n\n        rd.set_ready(self.buffers[self.index].0.as_mut_ptr());\n\n        // \"Preceding reads and writes cannot be moved past subsequent writes.\"\n        fence(Ordering::Release);\n\n        // signal DMA it can try again.\n        // See issue #2129\n        ETH.ethernet_dma().dmacrx_dtpr().write(|w| w.0 = &rd as *const _ as u32);\n\n        // Increment index.\n        self.index = (self.index + 1) % self.descriptors.len();\n    }\n}\n"
  },
  {
    "path": "embassy-stm32/src/eth/v2/mod.rs",
    "content": "mod descriptors;\n\nuse core::sync::atomic::{Ordering, fence};\n\nuse embassy_hal_internal::Peri;\nuse stm32_metapac::syscfg::vals::EthSelPhy;\n\npub(crate) use self::descriptors::{RDes, RDesRing, TDes, TDesRing};\nuse super::*;\nuse crate::gpio::{AfType, Flex, OutputType, Speed};\nuse crate::interrupt;\nuse crate::interrupt::InterruptExt;\nuse crate::pac::ETH;\n\n/// Interrupt handler.\npub struct InterruptHandler {}\n\nimpl interrupt::typelevel::Handler<interrupt::typelevel::ETH> for InterruptHandler {\n    unsafe fn on_interrupt() {\n        WAKER.wake();\n\n        // TODO: Check and clear more flags\n        let dma = ETH.ethernet_dma();\n\n        dma.dmacsr().modify(|w| {\n            w.set_ti(true);\n            w.set_ri(true);\n            w.set_nis(true);\n        });\n        // Delay two peripheral's clock\n        dma.dmacsr().read();\n        dma.dmacsr().read();\n    }\n}\n\n/// Ethernet driver.\npub struct Ethernet<'d, T: Instance, P: Phy> {\n    _peri: Peri<'d, T>,\n    pub(crate) tx: TDesRing<'d>,\n    pub(crate) rx: RDesRing<'d>,\n    _pins: Pins<'d>,\n    pub(crate) phy: P,\n    pub(crate) mac_addr: [u8; 6],\n}\n\n/// Pins of ethernet driver.\nenum Pins<'d> {\n    #[allow(unused)]\n    Rmii([Flex<'d>; 7]),\n    #[allow(unused)]\n    Mii([Flex<'d>; 12]),\n}\n\nmacro_rules! config_pins {\n    ($($pin:ident),*) => {\n        critical_section::with(|_| {\n            $(\n                // TODO: shouldn't some pins be configured as inputs?\n                set_as_af!($pin, AfType::output(OutputType::PushPull, Speed::VeryHigh));\n            )*\n        })\n    };\n}\n\nimpl<'d, T: Instance, SMA: sma::Instance> Ethernet<'d, T, GenericPhy<Sma<'d, SMA>>> {\n    /// Create a new RMII ethernet driver using 7 pins.\n    ///\n    /// This function uses a [`GenericPhy::new_auto`] as PHY, created using the\n    /// provided [`SMA`](sma::Instance), and MDIO and MDC pins.\n    ///\n    /// See [`Ethernet::new_with_phy`] for creating an RMII ethernet\n    /// river with a non-standard PHY.\n    pub fn new<const TX: usize, const RX: usize>(\n        queue: &'d mut PacketQueue<TX, RX>,\n        peri: Peri<'d, T>,\n        irq: impl interrupt::typelevel::Binding<interrupt::typelevel::ETH, InterruptHandler> + 'd,\n        ref_clk: Peri<'d, impl RefClkPin<T>>,\n        crs: Peri<'d, impl CRSPin<T>>,\n        rx_d0: Peri<'d, impl RXD0Pin<T>>,\n        rx_d1: Peri<'d, impl RXD1Pin<T>>,\n        tx_d0: Peri<'d, impl TXD0Pin<T>>,\n        tx_d1: Peri<'d, impl TXD1Pin<T>>,\n        tx_en: Peri<'d, impl TXEnPin<T>>,\n        mac_addr: [u8; 6],\n        sma: Peri<'d, SMA>,\n        mdio: Peri<'d, impl MDIOPin<SMA>>,\n        mdc: Peri<'d, impl MDCPin<SMA>>,\n    ) -> Self {\n        let sma = Sma::new(sma, mdio, mdc);\n        let phy = GenericPhy::new_auto(sma);\n\n        Self::new_with_phy(\n            queue, peri, irq, ref_clk, crs, rx_d0, rx_d1, tx_d0, tx_d1, tx_en, mac_addr, phy,\n        )\n    }\n\n    /// Create a new MII ethernet driver using 14 pins.\n    ///\n    /// This function uses a [`GenericPhy::new_auto`] as PHY, created using the\n    /// provided [`SMA`](sma::Instance), and MDIO and MDC pins.\n    ///\n    /// See [`Ethernet::new_mii_with_phy`] for creating an RMII ethernet\n    /// river with a non-standard PHY.\n    pub fn new_mii<const TX: usize, const RX: usize>(\n        queue: &'d mut PacketQueue<TX, RX>,\n        peri: Peri<'d, T>,\n        irq: impl interrupt::typelevel::Binding<interrupt::typelevel::ETH, InterruptHandler> + 'd,\n        rx_clk: Peri<'d, impl RXClkPin<T>>,\n        tx_clk: Peri<'d, impl TXClkPin<T>>,\n        rxdv: Peri<'d, impl RXDVPin<T>>,\n        rx_d0: Peri<'d, impl RXD0Pin<T>>,\n        rx_d1: Peri<'d, impl RXD1Pin<T>>,\n        rx_d2: Peri<'d, impl RXD2Pin<T>>,\n        rx_d3: Peri<'d, impl RXD3Pin<T>>,\n        tx_d0: Peri<'d, impl TXD0Pin<T>>,\n        tx_d1: Peri<'d, impl TXD1Pin<T>>,\n        tx_d2: Peri<'d, impl TXD2Pin<T>>,\n        tx_d3: Peri<'d, impl TXD3Pin<T>>,\n        tx_en: Peri<'d, impl TXEnPin<T>>,\n        mac_addr: [u8; 6],\n        sma: Peri<'d, SMA>,\n        mdio: Peri<'d, impl MDIOPin<SMA>>,\n        mdc: Peri<'d, impl MDCPin<SMA>>,\n    ) -> Self {\n        let sma = Sma::new(sma, mdio, mdc);\n        let phy = GenericPhy::new_auto(sma);\n\n        Self::new_mii_with_phy(\n            queue, peri, irq, rx_clk, tx_clk, rxdv, rx_d0, rx_d1, rx_d2, rx_d3, tx_d0, tx_d1, tx_d2, tx_d3, tx_en,\n            mac_addr, phy,\n        )\n    }\n}\n\nimpl<'d, T: Instance, P: Phy> Ethernet<'d, T, P> {\n    /// Create a new RMII ethernet driver using 7 pins.\n    pub fn new_with_phy<const TX: usize, const RX: usize>(\n        queue: &'d mut PacketQueue<TX, RX>,\n        peri: Peri<'d, T>,\n        irq: impl interrupt::typelevel::Binding<interrupt::typelevel::ETH, InterruptHandler> + 'd,\n        ref_clk: Peri<'d, impl RefClkPin<T>>,\n        crs: Peri<'d, impl CRSPin<T>>,\n        rx_d0: Peri<'d, impl RXD0Pin<T>>,\n        rx_d1: Peri<'d, impl RXD1Pin<T>>,\n        tx_d0: Peri<'d, impl TXD0Pin<T>>,\n        tx_d1: Peri<'d, impl TXD1Pin<T>>,\n        tx_en: Peri<'d, impl TXEnPin<T>>,\n        mac_addr: [u8; 6],\n        phy: P,\n    ) -> Self {\n        config_pins!(ref_clk, crs, rx_d0, rx_d1, tx_d0, tx_d1, tx_en);\n\n        let pins = Pins::Rmii([\n            Flex::new(ref_clk),\n            Flex::new(crs),\n            Flex::new(rx_d0),\n            Flex::new(rx_d1),\n            Flex::new(tx_d0),\n            Flex::new(tx_d1),\n            Flex::new(tx_en),\n        ]);\n\n        Self::new_inner(queue, peri, irq, pins, phy, mac_addr, EthSelPhy::RMII)\n    }\n\n    /// Create a new MII ethernet driver using 12 pins.\n    pub fn new_mii_with_phy<const TX: usize, const RX: usize>(\n        queue: &'d mut PacketQueue<TX, RX>,\n        peri: Peri<'d, T>,\n        irq: impl interrupt::typelevel::Binding<interrupt::typelevel::ETH, InterruptHandler> + 'd,\n        rx_clk: Peri<'d, impl RXClkPin<T>>,\n        tx_clk: Peri<'d, impl TXClkPin<T>>,\n        rxdv: Peri<'d, impl RXDVPin<T>>,\n        rx_d0: Peri<'d, impl RXD0Pin<T>>,\n        rx_d1: Peri<'d, impl RXD1Pin<T>>,\n        rx_d2: Peri<'d, impl RXD2Pin<T>>,\n        rx_d3: Peri<'d, impl RXD3Pin<T>>,\n        tx_d0: Peri<'d, impl TXD0Pin<T>>,\n        tx_d1: Peri<'d, impl TXD1Pin<T>>,\n        tx_d2: Peri<'d, impl TXD2Pin<T>>,\n        tx_d3: Peri<'d, impl TXD3Pin<T>>,\n        tx_en: Peri<'d, impl TXEnPin<T>>,\n        mac_addr: [u8; 6],\n        phy: P,\n    ) -> Self {\n        config_pins!(\n            rx_clk, tx_clk, rxdv, rx_d0, rx_d1, rx_d2, rx_d3, tx_d0, tx_d1, tx_d2, tx_d3, tx_en\n        );\n\n        let pins = Pins::Mii([\n            Flex::new(rx_clk),\n            Flex::new(tx_clk),\n            Flex::new(rxdv),\n            Flex::new(rx_d0),\n            Flex::new(rx_d1),\n            Flex::new(rx_d2),\n            Flex::new(rx_d3),\n            Flex::new(tx_d0),\n            Flex::new(tx_d1),\n            Flex::new(tx_d2),\n            Flex::new(tx_d3),\n            Flex::new(tx_en),\n        ]);\n\n        Self::new_inner(queue, peri, irq, pins, phy, mac_addr, EthSelPhy::MII_GMII)\n    }\n\n    fn new_inner<const TX: usize, const RX: usize>(\n        queue: &'d mut PacketQueue<TX, RX>,\n        peri: Peri<'d, T>,\n        _irq: impl interrupt::typelevel::Binding<interrupt::typelevel::ETH, InterruptHandler> + 'd,\n        pins: Pins<'d>,\n        phy: P,\n        mac_addr: [u8; 6],\n        eth_sel_phy: EthSelPhy,\n    ) -> Self {\n        // Enable the necessary clocks\n        critical_section::with(|_| {\n            crate::pac::RCC.ahb1enr().modify(|w| {\n                w.set_ethen(true);\n                w.set_ethtxen(true);\n                w.set_ethrxen(true);\n            });\n\n            crate::pac::SYSCFG.pmcr().modify(|w| w.set_eth_sel_phy(eth_sel_phy));\n        });\n\n        let dma = T::regs().ethernet_dma();\n        let mac = T::regs().ethernet_mac();\n        let mtl = T::regs().ethernet_mtl();\n\n        // Reset and wait\n        dma.dmamr().modify(|w| w.set_swr(true));\n        while dma.dmamr().read().swr() {}\n\n        mac.maccr().modify(|w| {\n            w.set_ipg(0b000); // 96 bit times\n            w.set_acs(true);\n            w.set_fes(true);\n            w.set_dm(true);\n            // TODO: Carrier sense ? ECRSFD\n        });\n\n        // Disable multicast filter\n        mac.macpfr().modify(|w| w.set_pm(true));\n\n        // Note: Writing to LR triggers synchronisation of both LR and HR into the MAC core,\n        // so the LR write must happen after the HR write.\n        mac.maca0hr()\n            .modify(|w| w.set_addrhi(u16::from(mac_addr[4]) | (u16::from(mac_addr[5]) << 8)));\n        mac.maca0lr().write(|w| {\n            w.set_addrlo(\n                u32::from(mac_addr[0])\n                    | (u32::from(mac_addr[1]) << 8)\n                    | (u32::from(mac_addr[2]) << 16)\n                    | (u32::from(mac_addr[3]) << 24),\n            )\n        });\n\n        mac.macqtx_fcr().modify(|w| w.set_pt(0x100));\n\n        // disable all MMC RX interrupts\n        mac.mmc_rx_interrupt_mask().write(|w| {\n            w.set_rxcrcerpim(true);\n            w.set_rxalgnerpim(true);\n            w.set_rxucgpim(true);\n            w.set_rxlpiuscim(true);\n            w.set_rxlpitrcim(true)\n        });\n\n        // disable all MMC TX interrupts\n        mac.mmc_tx_interrupt_mask().write(|w| {\n            w.set_txscolgpim(true);\n            w.set_txmcolgpim(true);\n            w.set_txgpktim(true);\n            w.set_txlpiuscim(true);\n            w.set_txlpitrcim(true);\n        });\n\n        mtl.mtlrx_qomr().modify(|w| w.set_rsf(true));\n        mtl.mtltx_qomr().modify(|w| w.set_tsf(true));\n\n        dma.dmactx_cr().modify(|w| w.set_txpbl(1)); // 32 ?\n        dma.dmacrx_cr().modify(|w| {\n            w.set_rxpbl(1); // 32 ?\n            w.set_rbsz(RX_BUFFER_SIZE as u16);\n        });\n\n        let mut this = Self {\n            _peri: peri,\n            tx: TDesRing::new(&mut queue.tx_desc, &mut queue.tx_buf),\n            rx: RDesRing::new(&mut queue.rx_desc, &mut queue.rx_buf),\n            _pins: pins,\n            phy,\n            mac_addr,\n        };\n\n        fence(Ordering::SeqCst);\n\n        let mac = T::regs().ethernet_mac();\n        let mtl = T::regs().ethernet_mtl();\n        let dma = T::regs().ethernet_dma();\n\n        mac.maccr().modify(|w| {\n            w.set_re(true);\n            w.set_te(true);\n        });\n        mtl.mtltx_qomr().modify(|w| w.set_ftq(true));\n\n        dma.dmactx_cr().modify(|w| w.set_st(true));\n        dma.dmacrx_cr().modify(|w| w.set_sr(true));\n\n        // Enable interrupts\n        dma.dmacier().modify(|w| {\n            w.set_nie(true);\n            w.set_rie(true);\n            w.set_tie(true);\n        });\n\n        this.phy.phy_reset();\n        this.phy.phy_init();\n\n        interrupt::ETH.unpend();\n        unsafe { interrupt::ETH.enable() };\n\n        this\n    }\n}\n\nimpl<'d, T: Instance, P: Phy> Drop for Ethernet<'d, T, P> {\n    fn drop(&mut self) {\n        let dma = T::regs().ethernet_dma();\n        let mac = T::regs().ethernet_mac();\n        let mtl = T::regs().ethernet_mtl();\n\n        // Disable the TX DMA and wait for any previous transmissions to be completed\n        dma.dmactx_cr().modify(|w| w.set_st(false));\n        while {\n            let txqueue = mtl.mtltx_qdr().read();\n            txqueue.trcsts() == 0b01 || txqueue.txqsts()\n        } {}\n\n        // Disable MAC transmitter and receiver\n        mac.maccr().modify(|w| {\n            w.set_re(false);\n            w.set_te(false);\n        });\n\n        // Wait for previous receiver transfers to be completed and then disable the RX DMA\n        while {\n            let rxqueue = mtl.mtlrx_qdr().read();\n            rxqueue.rxqsts() != 0b00 || rxqueue.prxq() != 0\n        } {}\n        dma.dmacrx_cr().modify(|w| w.set_sr(false));\n    }\n}\n"
  },
  {
    "path": "embassy-stm32/src/executor.rs",
    "content": "//! STM32-specific `embassy-executor` platform.\n//!\n//! This module provides an `embassy-executor` platform specific for STM32 chips that integrates [`low_power::sleep()`](crate::low_power::sleep) in the main loop.\n//! Read the `embassy-executor` README for information about what \"executor platforms\" are and how they work.\n//!\n//! The implementation of the executor platform is equivalent to `platform-cortex-m` in `embassy-executor` other than low-power support.\n//!\n//! To use it:\n//!\n//! - Enable feature `executor-thread` and/or `executor-interrupt` in this crate.\n//! - **do not** enable features `platform-cortex-m`, `executor-thread` or `executor-interrupt` in the `embassy-executor` crate.\n//! - Tell the `main` macro to use this executor like this:\n//!\n//! ```rust,no_run\n//! use embassy_executor::Spawner;\n//! use embassy_time::Duration;\n//!\n//! #[embassy_executor::main(executor = \"embassy_stm32::executor::Executor\", entry = \"cortex_m_rt::entry\")]\n//! async fn main(spawner: Spawner) {\n//!     // initialize the platform...\n//!     let mut config = embassy_stm32::Config::default();\n//!     // the default value, but can be adjusted\n//!     config.min_stop_pause = Duration::from_millis(250);\n//!     // when enabled the power-consumption is much higher during stop, but debugging and RTT is working\n//!     config.enable_debug_during_sleep = false;\n//!     let p = embassy_stm32::init(config);\n//!\n//!     // your application here...\n//! }\n//! ```\n\n#[unsafe(export_name = \"__pender\")]\n#[cfg(any(feature = \"executor-thread\", feature = \"executor-interrupt\"))]\nfn __pender(context: *mut ()) {\n    // Safety: `context` is either `usize::MAX` created by `Executor::run`, or a valid interrupt\n    // request number given to `InterruptExecutor::start`.\n\n    let context = context as usize;\n\n    #[cfg(feature = \"executor-thread\")]\n    // Try to make Rust optimize the branching away if we only use thread mode.\n    if !cfg!(feature = \"executor-interrupt\") || context == THREAD_PENDER {\n        thread::SIGNAL_WORK_THREAD_MODE.store(true, core::sync::atomic::Ordering::SeqCst);\n        return;\n    }\n\n    #[cfg(feature = \"executor-interrupt\")]\n    unsafe {\n        use cortex_m::interrupt::InterruptNumber;\n        use cortex_m::peripheral::NVIC;\n\n        #[derive(Clone, Copy)]\n        struct Irq(u16);\n        unsafe impl InterruptNumber for Irq {\n            fn number(self) -> u16 {\n                self.0\n            }\n        }\n\n        let irq = Irq(context as u16);\n\n        // STIR is faster, but is only available in v7 and higher.\n        #[cfg(not(armv6m))]\n        {\n            let mut nvic: NVIC = core::mem::transmute(());\n            nvic.request(irq);\n        }\n\n        #[cfg(armv6m)]\n        NVIC::pend(irq);\n    }\n}\n\n#[cfg(feature = \"executor-thread\")]\npub use thread::*;\n#[cfg(feature = \"executor-thread\")]\nmod thread {\n    pub(super) const THREAD_PENDER: usize = usize::MAX;\n\n    use core::marker::PhantomData;\n\n    use embassy_executor::{Spawner, raw};\n\n    /// global atomic used to keep track of whether there is work to do since sev() is not available on RISCV\n    pub(crate) static SIGNAL_WORK_THREAD_MODE: core::sync::atomic::AtomicBool =\n        core::sync::atomic::AtomicBool::new(false);\n\n    /// Thread mode executor, using WFE/SEV.\n    ///\n    /// This is the simplest and most common kind of executor. It runs on\n    /// thread mode (at the lowest priority level), and uses the `WFE` ARM instruction\n    /// to sleep when it has no more work to do. When a task is woken, a `SEV` instruction\n    /// is executed, to make the `WFE` exit from sleep and poll the task.\n    ///\n    /// This executor allows for ultra low power consumption for chips where `WFE`\n    /// triggers low-power sleep without extra steps. If your chip requires extra steps,\n    /// you may use [`raw::Executor`] directly to program custom behavior.\n    pub struct Executor {\n        inner: raw::Executor,\n        not_send: PhantomData<*mut ()>,\n    }\n\n    impl Executor {\n        /// Create a new Executor.\n        pub fn new() -> Self {\n            Self {\n                inner: raw::Executor::new(THREAD_PENDER as *mut ()),\n                not_send: PhantomData,\n            }\n        }\n\n        /// Run the executor.\n        ///\n        /// The `init` closure is called with a [`Spawner`] that spawns tasks on\n        /// this executor. Use it to spawn the initial task(s). After `init` returns,\n        /// the executor starts running the tasks.\n        ///\n        /// To spawn more tasks later, you may keep copies of the [`Spawner`] (it is `Copy`),\n        /// for example by passing it as an argument to the initial tasks.\n        ///\n        /// This function requires `&'static mut self`. This means you have to store the\n        /// Executor instance in a place where it'll live forever and grants you mutable\n        /// access. There's a few ways to do this:\n        ///\n        /// - a [StaticCell](https://docs.rs/static_cell/latest/static_cell/) (safe)\n        /// - a `static mut` (unsafe)\n        /// - a local variable in a function you know never returns (like `fn main() -> !`), upgrading its lifetime with `transmute`. (unsafe)\n        ///\n        /// This function never returns.\n        pub fn run(&'static mut self, init: impl FnOnce(Spawner)) -> ! {\n            init(self.inner.spawner());\n\n            loop {\n                unsafe {\n                    self.inner.poll();\n\n                    // we do not care about race conditions between the load and store operations, interrupts\n                    // will only set this value to true.\n                    critical_section::with(|cs| {\n                        // if there is work to do, loop back to polling\n                        if SIGNAL_WORK_THREAD_MODE.load(core::sync::atomic::Ordering::SeqCst) {\n                            SIGNAL_WORK_THREAD_MODE.store(false, core::sync::atomic::Ordering::SeqCst);\n                        } else {\n                            // if not, sleep waiting for interrupt\n                            crate::low_power::sleep(cs);\n                        }\n                    });\n                    // if an interrupt occurred while waiting, it will be serviced here\n                };\n            }\n        }\n    }\n}\n\n#[cfg(feature = \"executor-interrupt\")]\npub use interrupt::*;\n#[cfg(feature = \"executor-interrupt\")]\nmod interrupt {\n    use core::cell::{Cell, UnsafeCell};\n    use core::mem::MaybeUninit;\n\n    use cortex_m::interrupt::InterruptNumber;\n    use cortex_m::peripheral::NVIC;\n    use critical_section::Mutex;\n    use embassy_executor::raw;\n\n    /// Interrupt mode executor.\n    ///\n    /// This executor runs tasks in interrupt mode. The interrupt handler is set up\n    /// to poll tasks, and when a task is woken the interrupt is pended from software.\n    ///\n    /// This allows running async tasks at a priority higher than thread mode. One\n    /// use case is to leave thread mode free for non-async tasks. Another use case is\n    /// to run multiple executors: one in thread mode for low priority tasks and another in\n    /// interrupt mode for higher priority tasks. Higher priority tasks will preempt lower\n    /// priority ones.\n    ///\n    /// It is even possible to run multiple interrupt mode executors at different priorities,\n    /// by assigning different priorities to the interrupts. For an example on how to do this,\n    /// See the 'multiprio' example for 'embassy-nrf'.\n    ///\n    /// To use it, you have to pick an interrupt that won't be used by the hardware.\n    /// Some chips reserve some interrupts for this purpose, sometimes named \"software interrupts\" (SWI).\n    /// If this is not the case, you may use an interrupt from any unused peripheral.\n    ///\n    /// It is somewhat more complex to use, it's recommended to use the thread-mode\n    /// [`Executor`](crate::executor::Executor) instead, if it works for your use case.\n    pub struct InterruptExecutor {\n        started: Mutex<Cell<bool>>,\n        executor: UnsafeCell<MaybeUninit<raw::Executor>>,\n    }\n\n    unsafe impl Send for InterruptExecutor {}\n    unsafe impl Sync for InterruptExecutor {}\n\n    impl InterruptExecutor {\n        /// Create a new, not started `InterruptExecutor`.\n        #[inline]\n        pub const fn new() -> Self {\n            Self {\n                started: Mutex::new(Cell::new(false)),\n                executor: UnsafeCell::new(MaybeUninit::uninit()),\n            }\n        }\n\n        /// Executor interrupt callback.\n        ///\n        /// # Safety\n        ///\n        /// - You MUST call this from the interrupt handler, and from nowhere else.\n        /// - You must not call this before calling `start()`.\n        pub unsafe fn on_interrupt(&'static self) {\n            let executor = unsafe { (&*self.executor.get()).assume_init_ref() };\n            executor.poll();\n        }\n\n        /// Start the executor.\n        ///\n        /// This initializes the executor, enables the interrupt, and returns.\n        /// The executor keeps running in the background through the interrupt.\n        ///\n        /// This returns a [`SendSpawner`] you can use to spawn tasks on it. A [`SendSpawner`]\n        /// is returned instead of a [`Spawner`](embassy_executor::Spawner) because the executor effectively runs in a\n        /// different \"thread\" (the interrupt), so spawning tasks on it is effectively\n        /// sending them.\n        ///\n        /// To obtain a [`Spawner`](embassy_executor::Spawner) for this executor, use [`Spawner::for_current_executor()`](embassy_executor::Spawner::for_current_executor()) from\n        /// a task running in it.\n        ///\n        /// # Interrupt requirements\n        ///\n        /// You must write the interrupt handler yourself, and make it call [`on_interrupt()`](Self::on_interrupt).\n        ///\n        /// This method already enables (unmasks) the interrupt, you must NOT do it yourself.\n        ///\n        /// You must set the interrupt priority before calling this method. You MUST NOT\n        /// do it after.\n        ///\n        /// [`SendSpawner`]: embassy_executor::SendSpawner\n        pub fn start(&'static self, irq: impl InterruptNumber) -> embassy_executor::SendSpawner {\n            if critical_section::with(|cs| self.started.borrow(cs).replace(true)) {\n                panic!(\"InterruptExecutor::start() called multiple times on the same executor.\");\n            }\n\n            unsafe {\n                (&mut *self.executor.get())\n                    .as_mut_ptr()\n                    .write(raw::Executor::new(irq.number() as *mut ()))\n            }\n\n            let executor = unsafe { (&*self.executor.get()).assume_init_ref() };\n\n            unsafe { NVIC::unmask(irq) }\n\n            executor.spawner().make_send()\n        }\n\n        /// Get a SendSpawner for this executor\n        ///\n        /// This returns a [`SendSpawner`](embassy_executor::SendSpawner) you can use to spawn tasks on this\n        /// executor.\n        ///\n        /// This MUST only be called on an executor that has already been started.\n        /// The function will panic otherwise.\n        pub fn spawner(&'static self) -> embassy_executor::SendSpawner {\n            if !critical_section::with(|cs| self.started.borrow(cs).get()) {\n                panic!(\"InterruptExecutor::spawner() called on uninitialized executor.\");\n            }\n            let executor = unsafe { (&*self.executor.get()).assume_init_ref() };\n            executor.spawner().make_send()\n        }\n    }\n}\n"
  },
  {
    "path": "embassy-stm32/src/exti/blocking.rs",
    "content": "//! Blocking-mode EXTI utilities\n//!\n//! This module provides types and functions for manual EXTI interrupt handling.\n//! Use these when you need direct control over EXTI interrupts outside of\n//! Embassy's async executor.\n//!\n//! The main blocking EXTI driver is [`ExtiInput<Blocking>`](super::ExtiInput).\n\nuse super::{ExtiInput, low_level};\nuse crate::mode::Blocking;\n\n/// Pre-computed bitmask for clearing multiple EXTI interrupt lines at once.\n///\n/// This type is intended for use with shared EXTI IRQ handlers\n/// (for example `EXTI15_10` or `EXTI9_5`), where multiple pins share\n/// a single interrupt vector.\n///\n/// Instead of checking and clearing each pin individually in your interrupt\n/// handler, you can pre-compute a mask during initialization and use it to\n/// clear all pending flags with a single register write.\npub struct ExtiGroupMask(u32);\n\nimpl ExtiGroupMask {\n    /// Creates a new EXTI group mask from a list of inputs.\n    ///\n    /// The mask is computed once and can be reused inside an interrupt\n    /// handler for fast clearing of pending flags.\n    ///\n    /// # Benefits\n    /// * **Reduced verbosity**: Clear all grouped pins with a single call\n    ///   instead of checking and clearing each pin individually.\n    /// * **Performance**: Clears all pending flags in one register write\n    ///   rather than multiple critical sections.\n    ///\n    /// # Parameters\n    /// * `inputs` - Slice of references to [`ExtiInput<Blocking>`](super::ExtiInput) instances that\n    ///   belong to the same EXTI IRQ group.\n    ///\n    /// # Safety Notes\n    /// * All inputs **must** map to the same EXTI IRQ group (for instance all pins\n    ///   in the range 10–15 for `EXTI15_10` handler).\n    /// * Mixing pins from different IRQ groups will cause `clear()` to\n    ///   incorrectly clear pending flags belonging to other interrupt vectors,\n    ///   potentially causing missed interrupts.\n    /// * Consult your STM32 reference manual for the EXTI line to IRQ\n    ///   vector mapping for your specific chip.\n    pub fn new(inputs: &[&ExtiInput<Blocking>]) -> Self {\n        let mut mask = 0;\n        for input in inputs {\n            mask |= input.pin_mask();\n        }\n        Self(mask)\n    }\n\n    /// Clears all pending EXTI interrupt flags contained in this mask.\n    ///\n    /// This performs a single write to the EXTI pending register,\n    /// making it suitable for use inside interrupt handlers.\n    pub fn clear(&self) {\n        critical_section::with(|_| low_level::clear_exti_pending_mask(self.0))\n    }\n}\n"
  },
  {
    "path": "embassy-stm32/src/exti/low_level.rs",
    "content": "//! Low-level EXTI (External Interrupt) functionality\n//!\n//! This module provides low-level functions for configuring and controlling\n//! the External Interrupt (EXTI) lines on STM32 microcontrollers.\n\nuse super::{cpu_regs, exticr_regs};\nuse crate::gpio::{Input, Pin as GpioPin, PinNumber};\nuse crate::pac::EXTI;\nuse crate::pac::exti::regs::Lines;\n\n/// Defines the edge triggering mode for EXTI interrupts\n///\n/// This enum specifies which signal edges should trigger an EXTI interrupt:\n/// - `Falling`: Only falling edges (high to low transitions) trigger interrupts\n/// - `Rising`: Only rising edges (low to high transitions) trigger interrupts\n/// - `Any`: Both rising and falling edges trigger interrupts\npub enum TriggerEdge {\n    /// Detect only falling edges (high to low transitions)\n    Falling,\n    /// Detect only rising edges (low to high transitions)\n    Rising,\n    /// Detect both rising and falling edges\n    Any,\n}\n\n/// Configures an EXTI line for a specific GPIO pin\npub(super) fn configure_exti_pin(pin: PinNumber, port: PinNumber, trigger_edge: TriggerEdge) {\n    critical_section::with(|_| {\n        let pin_num = pin as usize;\n        // Cast needed: on N6, PinNumber is u16 (for total pin counting), but port is always 0-15.\n        exticr_regs()\n            .exticr(pin_num / 4)\n            .modify(|w| w.set_exti(pin_num % 4, port as _));\n\n        let (rising, falling) = match trigger_edge {\n            TriggerEdge::Falling => (false, true),\n            TriggerEdge::Rising => (true, false),\n            TriggerEdge::Any => (true, true),\n        };\n\n        EXTI.rtsr(0).modify(|w| w.set_line(pin_num, rising));\n        EXTI.ftsr(0).modify(|w| w.set_line(pin_num, falling));\n\n        clear_exti_pending(pin);\n    });\n}\n\n/// EXTI interrupt state\npub enum InterruptState {\n    /// Interrupt enabled\n    Enabled,\n    /// Interrupt disabled\n    Disabled,\n}\n\nimpl From<InterruptState> for bool {\n    fn from(state: InterruptState) -> bool {\n        matches!(state, InterruptState::Enabled)\n    }\n}\n\n/// Enables the EXTI interrupt for a specific pin\npub(super) fn set_exti_interrupt_enabled(pin: PinNumber, state: InterruptState) {\n    critical_section::with(|_| {\n        let pin = pin as usize;\n        cpu_regs().imr(0).modify(|w| w.set_line(pin, state.into()));\n    });\n}\n\n/// Configures and enables an EXTI line from a GPIO Input in a single critical section\npub(super) fn configure_and_enable_exti(pin: &Input, trigger_edge: TriggerEdge) {\n    let pin_num = pin.pin.pin.pin();\n    let port_num = pin.pin.pin.port();\n\n    critical_section::with(|_| {\n        configure_exti_pin(pin_num, port_num, trigger_edge);\n        set_exti_interrupt_enabled(pin_num, InterruptState::Enabled);\n    });\n}\n\n/// Conditional compilation for STM32 variants with separate RPR/FPR registers.\n/// These variants use separate Rising/Falling Pending Registers instead of the unified PR register.\nmacro_rules! cfg_has_rpr_fpr {\n    ($($tokens:tt)*) => {\n        #[cfg(any(exti_c0, exti_g0, exti_u0, exti_l5, exti_u5, exti_u3, exti_h5, exti_h50, exti_n6))]\n        $($tokens)*\n    };\n}\n\n/// Conditional compilation for STM32 variants with unified PR register.\n/// Represents variants that use a single Pending Register for both rising and falling edges.\nmacro_rules! cfg_no_rpr_fpr {\n    ($($tokens:tt)*) => {\n        #[cfg(not(any(exti_c0, exti_g0, exti_u0, exti_l5, exti_u5, exti_u3, exti_h5, exti_h50, exti_n6)))]\n        $($tokens)*\n    };\n}\n\n/// Clears any pending EXTI interrupt flag for a specific bit mask\npub(super) fn clear_exti_pending_mask(mask: u32) {\n    cfg_no_rpr_fpr! {\n        EXTI.pr(0).write_value(Lines(mask));\n    }\n\n    cfg_has_rpr_fpr!({\n        EXTI.rpr(0).write_value(Lines(mask));\n        EXTI.fpr(0).write_value(Lines(mask));\n    })\n}\n\n/// Clears the pending EXTI interrupt flag for a specific pin\npub(super) fn clear_exti_pending(pin: PinNumber) {\n    let mask = 1u32 << pin;\n\n    critical_section::with(|_| {\n        clear_exti_pending_mask(mask);\n    });\n}\n\n/// Checks if an EXTI interrupt is pending for a specific pin\n///\n/// # Returns\n/// `true` if an interrupt is pending, `false` otherwise\npub(super) fn is_exti_pending(pin: PinNumber) -> bool {\n    let pin = pin as usize;\n\n    cfg_no_rpr_fpr! {\n        return EXTI.pr(0).read().line(pin);\n    }\n\n    cfg_has_rpr_fpr! {\n        return EXTI.rpr(0).read().line(pin) || EXTI.fpr(0).read().line(pin);\n    }\n}\n"
  },
  {
    "path": "embassy-stm32/src/exti/mod.rs",
    "content": "//! External Interrupts (EXTI)\nuse core::convert::Infallible;\nuse core::future::Future;\nuse core::marker::PhantomData;\nuse core::pin::Pin;\nuse core::task::{Context, Poll};\n\nuse embassy_hal_internal::PeripheralType;\nuse embassy_sync::waitqueue::AtomicWaker;\nuse futures_util::FutureExt;\n\nuse crate::gpio::{AnyPin, ExtiPin, Flex, Input, Level, Pin as GpioPin, PinNumber, Pull};\nuse crate::interrupt::Interrupt as InterruptEnum;\nuse crate::interrupt::typelevel::{Binding, Handler, Interrupt as InterruptType};\nuse crate::mode::{Async, Blocking, Mode as PeriMode};\nuse crate::pac::EXTI;\nuse crate::{Peri, pac};\n\n#[macro_use]\nmod low_level;\npub use low_level::{InterruptState, TriggerEdge};\n\npub mod blocking;\n\nconst EXTI_COUNT: usize = 16;\nstatic EXTI_WAKERS: [AtomicWaker; EXTI_COUNT] = [const { AtomicWaker::new() }; EXTI_COUNT];\n\n#[cfg(all(exti_w, feature = \"_core-cm0p\"))]\nfn cpu_regs() -> pac::exti::Cpu {\n    EXTI.cpu(1)\n}\n\n#[cfg(all(exti_w, not(feature = \"_core-cm0p\")))]\nfn cpu_regs() -> pac::exti::Cpu {\n    EXTI.cpu(0)\n}\n\n#[cfg(not(exti_w))]\nfn cpu_regs() -> pac::exti::Exti {\n    EXTI\n}\n\n#[cfg(not(any(\n    exti_c0, exti_g0, exti_u0, exti_l5, gpio_v1, exti_u5, exti_u3, exti_h5, exti_h50, exti_n6\n)))]\nfn exticr_regs() -> pac::syscfg::Syscfg {\n    pac::SYSCFG\n}\n#[cfg(any(exti_c0, exti_g0, exti_u0, exti_l5, exti_u5, exti_u3, exti_h5, exti_h50, exti_n6))]\nfn exticr_regs() -> pac::exti::Exti {\n    EXTI\n}\n#[cfg(gpio_v1)]\nfn exticr_regs() -> pac::afio::Afio {\n    pac::AFIO\n}\n\nunsafe fn on_irq() {\n    cfg_no_rpr_fpr! {\n        let bits = EXTI.pr(0).read().0;\n    }\n    cfg_has_rpr_fpr! {\n        let bits = EXTI.rpr(0).read().0 | EXTI.fpr(0).read().0;\n    }\n\n    // We don't handle or change any EXTI lines above 16.\n    let bits = bits & 0x0000FFFF;\n\n    // Mask all the channels that fired.\n    cpu_regs().imr(0).modify(|w| w.0 &= !bits);\n\n    // Wake the tasks\n    for pin in BitIter(bits) {\n        EXTI_WAKERS[pin as usize].wake();\n    }\n\n    // Clear pending\n    low_level::clear_exti_pending_mask(bits);\n}\n\nstruct BitIter(u32);\n\nimpl Iterator for BitIter {\n    type Item = u32;\n\n    fn next(&mut self) -> Option<Self::Item> {\n        match self.0.trailing_zeros() {\n            32 => None,\n            b => {\n                self.0 &= !(1 << b);\n                Some(b)\n            }\n        }\n    }\n}\n\n/// EXTI input driver.\n///\n/// This driver augments a GPIO `Input` with EXTI functionality. EXTI is not\n/// built into `Input` itself because it needs to take ownership of the corresponding\n/// EXTI channel, which is a limited resource.\n///\n/// Pins PA5, PB5, PC5... all use EXTI channel 5, so you can't use EXTI on, say, PA5 and PC5 at the same time.\npub struct ExtiInput<'d, Mode: PeriMode> {\n    pin: Input<'d>,\n    _kind: PhantomData<Mode>,\n}\n\nimpl<'d, Mode: PeriMode> Unpin for ExtiInput<'d, Mode> {}\n\nimpl<'d, Mode: PeriMode> ExtiInput<'d, Mode> {\n    /// Get whether the pin is high.\n    pub fn is_high(&self) -> bool {\n        self.pin.is_high()\n    }\n\n    /// Get whether the pin is low.\n    pub fn is_low(&self) -> bool {\n        self.pin.is_low()\n    }\n\n    /// Get the pin level.\n    pub fn get_level(&self) -> Level {\n        self.pin.get_level()\n    }\n}\n\nimpl<'d> ExtiInput<'d, Async> {\n    /// Create an EXTI input.\n    ///\n    /// The Binding must bind the Channel's IRQ to [InterruptHandler].\n    pub fn new<T: ExtiPin + GpioPin>(\n        pin: Peri<'d, T>,\n        _ch: Peri<'d, T::ExtiChannel>,\n        pull: Pull,\n        _irq: impl Binding<\n            <<T as ExtiPin>::ExtiChannel as Channel>::IRQ,\n            InterruptHandler<<<T as ExtiPin>::ExtiChannel as Channel>::IRQ>,\n        >,\n    ) -> Self {\n        Self {\n            pin: Input::new(pin, pull),\n            _kind: PhantomData,\n        }\n    }\n\n    /// Create an EXTI input from an existing [`Input`] pin.\n    ///\n    /// Useful when a pin was previously used as a plain [`Input`] and needs to\n    /// be upgraded to interrupt-driven mode without re-acquiring the peripheral\n    /// token. The pin retains its current pull configuration.\n    ///\n    /// The Binding must bind the Channel's IRQ to [InterruptHandler].\n    pub fn from_input<C: Channel>(\n        pin: Input<'d>,\n        _ch: Peri<'d, C>,\n        _irq: impl Binding<C::IRQ, InterruptHandler<C::IRQ>>,\n    ) -> Self {\n        Self {\n            pin,\n            _kind: PhantomData,\n        }\n    }\n\n    /// Create an EXTI input from an existing [`Flex`] pin.\n    ///\n    /// Useful when a pin was previously used in bidirectional mode (e.g.,\n    /// driven as an output for a hardware entry sequence) and needs to be\n    /// switched to interrupt-driven input mode without re-acquiring the\n    /// peripheral token.\n    ///\n    /// The pin should be in input mode (configured via [`Flex::set_as_input()`])\n    /// before calling this.\n    ///\n    /// The Binding must bind the Channel's IRQ to [InterruptHandler].\n    pub fn from_flex<C: Channel>(\n        pin: Flex<'d>,\n        _ch: Peri<'d, C>,\n        _irq: impl Binding<C::IRQ, InterruptHandler<C::IRQ>>,\n    ) -> Self {\n        Self {\n            pin: Input::from_flex(pin),\n            _kind: PhantomData,\n        }\n    }\n\n    /// Asynchronously wait until the pin is high.\n    ///\n    /// This returns immediately if the pin is already high.\n    pub async fn wait_for_high(&mut self) {\n        let fut = ExtiInputFuture::new(&self.pin, TriggerEdge::Rising, true);\n        if self.is_high() {\n            return;\n        }\n        fut.await\n    }\n\n    /// Asynchronously wait until the pin is low.\n    ///\n    /// This returns immediately if the pin is already low.\n    pub async fn wait_for_low(&mut self) {\n        let fut = ExtiInputFuture::new(&self.pin, TriggerEdge::Falling, true);\n        if self.is_low() {\n            return;\n        }\n        fut.await\n    }\n\n    /// Asynchronously wait until the pin sees a rising edge.\n    ///\n    /// If the pin is already high, it will wait for it to go low then back high.\n    pub async fn wait_for_rising_edge(&mut self) {\n        ExtiInputFuture::new(&self.pin, TriggerEdge::Rising, true).await\n    }\n\n    /// Asynchronously wait until the pin sees a rising edge.\n    ///\n    /// If the pin is already high, it will wait for it to go low then back high.\n    pub fn poll_for_rising_edge<'a>(&mut self, cx: &mut Context<'a>) {\n        let _ = ExtiInputFuture::new(&self.pin, TriggerEdge::Rising, false).poll_unpin(cx);\n    }\n\n    /// Asynchronously wait until the pin sees a falling edge.\n    ///\n    /// If the pin is already low, it will wait for it to go high then back low.\n    pub async fn wait_for_falling_edge(&mut self) {\n        ExtiInputFuture::new(&self.pin, TriggerEdge::Falling, true).await\n    }\n\n    /// Asynchronously wait until the pin sees a falling edge.\n    ///\n    /// If the pin is already low, it will wait for it to go high then back low.\n    pub fn poll_for_falling_edge<'a>(&mut self, cx: &mut Context<'a>) {\n        let _ = ExtiInputFuture::new(&self.pin, TriggerEdge::Falling, false).poll_unpin(cx);\n    }\n\n    /// Asynchronously wait until the pin sees any edge (either rising or falling).\n    pub async fn wait_for_any_edge(&mut self) {\n        ExtiInputFuture::new(&self.pin, TriggerEdge::Any, true).await\n    }\n\n    /// Asynchronously wait until the pin sees any edge (either rising or falling).\n    pub fn poll_for_any_edge<'a>(&mut self, cx: &mut Context<'a>) {\n        let _ = ExtiInputFuture::new(&self.pin, TriggerEdge::Any, false).poll_unpin(cx);\n    }\n}\n\nimpl<'d> ExtiInput<'d, Blocking> {\n    /// Creates a new EXTI input for use with manual interrupt handling.\n    ///\n    /// This configures and enables the EXTI interrupt for the pin, but does not\n    /// provide async methods for waiting on events. You must provide your own\n    /// interrupt handler to service EXTI events and clear pending flags.\n    ///\n    /// For async/await integration with Embassy's executor, use `ExtiInput<Async>` instead.\n    ///\n    /// # Arguments\n    /// * `pin` - The GPIO pin to use\n    /// * `ch` - The EXTI channel corresponding to the pin (consumed for ownership tracking)\n    /// * `pull` - The pull configuration for the pin\n    /// * `trigger_edge` - The edge triggering mode (falling, rising, or any)\n    ///\n    /// # Returns\n    /// A new `ExtiInput` instance with interrupts enabled\n    pub fn new_blocking<T: GpioPin + ExtiPin>(\n        pin: Peri<'d, T>,\n        _ch: Peri<'d, T::ExtiChannel>, // Consumed for ownership tracking\n        pull: Pull,\n        trigger_edge: TriggerEdge,\n    ) -> Self {\n        let pin = Input::new(pin, pull);\n\n        low_level::configure_and_enable_exti(&pin, trigger_edge);\n\n        Self {\n            pin,\n            _kind: PhantomData,\n        }\n    }\n\n    /// Reconfigures the edge detection mode for this pin's EXTI line\n    ///\n    /// This method updates which edges (rising, falling, or any) will trigger\n    /// interrupts for this pin.\n    /// Note that reconfiguring the edge detection will clear any pending\n    /// interrupt flag for this pin.\n    pub fn set_edge_detection(&mut self, trigger_edge: TriggerEdge) {\n        let pin_num = self.pin.pin.pin.pin();\n        let port_num = self.pin.pin.pin.port();\n        low_level::configure_exti_pin(pin_num, port_num, trigger_edge);\n    }\n\n    /// Enables the EXTI interrupt for this pin\n    pub fn enable_interrupt(&mut self) {\n        let pin_num = self.pin.pin.pin.pin();\n        low_level::set_exti_interrupt_enabled(pin_num, InterruptState::Enabled);\n    }\n\n    /// Disables the EXTI interrupt for this pin\n    pub fn disable_interrupt(&mut self) {\n        let pin_num = self.pin.pin.pin.pin();\n        low_level::set_exti_interrupt_enabled(pin_num, InterruptState::Disabled);\n    }\n\n    /// Clears any pending interrupt for this pin\n    ///\n    /// This method clears the pending interrupt flag for the EXTI line\n    /// associated with this pin. This should typically be called from\n    /// the interrupt handler after processing an interrupt.\n    pub fn clear_pending(&mut self) {\n        let pin_num = self.pin.pin.pin.pin();\n        low_level::clear_exti_pending(pin_num);\n    }\n\n    /// Checks if an interrupt is pending for the current pin\n    ///\n    /// This method checks if there is a pending interrupt on the EXTI line\n    /// associated with this pin.\n    ///\n    /// # Returns\n    /// `true` if an interrupt is pending, `false` otherwise\n    pub fn is_pending(&self) -> bool {\n        let pin_num = self.pin.pin.pin.pin();\n        low_level::is_exti_pending(pin_num)\n    }\n\n    fn pin_mask(&self) -> u32 {\n        1 << self.pin.pin.pin.pin()\n    }\n}\n\nimpl<'d, Mode: PeriMode> embedded_hal_02::digital::v2::InputPin for ExtiInput<'d, Mode> {\n    type Error = Infallible;\n\n    fn is_high(&self) -> Result<bool, Self::Error> {\n        Ok(self.is_high())\n    }\n\n    fn is_low(&self) -> Result<bool, Self::Error> {\n        Ok(self.is_low())\n    }\n}\n\nimpl<'d, Mode: PeriMode> embedded_hal_1::digital::ErrorType for ExtiInput<'d, Mode> {\n    type Error = Infallible;\n}\n\nimpl<'d, Mode: PeriMode> embedded_hal_1::digital::InputPin for ExtiInput<'d, Mode> {\n    fn is_high(&mut self) -> Result<bool, Self::Error> {\n        Ok((*self).is_high())\n    }\n\n    fn is_low(&mut self) -> Result<bool, Self::Error> {\n        Ok((*self).is_low())\n    }\n}\n\nimpl<'d> embedded_hal_async::digital::Wait for ExtiInput<'d, Async> {\n    async fn wait_for_high(&mut self) -> Result<(), Self::Error> {\n        self.wait_for_high().await;\n        Ok(())\n    }\n\n    async fn wait_for_low(&mut self) -> Result<(), Self::Error> {\n        self.wait_for_low().await;\n        Ok(())\n    }\n\n    async fn wait_for_rising_edge(&mut self) -> Result<(), Self::Error> {\n        self.wait_for_rising_edge().await;\n        Ok(())\n    }\n\n    async fn wait_for_falling_edge(&mut self) -> Result<(), Self::Error> {\n        self.wait_for_falling_edge().await;\n        Ok(())\n    }\n\n    async fn wait_for_any_edge(&mut self) -> Result<(), Self::Error> {\n        self.wait_for_any_edge().await;\n        Ok(())\n    }\n}\n\n#[must_use = \"futures do nothing unless you `.await` or poll them\"]\nstruct ExtiInputFuture<'a> {\n    pin: PinNumber,\n    drop: bool,\n    phantom: PhantomData<&'a mut AnyPin>,\n}\n\nimpl<'a> ExtiInputFuture<'a> {\n    fn new(pin: &Input, trigger_edge: TriggerEdge, drop: bool) -> Self {\n        low_level::configure_and_enable_exti(pin, trigger_edge);\n\n        Self {\n            pin: pin.pin.pin.pin(),\n            drop,\n            phantom: PhantomData,\n        }\n    }\n}\n\nimpl<'a> Drop for ExtiInputFuture<'a> {\n    fn drop(&mut self) {\n        if self.drop {\n            critical_section::with(|_| {\n                let pin = self.pin as _;\n                cpu_regs().imr(0).modify(|w| w.set_line(pin, false));\n            });\n        }\n    }\n}\n\nimpl<'a> Future for ExtiInputFuture<'a> {\n    type Output = ();\n\n    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {\n        EXTI_WAKERS[self.pin as usize].register(cx.waker());\n\n        let imr = cpu_regs().imr(0).read();\n        if !imr.line(self.pin as _) {\n            Poll::Ready(())\n        } else {\n            Poll::Pending\n        }\n    }\n}\n\nmacro_rules! foreach_exti_irq {\n    ($action:ident) => {\n        foreach_interrupt!(\n            (EXTI0)  => { $action!(EXTI0); };\n            (EXTI1)  => { $action!(EXTI1); };\n            (EXTI2)  => { $action!(EXTI2); };\n            (EXTI3)  => { $action!(EXTI3); };\n            (EXTI4)  => { $action!(EXTI4); };\n            (EXTI5)  => { $action!(EXTI5); };\n            (EXTI6)  => { $action!(EXTI6); };\n            (EXTI7)  => { $action!(EXTI7); };\n            (EXTI8)  => { $action!(EXTI8); };\n            (EXTI9)  => { $action!(EXTI9); };\n            (EXTI10) => { $action!(EXTI10); };\n            (EXTI11) => { $action!(EXTI11); };\n            (EXTI12) => { $action!(EXTI12); };\n            (EXTI13) => { $action!(EXTI13); };\n            (EXTI14) => { $action!(EXTI14); };\n            (EXTI15) => { $action!(EXTI15); };\n\n            // plus the weird ones\n            (EXTI0_1)   => { $action!(EXTI0_1); };\n            (EXTI15_10) => { $action!(EXTI15_10); };\n            (EXTI15_4)  => { $action!(EXTI15_4); };\n            (EXTI1_0)   => { $action!(EXTI1_0); };\n            (EXTI2_3)   => { $action!(EXTI2_3); };\n            (EXTI2_TSC) => { $action!(EXTI2_TSC); };\n            (EXTI3_2)   => { $action!(EXTI3_2); };\n            (EXTI4_15)  => { $action!(EXTI4_15); };\n            (EXTI9_5)   => { $action!(EXTI9_5); };\n        );\n    };\n}\n\n///EXTI interrupt handler. All EXTI interrupt vectors should be bound to this handler.\n///\n/// It is generic over the [Interrupt](InterruptType) rather\n/// than the [Channel] because it should not be bound multiple\n/// times to the same vector on chips which multiplex multiple EXTI interrupts into one vector.\n//\n// It technically doesn't need to be generic at all, except to satisfy the generic argument\n// of [Handler]. All EXTI interrupts eventually land in the same on_irq() function.\npub struct InterruptHandler<T: crate::interrupt::typelevel::Interrupt> {\n    _phantom: PhantomData<T>,\n}\n\nimpl<T: InterruptType> Handler<T> for InterruptHandler<T> {\n    unsafe fn on_interrupt() {\n        on_irq()\n    }\n}\n\ntrait SealedChannel {}\n\n/// EXTI channel trait.\n#[allow(private_bounds)]\npub trait Channel: PeripheralType + SealedChannel + Sized {\n    /// EXTI channel number.\n    fn number(&self) -> PinNumber;\n    /// [Enum-level Interrupt](InterruptEnum), which may be the same for multiple channels.\n    fn irq(&self) -> InterruptEnum;\n    /// [Type-level Interrupt](InterruptType), which may be the same for multiple channels.\n    type IRQ: InterruptType;\n}\n\n//Doc isn't hidden in order to surface the explanation to users, even though it's completely inoperable, not just deprecated.\n//Entire type along with doc can probably be removed after deprecation has appeared in a release once.\n/// Deprecated type-erased EXTI channel.\n///\n/// Support for AnyChannel was removed in order to support manually bindable EXTI interrupts via bind_interrupts; [ExtiInput::new()]\n/// must know the required IRQ at compile time, and therefore cannot support type-erased channels.\n#[deprecated = \"type-erased EXTI channels are no longer supported, in order to support manually bindable EXTI interrupts (more info: https://github.com/embassy-rs/embassy/pull/4922)\"]\npub struct AnyChannel {\n    #[allow(unused)]\n    number: PinNumber,\n}\n\nmacro_rules! impl_exti {\n    ($type:ident, $number:expr) => {\n        impl SealedChannel for crate::peripherals::$type {}\n        impl Channel for crate::peripherals::$type {\n            fn number(&self) -> PinNumber {\n                $number\n            }\n            fn irq(&self) -> InterruptEnum {\n                crate::_generated::peripheral_interrupts::EXTI::$type::IRQ\n            }\n            type IRQ = crate::_generated::peripheral_interrupts::EXTI::$type;\n        }\n\n        //Still here to surface deprecation messages to the user - remove when removing AnyChannel\n        #[allow(deprecated)]\n        impl From<crate::peripherals::$type> for AnyChannel {\n            fn from(_val: crate::peripherals::$type) -> Self {\n                Self { number: $number }\n            }\n        }\n    };\n}\n\nimpl_exti!(EXTI0, 0);\nimpl_exti!(EXTI1, 1);\nimpl_exti!(EXTI2, 2);\nimpl_exti!(EXTI3, 3);\nimpl_exti!(EXTI4, 4);\nimpl_exti!(EXTI5, 5);\nimpl_exti!(EXTI6, 6);\nimpl_exti!(EXTI7, 7);\nimpl_exti!(EXTI8, 8);\nimpl_exti!(EXTI9, 9);\nimpl_exti!(EXTI10, 10);\nimpl_exti!(EXTI11, 11);\nimpl_exti!(EXTI12, 12);\nimpl_exti!(EXTI13, 13);\nimpl_exti!(EXTI14, 14);\nimpl_exti!(EXTI15, 15);\n\nmacro_rules! enable_irq {\n    ($e:ident) => {\n        crate::interrupt::typelevel::$e::enable();\n    };\n}\n\n/// safety: must be called only once\npub(crate) unsafe fn init(_cs: critical_section::CriticalSection) {\n    use crate::interrupt::typelevel::Interrupt;\n\n    foreach_exti_irq!(enable_irq);\n}\n"
  },
  {
    "path": "embassy-stm32/src/flash/asynch.rs",
    "content": "use core::marker::PhantomData;\nuse core::sync::atomic::{Ordering, fence};\n\nuse embassy_hal_internal::drop::OnDrop;\nuse embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;\nuse embassy_sync::mutex::Mutex;\n\nuse super::{\n    Async, Error, FLASH_BASE, FLASH_SIZE, Flash, FlashLayout, WRITE_SIZE, blocking_read, ensure_sector_aligned, family,\n    get_flash_regions, get_sector,\n};\nuse crate::interrupt::InterruptExt;\nuse crate::peripherals::FLASH;\nuse crate::{Peri, interrupt};\n\npub(super) static REGION_ACCESS: Mutex<CriticalSectionRawMutex, ()> = Mutex::new(());\n\nimpl<'d> Flash<'d, Async> {\n    /// Create a new flash driver with async capabilities.\n    pub fn new(\n        p: Peri<'d, FLASH>,\n        _irq: impl interrupt::typelevel::Binding<crate::interrupt::typelevel::FLASH, InterruptHandler> + 'd,\n    ) -> Self {\n        crate::interrupt::FLASH.unpend();\n        unsafe { crate::interrupt::FLASH.enable() };\n\n        Self {\n            inner: p,\n            _mode: PhantomData,\n        }\n    }\n\n    /// Split this flash driver into one instance per flash memory region.\n    ///\n    /// See module-level documentation for details on how memory regions work.\n    pub fn into_regions(self) -> FlashLayout<'d, Async> {\n        FlashLayout::new(self.inner)\n    }\n\n    /// Async write.\n    ///\n    /// NOTE: `offset` is an offset from the flash start, NOT an absolute address.\n    /// For example, to write address `0x0800_1234` you have to use offset `0x1234`.\n    pub async fn write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Error> {\n        unsafe { write_chunked(FLASH_BASE as u32, FLASH_SIZE as u32, offset, bytes).await }\n    }\n\n    /// Async erase.\n    ///\n    /// NOTE: `from` and `to` are offsets from the flash start, NOT an absolute address.\n    /// For example, to erase address `0x0801_0000` you have to use offset `0x1_0000`.\n    pub async fn erase(&mut self, from: u32, to: u32) -> Result<(), Error> {\n        unsafe { erase_sectored(FLASH_BASE as u32, from, to).await }\n    }\n}\n\n/// Interrupt handler\npub struct InterruptHandler;\n\nimpl interrupt::typelevel::Handler<crate::interrupt::typelevel::FLASH> for InterruptHandler {\n    unsafe fn on_interrupt() {\n        family::on_interrupt();\n    }\n}\n\nimpl embedded_storage_async::nor_flash::ReadNorFlash for Flash<'_, Async> {\n    const READ_SIZE: usize = super::READ_SIZE;\n\n    async fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Self::Error> {\n        self.blocking_read(offset, bytes)\n    }\n\n    fn capacity(&self) -> usize {\n        FLASH_SIZE\n    }\n}\n\nimpl embedded_storage_async::nor_flash::NorFlash for Flash<'_, Async> {\n    const WRITE_SIZE: usize = WRITE_SIZE;\n    const ERASE_SIZE: usize = super::MAX_ERASE_SIZE;\n\n    async fn write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Self::Error> {\n        self.write(offset, bytes).await\n    }\n\n    async fn erase(&mut self, from: u32, to: u32) -> Result<(), Self::Error> {\n        self.erase(from, to).await\n    }\n}\n\npub(super) async unsafe fn write_chunked(base: u32, size: u32, offset: u32, bytes: &[u8]) -> Result<(), Error> {\n    if offset + bytes.len() as u32 > size {\n        return Err(Error::Size);\n    }\n    if offset % WRITE_SIZE as u32 != 0 || bytes.len() % WRITE_SIZE != 0 {\n        return Err(Error::Unaligned);\n    }\n\n    let mut address = base + offset;\n    trace!(\"Writing {} bytes at 0x{:x}\", bytes.len(), address);\n\n    for chunk in bytes.chunks(WRITE_SIZE) {\n        family::clear_all_err();\n        fence(Ordering::SeqCst);\n        family::unlock();\n        fence(Ordering::SeqCst);\n        family::enable_write();\n        fence(Ordering::SeqCst);\n\n        let _on_drop = OnDrop::new(|| {\n            family::disable_write();\n            fence(Ordering::SeqCst);\n            family::lock();\n        });\n\n        family::write(address, unwrap!(chunk.try_into())).await?;\n        address += WRITE_SIZE as u32;\n    }\n    Ok(())\n}\n\npub(super) async unsafe fn erase_sectored(base: u32, from: u32, to: u32) -> Result<(), Error> {\n    let start_address = base + from;\n    let end_address = base + to;\n    let regions = get_flash_regions();\n\n    ensure_sector_aligned(start_address, end_address, regions)?;\n\n    trace!(\"Erasing from 0x{:x} to 0x{:x}\", start_address, end_address);\n\n    let mut address = start_address;\n    while address < end_address {\n        let sector = get_sector(address, regions);\n        trace!(\"Erasing sector: {:?}\", sector);\n\n        family::clear_all_err();\n        fence(Ordering::SeqCst);\n        family::unlock();\n        fence(Ordering::SeqCst);\n\n        let _on_drop = OnDrop::new(|| family::lock());\n\n        family::erase_sector(&sector).await?;\n        address += sector.size;\n    }\n    Ok(())\n}\n\nforeach_flash_region! {\n    ($type_name:ident, $write_size:literal, $erase_size:literal) => {\n        impl crate::_generated::flash_regions::$type_name<'_, Async> {\n            /// Async read.\n            ///\n            /// Note: reading from flash can't actually block, so this is the same as `blocking_read`.\n            pub async fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Error> {\n                blocking_read(self.0.base(), self.0.size, offset, bytes)\n            }\n\n            /// Async write.\n            pub async fn write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Error> {\n                let _guard = REGION_ACCESS.lock().await;\n                unsafe { write_chunked(self.0.base(), self.0.size, offset, bytes).await }\n            }\n\n            /// Async erase.\n            pub async fn erase(&mut self, from: u32, to: u32) -> Result<(), Error> {\n                let _guard = REGION_ACCESS.lock().await;\n                unsafe { erase_sectored(self.0.base(), from, to).await }\n            }\n        }\n\n                impl embedded_storage_async::nor_flash::ReadNorFlash for crate::_generated::flash_regions::$type_name<'_, Async> {\n            const READ_SIZE: usize = super::READ_SIZE;\n\n            async fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Self::Error> {\n                self.read(offset, bytes).await\n            }\n\n            fn capacity(&self) -> usize {\n                self.0.size as usize\n            }\n        }\n\n                impl embedded_storage_async::nor_flash::NorFlash for crate::_generated::flash_regions::$type_name<'_, Async> {\n            const WRITE_SIZE: usize = $write_size;\n            const ERASE_SIZE: usize = $erase_size;\n\n            async fn write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Self::Error> {\n                self.write(offset, bytes).await\n            }\n\n            async fn erase(&mut self, from: u32, to: u32) -> Result<(), Self::Error> {\n                self.erase(from, to).await\n            }\n        }\n    };\n}\n"
  },
  {
    "path": "embassy-stm32/src/flash/c.rs",
    "content": "use core::ptr::write_volatile;\nuse core::sync::atomic::{Ordering, fence};\n\nuse cortex_m::interrupt;\n\nuse super::{FlashSector, WRITE_SIZE};\nuse crate::flash::Error;\nuse crate::pac;\n\npub(crate) unsafe fn lock() {\n    pac::FLASH.cr().modify(|w| w.set_lock(true));\n}\npub(crate) unsafe fn unlock() {\n    // Wait, while the memory interface is busy.\n    wait_busy();\n\n    // Unlock flash\n    if pac::FLASH.cr().read().lock() {\n        pac::FLASH.keyr().write_value(0x4567_0123);\n        pac::FLASH.keyr().write_value(0xCDEF_89AB);\n    }\n}\n\npub(crate) unsafe fn enable_blocking_write() {\n    assert_eq!(0, WRITE_SIZE % 4);\n    pac::FLASH.cr().write(|w| w.set_pg(true));\n}\n\npub(crate) unsafe fn disable_blocking_write() {\n    pac::FLASH.cr().write(|w| w.set_pg(false));\n}\n\npub(crate) unsafe fn blocking_write(start_address: u32, buf: &[u8; WRITE_SIZE]) -> Result<(), Error> {\n    let mut address = start_address;\n    for val in buf.chunks(4) {\n        write_volatile(address as *mut u32, u32::from_le_bytes(unwrap!(val.try_into())));\n        address += val.len() as u32;\n\n        // prevents parallelism errors\n        fence(Ordering::SeqCst);\n    }\n\n    wait_ready_blocking()\n}\n\npub(crate) unsafe fn blocking_erase_sector(sector: &FlashSector) -> Result<(), Error> {\n    let idx = (sector.start - super::FLASH_BASE as u32) / super::BANK1_REGION.erase_size as u32;\n\n    #[cfg(feature = \"defmt\")]\n    defmt::trace!(\n        \"STM32C0 Erase: addr=0x{:08x}, idx={}, erase_size={}\",\n        sector.start,\n        idx,\n        super::BANK1_REGION.erase_size\n    );\n\n    wait_busy();\n    clear_all_err();\n\n    // Explicitly unlock before erase\n    unlock();\n\n    interrupt::free(|_| {\n        #[cfg(feature = \"defmt\")]\n        {\n            let cr_before = pac::FLASH.cr().read();\n            defmt::trace!(\"FLASH_CR before: 0x{:08x}\", cr_before.0);\n        }\n\n        pac::FLASH.cr().modify(|w| {\n            w.set_per(true);\n            w.set_pnb(idx as u8);\n            w.set_strt(true);\n        });\n\n        #[cfg(feature = \"defmt\")]\n        {\n            let cr_after = pac::FLASH.cr().read();\n            defmt::trace!(\n                \"FLASH_CR after: 0x{:08x}, PER={}, PNB={}, STRT={}\",\n                cr_after.0,\n                cr_after.per(),\n                cr_after.pnb(),\n                cr_after.strt()\n            );\n        }\n    });\n\n    let ret: Result<(), Error> = wait_ready_blocking();\n\n    // Clear erase bit\n    pac::FLASH.cr().modify(|w| w.set_per(false));\n\n    // Explicitly lock after erase\n    lock();\n\n    // Extra wait to ensure operation completes\n    wait_busy();\n\n    ret\n}\n\npub(crate) unsafe fn wait_ready_blocking() -> Result<(), Error> {\n    while pac::FLASH.sr().read().bsy() {}\n\n    let sr = pac::FLASH.sr().read();\n\n    if sr.progerr() {\n        return Err(Error::Prog);\n    }\n\n    if sr.wrperr() {\n        return Err(Error::Protected);\n    }\n\n    if sr.pgaerr() {\n        return Err(Error::Unaligned);\n    }\n\n    Ok(())\n}\n\npub(crate) unsafe fn clear_all_err() {\n    // read and write back the same value.\n    // This clears all \"write 1 to clear\" bits.\n    pac::FLASH.sr().modify(|_| {});\n}\n\nfn wait_busy() {\n    while pac::FLASH.sr().read().bsy() {}\n}\n"
  },
  {
    "path": "embassy-stm32/src/flash/common.rs",
    "content": "use core::marker::PhantomData;\nuse core::sync::atomic::{Ordering, fence};\n\nuse embassy_hal_internal::drop::OnDrop;\n\nuse super::{\n    Async, Blocking, Error, FLASH_SIZE, FlashBank, FlashLayout, FlashRegion, FlashSector, MAX_ERASE_SIZE, READ_SIZE,\n    WRITE_SIZE, family, get_flash_regions,\n};\nuse crate::_generated::FLASH_BASE;\nuse crate::Peri;\nuse crate::peripherals::FLASH;\n\n/// Internal flash memory driver.\npub struct Flash<'d, MODE = Async> {\n    pub(crate) inner: Peri<'d, FLASH>,\n    pub(crate) _mode: PhantomData<MODE>,\n}\n\nimpl<'d> Flash<'d, Blocking> {\n    /// Create a new flash driver, usable in blocking mode.\n    pub fn new_blocking(p: Peri<'d, FLASH>) -> Self {\n        #[cfg(bank_setup_configurable)]\n        // Check if the configuration matches the embassy setup\n        super::check_bank_setup();\n\n        Self {\n            inner: p,\n            _mode: PhantomData,\n        }\n    }\n}\n\nimpl<'d, MODE> Flash<'d, MODE> {\n    /// Split this flash driver into one instance per flash memory region.\n    ///\n    /// See module-level documentation for details on how memory regions work.\n    pub fn into_blocking_regions(self) -> FlashLayout<'d, Blocking> {\n        FlashLayout::new(self.inner)\n    }\n\n    /// Blocking read.\n    ///\n    /// NOTE: `offset` is an offset from the flash start, NOT an absolute address.\n    /// For example, to read address `0x0800_1234` you have to use offset `0x1234`.\n    pub fn blocking_read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Error> {\n        blocking_read(FLASH_BASE as u32, FLASH_SIZE as u32, offset, bytes)\n    }\n\n    /// Blocking write.\n    ///\n    /// NOTE: `offset` is an offset from the flash start, NOT an absolute address.\n    /// For example, to write address `0x0800_1234` you have to use offset `0x1234`.\n    pub fn blocking_write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Error> {\n        unsafe {\n            blocking_write(\n                FLASH_BASE as u32,\n                FLASH_SIZE as u32,\n                offset,\n                bytes,\n                write_chunk_unlocked,\n            )\n        }\n    }\n\n    /// Blocking erase.\n    ///\n    /// NOTE: `from` and `to` are offsets from the flash start, NOT an absolute address.\n    /// For example, to erase address `0x0801_0000` you have to use offset `0x1_0000`.\n    pub fn blocking_erase(&mut self, from: u32, to: u32) -> Result<(), Error> {\n        unsafe { blocking_erase(FLASH_BASE as u32, from, to, erase_sector_unlocked) }\n    }\n}\n\npub(super) fn blocking_read(base: u32, size: u32, offset: u32, bytes: &mut [u8]) -> Result<(), Error> {\n    if offset + bytes.len() as u32 > size {\n        return Err(Error::Size);\n    }\n\n    let start_address = base + offset;\n\n    #[cfg(flash_f4)]\n    family::assert_not_corrupted_read(start_address + bytes.len() as u32);\n\n    let flash_data = unsafe { core::slice::from_raw_parts(start_address as *const u8, bytes.len()) };\n    bytes.copy_from_slice(flash_data);\n    Ok(())\n}\n\npub(super) unsafe fn blocking_write(\n    base: u32,\n    size: u32,\n    offset: u32,\n    bytes: &[u8],\n    write_chunk: unsafe fn(u32, &[u8]) -> Result<(), Error>,\n) -> Result<(), Error> {\n    if offset + bytes.len() as u32 > size {\n        return Err(Error::Size);\n    }\n    if offset % WRITE_SIZE as u32 != 0 || bytes.len() % WRITE_SIZE != 0 {\n        return Err(Error::Unaligned);\n    }\n\n    let mut address = base + offset;\n    trace!(\n        \"Writing {} bytes at 0x{:x} (base=0x{:x}, offset=0x{:x})\",\n        bytes.len(),\n        address,\n        base,\n        offset\n    );\n\n    for chunk in bytes.chunks(WRITE_SIZE) {\n        write_chunk(address, chunk)?;\n        address += WRITE_SIZE as u32;\n    }\n    Ok(())\n}\n\npub(super) unsafe fn write_chunk_unlocked(address: u32, chunk: &[u8]) -> Result<(), Error> {\n    family::clear_all_err();\n    fence(Ordering::SeqCst);\n    family::unlock();\n    fence(Ordering::SeqCst);\n    family::enable_blocking_write();\n    fence(Ordering::SeqCst);\n\n    let _on_drop = OnDrop::new(|| {\n        family::disable_blocking_write();\n        fence(Ordering::SeqCst);\n        family::lock();\n    });\n\n    family::blocking_write(address, unwrap!(chunk.try_into()))\n}\n\npub(super) unsafe fn write_chunk_with_critical_section(address: u32, chunk: &[u8]) -> Result<(), Error> {\n    critical_section::with(|_| write_chunk_unlocked(address, chunk))\n}\n\npub(super) unsafe fn blocking_erase(\n    base: u32,\n    from: u32,\n    to: u32,\n    erase_sector: unsafe fn(&FlashSector) -> Result<(), Error>,\n) -> Result<(), Error> {\n    let start_address = base + from;\n    let end_address = base + to;\n    let regions = get_flash_regions();\n\n    ensure_sector_aligned(start_address, end_address, regions)?;\n\n    trace!(\"Erasing from 0x{:x} to 0x{:x}\", start_address, end_address);\n\n    let mut address = start_address;\n    while address < end_address {\n        let sector = get_sector(address, regions);\n        trace!(\"Erasing sector: {:?}\", sector);\n        erase_sector(&sector)?;\n        address += sector.size;\n    }\n    Ok(())\n}\n\npub(super) unsafe fn erase_sector_unlocked(sector: &FlashSector) -> Result<(), Error> {\n    family::clear_all_err();\n    fence(Ordering::SeqCst);\n    family::unlock();\n    fence(Ordering::SeqCst);\n\n    let _on_drop = OnDrop::new(|| family::lock());\n\n    family::blocking_erase_sector(sector)\n}\n\npub(super) unsafe fn erase_sector_with_critical_section(sector: &FlashSector) -> Result<(), Error> {\n    critical_section::with(|_| erase_sector_unlocked(sector))\n}\n\npub(super) fn get_sector(address: u32, regions: &[&FlashRegion]) -> FlashSector {\n    let mut current_bank = FlashBank::Bank1;\n    let mut bank_offset = 0;\n    for region in regions {\n        if region.bank != current_bank {\n            current_bank = region.bank;\n            bank_offset = 0;\n        }\n\n        if address >= region.base() && address < region.end() {\n            let index_in_region = (address - region.base()) / region.erase_size;\n            return FlashSector {\n                bank: region.bank,\n                index_in_bank: bank_offset + index_in_region as u8,\n                start: region.base() + index_in_region * region.erase_size,\n                size: region.erase_size,\n            };\n        }\n\n        bank_offset += region.sectors();\n    }\n\n    panic!(\"Flash sector not found\");\n}\n\npub(super) fn ensure_sector_aligned(\n    start_address: u32,\n    end_address: u32,\n    regions: &[&FlashRegion],\n) -> Result<(), Error> {\n    let mut address = start_address;\n    while address < end_address {\n        let sector = get_sector(address, regions);\n        if sector.start != address {\n            return Err(Error::Unaligned);\n        }\n        address += sector.size;\n    }\n    if address != end_address {\n        return Err(Error::Unaligned);\n    }\n    Ok(())\n}\n\nimpl<MODE> embedded_storage::nor_flash::ErrorType for Flash<'_, MODE> {\n    type Error = Error;\n}\n\nimpl<MODE> embedded_storage::nor_flash::ReadNorFlash for Flash<'_, MODE> {\n    const READ_SIZE: usize = READ_SIZE;\n\n    fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Self::Error> {\n        self.blocking_read(offset, bytes)\n    }\n\n    fn capacity(&self) -> usize {\n        FLASH_SIZE\n    }\n}\n\nimpl<MODE> embedded_storage::nor_flash::NorFlash for Flash<'_, MODE> {\n    const WRITE_SIZE: usize = WRITE_SIZE;\n    const ERASE_SIZE: usize = MAX_ERASE_SIZE;\n\n    fn write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Self::Error> {\n        self.blocking_write(offset, bytes)\n    }\n\n    fn erase(&mut self, from: u32, to: u32) -> Result<(), Self::Error> {\n        self.blocking_erase(from, to)\n    }\n}\n\nforeach_flash_region! {\n    ($type_name:ident, $write_size:literal, $erase_size:literal) => {\n        impl<MODE> crate::_generated::flash_regions::$type_name<'_, MODE> {\n            /// Blocking read.\n            ///\n            /// NOTE: `offset` is an offset from the flash start, NOT an absolute address.\n            /// For example, to read address `0x0800_1234` you have to use offset `0x1234`.\n            pub fn blocking_read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Error> {\n                blocking_read(self.0.base(), self.0.size, offset, bytes)\n            }\n        }\n\n        impl crate::_generated::flash_regions::$type_name<'_, Blocking> {\n            /// Blocking write.\n            ///\n            /// NOTE: `offset` is an offset from the flash start, NOT an absolute address.\n            /// For example, to write address `0x0800_1234` you have to use offset `0x1234`.\n            pub fn blocking_write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Error> {\n                unsafe { blocking_write(self.0.base(), self.0.size, offset, bytes, write_chunk_with_critical_section) }\n            }\n\n            /// Blocking erase.\n            ///\n            /// NOTE: `from` and `to` are offsets from the flash start, NOT an absolute address.\n            /// For example, to erase address `0x0801_0000` you have to use offset `0x1_0000`.\n            pub fn blocking_erase(&mut self, from: u32, to: u32) -> Result<(), Error> {\n                unsafe { blocking_erase(self.0.base(), from, to, erase_sector_with_critical_section) }\n            }\n        }\n\n        impl<MODE> embedded_storage::nor_flash::ErrorType for crate::_generated::flash_regions::$type_name<'_, MODE> {\n            type Error = Error;\n        }\n\n        impl<MODE> embedded_storage::nor_flash::ReadNorFlash for crate::_generated::flash_regions::$type_name<'_, MODE> {\n            const READ_SIZE: usize = READ_SIZE;\n\n            fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Self::Error> {\n                self.blocking_read(offset, bytes)\n            }\n\n            fn capacity(&self) -> usize {\n                self.0.size as usize\n            }\n        }\n\n        impl embedded_storage::nor_flash::NorFlash for crate::_generated::flash_regions::$type_name<'_, Blocking> {\n            const WRITE_SIZE: usize = $write_size;\n            const ERASE_SIZE: usize = $erase_size;\n\n            fn write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Self::Error> {\n                self.blocking_write(offset, bytes)\n            }\n\n            fn erase(&mut self, from: u32, to: u32) -> Result<(), Self::Error> {\n                self.blocking_erase(from, to)\n            }\n        }\n    };\n}\n"
  },
  {
    "path": "embassy-stm32/src/flash/eeprom.rs",
    "content": "use embassy_hal_internal::drop::OnDrop;\n\nuse super::{Blocking, EEPROM_BASE, EEPROM_SIZE, Error, Flash, family};\n\n#[cfg(eeprom)]\nimpl<'d> Flash<'d, Blocking> {\n    // --- Internal helpers ---\n\n    /// Checks if the given offset and size are within the EEPROM bounds.\n    fn check_eeprom_offset(&self, offset: u32, size: u32) -> Result<(), Error> {\n        if offset\n            .checked_add(size)\n            .filter(|&end| end <= EEPROM_SIZE as u32)\n            .is_some()\n        {\n            Ok(())\n        } else {\n            Err(Error::Size)\n        }\n    }\n\n    // --- Unlocked (unsafe, internal) functions ---\n\n    /// Writes a slice of bytes to EEPROM at the given offset without locking.\n    ///\n    /// # Safety\n    /// Caller must ensure EEPROM is unlocked and offset is valid.\n    unsafe fn eeprom_write_u8_slice_unlocked(&self, offset: u32, data: &[u8]) -> Result<(), Error> {\n        for (i, &byte) in data.iter().enumerate() {\n            let addr = EEPROM_BASE as u32 + offset + i as u32;\n            core::ptr::write_volatile(addr as *mut u8, byte);\n            family::wait_ready_blocking()?;\n            family::clear_all_err();\n        }\n        Ok(())\n    }\n\n    /// Writes a slice of u16 values to EEPROM at the given offset without locking.\n    ///\n    /// # Safety\n    /// Caller must ensure EEPROM is unlocked and offset is valid and aligned.\n    unsafe fn eeprom_write_u16_slice_unlocked(&self, offset: u32, data: &[u16]) -> Result<(), Error> {\n        for (i, &value) in data.iter().enumerate() {\n            let addr = EEPROM_BASE as u32 + offset + i as u32 * 2;\n            core::ptr::write_volatile(addr as *mut u16, value);\n            family::wait_ready_blocking()?;\n            family::clear_all_err();\n        }\n        Ok(())\n    }\n\n    /// Writes a slice of u32 values to EEPROM at the given offset without locking.\n    ///\n    /// # Safety\n    /// Caller must ensure EEPROM is unlocked and offset is valid and aligned.\n    unsafe fn eeprom_write_u32_slice_unlocked(&self, offset: u32, data: &[u32]) -> Result<(), Error> {\n        for (i, &value) in data.iter().enumerate() {\n            let addr = EEPROM_BASE as u32 + offset + i as u32 * 4;\n            core::ptr::write_volatile(addr as *mut u32, value);\n            family::wait_ready_blocking()?;\n            family::clear_all_err();\n        }\n        Ok(())\n    }\n\n    // --- Public, safe API ---\n\n    /// Writes a single byte to EEPROM at the given offset.\n    pub fn eeprom_write_u8(&mut self, offset: u32, value: u8) -> Result<(), Error> {\n        self.check_eeprom_offset(offset, 1)?;\n        unsafe {\n            family::unlock();\n            let _on_drop = OnDrop::new(|| family::lock());\n            self.eeprom_write_u8_slice_unlocked(offset, core::slice::from_ref(&value))?;\n        }\n        Ok(())\n    }\n\n    /// Writes a single 16-bit value to EEPROM at the given offset.\n    ///\n    /// Returns an error if the offset is not 2-byte aligned.\n    pub fn eeprom_write_u16(&mut self, offset: u32, value: u16) -> Result<(), Error> {\n        if offset % 2 != 0 {\n            return Err(Error::Unaligned);\n        }\n        self.check_eeprom_offset(offset, 2)?;\n        unsafe {\n            family::unlock();\n            let _on_drop = OnDrop::new(|| family::lock());\n            self.eeprom_write_u16_slice_unlocked(offset, core::slice::from_ref(&value))?;\n        }\n        Ok(())\n    }\n\n    /// Writes a single 32-bit value to EEPROM at the given offset.\n    ///\n    /// Returns an error if the offset is not 4-byte aligned.\n    pub fn eeprom_write_u32(&mut self, offset: u32, value: u32) -> Result<(), Error> {\n        if offset % 4 != 0 {\n            return Err(Error::Unaligned);\n        }\n        self.check_eeprom_offset(offset, 4)?;\n        unsafe {\n            family::unlock();\n            let _on_drop = OnDrop::new(|| family::lock());\n            self.eeprom_write_u32_slice_unlocked(offset, core::slice::from_ref(&value))?;\n        }\n        Ok(())\n    }\n\n    /// Writes a slice of bytes to EEPROM at the given offset.\n    pub fn eeprom_write_u8_slice(&mut self, offset: u32, data: &[u8]) -> Result<(), Error> {\n        self.check_eeprom_offset(offset, data.len() as u32)?;\n        unsafe {\n            family::unlock();\n            let _on_drop = OnDrop::new(|| family::lock());\n            self.eeprom_write_u8_slice_unlocked(offset, data)?;\n        }\n        Ok(())\n    }\n\n    /// Writes a slice of 16-bit values to EEPROM at the given offset.\n    ///\n    /// Returns an error if the offset is not 2-byte aligned.\n    pub fn eeprom_write_u16_slice(&mut self, offset: u32, data: &[u16]) -> Result<(), Error> {\n        if offset % 2 != 0 {\n            return Err(Error::Unaligned);\n        }\n        self.check_eeprom_offset(offset, data.len() as u32 * 2)?;\n        unsafe {\n            family::unlock();\n            let _on_drop = OnDrop::new(|| family::lock());\n            self.eeprom_write_u16_slice_unlocked(offset, data)?;\n        }\n        Ok(())\n    }\n\n    /// Writes a slice of 32-bit values to EEPROM at the given offset.\n    ///\n    /// Returns an error if the offset is not 4-byte aligned.\n    pub fn eeprom_write_u32_slice(&mut self, offset: u32, data: &[u32]) -> Result<(), Error> {\n        if offset % 4 != 0 {\n            return Err(Error::Unaligned);\n        }\n        self.check_eeprom_offset(offset, data.len() as u32 * 4)?;\n        unsafe {\n            family::unlock();\n            let _on_drop = OnDrop::new(|| family::lock());\n            self.eeprom_write_u32_slice_unlocked(offset, data)?;\n        }\n        Ok(())\n    }\n\n    /// Writes a byte slice to EEPROM at the given offset, handling alignment.\n    ///\n    /// This method will write unaligned prefix and suffix as bytes, and aligned middle as u32.\n    pub fn eeprom_write_slice(&mut self, offset: u32, data: &[u8]) -> Result<(), Error> {\n        self.check_eeprom_offset(offset, data.len() as u32)?;\n        let start = offset;\n        let misalign = (start % 4) as usize;\n        let prefix_len = if misalign == 0 {\n            0\n        } else {\n            (4 - misalign).min(data.len())\n        };\n        let (prefix, rest) = data.split_at(prefix_len);\n        let aligned_len = (rest.len() / 4) * 4;\n        let (bytes_for_u32_write, suffix) = rest.split_at(aligned_len);\n\n        unsafe {\n            family::unlock();\n            let _on_drop = OnDrop::new(|| family::lock());\n\n            if !prefix.is_empty() {\n                self.eeprom_write_u8_slice_unlocked(start, prefix)?;\n            }\n            if !bytes_for_u32_write.is_empty() {\n                let aligned_eeprom_offset = start + prefix_len as u32;\n                let base_eeprom_addr = EEPROM_BASE as u32 + aligned_eeprom_offset;\n                for (i, chunk) in bytes_for_u32_write.chunks_exact(4).enumerate() {\n                    // Safely read a u32 from a potentially unaligned pointer into the chunk.\n                    let value = (chunk.as_ptr() as *const u32).read_unaligned();\n                    let current_eeprom_addr = base_eeprom_addr + (i * 4) as u32;\n                    core::ptr::write_volatile(current_eeprom_addr as *mut u32, value);\n                    family::wait_ready_blocking()?;\n                    family::clear_all_err();\n                }\n            }\n            if !suffix.is_empty() {\n                let suffix_offset = start + (prefix_len + aligned_len) as u32;\n                self.eeprom_write_u8_slice_unlocked(suffix_offset, suffix)?;\n            }\n        }\n        Ok(())\n    }\n\n    /// Reads a single byte from EEPROM at the given offset.\n    pub fn eeprom_read_u8(&self, offset: u32) -> Result<u8, Error> {\n        self.check_eeprom_offset(offset, 1)?;\n        let addr = EEPROM_BASE as u32 + offset;\n        Ok(unsafe { core::ptr::read_volatile(addr as *const u8) })\n    }\n\n    /// Reads a single 16-bit value from EEPROM at the given offset.\n    ///\n    /// Returns an error if the offset is not 2-byte aligned.\n    pub fn eeprom_read_u16(&self, offset: u32) -> Result<u16, Error> {\n        if offset % 2 != 0 {\n            return Err(Error::Unaligned);\n        }\n        self.check_eeprom_offset(offset, 2)?;\n        let addr = EEPROM_BASE as u32 + offset;\n        Ok(unsafe { core::ptr::read_volatile(addr as *const u16) })\n    }\n\n    /// Reads a single 32-bit value from EEPROM at the given offset.\n    ///\n    /// Returns an error if the offset is not 4-byte aligned.\n    pub fn eeprom_read_u32(&self, offset: u32) -> Result<u32, Error> {\n        if offset % 4 != 0 {\n            return Err(Error::Unaligned);\n        }\n        self.check_eeprom_offset(offset, 4)?;\n        let addr = EEPROM_BASE as u32 + offset;\n        Ok(unsafe { core::ptr::read_volatile(addr as *const u32) })\n    }\n\n    /// Reads a slice of bytes from EEPROM at the given offset into the provided buffer.\n    pub fn eeprom_read_slice(&self, offset: u32, buf: &mut [u8]) -> Result<(), Error> {\n        self.check_eeprom_offset(offset, buf.len() as u32)?;\n        let addr = EEPROM_BASE as u32 + offset;\n        let src = unsafe { core::slice::from_raw_parts(addr as *const u8, buf.len()) };\n        buf.copy_from_slice(src);\n        Ok(())\n    }\n}\n"
  },
  {
    "path": "embassy-stm32/src/flash/f0.rs",
    "content": "use core::ptr::write_volatile;\nuse core::sync::atomic::{Ordering, fence};\n\nuse super::{FlashSector, WRITE_SIZE};\nuse crate::flash::Error;\nuse crate::pac;\n\npub(crate) unsafe fn lock() {\n    pac::FLASH.cr().modify(|w| w.set_lock(true));\n}\n\npub(crate) unsafe fn unlock() {\n    if pac::FLASH.cr().read().lock() {\n        pac::FLASH.keyr().write_value(0x4567_0123);\n        pac::FLASH.keyr().write_value(0xCDEF_89AB);\n    }\n}\n\npub(crate) unsafe fn enable_blocking_write() {\n    assert_eq!(0, WRITE_SIZE % 2);\n\n    pac::FLASH.cr().write(|w| w.set_pg(true));\n}\n\npub(crate) unsafe fn disable_blocking_write() {\n    pac::FLASH.cr().write(|w| w.set_pg(false));\n}\n\npub(crate) unsafe fn blocking_write(start_address: u32, buf: &[u8; WRITE_SIZE]) -> Result<(), Error> {\n    let mut address = start_address;\n    for chunk in buf.chunks(2) {\n        write_volatile(address as *mut u16, u16::from_le_bytes(unwrap!(chunk.try_into())));\n        address += chunk.len() as u32;\n\n        // prevents parallelism errors\n        fence(Ordering::SeqCst);\n    }\n\n    wait_ready_blocking()\n}\n\npub(crate) unsafe fn blocking_erase_sector(sector: &FlashSector) -> Result<(), Error> {\n    pac::FLASH.cr().modify(|w| {\n        w.set_per(true);\n    });\n\n    pac::FLASH.ar().write(|w| w.set_far(sector.start));\n\n    pac::FLASH.cr().modify(|w| {\n        w.set_strt(true);\n    });\n\n    let mut ret: Result<(), Error> = wait_ready_blocking();\n\n    if !pac::FLASH.sr().read().eop() {\n        trace!(\"FLASH: EOP not set\");\n        ret = Err(Error::Prog);\n    } else {\n        pac::FLASH.sr().write(|w| w.set_eop(true));\n    }\n\n    pac::FLASH.cr().modify(|w| w.set_per(false));\n\n    clear_all_err();\n    if ret.is_err() {\n        return ret;\n    }\n    Ok(())\n}\n\npub(crate) unsafe fn clear_all_err() {\n    // read and write back the same value.\n    // This clears all \"write 1 to clear\" bits.\n    pac::FLASH.sr().modify(|_| {});\n}\n\nunsafe fn wait_ready_blocking() -> Result<(), Error> {\n    loop {\n        let sr = pac::FLASH.sr().read();\n\n        if !sr.bsy() {\n            if sr.wrprt() {\n                return Err(Error::Protected);\n            }\n\n            if sr.pgerr() {\n                return Err(Error::Seq);\n            }\n\n            return Ok(());\n        }\n    }\n}\n"
  },
  {
    "path": "embassy-stm32/src/flash/f1f3.rs",
    "content": "use core::ptr::write_volatile;\nuse core::sync::atomic::{Ordering, fence};\n\nuse super::{FlashSector, WRITE_SIZE};\nuse crate::flash::Error;\nuse crate::pac;\n\npub(crate) unsafe fn lock() {\n    pac::FLASH.cr().modify(|w| w.set_lock(true));\n}\n\npub(crate) unsafe fn unlock() {\n    if pac::FLASH.cr().read().lock() {\n        pac::FLASH.keyr().write_value(0x4567_0123);\n        pac::FLASH.keyr().write_value(0xCDEF_89AB);\n    }\n}\n\npub(crate) unsafe fn enable_blocking_write() {\n    assert_eq!(0, WRITE_SIZE % 2);\n\n    pac::FLASH.cr().write(|w| w.set_pg(true));\n}\n\npub(crate) unsafe fn disable_blocking_write() {\n    pac::FLASH.cr().write(|w| w.set_pg(false));\n}\n\npub(crate) unsafe fn blocking_write(start_address: u32, buf: &[u8; WRITE_SIZE]) -> Result<(), Error> {\n    let mut address = start_address;\n    for chunk in buf.chunks(2) {\n        write_volatile(address as *mut u16, u16::from_le_bytes(unwrap!(chunk.try_into())));\n        address += chunk.len() as u32;\n\n        // prevents parallelism errors\n        fence(Ordering::SeqCst);\n\n        wait_ready_blocking()?;\n    }\n\n    Ok(())\n}\n\npub(crate) unsafe fn blocking_erase_sector(sector: &FlashSector) -> Result<(), Error> {\n    pac::FLASH.cr().modify(|w| {\n        w.set_per(true);\n    });\n\n    pac::FLASH.ar().write(|w| w.set_far(sector.start));\n\n    pac::FLASH.cr().modify(|w| {\n        w.set_strt(true);\n    });\n\n    // Wait for at least one clock cycle before reading the\n    // BSY bit, because there is a one-cycle delay between\n    // setting the STRT bit and the BSY bit being asserted\n    // by hardware. See STM32F105xx, STM32F107xx device errata,\n    // section 2.2.8, and also RM0316 Rev 10 section 4.2.3 for\n    // STM32F3xx series.\n    pac::FLASH.cr().read();\n\n    let mut ret: Result<(), Error> = wait_ready_blocking();\n\n    if !pac::FLASH.sr().read().eop() {\n        trace!(\"FLASH: EOP not set\");\n        ret = Err(Error::Prog);\n    } else {\n        pac::FLASH.sr().write(|w| w.set_eop(true));\n    }\n\n    pac::FLASH.cr().modify(|w| w.set_per(false));\n\n    clear_all_err();\n    if ret.is_err() {\n        return ret;\n    }\n    Ok(())\n}\n\npub(crate) unsafe fn clear_all_err() {\n    // read and write back the same value.\n    // This clears all \"write 1 to clear\" bits.\n    pac::FLASH.sr().modify(|_| {});\n}\n\nunsafe fn wait_ready_blocking() -> Result<(), Error> {\n    loop {\n        let sr = pac::FLASH.sr().read();\n\n        if !sr.bsy() {\n            if sr.wrprterr() {\n                return Err(Error::Protected);\n            }\n\n            if sr.pgerr() {\n                return Err(Error::Seq);\n            }\n\n            return Ok(());\n        }\n    }\n}\n"
  },
  {
    "path": "embassy-stm32/src/flash/f2.rs",
    "content": "use core::ptr::write_volatile;\nuse core::sync::atomic::{AtomicBool, Ordering, fence};\n\nuse pac::flash::regs::Sr;\n\nuse super::{FlashBank, FlashSector, WRITE_SIZE, get_flash_regions};\nuse crate::flash::Error;\nuse crate::pac;\n\nstatic DATA_CACHE_WAS_ENABLED: AtomicBool = AtomicBool::new(false);\n\nimpl FlashSector {\n    const fn snb(&self) -> u8 {\n        ((self.bank as u8) << 4) + self.index_in_bank\n    }\n}\n\npub(crate) unsafe fn lock() {\n    pac::FLASH.cr().modify(|w| w.set_lock(true));\n}\n\npub(crate) unsafe fn unlock() {\n    if pac::FLASH.cr().read().lock() {\n        pac::FLASH.keyr().write_value(0x4567_0123);\n        pac::FLASH.keyr().write_value(0xCDEF_89AB);\n    }\n}\n\npub(crate) unsafe fn enable_blocking_write() {\n    assert_eq!(0, WRITE_SIZE % 4);\n    save_data_cache_state();\n\n    pac::FLASH.cr().write(|w| {\n        w.set_pg(true);\n        w.set_psize(pac::flash::vals::Psize::PSIZE32);\n    });\n}\n\npub(crate) unsafe fn disable_blocking_write() {\n    pac::FLASH.cr().write(|w| w.set_pg(false));\n    restore_data_cache_state();\n}\n\npub(crate) unsafe fn blocking_write(start_address: u32, buf: &[u8; WRITE_SIZE]) -> Result<(), Error> {\n    write_start(start_address, buf);\n    blocking_wait_ready()\n}\n\nunsafe fn write_start(start_address: u32, buf: &[u8; WRITE_SIZE]) {\n    let mut address = start_address;\n    for val in buf.chunks(4) {\n        write_volatile(address as *mut u32, u32::from_le_bytes(unwrap!(val.try_into())));\n        address += val.len() as u32;\n\n        // prevents parallelism errors\n        fence(Ordering::SeqCst);\n    }\n}\n\npub(crate) unsafe fn blocking_erase_sector(sector: &FlashSector) -> Result<(), Error> {\n    save_data_cache_state();\n\n    trace!(\"Blocking erasing sector number {}\", sector.snb());\n\n    pac::FLASH.cr().modify(|w| {\n        w.set_ser(true);\n        w.set_snb(sector.snb())\n    });\n\n    pac::FLASH.cr().modify(|w| {\n        w.set_strt(true);\n    });\n\n    let ret: Result<(), Error> = blocking_wait_ready();\n    clear_all_err();\n    restore_data_cache_state();\n    ret\n}\n\npub(crate) unsafe fn clear_all_err() {\n    // read and write back the same value.\n    // This clears all \"write 1 to clear\" bits.\n    pac::FLASH.sr().modify(|_| {});\n}\n\nunsafe fn blocking_wait_ready() -> Result<(), Error> {\n    loop {\n        let sr = pac::FLASH.sr().read();\n\n        if !sr.bsy() {\n            return get_result(sr);\n        }\n    }\n}\n\nfn get_result(sr: Sr) -> Result<(), Error> {\n    if sr.pgserr() {\n        Err(Error::Seq)\n    } else if sr.pgperr() {\n        Err(Error::Parallelism)\n    } else if sr.pgaerr() {\n        Err(Error::Unaligned)\n    } else if sr.wrperr() {\n        Err(Error::Protected)\n    } else {\n        Ok(())\n    }\n}\n\nfn save_data_cache_state() {\n    let dual_bank = unwrap!(get_flash_regions().last()).bank == FlashBank::Bank2;\n    if dual_bank {\n        // Disable data cache during write/erase if there are two banks, see errata 2.2.12\n        let dcen = pac::FLASH.acr().read().dcen();\n        DATA_CACHE_WAS_ENABLED.store(dcen, Ordering::Relaxed);\n        if dcen {\n            pac::FLASH.acr().modify(|w| w.set_dcen(false));\n        }\n    }\n}\n\nfn restore_data_cache_state() {\n    let dual_bank = unwrap!(get_flash_regions().last()).bank == FlashBank::Bank2;\n    if dual_bank {\n        // Restore data cache if it was enabled\n        let dcen = DATA_CACHE_WAS_ENABLED.load(Ordering::Relaxed);\n        if dcen {\n            // Reset data cache before we enable it again\n            pac::FLASH.acr().modify(|w| w.set_dcrst(true));\n            pac::FLASH.acr().modify(|w| w.set_dcrst(false));\n            pac::FLASH.acr().modify(|w| w.set_dcen(true))\n        }\n    }\n}\n"
  },
  {
    "path": "embassy-stm32/src/flash/f4.rs",
    "content": "use core::ptr::write_volatile;\nuse core::sync::atomic::{AtomicBool, Ordering, fence};\n\nuse embassy_sync::waitqueue::AtomicWaker;\nuse pac::flash::regs::Sr;\n\nuse super::{FlashBank, FlashSector, WRITE_SIZE, get_flash_regions};\nuse crate::_generated::FLASH_SIZE;\nuse crate::flash::Error;\nuse crate::pac;\n\nstatic WAKER: AtomicWaker = AtomicWaker::new();\nstatic DATA_CACHE_WAS_ENABLED: AtomicBool = AtomicBool::new(false);\n\nimpl FlashSector {\n    const fn snb(&self) -> u8 {\n        ((self.bank as u8) << 4) + self.index_in_bank\n    }\n}\n\npub(crate) unsafe fn on_interrupt() {\n    // Clear IRQ flags\n    pac::FLASH.sr().write(|w| {\n        w.set_operr(true);\n        w.set_eop(true);\n    });\n\n    WAKER.wake();\n}\n\npub(crate) unsafe fn lock() {\n    pac::FLASH.cr().modify(|w| w.set_lock(true));\n}\n\npub(crate) unsafe fn unlock() {\n    if pac::FLASH.cr().read().lock() {\n        pac::FLASH.keyr().write_value(0x4567_0123);\n        pac::FLASH.keyr().write_value(0xCDEF_89AB);\n    }\n}\n\npub(crate) unsafe fn enable_write() {\n    assert_eq!(0, WRITE_SIZE % 4);\n    save_data_cache_state();\n\n    pac::FLASH.cr().write(|w| {\n        w.set_pg(true);\n        w.set_psize(pac::flash::vals::Psize::PSIZE32);\n        w.set_eopie(true);\n        w.set_errie(true);\n    });\n}\n\npub(crate) unsafe fn disable_write() {\n    pac::FLASH.cr().write(|w| {\n        w.set_pg(false);\n        w.set_eopie(false);\n        w.set_errie(false);\n    });\n    restore_data_cache_state();\n}\n\npub(crate) unsafe fn enable_blocking_write() {\n    assert_eq!(0, WRITE_SIZE % 4);\n    save_data_cache_state();\n\n    pac::FLASH.cr().write(|w| {\n        w.set_pg(true);\n        w.set_psize(pac::flash::vals::Psize::PSIZE32);\n    });\n}\n\npub(crate) unsafe fn disable_blocking_write() {\n    pac::FLASH.cr().write(|w| w.set_pg(false));\n    restore_data_cache_state();\n}\n\npub(crate) async unsafe fn write(start_address: u32, buf: &[u8; WRITE_SIZE]) -> Result<(), Error> {\n    write_start(start_address, buf);\n    wait_ready().await\n}\n\npub(crate) unsafe fn blocking_write(start_address: u32, buf: &[u8; WRITE_SIZE]) -> Result<(), Error> {\n    write_start(start_address, buf);\n    blocking_wait_ready()\n}\n\nunsafe fn write_start(start_address: u32, buf: &[u8; WRITE_SIZE]) {\n    let mut address = start_address;\n    for val in buf.chunks(4) {\n        write_volatile(address as *mut u32, u32::from_le_bytes(unwrap!(val.try_into())));\n        address += val.len() as u32;\n\n        // prevents parallelism errors\n        fence(Ordering::SeqCst);\n    }\n}\n\npub(crate) async unsafe fn erase_sector(sector: &FlashSector) -> Result<(), Error> {\n    save_data_cache_state();\n\n    trace!(\"Erasing sector number {}\", sector.snb());\n\n    pac::FLASH.cr().modify(|w| {\n        w.set_ser(true);\n        w.set_snb(sector.snb());\n        w.set_eopie(true);\n        w.set_errie(true);\n    });\n\n    pac::FLASH.cr().modify(|w| {\n        w.set_strt(true);\n    });\n\n    let ret: Result<(), Error> = wait_ready().await;\n    pac::FLASH.cr().modify(|w| {\n        w.set_eopie(false);\n        w.set_errie(false);\n    });\n    clear_all_err();\n    restore_data_cache_state();\n    ret\n}\n\npub(crate) unsafe fn blocking_erase_sector(sector: &FlashSector) -> Result<(), Error> {\n    save_data_cache_state();\n\n    trace!(\"Blocking erasing sector number {}\", sector.snb());\n\n    pac::FLASH.cr().modify(|w| {\n        w.set_ser(true);\n        w.set_snb(sector.snb())\n    });\n\n    pac::FLASH.cr().modify(|w| {\n        w.set_strt(true);\n    });\n\n    let ret: Result<(), Error> = blocking_wait_ready();\n    clear_all_err();\n    restore_data_cache_state();\n    ret\n}\n\npub(crate) fn clear_all_err() {\n    // read and write back the same value.\n    // This clears all \"write 1 to clear\" bits.\n    pac::FLASH.sr().modify(|_| {});\n}\n\npub(crate) async fn wait_ready() -> Result<(), Error> {\n    use core::future::poll_fn;\n    use core::task::Poll;\n\n    poll_fn(|cx| {\n        WAKER.register(cx.waker());\n\n        let sr = pac::FLASH.sr().read();\n        if !sr.bsy() {\n            Poll::Ready(get_result(sr))\n        } else {\n            return Poll::Pending;\n        }\n    })\n    .await\n}\n\nunsafe fn blocking_wait_ready() -> Result<(), Error> {\n    loop {\n        let sr = pac::FLASH.sr().read();\n\n        if !sr.bsy() {\n            return get_result(sr);\n        }\n    }\n}\n\nfn get_result(sr: Sr) -> Result<(), Error> {\n    if sr.pgserr() {\n        Err(Error::Seq)\n    } else if sr.pgperr() {\n        Err(Error::Parallelism)\n    } else if sr.pgaerr() {\n        Err(Error::Unaligned)\n    } else if sr.wrperr() {\n        Err(Error::Protected)\n    } else {\n        Ok(())\n    }\n}\n\nfn save_data_cache_state() {\n    let dual_bank = unwrap!(get_flash_regions().last()).bank == FlashBank::Bank2;\n    if dual_bank {\n        // Disable data cache during write/erase if there are two banks, see errata 2.2.12\n        let dcen = pac::FLASH.acr().read().dcen();\n        DATA_CACHE_WAS_ENABLED.store(dcen, Ordering::Relaxed);\n        if dcen {\n            pac::FLASH.acr().modify(|w| w.set_dcen(false));\n        }\n    }\n}\n\nfn restore_data_cache_state() {\n    let dual_bank = unwrap!(get_flash_regions().last()).bank == FlashBank::Bank2;\n    if dual_bank {\n        // Restore data cache if it was enabled\n        let dcen = DATA_CACHE_WAS_ENABLED.load(Ordering::Relaxed);\n        if dcen {\n            // Reset data cache before we enable it again\n            pac::FLASH.acr().modify(|w| w.set_dcrst(true));\n            pac::FLASH.acr().modify(|w| w.set_dcrst(false));\n            pac::FLASH.acr().modify(|w| w.set_dcen(true))\n        }\n    }\n}\n\npub(crate) fn assert_not_corrupted_read(end_address: u32) {\n    #[allow(unused)]\n    const REVISION_3: u16 = 0x2001;\n\n    #[allow(unused)]\n    let second_bank_read =\n        unwrap!(get_flash_regions().last()).bank == FlashBank::Bank2 && end_address > (FLASH_SIZE / 2) as u32;\n\n    #[cfg(any(\n        feature = \"stm32f427ai\",\n        feature = \"stm32f427ii\",\n        feature = \"stm32f427vi\",\n        feature = \"stm32f427zi\",\n        feature = \"stm32f429ai\",\n        feature = \"stm32f429bi\",\n        feature = \"stm32f429ii\",\n        feature = \"stm32f429ni\",\n        feature = \"stm32f429vi\",\n        feature = \"stm32f429zi\",\n        feature = \"stm32f437ai\",\n        feature = \"stm32f437ii\",\n        feature = \"stm32f437vi\",\n        feature = \"stm32f437zi\",\n        feature = \"stm32f439ai\",\n        feature = \"stm32f439bi\",\n        feature = \"stm32f439ii\",\n        feature = \"stm32f439ni\",\n        feature = \"stm32f439vi\",\n        feature = \"stm32f439zi\",\n    ))]\n    if second_bank_read && pac::DBGMCU.idcode().read().rev_id() < REVISION_3 && !pa12_is_output_pull_low() {\n        panic!(\n            \"Read corruption for stm32f42xxI and stm32f43xxI when PA12 is in use for chips below revision 3, see errata 2.2.11\"\n        );\n    }\n\n    #[cfg(any(\n        feature = \"stm32f427ag\",\n        feature = \"stm32f427ig\",\n        feature = \"stm32f427vg\",\n        feature = \"stm32f427zg\",\n        feature = \"stm32f429ag\",\n        feature = \"stm32f429bg\",\n        feature = \"stm32f429ig\",\n        feature = \"stm32f429ng\",\n        feature = \"stm32f429vg\",\n        feature = \"stm32f429zg\",\n        feature = \"stm32f437ig\",\n        feature = \"stm32f437vg\",\n        feature = \"stm32f437zg\",\n        feature = \"stm32f439bg\",\n        feature = \"stm32f439ig\",\n        feature = \"stm32f439ng\",\n        feature = \"stm32f439vg\",\n        feature = \"stm32f439zg\",\n    ))]\n    if second_bank_read && pac::DBGMCU.idcode().read().rev_id() < REVISION_3 && !pa12_is_output_pull_low() {\n        panic!(\n            \"Read corruption for stm32f42xxG and stm32f43xxG in dual bank mode when PA12 is in use for chips below revision 3, see errata 2.2.11\"\n        );\n    }\n}\n\n#[allow(unused)]\nfn pa12_is_output_pull_low() -> bool {\n    use pac::GPIOA;\n    use pac::gpio::vals;\n    const PIN: usize = 12;\n    GPIOA.moder().read().moder(PIN) == vals::Moder::OUTPUT\n        && GPIOA.pupdr().read().pupdr(PIN) == vals::Pupdr::PULL_DOWN\n        && GPIOA.odr().read().odr(PIN) == vals::Odr::LOW\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n    use crate::flash::{FlashBank, get_sector};\n\n    #[test]\n    #[cfg(stm32f429)]\n    fn can_get_sector() {\n        const SMALL_SECTOR_SIZE: u32 = 16 * 1024;\n        const MEDIUM_SECTOR_SIZE: u32 = 64 * 1024;\n        const LARGE_SECTOR_SIZE: u32 = 128 * 1024;\n\n        if !cfg!(feature = \"dual-bank\") {\n            let assert_sector = |snb: u8, index_in_bank: u8, start: u32, size: u32, address: u32| {\n                let sector = get_sector(address, crate::flash::get_flash_regions());\n                assert_eq!(snb, sector.snb());\n                assert_eq!(\n                    FlashSector {\n                        bank: sector.bank,\n                        index_in_bank,\n                        start,\n                        size\n                    },\n                    sector\n                );\n            };\n\n            assert_sector(0x00, 0, 0x0800_0000, SMALL_SECTOR_SIZE, 0x0800_0000);\n            assert_sector(0x00, 0, 0x0800_0000, SMALL_SECTOR_SIZE, 0x0800_3FFF);\n            assert_sector(0x03, 3, 0x0800_C000, SMALL_SECTOR_SIZE, 0x0800_C000);\n            assert_sector(0x03, 3, 0x0800_C000, SMALL_SECTOR_SIZE, 0x0800_FFFF);\n\n            assert_sector(0x04, 4, 0x0801_0000, MEDIUM_SECTOR_SIZE, 0x0801_0000);\n            assert_sector(0x04, 4, 0x0801_0000, MEDIUM_SECTOR_SIZE, 0x0801_FFFF);\n\n            assert_sector(0x05, 5, 0x0802_0000, LARGE_SECTOR_SIZE, 0x0802_0000);\n            assert_sector(0x05, 5, 0x0802_0000, LARGE_SECTOR_SIZE, 0x0803_FFFF);\n            assert_sector(0x0B, 11, 0x080E_0000, LARGE_SECTOR_SIZE, 0x080E_0000);\n            assert_sector(0x0B, 11, 0x080E_0000, LARGE_SECTOR_SIZE, 0x080F_FFFF);\n        } else {\n            let assert_sector = |snb: u8, bank: FlashBank, index_in_bank: u8, start: u32, size: u32, address: u32| {\n                let sector = get_sector(address, crate::flash::get_flash_regions());\n                assert_eq!(snb, sector.snb());\n                assert_eq!(\n                    FlashSector {\n                        bank,\n                        index_in_bank,\n                        start,\n                        size\n                    },\n                    sector\n                )\n            };\n\n            assert_sector(0x00, FlashBank::Bank1, 0, 0x0800_0000, SMALL_SECTOR_SIZE, 0x0800_0000);\n            assert_sector(0x00, FlashBank::Bank1, 0, 0x0800_0000, SMALL_SECTOR_SIZE, 0x0800_3FFF);\n            assert_sector(0x03, FlashBank::Bank1, 3, 0x0800_C000, SMALL_SECTOR_SIZE, 0x0800_C000);\n            assert_sector(0x03, FlashBank::Bank1, 3, 0x0800_C000, SMALL_SECTOR_SIZE, 0x0800_FFFF);\n\n            assert_sector(0x04, FlashBank::Bank1, 4, 0x0801_0000, MEDIUM_SECTOR_SIZE, 0x0801_0000);\n            assert_sector(0x04, FlashBank::Bank1, 4, 0x0801_0000, MEDIUM_SECTOR_SIZE, 0x0801_FFFF);\n\n            assert_sector(0x05, FlashBank::Bank1, 5, 0x0802_0000, LARGE_SECTOR_SIZE, 0x0802_0000);\n            assert_sector(0x05, FlashBank::Bank1, 5, 0x0802_0000, LARGE_SECTOR_SIZE, 0x0803_FFFF);\n            assert_sector(0x07, FlashBank::Bank1, 7, 0x0806_0000, LARGE_SECTOR_SIZE, 0x0806_0000);\n            assert_sector(0x07, FlashBank::Bank1, 7, 0x0806_0000, LARGE_SECTOR_SIZE, 0x0807_FFFF);\n\n            assert_sector(0x10, FlashBank::Bank2, 0, 0x0808_0000, SMALL_SECTOR_SIZE, 0x0808_0000);\n            assert_sector(0x10, FlashBank::Bank2, 0, 0x0808_0000, SMALL_SECTOR_SIZE, 0x0808_3FFF);\n            assert_sector(0x13, FlashBank::Bank2, 3, 0x0808_C000, SMALL_SECTOR_SIZE, 0x0808_C000);\n            assert_sector(0x13, FlashBank::Bank2, 3, 0x0808_C000, SMALL_SECTOR_SIZE, 0x0808_FFFF);\n\n            assert_sector(0x14, FlashBank::Bank2, 4, 0x0809_0000, MEDIUM_SECTOR_SIZE, 0x0809_0000);\n            assert_sector(0x14, FlashBank::Bank2, 4, 0x0809_0000, MEDIUM_SECTOR_SIZE, 0x0809_FFFF);\n\n            assert_sector(0x15, FlashBank::Bank2, 5, 0x080A_0000, LARGE_SECTOR_SIZE, 0x080A_0000);\n            assert_sector(0x15, FlashBank::Bank2, 5, 0x080A_0000, LARGE_SECTOR_SIZE, 0x080B_FFFF);\n            assert_sector(0x17, FlashBank::Bank2, 7, 0x080E_0000, LARGE_SECTOR_SIZE, 0x080E_0000);\n            assert_sector(0x17, FlashBank::Bank2, 7, 0x080E_0000, LARGE_SECTOR_SIZE, 0x080F_FFFF);\n        }\n    }\n}\n\n#[cfg(all(bank_setup_configurable))]\npub(crate) fn check_bank_setup() {\n    if cfg!(feature = \"single-bank\") && pac::FLASH.optcr().read().db1m() {\n        panic!(\n            \"Embassy is configured as single-bank, but the hardware is running in dual-bank mode. Change the hardware by changing the db1m value in the user option bytes or configure embassy to use dual-bank config\"\n        );\n    }\n    if cfg!(feature = \"dual-bank\") && !pac::FLASH.optcr().read().db1m() {\n        panic!(\n            \"Embassy is configured as dual-bank, but the hardware is running in single-bank mode. Change the hardware by changing the db1m value in the user option bytes or configure embassy to use single-bank config\"\n        );\n    }\n}\n"
  },
  {
    "path": "embassy-stm32/src/flash/f7.rs",
    "content": "use core::ptr::write_volatile;\nuse core::sync::atomic::{Ordering, fence};\n\nuse super::{FlashSector, WRITE_SIZE};\nuse crate::flash::Error;\nuse crate::pac;\n\nimpl FlashSector {\n    const fn snb(&self) -> u8 {\n        ((self.bank as u8) << 4) + self.index_in_bank\n    }\n}\n\npub(crate) unsafe fn lock() {\n    pac::FLASH.cr().modify(|w| w.set_lock(true));\n}\n\npub(crate) unsafe fn unlock() {\n    if pac::FLASH.cr().read().lock() {\n        pac::FLASH.keyr().write_value(0x4567_0123);\n        pac::FLASH.keyr().write_value(0xCDEF_89AB);\n    }\n}\n\npub(crate) unsafe fn enable_blocking_write() {\n    assert_eq!(0, WRITE_SIZE % 4);\n\n    pac::FLASH.cr().write(|w| {\n        w.set_pg(true);\n        w.set_psize(pac::flash::vals::Psize::PSIZE32);\n    });\n}\n\npub(crate) unsafe fn disable_blocking_write() {\n    pac::FLASH.cr().write(|w| w.set_pg(false));\n}\n\npub(crate) unsafe fn blocking_write(start_address: u32, buf: &[u8; WRITE_SIZE]) -> Result<(), Error> {\n    let mut address = start_address;\n    for val in buf.chunks(4) {\n        write_volatile(address as *mut u32, u32::from_le_bytes(unwrap!(val.try_into())));\n        address += val.len() as u32;\n\n        // prevents parallelism errors\n        fence(Ordering::SeqCst);\n    }\n\n    blocking_wait_ready()\n}\n\npub(crate) unsafe fn blocking_erase_sector(sector: &FlashSector) -> Result<(), Error> {\n    pac::FLASH.cr().modify(|w| {\n        w.set_ser(true);\n        w.set_snb(sector.snb())\n    });\n\n    pac::FLASH.cr().modify(|w| {\n        w.set_strt(true);\n    });\n\n    let ret: Result<(), Error> = blocking_wait_ready();\n    pac::FLASH.cr().modify(|w| w.set_ser(false));\n    clear_all_err();\n    ret\n}\n\npub(crate) unsafe fn clear_all_err() {\n    // read and write back the same value.\n    // This clears all \"write 1 to clear\" bits.\n    pac::FLASH.sr().modify(|_| {});\n}\n\nunsafe fn blocking_wait_ready() -> Result<(), Error> {\n    loop {\n        let sr = pac::FLASH.sr().read();\n\n        if !sr.bsy() {\n            if sr.erserr() {\n                return Err(Error::Seq);\n            }\n\n            if sr.pgperr() {\n                return Err(Error::Parallelism);\n            }\n\n            if sr.pgaerr() {\n                return Err(Error::Unaligned);\n            }\n\n            if sr.wrperr() {\n                return Err(Error::Protected);\n            }\n\n            return Ok(());\n        }\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n    use crate::flash::{FlashBank, get_sector};\n\n    #[test]\n    #[cfg(stm32f732)]\n    fn can_get_sector() {\n        const SMALL_SECTOR_SIZE: u32 = 16 * 1024;\n        const MEDIUM_SECTOR_SIZE: u32 = 64 * 1024;\n        const LARGE_SECTOR_SIZE: u32 = 128 * 1024;\n\n        let assert_sector = |index_in_bank: u8, start: u32, size: u32, address: u32| {\n            assert_eq!(\n                FlashSector {\n                    bank: FlashBank::Bank1,\n                    index_in_bank,\n                    start,\n                    size\n                },\n                get_sector(address, crate::flash::get_flash_regions())\n            )\n        };\n\n        assert_sector(0, 0x0800_0000, SMALL_SECTOR_SIZE, 0x0800_0000);\n        assert_sector(0, 0x0800_0000, SMALL_SECTOR_SIZE, 0x0800_3FFF);\n        assert_sector(3, 0x0800_C000, SMALL_SECTOR_SIZE, 0x0800_C000);\n        assert_sector(3, 0x0800_C000, SMALL_SECTOR_SIZE, 0x0800_FFFF);\n\n        assert_sector(4, 0x0801_0000, MEDIUM_SECTOR_SIZE, 0x0801_0000);\n        assert_sector(4, 0x0801_0000, MEDIUM_SECTOR_SIZE, 0x0801_FFFF);\n\n        assert_sector(5, 0x0802_0000, LARGE_SECTOR_SIZE, 0x0802_0000);\n        assert_sector(5, 0x0802_0000, LARGE_SECTOR_SIZE, 0x0803_FFFF);\n        assert_sector(7, 0x0806_0000, LARGE_SECTOR_SIZE, 0x0806_0000);\n        assert_sector(7, 0x0806_0000, LARGE_SECTOR_SIZE, 0x0807_FFFF);\n    }\n\n    #[test]\n    #[cfg(all(stm32f769, feature = \"single-bank\"))]\n    fn can_get_sector() {\n        const SMALL_SECTOR_SIZE: u32 = 32 * 1024;\n        const MEDIUM_SECTOR_SIZE: u32 = 128 * 1024;\n        const LARGE_SECTOR_SIZE: u32 = 256 * 1024;\n\n        let assert_sector = |index_in_bank: u8, start: u32, size: u32, address: u32| {\n            assert_eq!(\n                FlashSector {\n                    bank: FlashBank::Bank1,\n                    index_in_bank,\n                    start,\n                    size\n                },\n                get_sector(address, crate::flash::get_flash_regions())\n            )\n        };\n\n        assert_sector(0, 0x0800_0000, SMALL_SECTOR_SIZE, 0x0800_0000);\n        assert_sector(0, 0x0800_0000, SMALL_SECTOR_SIZE, 0x0800_7FFF);\n        assert_sector(3, 0x0801_8000, SMALL_SECTOR_SIZE, 0x0801_8000);\n        assert_sector(3, 0x0801_8000, SMALL_SECTOR_SIZE, 0x0801_FFFF);\n\n        assert_sector(4, 0x0802_0000, MEDIUM_SECTOR_SIZE, 0x0802_0000);\n        assert_sector(4, 0x0802_0000, MEDIUM_SECTOR_SIZE, 0x0803_FFFF);\n\n        assert_sector(5, 0x0804_0000, LARGE_SECTOR_SIZE, 0x0804_0000);\n        assert_sector(5, 0x0804_0000, LARGE_SECTOR_SIZE, 0x0807_FFFF);\n        assert_sector(7, 0x080C_0000, LARGE_SECTOR_SIZE, 0x080C_0000);\n        assert_sector(7, 0x080C_0000, LARGE_SECTOR_SIZE, 0x080F_FFFF);\n    }\n}\n\n#[cfg(all(bank_setup_configurable))]\npub(crate) fn check_bank_setup() {\n    if cfg!(feature = \"single-bank\") && !pac::FLASH.optcr().read().n_dbank() {\n        panic!(\n            \"Embassy is configured as single-bank, but the hardware is running in dual-bank mode. Change the hardware by changing the ndbank value in the user option bytes or configure embassy to use dual-bank config\"\n        );\n    }\n    if cfg!(feature = \"dual-bank\") && pac::FLASH.optcr().read().n_dbank() {\n        panic!(\n            \"Embassy is configured as dual-bank, but the hardware is running in single-bank mode. Change the hardware by changing the ndbank value in the user option bytes or configure embassy to use single-bank config\"\n        );\n    }\n}\n"
  },
  {
    "path": "embassy-stm32/src/flash/g.rs",
    "content": "use core::ptr::write_volatile;\n#[cfg(any(flash_g4c2, flash_g4c3, flash_g4c4))]\nuse core::sync::atomic::AtomicBool;\nuse core::sync::atomic::{Ordering, fence};\n\nuse cortex_m::interrupt;\nuse embassy_sync::waitqueue::AtomicWaker;\nuse pac::flash::regs::Sr;\n\nuse super::{FlashSector, WRITE_SIZE};\nuse crate::flash::Error;\nuse crate::pac;\n\nstatic WAKER: AtomicWaker = AtomicWaker::new();\n\n// G4 has data cache that needs to be handled during flash operations\n#[cfg(any(flash_g4c2, flash_g4c3, flash_g4c4))]\nstatic DATA_CACHE_WAS_ENABLED: AtomicBool = AtomicBool::new(false);\n\npub(crate) unsafe fn on_interrupt() {\n    // Clear IRQ flags (EOP and error flags are write-1-to-clear)\n    pac::FLASH.sr().write(|w| {\n        w.set_eop(true);\n        w.set_operr(true);\n        w.set_progerr(true);\n        w.set_wrperr(true);\n        w.set_pgaerr(true);\n        w.set_sizerr(true);\n        w.set_pgserr(true);\n    });\n\n    WAKER.wake();\n}\n\npub(crate) unsafe fn lock() {\n    pac::FLASH.cr().modify(|w| w.set_lock(true));\n}\n\npub(crate) unsafe fn unlock() {\n    // Wait, while the memory interface is busy.\n    wait_busy();\n\n    // Unlock flash\n    if pac::FLASH.cr().read().lock() {\n        pac::FLASH.keyr().write_value(0x4567_0123);\n        pac::FLASH.keyr().write_value(0xCDEF_89AB);\n    }\n}\n\npub(crate) unsafe fn enable_write() {\n    assert_eq!(0, WRITE_SIZE % 4);\n    save_data_cache_state();\n\n    pac::FLASH.cr().write(|w| {\n        w.set_pg(true);\n        w.set_eopie(true);\n        w.set_errie(true);\n    });\n}\n\npub(crate) unsafe fn disable_write() {\n    pac::FLASH.cr().write(|w| {\n        w.set_pg(false);\n        w.set_eopie(false);\n        w.set_errie(false);\n    });\n    restore_data_cache_state();\n}\n\npub(crate) unsafe fn enable_blocking_write() {\n    assert_eq!(0, WRITE_SIZE % 4);\n    save_data_cache_state();\n    pac::FLASH.cr().write(|w| w.set_pg(true));\n}\n\npub(crate) unsafe fn disable_blocking_write() {\n    pac::FLASH.cr().write(|w| w.set_pg(false));\n    restore_data_cache_state();\n}\n\npub(crate) async unsafe fn write(start_address: u32, buf: &[u8; WRITE_SIZE]) -> Result<(), Error> {\n    write_start(start_address, buf);\n    wait_ready().await\n}\n\npub(crate) unsafe fn blocking_write(start_address: u32, buf: &[u8; WRITE_SIZE]) -> Result<(), Error> {\n    write_start(start_address, buf);\n    wait_ready_blocking()\n}\n\nunsafe fn write_start(start_address: u32, buf: &[u8; WRITE_SIZE]) {\n    let mut address = start_address;\n    for val in buf.chunks(4) {\n        write_volatile(address as *mut u32, u32::from_le_bytes(unwrap!(val.try_into())));\n        address += val.len() as u32;\n\n        // prevents parallelism errors\n        fence(Ordering::SeqCst);\n    }\n}\n\npub(crate) async unsafe fn erase_sector(sector: &FlashSector) -> Result<(), Error> {\n    wait_busy();\n    clear_all_err();\n    save_data_cache_state();\n\n    interrupt::free(|_| {\n        pac::FLASH.cr().modify(|w| {\n            w.set_per(true);\n            #[cfg(any(flash_g0x0, flash_g0x1, flash_g4c3))]\n            w.set_bker(sector.bank == crate::flash::FlashBank::Bank2);\n            #[cfg(flash_g0x0)]\n            w.set_pnb(sector.index_in_bank as u16);\n            #[cfg(not(flash_g0x0))]\n            w.set_pnb(sector.index_in_bank as u8);\n            w.set_eopie(true);\n            w.set_errie(true);\n            w.set_strt(true);\n        });\n    });\n\n    let ret: Result<(), Error> = wait_ready().await;\n\n    pac::FLASH.cr().modify(|w| {\n        w.set_per(false);\n        w.set_eopie(false);\n        w.set_errie(false);\n    });\n    clear_all_err();\n    restore_data_cache_state();\n\n    ret\n}\n\npub(crate) unsafe fn blocking_erase_sector(sector: &FlashSector) -> Result<(), Error> {\n    wait_busy();\n    clear_all_err();\n    save_data_cache_state();\n\n    interrupt::free(|_| {\n        pac::FLASH.cr().modify(|w| {\n            w.set_per(true);\n            #[cfg(any(flash_g0x0, flash_g0x1, flash_g4c3))]\n            w.set_bker(sector.bank == crate::flash::FlashBank::Bank2);\n            #[cfg(flash_g0x0)]\n            w.set_pnb(sector.index_in_bank as u16);\n            #[cfg(not(flash_g0x0))]\n            w.set_pnb(sector.index_in_bank as u8);\n            w.set_strt(true);\n        });\n    });\n\n    let ret: Result<(), Error> = wait_ready_blocking();\n    pac::FLASH.cr().modify(|w| w.set_per(false));\n    clear_all_err();\n    restore_data_cache_state();\n    ret\n}\n\npub(crate) async fn wait_ready() -> Result<(), Error> {\n    use core::future::poll_fn;\n    use core::task::Poll;\n\n    poll_fn(|cx| {\n        WAKER.register(cx.waker());\n\n        let sr = pac::FLASH.sr().read();\n        if !sr.bsy() {\n            Poll::Ready(get_result(sr))\n        } else {\n            Poll::Pending\n        }\n    })\n    .await\n}\n\npub(crate) unsafe fn wait_ready_blocking() -> Result<(), Error> {\n    loop {\n        let sr = pac::FLASH.sr().read();\n        if !sr.bsy() {\n            return get_result(sr);\n        }\n    }\n}\n\nfn get_result(sr: Sr) -> Result<(), Error> {\n    if sr.progerr() {\n        Err(Error::Prog)\n    } else if sr.wrperr() {\n        Err(Error::Protected)\n    } else if sr.pgaerr() {\n        Err(Error::Unaligned)\n    } else if sr.sizerr() {\n        Err(Error::Size)\n    } else if sr.pgserr() {\n        Err(Error::Seq)\n    } else {\n        Ok(())\n    }\n}\n\npub(crate) unsafe fn clear_all_err() {\n    // read and write back the same value.\n    // This clears all \"write 1 to clear\" bits.\n    pac::FLASH.sr().modify(|_| {});\n}\n\n#[cfg(any(flash_g0x0, flash_g0x1))]\nfn wait_busy() {\n    while pac::FLASH.sr().read().bsy() | pac::FLASH.sr().read().bsy2() {}\n}\n\n#[cfg(not(any(flash_g0x0, flash_g0x1)))]\nfn wait_busy() {\n    while pac::FLASH.sr().read().bsy() {}\n}\n\n// G4 data cache handling - must disable during flash operations\n#[cfg(any(flash_g4c2, flash_g4c3, flash_g4c4))]\nfn save_data_cache_state() {\n    let dcen = pac::FLASH.acr().read().dcen();\n    DATA_CACHE_WAS_ENABLED.store(dcen, Ordering::Relaxed);\n    if dcen {\n        pac::FLASH.acr().modify(|w| w.set_dcen(false));\n    }\n}\n\n#[cfg(any(flash_g4c2, flash_g4c3, flash_g4c4))]\nfn restore_data_cache_state() {\n    // Restore data cache if it was enabled\n    if DATA_CACHE_WAS_ENABLED.load(Ordering::Relaxed) {\n        // Reset data cache before re-enabling\n        pac::FLASH.acr().modify(|w| w.set_dcrst(true));\n        pac::FLASH.acr().modify(|w| w.set_dcrst(false));\n        pac::FLASH.acr().modify(|w| w.set_dcen(true));\n    }\n}\n\n// G0 doesn't have data cache, use no-op functions\n#[cfg(any(flash_g0x0, flash_g0x1))]\nfn save_data_cache_state() {}\n\n#[cfg(any(flash_g0x0, flash_g0x1))]\nfn restore_data_cache_state() {}\n\n#[cfg(all(bank_setup_configurable, any(flash_g4c2, flash_g4c3, flash_g4c4)))]\npub(crate) fn check_bank_setup() {\n    if cfg!(feature = \"single-bank\") && pac::FLASH.optr().read().dbank() {\n        panic!(\n            \"Embassy is configured as single-bank, but the hardware is running in dual-bank mode. Change the hardware by changing the dbank value in the user option bytes or configure embassy to use dual-bank config\"\n        );\n    }\n    if cfg!(feature = \"dual-bank\") && !pac::FLASH.optr().read().dbank() {\n        panic!(\n            \"Embassy is configured as dual-bank, but the hardware is running in single-bank mode. Change the hardware by changing the dbank value in the user option bytes or configure embassy to use single-bank config\"\n        );\n    }\n}\n\n#[cfg(all(bank_setup_configurable, flash_g0x1))]\npub(crate) fn check_bank_setup() {\n    if cfg!(feature = \"single-bank\") && pac::FLASH.optr().read().dual_bank() {\n        panic!(\n            \"Embassy is configured as single-bank, but the hardware is running in dual-bank mode. Change the hardware by changing the dual_bank value in the user option bytes or configure embassy to use dual-bank config\"\n        );\n    }\n    if cfg!(feature = \"dual-bank\") && !pac::FLASH.optr().read().dual_bank() {\n        panic!(\n            \"Embassy is configured as dual-bank, but the hardware is running in single-bank mode. Change the hardware by changing the dual_bank value in the user option bytes or configure embassy to use single-bank config\"\n        );\n    }\n}\n"
  },
  {
    "path": "embassy-stm32/src/flash/h5.rs",
    "content": "use core::ptr::write_volatile;\nuse core::sync::atomic::{Ordering, fence};\n\nuse super::{FlashSector, WRITE_SIZE};\nuse crate::flash::Error;\nuse crate::pac;\n\npub(crate) unsafe fn lock() {\n    if !pac::FLASH.nscr().read().lock() {\n        pac::FLASH.nscr().modify(|r| {\n            r.set_lock(true);\n        });\n    }\n}\n\npub(crate) unsafe fn unlock() {\n    // TODO: check locked first\n    while pac::FLASH.nssr().read().bsy() {\n        #[cfg(feature = \"defmt\")]\n        defmt::trace!(\"busy\")\n    }\n\n    // only unlock if locked to begin with\n    if pac::FLASH.nscr().read().lock() {\n        pac::FLASH.nskeyr().write_value(0x4567_0123);\n        pac::FLASH.nskeyr().write_value(0xCDEF_89AB);\n    }\n}\n\npub(crate) unsafe fn enable_blocking_write() {\n    assert_eq!(0, WRITE_SIZE % 4);\n}\n\npub(crate) unsafe fn disable_blocking_write() {}\n\npub(crate) unsafe fn blocking_write(start_address: u32, buf: &[u8; WRITE_SIZE]) -> Result<(), Error> {\n    // // We cannot have the write setup sequence in begin_write as it depends on the address\n    // let bank = if start_address < BANK1_REGION.end() {\n    //     pac::FLASH.bank(0)\n    // } else {\n    //     pac::FLASH.bank(1)\n    // };\n\n    cortex_m::asm::isb();\n    cortex_m::asm::dsb();\n    fence(Ordering::SeqCst);\n\n    clear_all_err();\n\n    pac::FLASH.nscr().write(|w| {\n        w.set_pg(true);\n        // w.set_psize(2); // 32 bits at once\n    });\n\n    let mut res = None;\n    let mut address = start_address;\n    // TODO: see write size\n    for val in buf.chunks(4) {\n        write_volatile(address as *mut u32, u32::from_le_bytes(unwrap!(val.try_into())));\n        address += val.len() as u32;\n\n        res = Some(blocking_wait_ready().map_err(|e| {\n            error!(\"write err\");\n            e\n        }));\n        pac::FLASH.nssr().modify(|w| {\n            if w.eop() {\n                w.set_eop(true);\n            }\n        });\n        if unwrap!(res).is_err() {\n            break;\n        }\n    }\n\n    cortex_m::asm::isb();\n    cortex_m::asm::dsb();\n    fence(Ordering::SeqCst);\n\n    pac::FLASH.nscr().write(|w| w.set_pg(false));\n\n    unwrap!(res)\n}\n\npub(crate) unsafe fn blocking_erase_sector(sector: &FlashSector) -> Result<(), Error> {\n    // pac::FLASH.wrp2r_cur().read().wrpsg()\n    // TODO: write protection check\n    if pac::FLASH.nscr().read().lock() == true {\n        error!(\"flash locked\");\n    }\n\n    loop {\n        let sr = pac::FLASH.nssr().read();\n        if !sr.bsy() && !sr.dbne() {\n            break;\n        }\n    }\n    clear_all_err();\n\n    pac::FLASH.nscr().modify(|r| {\n        // TODO: later check bank swap\n        r.set_bksel(match sector.bank {\n            crate::flash::FlashBank::Bank1 => stm32_metapac::flash::vals::NscrBksel::B_0X0,\n            crate::flash::FlashBank::Bank2 => stm32_metapac::flash::vals::NscrBksel::B_0X1,\n            _ => unreachable!(),\n        });\n        r.set_snb(sector.index_in_bank);\n        r.set_ser(true);\n    });\n\n    pac::FLASH.nscr().modify(|r| {\n        r.set_strt(true);\n    });\n\n    cortex_m::asm::isb();\n    cortex_m::asm::dsb();\n    fence(Ordering::SeqCst);\n\n    let ret: Result<(), Error> = blocking_wait_ready().map_err(|e| {\n        error!(\"erase err\");\n        e\n    });\n\n    pac::FLASH.nscr().modify(|w| w.set_ser(false));\n    clear_all_err();\n    ret\n}\n\npub(crate) unsafe fn clear_all_err() {\n    pac::FLASH.nssr().modify(|_| {})\n}\n\nunsafe fn blocking_wait_ready() -> Result<(), Error> {\n    loop {\n        let sr = pac::FLASH.nssr().read();\n\n        if !sr.bsy() {\n            if sr.optchangeerr() {\n                error!(\"optchangeerr\");\n                return Err(Error::Prog);\n            }\n            if sr.obkwerr() {\n                error!(\"obkwerr\");\n                return Err(Error::Seq);\n            }\n            if sr.obkerr() {\n                error!(\"obkerr\");\n                return Err(Error::Seq);\n            }\n            if sr.incerr() {\n                error!(\"incerr\");\n                return Err(Error::Unaligned);\n            }\n            if sr.strberr() {\n                error!(\"strberr\");\n                return Err(Error::Parallelism);\n            }\n            if sr.wrperr() {\n                error!(\"protected\");\n                return Err(Error::Protected);\n            }\n\n            return Ok(());\n        }\n    }\n}\n"
  },
  {
    "path": "embassy-stm32/src/flash/h50.rs",
    "content": "/// STM32H50 series flash impl. See RM0492\nuse core::{\n    ptr::write_volatile,\n    sync::atomic::{Ordering, fence},\n};\n\nuse cortex_m::interrupt;\nuse pac::flash::regs::Nssr;\nuse pac::flash::vals::Bksel;\n\nuse super::{Error, FlashBank, FlashSector, WRITE_SIZE};\nuse crate::pac;\n\npub(crate) unsafe fn lock() {\n    pac::FLASH.nscr().modify(|w| w.set_lock(true));\n}\n\npub(crate) unsafe fn unlock() {\n    while busy() {}\n\n    if pac::FLASH.nscr().read().lock() {\n        pac::FLASH.nskeyr().write_value(0x4567_0123);\n        pac::FLASH.nskeyr().write_value(0xCDEF_89AB);\n    }\n}\n\npub(crate) unsafe fn enable_blocking_write() {\n    assert_eq!(0, WRITE_SIZE % 4);\n    pac::FLASH.nscr().write(|w| w.set_pg(true));\n}\n\npub(crate) unsafe fn disable_blocking_write() {\n    pac::FLASH.nscr().write(|w| w.set_pg(false));\n}\n\npub(crate) unsafe fn blocking_write(start_address: u32, buf: &[u8; WRITE_SIZE]) -> Result<(), Error> {\n    let mut address = start_address;\n    for val in buf.chunks(4) {\n        write_volatile(address as *mut u32, u32::from_le_bytes(unwrap!(val.try_into())));\n        address += val.len() as u32;\n\n        // prevents parallelism errors\n        fence(Ordering::SeqCst);\n    }\n\n    wait_ready_blocking()\n}\n\npub(crate) unsafe fn blocking_erase_sector(sector: &FlashSector) -> Result<(), Error> {\n    assert!(sector.bank != FlashBank::Otp);\n    assert!(sector.index_in_bank < 8);\n\n    while busy() {}\n\n    interrupt::free(|_| {\n        pac::FLASH.nscr().modify(|w| {\n            // BKSEL ignores SWAP_BANK, so we must take it into account here\n            w.set_bksel(match (sector.bank, banks_swapped()) {\n                (FlashBank::Bank1, false) => Bksel::BANK1,\n                (FlashBank::Bank2, true) => Bksel::BANK1,\n                (FlashBank::Bank2, false) => Bksel::BANK2,\n                (FlashBank::Bank1, true) => Bksel::BANK2,\n                _ => unreachable!(),\n            });\n            w.set_snb(sector.index_in_bank);\n            w.set_ser(true);\n            w.set_strt(true);\n        })\n    });\n\n    let ret = wait_ready_blocking();\n    pac::FLASH.nscr().modify(|w| w.set_ser(false));\n    ret\n}\n\npub(crate) unsafe fn wait_ready_blocking() -> Result<(), Error> {\n    loop {\n        let sr = pac::FLASH.nssr().read();\n\n        if !sr_busy(sr) {\n            if sr.wrperr() {\n                return Err(Error::Protected);\n            }\n            if sr.pgserr() {\n                return Err(Error::Seq);\n            }\n            if sr.strberr() {\n                // writing several times to the same byte in the write buffer\n                return Err(Error::Prog);\n            }\n            if sr.incerr() {\n                // attempting write operation before completion of previous\n                // write operation\n                return Err(Error::Seq);\n            }\n\n            return Ok(());\n        }\n    }\n}\n\npub(crate) unsafe fn clear_all_err() {\n    pac::FLASH.nsccr().modify(|w| {\n        w.set_clr_wrperr(true);\n        w.set_clr_pgserr(true);\n        w.set_clr_strberr(true);\n        w.set_clr_incerr(true);\n    })\n}\n\n/// Get the current SWAP_BANK option.\n///\n/// This value is only loaded on system or power-on reset. `perform_bank_swap()`\n/// will not reflect here.\npub fn banks_swapped() -> bool {\n    pac::FLASH.optcr().read().swap_bank()\n}\n\n/// Logical, persistent swap of flash banks 1 and 2.\n///\n/// This allows the application to write a new firmware blob into bank 2, then\n/// swap the banks and perform a reset, loading the new firmware.\n///\n/// Swap does not take effect until system or power-on reset.\n///\n/// PLEASE READ THE REFERENCE MANUAL - there are nuances to this feature. For\n/// instance, erase commands and interrupt enables which take a flash bank as a\n/// parameter ignore the swap!\npub fn perform_bank_swap() {\n    while busy() {}\n\n    unsafe {\n        clear_all_err();\n    }\n\n    // unlock OPTLOCK\n    pac::FLASH.optkeyr().write(|w| *w = 0x0819_2A3B);\n    pac::FLASH.optkeyr().write(|w| *w = 0x4C5D_6E7F);\n    while pac::FLASH.optcr().read().optlock() {}\n\n    // toggle SWAP_BANK option\n    pac::FLASH.optsr_prg().modify(|w| w.set_swap_bank(!banks_swapped()));\n\n    // load option bytes\n    pac::FLASH.optcr().modify(|w| w.set_optstrt(true));\n    while pac::FLASH.optcr().read().optstrt() {}\n\n    // re-lock OPTLOCK\n    pac::FLASH.optcr().modify(|w| w.set_optlock(true));\n}\n\nfn sr_busy(sr: Nssr) -> bool {\n    // Note: RM0492 sometimes incorrectly refers to WBNE as NSWBNE\n    sr.bsy() || sr.dbne() || sr.wbne()\n}\n\nfn busy() -> bool {\n    let sr = pac::FLASH.nssr().read();\n    sr_busy(sr)\n}\n"
  },
  {
    "path": "embassy-stm32/src/flash/h7.rs",
    "content": "use core::ptr::write_volatile;\nuse core::sync::atomic::{Ordering, fence};\n\nuse embassy_sync::waitqueue::AtomicWaker;\nuse pac::flash::regs::Sr;\n\nuse super::{BANK1_REGION, FLASH_REGIONS, FlashSector, WRITE_SIZE};\nuse crate::flash::Error;\nuse crate::pac;\n\nstatic WAKER: AtomicWaker = AtomicWaker::new();\n\npub(crate) unsafe fn on_interrupt() {\n    // Clear IRQ flags\n    pac::FLASH.bank(0).ccr().write(|w| {\n        w.set_clr_eop(true);\n        w.set_clr_operr(true);\n    });\n    if is_dual_bank() {\n        pac::FLASH.bank(1).ccr().write(|w| {\n            w.set_clr_eop(true);\n            w.set_clr_operr(true);\n        });\n    }\n\n    WAKER.wake();\n}\n\nconst fn is_dual_bank() -> bool {\n    FLASH_REGIONS.len() >= 2\n}\n\npub(crate) unsafe fn lock() {\n    pac::FLASH.bank(0).cr().modify(|w| w.set_lock(true));\n    if is_dual_bank() {\n        pac::FLASH.bank(1).cr().modify(|w| w.set_lock(true));\n    }\n}\n\npub(crate) unsafe fn unlock() {\n    if pac::FLASH.bank(0).cr().read().lock() {\n        pac::FLASH.bank(0).keyr().write_value(0x4567_0123);\n        pac::FLASH.bank(0).keyr().write_value(0xCDEF_89AB);\n    }\n    if is_dual_bank() {\n        if pac::FLASH.bank(1).cr().read().lock() {\n            pac::FLASH.bank(1).keyr().write_value(0x4567_0123);\n            pac::FLASH.bank(1).keyr().write_value(0xCDEF_89AB);\n        }\n    }\n}\n\npub(crate) unsafe fn enable_write() {\n    enable_blocking_write();\n}\n\npub(crate) unsafe fn disable_write() {\n    disable_blocking_write();\n}\n\npub(crate) unsafe fn enable_blocking_write() {\n    assert_eq!(0, WRITE_SIZE % 4);\n}\n\npub(crate) unsafe fn disable_blocking_write() {}\n\npub(crate) async unsafe fn write(start_address: u32, buf: &[u8; WRITE_SIZE]) -> Result<(), Error> {\n    // We cannot have the write setup sequence in begin_write as it depends on the address\n    let bank = if start_address >= BANK1_REGION.base() && start_address < BANK1_REGION.end() {\n        pac::FLASH.bank(0)\n    } else {\n        pac::FLASH.bank(1)\n    };\n    bank.cr().write(|w| {\n        w.set_pg(true);\n        #[cfg(flash_h7)]\n        w.set_psize(2); // 32 bits at once\n        w.set_eopie(true);\n        w.set_operrie(true);\n    });\n    cortex_m::asm::isb();\n    cortex_m::asm::dsb();\n    fence(Ordering::SeqCst);\n\n    let mut res = None;\n    let mut address = start_address;\n    for val in buf.chunks(4) {\n        write_volatile(address as *mut u32, u32::from_le_bytes(unwrap!(val.try_into())));\n        address += val.len() as u32;\n\n        res = Some(wait_ready(bank).await);\n        bank.sr().modify(|w| {\n            if w.eop() {\n                w.set_eop(true);\n            }\n        });\n        if unwrap!(res).is_err() {\n            break;\n        }\n    }\n\n    cortex_m::asm::isb();\n    cortex_m::asm::dsb();\n    fence(Ordering::SeqCst);\n\n    bank.cr().write(|w| {\n        w.set_pg(false);\n        w.set_eopie(false);\n        w.set_operrie(false);\n    });\n\n    unwrap!(res)\n}\n\npub(crate) unsafe fn blocking_write(start_address: u32, buf: &[u8; WRITE_SIZE]) -> Result<(), Error> {\n    // We cannot have the write setup sequence in begin_write as it depends on the address\n    let bank = if start_address >= BANK1_REGION.base() && start_address < BANK1_REGION.end() {\n        pac::FLASH.bank(0)\n    } else {\n        pac::FLASH.bank(1)\n    };\n    bank.cr().write(|w| {\n        w.set_pg(true);\n        #[cfg(flash_h7)]\n        w.set_psize(2); // 32 bits at once\n    });\n    cortex_m::asm::isb();\n    cortex_m::asm::dsb();\n    fence(Ordering::SeqCst);\n\n    let mut res = None;\n    let mut address = start_address;\n    for val in buf.chunks(4) {\n        write_volatile(address as *mut u32, u32::from_le_bytes(unwrap!(val.try_into())));\n        address += val.len() as u32;\n\n        res = Some(blocking_wait_ready(bank));\n        bank.sr().modify(|w| {\n            if w.eop() {\n                w.set_eop(true);\n            }\n        });\n        if unwrap!(res).is_err() {\n            break;\n        }\n    }\n\n    cortex_m::asm::isb();\n    cortex_m::asm::dsb();\n    fence(Ordering::SeqCst);\n\n    bank.cr().write(|w| w.set_pg(false));\n\n    unwrap!(res)\n}\n\npub(crate) async unsafe fn erase_sector(sector: &FlashSector) -> Result<(), Error> {\n    let bank = pac::FLASH.bank(sector.bank as usize);\n    bank.cr().modify(|w| {\n        w.set_ser(true);\n        #[cfg(flash_h7)]\n        w.set_snb(sector.index_in_bank);\n        #[cfg(flash_h7ab)]\n        w.set_ssn(sector.index_in_bank);\n        w.set_eopie(true);\n        w.set_operrie(true);\n    });\n\n    bank.cr().modify(|w| {\n        w.set_start(true);\n    });\n\n    cortex_m::asm::isb();\n    cortex_m::asm::dsb();\n    fence(Ordering::SeqCst);\n\n    let ret: Result<(), Error> = wait_ready(bank).await;\n    bank.cr().modify(|w| {\n        w.set_ser(false);\n        w.set_eopie(false);\n        w.set_operrie(false);\n    });\n    bank_clear_all_err(bank);\n    ret\n}\n\npub(crate) unsafe fn blocking_erase_sector(sector: &FlashSector) -> Result<(), Error> {\n    let bank = pac::FLASH.bank(sector.bank as usize);\n    bank.cr().modify(|w| {\n        w.set_ser(true);\n        #[cfg(flash_h7)]\n        w.set_snb(sector.index_in_bank);\n        #[cfg(flash_h7ab)]\n        w.set_ssn(sector.index_in_bank);\n    });\n\n    bank.cr().modify(|w| {\n        w.set_start(true);\n    });\n\n    cortex_m::asm::isb();\n    cortex_m::asm::dsb();\n    fence(Ordering::SeqCst);\n\n    let ret: Result<(), Error> = blocking_wait_ready(bank);\n    bank.cr().modify(|w| w.set_ser(false));\n    bank_clear_all_err(bank);\n    ret\n}\n\npub(crate) unsafe fn clear_all_err() {\n    bank_clear_all_err(pac::FLASH.bank(0));\n    bank_clear_all_err(pac::FLASH.bank(1));\n}\n\nunsafe fn bank_clear_all_err(bank: pac::flash::Bank) {\n    // read and write back the same value.\n    // This clears all \"write 1 to clear\" bits.\n    bank.sr().modify(|_| {});\n}\n\nasync fn wait_ready(bank: pac::flash::Bank) -> Result<(), Error> {\n    use core::future::poll_fn;\n    use core::task::Poll;\n\n    poll_fn(|cx| {\n        WAKER.register(cx.waker());\n\n        let sr = bank.sr().read();\n        if !sr.bsy() && !sr.qw() {\n            Poll::Ready(get_result(sr))\n        } else {\n            return Poll::Pending;\n        }\n    })\n    .await\n}\n\nunsafe fn blocking_wait_ready(bank: pac::flash::Bank) -> Result<(), Error> {\n    loop {\n        let sr = bank.sr().read();\n\n        if !sr.bsy() && !sr.qw() {\n            return get_result(sr);\n        }\n    }\n}\n\nfn get_result(sr: Sr) -> Result<(), Error> {\n    if sr.wrperr() {\n        Err(Error::Protected)\n    } else if sr.pgserr() {\n        error!(\"pgserr\");\n        Err(Error::Seq)\n    } else if sr.incerr() {\n        // writing to a different address when programming 256 bit word was not finished\n        error!(\"incerr\");\n        Err(Error::Seq)\n    } else if sr.crcrderr() {\n        error!(\"crcrderr\");\n        Err(Error::Seq)\n    } else if sr.operr() {\n        Err(Error::Prog)\n    } else if sr.sneccerr1() {\n        // single ECC error\n        Err(Error::Prog)\n    } else if sr.dbeccerr() {\n        // double ECC error\n        Err(Error::Prog)\n    } else if sr.rdperr() {\n        Err(Error::Protected)\n    } else if sr.rdserr() {\n        Err(Error::Protected)\n    } else {\n        Ok(())\n    }\n}\n"
  },
  {
    "path": "embassy-stm32/src/flash/l.rs",
    "content": "use core::ptr::write_volatile;\nuse core::sync::atomic::{Ordering, fence};\n\n#[cfg(flash_l4)]\nuse cortex_m::interrupt;\n#[cfg(flash_l4)]\nuse embassy_sync::waitqueue::AtomicWaker;\n#[cfg(flash_l4)]\nuse pac::flash::regs::Sr;\n\nuse super::{FlashSector, WRITE_SIZE};\nuse crate::flash::Error;\nuse crate::pac;\n\n#[cfg(flash_l4)]\nstatic WAKER: AtomicWaker = AtomicWaker::new();\n\n#[cfg(flash_l4)]\npub(crate) unsafe fn on_interrupt() {\n    // Clear IRQ flags (EOP and error flags are write-1-to-clear)\n    pac::FLASH.sr().write(|w| {\n        w.set_eop(true);\n        w.set_operr(true);\n        w.set_progerr(true);\n        w.set_wrperr(true);\n        w.set_pgaerr(true);\n        w.set_sizerr(true);\n        w.set_pgserr(true);\n        w.set_miserr(true);\n        w.set_fasterr(true);\n        w.set_rderr(true);\n        w.set_optverr(true);\n    });\n\n    WAKER.wake();\n}\n\n#[cfg(flash_l4)]\npub(crate) unsafe fn enable_write() {\n    assert_eq!(0, WRITE_SIZE % 4);\n    pac::FLASH.cr().write(|w| {\n        w.set_pg(true);\n        w.set_eopie(true);\n        w.set_errie(true);\n    });\n}\n\n#[cfg(flash_l4)]\npub(crate) unsafe fn disable_write() {\n    pac::FLASH.cr().write(|w| {\n        w.set_pg(false);\n        w.set_eopie(false);\n        w.set_errie(false);\n    });\n}\n\n#[cfg(flash_l4)]\nunsafe fn write_start(start_address: u32, buf: &[u8; WRITE_SIZE]) {\n    let mut address = start_address;\n    for val in buf.chunks(4) {\n        write_volatile(address as *mut u32, u32::from_le_bytes(unwrap!(val.try_into())));\n        address += val.len() as u32;\n\n        // prevents parallelism errors\n        fence(Ordering::SeqCst);\n    }\n}\n\n#[cfg(flash_l4)]\npub(crate) async unsafe fn write(start_address: u32, buf: &[u8; WRITE_SIZE]) -> Result<(), Error> {\n    write_start(start_address, buf);\n    wait_ready().await\n}\n\n#[cfg(flash_l4)]\npub(crate) async unsafe fn erase_sector(sector: &FlashSector) -> Result<(), Error> {\n    interrupt::free(|_| {\n        pac::FLASH.cr().modify(|w| {\n            w.set_per(true);\n            w.set_pnb(sector.index_in_bank);\n            w.set_bker(sector.bank == crate::flash::FlashBank::Bank2);\n            w.set_eopie(true);\n            w.set_errie(true);\n            w.set_start(true);\n        });\n    });\n\n    let ret = wait_ready().await;\n\n    pac::FLASH.cr().modify(|w| {\n        w.set_per(false);\n        w.set_eopie(false);\n        w.set_errie(false);\n    });\n    clear_all_err();\n    ret\n}\n\n#[cfg(flash_l4)]\npub(crate) async fn wait_ready() -> Result<(), Error> {\n    use core::future::poll_fn;\n    use core::task::Poll;\n\n    poll_fn(|cx| {\n        WAKER.register(cx.waker());\n\n        let sr = pac::FLASH.sr().read();\n        if !sr.bsy() {\n            Poll::Ready(get_result(sr))\n        } else {\n            Poll::Pending\n        }\n    })\n    .await\n}\n\n#[cfg(flash_l4)]\nfn get_result(sr: Sr) -> Result<(), Error> {\n    if sr.progerr() {\n        Err(Error::Prog)\n    } else if sr.wrperr() {\n        Err(Error::Protected)\n    } else if sr.pgaerr() {\n        Err(Error::Unaligned)\n    } else if sr.sizerr() {\n        Err(Error::Size)\n    } else if sr.miserr() {\n        Err(Error::Miss)\n    } else if sr.pgserr() {\n        Err(Error::Seq)\n    } else {\n        Ok(())\n    }\n}\n\npub(crate) unsafe fn lock() {\n    #[cfg(any(flash_wl, flash_wb, flash_l4))]\n    pac::FLASH.cr().modify(|w| w.set_lock(true));\n\n    #[cfg(any(flash_l0))]\n    pac::FLASH.pecr().modify(|w| {\n        w.set_optlock(true);\n        w.set_prglock(true);\n        w.set_pelock(true);\n    });\n\n    #[cfg(any(flash_l5))]\n    pac::FLASH.nscr().modify(|w| w.set_nslock(true));\n}\n\npub(crate) unsafe fn unlock() {\n    #[cfg(any(flash_wl, flash_wb, flash_l4))]\n    {\n        if pac::FLASH.cr().read().lock() {\n            pac::FLASH.keyr().write_value(0x4567_0123);\n            pac::FLASH.keyr().write_value(0xCDEF_89AB);\n        }\n    }\n\n    #[cfg(any(flash_l0, flash_l1))]\n    {\n        if pac::FLASH.pecr().read().pelock() {\n            pac::FLASH.pekeyr().write_value(0x89AB_CDEF);\n            pac::FLASH.pekeyr().write_value(0x0203_0405);\n        }\n\n        if pac::FLASH.pecr().read().prglock() {\n            pac::FLASH.prgkeyr().write_value(0x8C9D_AEBF);\n            pac::FLASH.prgkeyr().write_value(0x1314_1516);\n        }\n    }\n\n    #[cfg(any(flash_l5))]\n    {\n        if pac::FLASH.nscr().read().nslock() {\n            pac::FLASH.nskeyr().write_value(0x4567_0123);\n            pac::FLASH.nskeyr().write_value(0xCDEF_89AB);\n        }\n    }\n}\n\npub(crate) unsafe fn enable_blocking_write() {\n    assert_eq!(0, WRITE_SIZE % 4);\n\n    #[cfg(any(flash_wl, flash_wb, flash_l4))]\n    pac::FLASH.cr().write(|w| w.set_pg(true));\n\n    #[cfg(any(flash_l5))]\n    pac::FLASH.nscr().write(|w| w.set_nspg(true));\n}\n\npub(crate) unsafe fn disable_blocking_write() {\n    #[cfg(any(flash_wl, flash_wb, flash_l4))]\n    pac::FLASH.cr().write(|w| w.set_pg(false));\n\n    #[cfg(any(flash_l5))]\n    pac::FLASH.nscr().write(|w| w.set_nspg(false));\n}\n\npub(crate) unsafe fn blocking_write(start_address: u32, buf: &[u8; WRITE_SIZE]) -> Result<(), Error> {\n    let mut address = start_address;\n    for val in buf.chunks(4) {\n        write_volatile(address as *mut u32, u32::from_le_bytes(unwrap!(val.try_into())));\n        address += val.len() as u32;\n\n        // prevents parallelism errors\n        fence(Ordering::SeqCst);\n    }\n\n    wait_ready_blocking()\n}\n\npub(crate) unsafe fn blocking_erase_sector(sector: &FlashSector) -> Result<(), Error> {\n    #[cfg(any(flash_l0, flash_l1))]\n    {\n        pac::FLASH.pecr().modify(|w| {\n            w.set_erase(true);\n            w.set_prog(true);\n        });\n\n        write_volatile(sector.start as *mut u32, 0xFFFFFFFF);\n    }\n\n    #[cfg(any(flash_wl, flash_wb, flash_l4, flash_l5))]\n    {\n        let idx = (sector.start - super::FLASH_BASE as u32) / super::BANK1_REGION.erase_size as u32;\n        #[cfg(any(flash_l4, flash_l5))]\n        let pgn = super::BANK1_REGION.size as u32 / super::BANK1_REGION.erase_size as u32;\n\n        #[cfg(flash_l4)]\n        let (idx, bank) = if idx > (pgn - 1) {\n            (idx - pgn, true)\n        } else {\n            (idx, false)\n        };\n\n        #[cfg(flash_l5)]\n        let (idx, bank) = if pac::FLASH.optr().read().dbank() {\n            if idx > (pgn - 1) {\n                (idx - pgn, Some(true))\n            } else {\n                (idx, Some(false))\n            }\n        } else {\n            (idx, None)\n        };\n\n        #[cfg(not(flash_l5))]\n        pac::FLASH.cr().modify(|w| {\n            w.set_per(true);\n            w.set_pnb(idx as u8);\n            #[cfg(any(flash_wl, flash_wb))]\n            w.set_strt(true);\n            #[cfg(any(flash_l4))]\n            w.set_start(true);\n            #[cfg(any(flash_l4))]\n            w.set_bker(bank);\n        });\n\n        #[cfg(flash_l5)]\n        pac::FLASH.nscr().modify(|w| {\n            w.set_nsper(true);\n            w.set_nspnb(idx as u8);\n            if let Some(bank) = bank {\n                w.set_nsbker(bank);\n            }\n            w.set_nsstrt(true);\n        });\n    }\n\n    let ret: Result<(), Error> = wait_ready_blocking();\n\n    #[cfg(any(flash_wl, flash_wb, flash_l4))]\n    pac::FLASH.cr().modify(|w| w.set_per(false));\n\n    #[cfg(any(flash_l5))]\n    pac::FLASH.nscr().modify(|w| w.set_nsper(false));\n\n    #[cfg(any(flash_l0, flash_l1))]\n    pac::FLASH.pecr().modify(|w| {\n        w.set_erase(false);\n        w.set_prog(false);\n    });\n\n    clear_all_err();\n    ret\n}\n\npub(crate) unsafe fn clear_all_err() {\n    // read and write back the same value.\n    // This clears all \"write 1 to clear\" bits.\n    #[cfg(not(flash_l5))]\n    pac::FLASH.sr().modify(|_| {});\n\n    #[cfg(flash_l5)]\n    pac::FLASH.nssr().modify(|_| {});\n}\n\npub(crate) unsafe fn wait_ready_blocking() -> Result<(), Error> {\n    loop {\n        #[cfg(not(flash_l5))]\n        {\n            let sr = pac::FLASH.sr().read();\n\n            if !sr.bsy() {\n                #[cfg(any(flash_wl, flash_wb, flash_l4))]\n                if sr.progerr() {\n                    return Err(Error::Prog);\n                }\n\n                if sr.wrperr() {\n                    return Err(Error::Protected);\n                }\n\n                if sr.pgaerr() {\n                    return Err(Error::Unaligned);\n                }\n\n                if sr.sizerr() {\n                    return Err(Error::Size);\n                }\n\n                #[cfg(any(flash_wl, flash_wb, flash_l4))]\n                if sr.miserr() {\n                    return Err(Error::Miss);\n                }\n\n                #[cfg(any(flash_wl, flash_wb, flash_l4))]\n                if sr.pgserr() {\n                    return Err(Error::Seq);\n                }\n\n                return Ok(());\n            }\n        }\n\n        #[cfg(flash_l5)]\n        {\n            let nssr = pac::FLASH.nssr().read();\n\n            if !nssr.nsbsy() {\n                if nssr.nsprogerr() {\n                    return Err(Error::Prog);\n                }\n\n                if nssr.nswrperr() {\n                    return Err(Error::Protected);\n                }\n\n                if nssr.nspgaerr() {\n                    return Err(Error::Unaligned);\n                }\n\n                if nssr.nssizerr() {\n                    return Err(Error::Size);\n                }\n\n                if nssr.nspgserr() {\n                    return Err(Error::Seq);\n                }\n\n                return Ok(());\n            }\n        }\n    }\n}\n\n#[cfg(all(bank_setup_configurable, flash_l5))]\npub(crate) fn check_bank_setup() {\n    if cfg!(feature = \"single-bank\") && pac::FLASH.optr().read().dbank() {\n        panic!(\n            \"Embassy is configured as single-bank, but the hardware is running in dual-bank mode. Change the hardware by changing the dbank value in the user option bytes or configure embassy to use dual-bank config\"\n        );\n    }\n    if cfg!(feature = \"dual-bank\") && !pac::FLASH.optr().read().dbank() {\n        panic!(\n            \"Embassy is configured as dual-bank, but the hardware is running in single-bank mode. Change the hardware by changing the dbank value in the user option bytes or configure embassy to use single-bank config\"\n        );\n    }\n}\n\n#[cfg(all(bank_setup_configurable, flash_l4))]\npub(crate) fn check_bank_setup() {\n    if cfg!(feature = \"single-bank\") && pac::FLASH.optr().read().dualbank() {\n        panic!(\n            \"Embassy is configured as single-bank, but the hardware is running in dual-bank mode. Change the hardware by changing the dualbank value in the user option bytes or configure embassy to use dual-bank config\"\n        );\n    }\n    if cfg!(feature = \"dual-bank\") && !pac::FLASH.optr().read().dualbank() {\n        panic!(\n            \"Embassy is configured as dual-bank, but the hardware is running in single-bank mode. Change the hardware by changing the dualbank value in the user option bytes or configure embassy to use single-bank config\"\n        );\n    }\n}\n"
  },
  {
    "path": "embassy-stm32/src/flash/mod.rs",
    "content": "//! Flash memory (FLASH)\nuse embedded_storage::nor_flash::{NorFlashError, NorFlashErrorKind};\n\n#[cfg(any(\n    flash_f4, flash_g0x0, flash_g0x1, flash_g4c2, flash_g4c3, flash_g4c4, flash_h7, flash_h7ab, flash_l4\n))]\nmod asynch;\n#[cfg(flash)]\nmod common;\n#[cfg(eeprom)]\nmod eeprom;\n\n#[cfg(any(\n    flash_f4, flash_g0x0, flash_g0x1, flash_g4c2, flash_g4c3, flash_g4c4, flash_h7, flash_h7ab, flash_l4\n))]\npub use asynch::InterruptHandler;\n#[cfg(flash)]\npub use common::*;\n#[cfg(eeprom)]\n#[allow(unused_imports)]\npub use eeprom::*;\n\npub use crate::_generated::flash_regions::*;\n#[cfg(eeprom)]\npub use crate::_generated::{EEPROM_BASE, EEPROM_SIZE};\npub use crate::_generated::{FLASH_BASE, FLASH_SIZE, MAX_ERASE_SIZE, WRITE_SIZE};\n\n/// Get all flash regions.\npub fn get_flash_regions() -> &'static [&'static FlashRegion] {\n    &FLASH_REGIONS\n}\n\n/// Read size (always 1)\npub const READ_SIZE: usize = 1;\n\n/// Blocking flash mode typestate.\npub enum Blocking {}\n/// Async flash mode typestate.\npub enum Async {}\n\n/// Flash memory region\n#[derive(Debug)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub struct FlashRegion {\n    /// Bank number.\n    pub bank: FlashBank,\n    /// Offset from bank base.\n    pub offset: u32,\n    /// Size in bytes.\n    pub size: u32,\n    /// Erase size (sector size).\n    pub erase_size: u32,\n    /// Minimum write size.\n    pub write_size: u32,\n    /// Erase value (usually `0xFF`, but is `0x00` in some chips)\n    pub erase_value: u8,\n    pub(crate) _ensure_internal: (),\n}\n\nimpl FlashRegion {\n    /// Absolute base address.\n    pub fn base(&self) -> u32 {\n        self.bank.base() + self.offset\n    }\n\n    /// Absolute end address.\n    pub fn end(&self) -> u32 {\n        self.base() + self.size\n    }\n\n    /// Number of sectors in the region.\n    pub const fn sectors(&self) -> u8 {\n        (self.size / self.erase_size) as u8\n    }\n}\n\n/// Flash sector.\n#[derive(Debug, PartialEq)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub struct FlashSector {\n    /// Bank number.\n    pub bank: FlashBank,\n    /// Sector number within the bank.\n    pub index_in_bank: u8,\n    /// Absolute start address.\n    pub start: u32,\n    /// Size in bytes.\n    pub size: u32,\n}\n\n/// Flash bank.\n#[derive(Clone, Copy, Debug, PartialEq)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub enum FlashBank {\n    /// Bank 1\n    Bank1 = 0,\n    /// Bank 2\n    Bank2 = 1,\n    /// OTP region,\n    Otp,\n}\n#[cfg(all(eeprom, not(any(flash_l0, flash_l1))))]\ncompile_error!(\"The 'eeprom' cfg is enabled for a non-L0/L1 chip family. This is an unsupported configuration.\");\n#[cfg_attr(any(flash_l0, flash_l1, flash_l4, flash_l5, flash_wl, flash_wb), path = \"l.rs\")]\n#[cfg_attr(flash_f0, path = \"f0.rs\")]\n#[cfg_attr(any(flash_f1, flash_f3), path = \"f1f3.rs\")]\n#[cfg_attr(flash_f2, path = \"f2.rs\")]\n#[cfg_attr(flash_f4, path = \"f4.rs\")]\n#[cfg_attr(flash_f7, path = \"f7.rs\")]\n#[cfg_attr(any(flash_g0x0, flash_g0x1, flash_g4c2, flash_g4c3, flash_g4c4), path = \"g.rs\")]\n#[cfg_attr(flash_c0, path = \"c.rs\")]\n#[cfg_attr(flash_h7, path = \"h7.rs\")]\n#[cfg_attr(flash_h7ab, path = \"h7.rs\")]\n#[cfg_attr(any(flash_u5, flash_wba), path = \"u5.rs\")]\n#[cfg_attr(flash_h5, path = \"h5.rs\")]\n#[cfg_attr(flash_h50, path = \"h50.rs\")]\n#[cfg_attr(flash_u0, path = \"u0.rs\")]\n#[cfg_attr(\n    not(any(\n        flash_l0, flash_l1, flash_l4, flash_l5, flash_wl, flash_wb, flash_f0, flash_f1, flash_f2, flash_f3, flash_f4,\n        flash_f7, flash_g0x0, flash_g0x1, flash_g4c2, flash_g4c3, flash_g4c4, flash_c0, flash_h7, flash_h7ab, flash_u5,\n        flash_wba, flash_h50, flash_u0, flash_h5,\n    )),\n    path = \"other.rs\"\n)]\nmod family;\n\n#[allow(unused_imports)]\npub use family::*;\n\n/// Flash error\n///\n/// See STM32 Reference Manual for your chip for details.\n#[allow(missing_docs)]\n#[derive(Debug, Copy, Clone, PartialEq, Eq)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub enum Error {\n    Prog,\n    Size,\n    Miss,\n    Seq,\n    Protected,\n    Unaligned,\n    Parallelism,\n}\n\nimpl NorFlashError for Error {\n    fn kind(&self) -> NorFlashErrorKind {\n        match self {\n            Self::Size => NorFlashErrorKind::OutOfBounds,\n            Self::Unaligned => NorFlashErrorKind::NotAligned,\n            _ => NorFlashErrorKind::Other,\n        }\n    }\n}\n"
  },
  {
    "path": "embassy-stm32/src/flash/other.rs",
    "content": "#![allow(unused)]\n\nuse super::{Error, FlashSector, WRITE_SIZE};\n\npub(crate) unsafe fn lock() {\n    unimplemented!();\n}\npub(crate) unsafe fn unlock() {\n    unimplemented!();\n}\npub(crate) unsafe fn enable_blocking_write() {\n    unimplemented!();\n}\npub(crate) unsafe fn disable_blocking_write() {\n    unimplemented!();\n}\npub(crate) unsafe fn blocking_write(_start_address: u32, _buf: &[u8; WRITE_SIZE]) -> Result<(), Error> {\n    unimplemented!();\n}\npub(crate) unsafe fn blocking_erase_sector(_sector: &FlashSector) -> Result<(), Error> {\n    unimplemented!();\n}\npub(crate) unsafe fn clear_all_err() {\n    unimplemented!();\n}\n"
  },
  {
    "path": "embassy-stm32/src/flash/u0.rs",
    "content": "use core::ptr::write_volatile;\nuse core::sync::atomic::{Ordering, fence};\n\nuse cortex_m::interrupt;\n\nuse super::{FlashSector, WRITE_SIZE};\nuse crate::flash::Error;\nuse crate::pac;\n\npub(crate) unsafe fn lock() {\n    pac::FLASH.cr().modify(|w| w.set_lock(true));\n}\npub(crate) unsafe fn unlock() {\n    // Wait, while the memory interface is busy.\n    while pac::FLASH.sr().read().bsy1() {}\n\n    // Unlock flash\n    if pac::FLASH.cr().read().lock() {\n        pac::FLASH.keyr().write(|w| w.set_key(0x4567_0123));\n        pac::FLASH.keyr().write(|w| w.set_key(0xCDEF_89AB));\n    }\n}\n\npub(crate) unsafe fn enable_blocking_write() {\n    assert_eq!(0, WRITE_SIZE % 4);\n    pac::FLASH.cr().write(|w| w.set_pg(true));\n}\n\npub(crate) unsafe fn disable_blocking_write() {\n    pac::FLASH.cr().write(|w| w.set_pg(false));\n}\n\npub(crate) unsafe fn blocking_write(start_address: u32, buf: &[u8; WRITE_SIZE]) -> Result<(), Error> {\n    let mut address = start_address;\n    for val in buf.chunks(4) {\n        write_volatile(address as *mut u32, u32::from_le_bytes(unwrap!(val.try_into())));\n        address += val.len() as u32;\n\n        // prevents parallelism errors\n        fence(Ordering::SeqCst);\n    }\n\n    wait_ready_blocking()\n}\n\npub(crate) unsafe fn blocking_erase_sector(sector: &FlashSector) -> Result<(), Error> {\n    let idx = (sector.start - super::FLASH_BASE as u32) / super::BANK1_REGION.erase_size as u32;\n    while pac::FLASH.sr().read().bsy1() {}\n    clear_all_err();\n\n    interrupt::free(|_| {\n        pac::FLASH.cr().modify(|w| {\n            w.set_per(true);\n            w.set_pnb(idx as u8);\n            w.set_strt(true);\n        });\n    });\n\n    let ret: Result<(), Error> = wait_ready_blocking();\n    pac::FLASH.cr().modify(|w| w.set_per(false));\n    ret\n}\n\npub(crate) unsafe fn wait_ready_blocking() -> Result<(), Error> {\n    while pac::FLASH.sr().read().bsy1() {}\n\n    let sr = pac::FLASH.sr().read();\n\n    if sr.progerr() {\n        return Err(Error::Prog);\n    }\n\n    if sr.wrperr() {\n        return Err(Error::Protected);\n    }\n\n    if sr.pgaerr() {\n        return Err(Error::Unaligned);\n    }\n\n    Ok(())\n}\n\npub(crate) unsafe fn clear_all_err() {\n    // read and write back the same value.\n    // This clears all \"write 1 to clear\" bits.\n    pac::FLASH.sr().modify(|_| {});\n}\n"
  },
  {
    "path": "embassy-stm32/src/flash/u5.rs",
    "content": "use core::ptr::write_volatile;\nuse core::sync::atomic::{Ordering, fence};\n\nuse super::{FlashBank, FlashSector, WRITE_SIZE};\nuse crate::flash::Error;\nuse crate::pac;\n\npub(crate) unsafe fn lock() {\n    #[cfg(feature = \"trustzone-secure\")]\n    pac::FLASH.seccr().modify(|w| w.set_lock(true));\n    #[cfg(not(feature = \"trustzone-secure\"))]\n    pac::FLASH.nscr().modify(|w| w.set_lock(true));\n}\n\npub(crate) unsafe fn unlock() {\n    #[cfg(feature = \"trustzone-secure\")]\n    if pac::FLASH.seccr().read().lock() {\n        pac::FLASH.seckeyr().write_value(0x4567_0123);\n        pac::FLASH.seckeyr().write_value(0xCDEF_89AB);\n    }\n    #[cfg(not(feature = \"trustzone-secure\"))]\n    if pac::FLASH.nscr().read().lock() {\n        pac::FLASH.nskeyr().write_value(0x4567_0123);\n        pac::FLASH.nskeyr().write_value(0xCDEF_89AB);\n    }\n}\n\npub(crate) unsafe fn enable_blocking_write() {\n    assert_eq!(0, WRITE_SIZE % 4);\n    // STM32WBA: quad-word programming only; WRITE_SIZE must be 16.\n    #[cfg(flash_wba)]\n    const _: () = core::assert!(WRITE_SIZE == 16);\n\n    #[cfg(feature = \"trustzone-secure\")]\n    pac::FLASH.seccr().write(|w| {\n        w.set_pg(true);\n    });\n    #[cfg(not(feature = \"trustzone-secure\"))]\n    pac::FLASH.nscr().write(|w| {\n        w.set_pg(true);\n    });\n}\n\npub(crate) unsafe fn disable_blocking_write() {\n    #[cfg(feature = \"trustzone-secure\")]\n    pac::FLASH.seccr().write(|w| w.set_pg(false));\n    #[cfg(not(feature = \"trustzone-secure\"))]\n    pac::FLASH.nscr().write(|w| w.set_pg(false));\n}\n\n/// Wait until write buffer is empty (WDW) and no operation is in progress (BSY).\n/// Per RM: check before starting a new program; after writing, wait WDW then BSY.\nunsafe fn blocking_wait_wdw_and_bsy() {\n    loop {\n        #[cfg(feature = \"trustzone-secure\")]\n        let sr = pac::FLASH.secsr().read();\n        #[cfg(not(feature = \"trustzone-secure\"))]\n        let sr = pac::FLASH.nssr().read();\n\n        if !sr.wdw() && !sr.bsy() {\n            return;\n        }\n    }\n}\n\npub(crate) unsafe fn blocking_write(start_address: u32, buf: &[u8; WRITE_SIZE]) -> Result<(), Error> {\n    // Per RM: before programming, ensure WDW (write buffer empty) and BSY (no op ongoing).\n    blocking_wait_wdw_and_bsy();\n\n    let mut address = start_address;\n    for val in buf.chunks(4) {\n        write_volatile(address as *mut u32, u32::from_le_bytes(unwrap!(val.try_into())));\n        address += val.len() as u32;\n\n        // prevents parallelism errors\n        fence(Ordering::SeqCst);\n    }\n\n    blocking_wait_ready()\n}\n\npub(crate) unsafe fn blocking_erase_sector(sector: &FlashSector) -> Result<(), Error> {\n    #[cfg(feature = \"trustzone-secure\")]\n    pac::FLASH.seccr().modify(|w| {\n        w.set_per(pac::flash::vals::SeccrPer::B_0X1);\n        w.set_pnb(sector.index_in_bank);\n        // TODO: add check for bank swap\n        w.set_bker(match sector.bank {\n            FlashBank::Bank1 => false,\n            FlashBank::Bank2 => true,\n            _ => unreachable!(),\n        });\n    });\n    #[cfg(not(feature = \"trustzone-secure\"))]\n    pac::FLASH.nscr().modify(|w| {\n        w.set_per(true);\n        w.set_pnb(sector.index_in_bank);\n        // TODO: add check for bank swap\n        w.set_bker(match sector.bank {\n            FlashBank::Bank1 => false,\n            FlashBank::Bank2 => true,\n            _ => unreachable!(),\n        });\n    });\n\n    #[cfg(feature = \"trustzone-secure\")]\n    pac::FLASH.seccr().modify(|w| {\n        w.set_strt(true);\n    });\n    #[cfg(not(feature = \"trustzone-secure\"))]\n    pac::FLASH.nscr().modify(|w| {\n        w.set_strt(true);\n    });\n\n    let ret: Result<(), Error> = blocking_wait_ready();\n    #[cfg(feature = \"trustzone-secure\")]\n    pac::FLASH.seccr().modify(|w| w.set_per(false));\n    #[cfg(not(feature = \"trustzone-secure\"))]\n    pac::FLASH.nscr().modify(|w| w.set_per(false));\n    clear_all_err();\n    ret\n}\n\npub(crate) unsafe fn clear_all_err() {\n    // Explicit clear of all \"write 1 to clear\" bits in NSSR/SECSR (per RM).\n    // EOP, OPERR, PROGERR, WRPERR, PGAERR, SIZERR, PGSERR, OPTWERR.\n    #[cfg(feature = \"trustzone-secure\")]\n    pac::FLASH.secsr().modify(|w| {\n        w.set_eop(true);\n        w.set_operr(true);\n        w.set_progerr(true);\n        w.set_wrperr(true);\n        w.set_pgaerr(true);\n        w.set_sizerr(true);\n        w.set_pgserr(true);\n        w.set_optwerr(true);\n    });\n    #[cfg(not(feature = \"trustzone-secure\"))]\n    pac::FLASH.nssr().modify(|w| {\n        w.set_eop(true);\n        w.set_operr(true);\n        w.set_progerr(true);\n        w.set_wrperr(true);\n        w.set_pgaerr(true);\n        w.set_sizerr(true);\n        w.set_pgserr(true);\n        w.set_optwerr(true);\n    });\n}\n\nunsafe fn blocking_wait_ready() -> Result<(), Error> {\n    // Per RM: wait until WDW is cleared, then until BSY is cleared.\n    loop {\n        #[cfg(feature = \"trustzone-secure\")]\n        let sr = pac::FLASH.secsr().read();\n        #[cfg(not(feature = \"trustzone-secure\"))]\n        let sr = pac::FLASH.nssr().read();\n\n        if !sr.wdw() && !sr.bsy() {\n            if sr.pgserr() {\n                return Err(Error::Seq);\n            }\n\n            if sr.sizerr() {\n                return Err(Error::Size);\n            }\n\n            if sr.pgaerr() {\n                return Err(Error::Unaligned);\n            }\n\n            if sr.wrperr() {\n                return Err(Error::Protected);\n            }\n\n            if sr.progerr() {\n                return Err(Error::Prog);\n            }\n\n            if sr.operr() {\n                return Err(Error::Prog);\n            }\n\n            return Ok(());\n        }\n    }\n}\n"
  },
  {
    "path": "embassy-stm32/src/fmc.rs",
    "content": "//! Flexible Memory Controller (FMC) / Flexible Static Memory Controller (FSMC)\nuse core::marker::PhantomData;\n\nuse embassy_hal_internal::PeripheralType;\n\nuse crate::gpio::{AfType, OutputType, Pull, Speed};\nuse crate::{Peri, rcc};\n\n/// FMC driver\npub struct Fmc<'d, T: Instance> {\n    peri: PhantomData<&'d mut T>,\n}\n\nunsafe impl<'d, T> Send for Fmc<'d, T> where T: Instance {}\n\nimpl<'d, T> Fmc<'d, T>\nwhere\n    T: Instance,\n{\n    /// Create a raw FMC instance.\n    ///\n    /// **Note:** This is currently used to provide access to some basic FMC functions\n    /// for manual configuration for memory types that stm32-fmc does not support.\n    pub fn new_raw(_instance: Peri<'d, T>) -> Self {\n        Self { peri: PhantomData }\n    }\n\n    /// Enable the FMC peripheral and reset it.\n    pub fn enable(&mut self) {\n        rcc::enable_and_reset::<T>();\n    }\n\n    /// Enable the memory controller on applicable chips.\n    pub fn memory_controller_enable(&mut self) {\n        // fmc v1 and v2 does not have the fmcen bit\n        // fsmc v1, v2 and v3 does not have the fmcen bit\n        // This is a \"not\" because it is expected that all future versions have this bit\n        #[cfg(not(any(fmc_v1x3, fmc_v2x1, fsmc_v1x0, fsmc_v1x3, fmc_v4, fmc_n6)))]\n        T::REGS.bcr1().modify(|r| r.set_fmcen(true));\n        #[cfg(any(fmc_v4, fmc_n6))]\n        T::REGS.nor_psram().bcr1().modify(|r| r.set_fmcen(true));\n    }\n\n    /// Get the kernel clock currently in use for this FMC instance.\n    pub fn source_clock_hz(&self) -> u32 {\n        <T as crate::rcc::SealedRccPeripheral>::frequency().0\n    }\n}\n\nunsafe impl<'d, T> stm32_fmc::FmcPeripheral for Fmc<'d, T>\nwhere\n    T: Instance,\n{\n    const REGISTERS: *const () = T::REGS.as_ptr() as *const _;\n\n    fn enable(&mut self) {\n        rcc::enable_and_reset::<T>();\n    }\n\n    fn memory_controller_enable(&mut self) {\n        // fmc v1 and v2 does not have the fmcen bit\n        // fsmc v1, v2 and v3 does not have the fmcen bit\n        // This is a \"not\" because it is expected that all future versions have this bit\n        #[cfg(not(any(fmc_v1x3, fmc_v2x1, fsmc_v1x0, fsmc_v1x3, fmc_v4, fmc_n6)))]\n        T::REGS.bcr1().modify(|r| r.set_fmcen(true));\n        #[cfg(any(fmc_v4, fmc_n6))]\n        T::REGS.nor_psram().bcr1().modify(|r| r.set_fmcen(true));\n    }\n\n    fn source_clock_hz(&self) -> u32 {\n        <T as crate::rcc::SealedRccPeripheral>::frequency().0\n    }\n}\n\nmacro_rules! config_pins {\n    ($($pin:ident),*) => {\n                $(\n            set_as_af!($pin, AfType::output_pull(OutputType::PushPull, Speed::VeryHigh, Pull::Up));\n        )*\n    };\n}\n\nmacro_rules! fmc_sdram_constructor {\n    ($name:ident: (\n        bank: $bank:expr,\n        addr: [$(($addr_pin_name:ident: $addr_signal:ident)),*],\n        ba: [$(($ba_pin_name:ident: $ba_signal:ident)),*],\n        d: [$(($d_pin_name:ident: $d_signal:ident)),*],\n        nbl: [$(($nbl_pin_name:ident: $nbl_signal:ident)),*],\n        ctrl: [$(($ctrl_pin_name:ident: $ctrl_signal:ident)),*]\n    )) => {\n        /// Create a new FMC instance.\n        pub fn $name<CHIP: stm32_fmc::SdramChip>(\n            _instance: Peri<'d, T>,\n            $($addr_pin_name: Peri<'d, impl $addr_signal<T>>),*,\n            $($ba_pin_name: Peri<'d, impl $ba_signal<T>>),*,\n            $($d_pin_name: Peri<'d, impl $d_signal<T>>),*,\n            $($nbl_pin_name: Peri<'d, impl $nbl_signal<T>>),*,\n            $($ctrl_pin_name: Peri<'d, impl $ctrl_signal<T>>),*,\n            chip: CHIP\n        ) -> stm32_fmc::Sdram<Fmc<'d, T>, CHIP> {\n\n        critical_section::with(|_| {\n            config_pins!(\n                $($addr_pin_name),*,\n                $($ba_pin_name),*,\n                $($d_pin_name),*,\n                $($nbl_pin_name),*,\n                $($ctrl_pin_name),*\n            );\n        });\n\n            let fmc = Self { peri: PhantomData };\n            stm32_fmc::Sdram::new_unchecked(\n                fmc,\n                $bank,\n                chip,\n            )\n        }\n    };\n}\n\nimpl<'d, T: Instance> Fmc<'d, T> {\n    fmc_sdram_constructor!(sdram_a12bits_d16bits_4banks_bank1: (\n        bank: stm32_fmc::SdramTargetBank::Bank1,\n        addr: [\n            (a0: A0Pin), (a1: A1Pin), (a2: A2Pin), (a3: A3Pin), (a4: A4Pin), (a5: A5Pin), (a6: A6Pin), (a7: A7Pin), (a8: A8Pin), (a9: A9Pin), (a10: A10Pin), (a11: A11Pin)\n        ],\n        ba: [(ba0: BA0Pin), (ba1: BA1Pin)],\n        d: [\n            (d0: D0Pin), (d1: D1Pin), (d2: D2Pin), (d3: D3Pin), (d4: D4Pin), (d5: D5Pin), (d6: D6Pin), (d7: D7Pin),\n            (d8: D8Pin), (d9: D9Pin), (d10: D10Pin), (d11: D11Pin), (d12: D12Pin), (d13: D13Pin), (d14: D14Pin), (d15: D15Pin)\n        ],\n        nbl: [\n            (nbl0: NBL0Pin), (nbl1: NBL1Pin)\n        ],\n        ctrl: [\n            (sdcke: SDCKE0Pin), (sdclk: SDCLKPin), (sdncas: SDNCASPin), (sdne: SDNE0Pin), (sdnras: SDNRASPin), (sdnwe: SDNWEPin)\n        ]\n    ));\n\n    fmc_sdram_constructor!(sdram_a12bits_d32bits_4banks_bank1: (\n        bank: stm32_fmc::SdramTargetBank::Bank1,\n        addr: [\n            (a0: A0Pin), (a1: A1Pin), (a2: A2Pin), (a3: A3Pin), (a4: A4Pin), (a5: A5Pin), (a6: A6Pin), (a7: A7Pin), (a8: A8Pin), (a9: A9Pin), (a10: A10Pin), (a11: A11Pin)\n        ],\n        ba: [(ba0: BA0Pin), (ba1: BA1Pin)],\n        d: [\n            (d0: D0Pin), (d1: D1Pin), (d2: D2Pin), (d3: D3Pin), (d4: D4Pin), (d5: D5Pin), (d6: D6Pin), (d7: D7Pin),\n            (d8: D8Pin), (d9: D9Pin), (d10: D10Pin), (d11: D11Pin), (d12: D12Pin), (d13: D13Pin), (d14: D14Pin), (d15: D15Pin),\n            (d16: D16Pin), (d17: D17Pin), (d18: D18Pin), (d19: D19Pin), (d20: D20Pin), (d21: D21Pin), (d22: D22Pin), (d23: D23Pin),\n            (d24: D24Pin), (d25: D25Pin), (d26: D26Pin), (d27: D27Pin), (d28: D28Pin), (d29: D29Pin), (d30: D30Pin), (d31: D31Pin)\n        ],\n        nbl: [\n            (nbl0: NBL0Pin), (nbl1: NBL1Pin), (nbl2: NBL2Pin), (nbl3: NBL3Pin)\n        ],\n        ctrl: [\n            (sdcke: SDCKE0Pin), (sdclk: SDCLKPin), (sdncas: SDNCASPin), (sdne: SDNE0Pin), (sdnras: SDNRASPin), (sdnwe: SDNWEPin)\n        ]\n    ));\n\n    fmc_sdram_constructor!(sdram_a13bits_d32bits_4banks_bank1: (\n        bank: stm32_fmc::SdramTargetBank::Bank1,\n        addr: [\n            (a0: A0Pin), (a1: A1Pin), (a2: A2Pin), (a3: A3Pin), (a4: A4Pin), (a5: A5Pin), (a6: A6Pin), (a7: A7Pin), (a8: A8Pin), (a9: A9Pin), (a10: A10Pin), (a11: A11Pin), (a12: A12Pin)\n        ],\n        ba: [(ba0: BA0Pin), (ba1: BA1Pin)],\n        d: [\n            (d0: D0Pin), (d1: D1Pin), (d2: D2Pin), (d3: D3Pin), (d4: D4Pin), (d5: D5Pin), (d6: D6Pin), (d7: D7Pin),\n            (d8: D8Pin), (d9: D9Pin), (d10: D10Pin), (d11: D11Pin), (d12: D12Pin), (d13: D13Pin), (d14: D14Pin), (d15: D15Pin),\n            (d16: D16Pin), (d17: D17Pin), (d18: D18Pin), (d19: D19Pin), (d20: D20Pin), (d21: D21Pin), (d22: D22Pin), (d23: D23Pin),\n            (d24: D24Pin), (d25: D25Pin), (d26: D26Pin), (d27: D27Pin), (d28: D28Pin), (d29: D29Pin), (d30: D30Pin), (d31: D31Pin)\n        ],\n        nbl: [\n            (nbl0: NBL0Pin), (nbl1: NBL1Pin), (nbl2: NBL2Pin), (nbl3: NBL3Pin)\n        ],\n        ctrl: [\n            (sdcke: SDCKE0Pin), (sdclk: SDCLKPin), (sdncas: SDNCASPin), (sdne: SDNE0Pin), (sdnras: SDNRASPin), (sdnwe: SDNWEPin)\n        ]\n    ));\n\n    fmc_sdram_constructor!(sdram_a12bits_d16bits_4banks_bank2: (\n        bank: stm32_fmc::SdramTargetBank::Bank2,\n        addr: [\n            (a0: A0Pin), (a1: A1Pin), (a2: A2Pin), (a3: A3Pin), (a4: A4Pin), (a5: A5Pin), (a6: A6Pin), (a7: A7Pin), (a8: A8Pin), (a9: A9Pin), (a10: A10Pin), (a11: A11Pin)\n        ],\n        ba: [(ba0: BA0Pin), (ba1: BA1Pin)],\n        d: [\n            (d0: D0Pin), (d1: D1Pin), (d2: D2Pin), (d3: D3Pin), (d4: D4Pin), (d5: D5Pin), (d6: D6Pin), (d7: D7Pin),\n            (d8: D8Pin), (d9: D9Pin), (d10: D10Pin), (d11: D11Pin), (d12: D12Pin), (d13: D13Pin), (d14: D14Pin), (d15: D15Pin)\n        ],\n        nbl: [\n            (nbl0: NBL0Pin), (nbl1: NBL1Pin)\n        ],\n        ctrl: [\n            (sdcke: SDCKE1Pin), (sdclk: SDCLKPin), (sdncas: SDNCASPin), (sdne: SDNE1Pin), (sdnras: SDNRASPin), (sdnwe: SDNWEPin)\n        ]\n    ));\n\n    fmc_sdram_constructor!(sdram_a12bits_d32bits_4banks_bank2: (\n        bank: stm32_fmc::SdramTargetBank::Bank2,\n        addr: [\n            (a0: A0Pin), (a1: A1Pin), (a2: A2Pin), (a3: A3Pin), (a4: A4Pin), (a5: A5Pin), (a6: A6Pin), (a7: A7Pin), (a8: A8Pin), (a9: A9Pin), (a10: A10Pin), (a11: A11Pin)\n        ],\n        ba: [(ba0: BA0Pin), (ba1: BA1Pin)],\n        d: [\n            (d0: D0Pin), (d1: D1Pin), (d2: D2Pin), (d3: D3Pin), (d4: D4Pin), (d5: D5Pin), (d6: D6Pin), (d7: D7Pin),\n            (d8: D8Pin), (d9: D9Pin), (d10: D10Pin), (d11: D11Pin), (d12: D12Pin), (d13: D13Pin), (d14: D14Pin), (d15: D15Pin),\n            (d16: D16Pin), (d17: D17Pin), (d18: D18Pin), (d19: D19Pin), (d20: D20Pin), (d21: D21Pin), (d22: D22Pin), (d23: D23Pin),\n            (d24: D24Pin), (d25: D25Pin), (d26: D26Pin), (d27: D27Pin), (d28: D28Pin), (d29: D29Pin), (d30: D30Pin), (d31: D31Pin)\n        ],\n        nbl: [\n            (nbl0: NBL0Pin), (nbl1: NBL1Pin), (nbl2: NBL2Pin), (nbl3: NBL3Pin)\n        ],\n        ctrl: [\n            (sdcke: SDCKE1Pin), (sdclk: SDCLKPin), (sdncas: SDNCASPin), (sdne: SDNE1Pin), (sdnras: SDNRASPin), (sdnwe: SDNWEPin)\n        ]\n    ));\n\n    fmc_sdram_constructor!(sdram_a13bits_d32bits_4banks_bank2: (\n        bank: stm32_fmc::SdramTargetBank::Bank2,\n        addr: [\n            (a0: A0Pin), (a1: A1Pin), (a2: A2Pin), (a3: A3Pin), (a4: A4Pin), (a5: A5Pin), (a6: A6Pin), (a7: A7Pin), (a8: A8Pin), (a9: A9Pin), (a10: A10Pin), (a11: A11Pin), (a12: A12Pin)\n        ],\n        ba: [(ba0: BA0Pin), (ba1: BA1Pin)],\n        d: [\n            (d0: D0Pin), (d1: D1Pin), (d2: D2Pin), (d3: D3Pin), (d4: D4Pin), (d5: D5Pin), (d6: D6Pin), (d7: D7Pin),\n            (d8: D8Pin), (d9: D9Pin), (d10: D10Pin), (d11: D11Pin), (d12: D12Pin), (d13: D13Pin), (d14: D14Pin), (d15: D15Pin),\n            (d16: D16Pin), (d17: D17Pin), (d18: D18Pin), (d19: D19Pin), (d20: D20Pin), (d21: D21Pin), (d22: D22Pin), (d23: D23Pin),\n            (d24: D24Pin), (d25: D25Pin), (d26: D26Pin), (d27: D27Pin), (d28: D28Pin), (d29: D29Pin), (d30: D30Pin), (d31: D31Pin)\n        ],\n        nbl: [\n            (nbl0: NBL0Pin), (nbl1: NBL1Pin), (nbl2: NBL2Pin), (nbl3: NBL3Pin)\n        ],\n        ctrl: [\n            (sdcke: SDCKE1Pin), (sdclk: SDCLKPin), (sdncas: SDNCASPin), (sdne: SDNE1Pin), (sdnras: SDNRASPin), (sdnwe: SDNWEPin)\n        ]\n    ));\n\n    fmc_sdram_constructor!(sdram_a13bits_d16bits_4banks_bank1: (\n        bank: stm32_fmc::SdramTargetBank::Bank1,\n        addr: [\n            (a0: A0Pin), (a1: A1Pin), (a2: A2Pin), (a3: A3Pin), (a4: A4Pin), (a5: A5Pin), (a6: A6Pin), (a7: A7Pin), (a8: A8Pin), (a9: A9Pin), (a10: A10Pin), (a11: A11Pin), (a12: A12Pin)\n        ],\n        ba: [(ba0: BA0Pin), (ba1: BA1Pin)],\n        d: [\n            (d0: D0Pin), (d1: D1Pin), (d2: D2Pin), (d3: D3Pin), (d4: D4Pin), (d5: D5Pin), (d6: D6Pin), (d7: D7Pin),\n            (d8: D8Pin), (d9: D9Pin), (d10: D10Pin), (d11: D11Pin), (d12: D12Pin), (d13: D13Pin), (d14: D14Pin), (d15: D15Pin)\n        ],\n        nbl: [\n            (nbl0: NBL0Pin), (nbl1: NBL1Pin)\n        ],\n        ctrl: [\n            (sdcke: SDCKE0Pin), (sdclk: SDCLKPin), (sdncas: SDNCASPin), (sdne: SDNE0Pin), (sdnras: SDNRASPin), (sdnwe: SDNWEPin)\n        ]\n    ));\n\n    fmc_sdram_constructor!(sdram_a13bits_d16bits_4banks_bank2: (\n        bank: stm32_fmc::SdramTargetBank::Bank2,\n        addr: [\n            (a0: A0Pin), (a1: A1Pin), (a2: A2Pin), (a3: A3Pin), (a4: A4Pin), (a5: A5Pin), (a6: A6Pin), (a7: A7Pin), (a8: A8Pin), (a9: A9Pin), (a10: A10Pin), (a11: A11Pin), (a12: A12Pin)\n        ],\n        ba: [(ba0: BA0Pin), (ba1: BA1Pin)],\n        d: [\n            (d0: D0Pin), (d1: D1Pin), (d2: D2Pin), (d3: D3Pin), (d4: D4Pin), (d5: D5Pin), (d6: D6Pin), (d7: D7Pin),\n            (d8: D8Pin), (d9: D9Pin), (d10: D10Pin), (d11: D11Pin), (d12: D12Pin), (d13: D13Pin), (d14: D14Pin), (d15: D15Pin)\n        ],\n        nbl: [\n            (nbl0: NBL0Pin), (nbl1: NBL1Pin)\n        ],\n        ctrl: [\n            (sdcke: SDCKE1Pin), (sdclk: SDCLKPin), (sdncas: SDNCASPin), (sdne: SDNE1Pin), (sdnras: SDNRASPin), (sdnwe: SDNWEPin)\n        ]\n    ));\n}\n\ntrait SealedInstance: crate::rcc::RccPeripheral {\n    const REGS: crate::pac::fmc::Fmc;\n}\n\n/// FMC instance trait.\n#[allow(private_bounds)]\npub trait Instance: SealedInstance + PeripheralType + 'static {}\n\nforeach_peripheral!(\n    (fmc, $inst:ident) => {\n        impl crate::fmc::SealedInstance for crate::peripherals::$inst {\n            const REGS: crate::pac::fmc::Fmc = crate::pac::$inst;\n        }\n        impl crate::fmc::Instance for crate::peripherals::$inst {}\n    };\n);\n\npin_trait!(SDNWEPin, Instance);\npin_trait!(SDNCASPin, Instance);\npin_trait!(SDNRASPin, Instance);\n\npin_trait!(SDNE0Pin, Instance);\npin_trait!(SDNE1Pin, Instance);\n\npin_trait!(SDCKE0Pin, Instance);\npin_trait!(SDCKE1Pin, Instance);\n\npin_trait!(SDCLKPin, Instance);\n\npin_trait!(NBL0Pin, Instance);\npin_trait!(NBL1Pin, Instance);\npin_trait!(NBL2Pin, Instance);\npin_trait!(NBL3Pin, Instance);\n\npin_trait!(INTPin, Instance);\npin_trait!(NLPin, Instance);\npin_trait!(NWaitPin, Instance);\n\npin_trait!(NE1Pin, Instance);\npin_trait!(NE2Pin, Instance);\npin_trait!(NE3Pin, Instance);\npin_trait!(NE4Pin, Instance);\n\npin_trait!(NCEPin, Instance);\npin_trait!(NOEPin, Instance);\npin_trait!(NWEPin, Instance);\npin_trait!(ClkPin, Instance);\n\npin_trait!(BA0Pin, Instance);\npin_trait!(BA1Pin, Instance);\n\npin_trait!(D0Pin, Instance);\npin_trait!(D1Pin, Instance);\npin_trait!(D2Pin, Instance);\npin_trait!(D3Pin, Instance);\npin_trait!(D4Pin, Instance);\npin_trait!(D5Pin, Instance);\npin_trait!(D6Pin, Instance);\npin_trait!(D7Pin, Instance);\npin_trait!(D8Pin, Instance);\npin_trait!(D9Pin, Instance);\npin_trait!(D10Pin, Instance);\npin_trait!(D11Pin, Instance);\npin_trait!(D12Pin, Instance);\npin_trait!(D13Pin, Instance);\npin_trait!(D14Pin, Instance);\npin_trait!(D15Pin, Instance);\npin_trait!(D16Pin, Instance);\npin_trait!(D17Pin, Instance);\npin_trait!(D18Pin, Instance);\npin_trait!(D19Pin, Instance);\npin_trait!(D20Pin, Instance);\npin_trait!(D21Pin, Instance);\npin_trait!(D22Pin, Instance);\npin_trait!(D23Pin, Instance);\npin_trait!(D24Pin, Instance);\npin_trait!(D25Pin, Instance);\npin_trait!(D26Pin, Instance);\npin_trait!(D27Pin, Instance);\npin_trait!(D28Pin, Instance);\npin_trait!(D29Pin, Instance);\npin_trait!(D30Pin, Instance);\npin_trait!(D31Pin, Instance);\n\npin_trait!(DA0Pin, Instance);\npin_trait!(DA1Pin, Instance);\npin_trait!(DA2Pin, Instance);\npin_trait!(DA3Pin, Instance);\npin_trait!(DA4Pin, Instance);\npin_trait!(DA5Pin, Instance);\npin_trait!(DA6Pin, Instance);\npin_trait!(DA7Pin, Instance);\npin_trait!(DA8Pin, Instance);\npin_trait!(DA9Pin, Instance);\npin_trait!(DA10Pin, Instance);\npin_trait!(DA11Pin, Instance);\npin_trait!(DA12Pin, Instance);\npin_trait!(DA13Pin, Instance);\npin_trait!(DA14Pin, Instance);\npin_trait!(DA15Pin, Instance);\n\npin_trait!(A0Pin, Instance);\npin_trait!(A1Pin, Instance);\npin_trait!(A2Pin, Instance);\npin_trait!(A3Pin, Instance);\npin_trait!(A4Pin, Instance);\npin_trait!(A5Pin, Instance);\npin_trait!(A6Pin, Instance);\npin_trait!(A7Pin, Instance);\npin_trait!(A8Pin, Instance);\npin_trait!(A9Pin, Instance);\npin_trait!(A10Pin, Instance);\npin_trait!(A11Pin, Instance);\npin_trait!(A12Pin, Instance);\npin_trait!(A13Pin, Instance);\npin_trait!(A14Pin, Instance);\npin_trait!(A15Pin, Instance);\npin_trait!(A16Pin, Instance);\npin_trait!(A17Pin, Instance);\npin_trait!(A18Pin, Instance);\npin_trait!(A19Pin, Instance);\npin_trait!(A20Pin, Instance);\npin_trait!(A21Pin, Instance);\npin_trait!(A22Pin, Instance);\npin_trait!(A23Pin, Instance);\npin_trait!(A24Pin, Instance);\npin_trait!(A25Pin, Instance);\n"
  },
  {
    "path": "embassy-stm32/src/fmt.rs",
    "content": "#![macro_use]\n#![allow(unused)]\n\nuse core::fmt::{Debug, Display, LowerHex};\n\n#[cfg(all(feature = \"defmt\", feature = \"log\"))]\ncompile_error!(\"You may not enable both `defmt` and `log` features.\");\n\n#[collapse_debuginfo(yes)]\nmacro_rules! rcc_assert {\n    ($($x:tt)*) => {\n        {\n            #[cfg(not(feature = \"unchecked-overclocking\"))]\n            {\n                #[cfg(not(feature = \"defmt\"))]\n                ::core::assert!($($x)*);\n                #[cfg(feature = \"defmt\")]\n                ::defmt::assert!($($x)*);\n            }\n            #[cfg(feature = \"unchecked-overclocking\")]\n            {\n                #[cfg(feature = \"log\")]\n                ::log::warn!(\"`rcc_assert!` skipped: `unchecked-overclocking` feature is enabled.\");\n                #[cfg(feature = \"defmt\")]\n                ::defmt::warn!(\"`rcc_assert!` skipped: `unchecked-overclocking` feature is enabled.\");\n            }\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! assert {\n    ($($x:tt)*) => {\n        {\n            #[cfg(not(feature = \"defmt\"))]\n            ::core::assert!($($x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::assert!($($x)*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! assert_eq {\n    ($($x:tt)*) => {\n        {\n            #[cfg(not(feature = \"defmt\"))]\n            ::core::assert_eq!($($x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::assert_eq!($($x)*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! assert_ne {\n    ($($x:tt)*) => {\n        {\n            #[cfg(not(feature = \"defmt\"))]\n            ::core::assert_ne!($($x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::assert_ne!($($x)*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! debug_assert {\n    ($($x:tt)*) => {\n        {\n            #[cfg(not(feature = \"defmt\"))]\n            ::core::debug_assert!($($x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::debug_assert!($($x)*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! debug_assert_eq {\n    ($($x:tt)*) => {\n        {\n            #[cfg(not(feature = \"defmt\"))]\n            ::core::debug_assert_eq!($($x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::debug_assert_eq!($($x)*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! debug_assert_ne {\n    ($($x:tt)*) => {\n        {\n            #[cfg(not(feature = \"defmt\"))]\n            ::core::debug_assert_ne!($($x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::debug_assert_ne!($($x)*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! todo {\n    ($($x:tt)*) => {\n        {\n            #[cfg(not(feature = \"defmt\"))]\n            ::core::todo!($($x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::todo!($($x)*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! unreachable {\n    ($($x:tt)*) => {\n        {\n            #[cfg(not(feature = \"defmt\"))]\n            ::core::unreachable!($($x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::unreachable!($($x)*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! panic {\n    ($($x:tt)*) => {\n        {\n            #[cfg(not(feature = \"defmt\"))]\n            ::core::panic!($($x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::panic!($($x)*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! trace {\n    ($s:literal $(, $x:expr)* $(,)?) => {\n        {\n            #[cfg(feature = \"log\")]\n            ::log::trace!($s $(, $x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::trace!($s $(, $x)*);\n            #[cfg(not(any(feature = \"log\", feature=\"defmt\")))]\n            let _ = ($( & $x ),*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! debug {\n    ($s:literal $(, $x:expr)* $(,)?) => {\n        {\n            #[cfg(feature = \"log\")]\n            ::log::debug!($s $(, $x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::debug!($s $(, $x)*);\n            #[cfg(not(any(feature = \"log\", feature=\"defmt\")))]\n            let _ = ($( & $x ),*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! info {\n    ($s:literal $(, $x:expr)* $(,)?) => {\n        {\n            #[cfg(feature = \"log\")]\n            ::log::info!($s $(, $x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::info!($s $(, $x)*);\n            #[cfg(not(any(feature = \"log\", feature=\"defmt\")))]\n            let _ = ($( & $x ),*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! warn {\n    ($s:literal $(, $x:expr)* $(,)?) => {\n        {\n            #[cfg(feature = \"log\")]\n            ::log::warn!($s $(, $x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::warn!($s $(, $x)*);\n            #[cfg(not(any(feature = \"log\", feature=\"defmt\")))]\n            let _ = ($( & $x ),*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! error {\n    ($s:literal $(, $x:expr)* $(,)?) => {\n        {\n            #[cfg(feature = \"log\")]\n            ::log::error!($s $(, $x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::error!($s $(, $x)*);\n            #[cfg(not(any(feature = \"log\", feature=\"defmt\")))]\n            let _ = ($( & $x ),*);\n        }\n    };\n}\n\n#[cfg(feature = \"defmt\")]\ntrait_set::trait_set! {\n    pub trait Debuggable = Debug + defmt::Format;\n}\n\n#[cfg(not(feature = \"defmt\"))]\ntrait_set::trait_set! {\n    pub trait Debuggable = Debug;\n}\n\n#[cfg(feature = \"defmt\")]\n#[collapse_debuginfo(yes)]\nmacro_rules! unwrap {\n    ($($x:tt)*) => {\n        ::defmt::unwrap!($($x)*)\n    };\n}\n\n#[cfg(not(feature = \"defmt\"))]\n#[collapse_debuginfo(yes)]\nmacro_rules! unwrap {\n    ($arg:expr) => {\n        match $crate::fmt::Try::into_result($arg) {\n            ::core::result::Result::Ok(t) => t,\n            ::core::result::Result::Err(e) => {\n                ::core::panic!(\"unwrap of `{}` failed: {:?}\", ::core::stringify!($arg), e);\n            }\n        }\n    };\n    ($arg:expr, $($msg:expr),+ $(,)? ) => {\n        match $crate::fmt::Try::into_result($arg) {\n            ::core::result::Result::Ok(t) => t,\n            ::core::result::Result::Err(e) => {\n                ::core::panic!(\"unwrap of `{}` failed: {}: {:?}\", ::core::stringify!($arg), ::core::format_args!($($msg,)*), e);\n            }\n        }\n    }\n}\n\n#[derive(Debug, Copy, Clone, Eq, PartialEq)]\npub struct NoneError;\n\npub trait Try {\n    type Ok;\n    type Error;\n    fn into_result(self) -> Result<Self::Ok, Self::Error>;\n}\n\nimpl<T> Try for Option<T> {\n    type Ok = T;\n    type Error = NoneError;\n\n    #[inline]\n    fn into_result(self) -> Result<T, NoneError> {\n        self.ok_or(NoneError)\n    }\n}\n\nimpl<T, E> Try for Result<T, E> {\n    type Ok = T;\n    type Error = E;\n\n    #[inline]\n    fn into_result(self) -> Self {\n        self\n    }\n}\n\npub(crate) struct Bytes<'a>(pub &'a [u8]);\n\nimpl<'a> Debug for Bytes<'a> {\n    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {\n        write!(f, \"{:#02x?}\", self.0)\n    }\n}\n\nimpl<'a> Display for Bytes<'a> {\n    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {\n        write!(f, \"{:#02x?}\", self.0)\n    }\n}\n\nimpl<'a> LowerHex for Bytes<'a> {\n    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {\n        write!(f, \"{:#02x?}\", self.0)\n    }\n}\n\n#[cfg(feature = \"defmt\")]\nimpl<'a> defmt::Format for Bytes<'a> {\n    fn format(&self, fmt: defmt::Formatter) {\n        defmt::write!(fmt, \"{:02x}\", self.0)\n    }\n}\n"
  },
  {
    "path": "embassy-stm32/src/gpio.rs",
    "content": "//! General-purpose Input/Output (GPIO)\n\n#![macro_use]\nuse core::convert::Infallible;\n\nuse critical_section::CriticalSection;\nuse embassy_hal_internal::{Peri, PeripheralType, impl_peripheral};\n\nuse crate::pac::gpio::{self, vals};\nuse crate::peripherals;\n\n/// GPIO flexible pin.\n///\n/// This pin can either be a disconnected, input, or output pin, or both. The level register bit will remain\n/// set while not in output mode, so the pin's level will be 'remembered' when it is not in output\n/// mode.\npub struct Flex<'d> {\n    pub(crate) pin: Peri<'d, AnyPin>,\n}\n\nimpl<'d> Flex<'d> {\n    /// Wrap the pin in a `Flex`.\n    ///\n    /// The pin remains disconnected. The initial output level is unspecified, but can be changed\n    /// before the pin is put into output mode.\n    ///\n    #[inline]\n    pub fn new(pin: Peri<'d, impl Pin>) -> Self {\n        // Pin will be in disconnected state.\n        Self { pin: pin.into() }\n    }\n\n    /// Reborrow into a \"child\" Flex.\n    ///\n    /// `self` will stay borrowed until the child Peripheral is dropped.\n    pub fn reborrow(&mut self) -> Flex<'_> {\n        Flex {\n            pin: self.pin.reborrow(),\n        }\n    }\n\n    /// Unsafely clone (duplicate) a Flex.\n    pub unsafe fn clone_unchecked(&self) -> Flex<'d> {\n        Flex {\n            pin: self.pin.clone_unchecked(),\n        }\n    }\n\n    /// Put the pin into input mode.\n    ///\n    /// The internal weak pull-up and pull-down resistors will be enabled according to `pull`.\n    #[inline(never)]\n    pub fn set_as_input(&mut self, pull: Pull) {\n        critical_section::with(|_| {\n            let r = self.pin.block();\n            let n = self.pin.pin() as usize;\n            #[cfg(gpio_v1)]\n            {\n                let cnf = match pull {\n                    Pull::Up => {\n                        r.bsrr().write(|w| w.set_bs(n, true));\n                        vals::CnfIn::PULL\n                    }\n                    Pull::Down => {\n                        r.bsrr().write(|w| w.set_br(n, true));\n                        vals::CnfIn::PULL\n                    }\n                    Pull::None => vals::CnfIn::FLOATING,\n                };\n\n                r.cr(n / 8).modify(|w| {\n                    w.set_mode(n % 8, vals::Mode::INPUT);\n                    w.set_cnf_in(n % 8, cnf);\n                });\n            }\n            #[cfg(gpio_v2)]\n            {\n                r.pupdr().modify(|w| w.set_pupdr(n, pull.to_pupdr()));\n                r.otyper().modify(|w| w.set_ot(n, vals::Ot::PUSH_PULL));\n                r.moder().modify(|w| w.set_moder(n, vals::Moder::INPUT));\n            }\n        });\n    }\n\n    /// Put the pin into push-pull output mode.\n    ///\n    /// The pin level will be whatever was set before (or low by default). If you want it to begin\n    /// at a specific level, call `set_high`/`set_low` on the pin first.\n    ///\n    /// The internal weak pull-up and pull-down resistors will be disabled.\n    #[inline(never)]\n    pub fn set_as_output(&mut self, speed: Speed) {\n        critical_section::with(|_| {\n            let r = self.pin.block();\n            let n = self.pin.pin() as usize;\n            #[cfg(gpio_v1)]\n            {\n                r.cr(n / 8).modify(|w| {\n                    w.set_mode(n % 8, speed.to_mode());\n                    w.set_cnf_out(n % 8, vals::CnfOut::PUSH_PULL);\n                });\n            }\n            #[cfg(gpio_v2)]\n            {\n                r.pupdr().modify(|w| w.set_pupdr(n, vals::Pupdr::FLOATING));\n                r.otyper().modify(|w| w.set_ot(n, vals::Ot::PUSH_PULL));\n                r.ospeedr().modify(|w| w.set_ospeedr(n, speed.to_ospeedr()));\n                r.moder().modify(|w| w.set_moder(n, vals::Moder::OUTPUT));\n            }\n        });\n    }\n\n    /// Put the pin into input + open-drain output mode.\n    ///\n    /// The hardware will drive the line low if you set it to low, and will leave it floating if you set\n    /// it to high, in which case you can read the input to figure out whether another device\n    /// is driving the line low.\n    ///\n    /// The pin level will be whatever was set before (or low by default). If you want it to begin\n    /// at a specific level, call `set_high`/`set_low` on the pin first.\n    ///\n    /// The internal weak pull-up and pull-down resistors will be disabled.\n    #[inline(never)]\n    pub fn set_as_input_output(&mut self, speed: Speed) {\n        #[cfg(gpio_v1)]\n        critical_section::with(|_| {\n            let r = self.pin.block();\n            let n = self.pin.pin() as usize;\n            r.cr(n / 8).modify(|w| w.set_mode(n % 8, speed.to_mode()));\n            r.cr(n / 8).modify(|w| w.set_cnf_out(n % 8, vals::CnfOut::OPEN_DRAIN));\n        });\n\n        #[cfg(gpio_v2)]\n        self.set_as_input_output_pull(speed, Pull::None);\n    }\n\n    /// Put the pin into input + open-drain output mode with internal pullup or pulldown.\n    ///\n    /// This works like [`Self::set_as_input_output()`], but it also allows to enable the internal\n    /// weak pull-up or pull-down resistors.\n    #[inline(never)]\n    #[cfg(gpio_v2)]\n    pub fn set_as_input_output_pull(&mut self, speed: Speed, pull: Pull) {\n        critical_section::with(|_| {\n            let r = self.pin.block();\n            let n = self.pin.pin() as usize;\n            r.pupdr().modify(|w| w.set_pupdr(n, pull.to_pupdr()));\n            r.otyper().modify(|w| w.set_ot(n, vals::Ot::OPEN_DRAIN));\n            r.ospeedr().modify(|w| w.set_ospeedr(n, speed.to_ospeedr()));\n            r.moder().modify(|w| w.set_moder(n, vals::Moder::OUTPUT));\n        });\n    }\n\n    /// Put the pin into analog mode\n    ///\n    /// This mode is used by ADC and COMP but usually there is no need to set this manually\n    /// as the mode change is handled by the driver.\n    #[inline]\n    pub fn set_as_analog(&mut self) {\n        // TODO: does this also need a critical section, like other methods?\n        self.pin.set_as_analog();\n    }\n\n    /// Put the pin into AF mode, unchecked.\n    ///\n    /// This puts the pin into the AF mode, with the requested number and AF type. This is\n    /// completely unchecked, it can attach the pin to literally any peripheral, so use with care.\n    #[inline]\n    pub fn set_as_af_unchecked(&mut self, #[cfg(not(afio))] af_num: u8, af_type: AfType) {\n        critical_section::with(|_| {\n            self.pin.set_as_af(\n                #[cfg(not(afio))]\n                af_num,\n                af_type,\n            );\n        });\n    }\n\n    /// Get whether the pin input level is high.\n    #[inline]\n    pub fn is_high(&self) -> bool {\n        !self.is_low()\n    }\n\n    /// Get whether the pin input level is low.\n    #[inline]\n    pub fn is_low(&self) -> bool {\n        let state = self.pin.block().idr().read().idr(self.pin.pin() as _);\n        state == vals::Idr::LOW\n    }\n\n    /// Get the current pin input level.\n    #[inline]\n    pub fn get_level(&self) -> Level {\n        self.is_high().into()\n    }\n\n    /// Get whether the output level is set to high.\n    #[inline]\n    pub fn is_set_high(&self) -> bool {\n        !self.is_set_low()\n    }\n\n    /// Get whether the output level is set to low.\n    #[inline]\n    pub fn is_set_low(&self) -> bool {\n        let state = self.pin.block().odr().read().odr(self.pin.pin() as _);\n        state == vals::Odr::LOW\n    }\n\n    /// Get the current output level.\n    #[inline]\n    pub fn get_output_level(&self) -> Level {\n        self.is_set_high().into()\n    }\n\n    /// Set the output as high.\n    #[inline]\n    pub fn set_high(&mut self) {\n        self.pin.set_high();\n    }\n\n    /// Set the output as low.\n    #[inline]\n    pub fn set_low(&mut self) {\n        self.pin.set_low();\n    }\n\n    /// Set the output level.\n    #[inline]\n    pub fn set_level(&mut self, level: Level) {\n        match level {\n            Level::Low => self.pin.set_low(),\n            Level::High => self.pin.set_high(),\n        }\n    }\n\n    /// Toggle the output level.\n    #[inline]\n    pub fn toggle(&mut self) {\n        if self.is_set_low() {\n            self.set_high()\n        } else {\n            self.set_low()\n        }\n    }\n}\n\nimpl<'d> Drop for Flex<'d> {\n    #[inline]\n    fn drop(&mut self) {\n        trace!(\"gpio: dropping {}\", self.pin);\n        critical_section::with(|_| {\n            self.pin.set_as_disconnected();\n        });\n    }\n}\n\n/// Pull setting for an input.\n#[derive(Debug, Eq, PartialEq, Copy, Clone)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub enum Pull {\n    /// No pull\n    None,\n    /// Pull up\n    Up,\n    /// Pull down\n    Down,\n}\n\nimpl Pull {\n    #[cfg(gpio_v2)]\n    const fn to_pupdr(self) -> vals::Pupdr {\n        match self {\n            Pull::None => vals::Pupdr::FLOATING,\n            Pull::Up => vals::Pupdr::PULL_UP,\n            Pull::Down => vals::Pupdr::PULL_DOWN,\n        }\n    }\n}\n\n/// Speed setting for an output.\n///\n/// These vary depending on the chip, check the reference manual and datasheet (\"I/O port\n/// characteristics\") for details.\n#[derive(Debug, Copy, Clone)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub enum Speed {\n    #[cfg_attr(gpio_v1, doc = \"Output speed OUTPUT2MHZ\")]\n    #[cfg_attr(gpio_v2, doc = \"Output speed 00\")]\n    Low,\n    #[cfg_attr(gpio_v1, doc = \"Output speed OUTPUT10MHZ\")]\n    #[cfg_attr(gpio_v2, doc = \"Output speed 01\")]\n    Medium,\n    #[cfg_attr(gpio_v2, doc = \"Output speed 10\")]\n    #[cfg(not(any(gpio_v1, syscfg_f0)))]\n    High,\n    #[cfg_attr(gpio_v1, doc = \"Output speed OUTPUT50MHZ\")]\n    #[cfg_attr(gpio_v2, doc = \"Output speed 11\")]\n    VeryHigh,\n}\n\nimpl Speed {\n    #[cfg(gpio_v1)]\n    const fn to_mode(self) -> vals::Mode {\n        match self {\n            Speed::Low => vals::Mode::OUTPUT2MHZ,\n            Speed::Medium => vals::Mode::OUTPUT10MHZ,\n            Speed::VeryHigh => vals::Mode::OUTPUT50MHZ,\n        }\n    }\n\n    #[cfg(gpio_v2)]\n    const fn to_ospeedr(self: Speed) -> vals::Ospeedr {\n        match self {\n            Speed::Low => vals::Ospeedr::LOW_SPEED,\n            Speed::Medium => vals::Ospeedr::MEDIUM_SPEED,\n            #[cfg(not(syscfg_f0))]\n            Speed::High => vals::Ospeedr::HIGH_SPEED,\n            Speed::VeryHigh => vals::Ospeedr::VERY_HIGH_SPEED,\n        }\n    }\n}\n\n/// GPIO input driver.\npub struct Input<'d> {\n    pub(crate) pin: Flex<'d>,\n}\n\nimpl<'d> Input<'d> {\n    /// Create GPIO input driver for a [Pin] with the provided [Pull] configuration.\n    #[inline]\n    pub fn new(pin: Peri<'d, impl Pin>, pull: Pull) -> Self {\n        let mut pin = Flex::new(pin);\n        pin.set_as_input(pull);\n        Self { pin }\n    }\n\n    /// Create a GPIO input driver from an existing [`Flex`] pin.\n    ///\n    /// This is useful when a pin was previously used in bidirectional mode and\n    /// needs to be converted to a typed input driver without re-acquiring the\n    /// peripheral token. The pin should already be configured as an input via\n    /// [`Flex::set_as_input()`].\n    #[inline]\n    pub fn from_flex(pin: Flex<'d>) -> Self {\n        Self { pin }\n    }\n\n    /// Get whether the pin input level is high.\n    #[inline]\n    pub fn is_high(&self) -> bool {\n        self.pin.is_high()\n    }\n\n    /// Get whether the pin input level is low.\n    #[inline]\n    pub fn is_low(&self) -> bool {\n        self.pin.is_low()\n    }\n\n    /// Get the current pin input level.\n    #[inline]\n    pub fn get_level(&self) -> Level {\n        self.pin.get_level()\n    }\n}\n\n/// Digital input or output level.\n#[derive(Debug, Eq, PartialEq, Copy, Clone)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub enum Level {\n    /// Low\n    Low,\n    /// High\n    High,\n}\n\nimpl From<bool> for Level {\n    fn from(val: bool) -> Self {\n        match val {\n            true => Self::High,\n            false => Self::Low,\n        }\n    }\n}\n\nimpl From<Level> for bool {\n    fn from(level: Level) -> bool {\n        match level {\n            Level::Low => false,\n            Level::High => true,\n        }\n    }\n}\n\n/// GPIO output driver.\n///\n/// Note that pins will **return to their floating state** when `Output` is dropped.\n/// If pins should retain their state indefinitely, either keep ownership of the\n/// `Output`, or pass it to [`core::mem::forget`].\npub struct Output<'d> {\n    pub(crate) pin: Flex<'d>,\n}\n\nimpl<'d> Output<'d> {\n    /// Create GPIO output driver for a [Pin] with the provided [Level] and [Speed] configuration.\n    #[inline]\n    pub fn new(pin: Peri<'d, impl Pin>, initial_output: Level, speed: Speed) -> Self {\n        let mut pin = Flex::new(pin);\n        match initial_output {\n            Level::High => pin.set_high(),\n            Level::Low => pin.set_low(),\n        }\n        pin.set_as_output(speed);\n        Self { pin }\n    }\n\n    /// Set the output as high.\n    #[inline]\n    pub fn set_high(&mut self) {\n        self.pin.set_high();\n    }\n\n    /// Set the output as low.\n    #[inline]\n    pub fn set_low(&mut self) {\n        self.pin.set_low();\n    }\n\n    /// Set the output level.\n    #[inline]\n    pub fn set_level(&mut self, level: Level) {\n        self.pin.set_level(level)\n    }\n\n    /// Is the output pin set as high?\n    #[inline]\n    pub fn is_set_high(&self) -> bool {\n        self.pin.is_set_high()\n    }\n\n    /// Is the output pin set as low?\n    #[inline]\n    pub fn is_set_low(&self) -> bool {\n        self.pin.is_set_low()\n    }\n\n    /// What level output is set to\n    #[inline]\n    pub fn get_output_level(&self) -> Level {\n        self.pin.get_output_level()\n    }\n\n    /// Toggle pin output\n    #[inline]\n    pub fn toggle(&mut self) {\n        self.pin.toggle();\n    }\n}\n\n/// GPIO output open-drain driver.\n///\n/// Note that pins will **return to their floating state** when `OutputOpenDrain` is dropped.\n/// If pins should retain their state indefinitely, either keep ownership of the\n/// `OutputOpenDrain`, or pass it to [`core::mem::forget`].\npub struct OutputOpenDrain<'d> {\n    pub(crate) pin: Flex<'d>,\n}\n\nimpl<'d> OutputOpenDrain<'d> {\n    /// Create a new GPIO open drain output driver for a [Pin] with the provided [Level] and [Speed].\n    #[inline]\n    pub fn new(pin: Peri<'d, impl Pin>, initial_output: Level, speed: Speed) -> Self {\n        let mut pin = Flex::new(pin);\n        match initial_output {\n            Level::High => pin.set_high(),\n            Level::Low => pin.set_low(),\n        }\n        pin.set_as_input_output(speed);\n        Self { pin }\n    }\n\n    /// Create a new GPIO open drain output driver for a [Pin] with the provided [Level], [Speed]\n    /// and [Pull].\n    #[inline]\n    #[cfg(gpio_v2)]\n    pub fn new_pull(pin: Peri<'d, impl Pin>, initial_output: Level, speed: Speed, pull: Pull) -> Self {\n        let mut pin = Flex::new(pin);\n        match initial_output {\n            Level::High => pin.set_high(),\n            Level::Low => pin.set_low(),\n        }\n        pin.set_as_input_output_pull(speed, pull);\n        Self { pin }\n    }\n\n    /// Get whether the pin input level is high.\n    #[inline]\n    pub fn is_high(&self) -> bool {\n        !self.pin.is_low()\n    }\n\n    /// Get whether the pin input level is low.\n    #[inline]\n    pub fn is_low(&self) -> bool {\n        self.pin.is_low()\n    }\n\n    /// Get the current pin input level.\n    #[inline]\n    pub fn get_level(&self) -> Level {\n        self.pin.get_level()\n    }\n\n    /// Set the output as high.\n    #[inline]\n    pub fn set_high(&mut self) {\n        self.pin.set_high();\n    }\n\n    /// Set the output as low.\n    #[inline]\n    pub fn set_low(&mut self) {\n        self.pin.set_low();\n    }\n\n    /// Set the output level.\n    #[inline]\n    pub fn set_level(&mut self, level: Level) {\n        self.pin.set_level(level);\n    }\n\n    /// Get whether the output level is set to high.\n    #[inline]\n    pub fn is_set_high(&self) -> bool {\n        self.pin.is_set_high()\n    }\n\n    /// Get whether the output level is set to low.\n    #[inline]\n    pub fn is_set_low(&self) -> bool {\n        self.pin.is_set_low()\n    }\n\n    /// Get the current output level.\n    #[inline]\n    pub fn get_output_level(&self) -> Level {\n        self.pin.get_output_level()\n    }\n\n    /// Toggle pin output\n    #[inline]\n    pub fn toggle(&mut self) {\n        self.pin.toggle()\n    }\n}\n\n/// GPIO output type\n#[derive(Debug, Copy, Clone)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub enum OutputType {\n    /// Drive the pin both high or low.\n    PushPull,\n    /// Drive the pin low, or don't drive it at all if the output level is high.\n    OpenDrain,\n}\n\nimpl OutputType {\n    #[cfg(gpio_v1)]\n    const fn to_cnf_out(self) -> vals::CnfOut {\n        match self {\n            OutputType::PushPull => vals::CnfOut::ALT_PUSH_PULL,\n            OutputType::OpenDrain => vals::CnfOut::ALT_OPEN_DRAIN,\n        }\n    }\n\n    #[cfg(gpio_v2)]\n    const fn to_ot(self) -> vals::Ot {\n        match self {\n            OutputType::PushPull => vals::Ot::PUSH_PULL,\n            OutputType::OpenDrain => vals::Ot::OPEN_DRAIN,\n        }\n    }\n}\n\n/// Alternate function type settings.\n#[derive(Copy, Clone)]\n#[cfg(gpio_v1)]\npub struct AfType {\n    mode: vals::Mode,\n    cnf: u8,\n    pull: Pull,\n}\n\n#[cfg(gpio_v1)]\nimpl AfType {\n    /// Input with optional pullup or pulldown.\n    pub const fn input(pull: Pull) -> Self {\n        let cnf_in = match pull {\n            Pull::Up | Pull::Down => vals::CnfIn::PULL,\n            Pull::None => vals::CnfIn::FLOATING,\n        };\n        Self {\n            mode: vals::Mode::INPUT,\n            cnf: cnf_in.to_bits(),\n            pull,\n        }\n    }\n\n    /// Output with output type and speed and no pull-up or pull-down.\n    pub const fn output(output_type: OutputType, speed: Speed) -> Self {\n        Self {\n            mode: speed.to_mode(),\n            cnf: output_type.to_cnf_out().to_bits(),\n            pull: Pull::None,\n        }\n    }\n}\n\n#[inline(never)]\n#[cfg(gpio_v1)]\nfn set_as_af(pin_port: PinNumber, af_type: AfType) {\n    let pin = unsafe { AnyPin::steal(pin_port) };\n    let r = pin.block();\n    let n = pin._pin() as usize;\n\n    r.cr(n / 8).modify(|w| {\n        w.set_mode(n % 8, af_type.mode);\n        // note that we are writing the CNF field, which is exposed as both `cnf_in` and `cnf_out`\n        // in the PAC. the choice of `cnf_in` instead of `cnf_out` in this code is arbitrary and\n        // does not affect the result.\n        w.set_cnf_in(n % 8, vals::CnfIn::from_bits(af_type.cnf));\n    });\n\n    match af_type.pull {\n        Pull::Up => r.bsrr().write(|w| w.set_bs(n, true)),\n        Pull::Down => r.bsrr().write(|w| w.set_br(n, true)),\n        Pull::None => {}\n    }\n}\n\n/// Alternate function type settings.\n#[derive(Copy, Clone)]\n#[cfg(gpio_v2)]\npub struct AfType {\n    pupdr: vals::Pupdr,\n    ot: vals::Ot,\n    ospeedr: vals::Ospeedr,\n}\n\n#[cfg(gpio_v2)]\nimpl AfType {\n    /// Input with optional pullup or pulldown.\n    pub const fn input(pull: Pull) -> Self {\n        Self {\n            pupdr: pull.to_pupdr(),\n            ot: vals::Ot::PUSH_PULL,\n            ospeedr: vals::Ospeedr::LOW_SPEED,\n        }\n    }\n\n    /// Output with output type and speed and no pull-up or pull-down.\n    pub const fn output(output_type: OutputType, speed: Speed) -> Self {\n        Self::output_pull(output_type, speed, Pull::None)\n    }\n\n    /// Output with output type, speed and pull-up or pull-down;\n    pub const fn output_pull(output_type: OutputType, speed: Speed, pull: Pull) -> Self {\n        Self {\n            pupdr: pull.to_pupdr(),\n            ot: output_type.to_ot(),\n            ospeedr: speed.to_ospeedr(),\n        }\n    }\n}\n\n#[inline(never)]\n#[cfg(gpio_v2)]\nfn set_as_af(pin_port: PinNumber, af_num: u8, af_type: AfType) {\n    let pin = unsafe { AnyPin::steal(pin_port) };\n    let r = pin.block();\n    let n = pin._pin() as usize;\n\n    r.afr(n / 8).modify(|w| w.set_afr(n % 8, af_num));\n    r.pupdr().modify(|w| w.set_pupdr(n, af_type.pupdr));\n    r.otyper().modify(|w| w.set_ot(n, af_type.ot));\n    r.ospeedr().modify(|w| w.set_ospeedr(n, af_type.ospeedr));\n    r.moder().modify(|w| w.set_moder(n, vals::Moder::ALTERNATE));\n}\n\n#[inline(never)]\n#[cfg(gpio_v2)]\nfn set_speed(pin_port: PinNumber, speed: Speed) {\n    let pin = unsafe { AnyPin::steal(pin_port) };\n    let r = pin.block();\n    let n = pin._pin() as usize;\n\n    r.ospeedr().modify(|w| w.set_ospeedr(n, speed.to_ospeedr()));\n}\n\n#[inline(never)]\npub(crate) fn set_as_analog(pin_port: PinNumber) {\n    let pin = unsafe { AnyPin::steal(pin_port) };\n    let r = pin.block();\n    let n = pin._pin() as usize;\n\n    #[cfg(gpio_v1)]\n    r.cr(n / 8).modify(|w| {\n        w.set_mode(n % 8, vals::Mode::INPUT);\n        w.set_cnf_in(n % 8, vals::CnfIn::ANALOG);\n    });\n\n    #[cfg(gpio_v2)]\n    {\n        #[cfg(any(stm32l47x, stm32l48x))]\n        r.ascr().modify(|w| w.set_asc(n, true));\n        r.moder().modify(|w| w.set_moder(n, vals::Moder::ANALOG));\n    }\n}\n\n#[inline(never)]\nfn get_pull(pin_port: PinNumber) -> Pull {\n    let pin = unsafe { AnyPin::steal(pin_port) };\n    let r = pin.block();\n    let n = pin._pin() as usize;\n\n    #[cfg(gpio_v1)]\n    return match r.cr(n / 8).read().mode(n % 8) {\n        vals::Mode::INPUT => match r.cr(n / 8).read().cnf_in(n % 8) {\n            vals::CnfIn::PULL => match r.odr().read().odr(n) {\n                vals::Odr::LOW => Pull::Down,\n                vals::Odr::HIGH => Pull::Up,\n            },\n            _ => Pull::None,\n        },\n        _ => Pull::None,\n    };\n\n    #[cfg(gpio_v2)]\n    return match r.pupdr().read().pupdr(n) {\n        vals::Pupdr::FLOATING => Pull::None,\n        vals::Pupdr::PULL_DOWN => Pull::Down,\n        vals::Pupdr::PULL_UP => Pull::Up,\n        vals::Pupdr::_RESERVED_3 => Pull::None,\n    };\n}\n\n#[cfg(afio)]\n/// Holds the AFIO remap value for a peripheral's pin\npub struct AfioRemap<const V: u8>;\n\n#[cfg(afio)]\n/// Holds the AFIO remap value for a peripheral's pin\npub struct AfioRemapBool<const V: bool>;\n\n#[cfg(afio)]\n/// Placeholder for a peripheral's pin which cannot be remapped via AFIO.\npub struct AfioRemapNotApplicable;\n\npub(crate) trait SealedPin {\n    fn pin_port(&self) -> PinNumber;\n\n    #[inline]\n    fn _pin(&self) -> PinNumber {\n        self.pin_port() % 16\n    }\n\n    #[inline]\n    fn _port(&self) -> PinNumber {\n        self.pin_port() / 16\n    }\n\n    #[inline]\n    fn block(&self) -> gpio::Gpio {\n        crate::_generated::gpio_block(self._port() as _)\n    }\n\n    /// Set the output as high.\n    #[inline]\n    fn set_high(&self) {\n        let n = self._pin() as _;\n        self.block().bsrr().write(|w| w.set_bs(n, true));\n    }\n\n    /// Set the output as low.\n    #[inline]\n    fn set_low(&self) {\n        let n = self._pin() as _;\n        self.block().bsrr().write(|w| w.set_br(n, true));\n    }\n\n    #[inline]\n    fn set_as_af(&self, #[cfg(not(afio))] af_num: u8, af_type: AfType) {\n        set_as_af(\n            self.pin_port(),\n            #[cfg(not(afio))]\n            af_num,\n            af_type,\n        )\n    }\n\n    #[inline]\n    #[cfg(gpio_v2)]\n    fn set_speed(&self, speed: Speed) {\n        set_speed(self.pin_port(), speed)\n    }\n\n    #[inline]\n    fn set_as_analog(&self) {\n        set_as_analog(self.pin_port());\n    }\n\n    /// Set the pin as \"disconnected\", ie doing nothing and consuming the lowest\n    /// amount of power possible.\n    ///\n    /// This is currently the same as [`Self::set_as_analog()`] but is semantically different\n    /// really. Drivers should `set_as_disconnected()` pins when dropped.\n    ///\n    /// Note that this also disables the internal weak pull-up and pull-down resistors.\n    #[inline]\n    fn set_as_disconnected(&self) {\n        self.set_as_analog();\n    }\n\n    /// Get the pull-up configuration.\n    #[inline]\n    fn pull(&self) -> Pull {\n        critical_section::with(|_| get_pull(self.pin_port()))\n    }\n}\n\n/// GPIO pin number type.\n///\n/// Some chips have a total number of ports that exceeds 8, a larger integer\n/// is needed to hold the total pin number `(ports * number)`.\npub type PinNumber = u8;\n\n/// Pin that can be used to configure an [ExtiInput](crate::exti::ExtiInput). This trait is lost when converting to [AnyPin].\n#[cfg(feature = \"exti\")]\n#[allow(private_bounds)]\npub trait ExtiPin: PeripheralType + SealedPin {\n    /// EXTI channel assigned to this pin.\n    ///\n    /// For example, PC4 uses EXTI4.\n    type ExtiChannel: crate::exti::Channel;\n}\n\n/// GPIO pin trait.\n#[allow(private_bounds)]\npub trait Pin: PeripheralType + Into<AnyPin> + SealedPin + Sized + 'static {\n    /// Number of the pin within the port (0..31)\n    #[inline]\n    fn pin(&self) -> PinNumber {\n        self._pin()\n    }\n\n    /// Port of the pin\n    #[inline]\n    fn port(&self) -> PinNumber {\n        self._port()\n    }\n}\n\n/// Type-erased GPIO pin.\npub struct AnyPin {\n    pin_port: PinNumber,\n}\n\nimpl AnyPin {\n    /// Unsafely create an `AnyPin` from a pin+port number.\n    ///\n    /// `pin_port` is `port_num * 16 + pin_num`, where `port_num` is 0 for port `A`, 1 for port `B`, etc...\n    #[inline]\n    pub const unsafe fn steal(pin_port: PinNumber) -> Peri<'static, Self> {\n        Peri::new_unchecked(Self { pin_port })\n    }\n\n    #[inline]\n    const fn _port(&self) -> PinNumber {\n        self.pin_port / 16\n    }\n\n    /// Get the GPIO register block for this pin.\n    #[cfg(feature = \"unstable-pac\")]\n    #[inline]\n    pub const fn block(&self) -> gpio::Gpio {\n        crate::_generated::gpio_block(self._port() as _)\n    }\n}\n\nimpl core::fmt::Display for AnyPin {\n    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {\n        let port = char::from(b'A' + self.port());\n        let pin = self.pin();\n        write!(f, \"P{port}{pin}\")\n    }\n}\n#[cfg(feature = \"defmt\")]\nimpl defmt::Format for AnyPin {\n    fn format(&self, f: defmt::Formatter) {\n        let port = char::from(b'A' + self.port());\n        let pin = self.pin();\n        defmt::write!(f, \"P{}{}\", port, pin)\n    }\n}\n\nimpl_peripheral!(AnyPin);\nimpl Pin for AnyPin {}\nimpl SealedPin for AnyPin {\n    #[inline]\n    fn pin_port(&self) -> PinNumber {\n        self.pin_port\n    }\n}\n\n// ====================\n\nforeach_pin!(\n    ($pin_name:ident, $port_name:ident, $port_num:expr, $pin_num:expr, $exti_ch:ident) => {\n        impl Pin for peripherals::$pin_name {\n        }\n        #[cfg(feature = \"exti\")]\n        impl ExtiPin for peripherals::$pin_name {\n            type ExtiChannel = peripherals::$exti_ch;\n        }\n        impl SealedPin for peripherals::$pin_name {\n            #[inline]\n            fn pin_port(&self) -> PinNumber {\n                $port_num * 16 + $pin_num\n            }\n        }\n\n        impl From<peripherals::$pin_name> for AnyPin {\n            fn from(val: peripherals::$pin_name) -> Self {\n                Self {\n                    pin_port: val.pin_port(),\n                }\n            }\n        }\n    };\n);\n\npub(crate) unsafe fn init(_cs: CriticalSection) {\n    #[cfg(afio)]\n    crate::rcc::enable_and_reset_with_cs::<crate::peripherals::AFIO>(_cs);\n\n    crate::_generated::init_gpio();\n}\n\n#[cfg(stm32f1)]\n/// SWJ Config\n#[derive(Clone, Copy, Debug, Default)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub enum SwjCfg {\n    /// Full SWJ (JTAG-DP + SW-DP) (Reset state)\n    ///\n    /// PA13, PA14, PA15, PB3, and PB4 cannot be used\n    #[default]\n    SwdAndJtag = 0x0,\n    /// Full SWJ (JTAG-DP + SW-DP) but without NJTRST\n    ///\n    /// PA13, PA14, PA15, and PB3 cannot be used\n    ///\n    /// PB4 can be used\n    SwdAndJtagNoRst = 0x01,\n    /// JTAG-DP Disabled and SW-DP Enabled\n    ///\n    /// PA13 and  PA14 cannot be used\n    ///\n    /// PA15, PB3, and PB4 can be used\n    SwdOnly = 0x02,\n    /// JTAG-DP Disabled and SW-DP Disabled\n    ///\n    /// PA13, PA14, PA15, PB3, and PB4 can be used\n    Disabled = 0x04,\n}\n\n#[cfg(stm32f1)]\nimpl From<SwjCfg> for crate::pac::afio::vals::SwjCfg {\n    #[inline(always)]\n    fn from(value: SwjCfg) -> Self {\n        match value {\n            SwjCfg::SwdAndJtag => crate::pac::afio::vals::SwjCfg::RESET,\n            SwjCfg::SwdAndJtagNoRst => crate::pac::afio::vals::SwjCfg::NO_JNT_RST,\n            SwjCfg::SwdOnly => crate::pac::afio::vals::SwjCfg::JTAG_DISABLE,\n            SwjCfg::Disabled => crate::pac::afio::vals::SwjCfg::DISABLE,\n        }\n    }\n}\n\nimpl<'d> embedded_hal_02::digital::v2::InputPin for Input<'d> {\n    type Error = Infallible;\n\n    #[inline]\n    fn is_high(&self) -> Result<bool, Self::Error> {\n        Ok(self.is_high())\n    }\n\n    #[inline]\n    fn is_low(&self) -> Result<bool, Self::Error> {\n        Ok(self.is_low())\n    }\n}\n\nimpl<'d> embedded_hal_02::digital::v2::OutputPin for Output<'d> {\n    type Error = Infallible;\n\n    #[inline]\n    fn set_high(&mut self) -> Result<(), Self::Error> {\n        self.set_high();\n        Ok(())\n    }\n\n    #[inline]\n    fn set_low(&mut self) -> Result<(), Self::Error> {\n        self.set_low();\n        Ok(())\n    }\n}\n\nimpl<'d> embedded_hal_02::digital::v2::StatefulOutputPin for Output<'d> {\n    #[inline]\n    fn is_set_high(&self) -> Result<bool, Self::Error> {\n        Ok(self.is_set_high())\n    }\n\n    /// Is the output pin set as low?\n    #[inline]\n    fn is_set_low(&self) -> Result<bool, Self::Error> {\n        Ok(self.is_set_low())\n    }\n}\n\nimpl<'d> embedded_hal_02::digital::v2::ToggleableOutputPin for Output<'d> {\n    type Error = Infallible;\n    #[inline]\n    fn toggle(&mut self) -> Result<(), Self::Error> {\n        self.toggle();\n        Ok(())\n    }\n}\n\nimpl<'d> embedded_hal_02::digital::v2::InputPin for OutputOpenDrain<'d> {\n    type Error = Infallible;\n\n    fn is_high(&self) -> Result<bool, Self::Error> {\n        Ok(self.is_high())\n    }\n\n    fn is_low(&self) -> Result<bool, Self::Error> {\n        Ok(self.is_low())\n    }\n}\n\nimpl<'d> embedded_hal_02::digital::v2::OutputPin for OutputOpenDrain<'d> {\n    type Error = Infallible;\n\n    #[inline]\n    fn set_high(&mut self) -> Result<(), Self::Error> {\n        self.set_high();\n        Ok(())\n    }\n\n    #[inline]\n    fn set_low(&mut self) -> Result<(), Self::Error> {\n        self.set_low();\n        Ok(())\n    }\n}\n\nimpl<'d> embedded_hal_02::digital::v2::StatefulOutputPin for OutputOpenDrain<'d> {\n    #[inline]\n    fn is_set_high(&self) -> Result<bool, Self::Error> {\n        Ok(self.is_set_high())\n    }\n\n    /// Is the output pin set as low?\n    #[inline]\n    fn is_set_low(&self) -> Result<bool, Self::Error> {\n        Ok(self.is_set_low())\n    }\n}\n\nimpl<'d> embedded_hal_02::digital::v2::ToggleableOutputPin for OutputOpenDrain<'d> {\n    type Error = Infallible;\n    #[inline]\n    fn toggle(&mut self) -> Result<(), Self::Error> {\n        self.toggle();\n        Ok(())\n    }\n}\n\nimpl<'d> embedded_hal_02::digital::v2::InputPin for Flex<'d> {\n    type Error = Infallible;\n\n    #[inline]\n    fn is_high(&self) -> Result<bool, Self::Error> {\n        Ok(self.is_high())\n    }\n\n    #[inline]\n    fn is_low(&self) -> Result<bool, Self::Error> {\n        Ok(self.is_low())\n    }\n}\n\nimpl<'d> embedded_hal_02::digital::v2::OutputPin for Flex<'d> {\n    type Error = Infallible;\n\n    #[inline]\n    fn set_high(&mut self) -> Result<(), Self::Error> {\n        self.set_high();\n        Ok(())\n    }\n\n    #[inline]\n    fn set_low(&mut self) -> Result<(), Self::Error> {\n        self.set_low();\n        Ok(())\n    }\n}\n\nimpl<'d> embedded_hal_02::digital::v2::StatefulOutputPin for Flex<'d> {\n    #[inline]\n    fn is_set_high(&self) -> Result<bool, Self::Error> {\n        Ok(self.is_set_high())\n    }\n\n    /// Is the output pin set as low?\n    #[inline]\n    fn is_set_low(&self) -> Result<bool, Self::Error> {\n        Ok(self.is_set_low())\n    }\n}\n\nimpl<'d> embedded_hal_02::digital::v2::ToggleableOutputPin for Flex<'d> {\n    type Error = Infallible;\n    #[inline]\n    fn toggle(&mut self) -> Result<(), Self::Error> {\n        self.toggle();\n        Ok(())\n    }\n}\n\nimpl<'d> embedded_hal_1::digital::ErrorType for Input<'d> {\n    type Error = Infallible;\n}\n\nimpl<'d> embedded_hal_1::digital::InputPin for Input<'d> {\n    #[inline]\n    fn is_high(&mut self) -> Result<bool, Self::Error> {\n        Ok((*self).is_high())\n    }\n\n    #[inline]\n    fn is_low(&mut self) -> Result<bool, Self::Error> {\n        Ok((*self).is_low())\n    }\n}\n\nimpl<'d> embedded_hal_1::digital::ErrorType for Output<'d> {\n    type Error = Infallible;\n}\n\nimpl<'d> embedded_hal_1::digital::OutputPin for Output<'d> {\n    #[inline]\n    fn set_high(&mut self) -> Result<(), Self::Error> {\n        Ok(self.set_high())\n    }\n\n    #[inline]\n    fn set_low(&mut self) -> Result<(), Self::Error> {\n        Ok(self.set_low())\n    }\n}\n\nimpl<'d> embedded_hal_1::digital::StatefulOutputPin for Output<'d> {\n    #[inline]\n    fn is_set_high(&mut self) -> Result<bool, Self::Error> {\n        Ok((*self).is_set_high())\n    }\n\n    /// Is the output pin set as low?\n    #[inline]\n    fn is_set_low(&mut self) -> Result<bool, Self::Error> {\n        Ok((*self).is_set_low())\n    }\n}\n\nimpl<'d> embedded_hal_1::digital::ErrorType for OutputOpenDrain<'d> {\n    type Error = Infallible;\n}\n\nimpl<'d> embedded_hal_1::digital::InputPin for OutputOpenDrain<'d> {\n    #[inline]\n    fn is_high(&mut self) -> Result<bool, Self::Error> {\n        Ok((*self).is_high())\n    }\n\n    #[inline]\n    fn is_low(&mut self) -> Result<bool, Self::Error> {\n        Ok((*self).is_low())\n    }\n}\n\nimpl<'d> embedded_hal_1::digital::OutputPin for OutputOpenDrain<'d> {\n    #[inline]\n    fn set_high(&mut self) -> Result<(), Self::Error> {\n        Ok(self.set_high())\n    }\n\n    #[inline]\n    fn set_low(&mut self) -> Result<(), Self::Error> {\n        Ok(self.set_low())\n    }\n}\n\nimpl<'d> embedded_hal_1::digital::StatefulOutputPin for OutputOpenDrain<'d> {\n    #[inline]\n    fn is_set_high(&mut self) -> Result<bool, Self::Error> {\n        Ok((*self).is_set_high())\n    }\n\n    /// Is the output pin set as low?\n    #[inline]\n    fn is_set_low(&mut self) -> Result<bool, Self::Error> {\n        Ok((*self).is_set_low())\n    }\n}\n\nimpl<'d> embedded_hal_1::digital::InputPin for Flex<'d> {\n    #[inline]\n    fn is_high(&mut self) -> Result<bool, Self::Error> {\n        Ok((*self).is_high())\n    }\n\n    #[inline]\n    fn is_low(&mut self) -> Result<bool, Self::Error> {\n        Ok((*self).is_low())\n    }\n}\n\nimpl<'d> embedded_hal_1::digital::OutputPin for Flex<'d> {\n    #[inline]\n    fn set_high(&mut self) -> Result<(), Self::Error> {\n        Ok(self.set_high())\n    }\n\n    #[inline]\n    fn set_low(&mut self) -> Result<(), Self::Error> {\n        Ok(self.set_low())\n    }\n}\n\nimpl<'d> embedded_hal_1::digital::ErrorType for Flex<'d> {\n    type Error = Infallible;\n}\n\nimpl<'d> embedded_hal_1::digital::StatefulOutputPin for Flex<'d> {\n    #[inline]\n    fn is_set_high(&mut self) -> Result<bool, Self::Error> {\n        Ok((*self).is_set_high())\n    }\n\n    /// Is the output pin set as low?\n    #[inline]\n    fn is_set_low(&mut self) -> Result<bool, Self::Error> {\n        Ok((*self).is_set_low())\n    }\n}\n"
  },
  {
    "path": "embassy-stm32/src/hash/mod.rs",
    "content": "//! Hash generator (HASH)\nuse core::cmp::min;\n#[cfg(hash_v2)]\nuse core::future::poll_fn;\nuse core::marker::PhantomData;\n#[cfg(hash_v2)]\nuse core::ptr;\n#[cfg(hash_v2)]\nuse core::task::Poll;\n\nuse embassy_hal_internal::PeripheralType;\nuse embassy_sync::waitqueue::AtomicWaker;\nuse stm32_metapac::hash::regs::*;\n\n#[cfg(hash_v2)]\nuse crate::dma::ChannelAndRequest;\nuse crate::interrupt::typelevel::Interrupt;\n#[cfg(hash_v2)]\nuse crate::mode::Async;\nuse crate::mode::{Blocking, Mode};\nuse crate::peripherals::HASH;\nuse crate::{Peri, interrupt, pac, peripherals, rcc};\n\n#[cfg(hash_v1)]\nconst NUM_CONTEXT_REGS: usize = 51;\n#[cfg(hash_v3)]\nconst NUM_CONTEXT_REGS: usize = 103;\n#[cfg(any(hash_v2, hash_v4))]\nconst NUM_CONTEXT_REGS: usize = 54;\n\nconst HASH_BUFFER_LEN: usize = 132;\nconst DIGEST_BLOCK_SIZE: usize = 128;\n\nstatic HASH_WAKER: AtomicWaker = AtomicWaker::new();\n\n/// HASH interrupt handler.\npub struct InterruptHandler<T: Instance> {\n    _phantom: PhantomData<T>,\n}\n\nimpl<T: Instance> interrupt::typelevel::Handler<T::Interrupt> for InterruptHandler<T> {\n    unsafe fn on_interrupt() {\n        let bits = T::regs().sr().read();\n        if bits.dinis() {\n            T::regs().imr().modify(|reg| reg.set_dinie(false));\n            HASH_WAKER.wake();\n        }\n        if bits.dcis() {\n            T::regs().imr().modify(|reg| reg.set_dcie(false));\n            HASH_WAKER.wake();\n        }\n    }\n}\n\n///Hash algorithm selection\n#[derive(Clone, Copy, PartialEq)]\npub enum Algorithm {\n    /// SHA-1 Algorithm\n    SHA1 = 0,\n\n    #[cfg(any(hash_v1, hash_v2, hash_v4))]\n    /// MD5 Algorithm\n    MD5 = 1,\n\n    /// SHA-224 Algorithm\n    SHA224 = 2,\n\n    /// SHA-256 Algorithm\n    SHA256 = 3,\n\n    #[cfg(hash_v3)]\n    /// SHA-384 Algorithm\n    SHA384 = 12,\n\n    #[cfg(hash_v3)]\n    /// SHA-512/224 Algorithm\n    SHA512_224 = 13,\n\n    #[cfg(hash_v3)]\n    /// SHA-512/256 Algorithm\n    SHA512_256 = 14,\n\n    #[cfg(hash_v3)]\n    /// SHA-256 Algorithm\n    SHA512 = 15,\n}\n\n/// Input data width selection\n#[repr(u8)]\n#[derive(Clone, Copy)]\npub enum DataType {\n    ///32-bit data, no data is swapped.\n    Width32 = 0,\n    ///16-bit data, each half-word is swapped.\n    Width16 = 1,\n    ///8-bit data, all bytes are swapped.\n    Width8 = 2,\n    ///1-bit data, all bits are swapped.\n    Width1 = 3,\n}\n\n/// Stores the state of the HASH peripheral for suspending/resuming\n/// digest calculation.\n#[derive(Clone)]\npub struct Context<'c> {\n    first_word_sent: bool,\n    key_sent: bool,\n    buffer: [u8; HASH_BUFFER_LEN],\n    buflen: usize,\n    algo: Algorithm,\n    format: DataType,\n    imr: u32,\n    str: u32,\n    cr: u32,\n    csr: [u32; NUM_CONTEXT_REGS],\n    key: HmacKey<'c>,\n}\n\ntype HmacKey<'k> = Option<&'k [u8]>;\n\n/// HASH driver.\npub struct Hash<'d, T: Instance, M: Mode> {\n    _peripheral: Peri<'d, T>,\n    _phantom: PhantomData<M>,\n    #[cfg(hash_v2)]\n    dma: Option<ChannelAndRequest<'d>>,\n}\n\nimpl<'d, T: Instance> Hash<'d, T, Blocking> {\n    /// Instantiates, resets, and enables the HASH peripheral.\n    pub fn new_blocking(\n        peripheral: Peri<'d, T>,\n        _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd,\n    ) -> Self {\n        rcc::enable_and_reset::<HASH>();\n        let instance = Self {\n            _peripheral: peripheral,\n            _phantom: PhantomData,\n            #[cfg(hash_v2)]\n            dma: None,\n        };\n\n        T::Interrupt::unpend();\n        unsafe { T::Interrupt::enable() };\n\n        instance\n    }\n}\n\nimpl<'d, T: Instance, M: Mode> Hash<'d, T, M> {\n    /// Starts computation of a new hash and returns the saved peripheral state.\n    pub fn start<'c>(&mut self, algorithm: Algorithm, format: DataType, key: HmacKey<'c>) -> Context<'c> {\n        // Define a context for this new computation.\n        let mut ctx = Context {\n            first_word_sent: false,\n            key_sent: false,\n            buffer: [0; HASH_BUFFER_LEN],\n            buflen: 0,\n            algo: algorithm,\n            format: format,\n            imr: 0,\n            str: 0,\n            cr: 0,\n            csr: [0; NUM_CONTEXT_REGS],\n            key,\n        };\n\n        // Set the data type in the peripheral.\n        T::regs().cr().modify(|w| w.set_datatype(ctx.format as u8));\n\n        // Select the algorithm.\n        #[cfg(hash_v1)]\n        if ctx.algo == Algorithm::MD5 {\n            T::regs().cr().modify(|w| w.set_algo(true));\n        }\n\n        #[cfg(hash_v2)]\n        {\n            // Select the algorithm.\n            let mut algo0 = false;\n            let mut algo1 = false;\n            if ctx.algo == Algorithm::MD5 || ctx.algo == Algorithm::SHA256 {\n                algo0 = true;\n            }\n            if ctx.algo == Algorithm::SHA224 || ctx.algo == Algorithm::SHA256 {\n                algo1 = true;\n            }\n            T::regs().cr().modify(|w| w.set_algo0(algo0));\n            T::regs().cr().modify(|w| w.set_algo1(algo1));\n        }\n\n        #[cfg(any(hash_v3, hash_v4))]\n        T::regs().cr().modify(|w| w.set_algo(ctx.algo as u8));\n\n        // Configure HMAC mode if a key is provided.\n        if let Some(key) = ctx.key {\n            T::regs().cr().modify(|w| w.set_mode(true));\n            if key.len() > 64 {\n                T::regs().cr().modify(|w| w.set_lkey(true));\n            }\n        } else {\n            T::regs().cr().modify(|w| w.set_mode(false));\n        }\n\n        T::regs().cr().modify(|w| w.set_init(true));\n\n        // Store and return the state of the peripheral.\n        self.store_context(&mut ctx);\n        ctx\n    }\n\n    /// Restores the peripheral state using the given context,\n    /// then updates the state with the provided data.\n    /// Peripheral state is saved upon return.\n    pub fn update_blocking<'c>(&mut self, ctx: &mut Context<'c>, input: &[u8]) {\n        // Restore the peripheral state.\n        self.load_context(&ctx);\n\n        // Load the HMAC key if provided.\n        if !ctx.key_sent {\n            if let Some(key) = ctx.key {\n                self.accumulate_blocking(key);\n                T::regs().str().write(|w| w.set_dcal(true));\n                // Block waiting for digest.\n                while !T::regs().sr().read().dinis() {}\n            }\n            ctx.key_sent = true;\n        }\n\n        let mut data_waiting = input.len() + ctx.buflen;\n        if data_waiting < DIGEST_BLOCK_SIZE || (data_waiting < ctx.buffer.len() && !ctx.first_word_sent) {\n            // There isn't enough data to digest a block, so append it to the buffer.\n            ctx.buffer[ctx.buflen..ctx.buflen + input.len()].copy_from_slice(input);\n            ctx.buflen += input.len();\n            self.store_context(ctx);\n            return;\n        }\n\n        let mut ilen_remaining = input.len();\n        let mut input_start = 0;\n\n        // Handle first block.\n        if !ctx.first_word_sent {\n            let empty_len = ctx.buffer.len() - ctx.buflen;\n            let copy_len = min(empty_len, ilen_remaining);\n            // Fill the buffer.\n            if copy_len > 0 {\n                ctx.buffer[ctx.buflen..ctx.buflen + copy_len].copy_from_slice(&input[0..copy_len]);\n                ctx.buflen += copy_len;\n                ilen_remaining -= copy_len;\n                input_start += copy_len;\n            }\n            self.accumulate_blocking(ctx.buffer.as_slice());\n            data_waiting -= ctx.buflen;\n            ctx.buflen = 0;\n            ctx.first_word_sent = true;\n        }\n\n        if data_waiting < DIGEST_BLOCK_SIZE {\n            // There isn't enough data remaining to process another block, so store it.\n            ctx.buffer[0..ilen_remaining].copy_from_slice(&input[input_start..input_start + ilen_remaining]);\n            ctx.buflen += ilen_remaining;\n        } else {\n            // First ingest the data in the buffer.\n            let empty_len = DIGEST_BLOCK_SIZE - ctx.buflen;\n            if empty_len > 0 {\n                let copy_len = min(empty_len, ilen_remaining);\n                ctx.buffer[ctx.buflen..ctx.buflen + copy_len]\n                    .copy_from_slice(&input[input_start..input_start + copy_len]);\n                ctx.buflen += copy_len;\n                ilen_remaining -= copy_len;\n                input_start += copy_len;\n            }\n            self.accumulate_blocking(&ctx.buffer[0..DIGEST_BLOCK_SIZE]);\n            ctx.buflen = 0;\n\n            // Move any extra data to the now-empty buffer.\n            let leftovers = ilen_remaining % 64;\n            if leftovers > 0 {\n                ctx.buffer[0..leftovers].copy_from_slice(&input[input.len() - leftovers..input.len()]);\n                ctx.buflen += leftovers;\n                ilen_remaining -= leftovers;\n            }\n\n            // Hash the remaining data.\n            self.accumulate_blocking(&input[input_start..input_start + ilen_remaining]);\n        }\n\n        // Save the peripheral context.\n        self.store_context(ctx);\n    }\n\n    /// Computes a digest for the given context.\n    /// The digest buffer must be large enough to accomodate a digest for the selected algorithm.\n    /// The largest returned digest size is 128 bytes for SHA-512.\n    /// Panics if the supplied digest buffer is too short.\n    pub fn finish_blocking<'c>(&mut self, mut ctx: Context<'c>, digest: &mut [u8]) -> usize {\n        // Restore the peripheral state.\n        self.load_context(&ctx);\n\n        // Hash the leftover bytes, if any.\n        self.accumulate_blocking(&ctx.buffer[0..ctx.buflen]);\n        ctx.buflen = 0;\n\n        //Start the digest calculation.\n        T::regs().str().write(|w| w.set_dcal(true));\n\n        // Load the HMAC key if provided.\n        if let Some(key) = ctx.key {\n            while !T::regs().sr().read().dinis() {}\n            self.accumulate_blocking(key);\n            T::regs().str().write(|w| w.set_dcal(true));\n        }\n\n        // Block until digest computation is complete.\n        while !T::regs().sr().read().dcis() {}\n\n        // Return the digest.\n        let digest_words = match ctx.algo {\n            Algorithm::SHA1 => 5,\n            #[cfg(any(hash_v1, hash_v2, hash_v4))]\n            Algorithm::MD5 => 4,\n            Algorithm::SHA224 => 7,\n            Algorithm::SHA256 => 8,\n            #[cfg(hash_v3)]\n            Algorithm::SHA384 => 12,\n            #[cfg(hash_v3)]\n            Algorithm::SHA512_224 => 7,\n            #[cfg(hash_v3)]\n            Algorithm::SHA512_256 => 8,\n            #[cfg(hash_v3)]\n            Algorithm::SHA512 => 16,\n        };\n\n        let digest_len_bytes = digest_words * 4;\n        // Panics if the supplied digest buffer is too short.\n        if digest.len() < digest_len_bytes {\n            panic!(\"Digest buffer must be at least {} bytes long.\", digest_words * 4);\n        }\n\n        let mut i = 0;\n        while i < digest_words {\n            let word = T::regs().hr(i).read();\n            digest[(i * 4)..((i * 4) + 4)].copy_from_slice(word.to_be_bytes().as_slice());\n            i += 1;\n        }\n        digest_len_bytes\n    }\n\n    /// Push data into the hash core.\n    fn accumulate_blocking(&mut self, input: &[u8]) {\n        // Set the number of valid bits.\n        let num_valid_bits: u8 = (8 * (input.len() % 4)) as u8;\n        T::regs().str().modify(|w| w.set_nblw(num_valid_bits));\n\n        let mut chunks = input.chunks_exact(4);\n        for chunk in &mut chunks {\n            T::regs()\n                .din()\n                .write_value(u32::from_ne_bytes(chunk.try_into().unwrap()));\n        }\n        let rem = chunks.remainder();\n        if !rem.is_empty() {\n            let mut word: [u8; 4] = [0; 4];\n            word[0..rem.len()].copy_from_slice(rem);\n            T::regs().din().write_value(u32::from_ne_bytes(word));\n        }\n    }\n\n    /// Save the peripheral state to a context.\n    fn store_context<'c>(&mut self, ctx: &mut Context<'c>) {\n        // Block waiting for data in ready.\n        while !T::regs().sr().read().dinis() {}\n\n        // Store peripheral context.\n        ctx.imr = T::regs().imr().read().0;\n        ctx.str = T::regs().str().read().0;\n        ctx.cr = T::regs().cr().read().0;\n        let mut i = 0;\n        while i < NUM_CONTEXT_REGS {\n            ctx.csr[i] = T::regs().csr(i).read();\n            i += 1;\n        }\n    }\n\n    /// Restore the peripheral state from a context.\n    fn load_context(&mut self, ctx: &Context) {\n        // Restore the peripheral state from the context.\n        T::regs().imr().write_value(Imr { 0: ctx.imr });\n        T::regs().str().write_value(Str { 0: ctx.str });\n        T::regs().cr().write_value(Cr { 0: ctx.cr });\n        T::regs().cr().modify(|w| w.set_init(true));\n        let mut i = 0;\n        while i < NUM_CONTEXT_REGS {\n            T::regs().csr(i).write_value(ctx.csr[i]);\n            i += 1;\n        }\n    }\n}\n\n#[cfg(hash_v2)]\nimpl<'d, T: Instance> Hash<'d, T, Async> {\n    /// Instantiates, resets, and enables the HASH peripheral.\n    pub fn new<D: Dma<T>>(\n        peripheral: Peri<'d, T>,\n        dma: Peri<'d, D>,\n        _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>>\n        + interrupt::typelevel::Binding<D::Interrupt, crate::dma::InterruptHandler<D>>\n        + 'd,\n    ) -> Self {\n        rcc::enable_and_reset::<HASH>();\n        let instance = Self {\n            _peripheral: peripheral,\n            _phantom: PhantomData,\n            dma: new_dma!(dma, _irq),\n        };\n\n        T::Interrupt::unpend();\n        unsafe { T::Interrupt::enable() };\n\n        instance\n    }\n\n    /// Restores the peripheral state using the given context,\n    /// then updates the state with the provided data.\n    /// Peripheral state is saved upon return.\n    pub async fn update(&mut self, ctx: &mut Context<'_>, input: &[u8]) {\n        // Restore the peripheral state.\n        self.load_context(&ctx);\n\n        // Load the HMAC key if provided.\n        if !ctx.key_sent {\n            if let Some(key) = ctx.key {\n                self.accumulate(key).await;\n            }\n            ctx.key_sent = true;\n        }\n\n        let data_waiting = input.len() + ctx.buflen;\n        if data_waiting < DIGEST_BLOCK_SIZE {\n            // There isn't enough data to digest a block, so append it to the buffer.\n            ctx.buffer[ctx.buflen..ctx.buflen + input.len()].copy_from_slice(input);\n            ctx.buflen += input.len();\n            self.store_context(ctx);\n            return;\n        }\n\n        // Enable multiple DMA transfers.\n        T::regs().cr().modify(|w| w.set_mdmat(true));\n\n        let mut ilen_remaining = input.len();\n        let mut input_start = 0;\n\n        // First ingest the data in the buffer.\n        let empty_len = DIGEST_BLOCK_SIZE - ctx.buflen;\n        if empty_len > 0 {\n            let copy_len = min(empty_len, ilen_remaining);\n            ctx.buffer[ctx.buflen..ctx.buflen + copy_len].copy_from_slice(&input[input_start..input_start + copy_len]);\n            ctx.buflen += copy_len;\n            ilen_remaining -= copy_len;\n            input_start += copy_len;\n        }\n        self.accumulate(&ctx.buffer[..DIGEST_BLOCK_SIZE]).await;\n        ctx.buflen = 0;\n\n        // Move any extra data to the now-empty buffer.\n        let leftovers = ilen_remaining % DIGEST_BLOCK_SIZE;\n        if leftovers > 0 {\n            assert!(ilen_remaining >= leftovers);\n            ctx.buffer[0..leftovers].copy_from_slice(&input[input.len() - leftovers..input.len()]);\n            ctx.buflen += leftovers;\n            ilen_remaining -= leftovers;\n        } else {\n            ctx.buffer\n                .copy_from_slice(&input[input.len() - DIGEST_BLOCK_SIZE..input.len()]);\n            ctx.buflen += DIGEST_BLOCK_SIZE;\n            ilen_remaining -= DIGEST_BLOCK_SIZE;\n        }\n\n        // Hash the remaining data.\n        self.accumulate(&input[input_start..input_start + ilen_remaining]).await;\n\n        // Save the peripheral context.\n        self.store_context(ctx);\n    }\n\n    /// Computes a digest for the given context.\n    /// The digest buffer must be large enough to accomodate a digest for the selected algorithm.\n    /// The largest returned digest size is 128 bytes for SHA-512.\n    /// Panics if the supplied digest buffer is too short.\n    pub async fn finish<'c>(&mut self, mut ctx: Context<'c>, digest: &mut [u8]) -> usize {\n        // Restore the peripheral state.\n        self.load_context(&ctx);\n\n        // Must be cleared prior to the last DMA transfer.\n        T::regs().cr().modify(|w| w.set_mdmat(false));\n\n        // Hash the leftover bytes, if any.\n        self.accumulate(&ctx.buffer[0..ctx.buflen]).await;\n        ctx.buflen = 0;\n\n        // Load the HMAC key if provided.\n        if let Some(key) = ctx.key {\n            self.accumulate(key).await;\n        }\n\n        // Wait for completion.\n        poll_fn(|cx| {\n            // Check if already done.\n            let bits = T::regs().sr().read();\n            if bits.dcis() {\n                return Poll::Ready(());\n            }\n            // Register waker, then enable interrupts.\n            HASH_WAKER.register(cx.waker());\n            T::regs().imr().modify(|reg| reg.set_dcie(true));\n            // Check for completion.\n            let bits = T::regs().sr().read();\n            if bits.dcis() { Poll::Ready(()) } else { Poll::Pending }\n        })\n        .await;\n\n        // Return the digest.\n        let digest_words = match ctx.algo {\n            Algorithm::SHA1 => 5,\n            #[cfg(any(hash_v1, hash_v2, hash_v4))]\n            Algorithm::MD5 => 4,\n            Algorithm::SHA224 => 7,\n            Algorithm::SHA256 => 8,\n            #[cfg(hash_v3)]\n            Algorithm::SHA384 => 12,\n            #[cfg(hash_v3)]\n            Algorithm::SHA512_224 => 7,\n            #[cfg(hash_v3)]\n            Algorithm::SHA512_256 => 8,\n            #[cfg(hash_v3)]\n            Algorithm::SHA512 => 16,\n        };\n\n        let digest_len_bytes = digest_words * 4;\n        // Panics if the supplied digest buffer is too short.\n        if digest.len() < digest_len_bytes {\n            panic!(\"Digest buffer must be at least {} bytes long.\", digest_words * 4);\n        }\n\n        let mut i = 0;\n        while i < digest_words {\n            let word = T::regs().hr(i).read();\n            digest[(i * 4)..((i * 4) + 4)].copy_from_slice(word.to_be_bytes().as_slice());\n            i += 1;\n        }\n        digest_len_bytes\n    }\n\n    /// Push data into the hash core.\n    async fn accumulate(&mut self, input: &[u8]) {\n        // Ignore an input length of 0.\n        if input.len() == 0 {\n            return;\n        }\n\n        // Set the number of valid bits.\n        let num_valid_bits: u8 = (8 * (input.len() % 4)) as u8;\n        T::regs().str().modify(|w| w.set_nblw(num_valid_bits));\n\n        // Configure DMA to transfer input to hash core.\n        let dst_ptr: *mut u32 = T::regs().din().as_ptr();\n        let mut num_words = input.len() / 4;\n        if input.len() % 4 > 0 {\n            num_words += 1;\n        }\n        let src_ptr: *const [u8] = ptr::slice_from_raw_parts(input.as_ptr().cast(), num_words);\n\n        let dma = self.dma.as_mut().unwrap();\n        let dma_transfer = unsafe { dma.write_raw(src_ptr, dst_ptr as *mut u32, Default::default()) };\n        T::regs().cr().modify(|w| w.set_dmae(true));\n\n        // Wait for the transfer to complete.\n        dma_transfer.await;\n    }\n}\n\ntrait SealedInstance {\n    fn regs() -> pac::hash::Hash;\n}\n\n/// HASH instance trait.\n#[allow(private_bounds)]\npub trait Instance: SealedInstance + PeripheralType + crate::rcc::RccPeripheral + 'static + Send {\n    /// Interrupt for this HASH instance.\n    type Interrupt: interrupt::typelevel::Interrupt;\n}\n\nforeach_interrupt!(\n    ($inst:ident, hash, HASH, GLOBAL, $irq:ident) => {\n        impl Instance for peripherals::$inst {\n            type Interrupt = crate::interrupt::typelevel::$irq;\n        }\n\n        impl SealedInstance for peripherals::$inst {\n            fn regs() -> crate::pac::hash::Hash {\n                crate::pac::$inst\n            }\n        }\n    };\n);\n\ndma_trait!(Dma, Instance);\n"
  },
  {
    "path": "embassy-stm32/src/hrtim/bridge_converter.rs",
    "content": "//! Fixed-frequency bridge converter driver.\n//!\n//! Our implementation of the bridge converter uses a single channel and three compare registers,\n//! allowing implementation of a synchronous buck or boost converter in continuous or discontinuous\n//! conduction mode.\n//!\n//! It is important to remember that in synchronous topologies, energy can flow in reverse during\n//! light loading conditions, and that the low-side switch must be active for a short time to drive\n//! a bootstrapped high-side switch.\nuse stm32_hrtim::compare_register::HrCompareRegister;\nuse stm32_hrtim::control::{HrPwmControl, HrPwmCtrl};\nuse stm32_hrtim::output::{HrOutput, Output1Pin, Output2Pin};\nuse stm32_hrtim::timer::{HrTim, HrTimer};\nuse stm32_hrtim::{HrParts, HrPwmAdvExt, HrPwmBuilder, HrtimPrescaler, PreloadSource, capture};\n\nuse crate::hrtim::HrPwmBuilderExt;\nuse crate::peripherals::HRTIM1;\nuse crate::rcc::SealedRccPeripheral;\nuse crate::time::Hertz;\n\n/// Fixed-frequency bridge converter driver.\npub struct BridgeConverter<TIM, PSCL> {\n    timer: HrParts<TIM, PSCL>,\n    dead_time: u16,\n    primary_duty: u16,\n    min_secondary_duty: u16,\n    max_secondary_duty: u16,\n}\n\nimpl<TIM: HrPwmAdvExt, PSCL: HrtimPrescaler> BridgeConverter<TIM, PSCL>\nwhere\n    TIM: stm32_hrtim::timer::InstanceX + HrPwmAdvExt<PreloadSource = PreloadSource>,\n    HrTim<TIM, PSCL, capture::NoDma, capture::NoDma>: HrTimer,\n{\n    /// Create a new HRTIM bridge converter driver.\n    pub fn new<P1, P2>(\n        timer: TIM,\n        pin1: P1,\n        pin2: P2,\n        frequency: Hertz,\n        prescaler: PSCL,\n        hr_control: &mut HrPwmControl,\n    ) -> Self\n    where\n        P1: Output1Pin<TIM>,\n        P2: Output2Pin<TIM>,\n        HrPwmBuilder<TIM, PSCL, PreloadSource, P1, P2>: HrPwmBuilderExt<TIM, PSCL, P1, P2>,\n    {\n        let f = frequency.0;\n\n        let timer_f = HRTIM1::frequency().0;\n\n        let psc_min = (timer_f / f) / (u16::MAX as u32 / 32);\n        let psc = PSCL::VALUE as u32;\n        assert!(\n            psc >= psc_min,\n            \"Prescaler set too low to be able to reach target frequency\"\n        );\n\n        let timer_f = 32 * (timer_f / psc);\n        let per: u16 = (timer_f / f) as u16;\n\n        let mut timer = timer\n            .pwm_advanced(pin1, pin2)\n            .prescaler(prescaler)\n            .period(per)\n            .preload(PreloadSource::OnRepetitionUpdate)\n            .finalize(hr_control);\n\n        // The dead-time generation unit cannot be used because it forces the other output\n        // to be completely complementary to the first output, which restricts certain waveforms\n        // Therefore, software-implemented dead time must be used when setting the duty cycles\n\n        // Set output 1 to active on a period event\n        timer.out1.enable_set_event(&timer.timer);\n\n        // Set output 1 to inactive on a compare 1 event\n        timer.out1.enable_rst_event(&timer.cr1);\n\n        // Set output 2 to active on a compare 2 event\n        timer.out2.enable_set_event(&timer.cr2);\n\n        // Set output 2 to inactive on a compare 3 event\n        timer.out2.enable_rst_event(&timer.cr3);\n\n        Self {\n            timer,\n            dead_time: 0,\n            primary_duty: 0,\n            min_secondary_duty: 0,\n            max_secondary_duty: 0,\n        }\n    }\n\n    /// Start HRTIM.\n    pub fn start(&mut self, hr_control: &mut HrPwmCtrl) {\n        self.timer.timer.start(hr_control);\n    }\n\n    /// Stop HRTIM.\n    pub fn stop(&mut self, hr_control: &mut HrPwmCtrl) {\n        self.timer.timer.stop(hr_control);\n    }\n\n    /*\n    /// Enable burst mode.\n    pub fn enable_burst_mode(&mut self) {\n        T::regs().tim(C::raw()).outr().modify(|w| {\n            // Enable Burst Mode\n            w.set_idlem(0, true);\n            w.set_idlem(1, true);\n\n            // Set output to active during the burst\n            w.set_idles(0, true);\n            w.set_idles(1, true);\n        })\n    }\n\n    /// Disable burst mode.\n    pub fn disable_burst_mode(&mut self) {\n        T::regs().tim(C::raw()).outr().modify(|w| {\n            // Disable Burst Mode\n            w.set_idlem(0, false);\n            w.set_idlem(1, false);\n        })\n    }*/\n\n    fn update_primary_duty_or_dead_time(&mut self) {\n        self.min_secondary_duty = self.primary_duty + self.dead_time;\n        self.timer.cr1.set_duty(self.primary_duty);\n        self.timer.cr2.set_duty(self.min_secondary_duty);\n    }\n\n    /// Set the dead time as a proportion of the maximum compare value\n    pub fn set_dead_time(&mut self, dead_time: u16) {\n        self.dead_time = dead_time;\n        self.max_secondary_duty = self.get_max_compare_value() - dead_time;\n        self.update_primary_duty_or_dead_time();\n    }\n\n    /// Get the maximum compare value of a duty cycle\n    pub fn get_max_compare_value(&mut self) -> u16 {\n        self.timer.timer.get_period()\n    }\n\n    /// The primary duty is the period in which the primary switch is active\n    ///\n    /// In the case of a buck converter, this is the high-side switch\n    /// In the case of a boost converter, this is the low-side switch\n    pub fn set_primary_duty(&mut self, primary_duty: u16) {\n        self.primary_duty = primary_duty;\n        self.update_primary_duty_or_dead_time();\n    }\n\n    /// The secondary duty is the period in any switch is active\n    ///\n    /// If less than or equal to the primary duty, the secondary switch will be active for one tick\n    /// If a fully complementary output is desired, the secondary duty can be set to the max compare\n    pub fn set_secondary_duty(&mut self, secondary_duty: u16) {\n        let secondary_duty = if secondary_duty > self.max_secondary_duty {\n            self.max_secondary_duty\n        } else if secondary_duty <= self.min_secondary_duty {\n            self.min_secondary_duty + 1\n        } else {\n            secondary_duty\n        };\n\n        self.timer.cr3.set_duty(secondary_duty);\n    }\n}\n"
  },
  {
    "path": "embassy-stm32/src/hrtim/mod.rs",
    "content": "//! High Resolution Timer (HRTIM)\n\nmod traits;\n\npub mod bridge_converter;\npub mod resonant_converter;\n\nuse core::mem::MaybeUninit;\n\nuse embassy_hal_internal::Peri;\n#[allow(unused_imports)] // HrPwmAdvExt::pwm_advanced used by doc\nuse stm32_hrtim::HrPwmAdvExt;\nuse stm32_hrtim::control::{HrPwmControl, HrTimOngoingCalibration};\nuse stm32_hrtim::output::{Output1Pin, Output2Pin};\n#[cfg(hrtim_v2)]\nuse stm32_hrtim::pac::HRTIM_TIMF;\nuse stm32_hrtim::pac::{HRTIM_MASTER, HRTIM_TIMA, HRTIM_TIMB, HRTIM_TIMC, HRTIM_TIMD, HRTIM_TIME};\npub use stm32_hrtim::{self, Pscl1, Pscl2, Pscl4, Pscl8, Pscl16, Pscl32, Pscl64, Pscl128, PsclDefault};\nuse stm32_hrtim::{DacResetTrigger, DacStepTrigger, HrParts, HrPwmBuilder};\nuse traits::Instance;\n\nuse crate::gpio::{AfType, OutputType, Speed};\nuse crate::peripherals::HRTIM1;\nuse crate::rcc;\n\n/// Uninitialized HRTIM resources as returned by [HrControltExt::hr_control]\npub struct Parts {\n    /// Control resources common for all of the HRTIM instances timers\n    ///\n    /// This needs to be initialized and calibrated by calling [HrTimOngoingCalibration::wait_for_calibration]\n    pub control: HrTimOngoingCalibration,\n\n    /// Uninitialized MASTER, call [HRTIM_MASTER::pwm_advanced] to set it up\n    pub master: HRTIM_MASTER,\n\n    /// Uninitialized TIMA, call [HRTIM_TIMA::pwm_advanced] to set it up\n    pub tima: HRTIM_TIMA,\n\n    /// Uninitialized TIMB, call [HRTIM_TIMB::pwm_advanced] to set it up\n    pub timb: HRTIM_TIMB,\n\n    /// Uninitialized TIMC, call [HRTIM_TIMC::pwm_advanced] to set it up\n    pub timc: HRTIM_TIMC,\n\n    /// Uninitialized TIMD, call [HRTIM_TIMD::pwm_advanced] to set it up\n    pub timd: HRTIM_TIMD,\n\n    /// Uninitialized TIME, call [HRTIM_TIME::pwm_advanced] to set it up\n    pub time: HRTIM_TIME,\n\n    /// Uninitialized TIMF, call [HRTIM_TIMF::pwm_advanced] to set it up\n    #[cfg(hrtim_v2)]\n    pub timf: HRTIM_TIMF,\n}\n\n/// Implemented for HRTIM peripheral block\npub trait HrControltExt {\n    /// Setup HRTIM peripheral block\n    fn hr_control(self) -> Parts;\n}\n\nimpl<T: super::hrtim::Instance> HrControltExt for T {\n    fn hr_control(self) -> Parts {\n        rcc::enable_and_reset::<T>();\n\n        // TODO: Verify that the HRTIM gets a clock of the correct speed as input\n        // * 100-170MHz for g4\n        // SAFETY:\n        // * hr_control - We have enabled the rcc\n        // * steal - We only steal these once\n        unsafe {\n            Parts {\n                control: HrTimOngoingCalibration::hr_control(),\n                master: HRTIM_MASTER::steal(),\n                tima: HRTIM_TIMA::steal(),\n                timb: HRTIM_TIMB::steal(),\n                timc: HRTIM_TIMC::steal(),\n                timd: HRTIM_TIMD::steal(),\n                time: HRTIM_TIME::steal(),\n                #[cfg(hrtim_v2)]\n                timf: HRTIM_TIMF::steal(),\n            }\n        }\n    }\n}\n\n/// Extension trait for initializing the HRTIM timer instance\npub trait HrPwmBuilderExt<TIM, PSCL, P1: Output1Pin<TIM>, P2: Output2Pin<TIM>> {\n    /// Finalize the configuration and initialize the timer\n    fn finalize(self, control: &mut HrPwmControl) -> HrParts<TIM, PSCL>;\n}\nmacro_rules! impl_finalize {\n    ($($TIMX:ident),+) => {$(\n        impl<PSCL, P1, P2, DacRst, DacStp> HrPwmBuilderExt<$TIMX, PSCL, P1, P2>\n            for HrPwmBuilder<$TIMX, PSCL, stm32_hrtim::PreloadSource, P1, P2, DacRst, DacStp>\n        where PSCL:\n            stm32_hrtim::HrtimPrescaler,\n            P1: Out1Pin<$TIMX>,\n            P2: Out2Pin<$TIMX>,\n            DacRst: DacResetTrigger,\n            DacStp: DacStepTrigger,\n        {\n            fn finalize(\n                self,\n                control: &mut HrPwmControl,\n            ) -> HrParts<$TIMX, PSCL> {\n                let (pin1, pin2) = self._init(control);\n                pin1.connect_to_hrtim();\n                pin2.connect_to_hrtim();\n                unsafe { MaybeUninit::uninit().assume_init() }\n            }\n        }\n    )+};\n}\n\n/// Wrapper a pin that can be used as output to one of the HRTIM timer instances\npub struct Pin<P> {\n    /// The speed setting of the pin\n    pub speed: Speed,\n\n    /// The pin\n    pub pin: P,\n}\n\nimpl_finalize! {\n    HRTIM_TIMA,\n    HRTIM_TIMB,\n    HRTIM_TIMC,\n    HRTIM_TIMD,\n    HRTIM_TIME\n}\n\n#[cfg(hrtim_v2)]\nimpl_finalize! {\n    HRTIM_TIMF\n}\n\n/// Implemented for types that can be used as output1 for HRTIM timer instances\npub trait Out1Pin<TIM>: Output1Pin<TIM> {\n    /// Connect pin to hrtim timer\n    fn connect_to_hrtim(self);\n}\n\n/// Implemented for types that can be used as output2 for HRTIM timer instances\npub trait Out2Pin<TIM>: Output2Pin<TIM> {\n    /// Connect pin to hrtim timer\n    fn connect_to_hrtim(self);\n}\n\nmacro_rules! pins_helper {\n    ($TIMX:ty, $ChannelXPin:ident, $OutYPin:ident, $OutputYPin:ident, $HrOutY:ident) => {\n        unsafe impl<'d, T: $ChannelXPin<HRTIM1>> $OutputYPin<$TIMX> for Pin<Peri<'d, T>> {}\n        impl<'d, T: $ChannelXPin<HRTIM1>> $OutYPin<$TIMX> for Pin<Peri<'d, T>> {\n            fn connect_to_hrtim(self) {\n                self.pin.set_as_af(\n                    self.pin.af_num(),\n                    AfType::output(OutputType::PushPull, self.speed),\n                );\n            }\n        }\n    };\n}\n\npins_helper!(HRTIM_TIMA, ChannelAPin, Out1Pin, Output1Pin, HrOut1);\npins_helper!(HRTIM_TIMA, ChannelAComplementaryPin, Out2Pin, Output2Pin, HrOut2);\npins_helper!(HRTIM_TIMB, ChannelBPin, Out1Pin, Output1Pin, HrOut1);\npins_helper!(HRTIM_TIMB, ChannelBComplementaryPin, Out2Pin, Output2Pin, HrOut2);\npins_helper!(HRTIM_TIMC, ChannelCPin, Out1Pin, Output1Pin, HrOut1);\npins_helper!(HRTIM_TIMC, ChannelCComplementaryPin, Out2Pin, Output2Pin, HrOut2);\npins_helper!(HRTIM_TIMD, ChannelDPin, Out1Pin, Output1Pin, HrOut1);\npins_helper!(HRTIM_TIMD, ChannelDComplementaryPin, Out2Pin, Output2Pin, HrOut2);\npins_helper!(HRTIM_TIME, ChannelEPin, Out1Pin, Output1Pin, HrOut1);\npins_helper!(HRTIM_TIME, ChannelEComplementaryPin, Out2Pin, Output2Pin, HrOut2);\n\n#[cfg(stm32g4)]\npins_helper!(HRTIM_TIMF, ChannelFPin, Out1Pin, Output1Pin, HrOut1);\n#[cfg(stm32g4)]\npins_helper!(HRTIM_TIMF, ChannelFComplementaryPin, Out2Pin, Output2Pin, HrOut2);\n\n// macro_rules! pins {\n//     ($($TIMX:ty: CH1: $CH1:ident<$CH1_AF:literal>, CH2: $CH2:ident<$CH2_AF:literal>),+) => {$(\n//         pins_helper!($TIMX, Out1Pin, Output1Pin, HrOut1, $CH1<$CH1_AF>);\n//         pins_helper!($TIMX, Out2Pin, Output2Pin, HrOut2, $CH2<$CH1_AF>);\n//     )+};\n// }\n\n// #[cfg(stm32g4)]\n// pins! {\n//     HRTIM_TIMA: CH1: PA8<13>,  CH2: PA9<13>,\n//     HRTIM_TIMB: CH1: PA10<13>, CH2: PA11<13>,\n//     HRTIM_TIMC: CH1: PB12<13>, CH2: PB13<13>,\n//     HRTIM_TIMD: CH1: PB14<13>, CH2: PB15<13>,\n//     HRTIM_TIME: CH1: PC8<3>,   CH2: PC9<3>,\n//     HRTIM_TIMF: CH1: PC6<13>,  CH2: PC7<13>\n// }\n\npin_trait!(ChannelAPin, Instance);\npin_trait!(ChannelAComplementaryPin, Instance);\npin_trait!(ChannelBPin, Instance);\npin_trait!(ChannelBComplementaryPin, Instance);\npin_trait!(ChannelCPin, Instance);\npin_trait!(ChannelCComplementaryPin, Instance);\npin_trait!(ChannelDPin, Instance);\npin_trait!(ChannelDComplementaryPin, Instance);\npin_trait!(ChannelEPin, Instance);\npin_trait!(ChannelEComplementaryPin, Instance);\n#[cfg(hrtim_v2)]\npin_trait!(ChannelFPin, Instance);\n#[cfg(hrtim_v2)]\npin_trait!(ChannelFComplementaryPin, Instance);\n"
  },
  {
    "path": "embassy-stm32/src/hrtim/resonant_converter.rs",
    "content": "//! Variable-frequency resonant converter driver.\n//!\n//! This implementation of a resonsant converter is appropriate for a half or full bridge,\n//! but does not include secondary rectification, which is appropriate for applications\n//! with a low-voltage on the secondary side.\nuse stm32_hrtim::control::HrPwmControl;\npub use stm32_hrtim::deadtime::DeadtimeConfig;\nuse stm32_hrtim::output::{HrOutput, Output1Pin, Output2Pin};\nuse stm32_hrtim::timer::{HrTim, HrTimer};\nuse stm32_hrtim::{HrParts, HrPwmAdvExt, HrPwmBuilder, HrtimPrescaler, InterleavedMode, PreloadSource, capture};\n\nuse crate::hrtim::HrPwmBuilderExt;\nuse crate::peripherals::HRTIM1;\nuse crate::rcc::SealedRccPeripheral;\nuse crate::time::Hertz;\n\n/// Variable-frequency resonant converter driver.\npub struct ResonantConverter<TIM, PSCL> {\n    timer: HrParts<TIM, PSCL>,\n    min_period: u16,\n    max_period: u16,\n}\n\nimpl<TIM: HrPwmAdvExt, PSCL: HrtimPrescaler> ResonantConverter<TIM, PSCL>\nwhere\n    TIM: stm32_hrtim::timer::InstanceX + HrPwmAdvExt<PreloadSource = PreloadSource>,\n    HrTim<TIM, PSCL, capture::NoDma, capture::NoDma>: HrTimer,\n{\n    /// Create a new variable-frequency resonant converter driver.\n    pub fn new<P1, P2>(\n        timer: TIM,\n        pin1: P1,\n        pin2: P2,\n        prescaler: PSCL,\n        min_frequency: Hertz,\n        max_frequency: Hertz,\n        hr_control: &mut HrPwmControl,\n        deadtime_cfg: DeadtimeConfig,\n    ) -> Self\n    where\n        P1: Output1Pin<TIM>,\n        P2: Output2Pin<TIM>,\n        HrPwmBuilder<TIM, PSCL, PreloadSource, P1, P2>: HrPwmBuilderExt<TIM, PSCL, P1, P2>,\n    {\n        let f_min = min_frequency.0;\n\n        let timer_f = HRTIM1::frequency().0;\n\n        let psc_min = (timer_f / f_min) / (u16::MAX as u32 / 32);\n        let psc = PSCL::VALUE as u32;\n        assert!(\n            psc >= psc_min,\n            \"Prescaler set too low to be able to reach target frequency\"\n        );\n\n        let timer_f = 32 * (timer_f / psc);\n        let max_period: u16 = (timer_f / f_min) as u16;\n\n        let mut timer = timer\n            .pwm_advanced(pin1, pin2)\n            .prescaler(prescaler)\n            .period(max_period)\n            .preload(PreloadSource::OnRepetitionUpdate)\n            .interleaved_mode(InterleavedMode::Dual)\n            // Dead-time generator can be used in this case because the primary fets\n            // of a resonant converter are always complementary\n            .deadtime(deadtime_cfg)\n            .finalize(hr_control);\n\n        // Set output 1 to active on a period event\n        timer.out1.enable_set_event(&timer.timer);\n\n        // Set output 1 to inactive on a compare 1 event\n        timer.out1.enable_rst_event(&timer.cr1);\n\n        timer.out1.enable();\n        timer.out2.enable();\n\n        let min_period = max_period * (min_frequency.0 / max_frequency.0) as u16;\n\n        Self {\n            timer,\n            min_period: min_period,\n            max_period: max_period,\n        }\n    }\n\n    /// Set the timer period.\n    pub fn set_period(&mut self, period: u16) {\n        assert!(period < self.max_period);\n        assert!(period > self.min_period);\n\n        self.timer.timer.set_period(period);\n    }\n\n    /// Get the minimum compare value of a duty cycle\n    pub fn get_min_period(&mut self) -> u16 {\n        self.min_period\n    }\n\n    /// Get the maximum compare value of a duty cycle\n    pub fn get_max_period(&mut self) -> u16 {\n        self.max_period\n    }\n}\n"
  },
  {
    "path": "embassy-stm32/src/hrtim/traits.rs",
    "content": "use embassy_hal_internal::PeripheralType;\n\nuse crate::rcc::RccPeripheral;\n\npub(crate) trait SealedInstance: RccPeripheral {}\n\n/// HRTIM instance trait.\n#[allow(private_bounds)]\npub trait Instance: SealedInstance + PeripheralType + 'static {}\n\nforeach_interrupt! {\n    ($inst:ident, hrtim, HRTIM, MASTER, $irq:ident) => {\n        impl SealedInstance for crate::peripherals::$inst { }\n\n        impl Instance for crate::peripherals::$inst {\n\n        }\n    };\n}\n"
  },
  {
    "path": "embassy-stm32/src/hsem/mod.rs",
    "content": "//! Hardware Semaphore (HSEM)\n\nuse core::future::poll_fn;\nuse core::marker::PhantomData;\nuse core::sync::atomic::{Ordering, compiler_fence};\nuse core::task::Poll;\n\n#[cfg(all(any(stm32wb, stm32wl5x), feature = \"low-power\"))]\nuse critical_section::CriticalSection;\nuse embassy_hal_internal::PeripheralType;\nuse embassy_sync::waitqueue::AtomicWaker;\n\n// TODO: This code works for all HSEM implemenations except for the STM32WBA52/4/5xx MCUs.\n// Those MCUs have a different HSEM implementation (Secure semaphore lock support,\n// Privileged / unprivileged semaphore lock support, Semaphore lock protection via semaphore attribute),\n// which is not yet supported by this code.\nuse crate::Peri;\nuse crate::cpu::CoreId;\nuse crate::rcc::{self, RccPeripheral};\nuse crate::{interrupt, pac};\n\n/// HSEM error.\n#[derive(Debug)]\npub enum HsemError {\n    /// Locking the semaphore failed.\n    LockFailed,\n}\n\n#[cfg(all(not(all(stm32wb, feature = \"low-power\")), not(all(stm32wl5x, feature = \"low-power\"))))]\nconst PUB_CHANNELS: usize = 6;\n\n#[cfg(all(stm32wl5x, feature = \"low-power\"))]\nconst PUB_CHANNELS: usize = 5;\n\n#[cfg(all(stm32wb, feature = \"low-power\"))]\nconst PUB_CHANNELS: usize = 4;\n\n/// TX interrupt handler.\npub struct HardwareSemaphoreInterruptHandler<T: Instance> {\n    _phantom: PhantomData<T>,\n}\n\nimpl<T: Instance> interrupt::typelevel::Handler<T::Interrupt> for HardwareSemaphoreInterruptHandler<T> {\n    unsafe fn on_interrupt() {\n        let core_id = CoreId::current();\n        let isr = T::regs().isr(core_id.to_index()).read();\n\n        for number in 0..5 {\n            if isr.isf(number as usize) {\n                T::regs()\n                    .icr(core_id.to_index())\n                    .write(|w| w.set_isc(number as usize, true));\n\n                T::state().waker_for(number).wake();\n            }\n        }\n    }\n}\n\n/// Hardware semaphore mutex\npub struct HardwareSemaphoreMutex<'a, T: Instance> {\n    index: u8,\n    process_id: u8,\n    _lifetime: PhantomData<&'a mut T>,\n}\n\nimpl<'a, T: Instance> Drop for HardwareSemaphoreMutex<'a, T> {\n    fn drop(&mut self) {\n        let core_id = CoreId::current();\n\n        T::regs()\n            .icr(core_id.to_index())\n            .write(|w| w.set_isc(self.index as usize, true));\n\n        critical_section::with(|_| {\n            T::regs()\n                .ier(core_id.to_index())\n                .modify(|w| w.set_ise(self.index as usize, false));\n        });\n\n        HardwareSemaphoreChannel::<'a, T> {\n            index: self.index,\n            _lifetime: PhantomData,\n        }\n        .unlock(self.process_id);\n    }\n}\n\n/// Hardware semaphore channel\npub struct HardwareSemaphoreChannel<'a, T: Instance> {\n    index: u8,\n    _lifetime: PhantomData<&'a mut T>,\n}\n\nimpl<'a, T: Instance> HardwareSemaphoreChannel<'a, T> {\n    pub(crate) const fn new(number: u8) -> Self {\n        core::assert!(number > 0 && number <= 6);\n\n        Self {\n            index: number - 1,\n            _lifetime: PhantomData,\n        }\n    }\n\n    /// Locks the semaphore.\n    /// The 2-step lock procedure consists in a write to lock the semaphore, followed by a read to\n    /// check if the lock has been successful, carried out from the HSEM_Rx register.\n    pub async fn lock(&mut self, process_id: u8) -> HardwareSemaphoreMutex<'a, T> {\n        let _scoped_wake_guard = T::RCC_INFO.wake_guard();\n        let core_id = CoreId::current();\n\n        poll_fn(|cx| {\n            T::state().waker_for(self.index).register(cx.waker());\n\n            compiler_fence(Ordering::SeqCst);\n\n            critical_section::with(|_| {\n                T::regs()\n                    .ier(core_id.to_index())\n                    .modify(|w| w.set_ise(self.index as usize, true));\n            });\n\n            match self.try_lock(process_id) {\n                Some(mutex) => Poll::Ready(mutex),\n                None => Poll::Pending,\n            }\n        })\n        .await\n    }\n\n    /// Locks the semaphore in blocking mode\n    /// The 2-step lock procedure consists in a write to lock the semaphore, followed by a read to\n    /// check if the lock has been successful, carried out from the HSEM_Rx register.\n    pub fn blocking_lock(&mut self, process_id: u8) -> HardwareSemaphoreMutex<'a, T> {\n        loop {\n            if let Some(lock) = self.try_lock(process_id) {\n                return lock;\n            }\n        }\n    }\n\n    /// Try to lock the semaphor\n    /// The 2-step lock procedure consists in a write to lock the semaphore, followed by a read to\n    /// check if the lock has been successful, carried out from the HSEM_Rx register.\n    pub fn try_lock(&mut self, process_id: u8) -> Option<HardwareSemaphoreMutex<'a, T>> {\n        if self.two_step_lock(process_id).is_ok() {\n            Some(HardwareSemaphoreMutex {\n                index: self.index,\n                process_id: process_id,\n                _lifetime: PhantomData,\n            })\n        } else {\n            None\n        }\n    }\n\n    /// Locks the semaphore.\n    /// The 2-step lock procedure consists in a write to lock the semaphore, followed by a read to\n    /// check if the lock has been successful, carried out from the HSEM_Rx register.\n    pub fn two_step_lock(&mut self, process_id: u8) -> Result<(), HsemError> {\n        T::regs().r(self.index as usize).write(|w| {\n            w.set_procid(process_id);\n            w.set_coreid(CoreId::current() as u8);\n            w.set_lock(true);\n        });\n        let reg = T::regs().r(self.index as usize).read();\n        match (\n            reg.lock(),\n            reg.coreid() == CoreId::current() as u8,\n            reg.procid() == process_id,\n        ) {\n            (true, true, true) => Ok(()),\n            _ => Err(HsemError::LockFailed),\n        }\n    }\n\n    /// Locks the semaphore.\n    /// The 1-step procedure consists in a read to lock and check the semaphore in a single step,\n    /// carried out from the HSEM_RLRx register.\n    pub fn one_step_lock(&mut self) -> Result<(), HsemError> {\n        let reg = T::regs().rlr(self.index as usize).read();\n        match (reg.lock(), reg.coreid() == CoreId::current() as u8, reg.procid()) {\n            (false, true, 0) => Ok(()),\n            _ => Err(HsemError::LockFailed),\n        }\n    }\n\n    /// Unlocks the semaphore.\n    /// Unlocking a semaphore is a protected process, to prevent accidental clearing by a AHB bus\n    /// core ID or by a process not having the semaphore lock right.\n    pub fn unlock(&mut self, process_id: u8) {\n        T::regs().r(self.index as usize).write(|w| {\n            w.set_procid(process_id);\n            w.set_coreid(CoreId::current() as u8);\n            w.set_lock(false);\n        });\n    }\n\n    /// Return the channel number\n    pub const fn channel(&self) -> u8 {\n        self.index + 1\n    }\n}\n\n/// HSEM driver\npub struct HardwareSemaphore<T: Instance> {\n    _type: PhantomData<T>,\n}\n\nimpl<T: Instance> HardwareSemaphore<T> {\n    /// Creates a new HardwareSemaphore instance.\n    pub fn new<'d>(\n        _peripheral: Peri<'d, T>,\n        _irq: impl interrupt::typelevel::Binding<T::Interrupt, HardwareSemaphoreInterruptHandler<T>> + 'd,\n    ) -> Self {\n        rcc::enable_and_reset_without_stop::<T>();\n\n        HardwareSemaphore { _type: PhantomData }\n    }\n\n    /// Get a single channel, and keep the global struct\n    pub const fn channel_for<'a>(&'a mut self, number: u8) -> HardwareSemaphoreChannel<'a, T> {\n        #[cfg(all(stm32wb, feature = \"low-power\"))]\n        core::assert!(number != 3 && number != 4);\n        #[cfg(all(stm32wl5x, feature = \"low-power\"))]\n        core::assert!(number != 3);\n\n        HardwareSemaphoreChannel::new(number)\n    }\n\n    /// Split the global struct into channels\n    ///\n    /// If using low-power mode, channels 3 and 4 will not be returned\n    pub const fn split<'a>(self) -> [HardwareSemaphoreChannel<'a, T>; PUB_CHANNELS] {\n        [\n            HardwareSemaphoreChannel::new(1),\n            HardwareSemaphoreChannel::new(2),\n            #[cfg(not(all(any(stm32wb, stm32wl5x), feature = \"low-power\")))]\n            HardwareSemaphoreChannel::new(3),\n            #[cfg(not(all(stm32wb, feature = \"low-power\")))]\n            HardwareSemaphoreChannel::new(4),\n            HardwareSemaphoreChannel::new(5),\n            HardwareSemaphoreChannel::new(6),\n        ]\n    }\n\n    /// Unlocks all semaphores.\n    /// All semaphores locked by a COREID can be unlocked at once by using the HSEM_CR\n    /// register. Write COREID and correct KEY value in HSEM_CR. All locked semaphores with a\n    /// matching COREID are unlocked, and may generate an interrupt when enabled.\n    pub fn unlock_all(&mut self, key: u16, core_id: u8) {\n        T::regs().cr().write(|w| {\n            w.set_key(key);\n            w.set_coreid(core_id);\n        });\n    }\n\n    /// Sets the clear (unlock) key\n    pub fn set_clear_key(&mut self, key: u16) {\n        T::regs().keyr().modify(|w| w.set_key(key));\n    }\n\n    /// Gets the clear (unlock) key\n    pub fn get_clear_key(&mut self) -> u16 {\n        T::regs().keyr().read().key()\n    }\n}\n\n#[cfg(all(any(stm32wb, stm32wl5x), feature = \"low-power\"))]\npub(crate) fn init_hsem(cs: CriticalSection) {\n    rcc::enable_and_reset_with_cs::<crate::peripherals::HSEM>(cs);\n}\n\n#[cfg(any(all(stm32wb, feature = \"low-power\"), stm32wl5x))]\npub(crate) const fn get_hsem<'a>(index: usize) -> HardwareSemaphoreChannel<'a, crate::peripherals::HSEM> {\n    match index {\n        #[cfg(any(stm32wb, stm32wl5x))]\n        3 => HardwareSemaphoreChannel::new(3),\n        #[cfg(stm32wb)]\n        4 => HardwareSemaphoreChannel::new(4),\n        _ => core::unreachable!(),\n    }\n}\n\nstruct State {\n    wakers: [AtomicWaker; 6],\n}\n\nimpl State {\n    const fn new() -> Self {\n        Self {\n            wakers: [const { AtomicWaker::new() }; 6],\n        }\n    }\n\n    const fn waker_for(&self, index: u8) -> &AtomicWaker {\n        &self.wakers[index as usize]\n    }\n}\n\ntrait SealedInstance {\n    fn regs() -> pac::hsem::Hsem;\n    fn state() -> &'static State;\n}\n\n/// HSEM instance trait.\n#[allow(private_bounds)]\npub trait Instance: SealedInstance + PeripheralType + RccPeripheral + Send + 'static {\n    /// Interrupt for this peripheral.\n    type Interrupt: interrupt::typelevel::Interrupt;\n}\n\nimpl SealedInstance for crate::peripherals::HSEM {\n    fn regs() -> crate::pac::hsem::Hsem {\n        crate::pac::HSEM\n    }\n\n    fn state() -> &'static State {\n        static STATE: State = State::new();\n        &STATE\n    }\n}\n\nforeach_interrupt!(\n    ($inst:ident, hsem, $block:ident, $signal_name:ident, $irq:ident) => {\n        impl Instance for crate::peripherals::$inst {\n            type Interrupt = crate::interrupt::typelevel::$irq;\n        }\n    };\n);\n"
  },
  {
    "path": "embassy-stm32/src/hspi/enums.rs",
    "content": "//! Enums used in Hspi configuration.\n\n#[allow(dead_code)]\n#[derive(Copy, Clone)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub(crate) enum HspiMode {\n    IndirectWrite,\n    IndirectRead,\n    AutoPolling,\n    MemoryMapped,\n}\n\nimpl Into<u8> for HspiMode {\n    fn into(self) -> u8 {\n        match self {\n            HspiMode::IndirectWrite => 0b00,\n            HspiMode::IndirectRead => 0b01,\n            HspiMode::AutoPolling => 0b10,\n            HspiMode::MemoryMapped => 0b11,\n        }\n    }\n}\n\n/// Hspi lane width\n#[allow(dead_code)]\n#[derive(Copy, Clone)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub enum HspiWidth {\n    /// None\n    NONE,\n    /// Single lane\n    SING,\n    /// Dual lanes\n    DUAL,\n    /// Quad lanes\n    QUAD,\n    /// Eight lanes\n    OCTO,\n    /// Sixteen lanes\n    HEXADECA,\n}\n\nimpl Into<u8> for HspiWidth {\n    fn into(self) -> u8 {\n        match self {\n            HspiWidth::NONE => 0b00,\n            HspiWidth::SING => 0b01,\n            HspiWidth::DUAL => 0b10,\n            HspiWidth::QUAD => 0b11,\n            HspiWidth::OCTO => 0b100,\n            HspiWidth::HEXADECA => 0b101,\n        }\n    }\n}\n\n/// Flash bank selection\n#[allow(dead_code)]\n#[derive(Copy, Clone)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub enum FlashSelection {\n    /// Bank 1\n    Flash1,\n    /// Bank 2\n    Flash2,\n}\n\nimpl Into<bool> for FlashSelection {\n    fn into(self) -> bool {\n        match self {\n            FlashSelection::Flash1 => false,\n            FlashSelection::Flash2 => true,\n        }\n    }\n}\n\n/// Wrap Size\n#[allow(dead_code)]\n#[allow(missing_docs)]\n#[derive(Copy, Clone)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub enum WrapSize {\n    None,\n    _16Bytes,\n    _32Bytes,\n    _64Bytes,\n    _128Bytes,\n}\n\nimpl Into<u8> for WrapSize {\n    fn into(self) -> u8 {\n        match self {\n            WrapSize::None => 0x00,\n            WrapSize::_16Bytes => 0x02,\n            WrapSize::_32Bytes => 0x03,\n            WrapSize::_64Bytes => 0x04,\n            WrapSize::_128Bytes => 0x05,\n        }\n    }\n}\n\n/// Memory Type\n#[allow(missing_docs)]\n#[allow(dead_code)]\n#[derive(Copy, Clone)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub enum MemoryType {\n    Micron,\n    Macronix,\n    Standard,\n    MacronixRam,\n    HyperBusMemory,\n    HyperBusRegister,\n}\n\nimpl Into<u8> for MemoryType {\n    fn into(self) -> u8 {\n        match self {\n            MemoryType::Micron => 0x00,\n            MemoryType::Macronix => 0x01,\n            MemoryType::Standard => 0x02,\n            MemoryType::MacronixRam => 0x03,\n            MemoryType::HyperBusMemory => 0x04,\n            MemoryType::HyperBusRegister => 0x04,\n        }\n    }\n}\n\n/// Hspi memory size.\n#[allow(missing_docs)]\n#[derive(Copy, Clone)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub enum MemorySize {\n    _1KiB,\n    _2KiB,\n    _4KiB,\n    _8KiB,\n    _16KiB,\n    _32KiB,\n    _64KiB,\n    _128KiB,\n    _256KiB,\n    _512KiB,\n    _1MiB,\n    _2MiB,\n    _4MiB,\n    _8MiB,\n    _16MiB,\n    _32MiB,\n    _64MiB,\n    _128MiB,\n    _256MiB,\n    _512MiB,\n    _1GiB,\n    _2GiB,\n    _4GiB,\n    Other(u8),\n}\n\nimpl Into<u8> for MemorySize {\n    fn into(self) -> u8 {\n        match self {\n            MemorySize::_1KiB => 6,\n            MemorySize::_2KiB => 7,\n            MemorySize::_4KiB => 8,\n            MemorySize::_8KiB => 9,\n            MemorySize::_16KiB => 10,\n            MemorySize::_32KiB => 11,\n            MemorySize::_64KiB => 12,\n            MemorySize::_128KiB => 13,\n            MemorySize::_256KiB => 14,\n            MemorySize::_512KiB => 15,\n            MemorySize::_1MiB => 16,\n            MemorySize::_2MiB => 17,\n            MemorySize::_4MiB => 18,\n            MemorySize::_8MiB => 19,\n            MemorySize::_16MiB => 20,\n            MemorySize::_32MiB => 21,\n            MemorySize::_64MiB => 22,\n            MemorySize::_128MiB => 23,\n            MemorySize::_256MiB => 24,\n            MemorySize::_512MiB => 25,\n            MemorySize::_1GiB => 26,\n            MemorySize::_2GiB => 27,\n            MemorySize::_4GiB => 28,\n            MemorySize::Other(val) => val,\n        }\n    }\n}\n\n/// Hspi Address size\n#[derive(Copy, Clone)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub enum AddressSize {\n    /// 8-bit address\n    _8Bit,\n    /// 16-bit address\n    _16Bit,\n    /// 24-bit address\n    _24Bit,\n    /// 32-bit address\n    _32Bit,\n}\n\nimpl Into<u8> for AddressSize {\n    fn into(self) -> u8 {\n        match self {\n            AddressSize::_8Bit => 0b00,\n            AddressSize::_16Bit => 0b01,\n            AddressSize::_24Bit => 0b10,\n            AddressSize::_32Bit => 0b11,\n        }\n    }\n}\n\n/// Time the Chip Select line stays high.\n#[allow(missing_docs)]\n#[derive(Copy, Clone)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub enum ChipSelectHighTime {\n    _1Cycle,\n    _2Cycle,\n    _3Cycle,\n    _4Cycle,\n    _5Cycle,\n    _6Cycle,\n    _7Cycle,\n    _8Cycle,\n}\n\nimpl Into<u8> for ChipSelectHighTime {\n    fn into(self) -> u8 {\n        match self {\n            ChipSelectHighTime::_1Cycle => 0,\n            ChipSelectHighTime::_2Cycle => 1,\n            ChipSelectHighTime::_3Cycle => 2,\n            ChipSelectHighTime::_4Cycle => 3,\n            ChipSelectHighTime::_5Cycle => 4,\n            ChipSelectHighTime::_6Cycle => 5,\n            ChipSelectHighTime::_7Cycle => 6,\n            ChipSelectHighTime::_8Cycle => 7,\n        }\n    }\n}\n\n/// FIFO threshold.\n#[allow(missing_docs)]\n#[derive(Copy, Clone)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub enum FIFOThresholdLevel {\n    _1Bytes,\n    _2Bytes,\n    _3Bytes,\n    _4Bytes,\n    _5Bytes,\n    _6Bytes,\n    _7Bytes,\n    _8Bytes,\n    _9Bytes,\n    _10Bytes,\n    _11Bytes,\n    _12Bytes,\n    _13Bytes,\n    _14Bytes,\n    _15Bytes,\n    _16Bytes,\n    _17Bytes,\n    _18Bytes,\n    _19Bytes,\n    _20Bytes,\n    _21Bytes,\n    _22Bytes,\n    _23Bytes,\n    _24Bytes,\n    _25Bytes,\n    _26Bytes,\n    _27Bytes,\n    _28Bytes,\n    _29Bytes,\n    _30Bytes,\n    _31Bytes,\n    _32Bytes,\n}\n\nimpl Into<u8> for FIFOThresholdLevel {\n    fn into(self) -> u8 {\n        match self {\n            FIFOThresholdLevel::_1Bytes => 0,\n            FIFOThresholdLevel::_2Bytes => 1,\n            FIFOThresholdLevel::_3Bytes => 2,\n            FIFOThresholdLevel::_4Bytes => 3,\n            FIFOThresholdLevel::_5Bytes => 4,\n            FIFOThresholdLevel::_6Bytes => 5,\n            FIFOThresholdLevel::_7Bytes => 6,\n            FIFOThresholdLevel::_8Bytes => 7,\n            FIFOThresholdLevel::_9Bytes => 8,\n            FIFOThresholdLevel::_10Bytes => 9,\n            FIFOThresholdLevel::_11Bytes => 10,\n            FIFOThresholdLevel::_12Bytes => 11,\n            FIFOThresholdLevel::_13Bytes => 12,\n            FIFOThresholdLevel::_14Bytes => 13,\n            FIFOThresholdLevel::_15Bytes => 14,\n            FIFOThresholdLevel::_16Bytes => 15,\n            FIFOThresholdLevel::_17Bytes => 16,\n            FIFOThresholdLevel::_18Bytes => 17,\n            FIFOThresholdLevel::_19Bytes => 18,\n            FIFOThresholdLevel::_20Bytes => 19,\n            FIFOThresholdLevel::_21Bytes => 20,\n            FIFOThresholdLevel::_22Bytes => 21,\n            FIFOThresholdLevel::_23Bytes => 22,\n            FIFOThresholdLevel::_24Bytes => 23,\n            FIFOThresholdLevel::_25Bytes => 24,\n            FIFOThresholdLevel::_26Bytes => 25,\n            FIFOThresholdLevel::_27Bytes => 26,\n            FIFOThresholdLevel::_28Bytes => 27,\n            FIFOThresholdLevel::_29Bytes => 28,\n            FIFOThresholdLevel::_30Bytes => 29,\n            FIFOThresholdLevel::_31Bytes => 30,\n            FIFOThresholdLevel::_32Bytes => 31,\n        }\n    }\n}\n\n/// Dummy cycle count\n#[allow(missing_docs)]\n#[derive(Copy, Clone)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub enum DummyCycles {\n    _0,\n    _1,\n    _2,\n    _3,\n    _4,\n    _5,\n    _6,\n    _7,\n    _8,\n    _9,\n    _10,\n    _11,\n    _12,\n    _13,\n    _14,\n    _15,\n    _16,\n    _17,\n    _18,\n    _19,\n    _20,\n    _21,\n    _22,\n    _23,\n    _24,\n    _25,\n    _26,\n    _27,\n    _28,\n    _29,\n    _30,\n    _31,\n}\n\nimpl Into<u8> for DummyCycles {\n    fn into(self) -> u8 {\n        match self {\n            DummyCycles::_0 => 0,\n            DummyCycles::_1 => 1,\n            DummyCycles::_2 => 2,\n            DummyCycles::_3 => 3,\n            DummyCycles::_4 => 4,\n            DummyCycles::_5 => 5,\n            DummyCycles::_6 => 6,\n            DummyCycles::_7 => 7,\n            DummyCycles::_8 => 8,\n            DummyCycles::_9 => 9,\n            DummyCycles::_10 => 10,\n            DummyCycles::_11 => 11,\n            DummyCycles::_12 => 12,\n            DummyCycles::_13 => 13,\n            DummyCycles::_14 => 14,\n            DummyCycles::_15 => 15,\n            DummyCycles::_16 => 16,\n            DummyCycles::_17 => 17,\n            DummyCycles::_18 => 18,\n            DummyCycles::_19 => 19,\n            DummyCycles::_20 => 20,\n            DummyCycles::_21 => 21,\n            DummyCycles::_22 => 22,\n            DummyCycles::_23 => 23,\n            DummyCycles::_24 => 24,\n            DummyCycles::_25 => 25,\n            DummyCycles::_26 => 26,\n            DummyCycles::_27 => 27,\n            DummyCycles::_28 => 28,\n            DummyCycles::_29 => 29,\n            DummyCycles::_30 => 30,\n            DummyCycles::_31 => 31,\n        }\n    }\n}\n\n/// Functional mode\n#[allow(missing_docs)]\n#[allow(dead_code)]\n#[derive(Copy, Clone)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub enum FunctionalMode {\n    IndirectWrite,\n    IndirectRead,\n    AutoStatusPolling,\n    MemoryMapped,\n}\n\nimpl Into<u8> for FunctionalMode {\n    fn into(self) -> u8 {\n        match self {\n            FunctionalMode::IndirectWrite => 0x00,\n            FunctionalMode::IndirectRead => 0x01,\n            FunctionalMode::AutoStatusPolling => 0x02,\n            FunctionalMode::MemoryMapped => 0x03,\n        }\n    }\n}\n"
  },
  {
    "path": "embassy-stm32/src/hspi/mod.rs",
    "content": "//! HSPI Serial Peripheral Interface\n//!\n\n// NOTE: This is a partial implementation of the HSPI driver.\n// It implements only Single and Octal SPI modes, but additional\n// modes can be added as needed following the same pattern and\n// using ospi/mod.rs as a reference.\n\n#![macro_use]\n\npub mod enums;\n\nuse core::marker::PhantomData;\n\nuse embassy_embedded_hal::{GetConfig, SetConfig};\nuse embassy_hal_internal::{Peri, PeripheralType};\npub use enums::*;\n\nuse crate::dma::{ChannelAndRequest, word};\nuse crate::gpio::{AfType, Flex, OutputType, Pull, Speed};\nuse crate::mode::{Async, Blocking, Mode as PeriMode};\nuse crate::pac::hspi::Hspi as Regs;\nuse crate::peripherals;\nuse crate::rcc::{self, RccPeripheral};\n\n/// HSPI driver config.\n#[derive(Clone, Copy)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub struct Config {\n    /// Fifo threshold used by the peripheral to generate the interrupt indicating data\n    /// or space is available in the FIFO\n    pub fifo_threshold: FIFOThresholdLevel,\n    /// Indicates the type of external device connected\n    pub memory_type: MemoryType, // Need to add an additional enum to provide this public interface\n    /// Defines the size of the external device connected to the HSPI corresponding\n    /// to the number of address bits required to access the device\n    pub device_size: MemorySize,\n    /// Sets the minimum number of clock cycles that the chip select signal must be held high\n    /// between commands\n    pub chip_select_high_time: ChipSelectHighTime,\n    /// Enables the free running clock\n    pub free_running_clock: bool,\n    /// Sets the clock level when the device is not selected\n    pub clock_mode: bool,\n    /// Indicates the wrap size corresponding to the external device configuration\n    pub wrap_size: WrapSize,\n    /// Specified the prescaler factor used for generating the external clock based\n    /// on the AHB clock\n    pub clock_prescaler: u8,\n    /// Allows the delay of 1/2 cycle the data sampling to account for external\n    /// signal delays\n    pub sample_shifting: bool,\n    /// Allows hold to 1/4 cycle the data\n    pub delay_hold_quarter_cycle: bool,\n    /// Enables the transaction boundary feature and defines the boundary to release\n    /// the chip select\n    pub chip_select_boundary: u8,\n    /// Enables the delay block bypass so the sampling is not affected by the delay block\n    pub delay_block_bypass: bool,\n    /// Enables communication regulation feature. Chip select is released when the other\n    /// HSPI requests access to the bus\n    pub max_transfer: u8,\n    /// Enables the refresh feature, chip select is released every refresh + 1 clock cycles\n    pub refresh: u32,\n}\n\nimpl Default for Config {\n    fn default() -> Self {\n        Self {\n            fifo_threshold: FIFOThresholdLevel::_16Bytes,\n            memory_type: MemoryType::Micron,\n            device_size: MemorySize::Other(0),\n            chip_select_high_time: ChipSelectHighTime::_5Cycle,\n            free_running_clock: false,\n            clock_mode: false,\n            wrap_size: WrapSize::None,\n            clock_prescaler: 0,\n            sample_shifting: false,\n            delay_hold_quarter_cycle: false,\n            chip_select_boundary: 0, // Acceptable range 0 to 31\n            delay_block_bypass: true,\n            max_transfer: 0,\n            refresh: 0,\n        }\n    }\n}\n\n/// HSPI transfer configuration.\n#[derive(Clone, Copy)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub struct TransferConfig {\n    /// Instruction width (IMODE)\n    pub iwidth: HspiWidth,\n    /// Instruction Id\n    pub instruction: Option<u32>,\n    /// Number of Instruction Bytes\n    pub isize: AddressSize,\n    /// Instruction Double Transfer rate enable\n    pub idtr: bool,\n\n    /// Address width (ADMODE)\n    pub adwidth: HspiWidth,\n    /// Device memory address\n    pub address: Option<u32>,\n    /// Number of Address Bytes\n    pub adsize: AddressSize,\n    /// Address Double Transfer rate enable\n    pub addtr: bool,\n\n    /// Alternate bytes width (ABMODE)\n    pub abwidth: HspiWidth,\n    /// Alternate Bytes\n    pub alternate_bytes: Option<u32>,\n    /// Number of Alternate Bytes\n    pub absize: AddressSize,\n    /// Alternate Bytes Double Transfer rate enable\n    pub abdtr: bool,\n\n    /// Data width (DMODE)\n    pub dwidth: HspiWidth,\n    /// Data Double Transfer rate enable\n    pub ddtr: bool,\n\n    /// Number of dummy cycles (DCYC)\n    pub dummy: DummyCycles,\n}\n\nimpl Default for TransferConfig {\n    fn default() -> Self {\n        Self {\n            iwidth: HspiWidth::NONE,\n            instruction: None,\n            isize: AddressSize::_8Bit,\n            idtr: false,\n\n            adwidth: HspiWidth::NONE,\n            address: None,\n            adsize: AddressSize::_8Bit,\n            addtr: false,\n\n            abwidth: HspiWidth::NONE,\n            alternate_bytes: None,\n            absize: AddressSize::_8Bit,\n            abdtr: false,\n\n            dwidth: HspiWidth::NONE,\n            ddtr: false,\n\n            dummy: DummyCycles::_0,\n        }\n    }\n}\n\n/// Error used for HSPI implementation\n#[derive(Debug)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub enum HspiError {\n    /// Peripheral configuration is invalid\n    InvalidConfiguration,\n    /// Operation configuration is invalid\n    InvalidCommand,\n    /// Size zero buffer passed to instruction\n    EmptyBuffer,\n}\n\n/// HSPI driver.\npub struct Hspi<'d, T: Instance, M: PeriMode> {\n    _peri: Peri<'d, T>,\n    _sck: Option<Flex<'d>>,\n    _d0: Option<Flex<'d>>,\n    _d1: Option<Flex<'d>>,\n    _d2: Option<Flex<'d>>,\n    _d3: Option<Flex<'d>>,\n    _d4: Option<Flex<'d>>,\n    _d5: Option<Flex<'d>>,\n    _d6: Option<Flex<'d>>,\n    _d7: Option<Flex<'d>>,\n    _d8: Option<Flex<'d>>,\n    _d9: Option<Flex<'d>>,\n    _d10: Option<Flex<'d>>,\n    _d11: Option<Flex<'d>>,\n    _d12: Option<Flex<'d>>,\n    _d13: Option<Flex<'d>>,\n    _d14: Option<Flex<'d>>,\n    _d15: Option<Flex<'d>>,\n    _nss: Option<Flex<'d>>,\n    _dqs0: Option<Flex<'d>>,\n    _dqs1: Option<Flex<'d>>,\n    dma: Option<ChannelAndRequest<'d>>,\n    _phantom: PhantomData<M>,\n    config: Config,\n    width: HspiWidth,\n}\n\nimpl<'d, T: Instance, M: PeriMode> Hspi<'d, T, M> {\n    /// Enter memory mode.\n    /// The Input `read_config` is used to configure the read operation in memory mode\n    pub fn enable_memory_mapped_mode(\n        &mut self,\n        read_config: TransferConfig,\n        write_config: TransferConfig,\n    ) -> Result<(), HspiError> {\n        // Use configure command to set read config\n        self.configure_command(&read_config, None)?;\n\n        // Set writing configurations, there are separate registers for write configurations in memory mapped mode\n        T::REGS.wccr().modify(|w| {\n            w.set_imode(write_config.iwidth.into());\n            w.set_idtr(write_config.idtr);\n            w.set_isize(write_config.isize.into());\n\n            w.set_admode(write_config.adwidth.into());\n            w.set_addtr(write_config.idtr);\n            w.set_adsize(write_config.adsize.into());\n\n            w.set_dmode(write_config.dwidth.into());\n            w.set_ddtr(write_config.ddtr);\n\n            w.set_abmode(write_config.abwidth.into());\n            w.set_dqse(true);\n        });\n\n        T::REGS.wtcr().modify(|w| w.set_dcyc(write_config.dummy.into()));\n\n        // Enable memory mapped mode\n        T::REGS.cr().modify(|r| {\n            r.set_fmode(FunctionalMode::MemoryMapped.into());\n            r.set_tcen(false);\n        });\n        Ok(())\n    }\n\n    /// Quit from memory mapped mode\n    pub fn disable_memory_mapped_mode(&mut self) {\n        T::REGS.cr().modify(|r| {\n            r.set_fmode(FunctionalMode::IndirectWrite.into());\n            r.set_abort(true);\n            r.set_dmaen(false);\n            r.set_en(false);\n        });\n\n        // Clear transfer complete flag\n        T::REGS.fcr().write(|w| w.set_ctcf(true));\n\n        // Re-enable HSPI\n        T::REGS.cr().modify(|r| {\n            r.set_en(true);\n        });\n    }\n\n    fn new_inner(\n        peri: Peri<'d, T>,\n        d0: Option<Flex<'d>>,\n        d1: Option<Flex<'d>>,\n        d2: Option<Flex<'d>>,\n        d3: Option<Flex<'d>>,\n        d4: Option<Flex<'d>>,\n        d5: Option<Flex<'d>>,\n        d6: Option<Flex<'d>>,\n        d7: Option<Flex<'d>>,\n        d8: Option<Flex<'d>>,\n        d9: Option<Flex<'d>>,\n        d10: Option<Flex<'d>>,\n        d11: Option<Flex<'d>>,\n        d12: Option<Flex<'d>>,\n        d13: Option<Flex<'d>>,\n        d14: Option<Flex<'d>>,\n        d15: Option<Flex<'d>>,\n        sck: Option<Flex<'d>>,\n        nss: Option<Flex<'d>>,\n        dqs0: Option<Flex<'d>>,\n        dqs1: Option<Flex<'d>>,\n        dma: Option<ChannelAndRequest<'d>>,\n        config: Config,\n        width: HspiWidth,\n        dual_memory_mode: bool,\n    ) -> Self {\n        // System configuration\n        rcc::enable_and_reset::<T>();\n\n        // Call this function just to check that the clock for HSPI1 is properly setup\n        let _ = T::frequency();\n\n        while T::REGS.sr().read().busy() {}\n\n        Self::configure_registers(&config, Some(dual_memory_mode));\n\n        Self {\n            _peri: peri,\n            _sck: sck,\n            _d0: d0,\n            _d1: d1,\n            _d2: d2,\n            _d3: d3,\n            _d4: d4,\n            _d5: d5,\n            _d6: d6,\n            _d7: d7,\n            _d8: d8,\n            _d9: d9,\n            _d10: d10,\n            _d11: d11,\n            _d12: d12,\n            _d13: d13,\n            _d14: d14,\n            _d15: d15,\n            _nss: nss,\n            _dqs0: dqs0,\n            _dqs1: dqs1,\n            dma,\n            _phantom: PhantomData,\n            config,\n            width,\n        }\n    }\n\n    fn configure_registers(config: &Config, dual_memory_mode: Option<bool>) {\n        // Device configuration\n        T::REGS.dcr1().modify(|w| {\n            w.set_mtyp(config.memory_type.into());\n            w.set_devsize(config.device_size.into());\n            w.set_csht(config.chip_select_high_time.into());\n            w.set_frck(false);\n            w.set_ckmode(config.clock_mode);\n            w.set_dlybyp(config.delay_block_bypass);\n        });\n\n        T::REGS.dcr2().modify(|w| {\n            w.set_wrapsize(config.wrap_size.into());\n        });\n\n        T::REGS.dcr3().modify(|w| {\n            w.set_csbound(config.chip_select_boundary);\n            w.set_maxtran(config.max_transfer);\n        });\n\n        T::REGS.dcr4().modify(|w| {\n            w.set_refresh(config.refresh);\n        });\n\n        T::REGS.cr().modify(|w| {\n            w.set_fthres(config.fifo_threshold.into());\n        });\n\n        // Wait for busy flag to clear\n        while T::REGS.sr().read().busy() {}\n\n        T::REGS.dcr2().modify(|w| {\n            w.set_prescaler(config.clock_prescaler);\n        });\n\n        // The configuration of clock prescaler trigger automatically a calibration process\n        // So it is necessary to wait the calibration is complete\n        while T::REGS.sr().read().busy() {}\n\n        if let Some(dual_memory_mode) = dual_memory_mode {\n            T::REGS.cr().modify(|w| {\n                w.set_dmm(dual_memory_mode);\n            });\n        }\n\n        T::REGS.tcr().modify(|w| {\n            w.set_sshift(config.sample_shifting);\n            w.set_dhqc(config.delay_hold_quarter_cycle);\n        });\n\n        // Enable peripheral\n        T::REGS.cr().modify(|w| {\n            w.set_en(true);\n        });\n\n        // Free running clock needs to be set after peripheral enable\n        if config.free_running_clock {\n            T::REGS.dcr1().modify(|w| {\n                w.set_frck(config.free_running_clock);\n            });\n        }\n    }\n\n    // Function to configure the peripheral for the requested command\n    fn configure_command(&mut self, command: &TransferConfig, data_len: Option<usize>) -> Result<(), HspiError> {\n        // Check that transaction doesn't use more than hardware initialized pins\n        if <enums::HspiWidth as Into<u8>>::into(command.iwidth) > <enums::HspiWidth as Into<u8>>::into(self.width)\n            || <enums::HspiWidth as Into<u8>>::into(command.adwidth) > <enums::HspiWidth as Into<u8>>::into(self.width)\n            || <enums::HspiWidth as Into<u8>>::into(command.abwidth) > <enums::HspiWidth as Into<u8>>::into(self.width)\n            || <enums::HspiWidth as Into<u8>>::into(command.dwidth) > <enums::HspiWidth as Into<u8>>::into(self.width)\n        {\n            return Err(HspiError::InvalidCommand);\n        }\n\n        while T::REGS.sr().read().busy() {}\n\n        T::REGS.cr().modify(|w| {\n            w.set_fmode(FunctionalMode::IndirectWrite.into());\n        });\n\n        // Configure alternate bytes\n        if let Some(ab) = command.alternate_bytes {\n            T::REGS.abr().write(|v| v.set_alternate(ab));\n        }\n\n        // Configure dummy cycles\n        T::REGS.tcr().modify(|w| {\n            w.set_dcyc(command.dummy.into());\n        });\n\n        // Configure data\n        if let Some(data_length) = data_len {\n            T::REGS.dlr().write(|v| {\n                v.set_dl((data_length - 1) as u32);\n            });\n        } else {\n            T::REGS.dlr().write(|v| {\n                v.set_dl((0) as u32);\n            });\n        }\n\n        // Configure instruction/address/alternate bytes/data modes\n        T::REGS.ccr().modify(|w| {\n            w.set_imode(command.iwidth.into());\n            w.set_idtr(command.idtr);\n            w.set_isize(command.isize.into());\n\n            w.set_admode(command.adwidth.into());\n            w.set_addtr(command.addtr);\n            w.set_adsize(command.adsize.into());\n\n            w.set_abmode(command.abwidth.into());\n            w.set_abdtr(command.abdtr);\n            w.set_absize(command.absize.into());\n\n            w.set_dmode(command.dwidth.into());\n            w.set_ddtr(command.ddtr);\n        });\n\n        // Configure DQS\n        T::REGS.ccr().modify(|w| {\n            w.set_dqse(command.ddtr && command.instruction.unwrap_or(0) != 0x12ED);\n        });\n\n        // Set information required to initiate transaction\n        if let Some(instruction) = command.instruction {\n            if let Some(address) = command.address {\n                T::REGS.ir().write(|v| {\n                    v.set_instruction(instruction);\n                });\n\n                T::REGS.ar().write(|v| {\n                    v.set_address(address);\n                });\n            } else {\n                T::REGS.ir().write(|v| {\n                    v.set_instruction(instruction);\n                });\n            }\n        } else {\n            if let Some(address) = command.address {\n                T::REGS.ar().write(|v| {\n                    v.set_address(address);\n                });\n            } else {\n                // The only single phase transaction supported is instruction only\n                return Err(HspiError::InvalidCommand);\n            }\n        }\n\n        Ok(())\n    }\n\n    /// Function used to control or configure the target device without data transfer\n    pub fn blocking_command(&mut self, command: &TransferConfig) -> Result<(), HspiError> {\n        // Wait for peripheral to be free\n        while T::REGS.sr().read().busy() {}\n\n        // Need additional validation that command configuration doesn't have data set\n        self.configure_command(command, None)?;\n\n        // Transaction initiated by setting final configuration, i.e the instruction register\n        while !T::REGS.sr().read().tcf() {}\n        T::REGS.fcr().write(|w| {\n            w.set_ctcf(true);\n        });\n\n        Ok(())\n    }\n\n    /// Blocking read with byte by byte data transfer\n    pub fn blocking_read<W: Word>(&mut self, buf: &mut [W], transaction: TransferConfig) -> Result<(), HspiError> {\n        if buf.is_empty() {\n            return Err(HspiError::EmptyBuffer);\n        }\n\n        // Wait for peripheral to be free\n        while T::REGS.sr().read().busy() {}\n\n        // Ensure DMA is not enabled for this transaction\n        T::REGS.cr().modify(|w| {\n            w.set_dmaen(false);\n        });\n\n        let transfer_size_bytes = buf.len() * W::size().bytes();\n        self.configure_command(&transaction, Some(transfer_size_bytes))?;\n\n        let current_address = T::REGS.ar().read().address();\n        let current_instruction = T::REGS.ir().read().instruction();\n\n        // For a indirect read transaction, the transaction begins when the instruction/address is set\n        T::REGS\n            .cr()\n            .modify(|v| v.set_fmode(FunctionalMode::IndirectRead.into()));\n        if T::REGS.ccr().read().admode() == HspiWidth::NONE.into() {\n            T::REGS.ir().write(|v| v.set_instruction(current_instruction));\n        } else {\n            T::REGS.ar().write(|v| v.set_address(current_address));\n        }\n\n        for idx in 0..buf.len() {\n            while !T::REGS.sr().read().tcf() && !T::REGS.sr().read().ftf() {}\n            buf[idx] = unsafe { (T::REGS.dr().as_ptr() as *mut W).read_volatile() };\n        }\n\n        while !T::REGS.sr().read().tcf() {}\n        T::REGS.fcr().write(|v| v.set_ctcf(true));\n\n        Ok(())\n    }\n\n    /// Blocking write with byte by byte data transfer\n    pub fn blocking_write<W: Word>(&mut self, buf: &[W], transaction: TransferConfig) -> Result<(), HspiError> {\n        if buf.is_empty() {\n            return Err(HspiError::EmptyBuffer);\n        }\n\n        // Wait for peripheral to be free\n        while T::REGS.sr().read().busy() {}\n\n        T::REGS.cr().modify(|w| {\n            w.set_dmaen(false);\n        });\n\n        let transfer_size_bytes = buf.len() * W::size().bytes();\n        self.configure_command(&transaction, Some(transfer_size_bytes))?;\n\n        T::REGS\n            .cr()\n            .modify(|v| v.set_fmode(FunctionalMode::IndirectWrite.into()));\n\n        for idx in 0..buf.len() {\n            while !T::REGS.sr().read().ftf() {}\n            unsafe { (T::REGS.dr().as_ptr() as *mut W).write_volatile(buf[idx]) };\n        }\n\n        while !T::REGS.sr().read().tcf() {}\n        T::REGS.fcr().write(|v| v.set_ctcf(true));\n\n        Ok(())\n    }\n\n    /// Set new bus configuration\n    pub fn set_config(&mut self, config: &Config) {\n        // Wait for busy flag to clear\n        while T::REGS.sr().read().busy() {}\n\n        // Disable DMA channel while configuring the peripheral\n        T::REGS.cr().modify(|w| {\n            w.set_dmaen(false);\n        });\n\n        Self::configure_registers(config, None);\n\n        self.config = *config;\n    }\n\n    /// Get current configuration\n    pub fn get_config(&self) -> Config {\n        self.config\n    }\n}\n\nimpl<'d, T: Instance> Hspi<'d, T, Blocking> {\n    /// Create new blocking HSPI driver for single spi external chip\n    pub fn new_blocking_singlespi(\n        peri: Peri<'d, T>,\n        sck: Peri<'d, impl SckPin<T>>,\n        d0: Peri<'d, impl D0Pin<T>>,\n        d1: Peri<'d, impl D1Pin<T>>,\n        nss: Peri<'d, impl NSSPin<T>>,\n        config: Config,\n    ) -> Self {\n        Self::new_inner(\n            peri,\n            new_pin!(d0, AfType::output(OutputType::PushPull, Speed::VeryHigh)),\n            new_pin!(d1, AfType::input(Pull::None)),\n            None,\n            None,\n            None,\n            None,\n            None,\n            None,\n            None,\n            None,\n            None,\n            None,\n            None,\n            None,\n            None,\n            None,\n            new_pin!(sck, AfType::output(OutputType::PushPull, Speed::VeryHigh)),\n            new_pin!(\n                nss,\n                AfType::output_pull(OutputType::PushPull, Speed::VeryHigh, Pull::Up)\n            ),\n            None,\n            None,\n            None,\n            config,\n            HspiWidth::SING,\n            false,\n        )\n    }\n\n    /// Create new blocking HSPI driver for octospi external chip\n    pub fn new_blocking_octospi(\n        peri: Peri<'d, T>,\n        sck: Peri<'d, impl SckPin<T>>,\n        d0: Peri<'d, impl D0Pin<T>>,\n        d1: Peri<'d, impl D1Pin<T>>,\n        d2: Peri<'d, impl D2Pin<T>>,\n        d3: Peri<'d, impl D3Pin<T>>,\n        d4: Peri<'d, impl D4Pin<T>>,\n        d5: Peri<'d, impl D5Pin<T>>,\n        d6: Peri<'d, impl D6Pin<T>>,\n        d7: Peri<'d, impl D7Pin<T>>,\n        nss: Peri<'d, impl NSSPin<T>>,\n        dqs0: Peri<'d, impl DQS0Pin<T>>,\n        config: Config,\n    ) -> Self {\n        Self::new_inner(\n            peri,\n            new_pin!(d0, AfType::output(OutputType::PushPull, Speed::VeryHigh)),\n            new_pin!(d1, AfType::output(OutputType::PushPull, Speed::VeryHigh)),\n            new_pin!(d2, AfType::output(OutputType::PushPull, Speed::VeryHigh)),\n            new_pin!(d3, AfType::output(OutputType::PushPull, Speed::VeryHigh)),\n            new_pin!(d4, AfType::output(OutputType::PushPull, Speed::VeryHigh)),\n            new_pin!(d5, AfType::output(OutputType::PushPull, Speed::VeryHigh)),\n            new_pin!(d6, AfType::output(OutputType::PushPull, Speed::VeryHigh)),\n            new_pin!(d7, AfType::output(OutputType::PushPull, Speed::VeryHigh)),\n            None,\n            None,\n            None,\n            None,\n            None,\n            None,\n            None,\n            None,\n            new_pin!(sck, AfType::output(OutputType::PushPull, Speed::VeryHigh)),\n            new_pin!(\n                nss,\n                AfType::output_pull(OutputType::PushPull, Speed::VeryHigh, Pull::Up)\n            ),\n            new_pin!(dqs0, AfType::output(OutputType::PushPull, Speed::VeryHigh)),\n            None,\n            None,\n            config,\n            HspiWidth::OCTO,\n            false,\n        )\n    }\n}\n\nimpl<'d, T: Instance> Hspi<'d, T, Async> {\n    /// Create new HSPI driver for a single spi external chip\n    pub fn new_singlespi<D: HspiDma<T>>(\n        peri: Peri<'d, T>,\n        sck: Peri<'d, impl SckPin<T>>,\n        d0: Peri<'d, impl D0Pin<T>>,\n        d1: Peri<'d, impl D1Pin<T>>,\n        nss: Peri<'d, impl NSSPin<T>>,\n        dma: Peri<'d, D>,\n        _irq: impl crate::interrupt::typelevel::Binding<D::Interrupt, crate::dma::InterruptHandler<D>> + 'd,\n        config: Config,\n    ) -> Self {\n        Self::new_inner(\n            peri,\n            new_pin!(d0, AfType::output(OutputType::PushPull, Speed::VeryHigh)),\n            new_pin!(d1, AfType::input(Pull::None)),\n            None,\n            None,\n            None,\n            None,\n            None,\n            None,\n            None,\n            None,\n            None,\n            None,\n            None,\n            None,\n            None,\n            None,\n            new_pin!(sck, AfType::output(OutputType::PushPull, Speed::VeryHigh)),\n            new_pin!(\n                nss,\n                AfType::output_pull(OutputType::PushPull, Speed::VeryHigh, Pull::Up)\n            ),\n            None,\n            None,\n            new_dma!(dma, _irq),\n            config,\n            HspiWidth::SING,\n            false,\n        )\n    }\n\n    /// Create new HSPI driver for octospi external chip\n    pub fn new_octospi<D: HspiDma<T>>(\n        peri: Peri<'d, T>,\n        sck: Peri<'d, impl SckPin<T>>,\n        d0: Peri<'d, impl D0Pin<T>>,\n        d1: Peri<'d, impl D1Pin<T>>,\n        d2: Peri<'d, impl D2Pin<T>>,\n        d3: Peri<'d, impl D3Pin<T>>,\n        d4: Peri<'d, impl D4Pin<T>>,\n        d5: Peri<'d, impl D5Pin<T>>,\n        d6: Peri<'d, impl D6Pin<T>>,\n        d7: Peri<'d, impl D7Pin<T>>,\n        nss: Peri<'d, impl NSSPin<T>>,\n        dqs0: Peri<'d, impl DQS0Pin<T>>,\n        dma: Peri<'d, D>,\n        _irq: impl crate::interrupt::typelevel::Binding<D::Interrupt, crate::dma::InterruptHandler<D>> + 'd,\n        config: Config,\n    ) -> Self {\n        Self::new_inner(\n            peri,\n            new_pin!(d0, AfType::output(OutputType::PushPull, Speed::VeryHigh)),\n            new_pin!(d1, AfType::output(OutputType::PushPull, Speed::VeryHigh)),\n            new_pin!(d2, AfType::output(OutputType::PushPull, Speed::VeryHigh)),\n            new_pin!(d3, AfType::output(OutputType::PushPull, Speed::VeryHigh)),\n            new_pin!(d4, AfType::output(OutputType::PushPull, Speed::VeryHigh)),\n            new_pin!(d5, AfType::output(OutputType::PushPull, Speed::VeryHigh)),\n            new_pin!(d6, AfType::output(OutputType::PushPull, Speed::VeryHigh)),\n            new_pin!(d7, AfType::output(OutputType::PushPull, Speed::VeryHigh)),\n            new_pin!(sck, AfType::output(OutputType::PushPull, Speed::VeryHigh)),\n            None,\n            None,\n            None,\n            None,\n            None,\n            None,\n            None,\n            None,\n            new_pin!(\n                nss,\n                AfType::output_pull(OutputType::PushPull, Speed::VeryHigh, Pull::Up)\n            ),\n            new_pin!(dqs0, AfType::output(OutputType::PushPull, Speed::VeryHigh)),\n            None,\n            new_dma!(dma, _irq),\n            config,\n            HspiWidth::OCTO,\n            false,\n        )\n    }\n\n    /// Blocking read with DMA transfer\n    pub fn blocking_read_dma<W: Word>(&mut self, buf: &mut [W], transaction: TransferConfig) -> Result<(), HspiError> {\n        if buf.is_empty() {\n            return Err(HspiError::EmptyBuffer);\n        }\n\n        // Wait for peripheral to be free\n        while T::REGS.sr().read().busy() {}\n\n        let transfer_size_bytes = buf.len() * W::size().bytes();\n        self.configure_command(&transaction, Some(transfer_size_bytes))?;\n\n        let current_address = T::REGS.ar().read().address();\n        let current_instruction = T::REGS.ir().read().instruction();\n\n        // For a indirect read transaction, the transaction begins when the instruction/address is set\n        T::REGS\n            .cr()\n            .modify(|v| v.set_fmode(FunctionalMode::IndirectRead.into()));\n        if T::REGS.ccr().read().admode() == HspiWidth::NONE.into() {\n            T::REGS.ir().write(|v| v.set_instruction(current_instruction));\n        } else {\n            T::REGS.ar().write(|v| v.set_address(current_address));\n        }\n\n        for chunk in buf.chunks_mut(0xFFFF / W::size().bytes()) {\n            let transfer = unsafe {\n                self.dma\n                    .as_mut()\n                    .unwrap()\n                    .read(T::REGS.dr().as_ptr() as *mut W, chunk, Default::default())\n            };\n\n            T::REGS.cr().modify(|w| w.set_dmaen(true));\n\n            transfer.blocking_wait();\n        }\n\n        finish_dma(T::REGS);\n\n        Ok(())\n    }\n\n    /// Blocking write with DMA transfer\n    pub fn blocking_write_dma<W: Word>(&mut self, buf: &[W], transaction: TransferConfig) -> Result<(), HspiError> {\n        if buf.is_empty() {\n            return Err(HspiError::EmptyBuffer);\n        }\n\n        // Wait for peripheral to be free\n        while T::REGS.sr().read().busy() {}\n\n        let transfer_size_bytes = buf.len() * W::size().bytes();\n        self.configure_command(&transaction, Some(transfer_size_bytes))?;\n        T::REGS\n            .cr()\n            .modify(|v| v.set_fmode(FunctionalMode::IndirectWrite.into()));\n\n        for chunk in buf.chunks(0xFFFF / W::size().bytes()) {\n            let transfer = unsafe {\n                self.dma\n                    .as_mut()\n                    .unwrap()\n                    .write(chunk, T::REGS.dr().as_ptr() as *mut W, Default::default())\n            };\n\n            T::REGS.cr().modify(|w| w.set_dmaen(true));\n\n            transfer.blocking_wait();\n        }\n\n        finish_dma(T::REGS);\n\n        Ok(())\n    }\n\n    /// Asynchronous read from external device\n    pub async fn read<W: Word>(&mut self, buf: &mut [W], transaction: TransferConfig) -> Result<(), HspiError> {\n        if buf.is_empty() {\n            return Err(HspiError::EmptyBuffer);\n        }\n\n        // Wait for peripheral to be free\n        while T::REGS.sr().read().busy() {}\n\n        let transfer_size_bytes = buf.len() * W::size().bytes();\n        self.configure_command(&transaction, Some(transfer_size_bytes))?;\n\n        let current_address = T::REGS.ar().read().address();\n        let current_instruction = T::REGS.ir().read().instruction();\n\n        // For a indirect read transaction, the transaction begins when the instruction/address is set\n        T::REGS\n            .cr()\n            .modify(|v| v.set_fmode(FunctionalMode::IndirectRead.into()));\n        if T::REGS.ccr().read().admode() == HspiWidth::NONE.into() {\n            T::REGS.ir().write(|v| v.set_instruction(current_instruction));\n        } else {\n            T::REGS.ar().write(|v| v.set_address(current_address));\n        }\n\n        for chunk in buf.chunks_mut(0xFFFF / W::size().bytes()) {\n            let transfer = unsafe {\n                self.dma\n                    .as_mut()\n                    .unwrap()\n                    .read(T::REGS.dr().as_ptr() as *mut W, chunk, Default::default())\n            };\n\n            T::REGS.cr().modify(|w| w.set_dmaen(true));\n\n            transfer.await;\n        }\n\n        finish_dma(T::REGS);\n\n        Ok(())\n    }\n\n    /// Asynchronous write to external device\n    pub async fn write<W: Word>(&mut self, buf: &[W], transaction: TransferConfig) -> Result<(), HspiError> {\n        if buf.is_empty() {\n            return Err(HspiError::EmptyBuffer);\n        }\n\n        // Wait for peripheral to be free\n        while T::REGS.sr().read().busy() {}\n\n        let transfer_size_bytes = buf.len() * W::size().bytes();\n        self.configure_command(&transaction, Some(transfer_size_bytes))?;\n        T::REGS\n            .cr()\n            .modify(|v| v.set_fmode(FunctionalMode::IndirectWrite.into()));\n\n        // TODO: implement this using a LinkedList DMA to offload the whole transfer off the CPU.\n        for chunk in buf.chunks(0xFFFF / W::size().bytes()) {\n            let transfer = unsafe {\n                self.dma\n                    .as_mut()\n                    .unwrap()\n                    .write(chunk, T::REGS.dr().as_ptr() as *mut W, Default::default())\n            };\n\n            T::REGS.cr().modify(|w| w.set_dmaen(true));\n\n            transfer.await;\n        }\n\n        finish_dma(T::REGS);\n\n        Ok(())\n    }\n}\n\nimpl<'d, T: Instance, M: PeriMode> Drop for Hspi<'d, T, M> {\n    fn drop(&mut self) {\n        rcc::disable::<T>();\n    }\n}\n\nfn finish_dma(regs: Regs) {\n    while !regs.sr().read().tcf() {}\n    regs.fcr().write(|v| v.set_ctcf(true));\n\n    regs.cr().modify(|w| {\n        w.set_dmaen(false);\n    });\n}\n\n/// HSPI instance trait.\npub(crate) trait SealedInstance {\n    const REGS: Regs;\n}\n\n/// HSPI instance trait.\n#[allow(private_bounds)]\npub trait Instance: SealedInstance + PeripheralType + RccPeripheral {}\n\npin_trait!(SckPin, Instance);\npin_trait!(NckPin, Instance);\npin_trait!(D0Pin, Instance);\npin_trait!(D1Pin, Instance);\npin_trait!(D2Pin, Instance);\npin_trait!(D3Pin, Instance);\npin_trait!(D4Pin, Instance);\npin_trait!(D5Pin, Instance);\npin_trait!(D6Pin, Instance);\npin_trait!(D7Pin, Instance);\npin_trait!(D8Pin, Instance);\npin_trait!(D9Pin, Instance);\npin_trait!(D10Pin, Instance);\npin_trait!(D11Pin, Instance);\npin_trait!(D12Pin, Instance);\npin_trait!(D13Pin, Instance);\npin_trait!(D14Pin, Instance);\npin_trait!(D15Pin, Instance);\npin_trait!(DQS0Pin, Instance);\npin_trait!(DQS1Pin, Instance);\npin_trait!(NSSPin, Instance);\ndma_trait!(HspiDma, Instance);\n\nforeach_peripheral!(\n    (hspi, $inst:ident) => {\n        impl SealedInstance for peripherals::$inst {\n            const REGS: Regs = crate::pac::$inst;\n        }\n\n        impl Instance for peripherals::$inst {}\n    };\n);\n\nimpl<'d, T: Instance, M: PeriMode> SetConfig for Hspi<'d, T, M> {\n    type Config = Config;\n    type ConfigError = ();\n    fn set_config(&mut self, config: &Self::Config) -> Result<(), ()> {\n        self.set_config(config);\n        Ok(())\n    }\n}\n\nimpl<'d, T: Instance, M: PeriMode> GetConfig for Hspi<'d, T, M> {\n    type Config = Config;\n    fn get_config(&self) -> Self::Config {\n        self.get_config()\n    }\n}\n\n/// Word sizes usable for HSPI.\n#[allow(private_bounds)]\npub trait Word: word::Word {}\n\nmacro_rules! impl_word {\n    ($T:ty) => {\n        impl Word for $T {}\n    };\n}\n\nimpl_word!(u8);\nimpl_word!(u16);\nimpl_word!(u32);\n"
  },
  {
    "path": "embassy-stm32/src/i2c/config.rs",
    "content": "#[cfg(gpio_v2)]\nuse crate::gpio::Pull;\nuse crate::gpio::{AfType, OutputType, Speed};\nuse crate::time::Hertz;\n\n#[repr(u8)]\n#[derive(Debug, Copy, Clone)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\n/// Bits of the I2C OA2 register to mask out.\npub enum AddrMask {\n    /// No mask\n    NOMASK,\n    /// OA2\\[1\\] is masked and don’t care. Only OA2\\[7:2\\] are compared.\n    MASK1,\n    /// OA2\\[2:1\\] are masked and don’t care. Only OA2\\[7:3\\] are compared.\n    MASK2,\n    /// OA2\\[3:1\\] are masked and don’t care. Only OA2\\[7:4\\] are compared.\n    MASK3,\n    /// OA2\\[4:1\\] are masked and don’t care. Only OA2\\[7:5\\] are compared.\n    MASK4,\n    /// OA2\\[5:1\\] are masked and don’t care. Only OA2\\[7:6\\] are compared.\n    MASK5,\n    /// OA2\\[6:1\\] are masked and don’t care. Only OA2\\[7:6\\] are compared.\n    MASK6,\n    /// OA2\\[7:1\\] are masked and don’t care. No comparison is done, and all (except reserved) 7-bit received addresses are acknowledged\n    MASK7,\n}\n\n#[derive(Debug, Copy, Clone, PartialEq, Eq)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\n/// An I2C address. Either 7 or 10 bit.\npub enum Address {\n    /// A 7 bit address\n    SevenBit(u8),\n    /// A 10 bit address.\n    ///\n    /// When using an address to configure the Own Address, only the OA1 register can be set to a 10-bit address.\n    TenBit(u16),\n}\nimpl From<u8> for Address {\n    fn from(value: u8) -> Self {\n        Address::SevenBit(value)\n    }\n}\nimpl From<u16> for Address {\n    fn from(value: u16) -> Self {\n        assert!(value < 0x400, \"Ten bit address must be less than 0x400\");\n        Address::TenBit(value)\n    }\n}\nimpl Address {\n    /// Get the inner address as a u16.\n    ///\n    /// For 7 bit addresses, the u8 that was used to store the address is returned as a u16.\n    pub fn addr(&self) -> u16 {\n        match self {\n            Address::SevenBit(addr) => *addr as u16,\n            Address::TenBit(addr) => *addr,\n        }\n    }\n}\n\n#[derive(Debug, Copy, Clone)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\n/// The second Own Address register.\npub struct OA2 {\n    /// The address.\n    pub addr: u8,\n    /// The bit mask that will affect how the own address 2 register is compared.\n    pub mask: AddrMask,\n}\n\n#[derive(Debug, Copy, Clone)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\n/// The Own Address(es) of the I2C peripheral.\npub enum OwnAddresses {\n    /// Configuration for only the OA1 register.\n    OA1(Address),\n    /// Configuration for only the OA2 register.\n    OA2(OA2),\n    /// Configuration for both the OA1 and OA2 registers.\n    Both {\n        /// The [Address] for the OA1 register.\n        oa1: Address,\n        /// The [OA2] configuration.\n        oa2: OA2,\n    },\n}\n\n/// Slave Configuration\n#[derive(Debug, Copy, Clone)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub struct SlaveAddrConfig {\n    /// Target Address(es)\n    pub addr: OwnAddresses,\n    /// Control if the peripheral should respond to the general call address\n    pub general_call: bool,\n}\nimpl SlaveAddrConfig {\n    /// Create a new slave address configuration with only the OA1 register set in 7 bit mode and the general call disabled.\n    pub fn basic(addr: u8) -> Self {\n        Self {\n            addr: OwnAddresses::OA1(Address::SevenBit(addr)),\n            general_call: false,\n        }\n    }\n}\n\n/// I2C config\n#[non_exhaustive]\n#[derive(Copy, Clone)]\npub struct Config {\n    /// Frequency\n    pub frequency: Hertz,\n    /// GPIO Speed\n    pub gpio_speed: Speed,\n    /// Enable internal pullup on SDA.\n    ///\n    /// Using external pullup resistors is recommended for I2C. If you do\n    /// have external pullups you should not enable this.\n    #[cfg(gpio_v2)]\n    pub sda_pullup: bool,\n    /// Enable internal pullup on SCL.\n    ///\n    /// Using external pullup resistors is recommended for I2C. If you do\n    /// have external pullups you should not enable this.\n    #[cfg(gpio_v2)]\n    pub scl_pullup: bool,\n    /// Timeout.\n    #[cfg(feature = \"time\")]\n    pub timeout: embassy_time::Duration,\n}\n\nimpl Default for Config {\n    fn default() -> Self {\n        Self {\n            frequency: Hertz::khz(100),\n            gpio_speed: Speed::Medium,\n            #[cfg(gpio_v2)]\n            sda_pullup: false,\n            #[cfg(gpio_v2)]\n            scl_pullup: false,\n            #[cfg(feature = \"time\")]\n            timeout: embassy_time::Duration::from_millis(1000),\n        }\n    }\n}\n\nimpl Config {\n    pub(super) fn scl_af(&self) -> AfType {\n        #[cfg(gpio_v1)]\n        return AfType::output(OutputType::OpenDrain, self.gpio_speed);\n        #[cfg(gpio_v2)]\n        return AfType::output_pull(\n            OutputType::OpenDrain,\n            self.gpio_speed,\n            match self.scl_pullup {\n                true => Pull::Up,\n                false => Pull::None,\n            },\n        );\n    }\n\n    pub(super) fn sda_af(&self) -> AfType {\n        #[cfg(gpio_v1)]\n        return AfType::output(OutputType::OpenDrain, self.gpio_speed);\n        #[cfg(gpio_v2)]\n        return AfType::output_pull(\n            OutputType::OpenDrain,\n            self.gpio_speed,\n            match self.sda_pullup {\n                true => Pull::Up,\n                false => Pull::None,\n            },\n        );\n    }\n}\n"
  },
  {
    "path": "embassy-stm32/src/i2c/mod.rs",
    "content": "//! Inter-Integrated-Circuit (I2C)\n#![macro_use]\n\n#[cfg_attr(i2c_v1, path = \"v1.rs\")]\n#[cfg_attr(any(i2c_v2, i2c_v3), path = \"v2.rs\")]\nmod _version;\n\nmod config;\n\nuse core::future::Future;\nuse core::iter;\nuse core::marker::PhantomData;\n\npub use config::*;\nuse embassy_hal_internal::Peri;\nuse embassy_sync::waitqueue::AtomicWaker;\n#[cfg(feature = \"time\")]\nuse embassy_time::{Duration, Instant};\nuse mode::MasterMode;\npub use mode::{Master, MultiMaster};\n\nuse crate::dma::ChannelAndRequest;\nuse crate::gpio::Flex;\nuse crate::interrupt::typelevel::Interrupt;\nuse crate::mode::{Async, Blocking, Mode};\nuse crate::rcc::{RccInfo, SealedRccPeripheral};\nuse crate::time::Hertz;\nuse crate::{interrupt, peripherals};\n\n/// I2C error.\n#[derive(Debug, PartialEq, Eq, Copy, Clone)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub enum Error {\n    /// Bus error\n    Bus,\n    /// Arbitration lost\n    Arbitration,\n    /// ACK not received (either to the address or to a data byte)\n    Nack,\n    /// Timeout\n    Timeout,\n    /// CRC error\n    Crc,\n    /// Overrun error\n    Overrun,\n    /// Zero-length transfers are not allowed.\n    ZeroLengthTransfer,\n}\n\nimpl core::fmt::Display for Error {\n    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {\n        let message = match self {\n            Self::Bus => \"Bus Error\",\n            Self::Arbitration => \"Arbitration Lost\",\n            Self::Nack => \"ACK Not Received\",\n            Self::Timeout => \"Request Timed Out\",\n            Self::Crc => \"CRC Mismatch\",\n            Self::Overrun => \"Buffer Overrun\",\n            Self::ZeroLengthTransfer => \"Zero-Length Transfers are not allowed\",\n        };\n\n        write!(f, \"{}\", message)\n    }\n}\n\nimpl core::error::Error for Error {}\n\n/// I2C modes\npub mod mode {\n    trait SealedMode {}\n\n    /// Trait for I2C master operations.\n    #[allow(private_bounds)]\n    pub trait MasterMode: SealedMode {}\n\n    /// Mode allowing for I2C master operations.\n    pub struct Master;\n    /// Mode allowing for I2C master and slave operations.\n    pub struct MultiMaster;\n\n    impl SealedMode for Master {}\n    impl MasterMode for Master {}\n\n    impl SealedMode for MultiMaster {}\n    impl MasterMode for MultiMaster {}\n}\n\n#[derive(Debug, Clone, PartialEq, Eq)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\n/// The command kind to the slave from the master\npub enum SlaveCommandKind {\n    /// Write to the slave\n    Write,\n    /// Read from the slave\n    Read,\n}\n\n#[derive(Debug, Clone, PartialEq, Eq)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\n/// The command kind to the slave from the master and the address that the slave matched\npub struct SlaveCommand {\n    /// The kind of command\n    pub kind: SlaveCommandKind,\n    /// The address that the slave matched\n    pub address: Address,\n}\n\n#[derive(Debug, Clone, PartialEq, Eq)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\n/// The status of the slave send operation\npub enum SendStatus {\n    /// The slave send operation is done, all bytes have been sent and the master is not requesting more\n    Done,\n    /// The slave send operation is done, but there are leftover bytes that the master did not read\n    LeftoverBytes(usize),\n}\n\nstruct I2CDropGuard<'d> {\n    info: &'static Info,\n    _scl: Option<Flex<'d>>,\n    _sda: Option<Flex<'d>>,\n}\nimpl<'d> Drop for I2CDropGuard<'d> {\n    fn drop(&mut self) {\n        self.info.rcc.disable_without_stop();\n    }\n}\n\n/// I2C driver.\npub struct I2c<'d, M: Mode, IM: MasterMode> {\n    info: &'static Info,\n    state: &'static State,\n    kernel_clock: Hertz,\n    tx_dma: Option<ChannelAndRequest<'d>>,\n    rx_dma: Option<ChannelAndRequest<'d>>,\n    #[cfg(feature = \"time\")]\n    timeout: Duration,\n    _phantom: PhantomData<M>,\n    _phantom2: PhantomData<IM>,\n    _drop_guard: I2CDropGuard<'d>,\n}\n\nimpl<'d> I2c<'d, Async, Master> {\n    /// Create a new I2C driver.\n    pub fn new<T: Instance, D1: TxDma<T>, D2: RxDma<T>, #[cfg(afio)] A>(\n        peri: Peri<'d, T>,\n        scl: Peri<'d, if_afio!(impl SclPin<T, A>)>,\n        sda: Peri<'d, if_afio!(impl SdaPin<T, A>)>,\n        tx_dma: Peri<'d, D1>,\n        rx_dma: Peri<'d, D2>,\n        _irq: impl interrupt::typelevel::Binding<T::EventInterrupt, EventInterruptHandler<T>>\n        + interrupt::typelevel::Binding<T::ErrorInterrupt, ErrorInterruptHandler<T>>\n        + interrupt::typelevel::Binding<D1::Interrupt, crate::dma::InterruptHandler<D1>>\n        + interrupt::typelevel::Binding<D2::Interrupt, crate::dma::InterruptHandler<D2>>\n        + 'd,\n        config: Config,\n    ) -> Self {\n        Self::new_inner(\n            peri,\n            new_pin!(scl, config.scl_af()),\n            new_pin!(sda, config.sda_af()),\n            new_dma!(tx_dma, _irq),\n            new_dma!(rx_dma, _irq),\n            config,\n        )\n    }\n}\n\nimpl<'d> I2c<'d, Blocking, Master> {\n    /// Create a new blocking I2C driver.\n    pub fn new_blocking<T: Instance, #[cfg(afio)] A>(\n        peri: Peri<'d, T>,\n        scl: Peri<'d, if_afio!(impl SclPin<T, A>)>,\n        sda: Peri<'d, if_afio!(impl SdaPin<T, A>)>,\n        config: Config,\n    ) -> Self {\n        Self::new_inner(\n            peri,\n            new_pin!(scl, config.scl_af()),\n            new_pin!(sda, config.sda_af()),\n            None,\n            None,\n            config,\n        )\n    }\n}\n\nimpl<'d, M: Mode> I2c<'d, M, Master> {\n    /// Create a new I2C driver.\n    fn new_inner<T: Instance>(\n        _peri: Peri<'d, T>,\n        _scl: Option<Flex<'d>>,\n        _sda: Option<Flex<'d>>,\n        tx_dma: Option<ChannelAndRequest<'d>>,\n        rx_dma: Option<ChannelAndRequest<'d>>,\n        config: Config,\n    ) -> Self {\n        unsafe { T::EventInterrupt::enable() };\n        unsafe { T::ErrorInterrupt::enable() };\n\n        let mut this = Self {\n            info: T::info(),\n            state: T::state(),\n            kernel_clock: T::frequency(),\n            tx_dma,\n            rx_dma,\n            #[cfg(feature = \"time\")]\n            timeout: config.timeout,\n            _phantom: PhantomData,\n            _phantom2: PhantomData,\n            _drop_guard: I2CDropGuard {\n                info: T::info(),\n                _scl,\n                _sda,\n            },\n        };\n\n        this.enable_and_init(config);\n\n        this\n    }\n\n    fn enable_and_init(&mut self, config: Config) {\n        self.info.rcc.enable_and_reset_without_stop();\n        self.init(config);\n    }\n}\n\nimpl<'d, M: Mode, IM: MasterMode> I2c<'d, M, IM> {\n    fn timeout(&self) -> Timeout {\n        Timeout {\n            #[cfg(feature = \"time\")]\n            deadline: Instant::now() + self.timeout,\n        }\n    }\n}\n\n#[derive(Copy, Clone)]\nstruct Timeout {\n    #[cfg(feature = \"time\")]\n    deadline: Instant,\n}\n\n#[allow(dead_code)]\nimpl Timeout {\n    #[inline]\n    fn check(self) -> Result<(), Error> {\n        #[cfg(feature = \"time\")]\n        if Instant::now() > self.deadline {\n            return Err(Error::Timeout);\n        }\n\n        Ok(())\n    }\n\n    #[inline]\n    fn with<R>(self, fut: impl Future<Output = Result<R, Error>>) -> impl Future<Output = Result<R, Error>> {\n        #[cfg(feature = \"time\")]\n        {\n            use futures_util::FutureExt;\n\n            embassy_futures::select::select(embassy_time::Timer::at(self.deadline), fut).map(|r| match r {\n                embassy_futures::select::Either::First(_) => Err(Error::Timeout),\n                embassy_futures::select::Either::Second(r) => r,\n            })\n        }\n\n        #[cfg(not(feature = \"time\"))]\n        fut\n    }\n}\n\nstruct State {\n    #[allow(unused)]\n    waker: AtomicWaker,\n}\n\nimpl State {\n    const fn new() -> Self {\n        Self {\n            waker: AtomicWaker::new(),\n        }\n    }\n}\n\nstruct Info {\n    regs: crate::pac::i2c::I2c,\n    rcc: RccInfo,\n}\n\nperi_trait!(\n    irqs: [EventInterrupt, ErrorInterrupt],\n);\n\npin_trait!(SclPin, Instance, @A);\npin_trait!(SdaPin, Instance, @A);\ndma_trait!(RxDma, Instance);\ndma_trait!(TxDma, Instance);\n\n/// Event interrupt handler.\npub struct EventInterruptHandler<T: Instance> {\n    _phantom: PhantomData<T>,\n}\n\nimpl<T: Instance> interrupt::typelevel::Handler<T::EventInterrupt> for EventInterruptHandler<T> {\n    unsafe fn on_interrupt() {\n        _version::on_interrupt::<T>()\n    }\n}\n\n/// Error interrupt handler.\npub struct ErrorInterruptHandler<T: Instance> {\n    _phantom: PhantomData<T>,\n}\n\nimpl<T: Instance> interrupt::typelevel::Handler<T::ErrorInterrupt> for ErrorInterruptHandler<T> {\n    unsafe fn on_interrupt() {\n        _version::on_interrupt::<T>()\n    }\n}\n\nforeach_peripheral!(\n    (i2c, $inst:ident) => {\n        #[allow(private_interfaces)]\n        impl SealedInstance for peripherals::$inst {\n            fn info() -> &'static Info {\n                static INFO: Info = Info{\n                    regs: crate::pac::$inst,\n                    rcc: crate::peripherals::$inst::RCC_INFO,\n                };\n                &INFO\n            }\n            fn state() -> &'static State {\n                static STATE: State = State::new();\n                &STATE\n            }\n        }\n\n        impl Instance for peripherals::$inst {\n            type EventInterrupt = crate::_generated::peripheral_interrupts::$inst::EV;\n            type ErrorInterrupt = crate::_generated::peripheral_interrupts::$inst::ER;\n        }\n    };\n);\n\nimpl<'d, M: Mode, IM: MasterMode> embedded_hal_02::blocking::i2c::Read for I2c<'d, M, IM> {\n    type Error = Error;\n\n    fn read(&mut self, address: u8, buffer: &mut [u8]) -> Result<(), Self::Error> {\n        self.blocking_read(address, buffer)\n    }\n}\n\nimpl<'d, M: Mode, IM: MasterMode> embedded_hal_02::blocking::i2c::Write for I2c<'d, M, IM> {\n    type Error = Error;\n\n    fn write(&mut self, address: u8, write: &[u8]) -> Result<(), Self::Error> {\n        self.blocking_write(address, write)\n    }\n}\n\nimpl<'d, M: Mode, IM: MasterMode> embedded_hal_02::blocking::i2c::WriteRead for I2c<'d, M, IM> {\n    type Error = Error;\n\n    fn write_read(&mut self, address: u8, write: &[u8], read: &mut [u8]) -> Result<(), Self::Error> {\n        self.blocking_write_read(address, write, read)\n    }\n}\n\nimpl embedded_hal_1::i2c::Error for Error {\n    fn kind(&self) -> embedded_hal_1::i2c::ErrorKind {\n        match *self {\n            Self::Bus => embedded_hal_1::i2c::ErrorKind::Bus,\n            Self::Arbitration => embedded_hal_1::i2c::ErrorKind::ArbitrationLoss,\n            Self::Nack => {\n                embedded_hal_1::i2c::ErrorKind::NoAcknowledge(embedded_hal_1::i2c::NoAcknowledgeSource::Unknown)\n            }\n            Self::Timeout => embedded_hal_1::i2c::ErrorKind::Other,\n            Self::Crc => embedded_hal_1::i2c::ErrorKind::Other,\n            Self::Overrun => embedded_hal_1::i2c::ErrorKind::Overrun,\n            Self::ZeroLengthTransfer => embedded_hal_1::i2c::ErrorKind::Other,\n        }\n    }\n}\n\nimpl<'d, M: Mode, IM: MasterMode> embedded_hal_1::i2c::ErrorType for I2c<'d, M, IM> {\n    type Error = Error;\n}\n\nimpl<'d, M: Mode, IM: MasterMode> embedded_hal_1::i2c::I2c for I2c<'d, M, IM> {\n    fn read(&mut self, address: u8, read: &mut [u8]) -> Result<(), Self::Error> {\n        self.blocking_read(address, read)\n    }\n\n    fn write(&mut self, address: u8, write: &[u8]) -> Result<(), Self::Error> {\n        self.blocking_write(address, write)\n    }\n\n    fn write_read(&mut self, address: u8, write: &[u8], read: &mut [u8]) -> Result<(), Self::Error> {\n        self.blocking_write_read(address, write, read)\n    }\n\n    fn transaction(\n        &mut self,\n        address: u8,\n        operations: &mut [embedded_hal_1::i2c::Operation<'_>],\n    ) -> Result<(), Self::Error> {\n        self.blocking_transaction(address, operations)\n    }\n}\n\nimpl<'d, IM: MasterMode> embedded_hal_async::i2c::I2c for I2c<'d, Async, IM> {\n    async fn read(&mut self, address: u8, read: &mut [u8]) -> Result<(), Self::Error> {\n        self.read(address, read).await\n    }\n\n    async fn write(&mut self, address: u8, write: &[u8]) -> Result<(), Self::Error> {\n        self.write(address, write).await\n    }\n\n    async fn write_read(&mut self, address: u8, write: &[u8], read: &mut [u8]) -> Result<(), Self::Error> {\n        self.write_read(address, write, read).await\n    }\n\n    async fn transaction(\n        &mut self,\n        address: u8,\n        operations: &mut [embedded_hal_1::i2c::Operation<'_>],\n    ) -> Result<(), Self::Error> {\n        self.transaction(address, operations).await\n    }\n}\n\n/// Frame type in I2C transaction.\n///\n/// This tells each method what kind of frame to use, to generate a (repeated) start condition (ST\n/// or SR), and/or a stop condition (SP). For read operations, this also controls whether to send an\n/// ACK or NACK after the last byte received.\n///\n/// For write operations, the following options are identical because they differ only in the (N)ACK\n/// treatment relevant for read operations:\n///\n/// - `FirstFrame` and `FirstAndNextFrame` behave identically for writes\n/// - `NextFrame` and `LastFrameNoStop` behave identically for writes\n///\n/// Abbreviations used below:\n///\n/// - `ST` = start condition\n/// - `SR` = repeated start condition\n/// - `SP` = stop condition\n/// - `ACK`/`NACK` = last byte in read operation\n#[derive(Copy, Clone)]\n#[allow(dead_code)]\nenum FrameOptions {\n    /// `[ST/SR]+[NACK]+[SP]` First frame (of this type) in transaction and also last frame overall.\n    FirstAndLastFrame,\n    /// `[ST/SR]+[NACK]` First frame of this type in transaction, last frame in a read operation but\n    /// not the last frame overall.\n    FirstFrame,\n    /// `[ST/SR]+[ACK]` First frame of this type in transaction, neither last frame overall nor last\n    /// frame in a read operation.\n    FirstAndNextFrame,\n    /// `[ACK]` Middle frame in a read operation (neither first nor last).\n    NextFrame,\n    /// `[NACK]+[SP]` Last frame overall in this transaction but not the first frame.\n    LastFrame,\n    /// `[NACK]` Last frame in a read operation but not last frame overall in this transaction.\n    LastFrameNoStop,\n}\n\n#[allow(dead_code)]\nimpl FrameOptions {\n    /// Returns true if a start or repeated start condition should be generated before this operation.\n    fn send_start(self) -> bool {\n        match self {\n            Self::FirstAndLastFrame | Self::FirstFrame | Self::FirstAndNextFrame => true,\n            Self::NextFrame | Self::LastFrame | Self::LastFrameNoStop => false,\n        }\n    }\n\n    /// Returns true if a stop condition should be generated after this operation.\n    fn send_stop(self) -> bool {\n        match self {\n            Self::FirstAndLastFrame | Self::LastFrame => true,\n            Self::FirstFrame | Self::FirstAndNextFrame | Self::NextFrame | Self::LastFrameNoStop => false,\n        }\n    }\n\n    /// Returns true if NACK should be sent after the last byte received in a read operation.\n    ///\n    /// This signals the end of a read sequence and releases the bus for the master's\n    /// next transmission (or stop condition).\n    fn send_nack(self) -> bool {\n        match self {\n            Self::FirstAndLastFrame | Self::FirstFrame | Self::LastFrame | Self::LastFrameNoStop => true,\n            Self::FirstAndNextFrame | Self::NextFrame => false,\n        }\n    }\n}\n\n/// Analyzes I2C transaction operations and assigns appropriate frame to each.\n///\n/// This function processes a sequence of I2C operations and determines the correct\n/// frame configuration for each operation to ensure proper I2C protocol compliance.\n/// It handles the complex logic of:\n///\n/// - Generating start conditions for the first operation of each type (read/write)\n/// - Generating stop conditions for the final operation in the entire transaction\n/// - Managing ACK/NACK behavior for read operations, including merging consecutive reads\n/// - Ensuring proper bus handoff between different operation types\n///\n/// **Transaction Contract Compliance:**\n/// The frame assignments ensure compliance with the embedded-hal I2C transaction contract,\n/// where consecutive operations of the same type are logically merged while maintaining\n/// proper protocol boundaries.\n///\n/// **Error Handling:**\n/// Returns an error if any read operation has an empty buffer, as this would create\n/// an invalid I2C transaction that could halt mid-execution.\n///\n/// # Arguments\n/// * `operations` - Mutable slice of I2C operations from embedded-hal\n///\n/// # Returns\n/// An iterator over (operation, frame) pairs, or an error if the transaction is invalid\n///\n#[allow(dead_code)]\nfn operation_frames<'a, 'b: 'a>(\n    operations: &'a mut [embedded_hal_1::i2c::Operation<'b>],\n) -> Result<impl IntoIterator<Item = (&'a mut embedded_hal_1::i2c::Operation<'b>, FrameOptions)>, Error> {\n    use embedded_hal_1::i2c::Operation::{Read, Write};\n\n    // Validate that no read operations have empty buffers before starting the transaction.\n    // Empty read operations would risk halting with an error mid-transaction.\n    //\n    // Note: We could theoretically allow empty read operations within consecutive read\n    // sequences as long as the final merged read has at least one byte, but this would\n    // complicate the logic significantly and create error-prone edge cases.\n    if operations.iter().any(|op| match op {\n        Read(read) => read.is_empty(),\n        Write(_) => false,\n    }) {\n        return Err(Error::Overrun);\n    }\n\n    let mut operations = operations.iter_mut().peekable();\n    let mut next_first_operation = true;\n\n    Ok(iter::from_fn(move || {\n        let current_op = operations.next()?;\n\n        // Determine if this is the first operation of its type (read or write)\n        let is_first_of_type = next_first_operation;\n        let next_op = operations.peek();\n\n        // Compute the appropriate frame based on three key properties:\n        //\n        // 1. **Start Condition**: Generate (repeated) start for first operation of each type\n        // 2. **Stop Condition**: Generate stop for the final operation in the entire transaction\n        // 3. **ACK/NACK for Reads**: For read operations, send ACK if more reads follow in the\n        //    sequence, or NACK for the final read in a sequence (before write or transaction end)\n        //\n        // The third property is checked for all operations since the resulting frame\n        // configurations are identical for write operations regardless of ACK/NACK treatment.\n        let frame = match (is_first_of_type, next_op) {\n            // First operation of type, and it's also the final operation overall\n            (true, None) => FrameOptions::FirstAndLastFrame,\n            // First operation of type, next operation is also a read (continue read sequence)\n            (true, Some(Read(_))) => FrameOptions::FirstAndNextFrame,\n            // First operation of type, next operation is write (end current sequence)\n            (true, Some(Write(_))) => FrameOptions::FirstFrame,\n\n            // Continuation operation, and it's the final operation overall\n            (false, None) => FrameOptions::LastFrame,\n            // Continuation operation, next operation is also a read (continue read sequence)\n            (false, Some(Read(_))) => FrameOptions::NextFrame,\n            // Continuation operation, next operation is write (end current sequence, no stop)\n            (false, Some(Write(_))) => FrameOptions::LastFrameNoStop,\n        };\n\n        // Pre-calculate whether the next operation will be the first of its type.\n        // This is done here because we consume `current_op` as the iterator value\n        // and cannot access it in the next iteration.\n        next_first_operation = match (&current_op, next_op) {\n            // No next operation\n            (_, None) => false,\n            // Operation type changes: next will be first of its type\n            (Read(_), Some(Write(_))) | (Write(_), Some(Read(_))) => true,\n            // Operation type continues: next will not be first of its type\n            (Read(_), Some(Read(_))) | (Write(_), Some(Write(_))) => false,\n        };\n\n        Some((current_op, frame))\n    }))\n}\n"
  },
  {
    "path": "embassy-stm32/src/i2c/v1.rs",
    "content": "//! # I2Cv1\n//!\n//! This implementation is used for STM32F1, STM32F2, STM32F4, and STM32L1 devices.\n//!\n//! All other devices (as of 2023-12-28) use [`v2`](super::v2) instead.\n\nuse core::future::poll_fn;\nuse core::task::Poll;\n\nuse embassy_embedded_hal::SetConfig;\nuse embassy_futures::select::{Either, select};\nuse embassy_hal_internal::drop::OnDrop;\nuse embedded_hal_1::i2c::Operation;\nuse mode::Master;\n\nuse super::*;\nuse crate::mode::Mode as PeriMode;\nuse crate::pac::i2c;\n\n// /!\\                      /!\\\n// /!\\ Implementation note! /!\\\n// /!\\                      /!\\\n//\n// It's somewhat unclear whether using interrupts here in a *strictly* one-shot style is actually\n// what we want! If you are looking in this file because you are doing async I2C and your code is\n// just totally hanging (sometimes), maybe swing by this issue:\n// <https://github.com/embassy-rs/embassy/issues/2372>.\n//\n// There's some more details there, and we might have a fix for you. But please let us know if you\n// hit a case like this!\npub unsafe fn on_interrupt<T: Instance>() {\n    let regs = T::info().regs;\n    trace!(\"I2C interrupt triggered\");\n    // i2c v2 only woke the task on transfer complete interrupts. v1 uses interrupts for a bunch of\n    // other stuff, so we wake the task on every interrupt.\n    T::state().waker.wake();\n    critical_section::with(|_| {\n        // Clear event interrupt flag.\n        regs.cr2().modify(|w| {\n            w.set_itevten(false);\n            w.set_iterren(false);\n        });\n    });\n}\n\nimpl<'d, M: PeriMode, IM: MasterMode> I2c<'d, M, IM> {\n    pub(crate) fn init(&mut self, config: Config) {\n        self.info.regs.cr1().modify(|reg| {\n            reg.set_pe(false);\n            //reg.set_anfoff(false);\n        });\n\n        // Errata: \"Start cannot be generated after a misplaced Stop\"\n        //\n        // > If a master generates a misplaced Stop on the bus (bus error)\n        // > while the microcontroller I2C peripheral attempts to switch to\n        // > Master mode by setting the START bit, the Start condition is\n        // > not properly generated.\n        //\n        // This also can occur with falsely detected STOP events, for example\n        // if the SDA line is shorted to low.\n        //\n        // The workaround for this is to trigger the SWRST line AFTER power is\n        // enabled, AFTER PE is disabled and BEFORE making any other configuration.\n        //\n        // It COULD be possible to apply this workaround at runtime, instead of\n        // only on initialization, however this would require detecting the timeout\n        // or BUSY lockup condition, and re-configuring the peripheral after reset.\n        //\n        // This presents as an ~infinite hang on read or write, as the START condition\n        // is never generated, meaning the start event is never generated.\n        self.info.regs.cr1().modify(|reg| {\n            reg.set_swrst(true);\n        });\n        self.info.regs.cr1().modify(|reg| {\n            reg.set_swrst(false);\n        });\n\n        let timings = Timings::new(self.kernel_clock, config.frequency);\n\n        self.info.regs.cr2().modify(|reg| {\n            reg.set_freq(timings.freq);\n        });\n        self.info.regs.ccr().modify(|reg| {\n            reg.set_f_s(timings.mode.f_s());\n            reg.set_duty(timings.duty.duty());\n            reg.set_ccr(timings.ccr);\n        });\n        self.info.regs.trise().modify(|reg| {\n            reg.set_trise(timings.trise);\n        });\n\n        self.info.regs.cr1().modify(|reg| {\n            reg.set_pe(true);\n        });\n        trace!(\"i2c v1 init complete\");\n    }\n\n    fn check_and_clear_error_flags(info: &'static Info) -> Result<i2c::regs::Sr1, Error> {\n        // Note that flags should only be cleared once they have been registered. If flags are\n        // cleared otherwise, there may be an inherent race condition and flags may be missed.\n        let sr1 = info.regs.sr1().read();\n\n        if sr1.timeout() {\n            info.regs.sr1().write(|reg| {\n                reg.0 = !0;\n                reg.set_timeout(false);\n            });\n            return Err(Error::Timeout);\n        }\n\n        if sr1.pecerr() {\n            info.regs.sr1().write(|reg| {\n                reg.0 = !0;\n                reg.set_pecerr(false);\n            });\n            return Err(Error::Crc);\n        }\n\n        if sr1.ovr() {\n            info.regs.sr1().write(|reg| {\n                reg.0 = !0;\n                reg.set_ovr(false);\n            });\n            return Err(Error::Overrun);\n        }\n\n        if sr1.af() {\n            info.regs.sr1().write(|reg| {\n                reg.0 = !0;\n                reg.set_af(false);\n            });\n            return Err(Error::Nack);\n        }\n\n        if sr1.arlo() {\n            info.regs.sr1().write(|reg| {\n                reg.0 = !0;\n                reg.set_arlo(false);\n            });\n            return Err(Error::Arbitration);\n        }\n\n        // The errata indicates that BERR may be incorrectly detected. It recommends ignoring and\n        // clearing the BERR bit instead.\n        if sr1.berr() {\n            info.regs.sr1().write(|reg| {\n                reg.0 = !0;\n                reg.set_berr(false);\n            });\n        }\n\n        Ok(sr1)\n    }\n\n    fn write_bytes(\n        &mut self,\n        address: u8,\n        write_buffer: &[u8],\n        timeout: Timeout,\n        frame: FrameOptions,\n    ) -> Result<(), Error> {\n        if frame.send_start() {\n            // Send a START condition\n\n            self.info.regs.cr1().modify(|reg| {\n                reg.set_start(true);\n            });\n\n            // Wait until START condition was generated\n            while !Self::check_and_clear_error_flags(self.info)?.start() {\n                timeout.check()?;\n            }\n\n            // Check if we were the ones to generate START\n            if self.info.regs.cr1().read().start() || !self.info.regs.sr2().read().msl() {\n                return Err(Error::Arbitration);\n            }\n\n            // Set up current address we're trying to talk to\n            self.info.regs.dr().write(|reg| reg.set_dr(address << 1));\n\n            // Wait until address was sent\n            // Wait for the address to be acknowledged\n            // Check for any I2C errors. If a NACK occurs, the ADDR bit will never be set.\n            while !Self::check_and_clear_error_flags(self.info)?.addr() {\n                timeout.check()?;\n            }\n\n            // Clear condition by reading SR2\n            let _ = self.info.regs.sr2().read();\n        }\n\n        // Send bytes\n        for c in write_buffer {\n            self.send_byte(*c, timeout)?;\n        }\n\n        // Wait for the last byte to finish transmitting. This is essential even when not sending\n        // STOP - if we're about to send a repeated START (for write_read), we must wait for the\n        // last byte to finish transmitting, otherwise the repeated START will interrupt the\n        // ongoing byte transmission and corrupt the transfer.\n        while !Self::check_and_clear_error_flags(self.info)?.btf() {\n            timeout.check()?;\n        }\n\n        if frame.send_stop() {\n            // Send a STOP condition\n            self.info.regs.cr1().modify(|reg| reg.set_stop(true));\n        }\n\n        // Fallthrough is success\n        Ok(())\n    }\n\n    fn send_byte(&self, byte: u8, timeout: Timeout) -> Result<(), Error> {\n        // Wait until we're ready for sending\n        while {\n            // Check for any I2C errors. If a NACK occurs, the ADDR bit will never be set.\n            !Self::check_and_clear_error_flags(self.info)?.txe()\n        } {\n            timeout.check()?;\n        }\n\n        // Push out a byte of data\n        self.info.regs.dr().write(|reg| reg.set_dr(byte));\n\n        // Wait until byte is transferred\n        while {\n            // Check for any potential error conditions.\n            !Self::check_and_clear_error_flags(self.info)?.btf()\n        } {\n            timeout.check()?;\n        }\n\n        Ok(())\n    }\n\n    fn recv_byte(&self, timeout: Timeout) -> Result<u8, Error> {\n        while {\n            // Check for any potential error conditions.\n            Self::check_and_clear_error_flags(self.info)?;\n\n            !self.info.regs.sr1().read().rxne()\n        } {\n            timeout.check()?;\n        }\n\n        let value = self.info.regs.dr().read().dr();\n        Ok(value)\n    }\n\n    fn blocking_read_timeout(\n        &mut self,\n        address: u8,\n        read_buffer: &mut [u8],\n        timeout: Timeout,\n        frame: FrameOptions,\n    ) -> Result<(), Error> {\n        let Some((last_byte, read_buffer)) = read_buffer.split_last_mut() else {\n            return Err(Error::Overrun);\n        };\n\n        if frame.send_start() {\n            // Send a START condition and set ACK bit\n            self.info.regs.cr1().modify(|reg| {\n                reg.set_start(true);\n                reg.set_ack(true);\n            });\n\n            // Wait until START condition was generated\n            while !Self::check_and_clear_error_flags(self.info)?.start() {\n                timeout.check()?;\n            }\n\n            // Check if we were the ones to generate START\n            if self.info.regs.cr1().read().start() || !self.info.regs.sr2().read().msl() {\n                return Err(Error::Arbitration);\n            }\n\n            // Set up current address we're trying to talk to\n            self.info.regs.dr().write(|reg| reg.set_dr((address << 1) + 1));\n\n            // Wait until address was sent\n            // Wait for the address to be acknowledged\n            while !Self::check_and_clear_error_flags(self.info)?.addr() {\n                timeout.check()?;\n            }\n\n            // Clear condition by reading SR2\n            let _ = self.info.regs.sr2().read();\n        }\n\n        // Receive bytes into buffer\n        for c in read_buffer {\n            *c = self.recv_byte(timeout)?;\n        }\n\n        // Prepare to send NACK then STOP after next byte\n        self.info.regs.cr1().modify(|reg| {\n            if frame.send_nack() {\n                reg.set_ack(false);\n            }\n            if frame.send_stop() {\n                reg.set_stop(true);\n            }\n        });\n\n        // Receive last byte\n        *last_byte = self.recv_byte(timeout)?;\n\n        // Fallthrough is success\n        Ok(())\n    }\n\n    /// Blocking read.\n    pub fn blocking_read(&mut self, address: u8, read_buffer: &mut [u8]) -> Result<(), Error> {\n        self.blocking_read_timeout(address, read_buffer, self.timeout(), FrameOptions::FirstAndLastFrame)\n    }\n\n    /// Blocking write.\n    pub fn blocking_write(&mut self, address: u8, write_buffer: &[u8]) -> Result<(), Error> {\n        self.write_bytes(address, write_buffer, self.timeout(), FrameOptions::FirstAndLastFrame)?;\n\n        // Fallthrough is success\n        Ok(())\n    }\n\n    /// Blocking write, restart, read.\n    pub fn blocking_write_read(\n        &mut self,\n        address: u8,\n        write_buffer: &[u8],\n        read_buffer: &mut [u8],\n    ) -> Result<(), Error> {\n        // Check empty read buffer before starting transaction. Otherwise, we would not generate the\n        // stop condition below.\n        if read_buffer.is_empty() {\n            return Err(Error::Overrun);\n        }\n\n        let timeout = self.timeout();\n\n        self.write_bytes(address, write_buffer, timeout, FrameOptions::FirstFrame)?;\n        self.blocking_read_timeout(address, read_buffer, timeout, FrameOptions::FirstAndLastFrame)?;\n\n        Ok(())\n    }\n\n    /// Blocking transaction with operations.\n    ///\n    /// Consecutive operations of same type are merged. See [transaction contract] for details.\n    ///\n    /// [transaction contract]: embedded_hal_1::i2c::I2c::transaction\n    pub fn blocking_transaction(&mut self, address: u8, operations: &mut [Operation<'_>]) -> Result<(), Error> {\n        let timeout = self.timeout();\n\n        for (op, frame) in operation_frames(operations)? {\n            match op {\n                Operation::Read(read_buffer) => self.blocking_read_timeout(address, read_buffer, timeout, frame)?,\n                Operation::Write(write_buffer) => self.write_bytes(address, write_buffer, timeout, frame)?,\n            }\n        }\n\n        Ok(())\n    }\n\n    /// Can be used by both blocking and async implementations  \n    #[inline] // pretty sure this should always be inlined\n    fn enable_interrupts(info: &'static Info) {\n        // The interrupt handler disables interrupts globally, so we need to re-enable them\n        // This must be done in a critical section to avoid races\n        critical_section::with(|_| {\n            info.regs.cr2().modify(|w| {\n                w.set_iterren(true);\n                w.set_itevten(true);\n            });\n        });\n    }\n\n    /// Can be used by both blocking and async implementations\n    fn clear_stop_flag(info: &'static Info) {\n        trace!(\"I2C slave: clearing STOPF flag (v1 sequence)\");\n        // v1 requires: READ SR1 then WRITE CR1 to clear STOPF\n        let _ = info.regs.sr1().read();\n        info.regs.cr1().modify(|_| {}); // Dummy write to clear STOPF\n    }\n}\n\nimpl<'d, IM: MasterMode> I2c<'d, Async, IM> {\n    async fn write_frame(&mut self, address: u8, write_buffer: &[u8], frame: FrameOptions) -> Result<(), Error> {\n        self.info.regs.cr2().modify(|w| {\n            // Note: Do not enable the ITBUFEN bit in the I2C_CR2 register if DMA is used for\n            // reception.\n            w.set_itbufen(false);\n            // DMA mode can be enabled for transmission by setting the DMAEN bit in the I2C_CR2\n            // register.\n            w.set_dmaen(true);\n            // Sending NACK is not necessary (nor possible) for write transfer.\n            w.set_last(false);\n        });\n\n        // Sentinel to disable transfer when an error occurs or future is canceled.\n        // TODO: Generate STOP condition on cancel?\n        let on_drop = OnDrop::new(|| {\n            self.info.regs.cr2().modify(|w| {\n                w.set_dmaen(false);\n                w.set_iterren(false);\n                w.set_itevten(false);\n            })\n        });\n\n        if frame.send_start() {\n            // Send a START condition\n            self.info.regs.cr1().modify(|reg| {\n                reg.set_start(true);\n            });\n\n            // Wait until START condition was generated\n            poll_fn(|cx| {\n                self.state.waker.register(cx.waker());\n\n                match Self::check_and_clear_error_flags(self.info) {\n                    Err(e) => Poll::Ready(Err(e)),\n                    Ok(sr1) => {\n                        if sr1.start() {\n                            Poll::Ready(Ok(()))\n                        } else {\n                            // When pending, (re-)enable interrupts to wake us up.\n                            Self::enable_interrupts(self.info);\n                            Poll::Pending\n                        }\n                    }\n                }\n            })\n            .await?;\n\n            // Check if we were the ones to generate START\n            if self.info.regs.cr1().read().start() || !self.info.regs.sr2().read().msl() {\n                return Err(Error::Arbitration);\n            }\n\n            // Set up current address we're trying to talk to\n            self.info.regs.dr().write(|reg| reg.set_dr(address << 1));\n\n            // Wait for the address to be acknowledged\n            poll_fn(|cx| {\n                self.state.waker.register(cx.waker());\n\n                match Self::check_and_clear_error_flags(self.info) {\n                    Err(e) => {\n                        // Send STOP condition, otherwise SCL will remain low forever.\n                        trace!(\"I2C master: address not acknowledged, send stop\");\n                        self.info.regs.cr1().modify(|reg| reg.set_stop(true));\n\n                        Poll::Ready(Err(e))\n                    }\n                    Ok(sr1) => {\n                        if sr1.addr() {\n                            Poll::Ready(Ok(()))\n                        } else {\n                            // When pending, (re-)enable interrupts to wake us up.\n                            Self::enable_interrupts(self.info);\n                            Poll::Pending\n                        }\n                    }\n                }\n            })\n            .await?;\n\n            // Clear condition by reading SR2\n            self.info.regs.sr2().read();\n        }\n\n        let dma_transfer = unsafe {\n            // Set the I2C_DR register address in the DMA_SxPAR register. The data will be moved to\n            // this address from the memory after each TxE event.\n            let dst = self.info.regs.dr().as_ptr() as *mut u8;\n\n            self.tx_dma\n                .as_mut()\n                .unwrap()\n                .write(write_buffer, dst, Default::default())\n        };\n\n        // Wait for bytes to be sent, or an error to occur.\n        let poll_error = poll_fn(|cx| {\n            self.state.waker.register(cx.waker());\n\n            match Self::check_and_clear_error_flags(self.info) {\n                Err(e) => Poll::Ready(Err::<(), Error>(e)),\n                Ok(_) => {\n                    // When pending, (re-)enable interrupts to wake us up.\n                    Self::enable_interrupts(self.info);\n                    Poll::Pending\n                }\n            }\n        });\n\n        // Wait for either the DMA transfer to successfully finish, or an I2C error to occur.\n        match select(dma_transfer, poll_error).await {\n            Either::Second(Err(e)) => Err(e),\n            _ => Ok(()),\n        }?;\n\n        self.info.regs.cr2().modify(|w| {\n            w.set_dmaen(false);\n        });\n\n        // The I2C transfer itself will take longer than the DMA transfer, so wait for that to finish too.\n        // This is essential even when not sending STOP - if we're about to send a repeated START\n        // (for write_read), we must wait for the last byte to finish transmitting, otherwise the\n        // repeated START will interrupt the ongoing byte transmission and corrupt the transfer.\n        //\n        // 18.3.8 \"Master transmitter: In the interrupt routine after the EOT interrupt, disable DMA\n        // requests then wait for a BTF event before programming the Stop condition.\"\n        poll_fn(|cx| {\n            self.state.waker.register(cx.waker());\n\n            match Self::check_and_clear_error_flags(self.info) {\n                Err(e) => Poll::Ready(Err(e)),\n                Ok(sr1) => {\n                    if sr1.btf() {\n                        Poll::Ready(Ok(()))\n                    } else {\n                        // When pending, (re-)enable interrupts to wake us up.\n                        Self::enable_interrupts(self.info);\n                        Poll::Pending\n                    }\n                }\n            }\n        })\n        .await?;\n\n        if frame.send_stop() {\n            self.info.regs.cr1().modify(|w| {\n                w.set_stop(true);\n            });\n        }\n\n        drop(on_drop);\n\n        // Fallthrough is success\n        Ok(())\n    }\n\n    /// Write.\n    pub async fn write(&mut self, address: u8, write_buffer: &[u8]) -> Result<(), Error> {\n        let _scoped_wake_guard = self.info.rcc.wake_guard();\n        self.write_frame(address, write_buffer, FrameOptions::FirstAndLastFrame)\n            .await?;\n\n        Ok(())\n    }\n\n    /// Read.\n    pub async fn read(&mut self, address: u8, read_buffer: &mut [u8]) -> Result<(), Error> {\n        let _scoped_wake_guard = self.info.rcc.wake_guard();\n        self.read_frame(address, read_buffer, FrameOptions::FirstAndLastFrame)\n            .await?;\n\n        Ok(())\n    }\n\n    async fn read_frame(&mut self, address: u8, read_buffer: &mut [u8], frame: FrameOptions) -> Result<(), Error> {\n        if read_buffer.is_empty() {\n            return Err(Error::Overrun);\n        }\n\n        // Some branches below depend on whether the buffer contains only a single byte.\n        let single_byte = read_buffer.len() == 1;\n\n        self.info.regs.cr2().modify(|w| {\n            // Note: Do not enable the ITBUFEN bit in the I2C_CR2 register if DMA is used for\n            // reception.\n            w.set_itbufen(false);\n            // DMA mode can be enabled for transmission by setting the DMAEN bit in the I2C_CR2\n            // register.\n            w.set_dmaen(true);\n            // If, in the I2C_CR2 register, the LAST bit is set, I2C automatically sends a NACK\n            // after the next byte following EOT_1. The user can generate a Stop condition in\n            // the DMA Transfer Complete interrupt routine if enabled.\n            w.set_last(frame.send_nack() && !single_byte);\n        });\n\n        // Sentinel to disable transfer when an error occurs or future is canceled.\n        // TODO: Generate STOP condition on cancel?\n        let on_drop = OnDrop::new(|| {\n            self.info.regs.cr2().modify(|w| {\n                w.set_dmaen(false);\n                w.set_iterren(false);\n                w.set_itevten(false);\n            })\n        });\n\n        if frame.send_start() {\n            // Send a START condition and set ACK bit\n            self.info.regs.cr1().modify(|reg| {\n                reg.set_start(true);\n                reg.set_ack(true);\n            });\n\n            // Wait until START condition was generated\n            poll_fn(|cx| {\n                self.state.waker.register(cx.waker());\n\n                match Self::check_and_clear_error_flags(self.info) {\n                    Err(e) => Poll::Ready(Err(e)),\n                    Ok(sr1) => {\n                        if sr1.start() {\n                            Poll::Ready(Ok(()))\n                        } else {\n                            // When pending, (re-)enable interrupts to wake us up.\n                            Self::enable_interrupts(self.info);\n                            Poll::Pending\n                        }\n                    }\n                }\n            })\n            .await?;\n\n            // Check if we were the ones to generate START\n            if self.info.regs.cr1().read().start() || !self.info.regs.sr2().read().msl() {\n                return Err(Error::Arbitration);\n            }\n\n            // Set up current address we're trying to talk to\n            self.info.regs.dr().write(|reg| reg.set_dr((address << 1) + 1));\n\n            // Wait for the address to be acknowledged\n            poll_fn(|cx| {\n                self.state.waker.register(cx.waker());\n\n                match Self::check_and_clear_error_flags(self.info) {\n                    Err(e) => {\n                        // Send STOP condition, otherwise SCL will remain low forever.\n                        trace!(\"I2C master: address not acknowledged, send stop\");\n                        self.info.regs.cr1().modify(|reg| reg.set_stop(true));\n\n                        Poll::Ready(Err(e))\n                    }\n                    Ok(sr1) => {\n                        if sr1.addr() {\n                            Poll::Ready(Ok(()))\n                        } else {\n                            // When pending, (re-)enable interrupts to wake us up.\n                            Self::enable_interrupts(self.info);\n                            Poll::Pending\n                        }\n                    }\n                }\n            })\n            .await?;\n\n            // 18.3.8: When a single byte must be received: the NACK must be programmed during EV6\n            // event, i.e. program ACK=0 when ADDR=1, before clearing ADDR flag.\n            if frame.send_nack() && single_byte {\n                self.info.regs.cr1().modify(|w| {\n                    w.set_ack(false);\n                });\n            }\n\n            // Clear condition by reading SR2\n            self.info.regs.sr2().read();\n        } else {\n            // Before starting reception of single byte (but without START condition, i.e. in case\n            // of merged operations), program NACK to emit at end of this byte.\n            if frame.send_nack() && single_byte {\n                self.info.regs.cr1().modify(|w| {\n                    w.set_ack(false);\n                });\n            }\n        }\n\n        // 18.3.8: When a single byte must be received: [snip] Then the user can program the STOP\n        // condition either after clearing ADDR flag, or in the DMA Transfer Complete interrupt\n        // routine.\n        if frame.send_stop() && single_byte {\n            self.info.regs.cr1().modify(|w| {\n                w.set_stop(true);\n            });\n        }\n\n        let dma_transfer = unsafe {\n            // Set the I2C_DR register address in the DMA_SxPAR register. The data will be moved\n            // from this address from the memory after each RxE event.\n            let src = self.info.regs.dr().as_ptr() as *mut u8;\n\n            self.rx_dma.as_mut().unwrap().read(src, read_buffer, Default::default())\n        };\n\n        // Wait for bytes to be received, or an error to occur.\n        let poll_error = poll_fn(|cx| {\n            self.state.waker.register(cx.waker());\n\n            match Self::check_and_clear_error_flags(self.info) {\n                Err(e) => Poll::Ready(Err::<(), Error>(e)),\n                _ => {\n                    // When pending, (re-)enable interrupts to wake us up.\n                    Self::enable_interrupts(self.info);\n                    Poll::Pending\n                }\n            }\n        });\n\n        match select(dma_transfer, poll_error).await {\n            Either::Second(Err(e)) => Err(e),\n            _ => Ok(()),\n        }?;\n\n        self.info.regs.cr2().modify(|w| {\n            w.set_dmaen(false);\n        });\n\n        if frame.send_stop() && !single_byte {\n            self.info.regs.cr1().modify(|w| {\n                w.set_stop(true);\n            });\n        }\n\n        drop(on_drop);\n\n        // Fallthrough is success\n        Ok(())\n    }\n\n    /// Write, restart, read.\n    pub async fn write_read(&mut self, address: u8, write_buffer: &[u8], read_buffer: &mut [u8]) -> Result<(), Error> {\n        let _scoped_wake_guard = self.info.rcc.wake_guard();\n        // Check empty read buffer before starting transaction. Otherwise, we would not generate the\n        // stop condition below.\n        if read_buffer.is_empty() {\n            return Err(Error::Overrun);\n        }\n\n        self.write_frame(address, write_buffer, FrameOptions::FirstFrame)\n            .await?;\n        self.read_frame(address, read_buffer, FrameOptions::FirstAndLastFrame)\n            .await\n    }\n\n    /// Transaction with operations.\n    ///\n    /// Consecutive operations of same type are merged. See [transaction contract] for details.\n    ///\n    /// [transaction contract]: embedded_hal_1::i2c::I2c::transaction\n    pub async fn transaction(&mut self, address: u8, operations: &mut [Operation<'_>]) -> Result<(), Error> {\n        let _scoped_wake_guard = self.info.rcc.wake_guard();\n        for (op, frame) in operation_frames(operations)? {\n            match op {\n                Operation::Read(read_buffer) => self.read_frame(address, read_buffer, frame).await?,\n                Operation::Write(write_buffer) => self.write_frame(address, write_buffer, frame).await?,\n            }\n        }\n\n        Ok(())\n    }\n}\n\nenum Mode {\n    Fast,\n    Standard,\n}\n\nimpl Mode {\n    fn f_s(&self) -> i2c::vals::FS {\n        match self {\n            Mode::Fast => i2c::vals::FS::FAST,\n            Mode::Standard => i2c::vals::FS::STANDARD,\n        }\n    }\n}\n\nenum Duty {\n    Duty2_1,\n    Duty16_9,\n}\n\nimpl Duty {\n    fn duty(&self) -> i2c::vals::Duty {\n        match self {\n            Duty::Duty2_1 => i2c::vals::Duty::DUTY2_1,\n            Duty::Duty16_9 => i2c::vals::Duty::DUTY16_9,\n        }\n    }\n}\n\n/// Result of attempting to send a byte in slave transmitter mode\n#[derive(Debug, PartialEq)]\nenum TransmitResult {\n    /// Byte sent and ACKed by master - continue transmission\n    Acknowledged,\n    /// Byte sent but NACKed by master - normal end of read transaction\n    NotAcknowledged,\n    /// STOP condition detected - master terminated transaction\n    Stopped,\n    /// RESTART condition detected - master starting new transaction\n    Restarted,\n}\n\n/// Result of attempting to receive a byte in slave receiver mode\n#[derive(Debug, PartialEq)]\nenum ReceiveResult {\n    /// Data byte successfully received\n    Data(u8),\n    /// STOP condition detected - end of write transaction\n    Stopped,\n    /// RESTART condition detected - master starting new transaction\n    Restarted,\n}\n\n/// Enumeration of slave transaction termination conditions\n#[derive(Debug, Clone, Copy, PartialEq)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\nenum SlaveTermination {\n    /// STOP condition received - normal end of transaction\n    Stop,\n    /// RESTART condition received - master starting new transaction  \n    Restart,\n    /// NACK received - normal end of read transaction\n    Nack,\n}\n\nimpl<'d, M: PeriMode> I2c<'d, M, Master> {\n    /// Configure the I2C driver for slave operations, allowing for the driver to be used as a slave and a master (multimaster)\n    pub fn into_slave_multimaster(mut self, slave_addr_config: SlaveAddrConfig) -> I2c<'d, M, MultiMaster> {\n        let mut slave = I2c {\n            info: self.info,\n            state: self.state,\n            kernel_clock: self.kernel_clock,\n            tx_dma: self.tx_dma.take(), // Use take() to move ownership\n            rx_dma: self.rx_dma.take(), // Use take() to move ownership\n            #[cfg(feature = \"time\")]\n            timeout: self.timeout,\n            _phantom: PhantomData,\n            _phantom2: PhantomData,\n            _drop_guard: self._drop_guard, // Move the drop guard\n        };\n        slave.init_slave(slave_addr_config);\n        slave\n    }\n}\n\n// Address configuration methods\nimpl<'d, M: PeriMode, IM: MasterMode> I2c<'d, M, IM> {\n    /// Initialize slave mode with address configuration\n    pub(crate) fn init_slave(&mut self, config: SlaveAddrConfig) {\n        trace!(\"I2C slave: initializing with config={:?}\", config);\n\n        // Disable peripheral for configuration\n        self.info.regs.cr1().modify(|reg| reg.set_pe(false));\n\n        // Configure slave addresses\n        self.apply_address_configuration(config);\n\n        // Enable peripheral with slave settings\n        self.info.regs.cr1().modify(|reg| {\n            reg.set_pe(true);\n            reg.set_ack(true); // Enable acknowledgment for slave mode\n            reg.set_nostretch(false); // Allow clock stretching for processing time\n        });\n\n        trace!(\"I2C slave: initialization complete\");\n    }\n\n    /// Apply the complete address configuration for slave mode\n    fn apply_address_configuration(&mut self, config: SlaveAddrConfig) {\n        match config.addr {\n            OwnAddresses::OA1(addr) => {\n                self.configure_primary_address(addr);\n                self.disable_secondary_address();\n            }\n            OwnAddresses::OA2(oa2) => {\n                self.configure_default_primary_address();\n                self.configure_secondary_address(oa2.addr); // v1 ignores mask\n            }\n            OwnAddresses::Both { oa1, oa2 } => {\n                self.configure_primary_address(oa1);\n                self.configure_secondary_address(oa2.addr); // v1 ignores mask\n            }\n        }\n\n        // Configure general call detection\n        if config.general_call {\n            self.info.regs.cr1().modify(|w| w.set_engc(true));\n        }\n    }\n\n    /// Configure the primary address (OA1) register\n    fn configure_primary_address(&mut self, addr: Address) {\n        match addr {\n            Address::SevenBit(addr) => {\n                self.info.regs.oar1().write(|reg| {\n                    let hw_addr = (addr as u16) << 1; // Address in bits [7:1]\n                    reg.set_add(hw_addr);\n                    reg.set_addmode(i2c::vals::Addmode::BIT7);\n                });\n            }\n            Address::TenBit(addr) => {\n                self.info.regs.oar1().write(|reg| {\n                    reg.set_add(addr);\n                    reg.set_addmode(i2c::vals::Addmode::BIT10);\n                });\n            }\n        }\n\n        // Set required bit 14 as per reference manual\n        self.info.regs.oar1().modify(|reg| reg.0 |= 1 << 14);\n    }\n\n    /// Configure the secondary address (OA2) register  \n    fn configure_secondary_address(&mut self, addr: u8) {\n        self.info.regs.oar2().write(|reg| {\n            reg.set_add2(addr);\n            reg.set_endual(i2c::vals::Endual::DUAL);\n        });\n    }\n\n    /// Set a default primary address when using OA2-only mode\n    fn configure_default_primary_address(&mut self) {\n        self.info.regs.oar1().write(|reg| {\n            reg.set_add(0); // Reserved address, safe to use\n            reg.set_addmode(i2c::vals::Addmode::BIT7);\n        });\n        self.info.regs.oar1().modify(|reg| reg.0 |= 1 << 14);\n    }\n\n    /// Disable secondary address when not needed\n    fn disable_secondary_address(&mut self) {\n        self.info.regs.oar2().write(|reg| {\n            reg.set_endual(i2c::vals::Endual::SINGLE);\n        });\n    }\n}\n\nimpl<'d, M: PeriMode> I2c<'d, M, MultiMaster> {\n    /// Listen for incoming I2C address match and return the command type\n    ///\n    /// This method blocks until the slave address is matched by a master.\n    /// Returns the command type (Read/Write) and the matched address.\n    pub fn blocking_listen(&mut self) -> Result<SlaveCommand, Error> {\n        trace!(\"I2C slave: starting blocking listen for address match\");\n        let result = self.blocking_listen_with_timeout(self.timeout());\n        trace!(\"I2C slave: blocking listen complete, result={:?}\", result);\n        result\n    }\n\n    /// Respond to a master read request by transmitting data\n    ///\n    /// Sends the provided data to the master. If the master requests more bytes\n    /// than available, padding bytes (0x00) are sent until the master terminates\n    /// the transaction with NACK.\n    ///\n    /// Returns the total number of bytes transmitted (including padding).\n    pub fn blocking_respond_to_read(&mut self, data: &[u8]) -> Result<usize, Error> {\n        trace!(\"I2C slave: starting blocking respond_to_read, data_len={}\", data.len());\n\n        if let Some(zero_length_result) = self.detect_zero_length_read(self.timeout())? {\n            trace!(\"I2C slave: zero-length read detected\");\n            return Ok(zero_length_result);\n        }\n\n        let result = self.transmit_to_master(data, self.timeout());\n        trace!(\"I2C slave: blocking respond_to_read complete, result={:?}\", result);\n        result\n    }\n\n    /// Respond to a master write request by receiving data\n    ///\n    /// Receives data from the master into the provided buffer. If the master\n    /// sends more bytes than the buffer can hold, excess bytes are acknowledged\n    /// but discarded.\n    ///\n    /// Returns the number of bytes stored in the buffer (not total received).\n    pub fn blocking_respond_to_write(&mut self, buffer: &mut [u8]) -> Result<usize, Error> {\n        trace!(\n            \"I2C slave: starting blocking respond_to_write, buffer_len={}\",\n            buffer.len()\n        );\n        let result = self.receive_from_master(buffer, self.timeout());\n        trace!(\"I2C slave: blocking respond_to_write complete, result={:?}\", result);\n        result\n    }\n\n    // Private implementation methods\n\n    /// Wait for address match and determine transaction type\n    fn blocking_listen_with_timeout(&mut self, timeout: Timeout) -> Result<SlaveCommand, Error> {\n        // Ensure interrupts are disabled for blocking operation\n        self.disable_i2c_interrupts();\n\n        // Wait for address match (ADDR flag)\n        loop {\n            let sr1 = Self::read_status_and_handle_errors(self.info)?;\n\n            if sr1.addr() {\n                // Address matched - read SR2 to get direction and clear ADDR flag\n                let sr2 = self.info.regs.sr2().read();\n                let direction = if sr2.tra() {\n                    SlaveCommandKind::Read\n                } else {\n                    SlaveCommandKind::Write\n                };\n\n                // Use the static method instead of the instance method\n                let matched_address = Self::decode_matched_address(sr2, self.info)?;\n                trace!(\n                    \"I2C slave: address matched, direction={:?}, addr={:?}\",\n                    direction, matched_address\n                );\n\n                return Ok(SlaveCommand {\n                    kind: direction,\n                    address: matched_address,\n                });\n            }\n\n            timeout.check()?;\n        }\n    }\n\n    /// Transmit data to master in response to read request\n    fn transmit_to_master(&mut self, data: &[u8], timeout: Timeout) -> Result<usize, Error> {\n        let mut bytes_transmitted = 0;\n        let mut padding_count = 0;\n\n        loop {\n            let byte_to_send = if bytes_transmitted < data.len() {\n                data[bytes_transmitted]\n            } else {\n                padding_count += 1;\n                0x00 // Send padding bytes when data is exhausted\n            };\n\n            match self.transmit_byte(byte_to_send, timeout)? {\n                TransmitResult::Acknowledged => {\n                    bytes_transmitted += 1;\n                }\n                TransmitResult::NotAcknowledged => {\n                    bytes_transmitted += 1; // Count the NACKed byte\n                    break;\n                }\n                TransmitResult::Stopped | TransmitResult::Restarted => {\n                    break;\n                }\n            }\n        }\n\n        if padding_count > 0 {\n            trace!(\n                \"I2C slave: sent {} data bytes + {} padding bytes = {} total\",\n                data.len(),\n                padding_count,\n                bytes_transmitted\n            );\n        }\n\n        Ok(bytes_transmitted)\n    }\n\n    /// Receive data from master during write request\n    fn receive_from_master(&mut self, buffer: &mut [u8], timeout: Timeout) -> Result<usize, Error> {\n        let mut bytes_stored = 0;\n\n        // Receive bytes that fit in buffer\n        while bytes_stored < buffer.len() {\n            match self.receive_byte(timeout)? {\n                ReceiveResult::Data(byte) => {\n                    buffer[bytes_stored] = byte;\n                    bytes_stored += 1;\n                }\n                ReceiveResult::Stopped | ReceiveResult::Restarted => {\n                    return Ok(bytes_stored);\n                }\n            }\n        }\n\n        // Handle buffer overflow by discarding excess bytes\n        if bytes_stored == buffer.len() {\n            trace!(\"I2C slave: buffer full, discarding excess bytes\");\n            self.discard_excess_bytes(timeout)?;\n        }\n\n        Ok(bytes_stored)\n    }\n\n    /// Detect zero-length read pattern early\n    ///\n    /// Zero-length reads occur when a master sends START+ADDR+R followed immediately\n    /// by NACK+STOP without wanting any data. This must be detected before attempting\n    /// to transmit any bytes to avoid SDA line issues.\n    fn detect_zero_length_read(&mut self, _timeout: Timeout) -> Result<Option<usize>, Error> {\n        // Quick check for immediate termination signals\n        let sr1 = self.info.regs.sr1().read();\n\n        // Check for immediate NACK (fastest zero-length pattern)\n        if sr1.af() {\n            self.clear_acknowledge_failure();\n            return Ok(Some(0));\n        }\n\n        // Check for immediate STOP (alternative zero-length pattern)\n        if sr1.stopf() {\n            Self::clear_stop_flag(self.info);\n            return Ok(Some(0));\n        }\n\n        // Give a brief window for master to send termination signals\n        // This handles masters that have slight delays between address ACK and NACK\n        const ZERO_LENGTH_DETECTION_CYCLES: u32 = 20; // ~5-10µs window\n\n        for _ in 0..ZERO_LENGTH_DETECTION_CYCLES {\n            let sr1 = self.info.regs.sr1().read();\n\n            // Immediate NACK indicates zero-length read\n            if sr1.af() {\n                self.clear_acknowledge_failure();\n                return Ok(Some(0));\n            }\n\n            // Immediate STOP indicates zero-length read\n            if sr1.stopf() {\n                Self::clear_stop_flag(self.info);\n                return Ok(Some(0));\n            }\n\n            // If TXE becomes ready, master is waiting for data - not zero-length\n            if sr1.txe() {\n                return Ok(None); // Proceed with normal transmission\n            }\n\n            // If RESTART detected, handle as zero-length\n            if sr1.addr() {\n                return Ok(Some(0));\n            }\n        }\n\n        // No zero-length pattern detected within the window\n        Ok(None)\n    }\n\n    /// Discard excess bytes when buffer is full\n    fn discard_excess_bytes(&mut self, timeout: Timeout) -> Result<(), Error> {\n        let mut discarded_count = 0;\n\n        loop {\n            match self.receive_byte(timeout)? {\n                ReceiveResult::Data(_) => {\n                    discarded_count += 1;\n                    continue;\n                }\n                ReceiveResult::Stopped | ReceiveResult::Restarted => {\n                    if discarded_count > 0 {\n                        trace!(\"I2C slave: discarded {} excess bytes\", discarded_count);\n                    }\n                    break;\n                }\n            }\n        }\n        Ok(())\n    }\n\n    /// Send a single byte and wait for master's response\n    fn transmit_byte(&mut self, byte: u8, timeout: Timeout) -> Result<TransmitResult, Error> {\n        // Wait for transmit buffer ready\n        self.wait_for_transmit_ready(timeout)?;\n\n        // Send the byte\n        self.info.regs.dr().write(|w| w.set_dr(byte));\n\n        // Wait for transmission completion or master response\n        self.wait_for_transmit_completion(timeout)\n    }\n\n    /// Wait until transmit buffer is ready (TXE flag set)\n    fn wait_for_transmit_ready(&mut self, timeout: Timeout) -> Result<(), Error> {\n        loop {\n            let sr1 = Self::read_status_and_handle_errors(self.info)?;\n\n            // Check for early termination conditions\n            if let Some(result) = Self::check_early_termination(sr1) {\n                return Err(self.handle_early_termination(result));\n            }\n\n            if sr1.txe() {\n                return Ok(()); // Ready to transmit\n            }\n\n            timeout.check()?;\n        }\n    }\n\n    /// Wait for byte transmission completion or master response\n    fn wait_for_transmit_completion(&mut self, timeout: Timeout) -> Result<TransmitResult, Error> {\n        loop {\n            let sr1 = self.info.regs.sr1().read();\n\n            // Check flags in priority order\n            if sr1.af() {\n                self.clear_acknowledge_failure();\n                return Ok(TransmitResult::NotAcknowledged);\n            }\n\n            if sr1.btf() {\n                return Ok(TransmitResult::Acknowledged);\n            }\n\n            if sr1.stopf() {\n                Self::clear_stop_flag(self.info);\n                return Ok(TransmitResult::Stopped);\n            }\n\n            if sr1.addr() {\n                return Ok(TransmitResult::Restarted);\n            }\n\n            // Check for other error conditions\n            self.check_for_hardware_errors(sr1)?;\n\n            timeout.check()?;\n        }\n    }\n\n    /// Receive a single byte or detect transaction termination\n    fn receive_byte(&mut self, timeout: Timeout) -> Result<ReceiveResult, Error> {\n        loop {\n            let sr1 = Self::read_status_and_handle_errors(self.info)?;\n\n            // Check for received data first (prioritize data over control signals)\n            if sr1.rxne() {\n                let byte = self.info.regs.dr().read().dr();\n                return Ok(ReceiveResult::Data(byte));\n            }\n\n            // Check for transaction termination\n            if sr1.addr() {\n                return Ok(ReceiveResult::Restarted);\n            }\n\n            if sr1.stopf() {\n                Self::clear_stop_flag(self.info);\n                return Ok(ReceiveResult::Stopped);\n            }\n\n            timeout.check()?;\n        }\n    }\n\n    /// Determine which slave address was matched based on SR2 flags\n    fn decode_matched_address(sr2: i2c::regs::Sr2, info: &'static Info) -> Result<Address, Error> {\n        if sr2.gencall() {\n            Ok(Address::SevenBit(0x00)) // General call address\n        } else if sr2.dualf() {\n            // OA2 (secondary address) was matched\n            let oar2 = info.regs.oar2().read();\n            if oar2.endual() != i2c::vals::Endual::DUAL {\n                return Err(Error::Bus); // Hardware inconsistency\n            }\n            Ok(Address::SevenBit(oar2.add2()))\n        } else {\n            // OA1 (primary address) was matched\n            let oar1 = info.regs.oar1().read();\n            match oar1.addmode() {\n                i2c::vals::Addmode::BIT7 => {\n                    let addr = (oar1.add() >> 1) as u8;\n                    Ok(Address::SevenBit(addr))\n                }\n                i2c::vals::Addmode::BIT10 => Ok(Address::TenBit(oar1.add())),\n            }\n        }\n    }\n\n    // Helper methods for hardware interaction\n\n    /// Read status register and handle I2C errors (except NACK in slave mode)\n    fn read_status_and_handle_errors(info: &'static Info) -> Result<i2c::regs::Sr1, Error> {\n        match Self::check_and_clear_error_flags(info) {\n            Ok(sr1) => Ok(sr1),\n            Err(Error::Nack) => {\n                // In slave mode, NACK is normal protocol behavior, not an error\n                Ok(info.regs.sr1().read())\n            }\n            Err(other_error) => Err(other_error),\n        }\n    }\n\n    /// Check for conditions that cause early termination of operations\n    fn check_early_termination(sr1: i2c::regs::Sr1) -> Option<TransmitResult> {\n        if sr1.stopf() {\n            Some(TransmitResult::Stopped)\n        } else if sr1.addr() {\n            Some(TransmitResult::Restarted)\n        } else if sr1.af() {\n            Some(TransmitResult::NotAcknowledged)\n        } else {\n            None\n        }\n    }\n\n    /// Convert early termination to appropriate error\n    fn handle_early_termination(&mut self, result: TransmitResult) -> Error {\n        match result {\n            TransmitResult::Stopped => {\n                Self::clear_stop_flag(self.info);\n                Error::Bus // Unexpected STOP during setup\n            }\n            TransmitResult::Restarted => {\n                Error::Bus // Unexpected RESTART during setup\n            }\n            TransmitResult::NotAcknowledged => {\n                self.clear_acknowledge_failure();\n                Error::Bus // Unexpected NACK during setup\n            }\n            TransmitResult::Acknowledged => {\n                unreachable!() // This should never be passed to this function\n            }\n        }\n    }\n\n    /// Check for hardware-level I2C errors during transmission\n    fn check_for_hardware_errors(&self, sr1: i2c::regs::Sr1) -> Result<(), Error> {\n        if sr1.timeout() || sr1.ovr() || sr1.arlo() || sr1.berr() {\n            // Delegate to existing error handling\n            Self::check_and_clear_error_flags(self.info)?;\n        }\n        Ok(())\n    }\n\n    /// Disable I2C event and error interrupts for blocking operations\n    fn disable_i2c_interrupts(&mut self) {\n        self.info.regs.cr2().modify(|w| {\n            w.set_itevten(false);\n            w.set_iterren(false);\n        });\n    }\n\n    /// Clear the acknowledge failure flag\n    fn clear_acknowledge_failure(&mut self) {\n        self.info.regs.sr1().write(|reg| {\n            reg.0 = !0;\n            reg.set_af(false);\n        });\n    }\n\n    /// Configure DMA settings for slave operations (shared between read/write)\n    fn setup_slave_dma_base(&mut self) {\n        self.info.regs.cr2().modify(|w| {\n            w.set_itbufen(false); // Always disable buffer interrupts when using DMA\n            w.set_dmaen(true); // Enable DMA requests\n            w.set_last(false); // LAST bit not used in slave mode for v1 hardware\n        });\n    }\n\n    /// Disable DMA and interrupts in a critical section\n    fn disable_dma_and_interrupts(info: &'static Info) {\n        critical_section::with(|_| {\n            info.regs.cr2().modify(|w| {\n                w.set_dmaen(false);\n                w.set_iterren(false);\n                w.set_itevten(false);\n            });\n        });\n    }\n\n    /// Check for early termination conditions during slave operations\n    /// Returns Some(result) if termination detected, None to continue\n    fn check_slave_termination_conditions(sr1: i2c::regs::Sr1) -> Option<SlaveTermination> {\n        if sr1.stopf() {\n            Some(SlaveTermination::Stop)\n        } else if sr1.addr() {\n            Some(SlaveTermination::Restart)\n        } else if sr1.af() {\n            Some(SlaveTermination::Nack)\n        } else {\n            None\n        }\n    }\n}\n\nimpl<'d> I2c<'d, Async, MultiMaster> {\n    /// Async listen for incoming I2C messages using interrupts\n    ///\n    /// Waits for a master to address this slave and returns the command type\n    /// (Read/Write) and the matched address. This method will suspend until\n    /// an address match occurs.\n    pub async fn listen(&mut self) -> Result<SlaveCommand, Error> {\n        let _scoped_wake_guard = self.info.rcc.wake_guard();\n        trace!(\"I2C slave: starting async listen for address match\");\n        let state = self.state;\n        let info = self.info;\n\n        Self::enable_interrupts(info);\n\n        let on_drop = OnDrop::new(|| {\n            Self::disable_dma_and_interrupts(info);\n        });\n\n        let result = poll_fn(|cx| {\n            state.waker.register(cx.waker());\n\n            match Self::check_and_clear_error_flags(info) {\n                Err(e) => {\n                    error!(\"I2C slave: error during listen: {:?}\", e);\n                    Poll::Ready(Err(e))\n                }\n                Ok(sr1) => {\n                    if sr1.addr() {\n                        let sr2 = info.regs.sr2().read();\n                        let direction = if sr2.tra() {\n                            SlaveCommandKind::Read\n                        } else {\n                            SlaveCommandKind::Write\n                        };\n\n                        let matched_address = match Self::decode_matched_address(sr2, info) {\n                            Ok(addr) => {\n                                trace!(\"I2C slave: address matched, direction={:?}, addr={:?}\", direction, addr);\n                                addr\n                            }\n                            Err(e) => {\n                                error!(\"I2C slave: failed to decode matched address: {:?}\", e);\n                                return Poll::Ready(Err(e));\n                            }\n                        };\n\n                        Poll::Ready(Ok(SlaveCommand {\n                            kind: direction,\n                            address: matched_address,\n                        }))\n                    } else {\n                        Self::enable_interrupts(info);\n                        Poll::Pending\n                    }\n                }\n            }\n        })\n        .await;\n\n        drop(on_drop);\n        trace!(\"I2C slave: listen complete, result={:?}\", result);\n        result\n    }\n\n    /// Async respond to write command using RX DMA\n    ///\n    /// Receives data from the master into the provided buffer using DMA.\n    /// If the master sends more bytes than the buffer can hold, excess bytes\n    /// are acknowledged but discarded to prevent interrupt flooding.\n    ///\n    /// Returns the number of bytes stored in the buffer (not total received).\n    pub async fn respond_to_write(&mut self, buffer: &mut [u8]) -> Result<usize, Error> {\n        let _scoped_wake_guard = self.info.rcc.wake_guard();\n        trace!(\"I2C slave: starting respond_to_write, buffer_len={}\", buffer.len());\n\n        if buffer.is_empty() {\n            warn!(\"I2C slave: respond_to_write called with empty buffer\");\n            return Err(Error::Overrun);\n        }\n\n        let state = self.state;\n        let info = self.info;\n\n        self.setup_slave_dma_base();\n\n        let on_drop = OnDrop::new(|| {\n            Self::disable_dma_and_interrupts(info);\n        });\n\n        info.regs.sr2().read();\n\n        let result = self.execute_slave_receive_transfer(buffer, state, info).await;\n\n        drop(on_drop);\n        trace!(\"I2C slave: respond_to_write complete, result={:?}\", result);\n        result\n    }\n\n    /// Async respond to read command using TX DMA\n    ///\n    /// Transmits data to the master using DMA. If the master requests more bytes\n    /// than available in the data buffer, padding bytes (0x00) are sent until\n    /// the master terminates the transaction with NACK, STOP, or RESTART.\n    ///\n    /// Returns the total number of bytes transmitted (data + padding).\n    pub async fn respond_to_read(&mut self, data: &[u8]) -> Result<usize, Error> {\n        let _scoped_wake_guard = self.info.rcc.wake_guard();\n        trace!(\"I2C slave: starting respond_to_read, data_len={}\", data.len());\n\n        if data.is_empty() {\n            warn!(\"I2C slave: respond_to_read called with empty data\");\n            return Err(Error::Overrun);\n        }\n\n        let state = self.state;\n        let info = self.info;\n\n        self.setup_slave_dma_base();\n\n        let on_drop = OnDrop::new(|| {\n            Self::disable_dma_and_interrupts(info);\n        });\n\n        info.regs.sr2().read();\n\n        let result = self.execute_slave_transmit_transfer(data, state, info).await;\n\n        drop(on_drop);\n        trace!(\"I2C slave: respond_to_read complete, result={:?}\", result);\n        result\n    }\n\n    // === Private Transfer Execution Methods ===\n\n    /// Execute complete slave receive transfer with excess byte handling\n    async fn execute_slave_receive_transfer(\n        &mut self,\n        buffer: &mut [u8],\n        state: &'static State,\n        info: &'static Info,\n    ) -> Result<usize, Error> {\n        let total_len = buffer.len();\n\n        let mut dma_transfer = unsafe {\n            let src = info.regs.dr().as_ptr() as *mut u8;\n            self.rx_dma.as_mut().unwrap().read(src, buffer, Default::default())\n        };\n\n        // Track whether transfer was terminated by I2C event (STOP/RESTART) vs DMA completion.\n        // We only need to handle excess bytes if DMA completed (buffer full) and master\n        // is still sending. If STOP/RESTART terminated the transfer, there are no excess bytes.\n        let mut terminated_by_i2c_event = false;\n\n        // Use poll_fn to monitor both DMA and I2C events, allowing us to get the\n        // remaining DMA transfer count when STOP/RESTART is detected.\n        // Returns Ok(remaining) where remaining is the DMA NDTR value when termination was detected.\n        let result = poll_fn(|cx| {\n            state.waker.register(cx.waker());\n\n            // Check for I2C errors first\n            match Self::check_and_clear_error_flags(info) {\n                Err(e) => {\n                    error!(\"I2C slave: error during receive transfer: {:?}\", e);\n                    return Poll::Ready(Err(e));\n                }\n                Ok(sr1) => {\n                    // Check for STOP or RESTART conditions\n                    if let Some(termination) = Self::check_slave_termination_conditions(sr1) {\n                        match termination {\n                            SlaveTermination::Stop | SlaveTermination::Restart => {\n                                // Get DMA remaining count\n                                let remaining = dma_transfer.get_remaining_transfers() as usize;\n\n                                // Clear the termination flag\n                                match termination {\n                                    SlaveTermination::Stop => Self::clear_stop_flag(info),\n                                    SlaveTermination::Restart => {\n                                        // ADDR flag will be handled by next listen() call\n                                    }\n                                    SlaveTermination::Nack => unreachable!(),\n                                }\n\n                                terminated_by_i2c_event = true;\n                                trace!(\n                                    \"I2C slave: receive terminated by {:?}, received {} bytes (remaining {})\",\n                                    termination,\n                                    total_len.saturating_sub(remaining),\n                                    remaining\n                                );\n                                return Poll::Ready(Ok(remaining));\n                            }\n                            SlaveTermination::Nack => {\n                                // Unexpected NACK during receive\n                                return Poll::Ready(Err(Error::Bus));\n                            }\n                        }\n                    }\n\n                    // Check if DMA transfer completed (buffer full)\n                    if !dma_transfer.is_running() {\n                        trace!(\"I2C slave: DMA receive completed (buffer full)\");\n                        return Poll::Ready(Ok(0_usize)); // remaining = 0\n                    }\n\n                    // Neither condition met, enable interrupts and wait\n                    Self::enable_interrupts(info);\n                    Poll::Pending\n                }\n            }\n        })\n        .await;\n\n        // Stop the DMA transfer - this releases the borrow on buffer\n        drop(dma_transfer);\n        Self::disable_dma_and_interrupts(info);\n\n        // Calculate actual bytes received\n        let result = match result {\n            Ok(remaining) => {\n                let received = total_len.saturating_sub(remaining);\n                trace!(\"I2C slave: receive complete, received {} bytes\", received);\n                Ok(received)\n            }\n            Err(e) => Err(e),\n        };\n\n        // Only handle excess bytes if DMA completed (buffer full) AND transfer was NOT\n        // terminated by STOP/RESTART. If STOP/RESTART occurred, there are no more bytes coming.\n        if let Ok(received) = result {\n            if received == total_len && !terminated_by_i2c_event {\n                self.handle_excess_bytes(state, info).await?;\n            }\n        }\n\n        result\n    }\n\n    /// Execute complete slave transmit transfer with padding byte handling\n    async fn execute_slave_transmit_transfer(\n        &mut self,\n        data: &[u8],\n        state: &'static State,\n        info: &'static Info,\n    ) -> Result<usize, Error> {\n        let total_len = data.len();\n\n        let mut dma_transfer = unsafe {\n            let dst = info.regs.dr().as_ptr() as *mut u8;\n            self.tx_dma.as_mut().unwrap().write(data, dst, Default::default())\n        };\n\n        // Track whether transfer was terminated by I2C event (STOP/RESTART/NACK) vs DMA completion.\n        // We only need to handle padding bytes if DMA completed and master wants more data.\n        let mut terminated_by_i2c_event = false;\n\n        // Use poll_fn to monitor both DMA and I2C events, allowing us to get the\n        // remaining DMA transfer count when STOP/RESTART/NACK is detected\n        // Helper to calculate actual bytes transmitted.\n        // DMA pre-loads the next byte into DR before it's clocked out. When termination\n        // occurs, if TXE is clear, there's a byte in DR that was loaded but never sent.\n        let calc_bytes_sent = |remaining: usize, sr1: crate::pac::i2c::regs::Sr1| {\n            let dma_sent = total_len.saturating_sub(remaining);\n            // If TXE is clear, DR contains a byte that DMA loaded but master never clocked out\n            if !sr1.txe() && dma_sent > 0 {\n                dma_sent - 1\n            } else {\n                dma_sent\n            }\n        };\n\n        let result = poll_fn(|cx| {\n            state.waker.register(cx.waker());\n\n            // Check for I2C errors first (NACK is expected for read termination)\n            match Self::check_and_clear_error_flags(info) {\n                Err(Error::Nack) => {\n                    // NACK is normal - master doesn't want more data\n                    let sr1 = info.regs.sr1().read();\n                    let remaining = dma_transfer.get_remaining_transfers() as usize;\n                    let sent = calc_bytes_sent(remaining, sr1);\n                    terminated_by_i2c_event = true;\n                    trace!(\n                        \"I2C slave: transmit terminated by Nack, sent {} bytes (remaining {}, txe={})\",\n                        sent,\n                        remaining,\n                        sr1.txe()\n                    );\n                    return Poll::Ready(Ok(sent));\n                }\n                Err(e) => {\n                    error!(\"I2C slave: error during transmit transfer: {:?}\", e);\n                    return Poll::Ready(Err(e));\n                }\n                Ok(sr1) => {\n                    // Check for STOP or RESTART conditions\n                    if let Some(termination) = Self::check_slave_termination_conditions(sr1) {\n                        match termination {\n                            SlaveTermination::Stop | SlaveTermination::Restart => {\n                                let remaining = dma_transfer.get_remaining_transfers() as usize;\n                                let sent = calc_bytes_sent(remaining, sr1);\n\n                                match termination {\n                                    SlaveTermination::Stop => Self::clear_stop_flag(info),\n                                    SlaveTermination::Restart => {\n                                        // ADDR flag will be handled by next listen() call\n                                    }\n                                    SlaveTermination::Nack => unreachable!(),\n                                }\n\n                                terminated_by_i2c_event = true;\n                                trace!(\n                                    \"I2C slave: transmit terminated by {:?}, sent {} bytes (remaining {}, txe={})\",\n                                    termination,\n                                    sent,\n                                    remaining,\n                                    sr1.txe()\n                                );\n                                return Poll::Ready(Ok(sent));\n                            }\n                            SlaveTermination::Nack => {\n                                // Handled above via check_and_clear_error_flags\n                                let remaining = dma_transfer.get_remaining_transfers() as usize;\n                                let sent = calc_bytes_sent(remaining, sr1);\n                                terminated_by_i2c_event = true;\n                                info.regs.sr1().write(|reg| {\n                                    reg.0 = !0;\n                                    reg.set_af(false);\n                                });\n                                trace!(\n                                    \"I2C slave: transmit terminated by Nack, sent {} bytes (remaining {}, txe={})\",\n                                    sent,\n                                    remaining,\n                                    sr1.txe()\n                                );\n                                return Poll::Ready(Ok(sent));\n                            }\n                        }\n                    }\n\n                    // Check if DMA transfer completed (all data sent)\n                    if !dma_transfer.is_running() {\n                        trace!(\"I2C slave: DMA transmit completed (all data sent)\");\n                        return Poll::Ready(Ok(total_len));\n                    }\n\n                    // Neither condition met, enable interrupts and wait\n                    Self::enable_interrupts(info);\n                    Poll::Pending\n                }\n            }\n        })\n        .await;\n\n        // Stop the DMA transfer\n        drop(dma_transfer);\n        Self::disable_dma_and_interrupts(info);\n\n        // Only handle padding bytes if DMA completed (all data sent) AND transfer was NOT\n        // terminated by STOP/RESTART/NACK. If terminated early, master doesn't want more data.\n        if let Ok(sent) = result {\n            if sent == total_len && !terminated_by_i2c_event {\n                let padding_count = self.handle_padding_bytes(state, info).await?;\n                let total_bytes = sent + padding_count;\n                trace!(\n                    \"I2C slave: sent {} data bytes + {} padding bytes = {} total\",\n                    sent, padding_count, total_bytes\n                );\n                return Ok(total_bytes);\n            }\n        }\n\n        result\n    }\n\n    /// Handle excess bytes after DMA buffer is full\n    ///\n    /// Reads and discards bytes until transaction termination to prevent interrupt flooding\n    async fn handle_excess_bytes(&mut self, state: &'static State, info: &'static Info) -> Result<(), Error> {\n        let mut discarded_count = 0;\n\n        poll_fn(|cx| {\n            state.waker.register(cx.waker());\n\n            match Self::check_and_clear_error_flags(info) {\n                Err(e) => {\n                    error!(\"I2C slave: error while discarding excess bytes: {:?}\", e);\n                    Poll::Ready(Err(e))\n                }\n                Ok(sr1) => {\n                    // Drain any pending data BEFORE checking for termination.\n                    // This ensures we count all excess bytes even if STOP arrives\n                    // at the same time as the last data byte.\n                    if sr1.rxne() {\n                        let _discarded_byte = info.regs.dr().read().dr();\n                        discarded_count += 1;\n                        Self::enable_interrupts(info);\n                        return Poll::Pending;\n                    }\n\n                    if let Some(termination) = Self::check_slave_termination_conditions(sr1) {\n                        match termination {\n                            SlaveTermination::Stop => Self::clear_stop_flag(info),\n                            SlaveTermination::Restart => {}\n                            SlaveTermination::Nack => unreachable!(\"NACK not expected during receive\"),\n                        }\n                        if discarded_count > 0 {\n                            trace!(\"I2C slave: discarded {} excess bytes\", discarded_count);\n                        }\n                        return Poll::Ready(Ok(()));\n                    }\n\n                    Self::enable_interrupts(info);\n                    Poll::Pending\n                }\n            }\n        })\n        .await\n    }\n\n    /// Handle padding bytes after DMA data is exhausted\n    ///\n    /// Sends 0x00 bytes until transaction termination to prevent interrupt flooding\n    async fn handle_padding_bytes(&mut self, state: &'static State, info: &'static Info) -> Result<usize, Error> {\n        let mut padding_count = 0;\n\n        poll_fn(|cx| {\n            state.waker.register(cx.waker());\n\n            match Self::check_and_clear_error_flags(info) {\n                Err(Error::Nack) => Poll::Ready(Ok(padding_count)),\n                Err(e) => {\n                    error!(\"I2C slave: error while sending padding bytes: {:?}\", e);\n                    Poll::Ready(Err(e))\n                }\n                Ok(sr1) => {\n                    if let Some(termination) = Self::check_slave_termination_conditions(sr1) {\n                        match termination {\n                            SlaveTermination::Stop => Self::clear_stop_flag(info),\n                            SlaveTermination::Restart => {}\n                            SlaveTermination::Nack => {\n                                info.regs.sr1().write(|reg| {\n                                    reg.0 = !0;\n                                    reg.set_af(false);\n                                });\n                            }\n                        }\n                        return Poll::Ready(Ok(padding_count));\n                    }\n\n                    if sr1.txe() {\n                        info.regs.dr().write(|w| w.set_dr(0x00));\n                        padding_count += 1;\n                        Self::enable_interrupts(info);\n                        return Poll::Pending;\n                    }\n\n                    Self::enable_interrupts(info);\n                    Poll::Pending\n                }\n            }\n        })\n        .await\n    }\n}\n\n/// Timing configuration for I2C v1 hardware\n///\n/// This struct encapsulates the complex timing calculations required for STM32 I2C v1\n/// peripherals, which use three separate registers (CR2.FREQ, CCR, TRISE) instead of\n/// the unified TIMINGR register found in v2 hardware.\nstruct Timings {\n    freq: u8,   // APB frequency in MHz for CR2.FREQ register\n    mode: Mode, // Standard or Fast mode selection\n    trise: u8,  // Rise time compensation value\n    ccr: u16,   // Clock control register value\n    duty: Duty, // Fast mode duty cycle selection\n}\n\nimpl Timings {\n    fn new(i2cclk: Hertz, frequency: Hertz) -> Self {\n        // Calculate settings for I2C speed modes\n        let frequency = frequency.0;\n        let clock = i2cclk.0;\n        let freq = clock / 1_000_000;\n        assert!((2..=50).contains(&freq));\n\n        // Configure bus frequency into I2C peripheral\n        let trise = if frequency <= 100_000 {\n            freq + 1\n        } else {\n            (freq * 300) / 1000 + 1\n        };\n\n        let mut ccr;\n        let duty;\n        let mode;\n\n        // I2C clock control calculation\n        if frequency <= 100_000 {\n            duty = Duty::Duty2_1;\n            mode = Mode::Standard;\n            ccr = {\n                let ccr = clock / (frequency * 2);\n                if ccr < 4 { 4 } else { ccr }\n            };\n        } else {\n            const DUTYCYCLE: u8 = 0;\n            mode = Mode::Fast;\n            if DUTYCYCLE == 0 {\n                duty = Duty::Duty2_1;\n                ccr = clock / (frequency * 3);\n                ccr = if ccr < 1 { 1 } else { ccr };\n            } else {\n                duty = Duty::Duty16_9;\n                ccr = clock / (frequency * 25);\n                ccr = if ccr < 1 { 1 } else { ccr };\n            }\n        }\n\n        Self {\n            freq: freq as u8,\n            trise: trise as u8,\n            ccr: ccr as u16,\n            duty,\n            mode,\n        }\n    }\n}\n\nimpl<'d, M: PeriMode> SetConfig for I2c<'d, M, Master> {\n    type Config = Hertz;\n    type ConfigError = ();\n    fn set_config(&mut self, config: &Self::Config) -> Result<(), ()> {\n        let timings = Timings::new(self.kernel_clock, *config);\n        self.info.regs.cr2().modify(|reg| {\n            reg.set_freq(timings.freq);\n        });\n        self.info.regs.ccr().modify(|reg| {\n            reg.set_f_s(timings.mode.f_s());\n            reg.set_duty(timings.duty.duty());\n            reg.set_ccr(timings.ccr);\n        });\n        self.info.regs.trise().modify(|reg| {\n            reg.set_trise(timings.trise);\n        });\n\n        Ok(())\n    }\n}\n"
  },
  {
    "path": "embassy-stm32/src/i2c/v2.rs",
    "content": "use core::cmp;\nuse core::future::poll_fn;\nuse core::task::Poll;\n\nuse config::{Address, OA2, OwnAddresses};\nuse embassy_embedded_hal::SetConfig;\nuse embassy_hal_internal::drop::OnDrop;\nuse embedded_hal_1::i2c::Operation;\nuse mode::{Master, MultiMaster};\nuse stm32_metapac::i2c::vals::{Addmode, Oamsk};\n\nuse super::*;\nuse crate::pac::i2c;\n\nimpl From<AddrMask> for Oamsk {\n    fn from(value: AddrMask) -> Self {\n        match value {\n            AddrMask::NOMASK => Oamsk::NO_MASK,\n            AddrMask::MASK1 => Oamsk::MASK1,\n            AddrMask::MASK2 => Oamsk::MASK2,\n            AddrMask::MASK3 => Oamsk::MASK3,\n            AddrMask::MASK4 => Oamsk::MASK4,\n            AddrMask::MASK5 => Oamsk::MASK5,\n            AddrMask::MASK6 => Oamsk::MASK6,\n            AddrMask::MASK7 => Oamsk::MASK7,\n        }\n    }\n}\n\nimpl Address {\n    pub(super) fn add_mode(&self) -> stm32_metapac::i2c::vals::Addmode {\n        match self {\n            Address::SevenBit(_) => stm32_metapac::i2c::vals::Addmode::BIT7,\n            Address::TenBit(_) => stm32_metapac::i2c::vals::Addmode::BIT10,\n        }\n    }\n}\n\nenum ReceiveResult {\n    DataAvailable,\n    StopReceived,\n    NewStart,\n}\n\nfn debug_print_interrupts(isr: stm32_metapac::i2c::regs::Isr) {\n    if isr.tcr() {\n        trace!(\"interrupt: tcr\");\n    }\n    if isr.tc() {\n        trace!(\"interrupt: tc\");\n    }\n    if isr.addr() {\n        trace!(\"interrupt: addr\");\n    }\n    if isr.stopf() {\n        trace!(\"interrupt: stopf\");\n    }\n    if isr.nackf() {\n        trace!(\"interrupt: nackf\");\n    }\n    if isr.berr() {\n        trace!(\"interrupt: berr\");\n    }\n    if isr.arlo() {\n        trace!(\"interrupt: arlo\");\n    }\n    if isr.ovr() {\n        trace!(\"interrupt: ovr\");\n    }\n}\n\npub(crate) unsafe fn on_interrupt<T: Instance>() {\n    let regs = T::info().regs;\n    let isr = regs.isr().read();\n\n    if isr.tcr() || isr.tc() || isr.addr() || isr.stopf() || isr.nackf() || isr.berr() || isr.arlo() || isr.ovr() {\n        debug_print_interrupts(isr);\n\n        T::state().waker.wake();\n    }\n\n    critical_section::with(|_| {\n        regs.cr1().modify(|w| {\n            w.set_addrie(false);\n            w.set_stopie(false);\n            // The flag can only be cleared by writting to nbytes, we won't do that here\n            w.set_tcie(false);\n            // Error flags are to be read in the routines, so we also don't clear them here\n            w.set_nackie(false);\n            w.set_errie(false);\n        });\n    });\n}\n\nimpl<'d, M: Mode, IM: MasterMode> I2c<'d, M, IM> {\n    #[inline]\n    fn to_reload(reload: bool) -> i2c::vals::Reload {\n        if reload {\n            i2c::vals::Reload::NOT_COMPLETED\n        } else {\n            i2c::vals::Reload::COMPLETED\n        }\n    }\n\n    /// Calculate total bytes in a group of operations\n    #[inline]\n    fn total_operation_bytes(operations: &[Operation<'_>]) -> usize {\n        operations\n            .iter()\n            .map(|op| match op {\n                Operation::Write(buf) => buf.len(),\n                Operation::Read(buf) => buf.len(),\n            })\n            .sum()\n    }\n\n    pub(crate) fn init(&mut self, config: Config) {\n        self.info.regs.cr1().modify(|reg| {\n            reg.set_pe(false);\n            reg.set_anfoff(false);\n        });\n\n        let timings = Timings::new(self.kernel_clock, config.frequency.into());\n\n        self.info.regs.timingr().write(|reg| {\n            reg.set_presc(timings.prescale);\n            reg.set_scll(timings.scll);\n            reg.set_sclh(timings.sclh);\n            reg.set_sdadel(timings.sdadel);\n            reg.set_scldel(timings.scldel);\n        });\n\n        self.info.regs.cr1().modify(|reg| {\n            reg.set_pe(true);\n        });\n    }\n\n    fn master_stop(&mut self) {\n        self.info.regs.cr2().write(|w| w.set_stop(true));\n    }\n\n    /// Toggle PE off/on to reset the I2C peripheral state machine.\n    /// TIMINGR and other config registers are preserved across this.\n    fn soft_reset(&self) {\n        self.info.regs.cr1().modify(|w| w.set_pe(false));\n        // PE needs a few APB cycles to actually clear\n        while self.info.regs.cr1().read().pe() {}\n        self.info.regs.cr1().modify(|w| w.set_pe(true));\n    }\n\n    fn master_read(\n        info: &'static Info,\n        address: Address,\n        length: usize,\n        stop: Stop,\n        reload: bool,\n        restart: bool,\n        timeout: Timeout,\n    ) -> Result<(), Error> {\n        assert!(length < 256);\n\n        if !restart {\n            // Wait for any previous address sequence to end\n            // automatically. This could be up to 50% of a bus\n            // cycle (ie. up to 0.5/freq)\n            while info.regs.cr2().read().start() {\n                timeout.check()?;\n            }\n        }\n\n        // Set START and prepare to receive bytes into\n        // `buffer`. The START bit can be set even if the bus\n        // is BUSY or I2C is in slave mode.\n\n        info.regs.cr2().modify(|w| {\n            w.set_sadd(address.addr() << 1);\n            w.set_add10(address.add_mode());\n            w.set_dir(i2c::vals::Dir::READ);\n            w.set_nbytes(length as u8);\n            w.set_start(true);\n            w.set_autoend(stop.autoend());\n            w.set_reload(Self::to_reload(reload));\n        });\n\n        Ok(())\n    }\n\n    fn master_write(\n        info: &'static Info,\n        address: Address,\n        length: usize,\n        stop: Stop,\n        reload: bool,\n        restart: bool,\n        timeout: Timeout,\n    ) -> Result<(), Error> {\n        assert!(length < 256);\n\n        if !restart {\n            // Wait for any previous address sequence to end\n            // automatically. This could be up to 50% of a bus\n            // cycle (ie. up to 0.5/freq)\n            while info.regs.cr2().read().start() {\n                timeout.check()?;\n            }\n\n            // Wait for the bus to be free\n            while info.regs.isr().read().busy() {\n                timeout.check()?;\n            }\n        }\n\n        // Set START and prepare to send `bytes`. The\n        // START bit can be set even if the bus is BUSY or\n        // I2C is in slave mode.\n        info.regs.cr2().modify(|w| {\n            w.set_sadd(address.addr() << 1);\n            w.set_add10(address.add_mode());\n            w.set_dir(i2c::vals::Dir::WRITE);\n            w.set_nbytes(length as u8);\n            w.set_start(true);\n            w.set_autoend(stop.autoend());\n            w.set_reload(Self::to_reload(reload));\n        });\n\n        Ok(())\n    }\n\n    fn reload(\n        info: &'static Info,\n        length: usize,\n        will_reload: bool,\n        stop: Stop,\n        timeout: Timeout,\n    ) -> Result<(), Error> {\n        assert!(length < 256 && length > 0);\n\n        // Wait for either TCR (Transfer Complete Reload) or TC (Transfer Complete)\n        // TCR occurs when RELOAD=1, TC occurs when RELOAD=0\n        // Both indicate the peripheral is ready for the next transfer\n        loop {\n            let isr = info.regs.isr().read();\n            if isr.tcr() || isr.tc() {\n                break;\n            }\n            timeout.check()?;\n        }\n\n        info.regs.cr2().modify(|w| {\n            w.set_nbytes(length as u8);\n            w.set_reload(Self::to_reload(will_reload));\n            w.set_autoend(stop.autoend());\n        });\n\n        Ok(())\n    }\n\n    fn flush_txdr(&self) {\n        if self.info.regs.isr().read().txis() {\n            trace!(\"Flush TXDATA with zeroes\");\n            self.info.regs.txdr().modify(|w| w.set_txdata(0));\n        }\n        if !self.info.regs.isr().read().txe() {\n            trace!(\"Flush TXDR\");\n            self.info.regs.isr().modify(|w| w.set_txe(true))\n        }\n    }\n\n    fn error_occurred(&self, isr: &i2c::regs::Isr, timeout: Timeout) -> Result<(), Error> {\n        if isr.nackf() {\n            trace!(\"NACK triggered.\");\n            self.info.regs.icr().modify(|reg| reg.set_nackcf(true));\n            // NACK should be followed by STOP\n            if let Ok(()) = self.wait_stop(timeout) {\n                trace!(\"Got STOP after NACK, clearing flag.\");\n                self.info.regs.icr().modify(|reg| reg.set_stopcf(true));\n            }\n            self.flush_txdr();\n            return Err(Error::Nack);\n        } else if isr.berr() {\n            trace!(\"BERR triggered.\");\n            self.info.regs.icr().modify(|reg| reg.set_berrcf(true));\n            self.flush_txdr();\n            self.soft_reset();\n            return Err(Error::Bus);\n        } else if isr.arlo() {\n            trace!(\"ARLO triggered.\");\n            self.info.regs.icr().modify(|reg| reg.set_arlocf(true));\n            self.flush_txdr();\n            self.soft_reset();\n            return Err(Error::Arbitration);\n        } else if isr.ovr() {\n            trace!(\"OVR triggered.\");\n            self.info.regs.icr().modify(|reg| reg.set_ovrcf(true));\n            return Err(Error::Overrun);\n        }\n        return Ok(());\n    }\n\n    fn wait_txis(&self, timeout: Timeout) -> Result<(), Error> {\n        let mut first_loop = true;\n\n        loop {\n            let isr = self.info.regs.isr().read();\n            self.error_occurred(&isr, timeout)?;\n            if isr.txis() {\n                trace!(\"TXIS\");\n                return Ok(());\n            }\n\n            {\n                if first_loop {\n                    trace!(\"Waiting for TXIS...\");\n                    first_loop = false;\n                }\n            }\n            timeout.check()?;\n        }\n    }\n\n    /// Wait for TXIS or NACK. Returns Ok(true) if TXIS, Ok(false) if NACK.\n    /// Used in slave transmit mode where NACK indicates master terminated early (not an error).\n    fn wait_txis_or_nack(&self, timeout: Timeout) -> Result<bool, Error> {\n        loop {\n            let isr = self.info.regs.isr().read();\n\n            // Check for actual errors (not NACK, which is expected in slave mode)\n            if isr.berr() {\n                trace!(\"BERR triggered.\");\n                self.info.regs.icr().modify(|reg| reg.set_berrcf(true));\n                self.flush_txdr();\n                self.soft_reset();\n                return Err(Error::Bus);\n            }\n            if isr.arlo() {\n                trace!(\"ARLO triggered.\");\n                self.info.regs.icr().modify(|reg| reg.set_arlocf(true));\n                self.flush_txdr();\n                self.soft_reset();\n                return Err(Error::Arbitration);\n            }\n            if isr.ovr() {\n                trace!(\"OVR triggered.\");\n                self.info.regs.icr().modify(|reg| reg.set_ovrcf(true));\n                self.flush_txdr();\n                return Err(Error::Overrun);\n            }\n\n            if isr.txis() {\n                trace!(\"TXIS\");\n                return Ok(true);\n            }\n\n            if isr.nackf() {\n                trace!(\"NACK received (master terminated early)\");\n                self.info.regs.icr().modify(|reg| reg.set_nackcf(true));\n                return Ok(false);\n            }\n\n            timeout.check()?;\n        }\n    }\n\n    fn wait_stop_or_err(&self, timeout: Timeout) -> Result<(), Error> {\n        loop {\n            let isr = self.info.regs.isr().read();\n            self.error_occurred(&isr, timeout)?;\n            if isr.stopf() {\n                trace!(\"STOP triggered.\");\n                self.info.regs.icr().modify(|reg| reg.set_stopcf(true));\n                return Ok(());\n            }\n            timeout.check()?;\n        }\n    }\n    fn wait_stop(&self, timeout: Timeout) -> Result<(), Error> {\n        loop {\n            let isr = self.info.regs.isr().read();\n            if isr.stopf() {\n                trace!(\"STOP triggered.\");\n                self.info.regs.icr().modify(|reg| reg.set_stopcf(true));\n                return Ok(());\n            }\n            timeout.check()?;\n        }\n    }\n\n    fn wait_af(&self, timeout: Timeout) -> Result<(), Error> {\n        loop {\n            let isr = self.info.regs.isr().read();\n            if isr.nackf() {\n                trace!(\"AF triggered.\");\n                self.info.regs.icr().modify(|reg| reg.set_nackcf(true));\n                return Ok(());\n            }\n            timeout.check()?;\n        }\n    }\n\n    fn wait_rxne(&self, timeout: Timeout) -> Result<ReceiveResult, Error> {\n        let mut first_loop = true;\n\n        loop {\n            let isr = self.info.regs.isr().read();\n            self.error_occurred(&isr, timeout)?;\n            if isr.stopf() {\n                trace!(\"STOP when waiting for RXNE.\");\n                if self.info.regs.isr().read().rxne() {\n                    trace!(\"Data received with STOP.\");\n                    return Ok(ReceiveResult::DataAvailable);\n                }\n                trace!(\"STOP triggered without data.\");\n                return Ok(ReceiveResult::StopReceived);\n            } else if isr.rxne() {\n                trace!(\"RXNE.\");\n                return Ok(ReceiveResult::DataAvailable);\n            } else if isr.addr() {\n                // Another addr event received, which means START was sent again\n                // which happens when accessing memory registers (common i2c interface design)\n                // e.g. master sends: START, write 1 byte (register index), START, read N bytes (until NACK)\n                // Possible to receive this flag at the same time as rxne, so check rxne first\n                trace!(\"START when waiting for RXNE. Ending receive loop.\");\n                // Return without clearing ADDR so `listen` can catch it\n                return Ok(ReceiveResult::NewStart);\n            }\n            {\n                if first_loop {\n                    trace!(\"Waiting for RXNE...\");\n                    first_loop = false;\n                }\n            }\n\n            timeout.check()?;\n        }\n    }\n\n    fn wait_tc(&self, timeout: Timeout) -> Result<(), Error> {\n        loop {\n            let isr = self.info.regs.isr().read();\n            self.error_occurred(&isr, timeout)?;\n            // Wait for either TC or TCR - both indicate transfer completion\n            // TCR occurs when RELOAD=1, TC occurs when RELOAD=0\n            if isr.tc() || isr.tcr() {\n                return Ok(());\n            }\n            timeout.check()?;\n        }\n    }\n\n    fn read_internal(\n        &mut self,\n        address: Address,\n        read: &mut [u8],\n        restart: bool,\n        timeout: Timeout,\n    ) -> Result<(), Error> {\n        let completed_chunks = read.len() / 255;\n        let total_chunks = if completed_chunks * 255 == read.len() {\n            completed_chunks\n        } else {\n            completed_chunks + 1\n        };\n        let last_chunk_idx = total_chunks.saturating_sub(1);\n\n        Self::master_read(\n            self.info,\n            address,\n            read.len().min(255),\n            Stop::Automatic,\n            last_chunk_idx != 0, // reload\n            restart,\n            timeout,\n        )?;\n\n        for (number, chunk) in read.chunks_mut(255).enumerate() {\n            if number != 0 {\n                Self::reload(\n                    self.info,\n                    chunk.len(),\n                    number != last_chunk_idx,\n                    Stop::Automatic,\n                    timeout,\n                )?;\n            }\n\n            for byte in chunk {\n                // Wait until we have received something\n                self.wait_rxne(timeout)?;\n\n                *byte = self.info.regs.rxdr().read().rxdata();\n            }\n        }\n        self.wait_stop(timeout)?;\n        Ok(())\n    }\n\n    fn write_internal(\n        &mut self,\n        address: Address,\n        write: &[u8],\n        send_stop: bool,\n        timeout: Timeout,\n    ) -> Result<(), Error> {\n        let completed_chunks = write.len() / 255;\n        let total_chunks = if completed_chunks * 255 == write.len() {\n            completed_chunks\n        } else {\n            completed_chunks + 1\n        };\n        let last_chunk_idx = total_chunks.saturating_sub(1);\n\n        // I2C start\n        //\n        // ST SAD+W\n        if let Err(err) = Self::master_write(\n            self.info,\n            address,\n            write.len().min(255),\n            Stop::Software,\n            last_chunk_idx != 0,\n            false, // restart\n            timeout,\n        ) {\n            if send_stop {\n                self.master_stop();\n            }\n            return Err(err);\n        }\n\n        for (number, chunk) in write.chunks(255).enumerate() {\n            if number != 0 {\n                Self::reload(\n                    self.info,\n                    chunk.len(),\n                    number != last_chunk_idx,\n                    Stop::Software,\n                    timeout,\n                )?;\n            }\n\n            for byte in chunk {\n                // Wait until we are allowed to send data\n                // (START has been ACKed or last byte when\n                // through)\n                if let Err(err) = self.wait_txis(timeout) {\n                    if send_stop && err != Error::Nack {\n                        // STOP is sent automatically if a NACK was received\n                        self.master_stop();\n                    }\n                    return Err(err);\n                }\n\n                self.info.regs.txdr().write(|w| w.set_txdata(*byte));\n            }\n        }\n        // Wait until the write finishes\n        self.wait_tc(timeout)?;\n        if send_stop {\n            self.master_stop();\n            self.wait_stop(timeout)?;\n        }\n\n        Ok(())\n    }\n\n    // =========================\n    //  Blocking public API\n\n    /// Blocking read.\n    pub fn blocking_read(&mut self, address: u8, read: &mut [u8]) -> Result<(), Error> {\n        self.read_internal(address.into(), read, false, self.timeout())\n        // Automatic Stop\n    }\n\n    /// Blocking write.\n    pub fn blocking_write(&mut self, address: u8, write: &[u8]) -> Result<(), Error> {\n        self.write_internal(address.into(), write, true, self.timeout())\n    }\n\n    /// Blocking write, restart, read.\n    pub fn blocking_write_read(&mut self, address: u8, write: &[u8], read: &mut [u8]) -> Result<(), Error> {\n        let timeout = self.timeout();\n        self.write_internal(address.into(), write, false, timeout)?;\n        self.read_internal(address.into(), read, true, timeout)\n        // Automatic Stop\n    }\n\n    /// Blocking transaction with operations.\n    ///\n    /// Consecutive operations of same type are merged. See [transaction contract] for details.\n    ///\n    /// [transaction contract]: embedded_hal_1::i2c::I2c::transaction\n    pub fn blocking_transaction(&mut self, addr: u8, operations: &mut [Operation<'_>]) -> Result<(), Error> {\n        if operations.is_empty() {\n            return Err(Error::ZeroLengthTransfer);\n        }\n\n        let address = addr.into();\n        let timeout = self.timeout();\n\n        // Group consecutive operations of the same type\n        let mut op_idx = 0;\n        let mut is_first_group = true;\n\n        while op_idx < operations.len() {\n            // Determine the type of current group and find all consecutive operations of same type\n            let is_read = matches!(operations[op_idx], Operation::Read(_));\n            let group_start = op_idx;\n\n            // Find end of this group (consecutive operations of same type)\n            while op_idx < operations.len() && matches!(operations[op_idx], Operation::Read(_)) == is_read {\n                op_idx += 1;\n            }\n            let group_end = op_idx;\n            let is_last_group = op_idx >= operations.len();\n\n            // Execute this group of operations\n            if is_read {\n                self.execute_read_group(\n                    address,\n                    &mut operations[group_start..group_end],\n                    is_first_group,\n                    is_last_group,\n                    timeout,\n                )?;\n            } else {\n                self.execute_write_group(\n                    address,\n                    &operations[group_start..group_end],\n                    is_first_group,\n                    is_last_group,\n                    timeout,\n                )?;\n            }\n\n            is_first_group = false;\n        }\n\n        Ok(())\n    }\n\n    fn execute_write_group(\n        &mut self,\n        address: Address,\n        operations: &[Operation<'_>],\n        is_first_group: bool,\n        is_last_group: bool,\n        timeout: Timeout,\n    ) -> Result<(), Error> {\n        // Calculate total bytes across all operations in this group\n        let total_bytes = Self::total_operation_bytes(operations);\n\n        if total_bytes == 0 {\n            // Handle empty write group - just send address\n            if is_first_group {\n                Self::master_write(self.info, address, 0, Stop::Software, false, !is_first_group, timeout)?;\n            }\n            if is_last_group {\n                self.master_stop();\n            }\n            return Ok(());\n        }\n\n        let mut total_remaining = total_bytes;\n        let mut first_chunk = true;\n\n        for operation in operations {\n            if let Operation::Write(buffer) = operation {\n                for chunk in buffer.chunks(255) {\n                    let chunk_len = chunk.len();\n                    total_remaining -= chunk_len;\n                    let is_last_chunk = total_remaining == 0;\n                    let will_reload = !is_last_chunk;\n\n                    if first_chunk {\n                        // First chunk: initiate transfer\n                        // If not first group, use RESTART instead of START\n                        Self::master_write(\n                            self.info,\n                            address,\n                            chunk_len,\n                            Stop::Software,\n                            will_reload,\n                            !is_first_group,\n                            timeout,\n                        )?;\n                        first_chunk = false;\n                    } else {\n                        // Subsequent chunks: use reload\n                        // Always use Software stop for writes\n                        Self::reload(self.info, chunk_len, will_reload, Stop::Software, timeout)?;\n                    }\n\n                    // Send data bytes\n                    for byte in chunk {\n                        self.wait_txis(timeout)?;\n                        self.info.regs.txdr().write(|w| w.set_txdata(*byte));\n                    }\n                }\n            }\n        }\n\n        // Wait for transfer to complete\n        if is_last_group {\n            self.wait_tc(timeout)?;\n            self.master_stop();\n            self.wait_stop(timeout)?;\n        } else {\n            // Wait for TC before next group (enables RESTART)\n            self.wait_tc(timeout)?;\n        }\n\n        Ok(())\n    }\n\n    fn execute_read_group(\n        &mut self,\n        address: Address,\n        operations: &mut [Operation<'_>],\n        is_first_group: bool,\n        is_last_group: bool,\n        timeout: Timeout,\n    ) -> Result<(), Error> {\n        // Calculate total bytes across all operations in this group\n        let total_bytes = Self::total_operation_bytes(operations);\n\n        if total_bytes == 0 {\n            // Handle empty read group\n            if is_first_group {\n                Self::master_read(\n                    self.info,\n                    address,\n                    0,\n                    if is_last_group { Stop::Automatic } else { Stop::Software },\n                    false, // reload\n                    !is_first_group,\n                    timeout,\n                )?;\n            }\n            if is_last_group {\n                self.wait_stop(timeout)?;\n            }\n            return Ok(());\n        }\n\n        let mut total_remaining = total_bytes;\n        let mut first_chunk = true;\n\n        for operation in operations {\n            if let Operation::Read(buffer) = operation {\n                for chunk in buffer.chunks_mut(255) {\n                    let chunk_len = chunk.len();\n                    total_remaining -= chunk_len;\n                    let is_last_chunk = total_remaining == 0;\n                    let will_reload = !is_last_chunk;\n\n                    if first_chunk {\n                        // First chunk: initiate transfer\n                        let stop = if is_last_group && is_last_chunk {\n                            Stop::Automatic\n                        } else {\n                            Stop::Software\n                        };\n\n                        Self::master_read(\n                            self.info,\n                            address,\n                            chunk_len,\n                            stop,\n                            will_reload,\n                            !is_first_group, // restart if not first group\n                            timeout,\n                        )?;\n                        first_chunk = false;\n                    } else {\n                        // Subsequent chunks: use reload\n                        let stop = if is_last_group && is_last_chunk {\n                            Stop::Automatic\n                        } else {\n                            Stop::Software\n                        };\n                        Self::reload(self.info, chunk_len, will_reload, stop, timeout)?;\n                    }\n\n                    // Receive data bytes\n                    for byte in chunk {\n                        self.wait_rxne(timeout)?;\n                        *byte = self.info.regs.rxdr().read().rxdata();\n                    }\n                }\n            }\n        }\n\n        // Wait for transfer to complete\n        if is_last_group {\n            self.wait_stop(timeout)?;\n        }\n        // For non-last read groups, don't wait for TC - the peripheral may hold SCL low\n        // in Software AUTOEND mode until the next START is issued. Just proceed directly\n        // to the next group which will issue RESTART and release the clock.\n\n        Ok(())\n    }\n\n    /// Blocking write multiple buffers.\n    ///\n    /// The buffers are concatenated in a single write transaction.\n    pub fn blocking_write_vectored(&mut self, address: u8, write: &[&[u8]]) -> Result<(), Error> {\n        if write.is_empty() {\n            return Err(Error::ZeroLengthTransfer);\n        }\n\n        let timeout = self.timeout();\n\n        let first_length = write[0].len();\n        let last_slice_index = write.len() - 1;\n\n        if let Err(err) = Self::master_write(\n            self.info,\n            address.into(),\n            first_length.min(255),\n            Stop::Software,\n            (first_length > 255) || (last_slice_index != 0),\n            false, // restart\n            timeout,\n        ) {\n            self.master_stop();\n            return Err(err);\n        }\n\n        for (idx, slice) in write.iter().enumerate() {\n            let slice_len = slice.len();\n            let completed_chunks = slice_len / 255;\n            let total_chunks = if completed_chunks * 255 == slice_len {\n                completed_chunks\n            } else {\n                completed_chunks + 1\n            };\n            let last_chunk_idx = total_chunks.saturating_sub(1);\n\n            if idx != 0 {\n                if let Err(err) = Self::reload(\n                    self.info,\n                    slice_len.min(255),\n                    (idx != last_slice_index) || (slice_len > 255),\n                    Stop::Software,\n                    timeout,\n                ) {\n                    if err != Error::Nack {\n                        self.master_stop();\n                    }\n                    return Err(err);\n                }\n            }\n\n            for (number, chunk) in slice.chunks(255).enumerate() {\n                if number != 0 {\n                    if let Err(err) = Self::reload(\n                        self.info,\n                        chunk.len(),\n                        (number != last_chunk_idx) || (idx != last_slice_index),\n                        Stop::Software,\n                        timeout,\n                    ) {\n                        if err != Error::Nack {\n                            self.master_stop();\n                        }\n                        return Err(err);\n                    }\n                }\n\n                for byte in chunk {\n                    // Wait until we are allowed to send data\n                    // (START has been ACKed or last byte when\n                    // through)\n                    if let Err(err) = self.wait_txis(timeout) {\n                        if err != Error::Nack {\n                            self.master_stop();\n                        }\n                        return Err(err);\n                    }\n\n                    // Put byte on the wire\n                    //self.i2c.txdr.write(|w| w.txdata().bits(*byte));\n                    self.info.regs.txdr().write(|w| w.set_txdata(*byte));\n                }\n            }\n        }\n        // Wait until the write finishes\n        self.wait_tc(timeout)?;\n        self.master_stop();\n        self.wait_stop(timeout)?;\n\n        Ok(())\n    }\n}\n\nimpl<'d, IM: MasterMode> I2c<'d, Async, IM> {\n    async fn write_dma_internal(\n        &mut self,\n        address: Address,\n        write: &[u8],\n        first_slice: bool,\n        last_slice: bool,\n        send_stop: bool,\n        restart: bool,\n        timeout: Timeout,\n    ) -> Result<(), Error> {\n        let total_len = write.len();\n\n        let dma_transfer = unsafe {\n            let regs = self.info.regs;\n            regs.cr1().modify(|w| {\n                w.set_txdmaen(true);\n                if first_slice {\n                    w.set_tcie(true);\n                }\n                w.set_nackie(true);\n                w.set_errie(true);\n            });\n            let dst = regs.txdr().as_ptr() as *mut u8;\n\n            self.tx_dma.as_mut().unwrap().write(write, dst, Default::default())\n        };\n\n        let mut remaining_len = total_len;\n\n        let on_drop = OnDrop::new(|| {\n            let regs = self.info.regs;\n            let isr = regs.isr().read();\n            regs.cr1().modify(|w| {\n                if last_slice || isr.nackf() || isr.arlo() || isr.berr() || isr.ovr() {\n                    w.set_txdmaen(false);\n                }\n                w.set_tcie(false);\n                w.set_nackie(false);\n                w.set_errie(false);\n            });\n            regs.icr().write(|w| {\n                w.set_nackcf(true);\n                w.set_berrcf(true);\n                w.set_arlocf(true);\n                w.set_ovrcf(true);\n            });\n            if isr.berr() || isr.arlo() {\n                regs.cr1().modify(|w| w.set_pe(false));\n                while regs.cr1().read().pe() {}\n                regs.cr1().modify(|w| w.set_pe(true));\n            }\n        });\n\n        poll_fn(|cx| {\n            self.state.waker.register(cx.waker());\n\n            let isr = self.info.regs.isr().read();\n\n            if isr.nackf() {\n                return Poll::Ready(Err(Error::Nack));\n            }\n            if isr.arlo() {\n                return Poll::Ready(Err(Error::Arbitration));\n            }\n            if isr.berr() {\n                return Poll::Ready(Err(Error::Bus));\n            }\n            if isr.ovr() {\n                return Poll::Ready(Err(Error::Overrun));\n            }\n\n            if remaining_len == total_len {\n                if first_slice {\n                    Self::master_write(\n                        self.info,\n                        address,\n                        total_len.min(255),\n                        Stop::Software,\n                        (total_len > 255) || !last_slice,\n                        restart,\n                        timeout,\n                    )?;\n                } else {\n                    Self::reload(\n                        self.info,\n                        total_len.min(255),\n                        (total_len > 255) || !last_slice,\n                        Stop::Software,\n                        timeout,\n                    )?;\n                    self.info.regs.cr1().modify(|w| w.set_tcie(true));\n                }\n            } else if !(isr.tcr() || isr.tc()) {\n                // poll_fn was woken without an interrupt present\n                return Poll::Pending;\n            } else if remaining_len == 0 {\n                return Poll::Ready(Ok(()));\n            } else {\n                if let Err(e) = Self::reload(\n                    self.info,\n                    remaining_len.min(255),\n                    (remaining_len > 255) || !last_slice,\n                    Stop::Software,\n                    timeout,\n                ) {\n                    return Poll::Ready(Err(e));\n                }\n                self.info.regs.cr1().modify(|w| w.set_tcie(true));\n            }\n\n            remaining_len = remaining_len.saturating_sub(255);\n            Poll::Pending\n        })\n        .await?;\n\n        dma_transfer.await;\n\n        // Always wait for TC after DMA completes - needed for consecutive buffers\n        self.wait_tc(timeout)?;\n\n        if last_slice & send_stop {\n            self.master_stop();\n        }\n\n        drop(on_drop);\n\n        Ok(())\n    }\n\n    async fn read_dma_internal(\n        &mut self,\n        address: Address,\n        buffer: &mut [u8],\n        restart: bool,\n        timeout: Timeout,\n    ) -> Result<(), Error> {\n        let total_len = buffer.len();\n\n        let dma_transfer = unsafe {\n            let regs = self.info.regs;\n            regs.cr1().modify(|w| {\n                w.set_rxdmaen(true);\n                w.set_tcie(true);\n                w.set_nackie(true);\n                w.set_errie(true);\n            });\n            let src = regs.rxdr().as_ptr() as *mut u8;\n\n            self.rx_dma.as_mut().unwrap().read(src, buffer, Default::default())\n        };\n\n        let mut remaining_len = total_len;\n\n        let on_drop = OnDrop::new(|| {\n            let regs = self.info.regs;\n            let isr = regs.isr().read();\n            regs.cr1().modify(|w| {\n                w.set_rxdmaen(false);\n                w.set_tcie(false);\n                w.set_nackie(false);\n                w.set_errie(false);\n            });\n            regs.icr().write(|w| {\n                w.set_nackcf(true);\n                w.set_berrcf(true);\n                w.set_arlocf(true);\n                w.set_ovrcf(true);\n            });\n            if isr.berr() || isr.arlo() {\n                regs.cr1().modify(|w| w.set_pe(false));\n                while regs.cr1().read().pe() {}\n                regs.cr1().modify(|w| w.set_pe(true));\n            }\n        });\n\n        poll_fn(|cx| {\n            self.state.waker.register(cx.waker());\n\n            let isr = self.info.regs.isr().read();\n\n            if isr.nackf() {\n                return Poll::Ready(Err(Error::Nack));\n            }\n            if isr.arlo() {\n                return Poll::Ready(Err(Error::Arbitration));\n            }\n            if isr.berr() {\n                return Poll::Ready(Err(Error::Bus));\n            }\n            if isr.ovr() {\n                return Poll::Ready(Err(Error::Overrun));\n            }\n\n            if remaining_len == total_len {\n                Self::master_read(\n                    self.info,\n                    address,\n                    total_len.min(255),\n                    Stop::Automatic,\n                    total_len > 255, // reload\n                    restart,\n                    timeout,\n                )?;\n                if total_len <= 255 {\n                    return Poll::Ready(Ok(()));\n                }\n            } else if isr.tcr() {\n                // Transfer Complete Reload - need to set up next chunk\n                let last_piece = remaining_len <= 255;\n\n                if let Err(e) = Self::reload(self.info, remaining_len.min(255), !last_piece, Stop::Automatic, timeout) {\n                    return Poll::Ready(Err(e));\n                }\n                // Return here if we are on last chunk,\n                // end of transfer will be awaited with the DMA below\n                if last_piece {\n                    return Poll::Ready(Ok(()));\n                }\n                self.info.regs.cr1().modify(|w| w.set_tcie(true));\n            } else {\n                // poll_fn was woken without TCR interrupt\n                return Poll::Pending;\n            }\n\n            remaining_len = remaining_len.saturating_sub(255);\n            Poll::Pending\n        })\n        .await?;\n\n        dma_transfer.await;\n        drop(on_drop);\n\n        Ok(())\n    }\n    // =========================\n    //  Async public API\n\n    /// Write.\n    pub async fn write(&mut self, address: u8, write: &[u8]) -> Result<(), Error> {\n        let _scoped_wake_guard = self.info.rcc.wake_guard();\n        let timeout = self.timeout();\n        if write.is_empty() {\n            self.write_internal(address.into(), write, true, timeout)\n        } else {\n            timeout\n                .with(self.write_dma_internal(address.into(), write, true, true, true, false, timeout))\n                .await\n        }\n    }\n\n    /// Write multiple buffers.\n    ///\n    /// The buffers are concatenated in a single write transaction.\n    pub async fn write_vectored(&mut self, address: Address, write: &[&[u8]]) -> Result<(), Error> {\n        let _scoped_wake_guard = self.info.rcc.wake_guard();\n        let timeout = self.timeout();\n\n        if write.is_empty() {\n            return Err(Error::ZeroLengthTransfer);\n        }\n\n        let mut iter = write.iter();\n        let mut first = true;\n        let mut current = iter.next();\n\n        while let Some(c) = current {\n            let next = iter.next();\n            let is_last = next.is_none();\n\n            let fut = self.write_dma_internal(\n                address, c, first,   // first_slice\n                is_last, // last_slice\n                is_last, // send_stop (only on last buffer)\n                false,   // restart (false for all - they're one continuous write)\n                timeout,\n            );\n            timeout.with(fut).await?;\n\n            first = false;\n            current = next;\n        }\n        Ok(())\n    }\n\n    /// Read.\n    pub async fn read(&mut self, address: u8, buffer: &mut [u8]) -> Result<(), Error> {\n        let _scoped_wake_guard = self.info.rcc.wake_guard();\n        let timeout = self.timeout();\n\n        if buffer.is_empty() {\n            self.read_internal(address.into(), buffer, false, timeout)\n        } else {\n            let fut = self.read_dma_internal(address.into(), buffer, false, timeout);\n            timeout.with(fut).await\n        }\n    }\n\n    /// Write, restart, read.\n    pub async fn write_read(&mut self, address: u8, write: &[u8], read: &mut [u8]) -> Result<(), Error> {\n        let _scoped_wake_guard = self.info.rcc.wake_guard();\n        let timeout = self.timeout();\n\n        if write.is_empty() {\n            self.write_internal(address.into(), write, false, timeout)?;\n        } else {\n            let fut = self.write_dma_internal(address.into(), write, true, true, false, false, timeout);\n            timeout.with(fut).await?;\n        }\n\n        if read.is_empty() {\n            self.read_internal(address.into(), read, true, timeout)?;\n        } else {\n            let fut = self.read_dma_internal(address.into(), read, true, timeout);\n            timeout.with(fut).await?;\n        }\n\n        Ok(())\n    }\n\n    /// Transaction with operations.\n    ///\n    /// Consecutive operations of same type are merged. See [transaction contract] for details.\n    ///\n    /// [transaction contract]: embedded_hal_1::i2c::I2c::transaction\n    pub async fn transaction(&mut self, addr: u8, operations: &mut [Operation<'_>]) -> Result<(), Error> {\n        let _scoped_wake_guard = self.info.rcc.wake_guard();\n        if operations.is_empty() {\n            return Err(Error::ZeroLengthTransfer);\n        }\n\n        let address = addr.into();\n        let timeout = self.timeout();\n\n        // Group consecutive operations of the same type\n        let mut op_idx = 0;\n        let mut is_first_group = true;\n\n        while op_idx < operations.len() {\n            // Determine the type of current group and find all consecutive operations of same type\n            let is_read = matches!(operations[op_idx], Operation::Read(_));\n            let group_start = op_idx;\n\n            // Find end of this group (consecutive operations of same type)\n            while op_idx < operations.len() && matches!(operations[op_idx], Operation::Read(_)) == is_read {\n                op_idx += 1;\n            }\n            let group_end = op_idx;\n            let is_last_group = op_idx >= operations.len();\n\n            // Execute this group of operations\n            if is_read {\n                self.execute_read_group_async(\n                    address,\n                    &mut operations[group_start..group_end],\n                    is_first_group,\n                    is_last_group,\n                    timeout,\n                )\n                .await?;\n            } else {\n                self.execute_write_group_async(\n                    address,\n                    &operations[group_start..group_end],\n                    is_first_group,\n                    is_last_group,\n                    timeout,\n                )\n                .await?;\n            }\n\n            is_first_group = false;\n        }\n\n        Ok(())\n    }\n\n    async fn execute_write_group_async(\n        &mut self,\n        address: Address,\n        operations: &[Operation<'_>],\n        is_first_group: bool,\n        is_last_group: bool,\n        timeout: Timeout,\n    ) -> Result<(), Error> {\n        // Calculate total bytes across all operations in this group\n        let total_bytes = Self::total_operation_bytes(operations);\n\n        if total_bytes == 0 {\n            // Handle empty write group using blocking call\n            if is_first_group {\n                Self::master_write(self.info, address, 0, Stop::Software, false, !is_first_group, timeout)?;\n            }\n            if is_last_group {\n                self.master_stop();\n            }\n            return Ok(());\n        }\n\n        // Collect all write buffers\n        let mut write_buffers: heapless::Vec<&[u8], 16> = heapless::Vec::new();\n        for operation in operations {\n            if let Operation::Write(buffer) = operation {\n                if !buffer.is_empty() {\n                    let _ = write_buffers.push(buffer);\n                }\n            }\n        }\n\n        if write_buffers.is_empty() {\n            return Ok(());\n        }\n\n        // Send each buffer using DMA\n        let num_buffers = write_buffers.len();\n        for (idx, buffer) in write_buffers.iter().enumerate() {\n            let is_first_buffer = idx == 0;\n            let is_last_buffer = idx == num_buffers - 1;\n\n            let fut = self.write_dma_internal(\n                address,\n                buffer,\n                is_first_buffer,                    // first_slice\n                is_last_buffer,                     // last_slice\n                is_last_buffer && is_last_group,    // send_stop\n                is_first_buffer && !is_first_group, // restart (only for first buffer if not first group)\n                timeout,\n            );\n            timeout.with(fut).await?;\n        }\n\n        Ok(())\n    }\n\n    async fn execute_read_group_async(\n        &mut self,\n        address: Address,\n        operations: &mut [Operation<'_>],\n        is_first_group: bool,\n        is_last_group: bool,\n        timeout: Timeout,\n    ) -> Result<(), Error> {\n        // Calculate total bytes across all operations in this group\n        let total_bytes = Self::total_operation_bytes(operations);\n\n        if total_bytes == 0 {\n            // Handle empty read group using blocking call\n            if is_first_group {\n                Self::master_read(\n                    self.info,\n                    address,\n                    0,\n                    if is_last_group { Stop::Automatic } else { Stop::Software },\n                    false, // reload\n                    !is_first_group,\n                    timeout,\n                )?;\n            }\n            if is_last_group {\n                self.wait_stop(timeout)?;\n            }\n            return Ok(());\n        }\n\n        // Use DMA for read operations - need to handle multiple buffers\n        let restart = !is_first_group;\n        let mut total_remaining = total_bytes;\n        let mut is_first_in_group = true;\n\n        for operation in operations {\n            if let Operation::Read(buffer) = operation {\n                if buffer.is_empty() {\n                    // Skip empty buffers\n                    continue;\n                }\n\n                let buf_len = buffer.len();\n                total_remaining -= buf_len;\n                let is_last_in_group = total_remaining == 0;\n\n                // Perform DMA read\n                if is_first_in_group {\n                    // First buffer: use read_dma_internal which handles restart properly\n                    // Only use Automatic stop if this is the last buffer in the last group\n                    let stop_mode = if is_last_group && is_last_in_group {\n                        Stop::Automatic\n                    } else {\n                        Stop::Software\n                    };\n\n                    // We need a custom DMA read that respects our stop mode\n                    self.read_dma_group_internal(address, buffer, restart, stop_mode, timeout)\n                        .await?;\n                    is_first_in_group = false;\n                } else {\n                    // Subsequent buffers: need to reload and continue\n                    let stop_mode = if is_last_group && is_last_in_group {\n                        Stop::Automatic\n                    } else {\n                        Stop::Software\n                    };\n\n                    self.read_dma_group_internal(\n                        address, buffer, false, // no restart for subsequent buffers in same group\n                        stop_mode, timeout,\n                    )\n                    .await?;\n                }\n            }\n        }\n\n        // Wait for transfer to complete\n        if is_last_group {\n            self.wait_stop(timeout)?;\n        }\n\n        Ok(())\n    }\n\n    /// Internal DMA read helper for transaction groups\n    async fn read_dma_group_internal(\n        &mut self,\n        address: Address,\n        buffer: &mut [u8],\n        restart: bool,\n        stop_mode: Stop,\n        timeout: Timeout,\n    ) -> Result<(), Error> {\n        let total_len = buffer.len();\n\n        let dma_transfer = unsafe {\n            let regs = self.info.regs;\n            regs.cr1().modify(|w| {\n                w.set_rxdmaen(true);\n                w.set_tcie(true);\n                w.set_nackie(true);\n                w.set_errie(true);\n            });\n            let src = regs.rxdr().as_ptr() as *mut u8;\n\n            self.rx_dma.as_mut().unwrap().read(src, buffer, Default::default())\n        };\n\n        let mut remaining_len = total_len;\n\n        let on_drop = OnDrop::new(|| {\n            let regs = self.info.regs;\n            let isr = regs.isr().read();\n            regs.cr1().modify(|w| {\n                w.set_rxdmaen(false);\n                w.set_tcie(false);\n                w.set_nackie(false);\n                w.set_errie(false);\n            });\n            regs.icr().write(|w| {\n                w.set_nackcf(true);\n                w.set_berrcf(true);\n                w.set_arlocf(true);\n                w.set_ovrcf(true);\n            });\n            if isr.berr() || isr.arlo() {\n                regs.cr1().modify(|w| w.set_pe(false));\n                while regs.cr1().read().pe() {}\n                regs.cr1().modify(|w| w.set_pe(true));\n            }\n        });\n\n        poll_fn(|cx| {\n            self.state.waker.register(cx.waker());\n\n            let isr = self.info.regs.isr().read();\n\n            if isr.nackf() {\n                return Poll::Ready(Err(Error::Nack));\n            }\n            if isr.arlo() {\n                return Poll::Ready(Err(Error::Arbitration));\n            }\n            if isr.berr() {\n                return Poll::Ready(Err(Error::Bus));\n            }\n            if isr.ovr() {\n                return Poll::Ready(Err(Error::Overrun));\n            }\n\n            if remaining_len == total_len {\n                Self::master_read(\n                    self.info,\n                    address,\n                    total_len.min(255),\n                    stop_mode,\n                    total_len > 255, // reload\n                    restart,\n                    timeout,\n                )?;\n                if total_len <= 255 {\n                    return Poll::Ready(Ok(()));\n                }\n            } else if isr.tcr() {\n                // Transfer Complete Reload - need to set up next chunk\n                let last_piece = remaining_len <= 255;\n\n                if let Err(e) = Self::reload(self.info, remaining_len.min(255), !last_piece, stop_mode, timeout) {\n                    return Poll::Ready(Err(e));\n                }\n                // Return here if we are on last chunk,\n                // end of transfer will be awaited with the DMA below\n                if last_piece {\n                    return Poll::Ready(Ok(()));\n                }\n                self.info.regs.cr1().modify(|w| w.set_tcie(true));\n            } else {\n                // poll_fn was woken without TCR interrupt\n                return Poll::Pending;\n            }\n\n            remaining_len = remaining_len.saturating_sub(255);\n            Poll::Pending\n        })\n        .await?;\n\n        dma_transfer.await;\n        drop(on_drop);\n\n        Ok(())\n    }\n}\n\nimpl<'d, M: Mode> I2c<'d, M, Master> {\n    /// Configure the I2C driver for slave operations, allowing for the driver to be used as a slave and a master (multimaster)\n    pub fn into_slave_multimaster(mut self, slave_addr_config: SlaveAddrConfig) -> I2c<'d, M, MultiMaster> {\n        let mut slave = I2c {\n            info: self.info,\n            state: self.state,\n            kernel_clock: self.kernel_clock,\n            tx_dma: self.tx_dma.take(),\n            rx_dma: self.rx_dma.take(),\n            #[cfg(feature = \"time\")]\n            timeout: self.timeout,\n            _phantom: PhantomData,\n            _phantom2: PhantomData,\n            _drop_guard: self._drop_guard,\n        };\n        slave.init_slave(slave_addr_config);\n        slave\n    }\n}\n\nimpl<'d, M: Mode> I2c<'d, M, MultiMaster> {\n    pub(crate) fn init_slave(&mut self, config: SlaveAddrConfig) {\n        self.info.regs.cr1().modify(|reg| {\n            reg.set_pe(false);\n        });\n\n        self.info.regs.cr1().modify(|reg| {\n            reg.set_nostretch(false);\n            reg.set_gcen(config.general_call);\n            reg.set_sbc(true);\n            reg.set_pe(true);\n        });\n\n        self.reconfigure_addresses(config.addr);\n    }\n\n    /// Configure the slave address.\n    pub fn reconfigure_addresses(&mut self, addresses: OwnAddresses) {\n        match addresses {\n            OwnAddresses::OA1(oa1) => self.configure_oa1(oa1),\n            OwnAddresses::OA2(oa2) => self.configure_oa2(oa2),\n            OwnAddresses::Both { oa1, oa2 } => {\n                self.configure_oa1(oa1);\n                self.configure_oa2(oa2);\n            }\n        }\n    }\n\n    fn configure_oa1(&mut self, oa1: Address) {\n        match oa1 {\n            Address::SevenBit(addr) => self.info.regs.oar1().write(|reg| {\n                reg.set_oa1en(false);\n                reg.set_oa1((addr << 1) as u16);\n                reg.set_oa1mode(Addmode::BIT7);\n                reg.set_oa1en(true);\n            }),\n            Address::TenBit(addr) => self.info.regs.oar1().write(|reg| {\n                reg.set_oa1en(false);\n                reg.set_oa1(addr);\n                reg.set_oa1mode(Addmode::BIT10);\n                reg.set_oa1en(true);\n            }),\n        }\n    }\n\n    fn configure_oa2(&mut self, oa2: OA2) {\n        self.info.regs.oar2().write(|reg| {\n            reg.set_oa2en(false);\n            reg.set_oa2msk(oa2.mask.into());\n            reg.set_oa2(oa2.addr << 1);\n            reg.set_oa2en(true);\n        });\n    }\n\n    fn determine_matched_address(&self) -> Result<Address, Error> {\n        let matched = self.info.regs.isr().read().addcode();\n\n        if matched >> 3 == 0b11110 {\n            // is 10-bit address and we need to get the other 8 bits from the rxdr\n            // we do this by doing a blocking read of 1 byte\n            let mut buffer = [0];\n            self.slave_read_internal(&mut buffer, self.timeout())?;\n            Ok(Address::TenBit((matched as u16) << 6 | buffer[0] as u16))\n        } else {\n            Ok(Address::SevenBit(matched))\n        }\n    }\n}\n\nimpl<'d, M: Mode> I2c<'d, M, MultiMaster> {\n    /// # Safety\n    /// This function will clear the address flag which will stop the clock stretching.\n    /// This should only be done after the dma transfer has been set up.\n    fn slave_start(info: &'static Info, length: usize, reload: bool) {\n        assert!(length < 256);\n\n        let reload = if reload {\n            i2c::vals::Reload::NOT_COMPLETED\n        } else {\n            i2c::vals::Reload::COMPLETED\n        };\n\n        info.regs.cr2().modify(|w| {\n            w.set_nbytes(length as u8);\n            w.set_reload(reload);\n        });\n\n        // clear the address flag, will stop the clock stretching.\n        // this should only be done after the dma transfer has been set up.\n        info.regs.icr().modify(|reg| reg.set_addrcf(true));\n        trace!(\"ADDRCF cleared (ADDR interrupt enabled, clock stretching ended)\");\n    }\n\n    /// Drains excess bytes from RXDR until STOP is received.\n    /// Returns the number of bytes discarded.\n    fn drain_rxdr_until_stop(&self, timeout: Timeout) -> Result<usize, Error> {\n        let mut discarded = 0;\n        loop {\n            let isr = self.info.regs.isr().read();\n\n            // Check for errors first\n            self.error_occurred(&isr, timeout)?;\n\n            if isr.stopf() {\n                self.info.regs.icr().write(|w| w.set_stopcf(true));\n                if discarded > 0 {\n                    trace!(\"Drained {} excess bytes\", discarded);\n                }\n                return Ok(discarded);\n            }\n\n            if isr.addr() {\n                // New START received (repeated start) - don't clear ADDR, let listen() handle it\n                trace!(\"New START during drain, ending receive\");\n                return Ok(discarded);\n            }\n\n            if isr.rxne() {\n                let _ = self.info.regs.rxdr().read().rxdata();\n                discarded += 1;\n            }\n\n            timeout.check()?;\n        }\n    }\n\n    // A blocking read operation for slave mode.\n    // Receives data into the provided buffer. If the master sends more data than the buffer\n    // can hold, excess bytes are acknowledged but discarded.\n    fn slave_read_internal(&self, read: &mut [u8], timeout: Timeout) -> Result<usize, Error> {\n        let completed_chunks = read.len() / 255;\n        let total_chunks = if completed_chunks * 255 == read.len() {\n            completed_chunks\n        } else {\n            completed_chunks + 1\n        };\n        let last_chunk_idx = total_chunks.saturating_sub(1);\n        let mut received = 0;\n        let mut stop_received = false;\n\n        'chunks: for (number, chunk) in read.chunks_mut(255).enumerate() {\n            trace!(\n                \"--- Slave RX transmission start - chunk: {}, expected (max) size: {}\",\n                number,\n                chunk.len()\n            );\n\n            // Check if STOP is pending before starting/reloading a new chunk\n            if number != 0 && self.info.regs.isr().read().stopf() {\n                trace!(\"STOP detected before chunk {}, ending receive\", number);\n                stop_received = true;\n                break;\n            }\n\n            if number == 0 {\n                Self::slave_start(self.info, chunk.len(), number != last_chunk_idx);\n            } else {\n                Self::reload(\n                    self.info,\n                    chunk.len(),\n                    number != last_chunk_idx,\n                    Stop::Software,\n                    timeout,\n                )?;\n            }\n\n            for (index, byte) in chunk.iter_mut().enumerate() {\n                // Wait until we have received something\n                match self.wait_rxne(timeout) {\n                    Ok(ReceiveResult::StopReceived) => {\n                        trace!(\"STOP received at byte {} of chunk {}\", index, number);\n                        stop_received = true;\n                        break 'chunks;\n                    }\n                    Ok(ReceiveResult::NewStart) => {\n                        trace!(\"--- Slave RX transmission end (new START)\");\n                        return Ok(received);\n                    }\n                    Ok(ReceiveResult::DataAvailable) => {\n                        *byte = self.info.regs.rxdr().read().rxdata();\n                        received += 1;\n                        trace!(\"Slave RX data {}: {:#04x}\", received - 1, byte);\n                    }\n                    Err(e) => return Err(e),\n                };\n            }\n        }\n\n        // If we haven't seen STOP yet, drain any excess bytes the master might send\n        if !stop_received {\n            self.drain_rxdr_until_stop(timeout)?;\n        } else {\n            // Clear the STOP flag if we detected it during receive\n            self.info.regs.icr().write(|w| w.set_stopcf(true));\n        }\n\n        trace!(\"--- Slave RX transmission end, received {} bytes\", received);\n        Ok(received)\n    }\n\n    // A blocking write operation for slave mode.\n    // Transmits data to the master. Returns SendStatus indicating whether all bytes were sent\n    // or if the master terminated early (sent NACK before all data was transmitted).\n    fn slave_write_internal(&mut self, write: &[u8], timeout: Timeout) -> Result<SendStatus, Error> {\n        let total_len = write.len();\n        let completed_chunks = total_len / 255;\n        let total_chunks = if completed_chunks * 255 == total_len {\n            completed_chunks\n        } else {\n            completed_chunks + 1\n        };\n        let last_chunk_idx = total_chunks.saturating_sub(1);\n        let mut bytes_sent = 0;\n\n        for (number, chunk) in write.chunks(255).enumerate() {\n            trace!(\n                \"--- Slave TX transmission start - chunk: {}, size: {}\",\n                number,\n                chunk.len()\n            );\n            if number == 0 {\n                Self::slave_start(self.info, chunk.len(), number != last_chunk_idx);\n            } else {\n                Self::reload(\n                    self.info,\n                    chunk.len(),\n                    number != last_chunk_idx,\n                    Stop::Software,\n                    timeout,\n                )?;\n            }\n\n            for (index, byte) in chunk.iter().enumerate() {\n                // Check for NACK before waiting - master may have terminated early\n                let isr = self.info.regs.isr().read();\n                if isr.nackf() {\n                    trace!(\"NACK received at byte {} of chunk {}\", index, number);\n                    self.info.regs.icr().modify(|reg| reg.set_nackcf(true));\n                    self.flush_txdr();\n                    self.wait_stop_or_err(timeout)?;\n                    let leftover = total_len - bytes_sent;\n                    trace!(\"--- Slave TX transmission end (early NACK), {} bytes unsent\", leftover);\n                    return Ok(SendStatus::LeftoverBytes(leftover));\n                }\n\n                // Wait until we are allowed to send data\n                // (START has been ACKed or last byte went through)\n                match self.wait_txis_or_nack(timeout) {\n                    Ok(true) => {\n                        // TXIS - ready to send\n                        trace!(\"Slave TX data {}: {:#04x}\", bytes_sent, byte);\n                        self.info.regs.txdr().write(|w| w.set_txdata(*byte));\n                        bytes_sent += 1;\n                    }\n                    Ok(false) => {\n                        // NACK - master terminated early\n                        self.flush_txdr();\n                        self.wait_stop_or_err(timeout)?;\n                        let leftover = total_len - bytes_sent;\n                        trace!(\"--- Slave TX transmission end (early NACK), {} bytes unsent\", leftover);\n                        return Ok(SendStatus::LeftoverBytes(leftover));\n                    }\n                    Err(e) => return Err(e),\n                }\n            }\n        }\n\n        // All bytes sent, wait for final NACK from master\n        self.wait_af(timeout)?;\n        self.flush_txdr();\n        self.wait_stop_or_err(timeout)?;\n\n        trace!(\"--- Slave TX transmission end, all {} bytes sent\", bytes_sent);\n        Ok(SendStatus::Done)\n    }\n\n    /// Listen for incoming I2C messages.\n    ///\n    /// This method blocks until the slave address is matched by a master.\n    pub fn blocking_listen(&mut self) -> Result<SlaveCommand, Error> {\n        let timeout = self.timeout();\n\n        self.info.regs.cr1().modify(|reg| {\n            reg.set_addrie(true);\n            trace!(\"Enable ADDRIE\");\n        });\n\n        loop {\n            let isr = self.info.regs.isr().read();\n            if isr.addr() {\n                break;\n            }\n            timeout.check()?;\n        }\n\n        trace!(\"ADDR triggered (address match)\");\n\n        // we do not clear the address flag here as it will be cleared by the dma read/write\n        // if we clear it here the clock stretching will stop and the master will read in data before the slave is ready to send it\n        self.slave_command()\n    }\n\n    /// Determine the received slave command.\n    fn slave_command(&self) -> Result<SlaveCommand, Error> {\n        let isr = self.info.regs.isr().read();\n\n        match isr.dir() {\n            i2c::vals::Dir::WRITE => {\n                trace!(\"DIR: write\");\n                Ok(SlaveCommand {\n                    kind: SlaveCommandKind::Write,\n                    address: self.determine_matched_address()?,\n                })\n            }\n            i2c::vals::Dir::READ => {\n                trace!(\"DIR: read\");\n                Ok(SlaveCommand {\n                    kind: SlaveCommandKind::Read,\n                    address: self.determine_matched_address()?,\n                })\n            }\n        }\n    }\n\n    /// Respond to a write command by receiving data from the master.\n    ///\n    /// Receives up to `buffer.len()` bytes from the master into the provided buffer.\n    /// If the master sends more data than the buffer can hold, excess bytes are\n    /// acknowledged but discarded.\n    ///\n    /// Returns the number of bytes actually stored in `buffer`.\n    pub fn blocking_respond_to_write(&self, buffer: &mut [u8]) -> Result<usize, Error> {\n        let timeout = self.timeout();\n        self.slave_read_internal(buffer, timeout)\n    }\n\n    /// Respond to a read command by transmitting data to the master.\n    ///\n    /// Transmits the provided data to the master. The master controls how many bytes\n    /// it reads by sending a NACK after the last byte it wants.\n    ///\n    /// Returns [`SendStatus::Done`] if all bytes were sent, or [`SendStatus::LeftoverBytes`]\n    /// if the master ended the transfer early (sent NACK before all data was transmitted).\n    pub fn blocking_respond_to_read(&mut self, write: &[u8]) -> Result<SendStatus, Error> {\n        let timeout = self.timeout();\n        self.slave_write_internal(write, timeout)\n    }\n}\n\nimpl<'d> I2c<'d, Async, MultiMaster> {\n    /// Listen for incoming I2C messages.\n    ///\n    /// The listen method is an asynchronous method but it does not require DMA to be asynchronous.\n    pub async fn listen(&mut self) -> Result<SlaveCommand, Error> {\n        let _scoped_wake_guard = self.info.rcc.wake_guard();\n        let state = self.state;\n        self.info.regs.cr1().modify(|reg| {\n            reg.set_addrie(true);\n            trace!(\"Enable ADDRIE\");\n        });\n\n        poll_fn(|cx| {\n            state.waker.register(cx.waker());\n            let isr = self.info.regs.isr().read();\n            if !isr.addr() {\n                Poll::Pending\n            } else {\n                trace!(\"ADDR triggered (address match)\");\n                // we do not clear the address flag here as it will be cleared by the dma read/write\n                // if we clear it here the clock stretching will stop and the master will read in data before the slave is ready to send it\n                Poll::Ready(self.slave_command())\n            }\n        })\n        .await\n    }\n\n    /// Respond to a write command by receiving data from the master.\n    ///\n    /// Receives up to `buffer.len()` bytes from the master into the provided buffer.\n    /// If the master sends more data than the buffer can hold, excess bytes are\n    /// acknowledged but discarded.\n    ///\n    /// Returns the number of bytes actually stored in `buffer`.\n    pub async fn respond_to_write(&mut self, buffer: &mut [u8]) -> Result<usize, Error> {\n        let _scoped_wake_guard = self.info.rcc.wake_guard();\n        let timeout = self.timeout();\n        timeout.with(self.read_dma_internal_slave(buffer, timeout)).await\n    }\n\n    /// Respond to a read request from an I2C master.\n    pub async fn respond_to_read(&mut self, write: &[u8]) -> Result<SendStatus, Error> {\n        let _scoped_wake_guard = self.info.rcc.wake_guard();\n        let timeout = self.timeout();\n        timeout.with(self.write_dma_internal_slave(write, timeout)).await\n    }\n\n    // for data reception in slave mode\n    //\n    // returns the total number of bytes received (stored in buffer)\n    // If master sends more data than buffer can hold, excess bytes are drained and discarded.\n    async fn read_dma_internal_slave(&mut self, buffer: &mut [u8], timeout: Timeout) -> Result<usize, Error> {\n        let total_len = buffer.len();\n        let mut remaining_len = total_len;\n\n        let regs = self.info.regs;\n\n        let mut dma_transfer = unsafe {\n            regs.cr1().modify(|w| {\n                w.set_rxdmaen(true);\n                w.set_stopie(true);\n                w.set_tcie(true);\n                w.set_addrie(true); // Enable to detect RESTART condition\n            });\n            let src = regs.rxdr().as_ptr() as *mut u8;\n\n            self.rx_dma.as_mut().unwrap().read(src, buffer, Default::default())\n        };\n\n        let state = self.state;\n\n        let on_drop = OnDrop::new(|| {\n            regs.cr1().modify(|w| {\n                w.set_rxdmaen(false);\n                w.set_stopie(false);\n                w.set_tcie(false);\n                w.set_addrie(false);\n            });\n        });\n\n        // Track whether STOP was received during DMA transfer\n        let mut stop_received = false;\n\n        let total_received = poll_fn(|cx| {\n            state.waker.register(cx.waker());\n\n            let isr = regs.isr().read();\n            // Check for STOP first - master may have finished early\n            if isr.stopf() {\n                remaining_len = remaining_len.saturating_add(dma_transfer.get_remaining_transfers() as usize);\n                regs.icr().write(|reg| reg.set_stopcf(true));\n                stop_received = true;\n                return Poll::Ready(Ok(total_len - remaining_len));\n            }\n\n            // Check for ADDR - indicates a RESTART condition (master sending new command)\n            // This happens in write_read transactions where master writes, then RESTARTs to read\n            // Don't clear ADDR flag - let listen() handle the new command\n            if isr.addr() && remaining_len != total_len {\n                // Only treat as RESTART if we've started receiving (remaining_len != total_len)\n                trace!(\"RESTART detected during slave receive\");\n                remaining_len = remaining_len.saturating_add(dma_transfer.get_remaining_transfers() as usize);\n                stop_received = true; // Prevent drain_rxdr_until_stop from running\n                return Poll::Ready(Ok(total_len - remaining_len));\n            }\n\n            if remaining_len == total_len {\n                Self::slave_start(self.info, total_len.min(255), total_len > 255);\n                remaining_len = remaining_len.saturating_sub(255);\n                // Re-enable interrupts that were disabled by the interrupt handler\n                regs.cr1().modify(|w| {\n                    w.set_tcie(true);\n                    w.set_stopie(true);\n                    w.set_addrie(true);\n                });\n                Poll::Pending\n            } else if isr.tcr() {\n                // Transfer Complete Reload - need to set up next chunk\n                let is_last_slice = remaining_len <= 255;\n                if let Err(e) = Self::reload(\n                    self.info,\n                    remaining_len.min(255),\n                    !is_last_slice,\n                    Stop::Software,\n                    timeout,\n                ) {\n                    return Poll::Ready(Err(e));\n                }\n                remaining_len = remaining_len.saturating_sub(255);\n                regs.cr1().modify(|w| {\n                    w.set_tcie(true);\n                    w.set_stopie(true);\n                    w.set_addrie(true);\n                });\n                Poll::Pending\n            } else if isr.tc() || dma_transfer.get_remaining_transfers() == 0 {\n                // Buffer is full - either TC fired or DMA completed all transfers.\n                // Note: TC is primarily a master mode flag; in slave mode we rely on DMA completion.\n                // Master may still be sending more data; we'll drain excess after returning.\n                Poll::Ready(Ok(total_len))\n            } else {\n                // No relevant flag set - re-enable interrupts in case they were\n                // disabled by the interrupt handler during a spurious wake\n                regs.cr1().modify(|w| {\n                    w.set_tcie(true);\n                    w.set_stopie(true);\n                    w.set_addrie(true);\n                });\n                Poll::Pending\n            }\n        })\n        .await?;\n\n        dma_transfer.request_pause();\n        dma_transfer.await;\n\n        // Disable DMA before potentially draining\n        regs.cr1().modify(|w| w.set_rxdmaen(false));\n\n        drop(on_drop);\n\n        // If STOP wasn't received during DMA, we need to drain any excess bytes\n        // the master might be sending\n        if !stop_received {\n            self.drain_rxdr_until_stop(timeout)?;\n        }\n\n        Ok(total_received)\n    }\n\n    async fn write_dma_internal_slave(&mut self, buffer: &[u8], timeout: Timeout) -> Result<SendStatus, Error> {\n        let total_len = buffer.len();\n        let mut remaining_len = total_len;\n\n        let mut dma_transfer = unsafe {\n            let regs = self.info.regs;\n            regs.cr1().modify(|w| {\n                w.set_txdmaen(true);\n                w.set_stopie(true);\n                w.set_tcie(true);\n            });\n            let dst = regs.txdr().as_ptr() as *mut u8;\n\n            self.tx_dma.as_mut().unwrap().write(buffer, dst, Default::default())\n        };\n\n        let on_drop = OnDrop::new(|| {\n            let regs = self.info.regs;\n            regs.cr1().modify(|w| {\n                w.set_txdmaen(false);\n                w.set_stopie(false);\n                w.set_tcie(false);\n            });\n            regs.isr().write(|w| w.set_txe(true));\n        });\n\n        let state = self.state;\n\n        let size = poll_fn(|cx| {\n            state.waker.register(cx.waker());\n\n            let isr = self.info.regs.isr().read();\n\n            if remaining_len == total_len {\n                Self::slave_start(self.info, total_len.min(255), total_len > 255);\n                remaining_len = remaining_len.saturating_sub(255);\n                self.info.regs.cr1().modify(|w| {\n                    w.set_tcie(true);\n                    w.set_stopie(true);\n                });\n                Poll::Pending\n            } else if isr.tcr() {\n                let is_last_slice = remaining_len <= 255;\n                if let Err(e) = Self::reload(\n                    self.info,\n                    remaining_len.min(255),\n                    !is_last_slice,\n                    Stop::Software,\n                    timeout,\n                ) {\n                    return Poll::Ready(Err(e));\n                }\n                remaining_len = remaining_len.saturating_sub(255);\n                self.info.regs.cr1().modify(|w| {\n                    w.set_tcie(true);\n                    w.set_stopie(true);\n                });\n                Poll::Pending\n            } else if isr.stopf() {\n                let mut leftover_bytes = dma_transfer.get_remaining_transfers();\n                if !self.info.regs.isr().read().txe() {\n                    leftover_bytes = leftover_bytes.saturating_add(1);\n                }\n                remaining_len = remaining_len.saturating_add(leftover_bytes as usize);\n                // Clear both STOP and NACK flags - NACK is expected when master ends read\n                self.info.regs.icr().write(|reg| {\n                    reg.set_stopcf(true);\n                    reg.set_nackcf(true);\n                });\n                if remaining_len > 0 {\n                    dma_transfer.request_pause();\n                    Poll::Ready(Ok(SendStatus::LeftoverBytes(remaining_len as usize)))\n                } else {\n                    Poll::Ready(Ok(SendStatus::Done))\n                }\n            } else {\n                // Re-enable interrupts in case they were disabled by spurious wake\n                self.info.regs.cr1().modify(|w| {\n                    w.set_tcie(true);\n                    w.set_stopie(true);\n                });\n                Poll::Pending\n            }\n        })\n        .await?;\n\n        dma_transfer.request_pause();\n        dma_transfer.await;\n\n        drop(on_drop);\n\n        Ok(size)\n    }\n}\n\n/// I2C Stop Configuration\n///\n/// Peripheral options for generating the STOP condition\n#[derive(Copy, Clone, PartialEq)]\nenum Stop {\n    /// Software end mode: Must write register to generate STOP condition\n    Software,\n    /// Automatic end mode: A STOP condition is automatically generated once the\n    /// configured number of bytes have been transferred\n    Automatic,\n}\n\nimpl Stop {\n    fn autoend(&self) -> i2c::vals::Autoend {\n        match self {\n            Stop::Software => i2c::vals::Autoend::SOFTWARE,\n            Stop::Automatic => i2c::vals::Autoend::AUTOMATIC,\n        }\n    }\n}\n\nstruct Timings {\n    prescale: u8,\n    scll: u8,\n    sclh: u8,\n    sdadel: u8,\n    scldel: u8,\n}\n\nimpl Timings {\n    fn new(i2cclk: Hertz, frequency: Hertz) -> Self {\n        let i2cclk = i2cclk.0;\n        let frequency = frequency.0;\n        // Refer to RM0433 Rev 7 Figure 539 for setup and hold timing:\n        //\n        // t_I2CCLK = 1 / PCLK1\n        // t_PRESC  = (PRESC + 1) * t_I2CCLK\n        // t_SCLL   = (SCLL + 1) * t_PRESC\n        // t_SCLH   = (SCLH + 1) * t_PRESC\n        //\n        // t_SYNC1 + t_SYNC2 > 4 * t_I2CCLK\n        // t_SCL ~= t_SYNC1 + t_SYNC2 + t_SCLL + t_SCLH\n        let ratio = i2cclk / frequency;\n\n        // For the standard-mode configuration method, we must have a ratio of 4\n        // or higher\n        assert!(ratio >= 4, \"The I2C PCLK must be at least 4 times the bus frequency!\");\n\n        let (presc_reg, scll, sclh, sdadel, scldel) = if frequency > 100_000 {\n            // Fast-mode (Fm) or Fast-mode Plus (Fm+)\n            // here we pick SCLL + 1 = 2 * (SCLH + 1)\n\n            // Prescaler, 384 ticks for sclh/scll. Round up then subtract 1\n            let presc_reg = ((ratio - 1) / 384) as u8;\n            // ratio < 1200 by pclk 120MHz max., therefore presc < 16\n\n            // Actual precale value selected\n            let presc = (presc_reg + 1) as u32;\n\n            let sclh = ((ratio / presc) - 3) / 3;\n            let scll = (2 * (sclh + 1)) - 1;\n\n            let (sdadel, scldel) = if frequency > 400_000 {\n                // Fast-mode Plus (Fm+)\n                assert!(i2cclk >= 17_000_000); // See table in datsheet\n\n                let sdadel = i2cclk / 8_000_000 / presc;\n                let scldel = i2cclk / 4_000_000 / presc - 1;\n\n                (sdadel, scldel)\n            } else {\n                // Fast-mode (Fm)\n                assert!(i2cclk >= 8_000_000); // See table in datsheet\n\n                let sdadel = i2cclk / 4_000_000 / presc;\n                let scldel = i2cclk / 2_000_000 / presc - 1;\n\n                (sdadel, scldel)\n            };\n\n            (presc_reg, scll as u8, sclh as u8, sdadel as u8, scldel as u8)\n        } else {\n            // Standard-mode (Sm)\n            // here we pick SCLL = SCLH\n            assert!(i2cclk >= 2_000_000); // See table in datsheet\n\n            // Prescaler, 512 ticks for sclh/scll. Round up then\n            // subtract 1\n            let presc = (ratio - 1) / 512;\n            let presc_reg = cmp::min(presc, 15) as u8;\n\n            // Actual prescale value selected\n            let presc = (presc_reg + 1) as u32;\n\n            let sclh = ((ratio / presc) - 2) / 2;\n            let scll = sclh;\n\n            // Speed check\n            assert!(sclh < 256, \"The I2C PCLK is too fast for this bus frequency!\");\n\n            let sdadel = i2cclk / 2_000_000 / presc;\n            let scldel = i2cclk / 500_000 / presc - 1;\n\n            (presc_reg, scll as u8, sclh as u8, sdadel as u8, scldel as u8)\n        };\n\n        // Sanity check\n        assert!(presc_reg < 16);\n\n        // Keep values within reasonable limits for fast per_ck\n        let sdadel = cmp::max(sdadel, 2);\n        let scldel = cmp::max(scldel, 4);\n\n        //(presc_reg, scll, sclh, sdadel, scldel)\n        Self {\n            prescale: presc_reg,\n            scll,\n            sclh,\n            sdadel,\n            scldel,\n        }\n    }\n}\n\nimpl<'d, M: Mode> SetConfig for I2c<'d, M, Master> {\n    type Config = Hertz;\n    type ConfigError = ();\n    fn set_config(&mut self, config: &Self::Config) -> Result<(), ()> {\n        self.info.regs.cr1().modify(|reg| {\n            reg.set_pe(false);\n        });\n\n        let timings = Timings::new(self.kernel_clock, *config);\n\n        self.info.regs.timingr().write(|reg| {\n            reg.set_presc(timings.prescale);\n            reg.set_scll(timings.scll);\n            reg.set_sclh(timings.sclh);\n            reg.set_sdadel(timings.sdadel);\n            reg.set_scldel(timings.scldel);\n        });\n\n        self.info.regs.cr1().modify(|reg| {\n            reg.set_pe(true);\n        });\n\n        Ok(())\n    }\n}\n\nimpl<'d, M: Mode> SetConfig for I2c<'d, M, MultiMaster> {\n    type Config = (Hertz, SlaveAddrConfig);\n    type ConfigError = ();\n    fn set_config(&mut self, (config, addr_config): &Self::Config) -> Result<(), ()> {\n        let timings = Timings::new(self.kernel_clock, *config);\n        self.info.regs.timingr().write(|reg| {\n            reg.set_presc(timings.prescale);\n            reg.set_scll(timings.scll);\n            reg.set_sclh(timings.sclh);\n            reg.set_sdadel(timings.sdadel);\n            reg.set_scldel(timings.scldel);\n        });\n        self.init_slave(*addr_config);\n\n        Ok(())\n    }\n}\n"
  },
  {
    "path": "embassy-stm32/src/i2s.rs",
    "content": "//! Inter-IC Sound (I2S)\n\nuse embassy_futures::join::join;\nuse stm32_metapac::spi::vals;\n\nuse crate::Peri;\nuse crate::dma::{ChannelAndRequest, ReadableRingBuffer, TransferOptions, WritableRingBuffer, ringbuffer};\nuse crate::gpio::{AfType, Flex, OutputType, Speed};\nuse crate::mode::Async;\nuse crate::spi::mode::Master;\nuse crate::spi::{Config as SpiConfig, RegsExt as _, *};\nuse crate::time::Hertz;\n\n/// I2S mode\n#[derive(Copy, Clone)]\npub enum Mode {\n    /// Master mode\n    Master,\n    /// Slave mode\n    Slave,\n}\n\n/// I2S function\n#[derive(Copy, Clone)]\n#[allow(dead_code)]\nenum Function {\n    /// Transmit audio data\n    Transmit,\n    /// Receive audio data\n    Receive,\n    #[cfg(any(spi_v4, spi_v5))]\n\n    /// Transmit and Receive audio data\n    FullDuplex,\n}\n\n/// I2C standard\n#[derive(Copy, Clone)]\npub enum Standard {\n    /// Philips\n    Philips,\n    /// Most significant bit first.\n    MsbFirst,\n    /// Least significant bit first.\n    LsbFirst,\n    /// PCM with long sync.\n    PcmLongSync,\n    /// PCM with short sync.\n    PcmShortSync,\n}\n\n/// SAI error\n#[derive(Debug, PartialEq, Eq)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub enum Error {\n    /// `write` called on a SAI in receive mode.\n    NotATransmitter,\n    /// `read` called on a SAI in transmit mode.\n    NotAReceiver,\n    /// Overrun\n    Overrun,\n}\n\nimpl From<ringbuffer::Error> for Error {\n    fn from(#[allow(unused)] err: ringbuffer::Error) -> Self {\n        #[cfg(feature = \"defmt\")]\n        {\n            if err == ringbuffer::Error::DmaUnsynced {\n                defmt::error!(\"Ringbuffer broken invariants detected!\");\n            }\n        }\n        Self::Overrun\n    }\n}\n\nimpl Standard {\n    const fn i2sstd(&self) -> vals::I2sstd {\n        match self {\n            Standard::Philips => vals::I2sstd::PHILIPS,\n            Standard::MsbFirst => vals::I2sstd::MSB,\n            Standard::LsbFirst => vals::I2sstd::LSB,\n            Standard::PcmLongSync => vals::I2sstd::PCM,\n            Standard::PcmShortSync => vals::I2sstd::PCM,\n        }\n    }\n\n    const fn pcmsync(&self) -> vals::Pcmsync {\n        match self {\n            Standard::PcmLongSync => vals::Pcmsync::LONG,\n            _ => vals::Pcmsync::SHORT,\n        }\n    }\n}\n\n/// I2S data format.\n#[derive(Copy, Clone)]\npub enum Format {\n    /// 16 bit data length on 16 bit wide channel\n    Data16Channel16,\n    /// 16 bit data length on 32 bit wide channel\n    Data16Channel32,\n    /// 24 bit data length on 32 bit wide channel\n    Data24Channel32,\n    /// 32 bit data length on 32 bit wide channel\n    Data32Channel32,\n}\n\nimpl Format {\n    const fn datlen(&self) -> vals::Datlen {\n        match self {\n            Format::Data16Channel16 => vals::Datlen::BITS16,\n            Format::Data16Channel32 => vals::Datlen::BITS16,\n            Format::Data24Channel32 => vals::Datlen::BITS24,\n            Format::Data32Channel32 => vals::Datlen::BITS32,\n        }\n    }\n\n    const fn chlen(&self) -> vals::Chlen {\n        match self {\n            Format::Data16Channel16 => vals::Chlen::BITS16,\n            Format::Data16Channel32 => vals::Chlen::BITS32,\n            Format::Data24Channel32 => vals::Chlen::BITS32,\n            Format::Data32Channel32 => vals::Chlen::BITS32,\n        }\n    }\n}\n\n/// Clock polarity\n#[derive(Copy, Clone)]\npub enum ClockPolarity {\n    /// Low on idle.\n    IdleLow,\n    /// High on idle.\n    IdleHigh,\n}\n\nimpl ClockPolarity {\n    const fn ckpol(&self) -> vals::Ckpol {\n        match self {\n            ClockPolarity::IdleHigh => vals::Ckpol::IDLE_HIGH,\n            ClockPolarity::IdleLow => vals::Ckpol::IDLE_LOW,\n        }\n    }\n}\n\n/// [`I2S`] configuration.\n///\n///  - `MS`: `Master` or `Slave`\n///  - `TR`: `Transmit` or `Receive`\n///  - `STD`: I2S standard, eg `Philips`\n///  - `FMT`: Frame Format marker, eg `Data16Channel16`\n#[non_exhaustive]\n#[derive(Copy, Clone)]\npub struct Config {\n    /// Frequency\n    pub frequency: Hertz,\n    /// GPIO Speed\n    pub gpio_speed: Speed,\n    /// Mode\n    pub mode: Mode,\n    /// Which I2S standard to use.\n    pub standard: Standard,\n    /// Data format.\n    pub format: Format,\n    /// Clock polarity.\n    pub clock_polarity: ClockPolarity,\n    /// True to enable master clock output from this instance.\n    pub master_clock: bool,\n}\n\nimpl Default for Config {\n    fn default() -> Self {\n        Self {\n            frequency: Hertz::khz(48),\n            gpio_speed: Speed::VeryHigh,\n            mode: Mode::Master,\n            standard: Standard::Philips,\n            format: Format::Data16Channel16,\n            clock_polarity: ClockPolarity::IdleLow,\n            master_clock: true,\n        }\n    }\n}\n\n/// I2S driver writer. Useful for moving write functionality across tasks.\npub struct Writer<'s, 'd, W: Word>(&'s mut WritableRingBuffer<'d, W>);\n\nimpl<'s, 'd, W: Word> Writer<'s, 'd, W> {\n    /// Write data to the I2S ringbuffer.\n    /// This appends the data to the buffer and returns immediately. The data will be transmitted in the background.\n    /// If thfre’s no space in the buffer, this waits until there is.\n    pub async fn write(&mut self, data: &[W]) -> Result<(), Error> {\n        self.0.write_exact(data).await?;\n        Ok(())\n    }\n\n    /// Reset the ring buffer to its initial state.\n    /// Can be used to recover from overrun.\n    /// The ringbuffer will always auto-reset on Overrun in any case.\n    ///\n    /// NOTE: This only clears the DMA buffer and is not synchronized to WS/LR clock, so the order\n    /// of channels may or may not be swapped after this. A full restart is required to ensure\n    /// buffer contents and I2S transmissions are in sync.\n    pub fn reset(&mut self) {\n        self.0.clear();\n    }\n}\n\n/// I2S driver reader. Useful for moving read functionality across tasks.\npub struct Reader<'s, 'd, W: Word>(&'s mut ReadableRingBuffer<'d, W>);\n\nimpl<'s, 'd, W: Word> Reader<'s, 'd, W> {\n    /// Read data from the I2S ringbuffer.\n    /// SAI is always receiving data in the background. This function pops already-received data from the buffer.\n    /// If there’s less than data.len() data in the buffer, this waits until there is.\n    pub async fn read(&mut self, data: &mut [W]) -> Result<(), Error> {\n        self.0.read_exact(data).await?;\n        Ok(())\n    }\n\n    /// Reset the ring buffer to its initial state.\n    /// Can be used to prevent overrun.\n    /// The ringbuffer will always auto-reset on Overrun in any case.\n    ///\n    /// After reset, the next read will automatically realign to a frame boundary,\n    /// discarding any partial frame at the current DMA position.\n    pub fn reset(&mut self) {\n        self.0.clear();\n    }\n}\n\n/// I2S driver.\npub struct I2S<'d, W: Word> {\n    #[allow(dead_code)]\n    mode: Mode,\n    spi: Spi<'d, Async, Master>,\n    _txsd: Option<Flex<'d>>,\n    _rxsd: Option<Flex<'d>>,\n    _ws: Option<Flex<'d>>,\n    _ck: Option<Flex<'d>>,\n    _mck: Option<Flex<'d>>,\n    tx_ring_buffer: Option<WritableRingBuffer<'d, W>>,\n    rx_ring_buffer: Option<ReadableRingBuffer<'d, W>>,\n}\n\nimpl<'d, W: Word> I2S<'d, W> {\n    /// Create a transmitter driver.\n    pub fn new_txonly<T: Instance, D1: TxDma<T>, #[cfg(afio)] A>(\n        peri: Peri<'d, T>,\n        sd: Peri<'d, if_afio!(impl I2sSdPin<T, A>)>,\n        ws: Peri<'d, if_afio!(impl WsPin<T, A>)>,\n        ck: Peri<'d, if_afio!(impl CkPin<T, A>)>,\n        mck: Peri<'d, if_afio!(impl MckPin<T, A>)>,\n        txdma: Peri<'d, D1>,\n        txdma_buf: &'d mut [W],\n        _irq: impl crate::interrupt::typelevel::Binding<D1::Interrupt, crate::dma::InterruptHandler<D1>> + 'd,\n        config: Config,\n    ) -> Self {\n        Self::new_inner(\n            peri,\n            new_pin!(sd, AfType::output(OutputType::PushPull, Speed::VeryHigh)),\n            None,\n            ws,\n            ck,\n            new_pin!(mck, AfType::output(OutputType::PushPull, Speed::VeryHigh)),\n            new_dma!(txdma, _irq).map(|d| (d, txdma_buf)),\n            None,\n            config,\n            Function::Transmit,\n        )\n    }\n\n    /// Create a transmitter driver without a master clock pin.\n    pub fn new_txonly_nomck<T: Instance, D1: TxDma<T>, #[cfg(afio)] A>(\n        peri: Peri<'d, T>,\n        sd: Peri<'d, if_afio!(impl I2sSdPin<T, A>)>,\n        ws: Peri<'d, if_afio!(impl WsPin<T, A>)>,\n        ck: Peri<'d, if_afio!(impl CkPin<T, A>)>,\n        txdma: Peri<'d, D1>,\n        txdma_buf: &'d mut [W],\n        _irq: impl crate::interrupt::typelevel::Binding<D1::Interrupt, crate::dma::InterruptHandler<D1>> + 'd,\n        config: Config,\n    ) -> Self {\n        Self::new_inner(\n            peri,\n            new_pin!(sd, AfType::output(OutputType::PushPull, Speed::VeryHigh)),\n            None,\n            ws,\n            ck,\n            None,\n            new_dma!(txdma, _irq).map(|d| (d, txdma_buf)),\n            None,\n            config,\n            Function::Transmit,\n        )\n    }\n\n    /// Create a receiver driver.\n    pub fn new_rxonly<T: Instance, D1: RxDma<T>, #[cfg(afio)] A>(\n        peri: Peri<'d, T>,\n        sd: Peri<'d, if_afio!(impl I2sSdPin<T, A>)>,\n        ws: Peri<'d, if_afio!(impl WsPin<T, A>)>,\n        ck: Peri<'d, if_afio!(impl CkPin<T, A>)>,\n        mck: Peri<'d, if_afio!(impl MckPin<T, A>)>,\n        rxdma: Peri<'d, D1>,\n        rxdma_buf: &'d mut [W],\n        _irq: impl crate::interrupt::typelevel::Binding<D1::Interrupt, crate::dma::InterruptHandler<D1>> + 'd,\n        config: Config,\n    ) -> Self {\n        Self::new_inner(\n            peri,\n            None,\n            new_pin!(sd, AfType::output(OutputType::PushPull, Speed::VeryHigh)),\n            ws,\n            ck,\n            new_pin!(mck, AfType::output(OutputType::PushPull, Speed::VeryHigh)),\n            None,\n            new_dma!(rxdma, _irq).map(|d| (d, rxdma_buf)),\n            config,\n            Function::Receive,\n        )\n    }\n\n    /// Create a receiver driver without a master clock pin.\n    pub fn new_rxonly_nomck<T: Instance, D1: RxDma<T>, #[cfg(afio)] A>(\n        peri: Peri<'d, T>,\n        sd: Peri<'d, if_afio!(impl I2sSdPin<T, A>)>,\n        ws: Peri<'d, if_afio!(impl WsPin<T, A>)>,\n        ck: Peri<'d, if_afio!(impl CkPin<T, A>)>,\n        rxdma: Peri<'d, D1>,\n        rxdma_buf: &'d mut [W],\n        _irq: impl crate::interrupt::typelevel::Binding<D1::Interrupt, crate::dma::InterruptHandler<D1>> + 'd,\n        config: Config,\n    ) -> Self {\n        Self::new_inner(\n            peri,\n            None,\n            new_pin!(sd, AfType::output(OutputType::PushPull, Speed::VeryHigh)),\n            ws,\n            ck,\n            None,\n            None,\n            new_dma!(rxdma, _irq).map(|d| (d, rxdma_buf)),\n            config,\n            Function::Receive,\n        )\n    }\n\n    #[cfg(any(spi_v4, spi_v5))]\n    /// Create a full duplex driver.\n    pub fn new_full_duplex<T: Instance, D1: TxDma<T>, D2: RxDma<T>, #[cfg(afio)] A>(\n        peri: Peri<'d, T>,\n        txsd: Peri<'d, if_afio!(impl MosiPin<T, A>)>,\n        rxsd: Peri<'d, if_afio!(impl MisoPin<T, A>)>,\n        ws: Peri<'d, if_afio!(impl WsPin<T, A>)>,\n        ck: Peri<'d, if_afio!(impl CkPin<T, A>)>,\n        mck: Peri<'d, if_afio!(impl MckPin<T, A>)>,\n        txdma: Peri<'d, D1>,\n        txdma_buf: &'d mut [W],\n        rxdma: Peri<'d, D2>,\n        rxdma_buf: &'d mut [W],\n        _irq: impl crate::interrupt::typelevel::Binding<D1::Interrupt, crate::dma::InterruptHandler<D1>>\n        + crate::interrupt::typelevel::Binding<D2::Interrupt, crate::dma::InterruptHandler<D2>>\n        + 'd,\n        config: Config,\n    ) -> Self {\n        Self::new_inner(\n            peri,\n            new_pin!(txsd, AfType::output(OutputType::PushPull, Speed::VeryHigh)),\n            new_pin!(rxsd, AfType::output(OutputType::PushPull, Speed::VeryHigh)),\n            ws,\n            ck,\n            new_pin!(mck, AfType::output(OutputType::PushPull, Speed::VeryHigh)),\n            new_dma!(txdma, _irq).map(|d| (d, txdma_buf)),\n            new_dma!(rxdma, _irq).map(|d| (d, rxdma_buf)),\n            config,\n            Function::FullDuplex,\n        )\n    }\n\n    #[cfg(any(spi_v4, spi_v5))]\n    /// Create a full duplex driver without a master clock pin.\n    pub fn new_full_duplex_nomck<T: Instance, D1: TxDma<T>, D2: RxDma<T>, #[cfg(afio)] A>(\n        peri: Peri<'d, T>,\n        txsd: Peri<'d, if_afio!(impl MosiPin<T, A>)>,\n        rxsd: Peri<'d, if_afio!(impl MisoPin<T, A>)>,\n        ws: Peri<'d, if_afio!(impl WsPin<T, A>)>,\n        ck: Peri<'d, if_afio!(impl CkPin<T, A>)>,\n        txdma: Peri<'d, D1>,\n        txdma_buf: &'d mut [W],\n        rxdma: Peri<'d, D2>,\n        rxdma_buf: &'d mut [W],\n        _irq: impl crate::interrupt::typelevel::Binding<D1::Interrupt, crate::dma::InterruptHandler<D1>>\n        + crate::interrupt::typelevel::Binding<D2::Interrupt, crate::dma::InterruptHandler<D2>>\n        + 'd,\n        config: Config,\n    ) -> Self {\n        Self::new_inner(\n            peri,\n            new_pin!(txsd, AfType::output(OutputType::PushPull, Speed::VeryHigh)),\n            new_pin!(rxsd, AfType::output(OutputType::PushPull, Speed::VeryHigh)),\n            ws,\n            ck,\n            None,\n            new_dma!(txdma, _irq).map(|d| (d, txdma_buf)),\n            new_dma!(rxdma, _irq).map(|d| (d, rxdma_buf)),\n            config,\n            Function::FullDuplex,\n        )\n    }\n\n    /// Start I2S driver.\n    pub fn start(&mut self) {\n        self.spi.info.regs.cr1().modify(|w| {\n            w.set_spe(false);\n        });\n        self.spi.set_word_size(W::CONFIG);\n        if let Some(tx_ring_buffer) = &mut self.tx_ring_buffer {\n            tx_ring_buffer.start();\n\n            set_txdmaen(self.spi.info.regs, true);\n        }\n        if let Some(rx_ring_buffer) = &mut self.rx_ring_buffer {\n            rx_ring_buffer.start();\n            // SPIv3 clears rxfifo on SPE=0\n            #[cfg(not(any(spi_v4, spi_v5, spi_v6)))]\n            flush_rx_fifo(self.spi.info.regs);\n\n            set_rxdmaen(self.spi.info.regs, true);\n        }\n        self.spi.info.regs.cr1().modify(|w| {\n            w.set_spe(true);\n        });\n        #[cfg(any(spi_v1, spi_v2, spi_v3))]\n        self.spi.info.regs.i2scfgr().modify(|w| {\n            w.set_i2se(true);\n        });\n        #[cfg(any(spi_v4, spi_v5, spi_v6))]\n        self.spi.info.regs.cr1().modify(|w| {\n            w.set_cstart(true);\n        });\n    }\n\n    /// Reset the ring buffer to its initial state.\n    /// Can be used to recover from overrun.\n    ///\n    /// After reset, the next RX read will automatically realign to a frame boundary,\n    /// discarding any partial frame at the current DMA position.\n    pub fn clear(&mut self) {\n        if let Some(rx_ring_buffer) = &mut self.rx_ring_buffer {\n            rx_ring_buffer.clear();\n        }\n        if let Some(tx_ring_buffer) = &mut self.tx_ring_buffer {\n            tx_ring_buffer.clear();\n        }\n    }\n\n    /// Stop I2S driver.\n    pub async fn stop(&mut self) {\n        let regs = self.spi.info.regs;\n\n        let tx_f = async {\n            if let Some(tx_ring_buffer) = &mut self.tx_ring_buffer {\n                tx_ring_buffer.stop().await;\n\n                set_txdmaen(regs, false);\n            }\n        };\n\n        let rx_f = async {\n            if let Some(rx_ring_buffer) = &mut self.rx_ring_buffer {\n                rx_ring_buffer.stop().await;\n\n                set_rxdmaen(regs, false);\n            }\n        };\n\n        join(rx_f, tx_f).await;\n\n        #[cfg(any(spi_v4, spi_v5, spi_v6))]\n        {\n            if let Mode::Master = self.mode {\n                regs.cr1().modify(|w| {\n                    w.set_csusp(true);\n                });\n\n                while regs.cr1().read().cstart() {}\n            }\n        }\n\n        regs.cr1().modify(|w| {\n            w.set_spe(false);\n        });\n\n        self.clear();\n    }\n\n    /// Split the driver into a Reader/Writer pair.\n    /// Useful for splitting the reader/writer functionality across tasks or\n    /// for calling the read/write methods in parallel.\n    pub fn split<'s>(&'s mut self) -> Result<(Reader<'s, 'd, W>, Writer<'s, 'd, W>), Error> {\n        match (&mut self.rx_ring_buffer, &mut self.tx_ring_buffer) {\n            (None, _) => Err(Error::NotAReceiver),\n            (_, None) => Err(Error::NotATransmitter),\n            (Some(rx_ring), Some(tx_ring)) => Ok((Reader(rx_ring), Writer(tx_ring))),\n        }\n    }\n\n    /// Read data from the I2S ringbuffer.\n    /// SAI is always receiving data in the background. This function pops already-received data from the buffer.\n    /// If there’s less than data.len() data in the buffer, this waits until there is.\n    pub async fn read(&mut self, data: &mut [W]) -> Result<(), Error> {\n        match &mut self.rx_ring_buffer {\n            Some(ring) => Reader(ring).read(data).await,\n            _ => Err(Error::NotAReceiver),\n        }\n    }\n\n    /// Write data to the I2S ringbuffer.\n    /// This appends the data to the buffer and returns immediately. The data will be transmitted in the background.\n    /// If thfre’s no space in the buffer, this waits until there is.\n    pub async fn write(&mut self, data: &[W]) -> Result<(), Error> {\n        match &mut self.tx_ring_buffer {\n            Some(ring) => Writer(ring).write(data).await,\n            _ => Err(Error::NotATransmitter),\n        }\n    }\n\n    /// Write data directly to the raw I2S ringbuffer.\n    /// This can be used to fill the buffer before starting the DMA transfer.\n    pub async fn write_immediate(&mut self, data: &[W]) -> Result<(usize, usize), Error> {\n        match &mut self.tx_ring_buffer {\n            Some(ring) => Ok(ring.write_immediate(data)?),\n            _ => return Err(Error::NotATransmitter),\n        }\n    }\n\n    fn new_inner<T: Instance, #[cfg(afio)] A>(\n        peri: Peri<'d, T>,\n        txsd: Option<Flex<'d>>,\n        rxsd: Option<Flex<'d>>,\n        ws: Peri<'d, if_afio!(impl WsPin<T, A>)>,\n        ck: Peri<'d, if_afio!(impl CkPin<T, A>)>,\n        mck: Option<Flex<'d>>,\n        txdma: Option<(ChannelAndRequest<'d>, &'d mut [W])>,\n        rxdma: Option<(ChannelAndRequest<'d>, &'d mut [W])>,\n        config: Config,\n        function: Function,\n    ) -> Self {\n        let spi = Spi::new_internal(peri, None, None, {\n            let mut spi_config = SpiConfig::default();\n            spi_config.frequency = config.frequency;\n            spi_config\n        });\n\n        let regs = T::info().regs;\n\n        #[cfg(any(all(rcc_f4, not(stm32f410)), rcc_f2, rcc_f7))]\n        let pclk = unsafe { crate::rcc::get_freqs() }.plli2s1_r.to_hertz().unwrap();\n        #[cfg(not(any(all(rcc_f4, not(stm32f410)), rcc_f2, rcc_f7)))]\n        let pclk = T::frequency();\n\n        let (odd, div) = compute_baud_rate(pclk, config.frequency, config.master_clock, config.format);\n\n        #[cfg(any(spi_v4, spi_v5))]\n        {\n            regs.cr1().modify(|w| w.set_spe(false));\n\n            reset_incompatible_bitfields::<T>();\n        }\n\n        use stm32_metapac::spi::vals::{I2scfg, Odd};\n\n        // 1. Select the I2SDIV[7:0] bits in the SPI_I2SPR/SPI_I2SCFGR register to define the serial clock baud\n        // rate to reach the proper audio sample frequency. The ODD bit in the\n        // SPI_I2SPR/SPI_I2SCFGR register also has to be defined.\n\n        // 2. Select the CKPOL bit to define the steady level for the communication clock. Set the\n        // MCKOE bit in the SPI_I2SPR/SPI_I2SCFGR register if the master clock MCK needs to be provided to\n        // the external DAC/ADC audio component (the I2SDIV and ODD values should be\n        // computed depending on the state of the MCK output, for more details refer to\n        // Section 28.4.4: Clock generator).\n\n        // 3. Set the I2SMOD bit in SPI_I2SCFGR to activate the I2S functionalities and choose the\n        // I2S standard through the I2SSTD[1:0] and PCMSYNC bits, the data length through the\n        // DATLEN[1:0] bits and the number of bits per channel by configuring the CHLEN bit.\n        // Select also the I2S master mode and direction (Transmitter or Receiver) through the\n        // I2SCFG[1:0] bits in the SPI_I2SCFGR register.\n\n        // 4. If needed, select all the potential interruption sources and the DMA capabilities by\n        // writing the SPI_CR2 register.\n\n        // 5. The I2SE bit in SPI_I2SCFGR register must be set.\n\n        let clk_reg = {\n            #[cfg(any(spi_v1, spi_v2, spi_v3))]\n            {\n                regs.i2spr()\n            }\n            #[cfg(any(spi_v4, spi_v5))]\n            {\n                regs.i2scfgr()\n            }\n        };\n\n        clk_reg.modify(|w| {\n            w.set_i2sdiv(div);\n            w.set_odd(match odd {\n                true => Odd::ODD,\n                false => Odd::EVEN,\n            });\n\n            w.set_mckoe(config.master_clock);\n        });\n\n        regs.i2scfgr().modify(|w| {\n            w.set_ckpol(config.clock_polarity.ckpol());\n\n            w.set_i2smod(true);\n\n            w.set_i2sstd(config.standard.i2sstd());\n            w.set_pcmsync(config.standard.pcmsync());\n\n            w.set_datlen(config.format.datlen());\n            w.set_chlen(config.format.chlen());\n\n            w.set_i2scfg(match (config.mode, function) {\n                (Mode::Master, Function::Transmit) => I2scfg::MASTER_TX,\n                (Mode::Master, Function::Receive) => I2scfg::MASTER_RX,\n                #[cfg(any(spi_v4, spi_v5))]\n                (Mode::Master, Function::FullDuplex) => I2scfg::MASTER_FULL_DUPLEX,\n                (Mode::Slave, Function::Transmit) => I2scfg::SLAVE_TX,\n                (Mode::Slave, Function::Receive) => I2scfg::SLAVE_RX,\n                #[cfg(any(spi_v4, spi_v5))]\n                (Mode::Slave, Function::FullDuplex) => I2scfg::SLAVE_FULL_DUPLEX,\n            });\n        });\n\n        let mut opts = TransferOptions::default();\n        opts.half_transfer_ir = true;\n\n        // Compute stereo frame size in DMA half-words for ring buffer alignment.\n        // 16-bit channel width: 1 half-word per channel × 2 channels = 2\n        // 32-bit channel width: 2 half-words per channel × 2 channels = 4\n        let frame_words = match config.format.chlen() {\n            vals::Chlen::BITS16 => 2,\n            vals::Chlen::BITS32 => 4,\n        };\n\n        Self {\n            mode: config.mode,\n            spi,\n            _txsd: txsd.map(|w| w.into()),\n            _rxsd: rxsd.map(|w| w.into()),\n            _ws: new_pin!(ws, AfType::output(OutputType::PushPull, config.gpio_speed)),\n            _ck: new_pin!(ck, AfType::output(OutputType::PushPull, config.gpio_speed)),\n            _mck: mck.map(|w| w.into()),\n            tx_ring_buffer: txdma\n                .map(|(ch, buf)| unsafe { WritableRingBuffer::new(ch.channel, ch.request, regs.tx_ptr(), buf, opts) }),\n            rx_ring_buffer: rxdma.map(|(ch, buf)| {\n                let mut rb = unsafe { ReadableRingBuffer::new(ch.channel, ch.request, regs.rx_ptr(), buf, opts) };\n                rb.set_alignment(frame_words);\n                rb\n            }),\n        }\n    }\n}\n\n// Note, calculation details:\n// Fs = i2s_clock / [256 * ((2 * div) + odd)] when master clock is enabled\n// Fs = i2s_clock / [(channel_length * 2) * ((2 * div) + odd)]` when master clock is disabled\n// channel_length is 16 or 32\n//\n// can be rewritten as\n// Fs = i2s_clock / (coef * division)\n// where coef is a constant equal to 256, 64 or 32 depending channel length and master clock\n// and where division = (2 * div) + odd\n//\n// Equation can be rewritten as\n// division = i2s_clock/ (coef * Fs)\n//\n// note: division = (2 * div) + odd = (div << 1) + odd\n// in other word, from bits point of view, division[8:1] = div[7:0] and division[0] = odd\nfn compute_baud_rate(i2s_clock: Hertz, request_freq: Hertz, mclk: bool, data_format: Format) -> (bool, u8) {\n    let coef = if mclk {\n        256\n    } else if let Format::Data16Channel16 = data_format {\n        32\n    } else {\n        64\n    };\n\n    let (n, d) = (i2s_clock.0, coef * request_freq.0);\n    let division = (n + (d >> 1)) / d;\n\n    if division < 4 {\n        (false, 2)\n    } else if division > 511 {\n        (true, 255)\n    } else {\n        ((division & 1) == 1, (division >> 1) as u8)\n    }\n}\n\n#[cfg(any(spi_v4, spi_v5))]\n\n// The STM32H7 reference manual specifies that any incompatible bitfields should be reset\n// to their reset values while operating in I2S mode.\nfn reset_incompatible_bitfields<T: Instance>() {\n    let regs = T::info().regs;\n    regs.cr1().modify(|w| {\n        let iolock = w.iolock();\n        let csusp = w.csusp();\n        let spe = w.cstart();\n        let cstart = w.cstart();\n        w.0 = 0;\n        w.set_iolock(iolock);\n        w.set_csusp(csusp);\n        w.set_spe(spe);\n        w.set_cstart(cstart);\n    });\n\n    regs.cr2().write(|w| w.0 = 0);\n\n    regs.cfg1().modify(|w| {\n        let txdmaen = w.txdmaen();\n        let rxdmaen = w.rxdmaen();\n        let fthlv = w.fthlv();\n        w.0 = 0;\n        w.set_txdmaen(txdmaen);\n        w.set_rxdmaen(rxdmaen);\n        w.set_fthlv(fthlv);\n    });\n\n    regs.cfg2().modify(|w| {\n        let afcntr = w.afcntr();\n        let lsbfirst = w.lsbfirst();\n        let ioswp = w.ioswp();\n        w.0 = 0;\n        w.set_afcntr(afcntr);\n        w.set_lsbfirst(lsbfirst);\n        w.set_ioswp(ioswp);\n    });\n\n    regs.ier().modify(|w| {\n        let tifreie = w.tifreie();\n        let ovrie = w.ovrie();\n        let udrie = w.udrie();\n        let txpie = w.txpie();\n        let rxpie = w.rxpie();\n\n        w.0 = 0;\n\n        w.set_tifreie(tifreie);\n        w.set_ovrie(ovrie);\n        w.set_udrie(udrie);\n        w.set_txpie(txpie);\n        w.set_rxpie(rxpie);\n    });\n\n    regs.ifcr().write(|w| {\n        w.set_suspc(true);\n        w.set_tifrec(true);\n        w.set_ovrc(true);\n        w.set_udrc(true);\n    });\n\n    regs.crcpoly().write(|w| w.0 = 0x107);\n    regs.txcrc().write(|w| w.0 = 0);\n    regs.rxcrc().write(|w| w.0 = 0);\n    regs.udrdr().write(|w| w.0 = 0);\n}\n"
  },
  {
    "path": "embassy-stm32/src/ipcc.rs",
    "content": "//! Inter-Process Communication Controller (IPCC)\n\nuse core::future::poll_fn;\nuse core::marker::PhantomData;\nuse core::sync::atomic::{Ordering, compiler_fence, fence};\nuse core::task::Poll;\n\nuse embassy_hal_internal::Peri;\nuse embassy_sync::waitqueue::AtomicWaker;\n\nuse crate::cpu::CoreId;\nuse crate::interrupt::typelevel::Interrupt;\nuse crate::peripherals::IPCC;\nuse crate::rcc::SealedRccPeripheral;\nuse crate::{interrupt, rcc};\n\n/// Interrupt handler.\n#[cfg(not(feature = \"_core-cm0p\"))]\npub struct ReceiveInterruptHandler {}\n\n#[cfg(not(feature = \"_core-cm0p\"))]\nimpl interrupt::typelevel::Handler<interrupt::typelevel::IPCC_C1_RX> for ReceiveInterruptHandler {\n    unsafe fn on_interrupt() {\n        let regs = IPCC::regs();\n        let core = CoreId::current();\n\n        trace!(\"ipcc: rx interrupt\");\n        // Status register gives channel occupied status. For rx, use the other.\n        let sr = regs.cpu(core.other().to_index()).sr().read();\n        regs.cpu(core.to_index()).mr().modify(|w| {\n            for index in 0..5 {\n                if sr.chf(index as usize) {\n                    // If bit is set to 1 then interrupt is disabled; we want to disable the interrupt\n                    w.set_chom(index as usize, true);\n\n                    // There shouldn't be a race because the channel is masked only if the interrupt has fired\n                    IPCC::state().rx_waker_for(index).wake();\n                }\n            }\n        })\n    }\n}\n\n/// TX interrupt handler.\n#[cfg(not(feature = \"_core-cm0p\"))]\npub struct TransmitInterruptHandler {}\n\n#[cfg(not(feature = \"_core-cm0p\"))]\nimpl interrupt::typelevel::Handler<interrupt::typelevel::IPCC_C1_TX> for TransmitInterruptHandler {\n    unsafe fn on_interrupt() {\n        let regs = IPCC::regs();\n        let core = CoreId::current();\n\n        trace!(\"ipcc: rx interrupt\");\n        // Status register gives channel occupied status. For tx, use cpu0.\n        let sr = regs.cpu(core.to_index()).sr().read();\n        regs.cpu(core.to_index()).mr().modify(|w| {\n            for index in 0..5 {\n                if !sr.chf(index as usize) {\n                    // If bit is set to 1 then interrupt is disabled; we want to disable the interrupt\n                    w.set_chfm(index as usize, true);\n\n                    // There shouldn't be a race because the channel is masked only if the interrupt has fired\n                    IPCC::state().tx_waker_for(index).wake();\n                }\n            }\n        });\n    }\n}\n\n/// TX/RX interrupt handler\n#[cfg(feature = \"_core-cm0p\")]\npub struct InterruptHandler {}\n\n#[cfg(feature = \"_core-cm0p\")]\nimpl interrupt::typelevel::Handler<interrupt::typelevel::IPCC_C2_RX_C2_TX> for InterruptHandler {\n    unsafe fn on_interrupt() {\n        let regs = IPCC::regs();\n        let core = CoreId::current();\n\n        // Status register gives channel occupied status. For rx, use the other.\n        let rx_sr = regs.cpu(core.other().to_index()).sr().read();\n        // Status register gives channel occupied status. For tx, use cpu0.\n        let tx_sr = regs.cpu(core.to_index()).sr().read();\n\n        regs.cpu(core.to_index()).mr().modify(|w| {\n            for index in 0..5 {\n                if rx_sr.chf(index as usize) {\n                    // If bit is set to 1 then interrupt is disabled; we want to disable the interrupt\n                    w.set_chom(index as usize, true);\n\n                    // There shouldn't be a race because the channel is masked only if the interrupt has fired\n                    IPCC::state().rx_waker_for(index).wake();\n                }\n                if !tx_sr.chf(index as usize) {\n                    // If bit is set to 1 then interrupt is disabled; we want to disable the interrupt\n                    w.set_chfm(index as usize, true);\n\n                    // There shouldn't be a race because the channel is masked only if the interrupt has fired\n                    IPCC::state().tx_waker_for(index).wake();\n                }\n            }\n        });\n    }\n}\n\n/// IPCC config.\n#[non_exhaustive]\n#[derive(Clone, Copy)]\npub struct Config {\n    /// Boot CPU2 is true (default is true)\n    #[cfg(not(feature = \"_core-cm0p\"))]\n    pub c2_boot: bool,\n}\n\nimpl Default for Config {\n    fn default() -> Self {\n        Self {\n            #[cfg(not(feature = \"_core-cm0p\"))]\n            c2_boot: true,\n        }\n    }\n}\n\n/// IPCC TX Channel\npub struct IpccTxChannel<'a> {\n    index: u8,\n    _lifetime: PhantomData<&'a mut usize>,\n}\n\nimpl<'a> IpccTxChannel<'a> {\n    pub(crate) const fn new(index: u8) -> Self {\n        core::assert!(index < 6);\n\n        Self {\n            index: index,\n            _lifetime: PhantomData,\n        }\n    }\n\n    /// Send data to an IPCC channel. The closure is called to write the data when appropriate.\n    pub async fn send(&mut self, f: impl FnOnce()) {\n        let regs = IPCC::regs();\n        let core = CoreId::current();\n\n        self.flush().await;\n\n        f();\n        fence(Ordering::Release);\n\n        trace!(\"ipcc: ch {}: send data\", self.index as u8);\n        regs.cpu(core.to_index())\n            .scr()\n            .write(|w| w.set_chs(self.index as usize, true));\n    }\n\n    /// Wait for the tx channel to become clear\n    pub async fn flush(&mut self) {\n        let _scoped_wake_guard = IPCC::RCC_INFO.wake_guard();\n        let regs = IPCC::regs();\n        let core = CoreId::current();\n\n        // This is a race, but is nice for debugging\n        if regs.cpu(core.to_index()).sr().read().chf(self.index as usize) {\n            trace!(\"ipcc: ch {}: wait for tx free\", self.index as u8);\n        }\n\n        poll_fn(|cx| {\n            IPCC::state().tx_waker_for(self.index).register(cx.waker());\n            // If bit is set to 1 then interrupt is disabled; we want to enable the interrupt\n            critical_section::with(|_| {\n                regs.cpu(core.to_index())\n                    .mr()\n                    .modify(|w| w.set_chfm(self.index as usize, false))\n            });\n\n            compiler_fence(Ordering::SeqCst);\n\n            if !regs.cpu(core.to_index()).sr().read().chf(self.index as usize) {\n                // If bit is set to 1 then interrupt is disabled; we want to disable the interrupt\n                critical_section::with(|_| {\n                    regs.cpu(core.to_index())\n                        .mr()\n                        .modify(|w| w.set_chfm(self.index as usize, true))\n                });\n\n                Poll::Ready(())\n            } else {\n                Poll::Pending\n            }\n        })\n        .await;\n    }\n}\n\n/// IPCC RX Channel\npub struct IpccRxChannel<'a> {\n    index: u8,\n    _lifetime: PhantomData<&'a mut usize>,\n}\n\nimpl<'a> IpccRxChannel<'a> {\n    pub(crate) const fn new(index: u8) -> Self {\n        core::assert!(index < 6);\n\n        Self {\n            index: index,\n            _lifetime: PhantomData,\n        }\n    }\n\n    /// Receive data from an IPCC channel. The closure is called to read the data when appropriate.\n    pub async fn receive<R>(&mut self, mut f: impl FnMut() -> Option<R>) -> R {\n        let _scoped_wake_guard = IPCC::RCC_INFO.wake_guard();\n        let regs = IPCC::regs();\n        let core = CoreId::current();\n\n        loop {\n            // This is a race, but is nice for debugging\n            if !regs.cpu(core.other().to_index()).sr().read().chf(self.index as usize) {\n                trace!(\"ipcc: ch {}: wait for rx occupied\", self.index as u8);\n            }\n\n            poll_fn(|cx| {\n                IPCC::state().rx_waker_for(self.index).register(cx.waker());\n                // If bit is set to 1 then interrupt is disabled; we want to enable the interrupt\n                critical_section::with(|_| {\n                    regs.cpu(core.to_index())\n                        .mr()\n                        .modify(|w| w.set_chom(self.index as usize, false))\n                });\n\n                compiler_fence(Ordering::SeqCst);\n\n                if regs.cpu(core.other().to_index()).sr().read().chf(self.index as usize) {\n                    // If bit is set to 1 then interrupt is disabled; we want to disable the interrupt\n                    critical_section::with(|_| {\n                        regs.cpu(core.to_index())\n                            .mr()\n                            .modify(|w| w.set_chom(self.index as usize, true))\n                    });\n\n                    Poll::Ready(())\n                } else {\n                    Poll::Pending\n                }\n            })\n            .await;\n\n            trace!(\"ipcc: ch {}: read data\", self.index as u8);\n\n            fence(Ordering::Acquire);\n            let ret = f();\n\n            trace!(\"ipcc: ch {}: clear rx\", self.index as u8);\n            compiler_fence(Ordering::SeqCst);\n            // If the channel is clear and the read function returns none, fetch more data\n            regs.cpu(core.to_index())\n                .scr()\n                .write(|w| w.set_chc(self.index as usize, true));\n            match ret {\n                Some(ret) => return ret,\n                None => {}\n            }\n        }\n    }\n}\n\n/// IPCC Channel\npub struct IpccChannel<'a> {\n    index: u8,\n    _lifetime: PhantomData<&'a mut usize>,\n}\n\nimpl<'a> IpccChannel<'a> {\n    pub(crate) const fn new(number: u8) -> Self {\n        core::assert!(number > 0 && number <= 6);\n\n        Self {\n            index: number - 1,\n            _lifetime: PhantomData,\n        }\n    }\n\n    /// Split into a tx and rx channel\n    pub const fn split(self) -> (IpccTxChannel<'a>, IpccRxChannel<'a>) {\n        (IpccTxChannel::new(self.index), IpccRxChannel::new(self.index))\n    }\n}\n\n/// IPCC driver.\npub struct Ipcc {\n    _private: (),\n}\n\nimpl Ipcc {\n    /// Creates a new Ipcc instance.\n    #[cfg(not(feature = \"_core-cm0p\"))]\n    pub fn new<'d>(\n        _peripheral: Peri<'d, crate::peripherals::IPCC>,\n        _irq: impl interrupt::typelevel::Binding<interrupt::typelevel::IPCC_C1_RX, ReceiveInterruptHandler>\n        + interrupt::typelevel::Binding<interrupt::typelevel::IPCC_C1_TX, TransmitInterruptHandler>\n        + 'd,\n        _config: Config,\n    ) -> Self {\n        rcc::enable_and_reset_without_stop::<IPCC>();\n        IPCC::set_cpu2(_config.c2_boot);\n\n        Self::init(_config);\n\n        // enable interrupts\n        crate::interrupt::typelevel::IPCC_C1_RX::unpend();\n        crate::interrupt::typelevel::IPCC_C1_TX::unpend();\n\n        unsafe { crate::interrupt::typelevel::IPCC_C1_RX::enable() };\n        unsafe { crate::interrupt::typelevel::IPCC_C1_TX::enable() };\n\n        Self { _private: () }\n    }\n\n    /// Creates a new Ipcc instance.\n    #[cfg(feature = \"_core-cm0p\")]\n    pub fn new<'d>(\n        _peripheral: Peri<'d, crate::peripherals::IPCC>,\n        _irq: impl interrupt::typelevel::Binding<interrupt::typelevel::IPCC_C2_RX_C2_TX, InterruptHandler> + 'd,\n        _config: Config,\n    ) -> Self {\n        rcc::enable_and_reset_without_stop::<IPCC>();\n\n        Self::init(_config);\n\n        // enable interrupts\n        crate::interrupt::typelevel::IPCC_C2_RX_C2_TX::unpend();\n        unsafe { crate::interrupt::typelevel::IPCC_C2_RX_C2_TX::enable() };\n\n        Self { _private: () }\n    }\n\n    fn init(_config: Config) {\n        // Verify rfwkpsel is set\n        let _ = IPCC::frequency();\n\n        let regs = IPCC::regs();\n        let core = CoreId::current();\n\n        regs.cpu(core.to_index()).cr().modify(|w| {\n            w.set_rxoie(true);\n            w.set_txfie(true);\n        });\n    }\n\n    /// Split into a tx and rx channel\n    pub const fn split<'a>(self) -> [(IpccTxChannel<'a>, IpccRxChannel<'a>); 6] {\n        [\n            IpccChannel::new(1).split(),\n            IpccChannel::new(2).split(),\n            IpccChannel::new(3).split(),\n            IpccChannel::new(4).split(),\n            IpccChannel::new(5).split(),\n            IpccChannel::new(6).split(),\n        ]\n    }\n\n    /// Receive from a channel number\n    pub async unsafe fn receive<R>(number: u8, f: impl FnMut() -> Option<R>) -> R {\n        core::assert!(number > 0 && number <= 6);\n\n        IpccRxChannel::new(number - 1).receive(f).await\n    }\n\n    /// Send to a channel number\n    pub async unsafe fn send(number: u8, f: impl FnOnce()) {\n        core::assert!(number > 0 && number <= 6);\n\n        IpccTxChannel::new(number - 1).send(f).await\n    }\n\n    /// Send to a channel number\n    pub async unsafe fn flush(number: u8) {\n        core::assert!(number > 0 && number <= 6);\n\n        IpccTxChannel::new(number - 1).flush().await\n    }\n}\n\nimpl SealedInstance for crate::peripherals::IPCC {\n    fn regs() -> crate::pac::ipcc::Ipcc {\n        crate::pac::IPCC\n    }\n\n    #[cfg(not(feature = \"_core-cm0p\"))]\n    fn set_cpu2(enabled: bool) {\n        crate::pac::PWR.cr4().modify(|w| w.set_c2boot(enabled));\n    }\n\n    fn state() -> &'static State {\n        static STATE: State = State::new();\n        &STATE\n    }\n}\n\nstruct State {\n    rx_wakers: [AtomicWaker; 6],\n    tx_wakers: [AtomicWaker; 6],\n}\n\nimpl State {\n    const fn new() -> Self {\n        Self {\n            rx_wakers: [const { AtomicWaker::new() }; 6],\n            tx_wakers: [const { AtomicWaker::new() }; 6],\n        }\n    }\n\n    const fn rx_waker_for(&self, index: u8) -> &AtomicWaker {\n        &self.rx_wakers[index as usize]\n    }\n\n    const fn tx_waker_for(&self, index: u8) -> &AtomicWaker {\n        &self.tx_wakers[index as usize]\n    }\n}\n\ntrait SealedInstance: crate::rcc::RccPeripheral {\n    fn regs() -> crate::pac::ipcc::Ipcc;\n    #[cfg(not(feature = \"_core-cm0p\"))]\n    fn set_cpu2(enabled: bool);\n    fn state() -> &'static State;\n}\n"
  },
  {
    "path": "embassy-stm32/src/lcd.rs",
    "content": "//! LCD\nuse core::marker::PhantomData;\n\nuse embassy_hal_internal::{Peri, PeripheralType};\n\nuse crate::gpio::{AfType, Flex, SealedPin};\nuse crate::peripherals;\nuse crate::rcc::{self, RccPeripheral};\nuse crate::time::Hertz;\n\n#[cfg(any(stm32u0, stm32l073, stm32l083))]\nconst NUM_SEGMENTS: u8 = 52;\n#[cfg(any(stm32wb, stm32l4x6, stm32l15x, stm32l162, stm32l4x3, stm32l4x6))]\nconst NUM_SEGMENTS: u8 = 44;\n#[cfg(any(stm32l053, stm32l063, stm32l100))]\nconst NUM_SEGMENTS: u8 = 32;\n\n/// LCD configuration struct\n#[non_exhaustive]\n#[derive(Debug, Clone, Copy)]\npub struct Config {\n    #[cfg(lcd_v2)]\n    /// Enable the voltage output buffer for higher driving capability.\n    ///\n    /// The LCD driving capability is improved as buffers prevent the LCD capacitive loads from loading the resistor\n    /// bridge unacceptably and interfering with its voltage generation.\n    pub use_voltage_output_buffer: bool,\n    /// Enable SEG pin remapping. SEG[31:28] multiplexed with SEG[43:40]\n    pub use_segment_muxing: bool,\n    /// Bias selector\n    pub bias: Bias,\n    /// Duty selector\n    pub duty: Duty,\n    /// Internal or external voltage source\n    pub voltage_source: VoltageSource,\n    /// The frequency used to update the LCD with.\n    /// Should be between ~30 and ~100. Lower is better for power consumption, but has lower visual fidelity.\n    pub target_fps: Hertz,\n    /// LCD driver selector\n    pub drive: Drive,\n}\n\nimpl Default for Config {\n    fn default() -> Self {\n        Self {\n            #[cfg(lcd_v2)]\n            use_voltage_output_buffer: false,\n            use_segment_muxing: false,\n            bias: Default::default(),\n            duty: Default::default(),\n            voltage_source: Default::default(),\n            target_fps: Hertz(60),\n            drive: Drive::Medium,\n        }\n    }\n}\n\n/// The number of voltage levels used when driving an LCD.\n/// Your LCD datasheet should tell you what to use.\n#[repr(u8)]\n#[derive(Debug, Default, Clone, Copy)]\npub enum Bias {\n    /// 1/4 bias\n    #[default]\n    Quarter = 0b00,\n    /// 1/2 bias\n    Half = 0b01,\n    /// 1/3 bias\n    Third = 0b10,\n}\n\n/// The duty used by the LCD driver.\n///\n/// This is essentially how many COM pins you're using.\n#[repr(u8)]\n#[derive(Debug, Default, Clone, Copy)]\npub enum Duty {\n    #[default]\n    /// Use a single COM pin\n    Static = 0b000,\n    /// Use two COM pins\n    Half = 0b001,\n    /// Use three COM pins\n    Third = 0b010,\n    /// Use four COM pins\n    Quarter = 0b011,\n    /// Use eight COM pins.\n    ///\n    /// In this mode, `COM[7:4]` outputs are available on `SEG[51:48]`.\n    /// This allows reducing the number of available segments.\n    Eigth = 0b100,\n}\n\nimpl Duty {\n    fn num_com_pins(&self) -> u8 {\n        match self {\n            Duty::Static => 1,\n            Duty::Half => 2,\n            Duty::Third => 3,\n            Duty::Quarter => 4,\n            Duty::Eigth => 8,\n        }\n    }\n}\n\n/// Whether to use the internal or external voltage source to drive the LCD\n#[repr(u8)]\n#[derive(Debug, Default, Clone, Copy)]\npub enum VoltageSource {\n    #[default]\n    /// Voltage stepup converter\n    Internal,\n    /// VLCD pin\n    External,\n}\n\n/// Defines the pulse duration in terms of ck_ps pulses.\n///\n/// A short pulse leads to lower power consumption, but displays with high internal resistance\n/// may need a longer pulse to achieve satisfactory contrast.\n/// Note that the pulse is never longer than one half prescaled LCD clock period.\n///\n/// Displays with high internal resistance may need a longer drive time to achieve satisfactory contrast.\n/// `PermanentHighDrive` is useful in this case if some additional power consumption can be tolerated.\n///\n/// Basically, for power usage, you want this as low as possible while still being able to use the LCD\n/// with a good enough contrast.\n#[repr(u8)]\n#[derive(Debug, Clone, Copy)]\npub enum Drive {\n    /// Zero clock pulse on duration\n    Lowest = 0x00,\n    /// One clock pulse on duration\n    VeryLow = 0x01,\n    /// Two clock pulse on duration\n    Low = 0x02,\n    /// Three clock pulse on duration\n    Medium = 0x03,\n    /// Four clock pulse on duration\n    MediumHigh = 0x04,\n    /// Five clock pulse on duration\n    High = 0x05,\n    /// Six clock pulse on duration\n    VeryHigh = 0x06,\n    /// Seven clock pulse on duration\n    Highest = 0x07,\n    /// Enables the highdrive bit of the hardware\n    PermanentHighDrive = 0x09,\n}\n\n/// LCD driver.\npub struct Lcd<'d, T: Instance> {\n    _peri: PhantomData<&'d mut T>,\n    duty: Duty,\n    ck_div: u32,\n}\n\nimpl<'d, T: Instance> Lcd<'d, T> {\n    /// Initialize the lcd driver.\n    ///\n    /// The `pins` parameter must contain *all* segment and com pins that are connected to the LCD.\n    /// This is not further checked by this driver. Pins not routed to the LCD can be used for other purposes.\n    pub fn new<const N: usize>(\n        _peripheral: Peri<'d, T>,\n        config: Config,\n        vlcd_pin: Peri<'_, impl VlcdPin<T>>,\n        pins: [LcdPin<'d, T>; N],\n    ) -> Self {\n        rcc::enable_and_reset::<T>();\n\n        vlcd_pin.set_as_af(\n            vlcd_pin.af_num(),\n            AfType::output(crate::gpio::OutputType::PushPull, crate::gpio::Speed::VeryHigh),\n        );\n\n        assert_eq!(\n            pins.iter().filter(|pin| !pin.is_seg).count(),\n            config.duty.num_com_pins() as usize,\n            \"The number of provided COM pins is not the same as the duty configures\"\n        );\n\n        // Set the pins\n        for pin in pins {\n            pin.pin.pin.set_as_af(\n                pin.af_num,\n                AfType::output(crate::gpio::OutputType::PushPull, crate::gpio::Speed::VeryHigh),\n            );\n        }\n\n        // Initialize the display ram to 0\n        for i in 0..8 {\n            T::regs().ram_com(i).low().write_value(0);\n            T::regs().ram_com(i).high().write_value(0);\n        }\n\n        // Calculate the clock dividers\n        let Some(lcd_clk) = (unsafe { rcc::get_freqs().rtc.to_hertz() }) else {\n            panic!(\"The LCD driver needs the RTC/LCD clock to be running\");\n        };\n        let duty_divider = match config.duty {\n            Duty::Static => 1,\n            Duty::Half => 2,\n            Duty::Third => 3,\n            Duty::Quarter => 4,\n            Duty::Eigth => 8,\n        };\n        let target_clock = config.target_fps.0 * duty_divider;\n        let target_division = lcd_clk.0 / target_clock;\n\n        let mut ps = 0;\n        let mut div = 0;\n        let mut best_fps_match = u32::MAX;\n\n        for trial_div in 0..0xF {\n            let trial_ps = (target_division / (trial_div + 16))\n                .next_power_of_two()\n                .trailing_zeros();\n            let fps = lcd_clk.0 / ((1 << trial_ps) * (trial_div + 16)) / duty_divider;\n\n            if fps < config.target_fps.0 {\n                continue;\n            }\n\n            if fps < best_fps_match {\n                ps = trial_ps;\n                div = trial_div;\n                best_fps_match = fps;\n            }\n        }\n\n        let ck_div = lcd_clk.0 / ((1 << ps) * (div + 16));\n\n        trace!(\n            \"lcd_clk: {}, fps: {}, ps: {}, div: {}, ck_div: {}\",\n            lcd_clk, best_fps_match, ps, div, ck_div\n        );\n\n        if best_fps_match == u32::MAX || ps > 0xF {\n            panic!(\"Lcd clock error\");\n        }\n\n        // Set the frame control\n        T::regs().fcr().modify(|w| {\n            w.set_ps(ps as u8);\n            w.set_div(div as u8);\n            w.set_cc(0b100); // Init in the middle-ish\n            w.set_dead(0b000);\n            w.set_pon(config.drive as u8 & 0x07);\n            w.set_hd((config.drive as u8 & !0x07) != 0);\n        });\n\n        // Wait for the frame control to synchronize\n        while !T::regs().sr().read().fcrsf() {}\n\n        // Set the control register values\n        T::regs().cr().modify(|w| {\n            #[cfg(lcd_v2)]\n            w.set_bufen(config.use_voltage_output_buffer);\n            w.set_mux_seg(config.use_segment_muxing);\n            w.set_bias(config.bias as u8);\n            w.set_duty(config.duty as u8);\n            w.set_vsel(matches!(config.voltage_source, VoltageSource::External));\n        });\n\n        // Enable the lcd\n        T::regs().cr().modify(|w| w.set_lcden(true));\n\n        // Wait for the lcd to be enabled\n        while !T::regs().sr().read().ens() {}\n\n        // Wait for the stepup converter to be ready\n        while !T::regs().sr().read().rdy() {}\n\n        Self {\n            _peri: PhantomData,\n            duty: config.duty,\n            ck_div,\n        }\n    }\n\n    /// Change the contrast by changing the voltage being used.\n    ///\n    /// This is from low at 0 to high at 7.\n    pub fn set_contrast_control(&mut self, value: u8) {\n        assert!((0..=7).contains(&value));\n        T::regs().fcr().modify(|w| w.set_cc(value));\n    }\n\n    /// Change the contrast by introducing a deadtime to the signals\n    /// where the voltages are held at 0V.\n    ///\n    /// This is from no dead time at 0 to high dead time at 7.\n    pub fn set_dead_time(&mut self, value: u8) {\n        assert!((0..=7).contains(&value));\n        T::regs()\n            .fcr()\n            .modify(|w: &mut stm32_metapac::lcd::regs::Fcr| w.set_dead(value));\n    }\n\n    /// Write data into the display RAM. This overwrites the data already in it for the specified com index.\n    ///\n    /// The `com_index` value determines which part of the RAM is written to.\n    /// The `segments` value is a bitmap where each bit represents whether a pixel is turned on or off.\n    ///\n    /// This function waits last update request to be finished, but does not submit the buffer to the LCD with a new request.\n    /// Submission has to be done manually using [Self::submit_frame].\n    pub fn write_com_segments(&mut self, com_index: u8, segments: u64) {\n        while T::regs().sr().read().udr() {}\n\n        assert!(\n            com_index < self.duty.num_com_pins(),\n            \"Com index cannot be higher than number of configured com pins (through the Duty setting in the config)\"\n        );\n\n        assert!(\n            segments.leading_zeros() >= 64 - self.num_segments() as u32,\n            \"Invalid segment pixel set\",\n        );\n\n        T::regs()\n            .ram_com(com_index as usize)\n            .low()\n            .write_value((segments & 0xFFFF_FFFF) as u32);\n        T::regs()\n            .ram_com(com_index as usize)\n            .high()\n            .write_value(((segments >> 32) & 0xFFFF_FFFF) as u32);\n    }\n\n    /// Read the data from the display RAM.\n    ///\n    /// The `com_index` value determines which part of the RAM is read from.\n    ///\n    /// This function waits for the last update request to be finished.\n    pub fn read_com_segments(&self, com_index: u8) -> u64 {\n        while T::regs().sr().read().udr() {}\n\n        assert!(\n            com_index < self.duty.num_com_pins(),\n            \"Com index cannot be higher than number of configured com pins (through the Duty setting in the config)\"\n        );\n\n        let low = T::regs().ram_com(com_index as usize).low().read();\n        let high = T::regs().ram_com(com_index as usize).high().read();\n\n        ((high as u64) << 32) | low as u64\n    }\n\n    /// Submit the current RAM data to the LCD.\n    ///\n    /// This function waits until the RAM is writable, but does not wait for the frame to be drawn.\n    pub fn submit_frame(&mut self) {\n        while T::regs().sr().read().udr() {}\n        // Clear the update done flag\n        T::regs().sr().write(|w| w.set_udd(true));\n        // Set the update request flag\n        T::regs().sr().write(|w| w.set_udr(true));\n    }\n\n    /// Get the number of segments that are supported on this LCD\n    pub fn num_segments(&self) -> u8 {\n        match self.duty {\n            Duty::Eigth => NUM_SEGMENTS - 4, // With 8 coms, 4 of the segment pins turn into com pins\n            _ => NUM_SEGMENTS,\n        }\n    }\n\n    /// Get the pixel mask for the current LCD setup.\n    /// This is a mask of all bits that are allowed to be set in the [Self::write_com_segments] function.\n    pub fn segment_pixel_mask(&self) -> u64 {\n        (1 << self.num_segments()) - 1\n    }\n\n    /// Get the number of COM pins that were configured through the Drive config\n    pub fn num_com_pins(&self) -> u8 {\n        self.duty.num_com_pins()\n    }\n\n    /// Set the blink behavior on some pixels.\n    ///\n    /// The blink frequency is an approximation. It's divided from the clock selected by the FPS.\n    /// Play with the FPS value if you want the blink frequency to be more accurate.\n    ///\n    /// If a blink frequency cannot be attained, this function will panic.\n    pub fn set_blink(&mut self, selector: BlinkSelector, freq: BlinkFreq) {\n        // Freq * 100 to be able to do integer math\n        let scaled_blink_freq = match freq {\n            BlinkFreq::Hz0_25 => 25,\n            BlinkFreq::Hz0_5 => 50,\n            BlinkFreq::Hz1 => 100,\n            BlinkFreq::Hz2 => 200,\n            BlinkFreq::Hz4 => 400,\n        };\n\n        let desired_divider = self.ck_div * 100 / scaled_blink_freq;\n        let target_divider = desired_divider.next_power_of_two();\n        let power_divisions = target_divider.trailing_zeros();\n\n        trace!(\n            \"Setting LCD blink frequency -> desired_divider: {}, target_divider: {}\",\n            desired_divider, target_divider\n        );\n\n        assert!(\n            (8..=1024).contains(&target_divider),\n            \"LCD blink frequency cannot be attained\"\n        );\n\n        T::regs().fcr().modify(|reg| {\n            reg.set_blinkf((power_divisions - 3) as u8);\n            reg.set_blink(selector as u8);\n        })\n    }\n}\n\nimpl<'d, T: Instance> Drop for Lcd<'d, T> {\n    fn drop(&mut self) {\n        // Disable the lcd\n        T::regs().cr().modify(|w| w.set_lcden(false));\n        rcc::disable::<T>();\n    }\n}\n\n/// Blink frequency\n#[derive(Debug, Clone, Copy, PartialEq, Eq)]\npub enum BlinkFreq {\n    /// 0.25 hz\n    Hz0_25,\n    /// 0.5 hz\n    Hz0_5,\n    /// 1 hz\n    Hz1,\n    /// 2 hz\n    Hz2,\n    /// 4 hz\n    Hz4,\n}\n\n/// Blink pixel selector\n#[derive(Debug, Clone, Copy, PartialEq, Eq)]\n#[repr(u8)]\npub enum BlinkSelector {\n    /// No pixels blink\n    None = 0b00,\n    /// The SEG0, COM0 pixel blinks if the pixel is set\n    Seg0Com0 = 0b01,\n    /// The SEG0 pixel of all COMs blinks if the pixel is set\n    Seg0ComAll = 0b10,\n    /// All pixels blink if the pixel is set\n    All = 0b11,\n}\n\n/// A type-erased pin that can be configured as an LCD pin.\n/// This is used for passing pins to the new function in the array.\npub struct LcdPin<'d, T: Instance> {\n    pin: Flex<'d>,\n    af_num: u8,\n    is_seg: bool,\n    _phantom: PhantomData<T>,\n}\n\nimpl<'d, T: Instance> LcdPin<'d, T> {\n    /// Construct an LCD pin from any pin that supports it\n    pub fn new_seg(pin: Peri<'d, impl SegPin<T>>) -> Self {\n        let af = pin.af_num();\n\n        Self {\n            pin: Flex::new(pin),\n            af_num: af,\n            is_seg: true,\n            _phantom: PhantomData,\n        }\n    }\n\n    /// Construct an LCD pin from any pin that supports it\n    pub fn new_com(pin: Peri<'d, impl ComPin<T>>) -> Self {\n        let af = pin.af_num();\n\n        Self {\n            pin: Flex::new(pin),\n            af_num: af,\n            is_seg: false,\n            _phantom: PhantomData,\n        }\n    }\n}\n\ntrait SealedInstance: crate::rcc::SealedRccPeripheral + PeripheralType {\n    fn regs() -> crate::pac::lcd::Lcd;\n}\n\n/// DSI instance trait.\n#[allow(private_bounds)]\npub trait Instance: SealedInstance + RccPeripheral + 'static {}\n\npin_trait!(SegPin, Instance);\npin_trait!(ComPin, Instance);\npin_trait!(VlcdPin, Instance);\n\nforeach_peripheral!(\n    (lcd, $inst:ident) => {\n        impl crate::lcd::SealedInstance for peripherals::$inst {\n            fn regs() -> crate::pac::lcd::Lcd {\n                crate::pac::$inst\n            }\n        }\n\n        impl crate::lcd::Instance for peripherals::$inst {}\n    };\n);\n"
  },
  {
    "path": "embassy-stm32/src/lib.rs",
    "content": "#![cfg_attr(not(test), no_std)]\n#![allow(async_fn_in_trait)]\n#![allow(unsafe_op_in_unsafe_fn)]\n#![cfg_attr(\n    docsrs,\n    doc = \"<div style='padding:30px;background:#810;color:#fff;text-align:center;'><p>You might want to <a href='https://docs.embassy.dev/embassy-stm32'>browse the `embassy-stm32` documentation on the Embassy website</a> instead.</p><p>The documentation here on `docs.rs` is built for a single chip only (stm32h7, stm32h7rs55 in particular), while on the Embassy website you can pick your exact chip from the top menu. Available peripherals and their APIs change depending on the chip.</p></div>\\n\\n\"\n)]\n#![doc = include_str!(\"../README.md\")]\n#![warn(missing_docs)]\n\n//! ## Feature flags\n#![doc = document_features::document_features!(feature_label = r#\"<span class=\"stab portability\"><code>{feature}</code></span>\"#)]\n\n// This must go FIRST so that all the other modules see its macros.\nmod fmt;\ninclude!(concat!(env!(\"OUT_DIR\"), \"/_macros.rs\"));\n\n// Utilities\nmod macros;\npub mod time;\n/// Operating modes for peripherals.\npub mod mode {\n    trait SealedMode {}\n\n    /// Operating mode for a peripheral.\n    #[allow(private_bounds)]\n    pub trait Mode: SealedMode {}\n\n    macro_rules! impl_mode {\n        ($name:ident) => {\n            impl SealedMode for $name {}\n            impl Mode for $name {}\n        };\n    }\n\n    /// Blocking mode.\n    pub struct Blocking;\n    /// Async mode.\n    pub struct Async;\n\n    impl_mode!(Blocking);\n    impl_mode!(Async);\n}\n\n// Always-present hardware\npub mod dma;\npub mod gpio;\npub mod rcc;\n#[cfg(feature = \"_time-driver\")]\nmod time_driver;\npub mod timer;\n\n// Sometimes-present hardware\n\n#[cfg(adc)]\npub mod adc;\n#[cfg(aes_v3b)]\npub mod aes;\n#[cfg(backup_sram)]\npub mod backup_sram;\n#[cfg(can)]\npub mod can;\n#[cfg(any(comp_u5, comp_v2))]\npub mod comp;\n#[cfg(cordic)]\npub mod cordic;\n\n// Stub macros for COMP pin implementations when comp module is not compiled.\n// These are needed because build.rs generates macro calls for all chips with COMP,\n// but the actual macros are only defined in the comp module.\n#[cfg(all(comp, not(any(comp_u5, comp_v2))))]\n#[allow(unused_macros)]\nmacro_rules! impl_comp_inp_pin {\n    ($inst:ident, $pin:ident, $ch:expr) => {};\n}\n#[cfg(all(comp, not(any(comp_u5, comp_v2))))]\n#[allow(unused_macros)]\nmacro_rules! impl_comp_inm_pin {\n    ($inst:ident, $pin:ident, $ch:expr) => {};\n}\n#[cfg(any(ipcc, hsem))]\npub mod cpu;\n#[cfg(crc)]\npub mod crc;\n#[cfg(cryp)]\npub mod cryp;\n#[cfg(dac)]\npub mod dac;\n#[cfg(dcmi)]\npub mod dcmi;\n#[cfg(dsihost)]\npub mod dsihost;\n#[cfg(dts)]\npub mod dts;\n#[cfg(eth)]\npub mod eth;\n#[cfg(feature = \"exti\")]\npub mod exti;\n#[cfg(flash)]\npub mod flash;\n#[cfg(fmc)]\npub mod fmc;\n#[cfg(hash)]\npub mod hash;\n#[cfg(all(hrtim, feature = \"stm32-hrtim\"))]\npub mod hrtim;\n#[cfg(hsem)]\npub mod hsem;\n#[cfg(hspi)]\npub mod hspi;\n#[cfg(i2c)]\npub mod i2c;\n#[cfg(any(spi_v1_i2s, spi_v2_i2s, spi_v3_i2s, spi_v4_i2s, spi_v5_i2s))]\npub mod i2s;\n#[cfg(any(stm32wb, stm32wl5x))]\npub mod ipcc;\n#[cfg(lcd)]\npub mod lcd;\n#[cfg(feature = \"low-power\")]\npub mod low_power;\n#[cfg(lptim)]\npub mod lptim;\n#[cfg(ltdc)]\npub mod ltdc;\n#[cfg(opamp)]\npub mod opamp;\n#[cfg(octospi)]\npub mod ospi;\n#[cfg(pka_v1a)]\npub mod pka;\n#[cfg(quadspi)]\npub mod qspi;\n#[cfg(rng)]\npub mod rng;\n#[cfg(all(rtc, not(rtc_v1)))]\npub mod rtc;\n#[cfg(saes_v1a)]\npub mod saes;\n#[cfg(sai)]\npub mod sai;\n#[cfg(sdmmc)]\npub mod sdmmc;\n#[cfg(spdifrx)]\npub mod spdifrx;\n#[cfg(spi)]\npub mod spi;\n#[cfg(tsc)]\npub mod tsc;\n#[cfg(ucpd)]\npub mod ucpd;\n#[cfg(uid)]\npub mod uid;\n#[cfg(usart)]\npub mod usart;\n#[cfg(any(usb, otg))]\npub mod usb;\n#[cfg(vrefbuf)]\npub mod vrefbuf;\n#[cfg(iwdg)]\npub mod wdg;\n#[cfg(xspi)]\npub mod xspi;\n\n#[cfg(feature = \"_executor\")]\npub mod executor;\n\n// This must go last, so that it sees all the impl_foo! macros defined earlier.\npub(crate) mod _generated {\n    #![allow(dead_code)]\n    #![allow(unused_imports)]\n    #![allow(non_snake_case)]\n    #![allow(missing_docs)]\n\n    include!(concat!(env!(\"OUT_DIR\"), \"/_generated.rs\"));\n}\n\npub use crate::_generated::{interrupt, triggers};\n\n/// Macro to bind interrupts to handlers.\n///\n/// This defines the right interrupt handlers, and creates a unit struct (like `struct Irqs;`)\n/// and implements the right [`Binding`](crate::interrupt::typelevel::Binding)s for it. You can pass this struct to drivers to\n/// prove at compile-time that the right interrupts have been bound.\n///\n/// Example of how to bind one interrupt:\n///\n/// ```rust,ignore\n/// use embassy_stm32::{bind_interrupts, usb, peripherals};\n///\n/// bind_interrupts!(struct Irqs {\n///     OTG_FS => usb::InterruptHandler<peripherals::USB_OTG_FS>;\n/// });\n/// ```\n///\n/// Example of how to bind multiple interrupts, and multiple handlers to each interrupt, in a single macro invocation:\n///\n/// ```rust,ignore\n/// use embassy_stm32::{bind_interrupts, i2c, peripherals};\n///\n/// bind_interrupts!(\n///     /// Binds the I2C interrupts.\n///     struct Irqs {\n///         I2C1 => i2c::EventInterruptHandler<peripherals::I2C1>, i2c::ErrorInterruptHandler<peripherals::I2C1>;\n///         I2C2_3 => i2c::EventInterruptHandler<peripherals::I2C2>, i2c::ErrorInterruptHandler<peripherals::I2C2>,\n///             i2c::EventInterruptHandler<peripherals::I2C3>, i2c::ErrorInterruptHandler<peripherals::I2C3>;\n///     }\n/// );\n/// ```\n///\n/// Some chips collate multiple interrupt signals into a single interrupt vector. In the above example, I2C2_3 is a\n/// single vector which is activated by events and errors on both peripherals I2C2 and I2C3. Check your chip's list\n/// of interrupt vectors if you get an unexpected compile error trying to bind the standard name.\n// developer note: this macro can't be in `embassy-hal-internal` due to the use of `$crate`.\n#[macro_export]\nmacro_rules! bind_interrupts {\n    ($(#[$outer:meta])* $vis:vis struct $name:ident {\n        $(\n            $(#[doc = $doc:literal])*\n            $(#[cfg($cond_irq:meta)])?\n            $irq:ident => $(\n                $(#[cfg($cond_handler:meta)])?\n                $handler:ty\n            ),*;\n        )*\n    }) => {\n        #[derive(Copy, Clone)]\n        $(#[$outer])*\n        $vis struct $name;\n\n        $(\n            #[allow(non_snake_case)]\n            #[unsafe(no_mangle)]\n            $(#[cfg($cond_irq)])?\n            $(#[doc = $doc])*\n            unsafe extern \"C\" fn $irq() {\n                unsafe {\n                    $(\n                        $(#[cfg($cond_handler)])?\n                        <$handler as $crate::interrupt::typelevel::Handler<$crate::interrupt::typelevel::$irq>>::on_interrupt();\n\n                    )*\n                }\n            }\n\n            $(#[cfg($cond_irq)])?\n            $crate::bind_interrupts!(@inner\n                $(\n                    $(#[cfg($cond_handler)])?\n                    unsafe impl $crate::interrupt::typelevel::Binding<$crate::interrupt::typelevel::$irq, $handler> for $name {}\n                )*\n            );\n        )*\n    };\n    (@inner $($t:tt)*) => {\n        $($t)*\n    }\n}\n\n// Reexports\npub use _generated::{Peripherals, peripherals};\npub use embassy_hal_internal::{Peri, PeripheralType};\n#[cfg(feature = \"unstable-pac\")]\npub use stm32_metapac as pac;\n#[cfg(not(feature = \"unstable-pac\"))]\npub(crate) use stm32_metapac as pac;\n\nuse crate::interrupt::Priority;\n#[cfg(feature = \"rt\")]\npub use crate::pac::NVIC_PRIO_BITS;\n\n/// `embassy-stm32` global configuration.\n#[non_exhaustive]\n#[derive(Clone, Copy)]\npub struct Config {\n    /// RCC config.\n    pub rcc: rcc::Config,\n\n    #[cfg(feature = \"low-power\")]\n    /// RTC config\n    pub rtc: rtc::RtcConfig,\n\n    #[cfg(feature = \"low-power\")]\n    /// Minimum time to stop\n    pub min_stop_pause: embassy_time::Duration,\n\n    /// Enable debug during sleep and stop.\n    ///\n    /// May increase power consumption. Defaults to true.\n    #[cfg(dbgmcu)]\n    pub enable_debug_during_sleep: bool,\n\n    /// On low-power boards (eg. `stm32l4`, `stm32l5`, `stm32wba` and `stm32u5`),\n    /// some GPIO pins are powered by an auxiliary, independent power supply (`VDDIO2`),\n    /// which needs to be enabled before these pins can be used.\n    ///\n    /// May increase power consumption. Defaults to true.\n    #[cfg(any(stm32l4, stm32l5, stm32u5, stm32u3, stm32wba))]\n    pub enable_independent_io_supply: bool,\n\n    /// On the U5 series all analog peripherals are powered by a separate supply.\n    #[cfg(any(stm32u5, stm32u3))]\n    pub enable_independent_analog_supply: bool,\n\n    /// BDMA interrupt priority.\n    ///\n    /// Defaults to P0 (highest).\n    #[cfg(bdma)]\n    pub bdma_interrupt_priority: Priority,\n\n    /// DMA interrupt priority.\n    ///\n    /// Defaults to P0 (highest).\n    #[cfg(dma)]\n    pub dma_interrupt_priority: Priority,\n\n    /// GPDMA interrupt priority.\n    ///\n    /// Defaults to P0 (highest).\n    #[cfg(gpdma)]\n    pub gpdma_interrupt_priority: Priority,\n\n    /// MDMA interrupt priority.\n    ///\n    /// Defaults to P0 (highest).\n    #[cfg(mdma)]\n    pub mdma_interrupt_priority: Priority,\n\n    /// Enables UCPD1 dead battery functionality.\n    ///\n    /// Defaults to false (disabled).\n    #[cfg(peri_ucpd1)]\n    pub enable_ucpd1_dead_battery: bool,\n\n    /// Enables UCPD2 dead battery functionality.\n    ///\n    /// Defaults to false (disabled).\n    #[cfg(peri_ucpd2)]\n    pub enable_ucpd2_dead_battery: bool,\n\n    /// Allows JTAG pins to be used for GPIO\n    #[cfg(stm32f1)]\n    pub swj: gpio::SwjCfg,\n}\n\nimpl Default for Config {\n    fn default() -> Self {\n        Self {\n            rcc: Default::default(),\n            #[cfg(feature = \"low-power\")]\n            rtc: Default::default(),\n            #[cfg(feature = \"low-power\")]\n            min_stop_pause: embassy_time::Duration::from_millis(250),\n            #[cfg(dbgmcu)]\n            enable_debug_during_sleep: true,\n            #[cfg(any(stm32l4, stm32l5, stm32u5, stm32u3, stm32wba))]\n            enable_independent_io_supply: true,\n            #[cfg(any(stm32u5, stm32u3))]\n            enable_independent_analog_supply: true,\n            #[cfg(bdma)]\n            bdma_interrupt_priority: Priority::P0,\n            #[cfg(dma)]\n            dma_interrupt_priority: Priority::P0,\n            #[cfg(gpdma)]\n            gpdma_interrupt_priority: Priority::P0,\n            #[cfg(mdma)]\n            mdma_interrupt_priority: Priority::P0,\n            #[cfg(peri_ucpd1)]\n            enable_ucpd1_dead_battery: false,\n            #[cfg(peri_ucpd2)]\n            enable_ucpd2_dead_battery: false,\n            #[cfg(stm32f1)]\n            swj: Default::default(),\n        }\n    }\n}\n\n/// Initialize the `embassy-stm32` HAL with the provided configuration.\n///\n/// This returns the peripheral singletons that can be used for creating drivers.\n///\n/// This should only be called once at startup, otherwise it panics.\n#[cfg(not(feature = \"_dual-core\"))]\npub fn init(config: Config) -> Peripherals {\n    init_hw(config)\n}\n\n#[cfg(feature = \"_dual-core\")]\nmod dual_core {\n    use core::cell::UnsafeCell;\n    use core::mem::MaybeUninit;\n    use core::sync::atomic::{AtomicUsize, Ordering};\n\n    use rcc::Clocks;\n\n    use super::*;\n\n    /// Object containing data that embassy needs to share between cores.\n    ///\n    /// It cannot be initialized by the user. The intended use is:\n    ///\n    /// ```\n    /// use core::mem::MaybeUninit;\n    /// use embassy_stm32::{init_secondary, SharedData};\n    ///\n    /// #[link_section = \".ram_d3\"]\n    /// static SHARED_DATA: MaybeUninit<SharedData> = MaybeUninit::uninit();\n    ///\n    /// init_secondary(&SHARED_DATA);\n    /// ```\n    ///\n    /// This static must be placed in the same position for both cores. How and where this is done is left to the user.\n    #[repr(C)]\n    pub struct SharedData {\n        init_flag: AtomicUsize,\n        clocks: UnsafeCell<MaybeUninit<Clocks>>,\n        config: UnsafeCell<MaybeUninit<SharedConfig>>,\n        #[cfg(feature = \"low-power\")]\n        rcc_config: UnsafeCell<MaybeUninit<Option<rcc::Config>>>,\n    }\n\n    unsafe impl Sync for SharedData {}\n\n    const INIT_DONE_FLAG: usize = 0xca11ab1e;\n\n    /// Initialize the `embassy-stm32` HAL with the provided configuration.\n    /// This function does the actual initialization of the hardware, in contrast to [init_secondary] or [try_init_secondary].\n    /// Any core can do the init, but it's important only one core does it.\n    ///\n    /// This returns the peripheral singletons that can be used for creating drivers.\n    ///\n    /// This should only be called once at startup, otherwise it panics.\n    ///\n    /// The `shared_data` is used to coordinate the init with the second core. Read the [SharedData] docs\n    /// for more information on its requirements.\n    pub fn init_primary(config: Config, shared_data: &'static MaybeUninit<SharedData>) -> Peripherals {\n        let shared_data = unsafe { shared_data.assume_init_ref() };\n\n        // Write the flag as soon as possible. Reading this flag uninitialized in the `init_secondary`\n        // is maybe unsound? Unclear. If it is indeed unsound, writing it sooner doesn't fix it all,\n        // but improves the odds of it going right\n        shared_data.init_flag.store(0, Ordering::SeqCst);\n\n        rcc::set_freqs_ptr(shared_data.clocks.get());\n        #[cfg(feature = \"low-power\")]\n        rcc::set_rcc_config_ptr(shared_data.rcc_config.get());\n        let p = init_hw(config);\n\n        unsafe { *shared_data.config.get() }.write(config.into());\n\n        shared_data.init_flag.store(INIT_DONE_FLAG, Ordering::SeqCst);\n\n        p\n    }\n\n    /// Try to initialize the `embassy-stm32` HAL based on the init done by the other core using [init_primary].\n    ///\n    /// This returns the peripheral singletons that can be used for creating drivers if the other core is done with its init.\n    /// If the other core is not done yet, this will return `None`.\n    ///\n    /// This should only be called once at startup, otherwise it may panic.\n    ///\n    /// The `shared_data` is used to coordinate the init with the second core. Read the [SharedData] docs\n    /// for more information on its requirements.\n    pub fn try_init_secondary(shared_data: &'static MaybeUninit<SharedData>) -> Option<Peripherals> {\n        let shared_data = unsafe { shared_data.assume_init_ref() };\n\n        if shared_data.init_flag.load(Ordering::SeqCst) != INIT_DONE_FLAG {\n            return None;\n        }\n\n        // Separate load and store to support the CM0 of the STM32WL\n        shared_data.init_flag.store(0, Ordering::SeqCst);\n\n        Some(init_secondary_hw(shared_data))\n    }\n\n    /// Initialize the `embassy-stm32` HAL based on the init done by the other core using [init_primary].\n    ///\n    /// This returns the peripheral singletons that can be used for creating drivers when the other core is done with its init.\n    /// If the other core is not done yet, this will spinloop wait on it.\n    ///\n    /// This should only be called once at startup, otherwise it may panic.\n    ///\n    /// The `shared_data` is used to coordinate the init with the second core. Read the [SharedData] docs\n    /// for more information on its requirements.\n    pub fn init_secondary(shared_data: &'static MaybeUninit<SharedData>) -> Peripherals {\n        loop {\n            if let Some(p) = try_init_secondary(shared_data) {\n                return p;\n            }\n        }\n    }\n\n    fn init_secondary_hw(shared_data: &'static SharedData) -> Peripherals {\n        rcc::set_freqs_ptr(shared_data.clocks.get());\n        #[cfg(feature = \"low-power\")]\n        rcc::set_rcc_config_ptr(shared_data.rcc_config.get());\n\n        let config = unsafe { (*shared_data.config.get()).assume_init() };\n\n        // We use different timers on the different cores, so we have to still initialize one here\n        critical_section::with(|cs| {\n            unsafe {\n                dma::init(\n                    cs,\n                    #[cfg(bdma)]\n                    config.bdma_interrupt_priority,\n                    #[cfg(dma)]\n                    config.dma_interrupt_priority,\n                    #[cfg(gpdma)]\n                    config.gpdma_interrupt_priority,\n                    #[cfg(mdma)]\n                    config.mdma_interrupt_priority,\n                )\n            }\n\n            #[cfg(feature = \"_time-driver\")]\n            // must be after rcc init\n            time_driver::init(cs);\n        });\n\n        Peripherals::take()\n    }\n\n    #[repr(C)]\n    #[derive(Clone, Copy)]\n    struct SharedConfig {\n        #[cfg(bdma)]\n        bdma_interrupt_priority: Priority,\n        #[cfg(dma)]\n        dma_interrupt_priority: Priority,\n        #[cfg(gpdma)]\n        gpdma_interrupt_priority: Priority,\n        #[cfg(mdma)]\n        mdma_interrupt_priority: Priority,\n    }\n\n    impl From<Config> for SharedConfig {\n        fn from(value: Config) -> Self {\n            let Config {\n                #[cfg(bdma)]\n                bdma_interrupt_priority,\n                #[cfg(dma)]\n                dma_interrupt_priority,\n                #[cfg(gpdma)]\n                gpdma_interrupt_priority,\n                #[cfg(mdma)]\n                mdma_interrupt_priority,\n                ..\n            } = value;\n\n            SharedConfig {\n                #[cfg(bdma)]\n                bdma_interrupt_priority,\n                #[cfg(dma)]\n                dma_interrupt_priority,\n                #[cfg(gpdma)]\n                gpdma_interrupt_priority,\n                #[cfg(mdma)]\n                mdma_interrupt_priority,\n            }\n        }\n    }\n}\n\n#[cfg(feature = \"_dual-core\")]\npub use dual_core::*;\n\nfn init_hw(config: Config) -> Peripherals {\n    critical_section::with(|cs| {\n        let p = Peripherals::take_with_cs(cs);\n\n        #[cfg(dbgmcu_n6)]\n        {\n            crate::pac::RCC.miscensr().write(|w| w.set_dbgens(true));\n            crate::pac::RCC.miscenr().read(); // volatile read\n            crate::pac::DBGMCU\n                .cr()\n                .modify(|w| w.set_dbgclken(stm32_metapac::dbgmcu::vals::Dbgclken::B_0X1));\n            crate::pac::DBGMCU.cr().read();\n        }\n\n        #[cfg(dbgmcu)]\n        crate::pac::DBGMCU.cr().modify(|cr| {\n            #[cfg(dbgmcu_h5)]\n            {\n                cr.set_stop(config.enable_debug_during_sleep);\n                cr.set_standby(config.enable_debug_during_sleep);\n            }\n            #[cfg(any(\n                dbgmcu_f0, dbgmcu_c0, dbgmcu_g0, dbgmcu_u0, dbgmcu_u3, dbgmcu_u5, dbgmcu_wba, dbgmcu_l5\n            ))]\n            {\n                cr.set_dbg_stop(config.enable_debug_during_sleep);\n                cr.set_dbg_standby(config.enable_debug_during_sleep);\n            }\n            #[cfg(any(\n                dbgmcu_f1, dbgmcu_f2, dbgmcu_f3, dbgmcu_f4, dbgmcu_f7, dbgmcu_g4, dbgmcu_f7, dbgmcu_l0, dbgmcu_l1,\n                dbgmcu_l4, dbgmcu_wb, dbgmcu_wl, dbgmcu_n6\n            ))]\n            {\n                cr.set_dbg_sleep(config.enable_debug_during_sleep);\n                cr.set_dbg_stop(config.enable_debug_during_sleep);\n                cr.set_dbg_standby(config.enable_debug_during_sleep);\n            }\n            #[cfg(dbgmcu_h7)]\n            {\n                cr.set_d1dbgcken(config.enable_debug_during_sleep);\n                cr.set_d3dbgcken(config.enable_debug_during_sleep);\n                cr.set_dbgsleep_d1(config.enable_debug_during_sleep);\n                cr.set_dbgstby_d1(config.enable_debug_during_sleep);\n                cr.set_dbgstop_d1(config.enable_debug_during_sleep);\n            }\n        });\n\n        #[cfg(any(stm32h7rs))]\n        // On the H7RS the SYSCFG should not be reset if it is already enabled. This is typically the case when running from external flash and the bootloader enables the SYSCFG.\n        rcc::enable_with_cs::<peripherals::SYSCFG>(cs);\n        #[cfg(not(any(stm32f1, stm32wb, stm32wl, stm32h7rs)))]\n        rcc::enable_and_reset_with_cs::<peripherals::SYSCFG>(cs);\n        #[cfg(not(any(stm32h5, stm32h7, stm32h7rs, stm32wb, stm32wl)))]\n        rcc::enable_and_reset_with_cs::<peripherals::PWR>(cs);\n        #[cfg(all(flash, not(any(stm32f2, stm32f4, stm32f7, stm32l0, stm32h5, stm32h7, stm32h7rs))))]\n        rcc::enable_and_reset_with_cs::<peripherals::FLASH>(cs);\n\n        // Enable the VDDIO2 power supply on chips that have it.\n        // Note that this requires the PWR peripheral to be enabled first.\n        #[cfg(any(stm32l4, stm32l5))]\n        {\n            crate::pac::PWR.cr2().modify(|w| {\n                // The official documentation states that we should ideally enable VDDIO2\n                // through the PVME2 bit, but it looks like this isn't required,\n                // and CubeMX itself skips this step.\n                w.set_iosv(config.enable_independent_io_supply);\n            });\n        }\n        #[cfg(stm32wba)]\n        {\n            use crate::pac::pwr::vals;\n            crate::pac::PWR.svmcr().modify(|w| {\n                w.set_io2sv(if config.enable_independent_io_supply {\n                    vals::Io2sv::B_0X1\n                } else {\n                    vals::Io2sv::B_0X0\n                });\n            });\n        }\n        #[cfg(any(stm32u5, stm32u3))]\n        {\n            crate::pac::PWR.svmcr().modify(|w| {\n                w.set_io2sv(config.enable_independent_io_supply);\n            });\n            if config.enable_independent_analog_supply {\n                crate::pac::PWR.svmcr().modify(|w| {\n                    w.set_avm1en(true);\n                });\n                while !crate::pac::PWR.svmsr().read().vdda1rdy() {}\n                crate::pac::PWR.svmcr().modify(|w| {\n                    w.set_asv(true);\n                });\n            } else {\n                crate::pac::PWR.svmcr().modify(|w| {\n                    w.set_avm1en(false);\n                    w.set_avm2en(false);\n                });\n            }\n        }\n\n        // dead battery functionality is still present on these\n        // chips despite them not having UCPD- disable it\n        #[cfg(any(stm32g070, stm32g0b0))]\n        {\n            crate::pac::SYSCFG.cfgr1().modify(|w| {\n                w.set_ucpd1_strobe(true);\n                w.set_ucpd2_strobe(true);\n            });\n        }\n\n        unsafe {\n            #[cfg(ucpd)]\n            ucpd::init(\n                cs,\n                #[cfg(all(peri_ucpd1, not(stm32n6)))]\n                config.enable_ucpd1_dead_battery,\n                #[cfg(peri_ucpd2)]\n                config.enable_ucpd2_dead_battery,\n            );\n\n            #[cfg(feature = \"_split-pins-enabled\")]\n            crate::pac::SYSCFG.pmcr().modify(|pmcr| {\n                #[cfg(feature = \"split-pa0\")]\n                pmcr.set_pa0so(true);\n                #[cfg(feature = \"split-pa1\")]\n                pmcr.set_pa1so(true);\n                #[cfg(feature = \"split-pc2\")]\n                pmcr.set_pc2so(true);\n                #[cfg(feature = \"split-pc3\")]\n                pmcr.set_pc3so(true);\n            });\n\n            gpio::init(cs);\n\n            #[cfg(stm32f1)]\n            crate::pac::AFIO.mapr().modify(|w| w.set_swj_cfg(config.swj.into()));\n\n            dma::init(\n                cs,\n                #[cfg(bdma)]\n                config.bdma_interrupt_priority,\n                #[cfg(dma)]\n                config.dma_interrupt_priority,\n                #[cfg(gpdma)]\n                config.gpdma_interrupt_priority,\n                #[cfg(mdma)]\n                config.mdma_interrupt_priority,\n            );\n            #[cfg(feature = \"exti\")]\n            exti::init(cs);\n\n            rcc::init_rcc(cs, config.rcc);\n\n            // must be before time_driver init to allow refcount reset\n            #[cfg(all(any(stm32wb, stm32wl5x), feature = \"low-power\"))]\n            hsem::init_hsem(cs);\n\n            // must be after rcc init\n            #[cfg(feature = \"_time-driver\")]\n            crate::time_driver::init(cs);\n\n            // must be after time-driver init\n            #[cfg(all(feature = \"low-power\", not(feature = \"_lp-time-driver\")))]\n            rtc::init_rtc(cs, config.rtc, config.min_stop_pause);\n        }\n\n        p\n    })\n}\n\n/// Performs a busy-wait delay for a specified number of microseconds.\n#[allow(unused)]\npub(crate) fn block_for_us(us: u64) {\n    cortex_m::asm::delay(unsafe { rcc::get_freqs().sys.to_hertz().unwrap().0 as u64 * us / 1_000_000 } as u32);\n}\n"
  },
  {
    "path": "embassy-stm32/src/low_power.rs",
    "content": "//! Low-power support.\n//!\n//! The STM32 line of microcontrollers support various deep-sleep modes which exploit clock-gating\n//! to reduce power consumption. The `embassy-stm32` HAL provides a `sleep()` function which\n//! can use knowledge of which peripherals are currently blocked upon to transparently and safely\n//! enter such low-power modes including `STOP1` and `STOP2` when possible.\n//!\n//! `sleep()` determines which peripherals are active by their RCC state; consequently,\n//! low-power states can only be entered if peripherals which block stop have been `drop`'d and if\n//! peripherals that do not block stop are busy. Peripherals which never block stop include:\n//!\n//!  * `GPIO`\n//!  * `RTC`\n//!\n//! Other peripherals which block stop when busy include (this list may be stale):\n//!\n//!  * `I2C`\n//!  * `USART`\n//!\n//! Since entering and leaving low-power modes typically incurs a significant latency, `sleep()`\n//! will only attempt to enter when the next timer event is at least [`config.min_stop_pause`] in the future.\n//!\n//! `embassy-stm32` also provides an `embassy-executor` platform implementation that integrates `sleep()` into the main loop. It is available\n//! in the `embassy_stm32::executor` module, and is enabled by the `executor-thread` or `executor-interrupt` features. This stm32-specific\n//! executor is the preferred way to lower power consumption if you're using `async`, instead of calling `sleep()` directly.\n\nuse core::mem;\nuse core::sync::atomic::{Ordering, compiler_fence};\n\nuse cortex_m::peripheral::SCB;\nuse critical_section::CriticalSection;\n\n#[cfg(not(feature = \"_lp-time-driver\"))]\nuse crate::interrupt;\npub use crate::rcc::StopMode;\nuse crate::rcc::get_stop_mode;\nuse crate::time_driver::{LPTimeDriver, get_driver};\n\n#[cfg(not(any(stm32u0, feature = \"_lp-time-driver\")))]\nforeach_interrupt! {\n    (RTC, rtc, $block:ident, WKUP, $irq:ident) => {\n        #[interrupt]\n        #[allow(non_snake_case)]\n        unsafe fn $irq() {\n        }\n    };\n}\n\n#[cfg(stm32u0)]\nforeach_interrupt! {\n    (RTC, rtc, $block:ident, TAMP, $irq:ident) => {\n        #[interrupt]\n        #[allow(non_snake_case)]\n        unsafe fn $irq() {\n        }\n    };\n}\n\n#[cfg(any(stm32l4, stm32l5, stm32u5, stm32u3, stm32wba, stm32wb, stm32wl, stm32u0))]\nuse crate::pac::pwr::vals::Lpms;\n\n#[cfg(any(stm32l4, stm32l5, stm32u5, stm32u3, stm32wba, stm32wb, stm32wl, stm32u0))]\nimpl Into<Lpms> for StopMode {\n    fn into(self) -> Lpms {\n        match self {\n            StopMode::Stop1 => Lpms::STOP1,\n            #[cfg(not(stm32wba))]\n            StopMode::Standby | StopMode::Stop2 => Lpms::STOP2,\n            #[cfg(stm32wba)]\n            // WBA STOP2 is auto-entered by hardware when LPMS=STOP0 and\n            // the 2.4 GHz radio is in deep sleep. It's not a separate LPMS value.\n            StopMode::Standby | StopMode::Stop2 => Lpms::STOP0,\n        }\n    }\n}\n\nmod platform {\n    use critical_section::CriticalSection;\n\n    use crate::rcc::StopMode;\n\n    /// Enter stop mode\n    pub fn enter_stop(_cs: CriticalSection, stop_mode: StopMode) -> Result<(), ()> {\n        #[cfg(stm32wb)]\n        fn enter_stop_stm32wb(\n            _cs: CriticalSection<'_>,\n        ) -> Result<crate::hsem::HardwareSemaphoreMutex<'_, crate::peripherals::HSEM>, ()> {\n            use core::task::Poll;\n\n            use embassy_futures::poll_once;\n\n            use crate::hsem::get_hsem;\n            use crate::pac::rcc::vals::{Smps, Sw};\n            use crate::pac::{PWR, RCC};\n\n            trace!(\"low power: trying to get sem3\");\n\n            let sem3_mutex = match poll_once(get_hsem(3).lock(0)) {\n                Poll::Pending => None,\n                Poll::Ready(mutex) => Some(mutex),\n            }\n            .ok_or(())?;\n\n            trace!(\"low power: got sem3\");\n\n            let sem4_mutex = get_hsem(4).try_lock(0);\n            if let Some(sem4_mutex) = sem4_mutex {\n                trace!(\"low power: got sem4\");\n\n                if PWR.extscr().read().c2ds() {\n                    drop(sem4_mutex);\n                } else {\n                    return Ok(sem3_mutex);\n                }\n            }\n\n            // Sem4 not granted\n            // Set HSION\n            RCC.cr().modify(|w| {\n                w.set_hsion(true);\n            });\n\n            // Wait for HSIRDY\n            while !RCC.cr().read().hsirdy() {}\n\n            // Set SW to HSI\n            RCC.cfgr().modify(|w| {\n                w.set_sw(Sw::HSI);\n            });\n\n            // Wait for SWS to report HSI\n            while !RCC.cfgr().read().sws().eq(&Sw::HSI) {}\n\n            // Set SMPSSEL to HSI\n            RCC.smpscr().modify(|w| {\n                w.set_smpssel(Smps::HSI);\n            });\n\n            Ok(sem3_mutex)\n        }\n\n        #[cfg(stm32wb)]\n        let mutex = {\n            use crate::pac::{PWR, RCC};\n\n            let mutex = enter_stop_stm32wb(_cs)?;\n\n            // on PWR\n            RCC.apb1enr1().modify(|r| r.0 |= 1 << 28);\n            cortex_m::asm::dsb();\n\n            // off SMPS, on Bypass\n            PWR.cr5().modify(|r| {\n                let mut val = r.0;\n                val &= !(1 << 15); // sdeb = 0 (off SMPS)\n                val |= 1 << 14; // sdben = 1 (on Bypass)\n                r.0 = val\n            });\n\n            cortex_m::asm::delay(1000);\n\n            mutex\n        };\n\n        #[cfg(any(stm32l4, stm32l5, stm32u5, stm32u3, stm32u0, stm32wb, stm32wba, stm32wl))]\n        {\n            #[cfg(not(feature = \"_core-cm0p\"))]\n            crate::pac::PWR.cr1().modify(|m| m.set_lpms(stop_mode.into()));\n            #[cfg(feature = \"_core-cm0p\")]\n            crate::pac::PWR.c2cr1().modify(|m| m.set_lpms(stop_mode.into()));\n        }\n        #[cfg(stm32h5)]\n        crate::pac::PWR.pmcr().modify(|v| {\n            use crate::pac::pwr::vals;\n            v.set_lpms(vals::Lpms::STOP);\n            v.set_svos(vals::Svos::SCALE3);\n        });\n\n        #[cfg(stm32l0)]\n        {\n            use crate::pac::pwr::vals::Pdds;\n            crate::pac::PWR.cr().modify(|w| {\n                w.set_pdds(Pdds::STOP_MODE);\n                w.set_cwuf(true);\n            });\n        }\n\n        #[cfg(stm32wb)]\n        drop(mutex);\n\n        let _ = stop_mode;\n\n        Ok(())\n    }\n\n    /// Clear any previous stop flags\n    pub fn clear_flags() {\n        #[cfg(stm32wl)]\n        crate::pac::PWR.extscr().modify(|w| {\n            #[cfg(not(feature = \"_core-cm0p\"))]\n            w.set_c1cssf(true);\n            #[cfg(feature = \"_core-cm0p\")]\n            w.set_c2cssf(true);\n        });\n        #[cfg(stm32wba)]\n        crate::pac::PWR.sr().modify(|w| w.set_cssf(true));\n\n        #[cfg(stm32l0)]\n        crate::pac::PWR.cr().modify(|w| w.set_cwuf(true));\n    }\n\n    /// Exit stop mode, reinitializing timer and rcc if required\n    pub fn exit_stop(_cs: CriticalSection) {\n        #[cfg(any(stm32l0, stm32wl, stm32wb, stm32wba))]\n        {\n            // stm32wl5x is dual core and we don't want BOTH cores to re-initialize RCC so we hold a lock\n            #[cfg(stm32wl5x)]\n            let lock = crate::hsem::get_hsem(3).blocking_lock(0);\n\n            #[cfg(any(stm32wl, stm32wb))]\n            let es = crate::pac::PWR.extscr().read();\n\n            #[cfg(stm32wba)]\n            let es = crate::pac::PWR.sr().read();\n\n            #[cfg(stm32l0)]\n            let es = crate::pac::PWR.csr().read();\n\n            // we need to re-initialize RCC if *BOTH* cores have been in some STOP mode!\n            #[cfg(any(stm32l0, stm32wl, stm32wba))]\n            let re_initialize_rcc = {\n                #[cfg(stm32wl5x)]\n                {\n                    // core 1 in any STOP mode AND core 2 in any STOP mode\n                    (es.c1stopf() || es.c1stop2f()) && (es.c2stopf() || es.c2stop2f())\n                }\n                #[cfg(stm32wlex)]\n                {\n                    es.c1stop2f() || es.c1stopf()\n                }\n                #[cfg(stm32wba)]\n                {\n                    es.stopf()\n                }\n                #[cfg(stm32l0)]\n                {\n                    es.wuf()\n                }\n            };\n\n            #[cfg(any(stm32wl, stm32wba))]\n            let re_initialize_timer = {\n                #[cfg(all(stm32wl, not(feature = \"_core-cm0p\")))]\n                {\n                    es.c1stop2f()\n                }\n                #[cfg(all(stm32wl, feature = \"_core-cm0p\"))]\n                {\n                    es.c2stop2f()\n                }\n                #[cfg(stm32wba)]\n                {\n                    es.stopf()\n                }\n            };\n\n            #[cfg(any(stm32l0, stm32wl, stm32wba))]\n            if re_initialize_rcc {\n                // when we wake from any stop mode we need to re-initialize the rcc\n                crate::rcc::reinit_saved(_cs);\n            }\n\n            #[cfg(stm32wba)]\n            match (es.stopf(), es.stop2f()) {\n                (true, true) => debug!(\"low power: WBA woke from STOP2\"),\n                (true, false) => debug!(\"low power: WBA woke from STOP0/1\"),\n                _ => {}\n            };\n\n            #[cfg(stm32wl)]\n            match (es.c1stopf(), es.c1stop2f()) {\n                (true, false) => debug!(\"low power: cpu1 has been in STOP1\"),\n                (false, true) => debug!(\"low power: cpu1 has been in STOP2\"),\n                (true, true) => debug!(\"low power: cpu1 has been in STOP1 and STOP2 ???\"),\n                (false, false) => trace!(\"low power: cpu1 stop mode not entered\"),\n            };\n\n            #[cfg(stm32wl5x)]\n            // TODO: only for the current cpu\n            match (es.c2stopf(), es.c2stop2f()) {\n                (true, false) => debug!(\"low power: cpu2 has been in STOP1\"),\n                (false, true) => debug!(\"low power: cpu2 has been in STOP2\"),\n                (true, true) => debug!(\"low power: cpu2 has been in STOP1 and STOP2 ???\"),\n                (false, false) => trace!(\"low power: cpu2 stop mode not entered\"),\n            };\n\n            #[cfg(stm32wb)]\n            match (es.c1stopf(), es.c2stopf()) {\n                (true, false) => debug!(\"low power: cpu1 has been in STOP\"),\n                (false, true) => debug!(\"low power: cpu2 has been in STOP\"),\n                (true, true) => debug!(\"low power: cpu1 and cpu2 have been in STOP\"),\n                (false, false) => trace!(\"low power: stop mode not entered\"),\n            };\n\n            #[cfg(stm32l0)]\n            match es.wuf() {\n                true => debug!(\"low power: L0 has been in stop\"),\n                _ => {}\n            };\n\n            clear_flags();\n\n            #[cfg(stm32wl5x)]\n            drop(lock);\n\n            #[cfg(any(stm32wl, stm32wba))]\n            if re_initialize_timer {\n                trace!(\"low power: re-initializing timer\");\n                // when we wake from STOP2, we need to re-initialize the time driver\n                #[cfg(not(feature = \"_lp-time-driver\"))]\n                super::get_driver().init_timer(_cs);\n            }\n        }\n    }\n}\n\nunsafe fn on_wakeup_irq_or_event() {\n    if !get_driver().is_stopped() {\n        //trace!(\"low power: time driver not stopped!\");\n        return;\n    }\n\n    critical_section::with(|cs| {\n        platform::exit_stop(cs);\n\n        get_driver().resume_time(cs);\n        trace!(\"low power: resumed\");\n    });\n}\n\nfn configure_pwr(cs: CriticalSection) {\n    const fn get_scb() -> SCB {\n        unsafe { mem::transmute(()) }\n    }\n\n    get_scb().clear_sleepdeep();\n    platform::clear_flags();\n\n    compiler_fence(Ordering::Acquire);\n\n    let Some(stop_mode) = get_stop_mode(cs) else {\n        //trace!(\"low power: no stop mode available\");\n        return;\n    };\n\n    if get_driver().pause_time(cs).is_err() {\n        warn!(\"low_power: failed to pause time, not entering stop\");\n    }\n\n    if platform::enter_stop(cs, stop_mode).is_err() {\n        warn!(\"low_power: failed to enter stop\");\n    }\n\n    #[cfg(stm32l0)]\n    trace!(\"low power: enter stop\");\n    #[cfg(not(stm32l0))]\n    trace!(\"low power: enter stop: {}\", stop_mode);\n\n    #[cfg(not(feature = \"low-power-debug-with-sleep\"))]\n    get_scb().set_sleepdeep();\n}\n\n/// Sleep with WFI, attempting to enter the deepest STOP mode possible.\n///\n/// If it's not possible to enter any STOP mode due to running peripherals it will\n/// still do a `WFI` sleep. Therefore this function is equivalent to `WFI` except\n/// with lower power consumption and higher latency.\n///\n/// ## SAFETY\n///\n/// Care must be taken that we have ensured that the system is ready to go to deep\n/// sleep, otherwise HAL peripherals may misbehave. HAL drivers automatically prevent\n/// sleep as needed, but you might have to do it manually if you're using some peripherals\n/// with the PAC directly.\npub unsafe fn sleep(cs: CriticalSection) {\n    configure_pwr(cs);\n\n    #[cfg(feature = \"low-power-defmt-flush\")]\n    defmt::flush();\n\n    cortex_m::asm::dsb();\n    cortex_m::asm::wfi();\n\n    on_wakeup_irq_or_event();\n}\n"
  },
  {
    "path": "embassy-stm32/src/lptim/channel.rs",
    "content": "/// Timer channel.\n#[derive(Clone, Copy)]\npub enum Channel {\n    /// Channel 1.\n    Ch1,\n    /// Channel 2.\n    Ch2,\n}\n\nimpl Channel {\n    /// Get the channel index (0..1)\n    pub fn index(&self) -> usize {\n        match self {\n            Channel::Ch1 => 0,\n            Channel::Ch2 => 1,\n        }\n    }\n}\n"
  },
  {
    "path": "embassy-stm32/src/lptim/mod.rs",
    "content": "//! Low-power timer (LPTIM)\n\npub mod pwm;\npub mod timer;\n\nuse crate::rcc::RccPeripheral;\n\n/// Timer channel.\n#[cfg(any(lptim_v2a, lptim_v2b))]\nmod channel;\n#[cfg(any(lptim_v2a, lptim_v2b))]\npub use channel::Channel;\nuse embassy_hal_internal::PeripheralType;\n\nuse crate::interrupt;\n\npin_trait!(OutputPin, BasicInstance);\npin_trait!(Channel1Pin, BasicInstance);\npin_trait!(Channel2Pin, BasicInstance);\n\npub(crate) trait SealedInstance: RccPeripheral {\n    fn regs() -> crate::pac::lptim::Lptim;\n}\n\npub(crate) trait SealedBasicInstance: RccPeripheral {\n    type GlobalInterrupt: interrupt::typelevel::Interrupt;\n}\n\n/// LPTIM basic instance trait.\n#[allow(private_bounds)]\npub trait BasicInstance: PeripheralType + SealedBasicInstance + 'static {}\n\n/// LPTIM instance trait.\n#[allow(private_bounds)]\npub trait Instance: BasicInstance + SealedInstance + 'static {}\n\nforeach_interrupt! {\n    ($inst:ident, lptim, LPTIM, GLOBAL, $irq:ident) => {\n        impl SealedInstance for crate::peripherals::$inst {\n            fn regs() -> crate::pac::lptim::Lptim {\n                crate::pac::$inst\n            }\n        }\n        impl SealedBasicInstance for crate::peripherals::$inst {\n            type GlobalInterrupt = crate::interrupt::typelevel::$irq;\n        }\n        impl BasicInstance for crate::peripherals::$inst {}\n        impl Instance for crate::peripherals::$inst {}\n    };\n    ($inst:ident, lptim, LPTIM_BASIC, GLOBAL, $irq:ident) => {\n        impl SealedBasicInstance for crate::peripherals::$inst {\n            type GlobalInterrupt = crate::interrupt::typelevel::$irq;\n        }\n        impl BasicInstance for crate::peripherals::$inst {}\n    };\n}\n"
  },
  {
    "path": "embassy-stm32/src/lptim/pwm.rs",
    "content": "//! PWM driver.\n\nuse core::marker::PhantomData;\n\nuse embassy_hal_internal::Peri;\n\n#[cfg(not(any(lptim_v2a, lptim_v2b)))]\nuse super::OutputPin;\nuse super::timer::Timer;\nuse super::{BasicInstance, Instance};\n#[cfg(any(lptim_v2a, lptim_v2b))]\nuse super::{Channel1Pin, Channel2Pin, channel::Channel, timer::ChannelDirection};\n#[cfg(gpio_v2)]\nuse crate::gpio::Pull;\nuse crate::gpio::{AfType, Flex, OutputType, Speed};\nuse crate::time::Hertz;\n\n/// Output marker type.\npub enum Output {}\n/// Channel 1 marker type.\npub enum Ch1 {}\n/// Channel 2 marker type.\npub enum Ch2 {}\n\n/// PWM pin wrapper.\n///\n/// This wraps a pin to make it usable with PWM.\npub struct PwmPin<'d, T, C> {\n    _pin: Flex<'d>,\n    phantom: PhantomData<(T, C)>,\n}\n\n/// PWM pin config\n///\n/// This configures the pwm pin settings\npub struct PwmPinConfig {\n    /// PWM Pin output type\n    pub output_type: OutputType,\n    /// PWM Pin speed\n    pub speed: Speed,\n    /// PWM Pin pull type\n    #[cfg(gpio_v2)]\n    pub pull: Pull,\n}\n\nmacro_rules! channel_impl {\n    ($new_chx:ident, $new_chx_with_config:ident, $channel:ident, $pin_trait:ident) => {\n        impl<'d, T: BasicInstance> PwmPin<'d, T, $channel> {\n            #[doc = concat!(\"Create a new \", stringify!($channel), \" PWM pin instance.\")]\n            pub fn $new_chx(pin: Peri<'d, impl $pin_trait<T>>) -> Self {\n                critical_section::with(|_| {\n                    pin.set_low();\n                    set_as_af!(pin, AfType::output(OutputType::PushPull, Speed::VeryHigh));\n                });\n                PwmPin {\n                    _pin: Flex::new(pin),\n                    phantom: PhantomData,\n                }\n            }\n            #[doc = concat!(\"Create a new \", stringify!($channel), \" PWM pin instance with config.\")]\n            pub fn $new_chx_with_config(pin: Peri<'d, impl $pin_trait<T>>, pin_config: PwmPinConfig) -> Self {\n                critical_section::with(|_| {\n                    pin.set_low();\n                    #[cfg(gpio_v1)]\n                    set_as_af!(pin, AfType::output(pin_config.output_type, pin_config.speed));\n                    #[cfg(gpio_v2)]\n                    set_as_af!(\n                        pin,\n                        AfType::output_pull(pin_config.output_type, pin_config.speed, pin_config.pull)\n                    );\n                });\n                PwmPin {\n                    _pin: Flex::new(pin),\n                    phantom: PhantomData,\n                }\n            }\n        }\n    };\n}\n\n#[cfg(not(any(lptim_v2a, lptim_v2b)))]\nchannel_impl!(new, new_with_config, Output, OutputPin);\n#[cfg(any(lptim_v2a, lptim_v2b))]\nchannel_impl!(new_ch1, new_ch1_with_config, Ch1, Channel1Pin);\n#[cfg(any(lptim_v2a, lptim_v2b))]\nchannel_impl!(new_ch2, new_ch2_with_config, Ch2, Channel2Pin);\n\n/// PWM driver.\npub struct Pwm<'d, T: Instance> {\n    inner: Timer<'d, T>,\n}\n\n#[cfg(not(any(lptim_v2a, lptim_v2b)))]\nimpl<'d, T: Instance> Pwm<'d, T> {\n    /// Create a new PWM driver.\n    pub fn new(tim: Peri<'d, T>, _output_pin: PwmPin<'d, T, Output>, freq: Hertz) -> Self {\n        Self::new_inner(tim, freq)\n    }\n\n    /// Set the duty.\n    ///\n    /// The value ranges from 0 for 0% duty, to [`get_max_duty`](Self::get_max_duty) for 100% duty, both included.\n    pub fn set_duty(&mut self, duty: u16) {\n        assert!(duty <= self.get_max_duty());\n        self.inner.set_compare_value(duty)\n    }\n\n    /// Get the duty.\n    ///\n    /// The value ranges from 0 for 0% duty, to [`get_max_duty`](Self::get_max_duty) for 100% duty, both included.\n    pub fn get_duty(&self) -> u16 {\n        self.inner.get_compare_value()\n    }\n\n    fn post_init(&mut self) {}\n}\n\n#[cfg(any(lptim_v2a, lptim_v2b))]\nimpl<'d, T: Instance> Pwm<'d, T> {\n    /// Create a new PWM driver.\n    pub fn new(\n        tim: Peri<'d, T>,\n        _ch1_pin: Option<PwmPin<'d, T, Ch1>>,\n        _ch2_pin: Option<PwmPin<'d, T, Ch2>>,\n        freq: Hertz,\n    ) -> Self {\n        Self::new_inner(tim, freq)\n    }\n\n    /// Enable the given channel.\n    pub fn enable(&mut self, channel: Channel) {\n        self.inner.enable_channel(channel, true);\n    }\n\n    /// Disable the given channel.\n    pub fn disable(&mut self, channel: Channel) {\n        self.inner.enable_channel(channel, false);\n    }\n\n    /// Check whether given channel is enabled\n    pub fn is_enabled(&self, channel: Channel) -> bool {\n        self.inner.get_channel_enable_state(channel)\n    }\n\n    /// Set the duty for a given channel.\n    ///\n    /// The value ranges from 0 for 0% duty, to [`get_max_duty`](Self::get_max_duty) for 100% duty, both included.\n    pub fn set_duty(&mut self, channel: Channel, duty: u16) {\n        assert!(duty <= self.get_max_duty());\n        self.inner.set_compare_value(channel, duty)\n    }\n\n    /// Get the duty for a given channel.\n    ///\n    /// The value ranges from 0 for 0% duty, to [`get_max_duty`](Self::get_max_duty) for 100% duty, both included.\n    pub fn get_duty(&self, channel: Channel) -> u16 {\n        self.inner.get_compare_value(channel)\n    }\n\n    fn post_init(&mut self) {\n        [Channel::Ch1, Channel::Ch2].iter().for_each(|&channel| {\n            self.inner.set_channel_direction(channel, ChannelDirection::OutputPwm);\n        });\n    }\n}\n\nimpl<'d, T: Instance> Pwm<'d, T> {\n    fn new_inner(tim: Peri<'d, T>, freq: Hertz) -> Self {\n        let mut this = Self { inner: Timer::new(tim) };\n\n        this.inner.enable();\n        this.set_frequency(freq);\n\n        this.post_init();\n\n        this.inner.continuous_mode_start();\n\n        this\n    }\n\n    /// Set PWM frequency.\n    ///\n    /// Note: when you call this, the max duty value changes, so you will have to\n    /// call `set_duty` on all channels with the duty calculated based on the new max duty.\n    pub fn set_frequency(&mut self, frequency: Hertz) {\n        self.inner.set_frequency(frequency);\n    }\n\n    /// Get max duty value.\n    ///\n    /// This value depends on the configured frequency and the timer's clock rate from RCC.\n    pub fn get_max_duty(&self) -> u16 {\n        self.inner.get_max_compare_value() + 1\n    }\n}\n"
  },
  {
    "path": "embassy-stm32/src/lptim/timer/channel_direction.rs",
    "content": "use crate::pac::lptim::vals;\n\n/// Direction of a low-power timer channel\npub enum ChannelDirection {\n    /// Use channel as a PWM output\n    OutputPwm,\n    /// Use channel as an input capture\n    InputCapture,\n}\n\nimpl From<ChannelDirection> for vals::Ccsel {\n    fn from(direction: ChannelDirection) -> Self {\n        match direction {\n            ChannelDirection::OutputPwm => vals::Ccsel::OUTPUT_COMPARE,\n            ChannelDirection::InputCapture => vals::Ccsel::INPUT_CAPTURE,\n        }\n    }\n}\n"
  },
  {
    "path": "embassy-stm32/src/lptim/timer/mod.rs",
    "content": "//! Low-level timer driver.\nmod prescaler;\n\nuse embassy_hal_internal::Peri;\n\n#[cfg(any(lptim_v2a, lptim_v2b))]\nuse super::channel::Channel;\n#[cfg(any(lptim_v2a, lptim_v2b))]\nmod channel_direction;\n#[cfg(any(lptim_v2a, lptim_v2b))]\npub use channel_direction::ChannelDirection;\nuse prescaler::Prescaler;\n\nuse super::Instance;\nuse crate::rcc;\nuse crate::time::Hertz;\n\n/// Low-level timer driver.\npub struct Timer<'d, T: Instance> {\n    _tim: Peri<'d, T>,\n}\n\nimpl<'d, T: Instance> Timer<'d, T> {\n    /// Create a new timer driver.\n    pub fn new(tim: Peri<'d, T>) -> Self {\n        rcc::enable_and_reset::<T>();\n\n        Self { _tim: tim }\n    }\n\n    /// Enable the timer.\n    pub fn enable(&self) {\n        T::regs().cr().modify(|w| w.set_enable(true));\n    }\n\n    /// Disable the timer.\n    pub fn disable(&self) {\n        T::regs().cr().modify(|w| w.set_enable(false));\n    }\n\n    /// Start the timer in single pulse mode.\n    pub fn single_mode_start(&self) {\n        T::regs().cr().modify(|w| w.set_sngstrt(true));\n    }\n\n    /// Start the timer in continuous mode.\n    pub fn continuous_mode_start(&self) {\n        T::regs().cr().modify(|w| w.set_cntstrt(true));\n    }\n\n    /// Set the frequency of how many times per second the timer counts up to the max value or down to 0.\n    pub fn set_frequency(&self, frequency: Hertz) {\n        let f = frequency.0;\n        assert!(f > 0);\n\n        let pclk_f = T::frequency().0;\n\n        let pclk_ticks_per_timer_period = pclk_f / f;\n\n        let psc = Prescaler::from_ticks(pclk_ticks_per_timer_period);\n        let arr = psc.scale_down(pclk_ticks_per_timer_period);\n\n        T::regs().cfgr().modify(|r| r.set_presc((&psc).into()));\n        T::regs().arr().modify(|r| r.set_arr(arr.into()));\n    }\n\n    /// Get the timer frequency.\n    pub fn get_frequency(&self) -> Hertz {\n        let pclk_f = T::frequency();\n        let arr = T::regs().arr().read().arr();\n        let psc = Prescaler::from(T::regs().cfgr().read().presc());\n\n        pclk_f / psc.scale_up(arr)\n    }\n\n    /// Get the clock frequency of the timer (before prescaler is applied).\n    pub fn get_clock_frequency(&self) -> Hertz {\n        T::frequency()\n    }\n\n    /// Get max compare value. This depends on the timer frequency and the clock frequency from RCC.\n    pub fn get_max_compare_value(&self) -> u16 {\n        T::regs().arr().read().arr()\n    }\n}\n\n#[cfg(any(lptim_v2a, lptim_v2b))]\nimpl<'d, T: Instance> Timer<'d, T> {\n    /// Enable/disable a channel.\n    pub fn enable_channel(&self, channel: Channel, enable: bool) {\n        T::regs().ccmr(0).modify(|w| {\n            w.set_cce(channel.index(), enable);\n        });\n    }\n\n    /// Get enable/disable state of a channel\n    pub fn get_channel_enable_state(&self, channel: Channel) -> bool {\n        T::regs().ccmr(0).read().cce(channel.index())\n    }\n\n    /// Set compare value for a channel.\n    pub fn set_compare_value(&self, channel: Channel, value: u16) {\n        T::regs().ccr(channel.index()).modify(|w| w.set_ccr(value));\n    }\n\n    /// Get compare value for a channel.\n    pub fn get_compare_value(&self, channel: Channel) -> u16 {\n        T::regs().ccr(channel.index()).read().ccr()\n    }\n\n    /// Set channel direction.\n    #[cfg(any(lptim_v2a, lptim_v2b))]\n    pub fn set_channel_direction(&self, channel: Channel, direction: ChannelDirection) {\n        T::regs()\n            .ccmr(0)\n            .modify(|w| w.set_ccsel(channel.index(), direction.into()));\n    }\n\n    /// Enable the timer interrupt.\n    pub fn enable_interrupt(&self) {\n        T::regs().dier().modify(|w| w.set_arrmie(true));\n    }\n\n    /// Disable the timer interrupt.\n    pub fn disable_interrupt(&self) {\n        T::regs().dier().modify(|w| w.set_arrmie(false));\n    }\n\n    /// Check if the timer interrupt is enabled.\n    pub fn is_interrupt_enabled(&self) -> bool {\n        T::regs().dier().read().arrmie()\n    }\n\n    /// Check if the timer interrupt is pending.\n    pub fn is_interrupt_pending(&self) -> bool {\n        T::regs().isr().read().arrm()\n    }\n\n    /// Clear the timer interrupt.\n    pub fn clear_interrupt(&self) {\n        T::regs().icr().write(|w| w.set_arrmcf(true));\n    }\n}\n\n#[cfg(not(any(lptim_v2a, lptim_v2b)))]\nimpl<'d, T: Instance> Timer<'d, T> {\n    /// Set compare value for a channel.\n    pub fn set_compare_value(&self, value: u16) {\n        T::regs().cmp().modify(|w| w.set_cmp(value));\n    }\n\n    /// Get compare value for a channel.\n    pub fn get_compare_value(&self) -> u16 {\n        T::regs().cmp().read().cmp()\n    }\n\n    /// Enable the timer interrupt.\n    pub fn enable_interrupt(&self) {\n        T::regs().ier().modify(|w| w.set_arrmie(true));\n    }\n\n    /// Disable the timer interrupt.\n    pub fn disable_interrupt(&self) {\n        T::regs().ier().modify(|w| w.set_arrmie(false));\n    }\n\n    /// Check if the timer interrupt is enabled.\n    pub fn is_interrupt_enabled(&self) -> bool {\n        T::regs().ier().read().arrmie()\n    }\n\n    /// Check if the timer interrupt is pending.\n    pub fn is_interrupt_pending(&self) -> bool {\n        T::regs().isr().read().arrm()\n    }\n\n    /// Clear the timer interrupt.\n    pub fn clear_interrupt(&self) {\n        T::regs().icr().write(|w| w.set_arrmcf(true));\n    }\n}\n"
  },
  {
    "path": "embassy-stm32/src/lptim/timer/prescaler.rs",
    "content": "//! Low-level timer driver.\n\nuse crate::pac::lptim::vals;\n\npub enum Prescaler {\n    Div1,\n    Div2,\n    Div4,\n    Div8,\n    Div16,\n    Div32,\n    Div64,\n    Div128,\n}\n\nimpl From<&Prescaler> for vals::Presc {\n    fn from(prescaler: &Prescaler) -> Self {\n        match prescaler {\n            Prescaler::Div1 => vals::Presc::DIV1,\n            Prescaler::Div2 => vals::Presc::DIV2,\n            Prescaler::Div4 => vals::Presc::DIV4,\n            Prescaler::Div8 => vals::Presc::DIV8,\n            Prescaler::Div16 => vals::Presc::DIV16,\n            Prescaler::Div32 => vals::Presc::DIV32,\n            Prescaler::Div64 => vals::Presc::DIV64,\n            Prescaler::Div128 => vals::Presc::DIV128,\n        }\n    }\n}\n\nimpl From<vals::Presc> for Prescaler {\n    fn from(prescaler: vals::Presc) -> Self {\n        match prescaler {\n            vals::Presc::DIV1 => Prescaler::Div1,\n            vals::Presc::DIV2 => Prescaler::Div2,\n            vals::Presc::DIV4 => Prescaler::Div4,\n            vals::Presc::DIV8 => Prescaler::Div8,\n            vals::Presc::DIV16 => Prescaler::Div16,\n            vals::Presc::DIV32 => Prescaler::Div32,\n            vals::Presc::DIV64 => Prescaler::Div64,\n            vals::Presc::DIV128 => Prescaler::Div128,\n        }\n    }\n}\n\nimpl From<&Prescaler> for u32 {\n    fn from(prescaler: &Prescaler) -> Self {\n        match prescaler {\n            Prescaler::Div1 => 1,\n            Prescaler::Div2 => 2,\n            Prescaler::Div4 => 4,\n            Prescaler::Div8 => 8,\n            Prescaler::Div16 => 16,\n            Prescaler::Div32 => 32,\n            Prescaler::Div64 => 64,\n            Prescaler::Div128 => 128,\n        }\n    }\n}\n\nimpl From<u32> for Prescaler {\n    fn from(prescaler: u32) -> Self {\n        match prescaler {\n            1 => Prescaler::Div1,\n            2 => Prescaler::Div2,\n            4 => Prescaler::Div4,\n            8 => Prescaler::Div8,\n            16 => Prescaler::Div16,\n            32 => Prescaler::Div32,\n            64 => Prescaler::Div64,\n            128 => Prescaler::Div128,\n            _ => unreachable!(),\n        }\n    }\n}\n\nimpl Prescaler {\n    pub fn from_ticks(ticks: u32) -> Self {\n        // We need to scale down to a 16-bit range\n        (ticks >> 16).next_power_of_two().into()\n    }\n\n    pub fn scale_down(&self, ticks: u32) -> u16 {\n        (ticks / u32::from(self)).try_into().unwrap()\n    }\n\n    pub fn scale_up(&self, ticks: u16) -> u32 {\n        u32::from(self) * ticks as u32\n    }\n}\n"
  },
  {
    "path": "embassy-stm32/src/ltdc.rs",
    "content": "//! LTDC - LCD-TFT Display Controller\n//! See ST application note AN4861: Introduction to LCD-TFT display controller (LTDC) on STM32 MCUs for high level details\n//! This module was tested against the stm32h735g-dk using the RM0468 ST reference manual for detailed register information\n\nuse core::future::poll_fn;\nuse core::marker::PhantomData;\nuse core::task::Poll;\n\nuse embassy_hal_internal::PeripheralType;\nuse embassy_sync::waitqueue::AtomicWaker;\nuse stm32_metapac::ltdc::regs::Dccr;\nuse stm32_metapac::ltdc::vals::{Bf1, Bf2, Cfuif, Clif, Crrif, Cterrif, Pf, Vbr};\n\nuse crate::gpio::{AfType, Flex, OutputType, Speed};\nuse crate::interrupt::typelevel::Interrupt;\nuse crate::interrupt::{self};\nuse crate::{Peri, peripherals, rcc};\n\nstatic LTDC_WAKER: AtomicWaker = AtomicWaker::new();\n\n/// LTDC error\n#[derive(Debug, PartialEq, Eq, Clone, Copy)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub enum Error {\n    /// FIFO underrun. Generated when a pixel is requested while the FIFO is empty\n    FifoUnderrun,\n    /// Transfer error. Generated when a bus error occurs\n    TransferError,\n}\n\n/// Display configuration parameters\n#[derive(Clone, Copy, Debug, PartialEq)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub struct LtdcConfiguration {\n    /// Active width in pixels\n    pub active_width: u16,\n    /// Active height in pixels\n    pub active_height: u16,\n\n    /// Horizontal back porch (in units of pixel clock period)\n    pub h_back_porch: u16,\n    /// Horizontal front porch (in units of pixel clock period)\n    pub h_front_porch: u16,\n    /// Vertical back porch (in units of horizontal scan line)\n    pub v_back_porch: u16,\n    /// Vertical front porch (in units of horizontal scan line)\n    pub v_front_porch: u16,\n\n    /// Horizontal synchronization width (in units of pixel clock period)\n    pub h_sync: u16,\n    /// Vertical synchronization height (in units of horizontal scan line)\n    pub v_sync: u16,\n\n    /// Horizontal synchronization polarity: `false`: active low, `true`: active high\n    pub h_sync_polarity: PolarityActive,\n    /// Vertical synchronization polarity: `false`: active low, `true`: active high\n    pub v_sync_polarity: PolarityActive,\n    /// Data enable polarity: `false`: active low, `true`: active high\n    pub data_enable_polarity: PolarityActive,\n    /// Pixel clock polarity: `false`: falling edge, `true`: rising edge\n    pub pixel_clock_polarity: PolarityEdge,\n}\n\n/// Edge polarity\n#[derive(Clone, Copy, Debug, PartialEq)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub enum PolarityEdge {\n    /// Falling edge\n    FallingEdge,\n    /// Rising edge\n    RisingEdge,\n}\n\n/// Active polarity\n#[derive(Clone, Copy, Debug, PartialEq)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub enum PolarityActive {\n    /// Active low\n    ActiveLow,\n    /// Active high\n    ActiveHigh,\n}\n\n/// LTDC driver.\npub struct Ltdc<'d, T: Instance> {\n    _peri: Peri<'d, T>,\n    _pins: Option<[Flex<'d>; 27]>,\n}\n\n/// LTDC interrupt handler.\npub struct InterruptHandler<T: Instance> {\n    _phantom: PhantomData<T>,\n}\n\n/// 24 bit color\n#[derive(Debug, PartialEq, Eq, Clone, Copy, Default)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub struct RgbColor {\n    /// Red\n    pub red: u8,\n    /// Green\n    pub green: u8,\n    /// Blue\n    pub blue: u8,\n}\n\n/// Layer\n#[derive(Debug, PartialEq, Eq, Clone, Copy)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub struct LtdcLayerConfig {\n    /// Layer number\n    pub layer: LtdcLayer,\n    /// Pixel format\n    pub pixel_format: PixelFormat,\n    /// Window left x in pixels\n    pub window_x0: u16,\n    /// Window right x in pixels\n    pub window_x1: u16,\n    /// Window top y in pixels\n    pub window_y0: u16,\n    /// Window bottom y in pixels\n    pub window_y1: u16,\n}\n\n/// Pixel format\n#[repr(u8)]\n#[derive(Debug, PartialEq, Eq, Clone, Copy)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\n#[cfg(not(ltdc_v1_3))]\npub enum PixelFormat {\n    /// ARGB8888\n    ARGB8888 = Pf::ARGB8888 as u8,\n    /// RGB888\n    RGB888 = Pf::RGB888 as u8,\n    /// RGB565\n    RGB565 = Pf::RGB565 as u8,\n    /// ARGB1555\n    ARGB1555 = Pf::ARGB1555 as u8,\n    /// ARGB4444\n    ARGB4444 = Pf::ARGB4444 as u8,\n    /// L8 (8-bit luminance)\n    L8 = Pf::L8 as u8,\n    /// AL44 (4-bit alpha, 4-bit luminance\n    AL44 = Pf::AL44 as u8,\n    /// AL88 (8-bit alpha, 8-bit luminance)\n    AL88 = Pf::AL88 as u8,\n}\n/// Pixel format\n#[repr(u8)]\n#[derive(Debug, PartialEq, Eq, Clone, Copy)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\n#[cfg(ltdc_v1_3)]\npub enum PixelFormat {\n    /// ARGB8888\n    ARGB8888 = Pf::ARGB8888 as u8,\n    /// ABGR8888\n    ABGR8888 = Pf::ABGR8888 as u8,\n    /// RGBA8888\n    RGBA8888 = Pf::RGBA8888 as u8,\n    /// BGRA8888\n    BGRA8888 = Pf::BGRA8888 as u8,\n    /// RGB565\n    RGB565 = Pf::RGB565 as u8,\n    /// BGR565\n    BGR565 = Pf::BGR565 as u8,\n    /// RGB888\n    RGB888 = Pf::RGB888 as u8,\n    /// Flexible\n    FLEXIBLE = Pf::FLEXIBLE as u8,\n}\n\nimpl PixelFormat {\n    /// Number of bytes per pixel\n    pub fn bytes_per_pixel(&self) -> usize {\n        #[cfg(not(ltdc_v1_3))]\n        match self {\n            PixelFormat::ARGB8888 => 4,\n            PixelFormat::RGB888 => 3,\n            PixelFormat::RGB565 | PixelFormat::ARGB4444 | PixelFormat::ARGB1555 | PixelFormat::AL88 => 2,\n            PixelFormat::AL44 | PixelFormat::L8 => 1,\n        }\n        #[cfg(ltdc_v1_3)]\n        match self {\n            PixelFormat::ARGB8888 | PixelFormat::ABGR8888 | PixelFormat::RGBA8888 | PixelFormat::BGRA8888 => 4,\n            PixelFormat::RGB888 => 3,\n            PixelFormat::RGB565 | PixelFormat::BGR565 => 2,\n            PixelFormat::FLEXIBLE => todo!(\"Flexable pixels are not yet implemented\"),\n        }\n    }\n}\n\n/// Ltdc Blending Layer\n#[repr(usize)]\n#[derive(Debug, PartialEq, Eq, Clone, Copy)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub enum LtdcLayer {\n    /// Bottom layer\n    Layer1 = 0,\n    /// Top layer\n    Layer2 = 1,\n}\n\nimpl<T: Instance> interrupt::typelevel::Handler<T::Interrupt> for InterruptHandler<T> {\n    unsafe fn on_interrupt() {\n        cortex_m::asm::dsb();\n        Ltdc::<T>::enable_interrupts(false);\n        LTDC_WAKER.wake();\n    }\n}\n\nimpl<'d, T: Instance> Ltdc<'d, T> {\n    // Create a new LTDC driver without specifying color and control pins. This is typically used if you want to drive a display though a DsiHost\n    /// Note: Full-Duplex modes are not supported at this time\n    pub fn new(peri: Peri<'d, T>) -> Self {\n        Self::setup_clocks();\n        Self {\n            _peri: peri,\n            _pins: None,\n        }\n    }\n\n    /// Create a new LTDC driver. 8 pins per color channel for blue, green and red\n    #[allow(clippy::too_many_arguments)]\n    pub fn new_with_pins(\n        peri: Peri<'d, T>,\n        _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd,\n        clk: Peri<'d, impl ClkPin<T>>,\n        hsync: Peri<'d, impl HsyncPin<T>>,\n        vsync: Peri<'d, impl VsyncPin<T>>,\n        b0: Peri<'d, impl B0Pin<T>>,\n        b1: Peri<'d, impl B1Pin<T>>,\n        b2: Peri<'d, impl B2Pin<T>>,\n        b3: Peri<'d, impl B3Pin<T>>,\n        b4: Peri<'d, impl B4Pin<T>>,\n        b5: Peri<'d, impl B5Pin<T>>,\n        b6: Peri<'d, impl B6Pin<T>>,\n        b7: Peri<'d, impl B7Pin<T>>,\n        g0: Peri<'d, impl G0Pin<T>>,\n        g1: Peri<'d, impl G1Pin<T>>,\n        g2: Peri<'d, impl G2Pin<T>>,\n        g3: Peri<'d, impl G3Pin<T>>,\n        g4: Peri<'d, impl G4Pin<T>>,\n        g5: Peri<'d, impl G5Pin<T>>,\n        g6: Peri<'d, impl G6Pin<T>>,\n        g7: Peri<'d, impl G7Pin<T>>,\n        r0: Peri<'d, impl R0Pin<T>>,\n        r1: Peri<'d, impl R1Pin<T>>,\n        r2: Peri<'d, impl R2Pin<T>>,\n        r3: Peri<'d, impl R3Pin<T>>,\n        r4: Peri<'d, impl R4Pin<T>>,\n        r5: Peri<'d, impl R5Pin<T>>,\n        r6: Peri<'d, impl R6Pin<T>>,\n        r7: Peri<'d, impl R7Pin<T>>,\n    ) -> Self {\n        Self::setup_clocks();\n        let pins = [\n            new_pin!(clk, AfType::output(OutputType::PushPull, Speed::VeryHigh)).unwrap(),\n            new_pin!(hsync, AfType::output(OutputType::PushPull, Speed::VeryHigh)).unwrap(),\n            new_pin!(vsync, AfType::output(OutputType::PushPull, Speed::VeryHigh)).unwrap(),\n            new_pin!(b0, AfType::output(OutputType::PushPull, Speed::VeryHigh)).unwrap(),\n            new_pin!(b1, AfType::output(OutputType::PushPull, Speed::VeryHigh)).unwrap(),\n            new_pin!(b2, AfType::output(OutputType::PushPull, Speed::VeryHigh)).unwrap(),\n            new_pin!(b3, AfType::output(OutputType::PushPull, Speed::VeryHigh)).unwrap(),\n            new_pin!(b4, AfType::output(OutputType::PushPull, Speed::VeryHigh)).unwrap(),\n            new_pin!(b5, AfType::output(OutputType::PushPull, Speed::VeryHigh)).unwrap(),\n            new_pin!(b6, AfType::output(OutputType::PushPull, Speed::VeryHigh)).unwrap(),\n            new_pin!(b7, AfType::output(OutputType::PushPull, Speed::VeryHigh)).unwrap(),\n            new_pin!(g0, AfType::output(OutputType::PushPull, Speed::VeryHigh)).unwrap(),\n            new_pin!(g1, AfType::output(OutputType::PushPull, Speed::VeryHigh)).unwrap(),\n            new_pin!(g2, AfType::output(OutputType::PushPull, Speed::VeryHigh)).unwrap(),\n            new_pin!(g3, AfType::output(OutputType::PushPull, Speed::VeryHigh)).unwrap(),\n            new_pin!(g4, AfType::output(OutputType::PushPull, Speed::VeryHigh)).unwrap(),\n            new_pin!(g5, AfType::output(OutputType::PushPull, Speed::VeryHigh)).unwrap(),\n            new_pin!(g6, AfType::output(OutputType::PushPull, Speed::VeryHigh)).unwrap(),\n            new_pin!(g7, AfType::output(OutputType::PushPull, Speed::VeryHigh)).unwrap(),\n            new_pin!(r0, AfType::output(OutputType::PushPull, Speed::VeryHigh)).unwrap(),\n            new_pin!(r1, AfType::output(OutputType::PushPull, Speed::VeryHigh)).unwrap(),\n            new_pin!(r2, AfType::output(OutputType::PushPull, Speed::VeryHigh)).unwrap(),\n            new_pin!(r3, AfType::output(OutputType::PushPull, Speed::VeryHigh)).unwrap(),\n            new_pin!(r4, AfType::output(OutputType::PushPull, Speed::VeryHigh)).unwrap(),\n            new_pin!(r5, AfType::output(OutputType::PushPull, Speed::VeryHigh)).unwrap(),\n            new_pin!(r6, AfType::output(OutputType::PushPull, Speed::VeryHigh)).unwrap(),\n            new_pin!(r7, AfType::output(OutputType::PushPull, Speed::VeryHigh)).unwrap(),\n        ];\n\n        Self {\n            _peri: peri,\n            _pins: Some(pins),\n        }\n    }\n\n    /// Initialise and enable the display\n    pub fn init(&mut self, config: &LtdcConfiguration) {\n        use stm32_metapac::ltdc::vals::{Depol, Hspol, Pcpol, Vspol};\n        let ltdc = T::regs();\n\n        // check bus access\n        assert!(ltdc.gcr().read().0 == 0x2220); // reset value\n\n        // configure the HS, VS, DE and PC polarity\n        ltdc.gcr().modify(|w| {\n            w.set_hspol(match config.h_sync_polarity {\n                PolarityActive::ActiveHigh => Hspol::ACTIVE_HIGH,\n                PolarityActive::ActiveLow => Hspol::ACTIVE_LOW,\n            });\n\n            w.set_vspol(match config.v_sync_polarity {\n                PolarityActive::ActiveHigh => Vspol::ACTIVE_HIGH,\n                PolarityActive::ActiveLow => Vspol::ACTIVE_LOW,\n            });\n\n            w.set_depol(match config.data_enable_polarity {\n                PolarityActive::ActiveHigh => Depol::ACTIVE_HIGH,\n                PolarityActive::ActiveLow => Depol::ACTIVE_LOW,\n            });\n\n            w.set_pcpol(match config.pixel_clock_polarity {\n                PolarityEdge::RisingEdge => Pcpol::RISING_EDGE,\n                PolarityEdge::FallingEdge => Pcpol::FALLING_EDGE,\n            });\n        });\n\n        // set synchronization pulse width\n        ltdc.sscr().modify(|w| {\n            w.set_vsh(config.v_sync - 1);\n            w.set_hsw(config.h_sync - 1);\n        });\n\n        // set accumulated back porch\n        ltdc.bpcr().modify(|w| {\n            w.set_avbp(config.v_sync + config.v_back_porch - 1);\n            w.set_ahbp(config.h_sync + config.h_back_porch - 1);\n        });\n\n        // set accumulated active width\n        let aa_height = config.v_sync + config.v_back_porch + config.active_height - 1;\n        let aa_width = config.h_sync + config.h_back_porch + config.active_width - 1;\n        ltdc.awcr().modify(|w| {\n            w.set_aah(aa_height);\n            w.set_aaw(aa_width);\n        });\n\n        // set total width and height\n        let total_height: u16 = config.v_sync + config.v_back_porch + config.active_height + config.v_front_porch - 1;\n        let total_width: u16 = config.h_sync + config.h_back_porch + config.active_width + config.h_front_porch - 1;\n        ltdc.twcr().modify(|w| {\n            w.set_totalh(total_height);\n            w.set_totalw(total_width)\n        });\n\n        // set the background color value to black\n        ltdc.bccr().modify(|w| {\n            w.set_bcred(0);\n            w.set_bcgreen(0);\n            w.set_bcblue(0);\n        });\n\n        self.enable();\n    }\n\n    /// Set the enable bit in the control register and assert that it has been enabled\n    ///\n    /// This does need to be called if init has already been called\n    pub fn enable(&mut self) {\n        T::regs().gcr().modify(|w| w.set_ltdcen(true));\n        assert!(T::regs().gcr().read().ltdcen())\n    }\n\n    /// Unset the enable bit in the control register and assert that it has been disabled\n    pub fn disable(&mut self) {\n        T::regs().gcr().modify(|w| w.set_ltdcen(false));\n        assert!(!T::regs().gcr().read().ltdcen())\n    }\n\n    /// Initialise and enable the layer\n    ///\n    /// clut - a 256 length color look-up table applies to L8, AL44 and AL88 pixel format and will default to greyscale if `None` supplied and these pixel formats are used\n    pub fn init_layer(&mut self, layer_config: &LtdcLayerConfig, clut: Option<&[RgbColor]>) {\n        let ltdc = T::regs();\n        let layer = ltdc.layer(layer_config.layer as usize);\n\n        // 256 color look-up table for L8, AL88 and AL88 pixel formats\n        if let Some(clut) = clut {\n            assert_eq!(clut.len(), 256, \"Color lookup table must be exactly 256 in length\");\n            for (index, color) in clut.iter().enumerate() {\n                layer.clutwr().write(|w| {\n                    w.set_clutadd(index as u8);\n                    w.set_red(color.red);\n                    w.set_green(color.green);\n                    w.set_blue(color.blue);\n                });\n            }\n        }\n\n        // configure the horizontal start and stop position\n        let h_win_start = layer_config.window_x0 + ltdc.bpcr().read().ahbp() + 1;\n        let h_win_stop = layer_config.window_x1 + ltdc.bpcr().read().ahbp();\n        layer.whpcr().write(|w| {\n            w.set_whstpos(h_win_start);\n            w.set_whsppos(h_win_stop);\n        });\n\n        // configure the vertical start and stop position\n        let v_win_start = layer_config.window_y0 + ltdc.bpcr().read().avbp() + 1;\n        let v_win_stop = layer_config.window_y1 + ltdc.bpcr().read().avbp();\n        layer.wvpcr().write(|w| {\n            w.set_wvstpos(v_win_start);\n            w.set_wvsppos(v_win_stop)\n        });\n\n        // set the pixel format\n        layer\n            .pfcr()\n            .write(|w| w.set_pf(Pf::from_bits(layer_config.pixel_format as u8)));\n\n        // set the default color value to transparent black\n        layer.dccr().write_value(Dccr::default());\n\n        // set the global constant alpha value\n        let alpha = 0xFF;\n        layer.cacr().write(|w| w.set_consta(alpha));\n\n        // set the blending factors.\n        layer.bfcr().modify(|w| {\n            w.set_bf1(Bf1::PIXEL);\n            w.set_bf2(Bf2::PIXEL);\n        });\n\n        // calculate framebuffer pixel size in bytes\n        let bytes_per_pixel = layer_config.pixel_format.bytes_per_pixel() as u16;\n        let width = layer_config.window_x1 - layer_config.window_x0;\n        let height = layer_config.window_y1 - layer_config.window_y0;\n\n        // framebuffer pitch and line length\n        layer.cfblr().modify(|w| {\n            w.set_cfbp(width * bytes_per_pixel);\n            #[cfg(not(any(stm32u5, stm32f7)))]\n            w.set_cfbll(width * bytes_per_pixel + 7);\n            #[cfg(any(stm32u5, stm32f7))]\n            w.set_cfbll(width * bytes_per_pixel + 3);\n        });\n\n        // framebuffer line number\n        layer.cfblnr().modify(|w| w.set_cfblnbr(height));\n\n        // enable LTDC_Layer by setting LEN bit\n        layer.cr().modify(|w| {\n            if clut.is_some() {\n                w.set_cluten(true);\n            }\n            w.set_len(true);\n        });\n    }\n\n    /// Set the current buffer. The async function will return when buffer has been completely copied to the LCD screen\n    /// frame_buffer_addr is a pointer to memory that should not move (best to make it static)\n    pub async fn set_buffer(&mut self, layer: LtdcLayer, frame_buffer_addr: *const ()) -> Result<(), Error> {\n        let mut bits = T::regs().isr().read();\n\n        // if all clear\n        if !bits.fuif() && !bits.lif() && !bits.rrif() && !bits.terrif() {\n            // wait for interrupt\n            poll_fn(|cx| {\n                // quick check to avoid registration if already done.\n                let bits = T::regs().isr().read();\n                if bits.fuif() || bits.lif() || bits.rrif() || bits.terrif() {\n                    return Poll::Ready(());\n                }\n\n                LTDC_WAKER.register(cx.waker());\n                Self::clear_interrupt_flags(); // don't poison the request with old flags\n                Self::enable_interrupts(true);\n\n                // set the new frame buffer address\n                let layer = T::regs().layer(layer as usize);\n                layer.cfbar().modify(|w| w.set_cfbadd(frame_buffer_addr as u32));\n\n                // configure a shadow reload for the next blanking period\n                T::regs().srcr().write(|w| {\n                    w.set_vbr(Vbr::RELOAD);\n                });\n\n                // need to check condition after register to avoid a race\n                // condition that would result in lost notifications.\n                let bits = T::regs().isr().read();\n                if bits.fuif() || bits.lif() || bits.rrif() || bits.terrif() {\n                    Poll::Ready(())\n                } else {\n                    Poll::Pending\n                }\n            })\n            .await;\n\n            // re-read the status register after wait.\n            bits = T::regs().isr().read();\n        }\n\n        let result = if bits.fuif() {\n            Err(Error::FifoUnderrun)\n        } else if bits.terrif() {\n            Err(Error::TransferError)\n        } else if bits.lif() {\n            panic!(\"line interrupt event is disabled\")\n        } else if bits.rrif() {\n            // register reload flag is expected\n            Ok(())\n        } else {\n            unreachable!(\"all interrupt status values checked\")\n        };\n\n        Self::clear_interrupt_flags();\n        result\n    }\n\n    fn setup_clocks() {\n        critical_section::with(|_cs| {\n            // RM says the pllsaidivr should only be changed when pllsai is off. But this could have other unintended side effects. So let's just give it a try like this.\n            // According to the debugger, this bit gets set, anyway.\n            #[cfg(stm32f7)]\n            crate::pac::RCC\n                .dckcfgr1()\n                .modify(|w| w.set_pllsaidivr(stm32_metapac::rcc::vals::Pllsaidivr::DIV2));\n\n            // It is set to RCC_PLLSAIDIVR_2 in ST's BSP example for the STM32469I-DISCO.\n            #[cfg(stm32f4)]\n            crate::pac::RCC\n                .dckcfgr()\n                .modify(|w| w.set_pllsaidivr(stm32_metapac::rcc::vals::Pllsaidivr::DIV2));\n        });\n\n        rcc::enable_and_reset::<T>();\n    }\n\n    fn clear_interrupt_flags() {\n        T::regs().icr().write(|w| {\n            w.set_cfuif(Cfuif::CLEAR);\n            w.set_clif(Clif::CLEAR);\n            w.set_crrif(Crrif::CLEAR);\n            w.set_cterrif(Cterrif::CLEAR);\n        });\n    }\n\n    fn enable_interrupts(enable: bool) {\n        T::regs().ier().write(|w| {\n            w.set_fuie(enable);\n            w.set_lie(false); // we are not interested in the line interrupt enable event\n            w.set_rrie(enable);\n            w.set_terrie(enable)\n        });\n\n        // enable interrupts for LTDC peripheral\n        T::Interrupt::unpend();\n        if enable {\n            unsafe { T::Interrupt::enable() };\n        } else {\n            T::Interrupt::disable()\n        }\n    }\n}\n\nimpl<'d, T: Instance> Drop for Ltdc<'d, T> {\n    fn drop(&mut self) {}\n}\n\ntrait SealedInstance: crate::rcc::SealedRccPeripheral {\n    fn regs() -> crate::pac::ltdc::Ltdc;\n}\n\n/// LTDC instance trait.\n#[allow(private_bounds)]\npub trait Instance: SealedInstance + PeripheralType + crate::rcc::RccPeripheral + 'static + Send {\n    /// Interrupt for this LTDC instance.\n    type Interrupt: interrupt::typelevel::Interrupt;\n}\n\npin_trait!(ClkPin, Instance);\npin_trait!(HsyncPin, Instance);\npin_trait!(VsyncPin, Instance);\npin_trait!(DePin, Instance);\npin_trait!(R0Pin, Instance);\npin_trait!(R1Pin, Instance);\npin_trait!(R2Pin, Instance);\npin_trait!(R3Pin, Instance);\npin_trait!(R4Pin, Instance);\npin_trait!(R5Pin, Instance);\npin_trait!(R6Pin, Instance);\npin_trait!(R7Pin, Instance);\npin_trait!(G0Pin, Instance);\npin_trait!(G1Pin, Instance);\npin_trait!(G2Pin, Instance);\npin_trait!(G3Pin, Instance);\npin_trait!(G4Pin, Instance);\npin_trait!(G5Pin, Instance);\npin_trait!(G6Pin, Instance);\npin_trait!(G7Pin, Instance);\npin_trait!(B0Pin, Instance);\npin_trait!(B1Pin, Instance);\npin_trait!(B2Pin, Instance);\npin_trait!(B3Pin, Instance);\npin_trait!(B4Pin, Instance);\npin_trait!(B5Pin, Instance);\npin_trait!(B6Pin, Instance);\npin_trait!(B7Pin, Instance);\n\nforeach_interrupt!(\n    ($inst:ident, ltdc, LTDC, LO, $irq:ident) => {\n        impl Instance for peripherals::$inst {\n            type Interrupt = crate::interrupt::typelevel::$irq;\n        }\n\n        impl SealedInstance for peripherals::$inst {\n            fn regs() -> crate::pac::ltdc::Ltdc {\n                crate::pac::$inst\n            }\n        }\n    };\n);\n"
  },
  {
    "path": "embassy-stm32/src/macros.rs",
    "content": "#![macro_use]\n\nmacro_rules! peri_trait {\n    (\n        $(irqs: [$($irq:ident),*],)?\n    ) => {\n        #[allow(private_interfaces)]\n        pub(crate) trait SealedInstance {\n            #[allow(unused)]\n            fn info() -> &'static Info;\n            #[allow(unused)]\n            fn state() -> &'static State;\n        }\n\n        /// Peripheral instance trait.\n        #[allow(private_bounds)]\n        pub trait Instance: SealedInstance + crate::PeripheralType  + crate::rcc::RccPeripheral {\n            $($(\n                /// Interrupt for this peripheral.\n                type $irq: crate::interrupt::typelevel::Interrupt;\n            )*)?\n        }\n    };\n}\n\nmacro_rules! peri_trait_impl {\n    ($instance:ident, $info:expr) => {\n        #[allow(private_interfaces)]\n        impl SealedInstance for crate::peripherals::$instance {\n            fn info() -> &'static Info {\n                static INFO: Info = $info;\n                &INFO\n            }\n            fn state() -> &'static State {\n                static STATE: State = State::new();\n                &STATE\n            }\n        }\n        impl Instance for crate::peripherals::$instance {}\n    };\n}\n\nmacro_rules! pin_trait {\n    ($signal:ident, $instance:path $(, $mode:path)? $(, @$afio:ident)?) => {\n        #[doc = concat!(stringify!($signal), \" pin trait\")]\n        pub trait $signal<T: $instance $(, M: $mode)? $(, #[cfg(afio)] $afio)?>: crate::gpio::Pin {\n            #[cfg(not(afio))]\n            #[doc = concat!(\"Get the AF number needed to use this pin as \", stringify!($signal))]\n            fn af_num(&self) -> u8;\n\n            #[cfg(afio)]\n            #[doc = concat!(\"Configures AFIO_MAPR to use this pin as \", stringify!($signal))]\n            fn afio_remap(&self);\n        }\n    };\n}\n\nmacro_rules! pin_trait_impl {\n    (crate::$mod:ident::$trait:ident$(<$mode:ident>)?, $instance:ident, $pin:ident, $af:expr $(, $afio:path)?) => {\n        #[cfg(afio)]\n        impl crate::$mod::$trait<crate::peripherals::$instance $(, crate::$mod::$mode)? $(, $afio)?> for crate::peripherals::$pin {\n            fn afio_remap(&self) {\n                // nothing\n            }\n        }\n\n        #[cfg(not(afio))]\n        impl crate::$mod::$trait<crate::peripherals::$instance $(, crate::$mod::$mode)?> for crate::peripherals::$pin {\n            fn af_num(&self) -> u8 {\n                $af\n            }\n        }\n    };\n}\n\n#[cfg(afio)]\nmacro_rules! pin_trait_afio_impl {\n    (@set mapr, $setter:ident, $val:expr) => {\n        crate::pac::AFIO.mapr().modify(|w| {\n            w.set_swj_cfg(crate::pac::afio::vals::SwjCfg::NO_OP);\n            w.$setter($val);\n        });\n    };\n    (@set mapr2, $setter:ident, $val:expr) => {\n        crate::pac::AFIO.mapr2().modify(|w| {\n            w.$setter($val);\n        });\n    };\n    (crate::$mod:ident::$trait:ident<$mode:ident>, $instance:ident, $pin:ident, {$reg:ident, $setter:ident, $type:ident, [$($val:expr),+]}) => {\n        $(\n            impl crate::$mod::$trait<crate::peripherals::$instance, crate::$mod::$mode, crate::gpio::$type<$val>> for crate::peripherals::$pin {\n                fn afio_remap(&self) {\n                    pin_trait_afio_impl!(@set $reg, $setter, $val);\n                }\n            }\n        )+\n    };\n    (crate::$mod:ident::$trait:ident, $instance:ident, $pin:ident, {$reg:ident, $setter:ident, $type:ident, [$($val:expr),+]}) => {\n        $(\n            impl crate::$mod::$trait<crate::peripherals::$instance, crate::gpio::$type<$val>> for crate::peripherals::$pin {\n                fn afio_remap(&self) {\n                    pin_trait_afio_impl!(@set $reg, $setter, $val);\n                }\n            }\n        )+\n    };\n}\n\n#[allow(unused_macros)]\nmacro_rules! sel_trait_impl {\n    (crate::$mod:ident::$trait:ident$(<$mode:ident>)?, $instance:ident, $pin:ident, $sel:expr) => {\n        impl crate::$mod::$trait<crate::peripherals::$instance $(, crate::$mod::$mode)?> for crate::peripherals::$pin {\n            fn sel(&self) -> u8 {\n                $sel\n            }\n        }\n    };\n}\n\n// ====================\n\nmacro_rules! dma_trait {\n    ($signal:ident, $instance:path$(, $mode:path)?) => {\n        #[doc = concat!(stringify!($signal), \" DMA request trait\")]\n        pub trait $signal<T: $instance $(, M: $mode)?>: crate::dma::ChannelInstance {\n            #[doc = concat!(\"Get the DMA request number needed to use this channel as\", stringify!($signal))]\n            /// Note: in some chips, ST calls this the \"channel\", and calls channels \"streams\".\n            /// `embassy-stm32` always uses the \"channel\" and \"request number\" names.\n            fn request(&self) -> crate::dma::Request;\n            #[doc = \"Remap the DMA channel\"]\n            fn remap(&self);\n        }\n    };\n}\n\n#[allow(unused)]\nmacro_rules! dma_trait_impl {\n    (crate::$mod:ident::$trait:ident$(<$mode:ident>)?, $instance:ident, $channel:ident, $request:expr, $remap:expr) => {\n        impl crate::$mod::$trait<crate::peripherals::$instance $(, crate::$mod::$mode)?> for crate::peripherals::$channel {\n            fn request(&self) -> crate::dma::Request {\n                $request\n            }\n\n            fn remap(&self) {\n                critical_section::with(|_| {\n                    #[allow(unused_unsafe)]\n                    unsafe {\n                        $remap;\n                    }\n                });\n            }\n        }\n    };\n}\n\n// ====================\n\n#[allow(unused)]\nmacro_rules! trigger_trait {\n    ($signal:ident, $instance:path$(, $mode:path)?) => {\n        #[doc = concat!(stringify!($signal), \" trigger trait\")]\n        pub trait $signal<T: $instance $(, M: $mode)?>  {\n            #[doc = concat!(\"Get the signal number needed to use this trigger as\", stringify!($signal))]\n            /// Note: in some chips, ST calls this the \"channel\", and calls channels \"streams\".\n            /// `embassy-stm32` always uses the \"channel\" and \"request number\" names.\n            fn signal(&self) -> u8;\n        }\n    };\n}\n\n#[allow(unused)]\nmacro_rules! trigger_trait_impl {\n    (crate::$mod:ident::$trait:ident$(<$mode:ident>)?, $instance:ident, $trigger:ident, $signal:expr) => {\n        impl crate::$mod::$trait<crate::peripherals::$instance $(, crate::$mod::$mode)?> for crate::triggers::$trigger {\n            fn signal(&self) -> u8 {\n                $signal\n            }\n        }\n    };\n}\n\n#[allow(unused)]\nmacro_rules! new_dma_nonopt {\n    ($name:ident, $irqs:expr) => {{\n        let dma = $name;\n        dma.remap();\n        let request = dma.request();\n        crate::dma::ChannelAndRequest::new(dma, $irqs, request)\n    }};\n}\n\nmacro_rules! new_dma {\n    ($name:ident, $irqs:expr) => {{\n        let dma = $name;\n        dma.remap();\n        let request = dma.request();\n        Some(crate::dma::ChannelAndRequest::new(dma, $irqs, request))\n    }};\n}\n\nmacro_rules! new_pin {\n    ($name:ident, $af_type:expr) => {{\n        let pin = $name;\n        #[cfg(afio)]\n        pin.afio_remap();\n        pin.set_as_af(\n            #[cfg(not(afio))]\n            pin.af_num(),\n            $af_type,\n        );\n        Some(crate::gpio::Flex::new(pin))\n    }};\n}\n\n/// Macro to configure a pin for alternate function use.\n/// For AFIO chips (STM32F1), it calls afio_remap().\n/// For non-AFIO chips, it calls set_as_af() with the pin's af_num().\nmacro_rules! set_as_af {\n    ($pin:expr, $af_type:expr) => {\n        #[cfg(afio)]\n        {\n            $pin.set_as_af($af_type);\n            $pin.afio_remap();\n        }\n        #[cfg(not(afio))]\n        {\n            $pin.set_as_af($pin.af_num(), $af_type);\n        }\n    };\n}\n\n#[cfg(afio)]\nmacro_rules! if_afio {\n    ($($t:tt)*) => {\n        $($t)*\n    }\n}\n#[cfg(not(afio))]\nmacro_rules! if_afio {\n    (($a:ty, A)) => {\n        ($a,)\n    };\n    (($a:ty, $b:ty, A)) => {\n        ($a,$b)\n    };\n    (($a:ty, $b:ty, $c:ty, A)) => {\n        ($a,$b, $c)\n    };\n    ($type:ident<$lt:lifetime, $a:ty, $b:ty, A>) => {\n        $type<$lt, $a, $b>\n    };\n    ($type:ident<$lt:lifetime, $a:ty, $b:ty, $c:ty, A>) => {\n        $type<$lt, $a, $b, $c>\n    };\n    ($type:ident<$a:ty, A>) => {\n        $type<$a>\n    };\n    ($type:ident<$a:ty, $b:ty, A>) => {\n        $type<$a, $b>\n    };\n    ($type:ident<$a:ty, $b:ty, $c:ty, A>) => {\n        $type<$a, $b, $c>\n    };\n    (impl $trait:ident<$a:ty, A>) => {\n        impl $trait<$a>\n    };\n    (impl $trait:ident<$a:ty, $b:ty, A>) => {\n        impl $trait<$a, $b>\n    };\n    (impl $trait:ident<$a:ty, $b:ty, $c:ty, A>) => {\n        impl $trait<$a, $b, $c>\n    };\n}\n"
  },
  {
    "path": "embassy-stm32/src/opamp.rs",
    "content": "//! Operational Amplifier (OPAMP)\n#![macro_use]\n\nuse embassy_hal_internal::PeripheralType;\n\nuse crate::Peri;\n#[cfg(opamp_v5)]\nuse crate::block_for_us;\nuse crate::pac::opamp::vals::*;\n#[cfg(not(any(stm32g4, stm32f3)))]\nuse crate::rcc::RccInfo;\n\n/// Gain\n#[allow(missing_docs)]\n#[derive(Clone, Copy)]\npub enum OpAmpGain {\n    Mul2,\n    Mul4,\n    Mul8,\n    Mul16,\n    #[cfg(opamp_v5)]\n    Mul32,\n    #[cfg(opamp_v5)]\n    Mul64,\n}\n\n#[cfg(opamp_v5)]\nenum OpAmpDifferentialPair {\n    P,\n    N,\n}\n\n/// Speed\n#[allow(missing_docs)]\n#[derive(Clone, Copy, PartialEq)]\npub enum OpAmpSpeed {\n    Normal,\n    HighSpeed,\n}\n\n/// OpAmp external outputs, wired to a GPIO pad.\n///\n/// This struct can also be used as an ADC input.\npub struct OpAmpOutput<'d, T: Instance> {\n    _inner: &'d OpAmp<'d, T>,\n}\n\n/// OpAmp internal outputs, wired directly to ADC inputs.\n///\n/// This struct can be used as an ADC input.\n#[cfg(opamp_v5)]\npub struct OpAmpInternalOutput<'d, T: Instance> {\n    _inner: &'d OpAmp<'d, T>,\n}\n\n/// OpAmp driver.\npub struct OpAmp<'d, T: Instance> {\n    _inner: Peri<'d, T>,\n}\n\nimpl<'d, T: Instance> OpAmp<'d, T> {\n    /// Create a new driver instance.\n    ///\n    /// Does not enable the opamp, but does set the speed mode on some families.\n    pub fn new(opamp: Peri<'d, T>, #[cfg(opamp_v5)] speed: OpAmpSpeed) -> Self {\n        #[cfg(not(any(stm32g4, stm32f3)))]\n        T::info().rcc.enable_and_reset();\n        #[cfg(opamp_v5)]\n        T::regs().csr().modify(|w| {\n            w.set_opahsm(speed == OpAmpSpeed::HighSpeed);\n        });\n\n        Self { _inner: opamp }\n    }\n\n    /// Configure the OpAmp as a buffer for the provided input pin,\n    /// outputting to the provided output pin, and enable the opamp.\n    ///\n    /// The input pin is configured for analogue mode but not consumed,\n    /// so it may subsequently be used for ADC or comparator inputs.\n    ///\n    /// The output pin is held within the returned [`OpAmpOutput`] struct,\n    /// preventing it being used elsewhere. The `OpAmpOutput` can then be\n    /// directly used as an ADC input. The opamp will be disabled when the\n    /// [`OpAmpOutput`] is dropped.\n    pub fn buffer_ext(\n        &mut self,\n        in_pin: Peri<'_, impl NonInvertingPin<T> + crate::gpio::Pin>,\n        out_pin: Peri<'_, impl OutputPin<T> + crate::gpio::Pin>,\n    ) -> OpAmpOutput<'_, T> {\n        in_pin.set_as_analog();\n        out_pin.set_as_analog();\n\n        #[cfg(opamp_v5)]\n        let vm_sel = VmSel::OUTPUT;\n        #[cfg(not(opamp_v5))]\n        let vm_sel = VmSel::from_bits(0b11);\n\n        T::regs().csr().modify(|w| {\n            w.set_vp_sel(VpSel::from_bits(in_pin.channel()));\n            w.set_vm_sel(vm_sel);\n            #[cfg(opamp_v5)]\n            w.set_opaintoen(false);\n            w.set_opampen(true);\n        });\n\n        OpAmpOutput { _inner: self }\n    }\n\n    /// Configure the OpAmp as a PGA for the provided input pin,\n    /// outputting to the provided output pin, and enable the opamp.\n    ///\n    /// The input pin is configured for analogue mode but not consumed,\n    /// so it may subsequently be used for ADC or comparator inputs.\n    ///\n    /// The output pin is held within the returned [`OpAmpOutput`] struct,\n    /// preventing it being used elsewhere. The `OpAmpOutput` can then be\n    /// directly used as an ADC input. The opamp will be disabled when the\n    /// [`OpAmpOutput`] is dropped.\n    pub fn pga_ext(\n        &mut self,\n        in_pin: Peri<'_, impl NonInvertingPin<T> + crate::gpio::Pin>,\n        out_pin: Peri<'_, impl OutputPin<T> + crate::gpio::Pin>,\n        gain: OpAmpGain,\n    ) -> OpAmpOutput<'_, T> {\n        in_pin.set_as_analog();\n        out_pin.set_as_analog();\n\n        #[cfg(opamp_v5)]\n        let vm_sel = VmSel::PGA;\n        #[cfg(not(opamp_v5))]\n        let vm_sel = VmSel::from_bits(0b10);\n\n        #[cfg(opamp_v5)]\n        let pga_gain = match gain {\n            OpAmpGain::Mul2 => PgaGain::GAIN2,\n            OpAmpGain::Mul4 => PgaGain::GAIN4,\n            OpAmpGain::Mul8 => PgaGain::GAIN8,\n            OpAmpGain::Mul16 => PgaGain::GAIN16,\n            OpAmpGain::Mul32 => PgaGain::GAIN32,\n            OpAmpGain::Mul64 => PgaGain::GAIN64,\n        };\n        #[cfg(not(opamp_v5))]\n        let pga_gain = PgaGain::from_bits(match gain {\n            OpAmpGain::Mul2 => 0b00,\n            OpAmpGain::Mul4 => 0b01,\n            OpAmpGain::Mul8 => 0b10,\n            OpAmpGain::Mul16 => 0b11,\n        });\n\n        T::regs().csr().modify(|w| {\n            w.set_vp_sel(VpSel::from_bits(in_pin.channel()));\n            w.set_vm_sel(vm_sel);\n            w.set_pga_gain(pga_gain);\n            #[cfg(opamp_v5)]\n            w.set_opaintoen(false);\n            w.set_opampen(true);\n        });\n\n        OpAmpOutput { _inner: self }\n    }\n\n    /// Configure the OpAmp as a PGA for the provided input pin,\n    /// outputting to the provided output pin, and enable the opamp.\n    ///\n    /// The ground node of the gain network is connected to the provided VINM0 bias pin.\n    ///\n    /// The input and bias pins are configured for analogue mode but not consumed,\n    /// so it may subsequently be used for ADC or comparator inputs.\n    ///\n    /// The output pin is held within the returned [`OpAmpOutput`] struct,\n    /// preventing it being used elsewhere. The `OpAmpOutput` can then be\n    /// directly used as an ADC input. The opamp will be disabled when the\n    /// [`OpAmpOutput`] is dropped.\n    #[cfg(opamp_v5)]\n    pub fn pga_biased_ext(\n        &mut self,\n        in_pin: Peri<'_, impl NonInvertingPin<T> + crate::gpio::Pin>,\n        bias_pin: Peri<'_, impl BiasPin<T> + crate::gpio::Pin>,\n        out_pin: Peri<'_, impl OutputPin<T> + crate::gpio::Pin>,\n        gain: OpAmpGain,\n    ) -> OpAmpOutput<'_, T> {\n        in_pin.set_as_analog();\n        bias_pin.set_as_analog();\n        out_pin.set_as_analog();\n\n        let pga_gain = match gain {\n            OpAmpGain::Mul2 => PgaGain::GAIN2_INPUT_VINM0,\n            OpAmpGain::Mul4 => PgaGain::GAIN4_INPUT_VINM0,\n            OpAmpGain::Mul8 => PgaGain::GAIN8_INPUT_VINM0,\n            OpAmpGain::Mul16 => PgaGain::GAIN16_INPUT_VINM0,\n            OpAmpGain::Mul32 => PgaGain::GAIN32_INPUT_VINM0,\n            OpAmpGain::Mul64 => PgaGain::GAIN64_INPUT_VINM0,\n        };\n\n        T::regs().csr().modify(|w| {\n            w.set_vp_sel(VpSel::from_bits(in_pin.channel()));\n            w.set_vm_sel(VmSel::PGA);\n            w.set_pga_gain(pga_gain);\n            w.set_opaintoen(false);\n            w.set_opampen(true);\n        });\n\n        OpAmpOutput { _inner: self }\n    }\n\n    /// Configure the OpAmp as a buffer for the DAC it is connected to,\n    /// outputting to the provided output pin, and enable the opamp.\n    ///\n    /// The output pin is held within the returned [`OpAmpOutput`] struct,\n    /// preventing it being used elsewhere. The `OpAmpOutput` can then be\n    /// directly used as an ADC input. The opamp will be disabled when the\n    /// [`OpAmpOutput`] is dropped.\n    #[cfg(opamp_v5)]\n    pub fn buffer_dac(&mut self, out_pin: Peri<'_, impl OutputPin<T> + crate::gpio::Pin>) -> OpAmpOutput<'_, T> {\n        out_pin.set_as_analog();\n\n        T::regs().csr().modify(|w| {\n            use crate::pac::opamp::vals::*;\n\n            w.set_vm_sel(VmSel::OUTPUT);\n            w.set_vp_sel(VpSel::DAC3_CH1);\n            w.set_opaintoen(false);\n            w.set_opampen(true);\n        });\n\n        OpAmpOutput { _inner: self }\n    }\n\n    /// Configure the OpAmp as a buffer for the provided input pin,\n    /// with the output only used internally, and enable the opamp.\n    ///\n    /// The input pin is configured for analogue mode but not consumed,\n    /// so it may be subsequently used for ADC or comparator inputs.\n    ///\n    /// The returned `OpAmpInternalOutput` struct may be used as an ADC input.\n    /// The opamp output will be disabled when it is dropped.\n    #[cfg(opamp_v5)]\n    pub fn buffer_int(\n        &mut self,\n        pin: Peri<'_, impl NonInvertingPin<T> + crate::gpio::Pin>,\n    ) -> OpAmpInternalOutput<'_, T> {\n        pin.set_as_analog();\n\n        T::regs().csr().modify(|w| {\n            w.set_vp_sel(VpSel::from_bits(pin.channel()));\n            w.set_vm_sel(VmSel::OUTPUT);\n            #[cfg(opamp_v5)]\n            w.set_opaintoen(true);\n            w.set_opampen(true);\n        });\n\n        OpAmpInternalOutput { _inner: self }\n    }\n\n    /// Configure the OpAmp as a PGA for the provided input pin,\n    /// with the output only used internally, and enable the opamp.\n    ///\n    /// The input pin is configured for analogue mode but not consumed,\n    /// so it may be subsequently used for ADC or comparator inputs.\n    ///\n    /// The returned `OpAmpInternalOutput` struct may be used as an ADC input.\n    /// The opamp output will be disabled when it is dropped.\n    #[cfg(opamp_v5)]\n    pub fn pga_int(\n        &mut self,\n        pin: Peri<'_, impl NonInvertingPin<T> + crate::gpio::Pin>,\n        gain: OpAmpGain,\n    ) -> OpAmpInternalOutput<'_, T> {\n        pin.set_as_analog();\n\n        let pga_gain = match gain {\n            OpAmpGain::Mul2 => PgaGain::GAIN2,\n            OpAmpGain::Mul4 => PgaGain::GAIN4,\n            OpAmpGain::Mul8 => PgaGain::GAIN8,\n            OpAmpGain::Mul16 => PgaGain::GAIN16,\n            OpAmpGain::Mul32 => PgaGain::GAIN32,\n            OpAmpGain::Mul64 => PgaGain::GAIN64,\n        };\n\n        T::regs().csr().modify(|w| {\n            w.set_vp_sel(VpSel::from_bits(pin.channel()));\n            w.set_vm_sel(VmSel::PGA);\n            w.set_pga_gain(pga_gain);\n            w.set_opaintoen(true);\n            w.set_opampen(true);\n        });\n\n        OpAmpInternalOutput { _inner: self }\n    }\n\n    /// Configure the OpAmp as a PGA for the provided input pin,\n    /// with the output only used internally, and enable the opamp.\n    ///\n    /// The ground node of the gain network is connected to the provided VINM0 bias pin.\n    ///\n    /// The input pin and bias pins are configured for analogue mode but not consumed,\n    /// so it may be subsequently used for ADC or comparator inputs.\n    ///\n    /// The returned `OpAmpInternalOutput` struct may be used as an ADC input.\n    /// The opamp output will be disabled when it is dropped.\n    #[cfg(opamp_v5)]\n    pub fn pga_biased_int(\n        &mut self,\n        in_pin: Peri<'_, impl NonInvertingPin<T> + crate::gpio::Pin>,\n        bias_pin: Peri<'_, impl BiasPin<T> + crate::gpio::Pin>,\n        gain: OpAmpGain,\n    ) -> OpAmpInternalOutput<'_, T> {\n        in_pin.set_as_analog();\n        bias_pin.set_as_analog();\n\n        let pga_gain = match gain {\n            OpAmpGain::Mul2 => PgaGain::GAIN2_INPUT_VINM0,\n            OpAmpGain::Mul4 => PgaGain::GAIN4_INPUT_VINM0,\n            OpAmpGain::Mul8 => PgaGain::GAIN8_INPUT_VINM0,\n            OpAmpGain::Mul16 => PgaGain::GAIN16_INPUT_VINM0,\n            OpAmpGain::Mul32 => PgaGain::GAIN32_INPUT_VINM0,\n            OpAmpGain::Mul64 => PgaGain::GAIN64_INPUT_VINM0,\n        };\n\n        T::regs().csr().modify(|w| {\n            w.set_vp_sel(VpSel::from_bits(in_pin.channel()));\n            w.set_vm_sel(VmSel::PGA);\n            w.set_pga_gain(pga_gain);\n            w.set_opaintoen(true);\n            w.set_opampen(true);\n        });\n\n        OpAmpInternalOutput { _inner: self }\n    }\n\n    /// Configure the OpAmp as a standalone DAC with the inverting input\n    /// connected to the provided pin, and the output is connected\n    /// internally to an ADC channel.\n    ///\n    /// The input pin is configured for analogue mode but not consumed,\n    /// so it may be subsequently used for ADC or comparator inputs.\n    ///\n    /// The returned `OpAmpInternalOutput` struct may be used as an ADC\n    /// input. The opamp output will be disabled when it is dropped.\n    #[cfg(opamp_v5)]\n    pub fn standalone_dac_int(\n        &mut self,\n        m_pin: Peri<'_, impl InvertingPin<T> + crate::gpio::Pin>,\n    ) -> OpAmpInternalOutput<'_, T> {\n        m_pin.set_as_analog();\n\n        T::regs().csr().modify(|w| {\n            use crate::pac::opamp::vals::*;\n            w.set_vp_sel(VpSel::DAC3_CH1); // Actually DAC3_CHx\n            w.set_vm_sel(VmSel::from_bits(m_pin.channel()));\n            w.set_opaintoen(true);\n            w.set_opampen(true);\n        });\n\n        OpAmpInternalOutput { _inner: self }\n    }\n\n    /// Configure the OpAmp as a standalone DAC with the inverting input\n    /// connected to the provided pin, and the output connected to the\n    /// provided pin.\n    ///\n    /// The input pin is configured for analogue mode but not consumed,\n    /// so it may be subsequently used for ADC or comparator inputs.\n    ///\n    /// The output pin is held within the returned [`OpAmpOutput`] struct,\n    /// preventing it being used elsewhere. The opamp will be disabled when\n    /// the [`OpAmpOutput`] is dropped.\n    #[cfg(opamp_v5)]\n    pub fn standalone_dac_ext(\n        &mut self,\n        m_pin: Peri<'_, impl InvertingPin<T> + crate::gpio::Pin>,\n        out_pin: Peri<'_, impl OutputPin<T> + crate::gpio::Pin>,\n    ) -> OpAmpOutput<'_, T> {\n        m_pin.set_as_analog();\n        out_pin.set_as_analog();\n\n        T::regs().csr().modify(|w| {\n            use crate::pac::opamp::vals::*;\n            w.set_vp_sel(VpSel::DAC3_CH1); // Actually DAC3_CHx\n            w.set_vm_sel(VmSel::from_bits(m_pin.channel()));\n            w.set_opaintoen(false);\n            w.set_opampen(true);\n        });\n\n        OpAmpOutput { _inner: self }\n    }\n\n    /// Configure the OpAmp in standalone mode with the non-inverting input\n    /// connected to the provided `p_pin`, the inverting input connected to\n    /// the `m_pin`, and output to the provided `out_pin`.\n    ///\n    /// The input pins are configured for analogue mode but not consumed,\n    /// allowing their subsequent use for ADC or comparator inputs.\n    ///\n    /// The output pin is held within the returned [`OpAmpOutput`] struct,\n    /// preventing it being used elsewhere. The opamp will be disabled when\n    /// the [`OpAmpOutput`] is dropped.\n    #[cfg(opamp_v5)]\n    pub fn standalone_ext(\n        &mut self,\n        p_pin: Peri<'d, impl NonInvertingPin<T> + crate::gpio::Pin>,\n        m_pin: Peri<'d, impl InvertingPin<T> + crate::gpio::Pin>,\n        out_pin: Peri<'d, impl OutputPin<T> + crate::gpio::Pin>,\n    ) -> OpAmpOutput<'_, T> {\n        p_pin.set_as_analog();\n        m_pin.set_as_analog();\n        out_pin.set_as_analog();\n\n        T::regs().csr().modify(|w| {\n            use crate::pac::opamp::vals::*;\n            w.set_vp_sel(VpSel::from_bits(p_pin.channel()));\n            w.set_vm_sel(VmSel::from_bits(m_pin.channel()));\n            w.set_opaintoen(false);\n            w.set_opampen(true);\n        });\n\n        OpAmpOutput { _inner: self }\n    }\n\n    /// Configure the OpAmp in standalone mode with the non-inverting input\n    /// connected to the provided `p_pin`, the inverting input connected to\n    /// the `m_pin`, and output is connected to the DAC.\n    ///\n    /// The input pins are configured for analogue mode but not consumed,\n    /// allowing their subsequent use for ADC or comparator inputs.\n    ///\n    /// The returned `OpAmpOutput` struct may be used as an ADC\n    /// input. The opamp output will be disabled when it is dropped.\n    #[cfg(opamp_v5)]\n    pub fn standalone_int(\n        &mut self,\n        p_pin: Peri<'d, impl NonInvertingPin<T> + crate::gpio::Pin>,\n        m_pin: Peri<'d, impl InvertingPin<T> + crate::gpio::Pin>,\n    ) -> OpAmpOutput<'_, T> {\n        p_pin.set_as_analog();\n        m_pin.set_as_analog();\n\n        T::regs().csr().modify(|w| {\n            use crate::pac::opamp::vals::*;\n            w.set_vp_sel(VpSel::from_bits(p_pin.channel()));\n            w.set_vm_sel(VmSel::from_bits(m_pin.channel()));\n            w.set_opaintoen(true);\n            w.set_opampen(true);\n        });\n\n        OpAmpOutput { _inner: self }\n    }\n\n    /// Calibrates the operational amplifier.\n    ///\n    /// This function enables the opamp and sets the user trim mode for calibration.\n    /// Depending on the speed mode of the opamp, it calibrates the differential pair inputs.\n    /// For normal speed, both the P and N differential pairs are calibrated,\n    /// while for high-speed mode, only the P differential pair is calibrated.\n    ///\n    /// Calibrating a differential pair requires waiting 12ms in the worst case (binary method).\n    #[cfg(opamp_v5)]\n    pub fn calibrate(&mut self) {\n        T::regs().csr().modify(|w| {\n            w.set_opampen(true);\n            w.set_calon(true);\n            w.set_usertrim(true);\n        });\n\n        if T::regs().csr().read().opahsm() {\n            self.calibrate_differential_pair(OpAmpDifferentialPair::P);\n        } else {\n            self.calibrate_differential_pair(OpAmpDifferentialPair::P);\n            self.calibrate_differential_pair(OpAmpDifferentialPair::N);\n        }\n\n        T::regs().csr().modify(|w| {\n            w.set_calon(false);\n            w.set_opampen(false);\n        });\n    }\n\n    /// Calibrate differential pair.\n    ///\n    /// The calibration is done by trying different offset values and\n    /// measuring the outcal bit.\n    ///\n    /// The calibration range is from 0 to 31.\n    ///\n    /// The result is stored in the OPAMP_CSR register.\n    #[cfg(opamp_v5)]\n    fn calibrate_differential_pair(&mut self, pair: OpAmpDifferentialPair) {\n        let mut low = 0;\n        let mut high = 31;\n\n        let calsel = match pair {\n            OpAmpDifferentialPair::P => Calsel::PERCENT10,\n            OpAmpDifferentialPair::N => Calsel::PERCENT90,\n        };\n\n        T::regs().csr().modify(|w| {\n            w.set_calsel(calsel);\n        });\n\n        while low <= high {\n            let mid = (low + high) / 2;\n\n            T::regs().csr().modify(|w| match pair {\n                OpAmpDifferentialPair::P => {\n                    #[cfg(feature = \"defmt\")]\n                    defmt::debug!(\"opamp p calibration. offset: {}\", mid);\n                    w.set_trimoffsetp(mid);\n                }\n                OpAmpDifferentialPair::N => {\n                    #[cfg(feature = \"defmt\")]\n                    defmt::debug!(\"opamp n calibration. offset: {}\", mid);\n                    w.set_trimoffsetn(mid);\n                }\n            });\n\n            // The closer the trimming value is to the optimum trimming value, the longer it takes to stabilize\n            // (with a maximum stabilization time remaining below 2 ms in any case) -- RM0440 25.3.7\n            block_for_us(2_000);\n\n            if !T::regs().csr().read().calout() {\n                if mid == 0 {\n                    break;\n                }\n                high = mid - 1;\n            } else {\n                if mid == 31 {\n                    break;\n                }\n                low = mid + 1;\n            }\n        }\n    }\n}\n\n#[cfg(not(any(stm32g4, stm32f3)))]\nimpl<'d, T: Instance> Drop for OpAmp<'d, T> {\n    fn drop(&mut self) {\n        T::info().rcc.disable();\n    }\n}\n\nimpl<'d, T: Instance> Drop for OpAmpOutput<'d, T> {\n    fn drop(&mut self) {\n        T::regs().csr().modify(|w| {\n            w.set_opampen(false);\n        });\n    }\n}\n\n#[cfg(opamp_v5)]\nimpl<'d, T: Instance> Drop for OpAmpInternalOutput<'d, T> {\n    fn drop(&mut self) {\n        T::regs().csr().modify(|w| {\n            w.set_opampen(false);\n        });\n    }\n}\n\n#[cfg(not(any(stm32g4, stm32f3)))]\npub(crate) struct Info {\n    rcc: RccInfo,\n}\n\npub(crate) trait SealedInstance {\n    #[cfg(not(any(stm32g4, stm32f3)))]\n    fn info() -> &'static Info;\n    fn regs() -> crate::pac::opamp::Opamp;\n}\n\npub(crate) trait SealedNonInvertingPin<T: Instance> {\n    fn channel(&self) -> u8;\n}\n\npub(crate) trait SealedInvertingPin<T: Instance> {\n    #[allow(unused)]\n    fn channel(&self) -> u8;\n}\n\npub(crate) trait SealedBiasPin<T: Instance> {}\n\npub(crate) trait SealedOutputPin<T: Instance> {}\n\n/// Opamp instance trait.\n#[allow(private_bounds)]\npub trait Instance: SealedInstance + PeripheralType + 'static {}\n/// Non-inverting pin trait.\n#[allow(private_bounds)]\npub trait NonInvertingPin<T: Instance>: SealedNonInvertingPin<T> {}\n/// Inverting pin trait.\n#[allow(private_bounds)]\npub trait InvertingPin<T: Instance>: SealedInvertingPin<T> {}\n/// Bias pin trait.\n#[allow(private_bounds)]\npub trait BiasPin<T: Instance>: SealedBiasPin<T> {}\n/// Output pin trait.\n#[allow(private_bounds)]\npub trait OutputPin<T: Instance>: SealedOutputPin<T> {}\n\nmacro_rules! impl_opamp_external_output {\n    ($inst:ident, $adc:ident, $ch:expr) => {\n        foreach_adc!(\n            ($adc, $common_inst:ident, $adc_clock:ident) => {\n                impl<'d> crate::adc::SealedAdcChannel<crate::peripherals::$adc>\n                    for crate::opamp::OpAmpOutput<'d, crate::peripherals::$inst>\n                {\n                    fn channel(&self) -> u8 {\n                        $ch\n                    }\n                }\n\n                impl<'d> crate::adc::AdcChannel<crate::peripherals::$adc>\n                    for crate::opamp::OpAmpOutput<'d, crate::peripherals::$inst>\n                {\n                }\n            };\n        );\n    };\n}\n\n#[cfg(opamp_v5)]\nmacro_rules! impl_opamp_internal_output {\n    ($inst:ident, $adc:ident, $ch:expr) => {\n        foreach_adc!(\n            ($adc, $common_inst:ident, $adc_clock:ident) => {\n                impl<'d> crate::adc::SealedAdcChannel<crate::peripherals::$adc>\n                    for OpAmpInternalOutput<'d, crate::peripherals::$inst>\n                {\n                    fn channel(&self) -> u8 {\n                        $ch\n                    }\n                }\n\n                impl<'d> crate::adc::AdcChannel<crate::peripherals::$adc>\n                    for OpAmpInternalOutput<'d, crate::peripherals::$inst>\n                {\n                }\n            };\n        );\n    };\n}\n\n#[cfg(opamp_v5)]\nforeach_peripheral!(\n    (opamp, OPAMP1) => {\n        impl_opamp_internal_output!(OPAMP1, ADC1, 13);\n    };\n    (opamp, OPAMP2) => {\n        impl_opamp_internal_output!(OPAMP2, ADC2, 16);\n    };\n    (opamp, OPAMP3) => {\n        impl_opamp_internal_output!(OPAMP3, ADC2, 18);\n        // Only in Cat 3/4 devices\n        impl_opamp_internal_output!(OPAMP3, ADC3, 13);\n    };\n    // OPAMP4 only in Cat 3 devices\n    (opamp, OPAMP4) => {\n        impl_opamp_internal_output!(OPAMP4, ADC5, 5);\n    };\n    // OPAMP5 only in Cat 3 devices\n    (opamp, OPAMP5) => {\n        impl_opamp_internal_output!(OPAMP5, ADC5, 3);\n    };\n    // OPAMP6 only in Cat 3/4 devices\n    (opamp, OPAMP6) => {\n        // Only in Cat 3 devices\n        impl_opamp_internal_output!(OPAMP6, ADC4, 17);\n        // Only in Cat 4 devices\n        impl_opamp_internal_output!(OPAMP6, ADC3, 17);\n    };\n);\n\nforeach_peripheral! {\n    (opamp, $inst:ident) => {\n        impl SealedInstance for crate::peripherals::$inst {\n            // G4 and F3 use SYSCFGEN, which is always enabled\n            #[cfg(not(any(stm32g4, stm32f3)))]\n            fn info() -> &'static Info {\n                use crate::rcc::SealedRccPeripheral;\n                static INFO: Info = Info {\n                    rcc: crate::peripherals::$inst::RCC_INFO,\n                };\n                &INFO\n            }\n            fn regs() -> crate::pac::opamp::Opamp {\n                crate::pac::$inst\n            }\n        }\n\n        impl Instance for crate::peripherals::$inst {\n        }\n    };\n}\n\n#[allow(unused_macros)]\nmacro_rules! impl_opamp_vp_pin {\n    ($inst:ident, $pin:ident, $ch:expr) => {\n        impl crate::opamp::NonInvertingPin<peripherals::$inst> for crate::peripherals::$pin {}\n        impl crate::opamp::SealedNonInvertingPin<peripherals::$inst> for crate::peripherals::$pin {\n            fn channel(&self) -> u8 {\n                $ch\n            }\n        }\n    };\n}\n\n#[allow(unused_macros)]\nmacro_rules! impl_opamp_vn_pin {\n    ($inst:ident, $pin:ident, $ch:expr) => {\n        impl crate::opamp::InvertingPin<peripherals::$inst> for crate::peripherals::$pin {}\n        impl crate::opamp::SealedInvertingPin<peripherals::$inst> for crate::peripherals::$pin {\n            fn channel(&self) -> u8 {\n                $ch\n            }\n        }\n    };\n}\n\n#[allow(unused_macros)]\nmacro_rules! impl_opamp_bias_pin {\n    ($inst:ident, $pin:ident, $ch:expr) => {\n        impl crate::opamp::BiasPin<peripherals::$inst> for crate::peripherals::$pin {}\n        impl crate::opamp::SealedBiasPin<peripherals::$inst> for crate::peripherals::$pin {}\n    };\n}\n#[allow(unused_macros)]\nmacro_rules! impl_opamp_vout_pin {\n    ($inst:ident, $pin:ident) => {\n        impl crate::opamp::OutputPin<peripherals::$inst> for crate::peripherals::$pin {}\n        impl crate::opamp::SealedOutputPin<peripherals::$inst> for crate::peripherals::$pin {}\n    };\n}\n"
  },
  {
    "path": "embassy-stm32/src/ospi/enums.rs",
    "content": "//! Enums used in Ospi configuration.\n\n#[allow(dead_code)]\n#[derive(Copy, Clone)]\npub(crate) enum OspiMode {\n    IndirectWrite,\n    IndirectRead,\n    AutoPolling,\n    MemoryMapped,\n}\n\nimpl Into<u8> for OspiMode {\n    fn into(self) -> u8 {\n        match self {\n            OspiMode::IndirectWrite => 0b00,\n            OspiMode::IndirectRead => 0b01,\n            OspiMode::AutoPolling => 0b10,\n            OspiMode::MemoryMapped => 0b11,\n        }\n    }\n}\n\n/// Ospi lane width\n#[allow(dead_code)]\n#[derive(Copy, Clone)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub enum OspiWidth {\n    /// None\n    NONE,\n    /// Single lane\n    SING,\n    /// Dual lanes\n    DUAL,\n    /// Quad lanes\n    QUAD,\n    /// Eight lanes\n    OCTO,\n}\n\nimpl Into<u8> for OspiWidth {\n    fn into(self) -> u8 {\n        match self {\n            OspiWidth::NONE => 0b00,\n            OspiWidth::SING => 0b01,\n            OspiWidth::DUAL => 0b10,\n            OspiWidth::QUAD => 0b11,\n            OspiWidth::OCTO => 0b100,\n        }\n    }\n}\n\n/// Flash bank selection\n#[allow(dead_code)]\n#[derive(Copy, Clone)]\npub enum FlashSelection {\n    /// Bank 1\n    Flash1,\n    /// Bank 2\n    Flash2,\n}\n\nimpl Into<bool> for FlashSelection {\n    fn into(self) -> bool {\n        match self {\n            FlashSelection::Flash1 => false,\n            FlashSelection::Flash2 => true,\n        }\n    }\n}\n\n/// Wrap Size\n#[allow(dead_code)]\n#[allow(missing_docs)]\n#[derive(Copy, Clone)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub enum WrapSize {\n    None,\n    _16Bytes,\n    _32Bytes,\n    _64Bytes,\n    _128Bytes,\n}\n\nimpl Into<u8> for WrapSize {\n    fn into(self) -> u8 {\n        match self {\n            WrapSize::None => 0x00,\n            WrapSize::_16Bytes => 0x02,\n            WrapSize::_32Bytes => 0x03,\n            WrapSize::_64Bytes => 0x04,\n            WrapSize::_128Bytes => 0x05,\n        }\n    }\n}\n\n/// Memory Type\n#[allow(missing_docs)]\n#[allow(dead_code)]\n#[derive(Copy, Clone)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub enum MemoryType {\n    Micron,\n    Macronix,\n    Standard,\n    MacronixRam,\n    HyperBusMemory,\n    HyperBusRegister,\n}\n\nimpl Into<u8> for MemoryType {\n    fn into(self) -> u8 {\n        match self {\n            MemoryType::Micron => 0x00,\n            MemoryType::Macronix => 0x01,\n            MemoryType::Standard => 0x02,\n            MemoryType::MacronixRam => 0x03,\n            MemoryType::HyperBusMemory => 0x04,\n            MemoryType::HyperBusRegister => 0x04,\n        }\n    }\n}\n\n/// Ospi memory size.\n#[allow(missing_docs)]\n#[derive(Copy, Clone)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub enum MemorySize {\n    _1KiB,\n    _2KiB,\n    _4KiB,\n    _8KiB,\n    _16KiB,\n    _32KiB,\n    _64KiB,\n    _128KiB,\n    _256KiB,\n    _512KiB,\n    _1MiB,\n    _2MiB,\n    _4MiB,\n    _8MiB,\n    _16MiB,\n    _32MiB,\n    _64MiB,\n    _128MiB,\n    _256MiB,\n    _512MiB,\n    _1GiB,\n    _2GiB,\n    _4GiB,\n    Other(u8),\n}\n\nimpl Into<u8> for MemorySize {\n    fn into(self) -> u8 {\n        match self {\n            MemorySize::_1KiB => 9,\n            MemorySize::_2KiB => 10,\n            MemorySize::_4KiB => 11,\n            MemorySize::_8KiB => 12,\n            MemorySize::_16KiB => 13,\n            MemorySize::_32KiB => 14,\n            MemorySize::_64KiB => 15,\n            MemorySize::_128KiB => 16,\n            MemorySize::_256KiB => 17,\n            MemorySize::_512KiB => 18,\n            MemorySize::_1MiB => 19,\n            MemorySize::_2MiB => 20,\n            MemorySize::_4MiB => 21,\n            MemorySize::_8MiB => 22,\n            MemorySize::_16MiB => 23,\n            MemorySize::_32MiB => 24,\n            MemorySize::_64MiB => 25,\n            MemorySize::_128MiB => 26,\n            MemorySize::_256MiB => 27,\n            MemorySize::_512MiB => 28,\n            MemorySize::_1GiB => 29,\n            MemorySize::_2GiB => 30,\n            MemorySize::_4GiB => 31,\n            MemorySize::Other(val) => val,\n        }\n    }\n}\n\n/// Ospi Address size\n#[derive(Copy, Clone)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub enum AddressSize {\n    /// 8-bit address\n    _8Bit,\n    /// 16-bit address\n    _16Bit,\n    /// 24-bit address\n    _24bit,\n    /// 32-bit address\n    _32bit,\n}\n\nimpl Into<u8> for AddressSize {\n    fn into(self) -> u8 {\n        match self {\n            AddressSize::_8Bit => 0b00,\n            AddressSize::_16Bit => 0b01,\n            AddressSize::_24bit => 0b10,\n            AddressSize::_32bit => 0b11,\n        }\n    }\n}\n\n/// Time the Chip Select line stays high.\n#[allow(missing_docs)]\n#[derive(Copy, Clone)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub enum ChipSelectHighTime {\n    _1Cycle,\n    _2Cycle,\n    _3Cycle,\n    _4Cycle,\n    _5Cycle,\n    _6Cycle,\n    _7Cycle,\n    _8Cycle,\n}\n\nimpl Into<u8> for ChipSelectHighTime {\n    fn into(self) -> u8 {\n        match self {\n            ChipSelectHighTime::_1Cycle => 0,\n            ChipSelectHighTime::_2Cycle => 1,\n            ChipSelectHighTime::_3Cycle => 2,\n            ChipSelectHighTime::_4Cycle => 3,\n            ChipSelectHighTime::_5Cycle => 4,\n            ChipSelectHighTime::_6Cycle => 5,\n            ChipSelectHighTime::_7Cycle => 6,\n            ChipSelectHighTime::_8Cycle => 7,\n        }\n    }\n}\n\n/// FIFO threshold.\n#[allow(missing_docs)]\n#[derive(Copy, Clone)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub enum FIFOThresholdLevel {\n    _1Bytes,\n    _2Bytes,\n    _3Bytes,\n    _4Bytes,\n    _5Bytes,\n    _6Bytes,\n    _7Bytes,\n    _8Bytes,\n    _9Bytes,\n    _10Bytes,\n    _11Bytes,\n    _12Bytes,\n    _13Bytes,\n    _14Bytes,\n    _15Bytes,\n    _16Bytes,\n    _17Bytes,\n    _18Bytes,\n    _19Bytes,\n    _20Bytes,\n    _21Bytes,\n    _22Bytes,\n    _23Bytes,\n    _24Bytes,\n    _25Bytes,\n    _26Bytes,\n    _27Bytes,\n    _28Bytes,\n    _29Bytes,\n    _30Bytes,\n    _31Bytes,\n    _32Bytes,\n}\n\nimpl Into<u8> for FIFOThresholdLevel {\n    fn into(self) -> u8 {\n        match self {\n            FIFOThresholdLevel::_1Bytes => 0,\n            FIFOThresholdLevel::_2Bytes => 1,\n            FIFOThresholdLevel::_3Bytes => 2,\n            FIFOThresholdLevel::_4Bytes => 3,\n            FIFOThresholdLevel::_5Bytes => 4,\n            FIFOThresholdLevel::_6Bytes => 5,\n            FIFOThresholdLevel::_7Bytes => 6,\n            FIFOThresholdLevel::_8Bytes => 7,\n            FIFOThresholdLevel::_9Bytes => 8,\n            FIFOThresholdLevel::_10Bytes => 9,\n            FIFOThresholdLevel::_11Bytes => 10,\n            FIFOThresholdLevel::_12Bytes => 11,\n            FIFOThresholdLevel::_13Bytes => 12,\n            FIFOThresholdLevel::_14Bytes => 13,\n            FIFOThresholdLevel::_15Bytes => 14,\n            FIFOThresholdLevel::_16Bytes => 15,\n            FIFOThresholdLevel::_17Bytes => 16,\n            FIFOThresholdLevel::_18Bytes => 17,\n            FIFOThresholdLevel::_19Bytes => 18,\n            FIFOThresholdLevel::_20Bytes => 19,\n            FIFOThresholdLevel::_21Bytes => 20,\n            FIFOThresholdLevel::_22Bytes => 21,\n            FIFOThresholdLevel::_23Bytes => 22,\n            FIFOThresholdLevel::_24Bytes => 23,\n            FIFOThresholdLevel::_25Bytes => 24,\n            FIFOThresholdLevel::_26Bytes => 25,\n            FIFOThresholdLevel::_27Bytes => 26,\n            FIFOThresholdLevel::_28Bytes => 27,\n            FIFOThresholdLevel::_29Bytes => 28,\n            FIFOThresholdLevel::_30Bytes => 29,\n            FIFOThresholdLevel::_31Bytes => 30,\n            FIFOThresholdLevel::_32Bytes => 31,\n        }\n    }\n}\n\n/// Dummy cycle count\n#[allow(missing_docs)]\n#[derive(Copy, Clone)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub enum DummyCycles {\n    _0,\n    _1,\n    _2,\n    _3,\n    _4,\n    _5,\n    _6,\n    _7,\n    _8,\n    _9,\n    _10,\n    _11,\n    _12,\n    _13,\n    _14,\n    _15,\n    _16,\n    _17,\n    _18,\n    _19,\n    _20,\n    _21,\n    _22,\n    _23,\n    _24,\n    _25,\n    _26,\n    _27,\n    _28,\n    _29,\n    _30,\n    _31,\n}\n\nimpl Into<u8> for DummyCycles {\n    fn into(self) -> u8 {\n        match self {\n            DummyCycles::_0 => 0,\n            DummyCycles::_1 => 1,\n            DummyCycles::_2 => 2,\n            DummyCycles::_3 => 3,\n            DummyCycles::_4 => 4,\n            DummyCycles::_5 => 5,\n            DummyCycles::_6 => 6,\n            DummyCycles::_7 => 7,\n            DummyCycles::_8 => 8,\n            DummyCycles::_9 => 9,\n            DummyCycles::_10 => 10,\n            DummyCycles::_11 => 11,\n            DummyCycles::_12 => 12,\n            DummyCycles::_13 => 13,\n            DummyCycles::_14 => 14,\n            DummyCycles::_15 => 15,\n            DummyCycles::_16 => 16,\n            DummyCycles::_17 => 17,\n            DummyCycles::_18 => 18,\n            DummyCycles::_19 => 19,\n            DummyCycles::_20 => 20,\n            DummyCycles::_21 => 21,\n            DummyCycles::_22 => 22,\n            DummyCycles::_23 => 23,\n            DummyCycles::_24 => 24,\n            DummyCycles::_25 => 25,\n            DummyCycles::_26 => 26,\n            DummyCycles::_27 => 27,\n            DummyCycles::_28 => 28,\n            DummyCycles::_29 => 29,\n            DummyCycles::_30 => 30,\n            DummyCycles::_31 => 31,\n        }\n    }\n}\n"
  },
  {
    "path": "embassy-stm32/src/ospi/mod.rs",
    "content": "//! OCTOSPI Serial Peripheral Interface\n//!\n\n#![macro_use]\n\npub mod enums;\n\nuse core::marker::PhantomData;\n\nuse embassy_embedded_hal::{GetConfig, SetConfig};\nuse embassy_hal_internal::PeripheralType;\npub use enums::*;\nuse stm32_metapac::octospi::vals::{PhaseMode, SizeInBits};\n\nuse crate::dma::{ChannelAndRequest, word};\nuse crate::gpio::{AfType, Flex, OutputType, Pull, Speed};\nuse crate::mode::{Async, Blocking, Mode as PeriMode};\nuse crate::pac::octospi::{Octospi as Regs, vals};\n#[cfg(octospim_v1)]\nuse crate::pac::octospim::Octospim;\nuse crate::rcc::{self, RccPeripheral};\nuse crate::{Peri, peripherals};\n\n/// OPSI driver config.\n#[derive(Clone, Copy)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub struct Config {\n    /// Fifo threshold used by the peripheral to generate the interrupt indicating data\n    /// or space is available in the FIFO\n    pub fifo_threshold: FIFOThresholdLevel,\n    /// Indicates the type of external device connected\n    pub memory_type: MemoryType, // Need to add an additional enum to provide this public interface\n    /// Defines the size of the external device connected to the OSPI corresponding\n    /// to the number of address bits required to access the device.\n    /// When using indirect mode, [`TransferConfig::address`] + the length of the data being read\n    /// or written must fit within the configured `device_size`, otherwise an error is returned.\n    pub device_size: MemorySize,\n    /// Sets the minimum number of clock cycles that the chip select signal must be held high\n    /// between commands\n    pub chip_select_high_time: ChipSelectHighTime,\n    /// Enables the free running clock\n    pub free_running_clock: bool,\n    /// Sets the clock level when the device is not selected\n    pub clock_mode: bool,\n    /// Indicates the wrap size corresponding to the external device configuration\n    pub wrap_size: WrapSize,\n    /// Specified the prescaler factor used for generating the external clock based\n    /// on the AHB clock\n    pub clock_prescaler: u8,\n    /// Allows the delay of 1/2 cycle the data sampling to account for external\n    /// signal delays\n    pub sample_shifting: bool,\n    /// Allows hold to 1/4 cycle the data\n    pub delay_hold_quarter_cycle: bool,\n    /// Enables the transaction boundary feature and defines the boundary to release\n    /// the chip select\n    pub chip_select_boundary: u8,\n    /// Enables the delay block bypass so the sampling is not affected by the delay block\n    pub delay_block_bypass: bool,\n    /// Enables communication regulation feature. Chip select is released when the other\n    /// OctoSpi requests access to the bus\n    pub max_transfer: u8,\n    /// Enables the refresh feature, chip select is released every refresh + 1 clock cycles\n    pub refresh: u32,\n}\n\nimpl Default for Config {\n    fn default() -> Self {\n        Self {\n            fifo_threshold: FIFOThresholdLevel::_16Bytes, // 32 bytes FIFO, half capacity\n            memory_type: MemoryType::Micron,\n            device_size: MemorySize::Other(0),\n            chip_select_high_time: ChipSelectHighTime::_5Cycle,\n            free_running_clock: false,\n            clock_mode: false,\n            wrap_size: WrapSize::None,\n            clock_prescaler: 0,\n            sample_shifting: false,\n            delay_hold_quarter_cycle: false,\n            chip_select_boundary: 0, // Acceptable range 0 to 31\n            delay_block_bypass: true,\n            max_transfer: 0,\n            refresh: 0,\n        }\n    }\n}\n\n/// OSPI transfer configuration.\n#[derive(Clone, Copy)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub struct TransferConfig {\n    /// Instruction width (IMODE)\n    pub iwidth: OspiWidth,\n    /// Instruction Id\n    pub instruction: Option<u32>,\n    /// Number of Instruction Bytes\n    pub isize: AddressSize,\n    /// Instruction Double Transfer rate enable\n    pub idtr: bool,\n    /// Address width (ADMODE)\n    pub adwidth: OspiWidth,\n    /// Device memory address.\n    /// In indirect mode, this value + the length of the data being read or written must be within\n    /// configured [`Config::device_size`], otherwise the transfer returns an error.\n    pub address: Option<u32>,\n    /// Number of Address Bytes\n    pub adsize: AddressSize,\n    /// Address Double Transfer rate enable\n    pub addtr: bool,\n\n    /// Alternate bytes width (ABMODE)\n    pub abwidth: OspiWidth,\n    /// Alternate Bytes\n    pub alternate_bytes: Option<u32>,\n    /// Number of Alternate Bytes\n    pub absize: AddressSize,\n    /// Alternate Bytes Double Transfer rate enable\n    pub abdtr: bool,\n\n    /// Data width (DMODE)\n    pub dwidth: OspiWidth,\n    /// Data Double Transfer rate enable\n    pub ddtr: bool,\n\n    /// Number of dummy cycles (DCYC)\n    pub dummy: DummyCycles,\n\n    /// Data strobe (DQS) management enable\n    pub dqse: bool,\n    /// Send instruction only once (SIOO) mode enable\n    pub sioo: bool,\n}\n\nimpl Default for TransferConfig {\n    fn default() -> Self {\n        Self {\n            iwidth: OspiWidth::NONE,\n            instruction: None,\n            isize: AddressSize::_8Bit,\n            idtr: false,\n\n            adwidth: OspiWidth::NONE,\n            address: None,\n            adsize: AddressSize::_8Bit,\n            addtr: false,\n\n            abwidth: OspiWidth::NONE,\n            alternate_bytes: None,\n            absize: AddressSize::_8Bit,\n            abdtr: false,\n\n            dwidth: OspiWidth::NONE,\n            ddtr: false,\n\n            dummy: DummyCycles::_0,\n\n            dqse: false,\n            sioo: true,\n        }\n    }\n}\n\n/// Error used for Octospi implementation\n#[derive(Debug)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub enum OspiError {\n    /// Peripheral configuration is invalid\n    InvalidConfiguration,\n    /// Operation configuration is invalid\n    InvalidCommand,\n    /// Size zero buffer passed to instruction\n    EmptyBuffer,\n}\n\n/// OSPI driver.\npub struct Ospi<'d, T: Instance, M: PeriMode> {\n    _peri: Peri<'d, T>,\n    _sck: Option<Flex<'d>>,\n    _d0: Option<Flex<'d>>,\n    _d1: Option<Flex<'d>>,\n    _d2: Option<Flex<'d>>,\n    _d3: Option<Flex<'d>>,\n    _d4: Option<Flex<'d>>,\n    _d5: Option<Flex<'d>>,\n    _d6: Option<Flex<'d>>,\n    _d7: Option<Flex<'d>>,\n    _nss: Option<Flex<'d>>,\n    _dqs: Option<Flex<'d>>,\n    dma: Option<ChannelAndRequest<'d>>,\n    _phantom: PhantomData<M>,\n    config: Config,\n    width: OspiWidth,\n}\n\nimpl<'d, T: Instance, M: PeriMode> Ospi<'d, T, M> {\n    /// Enter memory mode.\n    /// The Input `read_config` is used to configure the read operation in memory mode\n    pub fn enable_memory_mapped_mode(\n        &mut self,\n        read_config: TransferConfig,\n        write_config: TransferConfig,\n    ) -> Result<(), OspiError> {\n        // Use configure command to set read config\n        self.configure_command(&read_config, None)?;\n\n        let reg = T::REGS;\n        while reg.sr().read().busy() {}\n\n        if let Some(instruction) = write_config.instruction {\n            reg.wir().write(|r| {\n                r.set_instruction(instruction);\n            });\n        }\n\n        // Set writing configurations, there are separate registers for write configurations in memory mapped mode\n        reg.wccr().modify(|w| {\n            w.set_imode(PhaseMode::from_bits(write_config.iwidth.into()));\n            w.set_idtr(write_config.idtr);\n            w.set_isize(SizeInBits::from_bits(write_config.isize.into()));\n\n            w.set_admode(PhaseMode::from_bits(write_config.adwidth.into()));\n            w.set_addtr(write_config.addtr);\n            w.set_adsize(SizeInBits::from_bits(write_config.adsize.into()));\n\n            w.set_dmode(PhaseMode::from_bits(write_config.dwidth.into()));\n            w.set_ddtr(write_config.ddtr);\n\n            w.set_abmode(PhaseMode::from_bits(write_config.abwidth.into()));\n            w.set_dqse(write_config.dqse);\n        });\n\n        reg.wtcr().modify(|w| w.set_dcyc(write_config.dummy.into()));\n\n        // Enable memory mapped mode\n        reg.cr().modify(|r| {\n            r.set_fmode(crate::ospi::vals::FunctionalMode::MEMORY_MAPPED);\n            r.set_tcen(false);\n        });\n        Ok(())\n    }\n\n    /// Quit from memory mapped mode\n    pub fn disable_memory_mapped_mode(&mut self) {\n        let reg = T::REGS;\n\n        reg.cr().modify(|r| {\n            r.set_fmode(crate::ospi::vals::FunctionalMode::INDIRECT_WRITE);\n            r.set_abort(true);\n            r.set_dmaen(false);\n            r.set_en(false);\n        });\n\n        // Clear transfer complete flag\n        reg.fcr().write(|w| w.set_ctcf(true));\n\n        // Re-enable ospi\n        reg.cr().modify(|r| {\n            r.set_en(true);\n        });\n    }\n\n    fn new_inner(\n        peri: Peri<'d, T>,\n        d0: Option<Flex<'d>>,\n        d1: Option<Flex<'d>>,\n        d2: Option<Flex<'d>>,\n        d3: Option<Flex<'d>>,\n        d4: Option<Flex<'d>>,\n        d5: Option<Flex<'d>>,\n        d6: Option<Flex<'d>>,\n        d7: Option<Flex<'d>>,\n        sck: Option<Flex<'d>>,\n        nss: Option<Flex<'d>>,\n        dqs: Option<Flex<'d>>,\n        dma: Option<ChannelAndRequest<'d>>,\n        config: Config,\n        width: OspiWidth,\n        dual_quad: bool,\n    ) -> Self {\n        #[cfg(octospim_v1)]\n        {\n            // RCC for octospim should be enabled before writing register\n            #[cfg(stm32l4)]\n            crate::pac::RCC.ahb2smenr().modify(|w| w.set_octospimsmen(true));\n            #[cfg(stm32u5)]\n            crate::pac::RCC.ahb2enr1().modify(|w| w.set_octospimen(true));\n            #[cfg(not(any(stm32l4, stm32u5)))]\n            crate::pac::RCC.ahb3enr().modify(|w| w.set_iomngren(true));\n\n            // Disable OctoSPI peripheral first\n            T::REGS.cr().modify(|w| {\n                w.set_en(false);\n            });\n\n            // OctoSPI IO Manager has been enabled before\n            T::OCTOSPIM_REGS.cr().modify(|w| {\n                w.set_muxen(false);\n                w.set_req2ack_time(0xff);\n            });\n\n            // Clear config\n            T::OCTOSPIM_REGS.p1cr().modify(|w| {\n                w.set_clksrc(false);\n                w.set_dqssrc(false);\n                w.set_ncssrc(false);\n                w.set_clken(false);\n                w.set_dqsen(false);\n                w.set_ncsen(false);\n                w.set_iolsrc(0);\n                w.set_iohsrc(0);\n            });\n\n            T::OCTOSPIM_REGS.p1cr().modify(|w| {\n                let octospi_src = if T::OCTOSPI_IDX == 1 { false } else { true };\n                w.set_ncsen(true);\n                w.set_ncssrc(octospi_src);\n                w.set_clken(true);\n                w.set_clksrc(octospi_src);\n                if dqs.is_some() {\n                    w.set_dqsen(true);\n                    w.set_dqssrc(octospi_src);\n                }\n\n                // Set OCTOSPIM IOL and IOH according to the index of OCTOSPI instance\n                if T::OCTOSPI_IDX == 1 {\n                    w.set_iolen(true);\n                    w.set_iolsrc(0);\n                    // Enable IOH in octo and dual quad mode\n                    if let OspiWidth::OCTO = width {\n                        w.set_iohen(true);\n                        w.set_iohsrc(0b01);\n                    } else if dual_quad {\n                        w.set_iohen(true);\n                        w.set_iohsrc(0b00);\n                    } else {\n                        w.set_iohen(false);\n                        w.set_iohsrc(0b00);\n                    }\n                } else {\n                    w.set_iolen(true);\n                    w.set_iolsrc(0b10);\n                    // Enable IOH in octo and dual quad mode\n                    if let OspiWidth::OCTO = width {\n                        w.set_iohen(true);\n                        w.set_iohsrc(0b11);\n                    } else if dual_quad {\n                        w.set_iohen(true);\n                        w.set_iohsrc(0b10);\n                    } else {\n                        w.set_iohen(false);\n                        w.set_iohsrc(0b00);\n                    }\n                }\n            });\n        }\n\n        // System configuration\n        rcc::enable_and_reset::<T>();\n        while T::REGS.sr().read().busy() {}\n\n        // Device configuration\n        T::REGS.dcr1().modify(|w| {\n            w.set_devsize(config.device_size.into());\n            w.set_mtyp(vals::MemType::from_bits(config.memory_type.into()));\n            w.set_csht(config.chip_select_high_time.into());\n            w.set_dlybyp(config.delay_block_bypass);\n            w.set_frck(false);\n            w.set_ckmode(config.clock_mode);\n        });\n\n        T::REGS.dcr2().modify(|w| {\n            w.set_wrapsize(config.wrap_size.into());\n        });\n\n        T::REGS.dcr3().modify(|w| {\n            w.set_csbound(config.chip_select_boundary);\n            #[cfg(octospi_v1)]\n            {\n                w.set_maxtran(config.max_transfer);\n            }\n        });\n\n        T::REGS.dcr4().modify(|w| {\n            w.set_refresh(config.refresh);\n        });\n\n        T::REGS.cr().modify(|w| {\n            w.set_fthres(vals::Threshold::from_bits(config.fifo_threshold.into()));\n        });\n\n        // Wait for busy flag to clear\n        while T::REGS.sr().read().busy() {}\n\n        T::REGS.dcr2().modify(|w| {\n            w.set_prescaler(config.clock_prescaler);\n        });\n\n        T::REGS.cr().modify(|w| {\n            w.set_dmm(dual_quad);\n        });\n\n        T::REGS.tcr().modify(|w| {\n            w.set_sshift(match config.sample_shifting {\n                true => vals::SampleShift::HALF_CYCLE,\n                false => vals::SampleShift::NONE,\n            });\n            w.set_dhqc(config.delay_hold_quarter_cycle);\n        });\n\n        // Enable peripheral\n        T::REGS.cr().modify(|w| {\n            w.set_en(true);\n        });\n\n        // Free running clock needs to be set after peripheral enable\n        if config.free_running_clock {\n            T::REGS.dcr1().modify(|w| {\n                w.set_frck(config.free_running_clock);\n            });\n        }\n\n        Self {\n            _peri: peri,\n            _sck: sck,\n            _d0: d0,\n            _d1: d1,\n            _d2: d2,\n            _d3: d3,\n            _d4: d4,\n            _d5: d5,\n            _d6: d6,\n            _d7: d7,\n            _nss: nss,\n            _dqs: dqs,\n            dma,\n            _phantom: PhantomData,\n            config,\n            width,\n        }\n    }\n\n    // Function to configure the peripheral for the requested command\n    fn configure_command(&mut self, command: &TransferConfig, data_len: Option<usize>) -> Result<(), OspiError> {\n        // Check that transaction doesn't use more than hardware initialized pins\n        if <enums::OspiWidth as Into<u8>>::into(command.iwidth) > <enums::OspiWidth as Into<u8>>::into(self.width)\n            || <enums::OspiWidth as Into<u8>>::into(command.adwidth) > <enums::OspiWidth as Into<u8>>::into(self.width)\n            || <enums::OspiWidth as Into<u8>>::into(command.abwidth) > <enums::OspiWidth as Into<u8>>::into(self.width)\n            || <enums::OspiWidth as Into<u8>>::into(command.dwidth) > <enums::OspiWidth as Into<u8>>::into(self.width)\n        {\n            return Err(OspiError::InvalidCommand);\n        }\n\n        T::REGS.cr().modify(|w| {\n            w.set_fmode(vals::FunctionalMode::INDIRECT_WRITE);\n        });\n\n        // Configure alternate bytes\n        if let Some(ab) = command.alternate_bytes {\n            T::REGS.abr().write(|v| v.set_alternate(ab));\n        }\n\n        // Configure dummy cycles\n        T::REGS.tcr().modify(|w| {\n            w.set_dcyc(command.dummy.into());\n        });\n\n        // Configure data\n        if let Some(data_length) = data_len {\n            T::REGS.dlr().write(|v| {\n                v.set_dl((data_length - 1) as u32);\n            });\n        } else {\n            T::REGS.dlr().write(|v| {\n                v.set_dl((0) as u32);\n            });\n        }\n\n        // Configure instruction/address/alternate bytes/data/communication modes\n        T::REGS.ccr().modify(|w| {\n            w.set_imode(PhaseMode::from_bits(command.iwidth.into()));\n            w.set_idtr(command.idtr);\n            w.set_isize(SizeInBits::from_bits(command.isize.into()));\n\n            w.set_admode(PhaseMode::from_bits(command.adwidth.into()));\n            w.set_addtr(command.addtr);\n            w.set_adsize(SizeInBits::from_bits(command.adsize.into()));\n\n            w.set_abmode(PhaseMode::from_bits(command.abwidth.into()));\n            w.set_abdtr(command.abdtr);\n            w.set_absize(SizeInBits::from_bits(command.absize.into()));\n\n            w.set_dmode(PhaseMode::from_bits(command.dwidth.into()));\n            w.set_ddtr(command.ddtr);\n\n            w.set_dqse(command.dqse);\n            w.set_sioo(command.sioo);\n        });\n\n        // Set information required to initiate transaction\n        if let Some(instruction) = command.instruction {\n            if let Some(address) = command.address {\n                T::REGS.ir().write(|v| {\n                    v.set_instruction(instruction);\n                });\n\n                T::REGS.ar().write(|v| {\n                    v.set_address(address);\n                });\n            } else {\n                // Double check requirements for delay hold and sample shifting\n                // if let None = command.data_len {\n                //     if self.config.delay_hold_quarter_cycle && command.idtr {\n                //         T::REGS.ccr().modify(|w| {\n                //             w.set_ddtr(true);\n                //         });\n                //     }\n                // }\n\n                T::REGS.ir().write(|v| {\n                    v.set_instruction(instruction);\n                });\n            }\n        } else {\n            if let Some(address) = command.address {\n                T::REGS.ar().write(|v| {\n                    v.set_address(address);\n                });\n            } else {\n                // The only single phase transaction supported is instruction only\n                return Err(OspiError::InvalidCommand);\n            }\n        }\n\n        // The following errors set the TEF flag in OCTOSPI_SR register:\n        // - in indirect or automatic status-polling mode, when a wrong address has been programmed\n        //   in OCTOSPI_AR (according to the device size defined by DEVSIZE[4:0])\n        // - in indirect mode, if the address plus the data length exceed the device size: TEF is\n        // set as soon as the access is triggered.\n        if T::REGS.sr().read().tef() {\n            // Clear the TEF register to make it ready for the next transfer.\n            T::REGS.fcr().write(|w| w.set_ctef(true));\n\n            return Err(OspiError::InvalidCommand);\n        }\n\n        Ok(())\n    }\n\n    /// Function used to control or configure the target device without data transfer\n    pub fn blocking_command(&mut self, command: &TransferConfig) -> Result<(), OspiError> {\n        // Wait for peripheral to be free\n        while T::REGS.sr().read().busy() {}\n\n        // Need additional validation that command configuration doesn't have data set\n        self.configure_command(command, None)?;\n\n        // Transaction initiated by setting final configuration, i.e the instruction register\n        while !T::REGS.sr().read().tcf() {}\n        T::REGS.fcr().write(|w| {\n            w.set_ctcf(true);\n        });\n\n        Ok(())\n    }\n\n    /// Blocking read with byte by byte data transfer\n    pub fn blocking_read<W: Word>(&mut self, buf: &mut [W], transaction: TransferConfig) -> Result<(), OspiError> {\n        if buf.is_empty() {\n            return Err(OspiError::EmptyBuffer);\n        }\n\n        // Wait for peripheral to be free\n        while T::REGS.sr().read().busy() {}\n\n        // Ensure DMA is not enabled for this transaction\n        T::REGS.cr().modify(|w| {\n            w.set_dmaen(false);\n        });\n\n        let transfer_size_bytes = buf.len() * W::size().bytes();\n        self.configure_command(&transaction, Some(transfer_size_bytes))?;\n\n        let current_address = T::REGS.ar().read().address();\n        let current_instruction = T::REGS.ir().read().instruction();\n\n        // For a indirect read transaction, the transaction begins when the instruction/address is set\n        T::REGS\n            .cr()\n            .modify(|v| v.set_fmode(vals::FunctionalMode::INDIRECT_READ));\n        if T::REGS.ccr().read().admode() == vals::PhaseMode::NONE {\n            T::REGS.ir().write(|v| v.set_instruction(current_instruction));\n        } else {\n            T::REGS.ar().write(|v| v.set_address(current_address));\n        }\n\n        for idx in 0..buf.len() {\n            while !T::REGS.sr().read().tcf() && !T::REGS.sr().read().ftf() {}\n            buf[idx] = unsafe { (T::REGS.dr().as_ptr() as *mut W).read_volatile() };\n        }\n\n        while !T::REGS.sr().read().tcf() {}\n        T::REGS.fcr().write(|v| v.set_ctcf(true));\n\n        Ok(())\n    }\n\n    /// Blocking write with byte by byte data transfer\n    pub fn blocking_write<W: Word>(&mut self, buf: &[W], transaction: TransferConfig) -> Result<(), OspiError> {\n        if buf.is_empty() {\n            return Err(OspiError::EmptyBuffer);\n        }\n\n        // Wait for peripheral to be free\n        while T::REGS.sr().read().busy() {}\n\n        T::REGS.cr().modify(|w| {\n            w.set_dmaen(false);\n        });\n\n        let transfer_size_bytes = buf.len() * W::size().bytes();\n        self.configure_command(&transaction, Some(transfer_size_bytes))?;\n\n        T::REGS\n            .cr()\n            .modify(|v| v.set_fmode(vals::FunctionalMode::INDIRECT_WRITE));\n\n        for idx in 0..buf.len() {\n            while !T::REGS.sr().read().ftf() {}\n            unsafe { (T::REGS.dr().as_ptr() as *mut W).write_volatile(buf[idx]) };\n        }\n\n        while !T::REGS.sr().read().tcf() {}\n        T::REGS.fcr().write(|v| v.set_ctcf(true));\n\n        Ok(())\n    }\n\n    /// Set new bus configuration\n    pub fn set_config(&mut self, config: &Config) {\n        // Wait for busy flag to clear\n        while T::REGS.sr().read().busy() {}\n\n        // Disable DMA channel while configuring the peripheral\n        T::REGS.cr().modify(|w| {\n            w.set_dmaen(false);\n        });\n\n        // Device configuration\n        T::REGS.dcr1().modify(|w| {\n            w.set_devsize(config.device_size.into());\n            w.set_mtyp(vals::MemType::from_bits(config.memory_type.into()));\n            w.set_csht(config.chip_select_high_time.into());\n            w.set_dlybyp(config.delay_block_bypass);\n            w.set_frck(false);\n            w.set_ckmode(config.clock_mode);\n        });\n\n        T::REGS.dcr2().modify(|w| {\n            w.set_wrapsize(config.wrap_size.into());\n        });\n\n        T::REGS.dcr3().modify(|w| {\n            w.set_csbound(config.chip_select_boundary);\n            #[cfg(octospi_v1)]\n            {\n                w.set_maxtran(config.max_transfer);\n            }\n        });\n\n        T::REGS.dcr4().modify(|w| {\n            w.set_refresh(config.refresh);\n        });\n\n        T::REGS.cr().modify(|w| {\n            w.set_fthres(vals::Threshold::from_bits(config.fifo_threshold.into()));\n        });\n\n        // Wait for busy flag to clear\n        while T::REGS.sr().read().busy() {}\n\n        T::REGS.dcr2().modify(|w| {\n            w.set_prescaler(config.clock_prescaler);\n        });\n\n        T::REGS.tcr().modify(|w| {\n            w.set_sshift(match config.sample_shifting {\n                true => vals::SampleShift::HALF_CYCLE,\n                false => vals::SampleShift::NONE,\n            });\n            w.set_dhqc(config.delay_hold_quarter_cycle);\n        });\n\n        // Enable peripheral\n        T::REGS.cr().modify(|w| {\n            w.set_en(true);\n        });\n\n        // Free running clock needs to be set after peripheral enable\n        if config.free_running_clock {\n            T::REGS.dcr1().modify(|w| {\n                w.set_frck(config.free_running_clock);\n            });\n        }\n\n        self.config = *config;\n    }\n\n    /// Get current configuration\n    pub fn get_config(&self) -> Config {\n        self.config\n    }\n}\n\nimpl<'d, T: Instance> Ospi<'d, T, Blocking> {\n    /// Create new blocking OSPI driver for a single spi external chip\n    pub fn new_blocking_singlespi(\n        peri: Peri<'d, T>,\n        sck: Peri<'d, impl SckPin<T>>,\n        d0: Peri<'d, impl D0Pin<T>>,\n        d1: Peri<'d, impl D1Pin<T>>,\n        nss: Peri<'d, impl NSSPin<T>>,\n        config: Config,\n    ) -> Self {\n        Self::new_inner(\n            peri,\n            new_pin!(d0, AfType::output(OutputType::PushPull, Speed::VeryHigh)),\n            new_pin!(d1, AfType::input(Pull::None)),\n            None,\n            None,\n            None,\n            None,\n            None,\n            None,\n            new_pin!(sck, AfType::output(OutputType::PushPull, Speed::VeryHigh)),\n            new_pin!(\n                nss,\n                AfType::output_pull(OutputType::PushPull, Speed::VeryHigh, Pull::Up)\n            ),\n            None,\n            None,\n            config,\n            OspiWidth::SING,\n            false,\n        )\n    }\n\n    /// Create new blocking OSPI driver for a dualspi external chip\n    pub fn new_blocking_dualspi(\n        peri: Peri<'d, T>,\n        sck: Peri<'d, impl SckPin<T>>,\n        d0: Peri<'d, impl D0Pin<T>>,\n        d1: Peri<'d, impl D1Pin<T>>,\n        nss: Peri<'d, impl NSSPin<T>>,\n        config: Config,\n    ) -> Self {\n        Self::new_inner(\n            peri,\n            new_pin!(d0, AfType::output(OutputType::PushPull, Speed::VeryHigh)),\n            new_pin!(d1, AfType::output(OutputType::PushPull, Speed::VeryHigh)),\n            None,\n            None,\n            None,\n            None,\n            None,\n            None,\n            new_pin!(sck, AfType::output(OutputType::PushPull, Speed::VeryHigh)),\n            new_pin!(\n                nss,\n                AfType::output_pull(OutputType::PushPull, Speed::VeryHigh, Pull::Up)\n            ),\n            None,\n            None,\n            config,\n            OspiWidth::DUAL,\n            false,\n        )\n    }\n\n    /// Create new blocking OSPI driver for a quadspi external chip\n    pub fn new_blocking_quadspi(\n        peri: Peri<'d, T>,\n        sck: Peri<'d, impl SckPin<T>>,\n        d0: Peri<'d, impl D0Pin<T>>,\n        d1: Peri<'d, impl D1Pin<T>>,\n        d2: Peri<'d, impl D2Pin<T>>,\n        d3: Peri<'d, impl D3Pin<T>>,\n        nss: Peri<'d, impl NSSPin<T>>,\n        config: Config,\n    ) -> Self {\n        Self::new_inner(\n            peri,\n            new_pin!(d0, AfType::output(OutputType::PushPull, Speed::VeryHigh)),\n            new_pin!(d1, AfType::output(OutputType::PushPull, Speed::VeryHigh)),\n            new_pin!(d2, AfType::output(OutputType::PushPull, Speed::VeryHigh)),\n            new_pin!(d3, AfType::output(OutputType::PushPull, Speed::VeryHigh)),\n            None,\n            None,\n            None,\n            None,\n            new_pin!(sck, AfType::output(OutputType::PushPull, Speed::VeryHigh)),\n            new_pin!(\n                nss,\n                AfType::output_pull(OutputType::PushPull, Speed::VeryHigh, Pull::Up)\n            ),\n            None,\n            None,\n            config,\n            OspiWidth::QUAD,\n            false,\n        )\n    }\n\n    /// Create new blocking OSPI driver for two quadspi external chips\n    pub fn new_blocking_dualquadspi(\n        peri: Peri<'d, T>,\n        sck: Peri<'d, impl SckPin<T>>,\n        d0: Peri<'d, impl D0Pin<T>>,\n        d1: Peri<'d, impl D1Pin<T>>,\n        d2: Peri<'d, impl D2Pin<T>>,\n        d3: Peri<'d, impl D3Pin<T>>,\n        d4: Peri<'d, impl D4Pin<T>>,\n        d5: Peri<'d, impl D5Pin<T>>,\n        d6: Peri<'d, impl D6Pin<T>>,\n        d7: Peri<'d, impl D7Pin<T>>,\n        nss: Peri<'d, impl NSSPin<T>>,\n        config: Config,\n    ) -> Self {\n        Self::new_inner(\n            peri,\n            new_pin!(d0, AfType::output(OutputType::PushPull, Speed::VeryHigh)),\n            new_pin!(d1, AfType::output(OutputType::PushPull, Speed::VeryHigh)),\n            new_pin!(d2, AfType::output(OutputType::PushPull, Speed::VeryHigh)),\n            new_pin!(d3, AfType::output(OutputType::PushPull, Speed::VeryHigh)),\n            new_pin!(d4, AfType::output(OutputType::PushPull, Speed::VeryHigh)),\n            new_pin!(d5, AfType::output(OutputType::PushPull, Speed::VeryHigh)),\n            new_pin!(d6, AfType::output(OutputType::PushPull, Speed::VeryHigh)),\n            new_pin!(d7, AfType::output(OutputType::PushPull, Speed::VeryHigh)),\n            new_pin!(sck, AfType::output(OutputType::PushPull, Speed::VeryHigh)),\n            new_pin!(\n                nss,\n                AfType::output_pull(OutputType::PushPull, Speed::VeryHigh, Pull::Up)\n            ),\n            None,\n            None,\n            config,\n            OspiWidth::QUAD,\n            true,\n        )\n    }\n\n    /// Create new blocking OSPI driver for octospi external chips\n    pub fn new_blocking_octospi(\n        peri: Peri<'d, T>,\n        sck: Peri<'d, impl SckPin<T>>,\n        d0: Peri<'d, impl D0Pin<T>>,\n        d1: Peri<'d, impl D1Pin<T>>,\n        d2: Peri<'d, impl D2Pin<T>>,\n        d3: Peri<'d, impl D3Pin<T>>,\n        d4: Peri<'d, impl D4Pin<T>>,\n        d5: Peri<'d, impl D5Pin<T>>,\n        d6: Peri<'d, impl D6Pin<T>>,\n        d7: Peri<'d, impl D7Pin<T>>,\n        nss: Peri<'d, impl NSSPin<T>>,\n        config: Config,\n    ) -> Self {\n        Self::new_inner(\n            peri,\n            new_pin!(d0, AfType::output(OutputType::PushPull, Speed::VeryHigh)),\n            new_pin!(d1, AfType::output(OutputType::PushPull, Speed::VeryHigh)),\n            new_pin!(d2, AfType::output(OutputType::PushPull, Speed::VeryHigh)),\n            new_pin!(d3, AfType::output(OutputType::PushPull, Speed::VeryHigh)),\n            new_pin!(d4, AfType::output(OutputType::PushPull, Speed::VeryHigh)),\n            new_pin!(d5, AfType::output(OutputType::PushPull, Speed::VeryHigh)),\n            new_pin!(d6, AfType::output(OutputType::PushPull, Speed::VeryHigh)),\n            new_pin!(d7, AfType::output(OutputType::PushPull, Speed::VeryHigh)),\n            new_pin!(sck, AfType::output(OutputType::PushPull, Speed::VeryHigh)),\n            new_pin!(\n                nss,\n                AfType::output_pull(OutputType::PushPull, Speed::VeryHigh, Pull::Up)\n            ),\n            None,\n            None,\n            config,\n            OspiWidth::OCTO,\n            false,\n        )\n    }\n\n    /// Create new blocking OSPI driver for octospi external chips with DQS support\n    pub fn new_blocking_octospi_with_dqs(\n        peri: Peri<'d, T>,\n        sck: Peri<'d, impl SckPin<T>>,\n        d0: Peri<'d, impl D0Pin<T>>,\n        d1: Peri<'d, impl D1Pin<T>>,\n        d2: Peri<'d, impl D2Pin<T>>,\n        d3: Peri<'d, impl D3Pin<T>>,\n        d4: Peri<'d, impl D4Pin<T>>,\n        d5: Peri<'d, impl D5Pin<T>>,\n        d6: Peri<'d, impl D6Pin<T>>,\n        d7: Peri<'d, impl D7Pin<T>>,\n        nss: Peri<'d, impl NSSPin<T>>,\n        dqs: Peri<'d, impl DQSPin<T>>,\n        config: Config,\n    ) -> Self {\n        Self::new_inner(\n            peri,\n            new_pin!(d0, AfType::output(OutputType::PushPull, Speed::VeryHigh)),\n            new_pin!(d1, AfType::output(OutputType::PushPull, Speed::VeryHigh)),\n            new_pin!(d2, AfType::output(OutputType::PushPull, Speed::VeryHigh)),\n            new_pin!(d3, AfType::output(OutputType::PushPull, Speed::VeryHigh)),\n            new_pin!(d4, AfType::output(OutputType::PushPull, Speed::VeryHigh)),\n            new_pin!(d5, AfType::output(OutputType::PushPull, Speed::VeryHigh)),\n            new_pin!(d6, AfType::output(OutputType::PushPull, Speed::VeryHigh)),\n            new_pin!(d7, AfType::output(OutputType::PushPull, Speed::VeryHigh)),\n            new_pin!(sck, AfType::output(OutputType::PushPull, Speed::VeryHigh)),\n            new_pin!(\n                nss,\n                AfType::output_pull(OutputType::PushPull, Speed::VeryHigh, Pull::Up)\n            ),\n            new_pin!(dqs, AfType::output(OutputType::PushPull, Speed::VeryHigh)),\n            None,\n            config,\n            OspiWidth::OCTO,\n            false,\n        )\n    }\n}\n\nimpl<'d, T: Instance> Ospi<'d, T, Async> {\n    /// Create new blocking OSPI driver for a single spi external chip\n    pub fn new_singlespi<D: OctoDma<T>>(\n        peri: Peri<'d, T>,\n        sck: Peri<'d, impl SckPin<T>>,\n        d0: Peri<'d, impl D0Pin<T>>,\n        d1: Peri<'d, impl D1Pin<T>>,\n        nss: Peri<'d, impl NSSPin<T>>,\n        dma: Peri<'d, D>,\n        _irq: impl crate::interrupt::typelevel::Binding<D::Interrupt, crate::dma::InterruptHandler<D>> + 'd,\n        config: Config,\n    ) -> Self {\n        Self::new_inner(\n            peri,\n            new_pin!(d0, AfType::output(OutputType::PushPull, Speed::VeryHigh)),\n            new_pin!(d1, AfType::input(Pull::None)),\n            None,\n            None,\n            None,\n            None,\n            None,\n            None,\n            new_pin!(sck, AfType::output(OutputType::PushPull, Speed::VeryHigh)),\n            new_pin!(\n                nss,\n                AfType::output_pull(OutputType::PushPull, Speed::VeryHigh, Pull::Up)\n            ),\n            None,\n            new_dma!(dma, _irq),\n            config,\n            OspiWidth::SING,\n            false,\n        )\n    }\n\n    /// Create new blocking OSPI driver for a dualspi external chip\n    pub fn new_dualspi<D: OctoDma<T>>(\n        peri: Peri<'d, T>,\n        sck: Peri<'d, impl SckPin<T>>,\n        d0: Peri<'d, impl D0Pin<T>>,\n        d1: Peri<'d, impl D1Pin<T>>,\n        nss: Peri<'d, impl NSSPin<T>>,\n        dma: Peri<'d, D>,\n        _irq: impl crate::interrupt::typelevel::Binding<D::Interrupt, crate::dma::InterruptHandler<D>> + 'd,\n        config: Config,\n    ) -> Self {\n        Self::new_inner(\n            peri,\n            new_pin!(d0, AfType::output(OutputType::PushPull, Speed::VeryHigh)),\n            new_pin!(d1, AfType::output(OutputType::PushPull, Speed::VeryHigh)),\n            None,\n            None,\n            None,\n            None,\n            None,\n            None,\n            new_pin!(sck, AfType::output(OutputType::PushPull, Speed::VeryHigh)),\n            new_pin!(\n                nss,\n                AfType::output_pull(OutputType::PushPull, Speed::VeryHigh, Pull::Up)\n            ),\n            None,\n            new_dma!(dma, _irq),\n            config,\n            OspiWidth::DUAL,\n            false,\n        )\n    }\n\n    /// Create new blocking OSPI driver for a quadspi external chip\n    pub fn new_quadspi<D: OctoDma<T>>(\n        peri: Peri<'d, T>,\n        sck: Peri<'d, impl SckPin<T>>,\n        d0: Peri<'d, impl D0Pin<T>>,\n        d1: Peri<'d, impl D1Pin<T>>,\n        d2: Peri<'d, impl D2Pin<T>>,\n        d3: Peri<'d, impl D3Pin<T>>,\n        nss: Peri<'d, impl NSSPin<T>>,\n        dma: Peri<'d, D>,\n        _irq: impl crate::interrupt::typelevel::Binding<D::Interrupt, crate::dma::InterruptHandler<D>> + 'd,\n        config: Config,\n    ) -> Self {\n        Self::new_inner(\n            peri,\n            new_pin!(d0, AfType::output(OutputType::PushPull, Speed::VeryHigh)),\n            new_pin!(d1, AfType::output(OutputType::PushPull, Speed::VeryHigh)),\n            new_pin!(d2, AfType::output(OutputType::PushPull, Speed::VeryHigh)),\n            new_pin!(d3, AfType::output(OutputType::PushPull, Speed::VeryHigh)),\n            None,\n            None,\n            None,\n            None,\n            new_pin!(sck, AfType::output(OutputType::PushPull, Speed::VeryHigh)),\n            new_pin!(\n                nss,\n                AfType::output_pull(OutputType::PushPull, Speed::VeryHigh, Pull::Up)\n            ),\n            None,\n            new_dma!(dma, _irq),\n            config,\n            OspiWidth::QUAD,\n            false,\n        )\n    }\n\n    /// Create new blocking OSPI driver for two quadspi external chips\n    pub fn new_dualquadspi<D: OctoDma<T>>(\n        peri: Peri<'d, T>,\n        sck: Peri<'d, impl SckPin<T>>,\n        d0: Peri<'d, impl D0Pin<T>>,\n        d1: Peri<'d, impl D1Pin<T>>,\n        d2: Peri<'d, impl D2Pin<T>>,\n        d3: Peri<'d, impl D3Pin<T>>,\n        d4: Peri<'d, impl D4Pin<T>>,\n        d5: Peri<'d, impl D5Pin<T>>,\n        d6: Peri<'d, impl D6Pin<T>>,\n        d7: Peri<'d, impl D7Pin<T>>,\n        nss: Peri<'d, impl NSSPin<T>>,\n        dma: Peri<'d, D>,\n        _irq: impl crate::interrupt::typelevel::Binding<D::Interrupt, crate::dma::InterruptHandler<D>> + 'd,\n        config: Config,\n    ) -> Self {\n        Self::new_inner(\n            peri,\n            new_pin!(d0, AfType::output(OutputType::PushPull, Speed::VeryHigh)),\n            new_pin!(d1, AfType::output(OutputType::PushPull, Speed::VeryHigh)),\n            new_pin!(d2, AfType::output(OutputType::PushPull, Speed::VeryHigh)),\n            new_pin!(d3, AfType::output(OutputType::PushPull, Speed::VeryHigh)),\n            new_pin!(d4, AfType::output(OutputType::PushPull, Speed::VeryHigh)),\n            new_pin!(d5, AfType::output(OutputType::PushPull, Speed::VeryHigh)),\n            new_pin!(d6, AfType::output(OutputType::PushPull, Speed::VeryHigh)),\n            new_pin!(d7, AfType::output(OutputType::PushPull, Speed::VeryHigh)),\n            new_pin!(sck, AfType::output(OutputType::PushPull, Speed::VeryHigh)),\n            new_pin!(\n                nss,\n                AfType::output_pull(OutputType::PushPull, Speed::VeryHigh, Pull::Up)\n            ),\n            None,\n            new_dma!(dma, _irq),\n            config,\n            OspiWidth::QUAD,\n            true,\n        )\n    }\n\n    /// Create new blocking OSPI driver for octospi external chips\n    pub fn new_octospi<D: OctoDma<T>>(\n        peri: Peri<'d, T>,\n        sck: Peri<'d, impl SckPin<T>>,\n        d0: Peri<'d, impl D0Pin<T>>,\n        d1: Peri<'d, impl D1Pin<T>>,\n        d2: Peri<'d, impl D2Pin<T>>,\n        d3: Peri<'d, impl D3Pin<T>>,\n        d4: Peri<'d, impl D4Pin<T>>,\n        d5: Peri<'d, impl D5Pin<T>>,\n        d6: Peri<'d, impl D6Pin<T>>,\n        d7: Peri<'d, impl D7Pin<T>>,\n        nss: Peri<'d, impl NSSPin<T>>,\n        dma: Peri<'d, D>,\n        _irq: impl crate::interrupt::typelevel::Binding<D::Interrupt, crate::dma::InterruptHandler<D>> + 'd,\n        config: Config,\n    ) -> Self {\n        Self::new_inner(\n            peri,\n            new_pin!(d0, AfType::output(OutputType::PushPull, Speed::VeryHigh)),\n            new_pin!(d1, AfType::output(OutputType::PushPull, Speed::VeryHigh)),\n            new_pin!(d2, AfType::output(OutputType::PushPull, Speed::VeryHigh)),\n            new_pin!(d3, AfType::output(OutputType::PushPull, Speed::VeryHigh)),\n            new_pin!(d4, AfType::output(OutputType::PushPull, Speed::VeryHigh)),\n            new_pin!(d5, AfType::output(OutputType::PushPull, Speed::VeryHigh)),\n            new_pin!(d6, AfType::output(OutputType::PushPull, Speed::VeryHigh)),\n            new_pin!(d7, AfType::output(OutputType::PushPull, Speed::VeryHigh)),\n            new_pin!(sck, AfType::output(OutputType::PushPull, Speed::VeryHigh)),\n            new_pin!(\n                nss,\n                AfType::output_pull(OutputType::PushPull, Speed::VeryHigh, Pull::Up)\n            ),\n            None,\n            new_dma!(dma, _irq),\n            config,\n            OspiWidth::OCTO,\n            false,\n        )\n    }\n\n    /// Create new blocking OSPI driver for octospi external chips with DQS support\n    pub fn new_octospi_with_dqs<D: OctoDma<T>>(\n        peri: Peri<'d, T>,\n        sck: Peri<'d, impl SckPin<T>>,\n        d0: Peri<'d, impl D0Pin<T>>,\n        d1: Peri<'d, impl D1Pin<T>>,\n        d2: Peri<'d, impl D2Pin<T>>,\n        d3: Peri<'d, impl D3Pin<T>>,\n        d4: Peri<'d, impl D4Pin<T>>,\n        d5: Peri<'d, impl D5Pin<T>>,\n        d6: Peri<'d, impl D6Pin<T>>,\n        d7: Peri<'d, impl D7Pin<T>>,\n        nss: Peri<'d, impl NSSPin<T>>,\n        dqs: Peri<'d, impl DQSPin<T>>,\n        dma: Peri<'d, D>,\n        _irq: impl crate::interrupt::typelevel::Binding<D::Interrupt, crate::dma::InterruptHandler<D>> + 'd,\n        config: Config,\n    ) -> Self {\n        Self::new_inner(\n            peri,\n            new_pin!(d0, AfType::output(OutputType::PushPull, Speed::VeryHigh)),\n            new_pin!(d1, AfType::output(OutputType::PushPull, Speed::VeryHigh)),\n            new_pin!(d2, AfType::output(OutputType::PushPull, Speed::VeryHigh)),\n            new_pin!(d3, AfType::output(OutputType::PushPull, Speed::VeryHigh)),\n            new_pin!(d4, AfType::output(OutputType::PushPull, Speed::VeryHigh)),\n            new_pin!(d5, AfType::output(OutputType::PushPull, Speed::VeryHigh)),\n            new_pin!(d6, AfType::output(OutputType::PushPull, Speed::VeryHigh)),\n            new_pin!(d7, AfType::output(OutputType::PushPull, Speed::VeryHigh)),\n            new_pin!(sck, AfType::output(OutputType::PushPull, Speed::VeryHigh)),\n            new_pin!(\n                nss,\n                AfType::output_pull(OutputType::PushPull, Speed::VeryHigh, Pull::Up)\n            ),\n            new_pin!(dqs, AfType::output(OutputType::PushPull, Speed::VeryHigh)),\n            new_dma!(dma, _irq),\n            config,\n            OspiWidth::OCTO,\n            false,\n        )\n    }\n\n    /// Blocking read with DMA transfer\n    pub fn blocking_read_dma<W: Word>(&mut self, buf: &mut [W], transaction: TransferConfig) -> Result<(), OspiError> {\n        if buf.is_empty() {\n            return Err(OspiError::EmptyBuffer);\n        }\n\n        // Wait for peripheral to be free\n        while T::REGS.sr().read().busy() {}\n\n        let transfer_size_bytes = buf.len() * W::size().bytes();\n        self.configure_command(&transaction, Some(transfer_size_bytes))?;\n\n        let current_address = T::REGS.ar().read().address();\n        let current_instruction = T::REGS.ir().read().instruction();\n\n        // For a indirect read transaction, the transaction begins when the instruction/address is set\n        T::REGS\n            .cr()\n            .modify(|v| v.set_fmode(vals::FunctionalMode::INDIRECT_READ));\n        if T::REGS.ccr().read().admode() == vals::PhaseMode::NONE {\n            T::REGS.ir().write(|v| v.set_instruction(current_instruction));\n        } else {\n            T::REGS.ar().write(|v| v.set_address(current_address));\n        }\n\n        for chunk in buf.chunks_mut(0xFFFF / W::size().bytes()) {\n            let transfer = unsafe {\n                self.dma\n                    .as_mut()\n                    .unwrap()\n                    .read(T::REGS.dr().as_ptr() as *mut W, chunk, Default::default())\n            };\n\n            T::REGS.cr().modify(|w| w.set_dmaen(true));\n\n            transfer.blocking_wait();\n        }\n\n        finish_dma(T::REGS);\n\n        Ok(())\n    }\n\n    /// Blocking write with DMA transfer\n    pub fn blocking_write_dma<W: Word>(&mut self, buf: &[W], transaction: TransferConfig) -> Result<(), OspiError> {\n        if buf.is_empty() {\n            return Err(OspiError::EmptyBuffer);\n        }\n\n        // Wait for peripheral to be free\n        while T::REGS.sr().read().busy() {}\n\n        let transfer_size_bytes = buf.len() * W::size().bytes();\n        self.configure_command(&transaction, Some(transfer_size_bytes))?;\n        T::REGS\n            .cr()\n            .modify(|v| v.set_fmode(vals::FunctionalMode::INDIRECT_WRITE));\n\n        // TODO: implement this using a LinkedList DMA to offload the whole transfer off the CPU.\n        for chunk in buf.chunks(0xFFFF / W::size().bytes()) {\n            let transfer = unsafe {\n                self.dma\n                    .as_mut()\n                    .unwrap()\n                    .write(chunk, T::REGS.dr().as_ptr() as *mut W, Default::default())\n            };\n\n            T::REGS.cr().modify(|w| w.set_dmaen(true));\n\n            transfer.blocking_wait();\n        }\n\n        finish_dma(T::REGS);\n\n        Ok(())\n    }\n\n    /// Asynchronous read from external device\n    pub async fn read<W: Word>(&mut self, buf: &mut [W], transaction: TransferConfig) -> Result<(), OspiError> {\n        if buf.is_empty() {\n            return Err(OspiError::EmptyBuffer);\n        }\n\n        // Wait for peripheral to be free\n        while T::REGS.sr().read().busy() {}\n\n        let transfer_size_bytes = buf.len() * W::size().bytes();\n        self.configure_command(&transaction, Some(transfer_size_bytes))?;\n\n        let current_address = T::REGS.ar().read().address();\n        let current_instruction = T::REGS.ir().read().instruction();\n\n        // For a indirect read transaction, the transaction begins when the instruction/address is set\n        T::REGS\n            .cr()\n            .modify(|v| v.set_fmode(vals::FunctionalMode::INDIRECT_READ));\n        if T::REGS.ccr().read().admode() == vals::PhaseMode::NONE {\n            T::REGS.ir().write(|v| v.set_instruction(current_instruction));\n        } else {\n            T::REGS.ar().write(|v| v.set_address(current_address));\n        }\n\n        for chunk in buf.chunks_mut(0xFFFF / W::size().bytes()) {\n            let transfer = unsafe {\n                self.dma\n                    .as_mut()\n                    .unwrap()\n                    .read(T::REGS.dr().as_ptr() as *mut W, chunk, Default::default())\n            };\n\n            T::REGS.cr().modify(|w| w.set_dmaen(true));\n\n            transfer.await;\n        }\n\n        finish_dma(T::REGS);\n\n        Ok(())\n    }\n\n    /// Asynchronous write to external device\n    pub async fn write<W: Word>(&mut self, buf: &[W], transaction: TransferConfig) -> Result<(), OspiError> {\n        if buf.is_empty() {\n            return Err(OspiError::EmptyBuffer);\n        }\n\n        // Wait for peripheral to be free\n        while T::REGS.sr().read().busy() {}\n\n        let transfer_size_bytes = buf.len() * W::size().bytes();\n        self.configure_command(&transaction, Some(transfer_size_bytes))?;\n        T::REGS\n            .cr()\n            .modify(|v| v.set_fmode(vals::FunctionalMode::INDIRECT_WRITE));\n\n        // TODO: implement this using a LinkedList DMA to offload the whole transfer off the CPU.\n        for chunk in buf.chunks(0xFFFF / W::size().bytes()) {\n            let transfer = unsafe {\n                self.dma\n                    .as_mut()\n                    .unwrap()\n                    .write(chunk, T::REGS.dr().as_ptr() as *mut W, Default::default())\n            };\n\n            T::REGS.cr().modify(|w| w.set_dmaen(true));\n\n            transfer.await;\n        }\n\n        finish_dma(T::REGS);\n\n        Ok(())\n    }\n}\n\nimpl<'d, T: Instance, M: PeriMode> Drop for Ospi<'d, T, M> {\n    fn drop(&mut self) {\n        rcc::disable::<T>();\n    }\n}\n\nfn finish_dma(regs: Regs) {\n    while !regs.sr().read().tcf() {}\n    regs.fcr().write(|v| v.set_ctcf(true));\n\n    regs.cr().modify(|w| {\n        w.set_dmaen(false);\n    });\n}\n\n#[cfg(octospim_v1)]\n/// OctoSPI I/O manager instance trait.\npub(crate) trait SealedOctospimInstance {\n    const OCTOSPIM_REGS: Octospim;\n    const OCTOSPI_IDX: u8;\n}\n\n/// OctoSPI instance trait.\npub(crate) trait SealedInstance {\n    const REGS: Regs;\n}\n\n/// OSPI instance trait.\n#[cfg(octospim_v1)]\n#[allow(private_bounds)]\npub trait Instance: SealedInstance + PeripheralType + RccPeripheral + SealedOctospimInstance {}\n\n/// OSPI instance trait.\n#[cfg(not(octospim_v1))]\n#[allow(private_bounds)]\npub trait Instance: SealedInstance + PeripheralType + RccPeripheral {}\n\npin_trait!(SckPin, Instance);\npin_trait!(NckPin, Instance);\npin_trait!(D0Pin, Instance);\npin_trait!(D1Pin, Instance);\npin_trait!(D2Pin, Instance);\npin_trait!(D3Pin, Instance);\npin_trait!(D4Pin, Instance);\npin_trait!(D5Pin, Instance);\npin_trait!(D6Pin, Instance);\npin_trait!(D7Pin, Instance);\npin_trait!(DQSPin, Instance);\npin_trait!(NSSPin, Instance);\ndma_trait!(OctoDma, Instance);\n\n// Hard-coded the octospi index, for OCTOSPIM\n#[cfg(octospim_v1)]\nimpl SealedOctospimInstance for peripherals::OCTOSPI1 {\n    const OCTOSPIM_REGS: Octospim = crate::pac::OCTOSPIM;\n    const OCTOSPI_IDX: u8 = 1;\n}\n\n#[cfg(all(octospim_v1, peri_octospi2))]\nimpl SealedOctospimInstance for peripherals::OCTOSPI2 {\n    const OCTOSPIM_REGS: Octospim = crate::pac::OCTOSPIM;\n    const OCTOSPI_IDX: u8 = 2;\n}\n\n#[cfg(octospim_v1)]\nforeach_peripheral!(\n    (octospi, $inst:ident) => {\n        impl SealedInstance for peripherals::$inst {\n            const REGS: Regs = crate::pac::$inst;\n        }\n\n        impl Instance for peripherals::$inst {}\n    };\n);\n\n#[cfg(not(octospim_v1))]\nforeach_peripheral!(\n    (octospi, $inst:ident) => {\n        impl SealedInstance for peripherals::$inst {\n            const REGS: Regs = crate::pac::$inst;\n        }\n\n        impl Instance for peripherals::$inst {}\n    };\n);\n\nimpl<'d, T: Instance, M: PeriMode> SetConfig for Ospi<'d, T, M> {\n    type Config = Config;\n    type ConfigError = ();\n    fn set_config(&mut self, config: &Self::Config) -> Result<(), ()> {\n        self.set_config(config);\n        Ok(())\n    }\n}\n\nimpl<'d, T: Instance, M: PeriMode> GetConfig for Ospi<'d, T, M> {\n    type Config = Config;\n    fn get_config(&self) -> Self::Config {\n        self.get_config()\n    }\n}\n\n/// Word sizes usable for OSPI.\n#[allow(private_bounds)]\npub trait Word: word::Word {}\n\nmacro_rules! impl_word {\n    ($T:ty) => {\n        impl Word for $T {}\n    };\n}\n\nimpl_word!(u8);\nimpl_word!(u16);\nimpl_word!(u32);\n"
  },
  {
    "path": "embassy-stm32/src/pka/mod.rs",
    "content": "//! Public Key Accelerator (PKA)\n//!\n//! This module provides hardware-accelerated public key cryptographic operations using the PKA\n//! peripheral. The PKA can accelerate:\n//!\n//! - **ECDSA**: Signature generation and verification\n//! - **ECDH**: Elliptic Curve Diffie-Hellman key agreement (via scalar multiplication)\n//! - **RSA**: Encryption, decryption, and signing (via modular exponentiation)\n//! - **Arithmetic**: Modular operations, Montgomery multiplication\n//!\n//! # Supported Operations\n//!\n//! | Operation | Mode | Description |\n//! |-----------|------|-------------|\n//! | Modular Exponentiation | 0x00 | RSA encryption/decryption |\n//! | Montgomery Parameter | 0x01 | Compute Montgomery parameter for RSA |\n//! | RSA CRT Exponentiation | 0x07 | Fast RSA with Chinese Remainder Theorem |\n//! | Modular Inversion | 0x08 | Compute modular inverse |\n//! | ECC Scalar Multiplication | 0x20 | ECDH key agreement, point multiplication |\n//! | ECDSA Sign | 0x24 | Generate ECDSA signatures |\n//! | ECDSA Verify | 0x26 | Verify ECDSA signatures |\n//! | Point Check | 0x28 | Validate point is on curve |\n//!\n//! # Example - ECDSA Signature Verification\n//!\n//! ```no_run\n//! use embassy_stm32::pka::{Pka, EcdsaCurveParams, EcdsaPublicKey, EcdsaSignature};\n//!\n//! let mut pka = Pka::new_blocking(p.PKA, Irqs);\n//! let params = EcdsaCurveParams::nist_p256();\n//!\n//! let public_key = EcdsaPublicKey {\n//!     x: &pub_key_x,\n//!     y: &pub_key_y,\n//! };\n//!\n//! let signature = EcdsaSignature {\n//!     r: &sig_r,\n//!     s: &sig_s,\n//! };\n//!\n//! let valid = pka.ecdsa_verify(&params, &public_key, &signature, &hash)?;\n//! ```\n//!\n//! # Example - ECDH Key Agreement\n//!\n//! ```no_run\n//! use embassy_stm32::pka::{Pka, EccMulParams, EccPoint};\n//!\n//! let mut pka = Pka::new_blocking(p.PKA, Irqs);\n//! let params = EccMulParams::nist_p256();\n//!\n//! // Compute shared_secret = private_key * peer_public_key\n//! let peer_public = EccPoint { x: &peer_x, y: &peer_y };\n//! let shared_point = pka.ecc_mul(&params, &private_key, &peer_public)?;\n//! ```\n//!\n//! # Security Notes\n//!\n//! - Always use cryptographically secure random numbers for ECDSA k values\n//! - Validate all public keys before use (use `point_check`)\n//! - Use constant-time operations when possible (hardware provides this)\n//! - Clear sensitive data from memory after use\n\nuse core::marker::PhantomData;\n\nuse embassy_hal_internal::{Peri, PeripheralType};\nuse embassy_sync::waitqueue::AtomicWaker;\n\nuse crate::interrupt::typelevel::Interrupt;\nuse crate::{interrupt, pac, peripherals, rcc};\n\nstatic PKA_WAKER: AtomicWaker = AtomicWaker::new();\n\n// ============================================================================\n// PKA Modes\n// ============================================================================\n\n/// PKA operation modes\n#[derive(Debug, Clone, Copy, PartialEq, Eq)]\n#[repr(u8)]\npub enum PkaMode {\n    /// Modular exponentiation (RSA)\n    ModularExp = 0x00,\n    /// Montgomery parameter computation\n    MontgomeryParam = 0x01,\n    /// Modular exponentiation fast mode\n    ModularExpFast = 0x02,\n    /// Modular exponentiation with protection\n    ModularExpProtect = 0x03,\n    /// RSA CRT exponentiation\n    RsaCrtExp = 0x07,\n    /// Modular inversion\n    ModularInv = 0x08,\n    /// Arithmetic addition\n    ArithmeticAdd = 0x09,\n    /// Arithmetic subtraction\n    ArithmeticSub = 0x0A,\n    /// Arithmetic multiplication\n    ArithmeticMul = 0x0B,\n    /// Comparison\n    Comparison = 0x0C,\n    /// Modular reduction\n    ModularRed = 0x0D,\n    /// Modular addition\n    ModularAdd = 0x0E,\n    /// Modular subtraction\n    ModularSub = 0x0F,\n    /// Montgomery multiplication\n    MontgomeryMul = 0x10,\n    /// ECC scalar multiplication\n    EccMul = 0x20,\n    /// ECC complete addition\n    EccCompleteAdd = 0x23,\n    /// ECDSA signature generation\n    EcdsaSign = 0x24,\n    /// ECDSA signature verification\n    EcdsaVerify = 0x26,\n    /// Double base ladder\n    DoubleBaseLadder = 0x27,\n    /// Point check (validate point on curve)\n    PointCheck = 0x28,\n    /// ECC projective to affine\n    EccProjectiveToAffine = 0x2F,\n}\n\n// ============================================================================\n// RAM Offsets for each operation (byte offsets from PKA RAM base)\n// Derived from CMSIS headers: offset = raw_address - 0x0400\n// ============================================================================\n\nmod offsets {\n    // Montgomery parameter computation\n    pub mod montgomery_param {\n        pub const IN_MOD_NB_BITS: usize = 0x08;\n        pub const IN_MODULUS: usize = 0xC88;\n        pub const OUT_PARAMETER: usize = 0x220;\n    }\n\n    // Modular exponentiation (RSA)\n    pub mod modular_exp {\n        pub const IN_EXP_NB_BITS: usize = 0x00;\n        pub const IN_OP_NB_BITS: usize = 0x08;\n        pub const IN_MONTGOMERY_PARAM: usize = 0x220;\n        pub const IN_EXPONENT_BASE: usize = 0x868;\n        pub const IN_EXPONENT: usize = 0xA78;\n        pub const IN_MODULUS: usize = 0xC88;\n        pub const OUT_RESULT: usize = 0x438;\n        #[allow(dead_code)]\n        pub const OUT_ERROR: usize = 0xE98;\n    }\n\n    // Modular exponentiation protected mode\n    pub mod modular_exp_protect {\n        pub const IN_EXP_NB_BITS: usize = 0x00;\n        pub const IN_OP_NB_BITS: usize = 0x08;\n        pub const IN_EXPONENT_BASE: usize = 0x12C8; // 0x16C8 - 0x0400\n        pub const IN_EXPONENT: usize = 0x10B8; // 0x14B8 - 0x0400\n        pub const IN_MODULUS: usize = 0x438; // 0x0838 - 0x0400\n        pub const IN_PHI: usize = 0x868; // 0x0C68 - 0x0400\n        pub const OUT_RESULT: usize = 0x438;\n    }\n\n    // RSA CRT exponentiation\n    pub mod rsa_crt {\n        pub const IN_MOD_NB_BITS: usize = 0x08;\n        pub const IN_DP_CRT: usize = 0x330;\n        pub const IN_DQ_CRT: usize = 0xA78;\n        pub const IN_QINV_CRT: usize = 0x548;\n        pub const IN_PRIME_P: usize = 0x760;\n        pub const IN_PRIME_Q: usize = 0xC88;\n        pub const IN_EXPONENT_BASE: usize = 0xEA0;\n        pub const OUT_RESULT: usize = 0x438;\n    }\n\n    // ECC scalar multiplication\n    pub mod ecc_mul {\n        pub const IN_EXP_NB_BITS: usize = 0x00;\n        pub const IN_OP_NB_BITS: usize = 0x08;\n        pub const IN_A_COEFF_SIGN: usize = 0x10;\n        pub const IN_A_COEFF: usize = 0x18;\n        pub const IN_B_COEFF: usize = 0x120;\n        pub const IN_MOD_GF: usize = 0xC88;\n        pub const IN_K: usize = 0xEA0;\n        pub const IN_INITIAL_POINT_X: usize = 0x178;\n        pub const IN_INITIAL_POINT_Y: usize = 0x70;\n        pub const IN_N_PRIME_ORDER: usize = 0xB88;\n        pub const OUT_RESULT_X: usize = 0x178;\n        pub const OUT_RESULT_Y: usize = 0x1D0;\n        pub const OUT_ERROR: usize = 0x280;\n    }\n\n    // ECDSA signature generation\n    pub mod ecdsa_sign {\n        pub const IN_ORDER_NB_BITS: usize = 0x00;\n        pub const IN_MOD_NB_BITS: usize = 0x08;\n        pub const IN_A_COEFF_SIGN: usize = 0x10;\n        pub const IN_A_COEFF: usize = 0x18;\n        pub const IN_B_COEFF: usize = 0x120;\n        pub const IN_MOD_GF: usize = 0xC88;\n        pub const IN_K: usize = 0xEA0;\n        pub const IN_INITIAL_POINT_X: usize = 0x178;\n        pub const IN_INITIAL_POINT_Y: usize = 0x70;\n        pub const IN_HASH_E: usize = 0xBE8;\n        pub const IN_PRIVATE_KEY_D: usize = 0xB28;\n        pub const IN_ORDER_N: usize = 0xB88;\n        pub const OUT_ERROR: usize = 0xBE0;\n        pub const OUT_SIGNATURE_R: usize = 0x330;\n        pub const OUT_SIGNATURE_S: usize = 0x388;\n        #[allow(dead_code)]\n        pub const OUT_FINAL_POINT_X: usize = 0x1000;\n        #[allow(dead_code)]\n        pub const OUT_FINAL_POINT_Y: usize = 0x1058;\n    }\n\n    // ECDSA signature verification\n    pub mod ecdsa_verif {\n        pub const IN_ORDER_NB_BITS: usize = 0x08;\n        pub const IN_MOD_NB_BITS: usize = 0xC8;\n        pub const IN_A_COEFF_SIGN: usize = 0x68;\n        pub const IN_A_COEFF: usize = 0x70;\n        pub const IN_MOD_GF: usize = 0xD0;\n        pub const IN_INITIAL_POINT_X: usize = 0x278;\n        pub const IN_INITIAL_POINT_Y: usize = 0x2D0;\n        pub const IN_PUBLIC_KEY_POINT_X: usize = 0xEF8;\n        pub const IN_PUBLIC_KEY_POINT_Y: usize = 0xF50;\n        pub const IN_SIGNATURE_R: usize = 0xCE0;\n        pub const IN_SIGNATURE_S: usize = 0x868;\n        pub const IN_HASH_E: usize = 0xFA8;\n        pub const IN_ORDER_N: usize = 0xC88;\n        pub const OUT_RESULT: usize = 0x1D0;\n    }\n\n    // Point check\n    pub mod point_check {\n        pub const IN_MOD_NB_BITS: usize = 0x08;\n        pub const IN_A_COEFF_SIGN: usize = 0x10;\n        pub const IN_A_COEFF: usize = 0x18;\n        pub const IN_B_COEFF: usize = 0x120;\n        pub const IN_MOD_GF: usize = 0x70;\n        pub const IN_INITIAL_POINT_X: usize = 0x178;\n        pub const IN_INITIAL_POINT_Y: usize = 0x1D0;\n        #[allow(dead_code)]\n        pub const IN_MONTGOMERY_PARAM: usize = 0xC8;\n        pub const OUT_ERROR: usize = 0x280;\n    }\n\n    // Modular inversion\n    pub mod modular_inv {\n        pub const IN_NB_BITS: usize = 0x08;\n        pub const IN_OP1: usize = 0x650;\n        pub const IN_OP2_MOD: usize = 0x868;\n        pub const OUT_RESULT: usize = 0xA78;\n    }\n\n    // Generic arithmetic operations (add, sub, mul, comparison, modular add/sub, montgomery mul)\n    pub mod arithmetic {\n        pub const IN_NB_BITS: usize = 0x08;\n        pub const IN_OP1: usize = 0x650;\n        pub const IN_OP2: usize = 0x868;\n        pub const IN_OP3_MOD: usize = 0xC88;\n        pub const OUT_RESULT: usize = 0xA78;\n    }\n\n    // Modular reduction\n    pub mod modular_red {\n        pub const IN_OP_LENGTH: usize = 0x00;\n        pub const IN_MOD_LENGTH: usize = 0x08;\n        pub const IN_OPERAND: usize = 0x650;\n        pub const IN_MODULUS: usize = 0x868;\n        pub const OUT_RESULT: usize = 0xA78;\n    }\n\n    // ECC complete addition (projective coordinates)\n    pub mod ecc_complete_add {\n        pub const IN_MOD_NB_BITS: usize = 0x08;\n        pub const IN_A_COEFF_SIGN: usize = 0x10;\n        pub const IN_A_COEFF: usize = 0x18;\n        pub const IN_MOD_P: usize = 0x70;\n        pub const IN_POINT1_X: usize = 0x228;\n        pub const IN_POINT1_Y: usize = 0x280;\n        pub const IN_POINT1_Z: usize = 0x2D8;\n        pub const IN_POINT2_X: usize = 0x330;\n        pub const IN_POINT2_Y: usize = 0x388;\n        pub const IN_POINT2_Z: usize = 0x3E0;\n        pub const OUT_RESULT_X: usize = 0x960;\n        pub const OUT_RESULT_Y: usize = 0x9B8;\n        pub const OUT_RESULT_Z: usize = 0xA10;\n    }\n\n    // ECC double base ladder (k*P + m*Q)\n    pub mod double_base_ladder {\n        pub const IN_PRIME_ORDER_NB_BITS: usize = 0x00;\n        pub const IN_MOD_NB_BITS: usize = 0x08;\n        pub const IN_A_COEFF_SIGN: usize = 0x10;\n        pub const IN_A_COEFF: usize = 0x18;\n        pub const IN_MOD_P: usize = 0x70;\n        pub const IN_K: usize = 0x120;\n        pub const IN_M: usize = 0x178;\n        pub const IN_POINT1_X: usize = 0x228;\n        pub const IN_POINT1_Y: usize = 0x280;\n        pub const IN_POINT1_Z: usize = 0x2D8;\n        pub const IN_POINT2_X: usize = 0x330;\n        pub const IN_POINT2_Y: usize = 0x388;\n        pub const IN_POINT2_Z: usize = 0x3E0;\n        pub const OUT_RESULT_X: usize = 0x178;\n        pub const OUT_RESULT_Y: usize = 0x1D0;\n        pub const OUT_ERROR: usize = 0x120;\n    }\n\n    // ECC projective to affine conversion\n    pub mod projective_to_affine {\n        pub const IN_MOD_NB_BITS: usize = 0x08;\n        pub const IN_MOD_P: usize = 0x70;\n        pub const IN_POINT_X: usize = 0x960;\n        pub const IN_POINT_Y: usize = 0x9B8;\n        pub const IN_POINT_Z: usize = 0xA10;\n        pub const IN_MONTGOMERY_PARAM: usize = 0xC8;\n        pub const OUT_RESULT_X: usize = 0x178;\n        pub const OUT_RESULT_Y: usize = 0x1D0;\n        pub const OUT_ERROR: usize = 0x280;\n    }\n}\n\n// ============================================================================\n// Interrupt Handler\n// ============================================================================\n\n/// PKA interrupt handler.\npub struct InterruptHandler<T: Instance> {\n    _phantom: PhantomData<T>,\n}\n\nimpl<T: Instance> interrupt::typelevel::Handler<T::Interrupt> for InterruptHandler<T> {\n    unsafe fn on_interrupt() {\n        let sr = T::regs().sr().read();\n\n        if sr.procendf() {\n            T::regs().clrfr().write(|w| w.set_procendfc(true));\n            PKA_WAKER.wake();\n        }\n\n        if sr.ramerrf() || sr.addrerrf() || sr.operrf() {\n            T::regs().clrfr().write(|w| {\n                w.set_ramerrfc(true);\n                w.set_addrerrfc(true);\n                w.set_operrfc(true);\n            });\n            PKA_WAKER.wake();\n        }\n    }\n}\n\n// ============================================================================\n// Error Types\n// ============================================================================\n\n/// PKA error\n#[derive(Debug, PartialEq, Eq, Clone, Copy)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub enum Error {\n    /// PKA RAM access error\n    RamError,\n    /// Invalid RAM address\n    AddressError,\n    /// Operation error (invalid inputs or computation failed)\n    OperationError,\n    /// Invalid parameter size\n    InvalidSize,\n    /// Initialization timeout\n    Timeout,\n    /// Point is not on the curve\n    PointNotOnCurve,\n}\n\n// ============================================================================\n// Data Structures\n// ============================================================================\n\n/// ECDSA/ECC curve parameters\n#[derive(Clone)]\npub struct EcdsaCurveParams {\n    /// Prime field modulus p\n    pub p_modulus: &'static [u8],\n    /// Curve coefficient |a| (absolute value)\n    pub a_coefficient: &'static [u8],\n    /// Sign of curve coefficient a (0 = positive, 1 = negative)\n    pub a_coefficient_sign: u32,\n    /// Curve coefficient b\n    pub b_coefficient: &'static [u8],\n    /// Base point x-coordinate\n    pub generator_x: &'static [u8],\n    /// Base point y-coordinate\n    pub generator_y: &'static [u8],\n    /// Curve order n\n    pub order: &'static [u8],\n}\n\nimpl EcdsaCurveParams {\n    /// NIST P-256 (secp256r1) curve parameters\n    pub const fn nist_p256() -> Self {\n        Self {\n            p_modulus: &P256_P,\n            // For P-256, a = -3 (mod p), so we use |a| = 3 with sign = 1 (negative)\n            a_coefficient: &P256_A,\n            a_coefficient_sign: 1, // negative\n            b_coefficient: &P256_B,\n            generator_x: &P256_GX,\n            generator_y: &P256_GY,\n            order: &P256_N,\n        }\n    }\n}\n\n// NIST P-256 curve parameters (big-endian)\nconst P256_P: [u8; 32] = [\n    0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n    0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,\n];\n// |a| = 3 (absolute value of -3)\nconst P256_A: [u8; 32] = [\n    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03,\n];\nconst P256_B: [u8; 32] = [\n    0x5A, 0xC6, 0x35, 0xD8, 0xAA, 0x3A, 0x93, 0xE7, 0xB3, 0xEB, 0xBD, 0x55, 0x76, 0x98, 0x86, 0xBC, 0x65, 0x1D, 0x06,\n    0xB0, 0xCC, 0x53, 0xB0, 0xF6, 0x3B, 0xCE, 0x3C, 0x3E, 0x27, 0xD2, 0x60, 0x4B,\n];\nconst P256_GX: [u8; 32] = [\n    0x6B, 0x17, 0xD1, 0xF2, 0xE1, 0x2C, 0x42, 0x47, 0xF8, 0xBC, 0xE6, 0xE5, 0x63, 0xA4, 0x40, 0xF2, 0x77, 0x03, 0x7D,\n    0x81, 0x2D, 0xEB, 0x33, 0xA0, 0xF4, 0xA1, 0x39, 0x45, 0xD8, 0x98, 0xC2, 0x96,\n];\nconst P256_GY: [u8; 32] = [\n    0x4F, 0xE3, 0x42, 0xE2, 0xFE, 0x1A, 0x7F, 0x9B, 0x8E, 0xE7, 0xEB, 0x4A, 0x7C, 0x0F, 0x9E, 0x16, 0x2B, 0xCE, 0x33,\n    0x57, 0x6B, 0x31, 0x5E, 0xCE, 0xCB, 0xB6, 0x40, 0x68, 0x37, 0xBF, 0x51, 0xF5,\n];\nconst P256_N: [u8; 32] = [\n    0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xBC, 0xE6, 0xFA,\n    0xAD, 0xA7, 0x17, 0x9E, 0x84, 0xF3, 0xB9, 0xCA, 0xC2, 0xFC, 0x63, 0x25, 0x51,\n];\n\n/// ECDSA public key\npub struct EcdsaPublicKey<'a> {\n    /// Public key x-coordinate\n    pub x: &'a [u8],\n    /// Public key y-coordinate\n    pub y: &'a [u8],\n}\n\n/// ECDSA signature\npub struct EcdsaSignature<'a> {\n    /// Signature r component\n    pub r: &'a [u8],\n    /// Signature s component\n    pub s: &'a [u8],\n}\n\n/// ECC point (for scalar multiplication results)\npub struct EccPoint {\n    /// X coordinate\n    pub x: [u8; 66], // Max size for P-521\n    /// Y coordinate\n    pub y: [u8; 66],\n    /// Actual size of coordinates in bytes\n    pub size: usize,\n}\n\nimpl EccPoint {\n    /// Create a new point with given size\n    pub fn new(size: usize) -> Self {\n        Self {\n            x: [0u8; 66],\n            y: [0u8; 66],\n            size,\n        }\n    }\n}\n\n/// RSA operation parameters\npub struct RsaParams<'a> {\n    /// Modulus n\n    pub modulus: &'a [u8],\n    /// Exponent (public or private)\n    pub exponent: &'a [u8],\n}\n\n/// RSA CRT parameters for fast decryption\npub struct RsaCrtParams<'a> {\n    /// Prime p\n    pub prime_p: &'a [u8],\n    /// Prime q\n    pub prime_q: &'a [u8],\n    /// d mod (p-1)\n    pub dp: &'a [u8],\n    /// d mod (q-1)\n    pub dq: &'a [u8],\n    /// q^(-1) mod p\n    pub qinv: &'a [u8],\n}\n\n/// ECC point in projective coordinates (X, Y, Z)\n///\n/// In projective coordinates, the affine point (x, y) is represented as (X, Y, Z)\n/// where x = X/Z and y = Y/Z (for standard projective) or x = X/Z² and y = Y/Z³\n/// (for Jacobian projective).\npub struct EccProjectivePoint {\n    /// X coordinate\n    pub x: [u8; 66], // Max size for P-521\n    /// Y coordinate\n    pub y: [u8; 66],\n    /// Z coordinate\n    pub z: [u8; 66],\n    /// Actual size of coordinates in bytes\n    pub size: usize,\n}\n\nimpl EccProjectivePoint {\n    /// Create a new projective point with given size\n    pub fn new(size: usize) -> Self {\n        Self {\n            x: [0u8; 66],\n            y: [0u8; 66],\n            z: [0u8; 66],\n            size,\n        }\n    }\n\n    /// Create from affine point (Z = 1)\n    pub fn from_affine(x: &[u8], y: &[u8]) -> Self {\n        let size = x.len();\n        let mut point = Self::new(size);\n        point.x[..size].copy_from_slice(x);\n        point.y[..size].copy_from_slice(y);\n        // Z = 1 in big-endian\n        point.z[size - 1] = 1;\n        point\n    }\n}\n\n/// Result of a comparison operation\n#[derive(Debug, Clone, Copy, PartialEq, Eq)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub enum ComparisonResult {\n    /// A < B\n    Less,\n    /// A == B\n    Equal,\n    /// A > B\n    Greater,\n}\n\n/// Parameters for modular exponentiation with protection (side-channel resistant)\npub struct ModExpProtectParams<'a> {\n    /// Base value\n    pub base: &'a [u8],\n    /// Exponent\n    pub exponent: &'a [u8],\n    /// Modulus n\n    pub modulus: &'a [u8],\n    /// Phi(n) = (p-1)(q-1) for RSA\n    pub phi: &'a [u8],\n}\n\n// ============================================================================\n// PKA Driver\n// ============================================================================\n\n/// PKA driver\npub struct Pka<'d, T: Instance> {\n    _peripheral: Peri<'d, T>,\n}\n\nimpl<'d, T: Instance> Pka<'d, T> {\n    const RAM_ERASE_TIMEOUT: u32 = 100_000;\n\n    /// Create a new PKA driver\n    pub fn new_blocking(\n        peripheral: Peri<'d, T>,\n        _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd,\n    ) -> Self {\n        rcc::enable_and_reset::<T>();\n\n        T::Interrupt::unpend();\n        unsafe { T::Interrupt::enable() };\n\n        Self {\n            _peripheral: peripheral,\n        }\n    }\n\n    // ========================================================================\n    // ECDSA Operations\n    // ========================================================================\n\n    /// Verify an ECDSA signature\n    ///\n    /// Returns `Ok(true)` if signature is valid, `Ok(false)` if invalid.\n    pub fn ecdsa_verify(\n        &mut self,\n        curve: &EcdsaCurveParams,\n        public_key: &EcdsaPublicKey,\n        signature: &EcdsaSignature,\n        message_hash: &[u8],\n    ) -> Result<bool, Error> {\n        let modulus_size = curve.p_modulus.len();\n        let order_size = curve.order.len();\n\n        // Validate sizes\n        if curve.a_coefficient.len() != modulus_size\n            || curve.generator_x.len() != modulus_size\n            || curve.generator_y.len() != modulus_size\n            || public_key.x.len() != modulus_size\n            || public_key.y.len() != modulus_size\n            || signature.r.len() != order_size\n            || signature.s.len() != order_size\n            || message_hash.len() > order_size\n        {\n            return Err(Error::InvalidSize);\n        }\n\n        self.init_pka()?;\n\n        // Write bit counts\n        let order_nb_bits = Self::get_opt_bit_size(order_size, curve.order[0]);\n        let mod_nb_bits = Self::get_opt_bit_size(modulus_size, curve.p_modulus[0]);\n\n        self.write_ram_word(offsets::ecdsa_verif::IN_ORDER_NB_BITS, order_nb_bits);\n        self.write_ram_word(offsets::ecdsa_verif::IN_MOD_NB_BITS, mod_nb_bits);\n        self.write_ram_word(offsets::ecdsa_verif::IN_A_COEFF_SIGN, curve.a_coefficient_sign);\n\n        // Write curve parameters (matching ST-HAL order)\n        self.write_operand(offsets::ecdsa_verif::IN_A_COEFF, curve.a_coefficient);\n        self.write_operand(offsets::ecdsa_verif::IN_MOD_GF, curve.p_modulus);\n        self.write_operand(offsets::ecdsa_verif::IN_INITIAL_POINT_X, curve.generator_x);\n        self.write_operand(offsets::ecdsa_verif::IN_INITIAL_POINT_Y, curve.generator_y);\n\n        // Write public key\n        self.write_operand(offsets::ecdsa_verif::IN_PUBLIC_KEY_POINT_X, public_key.x);\n        self.write_operand(offsets::ecdsa_verif::IN_PUBLIC_KEY_POINT_Y, public_key.y);\n\n        // Write signature\n        self.write_operand(offsets::ecdsa_verif::IN_SIGNATURE_R, signature.r);\n        self.write_operand(offsets::ecdsa_verif::IN_SIGNATURE_S, signature.s);\n\n        // Write hash and order (ST-HAL writes these last)\n        self.write_operand(offsets::ecdsa_verif::IN_HASH_E, message_hash);\n        self.write_operand(offsets::ecdsa_verif::IN_ORDER_N, curve.order);\n\n        // Set mode and start (matching ST-HAL: mode is set AFTER writing parameters)\n        self.set_mode(PkaMode::EcdsaVerify);\n        self.start_and_wait()?;\n\n        let result = self.read_ram_word(offsets::ecdsa_verif::OUT_RESULT);\n        self.disable_pka();\n\n        Ok(result == 0xD60D)\n    }\n\n    /// Generate an ECDSA signature\n    ///\n    /// # Arguments\n    /// * `curve` - Curve parameters\n    /// * `private_key` - Private key d\n    /// * `k` - Random nonce (MUST be cryptographically random and unique per signature!)\n    /// * `message_hash` - Hash of the message to sign\n    ///\n    /// # Returns\n    /// Signature (r, s) as byte arrays\n    ///\n    /// # Security Warning\n    /// The `k` value MUST be:\n    /// - Cryptographically random\n    /// - Unique for every signature\n    /// - Never reused or predictable\n    /// Failure to ensure this will compromise the private key!\n    pub fn ecdsa_sign(\n        &mut self,\n        curve: &EcdsaCurveParams,\n        private_key: &[u8],\n        k: &[u8],\n        message_hash: &[u8],\n        signature_r: &mut [u8],\n        signature_s: &mut [u8],\n    ) -> Result<(), Error> {\n        let modulus_size = curve.p_modulus.len();\n        let order_size = curve.order.len();\n\n        // Validate sizes\n        if private_key.len() != order_size\n            || k.len() != order_size\n            || message_hash.len() > order_size\n            || signature_r.len() < order_size\n            || signature_s.len() < order_size\n        {\n            return Err(Error::InvalidSize);\n        }\n\n        self.init_pka()?;\n        self.set_mode(PkaMode::EcdsaSign);\n\n        // Write bit counts\n        let order_nb_bits = Self::get_opt_bit_size(order_size, curve.order[0]);\n        let mod_nb_bits = Self::get_opt_bit_size(modulus_size, curve.p_modulus[0]);\n\n        self.write_ram_word(offsets::ecdsa_sign::IN_ORDER_NB_BITS, order_nb_bits);\n        self.write_ram_word(offsets::ecdsa_sign::IN_MOD_NB_BITS, mod_nb_bits);\n        self.write_ram_word(offsets::ecdsa_sign::IN_A_COEFF_SIGN, curve.a_coefficient_sign);\n\n        // Write curve parameters\n        self.write_operand(offsets::ecdsa_sign::IN_A_COEFF, curve.a_coefficient);\n        self.write_operand(offsets::ecdsa_sign::IN_B_COEFF, curve.b_coefficient);\n        self.write_operand(offsets::ecdsa_sign::IN_MOD_GF, curve.p_modulus);\n        self.write_operand(offsets::ecdsa_sign::IN_INITIAL_POINT_X, curve.generator_x);\n        self.write_operand(offsets::ecdsa_sign::IN_INITIAL_POINT_Y, curve.generator_y);\n        self.write_operand(offsets::ecdsa_sign::IN_ORDER_N, curve.order);\n\n        // Write private key and random k\n        self.write_operand(offsets::ecdsa_sign::IN_PRIVATE_KEY_D, private_key);\n        self.write_operand(offsets::ecdsa_sign::IN_K, k);\n        self.write_operand(offsets::ecdsa_sign::IN_HASH_E, message_hash);\n\n        self.start_and_wait()?;\n\n        // Check for errors - 0xD60D indicates success\n        let result = self.read_ram_word(offsets::ecdsa_sign::OUT_ERROR);\n        if result != 0xD60D {\n            self.disable_pka();\n            return Err(Error::OperationError);\n        }\n\n        // Read signature\n        self.read_operand(offsets::ecdsa_sign::OUT_SIGNATURE_R, &mut signature_r[..order_size]);\n        self.read_operand(offsets::ecdsa_sign::OUT_SIGNATURE_S, &mut signature_s[..order_size]);\n\n        self.disable_pka();\n        Ok(())\n    }\n\n    // ========================================================================\n    // ECC Scalar Multiplication (for ECDH)\n    // ========================================================================\n\n    /// Perform ECC scalar multiplication: result = k * P\n    ///\n    /// This is the core operation for ECDH key agreement:\n    /// - To generate public key: public = private_key * G (generator point)\n    /// - To compute shared secret: shared = my_private * peer_public\n    ///\n    /// # Arguments\n    /// * `curve` - Curve parameters\n    /// * `k` - Scalar multiplier\n    /// * `point_x` - Input point X coordinate\n    /// * `point_y` - Input point Y coordinate\n    /// * `result` - Output point (must be initialized with correct size)\n    pub fn ecc_mul(\n        &mut self,\n        curve: &EcdsaCurveParams,\n        k: &[u8],\n        point_x: &[u8],\n        point_y: &[u8],\n        result: &mut EccPoint,\n    ) -> Result<(), Error> {\n        let modulus_size = curve.p_modulus.len();\n        let order_size = curve.order.len();\n\n        if k.len() != order_size\n            || point_x.len() != modulus_size\n            || point_y.len() != modulus_size\n            || result.size != modulus_size\n        {\n            return Err(Error::InvalidSize);\n        }\n\n        self.init_pka()?;\n\n        // Write bit counts\n        // ST HAL uses scalar size with MSB of prime order (not scalar MSB)\n        let exp_nb_bits = Self::get_opt_bit_size(k.len(), curve.order[0]);\n        let mod_nb_bits = Self::get_opt_bit_size(modulus_size, curve.p_modulus[0]);\n\n        self.write_ram_word(offsets::ecc_mul::IN_EXP_NB_BITS, exp_nb_bits);\n        self.write_ram_word(offsets::ecc_mul::IN_OP_NB_BITS, mod_nb_bits);\n        self.write_ram_word(offsets::ecc_mul::IN_A_COEFF_SIGN, curve.a_coefficient_sign);\n\n        // Write curve parameters\n        self.write_operand(offsets::ecc_mul::IN_A_COEFF, curve.a_coefficient);\n        self.write_operand(offsets::ecc_mul::IN_B_COEFF, curve.b_coefficient);\n        self.write_operand(offsets::ecc_mul::IN_MOD_GF, curve.p_modulus);\n        self.write_operand(offsets::ecc_mul::IN_N_PRIME_ORDER, curve.order);\n\n        // Write scalar and point\n        self.write_operand(offsets::ecc_mul::IN_K, k);\n        self.write_operand(offsets::ecc_mul::IN_INITIAL_POINT_X, point_x);\n        self.write_operand(offsets::ecc_mul::IN_INITIAL_POINT_Y, point_y);\n\n        // Set mode right before start (matching ST HAL order)\n        self.set_mode(PkaMode::EccMul);\n        self.start_and_wait()?;\n\n        // Check for errors - 0xD60D indicates success\n        let status = self.read_ram_word(offsets::ecc_mul::OUT_ERROR);\n        if status != 0xD60D {\n            self.disable_pka();\n            return Err(Error::OperationError);\n        }\n\n        // Read result\n        self.read_operand(offsets::ecc_mul::OUT_RESULT_X, &mut result.x[..modulus_size]);\n        self.read_operand(offsets::ecc_mul::OUT_RESULT_Y, &mut result.y[..modulus_size]);\n\n        self.disable_pka();\n        Ok(())\n    }\n\n    /// Check if a point is on the curve\n    ///\n    /// This should be called to validate any externally-provided public key\n    /// before using it in cryptographic operations.\n    pub fn point_check(&mut self, curve: &EcdsaCurveParams, point_x: &[u8], point_y: &[u8]) -> Result<bool, Error> {\n        let modulus_size = curve.p_modulus.len();\n\n        if point_x.len() != modulus_size || point_y.len() != modulus_size {\n            return Err(Error::InvalidSize);\n        }\n\n        self.init_pka()?;\n\n        let mod_nb_bits = Self::get_opt_bit_size(modulus_size, curve.p_modulus[0]);\n\n        self.write_ram_word(offsets::point_check::IN_MOD_NB_BITS, mod_nb_bits);\n        self.write_ram_word(offsets::point_check::IN_A_COEFF_SIGN, curve.a_coefficient_sign);\n\n        self.write_operand(offsets::point_check::IN_A_COEFF, curve.a_coefficient);\n        self.write_operand(offsets::point_check::IN_B_COEFF, curve.b_coefficient);\n        self.write_operand(offsets::point_check::IN_MOD_GF, curve.p_modulus);\n        self.write_operand(offsets::point_check::IN_INITIAL_POINT_X, point_x);\n        self.write_operand(offsets::point_check::IN_INITIAL_POINT_Y, point_y);\n\n        // Set mode right before start (matching ST HAL order)\n        self.set_mode(PkaMode::PointCheck);\n        self.start_and_wait()?;\n\n        let result = self.read_ram_word(offsets::point_check::OUT_ERROR);\n        self.disable_pka();\n\n        // 0xD60D means point is on curve\n        Ok(result == 0xD60D)\n    }\n\n    // ========================================================================\n    // RSA Operations\n    // ========================================================================\n\n    /// Perform modular exponentiation: result = base^exp mod n\n    ///\n    /// This is the core RSA operation:\n    /// - Encryption: ciphertext = plaintext^e mod n\n    /// - Decryption: plaintext = ciphertext^d mod n\n    /// - Signing: signature = hash^d mod n\n    /// - Verification: hash = signature^e mod n\n    ///\n    /// # Arguments\n    /// * `base` - Base value (plaintext/ciphertext)\n    /// * `exponent` - Exponent (e for encrypt/verify, d for decrypt/sign)\n    /// * `modulus` - RSA modulus n\n    /// * `result` - Output buffer (must be same size as modulus)\n    pub fn modular_exp(\n        &mut self,\n        base: &[u8],\n        exponent: &[u8],\n        modulus: &[u8],\n        result: &mut [u8],\n    ) -> Result<(), Error> {\n        let mod_size = modulus.len();\n        let exp_size = exponent.len();\n\n        if base.len() > mod_size || result.len() < mod_size {\n            return Err(Error::InvalidSize);\n        }\n\n        self.init_pka()?;\n        self.set_mode(PkaMode::ModularExp);\n\n        // HAL uses byte-aligned bit sizes for modular exponentiation\n        let exp_nb_bits = (exp_size * 8) as u32;\n        let mod_nb_bits = (mod_size * 8) as u32;\n\n        self.write_ram_word(offsets::modular_exp::IN_EXP_NB_BITS, exp_nb_bits);\n        self.write_ram_word(offsets::modular_exp::IN_OP_NB_BITS, mod_nb_bits);\n\n        self.write_operand(offsets::modular_exp::IN_EXPONENT_BASE, base);\n        self.write_operand(offsets::modular_exp::IN_EXPONENT, exponent);\n        self.write_operand(offsets::modular_exp::IN_MODULUS, modulus);\n\n        self.start_and_wait()?;\n\n        self.read_operand(offsets::modular_exp::OUT_RESULT, &mut result[..mod_size]);\n\n        Ok(())\n    }\n\n    /// Perform RSA CRT exponentiation for fast decryption\n    ///\n    /// Uses Chinese Remainder Theorem for ~4x faster RSA private key operations.\n    ///\n    /// # Arguments\n    /// * `ciphertext` - Encrypted data\n    /// * `params` - CRT parameters (p, q, dp, dq, qinv)\n    /// * `result` - Output buffer\n    pub fn rsa_crt_exp(&mut self, ciphertext: &[u8], params: &RsaCrtParams, result: &mut [u8]) -> Result<(), Error> {\n        let p_size = params.prime_p.len();\n        let q_size = params.prime_q.len();\n        let mod_size = p_size + q_size; // n = p * q\n\n        if ciphertext.len() > mod_size\n            || params.dp.len() != p_size\n            || params.dq.len() != q_size\n            || params.qinv.len() != p_size\n            || result.len() < mod_size\n        {\n            return Err(Error::InvalidSize);\n        }\n\n        self.init_pka()?;\n        self.set_mode(PkaMode::RsaCrtExp);\n\n        // HAL uses byte-aligned bit sizes for RSA CRT\n        let mod_nb_bits = (mod_size * 8) as u32;\n\n        self.write_ram_word(offsets::rsa_crt::IN_MOD_NB_BITS, mod_nb_bits);\n\n        self.write_operand(offsets::rsa_crt::IN_PRIME_P, params.prime_p);\n        self.write_operand(offsets::rsa_crt::IN_PRIME_Q, params.prime_q);\n        self.write_operand(offsets::rsa_crt::IN_DP_CRT, params.dp);\n        self.write_operand(offsets::rsa_crt::IN_DQ_CRT, params.dq);\n        self.write_operand(offsets::rsa_crt::IN_QINV_CRT, params.qinv);\n        self.write_operand(offsets::rsa_crt::IN_EXPONENT_BASE, ciphertext);\n\n        self.start_and_wait()?;\n\n        self.read_operand(offsets::rsa_crt::OUT_RESULT, &mut result[..mod_size]);\n\n        Ok(())\n    }\n\n    // ========================================================================\n    // Modular Arithmetic Operations\n    // ========================================================================\n\n    /// Compute modular inverse: result = a^(-1) mod n\n    pub fn modular_inv(&mut self, a: &[u8], modulus: &[u8], result: &mut [u8]) -> Result<(), Error> {\n        let size = modulus.len();\n\n        if a.len() != size || result.len() < size {\n            return Err(Error::InvalidSize);\n        }\n\n        self.init_pka()?;\n        self.set_mode(PkaMode::ModularInv);\n\n        let nb_bits = Self::get_opt_bit_size(size, modulus[0]);\n        self.write_ram_word(offsets::modular_inv::IN_NB_BITS, nb_bits);\n\n        self.write_operand(offsets::modular_inv::IN_OP1, a);\n        self.write_operand(offsets::modular_inv::IN_OP2_MOD, modulus);\n\n        self.start_and_wait()?;\n\n        self.read_operand(offsets::modular_inv::OUT_RESULT, &mut result[..size]);\n\n        Ok(())\n    }\n\n    /// Compute modular addition: result = (a + b) mod n\n    pub fn modular_add(&mut self, a: &[u8], b: &[u8], modulus: &[u8], result: &mut [u8]) -> Result<(), Error> {\n        self.arithmetic_op(PkaMode::ModularAdd, a, b, Some(modulus), result)\n    }\n\n    /// Compute modular subtraction: result = (a - b) mod n\n    pub fn modular_sub(&mut self, a: &[u8], b: &[u8], modulus: &[u8], result: &mut [u8]) -> Result<(), Error> {\n        self.arithmetic_op(PkaMode::ModularSub, a, b, Some(modulus), result)\n    }\n\n    /// Compute arithmetic multiplication: result = a * b\n    pub fn arithmetic_mul(&mut self, a: &[u8], b: &[u8], result: &mut [u8]) -> Result<(), Error> {\n        self.arithmetic_op(PkaMode::ArithmeticMul, a, b, None, result)\n    }\n\n    // Generic arithmetic operation helper\n    fn arithmetic_op(\n        &mut self,\n        mode: PkaMode,\n        a: &[u8],\n        b: &[u8],\n        modulus: Option<&[u8]>,\n        result: &mut [u8],\n    ) -> Result<(), Error> {\n        let size = a.len();\n\n        if b.len() != size {\n            return Err(Error::InvalidSize);\n        }\n\n        self.init_pka()?;\n        self.set_mode(mode);\n\n        // HAL uses byte-aligned bit sizes for arithmetic operations\n        let nb_bits = (size * 8) as u32;\n        self.write_ram_word(offsets::arithmetic::IN_NB_BITS, nb_bits);\n\n        self.write_operand(offsets::arithmetic::IN_OP1, a);\n        self.write_operand(offsets::arithmetic::IN_OP2, b);\n\n        if let Some(m) = modulus {\n            self.write_operand(offsets::arithmetic::IN_OP3_MOD, m);\n        }\n\n        self.start_and_wait()?;\n\n        let result_size = if mode == PkaMode::ArithmeticMul { size * 2 } else { size };\n        self.read_operand(offsets::arithmetic::OUT_RESULT, &mut result[..result_size]);\n\n        Ok(())\n    }\n\n    // ========================================================================\n    // Montgomery Operations\n    // ========================================================================\n\n    /// Compute Montgomery parameter R² mod n\n    ///\n    /// This parameter is required for fast modular exponentiation and other\n    /// Montgomery-form operations. The result should be stored and reused\n    /// for multiple operations with the same modulus.\n    ///\n    /// # Arguments\n    /// * `modulus` - The modulus n\n    /// * `result` - Output buffer for R² mod n (must be same size as modulus)\n    pub fn montgomery_param(&mut self, modulus: &[u8], result: &mut [u32]) -> Result<(), Error> {\n        let size = modulus.len();\n        let word_count = (size + 3) / 4;\n\n        if result.len() < word_count {\n            return Err(Error::InvalidSize);\n        }\n\n        self.init_pka()?;\n        self.set_mode(PkaMode::MontgomeryParam);\n\n        // Skip leading zero bytes to find the actual MSB (matching HAL behavior)\n        let mut bytes_to_skip = 0;\n        while bytes_to_skip < size && modulus[bytes_to_skip] == 0 {\n            bytes_to_skip += 1;\n        }\n        let new_size = size - bytes_to_skip;\n        let first_nonzero = if bytes_to_skip < size {\n            modulus[bytes_to_skip]\n        } else {\n            0\n        };\n\n        let nb_bits = Self::get_opt_bit_size(new_size, first_nonzero);\n        self.write_ram_word(offsets::montgomery_param::IN_MOD_NB_BITS, nb_bits);\n        self.write_operand(offsets::montgomery_param::IN_MODULUS, modulus);\n\n        self.start_and_wait()?;\n\n        // Read result as u32 words (native PKA format)\n        for i in 0..word_count {\n            result[i] = self.read_ram_word(offsets::montgomery_param::OUT_PARAMETER + i * 4);\n        }\n\n        Ok(())\n    }\n\n    /// Perform modular exponentiation with pre-computed Montgomery parameter (fast mode)\n    ///\n    /// This is faster than `modular_exp` when you have the Montgomery parameter\n    /// pre-computed (via `montgomery_param`).\n    ///\n    /// # Arguments\n    /// * `base` - Base value\n    /// * `exponent` - Exponent\n    /// * `modulus` - Modulus n\n    /// * `montgomery_param` - Pre-computed Montgomery parameter R² mod n\n    /// * `result` - Output buffer (must be same size as modulus)\n    pub fn modular_exp_fast(\n        &mut self,\n        base: &[u8],\n        exponent: &[u8],\n        modulus: &[u8],\n        montgomery_param: &[u32],\n        result: &mut [u8],\n    ) -> Result<(), Error> {\n        let mod_size = modulus.len();\n        let exp_size = exponent.len();\n\n        if base.len() > mod_size || result.len() < mod_size {\n            return Err(Error::InvalidSize);\n        }\n\n        self.init_pka()?;\n        self.set_mode(PkaMode::ModularExpFast);\n\n        let exp_nb_bits = (exp_size * 8) as u32;\n        let mod_nb_bits = (mod_size * 8) as u32;\n\n        self.write_ram_word(offsets::modular_exp::IN_EXP_NB_BITS, exp_nb_bits);\n        self.write_ram_word(offsets::modular_exp::IN_OP_NB_BITS, mod_nb_bits);\n\n        // Write Montgomery parameter (u32 words)\n        for (i, &word) in montgomery_param.iter().enumerate() {\n            self.write_ram_word(offsets::modular_exp::IN_MONTGOMERY_PARAM + i * 4, word);\n        }\n\n        self.write_operand(offsets::modular_exp::IN_EXPONENT_BASE, base);\n        self.write_operand(offsets::modular_exp::IN_EXPONENT, exponent);\n        self.write_operand(offsets::modular_exp::IN_MODULUS, modulus);\n\n        self.start_and_wait()?;\n\n        // Modular exponentiation (fast mode) doesn't write to OUT_ERROR\n        // Errors are indicated by SR flags which are checked in start_and_wait()\n\n        self.read_operand(offsets::modular_exp::OUT_RESULT, &mut result[..mod_size]);\n\n        Ok(())\n    }\n\n    /// Perform modular exponentiation with side-channel protection\n    ///\n    /// This mode provides constant-time execution to protect against\n    /// timing and power analysis attacks. It requires phi(n) as input.\n    ///\n    /// # Arguments\n    /// * `params` - Protected mode parameters including phi(n)\n    /// * `result` - Output buffer (must be same size as modulus)\n    pub fn modular_exp_protect(&mut self, params: &ModExpProtectParams, result: &mut [u8]) -> Result<(), Error> {\n        let mod_size = params.modulus.len();\n        let exp_size = params.exponent.len();\n\n        if params.base.len() > mod_size || params.phi.len() != mod_size || result.len() < mod_size {\n            return Err(Error::InvalidSize);\n        }\n\n        self.init_pka()?;\n        self.set_mode(PkaMode::ModularExpProtect);\n\n        // HAL uses byte-aligned bit sizes for modular exponentiation\n        let exp_nb_bits = (exp_size * 8) as u32;\n        let mod_nb_bits = (mod_size * 8) as u32;\n\n        self.write_ram_word(offsets::modular_exp_protect::IN_EXP_NB_BITS, exp_nb_bits);\n        self.write_ram_word(offsets::modular_exp_protect::IN_OP_NB_BITS, mod_nb_bits);\n\n        self.write_operand(offsets::modular_exp_protect::IN_EXPONENT_BASE, params.base);\n        self.write_operand(offsets::modular_exp_protect::IN_EXPONENT, params.exponent);\n        self.write_operand(offsets::modular_exp_protect::IN_MODULUS, params.modulus);\n        self.write_operand(offsets::modular_exp_protect::IN_PHI, params.phi);\n\n        self.start_and_wait()?;\n\n        // Modular exponentiation (protected mode) doesn't write to OUT_ERROR\n        // Errors are indicated by SR flags which are checked in start_and_wait()\n\n        self.read_operand(offsets::modular_exp_protect::OUT_RESULT, &mut result[..mod_size]);\n\n        Ok(())\n    }\n\n    /// Perform Montgomery multiplication: result = (a * b * R^-1) mod n\n    ///\n    /// This is useful for operations in Montgomery form.\n    pub fn montgomery_mul(&mut self, a: &[u8], b: &[u8], modulus: &[u8], result: &mut [u8]) -> Result<(), Error> {\n        self.arithmetic_op(PkaMode::MontgomeryMul, a, b, Some(modulus), result)\n    }\n\n    // ========================================================================\n    // Additional Arithmetic Operations\n    // ========================================================================\n\n    /// Compute arithmetic addition: result = a + b\n    ///\n    /// Note: Result may be one word larger than inputs if there's overflow.\n    pub fn arithmetic_add(&mut self, a: &[u8], b: &[u8], result: &mut [u8]) -> Result<(), Error> {\n        let size = a.len();\n\n        if b.len() != size || result.len() < size {\n            return Err(Error::InvalidSize);\n        }\n\n        self.init_pka()?;\n        self.set_mode(PkaMode::ArithmeticAdd);\n\n        let nb_bits = Self::get_opt_bit_size(size, a[0].max(b[0]));\n        self.write_ram_word(offsets::arithmetic::IN_NB_BITS, nb_bits);\n\n        self.write_operand(offsets::arithmetic::IN_OP1, a);\n        self.write_operand(offsets::arithmetic::IN_OP2, b);\n\n        self.start_and_wait()?;\n\n        self.read_operand(offsets::arithmetic::OUT_RESULT, &mut result[..size]);\n\n        self.disable_pka();\n        Ok(())\n    }\n\n    /// Compute arithmetic subtraction: result = a - b\n    ///\n    /// Note: If a < b, the result will be the two's complement.\n    pub fn arithmetic_sub(&mut self, a: &[u8], b: &[u8], result: &mut [u8]) -> Result<(), Error> {\n        let size = a.len();\n\n        if b.len() != size || result.len() < size {\n            return Err(Error::InvalidSize);\n        }\n\n        self.init_pka()?;\n        self.set_mode(PkaMode::ArithmeticSub);\n\n        let nb_bits = Self::get_opt_bit_size(size, a[0].max(b[0]));\n        self.write_ram_word(offsets::arithmetic::IN_NB_BITS, nb_bits);\n\n        self.write_operand(offsets::arithmetic::IN_OP1, a);\n        self.write_operand(offsets::arithmetic::IN_OP2, b);\n\n        self.start_and_wait()?;\n\n        self.read_operand(offsets::arithmetic::OUT_RESULT, &mut result[..size]);\n\n        Ok(())\n    }\n\n    /// Compare two big integers\n    ///\n    /// Returns the comparison result indicating whether a < b, a == b, or a > b.\n    pub fn comparison(&mut self, a: &[u8], b: &[u8]) -> Result<ComparisonResult, Error> {\n        let size = a.len();\n\n        if b.len() != size {\n            return Err(Error::InvalidSize);\n        }\n\n        self.init_pka()?;\n        self.set_mode(PkaMode::Comparison);\n\n        // HAL uses byte-aligned bit sizes for comparison\n        let nb_bits = (size * 8) as u32;\n        self.write_ram_word(offsets::arithmetic::IN_NB_BITS, nb_bits);\n\n        self.write_operand(offsets::arithmetic::IN_OP1, a);\n        self.write_operand(offsets::arithmetic::IN_OP2, b);\n\n        self.start_and_wait()?;\n\n        let result = self.read_ram_word(offsets::arithmetic::OUT_RESULT);\n\n        // PKA comparison result encoding (from STM32WBA reference manual)\n        match result {\n            0xED2C => Ok(ComparisonResult::Equal),   // A == B\n            0x7AF8 => Ok(ComparisonResult::Greater), // A > B\n            0x916A => Ok(ComparisonResult::Less),    // A < B\n            _ => Err(Error::OperationError),\n        }\n    }\n\n    /// Compute modular reduction: result = a mod n\n    pub fn modular_red(&mut self, a: &[u8], modulus: &[u8], result: &mut [u8]) -> Result<(), Error> {\n        let op_size = a.len();\n        let mod_size = modulus.len();\n\n        if result.len() < mod_size {\n            return Err(Error::InvalidSize);\n        }\n\n        self.init_pka()?;\n        self.set_mode(PkaMode::ModularRed);\n\n        let op_nb_bits = (op_size * 8) as u32;\n        let mod_nb_bits = (mod_size * 8) as u32;\n\n        self.write_ram_word(offsets::modular_red::IN_OP_LENGTH, op_nb_bits);\n        self.write_ram_word(offsets::modular_red::IN_MOD_LENGTH, mod_nb_bits);\n\n        self.write_operand(offsets::modular_red::IN_OPERAND, a);\n        self.write_operand(offsets::modular_red::IN_MODULUS, modulus);\n\n        self.start_and_wait()?;\n\n        self.read_operand(offsets::modular_red::OUT_RESULT, &mut result[..mod_size]);\n\n        Ok(())\n    }\n\n    // ========================================================================\n    // Advanced ECC Operations\n    // ========================================================================\n\n    /// ECC complete point addition in projective coordinates: R = P + Q\n    ///\n    /// This operation adds two points in projective coordinates and handles\n    /// all edge cases (point at infinity, point doubling, etc.).\n    ///\n    /// # Arguments\n    /// * `curve` - Curve parameters\n    /// * `p` - First point in projective coordinates\n    /// * `q` - Second point in projective coordinates\n    /// * `result` - Output point in projective coordinates\n    pub fn ecc_complete_add(\n        &mut self,\n        curve: &EcdsaCurveParams,\n        p: &EccProjectivePoint,\n        q: &EccProjectivePoint,\n        result: &mut EccProjectivePoint,\n    ) -> Result<(), Error> {\n        let modulus_size = curve.p_modulus.len();\n\n        if p.size != modulus_size || q.size != modulus_size || result.size != modulus_size {\n            return Err(Error::InvalidSize);\n        }\n\n        self.init_pka()?;\n\n        let mod_nb_bits = Self::get_opt_bit_size(modulus_size, curve.p_modulus[0]);\n\n        self.write_ram_word(offsets::ecc_complete_add::IN_MOD_NB_BITS, mod_nb_bits);\n        self.write_ram_word(offsets::ecc_complete_add::IN_A_COEFF_SIGN, curve.a_coefficient_sign);\n\n        self.write_operand(offsets::ecc_complete_add::IN_A_COEFF, curve.a_coefficient);\n        self.write_operand(offsets::ecc_complete_add::IN_MOD_P, curve.p_modulus);\n\n        // Write point P\n        self.write_operand(offsets::ecc_complete_add::IN_POINT1_X, &p.x[..modulus_size]);\n        self.write_operand(offsets::ecc_complete_add::IN_POINT1_Y, &p.y[..modulus_size]);\n        self.write_operand(offsets::ecc_complete_add::IN_POINT1_Z, &p.z[..modulus_size]);\n\n        // Write point Q\n        self.write_operand(offsets::ecc_complete_add::IN_POINT2_X, &q.x[..modulus_size]);\n        self.write_operand(offsets::ecc_complete_add::IN_POINT2_Y, &q.y[..modulus_size]);\n        self.write_operand(offsets::ecc_complete_add::IN_POINT2_Z, &q.z[..modulus_size]);\n\n        self.set_mode(PkaMode::EccCompleteAdd);\n        self.start_and_wait()?;\n\n        // Read result\n        self.read_operand(offsets::ecc_complete_add::OUT_RESULT_X, &mut result.x[..modulus_size]);\n        self.read_operand(offsets::ecc_complete_add::OUT_RESULT_Y, &mut result.y[..modulus_size]);\n        self.read_operand(offsets::ecc_complete_add::OUT_RESULT_Z, &mut result.z[..modulus_size]);\n\n        self.disable_pka();\n        Ok(())\n    }\n\n    /// ECC double base ladder: result = k*P + m*Q (side-channel resistant)\n    ///\n    /// This operation computes the linear combination of two points using the\n    /// double base ladder algorithm, which provides side-channel resistance.\n    ///\n    /// # Arguments\n    /// * `curve` - Curve parameters\n    /// * `k` - Scalar for point P\n    /// * `p` - First point in projective coordinates\n    /// * `m` - Scalar for point Q\n    /// * `q` - Second point in projective coordinates\n    /// * `result` - Output point in affine coordinates\n    pub fn double_base_ladder(\n        &mut self,\n        curve: &EcdsaCurveParams,\n        k: &[u8],\n        p: &EccProjectivePoint,\n        m: &[u8],\n        q: &EccProjectivePoint,\n        result: &mut EccPoint,\n    ) -> Result<(), Error> {\n        let modulus_size = curve.p_modulus.len();\n        let order_size = curve.order.len();\n\n        if k.len() != order_size\n            || m.len() != order_size\n            || p.size != modulus_size\n            || q.size != modulus_size\n            || result.size != modulus_size\n        {\n            return Err(Error::InvalidSize);\n        }\n\n        self.init_pka()?;\n\n        let order_nb_bits = Self::get_opt_bit_size(order_size, curve.order[0]);\n        let mod_nb_bits = Self::get_opt_bit_size(modulus_size, curve.p_modulus[0]);\n\n        self.write_ram_word(offsets::double_base_ladder::IN_PRIME_ORDER_NB_BITS, order_nb_bits);\n        self.write_ram_word(offsets::double_base_ladder::IN_MOD_NB_BITS, mod_nb_bits);\n        self.write_ram_word(offsets::double_base_ladder::IN_A_COEFF_SIGN, curve.a_coefficient_sign);\n\n        self.write_operand(offsets::double_base_ladder::IN_A_COEFF, curve.a_coefficient);\n        self.write_operand(offsets::double_base_ladder::IN_MOD_P, curve.p_modulus);\n\n        // Write scalars\n        self.write_operand(offsets::double_base_ladder::IN_K, k);\n        self.write_operand(offsets::double_base_ladder::IN_M, m);\n\n        // Write point P\n        self.write_operand(offsets::double_base_ladder::IN_POINT1_X, &p.x[..modulus_size]);\n        self.write_operand(offsets::double_base_ladder::IN_POINT1_Y, &p.y[..modulus_size]);\n        self.write_operand(offsets::double_base_ladder::IN_POINT1_Z, &p.z[..modulus_size]);\n\n        // Write point Q\n        self.write_operand(offsets::double_base_ladder::IN_POINT2_X, &q.x[..modulus_size]);\n        self.write_operand(offsets::double_base_ladder::IN_POINT2_Y, &q.y[..modulus_size]);\n        self.write_operand(offsets::double_base_ladder::IN_POINT2_Z, &q.z[..modulus_size]);\n\n        self.set_mode(PkaMode::DoubleBaseLadder);\n        self.start_and_wait()?;\n\n        // Check for errors\n        let status = self.read_ram_word(offsets::double_base_ladder::OUT_ERROR);\n        if status != 0xD60D {\n            self.disable_pka();\n            return Err(Error::OperationError);\n        }\n\n        // Read result (affine coordinates)\n        self.read_operand(offsets::double_base_ladder::OUT_RESULT_X, &mut result.x[..modulus_size]);\n        self.read_operand(offsets::double_base_ladder::OUT_RESULT_Y, &mut result.y[..modulus_size]);\n\n        self.disable_pka();\n        Ok(())\n    }\n\n    /// Convert a point from projective to affine coordinates\n    ///\n    /// # Arguments\n    /// * `modulus` - The curve modulus p\n    /// * `montgomery_param` - Pre-computed Montgomery parameter R² mod p\n    /// * `point` - Point in projective coordinates\n    /// * `result` - Output point in affine coordinates\n    pub fn projective_to_affine(\n        &mut self,\n        modulus: &[u8],\n        montgomery_param: &[u32],\n        point: &EccProjectivePoint,\n        result: &mut EccPoint,\n    ) -> Result<(), Error> {\n        let modulus_size = modulus.len();\n\n        if point.size != modulus_size || result.size != modulus_size {\n            return Err(Error::InvalidSize);\n        }\n\n        self.init_pka()?;\n\n        let mod_nb_bits = Self::get_opt_bit_size(modulus_size, modulus[0]);\n\n        self.write_ram_word(offsets::projective_to_affine::IN_MOD_NB_BITS, mod_nb_bits);\n        self.write_operand(offsets::projective_to_affine::IN_MOD_P, modulus);\n\n        // Write Montgomery parameter\n        for (i, &word) in montgomery_param.iter().enumerate() {\n            self.write_ram_word(offsets::projective_to_affine::IN_MONTGOMERY_PARAM + i * 4, word);\n        }\n\n        // Write projective point\n        self.write_operand(offsets::projective_to_affine::IN_POINT_X, &point.x[..modulus_size]);\n        self.write_operand(offsets::projective_to_affine::IN_POINT_Y, &point.y[..modulus_size]);\n        self.write_operand(offsets::projective_to_affine::IN_POINT_Z, &point.z[..modulus_size]);\n\n        self.set_mode(PkaMode::EccProjectiveToAffine);\n        self.start_and_wait()?;\n\n        // Check for errors\n        let status = self.read_ram_word(offsets::projective_to_affine::OUT_ERROR);\n        if status != 0xD60D {\n            self.disable_pka();\n            return Err(Error::OperationError);\n        }\n\n        // Read affine result\n        self.read_operand(\n            offsets::projective_to_affine::OUT_RESULT_X,\n            &mut result.x[..modulus_size],\n        );\n        self.read_operand(\n            offsets::projective_to_affine::OUT_RESULT_Y,\n            &mut result.y[..modulus_size],\n        );\n\n        self.disable_pka();\n        Ok(())\n    }\n\n    // ========================================================================\n    // Internal Helper Functions\n    // ========================================================================\n\n    fn init_pka(&mut self) -> Result<(), Error> {\n        let p = T::regs();\n        let sr_ptr = p.sr().as_ptr() as *const u32;\n\n        // Check if PKA is already enabled and initialized\n        let sr_raw = unsafe { sr_ptr.read_volatile() };\n        let cr_raw = p.cr().read().0;\n\n        // If already enabled and INITOK is set, skip re-initialization\n        if (cr_raw & 0x01) != 0 && (sr_raw & 0x01) != 0 {\n            return Ok(());\n        }\n\n        // If not enabled, enable it\n        if (cr_raw & 0x01) == 0 {\n            #[cfg(rng_wba6)]\n            {\n                // On STM32WBA6, PKA requires RNG to be running for RAM initialization\n                use crate::pac::rcc::vals::Rngsel;\n\n                let rcc = crate::pac::RCC;\n                let was_rng_enabled = rcc.ahb2enr().read().rngen();\n\n                if !was_rng_enabled {\n                    // Configure RNG clock source to HSI (required for PKA)\n                    rcc.ccipr2().modify(|w| w.set_rngsel(Rngsel::HSI));\n\n                    // Enable RNG clock\n                    rcc.ahb2enr().modify(|w| w.set_rngen(true));\n\n                    // Enable RNG peripheral itself\n                    let rng = crate::pac::RNG;\n                    rng.cr().modify(|w| w.set_rngen(true));\n\n                    // Small delay for RNG to start\n                    cortex_m::asm::delay(10000); // ~100µs at 96MHz\n                }\n            }\n\n            // Enable PKA and wait for RAM erase to complete\n            let mut timeout: u32 = 0;\n            loop {\n                p.cr().write(|w| w.set_en(true));\n\n                // Check if EN bit is set\n                if p.cr().read().en() {\n                    break;\n                }\n\n                timeout += 1;\n                if timeout > Self::RAM_ERASE_TIMEOUT {\n                    return Err(Error::Timeout);\n                }\n            }\n        }\n\n        // Wait for INITOK (bit 0 of SR) - indicates RAM initialization complete\n        let mut timeout: u32 = 0;\n        loop {\n            let sr_raw = unsafe { sr_ptr.read_volatile() };\n            if sr_raw & 0x01 != 0 {\n                break;\n            }\n            timeout += 1;\n            if timeout > 1_000_000 {\n                return Err(Error::Timeout);\n            }\n        }\n\n        // Clear any pending flags\n        p.clrfr().write(|w| {\n            w.set_procendfc(true);\n            w.set_ramerrfc(true);\n            w.set_addrerrfc(true);\n            w.set_operrfc(true);\n        });\n\n        Ok(())\n    }\n\n    fn set_mode(&mut self, mode: PkaMode) {\n        let p = T::regs();\n        p.cr().modify(|w| {\n            w.set_mode(mode as u8);\n            w.set_procendie(false);\n            w.set_ramerrie(false);\n            w.set_addrerrie(false);\n            w.set_operrie(false);\n        });\n    }\n\n    fn start_and_wait(&mut self) -> Result<(), Error> {\n        let p = T::regs();\n\n        p.cr().modify(|w| w.set_start(true));\n\n        let mut timeout: u32 = 0;\n        loop {\n            let sr = p.sr().read();\n\n            if sr.ramerrf() {\n                p.clrfr().write(|w| w.set_ramerrfc(true));\n                return Err(Error::RamError);\n            }\n            if sr.addrerrf() {\n                p.clrfr().write(|w| w.set_addrerrfc(true));\n                return Err(Error::AddressError);\n            }\n            if sr.operrf() {\n                p.clrfr().write(|w| w.set_operrfc(true));\n                return Err(Error::OperationError);\n            }\n            if sr.procendf() {\n                p.clrfr().write(|w| w.set_procendfc(true));\n                break;\n            }\n\n            timeout += 1;\n            if timeout > 10_000_000 {\n                return Err(Error::Timeout);\n            }\n        }\n\n        Ok(())\n    }\n\n    fn disable_pka(&mut self) {\n        T::regs().cr().modify(|w| w.set_en(false));\n    }\n\n    fn get_opt_bit_size(byte_count: usize, msb: u8) -> u32 {\n        let position = if msb == 0 { 0 } else { 8 - msb.leading_zeros() };\n        ((byte_count as u32 - 1) * 8) + position\n    }\n\n    fn write_operand(&mut self, offset: usize, data: &[u8]) {\n        let n = data.len();\n        let word_count = (n + 3) / 4;\n\n        for index in 0..(n / 4) {\n            let i = n - (index * 4);\n            let word = (data[i - 1] as u32)\n                | ((data[i - 2] as u32) << 8)\n                | ((data[i - 3] as u32) << 16)\n                | ((data[i - 4] as u32) << 24);\n            self.write_ram_word(offset + index * 4, word);\n        }\n\n        let remainder = n % 4;\n        if remainder > 0 {\n            let index = n / 4;\n            let word = match remainder {\n                1 => data[0] as u32,\n                2 => (data[1] as u32) | ((data[0] as u32) << 8),\n                3 => (data[2] as u32) | ((data[1] as u32) << 8) | ((data[0] as u32) << 16),\n                _ => 0,\n            };\n            self.write_ram_word(offset + index * 4, word);\n        }\n\n        // Terminate with two zero words (matches ST-HAL __PKA_RAM_PARAM_END macro)\n        self.write_ram_word(offset + word_count * 4, 0);\n        self.write_ram_word(offset + (word_count + 1) * 4, 0);\n    }\n\n    fn read_operand(&self, offset: usize, data: &mut [u8]) {\n        let n = data.len();\n\n        for index in 0..(n / 4) {\n            let word = self.read_ram_word(offset + index * 4);\n            let i = n - (index * 4);\n            data[i - 1] = (word & 0xFF) as u8;\n            data[i - 2] = ((word >> 8) & 0xFF) as u8;\n            data[i - 3] = ((word >> 16) & 0xFF) as u8;\n            data[i - 4] = ((word >> 24) & 0xFF) as u8;\n        }\n\n        let remainder = n % 4;\n        if remainder > 0 {\n            let index = n / 4;\n            let word = self.read_ram_word(offset + index * 4);\n            match remainder {\n                1 => data[0] = (word & 0xFF) as u8,\n                2 => {\n                    data[1] = (word & 0xFF) as u8;\n                    data[0] = ((word >> 8) & 0xFF) as u8;\n                }\n                3 => {\n                    data[2] = (word & 0xFF) as u8;\n                    data[1] = ((word >> 8) & 0xFF) as u8;\n                    data[0] = ((word >> 16) & 0xFF) as u8;\n                }\n                _ => {}\n            }\n        }\n    }\n\n    fn write_ram_word(&mut self, offset: usize, value: u32) {\n        let p = T::regs();\n        let word_index = offset / 4;\n        unsafe {\n            let ram_ptr = p.ram(word_index).as_ptr() as *mut u32;\n            ram_ptr.write_volatile(value);\n        }\n    }\n\n    fn read_ram_word(&self, offset: usize) -> u32 {\n        let p = T::regs();\n        let word_index = offset / 4;\n        unsafe {\n            let ram_ptr = p.ram(word_index).as_ptr() as *const u32;\n            ram_ptr.read_volatile()\n        }\n    }\n}\n\n// ============================================================================\n// Instance Traits\n// ============================================================================\n\ntrait SealedInstance {\n    fn regs() -> pac::pka::Pka;\n}\n\n/// PKA instance trait.\n#[allow(private_bounds)]\npub trait Instance: SealedInstance + PeripheralType + crate::rcc::RccPeripheral + 'static + Send {\n    /// Interrupt for this PKA instance.\n    type Interrupt: interrupt::typelevel::Interrupt;\n}\n\nforeach_interrupt!(\n    ($inst:ident, pka, PKA, GLOBAL, $irq:ident) => {\n        impl Instance for peripherals::$inst {\n            type Interrupt = crate::interrupt::typelevel::$irq;\n        }\n\n        impl SealedInstance for peripherals::$inst {\n            fn regs() -> crate::pac::pka::Pka {\n                crate::pac::$inst\n            }\n        }\n    };\n);\n"
  },
  {
    "path": "embassy-stm32/src/qspi/enums.rs",
    "content": "//! Enums used in QSPI configuration.\n\n#[allow(dead_code)]\n#[derive(Copy, Clone)]\npub(crate) enum QspiMode {\n    IndirectWrite,\n    IndirectRead,\n    AutoPolling,\n    MemoryMapped,\n}\n\nimpl From<QspiMode> for u8 {\n    fn from(val: QspiMode) -> Self {\n        match val {\n            QspiMode::IndirectWrite => 0b00,\n            QspiMode::IndirectRead => 0b01,\n            QspiMode::AutoPolling => 0b10,\n            QspiMode::MemoryMapped => 0b11,\n        }\n    }\n}\n\n/// QSPI lane width\n#[allow(dead_code)]\n#[derive(Copy, Clone)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub enum QspiWidth {\n    /// None\n    NONE,\n    /// Single lane\n    SING,\n    /// Dual lanes\n    DUAL,\n    /// Quad lanes\n    QUAD,\n}\n\nimpl From<QspiWidth> for u8 {\n    fn from(val: QspiWidth) -> Self {\n        match val {\n            QspiWidth::NONE => 0b00,\n            QspiWidth::SING => 0b01,\n            QspiWidth::DUAL => 0b10,\n            QspiWidth::QUAD => 0b11,\n        }\n    }\n}\n\n/// Flash bank selection\n#[allow(dead_code)]\n#[derive(Copy, Clone)]\npub enum FlashSelection {\n    /// Bank 1\n    Flash1,\n    /// Bank 2\n    Flash2,\n}\n\nimpl From<FlashSelection> for bool {\n    fn from(val: FlashSelection) -> Self {\n        match val {\n            FlashSelection::Flash1 => false,\n            FlashSelection::Flash2 => true,\n        }\n    }\n}\n\n/// QSPI memory size.\n#[allow(missing_docs)]\n#[derive(Copy, Clone)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub enum MemorySize {\n    _1KiB,\n    _2KiB,\n    _4KiB,\n    _8KiB,\n    _16KiB,\n    _32KiB,\n    _64KiB,\n    _128KiB,\n    _256KiB,\n    _512KiB,\n    _1MiB,\n    _2MiB,\n    _4MiB,\n    _8MiB,\n    _16MiB,\n    _32MiB,\n    _64MiB,\n    _128MiB,\n    _256MiB,\n    _512MiB,\n    _1GiB,\n    _2GiB,\n    _4GiB,\n    Other(u8),\n}\n\nimpl From<MemorySize> for u8 {\n    fn from(val: MemorySize) -> Self {\n        match val {\n            MemorySize::_1KiB => 9,\n            MemorySize::_2KiB => 10,\n            MemorySize::_4KiB => 11,\n            MemorySize::_8KiB => 12,\n            MemorySize::_16KiB => 13,\n            MemorySize::_32KiB => 14,\n            MemorySize::_64KiB => 15,\n            MemorySize::_128KiB => 16,\n            MemorySize::_256KiB => 17,\n            MemorySize::_512KiB => 18,\n            MemorySize::_1MiB => 19,\n            MemorySize::_2MiB => 20,\n            MemorySize::_4MiB => 21,\n            MemorySize::_8MiB => 22,\n            MemorySize::_16MiB => 23,\n            MemorySize::_32MiB => 24,\n            MemorySize::_64MiB => 25,\n            MemorySize::_128MiB => 26,\n            MemorySize::_256MiB => 27,\n            MemorySize::_512MiB => 28,\n            MemorySize::_1GiB => 29,\n            MemorySize::_2GiB => 30,\n            MemorySize::_4GiB => 31,\n            MemorySize::Other(val) => val,\n        }\n    }\n}\n\n/// QSPI Address size\n#[derive(Copy, Clone)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub enum AddressSize {\n    /// 8-bit address\n    _8Bit,\n    /// 16-bit address\n    _16Bit,\n    /// 24-bit address\n    _24bit,\n    /// 32-bit address\n    _32bit,\n}\n\nimpl From<AddressSize> for u8 {\n    fn from(val: AddressSize) -> Self {\n        match val {\n            AddressSize::_8Bit => 0b00,\n            AddressSize::_16Bit => 0b01,\n            AddressSize::_24bit => 0b10,\n            AddressSize::_32bit => 0b11,\n        }\n    }\n}\n\n/// Time the Chip Select line stays high.\n#[allow(missing_docs)]\n#[derive(Copy, Clone)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub enum ChipSelectHighTime {\n    _1Cycle,\n    _2Cycle,\n    _3Cycle,\n    _4Cycle,\n    _5Cycle,\n    _6Cycle,\n    _7Cycle,\n    _8Cycle,\n}\n\nimpl From<ChipSelectHighTime> for u8 {\n    fn from(val: ChipSelectHighTime) -> Self {\n        match val {\n            ChipSelectHighTime::_1Cycle => 0,\n            ChipSelectHighTime::_2Cycle => 1,\n            ChipSelectHighTime::_3Cycle => 2,\n            ChipSelectHighTime::_4Cycle => 3,\n            ChipSelectHighTime::_5Cycle => 4,\n            ChipSelectHighTime::_6Cycle => 5,\n            ChipSelectHighTime::_7Cycle => 6,\n            ChipSelectHighTime::_8Cycle => 7,\n        }\n    }\n}\n\n/// FIFO threshold.\n#[allow(missing_docs)]\n#[derive(Copy, Clone)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub enum FIFOThresholdLevel {\n    _1Bytes,\n    _2Bytes,\n    _3Bytes,\n    _4Bytes,\n    _5Bytes,\n    _6Bytes,\n    _7Bytes,\n    _8Bytes,\n    _9Bytes,\n    _10Bytes,\n    _11Bytes,\n    _12Bytes,\n    _13Bytes,\n    _14Bytes,\n    _15Bytes,\n    _16Bytes,\n    _17Bytes,\n    _18Bytes,\n    _19Bytes,\n    _20Bytes,\n    _21Bytes,\n    _22Bytes,\n    _23Bytes,\n    _24Bytes,\n    _25Bytes,\n    _26Bytes,\n    _27Bytes,\n    _28Bytes,\n    _29Bytes,\n    _30Bytes,\n    _31Bytes,\n    _32Bytes,\n}\n\nimpl From<FIFOThresholdLevel> for u8 {\n    fn from(val: FIFOThresholdLevel) -> Self {\n        match val {\n            FIFOThresholdLevel::_1Bytes => 0,\n            FIFOThresholdLevel::_2Bytes => 1,\n            FIFOThresholdLevel::_3Bytes => 2,\n            FIFOThresholdLevel::_4Bytes => 3,\n            FIFOThresholdLevel::_5Bytes => 4,\n            FIFOThresholdLevel::_6Bytes => 5,\n            FIFOThresholdLevel::_7Bytes => 6,\n            FIFOThresholdLevel::_8Bytes => 7,\n            FIFOThresholdLevel::_9Bytes => 8,\n            FIFOThresholdLevel::_10Bytes => 9,\n            FIFOThresholdLevel::_11Bytes => 10,\n            FIFOThresholdLevel::_12Bytes => 11,\n            FIFOThresholdLevel::_13Bytes => 12,\n            FIFOThresholdLevel::_14Bytes => 13,\n            FIFOThresholdLevel::_15Bytes => 14,\n            FIFOThresholdLevel::_16Bytes => 15,\n            FIFOThresholdLevel::_17Bytes => 16,\n            FIFOThresholdLevel::_18Bytes => 17,\n            FIFOThresholdLevel::_19Bytes => 18,\n            FIFOThresholdLevel::_20Bytes => 19,\n            FIFOThresholdLevel::_21Bytes => 20,\n            FIFOThresholdLevel::_22Bytes => 21,\n            FIFOThresholdLevel::_23Bytes => 22,\n            FIFOThresholdLevel::_24Bytes => 23,\n            FIFOThresholdLevel::_25Bytes => 24,\n            FIFOThresholdLevel::_26Bytes => 25,\n            FIFOThresholdLevel::_27Bytes => 26,\n            FIFOThresholdLevel::_28Bytes => 27,\n            FIFOThresholdLevel::_29Bytes => 28,\n            FIFOThresholdLevel::_30Bytes => 29,\n            FIFOThresholdLevel::_31Bytes => 30,\n            FIFOThresholdLevel::_32Bytes => 31,\n        }\n    }\n}\n\n/// Dummy cycle count\n#[allow(missing_docs)]\n#[derive(Copy, Clone)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub enum DummyCycles {\n    _0,\n    _1,\n    _2,\n    _3,\n    _4,\n    _5,\n    _6,\n    _7,\n    _8,\n    _9,\n    _10,\n    _11,\n    _12,\n    _13,\n    _14,\n    _15,\n    _16,\n    _17,\n    _18,\n    _19,\n    _20,\n    _21,\n    _22,\n    _23,\n    _24,\n    _25,\n    _26,\n    _27,\n    _28,\n    _29,\n    _30,\n    _31,\n}\n\nimpl From<DummyCycles> for u8 {\n    fn from(val: DummyCycles) -> Self {\n        match val {\n            DummyCycles::_0 => 0,\n            DummyCycles::_1 => 1,\n            DummyCycles::_2 => 2,\n            DummyCycles::_3 => 3,\n            DummyCycles::_4 => 4,\n            DummyCycles::_5 => 5,\n            DummyCycles::_6 => 6,\n            DummyCycles::_7 => 7,\n            DummyCycles::_8 => 8,\n            DummyCycles::_9 => 9,\n            DummyCycles::_10 => 10,\n            DummyCycles::_11 => 11,\n            DummyCycles::_12 => 12,\n            DummyCycles::_13 => 13,\n            DummyCycles::_14 => 14,\n            DummyCycles::_15 => 15,\n            DummyCycles::_16 => 16,\n            DummyCycles::_17 => 17,\n            DummyCycles::_18 => 18,\n            DummyCycles::_19 => 19,\n            DummyCycles::_20 => 20,\n            DummyCycles::_21 => 21,\n            DummyCycles::_22 => 22,\n            DummyCycles::_23 => 23,\n            DummyCycles::_24 => 24,\n            DummyCycles::_25 => 25,\n            DummyCycles::_26 => 26,\n            DummyCycles::_27 => 27,\n            DummyCycles::_28 => 28,\n            DummyCycles::_29 => 29,\n            DummyCycles::_30 => 30,\n            DummyCycles::_31 => 31,\n        }\n    }\n}\n\n#[allow(missing_docs)]\n#[derive(Copy, Clone)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub enum SampleShifting {\n    None,\n    HalfCycle,\n}\n\nimpl From<SampleShifting> for bool {\n    fn from(value: SampleShifting) -> Self {\n        match value {\n            SampleShifting::None => false,\n            SampleShifting::HalfCycle => true,\n        }\n    }\n}\n"
  },
  {
    "path": "embassy-stm32/src/qspi/mod.rs",
    "content": "//! Quad Serial Peripheral Interface (QSPI)\n\n#![macro_use]\n\npub mod enums;\n\nuse core::marker::PhantomData;\n\nuse embassy_hal_internal::PeripheralType;\nuse embassy_sync::waitqueue::AtomicWaker;\nuse enums::*;\n\nuse crate::dma::ChannelAndRequest;\nuse crate::gpio::{AfType, Flex, OutputType, Pull, Speed};\nuse crate::interrupt::typelevel::{Binding, Interrupt};\nuse crate::mode::{Async, Blocking, Mode as PeriMode};\nuse crate::pac::quadspi::Quadspi as Regs;\nuse crate::rcc::{self, RccPeripheral};\nuse crate::{Peri, interrupt};\n\n/// QSPI transfer configuration.\n#[derive(Clone, Copy)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub struct TransferConfig {\n    /// Instruction width (IMODE)\n    pub iwidth: QspiWidth,\n    /// Address width (ADMODE)\n    pub awidth: QspiWidth,\n    /// Data width (DMODE)\n    pub dwidth: QspiWidth,\n    /// Instruction Id\n    pub instruction: u8,\n    /// Flash memory address\n    pub address: Option<u32>,\n    /// Number of dummy cycles (DCYC)\n    pub dummy: DummyCycles,\n}\n\nimpl Default for TransferConfig {\n    fn default() -> Self {\n        Self {\n            iwidth: QspiWidth::NONE,\n            awidth: QspiWidth::NONE,\n            dwidth: QspiWidth::NONE,\n            instruction: 0,\n            address: None,\n            dummy: DummyCycles::_0,\n        }\n    }\n}\n\n/// QSPI driver configuration.\n#[derive(Clone, Copy)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\n#[non_exhaustive]\npub struct Config {\n    /// Flash memory size representend as 2^[0-32], as reasonable minimum 1KiB(9) was chosen.\n    /// If you need other value the whose predefined use `Other` variant.\n    pub memory_size: MemorySize,\n    /// Address size (8/16/24/32-bit)\n    pub address_size: AddressSize,\n    /// Scalar factor for generating CLK [0-255]\n    pub prescaler: u8,\n    /// Number of bytes to trigger FIFO threshold flag.\n    pub fifo_threshold: FIFOThresholdLevel,\n    /// Minimum number of cycles that chip select must be high between issued commands\n    pub cs_high_time: ChipSelectHighTime,\n    /// Shift sampling point of input data (none, or half-cycle)\n    pub sample_shifting: SampleShifting,\n    /// GPIO Speed\n    pub gpio_speed: Speed,\n    /// Dual flash mode\n    pub dual_flash: bool,\n}\n\nimpl Default for Config {\n    fn default() -> Self {\n        Self {\n            memory_size: MemorySize::Other(0),\n            address_size: AddressSize::_24bit,\n            prescaler: 128,\n            fifo_threshold: FIFOThresholdLevel::_17Bytes,\n            cs_high_time: ChipSelectHighTime::_5Cycle,\n            sample_shifting: SampleShifting::None,\n            gpio_speed: Speed::VeryHigh,\n            dual_flash: false,\n        }\n    }\n}\n\n/// QSPI driver.\n#[allow(dead_code)]\npub struct Qspi<'d, T: Instance, M: PeriMode> {\n    _peri: Peri<'d, T>,\n    sck: Option<Flex<'d>>,\n    bk1d0: Option<Flex<'d>>,\n    bk1d1: Option<Flex<'d>>,\n    bk1d2: Option<Flex<'d>>,\n    bk1d3: Option<Flex<'d>>,\n    bk2d0: Option<Flex<'d>>,\n    bk2d1: Option<Flex<'d>>,\n    bk2d2: Option<Flex<'d>>,\n    bk2d3: Option<Flex<'d>>,\n    bk1nss: Option<Flex<'d>>,\n    bk2nss: Option<Flex<'d>>,\n    dma: Option<ChannelAndRequest<'d>>,\n    _phantom: PhantomData<M>,\n    config: Config,\n}\n\nimpl<'d, T: Instance, M: PeriMode> Qspi<'d, T, M> {\n    fn new_inner(\n        peri: Peri<'d, T>,\n        bk1d0: Option<Flex<'d>>,\n        bk1d1: Option<Flex<'d>>,\n        bk1d2: Option<Flex<'d>>,\n        bk1d3: Option<Flex<'d>>,\n        bk2d0: Option<Flex<'d>>,\n        bk2d1: Option<Flex<'d>>,\n        bk2d2: Option<Flex<'d>>,\n        bk2d3: Option<Flex<'d>>,\n        sck: Option<Flex<'d>>,\n        bk1nss: Option<Flex<'d>>,\n        bk2nss: Option<Flex<'d>>,\n        dma: Option<ChannelAndRequest<'d>>,\n        config: Config,\n        fsel: FlashSelection,\n    ) -> Self {\n        rcc::enable_and_reset_without_stop::<T>();\n\n        while T::REGS.sr().read().busy() {}\n\n        #[cfg(stm32h7)]\n        {\n            use stm32_metapac::quadspi::regs::Cr;\n            // Apply precautionary steps according to the errata...\n            T::REGS.cr().write_value(Cr(0));\n            while T::REGS.sr().read().busy() {}\n            T::REGS.cr().write_value(Cr(0xFF000001));\n            T::REGS.ccr().write(|w| w.set_frcm(true));\n            T::REGS.ccr().write(|w| w.set_frcm(true));\n            T::REGS.cr().write_value(Cr(0));\n            while T::REGS.sr().read().busy() {}\n        }\n\n        T::REGS.cr().modify(|w| {\n            w.set_en(true);\n            //w.set_tcen(false);\n            w.set_sshift(config.sample_shifting.into());\n            w.set_fthres(config.fifo_threshold.into());\n            w.set_prescaler(config.prescaler);\n            w.set_fsel(fsel.into());\n            w.set_dfm(config.dual_flash.into());\n        });\n        T::REGS.dcr().modify(|w| {\n            w.set_fsize(config.memory_size.into());\n            w.set_csht(config.cs_high_time.into());\n            w.set_ckmode(true);\n        });\n\n        Self {\n            _peri: peri,\n            sck,\n            bk1d0,\n            bk1d1,\n            bk1d2,\n            bk1d3,\n            bk2d0,\n            bk2d1,\n            bk2d2,\n            bk2d3,\n            bk1nss,\n            bk2nss,\n            dma,\n            _phantom: PhantomData,\n            config,\n        }\n    }\n\n    /// Do a QSPI command.\n    pub fn blocking_command(&mut self, transaction: TransferConfig) {\n        #[cfg(not(stm32h7))]\n        T::REGS.cr().modify(|v| v.set_dmaen(false));\n        self.setup_transaction(QspiMode::IndirectWrite, &transaction, None);\n\n        while !T::REGS.sr().read().tcf() {}\n        T::REGS.fcr().modify(|v| v.set_ctcf(true));\n    }\n\n    /// Blocking read data.\n    pub fn blocking_read(&mut self, buf: &mut [u8], transaction: TransferConfig) {\n        #[cfg(not(stm32h7))]\n        T::REGS.cr().modify(|v| v.set_dmaen(false));\n        self.setup_transaction(QspiMode::IndirectWrite, &transaction, Some(buf.len()));\n\n        let current_ar = T::REGS.ar().read().address();\n        T::REGS.ccr().modify(|v| {\n            v.set_fmode(QspiMode::IndirectRead.into());\n        });\n        T::REGS.ar().write(|v| {\n            v.set_address(current_ar);\n        });\n\n        for b in buf {\n            while !T::REGS.sr().read().tcf() && (T::REGS.sr().read().flevel() == 0) {}\n            *b = unsafe { (T::REGS.dr().as_ptr() as *mut u8).read_volatile() };\n        }\n\n        while !T::REGS.sr().read().tcf() {}\n        T::REGS.fcr().modify(|v| v.set_ctcf(true));\n    }\n\n    /// Blocking write data.\n    pub fn blocking_write(&mut self, buf: &[u8], transaction: TransferConfig) {\n        // STM32H7 does not have dmaen\n        #[cfg(not(stm32h7))]\n        T::REGS.cr().modify(|v| v.set_dmaen(false));\n\n        self.setup_transaction(QspiMode::IndirectWrite, &transaction, Some(buf.len()));\n\n        T::REGS.ccr().modify(|v| {\n            v.set_fmode(QspiMode::IndirectWrite.into());\n        });\n\n        for &b in buf {\n            while !T::REGS.sr().read().ftf() {}\n            unsafe { (T::REGS.dr().as_ptr() as *mut u8).write_volatile(b) };\n        }\n\n        while !T::REGS.sr().read().tcf() {}\n        T::REGS.fcr().modify(|v| v.set_ctcf(true));\n    }\n\n    /// Enable memory map mode\n    pub fn enable_memory_map(&mut self, transaction: &TransferConfig) {\n        T::REGS.fcr().modify(|v| {\n            v.set_csmf(true);\n            v.set_ctcf(true);\n            v.set_ctef(true);\n            v.set_ctof(true);\n        });\n        T::REGS.ccr().write(|v| {\n            v.set_fmode(QspiMode::MemoryMapped.into());\n            v.set_imode(transaction.iwidth.into());\n            v.set_instruction(transaction.instruction);\n            v.set_admode(transaction.awidth.into());\n            v.set_adsize(self.config.address_size.into());\n            v.set_dmode(transaction.dwidth.into());\n            v.set_abmode(QspiWidth::NONE.into());\n            v.set_dcyc(transaction.dummy.into());\n        });\n    }\n\n    /// Automaticly poll until a desired status is received.\n    pub fn blocking_auto_poll(\n        &mut self,\n        // The transaction to send\n        transaction: TransferConfig,\n        // Polling frequency, in clock cycles\n        interval: u16,\n        // Data mask, 0 = ignore bit, 1 = match bit\n        mask: u32,\n        // Value to match\n        match_value: u32,\n        // Number of bytes to receive, 1..=4\n        data_len: usize,\n        // Matching mode\n        match_mode: MatchMode,\n        // Timeout\n        #[cfg(feature = \"time\")] timeout: embassy_time::Duration,\n    ) -> Result<(), Error> {\n        self.setup_auto_poll(transaction, interval, mask, match_value, data_len, match_mode);\n\n        #[cfg(feature = \"time\")]\n        let deadline = embassy_time::Instant::now() + timeout;\n\n        while !T::REGS.sr().read().smf() {\n            #[cfg(feature = \"time\")]\n            if embassy_time::Instant::now() > deadline {\n                return Err(Error::AutoPollTimeout);\n            }\n        }\n\n        Ok(())\n    }\n\n    fn setup_auto_poll(\n        &mut self,\n        transaction: TransferConfig,\n        interval: u16,\n        mask: u32,\n        match_value: u32,\n        data_len: usize,\n        match_mode: MatchMode,\n    ) {\n        assert!(data_len >= 1);\n        assert!(data_len <= 4);\n\n        while T::REGS.sr().read().busy() {}\n\n        T::REGS.fcr().modify(|v| {\n            v.set_csmf(true);\n            v.set_ctcf(true);\n            v.set_ctef(true);\n            v.set_ctof(true);\n        });\n\n        T::REGS.cr().modify(|m| {\n            // Set Match Mode\n            m.set_pmm(match_mode.into());\n            // Stop on match\n            m.set_apms(true);\n        });\n\n        T::REGS.psmkr().write(|w| w.set_mask(mask));\n        T::REGS.psmar().write(|w| w.set_match_(match_value));\n        T::REGS.pir().write(|w| w.set_interval(interval));\n\n        self.setup_transaction(QspiMode::AutoPolling, &transaction, Some(data_len));\n    }\n\n    fn setup_transaction(&mut self, fmode: QspiMode, transaction: &TransferConfig, data_len: Option<usize>) {\n        match (transaction.address, transaction.awidth) {\n            (Some(_), QspiWidth::NONE) => panic!(\"QSPI address can't be sent with an address width of NONE\"),\n            (Some(_), _) => {}\n            (None, QspiWidth::NONE) => {}\n            (None, _) => panic!(\"QSPI address is not set, so the address width should be NONE\"),\n        }\n\n        match (data_len, transaction.dwidth) {\n            (Some(0), _) => panic!(\"QSPI data must be at least one byte\"),\n            (Some(_), QspiWidth::NONE) => panic!(\"QSPI data can't be sent with a data width of NONE\"),\n            (Some(_), _) => {}\n            (None, QspiWidth::NONE) => {}\n            (None, _) => panic!(\"QSPI data is empty, so the data width should be NONE\"),\n        }\n\n        T::REGS.fcr().modify(|v| {\n            v.set_csmf(true);\n            v.set_ctcf(true);\n            v.set_ctef(true);\n            v.set_ctof(true);\n        });\n\n        while T::REGS.sr().read().busy() {}\n\n        if let Some(len) = data_len {\n            T::REGS.dlr().write(|v| v.set_dl(len as u32 - 1));\n        }\n\n        T::REGS.ccr().write(|v| {\n            v.set_fmode(fmode.into());\n            v.set_imode(transaction.iwidth.into());\n            v.set_instruction(transaction.instruction);\n            v.set_admode(transaction.awidth.into());\n            v.set_adsize(self.config.address_size.into());\n            v.set_dmode(transaction.dwidth.into());\n            v.set_abmode(QspiWidth::NONE.into());\n            v.set_dcyc(transaction.dummy.into());\n        });\n\n        if let Some(addr) = transaction.address {\n            T::REGS.ar().write(|v| {\n                v.set_address(addr);\n            });\n        }\n    }\n}\n\nimpl<'d, T: Instance> Qspi<'d, T, Blocking> {\n    /// Create a new QSPI driver for bank 1, in blocking mode.\n    pub fn new_blocking_bank1(\n        peri: Peri<'d, T>,\n        d0: Peri<'d, impl BK1D0Pin<T>>,\n        d1: Peri<'d, impl BK1D1Pin<T>>,\n        d2: Peri<'d, impl BK1D2Pin<T>>,\n        d3: Peri<'d, impl BK1D3Pin<T>>,\n        sck: Peri<'d, impl SckPin<T>>,\n        nss: Peri<'d, impl BK1NSSPin<T>>,\n        config: Config,\n    ) -> Self {\n        Self::new_inner(\n            peri,\n            new_pin!(d0, AfType::output(OutputType::PushPull, config.gpio_speed)),\n            new_pin!(d1, AfType::output(OutputType::PushPull, config.gpio_speed)),\n            new_pin!(d2, AfType::output(OutputType::PushPull, config.gpio_speed)),\n            new_pin!(d3, AfType::output(OutputType::PushPull, config.gpio_speed)),\n            None,\n            None,\n            None,\n            None,\n            new_pin!(sck, AfType::output(OutputType::PushPull, config.gpio_speed)),\n            new_pin!(\n                nss,\n                AfType::output_pull(OutputType::PushPull, config.gpio_speed, Pull::Up)\n            ),\n            None,\n            None,\n            config,\n            FlashSelection::Flash1,\n        )\n    }\n\n    /// Create a new QSPI driver for bank 2, in blocking mode.\n    pub fn new_blocking_bank2(\n        peri: Peri<'d, T>,\n        d0: Peri<'d, impl BK2D0Pin<T>>,\n        d1: Peri<'d, impl BK2D1Pin<T>>,\n        d2: Peri<'d, impl BK2D2Pin<T>>,\n        d3: Peri<'d, impl BK2D3Pin<T>>,\n        sck: Peri<'d, impl SckPin<T>>,\n        nss: Peri<'d, impl BK2NSSPin<T>>,\n        config: Config,\n    ) -> Self {\n        Self::new_inner(\n            peri,\n            None,\n            None,\n            None,\n            None,\n            new_pin!(d0, AfType::output(OutputType::PushPull, config.gpio_speed)),\n            new_pin!(d1, AfType::output(OutputType::PushPull, config.gpio_speed)),\n            new_pin!(d2, AfType::output(OutputType::PushPull, config.gpio_speed)),\n            new_pin!(d3, AfType::output(OutputType::PushPull, config.gpio_speed)),\n            new_pin!(sck, AfType::output(OutputType::PushPull, config.gpio_speed)),\n            None,\n            new_pin!(\n                nss,\n                AfType::output_pull(OutputType::PushPull, config.gpio_speed, Pull::Up)\n            ),\n            None,\n            config,\n            FlashSelection::Flash2,\n        )\n    }\n    /// Create a new QSPI driver for a dual bank, in blocking mode.\n    /// NOTE: Both nss pins are optional, there are 3 mods of operation: (1)boths flashes share nss 1, (2)boths flashes share nss 2,(3)each flash have its own nss pin.\n    pub fn new_blocking_dual_bank(\n        peri: Peri<'d, T>,\n        bk1d0: Peri<'d, impl BK1D0Pin<T>>,\n        bk1d1: Peri<'d, impl BK1D1Pin<T>>,\n        bk1d2: Peri<'d, impl BK1D2Pin<T>>,\n        bk1d3: Peri<'d, impl BK1D3Pin<T>>,\n        bk2d0: Peri<'d, impl BK2D0Pin<T>>,\n        bk2d1: Peri<'d, impl BK2D1Pin<T>>,\n        bk2d2: Peri<'d, impl BK2D2Pin<T>>,\n        bk2d3: Peri<'d, impl BK2D3Pin<T>>,\n        sck: Peri<'d, impl SckPin<T>>,\n        bk1nss: Peri<'d, impl BK1NSSPin<T>>,\n        bk2nss: Peri<'d, impl BK2NSSPin<T>>,\n        config: Config,\n    ) -> Self {\n        Self::new_inner(\n            peri,\n            new_pin!(bk1d0, AfType::output(OutputType::PushPull, config.gpio_speed)),\n            new_pin!(bk1d1, AfType::output(OutputType::PushPull, config.gpio_speed)),\n            new_pin!(bk1d2, AfType::output(OutputType::PushPull, config.gpio_speed)),\n            new_pin!(bk1d3, AfType::output(OutputType::PushPull, config.gpio_speed)),\n            new_pin!(bk2d0, AfType::output(OutputType::PushPull, config.gpio_speed)),\n            new_pin!(bk2d1, AfType::output(OutputType::PushPull, config.gpio_speed)),\n            new_pin!(bk2d2, AfType::output(OutputType::PushPull, config.gpio_speed)),\n            new_pin!(bk2d3, AfType::output(OutputType::PushPull, config.gpio_speed)),\n            new_pin!(sck, AfType::output(OutputType::PushPull, config.gpio_speed)),\n            new_pin!(bk1nss, AfType::output(OutputType::PushPull, config.gpio_speed)),\n            new_pin!(bk2nss, AfType::output(OutputType::PushPull, config.gpio_speed)),\n            None,\n            config,\n            FlashSelection::Flash1, // Dual bank mode, so DFM is set and both banks are used\n        )\n    }\n}\n\nimpl<'d, T: Instance> Qspi<'d, T, Async> {\n    /// Create a new QSPI driver for bank 1.\n    pub fn new_bank1<D, I>(\n        peri: Peri<'d, T>,\n        d0: Peri<'d, impl BK1D0Pin<T>>,\n        d1: Peri<'d, impl BK1D1Pin<T>>,\n        d2: Peri<'d, impl BK1D2Pin<T>>,\n        d3: Peri<'d, impl BK1D3Pin<T>>,\n        sck: Peri<'d, impl SckPin<T>>,\n        nss: Peri<'d, impl BK1NSSPin<T>>,\n        dma: Peri<'d, D>,\n        _irq: I,\n        config: Config,\n    ) -> Self\n    where\n        D: QuadDma<T>,\n        I: Binding<D::Interrupt, crate::dma::InterruptHandler<D>> + Binding<T::Interrupt, InterruptHandler<T>> + 'd,\n    {\n        T::Interrupt::unpend();\n        unsafe { T::Interrupt::enable() };\n\n        Self::new_inner(\n            peri,\n            new_pin!(d0, AfType::output(OutputType::PushPull, config.gpio_speed)),\n            new_pin!(d1, AfType::output(OutputType::PushPull, config.gpio_speed)),\n            new_pin!(d2, AfType::output(OutputType::PushPull, config.gpio_speed)),\n            new_pin!(d3, AfType::output(OutputType::PushPull, config.gpio_speed)),\n            None,\n            None,\n            None,\n            None,\n            new_pin!(sck, AfType::output(OutputType::PushPull, config.gpio_speed)),\n            new_pin!(\n                nss,\n                AfType::output_pull(OutputType::PushPull, config.gpio_speed, Pull::Up)\n            ),\n            None,\n            new_dma!(dma, _irq),\n            config,\n            FlashSelection::Flash1,\n        )\n    }\n\n    /// Create a new QSPI driver for bank 2.\n    pub fn new_bank2<D, I>(\n        peri: Peri<'d, T>,\n        d0: Peri<'d, impl BK2D0Pin<T>>,\n        d1: Peri<'d, impl BK2D1Pin<T>>,\n        d2: Peri<'d, impl BK2D2Pin<T>>,\n        d3: Peri<'d, impl BK2D3Pin<T>>,\n        sck: Peri<'d, impl SckPin<T>>,\n        nss: Peri<'d, impl BK2NSSPin<T>>,\n        dma: Peri<'d, D>,\n        _irq: I,\n        config: Config,\n    ) -> Self\n    where\n        D: QuadDma<T>,\n        I: Binding<D::Interrupt, crate::dma::InterruptHandler<D>> + Binding<T::Interrupt, InterruptHandler<T>> + 'd,\n    {\n        T::Interrupt::unpend();\n        unsafe { T::Interrupt::enable() };\n\n        Self::new_inner(\n            peri,\n            None,\n            None,\n            None,\n            None,\n            new_pin!(d0, AfType::output(OutputType::PushPull, config.gpio_speed)),\n            new_pin!(d1, AfType::output(OutputType::PushPull, config.gpio_speed)),\n            new_pin!(d2, AfType::output(OutputType::PushPull, config.gpio_speed)),\n            new_pin!(d3, AfType::output(OutputType::PushPull, config.gpio_speed)),\n            new_pin!(sck, AfType::output(OutputType::PushPull, config.gpio_speed)),\n            None,\n            new_pin!(\n                nss,\n                AfType::output_pull(OutputType::PushPull, config.gpio_speed, Pull::Up)\n            ),\n            new_dma!(dma, _irq),\n            config,\n            FlashSelection::Flash2,\n        )\n    }\n\n    /// Blocking read data, using DMA.\n    pub fn blocking_read_dma(&mut self, buf: &mut [u8], transaction: TransferConfig) {\n        let transfer = self.start_read_transfer(transaction, buf);\n        transfer.blocking_wait();\n    }\n\n    /// Async read data, using DMA.\n    pub async fn read_dma(&mut self, buf: &mut [u8], transaction: TransferConfig) {\n        let _scoped_wake_guard = T::RCC_INFO.wake_guard();\n        let transfer = self.start_read_transfer(transaction, buf);\n        transfer.await;\n    }\n\n    fn start_read_transfer<'a>(\n        &'a mut self,\n        transaction: TransferConfig,\n        buf: &'a mut [u8],\n    ) -> crate::dma::Transfer<'a> {\n        self.setup_transaction(QspiMode::IndirectWrite, &transaction, Some(buf.len()));\n\n        T::REGS.ccr().modify(|v| {\n            v.set_fmode(QspiMode::IndirectRead.into());\n        });\n        let current_ar = T::REGS.ar().read().address();\n        T::REGS.ar().write(|v| {\n            v.set_address(current_ar);\n        });\n\n        let transfer = unsafe {\n            self.dma\n                .as_mut()\n                .unwrap()\n                .read(T::REGS.dr().as_ptr() as *mut u8, buf, Default::default())\n        };\n\n        // STM32H7 does not have dmaen\n        #[cfg(not(stm32h7))]\n        T::REGS.cr().modify(|v| v.set_dmaen(true));\n        transfer\n    }\n\n    /// Blocking write data, using DMA.\n    pub fn blocking_write_dma(&mut self, buf: &[u8], transaction: TransferConfig) {\n        let transfer = self.start_write_transfer(transaction, buf);\n        transfer.blocking_wait();\n    }\n\n    /// Async write data, using DMA.\n    pub async fn write_dma(&mut self, buf: &[u8], transaction: TransferConfig) {\n        let _scoped_wake_guard = T::RCC_INFO.wake_guard();\n        let transfer = self.start_write_transfer(transaction, buf);\n        transfer.await;\n    }\n\n    fn start_write_transfer<'a>(&'a mut self, transaction: TransferConfig, buf: &'a [u8]) -> crate::dma::Transfer<'a> {\n        self.setup_transaction(QspiMode::IndirectWrite, &transaction, Some(buf.len()));\n\n        T::REGS.ccr().modify(|v| {\n            v.set_fmode(QspiMode::IndirectWrite.into());\n        });\n\n        let transfer = unsafe {\n            self.dma\n                .as_mut()\n                .unwrap()\n                .write(buf, T::REGS.dr().as_ptr() as *mut u8, Default::default())\n        };\n\n        // STM32H7 does not have dmaen\n        #[cfg(not(stm32h7))]\n        T::REGS.cr().modify(|v| v.set_dmaen(true));\n        transfer\n    }\n\n    /// Automaticly poll until a desired status is received.\n    /// In case the desired status is never received, it is advised to always use `WithTimeout::with_timeout()`.\n    pub async fn auto_poll(\n        &mut self,\n        // The transaction to send\n        transaction: TransferConfig,\n        // Polling frequency, in clock cycles\n        interval: u16,\n        // Data mask, 0 = ignore bit, 1 = match bit\n        mask: u32,\n        // Value to match\n        match_value: u32,\n        // Number of bytes to receive, 1..=4\n        data_len: usize,\n        // Matching mode\n        match_mode: MatchMode,\n    ) {\n        T::REGS.cr().modify(|m| {\n            // Set Status Match Interrupt Enable\n            m.set_smie(true);\n        });\n\n        self.setup_auto_poll(transaction, interval, mask, match_value, data_len, match_mode);\n\n        AutoPollFuture {\n            _peri: self._peri.reborrow(),\n        }\n        .await\n    }\n}\n\n/// QSPI error\n#[derive(Debug, PartialEq, Eq, Clone, Copy)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub enum Error {\n    /// Timed Out waiting for Status MAtch\n    AutoPollTimeout,\n}\n\ntrait SealedInstance {\n    const REGS: Regs;\n}\n\n/// QSPI instance trait.\n#[allow(private_bounds)]\npub trait Instance: SealedInstance + PeripheralType + RccPeripheral {\n    /// Interrupt for this instance.\n    type Interrupt: interrupt::typelevel::Interrupt;\n}\n\npin_trait!(SckPin, Instance);\npin_trait!(BK1D0Pin, Instance);\npin_trait!(BK1D1Pin, Instance);\npin_trait!(BK1D2Pin, Instance);\npin_trait!(BK1D3Pin, Instance);\npin_trait!(BK1NSSPin, Instance);\n\npin_trait!(BK2D0Pin, Instance);\npin_trait!(BK2D1Pin, Instance);\npin_trait!(BK2D2Pin, Instance);\npin_trait!(BK2D3Pin, Instance);\npin_trait!(BK2NSSPin, Instance);\n\ndma_trait!(QuadDma, Instance);\n\nmacro_rules! impl_peripheral {\n    ($inst:ident, $irq:ident) => {\n        impl SealedInstance for crate::peripherals::$inst {\n            const REGS: Regs = crate::pac::$inst;\n        }\n\n        impl Instance for crate::peripherals::$inst {\n            type Interrupt = crate::interrupt::typelevel::$irq;\n        }\n    };\n}\n\nforeach_interrupt! {\n    ($inst:ident, quadspi, $block:ident, GLOBAL, $irq:ident) => {\n        impl_peripheral!($inst, $irq);\n    };\n}\n\n#[must_use = \"futures do nothing unless you `.await` or poll them\"]\nstruct AutoPollFuture<'d, T: Instance> {\n    _peri: Peri<'d, T>,\n}\n\nimpl<'d, T: Instance> Unpin for AutoPollFuture<'d, T> {}\nimpl<'d, T: Instance> Drop for AutoPollFuture<'d, T> {\n    fn drop(&mut self) {\n        T::REGS.cr().modify(|m| {\n            // Unset Status Match Interrupt Enable\n            m.set_smie(false);\n        });\n\n        if T::REGS.ccr().read().fmode() == QspiMode::AutoPolling.into() && T::REGS.sr().read().busy() {\n            // Abort autopolling if dropped while still running\n            T::REGS.cr().modify(|m| m.set_abort(true));\n            while T::REGS.sr().read().busy() {}\n        }\n    }\n}\n\nimpl<'d, T: Instance> Future for AutoPollFuture<'d, T> {\n    type Output = ();\n\n    fn poll(self: core::pin::Pin<&mut Self>, cx: &mut core::task::Context<'_>) -> core::task::Poll<Self::Output> {\n        AUTOPOLL_WAKER.register(cx.waker());\n\n        if T::REGS.sr().read().busy() {\n            core::task::Poll::Pending\n        } else {\n            core::task::Poll::Ready(())\n        }\n    }\n}\n\nstatic AUTOPOLL_WAKER: AtomicWaker = AtomicWaker::new();\n\n/// AutoPolling Match Mode\npub enum MatchMode {\n    /// Match any masked bit\n    OR,\n    /// Match all masked bits\n    AND,\n}\n\nimpl From<MatchMode> for bool {\n    fn from(mode: MatchMode) -> Self {\n        match mode {\n            MatchMode::OR => true,\n            MatchMode::AND => false,\n        }\n    }\n}\n\n/// Interrupt handler.\npub struct InterruptHandler<T: Instance> {\n    _phantom: PhantomData<T>,\n}\n\nimpl<T: Instance> crate::interrupt::typelevel::Handler<T::Interrupt> for InterruptHandler<T> {\n    unsafe fn on_interrupt() {\n        if T::REGS.sr().read().smf() {\n            // clear status match flag\n            T::REGS.fcr().modify(|m| m.set_csmf(true));\n            AUTOPOLL_WAKER.wake();\n        }\n    }\n}\n"
  },
  {
    "path": "embassy-stm32/src/rcc/bd.rs",
    "content": "#[cfg(not(stm32n6))]\nuse core::sync::atomic::{Ordering, compiler_fence};\n\n#[cfg(not(stm32n6))]\nuse crate::pac::common::{RW, Reg};\n#[cfg(backup_sram)]\nuse crate::pac::pwr::vals::Retention;\npub use crate::pac::rcc::vals::Rtcsel as RtcClockSource;\nuse crate::time::Hertz;\n\n#[cfg(any(stm32f0, stm32f1, stm32f3))]\npub const LSI_FREQ: Hertz = Hertz(40_000);\n#[cfg(not(any(stm32f0, stm32f1, stm32f3)))]\npub const LSI_FREQ: Hertz = Hertz(32_000);\n\n#[allow(dead_code)]\n#[derive(Clone, Copy)]\npub enum LseMode {\n    Oscillator(LseDrive),\n    Bypass,\n}\n\n#[derive(Clone, Copy)]\npub struct LseConfig {\n    pub frequency: Hertz,\n    pub mode: LseMode,\n    /// If peripherals other than RTC/TAMP or RCC functions need the lse this bit must be set\n    #[cfg(any(rcc_l5, rcc_u5, rcc_u3, rcc_wle, rcc_wl5, rcc_wba))]\n    pub peripherals_clocked: bool,\n}\n\n#[allow(dead_code)]\n#[derive(Default, Clone, Copy)]\npub enum LseDrive {\n    #[cfg(not(stm32h5))] // ES0565: LSE Low drive mode is not functional\n    Low = 0,\n    MediumLow = 0x01,\n    #[default]\n    MediumHigh = 0x02,\n    High = 0x03,\n}\n\n// All families but these have the LSEDRV register\n#[cfg(not(any(rcc_f1, rcc_f1cl, rcc_f100, rcc_f2, rcc_f4, rcc_f410, rcc_l1)))]\nimpl From<LseDrive> for crate::pac::rcc::vals::Lsedrv {\n    fn from(value: LseDrive) -> Self {\n        use crate::pac::rcc::vals::Lsedrv;\n\n        match value {\n            #[cfg(not(stm32h5))] // ES0565: LSE Low drive mode is not functional\n            LseDrive::Low => Lsedrv::LOW,\n            LseDrive::MediumLow => Lsedrv::MEDIUM_LOW,\n            LseDrive::MediumHigh => Lsedrv::MEDIUM_HIGH,\n            LseDrive::High => Lsedrv::HIGH,\n        }\n    }\n}\n\n#[cfg(not(any(rtc_v2_l0, rtc_v2_l1, stm32c0, stm32n6)))]\ntype Bdcr = crate::pac::rcc::regs::Bdcr;\n#[cfg(any(rtc_v2_l0, rtc_v2_l1))]\ntype Bdcr = crate::pac::rcc::regs::Csr;\n#[cfg(any(stm32c0))]\ntype Bdcr = crate::pac::rcc::regs::Csr1;\n\n#[cfg(any(stm32c0))]\nfn unlock() {}\n\n#[cfg(not(any(stm32c0, stm32n6)))]\nfn unlock() {\n    #[cfg(any(stm32f0, stm32f1, stm32f2, stm32f3, stm32l0, stm32l1))]\n    let cr = crate::pac::PWR.cr();\n    #[cfg(not(any(\n        stm32f0, stm32f1, stm32f2, stm32f3, stm32l0, stm32l1, stm32u5, stm32u3, stm32h5, stm32wba, stm32n6\n    )))]\n    let cr = crate::pac::PWR.cr1();\n    #[cfg(any(stm32u5, stm32u3, stm32h5, stm32wba, stm32n6))]\n    let cr = crate::pac::PWR.dbpcr();\n\n    cr.modify(|w| w.set_dbp(true));\n    while !cr.read().dbp() {}\n}\n\n#[cfg(not(stm32n6))]\nfn bdcr() -> Reg<Bdcr, RW> {\n    #[cfg(any(rtc_v2_l0, rtc_v2_l1))]\n    return crate::pac::RCC.csr();\n    #[cfg(not(any(rtc_v2_l0, rtc_v2_l1, stm32c0)))]\n    return crate::pac::RCC.bdcr();\n    #[cfg(any(stm32c0))]\n    return crate::pac::RCC.csr1();\n}\n\n#[derive(Clone, Copy)]\npub struct LsConfig {\n    pub rtc: RtcClockSource,\n    pub lsi: bool,\n    pub lse: Option<LseConfig>,\n    #[cfg(backup_sram)]\n    pub enable_backup_sram: bool,\n}\n\nimpl LsConfig {\n    /// Creates an [`LsConfig`] using the LSI when possible.\n    pub const fn new() -> Self {\n        // on L5, just the fact that LSI is enabled makes things crash.\n        // TODO: investigate.\n\n        #[cfg(not(stm32l5))]\n        return Self::default_lsi();\n        #[cfg(stm32l5)]\n        return Self::off();\n    }\n\n    pub const fn default_lse() -> Self {\n        Self {\n            rtc: RtcClockSource::LSE,\n            lse: Some(LseConfig {\n                frequency: Hertz(32_768),\n                mode: LseMode::Oscillator(LseDrive::MediumHigh),\n                #[cfg(any(rcc_l5, rcc_u5, rcc_u3, rcc_wle, rcc_wl5, rcc_wba))]\n                peripherals_clocked: false,\n            }),\n            lsi: false,\n            #[cfg(backup_sram)]\n            enable_backup_sram: false,\n        }\n    }\n\n    pub const fn default_lsi() -> Self {\n        Self {\n            rtc: RtcClockSource::LSI,\n            lsi: true,\n            lse: None,\n            #[cfg(backup_sram)]\n            enable_backup_sram: false,\n        }\n    }\n\n    pub const fn off() -> Self {\n        Self {\n            rtc: RtcClockSource::DISABLE,\n            lsi: false,\n            lse: None,\n            #[cfg(backup_sram)]\n            enable_backup_sram: false,\n        }\n    }\n}\n\nimpl Default for LsConfig {\n    fn default() -> Self {\n        Self::new()\n    }\n}\n\nimpl LsConfig {\n    #[cfg(not(stm32n6))]\n    pub(crate) fn init(&self) -> Option<Hertz> {\n        let rtc_clk = match self.rtc {\n            RtcClockSource::LSI => {\n                assert!(self.lsi);\n                Some(LSI_FREQ)\n            }\n            RtcClockSource::LSE => Some(self.lse.as_ref().unwrap().frequency),\n            RtcClockSource::DISABLE => None,\n            _ => todo!(),\n        };\n\n        let (lse_en, lse_byp, lse_drv) = match &self.lse {\n            Some(c) => match c.mode {\n                LseMode::Oscillator(lse_drv) => (true, false, Some(lse_drv)),\n                LseMode::Bypass => (true, true, None),\n            },\n            None => (false, false, None),\n        };\n        #[cfg(any(rcc_l5, rcc_u5, rcc_wle, rcc_wl5, rcc_wba))]\n        let lse_sysen = if let Some(lse) = self.lse {\n            Some(lse.peripherals_clocked)\n        } else {\n            None\n        };\n        #[cfg(rcc_u0)]\n        let lse_sysen = Some(lse_en);\n\n        _ = lse_drv; // not all chips have it.\n\n        // Disable backup domain write protection\n        unlock();\n\n        if self.lsi {\n            #[cfg(any(stm32u5, stm32h5, stm32wba))]\n            let csr = crate::pac::RCC.bdcr();\n            #[cfg(stm32n6)]\n            let csr = crate::pac::RCC.sr();\n            #[cfg(not(any(stm32u5, stm32h5, stm32wba, stm32c0, stm32n6)))]\n            let csr = crate::pac::RCC.csr();\n            #[cfg(stm32c0)]\n            let csr = crate::pac::RCC.csr2();\n\n            #[cfg(not(any(rcc_wb, rcc_wba, rcc_n6)))]\n            csr.modify(|w| w.set_lsion(true));\n\n            #[cfg(rcc_n6)]\n            crate::pac::RCC.cr().modify(|w| w.set_lsion(true));\n\n            #[cfg(any(rcc_wb, rcc_wba))]\n            csr.modify(|w| w.set_lsi1on(true));\n\n            #[cfg(not(any(rcc_wb, rcc_wba)))]\n            while !csr.read().lsirdy() {}\n\n            #[cfg(any(rcc_wb, rcc_wba))]\n            while !csr.read().lsi1rdy() {}\n        }\n\n        // Enable backup regulator for peristent battery backed sram\n        #[cfg(backup_sram)]\n        {\n            unsafe { super::BKSRAM_RETAINED = crate::pac::PWR.bdcr().read().bren() == Retention::PRESERVED };\n\n            crate::pac::PWR.bdcr().modify(|w| {\n                w.set_bren(match self.enable_backup_sram {\n                    true => Retention::PRESERVED,\n                    false => Retention::LOST,\n                });\n            });\n\n            // Wait for backup regulator voltage to stabilize\n            while self.enable_backup_sram && !crate::pac::PWR.bdsr().read().brrdy() {}\n        }\n\n        // backup domain configuration (LSEON, RTCEN, RTCSEL) is kept across resets.\n        // once set, changing it requires a backup domain reset.\n        // first check if the configuration matches what we want.\n        // N6 has all the fields spread across multiple registers under RCC.\n\n        // check if it's already enabled and in the source we want.\n        #[cfg(not(rcc_n6))]\n        let reg = bdcr().read();\n        #[cfg(rcc_n6)]\n        let reg = crate::pac::RCC.cr().read();\n        #[cfg(rcc_n6)]\n        let apb4lenr = crate::pac::RCC.apb4lenr().read();\n        #[cfg(rcc_n6)]\n        let ccipr7 = crate::pac::RCC.ccipr7().read();\n        #[cfg(rcc_n6)]\n        let lsecfgr = crate::pac::RCC.lsecfgr().read();\n\n        let mut ok = true;\n        #[cfg(not(rcc_n6))]\n        {\n            ok &= reg.rtcsel() == self.rtc;\n        }\n        #[cfg(rcc_n6)]\n        {\n            ok &= ccipr7.rtcsel() == self.rtc;\n        }\n        #[cfg(not(any(rcc_wba, rcc_n6)))]\n        {\n            ok &= reg.rtcen() == (self.rtc != RtcClockSource::DISABLE);\n        }\n        #[cfg(rcc_n6)]\n        {\n            ok &= apb4lenr.rtcen() == (self.rtc != RtcClockSource::DISABLE);\n        }\n        ok &= reg.lseon() == lse_en;\n        #[cfg(not(rcc_n6))]\n        {\n            ok &= reg.lsebyp() == lse_byp;\n        }\n        #[cfg(rcc_n6)]\n        {\n            ok &= lsecfgr.lsebyp() == lse_byp;\n        }\n        #[cfg(any(rcc_l5, rcc_u5, rcc_wle, rcc_wl5, rcc_wba, rcc_u0))]\n        if let Some(lse_sysen) = lse_sysen\n            && !lse_sysen\n        {\n            ok &= !reg.lsesysen();\n        }\n        #[cfg(not(any(rcc_f1, rcc_f1cl, rcc_f100, rcc_f2, rcc_f4, rcc_f410, rcc_l1, rcc_n6)))]\n        if let Some(lse_drv) = lse_drv {\n            ok &= reg.lsedrv() == lse_drv.into();\n        }\n        #[cfg(rcc_n6)]\n        if let Some(lse_drv) = lse_drv {\n            ok &= lsecfgr.lsedrv() == lse_drv.into();\n        }\n\n        // After a power-on reset LSESYSEN will be set to 0\n        // even if VBAT was present and kept the RTC running\n        #[cfg(any(rcc_l5, rcc_u5, rcc_wle, rcc_wl5, rcc_wba, rcc_u0))]\n        if ok\n            && let Some(lse_sysen) = lse_sysen\n            && lse_sysen\n        {\n            bdcr().modify(|w| {\n                w.set_lsesysen(true);\n            });\n\n            while !bdcr().read().lsesysrdy() {}\n        }\n\n        // if configuration is OK, we're done.\n        if ok {\n            trace!(\"BDCR ok: {:08x}\", bdcr().read().0);\n            return rtc_clk;\n        }\n\n        // If not OK, reset backup domain and configure it.\n        #[cfg(not(any(rcc_l0, rcc_l0_v2, rcc_l1, stm32h5, stm32h7rs, stm32c0, stm32n6)))]\n        {\n            bdcr().modify(|w| w.set_bdrst(true));\n            bdcr().modify(|w| w.set_bdrst(false));\n        }\n        // H5 has a terrible, terrible errata: 'SRAM2 is erased when the backup domain is reset'\n        // pending a more sane sane way to handle this, just don't reset BD for now.\n        // This means the RTCSEL write below will have no effect, only if it has already been written\n        // after last power-on. Since it's uncommon to dynamically change RTCSEL, this is better than\n        // letting half our RAM go magically *poof*.\n        // STM32H503CB/EB/KB/RB device errata - 2.2.8 SRAM2 unduly erased upon a backup domain reset\n        // STM32H562xx/563xx/573xx device errata - 2.2.14 SRAM2 is erased when the backup domain is reset\n        //#[cfg(any(stm32h5, stm32h7rs))]\n        #[cfg(any(stm32h7rs, stm32n6))]\n        {\n            bdcr().modify(|w| w.set_vswrst(true));\n            bdcr().modify(|w| w.set_vswrst(false));\n        }\n        #[cfg(any(stm32c0, stm32l0))]\n        {\n            bdcr().modify(|w| w.set_rtcrst(true));\n            bdcr().modify(|w| w.set_rtcrst(false));\n        }\n\n        if lse_en {\n            #[cfg(not(rcc_n6))]\n            {\n                bdcr().modify(|w| {\n                    #[cfg(not(any(rcc_f1, rcc_f1cl, rcc_f100, rcc_f2, rcc_f4, rcc_f410, rcc_l1)))]\n                    if let Some(lse_drv) = lse_drv {\n                        w.set_lsedrv(lse_drv.into());\n                    }\n                    w.set_lsebyp(lse_byp);\n                    w.set_lseon(true);\n                });\n\n                while !bdcr().read().lserdy() {}\n            }\n            #[cfg(rcc_n6)]\n            {\n                crate::pac::RCC.lsecfgr().modify(|w| {\n                    if let Some(lse_drv) = lse_drv {\n                        w.set_lsedrv(lse_drv.into());\n                    }\n                    w.set_lsebyp(lse_byp);\n                });\n                crate::pac::RCC.cr().modify(|w| w.set_lseon(true));\n\n                while !crate::pac::RCC.sr().read().lserdy() {}\n            }\n\n            #[cfg(any(rcc_l5, rcc_u5, rcc_wle, rcc_wl5, rcc_wba, rcc_u0))]\n            if let Some(lse_sysen) = lse_sysen {\n                bdcr().modify(|w| {\n                    w.set_lsesysen(lse_sysen);\n                });\n\n                if lse_sysen {\n                    while !bdcr().read().lsesysrdy() {}\n                }\n            }\n        }\n\n        if self.rtc != RtcClockSource::DISABLE {\n            #[cfg(not(rcc_n6))]\n            bdcr().modify(|w| {\n                #[cfg(any(rtc_v2_h7, rtc_v2_l4, rtc_v2_wb, rtc_v3_base, rtc_v3_u5))]\n                assert!(!w.lsecsson(), \"RTC is not compatible with LSE CSS, yet.\");\n\n                #[cfg(not(rcc_wba))]\n                w.set_rtcen(true);\n                w.set_rtcsel(self.rtc);\n            });\n\n            #[cfg(rcc_n6)]\n            {\n                crate::pac::RCC.ccipr7().modify(|w| w.set_rtcsel(self.rtc));\n                crate::pac::RCC.apb4lenr().modify(|w| w.set_rtcen(true))\n            }\n        }\n\n        trace!(\"BDCR configured: {:08x}\", bdcr().read().0);\n\n        compiler_fence(Ordering::SeqCst);\n\n        rtc_clk\n    }\n}\n"
  },
  {
    "path": "embassy-stm32/src/rcc/c0.rs",
    "content": "use crate::pac::flash::vals::Latency;\npub use crate::pac::rcc::vals::{\n    Hpre as AHBPrescaler, Hsidiv as HsiSysDiv, Hsikerdiv as HsiKerDiv, Ppre as APBPrescaler, Sw as Sysclk,\n};\nuse crate::pac::{FLASH, RCC};\nuse crate::rcc::LSI_FREQ;\nuse crate::time::Hertz;\n\n/// HSI speed\npub const HSI_FREQ: Hertz = Hertz(48_000_000);\n\n/// HSE Mode\n#[derive(Clone, Copy, Eq, PartialEq)]\npub enum HseMode {\n    /// crystal/ceramic oscillator (HSEBYP=0)\n    Oscillator,\n    /// external analog clock (low swing) (HSEBYP=1)\n    Bypass,\n}\n\n/// HSE Configuration\n#[derive(Clone, Copy, Eq, PartialEq)]\npub struct Hse {\n    /// HSE frequency.\n    pub freq: Hertz,\n    /// HSE mode.\n    pub mode: HseMode,\n}\n\n/// HSI Configuration\n#[derive(Clone, Copy, Eq, PartialEq)]\npub struct Hsi {\n    /// Division factor for HSISYS clock. Default is 4.\n    pub sys_div: HsiSysDiv,\n    /// Division factor for HSIKER clock. Default is 3.\n    pub ker_div: HsiKerDiv,\n}\n\n/// Clocks configutation\n#[non_exhaustive]\n#[derive(Clone, Copy)]\npub struct Config {\n    /// HSI Configuration\n    pub hsi: Option<Hsi>,\n\n    /// HSE Configuration\n    pub hse: Option<Hse>,\n\n    /// System Clock Configuration\n    pub sys: Sysclk,\n\n    /// HSI48 Configuration\n    #[cfg(crs)]\n    pub hsi48: Option<super::Hsi48Config>,\n\n    pub ahb_pre: AHBPrescaler,\n    pub apb1_pre: APBPrescaler,\n\n    /// Low-Speed Clock Configuration\n    pub ls: super::LsConfig,\n\n    /// Per-peripheral kernel clock selection muxes\n    pub mux: super::mux::ClockMux,\n}\n\nimpl Config {\n    pub const fn new() -> Self {\n        Config {\n            hsi: Some(Hsi {\n                sys_div: HsiSysDiv::DIV4,\n                ker_div: HsiKerDiv::DIV3,\n            }),\n            hse: None,\n            sys: Sysclk::HSISYS,\n            #[cfg(crs)]\n            hsi48: Some(crate::rcc::Hsi48Config::new()),\n            ahb_pre: AHBPrescaler::DIV1,\n            apb1_pre: APBPrescaler::DIV1,\n            ls: crate::rcc::LsConfig::new(),\n            mux: super::mux::ClockMux::default(),\n        }\n    }\n}\n\nimpl Default for Config {\n    fn default() -> Config {\n        Self::new()\n    }\n}\n\npub(crate) unsafe fn init(config: Config) {\n    // Turn on the HSI\n    match config.hsi {\n        None => RCC.cr().modify(|w| w.set_hsion(true)),\n        Some(hsi) => RCC.cr().modify(|w| {\n            w.set_hsidiv(hsi.sys_div);\n            w.set_hsikerdiv(hsi.ker_div);\n            w.set_hsion(true);\n        }),\n    }\n    while !RCC.cr().read().hsirdy() {}\n\n    // Use the HSI clock as system clock during the actual clock setup\n    RCC.cfgr().modify(|w| w.set_sw(Sysclk::HSISYS));\n    while RCC.cfgr().read().sws() != Sysclk::HSISYS {}\n\n    // Configure HSI\n    let (hsi, hsisys, hsiker) = match config.hsi {\n        None => (None, None, None),\n        Some(hsi) => (\n            Some(HSI_FREQ),\n            Some(HSI_FREQ / hsi.sys_div),\n            Some(HSI_FREQ / hsi.ker_div),\n        ),\n    };\n\n    // Configure HSE\n    let hse = match config.hse {\n        None => {\n            RCC.cr().modify(|w| w.set_hseon(false));\n            None\n        }\n        Some(hse) => {\n            match hse.mode {\n                HseMode::Bypass => rcc_assert!(max::HSE_BYP.contains(&hse.freq)),\n                HseMode::Oscillator => rcc_assert!(max::HSE_OSC.contains(&hse.freq)),\n            }\n\n            RCC.cr().modify(|w| w.set_hsebyp(hse.mode != HseMode::Oscillator));\n            RCC.cr().modify(|w| w.set_hseon(true));\n            while !RCC.cr().read().hserdy() {}\n            Some(hse.freq)\n        }\n    };\n\n    // Configure HSI48 if required\n    #[cfg(crs)]\n    let hsi48 = config.hsi48.map(super::init_hsi48);\n\n    let rtc = config.ls.init();\n\n    let sys = match config.sys {\n        Sysclk::HSISYS => unwrap!(hsisys),\n        Sysclk::HSE => unwrap!(hse),\n        Sysclk::LSI => {\n            assert!(config.ls.lsi);\n            LSI_FREQ\n        }\n        Sysclk::LSE => unwrap!(config.ls.lse).frequency,\n        _ => unreachable!(),\n    };\n\n    rcc_assert!(max::SYSCLK.contains(&sys));\n\n    // Calculate the AHB frequency (HCLK), among other things so we can calculate the correct flash read latency.\n    let hclk = sys / config.ahb_pre;\n    rcc_assert!(max::HCLK.contains(&hclk));\n\n    let (pclk1, pclk1_tim) = super::util::calc_pclk(hclk, config.apb1_pre);\n    rcc_assert!(max::PCLK.contains(&pclk1));\n\n    let latency = match hclk.0 {\n        ..=24_000_000 => Latency::WS0,\n        _ => Latency::WS1,\n    };\n\n    // Configure flash read access latency based on voltage scale and frequency\n    FLASH.acr().modify(|w| {\n        w.set_latency(latency);\n    });\n\n    // Spin until the effective flash latency is set.\n    while FLASH.acr().read().latency() != latency {}\n\n    // Now that boost mode and flash read access latency are configured, set up SYSCLK\n    RCC.cfgr().modify(|w| {\n        w.set_sw(config.sys);\n        w.set_hpre(config.ahb_pre);\n        w.set_ppre(config.apb1_pre);\n    });\n    while RCC.cfgr().read().sws() != config.sys {}\n\n    // Disable HSI if not used\n    if config.hsi.is_none() {\n        RCC.cr().modify(|w| w.set_hsion(false));\n    }\n\n    // Disable the HSI48, if not used\n    #[cfg(crs)]\n    if config.hsi48.is_none() {\n        super::disable_hsi48();\n    }\n\n    config.mux.init();\n\n    set_clocks!(\n        sys: Some(sys),\n        hclk1: Some(hclk),\n        pclk1: Some(pclk1),\n        pclk1_tim: Some(pclk1_tim),\n        hsi: hsi,\n        hsiker: hsiker,\n        hse: hse,\n        #[cfg(crs)]\n        hsi48: hsi48,\n        rtc: rtc,\n\n        // TODO\n        lsi: None,\n        lse: None,\n    );\n\n    RCC.ccipr()\n        .modify(|w| w.set_adc1sel(stm32_metapac::rcc::vals::Adcsel::SYS));\n}\n\nmod max {\n    use core::ops::RangeInclusive;\n\n    use crate::time::Hertz;\n\n    pub(crate) const HSE_OSC: RangeInclusive<Hertz> = Hertz(4_000_000)..=Hertz(48_000_000);\n    pub(crate) const HSE_BYP: RangeInclusive<Hertz> = Hertz(0)..=Hertz(48_000_000);\n    pub(crate) const SYSCLK: RangeInclusive<Hertz> = Hertz(0)..=Hertz(48_000_000);\n    pub(crate) const PCLK: RangeInclusive<Hertz> = Hertz(8)..=Hertz(48_000_000);\n    pub(crate) const HCLK: RangeInclusive<Hertz> = Hertz(0)..=Hertz(48_000_000);\n}\n"
  },
  {
    "path": "embassy-stm32/src/rcc/f013.rs",
    "content": "use crate::pac::flash::vals::Latency;\n#[cfg(stm32f1)]\npub use crate::pac::rcc::vals::Adcpre as ADCPrescaler;\n#[cfg(stm32f3)]\npub use crate::pac::rcc::vals::Adcpres as AdcPllPrescaler;\nuse crate::pac::rcc::vals::Pllsrc;\n#[cfg(all(stm32f1, not(any(stm32f105, stm32f107))))]\npub use crate::pac::rcc::vals::Pllxtpre as PllPreDiv;\n#[cfg(any(stm32f0, stm32f3))]\npub use crate::pac::rcc::vals::Prediv as PllPreDiv;\npub use crate::pac::rcc::vals::{Hpre as AHBPrescaler, Pllmul as PllMul, Ppre as APBPrescaler, Sw as Sysclk};\n#[cfg(any(stm32f105, stm32f107))]\npub use crate::pac::rcc::vals::{I2s2src, Pll2mul as Pll2Mul, Prediv1 as PllPreDiv, Prediv1src, Usbpre as UsbPre};\nuse crate::pac::{FLASH, RCC};\nuse crate::time::Hertz;\n\n/// HSI speed\npub const HSI_FREQ: Hertz = Hertz(8_000_000);\n\n#[derive(Clone, Copy, Eq, PartialEq)]\npub enum HseMode {\n    /// crystal/ceramic oscillator (HSEBYP=0)\n    Oscillator,\n    /// external analog clock (low swing) (HSEBYP=1)\n    Bypass,\n}\n\n#[derive(Clone, Copy, Eq, PartialEq)]\npub struct Hse {\n    /// HSE frequency.\n    pub freq: Hertz,\n    /// HSE mode.\n    pub mode: HseMode,\n}\n\n#[derive(Clone, Copy, Eq, PartialEq)]\npub enum PllSource {\n    HSE,\n    HSI,\n    #[cfg(rcc_f0v4)]\n    HSI48,\n    #[cfg(any(stm32f105, stm32f107))]\n    PLL2,\n}\n\n#[derive(Clone, Copy)]\npub struct Pll {\n    pub src: PllSource,\n\n    /// PLL pre-divider.\n    ///\n    /// On some chips, this must be 2 if `src == HSI`. Init will panic if this is not the case.\n    pub prediv: PllPreDiv,\n\n    /// PLL multiplication factor.\n    pub mul: PllMul,\n}\n\n#[cfg(any(stm32f105, stm32f107))]\n#[derive(Clone, Copy)]\npub struct Pll2Or3 {\n    pub mul: Pll2Mul,\n}\n\n#[cfg(all(stm32f3, not(rcc_f37)))]\n#[derive(Clone, Copy)]\npub enum AdcClockSource {\n    Pll(AdcPllPrescaler),\n    Hclk(AdcHclkPrescaler),\n}\n\n#[cfg(all(stm32f3, not(rcc_f37)))]\n#[derive(Clone, Copy, PartialEq, Eq)]\npub enum AdcHclkPrescaler {\n    Div1,\n    Div2,\n    Div4,\n}\n\n#[cfg(stm32f334)]\n#[derive(Clone, Copy, PartialEq, Eq)]\npub enum HrtimClockSource {\n    BusClk,\n    PllClk,\n}\n\n/// Clocks configutation\n#[non_exhaustive]\n#[derive(Clone, Copy)]\npub struct Config {\n    pub hsi: bool,\n    pub hse: Option<Hse>,\n    #[cfg(crs)]\n    pub hsi48: Option<super::Hsi48Config>,\n    pub sys: Sysclk,\n\n    pub pll: Option<Pll>,\n    #[cfg(any(stm32f105, stm32f107))]\n    pub pll2: Option<Pll2Or3>,\n    #[cfg(any(stm32f105, stm32f107))]\n    pub pll3: Option<Pll2Or3>,\n    #[cfg(any(stm32f105, stm32f107))]\n    pub prediv2: PllPreDiv,\n\n    pub ahb_pre: AHBPrescaler,\n    pub apb1_pre: APBPrescaler,\n    #[cfg(not(stm32f0))]\n    pub apb2_pre: APBPrescaler,\n\n    #[cfg(stm32f1)]\n    pub adc_pre: ADCPrescaler,\n\n    #[cfg(all(stm32f3, not(rcc_f37)))]\n    pub adc: AdcClockSource,\n    #[cfg(all(stm32f3, not(rcc_f37), any(peri_adc3_common, peri_adc34_common)))]\n    pub adc34: AdcClockSource,\n\n    #[cfg(any(stm32f105, stm32f107))]\n    pub i2s2_src: I2s2src,\n    #[cfg(any(stm32f105, stm32f107))]\n    pub i2s3_src: I2s2src,\n\n    /// Per-peripheral kernel clock selection muxes\n    pub mux: super::mux::ClockMux,\n\n    pub ls: super::LsConfig,\n}\n\nimpl Config {\n    pub const fn new() -> Self {\n        Self {\n            hsi: true,\n            hse: None,\n            #[cfg(crs)]\n            hsi48: Some(crate::rcc::Hsi48Config::new()),\n            sys: Sysclk::HSI,\n            pll: None,\n\n            #[cfg(any(stm32f105,stm32f107))]\n            pll2: None,\n            #[cfg(any(stm32f105,stm32f107))]\n            pll3: None,\n            #[cfg(any(stm32f105,stm32f107))]\n            prediv2: PllPreDiv::DIV1,\n\n            ahb_pre: AHBPrescaler::DIV1,\n            apb1_pre: APBPrescaler::DIV1,\n            #[cfg(not(stm32f0))]\n            apb2_pre: APBPrescaler::DIV1,\n            ls: crate::rcc::LsConfig::new(),\n\n            #[cfg(stm32f1)]\n            // ensure ADC is not out of range by default even if APB2 is maxxed out (36mhz)\n            adc_pre: ADCPrescaler::DIV6,\n\n            #[cfg(all(stm32f3, not(rcc_f37)))]\n            adc: AdcClockSource::Hclk(AdcHclkPrescaler::Div1),\n            #[cfg(all(stm32f3, not(rcc_f37), any(peri_adc3_common, peri_adc34_common)))]\n            adc34: AdcClockSource::Hclk(AdcHclkPrescaler::Div1),\n\n            #[cfg(any(stm32f105,stm32f107))]\n            i2s2_src: I2s2src::SYS,\n            #[cfg(any(stm32f105,stm32f107))]\n            i2s3_src: I2s2src::SYS,\n\n            mux: super::mux::ClockMux::default(),\n        }\n    }\n}\n\nimpl Default for Config {\n    fn default() -> Self {\n        Self::new()\n    }\n}\n\n/// Initialize and Set the clock frequencies\npub(crate) unsafe fn init(config: Config) {\n    // Turn on the HSI\n    RCC.cr().modify(|w| w.set_hsion(true));\n    while !RCC.cr().read().hsirdy() {}\n\n    // Use the HSI clock as system clock during the actual clock setup\n    RCC.cfgr().modify(|w| w.set_sw(Sysclk::HSI));\n    while RCC.cfgr().read().sws() != Sysclk::HSI {}\n\n    // Configure HSI\n    let hsi = match config.hsi {\n        false => None,\n        true => Some(HSI_FREQ),\n    };\n\n    // Configure HSE\n    let hse = match config.hse {\n        None => {\n            RCC.cr().modify(|w| w.set_hseon(false));\n            None\n        }\n        Some(hse) => {\n            match hse.mode {\n                HseMode::Bypass => rcc_assert!(max::HSE_BYP.contains(&hse.freq)),\n                HseMode::Oscillator => rcc_assert!(max::HSE_OSC.contains(&hse.freq)),\n            }\n\n            RCC.cr().modify(|w| w.set_hsebyp(hse.mode != HseMode::Oscillator));\n            RCC.cr().modify(|w| w.set_hseon(true));\n            while !RCC.cr().read().hserdy() {}\n            Some(hse.freq)\n        }\n    };\n\n    // configure HSI48\n    #[cfg(crs)]\n    let hsi48 = config.hsi48.map(|config| super::init_hsi48(config));\n    #[cfg(not(crs))]\n    let hsi48: Option<Hertz> = None;\n\n    // PLL2 and PLL3\n    // Configure this before PLL since PLL2 can be the source for PLL.\n    #[cfg(any(stm32f105, stm32f107))]\n    {\n        // Common prediv for PLL2 and PLL3\n        RCC.cfgr2().modify(|w| w.set_prediv2(config.prediv2));\n\n        // Configure PLL2\n        if let Some(pll2) = config.pll2 {\n            RCC.cfgr2().modify(|w| w.set_pll2mul(pll2.mul));\n            RCC.cr().modify(|w| w.set_pll2on(true));\n            while !RCC.cr().read().pll2rdy() {}\n        }\n\n        // Configure PLL3\n        if let Some(pll3) = config.pll3 {\n            RCC.cfgr2().modify(|w| w.set_pll3mul(pll3.mul));\n            RCC.cr().modify(|w| w.set_pll3on(true));\n            while !RCC.cr().read().pll3rdy() {}\n        }\n    }\n\n    // Enable PLL\n    let pll = config.pll.map(|pll| {\n        let (src_val, src_freq) = match pll.src {\n            #[cfg(any(rcc_f0v3, rcc_f0v4, rcc_f3v3))]\n            PllSource::HSI => (Pllsrc::HSI_DIV_PREDIV, unwrap!(hsi)),\n            #[cfg(not(any(rcc_f0v3, rcc_f0v4, rcc_f3v3)))]\n            PllSource::HSI => {\n                if pll.prediv != PllPreDiv::DIV2 {\n                    panic!(\"if PLL source is HSI, PLL prediv must be 2.\");\n                }\n                (Pllsrc::HSI_DIV2, unwrap!(hsi))\n            }\n            PllSource::HSE => {\n                #[cfg(any(stm32f105, stm32f107))]\n                RCC.cfgr2().modify(|w| w.set_prediv1src(Prediv1src::HSE));\n\n                (Pllsrc::HSE_DIV_PREDIV, unwrap!(hse))\n            }\n            #[cfg(rcc_f0v4)]\n            PllSource::HSI48 => (Pllsrc::HSI48_DIV_PREDIV, unwrap!(hsi48)),\n            #[cfg(any(stm32f105, stm32f107))]\n            PllSource::PLL2 => {\n                if config.pll2.is_none() {\n                    panic!(\"if PLL source is PLL2, Config::pll2 must also be set.\");\n                }\n                RCC.cfgr2().modify(|w| w.set_prediv1src(Prediv1src::PLL2));\n\n                let pll2 = unwrap!(config.pll2);\n                let in_freq = hse.unwrap() / config.prediv2;\n                let pll2freq = in_freq * pll2.mul;\n\n                (Pllsrc::HSE_DIV_PREDIV, pll2freq)\n            }\n        };\n        let in_freq = src_freq / pll.prediv;\n\n        rcc_assert!(max::PLL_IN.contains(&in_freq));\n        let out_freq = in_freq * pll.mul;\n        rcc_assert!(max::PLL_OUT.contains(&out_freq));\n\n        #[cfg(not(stm32f1))]\n        RCC.cfgr2().modify(|w| w.set_prediv(pll.prediv));\n\n        #[cfg(any(stm32f105, stm32f107))]\n        RCC.cfgr2().modify(|w| w.set_prediv1(pll.prediv));\n\n        RCC.cfgr().modify(|w| {\n            w.set_pllmul(pll.mul);\n            w.set_pllsrc(src_val);\n            #[cfg(all(stm32f1, not(any(stm32f105, stm32f107))))]\n            w.set_pllxtpre(pll.prediv);\n        });\n        RCC.cr().modify(|w| w.set_pllon(true));\n        while !RCC.cr().read().pllrdy() {}\n\n        out_freq\n    });\n\n    #[cfg(any(rcc_f1, rcc_f1cl, stm32f3, stm32f105, stm32f107))]\n    let usb = match pll {\n        Some(Hertz(72_000_000)) => Some(crate::pac::rcc::vals::Usbpre::DIV1_5),\n        Some(Hertz(48_000_000)) => Some(crate::pac::rcc::vals::Usbpre::DIV1),\n        _ => None,\n    }\n    .map(|usbpre| {\n        RCC.cfgr().modify(|w| w.set_usbpre(usbpre));\n        Hertz(48_000_000)\n    });\n\n    // Configure sysclk\n    let sys = match config.sys {\n        Sysclk::HSI => unwrap!(hsi),\n        Sysclk::HSE => unwrap!(hse),\n        Sysclk::PLL1_P => unwrap!(pll),\n        #[cfg(crs)]\n        Sysclk::HSI48 => unwrap!(hsi48),\n        #[cfg(not(crs))]\n        _ => unreachable!(),\n    };\n\n    let hclk = sys / config.ahb_pre;\n    let (pclk1, pclk1_tim) = super::util::calc_pclk(hclk, config.apb1_pre);\n    #[cfg(not(stm32f0))]\n    let (pclk2, pclk2_tim) = super::util::calc_pclk(hclk, config.apb2_pre);\n    #[cfg(stm32f0)]\n    let (pclk2, pclk2_tim) = (pclk1, pclk1_tim);\n\n    rcc_assert!(max::HCLK.contains(&hclk));\n    rcc_assert!(max::PCLK1.contains(&pclk1));\n    #[cfg(not(stm32f0))]\n    rcc_assert!(max::PCLK2.contains(&pclk2));\n\n    #[cfg(stm32f1)]\n    let adc = pclk2 / config.adc_pre;\n    #[cfg(stm32f1)]\n    rcc_assert!(max::ADC.contains(&adc));\n\n    // Set latency based on HCLK frquency\n    #[cfg(stm32f0)]\n    let latency = match hclk.0 {\n        ..=24_000_000 => Latency::WS0,\n        _ => Latency::WS1,\n    };\n    #[cfg(any(stm32f1, stm32f3))]\n    let latency = match hclk.0 {\n        ..=24_000_000 => Latency::WS0,\n        ..=48_000_000 => Latency::WS1,\n        _ => Latency::WS2,\n    };\n    FLASH.acr().modify(|w| {\n        w.set_latency(latency);\n        // RM0316: \"The prefetch buffer must be kept on when using a prescaler\n        // different from 1 on the AHB clock.\", \"Half-cycle access cannot be\n        // used when there is a prescaler different from 1 on the AHB clock\"\n        #[cfg(stm32f3)]\n        if config.ahb_pre != AHBPrescaler::DIV1 {\n            w.set_hlfcya(false);\n            w.set_prftbe(true);\n        }\n        #[cfg(not(stm32f3))]\n        w.set_prftbe(true);\n    });\n\n    // Set prescalers\n    // CFGR has been written before (PLL, PLL48) don't overwrite these settings\n    RCC.cfgr().modify(|w| {\n        #[cfg(not(stm32f0))]\n        {\n            w.set_ppre1(config.apb1_pre);\n            w.set_ppre2(config.apb2_pre);\n        }\n        #[cfg(stm32f0)]\n        w.set_ppre(config.apb1_pre);\n        w.set_hpre(config.ahb_pre);\n        #[cfg(stm32f1)]\n        w.set_adcpre(config.adc_pre);\n    });\n\n    // I2S2 and I2S3\n    #[cfg(any(stm32f105, stm32f107))]\n    {\n        RCC.cfgr2().modify(|w| w.set_i2s2src(config.i2s2_src));\n        RCC.cfgr2().modify(|w| w.set_i2s3src(config.i2s3_src));\n    }\n\n    // Wait for the new prescalers to kick in\n    // \"The clocks are divided with the new prescaler factor from\n    //  1 to 16 AHB cycles after write\"\n    cortex_m::asm::delay(16);\n\n    // CFGR has been written before (PLL, PLL48, clock divider) don't overwrite these settings\n    RCC.cfgr().modify(|w| w.set_sw(config.sys));\n    while RCC.cfgr().read().sws() != config.sys {}\n\n    // Disable HSI if not used\n    if !config.hsi {\n        RCC.cr().modify(|w| w.set_hsion(false));\n    }\n\n    let rtc = config.ls.init();\n\n    // TODO: all this ADC stuff should probably go into the ADC module, not here.\n    // Most STM32s manage ADC clocks in a similar way with ADCx_COMMON.\n    #[cfg(all(stm32f3, not(rcc_f37)))]\n    use crate::pac::adccommon::vals::Ckmode;\n\n    #[cfg(all(stm32f3, not(rcc_f37)))]\n    let adc = {\n        #[cfg(peri_adc1_common)]\n        let common = crate::pac::ADC1_COMMON;\n        #[cfg(peri_adc12_common)]\n        let common = crate::pac::ADC12_COMMON;\n\n        match config.adc {\n            AdcClockSource::Pll(adcpres) => {\n                RCC.cfgr2().modify(|w| w.set_adc12pres(adcpres));\n                common.ccr().modify(|w| w.set_ckmode(Ckmode::ASYNCHRONOUS));\n\n                unwrap!(pll) / adcpres\n            }\n            AdcClockSource::Hclk(adcpres) => {\n                assert!(!(adcpres == AdcHclkPrescaler::Div1 && config.ahb_pre != AHBPrescaler::DIV1));\n\n                let (div, ckmode) = match adcpres {\n                    AdcHclkPrescaler::Div1 => (1u32, Ckmode::SYNC_DIV1),\n                    AdcHclkPrescaler::Div2 => (2u32, Ckmode::SYNC_DIV2),\n                    AdcHclkPrescaler::Div4 => (4u32, Ckmode::SYNC_DIV4),\n                };\n                common.ccr().modify(|w| w.set_ckmode(ckmode));\n\n                hclk / div\n            }\n        }\n    };\n\n    #[cfg(all(stm32f3, not(rcc_f37), any(peri_adc3_common, peri_adc34_common)))]\n    let adc34 = {\n        #[cfg(peri_adc3_common)]\n        let common = crate::pac::ADC3_COMMON;\n        #[cfg(peri_adc34_common)]\n        let common = crate::pac::ADC34_COMMON;\n\n        match config.adc34 {\n            AdcClockSource::Pll(adcpres) => {\n                RCC.cfgr2().modify(|w| w.set_adc34pres(adcpres));\n                common.ccr().modify(|w| w.set_ckmode(Ckmode::ASYNCHRONOUS));\n\n                unwrap!(pll) / adcpres\n            }\n            AdcClockSource::Hclk(adcpres) => {\n                assert!(!(adcpres == AdcHclkPrescaler::Div1 && config.ahb_pre != AHBPrescaler::DIV1));\n\n                let (div, ckmode) = match adcpres {\n                    AdcHclkPrescaler::Div1 => (1u32, Ckmode::SYNC_DIV1),\n                    AdcHclkPrescaler::Div2 => (2u32, Ckmode::SYNC_DIV2),\n                    AdcHclkPrescaler::Div4 => (4u32, Ckmode::SYNC_DIV4),\n                };\n                common.ccr().modify(|w| w.set_ckmode(ckmode));\n\n                hclk / div\n            }\n        }\n    };\n\n    /*\n    TODO: Maybe add something like this to clock_mux? How can we autogenerate the data for this?\n    let hrtim = match config.hrtim {\n        // Must be configured after the bus is ready, otherwise it won't work\n        HrtimClockSource::BusClk => None,\n        HrtimClockSource::PllClk => {\n            use crate::pac::rcc::vals::Timsw;\n\n            // Make sure that we're using the PLL\n            let pll = unwrap!(pll);\n            assert!((pclk2 == pll) || (pclk2 * 2u32 == pll));\n\n            RCC.cfgr3().modify(|w| w.set_hrtim1sw(Timsw::PLL1_P));\n\n            Some(pll * 2u32)\n        }\n    };\n     */\n\n    // Disable the HSI48, if not used\n    #[cfg(crs)]\n    if config.hsi48.is_none() {\n        super::disable_hsi48();\n    }\n\n    config.mux.init();\n\n    set_clocks!(\n        hsi: hsi,\n        hse: hse,\n        pll1_p: pll,\n        sys: Some(sys),\n        pclk1: Some(pclk1),\n        pclk2: Some(pclk2),\n        pclk1_tim: Some(pclk1_tim),\n        pclk2_tim: Some(pclk2_tim),\n        hclk1: Some(hclk),\n        #[cfg(all(stm32f3, not(rcc_f37)))]\n        adc: Some(adc),\n        #[cfg(all(stm32f3, not(rcc_f37), any(peri_adc3_common, peri_adc34_common)))]\n        adc34: Some(adc34),\n        rtc: rtc,\n        hsi48: hsi48,\n        #[cfg(any(rcc_f1, rcc_f1cl, stm32f3))]\n        usb: usb,\n        lse: None,\n    );\n}\n\n#[cfg(stm32f0)]\nmod max {\n    use core::ops::RangeInclusive;\n\n    use crate::time::Hertz;\n\n    pub(crate) const HSE_OSC: RangeInclusive<Hertz> = Hertz(4_000_000)..=Hertz(32_000_000);\n    pub(crate) const HSE_BYP: RangeInclusive<Hertz> = Hertz(1_000_000)..=Hertz(32_000_000);\n\n    pub(crate) const HCLK: RangeInclusive<Hertz> = Hertz(0)..=Hertz(48_000_000);\n    pub(crate) const PCLK1: RangeInclusive<Hertz> = Hertz(0)..=Hertz(48_000_000);\n\n    pub(crate) const PLL_IN: RangeInclusive<Hertz> = Hertz(1_000_000)..=Hertz(24_000_000);\n    pub(crate) const PLL_OUT: RangeInclusive<Hertz> = Hertz(16_000_000)..=Hertz(48_000_000);\n}\n\n#[cfg(stm32f1)]\nmod max {\n    use core::ops::RangeInclusive;\n\n    use crate::time::Hertz;\n\n    #[cfg(not(rcc_f1cl))]\n    pub(crate) const HSE_OSC: RangeInclusive<Hertz> = Hertz(4_000_000)..=Hertz(16_000_000);\n    #[cfg(not(rcc_f1cl))]\n    pub(crate) const HSE_BYP: RangeInclusive<Hertz> = Hertz(1_000_000)..=Hertz(25_000_000);\n\n    #[cfg(rcc_f1cl)]\n    pub(crate) const HSE_OSC: RangeInclusive<Hertz> = Hertz(3_000_000)..=Hertz(25_000_000);\n    #[cfg(rcc_f1cl)]\n    pub(crate) const HSE_BYP: RangeInclusive<Hertz> = Hertz(1_000_000)..=Hertz(50_000_000);\n\n    pub(crate) const HCLK: RangeInclusive<Hertz> = Hertz(0)..=Hertz(72_000_000);\n    pub(crate) const PCLK1: RangeInclusive<Hertz> = Hertz(0)..=Hertz(36_000_000);\n    pub(crate) const PCLK2: RangeInclusive<Hertz> = Hertz(0)..=Hertz(72_000_000);\n\n    pub(crate) const PLL_IN: RangeInclusive<Hertz> = Hertz(1_000_000)..=Hertz(25_000_000);\n    pub(crate) const PLL_OUT: RangeInclusive<Hertz> = Hertz(16_000_000)..=Hertz(72_000_000);\n\n    pub(crate) const ADC: RangeInclusive<Hertz> = Hertz(0)..=Hertz(14_000_000);\n}\n\n#[cfg(stm32f3)]\nmod max {\n    use core::ops::RangeInclusive;\n\n    use crate::time::Hertz;\n\n    pub(crate) const HSE_OSC: RangeInclusive<Hertz> = Hertz(4_000_000)..=Hertz(32_000_000);\n    pub(crate) const HSE_BYP: RangeInclusive<Hertz> = Hertz(1_000_000)..=Hertz(32_000_000);\n\n    pub(crate) const HCLK: RangeInclusive<Hertz> = Hertz(0)..=Hertz(72_000_000);\n    pub(crate) const PCLK1: RangeInclusive<Hertz> = Hertz(0)..=Hertz(36_000_000);\n    pub(crate) const PCLK2: RangeInclusive<Hertz> = Hertz(0)..=Hertz(72_000_000);\n\n    pub(crate) const PLL_IN: RangeInclusive<Hertz> = Hertz(1_000_000)..=Hertz(24_000_000);\n    pub(crate) const PLL_OUT: RangeInclusive<Hertz> = Hertz(16_000_000)..=Hertz(72_000_000);\n}\n"
  },
  {
    "path": "embassy-stm32/src/rcc/f247.rs",
    "content": "use stm32_metapac::flash::vals::Latency;\n\n#[cfg(any(stm32f4, stm32f7))]\nuse crate::pac::PWR;\n#[cfg(any(stm32f413, stm32f423, stm32f412))]\npub use crate::pac::rcc::vals::Plli2ssrc as Plli2sSource;\npub use crate::pac::rcc::vals::{\n    Hpre as AHBPrescaler, Pllm as PllPreDiv, Plln as PllMul, Pllp as PllPDiv, Pllq as PllQDiv, Pllr as PllRDiv,\n    Pllsrc as PllSource, Ppre as APBPrescaler, Sw as Sysclk,\n};\nuse crate::pac::{FLASH, RCC};\nuse crate::time::Hertz;\n\n// TODO: on some F4s, PLLM is shared between all PLLs. Enforce that.\n// TODO: on some F4s, add support for plli2s_src\n//\n//             plli2s  plli2s_m     plli2s_src   pllsai   pllsai_m\n// f401        y       shared\n// f410\n// f411        y       individual\n// f412        y       individual   y\n// f4[12]3     y       individual   y\n// f446        y       individual                y        individual\n// f4[67]9     y       shared                    y        shared\n// f4[23][79]  y       shared                    y        shared\n// f4[01][57]  y       shared\n\n/// HSI speed\npub const HSI_FREQ: Hertz = Hertz(16_000_000);\n\n#[derive(Clone, Copy, Eq, PartialEq)]\npub enum HseMode {\n    /// crystal/ceramic oscillator (HSEBYP=0)\n    Oscillator,\n    /// external analog clock (low swing) (HSEBYP=1)\n    Bypass,\n}\n\n#[derive(Clone, Copy, Eq, PartialEq)]\npub struct Hse {\n    /// HSE frequency.\n    pub freq: Hertz,\n    /// HSE mode.\n    pub mode: HseMode,\n}\n\n#[derive(Clone, Copy)]\npub struct Pll {\n    /// PLL pre-divider (DIVM).\n    pub prediv: PllPreDiv,\n\n    /// PLL multiplication factor.\n    pub mul: PllMul,\n\n    /// PLL P division factor. If None, PLL P output is disabled.\n    pub divp: Option<PllPDiv>,\n    /// PLL Q division factor. If None, PLL Q output is disabled.\n    pub divq: Option<PllQDiv>,\n    /// PLL R division factor. If None, PLL R output is disabled.\n    pub divr: Option<PllRDiv>,\n}\n\n/// Voltage range of the power supply used.\n///\n/// Used to calculate flash waitstates. See\n/// RM0033 - Table 3. Number of wait states according to Cortex®-M3 clock frequency\n#[cfg(stm32f2)]\n#[derive(Clone, Copy)]\npub enum VoltageScale {\n    /// 2.7 to 3.6 V\n    Range0,\n    /// 2.4 to 2.7 V\n    Range1,\n    /// 2.1 to 2.4 V\n    Range2,\n    /// 1.8 to 2.1 V\n    Range3,\n}\n\n/// Configuration of the core clocks\n#[non_exhaustive]\n#[derive(Clone, Copy)]\npub struct Config {\n    pub hsi: bool,\n    pub hse: Option<Hse>,\n    pub sys: Sysclk,\n\n    pub pll_src: PllSource,\n    #[cfg(any(stm32f412, stm32f413, stm32f423))]\n    pub external_i2s_clock: Option<Hertz>,\n\n    pub pll: Option<Pll>,\n    #[cfg(any(stm32f2, all(stm32f4, not(stm32f410)), stm32f7))]\n    pub plli2s: Option<Pll>,\n    #[cfg(any(stm32f446, stm32f427, stm32f437, stm32f4x9, stm32f7))]\n    pub pllsai: Option<Pll>,\n\n    pub ahb_pre: AHBPrescaler,\n    pub apb1_pre: APBPrescaler,\n    pub apb2_pre: APBPrescaler,\n\n    pub ls: super::LsConfig,\n\n    /// Per-peripheral kernel clock selection muxes\n    pub mux: super::mux::ClockMux,\n\n    #[cfg(stm32f2)]\n    pub voltage: VoltageScale,\n}\n\nimpl Config {\n    pub const fn new() -> Self {\n        Self {\n            hsi: true,\n            hse: None,\n            sys: Sysclk::HSI,\n            pll_src: PllSource::HSI,\n            #[cfg(any(stm32f412, stm32f413, stm32f423))]\n            external_i2s_clock: None,\n            pll: None,\n            #[cfg(any(stm32f2, all(stm32f4, not(stm32f410)), stm32f7))]\n            plli2s: None,\n            #[cfg(any(stm32f446, stm32f427, stm32f437, stm32f4x9, stm32f7))]\n            pllsai: None,\n\n            ahb_pre: AHBPrescaler::DIV1,\n            apb1_pre: APBPrescaler::DIV1,\n            apb2_pre: APBPrescaler::DIV1,\n\n            ls: crate::rcc::LsConfig::new(),\n\n            #[cfg(stm32f2)]\n            voltage: VoltageScale::Range3,\n            mux: super::mux::ClockMux::default(),\n        }\n    }\n}\n\nimpl Default for Config {\n    fn default() -> Self {\n        Self::new()\n    }\n}\n\npub(crate) unsafe fn init(config: Config) {\n    // set VOS to SCALE1, if use PLL\n    // TODO: check real clock speed before set VOS\n    #[cfg(any(stm32f4, stm32f7))]\n    if config.pll.is_some() {\n        PWR.cr1().modify(|w| w.set_vos(crate::pac::pwr::vals::Vos::SCALE1));\n    }\n\n    // always enable overdrive for now. Make it configurable in the future.\n    #[cfg(any(stm32f446, stm32f4x9, stm32f427, stm32f437, stm32f7))]\n    {\n        PWR.cr1().modify(|w| w.set_oden(true));\n        while !PWR.csr1().read().odrdy() {}\n\n        PWR.cr1().modify(|w| w.set_odswen(true));\n        while !PWR.csr1().read().odswrdy() {}\n    }\n\n    // Turn on the HSI\n    RCC.cr().modify(|w| w.set_hsion(true));\n    while !RCC.cr().read().hsirdy() {}\n\n    // Use the HSI clock as system clock during the actual clock setup\n    RCC.cfgr().modify(|w| w.set_sw(Sysclk::HSI));\n    while RCC.cfgr().read().sws() != Sysclk::HSI {}\n\n    // Configure HSI\n    let hsi = match config.hsi {\n        false => None,\n        true => Some(HSI_FREQ),\n    };\n\n    // Configure HSE\n    let hse = match config.hse {\n        None => {\n            RCC.cr().modify(|w| w.set_hseon(false));\n            None\n        }\n        Some(hse) => {\n            match hse.mode {\n                HseMode::Bypass => rcc_assert!(max::HSE_BYP.contains(&hse.freq)),\n                HseMode::Oscillator => rcc_assert!(max::HSE_OSC.contains(&hse.freq)),\n            }\n\n            RCC.cr().modify(|w| w.set_hsebyp(hse.mode != HseMode::Oscillator));\n            RCC.cr().modify(|w| w.set_hseon(true));\n            while !RCC.cr().read().hserdy() {}\n            Some(hse.freq)\n        }\n    };\n\n    // Configure PLLs.\n    let pll_input = PllInput {\n        hse,\n        hsi,\n        #[cfg(any(stm32f412, stm32f413, stm32f423))]\n        external: config.external_i2s_clock,\n        source: config.pll_src,\n    };\n    let pll = config.pll.map_or_else(\n        || {\n            pll_enable(PllInstance::Pll, false);\n            PllOutput::default()\n        },\n        |c| init_pll(PllInstance::Pll, Some(c), &pll_input),\n    );\n    #[cfg(any(stm32f2, all(stm32f4, not(stm32f410)), stm32f7))]\n    let plli2s = config.plli2s.map_or_else(\n        || {\n            pll_enable(PllInstance::Plli2s, false);\n            PllOutput::default()\n        },\n        |c| init_pll(PllInstance::Plli2s, Some(c), &pll_input),\n    );\n    #[cfg(any(stm32f446, stm32f427, stm32f437, stm32f4x9, stm32f7))]\n    let pllsai = config.pllsai.map_or_else(\n        || {\n            pll_enable(PllInstance::Pllsai, false);\n            PllOutput::default()\n        },\n        |c| init_pll(PllInstance::Pllsai, Some(c), &pll_input),\n    );\n\n    // Configure sysclk\n    let sys = match config.sys {\n        Sysclk::HSI => unwrap!(hsi),\n        Sysclk::HSE => unwrap!(hse),\n        Sysclk::PLL1_P => unwrap!(pll.p),\n        _ => unreachable!(),\n    };\n\n    let hclk = sys / config.ahb_pre;\n    let (pclk1, pclk1_tim) = super::util::calc_pclk(hclk, config.apb1_pre);\n    let (pclk2, pclk2_tim) = super::util::calc_pclk(hclk, config.apb2_pre);\n\n    rcc_assert!(max::SYSCLK.contains(&sys));\n    rcc_assert!(max::HCLK.contains(&hclk));\n    rcc_assert!(max::PCLK1.contains(&pclk1));\n    rcc_assert!(max::PCLK2.contains(&pclk2));\n\n    let rtc = config.ls.init();\n\n    #[cfg(stm32f2)]\n    let latency = match (config.voltage, hclk.0) {\n        (VoltageScale::Range3, ..=16_000_000) => Latency::WS0,\n        (VoltageScale::Range3, ..=32_000_000) => Latency::WS1,\n        (VoltageScale::Range3, ..=48_000_000) => Latency::WS2,\n        (VoltageScale::Range3, ..=64_000_000) => Latency::WS3,\n        (VoltageScale::Range3, ..=80_000_000) => Latency::WS4,\n        (VoltageScale::Range3, ..=96_000_000) => Latency::WS5,\n        (VoltageScale::Range3, ..=112_000_000) => Latency::WS6,\n        (VoltageScale::Range3, ..=120_000_000) => Latency::WS7,\n        (VoltageScale::Range2, ..=18_000_000) => Latency::WS0,\n        (VoltageScale::Range2, ..=36_000_000) => Latency::WS1,\n        (VoltageScale::Range2, ..=54_000_000) => Latency::WS2,\n        (VoltageScale::Range2, ..=72_000_000) => Latency::WS3,\n        (VoltageScale::Range2, ..=90_000_000) => Latency::WS4,\n        (VoltageScale::Range2, ..=108_000_000) => Latency::WS5,\n        (VoltageScale::Range2, ..=120_000_000) => Latency::WS6,\n        (VoltageScale::Range1, ..=24_000_000) => Latency::WS0,\n        (VoltageScale::Range1, ..=48_000_000) => Latency::WS1,\n        (VoltageScale::Range1, ..=72_000_000) => Latency::WS2,\n        (VoltageScale::Range1, ..=96_000_000) => Latency::WS3,\n        (VoltageScale::Range1, ..=120_000_000) => Latency::WS4,\n        (VoltageScale::Range0, ..=30_000_000) => Latency::WS0,\n        (VoltageScale::Range0, ..=60_000_000) => Latency::WS1,\n        (VoltageScale::Range0, ..=90_000_000) => Latency::WS2,\n        (VoltageScale::Range0, ..=120_000_000) => Latency::WS3,\n        _ => unreachable!(),\n    };\n\n    #[cfg(any(stm32f4, stm32f7))]\n    let latency = {\n        // Be conservative with voltage ranges\n        const FLASH_LATENCY_STEP: u32 = 30_000_000;\n\n        let latency = (hclk.0 - 1) / FLASH_LATENCY_STEP;\n        debug!(\"flash: latency={}\", latency);\n\n        Latency::from_bits(latency as u8)\n    };\n\n    FLASH.acr().write(|w| w.set_latency(latency));\n    while FLASH.acr().read().latency() != latency {}\n\n    RCC.cfgr().modify(|w| {\n        w.set_sw(config.sys);\n        w.set_hpre(config.ahb_pre);\n        w.set_ppre1(config.apb1_pre);\n        w.set_ppre2(config.apb2_pre);\n    });\n    while RCC.cfgr().read().sws() != config.sys {}\n\n    // Disable HSI if not used\n    if !config.hsi {\n        RCC.cr().modify(|w| w.set_hsion(false));\n    }\n\n    config.mux.init();\n\n    set_clocks!(\n        hsi: hsi,\n        hse: hse,\n        lse: None, // TODO\n        lsi: None, // TODO\n        sys: Some(sys),\n        hclk1: Some(hclk),\n        hclk2: Some(hclk),\n        hclk3: Some(hclk),\n        pclk1: Some(pclk1),\n        pclk2: Some(pclk2),\n        pclk1_tim: Some(pclk1_tim),\n        pclk2_tim: Some(pclk2_tim),\n        rtc: rtc,\n        pll1_q: pll.q,\n        pll1_r: pll.r,\n\n        #[cfg(any(stm32f2, all(stm32f4, not(stm32f410)), stm32f7))]\n        plli2s1_p: plli2s.p,\n        #[cfg(any(stm32f2, all(stm32f4, not(stm32f410)), stm32f7))]\n        plli2s1_q: plli2s.q,\n        #[cfg(any(stm32f2, all(stm32f4, not(stm32f410)), stm32f7))]\n        plli2s1_r: plli2s.r,\n\n        #[cfg(any(stm32f446, stm32f427, stm32f437, stm32f4x9, stm32f7))]\n        pllsai1_p: pllsai.p,\n        #[cfg(any(stm32f446, stm32f427, stm32f437, stm32f4x9, stm32f7))]\n        pllsai1_q: pllsai.q,\n        #[cfg(any(stm32f446, stm32f427, stm32f437, stm32f4x9, stm32f7))]\n        pllsai1_r: pllsai.r,\n\n        // TODO workaround until f4 rcc is fixed in stm32-data\n        #[cfg(not(any(stm32f446, stm32f427, stm32f437, stm32f4x9, stm32f7)))]\n        pllsai1_q: None,\n\n        #[cfg(dsihost)]\n        dsi_phy: None, // DSI PLL clock not supported, don't call `RccPeripheral::frequency()` in the drivers\n\n        hsi_hse: None,\n        afif: None,\n    );\n}\n\nstruct PllInput {\n    source: PllSource,\n    hsi: Option<Hertz>,\n    hse: Option<Hertz>,\n    #[cfg(any(stm32f412, stm32f413, stm32f423))]\n    external: Option<Hertz>,\n}\n\n#[derive(Default)]\n#[allow(unused)]\nstruct PllOutput {\n    p: Option<Hertz>,\n    q: Option<Hertz>,\n    r: Option<Hertz>,\n}\n\n#[derive(PartialEq, Eq, Clone, Copy)]\nenum PllInstance {\n    Pll,\n    #[cfg(any(stm32f2, all(stm32f4, not(stm32f410)), stm32f7))]\n    Plli2s,\n    #[cfg(any(stm32f446, stm32f427, stm32f437, stm32f4x9, stm32f7))]\n    Pllsai,\n}\n\nfn pll_enable(instance: PllInstance, enabled: bool) {\n    match instance {\n        PllInstance::Pll => {\n            RCC.cr().modify(|w| w.set_pllon(enabled));\n            while RCC.cr().read().pllrdy() != enabled {}\n        }\n        #[cfg(any(stm32f2, all(stm32f4, not(stm32f410)), stm32f7))]\n        PllInstance::Plli2s => {\n            RCC.cr().modify(|w| w.set_plli2son(enabled));\n            while RCC.cr().read().plli2srdy() != enabled {}\n        }\n        #[cfg(any(stm32f446, stm32f427, stm32f437, stm32f4x9, stm32f7))]\n        PllInstance::Pllsai => {\n            RCC.cr().modify(|w| w.set_pllsaion(enabled));\n            while RCC.cr().read().pllsairdy() != enabled {}\n        }\n    }\n}\n\nfn init_pll(instance: PllInstance, config: Option<Pll>, input: &PllInput) -> PllOutput {\n    // Disable PLL\n    pll_enable(instance, false);\n\n    let Some(pll) = config else { return PllOutput::default() };\n\n    #[cfg(not(any(stm32f412, stm32f413, stm32f423)))]\n    let pll_src = match input.source {\n        PllSource::HSE => input.hse,\n        PllSource::HSI => input.hsi,\n    };\n    #[cfg(any(stm32f412, stm32f413, stm32f423))]\n    let pll_src = match (input.source, input.external) {\n        (PllSource::HSE, None) => input.hse,\n        (PllSource::HSI, None) => input.hsi,\n        (_, Some(ext)) => Some(ext),\n    };\n\n    let pll_src = pll_src.unwrap();\n\n    let in_freq = pll_src / pll.prediv;\n    assert!(max::PLL_IN.contains(&in_freq));\n    let vco_freq = in_freq * pll.mul;\n    assert!(max::PLL_VCO.contains(&vco_freq));\n\n    // stm32f2 plls are like swiss cheese\n    #[cfg(stm32f2)]\n    match instance {\n        PllInstance::Pll => {\n            assert!(pll.divr.is_none());\n        }\n        PllInstance::Plli2s => {\n            assert!(pll.divp.is_none());\n            assert!(pll.divq.is_none());\n        }\n    }\n\n    let p = pll.divp.map(|div| vco_freq / div);\n    let q = pll.divq.map(|div| vco_freq / div);\n    let r = pll.divr.map(|div| vco_freq / div);\n\n    macro_rules! write_fields {\n        ($w:ident) => {\n            $w.set_plln(pll.mul);\n            if let Some(divp) = pll.divp {\n                $w.set_pllp(divp);\n            }\n            if let Some(divq) = pll.divq {\n                $w.set_pllq(divq);\n            }\n            #[cfg(any(stm32f4, stm32f7))]\n            if let Some(divr) = pll.divr {\n                $w.set_pllr(divr);\n            }\n        };\n    }\n\n    match instance {\n        PllInstance::Pll => RCC.pllcfgr().write(|w| {\n            w.set_pllm(pll.prediv);\n            w.set_pllsrc(input.source);\n            write_fields!(w);\n        }),\n        #[cfg(any(all(stm32f4, not(stm32f410)), stm32f7))]\n        PllInstance::Plli2s => RCC.plli2scfgr().write(|w| {\n            #[cfg(any(stm32f411, stm32f412, stm32f413, stm32f423, stm32f446))]\n            w.set_pllm(pll.prediv);\n            #[cfg(any(stm32f412, stm32f413, stm32f423))]\n            {\n                let plli2ssource = match input.external {\n                    Some(_) => Plli2sSource::EXTERNAL,\n                    None => Plli2sSource::HSE_HSI,\n                };\n                w.set_plli2ssrc(plli2ssource);\n            }\n\n            write_fields!(w);\n        }),\n        #[cfg(stm32f2)]\n        PllInstance::Plli2s => RCC.plli2scfgr().write(|w| {\n            if let Some(divr) = pll.divr {\n                w.set_pllr(divr);\n            }\n        }),\n        #[cfg(any(stm32f446, stm32f427, stm32f437, stm32f4x9, stm32f7))]\n        PllInstance::Pllsai => RCC.pllsaicfgr().write(|w| {\n            write_fields!(w);\n        }),\n    }\n\n    // Enable PLL\n    pll_enable(instance, true);\n\n    PllOutput { p, q, r }\n}\n\n#[cfg(stm32f7)]\nmod max {\n    use core::ops::RangeInclusive;\n\n    use crate::time::Hertz;\n\n    pub(crate) const HSE_OSC: RangeInclusive<Hertz> = Hertz(4_000_000)..=Hertz(26_000_000);\n    pub(crate) const HSE_BYP: RangeInclusive<Hertz> = Hertz(1_000_000)..=Hertz(50_000_000);\n\n    pub(crate) const SYSCLK: RangeInclusive<Hertz> = Hertz(12_500_000)..=Hertz(216_000_000);\n    pub(crate) const HCLK: RangeInclusive<Hertz> = Hertz(12_500_000)..=Hertz(216_000_000);\n    pub(crate) const PCLK1: RangeInclusive<Hertz> = Hertz(12_500_000)..=Hertz(216_000_000 / 4);\n    pub(crate) const PCLK2: RangeInclusive<Hertz> = Hertz(12_500_000)..=Hertz(216_000_000 / 2);\n\n    pub(crate) const PLL_IN: RangeInclusive<Hertz> = Hertz(1_000_000)..=Hertz(2_100_000);\n    pub(crate) const PLL_VCO: RangeInclusive<Hertz> = Hertz(100_000_000)..=Hertz(432_000_000);\n}\n\n#[cfg(stm32f4)]\nmod max {\n    use core::ops::RangeInclusive;\n\n    use crate::time::Hertz;\n\n    pub(crate) const HSE_OSC: RangeInclusive<Hertz> = Hertz(4_000_000)..=Hertz(26_000_000);\n    pub(crate) const HSE_BYP: RangeInclusive<Hertz> = Hertz(1_000_000)..=Hertz(50_000_000);\n\n    #[cfg(stm32f401)]\n    pub(crate) const SYSCLK: RangeInclusive<Hertz> = Hertz(0)..=Hertz(84_000_000);\n    #[cfg(any(stm32f405, stm32f407, stm32f415, stm32f417,))]\n    pub(crate) const SYSCLK: RangeInclusive<Hertz> = Hertz(0)..=Hertz(168_000_000);\n    #[cfg(any(stm32f410, stm32f411, stm32f412, stm32f413, stm32f423,))]\n    pub(crate) const SYSCLK: RangeInclusive<Hertz> = Hertz(0)..=Hertz(100_000_000);\n    #[cfg(any(stm32f427, stm32f429, stm32f437, stm32f439, stm32f446, stm32f469, stm32f479,))]\n    pub(crate) const SYSCLK: RangeInclusive<Hertz> = Hertz(0)..=Hertz(180_000_000);\n\n    pub(crate) const HCLK: RangeInclusive<Hertz> = Hertz(0)..=Hertz(SYSCLK.end().0);\n\n    pub(crate) const PCLK1: RangeInclusive<Hertz> = Hertz(0)..=Hertz(PCLK2.end().0 / 2);\n\n    #[cfg(any(stm32f401, stm32f410, stm32f411, stm32f412, stm32f413, stm32f423,))]\n    pub(crate) const PCLK2: RangeInclusive<Hertz> = Hertz(0)..=Hertz(HCLK.end().0);\n    #[cfg(not(any(stm32f401, stm32f410, stm32f411, stm32f412, stm32f413, stm32f423,)))]\n    pub(crate) const PCLK2: RangeInclusive<Hertz> = Hertz(0)..=Hertz(HCLK.end().0 / 2);\n\n    pub(crate) const PLL_IN: RangeInclusive<Hertz> = Hertz(1_000_000)..=Hertz(2_100_000);\n    pub(crate) const PLL_VCO: RangeInclusive<Hertz> = Hertz(100_000_000)..=Hertz(432_000_000);\n}\n\n#[cfg(stm32f2)]\nmod max {\n    use core::ops::RangeInclusive;\n\n    use crate::time::Hertz;\n\n    pub(crate) const HSE_OSC: RangeInclusive<Hertz> = Hertz(4_000_000)..=Hertz(26_000_000);\n    pub(crate) const HSE_BYP: RangeInclusive<Hertz> = Hertz(1_000_000)..=Hertz(26_000_000);\n\n    pub(crate) const SYSCLK: RangeInclusive<Hertz> = Hertz(0)..=Hertz(120_000_000);\n\n    pub(crate) const HCLK: RangeInclusive<Hertz> = Hertz(0)..=Hertz(SYSCLK.end().0);\n    pub(crate) const PCLK1: RangeInclusive<Hertz> = Hertz(0)..=Hertz(SYSCLK.end().0 / 4);\n    pub(crate) const PCLK2: RangeInclusive<Hertz> = Hertz(0)..=Hertz(SYSCLK.end().0 / 2);\n\n    pub(crate) const PLL_IN: RangeInclusive<Hertz> = Hertz(0_950_000)..=Hertz(2_100_000);\n    pub(crate) const PLL_VCO: RangeInclusive<Hertz> = Hertz(192_000_000)..=Hertz(432_000_000);\n}\n"
  },
  {
    "path": "embassy-stm32/src/rcc/g0.rs",
    "content": "use crate::pac::flash::vals::Latency;\npub use crate::pac::pwr::vals::Vos as VoltageRange;\npub use crate::pac::rcc::vals::{\n    Hpre as AHBPrescaler, Hsidiv as HsiSysDiv, Pllm as PllPreDiv, Plln as PllMul, Pllp as PllPDiv, Pllq as PllQDiv,\n    Pllr as PllRDiv, Pllsrc as PllSource, Ppre as APBPrescaler, Sw as Sysclk,\n};\nuse crate::pac::{FLASH, PWR, RCC};\nuse crate::rcc::LSI_FREQ;\nuse crate::time::Hertz;\n\n/// HSI speed\npub const HSI_FREQ: Hertz = Hertz(16_000_000);\n\n/// HSE Mode\n#[derive(Clone, Copy, Eq, PartialEq)]\npub enum HseMode {\n    /// crystal/ceramic oscillator (HSEBYP=0)\n    Oscillator,\n    /// external analog clock (low swing) (HSEBYP=1)\n    Bypass,\n}\n\n/// HSE Configuration\n#[derive(Clone, Copy, Eq, PartialEq)]\npub struct Hse {\n    /// HSE frequency.\n    pub freq: Hertz,\n    /// HSE mode.\n    pub mode: HseMode,\n}\n\n#[derive(Clone, Copy, Eq, PartialEq)]\npub struct Hsi {\n    /// Division factor for HSISYS clock. Default is 1.\n    pub sys_div: HsiSysDiv,\n}\n\n/// PLL Configuration\n///\n/// Use this struct to configure the PLL source, input frequency, multiplication factor, and output\n/// dividers. Be sure to keep check the datasheet for your specific part for the appropriate\n/// frequency ranges for each of these settings.\n#[derive(Clone, Copy)]\npub struct Pll {\n    /// PLL Source clock selection.\n    pub source: PllSource,\n\n    /// PLL pre-divider\n    pub prediv: PllPreDiv,\n\n    /// PLL multiplication factor for VCO\n    pub mul: PllMul,\n\n    /// PLL division factor for P clock (ADC Clock)\n    pub divp: Option<PllPDiv>,\n\n    /// PLL division factor for Q clock (USB, I2S23, SAI1, FDCAN, QSPI)\n    pub divq: Option<PllQDiv>,\n\n    /// PLL division factor for R clock (SYSCLK)\n    pub divr: Option<PllRDiv>,\n}\n\n/// Clocks configutation\n#[non_exhaustive]\n#[derive(Clone, Copy)]\npub struct Config {\n    /// HSI Configuration\n    pub hsi: Option<Hsi>,\n\n    /// HSE Configuration\n    pub hse: Option<Hse>,\n\n    /// System Clock Configuration\n    pub sys: Sysclk,\n\n    /// HSI48 Configuration\n    #[cfg(crs)]\n    pub hsi48: Option<super::Hsi48Config>,\n\n    /// PLL Configuration\n    pub pll: Option<Pll>,\n\n    /// If PLL is requested as the main clock source in the `sys` field then the PLL configuration\n    /// MUST turn on the PLLR output.\n    pub ahb_pre: AHBPrescaler,\n    pub apb1_pre: APBPrescaler,\n\n    /// Low-Speed Clock Configuration\n    pub ls: super::LsConfig,\n\n    pub low_power_run: bool,\n\n    pub voltage_range: VoltageRange,\n\n    /// Per-peripheral kernel clock selection muxes\n    pub mux: super::mux::ClockMux,\n}\n\nimpl Config {\n    pub const fn new() -> Self {\n        Config {\n            hsi: Some(Hsi {\n                sys_div: HsiSysDiv::DIV1,\n            }),\n            hse: None,\n            sys: Sysclk::HSI,\n            #[cfg(crs)]\n            hsi48: Some(crate::rcc::Hsi48Config::new()),\n            pll: None,\n            ahb_pre: AHBPrescaler::DIV1,\n            apb1_pre: APBPrescaler::DIV1,\n            low_power_run: false,\n            ls: crate::rcc::LsConfig::new(),\n            voltage_range: VoltageRange::RANGE1,\n            mux: super::mux::ClockMux::default(),\n        }\n    }\n}\n\nimpl Default for Config {\n    fn default() -> Config {\n        Self::new()\n    }\n}\n\n#[derive(Default)]\npub struct PllFreq {\n    pub pll_p: Option<Hertz>,\n    pub pll_q: Option<Hertz>,\n    pub pll_r: Option<Hertz>,\n}\n\npub(crate) unsafe fn init(config: Config) {\n    // Turn on the HSI\n    RCC.cr().modify(|w| {\n        w.set_hsion(true);\n        if let Some(hsi) = config.hsi {\n            w.set_hsidiv(hsi.sys_div);\n        }\n    });\n    while !RCC.cr().read().hsirdy() {}\n\n    // Use the HSI clock as system clock during the actual clock setup\n    RCC.cfgr().modify(|w| w.set_sw(Sysclk::HSI));\n    while RCC.cfgr().read().sws() != Sysclk::HSI {}\n\n    // Configure HSI\n    let (hsi, hsisys) = match config.hsi {\n        None => (None, None),\n        Some(hsi) => (Some(HSI_FREQ), Some(HSI_FREQ / hsi.sys_div)),\n    };\n\n    // Configure HSE\n    let hse = match config.hse {\n        None => {\n            RCC.cr().modify(|w| w.set_hseon(false));\n            None\n        }\n        Some(hse) => {\n            match hse.mode {\n                HseMode::Bypass => rcc_assert!(max::HSE_BYP.contains(&hse.freq)),\n                HseMode::Oscillator => rcc_assert!(max::HSE_OSC.contains(&hse.freq)),\n            }\n\n            RCC.cr().modify(|w| w.set_hsebyp(hse.mode != HseMode::Oscillator));\n            RCC.cr().modify(|w| w.set_hseon(true));\n            while !RCC.cr().read().hserdy() {}\n            Some(hse.freq)\n        }\n    };\n\n    // Configure HSI48 if required\n    #[cfg(crs)]\n    let hsi48 = config.hsi48.map(super::init_hsi48);\n\n    let pll = config\n        .pll\n        .map(|pll_config| {\n            let src_freq = match pll_config.source {\n                PllSource::HSI => unwrap!(hsi),\n                PllSource::HSE => unwrap!(hse),\n                _ => unreachable!(),\n            };\n\n            // Disable PLL before configuration\n            RCC.cr().modify(|w| w.set_pllon(false));\n            while RCC.cr().read().pllrdy() {}\n\n            let in_freq = src_freq / pll_config.prediv;\n            rcc_assert!(max::PLL_IN.contains(&in_freq));\n            let internal_freq = in_freq * pll_config.mul;\n            rcc_assert!(max::PLL_VCO.contains(&internal_freq));\n\n            RCC.pllcfgr().write(|w| {\n                w.set_plln(pll_config.mul);\n                w.set_pllm(pll_config.prediv);\n                w.set_pllsrc(pll_config.source.into());\n            });\n\n            let pll_p_freq = pll_config.divp.map(|div_p| {\n                RCC.pllcfgr().modify(|w| {\n                    w.set_pllp(div_p);\n                    w.set_pllpen(true);\n                });\n                let freq = internal_freq / div_p;\n                rcc_assert!(max::PLL_P.contains(&freq));\n                freq\n            });\n\n            let pll_q_freq = pll_config.divq.map(|div_q| {\n                RCC.pllcfgr().modify(|w| {\n                    w.set_pllq(div_q);\n                    w.set_pllqen(true);\n                });\n                let freq = internal_freq / div_q;\n                rcc_assert!(max::PLL_Q.contains(&freq));\n                freq\n            });\n\n            let pll_r_freq = pll_config.divr.map(|div_r| {\n                RCC.pllcfgr().modify(|w| {\n                    w.set_pllr(div_r);\n                    w.set_pllren(true);\n                });\n                let freq = internal_freq / div_r;\n                rcc_assert!(max::PLL_R.contains(&freq));\n                freq\n            });\n\n            // Enable the PLL\n            RCC.cr().modify(|w| w.set_pllon(true));\n            while !RCC.cr().read().pllrdy() {}\n\n            PllFreq {\n                pll_p: pll_p_freq,\n                pll_q: pll_q_freq,\n                pll_r: pll_r_freq,\n            }\n        })\n        .unwrap_or_default();\n\n    let rtc = config.ls.init();\n\n    let sys = match config.sys {\n        Sysclk::HSI => unwrap!(hsisys),\n        Sysclk::HSE => unwrap!(hse),\n        Sysclk::PLL1_R => unwrap!(pll.pll_r),\n        Sysclk::LSI => {\n            assert!(config.ls.lsi);\n            LSI_FREQ\n        }\n        Sysclk::LSE => unwrap!(config.ls.lse).frequency,\n        _ => unreachable!(),\n    };\n\n    rcc_assert!(max::SYSCLK.contains(&sys));\n\n    // Calculate the AHB frequency (HCLK), among other things so we can calculate the correct flash read latency.\n    let hclk = sys / config.ahb_pre;\n    rcc_assert!(max::HCLK.contains(&hclk));\n\n    let (pclk1, pclk1_tim) = super::util::calc_pclk(hclk, config.apb1_pre);\n    rcc_assert!(max::PCLK.contains(&pclk1));\n\n    let latency = match (config.voltage_range, hclk.0) {\n        (VoltageRange::RANGE1, ..=24_000_000) => Latency::WS0,\n        (VoltageRange::RANGE1, ..=48_000_000) => Latency::WS1,\n        (VoltageRange::RANGE1, _) => Latency::WS2,\n        (VoltageRange::RANGE2, ..=8_000_000) => Latency::WS0,\n        (VoltageRange::RANGE2, ..=16_000_000) => Latency::WS1,\n        (VoltageRange::RANGE2, _) => Latency::WS2,\n        _ => unreachable!(),\n    };\n\n    // Configure flash read access latency based on voltage scale and frequency (RM0444 3.3.4)\n    FLASH.acr().modify(|w| {\n        w.set_latency(latency);\n    });\n\n    // Spin until the effective flash latency is set.\n    while FLASH.acr().read().latency() != latency {}\n\n    // Now that boost mode and flash read access latency are configured, set up SYSCLK\n    RCC.cfgr().modify(|w| {\n        w.set_sw(config.sys);\n        w.set_hpre(config.ahb_pre);\n        w.set_ppre(config.apb1_pre);\n    });\n    while RCC.cfgr().read().sws() != config.sys {}\n\n    // Disable HSI if not used\n    if config.hsi.is_none() {\n        RCC.cr().modify(|w| w.set_hsion(false));\n    }\n\n    // Disable the HSI48, if not used\n    #[cfg(crs)]\n    if config.hsi48.is_none() {\n        super::disable_hsi48();\n    }\n\n    if config.low_power_run {\n        assert!(sys <= Hertz(2_000_000));\n        PWR.cr1().modify(|w| w.set_lpr(true));\n    }\n\n    config.mux.init();\n\n    set_clocks!(\n        sys: Some(sys),\n        hclk1: Some(hclk),\n        pclk1: Some(pclk1),\n        pclk1_tim: Some(pclk1_tim),\n        pll1_p: pll.pll_p,\n        pll1_q: pll.pll_q,\n        pll1_r: pll.pll_r,\n        hsi: hsi,\n        hse: hse,\n        #[cfg(crs)]\n        hsi48: hsi48,\n        rtc: rtc,\n\n        // TODO\n        lsi: None,\n        lse: None,\n    );\n}\n\nmod max {\n    use core::ops::RangeInclusive;\n\n    use crate::time::Hertz;\n\n    pub(crate) const HSE_OSC: RangeInclusive<Hertz> = Hertz(4_000_000)..=Hertz(48_000_000);\n    pub(crate) const HSE_BYP: RangeInclusive<Hertz> = Hertz(0)..=Hertz(48_000_000);\n    pub(crate) const SYSCLK: RangeInclusive<Hertz> = Hertz(0)..=Hertz(64_000_000);\n    pub(crate) const PCLK: RangeInclusive<Hertz> = Hertz(8)..=Hertz(64_000_000);\n    pub(crate) const HCLK: RangeInclusive<Hertz> = Hertz(0)..=Hertz(64_000_000);\n    pub(crate) const PLL_IN: RangeInclusive<Hertz> = Hertz(2_660_000)..=Hertz(16_000_000);\n    pub(crate) const PLL_VCO: RangeInclusive<Hertz> = Hertz(96_000_000)..=Hertz(344_000_000);\n    pub(crate) const PLL_P: RangeInclusive<Hertz> = Hertz(3_090_000)..=Hertz(122_000_000);\n    pub(crate) const PLL_Q: RangeInclusive<Hertz> = Hertz(12_000_000)..=Hertz(128_000_000);\n    pub(crate) const PLL_R: RangeInclusive<Hertz> = Hertz(12_000_000)..=Hertz(64_000_000);\n}\n"
  },
  {
    "path": "embassy-stm32/src/rcc/g4.rs",
    "content": "use crate::pac::flash::vals::Latency;\npub use crate::pac::rcc::vals::{\n    Hpre as AHBPrescaler, Pllm as PllPreDiv, Plln as PllMul, Pllp as PllPDiv, Pllq as PllQDiv, Pllr as PllRDiv,\n    Pllsrc as PllSource, Ppre as APBPrescaler, Sw as Sysclk,\n};\nuse crate::pac::{FLASH, PWR, RCC};\nuse crate::time::Hertz;\n\n/// HSI speed\npub const HSI_FREQ: Hertz = Hertz(16_000_000);\n\n/// HSE Mode\n#[derive(Clone, Copy, Eq, PartialEq)]\npub enum HseMode {\n    /// crystal/ceramic oscillator (HSEBYP=0)\n    Oscillator,\n    /// external analog clock (low swing) (HSEBYP=1)\n    Bypass,\n}\n\n/// HSE Configuration\n#[derive(Clone, Copy, Eq, PartialEq)]\npub struct Hse {\n    /// HSE frequency.\n    pub freq: Hertz,\n    /// HSE mode.\n    pub mode: HseMode,\n}\n\n/// PLL Configuration\n///\n/// Use this struct to configure the PLL source, input frequency, multiplication factor, and output\n/// dividers. Be sure to keep check the datasheet for your specific part for the appropriate\n/// frequency ranges for each of these settings.\n#[derive(Clone, Copy)]\npub struct Pll {\n    /// PLL Source clock selection.\n    pub source: PllSource,\n\n    /// PLL pre-divider\n    pub prediv: PllPreDiv,\n\n    /// PLL multiplication factor for VCO\n    pub mul: PllMul,\n\n    /// PLL division factor for P clock (ADC Clock)\n    pub divp: Option<PllPDiv>,\n\n    /// PLL division factor for Q clock (USB, I2S23, SAI1, FDCAN, QSPI)\n    pub divq: Option<PllQDiv>,\n\n    /// PLL division factor for R clock (SYSCLK)\n    pub divr: Option<PllRDiv>,\n}\n\n/// Clocks configutation\n#[non_exhaustive]\n#[derive(Clone, Copy)]\npub struct Config {\n    /// HSI Enable\n    pub hsi: bool,\n\n    /// HSE Configuration\n    pub hse: Option<Hse>,\n\n    /// System Clock Configuration\n    pub sys: Sysclk,\n\n    /// HSI48 Configuration\n    pub hsi48: Option<super::Hsi48Config>,\n\n    /// PLL Configuration\n    pub pll: Option<Pll>,\n\n    /// If PLL is requested as the main clock source in the `sys` field then the PLL configuration\n    /// MUST turn on the PLLR output.\n    pub ahb_pre: AHBPrescaler,\n    pub apb1_pre: APBPrescaler,\n    pub apb2_pre: APBPrescaler,\n\n    pub low_power_run: bool,\n\n    /// Low-Speed Clock Configuration\n    pub ls: super::LsConfig,\n\n    /// Enable range1 boost mode\n    /// Recommended when the SYSCLK frequency is greater than 150MHz.\n    pub boost: bool,\n\n    /// Per-peripheral kernel clock selection muxes\n    pub mux: super::mux::ClockMux,\n}\n\nimpl Config {\n    pub const fn new() -> Self {\n        Config {\n            hsi: true,\n            hse: None,\n            sys: Sysclk::HSI,\n            hsi48: Some(crate::rcc::Hsi48Config::new()),\n            pll: None,\n            ahb_pre: AHBPrescaler::DIV1,\n            apb1_pre: APBPrescaler::DIV1,\n            apb2_pre: APBPrescaler::DIV1,\n            low_power_run: false,\n            ls: crate::rcc::LsConfig::new(),\n            boost: false,\n            mux: super::mux::ClockMux::default(),\n        }\n    }\n}\n\nimpl Default for Config {\n    fn default() -> Config {\n        Self::new()\n    }\n}\n\n#[derive(Default)]\npub struct PllFreq {\n    pub pll_p: Option<Hertz>,\n    pub pll_q: Option<Hertz>,\n    pub pll_r: Option<Hertz>,\n}\n\npub(crate) unsafe fn init(config: Config) {\n    // Turn on the HSI\n    RCC.cr().modify(|w| w.set_hsion(true));\n    while !RCC.cr().read().hsirdy() {}\n\n    // Use the HSI clock as system clock during the actual clock setup\n    RCC.cfgr().modify(|w| w.set_sw(Sysclk::HSI));\n    while RCC.cfgr().read().sws() != Sysclk::HSI {}\n\n    // Configure HSI\n    let hsi = match config.hsi {\n        false => None,\n        true => Some(HSI_FREQ),\n    };\n\n    // Configure HSE\n    let hse = match config.hse {\n        None => {\n            RCC.cr().modify(|w| w.set_hseon(false));\n            None\n        }\n        Some(hse) => {\n            match hse.mode {\n                HseMode::Bypass => rcc_assert!(max::HSE_BYP.contains(&hse.freq)),\n                HseMode::Oscillator => rcc_assert!(max::HSE_OSC.contains(&hse.freq)),\n            }\n\n            RCC.cr().modify(|w| w.set_hsebyp(hse.mode != HseMode::Oscillator));\n            RCC.cr().modify(|w| w.set_hseon(true));\n            while !RCC.cr().read().hserdy() {}\n            Some(hse.freq)\n        }\n    };\n\n    // Configure HSI48 if required\n    let hsi48 = config.hsi48.map(super::init_hsi48);\n\n    let pll = config\n        .pll\n        .map(|pll_config| {\n            let src_freq = match pll_config.source {\n                PllSource::HSI => unwrap!(hsi),\n                PllSource::HSE => unwrap!(hse),\n                _ => unreachable!(),\n            };\n\n            // Disable PLL before configuration\n            RCC.cr().modify(|w| w.set_pllon(false));\n            while RCC.cr().read().pllrdy() {}\n\n            let in_freq = src_freq / pll_config.prediv;\n            rcc_assert!(max::PLL_IN.contains(&in_freq));\n            let internal_freq = in_freq * pll_config.mul;\n\n            rcc_assert!(max::PLL_VCO.contains(&internal_freq));\n\n            RCC.pllcfgr().write(|w| {\n                w.set_plln(pll_config.mul);\n                w.set_pllm(pll_config.prediv);\n                w.set_pllsrc(pll_config.source.into());\n            });\n\n            let pll_p_freq = pll_config.divp.map(|div_p| {\n                RCC.pllcfgr().modify(|w| {\n                    w.set_pllp(div_p);\n                    w.set_pllpen(true);\n                });\n                let freq = internal_freq / div_p;\n                rcc_assert!(max::PLL_P.contains(&freq));\n                freq\n            });\n\n            let pll_q_freq = pll_config.divq.map(|div_q| {\n                RCC.pllcfgr().modify(|w| {\n                    w.set_pllq(div_q);\n                    w.set_pllqen(true);\n                });\n                let freq = internal_freq / div_q;\n                rcc_assert!(max::PLL_Q.contains(&freq));\n                freq\n            });\n\n            let pll_r_freq = pll_config.divr.map(|div_r| {\n                RCC.pllcfgr().modify(|w| {\n                    w.set_pllr(div_r);\n                    w.set_pllren(true);\n                });\n                let freq = internal_freq / div_r;\n                rcc_assert!(max::PLL_R.contains(&freq));\n                freq\n            });\n\n            // Enable the PLL\n            RCC.cr().modify(|w| w.set_pllon(true));\n            while !RCC.cr().read().pllrdy() {}\n\n            PllFreq {\n                pll_p: pll_p_freq,\n                pll_q: pll_q_freq,\n                pll_r: pll_r_freq,\n            }\n        })\n        .unwrap_or_default();\n\n    let sys = match config.sys {\n        Sysclk::HSI => unwrap!(hsi),\n        Sysclk::HSE => unwrap!(hse),\n        Sysclk::PLL1_R => unwrap!(pll.pll_r),\n        _ => unreachable!(),\n    };\n\n    rcc_assert!(max::SYSCLK.contains(&sys));\n\n    // Calculate the AHB frequency (HCLK), among other things so we can calculate the correct flash read latency.\n    let hclk = sys / config.ahb_pre;\n    rcc_assert!(max::HCLK.contains(&hclk));\n\n    let (pclk1, pclk1_tim) = super::util::calc_pclk(hclk, config.apb1_pre);\n    let (pclk2, pclk2_tim) = super::util::calc_pclk(hclk, config.apb2_pre);\n    rcc_assert!(max::PCLK.contains(&pclk1));\n    rcc_assert!(max::PCLK.contains(&pclk2));\n\n    // Configure Core Boost mode ([RM0440] p234 – inverted because setting r1mode to 0 enables boost mode!)\n    if config.boost {\n        // RM0440 p235\n        // “The sequence to switch from Range1 normal mode to Range1 boost mode is:\n        // 1. The system clock must be divided by 2 using the AHB prescaler before switching to a higher system frequency.\n        RCC.cfgr().modify(|w| w.set_hpre(AHBPrescaler::DIV2));\n        // 2. Clear the R1MODE bit in the PWR_CR5 register. (enables boost mode)\n        PWR.cr5().modify(|w| w.set_r1mode(false));\n\n        // Below:\n        // 3. Adjust wait states according to new freq target\n        // 4. Configure and switch to new frequency\n    }\n\n    let latency = match (config.boost, hclk.0) {\n        (true, ..=34_000_000) => Latency::WS0,\n        (true, ..=68_000_000) => Latency::WS1,\n        (true, ..=102_000_000) => Latency::WS2,\n        (true, ..=136_000_000) => Latency::WS3,\n        (true, _) => Latency::WS4,\n\n        (false, ..=36_000_000) => Latency::WS0,\n        (false, ..=60_000_000) => Latency::WS1,\n        (false, ..=90_000_000) => Latency::WS2,\n        (false, ..=120_000_000) => Latency::WS3,\n        (false, _) => Latency::WS4,\n    };\n\n    // Configure flash read access latency based on boost mode and frequency (RM0440 p98)\n    FLASH.acr().modify(|w| {\n        w.set_latency(latency);\n    });\n\n    // Spin until the effective flash latency is set.\n    while FLASH.acr().read().latency() != latency {}\n\n    if config.boost {\n        // 5. Wait for at least 1us and then reconfigure the AHB prescaler to get the needed HCLK clock frequency.\n        cortex_m::asm::delay(16);\n    }\n\n    // Now that boost mode and flash read access latency are configured, set up SYSCLK\n    RCC.cfgr().modify(|w| {\n        w.set_sw(config.sys);\n        w.set_hpre(config.ahb_pre);\n        w.set_ppre1(config.apb1_pre);\n        w.set_ppre2(config.apb2_pre);\n    });\n    while RCC.cfgr().read().sws() != config.sys {}\n\n    // Disable HSI if not used\n    if !config.hsi {\n        RCC.cr().modify(|w| w.set_hsion(false));\n    }\n\n    // Disable the HSI48, if not used\n    #[cfg(crs)]\n    if config.hsi48.is_none() {\n        super::disable_hsi48();\n    }\n\n    if config.low_power_run {\n        assert!(sys <= Hertz(2_000_000));\n        PWR.cr1().modify(|w| w.set_lpr(true));\n    }\n\n    let rtc = config.ls.init();\n\n    config.mux.init();\n\n    set_clocks!(\n        sys: Some(sys),\n        hclk1: Some(hclk),\n        hclk2: Some(hclk),\n        hclk3: Some(hclk),\n        pclk1: Some(pclk1),\n        pclk1_tim: Some(pclk1_tim),\n        pclk2: Some(pclk2),\n        pclk2_tim: Some(pclk2_tim),\n        pll1_p: pll.pll_p,\n        pll1_q: pll.pll_q,\n        pll1_r: pll.pll_r,\n        hsi: hsi,\n        hse: hse,\n        hsi48: hsi48,\n        rtc: rtc,\n        i2s_ckin: None,\n        lsi: None,\n        lse: None,\n    );\n}\n\n/// Acceptable Frequency Ranges\n/// Currently assuming voltage scaling range 1 boost mode.\n/// Where not specified in the generic G4 reference manual (RM0440), values taken from the STM32G474 datasheet.\n/// If acceptable ranges for other G4-family chips differ, make additional max modules gated behind cfg attrs.\nmod max {\n    use core::ops::RangeInclusive;\n\n    use crate::time::Hertz;\n\n    /// HSE Frequency Range (RM0440 p280)\n    pub(crate) const HSE_OSC: RangeInclusive<Hertz> = Hertz(4_000_000)..=Hertz(48_000_000);\n\n    /// External Clock Frequency Range (RM0440 p280)\n    pub(crate) const HSE_BYP: RangeInclusive<Hertz> = Hertz(0)..=Hertz(48_000_000);\n\n    /// SYSCLK Frequency Range (RM0440 p282)\n    pub(crate) const SYSCLK: RangeInclusive<Hertz> = Hertz(0)..=Hertz(170_000_000);\n\n    /// PLL Output Frequency Range (RM0440 p281, STM32G474 Datasheet p123, Table 46)\n    pub(crate) const PCLK: RangeInclusive<Hertz> = Hertz(8)..=Hertz(170_000_000);\n\n    /// HCLK (AHB) Clock Frequency Range (STM32G474 Datasheet)\n    pub(crate) const HCLK: RangeInclusive<Hertz> = Hertz(0)..=Hertz(170_000_000);\n\n    /// PLL Source Frequency Range (STM32G474 Datasheet p123, Table 46)\n    pub(crate) const PLL_IN: RangeInclusive<Hertz> = Hertz(2_660_000)..=Hertz(16_000_000);\n\n    /// PLL VCO (internal) Frequency Range (STM32G474 Datasheet p123, Table 46)\n    pub(crate) const PLL_VCO: RangeInclusive<Hertz> = Hertz(96_000_000)..=Hertz(344_000_000);\n    pub(crate) const PLL_P: RangeInclusive<Hertz> = Hertz(2_064_500)..=Hertz(170_000_000);\n    pub(crate) const PLL_Q: RangeInclusive<Hertz> = Hertz(8_000_000)..=Hertz(170_000_000);\n    pub(crate) const PLL_R: RangeInclusive<Hertz> = Hertz(8_000_000)..=Hertz(170_000_000);\n}\n"
  },
  {
    "path": "embassy-stm32/src/rcc/h.rs",
    "content": "use core::ops::RangeInclusive;\n\n#[cfg(stm32h7rs)]\nuse stm32_metapac::rcc::vals::Xspisel;\n\nuse crate::pac;\n#[cfg(stm32h7rs)]\npub use crate::pac::rcc::vals::Plldivst as PllDivSt;\npub use crate::pac::rcc::vals::{\n    Hsidiv as HSIPrescaler, Plldiv as PllDiv, Pllm as PllPreDiv, Plln as PllMul, Pllsrc as PllSource, Sw as Sysclk,\n};\nuse crate::pac::rcc::vals::{Pllrge, Pllvcosel, Timpre};\nuse crate::pac::{FLASH, PWR, RCC};\nuse crate::time::Hertz;\n\n/// HSI speed\npub const HSI_FREQ: Hertz = Hertz(64_000_000);\n\n/// CSI speed\npub const CSI_FREQ: Hertz = Hertz(4_000_000);\n\nconst VCO_RANGE: RangeInclusive<Hertz> = Hertz(150_000_000)..=Hertz(420_000_000);\n#[cfg(any(stm32h5, pwr_h7rm0455))]\nconst VCO_WIDE_RANGE: RangeInclusive<Hertz> = Hertz(128_000_000)..=Hertz(560_000_000);\n#[cfg(pwr_h7rm0468)]\nconst VCO_WIDE_RANGE: RangeInclusive<Hertz> = Hertz(192_000_000)..=Hertz(836_000_000);\n#[cfg(any(pwr_h7rm0399, pwr_h7rm0433))]\nconst VCO_WIDE_RANGE: RangeInclusive<Hertz> = Hertz(192_000_000)..=Hertz(960_000_000);\n#[cfg(any(stm32h7rs))]\nconst VCO_WIDE_RANGE: RangeInclusive<Hertz> = Hertz(384_000_000)..=Hertz(1672_000_000);\n\npub use crate::pac::rcc::vals::{Hpre as AHBPrescaler, Ppre as APBPrescaler};\n\n#[cfg(any(stm32h5, stm32h7))]\n#[derive(Clone, Copy, Eq, PartialEq)]\npub enum VoltageScale {\n    Scale0,\n    Scale1,\n    Scale2,\n    Scale3,\n}\n#[cfg(stm32h7rs)]\npub use crate::pac::pwr::vals::Vos as VoltageScale;\n#[cfg(all(stm32h7rs, peri_usb_otg_hs))]\npub use crate::pac::rcc::vals::{Usbphycsel, Usbrefcksel};\n\n#[derive(Clone, Copy, Eq, PartialEq)]\npub enum HseMode {\n    /// crystal/ceramic oscillator (HSEBYP=0)\n    Oscillator,\n    /// external analog clock (low swing) (HSEBYP=1, HSEEXT=0)\n    Bypass,\n    /// external digital clock (full swing) (HSEBYP=1, HSEEXT=1)\n    #[cfg(any(rcc_h5, rcc_h50, rcc_h7rs))]\n    BypassDigital,\n}\n\n#[derive(Clone, Copy, Eq, PartialEq)]\npub struct Hse {\n    /// HSE frequency.\n    pub freq: Hertz,\n    /// HSE mode.\n    pub mode: HseMode,\n}\n\n#[derive(Clone, Copy)]\npub struct Pll {\n    /// Source clock selection.\n    pub source: PllSource,\n\n    /// PLL pre-divider (DIVM).\n    pub prediv: PllPreDiv,\n\n    /// PLL multiplication factor.\n    pub mul: PllMul,\n\n    #[cfg(any(stm32h743))]\n    /// PLL Fractional multiplier.\n    pub fracn: Option<u16>,\n\n    /// PLL P division factor. If None, PLL P output is disabled.\n    /// On PLL1, it must be even for most series (in particular,\n    /// it cannot be 1 in series other than stm32h7, stm32h7rs23/733,\n    /// stm32h7, stm32h7rs25/735 and stm32h7, stm32h7rs30.)\n    pub divp: Option<PllDiv>,\n    /// PLL Q division factor. If None, PLL Q output is disabled.\n    pub divq: Option<PllDiv>,\n    /// PLL R division factor. If None, PLL R output is disabled.\n    pub divr: Option<PllDiv>,\n    #[cfg(stm32h7rs)]\n    /// PLL S division factor. If None, PLL S output is disabled.\n    pub divs: Option<PllDivSt>,\n    #[cfg(stm32h7rs)]\n    /// PLL T division factor. If None, PLL T output is disabled.\n    pub divt: Option<PllDivSt>,\n}\n\nfn apb_div_tim(apb: &APBPrescaler, clk: Hertz, tim: TimerPrescaler) -> Hertz {\n    match (tim, apb) {\n        (TimerPrescaler::DefaultX2, APBPrescaler::DIV1) => clk,\n        (TimerPrescaler::DefaultX2, APBPrescaler::DIV2) => clk,\n        (TimerPrescaler::DefaultX2, APBPrescaler::DIV4) => clk / 2u32,\n        (TimerPrescaler::DefaultX2, APBPrescaler::DIV8) => clk / 4u32,\n        (TimerPrescaler::DefaultX2, APBPrescaler::DIV16) => clk / 8u32,\n\n        (TimerPrescaler::DefaultX4, APBPrescaler::DIV1) => clk,\n        (TimerPrescaler::DefaultX4, APBPrescaler::DIV2) => clk,\n        (TimerPrescaler::DefaultX4, APBPrescaler::DIV4) => clk,\n        (TimerPrescaler::DefaultX4, APBPrescaler::DIV8) => clk / 2u32,\n        (TimerPrescaler::DefaultX4, APBPrescaler::DIV16) => clk / 4u32,\n\n        _ => unreachable!(),\n    }\n}\n\n/// Timer prescaler\n#[derive(Clone, Copy, Eq, PartialEq)]\npub enum TimerPrescaler {\n    /// The timers kernel clock is equal to hclk if PPREx corresponds to a\n    /// division by 1 or 2, else it is equal to 2*pclk\n    DefaultX2,\n\n    /// The timers kernel clock is equal to hclk if PPREx corresponds to a\n    /// division by 1, 2 or 4, else it is equal to 4*pclk\n    DefaultX4,\n}\n\nimpl From<TimerPrescaler> for Timpre {\n    fn from(value: TimerPrescaler) -> Self {\n        match value {\n            TimerPrescaler::DefaultX2 => Timpre::DEFAULT_X2,\n            TimerPrescaler::DefaultX4 => Timpre::DEFAULT_X4,\n        }\n    }\n}\n\n/// Power supply configuration\n/// See RM0433 Rev 4 7.4\n#[cfg(any(pwr_h7rm0399, pwr_h7rm0455, pwr_h7rm0468, pwr_h7rs))]\n#[derive(Clone, Copy, PartialEq)]\npub enum SupplyConfig {\n    /// Default power supply configuration.\n    /// V CORE Power Domains are supplied from the LDO according to VOS.\n    /// SMPS step-down converter enabled at 1.2V, may be used to supply the LDO.\n    Default,\n\n    /// Power supply configuration using the LDO.\n    /// V CORE Power Domains are supplied from the LDO according to VOS.\n    /// LDO power mode (Main, LP, Off) will follow system low-power modes.\n    /// SMPS step-down converter disabled.\n    LDO,\n\n    /// Power supply configuration directly from the SMPS step-down converter.\n    /// V CORE Power Domains are supplied from SMPS step-down converter according to VOS.\n    /// LDO bypassed.\n    /// SMPS step-down converter power mode (MR, LP, Off) will follow system low-power modes.\n    DirectSMPS,\n\n    /// Power supply configuration from the SMPS step-down converter, that supplies the LDO.\n    /// V CORE Power Domains are supplied from the LDO according to VOS\n    /// LDO power mode (Main, LP, Off) will follow system low-power modes.\n    /// SMPS step-down converter enabled according to SDLEVEL, and supplies the LDO.\n    /// SMPS step-down converter power mode (MR, LP, Off) will follow system low-power modes.\n    SMPSLDO(SMPSSupplyVoltage),\n\n    /// Power supply configuration from SMPS supplying external circuits and potentially the LDO.\n    /// V CORE Power Domains are supplied from voltage regulator according to VOS\n    /// LDO power mode (Main, LP, Off) will follow system low-power modes.\n    /// SMPS step-down converter enabled according to SDLEVEL used to supply external circuits and may supply the LDO.\n    /// SMPS step-down converter forced ON in MR mode.\n    SMPSExternalLDO(SMPSSupplyVoltage),\n\n    /// Power supply configuration from SMPS supplying external circuits and bypassing the LDO.\n    /// V CORE supplied from external source\n    /// SMPS step-down converter enabled according to SDLEVEL used to supply external circuits and may supply the external source for V CORE .\n    /// SMPS step-down converter forced ON in MR mode.\n    SMPSExternalLDOBypass(SMPSSupplyVoltage),\n\n    /// Power supply configuration from an external source, SMPS disabled and the LDO bypassed.\n    /// V CORE supplied from external source\n    /// SMPS step-down converter disabled and LDO bypassed, voltage monitoring still active.\n    SMPSDisabledLDOBypass,\n}\n\n/// SMPS step-down converter voltage output level.\n/// This is only used in certain power supply configurations:\n/// SMPSLDO, SMPSExternalLDO, SMPSExternalLDOBypass.\n#[cfg(any(pwr_h7rm0399, pwr_h7rm0455, pwr_h7rm0468, pwr_h7rs))]\n#[derive(Clone, Copy, Eq, PartialEq, Debug)]\npub enum SMPSSupplyVoltage {\n    /// 1.8v\n    V1_8,\n    /// 2.5v\n    #[cfg(not(pwr_h7rs))]\n    V2_5,\n}\n\n/// Configuration of the core clocks\n#[non_exhaustive]\n#[derive(Clone, Copy)]\npub struct Config {\n    pub hsi: Option<HSIPrescaler>,\n    pub hse: Option<Hse>,\n    pub csi: bool,\n    pub hsi48: Option<super::Hsi48Config>,\n    pub sys: Sysclk,\n\n    pub pll1: Option<Pll>,\n    pub pll2: Option<Pll>,\n    #[cfg(any(rcc_h5, stm32h7, stm32h7rs))]\n    pub pll3: Option<Pll>,\n\n    #[cfg(any(stm32h7, stm32h7rs))]\n    pub d1c_pre: AHBPrescaler,\n    pub ahb_pre: AHBPrescaler,\n    pub apb1_pre: APBPrescaler,\n    pub apb2_pre: APBPrescaler,\n    #[cfg(not(stm32h7rs))]\n    pub apb3_pre: APBPrescaler,\n    #[cfg(any(stm32h7, stm32h7rs))]\n    pub apb4_pre: APBPrescaler,\n    #[cfg(stm32h7rs)]\n    pub apb5_pre: APBPrescaler,\n\n    pub timer_prescaler: TimerPrescaler,\n    pub voltage_scale: VoltageScale,\n    pub ls: super::LsConfig,\n\n    #[cfg(any(pwr_h7rm0399, pwr_h7rm0455, pwr_h7rm0468, pwr_h7rs))]\n    pub supply_config: SupplyConfig,\n\n    /// Per-peripheral kernel clock selection muxes\n    pub mux: super::mux::ClockMux,\n}\n\nimpl Config {\n    pub const fn new() -> Self {\n        Self {\n            hsi: Some(HSIPrescaler::DIV1),\n            hse: None,\n            csi: false,\n            hsi48: Some(crate::rcc::Hsi48Config::new()),\n            sys: Sysclk::HSI,\n            pll1: None,\n            pll2: None,\n            #[cfg(any(rcc_h5, stm32h7, stm32h7rs))]\n            pll3: None,\n\n            #[cfg(any(stm32h7, stm32h7rs))]\n            d1c_pre: AHBPrescaler::DIV1,\n            ahb_pre: AHBPrescaler::DIV1,\n            apb1_pre: APBPrescaler::DIV1,\n            apb2_pre: APBPrescaler::DIV1,\n            #[cfg(not(stm32h7rs))]\n            apb3_pre: APBPrescaler::DIV1,\n            #[cfg(any(stm32h7, stm32h7rs))]\n            apb4_pre: APBPrescaler::DIV1,\n            #[cfg(stm32h7rs)]\n            apb5_pre: APBPrescaler::DIV1,\n\n            timer_prescaler: TimerPrescaler::DefaultX2,\n            #[cfg(not(rcc_h7rs))]\n            voltage_scale: VoltageScale::Scale0,\n            #[cfg(rcc_h7rs)]\n            voltage_scale: VoltageScale::HIGH,\n            ls: crate::rcc::LsConfig::new(),\n\n            #[cfg(any(pwr_h7rm0399, pwr_h7rm0455, pwr_h7rm0468, pwr_h7rs))]\n            supply_config: SupplyConfig::LDO,\n\n            mux: super::mux::ClockMux::default(),\n        }\n    }\n}\n\nimpl Default for Config {\n    fn default() -> Self {\n        Self::new()\n    }\n}\n\npub(crate) unsafe fn init(config: Config) {\n    #[cfg(any(stm32h7))]\n    let pwr_reg = PWR.cr3();\n    #[cfg(any(stm32h7rs))]\n    let pwr_reg = PWR.csr2();\n\n    // NB. The lower bytes of CR3 can only be written once after\n    // POR, and must be written with a valid combination. Refer to\n    // RM0433 Rev 7 6.8.4. This is partially enforced by dropping\n    // `self` at the end of this method, but of course we cannot\n    // know what happened between the previous POR and here.\n    #[cfg(pwr_h7rm0433)]\n    pwr_reg.modify(|w| {\n        w.set_scuen(true);\n        w.set_ldoen(true);\n        w.set_bypass(false);\n    });\n\n    #[cfg(any(pwr_h7rm0399, pwr_h7rm0455, pwr_h7rm0468, pwr_h7rs))]\n    {\n        use pac::pwr::vals::Sdlevel;\n        match config.supply_config {\n            SupplyConfig::Default => {\n                pwr_reg.modify(|w| {\n                    w.set_sdlevel(Sdlevel::RESET);\n                    w.set_sdexthp(false);\n                    w.set_sden(true);\n                    w.set_ldoen(true);\n                    w.set_bypass(false);\n                });\n            }\n            SupplyConfig::LDO => {\n                pwr_reg.modify(|w| {\n                    w.set_sden(false);\n                    w.set_ldoen(true);\n                    w.set_bypass(false);\n                });\n            }\n            SupplyConfig::DirectSMPS => {\n                pwr_reg.modify(|w| {\n                    w.set_sdexthp(false);\n                    w.set_sden(true);\n                    w.set_ldoen(false);\n                    w.set_bypass(false);\n                });\n            }\n            SupplyConfig::SMPSLDO(sdlevel)\n            | SupplyConfig::SMPSExternalLDO(sdlevel)\n            | SupplyConfig::SMPSExternalLDOBypass(sdlevel) => {\n                let sdlevel = match sdlevel {\n                    SMPSSupplyVoltage::V1_8 => Sdlevel::V1_8,\n                    #[cfg(not(pwr_h7rs))]\n                    SMPSSupplyVoltage::V2_5 => Sdlevel::V2_5,\n                };\n                pwr_reg.modify(|w| {\n                    w.set_sdlevel(sdlevel);\n                    w.set_sdexthp(matches!(\n                        config.supply_config,\n                        SupplyConfig::SMPSExternalLDO(_) | SupplyConfig::SMPSExternalLDOBypass(_)\n                    ));\n                    w.set_sden(true);\n                    w.set_ldoen(matches!(\n                        config.supply_config,\n                        SupplyConfig::SMPSLDO(_) | SupplyConfig::SMPSExternalLDO(_)\n                    ));\n                    w.set_bypass(matches!(config.supply_config, SupplyConfig::SMPSExternalLDOBypass(_)));\n                });\n            }\n            SupplyConfig::SMPSDisabledLDOBypass => {\n                pwr_reg.modify(|w| {\n                    w.set_sden(false);\n                    w.set_ldoen(false);\n                    w.set_bypass(true);\n                });\n            }\n        }\n    }\n\n    // Validate the supply configuration. If you are stuck here, it is\n    // because the voltages on your board do not match those specified\n    // in the D3CR.VOS and CR3.SDLEVEL fields. By default after reset\n    // VOS = Scale 3, so check that the voltage on the VCAP pins =\n    // 1.0V.\n    #[cfg(any(stm32h7))]\n    while !PWR.csr1().read().actvosrdy() {}\n    #[cfg(any(stm32h7rs))]\n    while !PWR.sr1().read().actvosrdy() {}\n\n    // Configure voltage scale.\n    #[cfg(any(pwr_h5, pwr_h50))]\n    {\n        PWR.voscr().modify(|w| {\n            w.set_vos(match config.voltage_scale {\n                VoltageScale::Scale0 => crate::pac::pwr::vals::Vos::SCALE0,\n                VoltageScale::Scale1 => crate::pac::pwr::vals::Vos::SCALE1,\n                VoltageScale::Scale2 => crate::pac::pwr::vals::Vos::SCALE2,\n                VoltageScale::Scale3 => crate::pac::pwr::vals::Vos::SCALE3,\n            })\n        });\n        while !PWR.vossr().read().vosrdy() {}\n    }\n    #[cfg(syscfg_h7)]\n    {\n        // in chips without the overdrive bit, we can go from any scale to any scale directly.\n        PWR.d3cr().modify(|w| {\n            w.set_vos(match config.voltage_scale {\n                VoltageScale::Scale0 => crate::pac::pwr::vals::Vos::SCALE0,\n                VoltageScale::Scale1 => crate::pac::pwr::vals::Vos::SCALE1,\n                VoltageScale::Scale2 => crate::pac::pwr::vals::Vos::SCALE2,\n                VoltageScale::Scale3 => crate::pac::pwr::vals::Vos::SCALE3,\n            })\n        });\n        while !PWR.d3cr().read().vosrdy() {}\n    }\n    #[cfg(pwr_h7rs)]\n    {\n        PWR.csr4().modify(|w| w.set_vos(config.voltage_scale));\n        while !PWR.csr4().read().vosrdy() {}\n    }\n\n    #[cfg(syscfg_h7od)]\n    {\n        match config.voltage_scale {\n            VoltageScale::Scale0 => {\n                // to go to scale0, we must go to Scale1 first...\n                PWR.d3cr().modify(|w| w.set_vos(crate::pac::pwr::vals::Vos::SCALE1));\n                while !PWR.d3cr().read().vosrdy() {}\n\n                // Then enable overdrive.\n                critical_section::with(|_| pac::SYSCFG.pwrcr().modify(|w| w.set_oden(1)));\n                while !PWR.d3cr().read().vosrdy() {}\n            }\n            _ => {\n                // for all other scales, we can go directly.\n                PWR.d3cr().modify(|w| {\n                    w.set_vos(match config.voltage_scale {\n                        VoltageScale::Scale0 => unreachable!(),\n                        VoltageScale::Scale1 => crate::pac::pwr::vals::Vos::SCALE1,\n                        VoltageScale::Scale2 => crate::pac::pwr::vals::Vos::SCALE2,\n                        VoltageScale::Scale3 => crate::pac::pwr::vals::Vos::SCALE3,\n                    })\n                });\n                while !PWR.d3cr().read().vosrdy() {}\n            }\n        }\n    }\n\n    // Turn on the HSI\n    match config.hsi {\n        None => RCC.cr().modify(|w| w.set_hsion(true)),\n        Some(hsidiv) => RCC.cr().modify(|w| {\n            w.set_hsidiv(hsidiv);\n            w.set_hsion(true);\n        }),\n    }\n    while !RCC.cr().read().hsirdy() {}\n\n    #[cfg(stm32h7rs)]\n    {\n        // Switch the XSPI clock source so it will use HSI\n        RCC.ahbperckselr().modify(|w| w.set_xspi1sel(Xspisel::HCLK5));\n        RCC.ahbperckselr().modify(|w| w.set_xspi2sel(Xspisel::HCLK5));\n    };\n\n    // Use the HSI clock as system clock during the actual clock setup\n    RCC.cfgr().modify(|w| w.set_sw(Sysclk::HSI));\n    while RCC.cfgr().read().sws() != Sysclk::HSI {}\n\n    // Configure HSI\n    let hsi = match config.hsi {\n        None => None,\n        Some(hsidiv) => Some(HSI_FREQ / hsidiv),\n    };\n\n    // Configure HSE\n    let hse = match config.hse {\n        None => {\n            RCC.cr().modify(|w| w.set_hseon(false));\n            None\n        }\n        Some(hse) => {\n            RCC.cr().modify(|w| {\n                w.set_hsebyp(hse.mode != HseMode::Oscillator);\n                #[cfg(any(rcc_h5, rcc_h50, rcc_h7rs))]\n                w.set_hseext(match hse.mode {\n                    HseMode::Oscillator | HseMode::Bypass => pac::rcc::vals::Hseext::ANALOG,\n                    HseMode::BypassDigital => pac::rcc::vals::Hseext::DIGITAL,\n                });\n            });\n            RCC.cr().modify(|w| w.set_hseon(true));\n            while !RCC.cr().read().hserdy() {}\n            Some(hse.freq)\n        }\n    };\n\n    // Configure HSI48.\n    let hsi48 = config.hsi48.map(super::init_hsi48);\n\n    // Configure CSI.\n    RCC.cr().modify(|w| w.set_csion(config.csi));\n    let csi = match config.csi {\n        false => None,\n        true => {\n            while !RCC.cr().read().csirdy() {}\n            Some(CSI_FREQ)\n        }\n    };\n\n    // H7 has shared PLLSRC, check it's equal in all PLLs.\n    #[cfg(any(stm32h7, stm32h7rs))]\n    {\n        let plls = [&config.pll1, &config.pll2, &config.pll3];\n        if !super::util::all_equal(plls.into_iter().flatten().map(|p| p.source)) {\n            panic!(\"Source must be equal across all enabled PLLs.\")\n        };\n    }\n\n    // Configure PLLs.\n    let pll_input = PllInput { csi, hse, hsi };\n    let pll1 = config.pll1.map_or_else(\n        || {\n            disable_pll(0);\n            PllOutput::default()\n        },\n        |c| init_pll(0, Some(c), &pll_input),\n    );\n    let pll2 = config.pll2.map_or_else(\n        || {\n            disable_pll(1);\n            PllOutput::default()\n        },\n        |c| init_pll(1, Some(c), &pll_input),\n    );\n    #[cfg(any(rcc_h5, stm32h7, stm32h7rs))]\n    let pll3 = config.pll3.map_or_else(\n        || {\n            disable_pll(2);\n            PllOutput::default()\n        },\n        |c| init_pll(2, Some(c), &pll_input),\n    );\n\n    // Configure sysclk\n    let sys = match config.sys {\n        Sysclk::HSI => unwrap!(hsi),\n        Sysclk::HSE => unwrap!(hse),\n        Sysclk::CSI => unwrap!(csi),\n        Sysclk::PLL1_P => unwrap!(pll1.p),\n        _ => unreachable!(),\n    };\n\n    // Check limits.\n    #[cfg(stm32h5)]\n    let (hclk_max, pclk_max) = match config.voltage_scale {\n        VoltageScale::Scale0 => (Hertz(250_000_000), Hertz(250_000_000)),\n        VoltageScale::Scale1 => (Hertz(200_000_000), Hertz(200_000_000)),\n        VoltageScale::Scale2 => (Hertz(150_000_000), Hertz(150_000_000)),\n        VoltageScale::Scale3 => (Hertz(100_000_000), Hertz(100_000_000)),\n    };\n    #[cfg(pwr_h7rm0455)]\n    let (d1cpre_clk_max, hclk_max, pclk_max) = match config.voltage_scale {\n        VoltageScale::Scale0 => (Hertz(280_000_000), Hertz(280_000_000), Hertz(140_000_000)),\n        VoltageScale::Scale1 => (Hertz(225_000_000), Hertz(225_000_000), Hertz(112_500_000)),\n        VoltageScale::Scale2 => (Hertz(160_000_000), Hertz(160_000_000), Hertz(80_000_000)),\n        VoltageScale::Scale3 => (Hertz(88_000_000), Hertz(88_000_000), Hertz(44_000_000)),\n    };\n    #[cfg(pwr_h7rm0468)]\n    let (d1cpre_clk_max, hclk_max, pclk_max) = match config.voltage_scale {\n        VoltageScale::Scale0 => {\n            let d1cpre_clk_max = if pac::SYSCFG.ur18().read().cpu_freq_boost() {\n                550_000_000\n            } else {\n                520_000_000\n            };\n            (Hertz(d1cpre_clk_max), Hertz(275_000_000), Hertz(137_500_000))\n        }\n        VoltageScale::Scale1 => (Hertz(400_000_000), Hertz(200_000_000), Hertz(100_000_000)),\n        VoltageScale::Scale2 => (Hertz(300_000_000), Hertz(150_000_000), Hertz(75_000_000)),\n        VoltageScale::Scale3 => (Hertz(170_000_000), Hertz(85_000_000), Hertz(42_500_000)),\n    };\n    #[cfg(all(stm32h7, not(any(pwr_h7rm0455, pwr_h7rm0468))))]\n    let (d1cpre_clk_max, hclk_max, pclk_max) = match config.voltage_scale {\n        VoltageScale::Scale0 => (Hertz(480_000_000), Hertz(240_000_000), Hertz(120_000_000)),\n        VoltageScale::Scale1 => (Hertz(400_000_000), Hertz(200_000_000), Hertz(100_000_000)),\n        VoltageScale::Scale2 => (Hertz(300_000_000), Hertz(150_000_000), Hertz(75_000_000)),\n        VoltageScale::Scale3 => (Hertz(200_000_000), Hertz(100_000_000), Hertz(50_000_000)),\n    };\n    #[cfg(stm32h7rs)]\n    let (d1cpre_clk_max, hclk_max, pclk_max) = match config.voltage_scale {\n        VoltageScale::HIGH => (Hertz(600_000_000), Hertz(300_000_000), Hertz(150_000_000)),\n        VoltageScale::LOW => (Hertz(400_000_000), Hertz(200_000_000), Hertz(100_000_000)),\n    };\n\n    #[cfg(any(stm32h7, stm32h7rs))]\n    let hclk = {\n        let d1cpre_clk = sys / config.d1c_pre;\n        assert!(d1cpre_clk <= d1cpre_clk_max);\n        sys / config.ahb_pre\n    };\n    #[cfg(stm32h5)]\n    let hclk = sys / config.ahb_pre;\n    assert!(hclk <= hclk_max);\n\n    let apb1 = hclk / config.apb1_pre;\n    let apb1_tim = apb_div_tim(&config.apb1_pre, hclk, config.timer_prescaler);\n    assert!(apb1 <= pclk_max);\n    let apb2 = hclk / config.apb2_pre;\n    let apb2_tim = apb_div_tim(&config.apb2_pre, hclk, config.timer_prescaler);\n    assert!(apb2 <= pclk_max);\n    #[cfg(not(stm32h7rs))]\n    let apb3 = hclk / config.apb3_pre;\n    #[cfg(not(stm32h7rs))]\n    assert!(apb3 <= pclk_max);\n    #[cfg(any(stm32h7, stm32h7rs))]\n    let apb4 = hclk / config.apb4_pre;\n    #[cfg(any(stm32h7, stm32h7rs))]\n    assert!(apb4 <= pclk_max);\n    #[cfg(stm32h7rs)]\n    let apb5 = hclk / config.apb5_pre;\n    #[cfg(stm32h7rs)]\n    assert!(apb5 <= pclk_max);\n\n    flash_setup(hclk, config.voltage_scale);\n\n    let rtc = config.ls.init();\n\n    #[cfg(all(stm32h7rs, peri_usb_otg_hs))]\n    let usb_refck = match config.mux.usbphycsel {\n        Usbphycsel::HSE => hse,\n        Usbphycsel::HSE_DIV_2 => hse.map(|hse_val| hse_val / 2u8),\n        Usbphycsel::PLL3_Q => pll3.q,\n        _ => None,\n    };\n    #[cfg(all(stm32h7rs, peri_usb_otg_hs))]\n    let usb_refck_sel = match usb_refck {\n        Some(clk_val) => match clk_val {\n            Hertz(16_000_000) => Usbrefcksel::MHZ16,\n            Hertz(19_200_000) => Usbrefcksel::MHZ19_2,\n            Hertz(20_000_000) => Usbrefcksel::MHZ20,\n            Hertz(24_000_000) => Usbrefcksel::MHZ24,\n            Hertz(26_000_000) => Usbrefcksel::MHZ26,\n            Hertz(32_000_000) => Usbrefcksel::MHZ32,\n            _ => panic!(\n                \"cannot select USBPHYC reference clock with source frequency of {}, must be one of 16, 19.2, 20, 24, 26, 32 MHz\",\n                clk_val\n            ),\n        },\n        None => Usbrefcksel::MHZ24,\n    };\n\n    #[cfg(stm32h7)]\n    {\n        RCC.d1cfgr().modify(|w| {\n            w.set_d1cpre(config.d1c_pre);\n            w.set_d1ppre(config.apb3_pre);\n            w.set_hpre(config.ahb_pre);\n        });\n        // Ensure core prescaler value is valid before future lower core voltage\n        while RCC.d1cfgr().read().d1cpre() != config.d1c_pre {}\n\n        RCC.d2cfgr().modify(|w| {\n            w.set_d2ppre1(config.apb1_pre);\n            w.set_d2ppre2(config.apb2_pre);\n        });\n        RCC.d3cfgr().modify(|w| {\n            w.set_d3ppre(config.apb4_pre);\n        });\n    }\n    #[cfg(stm32h7rs)]\n    {\n        RCC.cdcfgr().write(|w| {\n            w.set_cpre(config.d1c_pre);\n        });\n        while RCC.cdcfgr().read().cpre() != config.d1c_pre {}\n\n        RCC.bmcfgr().write(|w| {\n            w.set_bmpre(config.ahb_pre);\n        });\n        while RCC.bmcfgr().read().bmpre() != config.ahb_pre {}\n\n        RCC.apbcfgr().modify(|w| {\n            w.set_ppre1(config.apb1_pre);\n            w.set_ppre2(config.apb2_pre);\n            w.set_ppre4(config.apb4_pre);\n            w.set_ppre5(config.apb5_pre);\n        });\n\n        #[cfg(peri_usb_otg_hs)]\n        RCC.ahbperckselr().modify(|w| {\n            w.set_usbrefcksel(usb_refck_sel);\n        });\n    }\n    #[cfg(stm32h5)]\n    {\n        // Set hpre\n        RCC.cfgr2().modify(|w| w.set_hpre(config.ahb_pre));\n        while RCC.cfgr2().read().hpre() != config.ahb_pre {}\n\n        // set ppre\n        RCC.cfgr2().modify(|w| {\n            w.set_ppre1(config.apb1_pre);\n            w.set_ppre2(config.apb2_pre);\n            w.set_ppre3(config.apb3_pre);\n        });\n    }\n\n    RCC.cfgr().modify(|w| w.set_timpre(config.timer_prescaler.into()));\n\n    RCC.cfgr().modify(|w| w.set_sw(config.sys));\n    while RCC.cfgr().read().sws() != config.sys {}\n\n    // Disable HSI if not used\n    if config.hsi.is_none() {\n        RCC.cr().modify(|w| w.set_hsion(false));\n    }\n\n    // Disable the HSI48, if not used\n    #[cfg(crs)]\n    if config.hsi48.is_none() {\n        super::disable_hsi48();\n    }\n\n    // IO compensation cell - Requires CSI clock and SYSCFG\n    #[cfg(any(stm32h7))] // TODO h5, h7rs\n    if csi.is_some() {\n        // Enable the compensation cell, using back-bias voltage code\n        // provide by the cell.\n        critical_section::with(|_| {\n            pac::SYSCFG.cccsr().modify(|w| {\n                w.set_en(true);\n                w.set_cs(false);\n                w.set_hslv(false);\n            })\n        });\n        while !pac::SYSCFG.cccsr().read().rdy() {}\n    }\n\n    config.mux.init();\n\n    set_clocks!(\n        sys: Some(sys),\n        hclk1: Some(hclk),\n        hclk2: Some(hclk),\n        hclk3: Some(hclk),\n        hclk4: Some(hclk),\n        #[cfg(stm32h7rs)]\n        hclk5: Some(hclk),\n        pclk1: Some(apb1),\n        pclk2: Some(apb2),\n        #[cfg(not(stm32h7rs))]\n        pclk3: Some(apb3),\n        #[cfg(any(stm32h7, stm32h7rs))]\n        pclk4: Some(apb4),\n        #[cfg(stm32h7rs)]\n        pclk5: Some(apb5),\n\n        pclk1_tim: Some(apb1_tim),\n        pclk2_tim: Some(apb2_tim),\n        rtc: rtc,\n\n        hsi: hsi,\n        hsi48: hsi48,\n        csi: csi,\n        hse: hse,\n\n        lse: None,\n        lsi: None,\n\n        pll1_q: pll1.q,\n        pll2_p: pll2.p,\n        pll2_q: pll2.q,\n        pll2_r: pll2.r,\n        #[cfg(stm32h7rs)]\n        pll2_s: pll2.s,\n        #[cfg(stm32h7rs)]\n        pll2_t: pll2.t,\n        #[cfg(any(rcc_h5, stm32h7, stm32h7rs))]\n        pll3_p: pll3.p,\n        #[cfg(any(rcc_h5, stm32h7, stm32h7rs))]\n        pll3_q: pll3.q,\n        #[cfg(any(rcc_h5, stm32h7, stm32h7rs))]\n        pll3_r: pll3.r,\n\n        #[cfg(rcc_h50)]\n        pll3_p: None,\n        #[cfg(rcc_h50)]\n        pll3_q: None,\n        #[cfg(rcc_h50)]\n        pll3_r: None,\n\n        #[cfg(dsihost)]\n        dsi_phy: None, // DSI PLL clock not supported, don't call `RccPeripheral::frequency()` in the drivers\n\n        #[cfg(stm32h5)]\n        audioclk: None,\n        i2s_ckin: None,\n        #[cfg(stm32h7rs)]\n        spdifrx_symb: None, // TODO\n        #[cfg(stm32h7rs)]\n        clk48mohci: None, // TODO\n        #[cfg(stm32h7rs)]\n        usb: Some(Hertz(48_000_000)),\n        #[cfg(stm32h5)]\n        hse_div_rtcpre: None, // TODO\n    );\n}\n\nstruct PllInput {\n    hsi: Option<Hertz>,\n    hse: Option<Hertz>,\n    csi: Option<Hertz>,\n}\n\n#[derive(Default)]\nstruct PllOutput {\n    p: Option<Hertz>,\n    #[allow(dead_code)]\n    q: Option<Hertz>,\n    #[allow(dead_code)]\n    r: Option<Hertz>,\n    #[cfg(stm32h7rs)]\n    #[allow(dead_code)]\n    s: Option<Hertz>,\n    #[cfg(stm32h7rs)]\n    #[allow(dead_code)]\n    t: Option<Hertz>,\n}\n\nfn disable_pll(num: usize) {\n    // Stop PLL\n    RCC.cr().modify(|w| w.set_pllon(num, false));\n    while RCC.cr().read().pllrdy(num) {}\n\n    // \"To save power when PLL1 is not used, the value of PLL1M must be set to 0.\"\"\n    #[cfg(any(stm32h7, stm32h7rs))]\n    RCC.pllckselr().write(|w| w.set_divm(num, PllPreDiv::from_bits(0)));\n    #[cfg(stm32h5)]\n    RCC.pllcfgr(num).write(|w| w.set_divm(PllPreDiv::from_bits(0)));\n}\n\nfn init_pll(num: usize, config: Option<Pll>, input: &PllInput) -> PllOutput {\n    let Some(config) = config else {\n        disable_pll(num);\n\n        return PllOutput::default();\n    };\n\n    let in_clk = match config.source {\n        PllSource::DISABLE => panic!(\"must not set PllSource::Disable\"),\n        PllSource::HSI => unwrap!(input.hsi),\n        PllSource::HSE => unwrap!(input.hse),\n        PllSource::CSI => unwrap!(input.csi),\n    };\n\n    let ref_clk = in_clk / config.prediv as u32;\n\n    let ref_range = match ref_clk.0 {\n        ..=1_999_999 => Pllrge::RANGE1,\n        ..=3_999_999 => Pllrge::RANGE2,\n        ..=7_999_999 => Pllrge::RANGE4,\n        ..=16_000_000 => Pllrge::RANGE8,\n        x => panic!(\"pll ref_clk out of range: {} hz\", x),\n    };\n\n    // The smaller range (150 to 420 MHz) must\n    // be chosen when the reference clock frequency is lower than 2 MHz.\n    let wide_allowed = ref_range != Pllrge::RANGE1;\n\n    #[cfg(stm32h743)]\n    let vco_clk = match config.fracn {\n        Some(fracn) => {\n            Hertz::hz((ref_clk.0 as f32 * ((config.mul.to_bits() + 1) as f32 + (fracn as f32 / 8192.0))) as u32)\n        }\n        None => ref_clk * config.mul,\n    };\n    #[cfg(not(stm32h743))]\n    let vco_clk = ref_clk * config.mul;\n\n    let vco_range = if VCO_RANGE.contains(&vco_clk) {\n        Pllvcosel::MEDIUM_VCO\n    } else if wide_allowed && VCO_WIDE_RANGE.contains(&vco_clk) {\n        Pllvcosel::WIDE_VCO\n    } else {\n        panic!(\"pll vco_clk out of range: {}\", vco_clk)\n    };\n\n    let p = config.divp.map(|div| {\n        if num == 0 {\n            // on PLL1, DIVP must be even for most series.\n            // The enum value is 1 less than the divider, so check it's odd.\n            #[cfg(not(any(pwr_h7rm0468, stm32h7rs)))]\n            assert!(div.to_bits() % 2 == 1);\n            #[cfg(pwr_h7rm0468)]\n            assert!(div.to_bits() % 2 == 1 || div.to_bits() == 0);\n        }\n\n        vco_clk / div\n    });\n    let q = config.divq.map(|div| vco_clk / div);\n    let r = config.divr.map(|div| vco_clk / div);\n    #[cfg(stm32h7rs)]\n    let s = config.divs.map(|div| vco_clk / div);\n    #[cfg(stm32h7rs)]\n    let t = config.divt.map(|div| vco_clk / div);\n\n    #[cfg(stm32h5)]\n    RCC.pllcfgr(num).write(|w| {\n        w.set_pllsrc(config.source);\n        w.set_divm(config.prediv);\n        w.set_pllvcosel(vco_range);\n        w.set_pllrge(ref_range);\n        w.set_pllfracen(false);\n        w.set_pllpen(p.is_some());\n        w.set_pllqen(q.is_some());\n        w.set_pllren(r.is_some());\n    });\n\n    #[cfg(any(stm32h7, stm32h7rs))]\n    {\n        RCC.pllckselr().modify(|w| {\n            w.set_divm(num, config.prediv);\n            w.set_pllsrc(config.source);\n        });\n\n        #[cfg(stm32h743)]\n        if let Some(fracn) = config.fracn {\n            RCC.pllfracr(num).modify(|w| w.set_fracn(fracn))\n        }\n\n        RCC.pllcfgr().modify(|w| {\n            w.set_pllvcosel(num, vco_range);\n            w.set_pllrge(num, ref_range);\n\n            #[cfg(stm32h743)]\n            if config.fracn.is_some() {\n                w.set_pllfracen(num, true);\n            } else {\n                w.set_pllfracen(num, false);\n            }\n\n            #[cfg(not(stm32h743))]\n            w.set_pllfracen(num, false);\n\n            w.set_divpen(num, p.is_some());\n            w.set_divqen(num, q.is_some());\n            w.set_divren(num, r.is_some());\n            #[cfg(stm32h7rs)]\n            w.set_divsen(num, s.is_some());\n            #[cfg(stm32h7rs)]\n            w.set_divten(num, t.is_some());\n        });\n    }\n\n    RCC.plldivr(num).write(|w| {\n        w.set_plln(config.mul);\n        w.set_pllp(config.divp.unwrap_or(PllDiv::DIV2));\n        w.set_pllq(config.divq.unwrap_or(PllDiv::DIV2));\n        w.set_pllr(config.divr.unwrap_or(PllDiv::DIV2));\n    });\n\n    #[cfg(stm32h7rs)]\n    RCC.plldivr2(num).write(|w| {\n        w.set_plls(config.divs.unwrap_or(PllDivSt::DIV2));\n        w.set_pllt(config.divt.unwrap_or(PllDivSt::DIV2));\n    });\n\n    RCC.cr().modify(|w| w.set_pllon(num, true));\n    while !RCC.cr().read().pllrdy(num) {}\n\n    PllOutput {\n        p,\n        q,\n        r,\n        #[cfg(stm32h7rs)]\n        s,\n        #[cfg(stm32h7rs)]\n        t,\n    }\n}\n\nfn flash_setup(clk: Hertz, vos: VoltageScale) {\n    // RM0481 Rev 1, table 37\n    // LATENCY  WRHIGHFREQ  VOS3           VOS2            VOS1            VOS0\n    //      0           0   0 to 20 MHz    0 to 30 MHz     0 to 34 MHz     0 to 42 MHz\n    //      1           0   20 to 40 MHz   30 to 60 MHz    34 to 68 MHz    42 to 84 MHz\n    //      2           1   40 to 60 MHz   60 to 90 MHz    68 to 102 MHz   84 to 126 MHz\n    //      3           1   60 to 80 MHz   90 to 120 MHz   102 to 136 MHz  126 to 168 MHz\n    //      4           2   80 to 100 MHz  120 to 150 MHz  136 to 170 MHz  168 to 210 MHz\n    //      5           2                                  170 to 200 MHz  210 to 250 MHz\n    #[cfg(stm32h5)]\n    let (latency, wrhighfreq) = match (vos, clk.0) {\n        (VoltageScale::Scale0, ..=42_000_000) => (0, 0),\n        (VoltageScale::Scale0, ..=84_000_000) => (1, 0),\n        (VoltageScale::Scale0, ..=126_000_000) => (2, 1),\n        (VoltageScale::Scale0, ..=168_000_000) => (3, 1),\n        (VoltageScale::Scale0, ..=210_000_000) => (4, 2),\n        (VoltageScale::Scale0, ..=250_000_000) => (5, 2),\n\n        (VoltageScale::Scale1, ..=34_000_000) => (0, 0),\n        (VoltageScale::Scale1, ..=68_000_000) => (1, 0),\n        (VoltageScale::Scale1, ..=102_000_000) => (2, 1),\n        (VoltageScale::Scale1, ..=136_000_000) => (3, 1),\n        (VoltageScale::Scale1, ..=170_000_000) => (4, 2),\n        (VoltageScale::Scale1, ..=200_000_000) => (5, 2),\n\n        (VoltageScale::Scale2, ..=30_000_000) => (0, 0),\n        (VoltageScale::Scale2, ..=60_000_000) => (1, 0),\n        (VoltageScale::Scale2, ..=90_000_000) => (2, 1),\n        (VoltageScale::Scale2, ..=120_000_000) => (3, 1),\n        (VoltageScale::Scale2, ..=150_000_000) => (4, 2),\n\n        (VoltageScale::Scale3, ..=20_000_000) => (0, 0),\n        (VoltageScale::Scale3, ..=40_000_000) => (1, 0),\n        (VoltageScale::Scale3, ..=60_000_000) => (2, 1),\n        (VoltageScale::Scale3, ..=80_000_000) => (3, 1),\n        (VoltageScale::Scale3, ..=100_000_000) => (4, 2),\n\n        _ => unreachable!(),\n    };\n\n    #[cfg(all(flash_h7, not(pwr_h7rm0468)))]\n    let (latency, wrhighfreq) = match (vos, clk.0) {\n        // VOS 0 range VCORE 1.26V - 1.40V\n        (VoltageScale::Scale0, ..=70_000_000) => (0, 0),\n        (VoltageScale::Scale0, ..=140_000_000) => (1, 1),\n        (VoltageScale::Scale0, ..=185_000_000) => (2, 1),\n        (VoltageScale::Scale0, ..=210_000_000) => (2, 2),\n        (VoltageScale::Scale0, ..=225_000_000) => (3, 2),\n        (VoltageScale::Scale0, ..=240_000_000) => (4, 2),\n        // VOS 1 range VCORE 1.15V - 1.26V\n        (VoltageScale::Scale1, ..=70_000_000) => (0, 0),\n        (VoltageScale::Scale1, ..=140_000_000) => (1, 1),\n        (VoltageScale::Scale1, ..=185_000_000) => (2, 1),\n        (VoltageScale::Scale1, ..=210_000_000) => (2, 2),\n        (VoltageScale::Scale1, ..=225_000_000) => (3, 2),\n        // VOS 2 range VCORE 1.05V - 1.15V\n        (VoltageScale::Scale2, ..=55_000_000) => (0, 0),\n        (VoltageScale::Scale2, ..=110_000_000) => (1, 1),\n        (VoltageScale::Scale2, ..=165_000_000) => (2, 1),\n        (VoltageScale::Scale2, ..=224_000_000) => (3, 2),\n        // VOS 3 range VCORE 0.95V - 1.05V\n        (VoltageScale::Scale3, ..=45_000_000) => (0, 0),\n        (VoltageScale::Scale3, ..=90_000_000) => (1, 1),\n        (VoltageScale::Scale3, ..=135_000_000) => (2, 1),\n        (VoltageScale::Scale3, ..=180_000_000) => (3, 2),\n        (VoltageScale::Scale3, ..=224_000_000) => (4, 2),\n        _ => unreachable!(),\n    };\n\n    // See RM0468 Rev 3 Table 16. FLASH recommended number of wait\n    // states and programming delay\n    #[cfg(all(flash_h7, pwr_h7rm0468))]\n    let (latency, wrhighfreq) = match (vos, clk.0) {\n        // VOS 0 range VCORE 1.26V - 1.40V\n        (VoltageScale::Scale0, ..=70_000_000) => (0, 0),\n        (VoltageScale::Scale0, ..=140_000_000) => (1, 1),\n        (VoltageScale::Scale0, ..=210_000_000) => (2, 2),\n        (VoltageScale::Scale0, ..=275_000_000) => (3, 3),\n        // VOS 1 range VCORE 1.15V - 1.26V\n        (VoltageScale::Scale1, ..=67_000_000) => (0, 0),\n        (VoltageScale::Scale1, ..=133_000_000) => (1, 1),\n        (VoltageScale::Scale1, ..=200_000_000) => (2, 2),\n        // VOS 2 range VCORE 1.05V - 1.15V\n        (VoltageScale::Scale2, ..=50_000_000) => (0, 0),\n        (VoltageScale::Scale2, ..=100_000_000) => (1, 1),\n        (VoltageScale::Scale2, ..=150_000_000) => (2, 2),\n        // VOS 3 range VCORE 0.95V - 1.05V\n        (VoltageScale::Scale3, ..=35_000_000) => (0, 0),\n        (VoltageScale::Scale3, ..=70_000_000) => (1, 1),\n        (VoltageScale::Scale3, ..=85_000_000) => (2, 2),\n        _ => unreachable!(),\n    };\n\n    // See RM0455 Rev 10 Table 16. FLASH recommended number of wait\n    // states and programming delay\n    #[cfg(flash_h7ab)]\n    let (latency, wrhighfreq) = match (vos, clk.0) {\n        // VOS 0 range VCORE 1.25V - 1.35V\n        (VoltageScale::Scale0, ..=42_000_000) => (0, 0),\n        (VoltageScale::Scale0, ..=84_000_000) => (1, 0),\n        (VoltageScale::Scale0, ..=126_000_000) => (2, 1),\n        (VoltageScale::Scale0, ..=168_000_000) => (3, 1),\n        (VoltageScale::Scale0, ..=210_000_000) => (4, 2),\n        (VoltageScale::Scale0, ..=252_000_000) => (5, 2),\n        (VoltageScale::Scale0, ..=280_000_000) => (6, 3),\n        // VOS 1 range VCORE 1.15V - 1.25V\n        (VoltageScale::Scale1, ..=38_000_000) => (0, 0),\n        (VoltageScale::Scale1, ..=76_000_000) => (1, 0),\n        (VoltageScale::Scale1, ..=114_000_000) => (2, 1),\n        (VoltageScale::Scale1, ..=152_000_000) => (3, 1),\n        (VoltageScale::Scale1, ..=190_000_000) => (4, 2),\n        (VoltageScale::Scale1, ..=225_000_000) => (5, 2),\n        // VOS 2 range VCORE 1.05V - 1.15V\n        (VoltageScale::Scale2, ..=34) => (0, 0),\n        (VoltageScale::Scale2, ..=68) => (1, 0),\n        (VoltageScale::Scale2, ..=102) => (2, 1),\n        (VoltageScale::Scale2, ..=136) => (3, 1),\n        (VoltageScale::Scale2, ..=160) => (4, 2),\n        // VOS 3 range VCORE 0.95V - 1.05V\n        (VoltageScale::Scale3, ..=22) => (0, 0),\n        (VoltageScale::Scale3, ..=44) => (1, 0),\n        (VoltageScale::Scale3, ..=66) => (2, 1),\n        (VoltageScale::Scale3, ..=88) => (3, 1),\n        _ => unreachable!(),\n    };\n    #[cfg(flash_h7rs)]\n    let (latency, wrhighfreq) = match (vos, clk.0) {\n        // VOS high range VCORE 1.30V - 1.40V\n        (VoltageScale::HIGH, ..=40_000_000) => (0, 0),\n        (VoltageScale::HIGH, ..=80_000_000) => (1, 0),\n        (VoltageScale::HIGH, ..=120_000_000) => (2, 1),\n        (VoltageScale::HIGH, ..=160_000_000) => (3, 1),\n        (VoltageScale::HIGH, ..=200_000_000) => (4, 2),\n        (VoltageScale::HIGH, ..=240_000_000) => (5, 2),\n        (VoltageScale::HIGH, ..=280_000_000) => (6, 3),\n        (VoltageScale::HIGH, ..=320_000_000) => (7, 3),\n        // VOS low range VCORE 1.15V - 1.26V\n        (VoltageScale::LOW, ..=36_000_000) => (0, 0),\n        (VoltageScale::LOW, ..=72_000_000) => (1, 0),\n        (VoltageScale::LOW, ..=108_000_000) => (2, 1),\n        (VoltageScale::LOW, ..=144_000_000) => (3, 1),\n        (VoltageScale::LOW, ..=180_000_000) => (4, 2),\n        (VoltageScale::LOW, ..=216_000_000) => (5, 2),\n        _ => unreachable!(),\n    };\n\n    debug!(\"flash: latency={} wrhighfreq={}\", latency, wrhighfreq);\n\n    FLASH.acr().write(|w| {\n        w.set_wrhighfreq(wrhighfreq);\n        w.set_latency(latency);\n    });\n    while FLASH.acr().read().latency() != latency {}\n}\n"
  },
  {
    "path": "embassy-stm32/src/rcc/hsi48.rs",
    "content": "#![allow(unused)]\n\nuse crate::pac::crs::vals::Syncsrc;\nuse crate::pac::{CRS, RCC};\nuse crate::rcc::{self, SealedRccPeripheral};\nuse crate::time::Hertz;\n\n/// HSI48 speed\npub const HSI48_FREQ: Hertz = Hertz(48_000_000);\n\n/// Configuration for the HSI48 clock\n#[derive(Clone, Copy, Debug)]\npub struct Hsi48Config {\n    /// Enable CRS Sync from USB Start Of Frame (SOF) events.\n    /// Required if HSI48 is going to be used as USB clock.\n    ///\n    /// Other use cases of CRS are not supported yet.\n    pub sync_from_usb: bool,\n}\n\nimpl Hsi48Config {\n    pub const fn new() -> Self {\n        Self { sync_from_usb: false }\n    }\n}\n\nimpl Default for Hsi48Config {\n    fn default() -> Self {\n        Self::new()\n    }\n}\n\npub(crate) fn init_hsi48(config: Hsi48Config) -> Hertz {\n    // Enable VREFINT reference for HSI48 oscillator\n    #[cfg(stm32l0)]\n    crate::pac::SYSCFG.cfgr3().modify(|w| {\n        w.set_enref_hsi48(true);\n        w.set_en_vrefint(true);\n    });\n\n    // Enable HSI48\n    #[cfg(not(any(\n        stm32u5, stm32u3, stm32g0, stm32h5, stm32h7, stm32h7rs, stm32u5, stm32wba, stm32f0, stm32c071\n    )))]\n    let r = RCC.crrcr();\n    #[cfg(any(stm32u5, stm32u3, stm32g0, stm32h5, stm32h7, stm32h7rs, stm32u5, stm32wba, stm32c071))]\n    let r = RCC.cr();\n    #[cfg(any(stm32f0))]\n    let r = RCC.cr2();\n\n    r.modify(|w| w.set_hsi48on(true));\n    while r.read().hsi48rdy() == false {}\n\n    if config.sync_from_usb {\n        rcc::enable_and_reset::<crate::peripherals::CRS>();\n\n        CRS.cfgr().modify(|w| {\n            w.set_syncsrc(Syncsrc::USB);\n        });\n\n        // These are the correct settings for standard USB operation. If other settings\n        // are needed there will need to be additional config options for the CRS.\n        crate::pac::CRS.cr().modify(|w| {\n            w.set_autotrimen(true);\n            w.set_cen(true);\n        });\n    }\n\n    HSI48_FREQ\n}\n\npub(crate) fn disable_hsi48() {\n    // disable CRS if it is enabled\n    rcc::disable::<crate::peripherals::CRS>();\n\n    // Disable HSI48\n    #[cfg(not(any(stm32u5, stm32g0, stm32h5, stm32h7, stm32h7rs, stm32u3, stm32wba, stm32f0, stm32c071)))]\n    let r = RCC.crrcr();\n    #[cfg(any(stm32u5, stm32g0, stm32h5, stm32h7, stm32h7rs, stm32u3, stm32wba, stm32c071))]\n    let r = RCC.cr();\n    #[cfg(any(stm32f0))]\n    let r = RCC.cr2();\n\n    r.modify(|w| w.set_hsi48on(false));\n\n    // Disable VREFINT reference for HSI48 oscillator\n    #[cfg(stm32l0)]\n    crate::pac::SYSCFG.cfgr3().modify(|w| {\n        w.set_enref_hsi48(false);\n    });\n}\n"
  },
  {
    "path": "embassy-stm32/src/rcc/l.rs",
    "content": "#[cfg(any(stm32l0, stm32l1))]\npub use crate::pac::pwr::vals::Vos as VoltageScale;\nuse crate::pac::rcc::regs::Cfgr;\n#[cfg(any(stm32wb, stm32wl))]\npub use crate::pac::rcc::vals::Hsepre as HsePrescaler;\npub use crate::pac::rcc::vals::{Hpre as AHBPrescaler, Msirange as MSIRange, Ppre as APBPrescaler, Sw as Sysclk};\nuse crate::pac::{FLASH, RCC};\nuse crate::rcc::LSI_FREQ;\nuse crate::time::Hertz;\n\n/// HSI speed\npub const HSI_FREQ: Hertz = Hertz(16_000_000);\n\n#[derive(Clone, Copy, Eq, PartialEq)]\npub enum HseMode {\n    /// crystal/ceramic oscillator (HSEBYP=0)\n    Oscillator,\n    /// external analog clock (low swing) (HSEBYP=1)\n    Bypass,\n}\n\n#[derive(Clone, Copy, Eq, PartialEq)]\npub struct Hse {\n    /// HSE frequency.\n    pub freq: Hertz,\n    /// HSE mode.\n    pub mode: HseMode,\n    /// HSE prescaler\n    #[cfg(any(stm32wb, stm32wl))]\n    pub prescaler: HsePrescaler,\n}\n\n/// Clocks configuration\n#[derive(Clone, Copy)]\npub struct Config {\n    // base clock sources\n    pub msi: Option<MSIRange>,\n    pub hsi: bool,\n    pub hse: Option<Hse>,\n    #[cfg(crs)]\n    pub hsi48: Option<super::Hsi48Config>,\n\n    // pll\n    pub pll: Option<Pll>,\n    #[cfg(any(stm32l4, stm32l5, stm32wb))]\n    pub pllsai1: Option<Pll>,\n    #[cfg(any(stm32l47x, stm32l48x, stm32l49x, stm32l4ax, rcc_l4plus, stm32l5))]\n    pub pllsai2: Option<Pll>,\n\n    // sysclk, buses.\n    pub sys: Sysclk,\n    pub ahb_pre: AHBPrescaler,\n    pub apb1_pre: APBPrescaler,\n    #[cfg(not(stm32u0))]\n    pub apb2_pre: APBPrescaler,\n    #[cfg(any(stm32wl5x, stm32wb))]\n    pub core2_ahb_pre: AHBPrescaler,\n    #[cfg(any(stm32wl, stm32wb))]\n    pub shared_ahb_pre: AHBPrescaler,\n\n    // low speed LSI/LSE/RTC\n    pub ls: super::LsConfig,\n\n    #[cfg(any(stm32l0, stm32l1))]\n    pub voltage_scale: VoltageScale,\n\n    /// Per-peripheral kernel clock selection muxes\n    pub mux: super::mux::ClockMux,\n}\n\nimpl Config {\n    pub const fn new() -> Self {\n        Config {\n            hse: None,\n            hsi: false,\n            msi: Some(MSIRange::RANGE4M),\n            sys: Sysclk::MSI,\n            ahb_pre: AHBPrescaler::DIV1,\n            apb1_pre: APBPrescaler::DIV1,\n            #[cfg(not(stm32u0))]\n            apb2_pre: APBPrescaler::DIV1,\n            #[cfg(any(stm32wl5x, stm32wb))]\n            core2_ahb_pre: AHBPrescaler::DIV1,\n            #[cfg(any(stm32wl, stm32wb))]\n            shared_ahb_pre: AHBPrescaler::DIV1,\n            pll: None,\n            #[cfg(any(stm32l4, stm32l5, stm32wb))]\n            pllsai1: None,\n            #[cfg(any(stm32l47x, stm32l48x, stm32l49x, stm32l4ax, rcc_l4plus, stm32l5))]\n            pllsai2: None,\n            #[cfg(crs)]\n            hsi48: Some(crate::rcc::Hsi48Config::new()),\n            ls: crate::rcc::LsConfig::new(),\n            #[cfg(any(stm32l0, stm32l1))]\n            voltage_scale: VoltageScale::RANGE1,\n            mux: super::mux::ClockMux::default(),\n        }\n    }\n}\n\nimpl Default for Config {\n    fn default() -> Config {\n        Self::new()\n    }\n}\n\n#[cfg(stm32wb)]\npub const WPAN_DEFAULT: Config = Config {\n    hse: Some(Hse {\n        freq: Hertz(32_000_000),\n        mode: HseMode::Oscillator,\n        prescaler: HsePrescaler::DIV1,\n    }),\n    sys: Sysclk::PLL1_R,\n    #[cfg(crs)]\n    hsi48: Some(super::Hsi48Config { sync_from_usb: false }),\n    msi: None,\n    hsi: false,\n\n    ls: super::LsConfig::default_lse(),\n\n    pll: Some(Pll {\n        source: PllSource::HSE,\n        prediv: PllPreDiv::DIV2,\n        mul: PllMul::MUL12,\n        divp: Some(PllPDiv::DIV3), // 32 / 2 * 12 / 3 = 64Mhz\n        divq: Some(PllQDiv::DIV4), // 32 / 2 * 12 / 4 = 48Mhz\n        divr: Some(PllRDiv::DIV3), // 32 / 2 * 12 / 3 = 64Mhz\n    }),\n    pllsai1: None,\n\n    ahb_pre: AHBPrescaler::DIV1,\n    core2_ahb_pre: AHBPrescaler::DIV2,\n    shared_ahb_pre: AHBPrescaler::DIV1,\n    apb1_pre: APBPrescaler::DIV1,\n    apb2_pre: APBPrescaler::DIV1,\n\n    mux: {\n        use crate::pac::rcc::vals::Rfwkpsel;\n\n        let mut mux = super::mux::ClockMux::default();\n\n        mux.rfwkpsel = Rfwkpsel::LSE;\n        mux\n    },\n};\n\nfn msi_enable(range: MSIRange) {\n    #[cfg(any(stm32l4, stm32l5, stm32wb, stm32wl, stm32u0))]\n    RCC.cr().modify(|w| {\n        #[cfg(not(stm32wb))]\n        w.set_msirgsel(crate::pac::rcc::vals::Msirgsel::CR);\n        w.set_msirange(range);\n        w.set_msipllen(false);\n    });\n    #[cfg(any(stm32l0, stm32l1))]\n    RCC.icscr().modify(|w| w.set_msirange(range));\n\n    RCC.cr().modify(|w| w.set_msion(true));\n    while !RCC.cr().read().msirdy() {}\n}\n\npub(crate) unsafe fn init(config: Config) {\n    // Switch to MSI to prevent problems with PLL configuration.\n    if !RCC.cr().read().msion() {\n        // Turn on MSI and configure it to 4MHz.\n        msi_enable(MSIRange::RANGE4M)\n    }\n    if RCC.cfgr().read().sws() != Sysclk::MSI {\n        // Set MSI as a clock source, reset prescalers.\n        RCC.cfgr().write_value(Cfgr::default());\n        // Wait for clock switch status bits to change.\n        while RCC.cfgr().read().sws() != Sysclk::MSI {}\n    }\n\n    #[cfg(stm32wl)]\n    {\n        // Set max latency\n        FLASH.acr().modify(|w| w.set_prften(true));\n        FLASH.acr().modify(|w| w.set_latency(2));\n    }\n\n    // Set voltage scale\n    #[cfg(any(stm32l0, stm32l1))]\n    {\n        while crate::pac::PWR.csr().read().vosf() {}\n        crate::pac::PWR.cr().write(|w| w.set_vos(config.voltage_scale));\n        while crate::pac::PWR.csr().read().vosf() {}\n    }\n\n    #[cfg(stm32l5)]\n    crate::pac::PWR.cr1().modify(|w| {\n        w.set_vos(crate::pac::pwr::vals::Vos::RANGE0);\n    });\n\n    let rtc = config.ls.init();\n\n    let lse = config.ls.lse.map(|l| l.frequency);\n    let lsi = config.ls.lsi.then_some(LSI_FREQ);\n\n    let msi = config.msi.map(|range| {\n        msi_enable(range);\n        msirange_to_hertz(range)\n    });\n\n    // If LSE is enabled and the right freq, enable calibration of MSI\n    #[cfg(any(stm32l4, stm32l5, stm32wb, stm32wl))]\n    if config.ls.lse.map(|x| x.frequency) == Some(Hertz(32_768)) {\n        RCC.cr().modify(|w| w.set_msipllen(true));\n    }\n\n    let hsi = config.hsi.then(|| {\n        RCC.cr().modify(|w| w.set_hsion(true));\n        while !RCC.cr().read().hsirdy() {}\n\n        HSI_FREQ\n    });\n\n    let hse = config.hse.map(|hse| {\n        RCC.cr().modify(|w| {\n            #[cfg(stm32wl)]\n            w.set_hsebyppwr(hse.mode == HseMode::Bypass);\n            #[cfg(not(stm32wl))]\n            w.set_hsebyp(hse.mode == HseMode::Bypass);\n            w.set_hseon(true);\n        });\n        while !RCC.cr().read().hserdy() {}\n\n        hse.freq\n    });\n\n    #[cfg(crs)]\n    let hsi48 = config.hsi48.map(|config| super::init_hsi48(config));\n    #[cfg(not(crs))]\n    let hsi48: Option<Hertz> = None;\n\n    let _plls = [\n        &config.pll,\n        #[cfg(any(stm32l4, stm32l5, stm32wb))]\n        &config.pllsai1,\n        #[cfg(any(stm32l47x, stm32l48x, stm32l49x, stm32l4ax, rcc_l4plus, stm32l5))]\n        &config.pllsai2,\n    ];\n\n    // L4 has shared PLLSRC, PLLM, check it's equal in all PLLs.\n    #[cfg(all(stm32l4, not(rcc_l4plus)))]\n    match super::util::get_equal(_plls.into_iter().flatten().map(|p| (p.source, p.prediv))) {\n        Err(()) => panic!(\"Source must be equal across all enabled PLLs.\"),\n        Ok(None) => {}\n        Ok(Some((source, prediv))) => RCC.pllcfgr().write(|w| {\n            w.set_pllm(prediv);\n            w.set_pllsrc(source);\n        }),\n    };\n\n    // L4+, WL has shared PLLSRC, check it's equal in all PLLs.\n    #[cfg(any(rcc_l4plus, stm32wl))]\n    match super::util::get_equal(_plls.into_iter().flatten().map(|p| p.source)) {\n        Err(()) => panic!(\"Source must be equal across all enabled PLLs.\"),\n        Ok(None) => {}\n        Ok(Some(source)) => RCC.pllcfgr().write(|w| {\n            w.set_pllsrc(source);\n        }),\n    };\n\n    let pll_input = PllInput {\n        hse,\n        hsi,\n        #[cfg(any(stm32l4, stm32l5, stm32wb, stm32wl, stm32u0))]\n        msi,\n    };\n    let pll = config.pll.map_or_else(\n        || {\n            pll_enable(PllInstance::Pll, false);\n            PllOutput::default()\n        },\n        |c| init_pll(PllInstance::Pll, Some(c), &pll_input),\n    );\n    #[cfg(any(stm32l4, stm32l5, stm32wb))]\n    let pllsai1 = config.pllsai1.map_or_else(\n        || {\n            pll_enable(PllInstance::Pllsai1, false);\n            PllOutput::default()\n        },\n        |c| init_pll(PllInstance::Pllsai1, Some(c), &pll_input),\n    );\n    #[cfg(any(stm32l47x, stm32l48x, stm32l49x, stm32l4ax, rcc_l4plus, stm32l5))]\n    let pllsai2 = config.pllsai2.map_or_else(\n        || {\n            pll_enable(PllInstance::Pllsai2, false);\n            PllOutput::default()\n        },\n        |c| init_pll(PllInstance::Pllsai2, Some(c), &pll_input),\n    );\n\n    let sys_clk = match config.sys {\n        Sysclk::HSE => hse.unwrap(),\n        Sysclk::HSI => hsi.unwrap(),\n        Sysclk::MSI => msi.unwrap(),\n        Sysclk::PLL1_R => pll.r.unwrap(),\n        #[cfg(stm32u0)]\n        Sysclk::LSI | Sysclk::LSE => todo!(),\n        #[cfg(stm32u0)]\n        Sysclk::_RESERVED_6 | Sysclk::_RESERVED_7 => unreachable!(),\n    };\n\n    #[cfg(rcc_l4plus)]\n    assert!(sys_clk.0 <= 120_000_000);\n    #[cfg(all(stm32l4, not(rcc_l4plus)))]\n    assert!(sys_clk.0 <= 80_000_000);\n\n    let hclk1 = sys_clk / config.ahb_pre;\n    let (pclk1, pclk1_tim) = super::util::calc_pclk(hclk1, config.apb1_pre);\n    #[cfg(not(stm32u0))]\n    let (pclk2, pclk2_tim) = super::util::calc_pclk(hclk1, config.apb2_pre);\n    #[cfg(any(stm32l4, stm32l5, stm32wlex))]\n    let hclk2 = hclk1;\n    #[cfg(any(stm32wl5x, stm32wb))]\n    let hclk2 = sys_clk / config.core2_ahb_pre;\n    #[cfg(any(stm32l4, stm32l5, stm32wlex))]\n    let hclk3 = hclk1;\n    #[cfg(any(stm32wl5x, stm32wb))]\n    let hclk3 = sys_clk / config.shared_ahb_pre;\n\n    // Set flash wait states\n    #[cfg(any(stm32l0, stm32l1))]\n    let latency = match (config.voltage_scale, sys_clk.0) {\n        (VoltageScale::RANGE1, ..=16_000_000) => false,\n        (VoltageScale::RANGE2, ..=8_000_000) => false,\n        (VoltageScale::RANGE3, ..=4_200_000) => false,\n        _ => true,\n    };\n    #[cfg(stm32l4)]\n    let latency = match hclk1.0 {\n        0..=16_000_000 => 0,\n        0..=32_000_000 => 1,\n        0..=48_000_000 => 2,\n        0..=64_000_000 => 3,\n        _ => 4,\n    };\n    #[cfg(stm32l5)]\n    let latency = match hclk1.0 {\n        // VCORE Range 0 (performance), others TODO\n        0..=20_000_000 => 0,\n        0..=40_000_000 => 1,\n        0..=60_000_000 => 2,\n        0..=80_000_000 => 3,\n        0..=100_000_000 => 4,\n        _ => 5,\n    };\n    #[cfg(stm32wl)]\n    let latency = match hclk3.0 {\n        // VOS RANGE1, others TODO.\n        ..=18_000_000 => 0,\n        ..=36_000_000 => 1,\n        _ => 2,\n    };\n    #[cfg(stm32wb)]\n    let latency = match hclk3.0 {\n        // VOS RANGE1, others TODO.\n        ..=18_000_000 => 0,\n        ..=36_000_000 => 1,\n        ..=54_000_000 => 2,\n        ..=64_000_000 => 3,\n        _ => 4,\n    };\n    #[cfg(stm32u0)]\n    let latency = match hclk1.0 {\n        // VOS RANGE1, others TODO.\n        ..=24_000_000 => 0,\n        ..=48_000_000 => 1,\n        _ => 2,\n    };\n\n    #[cfg(stm32l1)]\n    FLASH.acr().write(|w| w.set_acc64(true));\n    #[cfg(not(stm32l5))]\n    FLASH.acr().modify(|w| w.set_prften(true));\n    FLASH.acr().modify(|w| w.set_latency(latency));\n    while FLASH.acr().read().latency() != latency {}\n\n    RCC.cfgr().modify(|w| {\n        w.set_sw(config.sys);\n        w.set_hpre(config.ahb_pre);\n        #[cfg(stm32u0)]\n        w.set_ppre(config.apb1_pre);\n        #[cfg(not(stm32u0))]\n        w.set_ppre1(config.apb1_pre);\n        #[cfg(not(stm32u0))]\n        w.set_ppre2(config.apb2_pre);\n    });\n    while RCC.cfgr().read().sws() != config.sys {}\n\n    #[cfg(any(stm32wl, stm32wb))]\n    {\n        RCC.extcfgr().modify(|w| {\n            w.set_shdhpre(config.shared_ahb_pre);\n            #[cfg(any(stm32wl5x, stm32wb))]\n            w.set_c2hpre(config.core2_ahb_pre);\n        });\n        while !RCC.extcfgr().read().shdhpref() {}\n        #[cfg(any(stm32wl5x, stm32wb))]\n        while !RCC.extcfgr().read().c2hpref() {}\n    }\n\n    // Disable HSI if not used\n    if !config.hsi {\n        RCC.cr().modify(|w| w.set_hsion(false));\n    }\n\n    // Disable the HSI48, if not used\n    #[cfg(crs)]\n    if config.hsi48.is_none() {\n        super::disable_hsi48();\n    }\n\n    config.mux.init();\n\n    set_clocks!(\n        sys: Some(sys_clk),\n        hclk1: Some(hclk1),\n        #[cfg(any(stm32l4, stm32l5, stm32wb, stm32wl))]\n        hclk2: Some(hclk2),\n        #[cfg(any(stm32l4, stm32l5, stm32wb, stm32wl))]\n        hclk3: Some(hclk3),\n        pclk1: Some(pclk1),\n        #[cfg(not(stm32u0))]\n        pclk2: Some(pclk2),\n        pclk1_tim: Some(pclk1_tim),\n        #[cfg(not(stm32u0))]\n        pclk2_tim: Some(pclk2_tim),\n        #[cfg(stm32wl)]\n        pclk3: Some(hclk3),\n        hsi: hsi,\n        hse: hse,\n        msi: msi,\n        hsi48: hsi48,\n\n        #[cfg(any(stm32l0, stm32l1))]\n        pll1_vco: pll.vco,\n\n        #[cfg(not(any(stm32l0, stm32l1)))]\n        pll1_p: pll.p,\n        #[cfg(not(any(stm32l0, stm32l1)))]\n        pll1_q: pll.q,\n        pll1_r: pll.r,\n\n        #[cfg(any(stm32l4, stm32l5, stm32wb))]\n        pllsai1_p: pllsai1.p,\n        #[cfg(any(stm32l4, stm32l5, stm32wb))]\n        pllsai1_q: pllsai1.q,\n        #[cfg(any(stm32l4, stm32l5, stm32wb))]\n        pllsai1_r: pllsai1.r,\n\n        #[cfg(not(any(stm32l47x, stm32l48x, stm32l49x, stm32l4ax, rcc_l4plus, stm32l5)))]\n        pllsai2_p: None,\n        #[cfg(not(any(stm32l47x, stm32l48x, stm32l49x, stm32l4ax, rcc_l4plus, stm32l5)))]\n        pllsai2_q: None,\n        #[cfg(not(any(stm32l47x, stm32l48x, stm32l49x, stm32l4ax, rcc_l4plus, stm32l5)))]\n        pllsai2_r: None,\n\n        #[cfg(any(stm32l47x, stm32l48x, stm32l49x, stm32l4ax, rcc_l4plus, stm32l5))]\n        pllsai2_p: pllsai2.p,\n        #[cfg(any(stm32l47x, stm32l48x, stm32l49x, stm32l4ax, rcc_l4plus, stm32l5))]\n        pllsai2_q: pllsai2.q,\n        #[cfg(any(stm32l47x, stm32l48x, stm32l49x, stm32l4ax, rcc_l4plus, stm32l5))]\n        pllsai2_r: pllsai2.r,\n\n        #[cfg(dsihost)]\n        dsi_phy: None, // DSI PLL clock not supported, don't call `RccPeripheral::frequency()` in the drivers\n\n        rtc: rtc,\n        lse: lse,\n        lsi: lsi,\n\n        // TODO\n        sai1_extclk: None,\n        sai2_extclk: None,\n    );\n}\n\n#[cfg(any(stm32l0, stm32l1))]\nfn msirange_to_hertz(range: MSIRange) -> Hertz {\n    Hertz(32_768 * (1 << (range as u8 + 1)))\n}\n\n#[cfg(any(stm32l4, stm32l5, stm32wb, stm32wl, stm32u0))]\nfn msirange_to_hertz(range: MSIRange) -> Hertz {\n    match range {\n        MSIRange::RANGE100K => Hertz(100_000),\n        MSIRange::RANGE200K => Hertz(200_000),\n        MSIRange::RANGE400K => Hertz(400_000),\n        MSIRange::RANGE800K => Hertz(800_000),\n        MSIRange::RANGE1M => Hertz(1_000_000),\n        MSIRange::RANGE2M => Hertz(2_000_000),\n        MSIRange::RANGE4M => Hertz(4_000_000),\n        MSIRange::RANGE8M => Hertz(8_000_000),\n        MSIRange::RANGE16M => Hertz(16_000_000),\n        MSIRange::RANGE24M => Hertz(24_000_000),\n        MSIRange::RANGE32M => Hertz(32_000_000),\n        MSIRange::RANGE48M => Hertz(48_000_000),\n        _ => unreachable!(),\n    }\n}\n\n#[derive(PartialEq, Eq, Clone, Copy)]\nenum PllInstance {\n    Pll,\n    #[cfg(any(stm32l4, stm32l5, stm32wb))]\n    Pllsai1,\n    #[cfg(any(stm32l47x, stm32l48x, stm32l49x, stm32l4ax, rcc_l4plus, stm32l5))]\n    Pllsai2,\n}\n\nfn pll_enable(instance: PllInstance, enabled: bool) {\n    match instance {\n        PllInstance::Pll => {\n            RCC.cr().modify(|w| w.set_pllon(enabled));\n            while RCC.cr().read().pllrdy() != enabled {}\n        }\n        #[cfg(any(stm32l4, stm32l5, stm32wb))]\n        PllInstance::Pllsai1 => {\n            RCC.cr().modify(|w| w.set_pllsai1on(enabled));\n            while RCC.cr().read().pllsai1rdy() != enabled {}\n        }\n        #[cfg(any(stm32l47x, stm32l48x, stm32l49x, stm32l4ax, rcc_l4plus, stm32l5))]\n        PllInstance::Pllsai2 => {\n            RCC.cr().modify(|w| w.set_pllsai2on(enabled));\n            while RCC.cr().read().pllsai2rdy() != enabled {}\n        }\n    }\n}\n\npub use pll::*;\n\n#[cfg(any(stm32l0, stm32l1))]\nmod pll {\n    use super::{PllInstance, pll_enable};\n    use crate::pac::RCC;\n    pub use crate::pac::rcc::vals::{Plldiv as PllDiv, Pllmul as PllMul, Pllsrc as PllSource};\n    use crate::time::Hertz;\n\n    #[derive(Clone, Copy)]\n    pub struct Pll {\n        /// PLL source\n        pub source: PllSource,\n\n        /// PLL multiplication factor.\n        pub mul: PllMul,\n\n        /// PLL main output division factor.\n        pub div: PllDiv,\n    }\n\n    pub(super) struct PllInput {\n        pub hsi: Option<Hertz>,\n        pub hse: Option<Hertz>,\n    }\n\n    #[allow(unused)]\n    #[derive(Default)]\n    pub(super) struct PllOutput {\n        pub r: Option<Hertz>,\n        pub vco: Option<Hertz>,\n    }\n\n    pub(super) fn init_pll(instance: PllInstance, config: Option<Pll>, input: &PllInput) -> PllOutput {\n        // Disable PLL\n        pll_enable(instance, false);\n\n        let Some(pll) = config else { return PllOutput::default() };\n\n        let pll_src = match pll.source {\n            PllSource::HSE => unwrap!(input.hse),\n            PllSource::HSI => unwrap!(input.hsi),\n        };\n\n        let vco_freq = pll_src * pll.mul;\n\n        let r = vco_freq / pll.div;\n\n        assert!(r <= Hertz(32_000_000));\n\n        RCC.cfgr().write(move |w| {\n            w.set_pllmul(pll.mul);\n            w.set_plldiv(pll.div);\n            w.set_pllsrc(pll.source);\n        });\n\n        // Enable PLL\n        pll_enable(instance, true);\n\n        PllOutput {\n            r: Some(r),\n            vco: Some(vco_freq),\n        }\n    }\n}\n\n#[cfg(any(stm32l4, stm32l5, stm32wb, stm32wl, stm32u0))]\nmod pll {\n    use super::{PllInstance, pll_enable};\n    use crate::pac::RCC;\n    pub use crate::pac::rcc::vals::{\n        Pllm as PllPreDiv, Plln as PllMul, Pllp as PllPDiv, Pllq as PllQDiv, Pllr as PllRDiv, Pllsrc as PllSource,\n    };\n    use crate::time::Hertz;\n\n    #[derive(Clone, Copy)]\n    pub struct Pll {\n        /// PLL source\n        pub source: PllSource,\n\n        /// PLL pre-divider (DIVM).\n        pub prediv: PllPreDiv,\n\n        /// PLL multiplication factor.\n        pub mul: PllMul,\n\n        /// PLL P division factor. If None, PLL P output is disabled.\n        pub divp: Option<PllPDiv>,\n        /// PLL Q division factor. If None, PLL Q output is disabled.\n        pub divq: Option<PllQDiv>,\n        /// PLL R division factor. If None, PLL R output is disabled.\n        pub divr: Option<PllRDiv>,\n    }\n\n    pub(super) struct PllInput {\n        pub hsi: Option<Hertz>,\n        pub hse: Option<Hertz>,\n        pub msi: Option<Hertz>,\n    }\n\n    #[allow(unused)]\n    #[derive(Default)]\n    pub(super) struct PllOutput {\n        pub p: Option<Hertz>,\n        pub q: Option<Hertz>,\n        pub r: Option<Hertz>,\n    }\n\n    pub(super) fn init_pll(instance: PllInstance, config: Option<Pll>, input: &PllInput) -> PllOutput {\n        // Disable PLL\n        pll_enable(instance, false);\n\n        let Some(pll) = config else { return PllOutput::default() };\n\n        let pll_src = match pll.source {\n            PllSource::DISABLE => panic!(\"must not select PLL source as DISABLE\"),\n            PllSource::HSE => unwrap!(input.hse),\n            PllSource::HSI => unwrap!(input.hsi),\n            PllSource::MSI => unwrap!(input.msi),\n        };\n\n        let vco_freq = pll_src / pll.prediv * pll.mul;\n\n        let p = pll.divp.map(|div| vco_freq / div);\n        let q = pll.divq.map(|div| vco_freq / div);\n        let r = pll.divr.map(|div| vco_freq / div);\n\n        #[cfg(stm32l5)]\n        if instance == PllInstance::Pllsai2 {\n            assert!(q.is_none(), \"PLLSAI2_Q is not available on L5\");\n            assert!(r.is_none(), \"PLLSAI2_R is not available on L5\");\n        }\n\n        macro_rules! write_fields {\n            ($w:ident) => {\n                $w.set_plln(pll.mul);\n                if let Some(divp) = pll.divp {\n                    $w.set_pllp(divp);\n                    $w.set_pllpen(true);\n                }\n                if let Some(divq) = pll.divq {\n                    $w.set_pllq(divq);\n                    $w.set_pllqen(true);\n                }\n                if let Some(divr) = pll.divr {\n                    $w.set_pllr(divr);\n                    $w.set_pllren(true);\n                }\n            };\n        }\n\n        match instance {\n            PllInstance::Pll => RCC.pllcfgr().write(|w| {\n                w.set_pllm(pll.prediv);\n                w.set_pllsrc(pll.source);\n                write_fields!(w);\n            }),\n            #[cfg(any(stm32l4, stm32l5, stm32wb))]\n            PllInstance::Pllsai1 => RCC.pllsai1cfgr().write(|w| {\n                #[cfg(any(rcc_l4plus, stm32l5))]\n                w.set_pllm(pll.prediv);\n                #[cfg(stm32l5)]\n                w.set_pllsrc(pll.source);\n                write_fields!(w);\n            }),\n            #[cfg(any(stm32l47x, stm32l48x, stm32l49x, stm32l4ax, rcc_l4plus, stm32l5))]\n            PllInstance::Pllsai2 => RCC.pllsai2cfgr().write(|w| {\n                #[cfg(any(rcc_l4plus, stm32l5))]\n                w.set_pllm(pll.prediv);\n                #[cfg(stm32l5)]\n                w.set_pllsrc(pll.source);\n                write_fields!(w);\n            }),\n        }\n\n        // Enable PLL\n        pll_enable(instance, true);\n\n        PllOutput { p, q, r }\n    }\n}\n"
  },
  {
    "path": "embassy-stm32/src/rcc/mco.rs",
    "content": "use core::marker::PhantomData;\n\nuse embassy_hal_internal::PeripheralType;\n\nuse crate::gpio::{AfType, OutputType, Speed};\nuse crate::pac::RCC;\n#[cfg(any(\n    rcc_f2,\n    rcc_f410,\n    rcc_f4,\n    rcc_f7,\n    rcc_h50,\n    rcc_h5,\n    rcc_h7ab,\n    rcc_h7rm0433,\n    rcc_h7,\n    rcc_h7rs,\n    rcc_n6\n))]\npub use crate::pac::rcc::vals::Mco1sel as Mco1Source;\n#[cfg(any(\n    rcc_f2,\n    rcc_f410,\n    rcc_f4,\n    rcc_f7,\n    rcc_h50,\n    rcc_h5,\n    rcc_h7ab,\n    rcc_h7rm0433,\n    rcc_h7,\n    rcc_h7rs,\n    rcc_n6,\n    rcc_u3,\n))]\npub use crate::pac::rcc::vals::Mco2sel as Mco2Source;\n#[cfg(not(any(stm32f1, rcc_f0v1, rcc_f3v1, rcc_f37)))]\npub use crate::pac::rcc::vals::Mcopre as McoPrescaler;\n#[cfg(not(any(\n    rcc_f2,\n    rcc_f410,\n    rcc_f4,\n    rcc_f7,\n    rcc_h50,\n    rcc_h5,\n    rcc_h7ab,\n    rcc_h7rm0433,\n    rcc_h7,\n    rcc_h7rs,\n    rcc_n6\n)))]\npub use crate::pac::rcc::vals::Mcosel as McoSource;\nuse crate::{Peri, peripherals};\n\n#[cfg(any(stm32f1, rcc_f0v1, rcc_f3v1, rcc_f37))]\n#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd)]\npub enum McoPrescaler {\n    DIV1,\n}\n\npub(crate) trait SealedMcoInstance {}\n\n#[allow(private_bounds)]\npub trait McoInstance: PeripheralType + SealedMcoInstance + 'static {\n    type Source;\n\n    #[doc(hidden)]\n    unsafe fn _apply_clock_settings(source: Self::Source, prescaler: super::McoPrescaler);\n}\n\npin_trait!(McoPin, McoInstance);\n\nmacro_rules! impl_peri {\n    ($peri:ident, $source:ident, $set_source:ident, $set_prescaler:ident) => {\n        impl SealedMcoInstance for peripherals::$peri {}\n        impl McoInstance for peripherals::$peri {\n            type Source = $source;\n\n            unsafe fn _apply_clock_settings(source: Self::Source, _prescaler: McoPrescaler) {\n                #[cfg(not(any(stm32u3, stm32u5, stm32wba, stm32n6)))]\n                let r = RCC.cfgr();\n                #[cfg(any(stm32u3, stm32u5, stm32wba))]\n                let r = RCC.cfgr1();\n                #[cfg(any(stm32n6))]\n                let r = RCC.ccipr5();\n\n                r.modify(|w| {\n                    w.$set_source(source);\n                    #[cfg(not(any(stm32f1, rcc_f0v1, rcc_f3v1, rcc_f37)))]\n                    w.$set_prescaler(_prescaler);\n                });\n            }\n        }\n    };\n}\n\n#[cfg(any(rcc_c0, rcc_c0v2, rcc_g0x0, rcc_g0x1, rcc_u0))]\n#[allow(unused_imports)]\nuse self::{McoSource as Mco1Source, McoSource as Mco2Source};\n\n#[cfg(mco)]\nimpl_peri!(MCO, McoSource, set_mcosel, set_mcopre);\n#[cfg(mco1)]\nimpl_peri!(MCO1, Mco1Source, set_mco1sel, set_mco1pre);\n#[cfg(mco2)]\nimpl_peri!(MCO2, Mco2Source, set_mco2sel, set_mco2pre);\n\npub struct Mco<'d, T: McoInstance> {\n    phantom: PhantomData<&'d mut T>,\n}\n\nimpl<'d, T: McoInstance> Mco<'d, T> {\n    /// Create a new MCO instance.\n    pub fn new(_peri: Peri<'d, T>, pin: Peri<'d, impl McoPin<T>>, source: T::Source, config: McoConfig) -> Self {\n        critical_section::with(|_| unsafe {\n            T::_apply_clock_settings(source, config.prescaler);\n            set_as_af!(pin, AfType::output(OutputType::PushPull, config.speed));\n        });\n\n        Self { phantom: PhantomData }\n    }\n}\n\n#[non_exhaustive]\npub struct McoConfig {\n    /// Master Clock Out prescaler\n    pub prescaler: McoPrescaler,\n    /// IO Drive Strength\n    pub speed: Speed,\n}\n\nimpl Default for McoConfig {\n    fn default() -> Self {\n        Self {\n            prescaler: McoPrescaler::DIV1,\n            speed: Speed::VeryHigh,\n        }\n    }\n}\n"
  },
  {
    "path": "embassy-stm32/src/rcc/mod.rs",
    "content": "//! Reset and Clock Control (RCC)\n\n#![macro_use]\n#![allow(missing_docs)] // TODO\n\nuse core::mem::MaybeUninit;\n\nmod bd;\npub use bd::*;\n\n#[cfg(any(mco, mco1, mco2))]\nmod mco;\nuse critical_section::CriticalSection;\n#[cfg(any(mco, mco1, mco2))]\npub use mco::*;\n\n#[cfg(crs)]\nmod hsi48;\n#[cfg(crs)]\npub use hsi48::*;\n\n#[cfg_attr(any(stm32f0, stm32f1, stm32f3), path = \"f013.rs\")]\n#[cfg_attr(any(stm32f2, stm32f4, stm32f7), path = \"f247.rs\")]\n#[cfg_attr(stm32c0, path = \"c0.rs\")]\n#[cfg_attr(stm32g0, path = \"g0.rs\")]\n#[cfg_attr(stm32g4, path = \"g4.rs\")]\n#[cfg_attr(any(stm32h5, stm32h7, stm32h7rs), path = \"h.rs\")]\n#[cfg_attr(any(stm32l0, stm32l1, stm32l4, stm32l5, stm32wb, stm32wl, stm32u0), path = \"l.rs\")]\n#[cfg_attr(stm32u3, path = \"u3.rs\")]\n#[cfg_attr(stm32u5, path = \"u5.rs\")]\n#[cfg_attr(stm32wba, path = \"wba.rs\")]\n#[cfg_attr(stm32n6, path = \"n6.rs\")]\nmod _version;\n\npub use _version::*;\nuse stm32_metapac::RCC;\n\npub use crate::_generated::{Clocks, mux};\nuse crate::time::Hertz;\n\n#[cfg(feature = \"low-power\")]\n/// Must be written within a critical section\n///\n/// May be read without a critical section\npub(crate) static mut REFCOUNT_STOP1: u32 = 0;\n\n#[cfg(feature = \"low-power\")]\n/// Must be written within a critical section\n///\n/// May be read without a critical section\npub(crate) static mut REFCOUNT_STOP2: u32 = 0;\n\n#[cfg(all(feature = \"low-power\", not(feature = \"_dual-core\")))]\npub(crate) static mut RCC_CONFIG: Option<Config> = None;\n\n#[cfg(all(feature = \"low-power\", feature = \"_dual-core\"))]\nstatic RCC_CONFIG_PTR: core::sync::atomic::AtomicPtr<MaybeUninit<Option<Config>>> =\n    core::sync::atomic::AtomicPtr::new(core::ptr::null_mut());\n\n#[cfg(backup_sram)]\npub(crate) static mut BKSRAM_RETAINED: bool = false;\n\n#[cfg(not(feature = \"_dual-core\"))]\n/// Frozen clock frequencies\n///\n/// The existence of this value indicates that the clock configuration can no longer be changed\nstatic mut CLOCK_FREQS: MaybeUninit<Clocks> = MaybeUninit::uninit();\n\n#[cfg(feature = \"_dual-core\")]\nstatic CLOCK_FREQS_PTR: core::sync::atomic::AtomicPtr<MaybeUninit<Clocks>> =\n    core::sync::atomic::AtomicPtr::new(core::ptr::null_mut());\n\n#[cfg(feature = \"_dual-core\")]\npub(crate) fn set_freqs_ptr(freqs: *mut MaybeUninit<Clocks>) {\n    CLOCK_FREQS_PTR.store(freqs, core::sync::atomic::Ordering::SeqCst);\n}\n\n#[cfg(all(feature = \"low-power\", feature = \"_dual-core\"))]\npub(crate) fn set_rcc_config_ptr(config: *mut MaybeUninit<Option<Config>>) {\n    RCC_CONFIG_PTR.store(config, core::sync::atomic::Ordering::SeqCst);\n}\n\n#[cfg(not(feature = \"_dual-core\"))]\n/// Sets the clock frequencies\n///\n/// Safety: Sets a mutable global.\npub(crate) unsafe fn set_freqs(freqs: Clocks) {\n    debug!(\"rcc: {:?}\", freqs);\n    CLOCK_FREQS = MaybeUninit::new(freqs);\n}\n\n#[cfg(feature = \"_dual-core\")]\n/// Sets the clock frequencies\n///\n/// Safety: Sets a mutable global.\npub(crate) unsafe fn set_freqs(freqs: Clocks) {\n    debug!(\"rcc: {:?}\", freqs);\n    CLOCK_FREQS_PTR\n        .load(core::sync::atomic::Ordering::SeqCst)\n        .write(MaybeUninit::new(freqs));\n}\n\n#[cfg(not(feature = \"_dual-core\"))]\n/// Safety: Reads a mutable global.\npub(crate) unsafe fn get_freqs() -> &'static Clocks {\n    (*core::ptr::addr_of_mut!(CLOCK_FREQS)).assume_init_ref()\n}\n\n#[cfg(feature = \"_dual-core\")]\n/// Safety: Reads a mutable global.\npub(crate) unsafe fn get_freqs() -> &'static Clocks {\n    unwrap!(CLOCK_FREQS_PTR.load(core::sync::atomic::Ordering::SeqCst).as_ref()).assume_init_ref()\n}\n\n#[cfg(all(feature = \"low-power\", not(feature = \"_dual-core\")))]\n/// Safety: Reads a mutable global.\npub(crate) unsafe fn get_rcc_config() -> Option<Config> {\n    RCC_CONFIG\n}\n\n#[cfg(all(feature = \"low-power\", feature = \"_dual-core\"))]\n/// Safety: Reads a mutable global.\npub(crate) unsafe fn get_rcc_config() -> Option<Config> {\n    unwrap!(RCC_CONFIG_PTR.load(core::sync::atomic::Ordering::SeqCst).as_ref()).assume_init()\n}\n\n#[cfg(all(feature = \"low-power\", not(feature = \"_dual-core\")))]\n/// Safety: Sets a mutable global.\nunsafe fn set_rcc_config(config: Option<Config>) {\n    RCC_CONFIG = config;\n}\n\n#[cfg(all(feature = \"low-power\", feature = \"_dual-core\"))]\n/// Safety: Sets a mutable global.\nunsafe fn set_rcc_config(config: Option<Config>) {\n    RCC_CONFIG_PTR\n        .load(core::sync::atomic::Ordering::SeqCst)\n        .write(MaybeUninit::new(config));\n}\n\n/// Get the current clock configuration of the chip.\npub fn clocks<'a>(_rcc: &'a crate::Peri<'a, crate::peripherals::RCC>) -> &'a Clocks {\n    // Safety: the existence of a `Peri<RCC>` means that `rcc::init()`\n    // has already been called, so `CLOCK_FREQS` must be initialized.\n    // The clocks could be modified again by `reinit()`, but reinit\n    // (for this reason) requires an exclusive reference to `Peri<RCC>`.\n    unsafe { get_freqs() }\n}\n\n#[cfg(feature = \"low-power\")]\n/// Get the current stop mode\npub fn get_stop_mode(_cs: CriticalSection) -> Option<StopMode> {\n    // If rcc config is not set, then we're not ready to stop\n    unsafe { get_rcc_config()? };\n\n    // We cannot enter standby because we will lose program state.\n    if unsafe { REFCOUNT_STOP2 == 0 && REFCOUNT_STOP1 == 0 } {\n        Some(StopMode::Stop2)\n    } else if unsafe { REFCOUNT_STOP1 == 0 } {\n        Some(StopMode::Stop1)\n    } else {\n        //trace!(\"low power: not ready to stop (refcount_stop1: {})\", unsafe {\n        //    REFCOUNT_STOP1\n        //});\n        None\n    }\n}\n\n#[cfg(feature = \"low-power\")]\n#[allow(dead_code)]\npub(crate) unsafe fn reset_stop_refcount(_cs: CriticalSection) {\n    REFCOUNT_STOP2 = 0;\n    REFCOUNT_STOP1 = 0;\n}\n\n#[cfg(feature = \"low-power\")]\nfn increment_stop_refcount(_cs: CriticalSection, stop_mode: StopMode) {\n    match stop_mode {\n        StopMode::Standby => {}\n        StopMode::Stop2 => unsafe {\n            REFCOUNT_STOP2 += 1;\n        },\n        StopMode::Stop1 => unsafe {\n            REFCOUNT_STOP1 += 1;\n        },\n    }\n}\n\n#[cfg(feature = \"low-power\")]\nfn decrement_stop_refcount(_cs: CriticalSection, stop_mode: StopMode) {\n    match stop_mode {\n        StopMode::Standby => {}\n        StopMode::Stop2 => unsafe {\n            REFCOUNT_STOP2 -= 1;\n        },\n        StopMode::Stop1 => unsafe {\n            REFCOUNT_STOP1 -= 1;\n        },\n    }\n}\n\npub(crate) trait SealedRccPeripheral {\n    fn frequency() -> Hertz;\n    #[allow(dead_code)]\n    fn bus_frequency() -> Hertz;\n    const RCC_INFO: RccInfo;\n}\n\n#[allow(private_bounds)]\npub trait RccPeripheral: SealedRccPeripheral + 'static {}\n\n/// Runtime information necessary to reset, enable and disable a peripheral.\npub(crate) struct RccInfo {\n    /// Offset in 32-bit words of the xxxRSTR register into the RCC register block, or 0xff if the\n    /// peripheral has no reset bit (we don't use an `Option` to save one byte of storage).\n    reset_offset_or_0xff: u8,\n    /// Position of the xxxRST bit within the xxxRSTR register (0..=31).\n    reset_bit: u8,\n    /// Offset in 32-bit words of the xxxENR register into the RCC register block.\n    enable_offset: u8,\n    /// Position of the xxxEN bit within the xxxENR register (0..=31).\n    enable_bit: u8,\n    /// If this peripheral shares the same xxxRSTR bit and xxxEN bit with other peripherals, we\n    /// maintain a refcount in `crate::_generated::REFCOUNTS` at this index. If the bit is not\n    /// shared, this is 0xff (we don't use an `Option` to save one byte of storage).\n    refcount_idx_or_0xff: u8,\n    /// Stop mode of the peripheral, used to maintain `REFCOUNT_STOP1` and `REFCOUNT_STOP2`.\n    #[cfg(feature = \"low-power\")]\n    stop_mode: StopMode,\n}\n\n/// Specifies a limit for the stop mode of the peripheral or the stop mode to be entered.\n/// E.g. if `StopMode::Stop1` is selected, the peripheral prevents the chip from entering Stop1 mode.\n#[cfg(feature = \"low-power\")]\n#[allow(dead_code)]\n#[derive(Debug, Clone, Copy, PartialEq, Default)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub enum StopMode {\n    #[default]\n    /// Peripheral prevents chip from entering Stop1 or executor will enter Stop1\n    Stop1,\n    /// Peripheral prevents chip from entering Stop2 or executor will enter Stop2\n    Stop2,\n    /// Peripheral does not prevent chip from entering Stop\n    Standby,\n}\n\n#[cfg(feature = \"low-power\")]\nimpl StopMode {\n    /// Return whether this stop mode is at least another stop mode.\n    pub const fn at_least(&self, other: StopMode) -> bool {\n        match other {\n            Self::Stop1 => true,\n            Self::Stop2 => match *self {\n                Self::Stop2 | Self::Standby => true,\n                _ => false,\n            },\n            Self::Standby => match *self {\n                Self::Standby => true,\n                _ => false,\n            },\n        }\n    }\n}\n\nimpl RccInfo {\n    /// Safety:\n    /// - `reset_offset_and_bit`, if set, must correspond to valid xxxRST bit\n    /// - `enable_offset_and_bit` must correspond to valid xxxEN bit\n    /// - `refcount_idx`, if set, must correspond to valid refcount in `_generated::REFCOUNTS`\n    /// - `stop_mode` must be valid\n    pub(crate) const unsafe fn new(\n        reset_offset_and_bit: Option<(u8, u8)>,\n        enable_offset_and_bit: (u8, u8),\n        refcount_idx: Option<u8>,\n        #[cfg(feature = \"low-power\")] stop_mode: StopMode,\n    ) -> Self {\n        let (reset_offset_or_0xff, reset_bit) = match reset_offset_and_bit {\n            Some((offset, bit)) => (offset, bit),\n            None => (0xff, 0xff),\n        };\n        let (enable_offset, enable_bit) = enable_offset_and_bit;\n        let refcount_idx_or_0xff = match refcount_idx {\n            Some(idx) => idx,\n            None => 0xff,\n        };\n        Self {\n            reset_offset_or_0xff,\n            reset_bit,\n            enable_offset,\n            enable_bit,\n            refcount_idx_or_0xff,\n            #[cfg(feature = \"low-power\")]\n            stop_mode,\n        }\n    }\n\n    // TODO: should this be `unsafe`?\n    pub(crate) fn enable_and_reset_with_cs(&self, cs: CriticalSection) {\n        if self.refcount_idx_or_0xff != 0xff {\n            let refcount_idx = self.refcount_idx_or_0xff as usize;\n\n            // Use .get_mut instead of []-operator so that we control how bounds checks happen.\n            // Otherwise, core::fmt will be pulled in here in order to format the integer in the\n            // out-of-bounds error.\n            if let Some(refcount) =\n                unsafe { (*core::ptr::addr_of_mut!(crate::_generated::REFCOUNTS)).get_mut(refcount_idx) }\n            {\n                *refcount += 1;\n                if *refcount > 1 {\n                    return;\n                }\n            } else {\n                panic!(\"refcount_idx out of bounds: {}\", refcount_idx)\n            }\n        }\n\n        // set the xxxRST bit\n        let reset_ptr = self.reset_ptr();\n        if let Some(reset_ptr) = reset_ptr {\n            #[cfg(not(stm32wl5x))]\n            unsafe {\n                let val = reset_ptr.read_volatile();\n                reset_ptr.write_volatile(val | 1u32 << self.reset_bit);\n            }\n\n            // on stm32wl5x each CPU has its own peripheral enable bits and if the othert CPU has enabled the peripheral we don;t want to reset it\n            // as that would reset the configuration that the other CPU has set up.\n            // we hold a hardware lock to prevent the other CPU from enabling the peripheral while we are resetting it.\n            #[cfg(stm32wl5x)]\n            unsafe {\n                let _lock = crate::hsem::get_hsem(3).blocking_lock(0);\n\n                if !self.is_enabled_by_other_core() {\n                    let val = reset_ptr.read_volatile();\n                    reset_ptr.write_volatile(val | 1u32 << self.reset_bit);\n\n                    trace!(\"rcc: reset 0x{:x}:{}\", self.enable_offset, self.enable_bit);\n                }\n            }\n        }\n\n        self.enable_with_cs(cs);\n    }\n\n    // TODO: should this be `unsafe`?\n    pub(crate) fn enable_with_cs(&self, _cs: CriticalSection) {\n        // set the xxxEN bit\n        let enable_ptr = self.enable_ptr();\n        unsafe {\n            #[cfg(not(all(stm32wl5x, feature = \"_core-cm0p\")))]\n            {\n                let val = enable_ptr.read_volatile();\n                enable_ptr.write_volatile(val | 1u32 << self.enable_bit);\n            }\n            // second core enable for stm32wl5x is at offset 0x100\n            #[cfg(all(stm32wl5x, feature = \"_core-cm0p\"))]\n            {\n                let enable_ptr = enable_ptr.add(0x100 / 4);\n                let val = enable_ptr.read_volatile();\n                enable_ptr.write_volatile(val | 1u32 << self.enable_bit);\n            }\n            trace!(\"rcc: enabled 0x{:x}:{}\", self.enable_offset, self.enable_bit);\n        }\n\n        // we must wait two peripheral clock cycles before the clock is active\n        // this seems to work, but might be incorrect\n        // see http://efton.sk/STM32/gotcha/g183.html\n\n        // dummy read (like in the ST HALs)\n        let _ = unsafe { enable_ptr.read_volatile() };\n\n        // DSB for good measure\n        cortex_m::asm::dsb();\n\n        // clear the xxxRST bit\n        let reset_ptr = self.reset_ptr();\n        if let Some(reset_ptr) = reset_ptr {\n            unsafe {\n                let val = reset_ptr.read_volatile();\n                reset_ptr.write_volatile(val & !(1u32 << self.reset_bit));\n            }\n        }\n    }\n\n    // TODO: should this be `unsafe`?\n    pub(crate) fn disable_with_cs(&self, _cs: CriticalSection) {\n        if self.refcount_idx_or_0xff != 0xff {\n            let refcount_idx = self.refcount_idx_or_0xff as usize;\n\n            // Use .get_mut instead of []-operator so that we control how bounds checks happen.\n            // Otherwise, core::fmt will be pulled in here in order to format the integer in the\n            // out-of-bounds error.\n            if let Some(refcount) =\n                unsafe { (*core::ptr::addr_of_mut!(crate::_generated::REFCOUNTS)).get_mut(refcount_idx) }\n            {\n                *refcount -= 1;\n                if *refcount > 0 {\n                    return;\n                }\n            } else {\n                panic!(\"refcount_idx out of bounds: {}\", refcount_idx)\n            }\n        }\n\n        // clear the xxxEN bit\n        let enable_ptr = self.enable_ptr();\n        unsafe {\n            #[cfg(not(all(stm32wl5x, feature = \"_core-cm0p\")))]\n            {\n                let val = enable_ptr.read_volatile();\n                enable_ptr.write_volatile(val & !(1u32 << self.enable_bit));\n            }\n            // second core enable for stm32wl5x is at offset 0x100\n            #[cfg(all(stm32wl5x, feature = \"_core-cm0p\"))]\n            {\n                let enable_ptr = enable_ptr.add(0x100 / 4);\n                let val = enable_ptr.read_volatile();\n                enable_ptr.write_volatile(val & !(1u32 << self.enable_bit));\n            }\n            trace!(\"rcc: disabled 0x{:x}:{}\", self.enable_offset, self.enable_bit);\n        }\n    }\n\n    #[allow(dead_code)]\n    pub(crate) fn increment_stop_refcount_with_cs(&self, _cs: CriticalSection) {\n        #[cfg(feature = \"low-power\")]\n        increment_stop_refcount(_cs, self.stop_mode);\n    }\n\n    #[allow(dead_code)]\n    fn increment_minimum_stop_refcount_with_cs(&self, _cs: CriticalSection) {\n        #[cfg(all(any(stm32wl, stm32wb), feature = \"low-power\"))]\n        match self.stop_mode {\n            StopMode::Stop1 | StopMode::Stop2 => increment_stop_refcount(_cs, StopMode::Stop2),\n            _ => {}\n        }\n    }\n\n    #[allow(dead_code)]\n    pub(crate) fn increment_stop_refcount(&self) {\n        #[cfg(feature = \"low-power\")]\n        critical_section::with(|cs| self.increment_stop_refcount_with_cs(cs))\n    }\n\n    #[allow(dead_code)]\n    pub(crate) fn decrement_stop_refcount_with_cs(&self, _cs: CriticalSection) {\n        #[cfg(feature = \"low-power\")]\n        decrement_stop_refcount(_cs, self.stop_mode);\n    }\n\n    #[allow(dead_code)]\n    fn decrement_minimum_stop_refcount_with_cs(&self, _cs: CriticalSection) {\n        #[cfg(all(any(stm32wl, stm32wb), feature = \"low-power\"))]\n        match self.stop_mode {\n            StopMode::Stop1 | StopMode::Stop2 => decrement_stop_refcount(_cs, StopMode::Stop2),\n            _ => {}\n        }\n    }\n\n    #[allow(dead_code)]\n    pub(crate) fn decrement_stop_refcount(&self) {\n        #[cfg(feature = \"low-power\")]\n        critical_section::with(|cs| self.decrement_stop_refcount_with_cs(cs))\n    }\n\n    // TODO: should this be `unsafe`?\n    pub(crate) fn enable_and_reset(&self) {\n        critical_section::with(|cs| {\n            self.enable_and_reset_with_cs(cs);\n            self.increment_stop_refcount_with_cs(cs);\n        })\n    }\n\n    #[allow(dead_code)]\n    pub(crate) fn enable_and_reset_without_stop(&self) {\n        critical_section::with(|cs| {\n            self.enable_and_reset_with_cs(cs);\n            self.increment_minimum_stop_refcount_with_cs(cs);\n        })\n    }\n\n    // TODO: should this be `unsafe`?\n    pub(crate) fn disable(&self) {\n        critical_section::with(|cs| {\n            self.disable_with_cs(cs);\n            self.decrement_stop_refcount_with_cs(cs);\n        })\n    }\n\n    // TODO: should this be `unsafe`?\n    #[allow(dead_code)]\n    pub(crate) fn disable_without_stop(&self) {\n        critical_section::with(|cs| {\n            self.disable_with_cs(cs);\n            self.decrement_minimum_stop_refcount_with_cs(cs);\n        })\n    }\n\n    #[allow(dead_code)]\n    pub(crate) fn wake_guard(&self) -> WakeGuard {\n        WakeGuard::new(\n            #[cfg(feature = \"low-power\")]\n            self.stop_mode,\n        )\n    }\n\n    fn reset_ptr(&self) -> Option<*mut u32> {\n        if self.reset_offset_or_0xff != 0xff {\n            Some(unsafe { (RCC.as_ptr() as *mut u32).add(self.reset_offset_or_0xff as _) })\n        } else {\n            None\n        }\n    }\n\n    fn enable_ptr(&self) -> *mut u32 {\n        unsafe { (RCC.as_ptr() as *mut u32).add(self.enable_offset as _) }\n    }\n\n    #[cfg(stm32wl5x)]\n    unsafe fn is_enabled_by_other_core(&self) -> bool {\n        let ptr = self.enable_ptr();\n        #[cfg(feature = \"_core-cm4\")]\n        let ptr = ptr.add(0x100);\n\n        (ptr.read_volatile() & (1u32 << self.enable_bit)) != 0\n    }\n}\n\npub struct WakeGuard {\n    #[cfg(feature = \"low-power\")]\n    stop_mode: StopMode,\n}\n\nimpl WakeGuard {\n    pub fn new(#[cfg(feature = \"low-power\")] stop_mode: StopMode) -> Self {\n        #[cfg(feature = \"low-power\")]\n        critical_section::with(|cs| increment_stop_refcount(cs, stop_mode));\n\n        Self {\n            #[cfg(feature = \"low-power\")]\n            stop_mode,\n        }\n    }\n}\n\nimpl Drop for WakeGuard {\n    fn drop(&mut self) {\n        #[cfg(feature = \"low-power\")]\n        critical_section::with(|cs| decrement_stop_refcount(cs, self.stop_mode));\n    }\n}\n\n#[allow(unused)]\nmod util {\n    use crate::time::Hertz;\n\n    pub fn calc_pclk<D>(hclk: Hertz, ppre: D) -> (Hertz, Hertz)\n    where\n        Hertz: core::ops::Div<D, Output = Hertz>,\n    {\n        let pclk = hclk / ppre;\n        let pclk_tim = if hclk == pclk { pclk } else { pclk * 2u32 };\n        (pclk, pclk_tim)\n    }\n\n    pub fn all_equal<T: Eq>(mut iter: impl Iterator<Item = T>) -> bool {\n        let Some(x) = iter.next() else { return true };\n        if !iter.all(|y| y == x) {\n            return false;\n        }\n        true\n    }\n\n    pub fn get_equal<T: Eq>(mut iter: impl Iterator<Item = T>) -> Result<Option<T>, ()> {\n        let Some(x) = iter.next() else { return Ok(None) };\n        if !iter.all(|y| y == x) {\n            return Err(());\n        }\n        Ok(Some(x))\n    }\n}\n\n/// Get the kernel clock frequency of the peripheral `T`.\n///\n/// # Panics\n///\n/// Panics if the clock is not active.\npub fn frequency<T: RccPeripheral>() -> Hertz {\n    T::frequency()\n}\n\n/// Enables and resets peripheral `T`.\n///\n/// # Safety\n///\n/// Peripheral must not be in use.\n// TODO: should this be `unsafe`?\npub fn enable_and_reset_with_cs<T: RccPeripheral>(cs: CriticalSection) {\n    T::RCC_INFO.enable_and_reset_with_cs(cs);\n}\n\n/// Enables and clears the reset for peripheral `T`.\n///\n/// # Safety\n///\n/// The peripheral can be in use since this does not reset it\npub fn enable_with_cs<T: RccPeripheral>(cs: CriticalSection) {\n    T::RCC_INFO.enable_with_cs(cs);\n}\n\n/// Disables peripheral `T`.\n///\n/// # Safety\n///\n/// Peripheral must not be in use.\n// TODO: should this be `unsafe`?\npub fn disable_with_cs<T: RccPeripheral>(cs: CriticalSection) {\n    T::RCC_INFO.disable_with_cs(cs);\n}\n\n/// Enables and resets peripheral `T`.\n///\n/// # Safety\n///\n/// Peripheral must not be in use.\n// TODO: should this be `unsafe`?\npub fn enable_and_reset<T: RccPeripheral>() {\n    T::RCC_INFO.enable_and_reset();\n}\n\n/// Enables and resets peripheral `T` without incrementing the stop refcount.\n///\n/// # Safety\n///\n/// Peripheral must not be in use.\n// TODO: should this be `unsafe`?\npub fn enable_and_reset_without_stop<T: RccPeripheral>() {\n    T::RCC_INFO.enable_and_reset_without_stop();\n}\n\n/// Disables peripheral `T`.\n///\n/// # Safety\n///\n/// Peripheral must not be in use.\n// TODO: should this be `unsafe`?\npub fn disable<T: RccPeripheral>() {\n    T::RCC_INFO.disable();\n}\n\n/// Re-initialize the `embassy-stm32` clock configuration with the provided configuration.\n///\n/// This is useful when you need to alter the CPU clock after configuring peripherals.\n/// For instance, configure an external clock via spi or i2c.\n///\n/// Please not this only re-configures the rcc and the time driver (not GPIO, EXTI, etc).\n///\n/// This should only be called after `init`.\n#[cfg(not(feature = \"_dual-core\"))]\npub fn reinit(config: Config, _rcc: &'_ mut crate::Peri<'_, crate::peripherals::RCC>) {\n    critical_section::with(|cs| {\n        init_rcc(cs, config);\n\n        // must be after rcc init\n        #[cfg(feature = \"_time-driver\")]\n        crate::time_driver::init(cs);\n    })\n}\n\n#[cfg(feature = \"low-power\")]\n#[allow(dead_code)]\n/// Re-initialize the `embassy-stm32` clock configuration with the saved configuration.\npub(crate) fn reinit_saved(_cs: CriticalSection) {\n    unsafe { init(get_rcc_config().unwrap()) };\n}\n\npub(crate) fn init_rcc(_cs: CriticalSection, config: Config) {\n    unsafe {\n        // if we are using a lp timer as the time driver, we need to make sure that the clock selection is sane\n        // TODO: how to share this config from the secondary core thats using a lp timer? (right now its not detected as missing!)\n        #[cfg(feature = \"_lp-time-driver\")]\n        let config = {\n            use crate::pac::rcc::vals::Lptimsel;\n            let mut config = config;\n            #[cfg(time_driver_lptim1)]\n            {\n                match config.mux.lptim1sel {\n                    Lptimsel::PCLK1 => {\n                        // set it to LSI\n                        config.mux.lptim1sel = Lptimsel::LSI;\n                        config.ls.lsi = true;\n                    }\n                    Lptimsel::LSI => {\n                        // ok but insure the lsi is enabled\n                        config.ls.lsi = true;\n                    }\n                    Lptimsel::HSI => {\n                        // ok but insure the hsi is enabled\n                        config.hsi = true;\n                    }\n                    Lptimsel::LSE => {\n                        // ok but insure the lse is configured with peripherals_clocked = true!!!\n                        if let Some(mut lse_config) = config.ls.lse {\n                            lse_config.peripherals_clocked = true;\n                            config.ls.lse = Some(lse_config);\n                        } else {\n                            panic!(\"LSE is not not configured, but selected for time_driver!!!\");\n                        }\n                    }\n                }\n            }\n            #[cfg(time_driver_lptim2)]\n            {\n                match config.mux.lptim2sel {\n                    Lptimsel::PCLK1 => {\n                        // set it to LSI\n                        config.mux.lptim2sel = Lptimsel::LSI;\n                        config.ls.lsi = true;\n                    }\n                    Lptimsel::LSI => {\n                        // ok but insure the lsi is enabled\n                        config.ls.lsi = true;\n                    }\n                    Lptimsel::HSI => {\n                        // ok but insure the hsi is enabled\n                        config.hsi = true;\n                    }\n                    Lptimsel::LSE => {\n                        // ok but insure the lse is configured with peripherals_clocked = true!!!\n                        if let Some(mut lse_config) = config.ls.lse {\n                            lse_config.peripherals_clocked = true;\n                            config.ls.lse = Some(lse_config);\n                        } else {\n                            panic!(\"LSE is not not configured, but selected for time_driver!!!\");\n                        }\n                    }\n                }\n            }\n            #[cfg(time_driver_lptim3)]\n            {\n                match config.mux.lptim3sel {\n                    Lptimsel::PCLK1 => {\n                        // set it to LSI\n                        config.mux.lptim3sel = Lptimsel::LSI;\n                        config.ls.lsi = true;\n                    }\n                    Lptimsel::LSI => {\n                        // ok but insure the lsi is enabled\n                        config.ls.lsi = true;\n                    }\n                    Lptimsel::HSI => {\n                        // ok but insure the hsi is enabled\n                        config.hsi = true;\n                    }\n                    Lptimsel::LSE => {\n                        // ok but insure the lse is configured with peripherals_clocked = true!!!\n                        if let Some(mut lse_config) = config.ls.lse {\n                            lse_config.peripherals_clocked = true;\n                            config.ls.lse = Some(lse_config);\n                        } else {\n                            panic!(\"LSE is not not configured, but selected for time_driver!!!\");\n                        }\n                    }\n                }\n            }\n            config\n        };\n\n        init(config);\n\n        #[cfg(feature = \"low-power\")]\n        set_rcc_config(Some(config));\n    }\n}\n\n/// Calculate intermediate prescaler number used to calculate peripheral prescalers\n///\n/// This function is intended to calculate a number indicating a minimum division\n/// necessary to result in a frequency lower than the provided `freq_max`.\n///\n/// The returned value indicates the `val + 1` divider is necessary to result in\n/// the output frequency that is below the maximum provided.\n///\n/// For example:\n/// 0 = divider of 1 => no division necessary as the input frequency is below max\n/// 1 = divider of 2 => division by 2 necessary\n/// ...\n///\n/// The provided max frequency is inclusive. So if `freq_in == freq_max` the result\n/// will be 0, indicating that no division is necessary. To accomplish that we subtract\n/// 1 from the input frequency so that the integer rounding plays in our favor.\n///\n/// For example:\n/// Let the input frequency be 110 and the max frequency be 55.\n/// If we naiively do `110/55 = 2` the renult will indicate that we need a divider by 3\n/// which in reality will be rounded up to 4 as usually a 3 division is not available.\n/// In either case the resulting frequency will be either 36 or 27 which is lower than\n/// what we would want. The result should be 1.\n/// If we do the following instead `109/55 = 1` indicating that we need a divide by 2\n/// which will result in the correct 55.\n#[allow(unused)]\npub(crate) fn raw_prescaler(freq_in: u32, freq_max: u32) -> u32 {\n    freq_in.saturating_sub(1) / freq_max\n}\n"
  },
  {
    "path": "embassy-stm32/src/rcc/n6.rs",
    "content": "use stm32_metapac::pwr::vals::{\n    Vddio2rdy, Vddio2sv, Vddio2vrsel, Vddio3rdy, Vddio3sv, Vddio3vrsel, Vddio4sv, Vddio5sv,\n};\nuse stm32_metapac::rcc::vals::{Cpusw, Cpusws, Hseext, Hsitrim, Msifreqsel, Pllmodssdis, Syssw, Syssws, Timpre};\npub use stm32_metapac::rcc::vals::{\n    Hpre as AhbPrescaler, Hsidiv as HsiPrescaler, Hsitrim as HsiCalibration, Icint, Icsel, Plldivm, Pllpdiv, Pllsel,\n    Ppre as ApbPrescaler, Xspisel as XspiClkSrc,\n};\nuse stm32_metapac::syscfg::vals::{Vddio2cccrEn, Vddio3cccrEn, Vddio4cccrEn};\n\nuse crate::pac::{PWR, RCC, RISAF3, SYSCFG};\nuse crate::time::Hertz;\n\npub const HSI_FREQ: Hertz = Hertz(64_000_000);\npub const LSE_FREQ: Hertz = Hertz(32_768);\n\n#[derive(Clone, Copy, Eq, PartialEq)]\npub enum HseMode {\n    /// crystal/ceramic oscillator\n    Oscillator,\n    /// oscillator bypassed with external clock (analog)\n    Bypass,\n    /// oscillator bypassed with external digital clock\n    BypassDigital,\n}\n\n#[derive(Clone, Copy, Eq, PartialEq)]\npub struct Hse {\n    /// HSE frequency.\n    pub freq: Hertz,\n    /// HSE oscillator mode.\n    pub mode: HseMode,\n}\n\n#[derive(Clone, Copy, Eq, PartialEq)]\npub struct Hsi {\n    pub pre: HsiPrescaler,\n    pub trim: Hsitrim,\n}\n\n#[derive(Clone, Copy, PartialEq)]\npub enum SupplyConfig {\n    Smps,\n    External,\n}\n\n#[derive(Clone, Copy, PartialEq)]\npub enum CpuClk {\n    Hsi,\n    Msi,\n    Hse,\n    Ic1,\n}\n\nimpl CpuClk {\n    const fn to_bits(self) -> u8 {\n        match self {\n            Self::Hsi => 0x0,\n            Self::Msi => 0x1,\n            Self::Hse => 0x2,\n            Self::Ic1 => 0x3,\n        }\n    }\n}\n\n/// Configuration for an internal clock (IC) divider.\n///\n/// Used for configuring XSPI kernel clocks (IC3 for XSPI1, IC4 for XSPI2).\n#[derive(Clone, Copy, PartialEq)]\npub struct IcConfig {\n    /// Clock source selection (PLL1, PLL2, etc.)\n    pub source: Icsel,\n    /// Divider value (1-256)\n    pub divider: Icint,\n}\n\n#[derive(Clone, Copy, PartialEq)]\npub enum SysClk {\n    Hsi,\n    Msi,\n    Hse,\n    Ic2,\n}\n\nimpl SysClk {\n    const fn to_bits(self) -> u8 {\n        match self {\n            Self::Hsi => 0x0,\n            Self::Msi => 0x1,\n            Self::Hse => 0x2,\n            Self::Ic2 => 0x3,\n        }\n    }\n}\n\n#[derive(Clone, Copy, PartialEq)]\npub struct Msi {\n    pub freq: Msifreqsel,\n    pub trim: u8,\n}\n\n#[derive(Clone, Copy, PartialEq)]\npub enum Pll {\n    Oscillator {\n        source: Pllsel,\n        divm: Plldivm,\n        fractional: u32,\n        divn: u16,\n        divp1: Pllpdiv,\n        divp2: Pllpdiv,\n    },\n    Bypass {\n        source: Pllsel,\n    },\n}\n\n/// Configuration of the core clocks\n#[non_exhaustive]\n#[derive(Clone, Copy)]\npub struct Config {\n    pub hsi: Option<Hsi>,\n    pub hse: Option<Hse>,\n    pub msi: Option<Msi>,\n    pub lsi: bool,\n    pub lse: bool,\n\n    pub cpu: CpuClk,\n    pub sys: SysClk,\n\n    pub pll1: Option<Pll>,\n    pub pll2: Option<Pll>,\n    pub pll3: Option<Pll>,\n    pub pll4: Option<Pll>,\n\n    pub ic1: Option<IcConfig>,\n    pub ic2: Option<IcConfig>,\n    pub ic3: Option<IcConfig>,\n    pub ic4: Option<IcConfig>,\n    pub ic5: Option<IcConfig>,\n    pub ic6: Option<IcConfig>,\n    pub ic7: Option<IcConfig>,\n    pub ic8: Option<IcConfig>,\n    pub ic9: Option<IcConfig>,\n    pub ic10: Option<IcConfig>,\n    pub ic11: Option<IcConfig>,\n    pub ic12: Option<IcConfig>,\n    pub ic13: Option<IcConfig>,\n    pub ic14: Option<IcConfig>,\n    pub ic15: Option<IcConfig>,\n    pub ic16: Option<IcConfig>,\n    pub ic17: Option<IcConfig>,\n    pub ic18: Option<IcConfig>,\n    pub ic19: Option<IcConfig>,\n    pub ic20: Option<IcConfig>,\n\n    pub ahb: AhbPrescaler,\n    pub apb1: ApbPrescaler,\n    pub apb2: ApbPrescaler,\n    pub apb4: ApbPrescaler,\n    pub apb5: ApbPrescaler,\n\n    pub supply_config: SupplyConfig,\n\n    /// VddIO2 voltage range (Ports O/P, XSPI1)\n    /// true = 1.8V, false = 3.3V (default)\n    pub vddio2_1v8: bool,\n    /// VddIO3 voltage range (Port N, XSPI2)\n    /// true = 1.8V, false = 3.3V (default)\n    pub vddio3_1v8: bool,\n\n    /// Per-peripheral kernel clock selection muxes\n    pub mux: super::mux::ClockMux,\n}\n\nimpl Config {\n    pub const fn new() -> Self {\n        Self {\n            hsi: Some(Hsi {\n                pre: HsiPrescaler::DIV1,\n                trim: HsiCalibration::from_bits(32),\n            }),\n            hse: None,\n            msi: None,\n            lsi: true,\n            lse: false,\n\n            cpu: CpuClk::Hsi,\n            sys: SysClk::Hsi,\n\n            pll1: Some(Pll::Bypass { source: Pllsel::HSI }),\n            pll2: Some(Pll::Bypass { source: Pllsel::HSI }),\n            pll3: Some(Pll::Bypass { source: Pllsel::HSI }),\n            pll4: Some(Pll::Bypass { source: Pllsel::HSI }),\n\n            ic1: None,\n            ic2: None,\n            ic3: None,\n            ic4: None,\n            ic5: None,\n            ic6: None,\n            ic7: None,\n            ic8: None,\n            ic9: None,\n            ic10: None,\n            ic11: None,\n            ic12: None,\n            ic13: None,\n            ic14: None,\n            ic15: None,\n            ic16: None,\n            ic17: None,\n            ic18: None,\n            ic19: None,\n            ic20: None,\n\n            ahb: AhbPrescaler::DIV2,\n            apb1: ApbPrescaler::DIV1,\n            apb2: ApbPrescaler::DIV1,\n            apb4: ApbPrescaler::DIV1,\n            apb5: ApbPrescaler::DIV1,\n\n            supply_config: SupplyConfig::Smps,\n\n            vddio2_1v8: false, // Default to 3.3V\n            vddio3_1v8: false, // Default to 3.3V\n\n            mux: super::mux::ClockMux::default(),\n        }\n    }\n}\n\n#[allow(dead_code)]\nstruct ClocksOutput {\n    cpuclk: Hertz,\n    sysclk: Hertz,\n    pclk_tim: Hertz,\n    ahb: Hertz,\n    apb1: Hertz,\n    apb2: Hertz,\n    apb4: Hertz,\n    apb5: Hertz,\n}\n\nstruct ClocksInput {\n    hsi: Option<Hertz>,\n    msi: Option<Hertz>,\n    hse: Option<Hertz>,\n    ic1: Option<Hertz>,\n    ic2: Option<Hertz>,\n    ic3: Option<Hertz>,\n    ic4: Option<Hertz>,\n    ic5: Option<Hertz>,\n    ic6: Option<Hertz>,\n    ic7: Option<Hertz>,\n    ic8: Option<Hertz>,\n    ic9: Option<Hertz>,\n    ic10: Option<Hertz>,\n    ic11: Option<Hertz>,\n    ic12: Option<Hertz>,\n    ic13: Option<Hertz>,\n    ic14: Option<Hertz>,\n    ic15: Option<Hertz>,\n    ic16: Option<Hertz>,\n    ic17: Option<Hertz>,\n    ic18: Option<Hertz>,\n    ic19: Option<Hertz>,\n    ic20: Option<Hertz>,\n}\n\nfn init_clocks(config: Config, input: &ClocksInput) -> ClocksOutput {\n    // IC configuration\n    for (index, ic) in [\n        config.ic1,\n        config.ic2,\n        config.ic3,\n        config.ic4,\n        config.ic5,\n        config.ic6,\n        config.ic7,\n        config.ic8,\n        config.ic9,\n        config.ic10,\n        config.ic11,\n        config.ic12,\n        config.ic13,\n        config.ic14,\n        config.ic15,\n        config.ic16,\n        config.ic17,\n        config.ic18,\n        config.ic19,\n        config.ic20,\n    ]\n    .iter()\n    .enumerate()\n    {\n        // Skip disabled ICs\n        let Some(ic) = ic else { continue };\n\n        let ic_source = ic.source.to_bits();\n        if !pll_source_ready(ic_source) {\n            panic!(\n                \"IC{} source was set to PLL{}, but it is not currently enabled\",\n                index + 1,\n                ic_source\n            )\n        }\n\n        RCC.iccfgr(index).write(|w| {\n            w.set_icsel(ic.source);\n            w.set_icint(ic.divider);\n        });\n        RCC.divensr().modify(|w| w.0 = 1 << index);\n    }\n\n    // handle increasing dividers\n    debug!(\"configuring increasing pclk dividers\");\n    RCC.cfgr2().modify(|w| {\n        if config.apb1 > w.ppre1() {\n            debug!(\"  - APB1\");\n            w.set_ppre1(config.apb1);\n        }\n        if config.apb2 > w.ppre2() {\n            debug!(\"  - APB2\");\n            w.set_ppre2(config.apb2);\n        }\n        if config.apb4 > w.ppre4() {\n            debug!(\"  - APB4\");\n            w.set_ppre4(config.apb4);\n        }\n        if config.apb5 > w.ppre5() {\n            debug!(\"  - APB5\");\n            w.set_ppre5(config.apb5);\n        }\n        if config.ahb > w.hpre() {\n            debug!(\"  - AHB\");\n            w.set_hpre(config.ahb);\n        }\n    });\n    // cpuclk\n    debug!(\"configuring cpuclk\");\n    match config.cpu {\n        CpuClk::Hsi if !RCC.sr().read().hsirdy() => panic!(\"HSI is not ready to be selected as CPU clock source\"),\n        CpuClk::Msi if !RCC.sr().read().msirdy() => panic!(\"MSI is not ready to be selected as CPU clock source\"),\n        CpuClk::Hse if !RCC.sr().read().hserdy() => panic!(\"HSE is not ready to be selected as CPU clock source\"),\n        CpuClk::Ic1 if !ic_enabled(1) => panic!(\"IC1 is not ready to be selected as CPU clock source\"),\n        _ => {}\n    }\n    // set source\n    let cpusw = Cpusw::from_bits(config.cpu.to_bits());\n    RCC.cfgr().modify(|w| w.set_cpusw(cpusw));\n    // wait for changes to take effect\n    while RCC.cfgr().read().cpusws() != Cpusws::from_bits(config.cpu.to_bits()) {}\n\n    // sysclk\n    debug!(\"configuring sysclk\");\n    match config.sys {\n        SysClk::Hsi if !RCC.sr().read().hsirdy() => panic!(\"HSI is not ready to be selected as system clock source\"),\n        SysClk::Msi if !RCC.sr().read().msirdy() => panic!(\"MSI is not ready to be selected as system clock source\"),\n        SysClk::Hse if !RCC.sr().read().hserdy() => panic!(\"HSE is not ready to be selected as system clock source\"),\n        SysClk::Ic2 if !ic_enabled(2) || !ic_enabled(6) || !ic_enabled(11) => panic!(\n            \"IC2 is not ready to be selected as system clock source (make sure that IC6 and IC11 were configured as well)\"\n        ),\n        _ => {}\n    }\n    // switch the system bus clock\n    let syssw = Syssw::from_bits(config.sys.to_bits());\n    RCC.cfgr().modify(|w| w.set_syssw(syssw));\n    // wait for changes to be applied\n    while RCC.cfgr().read().syssws() != Syssws::from_bits(config.sys.to_bits()) {}\n\n    // decreasing dividers\n    debug!(\"configuring decreasing pclk dividers\");\n    RCC.cfgr2().modify(|w| {\n        if config.ahb < w.hpre() {\n            debug!(\"  - AHB\");\n            w.set_hpre(config.ahb);\n        }\n        if config.apb1 < w.ppre1() {\n            debug!(\"  - APB1\");\n            w.set_ppre1(config.apb1);\n        }\n        if config.apb2 < w.ppre2() {\n            debug!(\"  - APB2\");\n            w.set_ppre2(config.apb2);\n        }\n        if config.apb4 < w.ppre4() {\n            debug!(\"  - APB4\");\n            w.set_ppre4(config.apb4);\n        }\n        if config.apb5 < w.ppre5() {\n            debug!(\"  - APB5\");\n            w.set_ppre5(config.apb5);\n        }\n    });\n\n    let cpuclk = match config.cpu {\n        CpuClk::Hsi => unwrap!(input.hsi),\n        CpuClk::Msi => unwrap!(input.msi),\n        CpuClk::Hse => unwrap!(input.hse),\n        CpuClk::Ic1 => unwrap!(input.ic1),\n    };\n\n    let sysclk = match config.sys {\n        SysClk::Hsi => unwrap!(input.hsi),\n        SysClk::Msi => unwrap!(input.msi),\n        SysClk::Hse => unwrap!(input.hse),\n        SysClk::Ic2 => unwrap!(input.ic2),\n    };\n\n    let timpre: u32 = match RCC.cfgr2().read().timpre() {\n        Timpre::DIV1 => 1,\n        Timpre::DIV2 => 2,\n        Timpre::DIV4 => 4,\n        Timpre::_RESERVED_3 => 8,\n    };\n\n    let hpre = periph_prescaler_to_value(config.ahb.to_bits());\n    let ppre1 = periph_prescaler_to_value(config.apb1.to_bits());\n    let ppre2 = periph_prescaler_to_value(config.apb2.to_bits());\n    let ppre4 = periph_prescaler_to_value(config.apb4.to_bits());\n    let ppre5 = periph_prescaler_to_value(config.apb5.to_bits());\n\n    // enable all peripherals in sleep mode\n    enable_low_power_peripherals();\n\n    // enable interrupts\n    unsafe {\n        core::arch::asm!(\"cpsie i\");\n    }\n\n    ClocksOutput {\n        cpuclk,\n        sysclk,\n        pclk_tim: sysclk / timpre,\n        ahb: Hertz(sysclk.0 / hpre as u32),\n        apb1: sysclk / hpre / ppre1,\n        apb2: sysclk / hpre / ppre2,\n        apb4: sysclk / hpre / ppre4,\n        apb5: sysclk / hpre / ppre5,\n    }\n}\n\nfn enable_low_power_peripherals() {\n    // AHB1-5\n    RCC.ahb1lpenr().modify(|w| {\n        w.set_adc12lpen(true);\n        w.set_gpdma1lpen(true);\n    });\n    RCC.ahb2lpenr().modify(|w| {\n        w.set_adf1lpen(true);\n        w.set_mdf1lpen(true);\n        w.set_ramcfglpen(true);\n    });\n    RCC.ahb3lpenr().modify(|w| {\n        w.set_risaflpen(true);\n        w.set_iaclpen(true);\n        w.set_rifsclpen(true);\n        w.set_pkalpen(true);\n        w.set_saeslpen(true);\n        w.set_cryplpen(true);\n        w.set_hashlpen(true);\n        w.set_rnglpen(true);\n    });\n    RCC.ahb4lpenr().modify(|w| {\n        w.set_crclpen(true);\n        w.set_pwrlpen(true);\n        w.set_gpioqlpen(true);\n        w.set_gpioplpen(true);\n        w.set_gpioolpen(true);\n        w.set_gpionlpen(true);\n        w.set_gpiohlpen(true);\n        w.set_gpioglpen(true);\n        w.set_gpioflpen(true);\n        w.set_gpioelpen(true);\n        w.set_gpiodlpen(true);\n        w.set_gpioclpen(true);\n        w.set_gpioblpen(true);\n        w.set_gpioalpen(true);\n    });\n    RCC.ahb5lpenr().modify(|w| {\n        w.set_npulpen(true);\n        w.set_npucachelpen(true);\n        w.set_otg2lpen(true);\n        w.set_otgphy2lpen(true);\n        w.set_otgphy1lpen(true);\n        w.set_otg1lpen(true);\n        w.set_eth1lpen(true);\n        w.set_eth1rxlpen(true);\n        w.set_eth1txlpen(true);\n        w.set_eth1maclpen(true);\n        w.set_gpulpen(true);\n        w.set_gfxmmulpen(true);\n        w.set_mce4lpen(true);\n        w.set_xspi3lpen(true);\n        w.set_mce3lpen(true);\n        w.set_mce2lpen(true);\n        w.set_mce1lpen(true);\n        w.set_xspimlpen(true);\n        w.set_xspi2lpen(true);\n        w.set_sdmmc1lpen(true);\n        w.set_sdmmc2lpen(true);\n        w.set_pssilpen(true);\n        w.set_xspi1lpen(true);\n        w.set_fmclpen(true);\n        w.set_jpeglpen(true);\n        w.set_dma2dlpen(true);\n        w.set_hpdma1lpen(true);\n    });\n\n    // APB1-5\n    RCC.apb1llpenr().modify(|w| {\n        w.set_uart8lpen(true);\n        w.set_uart7lpen(true);\n        w.set_i3c2lpen(true);\n        w.set_i3c1lpen(true);\n        w.set_i2c3lpen(true);\n        w.set_i2c2lpen(true);\n        w.set_i2c1lpen(true);\n        w.set_uart5lpen(true);\n        w.set_uart4lpen(true);\n        w.set_usart3lpen(true);\n        w.set_usart2lpen(true);\n        w.set_spdifrx1lpen(true);\n        w.set_spi3lpen(true);\n        w.set_spi2lpen(true);\n        w.set_tim11lpen(true);\n        w.set_tim10lpen(true);\n        w.set_wwdglpen(true);\n        w.set_lptim1lpen(true);\n        w.set_tim14lpen(true);\n        w.set_tim13lpen(true);\n        w.set_tim12lpen(true);\n        w.set_tim7lpen(true);\n        w.set_tim6lpen(true);\n        w.set_tim5lpen(true);\n        w.set_tim4lpen(true);\n        w.set_tim3lpen(true);\n        w.set_tim2lpen(true);\n    });\n    RCC.apb1hlpenr().modify(|w| {\n        w.set_ucpd1lpen(true);\n        w.set_fdcanlpen(true);\n        w.set_mdioslpen(true);\n    });\n    RCC.apb2lpenr().modify(|w| {\n        w.set_sai2lpen(true);\n        w.set_sai1lpen(true);\n        w.set_spi5lpen(true);\n        w.set_tim9lpen(true);\n        w.set_tim17lpen(true);\n        w.set_tim16lpen(true);\n        w.set_tim15lpen(true);\n        w.set_tim18lpen(true);\n        w.set_spi4lpen(true);\n        w.set_spi1lpen(true);\n        w.set_usart10lpen(true);\n        w.set_uart9lpen(true);\n        w.set_usart6lpen(true);\n        w.set_usart1lpen(true);\n        w.set_tim8lpen(true);\n        w.set_tim1lpen(true);\n    });\n    RCC.apb3lpenr().modify(|w| {\n        w.set_dftlpen(true);\n    });\n    RCC.apb4llpenr().modify(|w| {\n        w.set_rtcapblpen(true);\n        w.set_rtclpen(true);\n        w.set_vrefbuflpen(true);\n        w.set_lptim5lpen(true);\n        w.set_lptim4lpen(true);\n        w.set_lptim3lpen(true);\n        w.set_lptim2lpen(true);\n        w.set_i2c4lpen(true);\n        w.set_spi6lpen(true);\n        w.set_lpuart1lpen(true);\n        w.set_hdplpen(true);\n    });\n    RCC.apb4hlpenr().modify(|w| {\n        w.set_dtslpen(true);\n        w.set_bseclpen(true);\n        w.set_syscfglpen(true);\n    });\n    RCC.apb5lpenr().modify(|w| {\n        w.set_csilpen(true);\n        w.set_venclpen(true);\n        w.set_gfxtimlpen(true);\n        w.set_dcmilpen(true);\n        w.set_ltdclpen(true);\n    });\n\n    RCC.buslpenr().modify(|w| {\n        w.set_aclknclpen(true);\n        w.set_aclknlpen(true);\n    });\n\n    RCC.memlpenr().modify(|w| {\n        w.set_bootromlpen(true);\n        w.set_vencramlpen(true);\n        w.set_npucacheramlpen(true);\n        w.set_flexramlpen(true);\n        w.set_axisram2lpen(true);\n        w.set_axisram1lpen(true);\n        w.set_bkpsramlpen(true);\n        w.set_ahbsram2lpen(true);\n        w.set_ahbsram1lpen(true);\n        w.set_axisram6lpen(true);\n        w.set_axisram5lpen(true);\n        w.set_axisram4lpen(true);\n        w.set_axisram3lpen(true);\n    });\n\n    RCC.misclpenr().modify(|w| {\n        w.set_perlpen(true);\n        w.set_xspiphycomplpen(true);\n        w.set_dbglpen(true);\n    });\n}\n\nconst fn periph_prescaler_to_value(bits: u8) -> u8 {\n    match bits {\n        0 => 1,\n        1 => 2,\n        2 => 4,\n        3 => 8,\n        4 => 16,\n        5 => 32,\n        6 => 64,\n        7.. => 128,\n    }\n}\n\nfn pll_source_ready(source: u8) -> bool {\n    match source {\n        0x0 if RCC.sr().read().pllrdy(0) || RCC.pllcfgr1(0).read().pllbyp() => true,\n        0x1 if RCC.sr().read().pllrdy(1) || RCC.pllcfgr1(1).read().pllbyp() => true,\n        0x2 if RCC.sr().read().pllrdy(2) || RCC.pllcfgr1(2).read().pllbyp() => true,\n        0x3 if RCC.sr().read().pllrdy(3) || RCC.pllcfgr1(3).read().pllbyp() => true,\n        _ => false,\n    }\n}\n\nfn ic_enabled(ic: u8) -> bool {\n    ic > 0 && ic <= 20 && ((RCC.divenr().read().0 >> (ic - 1)) & 0x1) != 0\n}\n\nimpl Default for Config {\n    fn default() -> Self {\n        Self::new()\n    }\n}\n\nfn power_supply_config(supply_config: SupplyConfig) {\n    // power supply config\n    PWR.cr1().modify(|w| {\n        w.set_sden(match supply_config {\n            SupplyConfig::External => false,\n            SupplyConfig::Smps => true,\n        });\n    });\n\n    // Validate supply configuration\n    while !PWR.voscr().read().actvosrdy() {}\n}\n\nstruct PllInput {\n    hsi: Option<Hertz>,\n    msi: Option<Hertz>,\n    hse: Option<Hertz>,\n    i2s_ckin: Option<Hertz>,\n}\n\n#[derive(Clone, Copy, Default)]\n#[allow(dead_code)]\nstruct PllOutput {\n    divm: Option<Hertz>,\n    divn: Option<Hertz>,\n    divp1: Option<Hertz>,\n    divp2: Option<Hertz>,\n    output: Option<Hertz>,\n}\n\nfn disable_pll(pll_index: usize) {\n    let cfgr1 = RCC.pllcfgr1(pll_index);\n    let cfgr3 = RCC.pllcfgr3(pll_index);\n\n    cfgr3.modify(|w| w.set_pllpdiven(false));\n    RCC.ccr().write(|w| w.set_pllonc(pll_index, true));\n    // wait till disabled\n    while RCC.sr().read().pllrdy(pll_index) {}\n\n    // clear bypass mode\n    cfgr1.modify(|w| w.set_pllbyp(false));\n}\n\nfn init_pll(pll_config: Option<Pll>, pll_index: usize, input: &PllInput) -> PllOutput {\n    let cfgr1 = RCC.pllcfgr1(pll_index);\n    let cfgr2 = RCC.pllcfgr2(pll_index);\n    let cfgr3 = RCC.pllcfgr3(pll_index);\n\n    match pll_config {\n        Some(Pll::Oscillator {\n            source,\n            divm,\n            fractional,\n            divn,\n            divp1,\n            divp2,\n        }) => {\n            // ensure pll is disabled\n            debug!(\"PLL{}: disabling\", pll_index + 1);\n            RCC.ccr().write(|w| w.set_pllonc(pll_index, true));\n            while RCC.sr().read().pllrdy(pll_index) {}\n            debug!(\"PLL{}: disabled\", pll_index + 1);\n\n            // ensure PLLxMODSSDIS=1 to work in fractional mode\n            cfgr3.modify(|w| w.set_pllmodssdis(Pllmodssdis::FRACTIONAL_DIVIDE));\n            // clear bypass mode\n            cfgr1.modify(|w| w.set_pllbyp(false));\n            // configure the pll clock source, mul and div factors\n            cfgr1.modify(|w| {\n                w.set_pllsel(source);\n                w.set_plldivm(divm);\n                w.set_plldivn(divn);\n            });\n\n            let in_clk = match source {\n                Pllsel::HSI => unwrap!(input.hsi),\n                Pllsel::MSI => unwrap!(input.msi),\n                Pllsel::HSE => unwrap!(input.hse),\n                Pllsel::I2S_CKIN => unwrap!(input.i2s_ckin),\n                _ => panic!(\"reserved PLL source not allowed\"),\n            };\n\n            let m = divm.to_bits() as u32;\n            let n = divn as u32;\n\n            cfgr3.modify(|w| {\n                w.set_pllpdiv1(divp1);\n                w.set_pllpdiv2(divp2);\n            });\n\n            let p1 = divp1.to_bits() as u32;\n            let p2 = divp2.to_bits() as u32;\n\n            // configure pll divnfrac\n            cfgr2.modify(|w| w.set_plldivnfrac(fractional));\n            // clear pllxmoddsen\n            cfgr3.modify(|w| w.set_pllmoddsen(false));\n            // fractional mode\n            if fractional != 0 {\n                cfgr3.modify(|w| {\n                    w.set_pllmoddsen(true);\n                    w.set_plldacen(true);\n                })\n            }\n            // enable pll post divider output\n            cfgr3.modify(|w| {\n                w.set_pllmodssrst(true);\n                w.set_pllpdiven(true);\n            });\n            // enable the pll\n            debug!(\"PLL{}: enabling\", pll_index + 1);\n            RCC.csr().write(|w| w.set_pllons(pll_index, true));\n            // wait until ready\n            debug!(\"PLL{}: waiting for ready\", pll_index + 1);\n            while !RCC.sr().read().pllrdy(pll_index) {}\n            debug!(\"PLL{}: ready\", pll_index + 1);\n\n            PllOutput {\n                divm: Some(Hertz(m)),\n                divn: Some(Hertz(n)),\n                divp1: Some(Hertz(p1)),\n                divp2: Some(Hertz(p2)),\n                output: Some(Hertz(in_clk.0 / m * n / p1)),\n            }\n        }\n        Some(Pll::Bypass { source }) => {\n            // check if source is ready\n            if !pll_source_ready(source.to_bits()) {\n                panic!(\"PLL source is not ready\")\n            }\n\n            // ensure pll is disabled\n            RCC.ccr().write(|w| w.set_pllonc(pll_index, true));\n            while RCC.sr().read().pllrdy(pll_index) {}\n\n            cfgr1.modify(|w| {\n                w.set_pllbyp(true);\n                w.set_pllsel(source);\n            });\n\n            let in_clk = match source {\n                Pllsel::HSI => unwrap!(input.hsi),\n                Pllsel::MSI => unwrap!(input.msi),\n                Pllsel::HSE => unwrap!(input.hse),\n                Pllsel::I2S_CKIN => unwrap!(input.i2s_ckin),\n                _ => panic!(\"reserved PLL source not allowed\"),\n            };\n\n            PllOutput {\n                output: Some(in_clk),\n                ..Default::default()\n            }\n        }\n        None => {\n            disable_pll(pll_index);\n\n            PllOutput::default()\n        }\n    }\n}\n\n#[allow(dead_code)]\nstruct OscOutput {\n    hsi: Option<Hertz>,\n    hse: Option<Hertz>,\n    msi: Option<Hertz>,\n    lsi: Option<Hertz>,\n    lse: Option<Hertz>,\n    pll1: Option<Hertz>,\n    pll2: Option<Hertz>,\n    pll3: Option<Hertz>,\n    pll4: Option<Hertz>,\n    ic1sel: Icsel,\n    ic2sel: Icsel,\n    ic6sel: Icsel,\n    ic11sel: Icsel,\n}\n\nfn init_osc(config: Config) -> OscOutput {\n    let (cpu_src, sys_src) = {\n        let reg = RCC.cfgr().read();\n        (reg.cpusws(), reg.syssws())\n    };\n    let pll1_src = RCC.pllcfgr1(0).read().pllsel();\n    let pll2_src = RCC.pllcfgr1(1).read().pllsel();\n    let pll3_src = RCC.pllcfgr1(2).read().pllsel();\n    let pll4_src = RCC.pllcfgr1(3).read().pllsel();\n    let rcc_sr = RCC.sr().read();\n\n    debug!(\"configuring HSE\");\n\n    // hse configuration\n    let hse = if let Some(hse) = config.hse {\n        match hse.mode {\n            HseMode::Oscillator => {\n                debug!(\"HSE in oscillator mode\");\n            }\n            HseMode::Bypass => {\n                debug!(\"HSE in bypass mode\");\n                RCC.hsecfgr().modify(|w| {\n                    w.set_hsebyp(true);\n                    w.set_hseext(Hseext::ANALOG);\n                });\n            }\n            HseMode::BypassDigital => {\n                debug!(\"HSE in bypass digital mode\");\n                RCC.hsecfgr().modify(|w| {\n                    w.set_hsebyp(true);\n                    w.set_hseext(Hseext::DIGITAL);\n                });\n            }\n        }\n        RCC.csr().write(|w| w.set_hseons(true));\n\n        // wait until the hse is ready\n        while !RCC.sr().read().hserdy() {}\n\n        Some(hse.freq)\n    } else if cpu_src == Cpusws::HSE\n        || sys_src == Syssws::HSE\n        || (pll1_src == Pllsel::HSE && rcc_sr.pllrdy(0))\n        || (pll2_src == Pllsel::HSE && rcc_sr.pllrdy(1))\n        || (pll3_src == Pllsel::HSE && rcc_sr.pllrdy(2))\n        || (pll4_src == Pllsel::HSE && rcc_sr.pllrdy(3))\n    {\n        panic!(\n            \"When the HSE is used as cpu/system bus clock or clock source for any PLL, it is not allowed to be disabled\"\n        );\n    } else {\n        debug!(\"HSE off\");\n\n        RCC.ccr().write(|w| w.set_hseonc(true));\n        RCC.hsecfgr().modify(|w| {\n            w.set_hseext(Hseext::ANALOG);\n            w.set_hsebyp(false);\n        });\n\n        // wait until the hse is disabled\n        while RCC.sr().read().hserdy() {}\n\n        None\n    };\n\n    // hsi configuration\n    debug!(\"configuring HSI\");\n    let hsi = if let Some(hsi) = config.hsi {\n        RCC.csr().write(|w| w.set_hsions(true));\n        while !RCC.sr().read().hsirdy() {}\n\n        // set divider and calibration\n        RCC.hsicfgr().modify(|w| {\n            w.set_hsidiv(hsi.pre);\n            w.set_hsitrim(hsi.trim);\n        });\n\n        Some(HSI_FREQ / hsi.pre)\n    } else if cpu_src == Cpusws::HSI\n        || sys_src == Syssws::HSI\n        || (pll1_src == Pllsel::HSI && rcc_sr.pllrdy(0))\n        || (pll2_src == Pllsel::HSI && rcc_sr.pllrdy(1))\n        || (pll3_src == Pllsel::HSI && rcc_sr.pllrdy(2))\n        || (pll4_src == Pllsel::HSI && rcc_sr.pllrdy(3))\n    {\n        panic!(\n            \"When the HSI is used as cpu/system bus clock or clock source for any PLL, it is not allowed to be disabled\"\n        );\n    } else {\n        debug!(\"HSI off\");\n\n        RCC.ccr().write(|w| w.set_hsionc(true));\n        while RCC.sr().read().hsirdy() {}\n\n        None\n    };\n\n    // msi configuration\n    debug!(\"configuring MSI\");\n    let msi = if let Some(msi) = config.msi {\n        RCC.msicfgr().modify(|w| w.set_msifreqsel(msi.freq));\n        RCC.csr().write(|w| w.set_msions(true));\n        while !RCC.sr().read().msirdy() {}\n        RCC.msicfgr().modify(|w| w.set_msitrim(msi.trim));\n\n        Some(match msi.freq {\n            Msifreqsel::_4MHZ => Hertz::mhz(4),\n            Msifreqsel::_16MHZ => Hertz::mhz(16),\n        })\n    } else if cpu_src == Cpusws::MSI\n        || sys_src == Syssws::MSI\n        || (pll1_src == Pllsel::MSI && rcc_sr.pllrdy(0))\n        || (pll2_src == Pllsel::MSI && rcc_sr.pllrdy(1))\n        || (pll3_src == Pllsel::MSI && rcc_sr.pllrdy(2))\n        || (pll4_src == Pllsel::MSI && rcc_sr.pllrdy(3))\n    {\n        panic!(\n            \"When the MSI is used as cpu/system bus clock or clock source for any PLL, it is not allowed to be disabled\"\n        );\n    } else {\n        RCC.ccr().write(|w| w.set_msionc(true));\n        while RCC.sr().read().msirdy() {}\n\n        None\n    };\n\n    // lsi configuration\n    debug!(\"configuring LSI\");\n    let lsi = if config.lsi {\n        RCC.csr().write(|w| w.set_lsions(true));\n        while !RCC.sr().read().lsirdy() {}\n        Some(super::LSI_FREQ)\n    } else {\n        RCC.ccr().write(|w| w.set_lsionc(true));\n        while RCC.sr().read().lsirdy() {}\n        None\n    };\n\n    // lse configuration\n    debug!(\"configuring LSE\");\n    let lse = if config.lse {\n        RCC.csr().write(|w| w.set_lseons(true));\n        while !RCC.sr().read().lserdy() {}\n        Some(LSE_FREQ)\n    } else {\n        RCC.ccr().write(|w| w.set_lseonc(true));\n        while RCC.sr().read().lserdy() {}\n        None\n    };\n\n    let pll_input = PllInput {\n        hse,\n        msi,\n        hsi,\n        i2s_ckin: None,\n    };\n\n    // pll1,2,3,4 config\n    let pll_configs = [config.pll1, config.pll2, config.pll3, config.pll4];\n    let mut pll_outputs: [PllOutput; 4] = [PllOutput::default(); 4];\n\n    let ic1_src = RCC.iccfgr(0).read().icsel();\n    let ic2_src = RCC.iccfgr(1).read().icsel();\n    let ic6_src = RCC.iccfgr(5).read().icsel();\n    let ic11_src = RCC.iccfgr(10).read().icsel();\n\n    // If config wants a non-IC1 CPU source (HSI/HSE/MSI), switch now before\n    // touching PLLs. This prevents panicking when trying to reconfigure a PLL\n    // that's currently in use by IC1.\n    let cpu_src = if cpu_src == Cpusws::IC1 && !matches!(config.cpu, CpuClk::Ic1 { .. }) {\n        // Switch CPU clock to the target source first\n        debug!(\"switching CPU away from IC1 before PLL reconfiguration\");\n        let cpusw = Cpusw::from_bits(config.cpu.to_bits());\n        RCC.cfgr().modify(|w| w.set_cpusw(cpusw));\n        while RCC.cfgr().read().cpusws() != Cpusws::from_bits(config.cpu.to_bits()) {}\n        // Return the new CPU source\n        RCC.cfgr().read().cpusws()\n    } else {\n        cpu_src\n    };\n\n    // If config wants a non-IC2 sys source (HSI/HSE/MSI), switch now before\n    // touching PLLs. This prevents panicking when trying to reconfigure a PLL\n    // that's currently in use by IC2, IC6, or IC11.\n    let sys_src = if sys_src == Syssws::IC2 && !matches!(config.sys, SysClk::Ic2 { .. }) {\n        // Switch system clock to the target source first\n        debug!(\"switching sys clock away from IC2 before PLL reconfiguration\");\n        let syssw = Syssw::from_bits(config.sys.to_bits());\n        RCC.cfgr().modify(|w| w.set_syssw(syssw));\n        while RCC.cfgr().read().syssws() != Syssws::from_bits(config.sys.to_bits()) {}\n        // Return the new sys source\n        RCC.cfgr().read().syssws()\n    } else {\n        sys_src\n    };\n\n    for (n, (&pll, out)) in pll_configs.iter().zip(pll_outputs.iter_mut()).enumerate() {\n        debug!(\"configuring PLL{}\", n + 1);\n        let pll_ready = RCC.sr().read().pllrdy(n);\n\n        if is_new_pll_config(pll, n) {\n            let this_pll = Icsel::from_bits(n as u8);\n\n            if cpu_src == Cpusws::IC1 && ic1_src == this_pll {\n                panic!(\"PLL should not be disabled / reconfigured if used for IC1 (cpuclksrc)\")\n            }\n\n            if sys_src == Syssws::IC2 && (ic2_src == this_pll || ic6_src == this_pll || ic11_src == this_pll) {\n                panic!(\"PLL should not be disabled / reconfigured if used for IC2, IC6 or IC11 (sysclksrc)\")\n            }\n\n            *out = pll.map_or_else(\n                || {\n                    disable_pll(n);\n                    PllOutput::default()\n                },\n                |c| init_pll(Some(c), n, &pll_input),\n            );\n        } else if pll.is_some() && !pll_ready {\n            RCC.csr().write(|w| w.set_pllons(n, true));\n            while !RCC.sr().read().pllrdy(n) {}\n        }\n    }\n\n    OscOutput {\n        hsi,\n        hse,\n        msi,\n        lsi,\n        lse,\n        pll1: pll_outputs[0].output,\n        pll2: pll_outputs[1].output,\n        pll3: pll_outputs[2].output,\n        pll4: pll_outputs[3].output,\n        ic1sel: ic1_src,\n        ic2sel: ic2_src,\n        ic6sel: ic6_src,\n        ic11sel: ic11_src,\n    }\n}\n\nfn is_new_pll_config(pll: Option<Pll>, pll_index: usize) -> bool {\n    let cfgr1 = RCC.pllcfgr1(pll_index).read();\n    let cfgr2 = RCC.pllcfgr2(pll_index).read();\n    let cfgr3 = RCC.pllcfgr3(pll_index).read();\n\n    let ready = RCC.sr().read().pllrdy(pll_index);\n    let bypass = cfgr1.pllbyp();\n\n    match (pll, ready, bypass) {\n        (None, true, _) => return true,\n        (Some(_), false, _) => return true,\n        (Some(conf), true, bypass) => match (conf, bypass) {\n            (Pll::Bypass { .. }, false) => return true,\n            (Pll::Oscillator { .. }, true) => return true,\n            _ => {}\n        },\n        _ => {}\n    }\n\n    match pll {\n        Some(Pll::Bypass { source }) => cfgr1.pllsel() != source,\n        Some(Pll::Oscillator {\n            source,\n            divm: m,\n            fractional,\n            divn: n,\n            divp1: p1,\n            divp2: p2,\n        }) => {\n            cfgr1.pllsel() != source\n                || cfgr1.plldivm() != m\n                || cfgr1.plldivn() != n\n                || cfgr2.plldivnfrac() != fractional\n                || cfgr3.pllpdiv1() != p1\n                || cfgr3.pllpdiv2() != p2\n        }\n        None => false,\n    }\n}\n\npub(crate) unsafe fn init(config: Config) {\n    debug!(\"enabling SYSCFG\");\n    // system configuration setup\n    RCC.apb4hensr().write(|w| w.set_syscfgens(true));\n    // delay after RCC peripheral clock enabling\n    RCC.apb4hensr().read();\n\n    debug!(\"setting VTOR\");\n\n    let vtor = unsafe {\n        let p = cortex_m::Peripherals::steal();\n        p.SCB.vtor.read()\n    };\n\n    // set default vector table location after reset or standby\n    SYSCFG.initsvtorcr().write(|w| w.set_svtor_addr(vtor));\n    // read back the value to ensure it is written before deactivating SYSCFG\n    SYSCFG.initsvtorcr().read();\n\n    debug!(\"deactivating SYSCFG\");\n\n    // deactivate SYSCFG\n    RCC.apb4hensr().write(|w| w.set_syscfgens(false));\n\n    debug!(\"enabling FPU\");\n\n    // enable fpu\n    unsafe {\n        let p = cortex_m::Peripherals::steal();\n        p.SCB.cpacr.modify(|w| w | (3 << 20) | (3 << 22));\n    }\n\n    // Configure RIF/RISAF for memory access\n    // RISAF register offsets: REG_CFGR=0x40, REG_STARTR=0x44, REG_ENDR=0x48, REG_CIDCFGR=0x4C (stride 0x40)\n    // CRITICAL: Enable RISAF and RIFSC clocks BEFORE accessing registers\n    debug!(\"configuring RISAF\");\n    RCC.ahb3ensr().write(|w| {\n        w.set_risafens(true); // Enable RISAF clock\n        w.set_rifscens(true); // Enable RIFSC clock\n    });\n    // Read-back delay after RCC peripheral clock enabling (matches ST HAL pattern)\n    // Must read from AHB3ENR (status) not AHB3ENSR (set) to ensure clock is stable\n    RCC.ahb3enr().read();\n\n    // RISAF3: SRAM access for DMA\n    {\n        // Region 0: secure access (RW for CIDs 0-3)\n        RISAF3.reg_cidcfgr(0).write(|w| {\n            for i in 0..4 {\n                w.set_rdenc(i, true);\n                w.set_wrenc(i, true);\n            }\n        });\n        RISAF3.reg_endr(0).write(|w| w.set_baddend(0xFFFFFFFF));\n        RISAF3.reg_cfgr(0).write(|w| {\n            w.set_bren(true);\n            w.set_sec(true);\n        });\n\n        // Region 1: non-secure access (RW for all CIDs)\n        RISAF3.reg_cidcfgr(1).write(|w| {\n            for i in 0..8 {\n                w.set_rdenc(i, true);\n                w.set_wrenc(i, true);\n            }\n        });\n        RISAF3.reg_endr(1).write(|w| w.set_baddend(0xFFFFFFFF));\n        RISAF3.reg_cfgr(1).write(|w| {\n            w.set_bren(true);\n            w.set_sec(false);\n        });\n    }\n\n    debug!(\"setting power supply config\");\n\n    power_supply_config(config.supply_config);\n\n    // VddIO power domain configuration per STM32N6 errata ES0620\n    // This must be done early in boot - set SV bits and wait for RDY\n    debug!(\"configuring VddIO power domains\");\n    {\n        // Enable supply valid for all VddIO domains (like ST's SystemInit)\n        // PWR is always accessible on N6, no need to enable clock\n\n        // SVMCR1: VddIO4\n        PWR.svmcr1().modify(|w| {\n            w.set_vddio4sv(Vddio4sv::B_0X1);\n        });\n        // SVMCR2: VddIO5\n        PWR.svmcr2().modify(|w| {\n            w.set_vddio5sv(Vddio5sv::B_0X1);\n        });\n        // SVMCR3: VddIO2 and VddIO3 (for XSPI1 and XSPI2)\n        PWR.svmcr3().modify(|w| {\n            w.set_vddio2sv(Vddio2sv::B_0X1);\n            w.set_vddio2vmen(true); // Enable voltage monitoring\n            w.set_vddio3sv(Vddio3sv::B_0X1);\n            w.set_vddio3vmen(true); // Enable voltage monitoring\n            // Set voltage range based on config\n            if config.vddio2_1v8 {\n                w.set_vddio2vrsel(Vddio2vrsel::B_0X1); // 1.8V mode\n            }\n            if config.vddio3_1v8 {\n                w.set_vddio3vrsel(Vddio3vrsel::B_0X1); // 1.8V mode\n            }\n        });\n\n        // Wait for VddIO domains to be ready\n        while PWR.svmcr3().read().vddio2rdy() != Vddio2rdy::B_0X1 {}\n        while PWR.svmcr3().read().vddio3rdy() != Vddio3rdy::B_0X1 {}\n\n        // Debug VddIO status after configuration\n        let svmcr3 = PWR.svmcr3().read();\n        debug!(\"VddIO2 ready: {}\", svmcr3.vddio2rdy() == Vddio2rdy::B_0X1);\n        debug!(\"VddIO3 ready: {}\", svmcr3.vddio3rdy() == Vddio3rdy::B_0X1);\n        debug!(\"SVMCR3 raw = 0x{:08x}\", svmcr3.0);\n\n        // Configure compensation cells per errata ES0620\n        // SYSCFG is already enabled earlier in init\n\n        // Set compensation cell values (0x287 = ST's recommended value)\n        // ransrc=7 (bits 0-3), rapsrc=8 (bits 4-7), en=1 (bit 8)\n        SYSCFG.vddio2cccr().write(|w| {\n            w.set_ransrc(0x7);\n            w.set_rapsrc(0x8);\n            w.set_en(Vddio2cccrEn::B_0X1);\n        });\n        SYSCFG.vddio3cccr().write(|w| {\n            w.set_ransrc(0x7);\n            w.set_rapsrc(0x8);\n            w.set_en(Vddio3cccrEn::B_0X1);\n        });\n        SYSCFG.vddio4cccr().write(|w| {\n            w.set_ransrc(0x7);\n            w.set_rapsrc(0x8);\n            w.set_en(Vddio4cccrEn::B_0X1);\n        });\n    }\n\n    let osc = init_osc(config);\n    let ic_freqs = [\n        config.ic1,\n        config.ic2,\n        config.ic3,\n        config.ic4,\n        config.ic5,\n        config.ic6,\n        config.ic7,\n        config.ic8,\n        config.ic9,\n        config.ic10,\n        config.ic11,\n        config.ic12,\n        config.ic13,\n        config.ic14,\n        config.ic15,\n        config.ic16,\n        config.ic17,\n        config.ic18,\n        config.ic19,\n        config.ic20,\n    ]\n    .map(|ic| {\n        let ic_cfg = ic?;\n        let pll_freq = match ic_cfg.source.to_bits() {\n            0 => osc.pll1,\n            1 => osc.pll2,\n            2 => osc.pll3,\n            3 => osc.pll4,\n            _ => None,\n        }?;\n        let divider = (ic_cfg.divider.to_bits() as u32) + 1; // ICINT 0 = divide by 1\n        Some(Hertz(pll_freq.0 / divider))\n    });\n    let clock_inputs = ClocksInput {\n        hsi: osc.hsi,\n        msi: osc.msi,\n        hse: osc.hse,\n        ic1: ic_freqs[0],\n        ic2: ic_freqs[1],\n        ic3: ic_freqs[2],\n        ic4: ic_freqs[3],\n        ic5: ic_freqs[4],\n        ic6: ic_freqs[5],\n        ic7: ic_freqs[6],\n        ic8: ic_freqs[7],\n        ic9: ic_freqs[8],\n        ic10: ic_freqs[9],\n        ic11: ic_freqs[10],\n        ic12: ic_freqs[11],\n        ic13: ic_freqs[12],\n        ic14: ic_freqs[13],\n        ic15: ic_freqs[14],\n        ic16: ic_freqs[15],\n        ic17: ic_freqs[16],\n        ic18: ic_freqs[17],\n        ic19: ic_freqs[18],\n        ic20: ic_freqs[19],\n    };\n    let clocks = init_clocks(config, &clock_inputs);\n\n    // TODO: sysb, sysc, sysd must have the same clock source\n\n    config.mux.init();\n\n    set_clocks!(\n        sys: Some(clocks.sysclk),\n        hsi: clock_inputs.hsi,\n        hsi_div: None,\n        hse: clock_inputs.hse,\n        msi: clock_inputs.msi,\n        lse: None,\n        hclk1: Some(clocks.ahb),\n        hclk2: Some(clocks.ahb),\n        hclk3: Some(clocks.ahb),\n        hclk4: Some(clocks.ahb),\n        hclk5: Some(clocks.ahb),\n        pclk1: Some(clocks.apb1),\n        pclk2: Some(clocks.apb2),\n        pclk1_tim: Some(clocks.pclk_tim),\n        pclk2_tim: Some(clocks.pclk_tim),\n        pclk4: Some(clocks.apb4),\n        pclk5: Some(clocks.apb5),\n        per: None,\n        rtc: None,\n        i2s_ckin: None,\n        ic1: clock_inputs.ic1,\n        ic2: clock_inputs.ic2,\n        ic3: clock_inputs.ic3,\n        ic4: clock_inputs.ic4,\n        ic5: clock_inputs.ic5,\n        ic6: clock_inputs.ic6,\n        ic7: clock_inputs.ic7,\n        ic8: clock_inputs.ic8,\n        ic9: clock_inputs.ic9,\n        ic10: clock_inputs.ic10,\n        ic11: clock_inputs.ic11,\n        ic12: clock_inputs.ic12,\n        ic13: clock_inputs.ic13,\n        ic14: clock_inputs.ic14,\n        ic15: clock_inputs.ic15,\n        ic16: clock_inputs.ic16,\n        ic17: clock_inputs.ic17,\n        ic18: clock_inputs.ic18,\n        ic19: clock_inputs.ic19,\n        ic20: clock_inputs.ic20,\n    );\n}\n"
  },
  {
    "path": "embassy-stm32/src/rcc/u3.rs",
    "content": "use stm32_metapac::rcc::vals::{Msikdiv, Msisdiv, Msissel};\n\n// pub use crate::pac::pwr::vals::Vosr as VoltageScale;\npub use crate::pac::rcc::vals::{Hpre as AHBPrescaler, Ppre as APBPrescaler, Sw as Sysclk};\nuse crate::pac::rcc::vals::{Hseext, Msipllsel, Msirgsel};\nuse crate::pac::{FLASH, PWR, RCC};\nuse crate::rcc::LSI_FREQ;\nuse crate::time::Hertz;\n\n/// HSI speed\npub const HSI_FREQ: Hertz = Hertz(16_000_000);\n\n#[derive(Clone, Copy, Eq, PartialEq)]\npub enum VoltageScale {\n    /// High performance range (system clock freq up to 96MHz)\n    RANGE1,\n    /// Low power range (system clock freq up to 48MHz)\n    RANGE2,\n}\n\n#[derive(Clone, Copy, Eq, PartialEq)]\npub enum MSIRange {\n    /// MSIRC0\n    /// Range 0: 96 MHz\n    RANGE0_96MHZ,\n    /// Range 1: 48 MHz\n    RANGE1_48MHZ,\n    /// Range 2/4: 24 MHz\n    RANGE2_24MHZ,\n    /// Range 3/5: 12 MHz\n    RANGE3_12MHZ,\n    /// MSIRC1\n    /// Range 4: 24 MHz\n    RANGE4_24MHZ,\n    /// Range 5: 12 MHz\n    RANGE5_12MHZ,\n    /// Range 6: 6 MHz\n    RANGE6_6MHZ,\n    /// Range 7: 3 MHz\n    RANGE7_3MHZ,\n}\n\nimpl From<MSIRange> for Msisdiv {\n    fn from(range: MSIRange) -> Self {\n        match range {\n            MSIRange::RANGE0_96MHZ => Msisdiv::DIV1,\n            MSIRange::RANGE1_48MHZ => Msisdiv::DIV2,\n            MSIRange::RANGE2_24MHZ => Msisdiv::DIV4,\n            MSIRange::RANGE3_12MHZ => Msisdiv::DIV8,\n            MSIRange::RANGE4_24MHZ => Msisdiv::DIV1,\n            MSIRange::RANGE5_12MHZ => Msisdiv::DIV2,\n            MSIRange::RANGE6_6MHZ => Msisdiv::DIV4,\n            MSIRange::RANGE7_3MHZ => Msisdiv::DIV8,\n        }\n    }\n}\n\nimpl From<MSIRange> for Msikdiv {\n    fn from(range: MSIRange) -> Self {\n        match range {\n            MSIRange::RANGE0_96MHZ => Msikdiv::DIV1,\n            MSIRange::RANGE1_48MHZ => Msikdiv::DIV2,\n            MSIRange::RANGE2_24MHZ => Msikdiv::DIV4,\n            MSIRange::RANGE3_12MHZ => Msikdiv::DIV8,\n            MSIRange::RANGE4_24MHZ => Msikdiv::DIV1,\n            MSIRange::RANGE5_12MHZ => Msikdiv::DIV2,\n            MSIRange::RANGE6_6MHZ => Msikdiv::DIV4,\n            MSIRange::RANGE7_3MHZ => Msikdiv::DIV8,\n        }\n    }\n}\n\n#[derive(Clone, Copy, Eq, PartialEq)]\npub enum HseMode {\n    /// crystal/ceramic oscillator (HSEBYP=0)\n    Oscillator,\n    /// external analog clock (low swing) (HSEBYP=1, HSEEXT=0)\n    Bypass,\n    /// external digital clock (full swing) (HSEBYP=1, HSEEXT=1)\n    BypassDigital,\n}\n\n#[derive(Clone, Copy, Eq, PartialEq)]\npub struct Hse {\n    /// HSE frequency.\n    pub freq: Hertz,\n    /// HSE mode.\n    pub mode: HseMode,\n}\n\n#[derive(Clone, Copy, PartialEq)]\npub enum MsiAutoCalibration {\n    /// MSI auto-calibration is disabled\n    Disabled,\n    /// MSIS is given priority for auto-calibration\n    MSIS,\n    /// MSIK is given priority for auto-calibration\n    MSIK,\n    /// MSIS with fast mode (always on)\n    MsisFast,\n    /// MSIK with fast mode (always on)\n    MsikFast,\n}\n\nimpl MsiAutoCalibration {\n    const fn default() -> Self {\n        MsiAutoCalibration::Disabled\n    }\n\n    fn base_mode(&self) -> Self {\n        match self {\n            MsiAutoCalibration::Disabled => MsiAutoCalibration::Disabled,\n            MsiAutoCalibration::MSIS => MsiAutoCalibration::MSIS,\n            MsiAutoCalibration::MSIK => MsiAutoCalibration::MSIK,\n            MsiAutoCalibration::MsisFast => MsiAutoCalibration::MSIS,\n            MsiAutoCalibration::MsikFast => MsiAutoCalibration::MSIK,\n        }\n    }\n\n    fn is_fast(&self) -> bool {\n        matches!(self, MsiAutoCalibration::MsisFast | MsiAutoCalibration::MsikFast)\n    }\n}\n\nimpl Default for MsiAutoCalibration {\n    fn default() -> Self {\n        Self::default()\n    }\n}\n\n#[derive(Clone, Copy)]\npub struct Config {\n    // base clock sources\n    pub msis: Option<MSIRange>,\n    pub msik: Option<MSIRange>,\n    pub hsi: bool,\n    pub hse: Option<Hse>,\n    pub hsi48: Option<super::Hsi48Config>,\n\n    // sysclk, buses.\n    pub sys: Sysclk,\n    pub ahb_pre: AHBPrescaler,\n    pub apb1_pre: APBPrescaler,\n    pub apb2_pre: APBPrescaler,\n    pub apb3_pre: APBPrescaler,\n\n    /// The voltage range influences the maximum clock frequencies for different parts of the\n    /// device. In particular, system clocks exceeding 48 MHz require `RANGE1`.\n    ///\n    /// See RM0487 § 10.2.3 for clock source frequency limits.\n    pub voltage_range: VoltageScale,\n    pub ls: super::LsConfig,\n\n    /// Per-peripheral kernel clock selection muxes\n    pub mux: super::mux::ClockMux,\n    pub auto_calibration: MsiAutoCalibration,\n}\n\nimpl Config {\n    pub const fn new() -> Self {\n        Self {\n            msis: Some(MSIRange::RANGE5_12MHZ),\n            msik: Some(MSIRange::RANGE5_12MHZ),\n            hse: None,\n            hsi: false,\n            hsi48: Some(crate::rcc::Hsi48Config::new()),\n            sys: Sysclk::MSIS,\n            ahb_pre: AHBPrescaler::DIV1,\n            apb1_pre: APBPrescaler::DIV1,\n            apb2_pre: APBPrescaler::DIV1,\n            apb3_pre: APBPrescaler::DIV1,\n            voltage_range: VoltageScale::RANGE1,\n            ls: crate::rcc::LsConfig::new(),\n            mux: super::mux::ClockMux::default(),\n            auto_calibration: MsiAutoCalibration::default(),\n        }\n    }\n}\n\nimpl Default for Config {\n    fn default() -> Self {\n        Self::new()\n    }\n}\n\npub(crate) unsafe fn init(config: Config) {\n    // Set the requested power mode\n    // Voltage range enable bits must be at opposite values and can\n    // only be set once the current range is ready. See RM0487 § 9.5.4.\n    match config.voltage_range {\n        VoltageScale::RANGE1 => {\n            if PWR.vosr().read().r2en() {\n                while !PWR.vosr().read().r2rdy() {}\n            }\n            PWR.vosr().modify(|w| {\n                w.set_r1en(true);\n                w.set_r2en(false);\n            });\n            while !PWR.vosr().read().r1rdy() {}\n        }\n        VoltageScale::RANGE2 => {\n            if PWR.vosr().read().r1en() {\n                while !PWR.vosr().read().r1rdy() {}\n            }\n            PWR.vosr().modify(|w| {\n                w.set_r2en(true);\n                w.set_r1en(false);\n            });\n            while !PWR.vosr().read().r2rdy() {}\n        }\n    }\n\n    let lse_calibration_freq = if config.auto_calibration != MsiAutoCalibration::Disabled {\n        // LSE must be configured and peripherals clocked for MSI auto-calibration\n        let lse_config = config\n            .ls\n            .lse\n            .clone()\n            .expect(\"LSE must be configured for MSI auto-calibration\");\n        assert!(lse_config.peripherals_clocked);\n\n        // Expect less than +/- 5% deviation for LSE frequency\n        if (31_100..=34_400).contains(&lse_config.frequency.0) {\n            // Check that the calibration is applied to an active clock\n            match (\n                config.auto_calibration.base_mode(),\n                config.msis.is_some(),\n                config.msik.is_some(),\n            ) {\n                (MsiAutoCalibration::MSIS, true, _) => {\n                    // MSIS is active and using LSE for auto-calibration\n                    Some(lse_config.frequency)\n                }\n                (MsiAutoCalibration::MSIK, _, true) => {\n                    // MSIK is active and using LSE for auto-calibration\n                    Some(lse_config.frequency)\n                }\n                // improper configuration\n                _ => panic!(\"MSIx auto-calibration is enabled for a source that has not been configured.\"),\n            }\n        } else {\n            panic!(\"LSE frequency more than 5% off from 32.768 kHz, cannot use for MSI auto-calibration\");\n        }\n    } else {\n        None\n    };\n\n    let mut msis = config.msis.map(|range| {\n        // Check MSI output per RM0487 § 10.2.3 Table 98\n        match config.voltage_range {\n            VoltageScale::RANGE2 => {\n                assert!(msirange_to_hertz(range).0 <= 48_000_000);\n            }\n            _ => {}\n        }\n\n        // RM0487 § 10.5.2: spin until MSIS is off or MSIS is ready before setting its range\n        loop {\n            let cr = RCC.cr().read();\n            if cr.msison() == false || cr.msisrdy() == true {\n                break;\n            }\n        }\n\n        // Use MSIRC0 or MSIRC1 depending on requested range.\n        let msissel = if msirange_to_hertz(range).0 <= 24_000_000 {\n            Msissel::MSIRC1_24MHZ\n        } else {\n            Msissel::MSIRC0_96MHZ\n        };\n        RCC.icscr1().modify(|w| {\n            w.set_msissel(msissel);\n            w.set_msisdiv(range.into());\n            w.set_msirgsel(Msirgsel::RCC_ICSCR1);\n        });\n        RCC.cr().write(|w| {\n            // Out of reset MSIPLLxEN and MSIPLLSEL are 0\n            // and msison is true\n            w.set_msipll0en(false);\n            w.set_msipll1en(false);\n            w.set_msison(true);\n        });\n        let msis = if let (Some(freq), MsiAutoCalibration::MSIS) =\n            (lse_calibration_freq, config.auto_calibration.base_mode())\n        {\n            // Enable the MSIS auto-calibration feature\n            if msissel == Msissel::MSIRC0_96MHZ {\n                RCC.icscr1().modify(|w| w.set_msipll0sel(Msipllsel::LSE));\n                RCC.cr().modify(|w| w.set_msipll0en(true));\n            } else {\n                RCC.icscr1().modify(|w| w.set_msipll1sel(Msipllsel::LSE));\n                RCC.cr().modify(|w| w.set_msipll1en(true));\n            }\n            calculate_calibrated_msi_frequency(range, freq)\n        } else {\n            msirange_to_hertz(range)\n        };\n        while !RCC.cr().read().msisrdy() {}\n        msis\n    });\n\n    let mut msik = config.msik.map(|range| {\n        // Check MSI output per RM0456 § 11.4.10\n        match config.voltage_range {\n            VoltageScale::RANGE2 => {\n                assert!(msirange_to_hertz(range).0 <= 48_000_000);\n            }\n            _ => {}\n        }\n\n        // RM0456 § 11.8.2: spin until MSIK is off or MSIK is ready before setting its range\n        loop {\n            let cr = RCC.cr().read();\n            if cr.msikon() == false || cr.msikrdy() == true {\n                break;\n            }\n        }\n\n        RCC.icscr1().modify(|w| {\n            w.set_msikdiv(range.into());\n            w.set_msirgsel(Msirgsel::RCC_ICSCR1);\n        });\n        RCC.cr().modify(|w| {\n            w.set_msikon(true);\n        });\n        let msik = if let (Some(freq), MsiAutoCalibration::MSIK) =\n            (lse_calibration_freq, config.auto_calibration.base_mode())\n        {\n            // Enable the MSIK auto-calibration feature\n\n            // RCC.cr().modify(|w| w.set_msipllsel(Msipllsel::MSIK));\n            // RCC.cr().modify(|w| w.set_msipllen(true));\n            calculate_calibrated_msi_frequency(range, freq)\n        } else {\n            msirange_to_hertz(range)\n        };\n        while !RCC.cr().read().msikrdy() {}\n        msik\n    });\n\n    if let Some(lse_freq) = lse_calibration_freq {\n        // If both MSIS and MSIK are enabled, we need to check if they are using the same internal source.\n        if let (Some(msis_range), Some(msik_range)) = (config.msis, config.msik) {\n            if msis_range == msik_range {\n                // Clock source is shared, both will be auto calibrated, recalculate other frequency\n                match config.auto_calibration.base_mode() {\n                    MsiAutoCalibration::MSIS => {\n                        msik = Some(calculate_calibrated_msi_frequency(msik_range, lse_freq));\n                    }\n                    MsiAutoCalibration::MSIK => {\n                        msis = Some(calculate_calibrated_msi_frequency(msis_range, lse_freq));\n                    }\n                    _ => {}\n                }\n            }\n        }\n        // Check if Fast mode should be used\n        if config.auto_calibration.is_fast() {\n            RCC.cr().modify(|w| {\n                // TODO clean this up to select the correct MSIPLLxFAST bit\n                w.set_msipll0fast(true);\n                // w.set_msipllfast(Msipllfast::FAST);\n            });\n        }\n    }\n\n    let hsi = config.hsi.then(|| {\n        RCC.cr().modify(|w| w.set_hsion(true));\n        while !RCC.cr().read().hsirdy() {}\n\n        HSI_FREQ\n    });\n\n    let hse = config.hse.map(|hse| {\n        // Check frequency limits per RM456 § 11.4.10\n        match config.voltage_range {\n            VoltageScale::RANGE1 => {\n                assert!(hse.freq.0 <= 50_000_000);\n            }\n            VoltageScale::RANGE2 => {\n                assert!(hse.freq.0 <= 48_000_000);\n            }\n        }\n\n        // Enable HSE, and wait for it to stabilize\n        RCC.cr().modify(|w| {\n            w.set_hseon(true);\n            w.set_hsebyp(hse.mode != HseMode::Oscillator);\n            w.set_hseext(match hse.mode {\n                HseMode::Oscillator | HseMode::Bypass => Hseext::ANALOG,\n                HseMode::BypassDigital => Hseext::DIGITAL,\n            });\n        });\n        while !RCC.cr().read().hserdy() {}\n\n        hse.freq\n    });\n\n    let hsi48 = config.hsi48.map(super::init_hsi48);\n\n    // let pll_input = PllInput { hse, hsi, msi: msis };\n    // let pll1 = init_pll(PllInstance::Pll1, config.pll1, &pll_input, config.voltage_range);\n    // let pll2 = init_pll(PllInstance::Pll2, config.pll2, &pll_input, config.voltage_range);\n    // let pll3 = init_pll(PllInstance::Pll3, config.pll3, &pll_input, config.voltage_range);\n\n    let sys_clk = match config.sys {\n        Sysclk::HSE => hse.unwrap(),\n        Sysclk::HSI16 => hsi.unwrap(),\n        Sysclk::MSIS => msis.unwrap(),\n        Sysclk::_RESERVED_3 => unreachable!(),\n        // Sysclk:: => pll1.r.unwrap(),\n    };\n\n    // Do we need the EPOD booster to reach the target clock speed per § 10.5.4?\n    if sys_clk >= Hertz::mhz(24) {\n        // Enable the booster\n        PWR.vosr().modify(|w| w.set_boosten(true));\n        while !PWR.vosr().read().boostrdy() {}\n    }\n\n    // The clock source is ready\n    // Calculate and set the flash wait states\n    // TODO: add wait states for low power modes\n    let wait_states = match config.voltage_range {\n        // VOS 1 range VCORE 1.26V - 1.40V\n        VoltageScale::RANGE1 => match sys_clk.0 {\n            ..=32_000_000 => 0,\n            ..=64_000_000 => 1,\n            ..=96_000_000 => 2,\n            _ => 3,\n        },\n        // VOS 2 range VCORE 1.15V - 1.26V\n        VoltageScale::RANGE2 => match sys_clk.0 {\n            ..=16_000_000 => 0,\n            ..=32_000_000 => 1,\n            ..=48_000_000 => 2,\n            _ => 3,\n        },\n    };\n    FLASH.acr().modify(|w| {\n        w.set_latency(wait_states);\n    });\n\n    // Switch the system clock source\n    RCC.cfgr1().modify(|w| w.set_sw(config.sys));\n    while RCC.cfgr1().read().sw() != config.sys {}\n\n    // Configure the bus prescalers\n    RCC.cfgr2().modify(|w| {\n        w.set_hpre(config.ahb_pre);\n        w.set_ppre1(config.apb1_pre);\n        w.set_ppre2(config.apb2_pre);\n    });\n    RCC.cfgr3().modify(|w| {\n        w.set_ppre3(config.apb3_pre);\n    });\n\n    let hclk = sys_clk / config.ahb_pre;\n\n    let hclk_max = match config.voltage_range {\n        VoltageScale::RANGE1 => Hertz::mhz(96),\n        VoltageScale::RANGE2 => Hertz::mhz(48),\n    };\n    assert!(hclk <= hclk_max);\n\n    let (pclk1, pclk1_tim) = super::util::calc_pclk(hclk, config.apb1_pre);\n    let (pclk2, pclk2_tim) = super::util::calc_pclk(hclk, config.apb2_pre);\n    let (pclk3, _) = super::util::calc_pclk(hclk, config.apb3_pre);\n\n    let rtc = config.ls.init();\n    let lse = config.ls.lse.map(|l| l.frequency);\n    let lsi = config.ls.lsi.then_some(LSI_FREQ);\n\n    config.mux.init();\n\n    set_clocks!(\n        sys: Some(sys_clk),\n        hclk1: Some(hclk),\n        hclk2: Some(hclk),\n        hclk3: Some(hclk),\n        pclk1: Some(pclk1),\n        pclk2: Some(pclk2),\n        pclk3: Some(pclk3),\n        pclk1_tim: Some(pclk1_tim),\n        pclk2_tim: Some(pclk2_tim),\n        msik: msik,\n        hsi48: hsi48,\n        rtc: rtc,\n        lse: lse,\n        lsi: lsi,\n        hse: hse,\n        hsi: hsi,\n\n        // TODO\n        audioclk: None,\n        shsi: None,\n    );\n}\n\nfn msirange_to_hertz(range: MSIRange) -> Hertz {\n    match range {\n        MSIRange::RANGE0_96MHZ => Hertz(96_000_000),\n        MSIRange::RANGE1_48MHZ => Hertz(48_000_000),\n        MSIRange::RANGE2_24MHZ => Hertz(24_000_000),\n        MSIRange::RANGE3_12MHZ => Hertz(12_000_000),\n        MSIRange::RANGE4_24MHZ => Hertz(12_000_000),\n        MSIRange::RANGE5_12MHZ => Hertz(12_000_000),\n        MSIRange::RANGE6_6MHZ => Hertz(6_000_000),\n        MSIRange::RANGE7_3MHZ => Hertz(3_000_000),\n    }\n}\n\n/// Fraction structure for MSI auto-calibration\n/// Represents the multiplier as numerator/denominator that LSE frequency is multiplied by\n#[derive(Debug, Clone, Copy)]\nstruct MsiFraction {\n    numerator: u32,\n    denominator: u32,\n}\n\nimpl MsiFraction {\n    const fn new(numerator: u32, denominator: u32) -> Self {\n        Self { numerator, denominator }\n    }\n\n    /// Calculate the calibrated frequency given an LSE frequency\n    fn calculate_frequency(&self, lse_freq: Hertz) -> Hertz {\n        Hertz(lse_freq.0 * self.numerator / self.denominator)\n    }\n}\n\nfn get_msi_calibration_fraction(range: MSIRange) -> MsiFraction {\n    // Exploiting the MSIx internals to make calculations compact\n    let denominator = (range as u32 & 0x03) + 1;\n    // Base multipliers are deduced from Table 82: MSI oscillator characteristics in data sheet\n    let numerator = [1465, 122, 94, 12][range as usize >> 2];\n\n    MsiFraction::new(numerator, denominator)\n}\n\n/// Calculate the calibrated MSI frequency for a given range and LSE frequency\nfn calculate_calibrated_msi_frequency(range: MSIRange, lse_freq: Hertz) -> Hertz {\n    let fraction = get_msi_calibration_fraction(range);\n    fraction.calculate_frequency(lse_freq)\n}\n"
  },
  {
    "path": "embassy-stm32/src/rcc/u5.rs",
    "content": "pub use crate::pac::pwr::vals::Vos as VoltageScale;\n#[cfg(all(peri_usb_otg_hs))]\npub use crate::pac::rcc::vals::Otghssel;\npub use crate::pac::rcc::vals::{\n    Hpre as AHBPrescaler, Msirange, Msirange as MSIRange, Plldiv as PllDiv, Pllm as PllPreDiv, Plln as PllMul,\n    Pllsrc as PllSource, Ppre as APBPrescaler, Sw as Sysclk,\n};\nuse crate::pac::rcc::vals::{Hseext, Msipllfast, Msipllsel, Msirgsel, Pllmboost, Pllrge};\nuse crate::pac::{FLASH, PWR, RCC};\n#[cfg(all(peri_usb_otg_hs))]\npub use crate::pac::{SYSCFG, syscfg::vals::Usbrefcksel};\nuse crate::rcc::LSI_FREQ;\nuse crate::time::Hertz;\n\n/// HSI speed\npub const HSI_FREQ: Hertz = Hertz(16_000_000);\n\n#[derive(Clone, Copy, Eq, PartialEq)]\npub enum HseMode {\n    /// crystal/ceramic oscillator (HSEBYP=0)\n    Oscillator,\n    /// external analog clock (low swing) (HSEBYP=1, HSEEXT=0)\n    Bypass,\n    /// external digital clock (full swing) (HSEBYP=1, HSEEXT=1)\n    BypassDigital,\n}\n\n#[derive(Clone, Copy, Eq, PartialEq)]\npub struct Hse {\n    /// HSE frequency.\n    pub freq: Hertz,\n    /// HSE mode.\n    pub mode: HseMode,\n}\n\n#[derive(Clone, Copy)]\npub struct Pll {\n    /// The clock source for the PLL.\n    pub source: PllSource,\n    /// The PLL pre-divider.\n    ///\n    /// The clock speed of the `source` divided by `m` must be between 4 and 16 MHz.\n    pub prediv: PllPreDiv,\n    /// The PLL multiplier.\n    ///\n    /// The multiplied clock – `source` divided by `m` times `n` – must be between 128 and 544\n    /// MHz. The upper limit may be lower depending on the `Config { voltage_range }`.\n    pub mul: PllMul,\n    /// The divider for the P output.\n    ///\n    /// The P output is one of several options\n    /// that can be used to feed the SAI/MDF/ADF Clock mux's.\n    pub divp: Option<PllDiv>,\n    /// The divider for the Q output.\n    ///\n    /// The Q ouput is one of severals options that can be used to feed the 48MHz clocks\n    /// and the OCTOSPI clock. It may also be used on the MDF/ADF clock mux's.\n    pub divq: Option<PllDiv>,\n    /// The divider for the R output.\n    ///\n    /// When used to drive the system clock, `source` divided by `m` times `n` divided by `r`\n    /// must not exceed 160 MHz. System clocks above 55 MHz require a non-default\n    /// `Config { voltage_range }`.\n    pub divr: Option<PllDiv>,\n}\n\n#[derive(Clone, Copy, PartialEq)]\npub enum MsiAutoCalibration {\n    /// MSI auto-calibration is disabled\n    Disabled,\n    /// MSIS is given priority for auto-calibration\n    MSIS,\n    /// MSIK is given priority for auto-calibration\n    MSIK,\n    /// MSIS with fast mode (always on)\n    MsisFast,\n    /// MSIK with fast mode (always on)\n    MsikFast,\n}\n\nimpl MsiAutoCalibration {\n    const fn default() -> Self {\n        MsiAutoCalibration::Disabled\n    }\n\n    fn base_mode(&self) -> Self {\n        match self {\n            MsiAutoCalibration::Disabled => MsiAutoCalibration::Disabled,\n            MsiAutoCalibration::MSIS => MsiAutoCalibration::MSIS,\n            MsiAutoCalibration::MSIK => MsiAutoCalibration::MSIK,\n            MsiAutoCalibration::MsisFast => MsiAutoCalibration::MSIS,\n            MsiAutoCalibration::MsikFast => MsiAutoCalibration::MSIK,\n        }\n    }\n\n    fn is_fast(&self) -> bool {\n        matches!(self, MsiAutoCalibration::MsisFast | MsiAutoCalibration::MsikFast)\n    }\n}\n\nimpl Default for MsiAutoCalibration {\n    fn default() -> Self {\n        Self::default()\n    }\n}\n\n#[derive(Clone, Copy)]\npub struct Config {\n    // base clock sources\n    pub msis: Option<MSIRange>,\n    pub msik: Option<MSIRange>,\n    pub hsi: bool,\n    pub hse: Option<Hse>,\n    pub hsi48: Option<super::Hsi48Config>,\n\n    // pll\n    pub pll1: Option<Pll>,\n    pub pll2: Option<Pll>,\n    pub pll3: Option<Pll>,\n\n    // sysclk, buses.\n    pub sys: Sysclk,\n    pub ahb_pre: AHBPrescaler,\n    pub apb1_pre: APBPrescaler,\n    pub apb2_pre: APBPrescaler,\n    pub apb3_pre: APBPrescaler,\n\n    /// The voltage range influences the maximum clock frequencies for different parts of the\n    /// device. In particular, system clocks exceeding 110 MHz require `RANGE1`, and system clocks\n    /// exceeding 55 MHz require at least `RANGE2`.\n    ///\n    /// See RM0456 § 10.5.4 for a general overview and § 11.4.10 for clock source frequency limits.\n    pub voltage_range: VoltageScale,\n    pub ls: super::LsConfig,\n\n    /// Per-peripheral kernel clock selection muxes\n    pub mux: super::mux::ClockMux,\n    pub auto_calibration: MsiAutoCalibration,\n}\n\nimpl Config {\n    pub const fn new() -> Self {\n        Self {\n            msis: Some(Msirange::RANGE_4MHZ),\n            msik: Some(Msirange::RANGE_4MHZ),\n            hse: None,\n            hsi: false,\n            hsi48: Some(crate::rcc::Hsi48Config::new()),\n            pll1: None,\n            pll2: None,\n            pll3: None,\n            sys: Sysclk::MSIS,\n            ahb_pre: AHBPrescaler::DIV1,\n            apb1_pre: APBPrescaler::DIV1,\n            apb2_pre: APBPrescaler::DIV1,\n            apb3_pre: APBPrescaler::DIV1,\n            voltage_range: VoltageScale::RANGE1,\n            ls: crate::rcc::LsConfig::new(),\n            mux: super::mux::ClockMux::default(),\n            auto_calibration: MsiAutoCalibration::default(),\n        }\n    }\n}\n\nimpl Default for Config {\n    fn default() -> Self {\n        Self::new()\n    }\n}\n\npub(crate) unsafe fn init(config: Config) {\n    // Set the requested power mode\n    PWR.vosr().modify(|w| w.set_vos(config.voltage_range));\n    while !PWR.vosr().read().vosrdy() {}\n\n    let lse_calibration_freq = if config.auto_calibration != MsiAutoCalibration::Disabled {\n        // LSE must be configured and peripherals clocked for MSI auto-calibration\n        let lse_config = config\n            .ls\n            .lse\n            .clone()\n            .expect(\"LSE must be configured for MSI auto-calibration\");\n        assert!(lse_config.peripherals_clocked);\n\n        // Expect less than +/- 5% deviation for LSE frequency\n        if (31_100..=34_400).contains(&lse_config.frequency.0) {\n            // Check that the calibration is applied to an active clock\n            match (\n                config.auto_calibration.base_mode(),\n                config.msis.is_some(),\n                config.msik.is_some(),\n            ) {\n                (MsiAutoCalibration::MSIS, true, _) => {\n                    // MSIS is active and using LSE for auto-calibration\n                    Some(lse_config.frequency)\n                }\n                (MsiAutoCalibration::MSIK, _, true) => {\n                    // MSIK is active and using LSE for auto-calibration\n                    Some(lse_config.frequency)\n                }\n                // improper configuration\n                _ => panic!(\"MSIx auto-calibration is enabled for a source that has not been configured.\"),\n            }\n        } else {\n            panic!(\"LSE frequency more than 5% off from 32.768 kHz, cannot use for MSI auto-calibration\");\n        }\n    } else {\n        None\n    };\n\n    let mut msis = config.msis.map(|range| {\n        // Check MSI output per RM0456 § 11.4.10\n        match config.voltage_range {\n            VoltageScale::RANGE4 => {\n                assert!(msirange_to_hertz(range).0 <= 24_000_000);\n            }\n            _ => {}\n        }\n\n        // RM0456 § 11.8.2: spin until MSIS is off or MSIS is ready before setting its range\n        loop {\n            let cr = RCC.cr().read();\n            if cr.msison() == false || cr.msisrdy() == true {\n                break;\n            }\n        }\n\n        RCC.icscr1().modify(|w| {\n            w.set_msisrange(range);\n            w.set_msirgsel(Msirgsel::ICSCR1);\n        });\n        RCC.cr().write(|w| {\n            w.set_msipllen(false);\n            w.set_msison(true);\n        });\n        let msis = if let (Some(freq), MsiAutoCalibration::MSIS) =\n            (lse_calibration_freq, config.auto_calibration.base_mode())\n        {\n            // Enable the MSIS auto-calibration feature\n            RCC.cr().modify(|w| w.set_msipllsel(Msipllsel::MSIS));\n            RCC.cr().modify(|w| w.set_msipllen(true));\n            calculate_calibrated_msi_frequency(range, freq)\n        } else {\n            msirange_to_hertz(range)\n        };\n        while !RCC.cr().read().msisrdy() {}\n        msis\n    });\n\n    let mut msik = config.msik.map(|range| {\n        // Check MSI output per RM0456 § 11.4.10\n        match config.voltage_range {\n            VoltageScale::RANGE4 => {\n                assert!(msirange_to_hertz(range).0 <= 24_000_000);\n            }\n            _ => {}\n        }\n\n        // RM0456 § 11.8.2: spin until MSIS is off or MSIS is ready before setting its range\n        loop {\n            let cr = RCC.cr().read();\n            if cr.msikon() == false || cr.msikrdy() == true {\n                break;\n            }\n        }\n\n        RCC.icscr1().modify(|w| {\n            w.set_msikrange(range);\n            w.set_msirgsel(Msirgsel::ICSCR1);\n        });\n        RCC.cr().modify(|w| {\n            w.set_msikon(true);\n        });\n        let msik = if let (Some(freq), MsiAutoCalibration::MSIK) =\n            (lse_calibration_freq, config.auto_calibration.base_mode())\n        {\n            // Enable the MSIK auto-calibration feature\n            RCC.cr().modify(|w| w.set_msipllsel(Msipllsel::MSIK));\n            RCC.cr().modify(|w| w.set_msipllen(true));\n            calculate_calibrated_msi_frequency(range, freq)\n        } else {\n            msirange_to_hertz(range)\n        };\n        while !RCC.cr().read().msikrdy() {}\n        msik\n    });\n\n    if let Some(lse_freq) = lse_calibration_freq {\n        // If both MSIS and MSIK are enabled, we need to check if they are using the same internal source.\n        if let (Some(msis_range), Some(msik_range)) = (config.msis, config.msik) {\n            if (msis_range as u8 >> 2) == (msik_range as u8 >> 2) {\n                // Clock source is shared, both will be auto calibrated, recalculate other frequency\n                match config.auto_calibration.base_mode() {\n                    MsiAutoCalibration::MSIS => {\n                        msik = Some(calculate_calibrated_msi_frequency(msik_range, lse_freq));\n                    }\n                    MsiAutoCalibration::MSIK => {\n                        msis = Some(calculate_calibrated_msi_frequency(msis_range, lse_freq));\n                    }\n                    _ => {}\n                }\n            }\n        }\n        // Check if Fast mode should be used\n        if config.auto_calibration.is_fast() {\n            RCC.cr().modify(|w| {\n                w.set_msipllfast(Msipllfast::FAST);\n            });\n        }\n    }\n\n    let hsi = config.hsi.then(|| {\n        RCC.cr().modify(|w| w.set_hsion(true));\n        while !RCC.cr().read().hsirdy() {}\n\n        HSI_FREQ\n    });\n\n    let hse = config.hse.map(|hse| {\n        // Check frequency limits per RM456 § 11.4.10\n        match config.voltage_range {\n            VoltageScale::RANGE1 | VoltageScale::RANGE2 | VoltageScale::RANGE3 => {\n                assert!(hse.freq.0 <= 50_000_000);\n            }\n            VoltageScale::RANGE4 => {\n                assert!(hse.freq.0 <= 25_000_000);\n            }\n        }\n\n        // Enable HSE, and wait for it to stabilize\n        RCC.cr().modify(|w| {\n            w.set_hseon(true);\n            w.set_hsebyp(hse.mode != HseMode::Oscillator);\n            w.set_hseext(match hse.mode {\n                HseMode::Oscillator | HseMode::Bypass => Hseext::ANALOG,\n                HseMode::BypassDigital => Hseext::DIGITAL,\n            });\n        });\n        while !RCC.cr().read().hserdy() {}\n\n        hse.freq\n    });\n\n    let hsi48 = config.hsi48.map(super::init_hsi48);\n\n    // There's a possibility that a bootloader that ran before us has configured the system clock\n    // source to be PLL1_R. In that case we'd get forever stuck on (de)configuring PLL1 as the chip\n    // prohibits disabling PLL1 when it's used as a source for system clock. Change the system\n    // clock source to MSIS which doesn't suffer from this conflict. The correct source per the\n    // provided config is then set further down.\n    // See https://github.com/embassy-rs/embassy/issues/5072\n    let default_system_clock_source = Config::default().sys;\n    RCC.cfgr1().modify(|w| w.set_sw(default_system_clock_source));\n    while RCC.cfgr1().read().sws() != default_system_clock_source {}\n\n    let pll_input = PllInput { hse, hsi, msi: msis };\n    let pll1 = config.pll1.map_or_else(\n        || {\n            pll_enable(PllInstance::Pll1, false);\n            PllOutput::default()\n        },\n        |c| init_pll(PllInstance::Pll1, Some(c), &pll_input, config.voltage_range),\n    );\n    let pll2 = config.pll2.map_or_else(\n        || {\n            pll_enable(PllInstance::Pll2, false);\n            PllOutput::default()\n        },\n        |c| init_pll(PllInstance::Pll2, Some(c), &pll_input, config.voltage_range),\n    );\n    let pll3 = config.pll3.map_or_else(\n        || {\n            pll_enable(PllInstance::Pll3, false);\n            PllOutput::default()\n        },\n        |c| init_pll(PllInstance::Pll3, Some(c), &pll_input, config.voltage_range),\n    );\n\n    let sys_clk = match config.sys {\n        Sysclk::HSE => hse.unwrap(),\n        Sysclk::HSI => hsi.unwrap(),\n        Sysclk::MSIS => msis.unwrap(),\n        Sysclk::PLL1_R => pll1.r.unwrap(),\n    };\n\n    // Do we need the EPOD booster to reach the target clock speed per § 10.5.4?\n    if sys_clk >= Hertz::mhz(55) {\n        // Enable the booster\n        PWR.vosr().modify(|w| w.set_boosten(true));\n        while !PWR.vosr().read().boostrdy() {}\n    }\n\n    // The clock source is ready\n    // Calculate and set the flash wait states\n    let wait_states = match config.voltage_range {\n        // VOS 1 range VCORE 1.26V - 1.40V\n        VoltageScale::RANGE1 => match sys_clk.0 {\n            ..=32_000_000 => 0,\n            ..=64_000_000 => 1,\n            ..=96_000_000 => 2,\n            ..=128_000_000 => 3,\n            _ => 4,\n        },\n        // VOS 2 range VCORE 1.15V - 1.26V\n        VoltageScale::RANGE2 => match sys_clk.0 {\n            ..=30_000_000 => 0,\n            ..=60_000_000 => 1,\n            ..=90_000_000 => 2,\n            _ => 3,\n        },\n        // VOS 3 range VCORE 1.05V - 1.15V\n        VoltageScale::RANGE3 => match sys_clk.0 {\n            ..=24_000_000 => 0,\n            ..=48_000_000 => 1,\n            _ => 2,\n        },\n        // VOS 4 range VCORE 0.95V - 1.05V\n        VoltageScale::RANGE4 => match sys_clk.0 {\n            ..=12_000_000 => 0,\n            _ => 1,\n        },\n    };\n    FLASH.acr().modify(|w| {\n        w.set_latency(wait_states);\n    });\n\n    // Switch the system clock source\n    RCC.cfgr1().modify(|w| w.set_sw(config.sys));\n    while RCC.cfgr1().read().sws() != config.sys {}\n\n    // Configure the bus prescalers\n    RCC.cfgr2().modify(|w| {\n        w.set_hpre(config.ahb_pre);\n        w.set_ppre1(config.apb1_pre);\n        w.set_ppre2(config.apb2_pre);\n    });\n    RCC.cfgr3().modify(|w| {\n        w.set_ppre3(config.apb3_pre);\n    });\n\n    let hclk = sys_clk / config.ahb_pre;\n\n    let hclk_max = match config.voltage_range {\n        VoltageScale::RANGE1 => Hertz::mhz(160),\n        VoltageScale::RANGE2 => Hertz::mhz(110),\n        VoltageScale::RANGE3 => Hertz::mhz(55),\n        VoltageScale::RANGE4 => Hertz::mhz(25),\n    };\n    assert!(hclk <= hclk_max);\n\n    let (pclk1, pclk1_tim) = super::util::calc_pclk(hclk, config.apb1_pre);\n    let (pclk2, pclk2_tim) = super::util::calc_pclk(hclk, config.apb2_pre);\n    let (pclk3, _) = super::util::calc_pclk(hclk, config.apb3_pre);\n\n    let rtc = config.ls.init();\n\n    #[cfg(all(stm32u5, peri_usb_otg_hs))]\n    let usb_refck = match config.mux.otghssel {\n        Otghssel::HSE => hse,\n        Otghssel::HSE_DIV_2 => hse.map(|hse_val| hse_val / 2u8),\n        Otghssel::PLL1_P => pll1.p,\n        Otghssel::PLL1_P_DIV_2 => pll1.p.map(|pll1p_val| pll1p_val / 2u8),\n    };\n    #[cfg(all(stm32u5, peri_usb_otg_hs))]\n    let usb_refck_sel = match usb_refck {\n        Some(clk_val) => match clk_val {\n            Hertz(16_000_000) => Usbrefcksel::MHZ16,\n            Hertz(19_200_000) => Usbrefcksel::MHZ19_2,\n            Hertz(20_000_000) => Usbrefcksel::MHZ20,\n            Hertz(24_000_000) => Usbrefcksel::MHZ24,\n            Hertz(26_000_000) => Usbrefcksel::MHZ26,\n            Hertz(32_000_000) => Usbrefcksel::MHZ32,\n            _ => panic!(\n                \"cannot select OTG_HS reference clock with source frequency of {}, must be one of 16, 19.2, 20, 24, 26, 32 MHz\",\n                clk_val\n            ),\n        },\n        None => Usbrefcksel::MHZ24,\n    };\n    #[cfg(all(stm32u5, peri_usb_otg_hs))]\n    SYSCFG.otghsphycr().modify(|w| {\n        w.set_clksel(usb_refck_sel);\n    });\n\n    let lse = config.ls.lse.map(|l| l.frequency);\n    let lsi = config.ls.lsi.then_some(LSI_FREQ);\n\n    // Disable HSI if not used\n    if !config.hsi {\n        RCC.cr().modify(|w| w.set_hsion(false));\n    }\n\n    // Disable the HSI48, if not used\n    #[cfg(crs)]\n    if config.hsi48.is_none() {\n        super::disable_hsi48();\n    }\n\n    config.mux.init();\n\n    set_clocks!(\n        sys: Some(sys_clk),\n        hclk1: Some(hclk),\n        hclk2: Some(hclk),\n        hclk3: Some(hclk),\n        pclk1: Some(pclk1),\n        pclk2: Some(pclk2),\n        pclk3: Some(pclk3),\n        pclk1_tim: Some(pclk1_tim),\n        pclk2_tim: Some(pclk2_tim),\n        msik: msik,\n        hsi48: hsi48,\n        rtc: rtc,\n        lse: lse,\n        lsi: lsi,\n        hse: hse,\n        hsi: hsi,\n        pll1_p: pll1.p,\n        pll1_q: pll1.q,\n        pll1_r: pll1.r,\n        pll2_p: pll2.p,\n        pll2_q: pll2.q,\n        pll2_r: pll2.r,\n        pll3_p: pll3.p,\n        pll3_q: pll3.q,\n        pll3_r: pll3.r,\n\n        #[cfg(dsihost)]\n        dsi_phy: None, // DSI PLL clock not supported, don't call `RccPeripheral::frequency()` in the drivers\n\n        // TODO\n        audioclk: None,\n        shsi: None,\n    );\n}\n\nfn msirange_to_hertz(range: Msirange) -> Hertz {\n    match range {\n        Msirange::RANGE_48MHZ => Hertz(48_000_000),\n        Msirange::RANGE_24MHZ => Hertz(24_000_000),\n        Msirange::RANGE_16MHZ => Hertz(16_000_000),\n        Msirange::RANGE_12MHZ => Hertz(12_000_000),\n        Msirange::RANGE_4MHZ => Hertz(4_000_000),\n        Msirange::RANGE_2MHZ => Hertz(2_000_000),\n        Msirange::RANGE_1_33MHZ => Hertz(1_330_000),\n        Msirange::RANGE_1MHZ => Hertz(1_000_000),\n        Msirange::RANGE_3_072MHZ => Hertz(3_072_000),\n        Msirange::RANGE_1_536MHZ => Hertz(1_536_000),\n        Msirange::RANGE_1_024MHZ => Hertz(1_024_000),\n        Msirange::RANGE_768KHZ => Hertz(768_000),\n        Msirange::RANGE_400KHZ => Hertz(400_000),\n        Msirange::RANGE_200KHZ => Hertz(200_000),\n        Msirange::RANGE_133KHZ => Hertz(133_000),\n        Msirange::RANGE_100KHZ => Hertz(100_000),\n    }\n}\n\npub(super) struct PllInput {\n    pub hsi: Option<Hertz>,\n    pub hse: Option<Hertz>,\n    pub msi: Option<Hertz>,\n}\n\n#[allow(unused)]\n#[derive(Default)]\npub(super) struct PllOutput {\n    pub p: Option<Hertz>,\n    pub q: Option<Hertz>,\n    pub r: Option<Hertz>,\n}\n\n#[derive(PartialEq, Eq, Clone, Copy)]\nenum PllInstance {\n    Pll1 = 0,\n    Pll2 = 1,\n    Pll3 = 2,\n}\n\nfn pll_enable(instance: PllInstance, enabled: bool) {\n    RCC.cr().modify(|w| w.set_pllon(instance as _, enabled));\n    while RCC.cr().read().pllrdy(instance as _) != enabled {}\n}\n\nfn init_pll(instance: PllInstance, config: Option<Pll>, input: &PllInput, voltage_range: VoltageScale) -> PllOutput {\n    // Disable PLL\n    pll_enable(instance, false);\n\n    let Some(pll) = config else { return PllOutput::default() };\n\n    let src_freq = match pll.source {\n        PllSource::DISABLE => panic!(\"must not select PLL source as DISABLE\"),\n        PllSource::HSE => unwrap!(input.hse),\n        PllSource::HSI => unwrap!(input.hsi),\n        PllSource::MSIS => unwrap!(input.msi),\n    };\n\n    // Calculate the reference clock, which is the source divided by m\n    let ref_freq = src_freq / pll.prediv;\n    // Check limits per RM0456 § 11.4.6\n    assert!(Hertz::mhz(4) <= ref_freq && ref_freq <= Hertz::mhz(16));\n\n    // Check PLL clocks per RM0456 § 11.4.10\n    let (vco_min, vco_max, out_max) = match voltage_range {\n        VoltageScale::RANGE1 => (Hertz::mhz(128), Hertz::mhz(544), Hertz::mhz(208)),\n        VoltageScale::RANGE2 => (Hertz::mhz(128), Hertz::mhz(544), Hertz::mhz(110)),\n        VoltageScale::RANGE3 => (Hertz::mhz(128), Hertz::mhz(330), Hertz::mhz(55)),\n        VoltageScale::RANGE4 => panic!(\"PLL is unavailable in voltage range 4\"),\n    };\n\n    // Calculate the PLL VCO clock\n    let vco_freq = ref_freq * pll.mul;\n    assert!(vco_freq >= vco_min && vco_freq <= vco_max);\n\n    // Calculate output clocks.\n    let p = pll.divp.map(|div| vco_freq / div);\n    let q = pll.divq.map(|div| vco_freq / div);\n    let r = pll.divr.map(|div| vco_freq / div);\n    for freq in [p, q, r] {\n        if let Some(freq) = freq {\n            assert!(freq <= out_max);\n        }\n    }\n\n    let divr = match instance {\n        PllInstance::Pll1 => RCC.pll1divr(),\n        PllInstance::Pll2 => RCC.pll2divr(),\n        PllInstance::Pll3 => RCC.pll3divr(),\n    };\n    divr.write(|w| {\n        w.set_plln(pll.mul);\n        w.set_pllp(pll.divp.unwrap_or(PllDiv::DIV1));\n        w.set_pllq(pll.divq.unwrap_or(PllDiv::DIV1));\n        w.set_pllr(pll.divr.unwrap_or(PllDiv::DIV1));\n    });\n\n    let input_range = match ref_freq.0 {\n        ..=8_000_000 => Pllrge::FREQ_4TO8MHZ,\n        _ => Pllrge::FREQ_8TO16MHZ,\n    };\n\n    macro_rules! write_fields {\n        ($w:ident) => {\n            $w.set_pllpen(pll.divp.is_some());\n            $w.set_pllqen(pll.divq.is_some());\n            $w.set_pllren(pll.divr.is_some());\n            $w.set_pllm(pll.prediv);\n            $w.set_pllsrc(pll.source);\n            $w.set_pllrge(input_range);\n        };\n    }\n\n    match instance {\n        PllInstance::Pll1 => RCC.pll1cfgr().write(|w| {\n            // § 10.5.4: if we're targeting >= 55 MHz, we must configure PLL1MBOOST to a prescaler\n            // value that results in an output between 4 and 16 MHz for the PWR EPOD boost\n            if r.unwrap() >= Hertz::mhz(55) {\n                // source_clk can be up to 50 MHz, so there's just a few cases:\n                let mboost = match src_freq.0 {\n                    ..=16_000_000 => Pllmboost::DIV1, // Bypass, giving EPOD 4-16 MHz\n                    ..=32_000_000 => Pllmboost::DIV2, // Divide by 2, giving EPOD 8-16 MHz\n                    _ => Pllmboost::DIV4,             // Divide by 4, giving EPOD 8-12.5 MHz\n                };\n                w.set_pllmboost(mboost);\n            }\n            write_fields!(w);\n        }),\n        PllInstance::Pll2 => RCC.pll2cfgr().write(|w| {\n            write_fields!(w);\n        }),\n        PllInstance::Pll3 => RCC.pll3cfgr().write(|w| {\n            write_fields!(w);\n        }),\n    }\n\n    // Enable PLL\n    pll_enable(instance, true);\n\n    PllOutput { p, q, r }\n}\n\n/// Fraction structure for MSI auto-calibration\n/// Represents the multiplier as numerator/denominator that LSE frequency is multiplied by\n#[derive(Debug, Clone, Copy)]\nstruct MsiFraction {\n    numerator: u32,\n    denominator: u32,\n}\n\nimpl MsiFraction {\n    const fn new(numerator: u32, denominator: u32) -> Self {\n        Self { numerator, denominator }\n    }\n\n    /// Calculate the calibrated frequency given an LSE frequency\n    fn calculate_frequency(&self, lse_freq: Hertz) -> Hertz {\n        Hertz(lse_freq.0 * self.numerator / self.denominator)\n    }\n}\n\nfn get_msi_calibration_fraction(range: Msirange) -> MsiFraction {\n    // Exploiting the MSIx internals to make calculations compact\n    let denominator = (range as u32 & 0x03) + 1;\n    // Base multipliers are deduced from Table 82: MSI oscillator characteristics in data sheet\n    let numerator = [1465, 122, 94, 12][range as usize >> 2];\n\n    MsiFraction::new(numerator, denominator)\n}\n\n/// Calculate the calibrated MSI frequency for a given range and LSE frequency\nfn calculate_calibrated_msi_frequency(range: Msirange, lse_freq: Hertz) -> Hertz {\n    let fraction = get_msi_calibration_fraction(range);\n    fraction.calculate_frequency(lse_freq)\n}\n"
  },
  {
    "path": "embassy-stm32/src/rcc/wba.rs",
    "content": "pub use crate::pac::pwr::vals::Vos as VoltageScale;\nuse crate::pac::rcc::regs::Cfgr1;\n#[cfg(all(peri_usb_otg_hs))]\npub use crate::pac::rcc::vals::Otghssel;\nuse crate::pac::rcc::vals::Pllrge;\npub use crate::pac::rcc::vals::{\n    Hdiv5, Hpre as AHBPrescaler, Hpre5 as AHB5Prescaler, Hsepre as HsePrescaler, Plldiv as PllDiv, Pllm as PllPreDiv,\n    Plln as PllMul, Pllsrc as PllSource, Ppre as APBPrescaler, Sai1sel, Sw as Sysclk,\n};\nuse crate::pac::{FLASH, RCC};\n#[cfg(all(peri_usb_otg_hs))]\npub use crate::pac::{SYSCFG, syscfg::vals::Usbrefcksel};\nuse crate::rcc::LSI_FREQ;\nuse crate::time::Hertz;\n\n/// HSI speed\npub const HSI_FREQ: Hertz = Hertz(16_000_000);\n// HSE speed\npub const HSE_FREQ: Hertz = Hertz(32_000_000);\n\n#[derive(Clone, Copy, Eq, PartialEq)]\npub struct Hse {\n    pub prescaler: HsePrescaler,\n}\n\n#[derive(Clone, Copy)]\npub struct Pll {\n    /// The clock source for the PLL.\n    pub source: PllSource,\n    /// The PLL pre-divider.\n    ///\n    /// The clock speed of the `source` divided by `m` must be between 4 and 16 MHz.\n    pub prediv: PllPreDiv,\n    /// The PLL multiplier.\n    ///\n    /// The multiplied clock – `source` divided by `m` times `n` – must be between 128 and 544\n    /// MHz. The upper limit may be lower depending on the `Config { voltage_range }`.\n    pub mul: PllMul,\n    /// The divider for the P output.\n    ///\n    /// The P output is one of several options\n    /// that can be used to feed the SAI/MDF/ADF Clock mux's.\n    pub divp: Option<PllDiv>,\n    /// The divider for the Q output.\n    ///\n    /// The Q ouput is one of severals options that can be used to feed the 48MHz clocks\n    /// and the OCTOSPI clock. It may also be used on the MDF/ADF clock mux's.\n    pub divq: Option<PllDiv>,\n    /// The divider for the R output.\n    ///\n    /// When used to drive the system clock, `source` divided by `m` times `n` divided by `r`\n    /// must not exceed 160 MHz. System clocks above 55 MHz require a non-default\n    /// `Config { voltage_range }`.\n    pub divr: Option<PllDiv>,\n\n    pub frac: Option<u16>,\n}\n\n/// Clocks configuration\n#[derive(Clone, Copy)]\npub struct Config {\n    // base clock sources\n    pub hsi: bool,\n    pub hse: Option<Hse>,\n\n    // pll\n    pub pll1: Option<Pll>,\n\n    // sysclk, buses.\n    pub sys: Sysclk,\n    pub ahb_pre: AHBPrescaler,\n    pub ahb5_pre: AHB5Prescaler,\n    pub apb1_pre: APBPrescaler,\n    pub apb2_pre: APBPrescaler,\n    pub apb7_pre: APBPrescaler,\n\n    // low speed LSI/LSE/RTC\n    pub ls: super::LsConfig,\n\n    pub voltage_scale: VoltageScale,\n\n    /// Per-peripheral kernel clock selection muxes\n    pub mux: super::mux::ClockMux,\n}\n\nimpl Config {\n    pub const fn new() -> Self {\n        Config {\n            hsi: true,\n            hse: None,\n            pll1: None,\n            sys: Sysclk::HSI,\n            ahb_pre: AHBPrescaler::DIV1,\n            ahb5_pre: AHB5Prescaler::DIV1,\n            apb1_pre: APBPrescaler::DIV1,\n            apb2_pre: APBPrescaler::DIV1,\n            apb7_pre: APBPrescaler::DIV1,\n            ls: crate::rcc::LsConfig::new(),\n            // lsi2: crate::rcc::LsConfig::new(),\n            voltage_scale: VoltageScale::RANGE2,\n            mux: super::mux::ClockMux::default(),\n        }\n    }\n}\n\nimpl Default for Config {\n    fn default() -> Config {\n        Self::new()\n    }\n}\n\nfn hsi_enable() {\n    RCC.cr().modify(|w| w.set_hsion(true));\n    while !RCC.cr().read().hsirdy() {}\n}\n\npub(crate) unsafe fn init(config: Config) {\n    // Switch to HSI to prevent problems with PLL configuration.\n    if !RCC.cr().read().hsion() {\n        hsi_enable()\n    }\n    if RCC.cfgr1().read().sws() != Sysclk::HSI {\n        // Set HSI as a clock source, reset prescalers.\n        RCC.cfgr1().write_value(Cfgr1::default());\n        // Wait for clock switch status bits to change.\n        while RCC.cfgr1().read().sws() != Sysclk::HSI {}\n    }\n\n    // Set voltage scale\n    crate::pac::PWR.vosr().write(|w| w.set_vos(config.voltage_scale));\n    while !crate::pac::PWR.vosr().read().vosrdy() {}\n\n    let rtc = config.ls.init();\n\n    let hsi = config.hsi.then(|| {\n        hsi_enable();\n\n        HSI_FREQ\n    });\n\n    let hse = config.hse.map(|hse| {\n        RCC.cr().write(|w| {\n            w.set_hseon(true);\n            w.set_hsepre(hse.prescaler);\n        });\n        while !RCC.cr().read().hserdy() {}\n\n        HSE_FREQ\n    });\n\n    let pll_input = PllInput { hse, hsi };\n\n    let pll1 = config.pll1.map_or_else(\n        || {\n            pll_enable(false);\n            PllOutput::default()\n        },\n        |c| init_pll(Some(c), &pll_input, config.voltage_scale),\n    );\n\n    let sys_clk = match config.sys {\n        Sysclk::HSE => hse.unwrap(),\n        Sysclk::HSI => hsi.unwrap(),\n        Sysclk::_RESERVED_1 => unreachable!(),\n        Sysclk::PLL1_R => pll1.r.unwrap(),\n    };\n\n    assert!(sys_clk.0 <= 100_000_000);\n\n    let hclk1 = sys_clk / config.ahb_pre;\n    let hclk2 = hclk1;\n    let hclk4 = hclk1;\n    let (pclk1, pclk1_tim) = super::util::calc_pclk(hclk1, config.apb1_pre);\n    let (pclk2, pclk2_tim) = super::util::calc_pclk(hclk1, config.apb2_pre);\n    let (pclk7, _) = super::util::calc_pclk(hclk1, config.apb7_pre);\n\n    // Set flash wait states\n    let flash_latency = match config.voltage_scale {\n        VoltageScale::RANGE1 => match sys_clk.0 {\n            ..=32_000_000 => 0,\n            ..=64_000_000 => 1,\n            ..=96_000_000 => 2,\n            ..=100_000_000 => 3,\n            _ => 4,\n        },\n        VoltageScale::RANGE2 => match sys_clk.0 {\n            ..=8_000_000 => 0,\n            ..=16_000_000 => 1,\n            _ => 2,\n        },\n    };\n\n    FLASH.acr().modify(|w| w.set_latency(flash_latency));\n    while FLASH.acr().read().latency() != flash_latency {}\n\n    // Set sram wait states\n    let _sram_latency = match config.voltage_scale {\n        VoltageScale::RANGE1 => 0,\n        VoltageScale::RANGE2 => match sys_clk.0 {\n            ..=12_000_000 => 0,\n            ..=16_000_000 => 1,\n            _ => 2,\n        },\n    };\n    // TODO: Set the SRAM wait states\n\n    RCC.cfgr1().modify(|w| {\n        w.set_sw(config.sys);\n    });\n    while RCC.cfgr1().read().sws() != config.sys {}\n\n    RCC.cfgr2().modify(|w| {\n        w.set_hpre(config.ahb_pre);\n        w.set_ppre1(config.apb1_pre);\n        w.set_ppre2(config.apb2_pre);\n    });\n\n    // Set AHB5 prescaler depending on sysclk source\n    RCC.cfgr4().modify(|w| match config.sys {\n        // When using HSI or HSE, use HDIV5 bit (0 = div1, 1 = div2)\n        Sysclk::HSI | Sysclk::HSE => {\n            // Only Div1 and Div2 are valid for HDIV5, enforce this\n            match config.ahb5_pre {\n                AHB5Prescaler::DIV1 => w.set_hdiv5(Hdiv5::DIV1),\n                AHB5Prescaler::DIV2 => w.set_hdiv5(Hdiv5::DIV2),\n                _ => panic!(\"Invalid ahb5_pre for HSI/HSE sysclk: only DIV1 and DIV2 are allowed\"),\n            };\n        }\n        // When using PLL1, use HPRE5 bits [2:0]\n        Sysclk::PLL1_R => {\n            w.set_hpre5(config.ahb5_pre);\n        }\n        _ => {}\n    });\n\n    let hclk5 = sys_clk / config.ahb5_pre;\n\n    #[cfg(all(stm32wba, peri_usb_otg_hs))]\n    let usb_refck = match config.mux.otghssel {\n        Otghssel::HSE => hse,\n        Otghssel::HSE_DIV_2 => hse.map(|hse_val| hse_val / 2u8),\n        Otghssel::PLL1_P => pll1.p,\n        Otghssel::PLL1_P_DIV_2 => pll1.p.map(|pll1p_val| pll1p_val / 2u8),\n    };\n    #[cfg(all(stm32wba, peri_usb_otg_hs))]\n    let usb_refck_sel = match usb_refck {\n        Some(clk_val) => match clk_val {\n            Hertz(16_000_000) => Usbrefcksel::MHZ16,\n            Hertz(19_200_000) => Usbrefcksel::MHZ19_2,\n            Hertz(20_000_000) => Usbrefcksel::MHZ20,\n            Hertz(24_000_000) => Usbrefcksel::MHZ24,\n            Hertz(26_000_000) => Usbrefcksel::MHZ26,\n            Hertz(32_000_000) => Usbrefcksel::MHZ32,\n            _ => panic!(\n                \"cannot select OTG_HS reference clock with source frequency of {}, must be one of 16, 19.2, 20, 24, 26, 32 MHz\",\n                clk_val\n            ),\n        },\n        None => Usbrefcksel::MHZ24,\n    };\n    #[cfg(all(stm32wba, peri_usb_otg_hs))]\n    SYSCFG.otghsphycr().modify(|w| {\n        w.set_clksel(usb_refck_sel);\n    });\n\n    #[cfg(sai_v4_2pdm)]\n    let audioclk = match config.mux.sai1sel {\n        Sai1sel::HSI => Some(HSI_FREQ),\n        Sai1sel::PLL1_Q => Some(pll1.q.expect(\"PLL1.Q not configured\")),\n        Sai1sel::PLL1_P => Some(pll1.p.expect(\"PLL1.P not configured\")),\n        Sai1sel::SYS => panic!(\"SYS not supported yet\"),\n        Sai1sel::AUDIOCLK => panic!(\"AUDIOCLK not supported yet\"),\n        _ => None,\n    };\n\n    let lsi = config.ls.lsi.then_some(LSI_FREQ);\n\n    // Disable HSI if not used\n    if !config.hsi {\n        RCC.cr().modify(|w| w.set_hsion(false));\n    }\n\n    config.mux.init();\n\n    set_clocks!(\n        sys: Some(sys_clk),\n        hclk1: Some(hclk1),\n        hclk2: Some(hclk2),\n        hclk4: Some(hclk4),\n        hclk5: Some(hclk5),\n        pclk1: Some(pclk1),\n        pclk2: Some(pclk2),\n        pclk7: Some(pclk7),\n        pclk1_tim: Some(pclk1_tim),\n        pclk2_tim: Some(pclk2_tim),\n        rtc: rtc,\n        hse: hse,\n        lsi: lsi,\n        hsi: hsi,\n        pll1_p: pll1.p,\n        pll1_q: pll1.q,\n        pll1_r: pll1.r,\n\n        // TODO\n        lse: None,\n        #[cfg(sai_v4_2pdm)]\n        audioclk: audioclk,\n    );\n}\n\npub(super) struct PllInput {\n    pub hsi: Option<Hertz>,\n    pub hse: Option<Hertz>,\n}\n\n#[allow(unused)]\n#[derive(Default)]\npub(super) struct PllOutput {\n    pub p: Option<Hertz>,\n    pub q: Option<Hertz>,\n    pub r: Option<Hertz>,\n}\n\nfn pll_enable(enabled: bool) {\n    RCC.cr().modify(|w| w.set_pllon(enabled));\n    while RCC.cr().read().pllrdy() != enabled {}\n}\n\nfn init_pll(config: Option<Pll>, input: &PllInput, voltage_range: VoltageScale) -> PllOutput {\n    // Disable PLL\n    pll_enable(false);\n\n    let Some(pll) = config else { return PllOutput::default() };\n\n    let pre_src_freq = match pll.source {\n        PllSource::DISABLE => panic!(\"must not select PLL source as DISABLE\"),\n        PllSource::HSE => unwrap!(input.hse),\n        PllSource::HSI => unwrap!(input.hsi),\n        PllSource::_RESERVED_1 => panic!(\"must not select RESERVED_1 source as DISABLE\"),\n    };\n\n    // Only divide by the HSE prescaler when the PLL source is HSE\n    let src_freq = match pll.source {\n        PllSource::HSE => {\n            // read the prescaler bits and divide\n            let hsepre = RCC.cr().read().hsepre();\n            pre_src_freq / hsepre\n        }\n        _ => pre_src_freq,\n    };\n\n    // Calculate the reference clock, which is the source divided by m\n    let ref_freq = src_freq / pll.prediv;\n    // Check limits per RM0515 § 12.4.3\n    assert!(Hertz::mhz(4) <= ref_freq && ref_freq <= Hertz::mhz(16));\n\n    // Check PLL clocks per RM0515 § 12.4.5\n    let (vco_min, vco_max, out_max) = match voltage_range {\n        VoltageScale::RANGE1 => (Hertz::mhz(128), Hertz::mhz(544), Hertz::mhz(100)),\n        VoltageScale::RANGE2 => panic!(\"PLL is unavailable in voltage range 2\"),\n    };\n\n    // Calculate the PLL VCO clock\n    // let vco_freq = ref_freq * pll.mul;\n    // Calculate VCO frequency including fractional part: FVCO = Fref_ck × (N + FRAC/2^13)\n    let numerator = (ref_freq.0 as u64) * (((pll.mul as u64) + 1 << 13) + pll.frac.unwrap_or(0) as u64);\n    let vco_hz = (numerator >> 13) as u32;\n    let vco_freq = Hertz(vco_hz);\n    assert!(vco_freq >= vco_min && vco_freq <= vco_max);\n\n    // Calculate output clocks.\n    let p = pll.divp.map(|div| vco_freq / div);\n    let q = pll.divq.map(|div| vco_freq / div);\n    let r = pll.divr.map(|div| vco_freq / div);\n    for freq in [p, q, r] {\n        if let Some(freq) = freq {\n            assert!(freq <= out_max);\n        }\n    }\n\n    let divr = RCC.pll1divr();\n    divr.write(|w| {\n        w.set_plln(pll.mul);\n        w.set_pllp(pll.divp.unwrap_or(PllDiv::DIV1));\n        w.set_pllq(pll.divq.unwrap_or(PllDiv::DIV1));\n        w.set_pllr(pll.divr.unwrap_or(PllDiv::DIV1));\n    });\n    RCC.pll1fracr().write(|w| {\n        w.set_pllfracn(pll.frac.unwrap_or(0));\n    });\n\n    let input_range = match ref_freq.0 {\n        ..=8_000_000 => Pllrge::FREQ_4TO8MHZ,\n        _ => Pllrge::FREQ_8TO16MHZ,\n    };\n\n    macro_rules! write_fields {\n        ($w:ident) => {\n            $w.set_pllpen(pll.divp.is_some());\n            $w.set_pllqen(pll.divq.is_some());\n            $w.set_pllren(pll.divr.is_some());\n            $w.set_pllfracen(pll.frac.is_some());\n            $w.set_pllm(pll.prediv);\n            $w.set_pllsrc(pll.source);\n            $w.set_pllrge(input_range);\n        };\n    }\n\n    RCC.pll1cfgr().write(|w| {\n        write_fields!(w);\n    });\n\n    // Enable PLL\n    pll_enable(true);\n\n    PllOutput { p, q, r }\n}\n"
  },
  {
    "path": "embassy-stm32/src/rng.rs",
    "content": "//! Random Number Generator (RNG)\n#![macro_use]\n\nuse core::future::poll_fn;\nuse core::marker::PhantomData;\nuse core::task::Poll;\n\nuse embassy_hal_internal::PeripheralType;\nuse embassy_sync::waitqueue::AtomicWaker;\n\nuse crate::interrupt::typelevel::Interrupt;\nuse crate::{Peri, interrupt, pac, peripherals, rcc};\n\nstatic RNG_WAKER: AtomicWaker = AtomicWaker::new();\n\n/// WBA-specific health test configuration values for RNG\n#[derive(Clone, Copy)]\n#[allow(dead_code)]\nenum Htcfg {\n    /// WBA-specific health test configuration (0x0000AAC7)\n    /// Corresponds to configuration A, B, and C thresholds as recommended in the reference manual\n    WbaRecommended = 0x0000_AAC7,\n}\n\nimpl Htcfg {\n    /// Convert to the raw u32 value for register access\n    #[allow(dead_code)]\n    fn value(self) -> u32 {\n        self as u32\n    }\n}\n\n/// RNG error\n#[derive(Debug, PartialEq, Eq, Clone, Copy)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub enum Error {\n    /// Seed error.\n    SeedError,\n    /// Clock error. Double-check the RCC configuration,\n    /// see the Reference Manual for details on restrictions\n    /// on RNG clocks.\n    ClockError,\n}\n\n/// RNG interrupt handler.\npub struct InterruptHandler<T: Instance> {\n    _phantom: PhantomData<T>,\n}\n\nimpl<T: Instance> interrupt::typelevel::Handler<T::Interrupt> for InterruptHandler<T> {\n    unsafe fn on_interrupt() {\n        let bits = T::regs().sr().read();\n        if bits.drdy() || bits.seis() || bits.ceis() {\n            T::regs().cr().modify(|reg| reg.set_ie(false));\n            RNG_WAKER.wake();\n        }\n    }\n}\n\n/// RNG driver.\npub struct Rng<'d, T: Instance> {\n    _inner: Peri<'d, T>,\n}\n\nimpl<'d, T: Instance> Rng<'d, T> {\n    /// Create a new RNG driver.\n    pub fn new(\n        inner: Peri<'d, T>,\n        _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd,\n    ) -> Self {\n        rcc::enable_and_reset::<T>();\n\n        // Verify clock is available\n        T::frequency();\n\n        let mut random = Self { _inner: inner };\n        random.reset();\n\n        T::Interrupt::unpend();\n        unsafe { T::Interrupt::enable() };\n\n        random\n    }\n\n    /// Reset the RNG.\n    #[cfg(rng_v1)]\n    pub fn reset(&mut self) {\n        T::regs().cr().write(|reg| {\n            reg.set_rngen(false);\n        });\n        T::regs().sr().modify(|reg| {\n            reg.set_seis(false);\n            reg.set_ceis(false);\n        });\n        T::regs().cr().modify(|reg| {\n            reg.set_rngen(true);\n        });\n        // Reference manual says to discard the first.\n        let _ = self.next_u32();\n    }\n\n    /// Reset the RNG.\n    #[cfg(not(rng_v1))]\n    pub fn reset(&mut self) {\n        T::regs().cr().write(|reg| {\n            reg.set_condrst(true);\n            reg.set_nistc(pac::rng::vals::Nistc::CUSTOM);\n            // set RNG config \"A\" according to reference manual\n            // this has to be written within the same write access as setting the CONDRST bit\n            reg.set_rng_config1(pac::rng::vals::RngConfig1::CONFIG_A);\n            reg.set_clkdiv(pac::rng::vals::Clkdiv::NO_DIV);\n            reg.set_rng_config2(pac::rng::vals::RngConfig2::CONFIG_A_B);\n            reg.set_rng_config3(pac::rng::vals::RngConfig3::CONFIG_A);\n            reg.set_ced(true);\n            reg.set_ie(false);\n            reg.set_rngen(true);\n        });\n        T::regs().cr().modify(|reg| {\n            reg.set_ced(false);\n        });\n        // wait for CONDRST to be set\n        while !T::regs().cr().read().condrst() {}\n\n        // Set health test configuration values\n        #[cfg(not(rng_wba6))]\n        {\n            // magic number must be written immediately before every read or write access to HTCR\n            T::regs().htcr().write(|w| w.set_htcfg(pac::rng::vals::Htcfg::MAGIC));\n            // write recommended value according to reference manual\n            // note: HTCR can only be written during conditioning\n            T::regs()\n                .htcr()\n                .write(|w| w.set_htcfg(pac::rng::vals::Htcfg::RECOMMENDED));\n        }\n        #[cfg(rng_wba6)]\n        {\n            // For WBA6, set RNG_HTCR0 to the recommended value for configurations A, B, and C\n            // This value corresponds to the health test thresholds specified in the reference manual\n            T::regs().htcr(0).write(|w| w.0 = Htcfg::WbaRecommended.value());\n        }\n\n        // finish conditioning\n        T::regs().cr().modify(|reg| {\n            reg.set_rngen(true);\n            reg.set_condrst(false);\n        });\n\n        // According to reference manual for RNGv3: SEIS must be cleared manually.\n        // RNGv2 does not say anything about SEIS clearing, but ST Cube HAL clears it.\n        T::regs().sr().modify(|reg| {\n            reg.set_seis(false);\n        });\n\n        // According to reference manual: after software reset, wait for random number to be ready\n        // The next_u32() call will wait for DRDY, completing the initialization\n        let _ = self.next_u32();\n    }\n\n    /// Try to recover from a seed error.\n    pub fn recover_seed_error(&mut self) {\n        self.reset();\n        // reset should also clear the SEIS flag\n        if T::regs().sr().read().seis() {\n            warn!(\"recovering from seed error failed\");\n            return;\n        }\n        // wait for SECS to be cleared by RNG\n        while T::regs().sr().read().secs() {}\n    }\n\n    /// Fill the given slice with random values.\n    pub async fn async_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), Error> {\n        for chunk in dest.chunks_mut(4) {\n            let mut bits = T::regs().sr().read();\n            if !bits.seis() && !bits.ceis() && !bits.drdy() {\n                // wait for interrupt\n                poll_fn(|cx| {\n                    // quick check to avoid registration if already done.\n                    let bits = T::regs().sr().read();\n                    if bits.drdy() || bits.seis() || bits.ceis() {\n                        return Poll::Ready(());\n                    }\n                    RNG_WAKER.register(cx.waker());\n                    T::regs().cr().modify(|reg| reg.set_ie(true));\n                    // Need to check condition **after** `register` to avoid a race\n                    // condition that would result in lost notifications.\n                    let bits = T::regs().sr().read();\n                    if bits.drdy() || bits.seis() || bits.ceis() {\n                        Poll::Ready(())\n                    } else {\n                        Poll::Pending\n                    }\n                })\n                .await;\n\n                // Re-read the status register after wait.\n                bits = T::regs().sr().read()\n            }\n            if bits.seis() {\n                // in case of noise-source or seed error we try to recover here\n                // but we must not use the data in DR and we return an error\n                // to leave retry-logic to the application\n                self.recover_seed_error();\n                return Err(Error::SeedError);\n            } else if bits.ceis() {\n                // clock error detected, DR could still be used but keep it safe,\n                // clear the error and abort\n                T::regs().sr().modify(|sr| sr.set_ceis(false));\n                return Err(Error::ClockError);\n            } else if bits.drdy() {\n                // DR can be read up to four times until the output buffer is empty\n                // DRDY is cleared automatically when that happens\n                let random_word = T::regs().dr().read();\n                // reference manual: always check if DR is zero\n                if random_word == 0 {\n                    return Err(Error::SeedError);\n                }\n                // write bytes to chunk\n                for (dest, src) in chunk.iter_mut().zip(random_word.to_ne_bytes().iter()) {\n                    *dest = *src\n                }\n            }\n        }\n\n        Ok(())\n    }\n\n    /// Get a random u32\n    pub fn next_u32(&mut self) -> u32 {\n        loop {\n            let sr = T::regs().sr().read();\n            if sr.seis() | sr.ceis() {\n                self.reset();\n            } else if sr.drdy() {\n                return T::regs().dr().read();\n            }\n        }\n    }\n\n    /// Get a random u64\n    pub fn next_u64(&mut self) -> u64 {\n        let mut rand = self.next_u32() as u64;\n        rand |= (self.next_u32() as u64) << 32;\n        rand\n    }\n\n    /// Fill a slice with random bytes\n    pub fn fill_bytes(&mut self, dest: &mut [u8]) {\n        for chunk in dest.chunks_mut(4) {\n            let rand = self.next_u32();\n            for (slot, num) in chunk.iter_mut().zip(rand.to_ne_bytes().iter()) {\n                *slot = *num\n            }\n        }\n    }\n}\n\nimpl<'d, T: Instance> Drop for Rng<'d, T> {\n    fn drop(&mut self) {\n        T::regs().cr().modify(|reg| {\n            reg.set_rngen(false);\n        });\n        rcc::disable::<T>();\n    }\n}\n\nimpl<'d, T: Instance> rand_core_06::RngCore for Rng<'d, T> {\n    fn next_u32(&mut self) -> u32 {\n        self.next_u32()\n    }\n\n    fn next_u64(&mut self) -> u64 {\n        self.next_u64()\n    }\n\n    fn fill_bytes(&mut self, dest: &mut [u8]) {\n        self.fill_bytes(dest);\n    }\n\n    fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), rand_core_06::Error> {\n        self.fill_bytes(dest);\n        Ok(())\n    }\n}\n\nimpl<'d, T: Instance> rand_core_06::CryptoRng for Rng<'d, T> {}\n\nimpl<'d, T: Instance> rand_core_09::RngCore for Rng<'d, T> {\n    fn next_u32(&mut self) -> u32 {\n        self.next_u32()\n    }\n\n    fn next_u64(&mut self) -> u64 {\n        self.next_u64()\n    }\n\n    fn fill_bytes(&mut self, dest: &mut [u8]) {\n        self.fill_bytes(dest);\n    }\n}\n\nimpl<'d, T: Instance> rand_core_09::CryptoRng for Rng<'d, T> {}\n\ntrait SealedInstance {\n    fn regs() -> pac::rng::Rng;\n}\n\n/// RNG instance trait.\n#[allow(private_bounds)]\npub trait Instance: SealedInstance + PeripheralType + crate::rcc::RccPeripheral + 'static + Send {\n    /// Interrupt for this RNG instance.\n    type Interrupt: interrupt::typelevel::Interrupt;\n}\n\nforeach_interrupt!(\n    ($inst:ident, rng, RNG, GLOBAL, $irq:ident) => {\n        impl Instance for peripherals::$inst {\n            type Interrupt = crate::interrupt::typelevel::$irq;\n        }\n\n        impl SealedInstance for peripherals::$inst {\n            fn regs() -> crate::pac::rng::Rng {\n                crate::pac::$inst\n            }\n        }\n    };\n);\n"
  },
  {
    "path": "embassy-stm32/src/rtc/datetime.rs",
    "content": "#[cfg(feature = \"chrono\")]\nuse chrono::{Datelike, NaiveDate, Timelike, Weekday};\n\n/// Errors regarding the [`DateTime`] struct.\n#[derive(Clone, Debug, PartialEq, Eq)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub enum Error {\n    /// The [DateTime] contains an invalid year value. Must be between `0..=4095`.\n    InvalidYear,\n    /// The [DateTime] contains an invalid month value. Must be between `1..=12`.\n    InvalidMonth,\n    /// The [DateTime] contains an invalid day value. Must be between `1..=31`.\n    InvalidDay,\n    /// The [DateTime] contains an invalid day of week. Must be between `0..=6` where 0 is Sunday.\n    InvalidDayOfWeek(\n        /// The value of the DayOfWeek that was given.\n        u8,\n    ),\n    /// The [DateTime] contains an invalid hour value. Must be between `0..=23`.\n    InvalidHour,\n    /// The [DateTime] contains an invalid minute value. Must be between `0..=59`.\n    InvalidMinute,\n    /// The [DateTime] contains an invalid second value. Must be between `0..=59`.\n    InvalidSecond,\n    /// The [DateTime] contains an invalid microsecond value. Must be between `0..=999_999`.\n    InvalidMicrosecond,\n}\n\n/// Structure containing date and time information\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub struct DateTime {\n    /// 0..4095\n    year: u16,\n    /// 1..12, 1 is January\n    month: u8,\n    /// 1..28,29,30,31 depending on month\n    day: u8,\n    ///\n    day_of_week: DayOfWeek,\n    /// 0..23\n    hour: u8,\n    /// 0..59\n    minute: u8,\n    /// 0..59\n    second: u8,\n    /// 0..999_999\n    usecond: u32,\n}\n\nimpl DateTime {\n    /// Get the year (0..=4095)\n    pub const fn year(&self) -> u16 {\n        self.year\n    }\n\n    /// Get the month (1..=12, 1 is January)\n    pub const fn month(&self) -> u8 {\n        self.month\n    }\n\n    /// Get the day (1..=31)\n    pub const fn day(&self) -> u8 {\n        self.day\n    }\n\n    /// Get the day of week\n    pub const fn day_of_week(&self) -> DayOfWeek {\n        self.day_of_week\n    }\n\n    /// Get the hour (0..=23)\n    pub const fn hour(&self) -> u8 {\n        self.hour\n    }\n\n    /// Get the minute (0..=59)\n    pub const fn minute(&self) -> u8 {\n        self.minute\n    }\n\n    /// Get the second (0..=59)\n    pub const fn second(&self) -> u8 {\n        self.second\n    }\n\n    /// Get the microsecond (0..=999_999)\n    pub const fn microsecond(&self) -> u32 {\n        self.usecond\n    }\n\n    /// Create a new DateTime with the given information.\n    pub fn from(\n        year: u16,\n        month: u8,\n        day: u8,\n        day_of_week: DayOfWeek,\n        hour: u8,\n        minute: u8,\n        second: u8,\n        usecond: u32,\n    ) -> Result<Self, Error> {\n        if year > 4095 {\n            Err(Error::InvalidYear)\n        } else if !(1..=12).contains(&month) {\n            Err(Error::InvalidMonth)\n        } else if !(1..=31).contains(&day) {\n            Err(Error::InvalidDay)\n        } else if hour > 23 {\n            Err(Error::InvalidHour)\n        } else if minute > 59 {\n            Err(Error::InvalidMinute)\n        } else if second > 59 {\n            Err(Error::InvalidSecond)\n        } else if usecond > 999_999 {\n            Err(Error::InvalidMicrosecond)\n        } else {\n            Ok(Self {\n                year,\n                month,\n                day,\n                day_of_week,\n                hour,\n                minute,\n                second,\n                usecond,\n            })\n        }\n    }\n}\n\n#[cfg(feature = \"chrono\")]\nimpl From<chrono::NaiveDateTime> for DateTime {\n    fn from(date_time: chrono::NaiveDateTime) -> Self {\n        Self {\n            year: date_time.year() as u16,\n            month: date_time.month() as u8,\n            day: date_time.day() as u8,\n            day_of_week: date_time.weekday().into(),\n            hour: date_time.hour() as u8,\n            minute: date_time.minute() as u8,\n            second: date_time.second() as u8,\n            usecond: date_time.and_utc().timestamp_subsec_micros(),\n        }\n    }\n}\n\n#[cfg(feature = \"chrono\")]\nimpl From<DateTime> for chrono::NaiveDateTime {\n    fn from(date_time: DateTime) -> Self {\n        NaiveDate::from_ymd_opt(date_time.year as i32, date_time.month as u32, date_time.day as u32)\n            .unwrap()\n            .and_hms_micro_opt(\n                date_time.hour as u32,\n                date_time.minute as u32,\n                date_time.second as u32,\n                date_time.usecond,\n            )\n            .unwrap()\n    }\n}\n\n/// A day of the week\n#[repr(u8)]\n#[derive(Copy, Clone, Debug, PartialEq, Eq, Ord, PartialOrd, Hash)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\n#[allow(missing_docs)]\npub enum DayOfWeek {\n    Monday = 1,\n    Tuesday = 2,\n    Wednesday = 3,\n    Thursday = 4,\n    Friday = 5,\n    Saturday = 6,\n    Sunday = 7,\n}\n\n#[cfg(feature = \"chrono\")]\nimpl From<chrono::Weekday> for DayOfWeek {\n    fn from(weekday: Weekday) -> Self {\n        day_of_week_from_u8(weekday.number_from_monday() as u8).unwrap()\n    }\n}\n\n#[cfg(feature = \"chrono\")]\nimpl From<DayOfWeek> for chrono::Weekday {\n    fn from(weekday: DayOfWeek) -> Self {\n        match weekday {\n            DayOfWeek::Monday => Weekday::Mon,\n            DayOfWeek::Tuesday => Weekday::Tue,\n            DayOfWeek::Wednesday => Weekday::Wed,\n            DayOfWeek::Thursday => Weekday::Thu,\n            DayOfWeek::Friday => Weekday::Fri,\n            DayOfWeek::Saturday => Weekday::Sat,\n            DayOfWeek::Sunday => Weekday::Sun,\n        }\n    }\n}\n\npub(super) const fn day_of_week_from_u8(v: u8) -> Result<DayOfWeek, Error> {\n    Ok(match v {\n        1 => DayOfWeek::Monday,\n        2 => DayOfWeek::Tuesday,\n        3 => DayOfWeek::Wednesday,\n        4 => DayOfWeek::Thursday,\n        5 => DayOfWeek::Friday,\n        6 => DayOfWeek::Saturday,\n        7 => DayOfWeek::Sunday,\n        x => return Err(Error::InvalidDayOfWeek(x)),\n    })\n}\n\npub(super) const fn day_of_week_to_u8(dotw: DayOfWeek) -> u8 {\n    dotw as u8\n}\n"
  },
  {
    "path": "embassy-stm32/src/rtc/low_power.rs",
    "content": "use chrono::{DateTime, NaiveDateTime, TimeDelta, Utc};\nuse embassy_time::{Duration, Instant, TICK_HZ};\n\nuse super::Rtc;\nuse crate::interrupt::typelevel::Interrupt;\nuse crate::pac::rtc::vals::Wucksel;\nuse crate::peripherals::RTC;\nuse crate::rtc::{RtcTimeProvider, SealedInstance};\n\nfn wucksel_compute_min(val: u32) -> (Wucksel, u32) {\n    *[\n        (Wucksel::DIV2, 2),\n        (Wucksel::DIV4, 4),\n        (Wucksel::DIV8, 8),\n        (Wucksel::DIV16, 16),\n    ]\n    .iter()\n    .find(|(_, psc)| *psc as u32 > val)\n    .unwrap_or(&(Wucksel::DIV16, 16))\n}\n\nimpl Rtc {\n    pub(super) fn calc_epoch(&self) -> DateTime<Utc> {\n        let now: NaiveDateTime = RtcTimeProvider::new().now().unwrap().into();\n\n        now.and_utc() - TimeDelta::microseconds(Instant::now().as_micros().try_into().unwrap())\n    }\n\n    /// start the wakeup alarm and with a duration that is as close to but less than\n    /// the requested duration, and record the instant the wakeup alarm was started\n    pub(crate) fn start_wakeup_alarm(&mut self, requested_duration: embassy_time::Duration) {\n        // Panic if the rcc mod knows we're not using low-power rtc\n        #[cfg(any(rcc_wb, rcc_f4, rcc_f410))]\n        unsafe { crate::rcc::get_freqs() }.rtc.to_hertz().unwrap();\n\n        let requested_duration = requested_duration.as_ticks().clamp(0, u32::MAX as u64);\n        let rtc_hz = Self::frequency().0 as u64;\n        let rtc_ticks = requested_duration * rtc_hz / TICK_HZ;\n        let (wucksel, prescaler) = wucksel_compute_min((rtc_ticks / u16::MAX as u64) as u32);\n\n        // adjust the rtc ticks to the prescaler and subtract one rtc tick\n        let rtc_ticks = rtc_ticks / prescaler as u64;\n        let rtc_ticks = rtc_ticks.clamp(0, (u16::MAX - 1) as u64).saturating_sub(1) as u16;\n\n        self.write(false, |regs| {\n            regs.cr().modify(|w| w.set_wute(false));\n\n            #[cfg(rtc_v2)]\n            {\n                regs.isr().modify(|w| w.set_wutf(false));\n                while !regs.isr().read().wutwf() {}\n            }\n\n            #[cfg(rtc_v3)]\n            {\n                regs.scr().write(|w| w.set_cwutf(crate::pac::rtc::vals::Calrf::CLEAR));\n                while !regs.icsr().read().wutwf() {}\n            }\n\n            regs.cr().modify(|w| w.set_wucksel(wucksel));\n            regs.wutr().write(|w| w.set_wut(rtc_ticks));\n            regs.cr().modify(|w| w.set_wute(true));\n            regs.cr().modify(|w| w.set_wutie(true));\n        });\n\n        trace!(\n            \"rtc: start wakeup alarm for {} ms (psc: {}, ticks: {})\",\n            Duration::from_ticks(rtc_ticks as u64 * TICK_HZ * prescaler as u64 / rtc_hz).as_millis(),\n            prescaler as u32,\n            rtc_ticks,\n        );\n    }\n\n    /// stop the wakeup alarm and return the time elapsed since `start_wakeup_alarm`\n    /// was called, otherwise none\n    pub(crate) fn stop_wakeup_alarm(&mut self) -> embassy_time::Instant {\n        if RTC::regs().cr().read().wute() {\n            trace!(\"rtc: stop wakeup alarm\");\n\n            self.write(false, |regs| {\n                regs.cr().modify(|w| w.set_wutie(false));\n                regs.cr().modify(|w| w.set_wute(false));\n\n                #[cfg(rtc_v2)]\n                regs.isr().modify(|w| w.set_wutf(false));\n                #[cfg(rtc_v3)]\n                regs.scr().write(|w| w.set_cwutf(crate::pac::rtc::vals::Calrf::CLEAR));\n\n                // Check RM for EXTI and/or NVIC section, \"Event event input mapping\" or \"EXTI interrupt/event mapping\" or something similar,\n                // there is a table for every \"Event input\" / \"EXTI Line\".\n                // If you find the EXTI line related to \"RTC wakeup\" marks as \"Configurable\" (not \"Direct\"),\n                // then write 1 to related field of Pending Register, to clean it's pending state.\n                #[cfg(any(exti_v1, stm32h7, stm32wb))]\n                crate::pac::EXTI\n                    .pr(0)\n                    .modify(|w| w.set_line(RTC::EXTI_WAKEUP_LINE, true));\n\n                <RTC as crate::rtc::SealedInstance>::WakeupInterrupt::unpend();\n            });\n        }\n\n        let datetime: NaiveDateTime = RtcTimeProvider::new().now().expect(\"failed to read now\").into();\n        let offset = datetime.and_utc() - self.epoch;\n\n        Instant::from_micros(offset.num_microseconds().unwrap().try_into().unwrap())\n    }\n\n    pub(super) fn enable_wakeup_line(&mut self) {\n        <RTC as crate::rtc::SealedInstance>::WakeupInterrupt::unpend();\n        unsafe { <RTC as crate::rtc::SealedInstance>::WakeupInterrupt::enable() };\n\n        #[cfg(not(any(stm32u5, stm32u3, stm32u0, stm32wba)))]\n        {\n            use crate::pac::EXTI;\n            EXTI.rtsr(0).modify(|w| w.set_line(RTC::EXTI_WAKEUP_LINE, true));\n\n            #[cfg(not(any(stm32wb, stm32wl5x)))]\n            {\n                EXTI.imr(0).modify(|w| w.set_line(RTC::EXTI_WAKEUP_LINE, true));\n            }\n            #[cfg(any(stm32wb, stm32wl5x))]\n            {\n                EXTI.cpu(0).imr(0).modify(|w| w.set_line(RTC::EXTI_WAKEUP_LINE, true));\n            }\n        }\n        #[cfg(stm32u5)]\n        {\n            use crate::pac::RCC;\n            RCC.srdamr().modify(|w| w.set_rtcapbamen(true));\n            RCC.apb3smenr().modify(|w| w.set_rtcapbsmen(true));\n        }\n        #[cfg(stm32wba)]\n        {\n            use crate::pac::RCC;\n            // RCC.srdamr().modify(|w| w.set_rtcapbamen(true));\n            RCC.apb7smenr().modify(|w| w.set_rtcapbsmen(true));\n        }\n    }\n}\n"
  },
  {
    "path": "embassy-stm32/src/rtc/mod.rs",
    "content": "//! Real Time Clock (RTC)\nmod datetime;\n\n#[cfg(all(feature = \"low-power\", not(feature = \"_lp-time-driver\")))]\nmod low_power;\n\n#[cfg(all(feature = \"low-power\", not(feature = \"_lp-time-driver\")))]\nuse core::cell::{RefCell, RefMut};\n#[cfg(all(feature = \"low-power\", not(feature = \"_lp-time-driver\")))]\nuse core::ops;\n\n#[cfg(all(feature = \"low-power\", not(feature = \"_lp-time-driver\")))]\nuse critical_section::CriticalSection;\n#[cfg(all(feature = \"low-power\", not(feature = \"_lp-time-driver\")))]\nuse embassy_sync::blocking_mutex::Mutex;\n#[cfg(all(feature = \"low-power\", not(feature = \"_lp-time-driver\")))]\nuse embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;\n\npub use self::datetime::{DateTime, DayOfWeek, Error as DateTimeError};\nuse self::datetime::{day_of_week_from_u8, day_of_week_to_u8};\nuse crate::pac::rtc::regs::{Dr, Tr};\nuse crate::time::Hertz;\n\n/// refer to AN4759 to compare features of RTC2 and RTC3\n#[cfg_attr(rtc_v1, path = \"v1.rs\")]\n#[cfg_attr(rtc_v2, path = \"v2.rs\")]\n#[cfg_attr(rtc_v3, path = \"v3.rs\")]\nmod _version;\n#[allow(unused_imports)]\npub use _version::*;\n\nuse crate::Peri;\nuse crate::peripherals::RTC;\n\n/// Errors that can occur on methods on [Rtc]\n#[non_exhaustive]\n#[derive(Clone, Debug, PartialEq, Eq)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub enum RtcError {\n    /// An invalid DateTime was given or stored on the hardware.\n    InvalidDateTime(DateTimeError),\n\n    /// The current time could not be read\n    ReadFailure,\n\n    /// The RTC clock is not running\n    NotRunning,\n}\n\n/// Provides immutable access to the current time of the RTC.\n#[derive(Clone)]\npub struct RtcTimeProvider {\n    _private: (),\n}\n\nimpl RtcTimeProvider {\n    /// Create a new RTC time provider instance.\n    pub(self) const fn new() -> Self {\n        Self { _private: () }\n    }\n\n    /// Return the current datetime.\n    ///\n    /// # Errors\n    ///\n    /// Will return an `RtcError::InvalidDateTime` if the stored value in the system is not a valid [`DayOfWeek`].\n    pub fn now(&self) -> Result<DateTime, RtcError> {\n        self.read(|dr, tr, _ss| {\n            let second = bcd2_to_byte((tr.st(), tr.su()));\n            let minute = bcd2_to_byte((tr.mnt(), tr.mnu()));\n            let hour = bcd2_to_byte((tr.ht(), tr.hu()));\n\n            let weekday = day_of_week_from_u8(dr.wdu()).map_err(RtcError::InvalidDateTime)?;\n            let day = bcd2_to_byte((dr.dt(), dr.du()));\n            let month = bcd2_to_byte((dr.mt() as u8, dr.mu()));\n            let year = bcd2_to_byte((dr.yt(), dr.yu())) as u16 + 2000_u16;\n\n            // Calculate second fraction and multiply to microseconds\n            // Formula from RM0410\n            #[cfg(not(rtc_v2_f2))]\n            let us = {\n                let prediv = RTC::regs().prer().read().prediv_s() as f32;\n                (((prediv - _ss as f32) / (prediv + 1.0)) * 1e6).min(999_999.0) as u32\n            };\n            #[cfg(rtc_v2_f2)]\n            let us = 0;\n\n            DateTime::from(year, month, day, weekday, hour, minute, second, us).map_err(RtcError::InvalidDateTime)\n        })\n    }\n\n    fn read<R>(&self, mut f: impl FnMut(Dr, Tr, u16) -> Result<R, RtcError>) -> Result<R, RtcError> {\n        let r = RTC::regs();\n\n        #[cfg(not(rtc_v2_f2))]\n        let read_ss = || r.ssr().read().ss();\n        #[cfg(rtc_v2_f2)]\n        let read_ss = || 0;\n\n        let mut ss = read_ss();\n        for _ in 0..5 {\n            let tr = r.tr().read();\n            let dr = r.dr().read();\n            let ss_after = read_ss();\n\n            // If an RTCCLK edge occurs during read we may see inconsistent values\n            // so read ssr again and see if it has changed. (see RM0433 Rev 7 46.3.9)\n            if ss == ss_after {\n                return f(dr, tr, ss.try_into().unwrap());\n            } else {\n                ss = ss_after\n            }\n        }\n\n        Err(RtcError::ReadFailure)\n    }\n}\n\n#[cfg(all(feature = \"low-power\", not(feature = \"_lp-time-driver\")))]\n/// Contains an RTC driver.\n#[derive(Clone)]\npub struct RtcContainer {\n    pub(self) mutex: &'static Mutex<CriticalSectionRawMutex, RefCell<Option<Rtc>>>,\n}\n\n#[cfg(all(feature = \"low-power\", not(feature = \"_lp-time-driver\")))]\nimpl RtcContainer {\n    pub(self) const fn new() -> Self {\n        Self {\n            mutex: &crate::time_driver::get_driver().rtc,\n        }\n    }\n\n    /// Acquire an RTC borrow.\n    pub fn borrow_mut<'a>(&self, cs: CriticalSection<'a>) -> RtcBorrow<'a> {\n        RtcBorrow {\n            ref_mut: self.mutex.borrow(cs).borrow_mut(),\n        }\n    }\n}\n\n#[cfg(all(feature = \"low-power\", not(feature = \"_lp-time-driver\")))]\n/// Contains an RTC borrow.\npub struct RtcBorrow<'a> {\n    pub(self) ref_mut: RefMut<'a, Option<Rtc>>,\n}\n\n#[cfg(all(feature = \"low-power\", not(feature = \"_lp-time-driver\")))]\nimpl<'a> ops::Deref for RtcBorrow<'a> {\n    type Target = Rtc;\n\n    fn deref(&self) -> &Self::Target {\n        self.ref_mut.as_ref().unwrap()\n    }\n}\n\n#[cfg(all(feature = \"low-power\", not(feature = \"_lp-time-driver\")))]\nimpl<'a> ops::DerefMut for RtcBorrow<'a> {\n    fn deref_mut(&mut self) -> &mut Self::Target {\n        self.ref_mut.as_mut().unwrap()\n    }\n}\n\n/// RTC driver.\npub struct Rtc {\n    #[cfg(all(feature = \"low-power\", not(feature = \"_lp-time-driver\")))]\n    epoch: chrono::DateTime<chrono::Utc>,\n    _private: (),\n}\n\n/// RTC configuration.\n#[non_exhaustive]\n#[derive(Copy, Clone, PartialEq)]\npub struct RtcConfig {\n    /// The subsecond counter frequency; default is 256\n    ///\n    /// A high counter frequency may impact stop power consumption\n    pub frequency: Hertz,\n}\n\nimpl Default for RtcConfig {\n    /// LSI with prescalers assuming 32.768 kHz.\n    /// Raw sub-seconds in 1/256.\n    fn default() -> Self {\n        RtcConfig { frequency: Hertz(256) }\n    }\n}\n\n/// Calibration cycle period.\n#[derive(Default, Copy, Clone, Debug, PartialEq)]\n#[repr(u8)]\npub enum RtcCalibrationCyclePeriod {\n    /// 8-second calibration period\n    Seconds8,\n    /// 16-second calibration period\n    Seconds16,\n    /// 32-second calibration period\n    #[default]\n    Seconds32,\n}\n\nimpl Rtc {\n    #[cfg(any(not(feature = \"low-power\"), feature = \"_lp-time-driver\"))]\n    /// Create a new RTC instance.\n    pub fn new(_rtc: Peri<'static, RTC>, rtc_config: RtcConfig) -> (Self, RtcTimeProvider) {\n        (Self::new_inner(rtc_config), RtcTimeProvider::new())\n    }\n\n    #[cfg(all(feature = \"low-power\", not(feature = \"_lp-time-driver\")))]\n    /// Create a new RTC instance.\n    pub fn new(_rtc: Peri<'static, RTC>) -> (RtcContainer, RtcTimeProvider) {\n        (RtcContainer::new(), RtcTimeProvider::new())\n    }\n\n    pub(self) fn new_inner(rtc_config: RtcConfig) -> Self {\n        #[cfg(not(any(stm32l0, stm32f3, stm32l1, stm32f0, stm32f2)))]\n        crate::rcc::enable_and_reset::<RTC>();\n\n        let mut this = Self {\n            #[cfg(all(feature = \"low-power\", not(feature = \"_lp-time-driver\")))]\n            epoch: chrono::DateTime::from_timestamp_secs(0).unwrap(),\n            _private: (),\n        };\n\n        let frequency = Self::frequency();\n        let async_psc = ((frequency.0 / rtc_config.frequency.0) - 1) as u8;\n        let sync_psc = (rtc_config.frequency.0 - 1) as u16;\n\n        this.configure(async_psc, sync_psc);\n\n        // Wait for the clock to update after initialization\n        #[cfg(not(rtc_v2_f2))]\n        {\n            let now = RtcTimeProvider::new().read(|_, _, ss| Ok(ss)).unwrap();\n            while now == RtcTimeProvider::new().read(|_, _, ss| Ok(ss)).unwrap() {}\n        }\n\n        #[cfg(all(feature = \"low-power\", not(feature = \"_lp-time-driver\")))]\n        {\n            this.enable_wakeup_line();\n            this.epoch = this.calc_epoch();\n        }\n\n        this\n    }\n\n    fn frequency() -> Hertz {\n        let freqs = unsafe { crate::rcc::get_freqs() };\n        freqs.rtc.to_hertz().unwrap()\n    }\n\n    /// Set the datetime to a new value.\n    ///\n    /// # Errors\n    ///\n    /// Will return `RtcError::InvalidDateTime` if the datetime is not a valid range.\n    pub fn set_datetime(&mut self, t: DateTime) -> Result<(), RtcError> {\n        self.write(true, |rtc| {\n            let (ht, hu) = byte_to_bcd2(t.hour());\n            let (mnt, mnu) = byte_to_bcd2(t.minute());\n            let (st, su) = byte_to_bcd2(t.second());\n\n            let (dt, du) = byte_to_bcd2(t.day());\n            let (mt, mu) = byte_to_bcd2(t.month());\n            let yr = t.year();\n            let yr_offset = (yr - 2000_u16) as u8;\n            let (yt, yu) = byte_to_bcd2(yr_offset);\n\n            use crate::pac::rtc::vals::Ampm;\n\n            rtc.tr().write(|w| {\n                w.set_ht(ht);\n                w.set_hu(hu);\n                w.set_mnt(mnt);\n                w.set_mnu(mnu);\n                w.set_st(st);\n                w.set_su(su);\n                w.set_pm(Ampm::AM);\n            });\n\n            rtc.dr().write(|w| {\n                w.set_dt(dt);\n                w.set_du(du);\n                w.set_mt(mt > 0);\n                w.set_mu(mu);\n                w.set_yt(yt);\n                w.set_yu(yu);\n                w.set_wdu(day_of_week_to_u8(t.day_of_week()));\n            });\n        });\n\n        #[cfg(all(feature = \"low-power\", not(feature = \"_lp-time-driver\")))]\n        {\n            self.epoch = self.calc_epoch();\n        }\n\n        Ok(())\n    }\n\n    /// Check if daylight savings time is active.\n    pub fn get_daylight_savings(&self) -> bool {\n        let cr = RTC::regs().cr().read();\n        cr.bkp()\n    }\n\n    /// Enable/disable daylight savings time.\n    pub fn set_daylight_savings(&mut self, daylight_savings: bool) {\n        self.write(true, |rtc| {\n            rtc.cr().modify(|w| w.set_bkp(daylight_savings));\n        })\n    }\n\n    /// Number of backup registers of this instance.\n    pub const BACKUP_REGISTER_COUNT: usize = RTC::BACKUP_REGISTER_COUNT;\n\n    /// Read content of the backup register.\n    ///\n    /// The registers retain their values during wakes from standby mode or system resets. They also\n    /// retain their value when Vdd is switched off as long as V_BAT is powered.\n    pub fn read_backup_register(&self, register: usize) -> Option<u32> {\n        RTC::read_backup_register(RTC::regs(), register)\n    }\n\n    /// Set content of the backup register.\n    ///\n    /// The registers retain their values during wakes from standby mode or system resets. They also\n    /// retain their value when Vdd is switched off as long as V_BAT is powered.\n    pub fn write_backup_register(&self, register: usize, value: u32) {\n        RTC::write_backup_register(RTC::regs(), register, value)\n    }\n}\n\n/// Trait applicable to an `Rtc` or an `RtcContainer`\npub trait AnyRtc {\n    /// Set the datetime to a new value.\n    fn set_datetime(&mut self, t: DateTime) -> Result<(), RtcError>;\n    /// Check if daylight savings time is active.\n    fn get_daylight_savings(&self) -> bool;\n    /// Enable/disable daylight savings time.\n    fn set_daylight_savings(&mut self, daylight_savings: bool);\n    /// Read content of the backup register.\n    fn read_backup_register(&self, register: usize) -> Option<u32>;\n    /// Set content of the backup register.\n    fn write_backup_register(&self, register: usize, value: u32);\n}\n\n#[cfg(all(feature = \"low-power\", not(feature = \"_lp-time-driver\")))]\nimpl AnyRtc for RtcContainer {\n    fn set_datetime(&mut self, t: DateTime) -> Result<(), RtcError> {\n        critical_section::with(|cs| self.borrow_mut(cs).set_datetime(t))\n    }\n\n    fn get_daylight_savings(&self) -> bool {\n        critical_section::with(|cs| self.borrow_mut(cs).get_daylight_savings())\n    }\n\n    fn set_daylight_savings(&mut self, daylight_savings: bool) {\n        critical_section::with(|cs| self.borrow_mut(cs).set_daylight_savings(daylight_savings))\n    }\n\n    fn read_backup_register(&self, register: usize) -> Option<u32> {\n        critical_section::with(|cs| self.borrow_mut(cs).read_backup_register(register))\n    }\n\n    fn write_backup_register(&self, register: usize, value: u32) {\n        critical_section::with(|cs| self.borrow_mut(cs).write_backup_register(register, value))\n    }\n}\n\nimpl AnyRtc for Rtc {\n    fn set_datetime(&mut self, t: DateTime) -> Result<(), RtcError> {\n        self.set_datetime(t)\n    }\n\n    fn get_daylight_savings(&self) -> bool {\n        self.get_daylight_savings()\n    }\n\n    fn set_daylight_savings(&mut self, daylight_savings: bool) {\n        self.set_daylight_savings(daylight_savings)\n    }\n\n    fn read_backup_register(&self, register: usize) -> Option<u32> {\n        self.read_backup_register(register)\n    }\n\n    fn write_backup_register(&self, register: usize, value: u32) {\n        self.write_backup_register(register, value)\n    }\n}\n\npub(crate) fn byte_to_bcd2(byte: u8) -> (u8, u8) {\n    let mut bcd_high: u8 = 0;\n    let mut value = byte;\n\n    while value >= 10 {\n        bcd_high += 1;\n        value -= 10;\n    }\n\n    (bcd_high, ((bcd_high << 4) | value))\n}\n\npub(crate) fn bcd2_to_byte(bcd: (u8, u8)) -> u8 {\n    let value = bcd.1 | bcd.0 << 4;\n\n    let tmp = ((value & 0xF0) >> 0x4) * 10;\n\n    tmp + (value & 0x0F)\n}\n\ntrait SealedInstance {\n    const BACKUP_REGISTER_COUNT: usize;\n\n    #[cfg(feature = \"low-power\")]\n    #[cfg(not(feature = \"_lp-time-driver\"))]\n    #[cfg(not(any(stm32wba, stm32u5, stm32u3, stm32u0)))]\n    const EXTI_WAKEUP_LINE: usize;\n\n    #[cfg(feature = \"low-power\")]\n    type WakeupInterrupt: crate::interrupt::typelevel::Interrupt;\n\n    fn regs() -> crate::pac::rtc::Rtc {\n        crate::pac::RTC\n    }\n\n    /// Read content of the backup register.\n    ///\n    /// The registers retain their values during wakes from standby mode or system resets. They also\n    /// retain their value when Vdd is switched off as long as V_BAT is powered.\n    fn read_backup_register(rtc: crate::pac::rtc::Rtc, register: usize) -> Option<u32>;\n\n    /// Set content of the backup register.\n    ///\n    /// The registers retain their values during wakes from standby mode or system resets. They also\n    /// retain their value when Vdd is switched off as long as V_BAT is powered.\n    fn write_backup_register(rtc: crate::pac::rtc::Rtc, register: usize, value: u32);\n\n    // fn apply_config(&mut self, rtc_config: RtcConfig);\n}\n\n#[cfg(all(feature = \"low-power\", not(feature = \"_lp-time-driver\")))]\npub(crate) fn init_rtc(cs: CriticalSection, config: RtcConfig, min_stop_pause: embassy_time::Duration) {\n    use crate::time_driver::{LPTimeDriver, get_driver};\n\n    get_driver().set_rtc(cs, Rtc::new_inner(config));\n    get_driver().set_min_stop_pause(cs, min_stop_pause);\n\n    trace!(\"low power: stop with rtc configured\");\n}\n"
  },
  {
    "path": "embassy-stm32/src/rtc/v2.rs",
    "content": "use stm32_metapac::rtc::vals::{Osel, Pol};\n\nuse super::SealedInstance;\nuse crate::pac::rtc::Rtc;\nuse crate::peripherals::RTC;\n\n#[allow(dead_code)]\nimpl super::Rtc {\n    /// Applies the RTC config\n    /// It this changes the RTC clock source the time will be reset\n    pub(super) fn configure(&mut self, async_psc: u8, sync_psc: u16) {\n        self.write(true, |rtc| {\n            rtc.cr().modify(|w| {\n                #[cfg(not(rtc_v2_f2))]\n                w.set_bypshad(true);\n                #[cfg(rtc_v2_f2)]\n                w.set_fmt(false);\n                #[cfg(not(rtc_v2_f2))]\n                w.set_fmt(stm32_metapac::rtc::vals::Fmt::TWENTY_FOUR_HOUR);\n                w.set_osel(Osel::DISABLED);\n                w.set_pol(Pol::HIGH);\n            });\n\n            rtc.prer().modify(|w| {\n                w.set_prediv_s(sync_psc);\n                w.set_prediv_a(async_psc);\n            });\n        });\n    }\n\n    /// Calibrate the clock drift.\n    ///\n    /// `clock_drift` can be adjusted from -487.1 ppm to 488.5 ppm and is clamped to this range.\n    ///\n    /// ### Note\n    ///\n    /// To perform a calibration when `async_prescaler` is less then 3, `sync_prescaler`\n    /// has to be reduced accordingly (see RM0351 Rev 9, sec 38.3.12).\n    #[cfg(not(rtc_v2_f2))]\n    pub fn calibrate(&mut self, mut clock_drift: f32, period: super::RtcCalibrationCyclePeriod) {\n        const RTC_CALR_MIN_PPM: f32 = -487.1;\n        const RTC_CALR_MAX_PPM: f32 = 488.5;\n        const RTC_CALR_RESOLUTION_PPM: f32 = 0.9537;\n\n        if clock_drift < RTC_CALR_MIN_PPM {\n            clock_drift = RTC_CALR_MIN_PPM;\n        } else if clock_drift > RTC_CALR_MAX_PPM {\n            clock_drift = RTC_CALR_MAX_PPM;\n        }\n\n        clock_drift /= RTC_CALR_RESOLUTION_PPM;\n\n        self.write(false, |rtc| {\n            rtc.calr().write(|w| {\n                match period {\n                    super::RtcCalibrationCyclePeriod::Seconds8 => {\n                        w.set_calw8(stm32_metapac::rtc::vals::Calw8::EIGHT_SECOND);\n                    }\n                    super::RtcCalibrationCyclePeriod::Seconds16 => {\n                        w.set_calw16(stm32_metapac::rtc::vals::Calw16::SIXTEEN_SECOND);\n                    }\n                    super::RtcCalibrationCyclePeriod::Seconds32 => {\n                        // Set neither `calw8` nor `calw16` to use 32 seconds\n                    }\n                }\n\n                // Extra pulses during calibration cycle period: CALP * 512 - CALM\n                //\n                // CALP sets whether pulses are added or omitted.\n                //\n                // CALM contains how many pulses (out of 512) are masked in a\n                // given calibration cycle period.\n                if clock_drift > 0.0 {\n                    // Maximum (about 512.2) rounds to 512.\n                    clock_drift += 0.5;\n\n                    // When the offset is positive (0 to 512), the opposite of\n                    // the offset (512 - offset) is masked, i.e. for the\n                    // maximum offset (512), 0 pulses are masked.\n                    w.set_calp(stm32_metapac::rtc::vals::Calp::INCREASE_FREQ);\n                    w.set_calm(512 - clock_drift as u16);\n                } else {\n                    // Minimum (about -510.7) rounds to -511.\n                    clock_drift -= 0.5;\n\n                    // When the offset is negative or zero (-511 to 0),\n                    // the absolute offset is masked, i.e. for the minimum\n                    // offset (-511), 511 pulses are masked.\n                    w.set_calp(stm32_metapac::rtc::vals::Calp::NO_CHANGE);\n                    w.set_calm((clock_drift * -1.0) as u16);\n                }\n            });\n        })\n    }\n\n    pub(super) fn write<F, R>(&mut self, init_mode: bool, f: F) -> R\n    where\n        F: FnOnce(crate::pac::rtc::Rtc) -> R,\n    {\n        let r = RTC::regs();\n        // Disable write protection.\n        // This is safe, as we're only writin the correct and expected values.\n        r.wpr().write(|w| w.set_key(0xca));\n        r.wpr().write(|w| w.set_key(0x53));\n\n        // true if initf bit indicates RTC peripheral is in init mode\n        if init_mode && !r.isr().read().initf() {\n            // to update calendar date/time, time format, and prescaler configuration, RTC must be in init mode\n            r.isr().modify(|w| w.set_init(true));\n            // wait till init state entered\n            // ~2 RTCCLK cycles\n            while !r.isr().read().initf() {}\n        }\n\n        let result = f(r);\n\n        if init_mode {\n            r.isr().modify(|w| w.set_init(false)); // Exits init mode\n        }\n\n        // Re-enable write protection.\n        // This is safe, as the field accepts the full range of 8-bit values.\n        r.wpr().write(|w| w.set_key(0xff));\n        result\n    }\n}\n\nimpl SealedInstance for crate::peripherals::RTC {\n    const BACKUP_REGISTER_COUNT: usize = 20;\n\n    #[cfg(all(feature = \"low-power\", stm32f4))]\n    const EXTI_WAKEUP_LINE: usize = 22;\n\n    #[cfg(all(feature = \"low-power\", stm32l4))]\n    const EXTI_WAKEUP_LINE: usize = 20;\n\n    #[cfg(all(feature = \"low-power\", stm32l0))]\n    const EXTI_WAKEUP_LINE: usize = 20;\n\n    #[cfg(all(feature = \"low-power\", stm32wb))]\n    const EXTI_WAKEUP_LINE: usize = 19;\n\n    #[cfg(all(feature = \"low-power\", any(stm32f4, stm32l4, stm32wb)))]\n    type WakeupInterrupt = crate::interrupt::typelevel::RTC_WKUP;\n\n    #[cfg(all(feature = \"low-power\", stm32l0))]\n    type WakeupInterrupt = crate::interrupt::typelevel::RTC;\n\n    fn read_backup_register(rtc: Rtc, register: usize) -> Option<u32> {\n        if register < Self::BACKUP_REGISTER_COUNT {\n            Some(rtc.bkpr(register).read().bkp())\n        } else {\n            None\n        }\n    }\n\n    fn write_backup_register(rtc: Rtc, register: usize, value: u32) {\n        if register < Self::BACKUP_REGISTER_COUNT {\n            rtc.bkpr(register).write(|w| w.set_bkp(value));\n        }\n    }\n}\n"
  },
  {
    "path": "embassy-stm32/src/rtc/v3.rs",
    "content": "use stm32_metapac::rtc::vals::{Calp, Calw8, Calw16, Fmt, Key, Osel, Pol, TampalrmType};\n\nuse super::RtcCalibrationCyclePeriod;\nuse crate::pac::rtc::Rtc;\nuse crate::peripherals::RTC;\nuse crate::rtc::SealedInstance;\n\nimpl super::Rtc {\n    /// Applies the RTC config\n    /// It this changes the RTC clock source the time will be reset\n    pub(super) fn configure(&mut self, async_psc: u8, sync_psc: u16) {\n        self.write(true, |rtc| {\n            rtc.cr().modify(|w| {\n                w.set_bypshad(true);\n                w.set_fmt(Fmt::TWENTY_FOUR_HOUR);\n                w.set_osel(Osel::DISABLED);\n                w.set_pol(Pol::HIGH);\n            });\n\n            rtc.prer().modify(|w| {\n                w.set_prediv_s(sync_psc);\n                w.set_prediv_a(async_psc);\n            });\n\n            // TODO: configuration for output pins\n            rtc.cr().modify(|w| {\n                w.set_out2en(false);\n                w.set_tampalrm_type(TampalrmType::PUSH_PULL);\n                w.set_tampalrm_pu(false);\n            });\n        });\n    }\n\n    const RTC_CALR_MIN_PPM: f32 = -487.1;\n    const RTC_CALR_MAX_PPM: f32 = 488.5;\n    const RTC_CALR_RESOLUTION_PPM: f32 = 0.9537;\n\n    /// Calibrate the clock drift.\n    ///\n    /// `clock_drift` can be adjusted from -487.1 ppm to 488.5 ppm and is clamped to this range.\n    ///\n    /// ### Note\n    ///\n    /// To perform a calibration when `async_prescaler` is less then 3, `sync_prescaler`\n    /// has to be reduced accordingly (see RM0351 Rev 9, sec 38.3.12).\n    pub fn calibrate(&mut self, mut clock_drift: f32, period: RtcCalibrationCyclePeriod) {\n        if clock_drift < Self::RTC_CALR_MIN_PPM {\n            clock_drift = Self::RTC_CALR_MIN_PPM;\n        } else if clock_drift > Self::RTC_CALR_MAX_PPM {\n            clock_drift = Self::RTC_CALR_MAX_PPM;\n        }\n\n        clock_drift /= Self::RTC_CALR_RESOLUTION_PPM;\n\n        self.write(false, |rtc| {\n            rtc.calr().write(|w| {\n                match period {\n                    RtcCalibrationCyclePeriod::Seconds8 => {\n                        w.set_calw8(Calw8::EIGHT_SECONDS);\n                    }\n                    RtcCalibrationCyclePeriod::Seconds16 => {\n                        w.set_calw16(Calw16::SIXTEEN_SECONDS);\n                    }\n                    RtcCalibrationCyclePeriod::Seconds32 => {\n                        // Set neither `calw8` nor `calw16` to use 32 seconds\n                    }\n                }\n\n                // Extra pulses during calibration cycle period: CALP * 512 - CALM\n                //\n                // CALP sets whether pulses are added or omitted.\n                //\n                // CALM contains how many pulses (out of 512) are masked in a\n                // given calibration cycle period.\n                if clock_drift > 0.0 {\n                    // Maximum (about 512.2) rounds to 512.\n                    clock_drift += 0.5;\n\n                    // When the offset is positive (0 to 512), the opposite of\n                    // the offset (512 - offset) is masked, i.e. for the\n                    // maximum offset (512), 0 pulses are masked.\n                    w.set_calp(Calp::INCREASE_FREQ);\n                    w.set_calm(512 - clock_drift as u16);\n                } else {\n                    // Minimum (about -510.7) rounds to -511.\n                    clock_drift -= 0.5;\n\n                    // When the offset is negative or zero (-511 to 0),\n                    // the absolute offset is masked, i.e. for the minimum\n                    // offset (-511), 511 pulses are masked.\n                    w.set_calp(Calp::NO_CHANGE);\n                    w.set_calm((clock_drift * -1.0) as u16);\n                }\n            });\n        })\n    }\n\n    pub(super) fn write<F, R>(&mut self, init_mode: bool, f: F) -> R\n    where\n        F: FnOnce(crate::pac::rtc::Rtc) -> R,\n    {\n        let r = RTC::regs();\n        // Disable write protection.\n        // This is safe, as we're only writin the correct and expected values.\n        r.wpr().write(|w| w.set_key(Key::DEACTIVATE1));\n        r.wpr().write(|w| w.set_key(Key::DEACTIVATE2));\n\n        if init_mode && !r.icsr().read().initf() {\n            r.icsr().modify(|w| w.set_init(true));\n            // wait till init state entered\n            // ~2 RTCCLK cycles\n            while !r.icsr().read().initf() {}\n        }\n\n        let result = f(r);\n\n        if init_mode {\n            r.icsr().modify(|w| w.set_init(false)); // Exits init mode\n        }\n\n        // Re-enable write protection.\n        // This is safe, as the field accepts the full range of 8-bit values.\n        r.wpr().write(|w| w.set_key(Key::ACTIVATE));\n\n        result\n    }\n}\n\nimpl SealedInstance for crate::peripherals::RTC {\n    const BACKUP_REGISTER_COUNT: usize = 32;\n\n    #[cfg(feature = \"low-power\")]\n    #[cfg(not(feature = \"_lp-time-driver\"))]\n    cfg_if::cfg_if!(\n        if #[cfg(any(stm32g4, stm32wl))] {\n            const EXTI_WAKEUP_LINE: usize = 20;\n        } else if #[cfg(stm32g0)] {\n            const EXTI_WAKEUP_LINE: usize = 19;\n        } else if #[cfg(any(stm32l5, stm32h5))] {\n            const EXTI_WAKEUP_LINE: usize = 17;\n        }\n    );\n\n    #[cfg(feature = \"low-power\")]\n    cfg_if::cfg_if!(\n        if #[cfg(any(stm32g4, stm32wl))] {\n            #[cfg(not(feature = \"_core-cm0p\"))]\n            type WakeupInterrupt = crate::interrupt::typelevel::RTC_WKUP;\n            #[cfg(feature = \"_core-cm0p\")]\n            type WakeupInterrupt = crate::interrupt::typelevel::RTC_LSECSS;\n        } else if #[cfg(any(stm32g0, stm32u0))] {\n            type WakeupInterrupt = crate::interrupt::typelevel::RTC_TAMP;\n        } else if #[cfg(any(stm32l5, stm32h5, stm32u3, stm32u5, stm32wba))] {\n            type WakeupInterrupt = crate::interrupt::typelevel::RTC;\n        }\n    );\n\n    fn read_backup_register(_rtc: Rtc, register: usize) -> Option<u32> {\n        #[allow(clippy::if_same_then_else)]\n        if register < Self::BACKUP_REGISTER_COUNT {\n            //Some(rtc.bkpr()[register].read().bits())\n            None // RTC3 backup registers come from the TAMP peripheral, not RTC. Not() even in the L412 PAC\n        } else {\n            None\n        }\n    }\n\n    fn write_backup_register(_rtc: Rtc, register: usize, _value: u32) {\n        if register < Self::BACKUP_REGISTER_COUNT {\n            // RTC3 backup registers come from the TAMP peripheral, not RTC. Not() even in the L412 PAC\n            //self.rtc.bkpr()[register].write(|w| w.bits(value))\n        }\n    }\n}\n"
  },
  {
    "path": "embassy-stm32/src/saes/mod.rs",
    "content": "//! Secure Advanced Encryption Standard (SAES) hardware accelerator\n//!\n//! SAES provides the same cipher modes as AES but with enhanced security features\n//! for key management and protection. It's particularly useful in secure boot scenarios\n//! and applications requiring hardware root of trust.\n//!\n//! # Key Differences from AES\n//!\n//! | Feature | AES | SAES |\n//! |---------|-----|------|\n//! | Key Sources | Software only | Software + Hardware (DHUK, BHK) |\n//! | Key Protection | Basic | KEYPROT + isolation |\n//! | Key Sharing | No | Yes (with AES, other peripherals) |\n//! | Key Wrapping | No | Yes (wrapped/encrypted keys) |\n//! | Security Context | Standard | Enhanced/Secure |\n//!\n//! # Hardware Key Sources\n//!\n//! - **DHUK** (Derived Hardware Unique Key): Device-unique key derived from UID\n//! - **BHK** (Boot Hardware Key): Key loaded during secure boot\n//! - **XOR**: XOR combination of DHUK and BHK\n//!\n//! These keys are never exposed to software and remain in secure hardware.\n//!\n//! # Examples\n//!\n//! ## Using Software Keys (Same as AES)\n//!\n//! ```no_run\n//! use embassy_stm32::saes::{Saes, AesGcm, Direction};\n//!\n//! let key = [0u8; 16];\n//! let iv = [0u8; 12];\n//! let cipher = AesGcm::new(&key, &iv);\n//!\n//! let mut saes = Saes::new_blocking(p.SAES, Irqs);\n//! let mut ctx = saes.start(&cipher, Direction::Encrypt);\n//! // ... same as AES\n//! ```\n//!\n//! ## Using Hardware-Derived Keys\n//!\n//! ```no_run\n//! use embassy_stm32::saes::{Saes, AesGcm, Direction, HardwareKeySource};\n//!\n//! let iv = [0u8; 12];\n//! let cipher = AesGcm::new(&[], &iv); // No software key needed\n//!\n//! let mut saes = Saes::new_blocking(p.SAES, Irqs);\n//!\n//! // Use device-unique hardware key\n//! let mut ctx = saes.start_with_hw_key(\n//!     HardwareKeySource::DHUK,\n//!     &cipher,\n//!     Direction::Encrypt\n//! );\n//!\n//! // Hardware key is used automatically - never exposed to software\n//! saes.payload_blocking(&mut ctx, &plaintext, &mut ciphertext, true);\n//! saes.finish_blocking(ctx);\n//! ```\n//!\n//! ## Key Sharing Between Peripherals\n//!\n//! ```no_run\n//! use embassy_stm32::saes::{Saes, KeyShareTarget};\n//!\n//! // After unwrapping a key with SAES, share it with AES peripheral\n//! saes.share_key_with(KeyShareTarget::AES);\n//! // Now AES peripheral can use the unwrapped key\n//! ```\n//!\n//! # Security Features\n//!\n//! - **Key Protection**: KEYPROT flag prevents key readback\n//! - **Hardware Keys**: Never exposed to software, immune to memory dumps\n//! - **Key Wrapping**: Import encrypted keys securely\n//! - **Peripheral Isolation**: Keys can be shared without software access\n//!\n//! # Availability\n//!\n//! **Important**: SAES is only available on:\n//! - STM32WBA52 and higher\n//! - STM32WBA55\n//! - STM32WBA6x\n//! - NOT available on STM32WBA50\n//!\n//! # Use Cases\n//!\n//! - Secure boot key management\n//! - Device-unique encryption (uses DHUK based on chip UID)\n//! - Key provisioning and wrapping\n//! - Multi-peripheral cryptographic workflows\n//! - High-security applications requiring hardware root of trust\n//!\n//! # See Also\n//!\n//! - [`aes`](crate::aes) - Standard AES implementation (all WBA chips)\n//! - [`pka`](crate::pka) - Public Key Accelerator\n\n// Re-export most types from AES since they're identical\nuse core::marker::PhantomData;\n\nuse embassy_hal_internal::{Peri, PeripheralType};\nuse embassy_sync::waitqueue::AtomicWaker;\n\npub use crate::aes::{\n    AesCbc, AesCcm, AesCtr, AesEcb, AesGcm, Cipher, CipherAuthenticated, CipherSized, Context, Direction, Error,\n    IVSized, KeySize,\n};\nuse crate::dma::ChannelAndRequest;\nuse crate::interrupt::typelevel::Interrupt;\nuse crate::mode::{Async, Blocking, Mode};\nuse crate::{interrupt, pac, peripherals, rcc};\n\nstatic SAES_WAKER: AtomicWaker = AtomicWaker::new();\n\n/// SAES interrupt handler.\npub struct InterruptHandler<T: Instance> {\n    _phantom: PhantomData<T>,\n}\n\nimpl<T: Instance> interrupt::typelevel::Handler<T::Interrupt> for InterruptHandler<T> {\n    unsafe fn on_interrupt() {\n        // Wake on computation complete flag (CCF) from ISR, not on BUSY clearing.\n        // BUSY also clears during init/RNG-fetch/key-transfer which must not wake tasks.\n        // Note: CCF is in SAES_ISR, not SAES_SR (unlike AES which has CCF in SR).\n        let isr = T::regs().isr().read();\n        if isr.ccf() {\n            // Clear all interrupt flags\n            T::regs().icr().write(|w| w.0 = 0xFFFF_FFFF);\n            SAES_WAKER.wake();\n        }\n\n        // Clear error flags\n        if isr.rweif() {\n            T::regs().icr().write(|w| w.0 = 0xFFFF_FFFF);\n        }\n    }\n}\n\n/// Hardware key source for SAES\n#[derive(Debug, Clone, Copy, PartialEq, Eq)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub enum HardwareKeySource {\n    /// Derived Hardware Unique Key\n    DHUK = 1,\n    /// Boot Hardware Key\n    BHK = 2,\n    /// XOR of DHUK and BHK\n    XorDhukBhk = 3,\n}\n\n/// Key mode for SAES\n#[derive(Debug, Clone, Copy, PartialEq, Eq)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub enum KeyMode {\n    /// Normal software key mode\n    Normal = 0,\n    /// Wrapped key mode (encrypted key)\n    WrappedKey = 1,\n    /// Shared key mode (key shared between peripherals)\n    SharedKey = 2,\n}\n\n/// Peripheral to share key with\n#[derive(Debug, Clone, Copy, PartialEq, Eq)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub enum KeyShareTarget {\n    /// Share with AES peripheral\n    AES = 0,\n}\n\n/// SAES driver.\npub struct Saes<'d, T: Instance, M: Mode> {\n    _peripheral: Peri<'d, T>,\n    _phantom: PhantomData<M>,\n    #[allow(dead_code)] // Reserved for future async/DMA implementation\n    dma_in: Option<ChannelAndRequest<'d>>,\n    #[allow(dead_code)] // Reserved for future async/DMA implementation\n    dma_out: Option<ChannelAndRequest<'d>>,\n}\n\nimpl<'d, T: Instance> Saes<'d, T, Blocking> {\n    /// Instantiates, resets, and enables the SAES peripheral.\n    pub fn new_blocking(\n        peripheral: Peri<'d, T>,\n        _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd,\n    ) -> Self {\n        // On WBA6, SAES fetches a random seed from the RNG on every reset/enable.\n        // If the RNG is not already running, start it with HSI as the clock source.\n        // This mirrors the same workaround used by the PKA driver on WBA6.\n        #[cfg(rng_wba6)]\n        {\n            let rcc = pac::RCC;\n            if !rcc.ahb2enr().read().rngen() {\n                rcc.ccipr2().modify(|w| w.set_rngsel(pac::rcc::vals::Rngsel::HSI));\n                rcc.ahb2enr().modify(|w| w.set_rngen(true));\n                pac::RNG.cr().modify(|w| w.set_rngen(true));\n                // Brief settle delay (~100 µs at 96 MHz) before SAES tries to read from RNG\n                cortex_m::asm::delay(10_000);\n            }\n        }\n\n        rcc::enable_and_reset::<T>();\n\n        let p = T::regs();\n        // After reset, SAES sets BUSY while it fetches a random number from the internal RNG.\n        // Writing CR before BUSY clears is forbidden (HAL: CRYP_FLAG_BUSY check at init).\n        while p.sr().read().busy() {}\n        // Panic on RNG error - the peripheral is unusable without a working RNG.\n        assert!(!p.isr().read().rngeif(), \"SAES: RNG error during initialization\");\n\n        let instance = Self {\n            _peripheral: peripheral,\n            _phantom: PhantomData,\n            dma_in: None,\n            dma_out: None,\n        };\n\n        T::Interrupt::unpend();\n        unsafe { T::Interrupt::enable() };\n\n        instance\n    }\n}\n\nimpl<'d, T: Instance> Saes<'d, T, Async> {\n    /// Instantiates, resets, and enables the SAES peripheral with DMA support.\n    pub fn new<D1: DmaIn<T>, D2: DmaOut<T>>(\n        peripheral: Peri<'d, T>,\n        dma_in: Peri<'d, D1>,\n        dma_out: Peri<'d, D2>,\n        _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>>\n        + interrupt::typelevel::Binding<D1::Interrupt, crate::dma::InterruptHandler<D1>>\n        + interrupt::typelevel::Binding<D2::Interrupt, crate::dma::InterruptHandler<D2>>\n        + 'd,\n    ) -> Self {\n        // On WBA6, SAES fetches a random seed from the RNG on every reset/enable.\n        // If the RNG is not already running, start it with HSI as the clock source.\n        // This mirrors the same workaround used by the PKA driver on WBA6.\n        #[cfg(rng_wba6)]\n        {\n            let rcc = pac::RCC;\n            if !rcc.ahb2enr().read().rngen() {\n                rcc.ccipr2().modify(|w| w.set_rngsel(pac::rcc::vals::Rngsel::HSI));\n                rcc.ahb2enr().modify(|w| w.set_rngen(true));\n                pac::RNG.cr().modify(|w| w.set_rngen(true));\n                cortex_m::asm::delay(10_000);\n            }\n        }\n\n        rcc::enable_and_reset::<T>();\n\n        let p = T::regs();\n        // After reset, SAES sets BUSY while it fetches a random number from the internal RNG.\n        // Writing CR before BUSY clears is forbidden (HAL: CRYP_FLAG_BUSY check at init).\n        while p.sr().read().busy() {}\n        // Panic on RNG error - the peripheral is unusable without a working RNG.\n        assert!(!p.isr().read().rngeif(), \"SAES: RNG error during initialization\");\n\n        let instance = Self {\n            _peripheral: peripheral,\n            _phantom: PhantomData,\n            dma_in: new_dma!(dma_in, _irq),\n            dma_out: new_dma!(dma_out, _irq),\n        };\n\n        T::Interrupt::unpend();\n        unsafe { T::Interrupt::enable() };\n\n        instance\n    }\n}\n\nimpl<'d, T: Instance, M: Mode> Saes<'d, T, M> {\n    /// Starts a new cipher operation with a software key.\n    pub fn start<'c, C>(&mut self, cipher: &'c C, dir: Direction) -> Context<'c, C>\n    where\n        C: Cipher<'c> + CipherSized + IVSized,\n    {\n        self.start_with_key_mode(cipher, dir, KeyMode::Normal, None)\n    }\n\n    /// Starts a new cipher operation with a hardware-derived key.\n    pub fn start_with_hw_key<'c, C>(\n        &mut self,\n        key_source: HardwareKeySource,\n        cipher: &'c C,\n        dir: Direction,\n    ) -> Context<'c, C>\n    where\n        C: Cipher<'c> + CipherSized + IVSized,\n    {\n        self.start_with_key_mode(cipher, dir, KeyMode::Normal, Some(key_source))\n    }\n\n    /// Internal start method with full control over key mode.\n    fn start_with_key_mode<'c, C>(\n        &mut self,\n        cipher: &'c C,\n        dir: Direction,\n        key_mode: KeyMode,\n        hw_key: Option<HardwareKeySource>,\n    ) -> Context<'c, C>\n    where\n        C: Cipher<'c> + CipherSized + IVSized,\n    {\n        let p = T::regs();\n\n        // Disable the peripheral, then wait for BUSY to clear before touching CR.\n        // The HAL checks BUSY before every CR write (SetConfig, Encrypt, Decrypt).\n        p.cr().modify(|w| w.set_en(false));\n        while p.sr().read().busy() {}\n\n        // Software reset (IPRST): clears KEYVALID and all internal key state.\n        // Without this, KEYVALID from a previous 128-bit operation stays set when\n        // switching to 256-bit, causing the KEYVALID wait to exit before all 8 key\n        // registers are written (producing silent wrong-key encryption).\n        // Mirrors HAL_CRYP_DeInit(): SET_BIT(CR, IPRST) then CLEAR_BIT(CR, IPRST).\n        p.cr().modify(|w| w.set_iprst(true));\n        p.cr().modify(|w| w.set_iprst(false));\n\n        // Clear all pending flags — in particular KEIF, which if left set will permanently\n        // block KEYVALID from being asserted, making the key load silently fail.\n        p.icr().write(|w| w.0 = 0xFFFF_FFFF);\n\n        // Configure data type based on cipher mode (NO_SWAP, BYTE_SWAP, or BIT_SWAP)\n        p.cr()\n            .modify(|w| w.set_datatype(pac::saes::vals::Datatype::from_bits(cipher.datatype())));\n\n        // Configure key size\n        let keysize = cipher.key_size();\n        let keysize_val = match keysize {\n            KeySize::Bits128 => pac::saes::vals::Keysize::BITS128,\n            KeySize::Bits256 => pac::saes::vals::Keysize::BITS256,\n        };\n        p.cr().modify(|w| w.set_keysize(keysize_val));\n        // Changing KEYSIZE may trigger a new RNG mask fetch (BUSY=1) inside SAES,\n        // particularly when switching from 128-bit to 256-bit, which needs a larger mask.\n        while p.sr().read().busy() {}\n\n        // Set cipher mode using SAES-compatible method\n        self.set_cipher_mode(p, cipher);\n        let is_gcm_ccm = cipher.uses_gcm_phases();\n\n        // Set direction\n        let mode_val = match dir {\n            Direction::Encrypt => pac::saes::vals::Mode::ENCRYPTION,\n            Direction::Decrypt => pac::saes::vals::Mode::DECRYPTION,\n        };\n        p.cr().modify(|w| w.set_mode(mode_val));\n\n        // Set key mode\n        let kmod_val = pac::saes::vals::Kmod::from_bits(key_mode as u8);\n        p.cr().modify(|w| w.set_kmod(kmod_val));\n\n        // For GCM/CCM (authenticated) modes, set GCMPH=0 (init phase) BEFORE loading the key.\n        if is_gcm_ccm {\n            p.cr().modify(|w| w.set_gcmph(pac::saes::vals::Gcmph::from_bits(0)));\n        }\n\n        // Configure and load the key (after GCMPH is set).\n        if let Some(hw_key_src) = hw_key {\n            let keysel_val = pac::saes::vals::Keysel::from_bits(hw_key_src as u8);\n            p.cr().modify(|w| w.set_keysel(keysel_val));\n            p.cr().modify(|w| w.set_keyprot(true));\n            // For hardware keys (non-SW), SAES fetches the key autonomously: wait for KEYVALID.\n            while !p.sr().read().keyvalid() {}\n        } else {\n            // Load software key, then wait for KEYVALID.\n            // Unlike plain AES, SAES validates the key register write sequence; KEYVALID must\n            // be set before EN can be asserted (RM: \"EN cannot be set as long as KEYVALID = 0\").\n            self.load_key(cipher.key());\n            while !p.sr().read().keyvalid() {}\n        }\n\n        // For ECB/CBC decryption, perform key derivation (MODE=1) before the actual operation.\n        // CTR, GCM, and CCM use the encryption key schedule in both directions - no derivation.\n        let needs_key_derivation = dir == Direction::Decrypt && matches!(cipher.chmod_bits(), 0 | 1);\n        if needs_key_derivation {\n            p.cr().modify(|w| w.set_mode(pac::saes::vals::Mode::KEY_DERIVATION));\n            p.cr().modify(|w| w.set_en(true));\n            // Wait for CCF (computation complete), not BUSY\n            while !p.isr().read().ccf() {}\n            // Clear CCF via ICR\n            p.icr().write(|w| w.0 = 0xFFFF_FFFF);\n            // Restore decrypt mode for the actual operation\n            p.cr().modify(|w| w.set_mode(mode_val));\n        }\n\n        // Load IV\n        self.load_iv(cipher.iv());\n\n        // Perform init phase for GCM/CCM (hash-key H calculation, phase 0).\n        // MODE is already ENCRYPTION here (set above) — correct for H = AES_ENCRYPT(K, 0).\n        if is_gcm_ccm {\n            p.cr().modify(|w| w.set_en(true));\n            // Wait for CCF (init phase complete)\n            while !p.isr().read().ccf() {}\n            // Clear flags\n            p.icr().write(|w| w.0 = 0xFFFF_FFFF);\n        } else {\n            // For non-GCM/CCM modes, enable the peripheral then wait for BUSY to clear.\n            // SAES may assert BUSY after EN=1 to apply the per-key-size RNG mask\n            // (observed with 256-bit keys: the upper-half mask is applied here).\n            p.cr().modify(|w| w.set_en(true));\n            while p.sr().read().busy() {}\n        }\n\n        // Create context (peripheral is now enabled)\n        Context {\n            cipher,\n            dir,\n            last_block_processed: false,\n            is_gcm_ccm,\n            header_processed: false,\n            header_len: 0,\n            payload_len: 0,\n            aad_buffer: [0; 16],\n            aad_buffer_len: 0,\n            cr: p.cr().read().0,\n            iv: [p.ivr(0).read(), p.ivr(1).read(), p.ivr(2).read(), p.ivr(3).read()],\n            suspr: [0; 8],\n        }\n    }\n\n    /// Share the current unwrapped key with another peripheral.\n    /// This must be called after a decryption operation that unwrapped a key.\n    pub fn share_key_with(&mut self, target: KeyShareTarget) {\n        let p = T::regs();\n        let kshareid_val = match target {\n            KeyShareTarget::AES => pac::saes::vals::Kshareid::AES,\n        };\n        p.cr().modify(|w| w.set_kshareid(kshareid_val));\n    }\n\n    /// Set cipher mode for SAES peripheral using the cipher's CHMOD bits.\n    fn set_cipher_mode<'c, C>(&mut self, p: pac::saes::Saes, cipher: &C)\n    where\n        C: Cipher<'c>,\n    {\n        // Use the cipher's canonical CHMOD value (0=ECB, 1=CBC, 2=CTR, 3=GCM/GMAC, 4=CCM).\n        // Inferring mode from IV length is unreliable: GCM, CBC, and CTR all use 16-byte IVs\n        // after AesGcm::new() pads the 12-byte nonce to 16 bytes.\n        p.cr()\n            .modify(|w| w.set_chmod(pac::saes::vals::Chmod::from_bits(cipher.chmod_bits())));\n    }\n\n    /// Process authenticated additional data (AAD) for GCM/CCM modes.\n    pub fn aad_blocking<'c, C>(&mut self, ctx: &mut Context<'c, C>, aad: &[u8], last: bool) -> Result<(), Error>\n    where\n        C: Cipher<'c> + CipherAuthenticated<16>,\n    {\n        // Reuse AES implementation logic\n        let p = T::regs();\n\n        if ctx.header_processed && last {\n            return Ok(());\n        }\n\n        // Set GCM phase to header (phase 1), then re-enable.\n        // After the init phase SAES auto-clears EN, so we must set EN=1 here.\n        p.cr().modify(|w| w.set_gcmph(pac::saes::vals::Gcmph::from_bits(1)));\n        p.cr().modify(|w| w.set_en(true));\n\n        let mut aad_remaining = aad.len();\n        let mut aad_index = 0;\n\n        // Process buffered AAD first if any\n        if ctx.aad_buffer_len > 0 {\n            let space_available = 16 - ctx.aad_buffer_len;\n            let to_copy = core::cmp::min(space_available, aad_remaining);\n            ctx.aad_buffer[ctx.aad_buffer_len..ctx.aad_buffer_len + to_copy].copy_from_slice(&aad[..to_copy]);\n            ctx.aad_buffer_len += to_copy;\n            aad_index += to_copy;\n            aad_remaining -= to_copy;\n\n            if ctx.aad_buffer_len == 16 {\n                self.write_block_blocking(&ctx.aad_buffer)?;\n                // Wait for CCF (header block processed) — no output read in header phase.\n                // SAES CCF is in ISR, not SR (unlike plain AES).\n                while !p.isr().read().ccf() {}\n                p.icr().write(|w| w.0 = 0xFFFF_FFFF);\n                ctx.header_len += 16;\n                ctx.aad_buffer_len = 0;\n            }\n        }\n\n        // Process complete blocks\n        while aad_remaining >= 16 {\n            self.write_block_blocking(&aad[aad_index..aad_index + 16])?;\n            // Wait for CCF after each header block\n            while !p.isr().read().ccf() {}\n            p.icr().write(|w| w.0 = 0xFFFF_FFFF);\n            ctx.header_len += 16;\n            aad_index += 16;\n            aad_remaining -= 16;\n        }\n\n        // Buffer any remaining partial block\n        if aad_remaining > 0 {\n            ctx.aad_buffer[..aad_remaining].copy_from_slice(&aad[aad_index..aad_index + aad_remaining]);\n            ctx.aad_buffer_len = aad_remaining;\n        }\n\n        // If this is the last AAD block, pad and process\n        if last {\n            if ctx.aad_buffer_len > 0 {\n                // Pad partial block with zeros (NPBLB is payload-phase-only, not set here)\n                for i in ctx.aad_buffer_len..16 {\n                    ctx.aad_buffer[i] = 0;\n                }\n                self.write_block_blocking(&ctx.aad_buffer)?;\n                // Wait for CCF after last header block\n                while !p.isr().read().ccf() {}\n                p.icr().write(|w| w.0 = 0xFFFF_FFFF);\n                ctx.header_len += ctx.aad_buffer_len as u64;\n                ctx.aad_buffer_len = 0;\n            }\n            ctx.header_processed = true;\n        }\n\n        Ok(())\n    }\n\n    /// Process payload data in blocking mode.\n    pub fn payload_blocking<'c, C>(\n        &mut self,\n        ctx: &mut Context<'c, C>,\n        input: &[u8],\n        output: &mut [u8],\n        last: bool,\n    ) -> Result<(), Error>\n    where\n        C: Cipher<'c>,\n    {\n        let p = T::regs();\n\n        if output.len() < input.len() {\n            return Err(Error::ConfigError);\n        }\n\n        // For GCM/CCM, switch to payload phase.\n        // SAES requires EN=0→1 at every GCMPH transition; without re-enabling here,\n        // the GHASH state from the header phase is not properly transferred and the\n        // encrypt/decrypt tags diverge (observed on SAES v1a / WBA65RI with AAD).\n        if ctx.is_gcm_ccm {\n            if !ctx.header_processed {\n                ctx.header_processed = true;\n            }\n            p.cr().modify(|w| w.set_gcmph(pac::saes::vals::Gcmph::from_bits(2)));\n            p.cr().modify(|w| w.set_npblb(0));\n            p.cr().modify(|w| w.set_en(true));\n        }\n\n        let block_size = C::BLOCK_SIZE;\n        let mut processed = 0;\n\n        // Ensure proper block alignment for modes that require padding\n        if C::REQUIRES_PADDING && !last && input.len() % block_size != 0 {\n            return Err(Error::ConfigError);\n        }\n\n        // Process complete blocks\n        let complete_blocks = if last {\n            input.len() / block_size\n        } else {\n            input.len() / block_size\n        };\n\n        for _ in 0..complete_blocks {\n            let block = &input[processed..processed + block_size];\n            let out_block = &mut output[processed..processed + block_size];\n            self.write_block_blocking(block)?;\n            self.read_block_blocking(out_block)?;\n            processed += block_size;\n            ctx.payload_len += block_size as u64;\n        }\n\n        // Handle partial block if last\n        if last && processed < input.len() {\n            if C::REQUIRES_PADDING {\n                return Err(Error::ConfigError);\n            }\n\n            let remaining = input.len() - processed;\n            let mut partial_block = [0u8; 16];\n            partial_block[..remaining].copy_from_slice(&input[processed..]);\n\n            // NPBLB = Number of Padding Bytes in Last Block.\n            // For `remaining` valid bytes, there are (16 - remaining) padding bytes.\n            let padding_bytes = (16 - remaining) as u8;\n            p.cr().modify(|w| w.set_npblb(padding_bytes));\n\n            self.write_block_blocking(&partial_block)?;\n            self.read_block_blocking(&mut partial_block)?;\n\n            output[processed..processed + remaining].copy_from_slice(&partial_block[..remaining]);\n            ctx.payload_len += remaining as u64;\n        }\n\n        if last {\n            ctx.last_block_processed = true;\n        }\n\n        Ok(())\n    }\n\n    /// Finishes the cipher operation and returns the authentication tag (for GCM/CCM).\n    pub fn finish_blocking<'c, C>(&mut self, ctx: Context<'c, C>) -> Result<Option<[u8; 16]>, Error>\n    where\n        C: Cipher<'c>,\n    {\n        let p = T::regs();\n\n        // For GCM, perform final phase to get tag\n        if ctx.is_gcm_ccm {\n            // SAES may set BUSY during GCM payload encryption. The PAC document states:\n            // \"When GCM encryption is selected, the flag must be at zero before selecting\n            // the GCM final phase.\" The HAL (HAL_CRYPEx_AESGCM_GenerateAuthTAG) also\n            // checks BUSY before setting the final phase.\n            while p.sr().read().busy() {}\n\n            // Set GCM phase to final (phase 3)\n            p.cr().modify(|w| w.set_gcmph(pac::saes::vals::Gcmph::from_bits(3)));\n\n            // Write lengths (in bits) as final block\n            let header_bits = (ctx.header_len * 8) as u64;\n            let payload_bits = (ctx.payload_len * 8) as u64;\n\n            let mut length_block = [0u8; 16];\n            length_block[0..8].copy_from_slice(&header_bits.to_be_bytes());\n            length_block[8..16].copy_from_slice(&payload_bits.to_be_bytes());\n\n            self.write_block_blocking(&length_block)?;\n\n            // Read the authentication tag\n            let mut tag = [0u8; 16];\n            self.read_block_blocking(&mut tag)?;\n\n            // Disable peripheral\n            p.cr().modify(|w| w.set_en(false));\n\n            Ok(Some(tag))\n        } else {\n            // For non-authenticated modes, just disable\n            p.cr().modify(|w| w.set_en(false));\n            Ok(None)\n        }\n    }\n\n    /// Load key into SAES peripheral.\n    fn load_key(&mut self, key: &[u8]) {\n        let p = T::regs();\n\n        // Keys are loaded as 32-bit words (big-endian byte order), high register first.\n        // KEYR7 = key MSB (key[0..3]), KEYR0 = key LSB (key[28..31]) for 256-bit.\n        // This order matches the STM32Cube HAL's CRYP_SetKey() function.\n        let key_words = key.len() / 4;\n        for i in 0..key_words {\n            let word = u32::from_be_bytes([key[i * 4], key[i * 4 + 1], key[i * 4 + 2], key[i * 4 + 3]]);\n            p.keyr(key_words - 1 - i).write_value(word); // KEYR(N-1-i): descending from MSB\n        }\n    }\n\n    /// Load IV into SAES peripheral.\n    fn load_iv(&mut self, iv: &[u8]) {\n        if iv.is_empty() {\n            return;\n        }\n\n        let p = T::regs();\n\n        // IV is loaded as 32-bit words (big-endian byte order)\n        let iv_words = core::cmp::min(iv.len(), 16) / 4;\n        for i in 0..iv_words {\n            let word = u32::from_be_bytes([iv[i * 4], iv[i * 4 + 1], iv[i * 4 + 2], iv[i * 4 + 3]]);\n            p.ivr(i).write_value(word);\n        }\n\n        // Handle partial IV words\n        let remaining = core::cmp::min(iv.len(), 16) % 4;\n        if remaining > 0 {\n            let i = iv_words * 4;\n            let mut bytes = [0u8; 4];\n            bytes[..remaining].copy_from_slice(&iv[i..i + remaining]);\n            let word = u32::from_be_bytes(bytes);\n            p.ivr(iv_words).write_value(word);\n        }\n    }\n\n    /// Write a block to the SAES peripheral (blocking).\n    fn write_block_blocking(&mut self, block: &[u8]) -> Result<(), Error> {\n        let p = T::regs();\n\n        // Check for write error before starting\n        if p.sr().read().wrerr() {\n            return Err(Error::WriteError);\n        }\n\n        // Write all 4 words of the block (big-endian byte order)\n        for i in 0..4 {\n            let word = u32::from_be_bytes([block[i * 4], block[i * 4 + 1], block[i * 4 + 2], block[i * 4 + 3]]);\n            p.dinr().write_value(word);\n        }\n\n        Ok(())\n    }\n\n    /// Read a block from the SAES peripheral (blocking).\n    fn read_block_blocking(&mut self, block: &mut [u8]) -> Result<(), Error> {\n        let p = T::regs();\n\n        // Wait for CCF (Computation Complete Flag) in ISR — BUSY is not a reliable completion\n        // signal as it also clears during init/RNG-fetch/key-transfer.\n        while !p.isr().read().ccf() {}\n\n        // Check for read/write error flag in ISR before reading output\n        if p.isr().read().rweif() {\n            p.icr().write(|w| w.0 = 0xFFFF_FFFF);\n            return Err(Error::ReadError);\n        }\n\n        // Read as 32-bit words and convert to big-endian byte arrays\n        for i in 0..4 {\n            let word = p.doutr().read();\n            let bytes = word.to_be_bytes();\n            block[i * 4..i * 4 + 4].copy_from_slice(&bytes);\n        }\n\n        // Clear flags after successful read\n        p.icr().write(|w| w.0 = 0xFFFF_FFFF);\n\n        Ok(())\n    }\n}\n\ntrait SealedInstance {\n    fn regs() -> pac::saes::Saes;\n}\n\n/// SAES instance trait.\n#[allow(private_bounds)]\npub trait Instance: SealedInstance + PeripheralType + crate::rcc::RccPeripheral + 'static + Send {\n    /// Interrupt for this SAES instance.\n    type Interrupt: interrupt::typelevel::Interrupt;\n}\n\nforeach_interrupt!(\n    ($inst:ident, saes, SAES, GLOBAL, $irq:ident) => {\n        impl Instance for peripherals::$inst {\n            type Interrupt = crate::interrupt::typelevel::$irq;\n        }\n\n        impl SealedInstance for peripherals::$inst {\n            fn regs() -> crate::pac::saes::Saes {\n                crate::pac::$inst\n            }\n        }\n    };\n);\n\ndma_trait!(DmaIn, Instance);\ndma_trait!(DmaOut, Instance);\n"
  },
  {
    "path": "embassy-stm32/src/sai/mod.rs",
    "content": "//! Serial Audio Interface (SAI)\n#![macro_use]\n\nuse core::marker::PhantomData;\n\nuse embassy_hal_internal::PeripheralType;\n\npub use crate::dma::word;\nuse crate::dma::{self, Channel, ReadableRingBuffer, Request, TransferOptions, WritableRingBuffer, ringbuffer};\nuse crate::gpio::{AfType, Flex, OutputType, Pull, Speed};\npub use crate::pac::sai::vals::Mckdiv as MasterClockDivider;\nuse crate::pac::sai::{Sai as Regs, vals};\nuse crate::rcc::{self, RccPeripheral};\nuse crate::{Peri, interrupt, peripherals};\n\n/// SAI error\n#[derive(Debug, PartialEq, Eq, Clone, Copy)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub enum Error {\n    /// `write` called on a SAI in receive mode.\n    NotATransmitter,\n    /// `read` called on a SAI in transmit mode.\n    NotAReceiver,\n    /// Overrun\n    Overrun,\n}\n\nimpl From<ringbuffer::Error> for Error {\n    fn from(#[allow(unused)] err: ringbuffer::Error) -> Self {\n        #[cfg(feature = \"defmt\")]\n        {\n            if err == ringbuffer::Error::DmaUnsynced {\n                defmt::error!(\"Ringbuffer broken invariants detected!\");\n            }\n        }\n        Self::Overrun\n    }\n}\n\n/// Master/slave mode.\n#[derive(Copy, Clone)]\n#[allow(missing_docs)]\npub enum Mode {\n    Master,\n    Slave,\n}\n\nimpl Mode {\n    const fn mode(&self, tx_rx: TxRx) -> vals::Mode {\n        match tx_rx {\n            TxRx::Transmitter => match self {\n                Mode::Master => vals::Mode::MASTER_TX,\n                Mode::Slave => vals::Mode::SLAVE_TX,\n            },\n            TxRx::Receiver => match self {\n                Mode::Master => vals::Mode::MASTER_RX,\n                Mode::Slave => vals::Mode::SLAVE_RX,\n            },\n        }\n    }\n}\n\n/// Direction: transmit or receive\n#[derive(Copy, Clone)]\n#[allow(missing_docs)]\npub enum TxRx {\n    Transmitter,\n    Receiver,\n}\n\n/// Data slot size.\n#[derive(Copy, Clone)]\n#[allow(missing_docs)]\npub enum SlotSize {\n    DataSize,\n    /// 16 bit data length on 16 bit wide channel\n    Channel16,\n    /// 16 bit data length on 32 bit wide channel\n    Channel32,\n}\n\nimpl SlotSize {\n    const fn slotsz(&self) -> vals::Slotsz {\n        match self {\n            SlotSize::DataSize => vals::Slotsz::DATA_SIZE,\n            SlotSize::Channel16 => vals::Slotsz::BIT16,\n            SlotSize::Channel32 => vals::Slotsz::BIT32,\n        }\n    }\n}\n\n/// Data size.\n#[derive(Copy, Clone)]\n#[allow(missing_docs)]\npub enum DataSize {\n    Data8,\n    Data10,\n    Data16,\n    Data20,\n    Data24,\n    Data32,\n}\n\nimpl DataSize {\n    const fn ds(&self) -> vals::Ds {\n        match self {\n            DataSize::Data8 => vals::Ds::BIT8,\n            DataSize::Data10 => vals::Ds::BIT10,\n            DataSize::Data16 => vals::Ds::BIT16,\n            DataSize::Data20 => vals::Ds::BIT20,\n            DataSize::Data24 => vals::Ds::BIT24,\n            DataSize::Data32 => vals::Ds::BIT32,\n        }\n    }\n}\n\n/// FIFO threshold level.\n#[derive(Copy, Clone)]\n#[allow(missing_docs)]\npub enum FifoThreshold {\n    Empty,\n    Quarter,\n    Half,\n    ThreeQuarters,\n    Full,\n}\n\nimpl FifoThreshold {\n    const fn fth(&self) -> vals::Fth {\n        match self {\n            FifoThreshold::Empty => vals::Fth::EMPTY,\n            FifoThreshold::Quarter => vals::Fth::QUARTER1,\n            FifoThreshold::Half => vals::Fth::QUARTER2,\n            FifoThreshold::ThreeQuarters => vals::Fth::QUARTER3,\n            FifoThreshold::Full => vals::Fth::FULL,\n        }\n    }\n}\n\n/// Output value on mute.\n#[derive(Copy, Clone)]\n#[allow(missing_docs)]\npub enum MuteValue {\n    Zero,\n    LastValue,\n}\n\nimpl MuteValue {\n    const fn muteval(&self) -> vals::Muteval {\n        match self {\n            MuteValue::Zero => vals::Muteval::SEND_ZERO,\n            MuteValue::LastValue => vals::Muteval::SEND_LAST,\n        }\n    }\n}\n\n/// Protocol variant to use.\n#[derive(Copy, Clone)]\n#[allow(missing_docs)]\npub enum Protocol {\n    Free,\n    Spdif,\n    Ac97,\n}\n\nimpl Protocol {\n    const fn prtcfg(&self) -> vals::Prtcfg {\n        match self {\n            Protocol::Free => vals::Prtcfg::FREE,\n            Protocol::Spdif => vals::Prtcfg::SPDIF,\n            Protocol::Ac97 => vals::Prtcfg::AC97,\n        }\n    }\n}\n\n/// Sync input between SAI units/blocks.\n#[derive(Copy, Clone, PartialEq)]\n#[allow(missing_docs)]\npub enum SyncInput {\n    /// Not synced to any other SAI unit.\n    None,\n    /// Syncs with the other A/B sub-block within the SAI unit\n    Internal,\n    /// Syncs with a sub-block in the other SAI unit\n    #[cfg(any(sai_v3, sai_v4))]\n    External(SyncInputInstance),\n}\n\nimpl SyncInput {\n    const fn syncen(&self) -> vals::Syncen {\n        match self {\n            SyncInput::None => vals::Syncen::ASYNCHRONOUS,\n            SyncInput::Internal => vals::Syncen::INTERNAL,\n            #[cfg(any(sai_v3, sai_v4))]\n            SyncInput::External(_) => vals::Syncen::EXTERNAL,\n        }\n    }\n}\n\n/// SAI instance to sync from.\n#[cfg(any(sai_v3, sai_v4))]\n#[derive(Copy, Clone, PartialEq)]\n#[allow(missing_docs)]\npub enum SyncInputInstance {\n    #[cfg(peri_sai1)]\n    Sai1 = 0,\n    #[cfg(peri_sai2)]\n    Sai2 = 1,\n    #[cfg(peri_sai3)]\n    Sai3 = 2,\n    #[cfg(peri_sai4)]\n    Sai4 = 3,\n}\n\n/// Channels (stereo or mono).\n#[derive(Copy, Clone, PartialEq)]\n#[allow(missing_docs)]\npub enum StereoMono {\n    Stereo,\n    Mono,\n}\n\nimpl StereoMono {\n    const fn mono(&self) -> vals::Mono {\n        match self {\n            StereoMono::Stereo => vals::Mono::STEREO,\n            StereoMono::Mono => vals::Mono::MONO,\n        }\n    }\n}\n\n/// Bit order\n#[derive(Copy, Clone)]\npub enum BitOrder {\n    /// Least significant bit first.\n    LsbFirst,\n    /// Most significant bit first.\n    MsbFirst,\n}\n\nimpl BitOrder {\n    const fn lsbfirst(&self) -> vals::Lsbfirst {\n        match self {\n            BitOrder::LsbFirst => vals::Lsbfirst::LSB_FIRST,\n            BitOrder::MsbFirst => vals::Lsbfirst::MSB_FIRST,\n        }\n    }\n}\n\n/// Frame sync offset.\n#[derive(Copy, Clone)]\npub enum FrameSyncOffset {\n    /// This is used in modes other than standard I2S phillips mode\n    OnFirstBit,\n    /// This is used in standard I2S phillips mode\n    BeforeFirstBit,\n}\n\nimpl FrameSyncOffset {\n    const fn fsoff(&self) -> vals::Fsoff {\n        match self {\n            FrameSyncOffset::OnFirstBit => vals::Fsoff::ON_FIRST,\n            FrameSyncOffset::BeforeFirstBit => vals::Fsoff::BEFORE_FIRST,\n        }\n    }\n}\n\n/// Frame sync polarity\n#[derive(Copy, Clone)]\npub enum FrameSyncPolarity {\n    /// Sync signal is active low.\n    ActiveLow,\n    /// Sync signal is active high\n    ActiveHigh,\n}\n\nimpl FrameSyncPolarity {\n    const fn fspol(&self) -> vals::Fspol {\n        match self {\n            FrameSyncPolarity::ActiveLow => vals::Fspol::FALLING_EDGE,\n            FrameSyncPolarity::ActiveHigh => vals::Fspol::RISING_EDGE,\n        }\n    }\n}\n\n/// Sync definition.\n#[derive(Copy, Clone)]\n#[allow(missing_docs)]\npub enum FrameSyncDefinition {\n    StartOfFrame,\n    ChannelIdentification,\n}\n\nimpl FrameSyncDefinition {\n    const fn fsdef(&self) -> bool {\n        match self {\n            FrameSyncDefinition::StartOfFrame => false,\n            FrameSyncDefinition::ChannelIdentification => true,\n        }\n    }\n}\n\n/// Clock strobe.\n#[derive(Copy, Clone)]\n#[allow(missing_docs)]\npub enum ClockStrobe {\n    Falling,\n    Rising,\n}\n\nimpl ClockStrobe {\n    const fn ckstr(&self) -> vals::Ckstr {\n        match self {\n            ClockStrobe::Falling => vals::Ckstr::FALLING_EDGE,\n            ClockStrobe::Rising => vals::Ckstr::RISING_EDGE,\n        }\n    }\n}\n\n/// Complements format for negative samples.\n#[derive(Copy, Clone)]\n#[allow(missing_docs)]\npub enum ComplementFormat {\n    OnesComplement,\n    TwosComplement,\n}\n\nimpl ComplementFormat {\n    const fn cpl(&self) -> vals::Cpl {\n        match self {\n            ComplementFormat::OnesComplement => vals::Cpl::ONES_COMPLEMENT,\n            ComplementFormat::TwosComplement => vals::Cpl::TWOS_COMPLEMENT,\n        }\n    }\n}\n\n/// Companding setting.\n#[derive(Copy, Clone)]\n#[allow(missing_docs)]\npub enum Companding {\n    None,\n    MuLaw,\n    ALaw,\n}\n\nimpl Companding {\n    const fn comp(&self) -> vals::Comp {\n        match self {\n            Companding::None => vals::Comp::NO_COMPANDING,\n            Companding::MuLaw => vals::Comp::MU_LAW,\n            Companding::ALaw => vals::Comp::ALAW,\n        }\n    }\n}\n\n/// Output drive\n#[derive(Copy, Clone)]\n#[allow(missing_docs)]\npub enum OutputDrive {\n    OnStart,\n    Immediately,\n}\n\nimpl OutputDrive {\n    const fn outdriv(&self) -> vals::Outdriv {\n        match self {\n            OutputDrive::OnStart => vals::Outdriv::ON_START,\n            OutputDrive::Immediately => vals::Outdriv::IMMEDIATELY,\n        }\n    }\n}\n\n/// [`Sai`] configuration.\n#[allow(missing_docs)]\n#[non_exhaustive]\n#[derive(Copy, Clone)]\npub struct Config {\n    pub mode: Mode,\n    pub tx_rx: TxRx,\n    pub sync_input: SyncInput,\n    pub sync_output: bool,\n    pub protocol: Protocol,\n    pub slot_size: SlotSize,\n    pub slot_count: word::U4,\n    pub slot_enable: u16,\n    pub first_bit_offset: word::U5,\n    pub data_size: DataSize,\n    pub stereo_mono: StereoMono,\n    pub bit_order: BitOrder,\n    pub frame_sync_offset: FrameSyncOffset,\n    pub frame_sync_polarity: FrameSyncPolarity,\n    pub frame_sync_active_level_length: word::U7,\n    pub frame_sync_definition: FrameSyncDefinition,\n    pub frame_length: u16,\n    pub clock_strobe: ClockStrobe,\n    pub output_drive: OutputDrive,\n    pub master_clock_divider: MasterClockDivider,\n    pub nodiv: bool,\n    pub is_high_impedance_on_inactive_slot: bool,\n    pub fifo_threshold: FifoThreshold,\n    pub companding: Companding,\n    pub complement_format: ComplementFormat,\n    pub mute_value: MuteValue,\n    pub mute_detection_counter: word::U5,\n}\n\nimpl Default for Config {\n    fn default() -> Self {\n        Self {\n            mode: Mode::Master,\n            tx_rx: TxRx::Transmitter,\n            sync_output: false,\n            sync_input: SyncInput::None,\n            protocol: Protocol::Free,\n            slot_size: SlotSize::DataSize,\n            slot_count: word::U4(2),\n            first_bit_offset: word::U5(0),\n            slot_enable: 0b11,\n            data_size: DataSize::Data16,\n            stereo_mono: StereoMono::Stereo,\n            bit_order: BitOrder::LsbFirst,\n            frame_sync_offset: FrameSyncOffset::BeforeFirstBit,\n            frame_sync_polarity: FrameSyncPolarity::ActiveLow,\n            frame_sync_active_level_length: word::U7(16),\n            frame_sync_definition: FrameSyncDefinition::ChannelIdentification,\n            frame_length: 32,\n            master_clock_divider: MasterClockDivider::DIV1,\n            nodiv: false,\n            clock_strobe: ClockStrobe::Rising,\n            output_drive: OutputDrive::Immediately,\n            is_high_impedance_on_inactive_slot: false,\n            fifo_threshold: FifoThreshold::ThreeQuarters,\n            companding: Companding::None,\n            complement_format: ComplementFormat::TwosComplement,\n            mute_value: MuteValue::Zero,\n            mute_detection_counter: word::U5(4),\n        }\n    }\n}\n\nimpl Config {\n    /// Create a new config with all default values.\n    pub fn new() -> Self {\n        return Default::default();\n    }\n}\n\nenum RingBuffer<'d, W: word::Word> {\n    Writable(WritableRingBuffer<'d, W>),\n    Readable(ReadableRingBuffer<'d, W>),\n}\n\nfn dr<W: word::Word>(w: crate::pac::sai::Sai, sub_block: WhichSubBlock) -> *mut W {\n    let ch = w.ch(sub_block as usize);\n    ch.dr().as_ptr() as _\n}\n\n// return the type for (sd, sck)\nfn get_af_types(mode: Mode, tx_rx: TxRx) -> (AfType, AfType) {\n    (\n        //sd is defined by tx/rx mode\n        match tx_rx {\n            TxRx::Transmitter => AfType::output(OutputType::PushPull, Speed::VeryHigh),\n            TxRx::Receiver => AfType::input(Pull::Down), // Ensure mute level when no input is connected.\n        },\n        //clocks (mclk, sck and fs) are defined by master/slave\n        match mode {\n            Mode::Master => AfType::output(OutputType::PushPull, Speed::VeryHigh),\n            Mode::Slave => AfType::input(Pull::Down), // Ensure no clocks when no input is connected.\n        },\n    )\n}\n\nfn get_ring_buffer<'d, T: Instance, W: word::Word>(\n    dma: Channel<'d>,\n    dma_buf: &'d mut [W],\n    request: Request,\n    sub_block: WhichSubBlock,\n    tx_rx: TxRx,\n) -> RingBuffer<'d, W> {\n    let opts = TransferOptions {\n        half_transfer_ir: true,\n        //the new_write() and new_read() always use circular mode\n        ..Default::default()\n    };\n    match tx_rx {\n        TxRx::Transmitter => RingBuffer::Writable(unsafe {\n            WritableRingBuffer::new(dma, request, dr(T::REGS, sub_block), dma_buf, opts)\n        }),\n        TxRx::Receiver => RingBuffer::Readable(unsafe {\n            ReadableRingBuffer::new(dma, request, dr(T::REGS, sub_block), dma_buf, opts)\n        }),\n    }\n}\n\nfn update_synchronous_config(config: &mut Config) {\n    config.mode = Mode::Slave;\n    config.sync_output = false;\n\n    #[cfg(any(sai_v1, sai_v1_4pdm, sai_v2))]\n    {\n        config.sync_input = SyncInput::Internal;\n    }\n\n    #[cfg(any(sai_v3, sai_v4))]\n    {\n        //this must either be Internal or External\n        //The asynchronous sub-block on the same SAI needs to enable sync_output\n        assert!(config.sync_input != SyncInput::None);\n    }\n}\n\n/// SAI subblock instance.\npub struct SubBlock<'d, T: Instance, S: SubBlockInstance> {\n    peri: Peri<'d, T>,\n    _phantom: PhantomData<S>,\n}\n\n/// Split the main SAIx peripheral into the two subblocks.\n///\n/// You can then create a [`Sai`] driver for each each half.\npub fn split_subblocks<'d, T: Instance>(peri: Peri<'d, T>) -> (SubBlock<'d, T, A>, SubBlock<'d, T, B>) {\n    rcc::enable_and_reset::<T>();\n\n    (\n        SubBlock {\n            peri: unsafe { peri.clone_unchecked() },\n            _phantom: PhantomData,\n        },\n        SubBlock {\n            peri,\n            _phantom: PhantomData,\n        },\n    )\n}\n\n/// SAI sub-block driver.\npub struct Sai<'d, T: Instance, W: word::Word> {\n    _peri: Peri<'d, T>,\n    _sd: Option<Flex<'d>>,\n    _fs: Option<Flex<'d>>,\n    _sck: Option<Flex<'d>>,\n    _mclk: Option<Flex<'d>>,\n    ring_buffer: RingBuffer<'d, W>,\n    sub_block: WhichSubBlock,\n}\n\nimpl<'d, T: Instance, W: word::Word> Sai<'d, T, W> {\n    /// Create a new SAI driver in asynchronous mode with MCLK.\n    ///\n    /// You can obtain the [`SubBlock`] with [`split_subblocks`].\n    pub fn new_asynchronous_with_mclk<S: SubBlockInstance, D: Dma<T, S>>(\n        peri: SubBlock<'d, T, S>,\n        sck: Peri<'d, impl SckPin<T, S>>,\n        sd: Peri<'d, impl SdPin<T, S>>,\n        fs: Peri<'d, impl FsPin<T, S>>,\n        mclk: Peri<'d, impl MclkPin<T, S>>,\n        dma: Peri<'d, D>,\n        dma_buf: &'d mut [W],\n        _irq: impl interrupt::typelevel::Binding<D::Interrupt, dma::InterruptHandler<D>> + 'd,\n        config: Config,\n    ) -> Self {\n        let (_sd_af_type, ck_af_type) = get_af_types(config.mode, config.tx_rx);\n        set_as_af!(mclk, ck_af_type);\n\n        Self::new_asynchronous(peri, sck, sd, fs, dma, dma_buf, _irq, config)\n    }\n\n    /// Create a new SAI driver in asynchronous mode without MCLK.\n    ///\n    /// You can obtain the [`SubBlock`] with [`split_subblocks`].\n    pub fn new_asynchronous<S: SubBlockInstance, D: Dma<T, S>>(\n        peri: SubBlock<'d, T, S>,\n        sck: Peri<'d, impl SckPin<T, S>>,\n        sd: Peri<'d, impl SdPin<T, S>>,\n        fs: Peri<'d, impl FsPin<T, S>>,\n        dma: Peri<'d, D>,\n        dma_buf: &'d mut [W],\n        irq: impl interrupt::typelevel::Binding<D::Interrupt, dma::InterruptHandler<D>> + 'd,\n        config: Config,\n    ) -> Self {\n        let peri = peri.peri;\n\n        let (sd_af_type, ck_af_type) = get_af_types(config.mode, config.tx_rx);\n\n        let sub_block = S::WHICH;\n        let request = dma.request();\n\n        Self::new_inner(\n            peri,\n            sub_block,\n            new_pin!(sck, ck_af_type),\n            None,\n            new_pin!(sd, sd_af_type),\n            new_pin!(fs, ck_af_type),\n            get_ring_buffer::<T, W>(Channel::new(dma, irq), dma_buf, request, sub_block, config.tx_rx),\n            config,\n        )\n    }\n\n    /// Create a new SAI driver in synchronous mode.\n    ///\n    /// You can obtain the [`SubBlock`] with [`split_subblocks`].\n    pub fn new_synchronous<S: SubBlockInstance, D: Dma<T, S>>(\n        peri: SubBlock<'d, T, S>,\n        sd: Peri<'d, impl SdPin<T, S>>,\n        dma: Peri<'d, D>,\n        dma_buf: &'d mut [W],\n        irq: impl interrupt::typelevel::Binding<D::Interrupt, dma::InterruptHandler<D>> + 'd,\n        mut config: Config,\n    ) -> Self {\n        update_synchronous_config(&mut config);\n\n        let peri = peri.peri;\n\n        let (sd_af_type, _ck_af_type) = get_af_types(config.mode, config.tx_rx);\n\n        let sub_block = S::WHICH;\n        let request = dma.request();\n\n        Self::new_inner(\n            peri,\n            sub_block,\n            None,\n            None,\n            new_pin!(sd, sd_af_type),\n            None,\n            get_ring_buffer::<T, W>(Channel::new(dma, irq), dma_buf, request, sub_block, config.tx_rx),\n            config,\n        )\n    }\n\n    fn new_inner(\n        peri: Peri<'d, T>,\n        sub_block: WhichSubBlock,\n        sck: Option<Flex<'d>>,\n        mclk: Option<Flex<'d>>,\n        sd: Option<Flex<'d>>,\n        fs: Option<Flex<'d>>,\n        ring_buffer: RingBuffer<'d, W>,\n        config: Config,\n    ) -> Self {\n        let ch = T::REGS.ch(sub_block as usize);\n\n        ch.cr1().modify(|w| w.set_saien(false));\n\n        ch.cr2().modify(|w| w.set_fflush(true));\n\n        #[cfg(any(sai_v3, sai_v4))]\n        {\n            if let SyncInput::External(i) = config.sync_input {\n                T::REGS.gcr().modify(|w| {\n                    w.set_syncin(i as u8);\n                });\n            }\n\n            if config.sync_output {\n                let syncout: u8 = match sub_block {\n                    WhichSubBlock::A => 0b01,\n                    WhichSubBlock::B => 0b10,\n                };\n                T::REGS.gcr().modify(|w| {\n                    w.set_syncout(syncout);\n                });\n            }\n        }\n\n        ch.cr1().modify(|w| {\n            w.set_mode(config.mode.mode(if Self::is_transmitter(&ring_buffer) {\n                TxRx::Transmitter\n            } else {\n                TxRx::Receiver\n            }));\n            w.set_prtcfg(config.protocol.prtcfg());\n            w.set_ds(config.data_size.ds());\n            w.set_lsbfirst(config.bit_order.lsbfirst());\n            w.set_ckstr(config.clock_strobe.ckstr());\n            w.set_syncen(config.sync_input.syncen());\n            w.set_mono(config.stereo_mono.mono());\n            w.set_outdriv(config.output_drive.outdriv());\n            w.set_mckdiv(config.master_clock_divider);\n            w.set_nodiv(config.nodiv);\n            w.set_dmaen(true);\n        });\n\n        ch.cr2().modify(|w| {\n            w.set_fth(config.fifo_threshold.fth());\n            w.set_comp(config.companding.comp());\n            w.set_cpl(config.complement_format.cpl());\n            w.set_muteval(config.mute_value.muteval());\n            w.set_mutecnt(config.mute_detection_counter.0 as u8);\n            w.set_tris(config.is_high_impedance_on_inactive_slot);\n        });\n\n        ch.frcr().modify(|w| {\n            w.set_fsoff(config.frame_sync_offset.fsoff());\n            w.set_fspol(config.frame_sync_polarity.fspol());\n            w.set_fsdef(config.frame_sync_definition.fsdef());\n            w.set_fsall(config.frame_sync_active_level_length.0 as u8 - 1);\n            w.set_frl((config.frame_length - 1).try_into().unwrap());\n        });\n\n        ch.slotr().modify(|w| {\n            w.set_nbslot(config.slot_count.0 as u8 - 1);\n            w.set_slotsz(config.slot_size.slotsz());\n            w.set_fboff(config.first_bit_offset.0 as u8);\n            w.set_sloten(vals::Sloten::from_bits(config.slot_enable as u16));\n        });\n\n        ch.cr1().modify(|w| w.set_saien(true));\n\n        if ch.cr1().read().saien() == false {\n            panic!(\"SAI failed to enable. Check that config is valid (frame length, slot count, etc)\");\n        }\n\n        Self {\n            _peri: peri,\n            sub_block,\n            _sck: sck,\n            _mclk: mclk,\n            _sd: sd,\n            _fs: fs,\n            ring_buffer,\n        }\n    }\n\n    /// Start the SAI driver.\n    ///\n    /// Only receivers can be started. Transmitters are started on the first writing operation.\n    pub fn start(&mut self) -> Result<(), Error> {\n        match self.ring_buffer {\n            RingBuffer::Writable(_) => Err(Error::NotAReceiver),\n            RingBuffer::Readable(ref mut rb) => {\n                rb.start();\n                Ok(())\n            }\n        }\n    }\n\n    fn is_transmitter(ring_buffer: &RingBuffer<W>) -> bool {\n        match ring_buffer {\n            RingBuffer::Writable(_) => true,\n            _ => false,\n        }\n    }\n\n    /// Reset SAI operation.\n    pub fn reset() {\n        rcc::enable_and_reset::<T>();\n    }\n\n    /// Enable or disable mute.\n    pub fn set_mute(&mut self, value: bool) {\n        let ch = T::REGS.ch(self.sub_block as usize);\n        ch.cr2().modify(|w| w.set_mute(value));\n    }\n\n    /// Determine the mute state of the receiver.\n    ///\n    /// Clears the mute state flag in the status register.\n    pub fn is_muted(&self) -> Result<bool, Error> {\n        match &self.ring_buffer {\n            RingBuffer::Readable(_) => {\n                let ch = T::REGS.ch(self.sub_block as usize);\n                let mute_state = ch.sr().read().mutedet();\n                ch.clrfr().write(|w| w.set_cmutedet(true));\n                Ok(mute_state)\n            }\n            _ => Err(Error::NotAReceiver),\n        }\n    }\n\n    /// Wait until any SAI write error occurs.\n    ///\n    /// One useful application for this is stopping playback as soon as the SAI\n    /// experiences an overrun of the ring buffer. Then, instead of letting\n    /// the SAI peripheral play the last written buffer over and over again, SAI\n    /// can be muted or dropped instead.\n    pub async fn wait_write_error(&mut self) -> Result<(), Error> {\n        match &mut self.ring_buffer {\n            RingBuffer::Writable(buffer) => {\n                buffer.wait_write_error().await?;\n                Ok(())\n            }\n            _ => return Err(Error::NotATransmitter),\n        }\n    }\n\n    /// Write data to the SAI ringbuffer.\n    ///\n    /// The first write starts the DMA after filling the ring buffer with the provided data.\n    /// This ensures that the DMA does not run before data is available in the ring buffer.\n    ///\n    /// This appends the data to the buffer and returns immediately. The\n    /// data will be transmitted in the background.\n    ///\n    /// If there's no space in the buffer, this waits until there is.\n    pub async fn write(&mut self, data: &[W]) -> Result<(), Error> {\n        match &mut self.ring_buffer {\n            RingBuffer::Writable(buffer) => {\n                if buffer.is_running() {\n                    buffer.write_exact(data).await?;\n                } else {\n                    buffer.write_immediate(data)?;\n                    buffer.start();\n                }\n                Ok(())\n            }\n            _ => return Err(Error::NotATransmitter),\n        }\n    }\n\n    /// Read data from the SAI ringbuffer.\n    ///\n    /// SAI is always receiving data in the background. This function pops already-received\n    /// data from the buffer.\n    ///\n    /// If there's less than `data.len()` data in the buffer, this waits until there is.\n    pub async fn read(&mut self, data: &mut [W]) -> Result<(), Error> {\n        match &mut self.ring_buffer {\n            RingBuffer::Readable(buffer) => {\n                buffer.read_exact(data).await?;\n                Ok(())\n            }\n            _ => Err(Error::NotAReceiver),\n        }\n    }\n}\n\nimpl<'d, T: Instance, W: word::Word> Drop for Sai<'d, T, W> {\n    fn drop(&mut self) {\n        let ch = T::REGS.ch(self.sub_block as usize);\n        ch.cr1().modify(|w| w.set_saien(false));\n        ch.cr2().modify(|w| w.set_fflush(true));\n    }\n}\n\ntrait SealedInstance {\n    const REGS: Regs;\n}\n\n#[derive(Copy, Clone)]\nenum WhichSubBlock {\n    A = 0,\n    B = 1,\n}\n\ntrait SealedSubBlock {\n    const WHICH: WhichSubBlock;\n}\n\n/// Sub-block instance trait.\n#[allow(private_bounds)]\npub trait SubBlockInstance: SealedSubBlock {}\n\n/// Sub-block A.\npub enum A {}\nimpl SealedSubBlock for A {\n    const WHICH: WhichSubBlock = WhichSubBlock::A;\n}\nimpl SubBlockInstance for A {}\n\n/// Sub-block B.\npub enum B {}\nimpl SealedSubBlock for B {\n    const WHICH: WhichSubBlock = WhichSubBlock::B;\n}\nimpl SubBlockInstance for B {}\n\n/// SAI instance trait.\n#[allow(private_bounds)]\npub trait Instance: SealedInstance + PeripheralType + RccPeripheral {}\n\npin_trait!(SckPin, Instance, SubBlockInstance);\npin_trait!(FsPin, Instance, SubBlockInstance);\npin_trait!(SdPin, Instance, SubBlockInstance);\npin_trait!(MclkPin, Instance, SubBlockInstance);\n\ndma_trait!(Dma, Instance, SubBlockInstance);\n\nforeach_peripheral!(\n    (sai, $inst:ident) => {\n        impl SealedInstance for peripherals::$inst {\n            const REGS: Regs = crate::pac::$inst;\n        }\n\n        impl Instance for peripherals::$inst {}\n    };\n);\n"
  },
  {
    "path": "embassy-stm32/src/sdmmc/mod.rs",
    "content": "//! Secure Digital / MultiMedia Card (SDMMC)\n#![macro_use]\n\nuse core::default::Default;\nuse core::future::poll_fn;\nuse core::marker::PhantomData;\nuse core::slice;\nuse core::sync::atomic::{Ordering, fence};\nuse core::task::Poll;\n\nuse aligned::{A4, Aligned};\nuse embassy_hal_internal::{Peri, PeripheralType};\nuse embassy_sync::waitqueue::AtomicWaker;\nuse sdio_host::Cmd;\nuse sdio_host::common_cmd::{self, R1, R2, Resp, ResponseLen, Rz};\nuse sdio_host::sd::{BusWidth, CID, CSD, CardStatus};\n\n#[cfg(sdmmc_v1)]\nuse crate::dma::ChannelAndRequest;\n#[cfg(gpio_v2)]\nuse crate::gpio::Pull;\nuse crate::gpio::{AfType, Flex, OutputType, Speed};\nuse crate::interrupt::typelevel::Interrupt;\nuse crate::pac::sdmmc::Sdmmc as RegBlock;\nuse crate::rcc::{self, RccInfo, RccPeripheral, SealedRccPeripheral};\nuse crate::time::Hertz;\nuse crate::{block_for_us, interrupt, peripherals};\n\n/// Module for SD and EMMC cards\npub mod sd;\n\n/// Module for SDIO interface\npub mod sdio;\n\n/// Interrupt handler.\npub struct InterruptHandler<T: Instance> {\n    _phantom: PhantomData<T>,\n}\n\nimpl<T: Instance> interrupt::typelevel::Handler<T::Interrupt> for InterruptHandler<T> {\n    unsafe fn on_interrupt() {\n        T::state().tx_waker.wake();\n        let status = T::info().regs.star().read();\n        #[cfg(sdmmc_v1)]\n        if status.dcrcfail() || status.dtimeout() || status.dataend() || status.dbckend() || status.stbiterr() {\n            T::state().tx_waker.wake();\n        }\n\n        #[cfg(sdmmc_v2)]\n        if status.dcrcfail() || status.dtimeout() || status.dataend() || status.dbckend() || status.dabort() {\n            T::state().tx_waker.wake();\n        }\n\n        if status.sdioit() {\n            T::state().it_waker.wake();\n        }\n\n        T::info().regs.maskr().modify(|w| {\n            if status.sdioit() {\n                w.set_sdioitie(false);\n            }\n            if status.dcrcfail() {\n                w.set_dcrcfailie(false);\n            }\n            if status.dtimeout() {\n                w.set_dtimeoutie(false);\n            }\n            if status.dataend() {\n                w.set_dataendie(false);\n            }\n            if status.dbckend() {\n                w.set_dbckendie(false);\n            }\n            #[cfg(sdmmc_v1)]\n            if status.stbiterr() {\n                w.set_stbiterre(false);\n            }\n            #[cfg(sdmmc_v2)]\n            if status.dabort() {\n                w.set_dabortie(false);\n            }\n        });\n    }\n}\n\nstruct U128(pub u128);\n\nstruct CommandResponse<R: TypedResp>(R::Word);\n\ntrait TypedResp: Resp {\n    type Word: From<U128>;\n}\n\nimpl From<U128> for () {\n    fn from(value: U128) -> Self {\n        match value.0 {\n            0 => (),\n            _ => unreachable!(),\n        }\n    }\n}\n\nimpl From<U128> for u32 {\n    fn from(value: U128) -> Self {\n        value.0.try_into().unwrap()\n    }\n}\n\nimpl From<U128> for u128 {\n    fn from(value: U128) -> Self {\n        value.0\n    }\n}\n\nimpl TypedResp for Rz {\n    type Word = ();\n}\n\nimpl TypedResp for R1 {\n    type Word = u32;\n}\n\nimpl<E> From<CommandResponse<R1>> for CardStatus<E> {\n    fn from(value: CommandResponse<R1>) -> Self {\n        CardStatus::<E>::from(value.0)\n    }\n}\n\nimpl TypedResp for R2 {\n    type Word = u128;\n}\n\nimpl<E> From<CommandResponse<R2>> for CID<E> {\n    fn from(value: CommandResponse<R2>) -> Self {\n        CID::<E>::from(value.0)\n    }\n}\n\nimpl<E> From<CommandResponse<R2>> for CSD<E> {\n    fn from(value: CommandResponse<R2>) -> Self {\n        CSD::<E>::from(value.0)\n    }\n}\n\n/// Frequency used for SD Card initialization. Must be no higher than 400 kHz.\nconst SD_INIT_FREQ: Hertz = Hertz(400_000);\n\n/// The signalling scheme used on the SDMMC bus\n#[non_exhaustive]\n#[allow(missing_docs)]\n#[derive(Debug, Copy, Clone, PartialEq, Eq)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub enum Signalling {\n    SDR12,\n    SDR25,\n    SDR50,\n    SDR104,\n    DDR50,\n}\n\nimpl Default for Signalling {\n    fn default() -> Self {\n        Signalling::SDR12\n    }\n}\n\nconst fn aligned_mut(x: &mut [u32]) -> &mut Aligned<A4, [u8]> {\n    let len = x.len() * 4;\n    unsafe { core::mem::transmute(slice::from_raw_parts_mut(x.as_mut_ptr() as *mut u8, len)) }\n}\n\nconst fn slice8_mut(x: &mut [u32]) -> &mut [u8] {\n    let len = x.len() * 4;\n    unsafe { slice::from_raw_parts_mut(x.as_mut_ptr() as *mut u8, len) }\n}\n\n#[allow(unused)]\nconst fn slice32_mut(x: &mut Aligned<A4, [u8]>) -> &mut [u32] {\n    let len = (size_of_val(x) + 4 - 1) / 4;\n    unsafe { slice::from_raw_parts_mut(x as *mut Aligned<A4, [u8]> as *mut u32, len) }\n}\n\nconst fn aligned_ref(x: &[u32]) -> &Aligned<A4, [u8]> {\n    let len = x.len() * 4;\n    unsafe { core::mem::transmute(slice::from_raw_parts(x.as_ptr() as *const u8, len)) }\n}\n\nconst fn slice8_ref(x: &[u32]) -> &[u8] {\n    let len = x.len() * 4;\n    unsafe { slice::from_raw_parts(x.as_ptr() as *const u8, len) }\n}\n\n#[allow(unused)]\nconst fn slice32_ref(x: &Aligned<A4, [u8]>) -> &[u32] {\n    let len = (size_of_val(x) + 4 - 1) / 4;\n    unsafe { slice::from_raw_parts(x as *const Aligned<A4, [u8]> as *const u32, len) }\n}\n\n/// Errors\n#[non_exhaustive]\n#[derive(Debug, Copy, Clone, PartialEq, Eq)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub enum Error {\n    /// Timeout reported by the hardware\n    Timeout,\n    /// Timeout reported by the software driver.\n    SoftwareTimeout,\n    /// Unsupported card version.\n    UnsupportedCardVersion,\n    /// Unsupported card type.\n    UnsupportedCardType,\n    /// Unsupported voltage.\n    UnsupportedVoltage,\n    /// CRC error.\n    Crc,\n    /// No card inserted.\n    NoCard,\n    /// 8-lane buses are not supported for SD cards.\n    BusWidth,\n    /// Bad clock supplied to the SDMMC peripheral.\n    BadClock,\n    /// Signaling switch failed.\n    SignalingSwitchFailed,\n    /// Underrun error\n    Underrun,\n    /// ST bit error.\n    #[cfg(sdmmc_v1)]\n    StBitErr,\n}\n\n#[repr(u8)]\nenum PowerCtrl {\n    Off = 0b00,\n    On = 0b11,\n}\n\nenum DatapathMode {\n    Block(BlockSize),\n    Byte,\n}\n\nfn get_waitresp_val(rlen: ResponseLen) -> u8 {\n    match rlen {\n        common_cmd::ResponseLen::Zero => 0,\n        common_cmd::ResponseLen::R48 => 1,\n        common_cmd::ResponseLen::R136 => 3,\n    }\n}\n\n/// Calculate clock divisor. Returns a SDMMC_CK less than or equal to\n/// `sdmmc_ck` in Hertz.\n///\n/// Returns `(bypass, clk_div, clk_f)`, where `bypass` enables clock divisor bypass (only sdmmc_v1),\n/// `clk_div` is the divisor register value and `clk_f` is the resulting new clock frequency.\n#[cfg(sdmmc_v1)]\nfn clk_div(ker_ck: Hertz, sdmmc_ck: Hertz) -> Result<(bool, u8, Hertz), Error> {\n    // sdmmc_v1 maximum clock is 50 MHz\n    if sdmmc_ck.0 > 50_000_000 {\n        return Err(Error::BadClock);\n    }\n\n    // bypass divisor\n    if ker_ck.0 <= sdmmc_ck.0 {\n        return Ok((true, 0, ker_ck));\n    }\n\n    let clk_div = match ker_ck.0.div_ceil(sdmmc_ck.0) {\n        0 | 1 => Ok(0),\n        x @ 2..=258 => Ok((x - 2) as u8),\n        _ => Err(Error::BadClock),\n    }?;\n\n    // SDIO_CK frequency = SDIOCLK / [CLKDIV + 2]\n    let clk_f = Hertz(ker_ck.0 / (clk_div as u32 + 2));\n    Ok((false, clk_div, clk_f))\n}\n\nfn bus_width_vals(bus_width: BusWidth) -> (u8, u32) {\n    match bus_width {\n        BusWidth::One => (0, 1u32),\n        BusWidth::Four => (1, 4u32),\n        BusWidth::Eight => (2, 8u32),\n        _ => panic!(\"Invalid Bus Width\"),\n    }\n}\n\n#[repr(u8)]\nenum BlockSize {\n    Size1 = 0b0000,\n    Size2 = 0b0001,\n    Size4 = 0b0010,\n    Size8 = 0b0011,\n    Size16 = 0b0100,\n    Size32 = 0b0101,\n    Size64 = 0b0110,\n    Size128 = 0b0111,\n    Size256 = 0b1000,\n    Size512 = 0b1001,\n    Size1024 = 0b1010,\n    Size2048 = 0b1011,\n    Size4096 = 0b1100,\n    Size8192 = 0b1101,\n    Size16384 = 0b1110,\n}\n\nconst fn block_size(bytes: usize) -> BlockSize {\n    match bytes {\n        1 => BlockSize::Size1,\n        2 => BlockSize::Size2,\n        4 => BlockSize::Size4,\n        8 => BlockSize::Size8,\n        16 => BlockSize::Size16,\n        32 => BlockSize::Size32,\n        64 => BlockSize::Size64,\n        128 => BlockSize::Size128,\n        256 => BlockSize::Size256,\n        512 => BlockSize::Size512,\n        1024 => BlockSize::Size1024,\n        2048 => BlockSize::Size2048,\n        4096 => BlockSize::Size4096,\n        8192 => BlockSize::Size8192,\n        16384 => BlockSize::Size16384,\n        _ => core::unreachable!(),\n    }\n}\n\n/// Calculate clock divisor. Returns a SDMMC_CK less than or equal to\n/// `sdmmc_ck` in Hertz.\n///\n/// Returns `(bypass, clk_div, clk_f)`, where `bypass` enables clock divisor bypass (only sdmmc_v1),\n/// `clk_div` is the divisor register value and `clk_f` is the resulting new clock frequency.\n#[cfg(sdmmc_v2)]\nfn clk_div(ker_ck: Hertz, sdmmc_ck: Hertz) -> Result<(bool, u16, Hertz), Error> {\n    match ker_ck.0.div_ceil(sdmmc_ck.0) {\n        0 | 1 => Ok((false, 0, ker_ck)),\n        x @ 2..=2046 => {\n            // SDMMC_CK frequency = SDMMCCLK / [CLKDIV * 2]\n            let clk_div = x.div_ceil(2) as u16;\n            let clk = Hertz(ker_ck.0 / (clk_div as u32 * 2));\n\n            Ok((false, clk_div, clk))\n        }\n        _ => Err(Error::BadClock),\n    }\n}\n\n#[cfg(sdmmc_v1)]\ntype Transfer<'a> = crate::dma::Transfer<'a>;\n#[cfg(sdmmc_v2)]\nstruct Transfer<'a> {\n    _dummy: PhantomData<&'a ()>,\n}\n\nstruct WrappedTransfer<'a> {\n    _transfer: Transfer<'a>,\n    sdmmc: &'a Sdmmc<'a>,\n    defused: bool,\n}\n\nimpl<'a> WrappedTransfer<'a> {\n    pub const fn new(_transfer: Transfer<'a>, sdmmc: &'a Sdmmc) -> Self {\n        Self {\n            _transfer,\n            sdmmc,\n            defused: false,\n        }\n    }\n\n    pub fn defuse(&mut self) {\n        self.defused = true;\n    }\n}\n\nimpl<'a> Drop for WrappedTransfer<'a> {\n    fn drop(&mut self) {\n        if !self.defused {\n            self.sdmmc.on_drop();\n        }\n    }\n}\n\n#[cfg(all(sdmmc_v1, dma))]\nconst DMA_TRANSFER_OPTIONS: crate::dma::TransferOptions = crate::dma::TransferOptions {\n    pburst: crate::dma::Burst::Incr4,\n    mburst: crate::dma::Burst::Incr4,\n    flow_ctrl: crate::dma::FlowControl::Peripheral,\n    fifo_threshold: Some(crate::dma::FifoThreshold::Full),\n    priority: crate::dma::Priority::VeryHigh,\n    circular: false,\n    half_transfer_ir: false,\n    complete_transfer_ir: true,\n};\n#[cfg(all(sdmmc_v1, not(dma)))]\nconst DMA_TRANSFER_OPTIONS: crate::dma::TransferOptions = crate::dma::TransferOptions {\n    priority: crate::dma::Priority::VeryHigh,\n    circular: false,\n    half_transfer_ir: false,\n    complete_transfer_ir: true,\n};\n\n/// SDMMC configuration\n///\n/// Default values:\n/// data_transfer_timeout: 5_000_000\n#[non_exhaustive]\npub struct Config {\n    /// The timeout to be set for data transfers, in card bus clock periods\n    pub data_transfer_timeout: u32,\n}\n\nimpl Default for Config {\n    fn default() -> Self {\n        Self {\n            data_transfer_timeout: 5_000_000,\n        }\n    }\n}\n\n/// Sdmmc device\npub struct Sdmmc<'d> {\n    info: &'static Info,\n    state: &'static State,\n    ker_clk: Hertz,\n    #[cfg(sdmmc_v1)]\n    dma: ChannelAndRequest<'d>,\n\n    _clk: Flex<'d>,\n    _cmd: Flex<'d>,\n    _d0: Flex<'d>,\n    _d1: Option<Flex<'d>>,\n    _d2: Option<Flex<'d>>,\n    d3: Option<Flex<'d>>,\n    _d4: Option<Flex<'d>>,\n    _d5: Option<Flex<'d>>,\n    _d6: Option<Flex<'d>>,\n    d7: Option<Flex<'d>>,\n\n    config: Config,\n}\n\nconst CLK_AF: AfType = AfType::output(OutputType::PushPull, Speed::VeryHigh);\n#[cfg(gpio_v1)]\nconst CMD_AF: AfType = AfType::output(OutputType::PushPull, Speed::VeryHigh);\n#[cfg(gpio_v2)]\nconst CMD_AF: AfType = AfType::output_pull(OutputType::PushPull, Speed::VeryHigh, Pull::Up);\nconst DATA_AF: AfType = CMD_AF;\n\n#[cfg(sdmmc_v1)]\nimpl<'d> Sdmmc<'d> {\n    /// Create a new SDMMC driver, with 1 data lane.\n    pub fn new_1bit<T: Instance, D: SdmmcDma<T>>(\n        sdmmc: Peri<'d, T>,\n        dma: Peri<'d, D>,\n        _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>>\n        + interrupt::typelevel::Binding<D::Interrupt, crate::dma::InterruptHandler<D>>\n        + 'd,\n        clk: Peri<'d, impl CkPin<T>>,\n        cmd: Peri<'d, impl CmdPin<T>>,\n        d0: Peri<'d, impl D0Pin<T>>,\n        config: Config,\n    ) -> Self {\n        Self::new_inner(\n            sdmmc,\n            new_dma_nonopt!(dma, _irq),\n            new_pin!(clk, CLK_AF).unwrap(),\n            new_pin!(cmd, CMD_AF).unwrap(),\n            new_pin!(d0, DATA_AF).unwrap(),\n            None,\n            None,\n            None,\n            None,\n            None,\n            None,\n            None,\n            config,\n        )\n    }\n\n    /// Create a new SDMMC driver, with 4 data lanes.\n    pub fn new_4bit<T: Instance, D: SdmmcDma<T>>(\n        sdmmc: Peri<'d, T>,\n        dma: Peri<'d, D>,\n        _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>>\n        + interrupt::typelevel::Binding<D::Interrupt, crate::dma::InterruptHandler<D>>\n        + 'd,\n        clk: Peri<'d, impl CkPin<T>>,\n        cmd: Peri<'d, impl CmdPin<T>>,\n        d0: Peri<'d, impl D0Pin<T>>,\n        d1: Peri<'d, impl D1Pin<T>>,\n        d2: Peri<'d, impl D2Pin<T>>,\n        d3: Peri<'d, impl D3Pin<T>>,\n        config: Config,\n    ) -> Self {\n        Self::new_inner(\n            sdmmc,\n            new_dma_nonopt!(dma, _irq),\n            new_pin!(clk, CLK_AF).unwrap(),\n            new_pin!(cmd, CMD_AF).unwrap(),\n            new_pin!(d0, DATA_AF).unwrap(),\n            new_pin!(d1, DATA_AF),\n            new_pin!(d2, DATA_AF),\n            new_pin!(d3, DATA_AF),\n            None,\n            None,\n            None,\n            None,\n            config,\n        )\n    }\n}\n\n#[cfg(sdmmc_v1)]\nimpl<'d> Sdmmc<'d> {\n    /// Create a new SDMMC driver, with 8 data lanes.\n    pub fn new_8bit<T: Instance, D: SdmmcDma<T>>(\n        sdmmc: Peri<'d, T>,\n        dma: Peri<'d, D>,\n        _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>>\n        + interrupt::typelevel::Binding<D::Interrupt, crate::dma::InterruptHandler<D>>\n        + 'd,\n        clk: Peri<'d, impl CkPin<T>>,\n        cmd: Peri<'d, impl CmdPin<T>>,\n        d0: Peri<'d, impl D0Pin<T>>,\n        d1: Peri<'d, impl D1Pin<T>>,\n        d2: Peri<'d, impl D2Pin<T>>,\n        d3: Peri<'d, impl D3Pin<T>>,\n        d4: Peri<'d, impl D4Pin<T>>,\n        d5: Peri<'d, impl D5Pin<T>>,\n        d6: Peri<'d, impl D6Pin<T>>,\n        d7: Peri<'d, impl D7Pin<T>>,\n        config: Config,\n    ) -> Self {\n        Self::new_inner(\n            sdmmc,\n            new_dma_nonopt!(dma, _irq),\n            new_pin!(clk, CLK_AF).unwrap(),\n            new_pin!(cmd, CMD_AF).unwrap(),\n            new_pin!(d0, DATA_AF).unwrap(),\n            new_pin!(d1, DATA_AF),\n            new_pin!(d2, DATA_AF),\n            new_pin!(d3, DATA_AF),\n            new_pin!(d4, DATA_AF),\n            new_pin!(d5, DATA_AF),\n            new_pin!(d6, DATA_AF),\n            new_pin!(d7, DATA_AF),\n            config,\n        )\n    }\n}\n\n#[cfg(sdmmc_v2)]\nimpl<'d> Sdmmc<'d> {\n    /// Create a new SDMMC driver, with 1 data lane.\n    pub fn new_1bit<T: Instance>(\n        sdmmc: Peri<'d, T>,\n        _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd,\n        clk: Peri<'d, impl CkPin<T>>,\n        cmd: Peri<'d, impl CmdPin<T>>,\n        d0: Peri<'d, impl D0Pin<T>>,\n        config: Config,\n    ) -> Self {\n        Self::new_inner(\n            sdmmc,\n            new_pin!(clk, CLK_AF).unwrap(),\n            new_pin!(cmd, CMD_AF).unwrap(),\n            new_pin!(d0, DATA_AF).unwrap(),\n            None,\n            None,\n            None,\n            None,\n            None,\n            None,\n            None,\n            config,\n        )\n    }\n\n    /// Create a new SDMMC driver, with 4 data lanes.\n    pub fn new_4bit<T: Instance>(\n        sdmmc: Peri<'d, T>,\n        _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd,\n        clk: Peri<'d, impl CkPin<T>>,\n        cmd: Peri<'d, impl CmdPin<T>>,\n        d0: Peri<'d, impl D0Pin<T>>,\n        d1: Peri<'d, impl D1Pin<T>>,\n        d2: Peri<'d, impl D2Pin<T>>,\n        d3: Peri<'d, impl D3Pin<T>>,\n        config: Config,\n    ) -> Self {\n        Self::new_inner(\n            sdmmc,\n            new_pin!(clk, CLK_AF).unwrap(),\n            new_pin!(cmd, CMD_AF).unwrap(),\n            new_pin!(d0, DATA_AF).unwrap(),\n            new_pin!(d1, DATA_AF),\n            new_pin!(d2, DATA_AF),\n            new_pin!(d3, DATA_AF),\n            None,\n            None,\n            None,\n            None,\n            config,\n        )\n    }\n}\n\n#[cfg(sdmmc_v2)]\nimpl<'d> Sdmmc<'d> {\n    /// Create a new SDMMC driver, with 8 data lanes.\n    pub fn new_8bit<T: Instance>(\n        sdmmc: Peri<'d, T>,\n        _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd,\n        clk: Peri<'d, impl CkPin<T>>,\n        cmd: Peri<'d, impl CmdPin<T>>,\n        d0: Peri<'d, impl D0Pin<T>>,\n        d1: Peri<'d, impl D1Pin<T>>,\n        d2: Peri<'d, impl D2Pin<T>>,\n        d3: Peri<'d, impl D3Pin<T>>,\n        d4: Peri<'d, impl D4Pin<T>>,\n        d5: Peri<'d, impl D5Pin<T>>,\n        d6: Peri<'d, impl D6Pin<T>>,\n        d7: Peri<'d, impl D7Pin<T>>,\n        config: Config,\n    ) -> Self {\n        Self::new_inner(\n            sdmmc,\n            new_pin!(clk, CLK_AF).unwrap(),\n            new_pin!(cmd, CMD_AF).unwrap(),\n            new_pin!(d0, DATA_AF).unwrap(),\n            new_pin!(d1, DATA_AF),\n            new_pin!(d2, DATA_AF),\n            new_pin!(d3, DATA_AF),\n            new_pin!(d4, DATA_AF),\n            new_pin!(d5, DATA_AF),\n            new_pin!(d6, DATA_AF),\n            new_pin!(d7, DATA_AF),\n            config,\n        )\n    }\n}\n\nimpl<'d> Sdmmc<'d> {\n    fn enable_interrupts(&self) {\n        let regs = self.info.regs;\n        critical_section::with(|_| {\n            regs.maskr().modify(|w| {\n                w.set_dcrcfailie(true);\n                w.set_dtimeoutie(true);\n                w.set_dataendie(true);\n                w.set_dbckendie(true);\n\n                #[cfg(sdmmc_v1)]\n                w.set_stbiterre(true);\n                #[cfg(sdmmc_v2)]\n                w.set_dabortie(true);\n            });\n        });\n    }\n\n    fn new_inner<T: Instance>(\n        _sdmmc: Peri<'d, T>,\n        #[cfg(sdmmc_v1)] dma: ChannelAndRequest<'d>,\n        clk: Flex<'d>,\n        cmd: Flex<'d>,\n        d0: Flex<'d>,\n        d1: Option<Flex<'d>>,\n        d2: Option<Flex<'d>>,\n        d3: Option<Flex<'d>>,\n        d4: Option<Flex<'d>>,\n        d5: Option<Flex<'d>>,\n        d6: Option<Flex<'d>>,\n        d7: Option<Flex<'d>>,\n        config: Config,\n    ) -> Self {\n        rcc::enable_and_reset_without_stop::<T>();\n\n        T::Interrupt::unpend();\n        unsafe { T::Interrupt::enable() };\n\n        let info = T::info();\n        let state = T::state();\n        let ker_clk = T::frequency();\n\n        info.regs.clkcr().write(|w| {\n            w.set_pwrsav(false);\n            w.set_negedge(false);\n\n            // Hardware flow control is broken on SDIOv1 and causes clock glitches, which result in CRC errors.\n            // See chip erratas for more details.\n            #[cfg(sdmmc_v1)]\n            w.set_hwfc_en(false);\n            #[cfg(sdmmc_v2)]\n            w.set_hwfc_en(true);\n\n            #[cfg(sdmmc_v1)]\n            w.set_clken(true);\n        });\n\n        // Power off, writen 00: Clock to the card is stopped;\n        // D[7:0], CMD, and CK are driven high.\n        info.regs.power().modify(|w| w.set_pwrctrl(PowerCtrl::Off as u8));\n\n        Self {\n            info,\n            state,\n            ker_clk,\n            #[cfg(sdmmc_v1)]\n            dma,\n\n            _clk: clk,\n            _cmd: cmd,\n            _d0: d0,\n            _d1: d1,\n            _d2: d2,\n            d3,\n            _d4: d4,\n            _d5: d5,\n            _d6: d6,\n            d7,\n\n            config,\n        }\n    }\n\n    /// Data transfer is in progress\n    #[inline]\n    fn data_active(&self) -> bool {\n        let regs = self.info.regs;\n\n        let status = regs.star().read();\n        #[cfg(sdmmc_v1)]\n        return status.rxact() || status.txact();\n        #[cfg(sdmmc_v2)]\n        return status.dpsmact();\n    }\n\n    /// Coammand transfer is in progress\n    #[inline]\n    fn cmd_active(&self) -> bool {\n        let regs = self.info.regs;\n\n        let status = regs.star().read();\n        #[cfg(sdmmc_v1)]\n        return status.cmdact();\n        #[cfg(sdmmc_v2)]\n        return status.cpsmact();\n    }\n\n    /// Wait idle on CMDACT, RXACT and TXACT (v1) or DOSNACT and CPSMACT (v2)\n    #[inline]\n    fn wait_idle(&self) {\n        while self.data_active() || self.cmd_active() {}\n    }\n\n    fn bus_width(&self) -> BusWidth {\n        match (self.d3.is_some(), self.d7.is_some()) {\n            (true, true) => BusWidth::Eight,\n            (true, false) => BusWidth::Four,\n            _ => BusWidth::One,\n        }\n    }\n\n    /// # Safety\n    ///\n    /// `buffer` must be valid for the whole transfer and word aligned\n    #[allow(unused_variables)]\n    fn prepare_datapath_read<'a>(\n        &'a self,\n        buffer: &'a mut Aligned<A4, [u8]>,\n        mode: DatapathMode,\n    ) -> WrappedTransfer<'a> {\n        let regs = self.info.regs;\n\n        let (byte_mode, block_size) = match mode {\n            DatapathMode::Block(block_size) => (false, block_size as u8),\n            DatapathMode::Byte => (true, 0),\n        };\n\n        // Command AND Data state machines must be idle\n        self.wait_idle();\n        self.clear_interrupt_flags();\n\n        regs.dlenr().write(|w| w.set_datalength(size_of_val(buffer) as u32));\n\n        // SAFETY: No other functions use the dma\n        #[cfg(sdmmc_v1)]\n        let transfer = unsafe {\n            self.dma\n                .clone_unchecked()\n                .read(\n                    regs.fifor().as_ptr() as *mut u32,\n                    slice32_mut(buffer),\n                    DMA_TRANSFER_OPTIONS,\n                )\n                .unchecked_extend_lifetime()\n        };\n        #[cfg(sdmmc_v2)]\n        let transfer = {\n            regs.idmabase0r().write(|w| w.set_idmabase0(buffer.as_mut_ptr() as u32));\n            regs.idmactrlr().modify(|w| w.set_idmaen(true));\n            Transfer {\n                _dummy: core::marker::PhantomData,\n            }\n        };\n\n        #[cfg(sdmmc_v2)]\n        let byte_mode = byte_mode as u8;\n\n        regs.dctrl().modify(|w| {\n            w.set_dtmode(byte_mode);\n            w.set_dblocksize(block_size as u8);\n            w.set_dtdir(true);\n            #[cfg(sdmmc_v1)]\n            {\n                w.set_dmaen(true);\n                w.set_dten(true);\n            }\n        });\n\n        // Memory barrier after DMA setup to ensure register writes complete before command\n        fence(Ordering::SeqCst);\n\n        self.enable_interrupts();\n\n        WrappedTransfer::new(transfer, &self)\n    }\n\n    /// # Safety\n    ///\n    /// `buffer` must be valid for the whole transfer and word aligned\n    fn prepare_datapath_write<'a>(&'a self, buffer: &'a Aligned<A4, [u8]>, mode: DatapathMode) -> WrappedTransfer<'a> {\n        let regs = self.info.regs;\n\n        let (byte_mode, block_size) = match mode {\n            DatapathMode::Block(block_size) => (false, block_size as u8),\n            DatapathMode::Byte => (true, 0),\n        };\n\n        // Command AND Data state machines must be idle\n        self.wait_idle();\n        self.clear_interrupt_flags();\n\n        regs.dlenr().write(|w| w.set_datalength(size_of_val(buffer) as u32));\n\n        // SAFETY: No other functions use the dma\n        #[cfg(sdmmc_v1)]\n        let transfer = unsafe {\n            self.dma\n                .clone_unchecked()\n                .write(\n                    slice32_ref(buffer),\n                    regs.fifor().as_ptr() as *mut u32,\n                    DMA_TRANSFER_OPTIONS,\n                )\n                .unchecked_extend_lifetime()\n        };\n        #[cfg(sdmmc_v2)]\n        let transfer = {\n            regs.idmabase0r().write(|w| w.set_idmabase0(buffer.as_ptr() as u32));\n            regs.idmactrlr().modify(|w| w.set_idmaen(true));\n            Transfer {\n                _dummy: core::marker::PhantomData,\n            }\n        };\n\n        #[cfg(sdmmc_v2)]\n        let byte_mode = byte_mode as u8;\n\n        regs.dctrl().modify(|w| {\n            w.set_dtmode(byte_mode);\n            w.set_dblocksize(block_size as u8);\n            w.set_dtdir(false);\n            #[cfg(sdmmc_v1)]\n            {\n                w.set_dmaen(true);\n                w.set_dten(true);\n            }\n        });\n\n        // Memory barrier after DMA setup to ensure register writes complete before command\n        fence(Ordering::SeqCst);\n\n        self.enable_interrupts();\n\n        WrappedTransfer::new(transfer, &self)\n    }\n\n    /// Stops the DMA datapath\n    fn stop_datapath(&self) {\n        let regs = self.info.regs;\n\n        #[cfg(sdmmc_v1)]\n        regs.dctrl().modify(|w| {\n            w.set_dmaen(false);\n            w.set_dten(false);\n        });\n        #[cfg(sdmmc_v2)]\n        regs.idmactrlr().modify(|w| w.set_idmaen(false));\n    }\n\n    fn init_idle(&mut self) -> Result<CommandResponse<Rz>, Error> {\n        let regs = self.info.regs;\n\n        self.clkcr_set_clkdiv(SD_INIT_FREQ, BusWidth::One)?;\n        regs.dtimer()\n            .write(|w| w.set_datatime(self.config.data_transfer_timeout));\n\n        regs.power().modify(|w| w.set_pwrctrl(PowerCtrl::On as u8));\n\n        // Wait 74 cycles\n        block_for_us((74_000_000 / SD_INIT_FREQ.0) as u64);\n\n        self.cmd(common_cmd::idle(), true, false)\n    }\n\n    /// Sets the CLKDIV field in CLKCR. Updates clock field in self\n    fn clkcr_set_clkdiv(&mut self, freq: Hertz, width: BusWidth) -> Result<(), Error> {\n        let regs = self.info.regs;\n\n        let (widbus, width_u32) = bus_width_vals(width);\n        let (_bypass, clkdiv, new_clock) = clk_div(self.ker_clk, freq)?;\n\n        trace!(\"sdmmc: set clock to {}\", new_clock);\n\n        // Enforce AHB and SDMMC_CK clock relation. See RM0433 Rev 7\n        // Section 55.5.8\n        let sdmmc_bus_bandwidth = new_clock.0 * width_u32;\n        assert!(self.ker_clk.0 > 3 * sdmmc_bus_bandwidth / 32);\n\n        // CPSMACT and DPSMACT must be 0 to set CLKDIV or WIDBUS\n        self.wait_idle();\n        regs.clkcr().modify(|w| {\n            w.set_clkdiv(clkdiv);\n            #[cfg(sdmmc_v1)]\n            w.set_bypass(_bypass);\n            w.set_widbus(widbus);\n        });\n\n        Ok(())\n    }\n\n    fn get_cid(&self) -> Result<CommandResponse<R2>, Error> {\n        self.cmd(common_cmd::all_send_cid(), true, false) // CMD2\n    }\n\n    fn get_csd(&self, address: u16) -> Result<CommandResponse<R2>, Error> {\n        self.cmd(common_cmd::send_csd(address), true, false)\n    }\n\n    /// Query the card status (CMD13, returns R1)\n    fn read_status(&self, address: u16) -> Result<CommandResponse<R1>, Error> {\n        self.cmd(common_cmd::card_status(address, false), true, false)\n    }\n\n    /// Select one card and place it into the _Tranfer State_\n    ///\n    /// If `None` is specifed for `card`, all cards are put back into\n    /// _Stand-by State_\n    fn select_card(&self, rca: Option<u16>) -> Result<(), Error> {\n        match self.cmd(common_cmd::select_card(rca.unwrap_or(0)), true, false) {\n            Err(Error::Timeout) if rca == None => Ok(()),\n            result => result.map(|_| ()),\n        }\n    }\n\n    /// Clear flags in interrupt clear register\n    #[inline]\n    fn clear_interrupt_flags(&self) {\n        let regs = self.info.regs;\n        regs.icr().write(|w| {\n            w.set_ccrcfailc(true);\n            w.set_dcrcfailc(true);\n            w.set_ctimeoutc(true);\n            w.set_dtimeoutc(true);\n            w.set_txunderrc(true);\n            w.set_rxoverrc(true);\n            w.set_cmdrendc(true);\n            w.set_cmdsentc(true);\n            w.set_dataendc(true);\n            w.set_dbckendc(true);\n            #[cfg(sdmmc_v1)]\n            w.set_stbiterrc(true);\n\n            #[cfg(sdmmc_v2)]\n            {\n                w.set_dholdc(true);\n                w.set_dabortc(true);\n                w.set_busyd0endc(true);\n                w.set_ackfailc(true);\n                w.set_acktimeoutc(true);\n                w.set_vswendc(true);\n                w.set_ckstopc(true);\n                w.set_idmatec(true);\n                w.set_idmabtcc(true);\n            }\n        });\n    }\n\n    /// Send command to card\n    #[allow(unused_variables)]\n    fn cmd<R: TypedResp>(&self, cmd: Cmd<R>, check_crc: bool, data: bool) -> Result<CommandResponse<R>, Error> {\n        let regs = self.info.regs;\n\n        self.clear_interrupt_flags();\n        // CP state machine must be idle\n        while self.cmd_active() {}\n\n        // Command arg\n        regs.argr().write(|w| w.set_cmdarg(cmd.arg));\n\n        // Command index and start CP State Machine\n        regs.cmdr().write(|w| {\n            w.set_waitint(false);\n            w.set_waitresp(get_waitresp_val(cmd.response_len()));\n            w.set_cmdindex(cmd.cmd);\n            w.set_cpsmen(true);\n\n            #[cfg(sdmmc_v2)]\n            {\n                // Special mode in CP State Machine\n                // CMD12: Stop Transmission\n                let cpsm_stop_transmission = cmd.cmd == 12;\n                w.set_cmdstop(cpsm_stop_transmission);\n                w.set_cmdtrans(data);\n            }\n        });\n\n        let mut status;\n        if cmd.response_len() == ResponseLen::Zero {\n            // Wait for CMDSENT or a timeout\n            while {\n                status = regs.star().read();\n                !(status.ctimeout() || status.cmdsent())\n            } {}\n        } else {\n            // Wait for CMDREND or CCRCFAIL or a timeout\n            while {\n                status = regs.star().read();\n                !(status.ctimeout() || status.cmdrend() || status.ccrcfail())\n            } {}\n        }\n\n        if status.ctimeout() {\n            trace!(\"ctimeout: {}\", cmd.cmd);\n\n            return Err(Error::Timeout);\n        } else if check_crc && status.ccrcfail() {\n            return Err(Error::Crc);\n        }\n\n        Ok(CommandResponse(\n            match R::LENGTH {\n                ResponseLen::Zero => U128(0u128),\n                ResponseLen::R48 => U128(self.info.regs.respr(0).read().cardstatus() as u128),\n                ResponseLen::R136 => {\n                    let cid0 = self.info.regs.respr(0).read().cardstatus() as u128;\n                    let cid1 = self.info.regs.respr(1).read().cardstatus() as u128;\n                    let cid2 = self.info.regs.respr(2).read().cardstatus() as u128;\n                    let cid3 = self.info.regs.respr(3).read().cardstatus() as u128;\n\n                    U128((cid0 << 96) | (cid1 << 64) | (cid2 << 32) | (cid3))\n                }\n            }\n            .into(),\n        ))\n    }\n\n    fn on_drop(&self) {\n        let regs = self.info.regs;\n        if self.data_active() {\n            self.clear_interrupt_flags();\n            // Send abort\n            // CP state machine must be idle\n            while self.cmd_active() {}\n\n            // Command arg\n            regs.argr().write(|w| w.set_cmdarg(0));\n\n            // Command index and start CP State Machine\n            regs.cmdr().write(|w| {\n                w.set_waitint(false);\n                w.set_waitresp(get_waitresp_val(ResponseLen::R48));\n                w.set_cmdindex(12);\n                w.set_cpsmen(true);\n\n                #[cfg(sdmmc_v2)]\n                {\n                    w.set_cmdstop(true);\n                    w.set_cmdtrans(false);\n                }\n            });\n\n            // Wait for the abort\n            while self.data_active() {}\n        }\n        regs.maskr().write(|_| ()); // disable irqs\n        self.clear_interrupt_flags();\n        self.stop_datapath();\n    }\n\n    /// Wait for a previously started datapath transfer to complete from an interrupt.\n    #[inline]\n    #[allow(unused)]\n    async fn complete_datapath_transfer(&self, mut transfer: WrappedTransfer<'_>, block: bool) -> Result<(), Error> {\n        let res = poll_fn(|cx| {\n            // Compiler might not be sufficiently constrained here\n            // https://github.com/embassy-rs/embassy/issues/4723\n            self.state.tx_waker.register(cx.waker());\n            let status = self.info.regs.star().read();\n\n            if status.dcrcfail() {\n                return Poll::Ready(Err(Error::Crc));\n            }\n            if status.dtimeout() {\n                return Poll::Ready(Err(Error::Timeout));\n            }\n            if status.txunderr() {\n                return Poll::Ready(Err(Error::Underrun));\n            }\n            #[cfg(sdmmc_v1)]\n            if status.stbiterr() {\n                return Poll::Ready(Err(Error::StBitErr));\n            }\n            #[cfg(sdmmc_v1)]\n            let done = match block {\n                true => status.dbckend(),\n                false => status.dataend(),\n            };\n            #[cfg(sdmmc_v2)]\n            let done = status.dataend();\n            if done {\n                return Poll::Ready(Ok(()));\n            }\n            Poll::Pending\n        })\n        .await;\n\n        // Memory barrier after DMA completion to ensure CPU sees DMA-written data\n        fence(Ordering::Acquire);\n\n        self.clear_interrupt_flags();\n        self.stop_datapath();\n\n        if !res.is_err() {\n            transfer.defuse();\n        }\n        drop(transfer);\n\n        res\n    }\n}\n\nimpl<'d> Drop for Sdmmc<'d> {\n    fn drop(&mut self) {\n        // T::Interrupt::disable();\n        self.on_drop();\n        self.info.rcc.disable_without_stop();\n    }\n}\n\n//////////////////////////////////////////////////////\n\ntype Regs = RegBlock;\n\nstruct Info {\n    regs: Regs,\n    rcc: RccInfo,\n}\n\nstruct State {\n    tx_waker: AtomicWaker,\n    it_waker: AtomicWaker,\n}\n\nimpl State {\n    const fn new() -> Self {\n        Self {\n            tx_waker: AtomicWaker::new(),\n            it_waker: AtomicWaker::new(),\n        }\n    }\n}\n\ntrait SealedInstance {\n    fn info() -> &'static Info;\n    fn state() -> &'static State;\n}\n\n/// SDMMC instance trait.\n#[allow(private_bounds)]\npub trait Instance: SealedInstance + PeripheralType + RccPeripheral + 'static {\n    /// Interrupt for this instance.\n    type Interrupt: interrupt::typelevel::Interrupt;\n}\n\npin_trait!(CkPin, Instance);\npin_trait!(CmdPin, Instance);\npin_trait!(D0Pin, Instance);\npin_trait!(D1Pin, Instance);\npin_trait!(D2Pin, Instance);\npin_trait!(D3Pin, Instance);\npin_trait!(D4Pin, Instance);\npin_trait!(D5Pin, Instance);\npin_trait!(D6Pin, Instance);\npin_trait!(D7Pin, Instance);\n\n#[cfg(sdmmc_v1)]\ndma_trait!(SdmmcDma, Instance);\n\nforeach_peripheral!(\n    (sdmmc, $inst:ident) => {\n        impl SealedInstance for peripherals::$inst {\n            fn info() -> &'static Info {\n                static INFO: Info = Info {\n                    regs: unsafe { Regs::from_ptr(crate::pac::$inst.as_ptr()) },\n                    rcc: crate::peripherals::$inst::RCC_INFO,\n                };\n                &INFO\n            }\n\n            fn state() -> &'static State {\n                static STATE: State = State::new();\n                &STATE\n            }\n        }\n\n        impl Instance for peripherals::$inst {\n            type Interrupt = crate::interrupt::typelevel::$inst;\n        }\n    };\n);\n"
  },
  {
    "path": "embassy-stm32/src/sdmmc/sd.rs",
    "content": "use core::default::Default;\nuse core::ops::{Deref, DerefMut};\n\nuse sdio_host::common_cmd::R3;\nuse sdio_host::emmc::{EMMC, ExtCSD};\nuse sdio_host::sd::{BusWidth, CIC, CID, CSD, CardCapacity, CardStatus, CurrentState, OCR, RCA, SCR, SD, SDStatus};\nuse sdio_host::sd_cmd::{R6, R7};\nuse sdio_host::{common_cmd, emmc_cmd, sd_cmd};\n\nuse crate::sdmmc::{\n    BlockSize, CommandResponse, DatapathMode, Error, Sdmmc, Signalling, TypedResp, aligned_mut, aligned_ref,\n    block_size, bus_width_vals, slice8_mut, slice8_ref,\n};\nuse crate::time::{Hertz, mhz};\n\nimpl TypedResp for R3 {\n    type Word = u32;\n}\n\nimpl<E> From<CommandResponse<R3>> for OCR<E> {\n    fn from(value: CommandResponse<R3>) -> Self {\n        OCR::<E>::from(value.0)\n    }\n}\n\nimpl TypedResp for R6 {\n    type Word = u32;\n}\n\nimpl<E> From<CommandResponse<R6>> for RCA<E> {\n    fn from(value: CommandResponse<R6>) -> Self {\n        RCA::<E>::from(value.0)\n    }\n}\n\nimpl TypedResp for R7 {\n    type Word = u32;\n}\n\nimpl From<CommandResponse<R7>> for CIC {\n    fn from(value: CommandResponse<R7>) -> Self {\n        CIC::from(value.0)\n    }\n}\n\n/// Aligned data block for SDMMC transfers.\n///\n/// This is a 512-byte array, aligned to 4 bytes to satisfy DMA requirements.\n#[repr(align(4))]\n#[derive(Debug, Clone, PartialEq, Eq)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub struct DataBlock(pub [u32; 128]);\n\nimpl DataBlock {\n    /// Create a new DataBlock\n    pub const fn new() -> Self {\n        DataBlock([0u32; 128])\n    }\n}\n\nimpl Deref for DataBlock {\n    type Target = [u8; 512];\n\n    fn deref(&self) -> &Self::Target {\n        unwrap!(slice8_ref(&self.0[..]).try_into())\n    }\n}\n\nimpl DerefMut for DataBlock {\n    fn deref_mut(&mut self) -> &mut Self::Target {\n        unwrap!(slice8_mut(&mut self.0[..]).try_into())\n    }\n}\n\n/// Command Block buffer for SDMMC command transfers.\n///\n/// This is a 16-word array, exposed so that DMA commpatible memory can be used if required.\n#[derive(Debug, Clone, PartialEq, Eq)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub struct CmdBlock(pub [u32; 16]);\n\nimpl CmdBlock {\n    /// Creates a new instance of CmdBlock\n    pub const fn new() -> Self {\n        Self([0u32; 16])\n    }\n}\n\nimpl Deref for CmdBlock {\n    type Target = [u32; 16];\n\n    fn deref(&self) -> &Self::Target {\n        &self.0\n    }\n}\n\nimpl DerefMut for CmdBlock {\n    fn deref_mut(&mut self) -> &mut Self::Target {\n        &mut self.0\n    }\n}\n\n/// Represents either an SD or EMMC card\npub trait Addressable: Sized + Clone\nwhere\n    CardStatus<Self::Ext>: From<u32>,\n{\n    /// Associated type\n    type Ext;\n\n    /// Get this peripheral's address on the SDMMC bus\n    fn get_address(&self) -> u16;\n\n    /// Is this a standard or high capacity peripheral?\n    fn get_capacity(&self) -> CardCapacity;\n\n    /// Size in bytes\n    fn size(&self) -> u64;\n}\n\n/// Storage Device\npub struct StorageDevice<'a, 'b, T: Addressable> {\n    info: T,\n    /// Inner member\n    pub sdmmc: &'a mut Sdmmc<'b>,\n}\n\n/// Card Storage Device\nimpl<'a, 'b> StorageDevice<'a, 'b, Card> {\n    /// Create a new SD card\n    pub async fn new_sd_card(sdmmc: &'a mut Sdmmc<'b>, cmd_block: &mut CmdBlock, freq: Hertz) -> Result<Self, Error> {\n        let mut s = Self {\n            info: Card::default(),\n            sdmmc,\n        };\n\n        s.acquire(cmd_block, freq).await?;\n\n        Ok(s)\n    }\n\n    /// Create a new uninitialized card\n    pub fn new_uninit_sd_card(sdmmc: &'a mut Sdmmc<'b>) -> Self {\n        Self {\n            info: Card::default(),\n            sdmmc,\n        }\n    }\n\n    /// Re-acquire an sd card\n    pub async fn reacquire(&mut self, cmd_block: &mut CmdBlock, freq: Hertz) -> Result<(), Error> {\n        self.sdmmc.on_drop();\n        self.acquire(cmd_block, freq).await\n    }\n\n    /// Initializes the card into a known state (or at least tries to).\n    async fn acquire(&mut self, cmd_block: &mut CmdBlock, freq: Hertz) -> Result<(), Error> {\n        let _scoped_wake_guard = self.sdmmc.info.rcc.wake_guard();\n\n        // Get the bus width configured in the Sdmmc peripheral\n        let configured_bus_width = match self.sdmmc.bus_width() {\n            BusWidth::Eight => return Err(Error::BusWidth),\n            bus_width => bus_width,\n        };\n\n        // While the SD/SDIO card or eMMC is in identification mode,\n        // the SDMMC_CK frequency must be no more than 400 kHz.\n        self.sdmmc.init_idle()?;\n\n        // Check if cards supports CMD8 (with pattern)\n        let cic: CIC = self.sdmmc.cmd(sd_cmd::send_if_cond(1, 0xAA), true, false)?.into();\n\n        if cic.pattern() != 0xAA {\n            return Err(Error::UnsupportedCardVersion);\n        }\n\n        if cic.voltage_accepted() & 1 == 0 {\n            return Err(Error::UnsupportedVoltage);\n        }\n\n        let ocr = loop {\n            // Signal that next command is a app command\n            self.sdmmc.cmd(common_cmd::app_cmd(0), true, false)?; // CMD55\n\n            // 3.2-3.3V\n            let voltage_window = 1 << 5;\n            // Initialize card\n\n            let ocr: OCR<SD> = self\n                .sdmmc\n                .cmd(sd_cmd::sd_send_op_cond(true, false, true, voltage_window), false, false)?\n                .into();\n\n            if !ocr.is_busy() {\n                // Power up done\n                break ocr;\n            }\n        };\n\n        if ocr.high_capacity() {\n            // Card is SDHC or SDXC or SDUC\n            self.info.card_type = CardCapacity::HighCapacity;\n        } else {\n            self.info.card_type = CardCapacity::StandardCapacity;\n        }\n        self.info.ocr = ocr;\n\n        self.info.cid = self.sdmmc.get_cid()?.into();\n        let rca: RCA<SD> = self.sdmmc.cmd(sd_cmd::send_relative_address(), true, false)?.into();\n        self.info.rca = rca.address();\n        self.info.csd = self.sdmmc.get_csd(self.info.get_address())?.into();\n        self.sdmmc.select_card(Some(self.info.get_address()))?;\n        self.info.scr = self.get_scr(cmd_block).await?;\n\n        // Select bus width based on Sdmmc configuration and card capability\n        // Use 4-bit only if both the peripheral is configured for it AND the card supports it\n        let (bus_width, acmd_arg) = match configured_bus_width {\n            BusWidth::Four if self.info.scr.bus_width_four() => (BusWidth::Four, 2),\n            _ => (BusWidth::One, 0),\n        };\n\n        self.sdmmc.cmd(common_cmd::app_cmd(self.info.rca), true, false)?;\n        self.sdmmc.cmd(sd_cmd::cmd6(acmd_arg), true, false)?;\n\n        self.sdmmc.clkcr_set_clkdiv(freq.clamp(mhz(0), mhz(25)), bus_width)?;\n\n        // Read status\n        self.info.status = self.read_sd_status(cmd_block).await?;\n\n        if freq > mhz(25) {\n            // Switch to SDR25\n            let signalling = self.switch_signalling_mode(cmd_block, Signalling::SDR25).await?;\n\n            if signalling == Signalling::SDR25 {\n                // Set final clock frequency\n                self.sdmmc.clkcr_set_clkdiv(freq, bus_width)?;\n\n                let status: CardStatus<SD> = self.sdmmc.read_status(self.info.rca)?.into();\n                if status.state() != CurrentState::Transfer {\n                    return Err(Error::SignalingSwitchFailed);\n                }\n            }\n\n            // Read status after signalling change\n            self.read_sd_status(cmd_block).await?;\n        }\n\n        Ok(())\n    }\n\n    /// Switch mode using CMD6.\n    ///\n    /// Attempt to set a new signalling mode. The selected\n    /// signalling mode is returned. Expects the current clock\n    /// frequency to be > 12.5MHz.\n    ///\n    /// SD only.\n    async fn switch_signalling_mode(\n        &self,\n        cmd_block: &mut CmdBlock,\n        signalling: Signalling,\n    ) -> Result<Signalling, Error> {\n        // NB PLSS v7_10 4.3.10.4: \"the use of SET_BLK_LEN command is not\n        // necessary\"\n\n        let set_function = 0x8000_0000\n            | match signalling {\n                // See PLSS v7_10 Table 4-11\n                Signalling::DDR50 => 0xFF_FF04,\n                Signalling::SDR104 => 0xFF_1F03,\n                Signalling::SDR50 => 0xFF_1F02,\n                Signalling::SDR25 => 0xFF_FF01,\n                Signalling::SDR12 => 0xFF_FF00,\n            };\n\n        let buffer = &mut aligned_mut(&mut cmd_block.0)[..64];\n        let mode = DatapathMode::Block(block_size(size_of_val(buffer)));\n        let transfer = self.sdmmc.prepare_datapath_read(buffer, mode);\n\n        self.sdmmc.cmd(sd_cmd::cmd6(set_function), true, true)?; // CMD6\n\n        self.sdmmc.complete_datapath_transfer(transfer, true).await?;\n\n        // Host is allowed to use the new functions at least 8\n        // clocks after the end of the switch command\n        // transaction. We know the current clock period is < 80ns,\n        // so a total delay of 640ns is required here\n        for _ in 0..300 {\n            cortex_m::asm::nop();\n        }\n\n        // Function Selection of Function Group 1\n        let selection = (u32::from_be(cmd_block[4]) >> 24) & 0xF;\n\n        match selection {\n            0 => Ok(Signalling::SDR12),\n            1 => Ok(Signalling::SDR25),\n            2 => Ok(Signalling::SDR50),\n            3 => Ok(Signalling::SDR104),\n            4 => Ok(Signalling::DDR50),\n            _ => Err(Error::UnsupportedCardType),\n        }\n    }\n\n    /// Reads the SCR register.\n    ///\n    /// SD only.\n    async fn get_scr(&self, cmd_block: &mut CmdBlock) -> Result<SCR, Error> {\n        // Read the 64-bit SCR register\n        self.sdmmc.cmd(common_cmd::set_block_length(8), true, false)?; // CMD16\n        self.sdmmc.cmd(common_cmd::app_cmd(self.info.rca), true, false)?;\n\n        let scr = &mut cmd_block.0[..2];\n\n        let transfer = self\n            .sdmmc\n            .prepare_datapath_read(aligned_mut(scr), DatapathMode::Block(BlockSize::Size8));\n        self.sdmmc.cmd(sd_cmd::send_scr(), true, true)?;\n\n        self.sdmmc.complete_datapath_transfer(transfer, true).await?;\n\n        Ok(SCR(u64::from_be_bytes(unwrap!(slice8_mut(scr).try_into()))))\n    }\n\n    /// Reads the SD Status (ACMD13)\n    ///\n    /// SD only.\n    async fn read_sd_status(&self, cmd_block: &mut CmdBlock) -> Result<SDStatus, Error> {\n        let rca = self.info.rca;\n        let buffer = &mut aligned_mut(&mut cmd_block.0)[..64];\n\n        self.sdmmc\n            .cmd(common_cmd::set_block_length(size_of_val(buffer) as u32), true, false)?; // CMD16\n        self.sdmmc.cmd(common_cmd::app_cmd(rca), true, false)?; // APP\n\n        let mode = DatapathMode::Block(block_size(size_of_val(buffer)));\n        let transfer = self.sdmmc.prepare_datapath_read(buffer, mode);\n\n        self.sdmmc.cmd(sd_cmd::sd_status(), true, true)?;\n        self.sdmmc.complete_datapath_transfer(transfer, true).await?;\n\n        for word in cmd_block.iter_mut() {\n            *word = u32::from_be(*word);\n        }\n\n        Ok(cmd_block.0.into())\n    }\n}\n\n/// Emmc storage device\nimpl<'a, 'b> StorageDevice<'a, 'b, Emmc> {\n    /// Create a new EMMC card\n    pub async fn new_emmc(sdmmc: &'a mut Sdmmc<'b>, cmd_block: &mut CmdBlock, freq: Hertz) -> Result<Self, Error> {\n        let mut s = Self {\n            info: Emmc::default(),\n            sdmmc,\n        };\n\n        s.acquire(cmd_block, freq).await?;\n\n        Ok(s)\n    }\n\n    /// Create a new uninitialized emmc\n    pub fn new_uninit_emmc(sdmmc: &'a mut Sdmmc<'b>) -> Self {\n        Self {\n            info: Emmc::default(),\n            sdmmc,\n        }\n    }\n\n    /// Re-acquire an emmc\n    pub async fn reacquire(&mut self, cmd_block: &mut CmdBlock, freq: Hertz) -> Result<(), Error> {\n        self.sdmmc.on_drop();\n        self.acquire(cmd_block, freq).await\n    }\n\n    async fn acquire(&mut self, _cmd_block: &mut CmdBlock, freq: Hertz) -> Result<(), Error> {\n        let _scoped_wake_guard = self.sdmmc.info.rcc.wake_guard();\n\n        let bus_width = self.sdmmc.bus_width();\n\n        // While the SD/SDIO card or eMMC is in identification mode,\n        // the SDMMC_CK frequency must be no more than 400 kHz.\n        self.sdmmc.init_idle()?;\n\n        let ocr = loop {\n            let high_voltage = 0b0 << 7;\n            let access_mode = 0b10 << 29;\n            let op_cond = high_voltage | access_mode | 0b1_1111_1111 << 15;\n            // Initialize card\n            let ocr: OCR<EMMC> = self.sdmmc.cmd(emmc_cmd::send_op_cond(op_cond), false, false)?.into();\n            if !ocr.is_busy() {\n                // Power up done\n                break ocr;\n            }\n        };\n\n        self.info.capacity = if ocr.access_mode() == 0b10 {\n            // Card is SDHC or SDXC or SDUC\n            CardCapacity::HighCapacity\n        } else {\n            CardCapacity::StandardCapacity\n        };\n        self.info.ocr = ocr;\n        self.info.cid = self.sdmmc.get_cid()?.into();\n        self.info.rca = 1u16.into();\n\n        self.sdmmc\n            .cmd(emmc_cmd::assign_relative_address(self.info.rca), true, false)?;\n\n        self.info.csd = self.sdmmc.get_csd(self.info.get_address())?.into();\n        self.sdmmc.select_card(Some(self.info.get_address()))?;\n\n        let (widbus, _) = bus_width_vals(bus_width);\n\n        // Write bus width to ExtCSD byte 183\n        self.sdmmc.cmd(\n            emmc_cmd::modify_ext_csd(emmc_cmd::AccessMode::WriteByte, 183, widbus),\n            true,\n            false,\n        )?;\n\n        // Wait for ready after R1b response\n        loop {\n            let status: CardStatus<EMMC> = self.sdmmc.read_status(self.info.rca)?.into();\n            if status.ready_for_data() {\n                break;\n            }\n        }\n\n        self.sdmmc.clkcr_set_clkdiv(freq.clamp(mhz(0), mhz(25)), bus_width)?;\n        self.info.ext_csd = self.read_ext_csd().await?;\n\n        Ok(())\n    }\n\n    /// Gets the EXT_CSD register.\n    ///\n    /// eMMC only.\n    async fn read_ext_csd(&self) -> Result<ExtCSD, Error> {\n        // Note: cmd_block can't be used because ExtCSD is too long to fit.\n        let mut data_block = DataBlock::new();\n\n        self.sdmmc\n            .cmd(common_cmd::set_block_length(size_of::<DataBlock>() as u32), true, false)\n            .unwrap(); // CMD16\n\n        let transfer = self.sdmmc.prepare_datapath_read(\n            aligned_mut(&mut data_block.0),\n            DatapathMode::Block(block_size(size_of::<DataBlock>())),\n        );\n        self.sdmmc.cmd(emmc_cmd::send_ext_csd(), true, true)?;\n\n        self.sdmmc.complete_datapath_transfer(transfer, true).await?;\n\n        Ok(data_block.0.into())\n    }\n}\n\n/// Card or Emmc storage device\nimpl<'a, 'b, A: Addressable> StorageDevice<'a, 'b, A> {\n    /// Write a block\n    pub fn card(&self) -> A {\n        self.info.clone()\n    }\n\n    /// Read a data block.\n    #[inline]\n    pub async fn read_block(&mut self, block_idx: u32, data_block: &mut DataBlock) -> Result<(), Error> {\n        let _scoped_wake_guard = self.sdmmc.info.rcc.wake_guard();\n        let card_capacity = self.info.get_capacity();\n\n        // Always read 1 block of 512 bytes\n        // SDSC cards are byte addressed hence the blockaddress is in multiples of 512 bytes\n        let address = match card_capacity {\n            CardCapacity::StandardCapacity => block_idx * size_of::<DataBlock>() as u32,\n            _ => block_idx,\n        };\n        self.sdmmc\n            .cmd(common_cmd::set_block_length(size_of::<DataBlock>() as u32), true, false)?; // CMD16\n\n        let transfer = self.sdmmc.prepare_datapath_read(\n            aligned_mut(&mut data_block.0),\n            DatapathMode::Block(block_size(size_of::<DataBlock>())),\n        );\n        self.sdmmc.cmd(common_cmd::read_single_block(address), true, true)?;\n\n        self.sdmmc.complete_datapath_transfer(transfer, true).await?;\n\n        Ok(())\n    }\n\n    /// Read multiple data blocks.\n    #[inline]\n    pub async fn read_blocks(&mut self, block_idx: u32, blocks: &mut [DataBlock]) -> Result<(), Error> {\n        let _scoped_wake_guard = self.sdmmc.info.rcc.wake_guard();\n        let card_capacity = self.info.get_capacity();\n\n        // NOTE(unsafe) reinterpret buffer as &mut [u32]\n        let buffer = unsafe {\n            core::slice::from_raw_parts_mut(\n                blocks.as_mut_ptr() as *mut u32,\n                blocks.len() * size_of::<DataBlock>() / size_of::<u32>(),\n            )\n        };\n\n        // Always read 1 block of 512 bytes\n        // SDSC cards are byte addressed hence the blockaddress is in multiples of 512 bytes\n        let address = match card_capacity {\n            CardCapacity::StandardCapacity => block_idx * size_of::<DataBlock>() as u32,\n            _ => block_idx,\n        };\n        self.sdmmc\n            .cmd(common_cmd::set_block_length(size_of::<DataBlock>() as u32), true, false)?; // CMD16\n\n        let transfer = self.sdmmc.prepare_datapath_read(\n            aligned_mut(buffer),\n            DatapathMode::Block(block_size(size_of::<DataBlock>())),\n        );\n        self.sdmmc.cmd(common_cmd::read_multiple_blocks(address), true, true)?;\n\n        self.sdmmc.complete_datapath_transfer(transfer, false).await?;\n\n        self.sdmmc.cmd(common_cmd::stop_transmission(), true, false)?; // CMD12\n        self.sdmmc.clear_interrupt_flags();\n\n        Ok(())\n    }\n\n    /// Write a data block.\n    pub async fn write_block(&mut self, block_idx: u32, buffer: &DataBlock) -> Result<(), Error> {\n        let _scoped_wake_guard = self.sdmmc.info.rcc.wake_guard();\n\n        // Always read 1 block of 512 bytes\n        //  cards are byte addressed hence the blockaddress is in multiples of 512 bytes\n        let address = match self.info.get_capacity() {\n            CardCapacity::StandardCapacity => block_idx * size_of::<DataBlock>() as u32,\n            _ => block_idx,\n        };\n        self.sdmmc\n            .cmd(common_cmd::set_block_length(size_of::<DataBlock>() as u32), true, false)?; // CMD16\n\n        // sdmmc_v1 uses different cmd/dma order than v2, but only for writes\n        #[cfg(sdmmc_v1)]\n        self.sdmmc.cmd(common_cmd::write_single_block(address), true, true)?;\n\n        let transfer = self.sdmmc.prepare_datapath_write(\n            aligned_ref(&buffer.0),\n            DatapathMode::Block(block_size(size_of::<DataBlock>())),\n        );\n\n        #[cfg(sdmmc_v2)]\n        self.sdmmc.cmd(common_cmd::write_single_block(address), true, true)?;\n\n        self.sdmmc.complete_datapath_transfer(transfer, true).await?;\n\n        // TODO: Make this configurable\n        let mut timeout: u32 = 0x00FF_FFFF;\n\n        while timeout > 0 {\n            let status: CardStatus<A::Ext> = self.sdmmc.read_status(self.info.get_address())?.into();\n            if status.ready_for_data() {\n                return Ok(());\n            }\n            timeout -= 1;\n        }\n\n        Err(Error::SoftwareTimeout)\n    }\n\n    /// Write multiple data blocks.\n    pub async fn write_blocks(&mut self, block_idx: u32, blocks: &[DataBlock]) -> Result<(), Error> {\n        let _scoped_wake_guard = self.sdmmc.info.rcc.wake_guard();\n\n        // NOTE(unsafe) reinterpret buffer as &[u32]\n        let buffer = unsafe {\n            core::slice::from_raw_parts(\n                blocks.as_ptr() as *const u32,\n                blocks.len() * size_of::<DataBlock>() / size_of::<u32>(),\n            )\n        };\n        // Always read 1 block of 512 bytes\n        // SDSC cards are byte addressed hence the blockaddress is in multiples of 512 bytes\n        let address = match self.info.get_capacity() {\n            CardCapacity::StandardCapacity => block_idx * size_of::<DataBlock>() as u32,\n            _ => block_idx,\n        };\n\n        self.sdmmc\n            .cmd(common_cmd::set_block_length(size_of::<DataBlock>() as u32), true, false)?; // CMD16\n\n        #[cfg(sdmmc_v1)]\n        self.sdmmc.cmd(common_cmd::write_multiple_blocks(address), true, true)?; // CMD25\n\n        // Setup write command\n        let transfer = self.sdmmc.prepare_datapath_write(\n            aligned_ref(buffer),\n            DatapathMode::Block(block_size(size_of::<DataBlock>())),\n        );\n        #[cfg(sdmmc_v2)]\n        self.sdmmc.cmd(common_cmd::write_multiple_blocks(address), true, true)?; // CMD25\n\n        self.sdmmc.complete_datapath_transfer(transfer, false).await?;\n\n        self.sdmmc.cmd(common_cmd::stop_transmission(), true, false)?; // CMD12\n        self.sdmmc.clear_interrupt_flags();\n\n        // TODO: Make this configurable\n        let mut timeout: u32 = 0x00FF_FFFF;\n\n        while timeout > 0 {\n            let status: CardStatus<A::Ext> = self.sdmmc.read_status(self.info.get_address())?.into();\n            if status.ready_for_data() {\n                return Ok(());\n            }\n            timeout -= 1;\n        }\n        Err(Error::SoftwareTimeout)\n    }\n}\n\nimpl<'a, 'b, A: Addressable> Drop for StorageDevice<'a, 'b, A> {\n    fn drop(&mut self) {\n        self.sdmmc.on_drop();\n    }\n}\n\n#[derive(Clone, Copy, Debug, Default)]\n/// SD Card\npub struct Card {\n    /// The type of this card\n    pub card_type: CardCapacity,\n    /// Operation Conditions Register\n    pub ocr: OCR<SD>,\n    /// Relative Card Address\n    pub rca: u16,\n    /// Card ID\n    pub cid: CID<SD>,\n    /// Card Specific Data\n    pub csd: CSD<SD>,\n    /// SD CARD Configuration Register\n    pub scr: SCR,\n    /// SD Status\n    pub status: SDStatus,\n}\n\nimpl Addressable for Card {\n    type Ext = SD;\n\n    /// Get this peripheral's address on the SDMMC bus\n    fn get_address(&self) -> u16 {\n        self.rca\n    }\n\n    /// Is this a standard or high capacity peripheral?\n    fn get_capacity(&self) -> CardCapacity {\n        self.card_type\n    }\n\n    /// Size in bytes\n    fn size(&self) -> u64 {\n        u64::from(self.csd.block_count()) * 512\n    }\n}\n\n#[derive(Clone, Copy, Debug, Default)]\n/// eMMC storage\npub struct Emmc {\n    /// The capacity of this card\n    pub capacity: CardCapacity,\n    /// Operation Conditions Register\n    pub ocr: OCR<EMMC>,\n    /// Relative Card Address\n    pub rca: u16,\n    /// Card ID\n    pub cid: CID<EMMC>,\n    /// Card Specific Data\n    pub csd: CSD<EMMC>,\n    /// Extended Card Specific Data\n    pub ext_csd: ExtCSD,\n}\n\nimpl Addressable for Emmc {\n    type Ext = EMMC;\n\n    /// Get this peripheral's address on the SDMMC bus\n    fn get_address(&self) -> u16 {\n        self.rca\n    }\n\n    /// Is this a standard or high capacity peripheral?\n    fn get_capacity(&self) -> CardCapacity {\n        self.capacity\n    }\n\n    /// Size in bytes\n    fn size(&self) -> u64 {\n        u64::from(self.ext_csd.sector_count()) * 512\n    }\n}\n\nimpl<'d, 'e, A: Addressable> block_device_driver::BlockDevice<512> for StorageDevice<'d, 'e, A> {\n    type Error = Error;\n    type Align = aligned::A4;\n\n    async fn read(\n        &mut self,\n        block_address: u32,\n        buf: &mut [aligned::Aligned<Self::Align, [u8; 512]>],\n    ) -> Result<(), Self::Error> {\n        // TODO: I think block_address needs to be adjusted by the partition start offset\n        if buf.len() == 1 {\n            let block = unsafe { &mut *(&mut buf[0] as *mut _ as *mut DataBlock) };\n            self.read_block(block_address, block).await?;\n        } else {\n            let blocks: &mut [DataBlock] =\n                unsafe { core::slice::from_raw_parts_mut(buf.as_mut_ptr() as *mut DataBlock, buf.len()) };\n            self.read_blocks(block_address, blocks).await?;\n        }\n        Ok(())\n    }\n\n    async fn write(\n        &mut self,\n        block_address: u32,\n        buf: &[aligned::Aligned<Self::Align, [u8; 512]>],\n    ) -> Result<(), Self::Error> {\n        // TODO: I think block_address needs to be adjusted by the partition start offset\n        if buf.len() == 1 {\n            let block = unsafe { &*(&buf[0] as *const _ as *const DataBlock) };\n            self.write_block(block_address, block).await?;\n        } else {\n            let blocks: &[DataBlock] =\n                unsafe { core::slice::from_raw_parts(buf.as_ptr() as *const DataBlock, buf.len()) };\n            self.write_blocks(block_address, blocks).await?;\n        }\n        Ok(())\n    }\n\n    async fn size(&mut self) -> Result<u64, Self::Error> {\n        Ok(self.info.size())\n    }\n}\n"
  },
  {
    "path": "embassy-stm32/src/sdmmc/sdio.rs",
    "content": "use core::future::poll_fn;\nuse core::ops::{Deref, DerefMut};\nuse core::sync::atomic::{Ordering, compiler_fence};\nuse core::task::Poll;\n\nuse aligned::{A4, Aligned};\nuse sdio_host::common_cmd::{R1, Resp, cmd};\nuse sdio_host::sd::{BusWidth, OCR, RCA, SD};\nuse sdio_host::{Cmd, sd_cmd};\n\nuse crate::sdmmc::{\n    CommandResponse, DatapathMode, Error, Sdmmc, TypedResp, aligned_mut, aligned_ref, block_size, slice8_mut,\n    slice8_ref,\n};\nuse crate::time::Hertz;\n\n/// R4: OCR register\npub struct R4;\n\nimpl Resp for R4 {}\n\nimpl TypedResp for R4 {\n    type Word = u32;\n}\n\nimpl From<CommandResponse<R4>> for OCR<SD> {\n    fn from(value: CommandResponse<R4>) -> Self {\n        OCR::<SD>::from(value.0)\n    }\n}\n\n/// R5: IO_RW_DIRECT Response\npub struct R5;\n\nimpl Resp for R5 {}\n\nimpl TypedResp for R5 {\n    type Word = u32;\n}\n\n/// ACMD5: IO Op Command\n///\n/// * `switch_to_1_8v_request` - Switch to 1.8V signaling\n/// * `voltage_window` - 9-bit bitfield that represents the voltage window\n/// supported by the host. Use 0x1FF to indicate support for the full range of\n/// voltages\npub fn io_send_op_cond(switch_to_1_8v_request: bool, voltage_window: u16) -> Cmd<R4> {\n    let arg: u32 = u32::from(switch_to_1_8v_request) << 24 | u32::from(voltage_window & 0x1FF) << 15;\n    cmd(5, arg)\n}\n\n/// Aligned data block for SDMMC transfers.\n///\n/// This is a 64-byte array, aligned to 4 bytes to satisfy DMA requirements.\n#[repr(align(4))]\n#[derive(Debug, Clone, PartialEq, Eq)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub struct DataBlock(pub [u32; 16]);\n\nimpl DataBlock {\n    /// Create a new DataBlock\n    pub const fn new() -> Self {\n        DataBlock([0u32; 16])\n    }\n}\n\nimpl Deref for DataBlock {\n    type Target = [u8; 64];\n\n    fn deref(&self) -> &Self::Target {\n        unwrap!(slice8_ref(&self.0[..]).try_into())\n    }\n}\n\nimpl DerefMut for DataBlock {\n    fn deref_mut(&mut self) -> &mut Self::Target {\n        unwrap!(slice8_mut(&mut self.0[..]).try_into())\n    }\n}\n\n/// Storage Device\npub struct SerialDataInterface<'a, 'b> {\n    /// Inner member\n    sdmmc: &'a mut Sdmmc<'b>,\n}\n\n/// Card Storage Device\nimpl<'a, 'b> SerialDataInterface<'a, 'b> {\n    /// Create a new SD card\n    pub async fn new(sdmmc: &'a mut Sdmmc<'b>, freq: Hertz) -> Result<Self, Error> {\n        let mut s = Self { sdmmc };\n\n        s.acquire(freq).await?;\n\n        Ok(s)\n    }\n\n    /// Initializes the card into a known state (or at least tries to).\n    async fn acquire(&mut self, _freq: Hertz) -> Result<(), Error> {\n        let _scoped_wake_guard = self.sdmmc.info.rcc.wake_guard();\n\n        let _bus_width = match self.sdmmc.bus_width() {\n            BusWidth::Eight => return Err(Error::BusWidth),\n            bus_width => bus_width,\n        };\n\n        // While the SD/SDIO card or eMMC is in identification mode,\n        // the SDMMC_CK frequency must be no more than 400 kHz.\n        self.sdmmc.init_idle()?;\n\n        // Get IO OCR\n        let _ocr: OCR<SD> = self.sdmmc.cmd(io_send_op_cond(false, 0x0), false, false)?.into();\n\n        // UDB-based SDIO does not support io volt switch sequence\n\n        // Get RCA\n        let rca: RCA<SD> = self.sdmmc.cmd(sd_cmd::send_relative_address(), true, false)?.into();\n        trace!(\"sdio: got rca {}\", rca.address());\n\n        // Select the card with RCA\n        self.sdmmc.select_card(Some(rca.address()))?;\n        trace!(\"sdio: selected card {}\", rca.address());\n\n        Ok(())\n    }\n\n    /// Set the bus to the 4-bit high-speed frequency\n    pub fn set_bus_to_high_speed(&mut self, frequency: Hertz) -> Result<(), Error> {\n        self.sdmmc.clkcr_set_clkdiv(frequency, BusWidth::Four)?;\n\n        Ok(())\n    }\n\n    /// Run cmd52\n    pub async fn cmd52(&mut self, arg: u32) -> Result<u16, Error> {\n        self.sdmmc\n            .cmd(cmd::<R5>(52, arg), true, false)\n            .map(|r| r.0.try_into().unwrap())\n    }\n\n    /// Read in block mode using cmd53\n    pub async fn cmd53_block_read(&mut self, arg: u32, blocks: &mut [DataBlock]) -> Result<(), Error> {\n        let _scoped_wake_guard = self.sdmmc.info.rcc.wake_guard();\n\n        // NOTE(unsafe) reinterpret buffer as &mut [u32]\n        let buffer = unsafe {\n            core::slice::from_raw_parts_mut(blocks.as_mut_ptr() as *mut u32, size_of_val(blocks) / size_of::<u32>())\n        };\n\n        let transfer = self.sdmmc.prepare_datapath_read(\n            aligned_mut(buffer),\n            DatapathMode::Block(block_size(size_of::<DataBlock>())),\n        );\n        self.sdmmc.cmd(cmd::<R1>(53, arg), true, true)?;\n\n        self.sdmmc.complete_datapath_transfer(transfer, false).await?;\n        self.sdmmc.clear_interrupt_flags();\n\n        Ok(())\n    }\n\n    /// Read in multibyte mode using cmd53\n    pub async fn cmd53_byte_read(&mut self, arg: u32, buffer: &mut Aligned<A4, [u8]>) -> Result<(), Error> {\n        let _scoped_wake_guard = self.sdmmc.info.rcc.wake_guard();\n\n        // trace!(\"byte read start (len): {:#x} ({})\", arg, buffer.len());\n\n        let transfer = self.sdmmc.prepare_datapath_read(buffer, DatapathMode::Byte);\n        self.sdmmc.cmd(cmd::<R1>(53, arg), true, true)?;\n\n        // trace!(\"byte read before complete\");\n\n        self.sdmmc.complete_datapath_transfer(transfer, false).await?;\n        self.sdmmc.clear_interrupt_flags();\n\n        // trace!(\"byte read stop\");\n\n        Ok(())\n    }\n\n    /// Write in block mode using cmd53\n    pub async fn cmd53_block_write(&mut self, arg: u32, blocks: &[DataBlock]) -> Result<(), Error> {\n        let _scoped_wake_guard = self.sdmmc.info.rcc.wake_guard();\n\n        // NOTE(unsafe) reinterpret buffer as &mut [u32]\n        let buffer = unsafe {\n            core::slice::from_raw_parts_mut(blocks.as_ptr() as *mut u32, size_of_val(blocks) / size_of::<u32>())\n        };\n\n        #[cfg(sdmmc_v1)]\n        self.sdmmc.cmd(cmd::<R1>(53, arg), true, true)?;\n\n        let transfer = self.sdmmc.prepare_datapath_write(\n            aligned_ref(buffer),\n            DatapathMode::Block(block_size(size_of::<DataBlock>())),\n        );\n\n        #[cfg(sdmmc_v2)]\n        self.sdmmc.cmd(cmd::<R1>(53, arg), true, true)?;\n\n        self.sdmmc.complete_datapath_transfer(transfer, false).await?;\n        self.sdmmc.clear_interrupt_flags();\n\n        Ok(())\n    }\n\n    /// Write in multibyte mode using cmd53\n    pub async fn cmd53_byte_write(&mut self, arg: u32, buffer: &Aligned<A4, [u8]>) -> Result<(), Error> {\n        let _scoped_wake_guard = self.sdmmc.info.rcc.wake_guard();\n\n        #[cfg(sdmmc_v1)]\n        self.sdmmc.cmd(cmd::<R1>(53, arg), true, true)?;\n\n        let transfer = self.sdmmc.prepare_datapath_write(buffer, DatapathMode::Byte);\n\n        #[cfg(sdmmc_v2)]\n        self.sdmmc.cmd(cmd::<R1>(53, arg), true, true)?;\n\n        self.sdmmc.complete_datapath_transfer(transfer, false).await?;\n        self.sdmmc.clear_interrupt_flags();\n\n        Ok(())\n    }\n\n    /// Wait for an interrupt event\n    pub async fn wait_for_event(&mut self) {\n        poll_fn(|cx| {\n            self.sdmmc.state.it_waker.register(cx.waker());\n\n            compiler_fence(Ordering::Release);\n\n            let status = self.sdmmc.info.regs.star().read();\n            let icr = self.sdmmc.info.regs.icr();\n            let maskr = self.sdmmc.info.regs.maskr();\n\n            if status.sdioit() {\n                icr.write(|w| w.set_sdioitc(true));\n\n                Poll::Ready(())\n            } else {\n                // Note maskr could be modified from irq\n                critical_section::with(|_| maskr.modify(|w| w.set_sdioitie(true)));\n\n                Poll::Pending\n            }\n        })\n        .await;\n    }\n}\n\nimpl<'a, 'b> Drop for SerialDataInterface<'a, 'b> {\n    fn drop(&mut self) {\n        self.sdmmc.on_drop();\n    }\n}\n\n#[cfg(feature = \"cyw43\")]\nimpl<'a, 'b> cyw43::SdioBusCyw43<64> for SerialDataInterface<'a, 'b> {\n    /// The error type for the BlockDevice implementation.\n    type Error = Error;\n\n    /// Doc\n    fn set_bus_to_high_speed(&mut self, frequency: u32) -> Result<(), Self::Error> {\n        self.set_bus_to_high_speed(Hertz(frequency))\n    }\n\n    /// Doc\n    async fn cmd52(&mut self, arg: u32) -> Result<u16, Self::Error> {\n        self.cmd52(arg).await\n    }\n\n    /// Doc\n    async fn cmd53_block_read(&mut self, arg: u32, blocks: &mut [Aligned<A4, [u8; 64]>]) -> Result<(), Self::Error> {\n        let blocks: &mut [DataBlock] =\n            unsafe { core::slice::from_raw_parts_mut(blocks.as_mut_ptr() as *mut DataBlock, blocks.len()) };\n\n        self.cmd53_block_read(arg, blocks).await\n    }\n\n    /// Doc\n    async fn cmd53_byte_read(&mut self, arg: u32, buffer: &mut Aligned<A4, [u8]>) -> Result<(), Self::Error> {\n        self.cmd53_byte_read(arg, buffer).await\n    }\n\n    /// Doc\n    async fn cmd53_block_write(&mut self, arg: u32, blocks: &[Aligned<A4, [u8; 64]>]) -> Result<(), Self::Error> {\n        let blocks: &[DataBlock] =\n            unsafe { core::slice::from_raw_parts(blocks.as_ptr() as *const DataBlock, blocks.len()) };\n\n        self.cmd53_block_write(arg, blocks).await\n    }\n\n    /// Doc\n    async fn cmd53_byte_write(&mut self, arg: u32, buffer: &Aligned<A4, [u8]>) -> Result<(), Self::Error> {\n        self.cmd53_byte_write(arg, buffer).await\n    }\n\n    async fn wait_for_event(&mut self) {\n        self.wait_for_event().await\n    }\n}\n"
  },
  {
    "path": "embassy-stm32/src/spdifrx/mod.rs",
    "content": "//! S/PDIF receiver\n#![macro_use]\n#![cfg_attr(gpdma, allow(unused))]\n\nuse core::marker::PhantomData;\n\nuse embassy_sync::waitqueue::AtomicWaker;\n\nuse crate::dma::ringbuffer::Error as RingbufferError;\npub use crate::dma::word;\nuse crate::dma::{Channel, ReadableRingBuffer, TransferOptions};\nuse crate::gpio::{AfType, Flex, Pull};\nuse crate::interrupt::typelevel::Interrupt;\nuse crate::pac::spdifrx::Spdifrx as Regs;\nuse crate::rcc::{RccInfo, SealedRccPeripheral};\nuse crate::{Peri, interrupt, peripherals};\n\n/// Possible S/PDIF preamble types.\n#[allow(dead_code)]\n#[repr(u8)]\nenum PreambleType {\n    Unused = 0x00,\n    /// The preamble changes to preamble “B” once every 192 frames to identify the start of the block structure used to\n    /// organize the channel status and user information.\n    B = 0x01,\n    /// The first sub-frame (left or “A” channel in stereophonic operation and primary channel in monophonic operation)\n    /// normally starts with preamble “M”\n    M = 0x02,\n    /// The second sub-frame (right or “B” channel in stereophonic operation and secondary channel in monophonic\n    /// operation) always starts with preamble “W”.\n    W = 0x03,\n}\n\nmacro_rules! new_spdifrx_pin {\n    ($name:ident, $af_type:expr) => {{\n        let pin = $name;\n        let input_sel = pin.input_sel();\n        set_as_af!(pin, $af_type);\n        (Some(Flex::new(pin)), input_sel)\n    }};\n}\n\nmacro_rules! impl_spdifrx_pin {\n    ($inst:ident, $pin:ident, $af:expr, $sel:expr) => {\n        impl crate::spdifrx::InPin<peripherals::$inst> for crate::peripherals::$pin {\n            fn af_num(&self) -> u8 {\n                $af\n            }\n            fn input_sel(&self) -> u8 {\n                $sel\n            }\n        }\n    };\n}\n\n/// Ring-buffered SPDIFRX driver.\n///\n/// Data is read by DMAs and stored in a ring buffer.\npub struct Spdifrx<'d, T: Instance> {\n    _peri: Peri<'d, T>,\n    _spdifrx_in: Option<Flex<'d>>,\n    data_ring_buffer: ReadableRingBuffer<'d, u32>,\n}\n\n/// Gives the address of the data register.\nfn dr_address(r: Regs) -> *mut u32 {\n    #[cfg(spdifrx_v1)]\n    let address = r.dr().as_ptr() as _;\n    #[cfg(spdifrx_h7)]\n    let address = r.fmt0_dr().as_ptr() as _; // All fmtx_dr() implementations have the same address.\n\n    return address;\n}\n\n/// Gives the address of the channel status register.\n#[allow(unused)]\nfn csr_address(r: Regs) -> *mut u32 {\n    r.csr().as_ptr() as _\n}\n\n/// Select the channel for capturing control information.\npub enum ControlChannelSelection {\n    /// Capture control info from channel A.\n    A,\n    /// Capture control info from channel B.\n    B,\n}\n\n/// Configuration options for the SPDIFRX driver.\npub struct Config {\n    /// Select the channel for capturing control information.\n    pub control_channel_selection: ControlChannelSelection,\n}\n\n/// S/PDIF errors.\n#[derive(Debug)]\npub enum Error {\n    /// DMA overrun error.\n    RingbufferError(RingbufferError),\n    /// Left/right channel synchronization error.\n    ChannelSyncError,\n}\n\nimpl From<RingbufferError> for Error {\n    fn from(error: RingbufferError) -> Self {\n        Self::RingbufferError(error)\n    }\n}\n\nimpl Default for Config {\n    fn default() -> Self {\n        Self {\n            control_channel_selection: ControlChannelSelection::A,\n        }\n    }\n}\n\nimpl<'d, T: Instance> Spdifrx<'d, T> {\n    fn dma_opts() -> TransferOptions {\n        TransferOptions {\n            half_transfer_ir: true,\n            // new_write() and new_read() always use circular mode\n            ..Default::default()\n        }\n    }\n\n    /// Create a new `Spdifrx` instance.\n    pub fn new<D>(\n        peri: Peri<'d, T>,\n        irq: impl interrupt::typelevel::Binding<T::GlobalInterrupt, GlobalInterruptHandler<T>>\n        + interrupt::typelevel::Binding<D::Interrupt, crate::dma::InterruptHandler<D>>\n        + 'd,\n        config: Config,\n        spdifrx_in: Peri<'d, impl InPin<T>>,\n        data_dma: Peri<'d, D>,\n        data_dma_buf: &'d mut [u32],\n    ) -> Self\n    where\n        D: Dma<T>,\n    {\n        let (spdifrx_in, input_sel) = new_spdifrx_pin!(spdifrx_in, AfType::input(Pull::None));\n        Self::setup(config, input_sel);\n\n        let regs = T::info().regs;\n        let dr_request = data_dma.request();\n        let dr_ring_buffer = unsafe {\n            ReadableRingBuffer::new(\n                Channel::new(data_dma, irq),\n                dr_request,\n                dr_address(regs),\n                data_dma_buf,\n                Self::dma_opts(),\n            )\n        };\n\n        Self {\n            _peri: peri,\n            _spdifrx_in: spdifrx_in,\n            data_ring_buffer: dr_ring_buffer,\n        }\n    }\n\n    fn setup(config: Config, input_sel: u8) {\n        T::info().rcc.enable_and_reset();\n        T::GlobalInterrupt::unpend();\n        unsafe { T::GlobalInterrupt::enable() };\n\n        let regs = T::info().regs;\n\n        regs.imr().write(|imr| {\n            imr.set_ifeie(true); // Enables interrupts for TERR, SERR, FERR.\n            imr.set_syncdie(true); // Enables SYNCD interrupt.\n        });\n\n        regs.cr().write(|cr| {\n            cr.set_spdifen(0x00); // Disable SPDIF receiver synchronization.\n            cr.set_rxdmaen(true); // Use RX DMA for data. Enabled on `read`.\n            cr.set_cbdmaen(false); // Do not capture channel info.\n            cr.set_rxsteo(true); // Operate in stereo mode.\n            cr.set_drfmt(0x01); // Data is left-aligned (MSB).\n\n            // Disable all status fields in the data register.\n            // Status can be obtained directly with the status register DMA.\n            cr.set_pmsk(false); // Write parity bit to the data register. FIXME: Add parity check.\n            cr.set_vmsk(false); // Write validity to the data register.\n            cr.set_cumsk(true); // Do not write C and U bits to the data register.\n            cr.set_ptmsk(false); // Write preamble bits to the data register.\n\n            cr.set_chsel(match config.control_channel_selection {\n                ControlChannelSelection::A => false,\n                ControlChannelSelection::B => true,\n            }); // Select channel status source.\n\n            cr.set_nbtr(0x02); // 16 attempts are allowed.\n            cr.set_wfa(true); // Wait for activity before going to synchronization phase.\n            cr.set_insel(input_sel); // Input pin selection.\n\n            #[cfg(stm32h7)]\n            cr.set_cksen(true); // Generate a symbol clock.\n\n            #[cfg(stm32h7)]\n            cr.set_cksbkpen(true); // Generate a backup symbol clock.\n        });\n    }\n\n    /// Start the SPDIFRX driver.\n    pub fn start(&mut self) {\n        self.data_ring_buffer.start();\n\n        T::info().regs.cr().modify(|cr| {\n            cr.set_spdifen(0x03); // Enable S/PDIF receiver.\n        });\n    }\n\n    /// Read from the SPDIFRX data ring buffer.\n    ///\n    /// SPDIFRX is always receiving data in the background. This function pops already-received\n    /// data from the buffer.\n    ///\n    /// If there's less than `data.len()` data in the buffer, this waits until there is.\n    pub async fn read(&mut self, data: &mut [u32]) -> Result<(), Error> {\n        self.data_ring_buffer.read_exact(data).await?;\n\n        let first_preamble = (data[0] >> 4) & 0b11_u32;\n        if (first_preamble as u8) == (PreambleType::W as u8) {\n            trace!(\"S/PDIF left/right mismatch\");\n\n            // Resynchronize until the first sample is for the left channel.\n            self.data_ring_buffer.clear();\n            return Err(Error::ChannelSyncError);\n        };\n\n        for sample in data.as_mut() {\n            if (*sample & (0x0002_u32)) != 0 {\n                // Discard invalid samples, setting them to mute level.\n                *sample = 0;\n            } else {\n                // Discard status information in the lowest byte.\n                *sample &= 0xFFFFFF00;\n            }\n        }\n\n        Ok(())\n    }\n}\n\nimpl<'d, T: Instance> Drop for Spdifrx<'d, T> {\n    fn drop(&mut self) {\n        T::info().regs.cr().modify(|cr| cr.set_spdifen(0x00));\n    }\n}\n\nstruct State {\n    #[allow(unused)]\n    waker: AtomicWaker,\n}\n\nimpl State {\n    const fn new() -> Self {\n        Self {\n            waker: AtomicWaker::new(),\n        }\n    }\n}\n\nstruct Info {\n    regs: crate::pac::spdifrx::Spdifrx,\n    rcc: RccInfo,\n}\n\nperi_trait!(\n    irqs: [GlobalInterrupt],\n);\n\n/// SPIDFRX pin trait\npub trait InPin<T: Instance>: crate::gpio::Pin {\n    /// Get the GPIO AF number needed to use this pin.\n    fn af_num(&self) -> u8;\n    /// Get the SPIDFRX INSEL number needed to use this pin.\n    fn input_sel(&self) -> u8;\n}\n\ndma_trait!(Dma, Instance);\n\n/// Global interrupt handler.\npub struct GlobalInterruptHandler<T: Instance> {\n    _phantom: PhantomData<T>,\n}\n\nimpl<T: Instance> interrupt::typelevel::Handler<T::GlobalInterrupt> for GlobalInterruptHandler<T> {\n    unsafe fn on_interrupt() {\n        T::state().waker.wake();\n\n        let regs = T::info().regs;\n        let sr = regs.sr().read();\n\n        if sr.serr() || sr.terr() || sr.ferr() {\n            trace!(\"SPDIFRX error, resync\");\n\n            // Clear errors by disabling SPDIFRX, then reenable.\n            regs.cr().modify(|cr| cr.set_spdifen(0x00));\n            regs.cr().modify(|cr| cr.set_spdifen(0x03));\n        } else if sr.syncd() {\n            // Synchronization was successful.\n            trace!(\"SPDIFRX sync success\");\n        }\n\n        // Clear interrupt flags.\n        regs.ifcr().write(|ifcr| {\n            ifcr.set_perrcf(true); // Clears parity error flag.\n            ifcr.set_ovrcf(true); // Clears overrun error flag.\n            ifcr.set_sbdcf(true); // Clears synchronization block detected flag.\n            ifcr.set_syncdcf(true); // Clears SYNCD from SR (was read above).\n        });\n    }\n}\n\nforeach_peripheral!(\n    (spdifrx, $inst:ident) => {\n        #[allow(private_interfaces)]\n        impl SealedInstance for peripherals::$inst {\n            fn info() -> &'static Info {\n                static INFO: Info = Info{\n                    regs: crate::pac::$inst,\n                    rcc: crate::peripherals::$inst::RCC_INFO,\n                };\n                &INFO\n            }\n            fn state() -> &'static State {\n                static STATE: State = State::new();\n                &STATE\n            }\n        }\n\n        impl Instance for peripherals::$inst {\n            type GlobalInterrupt = crate::_generated::peripheral_interrupts::$inst::GLOBAL;\n        }\n    };\n);\n"
  },
  {
    "path": "embassy-stm32/src/spi/mod.rs",
    "content": "//! Serial Peripheral Interface (SPI)\n#![macro_use]\n\nuse core::marker::PhantomData;\nuse core::ptr;\n\nuse embassy_embedded_hal::SetConfig;\nuse embassy_futures::join::join;\npub use embedded_hal_02::spi::{MODE_0, MODE_1, MODE_2, MODE_3, Mode, Phase, Polarity};\n\nuse crate::Peri;\nuse crate::dma::{ChannelAndRequest, word};\nuse crate::gpio::{AfType, Flex, OutputType, Pull, SealedPin as _, Speed};\nuse crate::mode::{Async, Blocking, Mode as PeriMode};\nuse crate::pac::spi::{Spi as Regs, regs, vals};\nuse crate::rcc::{RccInfo, SealedRccPeripheral};\nuse crate::time::Hertz;\n\n/// SPI error.\n#[derive(Debug, PartialEq, Eq, Clone, Copy)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub enum Error {\n    /// Invalid framing.\n    Framing,\n    /// CRC error (only if hardware CRC checking is enabled).\n    Crc,\n    /// Mode fault\n    ModeFault,\n    /// Overrun.\n    Overrun,\n}\n\nimpl core::fmt::Display for Error {\n    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {\n        let message = match self {\n            Self::Framing => \"Invalid Framing\",\n            Self::Crc => \"Hardware CRC Check Failed\",\n            Self::ModeFault => \"Mode Fault\",\n            Self::Overrun => \"Buffer Overrun\",\n        };\n\n        write!(f, \"{}\", message)\n    }\n}\n\nimpl core::error::Error for Error {}\n\n/// SPI bit order\n#[derive(Copy, Clone)]\npub enum BitOrder {\n    /// Least significant bit first.\n    LsbFirst,\n    /// Most significant bit first.\n    MsbFirst,\n}\n\n/// SPI Direction.\n#[derive(Debug, PartialEq, Eq, Clone, Copy)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub enum Direction {\n    /// Transmit\n    Transmit,\n    /// Receive\n    Receive,\n}\n\n/// Slave Select (SS) pin polarity.\n#[derive(Debug, PartialEq, Eq, Clone, Copy)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub enum SlaveSelectPolarity {\n    /// SS active high\n    ActiveHigh,\n    /// SS active low\n    ActiveLow,\n}\n\n/// SPI configuration.\n#[non_exhaustive]\n#[derive(Copy, Clone)]\npub struct Config {\n    /// SPI mode.\n    pub mode: Mode,\n    /// Bit order.\n    pub bit_order: BitOrder,\n    /// Clock frequency.\n    pub frequency: Hertz,\n    /// Enable internal pullup on MISO.\n    ///\n    /// There are some ICs that require a pull-up on the MISO pin for some applications.\n    /// If you  are unsure, you probably don't need this.\n    pub miso_pull: Pull,\n    /// signal rise/fall speed (slew rate) - defaults to `VeryHigh`.\n    /// Increase for high SPI speeds. Change to `Low` to reduce ringing.\n    pub gpio_speed: Speed,\n    /// If True sets SSOE to zero even if SPI is in Master Mode.\n    /// NSS output enabled (SSM = 0, SSOE = 1): The NSS signal is driven low when the master starts the communication and is kept low until the SPI is disabled.\n    /// NSS output disabled (SSM = 0, SSOE = 0): For devices set as slave, the NSS pin acts as a classical NSS input: the slave is selected when NSS is low and deselected when NSS high.\n    pub nss_output_disable: bool,\n    /// Slave Select (SS) pin polarity.\n    #[cfg(any(spi_v4, spi_v5, spi_v6))]\n    pub nss_polarity: SlaveSelectPolarity,\n}\n\nimpl Default for Config {\n    fn default() -> Self {\n        Self {\n            mode: MODE_0,\n            bit_order: BitOrder::MsbFirst,\n            frequency: Hertz(1_000_000),\n            miso_pull: Pull::None,\n            gpio_speed: Speed::VeryHigh,\n            nss_output_disable: false,\n            #[cfg(any(spi_v4, spi_v5, spi_v6))]\n            nss_polarity: SlaveSelectPolarity::ActiveHigh,\n        }\n    }\n}\n\nimpl Config {\n    fn raw_phase(&self) -> vals::Cpha {\n        match self.mode.phase {\n            Phase::CaptureOnSecondTransition => vals::Cpha::SECOND_EDGE,\n            Phase::CaptureOnFirstTransition => vals::Cpha::FIRST_EDGE,\n        }\n    }\n\n    fn raw_polarity(&self) -> vals::Cpol {\n        match self.mode.polarity {\n            Polarity::IdleHigh => vals::Cpol::IDLE_HIGH,\n            Polarity::IdleLow => vals::Cpol::IDLE_LOW,\n        }\n    }\n\n    fn raw_byte_order(&self) -> vals::Lsbfirst {\n        match self.bit_order {\n            BitOrder::LsbFirst => vals::Lsbfirst::LSBFIRST,\n            BitOrder::MsbFirst => vals::Lsbfirst::MSBFIRST,\n        }\n    }\n\n    #[cfg(any(spi_v4, spi_v5, spi_v6))]\n    fn raw_nss_polarity(&self) -> vals::Ssiop {\n        match self.nss_polarity {\n            SlaveSelectPolarity::ActiveHigh => vals::Ssiop::ACTIVE_HIGH,\n            SlaveSelectPolarity::ActiveLow => vals::Ssiop::ACTIVE_LOW,\n        }\n    }\n\n    #[cfg(gpio_v1)]\n    fn sck_af(&self) -> AfType {\n        AfType::output(OutputType::PushPull, self.gpio_speed)\n    }\n\n    #[cfg(gpio_v2)]\n    fn sck_af(&self) -> AfType {\n        AfType::output_pull(\n            OutputType::PushPull,\n            self.gpio_speed,\n            match self.mode.polarity {\n                Polarity::IdleLow => Pull::Down,\n                Polarity::IdleHigh => Pull::Up,\n            },\n        )\n    }\n}\n\n/// SPI communication mode\npub mod mode {\n    use stm32_metapac::spi::vals;\n\n    trait SealedMode {}\n\n    /// Trait for SPI communication mode operations.\n    #[allow(private_bounds)]\n    pub trait CommunicationMode: SealedMode {\n        /// Spi communication mode\n        #[cfg(not(any(spi_v4, spi_v5, spi_v6)))]\n        const MASTER: vals::Mstr;\n        /// Spi communication mode\n        #[cfg(any(spi_v4, spi_v5, spi_v6))]\n        const MASTER: vals::Master;\n    }\n\n    /// Mode allowing for SPI master operations.\n    pub struct Master;\n    /// Mode allowing for SPI slave operations.\n    pub struct Slave;\n\n    impl SealedMode for Master {}\n    impl CommunicationMode for Master {\n        #[cfg(not(any(spi_v4, spi_v5, spi_v6)))]\n        const MASTER: vals::Mstr = vals::Mstr::MASTER;\n        #[cfg(any(spi_v4, spi_v5, spi_v6))]\n        const MASTER: vals::Master = vals::Master::MASTER;\n    }\n\n    impl SealedMode for Slave {}\n    impl CommunicationMode for Slave {\n        #[cfg(not(any(spi_v4, spi_v5, spi_v6)))]\n        const MASTER: vals::Mstr = vals::Mstr::SLAVE;\n        #[cfg(any(spi_v4, spi_v5, spi_v6))]\n        const MASTER: vals::Master = vals::Master::SLAVE;\n    }\n}\nuse mode::{CommunicationMode, Master, Slave};\n\n/// SPI driver.\npub struct Spi<'d, M: PeriMode, CM: CommunicationMode> {\n    pub(crate) info: &'static Info,\n    kernel_clock: Hertz,\n    _sck: Option<Flex<'d>>,\n    _mosi: Option<Flex<'d>>,\n    miso: Option<Flex<'d>>,\n    nss: Option<Flex<'d>>,\n    tx_dma: Option<ChannelAndRequest<'d>>,\n    rx_dma: Option<ChannelAndRequest<'d>>,\n    _phantom: PhantomData<(M, CM)>,\n    current_word_size: word_impl::Config,\n    gpio_speed: Speed,\n}\n\nimpl<'d, M: PeriMode, CM: CommunicationMode> Spi<'d, M, CM> {\n    fn new_inner<T: Instance>(\n        _peri: Peri<'d, T>,\n        sck: Option<Flex<'d>>,\n        mosi: Option<Flex<'d>>,\n        miso: Option<Flex<'d>>,\n        nss: Option<Flex<'d>>,\n        tx_dma: Option<ChannelAndRequest<'d>>,\n        rx_dma: Option<ChannelAndRequest<'d>>,\n        config: Config,\n    ) -> Self {\n        let mut this = Self {\n            info: T::info(),\n            kernel_clock: T::frequency(),\n            _sck: sck,\n            _mosi: mosi,\n            miso,\n            nss,\n            tx_dma,\n            rx_dma,\n            current_word_size: <u8 as SealedWord>::CONFIG,\n            _phantom: PhantomData,\n            gpio_speed: config.gpio_speed,\n        };\n        this.enable_and_init(config);\n        this\n    }\n\n    fn enable_and_init(&mut self, config: Config) {\n        let br = compute_baud_rate(self.kernel_clock, config.frequency);\n        let cpha = config.raw_phase();\n        let cpol = config.raw_polarity();\n        let lsbfirst = config.raw_byte_order();\n\n        self.info.rcc.enable_and_reset_without_stop();\n\n        /*\n        - Software NSS management (SSM = 1)\n        The slave select information is driven internally by the value of the SSI bit in the\n        SPI_CR1 register. The external NSS pin remains free for other application uses.\n\n        - Hardware NSS management (SSM = 0)\n        Two configurations are possible depending on the NSS output configuration (SSOE bit\n        in register SPI_CR1).\n\n        -- NSS output enabled (SSM = 0, SSOE = 1)\n          This configuration is used only when the device operates in master mode. The\n          NSS signal is driven low when the master starts the communication and is kept\n          low until the SPI is disabled.\n\n        -- NSS output disabled (SSM = 0, SSOE = 0)\n            This configuration allows multimaster capability for devices operating in master\n            mode. For devices set as slave, the NSS pin acts as a classical NSS input: the\n            slave is selected when NSS is low and deselected when NSS high\n         */\n        let ssm = self.nss.is_none();\n\n        let regs = self.info.regs;\n        #[cfg(any(spi_v1, spi_v2))]\n        {\n            let ssoe = CM::MASTER == vals::Mstr::MASTER && !config.nss_output_disable;\n            regs.cr2().modify(|w| {\n                w.set_ssoe(ssoe);\n            });\n            regs.cr1().modify(|w| {\n                w.set_cpha(cpha);\n                w.set_cpol(cpol);\n\n                w.set_mstr(CM::MASTER);\n                w.set_br(br);\n                w.set_spe(true);\n                w.set_lsbfirst(lsbfirst);\n                w.set_ssi(CM::MASTER == vals::Mstr::MASTER);\n                w.set_ssm(ssm);\n                w.set_crcen(false);\n                w.set_bidimode(vals::Bidimode::UNIDIRECTIONAL);\n                // we're doing \"fake rxonly\", by actually writing one\n                // byte to TXDR for each byte we want to receive. if we\n                // set OUTPUTDISABLED here, this hangs.\n                w.set_rxonly(vals::Rxonly::FULL_DUPLEX);\n                w.set_dff(<u8 as SealedWord>::CONFIG)\n            });\n        }\n        #[cfg(spi_v3)]\n        {\n            let ssoe = CM::MASTER == vals::Mstr::MASTER && !config.nss_output_disable;\n            regs.cr2().modify(|w| {\n                let (ds, frxth) = <u8 as SealedWord>::CONFIG;\n                w.set_frxth(frxth);\n                w.set_ds(ds);\n                w.set_ssoe(ssoe);\n            });\n            regs.cr1().modify(|w| {\n                w.set_cpha(cpha);\n                w.set_cpol(cpol);\n\n                w.set_mstr(CM::MASTER);\n                w.set_br(br);\n                w.set_lsbfirst(lsbfirst);\n                w.set_ssi(CM::MASTER == vals::Mstr::MASTER);\n                w.set_ssm(ssm);\n                w.set_crcen(false);\n                w.set_bidimode(vals::Bidimode::UNIDIRECTIONAL);\n                w.set_spe(true);\n            });\n        }\n        #[cfg(any(spi_v4, spi_v5, spi_v6))]\n        {\n            let ssoe = CM::MASTER == vals::Master::MASTER && !config.nss_output_disable;\n            let ssiop = config.raw_nss_polarity();\n            regs.ifcr().write(|w| w.0 = 0xffff_ffff);\n            regs.cfg2().modify(|w| {\n                w.set_ssoe(ssoe);\n                w.set_cpha(cpha);\n                w.set_cpol(cpol);\n                w.set_lsbfirst(lsbfirst);\n                w.set_ssm(ssm);\n                w.set_master(CM::MASTER);\n                w.set_comm(vals::Comm::FULL_DUPLEX);\n                w.set_ssom(vals::Ssom::ASSERTED);\n                w.set_midi(0);\n                w.set_mssi(0);\n                w.set_afcntr(true);\n                w.set_ssiop(ssiop);\n            });\n            regs.cfg1().modify(|w| {\n                w.set_crcen(false);\n                w.set_mbr(br);\n                w.set_dsize(<u8 as SealedWord>::CONFIG);\n                w.set_fthlv(vals::Fthlv::ONE_FRAME);\n            });\n            regs.cr2().modify(|w| {\n                w.set_tsize(0);\n            });\n            regs.cr1().modify(|w| {\n                w.set_ssi(false);\n                w.set_spe(true);\n            });\n        }\n    }\n\n    /// Reconfigures it with the supplied config.\n    pub fn set_config(&mut self, config: &Config) -> Result<(), ()> {\n        let cpha = config.raw_phase();\n        let cpol = config.raw_polarity();\n\n        let lsbfirst = config.raw_byte_order();\n\n        let br = compute_baud_rate(self.kernel_clock, config.frequency);\n\n        #[cfg(gpio_v2)]\n        {\n            self.gpio_speed = config.gpio_speed;\n            if let Some(sck) = self._sck.as_ref() {\n                sck.pin.set_speed(config.gpio_speed);\n            }\n            if let Some(mosi) = self._mosi.as_ref() {\n                mosi.pin.set_speed(config.gpio_speed);\n            }\n        }\n\n        #[cfg(any(spi_v1, spi_v2, spi_v3))]\n        {\n            self.info.regs.cr1().modify(|w| {\n                w.set_spe(false);\n            });\n            self.info.regs.cr1().modify(|w| {\n                w.set_cpha(cpha);\n                w.set_cpol(cpol);\n                w.set_br(br);\n                w.set_lsbfirst(lsbfirst);\n            });\n            self.info.regs.cr1().modify(|w| {\n                w.set_spe(true);\n            });\n        }\n\n        #[cfg(any(spi_v4, spi_v5, spi_v6))]\n        {\n            let ssiop = config.raw_nss_polarity();\n\n            self.info.regs.cr1().modify(|w| {\n                w.set_spe(false);\n            });\n\n            self.info.regs.cfg2().modify(|w| {\n                w.set_cpha(cpha);\n                w.set_cpol(cpol);\n                w.set_lsbfirst(lsbfirst);\n                w.set_ssiop(ssiop);\n            });\n            self.info.regs.cfg1().modify(|w| {\n                w.set_mbr(br);\n            });\n\n            self.info.regs.cr1().modify(|w| {\n                w.set_spe(true);\n            });\n        }\n        Ok(())\n    }\n\n    /// Set SPI direction for bidirectional mode.\n    ///\n    /// This properly handles the STM32 requirement that BIDIOE cannot be changed\n    /// while the SPI peripheral is enabled (SPE=1). Per the STM32 reference manual,\n    /// we must wait for TXE=1 and BSY=0 before disabling SPE to ensure any ongoing\n    /// transfer completes cleanly.\n    ///\n    /// The SPE state is preserved: if SPI was enabled before this call, it will\n    /// be re-enabled after; if it was disabled, it remains disabled.\n    #[cfg(any(spi_v1, spi_v2, spi_v3))]\n    pub fn set_direction(&mut self, dir: Option<Direction>) {\n        let (bidimode, bidioe) = match dir {\n            Some(Direction::Transmit) => (vals::Bidimode::BIDIRECTIONAL, vals::Bidioe::TRANSMIT),\n            Some(Direction::Receive) => (vals::Bidimode::BIDIRECTIONAL, vals::Bidioe::RECEIVE),\n            None => (vals::Bidimode::UNIDIRECTIONAL, vals::Bidioe::TRANSMIT),\n        };\n\n        let was_enabled = self.info.regs.cr1().read().spe();\n\n        // If SPE is currently enabled, wait for any ongoing transfer to complete.\n        // Per STM32 reference manual: wait for TXE=1 then BSY=0 before disabling SPE.\n        if was_enabled {\n            while !self.info.regs.sr().read().txe() {}\n            while self.info.regs.sr().read().bsy() {}\n        }\n\n        // BIDIOE cannot be changed while SPE=1, so disable first\n        self.info.regs.cr1().modify(|w| {\n            w.set_spe(false);\n        });\n        self.info.regs.cr1().modify(|w| {\n            w.set_bidimode(bidimode);\n            w.set_bidioe(bidioe);\n        });\n\n        // Restore previous SPE state\n        if was_enabled {\n            self.info.regs.cr1().modify(|w| {\n                w.set_spe(true);\n            });\n        }\n    }\n\n    /// Get current SPI configuration.\n    pub fn get_current_config(&self) -> Config {\n        #[cfg(any(spi_v1, spi_v2, spi_v3))]\n        let cfg = self.info.regs.cr1().read();\n        #[cfg(any(spi_v4, spi_v5, spi_v6))]\n        let cfg = self.info.regs.cfg2().read();\n        #[cfg(any(spi_v4, spi_v5, spi_v6))]\n        let cfg1 = self.info.regs.cfg1().read();\n\n        #[cfg(any(spi_v1, spi_v2, spi_v3))]\n        let ssoe = self.info.regs.cr2().read().ssoe();\n        #[cfg(any(spi_v4, spi_v5, spi_v6))]\n        let ssoe = cfg.ssoe();\n\n        let polarity = if cfg.cpol() == vals::Cpol::IDLE_LOW {\n            Polarity::IdleLow\n        } else {\n            Polarity::IdleHigh\n        };\n        let phase = if cfg.cpha() == vals::Cpha::FIRST_EDGE {\n            Phase::CaptureOnFirstTransition\n        } else {\n            Phase::CaptureOnSecondTransition\n        };\n\n        let bit_order = if cfg.lsbfirst() == vals::Lsbfirst::LSBFIRST {\n            BitOrder::LsbFirst\n        } else {\n            BitOrder::MsbFirst\n        };\n\n        let miso_pull = match &self.miso {\n            None => Pull::None,\n            Some(pin) => pin.pin.pull(),\n        };\n\n        #[cfg(any(spi_v1, spi_v2, spi_v3))]\n        let br = cfg.br();\n        #[cfg(any(spi_v4, spi_v5, spi_v6))]\n        let br = cfg1.mbr();\n\n        let frequency = compute_frequency(self.kernel_clock, br);\n\n        // NSS output disabled if SSOE=0 or if SSM=1 software slave management enabled\n        let nss_output_disable = !ssoe || cfg.ssm();\n\n        #[cfg(any(spi_v4, spi_v5, spi_v6))]\n        let nss_polarity = if cfg.ssiop() == vals::Ssiop::ACTIVE_LOW {\n            SlaveSelectPolarity::ActiveLow\n        } else {\n            SlaveSelectPolarity::ActiveHigh\n        };\n\n        Config {\n            mode: Mode { polarity, phase },\n            bit_order,\n            frequency,\n            miso_pull,\n            gpio_speed: self.gpio_speed,\n            nss_output_disable,\n            #[cfg(any(spi_v4, spi_v5, spi_v6))]\n            nss_polarity,\n        }\n    }\n\n    pub(crate) fn set_word_size(&mut self, word_size: word_impl::Config) {\n        if self.current_word_size == word_size {\n            return;\n        }\n\n        self.info.regs.cr1().modify(|w| {\n            w.set_spe(false);\n        });\n\n        #[cfg(any(spi_v1, spi_v2))]\n        self.info.regs.cr1().modify(|reg| {\n            reg.set_dff(word_size);\n        });\n        #[cfg(spi_v3)]\n        self.info.regs.cr2().modify(|w| {\n            w.set_frxth(word_size.1);\n            w.set_ds(word_size.0);\n        });\n        #[cfg(any(spi_v4, spi_v5, spi_v6))]\n        self.info.regs.cfg1().modify(|w| {\n            w.set_dsize(word_size);\n        });\n\n        self.current_word_size = word_size;\n    }\n\n    /// Blocking write.\n    pub fn blocking_write<W: Word>(&mut self, words: &[W]) -> Result<(), Error> {\n        // needed in v3+ to avoid overrun causing the SPI RX state machine to get stuck...?\n        #[cfg(any(spi_v4, spi_v5, spi_v6))]\n        self.info.regs.cr1().modify(|w| w.set_spe(false));\n        self.set_word_size(W::CONFIG);\n        self.info.regs.cr1().modify(|w| w.set_spe(true));\n        flush_rx_fifo(self.info.regs);\n        for word in words.iter() {\n            // this cannot use `transfer_word` because on SPIv2 and higher,\n            // the SPI RX state machine hangs if no physical pin is connected to the SCK AF.\n            // This is the case when the SPI has been created with `new_(blocking_?)txonly_nosck`.\n            // See https://github.com/embassy-rs/embassy/issues/2902\n            // This is not documented as an errata by ST, and I've been unable to find anything online...\n            #[cfg(not(any(spi_v1, spi_v2)))]\n            write_word(self.info.regs, *word)?;\n\n            // if we're doing tx only, after writing the last byte to FIFO we have to wait\n            // until it's actually sent. On SPIv1 you're supposed to use the BSY flag for this\n            // but apparently it's broken, it clears too soon. Workaround is to wait for RXNE:\n            // when it gets set you know the transfer is done, even if you don't care about rx.\n            // Luckily this doesn't affect SPIv2+.\n            // See http://efton.sk/STM32/gotcha/g68.html\n            // ST doesn't seem to document this in errata sheets (?)\n            #[cfg(any(spi_v1, spi_v2))]\n            transfer_word(self.info.regs, *word)?;\n        }\n\n        // wait until last word is transmitted. (except on v1, see above)\n        #[cfg(not(any(spi_v1, spi_v2, spi_v3)))]\n        while !self.info.regs.sr().read().txc() {}\n        #[cfg(spi_v3)]\n        while self.info.regs.sr().read().bsy() {}\n\n        Ok(())\n    }\n\n    /// Blocking read.\n    pub fn blocking_read<W: Word>(&mut self, words: &mut [W]) -> Result<(), Error> {\n        // needed in v3+ to avoid overrun causing the SPI RX state machine to get stuck...?\n        #[cfg(any(spi_v4, spi_v5, spi_v6))]\n        self.info.regs.cr1().modify(|w| w.set_spe(false));\n        self.set_word_size(W::CONFIG);\n        self.info.regs.cr1().modify(|w| w.set_spe(true));\n        flush_rx_fifo(self.info.regs);\n        for word in words.iter_mut() {\n            *word = transfer_word(self.info.regs, W::default())?;\n        }\n        Ok(())\n    }\n\n    /// Blocking in-place bidirectional transfer.\n    ///\n    /// This writes the contents of `data` on MOSI, and puts the received data on MISO in `data`, at the same time.\n    pub fn blocking_transfer_in_place<W: Word>(&mut self, words: &mut [W]) -> Result<(), Error> {\n        // needed in v3+ to avoid overrun causing the SPI RX state machine to get stuck...?\n        #[cfg(any(spi_v4, spi_v5, spi_v6))]\n        self.info.regs.cr1().modify(|w| w.set_spe(false));\n        self.set_word_size(W::CONFIG);\n        self.info.regs.cr1().modify(|w| w.set_spe(true));\n        flush_rx_fifo(self.info.regs);\n        for word in words.iter_mut() {\n            *word = transfer_word(self.info.regs, *word)?;\n        }\n        Ok(())\n    }\n\n    /// Blocking bidirectional transfer.\n    ///\n    /// This transfers both buffers at the same time, so it is NOT equivalent to `write` followed by `read`.\n    ///\n    /// The transfer runs for `max(read.len(), write.len())` bytes. If `read` is shorter extra bytes are ignored.\n    /// If `write` is shorter it is padded with zero bytes.\n    pub fn blocking_transfer<W: Word>(&mut self, read: &mut [W], write: &[W]) -> Result<(), Error> {\n        // needed in v3+ to avoid overrun causing the SPI RX state machine to get stuck...?\n        #[cfg(any(spi_v4, spi_v5, spi_v6))]\n        self.info.regs.cr1().modify(|w| w.set_spe(false));\n        self.set_word_size(W::CONFIG);\n        self.info.regs.cr1().modify(|w| w.set_spe(true));\n        flush_rx_fifo(self.info.regs);\n        let len = read.len().max(write.len());\n        for i in 0..len {\n            let wb = write.get(i).copied().unwrap_or_default();\n            let rb = transfer_word(self.info.regs, wb)?;\n            if let Some(r) = read.get_mut(i) {\n                *r = rb;\n            }\n        }\n        Ok(())\n    }\n}\n\nimpl<'d> Spi<'d, Blocking, Slave> {\n    /// Create a new blocking SPI slave driver.\n    pub fn new_blocking_slave<T: Instance, #[cfg(afio)] A>(\n        peri: Peri<'d, T>,\n        sck: Peri<'d, if_afio!(impl SckPin<T, A>)>,\n        mosi: Peri<'d, if_afio!(impl MosiPin<T, A>)>,\n        miso: Peri<'d, if_afio!(impl MisoPin<T, A>)>,\n        cs: Peri<'d, if_afio!(impl CsPin<T, A>)>,\n        config: Config,\n    ) -> Self {\n        Self::new_inner(\n            peri,\n            new_pin!(sck, config.sck_af()),\n            new_pin!(mosi, AfType::output(OutputType::PushPull, config.gpio_speed)),\n            new_pin!(miso, AfType::input(config.miso_pull)),\n            new_pin!(cs, AfType::input(Pull::None)),\n            None,\n            None,\n            config,\n        )\n    }\n}\n\nimpl<'d> Spi<'d, Blocking, Master> {\n    /// Create a new blocking SPI driver.\n    pub fn new_blocking<T: Instance, #[cfg(afio)] A>(\n        peri: Peri<'d, T>,\n        sck: Peri<'d, if_afio!(impl SckPin<T, A>)>,\n        mosi: Peri<'d, if_afio!(impl MosiPin<T, A>)>,\n        miso: Peri<'d, if_afio!(impl MisoPin<T, A>)>,\n        config: Config,\n    ) -> Self {\n        Self::new_inner(\n            peri,\n            new_pin!(sck, config.sck_af()),\n            new_pin!(mosi, AfType::output(OutputType::PushPull, config.gpio_speed)),\n            new_pin!(miso, AfType::input(config.miso_pull)),\n            None,\n            None,\n            None,\n            config,\n        )\n    }\n\n    /// Create a new blocking SPI driver, in RX-only mode (only MISO pin, no MOSI).\n    pub fn new_blocking_rxonly<T: Instance, #[cfg(afio)] A>(\n        peri: Peri<'d, T>,\n        sck: Peri<'d, if_afio!(impl SckPin<T, A>)>,\n        miso: Peri<'d, if_afio!(impl MisoPin<T, A>)>,\n        config: Config,\n    ) -> Self {\n        Self::new_inner(\n            peri,\n            new_pin!(sck, config.sck_af()),\n            None,\n            new_pin!(miso, AfType::input(config.miso_pull)),\n            None,\n            None,\n            None,\n            config,\n        )\n    }\n\n    /// Create a new blocking SPI driver, in TX-only mode (only MOSI pin, no MISO).\n    pub fn new_blocking_txonly<T: Instance, #[cfg(afio)] A>(\n        peri: Peri<'d, T>,\n        sck: Peri<'d, if_afio!(impl SckPin<T, A>)>,\n        mosi: Peri<'d, if_afio!(impl MosiPin<T, A>)>,\n        config: Config,\n    ) -> Self {\n        Self::new_inner(\n            peri,\n            new_pin!(sck, config.sck_af()),\n            new_pin!(mosi, AfType::output(OutputType::PushPull, config.gpio_speed)),\n            None,\n            None,\n            None,\n            None,\n            config,\n        )\n    }\n\n    /// Create a new SPI driver, in TX-only mode, without SCK pin.\n    ///\n    /// This can be useful for bit-banging non-SPI protocols.\n    pub fn new_blocking_txonly_nosck<T: Instance, #[cfg(afio)] A>(\n        peri: Peri<'d, T>,\n        mosi: Peri<'d, if_afio!(impl MosiPin<T, A>)>,\n        config: Config,\n    ) -> Self {\n        Self::new_inner(\n            peri,\n            None,\n            new_pin!(mosi, AfType::output(OutputType::PushPull, config.gpio_speed)),\n            None,\n            None,\n            None,\n            None,\n            config,\n        )\n    }\n}\n\nimpl<'d> Spi<'d, Async, Slave> {\n    /// Create a new SPI slave driver.\n    pub fn new_slave<T: Instance, D1: TxDma<T>, D2: RxDma<T>, #[cfg(afio)] A>(\n        peri: Peri<'d, T>,\n        sck: Peri<'d, if_afio!(impl SckPin<T, A>)>,\n        mosi: Peri<'d, if_afio!(impl MosiPin<T, A>)>,\n        miso: Peri<'d, if_afio!(impl MisoPin<T, A>)>,\n        cs: Peri<'d, if_afio!(impl CsPin<T, A>)>,\n        tx_dma: Peri<'d, D1>,\n        rx_dma: Peri<'d, D2>,\n        _irq: impl crate::interrupt::typelevel::Binding<D1::Interrupt, crate::dma::InterruptHandler<D1>>\n        + crate::interrupt::typelevel::Binding<D2::Interrupt, crate::dma::InterruptHandler<D2>>\n        + 'd,\n        config: Config,\n    ) -> Self {\n        Self::new_inner(\n            peri,\n            new_pin!(sck, config.sck_af()),\n            new_pin!(mosi, AfType::output(OutputType::PushPull, config.gpio_speed)),\n            new_pin!(miso, AfType::input(config.miso_pull)),\n            new_pin!(cs, AfType::input(Pull::None)),\n            new_dma!(tx_dma, _irq),\n            new_dma!(rx_dma, _irq),\n            config,\n        )\n    }\n}\n\nimpl<'d> Spi<'d, Async, Master> {\n    /// Create a new SPI driver.\n    pub fn new<T: Instance, D1: TxDma<T>, D2: RxDma<T>, #[cfg(afio)] A>(\n        peri: Peri<'d, T>,\n        sck: Peri<'d, if_afio!(impl SckPin<T, A>)>,\n        mosi: Peri<'d, if_afio!(impl MosiPin<T, A>)>,\n        miso: Peri<'d, if_afio!(impl MisoPin<T, A>)>,\n        tx_dma: Peri<'d, D1>,\n        rx_dma: Peri<'d, D2>,\n        _irq: impl crate::interrupt::typelevel::Binding<D1::Interrupt, crate::dma::InterruptHandler<D1>>\n        + crate::interrupt::typelevel::Binding<D2::Interrupt, crate::dma::InterruptHandler<D2>>\n        + 'd,\n        config: Config,\n    ) -> Self {\n        Self::new_inner(\n            peri,\n            new_pin!(sck, config.sck_af()),\n            new_pin!(mosi, AfType::output(OutputType::PushPull, config.gpio_speed)),\n            new_pin!(miso, AfType::input(config.miso_pull)),\n            None,\n            new_dma!(tx_dma, _irq),\n            new_dma!(rx_dma, _irq),\n            config,\n        )\n    }\n\n    /// Create a new SPI driver, in RX-only mode (only MISO pin, no MOSI).\n    pub fn new_rxonly<T: Instance, #[cfg(any(spi_v1, spi_v2, spi_v3))] D1: TxDma<T>, D2: RxDma<T>, #[cfg(afio)] A>(\n        peri: Peri<'d, T>,\n        sck: Peri<'d, if_afio!(impl SckPin<T, A>)>,\n        miso: Peri<'d, if_afio!(impl MisoPin<T, A>)>,\n        #[cfg(any(spi_v1, spi_v2, spi_v3))] tx_dma: Peri<'d, D1>,\n        rx_dma: Peri<'d, D2>,\n        #[cfg(any(spi_v1, spi_v2, spi_v3))] _irq: impl crate::interrupt::typelevel::Binding<\n            D1::Interrupt,\n            crate::dma::InterruptHandler<D1>,\n        > + crate::interrupt::typelevel::Binding<\n            D2::Interrupt,\n            crate::dma::InterruptHandler<D2>,\n        > + 'd,\n        #[cfg(any(spi_v4, spi_v5, spi_v6))] _irq: impl crate::interrupt::typelevel::Binding<\n            D2::Interrupt,\n            crate::dma::InterruptHandler<D2>,\n        > + 'd,\n        config: Config,\n    ) -> Self {\n        Self::new_inner(\n            peri,\n            new_pin!(sck, config.sck_af()),\n            None,\n            new_pin!(miso, AfType::input(config.miso_pull)),\n            None,\n            #[cfg(any(spi_v1, spi_v2, spi_v3))]\n            new_dma!(tx_dma, _irq),\n            #[cfg(any(spi_v4, spi_v5, spi_v6))]\n            None,\n            new_dma!(rx_dma, _irq),\n            config,\n        )\n    }\n\n    /// Create a new SPI driver, in TX-only mode (only MOSI pin, no MISO).\n    pub fn new_txonly<T: Instance, D1: TxDma<T>, #[cfg(afio)] A>(\n        peri: Peri<'d, T>,\n        sck: Peri<'d, if_afio!(impl SckPin<T, A>)>,\n        mosi: Peri<'d, if_afio!(impl MosiPin<T, A>)>,\n        tx_dma: Peri<'d, D1>,\n        _irq: impl crate::interrupt::typelevel::Binding<D1::Interrupt, crate::dma::InterruptHandler<D1>> + 'd,\n        config: Config,\n    ) -> Self {\n        Self::new_inner(\n            peri,\n            new_pin!(sck, config.sck_af()),\n            new_pin!(mosi, AfType::output(OutputType::PushPull, config.gpio_speed)),\n            None,\n            None,\n            new_dma!(tx_dma, _irq),\n            None,\n            config,\n        )\n    }\n\n    /// Create a new SPI driver, in bidirectional mode, specifically in tranmit mode\n    #[cfg(any(spi_v1, spi_v2, spi_v3))]\n    pub fn new_bidi<T: Instance, D1: TxDma<T>, D2: RxDma<T>, #[cfg(afio)] A>(\n        peri: Peri<'d, T>,\n        sck: Peri<'d, if_afio!(impl SckPin<T, A>)>,\n        sdio: Peri<'d, if_afio!(impl MosiPin<T, A>)>,\n        tx_dma: Peri<'d, D1>,\n        rx_dma: Peri<'d, D2>,\n        _irq: impl crate::interrupt::typelevel::Binding<D1::Interrupt, crate::dma::InterruptHandler<D1>>\n        + crate::interrupt::typelevel::Binding<D2::Interrupt, crate::dma::InterruptHandler<D2>>\n        + 'd,\n        config: Config,\n    ) -> Self {\n        let mut this = Self::new_inner(\n            peri,\n            new_pin!(sck, config.sck_af()),\n            new_pin!(sdio, AfType::output(OutputType::PushPull, config.gpio_speed)),\n            None,\n            None,\n            new_dma!(tx_dma, _irq),\n            new_dma!(rx_dma, _irq),\n            config,\n        );\n        this.set_direction(Some(Direction::Transmit));\n        this\n    }\n\n    /// Create a new SPI driver, in TX-only mode, without SCK pin.\n    ///\n    /// This can be useful for bit-banging non-SPI protocols.\n    pub fn new_txonly_nosck<T: Instance, D1: TxDma<T>, #[cfg(afio)] A>(\n        peri: Peri<'d, T>,\n        mosi: Peri<'d, if_afio!(impl MosiPin<T, A>)>,\n        tx_dma: Peri<'d, D1>,\n        _irq: impl crate::interrupt::typelevel::Binding<D1::Interrupt, crate::dma::InterruptHandler<D1>> + 'd,\n        config: Config,\n    ) -> Self {\n        Self::new_inner(\n            peri,\n            None,\n            new_pin!(mosi, AfType::output(OutputType::PushPull, config.gpio_speed)),\n            None,\n            None,\n            new_dma!(tx_dma, _irq),\n            None,\n            config,\n        )\n    }\n\n    #[cfg(stm32wl)]\n    /// Useful for on chip peripherals like SUBGHZ which are hardwired.\n    pub fn new_subghz<T: Instance, D1: TxDma<T>, D2: RxDma<T>>(\n        peri: Peri<'d, T>,\n        tx_dma: Peri<'d, D1>,\n        rx_dma: Peri<'d, D2>,\n        _irq: impl crate::interrupt::typelevel::Binding<D1::Interrupt, crate::dma::InterruptHandler<D1>>\n        + crate::interrupt::typelevel::Binding<D2::Interrupt, crate::dma::InterruptHandler<D2>>\n        + 'd,\n    ) -> Self {\n        // see RM0453 rev 1 section 7.2.13 page 291\n        // The SUBGHZSPI_SCK frequency is obtained by PCLK3 divided by two.\n        // The SUBGHZSPI_SCK clock maximum speed must not exceed 16 MHz.\n        let pclk3_freq = <crate::peripherals::SUBGHZSPI as SealedRccPeripheral>::frequency().0;\n        let freq = Hertz(core::cmp::min(pclk3_freq / 2, 16_000_000));\n        let mut config = Config::default();\n        config.mode = MODE_0;\n        config.bit_order = BitOrder::MsbFirst;\n        config.frequency = freq;\n\n        Self::new_inner(\n            peri,\n            None,\n            None,\n            None,\n            None,\n            new_dma!(tx_dma, _irq),\n            new_dma!(rx_dma, _irq),\n            config,\n        )\n    }\n\n    #[allow(dead_code)]\n    pub(crate) fn new_internal<T: Instance>(\n        peri: Peri<'d, T>,\n        tx_dma: Option<ChannelAndRequest<'d>>,\n        rx_dma: Option<ChannelAndRequest<'d>>,\n        config: Config,\n    ) -> Self {\n        Self::new_inner(peri, None, None, None, None, tx_dma, rx_dma, config)\n    }\n}\n\nimpl<'d, CM: CommunicationMode> Spi<'d, Async, CM> {\n    /// SPI write, using DMA.\n    pub async fn write<W: Word>(&mut self, data: &[W]) -> Result<(), Error> {\n        let _scoped_wake_guard = self.info.rcc.wake_guard();\n        if data.is_empty() {\n            return Ok(());\n        }\n\n        self.info.regs.cr1().modify(|w| {\n            w.set_spe(false);\n        });\n        self.set_word_size(W::CONFIG);\n\n        let tx_dst = self.info.regs.tx_ptr();\n        let tx_f = unsafe { self.tx_dma.as_mut().unwrap().write(data, tx_dst, Default::default()) };\n\n        set_txdmaen(self.info.regs, true);\n        self.info.regs.cr1().modify(|w| {\n            w.set_spe(true);\n        });\n        #[cfg(any(spi_v4, spi_v5, spi_v6))]\n        self.info.regs.cr1().modify(|w| {\n            w.set_cstart(true);\n        });\n\n        tx_f.await;\n\n        finish_dma(self.info.regs);\n\n        Ok(())\n    }\n\n    /// SPI read, using DMA.\n    #[cfg(any(spi_v4, spi_v5, spi_v6))]\n    pub async fn read<W: Word>(&mut self, data: &mut [W]) -> Result<(), Error> {\n        let _scoped_wake_guard = self.info.rcc.wake_guard();\n        if data.is_empty() {\n            return Ok(());\n        }\n\n        let regs = self.info.regs;\n\n        regs.cr1().modify(|w| {\n            w.set_spe(false);\n        });\n\n        self.set_word_size(W::CONFIG);\n\n        let comm = regs.cfg2().modify(|w| {\n            let prev = w.comm();\n            w.set_comm(vals::Comm::RECEIVER);\n            prev\n        });\n\n        #[cfg(spi_v4)]\n        let i2scfg = regs.i2scfgr().modify(|w| {\n            w.i2smod().then(|| {\n                let prev = w.i2scfg();\n                w.set_i2scfg(match prev {\n                    vals::I2scfg::SLAVE_RX | vals::I2scfg::SLAVE_FULL_DUPLEX => vals::I2scfg::SLAVE_RX,\n                    vals::I2scfg::MASTER_RX | vals::I2scfg::MASTER_FULL_DUPLEX => vals::I2scfg::MASTER_RX,\n                    _ => panic!(\"unsupported configuration\"),\n                });\n                prev\n            })\n        });\n\n        let rx_src = regs.rx_ptr();\n\n        for mut chunk in data.chunks_mut(u16::max_value().into()) {\n            set_rxdmaen(regs, true);\n\n            let tsize = chunk.len();\n\n            let transfer = unsafe {\n                self.rx_dma\n                    .as_mut()\n                    .unwrap()\n                    .read(rx_src, &mut chunk, Default::default())\n            };\n\n            regs.cr2().modify(|w| {\n                w.set_tsize(tsize as u16);\n            });\n\n            regs.cr1().modify(|w| {\n                w.set_spe(true);\n            });\n\n            regs.cr1().modify(|w| {\n                w.set_cstart(true);\n            });\n\n            transfer.await;\n\n            finish_dma(regs);\n        }\n\n        regs.cr1().modify(|w| {\n            w.set_spe(false);\n        });\n\n        regs.cfg2().modify(|w| {\n            w.set_comm(comm);\n        });\n\n        regs.cr2().modify(|w| {\n            w.set_tsize(0);\n        });\n\n        #[cfg(spi_v4)]\n        if let Some(i2scfg) = i2scfg {\n            regs.i2scfgr().modify(|w| {\n                w.set_i2scfg(i2scfg);\n            });\n        }\n\n        Ok(())\n    }\n\n    /// SPI read, using DMA.\n    #[cfg(any(spi_v1, spi_v2, spi_v3))]\n    pub async fn read<W: Word>(&mut self, data: &mut [W]) -> Result<(), Error> {\n        let _scoped_wake_guard = self.info.rcc.wake_guard();\n        if data.is_empty() {\n            return Ok(());\n        }\n\n        self.info.regs.cr1().modify(|w| {\n            w.set_spe(false);\n        });\n\n        self.set_word_size(W::CONFIG);\n\n        // SPIv3 clears rxfifo on SPE=0\n        #[cfg(not(any(spi_v4, spi_v5, spi_v6)))]\n        flush_rx_fifo(self.info.regs);\n\n        set_rxdmaen(self.info.regs, true);\n\n        let clock_byte_count = data.len();\n\n        let rx_src = self.info.regs.rx_ptr();\n        let rx_f = unsafe { self.rx_dma.as_mut().unwrap().read(rx_src, data, Default::default()) };\n\n        let tx_dst = self.info.regs.tx_ptr();\n        let clock_byte = W::default();\n        let tx_f = unsafe {\n            self.tx_dma\n                .as_mut()\n                .unwrap()\n                .write_repeated(&clock_byte, clock_byte_count, tx_dst, Default::default())\n        };\n\n        set_txdmaen(self.info.regs, true);\n        self.info.regs.cr1().modify(|w| {\n            w.set_spe(true);\n        });\n        #[cfg(any(spi_v4, spi_v5, spi_v6))]\n        self.info.regs.cr1().modify(|w| {\n            w.set_cstart(true);\n        });\n\n        join(tx_f, rx_f).await;\n\n        finish_dma(self.info.regs);\n\n        Ok(())\n    }\n\n    async fn transfer_inner<W: Word>(&mut self, read: *mut [W], write: *const [W]) -> Result<(), Error> {\n        let _scoped_wake_guard = self.info.rcc.wake_guard();\n        assert_eq!(read.len(), write.len());\n        if read.len() == 0 {\n            return Ok(());\n        }\n\n        self.info.regs.cr1().modify(|w| {\n            w.set_spe(false);\n        });\n\n        self.set_word_size(W::CONFIG);\n\n        // SPIv3 clears rxfifo on SPE=0\n        #[cfg(not(any(spi_v4, spi_v5, spi_v6)))]\n        flush_rx_fifo(self.info.regs);\n\n        set_rxdmaen(self.info.regs, true);\n\n        let rx_src = self.info.regs.rx_ptr::<W>();\n        let rx_f = unsafe { self.rx_dma.as_mut().unwrap().read_raw(rx_src, read, Default::default()) };\n\n        let tx_dst: *mut W = self.info.regs.tx_ptr();\n        let tx_f = unsafe {\n            self.tx_dma\n                .as_mut()\n                .unwrap()\n                .write_raw(write, tx_dst, Default::default())\n        };\n\n        set_txdmaen(self.info.regs, true);\n        self.info.regs.cr1().modify(|w| {\n            w.set_spe(true);\n        });\n        #[cfg(any(spi_v4, spi_v5, spi_v6))]\n        self.info.regs.cr1().modify(|w| {\n            w.set_cstart(true);\n        });\n\n        join(tx_f, rx_f).await;\n\n        finish_dma(self.info.regs);\n\n        Ok(())\n    }\n\n    /// Bidirectional transfer, using DMA.\n    ///\n    /// This transfers both buffers at the same time, so it is NOT equivalent to `write` followed by `read`.\n    ///\n    /// The transfer runs for `max(read.len(), write.len())` bytes. If `read` is shorter extra bytes are ignored.\n    /// If `write` is shorter it is padded with zero bytes.\n    pub async fn transfer<W: Word>(&mut self, read: &mut [W], write: &[W]) -> Result<(), Error> {\n        let _scoped_wake_guard = self.info.rcc.wake_guard();\n\n        self.transfer_inner(read, write).await\n    }\n\n    /// In-place bidirectional transfer, using DMA.\n    ///\n    /// This writes the contents of `data` on MOSI, and puts the received data on MISO in `data`, at the same time.\n    pub async fn transfer_in_place<W: Word>(&mut self, data: &mut [W]) -> Result<(), Error> {\n        let _scoped_wake_guard = self.info.rcc.wake_guard();\n\n        self.transfer_inner(data, data).await\n    }\n}\n\nimpl<'d, M: PeriMode, CM: CommunicationMode> Drop for Spi<'d, M, CM> {\n    fn drop(&mut self) {\n        self.info.rcc.disable_without_stop();\n    }\n}\n\n#[cfg(not(any(spi_v4, spi_v5, spi_v6)))]\nuse vals::Br;\n#[cfg(any(spi_v4, spi_v5, spi_v6))]\nuse vals::Mbr as Br;\n\nfn compute_baud_rate(kernel_clock: Hertz, freq: Hertz) -> Br {\n    let val = match kernel_clock.0 / freq.0 {\n        0 => panic!(\"You are trying to reach a frequency higher than the clock\"),\n        1..=2 => 0b000,\n        3..=5 => 0b001,\n        6..=11 => 0b010,\n        12..=23 => 0b011,\n        24..=39 => 0b100,\n        40..=95 => 0b101,\n        96..=191 => 0b110,\n        _ => 0b111,\n    };\n\n    Br::from_bits(val)\n}\n\nfn compute_frequency(kernel_clock: Hertz, br: Br) -> Hertz {\n    let div: u16 = match br {\n        Br::DIV2 => 2,\n        Br::DIV4 => 4,\n        Br::DIV8 => 8,\n        Br::DIV16 => 16,\n        Br::DIV32 => 32,\n        Br::DIV64 => 64,\n        Br::DIV128 => 128,\n        Br::DIV256 => 256,\n    };\n\n    kernel_clock / div\n}\n\npub(crate) trait RegsExt {\n    fn tx_ptr<W>(&self) -> *mut W;\n    fn rx_ptr<W>(&self) -> *mut W;\n}\n\nimpl RegsExt for Regs {\n    fn tx_ptr<W>(&self) -> *mut W {\n        #[cfg(any(spi_v1, spi_v2))]\n        let dr = self.dr();\n        #[cfg(spi_v3)]\n        let dr = self.dr16();\n        #[cfg(any(spi_v4, spi_v5, spi_v6))]\n        let dr = self.txdr32();\n        dr.as_ptr() as *mut W\n    }\n\n    fn rx_ptr<W>(&self) -> *mut W {\n        #[cfg(any(spi_v1, spi_v2))]\n        let dr = self.dr();\n        #[cfg(spi_v3)]\n        let dr = self.dr16();\n        #[cfg(any(spi_v4, spi_v5, spi_v6))]\n        let dr = self.rxdr32();\n        dr.as_ptr() as *mut W\n    }\n}\n\nfn check_error_flags(sr: regs::Sr, ovr: bool) -> Result<(), Error> {\n    if sr.ovr() && ovr {\n        return Err(Error::Overrun);\n    }\n    #[cfg(not(any(spi_v1, spi_v4, spi_v5, spi_v6)))]\n    if sr.fre() {\n        return Err(Error::Framing);\n    }\n    #[cfg(any(spi_v4, spi_v5, spi_v6))]\n    if sr.tifre() {\n        return Err(Error::Framing);\n    }\n    if sr.modf() {\n        return Err(Error::ModeFault);\n    }\n    #[cfg(not(any(spi_v4, spi_v5, spi_v6)))]\n    if sr.crcerr() {\n        return Err(Error::Crc);\n    }\n    #[cfg(any(spi_v4, spi_v5, spi_v6))]\n    if sr.crce() {\n        return Err(Error::Crc);\n    }\n\n    Ok(())\n}\n\nfn spin_until_tx_ready(regs: Regs, ovr: bool) -> Result<(), Error> {\n    loop {\n        let sr = regs.sr().read();\n\n        check_error_flags(sr, ovr)?;\n\n        #[cfg(not(any(spi_v4, spi_v5, spi_v6)))]\n        if sr.txe() {\n            return Ok(());\n        }\n        #[cfg(any(spi_v4, spi_v5, spi_v6))]\n        if sr.txp() {\n            return Ok(());\n        }\n    }\n}\n\nfn spin_until_rx_ready(regs: Regs) -> Result<(), Error> {\n    loop {\n        let sr = regs.sr().read();\n\n        check_error_flags(sr, true)?;\n\n        #[cfg(not(any(spi_v4, spi_v5, spi_v6)))]\n        if sr.rxne() {\n            return Ok(());\n        }\n        #[cfg(any(spi_v4, spi_v5, spi_v6))]\n        if sr.rxp() {\n            return Ok(());\n        }\n    }\n}\n\npub(crate) fn flush_rx_fifo(regs: Regs) {\n    #[cfg(not(any(spi_v4, spi_v5, spi_v6)))]\n    while regs.sr().read().rxne() {\n        #[cfg(not(spi_v3))]\n        let _ = regs.dr().read();\n        #[cfg(spi_v3)]\n        let _ = regs.dr16().read();\n    }\n    #[cfg(any(spi_v4, spi_v5, spi_v6))]\n    while regs.sr().read().rxp() {\n        let _ = regs.rxdr32().read();\n    }\n}\n\npub(crate) fn set_txdmaen(regs: Regs, val: bool) {\n    #[cfg(not(any(spi_v4, spi_v5, spi_v6)))]\n    regs.cr2().modify(|reg| {\n        reg.set_txdmaen(val);\n    });\n    #[cfg(any(spi_v4, spi_v5, spi_v6))]\n    regs.cfg1().modify(|reg| {\n        reg.set_txdmaen(val);\n    });\n}\n\npub(crate) fn set_rxdmaen(regs: Regs, val: bool) {\n    #[cfg(not(any(spi_v4, spi_v5, spi_v6)))]\n    regs.cr2().modify(|reg| {\n        reg.set_rxdmaen(val);\n    });\n    #[cfg(any(spi_v4, spi_v5, spi_v6))]\n    regs.cfg1().modify(|reg| {\n        reg.set_rxdmaen(val);\n    });\n}\n\nfn finish_dma(regs: Regs) {\n    #[cfg(spi_v3)]\n    while regs.sr().read().ftlvl().to_bits() > 0 {}\n\n    #[cfg(any(spi_v4, spi_v5, spi_v6))]\n    {\n        if regs.cr2().read().tsize() == 0 {\n            while !regs.sr().read().txc() {}\n        } else {\n            while !regs.sr().read().eot() {}\n        }\n    }\n    #[cfg(not(any(spi_v4, spi_v5, spi_v6)))]\n    while regs.sr().read().bsy() {}\n\n    // Disable the spi peripheral\n    regs.cr1().modify(|w| {\n        w.set_spe(false);\n    });\n\n    // The peripheral automatically disables the DMA stream on completion without error,\n    // but it does not clear the RXDMAEN/TXDMAEN flag in CR2.\n    #[cfg(not(any(spi_v4, spi_v5, spi_v6)))]\n    regs.cr2().modify(|reg| {\n        reg.set_txdmaen(false);\n        reg.set_rxdmaen(false);\n    });\n    #[cfg(any(spi_v4, spi_v5, spi_v6))]\n    regs.cfg1().modify(|reg| {\n        reg.set_txdmaen(false);\n        reg.set_rxdmaen(false);\n    });\n}\n\nfn transfer_word<W: Word>(regs: Regs, tx_word: W) -> Result<W, Error> {\n    spin_until_tx_ready(regs, true)?;\n\n    unsafe {\n        ptr::write_volatile(regs.tx_ptr(), tx_word);\n\n        #[cfg(any(spi_v4, spi_v5, spi_v6))]\n        regs.cr1().modify(|reg| reg.set_cstart(true));\n    }\n\n    spin_until_rx_ready(regs)?;\n\n    let rx_word = unsafe { ptr::read_volatile(regs.rx_ptr()) };\n    Ok(rx_word)\n}\n\n#[allow(unused)] // unused in SPIv1\nfn write_word<W: Word>(regs: Regs, tx_word: W) -> Result<(), Error> {\n    // for write, we intentionally ignore the rx fifo, which will cause\n    // overrun errors that we have to ignore.\n    spin_until_tx_ready(regs, false)?;\n\n    unsafe {\n        ptr::write_volatile(regs.tx_ptr(), tx_word);\n\n        #[cfg(any(spi_v4, spi_v5, spi_v6))]\n        regs.cr1().modify(|reg| reg.set_cstart(true));\n    }\n    Ok(())\n}\n\n// Note: It is not possible to impl these traits generically in embedded-hal 0.2 due to a conflict with\n// some marker traits. For details, see https://github.com/rust-embedded/embedded-hal/pull/289\nmacro_rules! impl_blocking {\n    ($w:ident) => {\n        impl<'d, M: PeriMode, CM: CommunicationMode> embedded_hal_02::blocking::spi::Write<$w> for Spi<'d, M, CM> {\n            type Error = Error;\n\n            fn write(&mut self, words: &[$w]) -> Result<(), Self::Error> {\n                self.blocking_write(words)\n            }\n        }\n\n        impl<'d, M: PeriMode, CM: CommunicationMode> embedded_hal_02::blocking::spi::Transfer<$w> for Spi<'d, M, CM> {\n            type Error = Error;\n\n            fn transfer<'w>(&mut self, words: &'w mut [$w]) -> Result<&'w [$w], Self::Error> {\n                self.blocking_transfer_in_place(words)?;\n                Ok(words)\n            }\n        }\n    };\n}\n\nimpl_blocking!(u8);\nimpl_blocking!(u16);\n\nimpl<'d, M: PeriMode, CM: CommunicationMode> embedded_hal_1::spi::ErrorType for Spi<'d, M, CM> {\n    type Error = Error;\n}\n\nimpl<'d, W: Word, M: PeriMode, CM: CommunicationMode> embedded_hal_1::spi::SpiBus<W> for Spi<'d, M, CM> {\n    fn flush(&mut self) -> Result<(), Self::Error> {\n        Ok(())\n    }\n\n    fn read(&mut self, words: &mut [W]) -> Result<(), Self::Error> {\n        self.blocking_read(words)\n    }\n\n    fn write(&mut self, words: &[W]) -> Result<(), Self::Error> {\n        self.blocking_write(words)\n    }\n\n    fn transfer(&mut self, read: &mut [W], write: &[W]) -> Result<(), Self::Error> {\n        self.blocking_transfer(read, write)\n    }\n\n    fn transfer_in_place(&mut self, words: &mut [W]) -> Result<(), Self::Error> {\n        self.blocking_transfer_in_place(words)\n    }\n}\n\nimpl embedded_hal_1::spi::Error for Error {\n    fn kind(&self) -> embedded_hal_1::spi::ErrorKind {\n        match *self {\n            Self::Framing => embedded_hal_1::spi::ErrorKind::FrameFormat,\n            Self::Crc => embedded_hal_1::spi::ErrorKind::Other,\n            Self::ModeFault => embedded_hal_1::spi::ErrorKind::ModeFault,\n            Self::Overrun => embedded_hal_1::spi::ErrorKind::Overrun,\n        }\n    }\n}\n\nimpl<'d, W: Word, CM: CommunicationMode> embedded_hal_async::spi::SpiBus<W> for Spi<'d, Async, CM> {\n    async fn flush(&mut self) -> Result<(), Self::Error> {\n        Ok(())\n    }\n\n    async fn write(&mut self, words: &[W]) -> Result<(), Self::Error> {\n        self.write(words).await\n    }\n\n    async fn read(&mut self, words: &mut [W]) -> Result<(), Self::Error> {\n        self.read(words).await\n    }\n\n    async fn transfer(&mut self, read: &mut [W], write: &[W]) -> Result<(), Self::Error> {\n        self.transfer(read, write).await\n    }\n\n    async fn transfer_in_place(&mut self, words: &mut [W]) -> Result<(), Self::Error> {\n        self.transfer_in_place(words).await\n    }\n}\n\npub(crate) trait SealedWord {\n    const CONFIG: word_impl::Config;\n}\n\n/// Word sizes usable for SPI.\n#[allow(private_bounds)]\npub trait Word: word::Word + SealedWord + Default {}\n\nmacro_rules! impl_word {\n    ($T:ty, $config:expr) => {\n        impl SealedWord for $T {\n            const CONFIG: Config = $config;\n        }\n        impl Word for $T {}\n    };\n}\n\n#[cfg(any(spi_v1, spi_v2))]\nmod word_impl {\n    use super::*;\n\n    pub type Config = vals::Dff;\n\n    impl_word!(u8, vals::Dff::BITS8);\n    impl_word!(u16, vals::Dff::BITS16);\n}\n\n#[cfg(spi_v3)]\nmod word_impl {\n    use super::*;\n\n    pub type Config = (vals::Ds, vals::Frxth);\n\n    impl_word!(word::U4, (vals::Ds::BITS4, vals::Frxth::QUARTER));\n    impl_word!(word::U5, (vals::Ds::BITS5, vals::Frxth::QUARTER));\n    impl_word!(word::U6, (vals::Ds::BITS6, vals::Frxth::QUARTER));\n    impl_word!(word::U7, (vals::Ds::BITS7, vals::Frxth::QUARTER));\n    impl_word!(u8, (vals::Ds::BITS8, vals::Frxth::QUARTER));\n    impl_word!(word::U9, (vals::Ds::BITS9, vals::Frxth::HALF));\n    impl_word!(word::U10, (vals::Ds::BITS10, vals::Frxth::HALF));\n    impl_word!(word::U11, (vals::Ds::BITS11, vals::Frxth::HALF));\n    impl_word!(word::U12, (vals::Ds::BITS12, vals::Frxth::HALF));\n    impl_word!(word::U13, (vals::Ds::BITS13, vals::Frxth::HALF));\n    impl_word!(word::U14, (vals::Ds::BITS14, vals::Frxth::HALF));\n    impl_word!(word::U15, (vals::Ds::BITS15, vals::Frxth::HALF));\n    impl_word!(u16, (vals::Ds::BITS16, vals::Frxth::HALF));\n}\n\n#[cfg(any(spi_v4, spi_v5, spi_v6))]\nmod word_impl {\n    use super::*;\n\n    pub type Config = u8;\n\n    impl_word!(word::U4, 4 - 1);\n    impl_word!(word::U5, 5 - 1);\n    impl_word!(word::U6, 6 - 1);\n    impl_word!(word::U7, 7 - 1);\n    impl_word!(u8, 8 - 1);\n    impl_word!(word::U9, 9 - 1);\n    impl_word!(word::U10, 10 - 1);\n    impl_word!(word::U11, 11 - 1);\n    impl_word!(word::U12, 12 - 1);\n    impl_word!(word::U13, 13 - 1);\n    impl_word!(word::U14, 14 - 1);\n    impl_word!(word::U15, 15 - 1);\n    impl_word!(u16, 16 - 1);\n    impl_word!(word::U17, 17 - 1);\n    impl_word!(word::U18, 18 - 1);\n    impl_word!(word::U19, 19 - 1);\n    impl_word!(word::U20, 20 - 1);\n    impl_word!(word::U21, 21 - 1);\n    impl_word!(word::U22, 22 - 1);\n    impl_word!(word::U23, 23 - 1);\n    impl_word!(word::U24, 24 - 1);\n    impl_word!(word::U25, 25 - 1);\n    impl_word!(word::U26, 26 - 1);\n    impl_word!(word::U27, 27 - 1);\n    impl_word!(word::U28, 28 - 1);\n    impl_word!(word::U29, 29 - 1);\n    impl_word!(word::U30, 30 - 1);\n    impl_word!(word::U31, 31 - 1);\n    impl_word!(u32, 32 - 1);\n}\n\npub(crate) struct Info {\n    pub(crate) regs: Regs,\n    pub(crate) rcc: RccInfo,\n}\n\nstruct State {}\n\nimpl State {\n    #[allow(unused)]\n    const fn new() -> Self {\n        Self {}\n    }\n}\n\nperi_trait!();\n\npin_trait!(SckPin, Instance, @A);\npin_trait!(MosiPin, Instance, @A);\npin_trait!(MisoPin, Instance, @A);\npin_trait!(CsPin, Instance, @A);\npin_trait!(MckPin, Instance, @A);\npin_trait!(CkPin, Instance, @A);\npin_trait!(WsPin, Instance, @A);\npin_trait!(I2sSdPin, Instance, @A);\ndma_trait!(RxDma, Instance);\ndma_trait!(TxDma, Instance);\n\nforeach_peripheral!(\n    (spi, $inst:ident) => {\n        peri_trait_impl!($inst, Info {\n            regs: crate::pac::$inst,\n            rcc: crate::peripherals::$inst::RCC_INFO,\n        });\n    };\n);\n\nimpl<'d, M: PeriMode, CM: CommunicationMode> SetConfig for Spi<'d, M, CM> {\n    type Config = Config;\n    type ConfigError = ();\n    fn set_config(&mut self, config: &Self::Config) -> Result<(), ()> {\n        self.set_config(config)\n    }\n}\n"
  },
  {
    "path": "embassy-stm32/src/time.rs",
    "content": "//! Time units\n\nuse core::fmt::Display;\nuse core::ops::{Div, Mul};\n\n/// Hertz\n#[derive(Eq, PartialEq, Ord, PartialOrd, Clone, Copy, Debug, Default)]\npub struct Hertz(pub u32);\n\nimpl Display for Hertz {\n    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {\n        write!(f, \"{} Hz\", self.0)\n    }\n}\n\n#[cfg(feature = \"defmt\")]\nimpl defmt::Format for Hertz {\n    fn format(&self, f: defmt::Formatter) {\n        defmt::write!(f, \"{=u32} Hz\", self.0)\n    }\n}\n\nimpl Hertz {\n    /// Create a `Hertz` from the given hertz.\n    pub const fn hz(hertz: u32) -> Self {\n        Self(hertz)\n    }\n\n    /// Create a `Hertz` from the given kilohertz.\n    pub const fn khz(kilohertz: u32) -> Self {\n        Self(kilohertz * 1_000)\n    }\n\n    /// Create a `Hertz` from the given megahertz.\n    pub const fn mhz(megahertz: u32) -> Self {\n        Self(megahertz * 1_000_000)\n    }\n}\n\npub(crate) trait Prescaler {\n    fn num(&self) -> u32;\n    fn denom(&self) -> u32;\n}\n\nimpl<T: Prescaler> Div<T> for Hertz {\n    type Output = Hertz;\n    fn div(self, rhs: T) -> Self::Output {\n        self * rhs.denom() / rhs.num()\n    }\n}\n\nimpl<T: Prescaler> Mul<T> for Hertz {\n    type Output = Hertz;\n    fn mul(self, rhs: T) -> Self::Output {\n        self * rhs.num() / rhs.denom()\n    }\n}\n\n/// This is a convenience shortcut for [`Hertz::hz`]\npub const fn hz(hertz: u32) -> Hertz {\n    Hertz::hz(hertz)\n}\n\n/// This is a convenience shortcut for [`Hertz::khz`]\npub const fn khz(kilohertz: u32) -> Hertz {\n    Hertz::khz(kilohertz)\n}\n\n/// This is a convenience shortcut for [`Hertz::mhz`]\npub const fn mhz(megahertz: u32) -> Hertz {\n    Hertz::mhz(megahertz)\n}\n\nimpl Mul<u32> for Hertz {\n    type Output = Hertz;\n    fn mul(self, rhs: u32) -> Self::Output {\n        Hertz(self.0 * rhs)\n    }\n}\n\nimpl Div<u32> for Hertz {\n    type Output = Hertz;\n    fn div(self, rhs: u32) -> Self::Output {\n        Hertz(self.0 / rhs)\n    }\n}\n\nimpl Mul<u16> for Hertz {\n    type Output = Hertz;\n    fn mul(self, rhs: u16) -> Self::Output {\n        self * (rhs as u32)\n    }\n}\n\nimpl Div<u16> for Hertz {\n    type Output = Hertz;\n    fn div(self, rhs: u16) -> Self::Output {\n        self / (rhs as u32)\n    }\n}\n\nimpl Mul<u8> for Hertz {\n    type Output = Hertz;\n    fn mul(self, rhs: u8) -> Self::Output {\n        self * (rhs as u32)\n    }\n}\n\nimpl Div<u8> for Hertz {\n    type Output = Hertz;\n    fn div(self, rhs: u8) -> Self::Output {\n        self / (rhs as u32)\n    }\n}\n\nimpl Div<Hertz> for Hertz {\n    type Output = u32;\n    fn div(self, rhs: Hertz) -> Self::Output {\n        self.0 / rhs.0\n    }\n}\n\n#[repr(C)]\n#[derive(Eq, PartialEq, Ord, PartialOrd, Clone, Copy, Debug, Default)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\n/// A variant on [Hertz] that acts as an `Option<Hertz>` that is smaller and repr C.\n///\n/// An `Option<Hertz>` can be `.into()`'d into this type and back.\n/// The only restriction is that that [Hertz] cannot have the value 0 since that's\n/// seen as the `None` variant.\npub struct MaybeHertz(u32);\n\nimpl MaybeHertz {\n    /// Same as calling the `.into()` function, but without type inference.\n    pub fn to_hertz(self) -> Option<Hertz> {\n        self.into()\n    }\n}\n\nimpl From<Option<Hertz>> for MaybeHertz {\n    fn from(value: Option<Hertz>) -> Self {\n        match value {\n            Some(Hertz(0)) => panic!(\"Hertz cannot be 0\"),\n            Some(Hertz(val)) => Self(val),\n            None => Self(0),\n        }\n    }\n}\n\nimpl From<MaybeHertz> for Option<Hertz> {\n    fn from(value: MaybeHertz) -> Self {\n        match value {\n            MaybeHertz(0) => None,\n            MaybeHertz(val) => Some(Hertz(val)),\n        }\n    }\n}\n"
  },
  {
    "path": "embassy-stm32/src/time_driver/gp16.rs",
    "content": "#![allow(non_snake_case)]\n\n#[cfg(feature = \"low-power\")]\nuse core::cell::Cell;\nuse core::cell::RefCell;\nuse core::sync::atomic::{AtomicU32, Ordering, compiler_fence};\n\nuse critical_section::CriticalSection;\nuse embassy_sync::blocking_mutex::Mutex;\nuse embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;\nuse embassy_time_driver::{Driver, TICK_HZ};\nuse embassy_time_queue_utils::Queue;\nuse stm32_metapac::timer::{TimGp16, regs};\n\nuse super::AlarmState;\nuse crate::interrupt::typelevel::Interrupt;\nuse crate::pac::timer::vals;\nuse crate::peripherals;\nuse crate::rcc::{self, SealedRccPeripheral};\n#[cfg(feature = \"low-power\")]\nuse crate::rtc::Rtc;\nuse crate::timer::{CoreInstance, GeneralInstance1Channel};\n\n// NOTE regarding ALARM_COUNT:\n//\n// As of 2023-12-04, this driver is implemented using CC1 as the halfway rollover interrupt, and any\n// additional CC capabilities to provide timer alarms to embassy-time. embassy-time requires AT LEAST\n// one alarm to be allocatable, which means timers that only have CC1, such as TIM16/TIM17, are not\n// candidates for use as an embassy-time driver provider. (a.k.a 1CH and 1CH_CMP are not, others are good.)\n\n#[cfg(time_driver_tim1)]\ntype T = peripherals::TIM1;\n#[cfg(time_driver_tim2)]\ntype T = peripherals::TIM2;\n#[cfg(time_driver_tim3)]\ntype T = peripherals::TIM3;\n#[cfg(time_driver_tim4)]\ntype T = peripherals::TIM4;\n#[cfg(time_driver_tim5)]\ntype T = peripherals::TIM5;\n#[cfg(time_driver_tim8)]\ntype T = peripherals::TIM8;\n#[cfg(time_driver_tim9)]\ntype T = peripherals::TIM9;\n#[cfg(time_driver_tim12)]\ntype T = peripherals::TIM12;\n#[cfg(time_driver_tim15)]\ntype T = peripherals::TIM15;\n#[cfg(time_driver_tim20)]\ntype T = peripherals::TIM20;\n#[cfg(time_driver_tim21)]\ntype T = peripherals::TIM21;\n#[cfg(time_driver_tim22)]\ntype T = peripherals::TIM22;\n#[cfg(time_driver_tim23)]\ntype T = peripherals::TIM23;\n#[cfg(time_driver_tim24)]\ntype T = peripherals::TIM24;\n\nfn regs_gp16() -> TimGp16 {\n    unsafe { TimGp16::from_ptr(T::regs()) }\n}\n\n// Clock timekeeping works with something we call \"periods\", which are time intervals\n// of 2^15 ticks. The Clock counter value is 16 bits, so one \"overflow cycle\" is 2 periods.\n//\n// A `period` count is maintained in parallel to the Timer hardware `counter`, like this:\n// - `period` and `counter` start at 0\n// - `period` is incremented on overflow (at counter value 0)\n// - `period` is incremented \"midway\" between overflows (at counter value 0x8000)\n//\n// Therefore, when `period` is even, counter is in 0..0x7FFF. When odd, counter is in 0x8000..0xFFFF\n// This allows for now() to return the correct value even if it races an overflow.\n//\n// To get `now()`, `period` is read first, then `counter` is read. If the counter value matches\n// the expected range for the `period` parity, we're done. If it doesn't, this means that\n// a new period start has raced us between reading `period` and `counter`, so we assume the `counter` value\n// corresponds to the next period.\n//\n// `period` is a 32bit integer, so It overflows on 2^32 * 2^15 / 32768 seconds of uptime, which is 136 years.\nfn calc_now(period: u32, counter: u16) -> u64 {\n    ((period as u64) << 15) + ((counter as u32 ^ ((period & 1) << 15)) as u64)\n}\n\n#[cfg(feature = \"low-power\")]\nfn calc_period_counter(ticks: u64) -> (u32, u16) {\n    (2 * (ticks >> 16) as u32 + (ticks as u16 >= 0x8000) as u32, ticks as u16)\n}\n\npub(crate) struct RtcDriver {\n    /// Number of 2^15 periods elapsed since boot.\n    period: AtomicU32,\n    alarm: Mutex<CriticalSectionRawMutex, AlarmState>,\n    #[cfg(feature = \"low-power\")]\n    pub(crate) rtc: Mutex<CriticalSectionRawMutex, RefCell<Option<Rtc>>>,\n    #[cfg(feature = \"low-power\")]\n    /// The minimum pause time beyond which the executor will enter a low-power state.\n    min_stop_pause: Mutex<CriticalSectionRawMutex, Cell<embassy_time::Duration>>,\n    queue: Mutex<CriticalSectionRawMutex, RefCell<Queue>>,\n}\n\nembassy_time_driver::time_driver_impl!(static DRIVER: RtcDriver = RtcDriver {\n    period: AtomicU32::new(0),\n    alarm: Mutex::const_new(CriticalSectionRawMutex::new(), AlarmState::new()),\n    #[cfg(feature = \"low-power\")]\n    rtc: Mutex::const_new(CriticalSectionRawMutex::new(), RefCell::new(None)),\n    #[cfg(feature = \"low-power\")]\n    min_stop_pause: Mutex::const_new(CriticalSectionRawMutex::new(), Cell::new(embassy_time::Duration::from_millis(0))),\n    queue: Mutex::new(RefCell::new(Queue::new()))\n});\n\nimpl RtcDriver {\n    /// initialize the timer, but don't start it.  Used for chips like stm32wle5\n    /// for low power where the timer config is lost in STOP2.\n    pub(crate) fn init_timer(&'static self, cs: critical_section::CriticalSection) {\n        let r = regs_gp16();\n\n        rcc::enable_and_reset_with_cs::<T>(cs);\n\n        let timer_freq = T::frequency();\n\n        r.cr1().modify(|w| w.set_cen(false));\n        r.cnt().write(|w| w.set_cnt(0));\n\n        let psc = timer_freq.0 / TICK_HZ as u32 - 1;\n        let psc: u16 = match psc.try_into() {\n            Err(_) => panic!(\"psc division overflow: {}\", psc),\n            Ok(n) => n,\n        };\n\n        r.psc().write_value(psc);\n        r.arr().write(|w| w.set_arr(u16::MAX));\n\n        // Set URS, generate update and clear URS\n        r.cr1().modify(|w| w.set_urs(vals::Urs::COUNTER_ONLY));\n        r.egr().write(|w| w.set_ug(true));\n        r.cr1().modify(|w| w.set_urs(vals::Urs::ANY_EVENT));\n\n        // Mid-way point\n        r.ccr(0).write(|w| w.set_ccr(0x8000));\n\n        // Enable overflow and half-overflow interrupts\n        r.dier().write(|w| {\n            w.set_uie(true);\n            w.set_ccie(0, true);\n        });\n\n        <T as GeneralInstance1Channel>::CaptureCompareInterrupt::unpend();\n        <T as CoreInstance>::UpdateInterrupt::unpend();\n        unsafe {\n            <T as GeneralInstance1Channel>::CaptureCompareInterrupt::enable();\n            <T as CoreInstance>::UpdateInterrupt::enable();\n\n            #[cfg(feature = \"low-power\")]\n            crate::rcc::reset_stop_refcount(cs);\n        }\n    }\n\n    fn init(&'static self, cs: CriticalSection) {\n        self.init_timer(cs);\n        regs_gp16().cr1().modify(|w| w.set_cen(true));\n    }\n\n    pub(crate) fn on_interrupt(&self) {\n        let r = regs_gp16();\n\n        critical_section::with(|cs| {\n            let sr = r.sr().read();\n            let dier = r.dier().read();\n\n            // Clear all interrupt flags. Bits in SR are \"write 0 to clear\", so write the bitwise NOT.\n            // Other approaches such as writing all zeros, or RMWing won't work, they can\n            // miss interrupts.\n            r.sr().write_value(regs::SrGp16(!sr.0));\n\n            // Overflow\n            if sr.uif() {\n                self.next_period();\n            }\n\n            // Half overflow\n            if sr.ccif(0) {\n                self.next_period();\n            }\n\n            let n = 0;\n            if sr.ccif(n + 1) && dier.ccie(n + 1) {\n                self.trigger_alarm(cs);\n            }\n        })\n    }\n\n    fn next_period(&self) {\n        let r = regs_gp16();\n\n        // We only modify the period from the timer interrupt, so we know this can't race.\n        let period = self.period.load(Ordering::Relaxed) + 1;\n        self.period.store(period, Ordering::Relaxed);\n        let t = (period as u64) << 15;\n\n        critical_section::with(move |cs| {\n            r.dier().modify(move |w| {\n                let n = 0;\n                let alarm = self.alarm.borrow(cs);\n                let at = alarm.timestamp.get();\n\n                if at < t + 0xc000 {\n                    // just enable it. `set_alarm` has already set the correct CCR val.\n                    w.set_ccie(n + 1, true);\n                }\n            })\n        })\n    }\n\n    fn trigger_alarm(&self, cs: CriticalSection) {\n        let mut next = self.queue.borrow(cs).borrow_mut().next_expiration(self.now());\n        while !self.set_alarm(cs, next) {\n            next = self.queue.borrow(cs).borrow_mut().next_expiration(self.now());\n        }\n    }\n\n    fn set_alarm(&self, cs: CriticalSection, timestamp: u64) -> bool {\n        let r = regs_gp16();\n\n        let n = 0;\n        self.alarm.borrow(cs).timestamp.set(timestamp);\n\n        let t = self.now();\n        if timestamp <= t {\n            // If alarm timestamp has passed the alarm will not fire.\n            // Disarm the alarm and return `false` to indicate that.\n            r.dier().modify(|w| w.set_ccie(n + 1, false));\n\n            self.alarm.borrow(cs).timestamp.set(u64::MAX);\n\n            return false;\n        }\n\n        // Write the CCR value regardless of whether we're going to enable it now or not.\n        // This way, when we enable it later, the right value is already set.\n        r.ccr(n + 1).write(|w| w.set_ccr(timestamp as u16));\n\n        // Enable it if it'll happen soon. Otherwise, `next_period` will enable it.\n        let diff = timestamp - t;\n        r.dier().modify(|w| w.set_ccie(n + 1, diff < 0xc000));\n\n        // Reevaluate if the alarm timestamp is still in the future\n        let t = self.now();\n        if timestamp <= t {\n            // If alarm timestamp has passed since we set it, we have a race condition and\n            // the alarm may or may not have fired.\n            // Disarm the alarm and return `false` to indicate that.\n            // It is the caller's responsibility to handle this ambiguity.\n            r.dier().modify(|w| w.set_ccie(n + 1, false));\n\n            self.alarm.borrow(cs).timestamp.set(u64::MAX);\n\n            return false;\n        }\n\n        // We're confident the alarm will ring in the future.\n        true\n    }\n\n    #[cfg(feature = \"low-power\")]\n    /// Set the current time and recompute any passed alarms\n    fn set_time(&self, instant: u64, cs: CriticalSection) {\n        let (period, counter) = calc_period_counter(core::cmp::max(self.now(), instant));\n\n        self.period.store(period, Ordering::SeqCst);\n        regs_gp16().cnt().write(|w| w.set_cnt(counter));\n\n        // Now, recompute alarm\n        let alarm = self.alarm.borrow(cs);\n\n        if !self.set_alarm(cs, alarm.timestamp.get()) {\n            // If the alarm timestamp has passed, we need to trigger it\n            self.trigger_alarm(cs);\n        }\n    }\n}\n\n#[cfg(feature = \"low-power\")]\nimpl super::LPTimeDriver for RtcDriver {\n    fn time_until_next_alarm(&self, cs: CriticalSection) -> embassy_time::Duration {\n        let now = self.now() + 32;\n\n        embassy_time::Duration::from_ticks(self.alarm.borrow(cs).timestamp.get().saturating_sub(now))\n    }\n\n    fn set_min_stop_pause(&self, cs: CriticalSection, min_stop_pause: embassy_time::Duration) {\n        self.min_stop_pause.borrow(cs).replace(min_stop_pause);\n    }\n\n    fn set_rtc(&self, cs: CriticalSection, mut rtc: Rtc) {\n        rtc.stop_wakeup_alarm();\n\n        assert!(self.rtc.borrow(cs).replace(Some(rtc)).is_none());\n    }\n\n    fn pause_time(&self, cs: CriticalSection) -> Result<(), ()> {\n        assert!(regs_gp16().cr1().read().cen());\n\n        let time_until_next_alarm = self.time_until_next_alarm(cs);\n        if time_until_next_alarm < self.min_stop_pause.borrow(cs).get() {\n            trace!(\n                \"time_until_next_alarm < self.min_stop_pause ({})\",\n                time_until_next_alarm\n            );\n            Err(())\n        } else {\n            self.rtc\n                .borrow(cs)\n                .borrow_mut()\n                .as_mut()\n                .unwrap()\n                .start_wakeup_alarm(time_until_next_alarm);\n\n            regs_gp16().cr1().modify(|w| w.set_cen(false));\n            Ok(())\n        }\n    }\n\n    fn resume_time(&self, cs: CriticalSection) {\n        assert!(!regs_gp16().cr1().read().cen());\n\n        self.set_time(\n            self.rtc\n                .borrow(cs)\n                .borrow_mut()\n                .as_mut()\n                .unwrap()\n                .stop_wakeup_alarm()\n                .as_ticks(),\n            cs,\n        );\n\n        regs_gp16().cr1().modify(|w| w.set_cen(true));\n    }\n\n    fn is_stopped(&self) -> bool {\n        !regs_gp16().cr1().read().cen()\n    }\n}\n\nimpl Driver for RtcDriver {\n    fn now(&self) -> u64 {\n        let r = regs_gp16();\n\n        let period = self.period.load(Ordering::Relaxed);\n        compiler_fence(Ordering::Acquire);\n        let counter = r.cnt().read().cnt();\n        calc_now(period, counter)\n    }\n\n    fn schedule_wake(&self, at: u64, waker: &core::task::Waker) {\n        critical_section::with(|cs| {\n            let mut queue = self.queue.borrow(cs).borrow_mut();\n\n            if queue.schedule_wake(at, waker) {\n                let mut next = queue.next_expiration(self.now());\n                while !self.set_alarm(cs, next) {\n                    next = queue.next_expiration(self.now());\n                }\n            }\n        })\n    }\n}\n\npub(crate) const fn get_driver() -> &'static RtcDriver {\n    &DRIVER\n}\n\npub(crate) fn init(cs: CriticalSection) {\n    DRIVER.init(cs)\n}\n"
  },
  {
    "path": "embassy-stm32/src/time_driver/lptim.rs",
    "content": "#![allow(non_snake_case)]\n\n#[cfg(feature = \"low-power\")]\nuse core::cell::Cell;\nuse core::cell::RefCell;\n#[cfg(feature = \"low-power\")]\nuse core::sync::atomic::AtomicBool;\nuse core::sync::atomic::{AtomicU32, Ordering, compiler_fence};\n\nuse critical_section::CriticalSection;\nuse embassy_sync::blocking_mutex::Mutex;\nuse embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;\nuse embassy_time_driver::{Driver, TICK_HZ};\nuse embassy_time_queue_utils::Queue;\nuse stm32_metapac::lptim::{Lptim, regs};\n\nuse super::AlarmState;\nuse crate::interrupt::typelevel::Interrupt;\nuse crate::lptim::SealedInstance;\nuse crate::pac::lptim::vals;\nuse crate::rcc::SealedRccPeripheral;\nuse crate::{peripherals, rcc};\n\n#[cfg(time_driver_lptim1)]\ntype T = peripherals::LPTIM1;\n#[cfg(time_driver_lptim2)]\ntype T = peripherals::LPTIM2;\n#[cfg(time_driver_lptim3)]\ntype T = peripherals::LPTIM3;\n\nfn regs_lptim() -> Lptim {\n    T::regs()\n}\n\npub(crate) struct RtcDriver {\n    /// Number of 2^15 periods elapsed since boot.\n    period: AtomicU32,\n    alarm: Mutex<CriticalSectionRawMutex, AlarmState>,\n    #[cfg(feature = \"low-power\")]\n    is_stopped: AtomicBool,\n    #[cfg(feature = \"low-power\")]\n    /// The minimum pause time beyond which the executor will enter a low-power state.\n    min_stop_pause: Mutex<CriticalSectionRawMutex, Cell<embassy_time::Duration>>,\n    queue: Mutex<CriticalSectionRawMutex, RefCell<Queue>>,\n}\n\nembassy_time_driver::time_driver_impl!(static DRIVER: RtcDriver = RtcDriver {\n    period: AtomicU32::new(0),\n    alarm: Mutex::const_new(CriticalSectionRawMutex::new(), AlarmState::new()),\n    #[cfg(feature = \"low-power\")]\n    is_stopped: AtomicBool::new(false),\n    #[cfg(feature = \"low-power\")]\n    min_stop_pause: Mutex::const_new(CriticalSectionRawMutex::new(), Cell::new(embassy_time::Duration::from_millis(0))),\n    queue: Mutex::new(RefCell::new(Queue::new())),\n});\n\nimpl RtcDriver {\n    /// initialize the timer, but don't start it.  Used for chips like stm32wle5\n    /// for low power where the timer config is lost in STOP2.\n    pub(crate) fn init_timer(&'static self, _cs: critical_section::CriticalSection) {\n        let r = regs_lptim();\n\n        // we want this to increment the stop mode counter (some lp timer can't do STOP2)\n        rcc::enable_and_reset_without_stop::<T>();\n\n        let timer_freq = T::frequency();\n\n        r.cnt().write(|w| w.set_cnt(0));\n\n        // let psc = timer_freq.0 / TICK_HZ as u32 - 1;\n        let psc = timer_freq.0 / TICK_HZ as u32;\n        let psc = match psc {\n            128 => vals::Presc::DIV128,\n            64 => vals::Presc::DIV64,\n            32 => vals::Presc::DIV32,\n            16 => vals::Presc::DIV16,\n            8 => vals::Presc::DIV8,\n            4 => vals::Presc::DIV4,\n            2 => vals::Presc::DIV2,\n            1 => vals::Presc::DIV1,\n            // TODO: we could compute the valid TICK_HZ for the valid prescalers to include in the panic message\n            _ => panic!(\"Invalid prescaler: {} for timer frequency: {}Hz\", psc, timer_freq.0),\n        };\n\n        trace!(\n            \"init: setting presc: {} timer_freq: {}Hz TICK_HZ: {}\",\n            psc, timer_freq, TICK_HZ\n        );\n        r.cfgr().modify(|w| w.set_presc(psc));\n\n        // RM says timer must be enabled before setting arr or cmp\n        r.cr().modify(|w| w.set_enable(true));\n        trace!(\"init: arr: {:?}\", r.arr().read());\n        // TRM says this is updated immediately if the timer is not started so no need to check for arrok! (stm32wl5 & stm32wle)\n        r.arr().write(|w| w.set_arr(u16::MAX));\n\n        // Enable overflow interrupts\n        T::regs().ier().modify(|w| w.set_ueie(true));\n\n        <T as crate::lptim::SealedBasicInstance>::GlobalInterrupt::unpend();\n        unsafe {\n            <T as crate::lptim::SealedBasicInstance>::GlobalInterrupt::enable();\n        }\n        #[cfg(feature = \"low-power\")]\n        {\n            // TODO: use a crate constant for the core number!!!!!!\n            #[cfg(feature = \"_core-cm4\")]\n            const CPU: usize = 0;\n            #[cfg(feature = \"_core-cm0p\")]\n            const CPU: usize = 1;\n            // TODO: define these elsewhere?\n            #[cfg(all(any(stm32wlex, stm32wl5x), time_driver_lptim1))]\n            const EXTI_WAKEUP_LINE: usize = 29;\n            #[cfg(all(any(stm32wlex, stm32wl5x), time_driver_lptim2))]\n            const EXTI_WAKEUP_LINE: usize = 30;\n            #[cfg(all(any(stm32wlex, stm32wl5x), time_driver_lptim3))]\n            const EXTI_WAKEUP_LINE: usize = 31;\n\n            #[cfg(any(stm32wb, stm32wl5x))]\n            {\n                crate::pac::EXTI\n                    .cpu(CPU)\n                    .imr(0)\n                    .modify(|w| w.set_line(EXTI_WAKEUP_LINE, true));\n                // TODO: from the RM: after line 22 all are reserved - try removing this\n                crate::pac::EXTI\n                    .cpu(CPU)\n                    .emr(0)\n                    .modify(|w| w.set_line(EXTI_WAKEUP_LINE, true));\n            }\n            #[cfg(not(any(stm32wb, stm32wl5x)))]\n            {\n                crate::pac::EXTI.imr(0).modify(|w| w.set_line(EXTI_WAKEUP_LINE, true));\n            }\n        }\n    }\n\n    fn init(&'static self, cs: CriticalSection) {\n        self.init_timer(cs);\n        regs_lptim().cr().modify(|w| w.set_cntstrt(true));\n    }\n\n    pub(crate) fn on_interrupt(&self) {\n        let r = regs_lptim();\n\n        critical_section::with(|cs| {\n            let sr = r.isr().read();\n            let ier = r.ier().read();\n            trace!(\"on_interrupt: sr: {:?}, ier: {:?}\", sr, ier);\n\n            // Clear all interrupt flags. Bits in ICR are \"write 1 to clear\"\n            r.icr().write_value(regs::Icr(ier.0));\n            r.icr().write_value(regs::Icr(sr.0));\n\n            // Overflow\n            if sr.ue() {\n                self.next_period();\n            }\n\n            if sr.ccif(0) && ier.ccie(0) {\n                self.trigger_alarm(cs);\n            }\n        })\n    }\n\n    fn next_period(&self) {\n        let r = regs_lptim();\n\n        // We only modify the period from the timer interrupt, so we know this can't race.\n        let period = self.period.load(Ordering::Relaxed) + 1;\n        self.period.store(period, Ordering::Relaxed);\n        let t = (period as u64) << 16;\n\n        critical_section::with(move |cs| {\n            r.ier().modify(move |w| {\n                let alarm = self.alarm.borrow(cs);\n                let at = alarm.timestamp.get();\n\n                if at < t + 0xc000 {\n                    // just enable it. `set_alarm` has already set the correct CCR val.\n                    w.set_ccie(0, true);\n                }\n            })\n        })\n    }\n\n    fn trigger_alarm(&self, cs: CriticalSection) {\n        trace!(\"trigger_alarm\");\n        let mut next = self.queue.borrow(cs).borrow_mut().next_expiration(self.now());\n        while !self.set_alarm(cs, next) {\n            next = self.queue.borrow(cs).borrow_mut().next_expiration(self.now());\n        }\n    }\n\n    fn set_alarm(&self, cs: CriticalSection, timestamp: u64) -> bool {\n        trace!(\"set_alarm: timestamp: {}\", timestamp);\n        let r = regs_lptim();\n\n        self.alarm.borrow(cs).timestamp.set(timestamp);\n\n        let t = self.now();\n        if timestamp <= t {\n            trace!(\"set_alarm: timestamp <= t\");\n            // If alarm timestamp has passed the alarm will not fire.\n            // Disarm the alarm and return `false` to indicate that.\n            r.ier().modify(|w| w.set_ccie(0, false));\n\n            self.alarm.borrow(cs).timestamp.set(u64::MAX);\n\n            return false;\n        }\n\n        // Write the CCR value regardless of whether we're going to enable it now or not.\n        // This way, when we enable it later, the right value is already set.\n        r.cmp().write(|w| w.set_cmp(timestamp as u16));\n        while !r.isr().read().cmpok(0) {}\n        // r.icr().write(|w| w.set_cmpokcf(0, true));\n\n        // Enable it if it'll happen soon. Otherwise, `next_period` will enable it.\n        let diff = timestamp - t;\n        r.ier().modify(|w| w.set_ccie(0, diff < 0xc000));\n\n        // Reevaluate if the alarm timestamp is still in the future\n        let t = self.now();\n        if timestamp <= t {\n            trace!(\"set_alarm: timestamp <= t (after set)\");\n            // If alarm timestamp has passed since we set it, we have a race condition and\n            // the alarm may or may not have fired.\n            // Disarm the alarm and return `false` to indicate that.\n            // It is the caller's responsibility to handle this ambiguity.\n            r.ier().modify(|w| w.set_ccie(0, false));\n\n            self.alarm.borrow(cs).timestamp.set(u64::MAX);\n\n            return false;\n        }\n\n        trace!(\"set_alarm: true\");\n        // We're confident the alarm will ring in the future.\n        true\n    }\n}\n\n#[cfg(feature = \"low-power\")]\nimpl super::LPTimeDriver for RtcDriver {\n    /// Compute the approximate amount of time until the next alarm\n    fn time_until_next_alarm(&self, cs: CriticalSection) -> embassy_time::Duration {\n        let now = self.now() + 32;\n\n        embassy_time::Duration::from_ticks(self.alarm.borrow(cs).timestamp.get().saturating_sub(now))\n    }\n\n    /// Set the stopped flag or return an error if the time until the next alarm is less than the minimum stop pause\n    fn pause_time(&self, cs: CriticalSection) -> Result<(), ()> {\n        trace!(\"pause_time\");\n        let time_until_next_alarm = self.time_until_next_alarm(cs);\n        if time_until_next_alarm < self.min_stop_pause.borrow(cs).get() {\n            trace!(\n                \"time_until_next_alarm < self.min_stop_pause ({})\",\n                time_until_next_alarm\n            );\n            Err(())\n        } else {\n            self.is_stopped.store(true, Ordering::Relaxed);\n            Ok(())\n        }\n    }\n\n    /// Reset the stopped flag\n    fn resume_time(&self, _cs: CriticalSection) {\n        trace!(\"resume_time\");\n        self.is_stopped.store(false, Ordering::Relaxed);\n    }\n\n    /// Returns whether time is currently \"stopped\"\n    fn is_stopped(&self) -> bool {\n        self.is_stopped.load(Ordering::Relaxed)\n    }\n}\n\nimpl Driver for RtcDriver {\n    fn now(&self) -> u64 {\n        let r = regs_lptim();\n        loop {\n            let period = self.period.load(Ordering::Relaxed);\n            compiler_fence(Ordering::Acquire);\n            let counter = r.cnt().read().cnt();\n            let now = ((period as u64) << 16) + (counter as u64);\n\n            if self.period.load(Ordering::Relaxed) == period {\n                break now;\n            }\n        }\n    }\n\n    fn schedule_wake(&self, at: u64, waker: &core::task::Waker) {\n        critical_section::with(|cs| {\n            let mut queue = self.queue.borrow(cs).borrow_mut();\n\n            if queue.schedule_wake(at, waker) {\n                let mut next = queue.next_expiration(self.now());\n                while !self.set_alarm(cs, next) {\n                    next = queue.next_expiration(self.now());\n                }\n            }\n        })\n    }\n}\n\npub(crate) const fn get_driver() -> &'static RtcDriver {\n    &DRIVER\n}\n\npub(crate) fn init(cs: CriticalSection) {\n    DRIVER.init(cs)\n}\n"
  },
  {
    "path": "embassy-stm32/src/time_driver/mod.rs",
    "content": "use core::cell::Cell;\n\n#[cfg(feature = \"low-power\")]\nuse critical_section::CriticalSection;\n\n// Common AlarmState struct used by both implementations\npub(crate) struct AlarmState {\n    pub(crate) timestamp: Cell<u64>,\n}\n\nunsafe impl Send for AlarmState {}\n\nimpl AlarmState {\n    pub(crate) const fn new() -> Self {\n        Self {\n            timestamp: Cell::new(u64::MAX),\n        }\n    }\n}\n\n#[cfg(feature = \"low-power\")]\npub(crate) trait LPTimeDriver {\n    /// Compute the approximate amount of time until the next alarm\n    fn time_until_next_alarm(&self, cs: CriticalSection) -> embassy_time::Duration;\n\n    /// Set the minimum pause time beyond which the executor will enter a low-power state.\n    #[cfg(not(feature = \"_lp-time-driver\"))]\n    fn set_min_stop_pause(&self, cs: CriticalSection, min_stop_pause: embassy_time::Duration);\n\n    /// Set the rtc but panic if it's already been set\n    #[cfg(not(feature = \"_lp-time-driver\"))]\n    fn set_rtc(&self, cs: CriticalSection, rtc: crate::rtc::Rtc);\n\n    /// Pause the timer if ready; return err if not\n    fn pause_time(&self, cs: CriticalSection) -> Result<(), ()>;\n\n    /// Resume the timer with the given offset\n    fn resume_time(&self, cs: CriticalSection);\n\n    /// Returns whether time is currently stopped\n    fn is_stopped(&self) -> bool;\n}\n\n#[cfg_attr(feature = \"_lp-time-driver\", path = \"lptim.rs\")]\n#[cfg_attr(not(feature = \"_lp-time-driver\"), path = \"gp16.rs\")]\nmod driver;\npub(crate) use driver::*;\n"
  },
  {
    "path": "embassy-stm32/src/timer/complementary_pwm.rs",
    "content": "//! PWM driver with complementary output support.\n\nuse core::marker::PhantomData;\n\npub use super::low_level::FilterValue;\nuse super::low_level::{CountingMode, OutputPolarity, RoundTo, Timer};\nuse super::simple_pwm::PwmPin;\nuse super::{AdvancedInstance4Channel, Ch1, Ch2, Ch3, Ch4, Channel, TimerComplementaryPin};\nuse crate::Peri;\nuse crate::dma::word::Word;\nuse crate::gpio::{AfType, Flex, OutputType};\npub use crate::pac::timer::vals::{\n    Bkinp as BreakComparatorPolarity, Bkp as BreakInputPolarity, Ccds, Ckd, Mms2, Ossi, Ossr,\n};\nuse crate::time::Hertz;\nuse crate::timer::TimerChannel;\nuse crate::timer::low_level::OutputCompareMode;\nuse crate::timer::simple_pwm::PwmPinConfig;\n\n/// Complementary PWM pin wrapper.\n///\n/// This wraps a pin to make it usable with PWM.\npub struct ComplementaryPwmPin<'d, T, C, #[cfg(afio)] A> {\n    #[allow(unused)]\n    pin: Flex<'d>,\n    phantom: PhantomData<if_afio!((T, C, A))>,\n}\n\nimpl<'d, T: AdvancedInstance4Channel, C: TimerChannel, #[cfg(afio)] A> if_afio!(ComplementaryPwmPin<'d, T, C, A>) {\n    /// Create a new  complementary PWM pin instance.\n    pub fn new(pin: Peri<'d, if_afio!(impl TimerComplementaryPin<T, C, A>)>, output_type: OutputType) -> Self {\n        critical_section::with(|_| {\n            pin.set_low();\n            set_as_af!(pin, AfType::output(output_type, crate::gpio::Speed::VeryHigh));\n        });\n        ComplementaryPwmPin {\n            pin: Flex::new(pin),\n            phantom: PhantomData,\n        }\n    }\n\n    /// Create a new PWM pin instance with config.\n    pub fn new_with_config(\n        pin: Peri<'d, if_afio!(impl TimerComplementaryPin<T, C, A>)>,\n        pin_config: PwmPinConfig,\n    ) -> Self {\n        critical_section::with(|_| {\n            pin.set_low();\n            #[cfg(gpio_v1)]\n            set_as_af!(pin, AfType::output(pin_config.output_type, pin_config.speed));\n            #[cfg(gpio_v2)]\n            pin.set_as_af(\n                pin.af_num(),\n                AfType::output_pull(pin_config.output_type, pin_config.speed, pin_config.pull),\n            );\n        });\n        ComplementaryPwmPin {\n            pin: Flex::new(pin),\n            phantom: PhantomData,\n        }\n    }\n}\n\n/// PWM driver with support for standard and complementary outputs.\npub struct ComplementaryPwm<'d, T: AdvancedInstance4Channel> {\n    inner: Timer<'d, T>,\n    _ch1: Option<Flex<'d>>,\n    _ch1n: Option<Flex<'d>>,\n    _ch2: Option<Flex<'d>>,\n    _ch2n: Option<Flex<'d>>,\n    _ch3: Option<Flex<'d>>,\n    _ch3n: Option<Flex<'d>>,\n    _ch4: Option<Flex<'d>>,\n    _ch4n: Option<Flex<'d>>,\n}\n\n#[derive(Copy, Clone, Debug, PartialEq, Eq)]\n/// Determines which outputs are active when PWM is in idle mode\npub enum IdlePolarity {\n    /// Normal channels are forced active and complementary channels are forced inactive\n    OisActive,\n    /// Normal channels are forced inactive and complementary channels are forced active\n    OisnActive,\n}\n\nimpl<'d, T: AdvancedInstance4Channel> ComplementaryPwm<'d, T> {\n    /// Create a new complementary PWM driver.\n    #[allow(clippy::too_many_arguments, unused)]\n    pub fn new<#[cfg(afio)] A>(\n        tim: Peri<'d, T>,\n        ch1: Option<if_afio!(PwmPin<'d, T, Ch1, A>)>,\n        ch1n: Option<if_afio!(ComplementaryPwmPin<'d, T, Ch1, A>)>,\n        ch2: Option<if_afio!(PwmPin<'d, T, Ch2, A>)>,\n        ch2n: Option<if_afio!(ComplementaryPwmPin<'d, T, Ch2, A>)>,\n        ch3: Option<if_afio!(PwmPin<'d, T, Ch3, A>)>,\n        ch3n: Option<if_afio!(ComplementaryPwmPin<'d, T, Ch3, A>)>,\n        ch4: Option<if_afio!(PwmPin<'d, T, Ch4, A>)>,\n        ch4n: Option<if_afio!(ComplementaryPwmPin<'d, T, Ch4, A>)>,\n        freq: Hertz,\n        counting_mode: CountingMode,\n    ) -> Self {\n        Self::new_inner(\n            tim,\n            ch1.map(|pin| pin.pin),\n            ch1n.map(|pin| pin.pin),\n            ch2.map(|pin| pin.pin),\n            ch2n.map(|pin| pin.pin),\n            ch3.map(|pin| pin.pin),\n            ch3n.map(|pin| pin.pin),\n            ch4.map(|pin| pin.pin),\n            ch4n.map(|pin| pin.pin),\n            freq,\n            counting_mode,\n        )\n    }\n\n    fn new_inner(\n        tim: Peri<'d, T>,\n        _ch1: Option<Flex<'d>>,\n        _ch1n: Option<Flex<'d>>,\n        _ch2: Option<Flex<'d>>,\n        _ch2n: Option<Flex<'d>>,\n        _ch3: Option<Flex<'d>>,\n        _ch3n: Option<Flex<'d>>,\n        _ch4: Option<Flex<'d>>,\n        _ch4n: Option<Flex<'d>>,\n        freq: Hertz,\n        counting_mode: CountingMode,\n    ) -> Self {\n        let mut this = Self {\n            inner: Timer::new(tim),\n            _ch1,\n            _ch1n,\n            _ch2,\n            _ch2n,\n            _ch3,\n            _ch3n,\n            _ch4,\n            _ch4n,\n        };\n\n        this.inner.set_counting_mode(counting_mode);\n        this.set_frequency(freq);\n        this.inner.enable_outputs();\n\n        [Channel::Ch1, Channel::Ch2, Channel::Ch3, Channel::Ch4]\n            .iter()\n            .for_each(|&channel| {\n                this.inner.set_output_compare_mode(channel, OutputCompareMode::PwmMode1);\n                this.inner.set_output_compare_preload(channel, true);\n            });\n        this.inner.set_autoreload_preload(true);\n\n        // Generate update event so pre-load registers are written to the shadow registers\n        this.inner.generate_update_event();\n        this.inner.start();\n\n        this\n    }\n\n    /// Sets the idle output state for the given channels.\n    pub fn set_output_idle_state(&mut self, channels: &[Channel], polarity: IdlePolarity) {\n        let ois_active = matches!(polarity, IdlePolarity::OisActive);\n        for &channel in channels {\n            self.inner.set_ois(channel, ois_active);\n            self.inner.set_oisn(channel, !ois_active);\n        }\n    }\n\n    /// Set state of OSSI-bit in BDTR register\n    pub fn set_off_state_selection_idle(&mut self, val: Ossi) {\n        self.inner.set_ossi(val);\n    }\n\n    /// Get state of OSSI-bit in BDTR register\n    pub fn get_off_state_selection_idle(&self) -> Ossi {\n        self.inner.get_ossi()\n    }\n\n    /// Set state of OSSR-bit in BDTR register\n    pub fn set_off_state_selection_run(&mut self, val: Ossr) {\n        self.inner.set_ossr(val);\n    }\n\n    /// Get state of OSSR-bit in BDTR register\n    pub fn get_off_state_selection_run(&self) -> Ossr {\n        self.inner.get_ossr()\n    }\n\n    /// Trigger break input from software\n    pub fn trigger_software_break(&mut self, n: usize) {\n        self.inner.trigger_software_break(n);\n    }\n\n    /// Set Master Output Enable\n    pub fn set_master_output_enable(&mut self, enable: bool) {\n        self.inner.set_moe(enable);\n    }\n\n    /// Get Master Output Enable\n    pub fn get_master_output_enable(&self) -> bool {\n        self.inner.get_moe()\n    }\n\n    /// Enable/disable break input 1.\n    ///\n    /// When enabled, an active level on the break input forces all timer\n    /// outputs to their safe state (configured by OSSI/OSSR and OIS/OISN).\n    /// This provides hardware-level overcurrent protection for motor drives.\n    pub fn set_break_enable(&mut self, enable: bool) {\n        self.inner.set_break_enable(enable);\n    }\n\n    /// Get break input 1 enable state.\n    pub fn get_break_enable(&self) -> bool {\n        self.inner.get_break_enable()\n    }\n\n    /// Set break input 1 polarity.\n    pub fn set_break_polarity(&mut self, polarity: BreakInputPolarity) {\n        self.inner.set_break_polarity(polarity);\n    }\n\n    /// Get break input 1 polarity.\n    pub fn get_break_polarity(&self) -> BreakInputPolarity {\n        self.inner.get_break_polarity()\n    }\n\n    /// Set break input 1 digital filter.\n    ///\n    /// The filter rejects glitches shorter than the configured number of\n    /// clock cycles, preventing false break events from noise on the pin.\n    pub fn set_break_filter(&mut self, filter: FilterValue) {\n        self.inner.set_break_filter(filter);\n    }\n\n    /// Get break input 1 digital filter.\n    pub fn get_break_filter(&self) -> FilterValue {\n        self.inner.get_break_filter()\n    }\n\n    /// Enable/disable break input 2.\n    pub fn set_break2_enable(&mut self, enable: bool) {\n        self.inner.set_break2_enable(enable);\n    }\n\n    /// Get break input 2 enable state.\n    pub fn get_break2_enable(&self) -> bool {\n        self.inner.get_break2_enable()\n    }\n\n    /// Set break input 2 polarity.\n    pub fn set_break2_polarity(&mut self, polarity: BreakInputPolarity) {\n        self.inner.set_break2_polarity(polarity);\n    }\n\n    /// Get break input 2 polarity.\n    pub fn get_break2_polarity(&self) -> BreakInputPolarity {\n        self.inner.get_break2_polarity()\n    }\n\n    /// Set break input 2 digital filter.\n    pub fn set_break2_filter(&mut self, filter: FilterValue) {\n        self.inner.set_break2_filter(filter);\n    }\n\n    /// Get break input 2 digital filter.\n    pub fn get_break2_filter(&self) -> FilterValue {\n        self.inner.get_break2_filter()\n    }\n\n    /// Enable/disable automatic output enable (AOE).\n    ///\n    /// When enabled, the MOE bit is automatically set at the next update\n    /// event after a break event, allowing the outputs to resume. When\n    /// disabled, MOE can only be re-enabled by software after a break.\n    pub fn set_automatic_output_enable(&mut self, enable: bool) {\n        self.inner.set_automatic_output_enable(enable);\n    }\n\n    /// Get automatic output enable (AOE) state.\n    pub fn get_automatic_output_enable(&self) -> bool {\n        self.inner.get_automatic_output_enable()\n    }\n\n    /// Enable/disable comparator output as break input 1 source.\n    ///\n    /// Routes the internal comparator output directly to the break input,\n    /// no GPIO pin needed. `comp_index` is 0-based (0=COMP1, 1=COMP2, etc.).\n    /// Multiple comparators can be enabled simultaneously (OR'd together).\n    pub fn set_break_comparator_enable(&mut self, comp_index: usize, enable: bool) {\n        self.inner.set_break_comparator_enable(comp_index, enable);\n    }\n\n    /// Get comparator break input 1 enable state.\n    pub fn get_break_comparator_enable(&self, comp_index: usize) -> bool {\n        self.inner.get_break_comparator_enable(comp_index)\n    }\n\n    /// Set comparator break input 1 polarity.\n    pub fn set_break_comparator_polarity(&mut self, comp_index: usize, polarity: BreakComparatorPolarity) {\n        self.inner.set_break_comparator_polarity(comp_index, polarity);\n    }\n\n    /// Get comparator break input 1 polarity.\n    pub fn get_break_comparator_polarity(&self, comp_index: usize) -> BreakComparatorPolarity {\n        self.inner.get_break_comparator_polarity(comp_index)\n    }\n\n    /// Enable/disable the external BKIN pin as break input 1 source.\n    pub fn set_break_input_pin_enable(&mut self, enable: bool) {\n        self.inner.set_break_input_pin_enable(enable);\n    }\n\n    /// Get external BKIN pin enable state.\n    pub fn get_break_input_pin_enable(&self) -> bool {\n        self.inner.get_break_input_pin_enable()\n    }\n\n    /// Enable/disable comparator output as break input 2 source.\n    pub fn set_break2_comparator_enable(&mut self, comp_index: usize, enable: bool) {\n        self.inner.set_break2_comparator_enable(comp_index, enable);\n    }\n\n    /// Get comparator break input 2 enable state.\n    pub fn get_break2_comparator_enable(&self, comp_index: usize) -> bool {\n        self.inner.get_break2_comparator_enable(comp_index)\n    }\n\n    /// Set comparator break input 2 polarity.\n    pub fn set_break2_comparator_polarity(&mut self, comp_index: usize, polarity: BreakComparatorPolarity) {\n        self.inner.set_break2_comparator_polarity(comp_index, polarity);\n    }\n\n    /// Get comparator break input 2 polarity.\n    pub fn get_break2_comparator_polarity(&self, comp_index: usize) -> BreakComparatorPolarity {\n        self.inner.get_break2_comparator_polarity(comp_index)\n    }\n\n    /// Enable/disable the external BK2IN pin as break input 2 source.\n    pub fn set_break2_input_pin_enable(&mut self, enable: bool) {\n        self.inner.set_break2_input_pin_enable(enable);\n    }\n\n    /// Get external BK2IN pin enable state.\n    pub fn get_break2_input_pin_enable(&self) -> bool {\n        self.inner.get_break2_input_pin_enable()\n    }\n\n    /// Set Master Slave Mode 2\n    pub fn set_mms2(&mut self, mms2: Mms2) {\n        self.inner.set_mms2_selection(mms2);\n    }\n\n    /// Set Repetition Counter\n    pub fn set_repetition_counter(&mut self, val: u16) {\n        self.inner.set_repetition_counter(val);\n    }\n\n    /// Enable the given channel.\n    pub fn enable(&mut self, channel: Channel) {\n        self.inner.enable_channel(channel, true);\n        self.inner.enable_complementary_channel(channel, true);\n    }\n\n    /// Disable the given channel.\n    pub fn disable(&mut self, channel: Channel) {\n        self.inner.enable_complementary_channel(channel, false);\n        self.inner.enable_channel(channel, false);\n    }\n\n    /// Set PWM frequency.\n    ///\n    /// The actual frequency may differ from the requested value due to hardware\n    /// limitations. The timer will round towards a slower (longer) period.\n    ///\n    /// Note: that the frequency will not be applied in the timer until an update event\n    /// occurs.\n    pub fn set_frequency(&mut self, freq: Hertz) {\n        let multiplier = if self.inner.get_counting_mode().is_center_aligned() {\n            2u64\n        } else {\n            1u64\n        };\n        let timer_f = T::frequency().0 as u64;\n        let clocks = timer_f / (freq.0 as u64 * multiplier);\n        self.inner.set_period_clocks_internal(clocks, RoundTo::Slower, 16);\n    }\n\n    /// Set the PWM period in milliseconds.\n    ///\n    /// The actual period may differ from the requested value due to hardware\n    /// limitations. The timer will round towards a slower (longer) period.\n    ///\n    /// Note: that the period will not be applied in the timer until an update event\n    /// occurs.\n    pub fn set_period_ms(&mut self, ms: u32) {\n        let timer_f = T::frequency().0 as u64;\n        let mut clocks = timer_f * ms as u64 / 1_000;\n        if self.inner.get_counting_mode().is_center_aligned() {\n            clocks = clocks / 2;\n        }\n        self.inner.set_period_clocks(clocks, RoundTo::Slower);\n    }\n\n    /// Set the PWM period in microseconds.\n    ///\n    /// The actual period may differ from the requested value due to hardware\n    /// limitations. The timer will round towards a slower (longer) period.\n    ///\n    /// Note: that the period will not be applied in the timer until an update event\n    /// occurs.\n    pub fn set_period_us(&mut self, us: u32) {\n        let timer_f = T::frequency().0 as u64;\n        let mut clocks = timer_f * us as u64 / 1_000_000;\n        if self.inner.get_counting_mode().is_center_aligned() {\n            clocks = clocks / 2;\n        }\n        self.inner.set_period_clocks(clocks, RoundTo::Slower);\n    }\n\n    /// Set the PWM period in seconds.\n    ///\n    /// The actual period may differ from the requested value due to hardware\n    /// limitations. The timer will round towards a slower (longer) period.\n    ///\n    /// Note: that the period will not be applied in the timer until an update event\n    /// occurs.\n    pub fn set_period_secs(&mut self, secs: u32) {\n        let timer_f = T::frequency().0 as u64;\n        let mut clocks = timer_f * secs as u64;\n        if self.inner.get_counting_mode().is_center_aligned() {\n            clocks = clocks / 2;\n        }\n        self.inner.set_period_clocks(clocks, RoundTo::Slower);\n    }\n\n    /// Set the PWM period using an `embassy_time::Duration`.\n    ///\n    /// The actual period may differ from the requested value due to hardware\n    /// limitations. The timer will round towards a slower (longer) period.\n    ///\n    /// Note: that the period will not be applied in the timer until an update event\n    /// occurs.\n    #[cfg(feature = \"time\")]\n    pub fn set_period(&mut self, period: embassy_time::Duration) {\n        let timer_f = T::frequency().0 as u64;\n        let mut clocks = timer_f * period.as_ticks() / embassy_time::TICK_HZ;\n        if self.inner.get_counting_mode().is_center_aligned() {\n            clocks = clocks / 2;\n        }\n        self.inner.set_period_clocks(clocks, RoundTo::Slower);\n    }\n\n    /// Get max duty value.\n    ///\n    /// This value depends on the configured frequency and the timer's clock rate from RCC.\n    pub fn get_max_duty(&self) -> u32 {\n        if self.inner.get_counting_mode().is_center_aligned() {\n            self.inner.get_max_compare_value().into()\n        } else {\n            self.inner.get_max_compare_value().into() + 1\n        }\n    }\n\n    /// Set the duty for a given channel.\n    ///\n    /// The value ranges from 0 for 0% duty, to [`get_max_duty`](Self::get_max_duty) for 100% duty, both included.\n    pub fn set_duty(&mut self, channel: Channel, duty: u32) {\n        assert!(duty <= self.get_max_duty());\n        self.inner.set_compare_value(channel, unwrap!(duty.try_into()))\n    }\n\n    /// Set the output polarity for a given channel.\n    pub fn set_polarity(&mut self, channel: Channel, polarity: OutputPolarity) {\n        self.inner.set_output_polarity(channel, polarity);\n        self.inner.set_complementary_output_polarity(channel, polarity);\n    }\n\n    /// Set the main output polarity for a given channel.\n    pub fn set_main_polarity(&mut self, channel: Channel, polarity: OutputPolarity) {\n        self.inner.set_output_polarity(channel, polarity);\n    }\n\n    /// Set the complementary output polarity for a given channel.\n    pub fn set_complementary_polarity(&mut self, channel: Channel, polarity: OutputPolarity) {\n        self.inner.set_complementary_output_polarity(channel, polarity);\n    }\n\n    /// Set the dead time as a proportion of max_duty\n    pub fn set_dead_time(&mut self, value: u16) {\n        let (ckd, value) = compute_dead_time_value(value);\n\n        self.inner.set_dead_time_clock_division(ckd);\n        self.inner.set_dead_time_value(value);\n    }\n\n    /// Generate a sequence of PWM waveform\n    ///\n    /// Note:\n    /// The DMA channel provided does not need to correspond to the requested channel.\n    pub async fn waveform<C: TimerChannel, W: Word + Into<T::Word>, D: super::Dma<T, C>>(\n        &mut self,\n        dma: Peri<'_, D>,\n        irq: impl crate::interrupt::typelevel::Binding<D::Interrupt, crate::dma::InterruptHandler<D>> + '_,\n        channel: Channel,\n        duty: &[W],\n    ) {\n        self.inner.enable_channel(channel, true);\n        self.inner.enable_channel(C::CHANNEL, true);\n        self.inner.clamp_compare_value::<W>(channel);\n        self.inner.set_cc_dma_selection(Ccds::ON_UPDATE);\n        self.inner.set_cc_dma_enable_state(C::CHANNEL, true);\n        self.inner.setup_channel_update_dma(dma, irq, channel, duty).await;\n        self.inner.set_cc_dma_enable_state(C::CHANNEL, false);\n    }\n\n    /// Generate a sequence of PWM waveform\n    ///\n    /// Note:\n    /// you will need to provide corresponding TIMx_UP DMA channel to use this method.\n    pub async fn waveform_up<W: Word + Into<T::Word>, D: super::UpDma<T>>(\n        &mut self,\n        dma: Peri<'_, D>,\n        irq: impl crate::interrupt::typelevel::Binding<D::Interrupt, crate::dma::InterruptHandler<D>> + '_,\n        channel: Channel,\n        duty: &[W],\n    ) {\n        self.inner.enable_channel(channel, true);\n        self.inner.clamp_compare_value::<W>(channel);\n        self.inner.enable_update_dma(true);\n        self.inner.setup_update_dma(dma, irq, channel, duty).await;\n        self.inner.enable_update_dma(false);\n    }\n\n    /// Generate a multichannel sequence of PWM waveforms using DMA triggered by timer update events.\n    ///\n    /// This method utilizes the timer's DMA burst transfer capability to update multiple CCRx registers\n    /// in sequence on each update event (UEV). The data is written via the DMAR register using the\n    /// DMA base address (DBA) and burst length (DBL) configured in the DCR register.\n    ///\n    /// The `duty` buffer must be structured as a flattened 2D array in row-major order, where each row\n    /// represents a single update event and each column corresponds to a specific timer channel (starting\n    /// from `starting_channel` up to and including `ending_channel`).\n    ///\n    /// For example, if using channels 1 through 4, a buffer of 4 update steps might look like:\n    ///\n    /// ```rust,ignore\n    /// let dma_buf: [u16; 16] = [\n    ///     ch1_duty_1, ch2_duty_1, ch3_duty_1, ch4_duty_1, // update 1\n    ///     ch1_duty_2, ch2_duty_2, ch3_duty_2, ch4_duty_2, // update 2\n    ///     ch1_duty_3, ch2_duty_3, ch3_duty_3, ch4_duty_3, // update 3\n    ///     ch1_duty_4, ch2_duty_4, ch3_duty_4, ch4_duty_4, // update 4\n    /// ];\n    /// ```\n    ///\n    /// Each group of `N` values (where `N` is number of channels) is transferred on one update event,\n    /// updating the duty cycles of all selected channels simultaneously.\n    ///\n    /// Note:\n    /// You will need to provide corresponding `TIMx_UP` DMA channel to use this method.\n    /// Also be aware that embassy timers use one of timers internally. It is possible to\n    /// switch this timer by using `time-driver-timX` feature.\n    ///\n    pub async fn waveform_up_multi_channel<W: Word + Into<T::Word>, D: super::UpDma<T>>(\n        &mut self,\n        dma: Peri<'_, D>,\n        irq: impl crate::interrupt::typelevel::Binding<D::Interrupt, crate::dma::InterruptHandler<D>> + '_,\n        starting_channel: Channel,\n        ending_channel: Channel,\n        duty: &[W],\n    ) {\n        [Channel::Ch1, Channel::Ch2, Channel::Ch3, Channel::Ch4]\n            .iter()\n            .filter(|ch| ch.index() >= starting_channel.index())\n            .filter(|ch| ch.index() <= ending_channel.index())\n            .for_each(|ch| {\n                self.inner.enable_channel(*ch, true);\n                self.inner.clamp_compare_value::<W>(*ch);\n            });\n        self.inner.enable_update_dma(true);\n        self.inner\n            .setup_update_dma_burst(dma, irq, starting_channel, ending_channel, duty)\n            .await;\n        self.inner.enable_update_dma(false);\n    }\n}\n\nimpl<'d, T: AdvancedInstance4Channel> embedded_hal_02::Pwm for ComplementaryPwm<'d, T> {\n    type Channel = Channel;\n    type Time = Hertz;\n    type Duty = u16;\n\n    fn disable(&mut self, channel: Self::Channel) {\n        self.inner.enable_complementary_channel(channel, false);\n        self.inner.enable_channel(channel, false);\n    }\n\n    fn enable(&mut self, channel: Self::Channel) {\n        self.inner.enable_channel(channel, true);\n        self.inner.enable_complementary_channel(channel, true);\n    }\n\n    fn get_period(&self) -> Self::Time {\n        self.inner.get_frequency()\n    }\n\n    fn get_duty(&self, channel: Self::Channel) -> Self::Duty {\n        unwrap!(self.inner.get_compare_value(channel).try_into())\n    }\n\n    fn get_max_duty(&self) -> Self::Duty {\n        if self.inner.get_counting_mode().is_center_aligned() {\n            unwrap!(self.inner.get_max_compare_value().try_into())\n        } else {\n            unwrap!(self.inner.get_max_compare_value().try_into()) + 1\n        }\n    }\n\n    fn set_duty(&mut self, channel: Self::Channel, duty: Self::Duty) {\n        assert!(duty <= unwrap!(self.get_max_duty().try_into()));\n        self.inner.set_compare_value(channel, unwrap!(duty.try_into()))\n    }\n\n    fn set_period<P>(&mut self, period: P)\n    where\n        P: Into<Self::Time>,\n    {\n        self.inner.set_frequency(period.into(), RoundTo::Slower);\n    }\n}\n\nfn compute_dead_time_value(value: u16) -> (Ckd, u8) {\n    /*\n        Dead-time = T_clk * T_dts * T_dtg\n\n        T_dts:\n        This bit-field indicates the division ratio between the timer clock (CK_INT) frequency and the\n        dead-time and sampling clock (tDTS)used by the dead-time generators and the digital filters\n        (ETR, TIx),\n        00: tDTS=tCK_INT\n        01: tDTS=2*tCK_INT\n        10: tDTS=4*tCK_INT\n\n        T_dtg:\n        This bit-field defines the duration of the dead-time inserted between the complementary\n        outputs. DT correspond to this duration.\n        DTG[7:5]=0xx => DT=DTG[7:0]x tdtg with tdtg=tDTS.\n        DTG[7:5]=10x => DT=(64+DTG[5:0])xtdtg with Tdtg=2xtDTS.\n        DTG[7:5]=110 => DT=(32+DTG[4:0])xtdtg with Tdtg=8xtDTS.\n        DTG[7:5]=111 => DT=(32+DTG[4:0])xtdtg with Tdtg=16xtDTS.\n        Example if TDTS=125ns (8MHz), dead-time possible values are:\n        0 to 15875 ns by 125 ns steps,\n        16 us to 31750 ns by 250 ns steps,\n        32 us to 63us by 1 us steps,\n        64 us to 126 us by 2 us steps\n    */\n\n    let mut error = u16::MAX;\n    let mut ckd = Ckd::DIV1;\n    let mut bits = 0u8;\n\n    for this_ckd in [Ckd::DIV1, Ckd::DIV2, Ckd::DIV4] {\n        let outdiv = match this_ckd {\n            Ckd::DIV1 => 1,\n            Ckd::DIV2 => 2,\n            Ckd::DIV4 => 4,\n            _ => unreachable!(),\n        };\n\n        // 127\n        // 128\n        // ..\n        // 254\n        // 256\n        // ..\n        // 504\n        // 512\n        // ..\n        // 1008\n\n        let target = value / outdiv;\n        let (these_bits, result) = if target < 128 {\n            (target as u8, target)\n        } else if target < 255 {\n            ((64 + (target / 2) as u8) | 128, (target - target % 2))\n        } else if target < 508 {\n            ((32 + (target / 8) as u8) | 192, (target - target % 8))\n        } else if target < 1008 {\n            ((32 + (target / 16) as u8) | 224, (target - target % 16))\n        } else {\n            (u8::MAX, 1008)\n        };\n\n        let this_error = value.abs_diff(result * outdiv);\n        if error > this_error {\n            ckd = this_ckd;\n            bits = these_bits;\n            error = this_error;\n        }\n\n        if error == 0 {\n            break;\n        }\n    }\n\n    (ckd, bits)\n}\n\n#[cfg(test)]\nmod tests {\n    use super::{Ckd, compute_dead_time_value};\n\n    #[test]\n    fn test_compute_dead_time_value() {\n        struct TestRun {\n            value: u16,\n            ckd: Ckd,\n            bits: u8,\n        }\n\n        let fn_results = [\n            TestRun {\n                value: 1,\n                ckd: Ckd::DIV1,\n                bits: 1,\n            },\n            TestRun {\n                value: 125,\n                ckd: Ckd::DIV1,\n                bits: 125,\n            },\n            TestRun {\n                value: 245,\n                ckd: Ckd::DIV1,\n                bits: 64 + 245 / 2,\n            },\n            TestRun {\n                value: 255,\n                ckd: Ckd::DIV2,\n                bits: 127,\n            },\n            TestRun {\n                value: 400,\n                ckd: Ckd::DIV1,\n                bits: 210,\n            },\n            TestRun {\n                value: 600,\n                ckd: Ckd::DIV4,\n                bits: 64 + (600u16 / 8) as u8,\n            },\n        ];\n\n        for test_run in fn_results {\n            let (ckd, bits) = compute_dead_time_value(test_run.value);\n\n            assert_eq!(ckd.to_bits(), test_run.ckd.to_bits());\n            assert_eq!(bits, test_run.bits);\n        }\n    }\n}\n"
  },
  {
    "path": "embassy-stm32/src/timer/input_capture.rs",
    "content": "//! Input capture driver.\n\nuse core::future::Future;\nuse core::marker::PhantomData;\nuse core::pin::Pin;\nuse core::task::{Context, Poll};\n\nuse super::low_level::{CountingMode, FilterValue, InputCaptureMode, InputTISelection, Timer};\nuse super::{CaptureCompareInterruptHandler, Channel, GeneralInstance4Channel, TimerPin};\npub use super::{Ch1, Ch2, Ch3, Ch4};\nuse crate::gpio::{AfType, Flex, Pull};\nuse crate::interrupt::typelevel::{Binding, Interrupt};\nuse crate::time::Hertz;\nuse crate::timer::TimerChannel;\nuse crate::{Peri, dma};\n\n/// Capture pin wrapper.\n///\n/// This wraps a pin to make it usable with capture.\npub struct CapturePin<'d, T, C, #[cfg(afio)] A> {\n    #[allow(unused)]\n    pin: Flex<'d>,\n    phantom: PhantomData<if_afio!((T, C, A))>,\n}\nimpl<'d, T: GeneralInstance4Channel, C: TimerChannel, #[cfg(afio)] A> if_afio!(CapturePin<'d, T, C, A>) {\n    /// Create a new capture pin instance.\n    pub fn new(pin: Peri<'d, if_afio!(impl TimerPin<T, C, A>)>, pull: Pull) -> Self {\n        set_as_af!(pin, AfType::input(pull));\n        CapturePin {\n            pin: Flex::new(pin),\n            phantom: PhantomData,\n        }\n    }\n}\n\n/// Input capture driver.\npub struct InputCapture<'d, T: GeneralInstance4Channel> {\n    inner: Timer<'d, T>,\n    _ch1: Option<Flex<'d>>,\n    _ch2: Option<Flex<'d>>,\n    _ch3: Option<Flex<'d>>,\n    _ch4: Option<Flex<'d>>,\n}\n\nimpl<'d, T: GeneralInstance4Channel> InputCapture<'d, T> {\n    /// Create a new input capture driver.\n    #[allow(unused)]\n    pub fn new<#[cfg(afio)] A>(\n        tim: Peri<'d, T>,\n        ch1: Option<if_afio!(CapturePin<'d, T, Ch1, A>)>,\n        ch2: Option<if_afio!(CapturePin<'d, T, Ch2, A>)>,\n        ch3: Option<if_afio!(CapturePin<'d, T, Ch3, A>)>,\n        ch4: Option<if_afio!(CapturePin<'d, T, Ch4, A>)>,\n        _irq: impl Binding<T::CaptureCompareInterrupt, CaptureCompareInterruptHandler<T>> + 'd,\n        freq: Hertz,\n        counting_mode: CountingMode,\n    ) -> Self {\n        Self::new_inner(\n            tim,\n            ch1.map(|pin| pin.pin),\n            ch2.map(|pin| pin.pin),\n            ch3.map(|pin| pin.pin),\n            ch4.map(|pin| pin.pin),\n            freq,\n            counting_mode,\n        )\n    }\n\n    fn new_inner(\n        tim: Peri<'d, T>,\n        _ch1: Option<Flex<'d>>,\n        _ch2: Option<Flex<'d>>,\n        _ch3: Option<Flex<'d>>,\n        _ch4: Option<Flex<'d>>,\n        freq: Hertz,\n        counting_mode: CountingMode,\n    ) -> Self {\n        let mut this = Self {\n            inner: Timer::new(tim),\n            _ch1,\n            _ch2,\n            _ch3,\n            _ch4,\n        };\n\n        this.inner.set_counting_mode(counting_mode);\n        this.inner.set_tick_freq(freq);\n        this.inner.enable_outputs(); // Required for advanced timers, see GeneralInstance4Channel for details\n        this.inner.generate_update_event();\n        this.inner.start();\n\n        // enable NVIC interrupt\n        T::CaptureCompareInterrupt::unpend();\n        unsafe { T::CaptureCompareInterrupt::enable() };\n\n        this\n    }\n\n    /// Enable the given channel.\n    pub fn enable(&mut self, channel: Channel) {\n        self.inner.enable_channel(channel, true);\n    }\n\n    /// Disable the given channel.\n    pub fn disable(&mut self, channel: Channel) {\n        self.inner.enable_channel(channel, false);\n    }\n\n    /// Check whether given channel is enabled\n    pub fn is_enabled(&self, channel: Channel) -> bool {\n        self.inner.get_channel_enable_state(channel)\n    }\n\n    /// Set the input capture mode for a given channel.\n    pub fn set_input_capture_mode(&mut self, channel: Channel, mode: InputCaptureMode) {\n        self.inner.set_input_capture_mode(channel, mode);\n    }\n\n    /// Set input TI selection.\n    pub fn set_input_ti_selection(&mut self, channel: Channel, tisel: InputTISelection) {\n        self.inner.set_input_ti_selection(channel, tisel)\n    }\n\n    /// Get capture value for a channel.\n    pub fn get_capture_value(&self, channel: Channel) -> T::Word {\n        self.inner.get_capture_value(channel)\n    }\n\n    /// Get input interrupt.\n    pub fn get_input_interrupt(&self, channel: Channel) -> bool {\n        self.inner.get_input_interrupt(channel)\n    }\n\n    fn new_future(&self, channel: Channel, mode: InputCaptureMode, tisel: InputTISelection) -> InputCaptureFuture<T> {\n        // Configuration steps from ST RM0390 (STM32F446) chapter 17.3.5\n        // or ST RM0008 (STM32F103) chapter 15.3.5 Input capture mode\n        self.inner.set_input_ti_selection(channel, tisel);\n        self.inner.set_input_capture_filter(channel, FilterValue::NO_FILTER);\n        self.inner.set_input_capture_mode(channel, mode);\n        self.inner.set_input_capture_prescaler(channel, 0);\n        self.inner.enable_channel(channel, true);\n        self.inner.enable_input_interrupt(channel, true);\n\n        InputCaptureFuture {\n            channel,\n            phantom: PhantomData,\n        }\n    }\n\n    /// Asynchronously wait until the pin sees a rising edge.\n    pub async fn wait_for_rising_edge(&mut self, channel: Channel) -> T::Word {\n        self.new_future(channel, InputCaptureMode::Rising, InputTISelection::Normal)\n            .await\n    }\n\n    /// Asynchronously wait until the pin sees a falling edge.\n    pub async fn wait_for_falling_edge(&mut self, channel: Channel) -> T::Word {\n        self.new_future(channel, InputCaptureMode::Falling, InputTISelection::Normal)\n            .await\n    }\n\n    /// Asynchronously wait until the pin sees any edge.\n    pub async fn wait_for_any_edge(&mut self, channel: Channel) -> T::Word {\n        self.new_future(channel, InputCaptureMode::BothEdges, InputTISelection::Normal)\n            .await\n    }\n\n    /// Asynchronously wait until the (alternate) pin sees a rising edge.\n    pub async fn wait_for_rising_edge_alternate(&mut self, channel: Channel) -> T::Word {\n        self.new_future(channel, InputCaptureMode::Rising, InputTISelection::Alternate)\n            .await\n    }\n\n    /// Asynchronously wait until the (alternate) pin sees a falling edge.\n    pub async fn wait_for_falling_edge_alternate(&mut self, channel: Channel) -> T::Word {\n        self.new_future(channel, InputCaptureMode::Falling, InputTISelection::Alternate)\n            .await\n    }\n\n    /// Asynchronously wait until the (alternate) pin sees any edge.\n    pub async fn wait_for_any_edge_alternate(&mut self, channel: Channel) -> T::Word {\n        self.new_future(channel, InputCaptureMode::BothEdges, InputTISelection::Alternate)\n            .await\n    }\n\n    /// Capture a sequence of timer input edges into a buffer using DMA\n    pub async fn receive_waveform<M, D: super::Dma<T, M>>(\n        &mut self,\n        dma: Peri<'_, D>,\n        irq: impl crate::interrupt::typelevel::Binding<D::Interrupt, crate::dma::InterruptHandler<D>>,\n        buf: &mut [u16],\n    ) where\n        M: TimerChannel,\n    {\n        #[allow(clippy::let_unit_value)] // eg. stm32f334\n        let req = dma.request();\n\n        let original_enable_state = self.is_enabled(M::CHANNEL);\n        let original_cc_dma_enable_state = self.inner.get_cc_dma_enable_state(M::CHANNEL);\n\n        self.inner.set_input_ti_selection(M::CHANNEL, InputTISelection::Normal);\n        self.inner\n            .set_input_capture_mode(M::CHANNEL, InputCaptureMode::BothEdges);\n\n        if !original_cc_dma_enable_state {\n            self.inner.set_cc_dma_enable_state(M::CHANNEL, true);\n        }\n\n        if !original_enable_state {\n            self.enable(M::CHANNEL);\n        }\n\n        unsafe {\n            let mut dma_channel = dma::Channel::new(dma, irq);\n            dma_channel\n                .read(\n                    req,\n                    self.inner.regs_gp16().ccr(M::CHANNEL.index()).as_ptr() as *mut u16,\n                    buf,\n                    dma::TransferOptions::default(),\n                )\n                .await\n        };\n\n        // restore output compare state\n        if !original_enable_state {\n            self.disable(M::CHANNEL);\n        }\n    }\n}\n\n#[must_use = \"futures do nothing unless you `.await` or poll them\"]\nstruct InputCaptureFuture<T: GeneralInstance4Channel> {\n    channel: Channel,\n    phantom: PhantomData<T>,\n}\n\nimpl<T: GeneralInstance4Channel> Drop for InputCaptureFuture<T> {\n    fn drop(&mut self) {\n        critical_section::with(|_| {\n            let regs = unsafe { crate::pac::timer::TimGp16::from_ptr(T::regs()) };\n\n            // disable interrupt enable\n            regs.dier().modify(|w| w.set_ccie(self.channel.index(), false));\n        });\n    }\n}\n\nimpl<T: GeneralInstance4Channel> Future for InputCaptureFuture<T> {\n    type Output = T::Word;\n\n    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {\n        T::state().cc_waker[self.channel.index()].register(cx.waker());\n\n        let regs = unsafe { crate::pac::timer::TimGp16::from_ptr(T::regs()) };\n\n        let dier = regs.dier().read();\n        if !dier.ccie(self.channel.index()) {\n            let val = unwrap!(regs.ccr(self.channel.index()).read().ccr().try_into());\n            Poll::Ready(val)\n        } else {\n            Poll::Pending\n        }\n    }\n}\n"
  },
  {
    "path": "embassy-stm32/src/timer/low_level.rs",
    "content": "//! Low-level timer driver.\n//!\n//! This is an unopinionated, very low-level driver for all STM32 timers. It allows direct register\n//! manipulation with the `regs_*()` methods, and has utility functions that are thin wrappers\n//! over the registers.\n//!\n//! The available functionality depends on the timer type.\n\nuse core::mem::ManuallyDrop;\n\nuse embassy_hal_internal::Peri;\n#[cfg(not(stm32l0))]\npub use stm32_metapac::timer::vals::{Bkinp as BreakComparatorPolarity, Bkp as BreakInputPolarity};\n// Re-export useful enums\npub use stm32_metapac::timer::vals::{FilterValue, Mms as MasterMode, Sms as SlaveMode, Ts as TriggerSource};\n\nuse super::*;\nuse crate::dma::{self, Transfer, WritableRingBuffer};\nuse crate::pac::timer::vals;\nuse crate::rcc;\nuse crate::time::Hertz;\n\n/// Input capture mode.\n#[derive(Clone, Copy)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub enum InputCaptureMode {\n    /// Rising edge only.\n    Rising,\n    /// Falling edge only.\n    Falling,\n    /// Both rising or falling edges.\n    BothEdges,\n}\n\n/// Input TI selection.\n#[derive(Clone, Copy)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub enum InputTISelection {\n    /// Normal\n    Normal,\n    /// Alternate\n    Alternate,\n    /// TRC\n    TRC,\n}\n\nimpl From<InputTISelection> for stm32_metapac::timer::vals::CcmrInputCcs {\n    fn from(tisel: InputTISelection) -> Self {\n        match tisel {\n            InputTISelection::Normal => stm32_metapac::timer::vals::CcmrInputCcs::TI4,\n            InputTISelection::Alternate => stm32_metapac::timer::vals::CcmrInputCcs::TI3,\n            InputTISelection::TRC => stm32_metapac::timer::vals::CcmrInputCcs::TRC,\n        }\n    }\n}\n\n/// Timer counting mode.\n#[repr(u8)]\n#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub enum CountingMode {\n    #[default]\n    /// The timer counts up to the reload value and then resets back to 0.\n    EdgeAlignedUp,\n    /// The timer counts down to 0 and then resets back to the reload value.\n    EdgeAlignedDown,\n    /// The timer counts up to the reload value and then counts back to 0.\n    ///\n    /// The output compare interrupt flags of channels configured in output are\n    /// set when the counter is counting down.\n    CenterAlignedDownInterrupts,\n    /// The timer counts up to the reload value and then counts back to 0.\n    ///\n    /// The output compare interrupt flags of channels configured in output are\n    /// set when the counter is counting up.\n    CenterAlignedUpInterrupts,\n    /// The timer counts up to the reload value and then counts back to 0.\n    ///\n    /// The output compare interrupt flags of channels configured in output are\n    /// set when the counter is counting both up or down.\n    CenterAlignedBothInterrupts,\n}\n\nimpl CountingMode {\n    /// Return whether this mode is edge-aligned (up or down).\n    pub fn is_edge_aligned(&self) -> bool {\n        matches!(self, CountingMode::EdgeAlignedUp | CountingMode::EdgeAlignedDown)\n    }\n\n    /// Return whether this mode is center-aligned.\n    pub fn is_center_aligned(&self) -> bool {\n        matches!(\n            self,\n            CountingMode::CenterAlignedDownInterrupts\n                | CountingMode::CenterAlignedUpInterrupts\n                | CountingMode::CenterAlignedBothInterrupts\n        )\n    }\n}\n\nimpl From<CountingMode> for (vals::Cms, vals::Dir) {\n    fn from(value: CountingMode) -> Self {\n        match value {\n            CountingMode::EdgeAlignedUp => (vals::Cms::EDGE_ALIGNED, vals::Dir::UP),\n            CountingMode::EdgeAlignedDown => (vals::Cms::EDGE_ALIGNED, vals::Dir::DOWN),\n            CountingMode::CenterAlignedDownInterrupts => (vals::Cms::CENTER_ALIGNED1, vals::Dir::UP),\n            CountingMode::CenterAlignedUpInterrupts => (vals::Cms::CENTER_ALIGNED2, vals::Dir::UP),\n            CountingMode::CenterAlignedBothInterrupts => (vals::Cms::CENTER_ALIGNED3, vals::Dir::UP),\n        }\n    }\n}\n\nimpl From<(vals::Cms, vals::Dir)> for CountingMode {\n    fn from(value: (vals::Cms, vals::Dir)) -> Self {\n        match value {\n            (vals::Cms::EDGE_ALIGNED, vals::Dir::UP) => CountingMode::EdgeAlignedUp,\n            (vals::Cms::EDGE_ALIGNED, vals::Dir::DOWN) => CountingMode::EdgeAlignedDown,\n            (vals::Cms::CENTER_ALIGNED1, _) => CountingMode::CenterAlignedDownInterrupts,\n            (vals::Cms::CENTER_ALIGNED2, _) => CountingMode::CenterAlignedUpInterrupts,\n            (vals::Cms::CENTER_ALIGNED3, _) => CountingMode::CenterAlignedBothInterrupts,\n        }\n    }\n}\n\n/// Output compare mode.\n#[derive(Clone, Copy)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub enum OutputCompareMode {\n    /// The comparison between the output compare register TIMx_CCRx and\n    /// the counter TIMx_CNT has no effect on the outputs.\n    /// (this mode is used to generate a timing base).\n    Frozen,\n    /// Set channel to active level on match. OCxREF signal is forced high when the\n    /// counter TIMx_CNT matches the capture/compare register x (TIMx_CCRx).\n    ActiveOnMatch,\n    /// Set channel to inactive level on match. OCxREF signal is forced low when the\n    /// counter TIMx_CNT matches the capture/compare register x (TIMx_CCRx).\n    InactiveOnMatch,\n    /// Toggle - OCxREF toggles when TIMx_CNT=TIMx_CCRx.\n    Toggle,\n    /// Force inactive level - OCxREF is forced low.\n    ForceInactive,\n    /// Force active level - OCxREF is forced high.\n    ForceActive,\n    /// PWM mode 1 - In upcounting, channel is active as long as TIMx_CNT<TIMx_CCRx\n    /// else inactive. In downcounting, channel is inactive (OCxREF=0) as long as\n    /// TIMx_CNT>TIMx_CCRx else active (OCxREF=1).\n    PwmMode1,\n    /// PWM mode 2 - In upcounting, channel is inactive as long as\n    /// TIMx_CNT<TIMx_CCRx else active. In downcounting, channel is active as long as\n    /// TIMx_CNT>TIMx_CCRx else inactive.\n    PwmMode2,\n\n    #[cfg(timer_v2)]\n    /// In up-counting mode, the channel is active until a trigger\n    /// event is detected (on tim_trgi signal). Then, a comparison is performed as in PWM\n    /// mode 1 and the channels becomes active again at the next update. In down-counting\n    /// mode, the channel is inactive until a trigger event is detected (on tim_trgi signal).\n    /// Then, a comparison is performed as in PWM mode 1 and the channels becomes\n    /// inactive again at the next update.\n    OnePulseMode1,\n\n    #[cfg(timer_v2)]\n    /// In up-counting mode, the channel is inactive until a\n    /// trigger event is detected (on tim_trgi signal). Then, a comparison is performed as in\n    /// PWM mode 2 and the channels becomes inactive again at the next update. In down\n    /// counting mode, the channel is active until a trigger event is detected (on tim_trgi\n    /// signal). Then, a comparison is performed as in PWM mode 1 and the channels\n    /// becomes active again at the next update.\n    OnePulseMode2,\n\n    #[cfg(timer_v2)]\n    /// Combined PWM mode 1 - tim_oc1ref has the same behavior as in PWM mode 1.\n    /// tim_oc1refc is the logical OR between tim_oc1ref and tim_oc2ref.\n    CombinedPwmMode1,\n\n    #[cfg(timer_v2)]\n    /// Combined PWM mode 2 - tim_oc1ref has the same behavior as in PWM mode 2.\n    /// tim_oc1refc is the logical AND between tim_oc1ref and tim_oc2ref.\n    CombinedPwmMode2,\n\n    #[cfg(timer_v2)]\n    /// tim_oc1ref has the same behavior as in PWM mode 1. tim_oc1refc outputs tim_oc1ref\n    /// when the counter is counting up, tim_oc2ref when it is counting down.\n    AsymmetricPwmMode1,\n\n    #[cfg(timer_v2)]\n    /// tim_oc1ref has the same behavior as in PWM mode 2. tim_oc1refc outputs tim_oc1ref\n    /// when the counter is counting up, tim_oc2ref when it is counting down.\n    AsymmetricPwmMode2,\n}\n\n#[cfg(timer_v3)]\nimpl From<OutputCompareMode> for crate::pac::timer::vals::OcmGp {\n    fn from(mode: OutputCompareMode) -> Self {\n        match mode {\n            OutputCompareMode::Frozen => crate::pac::timer::vals::OcmGp::FROZEN,\n            OutputCompareMode::ActiveOnMatch => crate::pac::timer::vals::OcmGp::ACTIVE_ON_MATCH,\n            OutputCompareMode::InactiveOnMatch => crate::pac::timer::vals::OcmGp::INACTIVE_ON_MATCH,\n            OutputCompareMode::Toggle => crate::pac::timer::vals::OcmGp::TOGGLE,\n            OutputCompareMode::ForceInactive => crate::pac::timer::vals::OcmGp::FORCE_INACTIVE,\n            OutputCompareMode::ForceActive => crate::pac::timer::vals::OcmGp::FORCE_ACTIVE,\n            OutputCompareMode::PwmMode1 => crate::pac::timer::vals::OcmGp::PWM_MODE1,\n            OutputCompareMode::PwmMode2 => crate::pac::timer::vals::OcmGp::PWM_MODE2,\n        }\n    }\n}\n\nimpl From<OutputCompareMode> for crate::pac::timer::vals::Ocm {\n    fn from(mode: OutputCompareMode) -> Self {\n        match mode {\n            OutputCompareMode::Frozen => crate::pac::timer::vals::Ocm::FROZEN,\n            OutputCompareMode::ActiveOnMatch => crate::pac::timer::vals::Ocm::ACTIVE_ON_MATCH,\n            OutputCompareMode::InactiveOnMatch => crate::pac::timer::vals::Ocm::INACTIVE_ON_MATCH,\n            OutputCompareMode::Toggle => crate::pac::timer::vals::Ocm::TOGGLE,\n            OutputCompareMode::ForceInactive => crate::pac::timer::vals::Ocm::FORCE_INACTIVE,\n            OutputCompareMode::ForceActive => crate::pac::timer::vals::Ocm::FORCE_ACTIVE,\n            OutputCompareMode::PwmMode1 => crate::pac::timer::vals::Ocm::PWM_MODE1,\n            OutputCompareMode::PwmMode2 => crate::pac::timer::vals::Ocm::PWM_MODE2,\n            #[cfg(timer_v2)]\n            OutputCompareMode::OnePulseMode1 => crate::pac::timer::vals::Ocm::RETRIGERRABLE_OPM_MODE_1,\n            #[cfg(timer_v2)]\n            OutputCompareMode::OnePulseMode2 => crate::pac::timer::vals::Ocm::RETRIGERRABLE_OPM_MODE_2,\n            #[cfg(timer_v2)]\n            OutputCompareMode::CombinedPwmMode1 => crate::pac::timer::vals::Ocm::COMBINED_PWM_MODE_1,\n            #[cfg(timer_v2)]\n            OutputCompareMode::CombinedPwmMode2 => crate::pac::timer::vals::Ocm::COMBINED_PWM_MODE_2,\n            #[cfg(timer_v2)]\n            OutputCompareMode::AsymmetricPwmMode1 => crate::pac::timer::vals::Ocm::ASYMMETRIC_PWM_MODE_1,\n            #[cfg(timer_v2)]\n            OutputCompareMode::AsymmetricPwmMode2 => crate::pac::timer::vals::Ocm::ASYMMETRIC_PWM_MODE_2,\n        }\n    }\n}\n\n/// Timer output pin polarity.\n#[derive(Clone, Copy)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub enum OutputPolarity {\n    /// Active high (higher duty value makes the pin spend more time high).\n    ActiveHigh,\n    /// Active low (higher duty value makes the pin spend more time low).\n    ActiveLow,\n}\n\nimpl From<OutputPolarity> for bool {\n    fn from(mode: OutputPolarity) -> Self {\n        match mode {\n            OutputPolarity::ActiveHigh => false,\n            OutputPolarity::ActiveLow => true,\n        }\n    }\n}\n\n/// Rounding mode for timer period/frequency configuration.\n///\n/// When configuring a timer, the exact requested period may not be achievable\n/// due to hardware limitations (prescaler and counter are integers). This enum\n/// controls how the driver rounds the configuration.\n#[derive(Debug, Clone, Copy, PartialEq, Eq)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub enum RoundTo {\n    /// Round towards a slower timer (higher period, lower frequency).\n    ///\n    /// The actual period will be >= the requested period.\n    Slower,\n    /// Round towards a faster timer (lower period, higher frequency).\n    ///\n    /// The actual period will be <= the requested period.\n    Faster,\n}\n\n/// Result of PSC/ARR calculation for timer configuration.\n#[derive(Debug, Clone, Copy, PartialEq, Eq)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\nstruct PscArrConfig {\n    /// Prescaler value (0-65535). The timer clock is divided by `psc + 1`.\n    psc: u16,\n    /// Auto-reload value. The timer counts from 0 to `arr`, then wraps.\n    arr: u64,\n    /// The actual period in clock cycles that will be achieved: `(psc + 1) * (arr + 1)`.\n    actual_period_clocks: u64,\n}\n\n/// Error returned when the requested timer period is out of range.\n///\n/// This occurs when:\n/// - For `RoundTo::Faster`: The requested period is less than 2 (minimum achievable is 2, since ARR >= 1).\n/// - For `RoundTo::Slower`: The required prescaler exceeds 16 bits.\n#[derive(Debug, Clone, Copy, PartialEq, Eq)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub struct OutOfRangeError;\n\n/// Calculate prescaler (PSC) and auto-reload (ARR) values for a desired timer period.\n///\n/// # Arguments\n/// * `period_clocks` - The desired period in timer clock cycles\n/// * `round` - How to round when exact period is not achievable\n/// * `max_arr_bits` - Maximum bits for ARR register (16 or 32)\n///\n/// # Returns\n/// A [`PscArrConfig`] containing the calculated values, or an [`OutOfRangeError`] if the\n/// requested period cannot be achieved with the given rounding mode.\n///\n/// # Errors\n/// Returns `OutOfRangeError` when:\n/// - `RoundTo::Faster` and `period_clocks < 2`: Cannot achieve period <= 1 (minimum is 2 since ARR >= 1).\n/// - `RoundTo::Slower` and the required prescaler exceeds 16 bits.\nfn calculate_psc_arr(period_clocks: u64, round: RoundTo, max_arr_bits: usize) -> Result<PscArrConfig, OutOfRangeError> {\n    let max_arr: u64 = (1 << max_arr_bits) - 1;\n\n    // Minimum achievable period is 2 (psc=0, arr=1), since ARR=0 is not valid.\n    const MIN_PERIOD: u64 = 2;\n\n    // For Faster, we need actual_period_clocks <= period_clocks\n    // If period_clocks < MIN_PERIOD, we can't achieve this\n    if round == RoundTo::Faster && period_clocks < MIN_PERIOD {\n        return Err(OutOfRangeError);\n    }\n\n    // We need: period_clocks = (psc + 1) * (arr + 1)\n    // Calculate minimum prescaler needed: psc >= period_clocks / (max_arr + 1) - 1\n    let psc_min = period_clocks.saturating_sub(1) / (max_arr + 1);\n    let psc: u16 = match psc_min.try_into() {\n        Ok(v) => v,\n        Err(_) => {\n            // Prescaler would overflow\n            match round {\n                RoundTo::Slower => return Err(OutOfRangeError), // Can't achieve actual >= requested\n                RoundTo::Faster => u16::MAX,                    // Use max psc; we only need actual <= requested\n            }\n        }\n    };\n\n    // Calculate arr for this prescaler\n    let psc_plus_1 = u64::from(psc) + 1;\n\n    // actual_clocks = (psc + 1) * (arr + 1), so arr = actual_clocks / (psc + 1) - 1\n    // We want actual_clocks as close to period_clocks as possible, respecting rounding mode\n    let arr = match round {\n        RoundTo::Faster => {\n            // Round down: actual_clocks <= period_clocks\n            // arr + 1 <= period_clocks / (psc + 1)\n            // arr <= period_clocks / (psc + 1) - 1\n            (period_clocks / psc_plus_1).saturating_sub(1)\n        }\n        RoundTo::Slower => {\n            // Round up: actual_clocks >= period_clocks\n            // arr + 1 >= ceil(period_clocks / (psc + 1))\n            // arr >= ceil(period_clocks / (psc + 1)) - 1\n            period_clocks.div_ceil(psc_plus_1).saturating_sub(1)\n        }\n    };\n\n    // Clamp arr to valid range (min is 1, not 0)\n    let arr = arr.clamp(1, max_arr);\n    let actual_period_clocks = psc_plus_1 * (arr + 1);\n\n    Ok(PscArrConfig {\n        psc,\n        arr,\n        actual_period_clocks,\n    })\n}\n\n/// Helper to round a division according to the rounding mode.\nfn div_round(numerator: u64, denominator: u64, round: RoundTo) -> u64 {\n    match round {\n        RoundTo::Faster => numerator / denominator,\n        RoundTo::Slower => numerator.div_ceil(denominator),\n    }\n}\n\n/// Low-level timer driver.\npub struct Timer<'d, T: CoreInstance> {\n    tim: Peri<'d, T>,\n}\n\nimpl<'d, T: CoreInstance> Drop for Timer<'d, T> {\n    fn drop(&mut self) {\n        rcc::disable::<T>();\n    }\n}\n\nimpl<'d, T: CoreInstance> Timer<'d, T> {\n    /// Create a new timer driver.\n    pub fn new(tim: Peri<'d, T>) -> Self {\n        rcc::enable_and_reset::<T>();\n\n        Self { tim }\n    }\n\n    pub(crate) unsafe fn clone_unchecked(&self) -> ManuallyDrop<Self> {\n        let tim = unsafe { self.tim.clone_unchecked() };\n        ManuallyDrop::new(Self { tim })\n    }\n\n    /// Get access to the virutal core 16bit timer registers.\n    ///\n    /// Note: This works even if the timer is more capable, because registers\n    /// for the less capable timers are a subset. This allows writing a driver\n    /// for a given set of capabilities, and having it transparently work with\n    /// more capable timers.\n    pub fn regs_core(&self) -> crate::pac::timer::TimCore {\n        unsafe { crate::pac::timer::TimCore::from_ptr(T::regs()) }\n    }\n\n    #[cfg(not(stm32l0))]\n    fn regs_gp32_unchecked(&self) -> crate::pac::timer::TimGp32 {\n        unsafe { crate::pac::timer::TimGp32::from_ptr(T::regs()) }\n    }\n\n    #[cfg(stm32l0)]\n    fn regs_gp32_unchecked(&self) -> crate::pac::timer::TimGp16 {\n        unsafe { crate::pac::timer::TimGp16::from_ptr(T::regs()) }\n    }\n\n    /// Start the timer.\n    pub fn start(&self) {\n        self.regs_core().cr1().modify(|r| r.set_cen(true));\n    }\n\n    /// Generate timer update event from software.\n    ///\n    /// Set URS to avoid generating interrupt or DMA request. This update event is only\n    /// used to load value from pre-load registers. If called when the timer is running,\n    /// it may disrupt the output waveform.\n    pub fn generate_update_event(&self) {\n        self.regs_core().cr1().modify(|r| r.set_urs(vals::Urs::COUNTER_ONLY));\n        self.regs_core().egr().write(|r| r.set_ug(true));\n        self.regs_core().cr1().modify(|r| r.set_urs(vals::Urs::ANY_EVENT));\n    }\n\n    /// Stop the timer.\n    pub fn stop(&self) {\n        self.regs_core().cr1().modify(|r| r.set_cen(false));\n    }\n\n    /// Reset the counter value to 0\n    pub fn reset(&self) {\n        self.regs_core().cnt().write(|r| r.set_cnt(0));\n    }\n\n    /// get the capability of the timer\n    pub fn bits(&self) -> TimerBits {\n        match T::Word::bits() {\n            16 => TimerBits::Bits16,\n            #[cfg(not(stm32l0))]\n            32 => TimerBits::Bits32,\n            _ => unreachable!(),\n        }\n    }\n\n    /// Set the timer period in timer clock cycles.\n    ///\n    /// The timer will count for `clocks` clock cycles before wrapping.\n    /// The actual period may differ from the requested value due to hardware\n    /// limitations; the `round` parameter controls how rounding is performed.\n    pub fn set_period_clocks(&self, clocks: u64, round: RoundTo) {\n        self.set_period_clocks_internal(clocks, round, T::Word::bits());\n    }\n\n    pub(crate) fn set_period_clocks_internal(&self, clocks: u64, round: RoundTo, max_arr_bits: usize) {\n        // TODO: we might want to propagate errors to the user instead of panicking.\n        let config = unwrap!(calculate_psc_arr(clocks, round, max_arr_bits));\n        let arr: T::Word = unwrap!(T::Word::try_from(config.arr));\n\n        let regs = self.regs_gp32_unchecked();\n        regs.psc().write_value(config.psc);\n        #[cfg(stm32l0)]\n        regs.arr().write(|r| r.set_arr(unwrap!(arr.try_into())));\n        #[cfg(not(stm32l0))]\n        regs.arr().write_value(arr.into());\n    }\n\n    /// Set the frequency of how many times per second the timer counts up to the max value or down to 0.\n    ///\n    /// This means that in the default edge-aligned mode,\n    /// the timer counter will wrap around at the same frequency as is being set.\n    /// In center-aligned mode (which not all timers support), the wrap-around frequency is effectively halved\n    /// because it needs to count up and down.\n    ///\n    /// The actual frequency may differ from the requested value due to hardware\n    /// limitations; the `round` parameter controls how rounding is performed.\n    pub fn set_frequency(&self, frequency: Hertz, round: RoundTo) {\n        let f = frequency.0;\n        assert!(f > 0);\n        let timer_f = T::frequency().0 as u64;\n        let clocks = div_round(timer_f, f as u64, round);\n        self.set_period_clocks(clocks, round);\n    }\n\n    /// Set the timer period in milliseconds.\n    ///\n    /// The actual period may differ from the requested value due to hardware\n    /// limitations; the `round` parameter controls how rounding is performed.\n    pub fn set_period_ms(&self, ms: u32, round: RoundTo) {\n        let timer_f = T::frequency().0 as u64;\n        let clocks = div_round(timer_f * ms as u64, 1_000, round);\n        self.set_period_clocks(clocks, round);\n    }\n\n    /// Set the timer period in microseconds.\n    ///\n    /// The actual period may differ from the requested value due to hardware\n    /// limitations; the `round` parameter controls how rounding is performed.\n    pub fn set_period_us(&self, us: u32, round: RoundTo) {\n        let timer_f = T::frequency().0 as u64;\n        let clocks = div_round(timer_f * us as u64, 1_000_000, round);\n        self.set_period_clocks(clocks, round);\n    }\n\n    /// Set the timer period in seconds.\n    ///\n    /// The actual period may differ from the requested value due to hardware\n    /// limitations; the `round` parameter controls how rounding is performed.\n    pub fn set_period_secs(&self, secs: u32, round: RoundTo) {\n        let timer_f = T::frequency().0 as u64;\n        let clocks = timer_f * secs as u64;\n        self.set_period_clocks(clocks, round);\n    }\n\n    /// Set the timer period using an `embassy_time::Duration`.\n    ///\n    /// The actual period may differ from the requested value due to hardware\n    /// limitations; the `round` parameter controls how rounding is performed.\n    #[cfg(feature = \"time\")]\n    pub fn set_period(&self, period: embassy_time::Duration, round: RoundTo) {\n        let timer_f = T::frequency().0 as u64;\n        let clocks = div_round(timer_f * period.as_ticks(), embassy_time::TICK_HZ, round);\n        self.set_period_clocks(clocks, round);\n    }\n\n    /// Set tick frequency.\n    pub fn set_tick_freq(&mut self, freq: Hertz) {\n        let f = freq;\n        assert!(f.0 > 0);\n        let timer_f = self.get_clock_frequency();\n\n        let pclk_ticks_per_timer_period = timer_f / f;\n        let psc: u16 = unwrap!((pclk_ticks_per_timer_period - 1).try_into());\n\n        let regs = self.regs_core();\n        regs.psc().write_value(psc);\n\n        // Generate an Update Request\n        regs.egr().write(|r| r.set_ug(true));\n    }\n\n    /// Clear update interrupt.\n    ///\n    /// Returns whether the update interrupt flag was set.\n    pub fn clear_update_interrupt(&self) -> bool {\n        let regs = self.regs_core();\n        let sr = regs.sr().read();\n        if sr.uif() {\n            regs.sr().modify(|r| {\n                r.set_uif(false);\n            });\n            true\n        } else {\n            false\n        }\n    }\n\n    /// Enable/disable the update interrupt.\n    pub fn enable_update_interrupt(&self, enable: bool) {\n        self.regs_core().dier().modify(|r| r.set_uie(enable));\n    }\n\n    /// Enable/disable autoreload preload.\n    pub fn set_autoreload_preload(&self, enable: bool) {\n        self.regs_core().cr1().modify(|r| r.set_arpe(enable));\n    }\n\n    /// Get the timer frequency.\n    pub fn get_frequency(&self) -> Hertz {\n        let timer_f = T::frequency();\n\n        let regs = self.regs_gp32_unchecked();\n        #[cfg(not(stm32l0))]\n        let arr = regs.arr().read();\n        #[cfg(stm32l0)]\n        let arr = regs.arr().read().arr();\n        let psc = regs.psc().read();\n\n        timer_f / arr / (psc + 1)\n    }\n\n    /// Get the clock frequency of the timer (before prescaler is applied).\n    pub fn get_clock_frequency(&self) -> Hertz {\n        T::frequency()\n    }\n}\n\nimpl<'d, T: BasicNoCr2Instance> Timer<'d, T> {\n    /// Get access to the Baisc 16bit timer registers.\n    ///\n    /// Note: This works even if the timer is more capable, because registers\n    /// for the less capable timers are a subset. This allows writing a driver\n    /// for a given set of capabilities, and having it transparently work with\n    /// more capable timers.\n    pub fn regs_basic_no_cr2(&self) -> crate::pac::timer::TimBasicNoCr2 {\n        unsafe { crate::pac::timer::TimBasicNoCr2::from_ptr(T::regs()) }\n    }\n\n    /// Enable/disable the update dma.\n    pub fn enable_update_dma(&self, enable: bool) {\n        self.regs_basic_no_cr2().dier().modify(|r| r.set_ude(enable));\n    }\n\n    /// Get the update dma enable/disable state.\n    pub fn get_update_dma_state(&self) -> bool {\n        self.regs_basic_no_cr2().dier().read().ude()\n    }\n}\n\nimpl<'d, T: BasicInstance> Timer<'d, T> {\n    /// Get access to the Baisc 16bit timer registers.\n    ///\n    /// Note: This works even if the timer is more capable, because registers\n    /// for the less capable timers are a subset. This allows writing a driver\n    /// for a given set of capabilities, and having it transparently work with\n    /// more capable timers.\n    pub fn regs_basic(&self) -> crate::pac::timer::TimBasic {\n        unsafe { crate::pac::timer::TimBasic::from_ptr(T::regs()) }\n    }\n}\n\nimpl<'d, T: GeneralInstance1Channel> Timer<'d, T> {\n    /// Get access to the general purpose 1 channel 16bit timer registers.\n    ///\n    /// Note: This works even if the timer is more capable, because registers\n    /// for the less capable timers are a subset. This allows writing a driver\n    /// for a given set of capabilities, and having it transparently work with\n    /// more capable timers.\n    pub fn regs_1ch(&self) -> crate::pac::timer::Tim1ch {\n        unsafe { crate::pac::timer::Tim1ch::from_ptr(T::regs()) }\n    }\n\n    /// Set clock divider.\n    pub fn set_clock_division(&self, ckd: vals::Ckd) {\n        self.regs_1ch().cr1().modify(|r| r.set_ckd(ckd));\n    }\n\n    /// Get max compare value. This depends on the timer frequency and the clock frequency from RCC.\n    pub fn get_max_compare_value(&self) -> T::Word {\n        #[cfg(not(stm32l0))]\n        return unwrap!(self.regs_gp32_unchecked().arr().read().try_into());\n        #[cfg(stm32l0)]\n        return unwrap!(self.regs_gp32_unchecked().arr().read().arr().try_into());\n    }\n\n    /// Set the max compare value.\n    ///\n    /// An update event is generated to load the new value. The update event is\n    /// generated such that it will not cause an interrupt or DMA request.\n    pub fn set_max_compare_value(&self, ticks: T::Word) {\n        let arr = ticks;\n\n        let regs = self.regs_gp32_unchecked();\n        #[cfg(not(stm32l0))]\n        regs.arr().write_value(arr.into());\n        #[cfg(stm32l0)]\n        regs.arr().write(|r| r.set_arr(unwrap!(arr.try_into())));\n\n        regs.cr1().modify(|r| r.set_urs(vals::Urs::COUNTER_ONLY));\n        regs.egr().write(|r| r.set_ug(true));\n        regs.cr1().modify(|r| r.set_urs(vals::Urs::ANY_EVENT));\n    }\n}\n\nimpl<'d, T: GeneralInstance2Channel> Timer<'d, T> {\n    /// Get access to the general purpose 2 channel 16bit timer registers.\n    ///\n    /// Note: This works even if the timer is more capable, because registers\n    /// for the less capable timers are a subset. This allows writing a driver\n    /// for a given set of capabilities, and having it transparently work with\n    /// more capable timers.\n    pub fn regs_2ch(&self) -> crate::pac::timer::Tim2ch {\n        unsafe { crate::pac::timer::Tim2ch::from_ptr(T::regs()) }\n    }\n}\n\nimpl<'d, T: GeneralInstance4Channel> Timer<'d, T> {\n    /// Get access to the general purpose 16bit timer registers.\n    ///\n    /// Note: This works even if the timer is more capable, because registers\n    /// for the less capable timers are a subset. This allows writing a driver\n    /// for a given set of capabilities, and having it transparently work with\n    /// more capable timers.\n    pub fn regs_gp16(&self) -> crate::pac::timer::TimGp16 {\n        unsafe { crate::pac::timer::TimGp16::from_ptr(T::regs()) }\n    }\n\n    /// Enable timer outputs.\n    pub fn enable_outputs(&self) {\n        self.tim.enable_outputs()\n    }\n\n    /// Set counting mode.\n    pub fn set_counting_mode(&self, mode: CountingMode) {\n        let (cms, dir) = mode.into();\n\n        let timer_enabled = self.regs_core().cr1().read().cen();\n        // Changing from edge aligned to center aligned (and vice versa) is not allowed while the timer is running.\n        // Changing direction is discouraged while the timer is running.\n        assert!(!timer_enabled);\n\n        self.regs_gp16().cr1().modify(|r| r.set_dir(dir));\n        self.regs_gp16().cr1().modify(|r| r.set_cms(cms))\n    }\n\n    /// Get counting mode.\n    pub fn get_counting_mode(&self) -> CountingMode {\n        let cr1 = self.regs_gp16().cr1().read();\n        (cr1.cms(), cr1.dir()).into()\n    }\n\n    /// Set input capture filter.\n    pub fn set_input_capture_filter(&self, channel: Channel, icf: vals::FilterValue) {\n        let raw_channel = channel.index();\n        self.regs_gp16()\n            .ccmr_input(raw_channel / 2)\n            .modify(|r| r.set_icf(raw_channel % 2, icf));\n    }\n\n    /// Clear input interrupt.\n    pub fn clear_input_interrupt(&self, channel: Channel) {\n        self.regs_gp16().sr().modify(|r| r.set_ccif(channel.index(), false));\n    }\n\n    /// Get input interrupt.\n    pub fn get_input_interrupt(&self, channel: Channel) -> bool {\n        self.regs_gp16().sr().read().ccif(channel.index())\n    }\n\n    /// Enable input interrupt.\n    pub fn enable_input_interrupt(&self, channel: Channel, enable: bool) {\n        self.regs_gp16().dier().modify(|r| r.set_ccie(channel.index(), enable));\n    }\n\n    /// Set input capture prescaler.\n    pub fn set_input_capture_prescaler(&self, channel: Channel, factor: u8) {\n        let raw_channel = channel.index();\n        self.regs_gp16()\n            .ccmr_input(raw_channel / 2)\n            .modify(|r| r.set_icpsc(raw_channel % 2, factor));\n    }\n\n    /// Set input TI selection.\n    pub fn set_input_ti_selection(&self, channel: Channel, tisel: InputTISelection) {\n        let raw_channel = channel.index();\n        self.regs_gp16()\n            .ccmr_input(raw_channel / 2)\n            .modify(|r| r.set_ccs(raw_channel % 2, tisel.into()));\n    }\n\n    /// Set input capture mode.\n    pub fn set_input_capture_mode(&self, channel: Channel, mode: InputCaptureMode) {\n        self.regs_gp16().ccer().modify(|r| match mode {\n            InputCaptureMode::Rising => {\n                r.set_ccnp(channel.index(), false);\n                r.set_ccp(channel.index(), false);\n            }\n            InputCaptureMode::Falling => {\n                r.set_ccnp(channel.index(), false);\n                r.set_ccp(channel.index(), true);\n            }\n            InputCaptureMode::BothEdges => {\n                r.set_ccnp(channel.index(), true);\n                r.set_ccp(channel.index(), true);\n            }\n        });\n    }\n\n    /// Set output compare mode.\n    pub fn set_output_compare_mode(&self, channel: Channel, mode: OutputCompareMode) {\n        let raw_channel: usize = channel.index();\n        self.regs_gp16()\n            .ccmr_output(raw_channel / 2)\n            .modify(|w| w.set_ocm(raw_channel % 2, mode.into()));\n    }\n\n    /// Set output polarity.\n    pub fn set_output_polarity(&self, channel: Channel, polarity: OutputPolarity) {\n        self.regs_gp16()\n            .ccer()\n            .modify(|w| w.set_ccp(channel.index(), polarity.into()));\n    }\n\n    /// Enable/disable a channel.\n    pub fn enable_channel(&self, channel: Channel, enable: bool) {\n        self.regs_gp16().ccer().modify(|w| w.set_cce(channel.index(), enable));\n    }\n\n    /// Get enable/disable state of a channel\n    pub fn get_channel_enable_state(&self, channel: Channel) -> bool {\n        self.regs_gp16().ccer().read().cce(channel.index())\n    }\n\n    /// Set compare value for a channel.\n    pub fn set_compare_value(&self, channel: Channel, value: T::Word) {\n        #[cfg(not(stm32l0))]\n        self.regs_gp32_unchecked()\n            .ccr(channel.index())\n            .write_value(value.into());\n        #[cfg(stm32l0)]\n        self.regs_gp16()\n            .ccr(channel.index())\n            .modify(|w| w.set_ccr(unwrap!(value.try_into())));\n    }\n\n    /// Get compare value for a channel.\n    pub fn get_compare_value(&self, channel: Channel) -> T::Word {\n        #[cfg(not(stm32l0))]\n        return unwrap!(self.regs_gp32_unchecked().ccr(channel.index()).read().try_into());\n        #[cfg(stm32l0)]\n        return unwrap!(self.regs_gp32_unchecked().ccr(channel.index()).read().ccr().try_into());\n    }\n\n    pub(crate) fn clamp_compare_value<W: Word>(&mut self, channel: Channel) {\n        self.set_compare_value(\n            channel,\n            unwrap!(\n                self.get_compare_value(channel)\n                    .into()\n                    .clamp(0, W::max() as u32)\n                    .try_into()\n            ),\n        );\n    }\n\n    /// Setup a ring buffer for the channel\n    pub fn setup_ring_buffer<'a, W: Word + Into<T::Word>, D: super::UpDma<T>>(\n        &mut self,\n        dma: Peri<'a, D>,\n        irq: impl crate::interrupt::typelevel::Binding<D::Interrupt, crate::dma::InterruptHandler<D>> + 'a,\n        channel: Channel,\n        dma_buf: &'a mut [W],\n    ) -> WritableRingBuffer<'a, W> {\n        #[allow(clippy::let_unit_value)] // eg. stm32f334\n        let req = dma.request();\n\n        unsafe {\n            use crate::dma::TransferOptions;\n            #[cfg(not(any(bdma, gpdma)))]\n            use crate::dma::{Burst, FifoThreshold};\n\n            let dma_transfer_option = TransferOptions {\n                #[cfg(not(any(bdma, gpdma)))]\n                fifo_threshold: Some(FifoThreshold::Full),\n                #[cfg(not(any(bdma, gpdma)))]\n                mburst: Burst::Incr8,\n                ..Default::default()\n            };\n\n            WritableRingBuffer::new(\n                dma::Channel::new(dma, irq),\n                req,\n                self.regs_1ch().ccr(channel.index()).as_ptr() as *mut W,\n                dma_buf,\n                dma_transfer_option,\n            )\n        }\n    }\n\n    /// Generate a sequence of PWM waveform\n    ///\n    /// Note:\n    /// you will need to provide corresponding TIMx_UP DMA channel to use this method.\n    pub fn setup_update_dma<'a, W: Word + Into<T::Word>, D: super::UpDma<T>>(\n        &mut self,\n        dma: Peri<'a, D>,\n        irq: impl crate::interrupt::typelevel::Binding<D::Interrupt, crate::dma::InterruptHandler<D>> + 'a,\n        channel: Channel,\n        duty: &'a [W],\n    ) -> Transfer<'a> {\n        self.setup_update_dma_inner(dma.request(), dma, irq, channel, duty)\n    }\n\n    /// Generate a sequence of PWM waveform\n    ///\n    /// Note:\n    /// The DMA channel provided does not need to correspond to the requested channel.\n    pub fn setup_channel_update_dma<'a, C: TimerChannel, W: Word + Into<T::Word>, D: super::Dma<T, C>>(\n        &mut self,\n        dma: Peri<'a, D>,\n        irq: impl crate::interrupt::typelevel::Binding<D::Interrupt, crate::dma::InterruptHandler<D>> + 'a,\n        channel: Channel,\n        duty: &'a [W],\n    ) -> Transfer<'a> {\n        self.setup_update_dma_inner(dma.request(), dma, irq, channel, duty)\n    }\n\n    fn setup_update_dma_inner<'a, W: Word + Into<T::Word>, D: dma::ChannelInstance>(\n        &mut self,\n        request: dma::Request,\n        dma: Peri<'a, D>,\n        irq: impl crate::interrupt::typelevel::Binding<D::Interrupt, crate::dma::InterruptHandler<D>> + 'a,\n        channel: Channel,\n        duty: &'a [W],\n    ) -> Transfer<'a> {\n        unsafe {\n            use crate::dma::TransferOptions;\n            #[cfg(not(any(bdma, gpdma)))]\n            use crate::dma::{Burst, FifoThreshold};\n\n            let dma_transfer_option = TransferOptions {\n                #[cfg(not(any(bdma, gpdma)))]\n                fifo_threshold: Some(FifoThreshold::Full),\n                #[cfg(not(any(bdma, gpdma)))]\n                mburst: Burst::Incr8,\n                ..Default::default()\n            };\n\n            let mut dma_channel = dma::Channel::new(dma, irq);\n            dma_channel\n                .write(\n                    request,\n                    duty,\n                    self.regs_gp16().ccr(channel.index()).as_ptr() as *mut W,\n                    dma_transfer_option,\n                )\n                .unchecked_extend_lifetime()\n        }\n    }\n\n    /// Generate a multichannel sequence of PWM waveforms using DMA triggered by timer update events.\n    ///\n    /// This method utilizes the timer's DMA burst transfer capability to update multiple CCRx registers\n    /// in sequence on each update event (UEV). The data is written via the DMAR register using the\n    /// DMA base address (DBA) and burst length (DBL) configured in the DCR register.\n    ///\n    /// The `duty` buffer must be structured as a flattened 2D array in row-major order, where each row\n    /// represents a single update event and each column corresponds to a specific timer channel (starting\n    /// from `starting_channel` up to and including `ending_channel`).\n    ///\n    /// For example, if using channels 1 through 4, a buffer of 4 update steps might look like:\n    ///\n    /// ```rust,ignore\n    /// let dma_buf: [u16; 16] = [\n    ///     ch1_duty_1, ch2_duty_1, ch3_duty_1, ch4_duty_1, // update 1\n    ///     ch1_duty_2, ch2_duty_2, ch3_duty_2, ch4_duty_2, // update 2\n    ///     ch1_duty_3, ch2_duty_3, ch3_duty_3, ch4_duty_3, // update 3\n    ///     ch1_duty_4, ch2_duty_4, ch3_duty_4, ch4_duty_4, // update 4\n    /// ];\n    /// ```\n    ///\n    /// Each group of `N` values (where `N` is number of channels) is transferred on one update event,\n    /// updating the duty cycles of all selected channels simultaneously.\n    ///\n    /// Note:\n    /// You will need to provide corresponding `TIMx_UP` DMA channel to use this method.\n    /// Also be aware that embassy timers use one of timers internally. It is possible to\n    /// switch this timer by using `time-driver-timX` feature.\n    ///\n    pub fn setup_update_dma_burst<'a, W: Word + Into<T::Word>, D: super::UpDma<T>>(\n        &mut self,\n        dma: Peri<'a, D>,\n        irq: impl crate::interrupt::typelevel::Binding<D::Interrupt, crate::dma::InterruptHandler<D>> + 'a,\n        starting_channel: Channel,\n        ending_channel: Channel,\n        duty: &'a [W],\n    ) -> Transfer<'a> {\n        let cr1_addr = self.regs_gp16().cr1().as_ptr() as u32;\n        let start_ch_index = starting_channel.index();\n        let end_ch_index = ending_channel.index();\n\n        assert!(start_ch_index <= end_ch_index);\n\n        let ccrx_addr = self.regs_gp16().ccr(start_ch_index).as_ptr() as u32;\n        self.regs_gp16()\n            .dcr()\n            .modify(|w| w.set_dba(((ccrx_addr - cr1_addr) / 4) as u8));\n        self.regs_gp16()\n            .dcr()\n            .modify(|w| w.set_dbl((end_ch_index - start_ch_index) as u8));\n\n        #[allow(clippy::let_unit_value)] // eg. stm32f334\n        let req = dma.request();\n\n        unsafe {\n            use crate::dma::TransferOptions;\n            #[cfg(not(any(bdma, gpdma)))]\n            use crate::dma::{Burst, FifoThreshold};\n\n            let dma_transfer_option = TransferOptions {\n                #[cfg(not(any(bdma, gpdma)))]\n                fifo_threshold: Some(FifoThreshold::Full),\n                #[cfg(not(any(bdma, gpdma)))]\n                mburst: Burst::Incr4,\n                ..Default::default()\n            };\n\n            let mut dma_channel = dma::Channel::new(dma, irq);\n            dma_channel\n                .write(\n                    req,\n                    duty,\n                    self.regs_gp16().dmar().as_ptr() as *mut W,\n                    dma_transfer_option,\n                )\n                .unchecked_extend_lifetime()\n        }\n    }\n\n    /// Get capture value for a channel.\n    pub fn get_capture_value(&self, channel: Channel) -> T::Word {\n        self.get_compare_value(channel)\n    }\n\n    /// Set output compare preload.\n    pub fn set_output_compare_preload(&self, channel: Channel, preload: bool) {\n        let channel_index = channel.index();\n        self.regs_gp16()\n            .ccmr_output(channel_index / 2)\n            .modify(|w| w.set_ocpe(channel_index % 2, preload));\n    }\n\n    /// Get capture compare DMA selection\n    pub fn get_cc_dma_selection(&self) -> vals::Ccds {\n        self.regs_gp16().cr2().read().ccds()\n    }\n\n    /// Set capture compare DMA selection\n    pub fn set_cc_dma_selection(&self, ccds: vals::Ccds) {\n        self.regs_gp16().cr2().modify(|w| w.set_ccds(ccds))\n    }\n\n    /// Get capture compare DMA enable state\n    pub fn get_cc_dma_enable_state(&self, channel: Channel) -> bool {\n        self.regs_gp16().dier().read().ccde(channel.index())\n    }\n\n    /// Set capture compare DMA enable state\n    pub fn set_cc_dma_enable_state(&self, channel: Channel, ccde: bool) {\n        self.regs_gp16().dier().modify(|w| w.set_ccde(channel.index(), ccde))\n    }\n\n    /// Set Timer Master Mode\n    pub fn set_master_mode(&self, mms: MasterMode) {\n        self.regs_gp16().cr2().modify(|w| w.set_mms(mms));\n    }\n\n    /// Set Timer Slave Mode\n    pub fn set_slave_mode(&self, sms: SlaveMode) {\n        self.regs_gp16().smcr().modify(|r| r.set_sms(sms));\n    }\n\n    /// Set Timer Trigger Source\n    pub fn set_trigger_source(&self, ts: TriggerSource) {\n        self.regs_gp16().smcr().modify(|r| r.set_ts(ts));\n    }\n\n    /// Set Timer Etr_in Source\n    #[cfg(not(stm32l0))]\n    pub fn set_etr_in_source(&self, val: u8) {\n        self.regs_gp16().af1().modify(|w| w.set_etrsel(val));\n    }\n\n    /// Set Timer External Trigger Filter\n    pub fn set_external_trigger_filter(&self, fv: FilterValue) {\n        self.regs_gp16().smcr().modify(|w| w.set_etf(fv));\n    }\n\n    /// Set Timer External Trigger prescaler\n    pub fn set_external_trigger_prescaler(&self, etp: vals::Etps) {\n        self.regs_gp16().smcr().modify(|w| w.set_etps(etp));\n    }\n\n    /// Set Timer External Trigger Polarity\n    pub fn set_external_trigger_polarity(&self, etp: vals::Etp) {\n        self.regs_gp16().smcr().modify(|w| w.set_etp(etp));\n    }\n\n    /// Set Timer External Clock Mode 2 Enable state\n    pub fn set_external_clock_mode_2_enable_state(&self, val: bool) {\n        self.regs_gp16().smcr().modify(|w| w.set_ece(val));\n    }\n}\n\n#[cfg(not(stm32l0))]\nimpl<'d, T: GeneralInstance32bit4Channel> Timer<'d, T> {\n    /// Get access to the general purpose 32bit timer registers.\n    ///\n    /// Note: This works even if the timer is more capable, because registers\n    /// for the less capable timers are a subset. This allows writing a driver\n    /// for a given set of capabilities, and having it transparently work with\n    /// more capable timers.\n    pub fn regs_gp32(&self) -> crate::pac::timer::TimGp32 {\n        unsafe { crate::pac::timer::TimGp32::from_ptr(T::regs()) }\n    }\n}\n\n#[cfg(not(stm32l0))]\nimpl<'d, T: AdvancedInstance1Channel> Timer<'d, T> {\n    /// Get access to the general purpose 1 channel with one complementary 16bit timer registers.\n    ///\n    /// Note: This works even if the timer is more capable, because registers\n    /// for the less capable timers are a subset. This allows writing a driver\n    /// for a given set of capabilities, and having it transparently work with\n    /// more capable timers.\n    pub fn regs_1ch_cmp(&self) -> crate::pac::timer::Tim1chCmp {\n        unsafe { crate::pac::timer::Tim1chCmp::from_ptr(T::regs()) }\n    }\n\n    /// Set clock divider for the dead time.\n    pub fn set_dead_time_clock_division(&self, value: vals::Ckd) {\n        self.regs_1ch_cmp().cr1().modify(|w| w.set_ckd(value));\n    }\n\n    /// Set dead time, as a fraction of the max duty value.\n    pub fn set_dead_time_value(&self, value: u8) {\n        self.regs_1ch_cmp().bdtr().modify(|w| w.set_dtg(value));\n    }\n\n    /// Set state of OSSI-bit in BDTR register\n    pub fn set_ossi(&self, val: vals::Ossi) {\n        self.regs_1ch_cmp().bdtr().modify(|w| w.set_ossi(val));\n    }\n\n    /// Get state of OSSI-bit in BDTR register\n    pub fn get_ossi(&self) -> vals::Ossi {\n        self.regs_1ch_cmp().bdtr().read().ossi()\n    }\n\n    /// Set state of OSSR-bit in BDTR register\n    pub fn set_ossr(&self, val: vals::Ossr) {\n        self.regs_1ch_cmp().bdtr().modify(|w| w.set_ossr(val));\n    }\n\n    /// Get state of OSSR-bit in BDTR register\n    pub fn get_ossr(&self) -> vals::Ossr {\n        self.regs_1ch_cmp().bdtr().read().ossr()\n    }\n\n    /// Set state of MOE-bit in BDTR register to en-/disable output\n    pub fn set_moe(&self, enable: bool) {\n        self.regs_1ch_cmp().bdtr().modify(|w| w.set_moe(enable));\n    }\n\n    /// Get state of MOE-bit in BDTR register\n    pub fn get_moe(&self) -> bool {\n        self.regs_1ch_cmp().bdtr().read().moe()\n    }\n\n    /// Enable/disable break input 1.\n    ///\n    /// When enabled, an active level on the break input puts the timer outputs\n    /// into a safe state (driven by OSSI/OSSR and OIS/OISN settings).\n    pub fn set_break_enable(&self, enable: bool) {\n        self.regs_1ch_cmp().bdtr().modify(|w| w.set_bke(0, enable));\n    }\n\n    /// Get break input 1 enable state.\n    pub fn get_break_enable(&self) -> bool {\n        self.regs_1ch_cmp().bdtr().read().bke(0)\n    }\n\n    /// Set break input 1 polarity.\n    pub fn set_break_polarity(&self, polarity: vals::Bkp) {\n        self.regs_1ch_cmp().bdtr().modify(|w| w.set_bkp(0, polarity));\n    }\n\n    /// Get break input 1 polarity.\n    pub fn get_break_polarity(&self) -> vals::Bkp {\n        self.regs_1ch_cmp().bdtr().read().bkp(0)\n    }\n\n    /// Set break input 1 digital filter.\n    ///\n    /// The filter rejects glitches shorter than the configured number of clock\n    /// cycles, preventing false break events from noise.\n    pub fn set_break_filter(&self, filter: FilterValue) {\n        self.regs_1ch_cmp().bdtr().modify(|w| w.set_bkf(0, filter));\n    }\n\n    /// Get break input 1 digital filter.\n    pub fn get_break_filter(&self) -> FilterValue {\n        self.regs_1ch_cmp().bdtr().read().bkf(0)\n    }\n\n    /// Enable/disable automatic output enable (AOE).\n    ///\n    /// When AOE is set, the MOE bit is automatically set at the next update\n    /// event after a break event (allowing automatic recovery). When cleared,\n    /// MOE can only be set by software.\n    pub fn set_automatic_output_enable(&self, enable: bool) {\n        self.regs_1ch_cmp().bdtr().modify(|w| w.set_aoe(enable));\n    }\n\n    /// Get automatic output enable (AOE) state.\n    pub fn get_automatic_output_enable(&self) -> bool {\n        self.regs_1ch_cmp().bdtr().read().aoe()\n    }\n\n    /// Enable/disable comparator output as break input 1 source.\n    ///\n    /// When enabled, the output of comparator `comp_index` (0-based: 0=COMP1, 1=COMP2, etc.)\n    /// is internally OR'd into the break input 1 signal. Multiple comparators can be\n    /// enabled simultaneously. This is configured via the TIMx_AF1 register BKCMPE bits.\n    ///\n    /// No GPIO pin is needed — the routing is fully internal.\n    pub fn set_break_comparator_enable(&self, comp_index: usize, enable: bool) {\n        self.regs_1ch_cmp().af1().modify(|w| w.set_bkcmpe(comp_index, enable));\n    }\n\n    /// Get comparator break input 1 enable state.\n    pub fn get_break_comparator_enable(&self, comp_index: usize) -> bool {\n        self.regs_1ch_cmp().af1().read().bkcmpe(comp_index)\n    }\n\n    /// Set comparator break input 1 polarity.\n    ///\n    /// Controls the polarity of comparator `comp_index` (0-based, max 3) output\n    /// when used as a break source. Only COMP1-COMP4 have individual polarity control.\n    pub fn set_break_comparator_polarity(&self, comp_index: usize, polarity: vals::Bkinp) {\n        self.regs_1ch_cmp().af1().modify(|w| w.set_bkcmpp(comp_index, polarity));\n    }\n\n    /// Get comparator break input 1 polarity.\n    pub fn get_break_comparator_polarity(&self, comp_index: usize) -> vals::Bkinp {\n        self.regs_1ch_cmp().af1().read().bkcmpp(comp_index)\n    }\n\n    /// Enable/disable the external BKIN pin as break input 1 source.\n    ///\n    /// This controls whether the TIMx_BKIN GPIO pin contributes to the break input.\n    /// When using only comparator-based break sources, this can be disabled.\n    pub fn set_break_input_pin_enable(&self, enable: bool) {\n        self.regs_1ch_cmp().af1().modify(|w| w.set_bkine(enable));\n    }\n\n    /// Get external BKIN pin enable state.\n    pub fn get_break_input_pin_enable(&self) -> bool {\n        self.regs_1ch_cmp().af1().read().bkine()\n    }\n}\n\n#[cfg(not(stm32l0))]\nimpl<'d, T: AdvancedInstance2Channel> Timer<'d, T> {\n    /// Get access to the general purpose 2 channel with one complementary 16bit timer registers.\n    ///\n    /// Note: This works even if the timer is more capable, because registers\n    /// for the less capable timers are a subset. This allows writing a driver\n    /// for a given set of capabilities, and having it transparently work with\n    /// more capable timers.\n    pub fn regs_2ch_cmp(&self) -> crate::pac::timer::Tim2chCmp {\n        unsafe { crate::pac::timer::Tim2chCmp::from_ptr(T::regs()) }\n    }\n}\n\n#[cfg(not(stm32l0))]\nimpl<'d, T: AdvancedInstance4Channel> Timer<'d, T> {\n    /// Get access to the advanced timer registers.\n    pub fn regs_advanced(&self) -> crate::pac::timer::TimAdv {\n        unsafe { crate::pac::timer::TimAdv::from_ptr(T::regs()) }\n    }\n\n    /// Set complementary output polarity.\n    pub fn set_complementary_output_polarity(&self, channel: Channel, polarity: OutputPolarity) {\n        self.regs_advanced()\n            .ccer()\n            .modify(|w| w.set_ccnp(channel.index(), polarity.into()));\n    }\n\n    /// Enable/disable a complementary channel.\n    pub fn enable_complementary_channel(&self, channel: Channel, enable: bool) {\n        self.regs_advanced()\n            .ccer()\n            .modify(|w| w.set_ccne(channel.index(), enable));\n    }\n\n    /// Set Output Idle State\n    pub fn set_ois(&self, channel: Channel, val: bool) {\n        self.regs_advanced().cr2().modify(|w| w.set_ois(channel.index(), val));\n    }\n    /// Set Output Idle State Complementary Channel\n    pub fn set_oisn(&self, channel: Channel, val: bool) {\n        self.regs_advanced().cr2().modify(|w| w.set_oisn(channel.index(), val));\n    }\n\n    /// Set master mode selection 2\n    pub fn set_mms2_selection(&self, mms2: vals::Mms2) {\n        self.regs_advanced().cr2().modify(|w| w.set_mms2(mms2));\n    }\n\n    /// Set repetition counter\n    pub fn set_repetition_counter(&self, val: u16) {\n        self.regs_advanced().rcr().modify(|w| w.set_rep(val));\n    }\n\n    /// Enable/disable break input 2.\n    ///\n    /// When enabled, an active level on break input 2 puts the timer outputs\n    /// into a safe state. Only available on advanced 4-channel timers.\n    pub fn set_break2_enable(&self, enable: bool) {\n        self.regs_advanced().bdtr().modify(|w| w.set_bke(1, enable));\n    }\n\n    /// Get break input 2 enable state.\n    pub fn get_break2_enable(&self) -> bool {\n        self.regs_advanced().bdtr().read().bke(1)\n    }\n\n    /// Set break input 2 polarity.\n    pub fn set_break2_polarity(&self, polarity: vals::Bkp) {\n        self.regs_advanced().bdtr().modify(|w| w.set_bkp(1, polarity));\n    }\n\n    /// Get break input 2 polarity.\n    pub fn get_break2_polarity(&self) -> vals::Bkp {\n        self.regs_advanced().bdtr().read().bkp(1)\n    }\n\n    /// Set break input 2 digital filter.\n    pub fn set_break2_filter(&self, filter: FilterValue) {\n        self.regs_advanced().bdtr().modify(|w| w.set_bkf(1, filter));\n    }\n\n    /// Get break input 2 digital filter.\n    pub fn get_break2_filter(&self) -> FilterValue {\n        self.regs_advanced().bdtr().read().bkf(1)\n    }\n\n    /// Trigger software break 1 or 2\n    /// Setting this bit generates a break event. This bit is automatically cleared by the hardware.\n    pub fn trigger_software_break(&self, n: usize) {\n        self.regs_advanced().egr().write(|r| r.set_bg(n, true));\n    }\n\n    /// Enable/disable comparator output as break input 2 source.\n    ///\n    /// When enabled, the output of comparator `comp_index` (0-based: 0=COMP1, 1=COMP2, etc.)\n    /// is internally OR'd into the break input 2 signal. Configured via TIMx_AF2 register.\n    pub fn set_break2_comparator_enable(&self, comp_index: usize, enable: bool) {\n        self.regs_advanced().af2().modify(|w| w.set_bk2cmpe(comp_index, enable));\n    }\n\n    /// Get comparator break input 2 enable state.\n    pub fn get_break2_comparator_enable(&self, comp_index: usize) -> bool {\n        self.regs_advanced().af2().read().bk2cmpe(comp_index)\n    }\n\n    /// Set comparator break input 2 polarity.\n    pub fn set_break2_comparator_polarity(&self, comp_index: usize, polarity: vals::Bkinp) {\n        self.regs_advanced()\n            .af2()\n            .modify(|w| w.set_bk2cmpp(comp_index, polarity));\n    }\n\n    /// Get comparator break input 2 polarity.\n    pub fn get_break2_comparator_polarity(&self, comp_index: usize) -> vals::Bkinp {\n        self.regs_advanced().af2().read().bk2cmpp(comp_index)\n    }\n\n    /// Enable/disable the external BK2IN pin as break input 2 source.\n    pub fn set_break2_input_pin_enable(&self, enable: bool) {\n        self.regs_advanced().af2().modify(|w| w.set_bk2ine(enable));\n    }\n\n    /// Get external BK2IN pin enable state.\n    pub fn get_break2_input_pin_enable(&self) -> bool {\n        self.regs_advanced().af2().read().bk2ine()\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n\n    /// Test cases: (period_clocks, max_arr_bits, expect_fail_slower, expect_fail_faster)\n    const TEST_CASES: &[(u64, usize, bool, bool)] = &[\n        // Small periods (no prescaler needed for 16-bit)\n        // period=0,1 fail for Faster because min achievable is 2 (arr=1)\n        (0, 16, false, true),\n        (1, 16, false, true),\n        (2, 16, false, false), // Minimum achievable period\n        (100, 16, false, false),\n        (1000, 16, false, false),\n        (65535, 16, false, false),\n        (65536, 16, false, false),\n        // Periods requiring prescaler for 16-bit\n        (65537, 16, false, false),\n        (100_000, 16, false, false),\n        (1_000_000, 16, false, false),\n        (10_000_000, 16, false, false),\n        // Edge cases around boundaries\n        (131070, 16, false, false), // 2 * 65535\n        (131072, 16, false, false), // 2 * 65536\n        (196605, 16, false, false), // 3 * 65535\n        // 32-bit timer cases\n        (0, 32, false, true),\n        (1, 32, false, true),\n        (2, 32, false, false),\n        (100_000, 32, false, false),\n        (1_000_000_000, 32, false, false),\n        (4_294_967_295, 32, false, false), // u32::MAX\n        (4_294_967_296, 32, false, false), // u32::MAX + 1\n        // Very large periods that would overflow 16-bit prescaler for Slower\n        // max_arr for 16-bit is 65535, so max period with psc=65535 is 65536*65536 = 4_294_967_296\n        // Anything larger than that fails for Slower (need actual >= requested, impossible)\n        // For Faster, it still works (need actual <= requested, can always use max period)\n        (4_294_967_297, 16, true, false), // Just over 16-bit max, fails Slower only\n    ];\n\n    fn actual_clocks(psc: u16, arr: u64) -> u64 {\n        (psc as u64 + 1) * (arr + 1)\n    }\n\n    #[test]\n    fn test_calculate_psc_arr() {\n        for &(period_clocks, max_arr_bits, expect_fail_slower, expect_fail_faster) in TEST_CASES {\n            let max_arr: u64 = (1 << max_arr_bits) - 1;\n\n            for round in [RoundTo::Slower, RoundTo::Faster] {\n                let expect_fail = match round {\n                    RoundTo::Slower => expect_fail_slower,\n                    RoundTo::Faster => expect_fail_faster,\n                };\n\n                let result = calculate_psc_arr(period_clocks, round, max_arr_bits);\n\n                if expect_fail {\n                    assert!(\n                        result.is_err(),\n                        \"Expected failure for period_clocks={}, round={:?}, max_arr_bits={}, but got {:?}\",\n                        period_clocks,\n                        round,\n                        max_arr_bits,\n                        result\n                    );\n                    continue;\n                }\n\n                let config = result.unwrap_or_else(|_| {\n                    panic!(\n                        \"Unexpected failure for period_clocks={}, round={:?}, max_arr_bits={}\",\n                        period_clocks, round, max_arr_bits\n                    )\n                });\n\n                // Verify actual_period_clocks matches (psc + 1) * (arr + 1)\n                let computed_actual = actual_clocks(config.psc, config.arr);\n                assert_eq!(\n                    config.actual_period_clocks, computed_actual,\n                    \"actual_period_clocks mismatch for period_clocks={}, round={:?}\",\n                    period_clocks, round\n                );\n\n                // Verify arr is within bounds (min is 1)\n                assert!(\n                    config.arr >= 1 && config.arr <= max_arr,\n                    \"arr {} out of bounds [1, {}] for period_clocks={}, round={:?}\",\n                    config.arr,\n                    max_arr,\n                    period_clocks,\n                    round\n                );\n\n                // Check rounding constraint\n                match round {\n                    RoundTo::Slower => {\n                        assert!(\n                            config.actual_period_clocks >= period_clocks,\n                            \"Slower: actual {} < requested {} for period_clocks={}, max_arr_bits={}\",\n                            config.actual_period_clocks,\n                            period_clocks,\n                            period_clocks,\n                            max_arr_bits\n                        );\n                    }\n                    RoundTo::Faster => {\n                        assert!(\n                            config.actual_period_clocks <= period_clocks,\n                            \"Faster: actual {} > requested {} for period_clocks={}, max_arr_bits={}\",\n                            config.actual_period_clocks,\n                            period_clocks,\n                            period_clocks,\n                            max_arr_bits\n                        );\n                    }\n                }\n\n                // Test mutations: verify the solution is not obviously suboptimal.\n                // Try all combinations of psc +/- 1 and arr +/- 1\n                // This doesn't guarantee optimality. but it's enough to catch dumb off-by-one bugs.\n                // Guaranteeing optimality would require searching all divisors of `period_clocks` which is obviously too expensive.\n                let mutations: [(i32, i64); 8] = [(-1, 0), (1, 0), (0, -1), (0, 1), (-1, -1), (-1, 1), (1, -1), (1, 1)];\n\n                for (psc_delta, arr_delta) in mutations {\n                    let new_psc = config.psc as i32 + psc_delta;\n                    let new_arr = config.arr as i64 + arr_delta;\n\n                    // Skip invalid mutations\n                    if new_psc < 0 || new_psc > u16::MAX as i32 {\n                        continue;\n                    }\n                    if new_arr < 1 || new_arr > max_arr as i64 {\n                        continue;\n                    }\n\n                    let new_psc = new_psc as u16;\n                    let new_arr = new_arr as u64;\n                    let new_actual = actual_clocks(new_psc, new_arr);\n\n                    // Check if mutation satisfies the rounding constraint\n                    let satisfies_constraint = match round {\n                        RoundTo::Slower => new_actual >= period_clocks,\n                        RoundTo::Faster => new_actual <= period_clocks,\n                    };\n\n                    if satisfies_constraint {\n                        // If it satisfies the constraint, it should not be better (closer) than our solution\n                        let our_distance = (config.actual_period_clocks as i64 - period_clocks as i64).abs();\n                        let new_distance = (new_actual as i64 - period_clocks as i64).abs();\n\n                        assert!(\n                            new_distance >= our_distance,\n                            \"Found better solution via mutation for period_clocks={}, round={:?}, max_arr_bits={}: \\\n                             original (psc={}, arr={}, actual={}, dist={}) vs \\\n                             mutated (psc={}, arr={}, actual={}, dist={})\",\n                            period_clocks,\n                            round,\n                            max_arr_bits,\n                            config.psc,\n                            config.arr,\n                            config.actual_period_clocks,\n                            our_distance,\n                            new_psc,\n                            new_arr,\n                            new_actual,\n                            new_distance\n                        );\n                    }\n                    // If mutation doesn't satisfy constraint, that's fine - our solution is better\n                }\n            }\n        }\n    }\n\n    #[test]\n    fn test_div_round() {\n        // Faster (round down)\n        assert_eq!(div_round(10, 3, RoundTo::Faster), 3);\n        assert_eq!(div_round(9, 3, RoundTo::Faster), 3);\n        assert_eq!(div_round(11, 3, RoundTo::Faster), 3);\n        assert_eq!(div_round(12, 3, RoundTo::Faster), 4);\n\n        // Slower (round up)\n        assert_eq!(div_round(10, 3, RoundTo::Slower), 4);\n        assert_eq!(div_round(9, 3, RoundTo::Slower), 3);\n        assert_eq!(div_round(11, 3, RoundTo::Slower), 4);\n        assert_eq!(div_round(12, 3, RoundTo::Slower), 4);\n    }\n}\n"
  },
  {
    "path": "embassy-stm32/src/timer/mod.rs",
    "content": "//! Timers, PWM, quadrature decoder.\n\nuse core::marker::PhantomData;\n\nuse embassy_hal_internal::PeripheralType;\nuse embassy_sync::waitqueue::AtomicWaker;\n\n#[cfg(not(stm32l0))]\npub mod complementary_pwm;\npub mod input_capture;\npub mod low_level;\npub mod one_pulse;\npub mod pwm_input;\npub mod qei;\npub mod ringbuffered;\npub mod simple_pwm;\n\nuse crate::dma::word::Word;\nuse crate::fmt::Debuggable;\nuse crate::interrupt;\nuse crate::rcc::RccPeripheral;\n\n/// Timer channel.\n#[derive(Clone, Copy)]\npub enum Channel {\n    /// Channel 1.\n    Ch1,\n    /// Channel 2.\n    Ch2,\n    /// Channel 3.\n    Ch3,\n    /// Channel 4.\n    Ch4,\n}\n\nimpl Channel {\n    /// Get the channel index (0..3)\n    pub fn index(&self) -> usize {\n        match self {\n            Channel::Ch1 => 0,\n            Channel::Ch2 => 1,\n            Channel::Ch3 => 2,\n            Channel::Ch4 => 3,\n        }\n    }\n}\n\n/// Channel 1 marker type.\npub enum Ch1 {}\n/// Channel 2 marker type.\npub enum Ch2 {}\n/// Channel 3 marker type.\npub enum Ch3 {}\n/// Channel 4 marker type.\npub enum Ch4 {}\n\n/// Timer channel trait.\n#[allow(private_bounds)]\npub trait TimerChannel: SealedTimerChannel {\n    /// The runtime channel.\n    const CHANNEL: Channel;\n}\n\ntrait SealedTimerChannel {}\n\nimpl TimerChannel for Ch1 {\n    const CHANNEL: Channel = Channel::Ch1;\n}\n\nimpl TimerChannel for Ch2 {\n    const CHANNEL: Channel = Channel::Ch2;\n}\n\nimpl TimerChannel for Ch3 {\n    const CHANNEL: Channel = Channel::Ch3;\n}\n\nimpl TimerChannel for Ch4 {\n    const CHANNEL: Channel = Channel::Ch4;\n}\n\nimpl SealedTimerChannel for Ch1 {}\nimpl SealedTimerChannel for Ch2 {}\nimpl SealedTimerChannel for Ch3 {}\nimpl SealedTimerChannel for Ch4 {}\n\n/// Timer break input.\n#[derive(Clone, Copy)]\npub enum BkIn {\n    /// Break input 1.\n    BkIn1,\n    /// Break input 2.\n    BkIn2,\n}\n\nimpl BkIn {\n    /// Get the channel index (0..3)\n    pub fn index(&self) -> usize {\n        match self {\n            BkIn::BkIn1 => 0,\n            BkIn::BkIn2 => 1,\n        }\n    }\n}\n\n/// Break input 1 marker type.\npub enum BkIn1 {}\n/// Break input 2 marker type.\npub enum BkIn2 {}\n\n/// Timer channel trait.\n#[allow(private_bounds)]\npub trait BreakInput: SealedBreakInput {\n    /// The runtim timer channel.\n    const INPUT: BkIn;\n}\n\ntrait SealedBreakInput {}\n\nimpl BreakInput for BkIn1 {\n    const INPUT: BkIn = BkIn::BkIn1;\n}\n\nimpl BreakInput for BkIn2 {\n    const INPUT: BkIn = BkIn::BkIn2;\n}\n\nimpl SealedBreakInput for BkIn1 {}\nimpl SealedBreakInput for BkIn2 {}\n\n/// Amount of bits of a timer.\n#[derive(Clone, Copy, PartialEq, Eq, Debug)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub enum TimerBits {\n    /// 16 bits.\n    Bits16,\n    /// 32 bits.\n    #[cfg(not(stm32l0))]\n    Bits32,\n}\n\nstruct State {\n    up_waker: AtomicWaker,\n    cc_waker: [AtomicWaker; 4],\n}\n\nimpl State {\n    const fn new() -> Self {\n        Self {\n            up_waker: AtomicWaker::new(),\n            cc_waker: [const { AtomicWaker::new() }; 4],\n        }\n    }\n}\n\ntrait SealedInstance: RccPeripheral + PeripheralType {\n    /// Async state for this timer\n    fn state() -> &'static State;\n}\n\n/// Core timer instance.\n#[allow(private_bounds)]\npub trait CoreInstance: SealedInstance + 'static {\n    /// Update Interrupt for this timer.\n    type UpdateInterrupt: interrupt::typelevel::Interrupt;\n\n    /// Amount of bits this timer has.\n    type Word: Word\n        + TryInto<u16, Error: Debuggable>\n        + From<u16>\n        + TryFrom<u32, Error: Debuggable>\n        + Into<u32>\n        + TryFrom<u64, Error: Debuggable>;\n\n    /// Registers for this timer.\n    ///\n    /// This is a raw pointer to the register block. The actual register block layout varies depending on the timer type.\n    fn regs() -> *mut ();\n}\n/// Cut-down basic timer instance.\npub trait BasicNoCr2Instance: CoreInstance {}\n/// Basic timer instance.\npub trait BasicInstance: BasicNoCr2Instance {}\n\n/// General-purpose 16-bit timer with 1 channel instance.\npub trait GeneralInstance1Channel: CoreInstance {\n    /// Capture compare interrupt for this timer.\n    type CaptureCompareInterrupt: interrupt::typelevel::Interrupt;\n}\n\n/// General-purpose 16-bit timer with 2 channels instance.\npub trait GeneralInstance2Channel: GeneralInstance1Channel {\n    /// Trigger event interrupt for this timer.\n    type TriggerInterrupt: interrupt::typelevel::Interrupt;\n}\n\n// This trait add *extra* methods to GeneralInstance4Channel,\n// that GeneralInstance4Channel doesn't use, but the \"AdvancedInstance\"s need.\n// And it's a private trait, so it's content won't leak to outer namespace.\n//\n// If you want to add a new method to it, please leave a detail comment to explain it.\ntrait General4ChBlankSealed {\n    // SimplePwm<'d, T> is implemented for T: GeneralInstance4Channel\n    // Advanced timers implement this trait, but the output needs to be\n    // enabled explicitly.\n    // To support general-purpose and advanced timers, this function is added\n    // here defaulting to noop and overwritten for advanced timers.\n    //\n    // Enable timer outputs.\n    fn enable_outputs(&self) {}\n}\n\n/// General-purpose 16-bit timer with 4 channels instance.\n#[allow(private_bounds)]\npub trait GeneralInstance4Channel: BasicInstance + GeneralInstance2Channel + General4ChBlankSealed {}\n\n/// General-purpose 32-bit timer with 4 channels instance.\npub trait GeneralInstance32bit4Channel: GeneralInstance4Channel {}\n\n/// Advanced 16-bit timer with 1 channel instance.\npub trait AdvancedInstance1Channel: BasicNoCr2Instance + GeneralInstance1Channel {\n    /// Communication interrupt for this timer.\n    type CommunicationInterrupt: interrupt::typelevel::Interrupt;\n    /// Break input interrupt for this timer.\n    type BreakInputInterrupt: interrupt::typelevel::Interrupt;\n}\n/// Advanced 16-bit timer with 2 channels instance.\n\npub trait AdvancedInstance2Channel: BasicInstance + GeneralInstance2Channel + AdvancedInstance1Channel {}\n\n/// Advanced 16-bit timer with 4 channels instance.\npub trait AdvancedInstance4Channel: AdvancedInstance2Channel + GeneralInstance4Channel {}\n\npin_trait!(TimerPin, GeneralInstance4Channel, TimerChannel, @A);\npin_trait!(ExternalTriggerPin, GeneralInstance4Channel, @A);\n\npin_trait!(TimerComplementaryPin, AdvancedInstance4Channel, TimerChannel, @A);\n\npin_trait!(BreakInputPin, AdvancedInstance4Channel, BreakInput, @A);\n\npin_trait!(BreakInputComparator1Pin, AdvancedInstance4Channel, BreakInput, @A);\npin_trait!(BreakInputComparator2Pin, AdvancedInstance4Channel, BreakInput, @A);\n\n// Update Event trigger DMA for every timer\ndma_trait!(UpDma, BasicInstance);\n\ndma_trait!(Dma, GeneralInstance4Channel, TimerChannel);\n\n#[allow(unused)]\nmacro_rules! impl_core_timer {\n    ($inst:ident, $bits:ident) => {\n        impl SealedInstance for crate::peripherals::$inst {\n            fn state() -> &'static State {\n                static STATE: State = State::new();\n                &STATE\n            }\n        }\n\n        impl CoreInstance for crate::peripherals::$inst {\n            type UpdateInterrupt = crate::_generated::peripheral_interrupts::$inst::UP;\n            type Word = $bits;\n\n            fn regs() -> *mut () {\n                crate::pac::$inst.as_ptr()\n            }\n        }\n    };\n}\n\n#[allow(unused)]\nmacro_rules! impl_general_1ch {\n    ($inst:ident) => {\n        impl GeneralInstance1Channel for crate::peripherals::$inst {\n            type CaptureCompareInterrupt = crate::_generated::peripheral_interrupts::$inst::CC;\n        }\n    };\n}\n\n#[allow(unused)]\nmacro_rules! impl_general_2ch {\n    ($inst:ident) => {\n        impl GeneralInstance2Channel for crate::peripherals::$inst {\n            type TriggerInterrupt = crate::_generated::peripheral_interrupts::$inst::TRG;\n        }\n    };\n}\n\n#[allow(unused)]\nmacro_rules! impl_advanced_1ch {\n    ($inst:ident) => {\n        impl AdvancedInstance1Channel for crate::peripherals::$inst {\n            type CommunicationInterrupt = crate::_generated::peripheral_interrupts::$inst::COM;\n            type BreakInputInterrupt = crate::_generated::peripheral_interrupts::$inst::BRK;\n        }\n    };\n}\n\n// This macro only apply to \"AdvancedInstance(s)\",\n// not \"GeneralInstance4Channel\" itself.\n#[allow(unused)]\nmacro_rules! impl_general_4ch_blank_sealed {\n    ($inst:ident) => {\n        impl General4ChBlankSealed for crate::peripherals::$inst {\n            fn enable_outputs(&self) {\n                unsafe { crate::pac::timer::Tim1chCmp::from_ptr(Self::regs()) }\n                    .bdtr()\n                    .modify(|w| w.set_moe(true));\n            }\n        }\n    };\n}\n\nforeach_interrupt! {\n    ($inst:ident, timer, TIM_BASIC, UP, $irq:ident) => {\n        impl_core_timer!($inst, u16);\n        impl BasicNoCr2Instance for crate::peripherals::$inst {}\n        impl BasicInstance for crate::peripherals::$inst {}\n    };\n\n    ($inst:ident, timer, TIM_1CH, UP, $irq:ident) => {\n        impl_core_timer!($inst, u16);\n        impl BasicNoCr2Instance for crate::peripherals::$inst {}\n        impl BasicInstance for crate::peripherals::$inst {}\n        impl_general_1ch!($inst);\n        impl_general_2ch!($inst);\n        impl GeneralInstance4Channel for crate::peripherals::$inst {}\n        impl General4ChBlankSealed for crate::peripherals::$inst {}\n    };\n\n    ($inst:ident, timer, TIM_2CH, UP, $irq:ident) => {\n        impl_core_timer!($inst, u16);\n        impl BasicNoCr2Instance for crate::peripherals::$inst {}\n        impl BasicInstance for crate::peripherals::$inst {}\n        impl_general_1ch!($inst);\n        impl_general_2ch!($inst);\n        impl GeneralInstance4Channel for crate::peripherals::$inst {}\n        impl General4ChBlankSealed for crate::peripherals::$inst {}\n    };\n\n    ($inst:ident, timer, TIM_GP16, UP, $irq:ident) => {\n        impl_core_timer!($inst, u16);\n        impl BasicNoCr2Instance for crate::peripherals::$inst {}\n        impl BasicInstance for crate::peripherals::$inst {}\n        impl_general_1ch!($inst);\n        impl_general_2ch!($inst);\n        impl GeneralInstance4Channel for crate::peripherals::$inst {}\n        impl General4ChBlankSealed for crate::peripherals::$inst {}\n    };\n\n    ($inst:ident, timer, TIM_GP32, UP, $irq:ident) => {\n        impl_core_timer!($inst, u32);\n        impl BasicNoCr2Instance for crate::peripherals::$inst {}\n        impl BasicInstance for crate::peripherals::$inst {}\n        impl_general_1ch!($inst);\n        impl_general_2ch!($inst);\n        impl GeneralInstance4Channel for crate::peripherals::$inst {}\n        impl GeneralInstance32bit4Channel for crate::peripherals::$inst {}\n        impl General4ChBlankSealed for crate::peripherals::$inst {}\n    };\n\n    ($inst:ident, timer, TIM_1CH_CMP, UP, $irq:ident) => {\n        impl_core_timer!($inst, u16);\n        impl BasicNoCr2Instance for crate::peripherals::$inst {}\n        impl BasicInstance for crate::peripherals::$inst {}\n        impl_general_1ch!($inst);\n        impl_general_2ch!($inst);\n        impl GeneralInstance4Channel for crate::peripherals::$inst {}\n        impl_general_4ch_blank_sealed!($inst);\n        impl_advanced_1ch!($inst);\n        impl AdvancedInstance2Channel for crate::peripherals::$inst {}\n        impl AdvancedInstance4Channel for crate::peripherals::$inst {}\n    };\n\n    ($inst:ident, timer, TIM_2CH_CMP, UP, $irq:ident) => {\n        impl_core_timer!($inst, u16);\n        impl BasicNoCr2Instance for crate::peripherals::$inst {}\n        impl BasicInstance for crate::peripherals::$inst {}\n        impl_general_1ch!($inst);\n        impl_general_2ch!($inst);\n        impl GeneralInstance4Channel for crate::peripherals::$inst {}\n        impl_general_4ch_blank_sealed!($inst);\n        impl_advanced_1ch!($inst);\n        impl AdvancedInstance2Channel for crate::peripherals::$inst {}\n        impl AdvancedInstance4Channel for crate::peripherals::$inst {}\n    };\n\n    ($inst:ident, timer, TIM_ADV, UP, $irq:ident) => {\n        impl_core_timer!($inst, u16);\n        impl BasicNoCr2Instance for crate::peripherals::$inst {}\n        impl BasicInstance for crate::peripherals::$inst {}\n        impl_general_1ch!($inst);\n        impl_general_2ch!($inst);\n        impl GeneralInstance4Channel for crate::peripherals::$inst {}\n        impl_general_4ch_blank_sealed!($inst);\n        impl_advanced_1ch!($inst);\n        impl AdvancedInstance2Channel for crate::peripherals::$inst {}\n        impl AdvancedInstance4Channel for crate::peripherals::$inst {}\n    };\n}\n\n/// Update interrupt handler.\npub struct UpdateInterruptHandler<T: CoreInstance> {\n    _phantom: PhantomData<T>,\n}\n\nimpl<T: CoreInstance> interrupt::typelevel::Handler<T::UpdateInterrupt> for UpdateInterruptHandler<T> {\n    unsafe fn on_interrupt() {\n        let regs = crate::pac::timer::TimCore::from_ptr(T::regs());\n\n        // Read TIM interrupt flags.\n        let sr = regs.sr().read();\n\n        // Mask relevant interrupts (UIE).\n        let bits = sr.0 & 0x00000001;\n\n        // Mask all the channels that fired.\n        regs.dier().modify(|w| w.0 &= !bits);\n\n        // Wake the tasks\n        if sr.uif() {\n            T::state().up_waker.wake();\n        }\n    }\n}\n\n/// Capture/Compare interrupt handler.\npub struct CaptureCompareInterruptHandler<T: GeneralInstance1Channel> {\n    _phantom: PhantomData<T>,\n}\n\nimpl<T: GeneralInstance1Channel> interrupt::typelevel::Handler<T::CaptureCompareInterrupt>\n    for CaptureCompareInterruptHandler<T>\n{\n    unsafe fn on_interrupt() {\n        let regs = crate::pac::timer::TimGp16::from_ptr(T::regs());\n\n        // Read TIM interrupt flags.\n        let sr = regs.sr().read();\n\n        // Mask relevant interrupts (CCIE).\n        let bits = sr.0 & 0x0000001E;\n\n        // Mask all the channels that fired.\n        regs.dier().modify(|w| w.0 &= !bits);\n\n        // Wake the tasks\n        for ch in 0..4 {\n            if sr.ccif(ch) {\n                T::state().cc_waker[ch].wake();\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "embassy-stm32/src/timer/one_pulse.rs",
    "content": "//! One pulse mode driver.\n\nuse core::future::Future;\nuse core::marker::PhantomData;\nuse core::mem::ManuallyDrop;\nuse core::pin::Pin;\nuse core::task::{Context, Poll};\n\nuse super::low_level::{\n    CountingMode, FilterValue, InputCaptureMode, InputTISelection, SlaveMode, Timer, TriggerSource as Ts,\n};\nuse super::{CaptureCompareInterruptHandler, Channel, ExternalTriggerPin, GeneralInstance4Channel, TimerPin};\npub use super::{Ch1, Ch2};\nuse crate::Peri;\nuse crate::gpio::{AfType, Flex, Pull};\nuse crate::interrupt::typelevel::{Binding, Interrupt};\nuse crate::pac::timer::vals::Etp;\nuse crate::time::Hertz;\nuse crate::timer::TimerChannel;\n\n/// External input marker type.\npub enum Ext {}\n\n/// External trigger pin trigger polarity.\n#[derive(Clone, Copy)]\npub enum ExternalTriggerPolarity {\n    /// Rising edge only.\n    Rising,\n    /// Falling edge only.\n    Falling,\n}\n\nimpl From<ExternalTriggerPolarity> for Etp {\n    fn from(mode: ExternalTriggerPolarity) -> Self {\n        match mode {\n            ExternalTriggerPolarity::Rising => 0.into(),\n            ExternalTriggerPolarity::Falling => 1.into(),\n        }\n    }\n}\n\n/// Trigger pin wrapper.\n///\n/// This wraps a pin to make it usable as a timer trigger.\npub struct TriggerPin<'d, T, C> {\n    #[allow(unused)]\n    pin: Flex<'d>,\n    phantom: PhantomData<(T, C)>,\n}\n\ntrait SealedTriggerSource {}\n\n/// Marker trait for a trigger source.\n#[expect(private_bounds)]\npub trait TriggerSource: SealedTriggerSource {}\n\nimpl TriggerSource for Ch1 {}\nimpl TriggerSource for Ch2 {}\nimpl TriggerSource for Ext {}\n\nimpl SealedTriggerSource for Ch1 {}\nimpl SealedTriggerSource for Ch2 {}\nimpl SealedTriggerSource for Ext {}\n\nimpl<'d, T: GeneralInstance4Channel, C: TriggerSource + TimerChannel> TriggerPin<'d, T, C> {\n    /// Create a new Channel trigger pin instance.\n    pub fn new<#[cfg(afio)] A>(pin: Peri<'d, if_afio!(impl TimerPin<T, C, A>)>, pull: Pull) -> Self {\n        TriggerPin {\n            pin: new_pin!(pin, AfType::input(pull)).unwrap(),\n            phantom: PhantomData,\n        }\n    }\n}\n\nimpl<'d, T: GeneralInstance4Channel> TriggerPin<'d, T, Ext> {\n    /// Create a new external trigger pin instance.\n    pub fn new_external<#[cfg(afio)] A>(pin: Peri<'d, if_afio!(impl ExternalTriggerPin<T, A>)>, pull: Pull) -> Self {\n        TriggerPin {\n            pin: new_pin!(pin, AfType::input(pull)).unwrap(),\n            phantom: PhantomData,\n        }\n    }\n}\n\n/// One pulse driver.\n///\n/// Generates a pulse after a trigger and some configurable delay.\npub struct OnePulse<'d, T: GeneralInstance4Channel> {\n    inner: Timer<'d, T>,\n    _pin: Flex<'d>,\n}\n\nimpl<'d, T: GeneralInstance4Channel> OnePulse<'d, T> {\n    /// Create a new one pulse driver.\n    ///\n    /// The pulse is triggered by a channel 1 input pin on both rising and\n    /// falling edges. Channel 1 will unusable as an output.\n    #[allow(unused)]\n    pub fn new_ch1_edge_detect(\n        tim: Peri<'d, T>,\n        pin: TriggerPin<'d, T, Ch1>,\n        _irq: impl Binding<T::CaptureCompareInterrupt, CaptureCompareInterruptHandler<T>> + 'd,\n        freq: Hertz,\n        pulse_end: u32,\n        counting_mode: CountingMode,\n    ) -> Self {\n        let mut this = Self {\n            inner: Timer::new(tim),\n            _pin: pin.pin,\n        };\n\n        this.inner.set_trigger_source(Ts::TI1F_ED);\n        this.inner\n            .set_input_ti_selection(Channel::Ch1, InputTISelection::Normal);\n        this.inner\n            .set_input_capture_filter(Channel::Ch1, FilterValue::NO_FILTER);\n        this.new_inner(freq, pulse_end, counting_mode);\n\n        this\n    }\n\n    /// Create a new one pulse driver.\n    ///\n    /// The pulse is triggered by a channel 1 input pin. Channel 1 will unusable\n    /// as an output.\n    pub fn new_ch1(\n        tim: Peri<'d, T>,\n        _pin: TriggerPin<'d, T, Ch1>,\n        _irq: impl Binding<T::CaptureCompareInterrupt, CaptureCompareInterruptHandler<T>> + 'd,\n        freq: Hertz,\n        pulse_end: u32,\n        counting_mode: CountingMode,\n        capture_mode: InputCaptureMode,\n    ) -> Self {\n        let mut this = Self {\n            inner: Timer::new(tim),\n            _pin: _pin.pin,\n        };\n\n        this.inner.set_trigger_source(Ts::TI1FP1);\n        this.inner\n            .set_input_ti_selection(Channel::Ch1, InputTISelection::Normal);\n        this.inner\n            .set_input_capture_filter(Channel::Ch1, FilterValue::NO_FILTER);\n        this.inner.set_input_capture_mode(Channel::Ch1, capture_mode);\n        this.new_inner(freq, pulse_end, counting_mode);\n\n        this\n    }\n\n    /// Create a new one pulse driver.\n    ///\n    /// The pulse is triggered by a channel 2 input pin. Channel 2 will unusable\n    /// as an output.\n    pub fn new_ch2(\n        tim: Peri<'d, T>,\n        _pin: TriggerPin<'d, T, Ch2>,\n        _irq: impl Binding<T::CaptureCompareInterrupt, CaptureCompareInterruptHandler<T>> + 'd,\n        freq: Hertz,\n        pulse_end: u32,\n        counting_mode: CountingMode,\n        capture_mode: InputCaptureMode,\n    ) -> Self {\n        let mut this = Self {\n            inner: Timer::new(tim),\n            _pin: _pin.pin,\n        };\n\n        this.inner.set_trigger_source(Ts::TI2FP2);\n        this.inner\n            .set_input_ti_selection(Channel::Ch2, InputTISelection::Normal);\n        this.inner\n            .set_input_capture_filter(Channel::Ch2, FilterValue::NO_FILTER);\n        this.inner.set_input_capture_mode(Channel::Ch2, capture_mode);\n        this.new_inner(freq, pulse_end, counting_mode);\n\n        this\n    }\n\n    /// Create a new one pulse driver.\n    ///\n    /// The pulse is triggered by a external trigger input pin.\n    pub fn new_ext(\n        tim: Peri<'d, T>,\n        _pin: TriggerPin<'d, T, Ext>,\n        _irq: impl Binding<T::CaptureCompareInterrupt, CaptureCompareInterruptHandler<T>> + 'd,\n        freq: Hertz,\n        pulse_end: u32,\n        counting_mode: CountingMode,\n        polarity: ExternalTriggerPolarity,\n    ) -> Self {\n        let mut this = Self {\n            inner: Timer::new(tim),\n            _pin: _pin.pin,\n        };\n\n        this.inner.regs_gp16().smcr().modify(|r| {\n            r.set_etp(polarity.into());\n            // No pre-scaling\n            r.set_etps(0.into());\n            // No filtering\n            r.set_etf(FilterValue::NO_FILTER);\n        });\n        this.inner.set_trigger_source(Ts::ETRF);\n        this.new_inner(freq, pulse_end, counting_mode);\n\n        this\n    }\n\n    fn new_inner(&mut self, freq: Hertz, pulse_end: u32, counting_mode: CountingMode) {\n        self.inner.set_counting_mode(counting_mode);\n        self.inner.set_tick_freq(freq);\n        self.inner.set_max_compare_value(unwrap!(pulse_end.try_into()));\n        self.inner.regs_core().cr1().modify(|r| r.set_opm(true));\n        // Required for advanced timers, see GeneralInstance4Channel for details\n        self.inner.enable_outputs();\n        self.inner.set_slave_mode(SlaveMode::TRIGGER_MODE);\n\n        T::CaptureCompareInterrupt::unpend();\n        unsafe { T::CaptureCompareInterrupt::enable() };\n    }\n\n    /// Get the end of the pulse in ticks from the trigger.\n    pub fn pulse_end(&self) -> u32 {\n        let max: u32 = self.inner.get_max_compare_value().into();\n        assert!(max < u32::MAX);\n        max + 1\n    }\n\n    /// Set the end of the pulse in ticks from the trigger.\n    pub fn set_pulse_end(&mut self, ticks: u32) {\n        self.inner.set_max_compare_value(unwrap!(ticks.try_into()))\n    }\n\n    /// Reset the timer on each trigger\n    #[cfg(not(stm32l0))]\n    pub fn set_reset_on_trigger(&mut self, reset: bool) {\n        let slave_mode = if reset {\n            SlaveMode::COMBINED_RESET_TRIGGER\n        } else {\n            SlaveMode::TRIGGER_MODE\n        };\n        self.inner.set_slave_mode(slave_mode);\n    }\n\n    /// Get a single channel\n    ///\n    /// If you need to use multiple channels, use [`Self::split`].\n    pub fn channel(&mut self, channel: Channel) -> OnePulseChannel<'_, T> {\n        OnePulseChannel {\n            inner: unsafe { self.inner.clone_unchecked() },\n            channel,\n        }\n    }\n\n    /// Channel 1\n    ///\n    /// This is just a convenience wrapper around [`Self::channel`].\n    ///\n    /// If you need to use multiple channels, use [`Self::split`].\n    pub fn ch1(&mut self) -> OnePulseChannel<'_, T> {\n        self.channel(Channel::Ch1)\n    }\n\n    /// Channel 2\n    ///\n    /// This is just a convenience wrapper around [`Self::channel`].\n    ///\n    /// If you need to use multiple channels, use [`Self::split`].\n    pub fn ch2(&mut self) -> OnePulseChannel<'_, T> {\n        self.channel(Channel::Ch2)\n    }\n\n    /// Channel 3\n    ///\n    /// This is just a convenience wrapper around [`Self::channel`].\n    ///\n    /// If you need to use multiple channels, use [`Self::split`].\n    pub fn ch3(&mut self) -> OnePulseChannel<'_, T> {\n        self.channel(Channel::Ch3)\n    }\n\n    /// Channel 4\n    ///\n    /// This is just a convenience wrapper around [`Self::channel`].\n    ///\n    /// If you need to use multiple channels, use [`Self::split`].\n    pub fn ch4(&mut self) -> OnePulseChannel<'_, T> {\n        self.channel(Channel::Ch4)\n    }\n\n    /// Splits a [`OnePulse`] into four output channels.\n    // TODO: I hate the name \"split\"\n    pub fn split(self) -> OnePulseChannels<'static, T>\n    where\n        // must be static because the timer will never be dropped/disabled\n        'd: 'static,\n    {\n        // without this, the timer would be disabled at the end of this function\n        let timer = ManuallyDrop::new(self.inner);\n\n        let ch = |channel| OnePulseChannel {\n            inner: unsafe { timer.clone_unchecked() },\n            channel,\n        };\n\n        OnePulseChannels {\n            ch1: ch(Channel::Ch1),\n            ch2: ch(Channel::Ch2),\n            ch3: ch(Channel::Ch3),\n            ch4: ch(Channel::Ch4),\n        }\n    }\n}\n\n/// A group of four [`OnePulseChannel`]s, obtained from [`OnePulse::split`].\npub struct OnePulseChannels<'d, T: GeneralInstance4Channel> {\n    /// Channel 1\n    pub ch1: OnePulseChannel<'d, T>,\n    /// Channel 2\n    pub ch2: OnePulseChannel<'d, T>,\n    /// Channel 3\n    pub ch3: OnePulseChannel<'d, T>,\n    /// Channel 4\n    pub ch4: OnePulseChannel<'d, T>,\n}\n\n/// A single channel of a one pulse-configured timer, obtained from\n/// [`OnePulse::split`],[`OnePulse::channel`], [`OnePulse::ch1`], etc.\n///\n/// It is not possible to change the pulse end tick because the end tick\n/// configuration is shared with all four channels.\npub struct OnePulseChannel<'d, T: GeneralInstance4Channel> {\n    inner: ManuallyDrop<Timer<'d, T>>,\n    channel: Channel,\n}\n\nimpl<'d, T: GeneralInstance4Channel> OnePulseChannel<'d, T> {\n    /// Get the end of the pulse in ticks from the trigger.\n    pub fn pulse_end(&self) -> u32 {\n        let max: u32 = self.inner.get_max_compare_value().into();\n        assert!(max < u32::MAX);\n        max + 1\n    }\n\n    /// Get the width of the pulse in ticks.\n    pub fn pulse_width(&mut self) -> u32 {\n        self.pulse_end().saturating_sub(self.pulse_delay())\n    }\n\n    /// Get the start of the pulse in ticks from the trigger.\n    pub fn pulse_delay(&mut self) -> u32 {\n        self.inner.get_compare_value(self.channel).into()\n    }\n\n    /// Set the start of the pulse in ticks from the trigger.\n    pub fn set_pulse_delay(&mut self, delay: u32) {\n        assert!(delay <= self.pulse_end());\n        self.inner.set_compare_value(self.channel, unwrap!(delay.try_into()));\n    }\n\n    /// Set the pulse width in ticks.\n    pub fn set_pulse_width(&mut self, width: u32) {\n        assert!(width <= self.pulse_end());\n        self.set_pulse_delay(self.pulse_end() - width);\n    }\n\n    /// Waits until the trigger and following delay has passed.\n    pub async fn wait_for_pulse_start(&mut self) {\n        self.inner.enable_input_interrupt(self.channel, true);\n\n        OnePulseFuture::<T> {\n            channel: self.channel,\n            phantom: PhantomData,\n        }\n        .await\n    }\n}\n\n#[must_use = \"futures do nothing unless you `.await` or poll them\"]\nstruct OnePulseFuture<T: GeneralInstance4Channel> {\n    channel: Channel,\n    phantom: PhantomData<T>,\n}\n\nimpl<'d, T: GeneralInstance4Channel> Drop for OnePulseFuture<T> {\n    fn drop(&mut self) {\n        critical_section::with(|_| {\n            let regs = unsafe { crate::pac::timer::TimGp16::from_ptr(T::regs()) };\n\n            // disable interrupt enable\n            regs.dier().modify(|w| w.set_ccie(self.channel.index(), false));\n        });\n    }\n}\n\nimpl<'d, T: GeneralInstance4Channel> Future for OnePulseFuture<T> {\n    type Output = ();\n\n    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {\n        T::state().cc_waker[self.channel.index()].register(cx.waker());\n\n        let regs = unsafe { crate::pac::timer::TimGp16::from_ptr(T::regs()) };\n\n        let dier = regs.dier().read();\n        if !dier.ccie(self.channel.index()) {\n            Poll::Ready(())\n        } else {\n            Poll::Pending\n        }\n    }\n}\n"
  },
  {
    "path": "embassy-stm32/src/timer/pwm_input.rs",
    "content": "//! PWM Input driver.\n\nuse core::marker::PhantomData;\nuse core::pin::Pin;\nuse core::task::{Context, Poll};\n\nuse super::low_level::{CountingMode, InputCaptureMode, InputTISelection, SlaveMode, Timer, TriggerSource};\nuse super::{CaptureCompareInterruptHandler, Ch1, Ch2, Channel, GeneralInstance4Channel, TimerPin};\nuse crate::Peri;\nuse crate::gpio::{AfType, Pull};\nuse crate::interrupt::typelevel::{Binding, Interrupt};\nuse crate::time::Hertz;\n\n/// PWM Input driver.\n///\n/// Only works with CH1 or CH2\n/// Note: Not all timer peripherals are supported\n/// Double check your chips reference manual\npub struct PwmInput<'d, T: GeneralInstance4Channel> {\n    channel: Channel,\n    inner: Timer<'d, T>,\n}\n\nimpl<'d, T: GeneralInstance4Channel> PwmInput<'d, T> {\n    /// Create a new PWM input driver.\n    pub fn new_ch1<#[cfg(afio)] A>(\n        tim: Peri<'d, T>,\n        pin: Peri<'d, if_afio!(impl TimerPin<T, Ch1, A>)>,\n        _irq: impl Binding<T::CaptureCompareInterrupt, CaptureCompareInterruptHandler<T>> + 'd,\n        pull: Pull,\n        freq: Hertz,\n    ) -> Self {\n        set_as_af!(pin, AfType::input(pull));\n\n        Self::new_inner(tim, freq, Channel::Ch1, Channel::Ch2)\n    }\n\n    /// Create a new PWM input driver.\n    pub fn new_ch2<#[cfg(afio)] A>(\n        tim: Peri<'d, T>,\n        pin: Peri<'d, if_afio!(impl TimerPin<T, Ch2, A>)>,\n        _irq: impl Binding<T::CaptureCompareInterrupt, CaptureCompareInterruptHandler<T>> + 'd,\n        pull: Pull,\n        freq: Hertz,\n    ) -> Self {\n        set_as_af!(pin, AfType::input(pull));\n\n        Self::new_inner(tim, freq, Channel::Ch2, Channel::Ch1)\n    }\n\n    fn new_inner(tim: Peri<'d, T>, freq: Hertz, ch1: Channel, ch2: Channel) -> Self {\n        let mut inner = Timer::new(tim);\n\n        inner.set_counting_mode(CountingMode::EdgeAlignedUp);\n        inner.set_tick_freq(freq);\n        inner.enable_outputs(); // Required for advanced timers, see GeneralInstance4Channel for details\n        inner.generate_update_event();\n        inner.start();\n\n        // Configuration steps from ST RM0390 (STM32F446) chapter 17.3.6\n        // or ST RM0008 (STM32F103) chapter 15.3.6 Input capture mode\n        // or ST RM0440 (STM32G4) chapter 30.4.8 PWM input mode\n        inner.set_input_ti_selection(ch1, InputTISelection::Normal);\n        inner.set_input_capture_mode(ch1, InputCaptureMode::Rising);\n\n        inner.set_input_ti_selection(ch2, InputTISelection::Alternate);\n        inner.set_input_capture_mode(ch2, InputCaptureMode::Falling);\n\n        inner.set_trigger_source(match ch1 {\n            Channel::Ch1 => TriggerSource::TI1FP1,\n            Channel::Ch2 => TriggerSource::TI2FP2,\n            _ => panic!(\"Invalid channel for PWM input\"),\n        });\n\n        inner.set_slave_mode(SlaveMode::RESET_MODE);\n\n        // Must call the `enable` function after\n\n        // enable NVIC interrupt\n        T::CaptureCompareInterrupt::unpend();\n        unsafe { T::CaptureCompareInterrupt::enable() };\n\n        Self { channel: ch1, inner }\n    }\n\n    /// Enable the given channel.\n    pub fn enable(&mut self) {\n        self.inner.enable_channel(Channel::Ch1, true);\n        self.inner.enable_channel(Channel::Ch2, true);\n    }\n\n    /// Disable the given channel.\n    pub fn disable(&mut self) {\n        self.inner.enable_channel(Channel::Ch1, false);\n        self.inner.enable_channel(Channel::Ch2, false);\n    }\n\n    /// Check whether given channel is enabled\n    pub fn is_enabled(&self) -> bool {\n        self.inner.get_channel_enable_state(Channel::Ch1)\n    }\n\n    /// Get the period tick count\n    pub fn get_period_ticks(&self) -> u32 {\n        self.inner.get_capture_value(self.channel).into()\n    }\n\n    /// Get the pulse width tick count\n    pub fn get_width_ticks(&self) -> u32 {\n        self.inner\n            .get_capture_value(match self.channel {\n                Channel::Ch1 => Channel::Ch2,\n                Channel::Ch2 => Channel::Ch1,\n                _ => panic!(\"Invalid channel for PWM input\"),\n            })\n            .into()\n    }\n\n    /// Get the duty cycle in 100%\n    pub fn get_duty_cycle(&self) -> f32 {\n        let period = self.get_period_ticks();\n        if period == 0 {\n            return 0.;\n        }\n        100. * (self.get_width_ticks() as f32) / (period as f32)\n    }\n\n    fn new_future(&self, channel: Channel) -> PwmInputFuture<T> {\n        self.inner.enable_channel(channel, true);\n        self.inner.enable_input_interrupt(channel, true);\n\n        PwmInputFuture {\n            channel,\n            phantom: PhantomData,\n        }\n    }\n\n    /// Asynchronously wait until the pin sees a rising edge (period measurement).\n    pub async fn wait_for_period(&self) -> u32 {\n        self.new_future(self.channel.into()).await\n    }\n\n    /// Asynchronously wait until the pin sees a falling edge (width measurement).\n    pub async fn wait_for_width(&self) -> u32 {\n        self.new_future(\n            match self.channel {\n                Channel::Ch1 => Channel::Ch2,\n                Channel::Ch2 => Channel::Ch1,\n                _ => panic!(\"Invalid channel for PWM input\"),\n            }\n            .into(),\n        )\n        .await\n    }\n}\n\n#[must_use = \"futures do nothing unless you `.await` or poll them\"]\nstruct PwmInputFuture<T: GeneralInstance4Channel> {\n    channel: Channel,\n    phantom: PhantomData<T>,\n}\n\nimpl<T: GeneralInstance4Channel> Drop for PwmInputFuture<T> {\n    fn drop(&mut self) {\n        critical_section::with(|_| {\n            let regs = unsafe { crate::pac::timer::TimGp16::from_ptr(T::regs()) };\n\n            // disable interrupt enable\n            regs.dier().modify(|w| w.set_ccie(self.channel.index(), false));\n        });\n    }\n}\n\nimpl<T: GeneralInstance4Channel> Future for PwmInputFuture<T> {\n    type Output = u32;\n\n    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {\n        T::state().cc_waker[self.channel.index()].register(cx.waker());\n\n        let regs = unsafe { crate::pac::timer::TimGp16::from_ptr(T::regs()) };\n\n        let dier = regs.dier().read();\n        if !dier.ccie(self.channel.index()) {\n            let val = regs.ccr(self.channel.index()).read().0;\n            Poll::Ready(val)\n        } else {\n            Poll::Pending\n        }\n    }\n}\n"
  },
  {
    "path": "embassy-stm32/src/timer/qei.rs",
    "content": "//! Quadrature decoder using a timer.\n\nuse stm32_metapac::timer::vals::{self, Sms};\n\nuse super::low_level::Timer;\npub use super::{Ch1, Ch2};\nuse super::{GeneralInstance4Channel, TimerPin};\nuse crate::Peri;\nuse crate::gpio::{AfType, Flex, Pull};\nuse crate::timer::TimerChannel;\n\n/// Qei driver config.\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\n#[derive(Clone, Copy)]\npub struct Config {\n    /// Configures the internal pull up/down resistor for Qei's channel 1 pin.\n    pub ch1_pull: Pull,\n    /// Configures the internal pull up/down resistor for Qei's channel 2 pin.\n    pub ch2_pull: Pull,\n    /// Specifies the encoder mode to use for the Qei peripheral.\n    pub mode: QeiMode,\n    /// Sets the auto-reload value for the counter.\n    pub auto_reload: u16,\n}\n\nimpl Default for Config {\n    /// Arbitrary defaults to preserve backwards compatibility\n    fn default() -> Self {\n        Self {\n            ch1_pull: Pull::None,\n            ch2_pull: Pull::None,\n            mode: QeiMode::Mode3,\n            auto_reload: u16::MAX,\n        }\n    }\n}\n\n/// See STMicro AN4013 for §2.3 for more information\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\n#[derive(Clone, Copy)]\npub enum QeiMode {\n    /// Direct alias for [`Sms::ENCODER_MODE_1`]\n    Mode1,\n    /// Direct alias for [`Sms::ENCODER_MODE_2`]\n    Mode2,\n    /// Direct alias for [`Sms::ENCODER_MODE_3`]\n    Mode3,\n}\n\nimpl From<QeiMode> for Sms {\n    fn from(mode: QeiMode) -> Self {\n        match mode {\n            QeiMode::Mode1 => Sms::ENCODER_MODE_1,\n            QeiMode::Mode2 => Sms::ENCODER_MODE_2,\n            QeiMode::Mode3 => Sms::ENCODER_MODE_3,\n        }\n    }\n}\n\n/// Counting direction\npub enum Direction {\n    /// Counting up.\n    Upcounting,\n    /// Counting down.\n    Downcounting,\n}\n\ntrait SealedQeiChannel: TimerChannel {}\n\n/// Marker trait for a timer channel eligible for use with QEI.\n#[expect(private_bounds)]\npub trait QeiChannel: SealedQeiChannel {}\n\nimpl QeiChannel for Ch1 {}\nimpl QeiChannel for Ch2 {}\n\nimpl SealedQeiChannel for Ch1 {}\nimpl SealedQeiChannel for Ch2 {}\n\n/// Quadrature decoder driver.\npub struct Qei<'d, T: GeneralInstance4Channel> {\n    inner: Timer<'d, T>,\n    _ch1: Flex<'d>,\n    _ch2: Flex<'d>,\n}\n\nimpl<'d, T: GeneralInstance4Channel> Qei<'d, T> {\n    /// Create a new quadrature decoder driver, with a given [`Config`].\n    #[allow(unused)]\n    pub fn new<CH1: QeiChannel, CH2: QeiChannel, #[cfg(afio)] A>(\n        tim: Peri<'d, T>,\n        ch1: Peri<'d, if_afio!(impl TimerPin<T, CH1, A>)>,\n        ch2: Peri<'d, if_afio!(impl TimerPin<T, CH2, A>)>,\n        config: Config,\n    ) -> Self {\n        // Configure the pins to be used for the QEI peripheral.\n        critical_section::with(|_| {\n            ch1.set_low();\n            ch2.set_low();\n        });\n\n        let inner = Timer::new(tim);\n        let r = inner.regs_gp16();\n\n        // Configure TxC1 and TxC2 as captures\n        r.ccmr_input(0).modify(|w| {\n            w.set_ccs(0, vals::CcmrInputCcs::TI4);\n            w.set_ccs(1, vals::CcmrInputCcs::TI4);\n        });\n\n        // enable and configure to capture on rising edge\n        r.ccer().modify(|w| {\n            w.set_cce(0, true);\n            w.set_cce(1, true);\n\n            w.set_ccp(0, false);\n            w.set_ccp(1, false);\n        });\n\n        r.smcr().modify(|w| {\n            w.set_sms(config.mode.into());\n        });\n\n        r.arr().modify(|w| w.set_arr(config.auto_reload));\n        r.cr1().modify(|w| w.set_cen(true));\n\n        Self {\n            inner,\n            _ch1: new_pin!(ch1, AfType::input(config.ch1_pull)).unwrap(),\n            _ch2: new_pin!(ch2, AfType::input(config.ch2_pull)).unwrap(),\n        }\n    }\n\n    /// Get direction.\n    pub fn read_direction(&self) -> Direction {\n        match self.inner.regs_gp16().cr1().read().dir() {\n            vals::Dir::DOWN => Direction::Downcounting,\n            vals::Dir::UP => Direction::Upcounting,\n        }\n    }\n\n    /// Get count.\n    pub fn count(&self) -> u16 {\n        self.inner.regs_gp16().cnt().read().cnt()\n    }\n\n    /// Reset count.\n    pub fn reset(&mut self) {\n        self.inner.regs_gp16().cnt().modify(|w| w.set_cnt(0));\n    }\n}\n"
  },
  {
    "path": "embassy-stm32/src/timer/ringbuffered.rs",
    "content": "//! RingBuffered PWM driver.\n\nuse core::mem::ManuallyDrop;\nuse core::task::Waker;\n\nuse super::low_level::Timer;\nuse super::{Channel, GeneralInstance4Channel};\nuse crate::dma::WritableRingBuffer;\nuse crate::dma::ringbuffer::Error;\nuse crate::dma::word::Word;\n\n/// A PWM channel that uses a DMA ring buffer for continuous waveform generation.\n///\n/// This allows you to continuously update PWM duty cycles via DMA without blocking the CPU.\n/// The ring buffer enables smooth, uninterrupted waveform generation by automatically cycling\n/// through duty cycle values stored in memory.\n///\n/// You can write new duty cycle values to the ring buffer while it's running, enabling\n/// dynamic waveform generation for applications like motor control, LED dimming, or audio output.\n///\n/// # Example\n/// ```ignore\n/// let mut channel = pwm.ch1().into_ring_buffered_channel(dma_ch, &mut buffer, Irqs);\n/// channel.start(); // Start DMA transfer\n/// channel.write(&[100, 200, 300]).ok(); // Update duty cycles\n/// ```\npub struct RingBufferedPwmChannel<'d, T: GeneralInstance4Channel, W: Word + Into<T::Word>> {\n    timer: ManuallyDrop<Timer<'d, T>>,\n    ring_buf: WritableRingBuffer<'d, W>,\n    channel: Channel,\n}\n\nimpl<'d, T: GeneralInstance4Channel, W: Word + Into<T::Word>> RingBufferedPwmChannel<'d, T, W> {\n    pub(crate) fn new(\n        timer: ManuallyDrop<Timer<'d, T>>,\n        channel: Channel,\n        ring_buf: WritableRingBuffer<'d, W>,\n    ) -> Self {\n        Self {\n            timer,\n            ring_buf,\n            channel,\n        }\n    }\n\n    /// Start the ring buffer operation.\n    ///\n    /// You must call this after creating it for it to work.\n    pub fn start(&mut self) {\n        self.ring_buf.start()\n    }\n\n    /// Clear all data in the ring buffer.\n    pub fn clear(&mut self) {\n        self.ring_buf.clear()\n    }\n\n    /// Write elements directly to the raw buffer. This can be used to fill the buffer before starting the DMA transfer.\n    pub fn write_immediate(&mut self, buf: &[W]) -> Result<(usize, usize), Error> {\n        self.ring_buf.write_immediate(buf)\n    }\n\n    /// Write elements from the ring buffer\n    /// Return a tuple of the length written and the length remaining in the buffer\n    pub fn write(&mut self, buf: &[W]) -> Result<(usize, usize), Error> {\n        self.ring_buf.write(buf)\n    }\n\n    /// Write an exact number of elements to the ringbuffer.\n    pub async fn write_exact(&mut self, buffer: &[W]) -> Result<usize, Error> {\n        self.ring_buf.write_exact(buffer).await\n    }\n\n    /// Wait for any ring buffer write error.\n    pub async fn wait_write_error(&mut self) -> Result<usize, Error> {\n        self.ring_buf.wait_write_error().await\n    }\n\n    /// The current length of the ringbuffer\n    pub fn len(&mut self) -> Result<usize, Error> {\n        self.ring_buf.len()\n    }\n\n    /// The capacity of the ringbuffer\n    pub const fn capacity(&self) -> usize {\n        self.ring_buf.capacity()\n    }\n\n    /// Set a waker to be woken when at least one byte is send.\n    pub fn set_waker(&mut self, waker: &Waker) {\n        self.ring_buf.set_waker(waker)\n    }\n\n    /// Request the DMA to reset. The configuration for this channel will not be preserved.\n    ///\n    /// This doesn't immediately stop the transfer, you have to wait until is_running returns false.\n    pub fn request_reset(&mut self) {\n        self.ring_buf.request_reset()\n    }\n\n    /// Request the transfer to pause, keeping the existing configuration for this channel.\n    /// To restart the transfer, call [`start`](Self::start) again.\n    ///\n    /// This doesn't immediately stop the transfer, you have to wait until is_running returns false.\n    pub fn request_pause(&mut self) {\n        self.ring_buf.request_pause()\n    }\n\n    /// Return whether DMA is still running.\n    ///\n    /// If this returns false, it can be because either the transfer finished, or it was requested to stop early with request_stop.\n    pub fn is_running(&mut self) -> bool {\n        self.ring_buf.is_running()\n    }\n\n    /// Stop the DMA transfer and await until the buffer is empty.\n    ///\n    /// This disables the DMA transfer's circular mode so that the transfer stops when all available data has been written.\n    ///\n    /// This is designed to be used with streaming output data such as the I2S/SAI or DAC.\n    pub async fn stop(&mut self) {\n        self.ring_buf.stop().await\n    }\n\n    /// Enable the given channel.\n    pub fn enable(&mut self) {\n        self.timer.enable_channel(self.channel, true);\n    }\n\n    /// Disable the given channel.\n    pub fn disable(&mut self) {\n        self.timer.enable_channel(self.channel, false);\n    }\n\n    /// Check whether given channel is enabled\n    pub fn is_enabled(&self) -> bool {\n        self.timer.get_channel_enable_state(self.channel)\n    }\n\n    /// Get max duty value.\n    ///\n    /// This value depends on the configured frequency and the timer's clock rate from RCC.\n    pub fn max_duty_cycle(&self) -> u16 {\n        let max: u32 = self.timer.get_max_compare_value().into();\n        assert!(max < u16::MAX as u32);\n        max as u16 + 1\n    }\n\n    /// Set the output polarity for a given channel.\n    pub fn set_polarity(&mut self, polarity: super::low_level::OutputPolarity) {\n        self.timer.set_output_polarity(self.channel, polarity);\n    }\n\n    /// Set the output compare mode for a given channel.\n    pub fn set_output_compare_mode(&mut self, mode: super::low_level::OutputCompareMode) {\n        self.timer.set_output_compare_mode(self.channel, mode);\n    }\n}\n"
  },
  {
    "path": "embassy-stm32/src/timer/simple_pwm.rs",
    "content": "//! Simple PWM driver.\n\nuse core::marker::PhantomData;\nuse core::mem::ManuallyDrop;\n\nuse super::low_level::{CountingMode, OutputCompareMode, OutputPolarity, RoundTo, Timer};\nuse super::ringbuffered::RingBufferedPwmChannel;\nuse super::{Ch1, Ch2, Ch3, Ch4, Channel, GeneralInstance4Channel, TimerChannel, TimerPin};\nuse crate::Peri;\nuse crate::dma::word::Word;\n#[cfg(gpio_v2)]\nuse crate::gpio::Pull;\nuse crate::gpio::{AfType, Flex, OutputType, Speed};\nuse crate::pac::timer::vals::Ccds;\nuse crate::time::Hertz;\n\n/// PWM pin wrapper.\n///\n/// This wraps a pin to make it usable with PWM.\npub struct PwmPin<'d, T, C, #[cfg(afio)] A> {\n    #[allow(unused)]\n    pub(crate) pin: Flex<'d>,\n    phantom: PhantomData<if_afio!((T, C, A))>,\n}\n\n/// PWM pin config\n///\n/// This configures the pwm pin settings\n#[derive(Debug, Copy, Clone)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub struct PwmPinConfig {\n    /// PWM Pin output type\n    pub output_type: OutputType,\n    /// PWM Pin speed\n    pub speed: Speed,\n    /// PWM Pin pull type\n    #[cfg(gpio_v2)]\n    pub pull: Pull,\n}\n\nimpl<'d, T: GeneralInstance4Channel, C: TimerChannel, #[cfg(afio)] A> if_afio!(PwmPin<'d, T, C, A>) {\n    /// Create a new PWM pin instance.\n    pub fn new(pin: Peri<'d, if_afio!(impl TimerPin<T, C, A>)>, output_type: OutputType) -> Self {\n        critical_section::with(|_| {\n            pin.set_low();\n            set_as_af!(pin, AfType::output(output_type, Speed::VeryHigh));\n        });\n        PwmPin {\n            pin: Flex::new(pin),\n            phantom: PhantomData,\n        }\n    }\n\n    /// Create a new PWM pin instance with a specific configuration.\n    pub fn new_with_config(pin: Peri<'d, if_afio!(impl TimerPin<T, C, A>)>, pin_config: PwmPinConfig) -> Self {\n        critical_section::with(|_| {\n            pin.set_low();\n            #[cfg(gpio_v1)]\n            set_as_af!(pin, AfType::output(pin_config.output_type, pin_config.speed));\n            #[cfg(gpio_v2)]\n            set_as_af!(\n                pin,\n                AfType::output_pull(pin_config.output_type, pin_config.speed, pin_config.pull)\n            );\n        });\n        PwmPin {\n            pin: Flex::new(pin),\n            phantom: PhantomData,\n        }\n    }\n}\n\n/// A single channel of a pwm, obtained from [`SimplePwm::split`],\n/// [`SimplePwm::channel`], [`SimplePwm::ch1`], etc.\n///\n/// It is not possible to change the pwm frequency because\n/// the frequency configuration is shared with all four channels.\npub struct SimplePwmChannel<'d, T: GeneralInstance4Channel> {\n    timer: ManuallyDrop<Timer<'d, T>>,\n    channel: Channel,\n    _pin: Option<Flex<'d>>,\n}\n\n// TODO: check for RMW races\nimpl<'d, T: GeneralInstance4Channel> SimplePwmChannel<'d, T> {\n    /// Enable the given channel.\n    pub fn enable(&mut self) {\n        self.timer.enable_channel(self.channel, true);\n    }\n\n    /// Disable the given channel.\n    pub fn disable(&mut self) {\n        self.timer.enable_channel(self.channel, false);\n    }\n\n    /// Check whether given channel is enabled\n    pub fn is_enabled(&self) -> bool {\n        self.timer.get_channel_enable_state(self.channel)\n    }\n\n    /// Get the frequency of the PWM channel.\n    pub fn get_frequency(&self) -> Hertz {\n        self.timer.get_frequency()\n    }\n\n    /// Get max duty value.\n    ///\n    /// This value depends on the configured frequency and the timer's clock rate from RCC.\n    pub fn max_duty_cycle(&self) -> u32 {\n        self.timer.get_max_compare_value().into() + 1\n    }\n\n    /// Set the duty for a given channel.\n    ///\n    /// The value ranges from 0 for 0% duty, to [`max_duty_cycle`](Self::max_duty_cycle) for 100% duty, both included.\n    pub fn set_duty_cycle(&mut self, duty: u32) {\n        assert!(duty <= (*self).max_duty_cycle());\n        self.timer.set_compare_value(self.channel, unwrap!(duty.try_into()))\n    }\n\n    /// Set the duty cycle to 0%, or always inactive.\n    pub fn set_duty_cycle_fully_off(&mut self) {\n        self.set_duty_cycle(0);\n    }\n\n    /// Set the duty cycle to 100%, or always active.\n    pub fn set_duty_cycle_fully_on(&mut self) {\n        self.set_duty_cycle((*self).max_duty_cycle());\n    }\n\n    /// Set the duty cycle to `num / denom`.\n    ///\n    /// The caller is responsible for ensuring that `num` is less than or equal to `denom`,\n    /// and that `denom` is not zero.\n    pub fn set_duty_cycle_fraction(&mut self, num: u32, denom: u32) {\n        assert!(denom != 0);\n        assert!(num <= denom);\n        let duty = u32::from(num) * u32::from(self.max_duty_cycle()) / u32::from(denom);\n\n        // This is safe because we know that `num <= denom`, so `duty <= self.max_duty_cycle()` (u16)\n        #[allow(clippy::cast_possible_truncation)]\n        self.set_duty_cycle(unwrap!(duty.try_into()));\n    }\n\n    /// Set the duty cycle to `percent / 100`\n    ///\n    /// The caller is responsible for ensuring that `percent` is less than or equal to 100.\n    pub fn set_duty_cycle_percent(&mut self, percent: u8) {\n        self.set_duty_cycle_fraction(percent as u32, 100)\n    }\n\n    /// Get the duty for a given channel.\n    ///\n    /// The value ranges from 0 for 0% duty, to [`max_duty_cycle`](Self::max_duty_cycle) for 100% duty, both included.\n    pub fn current_duty_cycle(&self) -> u16 {\n        unwrap!(self.timer.get_compare_value(self.channel).try_into())\n    }\n\n    /// Set the output polarity for a given channel.\n    pub fn set_polarity(&mut self, polarity: OutputPolarity) {\n        self.timer.set_output_polarity(self.channel, polarity);\n    }\n\n    /// Set the output compare mode for a given channel.\n    pub fn set_output_compare_mode(&mut self, mode: OutputCompareMode) {\n        self.timer.set_output_compare_mode(self.channel, mode);\n    }\n\n    /// Convert this PWM channel into a ring-buffered PWM channel.\n    ///\n    /// This allows continuous PWM waveform generation using a DMA ring buffer.\n    /// The ring buffer enables dynamic updates to the PWM duty cycle without blocking.\n    ///\n    /// # Arguments\n    /// * `tx_dma` - The DMA channel to use for transferring duty cycle values\n    /// * `dma_buf` - The buffer to use as a ring buffer (must be non-empty and <= 65535 elements)\n    ///\n    /// # Panics\n    /// Panics if `dma_buf` is empty or longer than 65535 elements.\n    pub fn into_ring_buffered_channel<W: Word + Into<T::Word>, D: super::UpDma<T>>(\n        mut self,\n        tx_dma: Peri<'d, D>,\n        dma_buf: &'d mut [W],\n        irq: impl crate::interrupt::typelevel::Binding<D::Interrupt, crate::dma::InterruptHandler<D>> + 'd,\n    ) -> RingBufferedPwmChannel<'d, T, W> {\n        assert!(!dma_buf.is_empty() && dma_buf.len() <= 0xFFFF);\n\n        self.timer.clamp_compare_value::<W>(self.channel);\n        self.timer.enable_update_dma(true);\n\n        RingBufferedPwmChannel::new(\n            unsafe { self.timer.clone_unchecked() },\n            self.channel,\n            self.timer.setup_ring_buffer(tx_dma, irq, self.channel, dma_buf),\n        )\n    }\n}\n\n/// A group of four [`SimplePwmChannel`]s, obtained from [`SimplePwm::split`].\npub struct SimplePwmChannels<'d, T: GeneralInstance4Channel> {\n    /// Channel 1\n    pub ch1: SimplePwmChannel<'d, T>,\n    /// Channel 2\n    pub ch2: SimplePwmChannel<'d, T>,\n    /// Channel 3\n    pub ch3: SimplePwmChannel<'d, T>,\n    /// Channel 4\n    pub ch4: SimplePwmChannel<'d, T>,\n}\n\n/// Simple PWM driver.\npub struct SimplePwm<'d, T: GeneralInstance4Channel> {\n    inner: Timer<'d, T>,\n    ch1: Option<Flex<'d>>,\n    ch2: Option<Flex<'d>>,\n    ch3: Option<Flex<'d>>,\n    ch4: Option<Flex<'d>>,\n}\n\nimpl<'d, T: GeneralInstance4Channel> SimplePwm<'d, T> {\n    /// Create a new simple PWM driver.\n    #[allow(unused)]\n    pub fn new<#[cfg(afio)] A>(\n        tim: Peri<'d, T>,\n        ch1: Option<if_afio!(PwmPin<'d, T, Ch1, A>)>,\n        ch2: Option<if_afio!(PwmPin<'d, T, Ch2, A>)>,\n        ch3: Option<if_afio!(PwmPin<'d, T, Ch3, A>)>,\n        ch4: Option<if_afio!(PwmPin<'d, T, Ch4, A>)>,\n        freq: Hertz,\n        counting_mode: CountingMode,\n    ) -> Self {\n        Self::new_inner(\n            tim,\n            ch1.map(|pin| pin.pin),\n            ch2.map(|pin| pin.pin),\n            ch3.map(|pin| pin.pin),\n            ch4.map(|pin| pin.pin),\n            freq,\n            counting_mode,\n        )\n    }\n\n    fn new_inner(\n        tim: Peri<'d, T>,\n        ch1: Option<Flex<'d>>,\n        ch2: Option<Flex<'d>>,\n        ch3: Option<Flex<'d>>,\n        ch4: Option<Flex<'d>>,\n        freq: Hertz,\n        counting_mode: CountingMode,\n    ) -> Self {\n        let mut this = Self {\n            inner: Timer::new(tim),\n            ch1,\n            ch2,\n            ch3,\n            ch4,\n        };\n\n        this.inner.set_counting_mode(counting_mode);\n        this.set_frequency(freq);\n        this.inner.enable_outputs(); // Required for advanced timers, see GeneralInstance4Channel for details\n\n        [Channel::Ch1, Channel::Ch2, Channel::Ch3, Channel::Ch4]\n            .iter()\n            .for_each(|&channel| {\n                this.inner.set_output_compare_mode(channel, OutputCompareMode::PwmMode1);\n\n                this.inner.set_output_compare_preload(channel, true);\n            });\n        this.inner.set_autoreload_preload(true);\n\n        // Generate update event so pre-load registers are written to the shadow registers\n        this.inner.generate_update_event();\n        this.inner.start();\n\n        this\n    }\n\n    /// Get a single channel\n    ///\n    /// If you need to use multiple channels, use [`Self::split`].\n    pub fn channel(&mut self, channel: Channel) -> SimplePwmChannel<'_, T> {\n        SimplePwmChannel {\n            timer: unsafe { self.inner.clone_unchecked() },\n            channel,\n            _pin: None,\n        }\n    }\n\n    /// Channel 1\n    ///\n    /// This is just a convenience wrapper around [`Self::channel`].\n    ///\n    /// If you need to use multiple channels, use [`Self::split`].\n    pub fn ch1(&mut self) -> SimplePwmChannel<'_, T> {\n        self.channel(Channel::Ch1)\n    }\n\n    /// Channel 2\n    ///\n    /// This is just a convenience wrapper around [`Self::channel`].\n    ///\n    /// If you need to use multiple channels, use [`Self::split`].\n    pub fn ch2(&mut self) -> SimplePwmChannel<'_, T> {\n        self.channel(Channel::Ch2)\n    }\n\n    /// Channel 3\n    ///\n    /// This is just a convenience wrapper around [`Self::channel`].\n    ///\n    /// If you need to use multiple channels, use [`Self::split`].\n    pub fn ch3(&mut self) -> SimplePwmChannel<'_, T> {\n        self.channel(Channel::Ch3)\n    }\n\n    /// Channel 4\n    ///\n    /// This is just a convenience wrapper around [`Self::channel`].\n    ///\n    /// If you need to use multiple channels, use [`Self::split`].\n    pub fn ch4(&mut self) -> SimplePwmChannel<'_, T> {\n        self.channel(Channel::Ch4)\n    }\n\n    /// Splits a [`SimplePwm`] into four pwm channels.\n    ///\n    /// This returns all four channels, including channels that\n    /// aren't configured with a [`PwmPin`].\n    // TODO: I hate the name \"split\"\n    pub fn split(self) -> SimplePwmChannels<'static, T>\n    where\n        // must be static because the timer will never be dropped/disabled\n        'd: 'static,\n    {\n        // without this, the timer would be disabled at the end of this function\n        let timer = ManuallyDrop::new(self.inner);\n\n        let ch = |channel, pin| SimplePwmChannel {\n            timer: unsafe { timer.clone_unchecked() },\n            channel,\n            _pin: pin,\n        };\n\n        SimplePwmChannels {\n            ch1: ch(Channel::Ch1, self.ch1),\n            ch2: ch(Channel::Ch2, self.ch2),\n            ch3: ch(Channel::Ch3, self.ch3),\n            ch4: ch(Channel::Ch4, self.ch4),\n        }\n    }\n\n    /// Set PWM frequency.\n    ///\n    /// The actual frequency may differ from the requested value due to hardware\n    /// limitations. The timer will round towards a slower (longer) period.\n    ///\n    /// Note: that the frequency will not be applied in the timer until an update event\n    /// occurs.\n    pub fn set_frequency(&mut self, freq: Hertz) {\n        // TODO: prevent ARR = u16::MAX?\n        let multiplier = if self.inner.get_counting_mode().is_center_aligned() {\n            2u64\n        } else {\n            1u64\n        };\n        let timer_f = T::frequency().0 as u64;\n        let clocks = timer_f / (freq.0 as u64 * multiplier);\n        self.inner.set_period_clocks_internal(clocks, RoundTo::Slower, 16);\n    }\n\n    /// Get the PWM driver frequency.\n    pub fn get_frequency(&self) -> Hertz {\n        self.inner.get_frequency()\n    }\n\n    /// Set PWM period in milliseconds.\n    ///\n    /// The actual period may differ from the requested value due to hardware\n    /// limitations. The timer will round towards a slower (longer) period.\n    ///\n    /// Note: that the period will not be applied in the timer until an update event\n    /// occurs.\n    pub fn set_period_ms(&mut self, ms: u32) {\n        let timer_f = T::frequency().0 as u64;\n        let mut clocks = timer_f * ms as u64 / 1_000;\n        if self.inner.get_counting_mode().is_center_aligned() {\n            clocks = clocks / 2;\n        }\n        self.inner.set_period_clocks(clocks, RoundTo::Slower);\n    }\n\n    /// Set PWM period in microseconds.\n    ///\n    /// The actual period may differ from the requested value due to hardware\n    /// limitations. The timer will round towards a slower (longer) period.\n    ///\n    /// Note: that the period will not be applied in the timer until an update event\n    /// occurs.\n    pub fn set_period_us(&mut self, us: u32) {\n        let timer_f = T::frequency().0 as u64;\n        let mut clocks = timer_f * us as u64 / 1_000_000;\n        if self.inner.get_counting_mode().is_center_aligned() {\n            clocks = clocks / 2;\n        }\n        self.inner.set_period_clocks(clocks, RoundTo::Slower);\n    }\n\n    /// Set PWM period in seconds.\n    ///\n    /// The actual period may differ from the requested value due to hardware\n    /// limitations. The timer will round towards a slower (longer) period.\n    ///\n    /// Note: that the period will not be applied in the timer until an update event\n    /// occurs.\n    pub fn set_period_secs(&mut self, secs: u32) {\n        let timer_f = T::frequency().0 as u64;\n        let mut clocks = timer_f * secs as u64;\n        if self.inner.get_counting_mode().is_center_aligned() {\n            clocks = clocks / 2;\n        }\n        self.inner.set_period_clocks(clocks, RoundTo::Slower);\n    }\n\n    /// Set PWM period using an `embassy_time::Duration`.\n    ///\n    /// The actual period may differ from the requested value due to hardware\n    /// limitations. The timer will round towards a slower (longer) period.\n    ///\n    /// Note: that the period will not be applied in the timer until an update event\n    /// occurs.\n    #[cfg(feature = \"time\")]\n    pub fn set_period(&mut self, period: embassy_time::Duration) {\n        let timer_f = T::frequency().0 as u64;\n        let mut clocks = timer_f * period.as_ticks() / embassy_time::TICK_HZ;\n        if self.inner.get_counting_mode().is_center_aligned() {\n            clocks = clocks / 2;\n        }\n        self.inner.set_period_clocks(clocks, RoundTo::Slower);\n    }\n\n    /// Get max duty value.\n    ///\n    /// This value depends on the configured frequency and the timer's clock rate from RCC.\n    pub fn max_duty_cycle(&self) -> u32 {\n        self.inner.get_max_compare_value().into() + 1\n    }\n\n    /// Generate a sequence of PWM waveform\n    ///\n    /// Note:\n    /// The DMA channel provided does not need to correspond to the requested channel.\n    pub async fn waveform<C: TimerChannel, W: Word + Into<T::Word>, D: super::Dma<T, C>>(\n        &mut self,\n        dma: Peri<'_, D>,\n        irq: impl crate::interrupt::typelevel::Binding<D::Interrupt, crate::dma::InterruptHandler<D>> + '_,\n        channel: Channel,\n        duty: &[W],\n    ) {\n        self.inner.enable_channel(channel, true);\n        self.inner.enable_channel(C::CHANNEL, true);\n        self.inner.clamp_compare_value::<W>(channel);\n        self.inner.set_cc_dma_selection(Ccds::ON_UPDATE);\n        self.inner.set_cc_dma_enable_state(C::CHANNEL, true);\n        self.inner.setup_channel_update_dma(dma, irq, channel, duty).await;\n        self.inner.set_cc_dma_enable_state(C::CHANNEL, false);\n    }\n\n    /// Generate a sequence of PWM waveform\n    ///\n    /// Note:\n    /// You will need to provide corresponding `TIMx_UP` DMA channel to use this method.\n    /// Also be aware that embassy timers use one of timers internally. It is possible to\n    /// switch this timer by using `time-driver-timX` feature.\n    pub async fn waveform_up<W: Word + Into<T::Word>, D: super::UpDma<T>>(\n        &mut self,\n        dma: Peri<'_, D>,\n        irq: impl crate::interrupt::typelevel::Binding<D::Interrupt, crate::dma::InterruptHandler<D>> + '_,\n        channel: Channel,\n        duty: &[W],\n    ) {\n        self.inner.enable_channel(channel, true);\n        self.inner.clamp_compare_value::<W>(channel);\n        self.inner.enable_update_dma(true);\n        self.inner.setup_update_dma(dma, irq, channel, duty).await;\n        self.inner.enable_update_dma(false);\n    }\n\n    /// Generate a multichannel sequence of PWM waveforms using DMA triggered by timer update events.\n    ///\n    /// This method utilizes the timer's DMA burst transfer capability to update multiple CCRx registers\n    /// in sequence on each update event (UEV). The data is written via the DMAR register using the\n    /// DMA base address (DBA) and burst length (DBL) configured in the DCR register.\n    ///\n    /// The `duty` buffer must be structured as a flattened 2D array in row-major order, where each row\n    /// represents a single update event and each column corresponds to a specific timer channel (starting\n    /// from `starting_channel` up to and including `ending_channel`).\n    ///\n    /// For example, if using channels 1 through 4, a buffer of 4 update steps might look like:\n    ///\n    /// ```rust,ignore\n    /// let dma_buf: [u16; 16] = [\n    ///     ch1_duty_1, ch2_duty_1, ch3_duty_1, ch4_duty_1, // update 1\n    ///     ch1_duty_2, ch2_duty_2, ch3_duty_2, ch4_duty_2, // update 2\n    ///     ch1_duty_3, ch2_duty_3, ch3_duty_3, ch4_duty_3, // update 3\n    ///     ch1_duty_4, ch2_duty_4, ch3_duty_4, ch4_duty_4, // update 4\n    /// ];\n    /// ```\n    ///\n    /// Each group of `N` values (where `N` is number of channels) is transferred on one update event,\n    /// updating the duty cycles of all selected channels simultaneously.\n    ///\n    /// Note:\n    /// You will need to provide corresponding `TIMx_UP` DMA channel to use this method.\n    /// Also be aware that embassy timers use one of timers internally. It is possible to\n    /// switch this timer by using `time-driver-timX` feature.\n    ///\n    pub async fn waveform_up_multi_channel<W: Word + Into<T::Word>, D: super::UpDma<T>>(\n        &mut self,\n        dma: Peri<'_, D>,\n        irq: impl crate::interrupt::typelevel::Binding<D::Interrupt, crate::dma::InterruptHandler<D>> + '_,\n        starting_channel: Channel,\n        ending_channel: Channel,\n        duty: &[W],\n    ) {\n        [Channel::Ch1, Channel::Ch2, Channel::Ch3, Channel::Ch4]\n            .iter()\n            .filter(|ch| ch.index() >= starting_channel.index())\n            .filter(|ch| ch.index() <= ending_channel.index())\n            .for_each(|ch| {\n                self.inner.enable_channel(*ch, true);\n                self.inner.clamp_compare_value::<W>(*ch);\n            });\n        self.inner.enable_update_dma(true);\n        self.inner\n            .setup_update_dma_burst(dma, irq, starting_channel, ending_channel, duty)\n            .await;\n        self.inner.enable_update_dma(false);\n    }\n}\n\nimpl<'d, T: GeneralInstance4Channel> embedded_hal_1::pwm::ErrorType for SimplePwmChannel<'d, T> {\n    type Error = core::convert::Infallible;\n}\n\nimpl<'d, T: GeneralInstance4Channel> embedded_hal_1::pwm::SetDutyCycle for SimplePwmChannel<'d, T> {\n    fn max_duty_cycle(&self) -> u16 {\n        unwrap!(self.max_duty_cycle().try_into())\n    }\n\n    fn set_duty_cycle(&mut self, duty: u16) -> Result<(), Self::Error> {\n        self.set_duty_cycle(duty.into());\n        Ok(())\n    }\n\n    fn set_duty_cycle_fully_off(&mut self) -> Result<(), Self::Error> {\n        self.set_duty_cycle_fully_off();\n        Ok(())\n    }\n\n    fn set_duty_cycle_fully_on(&mut self) -> Result<(), Self::Error> {\n        self.set_duty_cycle_fully_on();\n        Ok(())\n    }\n\n    fn set_duty_cycle_fraction(&mut self, num: u16, denom: u16) -> Result<(), Self::Error> {\n        self.set_duty_cycle_fraction(num.into(), denom.into());\n        Ok(())\n    }\n\n    fn set_duty_cycle_percent(&mut self, percent: u8) -> Result<(), Self::Error> {\n        self.set_duty_cycle_percent(percent);\n        Ok(())\n    }\n}\n\nimpl<'d, T: GeneralInstance4Channel> embedded_hal_02::Pwm for SimplePwm<'d, T> {\n    type Channel = Channel;\n    type Time = Hertz;\n    type Duty = u32;\n\n    fn disable(&mut self, channel: Self::Channel) {\n        self.inner.enable_channel(channel, false);\n    }\n\n    fn enable(&mut self, channel: Self::Channel) {\n        self.inner.enable_channel(channel, true);\n    }\n\n    fn get_period(&self) -> Self::Time {\n        self.inner.get_frequency()\n    }\n\n    fn get_duty(&self, channel: Self::Channel) -> Self::Duty {\n        self.inner.get_compare_value(channel).into()\n    }\n\n    fn get_max_duty(&self) -> Self::Duty {\n        self.inner.get_max_compare_value().into() + 1\n    }\n\n    fn set_duty(&mut self, channel: Self::Channel, duty: Self::Duty) {\n        assert!(duty <= self.max_duty_cycle() as u32);\n        self.inner.set_compare_value(channel, unwrap!(duty.try_into()))\n    }\n\n    fn set_period<P>(&mut self, period: P)\n    where\n        P: Into<Self::Time>,\n    {\n        self.inner.set_frequency(period.into(), RoundTo::Slower);\n    }\n}\n"
  },
  {
    "path": "embassy-stm32/src/tsc/acquisition_banks.rs",
    "content": "use super::TSC_NUM_GROUPS;\nuse super::io_pin::*;\n#[cfg(any(tsc_v2, tsc_v3))]\nuse super::pin_groups::G7;\n#[cfg(tsc_v3)]\nuse super::pin_groups::G8;\nuse super::pin_groups::{G1, G2, G3, G4, G5, G6, pin_roles};\nuse super::types::{Group, GroupStatus};\n\n/// Represents a collection of TSC (Touch Sensing Controller) pins for an acquisition bank.\n///\n/// This struct holds optional `tsc::IOPin` values for each TSC group, allowing for flexible\n/// configuration of TSC acquisition banks. Each field corresponds to a specific TSC group\n/// and can be set to `Some(tsc::IOPin)` if that group is to be included in the acquisition,\n/// or `None` if it should be excluded.\n#[allow(missing_docs)]\n#[derive(Default)]\npub struct AcquisitionBankPins {\n    pub g1_pin: Option<IOPinWithRole<G1, pin_roles::Channel>>,\n    pub g2_pin: Option<IOPinWithRole<G2, pin_roles::Channel>>,\n    pub g3_pin: Option<IOPinWithRole<G3, pin_roles::Channel>>,\n    pub g4_pin: Option<IOPinWithRole<G4, pin_roles::Channel>>,\n    pub g5_pin: Option<IOPinWithRole<G5, pin_roles::Channel>>,\n    pub g6_pin: Option<IOPinWithRole<G6, pin_roles::Channel>>,\n    #[cfg(any(tsc_v2, tsc_v3))]\n    pub g7_pin: Option<IOPinWithRole<G7, pin_roles::Channel>>,\n    #[cfg(tsc_v3)]\n    pub g8_pin: Option<IOPinWithRole<G8, pin_roles::Channel>>,\n}\n\nimpl AcquisitionBankPins {\n    /// Returns an iterator over the pins in this acquisition bank.\n    ///\n    /// This method allows for easy traversal of all configured pins in the bank.\n    pub fn iter(&self) -> AcquisitionBankPinsIterator<'_> {\n        AcquisitionBankPinsIterator(AcquisitionBankIterator::new(self))\n    }\n}\n\n/// Iterator for TSC acquisition banks.\n///\n/// This iterator allows traversing through the pins of a `AcquisitionBankPins` struct,\n/// yielding each configured pin in order of the TSC groups.\npub struct AcquisitionBankIterator<'a> {\n    pins: &'a AcquisitionBankPins,\n    current_group: u8,\n}\n\nimpl<'a> AcquisitionBankIterator<'a> {\n    fn new(pins: &'a AcquisitionBankPins) -> Self {\n        Self { pins, current_group: 0 }\n    }\n\n    fn next_pin(&mut self) -> Option<IOPin> {\n        while self.current_group < TSC_NUM_GROUPS as u8 {\n            let pin = match self.current_group {\n                0 => self.pins.g1_pin.map(IOPinWithRole::get_pin),\n                1 => self.pins.g2_pin.map(IOPinWithRole::get_pin),\n                2 => self.pins.g3_pin.map(IOPinWithRole::get_pin),\n                3 => self.pins.g4_pin.map(IOPinWithRole::get_pin),\n                4 => self.pins.g5_pin.map(IOPinWithRole::get_pin),\n                5 => self.pins.g6_pin.map(IOPinWithRole::get_pin),\n                #[cfg(any(tsc_v2, tsc_v3))]\n                6 => self.pins.g7_pin.map(IOPinWithRole::get_pin),\n                #[cfg(tsc_v3)]\n                7 => self.pins.g8_pin.map(IOPinWithRole::get_pin),\n                _ => None,\n            };\n            self.current_group += 1;\n            if pin.is_some() {\n                return pin;\n            }\n        }\n        None\n    }\n}\n\n/// Iterator for TSC acquisition bank pins.\n///\n/// This iterator yields `tsc::IOPin` values for each configured pin in the acquisition bank.\npub struct AcquisitionBankPinsIterator<'a>(AcquisitionBankIterator<'a>);\n\nimpl<'a> Iterator for AcquisitionBankPinsIterator<'a> {\n    type Item = IOPin;\n\n    fn next(&mut self) -> Option<Self::Item> {\n        self.0.next_pin()\n    }\n}\n\nimpl AcquisitionBankPins {\n    /// Returns an iterator over the available pins in the bank\n    pub fn pins_iterator(&self) -> AcquisitionBankPinsIterator<'_> {\n        AcquisitionBankPinsIterator(AcquisitionBankIterator::new(self))\n    }\n}\n\n/// Represents a collection of TSC pins to be acquired simultaneously.\n///\n/// This struct contains a set of pins to be used in a TSC acquisition with a pre-computed and\n/// verified mask for efficiently setting up the TSC peripheral before performing an acquisition.\n/// It ensures that only one channel pin per TSC group is included, adhering to hardware limitations.\npub struct AcquisitionBank {\n    pub(super) pins: AcquisitionBankPins,\n    pub(super) mask: u32,\n}\n\nimpl AcquisitionBank {\n    /// Returns an iterator over the available pins in the bank.\n    pub fn pins_iterator(&self) -> AcquisitionBankPinsIterator<'_> {\n        self.pins.pins_iterator()\n    }\n\n    /// Returns the mask for this bank.\n    pub fn mask(&self) -> u32 {\n        self.mask\n    }\n\n    /// Retrieves the TSC I/O pin for a given group in this acquisition bank.\n    ///\n    /// # Arguments\n    /// * `group` - The TSC group to retrieve the pin for.\n    ///\n    /// # Returns\n    /// An `Option<tsc::IOPin>` containing the pin if it exists for the given group, or `None` if not.\n    pub fn get_pin(&self, group: Group) -> Option<IOPin> {\n        match group {\n            Group::One => self.pins.g1_pin.map(|p| p.pin),\n            Group::Two => self.pins.g2_pin.map(|p| p.pin),\n            Group::Three => self.pins.g3_pin.map(|p| p.pin),\n            Group::Four => self.pins.g4_pin.map(|p| p.pin),\n            Group::Five => self.pins.g5_pin.map(|p| p.pin),\n            Group::Six => self.pins.g6_pin.map(|p| p.pin),\n            #[cfg(any(tsc_v2, tsc_v3))]\n            Group::Seven => self.pins.g7_pin.map(|p| p.pin),\n            #[cfg(tsc_v3)]\n            Group::Eight => self.pins.g8_pin.map(|p| p.pin),\n        }\n    }\n}\n\n/// Represents the status of all TSC groups in an acquisition bank\n#[derive(Default)]\npub struct AcquisitionBankStatus {\n    pub(super) groups: [Option<GroupStatus>; TSC_NUM_GROUPS],\n}\n\nimpl AcquisitionBankStatus {\n    /// Check if all groups in the bank are complete\n    pub fn all_complete(&self) -> bool {\n        self.groups\n            .iter()\n            .all(|&status| status.map_or(true, |s| s == GroupStatus::Complete))\n    }\n\n    /// Check if any group in the bank is ongoing\n    pub fn any_ongoing(&self) -> bool {\n        self.groups.iter().any(|&status| status == Some(GroupStatus::Ongoing))\n    }\n\n    /// Get the status of a specific group, if the group is present in the bank\n    pub fn get_group_status(&self, group: Group) -> Option<GroupStatus> {\n        let index: usize = group.into();\n        self.groups[index]\n    }\n\n    /// Iterator for groups present in the bank\n    pub fn iter(&self) -> impl Iterator<Item = (Group, GroupStatus)> + '_ {\n        self.groups.iter().enumerate().filter_map(|(group_num, status)| {\n            status.and_then(|s| Group::try_from(group_num).ok().map(|group| (group, s)))\n        })\n    }\n}\n\n/// Represents the result of a Touch Sensing Controller (TSC) acquisition for a specific pin.\n///\n/// This struct contains a reference to the `tsc::IOPin` from which a value was read,\n/// along with the actual sensor reading for that pin. It provides a convenient way\n/// to associate TSC readings with their corresponding pins after an acquisition.\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\n#[derive(Clone, Copy, Debug)]\npub struct ChannelReading {\n    /// The sensor reading value obtained from the TSC acquisition.\n    /// Lower values typically indicate a detected touch, while higher values indicate no touch.\n    pub sensor_value: u16,\n\n    /// The `tsc::IOPin` associated with this reading.\n    /// This allows for easy identification of which pin the reading corresponds to.\n    pub tsc_pin: IOPin,\n}\n\n/// Represents the readings from all TSC groups\n#[derive(Default)]\npub struct AcquisitionBankReadings {\n    pub(super) groups: [Option<ChannelReading>; TSC_NUM_GROUPS],\n}\n\nimpl AcquisitionBankReadings {\n    /// Get the reading for a specific group, if the group is present in the bank\n    pub fn get_group_reading(&self, group: Group) -> Option<ChannelReading> {\n        let index: usize = group.into();\n        self.groups[index]\n    }\n\n    /// Iterator for readings for groups present in the bank\n    pub fn iter(&self) -> impl Iterator<Item = ChannelReading> + '_ {\n        self.groups.iter().filter_map(|&x| x)\n    }\n}\n"
  },
  {
    "path": "embassy-stm32/src/tsc/config.rs",
    "content": "/// Charge transfer pulse cycles\n#[allow(missing_docs)]\n#[derive(Copy, Clone, PartialEq)]\npub enum ChargeTransferPulseCycle {\n    _1,\n    _2,\n    _3,\n    _4,\n    _5,\n    _6,\n    _7,\n    _8,\n    _9,\n    _10,\n    _11,\n    _12,\n    _13,\n    _14,\n    _15,\n    _16,\n}\n\nimpl Into<u8> for ChargeTransferPulseCycle {\n    fn into(self) -> u8 {\n        match self {\n            ChargeTransferPulseCycle::_1 => 0,\n            ChargeTransferPulseCycle::_2 => 1,\n            ChargeTransferPulseCycle::_3 => 2,\n            ChargeTransferPulseCycle::_4 => 3,\n            ChargeTransferPulseCycle::_5 => 4,\n            ChargeTransferPulseCycle::_6 => 5,\n            ChargeTransferPulseCycle::_7 => 6,\n            ChargeTransferPulseCycle::_8 => 7,\n            ChargeTransferPulseCycle::_9 => 8,\n            ChargeTransferPulseCycle::_10 => 9,\n            ChargeTransferPulseCycle::_11 => 10,\n            ChargeTransferPulseCycle::_12 => 11,\n            ChargeTransferPulseCycle::_13 => 12,\n            ChargeTransferPulseCycle::_14 => 13,\n            ChargeTransferPulseCycle::_15 => 14,\n            ChargeTransferPulseCycle::_16 => 15,\n        }\n    }\n}\n\n/// Max count\n#[allow(missing_docs)]\n#[derive(Copy, Clone)]\npub enum MaxCount {\n    _255,\n    _511,\n    _1023,\n    _2047,\n    _4095,\n    _8191,\n    _16383,\n}\n\nimpl Into<u8> for MaxCount {\n    fn into(self) -> u8 {\n        match self {\n            MaxCount::_255 => 0,\n            MaxCount::_511 => 1,\n            MaxCount::_1023 => 2,\n            MaxCount::_2047 => 3,\n            MaxCount::_4095 => 4,\n            MaxCount::_8191 => 5,\n            MaxCount::_16383 => 6,\n        }\n    }\n}\n\n/// Prescaler divider\n#[allow(missing_docs)]\n#[derive(Copy, Clone, PartialEq)]\npub enum PGPrescalerDivider {\n    _1,\n    _2,\n    _4,\n    _8,\n    _16,\n    _32,\n    _64,\n    _128,\n}\n\nimpl Into<u8> for PGPrescalerDivider {\n    fn into(self) -> u8 {\n        match self {\n            PGPrescalerDivider::_1 => 0,\n            PGPrescalerDivider::_2 => 1,\n            PGPrescalerDivider::_4 => 2,\n            PGPrescalerDivider::_8 => 3,\n            PGPrescalerDivider::_16 => 4,\n            PGPrescalerDivider::_32 => 5,\n            PGPrescalerDivider::_64 => 6,\n            PGPrescalerDivider::_128 => 7,\n        }\n    }\n}\n\n/// Error type for SSDeviation\n#[derive(Debug, Clone, Copy, PartialEq, Eq)]\npub enum SSDeviationError {\n    /// The provided value is too low (0)\n    ValueTooLow,\n    /// The provided value is too high (greater than 128)\n    ValueTooHigh,\n}\n\n/// Spread Spectrum Deviation\n#[derive(Copy, Clone)]\npub struct SSDeviation(u8);\nimpl SSDeviation {\n    /// Create new deviation value, acceptable inputs are 1-128\n    pub fn new(val: u8) -> Result<Self, SSDeviationError> {\n        if val == 0 {\n            return Err(SSDeviationError::ValueTooLow);\n        } else if val > 128 {\n            return Err(SSDeviationError::ValueTooHigh);\n        }\n        Ok(Self(val - 1))\n    }\n}\n\nimpl Into<u8> for SSDeviation {\n    fn into(self) -> u8 {\n        self.0\n    }\n}\n\n/// Peripheral configuration\n#[derive(Clone, Copy)]\npub struct Config {\n    /// Duration of high state of the charge transfer pulse\n    pub ct_pulse_high_length: ChargeTransferPulseCycle,\n    /// Duration of the low state of the charge transfer pulse\n    pub ct_pulse_low_length: ChargeTransferPulseCycle,\n    /// Enable/disable of spread spectrum feature\n    pub spread_spectrum: bool,\n    /// Adds variable number of periods of the SS clk to pulse high state\n    pub spread_spectrum_deviation: SSDeviation,\n    /// Selects AHB clock divider used to generate SS clk\n    pub spread_spectrum_prescaler: bool,\n    /// Selects AHB clock divider used to generate pulse generator clk\n    pub pulse_generator_prescaler: PGPrescalerDivider,\n    /// Maximum number of charge transfer pulses that can be generated before error\n    pub max_count_value: MaxCount,\n    /// Defines config of all IOs when no ongoing acquisition\n    pub io_default_mode: bool,\n    /// Polarity of sync input pin\n    pub synchro_pin_polarity: bool,\n    /// Acquisition starts when start bit is set or with sync pin input\n    pub acquisition_mode: bool,\n    /// Enable max count interrupt\n    pub max_count_interrupt: bool,\n}\n\nimpl Default for Config {\n    fn default() -> Self {\n        Self {\n            ct_pulse_high_length: ChargeTransferPulseCycle::_1,\n            ct_pulse_low_length: ChargeTransferPulseCycle::_1,\n            spread_spectrum: false,\n            spread_spectrum_deviation: SSDeviation::new(1).unwrap(),\n            spread_spectrum_prescaler: false,\n            pulse_generator_prescaler: PGPrescalerDivider::_1,\n            max_count_value: MaxCount::_255,\n            io_default_mode: false,\n            synchro_pin_polarity: false,\n            acquisition_mode: false,\n            max_count_interrupt: false,\n        }\n    }\n}\n"
  },
  {
    "path": "embassy-stm32/src/tsc/errors.rs",
    "content": "/// Represents errors that can occur when configuring or validating TSC pin groups.\n#[derive(Debug)]\npub enum GroupError {\n    /// Error when a group has no sampling capacitor\n    NoSamplingCapacitor,\n    /// Error when a group has neither channel IOs nor a shield IO\n    NoChannelOrShield,\n    /// Error when a group has both channel IOs and a shield IO\n    MixedChannelAndShield,\n    /// Error when there is more than one shield IO across all groups\n    MultipleShields,\n}\n\n/// Error returned when attempting to set an invalid channel pin as active in the TSC.\n#[derive(Debug)]\npub enum AcquisitionBankError {\n    /// Indicates that one or more of the provided pins is not a valid channel pin.\n    InvalidChannelPin,\n    /// Indicates that multiple channels from the same group were provided.\n    MultipleChannelsPerGroup,\n}\n"
  },
  {
    "path": "embassy-stm32/src/tsc/io_pin.rs",
    "content": "use core::marker::PhantomData;\nuse core::ops::{BitAnd, BitOr, BitOrAssign};\n\nuse super::pin_roles;\nuse super::types::Group;\n\n/// Pin defines\n#[allow(missing_docs)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\n#[derive(PartialEq, Clone, Copy, Debug)]\npub enum IOPin {\n    Group1Io1,\n    Group1Io2,\n    Group1Io3,\n    Group1Io4,\n    Group2Io1,\n    Group2Io2,\n    Group2Io3,\n    Group2Io4,\n    Group3Io1,\n    Group3Io2,\n    Group3Io3,\n    Group3Io4,\n    Group4Io1,\n    Group4Io2,\n    Group4Io3,\n    Group4Io4,\n    Group5Io1,\n    Group5Io2,\n    Group5Io3,\n    Group5Io4,\n    Group6Io1,\n    Group6Io2,\n    Group6Io3,\n    Group6Io4,\n    #[cfg(any(tsc_v2, tsc_v3))]\n    Group7Io1,\n    #[cfg(any(tsc_v2, tsc_v3))]\n    Group7Io2,\n    #[cfg(any(tsc_v2, tsc_v3))]\n    Group7Io3,\n    #[cfg(any(tsc_v2, tsc_v3))]\n    Group7Io4,\n    #[cfg(tsc_v3)]\n    Group8Io1,\n    #[cfg(tsc_v3)]\n    Group8Io2,\n    #[cfg(tsc_v3)]\n    Group8Io3,\n    #[cfg(tsc_v3)]\n    Group8Io4,\n}\n\n/// Represents a TSC I/O pin with associated group and role information.\n///\n/// This type combines an `tsc::IOPin` with phantom type parameters to statically\n/// encode the pin's group and role. This allows for type-safe operations\n/// on TSC pins within their specific contexts.\n///\n/// - `Group`: A type parameter representing the TSC group (e.g., `G1`, `G2`).\n/// - `Role`: A type parameter representing the pin's role (e.g., `Channel`, `Sample`).\n#[derive(Clone, Copy, Debug)]\npub struct IOPinWithRole<Group, Role: pin_roles::Role> {\n    /// The underlying TSC I/O pin.\n    pub pin: IOPin,\n    pub(super) phantom: PhantomData<(Group, Role)>,\n}\n\nimpl<G, R: pin_roles::Role> IOPinWithRole<G, R> {\n    pub(super) fn get_pin(wrapped_pin: IOPinWithRole<G, R>) -> IOPin {\n        wrapped_pin.pin\n    }\n}\n\nimpl IOPin {\n    /// Maps this IOPin to the Group it belongs to.\n    ///\n    /// This method provides a convenient way to determine which Group\n    /// a specific TSC I/O pin is associated with.\n    pub const fn group(&self) -> Group {\n        match self {\n            IOPin::Group1Io1 | IOPin::Group1Io2 | IOPin::Group1Io3 | IOPin::Group1Io4 => Group::One,\n            IOPin::Group2Io1 | IOPin::Group2Io2 | IOPin::Group2Io3 | IOPin::Group2Io4 => Group::Two,\n            IOPin::Group3Io1 | IOPin::Group3Io2 | IOPin::Group3Io3 | IOPin::Group3Io4 => Group::Three,\n            IOPin::Group4Io1 | IOPin::Group4Io2 | IOPin::Group4Io3 | IOPin::Group4Io4 => Group::Four,\n            IOPin::Group5Io1 | IOPin::Group5Io2 | IOPin::Group5Io3 | IOPin::Group5Io4 => Group::Five,\n            IOPin::Group6Io1 | IOPin::Group6Io2 | IOPin::Group6Io3 | IOPin::Group6Io4 => Group::Six,\n            #[cfg(any(tsc_v2, tsc_v3))]\n            IOPin::Group7Io1 | IOPin::Group7Io2 | IOPin::Group7Io3 | IOPin::Group7Io4 => Group::Seven,\n            #[cfg(tsc_v3)]\n            IOPin::Group8Io1 | IOPin::Group8Io2 | IOPin::Group8Io3 | IOPin::Group8Io4 => Group::Eight,\n        }\n    }\n\n    /// Returns the `Group` associated with the given `IOPin`.\n    pub fn get_group(pin: IOPin) -> Group {\n        pin.group()\n    }\n}\n\nimpl BitOr<IOPin> for u32 {\n    type Output = u32;\n    fn bitor(self, rhs: IOPin) -> Self::Output {\n        let rhs: u32 = rhs.into();\n        self | rhs\n    }\n}\n\nimpl BitOr<u32> for IOPin {\n    type Output = u32;\n    fn bitor(self, rhs: u32) -> Self::Output {\n        let val: u32 = self.into();\n        val | rhs\n    }\n}\n\nimpl BitOr for IOPin {\n    type Output = u32;\n    fn bitor(self, rhs: Self) -> Self::Output {\n        let val: u32 = self.into();\n        let rhs: u32 = rhs.into();\n        val | rhs\n    }\n}\n\nimpl BitOrAssign<IOPin> for u32 {\n    fn bitor_assign(&mut self, rhs: IOPin) {\n        let rhs: u32 = rhs.into();\n        *self |= rhs;\n    }\n}\n\nimpl BitAnd<IOPin> for u32 {\n    type Output = u32;\n    fn bitand(self, rhs: IOPin) -> Self::Output {\n        let rhs: u32 = rhs.into();\n        self & rhs\n    }\n}\n\nimpl BitAnd<u32> for IOPin {\n    type Output = u32;\n    fn bitand(self, rhs: u32) -> Self::Output {\n        let val: u32 = self.into();\n        val & rhs\n    }\n}\n\nimpl IOPin {\n    const fn to_u32(self) -> u32 {\n        match self {\n            IOPin::Group1Io1 => 0x00000001,\n            IOPin::Group1Io2 => 0x00000002,\n            IOPin::Group1Io3 => 0x00000004,\n            IOPin::Group1Io4 => 0x00000008,\n            IOPin::Group2Io1 => 0x00000010,\n            IOPin::Group2Io2 => 0x00000020,\n            IOPin::Group2Io3 => 0x00000040,\n            IOPin::Group2Io4 => 0x00000080,\n            IOPin::Group3Io1 => 0x00000100,\n            IOPin::Group3Io2 => 0x00000200,\n            IOPin::Group3Io3 => 0x00000400,\n            IOPin::Group3Io4 => 0x00000800,\n            IOPin::Group4Io1 => 0x00001000,\n            IOPin::Group4Io2 => 0x00002000,\n            IOPin::Group4Io3 => 0x00004000,\n            IOPin::Group4Io4 => 0x00008000,\n            IOPin::Group5Io1 => 0x00010000,\n            IOPin::Group5Io2 => 0x00020000,\n            IOPin::Group5Io3 => 0x00040000,\n            IOPin::Group5Io4 => 0x00080000,\n            IOPin::Group6Io1 => 0x00100000,\n            IOPin::Group6Io2 => 0x00200000,\n            IOPin::Group6Io3 => 0x00400000,\n            IOPin::Group6Io4 => 0x00800000,\n            #[cfg(any(tsc_v2, tsc_v3))]\n            IOPin::Group7Io1 => 0x01000000,\n            #[cfg(any(tsc_v2, tsc_v3))]\n            IOPin::Group7Io2 => 0x02000000,\n            #[cfg(any(tsc_v2, tsc_v3))]\n            IOPin::Group7Io3 => 0x04000000,\n            #[cfg(any(tsc_v2, tsc_v3))]\n            IOPin::Group7Io4 => 0x08000000,\n            #[cfg(tsc_v3)]\n            IOPin::Group8Io1 => 0x10000000,\n            #[cfg(tsc_v3)]\n            IOPin::Group8Io2 => 0x20000000,\n            #[cfg(tsc_v3)]\n            IOPin::Group8Io3 => 0x40000000,\n            #[cfg(tsc_v3)]\n            IOPin::Group8Io4 => 0x80000000,\n        }\n    }\n}\n\nimpl Into<u32> for IOPin {\n    fn into(self) -> u32 {\n        self.to_u32()\n    }\n}\n"
  },
  {
    "path": "embassy-stm32/src/tsc/mod.rs",
    "content": "//! TSC Peripheral Interface\n//!\n//! This module provides an interface for the Touch Sensing Controller (TSC) peripheral.\n//! It supports both blocking and async modes of operation, as well as different TSC versions (v1, v2, v3).\n//!\n//! # Key Concepts\n//!\n//! - **Pin Groups**: TSC pins are organized into groups, each containing up to four IOs.\n//! - **Pin Roles**: Each pin in a group can have a role: Channel, Sample, or Shield.\n//! - **Acquisition Banks**: Used for efficient, repeated TSC acquisitions on specific sets of pins.\n//!\n//! # Example (stm32)\n//!\n//! ```rust\n//! let device_config = embassy_stm32::Config::default();\n//! let context = embassy_stm32::init(device_config);\n//!\n//! let config = tsc::Config {\n//!     ct_pulse_high_length: ChargeTransferPulseCycle::_4,\n//!     ct_pulse_low_length: ChargeTransferPulseCycle::_4,\n//!     spread_spectrum: false,\n//!     spread_spectrum_deviation: SSDeviation::new(2).unwrap(),\n//!     spread_spectrum_prescaler: false,\n//!     pulse_generator_prescaler: PGPrescalerDivider::_16,\n//!     max_count_value: MaxCount::_255,\n//!     io_default_mode: false,\n//!     synchro_pin_polarity: false,\n//!     acquisition_mode: false,\n//!     max_count_interrupt: false,\n//! };\n//!\n//! let mut g2: PinGroupWithRoles<embassy_stm32::peripherals::TSC, G2> = PinGroupWithRoles::new();\n//! g2.set_io1::<tsc_pin_roles::Sample>(context.PB4);\n//! let sensor_pin = g2.set_io2::<tsc_pin_roles::Channel>(context.PB5);\n//!\n//! let pin_groups = PinGroups {\n//!     g2: Some(g2.pin_group),\n//!     ..Default::default()\n//! };\n//!\n//! let mut touch_controller = tsc::Tsc::new_blocking(\n//!     context.TSC,\n//!     pin_groups,\n//!     config,\n//! ).unwrap();\n//!\n//! let discharge_delay = 5; // ms\n//!\n//! loop {\n//!     touch_controller.set_active_channels_mask(sensor_pin.pin.into());\n//!     touch_controller.start();\n//!     touch_controller.poll_for_acquisition();\n//!     touch_controller.discharge_io(true);\n//!     Timer::after_millis(discharge_delay).await;\n//!\n//!     match touch_controller.group_get_status(sensor_pin.pin.group()) {\n//!         GroupStatus::Complete => {\n//!             let group_val = touch_controller.group_get_value(sensor_pin.pin.group());\n//!             // Process the touch value\n//!             // ...\n//!         }\n//!         GroupStatus::Ongoing => {\n//!             // Handle ongoing acquisition\n//!             // ...\n//!         }\n//!     }\n//! }\n//! ```\n//!\n//! # Async Usage\n//!\n//! For async operation, use `Tsc::new_async` and `pend_for_acquisition` instead of polling.\n\n#![macro_use]\n\n/// Configuration structures and enums for the TSC peripheral.\npub mod config;\n\n/// Definitions and implementations for TSC pin groups.\npub mod pin_groups;\n\n/// Definitions and implementations for individual TSC I/O pins.\npub mod io_pin;\n\n/// Structures and implementations for TSC acquisition banks.\npub mod acquisition_banks;\n\n/// Core implementation of the TSC (Touch Sensing Controller) driver.\npub mod tsc;\n\n/// Type definitions used throughout the TSC module.\npub mod types;\n\n/// Error types and definitions for the TSC module.\npub mod errors;\n\nuse core::marker::PhantomData;\n\npub use acquisition_banks::*;\npub use config::*;\nuse embassy_hal_internal::PeripheralType;\nuse embassy_sync::waitqueue::AtomicWaker;\npub use errors::*;\npub use io_pin::*;\npub use pin_groups::*;\npub use tsc::*;\npub use types::*;\n\nuse crate::rcc::RccPeripheral;\nuse crate::{interrupt, peripherals};\n\n#[cfg(tsc_v1)]\nconst TSC_NUM_GROUPS: usize = 6;\n#[cfg(tsc_v2)]\nconst TSC_NUM_GROUPS: usize = 7;\n#[cfg(tsc_v3)]\nconst TSC_NUM_GROUPS: usize = 8;\n\n/// Error type defined for TSC\n#[derive(Debug, Clone, Copy)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub enum Error {\n    /// Test error for TSC\n    Test,\n}\n\n/// TSC interrupt handler.\npub struct InterruptHandler<T: Instance> {\n    _phantom: PhantomData<T>,\n}\n\nimpl<T: Instance> interrupt::typelevel::Handler<T::Interrupt> for InterruptHandler<T> {\n    unsafe fn on_interrupt() {\n        T::regs().ier().write(|w| w.set_eoaie(false));\n        T::waker().wake();\n    }\n}\n\npub(crate) trait SealedInstance {\n    fn regs() -> crate::pac::tsc::Tsc;\n    fn waker() -> &'static AtomicWaker;\n}\n\n/// TSC instance trait\n#[allow(private_bounds)]\npub trait Instance: SealedInstance + PeripheralType + RccPeripheral {\n    /// Interrupt for this TSC instance\n    type Interrupt: interrupt::typelevel::Interrupt;\n}\n\nforeach_interrupt!(\n    ($inst:ident, tsc, TSC, GLOBAL, $irq:ident) => {\n        impl Instance for peripherals::$inst {\n            type Interrupt = crate::interrupt::typelevel::$irq;\n        }\n\n        impl SealedInstance for peripherals::$inst {\n            fn regs() -> crate::pac::tsc::Tsc {\n                crate::pac::$inst\n            }\n            fn waker() -> &'static AtomicWaker {\n                static WAKER: AtomicWaker = AtomicWaker::new();\n                &WAKER\n            }\n        }\n    };\n);\n"
  },
  {
    "path": "embassy-stm32/src/tsc/pin_groups.rs",
    "content": "use core::marker::PhantomData;\nuse core::ops::BitOr;\n\nuse super::Instance;\nuse super::errors::GroupError;\nuse super::io_pin::*;\nuse crate::Peri;\nuse crate::gpio::{AfType, Flex, OutputType, Speed};\n\n/// Pin type definition to control IO parameters\n#[derive(PartialEq, Clone, Copy)]\npub enum PinType {\n    /// Sensing channel pin connected to an electrode\n    Channel,\n    /// Sampling capacitor pin, one required for every pin group\n    Sample,\n    /// Shield pin connected to capacitive sensing shield\n    Shield,\n}\n\n/// Pin struct that maintains usage\n#[allow(missing_docs)]\npub struct Pin<'d, T, Group> {\n    _pin: Flex<'d>,\n    role: PinType,\n    tsc_io_pin: IOPin,\n    phantom: PhantomData<(T, Group)>,\n}\n\nimpl<'d, T, Group> Pin<'d, T, Group> {\n    /// Returns the role of this TSC pin.\n    ///\n    /// The role indicates whether this pin is configured as a channel,\n    /// sampling capacitor, or shield in the TSC group.\n    ///\n    /// # Returns\n    /// The `PinType` representing the role of this pin.\n    pub fn role(&self) -> PinType {\n        self.role\n    }\n\n    /// Returns the TSC IO pin associated with this pin.\n    ///\n    /// This method provides access to the specific TSC IO pin configuration,\n    /// which includes information about the pin's group and position within that group.\n    ///\n    /// # Returns\n    /// The `IOPin` representing this pin's TSC-specific configuration.\n    pub fn tsc_io_pin(&self) -> IOPin {\n        self.tsc_io_pin\n    }\n}\n\n/// Represents a group of TSC (Touch Sensing Controller) pins.\n///\n/// In the TSC peripheral, pins are organized into groups of four IOs. Each group\n/// must have exactly one sampling capacitor pin and can have multiple channel pins\n/// or a single shield pin. This structure encapsulates these pin configurations\n/// for a single TSC group.\n///\n/// # Pin Roles\n/// - Sampling Capacitor: One required per group, used for charge transfer.\n/// - Channel: Sensing pins connected to electrodes for touch detection.\n/// - Shield: Optional, used for active shielding to improve sensitivity.\n///\n/// # Constraints\n/// - Each group must have exactly one sampling capacitor pin.\n/// - A group can have either channel pins or a shield pin, but not both.\n/// - No more than one shield pin is allowed across all groups.\n#[allow(missing_docs)]\npub struct PinGroup<'d, T, Group> {\n    pin1: Option<Pin<'d, T, Group>>,\n    pin2: Option<Pin<'d, T, Group>>,\n    pin3: Option<Pin<'d, T, Group>>,\n    pin4: Option<Pin<'d, T, Group>>,\n}\n\nimpl<'d, T, G> Default for PinGroup<'d, T, G> {\n    fn default() -> Self {\n        Self {\n            pin1: None,\n            pin2: None,\n            pin3: None,\n            pin4: None,\n        }\n    }\n}\n\n/// Defines roles and traits for TSC (Touch Sensing Controller) pins.\n///\n/// This module contains marker types and traits that represent different roles\n/// a TSC pin can have, such as channel, sample, or shield.\npub mod pin_roles {\n    use super::{OutputType, PinType};\n\n    /// Marker type for a TSC channel pin.\n    #[derive(PartialEq, Clone, Copy, Debug)]\n    pub struct Channel;\n\n    /// Marker type for a TSC sampling pin.\n    #[derive(PartialEq, Clone, Copy, Debug)]\n    pub struct Sample;\n\n    /// Marker type for a TSC shield pin.\n    #[derive(PartialEq, Clone, Copy, Debug)]\n    pub struct Shield;\n\n    /// Trait for TSC pin roles.\n    ///\n    /// This trait defines the behavior and properties of different TSC pin roles.\n    /// It is implemented by the marker types `Channel`, `Sample`, and `Shield`.\n    pub trait Role {\n        /// Returns the `PinType` associated with this role.\n        fn pin_type() -> PinType;\n\n        /// Returns the `OutputType` associated with this role.\n        fn output_type() -> OutputType;\n    }\n\n    impl Role for Channel {\n        fn pin_type() -> PinType {\n            PinType::Channel\n        }\n        fn output_type() -> OutputType {\n            OutputType::PushPull\n        }\n    }\n\n    impl Role for Sample {\n        fn pin_type() -> PinType {\n            PinType::Sample\n        }\n        fn output_type() -> OutputType {\n            OutputType::OpenDrain\n        }\n    }\n\n    impl Role for Shield {\n        fn pin_type() -> PinType {\n            PinType::Shield\n        }\n        fn output_type() -> OutputType {\n            OutputType::PushPull\n        }\n    }\n}\n\n/// Represents a group of TSC pins with their associated roles.\n///\n/// This struct allows for type-safe configuration of TSC pin groups,\n/// ensuring that pins are assigned appropriate roles within their group.\n/// This type is essentially just a wrapper type around a `PinGroup` value.\n///\n/// # Type Parameters\n/// - `'d`: Lifetime of the pin group.\n/// - `T`: The TSC instance type.\n/// - `G`: The group identifier.\n/// - `R1`, `R2`, `R3`, `R4`: Role types for each pin in the group, defaulting to `Channel`.\npub struct PinGroupWithRoles<\n    'd,\n    T: Instance,\n    G,\n    R1 = pin_roles::Channel,\n    R2 = pin_roles::Channel,\n    R3 = pin_roles::Channel,\n    R4 = pin_roles::Channel,\n> {\n    /// The underlying pin group without role information.\n    pub pin_group: PinGroup<'d, T, G>,\n    _phantom: PhantomData<(R1, R2, R3, R4)>,\n}\n\nimpl<'d, T: Instance, G, R1, R2, R3, R4> Default for PinGroupWithRoles<'d, T, G, R1, R2, R3, R4> {\n    fn default() -> Self {\n        Self {\n            pin_group: PinGroup::default(),\n            _phantom: PhantomData,\n        }\n    }\n}\n\nimpl<'d, T: Instance, G> PinGroup<'d, T, G> {\n    fn contains_exactly_one_shield_pin(&self) -> bool {\n        let shield_count = self.shield_pins().count();\n        shield_count == 1\n    }\n\n    fn check_group(&self) -> Result<(), GroupError> {\n        let mut channel_count = 0;\n        let mut shield_count = 0;\n        let mut sample_count = 0;\n        for pin in self.pins().into_iter().flatten() {\n            match pin.role {\n                PinType::Channel => {\n                    channel_count += 1;\n                }\n                PinType::Shield => {\n                    shield_count += 1;\n                }\n                PinType::Sample => {\n                    sample_count += 1;\n                }\n            }\n        }\n\n        // Every group requires exactly one sampling capacitor\n        if sample_count != 1 {\n            return Err(GroupError::NoSamplingCapacitor);\n        }\n\n        // Each group must have at least one shield or channel IO\n        if shield_count == 0 && channel_count == 0 {\n            return Err(GroupError::NoChannelOrShield);\n        }\n\n        // Any group can either contain channel ios or a shield IO.\n        // (An active shield requires its own sampling capacitor)\n        if shield_count != 0 && channel_count != 0 {\n            return Err(GroupError::MixedChannelAndShield);\n        }\n\n        // No more than one shield IO is allow per group and amongst all groups\n        if shield_count > 1 {\n            return Err(GroupError::MultipleShields);\n        }\n\n        Ok(())\n    }\n\n    /// Returns a reference to the first pin in the group, if configured.\n    pub fn pin1(&self) -> Option<&Pin<'d, T, G>> {\n        self.pin1.as_ref()\n    }\n\n    /// Returns a reference to the second pin in the group, if configured.\n    pub fn pin2(&self) -> Option<&Pin<'d, T, G>> {\n        self.pin2.as_ref()\n    }\n\n    /// Returns a reference to the third pin in the group, if configured.\n    pub fn pin3(&self) -> Option<&Pin<'d, T, G>> {\n        self.pin3.as_ref()\n    }\n\n    /// Returns a reference to the fourth pin in the group, if configured.\n    pub fn pin4(&self) -> Option<&Pin<'d, T, G>> {\n        self.pin4.as_ref()\n    }\n\n    fn sample_pins(&self) -> impl Iterator<Item = IOPin> + '_ {\n        self.pins_filtered(PinType::Sample)\n    }\n\n    fn shield_pins(&self) -> impl Iterator<Item = IOPin> + '_ {\n        self.pins_filtered(PinType::Shield)\n    }\n\n    fn channel_pins(&self) -> impl Iterator<Item = IOPin> + '_ {\n        self.pins_filtered(PinType::Channel)\n    }\n\n    fn pins_filtered(&self, pin_type: PinType) -> impl Iterator<Item = IOPin> + '_ {\n        self.pins().into_iter().filter_map(move |pin| {\n            pin.as_ref()\n                .and_then(|p| if p.role == pin_type { Some(p.tsc_io_pin) } else { None })\n        })\n    }\n\n    fn make_channel_ios_mask(&self) -> u32 {\n        self.channel_pins().fold(0, u32::bitor)\n    }\n\n    fn make_shield_ios_mask(&self) -> u32 {\n        self.shield_pins().fold(0, u32::bitor)\n    }\n\n    fn make_sample_ios_mask(&self) -> u32 {\n        self.sample_pins().fold(0, u32::bitor)\n    }\n\n    fn pins(&self) -> [&Option<Pin<'d, T, G>>; 4] {\n        [&self.pin1, &self.pin2, &self.pin3, &self.pin4]\n    }\n\n    fn pins_mut(&mut self) -> [&mut Option<Pin<'d, T, G>>; 4] {\n        [&mut self.pin1, &mut self.pin2, &mut self.pin3, &mut self.pin4]\n    }\n}\n\n#[cfg(any(tsc_v2, tsc_v3))]\nmacro_rules! TSC_V2_V3_GUARD {\n    ($e:expr) => {{\n        #[cfg(any(tsc_v2, tsc_v3))]\n        {\n            $e\n        }\n        #[cfg(not(any(tsc_v2, tsc_v3)))]\n        {\n            compile_error!(\"Group 7 is not supported in this TSC version\")\n        }\n    }};\n}\n\n#[cfg(tsc_v3)]\nmacro_rules! TSC_V3_GUARD {\n    ($e:expr) => {{\n        #[cfg(tsc_v3)]\n        {\n            $e\n        }\n        #[cfg(not(tsc_v3))]\n        {\n            compile_error!(\"Group 8 is not supported in this TSC version\")\n        }\n    }};\n}\n\nmacro_rules! trait_to_io_pin {\n    (G1IO1Pin) => {\n        IOPin::Group1Io1\n    };\n    (G1IO2Pin) => {\n        IOPin::Group1Io2\n    };\n    (G1IO3Pin) => {\n        IOPin::Group1Io3\n    };\n    (G1IO4Pin) => {\n        IOPin::Group1Io4\n    };\n\n    (G2IO1Pin) => {\n        IOPin::Group2Io1\n    };\n    (G2IO2Pin) => {\n        IOPin::Group2Io2\n    };\n    (G2IO3Pin) => {\n        IOPin::Group2Io3\n    };\n    (G2IO4Pin) => {\n        IOPin::Group2Io4\n    };\n\n    (G3IO1Pin) => {\n        IOPin::Group3Io1\n    };\n    (G3IO2Pin) => {\n        IOPin::Group3Io2\n    };\n    (G3IO3Pin) => {\n        IOPin::Group3Io3\n    };\n    (G3IO4Pin) => {\n        IOPin::Group3Io4\n    };\n\n    (G4IO1Pin) => {\n        IOPin::Group4Io1\n    };\n    (G4IO2Pin) => {\n        IOPin::Group4Io2\n    };\n    (G4IO3Pin) => {\n        IOPin::Group4Io3\n    };\n    (G4IO4Pin) => {\n        IOPin::Group4Io4\n    };\n\n    (G5IO1Pin) => {\n        IOPin::Group5Io1\n    };\n    (G5IO2Pin) => {\n        IOPin::Group5Io2\n    };\n    (G5IO3Pin) => {\n        IOPin::Group5Io3\n    };\n    (G5IO4Pin) => {\n        IOPin::Group5Io4\n    };\n\n    (G6IO1Pin) => {\n        IOPin::Group6Io1\n    };\n    (G6IO2Pin) => {\n        IOPin::Group6Io2\n    };\n    (G6IO3Pin) => {\n        IOPin::Group6Io3\n    };\n    (G6IO4Pin) => {\n        IOPin::Group6Io4\n    };\n\n    (G7IO1Pin) => {\n        TSC_V2_V3_GUARD!(IOPin::Group7Io1)\n    };\n    (G7IO2Pin) => {\n        TSC_V2_V3_GUARD!(IOPin::Group7Io2)\n    };\n    (G7IO3Pin) => {\n        TSC_V2_V3_GUARD!(IOPin::Group7Io3)\n    };\n    (G7IO4Pin) => {\n        TSC_V2_V3_GUARD!(IOPin::Group7Io4)\n    };\n\n    (G8IO1Pin) => {\n        TSC_V3_GUARD!(IOPin::Group8Io1)\n    };\n    (G8IO2Pin) => {\n        TSC_V3_GUARD!(IOPin::Group8Io2)\n    };\n    (G8IO3Pin) => {\n        TSC_V3_GUARD!(IOPin::Group8Io3)\n    };\n    (G8IO4Pin) => {\n        TSC_V3_GUARD!(IOPin::Group8Io4)\n    };\n}\n\nmacro_rules! impl_set_io {\n    ($method:ident, $group:ident, $trait:ident, $index:expr) => {\n        #[doc = concat!(\"Create a new pin1 for \", stringify!($group), \" TSC group instance.\")]\n        pub fn $method<Role: pin_roles::Role>(&mut self, pin: Peri<'d, impl $trait<T>>) -> IOPinWithRole<$group, Role> {\n            critical_section::with(|_| {\n                pin.set_low();\n                set_as_af!(pin, AfType::output(Role::output_type(), Speed::VeryHigh));\n                let tsc_io_pin = trait_to_io_pin!($trait);\n                let new_pin = Pin {\n                    _pin: Flex::new(pin),\n                    role: Role::pin_type(),\n                    tsc_io_pin,\n                    phantom: PhantomData,\n                };\n                *self.pin_group.pins_mut()[$index] = Some(new_pin);\n                IOPinWithRole {\n                    pin: tsc_io_pin,\n                    phantom: PhantomData,\n                }\n            })\n        }\n    };\n}\n\nmacro_rules! group_impl {\n    ($group:ident, $trait1:ident, $trait2:ident, $trait3:ident, $trait4:ident) => {\n        impl<'d, T: Instance, R1: pin_roles::Role, R2: pin_roles::Role, R3: pin_roles::Role, R4: pin_roles::Role>\n            PinGroupWithRoles<'d, T, $group, R1, R2, R3, R4>\n        {\n            impl_set_io!(set_io1, $group, $trait1, 0);\n            impl_set_io!(set_io2, $group, $trait2, 1);\n            impl_set_io!(set_io3, $group, $trait3, 2);\n            impl_set_io!(set_io4, $group, $trait4, 3);\n        }\n    };\n}\n\ngroup_impl!(G1, G1IO1Pin, G1IO2Pin, G1IO3Pin, G1IO4Pin);\ngroup_impl!(G2, G2IO1Pin, G2IO2Pin, G2IO3Pin, G2IO4Pin);\ngroup_impl!(G3, G3IO1Pin, G3IO2Pin, G3IO3Pin, G3IO4Pin);\ngroup_impl!(G4, G4IO1Pin, G4IO2Pin, G4IO3Pin, G4IO4Pin);\ngroup_impl!(G5, G5IO1Pin, G5IO2Pin, G5IO3Pin, G5IO4Pin);\ngroup_impl!(G6, G6IO1Pin, G6IO2Pin, G6IO3Pin, G6IO4Pin);\n#[cfg(any(tsc_v2, tsc_v3))]\ngroup_impl!(G7, G7IO1Pin, G7IO2Pin, G7IO3Pin, G7IO4Pin);\n#[cfg(tsc_v3)]\ngroup_impl!(G8, G8IO1Pin, G8IO2Pin, G8IO3Pin, G8IO4Pin);\n\n/// Group 1 marker type.\n#[derive(Clone, Copy, Debug)]\npub enum G1 {}\n/// Group 2 marker type.\n#[derive(Clone, Copy, Debug)]\npub enum G2 {}\n/// Group 3 marker type.\n#[derive(Clone, Copy, Debug)]\npub enum G3 {}\n/// Group 4 marker type.\n#[derive(Clone, Copy, Debug)]\npub enum G4 {}\n/// Group 5 marker type.\n#[derive(Clone, Copy, Debug)]\npub enum G5 {}\n/// Group 6 marker type.\n#[derive(Clone, Copy, Debug)]\npub enum G6 {}\n/// Group 7 marker type.\n#[derive(Clone, Copy, Debug)]\npub enum G7 {}\n/// Group 8 marker type.\n#[derive(Clone, Copy, Debug)]\npub enum G8 {}\n\n/// Represents the collection of pin groups for the Touch Sensing Controller (TSC).\n///\n/// Each field corresponds to a specific group of TSC pins:\n#[allow(missing_docs)]\npub struct PinGroups<'d, T: Instance> {\n    pub g1: Option<PinGroup<'d, T, G1>>,\n    pub g2: Option<PinGroup<'d, T, G2>>,\n    pub g3: Option<PinGroup<'d, T, G3>>,\n    pub g4: Option<PinGroup<'d, T, G4>>,\n    pub g5: Option<PinGroup<'d, T, G5>>,\n    pub g6: Option<PinGroup<'d, T, G6>>,\n    #[cfg(any(tsc_v2, tsc_v3))]\n    pub g7: Option<PinGroup<'d, T, G7>>,\n    #[cfg(tsc_v3)]\n    pub g8: Option<PinGroup<'d, T, G8>>,\n}\n\nimpl<'d, T: Instance> PinGroups<'d, T> {\n    pub(super) fn check(&self) -> Result<(), GroupError> {\n        let mut shield_count = 0;\n\n        // Helper function to check a single group\n        fn check_group<C, T: Instance>(\n            group: &Option<PinGroup<'_, T, C>>,\n            shield_count: &mut u32,\n        ) -> Result<(), GroupError> {\n            if let Some(group) = group {\n                group.check_group()?;\n                if group.contains_exactly_one_shield_pin() {\n                    *shield_count += 1;\n                    if *shield_count > 1 {\n                        return Err(GroupError::MultipleShields);\n                    }\n                }\n            }\n            Ok(())\n        }\n\n        // Check each group\n        check_group(&self.g1, &mut shield_count)?;\n        check_group(&self.g2, &mut shield_count)?;\n        check_group(&self.g3, &mut shield_count)?;\n        check_group(&self.g4, &mut shield_count)?;\n        check_group(&self.g5, &mut shield_count)?;\n        check_group(&self.g6, &mut shield_count)?;\n        #[cfg(any(tsc_v2, tsc_v3))]\n        check_group(&self.g7, &mut shield_count)?;\n        #[cfg(tsc_v3)]\n        check_group(&self.g8, &mut shield_count)?;\n\n        Ok(())\n    }\n\n    pub(super) fn make_channel_ios_mask(&self) -> u32 {\n        #[allow(unused_mut)]\n        let mut mask = self.g1.as_ref().map_or(0, |g| g.make_channel_ios_mask())\n            | self.g2.as_ref().map_or(0, |g| g.make_channel_ios_mask())\n            | self.g3.as_ref().map_or(0, |g| g.make_channel_ios_mask())\n            | self.g4.as_ref().map_or(0, |g| g.make_channel_ios_mask())\n            | self.g5.as_ref().map_or(0, |g| g.make_channel_ios_mask())\n            | self.g6.as_ref().map_or(0, |g| g.make_channel_ios_mask());\n        #[cfg(any(tsc_v2, tsc_v3))]\n        {\n            mask |= self.g7.as_ref().map_or(0, |g| g.make_channel_ios_mask());\n        }\n        #[cfg(tsc_v3)]\n        {\n            mask |= self.g8.as_ref().map_or(0, |g| g.make_channel_ios_mask());\n        }\n        mask\n    }\n\n    pub(super) fn make_shield_ios_mask(&self) -> u32 {\n        #[allow(unused_mut)]\n        let mut mask = self.g1.as_ref().map_or(0, |g| g.make_shield_ios_mask())\n            | self.g2.as_ref().map_or(0, |g| g.make_shield_ios_mask())\n            | self.g3.as_ref().map_or(0, |g| g.make_shield_ios_mask())\n            | self.g4.as_ref().map_or(0, |g| g.make_shield_ios_mask())\n            | self.g5.as_ref().map_or(0, |g| g.make_shield_ios_mask())\n            | self.g6.as_ref().map_or(0, |g| g.make_shield_ios_mask());\n        #[cfg(any(tsc_v2, tsc_v3))]\n        {\n            mask |= self.g7.as_ref().map_or(0, |g| g.make_shield_ios_mask());\n        }\n        #[cfg(tsc_v3)]\n        {\n            mask |= self.g8.as_ref().map_or(0, |g| g.make_shield_ios_mask());\n        }\n        mask\n    }\n\n    pub(super) fn make_sample_ios_mask(&self) -> u32 {\n        #[allow(unused_mut)]\n        let mut mask = self.g1.as_ref().map_or(0, |g| g.make_sample_ios_mask())\n            | self.g2.as_ref().map_or(0, |g| g.make_sample_ios_mask())\n            | self.g3.as_ref().map_or(0, |g| g.make_sample_ios_mask())\n            | self.g4.as_ref().map_or(0, |g| g.make_sample_ios_mask())\n            | self.g5.as_ref().map_or(0, |g| g.make_sample_ios_mask())\n            | self.g6.as_ref().map_or(0, |g| g.make_sample_ios_mask());\n        #[cfg(any(tsc_v2, tsc_v3))]\n        {\n            mask |= self.g7.as_ref().map_or(0, |g| g.make_sample_ios_mask());\n        }\n        #[cfg(tsc_v3)]\n        {\n            mask |= self.g8.as_ref().map_or(0, |g| g.make_sample_ios_mask());\n        }\n        mask\n    }\n}\n\nimpl<'d, T: Instance> Default for PinGroups<'d, T> {\n    fn default() -> Self {\n        Self {\n            g1: None,\n            g2: None,\n            g3: None,\n            g4: None,\n            g5: None,\n            g6: None,\n            #[cfg(any(tsc_v2, tsc_v3))]\n            g7: None,\n            #[cfg(tsc_v3)]\n            g8: None,\n        }\n    }\n}\n\npin_trait!(G1IO1Pin, Instance);\npin_trait!(G1IO2Pin, Instance);\npin_trait!(G1IO3Pin, Instance);\npin_trait!(G1IO4Pin, Instance);\n\npin_trait!(G2IO1Pin, Instance);\npin_trait!(G2IO2Pin, Instance);\npin_trait!(G2IO3Pin, Instance);\npin_trait!(G2IO4Pin, Instance);\n\npin_trait!(G3IO1Pin, Instance);\npin_trait!(G3IO2Pin, Instance);\npin_trait!(G3IO3Pin, Instance);\npin_trait!(G3IO4Pin, Instance);\n\npin_trait!(G4IO1Pin, Instance);\npin_trait!(G4IO2Pin, Instance);\npin_trait!(G4IO3Pin, Instance);\npin_trait!(G4IO4Pin, Instance);\n\npin_trait!(G5IO1Pin, Instance);\npin_trait!(G5IO2Pin, Instance);\npin_trait!(G5IO3Pin, Instance);\npin_trait!(G5IO4Pin, Instance);\n\npin_trait!(G6IO1Pin, Instance);\npin_trait!(G6IO2Pin, Instance);\npin_trait!(G6IO3Pin, Instance);\npin_trait!(G6IO4Pin, Instance);\n\npin_trait!(G7IO1Pin, Instance);\npin_trait!(G7IO2Pin, Instance);\npin_trait!(G7IO3Pin, Instance);\npin_trait!(G7IO4Pin, Instance);\n\npin_trait!(G8IO1Pin, Instance);\npin_trait!(G8IO2Pin, Instance);\npin_trait!(G8IO3Pin, Instance);\npin_trait!(G8IO4Pin, Instance);\n"
  },
  {
    "path": "embassy-stm32/src/tsc/tsc.rs",
    "content": "use core::future::poll_fn;\nuse core::marker::PhantomData;\nuse core::ops::BitOr;\nuse core::task::Poll;\n\nuse embassy_hal_internal::Peri;\n\nuse super::acquisition_banks::*;\nuse super::config::*;\nuse super::errors::*;\nuse super::io_pin::*;\nuse super::pin_groups::*;\nuse super::types::*;\nuse super::{Instance, InterruptHandler, TSC_NUM_GROUPS};\nuse crate::interrupt::typelevel::Interrupt;\nuse crate::mode::{Async, Blocking, Mode as PeriMode};\nuse crate::{interrupt, rcc};\n\n/// Internal structure holding masks for different types of TSC IOs.\n///\n/// These masks are used during the initial configuration of the TSC peripheral\n/// and for validating pin types during operations like creating acquisition banks.\nstruct IOMasks {\n    /// Mask representing all configured channel IOs\n    channel_ios: u32,\n    /// Mask representing all configured shield IOs\n    shield_ios: u32,\n    /// Mask representing all configured sampling IOs\n    sampling_ios: u32,\n}\n\n/// TSC driver\npub struct Tsc<'d, T: Instance, K: PeriMode> {\n    _peri: Peri<'d, T>,\n    _pin_groups: PinGroups<'d, T>,\n    state: State,\n    config: Config,\n    masks: IOMasks,\n    _kind: PhantomData<K>,\n}\n\nimpl<'d, T: Instance, K: PeriMode> Tsc<'d, T, K> {\n    // Helper method to check if a pin is a channel pin\n    fn is_channel_pin(&self, pin: IOPin) -> bool {\n        (self.masks.channel_ios & pin) != 0\n    }\n\n    /// Get the status of all groups involved in a AcquisitionBank\n    pub fn get_acquisition_bank_status(&self, bank: &AcquisitionBank) -> AcquisitionBankStatus {\n        let mut bank_status = AcquisitionBankStatus::default();\n        for pin in bank.pins_iterator() {\n            let group = pin.group();\n            let group_status = self.group_get_status(group);\n            let index: usize = group.into();\n            bank_status.groups[index] = Some(group_status);\n        }\n        bank_status\n    }\n\n    /// Get the values for all channels involved in a AcquisitionBank\n    pub fn get_acquisition_bank_values(&self, bank: &AcquisitionBank) -> AcquisitionBankReadings {\n        let mut bank_readings = AcquisitionBankReadings::default();\n        for pin in bank.pins_iterator() {\n            let group = pin.group();\n            let value = self.group_get_value(group);\n            let reading = ChannelReading {\n                sensor_value: value,\n                tsc_pin: pin,\n            };\n            let index: usize = group.into();\n            bank_readings.groups[index] = Some(reading);\n        }\n        bank_readings\n    }\n\n    /// Creates a new TSC acquisition bank from the provided pin configuration.\n    ///\n    /// This method creates a `AcquisitionBank` that can be used for efficient,\n    /// repeated TSC acquisitions. It automatically generates the appropriate mask\n    /// for the provided pins.\n    ///\n    /// # Note on TSC Hardware Limitation\n    ///\n    /// The TSC hardware can only read one channel pin from each TSC group per acquisition.\n    ///\n    /// # Arguments\n    /// * `acquisition_bank_pins` - The pin configuration for the acquisition bank.\n    ///\n    /// # Returns\n    /// A new `AcquisitionBank` instance.\n    ///\n    /// # Example\n    ///\n    /// ```\n    /// let tsc = // ... initialize TSC\n    /// let tsc_sensor1: tsc::IOPinWithRole<G1, tsc_pin_roles::Channel> = ...;\n    /// let tsc_sensor2: tsc::IOPinWithRole<G2, tsc_pin_roles::Channel> = ...;\n    ///\n    /// let bank = tsc.create_acquisition_bank(AcquisitionBankPins {\n    ///     g1_pin: Some(tsc_sensor1),\n    ///     g2_pin: Some(tsc_sensor2),\n    ///     ..Default::default()\n    /// });\n    ///\n    /// // Use the bank for acquisitions\n    /// tsc.set_active_channels_bank(&bank);\n    /// tsc.start();\n    /// // ... perform acquisition ...\n    /// ```\n    pub fn create_acquisition_bank(&self, acquisition_bank_pins: AcquisitionBankPins) -> AcquisitionBank {\n        let bank_mask = acquisition_bank_pins.iter().fold(0u32, BitOr::bitor);\n\n        AcquisitionBank {\n            pins: acquisition_bank_pins,\n            mask: bank_mask,\n        }\n    }\n\n    fn make_channels_mask<Itt>(&self, channels: Itt) -> Result<u32, AcquisitionBankError>\n    where\n        Itt: IntoIterator<Item = IOPin>,\n    {\n        let mut group_mask = 0u32;\n        let mut channel_mask = 0u32;\n\n        for channel in channels {\n            if !self.is_channel_pin(channel) {\n                return Err(AcquisitionBankError::InvalidChannelPin);\n            }\n\n            let group = channel.group();\n            let group_bit: u32 = 1 << Into::<usize>::into(group);\n            if group_mask & group_bit != 0 {\n                return Err(AcquisitionBankError::MultipleChannelsPerGroup);\n            }\n\n            group_mask |= group_bit;\n            channel_mask |= channel;\n        }\n\n        Ok(channel_mask)\n    }\n\n    /// Sets the active channels for the next TSC acquisition.\n    ///\n    /// This is a low-level method that directly sets the channel mask. For most use cases,\n    /// consider using `set_active_channels_bank` with a `AcquisitionBank` instead, which\n    /// provides a higher-level interface and additional safety checks.\n    ///\n    /// This method configures which sensor channels will be read during the next\n    /// touch sensing acquisition cycle. It should be called before starting a new\n    /// acquisition with the start() method.\n    ///\n    /// # Arguments\n    /// * `mask` - A 32-bit mask where each bit represents a channel. Set bits indicate\n    ///            active channels.\n    ///\n    /// # Note\n    /// Only one pin from each TSC group can be read for each acquisition. This method\n    /// does not perform checks to ensure this limitation is met. Incorrect masks may\n    /// lead to unexpected behavior.\n    ///\n    /// # Safety\n    /// This method doesn't perform extensive checks on the provided mask. Ensure that\n    /// the mask is valid and adheres to hardware limitations to avoid undefined behavior.\n    pub fn set_active_channels_mask(&mut self, mask: u32) {\n        T::regs().ioccr().write(|w| w.0 = mask | self.masks.shield_ios);\n    }\n\n    /// Convenience method for setting active channels directly from a slice of tsc::IOPin.\n    /// This method performs safety checks but is less efficient for repeated use.\n    pub fn set_active_channels(&mut self, channels: &[IOPin]) -> Result<(), AcquisitionBankError> {\n        let mask = self.make_channels_mask(channels.iter().cloned())?;\n        self.set_active_channels_mask(mask);\n        Ok(())\n    }\n\n    /// Sets the active channels for the next TSC acquisition using a pre-configured acquisition bank.\n    ///\n    /// This method efficiently configures the TSC peripheral to read the channels specified\n    /// in the provided `AcquisitionBank`. It's the recommended way to set up\n    /// channel configurations for acquisition, especially when using the same set of channels repeatedly.\n    ///\n    /// # Arguments\n    ///\n    /// * `bank` - A reference to a `AcquisitionBank` containing the pre-configured\n    ///            TSC channel mask.\n    ///\n    /// # Example\n    ///\n    /// ```\n    /// let tsc_sensor1: tsc::IOPinWithRole<G1, Channel> = ...;\n    /// let tsc_sensor2: tsc::IOPinWithRole<G5, Channel> = ...;\n    /// let mut touch_controller: Tsc<'_, TSC, Async> = ...;\n    /// let bank = touch_controller.create_acquisition_bank(AcquisitionBankPins {\n    ///     g1_pin: Some(tsc_sensor1),\n    ///     g2_pin: Some(tsc_sensor2),\n    ///     ..Default::default()\n    /// });\n    ///\n    /// touch_controller.set_active_channels_bank(&bank);\n    /// touch_controller.start();\n    /// // ... perform acquisition ...\n    /// ```\n    ///\n    /// This method should be called before starting a new acquisition with the `start()` method.\n    pub fn set_active_channels_bank(&mut self, bank: &AcquisitionBank) {\n        self.set_active_channels_mask(bank.mask)\n    }\n\n    fn extract_groups(io_mask: u32) -> u32 {\n        let mut groups: u32 = 0;\n        for idx in 0..TSC_NUM_GROUPS {\n            if io_mask & (0x0F << (idx * 4)) != 0 {\n                groups |= 1 << idx\n            }\n        }\n        groups\n    }\n\n    fn new_inner(peri: Peri<'d, T>, pin_groups: PinGroups<'d, T>, config: Config) -> Result<Self, GroupError> {\n        pin_groups.check()?;\n\n        let masks = IOMasks {\n            channel_ios: pin_groups.make_channel_ios_mask(),\n            shield_ios: pin_groups.make_shield_ios_mask(),\n            sampling_ios: pin_groups.make_sample_ios_mask(),\n        };\n\n        rcc::enable_and_reset::<T>();\n\n        T::regs().cr().modify(|w| {\n            w.set_tsce(true);\n            w.set_ctph(config.ct_pulse_high_length.into());\n            w.set_ctpl(config.ct_pulse_low_length.into());\n            w.set_sse(config.spread_spectrum);\n            // Prevent invalid configuration for pulse generator prescaler\n            if config.ct_pulse_low_length == ChargeTransferPulseCycle::_1\n                && (config.pulse_generator_prescaler == PGPrescalerDivider::_1\n                    || config.pulse_generator_prescaler == PGPrescalerDivider::_2)\n            {\n                w.set_pgpsc(PGPrescalerDivider::_4.into());\n            } else if config.ct_pulse_low_length == ChargeTransferPulseCycle::_2\n                && config.pulse_generator_prescaler == PGPrescalerDivider::_1\n            {\n                w.set_pgpsc(PGPrescalerDivider::_2.into());\n            } else {\n                w.set_pgpsc(config.pulse_generator_prescaler.into());\n            }\n            w.set_ssd(config.spread_spectrum_deviation.into());\n            w.set_sspsc(config.spread_spectrum_prescaler);\n\n            w.set_mcv(config.max_count_value.into());\n            w.set_syncpol(config.synchro_pin_polarity);\n            w.set_am(config.acquisition_mode);\n        });\n\n        // Set IO configuration\n        // Disable Schmitt trigger hysteresis on all used TSC IOs\n        T::regs()\n            .iohcr()\n            .write(|w| w.0 = !(masks.channel_ios | masks.shield_ios | masks.sampling_ios));\n\n        // Set channel and shield IOs\n        T::regs().ioccr().write(|w| w.0 = masks.channel_ios | masks.shield_ios);\n\n        // Set sampling IOs\n        T::regs().ioscr().write(|w| w.0 = masks.sampling_ios);\n\n        // Set the groups to be acquired\n        // Lower bits of `iogcsr` are for enabling groups, while the higher bits are for reading\n        // status of acquisiton for a group, see method `Tsc::group_get_status`.\n        T::regs()\n            .iogcsr()\n            .write(|w| w.0 = Self::extract_groups(masks.channel_ios));\n\n        // Disable interrupts\n        T::regs().ier().modify(|w| {\n            w.set_eoaie(false);\n            w.set_mceie(false);\n        });\n\n        // Clear flags\n        T::regs().icr().modify(|w| {\n            w.set_eoaic(true);\n            w.set_mceic(true);\n        });\n\n        unsafe {\n            T::Interrupt::enable();\n        }\n\n        Ok(Self {\n            _peri: peri,\n            _pin_groups: pin_groups,\n            state: State::Ready,\n            config,\n            masks,\n            _kind: PhantomData,\n        })\n    }\n\n    /// Start charge transfer acquisition\n    pub fn start(&mut self) {\n        self.state = State::Busy;\n\n        // Disable interrupts\n        T::regs().ier().modify(|w| {\n            w.set_eoaie(false);\n            w.set_mceie(false);\n        });\n\n        // Clear flags\n        T::regs().icr().modify(|w| {\n            w.set_eoaic(true);\n            w.set_mceic(true);\n        });\n\n        // Set the touch sensing IOs not acquired to the default mode\n        T::regs().cr().modify(|w| {\n            w.set_iodef(self.config.io_default_mode);\n        });\n\n        // Start the acquisition\n        T::regs().cr().modify(|w| {\n            w.set_start(true);\n        });\n    }\n\n    /// Stop charge transfer acquisition\n    pub fn stop(&mut self) {\n        T::regs().cr().modify(|w| {\n            w.set_start(false);\n        });\n\n        // Set the touch sensing IOs in low power mode\n        T::regs().cr().modify(|w| {\n            w.set_iodef(false);\n        });\n\n        // Clear flags\n        T::regs().icr().modify(|w| {\n            w.set_eoaic(true);\n            w.set_mceic(true);\n        });\n\n        self.state = State::Ready;\n    }\n\n    /// Get current state of acquisition\n    pub fn get_state(&mut self) -> State {\n        if self.state == State::Busy && T::regs().isr().read().eoaf() {\n            if T::regs().isr().read().mcef() {\n                self.state = State::Error\n            } else {\n                self.state = State::Ready\n            }\n        }\n        self.state\n    }\n\n    /// Get the individual group status to check acquisition complete\n    pub fn group_get_status(&self, index: Group) -> GroupStatus {\n        // Status bits are set by hardware when the acquisition on the corresponding\n        // enabled analog IO group is complete, cleared when new acquisition is started\n        let status = match index {\n            Group::One => T::regs().iogcsr().read().g1s(),\n            Group::Two => T::regs().iogcsr().read().g2s(),\n            Group::Three => T::regs().iogcsr().read().g3s(),\n            Group::Four => T::regs().iogcsr().read().g4s(),\n            Group::Five => T::regs().iogcsr().read().g5s(),\n            Group::Six => T::regs().iogcsr().read().g6s(),\n            #[cfg(any(tsc_v2, tsc_v3))]\n            Group::Seven => T::regs().iogcsr().read().g7s(),\n            #[cfg(tsc_v3)]\n            Group::Eight => T::regs().iogcsr().read().g8s(),\n        };\n        match status {\n            true => GroupStatus::Complete,\n            false => GroupStatus::Ongoing,\n        }\n    }\n\n    /// Get the count for the acquisiton, valid once group status is set\n    pub fn group_get_value(&self, index: Group) -> u16 {\n        T::regs().iogcr(index.into()).read().cnt()\n    }\n\n    /// Discharge the IOs for subsequent acquisition\n    pub fn discharge_io(&mut self, status: bool) {\n        // Set the touch sensing IOs in low power mode\n        T::regs().cr().modify(|w| {\n            w.set_iodef(!status);\n        });\n    }\n}\n\nimpl<'d, T: Instance, K: PeriMode> Drop for Tsc<'d, T, K> {\n    fn drop(&mut self) {\n        rcc::disable::<T>();\n    }\n}\n\nimpl<'d, T: Instance> Tsc<'d, T, Async> {\n    /// Create a Tsc instance that can be awaited for completion\n    pub fn new_async(\n        peri: Peri<'d, T>,\n        pin_groups: PinGroups<'d, T>,\n        config: Config,\n        _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd,\n    ) -> Result<Self, GroupError> {\n        Self::new_inner(peri, pin_groups, config)\n    }\n\n    /// Asyncronously wait for the end of an acquisition\n    pub async fn pend_for_acquisition(&mut self) {\n        poll_fn(|cx| match self.get_state() {\n            State::Busy => {\n                T::waker().register(cx.waker());\n                T::regs().ier().write(|w| w.set_eoaie(true));\n                if self.get_state() != State::Busy {\n                    T::regs().ier().write(|w| w.set_eoaie(false));\n                    return Poll::Ready(());\n                }\n                Poll::Pending\n            }\n            _ => {\n                T::regs().ier().write(|w| w.set_eoaie(false));\n                Poll::Ready(())\n            }\n        })\n        .await;\n    }\n}\n\nimpl<'d, T: Instance> Tsc<'d, T, Blocking> {\n    /// Create a Tsc instance that must be polled for completion\n    pub fn new_blocking(peri: Peri<'d, T>, pin_groups: PinGroups<'d, T>, config: Config) -> Result<Self, GroupError> {\n        Self::new_inner(peri, pin_groups, config)\n    }\n\n    /// Wait for end of acquisition\n    pub fn poll_for_acquisition(&mut self) {\n        while self.get_state() == State::Busy {}\n    }\n}\n"
  },
  {
    "path": "embassy-stm32/src/tsc/types.rs",
    "content": "/// Peripheral state\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\n#[derive(PartialEq, Clone, Copy)]\npub enum State {\n    /// Peripheral is being setup or reconfigured\n    Reset,\n    /// Ready to start acquisition\n    Ready,\n    /// In process of sensor acquisition\n    Busy,\n    /// Error occured during acquisition\n    Error,\n}\n\n/// Individual group status checked after acquisition reported as complete\n/// For groups with multiple channel pins, may take longer because acquisitions\n/// are done sequentially. Check this status before pulling count for each\n/// sampled channel\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\n#[derive(PartialEq, Clone, Copy)]\npub enum GroupStatus {\n    /// Acquisition for channel still in progress\n    Ongoing,\n    /// Acquisition either not started or complete\n    Complete,\n}\n\n/// Group identifier used to interrogate status\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\n#[allow(missing_docs)]\n#[derive(PartialEq, Clone, Copy)]\npub enum Group {\n    One,\n    Two,\n    Three,\n    Four,\n    Five,\n    Six,\n    #[cfg(any(tsc_v2, tsc_v3))]\n    Seven,\n    #[cfg(tsc_v3)]\n    Eight,\n}\n\nimpl Into<usize> for Group {\n    fn into(self) -> usize {\n        match self {\n            Group::One => 0,\n            Group::Two => 1,\n            Group::Three => 2,\n            Group::Four => 3,\n            Group::Five => 4,\n            Group::Six => 5,\n            #[cfg(any(tsc_v2, tsc_v3))]\n            Group::Seven => 6,\n            #[cfg(tsc_v3)]\n            Group::Eight => 7,\n        }\n    }\n}\n\n/// Error returned when attempting to create a Group from an invalid numeric value.\n#[derive(Debug, Clone, Copy, PartialEq, Eq)]\npub struct InvalidGroupError {\n    invalid_value: usize,\n}\n\nimpl InvalidGroupError {\n    #[allow(missing_docs)]\n    pub fn new(value: usize) -> Self {\n        Self { invalid_value: value }\n    }\n}\n\nimpl TryFrom<usize> for Group {\n    type Error = InvalidGroupError;\n\n    fn try_from(value: usize) -> Result<Self, Self::Error> {\n        match value {\n            0 => Ok(Group::One),\n            1 => Ok(Group::Two),\n            2 => Ok(Group::Three),\n            3 => Ok(Group::Four),\n            4 => Ok(Group::Five),\n            5 => Ok(Group::Six),\n            #[cfg(any(tsc_v2, tsc_v3))]\n            6 => Ok(Group::Two),\n            #[cfg(tsc_v3)]\n            7 => Ok(Group::Two),\n            n => Err(InvalidGroupError::new(n)),\n        }\n    }\n}\n"
  },
  {
    "path": "embassy-stm32/src/ucpd.rs",
    "content": "//! USB Type-C/USB Power Delivery Interface (UCPD)\n\n// Implementation Notes\n//\n// As of Feb. 2024 the UCPD peripheral is availalbe on: G0, G4, H5, L5, U5\n//\n// Cube HAL LL Driver (g0):\n// https://github.com/STMicroelectronics/stm32g0xx_hal_driver/blob/v1.4.6/Inc/stm32g0xx_ll_ucpd.h\n// https://github.com/STMicroelectronics/stm32g0xx_hal_driver/blob/v1.4.6/Src/stm32g0xx_ll_ucpd.c\n// Except for a the `LL_UCPD_RxAnalogFilterEnable/Disable()` functions the Cube HAL implementation of\n// all families is the same.\n//\n// Dead battery pull-down resistors functionality is enabled by default on startup and must\n// be disabled by setting a bit in PWR/SYSCFG registers. The exact name and location for that\n// bit is different for each familily.\n\nuse core::future::poll_fn;\nuse core::marker::PhantomData;\nuse core::sync::atomic::{AtomicBool, Ordering};\nuse core::task::Poll;\n\nuse embassy_hal_internal::PeripheralType;\nuse embassy_hal_internal::drop::OnDrop;\nuse embassy_sync::waitqueue::AtomicWaker;\n\nuse crate::dma::{ChannelAndRequest, TransferOptions};\nuse crate::interrupt::typelevel::Interrupt;\nuse crate::pac::ucpd::vals::{Anamode, Ccenable, PscUsbpdclk, Txmode};\npub use crate::pac::ucpd::vals::{Phyccsel as CcSel, Rxordset, TypecVstateCc as CcVState};\nuse crate::rcc::{self, RccPeripheral};\nuse crate::{Peri, interrupt};\n\npub(crate) fn init(\n    _cs: critical_section::CriticalSection,\n    #[cfg(all(peri_ucpd1, not(stm32n6)))] ucpd1_db_enable: bool,\n    #[cfg(peri_ucpd2)] ucpd2_db_enable: bool,\n) {\n    #[cfg(stm32g0x1)]\n    {\n        // according to RM0444 (STM32G0x1) section 8.1.1:\n        // when UCPD is disabled setting the strobe will disable dead battery\n        // (which is enabled after reset) but if UCPD is enabled, setting the\n        // strobe will apply the CC pin configuration from the control register\n        // (which is why we need to be careful about when we call this)\n        crate::pac::SYSCFG.cfgr1().modify(|w| {\n            w.set_ucpd1_strobe(!ucpd1_db_enable);\n            w.set_ucpd2_strobe(!ucpd2_db_enable);\n        });\n    }\n\n    #[cfg(any(stm32g4, stm32l5))]\n    {\n        crate::pac::PWR.cr3().modify(|w| {\n            #[cfg(stm32g4)]\n            w.set_ucpd1_dbdis(!ucpd1_db_enable);\n            #[cfg(stm32l5)]\n            w.set_ucpd_dbdis(!ucpd1_db_enable);\n        })\n    }\n\n    #[cfg(any(stm32h5, stm32u5, stm32h7rs))]\n    {\n        crate::pac::PWR.ucpdr().modify(|w| {\n            w.set_ucpd_dbdis(!ucpd1_db_enable);\n        })\n    }\n}\n\n/// Pull-up or Pull-down resistor state of both CC lines.\n#[derive(Debug, Clone, Copy, PartialEq)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub enum CcPull {\n    /// Analog PHY for CC pin disabled.\n    Disabled,\n\n    /// Rd=5.1k pull-down resistor.\n    Sink,\n\n    /// Rp=56k pull-up resistor to indicate default USB power.\n    SourceDefaultUsb,\n\n    /// Rp=22k pull-up resistor to indicate support for up to 1.5A.\n    Source1_5A,\n\n    /// Rp=10k pull-up resistor to indicate support for up to 3.0A.\n    Source3_0A,\n}\n\n/// UCPD configuration\n#[non_exhaustive]\n#[derive(Copy, Clone, Debug)]\npub struct Config {\n    /// Receive SOP packets\n    pub sop: bool,\n    /// Receive SOP' packets\n    pub sop_prime: bool,\n    /// Receive SOP'' packets\n    pub sop_double_prime: bool,\n    /// Receive SOP'_Debug packets\n    pub sop_prime_debug: bool,\n    /// Receive SOP''_Debug packets\n    pub sop_double_prime_debug: bool,\n}\n\nimpl Default for Config {\n    fn default() -> Self {\n        Self {\n            sop: true,\n            sop_prime: false,\n            sop_double_prime: false,\n            sop_prime_debug: false,\n            sop_double_prime_debug: false,\n        }\n    }\n}\n\n/// UCPD driver.\npub struct Ucpd<'d, T: Instance> {\n    cc_phy: CcPhy<'d, T>,\n}\n\nimpl<'d, T: Instance> Ucpd<'d, T> {\n    /// Creates a new UCPD driver instance.\n    pub fn new(\n        _peri: Peri<'d, T>,\n        _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd,\n        cc1: Peri<'d, impl Cc1Pin<T>>,\n        cc2: Peri<'d, impl Cc2Pin<T>>,\n        config: Config,\n    ) -> Self {\n        cc1.set_as_analog();\n        cc2.set_as_analog();\n\n        rcc::enable_and_reset::<T>();\n        T::Interrupt::unpend();\n        unsafe { T::Interrupt::enable() };\n\n        let r = T::REGS;\n\n        #[cfg(stm32h5)]\n        r.cfgr2().write(|w| {\n            // Only takes effect, when UCPDEN=0.\n            w.set_rxafilten(true);\n        });\n\n        r.cfgr1().write(|w| {\n            // \"The receiver is designed to work in the clock frequency range from 6 to 18 MHz.\n            // However, the optimum performance is ensured in the range from 6 to 12 MHz\"\n            // UCPD is driven by HSI16 (16MHz internal oscillator), which we need to divide by 2.\n            w.set_psc_usbpdclk(PscUsbpdclk::DIV2);\n\n            // Prescaler to produce a target half-bit frequency of 600kHz which is required\n            // to produce transmit with a nominal nominal bit rate of 300Kbps+-10% using\n            // biphase mark coding (BMC, aka differential manchester coding).\n            // A divider of 13 gives the target frequency closest to spec (~615kHz, 1.625us).\n            w.set_hbitclkdiv(13 - 1);\n\n            // Time window for detecting non-idle (12-20us).\n            // 1.75us * 8 = 14us.\n            w.set_transwin(8 - 1);\n\n            // Time from the end of last bit of a Frame until the start of the first bit of the\n            // next Preamble (min 25us).\n            // 1.75us * 17 = ~30us\n            w.set_ifrgap(17 - 1);\n\n            // UNDOCUMENTED: This register can only be written while UCPDEN=0 (found by testing).\n            let rxordset = (config.sop as u16) << 0\n                | (config.sop_prime as u16) << 1\n                | (config.sop_double_prime as u16) << 2\n                // Hard reset\n                | 0x1 << 3\n                | (config.sop_prime_debug as u16) << 4\n                | (config.sop_double_prime_debug as u16) << 5;\n            w.set_rxordseten(rxordset);\n\n            // Enable DMA\n            w.set_txdmaen(true);\n            w.set_rxdmaen(true);\n\n            w.set_ucpden(true);\n        });\n\n        // Software trim according to RM0481, p. 2650/2668\n        #[cfg(stm32h5)]\n        {\n            let trim_rd_cc1 = unsafe { *(0x4002_242C as *const u32) & 0xF };\n            let trim_rd_cc2 = unsafe { ((*(0x4002_242C as *const u32)) >> 8) & 0xF };\n\n            r.cfgr3().write(|w| {\n                w.set_trim_cc1_rd(trim_rd_cc1 as u8);\n                w.set_trim_cc2_rd(trim_rd_cc2 as u8);\n            });\n        }\n\n        // Software trim according to RM0456, p. 3480/3462\n        #[cfg(stm32u5)]\n        {\n            let trim_rd_cc1 = unsafe { *(0x0BFA_0544 as *const u8) & 0xF };\n            let trim_rd_cc2 = unsafe { *(0x0BFA_0546 as *const u8) & 0xF };\n\n            r.cfgr3().write(|w| {\n                w.set_trim_cc1_rd(trim_rd_cc1);\n                w.set_trim_cc2_rd(trim_rd_cc2);\n            });\n        }\n\n        Self {\n            cc_phy: CcPhy { _lifetime: PhantomData },\n        }\n    }\n\n    /// Returns the TypeC CC PHY.\n    pub fn cc_phy(&mut self) -> &mut CcPhy<'d, T> {\n        &mut self.cc_phy\n    }\n\n    /// Splits the UCPD driver into a TypeC PHY to control and monitor CC voltage\n    /// and a Power Delivery (PD) PHY with receiver and transmitter.\n    pub fn split_pd_phy<RX, TX>(\n        self,\n        rx_dma: Peri<'d, RX>,\n        tx_dma: Peri<'d, TX>,\n        irqs: impl interrupt::typelevel::Binding<RX::Interrupt, crate::dma::InterruptHandler<RX>>\n        + interrupt::typelevel::Binding<TX::Interrupt, crate::dma::InterruptHandler<TX>>\n        + 'd,\n        cc_sel: CcSel,\n    ) -> (CcPhy<'d, T>, PdPhy<'d, T>)\n    where\n        RX: RxDma<T>,\n        TX: TxDma<T>,\n    {\n        let r = T::REGS;\n\n        // TODO: Currently only SOP messages are supported.\n        r.tx_ordsetr().write(|w| w.set_txordset(0b10001_11000_11000_11000));\n\n        // Enable the receiver on one of the two CC lines.\n        r.cr().modify(|w| w.set_phyccsel(cc_sel));\n\n        // Enable hard reset receive interrupt.\n        r.imr().modify(|w| w.set_rxhrstdetie(true));\n\n        // Enable PD packet reception\n        r.cr().modify(|w| w.set_phyrxen(true));\n\n        // Both parts must be dropped before the peripheral can be disabled.\n        T::state().drop_not_ready.store(true, Ordering::Relaxed);\n\n        let rx_dma = new_dma_nonopt!(rx_dma, irqs);\n        let tx_dma = new_dma_nonopt!(tx_dma, irqs);\n        (\n            self.cc_phy,\n            PdPhy {\n                _lifetime: PhantomData,\n                rx_dma,\n                tx_dma,\n            },\n        )\n    }\n}\n\n/// Control and monitoring of TypeC CC pin functionailty.\npub struct CcPhy<'d, T: Instance> {\n    _lifetime: PhantomData<&'d mut T>,\n}\n\nimpl<'d, T: Instance> Drop for CcPhy<'d, T> {\n    fn drop(&mut self) {\n        let r = T::REGS;\n        r.cr().modify(|w| {\n            w.set_cc1tcdis(true);\n            w.set_cc2tcdis(true);\n            w.set_ccenable(Ccenable::DISABLED);\n        });\n\n        // Check if the PdPhy part was dropped already.\n        let drop_not_ready = &T::state().drop_not_ready;\n        if drop_not_ready.load(Ordering::Relaxed) {\n            drop_not_ready.store(false, Ordering::Relaxed);\n        } else {\n            r.cfgr1().write(|w| w.set_ucpden(false));\n            rcc::disable::<T>();\n            T::Interrupt::disable();\n        }\n    }\n}\n\nimpl<'d, T: Instance> CcPhy<'d, T> {\n    /// Sets the pull-up/pull-down resistor values exposed on the CC pins.\n    pub fn set_pull(&mut self, cc_pull: CcPull) {\n        T::REGS.cr().modify(|w| {\n            w.set_anamode(if cc_pull == CcPull::Sink {\n                Anamode::SINK\n            } else {\n                Anamode::SOURCE\n            });\n            w.set_anasubmode(match cc_pull {\n                CcPull::SourceDefaultUsb => 1,\n                CcPull::Source1_5A => 2,\n                CcPull::Source3_0A => 3,\n                _ => 0,\n            });\n            w.set_ccenable(if cc_pull == CcPull::Disabled {\n                Ccenable::DISABLED\n            } else {\n                Ccenable::BOTH\n            });\n        });\n\n        // Software trim according to RM0481, p. 2650/2668\n        #[cfg(stm32h5)]\n        T::REGS.cfgr3().modify(|w| match cc_pull {\n            CcPull::Source1_5A => {\n                let trim_1a5_cc1 = unsafe { *(0x08FF_F844 as *const u32) & 0xF };\n                let trim_1a5_cc2 = unsafe { ((*(0x08FF_F844 as *const u32)) >> 16) & 0xF };\n\n                w.set_trim_cc1_rp(trim_1a5_cc1 as u8);\n                w.set_trim_cc2_rp(trim_1a5_cc2 as u8);\n            }\n            _ => {\n                let trim_3a0_cc1 = unsafe { (*(0x4002_242C as *const u32) >> 4) & 0xF };\n                let trim_3a0_cc2 = unsafe { ((*(0x4002_242C as *const u32)) >> 12) & 0xF };\n\n                w.set_trim_cc1_rp(trim_3a0_cc1 as u8);\n                w.set_trim_cc2_rp(trim_3a0_cc2 as u8);\n            }\n        });\n\n        // Software trim according to RM0456, p. 3480/3462\n        #[cfg(stm32u5)]\n        T::REGS.cfgr3().modify(|w| match cc_pull {\n            CcPull::Source1_5A => {\n                let trim_1a5_cc1 = unsafe { *(0x0BFA_07A7 as *const u8) & 0xF };\n                let trim_1a5_cc2 = unsafe { *(0x0BFA_07A8 as *const u8) & 0xF };\n\n                w.set_trim_cc1_rp(trim_1a5_cc1);\n                w.set_trim_cc2_rp(trim_1a5_cc2);\n            }\n            _ => {\n                let trim_3a0_cc1 = unsafe { *(0x0BFA_0545 as *const u8) & 0xF };\n                let trim_3a0_cc2 = unsafe { *(0x0BFA_0547 as *const u8) & 0xF };\n\n                w.set_trim_cc1_rp(trim_3a0_cc1);\n                w.set_trim_cc2_rp(trim_3a0_cc2);\n            }\n        });\n\n        // Disable dead-battery pull-down resistors which are enabled by default on boot.\n        critical_section::with(|cs| {\n            init(\n                cs,\n                #[cfg(not(stm32n6))]\n                false,\n                #[cfg(peri_ucpd2)]\n                false,\n            );\n        });\n    }\n\n    /// Returns the current voltage level of CC1 and CC2 pin as tuple.\n    ///\n    /// Interpretation of the voltage levels depends on the configured CC line\n    /// pull-up/pull-down resistance.\n    pub fn vstate(&self) -> (CcVState, CcVState) {\n        let sr = T::REGS.sr().read();\n        (sr.typec_vstate_cc1(), sr.typec_vstate_cc2())\n    }\n\n    /// Waits for a change in voltage state on either CC line.\n    pub async fn wait_for_vstate_change(&self) -> (CcVState, CcVState) {\n        let _on_drop = OnDrop::new(|| self.enable_cc_interrupts(false));\n        let prev_vstate = self.vstate();\n        poll_fn(|cx| {\n            let vstate = self.vstate();\n            if vstate != prev_vstate {\n                Poll::Ready(vstate)\n            } else {\n                T::state().waker.register(cx.waker());\n                self.enable_cc_interrupts(true);\n                Poll::Pending\n            }\n        })\n        .await\n    }\n\n    fn enable_cc_interrupts(&self, enable: bool) {\n        T::REGS.imr().modify(|w| {\n            w.set_typecevt1ie(enable);\n            w.set_typecevt2ie(enable);\n        });\n    }\n}\n\n/// Receive SOP.\n#[derive(Debug, Clone, Copy)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub enum Sop {\n    /// SOP\n    Sop,\n    /// SOP'\n    SopPrime,\n    /// SOP''\n    SopDoublePrime,\n    /// SOP'_Debug\n    SopPrimeDebug,\n    /// SOP''_Debug\n    SopDoublePrimeDebug,\n}\n\n/// Receive Error.\n#[derive(Debug, Clone, Copy)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub enum RxError {\n    /// Incorrect CRC or truncated message (a line becoming static before EOP is met).\n    Crc,\n\n    /// Provided buffer was too small for the received message.\n    Overrun,\n\n    /// Hard Reset received before or during reception.\n    HardReset,\n}\n\n/// Transmit Error.\n#[derive(Debug, Clone, Copy)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub enum TxError {\n    /// Concurrent receive in progress or excessive noise on the line.\n    Discarded,\n\n    /// Hard Reset received before or during transmission.\n    HardReset,\n}\n\n/// Power Delivery (PD) PHY.\npub struct PdPhy<'d, T: Instance> {\n    _lifetime: PhantomData<&'d mut T>,\n    rx_dma: ChannelAndRequest<'d>,\n    tx_dma: ChannelAndRequest<'d>,\n}\n\nimpl<'d, T: Instance> Drop for PdPhy<'d, T> {\n    fn drop(&mut self) {\n        let r = T::REGS;\n        r.cr().modify(|w| w.set_phyrxen(false));\n        // Check if the CcPhy part was dropped already.\n        let drop_not_ready = &T::state().drop_not_ready;\n        if drop_not_ready.load(Ordering::Relaxed) {\n            drop_not_ready.store(false, Ordering::Relaxed);\n        } else {\n            r.cfgr1().write(|w| w.set_ucpden(false));\n            rcc::disable::<T>();\n            T::Interrupt::disable();\n        }\n    }\n}\n\nimpl<'d, T: Instance> PdPhy<'d, T> {\n    /// Receives a PD message into the provided buffer.\n    ///\n    /// Returns the number of received bytes or an error.\n    pub async fn receive(&mut self, buf: &mut [u8]) -> Result<usize, RxError> {\n        self.receive_with_sop(buf).await.map(|(_sop, size)| size)\n    }\n\n    /// Receives SOP and a PD message into the provided buffer.\n    ///\n    /// Returns the start of packet type and number of received bytes or an error.\n    pub async fn receive_with_sop(&mut self, buf: &mut [u8]) -> Result<(Sop, usize), RxError> {\n        let r = T::REGS;\n\n        let mut dma = unsafe {\n            self.rx_dma\n                .read(r.rxdr().as_ptr() as *mut u8, buf, TransferOptions::default())\n        };\n\n        let _on_drop = OnDrop::new(|| {\n            Self::enable_rx_interrupt(false);\n            // Clear interrupt flags\n            r.icr().write(|w| {\n                w.set_rxorddetcf(true);\n                w.set_rxovrcf(true);\n                w.set_rxmsgendcf(true);\n            });\n        });\n\n        let mut rxpaysz = 0;\n\n        // Stop DMA reception immediately after receiving a packet, to prevent storing multiple packets in the same buffer.\n        poll_fn(|cx| {\n            let sr = r.sr().read();\n\n            if sr.rxhrstdet() {\n                dma.request_pause();\n\n                // Clean and re-enable hard reset receive interrupt.\n                r.icr().write(|w| w.set_rxhrstdetcf(true));\n                r.imr().modify(|w| w.set_rxhrstdetie(true));\n                Poll::Ready(Err(RxError::HardReset))\n            } else if sr.rxmsgend() {\n                dma.request_pause();\n                // Should be read immediately on interrupt.\n                rxpaysz = r.rx_payszr().read().rxpaysz().into();\n\n                let ret = if sr.rxovr() {\n                    Err(RxError::Overrun)\n                } else if sr.rxerr() {\n                    Err(RxError::Crc)\n                } else {\n                    Ok(())\n                };\n                Poll::Ready(ret)\n            } else {\n                T::state().waker.register(cx.waker());\n                Self::enable_rx_interrupt(true);\n                Poll::Pending\n            }\n        })\n        .await?;\n\n        // Make sure that the last byte was fetched by DMA.\n        while r.sr().read().rxne() {\n            if dma.get_remaining_transfers() == 0 {\n                return Err(RxError::Overrun);\n            }\n        }\n\n        let sop = match r.rx_ordsetr().read().rxordset() {\n            Rxordset::SOP => Sop::Sop,\n            Rxordset::SOP_PRIME => Sop::SopPrime,\n            Rxordset::SOP_DOUBLE_PRIME => Sop::SopDoublePrime,\n            Rxordset::SOP_PRIME_DEBUG => Sop::SopPrimeDebug,\n            Rxordset::SOP_DOUBLE_PRIME_DEBUG => Sop::SopDoublePrimeDebug,\n            Rxordset::CABLE_RESET => return Err(RxError::HardReset),\n            // Extension headers are not supported\n            _ => unreachable!(),\n        };\n\n        Ok((sop, rxpaysz))\n    }\n\n    fn enable_rx_interrupt(enable: bool) {\n        T::REGS.imr().modify(|w| w.set_rxmsgendie(enable));\n    }\n\n    /// Transmits a PD message.\n    pub async fn transmit(&mut self, buf: &[u8]) -> Result<(), TxError> {\n        let r = T::REGS;\n\n        // When a previous transmission was dropped before it had finished it\n        // might still be running because there is no way to abort an ongoing\n        // message transmission. Wait for it to finish but ignore errors.\n        if r.cr().read().txsend() {\n            if let Err(TxError::HardReset) = Self::wait_tx_done().await {\n                return Err(TxError::HardReset);\n            }\n        }\n\n        // Clear the TX interrupt flags.\n        T::REGS.icr().write(|w| {\n            w.set_txmsgdisccf(true);\n            w.set_txmsgsentcf(true);\n        });\n\n        // Start the DMA and let it do its thing in the background.\n        let _dma = unsafe {\n            self.tx_dma\n                .write(buf, r.txdr().as_ptr() as *mut u8, TransferOptions::default())\n        };\n\n        // Configure and start the transmission.\n        r.tx_payszr().write(|w| w.set_txpaysz(buf.len() as _));\n        r.cr().modify(|w| {\n            w.set_txmode(Txmode::PACKET);\n            w.set_txsend(true);\n        });\n\n        Self::wait_tx_done().await\n    }\n\n    async fn wait_tx_done() -> Result<(), TxError> {\n        let _on_drop = OnDrop::new(|| Self::enable_tx_interrupts(false));\n        poll_fn(|cx| {\n            let r = T::REGS;\n            let sr = r.sr().read();\n            if sr.rxhrstdet() {\n                // Clean and re-enable hard reset receive interrupt.\n                r.icr().write(|w| w.set_rxhrstdetcf(true));\n                r.imr().modify(|w| w.set_rxhrstdetie(true));\n                Poll::Ready(Err(TxError::HardReset))\n            } else if sr.txmsgdisc() {\n                Poll::Ready(Err(TxError::Discarded))\n            } else if sr.txmsgsent() {\n                Poll::Ready(Ok(()))\n            } else {\n                T::state().waker.register(cx.waker());\n                Self::enable_tx_interrupts(true);\n                Poll::Pending\n            }\n        })\n        .await\n    }\n\n    fn enable_tx_interrupts(enable: bool) {\n        T::REGS.imr().modify(|w| {\n            w.set_txmsgdiscie(enable);\n            w.set_txmsgsentie(enable);\n        });\n    }\n\n    /// Transmit a hard reset.\n    pub async fn transmit_hardreset(&mut self) -> Result<(), TxError> {\n        let r = T::REGS;\n\n        // Clear the hardreset interrupt flags.\n        T::REGS.icr().write(|w| {\n            w.set_hrstdisccf(true);\n            w.set_hrstsentcf(true);\n        });\n\n        // Trigger hard reset transmission.\n        r.cr().modify(|w| {\n            w.set_txhrst(true);\n        });\n\n        let _on_drop = OnDrop::new(|| self.enable_hardreset_interrupts(false));\n        poll_fn(|cx| {\n            let r = T::REGS;\n            let sr = r.sr().read();\n            if sr.rxhrstdet() {\n                // Clean and re-enable hard reset receive interrupt.\n                r.icr().write(|w| w.set_rxhrstdetcf(true));\n                r.imr().modify(|w| w.set_rxhrstdetie(true));\n                Poll::Ready(Err(TxError::HardReset))\n            } else if sr.hrstdisc() {\n                Poll::Ready(Err(TxError::Discarded))\n            } else if sr.hrstsent() {\n                Poll::Ready(Ok(()))\n            } else {\n                T::state().waker.register(cx.waker());\n                self.enable_hardreset_interrupts(true);\n                Poll::Pending\n            }\n        })\n        .await\n    }\n\n    fn enable_hardreset_interrupts(&self, enable: bool) {\n        T::REGS.imr().modify(|w| {\n            w.set_hrstdiscie(enable);\n            w.set_hrstsentie(enable);\n        });\n    }\n}\n\n/// Interrupt handler.\npub struct InterruptHandler<T: Instance> {\n    _phantom: PhantomData<T>,\n}\n\nimpl<T: Instance> interrupt::typelevel::Handler<T::Interrupt> for InterruptHandler<T> {\n    unsafe fn on_interrupt() {\n        let r = T::REGS;\n        let sr = r.sr().read();\n\n        if sr.typecevt1() || sr.typecevt2() {\n            r.icr().write(|w| {\n                w.set_typecevt1cf(true);\n                w.set_typecevt2cf(true);\n            });\n        }\n\n        if sr.rxhrstdet() {\n            r.imr().modify(|w| w.set_rxhrstdetie(false));\n        }\n\n        if sr.rxmsgend() {\n            r.imr().modify(|w| w.set_rxmsgendie(false));\n        }\n\n        if sr.txmsgdisc() || sr.txmsgsent() {\n            r.imr().modify(|w| {\n                w.set_txmsgdiscie(false);\n                w.set_txmsgsentie(false);\n            });\n        }\n\n        if sr.hrstdisc() || sr.hrstsent() {\n            r.imr().modify(|w| {\n                w.set_hrstdiscie(false);\n                w.set_hrstsentie(false);\n            });\n        }\n\n        // Wake the task to clear and re-enabled interrupts.\n        T::state().waker.wake();\n    }\n}\n\nstruct State {\n    waker: AtomicWaker,\n    // Inverted logic for a default state of 0 so that the data goes into the .bss section.\n    drop_not_ready: AtomicBool,\n}\n\nimpl State {\n    pub const fn new() -> Self {\n        Self {\n            waker: AtomicWaker::new(),\n            drop_not_ready: AtomicBool::new(false),\n        }\n    }\n}\n\ntrait SealedInstance {\n    const REGS: crate::pac::ucpd::Ucpd;\n    fn state() -> &'static State;\n}\n\n/// UCPD instance trait.\n#[allow(private_bounds)]\npub trait Instance: SealedInstance + PeripheralType + RccPeripheral {\n    /// Interrupt for this instance.\n    type Interrupt: crate::interrupt::typelevel::Interrupt;\n}\n\nforeach_interrupt!(\n    ($inst:ident, ucpd, UCPD, GLOBAL, $irq:ident) => {\n        impl SealedInstance for crate::peripherals::$inst {\n            const REGS: crate::pac::ucpd::Ucpd = crate::pac::$inst;\n\n            fn state() -> &'static State {\n                static STATE: State = State::new();\n                &STATE\n            }\n        }\n\n        impl Instance for crate::peripherals::$inst {\n            type Interrupt = crate::interrupt::typelevel::$irq;\n        }\n    };\n);\n\npin_trait!(Cc1Pin, Instance);\npin_trait!(Cc2Pin, Instance);\n\ndma_trait!(TxDma, Instance);\ndma_trait!(RxDma, Instance);\n"
  },
  {
    "path": "embassy-stm32/src/uid.rs",
    "content": "//! Unique ID (UID)\n\n/// Get this device's unique 96-bit ID.\npub fn uid() -> [u8; 12] {\n    unsafe { *crate::pac::UID.uid(0).as_ptr().cast::<[u8; 12]>() }\n}\n\n/// Get this device's unique 96-bit ID, encoded into a string of 24 hexadecimal ASCII digits.\npub fn uid_hex() -> &'static str {\n    unsafe { core::str::from_utf8_unchecked(uid_hex_bytes()) }\n}\n\n/// Get this device's unique 96-bit ID, encoded into 24 hexadecimal ASCII bytes.\npub fn uid_hex_bytes() -> &'static [u8; 24] {\n    const HEX: &[u8; 16] = b\"0123456789ABCDEF\";\n    static mut UID_HEX: [u8; 24] = [0; 24];\n    static mut LOADED: bool = false;\n    critical_section::with(|_| unsafe {\n        if !LOADED {\n            let uid = uid();\n            for (idx, v) in uid.iter().enumerate() {\n                let lo = v & 0x0f;\n                let hi = (v & 0xf0) >> 4;\n                UID_HEX[idx * 2] = HEX[hi as usize];\n                UID_HEX[idx * 2 + 1] = HEX[lo as usize];\n            }\n            LOADED = true;\n        }\n    });\n    unsafe { &*core::ptr::addr_of!(UID_HEX) }\n}\n"
  },
  {
    "path": "embassy-stm32/src/usart/buffered.rs",
    "content": "use core::future::poll_fn;\nuse core::marker::PhantomData;\nuse core::slice;\nuse core::sync::atomic::{AtomicBool, AtomicU8, AtomicUsize, Ordering};\nuse core::task::Poll;\n\nuse embassy_embedded_hal::SetConfig;\nuse embassy_hal_internal::Peri;\nuse embassy_hal_internal::atomic_ring_buffer::RingBuffer;\nuse embassy_sync::waitqueue::AtomicWaker;\n\n#[cfg(not(any(usart_v1, usart_v2)))]\nuse super::DePin;\nuse super::{\n    Config, ConfigError, CtsPin, Duplex, Error, HalfDuplexReadback, Info, Instance, Regs, RtsPin, RxPin, TxPin,\n    clear_interrupt_flags, configure, half_duplex_set_rx_tx_before_write, rdr, reconfigure, send_break, set_baudrate,\n    sr, tdr,\n};\nuse crate::gpio::{AfType, Flex, Pull};\nuse crate::interrupt::{self, InterruptExt};\nuse crate::time::Hertz;\n\n/// Interrupt handler.\npub struct InterruptHandler<T: Instance> {\n    _phantom: PhantomData<T>,\n}\n\nimpl<T: Instance> interrupt::typelevel::Handler<T::Interrupt> for InterruptHandler<T> {\n    unsafe fn on_interrupt() {\n        on_interrupt(T::info().regs, T::buffered_state())\n    }\n}\n\nunsafe fn on_interrupt(r: Regs, state: &'static State) {\n    // RX\n    let sr_val = sr(r).read();\n    // On v1 & v2, reading DR clears the rxne, error and idle interrupt\n    // flags. Keep this close to the SR read to reduce the chance of a\n    // flag being set in-between.\n    let dr = if sr_val.rxne() || cfg!(any(usart_v1, usart_v2)) && (sr_val.ore() || sr_val.idle()) {\n        Some(rdr(r).read_volatile())\n    } else {\n        None\n    };\n    clear_interrupt_flags(r, sr_val);\n\n    if sr_val.pe() {\n        warn!(\"Parity error\");\n    }\n    if sr_val.fe() {\n        warn!(\"Framing error\");\n    }\n    if sr_val.ne() {\n        warn!(\"Noise error\");\n    }\n    if sr_val.ore() {\n        warn!(\"Overrun error\");\n    }\n    if sr_val.rxne() {\n        let mut rx_writer = state.rx_buf.writer();\n        let buf = rx_writer.push_slice();\n        if !buf.is_empty() {\n            if let Some(byte) = dr {\n                buf[0] = byte;\n                rx_writer.push_done(1);\n            }\n        } else {\n            // FIXME: Should we disable any further RX interrupts when the buffer becomes full.\n        }\n\n        let eager = state.eager_reads.load(Ordering::Relaxed);\n        if eager > 0 {\n            if state.rx_buf.available() >= eager {\n                state.rx_waker.wake();\n            }\n        } else {\n            if state.rx_buf.is_half_full() {\n                state.rx_waker.wake();\n            }\n        }\n    }\n\n    if sr_val.idle() {\n        state.rx_waker.wake();\n    }\n\n    // With `usart_v4` hardware FIFO is enabled and Transmission complete (TC)\n    // indicates that all bytes are pushed out from the FIFO.\n    // For other usart variants it shows that last byte from the buffer was just sent.\n    if sr_val.tc() && r.cr1().read().tcie() {\n        // For others it is cleared above with `clear_interrupt_flags`.\n        #[cfg(any(usart_v1, usart_v2))]\n        sr(r).modify(|w| w.set_tc(false));\n\n        r.cr1().modify(|w| {\n            w.set_tcie(false);\n            // Reenable receiver for half-duplex if it was disabled\n            w.set_re(true);\n        });\n\n        state.tx_done.store(true, Ordering::Release);\n        state.tx_waker.wake();\n    }\n\n    // TX\n    if sr(r).read().txe() {\n        let mut tx_reader = state.tx_buf.reader();\n        let buf = tx_reader.pop_slice();\n        if !buf.is_empty() {\n            r.cr1().modify(|w| {\n                w.set_txeie(true);\n            });\n\n            // Enable transmission complete interrupt when last byte is going to be sent out.\n            if buf.len() == 1 {\n                r.cr1().modify(|w| {\n                    w.set_tcie(true);\n                });\n            }\n\n            half_duplex_set_rx_tx_before_write(&r, state.half_duplex_readback.load(Ordering::Relaxed));\n\n            tdr(r).write_volatile(buf[0].into());\n            tx_reader.pop_done(1);\n        } else {\n            // Disable interrupt until we have something to transmit again.\n            r.cr1().modify(|w| {\n                w.set_txeie(false);\n            });\n        }\n    }\n}\n\npub(super) struct State {\n    rx_waker: AtomicWaker,\n    rx_buf: RingBuffer,\n    tx_waker: AtomicWaker,\n    tx_buf: RingBuffer,\n    tx_done: AtomicBool,\n    tx_rx_refcount: AtomicU8,\n    half_duplex_readback: AtomicBool,\n    eager_reads: AtomicUsize,\n}\n\nimpl State {\n    pub(super) const fn new() -> Self {\n        Self {\n            rx_buf: RingBuffer::new(),\n            tx_buf: RingBuffer::new(),\n            rx_waker: AtomicWaker::new(),\n            tx_waker: AtomicWaker::new(),\n            tx_done: AtomicBool::new(true),\n            tx_rx_refcount: AtomicU8::new(0),\n            half_duplex_readback: AtomicBool::new(false),\n            eager_reads: AtomicUsize::new(0),\n        }\n    }\n}\n\n/// Bidirectional buffered UART\npub struct BufferedUart<'d> {\n    rx: BufferedUartRx<'d>,\n    tx: BufferedUartTx<'d>,\n}\n\n/// Tx-only buffered UART\n///\n/// Created with [BufferedUart::split]\npub struct BufferedUartTx<'d> {\n    info: &'static Info,\n    state: &'static State,\n    kernel_clock: Hertz,\n    tx: Option<Flex<'d>>,\n    cts: Option<Flex<'d>>,\n    de: Option<Flex<'d>>,\n    is_borrowed: bool,\n}\n\n/// Rx-only buffered UART\n///\n/// Created with [BufferedUart::split]\npub struct BufferedUartRx<'d> {\n    info: &'static Info,\n    state: &'static State,\n    kernel_clock: Hertz,\n    rx: Option<Flex<'d>>,\n    rts: Option<Flex<'d>>,\n    is_borrowed: bool,\n}\n\nimpl<'d> SetConfig for BufferedUart<'d> {\n    type Config = Config;\n    type ConfigError = ConfigError;\n\n    fn set_config(&mut self, config: &Self::Config) -> Result<(), Self::ConfigError> {\n        self.set_config(config)\n    }\n}\n\nimpl<'d> SetConfig for BufferedUartRx<'d> {\n    type Config = Config;\n    type ConfigError = ConfigError;\n\n    fn set_config(&mut self, config: &Self::Config) -> Result<(), Self::ConfigError> {\n        self.set_config(config)\n    }\n}\n\nimpl<'d> SetConfig for BufferedUartTx<'d> {\n    type Config = Config;\n    type ConfigError = ConfigError;\n\n    fn set_config(&mut self, config: &Self::Config) -> Result<(), Self::ConfigError> {\n        self.set_config(config)\n    }\n}\n\nimpl<'d> BufferedUart<'d> {\n    /// Create a new bidirectional buffered UART driver\n    pub fn new<T: Instance, #[cfg(afio)] A>(\n        peri: Peri<'d, T>,\n        rx: Peri<'d, if_afio!(impl RxPin<T, A>)>,\n        tx: Peri<'d, if_afio!(impl TxPin<T, A>)>,\n        tx_buffer: &'d mut [u8],\n        rx_buffer: &'d mut [u8],\n        _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd,\n        config: Config,\n    ) -> Result<Self, ConfigError> {\n        Self::new_inner(\n            peri,\n            new_pin!(rx, config.rx_af()),\n            new_pin!(tx, config.tx_af()),\n            None,\n            None,\n            None,\n            tx_buffer,\n            rx_buffer,\n            config,\n        )\n    }\n\n    /// Create a new bidirectional buffered UART driver with request-to-send and clear-to-send pins\n    pub fn new_with_rtscts<T: Instance, #[cfg(afio)] A>(\n        peri: Peri<'d, T>,\n        rx: Peri<'d, if_afio!(impl RxPin<T, A>)>,\n        tx: Peri<'d, if_afio!(impl TxPin<T, A>)>,\n        rts: Peri<'d, if_afio!(impl RtsPin<T, A>)>,\n        cts: Peri<'d, if_afio!(impl CtsPin<T, A>)>,\n        _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd,\n        tx_buffer: &'d mut [u8],\n        rx_buffer: &'d mut [u8],\n        config: Config,\n    ) -> Result<Self, ConfigError> {\n        Self::new_inner(\n            peri,\n            new_pin!(rx, config.rx_af()),\n            new_pin!(tx, config.tx_af()),\n            new_pin!(rts, config.rts_config.af_type()),\n            new_pin!(cts, AfType::input(config.cts_pull)),\n            None,\n            tx_buffer,\n            rx_buffer,\n            config,\n        )\n    }\n\n    /// Create a new bidirectional buffered UART driver with only the RTS pin as the DE pin\n    pub fn new_with_rts_as_de<T: Instance, #[cfg(afio)] A>(\n        peri: Peri<'d, T>,\n        rx: Peri<'d, if_afio!(impl RxPin<T, A>)>,\n        tx: Peri<'d, if_afio!(impl TxPin<T, A>)>,\n        rts: Peri<'d, if_afio!(impl RtsPin<T, A>)>,\n        _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd,\n        tx_buffer: &'d mut [u8],\n        rx_buffer: &'d mut [u8],\n        config: Config,\n    ) -> Result<Self, ConfigError> {\n        Self::new_inner(\n            peri,\n            new_pin!(rx, config.rx_af()),\n            new_pin!(tx, config.tx_af()),\n            None,\n            None,\n            new_pin!(rts, AfType::input(Pull::None)), // RTS mapped used as DE\n            tx_buffer,\n            rx_buffer,\n            config,\n        )\n    }\n\n    /// Create a new bidirectional buffered UART driver with only the request-to-send pin\n    pub fn new_with_rts<T: Instance, #[cfg(afio)] A>(\n        peri: Peri<'d, T>,\n        rx: Peri<'d, if_afio!(impl RxPin<T, A>)>,\n        tx: Peri<'d, if_afio!(impl TxPin<T, A>)>,\n        rts: Peri<'d, if_afio!(impl RtsPin<T, A>)>,\n        _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd,\n        tx_buffer: &'d mut [u8],\n        rx_buffer: &'d mut [u8],\n        config: Config,\n    ) -> Result<Self, ConfigError> {\n        Self::new_inner(\n            peri,\n            new_pin!(rx, config.rx_af()),\n            new_pin!(tx, config.tx_af()),\n            new_pin!(rts, AfType::input(Pull::None)),\n            None, // no CTS\n            None, // no DE\n            tx_buffer,\n            rx_buffer,\n            config,\n        )\n    }\n\n    /// Create a new bidirectional buffered UART driver with a driver-enable pin\n    #[cfg(not(any(usart_v1, usart_v2)))]\n    pub fn new_with_de<T: Instance, #[cfg(afio)] A>(\n        peri: Peri<'d, T>,\n        rx: Peri<'d, if_afio!(impl RxPin<T, A>)>,\n        tx: Peri<'d, if_afio!(impl TxPin<T, A>)>,\n        de: Peri<'d, if_afio!(impl DePin<T, A>)>,\n        _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd,\n        tx_buffer: &'d mut [u8],\n        rx_buffer: &'d mut [u8],\n        config: Config,\n    ) -> Result<Self, ConfigError> {\n        Self::new_inner(\n            peri,\n            new_pin!(rx, config.rx_af()),\n            new_pin!(tx, config.tx_af()),\n            None,\n            None,\n            new_pin!(de, config.de_config.af_type()),\n            tx_buffer,\n            rx_buffer,\n            config,\n        )\n    }\n\n    /// Create a single-wire half-duplex Uart transceiver on a single Tx pin.\n    ///\n    /// See [`new_half_duplex_on_rx`][`Self::new_half_duplex_on_rx`] if you would prefer to use an Rx pin\n    /// (when it is available for your chip). There is no functional difference between these methods, as both\n    /// allow bidirectional communication.\n    ///\n    /// The TX pin is always released when no data is transmitted. Thus, it acts as a standard\n    /// I/O in idle or in reception. It means that the I/O must be configured so that TX is\n    /// configured as alternate function open-drain with an external pull-up\n    /// Apart from this, the communication protocol is similar to normal USART mode. Any conflict\n    /// on the line must be managed by software (for instance by using a centralized arbiter).\n    #[doc(alias(\"HDSEL\"))]\n    pub fn new_half_duplex<T: Instance, #[cfg(afio)] A>(\n        peri: Peri<'d, T>,\n        tx: Peri<'d, if_afio!(impl TxPin<T, A>)>,\n        _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd,\n        tx_buffer: &'d mut [u8],\n        rx_buffer: &'d mut [u8],\n        mut config: Config,\n        readback: HalfDuplexReadback,\n    ) -> Result<Self, ConfigError> {\n        #[cfg(not(any(usart_v1, usart_v2)))]\n        {\n            config.swap_rx_tx = false;\n        }\n        config.duplex = Duplex::Half(readback);\n\n        Self::new_inner(\n            peri,\n            None,\n            new_pin!(tx, config.tx_af()),\n            None,\n            None,\n            None,\n            tx_buffer,\n            rx_buffer,\n            config,\n        )\n    }\n\n    /// Create a single-wire half-duplex Uart transceiver on a single Rx pin.\n    ///\n    /// See [`new_half_duplex`][`Self::new_half_duplex`] if you would prefer to use an Tx pin.\n    /// There is no functional difference between these methods, as both allow bidirectional communication.\n    ///\n    /// The pin is always released when no data is transmitted. Thus, it acts as a standard\n    /// I/O in idle or in reception.\n    /// Apart from this, the communication protocol is similar to normal USART mode. Any conflict\n    /// on the line must be managed by software (for instance by using a centralized arbiter).\n    #[cfg(not(any(usart_v1, usart_v2)))]\n    #[doc(alias(\"HDSEL\"))]\n    pub fn new_half_duplex_on_rx<T: Instance, #[cfg(afio)] A>(\n        peri: Peri<'d, T>,\n        rx: Peri<'d, if_afio!(impl RxPin<T, A>)>,\n        _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd,\n        tx_buffer: &'d mut [u8],\n        rx_buffer: &'d mut [u8],\n        mut config: Config,\n        readback: HalfDuplexReadback,\n    ) -> Result<Self, ConfigError> {\n        config.swap_rx_tx = true;\n        config.duplex = Duplex::Half(readback);\n\n        Self::new_inner(\n            peri,\n            new_pin!(rx, config.rx_af()),\n            None,\n            None,\n            None,\n            None,\n            tx_buffer,\n            rx_buffer,\n            config,\n        )\n    }\n\n    fn new_inner<T: Instance>(\n        _peri: Peri<'d, T>,\n        rx: Option<Flex<'d>>,\n        tx: Option<Flex<'d>>,\n        rts: Option<Flex<'d>>,\n        cts: Option<Flex<'d>>,\n        de: Option<Flex<'d>>,\n        tx_buffer: &'d mut [u8],\n        rx_buffer: &'d mut [u8],\n        config: Config,\n    ) -> Result<Self, ConfigError> {\n        let info = T::info();\n        let state = T::buffered_state();\n        let kernel_clock = T::frequency();\n\n        state\n            .eager_reads\n            .store(config.eager_reads.unwrap_or(0), Ordering::Relaxed);\n        state.half_duplex_readback.store(\n            config.duplex == Duplex::Half(HalfDuplexReadback::Readback),\n            Ordering::Relaxed,\n        );\n\n        let mut this = Self {\n            rx: BufferedUartRx {\n                info,\n                state,\n                kernel_clock,\n                rx,\n                rts,\n                is_borrowed: false,\n            },\n            tx: BufferedUartTx {\n                info,\n                state,\n                kernel_clock,\n                tx,\n                cts,\n                de,\n                is_borrowed: false,\n            },\n        };\n        this.enable_and_configure(tx_buffer, rx_buffer, &config)?;\n        Ok(this)\n    }\n\n    fn enable_and_configure(\n        &mut self,\n        tx_buffer: &'d mut [u8],\n        rx_buffer: &'d mut [u8],\n        config: &Config,\n    ) -> Result<(), ConfigError> {\n        let info = self.rx.info;\n        let state = self.rx.state;\n        state.tx_rx_refcount.store(2, Ordering::Relaxed);\n        state\n            .eager_reads\n            .store(config.eager_reads.unwrap_or(0), Ordering::Relaxed);\n\n        info.rcc.enable_and_reset();\n\n        assert!(!tx_buffer.is_empty());\n        let len = tx_buffer.len();\n        unsafe { state.tx_buf.init(tx_buffer.as_mut_ptr(), len) };\n        assert!(!rx_buffer.is_empty());\n        let len = rx_buffer.len();\n        unsafe { state.rx_buf.init(rx_buffer.as_mut_ptr(), len) };\n\n        info.regs.cr3().write(|w| {\n            w.set_rtse(self.rx.rts.is_some());\n            w.set_ctse(self.tx.cts.is_some());\n            #[cfg(not(any(usart_v1, usart_v2)))]\n            w.set_dem(self.tx.de.is_some());\n            w.set_hdsel(config.duplex.is_half());\n        });\n        configure(info, self.rx.kernel_clock, &config, true, true)?;\n\n        info.regs.cr1().modify(|w| {\n            w.set_rxneie(true);\n            w.set_idleie(true);\n\n            if config.duplex.is_half() {\n                // The te and re bits will be set by write, read and flush methods.\n                // Receiver should be enabled by default for Half-Duplex.\n                w.set_te(false);\n                w.set_re(true);\n            }\n        });\n\n        info.interrupt.unpend();\n        unsafe { info.interrupt.enable() };\n\n        Ok(())\n    }\n\n    /// Split the driver into a Tx and Rx part (useful for sending to separate tasks)\n    pub fn split(self) -> (BufferedUartTx<'d>, BufferedUartRx<'d>) {\n        (self.tx, self.rx)\n    }\n\n    /// Split the Uart into a transmitter and receiver,\n    /// which is particularly useful when having two tasks correlating to\n    /// transmitting and receiving.\n    pub fn split_ref(&mut self) -> (BufferedUartTx<'_>, BufferedUartRx<'_>) {\n        (\n            BufferedUartTx {\n                info: self.tx.info,\n                state: self.tx.state,\n                kernel_clock: self.tx.kernel_clock,\n                tx: self.tx.tx.as_mut().map(Flex::reborrow),\n                cts: self.tx.cts.as_mut().map(Flex::reborrow),\n                de: self.tx.de.as_mut().map(Flex::reborrow),\n                is_borrowed: true,\n            },\n            BufferedUartRx {\n                info: self.rx.info,\n                state: self.rx.state,\n                kernel_clock: self.rx.kernel_clock,\n                rx: self.rx.rx.as_mut().map(Flex::reborrow),\n                rts: self.rx.rts.as_mut().map(Flex::reborrow),\n                is_borrowed: true,\n            },\n        )\n    }\n\n    /// Reconfigure the driver\n    pub fn set_config(&mut self, config: &Config) -> Result<(), ConfigError> {\n        reconfigure(self.rx.info, self.rx.kernel_clock, config)?;\n\n        self.rx\n            .state\n            .eager_reads\n            .store(config.eager_reads.unwrap_or(0), Ordering::Relaxed);\n\n        self.rx.info.regs.cr1().modify(|w| {\n            w.set_rxneie(true);\n            w.set_idleie(true);\n        });\n\n        Ok(())\n    }\n\n    /// Send break character\n    pub fn send_break(&self) {\n        self.tx.send_break()\n    }\n\n    /// Set baudrate\n    pub fn set_baudrate(&self, baudrate: u32) -> Result<(), ConfigError> {\n        self.tx.set_baudrate(baudrate)?;\n        self.rx.set_baudrate(baudrate)?;\n        Ok(())\n    }\n}\n\nimpl<'d> BufferedUartRx<'d> {\n    async fn read(&self, buf: &mut [u8]) -> Result<usize, Error> {\n        poll_fn(move |cx| {\n            let state = self.state;\n            let mut rx_reader = unsafe { state.rx_buf.reader() };\n            let mut buf_len = 0;\n            let mut data = rx_reader.pop_slice();\n\n            while !data.is_empty() && buf_len < buf.len() {\n                let data_len = data.len().min(buf.len() - buf_len);\n                buf[buf_len..buf_len + data_len].copy_from_slice(&data[..data_len]);\n                buf_len += data_len;\n\n                let do_pend = state.rx_buf.is_full();\n                rx_reader.pop_done(data_len);\n\n                if do_pend {\n                    self.info.interrupt.pend();\n                }\n\n                data = rx_reader.pop_slice();\n            }\n\n            if buf_len != 0 {\n                Poll::Ready(Ok(buf_len))\n            } else {\n                state.rx_waker.register(cx.waker());\n                Poll::Pending\n            }\n        })\n        .await\n    }\n\n    fn blocking_read(&self, buf: &mut [u8]) -> Result<usize, Error> {\n        loop {\n            let state = self.state;\n            let mut rx_reader = unsafe { state.rx_buf.reader() };\n            let mut buf_len = 0;\n            let mut data = rx_reader.pop_slice();\n\n            while !data.is_empty() && buf_len < buf.len() {\n                let data_len = data.len().min(buf.len() - buf_len);\n                buf[buf_len..buf_len + data_len].copy_from_slice(&data[..data_len]);\n                buf_len += data_len;\n\n                let do_pend = state.rx_buf.is_full();\n                rx_reader.pop_done(data_len);\n\n                if do_pend {\n                    self.info.interrupt.pend();\n                }\n\n                data = rx_reader.pop_slice();\n            }\n            return Ok(buf_len);\n        }\n    }\n\n    async fn fill_buf(&self) -> Result<&[u8], Error> {\n        poll_fn(move |cx| {\n            let state = self.state;\n            let mut rx_reader = unsafe { state.rx_buf.reader() };\n            let (p, n) = rx_reader.pop_buf();\n            if n == 0 {\n                state.rx_waker.register(cx.waker());\n                return Poll::Pending;\n            }\n\n            let buf = unsafe { slice::from_raw_parts(p, n) };\n            Poll::Ready(Ok(buf))\n        })\n        .await\n    }\n\n    fn consume(&self, amt: usize) {\n        let state = self.state;\n        let mut rx_reader = unsafe { state.rx_buf.reader() };\n        let full = state.rx_buf.is_full();\n        rx_reader.pop_done(amt);\n        if full {\n            self.info.interrupt.pend();\n        }\n    }\n\n    /// we are ready to read if there is data in the buffer\n    fn read_ready(&mut self) -> Result<bool, Error> {\n        let state = self.state;\n        Ok(!state.rx_buf.is_empty())\n    }\n\n    /// Reconfigure the driver\n    pub fn set_config(&mut self, config: &Config) -> Result<(), ConfigError> {\n        reconfigure(self.info, self.kernel_clock, config)?;\n\n        self.state\n            .eager_reads\n            .store(config.eager_reads.unwrap_or(0), Ordering::Relaxed);\n\n        self.info.regs.cr1().modify(|w| {\n            w.set_rxneie(true);\n            w.set_idleie(true);\n        });\n\n        Ok(())\n    }\n\n    /// Set baudrate\n    pub fn set_baudrate(&self, baudrate: u32) -> Result<(), ConfigError> {\n        set_baudrate(self.info, self.kernel_clock, baudrate)\n    }\n}\n\nimpl<'d> BufferedUartTx<'d> {\n    async fn write(&self, buf: &[u8]) -> Result<usize, Error> {\n        poll_fn(move |cx| {\n            let state = self.state;\n            state.tx_done.store(false, Ordering::Release);\n\n            let empty = state.tx_buf.is_empty();\n\n            if state.tx_buf.len() < buf.len() {\n                return Poll::Ready(Err(Error::BufferTooLong));\n            }\n\n            if state.tx_buf.len() - state.tx_buf.available() < buf.len() {\n                return Poll::Pending;\n            }\n\n            let mut written: usize = 0;\n            let mut tx_writer = unsafe { state.tx_buf.writer() };\n            while written != buf.len() {\n                let data = tx_writer.push_slice();\n                if data.is_empty() {\n                    state.tx_waker.register(cx.waker());\n                    return Poll::Pending;\n                }\n                let n = data.len().min(buf.len() - written);\n                data[..n].copy_from_slice(&buf[written..written + n]);\n                written = written + n;\n                tx_writer.push_done(n);\n            }\n\n            if empty {\n                self.info.interrupt.pend();\n            }\n\n            Poll::Ready(Ok(written))\n        })\n        .await\n    }\n\n    async fn flush(&self) -> Result<(), Error> {\n        poll_fn(move |cx| {\n            let state = self.state;\n\n            if !state.tx_done.load(Ordering::Acquire) {\n                state.tx_waker.register(cx.waker());\n                return Poll::Pending;\n            }\n\n            Poll::Ready(Ok(()))\n        })\n        .await\n    }\n\n    fn blocking_write(&self, buf: &[u8]) -> Result<usize, Error> {\n        loop {\n            let state = self.state;\n            state.tx_done.store(false, Ordering::Release);\n\n            let empty = state.tx_buf.is_empty();\n\n            let mut written: usize = 0;\n            let mut tx_writer = unsafe { state.tx_buf.writer() };\n            while written != buf.len() {\n                let data = tx_writer.push_slice();\n                if !data.is_empty() {\n                    let n = data.len().min(buf.len() - written);\n                    data[..n].copy_from_slice(&buf[written..written + n]);\n                    written = written + n;\n                    tx_writer.push_done(n);\n                }\n            }\n\n            if empty {\n                self.info.interrupt.pend();\n            }\n\n            return Ok(written);\n        }\n    }\n\n    fn blocking_flush(&self) -> Result<(), Error> {\n        loop {\n            let state = self.state;\n            if state.tx_done.load(Ordering::Acquire) {\n                return Ok(());\n            }\n        }\n    }\n\n    /// Reconfigure the driver\n    pub fn set_config(&mut self, config: &Config) -> Result<(), ConfigError> {\n        reconfigure(self.info, self.kernel_clock, config)?;\n\n        self.info.regs.cr1().modify(|w| {\n            w.set_rxneie(true);\n            w.set_idleie(true);\n        });\n\n        Ok(())\n    }\n\n    /// Send break character\n    pub fn send_break(&self) {\n        send_break(&self.info.regs);\n    }\n\n    /// Set baudrate\n    pub fn set_baudrate(&self, baudrate: u32) -> Result<(), ConfigError> {\n        set_baudrate(self.info, self.kernel_clock, baudrate)\n    }\n}\n\nimpl<'d> Drop for BufferedUartRx<'d> {\n    fn drop(&mut self) {\n        if !self.is_borrowed {\n            let state = self.state;\n            unsafe {\n                state.rx_buf.deinit();\n\n                // TX is inactive if the buffer is not available.\n                // We can now unregister the interrupt handler\n                if state.tx_buf.len() == 0 {\n                    self.info.interrupt.disable();\n                }\n            }\n\n            drop_tx_rx(self.info, state);\n        }\n    }\n}\n\nimpl<'d> Drop for BufferedUartTx<'d> {\n    fn drop(&mut self) {\n        if !self.is_borrowed {\n            let state = self.state;\n            unsafe {\n                state.tx_buf.deinit();\n\n                // RX is inactive if the buffer is not available.\n                // We can now unregister the interrupt handler\n                if state.rx_buf.len() == 0 {\n                    self.info.interrupt.disable();\n                }\n            }\n            drop_tx_rx(self.info, state);\n        }\n    }\n}\n\nfn drop_tx_rx(info: &Info, state: &State) {\n    // We cannot use atomic subtraction here, because it's not supported for all targets\n    let is_last_drop = critical_section::with(|_| {\n        let refcount = state.tx_rx_refcount.load(Ordering::Relaxed);\n        assert!(refcount >= 1);\n        state.tx_rx_refcount.store(refcount - 1, Ordering::Relaxed);\n        refcount == 1\n    });\n    if is_last_drop {\n        info.rcc.disable();\n    }\n}\n\nimpl<'d> embedded_io_async::ErrorType for BufferedUart<'d> {\n    type Error = Error;\n}\n\nimpl<'d> embedded_io_async::ErrorType for BufferedUartRx<'d> {\n    type Error = Error;\n}\n\nimpl<'d> embedded_io_async::ErrorType for BufferedUartTx<'d> {\n    type Error = Error;\n}\n\nimpl<'d> embedded_io_async::Read for BufferedUart<'d> {\n    async fn read(&mut self, buf: &mut [u8]) -> Result<usize, Self::Error> {\n        self.rx.read(buf).await\n    }\n}\n\nimpl<'d> embedded_io_async::Read for BufferedUartRx<'d> {\n    async fn read(&mut self, buf: &mut [u8]) -> Result<usize, Self::Error> {\n        Self::read(self, buf).await\n    }\n}\n\nimpl<'d> embedded_io_async::ReadReady for BufferedUart<'d> {\n    fn read_ready(&mut self) -> Result<bool, Self::Error> {\n        BufferedUartRx::<'d>::read_ready(&mut self.rx)\n    }\n}\n\nimpl<'d> embedded_io_async::ReadReady for BufferedUartRx<'d> {\n    fn read_ready(&mut self) -> Result<bool, Self::Error> {\n        Self::read_ready(self)\n    }\n}\n\nimpl<'d> embedded_io_async::BufRead for BufferedUart<'d> {\n    async fn fill_buf(&mut self) -> Result<&[u8], Self::Error> {\n        self.rx.fill_buf().await\n    }\n\n    fn consume(&mut self, amt: usize) {\n        self.rx.consume(amt)\n    }\n}\n\nimpl<'d> embedded_io_async::BufRead for BufferedUartRx<'d> {\n    async fn fill_buf(&mut self) -> Result<&[u8], Self::Error> {\n        Self::fill_buf(self).await\n    }\n\n    fn consume(&mut self, amt: usize) {\n        Self::consume(self, amt)\n    }\n}\n\nimpl<'d> embedded_io_async::Write for BufferedUart<'d> {\n    async fn write(&mut self, buf: &[u8]) -> Result<usize, Self::Error> {\n        self.tx.write(buf).await\n    }\n\n    async fn flush(&mut self) -> Result<(), Self::Error> {\n        self.tx.flush().await\n    }\n}\n\nimpl<'d> embedded_io_async::Write for BufferedUartTx<'d> {\n    async fn write(&mut self, buf: &[u8]) -> Result<usize, Self::Error> {\n        Self::write(self, buf).await\n    }\n\n    async fn flush(&mut self) -> Result<(), Self::Error> {\n        Self::flush(self).await\n    }\n}\n\nimpl<'d> embedded_io::Read for BufferedUart<'d> {\n    fn read(&mut self, buf: &mut [u8]) -> Result<usize, Self::Error> {\n        self.rx.blocking_read(buf)\n    }\n}\n\nimpl<'d> embedded_io::Read for BufferedUartRx<'d> {\n    fn read(&mut self, buf: &mut [u8]) -> Result<usize, Self::Error> {\n        self.blocking_read(buf)\n    }\n}\n\nimpl<'d> embedded_io::Write for BufferedUart<'d> {\n    fn write(&mut self, buf: &[u8]) -> Result<usize, Self::Error> {\n        self.tx.blocking_write(buf)\n    }\n\n    fn flush(&mut self) -> Result<(), Self::Error> {\n        self.tx.blocking_flush()\n    }\n}\n\nimpl<'d> embedded_io::Write for BufferedUartTx<'d> {\n    fn write(&mut self, buf: &[u8]) -> Result<usize, Self::Error> {\n        Self::blocking_write(self, buf)\n    }\n\n    fn flush(&mut self) -> Result<(), Self::Error> {\n        Self::blocking_flush(self)\n    }\n}\n\nimpl<'d> embedded_hal_02::serial::Read<u8> for BufferedUartRx<'d> {\n    type Error = Error;\n\n    fn read(&mut self) -> Result<u8, nb::Error<Self::Error>> {\n        let state = self.state;\n        let mut rx_reader = unsafe { state.rx_buf.reader() };\n\n        let do_pend = state.rx_buf.is_full();\n        if let Some(data) = rx_reader.pop_one() {\n            if do_pend {\n                self.info.interrupt.pend();\n            }\n            Ok(data)\n        } else {\n            Err(nb::Error::WouldBlock)\n        }\n    }\n}\n\nimpl<'d> embedded_hal_02::blocking::serial::Write<u8> for BufferedUartTx<'d> {\n    type Error = Error;\n\n    fn bwrite_all(&mut self, mut buffer: &[u8]) -> Result<(), Self::Error> {\n        while !buffer.is_empty() {\n            match self.blocking_write(buffer) {\n                Ok(0) => panic!(\"zero-length write.\"),\n                Ok(n) => buffer = &buffer[n..],\n                Err(e) => return Err(e),\n            }\n        }\n        Ok(())\n    }\n\n    fn bflush(&mut self) -> Result<(), Self::Error> {\n        self.blocking_flush()\n    }\n}\n\nimpl<'d> embedded_hal_02::serial::Read<u8> for BufferedUart<'d> {\n    type Error = Error;\n\n    fn read(&mut self) -> Result<u8, nb::Error<Self::Error>> {\n        embedded_hal_02::serial::Read::read(&mut self.rx)\n    }\n}\n\nimpl<'d> embedded_hal_02::blocking::serial::Write<u8> for BufferedUart<'d> {\n    type Error = Error;\n\n    fn bwrite_all(&mut self, mut buffer: &[u8]) -> Result<(), Self::Error> {\n        while !buffer.is_empty() {\n            match self.tx.blocking_write(buffer) {\n                Ok(0) => panic!(\"zero-length write.\"),\n                Ok(n) => buffer = &buffer[n..],\n                Err(e) => return Err(e),\n            }\n        }\n        Ok(())\n    }\n\n    fn bflush(&mut self) -> Result<(), Self::Error> {\n        self.tx.blocking_flush()\n    }\n}\n\nimpl<'d> embedded_hal_nb::serial::ErrorType for BufferedUart<'d> {\n    type Error = Error;\n}\n\nimpl<'d> embedded_hal_nb::serial::ErrorType for BufferedUartTx<'d> {\n    type Error = Error;\n}\n\nimpl<'d> embedded_hal_nb::serial::ErrorType for BufferedUartRx<'d> {\n    type Error = Error;\n}\n\nimpl<'d> embedded_hal_nb::serial::Read for BufferedUartRx<'d> {\n    fn read(&mut self) -> nb::Result<u8, Self::Error> {\n        embedded_hal_02::serial::Read::read(self)\n    }\n}\n\nimpl<'d> embedded_hal_nb::serial::Write for BufferedUartTx<'d> {\n    fn write(&mut self, char: u8) -> nb::Result<(), Self::Error> {\n        self.blocking_write(&[char]).map(drop).map_err(nb::Error::Other)\n    }\n\n    fn flush(&mut self) -> nb::Result<(), Self::Error> {\n        self.blocking_flush().map_err(nb::Error::Other)\n    }\n}\n\nimpl<'d> embedded_hal_nb::serial::Read for BufferedUart<'d> {\n    fn read(&mut self) -> Result<u8, nb::Error<Self::Error>> {\n        embedded_hal_02::serial::Read::read(&mut self.rx)\n    }\n}\n\nimpl<'d> embedded_hal_nb::serial::Write for BufferedUart<'d> {\n    fn write(&mut self, char: u8) -> nb::Result<(), Self::Error> {\n        self.tx.blocking_write(&[char]).map(drop).map_err(nb::Error::Other)\n    }\n\n    fn flush(&mut self) -> nb::Result<(), Self::Error> {\n        self.tx.blocking_flush().map_err(nb::Error::Other)\n    }\n}\n"
  },
  {
    "path": "embassy-stm32/src/usart/mod.rs",
    "content": "//! Universal Synchronous/Asynchronous Receiver Transmitter (USART, UART, LPUART)\n#![macro_use]\n#![warn(missing_docs)]\n\nuse core::future::poll_fn;\nuse core::marker::PhantomData;\nuse core::sync::atomic::{AtomicU8, AtomicUsize, Ordering, compiler_fence};\nuse core::task::Poll;\n\nuse embassy_embedded_hal::SetConfig;\nuse embassy_hal_internal::PeripheralType;\nuse embassy_hal_internal::drop::OnDrop;\nuse embassy_sync::waitqueue::AtomicWaker;\nuse futures_util::future::{Either, select};\n\nuse crate::Peri;\nuse crate::dma::ChannelAndRequest;\nuse crate::gpio::{AfType, Flex, OutputType, Pull, Speed};\nuse crate::interrupt::typelevel::Interrupt as _;\nuse crate::interrupt::{self, Interrupt, InterruptExt};\nuse crate::mode::{Async, Blocking, Mode};\n#[cfg(not(any(usart_v1, usart_v2)))]\nuse crate::pac::usart::Lpuart as Regs;\n#[cfg(any(usart_v1, usart_v2))]\nuse crate::pac::usart::Usart as Regs;\nuse crate::pac::usart::{regs, vals};\nuse crate::rcc::{RccInfo, SealedRccPeripheral};\nuse crate::time::Hertz;\n\n/// Interrupt handler.\npub struct InterruptHandler<T: Instance> {\n    _phantom: PhantomData<T>,\n}\n\nimpl<T: Instance> interrupt::typelevel::Handler<T::Interrupt> for InterruptHandler<T> {\n    unsafe fn on_interrupt() {\n        on_interrupt(T::info().regs, T::state())\n    }\n}\n\nunsafe fn on_interrupt(r: Regs, s: &'static State) {\n    let (sr, cr1, cr3) = (sr(r).read(), r.cr1().read(), r.cr3().read());\n\n    let has_errors = (sr.pe() && cr1.peie()) || ((sr.fe() || sr.ne() || sr.ore()) && cr3.eie());\n    if has_errors {\n        // clear all interrupts and DMA Rx Request\n        r.cr1().modify(|w| {\n            // disable RXNE interrupt\n            w.set_rxneie(false);\n            // disable parity interrupt\n            w.set_peie(false);\n            // disable idle line interrupt\n            w.set_idleie(false);\n        });\n        r.cr3().modify(|w| {\n            // disable Error Interrupt: (Frame error, Noise error, Overrun error)\n            w.set_eie(false);\n            // disable DMA Rx Request\n            w.set_dmar(false);\n        });\n    } else if cr1.idleie() && sr.idle() {\n        // IDLE detected: no more data will come\n        r.cr1().modify(|w| {\n            // disable idle line detection\n            w.set_idleie(false);\n        });\n    } else if cr1.tcie() && sr.tc() {\n        // Transmission complete detected\n        r.cr1().modify(|w| {\n            // disable Transmission complete interrupt\n            w.set_tcie(false);\n        });\n    } else if cr1.rxneie() {\n        // We cannot check the RXNE flag as it is auto-cleared by the DMA controller\n\n        // It is up to the listener to determine if this in fact was a RX event and disable the RXNE detection\n    } else {\n        return;\n    }\n\n    compiler_fence(Ordering::SeqCst);\n    s.rx_waker.wake();\n    s.tx_waker.wake();\n}\n\n#[derive(Clone, Copy, PartialEq, Eq, Debug)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\n/// Number of data bits\npub enum DataBits {\n    /// 7 Data Bits\n    DataBits7,\n    /// 8 Data Bits\n    DataBits8,\n    /// 9 Data Bits\n    DataBits9,\n}\n\n#[derive(Clone, Copy, PartialEq, Eq, Debug)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\n/// Parity\npub enum Parity {\n    /// No parity\n    ParityNone,\n    /// Even Parity\n    ParityEven,\n    /// Odd Parity\n    ParityOdd,\n}\n\n#[derive(Clone, Copy, PartialEq, Eq, Debug)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\n/// Number of stop bits\npub enum StopBits {\n    #[doc = \"1 stop bit\"]\n    STOP1,\n    #[doc = \"0.5 stop bits\"]\n    STOP0P5,\n    #[doc = \"2 stop bits\"]\n    STOP2,\n    #[doc = \"1.5 stop bits\"]\n    STOP1P5,\n}\n\n#[derive(Clone, Copy, PartialEq, Eq, Debug)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\n/// Enables or disables receiver so written data are read back in half-duplex mode\npub enum HalfDuplexReadback {\n    /// Disables receiver so written data are not read back\n    NoReadback,\n    /// Enables receiver so written data are read back\n    Readback,\n}\n\n#[derive(Clone, Copy, PartialEq, Eq, Debug)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\n/// Half duplex IO mode\npub enum OutputConfig {\n    /// Push pull allows for faster baudrates, no internal pullup\n    PushPull,\n    /// Open drain output (external pull up needed)\n    OpenDrain,\n    #[cfg(not(gpio_v1))]\n    /// Open drain output with internal pull up resistor\n    OpenDrainPullUp,\n}\n\nimpl OutputConfig {\n    const fn af_type(self) -> AfType {\n        match self {\n            OutputConfig::PushPull => AfType::output(OutputType::PushPull, Speed::Medium),\n            OutputConfig::OpenDrain => AfType::output(OutputType::OpenDrain, Speed::Medium),\n            #[cfg(not(gpio_v1))]\n            OutputConfig::OpenDrainPullUp => AfType::output_pull(OutputType::OpenDrain, Speed::Medium, Pull::Up),\n        }\n    }\n}\n\n#[derive(Clone, Copy, PartialEq, Eq, Debug)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\n/// Duplex mode\npub enum Duplex {\n    /// Full duplex\n    Full,\n    /// Half duplex with possibility to read back written data\n    Half(HalfDuplexReadback),\n}\n\nimpl Duplex {\n    /// Returns true if half-duplex\n    fn is_half(&self) -> bool {\n        matches!(self, Duplex::Half(_))\n    }\n}\n\n#[non_exhaustive]\n#[derive(Clone, Copy, PartialEq, Eq, Debug)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\n/// Config Error\npub enum ConfigError {\n    /// Baudrate too low\n    BaudrateTooLow,\n    /// Baudrate too high\n    BaudrateTooHigh,\n    /// Rx or Tx not enabled\n    RxOrTxNotEnabled,\n    /// Data bits and parity combination not supported\n    DataParityNotSupported,\n    /// DE assertion time too high\n    #[cfg(not(any(usart_v1, usart_v2)))]\n    DeAssertionTimeTooHigh,\n    /// DE deassertion time too high\n    #[cfg(not(any(usart_v1, usart_v2)))]\n    DeDeassertionTimeTooHigh,\n}\n\n#[non_exhaustive]\n#[derive(Clone, Copy, PartialEq, Eq, Debug)]\n/// Config\npub struct Config {\n    /// Baud rate\n    pub baudrate: u32,\n    /// Number of data bits\n    pub data_bits: DataBits,\n    /// Number of stop bits\n    pub stop_bits: StopBits,\n    /// Parity type\n    pub parity: Parity,\n\n    /// If true: on a read-like method, if there is a latent error pending,\n    /// the read will abort and the error will be reported and cleared\n    ///\n    /// If false: the error is ignored and cleared\n    pub detect_previous_overrun: bool,\n\n    /// If `None` (the default) then read-like calls on `BufferedUartRx` and `RingBufferedUartRx`\n    /// typically only wake/return after line idle or after the buffer is at least half full\n    /// (for `BufferedUartRx`) or the DMA buffer is written at the half or full positions\n    /// (for `RingBufferedUartRx`), though it may also wake/return earlier in some circumstances.\n    ///\n    /// If `Some(n)` then such reads are also woken/return as soon as at least `n` words are\n    /// available in the buffer, in addition to waking/returning when the conditions described\n    /// above are met. `Some(0)` is treated as `None`. Setting this for `RingBufferedUartRx`\n    /// will trigger an interrupt for every received word to check the buffer level, which may\n    /// impact performance at high data rates.\n    ///\n    /// Has no effect on plain `Uart` or `UartRx` reads, which are specified to either\n    /// return a single word, a full buffer, or after line idle.\n    pub eager_reads: Option<usize>,\n\n    /// Set this to true if the line is considered noise free.\n    /// This will increase the receiver’s tolerance to clock deviations,\n    /// but will effectively disable noise detection.\n    #[cfg(not(usart_v1))]\n    pub assume_noise_free: bool,\n\n    /// Set this to true to swap the RX and TX pins.\n    #[cfg(any(usart_v3, usart_v4))]\n    pub swap_rx_tx: bool,\n\n    /// Set this to true to invert TX pin signal values (V<sub>DD</sub> = 0/mark, Gnd = 1/idle).\n    #[cfg(any(usart_v3, usart_v4))]\n    pub invert_tx: bool,\n\n    /// Set this to true to invert RX pin signal values (V<sub>DD</sub> = 0/mark, Gnd = 1/idle).\n    #[cfg(any(usart_v3, usart_v4))]\n    pub invert_rx: bool,\n\n    /// Set the pull configuration for the RX pin.\n    pub rx_pull: Pull,\n\n    /// Set the pull configuration for the CTS pin.\n    pub cts_pull: Pull,\n\n    /// Set the pin configuration for the TX pin.\n    pub tx_config: OutputConfig,\n\n    /// Set the pin configuration for the RTS pin.\n    pub rts_config: OutputConfig,\n\n    /// Set the pin configuration for the DE pin.\n    pub de_config: OutputConfig,\n\n    /// Set DE assertion time before the first start bit, 0-31 16ths of a bit period.\n    #[cfg(not(any(usart_v1, usart_v2)))]\n    pub de_assertion_time: u8,\n\n    /// Set DE deassertion time after the last stop bit, 0-31 16ths of a bit period.\n    #[cfg(not(any(usart_v1, usart_v2)))]\n    pub de_deassertion_time: u8,\n\n    // private: set by new_half_duplex, not by the user.\n    duplex: Duplex,\n}\n\nimpl Config {\n    fn tx_af(&self) -> AfType {\n        #[cfg(any(usart_v3, usart_v4))]\n        if self.swap_rx_tx {\n            return AfType::input(self.rx_pull);\n        };\n        self.tx_config.af_type()\n    }\n\n    fn rx_af(&self) -> AfType {\n        #[cfg(any(usart_v3, usart_v4))]\n        if self.swap_rx_tx {\n            return self.tx_config.af_type();\n        };\n        AfType::input(self.rx_pull)\n    }\n}\n\nimpl Default for Config {\n    fn default() -> Self {\n        Self {\n            baudrate: 115200,\n            data_bits: DataBits::DataBits8,\n            stop_bits: StopBits::STOP1,\n            parity: Parity::ParityNone,\n            // historical behavior\n            detect_previous_overrun: false,\n            eager_reads: None,\n            #[cfg(not(usart_v1))]\n            assume_noise_free: false,\n            #[cfg(any(usart_v3, usart_v4))]\n            swap_rx_tx: false,\n            #[cfg(any(usart_v3, usart_v4))]\n            invert_tx: false,\n            #[cfg(any(usart_v3, usart_v4))]\n            invert_rx: false,\n            rx_pull: Pull::None,\n            cts_pull: Pull::None,\n            tx_config: OutputConfig::PushPull,\n            rts_config: OutputConfig::PushPull,\n            de_config: OutputConfig::PushPull,\n            #[cfg(not(any(usart_v1, usart_v2)))]\n            de_assertion_time: 0,\n            #[cfg(not(any(usart_v1, usart_v2)))]\n            de_deassertion_time: 0,\n            duplex: Duplex::Full,\n        }\n    }\n}\n\n/// Serial error\n#[derive(Debug, Eq, PartialEq, Copy, Clone)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\n#[non_exhaustive]\npub enum Error {\n    /// Framing error\n    Framing,\n    /// Noise error\n    Noise,\n    /// RX buffer overrun\n    Overrun,\n    /// Parity check error\n    Parity,\n    /// Buffer too large for DMA\n    BufferTooLong,\n}\n\nimpl core::fmt::Display for Error {\n    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {\n        let message = match self {\n            Self::Framing => \"Framing Error\",\n            Self::Noise => \"Noise Error\",\n            Self::Overrun => \"RX Buffer Overrun\",\n            Self::Parity => \"Parity Check Error\",\n            Self::BufferTooLong => \"Buffer too large for DMA\",\n        };\n\n        write!(f, \"{}\", message)\n    }\n}\n\nimpl core::error::Error for Error {}\n\nenum ReadCompletionEvent {\n    // DMA Read transfer completed first\n    DmaCompleted,\n    // Idle line detected first\n    Idle(usize),\n}\n\n/// Bidirectional UART Driver, which acts as a combination of [`UartTx`] and [`UartRx`].\n///\n/// ### Notes on [`embedded_io::Read`]\n///\n/// `embedded_io::Read` requires guarantees that the base [`UartRx`] cannot provide.\n///\n/// See [`UartRx`] for more details, and see [`BufferedUart`] and [`RingBufferedUartRx`]\n/// as alternatives that do provide the necessary guarantees for `embedded_io::Read`.\npub struct Uart<'d, M: Mode> {\n    tx: UartTx<'d, M>,\n    rx: UartRx<'d, M>,\n}\n\nimpl<'d, M: Mode> SetConfig for Uart<'d, M> {\n    type Config = Config;\n    type ConfigError = ConfigError;\n\n    fn set_config(&mut self, config: &Self::Config) -> Result<(), Self::ConfigError> {\n        self.tx.set_config(config)?;\n        self.rx.set_config(config)\n    }\n}\n\n/// Tx-only UART Driver.\n///\n/// Can be obtained from [`Uart::split`], or can be constructed independently,\n/// if you do not need the receiving half of the driver.\npub struct UartTx<'d, M: Mode> {\n    info: &'static Info,\n    state: &'static State,\n    kernel_clock: Hertz,\n    _tx: Option<Flex<'d>>,\n    cts: Option<Flex<'d>>,\n    _de: Option<Flex<'d>>,\n    tx_dma: Option<ChannelAndRequest<'d>>,\n    duplex: Duplex,\n    _phantom: PhantomData<M>,\n}\n\nimpl<'d, M: Mode> SetConfig for UartTx<'d, M> {\n    type Config = Config;\n    type ConfigError = ConfigError;\n\n    fn set_config(&mut self, config: &Self::Config) -> Result<(), Self::ConfigError> {\n        self.set_config(config)\n    }\n}\n\n/// Rx-only UART Driver.\n///\n/// Can be obtained from [`Uart::split`], or can be constructed independently,\n/// if you do not need the transmitting half of the driver.\n///\n/// ### Notes on [`embedded_io::Read`]\n///\n/// `embedded_io::Read` requires guarantees that this struct cannot provide:\n///\n/// - Any data received between calls to [`UartRx::read`] or [`UartRx::blocking_read`]\n/// will be thrown away, as `UartRx` is unbuffered.\n/// Users of `embedded_io::Read` are likely to not expect this behavior\n/// (for instance if they read multiple small chunks in a row).\n/// - [`UartRx::read`] and [`UartRx::blocking_read`] only return once the entire buffer has been\n/// filled, whereas `embedded_io::Read` requires us to fill the buffer with what we already\n/// received, and only block/wait until the first byte arrived.\n/// <br />\n/// While [`UartRx::read_until_idle`] does return early, it will still eagerly wait for data until\n/// the buffer is full or no data has been transmitted in a while,\n/// which may not be what users of `embedded_io::Read` expect.\n///\n/// [`UartRx::into_ring_buffered`] can be called to equip `UartRx` with a buffer,\n/// that it can then use to store data received between calls to `read`,\n/// provided you are using DMA already.\n///\n/// Alternatively, you can use [`BufferedUartRx`], which is interrupt-based and which can also\n/// store data received between calls.\n///\n/// Also see [this github comment](https://github.com/embassy-rs/embassy/pull/2185#issuecomment-1810047043).\npub struct UartRx<'d, M: Mode> {\n    info: &'static Info,\n    state: &'static State,\n    kernel_clock: Hertz,\n    rx: Option<Flex<'d>>,\n    rts: Option<Flex<'d>>,\n    rx_dma: Option<ChannelAndRequest<'d>>,\n    detect_previous_overrun: bool,\n    #[cfg(any(usart_v1, usart_v2))]\n    buffered_sr: regs::Sr,\n    _phantom: PhantomData<M>,\n}\n\nimpl<'d, M: Mode> SetConfig for UartRx<'d, M> {\n    type Config = Config;\n    type ConfigError = ConfigError;\n\n    fn set_config(&mut self, config: &Self::Config) -> Result<(), Self::ConfigError> {\n        self.set_config(config)\n    }\n}\n\nimpl<'d> UartTx<'d, Async> {\n    /// Useful if you only want Uart Tx. It saves 1 pin and consumes a little less power.\n    pub fn new<T: Instance, D: TxDma<T>, #[cfg(afio)] A>(\n        peri: Peri<'d, T>,\n        tx: Peri<'d, if_afio!(impl TxPin<T, A>)>,\n        tx_dma: Peri<'d, D>,\n        _irq: impl interrupt::typelevel::Binding<D::Interrupt, crate::dma::InterruptHandler<D>> + 'd,\n        config: Config,\n    ) -> Result<Self, ConfigError> {\n        Self::new_inner(peri, new_pin!(tx, config.tx_af()), None, new_dma!(tx_dma, _irq), config)\n    }\n\n    /// Create a new tx-only UART with a clear-to-send pin\n    pub fn new_with_cts<T: Instance, D: TxDma<T>, #[cfg(afio)] A>(\n        peri: Peri<'d, T>,\n        tx: Peri<'d, if_afio!(impl TxPin<T, A>)>,\n        cts: Peri<'d, if_afio!(impl CtsPin<T, A>)>,\n        tx_dma: Peri<'d, D>,\n        _irq: impl interrupt::typelevel::Binding<D::Interrupt, crate::dma::InterruptHandler<D>> + 'd,\n        config: Config,\n    ) -> Result<Self, ConfigError> {\n        Self::new_inner(\n            peri,\n            new_pin!(tx, config.tx_af()),\n            new_pin!(cts, AfType::input(config.cts_pull)),\n            new_dma!(tx_dma, _irq),\n            config,\n        )\n    }\n\n    /// Initiate an asynchronous UART write\n    pub async fn write(&mut self, buffer: &[u8]) -> Result<(), Error> {\n        let _scoped_wake_guard = self.info.rcc.wake_guard();\n\n        let r = self.info.regs;\n\n        half_duplex_set_rx_tx_before_write(&r, self.duplex == Duplex::Half(HalfDuplexReadback::Readback));\n\n        let ch = self.tx_dma.as_mut().unwrap();\n        r.cr3().modify(|reg| {\n            reg.set_dmat(true);\n        });\n        // If we don't assign future to a variable, the data register pointer\n        // is held across an await and makes the future non-Send.\n        let transfer = unsafe { ch.write(buffer, tdr(r), Default::default()) };\n        transfer.await;\n        Ok(())\n    }\n\n    /// Wait until transmission complete\n    pub async fn flush(&mut self) -> Result<(), Error> {\n        let _scoped_wake_guard = self.info.rcc.wake_guard();\n\n        flush(&self.info, &self.state).await\n    }\n}\n\nimpl<'d> UartTx<'d, Blocking> {\n    /// Create a new blocking tx-only UART with no hardware flow control.\n    ///\n    /// Useful if you only want Uart Tx. It saves 1 pin and consumes a little less power.\n    pub fn new_blocking<T: Instance, #[cfg(afio)] A>(\n        peri: Peri<'d, T>,\n        tx: Peri<'d, if_afio!(impl TxPin<T, A>)>,\n        config: Config,\n    ) -> Result<Self, ConfigError> {\n        Self::new_inner(peri, new_pin!(tx, config.tx_af()), None, None, config)\n    }\n\n    /// Create a new blocking tx-only UART with a clear-to-send pin\n    pub fn new_blocking_with_cts<T: Instance, #[cfg(afio)] A>(\n        peri: Peri<'d, T>,\n        tx: Peri<'d, if_afio!(impl TxPin<T, A>)>,\n        cts: Peri<'d, if_afio!(impl CtsPin<T, A>)>,\n        config: Config,\n    ) -> Result<Self, ConfigError> {\n        Self::new_inner(\n            peri,\n            new_pin!(tx, config.tx_af()),\n            new_pin!(cts, AfType::input(config.cts_pull)),\n            None,\n            config,\n        )\n    }\n}\n\nimpl<'d, M: Mode> UartTx<'d, M> {\n    fn new_inner<T: Instance>(\n        _peri: Peri<'d, T>,\n        tx: Option<Flex<'d>>,\n        cts: Option<Flex<'d>>,\n        tx_dma: Option<ChannelAndRequest<'d>>,\n        config: Config,\n    ) -> Result<Self, ConfigError> {\n        let mut this = Self {\n            info: T::info(),\n            state: T::state(),\n            kernel_clock: T::frequency(),\n            _tx: tx,\n            cts,\n            _de: None,\n            tx_dma,\n            duplex: config.duplex,\n            _phantom: PhantomData,\n        };\n        this.enable_and_configure(&config)?;\n        Ok(this)\n    }\n\n    fn enable_and_configure(&mut self, config: &Config) -> Result<(), ConfigError> {\n        let info = self.info;\n        let state = self.state;\n        state.tx_rx_refcount.store(1, Ordering::Relaxed);\n\n        info.rcc.enable_and_reset_without_stop();\n\n        info.regs.cr3().modify(|w| {\n            w.set_ctse(self.cts.is_some());\n        });\n        configure(info, self.kernel_clock, config, false, true)?;\n\n        Ok(())\n    }\n\n    /// Reconfigure the driver\n    pub fn set_config(&mut self, config: &Config) -> Result<(), ConfigError> {\n        reconfigure(self.info, self.kernel_clock, config)\n    }\n\n    /// Write a single u8 if there is tx empty, otherwise return WouldBlock\n    pub(crate) fn nb_write(&mut self, byte: u8) -> Result<(), nb::Error<Error>> {\n        let r = self.info.regs;\n        let sr = sr(r).read();\n        if sr.txe() {\n            unsafe {\n                tdr(r).write_volatile(byte);\n            }\n            Ok(())\n        } else {\n            Err(nb::Error::WouldBlock)\n        }\n    }\n\n    /// Perform a blocking UART write\n    pub fn blocking_write(&mut self, buffer: &[u8]) -> Result<(), Error> {\n        let r = self.info.regs;\n\n        half_duplex_set_rx_tx_before_write(&r, self.duplex == Duplex::Half(HalfDuplexReadback::Readback));\n\n        for &b in buffer {\n            while !sr(r).read().txe() {}\n            unsafe { tdr(r).write_volatile(b) };\n        }\n        Ok(())\n    }\n\n    /// Block until transmission complete\n    pub fn blocking_flush(&mut self) -> Result<(), Error> {\n        blocking_flush(self.info)\n    }\n\n    /// Send break character\n    pub fn send_break(&self) {\n        send_break(&self.info.regs);\n    }\n\n    /// Set baudrate\n    pub fn set_baudrate(&self, baudrate: u32) -> Result<(), ConfigError> {\n        set_baudrate(self.info, self.kernel_clock, baudrate)\n    }\n}\n\n/// Wait until transmission complete\nasync fn flush(info: &Info, state: &State) -> Result<(), Error> {\n    let r = info.regs;\n    if r.cr1().read().te() && !sr(r).read().tc() {\n        r.cr1().modify(|w| {\n            // enable Transmission Complete interrupt\n            w.set_tcie(true);\n        });\n\n        compiler_fence(Ordering::SeqCst);\n\n        // future which completes when Transmission complete is detected\n        let abort = poll_fn(move |cx| {\n            state.tx_waker.register(cx.waker());\n\n            let sr = sr(r).read();\n            if sr.tc() {\n                // Transmission complete detected\n                return Poll::Ready(());\n            }\n\n            Poll::Pending\n        });\n\n        abort.await;\n    }\n\n    Ok(())\n}\n\nfn blocking_flush(info: &Info) -> Result<(), Error> {\n    let r = info.regs;\n    if r.cr1().read().te() {\n        while !sr(r).read().tc() {}\n    }\n\n    Ok(())\n}\n\n/// Send break character\npub fn send_break(regs: &Regs) {\n    // Busy wait until previous break has been sent\n    #[cfg(any(usart_v1, usart_v2))]\n    while regs.cr1().read().sbk() {}\n    #[cfg(any(usart_v3, usart_v4))]\n    while regs.isr().read().sbkf() {}\n\n    // Send break right after completing the current character transmission\n    #[cfg(any(usart_v1, usart_v2))]\n    regs.cr1().modify(|w| w.set_sbk(true));\n    #[cfg(any(usart_v3, usart_v4))]\n    regs.rqr().write(|w| w.set_sbkrq(true));\n}\n\n/// Enable Transmitter and disable Receiver for Half-Duplex mode\n/// In case of readback, keep Receiver enabled\nfn half_duplex_set_rx_tx_before_write(r: &Regs, enable_readback: bool) {\n    let mut cr1 = r.cr1().read();\n    if r.cr3().read().hdsel() {\n        cr1.set_te(true);\n        cr1.set_re(enable_readback);\n        r.cr1().write_value(cr1);\n    }\n}\n\nimpl<'d> UartRx<'d, Async> {\n    /// Create a new rx-only UART with no hardware flow control.\n    ///\n    /// Useful if you only want Uart Rx. It saves 1 pin and consumes a little less power.\n    pub fn new<T: Instance, D: RxDma<T>, #[cfg(afio)] A>(\n        peri: Peri<'d, T>,\n        rx: Peri<'d, if_afio!(impl RxPin<T, A>)>,\n        rx_dma: Peri<'d, D>,\n        _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>>\n        + interrupt::typelevel::Binding<D::Interrupt, crate::dma::InterruptHandler<D>>\n        + 'd,\n        config: Config,\n    ) -> Result<Self, ConfigError> {\n        Self::new_inner(peri, new_pin!(rx, config.rx_af()), None, new_dma!(rx_dma, _irq), config)\n    }\n\n    /// Create a new rx-only UART with a request-to-send pin\n    pub fn new_with_rts<T: Instance, D: RxDma<T>, #[cfg(afio)] A>(\n        peri: Peri<'d, T>,\n        rx: Peri<'d, if_afio!(impl RxPin<T, A>)>,\n        rts: Peri<'d, if_afio!(impl RtsPin<T, A>)>,\n        rx_dma: Peri<'d, D>,\n        _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>>\n        + interrupt::typelevel::Binding<D::Interrupt, crate::dma::InterruptHandler<D>>\n        + 'd,\n        config: Config,\n    ) -> Result<Self, ConfigError> {\n        Self::new_inner(\n            peri,\n            new_pin!(rx, config.rx_af()),\n            new_pin!(rts, config.rts_config.af_type()),\n            new_dma!(rx_dma, _irq),\n            config,\n        )\n    }\n\n    /// Initiate an asynchronous UART read\n    pub async fn read(&mut self, buffer: &mut [u8]) -> Result<(), Error> {\n        let _scoped_wake_guard = self.info.rcc.wake_guard();\n\n        self.inner_read(buffer, false).await?;\n\n        Ok(())\n    }\n\n    /// Initiate an asynchronous read with idle line detection enabled\n    pub async fn read_until_idle(&mut self, buffer: &mut [u8]) -> Result<usize, Error> {\n        let _scoped_wake_guard = self.info.rcc.wake_guard();\n\n        self.inner_read(buffer, true).await\n    }\n\n    async fn inner_read_run(\n        &mut self,\n        buffer: &mut [u8],\n        enable_idle_line_detection: bool,\n    ) -> Result<ReadCompletionEvent, Error> {\n        let r = self.info.regs;\n\n        // Call flush for Half-Duplex mode if some bytes were written and flush was not called.\n        // It prevents reading of bytes which have just been written.\n        if r.cr3().read().hdsel() && r.cr1().read().te() {\n            flush(&self.info, &self.state).await?;\n\n            // Disable Transmitter and enable Receiver after flush\n            r.cr1().modify(|reg| {\n                reg.set_re(true);\n                reg.set_te(false);\n            });\n        }\n\n        // make sure USART state is restored to neutral state when this future is dropped\n        let on_drop = OnDrop::new(move || {\n            // clear all interrupts and DMA Rx Request\n            r.cr1().modify(|w| {\n                // disable RXNE interrupt\n                w.set_rxneie(false);\n                // disable parity interrupt\n                w.set_peie(false);\n                // disable idle line interrupt\n                w.set_idleie(false);\n            });\n            r.cr3().modify(|w| {\n                // disable Error Interrupt: (Frame error, Noise error, Overrun error)\n                w.set_eie(false);\n                // disable DMA Rx Request\n                w.set_dmar(false);\n            });\n        });\n\n        let ch = self.rx_dma.as_mut().unwrap();\n\n        let buffer_len = buffer.len();\n\n        // Start USART DMA\n        // will not do anything yet because DMAR is not yet set\n        // future which will complete when DMA Read request completes\n        let transfer = unsafe { ch.read(rdr(r), buffer, Default::default()) };\n\n        // clear ORE flag just before enabling DMA Rx Request: can be mandatory for the second transfer\n        if !self.detect_previous_overrun {\n            let sr = sr(r).read();\n            // This read also clears the error and idle interrupt flags on v1.\n            unsafe { rdr(r).read_volatile() };\n            clear_interrupt_flags(r, sr);\n        }\n\n        r.cr1().modify(|w| {\n            // disable RXNE interrupt\n            w.set_rxneie(false);\n            // enable parity interrupt if not ParityNone\n            w.set_peie(w.pce());\n        });\n\n        r.cr3().modify(|w| {\n            // enable Error Interrupt: (Frame error, Noise error, Overrun error)\n            w.set_eie(true);\n            // enable DMA Rx Request\n            w.set_dmar(true);\n        });\n\n        compiler_fence(Ordering::SeqCst);\n\n        // In case of errors already pending when reception started, interrupts may have already been raised\n        // and lead to reception abortion (Overrun error for instance). In such a case, all interrupts\n        // have been disabled in interrupt handler and DMA Rx Request has been disabled.\n\n        let cr3 = r.cr3().read();\n\n        if !cr3.dmar() {\n            // something went wrong\n            // because the only way to get this flag cleared is to have an interrupt\n\n            // DMA will be stopped when transfer is dropped\n\n            let sr = sr(r).read();\n            // This read also clears the error and idle interrupt flags on v1.\n            unsafe { rdr(r).read_volatile() };\n            clear_interrupt_flags(r, sr);\n\n            if sr.pe() {\n                return Err(Error::Parity);\n            }\n            if sr.fe() {\n                return Err(Error::Framing);\n            }\n            if sr.ne() {\n                return Err(Error::Noise);\n            }\n            if sr.ore() {\n                return Err(Error::Overrun);\n            }\n\n            unreachable!();\n        }\n\n        if enable_idle_line_detection {\n            // clear idle flag\n            let sr = sr(r).read();\n            // This read also clears the error and idle interrupt flags on v1.\n            unsafe { rdr(r).read_volatile() };\n            clear_interrupt_flags(r, sr);\n\n            // enable idle interrupt\n            r.cr1().modify(|w| {\n                w.set_idleie(true);\n            });\n        }\n\n        compiler_fence(Ordering::SeqCst);\n\n        // future which completes when idle line or error is detected\n        let s = self.state;\n        let abort = poll_fn(move |cx| {\n            s.rx_waker.register(cx.waker());\n\n            let sr = sr(r).read();\n\n            // This read also clears the error and idle interrupt flags on v1.\n            unsafe { rdr(r).read_volatile() };\n            clear_interrupt_flags(r, sr);\n\n            if enable_idle_line_detection {\n                // enable idle interrupt\n                r.cr1().modify(|w| {\n                    w.set_idleie(true);\n                });\n            }\n\n            compiler_fence(Ordering::SeqCst);\n\n            let has_errors = sr.pe() || sr.fe() || sr.ne() || sr.ore();\n\n            if has_errors {\n                // all Rx interrupts and Rx DMA Request have already been cleared in interrupt handler\n\n                if sr.pe() {\n                    return Poll::Ready(Err(Error::Parity));\n                }\n                if sr.fe() {\n                    return Poll::Ready(Err(Error::Framing));\n                }\n                if sr.ne() {\n                    return Poll::Ready(Err(Error::Noise));\n                }\n                if sr.ore() {\n                    return Poll::Ready(Err(Error::Overrun));\n                }\n            }\n\n            if enable_idle_line_detection && sr.idle() {\n                // Idle line detected\n                return Poll::Ready(Ok(()));\n            }\n\n            Poll::Pending\n        });\n\n        // wait for the first of DMA request or idle line detected to completes\n        // select consumes its arguments\n        // when transfer is dropped, it will stop the DMA request\n        let r = match select(transfer, abort).await {\n            // DMA transfer completed first\n            Either::Left(((), _)) => Ok(ReadCompletionEvent::DmaCompleted),\n\n            // Idle line detected first\n            Either::Right((Ok(()), transfer)) => Ok(ReadCompletionEvent::Idle(\n                buffer_len - transfer.get_remaining_transfers() as usize,\n            )),\n\n            // error occurred\n            Either::Right((Err(e), _)) => Err(e),\n        };\n\n        drop(on_drop);\n\n        r\n    }\n\n    async fn inner_read(&mut self, buffer: &mut [u8], enable_idle_line_detection: bool) -> Result<usize, Error> {\n        if buffer.is_empty() {\n            return Ok(0);\n        } else if buffer.len() > 0xFFFF {\n            return Err(Error::BufferTooLong);\n        }\n\n        let buffer_len = buffer.len();\n\n        // wait for DMA to complete or IDLE line detection if requested\n        let res = self.inner_read_run(buffer, enable_idle_line_detection).await;\n\n        match res {\n            Ok(ReadCompletionEvent::DmaCompleted) => Ok(buffer_len),\n            Ok(ReadCompletionEvent::Idle(n)) => Ok(n),\n            Err(e) => Err(e),\n        }\n    }\n}\n\nimpl<'d> UartRx<'d, Blocking> {\n    /// Create a new rx-only UART with no hardware flow control.\n    ///\n    /// Useful if you only want Uart Rx. It saves 1 pin and consumes a little less power.\n    pub fn new_blocking<T: Instance, #[cfg(afio)] A>(\n        peri: Peri<'d, T>,\n        rx: Peri<'d, if_afio!(impl RxPin<T, A>)>,\n        config: Config,\n    ) -> Result<Self, ConfigError> {\n        Self::new_inner(peri, new_pin!(rx, config.rx_af()), None, None, config)\n    }\n\n    /// Create a new rx-only UART with a request-to-send pin\n    pub fn new_blocking_with_rts<T: Instance, #[cfg(afio)] A>(\n        peri: Peri<'d, T>,\n        rx: Peri<'d, if_afio!(impl RxPin<T, A>)>,\n        rts: Peri<'d, if_afio!(impl RtsPin<T, A>)>,\n        config: Config,\n    ) -> Result<Self, ConfigError> {\n        Self::new_inner(\n            peri,\n            new_pin!(rx, config.rx_af()),\n            new_pin!(rts, config.rts_config.af_type()),\n            None,\n            config,\n        )\n    }\n}\n\nimpl<'d, M: Mode> UartRx<'d, M> {\n    fn new_inner<T: Instance>(\n        _peri: Peri<'d, T>,\n        rx: Option<Flex<'d>>,\n        rts: Option<Flex<'d>>,\n        rx_dma: Option<ChannelAndRequest<'d>>,\n        config: Config,\n    ) -> Result<Self, ConfigError> {\n        let mut this = Self {\n            _phantom: PhantomData,\n            info: T::info(),\n            state: T::state(),\n            kernel_clock: T::frequency(),\n            rx,\n            rts,\n            rx_dma,\n            detect_previous_overrun: config.detect_previous_overrun,\n            #[cfg(any(usart_v1, usart_v2))]\n            buffered_sr: regs::Sr(0),\n        };\n        this.enable_and_configure(&config)?;\n        Ok(this)\n    }\n\n    fn enable_and_configure(&mut self, config: &Config) -> Result<(), ConfigError> {\n        let info = self.info;\n        let state = self.state;\n        state.tx_rx_refcount.store(1, Ordering::Relaxed);\n        state\n            .eager_reads\n            .store(config.eager_reads.unwrap_or(0), Ordering::Relaxed);\n\n        info.rcc.enable_and_reset_without_stop();\n\n        info.regs.cr3().write(|w| {\n            w.set_rtse(self.rts.is_some());\n        });\n        configure(info, self.kernel_clock, &config, true, false)?;\n\n        info.interrupt.unpend();\n        unsafe { info.interrupt.enable() };\n\n        Ok(())\n    }\n\n    /// Reconfigure the driver\n    pub fn set_config(&mut self, config: &Config) -> Result<(), ConfigError> {\n        self.state\n            .eager_reads\n            .store(config.eager_reads.unwrap_or(0), Ordering::Relaxed);\n        reconfigure(self.info, self.kernel_clock, config)\n    }\n\n    #[cfg(any(usart_v1, usart_v2))]\n    fn check_rx_flags(&mut self) -> Result<bool, Error> {\n        let r = self.info.regs;\n        loop {\n            // Handle all buffered error flags.\n            if self.buffered_sr.pe() {\n                self.buffered_sr.set_pe(false);\n                return Err(Error::Parity);\n            } else if self.buffered_sr.fe() {\n                self.buffered_sr.set_fe(false);\n                return Err(Error::Framing);\n            } else if self.buffered_sr.ne() {\n                self.buffered_sr.set_ne(false);\n                return Err(Error::Noise);\n            } else if self.buffered_sr.ore() {\n                self.buffered_sr.set_ore(false);\n                return Err(Error::Overrun);\n            } else if self.buffered_sr.rxne() {\n                self.buffered_sr.set_rxne(false);\n                return Ok(true);\n            } else {\n                // No error flags from previous iterations were set: Check the actual status register\n                let sr = r.sr().read();\n                if !sr.rxne() {\n                    return Ok(false);\n                }\n\n                // Buffer the status register and let the loop handle the error flags.\n                self.buffered_sr = sr;\n            }\n        }\n    }\n\n    #[cfg(any(usart_v3, usart_v4))]\n    fn check_rx_flags(&mut self) -> Result<bool, Error> {\n        let r = self.info.regs;\n        let sr = r.isr().read();\n        if sr.pe() {\n            r.icr().write(|w| w.set_pe(true));\n            return Err(Error::Parity);\n        } else if sr.fe() {\n            r.icr().write(|w| w.set_fe(true));\n            return Err(Error::Framing);\n        } else if sr.ne() {\n            r.icr().write(|w| w.set_ne(true));\n            return Err(Error::Noise);\n        } else if sr.ore() {\n            r.icr().write(|w| w.set_ore(true));\n            return Err(Error::Overrun);\n        }\n        Ok(sr.rxne())\n    }\n\n    /// Read a single u8 if there is one available, otherwise return WouldBlock\n    pub(crate) fn nb_read(&mut self) -> Result<u8, nb::Error<Error>> {\n        let r = self.info.regs;\n        if self.check_rx_flags()? {\n            Ok(unsafe { rdr(r).read_volatile() })\n        } else {\n            Err(nb::Error::WouldBlock)\n        }\n    }\n\n    /// Perform a blocking read into `buffer`\n    pub fn blocking_read(&mut self, buffer: &mut [u8]) -> Result<(), Error> {\n        let r = self.info.regs;\n\n        // Call flush for Half-Duplex mode if some bytes were written and flush was not called.\n        // It prevents reading of bytes which have just been written.\n        if r.cr3().read().hdsel() && r.cr1().read().te() {\n            blocking_flush(self.info)?;\n\n            // Disable Transmitter and enable Receiver after flush\n            r.cr1().modify(|reg| {\n                reg.set_re(true);\n                reg.set_te(false);\n            });\n        }\n\n        for b in buffer {\n            while !self.check_rx_flags()? {}\n            unsafe { *b = rdr(r).read_volatile() }\n        }\n        Ok(())\n    }\n\n    /// Set baudrate\n    pub fn set_baudrate(&self, baudrate: u32) -> Result<(), ConfigError> {\n        set_baudrate(self.info, self.kernel_clock, baudrate)\n    }\n}\n\nimpl<'d, M: Mode> Drop for UartTx<'d, M> {\n    fn drop(&mut self) {\n        drop_tx_rx(self.info, self.state);\n    }\n}\n\nimpl<'d, M: Mode> Drop for UartRx<'d, M> {\n    fn drop(&mut self) {\n        drop_tx_rx(self.info, self.state);\n    }\n}\n\nfn drop_tx_rx(info: &Info, state: &State) {\n    // We cannot use atomic subtraction here, because it's not supported for all targets\n    let is_last_drop = critical_section::with(|_| {\n        let refcount = state.tx_rx_refcount.load(Ordering::Relaxed);\n        assert!(refcount >= 1);\n        state.tx_rx_refcount.store(refcount - 1, Ordering::Relaxed);\n        refcount == 1\n    });\n    if is_last_drop {\n        info.rcc.disable_without_stop();\n    }\n}\n\nimpl<'d> Uart<'d, Async> {\n    /// Create a new bidirectional UART\n    pub fn new<T: Instance, D1: TxDma<T>, D2: RxDma<T>, #[cfg(afio)] A>(\n        peri: Peri<'d, T>,\n        rx: Peri<'d, if_afio!(impl RxPin<T, A>)>,\n        tx: Peri<'d, if_afio!(impl TxPin<T, A>)>,\n        tx_dma: Peri<'d, D1>,\n        rx_dma: Peri<'d, D2>,\n        _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>>\n        + interrupt::typelevel::Binding<D1::Interrupt, crate::dma::InterruptHandler<D1>>\n        + interrupt::typelevel::Binding<D2::Interrupt, crate::dma::InterruptHandler<D2>>\n        + 'd,\n        config: Config,\n    ) -> Result<Self, ConfigError> {\n        Self::new_inner(\n            peri,\n            new_pin!(rx, config.rx_af()),\n            new_pin!(tx, config.tx_af()),\n            None,\n            None,\n            None,\n            new_dma!(tx_dma, _irq),\n            new_dma!(rx_dma, _irq),\n            config,\n        )\n    }\n\n    /// Create a new bidirectional UART with request-to-send and clear-to-send pins\n    pub fn new_with_rtscts<T: Instance, D1: TxDma<T>, D2: RxDma<T>, #[cfg(afio)] A>(\n        peri: Peri<'d, T>,\n        rx: Peri<'d, if_afio!(impl RxPin<T, A>)>,\n        tx: Peri<'d, if_afio!(impl TxPin<T, A>)>,\n        rts: Peri<'d, if_afio!(impl RtsPin<T, A>)>,\n        cts: Peri<'d, if_afio!(impl CtsPin<T, A>)>,\n        tx_dma: Peri<'d, D1>,\n        rx_dma: Peri<'d, D2>,\n        _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>>\n        + interrupt::typelevel::Binding<D1::Interrupt, crate::dma::InterruptHandler<D1>>\n        + interrupt::typelevel::Binding<D2::Interrupt, crate::dma::InterruptHandler<D2>>\n        + 'd,\n        config: Config,\n    ) -> Result<Self, ConfigError> {\n        Self::new_inner(\n            peri,\n            new_pin!(rx, config.rx_af()),\n            new_pin!(tx, config.tx_af()),\n            new_pin!(rts, config.rts_config.af_type()),\n            new_pin!(cts, AfType::input(config.cts_pull)),\n            None,\n            new_dma!(tx_dma, _irq),\n            new_dma!(rx_dma, _irq),\n            config,\n        )\n    }\n\n    #[cfg(not(any(usart_v1, usart_v2)))]\n    /// Create a new bidirectional UART with a driver-enable pin\n    pub fn new_with_de<T: Instance, D1: TxDma<T>, D2: RxDma<T>, #[cfg(afio)] A>(\n        peri: Peri<'d, T>,\n        rx: Peri<'d, if_afio!(impl RxPin<T, A>)>,\n        tx: Peri<'d, if_afio!(impl TxPin<T, A>)>,\n        de: Peri<'d, if_afio!(impl DePin<T, A>)>,\n        tx_dma: Peri<'d, D1>,\n        rx_dma: Peri<'d, D2>,\n        _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>>\n        + interrupt::typelevel::Binding<D1::Interrupt, crate::dma::InterruptHandler<D1>>\n        + interrupt::typelevel::Binding<D2::Interrupt, crate::dma::InterruptHandler<D2>>\n        + 'd,\n        config: Config,\n    ) -> Result<Self, ConfigError> {\n        Self::new_inner(\n            peri,\n            new_pin!(rx, config.rx_af()),\n            new_pin!(tx, config.tx_af()),\n            None,\n            None,\n            new_pin!(de, config.de_config.af_type()),\n            new_dma!(tx_dma, _irq),\n            new_dma!(rx_dma, _irq),\n            config,\n        )\n    }\n\n    /// Create a single-wire half-duplex Uart transceiver on a single Tx pin.\n    ///\n    /// See [`new_half_duplex_on_rx`][`Self::new_half_duplex_on_rx`] if you would prefer to use an Rx pin\n    /// (when it is available for your chip). There is no functional difference between these methods, as both\n    /// allow bidirectional communication.\n    ///\n    /// The TX pin is always released when no data is transmitted. Thus, it acts as a standard\n    /// I/O in idle or in reception. It means that the I/O must be configured so that TX is\n    /// configured as alternate function open-drain with an external pull-up\n    /// Apart from this, the communication protocol is similar to normal USART mode. Any conflict\n    /// on the line must be managed by software (for instance by using a centralized arbiter).\n    #[doc(alias(\"HDSEL\"))]\n    pub fn new_half_duplex<T: Instance, D1: TxDma<T>, D2: RxDma<T>, #[cfg(afio)] A>(\n        peri: Peri<'d, T>,\n        tx: Peri<'d, if_afio!(impl TxPin<T, A>)>,\n        tx_dma: Peri<'d, D1>,\n        rx_dma: Peri<'d, D2>,\n        _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>>\n        + interrupt::typelevel::Binding<D1::Interrupt, crate::dma::InterruptHandler<D1>>\n        + interrupt::typelevel::Binding<D2::Interrupt, crate::dma::InterruptHandler<D2>>\n        + 'd,\n        mut config: Config,\n        readback: HalfDuplexReadback,\n    ) -> Result<Self, ConfigError> {\n        #[cfg(not(any(usart_v1, usart_v2)))]\n        {\n            config.swap_rx_tx = false;\n        }\n        config.duplex = Duplex::Half(readback);\n\n        Self::new_inner(\n            peri,\n            None,\n            new_pin!(tx, config.tx_af()),\n            None,\n            None,\n            None,\n            new_dma!(tx_dma, _irq),\n            new_dma!(rx_dma, _irq),\n            config,\n        )\n    }\n\n    /// Create a single-wire half-duplex Uart transceiver on a single Rx pin.\n    ///\n    /// See [`new_half_duplex`][`Self::new_half_duplex`] if you would prefer to use an Tx pin.\n    /// There is no functional difference between these methods, as both allow bidirectional communication.\n    ///\n    /// The pin is always released when no data is transmitted. Thus, it acts as a standard\n    /// I/O in idle or in reception.\n    /// Apart from this, the communication protocol is similar to normal USART mode. Any conflict\n    /// on the line must be managed by software (for instance by using a centralized arbiter).\n    #[cfg(not(any(usart_v1, usart_v2)))]\n    #[doc(alias(\"HDSEL\"))]\n    pub fn new_half_duplex_on_rx<T: Instance, D1: TxDma<T>, D2: RxDma<T>, #[cfg(afio)] A>(\n        peri: Peri<'d, T>,\n        rx: Peri<'d, if_afio!(impl RxPin<T, A>)>,\n        tx_dma: Peri<'d, D1>,\n        rx_dma: Peri<'d, D2>,\n        _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>>\n        + interrupt::typelevel::Binding<D1::Interrupt, crate::dma::InterruptHandler<D1>>\n        + interrupt::typelevel::Binding<D2::Interrupt, crate::dma::InterruptHandler<D2>>\n        + 'd,\n        mut config: Config,\n        readback: HalfDuplexReadback,\n    ) -> Result<Self, ConfigError> {\n        config.swap_rx_tx = true;\n        config.duplex = Duplex::Half(readback);\n\n        Self::new_inner(\n            peri,\n            None,\n            None,\n            new_pin!(rx, config.rx_af()),\n            None,\n            None,\n            new_dma!(tx_dma, _irq),\n            new_dma!(rx_dma, _irq),\n            config,\n        )\n    }\n\n    /// Perform an asynchronous write\n    pub async fn write(&mut self, buffer: &[u8]) -> Result<(), Error> {\n        self.tx.write(buffer).await\n    }\n\n    /// Wait until transmission complete\n    pub async fn flush(&mut self) -> Result<(), Error> {\n        self.tx.flush().await\n    }\n\n    /// Perform an asynchronous read into `buffer`\n    pub async fn read(&mut self, buffer: &mut [u8]) -> Result<(), Error> {\n        self.rx.read(buffer).await\n    }\n\n    /// Perform an an asynchronous read with idle line detection enabled\n    pub async fn read_until_idle(&mut self, buffer: &mut [u8]) -> Result<usize, Error> {\n        self.rx.read_until_idle(buffer).await\n    }\n}\n\nimpl<'d> Uart<'d, Blocking> {\n    /// Create a new blocking bidirectional UART.\n    pub fn new_blocking<T: Instance, #[cfg(afio)] A>(\n        peri: Peri<'d, T>,\n        rx: Peri<'d, if_afio!(impl RxPin<T, A>)>,\n        tx: Peri<'d, if_afio!(impl TxPin<T, A>)>,\n        config: Config,\n    ) -> Result<Self, ConfigError> {\n        Self::new_inner(\n            peri,\n            new_pin!(rx, config.rx_af()),\n            new_pin!(tx, config.tx_af()),\n            None,\n            None,\n            None,\n            None,\n            None,\n            config,\n        )\n    }\n\n    /// Create a new bidirectional UART with request-to-send and clear-to-send pins\n    pub fn new_blocking_with_rtscts<T: Instance, #[cfg(afio)] A>(\n        peri: Peri<'d, T>,\n        rx: Peri<'d, if_afio!(impl RxPin<T, A>)>,\n        tx: Peri<'d, if_afio!(impl TxPin<T, A>)>,\n        rts: Peri<'d, if_afio!(impl RtsPin<T, A>)>,\n        cts: Peri<'d, if_afio!(impl CtsPin<T, A>)>,\n        config: Config,\n    ) -> Result<Self, ConfigError> {\n        Self::new_inner(\n            peri,\n            new_pin!(rx, config.rx_af()),\n            new_pin!(tx, config.tx_af()),\n            new_pin!(rts, config.rts_config.af_type()),\n            new_pin!(cts, AfType::input(config.cts_pull)),\n            None,\n            None,\n            None,\n            config,\n        )\n    }\n\n    #[cfg(not(any(usart_v1, usart_v2)))]\n    /// Create a new bidirectional UART with a driver-enable pin\n    pub fn new_blocking_with_de<T: Instance, #[cfg(afio)] A>(\n        peri: Peri<'d, T>,\n        rx: Peri<'d, if_afio!(impl RxPin<T, A>)>,\n        tx: Peri<'d, if_afio!(impl TxPin<T, A>)>,\n        de: Peri<'d, if_afio!(impl DePin<T, A>)>,\n        config: Config,\n    ) -> Result<Self, ConfigError> {\n        Self::new_inner(\n            peri,\n            new_pin!(rx, config.rx_af()),\n            new_pin!(tx, config.tx_af()),\n            None,\n            None,\n            new_pin!(de, config.de_config.af_type()),\n            None,\n            None,\n            config,\n        )\n    }\n\n    /// Create a single-wire half-duplex Uart transceiver on a single Tx pin.\n    ///\n    /// See [`new_half_duplex_on_rx`][`Self::new_half_duplex_on_rx`] if you would prefer to use an Rx pin\n    /// (when it is available for your chip). There is no functional difference between these methods, as both\n    /// allow bidirectional communication.\n    ///\n    /// The pin is always released when no data is transmitted. Thus, it acts as a standard\n    /// I/O in idle or in reception.\n    /// Apart from this, the communication protocol is similar to normal USART mode. Any conflict\n    /// on the line must be managed by software (for instance by using a centralized arbiter).\n    #[doc(alias(\"HDSEL\"))]\n    pub fn new_blocking_half_duplex<T: Instance, #[cfg(afio)] A>(\n        peri: Peri<'d, T>,\n        tx: Peri<'d, if_afio!(impl TxPin<T, A>)>,\n        mut config: Config,\n        readback: HalfDuplexReadback,\n    ) -> Result<Self, ConfigError> {\n        #[cfg(not(any(usart_v1, usart_v2)))]\n        {\n            config.swap_rx_tx = false;\n        }\n        config.duplex = Duplex::Half(readback);\n\n        Self::new_inner(\n            peri,\n            None,\n            new_pin!(tx, config.tx_af()),\n            None,\n            None,\n            None,\n            None,\n            None,\n            config,\n        )\n    }\n\n    /// Create a single-wire half-duplex Uart transceiver on a single Rx pin.\n    ///\n    /// See [`new_half_duplex`][`Self::new_half_duplex`] if you would prefer to use an Tx pin.\n    /// There is no functional difference between these methods, as both allow bidirectional communication.\n    ///\n    /// The pin is always released when no data is transmitted. Thus, it acts as a standard\n    /// I/O in idle or in reception.\n    /// Apart from this, the communication protocol is similar to normal USART mode. Any conflict\n    /// on the line must be managed by software (for instance by using a centralized arbiter).\n    #[cfg(not(any(usart_v1, usart_v2)))]\n    #[doc(alias(\"HDSEL\"))]\n    pub fn new_blocking_half_duplex_on_rx<T: Instance, #[cfg(afio)] A>(\n        peri: Peri<'d, T>,\n        rx: Peri<'d, if_afio!(impl RxPin<T, A>)>,\n        mut config: Config,\n        readback: HalfDuplexReadback,\n    ) -> Result<Self, ConfigError> {\n        config.swap_rx_tx = true;\n        config.duplex = Duplex::Half(readback);\n\n        Self::new_inner(\n            peri,\n            None,\n            None,\n            new_pin!(rx, config.rx_af()),\n            None,\n            None,\n            None,\n            None,\n            config,\n        )\n    }\n}\n\nimpl<'d, M: Mode> Uart<'d, M> {\n    fn new_inner<T: Instance>(\n        _peri: Peri<'d, T>,\n        rx: Option<Flex<'d>>,\n        tx: Option<Flex<'d>>,\n        rts: Option<Flex<'d>>,\n        cts: Option<Flex<'d>>,\n        de: Option<Flex<'d>>,\n        tx_dma: Option<ChannelAndRequest<'d>>,\n        rx_dma: Option<ChannelAndRequest<'d>>,\n        config: Config,\n    ) -> Result<Self, ConfigError> {\n        let info = T::info();\n        let state = T::state();\n        let kernel_clock = T::frequency();\n\n        let mut this = Self {\n            tx: UartTx {\n                _phantom: PhantomData,\n                info,\n                state,\n                kernel_clock,\n                _tx: tx,\n                cts,\n                _de: de,\n                tx_dma,\n                duplex: config.duplex,\n            },\n            rx: UartRx {\n                _phantom: PhantomData,\n                info,\n                state,\n                kernel_clock,\n                rx,\n                rts,\n                rx_dma,\n                detect_previous_overrun: config.detect_previous_overrun,\n                #[cfg(any(usart_v1, usart_v2))]\n                buffered_sr: regs::Sr(0),\n            },\n        };\n        this.enable_and_configure(&config)?;\n        Ok(this)\n    }\n\n    fn enable_and_configure(&mut self, config: &Config) -> Result<(), ConfigError> {\n        let info = self.rx.info;\n        let state = self.rx.state;\n        state.tx_rx_refcount.store(2, Ordering::Relaxed);\n        state\n            .eager_reads\n            .store(config.eager_reads.unwrap_or(0), Ordering::Relaxed);\n\n        info.rcc.enable_and_reset_without_stop();\n\n        info.regs.cr3().write(|w| {\n            w.set_rtse(self.rx.rts.is_some());\n            w.set_ctse(self.tx.cts.is_some());\n            #[cfg(not(any(usart_v1, usart_v2)))]\n            w.set_dem(self.tx._de.is_some());\n        });\n        configure(info, self.rx.kernel_clock, config, true, true)?;\n\n        info.interrupt.unpend();\n        unsafe { info.interrupt.enable() };\n\n        Ok(())\n    }\n\n    /// Perform a blocking write\n    pub fn blocking_write(&mut self, buffer: &[u8]) -> Result<(), Error> {\n        self.tx.blocking_write(buffer)\n    }\n\n    /// Block until transmission complete\n    pub fn blocking_flush(&mut self) -> Result<(), Error> {\n        self.tx.blocking_flush()\n    }\n\n    /// Read a single `u8` or return `WouldBlock`\n    pub(crate) fn nb_read(&mut self) -> Result<u8, nb::Error<Error>> {\n        self.rx.nb_read()\n    }\n\n    /// Perform a blocking read into `buffer`\n    pub fn blocking_read(&mut self, buffer: &mut [u8]) -> Result<(), Error> {\n        self.rx.blocking_read(buffer)\n    }\n\n    /// Split the Uart into a transmitter and receiver, which is\n    /// particularly useful when having two tasks correlating to\n    /// transmitting and receiving.\n    pub fn split(self) -> (UartTx<'d, M>, UartRx<'d, M>) {\n        (self.tx, self.rx)\n    }\n\n    /// Split the Uart into a transmitter and receiver by mutable reference,\n    /// which is particularly useful when having two tasks correlating to\n    /// transmitting and receiving.\n    pub fn split_ref(&mut self) -> (&mut UartTx<'d, M>, &mut UartRx<'d, M>) {\n        (&mut self.tx, &mut self.rx)\n    }\n\n    /// Send break character\n    pub fn send_break(&self) {\n        self.tx.send_break();\n    }\n\n    /// Set baudrate\n    pub fn set_baudrate(&self, baudrate: u32) -> Result<(), ConfigError> {\n        self.tx.set_baudrate(baudrate)?;\n        self.rx.set_baudrate(baudrate)?;\n        Ok(())\n    }\n}\n\nfn reconfigure(info: &Info, kernel_clock: Hertz, config: &Config) -> Result<(), ConfigError> {\n    info.interrupt.disable();\n    let r = info.regs;\n\n    let cr = r.cr1().read();\n    configure(info, kernel_clock, config, cr.re(), cr.te())?;\n\n    info.interrupt.unpend();\n    unsafe { info.interrupt.enable() };\n\n    Ok(())\n}\n\nfn calculate_brr(baud: u32, pclk: u32, presc: u32, mul: u32) -> u32 {\n    // The calculation to be done to get the BRR is `mul * pclk / presc / baud`\n    // To do this in 32-bit only we can't multiply `mul` and `pclk`\n    let clock = pclk / presc;\n\n    // The mul is applied as the last operation to prevent overflow\n    let brr = clock / baud * mul;\n\n    // The BRR calculation will be a bit off because of integer rounding.\n    // Because we multiplied our inaccuracy with mul, our rounding now needs to be in proportion to mul.\n    let rounding = ((clock % baud) * mul + (baud / 2)) / baud;\n\n    brr + rounding\n}\n\nfn set_baudrate(info: &Info, kernel_clock: Hertz, baudrate: u32) -> Result<(), ConfigError> {\n    info.interrupt.disable();\n\n    set_usart_baudrate(info, kernel_clock, baudrate)?;\n\n    info.interrupt.unpend();\n    unsafe { info.interrupt.enable() };\n\n    Ok(())\n}\n\nfn find_and_set_brr(r: Regs, kind: Kind, kernel_clock: Hertz, baudrate: u32) -> Result<bool, ConfigError> {\n    #[cfg(not(usart_v4))]\n    static DIVS: [(u16, ()); 1] = [(1, ())];\n\n    #[cfg(usart_v4)]\n    static DIVS: [(u16, vals::Presc); 12] = [\n        (1, vals::Presc::DIV1),\n        (2, vals::Presc::DIV2),\n        (4, vals::Presc::DIV4),\n        (6, vals::Presc::DIV6),\n        (8, vals::Presc::DIV8),\n        (10, vals::Presc::DIV10),\n        (12, vals::Presc::DIV12),\n        (16, vals::Presc::DIV16),\n        (32, vals::Presc::DIV32),\n        (64, vals::Presc::DIV64),\n        (128, vals::Presc::DIV128),\n        (256, vals::Presc::DIV256),\n    ];\n\n    let (mul, brr_min, brr_max) = match kind {\n        #[cfg(any(usart_v3, usart_v4))]\n        Kind::Lpuart => {\n            trace!(\"USART: Kind::Lpuart\");\n            (256, 0x300, 0x10_0000)\n        }\n        Kind::Uart => {\n            trace!(\"USART: Kind::Uart\");\n            (1, 0x10, 0x1_0000)\n        }\n    };\n\n    let mut found_brr = None;\n    #[cfg(not(usart_v1))]\n    let mut over8 = false;\n    #[cfg(usart_v1)]\n    let over8 = false;\n\n    for &(presc, _presc_val) in &DIVS {\n        let brr = calculate_brr(baudrate, kernel_clock.0, presc as u32, mul);\n        trace!(\n            \"USART: presc={}, div=0x{:08x} (mantissa = {}, fraction = {})\",\n            presc,\n            brr,\n            brr >> 4,\n            brr & 0x0F\n        );\n\n        if brr < brr_min {\n            #[cfg(not(usart_v1))]\n            if brr * 2 >= brr_min && kind == Kind::Uart && !cfg!(usart_v1) {\n                over8 = true;\n                r.brr().write_value(regs::Brr(((brr << 1) & !0xF) | (brr & 0x07)));\n                #[cfg(usart_v4)]\n                r.presc().write(|w| w.set_prescaler(_presc_val));\n                found_brr = Some(brr);\n                break;\n            }\n            return Err(ConfigError::BaudrateTooHigh);\n        }\n\n        if brr < brr_max {\n            r.brr().write_value(regs::Brr(brr));\n            #[cfg(usart_v4)]\n            r.presc().write(|w| w.set_prescaler(_presc_val));\n            found_brr = Some(brr);\n            break;\n        }\n    }\n\n    match found_brr {\n        Some(brr) => {\n            #[cfg(not(usart_v1))]\n            let oversampling = if over8 { \"8 bit\" } else { \"16 bit\" };\n            #[cfg(usart_v1)]\n            let oversampling = \"default\";\n            trace!(\n                \"Using {} oversampling, desired baudrate: {}, actual baudrate: {}\",\n                oversampling,\n                baudrate,\n                kernel_clock.0 / brr * mul\n            );\n            Ok(over8)\n        }\n        None => Err(ConfigError::BaudrateTooLow),\n    }\n}\n\nfn set_usart_baudrate(info: &Info, kernel_clock: Hertz, baudrate: u32) -> Result<(), ConfigError> {\n    let r = info.regs;\n    r.cr1().modify(|w| {\n        // disable uart\n        w.set_ue(false);\n    });\n\n    #[cfg(not(usart_v1))]\n    let over8 = find_and_set_brr(r, info.kind, kernel_clock, baudrate)?;\n    #[cfg(usart_v1)]\n    let _over8 = find_and_set_brr(r, info.kind, kernel_clock, baudrate)?;\n\n    r.cr1().modify(|w| {\n        // enable uart\n        w.set_ue(true);\n\n        #[cfg(not(usart_v1))]\n        w.set_over8(vals::Over8::from_bits(over8 as _));\n    });\n\n    Ok(())\n}\n\nfn configure(\n    info: &Info,\n    kernel_clock: Hertz,\n    config: &Config,\n    enable_rx: bool,\n    enable_tx: bool,\n) -> Result<(), ConfigError> {\n    let r = info.regs;\n    let kind = info.kind;\n\n    if !enable_rx && !enable_tx {\n        return Err(ConfigError::RxOrTxNotEnabled);\n    }\n\n    #[cfg(not(any(usart_v1, usart_v2)))]\n    let dem = r.cr3().read().dem();\n\n    #[cfg(not(any(usart_v1, usart_v2)))]\n    if config.de_assertion_time > 31 {\n        return Err(ConfigError::DeAssertionTimeTooHigh);\n    } else if config.de_deassertion_time > 31 {\n        return Err(ConfigError::DeDeassertionTimeTooHigh);\n    }\n\n    // UART must be disabled during configuration.\n    r.cr1().modify(|w| {\n        w.set_ue(false);\n    });\n\n    #[cfg(not(usart_v1))]\n    let over8 = find_and_set_brr(r, kind, kernel_clock, config.baudrate)?;\n    #[cfg(usart_v1)]\n    let _over8 = find_and_set_brr(r, kind, kernel_clock, config.baudrate)?;\n\n    r.cr2().write(|w| {\n        w.set_stop(match config.stop_bits {\n            StopBits::STOP0P5 => vals::Stop::STOP0P5,\n            StopBits::STOP1 => vals::Stop::STOP1,\n            StopBits::STOP1P5 => vals::Stop::STOP1P5,\n            StopBits::STOP2 => vals::Stop::STOP2,\n        });\n\n        #[cfg(any(usart_v3, usart_v4))]\n        {\n            w.set_txinv(config.invert_tx);\n            w.set_rxinv(config.invert_rx);\n            w.set_swap(config.swap_rx_tx);\n        }\n    });\n\n    r.cr3().modify(|w| {\n        #[cfg(not(usart_v1))]\n        w.set_onebit(config.assume_noise_free);\n        w.set_hdsel(config.duplex.is_half());\n    });\n\n    r.cr1().write(|w| {\n        // enable uart\n        w.set_ue(true);\n\n        if config.duplex.is_half() {\n            // The te and re bits will be set by write, read and flush methods.\n            // Receiver should be enabled by default for Half-Duplex.\n            w.set_te(false);\n            w.set_re(true);\n        } else {\n            // enable transceiver\n            w.set_te(enable_tx);\n            // enable receiver\n            w.set_re(enable_rx);\n        }\n\n        #[cfg(not(any(usart_v1, usart_v2)))]\n        if dem {\n            w.set_deat(if over8 {\n                config.de_assertion_time / 2\n            } else {\n                config.de_assertion_time\n            });\n            w.set_dedt(if over8 {\n                config.de_assertion_time / 2\n            } else {\n                config.de_assertion_time\n            });\n        }\n\n        // configure word size and parity, since the parity bit is inserted into the MSB position,\n        // it increases the effective word size\n        match (config.parity, config.data_bits) {\n            (Parity::ParityNone, DataBits::DataBits8) => {\n                trace!(\"USART: m0: 8 data bits, no parity\");\n                w.set_m0(vals::M0::BIT8);\n                #[cfg(any(usart_v3, usart_v4))]\n                w.set_m1(vals::M1::M0);\n                w.set_pce(false);\n            }\n            (Parity::ParityNone, DataBits::DataBits9) => {\n                trace!(\"USART: m0: 9 data bits, no parity\");\n                w.set_m0(vals::M0::BIT9);\n                #[cfg(any(usart_v3, usart_v4))]\n                w.set_m1(vals::M1::M0);\n                w.set_pce(false);\n            }\n            #[cfg(any(usart_v3, usart_v4))]\n            (Parity::ParityNone, DataBits::DataBits7) => {\n                trace!(\"USART: m0: 7 data bits, no parity\");\n                w.set_m0(vals::M0::BIT8);\n                w.set_m1(vals::M1::BIT7);\n                w.set_pce(false);\n            }\n            (Parity::ParityEven, DataBits::DataBits8) => {\n                trace!(\"USART: m0: 8 data bits, even parity\");\n                w.set_m0(vals::M0::BIT9);\n                #[cfg(any(usart_v3, usart_v4))]\n                w.set_m1(vals::M1::M0);\n                w.set_pce(true);\n                w.set_ps(vals::Ps::EVEN);\n            }\n            (Parity::ParityEven, DataBits::DataBits7) => {\n                trace!(\"USART: m0: 7 data bits, even parity\");\n                w.set_m0(vals::M0::BIT8);\n                #[cfg(any(usart_v3, usart_v4))]\n                w.set_m1(vals::M1::M0);\n                w.set_pce(true);\n                w.set_ps(vals::Ps::EVEN);\n            }\n            (Parity::ParityOdd, DataBits::DataBits8) => {\n                trace!(\"USART: m0: 8 data bits, odd parity\");\n                w.set_m0(vals::M0::BIT9);\n                #[cfg(any(usart_v3, usart_v4))]\n                w.set_m1(vals::M1::M0);\n                w.set_pce(true);\n                w.set_ps(vals::Ps::ODD);\n            }\n            (Parity::ParityOdd, DataBits::DataBits7) => {\n                trace!(\"USART: m0: 7 data bits, odd parity\");\n                w.set_m0(vals::M0::BIT8);\n                #[cfg(any(usart_v3, usart_v4))]\n                w.set_m1(vals::M1::M0);\n                w.set_pce(true);\n                w.set_ps(vals::Ps::ODD);\n            }\n            _ => {\n                return Err(ConfigError::DataParityNotSupported);\n            }\n        }\n        #[cfg(not(usart_v1))]\n        w.set_over8(vals::Over8::from_bits(over8 as _));\n        #[cfg(usart_v4)]\n        {\n            trace!(\"USART: set_fifoen: true (usart_v4)\");\n            w.set_fifoen(true);\n        }\n\n        Ok(())\n    })?;\n\n    Ok(())\n}\n\nimpl<'d, M: Mode> embedded_hal_02::serial::Read<u8> for UartRx<'d, M> {\n    type Error = Error;\n    fn read(&mut self) -> Result<u8, nb::Error<Self::Error>> {\n        self.nb_read()\n    }\n}\n\nimpl<'d, M: Mode> embedded_hal_02::blocking::serial::Write<u8> for UartTx<'d, M> {\n    type Error = Error;\n    fn bwrite_all(&mut self, buffer: &[u8]) -> Result<(), Self::Error> {\n        self.blocking_write(buffer)\n    }\n    fn bflush(&mut self) -> Result<(), Self::Error> {\n        self.blocking_flush()\n    }\n}\n\nimpl<'d, M: Mode> embedded_hal_02::serial::Read<u8> for Uart<'d, M> {\n    type Error = Error;\n    fn read(&mut self) -> Result<u8, nb::Error<Self::Error>> {\n        self.nb_read()\n    }\n}\n\nimpl<'d, M: Mode> embedded_hal_02::blocking::serial::Write<u8> for Uart<'d, M> {\n    type Error = Error;\n    fn bwrite_all(&mut self, buffer: &[u8]) -> Result<(), Self::Error> {\n        self.blocking_write(buffer)\n    }\n    fn bflush(&mut self) -> Result<(), Self::Error> {\n        self.blocking_flush()\n    }\n}\n\nimpl embedded_hal_nb::serial::Error for Error {\n    fn kind(&self) -> embedded_hal_nb::serial::ErrorKind {\n        match *self {\n            Self::Framing => embedded_hal_nb::serial::ErrorKind::FrameFormat,\n            Self::Noise => embedded_hal_nb::serial::ErrorKind::Noise,\n            Self::Overrun => embedded_hal_nb::serial::ErrorKind::Overrun,\n            Self::Parity => embedded_hal_nb::serial::ErrorKind::Parity,\n            Self::BufferTooLong => embedded_hal_nb::serial::ErrorKind::Other,\n        }\n    }\n}\n\nimpl<'d, M: Mode> embedded_hal_nb::serial::ErrorType for Uart<'d, M> {\n    type Error = Error;\n}\n\nimpl<'d, M: Mode> embedded_hal_nb::serial::ErrorType for UartTx<'d, M> {\n    type Error = Error;\n}\n\nimpl<'d, M: Mode> embedded_hal_nb::serial::ErrorType for UartRx<'d, M> {\n    type Error = Error;\n}\n\nimpl<'d, M: Mode> embedded_hal_nb::serial::Read for UartRx<'d, M> {\n    fn read(&mut self) -> nb::Result<u8, Self::Error> {\n        self.nb_read()\n    }\n}\n\nimpl<'d, M: Mode> embedded_hal_nb::serial::Write for UartTx<'d, M> {\n    fn write(&mut self, char: u8) -> nb::Result<(), Self::Error> {\n        self.nb_write(char)\n    }\n\n    fn flush(&mut self) -> nb::Result<(), Self::Error> {\n        self.blocking_flush().map_err(nb::Error::Other)\n    }\n}\n\nimpl<'d, M: Mode> embedded_hal_nb::serial::Read for Uart<'d, M> {\n    fn read(&mut self) -> Result<u8, nb::Error<Self::Error>> {\n        self.nb_read()\n    }\n}\n\nimpl<'d, M: Mode> embedded_hal_nb::serial::Write for Uart<'d, M> {\n    fn write(&mut self, char: u8) -> nb::Result<(), Self::Error> {\n        self.blocking_write(&[char]).map_err(nb::Error::Other)\n    }\n\n    fn flush(&mut self) -> nb::Result<(), Self::Error> {\n        self.blocking_flush().map_err(nb::Error::Other)\n    }\n}\n\nimpl embedded_io::Error for Error {\n    fn kind(&self) -> embedded_io::ErrorKind {\n        embedded_io::ErrorKind::Other\n    }\n}\n\nimpl<M: Mode> embedded_io::ErrorType for Uart<'_, M> {\n    type Error = Error;\n}\n\nimpl<M: Mode> embedded_io::ErrorType for UartTx<'_, M> {\n    type Error = Error;\n}\n\nimpl<M: Mode> embedded_io::Write for Uart<'_, M> {\n    fn write(&mut self, buf: &[u8]) -> Result<usize, Self::Error> {\n        self.blocking_write(buf)?;\n        Ok(buf.len())\n    }\n\n    fn flush(&mut self) -> Result<(), Self::Error> {\n        self.blocking_flush()\n    }\n}\n\nimpl<M: Mode> embedded_io::Write for UartTx<'_, M> {\n    fn write(&mut self, buf: &[u8]) -> Result<usize, Self::Error> {\n        self.blocking_write(buf)?;\n        Ok(buf.len())\n    }\n\n    fn flush(&mut self) -> Result<(), Self::Error> {\n        self.blocking_flush()\n    }\n}\n\nimpl embedded_io_async::Write for Uart<'_, Async> {\n    async fn write(&mut self, buf: &[u8]) -> Result<usize, Self::Error> {\n        self.write(buf).await?;\n        Ok(buf.len())\n    }\n\n    async fn flush(&mut self) -> Result<(), Self::Error> {\n        self.flush().await\n    }\n}\n\nimpl embedded_io_async::Write for UartTx<'_, Async> {\n    async fn write(&mut self, buf: &[u8]) -> Result<usize, Self::Error> {\n        self.write(buf).await?;\n        Ok(buf.len())\n    }\n\n    async fn flush(&mut self) -> Result<(), Self::Error> {\n        self.flush().await\n    }\n}\n\npub use buffered::*;\n\npub use crate::usart::buffered::InterruptHandler as BufferedInterruptHandler;\nmod buffered;\n\nmod ringbuffered;\npub use ringbuffered::RingBufferedUartRx;\n\n#[cfg(any(usart_v1, usart_v2))]\nfn tdr(r: crate::pac::usart::Usart) -> *mut u8 {\n    r.dr().as_ptr() as _\n}\n\n#[cfg(any(usart_v1, usart_v2))]\nfn rdr(r: crate::pac::usart::Usart) -> *mut u8 {\n    r.dr().as_ptr() as _\n}\n\n#[cfg(any(usart_v1, usart_v2))]\nfn sr(r: crate::pac::usart::Usart) -> crate::pac::common::Reg<regs::Sr, crate::pac::common::RW> {\n    r.sr()\n}\n\n#[cfg(any(usart_v1, usart_v2))]\n#[allow(unused)]\nfn clear_interrupt_flags(_r: Regs, _sr: regs::Sr) {\n    // On v1 the flags are cleared implicitly by reads and writes to DR.\n}\n\n#[cfg(any(usart_v3, usart_v4))]\nfn tdr(r: Regs) -> *mut u8 {\n    r.tdr().as_ptr() as _\n}\n\n#[cfg(any(usart_v3, usart_v4))]\nfn rdr(r: Regs) -> *mut u8 {\n    r.rdr().as_ptr() as _\n}\n\n#[cfg(any(usart_v3, usart_v4))]\nfn sr(r: Regs) -> crate::pac::common::Reg<regs::Isr, crate::pac::common::R> {\n    r.isr()\n}\n\n#[cfg(any(usart_v3, usart_v4))]\n#[allow(unused)]\nfn clear_interrupt_flags(r: Regs, sr: regs::Isr) {\n    r.icr().write(|w| *w = regs::Icr(sr.0));\n}\n\n#[derive(Clone, Copy, PartialEq, Eq)]\nenum Kind {\n    Uart,\n    #[cfg(any(usart_v3, usart_v4))]\n    #[allow(unused)]\n    Lpuart,\n}\n\nstruct State {\n    rx_waker: AtomicWaker,\n    tx_waker: AtomicWaker,\n    tx_rx_refcount: AtomicU8,\n    eager_reads: AtomicUsize,\n}\n\nimpl State {\n    const fn new() -> Self {\n        Self {\n            rx_waker: AtomicWaker::new(),\n            tx_waker: AtomicWaker::new(),\n            tx_rx_refcount: AtomicU8::new(0),\n            eager_reads: AtomicUsize::new(0),\n        }\n    }\n}\n\nstruct Info {\n    regs: Regs,\n    rcc: RccInfo,\n    interrupt: Interrupt,\n    kind: Kind,\n}\n\n#[allow(private_interfaces)]\npub(crate) trait SealedInstance: crate::rcc::RccPeripheral {\n    fn info() -> &'static Info;\n    fn state() -> &'static State;\n    fn buffered_state() -> &'static buffered::State;\n}\n\n/// USART peripheral instance trait.\n#[allow(private_bounds)]\npub trait Instance: SealedInstance + PeripheralType + 'static + Send {\n    /// Interrupt for this peripheral.\n    type Interrupt: interrupt::typelevel::Interrupt;\n}\n\npin_trait!(RxPin, Instance, @A);\npin_trait!(TxPin, Instance, @A);\npin_trait!(CtsPin, Instance, @A);\npin_trait!(RtsPin, Instance, @A);\npin_trait!(CkPin, Instance, @A);\npin_trait!(DePin, Instance, @A);\n\ndma_trait!(TxDma, Instance);\ndma_trait!(RxDma, Instance);\n\nmacro_rules! impl_usart {\n    ($inst:ident, $irq:ident, $kind:expr) => {\n        #[allow(private_interfaces)]\n        impl SealedInstance for crate::peripherals::$inst {\n            fn info() -> &'static Info {\n                static INFO: Info = Info {\n                    regs: unsafe { Regs::from_ptr(crate::pac::$inst.as_ptr()) },\n                    rcc: crate::peripherals::$inst::RCC_INFO,\n                    interrupt: crate::interrupt::typelevel::$irq::IRQ,\n                    kind: $kind,\n                };\n                &INFO\n            }\n\n            fn state() -> &'static State {\n                static STATE: State = State::new();\n                &STATE\n            }\n\n            fn buffered_state() -> &'static buffered::State {\n                static BUFFERED_STATE: buffered::State = buffered::State::new();\n                &BUFFERED_STATE\n            }\n        }\n\n        impl Instance for crate::peripherals::$inst {\n            type Interrupt = crate::interrupt::typelevel::$irq;\n        }\n    };\n}\n\nforeach_interrupt!(\n    ($inst:ident, usart, LPUART, $signal_name:ident, $irq:ident) => {\n        impl_usart!($inst, $irq, Kind::Lpuart);\n    };\n    ($inst:ident, usart, $block:ident, $signal_name:ident, $irq:ident) => {\n        impl_usart!($inst, $irq, Kind::Uart);\n    };\n);\n"
  },
  {
    "path": "embassy-stm32/src/usart/ringbuffered.rs",
    "content": "use core::future::poll_fn;\nuse core::mem;\nuse core::sync::atomic::{Ordering, compiler_fence};\nuse core::task::Poll;\n\nuse embassy_embedded_hal::SetConfig;\nuse embedded_io_async::ReadReady;\nuse futures_util::future::{Either, select};\n\nuse super::{\n    Config, ConfigError, Error, Info, State, UartRx, clear_interrupt_flags, rdr, reconfigure, set_baudrate, sr,\n};\nuse crate::dma::ReadableRingBuffer;\nuse crate::gpio::Flex;\nuse crate::mode::Async;\nuse crate::time::Hertz;\nuse crate::usart::Regs;\n\n/// Rx-only Ring-buffered UART Driver\n///\n/// Created with [UartRx::into_ring_buffered]\n///\n/// ### Notes on 'waiting for bytes'\n///\n/// The `read(buf)` (but not `read()`) and `read_exact(buf)` functions\n/// may need to wait for bytes to arrive, if the ring buffer does not\n/// contain enough bytes to fill the buffer passed by the caller of\n/// the function, or is empty.\n///\n/// Waiting for bytes operates in one of three modes, depending on\n/// the behavior of the sender, the size of the buffer passed\n/// to the function, and the configuration:\n///\n/// - If the sender sends intermittently, the 'idle line'\n/// condition will be detected when the sender stops, and any\n/// bytes in the ring buffer will be returned. If there are no\n/// bytes in the buffer, the check will be repeated each time the\n/// 'idle line' condition is detected, so if the sender sends just\n/// a single byte, it will be returned once the 'idle line'\n/// condition is detected.\n///\n/// - If the sender sends continuously, the call will wait until\n/// the DMA controller indicates that it has written to either the\n/// middle byte or last byte of the ring buffer ('half transfer'\n/// or 'transfer complete', respectively). This does not indicate\n/// the buffer is half-full or full, though, because the DMA\n/// controller does not detect those conditions; it sends an\n/// interrupt when those specific buffer addresses have been\n/// written.\n///\n/// - If `eager_reads` is enabled in `config`, the UART interrupt\n/// is enabled on all data reception and the call will only wait\n/// for at least one byte to be available before returning.\n///\n/// In the first two cases this will result in variable latency due to the\n/// buffering effect. For example, if the baudrate is 2400 bps, and\n/// the configuration is 8 data bits, no parity bit, and one stop bit,\n/// then a byte will be received every ~4.16ms. If the ring buffer is\n/// 32 bytes, then a 'wait for bytes' delay may have to wait for 16\n/// bytes in the worst case, resulting in a delay (latency) of\n/// ~62.46ms for the first byte in the ring buffer. If the sender\n/// sends only 6 bytes and then stops, but the buffer was empty when\n/// the read function was called, then those bytes may not be returned\n/// until ~24.96ms after the first byte was received (time for 5\n/// additional bytes plus the 'idle frame' which triggers the 'idle\n/// line' condition).\n///\n/// Applications subject to this latency must be careful if they\n/// also apply timeouts during reception, as it may appear (to\n/// them) that the sender has stopped sending when it did not. In\n/// the example above, a 50ms timeout (12 bytes at 2400bps) might\n/// seem to be reasonable to detect that the sender has stopped\n/// sending, but would be falsely triggered in the worst-case\n/// buffer delay scenario.\n///\n/// Note: Enabling `eager_reads` with `RingBufferedUartRx` will enable\n/// an UART RXNE interrupt, which will cause an interrupt to occur on\n/// every received data byte. The data is still copied using DMA, but\n/// there is nevertheless additional processing overhead for each byte.\npub struct RingBufferedUartRx<'d> {\n    info: &'static Info,\n    state: &'static State,\n    kernel_clock: Hertz,\n    _rx: Option<Flex<'d>>,\n    _rts: Option<Flex<'d>>,\n    ring_buf: ReadableRingBuffer<'d, u8>,\n}\n\nimpl<'d> SetConfig for RingBufferedUartRx<'d> {\n    type Config = Config;\n    type ConfigError = ConfigError;\n\n    fn set_config(&mut self, config: &Self::Config) -> Result<(), Self::ConfigError> {\n        self.set_config(config)\n    }\n}\n\nimpl<'d> UartRx<'d, Async> {\n    /// Turn the `UartRx` into a buffered uart which can continously receive in the background\n    /// without the possibility of losing bytes. The `dma_buf` is a buffer registered to the\n    /// DMA controller, and must be large enough to prevent overflows.\n    pub fn into_ring_buffered(mut self, dma_buf: &'d mut [u8]) -> RingBufferedUartRx<'d> {\n        assert!(!dma_buf.is_empty() && dma_buf.len() <= 0xFFFF);\n\n        let opts = Default::default();\n\n        // Safety: we forget the struct before this function returns.\n        let rx_dma = self.rx_dma.as_mut().unwrap();\n        let request = rx_dma.request;\n        let rx_dma = unsafe { rx_dma.channel.clone_unchecked() };\n\n        let info = self.info;\n        let state = self.state;\n        let kernel_clock = self.kernel_clock;\n        let ring_buf = unsafe { ReadableRingBuffer::new(rx_dma, request, rdr(info.regs), dma_buf, opts) };\n        let rx = unsafe { self.rx.as_ref().map(|x| x.clone_unchecked()) };\n        let rts = unsafe { self.rts.as_ref().map(|x| x.clone_unchecked()) };\n\n        info.rcc.increment_stop_refcount();\n\n        // Don't disable the clock\n        mem::forget(self);\n\n        RingBufferedUartRx {\n            info,\n            state,\n            kernel_clock,\n            _rx: rx,\n            _rts: rts,\n            ring_buf,\n        }\n    }\n}\n\nimpl<'d> RingBufferedUartRx<'d> {\n    /// Reconfigure the driver\n    pub fn set_config(&mut self, config: &Config) -> Result<(), ConfigError> {\n        self.state\n            .eager_reads\n            .store(config.eager_reads.unwrap_or(0), Ordering::Relaxed);\n        reconfigure(self.info, self.kernel_clock, config)\n    }\n\n    /// Configure and start the DMA backed UART receiver\n    ///\n    /// Note: This is also done automatically by the read functions if\n    /// required.\n    pub fn start_uart(&mut self) {\n        // Clear the buffer so that it is ready to receive data\n        compiler_fence(Ordering::SeqCst);\n        self.ring_buf.start();\n\n        let r = self.info.regs;\n        // clear all interrupts and DMA Rx Request\n        r.cr1().modify(|w| {\n            // use RXNE only when returning reads early\n            w.set_rxneie(self.state.eager_reads.load(Ordering::Relaxed) > 0);\n            // enable parity interrupt if not ParityNone\n            w.set_peie(w.pce());\n            // enable idle line interrupt\n            w.set_idleie(true);\n        });\n        r.cr3().modify(|w| {\n            // enable Error Interrupt: (Frame error, Noise error, Overrun error)\n            w.set_eie(true);\n            // enable DMA Rx Request\n            w.set_dmar(true);\n        });\n    }\n\n    /// Stop DMA backed UART receiver\n    fn stop_uart(&mut self) {\n        self.ring_buf.request_pause();\n\n        let r = self.info.regs;\n        // clear all interrupts and DMA Rx Request\n        r.cr1().modify(|w| {\n            // disable RXNE interrupt\n            w.set_rxneie(false);\n            // disable parity interrupt\n            w.set_peie(false);\n            // disable idle line interrupt\n            w.set_idleie(false);\n        });\n        r.cr3().modify(|w| {\n            // disable Error Interrupt: (Frame error, Noise error, Overrun error)\n            w.set_eie(false);\n            // disable DMA Rx Request\n            w.set_dmar(false);\n        });\n\n        compiler_fence(Ordering::SeqCst);\n    }\n\n    /// (Re-)start DMA and Uart if it is not running (has not been started yet or has failed), and\n    /// check for errors in status register. Error flags are checked/cleared first.\n    fn start_dma_or_check_errors(&mut self) -> Result<(), Error> {\n        let r = self.info.regs;\n\n        check_idle_and_errors(r)?;\n        if !r.cr3().read().dmar() {\n            self.start_uart();\n        }\n        Ok(())\n    }\n\n    /// Read bytes that are available in the ring buffer, or wait for\n    /// bytes to become available and return them.\n    ///\n    /// Background reception is started if necessary (if `start_uart()` had\n    /// not previously been called, or if an error was detected which\n    /// caused background reception to be stopped).\n    ///\n    /// Background reception is terminated when an error is returned.\n    /// It must be started again by calling `start_uart()` or by\n    /// calling a read function again.\n    pub async fn read(&mut self, buf: &mut [u8]) -> Result<usize, Error> {\n        self.start_dma_or_check_errors()?;\n\n        // In half-duplex mode, we need to disable the Transmitter and enable the Receiver\n        // since they can't operate simultaneously on the shared line\n        let r = self.info.regs;\n        if r.cr3().read().hdsel() && r.cr1().read().te() {\n            r.cr1().modify(|reg| {\n                reg.set_re(true);\n                reg.set_te(false);\n            });\n        }\n\n        loop {\n            match self.ring_buf.read(buf) {\n                Ok((0, _)) => {}\n                Ok((len, _)) => {\n                    return Ok(len);\n                }\n                Err(_) => {\n                    self.stop_uart();\n                    return Err(Error::Overrun);\n                }\n            }\n\n            match self.wait_for_data_or_idle().await {\n                Ok(_) => {}\n                Err(err) => {\n                    self.stop_uart();\n                    return Err(err);\n                }\n            }\n        }\n    }\n\n    /// Wait for uart idle or dma half-full or full\n    async fn wait_for_data_or_idle(&mut self) -> Result<(), Error> {\n        compiler_fence(Ordering::SeqCst);\n\n        loop {\n            // Future which completes when idle line is detected\n            let s = self.state;\n            let mut uart_init = false;\n            let uart = poll_fn(|cx| {\n                s.rx_waker.register(cx.waker());\n\n                compiler_fence(Ordering::SeqCst);\n\n                // We may have been woken by IDLE or, if eager_reads is set, by RXNE.\n                // However, DMA will clear RXNE, so we can't check directly, and because\n                // the other future borrows `ring_buf`, we can't check `len()` here either.\n                // Instead, return from this future and we'll check the length afterwards.\n                let eager = s.eager_reads.load(Ordering::Relaxed) > 0;\n\n                let idle = check_idle_and_errors(self.info.regs)?;\n                if idle || (eager && uart_init) {\n                    // Idle line is detected, or eager reads is set and some data is available.\n                    Poll::Ready(Ok(idle))\n                } else {\n                    uart_init = true;\n                    Poll::Pending\n                }\n            });\n\n            let mut dma_init = false;\n            // Future which completes when the DMA controller indicates it\n            // has written to the ring buffer's middle byte, or last byte\n            let dma = poll_fn(|cx| {\n                self.ring_buf.set_waker(cx.waker());\n\n                let status = match dma_init {\n                    false => Poll::Pending,\n                    true => Poll::Ready(()),\n                };\n\n                dma_init = true;\n                status\n            });\n\n            match select(uart, dma).await {\n                // UART woke with line idle\n                Either::Left((Ok(true), _)) => {\n                    return Ok(());\n                }\n                // UART woke without idle or error: word received\n                Either::Left((Ok(false), _)) => {\n                    let eager = self.state.eager_reads.load(Ordering::Relaxed);\n                    if eager > 0 && self.ring_buf.len().unwrap_or(0) >= eager {\n                        return Ok(());\n                    } else {\n                        continue;\n                    }\n                }\n                // UART woke with error\n                Either::Left((Err(e), _)) => {\n                    return Err(e);\n                }\n                // DMA woke\n                Either::Right(((), _)) => return Ok(()),\n            }\n        }\n    }\n\n    /// Set baudrate\n    pub fn set_baudrate(&self, baudrate: u32) -> Result<(), ConfigError> {\n        set_baudrate(self.info, self.kernel_clock, baudrate)\n    }\n}\n\nimpl Drop for RingBufferedUartRx<'_> {\n    fn drop(&mut self) {\n        self.info.rcc.decrement_stop_refcount();\n        self.stop_uart();\n        super::drop_tx_rx(self.info, self.state);\n    }\n}\n\n/// Check and clear idle and error interrupts, return true if idle, Err(e) on error\n///\n/// All flags are read and cleared in a single step, respectively. When more than one flag is set\n/// at the same time, all flags will be cleared but only one flag will be reported. So the other\n/// flag(s) will gone missing unnoticed. The error flags are checked first, the idle flag last.\n///\n/// For usart_v1 and usart_v2, all status flags must be handled together anyway because all flags\n/// are cleared by a single read to the RDR register.\nfn check_idle_and_errors(r: Regs) -> Result<bool, Error> {\n    // SAFETY: read only and we only use Rx related flags\n    let sr = sr(r).read();\n\n    #[cfg(not(any(usart_v3, usart_v4)))]\n    unsafe {\n        // This read also clears the error and idle interrupt flags on v1 (TODO and v2?)\n        rdr(r).read_volatile()\n    };\n    clear_interrupt_flags(r, sr);\n\n    if sr.pe() {\n        Err(Error::Parity)\n    } else if sr.fe() {\n        Err(Error::Framing)\n    } else if sr.ne() {\n        Err(Error::Noise)\n    } else if sr.ore() {\n        Err(Error::Overrun)\n    } else {\n        r.cr1().modify(|w| w.set_idleie(true));\n        Ok(sr.idle())\n    }\n}\n\nimpl embedded_io_async::ErrorType for RingBufferedUartRx<'_> {\n    type Error = Error;\n}\n\nimpl embedded_io_async::Read for RingBufferedUartRx<'_> {\n    async fn read(&mut self, buf: &mut [u8]) -> Result<usize, Self::Error> {\n        self.read(buf).await\n    }\n}\n\nimpl embedded_hal_nb::serial::Read for RingBufferedUartRx<'_> {\n    fn read(&mut self) -> nb::Result<u8, Self::Error> {\n        self.start_dma_or_check_errors()?;\n\n        let mut buf = [0u8; 1];\n        match self.ring_buf.read(&mut buf) {\n            Ok((0, _)) => Err(nb::Error::WouldBlock),\n            Ok((len, _)) => {\n                assert!(len == 1);\n                Ok(buf[0])\n            }\n            Err(_) => {\n                self.stop_uart();\n                Err(nb::Error::Other(Error::Overrun))\n            }\n        }\n    }\n}\n\nimpl embedded_hal_nb::serial::ErrorType for RingBufferedUartRx<'_> {\n    type Error = Error;\n}\n\nimpl ReadReady for RingBufferedUartRx<'_> {\n    fn read_ready(&mut self) -> Result<bool, Self::Error> {\n        let len = self.ring_buf.len().map_err(|e| match e {\n            crate::dma::ringbuffer::Error::Overrun => Self::Error::Overrun,\n            crate::dma::ringbuffer::Error::DmaUnsynced => {\n                error!(\n                    \"Ringbuffer error: DmaUNsynced, driver implementation is\n                    probably bugged please open an issue\"\n                );\n                // we report this as overrun since its recoverable in the same way\n                Self::Error::Overrun\n            }\n        })?;\n        Ok(len > 0)\n    }\n}\n"
  },
  {
    "path": "embassy-stm32/src/usb/mod.rs",
    "content": "//! Universal Serial Bus (USB)\n\n#[cfg_attr(usb, path = \"usb.rs\")]\n#[cfg_attr(otg, path = \"otg.rs\")]\nmod _version;\npub use _version::*;\n\nuse crate::interrupt::typelevel::Interrupt;\nuse crate::rcc;\n\n/// clock, power initialization stuff that's common for USB and OTG.\nfn common_init<T: Instance>() {\n    // Check the USB clock is enabled and running at exactly 48 MHz.\n    // frequency() will panic if not enabled\n    let freq = T::frequency();\n\n    // On the H7RS, the USBPHYC embeds a PLL accepting one of the input frequencies listed below and providing 48MHz to OTG_FS and 60MHz to OTG_HS internally\n    #[cfg(any(stm32h7rs, all(stm32u5, peri_usb_otg_hs), all(stm32wba, peri_usb_otg_hs)))]\n    if ![16_000_000, 19_200_000, 20_000_000, 24_000_000, 26_000_000, 32_000_000].contains(&freq.0) {\n        panic!(\n            \"USB clock should be one of 16, 19.2, 20, 24, 26, 32Mhz but is {} Hz. Please double-check your RCC settings.\",\n            freq.0\n        )\n    }\n    // Check frequency is within the 0.25% tolerance allowed by the spec.\n    // Clock might not be exact 48Mhz due to rounding errors in PLL calculation, or if the user\n    // has tight clock restrictions due to something else (like audio).\n    #[cfg(not(any(stm32h7rs, all(stm32u5, peri_usb_otg_hs), all(stm32wba, peri_usb_otg_hs))))]\n    if freq.0.abs_diff(48_000_000) > 120_000 {\n        panic!(\n            \"USB clock should be 48Mhz but is {} Hz. Please double-check your RCC settings.\",\n            freq.0\n        )\n    }\n\n    #[cfg(any(stm32l4, stm32l5, stm32wb, stm32u0))]\n    critical_section::with(|_| crate::pac::PWR.cr2().modify(|w| w.set_usv(true)));\n\n    #[cfg(pwr_h5)]\n    critical_section::with(|_| crate::pac::PWR.usbscr().modify(|w| w.set_usb33sv(true)));\n\n    #[cfg(stm32h7)]\n    {\n        // If true, VDD33USB is generated by internal regulator from VDD50USB\n        // If false, VDD33USB and VDD50USB must be suplied directly with 3.3V (default on nucleo)\n        // TODO: unhardcode\n        let internal_regulator = false;\n\n        // Enable USB power\n        critical_section::with(|_| {\n            crate::pac::PWR.cr3().modify(|w| {\n                w.set_usb33den(true);\n                w.set_usbregen(internal_regulator);\n            })\n        });\n\n        // Wait for USB power to stabilize\n        while !crate::pac::PWR.cr3().read().usb33rdy() {}\n    }\n\n    #[cfg(stm32h7rs)]\n    {\n        // If true, VDD33USB is generated by internal regulator from VDD50USB\n        // If false, VDD33USB and VDD50USB must be suplied directly with 3.3V (default on nucleo)\n        // TODO: unhardcode\n        let internal_regulator = false;\n\n        // Enable USB power\n        critical_section::with(|_| {\n            crate::pac::PWR.csr2().modify(|w| {\n                w.set_usbregen(internal_regulator);\n                w.set_usb33den(true);\n                w.set_usbhsregen(true);\n            })\n        });\n\n        // Wait for USB power to stabilize\n        while !crate::pac::PWR.csr2().read().usb33rdy() {}\n    }\n\n    #[cfg(any(stm32u5, stm32u3))]\n    {\n        // Enable USB power\n        critical_section::with(|_| {\n            crate::pac::PWR.svmcr().modify(|w| {\n                w.set_usv(true);\n                w.set_uvmen(true);\n            })\n        });\n\n        // Wait for USB power to stabilize\n        while !crate::pac::PWR.svmsr().read().vddusbrdy() {}\n\n        // Now set up transceiver power if it's a OTG-HS\n        #[cfg(peri_usb_otg_hs)]\n        {\n            crate::pac::PWR.vosr().modify(|w| {\n                w.set_usbpwren(true);\n                w.set_usbboosten(true);\n            });\n            while !crate::pac::PWR.vosr().read().usbboostrdy() {}\n        }\n    }\n\n    #[cfg(stm32wba)]\n    {\n        // Enable USB power\n        critical_section::with(|_| {\n            crate::pac::PWR.svmcr().modify(|w| {\n                w.set_usv(crate::pac::pwr::vals::Usv::B_0X1);\n            });\n            crate::pac::PWR.vosr().modify(|w| {\n                w.set_vdd11usbdis(false);\n                w.set_usbpwren(true);\n            });\n        });\n\n        // Wait for USB power to stabilize\n        while !crate::pac::PWR.vosr().read().vdd11usbrdy() {}\n\n        // Now set up transceiver power if it's a OTG-HS\n        #[cfg(peri_usb_otg_hs)]\n        {\n            crate::pac::PWR.vosr().modify(|w| {\n                w.set_usbboosten(true);\n            });\n            while !crate::pac::PWR.vosr().read().usbboostrdy() {}\n        }\n    }\n\n    T::Interrupt::unpend();\n    unsafe { T::Interrupt::enable() };\n\n    rcc::enable_and_reset::<T>();\n}\n"
  },
  {
    "path": "embassy-stm32/src/usb/otg.rs",
    "content": "use core::marker::PhantomData;\n\nuse embassy_hal_internal::PeripheralType;\nuse embassy_usb_driver::{EndpointAddress, EndpointAllocError, EndpointType, Event, Unsupported};\npub use embassy_usb_synopsys_otg::Config;\nuse embassy_usb_synopsys_otg::otg_v1::Otg;\nuse embassy_usb_synopsys_otg::otg_v1::vals::Dspd;\nuse embassy_usb_synopsys_otg::{\n    Bus as OtgBus, ControlPipe, Driver as OtgDriver, Endpoint, In, OtgInstance, Out, PhyType, State,\n    on_interrupt as on_interrupt_impl,\n};\n\nuse crate::gpio::{AfType, OutputType, Speed};\nuse crate::interrupt::typelevel::Interrupt;\nuse crate::rcc::{self, RccPeripheral};\nuse crate::{Peri, interrupt};\n\nconst MAX_EP_COUNT: usize = 9;\n\n/// Interrupt handler.\npub struct InterruptHandler<T: Instance> {\n    _phantom: PhantomData<T>,\n}\n\nimpl<T: Instance> interrupt::typelevel::Handler<T::Interrupt> for InterruptHandler<T> {\n    unsafe fn on_interrupt() {\n        let r = T::regs();\n        let state = T::state();\n        on_interrupt_impl(r, state, T::ENDPOINT_COUNT);\n    }\n}\n\nmacro_rules! config_ulpi_pins {\n    ($($pin:ident),*) => {\n                critical_section::with(|_| {\n            $(\n                set_as_af!($pin, AfType::output(OutputType::PushPull, Speed::VeryHigh));\n            )*\n        })\n    };\n}\n\n// From `synopsys-usb-otg` crate:\n// This calculation doesn't correspond to one in a Reference Manual.\n// In fact, the required number of words is higher than indicated in RM.\n// The following numbers are pessimistic and were figured out empirically.\nconst RX_FIFO_EXTRA_SIZE_WORDS: u16 = 30;\n\n/// USB driver.\npub struct Driver<'d, T: Instance> {\n    phantom: PhantomData<&'d mut T>,\n    inner: OtgDriver<'d, MAX_EP_COUNT>,\n}\n\nimpl<'d, T: Instance> Driver<'d, T> {\n    /// Initializes USB OTG peripheral with internal Full-Speed PHY.\n    ///\n    /// # Arguments\n    ///\n    /// * `ep_out_buffer` - An internal buffer used to temporarily store received packets.\n    /// Must be large enough to fit all OUT endpoint max packet sizes.\n    /// Endpoint allocation will fail if it is too small.\n    pub fn new_fs(\n        _peri: Peri<'d, T>,\n        _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd,\n        dp: Peri<'d, impl DpPin<T>>,\n        dm: Peri<'d, impl DmPin<T>>,\n        ep_out_buffer: &'d mut [u8],\n        config: Config,\n    ) -> Self {\n        #[cfg(usb_alternate_function)]\n        {\n            set_as_af!(dp, AfType::output(OutputType::PushPull, Speed::VeryHigh));\n            set_as_af!(dm, AfType::output(OutputType::PushPull, Speed::VeryHigh));\n        }\n        #[cfg(not(usb_alternate_function))]\n        let _ = (dp, dm);\n\n        let regs = T::regs();\n\n        let instance = OtgInstance {\n            regs,\n            state: T::state(),\n            fifo_depth_words: T::FIFO_DEPTH_WORDS,\n            extra_rx_fifo_words: RX_FIFO_EXTRA_SIZE_WORDS,\n            endpoint_count: T::ENDPOINT_COUNT,\n            phy_type: PhyType::InternalFullSpeed,\n            calculate_trdt_fn: calculate_trdt::<T>,\n        };\n\n        Self {\n            inner: OtgDriver::new(ep_out_buffer, instance, config),\n            phantom: PhantomData,\n        }\n    }\n\n    /// Initializes USB OTG peripheral with internal High-Speed PHY.\n    ///\n    /// # Arguments\n    ///\n    /// * `ep_out_buffer` - An internal buffer used to temporarily store received packets.\n    /// Must be large enough to fit all OUT endpoint max packet sizes.\n    /// Endpoint allocation will fail if it is too small.\n    pub fn new_hs(\n        _peri: Peri<'d, T>,\n        _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd,\n        dp: Peri<'d, impl DpPin<T>>,\n        dm: Peri<'d, impl DmPin<T>>,\n        ep_out_buffer: &'d mut [u8],\n        config: Config,\n    ) -> Self {\n        #[cfg(usb_alternate_function)]\n        {\n            set_as_af!(dp, AfType::output(OutputType::PushPull, Speed::VeryHigh));\n            set_as_af!(dm, AfType::output(OutputType::PushPull, Speed::VeryHigh));\n        }\n        #[cfg(not(usb_alternate_function))]\n        let _ = (dp, dm);\n\n        let instance = OtgInstance {\n            regs: T::regs(),\n            state: T::state(),\n            fifo_depth_words: T::FIFO_DEPTH_WORDS,\n            extra_rx_fifo_words: RX_FIFO_EXTRA_SIZE_WORDS,\n            endpoint_count: T::ENDPOINT_COUNT,\n            phy_type: PhyType::InternalHighSpeed,\n            calculate_trdt_fn: calculate_trdt::<T>,\n        };\n\n        Self {\n            inner: OtgDriver::new(ep_out_buffer, instance, config),\n            phantom: PhantomData,\n        }\n    }\n\n    /// Initializes USB OTG peripheral with external Full-speed PHY (usually, a High-speed PHY in Full-speed mode).\n    ///\n    /// # Arguments\n    ///\n    /// * `ep_out_buffer` - An internal buffer used to temporarily store received packets.\n    /// Must be large enough to fit all OUT endpoint max packet sizes.\n    /// Endpoint allocation will fail if it is too small.\n    pub fn new_fs_ulpi(\n        _peri: Peri<'d, T>,\n        _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd,\n        ulpi_clk: Peri<'d, impl UlpiClkPin<T>>,\n        ulpi_dir: Peri<'d, impl UlpiDirPin<T>>,\n        ulpi_nxt: Peri<'d, impl UlpiNxtPin<T>>,\n        ulpi_stp: Peri<'d, impl UlpiStpPin<T>>,\n        ulpi_d0: Peri<'d, impl UlpiD0Pin<T>>,\n        ulpi_d1: Peri<'d, impl UlpiD1Pin<T>>,\n        ulpi_d2: Peri<'d, impl UlpiD2Pin<T>>,\n        ulpi_d3: Peri<'d, impl UlpiD3Pin<T>>,\n        ulpi_d4: Peri<'d, impl UlpiD4Pin<T>>,\n        ulpi_d5: Peri<'d, impl UlpiD5Pin<T>>,\n        ulpi_d6: Peri<'d, impl UlpiD6Pin<T>>,\n        ulpi_d7: Peri<'d, impl UlpiD7Pin<T>>,\n        ep_out_buffer: &'d mut [u8],\n        config: Config,\n    ) -> Self {\n        config_ulpi_pins!(\n            ulpi_clk, ulpi_dir, ulpi_nxt, ulpi_stp, ulpi_d0, ulpi_d1, ulpi_d2, ulpi_d3, ulpi_d4, ulpi_d5, ulpi_d6,\n            ulpi_d7\n        );\n\n        let instance = OtgInstance {\n            regs: T::regs(),\n            state: T::state(),\n            fifo_depth_words: T::FIFO_DEPTH_WORDS,\n            extra_rx_fifo_words: RX_FIFO_EXTRA_SIZE_WORDS,\n            endpoint_count: T::ENDPOINT_COUNT,\n            phy_type: PhyType::ExternalFullSpeed,\n            calculate_trdt_fn: calculate_trdt::<T>,\n        };\n\n        Self {\n            inner: OtgDriver::new(ep_out_buffer, instance, config),\n            phantom: PhantomData,\n        }\n    }\n\n    /// Initializes USB OTG peripheral with external High-Speed PHY.\n    ///\n    /// # Arguments\n    ///\n    /// * `ep_out_buffer` - An internal buffer used to temporarily store received packets.\n    /// Must be large enough to fit all OUT endpoint max packet sizes.\n    /// Endpoint allocation will fail if it is too small.\n    pub fn new_hs_ulpi(\n        _peri: Peri<'d, T>,\n        _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd,\n        ulpi_clk: Peri<'d, impl UlpiClkPin<T>>,\n        ulpi_dir: Peri<'d, impl UlpiDirPin<T>>,\n        ulpi_nxt: Peri<'d, impl UlpiNxtPin<T>>,\n        ulpi_stp: Peri<'d, impl UlpiStpPin<T>>,\n        ulpi_d0: Peri<'d, impl UlpiD0Pin<T>>,\n        ulpi_d1: Peri<'d, impl UlpiD1Pin<T>>,\n        ulpi_d2: Peri<'d, impl UlpiD2Pin<T>>,\n        ulpi_d3: Peri<'d, impl UlpiD3Pin<T>>,\n        ulpi_d4: Peri<'d, impl UlpiD4Pin<T>>,\n        ulpi_d5: Peri<'d, impl UlpiD5Pin<T>>,\n        ulpi_d6: Peri<'d, impl UlpiD6Pin<T>>,\n        ulpi_d7: Peri<'d, impl UlpiD7Pin<T>>,\n        ep_out_buffer: &'d mut [u8],\n        config: Config,\n    ) -> Self {\n        assert!(T::HIGH_SPEED == true, \"Peripheral is not capable of high-speed USB\");\n\n        config_ulpi_pins!(\n            ulpi_clk, ulpi_dir, ulpi_nxt, ulpi_stp, ulpi_d0, ulpi_d1, ulpi_d2, ulpi_d3, ulpi_d4, ulpi_d5, ulpi_d6,\n            ulpi_d7\n        );\n\n        let instance = OtgInstance {\n            regs: T::regs(),\n            state: T::state(),\n            fifo_depth_words: T::FIFO_DEPTH_WORDS,\n            extra_rx_fifo_words: RX_FIFO_EXTRA_SIZE_WORDS,\n            endpoint_count: T::ENDPOINT_COUNT,\n            phy_type: PhyType::ExternalHighSpeed,\n            calculate_trdt_fn: calculate_trdt::<T>,\n        };\n\n        Self {\n            inner: OtgDriver::new(ep_out_buffer, instance, config),\n            phantom: PhantomData,\n        }\n    }\n}\n\nimpl<'d, T: Instance> embassy_usb_driver::Driver<'d> for Driver<'d, T> {\n    type EndpointOut = Endpoint<'d, Out>;\n    type EndpointIn = Endpoint<'d, In>;\n    type ControlPipe = ControlPipe<'d>;\n    type Bus = Bus<'d, T>;\n\n    fn alloc_endpoint_in(\n        &mut self,\n        ep_type: EndpointType,\n        ep_addr: Option<EndpointAddress>,\n        max_packet_size: u16,\n        interval_ms: u8,\n    ) -> Result<Self::EndpointIn, EndpointAllocError> {\n        self.inner\n            .alloc_endpoint_in(ep_type, ep_addr, max_packet_size, interval_ms)\n    }\n\n    fn alloc_endpoint_out(\n        &mut self,\n        ep_type: EndpointType,\n        ep_addr: Option<EndpointAddress>,\n        max_packet_size: u16,\n        interval_ms: u8,\n    ) -> Result<Self::EndpointOut, EndpointAllocError> {\n        self.inner\n            .alloc_endpoint_out(ep_type, ep_addr, max_packet_size, interval_ms)\n    }\n\n    fn start(self, control_max_packet_size: u16) -> (Self::Bus, Self::ControlPipe) {\n        let (bus, cp) = self.inner.start(control_max_packet_size);\n\n        (\n            Bus {\n                phantom: PhantomData,\n                inner: bus,\n                inited: false,\n            },\n            cp,\n        )\n    }\n}\n\n/// USB bus.\npub struct Bus<'d, T: Instance> {\n    phantom: PhantomData<&'d mut T>,\n    inner: OtgBus<'d, MAX_EP_COUNT>,\n    inited: bool,\n}\n\nimpl<'d, T: Instance> Bus<'d, T> {\n    fn init(&mut self) {\n        super::common_init::<T>();\n\n        // Enable ULPI clock if external PHY is used\n        let phy_type = self.inner.phy_type();\n        let _ulpien = !phy_type.internal();\n\n        #[cfg(any(stm32f2, stm32f4, stm32f7))]\n        if T::HIGH_SPEED {\n            critical_section::with(|_| {\n                let rcc = crate::pac::RCC;\n                rcc.ahb1enr().modify(|w| w.set_usb_otg_hsulpien(_ulpien));\n                rcc.ahb1lpenr().modify(|w| w.set_usb_otg_hsulpilpen(_ulpien));\n            });\n        }\n\n        #[cfg(stm32h7)]\n        critical_section::with(|_| {\n            let rcc = crate::pac::RCC;\n            if T::HIGH_SPEED {\n                rcc.ahb1enr().modify(|w| w.set_usb_otg_hs_ulpien(_ulpien));\n                rcc.ahb1lpenr().modify(|w| w.set_usb_otg_hs_ulpilpen(_ulpien));\n            } else {\n                rcc.ahb1enr().modify(|w| w.set_usb_otg_fs_ulpien(_ulpien));\n                rcc.ahb1lpenr().modify(|w| w.set_usb_otg_fs_ulpilpen(_ulpien));\n            }\n        });\n\n        #[cfg(stm32h7rs)]\n        critical_section::with(|_| {\n            let rcc = crate::pac::RCC;\n            rcc.ahb1enr().modify(|w| {\n                w.set_usbphycen(true);\n                w.set_usb_otg_hsen(true);\n            });\n            rcc.ahb1lpenr().modify(|w| {\n                w.set_usbphyclpen(true);\n                w.set_usb_otg_hslpen(true);\n            });\n        });\n\n        #[cfg(all(stm32u5, peri_usb_otg_hs))]\n        {\n            critical_section::with(|_| {\n                crate::pac::RCC.ahb2enr1().modify(|w| {\n                    w.set_usb_otg_hsen(true);\n                    w.set_usb_otg_hs_phyen(true);\n                });\n            });\n\n            crate::pac::SYSCFG.otghsphycr().modify(|w| {\n                w.set_en(true);\n            });\n        }\n\n        #[cfg(all(stm32wba, peri_usb_otg_hs))]\n        {\n            critical_section::with(|_| {\n                crate::pac::RCC.ahb2enr().modify(|w| {\n                    w.set_usb_otg_hsen(true);\n                    w.set_usb_otg_hs_phyen(true);\n                });\n            });\n\n            crate::pac::SYSCFG.otghsphytuner2().modify(|w| {\n                w.set_compdistune(0b010);\n                w.set_sqrxtune(0b000);\n            });\n\n            crate::pac::SYSCFG.otghsphycr().modify(|w| {\n                w.set_en(true);\n            });\n        }\n\n        let r = T::regs();\n        let core_id = r.cid().read().0;\n        trace!(\"Core id {:08x}\", core_id);\n\n        // Wait for AHB ready.\n        while !r.grstctl().read().ahbidl() {}\n\n        // Configure as device.\n        self.inner.configure_as_device();\n\n        // Configuring Vbus sense and SOF output\n        match core_id {\n            0x0000_1200 | 0x0000_1100 | 0x0000_1000 => self.inner.config_v1(),\n            0x0000_2000 | 0x0000_2100 | 0x0000_2300 | 0x0000_3000 | 0x0000_3100 => self.inner.config_v2v3(),\n            0x0000_5000 | 0x0000_6100 => self.inner.config_v5(),\n            _ => unimplemented!(\"Unknown USB core id {:X}\", core_id),\n        }\n    }\n\n    fn disable(&mut self) {\n        T::Interrupt::disable();\n\n        rcc::disable::<T>();\n        self.inited = false;\n\n        #[cfg(stm32l4)]\n        crate::pac::PWR.cr2().modify(|w| w.set_usv(false));\n        // Cannot disable PWR, because other peripherals might be using it\n    }\n}\n\nimpl<'d, T: Instance> embassy_usb_driver::Bus for Bus<'d, T> {\n    async fn poll(&mut self) -> Event {\n        if !self.inited {\n            self.init();\n            self.inited = true;\n        }\n\n        self.inner.poll().await\n    }\n\n    fn endpoint_set_stalled(&mut self, ep_addr: EndpointAddress, stalled: bool) {\n        self.inner.endpoint_set_stalled(ep_addr, stalled)\n    }\n\n    fn endpoint_is_stalled(&mut self, ep_addr: EndpointAddress) -> bool {\n        self.inner.endpoint_is_stalled(ep_addr)\n    }\n\n    fn endpoint_set_enabled(&mut self, ep_addr: EndpointAddress, enabled: bool) {\n        self.inner.endpoint_set_enabled(ep_addr, enabled)\n    }\n\n    async fn enable(&mut self) {\n        self.inner.enable().await\n    }\n\n    async fn disable(&mut self) {\n        // NOTE: inner call is a no-op\n        self.inner.disable().await\n    }\n\n    async fn remote_wakeup(&mut self) -> Result<(), Unsupported> {\n        self.inner.remote_wakeup().await\n    }\n}\n\nimpl<'d, T: Instance> Drop for Bus<'d, T> {\n    fn drop(&mut self) {\n        Bus::disable(self);\n    }\n}\n\ntrait SealedInstance {\n    const HIGH_SPEED: bool;\n    const FIFO_DEPTH_WORDS: u16;\n    const ENDPOINT_COUNT: usize;\n\n    fn regs() -> Otg;\n    fn state() -> &'static State<{ MAX_EP_COUNT }>;\n}\n\n/// USB instance trait.\n#[allow(private_bounds)]\npub trait Instance: SealedInstance + PeripheralType + RccPeripheral + 'static {\n    /// Interrupt for this USB instance.\n    type Interrupt: interrupt::typelevel::Interrupt;\n}\n\n// Internal PHY pins\npin_trait!(DpPin, Instance);\npin_trait!(DmPin, Instance);\n\n// External PHY pins\npin_trait!(UlpiClkPin, Instance);\npin_trait!(UlpiDirPin, Instance);\npin_trait!(UlpiNxtPin, Instance);\npin_trait!(UlpiStpPin, Instance);\npin_trait!(UlpiD0Pin, Instance);\npin_trait!(UlpiD1Pin, Instance);\npin_trait!(UlpiD2Pin, Instance);\npin_trait!(UlpiD3Pin, Instance);\npin_trait!(UlpiD4Pin, Instance);\npin_trait!(UlpiD5Pin, Instance);\npin_trait!(UlpiD6Pin, Instance);\npin_trait!(UlpiD7Pin, Instance);\n\nforeach_interrupt!(\n    (USB_OTG_FS, otg, $block:ident, GLOBAL, $irq:ident) => {\n        impl SealedInstance for crate::peripherals::USB_OTG_FS {\n            const HIGH_SPEED: bool = false;\n\n            cfg_if::cfg_if! {\n                if #[cfg(stm32f1)] {\n                    const FIFO_DEPTH_WORDS: u16 = 128;\n                    const ENDPOINT_COUNT: usize = 8;\n                } else if #[cfg(any(\n                    stm32f2,\n                    stm32f401,\n                    stm32f405,\n                    stm32f407,\n                    stm32f411,\n                    stm32f415,\n                    stm32f417,\n                    stm32f427,\n                    stm32f429,\n                    stm32f437,\n                    stm32f439,\n                ))] {\n                    const FIFO_DEPTH_WORDS: u16 = 320;\n                    const ENDPOINT_COUNT: usize = 4;\n                } else if #[cfg(any(\n                    stm32f412,\n                    stm32f413,\n                    stm32f423,\n                    stm32f446,\n                    stm32f469,\n                    stm32f479,\n                    stm32f7,\n                    stm32l4,\n                    stm32u5,\n                    stm32wba,\n                ))] {\n                    const FIFO_DEPTH_WORDS: u16 = 320;\n                    const ENDPOINT_COUNT: usize = 6;\n                } else if #[cfg(stm32g0x1)] {\n                    const FIFO_DEPTH_WORDS: u16 = 512;\n                    const ENDPOINT_COUNT: usize = 8;\n                } else if #[cfg(any(stm32h7, stm32h7rs))] {\n                    const FIFO_DEPTH_WORDS: u16 = 1024;\n                    const ENDPOINT_COUNT: usize = 9;\n                } else if #[cfg(any(stm32wba, stm32u5))] {\n                    const FIFO_DEPTH_WORDS: u16 = 320;\n                    const ENDPOINT_COUNT: usize = 6;\n                } else {\n                    compile_error!(\"USB_OTG_FS peripheral is not supported by this chip.\");\n                }\n            }\n\n            fn regs() -> Otg {\n                unsafe { Otg::from_ptr(crate::pac::USB_OTG_FS.as_ptr()) }\n            }\n\n            fn state() -> &'static State<MAX_EP_COUNT> {\n                static STATE: State<MAX_EP_COUNT> = State::new();\n                &STATE\n            }\n        }\n\n        impl Instance for crate::peripherals::USB_OTG_FS {\n            type Interrupt = crate::interrupt::typelevel::$irq;\n        }\n    };\n\n    (USB_OTG_HS, otg, $block:ident, GLOBAL, $irq:ident) => {\n        impl SealedInstance for crate::peripherals::USB_OTG_HS {\n            const HIGH_SPEED: bool = true;\n\n            cfg_if::cfg_if! {\n                if #[cfg(any(\n                    stm32f2,\n                    stm32f405,\n                    stm32f407,\n                    stm32f415,\n                    stm32f417,\n                    stm32f427,\n                    stm32f429,\n                    stm32f437,\n                    stm32f439,\n                ))] {\n                    const FIFO_DEPTH_WORDS: u16 = 1024;\n                    const ENDPOINT_COUNT: usize = 6;\n                } else if #[cfg(any(\n                    stm32f446,\n                    stm32f469,\n                    stm32f479,\n                    stm32f7,\n                    stm32h7, stm32h7rs,\n                ))] {\n                    const FIFO_DEPTH_WORDS: u16 = 1024;\n                    const ENDPOINT_COUNT: usize = 9;\n                } else if #[cfg(any(stm32u5, stm32wba))] {\n                    const FIFO_DEPTH_WORDS: u16 = 1024;\n                    const ENDPOINT_COUNT: usize = 9;\n                } else {\n                    compile_error!(\"USB_OTG_HS peripheral is not supported by this chip.\");\n                }\n            }\n\n            fn regs() -> Otg {\n                // OTG HS registers are a superset of FS registers\n                unsafe { Otg::from_ptr(crate::pac::USB_OTG_HS.as_ptr()) }\n            }\n\n            fn state() -> &'static State<MAX_EP_COUNT> {\n                static STATE: State<MAX_EP_COUNT> = State::new();\n                &STATE\n            }\n        }\n\n        impl Instance for crate::peripherals::USB_OTG_HS {\n            type Interrupt = crate::interrupt::typelevel::$irq;\n        }\n    };\n);\n\nfn calculate_trdt<T: Instance>(speed: Dspd) -> u8 {\n    let ahb_freq = T::bus_frequency().0;\n    match speed {\n        Dspd::HIGH_SPEED => {\n            // From RM0431 (F72xx), RM0090 (F429), RM0390 (F446)\n            if ahb_freq >= 30_000_000 || cfg!(stm32h7rs) {\n                0x9\n            } else {\n                panic!(\"AHB frequency is too low\")\n            }\n        }\n        Dspd::FULL_SPEED_EXTERNAL | Dspd::FULL_SPEED_INTERNAL => {\n            // From RM0431 (F72xx), RM0090 (F429)\n            match ahb_freq {\n                0..=14_199_999 => panic!(\"AHB frequency is too low\"),\n                14_200_000..=14_999_999 => 0xF,\n                15_000_000..=15_999_999 => 0xE,\n                16_000_000..=17_199_999 => 0xD,\n                17_200_000..=18_499_999 => 0xC,\n                18_500_000..=19_999_999 => 0xB,\n                20_000_000..=21_799_999 => 0xA,\n                21_800_000..=23_999_999 => 0x9,\n                24_000_000..=27_499_999 => 0x8,\n                27_500_000..=31_999_999 => 0x7, // 27.7..32 in code from CubeIDE\n                32_000_000..=u32::MAX => 0x6,\n            }\n        }\n        _ => unimplemented!(),\n    }\n}\n"
  },
  {
    "path": "embassy-stm32/src/usb/usb.rs",
    "content": "#![macro_use]\n\nuse core::future::poll_fn;\nuse core::marker::PhantomData;\nuse core::sync::atomic::{AtomicBool, Ordering};\nuse core::task::Poll;\n\nuse embassy_hal_internal::PeripheralType;\nuse embassy_sync::waitqueue::AtomicWaker;\nuse embassy_usb_driver as driver;\nuse embassy_usb_driver::{\n    Direction, EndpointAddress, EndpointAllocError, EndpointError, EndpointInfo, EndpointType, Event, Unsupported,\n};\n\nuse crate::pac::USBRAM;\nuse crate::pac::usb::regs;\nuse crate::pac::usb::vals::{EpType, Stat};\nuse crate::rcc::RccPeripheral;\nuse crate::{Peri, interrupt};\n\n/// Interrupt handler.\npub struct InterruptHandler<T: Instance> {\n    _phantom: PhantomData<T>,\n}\n\nimpl<T: Instance> interrupt::typelevel::Handler<T::Interrupt> for InterruptHandler<T> {\n    unsafe fn on_interrupt() {\n        let regs = T::regs();\n        //let x = regs.istr().read().0;\n        //trace!(\"USB IRQ: {:08x}\", x);\n\n        let istr = regs.istr().read();\n\n        if istr.susp() {\n            //trace!(\"USB IRQ: susp\");\n            IRQ_SUSPEND.store(true, Ordering::Relaxed);\n            regs.cntr().modify(|w| {\n                w.set_fsusp(true);\n                w.set_lpmode(true);\n            });\n\n            // Write 0 to clear.\n            let mut clear = regs::Istr(!0);\n            clear.set_susp(false);\n            regs.istr().write_value(clear);\n\n            // Wake main thread.\n            BUS_WAKER.wake();\n        }\n\n        if istr.wkup() {\n            //trace!(\"USB IRQ: wkup\");\n            IRQ_RESUME.store(true, Ordering::Relaxed);\n            regs.cntr().modify(|w| {\n                w.set_fsusp(false);\n                w.set_lpmode(false);\n            });\n\n            // Write 0 to clear.\n            let mut clear = regs::Istr(!0);\n            clear.set_wkup(false);\n            regs.istr().write_value(clear);\n\n            // Wake main thread.\n            BUS_WAKER.wake();\n        }\n\n        if istr.reset() {\n            //trace!(\"USB IRQ: reset\");\n            IRQ_RESET.store(true, Ordering::Relaxed);\n\n            // Write 0 to clear.\n            let mut clear = regs::Istr(!0);\n            clear.set_reset(false);\n            regs.istr().write_value(clear);\n\n            // Wake main thread.\n            BUS_WAKER.wake();\n        }\n\n        if istr.ctr() {\n            let index = istr.ep_id() as usize;\n\n            let mut epr = regs.epr(index).read();\n            if epr.ctr_rx() {\n                RX_COMPLETE[index].store(true, Ordering::Relaxed);\n                if index == 0 && epr.setup() {\n                    EP0_SETUP.store(true, Ordering::Relaxed);\n                }\n                //trace!(\"EP {} RX, setup={}\", index, epr.setup());\n                EP_OUT_WAKERS[index].wake();\n            }\n            if epr.ctr_tx() {\n                TX_PENDING[index].store(false, Ordering::Relaxed);\n                //trace!(\"EP {} TX\", index);\n                EP_IN_WAKERS[index].wake();\n            }\n            epr.set_dtog_rx(false);\n            epr.set_dtog_tx(false);\n            epr.set_stat_rx(Stat::from_bits(0));\n            epr.set_stat_tx(Stat::from_bits(0));\n            epr.set_ctr_rx(!epr.ctr_rx());\n            epr.set_ctr_tx(!epr.ctr_tx());\n            regs.epr(index).write_value(epr);\n        }\n    }\n}\n\nconst EP_COUNT: usize = 8;\n\n#[cfg(any(usbram_16x1_512, usbram_16x2_512))]\nconst USBRAM_SIZE: usize = 512;\n#[cfg(any(usbram_16x2_1024, usbram_32_1024))]\nconst USBRAM_SIZE: usize = 1024;\n#[cfg(usbram_32_2048)]\nconst USBRAM_SIZE: usize = 2048;\n\n#[cfg(not(any(usbram_32_2048, usbram_32_1024)))]\nconst USBRAM_ALIGN: usize = 2;\n#[cfg(any(usbram_32_2048, usbram_32_1024))]\nconst USBRAM_ALIGN: usize = 4;\n\nstatic BUS_WAKER: AtomicWaker = AtomicWaker::new();\nstatic EP0_SETUP: AtomicBool = AtomicBool::new(false);\n\nstatic TX_PENDING: [AtomicBool; EP_COUNT] = [const { AtomicBool::new(false) }; EP_COUNT];\nstatic RX_COMPLETE: [AtomicBool; EP_COUNT] = [const { AtomicBool::new(false) }; EP_COUNT];\nstatic EP_IN_WAKERS: [AtomicWaker; EP_COUNT] = [const { AtomicWaker::new() }; EP_COUNT];\nstatic EP_OUT_WAKERS: [AtomicWaker; EP_COUNT] = [const { AtomicWaker::new() }; EP_COUNT];\nstatic IRQ_RESET: AtomicBool = AtomicBool::new(false);\nstatic IRQ_SUSPEND: AtomicBool = AtomicBool::new(false);\nstatic IRQ_RESUME: AtomicBool = AtomicBool::new(false);\n\nfn convert_type(t: EndpointType) -> EpType {\n    match t {\n        EndpointType::Bulk => EpType::BULK,\n        EndpointType::Control => EpType::CONTROL,\n        EndpointType::Interrupt => EpType::INTERRUPT,\n        EndpointType::Isochronous => EpType::ISO,\n    }\n}\n\nfn invariant(mut r: regs::Epr) -> regs::Epr {\n    r.set_ctr_rx(true); // don't clear\n    r.set_ctr_tx(true); // don't clear\n    r.set_dtog_rx(false); // don't toggle\n    r.set_dtog_tx(false); // don't toggle\n    r.set_stat_rx(Stat::from_bits(0));\n    r.set_stat_tx(Stat::from_bits(0));\n    r\n}\n\nfn align_len_up(len: u16) -> u16 {\n    ((len as usize + USBRAM_ALIGN - 1) / USBRAM_ALIGN * USBRAM_ALIGN) as u16\n}\n\n// Returns (actual_len, len_bits)\nfn calc_out_len(len: u16) -> (u16, u16) {\n    match len {\n        // NOTE: this could be 2..=62 with 16bit USBRAM, but not with 32bit. Limit it to 60 for simplicity.\n        2..=60 => (align_len_up(len), align_len_up(len) / 2 << 10),\n        61..=1024 => ((len + 31) / 32 * 32, (((len + 31) / 32 - 1) << 10) | 0x8000),\n        _ => panic!(\"invalid OUT length {}\", len),\n    }\n}\n\n#[cfg(not(any(usbram_32_2048, usbram_32_1024)))]\nmod btable {\n    use super::*;\n\n    pub(super) fn write_in_tx<T: Instance>(index: usize, addr: u16) {\n        USBRAM.mem(index * 4 + 0).write_value(addr);\n    }\n\n    pub(super) fn write_in_rx<T: Instance>(index: usize, addr: u16) {\n        USBRAM.mem(index * 4 + 2).write_value(addr);\n    }\n\n    pub(super) fn write_in_len_rx<T: Instance>(index: usize, _addr: u16, len: u16) {\n        USBRAM.mem(index * 4 + 3).write_value(len);\n    }\n\n    pub(super) fn write_in_len_tx<T: Instance>(index: usize, _addr: u16, len: u16) {\n        USBRAM.mem(index * 4 + 1).write_value(len);\n    }\n\n    pub(super) fn write_out_rx<T: Instance>(index: usize, addr: u16, max_len_bits: u16) {\n        USBRAM.mem(index * 4 + 2).write_value(addr);\n        USBRAM.mem(index * 4 + 3).write_value(max_len_bits);\n    }\n\n    pub(super) fn write_out_tx<T: Instance>(index: usize, addr: u16, max_len_bits: u16) {\n        USBRAM.mem(index * 4 + 0).write_value(addr);\n        USBRAM.mem(index * 4 + 1).write_value(max_len_bits);\n    }\n\n    pub(super) fn read_out_len_tx<T: Instance>(index: usize) -> u16 {\n        USBRAM.mem(index * 4 + 1).read()\n    }\n\n    pub(super) fn read_out_len_rx<T: Instance>(index: usize) -> u16 {\n        USBRAM.mem(index * 4 + 3).read()\n    }\n}\n#[cfg(any(usbram_32_2048, usbram_32_1024))]\nmod btable {\n    use super::*;\n\n    pub(super) fn write_in_len_tx<T: Instance>(index: usize, addr: u16, len: u16) {\n        assert_eq!(addr & 0b11, 0);\n        USBRAM.mem(index * 2).write_value((addr as u32) | ((len as u32) << 16));\n    }\n\n    pub(super) fn write_in_len_rx<T: Instance>(index: usize, addr: u16, len: u16) {\n        assert_eq!(addr & 0b11, 0);\n        USBRAM\n            .mem(index * 2 + 1)\n            .write_value((addr as u32) | ((len as u32) << 16));\n    }\n\n    pub(super) fn write_out_tx<T: Instance>(index: usize, addr: u16, max_len_bits: u16) {\n        USBRAM\n            .mem(index * 2)\n            .write_value((addr as u32) | ((max_len_bits as u32) << 16));\n    }\n\n    pub(super) fn write_out_rx<T: Instance>(index: usize, addr: u16, max_len_bits: u16) {\n        USBRAM\n            .mem(index * 2 + 1)\n            .write_value((addr as u32) | ((max_len_bits as u32) << 16));\n    }\n\n    pub(super) fn read_out_len_tx<T: Instance>(index: usize) -> u16 {\n        (USBRAM.mem(index * 2).read() >> 16) as u16\n    }\n\n    pub(super) fn read_out_len_rx<T: Instance>(index: usize) -> u16 {\n        (USBRAM.mem(index * 2 + 1).read() >> 16) as u16\n    }\n}\n\nstruct EndpointBuffer<T: Instance> {\n    addr: u16,\n    len: u16,\n    _phantom: PhantomData<T>,\n}\n\nimpl<T: Instance> EndpointBuffer<T> {\n    fn read(&mut self, buf: &mut [u8]) {\n        assert!(buf.len() <= self.len as usize);\n        for i in 0..(buf.len() + USBRAM_ALIGN - 1) / USBRAM_ALIGN {\n            let val = USBRAM.mem(self.addr as usize / USBRAM_ALIGN + i).read();\n            let n = USBRAM_ALIGN.min(buf.len() - i * USBRAM_ALIGN);\n            buf[i * USBRAM_ALIGN..][..n].copy_from_slice(&val.to_le_bytes()[..n]);\n        }\n    }\n\n    fn write(&mut self, buf: &[u8]) {\n        assert!(buf.len() <= self.len as usize);\n        for i in 0..(buf.len() + USBRAM_ALIGN - 1) / USBRAM_ALIGN {\n            let mut val = [0u8; USBRAM_ALIGN];\n            let n = USBRAM_ALIGN.min(buf.len() - i * USBRAM_ALIGN);\n            val[..n].copy_from_slice(&buf[i * USBRAM_ALIGN..][..n]);\n\n            #[cfg(not(any(usbram_32_2048, usbram_32_1024)))]\n            let val = u16::from_le_bytes(val);\n            #[cfg(any(usbram_32_2048, usbram_32_1024))]\n            let val = u32::from_le_bytes(val);\n            USBRAM.mem(self.addr as usize / USBRAM_ALIGN + i).write_value(val);\n        }\n    }\n}\n\n#[derive(Debug, Clone, Copy)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\nstruct EndpointData {\n    ep_type: EndpointType, // only valid if used_in || used_out\n    used_in: bool,\n    used_out: bool,\n}\n\n/// USB driver.\npub struct Driver<'d, T: Instance> {\n    phantom: PhantomData<&'d mut T>,\n    alloc: [EndpointData; EP_COUNT],\n    ep_mem_free: u16, // first free address in EP mem, in bytes.\n}\n\nimpl<'d, T: Instance> Driver<'d, T> {\n    /// Create a new USB driver with start-of-frame (SOF) output.\n    #[cfg(not(stm32l1))]\n    pub fn new_with_sof(\n        _usb: Peri<'d, T>,\n        _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd,\n        dp: Peri<'d, impl DpPin<T>>,\n        dm: Peri<'d, impl DmPin<T>>,\n        sof: Peri<'d, impl SofPin<T>>,\n    ) -> Self {\n        {\n            use crate::gpio::{AfType, OutputType, Speed};\n            set_as_af!(sof, AfType::output(OutputType::PushPull, Speed::VeryHigh));\n        }\n\n        Self::new(_usb, _irq, dp, dm)\n    }\n\n    /// Create a new USB driver.\n    pub fn new(\n        _usb: Peri<'d, T>,\n        _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd,\n        dp: Peri<'d, impl DpPin<T>>,\n        dm: Peri<'d, impl DmPin<T>>,\n    ) -> Self {\n        super::common_init::<T>();\n\n        let regs = T::regs();\n\n        regs.cntr().write(|w| {\n            w.set_pdwn(false);\n            w.set_fres(true);\n        });\n\n        // wait t_STARTUP = 1us\n        cortex_m::asm::delay(unsafe { crate::rcc::get_freqs() }.sys.to_hertz().unwrap().0 / 1_000_000);\n\n        #[cfg(not(usb_v4))]\n        regs.btable().write(|w| w.set_btable(0));\n\n        #[cfg(usb_alternate_function)]\n        {\n            use crate::gpio::{AfType, OutputType, Speed};\n            set_as_af!(dp, AfType::output(OutputType::PushPull, Speed::VeryHigh));\n            set_as_af!(dm, AfType::output(OutputType::PushPull, Speed::VeryHigh));\n        }\n        #[cfg(not(usb_alternate_function))]\n        let _ = (dp, dm); // suppress \"unused\" warnings.\n\n        // Initialize the bus so that it signals that power is available\n        BUS_WAKER.wake();\n\n        Self {\n            phantom: PhantomData,\n            alloc: [EndpointData {\n                ep_type: EndpointType::Bulk,\n                used_in: false,\n                used_out: false,\n            }; EP_COUNT],\n            ep_mem_free: EP_COUNT as u16 * 8, // for each EP, 4 regs, so 8 bytes\n        }\n    }\n\n    fn alloc_ep_mem(&mut self, len: u16) -> u16 {\n        assert!(len as usize % USBRAM_ALIGN == 0);\n        let addr = self.ep_mem_free;\n        if addr + len > USBRAM_SIZE as _ {\n            panic!(\"Endpoint memory full\");\n        }\n        self.ep_mem_free += len;\n        addr\n    }\n\n    fn is_endpoint_available<D: Dir>(&self, index: usize, ep_type: EndpointType) -> bool {\n        if index == 0 && ep_type != EndpointType::Control {\n            return false; // EP0 is reserved for control\n        }\n\n        let ep = match self.alloc.get(index) {\n            Some(ep) => ep,\n            None => return false,\n        };\n\n        let used = ep.used_out || ep.used_in;\n\n        if used && ep.ep_type == EndpointType::Isochronous {\n            // Isochronous endpoints are always double-buffered.\n            // Their corresponding endpoint/channel registers are forced to be unidirectional.\n            // Do not reuse this index.\n            // FIXME: Bulk endpoints can be double buffered, but are not in the current implementation.\n            return false;\n        }\n\n        let used_dir = match D::dir() {\n            Direction::Out => ep.used_out,\n            Direction::In => ep.used_in,\n        };\n\n        !used || (ep.ep_type == ep_type && !used_dir)\n    }\n\n    fn alloc_endpoint<D: Dir>(\n        &mut self,\n        ep_type: EndpointType,\n        ep_addr: Option<EndpointAddress>,\n        max_packet_size: u16,\n        interval_ms: u8,\n    ) -> Result<Endpoint<'d, T, D>, driver::EndpointAllocError> {\n        trace!(\n            \"allocating type={:?} mps={:?} interval_ms={}, dir={:?}\",\n            ep_type,\n            max_packet_size,\n            interval_ms,\n            D::dir()\n        );\n\n        let index = if let Some(addr) = ep_addr {\n            // Use the specified endpoint address\n            self.is_endpoint_available::<D>(addr.index(), ep_type)\n                .then_some(addr.index())\n        } else {\n            // Find any available endpoint\n            (0..self.alloc.len()).find(|&i| self.is_endpoint_available::<D>(i, ep_type))\n        };\n\n        let (index, ep) = match index {\n            Some(i) => (i, &mut self.alloc[i]),\n            None => return Err(EndpointAllocError),\n        };\n\n        ep.ep_type = ep_type;\n\n        let buf = match D::dir() {\n            Direction::Out => {\n                assert!(!ep.used_out);\n                ep.used_out = true;\n\n                let (len, len_bits) = calc_out_len(max_packet_size);\n                let addr = self.alloc_ep_mem(len);\n\n                trace!(\"  len_bits = {:04x}\", len_bits);\n                btable::write_out_rx::<T>(index, addr, len_bits);\n\n                if ep_type == EndpointType::Isochronous {\n                    btable::write_out_tx::<T>(index, addr, len_bits);\n                }\n\n                EndpointBuffer {\n                    addr,\n                    len,\n                    _phantom: PhantomData,\n                }\n            }\n            Direction::In => {\n                assert!(!ep.used_in);\n                ep.used_in = true;\n\n                let len = align_len_up(max_packet_size);\n                let addr = self.alloc_ep_mem(len);\n\n                #[cfg(not(any(usbram_32_2048, usbram_32_1024)))]\n                {\n                    // ep_in_len is written when actually transmitting packets.\n                    btable::write_in_tx::<T>(index, addr);\n\n                    if ep_type == EndpointType::Isochronous {\n                        btable::write_in_rx::<T>(index, addr);\n                    }\n                }\n\n                #[cfg(any(usbram_32_2048, usbram_32_1024))]\n                {\n                    btable::write_in_len_tx::<T>(index, addr, 0);\n\n                    if ep_type == EndpointType::Isochronous {\n                        btable::write_in_len_rx::<T>(index, addr, 0);\n                    }\n                }\n\n                EndpointBuffer {\n                    addr,\n                    len,\n                    _phantom: PhantomData,\n                }\n            }\n        };\n\n        trace!(\"  index={} addr={} len={}\", index, buf.addr, buf.len);\n\n        Ok(Endpoint {\n            _phantom: PhantomData,\n            info: EndpointInfo {\n                addr: EndpointAddress::from_parts(index, D::dir()),\n                ep_type,\n                max_packet_size,\n                interval_ms,\n            },\n            buf,\n        })\n    }\n}\n\nimpl<'d, T: Instance> driver::Driver<'d> for Driver<'d, T> {\n    type EndpointOut = Endpoint<'d, T, Out>;\n    type EndpointIn = Endpoint<'d, T, In>;\n    type ControlPipe = ControlPipe<'d, T>;\n    type Bus = Bus<'d, T>;\n\n    fn alloc_endpoint_in(\n        &mut self,\n        ep_type: EndpointType,\n        ep_addr: Option<EndpointAddress>,\n        max_packet_size: u16,\n        interval_ms: u8,\n    ) -> Result<Self::EndpointIn, driver::EndpointAllocError> {\n        self.alloc_endpoint(ep_type, ep_addr, max_packet_size, interval_ms)\n    }\n\n    fn alloc_endpoint_out(\n        &mut self,\n        ep_type: EndpointType,\n        ep_addr: Option<EndpointAddress>,\n        max_packet_size: u16,\n        interval_ms: u8,\n    ) -> Result<Self::EndpointOut, driver::EndpointAllocError> {\n        self.alloc_endpoint(ep_type, ep_addr, max_packet_size, interval_ms)\n    }\n\n    fn start(mut self, control_max_packet_size: u16) -> (Self::Bus, Self::ControlPipe) {\n        let ep_out = self\n            .alloc_endpoint(EndpointType::Control, None, control_max_packet_size, 0)\n            .unwrap();\n        let ep_in = self\n            .alloc_endpoint(EndpointType::Control, None, control_max_packet_size, 0)\n            .unwrap();\n        assert_eq!(ep_out.info.addr.index(), 0);\n        assert_eq!(ep_in.info.addr.index(), 0);\n\n        let regs = T::regs();\n\n        regs.cntr().write(|w| {\n            w.set_pdwn(false);\n            w.set_fres(false);\n            w.set_resetm(true);\n            w.set_suspm(true);\n            w.set_wkupm(true);\n            w.set_ctrm(true);\n        });\n\n        #[cfg(any(usb_v3, usb_v4))]\n        regs.bcdr().write(|w| w.set_dppu(true));\n\n        #[cfg(stm32l1)]\n        crate::pac::SYSCFG.pmc().modify(|w| w.set_usb_pu(true));\n\n        trace!(\"enabled\");\n\n        let mut ep_types = [EpType::BULK; EP_COUNT - 1];\n        for i in 1..EP_COUNT {\n            ep_types[i - 1] = convert_type(self.alloc[i].ep_type);\n        }\n\n        (\n            Bus {\n                phantom: PhantomData,\n                ep_types,\n                inited: false,\n            },\n            ControlPipe {\n                _phantom: PhantomData,\n                max_packet_size: control_max_packet_size,\n                ep_out,\n                ep_in,\n            },\n        )\n    }\n}\n\n/// USB bus.\npub struct Bus<'d, T: Instance> {\n    phantom: PhantomData<&'d mut T>,\n    ep_types: [EpType; EP_COUNT - 1],\n    inited: bool,\n}\n\nimpl<'d, T: Instance> driver::Bus for Bus<'d, T> {\n    async fn poll(&mut self) -> Event {\n        poll_fn(move |cx| {\n            BUS_WAKER.register(cx.waker());\n\n            // TODO: implement VBUS detection.\n            if !self.inited {\n                self.inited = true;\n                return Poll::Ready(Event::PowerDetected);\n            }\n\n            let regs = T::regs();\n\n            if IRQ_RESUME.load(Ordering::Acquire) {\n                IRQ_RESUME.store(false, Ordering::Relaxed);\n                return Poll::Ready(Event::Resume);\n            }\n\n            if IRQ_RESET.load(Ordering::Acquire) {\n                IRQ_RESET.store(false, Ordering::Relaxed);\n\n                trace!(\"RESET\");\n                regs.daddr().write(|w| {\n                    w.set_ef(true);\n                    w.set_add(0);\n                });\n\n                regs.epr(0).write(|w| {\n                    w.set_ep_type(EpType::CONTROL);\n                    w.set_stat_rx(Stat::NAK);\n                    w.set_stat_tx(Stat::NAK);\n                });\n\n                for i in 1..EP_COUNT {\n                    regs.epr(i).write(|w| {\n                        w.set_ea(i as _);\n                        w.set_ep_type(self.ep_types[i - 1]);\n                    })\n                }\n\n                for w in &EP_IN_WAKERS {\n                    w.wake()\n                }\n                for w in &EP_OUT_WAKERS {\n                    w.wake()\n                }\n\n                return Poll::Ready(Event::Reset);\n            }\n\n            if IRQ_SUSPEND.load(Ordering::Acquire) {\n                IRQ_SUSPEND.store(false, Ordering::Relaxed);\n                return Poll::Ready(Event::Suspend);\n            }\n\n            Poll::Pending\n        })\n        .await\n    }\n\n    fn endpoint_set_stalled(&mut self, ep_addr: EndpointAddress, stalled: bool) {\n        // This can race, so do a retry loop.\n        let reg = T::regs().epr(ep_addr.index() as _);\n        match ep_addr.direction() {\n            Direction::In => {\n                loop {\n                    let r = reg.read();\n                    match r.stat_tx() {\n                        Stat::DISABLED => break, // if disabled, stall does nothing.\n                        Stat::STALL => break,    // done!\n                        _ => {\n                            let want_stat = match stalled {\n                                false => Stat::NAK,\n                                true => Stat::STALL,\n                            };\n                            let mut w = invariant(r);\n                            w.set_stat_tx(Stat::from_bits(r.stat_tx().to_bits() ^ want_stat.to_bits()));\n                            reg.write_value(w);\n                        }\n                    }\n                }\n                EP_IN_WAKERS[ep_addr.index()].wake();\n            }\n            Direction::Out => {\n                loop {\n                    let r = reg.read();\n                    match r.stat_rx() {\n                        Stat::DISABLED => break, // if disabled, stall does nothing.\n                        Stat::STALL => break,    // done!\n                        _ => {\n                            let want_stat = match stalled {\n                                false => Stat::VALID,\n                                true => Stat::STALL,\n                            };\n                            let mut w = invariant(r);\n                            w.set_stat_rx(Stat::from_bits(r.stat_rx().to_bits() ^ want_stat.to_bits()));\n                            reg.write_value(w);\n                        }\n                    }\n                }\n                EP_OUT_WAKERS[ep_addr.index()].wake();\n            }\n        }\n    }\n\n    fn endpoint_is_stalled(&mut self, ep_addr: EndpointAddress) -> bool {\n        let regs = T::regs();\n        let epr = regs.epr(ep_addr.index() as _).read();\n        match ep_addr.direction() {\n            Direction::In => epr.stat_tx() == Stat::STALL,\n            Direction::Out => epr.stat_rx() == Stat::STALL,\n        }\n    }\n\n    fn endpoint_set_enabled(&mut self, ep_addr: EndpointAddress, enabled: bool) {\n        trace!(\"set_enabled {:?} {}\", ep_addr, enabled);\n        // This can race, so do a retry loop.\n        let epr = T::regs().epr(ep_addr.index() as _);\n        trace!(\"EPR before: {:04x}\", epr.read().0);\n        match ep_addr.direction() {\n            Direction::In => {\n                loop {\n                    let want_stat = match enabled {\n                        false => Stat::DISABLED,\n                        true => match epr.read().ep_type() {\n                            EpType::ISO => Stat::VALID,\n                            _ => Stat::NAK,\n                        },\n                    };\n                    let r = epr.read();\n                    if r.stat_tx() == want_stat {\n                        break;\n                    }\n                    let mut w = invariant(r);\n                    w.set_stat_tx(Stat::from_bits(r.stat_tx().to_bits() ^ want_stat.to_bits()));\n                    epr.write_value(w);\n                }\n                EP_IN_WAKERS[ep_addr.index()].wake();\n            }\n            Direction::Out => {\n                loop {\n                    let want_stat = match enabled {\n                        false => Stat::DISABLED,\n                        true => Stat::VALID,\n                    };\n                    let r = epr.read();\n                    if r.stat_rx() == want_stat {\n                        break;\n                    }\n                    let mut w = invariant(r);\n                    w.set_stat_rx(Stat::from_bits(r.stat_rx().to_bits() ^ want_stat.to_bits()));\n                    epr.write_value(w);\n                }\n                EP_OUT_WAKERS[ep_addr.index()].wake();\n            }\n        }\n        trace!(\"EPR after: {:04x}\", epr.read().0);\n    }\n\n    async fn enable(&mut self) {}\n    async fn disable(&mut self) {}\n\n    async fn remote_wakeup(&mut self) -> Result<(), Unsupported> {\n        Err(Unsupported)\n    }\n}\n\ntrait Dir {\n    fn dir() -> Direction;\n}\n\n/// Marker type for the \"IN\" direction.\npub enum In {}\nimpl Dir for In {\n    fn dir() -> Direction {\n        Direction::In\n    }\n}\n\n/// Marker type for the \"OUT\" direction.\npub enum Out {}\nimpl Dir for Out {\n    fn dir() -> Direction {\n        Direction::Out\n    }\n}\n\n/// Selects the packet buffer.\n///\n/// For double-buffered endpoints, both the `Rx` and `Tx` buffer from a channel are used for the same\n/// direction of transfer. This is opposed to single-buffered endpoints, where one channel can serve\n/// two directions at the same time.\n#[derive(Clone, Copy, Debug)]\nenum PacketBuffer {\n    /// The RX buffer - must be used for single-buffered OUT endpoints\n    Rx,\n    /// The TX buffer - must be used for single-buffered IN endpoints\n    Tx,\n}\n\n/// USB endpoint.\npub struct Endpoint<'d, T: Instance, D> {\n    _phantom: PhantomData<(&'d mut T, D)>,\n    info: EndpointInfo,\n    buf: EndpointBuffer<T>,\n}\n\nimpl<'d, T: Instance, D> Endpoint<'d, T, D> {\n    /// Write to a double-buffered endpoint.\n    ///\n    /// For double-buffered endpoints, the data buffers overlap, but we still need to write to the right counter field.\n    /// The DTOG_TX bit indicates the buffer that is currently in use by the USB peripheral, that is, the buffer in\n    /// which the next transmit packet will be stored, so we need to write the counter of the OTHER buffer, which is\n    /// where the last transmitted packet was stored.\n    fn write_data_double_buffered(&mut self, buf: &[u8], packet_buffer: PacketBuffer) {\n        let index = self.info.addr.index();\n        self.buf.write(buf);\n\n        match packet_buffer {\n            PacketBuffer::Rx => btable::write_in_len_rx::<T>(index, self.buf.addr, buf.len() as _),\n            PacketBuffer::Tx => btable::write_in_len_tx::<T>(index, self.buf.addr, buf.len() as _),\n        }\n    }\n\n    /// Write to a single-buffered endpoint.\n    fn write_data(&mut self, buf: &[u8]) {\n        self.write_data_double_buffered(buf, PacketBuffer::Tx);\n    }\n\n    /// Read from a double-buffered endpoint.\n    ///\n    /// For double-buffered endpoints, the data buffers overlap, but we still need to read from the right counter field.\n    /// The DTOG_RX bit indicates the buffer that is currently in use by the USB peripheral, that is, the buffer in\n    /// which the next received packet will be stored, so we need to read the counter of the OTHER buffer, which is\n    /// where the last received packet was stored.\n    fn read_data_double_buffered(\n        &mut self,\n        buf: &mut [u8],\n        packet_buffer: PacketBuffer,\n    ) -> Result<usize, EndpointError> {\n        let index = self.info.addr.index();\n\n        let rx_len = match packet_buffer {\n            PacketBuffer::Rx => btable::read_out_len_rx::<T>(index),\n            PacketBuffer::Tx => btable::read_out_len_tx::<T>(index),\n        } as usize\n            & 0x3FF;\n\n        trace!(\"READ DONE, rx_len = {}\", rx_len);\n        if rx_len > buf.len() {\n            return Err(EndpointError::BufferOverflow);\n        }\n        self.buf.read(&mut buf[..rx_len]);\n        Ok(rx_len)\n    }\n\n    /// Read from a single-buffered endpoint.\n    fn read_data(&mut self, buf: &mut [u8]) -> Result<usize, EndpointError> {\n        self.read_data_double_buffered(buf, PacketBuffer::Rx)\n    }\n}\n\nimpl<'d, T: Instance> driver::Endpoint for Endpoint<'d, T, In> {\n    fn info(&self) -> &EndpointInfo {\n        &self.info\n    }\n\n    async fn wait_enabled(&mut self) {\n        trace!(\"wait_enabled IN WAITING\");\n        let index = self.info.addr.index();\n        poll_fn(|cx| {\n            EP_IN_WAKERS[index].register(cx.waker());\n            let regs = T::regs();\n            if regs.epr(index).read().stat_tx() == Stat::DISABLED {\n                Poll::Pending\n            } else {\n                Poll::Ready(())\n            }\n        })\n        .await;\n        trace!(\"wait_enabled IN OK\");\n    }\n}\n\nimpl<'d, T: Instance> driver::Endpoint for Endpoint<'d, T, Out> {\n    fn info(&self) -> &EndpointInfo {\n        &self.info\n    }\n\n    async fn wait_enabled(&mut self) {\n        trace!(\"wait_enabled OUT WAITING\");\n        let index = self.info.addr.index();\n        poll_fn(|cx| {\n            EP_OUT_WAKERS[index].register(cx.waker());\n            let regs = T::regs();\n            if regs.epr(index).read().stat_rx() == Stat::DISABLED {\n                Poll::Pending\n            } else {\n                Poll::Ready(())\n            }\n        })\n        .await;\n        trace!(\"wait_enabled OUT OK\");\n    }\n}\n\nimpl<'d, T: Instance> driver::EndpointOut for Endpoint<'d, T, Out> {\n    async fn read(&mut self, buf: &mut [u8]) -> Result<usize, EndpointError> {\n        trace!(\"READ WAITING, buf.len() = {}\", buf.len());\n        let index = self.info.addr.index();\n        let stat = poll_fn(|cx| {\n            EP_OUT_WAKERS[index].register(cx.waker());\n            let regs = T::regs();\n            let stat = regs.epr(index).read().stat_rx();\n            if self.info.ep_type == EndpointType::Isochronous {\n                // The isochronous endpoint does not change its `STAT_RX` field to `NAK` when receiving a packet.\n                // Therefore, this instead waits until the `CTR` interrupt was triggered.\n                if matches!(stat, Stat::DISABLED) || RX_COMPLETE[index].load(Ordering::Relaxed) {\n                    assert!(matches!(stat, Stat::VALID | Stat::DISABLED));\n                    Poll::Ready(stat)\n                } else {\n                    Poll::Pending\n                }\n            } else {\n                if matches!(stat, Stat::NAK | Stat::DISABLED) {\n                    Poll::Ready(stat)\n                } else {\n                    Poll::Pending\n                }\n            }\n        })\n        .await;\n\n        // Errata for STM32H5, 2.20.1:\n        // During OUT transfers, the correct transfer interrupt (CTR) is triggered a little before the last USB SRAM accesses\n        // have completed. If the software responds quickly to the interrupt, the full buffer contents may not be correct.\n        //\n        // Workaround:\n        // Software should ensure that a small delay is included before accessing the SRAM contents. This delay should be\n        // 800 ns in Full Speed mode and 6.4 μs in Low Speed mode.\n        #[cfg(stm32h5)]\n        {\n            #[cfg(feature = \"time\")]\n            embassy_time::block_for(embassy_time::Duration::from_nanos(800));\n            #[cfg(not(feature = \"time\"))]\n            {\n                let freq = unsafe { crate::rcc::get_freqs() }.sys.to_hertz().unwrap().0 as u64;\n                let cycles = freq * 800 / 1_000_000;\n                cortex_m::asm::delay(cycles as u32);\n            }\n        }\n\n        RX_COMPLETE[index].store(false, Ordering::Relaxed);\n\n        if stat == Stat::DISABLED {\n            return Err(EndpointError::Disabled);\n        }\n\n        let regs = T::regs();\n\n        let rx_len = if self.info.ep_type == EndpointType::Isochronous {\n            // Find the buffer, which is currently in use. Read from the OTHER buffer.\n            let packet_buffer = if regs.epr(index).read().dtog_rx() {\n                PacketBuffer::Tx\n            } else {\n                PacketBuffer::Rx\n            };\n            self.read_data_double_buffered(buf, packet_buffer)?\n        } else {\n            let len = self.read_data(buf)?;\n\n            regs.epr(index).write(|w| {\n                w.set_ep_type(convert_type(self.info.ep_type));\n                w.set_ea(self.info.addr.index() as _);\n                w.set_stat_rx(Stat::from_bits(Stat::NAK.to_bits() ^ Stat::VALID.to_bits()));\n                w.set_stat_tx(Stat::from_bits(0));\n                w.set_ctr_rx(true); // don't clear\n                w.set_ctr_tx(true); // don't clear\n            });\n\n            len\n        };\n        trace!(\"READ OK, rx_len = {}\", rx_len);\n\n        Ok(rx_len)\n    }\n}\n\nimpl<'d, T: Instance> driver::EndpointIn for Endpoint<'d, T, In> {\n    async fn write(&mut self, buf: &[u8]) -> Result<(), EndpointError> {\n        if buf.len() > self.info.max_packet_size as usize {\n            return Err(EndpointError::BufferOverflow);\n        }\n        trace!(\"WRITE WAITING, buf.len() = {}\", buf.len());\n\n        let regs = T::regs();\n        let index = self.info.addr.index();\n\n        if self.info.ep_type == EndpointType::Isochronous {\n            // Find the buffer, which is currently in use. Write to the OTHER buffer.\n            let packet_buffer = if regs.epr(index).read().dtog_tx() {\n                PacketBuffer::Rx\n            } else {\n                PacketBuffer::Tx\n            };\n\n            self.write_data_double_buffered(buf, packet_buffer);\n        }\n\n        let stat = poll_fn(|cx| {\n            EP_IN_WAKERS[index].register(cx.waker());\n            let regs = T::regs();\n            let stat = regs.epr(index).read().stat_tx();\n            if self.info.ep_type == EndpointType::Isochronous {\n                // The isochronous endpoint does not change its `STAT_TX` field to `NAK` after sending a packet.\n                // Therefore, this instead waits until the `CTR` interrupt was triggered.\n                if matches!(stat, Stat::DISABLED) || !TX_PENDING[index].load(Ordering::Relaxed) {\n                    assert!(matches!(stat, Stat::VALID | Stat::DISABLED));\n                    Poll::Ready(stat)\n                } else {\n                    Poll::Pending\n                }\n            } else {\n                if matches!(stat, Stat::NAK | Stat::DISABLED) {\n                    Poll::Ready(stat)\n                } else {\n                    Poll::Pending\n                }\n            }\n        })\n        .await;\n\n        if stat == Stat::DISABLED {\n            return Err(EndpointError::Disabled);\n        }\n\n        if self.info.ep_type != EndpointType::Isochronous {\n            self.write_data(buf);\n\n            regs.epr(index).write(|w| {\n                w.set_ep_type(convert_type(self.info.ep_type));\n                w.set_ea(self.info.addr.index() as _);\n                w.set_stat_tx(Stat::from_bits(Stat::NAK.to_bits() ^ Stat::VALID.to_bits()));\n                w.set_stat_rx(Stat::from_bits(0));\n                w.set_ctr_rx(true); // don't clear\n                w.set_ctr_tx(true); // don't clear\n            });\n        }\n        TX_PENDING[index].store(true, Ordering::Relaxed);\n        trace!(\"WRITE OK\");\n\n        Ok(())\n    }\n}\n\n/// USB control pipe.\npub struct ControlPipe<'d, T: Instance> {\n    _phantom: PhantomData<&'d mut T>,\n    max_packet_size: u16,\n    ep_in: Endpoint<'d, T, In>,\n    ep_out: Endpoint<'d, T, Out>,\n}\n\nimpl<'d, T: Instance> driver::ControlPipe for ControlPipe<'d, T> {\n    fn max_packet_size(&self) -> usize {\n        usize::from(self.max_packet_size)\n    }\n\n    async fn setup(&mut self) -> [u8; 8] {\n        loop {\n            trace!(\"SETUP read waiting\");\n            poll_fn(|cx| {\n                EP_OUT_WAKERS[0].register(cx.waker());\n                if EP0_SETUP.load(Ordering::Relaxed) {\n                    Poll::Ready(())\n                } else {\n                    Poll::Pending\n                }\n            })\n            .await;\n\n            let mut buf = [0; 8];\n            let rx_len = self.ep_out.read_data(&mut buf);\n            if rx_len != Ok(8) {\n                trace!(\"SETUP read failed: {:?}\", rx_len);\n                continue;\n            }\n\n            EP0_SETUP.store(false, Ordering::Relaxed);\n\n            trace!(\"SETUP read ok\");\n            return buf;\n        }\n    }\n\n    async fn data_out(&mut self, buf: &mut [u8], first: bool, last: bool) -> Result<usize, EndpointError> {\n        let regs = T::regs();\n\n        // When a SETUP is received, Stat/Stat is set to NAK.\n        // On first transfer, we must set Stat=VALID, to get the OUT data stage.\n        // We want Stat=STALL so that the host gets a STALL if it switches to the status\n        // stage too soon, except in the last transfer we set Stat=NAK so that it waits\n        // for the status stage, which we will ACK or STALL later.\n        if first || last {\n            let mut stat_rx = 0;\n            let mut stat_tx = 0;\n            if first {\n                // change NAK -> VALID\n                stat_rx ^= Stat::NAK.to_bits() ^ Stat::VALID.to_bits();\n                stat_tx ^= Stat::NAK.to_bits() ^ Stat::STALL.to_bits();\n            }\n            if last {\n                // change STALL -> VALID\n                stat_tx ^= Stat::STALL.to_bits() ^ Stat::NAK.to_bits();\n            }\n            // Note: if this is the first AND last transfer, the above effectively\n            // changes stat_tx like NAK -> NAK, so noop.\n            regs.epr(0).write(|w| {\n                w.set_ep_type(EpType::CONTROL);\n                w.set_stat_rx(Stat::from_bits(stat_rx));\n                w.set_stat_tx(Stat::from_bits(stat_tx));\n                w.set_ctr_rx(true); // don't clear\n                w.set_ctr_tx(true); // don't clear\n            });\n        }\n\n        trace!(\"data_out WAITING, buf.len() = {}\", buf.len());\n        poll_fn(|cx| {\n            EP_OUT_WAKERS[0].register(cx.waker());\n            let regs = T::regs();\n            if regs.epr(0).read().stat_rx() == Stat::NAK {\n                Poll::Ready(())\n            } else {\n                Poll::Pending\n            }\n        })\n        .await;\n\n        if EP0_SETUP.load(Ordering::Relaxed) {\n            trace!(\"received another SETUP, aborting data_out.\");\n            return Err(EndpointError::Disabled);\n        }\n\n        let rx_len = self.ep_out.read_data(buf)?;\n\n        regs.epr(0).write(|w| {\n            w.set_ep_type(EpType::CONTROL);\n            w.set_stat_rx(Stat::from_bits(match last {\n                // If last, set STAT_RX=STALL.\n                true => Stat::NAK.to_bits() ^ Stat::STALL.to_bits(),\n                // Otherwise, set STAT_RX=VALID, to allow the host to send the next packet.\n                false => Stat::NAK.to_bits() ^ Stat::VALID.to_bits(),\n            }));\n            w.set_ctr_rx(true); // don't clear\n            w.set_ctr_tx(true); // don't clear\n        });\n\n        Ok(rx_len)\n    }\n\n    async fn data_in(&mut self, data: &[u8], first: bool, last: bool) -> Result<(), EndpointError> {\n        trace!(\"control: data_in\");\n\n        if data.len() > self.ep_in.info.max_packet_size as usize {\n            return Err(EndpointError::BufferOverflow);\n        }\n\n        let regs = T::regs();\n\n        // When a SETUP is received, Stat is set to NAK.\n        // We want it to be STALL in non-last transfers.\n        // We want it to be VALID in last transfer, so the HW does the status stage.\n        if first || last {\n            let mut stat_rx = 0;\n            if first {\n                // change NAK -> STALL\n                stat_rx ^= Stat::NAK.to_bits() ^ Stat::STALL.to_bits();\n            }\n            if last {\n                // change STALL -> VALID\n                stat_rx ^= Stat::STALL.to_bits() ^ Stat::VALID.to_bits();\n            }\n            // Note: if this is the first AND last transfer, the above effectively\n            // does a change of NAK -> VALID.\n            regs.epr(0).write(|w| {\n                w.set_ep_type(EpType::CONTROL);\n                w.set_stat_rx(Stat::from_bits(stat_rx));\n                w.set_ep_kind(last); // set OUT_STATUS if last.\n                w.set_ctr_rx(true); // don't clear\n                w.set_ctr_tx(true); // don't clear\n            });\n        }\n\n        trace!(\"WRITE WAITING\");\n        poll_fn(|cx| {\n            EP_IN_WAKERS[0].register(cx.waker());\n            EP_OUT_WAKERS[0].register(cx.waker());\n            let regs = T::regs();\n            if regs.epr(0).read().stat_tx() == Stat::NAK {\n                Poll::Ready(())\n            } else {\n                Poll::Pending\n            }\n        })\n        .await;\n\n        if EP0_SETUP.load(Ordering::Relaxed) {\n            trace!(\"received another SETUP, aborting data_in.\");\n            return Err(EndpointError::Disabled);\n        }\n\n        self.ep_in.write_data(data);\n\n        let regs = T::regs();\n        regs.epr(0).write(|w| {\n            w.set_ep_type(EpType::CONTROL);\n            w.set_stat_tx(Stat::from_bits(Stat::NAK.to_bits() ^ Stat::VALID.to_bits()));\n            w.set_ep_kind(last); // set OUT_STATUS if last.\n            w.set_ctr_rx(true); // don't clear\n            w.set_ctr_tx(true); // don't clear\n        });\n\n        trace!(\"WRITE OK\");\n\n        Ok(())\n    }\n\n    async fn accept(&mut self) {\n        let regs = T::regs();\n        trace!(\"control: accept\");\n\n        self.ep_in.write_data(&[]);\n\n        // Set OUT=stall, IN=accept\n        let epr = regs.epr(0).read();\n        regs.epr(0).write(|w| {\n            w.set_ep_type(EpType::CONTROL);\n            w.set_stat_rx(Stat::from_bits(epr.stat_rx().to_bits() ^ Stat::STALL.to_bits()));\n            w.set_stat_tx(Stat::from_bits(epr.stat_tx().to_bits() ^ Stat::VALID.to_bits()));\n            w.set_ctr_rx(true); // don't clear\n            w.set_ctr_tx(true); // don't clear\n        });\n        trace!(\"control: accept WAITING\");\n\n        // Wait is needed, so that we don't set the address too soon, breaking the status stage.\n        // (embassy-usb sets the address after accept() returns)\n        poll_fn(|cx| {\n            EP_IN_WAKERS[0].register(cx.waker());\n            let regs = T::regs();\n            if regs.epr(0).read().stat_tx() == Stat::NAK {\n                Poll::Ready(())\n            } else {\n                Poll::Pending\n            }\n        })\n        .await;\n\n        trace!(\"control: accept OK\");\n    }\n\n    async fn reject(&mut self) {\n        let regs = T::regs();\n        trace!(\"control: reject\");\n\n        // Set IN+OUT to stall\n        let epr = regs.epr(0).read();\n        regs.epr(0).write(|w| {\n            w.set_ep_type(EpType::CONTROL);\n            w.set_stat_rx(Stat::from_bits(epr.stat_rx().to_bits() ^ Stat::STALL.to_bits()));\n            w.set_stat_tx(Stat::from_bits(epr.stat_tx().to_bits() ^ Stat::STALL.to_bits()));\n            w.set_ctr_rx(true); // don't clear\n            w.set_ctr_tx(true); // don't clear\n        });\n    }\n\n    async fn accept_set_address(&mut self, addr: u8) {\n        self.accept().await;\n\n        let regs = T::regs();\n        trace!(\"setting addr: {}\", addr);\n        regs.daddr().write(|w| {\n            w.set_ef(true);\n            w.set_add(addr);\n        });\n    }\n}\n\ntrait SealedInstance {\n    fn regs() -> crate::pac::usb::Usb;\n}\n\n/// USB instance trait.\n#[allow(private_bounds)]\npub trait Instance: SealedInstance + PeripheralType + RccPeripheral + 'static {\n    /// Interrupt for this USB instance.\n    type Interrupt: interrupt::typelevel::Interrupt;\n}\n\n// Internal PHY pins\npin_trait!(DpPin, Instance);\npin_trait!(DmPin, Instance);\npin_trait!(SofPin, Instance);\n\nforeach_interrupt!(\n    ($inst:ident, usb, $block:ident, LP, $irq:ident) => {\n        impl SealedInstance for crate::peripherals::$inst {\n            fn regs() -> crate::pac::usb::Usb {\n                crate::pac::$inst\n            }\n        }\n\n        impl Instance for crate::peripherals::$inst {\n            type Interrupt = crate::interrupt::typelevel::$irq;\n        }\n    };\n);\n"
  },
  {
    "path": "embassy-stm32/src/vrefbuf/mod.rs",
    "content": "//! Voltage Reference Buffer (VREFBUF)\nuse core::marker::PhantomData;\n\nuse embassy_hal_internal::PeripheralType;\nuse stm32_metapac::vrefbuf::vals::*;\n\nuse crate::Peri;\n\n/// Voltage Reference (VREFBUF) driver.\npub struct VoltageReferenceBuffer<'d, T: Instance> {\n    vrefbuf: PhantomData<&'d mut T>,\n}\n\n#[cfg(rcc_wba)]\nfn get_refbuf_trim(voltage_scale: Vrs) -> usize {\n    match voltage_scale {\n        Vrs::VREF0 => 0x0BFA_07ABusize,\n        Vrs::VREF1 => 0x0BFA_07AAusize,\n        Vrs::VREF2 => 0x0BFA_07A9usize,\n        Vrs::VREF3 => 0x0BFA_07A8usize,\n        _ => panic!(\"Incorrect Vrs setting!\"),\n    }\n}\n\nimpl<'d, T: Instance> VoltageReferenceBuffer<'d, T> {\n    /// Creates an VREFBUF (Voltage Reference) instance with a voltage scale and impedance mode.\n    ///\n    /// [Self] has to be started with [Self::new()].\n    pub fn new(_instance: Peri<'d, T>, voltage_scale: Vrs, impedance_mode: Hiz) -> Self {\n        #[cfg(rcc_wba)]\n        {\n            use crate::pac::RCC;\n            RCC.apb7enr().modify(|w| w.set_vrefen(true));\n            // This is an errata for WBA6 devices. VREFBUF_TRIM value isn't set correctly\n            // [Link explaining it](https://www.st.com/resource/en/errata_sheet/es0644-stm32wba6xxx-device-errata-stmicroelectronics.pdf)\n            unsafe {\n                use crate::pac::VREFBUF;\n                let addr = get_refbuf_trim(voltage_scale);\n                let buf_trim_ptr = core::ptr::with_exposed_provenance::<u32>(addr);\n                let trim_val = core::ptr::read_volatile(buf_trim_ptr);\n                VREFBUF.ccr().write(|w| w.set_trim((trim_val & 0x3F) as u8));\n            }\n        }\n        #[cfg(any(rcc_u5, rcc_h50, rcc_h5))]\n        {\n            use crate::pac::RCC;\n            RCC.apb3enr().modify(|w| w.set_vrefen(true));\n        }\n        #[cfg(rcc_u3)]\n        {\n            use crate::pac::RCC;\n            RCC.apb1enr1().modify(|w| w.set_vrefen(true));\n        }\n        #[cfg(any(rcc_h7rs, rcc_h7rm0433, rcc_h7ab, rcc_h7))]\n        {\n            use crate::pac::RCC;\n            RCC.apb4enr().modify(|w| w.set_vrefen(true));\n        }\n        let vrefbuf = T::regs();\n        vrefbuf.csr().modify(|w| {\n            w.set_hiz(impedance_mode);\n            w.set_envr(true);\n            w.set_vrs(voltage_scale);\n        });\n        while vrefbuf.csr().read().vrr() != false {\n            // wait...\n        }\n        trace!(\n            \"Vrefbuf configured with voltage scale {} and impedance mode {}\",\n            voltage_scale as u8, impedance_mode as u8,\n        );\n        VoltageReferenceBuffer { vrefbuf: PhantomData }\n    }\n}\n\ntrait SealedInstance {\n    fn regs() -> crate::pac::vrefbuf::Vrefbuf;\n}\n\n/// VREFBUF instance trait.\n#[allow(private_bounds)]\npub trait Instance: SealedInstance + PeripheralType {}\n\nforeach_peripheral!(\n    (vrefbuf, $inst:ident) => {\n        impl SealedInstance for crate::peripherals::$inst {\n            fn regs() -> crate::pac::vrefbuf::Vrefbuf {\n                crate::pac::$inst\n            }\n        }\n\n        impl Instance for crate::peripherals::$inst {}\n    };\n);\n"
  },
  {
    "path": "embassy-stm32/src/wdg/mod.rs",
    "content": "//! Watchdog Timer (IWDG, WWDG)\nuse core::marker::PhantomData;\n\nuse embassy_hal_internal::PeripheralType;\nuse stm32_metapac::iwdg::vals::{Key, Pr};\n\nuse crate::Peri;\nuse crate::rcc::LSI_FREQ;\n\n/// Independent watchdog (IWDG) driver.\npub struct IndependentWatchdog<'d, T: Instance> {\n    wdg: PhantomData<&'d mut T>,\n}\n\n// 12-bit counter\nconst MAX_RL: u16 = 0xFFF;\n\n/// Calculates maximum watchdog timeout in us (RL = 0xFFF) for a given prescaler\nconst fn get_timeout_us(prescaler: u16, reload_value: u16) -> u32 {\n    1_000_000 * (reload_value + 1) as u32 / (LSI_FREQ.0 / prescaler as u32)\n}\n\n/// Calculates watchdog reload value for the given prescaler and desired timeout\nconst fn reload_value(prescaler: u16, timeout_us: u32) -> u16 {\n    (timeout_us / prescaler as u32 * LSI_FREQ.0 / 1_000_000) as u16 - 1\n}\n\nimpl<'d, T: Instance> IndependentWatchdog<'d, T> {\n    /// Creates an IWDG (Independent Watchdog) instance with a given timeout value in microseconds.\n    ///\n    /// [Self] has to be started with [Self::unleash()].\n    /// Once timer expires, MCU will be reset. To prevent this, timer must be reloaded by repeatedly calling [Self::pet()] within timeout interval.\n    pub fn new(_instance: Peri<'d, T>, timeout_us: u32) -> Self {\n        // Find lowest prescaler value, which makes watchdog period longer or equal to timeout.\n        // This iterates from 4 (2^2) to 256 (2^8).\n        let psc_power = unwrap!((2..=8).find(|psc_power| {\n            let psc = 2u16.pow(*psc_power);\n            timeout_us <= get_timeout_us(psc, MAX_RL)\n        }));\n\n        // Prescaler value\n        let psc = 2u16.pow(psc_power);\n\n        #[cfg(not(iwdg_v3))]\n        assert!(psc <= 256, \"IWDG prescaler should be no more than 256\");\n        #[cfg(iwdg_v3)] // H5, U5, WBA\n        assert!(psc <= 1024, \"IWDG prescaler should be no more than 1024\");\n\n        // Convert prescaler power to PR register value\n        let pr = psc_power as u8 - 2;\n\n        // Reload value\n        let rl = reload_value(psc, timeout_us);\n\n        let wdg = T::regs();\n        wdg.kr().write(|w| w.set_key(Key::ENABLE));\n        wdg.pr().write(|w| w.set_pr(Pr::from_bits(pr)));\n        wdg.rlr().write(|w| w.set_rl(rl));\n\n        trace!(\n            \"Watchdog configured with {}us timeout, desired was {}us (PR={}, RL={})\",\n            get_timeout_us(psc, rl),\n            timeout_us,\n            pr,\n            rl\n        );\n\n        IndependentWatchdog { wdg: PhantomData }\n    }\n\n    /// Unleash (start) the watchdog.\n    pub fn unleash(&mut self) {\n        T::regs().kr().write(|w| w.set_key(Key::START));\n    }\n\n    /// Pet (reload, refresh) the watchdog.\n    pub fn pet(&mut self) {\n        T::regs().kr().write(|w| w.set_key(Key::RESET));\n    }\n}\n\ntrait SealedInstance {\n    fn regs() -> crate::pac::iwdg::Iwdg;\n}\n\n/// IWDG instance trait.\n#[allow(private_bounds)]\npub trait Instance: SealedInstance + PeripheralType {}\n\nforeach_peripheral!(\n    (iwdg, $inst:ident) => {\n        impl SealedInstance for crate::peripherals::$inst {\n            fn regs() -> crate::pac::iwdg::Iwdg {\n                crate::pac::$inst\n            }\n        }\n\n        impl Instance for crate::peripherals::$inst {}\n    };\n);\n\n// ============================================================\n// WWDG (Window Watchdog)\n// ============================================================\n\n/// Returns `ceil(duration_us * pclk1_hz / (prescaler_mul * 4096 * 1_000_000))`.\n///\n/// Uses `u64` arithmetic throughout to prevent overflow.\n#[cfg(any(wwdg, test))]\nfn wwdg_ticks(duration_us: u32, pclk1_hz: u32, prescaler_mul: u32) -> u64 {\n    let num = duration_us as u64 * pclk1_hz as u64;\n    let den = prescaler_mul as u64 * 4096 * 1_000_000;\n    (num + den - 1) / den\n}\n\n#[cfg(wwdg)]\nuse stm32_metapac::wwdg::vals::Wdgtb;\n\n/// Window watchdog (WWDG) driver.\n///\n/// Once activated via [`WindowWatchdog::new`], the WWDG cannot be stopped\n/// without a system reset.\n///\n/// The counter counts from `T` down to 0x3F (63), triggering a reset when it\n/// reaches 0x3F. Petting the watchdog while the counter is still above the\n/// window register `W` (the *closed window*) also causes an immediate reset.\n///\n/// ```text\n/// T_initial ──count down──▶ W ──count down──▶ 0x40 ──▶ 0x3F (RESET)\n/// |◄──── closed window ────►|◄──── open window ────►|\n/// ```\n#[cfg(wwdg)]\npub struct WindowWatchdog<'d, T: WwdgInstance> {\n    wdg: PhantomData<&'d mut T>,\n    /// Counter value written to CR on every [`pet`](WindowWatchdog::pet) call.\n    counter: u8,\n}\n\n#[cfg(wwdg)]\nimpl<'d, T: WwdgInstance> WindowWatchdog<'d, T> {\n    /// Creates and immediately starts the window watchdog.\n    ///\n    /// - `timeout_us`: total watchdog period in microseconds (counter-to-reset time).\n    /// - `window_us`: closed-window duration in microseconds. During this initial\n    ///   portion of the period, petting the watchdog causes a reset. Pass `0` to\n    ///   disable the window restriction (allow petting at any time within the period).\n    ///   Must be strictly less than `timeout_us`.\n    pub fn new(_instance: Peri<'d, T>, timeout_us: u32, window_us: u32) -> Self {\n        assert!(window_us < timeout_us, \"window_us must be less than timeout_us\");\n\n        crate::rcc::enable_and_reset::<T>();\n\n        let pclk1 = T::frequency().0;\n\n        // Select the smallest prescaler such that ticks falls in [1, 64].\n        // wwdg_v1 has a 2-bit WDGTB field (DIV1–DIV8); wwdg_v2 has a 3-bit field (DIV1–DIV128).\n        #[cfg(wwdg_v2)]\n        const PRESCALER_MULS: &[u32] = &[1, 2, 4, 8, 16, 32, 64, 128];\n        #[cfg(not(wwdg_v2))]\n        const PRESCALER_MULS: &[u32] = &[1, 2, 4, 8];\n        let (prescaler_mul, ticks) = unwrap!(\n            PRESCALER_MULS.iter().find_map(|&mul| {\n                let t = wwdg_ticks(timeout_us, pclk1, mul);\n                if (1..=64).contains(&t) { Some((mul, t)) } else { None }\n            }),\n            \"WWDG: timeout_us is out of range for all prescalers\"\n        );\n\n        // T = 63 + ticks; T is in [0x40, 0x7F].\n        let t_val = 63u8 + ticks as u8;\n\n        // W = T − floor(window_us * pclk1 / (prescaler_mul * 4096 * 1_000_000)).\n        // When window_us == 0 the closed window is empty and W == T.\n        let den = prescaler_mul as u64 * 4096 * 1_000_000;\n        let closed_ticks = (window_us as u64 * pclk1 as u64) / den;\n        let w_val = t_val - closed_ticks as u8;\n\n        // WDGTB bits are log2(prescaler_mul): DIV1=0, DIV2=1, DIV4=2, ...\n        let wdgtb = Wdgtb::from_bits(prescaler_mul.trailing_zeros() as u8);\n\n        let regs = T::regs();\n\n        // Write CFR before CR: prescaler and window must be set before activation.\n        regs.cfr().write(|cfr| {\n            cfr.set_wdgtb(wdgtb);\n            cfr.set_w(w_val);\n        });\n\n        // Activate watchdog (WDGA = 1 is hardware-irreversible).\n        regs.cr().write(|cr| {\n            cr.set_t(t_val);\n            cr.set_wdga(true);\n        });\n\n        trace!(\n            \"WWDG configured: timeout={}us window={}us pclk1={} prescaler=x{} T={} W={}\",\n            timeout_us, window_us, pclk1, prescaler_mul, t_val, w_val,\n        );\n\n        WindowWatchdog {\n            wdg: PhantomData,\n            counter: t_val,\n        }\n    }\n\n    /// Pet (reload) the watchdog.\n    ///\n    /// Must be called while the counter has fallen into the open window\n    /// (counter ≤ W). Calling too early (counter > W) causes an immediate reset.\n    pub fn pet(&mut self) {\n        T::regs().cr().write(|cr| {\n            cr.set_t(self.counter);\n            cr.set_wdga(true);\n        });\n    }\n}\n\n#[cfg(wwdg)]\ntrait WwdgSealedInstance {\n    fn regs() -> crate::pac::wwdg::Wwdg;\n}\n\n/// WWDG instance trait.\n#[cfg(wwdg)]\n#[allow(private_bounds)]\npub trait WwdgInstance: WwdgSealedInstance + PeripheralType + crate::rcc::RccPeripheral {}\n\n#[cfg(wwdg)]\nforeach_peripheral!(\n    (wwdg, $inst:ident) => {\n        impl WwdgSealedInstance for crate::peripherals::$inst {\n            fn regs() -> crate::pac::wwdg::Wwdg {\n                crate::pac::$inst\n            }\n        }\n\n        impl WwdgInstance for crate::peripherals::$inst {}\n    };\n);\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n\n    #[test]\n    fn can_compute_timeout_us() {\n        assert_eq!(125, get_timeout_us(4, 0));\n        assert_eq!(512_000, get_timeout_us(4, MAX_RL));\n\n        assert_eq!(8_000, get_timeout_us(256, 0));\n        assert_eq!(32_768_000, get_timeout_us(256, MAX_RL));\n\n        assert_eq!(8_000_000, get_timeout_us(64, 3999));\n    }\n\n    #[test]\n    fn can_compute_reload_value() {\n        assert_eq!(0xFFF, reload_value(4, 512_000));\n        assert_eq!(0xFFF, reload_value(256, 32_768_000));\n\n        assert_eq!(3999, reload_value(64, 8_000_000));\n    }\n}\n\n#[cfg(test)]\nmod wwdg_tests {\n    use super::wwdg_ticks;\n\n    // Typical PCLK1 frequency for STM32WBA65 at 64 MHz.\n    const PCLK1: u32 = 64_000_000;\n\n    #[test]\n    fn ticks_rounded_up() {\n        // 1000 µs * 64 MHz / (1 * 4096 * 1e6) = 15.625 → ceil = 16\n        assert_eq!(16, wwdg_ticks(1_000, PCLK1, 1));\n    }\n\n    #[test]\n    fn ticks_exact() {\n        // 1024 µs * 64 MHz / (1 * 4096 * 1e6) = 16.0 exactly\n        assert_eq!(16, wwdg_ticks(1_024, PCLK1, 1));\n    }\n\n    #[test]\n    fn ticks_just_above_exact() {\n        // 1025 µs → ceil(16.015625) = 17\n        assert_eq!(17, wwdg_ticks(1_025, PCLK1, 1));\n    }\n\n    #[test]\n    fn ticks_large_prescaler() {\n        // 100 ms, prescaler 128:\n        // 100_000 * 64_000_000 / (128 * 4096 * 1e6) ≈ 12.207 → ceil = 13\n        assert_eq!(13, wwdg_ticks(100_000, PCLK1, 128));\n    }\n\n    #[test]\n    fn ticks_minimum_duration() {\n        // 1 µs: ceil(64_000_000 / 4_096_000_000) = 1\n        assert_eq!(1, wwdg_ticks(1, PCLK1, 1));\n    }\n}\n"
  },
  {
    "path": "embassy-stm32/src/xspi/enums.rs",
    "content": "//! Enums used in Xspi configuration.\n#[derive(Copy, Clone)]\npub(crate) enum XspiMode {\n    IndirectWrite,\n    IndirectRead,\n    #[expect(dead_code)]\n    AutoPolling,\n    #[expect(dead_code)]\n    MemoryMapped,\n}\n\nimpl Into<u8> for XspiMode {\n    fn into(self) -> u8 {\n        match self {\n            XspiMode::IndirectWrite => 0b00,\n            XspiMode::IndirectRead => 0b01,\n            XspiMode::AutoPolling => 0b10,\n            XspiMode::MemoryMapped => 0b11,\n        }\n    }\n}\n\n/// Xspi lane width\n#[derive(Copy, Clone)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub enum XspiWidth {\n    /// None\n    NONE,\n    /// Single lane\n    SING,\n    /// Dual lanes\n    DUAL,\n    /// Quad lanes\n    QUAD,\n    /// Eight lanes\n    OCTO,\n    /// Sixteen lanes (Hexadeca-SPI)\n    HEXA,\n}\n\nimpl Into<u8> for XspiWidth {\n    fn into(self) -> u8 {\n        match self {\n            XspiWidth::NONE => 0b00,\n            XspiWidth::SING => 0b01,\n            XspiWidth::DUAL => 0b10,\n            XspiWidth::QUAD => 0b11,\n            XspiWidth::OCTO => 0b100,\n            XspiWidth::HEXA => 0b101,\n        }\n    }\n}\n\n/// Wrap Size\n#[allow(missing_docs)]\n#[derive(Copy, Clone)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub enum WrapSize {\n    None,\n    _16Bytes,\n    _32Bytes,\n    _64Bytes,\n    _128Bytes,\n}\n\nimpl Into<u8> for WrapSize {\n    fn into(self) -> u8 {\n        match self {\n            WrapSize::None => 0x00,\n            WrapSize::_16Bytes => 0x02,\n            WrapSize::_32Bytes => 0x03,\n            WrapSize::_64Bytes => 0x04,\n            WrapSize::_128Bytes => 0x05,\n        }\n    }\n}\n\n/// Memory Type\n#[allow(missing_docs)]\n#[derive(Copy, Clone)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub enum MemoryType {\n    Micron,\n    Macronix,\n    Standard,\n    MacronixRam,\n    HyperBusMemory,\n    HyperBusRegister,\n    APMemory16Bits, // AP Memory 16-bit (for  PSRAM in X8/X16 mode)\n    APMemory,       //The same as Standard\n}\n\nimpl Into<u8> for MemoryType {\n    fn into(self) -> u8 {\n        match self {\n            MemoryType::Micron => 0x00,\n            MemoryType::Macronix => 0x01,\n            MemoryType::Standard => 0x02,\n            MemoryType::MacronixRam => 0x03,\n            MemoryType::HyperBusMemory => 0x04,\n            MemoryType::HyperBusRegister => 0x04,\n            MemoryType::APMemory16Bits => 0x06,\n            MemoryType::APMemory => 0x02,\n        }\n    }\n}\n\n/// Xspi memory size.\n#[allow(missing_docs)]\n#[derive(Copy, Clone)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub enum MemorySize {\n    _1KiB,\n    _2KiB,\n    _4KiB,\n    _8KiB,\n    _16KiB,\n    _32KiB,\n    _64KiB,\n    _128KiB,\n    _256KiB,\n    _512KiB,\n    _1MiB,\n    _2MiB,\n    _4MiB,\n    _8MiB,\n    _16MiB,\n    _32MiB,\n    _64MiB,\n    _128MiB,\n    _256MiB,\n    _512MiB,\n    _1GiB,\n    _2GiB,\n    _4GiB,\n    Other(u8),\n}\n\nimpl Into<u8> for MemorySize {\n    fn into(self) -> u8 {\n        match self {\n            MemorySize::_1KiB => 9,\n            MemorySize::_2KiB => 10,\n            MemorySize::_4KiB => 11,\n            MemorySize::_8KiB => 12,\n            MemorySize::_16KiB => 13,\n            MemorySize::_32KiB => 14,\n            MemorySize::_64KiB => 15,\n            MemorySize::_128KiB => 16,\n            MemorySize::_256KiB => 17,\n            MemorySize::_512KiB => 18,\n            MemorySize::_1MiB => 19,\n            MemorySize::_2MiB => 20,\n            MemorySize::_4MiB => 21,\n            MemorySize::_8MiB => 22,\n            MemorySize::_16MiB => 23,\n            MemorySize::_32MiB => 24,\n            MemorySize::_64MiB => 25,\n            MemorySize::_128MiB => 26,\n            MemorySize::_256MiB => 27,\n            MemorySize::_512MiB => 28,\n            MemorySize::_1GiB => 29,\n            MemorySize::_2GiB => 30,\n            MemorySize::_4GiB => 31,\n            MemorySize::Other(val) => val,\n        }\n    }\n}\n\n/// Xspi Address size\n#[derive(Copy, Clone)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub enum AddressSize {\n    /// 8-bit address\n    _8bit,\n    /// 16-bit address\n    _16bit,\n    /// 24-bit address\n    _24bit,\n    /// 32-bit address\n    _32bit,\n}\n\nimpl Into<u8> for AddressSize {\n    fn into(self) -> u8 {\n        match self {\n            AddressSize::_8bit => 0b00,\n            AddressSize::_16bit => 0b01,\n            AddressSize::_24bit => 0b10,\n            AddressSize::_32bit => 0b11,\n        }\n    }\n}\n\n/// Time the Chip Select line stays high.\n#[allow(missing_docs)]\n#[derive(Copy, Clone)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub enum ChipSelectHighTime {\n    _1Cycle,\n    _2Cycle,\n    _3Cycle,\n    _4Cycle,\n    _5Cycle,\n    _6Cycle,\n    _7Cycle,\n    _8Cycle,\n}\n\nimpl Into<u8> for ChipSelectHighTime {\n    fn into(self) -> u8 {\n        match self {\n            ChipSelectHighTime::_1Cycle => 0,\n            ChipSelectHighTime::_2Cycle => 1,\n            ChipSelectHighTime::_3Cycle => 2,\n            ChipSelectHighTime::_4Cycle => 3,\n            ChipSelectHighTime::_5Cycle => 4,\n            ChipSelectHighTime::_6Cycle => 5,\n            ChipSelectHighTime::_7Cycle => 6,\n            ChipSelectHighTime::_8Cycle => 7,\n        }\n    }\n}\n\n/// FIFO threshold.\n#[allow(missing_docs)]\n#[derive(Copy, Clone)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub enum FIFOThresholdLevel {\n    _1Bytes,\n    _2Bytes,\n    _3Bytes,\n    _4Bytes,\n    _5Bytes,\n    _6Bytes,\n    _7Bytes,\n    _8Bytes,\n    _9Bytes,\n    _10Bytes,\n    _11Bytes,\n    _12Bytes,\n    _13Bytes,\n    _14Bytes,\n    _15Bytes,\n    _16Bytes,\n    _17Bytes,\n    _18Bytes,\n    _19Bytes,\n    _20Bytes,\n    _21Bytes,\n    _22Bytes,\n    _23Bytes,\n    _24Bytes,\n    _25Bytes,\n    _26Bytes,\n    _27Bytes,\n    _28Bytes,\n    _29Bytes,\n    _30Bytes,\n    _31Bytes,\n    _32Bytes,\n}\n\nimpl Into<u8> for FIFOThresholdLevel {\n    fn into(self) -> u8 {\n        match self {\n            FIFOThresholdLevel::_1Bytes => 0,\n            FIFOThresholdLevel::_2Bytes => 1,\n            FIFOThresholdLevel::_3Bytes => 2,\n            FIFOThresholdLevel::_4Bytes => 3,\n            FIFOThresholdLevel::_5Bytes => 4,\n            FIFOThresholdLevel::_6Bytes => 5,\n            FIFOThresholdLevel::_7Bytes => 6,\n            FIFOThresholdLevel::_8Bytes => 7,\n            FIFOThresholdLevel::_9Bytes => 8,\n            FIFOThresholdLevel::_10Bytes => 9,\n            FIFOThresholdLevel::_11Bytes => 10,\n            FIFOThresholdLevel::_12Bytes => 11,\n            FIFOThresholdLevel::_13Bytes => 12,\n            FIFOThresholdLevel::_14Bytes => 13,\n            FIFOThresholdLevel::_15Bytes => 14,\n            FIFOThresholdLevel::_16Bytes => 15,\n            FIFOThresholdLevel::_17Bytes => 16,\n            FIFOThresholdLevel::_18Bytes => 17,\n            FIFOThresholdLevel::_19Bytes => 18,\n            FIFOThresholdLevel::_20Bytes => 19,\n            FIFOThresholdLevel::_21Bytes => 20,\n            FIFOThresholdLevel::_22Bytes => 21,\n            FIFOThresholdLevel::_23Bytes => 22,\n            FIFOThresholdLevel::_24Bytes => 23,\n            FIFOThresholdLevel::_25Bytes => 24,\n            FIFOThresholdLevel::_26Bytes => 25,\n            FIFOThresholdLevel::_27Bytes => 26,\n            FIFOThresholdLevel::_28Bytes => 27,\n            FIFOThresholdLevel::_29Bytes => 28,\n            FIFOThresholdLevel::_30Bytes => 29,\n            FIFOThresholdLevel::_31Bytes => 30,\n            FIFOThresholdLevel::_32Bytes => 31,\n        }\n    }\n}\n\n/// Dummy cycle count\n#[allow(missing_docs)]\n#[derive(Copy, Clone)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub enum DummyCycles {\n    _0,\n    _1,\n    _2,\n    _3,\n    _4,\n    _5,\n    _6,\n    _7,\n    _8,\n    _9,\n    _10,\n    _11,\n    _12,\n    _13,\n    _14,\n    _15,\n    _16,\n    _17,\n    _18,\n    _19,\n    _20,\n    _21,\n    _22,\n    _23,\n    _24,\n    _25,\n    _26,\n    _27,\n    _28,\n    _29,\n    _30,\n    _31,\n}\n\nimpl Into<u8> for DummyCycles {\n    fn into(self) -> u8 {\n        match self {\n            DummyCycles::_0 => 0,\n            DummyCycles::_1 => 1,\n            DummyCycles::_2 => 2,\n            DummyCycles::_3 => 3,\n            DummyCycles::_4 => 4,\n            DummyCycles::_5 => 5,\n            DummyCycles::_6 => 6,\n            DummyCycles::_7 => 7,\n            DummyCycles::_8 => 8,\n            DummyCycles::_9 => 9,\n            DummyCycles::_10 => 10,\n            DummyCycles::_11 => 11,\n            DummyCycles::_12 => 12,\n            DummyCycles::_13 => 13,\n            DummyCycles::_14 => 14,\n            DummyCycles::_15 => 15,\n            DummyCycles::_16 => 16,\n            DummyCycles::_17 => 17,\n            DummyCycles::_18 => 18,\n            DummyCycles::_19 => 19,\n            DummyCycles::_20 => 20,\n            DummyCycles::_21 => 21,\n            DummyCycles::_22 => 22,\n            DummyCycles::_23 => 23,\n            DummyCycles::_24 => 24,\n            DummyCycles::_25 => 25,\n            DummyCycles::_26 => 26,\n            DummyCycles::_27 => 27,\n            DummyCycles::_28 => 28,\n            DummyCycles::_29 => 29,\n            DummyCycles::_30 => 30,\n            DummyCycles::_31 => 31,\n        }\n    }\n}\n"
  },
  {
    "path": "embassy-stm32/src/xspi/mod.rs",
    "content": "//! XSPI Serial Peripheral Interface\n//!\n\n#![macro_use]\n\npub mod enums;\n\nuse core::marker::PhantomData;\n\nuse embassy_embedded_hal::{GetConfig, SetConfig};\nuse embassy_hal_internal::PeripheralType;\npub use enums::*;\n\nuse crate::dma::{ChannelAndRequest, word};\nuse crate::gpio::{AfType, Flex, OutputType, Pull, Speed};\nuse crate::mode::{Async, Blocking, Mode as PeriMode};\nuse crate::pac::xspi::Xspi as Regs;\nuse crate::pac::xspi::vals::*;\n#[cfg(xspim_v1)]\nuse crate::pac::xspim::Xspim;\nuse crate::rcc::{self, RccPeripheral};\nuse crate::{Peri, peripherals};\n\n/// XPSI driver config.\n#[derive(Clone, Copy)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub struct Config {\n    /// Fifo threshold used by the peripheral to generate the interrupt indicating data\n    /// or space is available in the FIFO\n    pub fifo_threshold: FIFOThresholdLevel,\n    /// Indicates the type of external device connected\n    pub memory_type: MemoryType, // Need to add an additional enum to provide this public interface\n    /// Defines the size of the external device connected to the XSPI corresponding\n    /// to the number of address bits required to access the device\n    pub device_size: MemorySize,\n    /// Sets the minimum number of clock cycles that the chip select signal must be held high\n    /// between commands\n    pub chip_select_high_time: ChipSelectHighTime,\n    /// Enables the free running clock\n    pub free_running_clock: bool,\n    /// Sets the clock level when the device is not selected\n    pub clock_mode: bool,\n    /// Indicates the wrap size corresponding to the external device configuration\n    pub wrap_size: WrapSize,\n    /// Specified the prescaler factor used for generating the external clock based\n    /// on the AHB clock. 0 = Fkernel, 1 = Fkernel/2, 2 = Fkernel/3 etc.\n    pub clock_prescaler: u8,\n    /// Allows the delay of 1/2 cycle the data sampling to account for external\n    /// signal delays\n    pub sample_shifting: bool,\n    /// Allows hold to 1/4 cycle the data\n    pub delay_hold_quarter_cycle: bool,\n    /// Enables the transaction boundary feature and defines the boundary to release\n    /// the chip select\n    pub chip_select_boundary: u8,\n    /// Enables communication regulation feature. Chip select is released when the other\n    /// XSpi requests access to the bus\n    pub max_transfer: u8,\n    /// Enables the refresh feature, chip select is released every refresh + 1 clock cycles\n    pub refresh: u32,\n}\n\nimpl Default for Config {\n    fn default() -> Self {\n        Self {\n            fifo_threshold: FIFOThresholdLevel::_16Bytes, // 32 bytes FIFO, half capacity\n            memory_type: MemoryType::Micron,\n            device_size: MemorySize::Other(0),\n            chip_select_high_time: ChipSelectHighTime::_5Cycle,\n            free_running_clock: false,\n            clock_mode: false,\n            wrap_size: WrapSize::None,\n            clock_prescaler: 0,\n            sample_shifting: false,\n            delay_hold_quarter_cycle: false,\n            chip_select_boundary: 0, // Acceptable range 0 to 31\n            max_transfer: 0,\n            refresh: 0,\n        }\n    }\n}\n\n/// XSPI transfer configuration.\n#[derive(Clone, Copy)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub struct TransferConfig {\n    /// Instruction width (IMODE)\n    pub iwidth: XspiWidth,\n    /// Instruction Id\n    pub instruction: Option<u32>,\n    /// Number of Instruction Bytes\n    pub isize: AddressSize,\n    /// Instruction Double Transfer rate enable\n    pub idtr: bool,\n\n    /// Address width (ADMODE)\n    pub adwidth: XspiWidth,\n    /// Device memory address\n    pub address: Option<u32>,\n    /// Number of Address Bytes\n    pub adsize: AddressSize,\n    /// Address Double Transfer rate enable\n    pub addtr: bool,\n\n    /// Alternate bytes width (ABMODE)\n    pub abwidth: XspiWidth,\n    /// Alternate Bytes\n    pub alternate_bytes: Option<u32>,\n    /// Number of Alternate Bytes\n    pub absize: AddressSize,\n    /// Alternate Bytes Double Transfer rate enable\n    pub abdtr: bool,\n\n    /// Data width (DMODE)\n    pub dwidth: XspiWidth,\n    /// Data Double Transfer rate enable\n    pub ddtr: bool,\n\n    /// Number of dummy cycles (DCYC)\n    pub dummy: DummyCycles,\n\n    /// DQS Enable\n    pub dqse: bool,\n}\n\nimpl Default for TransferConfig {\n    fn default() -> Self {\n        Self {\n            iwidth: XspiWidth::NONE,\n            instruction: None,\n            isize: AddressSize::_8bit,\n            idtr: false,\n\n            adwidth: XspiWidth::NONE,\n            address: None,\n            adsize: AddressSize::_8bit,\n            addtr: false,\n\n            abwidth: XspiWidth::NONE,\n            alternate_bytes: None,\n            absize: AddressSize::_8bit,\n            abdtr: false,\n\n            dwidth: XspiWidth::NONE,\n            ddtr: false,\n\n            dummy: DummyCycles::_0,\n\n            dqse: false,\n        }\n    }\n}\n\n/// Error used for Xspi implementation\n#[derive(Debug)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub enum XspiError {\n    /// Peripheral configuration is invalid\n    InvalidConfiguration,\n    /// Operation configuration is invalid\n    InvalidCommand,\n    /// Size zero buffer passed to instruction\n    EmptyBuffer,\n}\n\n/// XSPI driver.\npub struct Xspi<'d, T: Instance, M: PeriMode> {\n    _peri: Peri<'d, T>,\n    _clk: Option<Flex<'d>>,\n    _d0: Option<Flex<'d>>,\n    _d1: Option<Flex<'d>>,\n    _d2: Option<Flex<'d>>,\n    _d3: Option<Flex<'d>>,\n    _d4: Option<Flex<'d>>,\n    _d5: Option<Flex<'d>>,\n    _d6: Option<Flex<'d>>,\n    _d7: Option<Flex<'d>>,\n    _d8: Option<Flex<'d>>,\n    _d9: Option<Flex<'d>>,\n    _d10: Option<Flex<'d>>,\n    _d11: Option<Flex<'d>>,\n    _d12: Option<Flex<'d>>,\n    _d13: Option<Flex<'d>>,\n    _d14: Option<Flex<'d>>,\n    _d15: Option<Flex<'d>>,\n    _ncs: Option<Flex<'d>>,\n    // TODO: allow switching between multiple chips\n    _ncs_alt: Option<Flex<'d>>,\n    // false if ncs == NCS1, true if ncs == NCS2\n    // (ncs_alt will be the opposite to ncs).\n    _cssel_swap: bool,\n    _dqs0: Option<Flex<'d>>,\n    _dqs1: Option<Flex<'d>>,\n    dma: Option<ChannelAndRequest<'d>>,\n    _phantom: PhantomData<M>,\n    config: Config,\n    width: XspiWidth,\n}\n\nimpl<'d, T: Instance, M: PeriMode> Xspi<'d, T, M> {\n    /// Enter memory mode.\n    /// The Input `read_config` is used to configure the read operation in memory mode\n    pub fn enable_memory_mapped_mode(\n        &mut self,\n        read_config: TransferConfig,\n        write_config: TransferConfig,\n    ) -> Result<(), XspiError> {\n        let reg = T::REGS;\n        while reg.sr().read().busy() {}\n\n        // Configure WRITE registers first (matching C HAL order)\n        // Use write() for clean initialization instead of modify()\n        reg.wccr().write(|w| {\n            w.set_imode(WccrImode::from_bits(write_config.iwidth.into()));\n            w.set_idtr(write_config.idtr);\n            w.set_isize(WccrIsize::from_bits(write_config.isize.into()));\n\n            w.set_admode(WccrAdmode::from_bits(write_config.adwidth.into()));\n            w.set_addtr(write_config.addtr);\n            w.set_adsize(WccrAdsize::from_bits(write_config.adsize.into()));\n\n            w.set_abmode(WccrAbmode::from_bits(write_config.abwidth.into()));\n            w.set_abdtr(write_config.abdtr);\n            w.set_absize(WccrAbsize::from_bits(write_config.absize.into()));\n\n            w.set_dmode(WccrDmode::from_bits(write_config.dwidth.into()));\n            w.set_ddtr(write_config.ddtr);\n\n            w.set_dqse(write_config.dqse);\n        });\n\n        reg.wtcr().write(|w| w.set_dcyc(write_config.dummy.into()));\n\n        // Set write instruction register (WIR) for memory-mapped write operations\n        if let Some(instruction) = write_config.instruction {\n            reg.wir().write(|w| w.set_instruction(instruction));\n        }\n\n        // Set write alternate bytes register (WABR) if needed\n        if let Some(ab) = write_config.alternate_bytes {\n            reg.wabr().write(|w| w.set_alternate(ab));\n        }\n\n        // Configure READ registers\n        self.configure_command(&read_config, None)?;\n\n        while reg.sr().read().busy() {}\n\n        // Set read DQS correctly - respect read_config.dqse, don't hardcode false!\n        reg.ccr().modify(|r| {\n            r.set_dqse(read_config.dqse);\n        });\n\n        // Enable memory mapped mode\n        reg.cr().modify(|r| {\n            r.set_fmode(Fmode::B_0X3);\n            r.set_tcen(false);\n        });\n        Ok(())\n    }\n\n    /// Quit from memory mapped mode\n    pub fn disable_memory_mapped_mode(&mut self) {\n        let reg = T::REGS;\n\n        reg.cr().modify(|r| {\n            r.set_fmode(Fmode::B_0X0);\n            r.set_abort(true);\n            r.set_dmaen(false);\n            r.set_en(false);\n        });\n\n        // Clear transfer complete flag\n        reg.fcr().write(|w| w.set_ctcf(true));\n\n        // Re-enable ospi\n        reg.cr().modify(|r| {\n            r.set_en(true);\n        });\n    }\n\n    /// Set clock prescaler dynamically after initialization.\n    ///\n    /// - 0 = bypass (maximum speed, XSPI clock = kernel clock)\n    /// - 1-255 = divide by (N+1)\n    ///\n    /// This can be called after init to change clock speed, matching ST HAL's\n    /// `HAL_XSPI_SetClockPrescaler()` behavior.\n    pub fn set_clock_prescaler(&mut self, prescaler: u8) {\n        // Disable XSPI before modifying prescaler\n        T::REGS.cr().modify(|w| w.set_en(false));\n        while T::REGS.sr().read().busy() {}\n\n        // Change prescaler\n        T::REGS.dcr2().modify(|w| w.set_prescaler(prescaler));\n\n        // Re-enable XSPI\n        T::REGS.cr().modify(|w| w.set_en(true));\n\n        // Update stored config\n        self.config.clock_prescaler = prescaler;\n    }\n\n    fn new_inner(\n        peri: Peri<'d, T>,\n        _d0: Option<Flex<'d>>,\n        _d1: Option<Flex<'d>>,\n        _d2: Option<Flex<'d>>,\n        _d3: Option<Flex<'d>>,\n        _d4: Option<Flex<'d>>,\n        _d5: Option<Flex<'d>>,\n        _d6: Option<Flex<'d>>,\n        _d7: Option<Flex<'d>>,\n        _d8: Option<Flex<'d>>,\n        _d9: Option<Flex<'d>>,\n        _d10: Option<Flex<'d>>,\n        _d11: Option<Flex<'d>>,\n        _d12: Option<Flex<'d>>,\n        _d13: Option<Flex<'d>>,\n        _d14: Option<Flex<'d>>,\n        _d15: Option<Flex<'d>>,\n        _clk: Option<Flex<'d>>,\n        ncs_cssel: u8,\n        _ncs: Option<Flex<'d>>,\n        _ncs_alt: Option<Flex<'d>>,\n        _dqs0: Option<Flex<'d>>,\n        _dqs1: Option<Flex<'d>>,\n        dma: Option<ChannelAndRequest<'d>>,\n        config: Config,\n        width: XspiWidth,\n        dual_quad: bool,\n    ) -> Self {\n        // Enable the interface (H7RS only - N6 doesn't have PWR XSPIM enable bits)\n        #[cfg(pwr_h7rs)]\n        match T::SPI_IDX {\n            1 => crate::pac::PWR.csr2().modify(|r| r.set_en_xspim1(true)),\n            2 => crate::pac::PWR.csr2().modify(|r| r.set_en_xspim2(true)),\n            _ => unreachable!(),\n        };\n\n        #[cfg(xspim_v1)]\n        {\n            debug!(\"XSPI init: enabling XSPIM clock\");\n            // RCC for xspim should be enabled before writing register\n            #[cfg(rcc_h7rs)]\n            crate::pac::RCC.ahb5enr().modify(|w| w.set_iomngren(true));\n            #[cfg(rcc_n6)]\n            crate::pac::RCC.ahb5enr().modify(|w| w.set_xspimen(true));\n\n            // N6: Enable XSPI peripheral clock FIRST (bus fault if accessed without clock)\n            #[cfg(rcc_n6)]\n            rcc::enable_and_reset::<T>();\n\n            // Disable XSPI peripheral\n            T::REGS.cr().modify(|w| {\n                w.set_en(false);\n            });\n\n            // Configure XSPI IO Manager\n            // Note: ncs_cssel indicates which NCS pin is being used (0=NCS0, 1=NCS1)\n            T::SPIM_REGS.cr().modify(|w| {\n                w.set_muxen(false);\n                w.set_req2ack_time(1); // Match ST HAL (was 0xff, which is only relevant when muxen=true)\n                // STM32N6: Enable chip select override (required for proper NCS routing)\n                #[cfg(stm32n6)]\n                w.set_cssel_ovr_en(true);\n                // Set override value based on pin configuration (0=NCS0, 1=NCS1)\n                // Each XSPI has its own override field in XSPIM\n                match T::SPI_IDX {\n                    1 => w.set_cssel_ovr_o1(ncs_cssel != 0),\n                    2 => w.set_cssel_ovr_o2(ncs_cssel != 0),\n                    _ => {} // XSPI3 not supported in non-multiplexed mode\n                }\n            });\n            debug!(\"XSPI init: XSPIM configured\");\n\n            // H7RS: Enable XSPI clock after initial config (original sequence)\n            #[cfg(rcc_h7rs)]\n            rcc::enable_and_reset::<T>();\n        }\n\n        #[cfg(not(xspim_v1))]\n        rcc::enable_and_reset::<T>();\n\n        while T::REGS.sr().read().busy() {}\n\n        // Device configuration\n        T::REGS.dcr1().modify(|w| {\n            w.set_devsize(config.device_size.into());\n            w.set_mtyp(Mtyp::from_bits(config.memory_type.into()));\n            w.set_csht(Csht::from_bits(config.chip_select_high_time.into()));\n            w.set_frck(false);\n            w.set_ckmode(Ckmode::from_bits(config.clock_mode.into()));\n        });\n\n        T::REGS.dcr2().modify(|w| {\n            w.set_wrapsize(Wrapsize::from_bits(config.wrap_size.into()));\n        });\n\n        T::REGS.dcr3().modify(|w| {\n            w.set_csbound(Csbound::from_bits(config.chip_select_boundary.into()));\n            #[cfg(xspi_v1)]\n            {\n                w.set_maxtran(Maxtran::from_bits(config.max_transfer.into()));\n            }\n        });\n\n        T::REGS.dcr4().modify(|w| {\n            w.set_refresh(Refresh::from_bits(config.refresh.into()));\n        });\n\n        T::REGS.cr().modify(|w| {\n            w.set_fthres(Fthres::from_bits(config.fifo_threshold.into()));\n        });\n\n        // Wait for busy flag to clear\n        while T::REGS.sr().read().busy() {}\n\n        T::REGS.dcr2().modify(|w| {\n            w.set_prescaler(config.clock_prescaler);\n        });\n\n        // Wait for busy flag to clear after changing prescaler, during calibration\n        while T::REGS.sr().read().busy() {}\n\n        T::REGS.cr().modify(|w| {\n            w.set_dmm(dual_quad);\n\n            assert!(_ncs_alt.is_none(), \"ncs_alt TODO\");\n            let cssel = if ncs_cssel == 0 { Cssel::B_0X0 } else { Cssel::B_0X1 };\n            w.set_cssel(cssel);\n        });\n\n        T::REGS.tcr().modify(|w| {\n            w.set_sshift(config.sample_shifting);\n            w.set_dhqc(config.delay_hold_quarter_cycle);\n        });\n\n        // Enable peripheral\n        T::REGS.cr().modify(|w| {\n            w.set_en(true);\n        });\n\n        // Free running clock needs to be set after peripheral enable\n        if config.free_running_clock {\n            T::REGS.dcr1().modify(|w| {\n                w.set_frck(config.free_running_clock);\n            });\n        }\n\n        Self {\n            _peri: peri,\n            _clk,\n            _d0,\n            _d1,\n            _d2,\n            _d3,\n            _d4,\n            _d5,\n            _d6,\n            _d7,\n            _d8,\n            _d9,\n            _d10,\n            _d11,\n            _d12,\n            _d13,\n            _d14,\n            _d15,\n            _ncs,\n            _ncs_alt,\n            _cssel_swap: ncs_cssel == 1,\n            _dqs0,\n            _dqs1,\n            dma,\n            _phantom: PhantomData,\n            config,\n            width,\n        }\n    }\n\n    // Function to configure the peripheral for the requested command\n    fn configure_command(&mut self, command: &TransferConfig, data_len: Option<usize>) -> Result<(), XspiError> {\n        // Check that transaction doesn't use more than hardware initialized pins\n        if <enums::XspiWidth as Into<u8>>::into(command.iwidth) > <enums::XspiWidth as Into<u8>>::into(self.width)\n            || <enums::XspiWidth as Into<u8>>::into(command.adwidth) > <enums::XspiWidth as Into<u8>>::into(self.width)\n            || <enums::XspiWidth as Into<u8>>::into(command.abwidth) > <enums::XspiWidth as Into<u8>>::into(self.width)\n            || <enums::XspiWidth as Into<u8>>::into(command.dwidth) > <enums::XspiWidth as Into<u8>>::into(self.width)\n        {\n            return Err(XspiError::InvalidCommand);\n        }\n\n        T::REGS\n            .cr()\n            .modify(|w| w.set_fmode(Fmode::from_bits(XspiMode::IndirectWrite.into())));\n\n        // Configure alternate bytes\n        if let Some(ab) = command.alternate_bytes {\n            T::REGS.abr().write(|v| v.set_alternate(ab));\n        } else {\n            T::REGS.ccr().modify(|w| {\n                // disable alternate bytes\n                w.set_abmode(CcrAbmode::B_0X0);\n            })\n        }\n\n        // Configure dummy cycles\n        T::REGS.tcr().modify(|w| {\n            w.set_dcyc(command.dummy.into());\n        });\n\n        // STM32N6: Deactivate sample shifting when data DTR mode is enabled\n        // This is required for proper DQS-based sampling in DTR mode (matches C HAL behavior\n        // in stm32n6xx_hal_xspi.c lines 3289-3292)\n        #[cfg(stm32n6)]\n        if command.ddtr {\n            T::REGS.tcr().modify(|w| {\n                w.set_sshift(false);\n            });\n        }\n\n        // Configure data\n        if let Some(data_length) = data_len {\n            T::REGS.dlr().write(|v| {\n                v.set_dl((data_length - 1) as u32);\n            });\n        } else {\n            T::REGS.dlr().write(|v| {\n                v.set_dl((0) as u32);\n            });\n        }\n\n        // Configure instruction/address/alternate bytes/data modes\n        T::REGS.ccr().modify(|w| {\n            w.set_imode(CcrImode::from_bits(command.iwidth.into()));\n            w.set_idtr(command.idtr);\n            w.set_isize(CcrIsize::from_bits(command.isize.into()));\n\n            w.set_admode(CcrAdmode::from_bits(command.adwidth.into()));\n            w.set_addtr(command.addtr);\n            w.set_adsize(CcrAdsize::from_bits(command.adsize.into()));\n\n            w.set_abmode(CcrAbmode::from_bits(command.abwidth.into()));\n            w.set_abdtr(command.abdtr);\n            w.set_absize(CcrAbsize::from_bits(command.absize.into()));\n\n            w.set_dmode(CcrDmode::from_bits(command.dwidth.into()));\n            w.set_ddtr(command.ddtr);\n            w.set_dqse(command.dqse);\n        });\n\n        // Set information required to initiate transaction\n        if let Some(instruction) = command.instruction {\n            if let Some(address) = command.address {\n                T::REGS.ir().write(|v| {\n                    v.set_instruction(instruction);\n                });\n\n                T::REGS.ar().write(|v| {\n                    v.set_address(address);\n                });\n            } else {\n                // STM32N6: When DHQC is enabled and instruction is in DTR mode with no data phase,\n                // DDTR must be enabled for proper timing (STM32N6 XSPI hardware requirement)\n                #[cfg(stm32n6)]\n                if data_len.is_none() && self.config.delay_hold_quarter_cycle && command.idtr {\n                    T::REGS.ccr().modify(|w| {\n                        w.set_ddtr(true);\n                    });\n                }\n\n                T::REGS.ir().write(|v| {\n                    v.set_instruction(instruction);\n                });\n            }\n        } else {\n            if let Some(address) = command.address {\n                T::REGS.ar().write(|v| {\n                    v.set_address(address);\n                });\n            } else {\n                // The only single phase transaction supported is instruction only\n                return Err(XspiError::InvalidCommand);\n            }\n        }\n\n        Ok(())\n    }\n\n    /// Function used to control or configure the target device without data transfer\n    pub fn blocking_command(&mut self, command: &TransferConfig) -> Result<(), XspiError> {\n        // Wait for peripheral to be free\n        while T::REGS.sr().read().busy() {}\n\n        // Need additional validation that command configuration doesn't have data set\n        self.configure_command(command, None)?;\n\n        // Transaction initiated by setting final configuration, i.e the instruction register\n        while !T::REGS.sr().read().tcf() {}\n        T::REGS.fcr().write(|w| {\n            w.set_ctcf(true);\n        });\n\n        Ok(())\n    }\n\n    /// Blocking read with byte by byte data transfer\n    pub fn blocking_read<W: Word>(&mut self, buf: &mut [W], transaction: TransferConfig) -> Result<(), XspiError> {\n        if buf.is_empty() {\n            return Err(XspiError::EmptyBuffer);\n        }\n\n        // Wait for peripheral to be free\n        while T::REGS.sr().read().busy() {}\n\n        // Ensure DMA is not enabled for this transaction\n        T::REGS.cr().modify(|w| {\n            w.set_dmaen(false);\n        });\n\n        let transfer_size_bytes = buf.len() * W::size().bytes();\n        self.configure_command(&transaction, Some(transfer_size_bytes))?;\n\n        let current_address = T::REGS.ar().read().address();\n        let current_instruction = T::REGS.ir().read().instruction();\n\n        // For a indirect read transaction, the transaction begins when the instruction/address is set\n        T::REGS\n            .cr()\n            .modify(|v| v.set_fmode(Fmode::from_bits(XspiMode::IndirectRead.into())));\n        if T::REGS.ccr().read().admode() == CcrAdmode::B_0X0 {\n            T::REGS.ir().write(|v| v.set_instruction(current_instruction));\n        } else {\n            T::REGS.ar().write(|v| v.set_address(current_address));\n        }\n\n        for idx in 0..buf.len() {\n            while !T::REGS.sr().read().tcf() && !T::REGS.sr().read().ftf() {}\n            buf[idx] = unsafe { (T::REGS.dr().as_ptr() as *mut W).read_volatile() };\n        }\n\n        while !T::REGS.sr().read().tcf() {}\n        T::REGS.fcr().write(|v| v.set_ctcf(true));\n\n        Ok(())\n    }\n\n    /// Blocking write with byte by byte data transfer\n    pub fn blocking_write<W: Word>(&mut self, buf: &[W], transaction: TransferConfig) -> Result<(), XspiError> {\n        if buf.is_empty() {\n            return Err(XspiError::EmptyBuffer);\n        }\n\n        // Wait for peripheral to be free\n        while T::REGS.sr().read().busy() {}\n\n        T::REGS.cr().modify(|w| {\n            w.set_dmaen(false);\n        });\n\n        let transfer_size_bytes = buf.len() * W::size().bytes();\n        self.configure_command(&transaction, Some(transfer_size_bytes))?;\n\n        T::REGS\n            .cr()\n            .modify(|v| v.set_fmode(Fmode::from_bits(XspiMode::IndirectWrite.into())));\n\n        for idx in 0..buf.len() {\n            while !T::REGS.sr().read().ftf() {}\n            unsafe { (T::REGS.dr().as_ptr() as *mut W).write_volatile(buf[idx]) };\n        }\n\n        while !T::REGS.sr().read().tcf() {}\n        T::REGS.fcr().write(|v| v.set_ctcf(true));\n\n        Ok(())\n    }\n\n    /// Set new bus configuration\n    pub fn set_config(&mut self, config: &Config) {\n        // Wait for busy flag to clear\n        while T::REGS.sr().read().busy() {}\n\n        // Disable DMA channel while configuring the peripheral\n        T::REGS.cr().modify(|w| {\n            w.set_dmaen(false);\n        });\n\n        // Device configuration\n        T::REGS.dcr1().modify(|w| {\n            w.set_devsize(config.device_size.into());\n            w.set_mtyp(Mtyp::from_bits(config.memory_type.into()));\n            w.set_csht(Csht::from_bits(config.chip_select_high_time.into()));\n            w.set_frck(false);\n            w.set_ckmode(match config.clock_mode {\n                true => Ckmode::B_0X1,\n                false => Ckmode::B_0X0,\n            });\n        });\n\n        T::REGS.dcr2().modify(|w| {\n            w.set_wrapsize(Wrapsize::from_bits(config.wrap_size.into()));\n        });\n\n        T::REGS.dcr3().modify(|w| {\n            w.set_csbound(Csbound::from_bits(config.chip_select_boundary.into()));\n            #[cfg(xspi_v1)]\n            {\n                w.set_maxtran(Maxtran::from_bits(config.max_transfer.into()));\n            }\n        });\n\n        T::REGS.dcr4().modify(|w| {\n            w.set_refresh(Refresh::from_bits(config.refresh.into()));\n        });\n\n        T::REGS.cr().modify(|w| {\n            w.set_fthres(Fthres::from_bits(config.fifo_threshold.into()));\n        });\n\n        // Wait for busy flag to clear\n        while T::REGS.sr().read().busy() {}\n\n        T::REGS.dcr2().modify(|w| {\n            w.set_prescaler(config.clock_prescaler);\n        });\n\n        T::REGS.tcr().modify(|w| {\n            w.set_sshift(config.sample_shifting);\n            w.set_dhqc(config.delay_hold_quarter_cycle);\n        });\n\n        // Enable peripheral\n        T::REGS.cr().modify(|w| {\n            w.set_en(true);\n        });\n\n        // Free running clock needs to be set after peripheral enable\n        if config.free_running_clock {\n            T::REGS.dcr1().modify(|w| {\n                w.set_frck(config.free_running_clock);\n            });\n        }\n\n        self.config = *config;\n    }\n\n    /// Get current configuration\n    pub fn get_config(&self) -> Config {\n        self.config\n    }\n}\n\nimpl<'d, T: Instance> Xspi<'d, T, Blocking> {\n    /// Create new blocking XSPI driver for a single spi external chip\n    pub fn new_blocking_singlespi(\n        peri: Peri<'d, T>,\n        clk: Peri<'d, impl CLKPin<T>>,\n        d0: Peri<'d, impl D0Pin<T>>,\n        d1: Peri<'d, impl D1Pin<T>>,\n        ncs: Peri<'d, impl NCSEither<T>>,\n        config: Config,\n    ) -> Self {\n        Self::new_inner(\n            peri,\n            new_pin!(d0, AfType::output(OutputType::PushPull, Speed::VeryHigh)),\n            new_pin!(d1, AfType::input(Pull::None)),\n            None,\n            None,\n            None,\n            None,\n            None,\n            None,\n            None,\n            None,\n            None,\n            None,\n            None,\n            None,\n            None,\n            None,\n            new_pin!(clk, AfType::output(OutputType::PushPull, Speed::VeryHigh)),\n            ncs.sel(),\n            new_pin!(\n                ncs,\n                AfType::output_pull(OutputType::PushPull, Speed::VeryHigh, Pull::Up)\n            ),\n            None,\n            None,\n            None,\n            None,\n            config,\n            XspiWidth::SING,\n            false,\n        )\n    }\n\n    /// Create new blocking XSPI driver for a dualspi external chip\n    pub fn new_blocking_dualspi(\n        peri: Peri<'d, T>,\n        clk: Peri<'d, impl CLKPin<T>>,\n        d0: Peri<'d, impl D0Pin<T>>,\n        d1: Peri<'d, impl D1Pin<T>>,\n        ncs: Peri<'d, impl NCSEither<T>>,\n        config: Config,\n    ) -> Self {\n        Self::new_inner(\n            peri,\n            new_pin!(d0, AfType::output(OutputType::PushPull, Speed::VeryHigh)),\n            new_pin!(d1, AfType::output(OutputType::PushPull, Speed::VeryHigh)),\n            None,\n            None,\n            None,\n            None,\n            None,\n            None,\n            None,\n            None,\n            None,\n            None,\n            None,\n            None,\n            None,\n            None,\n            new_pin!(clk, AfType::output(OutputType::PushPull, Speed::VeryHigh)),\n            ncs.sel(),\n            new_pin!(\n                ncs,\n                AfType::output_pull(OutputType::PushPull, Speed::VeryHigh, Pull::Up)\n            ),\n            None,\n            None,\n            None,\n            None,\n            config,\n            XspiWidth::DUAL,\n            false,\n        )\n    }\n\n    /// Create new blocking XSPI driver for a quadspi external chip\n    pub fn new_blocking_quadspi(\n        peri: Peri<'d, T>,\n        clk: Peri<'d, impl CLKPin<T>>,\n        d0: Peri<'d, impl D0Pin<T>>,\n        d1: Peri<'d, impl D1Pin<T>>,\n        d2: Peri<'d, impl D2Pin<T>>,\n        d3: Peri<'d, impl D3Pin<T>>,\n        ncs: Peri<'d, impl NCSEither<T>>,\n        config: Config,\n    ) -> Self {\n        Self::new_inner(\n            peri,\n            new_pin!(d0, AfType::output(OutputType::PushPull, Speed::VeryHigh)),\n            new_pin!(d1, AfType::output(OutputType::PushPull, Speed::VeryHigh)),\n            new_pin!(d2, AfType::output(OutputType::PushPull, Speed::VeryHigh)),\n            new_pin!(d3, AfType::output(OutputType::PushPull, Speed::VeryHigh)),\n            None,\n            None,\n            None,\n            None,\n            None,\n            None,\n            None,\n            None,\n            None,\n            None,\n            None,\n            None,\n            new_pin!(clk, AfType::output(OutputType::PushPull, Speed::VeryHigh)),\n            ncs.sel(),\n            new_pin!(\n                ncs,\n                AfType::output_pull(OutputType::PushPull, Speed::VeryHigh, Pull::Up)\n            ),\n            None,\n            None,\n            None,\n            None,\n            config,\n            XspiWidth::QUAD,\n            false,\n        )\n    }\n\n    /// Create new blocking XSPI driver for two quadspi external chips\n    pub fn new_blocking_dualquadspi(\n        peri: Peri<'d, T>,\n        clk: Peri<'d, impl CLKPin<T>>,\n        d0: Peri<'d, impl D0Pin<T>>,\n        d1: Peri<'d, impl D1Pin<T>>,\n        d2: Peri<'d, impl D2Pin<T>>,\n        d3: Peri<'d, impl D3Pin<T>>,\n        d4: Peri<'d, impl D4Pin<T>>,\n        d5: Peri<'d, impl D5Pin<T>>,\n        d6: Peri<'d, impl D6Pin<T>>,\n        d7: Peri<'d, impl D7Pin<T>>,\n        ncs: Peri<'d, impl NCSEither<T>>,\n        config: Config,\n    ) -> Self {\n        Self::new_inner(\n            peri,\n            new_pin!(d0, AfType::output(OutputType::PushPull, Speed::VeryHigh)),\n            new_pin!(d1, AfType::output(OutputType::PushPull, Speed::VeryHigh)),\n            new_pin!(d2, AfType::output(OutputType::PushPull, Speed::VeryHigh)),\n            new_pin!(d3, AfType::output(OutputType::PushPull, Speed::VeryHigh)),\n            new_pin!(d4, AfType::output(OutputType::PushPull, Speed::VeryHigh)),\n            new_pin!(d5, AfType::output(OutputType::PushPull, Speed::VeryHigh)),\n            new_pin!(d6, AfType::output(OutputType::PushPull, Speed::VeryHigh)),\n            new_pin!(d7, AfType::output(OutputType::PushPull, Speed::VeryHigh)),\n            None,\n            None,\n            None,\n            None,\n            None,\n            None,\n            None,\n            None,\n            new_pin!(clk, AfType::output(OutputType::PushPull, Speed::VeryHigh)),\n            ncs.sel(),\n            new_pin!(\n                ncs,\n                AfType::output_pull(OutputType::PushPull, Speed::VeryHigh, Pull::Up)\n            ),\n            None,\n            None,\n            None,\n            None,\n            config,\n            XspiWidth::QUAD,\n            true,\n        )\n    }\n\n    /// Create new blocking XSPI driver for xspi external chips\n    pub fn new_blocking_xspi(\n        peri: Peri<'d, T>,\n        clk: Peri<'d, impl CLKPin<T>>,\n        d0: Peri<'d, impl D0Pin<T>>,\n        d1: Peri<'d, impl D1Pin<T>>,\n        d2: Peri<'d, impl D2Pin<T>>,\n        d3: Peri<'d, impl D3Pin<T>>,\n        d4: Peri<'d, impl D4Pin<T>>,\n        d5: Peri<'d, impl D5Pin<T>>,\n        d6: Peri<'d, impl D6Pin<T>>,\n        d7: Peri<'d, impl D7Pin<T>>,\n        ncs: Peri<'d, impl NCSEither<T>>,\n        config: Config,\n    ) -> Self {\n        Self::new_inner(\n            peri,\n            new_pin!(d0, AfType::output(OutputType::PushPull, Speed::VeryHigh)),\n            new_pin!(d1, AfType::output(OutputType::PushPull, Speed::VeryHigh)),\n            new_pin!(d2, AfType::output(OutputType::PushPull, Speed::VeryHigh)),\n            new_pin!(d3, AfType::output(OutputType::PushPull, Speed::VeryHigh)),\n            new_pin!(d4, AfType::output(OutputType::PushPull, Speed::VeryHigh)),\n            new_pin!(d5, AfType::output(OutputType::PushPull, Speed::VeryHigh)),\n            new_pin!(d6, AfType::output(OutputType::PushPull, Speed::VeryHigh)),\n            new_pin!(d7, AfType::output(OutputType::PushPull, Speed::VeryHigh)),\n            None,\n            None,\n            None,\n            None,\n            None,\n            None,\n            None,\n            None,\n            new_pin!(clk, AfType::output(OutputType::PushPull, Speed::VeryHigh)),\n            ncs.sel(),\n            new_pin!(\n                ncs,\n                AfType::output_pull(OutputType::PushPull, Speed::VeryHigh, Pull::Up)\n            ),\n            None,\n            None,\n            None,\n            None,\n            config,\n            XspiWidth::OCTO,\n            false,\n        )\n    }\n\n    /// Create new blocking XSPI driver for octo-spi with DQS pin support.\n    /// Required for high-speed DTR mode operation (>145 MHz).\n    pub fn new_blocking_xspi_dqs(\n        peri: Peri<'d, T>,\n        clk: Peri<'d, impl CLKPin<T>>,\n        d0: Peri<'d, impl D0Pin<T>>,\n        d1: Peri<'d, impl D1Pin<T>>,\n        d2: Peri<'d, impl D2Pin<T>>,\n        d3: Peri<'d, impl D3Pin<T>>,\n        d4: Peri<'d, impl D4Pin<T>>,\n        d5: Peri<'d, impl D5Pin<T>>,\n        d6: Peri<'d, impl D6Pin<T>>,\n        d7: Peri<'d, impl D7Pin<T>>,\n        ncs: Peri<'d, impl NCSEither<T>>,\n        dqs0: Peri<'d, impl DQS0Pin<T>>,\n        config: Config,\n    ) -> Self {\n        Self::new_inner(\n            peri,\n            new_pin!(d0, AfType::output(OutputType::PushPull, Speed::VeryHigh)),\n            new_pin!(d1, AfType::output(OutputType::PushPull, Speed::VeryHigh)),\n            new_pin!(d2, AfType::output(OutputType::PushPull, Speed::VeryHigh)),\n            new_pin!(d3, AfType::output(OutputType::PushPull, Speed::VeryHigh)),\n            new_pin!(d4, AfType::output(OutputType::PushPull, Speed::VeryHigh)),\n            new_pin!(d5, AfType::output(OutputType::PushPull, Speed::VeryHigh)),\n            new_pin!(d6, AfType::output(OutputType::PushPull, Speed::VeryHigh)),\n            new_pin!(d7, AfType::output(OutputType::PushPull, Speed::VeryHigh)),\n            None,\n            None,\n            None,\n            None,\n            None,\n            None,\n            None,\n            None,\n            new_pin!(clk, AfType::output(OutputType::PushPull, Speed::VeryHigh)),\n            ncs.sel(),\n            new_pin!(\n                ncs,\n                AfType::output_pull(OutputType::PushPull, Speed::VeryHigh, Pull::Up)\n            ),\n            None,\n            new_pin!(dqs0, AfType::input(Pull::None)),\n            None,\n            None,\n            config,\n            XspiWidth::OCTO,\n            false,\n        )\n    }\n\n    /// Create new blocking XSPI driver for 16-bit hexadeca-spi external chips\n    pub fn new_blocking_xspi_hexa(\n        peri: Peri<'d, T>,\n        clk: Peri<'d, impl CLKPin<T>>,\n        d0: Peri<'d, impl D0Pin<T>>,\n        d1: Peri<'d, impl D1Pin<T>>,\n        d2: Peri<'d, impl D2Pin<T>>,\n        d3: Peri<'d, impl D3Pin<T>>,\n        d4: Peri<'d, impl D4Pin<T>>,\n        d5: Peri<'d, impl D5Pin<T>>,\n        d6: Peri<'d, impl D6Pin<T>>,\n        d7: Peri<'d, impl D7Pin<T>>,\n        d8: Peri<'d, impl D8Pin<T>>,\n        d9: Peri<'d, impl D9Pin<T>>,\n        d10: Peri<'d, impl D10Pin<T>>,\n        d11: Peri<'d, impl D11Pin<T>>,\n        d12: Peri<'d, impl D12Pin<T>>,\n        d13: Peri<'d, impl D13Pin<T>>,\n        d14: Peri<'d, impl D14Pin<T>>,\n        d15: Peri<'d, impl D15Pin<T>>,\n        ncs: Peri<'d, impl NCSEither<T>>,\n        config: Config,\n    ) -> Self {\n        Self::new_inner(\n            peri,\n            new_pin!(d0, AfType::output(OutputType::PushPull, Speed::VeryHigh)),\n            new_pin!(d1, AfType::output(OutputType::PushPull, Speed::VeryHigh)),\n            new_pin!(d2, AfType::output(OutputType::PushPull, Speed::VeryHigh)),\n            new_pin!(d3, AfType::output(OutputType::PushPull, Speed::VeryHigh)),\n            new_pin!(d4, AfType::output(OutputType::PushPull, Speed::VeryHigh)),\n            new_pin!(d5, AfType::output(OutputType::PushPull, Speed::VeryHigh)),\n            new_pin!(d6, AfType::output(OutputType::PushPull, Speed::VeryHigh)),\n            new_pin!(d7, AfType::output(OutputType::PushPull, Speed::VeryHigh)),\n            new_pin!(d8, AfType::output(OutputType::PushPull, Speed::VeryHigh)),\n            new_pin!(d9, AfType::output(OutputType::PushPull, Speed::VeryHigh)),\n            new_pin!(d10, AfType::output(OutputType::PushPull, Speed::VeryHigh)),\n            new_pin!(d11, AfType::output(OutputType::PushPull, Speed::VeryHigh)),\n            new_pin!(d12, AfType::output(OutputType::PushPull, Speed::VeryHigh)),\n            new_pin!(d13, AfType::output(OutputType::PushPull, Speed::VeryHigh)),\n            new_pin!(d14, AfType::output(OutputType::PushPull, Speed::VeryHigh)),\n            new_pin!(d15, AfType::output(OutputType::PushPull, Speed::VeryHigh)),\n            new_pin!(clk, AfType::output(OutputType::PushPull, Speed::VeryHigh)),\n            ncs.sel(),\n            new_pin!(\n                ncs,\n                AfType::output_pull(OutputType::PushPull, Speed::VeryHigh, Pull::Up)\n            ),\n            None,\n            None,\n            None,\n            None,\n            config,\n            XspiWidth::HEXA,\n            false,\n        )\n    }\n\n    /// Create new blocking XSPI driver for a Hexadeca-SPI external chip with DQS pin\n    /// Required for Xccela protocol devices (like APS256XXN PSRAM) that use DTR mode\n    #[cfg(xspim_v1)]\n    pub fn new_blocking_xspi_hexa_dqs(\n        peri: Peri<'d, T>,\n        clk: Peri<'d, impl CLKPin<T>>,\n        d0: Peri<'d, impl D0Pin<T>>,\n        d1: Peri<'d, impl D1Pin<T>>,\n        d2: Peri<'d, impl D2Pin<T>>,\n        d3: Peri<'d, impl D3Pin<T>>,\n        d4: Peri<'d, impl D4Pin<T>>,\n        d5: Peri<'d, impl D5Pin<T>>,\n        d6: Peri<'d, impl D6Pin<T>>,\n        d7: Peri<'d, impl D7Pin<T>>,\n        d8: Peri<'d, impl D8Pin<T>>,\n        d9: Peri<'d, impl D9Pin<T>>,\n        d10: Peri<'d, impl D10Pin<T>>,\n        d11: Peri<'d, impl D11Pin<T>>,\n        d12: Peri<'d, impl D12Pin<T>>,\n        d13: Peri<'d, impl D13Pin<T>>,\n        d14: Peri<'d, impl D14Pin<T>>,\n        d15: Peri<'d, impl D15Pin<T>>,\n        ncs: Peri<'d, impl NCSEither<T>>,\n        dqs0: Peri<'d, impl DQS0Pin<T>>,\n        config: Config,\n    ) -> Self {\n        debug!(\"XSPI hexa_dqs: starting, about to configure pins\");\n        Self::new_inner(\n            peri,\n            new_pin!(d0, AfType::output(OutputType::PushPull, Speed::VeryHigh)),\n            new_pin!(d1, AfType::output(OutputType::PushPull, Speed::VeryHigh)),\n            new_pin!(d2, AfType::output(OutputType::PushPull, Speed::VeryHigh)),\n            new_pin!(d3, AfType::output(OutputType::PushPull, Speed::VeryHigh)),\n            new_pin!(d4, AfType::output(OutputType::PushPull, Speed::VeryHigh)),\n            new_pin!(d5, AfType::output(OutputType::PushPull, Speed::VeryHigh)),\n            new_pin!(d6, AfType::output(OutputType::PushPull, Speed::VeryHigh)),\n            new_pin!(d7, AfType::output(OutputType::PushPull, Speed::VeryHigh)),\n            new_pin!(d8, AfType::output(OutputType::PushPull, Speed::VeryHigh)),\n            new_pin!(d9, AfType::output(OutputType::PushPull, Speed::VeryHigh)),\n            new_pin!(d10, AfType::output(OutputType::PushPull, Speed::VeryHigh)),\n            new_pin!(d11, AfType::output(OutputType::PushPull, Speed::VeryHigh)),\n            new_pin!(d12, AfType::output(OutputType::PushPull, Speed::VeryHigh)),\n            new_pin!(d13, AfType::output(OutputType::PushPull, Speed::VeryHigh)),\n            new_pin!(d14, AfType::output(OutputType::PushPull, Speed::VeryHigh)),\n            new_pin!(d15, AfType::output(OutputType::PushPull, Speed::VeryHigh)),\n            new_pin!(clk, AfType::output(OutputType::PushPull, Speed::VeryHigh)),\n            ncs.sel(),\n            new_pin!(\n                ncs,\n                AfType::output_pull(OutputType::PushPull, Speed::VeryHigh, Pull::Up)\n            ),\n            None,\n            new_pin!(dqs0, AfType::input(Pull::None)),\n            None,\n            None,\n            config,\n            XspiWidth::HEXA,\n            false,\n        )\n    }\n\n    /// Create new blocking XSPI driver for Hexadeca-SPI with dual DQS pins\n    /// Required for APS256XX PSRAM on STM32N6570-DK which uses both DQS0 and DQS1\n    #[cfg(xspim_v1)]\n    pub fn new_blocking_xspi_hexa_dqs_dual(\n        peri: Peri<'d, T>,\n        clk: Peri<'d, impl CLKPin<T>>,\n        d0: Peri<'d, impl D0Pin<T>>,\n        d1: Peri<'d, impl D1Pin<T>>,\n        d2: Peri<'d, impl D2Pin<T>>,\n        d3: Peri<'d, impl D3Pin<T>>,\n        d4: Peri<'d, impl D4Pin<T>>,\n        d5: Peri<'d, impl D5Pin<T>>,\n        d6: Peri<'d, impl D6Pin<T>>,\n        d7: Peri<'d, impl D7Pin<T>>,\n        d8: Peri<'d, impl D8Pin<T>>,\n        d9: Peri<'d, impl D9Pin<T>>,\n        d10: Peri<'d, impl D10Pin<T>>,\n        d11: Peri<'d, impl D11Pin<T>>,\n        d12: Peri<'d, impl D12Pin<T>>,\n        d13: Peri<'d, impl D13Pin<T>>,\n        d14: Peri<'d, impl D14Pin<T>>,\n        d15: Peri<'d, impl D15Pin<T>>,\n        ncs: Peri<'d, impl NCSEither<T>>,\n        dqs0: Peri<'d, impl DQS0Pin<T>>,\n        dqs1: Peri<'d, impl DQS1Pin<T>>,\n        config: Config,\n    ) -> Self {\n        debug!(\"XSPI hexa_dqs_dual: starting, about to configure pins\");\n        Self::new_inner(\n            peri,\n            new_pin!(d0, AfType::output(OutputType::PushPull, Speed::VeryHigh)),\n            new_pin!(d1, AfType::output(OutputType::PushPull, Speed::VeryHigh)),\n            new_pin!(d2, AfType::output(OutputType::PushPull, Speed::VeryHigh)),\n            new_pin!(d3, AfType::output(OutputType::PushPull, Speed::VeryHigh)),\n            new_pin!(d4, AfType::output(OutputType::PushPull, Speed::VeryHigh)),\n            new_pin!(d5, AfType::output(OutputType::PushPull, Speed::VeryHigh)),\n            new_pin!(d6, AfType::output(OutputType::PushPull, Speed::VeryHigh)),\n            new_pin!(d7, AfType::output(OutputType::PushPull, Speed::VeryHigh)),\n            new_pin!(d8, AfType::output(OutputType::PushPull, Speed::VeryHigh)),\n            new_pin!(d9, AfType::output(OutputType::PushPull, Speed::VeryHigh)),\n            new_pin!(d10, AfType::output(OutputType::PushPull, Speed::VeryHigh)),\n            new_pin!(d11, AfType::output(OutputType::PushPull, Speed::VeryHigh)),\n            new_pin!(d12, AfType::output(OutputType::PushPull, Speed::VeryHigh)),\n            new_pin!(d13, AfType::output(OutputType::PushPull, Speed::VeryHigh)),\n            new_pin!(d14, AfType::output(OutputType::PushPull, Speed::VeryHigh)),\n            new_pin!(d15, AfType::output(OutputType::PushPull, Speed::VeryHigh)),\n            new_pin!(clk, AfType::output(OutputType::PushPull, Speed::VeryHigh)),\n            ncs.sel(),\n            new_pin!(\n                ncs,\n                AfType::output_pull(OutputType::PushPull, Speed::VeryHigh, Pull::Up)\n            ),\n            None,\n            new_pin!(dqs0, AfType::input(Pull::None)),\n            new_pin!(dqs1, AfType::input(Pull::None)),\n            None,\n            config,\n            XspiWidth::HEXA,\n            false,\n        )\n    }\n}\n\nimpl<'d, T: Instance> Xspi<'d, T, Async> {\n    /// Create new async XSPI driver for a single spi external chip\n    pub fn new_singlespi<D: XDma<T>>(\n        peri: Peri<'d, T>,\n        clk: Peri<'d, impl CLKPin<T>>,\n        d0: Peri<'d, impl D0Pin<T>>,\n        d1: Peri<'d, impl D1Pin<T>>,\n        ncs: Peri<'d, impl NCSEither<T>>,\n        dma: Peri<'d, D>,\n        _irq: impl crate::interrupt::typelevel::Binding<D::Interrupt, crate::dma::InterruptHandler<D>> + 'd,\n        config: Config,\n    ) -> Self {\n        Self::new_inner(\n            peri,\n            new_pin!(d0, AfType::output(OutputType::PushPull, Speed::VeryHigh)),\n            new_pin!(d1, AfType::input(Pull::None)),\n            None,\n            None,\n            None,\n            None,\n            None,\n            None,\n            None,\n            None,\n            None,\n            None,\n            None,\n            None,\n            None,\n            None,\n            new_pin!(clk, AfType::output(OutputType::PushPull, Speed::VeryHigh)),\n            ncs.sel(),\n            new_pin!(\n                ncs,\n                AfType::output_pull(OutputType::PushPull, Speed::VeryHigh, Pull::Up)\n            ),\n            None,\n            None,\n            None,\n            new_dma!(dma, _irq),\n            config,\n            XspiWidth::SING,\n            false,\n        )\n    }\n\n    /// Create new async XSPI driver for a dualspi external chip\n    pub fn new_dualspi<D: XDma<T>>(\n        peri: Peri<'d, T>,\n        clk: Peri<'d, impl CLKPin<T>>,\n        d0: Peri<'d, impl D0Pin<T>>,\n        d1: Peri<'d, impl D1Pin<T>>,\n        ncs: Peri<'d, impl NCSEither<T>>,\n        dma: Peri<'d, D>,\n        _irq: impl crate::interrupt::typelevel::Binding<D::Interrupt, crate::dma::InterruptHandler<D>> + 'd,\n        config: Config,\n    ) -> Self {\n        Self::new_inner(\n            peri,\n            new_pin!(d0, AfType::output(OutputType::PushPull, Speed::VeryHigh)),\n            new_pin!(d1, AfType::output(OutputType::PushPull, Speed::VeryHigh)),\n            None,\n            None,\n            None,\n            None,\n            None,\n            None,\n            None,\n            None,\n            None,\n            None,\n            None,\n            None,\n            None,\n            None,\n            new_pin!(clk, AfType::output(OutputType::PushPull, Speed::VeryHigh)),\n            ncs.sel(),\n            new_pin!(\n                ncs,\n                AfType::output_pull(OutputType::PushPull, Speed::VeryHigh, Pull::Up)\n            ),\n            None,\n            None,\n            None,\n            new_dma!(dma, _irq),\n            config,\n            XspiWidth::DUAL,\n            false,\n        )\n    }\n\n    /// Create new async XSPI driver for a quadspi external chip\n    pub fn new_quadspi<D: XDma<T>>(\n        peri: Peri<'d, T>,\n        clk: Peri<'d, impl CLKPin<T>>,\n        d0: Peri<'d, impl D0Pin<T>>,\n        d1: Peri<'d, impl D1Pin<T>>,\n        d2: Peri<'d, impl D2Pin<T>>,\n        d3: Peri<'d, impl D3Pin<T>>,\n        ncs: Peri<'d, impl NCSEither<T>>,\n        dma: Peri<'d, D>,\n        _irq: impl crate::interrupt::typelevel::Binding<D::Interrupt, crate::dma::InterruptHandler<D>> + 'd,\n        config: Config,\n    ) -> Self {\n        Self::new_inner(\n            peri,\n            new_pin!(d0, AfType::output(OutputType::PushPull, Speed::VeryHigh)),\n            new_pin!(d1, AfType::output(OutputType::PushPull, Speed::VeryHigh)),\n            new_pin!(d2, AfType::output(OutputType::PushPull, Speed::VeryHigh)),\n            new_pin!(d3, AfType::output(OutputType::PushPull, Speed::VeryHigh)),\n            None,\n            None,\n            None,\n            None,\n            None,\n            None,\n            None,\n            None,\n            None,\n            None,\n            None,\n            None,\n            new_pin!(clk, AfType::output(OutputType::PushPull, Speed::VeryHigh)),\n            ncs.sel(),\n            new_pin!(\n                ncs,\n                AfType::output_pull(OutputType::PushPull, Speed::VeryHigh, Pull::Up)\n            ),\n            None,\n            None,\n            None,\n            new_dma!(dma, _irq),\n            config,\n            XspiWidth::QUAD,\n            false,\n        )\n    }\n\n    /// Create new async XSPI driver for two quadspi external chips\n    pub fn new_dualquadspi<D: XDma<T>>(\n        peri: Peri<'d, T>,\n        clk: Peri<'d, impl CLKPin<T>>,\n        d0: Peri<'d, impl D0Pin<T>>,\n        d1: Peri<'d, impl D1Pin<T>>,\n        d2: Peri<'d, impl D2Pin<T>>,\n        d3: Peri<'d, impl D3Pin<T>>,\n        d4: Peri<'d, impl D4Pin<T>>,\n        d5: Peri<'d, impl D5Pin<T>>,\n        d6: Peri<'d, impl D6Pin<T>>,\n        d7: Peri<'d, impl D7Pin<T>>,\n        ncs: Peri<'d, impl NCSEither<T>>,\n        dma: Peri<'d, D>,\n        _irq: impl crate::interrupt::typelevel::Binding<D::Interrupt, crate::dma::InterruptHandler<D>> + 'd,\n        config: Config,\n    ) -> Self {\n        Self::new_inner(\n            peri,\n            new_pin!(d0, AfType::output(OutputType::PushPull, Speed::VeryHigh)),\n            new_pin!(d1, AfType::output(OutputType::PushPull, Speed::VeryHigh)),\n            new_pin!(d2, AfType::output(OutputType::PushPull, Speed::VeryHigh)),\n            new_pin!(d3, AfType::output(OutputType::PushPull, Speed::VeryHigh)),\n            new_pin!(d4, AfType::output(OutputType::PushPull, Speed::VeryHigh)),\n            new_pin!(d5, AfType::output(OutputType::PushPull, Speed::VeryHigh)),\n            new_pin!(d6, AfType::output(OutputType::PushPull, Speed::VeryHigh)),\n            new_pin!(d7, AfType::output(OutputType::PushPull, Speed::VeryHigh)),\n            None,\n            None,\n            None,\n            None,\n            None,\n            None,\n            None,\n            None,\n            new_pin!(clk, AfType::output(OutputType::PushPull, Speed::VeryHigh)),\n            ncs.sel(),\n            new_pin!(\n                ncs,\n                AfType::output_pull(OutputType::PushPull, Speed::VeryHigh, Pull::Up)\n            ),\n            None,\n            None,\n            None,\n            new_dma!(dma, _irq),\n            config,\n            XspiWidth::QUAD,\n            true,\n        )\n    }\n\n    /// Create new async XSPI driver for xspi external chips\n    pub fn new_xspi<D: XDma<T>>(\n        peri: Peri<'d, T>,\n        clk: Peri<'d, impl CLKPin<T>>,\n        d0: Peri<'d, impl D0Pin<T>>,\n        d1: Peri<'d, impl D1Pin<T>>,\n        d2: Peri<'d, impl D2Pin<T>>,\n        d3: Peri<'d, impl D3Pin<T>>,\n        d4: Peri<'d, impl D4Pin<T>>,\n        d5: Peri<'d, impl D5Pin<T>>,\n        d6: Peri<'d, impl D6Pin<T>>,\n        d7: Peri<'d, impl D7Pin<T>>,\n        ncs: Peri<'d, impl NCSEither<T>>,\n        dma: Peri<'d, D>,\n        _irq: impl crate::interrupt::typelevel::Binding<D::Interrupt, crate::dma::InterruptHandler<D>> + 'd,\n        config: Config,\n    ) -> Self {\n        Self::new_inner(\n            peri,\n            new_pin!(d0, AfType::output(OutputType::PushPull, Speed::VeryHigh)),\n            new_pin!(d1, AfType::output(OutputType::PushPull, Speed::VeryHigh)),\n            new_pin!(d2, AfType::output(OutputType::PushPull, Speed::VeryHigh)),\n            new_pin!(d3, AfType::output(OutputType::PushPull, Speed::VeryHigh)),\n            new_pin!(d4, AfType::output(OutputType::PushPull, Speed::VeryHigh)),\n            new_pin!(d5, AfType::output(OutputType::PushPull, Speed::VeryHigh)),\n            new_pin!(d6, AfType::output(OutputType::PushPull, Speed::VeryHigh)),\n            new_pin!(d7, AfType::output(OutputType::PushPull, Speed::VeryHigh)),\n            None,\n            None,\n            None,\n            None,\n            None,\n            None,\n            None,\n            None,\n            new_pin!(clk, AfType::output(OutputType::PushPull, Speed::VeryHigh)),\n            ncs.sel(),\n            new_pin!(\n                ncs,\n                AfType::output_pull(OutputType::PushPull, Speed::VeryHigh, Pull::Up)\n            ),\n            None,\n            None,\n            None,\n            new_dma!(dma, _irq),\n            config,\n            XspiWidth::OCTO,\n            false,\n        )\n    }\n\n    /// Create new async XSPI driver for octo-spi with DQS pin support.\n    /// Required for high-speed DTR mode operation (>145 MHz).\n    pub fn new_xspi_dqs<D: XDma<T>>(\n        peri: Peri<'d, T>,\n        clk: Peri<'d, impl CLKPin<T>>,\n        d0: Peri<'d, impl D0Pin<T>>,\n        d1: Peri<'d, impl D1Pin<T>>,\n        d2: Peri<'d, impl D2Pin<T>>,\n        d3: Peri<'d, impl D3Pin<T>>,\n        d4: Peri<'d, impl D4Pin<T>>,\n        d5: Peri<'d, impl D5Pin<T>>,\n        d6: Peri<'d, impl D6Pin<T>>,\n        d7: Peri<'d, impl D7Pin<T>>,\n        ncs: Peri<'d, impl NCSEither<T>>,\n        dqs0: Peri<'d, impl DQS0Pin<T>>,\n        dma: Peri<'d, D>,\n        _irq: impl crate::interrupt::typelevel::Binding<D::Interrupt, crate::dma::InterruptHandler<D>> + 'd,\n        config: Config,\n    ) -> Self {\n        Self::new_inner(\n            peri,\n            new_pin!(d0, AfType::output(OutputType::PushPull, Speed::VeryHigh)),\n            new_pin!(d1, AfType::output(OutputType::PushPull, Speed::VeryHigh)),\n            new_pin!(d2, AfType::output(OutputType::PushPull, Speed::VeryHigh)),\n            new_pin!(d3, AfType::output(OutputType::PushPull, Speed::VeryHigh)),\n            new_pin!(d4, AfType::output(OutputType::PushPull, Speed::VeryHigh)),\n            new_pin!(d5, AfType::output(OutputType::PushPull, Speed::VeryHigh)),\n            new_pin!(d6, AfType::output(OutputType::PushPull, Speed::VeryHigh)),\n            new_pin!(d7, AfType::output(OutputType::PushPull, Speed::VeryHigh)),\n            None,\n            None,\n            None,\n            None,\n            None,\n            None,\n            None,\n            None,\n            new_pin!(clk, AfType::output(OutputType::PushPull, Speed::VeryHigh)),\n            ncs.sel(),\n            new_pin!(\n                ncs,\n                AfType::output_pull(OutputType::PushPull, Speed::VeryHigh, Pull::Up)\n            ),\n            None,\n            new_pin!(dqs0, AfType::input(Pull::None)),\n            None,\n            new_dma!(dma, _irq),\n            config,\n            XspiWidth::OCTO,\n            false,\n        )\n    }\n\n    /// Create new async XSPI driver for 16-bit hexadeca-spi external chips\n    pub fn new_xspi_hexa<D: XDma<T>>(\n        peri: Peri<'d, T>,\n        clk: Peri<'d, impl CLKPin<T>>,\n        d0: Peri<'d, impl D0Pin<T>>,\n        d1: Peri<'d, impl D1Pin<T>>,\n        d2: Peri<'d, impl D2Pin<T>>,\n        d3: Peri<'d, impl D3Pin<T>>,\n        d4: Peri<'d, impl D4Pin<T>>,\n        d5: Peri<'d, impl D5Pin<T>>,\n        d6: Peri<'d, impl D6Pin<T>>,\n        d7: Peri<'d, impl D7Pin<T>>,\n        d8: Peri<'d, impl D8Pin<T>>,\n        d9: Peri<'d, impl D9Pin<T>>,\n        d10: Peri<'d, impl D10Pin<T>>,\n        d11: Peri<'d, impl D11Pin<T>>,\n        d12: Peri<'d, impl D12Pin<T>>,\n        d13: Peri<'d, impl D13Pin<T>>,\n        d14: Peri<'d, impl D14Pin<T>>,\n        d15: Peri<'d, impl D15Pin<T>>,\n        ncs: Peri<'d, impl NCSEither<T>>,\n        dma: Peri<'d, D>,\n        _irq: impl crate::interrupt::typelevel::Binding<D::Interrupt, crate::dma::InterruptHandler<D>> + 'd,\n        config: Config,\n    ) -> Self {\n        Self::new_inner(\n            peri,\n            new_pin!(d0, AfType::output(OutputType::PushPull, Speed::VeryHigh)),\n            new_pin!(d1, AfType::output(OutputType::PushPull, Speed::VeryHigh)),\n            new_pin!(d2, AfType::output(OutputType::PushPull, Speed::VeryHigh)),\n            new_pin!(d3, AfType::output(OutputType::PushPull, Speed::VeryHigh)),\n            new_pin!(d4, AfType::output(OutputType::PushPull, Speed::VeryHigh)),\n            new_pin!(d5, AfType::output(OutputType::PushPull, Speed::VeryHigh)),\n            new_pin!(d6, AfType::output(OutputType::PushPull, Speed::VeryHigh)),\n            new_pin!(d7, AfType::output(OutputType::PushPull, Speed::VeryHigh)),\n            new_pin!(d8, AfType::output(OutputType::PushPull, Speed::VeryHigh)),\n            new_pin!(d9, AfType::output(OutputType::PushPull, Speed::VeryHigh)),\n            new_pin!(d10, AfType::output(OutputType::PushPull, Speed::VeryHigh)),\n            new_pin!(d11, AfType::output(OutputType::PushPull, Speed::VeryHigh)),\n            new_pin!(d12, AfType::output(OutputType::PushPull, Speed::VeryHigh)),\n            new_pin!(d13, AfType::output(OutputType::PushPull, Speed::VeryHigh)),\n            new_pin!(d14, AfType::output(OutputType::PushPull, Speed::VeryHigh)),\n            new_pin!(d15, AfType::output(OutputType::PushPull, Speed::VeryHigh)),\n            new_pin!(clk, AfType::output(OutputType::PushPull, Speed::VeryHigh)),\n            ncs.sel(),\n            new_pin!(\n                ncs,\n                AfType::output_pull(OutputType::PushPull, Speed::VeryHigh, Pull::Up)\n            ),\n            None,\n            None,\n            None,\n            new_dma!(dma, _irq),\n            config,\n            XspiWidth::HEXA,\n            false,\n        )\n    }\n\n    /// Create new async XSPI driver for hexadeca-spi with DQS pin support\n    /// Required for Xccela protocol devices (like APS256XXN PSRAM) that use DTR mode\n    #[cfg(xspim_v1)]\n    pub fn new_xspi_hexa_dqs<D: XDma<T>>(\n        peri: Peri<'d, T>,\n        clk: Peri<'d, impl CLKPin<T>>,\n        d0: Peri<'d, impl D0Pin<T>>,\n        d1: Peri<'d, impl D1Pin<T>>,\n        d2: Peri<'d, impl D2Pin<T>>,\n        d3: Peri<'d, impl D3Pin<T>>,\n        d4: Peri<'d, impl D4Pin<T>>,\n        d5: Peri<'d, impl D5Pin<T>>,\n        d6: Peri<'d, impl D6Pin<T>>,\n        d7: Peri<'d, impl D7Pin<T>>,\n        d8: Peri<'d, impl D8Pin<T>>,\n        d9: Peri<'d, impl D9Pin<T>>,\n        d10: Peri<'d, impl D10Pin<T>>,\n        d11: Peri<'d, impl D11Pin<T>>,\n        d12: Peri<'d, impl D12Pin<T>>,\n        d13: Peri<'d, impl D13Pin<T>>,\n        d14: Peri<'d, impl D14Pin<T>>,\n        d15: Peri<'d, impl D15Pin<T>>,\n        ncs: Peri<'d, impl NCSEither<T>>,\n        dqs0: Peri<'d, impl DQS0Pin<T>>,\n        dma: Peri<'d, D>,\n        _irq: impl crate::interrupt::typelevel::Binding<D::Interrupt, crate::dma::InterruptHandler<D>> + 'd,\n        config: Config,\n    ) -> Self {\n        Self::new_inner(\n            peri,\n            new_pin!(d0, AfType::output(OutputType::PushPull, Speed::VeryHigh)),\n            new_pin!(d1, AfType::output(OutputType::PushPull, Speed::VeryHigh)),\n            new_pin!(d2, AfType::output(OutputType::PushPull, Speed::VeryHigh)),\n            new_pin!(d3, AfType::output(OutputType::PushPull, Speed::VeryHigh)),\n            new_pin!(d4, AfType::output(OutputType::PushPull, Speed::VeryHigh)),\n            new_pin!(d5, AfType::output(OutputType::PushPull, Speed::VeryHigh)),\n            new_pin!(d6, AfType::output(OutputType::PushPull, Speed::VeryHigh)),\n            new_pin!(d7, AfType::output(OutputType::PushPull, Speed::VeryHigh)),\n            new_pin!(d8, AfType::output(OutputType::PushPull, Speed::VeryHigh)),\n            new_pin!(d9, AfType::output(OutputType::PushPull, Speed::VeryHigh)),\n            new_pin!(d10, AfType::output(OutputType::PushPull, Speed::VeryHigh)),\n            new_pin!(d11, AfType::output(OutputType::PushPull, Speed::VeryHigh)),\n            new_pin!(d12, AfType::output(OutputType::PushPull, Speed::VeryHigh)),\n            new_pin!(d13, AfType::output(OutputType::PushPull, Speed::VeryHigh)),\n            new_pin!(d14, AfType::output(OutputType::PushPull, Speed::VeryHigh)),\n            new_pin!(d15, AfType::output(OutputType::PushPull, Speed::VeryHigh)),\n            new_pin!(clk, AfType::output(OutputType::PushPull, Speed::VeryHigh)),\n            ncs.sel(),\n            new_pin!(\n                ncs,\n                AfType::output_pull(OutputType::PushPull, Speed::VeryHigh, Pull::Up)\n            ),\n            None,\n            new_pin!(dqs0, AfType::input(Pull::None)),\n            None,\n            new_dma!(dma, _irq),\n            config,\n            XspiWidth::HEXA,\n            false,\n        )\n    }\n\n    /// Create new async XSPI driver for Hexadeca-SPI with dual DQS pins\n    /// Required for APS256XX PSRAM on STM32N6570-DK which uses both DQS0 and DQS1\n    #[cfg(xspim_v1)]\n    pub fn new_xspi_hexa_dqs_dual<D: XDma<T>>(\n        peri: Peri<'d, T>,\n        clk: Peri<'d, impl CLKPin<T>>,\n        d0: Peri<'d, impl D0Pin<T>>,\n        d1: Peri<'d, impl D1Pin<T>>,\n        d2: Peri<'d, impl D2Pin<T>>,\n        d3: Peri<'d, impl D3Pin<T>>,\n        d4: Peri<'d, impl D4Pin<T>>,\n        d5: Peri<'d, impl D5Pin<T>>,\n        d6: Peri<'d, impl D6Pin<T>>,\n        d7: Peri<'d, impl D7Pin<T>>,\n        d8: Peri<'d, impl D8Pin<T>>,\n        d9: Peri<'d, impl D9Pin<T>>,\n        d10: Peri<'d, impl D10Pin<T>>,\n        d11: Peri<'d, impl D11Pin<T>>,\n        d12: Peri<'d, impl D12Pin<T>>,\n        d13: Peri<'d, impl D13Pin<T>>,\n        d14: Peri<'d, impl D14Pin<T>>,\n        d15: Peri<'d, impl D15Pin<T>>,\n        ncs: Peri<'d, impl NCSEither<T>>,\n        dqs0: Peri<'d, impl DQS0Pin<T>>,\n        dqs1: Peri<'d, impl DQS1Pin<T>>,\n        dma: Peri<'d, D>,\n        _irq: impl crate::interrupt::typelevel::Binding<D::Interrupt, crate::dma::InterruptHandler<D>> + 'd,\n        config: Config,\n    ) -> Self {\n        Self::new_inner(\n            peri,\n            new_pin!(d0, AfType::output(OutputType::PushPull, Speed::VeryHigh)),\n            new_pin!(d1, AfType::output(OutputType::PushPull, Speed::VeryHigh)),\n            new_pin!(d2, AfType::output(OutputType::PushPull, Speed::VeryHigh)),\n            new_pin!(d3, AfType::output(OutputType::PushPull, Speed::VeryHigh)),\n            new_pin!(d4, AfType::output(OutputType::PushPull, Speed::VeryHigh)),\n            new_pin!(d5, AfType::output(OutputType::PushPull, Speed::VeryHigh)),\n            new_pin!(d6, AfType::output(OutputType::PushPull, Speed::VeryHigh)),\n            new_pin!(d7, AfType::output(OutputType::PushPull, Speed::VeryHigh)),\n            new_pin!(d8, AfType::output(OutputType::PushPull, Speed::VeryHigh)),\n            new_pin!(d9, AfType::output(OutputType::PushPull, Speed::VeryHigh)),\n            new_pin!(d10, AfType::output(OutputType::PushPull, Speed::VeryHigh)),\n            new_pin!(d11, AfType::output(OutputType::PushPull, Speed::VeryHigh)),\n            new_pin!(d12, AfType::output(OutputType::PushPull, Speed::VeryHigh)),\n            new_pin!(d13, AfType::output(OutputType::PushPull, Speed::VeryHigh)),\n            new_pin!(d14, AfType::output(OutputType::PushPull, Speed::VeryHigh)),\n            new_pin!(d15, AfType::output(OutputType::PushPull, Speed::VeryHigh)),\n            new_pin!(clk, AfType::output(OutputType::PushPull, Speed::VeryHigh)),\n            ncs.sel(),\n            new_pin!(\n                ncs,\n                AfType::output_pull(OutputType::PushPull, Speed::VeryHigh, Pull::Up)\n            ),\n            None,\n            new_pin!(dqs0, AfType::input(Pull::None)),\n            new_pin!(dqs1, AfType::input(Pull::None)),\n            new_dma!(dma, _irq),\n            config,\n            XspiWidth::HEXA,\n            false,\n        )\n    }\n\n    /// Blocking read with DMA transfer\n    pub fn blocking_read_dma<W: Word>(&mut self, buf: &mut [W], transaction: TransferConfig) -> Result<(), XspiError> {\n        if buf.is_empty() {\n            return Err(XspiError::EmptyBuffer);\n        }\n\n        // Wait for peripheral to be free\n        while T::REGS.sr().read().busy() {}\n\n        let transfer_size_bytes = buf.len() * W::size().bytes();\n        self.configure_command(&transaction, Some(transfer_size_bytes))?;\n\n        let current_address = T::REGS.ar().read().address();\n        let current_instruction = T::REGS.ir().read().instruction();\n\n        // For a indirect read transaction, the transaction begins when the instruction/address is set\n        T::REGS\n            .cr()\n            .modify(|v| v.set_fmode(Fmode::from_bits(XspiMode::IndirectRead.into())));\n        if T::REGS.ccr().read().admode() == CcrAdmode::B_0X0 {\n            T::REGS.ir().write(|v| v.set_instruction(current_instruction));\n        } else {\n            T::REGS.ar().write(|v| v.set_address(current_address));\n        }\n\n        for chunk in buf.chunks_mut(0xFFFF / W::size().bytes()) {\n            let transfer = unsafe {\n                self.dma\n                    .as_mut()\n                    .unwrap()\n                    .read(T::REGS.dr().as_ptr() as *mut W, chunk, Default::default())\n            };\n\n            T::REGS.cr().modify(|w| w.set_dmaen(true));\n\n            transfer.blocking_wait();\n        }\n\n        finish_dma(T::REGS);\n\n        Ok(())\n    }\n\n    /// Blocking write with DMA transfer\n    pub fn blocking_write_dma<W: Word>(&mut self, buf: &[W], transaction: TransferConfig) -> Result<(), XspiError> {\n        if buf.is_empty() {\n            return Err(XspiError::EmptyBuffer);\n        }\n\n        // Wait for peripheral to be free\n        while T::REGS.sr().read().busy() {}\n\n        let transfer_size_bytes = buf.len() * W::size().bytes();\n        self.configure_command(&transaction, Some(transfer_size_bytes))?;\n        T::REGS\n            .cr()\n            .modify(|v| v.set_fmode(Fmode::from_bits(XspiMode::IndirectWrite.into())));\n\n        for chunk in buf.chunks(0xFFFF / W::size().bytes()) {\n            let transfer = unsafe {\n                self.dma\n                    .as_mut()\n                    .unwrap()\n                    .write(chunk, T::REGS.dr().as_ptr() as *mut W, Default::default())\n            };\n\n            T::REGS.cr().modify(|w| w.set_dmaen(true));\n\n            transfer.blocking_wait();\n        }\n\n        finish_dma(T::REGS);\n\n        Ok(())\n    }\n\n    /// Asynchronous read from external device\n    pub async fn read<W: Word>(&mut self, buf: &mut [W], transaction: TransferConfig) -> Result<(), XspiError> {\n        if buf.is_empty() {\n            return Err(XspiError::EmptyBuffer);\n        }\n\n        // Wait for peripheral to be free\n        while T::REGS.sr().read().busy() {}\n\n        let transfer_size_bytes = buf.len() * W::size().bytes();\n        self.configure_command(&transaction, Some(transfer_size_bytes))?;\n\n        let current_address = T::REGS.ar().read().address();\n        let current_instruction = T::REGS.ir().read().instruction();\n\n        // For a indirect read transaction, the transaction begins when the instruction/address is set\n        T::REGS\n            .cr()\n            .modify(|v| v.set_fmode(Fmode::from_bits(XspiMode::IndirectRead.into())));\n        if T::REGS.ccr().read().admode() == CcrAdmode::B_0X0 {\n            T::REGS.ir().write(|v| v.set_instruction(current_instruction));\n        } else {\n            T::REGS.ar().write(|v| v.set_address(current_address));\n        }\n\n        for chunk in buf.chunks_mut(0xFFFF / W::size().bytes()) {\n            let transfer = unsafe {\n                self.dma\n                    .as_mut()\n                    .unwrap()\n                    .read(T::REGS.dr().as_ptr() as *mut W, chunk, Default::default())\n            };\n\n            T::REGS.cr().modify(|w| w.set_dmaen(true));\n\n            transfer.await;\n        }\n\n        finish_dma(T::REGS);\n\n        Ok(())\n    }\n\n    /// Asynchronous write to external device\n    pub async fn write<W: Word>(&mut self, buf: &[W], transaction: TransferConfig) -> Result<(), XspiError> {\n        if buf.is_empty() {\n            return Err(XspiError::EmptyBuffer);\n        }\n\n        // Wait for peripheral to be free\n        while T::REGS.sr().read().busy() {}\n\n        let transfer_size_bytes = buf.len() * W::size().bytes();\n        self.configure_command(&transaction, Some(transfer_size_bytes))?;\n        T::REGS\n            .cr()\n            .modify(|v| v.set_fmode(Fmode::from_bits(XspiMode::IndirectWrite.into())));\n\n        // TODO: implement this using a LinkedList DMA to offload the whole transfer off the CPU.\n        for chunk in buf.chunks(0xFFFF / W::size().bytes()) {\n            let transfer = unsafe {\n                self.dma\n                    .as_mut()\n                    .unwrap()\n                    .write(chunk, T::REGS.dr().as_ptr() as *mut W, Default::default())\n            };\n\n            T::REGS.cr().modify(|w| w.set_dmaen(true));\n\n            transfer.await;\n        }\n\n        finish_dma(T::REGS);\n\n        Ok(())\n    }\n}\n\nimpl<'d, T: Instance, M: PeriMode> Drop for Xspi<'d, T, M> {\n    fn drop(&mut self) {\n        rcc::disable::<T>();\n    }\n}\n\nfn finish_dma(regs: Regs) {\n    while !regs.sr().read().tcf() {}\n    regs.fcr().write(|v| v.set_ctcf(true));\n\n    regs.cr().modify(|w| {\n        w.set_dmaen(false);\n    });\n}\n\n/// XSPI I/O manager instance trait.\n#[cfg(xspim_v1)]\npub(crate) trait SealedXspimInstance {\n    const SPIM_REGS: Xspim;\n    const SPI_IDX: u8;\n}\n\n/// XSPI instance trait.\npub(crate) trait SealedInstance {\n    const REGS: Regs;\n}\n\n/// XSPI instance trait.\n#[cfg(xspim_v1)]\n#[allow(private_bounds)]\npub trait Instance: SealedInstance + PeripheralType + RccPeripheral + SealedXspimInstance {}\n\n/// XSPI instance trait.\n#[cfg(not(xspim_v1))]\n#[allow(private_bounds)]\npub trait Instance: SealedInstance + PeripheralType + RccPeripheral {}\n\npin_trait!(D0Pin, Instance);\npin_trait!(D1Pin, Instance);\npin_trait!(D2Pin, Instance);\npin_trait!(D3Pin, Instance);\npin_trait!(D4Pin, Instance);\npin_trait!(D5Pin, Instance);\npin_trait!(D6Pin, Instance);\npin_trait!(D7Pin, Instance);\npin_trait!(D8Pin, Instance);\npin_trait!(D9Pin, Instance);\npin_trait!(D10Pin, Instance);\npin_trait!(D11Pin, Instance);\npin_trait!(D12Pin, Instance);\npin_trait!(D13Pin, Instance);\npin_trait!(D14Pin, Instance);\npin_trait!(D15Pin, Instance);\npin_trait!(DQS0Pin, Instance);\npin_trait!(DQS1Pin, Instance);\npin_trait!(NCSPin, Instance);\npin_trait!(CLKPin, Instance);\npin_trait!(NCLKPin, Instance);\ndma_trait!(XDma, Instance);\n\n/// Trait for either NCS1 or NCS2 pins\npub trait NCSEither<T: Instance>: NCSPin<T> {\n    /// Get the CSSEL for this NCS pin\n    fn sel(&self) -> u8;\n}\n\n// Hard-coded the xspi index, for SPIM\n#[cfg(xspim_v1)]\nimpl SealedXspimInstance for peripherals::XSPI1 {\n    const SPIM_REGS: Xspim = crate::pac::XSPIM;\n    const SPI_IDX: u8 = 1;\n}\n\n// Some cubedb files are missing XSPI2, for example STM32H7R3Z8\n#[cfg(all(xspim_v1, peri_xspi2))]\nimpl SealedXspimInstance for peripherals::XSPI2 {\n    const SPIM_REGS: Xspim = crate::pac::XSPIM;\n    const SPI_IDX: u8 = 2;\n}\n\n// XSPI3 only exists on N6, available only in XSPIM multiplexed mode (MUXEN=1)\n// Currently not supported - would require multiplexed mode implementation\n#[cfg(all(xspim_v1, stm32n6))]\nimpl SealedXspimInstance for peripherals::XSPI3 {\n    const SPIM_REGS: Xspim = crate::pac::XSPIM;\n    const SPI_IDX: u8 = 3;\n}\n\n#[cfg(xspim_v1)]\nforeach_peripheral!(\n    (xspi, $inst:ident) => {\n        impl SealedInstance for peripherals::$inst {\n            const REGS: Regs = crate::pac::$inst;\n        }\n\n        impl Instance for peripherals::$inst {}\n    };\n);\n\n#[cfg(not(xspim_v1))]\nforeach_peripheral!(\n    (xspi, $inst:ident) => {\n        impl SealedInstance for peripherals::$inst {\n            const REGS: Regs = crate::pac::$inst;\n        }\n\n        impl Instance for peripherals::$inst {}\n    };\n);\n\nimpl<'d, T: Instance, M: PeriMode> SetConfig for Xspi<'d, T, M> {\n    type Config = Config;\n    type ConfigError = ();\n    fn set_config(&mut self, config: &Self::Config) -> Result<(), ()> {\n        self.set_config(config);\n        Ok(())\n    }\n}\n\nimpl<'d, T: Instance, M: PeriMode> GetConfig for Xspi<'d, T, M> {\n    type Config = Config;\n    fn get_config(&self) -> Self::Config {\n        self.get_config()\n    }\n}\n\n/// Word sizes usable for XSPI.\n#[allow(private_bounds)]\npub trait Word: word::Word {}\n\nmacro_rules! impl_word {\n    ($T:ty) => {\n        impl Word for $T {}\n    };\n}\n\nimpl_word!(u8);\nimpl_word!(u16);\nimpl_word!(u32);\n"
  },
  {
    "path": "embassy-stm32-wpan/CHANGELOG.md",
    "content": "# Changelog\n\nAll notable changes to this project will be documented in this file.\n\nThe format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),\nand this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).\n\n<!-- next-header -->\n## Unreleased - ReleaseDate\n\n- refactor into wb55 crate and add feature for wba\n- wpan: restructure hil and test wpan mac\n- restructure to allow embassy net driver to work.\n- First release with changelog.\n"
  },
  {
    "path": "embassy-stm32-wpan/Cargo.toml",
    "content": "[package]\nname = \"embassy-stm32-wpan\"\nversion = \"0.1.0\"\nedition = \"2024\"\nlicense = \"MIT OR Apache-2.0\"\ndescription = \"Async STM32 WPAN stack for embedded devices in Rust.\"\nkeywords = [\"embedded\", \"async\", \"stm32\", \"ble\", \"wpan\"]\ncategories = [\"embedded\", \"hardware-support\", \"no-std\", \"asynchronous\"]\nrepository = \"https://github.com/embassy-rs/embassy\"\ndocumentation = \"https://docs.embassy.dev/embassy-stm32-wpan\"\n\npublish = false\n\n[package.metadata.embassy]\nbuild = [\n    {target = \"thumbv7em-none-eabi\", features = [\"stm32wb55rg\"]},\n]\n\n[package.metadata.embassy_docs]\nsrc_base = \"https://github.com/embassy-rs/embassy/blob/embassy-stm32-wpan-v$VERSION/embassy-stm32-wpan/src/\"\nsrc_base_git = \"https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-stm32-wpan/src/\"\ntarget = \"thumbv7em-none-eabihf\"\nfeatures = [\"stm32wb55rg\", \"wb55_ble\", \"wb55_mac\"]\n\n[package.metadata.docs.rs]\nfeatures = [\"stm32wb55rg\", \"wb55_ble\", \"wb55_mac\"]\n\n[dependencies]\nembassy-stm32 = { version = \"0.6.0\", path = \"../embassy-stm32\" , features = [\"unstable-pac\"]}\nembassy-sync = { version = \"0.8.0\", path = \"../embassy-sync\" }\nembassy-time = { version = \"0.5.1\", path = \"../embassy-time\", optional = true }\nembassy-futures = { version = \"0.1.2\", path = \"../embassy-futures\" }\nembassy-hal-internal = { version = \"0.5.0\", path = \"../embassy-hal-internal\" }\nembassy-embedded-hal = { version = \"0.6.0\", path = \"../embassy-embedded-hal\" }\nembassy-net-driver = { version = \"0.2.0\", path = \"../embassy-net-driver\", optional=true }\nsmoltcp = { version = \"0.13.0\", optional=true, default-features = false }\nstm32-bindings = { version = \"0.1.4\", optional=true, default-features = false }\n\ndefmt = { version = \"1.0.1\", optional = true }\nlog = { version = \"0.4.17\", optional = true }\n\ncortex-m = \"0.7.6\"\nheapless = { version = \"0.9\", features = [\"defmt\"] }\naligned = \"0.4.3\"\ncritical-section = \"1.2\"\n\nbit_field = \"0.10.2\"\nstm32-device-signature = { version = \"0.3.3\", features = [\"stm32wb5x\"] }\nstm32wb-hci = { version = \"0.17.3\", optional = true }\nfutures-util = { version = \"0.3.30\", default-features = false }\nbitflags = { version = \"2.3.3\", optional = true }\n\n[features]\ndefmt = [\"dep:defmt\", \"embassy-sync/defmt\", \"embassy-embedded-hal/defmt\", \"embassy-hal-internal/defmt\", \"stm32wb-hci?/defmt\"]\n\nwb55 = []\nwb55_ble = [\"dep:stm32wb-hci\"]\nwb55_mac = [\"dep:bitflags\", \"dep:embassy-net-driver\", \"dep:smoltcp\", \"smoltcp/medium-ieee802154\"]\n\nwba = [ \"dep:stm32-bindings\", \"dep:embassy-time\" ]\nwba_ble = [ \"stm32-bindings/wba_wpan_ble\" , \"stm32-bindings/wba_wpan\", \"stm32-bindings/wba_wpan_mac\" ]\nwba_mac = [ \"stm32-bindings/wba_wpan_mac\", \"stm32-bindings/wba_wpan_ble\" , \"stm32-bindings/lib_wba5_linklayer15_4\", \"stm32-bindings/lib_wba_mac_lib\" , \"stm32-bindings/wba_wpan\" ]\n\n# BLE stack library variants (choose one)\nble-stack-basic = [ \"stm32-bindings/lib_stm32wba_ble_stack_basic\" ]\nble-stack-basic-plus = [ \"stm32-bindings/lib_stm32wba_ble_stack_basic_plus\" ]\nble-stack-full = [ \"stm32-bindings/lib_stm32wba_ble_stack_full\" ]\nble-stack-po = [ \"stm32-bindings/lib_stm32wba_ble_stack_po\" ]\nble-stack-llo = [ \"stm32-bindings/lib_stm32wba_ble_stack_llo\" ]\n\n# Link layer library variants for WBA5x (WBA50, WBA52, WBA54, WBA55)\nlinklayer-ble-basic = [ \"stm32-bindings/lib_wba5_linklayer_ble_basic_lib\" ]\nlinklayer-ble-basic-plus = [ \"stm32-bindings/lib_wba5_linklayer_ble_basic_plus_lib\" ]\nlinklayer-ble-full = [ \"stm32-bindings/lib_wba5_linklayer_ble_full_lib\" ]\nlinklayer-ble-peripheral-only = [ \"stm32-bindings/lib_wba5_linklayer_ble_peripheral_only_lib\" ]\n\n# Link layer library variants for WBA6x (WBA62)\nlinklayer6-ble-basic = [ \"stm32-bindings/lib_wba6_linklayer_ble_basic_lib\" ]\nlinklayer6-ble-basic-plus = [ \"stm32-bindings/lib_wba6_linklayer_ble_basic_plus_lib\" ]\nlinklayer6-ble-full = [ \"stm32-bindings/lib_wba6_linklayer_ble_full_lib\" ]\nlinklayer6-ble-peripheral-only = [ \"stm32-bindings/lib_wba6_linklayer_ble_peripheral_only_lib\" ]\n\nextended = []\n\nstm32wb10cc = [ \"embassy-stm32/stm32wb10cc\" , \"wb55\" ]\nstm32wb15cc = [ \"embassy-stm32/stm32wb15cc\" , \"wb55\" ]\nstm32wb30ce = [ \"embassy-stm32/stm32wb30ce\" , \"wb55\" ]\nstm32wb35cc = [ \"embassy-stm32/stm32wb35cc\" , \"wb55\" ]\nstm32wb35ce = [ \"embassy-stm32/stm32wb35ce\" , \"wb55\" ]\nstm32wb50cg = [ \"embassy-stm32/stm32wb50cg\" , \"wb55\" ]\nstm32wb55cc = [ \"embassy-stm32/stm32wb55cc\" , \"wb55\" ]\nstm32wb55ce = [ \"embassy-stm32/stm32wb55ce\" , \"wb55\" ]\nstm32wb55cg = [ \"embassy-stm32/stm32wb55cg\" , \"wb55\" ]\nstm32wb55rc = [ \"embassy-stm32/stm32wb55rc\" , \"wb55\" ]\nstm32wb55re = [ \"embassy-stm32/stm32wb55re\" , \"wb55\" ]\nstm32wb55rg = [ \"embassy-stm32/stm32wb55rg\" , \"wb55\" ]\nstm32wb55vc = [ \"embassy-stm32/stm32wb55vc\" , \"wb55\" ]\nstm32wb55ve = [ \"embassy-stm32/stm32wb55ve\" , \"wb55\" ]\nstm32wb55vg = [ \"embassy-stm32/stm32wb55vg\" , \"wb55\" ]\nstm32wb55vy = [ \"embassy-stm32/stm32wb55vy\" , \"wb55\" ]\nstm32wba50ke = [ \"embassy-stm32/stm32wba50ke\", \"wba\" ]\nstm32wba50kg = [ \"embassy-stm32/stm32wba50kg\", \"wba\" ]\nstm32wba52ce = [ \"embassy-stm32/stm32wba52ce\", \"wba\" ]\nstm32wba52cg = [ \"embassy-stm32/stm32wba52cg\", \"wba\" ]\nstm32wba52ke = [ \"embassy-stm32/stm32wba52ke\", \"wba\" ]\nstm32wba52kg = [ \"embassy-stm32/stm32wba52kg\", \"wba\" ]\nstm32wba54ce = [ \"embassy-stm32/stm32wba54ce\", \"wba\" ]\nstm32wba54cg = [ \"embassy-stm32/stm32wba54cg\", \"wba\" ]\nstm32wba54ke = [ \"embassy-stm32/stm32wba54ke\", \"wba\" ]\nstm32wba54kg = [ \"embassy-stm32/stm32wba54kg\", \"wba\" ]\nstm32wba55ce = [ \"embassy-stm32/stm32wba55ce\", \"wba\" ]\nstm32wba55cg = [ \"embassy-stm32/stm32wba55cg\", \"wba\" ]\nstm32wba55he = [ \"embassy-stm32/stm32wba55he\", \"wba\" ]\nstm32wba55hg = [ \"embassy-stm32/stm32wba55hg\", \"wba\" ]\nstm32wba55ue = [ \"embassy-stm32/stm32wba55ue\", \"wba\" ]\nstm32wba55ug = [ \"embassy-stm32/stm32wba55ug\", \"wba\" ]\nstm32wba62cg = [ \"embassy-stm32/stm32wba62cg\", \"wba\" ]\nstm32wba62ci = [ \"embassy-stm32/stm32wba62ci\", \"wba\" ]\nstm32wba62mg = [ \"embassy-stm32/stm32wba62mg\", \"wba\" ]\nstm32wba62mi = [ \"embassy-stm32/stm32wba62mi\", \"wba\" ]\nstm32wba62pg = [ \"embassy-stm32/stm32wba62pg\", \"wba\" ]\nstm32wba62pi = [ \"embassy-stm32/stm32wba62pi\", \"wba\" ]\nstm32wba63cg = [ \"embassy-stm32/stm32wba63cg\", \"wba\" ]\nstm32wba63ci = [ \"embassy-stm32/stm32wba63ci\", \"wba\" ]\nstm32wba64cg = [ \"embassy-stm32/stm32wba64cg\", \"wba\" ]\nstm32wba64ci = [ \"embassy-stm32/stm32wba64ci\", \"wba\" ]\nstm32wba65cg = [ \"embassy-stm32/stm32wba65cg\", \"wba\" ]\nstm32wba65ci = [ \"embassy-stm32/stm32wba65ci\", \"wba\" ]\nstm32wba65mg = [ \"embassy-stm32/stm32wba65mg\", \"wba\" ]\nstm32wba65mi = [ \"embassy-stm32/stm32wba65mi\", \"wba\" ]\nstm32wba65pg = [ \"embassy-stm32/stm32wba65pg\", \"wba\" ]\nstm32wba65pi = [ \"embassy-stm32/stm32wba65pi\", \"wba\" ]\nstm32wba65rg = [ \"embassy-stm32/stm32wba65rg\", \"wba\" ]\nstm32wba65ri = [ \"embassy-stm32/stm32wba65ri\", \"wba\" ]\n"
  },
  {
    "path": "embassy-stm32-wpan/README.md",
    "content": "# embassy-stm32-wpan\n\nAsync WPAN (short range wireless) on STM32WB families.\n\n## Features\n\n- Rust interface to the WPAN stack running on the STM32WB co-processor .\n- Controller trait implementation for the [stm32wb-hci](https://crates.io/crates/stm32wb-hci) crate.\n- Embassy-net driver implementation for 802.15.4 MAC.\n\n## Examples\n\nSee the [stm32wb examples](https://github.com/embassy-rs/embassy/tree/main/examples/stm32wb).\n"
  },
  {
    "path": "embassy-stm32-wpan/build.rs",
    "content": "use std::path::PathBuf;\nuse std::{env, fs};\n\nfn main() {\n    match env::vars()\n        .map(|(a, _)| a)\n        .filter(|x| x.starts_with(\"CARGO_FEATURE_STM32\"))\n        .get_one()\n    {\n        Ok(_) => {}\n        Err(GetOneError::None) => panic!(\"No stm32xx Cargo feature enabled\"),\n        Err(GetOneError::Multiple) => panic!(\"Multiple stm32xx Cargo features enabled\"),\n    }\n\n    let out_dir = &PathBuf::from(env::var_os(\"OUT_DIR\").unwrap());\n\n    // ========\n    // stm32wb tl_mbox link sections\n\n    let out_file = out_dir.join(\"tl_mbox.x\").to_string_lossy().to_string();\n    let in_file;\n    if env::var_os(\"CARGO_FEATURE_EXTENDED\").is_some() {\n        if env::vars()\n            .map(|(a, _)| a)\n            .any(|x| x.starts_with(\"CARGO_FEATURE_STM32WB1\"))\n        {\n            in_file = \"tl_mbox_extended_wb1.x.in\";\n        } else {\n            in_file = \"tl_mbox_extended_wbx5.x.in\";\n        }\n    } else {\n        in_file = \"tl_mbox.x.in\";\n    }\n    fs::write(out_file, fs::read_to_string(in_file).unwrap()).unwrap();\n    println!(\"cargo:rustc-link-search={}\", out_dir.display());\n    println!(\"cargo:rerun-if-changed={}\", in_file);\n}\n\nenum GetOneError {\n    None,\n    Multiple,\n}\n\ntrait IteratorExt: Iterator {\n    fn get_one(self) -> Result<Self::Item, GetOneError>;\n}\n\nimpl<T: Iterator> IteratorExt for T {\n    fn get_one(mut self) -> Result<Self::Item, GetOneError> {\n        match self.next() {\n            None => Err(GetOneError::None),\n            Some(res) => match self.next() {\n                Some(_) => Err(GetOneError::Multiple),\n                None => Ok(res),\n            },\n        }\n    }\n}\n"
  },
  {
    "path": "embassy-stm32-wpan/src/lib.rs",
    "content": "//! The embassy-stm32-wpan crate aims to provide safe use of the commands necessary to interface\n//! with the Cortex C0 CPU2 coprocessor of STM32WB microcontrollers. It implements safe wrappers\n//! around the Transport Layer, and in particular the system, memory, BLE and Mac channels.\n//!\n//! # Design\n//!\n//! This crate loosely follows the Application Note 5289 \"How to build wireless applications with\n//! STM32WB MCUs\"; several of the startup procedures laid out in Annex 14.1 are implemented using\n//! inline copies of the code contained within the `stm32wb_copro` C library.\n//!\n//! BLE commands are implemented via use of the [stm32wb_hci] crate, for which the\n//! [stm32wb_hci::Controller] trait has been implemented.\n\n#![no_std]\n#![allow(async_fn_in_trait)]\n#![allow(unsafe_op_in_unsafe_fn)]\n#![doc = include_str!(\"../README.md\")]\n// #![warn(missing_docs)]\n#![allow(static_mut_refs)] // TODO: Fix\n\n#[cfg(feature = \"wb55\")]\nmod wb55;\n\n#[cfg(feature = \"wb55\")]\npub use wb55::*;\n\n#[cfg(feature = \"wba\")]\nmod wba;\n\n#[cfg(feature = \"wba\")]\npub use wba::*;\n"
  },
  {
    "path": "embassy-stm32-wpan/src/wb55/channels.rs",
    "content": "//! CPU1                                             CPU2\n//!  |             (SYSTEM)                            |\n//!  |----HW_IPCC_SYSTEM_CMD_RSP_CHANNEL-------------->|\n//!  |                                                 |\n//!  |<---HW_IPCC_SYSTEM_EVENT_CHANNEL-----------------|\n//!  |                                                 |\n//!  |            (ZIGBEE)                             |\n//!  |----HW_IPCC_ZIGBEE_CMD_APPLI_CHANNEL------------>|\n//!  |                                                 |\n//!  |----HW_IPCC_ZIGBEE_CMD_CLI_CHANNEL-------------->|\n//!  |                                                 |\n//!  |<---HW_IPCC_ZIGBEE_APPLI_NOTIF_ACK_CHANNEL-------|\n//!  |                                                 |\n//!  |<---HW_IPCC_ZIGBEE_CLI_NOTIF_ACK_CHANNEL---------|\n//!  |                                                 |\n//!  |             (THREAD)                            |\n//!  |----HW_IPCC_THREAD_OT_CMD_RSP_CHANNEL----------->|\n//!  |                                                 |\n//!  |----HW_IPCC_THREAD_CLI_CMD_CHANNEL-------------->|\n//!  |                                                 |\n//!  |<---HW_IPCC_THREAD_NOTIFICATION_ACK_CHANNEL------|\n//!  |                                                 |\n//!  |<---HW_IPCC_THREAD_CLI_NOTIFICATION_ACK_CHANNEL--|\n//!  |                                                 |\n//!  |             (BLE)                               |\n//!  |----HW_IPCC_BLE_CMD_CHANNEL--------------------->|\n//!  |                                                 |\n//!  |----HW_IPCC_HCI_ACL_DATA_CHANNEL---------------->|\n//!  |                                                 |\n//!  |<---HW_IPCC_BLE_EVENT_CHANNEL--------------------|\n//!  |                                                 |\n//!  |             (BLE LLD)                           |\n//!  |----HW_IPCC_BLE_LLD_CMD_CHANNEL----------------->|\n//!  |                                                 |\n//!  |<---HW_IPCC_BLE_LLD_RSP_CHANNEL------------------|\n//!  |                                                 |\n//!  |<---HW_IPCC_BLE_LLD_M0_CMD_CHANNEL---------------|\n//!  |                                                 |\n//!  |             (MAC)                               |\n//!  |----HW_IPCC_MAC_802_15_4_CMD_RSP_CHANNEL-------->|\n//!  |                                                 |\n//!  |<---HW_IPCC_MAC_802_15_4_NOTIFICATION_ACK_CHANNEL|\n//!  |                                                 |\n//!  |             (BUFFER)                            |\n//!  |----HW_IPCC_MM_RELEASE_BUFFER_CHANNE------------>|\n//!  |                                                 |\n//!  |             (TRACE)                             |\n//!  |<----HW_IPCC_TRACES_CHANNEL----------------------|\n//!  |                                                 |\n//!\n//!\n\n#[repr(u8)]\npub enum IpccChannel {\n    Channel1 = 1,\n    Channel2 = 2,\n    Channel3 = 3,\n    Channel4 = 4,\n    Channel5 = 5,\n    Channel6 = 6,\n}\n\npub mod cpu1 {\n    use super::IpccChannel;\n\n    pub const IPCC_BLE_CMD_CHANNEL: IpccChannel = IpccChannel::Channel1;\n    pub const IPCC_SYSTEM_CMD_RSP_CHANNEL: IpccChannel = IpccChannel::Channel2;\n    pub const IPCC_THREAD_OT_CMD_RSP_CHANNEL: IpccChannel = IpccChannel::Channel3;\n    #[allow(dead_code)] // Not used currently but reserved\n    pub const IPCC_ZIGBEE_CMD_APPLI_CHANNEL: IpccChannel = IpccChannel::Channel3;\n    #[allow(dead_code)] // Not used currently but reserved\n    pub const IPCC_MAC_802_15_4_CMD_RSP_CHANNEL: IpccChannel = IpccChannel::Channel3;\n    #[allow(dead_code)] // Not used currently but reserved\n    pub const IPCC_MM_RELEASE_BUFFER_CHANNEL: IpccChannel = IpccChannel::Channel4;\n    pub const IPCC_THREAD_CLI_CMD_CHANNEL: IpccChannel = IpccChannel::Channel5;\n    #[allow(dead_code)] // Not used currently but reserved\n    pub const IPCC_LLDTESTS_CLI_CMD_CHANNEL: IpccChannel = IpccChannel::Channel5;\n    #[allow(dead_code)] // Not used currently but reserved\n    pub const IPCC_BLE_LLD_CMD_CHANNEL: IpccChannel = IpccChannel::Channel5;\n    pub const IPCC_HCI_ACL_DATA_CHANNEL: IpccChannel = IpccChannel::Channel6;\n}\n\npub mod cpu2 {\n    use super::IpccChannel;\n\n    pub const IPCC_BLE_EVENT_CHANNEL: IpccChannel = IpccChannel::Channel1;\n    pub const IPCC_SYSTEM_EVENT_CHANNEL: IpccChannel = IpccChannel::Channel2;\n    pub const IPCC_THREAD_NOTIFICATION_ACK_CHANNEL: IpccChannel = IpccChannel::Channel3;\n    #[allow(dead_code)] // Not used currently but reserved\n    pub const IPCC_ZIGBEE_APPLI_NOTIF_ACK_CHANNEL: IpccChannel = IpccChannel::Channel3;\n    #[allow(dead_code)] // Not used currently but reserved\n    pub const IPCC_MAC_802_15_4_NOTIFICATION_ACK_CHANNEL: IpccChannel = IpccChannel::Channel3;\n    #[allow(dead_code)] // Not used currently but reserved\n    pub const IPCC_LDDTESTS_M0_CMD_CHANNEL: IpccChannel = IpccChannel::Channel3;\n    #[allow(dead_code)] // Not used currently but reserved\n    pub const IPCC_BLE_LLDÇM0_CMD_CHANNEL: IpccChannel = IpccChannel::Channel3;\n    pub const IPCC_TRACES_CHANNEL: IpccChannel = IpccChannel::Channel4;\n    pub const IPCC_THREAD_CLI_NOTIFICATION_ACK_CHANNEL: IpccChannel = IpccChannel::Channel5;\n    #[allow(dead_code)] // Not used currently but reserved\n    pub const IPCC_LLDTESTS_CLI_RSP_CHANNEL: IpccChannel = IpccChannel::Channel5;\n    #[allow(dead_code)] // Not used currently but reserved\n    pub const IPCC_BLE_LLD_CLI_RSP_CHANNEL: IpccChannel = IpccChannel::Channel5;\n    #[allow(dead_code)] // Not used currently but reserved\n    pub const IPCC_BLE_LLD_RSP_CHANNEL: IpccChannel = IpccChannel::Channel5;\n    #[allow(dead_code)] // Not used currently but reserved\n    pub const IPCC_ZIGBEE_M0_REQUEST_CHANNEL: IpccChannel = IpccChannel::Channel5;\n}\n"
  },
  {
    "path": "embassy-stm32-wpan/src/wb55/cmd.rs",
    "content": "use core::ptr;\nuse core::sync::atomic::{Ordering, compiler_fence};\n\nuse crate::consts::TlPacketType;\nuse crate::wb55::PacketHeader;\n\n#[derive(Copy, Clone)]\n#[repr(C, packed)]\npub struct Cmd {\n    pub cmd_code: u16,\n    pub payload_len: u8,\n    pub payload: [u8; 255],\n}\n\nimpl Default for Cmd {\n    fn default() -> Self {\n        Self {\n            cmd_code: 0,\n            payload_len: 0,\n            payload: [0u8; 255],\n        }\n    }\n}\n\n#[derive(Copy, Clone, Default)]\n#[repr(C, packed)]\npub struct CmdSerial {\n    pub ty: u8,\n    pub cmd: Cmd,\n}\n\n#[derive(Copy, Clone, Default)]\n#[repr(C, packed)]\npub struct CmdSerialStub {\n    pub ty: u8,\n    pub cmd_code: u16,\n    pub payload_len: u8,\n}\n\n#[derive(Copy, Clone, Default)]\n#[repr(C, packed)]\npub struct CmdPacket {\n    pub header: PacketHeader,\n    pub cmdserial: CmdSerial,\n}\n\nimpl CmdPacket {\n    pub unsafe fn write_into(cmd_buf: *mut CmdPacket, packet_type: TlPacketType, cmd_code: u16, payload: &[u8]) {\n        let p_cmd_serial = (cmd_buf as *mut u8).add(size_of::<PacketHeader>());\n        let p_payload = p_cmd_serial.add(size_of::<CmdSerialStub>());\n\n        ptr::write_unaligned(\n            p_cmd_serial as *mut _,\n            CmdSerialStub {\n                ty: packet_type as u8,\n                cmd_code,\n                payload_len: payload.len() as u8,\n            },\n        );\n\n        ptr::copy_nonoverlapping(payload as *const _ as *const u8, p_payload, payload.len());\n\n        compiler_fence(Ordering::Release);\n    }\n}\n\n#[derive(Copy, Clone)]\n#[repr(C, packed)]\npub struct AclDataSerial {\n    pub ty: u8,\n    pub handle: u16,\n    pub length: u16,\n    pub acl_data: [u8; 1],\n}\n\n#[derive(Copy, Clone)]\n#[repr(C, packed)]\npub struct AclDataSerialStub {\n    pub ty: u8,\n    pub handle: u16,\n    pub length: u16,\n}\n\n#[derive(Copy, Clone)]\n#[repr(C, packed)]\npub struct AclDataPacket {\n    pub header: PacketHeader,\n    pub acl_data_serial: AclDataSerial,\n}\n\nimpl AclDataPacket {\n    pub unsafe fn write_into(cmd_buf: *mut AclDataPacket, packet_type: TlPacketType, handle: u16, payload: &[u8]) {\n        let p_cmd_serial = (cmd_buf as *mut u8).add(size_of::<PacketHeader>());\n        let p_payload = p_cmd_serial.add(size_of::<AclDataSerialStub>());\n\n        ptr::write_unaligned(\n            p_cmd_serial as *mut _,\n            AclDataSerialStub {\n                ty: packet_type as u8,\n                handle: handle,\n                length: payload.len() as u16,\n            },\n        );\n\n        ptr::copy_nonoverlapping(payload as *const _ as *const u8, p_payload, payload.len());\n\n        compiler_fence(Ordering::Release);\n    }\n}\n"
  },
  {
    "path": "embassy-stm32-wpan/src/wb55/consts.rs",
    "content": "use crate::evt::CsEvt;\nuse crate::wb55::PacketHeader;\n\n#[derive(Debug)]\n#[repr(C)]\npub enum TlPacketType {\n    MacCmd = 0x00,\n\n    BleCmd = 0x01,\n    AclData = 0x02,\n    BleEvt = 0x04,\n\n    OtCmd = 0x08,\n    OtRsp = 0x09,\n    CliCmd = 0x0A,\n    OtNot = 0x0C,\n    OtAck = 0x0D,\n    CliNot = 0x0E,\n    CliAck = 0x0F,\n\n    SysCmd = 0x10,\n    SysRsp = 0x11,\n    SysEvt = 0x12,\n\n    LocCmd = 0x20,\n    LocRsp = 0x21,\n\n    TracesApp = 0x40,\n    TracesWl = 0x41,\n}\n\nimpl TryFrom<u8> for TlPacketType {\n    type Error = ();\n\n    fn try_from(value: u8) -> Result<Self, Self::Error> {\n        match value {\n            0x01 => Ok(TlPacketType::BleCmd),\n            0x02 => Ok(TlPacketType::AclData),\n            0x04 => Ok(TlPacketType::BleEvt),\n            0x08 => Ok(TlPacketType::OtCmd),\n            0x09 => Ok(TlPacketType::OtRsp),\n            0x0A => Ok(TlPacketType::CliCmd),\n            0x0C => Ok(TlPacketType::OtNot),\n            0x0D => Ok(TlPacketType::OtAck),\n            0x0E => Ok(TlPacketType::CliNot),\n            0x0F => Ok(TlPacketType::CliAck),\n            0x10 => Ok(TlPacketType::SysCmd),\n            0x11 => Ok(TlPacketType::SysRsp),\n            0x12 => Ok(TlPacketType::SysEvt),\n            0x20 => Ok(TlPacketType::LocCmd),\n            0x21 => Ok(TlPacketType::LocRsp),\n            0x40 => Ok(TlPacketType::TracesApp),\n            0x41 => Ok(TlPacketType::TracesWl),\n\n            _ => Err(()),\n        }\n    }\n}\n\npub const TL_PACKET_HEADER_SIZE: usize = core::mem::size_of::<PacketHeader>();\npub const TL_EVT_HEADER_SIZE: usize = 3;\npub const TL_CS_EVT_SIZE: usize = core::mem::size_of::<CsEvt>();\n\n/**\n * Queue length of BLE Event\n * This parameter defines the number of asynchronous events that can be stored in the HCI layer before\n * being reported to the application. When a command is sent to the BLE core coprocessor, the HCI layer\n * is waiting for the event with the Num_HCI_Command_Packets set to 1. The receive queue shall be large\n * enough to store all asynchronous events received in between.\n * When CFG_TLBLE_MOST_EVENT_PAYLOAD_SIZE is set to 27, this allow to store three 255 bytes long asynchronous events\n * between the HCI command and its event.\n * This parameter depends on the value given to CFG_TLBLE_MOST_EVENT_PAYLOAD_SIZE. When the queue size is too small,\n * the system may hang if the queue is full with asynchronous events and the HCI layer is still waiting\n * for a CC/CS event, In that case, the notification TL_BLE_HCI_ToNot() is called to indicate\n * to the application a HCI command did not receive its command event within 30s (Default HCI Timeout).\n */\npub const CFG_TL_BLE_EVT_QUEUE_LENGTH: usize = 5;\npub const CFG_TL_BLE_MOST_EVENT_PAYLOAD_SIZE: usize = 255;\npub const TL_BLE_EVENT_FRAME_SIZE: usize = TL_EVT_HEADER_SIZE + CFG_TL_BLE_MOST_EVENT_PAYLOAD_SIZE;\n\npub const POOL_SIZE: usize = CFG_TL_BLE_EVT_QUEUE_LENGTH * 4 * divc(TL_PACKET_HEADER_SIZE + TL_BLE_EVENT_FRAME_SIZE, 4);\npub const C_SIZE_CMD_STRING: usize = 256;\n\npub const fn divc(x: usize, y: usize) -> usize {\n    (x + y - 1) / y\n}\n\npub const TL_BLE_EVT_CS_PACKET_SIZE: usize = TL_EVT_HEADER_SIZE + TL_CS_EVT_SIZE;\n#[allow(dead_code)]\npub const TL_BLE_EVT_CS_BUFFER_SIZE: usize = TL_PACKET_HEADER_SIZE + TL_BLE_EVT_CS_PACKET_SIZE;\n\npub const TL_BLEEVT_CC_OPCODE: u8 = 0x0E;\npub const TL_BLEEVT_CS_OPCODE: u8 = 0x0F;\npub const TL_BLEEVT_VS_OPCODE: u8 = 0xFF;\n"
  },
  {
    "path": "embassy-stm32-wpan/src/wb55/evt.rs",
    "content": "use core::marker::PhantomData;\nuse core::{ptr, slice};\n\nuse super::PacketHeader;\nuse crate::consts::TL_EVT_HEADER_SIZE;\n\n/**\n * The payload of `Evt` for a command status event\n */\n#[derive(Copy, Clone)]\n#[repr(C, packed)]\npub struct CsEvt {\n    pub status: u8,\n    pub num_cmd: u8,\n    pub cmd_code: u16,\n}\n\n/**\n * The payload of `Evt` for a command complete event\n */\n#[derive(Copy, Clone, Default)]\n#[repr(C, packed)]\npub struct CcEvt {\n    pub num_cmd: u8,\n    pub cmd_code: u16,\n    pub payload: [u8; 1],\n}\n\nimpl CcEvt {\n    pub fn write(&self, buf: &mut [u8]) {\n        unsafe {\n            let len = core::mem::size_of::<CcEvt>();\n            assert!(buf.len() >= len);\n\n            let self_ptr: *const CcEvt = self;\n            let self_buf_ptr: *const u8 = self_ptr.cast();\n\n            core::ptr::copy(self_buf_ptr, buf.as_mut_ptr(), len);\n        }\n    }\n}\n\n#[derive(Copy, Clone, Default)]\n#[repr(C, packed)]\npub struct AsynchEvt {\n    sub_evt_code: u16,\n    payload: [u8; 1],\n}\n\n#[derive(Copy, Clone)]\n#[repr(C, packed)]\npub struct Evt {\n    pub evt_code: u8,\n    pub payload_len: u8,\n    pub payload: [u8; 255],\n}\n\n#[derive(Copy, Clone)]\n#[repr(C, packed)]\npub struct EvtSerial {\n    pub kind: u8,\n    pub evt: Evt,\n}\n\n#[derive(Copy, Clone, Default)]\n#[repr(C, packed)]\npub struct EvtStub {\n    pub kind: u8,\n    pub evt_code: u8,\n    pub payload_len: u8,\n}\n\n/// This format shall be used for all events (asynchronous and command response) reported\n/// by the CPU2 except for the command response of a system command where the header is not there\n/// and the format to be used shall be `EvtSerial`.\n///\n/// ### Note:\n/// Be careful that the asynchronous events reported by the CPU2 on the system channel do\n/// include the header and shall use `EvtPacket` format. Only the command response format on the\n/// system channel is different.\n#[derive(Copy, Clone)]\n#[repr(C, packed)]\npub struct EvtPacket {\n    pub header: PacketHeader,\n    pub evt_serial: EvtSerial,\n}\n\nimpl EvtPacket {\n    pub fn kind(&self) -> u8 {\n        self.evt_serial.kind\n    }\n\n    pub fn evt(&self) -> &Evt {\n        &self.evt_serial.evt\n    }\n}\n\npub trait MemoryManager {\n    unsafe fn drop_event_packet(evt: *mut EvtPacket);\n}\n\n/// smart pointer to the [`EvtPacket`] that will dispose of [`EvtPacket`] buffer automatically\n/// on [`Drop`]\n#[derive(Debug)]\npub struct EvtBox<T: MemoryManager> {\n    ptr: *mut EvtPacket,\n    mm: PhantomData<T>,\n}\n\nunsafe impl<T: MemoryManager> Send for EvtBox<T> {}\nimpl<T: MemoryManager> EvtBox<T> {\n    pub(super) fn new(ptr: *mut EvtPacket) -> Self {\n        Self { ptr, mm: PhantomData }\n    }\n\n    /// Returns information about the event\n    pub fn stub(&self) -> EvtStub {\n        unsafe {\n            let p_evt_stub = &(*self.ptr).evt_serial as *const _ as *const EvtStub;\n\n            ptr::read_volatile(p_evt_stub)\n        }\n    }\n\n    pub fn payload<'a>(&'a self) -> &'a [u8] {\n        unsafe {\n            let p_payload_len = &(*self.ptr).evt_serial.evt.payload_len as *const u8;\n            let p_payload = &(*self.ptr).evt_serial.evt.payload as *const u8;\n\n            let payload_len = ptr::read_volatile(p_payload_len);\n\n            slice::from_raw_parts(p_payload, payload_len as usize)\n        }\n    }\n\n    pub fn serial<'a>(&'a self) -> &'a [u8] {\n        unsafe {\n            let evt_serial: *const EvtSerial = &(*self.ptr).evt_serial;\n            let evt_serial_buf: *const u8 = evt_serial.cast();\n\n            let len = (*evt_serial).evt.payload_len as usize + TL_EVT_HEADER_SIZE;\n\n            slice::from_raw_parts(evt_serial_buf, len)\n        }\n    }\n}\n\nimpl<T: MemoryManager> Drop for EvtBox<T> {\n    fn drop(&mut self) {\n        unsafe { T::drop_event_packet(self.ptr) };\n    }\n}\n"
  },
  {
    "path": "embassy-stm32-wpan/src/wb55/fmt.rs",
    "content": "#![macro_use]\n#![allow(unused)]\n\nuse core::fmt::{Debug, Display, LowerHex};\n\n#[cfg(all(feature = \"defmt\", feature = \"log\"))]\ncompile_error!(\"You may not enable both `defmt` and `log` features.\");\n\n#[collapse_debuginfo(yes)]\nmacro_rules! assert {\n    ($($x:tt)*) => {\n        {\n            #[cfg(not(feature = \"defmt\"))]\n            ::core::assert!($($x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::assert!($($x)*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! assert_eq {\n    ($($x:tt)*) => {\n        {\n            #[cfg(not(feature = \"defmt\"))]\n            ::core::assert_eq!($($x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::assert_eq!($($x)*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! assert_ne {\n    ($($x:tt)*) => {\n        {\n            #[cfg(not(feature = \"defmt\"))]\n            ::core::assert_ne!($($x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::assert_ne!($($x)*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! debug_assert {\n    ($($x:tt)*) => {\n        {\n            #[cfg(not(feature = \"defmt\"))]\n            ::core::debug_assert!($($x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::debug_assert!($($x)*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! debug_assert_eq {\n    ($($x:tt)*) => {\n        {\n            #[cfg(not(feature = \"defmt\"))]\n            ::core::debug_assert_eq!($($x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::debug_assert_eq!($($x)*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! debug_assert_ne {\n    ($($x:tt)*) => {\n        {\n            #[cfg(not(feature = \"defmt\"))]\n            ::core::debug_assert_ne!($($x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::debug_assert_ne!($($x)*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! todo {\n    ($($x:tt)*) => {\n        {\n            #[cfg(not(feature = \"defmt\"))]\n            ::core::todo!($($x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::todo!($($x)*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! unreachable {\n    ($($x:tt)*) => {\n        {\n            #[cfg(not(feature = \"defmt\"))]\n            ::core::unreachable!($($x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::unreachable!($($x)*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! panic {\n    ($($x:tt)*) => {\n        {\n            #[cfg(not(feature = \"defmt\"))]\n            ::core::panic!($($x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::panic!($($x)*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! trace {\n    ($s:literal $(, $x:expr)* $(,)?) => {\n        {\n            #[cfg(feature = \"log\")]\n            ::log::trace!($s $(, $x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::trace!($s $(, $x)*);\n            #[cfg(not(any(feature = \"log\", feature=\"defmt\")))]\n            let _ = ($( & $x ),*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! debug {\n    ($s:literal $(, $x:expr)* $(,)?) => {\n        {\n            #[cfg(feature = \"log\")]\n            ::log::debug!($s $(, $x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::debug!($s $(, $x)*);\n            #[cfg(not(any(feature = \"log\", feature=\"defmt\")))]\n            let _ = ($( & $x ),*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! info {\n    ($s:literal $(, $x:expr)* $(,)?) => {\n        {\n            #[cfg(feature = \"log\")]\n            ::log::info!($s $(, $x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::info!($s $(, $x)*);\n            #[cfg(not(any(feature = \"log\", feature=\"defmt\")))]\n            let _ = ($( & $x ),*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! warn {\n    ($s:literal $(, $x:expr)* $(,)?) => {\n        {\n            #[cfg(feature = \"log\")]\n            ::log::warn!($s $(, $x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::warn!($s $(, $x)*);\n            #[cfg(not(any(feature = \"log\", feature=\"defmt\")))]\n            let _ = ($( & $x ),*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! error {\n    ($s:literal $(, $x:expr)* $(,)?) => {\n        {\n            #[cfg(feature = \"log\")]\n            ::log::error!($s $(, $x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::error!($s $(, $x)*);\n            #[cfg(not(any(feature = \"log\", feature=\"defmt\")))]\n            let _ = ($( & $x ),*);\n        }\n    };\n}\n\n#[cfg(feature = \"defmt\")]\n#[collapse_debuginfo(yes)]\nmacro_rules! unwrap {\n    ($($x:tt)*) => {\n        ::defmt::unwrap!($($x)*)\n    };\n}\n\n#[cfg(not(feature = \"defmt\"))]\n#[collapse_debuginfo(yes)]\nmacro_rules! unwrap {\n    ($arg:expr) => {\n        match $crate::fmt::Try::into_result($arg) {\n            ::core::result::Result::Ok(t) => t,\n            ::core::result::Result::Err(e) => {\n                ::core::panic!(\"unwrap of `{}` failed: {:?}\", ::core::stringify!($arg), e);\n            }\n        }\n    };\n    ($arg:expr, $($msg:expr),+ $(,)? ) => {\n        match $crate::fmt::Try::into_result($arg) {\n            ::core::result::Result::Ok(t) => t,\n            ::core::result::Result::Err(e) => {\n                ::core::panic!(\"unwrap of `{}` failed: {}: {:?}\", ::core::stringify!($arg), ::core::format_args!($($msg,)*), e);\n            }\n        }\n    }\n}\n\n#[derive(Debug, Copy, Clone, Eq, PartialEq)]\npub struct NoneError;\n\npub trait Try {\n    type Ok;\n    type Error;\n    fn into_result(self) -> Result<Self::Ok, Self::Error>;\n}\n\nimpl<T> Try for Option<T> {\n    type Ok = T;\n    type Error = NoneError;\n\n    #[inline]\n    fn into_result(self) -> Result<T, NoneError> {\n        self.ok_or(NoneError)\n    }\n}\n\nimpl<T, E> Try for Result<T, E> {\n    type Ok = T;\n    type Error = E;\n\n    #[inline]\n    fn into_result(self) -> Self {\n        self\n    }\n}\n\npub(crate) struct Bytes<'a>(pub &'a [u8]);\n\nimpl<'a> Debug for Bytes<'a> {\n    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {\n        write!(f, \"{:#02x?}\", self.0)\n    }\n}\n\nimpl<'a> Display for Bytes<'a> {\n    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {\n        write!(f, \"{:#02x?}\", self.0)\n    }\n}\n\nimpl<'a> LowerHex for Bytes<'a> {\n    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {\n        write!(f, \"{:#02x?}\", self.0)\n    }\n}\n\n#[cfg(feature = \"defmt\")]\nimpl<'a> defmt::Format for Bytes<'a> {\n    fn format(&self, fmt: defmt::Formatter) {\n        defmt::write!(fmt, \"{:02x}\", self.0)\n    }\n}\n"
  },
  {
    "path": "embassy-stm32-wpan/src/wb55/fus.rs",
    "content": "use cortex_m::asm::wfi;\nuse cortex_m::peripheral::SCB;\nuse embassy_stm32::rtc::AnyRtc;\n\nuse crate::shci::{SchiSysEventReady, ShciFusGetStateErrorCode};\nuse crate::sub::sys::Sys;\n\n#[derive(Clone, Copy, PartialEq)]\nenum UpgradeStatus {\n    Pending,\n    Complete,\n}\n\n/// Magic number to request an upgrade\nconst MAGIC_PENDING: u32 = 0x4f2a9c1b;\n\n/// Administers FUS upgrades\npub struct FirmwareUpgrader<T: AnyRtc> {\n    rtc: T,\n    backup_register: usize,\n}\n\nimpl<T: AnyRtc> FirmwareUpgrader<T> {\n    pub fn new(rtc: T, backup_register: usize) -> Self {\n        Self { rtc, backup_register }\n    }\n\n    fn get_upgrade_status(&mut self) -> UpgradeStatus {\n        if self.rtc.read_backup_register(self.backup_register).unwrap_or_default() == MAGIC_PENDING {\n            UpgradeStatus::Pending\n        } else {\n            UpgradeStatus::Complete\n        }\n    }\n\n    fn set_upgrade_status(&mut self, upgrade_status: UpgradeStatus) {\n        self.rtc.write_backup_register(\n            self.backup_register,\n            match upgrade_status {\n                UpgradeStatus::Complete => 0,\n                UpgradeStatus::Pending => MAGIC_PENDING,\n            },\n        )\n    }\n\n    /// Start the upgrade of firmware; must be called after boot\n    pub async fn start_upgrade(&mut self, sys: &mut Sys<'_>) -> Result<(), ()> {\n        self.set_upgrade_status(UpgradeStatus::Pending);\n\n        sys.shci_c2_fus_getstate().await?;\n        sys.shci_c2_fus_getstate().await?;\n\n        // wait for FUS to reboot us\n        loop {\n            wfi();\n        }\n    }\n\n    /// Called on boot to drive the OTA process, or exit the FUS\n    pub async fn boot(&mut self, ready_event: SchiSysEventReady, sys: &mut Sys<'_>) -> Result<(), ()> {\n        let firmware_started = ready_event == SchiSysEventReady::WirelessFwRunning\n            && sys\n                .wireless_fw_info()\n                .is_some_and(|info| info.version_major() + info.version_minor() > 0);\n\n        let upgrade_status = self.get_upgrade_status();\n\n        // If wireless firmware is started, then abort the upgrade and return\n        if firmware_started {\n            self.set_upgrade_status(UpgradeStatus::Complete);\n\n            return Ok(());\n        }\n\n        // If we cannot get the FUS state, then return with an error\n        match sys.shci_c2_fus_getstate().await? {\n            ShciFusGetStateErrorCode::FusStateErrorErrUnknown => {\n                // This is the first time in the life of the product the FUS is involved.\n                // After this command, it will be properly initialized\n                // Request the device to reboot to install the wireless firmware\n\n                SCB::sys_reset();\n            }\n            ShciFusGetStateErrorCode::FusStateErrorNoError if upgrade_status == UpgradeStatus::Complete => {\n                // FUS is idle and upgrade is complete, start the wireless stack\n                sys.shci_c2_fus_startws().await?;\n            }\n            ShciFusGetStateErrorCode::FusStateErrorNoError if upgrade_status == UpgradeStatus::Pending => {\n                // FUS is idle and upgrade is pending, start the upgrade\n                self.set_upgrade_status(UpgradeStatus::Complete);\n\n                sys.shci_c2_fus_fwupgrade(0, 0).await?;\n            }\n            _ => {}\n        }\n\n        // Wait for the FUS to reboot us\n        loop {\n            wfi();\n        }\n    }\n}\n"
  },
  {
    "path": "embassy-stm32-wpan/src/wb55/lhci.rs",
    "content": "use core::ptr;\n\nuse crate::cmd::CmdPacket;\nuse crate::consts::{TL_EVT_HEADER_SIZE, TlPacketType};\nuse crate::evt::{CcEvt, EvtPacket, EvtSerial};\nuse crate::tables::{DeviceInfoTable, RssInfoTable, SafeBootInfoTable, TL_DEVICE_INFO_TABLE, WirelessFwInfoTable};\n\nconst TL_BLEEVT_CC_OPCODE: u8 = 0x0e;\nconst LHCI_OPCODE_C1_DEVICE_INF: u16 = 0xfd62;\n\nconst PACKAGE_DATA_PTR: *const u8 = 0x1FFF_7500 as _;\nconst UID64_PTR: *const u32 = 0x1FFF_7580 as _;\n\n#[derive(Debug, Copy, Clone)]\n#[repr(C, packed)]\npub struct LhciC1DeviceInformationCcrp {\n    pub status: u8,\n    pub rev_id: u16,\n    pub dev_code_id: u16,\n    pub package_type: u8,\n    pub device_type_id: u8,\n    pub st_company_id: u32,\n    pub uid64: u32,\n\n    pub uid96_0: u32,\n    pub uid96_1: u32,\n    pub uid96_2: u32,\n\n    pub safe_boot_info_table: SafeBootInfoTable,\n    pub rss_info_table: RssInfoTable,\n    pub wireless_fw_info_table: WirelessFwInfoTable,\n\n    pub app_fw_inf: u32,\n}\n\nimpl Default for LhciC1DeviceInformationCcrp {\n    fn default() -> Self {\n        let DeviceInfoTable {\n            safe_boot_info_table,\n            rss_info_table,\n            wireless_fw_info_table,\n        } = unsafe { ptr::read_volatile(TL_DEVICE_INFO_TABLE.as_ptr()) };\n\n        let device_id = stm32_device_signature::device_id();\n        let uid96_0 = (device_id[3] as u32) << 24\n            | (device_id[2] as u32) << 16\n            | (device_id[1] as u32) << 8\n            | device_id[0] as u32;\n        let uid96_1 = (device_id[7] as u32) << 24\n            | (device_id[6] as u32) << 16\n            | (device_id[5] as u32) << 8\n            | device_id[4] as u32;\n        let uid96_2 = (device_id[11] as u32) << 24\n            | (device_id[10] as u32) << 16\n            | (device_id[9] as u32) << 8\n            | device_id[8] as u32;\n\n        let package_type = unsafe { *PACKAGE_DATA_PTR };\n        let uid64 = unsafe { *UID64_PTR };\n        let st_company_id = unsafe { *UID64_PTR.offset(1) } >> 8 & 0x00FF_FFFF;\n        let device_type_id = (unsafe { *UID64_PTR.offset(1) } & 0x000000FF) as u8;\n\n        LhciC1DeviceInformationCcrp {\n            status: 0,\n            rev_id: 0,\n            dev_code_id: 0,\n            package_type,\n            device_type_id,\n            st_company_id,\n            uid64,\n            uid96_0,\n            uid96_1,\n            uid96_2,\n            safe_boot_info_table,\n            rss_info_table,\n            wireless_fw_info_table,\n            app_fw_inf: (1 << 8), // 0.0.1\n        }\n    }\n}\n\nimpl LhciC1DeviceInformationCcrp {\n    pub fn new() -> Self {\n        Self::default()\n    }\n\n    pub fn write(&self, cmd_packet: &mut CmdPacket) {\n        let self_size = core::mem::size_of::<LhciC1DeviceInformationCcrp>();\n\n        unsafe {\n            let cmd_packet_ptr: *mut CmdPacket = cmd_packet;\n            let evet_packet_ptr: *mut EvtPacket = cmd_packet_ptr.cast();\n\n            let evt_serial: *mut EvtSerial = &mut (*evet_packet_ptr).evt_serial;\n            let evt_payload = (*evt_serial).evt.payload.as_mut_ptr();\n            let evt_cc: *mut CcEvt = evt_payload.cast();\n            let evt_cc_payload_buf = (*evt_cc).payload.as_mut_ptr();\n\n            (*evt_serial).kind = TlPacketType::LocRsp as u8;\n            (*evt_serial).evt.evt_code = TL_BLEEVT_CC_OPCODE;\n            (*evt_serial).evt.payload_len = TL_EVT_HEADER_SIZE as u8 + self_size as u8;\n\n            (*evt_cc).cmd_code = LHCI_OPCODE_C1_DEVICE_INF;\n            (*evt_cc).num_cmd = 1;\n\n            let self_ptr: *const LhciC1DeviceInformationCcrp = self;\n            let self_buf = self_ptr.cast();\n\n            ptr::copy(self_buf, evt_cc_payload_buf, self_size);\n        }\n    }\n}\n"
  },
  {
    "path": "embassy-stm32-wpan/src/wb55/mac/commands.rs",
    "content": "#![allow(unused)]\n\nuse core::{mem, slice};\n\nuse smoltcp::wire::ieee802154::Frame;\n\nuse super::opcodes::OpcodeM4ToM0;\nuse super::typedefs::{\n    AddressMode, Capabilities, DisassociationReason, GtsCharacteristics, KeyIdMode, MacAddress, MacChannel, MacStatus,\n    PanId, PibId, ScanType, SecurityLevel,\n};\n\npub trait MacCommand: Sized {\n    const OPCODE: OpcodeM4ToM0;\n\n    fn payload<'a>(&'a self) -> &'a [u8] {\n        unsafe { slice::from_raw_parts(self as *const _ as *const u8, mem::size_of::<Self>()) }\n    }\n}\n\n/// MLME ASSOCIATE Request used to request an association\n#[repr(C)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub struct AssociateRequest {\n    /// the logical channel on which to attempt association\n    pub channel_number: MacChannel,\n    /// the channel page on which to attempt association\n    pub channel_page: u8,\n    /// coordinator addressing mode\n    pub coord_addr_mode: AddressMode,\n    /// operational capabilities of the associating device\n    pub capability_information: Capabilities,\n    /// the identifier of the PAN with which to associate\n    pub coord_pan_id: PanId,\n    /// the security level to be used\n    pub security_level: SecurityLevel,\n    /// the mode used to identify the key to be used\n    pub key_id_mode: KeyIdMode,\n    /// the originator of the key to be used\n    pub key_source: [u8; 8],\n    /// Coordinator address\n    pub coord_address: MacAddress,\n    /// the index of the key to be used\n    pub key_index: u8,\n}\n\nimpl MacCommand for AssociateRequest {\n    const OPCODE: OpcodeM4ToM0 = OpcodeM4ToM0::MlmeAssociateReq;\n}\n\n/// MLME DISASSOCIATE Request sed to request a disassociation\n#[repr(C)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub struct DisassociateRequest {\n    /// device addressing mode used\n    pub device_addr_mode: AddressMode,\n    /// the identifier of the PAN of the device\n    pub device_pan_id: PanId,\n    /// the reason for the disassociation\n    pub disassociation_reason: DisassociationReason,\n    /// device address\n    pub device_address: MacAddress,\n    /// `true` if the disassociation notification command is to be sent indirectly\n    pub tx_indirect: bool,\n    /// the security level to be used\n    pub security_level: SecurityLevel,\n    /// the mode to be used to indetify the key to be used\n    pub key_id_mode: KeyIdMode,\n    /// the index of the key to be used\n    pub key_index: u8,\n    /// the originator of the key to be used\n    pub key_source: [u8; 8],\n}\n\nimpl MacCommand for DisassociateRequest {\n    const OPCODE: OpcodeM4ToM0 = OpcodeM4ToM0::MlmeDisassociateReq;\n}\n\n/// MLME GET Request used to request a PIB value\n#[repr(C)]\n#[derive(Default)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub struct GetRequest {\n    /// the name of the PIB attribute to read\n    pub pib_attribute: PibId,\n\n    /// byte stuffing to keep 32 bit alignment\n    pub a_stuffing: [u8; 3],\n}\n\nimpl MacCommand for GetRequest {\n    const OPCODE: OpcodeM4ToM0 = OpcodeM4ToM0::MlmeGetReq;\n}\n\n/// MLME GTS Request used to request and maintain GTSs\n#[repr(C)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub struct GtsRequest {\n    /// the characteristics of the GTS\n    pub characteristics: GtsCharacteristics,\n    /// the security level to be used\n    pub security_level: SecurityLevel,\n    /// the mode used to identify the key to be used\n    pub key_id_mode: KeyIdMode,\n    /// the index of the key to be used\n    pub key_index: u8,\n    /// the originator of the key to be used\n    pub key_source: [u8; 8],\n}\n\nimpl MacCommand for GtsRequest {\n    const OPCODE: OpcodeM4ToM0 = OpcodeM4ToM0::MlmeGetReq;\n}\n\n#[repr(C)]\n#[derive(Default)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub struct ResetRequest {\n    /// MAC PIB attributes are set to their default values or not during reset\n    pub set_default_pib: bool,\n    /// byte stuffing to keep 32 bit alignment\n    pub a_stuffing: [u8; 3],\n}\n\nimpl MacCommand for ResetRequest {\n    const OPCODE: OpcodeM4ToM0 = OpcodeM4ToM0::MlmeResetReq;\n}\n\n/// MLME RX ENABLE Request used to request that the receiver is either enabled\n/// for a finite period of time or disabled\n#[repr(C)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub struct RxEnableRequest {\n    /// the request operation can be deferred or not\n    pub defer_permit: bool,\n    /// configure the transceiver to RX with ranging for a value of\n    /// RANGING_ON or to not enable ranging for RANGING_OFF\n    pub ranging_rx_control: u8,\n    /// byte stuffing to keep 32 bit alignment\n    pub a_stuffing: [u8; 2],\n    /// number of symbols measured before the receiver is to be enabled or disabled\n    pub rx_on_time: [u8; 4],\n    /// number of symbols for which the receiver is to be enabled\n    pub rx_on_duration: [u8; 4],\n}\n\nimpl MacCommand for RxEnableRequest {\n    const OPCODE: OpcodeM4ToM0 = OpcodeM4ToM0::MlmeRxEnableReq;\n}\n\n/// MLME SCAN Request used to initiate a channel scan over a given list of channels\n#[repr(C)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub struct ScanRequest {\n    /// the type of scan to be performed\n    pub scan_type: ScanType,\n    /// the time spent on scanning each channel\n    pub scan_duration: u8,\n    /// channel page on which to perform the scan\n    pub channel_page: u8,\n    /// security level to be used\n    pub security_level: SecurityLevel,\n    /// indicate which channels are to be scanned\n    pub scan_channels: [u8; 4],\n    /// originator the key to be used\n    pub key_source: [u8; 8],\n    /// mode used to identify the key to be used\n    pub key_id_mode: KeyIdMode,\n    /// index of the key to be used\n    pub key_index: u8,\n    /// byte stuffing to keep 32 bit alignment\n    pub a_stuffing: [u8; 2],\n}\n\nimpl MacCommand for ScanRequest {\n    const OPCODE: OpcodeM4ToM0 = OpcodeM4ToM0::MlmeScanReq;\n}\n\n/// MLME SET Request used to attempt to write the given value to the indicated PIB attribute\n#[repr(C)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub struct SetRequest {\n    /// the pointer to the value of the PIB attribute to set\n    pub pib_attribute_ptr: *const u8,\n    /// the name of the PIB attribute to set\n    pub pib_attribute: PibId,\n}\n\nimpl MacCommand for SetRequest {\n    const OPCODE: OpcodeM4ToM0 = OpcodeM4ToM0::MlmeSetReq;\n}\n\n/// MLME START Request used by the FFDs to intiate a new PAN or to begin using a new superframe\n/// configuration\n#[repr(C)]\n#[derive(Default)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub struct StartRequest {\n    /// PAN indentifier to used by the device\n    pub pan_id: PanId,\n    /// logical channel on which to begin\n    pub channel_number: MacChannel,\n    /// channel page on which to begin\n    pub channel_page: u8,\n    /// time at which to begin transmitting beacons\n    pub start_time: [u8; 4],\n    /// indicated how often the beacon is to be transmitted\n    pub beacon_order: u8,\n    /// length of the active portion of the superframe\n    pub superframe_order: u8,\n    /// indicated wheter the device is a PAN coordinator or not\n    pub pan_coordinator: bool,\n    /// indicates if the receiver of the beaconing device is disabled or not\n    pub battery_life_extension: bool,\n    /// indicated if the coordinator realignment command is to be trasmitted\n    pub coord_realignment: u8,\n    /// indicated if the coordinator realignment command is to be trasmitted\n    pub coord_realign_security_level: SecurityLevel,\n    /// index of the key to be used\n    pub coord_realign_key_id_index: u8,\n    /// originator of the key to be used\n    pub coord_realign_key_source: [u8; 8],\n    /// security level to be used for beacon frames\n    pub beacon_security_level: SecurityLevel,\n    /// mode used to identify the key to be used\n    pub beacon_key_id_mode: KeyIdMode,\n    /// index of the key to be used\n    pub beacon_key_index: u8,\n    /// originator of the key to be used\n    pub beacon_key_source: [u8; 8],\n}\n\nimpl MacCommand for StartRequest {\n    const OPCODE: OpcodeM4ToM0 = OpcodeM4ToM0::MlmeStartReq;\n}\n\n/// MLME SYNC Request used to synchronize with the coordinator by acquiring and, if\n/// specified, tracking its beacons\n#[repr(C)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub struct SyncRequest {\n    /// the channel number on which to attempt coordinator synchronization\n    pub channel_number: MacChannel,\n    /// the channel page on which to attempt coordinator synchronization\n    pub channel_page: u8,\n    /// `true` if the MLME is to synchronize with the next beacon and attempts\n    /// to track all future beacons.\n    ///\n    /// `false` if the MLME is to synchronize with only the next beacon\n    pub track_beacon: bool,\n    /// byte stuffing to keep 32 bit alignment\n    pub a_stuffing: [u8; 1],\n}\n\nimpl MacCommand for SyncRequest {\n    const OPCODE: OpcodeM4ToM0 = OpcodeM4ToM0::MlmeSyncReq;\n}\n\n/// MLME POLL Request propmts the device to request data from the coordinator\n#[repr(C)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub struct PollRequest {\n    /// addressing mode of the coordinator\n    pub coord_addr_mode: AddressMode,\n    /// security level to be used\n    pub security_level: SecurityLevel,\n    /// mode used to identify the key to be used\n    pub key_id_mode: KeyIdMode,\n    /// index of the key to be used\n    pub key_index: u8,\n    /// coordinator address\n    pub coord_address: MacAddress,\n    /// originator of the key to be used\n    pub key_source: [u8; 8],\n    /// PAN identifier of the coordinator\n    pub coord_pan_id: PanId,\n    /// byte stuffing to keep 32 bit alignment\n    pub a_stuffing: [u8; 2],\n}\n\nimpl MacCommand for PollRequest {\n    const OPCODE: OpcodeM4ToM0 = OpcodeM4ToM0::MlmePollReq;\n}\n\n/// MLME DPS Request allows the next higher layer to request that the PHY utilize a\n/// given pair of preamble codes for a single use pending expiration of the DPSIndexDuration\n#[repr(C)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub struct DpsRequest {\n    /// the index value for the transmitter\n    tx_dps_index: u8,\n    /// the index value of the receiver\n    rx_dps_index: u8,\n    /// the number of symbols for which the transmitter and receiver will utilize the\n    /// respective DPS indices\n    dps_index_duration: u8,\n    /// byte stuffing to keep 32 bit alignment\n    pub a_stuffing: [u8; 1],\n}\n\nimpl MacCommand for DpsRequest {\n    const OPCODE: OpcodeM4ToM0 = OpcodeM4ToM0::MlmeDpsReq;\n}\n\n/// MLME SOUNDING request primitive which is used by the next higher layer to request that\n/// the PHY respond with channel sounding information\n#[repr(C)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub struct SoundingRequest {\n    /// byte stuffing to keep 32 bit alignment\n    pub a_stuffing: [u8; 4],\n}\n\nimpl MacCommand for SoundingRequest {\n    const OPCODE: OpcodeM4ToM0 = OpcodeM4ToM0::MlmeSoundingReq;\n}\n\n/// MLME CALIBRATE request primitive which used  to obtain the results of a ranging\n/// calibration request from an RDEV\n#[repr(C)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub struct CalibrateRequest {\n    /// byte stuffing to keep 32 bit alignment\n    pub a_stuffing: [u8; 4],\n}\n\nimpl MacCommand for CalibrateRequest {\n    const OPCODE: OpcodeM4ToM0 = OpcodeM4ToM0::MlmeCalibrateReq;\n}\n\n/// MCPS DATA Request used for MAC data related requests from the application\n#[repr(C)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub struct DataRequest {\n    /// the handle assocated with the MSDU to be transmitted\n    pub msdu_ptr: *const u8,\n    /// source addressing mode used\n    pub src_addr_mode: AddressMode,\n    /// destination addressing mode used\n    pub dst_addr_mode: AddressMode,\n    /// destination PAN Id\n    pub dst_pan_id: PanId,\n    /// destination address\n    pub dst_address: MacAddress,\n    /// the number of octets contained in the MSDU\n    pub msdu_length: u8,\n    /// the handle assocated with the MSDU to be transmitted\n    pub msdu_handle: u8,\n    /// the ACK transmittion options for the MSDU\n    pub ack_tx: u8,\n    /// `true` if a GTS is to be used for transmission\n    ///\n    /// `false` indicates that the CAP will be used\n    pub gts_tx: bool,\n    /// the pending bit transmission options for the MSDU\n    pub indirect_tx: u8,\n    /// the security level to be used\n    pub security_level: SecurityLevel,\n    /// the mode used to indentify the key to be used\n    pub key_id_mode: KeyIdMode,\n    /// the index of the key to be used\n    pub key_index: u8,\n    /// the originator of the key to be used\n    pub key_source: [u8; 8],\n    /// 2011 - the pulse repitition value\n    pub uwbprf: u8,\n    /// 2011 - the ranging configuration\n    pub ranging: u8,\n    /// 2011 - the preamble symbol repititions\n    pub uwb_preamble_symbol_repetitions: u8,\n    /// 2011 - indicates the data rate\n    pub datrate: u8,\n}\n\nimpl DataRequest {\n    pub fn set_buffer<'a>(&'a mut self, buf: &'a [u8]) -> &'a mut Self {\n        self.msdu_ptr = buf as *const _ as *const u8;\n        self.msdu_length = buf.len() as u8;\n\n        self\n    }\n}\n\nimpl<'a, T: AsRef<[u8]>> TryFrom<Frame<&'a T>> for DataRequest {\n    type Error = ();\n\n    fn try_from(frame: Frame<&'a T>) -> Result<Self, Self::Error> {\n        // TODO: map the rest of these\n\n        let mut request = DataRequest {\n            src_addr_mode: frame.src_addressing_mode().try_into()?,\n            dst_addr_mode: frame.dst_addressing_mode().try_into()?,\n            dst_pan_id: frame.dst_pan_id().ok_or(())?.into(),\n            dst_address: frame.dst_addr().ok_or(())?.into(),\n            msdu_handle: frame.sequence_number().ok_or(())?,\n            key_source: frame.key_source().unwrap_or_default().try_into().unwrap_or_default(),\n            ack_tx: frame.ack_request() as u8,\n            gts_tx: false,\n            security_level: if frame.security_enabled() {\n                SecurityLevel::Secured\n            } else {\n                SecurityLevel::Unsecure\n            },\n            ..Default::default()\n        };\n\n        request.set_buffer(frame.payload().ok_or(())?);\n\n        Ok(request)\n    }\n}\n\nimpl Default for DataRequest {\n    fn default() -> Self {\n        Self {\n            msdu_ptr: 0 as *const u8,\n            src_addr_mode: AddressMode::NoAddress,\n            dst_addr_mode: AddressMode::NoAddress,\n            dst_pan_id: PanId([0, 0]),\n            dst_address: MacAddress { short: [0, 0] },\n            msdu_length: 0,\n            msdu_handle: 0,\n            ack_tx: 0,\n            gts_tx: false,\n            indirect_tx: 0,\n            security_level: SecurityLevel::Unsecure,\n            key_id_mode: KeyIdMode::Implicite,\n            key_index: 0,\n            key_source: [0u8; 8],\n            uwbprf: 0,\n            ranging: 0,\n            uwb_preamble_symbol_repetitions: 0,\n            datrate: 0,\n        }\n    }\n}\n\nimpl MacCommand for DataRequest {\n    const OPCODE: OpcodeM4ToM0 = OpcodeM4ToM0::McpsDataReq;\n}\n\n/// for MCPS PURGE Request used to purge an MSDU from the transaction queue\n#[repr(C)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub struct PurgeRequest {\n    /// the handle associated with the MSDU to be purged from the transaction\n    /// queue\n    pub msdu_handle: u8,\n    /// byte stuffing to keep 32 bit alignment\n    pub a_stuffing: [u8; 3],\n}\n\nimpl MacCommand for PurgeRequest {\n    const OPCODE: OpcodeM4ToM0 = OpcodeM4ToM0::McpsPurgeReq;\n}\n\n/// MLME ASSOCIATE Response used to initiate a response to an MLME-ASSOCIATE.indication\n#[repr(C)]\n#[derive(Default)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub struct AssociateResponse {\n    /// extended address of the device requesting association\n    pub device_address: [u8; 8],\n    /// 16-bitshort device address allocated by the coordinator on successful\n    /// association\n    pub assoc_short_address: [u8; 2],\n    /// status of the association attempt\n    pub status: MacStatus,\n    /// security level to be used\n    pub security_level: SecurityLevel,\n    /// the originator of the key to be used\n    pub key_source: [u8; 8],\n    /// the mode used to identify the key to be used\n    pub key_id_mode: KeyIdMode,\n    /// the index of the key to be used\n    pub key_index: u8,\n    /// byte stuffing to keep 32 bit alignment\n    pub a_stuffing: [u8; 2],\n}\n\nimpl MacCommand for AssociateResponse {\n    const OPCODE: OpcodeM4ToM0 = OpcodeM4ToM0::MlmeAssociateRes;\n}\n\n/// MLME ORPHAN Response used to respond to the MLME ORPHAN Indication\n#[repr(C)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub struct OrphanResponse {\n    /// extended address of the orphaned device\n    pub orphan_address: [u8; 8],\n    /// short address allocated to the orphaned device\n    pub short_address: [u8; 2],\n    /// if the orphaned device is associated with coordinator or not\n    pub associated_member: bool,\n    /// security level to be used\n    pub security_level: SecurityLevel,\n    /// the originator of the key to be used\n    pub key_source: [u8; 8],\n    /// the mode used to identify the key to be used\n    pub key_id_mode: KeyIdMode,\n    /// the index of the key to be used\n    pub key_index: u8,\n    /// byte stuffing to keep 32 bit alignment\n    pub a_stuffing: [u8; 2],\n}\n\nimpl MacCommand for OrphanResponse {\n    const OPCODE: OpcodeM4ToM0 = OpcodeM4ToM0::MlmeOrphanRes;\n}\n"
  },
  {
    "path": "embassy-stm32-wpan/src/wb55/mac/consts.rs",
    "content": "pub const MAX_PAN_DESC_SUPPORTED: usize = 6;\npub const MAX_SOUNDING_LIST_SUPPORTED: usize = 6;\npub const MAX_PENDING_ADDRESS: usize = 7;\npub const MAX_ED_SCAN_RESULTS_SUPPORTED: usize = 16;\n"
  },
  {
    "path": "embassy-stm32-wpan/src/wb55/mac/control.rs",
    "content": "use core::cell::RefCell;\nuse core::future::Future;\nuse core::sync::atomic::{Ordering, compiler_fence};\nuse core::task;\nuse core::task::Poll;\n\nuse embassy_net_driver::LinkState;\nuse embassy_sync::blocking_mutex;\nuse embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;\nuse embassy_sync::mutex::Mutex;\nuse embassy_sync::signal::Signal;\nuse futures_util::FutureExt;\n\nuse crate::mac::commands::*;\nuse crate::mac::driver::NetworkState;\nuse crate::mac::event::MacEvent;\nuse crate::mac::runner::ZeroCopyPubSub;\nuse crate::mac::typedefs::*;\nuse crate::sub::mac::MacTx;\n\npub struct Control<'a> {\n    rx_event_channel: &'a ZeroCopyPubSub<CriticalSectionRawMutex, MacEvent<'a>>,\n    mac_tx: &'a Mutex<CriticalSectionRawMutex, MacTx<'a>>,\n    #[allow(unused)]\n    network_state: &'a blocking_mutex::Mutex<CriticalSectionRawMutex, RefCell<NetworkState>>,\n}\n\nimpl<'a> Control<'a> {\n    pub(crate) fn new(\n        rx_event_channel: &'a ZeroCopyPubSub<CriticalSectionRawMutex, MacEvent<'a>>,\n        mac_tx: &'a Mutex<CriticalSectionRawMutex, MacTx<'a>>,\n        network_state: &'a blocking_mutex::Mutex<CriticalSectionRawMutex, RefCell<NetworkState>>,\n    ) -> Self {\n        Self {\n            rx_event_channel,\n            mac_tx,\n            network_state,\n        }\n    }\n\n    pub async fn init_link(&mut self, pan_id: [u8; 2]) {\n        debug!(\"resetting\");\n\n        debug!(\n            \"{:#x}\",\n            self.send_command_and_get_response(&ResetRequest {\n                set_default_pib: true,\n                ..Default::default()\n            })\n            .await\n            .unwrap()\n            .await\n        );\n\n        let (short_address, mac_address) = critical_section::with(|cs| {\n            let mut network_state = self.network_state.borrow(cs).borrow_mut();\n\n            network_state.pan_id = pan_id;\n\n            (network_state.short_addr, network_state.mac_addr)\n        });\n\n        debug!(\"setting extended address\");\n        debug!(\n            \"{:#x}\",\n            self.send_command_and_get_response(&SetRequest {\n                pib_attribute_ptr: &u64::from_be_bytes(mac_address) as *const _ as *const u8,\n                pib_attribute: PibId::ExtendedAddress,\n            })\n            .await\n            .unwrap()\n            .await\n        );\n\n        debug!(\"setting short address\");\n        debug!(\n            \"{:#x}\",\n            self.send_command_and_get_response(&SetRequest {\n                pib_attribute_ptr: &u16::from_be_bytes(short_address) as *const _ as *const u8,\n                pib_attribute: PibId::ShortAddress,\n            })\n            .await\n            .unwrap()\n            .await\n        );\n\n        debug!(\"setting association permit\");\n        let association_permit: bool = true;\n        debug!(\n            \"{:#x}\",\n            self.send_command_and_get_response(&SetRequest {\n                pib_attribute_ptr: &association_permit as *const _ as *const u8,\n                pib_attribute: PibId::AssociationPermit,\n            })\n            .await\n            .unwrap()\n            .await\n        );\n\n        debug!(\"setting TX power\");\n        let transmit_power: i8 = 2;\n        debug!(\n            \"{:#x}\",\n            self.send_command_and_get_response(&SetRequest {\n                pib_attribute_ptr: &transmit_power as *const _ as *const u8,\n                pib_attribute: PibId::TransmitPower,\n            })\n            .await\n            .unwrap()\n            .await\n        );\n\n        debug!(\"starting FFD device\");\n        debug!(\n            \"{:#x}\",\n            self.send_command_and_get_response(&StartRequest {\n                pan_id: PanId(pan_id),\n                channel_number: MacChannel::Channel16,\n                beacon_order: 0x0F,\n                superframe_order: 0x0F,\n                pan_coordinator: true,\n                battery_life_extension: false,\n                ..Default::default()\n            })\n            .await\n            .unwrap()\n            .await\n        );\n\n        debug!(\"setting RX on when idle\");\n        let rx_on_while_idle: bool = true;\n        debug!(\n            \"{:#x}\",\n            self.send_command_and_get_response(&SetRequest {\n                pib_attribute_ptr: &rx_on_while_idle as *const _ as *const u8,\n                pib_attribute: PibId::RxOnWhenIdle,\n            })\n            .await\n            .unwrap()\n            .await\n        );\n\n        critical_section::with(|cs| {\n            let mut network_state = self.network_state.borrow(cs).borrow_mut();\n\n            network_state.link_state = LinkState::Up;\n            network_state.link_waker.wake();\n        });\n    }\n\n    pub async fn send_command<T>(&self, cmd: &T) -> Result<(), MacError>\n    where\n        T: MacCommand,\n    {\n        self.mac_tx.lock().await.send_command(cmd).await\n    }\n\n    pub async fn send_command_and_get_response<T>(&self, cmd: &T) -> Result<EventToken<'a>, MacError>\n    where\n        T: MacCommand,\n    {\n        let token = EventToken::new(self.rx_event_channel);\n\n        compiler_fence(Ordering::Release);\n\n        self.mac_tx.lock().await.send_command(cmd).await?;\n\n        Ok(token)\n    }\n}\n\npub struct EventToken<'a> {\n    rx_event_channel: &'a ZeroCopyPubSub<CriticalSectionRawMutex, MacEvent<'a>>,\n}\n\nimpl<'a> EventToken<'a> {\n    pub(crate) fn new(rx_event_channel: &'a ZeroCopyPubSub<CriticalSectionRawMutex, MacEvent<'a>>) -> Self {\n        // Enable event receiving\n        rx_event_channel.lock(|s| {\n            *s.borrow_mut() = Some(Signal::new());\n        });\n\n        Self { rx_event_channel }\n    }\n}\n\nimpl<'a> Future for EventToken<'a> {\n    type Output = MacEvent<'a>;\n\n    fn poll(self: core::pin::Pin<&mut Self>, cx: &mut task::Context<'_>) -> Poll<Self::Output> {\n        self.rx_event_channel\n            .lock(|s| s.borrow_mut().as_mut().unwrap().wait().poll_unpin(cx))\n    }\n}\n\nimpl<'a> Drop for EventToken<'a> {\n    fn drop(&mut self) {\n        // Disable event receiving\n        // This will also drop the contained event, if it exists, and will free up receiving the next event\n        self.rx_event_channel.lock(|s| {\n            *s.borrow_mut() = None;\n        });\n    }\n}\n"
  },
  {
    "path": "embassy-stm32-wpan/src/wb55/mac/driver.rs",
    "content": "#![deny(unused_must_use)]\n\nuse core::cell::RefCell;\nuse core::task::Context;\n\nuse embassy_net_driver::{Capabilities, HardwareAddress, LinkState};\nuse embassy_sync::blocking_mutex;\nuse embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;\nuse embassy_sync::channel::Channel;\nuse embassy_sync::mutex::Mutex;\nuse embassy_sync::waitqueue::AtomicWaker;\n\nuse crate::mac::event::MacEvent;\nuse crate::mac::indications::{write_frame_from_beacon_indication, write_frame_from_data_indication};\nuse crate::mac::runner::{BUF_SIZE, ZeroCopyPubSub};\nuse crate::mac::{Control, MTU, Runner};\nuse crate::sub::mac::{Mac, MacRx, MacTx};\n\npub struct NetworkState {\n    pub mac_addr: [u8; 8],\n    pub short_addr: [u8; 2],\n    pub pan_id: [u8; 2],\n    pub link_state: LinkState,\n    pub link_waker: AtomicWaker,\n}\n\nimpl NetworkState {\n    pub const fn new() -> Self {\n        Self {\n            mac_addr: [0u8; 8],\n            short_addr: [0u8; 2],\n            pan_id: [0u8; 2],\n            link_state: LinkState::Down,\n            link_waker: AtomicWaker::new(),\n        }\n    }\n}\n\npub struct DriverState<'d> {\n    pub mac_tx: Mutex<CriticalSectionRawMutex, MacTx<'d>>,\n    pub mac_rx: MacRx<'d>,\n    pub rx_event_channel: ZeroCopyPubSub<CriticalSectionRawMutex, MacEvent<'d>>,\n    pub rx_data_channel: Channel<CriticalSectionRawMutex, MacEvent<'d>, 1>,\n    pub tx_data_channel: Channel<CriticalSectionRawMutex, (&'d mut [u8; MTU], usize), BUF_SIZE>,\n    pub tx_buf_channel: Channel<CriticalSectionRawMutex, &'d mut [u8; MTU], BUF_SIZE>,\n    pub tx_buf_queue: [[u8; MTU]; BUF_SIZE],\n    pub network_state: blocking_mutex::Mutex<CriticalSectionRawMutex, RefCell<NetworkState>>,\n}\n\nimpl<'d> DriverState<'d> {\n    pub const fn new(mac: Mac<'d>) -> Self {\n        let (mac_rx, mac_tx) = mac.split();\n        let mac_tx = Mutex::new(mac_tx);\n\n        Self {\n            mac_tx,\n            mac_rx,\n            rx_event_channel: ZeroCopyPubSub::new(RefCell::new(None)),\n            rx_data_channel: Channel::new(),\n            tx_data_channel: Channel::new(),\n            tx_buf_channel: Channel::new(),\n            tx_buf_queue: [[0u8; MTU]; BUF_SIZE],\n            network_state: blocking_mutex::Mutex::new(RefCell::new(NetworkState::new())),\n        }\n    }\n}\n\npub struct Driver<'d> {\n    tx_data_channel: &'d Channel<CriticalSectionRawMutex, (&'d mut [u8; MTU], usize), BUF_SIZE>,\n    tx_buf_channel: &'d Channel<CriticalSectionRawMutex, &'d mut [u8; MTU], BUF_SIZE>,\n    rx_data_channel: &'d Channel<CriticalSectionRawMutex, MacEvent<'d>, 1>,\n    network_state: &'d blocking_mutex::Mutex<CriticalSectionRawMutex, RefCell<NetworkState>>,\n}\n\nimpl<'d> Driver<'d> {\n    pub fn new(\n        driver_state: &'d mut DriverState<'d>,\n        short_address: [u8; 2],\n        mac_address: [u8; 8],\n    ) -> (Self, Runner<'d>, Control<'d>) {\n        (\n            Self {\n                tx_data_channel: &driver_state.tx_data_channel,\n                tx_buf_channel: &driver_state.tx_buf_channel,\n                rx_data_channel: &driver_state.rx_data_channel,\n                network_state: &driver_state.network_state,\n            },\n            Runner::new(\n                &driver_state.rx_event_channel,\n                &driver_state.rx_data_channel,\n                &mut driver_state.mac_rx,\n                &driver_state.tx_data_channel,\n                &driver_state.tx_buf_channel,\n                &driver_state.mac_tx,\n                &mut driver_state.tx_buf_queue,\n                &driver_state.network_state,\n                short_address,\n                mac_address,\n            ),\n            Control::new(\n                &driver_state.rx_event_channel,\n                &driver_state.mac_tx,\n                &driver_state.network_state,\n            ),\n        )\n    }\n}\n\nimpl<'d> embassy_net_driver::Driver for Driver<'d> {\n    // type RxToken<'a> = RxToken<'a, 'd> where Self: 'a;\n    // type TxToken<'a> = TxToken<'a, 'd> where Self: 'a;\n    type RxToken<'a>\n        = RxToken<'d>\n    where\n        Self: 'a;\n    type TxToken<'a>\n        = TxToken<'d>\n    where\n        Self: 'a;\n\n    fn receive(&mut self, cx: &mut Context) -> Option<(Self::RxToken<'_>, Self::TxToken<'_>)> {\n        if self.rx_data_channel.poll_ready_to_receive(cx).is_ready()\n            && self.tx_buf_channel.poll_ready_to_receive(cx).is_ready()\n        {\n            Some((\n                RxToken {\n                    rx: self.rx_data_channel,\n                },\n                TxToken {\n                    tx: self.tx_data_channel,\n                    tx_buf: self.tx_buf_channel,\n                },\n            ))\n        } else {\n            None\n        }\n    }\n\n    fn transmit(&mut self, cx: &mut Context) -> Option<Self::TxToken<'_>> {\n        if self.tx_buf_channel.poll_ready_to_receive(cx).is_ready() {\n            Some(TxToken {\n                tx: self.tx_data_channel,\n                tx_buf: self.tx_buf_channel,\n            })\n        } else {\n            None\n        }\n    }\n\n    fn capabilities(&self) -> Capabilities {\n        let mut caps = Capabilities::default();\n        caps.max_transmission_unit = MTU;\n        // caps.max_burst_size = Some(self.tx.len());\n        caps\n    }\n\n    fn link_state(&mut self, cx: &mut Context) -> LinkState {\n        critical_section::with(|cs| {\n            let network_state = self.network_state.borrow(cs).borrow_mut();\n\n            // Unconditionally register the waker to avoid a race\n            network_state.link_waker.register(cx.waker());\n            network_state.link_state\n        })\n    }\n\n    fn hardware_address(&self) -> HardwareAddress {\n        HardwareAddress::Ieee802154(critical_section::with(|cs| {\n            self.network_state.borrow(cs).borrow().mac_addr\n        }))\n    }\n}\n\npub struct RxToken<'d> {\n    rx: &'d Channel<CriticalSectionRawMutex, MacEvent<'d>, 1>,\n}\n\nimpl<'d> embassy_net_driver::RxToken for RxToken<'d> {\n    fn consume<R, F>(self, f: F) -> R\n    where\n        F: FnOnce(&mut [u8]) -> R,\n    {\n        let mut buffer = [0u8; MTU];\n        match self.rx.try_receive().unwrap() {\n            MacEvent::McpsDataInd(data_event) => write_frame_from_data_indication(data_event, &mut buffer),\n            MacEvent::MlmeBeaconNotifyInd(data_event) => write_frame_from_beacon_indication(data_event, &mut buffer),\n            _ => {}\n        };\n\n        f(&mut buffer[..])\n    }\n}\n\npub struct TxToken<'d> {\n    tx: &'d Channel<CriticalSectionRawMutex, (&'d mut [u8; MTU], usize), BUF_SIZE>,\n    tx_buf: &'d Channel<CriticalSectionRawMutex, &'d mut [u8; MTU], BUF_SIZE>,\n}\n\nimpl<'d> embassy_net_driver::TxToken for TxToken<'d> {\n    fn consume<R, F>(self, len: usize, f: F) -> R\n    where\n        F: FnOnce(&mut [u8]) -> R,\n    {\n        // Only valid tx buffers should be put into the queue\n        let buf = self.tx_buf.try_receive().unwrap();\n        let r = f(&mut buf[..len]);\n\n        // The tx channel should always be of equal capacity to the tx_buf channel\n        self.tx.try_send((buf, len)).unwrap();\n\n        r\n    }\n}\n"
  },
  {
    "path": "embassy-stm32-wpan/src/wb55/mac/event.rs",
    "content": "use core::{mem, ptr};\n\nuse super::indications::{\n    AssociateIndication, BeaconNotifyIndication, CommStatusIndication, DataIndication, DisassociateIndication,\n    DpsIndication, GtsIndication, OrphanIndication, PollIndication, SyncLossIndication,\n};\nuse super::responses::{\n    AssociateConfirm, CalibrateConfirm, DataConfirm, DisassociateConfirm, DpsConfirm, GetConfirm, GtsConfirm,\n    PollConfirm, PurgeConfirm, ResetConfirm, RxEnableConfirm, ScanConfirm, SetConfirm, SoundingConfirm, StartConfirm,\n};\nuse crate::evt::{EvtBox, MemoryManager};\nuse crate::mac::opcodes::OpcodeM0ToM4;\nuse crate::sub::mac::{self, MacRx};\n\npub(crate) trait ParseableMacEvent: Sized {\n    fn from_buffer<'a>(buf: &'a [u8]) -> Result<&'a Self, ()> {\n        if buf.len() < mem::size_of::<Self>() {\n            Err(())\n        } else {\n            Ok(unsafe { &*(buf as *const _ as *const Self) })\n        }\n    }\n}\n\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\n#[derive(Debug)]\npub enum MacEvent<'a> {\n    MlmeAssociateCnf(&'a AssociateConfirm),\n    MlmeDisassociateCnf(&'a DisassociateConfirm),\n    MlmeGetCnf(&'a GetConfirm),\n    MlmeGtsCnf(&'a GtsConfirm),\n    MlmeResetCnf(&'a ResetConfirm),\n    MlmeRxEnableCnf(&'a RxEnableConfirm),\n    MlmeScanCnf(&'a ScanConfirm),\n    MlmeSetCnf(&'a SetConfirm),\n    MlmeStartCnf(&'a StartConfirm),\n    MlmePollCnf(&'a PollConfirm),\n    MlmeDpsCnf(&'a DpsConfirm),\n    MlmeSoundingCnf(&'a SoundingConfirm),\n    MlmeCalibrateCnf(&'a CalibrateConfirm),\n    McpsDataCnf(&'a DataConfirm),\n    McpsPurgeCnf(&'a PurgeConfirm),\n    MlmeAssociateInd(&'a AssociateIndication),\n    MlmeDisassociateInd(&'a DisassociateIndication),\n    MlmeBeaconNotifyInd(&'a BeaconNotifyIndication),\n    MlmeCommStatusInd(&'a CommStatusIndication),\n    MlmeGtsInd(&'a GtsIndication),\n    MlmeOrphanInd(&'a OrphanIndication),\n    MlmeSyncLossInd(&'a SyncLossIndication),\n    MlmeDpsInd(&'a DpsIndication),\n    McpsDataInd(&'a DataIndication),\n    MlmePollInd(&'a PollIndication),\n}\n\nimpl<'a> MacEvent<'a> {\n    pub(crate) fn new(event_box: EvtBox<MacRx>) -> Result<Self, ()> {\n        let payload = event_box.payload();\n        let opcode = u16::from_le_bytes(payload[0..2].try_into().unwrap());\n\n        let opcode = OpcodeM0ToM4::try_from(opcode)?;\n        let buf = &payload[2..];\n\n        // To avoid re-parsing the opcode, we store the result of the parse\n        // this requires use of unsafe because rust cannot assume that a reference will become\n        // invalid when the underlying result is moved. However, because we refer to a \"heap\"\n        // allocation, the underlying reference will not move until the struct is dropped.\n\n        let mac_event = match opcode {\n            OpcodeM0ToM4::MlmeAssociateCnf => {\n                MacEvent::MlmeAssociateCnf(unsafe { &*(AssociateConfirm::from_buffer(buf)? as *const _) })\n            }\n            OpcodeM0ToM4::MlmeDisassociateCnf => {\n                MacEvent::MlmeDisassociateCnf(unsafe { &*(DisassociateConfirm::from_buffer(buf)? as *const _) })\n            }\n            OpcodeM0ToM4::MlmeGetCnf => MacEvent::MlmeGetCnf(unsafe { &*(GetConfirm::from_buffer(buf)? as *const _) }),\n            OpcodeM0ToM4::MlmeGtsCnf => MacEvent::MlmeGtsCnf(unsafe { &*(GtsConfirm::from_buffer(buf)? as *const _) }),\n            OpcodeM0ToM4::MlmeResetCnf => {\n                MacEvent::MlmeResetCnf(unsafe { &*(ResetConfirm::from_buffer(buf)? as *const _) })\n            }\n            OpcodeM0ToM4::MlmeRxEnableCnf => {\n                MacEvent::MlmeRxEnableCnf(unsafe { &*(RxEnableConfirm::from_buffer(buf)? as *const _) })\n            }\n            OpcodeM0ToM4::MlmeScanCnf => {\n                MacEvent::MlmeScanCnf(unsafe { &*(ScanConfirm::from_buffer(buf)? as *const _) })\n            }\n            OpcodeM0ToM4::MlmeSetCnf => MacEvent::MlmeSetCnf(unsafe { &*(SetConfirm::from_buffer(buf)? as *const _) }),\n            OpcodeM0ToM4::MlmeStartCnf => {\n                MacEvent::MlmeStartCnf(unsafe { &*(StartConfirm::from_buffer(buf)? as *const _) })\n            }\n            OpcodeM0ToM4::MlmePollCnf => {\n                MacEvent::MlmePollCnf(unsafe { &*(PollConfirm::from_buffer(buf)? as *const _) })\n            }\n            OpcodeM0ToM4::MlmeDpsCnf => MacEvent::MlmeDpsCnf(unsafe { &*(DpsConfirm::from_buffer(buf)? as *const _) }),\n            OpcodeM0ToM4::MlmeSoundingCnf => {\n                MacEvent::MlmeSoundingCnf(unsafe { &*(SoundingConfirm::from_buffer(buf)? as *const _) })\n            }\n            OpcodeM0ToM4::MlmeCalibrateCnf => {\n                MacEvent::MlmeCalibrateCnf(unsafe { &*(CalibrateConfirm::from_buffer(buf)? as *const _) })\n            }\n            OpcodeM0ToM4::McpsDataCnf => {\n                MacEvent::McpsDataCnf(unsafe { &*(DataConfirm::from_buffer(buf)? as *const _) })\n            }\n            OpcodeM0ToM4::McpsPurgeCnf => {\n                MacEvent::McpsPurgeCnf(unsafe { &*(PurgeConfirm::from_buffer(buf)? as *const _) })\n            }\n            OpcodeM0ToM4::MlmeAssociateInd => {\n                MacEvent::MlmeAssociateInd(unsafe { &*(AssociateIndication::from_buffer(buf)? as *const _) })\n            }\n            OpcodeM0ToM4::MlmeDisassociateInd => {\n                MacEvent::MlmeDisassociateInd(unsafe { &*(DisassociateIndication::from_buffer(buf)? as *const _) })\n            }\n            OpcodeM0ToM4::MlmeBeaconNotifyInd => {\n                MacEvent::MlmeBeaconNotifyInd(unsafe { &*(BeaconNotifyIndication::from_buffer(buf)? as *const _) })\n            }\n            OpcodeM0ToM4::MlmeCommStatusInd => {\n                MacEvent::MlmeCommStatusInd(unsafe { &*(CommStatusIndication::from_buffer(buf)? as *const _) })\n            }\n            OpcodeM0ToM4::MlmeGtsInd => {\n                MacEvent::MlmeGtsInd(unsafe { &*(GtsIndication::from_buffer(buf)? as *const _) })\n            }\n            OpcodeM0ToM4::MlmeOrphanInd => {\n                MacEvent::MlmeOrphanInd(unsafe { &*(OrphanIndication::from_buffer(buf)? as *const _) })\n            }\n            OpcodeM0ToM4::MlmeSyncLossInd => {\n                MacEvent::MlmeSyncLossInd(unsafe { &*(SyncLossIndication::from_buffer(buf)? as *const _) })\n            }\n            OpcodeM0ToM4::MlmeDpsInd => {\n                MacEvent::MlmeDpsInd(unsafe { &*(DpsIndication::from_buffer(buf)? as *const _) })\n            }\n            OpcodeM0ToM4::McpsDataInd => {\n                MacEvent::McpsDataInd(unsafe { &*(DataIndication::from_buffer(buf)? as *const _) })\n            }\n            OpcodeM0ToM4::MlmePollInd => {\n                MacEvent::MlmePollInd(unsafe { &*(PollIndication::from_buffer(buf)? as *const _) })\n            }\n        };\n\n        // Forget the event box so that drop isn't called\n        // We want to handle the lifetime ourselves\n\n        mem::forget(event_box);\n\n        Ok(mac_event)\n    }\n}\n\nunsafe impl<'a> Send for MacEvent<'a> {}\n\nimpl<'a> Drop for MacEvent<'a> {\n    fn drop(&mut self) {\n        unsafe { mac::MacRx::drop_event_packet(ptr::null_mut()) };\n    }\n}\n"
  },
  {
    "path": "embassy-stm32-wpan/src/wb55/mac/indications.rs",
    "content": "use core::slice;\n\nuse smoltcp::wire::Ieee802154FrameType;\nuse smoltcp::wire::ieee802154::Frame;\n\nuse super::consts::MAX_PENDING_ADDRESS;\nuse super::event::ParseableMacEvent;\nuse super::typedefs::{\n    AddressMode, Capabilities, DisassociationReason, KeyIdMode, MacAddress, MacChannel, MacStatus, PanDescriptor,\n    PanId, SecurityLevel,\n};\nuse crate::mac::typedefs::MacAddressAndMode;\n\n/// MLME ASSOCIATE Indication which will be used by the MAC\n/// to indicate the reception of an association request command\n#[repr(C)]\n#[derive(Debug)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub struct AssociateIndication {\n    /// Extended address of the device requesting association\n    pub device_address: [u8; 8],\n    /// Operational capabilities of the device requesting association\n    pub capability_information: Capabilities,\n    /// Security level purportedly used by the received MAC command frame\n    pub security_level: SecurityLevel,\n    /// The mode used to identify the key used by the originator of frame\n    pub key_id_mode: KeyIdMode,\n    /// Index of the key used by the originator of the received frame\n    pub key_index: u8,\n    /// The originator of the key used by the originator of the received frame\n    pub key_source: [u8; 8],\n}\n\nimpl ParseableMacEvent for AssociateIndication {}\n\n/// MLME DISASSOCIATE indication which will be used to send\n/// disassociation indication to the application.\n#[repr(C)]\n#[derive(Debug)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub struct DisassociateIndication {\n    /// Extended address of the device requesting association\n    pub device_address: [u8; 8],\n    /// The reason for the disassociation\n    pub disassociation_reason: DisassociationReason,\n    /// The security level to be used\n    pub security_level: SecurityLevel,\n    /// The mode used to identify the key to be used\n    pub key_id_mode: KeyIdMode,\n    /// The index of the key to be used\n    pub key_index: u8,\n    /// The originator of the key to be used\n    pub key_source: [u8; 8],\n}\n\nimpl ParseableMacEvent for DisassociateIndication {}\n\n/// MLME BEACON NOTIIFY Indication which is used to send parameters contained\n/// within a beacon frame received by the MAC to the application\n#[repr(C)]\n#[derive(Debug)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub struct BeaconNotifyIndication {\n    /// he set of octets comprising the beacon payload to be transferred\n    /// from the MAC sublayer entity to the next higher layer\n    pub sdu_ptr: *const u8,\n    /// The PAN Descriptor for the received beacon\n    pub pan_descriptor: PanDescriptor,\n    /// The list of addresses of the devices\n    pub addr_list: [MacAddress; MAX_PENDING_ADDRESS],\n    /// Beacon Sequence Number\n    pub bsn: u8,\n    /// The beacon pending address specification\n    pub pend_addr_spec: u8,\n    /// Number of octets contained in the beacon payload of the beacon frame\n    pub sdu_length: u8,\n}\n\nimpl ParseableMacEvent for BeaconNotifyIndication {}\n\nimpl BeaconNotifyIndication {\n    pub fn payload<'a>(&'a self) -> &'a mut [u8] {\n        unsafe { slice::from_raw_parts_mut(self.sdu_ptr as *mut _, self.sdu_length as usize) }\n    }\n}\n\npub fn write_frame_from_beacon_indication<'a, T: AsRef<[u8]> + AsMut<[u8]>>(\n    data: &'a BeaconNotifyIndication,\n    buffer: &'a mut T,\n) {\n    let mut frame = Frame::new_unchecked(buffer);\n\n    frame.set_frame_type(Ieee802154FrameType::Beacon);\n    frame.set_sequence_number(data.bsn);\n}\n\n/// MLME COMM STATUS Indication which is used by the MAC to indicate a communications status\n#[repr(C)]\n#[derive(Debug)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub struct CommStatusIndication {\n    /// The 16-bit PAN identifier of the device from which the frame\n    /// was received or to which the frame was being sent\n    pub pan_id: PanId,\n    /// Source addressing mode\n    pub src_addr_mode: AddressMode,\n    /// Destination addressing mode\n    pub dst_addr_mode: AddressMode,\n    /// Source address\n    pub src_address: MacAddress,\n    /// Destination address\n    pub dst_address: MacAddress,\n    /// The communications status\n    pub status: MacStatus,\n    /// Security level to be used\n    pub security_level: SecurityLevel,\n    /// Mode used to identify the key to be used\n    pub key_id_mode: KeyIdMode,\n    /// Index of the key to be used\n    pub key_index: u8,\n    /// Originator of the key to be used\n    pub key_source: [u8; 8],\n}\n\nimpl ParseableMacEvent for CommStatusIndication {}\n\n/// MLME GTS Indication indicates that a GTS has been allocated or that a\n/// previously allocated GTS has been deallocated\n#[repr(C)]\n#[derive(Debug)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub struct GtsIndication {\n    /// The short address of the device that has been allocated or deallocated a GTS\n    pub device_address: [u8; 2],\n    /// The characteristics of the GTS\n    pub gts_characteristics: u8,\n    /// Security level to be used\n    pub security_level: SecurityLevel,\n    /// Mode used to identify the key to be used\n    pub key_id_mode: KeyIdMode,\n    /// Index of the key to be used\n    pub key_index: u8,\n    /// byte stuffing to keep 32 bit alignment\n    a_stuffing: [u8; 2],\n    /// Originator of the key to be used\n    pub key_source: [u8; 8],\n}\n\nimpl ParseableMacEvent for GtsIndication {}\n\n/// MLME ORPHAN Indication which is used by the coordinator to notify the\n/// application of the presence of an orphaned device\n#[repr(C)]\n#[derive(Debug)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub struct OrphanIndication {\n    /// Extended address of the orphaned device\n    pub orphan_address: [u8; 8],\n    /// Originator of the key used by the originator of the received frame\n    pub key_source: [u8; 8],\n    /// Security level purportedly used by the received MAC command frame\n    pub security_level: SecurityLevel,\n    /// Mode used to identify the key used by originator of received frame\n    pub key_id_mode: KeyIdMode,\n    /// Index of the key used by the originator of the received frame\n    pub key_index: u8,\n    /// byte stuffing to keep 32 bit alignment\n    a_stuffing: [u8; 1],\n}\n\nimpl ParseableMacEvent for OrphanIndication {}\n\n/// MLME SYNC LOSS Indication which is used by the MAC to indicate the loss\n/// of synchronization with the coordinator\n#[repr(C)]\n#[derive(Debug)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub struct SyncLossIndication {\n    /// The PAN identifier with which the device lost synchronization or to which it was realigned\n    pub pan_id: PanId,\n    /// The reason that synchronization was lost\n    pub loss_reason: u8,\n    /// The logical channel on which the device lost synchronization or to whi\n    pub channel_number: MacChannel,\n    /// The channel page on which the device lost synchronization or to which\n    pub channel_page: u8,\n    /// The security level used by the received MAC frame\n    pub security_level: SecurityLevel,\n    /// Mode used to identify the key used by originator of received frame\n    pub key_id_mode: KeyIdMode,\n    /// Index of the key used by the originator of the received frame\n    pub key_index: u8,\n    /// Originator of the key used by the originator of the received frame\n    pub key_source: [u8; 8],\n}\n\nimpl ParseableMacEvent for SyncLossIndication {}\n\n/// MLME DPS Indication which indicates the expiration of the DPSIndexDuration\n///  and the resetting of the DPS values in the PHY\n#[repr(C)]\n#[derive(Debug)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub struct DpsIndication {\n    /// byte stuffing to keep 32 bit alignment\n    a_stuffing: [u8; 4],\n}\n\nimpl ParseableMacEvent for DpsIndication {}\n\n#[repr(C)]\n#[derive(Debug)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub struct DataIndication {\n    /// Pointer to the set of octets forming the MSDU being indicated  \n    pub msdu_ptr: *const u8,\n    /// Source addressing mode used  \n    pub src_addr_mode: AddressMode,\n    /// Source PAN ID   \n    pub src_pan_id: PanId,\n    /// Source address  \n    pub src_address: MacAddress,\n    /// Destination addressing mode used  \n    pub dst_addr_mode: AddressMode,\n    /// Destination PAN ID   \n    pub dst_pan_id: PanId,\n    /// Destination address  \n    pub dst_address: MacAddress,\n    /// The number of octets contained in the MSDU being indicated  \n    pub msdu_length: u8,\n    /// QI value measured during reception of the MPDU\n    pub mpdu_link_quality: u8,\n    /// The data sequence number of the received data frame  \n    pub dsn: u8,\n    /// The time, in symbols, at which the data were received  \n    pub time_stamp: [u8; 4],\n    /// The security level purportedly used by the received data frame  \n    security_level: SecurityLevel,\n    /// Mode used to identify the key used by originator of received frame  \n    key_id_mode: KeyIdMode,\n    /// The originator of the key  \n    pub key_source: [u8; 8],\n    /// The index of the key  \n    pub key_index: u8,\n    /// he pulse repetition value of the received PPDU\n    pub uwbprf: u8,\n    /// The preamble symbol repetitions of the UWB PHY frame  \n    pub uwn_preamble_symbol_repetitions: u8,\n    /// Indicates the data rate\n    pub datrate: u8,\n    /// time units corresponding to an RMARKER at the antenna at the end of a ranging exchange,  \n    pub ranging_received: u8,\n    pub ranging_counter_start: u32,\n    pub ranging_counter_stop: u32,\n    /// ime units in a message exchange over which the tracking offset was measured\n    pub ranging_tracking_interval: u32,\n    /// time units slipped or advanced by the radio tracking system  \n    pub ranging_offset: u32,\n    /// The FoM characterizing the ranging measurement\n    pub ranging_fom: u8,\n    /// The Received Signal Strength Indicator measured\n    pub rssi: u8,\n}\n\nimpl ParseableMacEvent for DataIndication {}\n\nimpl DataIndication {\n    pub fn payload<'a>(&'a self) -> &'a mut [u8] {\n        unsafe { slice::from_raw_parts_mut(self.msdu_ptr as *mut _, self.msdu_length as usize) }\n    }\n}\n\npub fn write_frame_from_data_indication<'a, T: AsRef<[u8]> + AsMut<[u8]>>(data: &'a DataIndication, buffer: &'a mut T) {\n    let mut frame = Frame::new_unchecked(buffer);\n\n    frame.set_frame_type(Ieee802154FrameType::Data);\n    frame.set_src_addr(MacAddressAndMode(data.src_address, data.src_addr_mode).into());\n    frame.set_dst_addr(MacAddressAndMode(data.dst_address, data.dst_addr_mode).into());\n    frame.set_dst_pan_id(data.dst_pan_id.into());\n    frame.set_src_pan_id(data.src_pan_id.into());\n    frame.set_sequence_number(data.dsn);\n    frame.set_security_enabled(data.security_level == SecurityLevel::Secured);\n\n    // No way around the copy with the current API\n    frame.payload_mut().unwrap().copy_from_slice(data.payload());\n}\n\n/// MLME POLL Indication which will be used for indicating the Data Request\n/// reception to upper layer as defined in Zigbee r22 - D.8.2\n#[repr(C)]\n#[derive(Debug)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub struct PollIndication {\n    /// addressing mode used\n    pub addr_mode: AddressMode,\n    /// Poll requester address\n    pub request_address: MacAddress,\n}\n\nimpl ParseableMacEvent for PollIndication {}\n"
  },
  {
    "path": "embassy-stm32-wpan/src/wb55/mac/macros.rs",
    "content": "#[macro_export]\nmacro_rules! numeric_enum {\n    (#[repr($repr:ident)]\n     $(#$attrs:tt)* $vis:vis enum $name:ident {\n        $($(#$enum_attrs:tt)* $enum:ident = $constant:expr),* $(,)?\n    } ) => {\n        #[repr($repr)]\n        $(#$attrs)*\n        $vis enum $name {\n            $($(#$enum_attrs)* $enum = $constant),*\n        }\n\n        impl ::core::convert::TryFrom<$repr> for $name {\n            type Error = ();\n\n            fn try_from(value: $repr) -> ::core::result::Result<Self, ()> {\n                match value {\n                    $($constant => Ok( $name :: $enum ),)*\n                    _ => Err(())\n                }\n            }\n        }\n\n        impl ::core::convert::From<$name> for $repr {\n            fn from(value: $name) -> $repr {\n                match value {\n                    $($name :: $enum => $constant,)*\n                }\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "embassy-stm32-wpan/src/wb55/mac/mod.rs",
    "content": "pub mod commands;\nmod consts;\npub mod control;\nmod driver;\npub mod event;\npub mod indications;\nmod macros;\nmod opcodes;\npub mod responses;\npub mod runner;\npub mod typedefs;\n\npub use crate::mac::control::Control;\npub use crate::mac::driver::{Driver, DriverState};\npub use crate::mac::runner::Runner;\n\nconst MTU: usize = 127;\n"
  },
  {
    "path": "embassy-stm32-wpan/src/wb55/mac/opcodes.rs",
    "content": "const ST_VENDOR_OGF: u16 = 0x3F;\nconst MAC_802_15_4_CMD_OPCODE_OFFSET: u16 = 0x280;\n\nconst fn opcode(ocf: u16) -> isize {\n    ((ST_VENDOR_OGF << 9) | (MAC_802_15_4_CMD_OPCODE_OFFSET + ocf)) as isize\n}\n\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub enum OpcodeM4ToM0 {\n    MlmeAssociateReq = opcode(0x00),\n    MlmeAssociateRes = opcode(0x01),\n    MlmeDisassociateReq = opcode(0x02),\n    MlmeGetReq = opcode(0x03),\n    MlmeGtsReq = opcode(0x04),\n    MlmeOrphanRes = opcode(0x05),\n    MlmeResetReq = opcode(0x06),\n    MlmeRxEnableReq = opcode(0x07),\n    MlmeScanReq = opcode(0x08),\n    MlmeSetReq = opcode(0x09),\n    MlmeStartReq = opcode(0x0A),\n    MlmeSyncReq = opcode(0x0B),\n    MlmePollReq = opcode(0x0C),\n    MlmeDpsReq = opcode(0x0D),\n    MlmeSoundingReq = opcode(0x0E),\n    MlmeCalibrateReq = opcode(0x0F),\n    McpsDataReq = opcode(0x10),\n    McpsPurgeReq = opcode(0x11),\n}\n\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub enum OpcodeM0ToM4 {\n    MlmeAssociateCnf = 0x00,\n    MlmeDisassociateCnf,\n    MlmeGetCnf,\n    MlmeGtsCnf,\n    MlmeResetCnf,\n    MlmeRxEnableCnf,\n    MlmeScanCnf,\n    MlmeSetCnf,\n    MlmeStartCnf,\n    MlmePollCnf,\n    MlmeDpsCnf,\n    MlmeSoundingCnf,\n    MlmeCalibrateCnf,\n    McpsDataCnf,\n    McpsPurgeCnf,\n    MlmeAssociateInd,\n    MlmeDisassociateInd,\n    MlmeBeaconNotifyInd,\n    MlmeCommStatusInd,\n    MlmeGtsInd,\n    MlmeOrphanInd,\n    MlmeSyncLossInd,\n    MlmeDpsInd,\n    McpsDataInd,\n    MlmePollInd,\n}\n\nimpl TryFrom<u16> for OpcodeM0ToM4 {\n    type Error = ();\n\n    fn try_from(value: u16) -> Result<Self, Self::Error> {\n        match value {\n            0 => Ok(Self::MlmeAssociateCnf),\n            1 => Ok(Self::MlmeDisassociateCnf),\n            2 => Ok(Self::MlmeGetCnf),\n            3 => Ok(Self::MlmeGtsCnf),\n            4 => Ok(Self::MlmeResetCnf),\n            5 => Ok(Self::MlmeRxEnableCnf),\n            6 => Ok(Self::MlmeScanCnf),\n            7 => Ok(Self::MlmeSetCnf),\n            8 => Ok(Self::MlmeStartCnf),\n            9 => Ok(Self::MlmePollCnf),\n            10 => Ok(Self::MlmeDpsCnf),\n            11 => Ok(Self::MlmeSoundingCnf),\n            12 => Ok(Self::MlmeCalibrateCnf),\n            13 => Ok(Self::McpsDataCnf),\n            14 => Ok(Self::McpsPurgeCnf),\n            15 => Ok(Self::MlmeAssociateInd),\n            16 => Ok(Self::MlmeDisassociateInd),\n            17 => Ok(Self::MlmeBeaconNotifyInd),\n            18 => Ok(Self::MlmeCommStatusInd),\n            19 => Ok(Self::MlmeGtsInd),\n            20 => Ok(Self::MlmeOrphanInd),\n            21 => Ok(Self::MlmeSyncLossInd),\n            22 => Ok(Self::MlmeDpsInd),\n            23 => Ok(Self::McpsDataInd),\n            24 => Ok(Self::MlmePollInd),\n            _ => Err(()),\n        }\n    }\n}\n"
  },
  {
    "path": "embassy-stm32-wpan/src/wb55/mac/responses.rs",
    "content": "use super::consts::{MAX_ED_SCAN_RESULTS_SUPPORTED, MAX_PAN_DESC_SUPPORTED, MAX_SOUNDING_LIST_SUPPORTED};\nuse super::event::ParseableMacEvent;\nuse super::typedefs::{\n    AddressMode, AssociationStatus, KeyIdMode, MacAddress, MacStatus, PanDescriptor, PanId, PibId, ScanType,\n    SecurityLevel,\n};\n\n/// MLME ASSOCIATE Confirm used to inform of the initiating device whether\n/// its request to associate was successful or unsuccessful\n#[repr(C)]\n#[derive(Debug)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub struct AssociateConfirm {\n    /// short address allocated by the coordinator on successful association\n    pub assoc_short_address: [u8; 2],\n    /// status of the association request\n    pub status: AssociationStatus,\n    /// security level to be used\n    pub security_level: SecurityLevel,\n    /// the originator of the key to be used\n    pub key_source: [u8; 8],\n    /// the mode used to identify the key to be used\n    pub key_id_mode: KeyIdMode,\n    /// the index of the key to be used\n    pub key_index: u8,\n    /// byte stuffing to keep 32 bit alignment\n    a_stuffing: [u8; 2],\n}\n\nimpl ParseableMacEvent for AssociateConfirm {}\n\n/// MLME DISASSOCIATE Confirm used to send disassociation Confirmation to the application.\n#[repr(C)]\n#[derive(Debug)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub struct DisassociateConfirm {\n    /// status of the disassociation attempt\n    pub status: MacStatus,\n    /// device addressing mode used\n    pub device_addr_mode: AddressMode,\n    /// the identifier of the PAN of the device\n    pub device_pan_id: PanId,\n    /// device address\n    pub device_address: MacAddress,\n}\n\nimpl ParseableMacEvent for DisassociateConfirm {}\n\n///  MLME GET Confirm which requests information about a given PIB attribute\n#[repr(C)]\n#[derive(Debug)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub struct GetConfirm {\n    /// The pointer to the value of the PIB attribute attempted to read\n    pub pib_attribute_value_ptr: *const u8,\n    /// Status of the GET attempt\n    pub status: MacStatus,\n    /// The name of the PIB attribute attempted to read\n    pub pib_attribute: PibId,\n    /// The lenght of the PIB attribute Value return\n    pub pib_attribute_value_len: u8,\n    /// byte stuffing to keep 32 bit alignment\n    a_stuffing: [u8; 1],\n}\n\nimpl ParseableMacEvent for GetConfirm {}\n\n/// MLME GTS Confirm which eports the results of a request to allocate a new GTS\n/// or to deallocate an existing GTS\n#[repr(C)]\n#[derive(Debug)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub struct GtsConfirm {\n    /// The characteristics of the GTS\n    pub gts_characteristics: u8,\n    /// The status of the GTS reques\n    pub status: MacStatus,\n    /// byte stuffing to keep 32 bit alignment\n    a_stuffing: [u8; 2],\n}\n\nimpl ParseableMacEvent for GtsConfirm {}\n\n/// MLME RESET Confirm which is used to report the results of the reset operation\n#[repr(C)]\n#[derive(Debug)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub struct ResetConfirm {\n    /// The result of the reset operation\n    pub status: MacStatus,\n    /// byte stuffing to keep 32 bit alignment\n    a_stuffing: [u8; 3],\n}\n\nimpl ParseableMacEvent for ResetConfirm {}\n\n/// MLME RX ENABLE Confirm which is used to report the results of the attempt\n/// to enable or disable the receiver\n#[repr(C)]\n#[derive(Debug)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub struct RxEnableConfirm {\n    /// Result of the request to enable or disable the receiver\n    pub status: MacStatus,\n    /// byte stuffing to keep 32 bit alignment\n    a_stuffing: [u8; 3],\n}\n\nimpl ParseableMacEvent for RxEnableConfirm {}\n\n/// MLME SCAN Confirm which is used to report the result of the channel scan request\n#[repr(C)]\n#[derive(Debug)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub struct ScanConfirm {\n    /// Status of the scan request\n    pub status: MacStatus,\n    /// The type of scan performed\n    pub scan_type: ScanType,\n    /// Channel page on which the scan was performed\n    pub channel_page: u8,\n    /// Channels given in the request which were not scanned\n    pub unscanned_channels: [u8; 4],\n    /// Number of elements returned in the appropriate result lists\n    pub result_list_size: u8,\n    /// List of energy measurements\n    pub energy_detect_list: [u8; MAX_ED_SCAN_RESULTS_SUPPORTED],\n    /// List of PAN descriptors\n    pub pan_descriptor_list: [PanDescriptor; MAX_PAN_DESC_SUPPORTED],\n    /// Categorization of energy detected in channel\n    pub detected_category: u8,\n    ///  For UWB PHYs, the list of energy measurements taken\n    pub uwb_energy_detect_list: [u8; MAX_ED_SCAN_RESULTS_SUPPORTED],\n}\n\nimpl ParseableMacEvent for ScanConfirm {}\n\n/// MLME SET Confirm which reports the result of an attempt to write a value to a PIB attribute\n#[repr(C)]\n#[derive(Debug)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub struct SetConfirm {\n    /// The result of the set operation\n    pub status: MacStatus,\n    /// The name of the PIB attribute that was written\n    pub pin_attribute: PibId,\n    /// byte stuffing to keep 32 bit alignment\n    a_stuffing: [u8; 2],\n}\n\nimpl ParseableMacEvent for SetConfirm {}\n\n/// MLME START Confirm which is used to report the results of the attempt to\n/// start using a new superframe configuration\n#[repr(C)]\n#[derive(Debug)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub struct StartConfirm {\n    /// Result of the attempt to start using an updated superframe configuration\n    pub status: MacStatus,\n    /// byte stuffing to keep 32 bit alignment\n    a_stuffing: [u8; 3],\n}\n\nimpl ParseableMacEvent for StartConfirm {}\n\n/// MLME POLL Confirm which is used to report the result of a request to poll the coordinator for data\n#[repr(C)]\n#[derive(Debug)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub struct PollConfirm {\n    /// The status of the data request\n    pub status: MacStatus,\n    /// byte stuffing to keep 32 bit alignment\n    a_stuffing: [u8; 3],\n}\n\nimpl ParseableMacEvent for PollConfirm {}\n\n/// MLME DPS Confirm which  reports the results of the attempt to enable or disable the DPS\n#[repr(C)]\n#[derive(Debug)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub struct DpsConfirm {\n    /// The status of the DPS request\n    pub status: MacStatus,\n    /// byte stuffing to keep 32 bit alignment\n    a_stuffing: [u8; 3],\n}\n\nimpl ParseableMacEvent for DpsConfirm {}\n\n/// MLME SOUNDING Confirm which  reports the result of a request to the PHY to provide\n/// channel sounding information\n#[repr(C)]\n#[derive(Debug)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub struct SoundingConfirm {\n    /// Results of the sounding measurement\n    pub sounding_list: [u8; MAX_SOUNDING_LIST_SUPPORTED],\n\n    status: u8,\n}\n\nimpl ParseableMacEvent for SoundingConfirm {}\n\n/// MLME CALIBRATE Confirm which reports the result of a request to the PHY\n/// to provide internal propagation path information\n#[repr(C)]\n#[derive(Debug)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub struct CalibrateConfirm {\n    /// The status of the attempt to return sounding data\n    pub status: MacStatus,\n    /// byte stuffing to keep 32 bit alignment\n    a_stuffing: [u8; 3],\n    /// A count of the propagation time from the ranging counter\n    /// to the transmit antenna\n    pub cal_tx_rmaker_offset: u32,\n    /// A count of the propagation time from the receive antenna\n    /// to the ranging counter\n    pub cal_rx_rmaker_offset: u32,\n}\n\nimpl ParseableMacEvent for CalibrateConfirm {}\n\n/// MCPS DATA Confirm which will be used for reporting the results of\n/// MAC data related requests from the application\n#[repr(C)]\n#[derive(Debug)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub struct DataConfirm {\n    /// The handle associated with the MSDU being confirmed\n    pub msdu_handle: u8,\n    /// The time, in symbols, at which the data were transmitted\n    pub time_stamp: [u8; 4],\n    /// ranging status\n    pub ranging_received: u8,\n    /// The status of the last MSDU transmission\n    pub status: MacStatus,\n    /// time units corresponding to an RMARKER at the antenna at\n    /// the beginning of a ranging exchange\n    pub ranging_counter_start: u32,\n    /// time units corresponding to an RMARKER at the antenna\n    /// at the end of a ranging exchange\n    pub ranging_counter_stop: u32,\n    /// time units in a message exchange over which the tracking offset was measured\n    pub ranging_tracking_interval: u32,\n    /// time units slipped or advanced by the radio tracking system\n    pub ranging_offset: u32,\n    /// The FoM characterizing the ranging measurement\n    pub ranging_fom: u8,\n    /// byte stuffing to keep 32 bit alignment\n    a_stuffing: [u8; 3],\n}\n\nimpl ParseableMacEvent for DataConfirm {}\n\n/// MCPS PURGE Confirm which will be used by the  MAC to notify the application of\n/// the status of its request to purge an MSDU from the transaction queue\n#[repr(C)]\n#[derive(Debug)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub struct PurgeConfirm {\n    /// Handle associated with the MSDU requested to be purged from the transaction queue\n    pub msdu_handle: u8,\n    /// The status of the request\n    pub status: MacStatus,\n    /// byte stuffing to keep 32 bit alignment\n    a_stuffing: [u8; 2],\n}\n\nimpl ParseableMacEvent for PurgeConfirm {}\n"
  },
  {
    "path": "embassy-stm32-wpan/src/wb55/mac/runner.rs",
    "content": "use core::cell::RefCell;\n\nuse embassy_futures::join;\nuse embassy_sync::blocking_mutex;\nuse embassy_sync::blocking_mutex::raw::{CriticalSectionRawMutex, NoopRawMutex};\nuse embassy_sync::channel::Channel;\nuse embassy_sync::mutex::Mutex;\nuse embassy_sync::signal::Signal;\nuse smoltcp::wire::Ieee802154FrameType;\nuse smoltcp::wire::ieee802154::Frame;\n\nuse crate::mac::MTU;\nuse crate::mac::commands::*;\nuse crate::mac::driver::NetworkState;\nuse crate::mac::event::MacEvent;\nuse crate::sub::mac::{MacRx, MacTx};\n\npub type ZeroCopyPubSub<M, T> = blocking_mutex::Mutex<M, RefCell<Option<Signal<NoopRawMutex, T>>>>;\n\npub const BUF_SIZE: usize = 3;\n\npub struct Runner<'a> {\n    // rx event backpressure is already provided through the MacEvent drop mechanism\n    // therefore, we don't need to worry about overwriting events\n    rx_event_channel: &'a ZeroCopyPubSub<CriticalSectionRawMutex, MacEvent<'a>>,\n    rx_data_channel: &'a Channel<CriticalSectionRawMutex, MacEvent<'a>, 1>,\n    mac_rx: Mutex<NoopRawMutex, &'a mut MacRx<'a>>,\n\n    tx_data_channel: &'a Channel<CriticalSectionRawMutex, (&'a mut [u8; MTU], usize), BUF_SIZE>,\n    tx_buf_channel: &'a Channel<CriticalSectionRawMutex, &'a mut [u8; MTU], BUF_SIZE>,\n    mac_tx: &'a Mutex<CriticalSectionRawMutex, MacTx<'a>>,\n\n    #[allow(unused)]\n    network_state: &'a blocking_mutex::Mutex<CriticalSectionRawMutex, RefCell<NetworkState>>,\n}\n\nimpl<'a> Runner<'a> {\n    pub(crate) fn new(\n        rx_event_channel: &'a ZeroCopyPubSub<CriticalSectionRawMutex, MacEvent<'a>>,\n        rx_data_channel: &'a Channel<CriticalSectionRawMutex, MacEvent<'a>, 1>,\n        mac_rx: &'a mut MacRx<'a>,\n        tx_data_channel: &'a Channel<CriticalSectionRawMutex, (&'a mut [u8; MTU], usize), BUF_SIZE>,\n        tx_buf_channel: &'a Channel<CriticalSectionRawMutex, &'a mut [u8; MTU], BUF_SIZE>,\n        mac_tx: &'a Mutex<CriticalSectionRawMutex, MacTx<'a>>,\n        tx_buf_queue: &'a mut [[u8; MTU]; BUF_SIZE],\n        network_state: &'a blocking_mutex::Mutex<CriticalSectionRawMutex, RefCell<NetworkState>>,\n        short_address: [u8; 2],\n        mac_address: [u8; 8],\n    ) -> Self {\n        for buf in tx_buf_queue {\n            tx_buf_channel.try_send(buf).unwrap();\n        }\n\n        critical_section::with(|cs| {\n            let mut network_state = network_state.borrow(cs).borrow_mut();\n\n            network_state.mac_addr = mac_address;\n            network_state.short_addr = short_address;\n        });\n\n        Self {\n            rx_event_channel,\n            rx_data_channel,\n            mac_rx: Mutex::new(mac_rx),\n            tx_data_channel,\n            tx_buf_channel,\n            mac_tx,\n            network_state,\n        }\n    }\n\n    async fn send_request<T: MacCommand, U: TryInto<T>>(&self, frame: U) -> Result<(), ()>\n    where\n        (): From<<U as TryInto<T>>::Error>,\n    {\n        let request: T = frame.try_into()?;\n        self.mac_tx.lock().await.send_command(&request).await.map_err(|_| ())?;\n\n        Ok(())\n    }\n\n    pub async fn run(&'a self) -> ! {\n        join::join(\n            async {\n                loop {\n                    if let Ok(mac_event) = self.mac_rx.try_lock().unwrap().read().await {\n                        match mac_event {\n                            MacEvent::MlmeAssociateCnf(_)\n                            | MacEvent::MlmeDisassociateCnf(_)\n                            | MacEvent::MlmeGetCnf(_)\n                            | MacEvent::MlmeGtsCnf(_)\n                            | MacEvent::MlmeResetCnf(_)\n                            | MacEvent::MlmeRxEnableCnf(_)\n                            | MacEvent::MlmeScanCnf(_)\n                            | MacEvent::MlmeSetCnf(_)\n                            | MacEvent::MlmeStartCnf(_)\n                            | MacEvent::MlmePollCnf(_)\n                            | MacEvent::MlmeDpsCnf(_)\n                            | MacEvent::MlmeSoundingCnf(_)\n                            | MacEvent::MlmeCalibrateCnf(_)\n                            | MacEvent::McpsDataCnf(_)\n                            | MacEvent::McpsPurgeCnf(_) => {\n                                self.rx_event_channel.lock(|s| {\n                                    s.borrow().as_ref().map(|signal| signal.signal(mac_event));\n                                });\n                            }\n                            MacEvent::McpsDataInd(_) => {\n                                // Pattern should match driver\n                                self.rx_data_channel.send(mac_event).await;\n                            }\n                            _ => {\n                                debug!(\"unhandled mac event: {:#x}\", mac_event);\n                            }\n                        }\n                    }\n                }\n            },\n            async {\n                loop {\n                    let (buf, _) = self.tx_data_channel.receive().await;\n\n                    // Smoltcp has created this frame, so there's no need to reparse it.\n                    let frame = Frame::new_unchecked(&buf);\n\n                    let result: Result<(), ()> = match frame.frame_type() {\n                        Ieee802154FrameType::Beacon => Err(()),\n                        Ieee802154FrameType::Data => self.send_request::<DataRequest, _>(frame).await,\n                        Ieee802154FrameType::Acknowledgement => Err(()),\n                        Ieee802154FrameType::MacCommand => Err(()),\n                        Ieee802154FrameType::Multipurpose => Err(()),\n                        Ieee802154FrameType::FragmentOrFrak => Err(()),\n                        Ieee802154FrameType::Extended => Err(()),\n                        _ => Err(()),\n                    };\n\n                    if result.is_err() {\n                        debug!(\"failed to parse mac frame\");\n                    } else {\n                        trace!(\"data frame sent!\");\n                    }\n\n                    // The tx channel should always be of equal capacity to the tx_buf channel\n                    self.tx_buf_channel.try_send(buf).unwrap();\n                }\n            },\n        )\n        .await;\n\n        loop {}\n    }\n}\n"
  },
  {
    "path": "embassy-stm32-wpan/src/wb55/mac/typedefs.rs",
    "content": "use core::fmt::Debug;\n\nuse smoltcp::wire::ieee802154::{Address, AddressingMode, Pan};\n\nuse crate::numeric_enum;\n\n#[derive(Debug)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub enum MacError {\n    Error = 0x01,\n    NotImplemented = 0x02,\n    NotSupported = 0x03,\n    HardwareNotSupported = 0x04,\n    Undefined = 0x05,\n}\n\nimpl From<u8> for MacError {\n    fn from(value: u8) -> Self {\n        match value {\n            0x01 => Self::Error,\n            0x02 => Self::NotImplemented,\n            0x03 => Self::NotSupported,\n            0x04 => Self::HardwareNotSupported,\n            0x05 => Self::Undefined,\n            _ => Self::Undefined,\n        }\n    }\n}\n\nnumeric_enum! {\n    #[repr(u8)]\n    #[derive(Debug, Default)]\n    #[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\n    pub enum MacStatus {\n        #[default]\n        Success = 0x00,\n        Failure = 0xFF\n    }\n}\n\nnumeric_enum! {\n    #[repr(u8)]\n    /// this enum contains all the MAC PIB Ids\n    #[derive(Default, Debug)]\n    #[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\n    pub enum PibId {\n        // PHY\n        #[default]\n        CurrentChannel = 0x00,\n        ChannelsSupported = 0x01,\n        TransmitPower = 0x02,\n        CCAMode = 0x03,\n        CurrentPage = 0x04,\n        MaxFrameDuration = 0x05,\n        SHRDuration = 0x06,\n        SymbolsPerOctet = 0x07,\n\n        // MAC\n        AckWaitDuration = 0x40,\n        AssociationPermit = 0x41,\n        AutoRequest = 0x42,\n        BeaconPayload = 0x45,\n        BeaconPayloadLength = 0x46,\n        BeaconOrder = 0x47,\n        Bsn = 0x49,\n        CoordExtendedAdddress = 0x4A,\n        CoordShortAddress = 0x4B,\n        Dsn = 0x4C,\n        MaxFrameTotalWaitTime = 0x58,\n        MaxFrameRetries = 0x59,\n        PanId = 0x50,\n        ResponseWaitTime = 0x5A,\n        RxOnWhenIdle = 0x52,\n        SecurityEnabled = 0x5D,\n        ShortAddress = 0x53,\n        SuperframeOrder = 0x54,\n        TimestampSupported = 0x5C,\n        TransactionPersistenceTime = 0x55,\n        MaxBe = 0x57,\n        LifsPeriod = 0x5E,\n        SifsPeriod = 0x5F,\n        MaxCsmaBackoffs = 0x4E,\n        MinBe = 0x4F,\n        PanCoordinator = 0x10,\n        AssocPanCoordinator = 0x11,\n        ExtendedAddress = 0x6F,\n        AclEntryDescriptor = 0x70,\n        AclEntryDescriptorSize = 0x71,\n        DefaultSecurity = 0x72,\n        DefaultSecurityMaterialLength = 0x73,\n        DefaultSecurityMaterial = 0x74,\n        DefaultSecuritySuite = 0x75,\n        SecurityMode = 0x76,\n        CurrentAclEntries = 0x80,\n        DefaultSecurityExtendedAddress = 0x81,\n        AssociatedPanCoordinator = 0x56,\n        PromiscuousMode = 0x51,\n    }\n}\n\nnumeric_enum! {\n    #[repr(u8)]\n    #[derive(Default, Clone, Copy, Debug)]\n    #[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\n    pub enum AddressMode {\n        #[default]\n        NoAddress = 0x00,\n        Reserved = 0x01,\n        Short = 0x02,\n        Extended = 0x03,\n}\n}\n\nimpl TryFrom<AddressingMode> for AddressMode {\n    type Error = ();\n\n    fn try_from(value: AddressingMode) -> Result<Self, Self::Error> {\n        match value {\n            AddressingMode::Absent => Ok(Self::NoAddress),\n            AddressingMode::Extended => Ok(Self::Extended),\n            AddressingMode::Short => Ok(Self::Short),\n            AddressingMode::Unknown(_) => Err(()),\n        }\n    }\n}\n\n#[derive(Clone, Copy)]\npub union MacAddress {\n    pub short: [u8; 2],\n    pub extended: [u8; 8],\n}\n\nimpl From<Address> for MacAddress {\n    fn from(value: Address) -> Self {\n        match value {\n            Address::Short(addr) => Self { short: addr },\n            Address::Extended(addr) => Self { extended: addr },\n            Address::Absent => Self { short: [0u8; 2] },\n        }\n    }\n}\n\npub struct MacAddressAndMode(pub MacAddress, pub AddressMode);\n\nimpl From<MacAddressAndMode> for Address {\n    fn from(mac_address_and_mode: MacAddressAndMode) -> Self {\n        let address = mac_address_and_mode.0;\n        let mode = mac_address_and_mode.1;\n\n        match mode {\n            AddressMode::Short => Address::Short(unsafe { address.short }),\n            AddressMode::Extended => Address::Extended(unsafe { address.extended }),\n            AddressMode::NoAddress => Address::Absent,\n            AddressMode::Reserved => Address::Absent,\n        }\n    }\n}\n\nimpl Debug for MacAddress {\n    fn fmt(&self, fmt: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {\n        unsafe {\n            write!(\n                fmt,\n                \"MacAddress {{ short: {:?}, extended: {:?} }}\",\n                self.short, self.extended\n            )\n        }\n    }\n}\n\n#[cfg(feature = \"defmt\")]\nimpl defmt::Format for MacAddress {\n    fn format(&self, fmt: defmt::Formatter) {\n        unsafe {\n            defmt::write!(\n                fmt,\n                \"MacAddress {{ short: {}, extended: {} }}\",\n                self.short,\n                self.extended\n            )\n        }\n    }\n}\n\nimpl Default for MacAddress {\n    fn default() -> Self {\n        Self { short: [0, 0] }\n    }\n}\n\nimpl MacAddress {\n    pub const BROADCAST: Self = Self { short: [0xFF, 0xFF] };\n}\n\nimpl TryFrom<&[u8]> for MacAddress {\n    type Error = ();\n\n    fn try_from(buf: &[u8]) -> Result<Self, Self::Error> {\n        const SIZE: usize = 8;\n        if buf.len() < SIZE {\n            return Err(());\n        }\n\n        Ok(Self {\n            extended: [buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6], buf[7]],\n        })\n    }\n}\n\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub struct GtsCharacteristics {\n    pub fields: u8,\n}\n\n/// MAC PAN Descriptor which contains the network details of the device from\n/// which the beacon is received\n#[derive(Default, Clone, Copy, Debug)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub struct PanDescriptor {\n    /// PAN identifier of the coordinator\n    pub coord_pan_id: PanId,\n    /// Coordinator addressing mode\n    pub coord_addr_mode: AddressMode,\n    /// The current logical channel occupied by the network\n    pub logical_channel: MacChannel,\n    /// Coordinator address\n    pub coord_addr: MacAddress,\n    /// The current channel page occupied by the network\n    pub channel_page: u8,\n    /// PAN coordinator is accepting GTS requests or not\n    pub gts_permit: bool,\n    /// Superframe specification as specified in the received beacon frame\n    pub superframe_spec: [u8; 2],\n    /// The time at which the beacon frame was received, in symbols\n    pub time_stamp: [u8; 4],\n    /// The LQI at which the network beacon was received\n    pub link_quality: u8,\n    /// Security level purportedly used by the received beacon frame\n    pub security_level: u8,\n}\n\nimpl TryFrom<&[u8]> for PanDescriptor {\n    type Error = ();\n\n    fn try_from(buf: &[u8]) -> Result<Self, Self::Error> {\n        const SIZE: usize = 22;\n        if buf.len() < SIZE {\n            return Err(());\n        }\n\n        let coord_addr_mode = AddressMode::try_from(buf[2])?;\n        let coord_addr = match coord_addr_mode {\n            AddressMode::NoAddress => MacAddress { short: [0, 0] },\n            AddressMode::Reserved => MacAddress { short: [0, 0] },\n            AddressMode::Short => MacAddress {\n                short: [buf[4], buf[5]],\n            },\n            AddressMode::Extended => MacAddress {\n                extended: [buf[4], buf[5], buf[6], buf[7], buf[8], buf[9], buf[10], buf[11]],\n            },\n        };\n\n        Ok(Self {\n            coord_pan_id: PanId([buf[0], buf[1]]),\n            coord_addr_mode,\n            logical_channel: MacChannel::try_from(buf[3])?,\n            coord_addr,\n            channel_page: buf[12],\n            gts_permit: buf[13] != 0,\n            superframe_spec: [buf[14], buf[15]],\n            time_stamp: [buf[16], buf[17], buf[18], buf[19]],\n            link_quality: buf[20],\n            security_level: buf[21],\n            // 2 byte stuffing\n        })\n    }\n}\n\nnumeric_enum! {\n    #[repr(u8)]\n    #[derive(Default, Clone, Copy, Debug)]\n    #[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\n    /// Building wireless applications with STM32WB series MCUs - Application note 13.10.3\n    pub enum MacChannel {\n        Channel11 = 0x0B,\n        Channel12 = 0x0C,\n        Channel13 = 0x0D,\n        Channel14 = 0x0E,\n        Channel15 = 0x0F,\n        #[default]\n        Channel16 = 0x10,\n        Channel17 = 0x11,\n        Channel18 = 0x12,\n        Channel19 = 0x13,\n        Channel20 = 0x14,\n        Channel21 = 0x15,\n        Channel22 = 0x16,\n        Channel23 = 0x17,\n        Channel24 = 0x18,\n        Channel25 = 0x19,\n        Channel26 = 0x1A,\n    }\n}\n\n#[cfg(not(feature = \"defmt\"))]\nbitflags::bitflags! {\n    pub struct Capabilities: u8 {\n        /// 1 if the device is capabaleof becoming a PAN coordinator\n        const IS_COORDINATOR_CAPABLE = 0b00000001;\n        /// 1 if the device is an FFD, 0 if it is an RFD\n        const IS_FFD = 0b00000010;\n        /// 1 if the device is receiving power from mains, 0 if it is battery-powered\n        const IS_MAINS_POWERED = 0b00000100;\n        /// 1 if the device does not disable its receiver to conserver power during idle periods\n        const RECEIVER_ON_WHEN_IDLE = 0b00001000;\n        // 0b00010000 reserved\n        // 0b00100000 reserved\n        /// 1 if the device is capable of sending and receiving secured MAC frames\n        const IS_SECURE = 0b01000000;\n        /// 1 if the device wishes the coordinator to allocate a short address as a result of the association\n        const ALLOCATE_ADDRESS = 0b10000000;\n    }\n}\n\n#[cfg(feature = \"defmt\")]\ndefmt::bitflags! {\n    pub struct Capabilities: u8 {\n        /// 1 if the device is capabaleof becoming a PAN coordinator\n        const IS_COORDINATOR_CAPABLE = 0b00000001;\n        /// 1 if the device is an FFD, 0 if it is an RFD\n        const IS_FFD = 0b00000010;\n        /// 1 if the device is receiving power from mains, 0 if it is battery-powered\n        const IS_MAINS_POWERED = 0b00000100;\n        /// 1 if the device does not disable its receiver to conserver power during idle periods\n        const RECEIVER_ON_WHEN_IDLE = 0b00001000;\n        // 0b00010000 reserved\n        // 0b00100000 reserved\n        /// 1 if the device is capable of sending and receiving secured MAC frames\n        const IS_SECURE = 0b01000000;\n        /// 1 if the device wishes the coordinator to allocate a short address as a result of the association\n        const ALLOCATE_ADDRESS = 0b10000000;\n    }\n}\n\nnumeric_enum! {\n    #[repr(u8)]\n    #[derive(Default, Clone, Copy, Debug)]\n    #[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\n    pub enum KeyIdMode {\n        #[default]\n        /// the key is determined implicitly from the originator and recipient(s) of the frame\n        Implicite = 0x00,\n        /// the key is determined explicitly using a 1 bytes key source and a 1 byte key index\n        Explicite1Byte = 0x01,\n        /// the key is determined explicitly using a 4 bytes key source and a 1 byte key index\n        Explicite4Byte = 0x02,\n        /// the key is determined explicitly using a 8 bytes key source and a 1 byte key index\n        Explicite8Byte = 0x03,\n    }\n}\n\nnumeric_enum! {\n    #[repr(u8)]\n    #[derive(Debug)]\n    #[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\n    pub enum AssociationStatus {\n        /// Association successful\n        Success = 0x00,\n        /// PAN at capacity\n        PanAtCapacity = 0x01,\n        /// PAN access denied\n        PanAccessDenied = 0x02\n    }\n}\n\nnumeric_enum! {\n    #[repr(u8)]\n    #[derive(Clone, Copy, Debug)]\n    #[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\n    pub enum DisassociationReason {\n        /// The coordinator wishes the device to leave the PAN.\n        CoordRequested = 0x01,\n        /// The device wishes to leave the PAN.\n        DeviceRequested = 0x02,\n    }\n}\n\nnumeric_enum! {\n    #[repr(u8)]\n    #[derive(Default, Clone, Copy, Debug, PartialEq)]\n    #[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\n    pub enum SecurityLevel {\n        /// MAC Unsecured Mode Security\n        #[default]\n        Unsecure = 0x00,\n        /// MAC ACL Mode Security\n        AclMode = 0x01,\n        /// MAC Secured Mode Security\n        Secured = 0x02,\n    }\n}\n\nnumeric_enum! {\n    #[repr(u8)]\n    #[derive(Debug)]\n    #[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\n    pub enum ScanType {\n        EdScan = 0x00,\n        Active = 0x01,\n        Passive = 0x02,\n        Orphan = 0x03\n    }\n}\n\n/// newtype for Pan Id\n#[derive(Default, Clone, Copy, Debug)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub struct PanId(pub [u8; 2]);\n\nimpl PanId {\n    pub const BROADCAST: Self = Self([0xFF, 0xFF]);\n}\n\nimpl From<Pan> for PanId {\n    fn from(value: Pan) -> Self {\n        Self(value.0.to_be_bytes())\n    }\n}\n\nimpl From<PanId> for Pan {\n    fn from(value: PanId) -> Self {\n        Self(u16::from_be_bytes(value.0))\n    }\n}\n"
  },
  {
    "path": "embassy-stm32-wpan/src/wb55/mod.rs",
    "content": "// This must go FIRST so that all the other modules see its macros.\nmod fmt;\n\nuse core::mem::MaybeUninit;\nuse core::sync::atomic::{Ordering, compiler_fence};\n\nuse embassy_hal_internal::Peri;\nuse embassy_stm32::interrupt;\nuse embassy_stm32::ipcc::{Config, Ipcc, IpccRxChannel, ReceiveInterruptHandler, TransmitInterruptHandler};\nuse embassy_stm32::peripherals::IPCC;\nuse sub::mm::MemoryManager;\nuse sub::sys::Sys;\nuse tables::*;\nuse unsafe_linked_list::LinkedListNode;\n\npub mod channels;\npub mod cmd;\npub mod consts;\npub mod evt;\npub mod fus;\npub mod lhci;\npub mod shci;\npub mod sub;\npub mod tables;\npub mod unsafe_linked_list;\n\n#[cfg(feature = \"wb55_mac\")]\npub mod mac;\n\nuse crate::shci::SchiSysEventReady;\n#[cfg(feature = \"wb55_ble\")]\npub use crate::sub::ble::hci;\n\ntype PacketHeader = LinkedListNode;\n\n/// Transport Layer for the Mailbox interface\npub struct TlMbox<'d> {\n    pub sys_event: SchiSysEventReady,\n    pub sys_subsystem: Sys<'d>,\n    pub mm_subsystem: MemoryManager<'d>,\n    #[cfg(feature = \"wb55_ble\")]\n    pub ble_subsystem: sub::ble::Ble<'d>,\n    #[cfg(feature = \"wb55_mac\")]\n    pub mac_subsystem: sub::mac::Mac<'d>,\n    pub traces: IpccRxChannel<'d>,\n}\n\nimpl<'d> TlMbox<'d> {\n    /// Initialise the Transport Layer, and creates and returns a wrapper around it.\n    ///\n    /// This method performs the initialisation laid out in AN5289 annex 14.1. However, it differs\n    /// from the implementation documented in Figure 64, to avoid needing to reference any C\n    /// function pointers.\n    ///\n    /// Annex 14.1 lays out the following methods that should be called:\n    ///     1. tl_mbox.c/TL_Init, which initialises the reference table that is shared between CPU1\n    ///        and CPU2.\n    ///     2. shci_tl.c/shci_init(), which initialises the system transport layer, and in turn\n    ///        calls tl_mbox.c/TL_SYS_Init, which initialises SYSTEM_EVT_QUEUE channel.\n    ///     3. tl_mbox.c/TL_MM_Init(), which initialises the channel used for sending memory\n    ///        manager commands.\n    ///     4. tl_mbox.c/TL_Enable(), which enables the IPCC, and starts CPU2.\n    /// This implementation initialises all of the shared refernce tables and all IPCC channel that\n    /// would be initialised by this process. The developer should therefore treat this method as\n    /// completing all steps in Figure 64.\n    ///\n    /// Once this method has been called, no system commands may be sent until the CPU2 ready\n    /// signal is received, via [sys_subsystem.read]; this completes the procedure laid out in\n    /// Figure 65.\n    ///\n    /// If the `ble` feature is enabled, at this point, the user should call\n    /// [sys_subsystem.shci_c2_ble_init], before any commands are written to the\n    /// [TlMbox.ble_subsystem] ([sub::ble::Ble::new()] completes the process that would otherwise\n    /// be handled by `TL_BLE_Init`; see Figure 66). This completes the procedure laid out in\n    /// Figure 66.\n    // TODO: document what the user should do after calling init to use the mac_802_15_4 subsystem\n    pub async fn init(\n        ipcc: Peri<'d, IPCC>,\n        _irqs: impl interrupt::typelevel::Binding<interrupt::typelevel::IPCC_C1_RX, ReceiveInterruptHandler>\n        + interrupt::typelevel::Binding<interrupt::typelevel::IPCC_C1_TX, TransmitInterruptHandler>,\n        config: Config,\n    ) -> Result<Self, ()> {\n        // this is an inlined version of TL_Init from the STM32WB firmware as requested by AN5289.\n        // HW_IPCC_Init is not called, and its purpose is (presumably?) covered by this\n        // implementation\n        unsafe {\n            TL_REF_TABLE.as_mut_ptr().write_volatile(RefTable {\n                device_info_table: TL_DEVICE_INFO_TABLE.as_ptr(),\n                ble_table: TL_BLE_TABLE.as_ptr(),\n                thread_table: TL_THREAD_TABLE.as_ptr(),\n                sys_table: TL_SYS_TABLE.as_ptr(),\n                mem_manager_table: TL_MEM_MANAGER_TABLE.as_ptr(),\n                traces_table: TL_TRACES_TABLE.as_ptr(),\n                mac_802_15_4_table: TL_MAC_802_15_4_TABLE.as_ptr(),\n                zigbee_table: TL_ZIGBEE_TABLE.as_ptr(),\n                lld_tests_table: TL_LLD_TESTS_TABLE.as_ptr(),\n                ble_lld_table: TL_BLE_LLD_TABLE.as_ptr(),\n            });\n\n            TL_SYS_TABLE\n                .as_mut_ptr()\n                .write_volatile(MaybeUninit::zeroed().assume_init());\n            TL_DEVICE_INFO_TABLE\n                .as_mut_ptr()\n                .write_volatile(MaybeUninit::zeroed().assume_init());\n            TL_BLE_TABLE\n                .as_mut_ptr()\n                .write_volatile(MaybeUninit::zeroed().assume_init());\n            TL_THREAD_TABLE\n                .as_mut_ptr()\n                .write_volatile(MaybeUninit::zeroed().assume_init());\n            TL_MEM_MANAGER_TABLE\n                .as_mut_ptr()\n                .write_volatile(MaybeUninit::zeroed().assume_init());\n\n            TL_TRACES_TABLE\n                .as_mut_ptr()\n                .write_volatile(MaybeUninit::zeroed().assume_init());\n            TL_MAC_802_15_4_TABLE\n                .as_mut_ptr()\n                .write_volatile(MaybeUninit::zeroed().assume_init());\n            TL_ZIGBEE_TABLE\n                .as_mut_ptr()\n                .write_volatile(MaybeUninit::zeroed().assume_init());\n            TL_LLD_TESTS_TABLE\n                .as_mut_ptr()\n                .write_volatile(MaybeUninit::zeroed().assume_init());\n            TL_BLE_LLD_TABLE\n                .as_mut_ptr()\n                .write_volatile(MaybeUninit::zeroed().assume_init());\n\n            EVT_POOL\n                .as_mut_ptr()\n                .write_volatile(MaybeUninit::zeroed().assume_init());\n            SYS_SPARE_EVT_BUF\n                .as_mut_ptr()\n                .write_volatile(MaybeUninit::zeroed().assume_init());\n            CS_BUFFER\n                .as_mut_ptr()\n                .write_volatile(MaybeUninit::zeroed().assume_init());\n\n            #[cfg(feature = \"wb55_ble\")]\n            {\n                BLE_SPARE_EVT_BUF\n                    .as_mut_ptr()\n                    .write_volatile(MaybeUninit::zeroed().assume_init());\n\n                BLE_CMD_BUFFER\n                    .as_mut_ptr()\n                    .write_volatile(MaybeUninit::zeroed().assume_init());\n                HCI_ACL_DATA_BUFFER\n                    .as_mut_ptr()\n                    .write_volatile(MaybeUninit::zeroed().assume_init());\n            }\n\n            #[cfg(feature = \"wb55_mac\")]\n            {\n                MAC_802_15_4_CMD_BUFFER\n                    .as_mut_ptr()\n                    .write_volatile(MaybeUninit::zeroed().assume_init());\n                MAC_802_15_4_NOTIF_RSP_EVT_BUFFER\n                    .as_mut_ptr()\n                    .write_volatile(MaybeUninit::zeroed().assume_init());\n            }\n        }\n\n        compiler_fence(Ordering::SeqCst);\n\n        // this is equivalent to `HW_IPCC_Enable`, which is called by `TL_Enable`\n        let [\n            (_hw_ipcc_ble_cmd_channel, _ipcc_ble_event_channel),\n            (ipcc_system_cmd_rsp_channel, ipcc_system_event_channel),\n            (_ipcc_mac_802_15_4_cmd_rsp_channel, _ipcc_mac_802_15_4_notification_ack_channel),\n            (ipcc_mm_release_buffer_channel, _ipcc_traces_channel),\n            (_ipcc_ble_lld_cmd_channel, _ipcc_ble_lld_rsp_channel),\n            (_ipcc_hci_acl_data_channel, _),\n        ] = Ipcc::new(ipcc, _irqs, config).split();\n\n        let mm = sub::mm::MemoryManager::new(ipcc_mm_release_buffer_channel);\n        let mut sys = sub::sys::Sys::new(ipcc_system_cmd_rsp_channel, ipcc_system_event_channel);\n\n        let sys_event = sys.read_ready().await?;\n\n        debug!(\"sys event: {}\", sys_event);\n\n        Ok(Self {\n            sys_event: sys_event,\n            sys_subsystem: sys,\n            #[cfg(feature = \"wb55_ble\")]\n            ble_subsystem: sub::ble::Ble::new(\n                _hw_ipcc_ble_cmd_channel,\n                _ipcc_ble_event_channel,\n                _ipcc_hci_acl_data_channel,\n            ),\n            #[cfg(feature = \"wb55_mac\")]\n            mac_subsystem: sub::mac::Mac::new(\n                _ipcc_mac_802_15_4_cmd_rsp_channel,\n                _ipcc_mac_802_15_4_notification_ack_channel,\n            ),\n            mm_subsystem: mm,\n            traces: _ipcc_traces_channel,\n        })\n    }\n}\n"
  },
  {
    "path": "embassy-stm32-wpan/src/wb55/shci.rs",
    "content": "use core::sync::atomic::{Ordering, compiler_fence};\nuse core::{mem, ptr, slice};\n\nuse crate::cmd::CmdPacket;\nuse crate::consts::{TL_CS_EVT_SIZE, TL_EVT_HEADER_SIZE, TL_PACKET_HEADER_SIZE};\nuse crate::evt::{CcEvt, EvtStub};\nuse crate::wb55::PacketHeader;\n\nconst SHCI_OGF: u16 = 0x3F;\n\nconst fn opcode(ogf: u16, ocf: u16) -> isize {\n    ((ogf << 10) + ocf) as isize\n}\n\npub(crate) trait SealedSchiFromPacket: Sized {\n    unsafe fn from_packet(cmd_buf: *const CmdPacket) -> Result<Self, ()>;\n}\n\n#[allow(private_bounds)]\npub trait SchiFromPacket: SealedSchiFromPacket {}\nimpl<T: SealedSchiFromPacket> SchiFromPacket for T {}\n\ntrait ShciFromEventSerial: TryFrom<u8, Error = ()> {}\n\nimpl<T: ShciFromEventSerial> SealedSchiFromPacket for T {\n    unsafe fn from_packet(cmd_buf: *const CmdPacket) -> Result<Self, ()> {\n        let p_cmd_serial = (cmd_buf as *mut u8).add(size_of::<PacketHeader>());\n        let p_evt_payload = p_cmd_serial.add(size_of::<EvtStub>());\n\n        compiler_fence(Ordering::Acquire);\n        let cc_evt = ptr::read_unaligned(p_evt_payload as *const CcEvt);\n\n        cc_evt.payload[0].try_into()\n    }\n}\n\n#[allow(dead_code)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub enum SchiCommandStatus {\n    ShciSuccess = 0x00,\n    ShciUnknownCmd = 0x01,\n    ShciMemoryCapacityExceededErrCode = 0x07,\n    ShciErrUnsupportedFeature = 0x11,\n    ShciErrInvalidHciCmdParams = 0x12,\n    ShciErrInvalidParams = 0x42,   /* only used for release < v1.13.0 */\n    ShciErrInvalidParamsV2 = 0x92, /* available for release >= v1.13.0 */\n    ShciFusCmdNotSupported = 0xFF,\n}\n\nimpl ShciFromEventSerial for SchiCommandStatus {}\nimpl TryFrom<u8> for SchiCommandStatus {\n    type Error = ();\n\n    fn try_from(v: u8) -> Result<Self, Self::Error> {\n        match v {\n            0x00 => Ok(Self::ShciSuccess),\n            0x01 => Ok(Self::ShciUnknownCmd),\n            0x07 => Ok(Self::ShciMemoryCapacityExceededErrCode),\n            0x11 => Ok(Self::ShciErrUnsupportedFeature),\n            0x12 => Ok(Self::ShciErrInvalidHciCmdParams),\n            0x42 => Ok(Self::ShciErrInvalidParams), /* only used for release < v1.13.0 */\n            0x92 => Ok(Self::ShciErrInvalidParamsV2), /* available for release >= v1.13.0 */\n            0xFF => Ok(Self::ShciFusCmdNotSupported),\n            _ => Err(()),\n        }\n    }\n}\n\n#[allow(dead_code)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub enum ShciOpcode {\n    // 0x50 reserved\n    // 0x51 reserved\n    FusGetState = opcode(SHCI_OGF, 0x52),\n    // 0x53 reserved\n    FusFirmwareUpgrade = opcode(SHCI_OGF, 0x54),\n    FusFirmwareDelete = opcode(SHCI_OGF, 0x55),\n    FusUpdateAuthKey = opcode(SHCI_OGF, 0x56),\n    FusLockAuthKey = opcode(SHCI_OGF, 0x57),\n    FusStoreUserKey = opcode(SHCI_OGF, 0x58),\n    FusLoadUserKey = opcode(SHCI_OGF, 0x59),\n    FusStartWirelessStack = opcode(SHCI_OGF, 0x5a),\n    // 0x5b reserved\n    // 0x5c reserved\n    FusLockUserKey = opcode(SHCI_OGF, 0x5d),\n    FusUnloadUserKey = opcode(SHCI_OGF, 0x5e),\n    FusActivateAntirollback = opcode(SHCI_OGF, 0x5f),\n    // 0x60 reserved\n    // 0x61 reserved\n    // 0x62 reserved\n    // 0x63 reserved\n    // 0x64 reserved\n    // 0x65 reserved\n    BleInit = opcode(SHCI_OGF, 0x66),\n    ThreadInit = opcode(SHCI_OGF, 0x67),\n    DebugInit = opcode(SHCI_OGF, 0x68),\n    FlashEraseActivity = opcode(SHCI_OGF, 0x69),\n    ConcurrentSetMode = opcode(SHCI_OGF, 0x6a),\n    FlashStoreData = opcode(SHCI_OGF, 0x6b),\n    FlashEraseData = opcode(SHCI_OGF, 0x6c),\n    RadioAllowLowPower = opcode(SHCI_OGF, 0x6d),\n    Mac802_15_4Init = opcode(SHCI_OGF, 0x6e),\n    ReInit = opcode(SHCI_OGF, 0x6f),\n    ZigbeeInit = opcode(SHCI_OGF, 0x70),\n    LldTestsInit = opcode(SHCI_OGF, 0x71),\n    ExtraConfig = opcode(SHCI_OGF, 0x72),\n    SetFlashActivityControl = opcode(SHCI_OGF, 0x73),\n    BleLldInit = opcode(SHCI_OGF, 0x74),\n    Config = opcode(SHCI_OGF, 0x75),\n    ConcurrentGetNextBleEvtTime = opcode(SHCI_OGF, 0x76),\n    ConcurrentEnableNext802_15_4EvtNotification = opcode(SHCI_OGF, 0x77),\n    Mac802_15_4DeInit = opcode(SHCI_OGF, 0x78),\n}\n\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub enum ShciFusGetStateErrorCode {\n    FusStateErrorNoError = 0x00,\n    FusStateErrorImgNotFound = 0x01,\n    FusStateErrorImgCorrupt = 0x02,\n    FusStateErrorImgNotAuthentic = 0x03,\n    FusStateErrorImgNotEnoughSpace = 0x04,\n    FusStateErrorImageUsrAbort = 0x05,\n    FusStateErrorImageErsError = 0x06,\n    FusStateErrorImageWrtError = 0x07,\n    FusStateErrorAuthTagStNotFound = 0x08,\n    FusStateErrorAuthTagCustNotFound = 0x09,\n    FusStateErrorAuthKeyLocked = 0x0A,\n    FusStateErrorFwRollbackError = 0x11,\n    FusStateErrorStateNotRunning = 0xFE,\n    FusStateErrorErrUnknown = 0xFF,\n}\n\nimpl ShciFromEventSerial for ShciFusGetStateErrorCode {}\nimpl TryFrom<u8> for ShciFusGetStateErrorCode {\n    type Error = ();\n\n    fn try_from(v: u8) -> Result<Self, Self::Error> {\n        match v {\n            0x00 => Ok(Self::FusStateErrorNoError),\n            0x01 => Ok(Self::FusStateErrorImgNotFound),\n            0x02 => Ok(Self::FusStateErrorImgCorrupt),\n            0x03 => Ok(Self::FusStateErrorImgNotAuthentic),\n            0x04 => Ok(Self::FusStateErrorImgNotEnoughSpace),\n            0x05 => Ok(Self::FusStateErrorImageUsrAbort),\n            0x06 => Ok(Self::FusStateErrorImageErsError),\n            0x07 => Ok(Self::FusStateErrorImageWrtError),\n            0x08 => Ok(Self::FusStateErrorAuthTagStNotFound),\n            0x09 => Ok(Self::FusStateErrorAuthTagCustNotFound),\n            0x0A => Ok(Self::FusStateErrorAuthKeyLocked),\n            0x11 => Ok(Self::FusStateErrorFwRollbackError),\n            0xFE => Ok(Self::FusStateErrorStateNotRunning),\n            0xFF => Ok(Self::FusStateErrorErrUnknown),\n            _ => Err(()),\n        }\n    }\n}\n\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\n#[derive(PartialEq, Clone, Copy)]\npub enum SchiSysEventReady {\n    WirelessFwRunning = 0x00,\n    FusFwRunning = 0x01,\n    NvmBackupRunning = 0x10,\n    NvmRestoreRunning = 0x11,\n}\n\nimpl TryFrom<u8> for SchiSysEventReady {\n    type Error = ();\n\n    fn try_from(v: u8) -> Result<Self, Self::Error> {\n        match v {\n            0x00 => Ok(Self::WirelessFwRunning),\n            0x01 => Ok(Self::FusFwRunning),\n            0x10 => Ok(Self::NvmBackupRunning),\n            0x11 => Ok(Self::NvmRestoreRunning),\n            _ => Err(()),\n        }\n    }\n}\n\npub const SHCI_C2_CONFIG_EVTMASK1_BIT0_ERROR_NOTIF_ENABLE: u8 = 1 << 0;\npub const SHCI_C2_CONFIG_EVTMASK1_BIT1_BLE_NVM_RAM_UPDATE_ENABLE: u8 = 1 << 1;\npub const SHCI_C2_CONFIG_EVTMASK1_BIT2_THREAD_NVM_RAM_UPDATE_ENABLE: u8 = 1 << 2;\npub const SHCI_C2_CONFIG_EVTMASK1_BIT3_NVM_START_WRITE_ENABLE: u8 = 1 << 3;\npub const SHCI_C2_CONFIG_EVTMASK1_BIT4_NVM_END_WRITE_ENABLE: u8 = 1 << 4;\npub const SHCI_C2_CONFIG_EVTMASK1_BIT5_NVM_START_ERASE_ENABLE: u8 = 1 << 5;\npub const SHCI_C2_CONFIG_EVTMASK1_BIT6_NVM_END_ERASE_ENABLE: u8 = 1 << 6;\n\n#[derive(Clone, Copy)]\n#[repr(C, packed)]\npub struct ShciConfigParam {\n    pub payload_cmd_size: u8,\n    pub config: u8,\n    pub event_mask: u8,\n    pub spare: u8,\n    pub ble_nvm_ram_address: u32,\n    pub thread_nvm_ram_address: u32,\n    pub revision_id: u16,\n    pub device_id: u16,\n}\n\nimpl ShciConfigParam {\n    pub fn payload<'a>(&'a self) -> &'a [u8] {\n        unsafe { slice::from_raw_parts(self as *const _ as *const u8, mem::size_of::<Self>()) }\n    }\n}\n\nimpl Default for ShciConfigParam {\n    fn default() -> Self {\n        Self {\n            payload_cmd_size: (mem::size_of::<Self>() - 1) as u8,\n            config: 0,\n            event_mask: SHCI_C2_CONFIG_EVTMASK1_BIT0_ERROR_NOTIF_ENABLE\n                + SHCI_C2_CONFIG_EVTMASK1_BIT1_BLE_NVM_RAM_UPDATE_ENABLE\n                + SHCI_C2_CONFIG_EVTMASK1_BIT2_THREAD_NVM_RAM_UPDATE_ENABLE\n                + SHCI_C2_CONFIG_EVTMASK1_BIT3_NVM_START_WRITE_ENABLE\n                + SHCI_C2_CONFIG_EVTMASK1_BIT4_NVM_END_WRITE_ENABLE\n                + SHCI_C2_CONFIG_EVTMASK1_BIT5_NVM_START_ERASE_ENABLE\n                + SHCI_C2_CONFIG_EVTMASK1_BIT6_NVM_END_ERASE_ENABLE,\n            spare: 0,\n            ble_nvm_ram_address: 0,\n            thread_nvm_ram_address: 0,\n            revision_id: 0,\n            device_id: 0,\n        }\n    }\n}\n\n#[derive(Clone, Copy)]\n#[repr(C, packed)]\npub struct ShciBleInitCmdParam {\n    /// NOT USED - shall be set to 0\n    pub p_ble_buffer_address: u32,\n    /// NOT USED - shall be set to 0\n    pub ble_buffer_size: u32,\n    /// Maximum number of attribute records related to all the required characteristics (excluding the services)\n    /// that can be stored in the GATT database, for the specific BLE user application.\n    /// For each characteristic, the number of attribute records goes from two to five depending on the characteristic properties:\n    ///    - minimum of two (one for declaration and one for the value)\n    ///    - add one more record for each additional property: notify or indicate, broadcast, extended property.\n    /// The total calculated value must be increased by 9, due to the records related to the standard attribute profile and\n    /// GAP service characteristics, and automatically added when initializing GATT and GAP layers\n    ///  - Min value: <number of user attributes> + 9\n    ///  - Max value: depending on the GATT database defined by user application\n    pub num_attr_record: u16,\n    /// Defines the maximum number of services that can be stored in the GATT database. Note that the GAP and GATT services\n    /// are automatically added at initialization so this parameter must be the number of user services increased by two.\n    ///    - Min value: <number of user service> + 2\n    ///    - Max value: depending GATT database defined by user application\n    pub num_attr_serv: u16,\n    /// NOTE: This parameter is ignored by the CPU2 when the parameter \"Options\" is set to \"LL_only\" ( see Options description in that structure )\n    ///\n    /// Size of the storage area for the attribute values.\n    /// Each characteristic contributes to the attrValueArrSize value as follows:\n    ///    - Characteristic value length plus:\n    ///        + 5 bytes if characteristic UUID is 16 bits\n    ///        + 19 bytes if characteristic UUID is 128 bits\n    ///        + 2 bytes if characteristic has a server configuration descriptor\n    ///        + 2 bytes * NumOfLinks if the characteristic has a client configuration descriptor\n    ///        + 2 bytes if the characteristic has extended properties\n    /// Each descriptor contributes to the attrValueArrSize value as follows:\n    ///    - Descriptor length\n    pub attr_value_arr_size: u16,\n    /// Maximum number of BLE links supported\n    ///    - Min value: 1\n    ///    - Max value: 8\n    pub num_of_links: u8,\n    /// Disable/enable the extended packet length BLE 5.0 feature\n    ///    - Disable: 0\n    ///    - Enable: 1\n    pub extended_packet_length_enable: u8,\n    /// NOTE: This parameter is ignored by the CPU2 when the parameter \"Options\" is set to \"LL_only\" ( see Options description in that structure )\n    ///\n    /// Maximum number of supported \"prepare write request\"\n    ///    - Min value: given by the macro DEFAULT_PREP_WRITE_LIST_SIZE\n    ///    - Max value: a value higher than the minimum required can be specified, but it is not recommended\n    pub prepare_write_list_size: u8,\n    /// NOTE: This parameter is overwritten by the CPU2 with an hardcoded optimal value when the parameter \"Options\" is set to \"LL_only\"\n    /// ( see Options description in that structure )\n    ///\n    /// Number of allocated memory blocks for the BLE stack\n    ///     - Min value: given by the macro MBLOCKS_CALC\n    ///     - Max value: a higher value can improve data throughput performance, but uses more memory\n    pub block_count: u8,\n    /// NOTE: This parameter is ignored by the CPU2 when the parameter \"Options\" is set to \"LL_only\" ( see Options description in that structure )\n    ///\n    /// Maximum ATT MTU size supported\n    ///     - Min value: 23\n    ///     - Max value: 512\n    pub att_mtu: u16,\n    /// The sleep clock accuracy (ppm value) that used in BLE connected slave mode to calculate the window widening\n    /// (in combination with the sleep clock accuracy sent by master in CONNECT_REQ PDU),\n    /// refer to BLE 5.0 specifications - Vol 6 - Part B - chap 4.5.7 and 4.2.2\n    ///     - Min value: 0\n    ///     - Max value: 500 (worst possible admitted by specification)\n    pub slave_sca: u16,\n    /// The sleep clock accuracy handled in master mode. It is used to determine the connection and advertising events timing.\n    /// It is transmitted to the slave in CONNEC_REQ PDU used by the slave to calculate the window widening,\n    /// see SlaveSca and Bluetooth Core Specification v5.0 Vol 6 - Part B - chap 4.5.7 and 4.2.2\n    /// Possible values:\n    ///    - 251 ppm to 500 ppm: 0\n    ///    - 151 ppm to 250 ppm: 1\n    ///    - 101 ppm to 150 ppm: 2\n    ///    - 76 ppm to 100 ppm: 3\n    ///    - 51 ppm to 75 ppm: 4\n    ///    - 31 ppm to 50 ppm: 5\n    ///    - 21 ppm to 30 ppm: 6\n    ///    - 0 ppm to 20 ppm: 7\n    pub master_sca: u8,\n    /// Some information for Low speed clock mapped in bits field\n    /// - bit 0:\n    ///     - 1: Calibration for the RF system wakeup clock source\n    ///     - 0: No calibration for the RF system wakeup clock source\n    /// - bit 1:\n    ///     - 1: STM32W5M Module device\n    ///     - 0: Other devices as STM32WBxx SOC, STM32WB1M module\n    /// - bit 2:\n    ///     - 1: HSE/1024 Clock config\n    ///     - 0: LSE Clock config\n    pub ls_source: u8,\n    /// This parameter determines the maximum duration of a slave connection event. When this duration is reached the slave closes\n    /// the current connections event (whatever is the CE_length parameter specified by the master in HCI_CREATE_CONNECTION HCI command),\n    /// expressed in units of 625/256 µs (~2.44 µs)\n    ///    - Min value: 0 (if 0 is specified, the master and slave perform only a single TX-RX exchange per connection event)\n    ///    - Max value: 1638400 (4000 ms). A higher value can be specified (max 0xFFFFFFFF) but results in a maximum connection time\n    ///      of 4000 ms as specified. In this case the parameter is not applied, and the predicted CE length calculated on slave is not shortened\n    pub max_conn_event_length: u32,\n    /// Startup time of the high speed (16 or 32 MHz) crystal oscillator in units of 625/256 µs (~2.44 µs).\n    ///    - Min value: 0\n    ///    - Max value:  820 (~2 ms). A higher value can be specified, but the value that implemented in stack is forced to ~2 ms\n    pub hs_startup_time: u16,\n    /// Viterbi implementation in BLE LL reception.\n    ///    - 0: Enable\n    ///    - 1: Disable\n    pub viterbi_enable: u8,\n    /// - bit 0:\n    ///     - 1: LL only\n    ///     - 0: LL + host\n    /// - bit 1:\n    ///     - 1: no service change desc.\n    ///     - 0: with service change desc.\n    /// - bit 2:\n    ///     - 1: device name Read-Only\n    ///     - 0: device name R/W\n    /// - bit 3:\n    ///     - 1: extended advertizing supported\n    ///     - 0: extended advertizing not supported\n    /// - bit 4:\n    ///     - 1: CS Algo #2 supported\n    ///     - 0: CS Algo #2 not supported\n    /// - bit 5:\n    ///     - 1: Reduced GATT database in NVM\n    ///     - 0: Full GATT database in NVM\n    /// - bit 6:\n    ///     - 1: GATT caching is used\n    ///     - 0: GATT caching is not used\n    /// - bit 7:\n    ///     - 1: LE Power Class 1\n    ///     - 0: LE Power Classe 2-3\n    /// - other bits: complete with Options_extension flag\n    pub options: u8,\n    /// Reserved for future use - shall be set to 0\n    pub hw_version: u8,\n    ///\n    /// Maximum number of connection-oriented channels in initiator mode.\n    /// Range: 0 .. 64\n    pub max_coc_initiator_nbr: u8,\n\n    ///\n    /// Minimum transmit power in dBm supported by the Controller.\n    /// Range: -127 .. 20\n    pub min_tx_power: i8,\n\n    ///\n    /// Maximum transmit power in dBm supported by the Controller.\n    /// Range: -127 .. 20\n    pub max_tx_power: i8,\n\n    ///\n    /// RX model configuration\n    /// - bit 0:   1: agc_rssi model improved vs RF blockers    0: Legacy agc_rssi model\n    /// - other bits: reserved ( shall be set to 0)\n    pub rx_model_config: u8,\n\n    /// Maximum number of advertising sets.\n    /// Range: 1 .. 8 with limitation:\n    /// This parameter is linked to max_adv_data_len such as both compliant with allocated Total memory computed with BLE_EXT_ADV_BUFFER_SIZE based\n    /// on Max Extended advertising configuration supported.\n    /// This parameter is considered by the CPU2 when Options has SHCI_C2_BLE_INIT_OPTIONS_EXT_ADV flag set\n    pub max_adv_set_nbr: u8,\n\n    /// Maximum advertising data length (in bytes)\n    /// Range: 31 .. 1650 with limitation:\n    /// This parameter is linked to max_adv_set_nbr such as both compliant with allocated Total memory computed with BLE_EXT_ADV_BUFFER_SIZE based\n    /// on Max Extended advertising configuration supported.\n    /// This parameter is considered by the CPU2 when Options has SHCI_C2_BLE_INIT_OPTIONS_EXT_ADV flag set\n    pub max_adv_data_len: u16,\n\n    /// RF TX Path Compensation Value (16-bit signed integer). Units: 0.1 dB.\n    /// Range: -1280 .. 1280\n    pub tx_path_compens: i16,\n\n    //// RF RX Path Compensation Value (16-bit signed integer). Units: 0.1 dB.\n    /// Range: -1280 .. 1280\n    pub rx_path_compens: i16,\n\n    /// BLE core specification version (8-bit unsigned integer).\n    /// values as: 11(5.2), 12(5.3)\n    pub ble_core_version: u8,\n\n    /// Options flags extension\n    /// - bit 0:   1: appearance Writable              0: appearance Read-Only\n    /// - bit 1:   1: Enhanced ATT supported           0: Enhanced ATT not supported\n    /// - other bits: reserved ( shall be set to 0)\n    pub options_extension: u8,\n\n    /// MaxAddEattBearers\n    /// Maximum number of bearers that can be created for Enhanced ATT\n    /// in addition to the number of links\n    ///     - Range: 0 .. 4\n    pub max_add_eatt_bearers: u8,\n}\n\nimpl ShciBleInitCmdParam {\n    pub fn payload<'a>(&'a self) -> &'a [u8] {\n        unsafe { slice::from_raw_parts(self as *const _ as *const u8, mem::size_of::<Self>()) }\n    }\n}\n\nimpl Default for ShciBleInitCmdParam {\n    fn default() -> Self {\n        Self {\n            p_ble_buffer_address: 0,\n            ble_buffer_size: 0,\n            num_attr_record: 68,\n            num_attr_serv: 4,\n            attr_value_arr_size: 1344,\n            num_of_links: 2,\n            extended_packet_length_enable: 1,\n            prepare_write_list_size: 0x3A,\n            block_count: 0x79,\n            att_mtu: 156,\n            slave_sca: 500,\n            master_sca: 0,\n            ls_source: 1,\n            max_conn_event_length: 0xFFFFFFFF,\n            hs_startup_time: 0x148,\n            viterbi_enable: 1,\n            options: 0,\n            hw_version: 0,\n            max_coc_initiator_nbr: 32,\n            min_tx_power: -40,\n            max_tx_power: 6,\n            rx_model_config: 0,\n            max_adv_set_nbr: 2,\n            max_adv_data_len: 1650,\n            tx_path_compens: 0,\n            rx_path_compens: 0,\n            ble_core_version: 11,\n            options_extension: 0,\n            max_add_eatt_bearers: 4,\n        }\n    }\n}\n\npub const TL_BLE_EVT_CS_PACKET_SIZE: usize = TL_EVT_HEADER_SIZE + TL_CS_EVT_SIZE;\n#[allow(dead_code)] // Not used currently but reserved\nconst TL_BLE_EVT_CS_BUFFER_SIZE: usize = TL_PACKET_HEADER_SIZE + TL_BLE_EVT_CS_PACKET_SIZE;\n"
  },
  {
    "path": "embassy-stm32-wpan/src/wb55/sub/ble.rs",
    "content": "use core::ptr;\n\nuse embassy_stm32::ipcc::{IpccRxChannel, IpccTxChannel};\nuse hci::Opcode;\n\nuse crate::cmd::CmdPacket;\nuse crate::consts::{TL_BLEEVT_CC_OPCODE, TL_BLEEVT_CS_OPCODE, TlPacketType};\nuse crate::evt;\nuse crate::evt::{EvtBox, EvtPacket, EvtStub};\nuse crate::sub::mm;\nuse crate::tables::{BLE_CMD_BUFFER, BleTable, CS_BUFFER, EVT_QUEUE, HCI_ACL_DATA_BUFFER, TL_BLE_TABLE};\nuse crate::unsafe_linked_list::LinkedListNode;\n\n/// A guard that, once constructed, may be used to send BLE commands to CPU2.\n///\n/// It is the responsibility of the caller to ensure that they have awaited an event via\n/// [crate::sub::sys::Sys::read] before sending any of these commands, and to call\n/// [crate::sub::sys::Sys::shci_c2_ble_init] and await the HCI_COMMAND_COMPLETE_EVENT before\n/// sending any other commands.\n///\n/// # Example\n///\n/// ```\n/// # embassy_stm32::bind_interrupts!(struct Irqs{\n/// #     IPCC_C1_RX => ReceiveInterruptHandler;\n/// #     IPCC_C1_TX => TransmitInterruptHandler;\n/// # });\n/// #\n/// # let p = embassy_stm32::init(embassy_stm32::Config::default());\n/// # let mut mbox = embassy_stm32_wpan::TlMbox::init(p.IPCC, Irqs, embassy_stm32::ipcc::Config::default());\n/// #\n/// # let sys_event = mbox.sys_subsystem.read().await;\n/// # let _command_status = mbox.sys_subsystem.shci_c2_ble_init(Default::default());\n/// # // BLE commands may now be sent\n/// #\n/// # mbox.ble_subsystem.reset().await;\n/// # let _reset_response = mbox.ble_subsystem.read().await;\n/// ```\npub struct Ble<'a> {\n    hw_ipcc_ble_cmd_channel: IpccTxChannel<'a>,\n    ipcc_ble_event_channel: IpccRxChannel<'a>,\n    ipcc_hci_acl_data_channel: IpccTxChannel<'a>,\n}\n\n/// BLE for only sending commands to CPU2\npub struct BleTx<'a> {\n    hw_ipcc_ble_cmd_channel: IpccTxChannel<'a>,\n    ipcc_hci_acl_data_channel: IpccTxChannel<'a>,\n}\n\n/// BLE for only receive commands from CPU2\npub struct BleRx<'a> {\n    ipcc_ble_event_channel: IpccRxChannel<'a>,\n}\n\nimpl<'a> Ble<'a> {\n    /// Constructs a guard that allows for BLE commands to be sent to CPU2.\n    ///\n    /// This takes the place of `TL_BLE_Init`, completing that step as laid out in AN5289, Fig 66.\n    pub(crate) fn new(\n        hw_ipcc_ble_cmd_channel: IpccTxChannel<'a>,\n        ipcc_ble_event_channel: IpccRxChannel<'a>,\n        ipcc_hci_acl_data_channel: IpccTxChannel<'a>,\n    ) -> Self {\n        unsafe {\n            LinkedListNode::init_head(EVT_QUEUE.as_mut_ptr());\n\n            TL_BLE_TABLE.as_mut_ptr().write_volatile(BleTable {\n                pcmd_buffer: BLE_CMD_BUFFER.as_mut_ptr().cast(),\n                pcs_buffer: CS_BUFFER.as_ptr().cast(),\n                pevt_queue: EVT_QUEUE.as_ptr().cast(),\n                phci_acl_data_buffer: HCI_ACL_DATA_BUFFER.as_mut_ptr().cast(),\n            });\n        }\n\n        Self {\n            hw_ipcc_ble_cmd_channel,\n            ipcc_ble_event_channel,\n            ipcc_hci_acl_data_channel,\n        }\n    }\n\n    /// Split current BLE into BleTx and BleRx\n    pub fn split(self) -> (BleTx<'a>, BleRx<'a>) {\n        (\n            BleTx {\n                hw_ipcc_ble_cmd_channel: self.hw_ipcc_ble_cmd_channel,\n                ipcc_hci_acl_data_channel: self.ipcc_hci_acl_data_channel,\n            },\n            BleRx {\n                ipcc_ble_event_channel: self.ipcc_ble_event_channel,\n            },\n        )\n    }\n\n    /// `HW_IPCC_BLE_EvtNot`\n    pub async fn tl_read(&mut self) -> EvtBox<Self> {\n        self.ipcc_ble_event_channel\n            .receive(|| unsafe {\n                if let Some(node_ptr) =\n                    critical_section::with(|cs| LinkedListNode::remove_head(cs, EVT_QUEUE.as_mut_ptr()))\n                {\n                    Some(EvtBox::new(node_ptr.cast()))\n                } else {\n                    None\n                }\n            })\n            .await\n    }\n\n    /// `TL_BLE_SendCmd`\n    pub async fn tl_write(&mut self, opcode: u16, payload: &[u8]) {\n        self.hw_ipcc_ble_cmd_channel\n            .send(|| unsafe {\n                CmdPacket::write_into(BLE_CMD_BUFFER.as_mut_ptr(), TlPacketType::BleCmd, opcode, payload);\n            })\n            .await;\n    }\n\n    /// `TL_BLE_SendAclData`\n    pub async fn acl_write(&mut self, handle: u16, payload: &[u8]) {\n        self.ipcc_hci_acl_data_channel\n            .send(|| unsafe {\n                CmdPacket::write_into(\n                    HCI_ACL_DATA_BUFFER.as_mut_ptr() as *mut _,\n                    TlPacketType::AclData,\n                    handle,\n                    payload,\n                );\n            })\n            .await;\n    }\n}\n\nimpl<'a> evt::MemoryManager for Ble<'a> {\n    /// SAFETY: passing a pointer to something other than a managed event packet is UB\n    unsafe fn drop_event_packet(evt: *mut EvtPacket) {\n        let stub = unsafe {\n            let p_evt_stub = &(*evt).evt_serial as *const _ as *const EvtStub;\n\n            ptr::read_volatile(p_evt_stub)\n        };\n\n        if !(stub.evt_code == TL_BLEEVT_CS_OPCODE || stub.evt_code == TL_BLEEVT_CC_OPCODE) {\n            mm::MemoryManager::drop_event_packet(evt);\n        }\n    }\n}\n\npub extern crate stm32wb_hci as hci;\n\nimpl<'a> hci::Controller for Ble<'a> {\n    async fn controller_write(&mut self, opcode: Opcode, payload: &[u8]) {\n        self.tl_write(opcode.0, payload).await;\n    }\n\n    async fn controller_read_into(&mut self, buf: &mut [u8]) {\n        let evt_box = self.tl_read().await;\n        let evt_serial = evt_box.serial();\n\n        buf[..evt_serial.len()].copy_from_slice(evt_serial);\n    }\n}\n\nimpl<'a> BleTx<'a> {\n    /// `TL_BLE_SendCmd`\n    pub async fn tl_write(&mut self, opcode: u16, payload: &[u8]) {\n        self.hw_ipcc_ble_cmd_channel\n            .send(|| unsafe {\n                CmdPacket::write_into(BLE_CMD_BUFFER.as_mut_ptr(), TlPacketType::BleCmd, opcode, payload);\n            })\n            .await;\n    }\n\n    /// `TL_BLE_SendAclData`\n    pub async fn acl_write(&mut self, handle: u16, payload: &[u8]) {\n        self.ipcc_hci_acl_data_channel\n            .send(|| unsafe {\n                CmdPacket::write_into(\n                    HCI_ACL_DATA_BUFFER.as_mut_ptr() as *mut _,\n                    TlPacketType::AclData,\n                    handle,\n                    payload,\n                );\n            })\n            .await;\n    }\n}\n\nimpl<'a> BleRx<'a> {\n    /// `HW_IPCC_BLE_EvtNot`\n    pub async fn tl_read(&mut self) -> EvtBox<Self> {\n        self.ipcc_ble_event_channel\n            .receive(|| unsafe {\n                if let Some(node_ptr) =\n                    critical_section::with(|cs| LinkedListNode::remove_head(cs, EVT_QUEUE.as_mut_ptr()))\n                {\n                    Some(EvtBox::new(node_ptr.cast()))\n                } else {\n                    None\n                }\n            })\n            .await\n    }\n}\n\nimpl<'a> evt::MemoryManager for BleRx<'a> {\n    /// SAFETY: passing a pointer to something other than a managed event packet is UB\n    unsafe fn drop_event_packet(evt: *mut EvtPacket) {\n        // Reuse the logic from the original BLE implementation\n        let stub = unsafe {\n            let p_evt_stub = &(*evt).evt_serial as *const _ as *const EvtStub;\n            ptr::read_volatile(p_evt_stub)\n        };\n\n        if !(stub.evt_code == TL_BLEEVT_CS_OPCODE || stub.evt_code == TL_BLEEVT_CC_OPCODE) {\n            mm::MemoryManager::drop_event_packet(evt);\n        }\n    }\n}\n\n/// Implement Controller for TX (Write only)\nimpl<'a> hci::Controller for BleTx<'a> {\n    async fn controller_write(&mut self, opcode: Opcode, payload: &[u8]) {\n        self.tl_write(opcode.0, payload).await;\n    }\n\n    async fn controller_read_into(&mut self, _buf: &mut [u8]) {\n        panic!(\"BleTx cannot read!\");\n    }\n}\n\n/// Implement Controller for RX (Read only)\nimpl<'a> hci::Controller for BleRx<'a> {\n    async fn controller_write(&mut self, _opcode: Opcode, _payload: &[u8]) {\n        panic!(\"BleRx cannot write!\");\n    }\n\n    async fn controller_read_into(&mut self, buf: &mut [u8]) {\n        let evt_box = self.tl_read().await;\n        let evt_serial = evt_box.serial();\n        buf[..evt_serial.len()].copy_from_slice(evt_serial);\n    }\n}\n"
  },
  {
    "path": "embassy-stm32-wpan/src/wb55/sub/mac.rs",
    "content": "use core::future::poll_fn;\nuse core::ptr;\nuse core::sync::atomic::{AtomicBool, Ordering};\nuse core::task::Poll;\n\nuse embassy_futures::poll_once;\nuse embassy_stm32::ipcc::{Ipcc, IpccRxChannel, IpccTxChannel};\nuse embassy_sync::waitqueue::AtomicWaker;\n\nuse crate::cmd::CmdPacket;\nuse crate::consts::TlPacketType;\nuse crate::evt;\nuse crate::evt::{EvtBox, EvtPacket};\nuse crate::mac::commands::MacCommand;\nuse crate::mac::event::MacEvent;\nuse crate::mac::typedefs::MacError;\nuse crate::tables::{MAC_802_15_4_CMD_BUFFER, MAC_802_15_4_NOTIF_RSP_EVT_BUFFER};\nuse crate::unsafe_linked_list::LinkedListNode;\n\nstatic MAC_WAKER: AtomicWaker = AtomicWaker::new();\nstatic MAC_EVT_OUT: AtomicBool = AtomicBool::new(false);\n\npub struct Mac<'a> {\n    ipcc_mac_802_15_4_cmd_rsp_channel: IpccTxChannel<'a>,\n    ipcc_mac_802_15_4_notification_ack_channel: IpccRxChannel<'a>,\n}\n\nimpl<'a> Mac<'a> {\n    pub(crate) fn new(\n        ipcc_mac_802_15_4_cmd_rsp_channel: IpccTxChannel<'a>,\n        ipcc_mac_802_15_4_notification_ack_channel: IpccRxChannel<'a>,\n    ) -> Self {\n        use crate::tables::{\n            MAC_802_15_4_CMD_BUFFER, MAC_802_15_4_NOTIF_RSP_EVT_BUFFER, Mac802_15_4Table, TL_MAC_802_15_4_TABLE,\n            TL_TRACES_TABLE, TRACES_EVT_QUEUE, TracesTable,\n        };\n\n        unsafe {\n            LinkedListNode::init_head(TRACES_EVT_QUEUE.as_mut_ptr() as *mut _);\n\n            TL_TRACES_TABLE.as_mut_ptr().write_volatile(TracesTable {\n                traces_queue: TRACES_EVT_QUEUE.as_ptr() as *const _,\n            });\n\n            TL_MAC_802_15_4_TABLE.as_mut_ptr().write_volatile(Mac802_15_4Table {\n                p_cmdrsp_buffer: MAC_802_15_4_CMD_BUFFER.as_mut_ptr().cast(),\n                p_notack_buffer: MAC_802_15_4_NOTIF_RSP_EVT_BUFFER.as_mut_ptr().cast(),\n                evt_queue: core::ptr::null_mut(),\n            });\n        };\n\n        Self {\n            ipcc_mac_802_15_4_cmd_rsp_channel,\n            ipcc_mac_802_15_4_notification_ack_channel,\n        }\n    }\n\n    pub const fn split(self) -> (MacRx<'a>, MacTx<'a>) {\n        (\n            MacRx {\n                ipcc_mac_802_15_4_notification_ack_channel: self.ipcc_mac_802_15_4_notification_ack_channel,\n            },\n            MacTx {\n                ipcc_mac_802_15_4_cmd_rsp_channel: self.ipcc_mac_802_15_4_cmd_rsp_channel,\n            },\n        )\n    }\n}\n\npub struct MacTx<'a> {\n    ipcc_mac_802_15_4_cmd_rsp_channel: IpccTxChannel<'a>,\n}\n\nimpl<'a> MacTx<'a> {\n    /// `HW_IPCC_MAC_802_15_4_CmdEvtNot`\n    pub async fn tl_write_and_get_response(&mut self, opcode: u16, payload: &[u8]) -> u8 {\n        self.tl_write(opcode, payload).await;\n        self.ipcc_mac_802_15_4_cmd_rsp_channel.flush().await;\n\n        unsafe {\n            let p_event_packet = MAC_802_15_4_CMD_BUFFER.as_ptr() as *const EvtPacket;\n            let p_mac_rsp_evt = &((*p_event_packet).evt_serial.evt.payload) as *const u8;\n\n            ptr::read_volatile(p_mac_rsp_evt)\n        }\n    }\n\n    /// `TL_MAC_802_15_4_SendCmd`\n    pub async fn tl_write(&mut self, opcode: u16, payload: &[u8]) {\n        self.ipcc_mac_802_15_4_cmd_rsp_channel\n            .send(|| unsafe {\n                CmdPacket::write_into(\n                    MAC_802_15_4_CMD_BUFFER.as_mut_ptr(),\n                    TlPacketType::MacCmd,\n                    opcode,\n                    payload,\n                );\n            })\n            .await;\n    }\n\n    pub async fn send_command<T>(&mut self, cmd: &T) -> Result<(), MacError>\n    where\n        T: MacCommand,\n    {\n        let response = self.tl_write_and_get_response(T::OPCODE as u16, cmd.payload()).await;\n\n        if response == 0x00 {\n            Ok(())\n        } else {\n            Err(MacError::from(response))\n        }\n    }\n}\n\npub struct MacRx<'a> {\n    ipcc_mac_802_15_4_notification_ack_channel: IpccRxChannel<'a>,\n}\n\nimpl<'a> MacRx<'a> {\n    /// `HW_IPCC_MAC_802_15_4_EvtNot`\n    ///\n    /// This function will stall if the previous `EvtBox` has not been dropped\n    pub async fn tl_read(&mut self) -> EvtBox<MacRx<'a>> {\n        // Wait for the last event box to be dropped\n        poll_fn(|cx| {\n            MAC_WAKER.register(cx.waker());\n            if MAC_EVT_OUT.load(Ordering::Acquire) {\n                Poll::Pending\n            } else {\n                Poll::Ready(())\n            }\n        })\n        .await;\n\n        // Return a new event box\n        self.ipcc_mac_802_15_4_notification_ack_channel\n            .receive(|| unsafe {\n                // The closure is not async, therefore the closure must execute to completion (cannot be dropped)\n                // Therefore, the event box is guaranteed to be cleaned up if it's not leaked\n                MAC_EVT_OUT.store(true, Ordering::SeqCst);\n\n                Some(EvtBox::new(MAC_802_15_4_NOTIF_RSP_EVT_BUFFER.as_mut_ptr() as *mut _))\n            })\n            .await\n    }\n\n    pub async fn read<'b>(&mut self) -> Result<MacEvent<'b>, ()> {\n        MacEvent::new(self.tl_read().await)\n    }\n}\n\nimpl<'a> evt::MemoryManager for MacRx<'a> {\n    /// SAFETY: passing a pointer to something other than a managed event packet is UB\n    unsafe fn drop_event_packet(_: *mut EvtPacket) {\n        trace!(\"mac drop event\");\n\n        // Write the ack\n        CmdPacket::write_into(\n            MAC_802_15_4_NOTIF_RSP_EVT_BUFFER.as_mut_ptr() as *mut _,\n            TlPacketType::OtAck,\n            0,\n            &[],\n        );\n\n        // Clear the rx flag\n        let _ = poll_once(Ipcc::receive::<()>(3, || None));\n\n        // Allow a new read call\n        MAC_EVT_OUT.store(false, Ordering::Release);\n        MAC_WAKER.wake();\n    }\n}\n"
  },
  {
    "path": "embassy-stm32-wpan/src/wb55/sub/mm.rs",
    "content": "//! Memory manager routines\nuse core::future::poll_fn;\nuse core::mem::MaybeUninit;\nuse core::task::Poll;\n\nuse aligned::{A4, Aligned};\nuse embassy_stm32::ipcc::IpccTxChannel;\nuse embassy_sync::waitqueue::AtomicWaker;\n\nuse crate::consts::POOL_SIZE;\nuse crate::evt;\nuse crate::evt::EvtPacket;\n#[cfg(feature = \"wb55_ble\")]\nuse crate::tables::BLE_SPARE_EVT_BUF;\nuse crate::tables::{EVT_POOL, FREE_BUF_QUEUE, MemManagerTable, SYS_SPARE_EVT_BUF, TL_MEM_MANAGER_TABLE};\nuse crate::unsafe_linked_list::LinkedListNode;\n\nstatic MM_WAKER: AtomicWaker = AtomicWaker::new();\nstatic mut LOCAL_FREE_BUF_QUEUE: Aligned<A4, MaybeUninit<LinkedListNode>> = Aligned(MaybeUninit::uninit());\n\npub struct MemoryManager<'a> {\n    ipcc_mm_release_buffer_channel: IpccTxChannel<'a>,\n}\n\nimpl<'a> MemoryManager<'a> {\n    pub(crate) fn new(ipcc_mm_release_buffer_channel: IpccTxChannel<'a>) -> Self {\n        unsafe {\n            LinkedListNode::init_head(FREE_BUF_QUEUE.as_mut_ptr());\n            LinkedListNode::init_head(LOCAL_FREE_BUF_QUEUE.as_mut_ptr());\n\n            TL_MEM_MANAGER_TABLE.as_mut_ptr().write_volatile(MemManagerTable {\n                #[cfg(feature = \"wb55_ble\")]\n                spare_ble_buffer: BLE_SPARE_EVT_BUF.as_ptr().cast(),\n                #[cfg(not(feature = \"wb55_ble\"))]\n                spare_ble_buffer: core::ptr::null(),\n                spare_sys_buffer: SYS_SPARE_EVT_BUF.as_ptr().cast(),\n                blepool: EVT_POOL.as_ptr().cast(),\n                blepoolsize: POOL_SIZE as u32,\n                pevt_free_buffer_queue: FREE_BUF_QUEUE.as_mut_ptr(),\n                traces_evt_pool: core::ptr::null(),\n                tracespoolsize: 0,\n            });\n        }\n\n        Self {\n            ipcc_mm_release_buffer_channel,\n        }\n    }\n\n    pub async fn run_queue(&mut self) -> ! {\n        loop {\n            poll_fn(|cx| unsafe {\n                MM_WAKER.register(cx.waker());\n                if critical_section::with(|cs| LinkedListNode::is_empty(cs, LOCAL_FREE_BUF_QUEUE.as_mut_ptr())) {\n                    Poll::Pending\n                } else {\n                    Poll::Ready(())\n                }\n            })\n            .await;\n\n            self.ipcc_mm_release_buffer_channel\n                .send(|| {\n                    critical_section::with(|cs| unsafe {\n                        while let Some(node_ptr) = LinkedListNode::remove_head(cs, LOCAL_FREE_BUF_QUEUE.as_mut_ptr()) {\n                            LinkedListNode::insert_head(cs, FREE_BUF_QUEUE.as_mut_ptr(), node_ptr);\n                        }\n                    })\n                })\n                .await;\n        }\n    }\n}\n\nimpl<'a> evt::MemoryManager for MemoryManager<'a> {\n    /// SAFETY: passing a pointer to something other than a managed event packet is UB\n    unsafe fn drop_event_packet(evt: *mut EvtPacket) {\n        critical_section::with(|cs| unsafe {\n            LinkedListNode::insert_head(cs, LOCAL_FREE_BUF_QUEUE.as_mut_ptr(), evt as *mut _);\n        });\n\n        MM_WAKER.wake();\n    }\n}\n"
  },
  {
    "path": "embassy-stm32-wpan/src/wb55/sub/mod.rs",
    "content": "#[cfg(feature = \"wb55_ble\")]\npub mod ble;\n#[cfg(feature = \"wb55_mac\")]\npub mod mac;\npub mod mm;\npub mod sys;\n"
  },
  {
    "path": "embassy-stm32-wpan/src/wb55/sub/sys.rs",
    "content": "use core::slice;\n\nuse embassy_stm32::ipcc::{IpccRxChannel, IpccTxChannel};\n\nuse crate::cmd::CmdPacket;\nuse crate::consts::TlPacketType;\nuse crate::evt::EvtBox;\n#[cfg(feature = \"wb55_ble\")]\nuse crate::shci::ShciBleInitCmdParam;\nuse crate::shci::{SchiCommandStatus, SchiFromPacket, SchiSysEventReady, ShciFusGetStateErrorCode, ShciOpcode};\nuse crate::sub::mm;\nuse crate::tables::{SysTable, WirelessFwInfoTable};\nuse crate::unsafe_linked_list::LinkedListNode;\nuse crate::wb55::{SYS_CMD_BUF, SYSTEM_EVT_QUEUE, TL_DEVICE_INFO_TABLE, TL_SYS_TABLE};\n\nconst fn slice8_ref(x: &[u32]) -> &[u8] {\n    let len = x.len() * 4;\n    unsafe { slice::from_raw_parts(x.as_ptr() as *const u8, len) }\n}\n\n/// A guard that, once constructed, allows for sys commands to be sent to CPU2.\npub struct Sys<'a> {\n    ipcc_system_cmd_rsp_channel: IpccTxChannel<'a>,\n    ipcc_system_event_channel: IpccRxChannel<'a>,\n}\n\nimpl<'a> Sys<'a> {\n    /// TL_Sys_Init\n    pub(crate) fn new(\n        ipcc_system_cmd_rsp_channel: IpccTxChannel<'a>,\n        ipcc_system_event_channel: IpccRxChannel<'a>,\n    ) -> Self {\n        unsafe {\n            LinkedListNode::init_head(SYSTEM_EVT_QUEUE.as_mut_ptr());\n\n            TL_SYS_TABLE.as_mut_ptr().write_volatile(SysTable {\n                pcmd_buffer: SYS_CMD_BUF.as_mut_ptr(),\n                sys_queue: SYSTEM_EVT_QUEUE.as_ptr(),\n            });\n        }\n\n        Self {\n            ipcc_system_cmd_rsp_channel,\n            ipcc_system_event_channel,\n        }\n    }\n\n    /// Returns CPU2 wireless firmware information (if present).\n    pub fn wireless_fw_info(&self) -> Option<WirelessFwInfoTable> {\n        let info = unsafe { TL_DEVICE_INFO_TABLE.as_mut_ptr().read_volatile().wireless_fw_info_table };\n\n        // Zero version indicates that CPU2 wasn't active and didn't fill the information table\n        if info.version != 0 { Some(info) } else { None }\n    }\n\n    pub async fn write(&mut self, opcode: ShciOpcode, payload: &[u8]) {\n        self.ipcc_system_cmd_rsp_channel\n            .send(|| unsafe {\n                CmdPacket::write_into(SYS_CMD_BUF.as_mut_ptr(), TlPacketType::SysCmd, opcode as u16, payload);\n            })\n            .await;\n    }\n\n    /// `HW_IPCC_SYS_CmdEvtNot`\n    pub async fn write_and_get_response<T: SchiFromPacket>(\n        &mut self,\n        opcode: ShciOpcode,\n        payload: &[u8],\n    ) -> Result<T, ()> {\n        self.write(opcode, payload).await;\n        self.ipcc_system_cmd_rsp_channel.flush().await;\n\n        unsafe { T::from_packet(SYS_CMD_BUF.as_ptr()) }\n    }\n\n    #[cfg(feature = \"wb55_mac\")]\n    pub async fn shci_c2_mac_802_15_4_init(&mut self) -> Result<SchiCommandStatus, ()> {\n        self.write_and_get_response(ShciOpcode::Mac802_15_4Init, &[]).await\n    }\n\n    /// Send a request to CPU2 to initialise the BLE stack.\n    ///\n    /// This must be called before any BLE commands are sent via the BLE channel (according to\n    /// AN5289, Figures 65 and 66). It should only be called after CPU2 sends a system event, via\n    /// `HW_IPCC_SYS_EvtNot`, aka `IoBusCallBackUserEvt` (as detailed in Figure 65), aka\n    /// [crate::sub::ble::hci::host::uart::UartHci::read].\n    #[cfg(feature = \"wb55_ble\")]\n    pub async fn shci_c2_ble_init(&mut self, param: ShciBleInitCmdParam) -> Result<SchiCommandStatus, ()> {\n        self.write_and_get_response(ShciOpcode::BleInit, param.payload()).await\n    }\n\n    pub async fn shci_c2_fus_getstate(&mut self) -> Result<ShciFusGetStateErrorCode, ()> {\n        self.write_and_get_response(ShciOpcode::FusGetState, &[]).await\n    }\n\n    /// Send a request to CPU2 to start the wireless stack\n    pub async fn shci_c2_fus_startws(&mut self) -> Result<SchiCommandStatus, ()> {\n        self.write_and_get_response(ShciOpcode::FusStartWirelessStack, &[])\n            .await\n    }\n\n    /// Send a request to CPU2 to upgrade the firmware\n    pub async fn shci_c2_fus_fwupgrade(&mut self, fw_src_add: u32, fw_dst_add: u32) -> Result<SchiCommandStatus, ()> {\n        let buf = [fw_src_add, fw_dst_add];\n        let len = if fw_dst_add != 0 {\n            2\n        } else if fw_src_add != 0 {\n            1\n        } else {\n            0\n        };\n\n        self.write_and_get_response(ShciOpcode::FusFirmwareUpgrade, slice8_ref(&buf[..len]))\n            .await\n    }\n\n    pub async fn read_ready(&mut self) -> Result<SchiSysEventReady, ()> {\n        self.read().await.payload()[0].try_into()\n    }\n\n    /// `HW_IPCC_SYS_EvtNot`\n    ///\n    /// This method takes the place of the `HW_IPCC_SYS_EvtNot`/`SysUserEvtRx`/`APPE_SysUserEvtRx`,\n    /// as the embassy implementation avoids the need to call C public bindings, and instead\n    /// handles the event channels directly.\n    pub async fn read(&mut self) -> EvtBox<mm::MemoryManager<'_>> {\n        self.ipcc_system_event_channel\n            .receive(|| unsafe {\n                if let Some(node_ptr) =\n                    critical_section::with(|cs| LinkedListNode::remove_head(cs, SYSTEM_EVT_QUEUE.as_mut_ptr()))\n                {\n                    Some(EvtBox::new(node_ptr.cast()))\n                } else {\n                    None\n                }\n            })\n            .await\n    }\n}\n"
  },
  {
    "path": "embassy-stm32-wpan/src/wb55/tables.rs",
    "content": "use core::mem::MaybeUninit;\n\nuse aligned::{A4, Aligned};\nuse bit_field::BitField;\n\nuse crate::cmd::{AclDataPacket, CmdPacket};\n#[cfg(feature = \"wb55_mac\")]\nuse crate::consts::C_SIZE_CMD_STRING;\nuse crate::consts::{POOL_SIZE, TL_CS_EVT_SIZE, TL_EVT_HEADER_SIZE, TL_PACKET_HEADER_SIZE};\nuse crate::unsafe_linked_list::LinkedListNode;\n\n#[derive(Debug, Copy, Clone)]\n#[repr(C, packed)]\npub struct SafeBootInfoTable {\n    version: u32,\n}\n\n#[derive(Debug, Copy, Clone)]\n#[repr(C, packed)]\npub struct RssInfoTable {\n    pub version: u32,\n    pub memory_size: u32,\n    pub rss_info: u32,\n}\n\n/**\n * Version\n * \\[0:3\\]   = Build - 0: Untracked - 15:Released - x: Tracked version\n * \\[4:7\\]   = branch - 0: Mass Market - x: ...\n * \\[8:15\\]  = Subversion\n * \\[16:23\\] = Version minor\n * \\[24:31\\] = Version major\n *\n * Memory Size\n * \\[0:7\\]   = Flash ( Number of 4k sector)\n * \\[8:15\\]  = Reserved ( Shall be set to 0 - may be used as flash extension )\n * \\[16:23\\] = SRAM2b ( Number of 1k sector)\n * \\[24:31\\] = SRAM2a ( Number of 1k sector)\n */\n#[derive(Debug, Copy, Clone)]\n#[repr(C, packed)]\npub struct WirelessFwInfoTable {\n    pub version: u32,\n    pub memory_size: u32,\n    pub thread_info: u32,\n    pub ble_info: u32,\n}\n\nimpl WirelessFwInfoTable {\n    pub fn version_major(&self) -> u8 {\n        let version = self.version;\n        (version.get_bits(24..31) & 0xff) as u8\n    }\n\n    pub fn version_minor(&self) -> u8 {\n        let version = self.version;\n        (version.clone().get_bits(16..23) & 0xff) as u8\n    }\n\n    pub fn subversion(&self) -> u8 {\n        let version = self.version;\n        (version.clone().get_bits(8..15) & 0xff) as u8\n    }\n\n    /// Size of FLASH, expressed in number of 4K sectors.\n    pub fn flash_size(&self) -> u8 {\n        let memory_size = self.memory_size;\n        (memory_size.clone().get_bits(0..7) & 0xff) as u8\n    }\n\n    /// Size of SRAM2a, expressed in number of 1K sectors.\n    pub fn sram2a_size(&self) -> u8 {\n        let memory_size = self.memory_size;\n        (memory_size.clone().get_bits(24..31) & 0xff) as u8\n    }\n\n    /// Size of SRAM2b, expressed in number of 1K sectors.\n    pub fn sram2b_size(&self) -> u8 {\n        let memory_size = self.memory_size;\n        (memory_size.clone().get_bits(16..23) & 0xff) as u8\n    }\n}\n\n#[derive(Debug, Clone)]\n#[repr(C)]\npub struct DeviceInfoTable {\n    pub safe_boot_info_table: SafeBootInfoTable,\n    pub rss_info_table: RssInfoTable,\n    pub wireless_fw_info_table: WirelessFwInfoTable,\n}\n\n/// The bluetooth reference table, as defined in figure 67 of STM32WX AN5289.\n#[derive(Debug)]\n#[repr(C)]\npub struct BleTable {\n    /// A pointer to the buffer that is used for sending BLE commands.\n    pub pcmd_buffer: *mut CmdPacket,\n    /// A pointer to the buffer used for storing Command statuses.\n    pub pcs_buffer: *const u8,\n    /// A pointer to the event queue, over which IPCC BLE events are sent. This may be accessed via\n    /// [crate::sub::ble::Ble::tl_read].\n    pub pevt_queue: *const u8,\n    /// A pointer to the buffer that is used for sending HCI (Host-Controller Interface) ACL\n    /// (Asynchronous Connection-oriented Logical transport) commands (unused).\n    pub phci_acl_data_buffer: *mut AclDataPacket,\n}\n\n#[derive(Debug)]\n#[repr(C)]\npub struct ThreadTable {\n    pub nostack_buffer: *const u8,\n    pub clicmdrsp_buffer: *const u8,\n    pub otcmdrsp_buffer: *const u8,\n}\n\n#[derive(Debug)]\n#[repr(C)]\npub struct LldTestsTable {\n    pub clicmdrsp_buffer: *const u8,\n    pub m0cmd_buffer: *const u8,\n}\n\n// TODO: use later\n#[derive(Debug)]\n#[repr(C)]\npub struct BleLldTable {\n    pub cmdrsp_buffer: *const u8,\n    pub m0cmd_buffer: *const u8,\n}\n\n// TODO: use later\n#[derive(Debug)]\n#[repr(C)]\npub struct ZigbeeTable {\n    pub notif_m0_to_m4_buffer: *const u8,\n    pub appli_cmd_m4_to_m0_bufer: *const u8,\n    pub request_m0_to_m4_buffer: *const u8,\n}\n\n#[derive(Debug)]\n#[repr(C)]\npub struct SysTable {\n    pub pcmd_buffer: *mut CmdPacket,\n    pub sys_queue: *const LinkedListNode,\n}\n\n#[derive(Debug)]\n#[repr(C)]\npub struct MemManagerTable {\n    pub spare_ble_buffer: *const u8,\n    pub spare_sys_buffer: *const u8,\n\n    pub blepool: *const u8,\n    pub blepoolsize: u32,\n\n    pub pevt_free_buffer_queue: *mut LinkedListNode,\n\n    pub traces_evt_pool: *const u8,\n    pub tracespoolsize: u32,\n}\n\n#[derive(Debug)]\n#[repr(C)]\npub struct TracesTable {\n    pub traces_queue: *const u8,\n}\n\n#[derive(Debug)]\n#[repr(C)]\npub struct Mac802_15_4Table {\n    pub p_cmdrsp_buffer: *const u8,\n    pub p_notack_buffer: *const u8,\n    pub evt_queue: *const u8,\n}\n\n/// Reference table. Contains pointers to all other tables.\n#[derive(Debug, Copy, Clone)]\n#[repr(C)]\npub struct RefTable {\n    pub device_info_table: *const DeviceInfoTable,\n    pub ble_table: *const BleTable,\n    pub thread_table: *const ThreadTable,\n    pub sys_table: *const SysTable,\n    pub mem_manager_table: *const MemManagerTable,\n    pub traces_table: *const TracesTable,\n    pub mac_802_15_4_table: *const Mac802_15_4Table,\n    pub zigbee_table: *const ZigbeeTable,\n    pub lld_tests_table: *const LldTestsTable,\n    pub ble_lld_table: *const BleLldTable,\n}\n\n// --------------------- ref table ---------------------\n#[unsafe(link_section = \"TL_REF_TABLE\")]\npub static mut TL_REF_TABLE: MaybeUninit<RefTable> = MaybeUninit::zeroed();\n\n#[unsafe(link_section = \"MB_MEM1\")]\npub static mut TL_DEVICE_INFO_TABLE: Aligned<A4, MaybeUninit<DeviceInfoTable>> = Aligned(MaybeUninit::zeroed());\n\n#[unsafe(link_section = \"MB_MEM1\")]\npub static mut TL_BLE_TABLE: Aligned<A4, MaybeUninit<BleTable>> = Aligned(MaybeUninit::zeroed());\n\n#[unsafe(link_section = \"MB_MEM1\")]\npub static mut TL_THREAD_TABLE: Aligned<A4, MaybeUninit<ThreadTable>> = Aligned(MaybeUninit::zeroed());\n\n#[unsafe(link_section = \"MB_MEM1\")]\npub static mut TL_LLD_TESTS_TABLE: Aligned<A4, MaybeUninit<LldTestsTable>> = Aligned(MaybeUninit::zeroed());\n\n#[unsafe(link_section = \"MB_MEM1\")]\npub static mut TL_BLE_LLD_TABLE: Aligned<A4, MaybeUninit<BleLldTable>> = Aligned(MaybeUninit::zeroed());\n\n#[unsafe(link_section = \"MB_MEM1\")]\npub static mut TL_SYS_TABLE: Aligned<A4, MaybeUninit<SysTable>> = Aligned(MaybeUninit::zeroed());\n\n#[unsafe(link_section = \"MB_MEM1\")]\npub static mut TL_MEM_MANAGER_TABLE: Aligned<A4, MaybeUninit<MemManagerTable>> = Aligned(MaybeUninit::zeroed());\n\n#[unsafe(link_section = \"MB_MEM1\")]\npub static mut TL_TRACES_TABLE: Aligned<A4, MaybeUninit<TracesTable>> = Aligned(MaybeUninit::zeroed());\n\n#[unsafe(link_section = \"MB_MEM1\")]\npub static mut TL_MAC_802_15_4_TABLE: Aligned<A4, MaybeUninit<Mac802_15_4Table>> = Aligned(MaybeUninit::zeroed());\n\n#[unsafe(link_section = \"MB_MEM1\")]\npub static mut TL_ZIGBEE_TABLE: Aligned<A4, MaybeUninit<ZigbeeTable>> = Aligned(MaybeUninit::zeroed());\n\n// --------------------- tables ---------------------\n#[unsafe(link_section = \"MB_MEM1\")]\npub static mut FREE_BUF_QUEUE: Aligned<A4, MaybeUninit<LinkedListNode>> = Aligned(MaybeUninit::zeroed());\n\n#[allow(dead_code)]\n#[unsafe(link_section = \"MB_MEM1\")]\npub static mut TRACES_EVT_QUEUE: Aligned<A4, MaybeUninit<LinkedListNode>> = Aligned(MaybeUninit::zeroed());\n\n#[unsafe(link_section = \"MB_MEM2\")]\npub static mut CS_BUFFER: Aligned<A4, MaybeUninit<[u8; TL_PACKET_HEADER_SIZE + TL_EVT_HEADER_SIZE + TL_CS_EVT_SIZE]>> =\n    Aligned(MaybeUninit::zeroed());\n\n#[unsafe(link_section = \"MB_MEM2\")]\npub static mut EVT_QUEUE: Aligned<A4, MaybeUninit<LinkedListNode>> = Aligned(MaybeUninit::zeroed());\n\n#[unsafe(link_section = \"MB_MEM2\")]\npub static mut SYSTEM_EVT_QUEUE: Aligned<A4, MaybeUninit<LinkedListNode>> = Aligned(MaybeUninit::zeroed());\n\n// --------------------- app tables ---------------------\n#[cfg(feature = \"wb55_mac\")]\n#[unsafe(link_section = \"MB_MEM2\")]\npub static mut MAC_802_15_4_CMD_BUFFER: Aligned<A4, MaybeUninit<CmdPacket>> = Aligned(MaybeUninit::zeroed());\n\n#[cfg(feature = \"wb55_mac\")]\n#[unsafe(link_section = \"MB_MEM2\")]\npub static mut MAC_802_15_4_NOTIF_RSP_EVT_BUFFER: MaybeUninit<\n    Aligned<A4, [u8; TL_PACKET_HEADER_SIZE + TL_EVT_HEADER_SIZE + 255]>,\n> = MaybeUninit::zeroed();\n\n#[unsafe(link_section = \"MB_MEM2\")]\npub static mut EVT_POOL: Aligned<A4, MaybeUninit<[u8; POOL_SIZE]>> = Aligned(MaybeUninit::zeroed());\n\n#[unsafe(link_section = \"MB_MEM2\")]\npub static mut SYS_CMD_BUF: Aligned<A4, MaybeUninit<CmdPacket>> = Aligned(MaybeUninit::zeroed());\n\n#[unsafe(link_section = \"MB_MEM2\")]\npub static mut SYS_SPARE_EVT_BUF: Aligned<A4, MaybeUninit<[u8; TL_PACKET_HEADER_SIZE + TL_EVT_HEADER_SIZE + 255]>> =\n    Aligned(MaybeUninit::zeroed());\n\n#[cfg(feature = \"wb55_mac\")]\n#[unsafe(link_section = \"MB_MEM2\")]\npub static mut MAC_802_15_4_CNFINDNOT: Aligned<A4, MaybeUninit<[u8; C_SIZE_CMD_STRING]>> =\n    Aligned(MaybeUninit::zeroed());\n\n#[cfg(feature = \"wb55_ble\")]\n#[unsafe(link_section = \"MB_MEM1\")]\npub static mut BLE_CMD_BUFFER: Aligned<A4, MaybeUninit<CmdPacket>> = Aligned(MaybeUninit::zeroed());\n\n#[cfg(feature = \"wb55_ble\")]\n#[unsafe(link_section = \"MB_MEM2\")]\npub static mut BLE_SPARE_EVT_BUF: Aligned<A4, MaybeUninit<[u8; TL_PACKET_HEADER_SIZE + TL_EVT_HEADER_SIZE + 255]>> =\n    Aligned(MaybeUninit::zeroed());\n\n#[cfg(feature = \"wb55_ble\")]\n#[unsafe(link_section = \"MB_MEM2\")]\n//                                                 fuck these \"magic\" numbers from ST ---v---v\npub static mut HCI_ACL_DATA_BUFFER: Aligned<A4, MaybeUninit<[u8; TL_PACKET_HEADER_SIZE + 5 + 251]>> =\n    Aligned(MaybeUninit::zeroed());\n"
  },
  {
    "path": "embassy-stm32-wpan/src/wb55/unsafe_linked_list.rs",
    "content": "//! Unsafe linked list.\n//! Translated from ST's C by `c2rust` tool.\n\n#![allow(\n    dead_code,\n    mutable_transmutes,\n    non_camel_case_types,\n    non_snake_case,\n    non_upper_case_globals,\n    unused_assignments,\n    unused_mut\n)]\n\nuse core::fmt::Debug;\nuse core::ptr;\n\nuse critical_section::CriticalSection;\n\n#[derive(Copy, Clone)]\n#[repr(C, packed(4))]\npub struct LinkedListNode {\n    pub next: *mut LinkedListNode,\n    pub prev: *mut LinkedListNode,\n}\n\nimpl Default for LinkedListNode {\n    fn default() -> Self {\n        LinkedListNode {\n            next: core::ptr::null_mut(),\n            prev: core::ptr::null_mut(),\n        }\n    }\n}\n\nimpl LinkedListNode {\n    pub unsafe fn init_head(mut p_list_head: *mut LinkedListNode) {\n        ptr::write_volatile(\n            p_list_head,\n            LinkedListNode {\n                next: p_list_head,\n                prev: p_list_head,\n            },\n        );\n    }\n\n    pub unsafe fn is_empty(_cs: CriticalSection, mut p_list_head: *mut LinkedListNode) -> bool {\n        ptr::read_volatile(p_list_head).next == p_list_head\n    }\n\n    /// Insert `node` after `list_head` and before the next node\n    pub unsafe fn insert_head(\n        _cs: CriticalSection,\n        mut p_list_head: *mut LinkedListNode,\n        mut p_node: *mut LinkedListNode,\n    ) {\n        let mut list_head = ptr::read_volatile(p_list_head);\n        if p_list_head != list_head.next {\n            let mut node_next = ptr::read_volatile(list_head.next);\n            let node = LinkedListNode {\n                next: list_head.next,\n                prev: p_list_head,\n            };\n\n            list_head.next = p_node;\n            node_next.prev = p_node;\n\n            // All nodes must be written because they will all be seen by another core\n            ptr::write_volatile(p_node, node);\n            ptr::write_volatile(node.next, node_next);\n            ptr::write_volatile(p_list_head, list_head);\n        } else {\n            let node = LinkedListNode {\n                next: list_head.next,\n                prev: p_list_head,\n            };\n\n            list_head.next = p_node;\n            list_head.prev = p_node;\n\n            // All nodes must be written because they will all be seen by another core\n            ptr::write_volatile(p_node, node);\n            ptr::write_volatile(p_list_head, list_head);\n        }\n    }\n\n    /// Insert `node` before `list_tail` and after the second-to-last node\n    pub unsafe fn insert_tail(\n        _cs: CriticalSection,\n        mut p_list_tail: *mut LinkedListNode,\n        mut p_node: *mut LinkedListNode,\n    ) {\n        let mut list_tail = ptr::read_volatile(p_list_tail);\n        if p_list_tail != list_tail.prev {\n            let mut node_prev = ptr::read_volatile(list_tail.prev);\n            let node = LinkedListNode {\n                next: p_list_tail,\n                prev: list_tail.prev,\n            };\n\n            list_tail.prev = p_node;\n            node_prev.next = p_node;\n\n            // All nodes must be written because they will all be seen by another core\n            ptr::write_volatile(p_node, node);\n            ptr::write_volatile(node.prev, node_prev);\n            ptr::write_volatile(p_list_tail, list_tail);\n        } else {\n            let node = LinkedListNode {\n                next: p_list_tail,\n                prev: list_tail.prev,\n            };\n\n            list_tail.prev = p_node;\n            list_tail.next = p_node;\n\n            // All nodes must be written because they will all be seen by another core\n            ptr::write_volatile(p_node, node);\n            ptr::write_volatile(p_list_tail, list_tail);\n        }\n    }\n\n    /// Remove `node` from the linked list\n    pub unsafe fn remove_node(_cs: CriticalSection, mut p_node: *mut LinkedListNode) {\n        let node = ptr::read_unaligned(p_node);\n\n        if node.next != node.prev {\n            let mut node_next = ptr::read_volatile(node.next);\n            let mut node_prev = ptr::read_volatile(node.prev);\n\n            node_prev.next = node.next;\n            node_next.prev = node.prev;\n\n            ptr::write_volatile(node.next, node_next);\n            ptr::write_volatile(node.prev, node_prev);\n        } else {\n            let mut node_next = ptr::read_volatile(node.next);\n\n            node_next.next = node.next;\n            node_next.prev = node.prev;\n\n            ptr::write_volatile(node.next, node_next);\n        }\n    }\n\n    /// Remove `list_head` and return a pointer to the `node`.\n    pub unsafe fn remove_head(\n        _cs: CriticalSection,\n        mut p_list_head: *mut LinkedListNode,\n    ) -> Option<*mut LinkedListNode> {\n        let list_head = ptr::read_volatile(p_list_head);\n\n        if list_head.next == p_list_head {\n            None\n        } else {\n            // Allowed because a removed node is not seen by another core\n            let p_node = list_head.next;\n            Self::remove_node(_cs, p_node);\n\n            Some(p_node)\n        }\n    }\n\n    /// Remove `list_tail` and return a pointer to the `node`.\n    pub unsafe fn remove_tail(\n        _cs: CriticalSection,\n        mut p_list_tail: *mut LinkedListNode,\n    ) -> Option<*mut LinkedListNode> {\n        let list_tail = ptr::read_volatile(p_list_tail);\n\n        if list_tail.prev == p_list_tail {\n            None\n        } else {\n            // Allowed because a removed node is not seen by another core\n            let p_node = list_tail.prev;\n            Self::remove_node(_cs, p_node);\n\n            Some(p_node)\n        }\n    }\n\n    pub unsafe fn insert_node_after(\n        _cs: CriticalSection,\n        mut p_node: *mut LinkedListNode,\n        mut p_ref_node: *mut LinkedListNode,\n    ) {\n        let mut node = ptr::read_volatile(p_node);\n        let mut ref_node = ptr::read_volatile(p_ref_node);\n        let mut prev_node = ptr::read_volatile(ref_node.next);\n\n        node.next = ref_node.next;\n        node.prev = p_ref_node;\n        ref_node.next = p_node;\n        prev_node.prev = p_node;\n\n        ptr::write_volatile(p_node, node);\n        ptr::write_volatile(p_ref_node, ref_node);\n        ptr::write_volatile(node.next, prev_node);\n    }\n\n    pub unsafe fn insert_node_before(\n        _cs: CriticalSection,\n        mut node: *mut LinkedListNode,\n        mut ref_node: *mut LinkedListNode,\n    ) {\n        (*node).next = ref_node;\n        (*node).prev = (*ref_node).prev;\n        (*ref_node).prev = node;\n        (*(*node).prev).next = node;\n\n        todo!(\"this function has not been converted to volatile semantics\");\n    }\n\n    pub unsafe fn get_size(_cs: CriticalSection, mut list_head: *mut LinkedListNode) -> usize {\n        let mut size = 0;\n        let mut temp: *mut LinkedListNode = core::ptr::null_mut::<LinkedListNode>();\n\n        temp = (*list_head).next;\n        while temp != list_head {\n            size += 1;\n            temp = (*temp).next\n        }\n\n        let _ = size;\n\n        todo!(\"this function has not been converted to volatile semantics\");\n    }\n\n    pub unsafe fn get_next_node(_cs: CriticalSection, mut p_ref_node: *mut LinkedListNode) -> *mut LinkedListNode {\n        let ref_node = ptr::read_volatile(p_ref_node);\n\n        // Allowed because a removed node is not seen by another core\n        ref_node.next\n    }\n\n    pub unsafe fn get_prev_node(_cs: CriticalSection, mut p_ref_node: *mut LinkedListNode) -> *mut LinkedListNode {\n        let ref_node = ptr::read_volatile(p_ref_node);\n\n        // Allowed because a removed node is not seen by another core\n        ref_node.prev\n    }\n}\n\npub struct DebuggableLinkedListNode(*const LinkedListNode);\n\nimpl Debug for DebuggableLinkedListNode {\n    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {\n        // Safe because this is just reading memory, and using unaligned to do it\n        let p_node = self.0;\n\n        f.write_fmt(format_args!(\"iterating list from node: {:x}\", p_node as usize))?;\n\n        let mut p_current_node = p_node;\n        for _ in 0..30 {\n            let current_node = unsafe { ptr::read_unaligned(p_current_node) };\n            f.write_fmt(format_args!(\n                \"node (prev, current, next): {:x}, {:x}, {:x}\",\n                current_node.prev as usize, p_current_node as usize, current_node.next as usize\n            ))?;\n\n            if current_node.next == p_node as *mut _ {\n                break;\n            }\n\n            p_current_node = current_node.next;\n        }\n\n        Ok(())\n    }\n}\n"
  },
  {
    "path": "embassy-stm32-wpan/src/wba/RNG_INTEGRATION.md",
    "content": "# Hardware RNG Integration for STM32WBA BLE Stack\n\nThis document describes how to integrate the hardware RNG from `embassy-stm32` with the WBA BLE stack.\n\n## Overview\n\nThe WBA link layer **requires** hardware random number generation for cryptographic operations and Bluetooth security features. The hardware RNG peripheral must be initialized and registered with the link layer before using any BLE/MAC functionality.\n\n**Important**: Hardware RNG is mandatory. The link layer will panic with a clear error message if RNG is not initialized when random numbers are requested.\n\n## Usage\n\n### Step 1: Set up the RNG peripheral\n\nFirst, initialize the hardware RNG peripheral using `embassy-stm32`:\n\n```rust\nuse embassy_stm32::rng::Rng;\nuse embassy_stm32::peripherals;\nuse embassy_stm32::bind_interrupts;\n\nbind_interrupts!(struct Irqs {\n    RNG => embassy_stm32::rng::InterruptHandler<peripherals::RNG>;\n});\n\n// Initialize the RNG peripheral\nlet mut rng = Rng::new(p.RNG, Irqs);\n```\n\n### Step 2: Register the RNG with the link layer\n\nBefore initializing the BLE stack, register your RNG instance with the link layer:\n\n```rust\nuse embassy_stm32_wpan::wba::linklayer_plat::set_rng_instance;\n\n// Register the RNG instance\n// SAFETY: The RNG instance must remain valid for the entire lifetime of BLE stack usage\nunsafe {\n    set_rng_instance(&mut rng as *mut _ as *mut ());\n}\n```\n\n### Step 3: Initialize the BLE stack\n\nNow you can initialize the BLE stack as usual. The link layer will automatically use the hardware RNG:\n\n```rust\nuse embassy_stm32_wpan::bindings::mac::{ST_MAC_callbacks_t, ST_MAC_init};\n\n// Your MAC callbacks\nstatic MAC_CALLBACKS: ST_MAC_callbacks_t = ST_MAC_callbacks_t {\n    // ... your callbacks\n};\n\n// Initialize the MAC layer\nlet status = unsafe { ST_MAC_init(&MAC_CALLBACKS as *const _ as *mut _) };\n```\n\n## Complete Example\n\n```rust\n#![no_std]\n#![no_main]\n\nuse embassy_executor::Spawner;\nuse embassy_stm32::Config;\nuse embassy_stm32::rng::Rng;\nuse embassy_stm32::bind_interrupts;\nuse embassy_stm32_wpan::wba::linklayer_plat::set_rng_instance;\nuse embassy_stm32_wpan::bindings::mac::{ST_MAC_callbacks_t, ST_MAC_init};\n\nbind_interrupts!(struct Irqs {\n    RNG => embassy_stm32::rng::InterruptHandler<embassy_stm32::peripherals::RNG>;\n});\n\nstatic MAC_CALLBACKS: ST_MAC_callbacks_t = ST_MAC_callbacks_t {\n    // Initialize all callbacks to None\n    mlmeAssociateCnfCb: None,\n    mlmeAssociateIndCb: None,\n    // ... (other callbacks)\n};\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    // Configure system clocks\n    let mut config = Config::default();\n    config.rcc.mux.rngsel = embassy_stm32::rcc::mux::Rngsel::HSI;\n    // ... (other clock configuration)\n    \n    let p = embassy_stm32::init(config);\n    \n    // Initialize hardware RNG\n    let mut rng = Rng::new(p.RNG, Irqs);\n    \n    // Register RNG with the link layer\n    unsafe {\n        set_rng_instance(&mut rng as *mut _ as *mut ());\n    }\n    \n    // Initialize the BLE stack\n    let status = unsafe { ST_MAC_init(&MAC_CALLBACKS as *const _ as *mut _) };\n    \n    // Your application logic here\n    loop {\n        // ...\n    }\n}\n```\n\n## Important Notes\n\n1. **Lifetime**: The RNG instance must remain valid for the entire lifetime of the BLE stack usage. Make sure not to drop the RNG instance while the link layer might still need it.\n\n2. **Safety**: The `set_rng_instance` function performs proper synchronization. However, the caller must ensure the pointer remains valid for the entire lifetime of BLE stack usage.\n\n3. **Mandatory RNG**: Hardware RNG is required. If `set_rng_instance` is not called before the BLE stack requests random numbers, the application will panic with the message: \"Hardware RNG not initialized! Call embassy_stm32_wpan::wba::set_rng_instance() before using the BLE stack.\"\n\n4. **Clock Configuration**: Make sure to properly configure the RNG clock source in your RCC configuration. For STM32WBA, this is typically done via `config.rcc.mux.rngsel`.\n\n## Clearing the RNG Instance\n\nIf you need to clear the hardware RNG instance (e.g., to fall back to software PRNG), you can use:\n\n```rust\nuse embassy_stm32_wpan::wba::linklayer_plat::clear_rng_instance;\n\nclear_rng_instance();\n```\n"
  },
  {
    "path": "embassy-stm32-wpan/src/wba/bindings.rs",
    "content": "pub use stm32_bindings::bindings::{mac, wba_ble_stack as ble, wba_link_layer as link_layer};\n"
  },
  {
    "path": "embassy-stm32-wpan/src/wba/ble.rs",
    "content": "//! High-level BLE API for STM32WBA\n//!\n//! This module provides the main `Ble` struct that manages the BLE stack lifecycle\n//! and provides access to GAP functionality including connection management.\n\nuse core::cell::RefCell;\nuse core::sync::atomic::{AtomicBool, Ordering};\n\nuse embassy_stm32::peripherals::RNG;\nuse embassy_stm32::rng::Rng;\nuse embassy_sync::blocking_mutex::Mutex;\nuse embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;\n\nuse crate::linklayer_plat::HARDWARE_RNG;\nuse crate::wba::error::BleError;\nuse crate::wba::gap::Advertiser;\nuse crate::wba::gap::connection::{\n    Connection, ConnectionInitParams, ConnectionManager, ConnectionRole, DisconnectReason, GapEvent, LePhy,\n    MAX_CONNECTIONS,\n};\nuse crate::wba::gap::scanner::Scanner;\nuse crate::wba::hci::command::CommandSender;\nuse crate::wba::hci::event::{Event, EventParams, read_event};\nuse crate::wba::hci::types::{Address, Handle, Status};\nuse crate::wba::ll_sys::init_ble_stack;\n\n/// Main BLE interface\n///\n/// This struct provides the primary interface to the BLE stack.\n///\n/// # Example\n///\n/// ```no_run\n/// use embassy_stm32_wpan::wba::{Ble, gap::{AdvData, AdvParams}};\n///\n/// let mut ble = Ble::new();\n///\n/// // Initialize BLE stack\n/// ble.init().await.unwrap();\n///\n/// // Create advertising data\n/// let mut adv_data = AdvData::new();\n/// adv_data.add_flags(0x06).unwrap();\n/// adv_data.add_name(\"MyDevice\").unwrap();\n///\n/// // Start advertising\n/// let mut advertiser = ble.advertiser();\n/// advertiser.start(AdvParams::default(), adv_data, None).unwrap();\n///\n/// // Event loop\n/// loop {\n///     let event = ble.read_event().await;\n///     // Handle BLE events\n/// }\n/// ```\npub struct Ble {\n    cmd_sender: CommandSender,\n    initialized: AtomicBool,\n    connections: ConnectionManager<MAX_CONNECTIONS>,\n}\n\nimpl Ble {\n    /// Create a new BLE instance\n    ///\n    /// Note: You must call `init()` before using other BLE functionality.\n    pub fn new(rng: &'static Mutex<CriticalSectionRawMutex, RefCell<Rng<'static, RNG>>>) -> Self {\n        unsafe { HARDWARE_RNG.replace(rng) };\n\n        Self {\n            cmd_sender: CommandSender::new(),\n            initialized: AtomicBool::new(false),\n            connections: ConnectionManager::new(),\n        }\n    }\n\n    /// Initialize the BLE stack\n    ///\n    /// This function performs the following initialization steps:\n    /// 1. Resets the BLE controller\n    /// 2. Reads and logs the local version information\n    /// 3. Reads the BD address\n    /// 4. Sets the event mask\n    /// 5. Reads buffer sizes\n    /// 6. Reads supported features\n    ///\n    /// Must be called before any other BLE operations.\n    ///\n    /// # Returns\n    ///\n    /// - `Ok(())` if initialization succeeded\n    /// - `Err(BleError)` if any initialization step failed\n    pub fn init(&mut self) -> Result<(), BleError> {\n        if self.initialized.load(Ordering::Acquire) {\n            return Ok(());\n        }\n\n        // 0. Initialize the BLE stack using BleStack_Init\n        // This properly initializes the BLE host stack including memory management,\n        // which is required before ll_intf_init can work properly.\n        init_ble_stack().map_err(|status| {\n            #[cfg(feature = \"defmt\")]\n            defmt::error!(\"BLE stack initialization failed: 0x{:02X}\", status);\n            BleError::InitializationFailed\n        })?;\n\n        #[cfg(feature = \"defmt\")]\n        defmt::info!(\"Ble::init: BLE stack initialized, sending HCI reset\");\n\n        // 1. Reset BLE controller\n        self.cmd_sender.reset()?;\n\n        // 2. Read local version information\n        let version = self.cmd_sender.read_local_version()?;\n        #[cfg(feature = \"defmt\")]\n        defmt::info!(\n            \"BLE Controller: HCI Version {}.{}, Revision: 0x{:04X}, LMP Version: {}.{}, Manufacturer: 0x{:04X}\",\n            version.hci_version >> 4,\n            version.hci_version & 0x0F,\n            version.hci_revision,\n            version.lmp_version >> 4,\n            version.lmp_version & 0x0F,\n            version.manufacturer_name\n        );\n\n        // 3. Read BD address\n        let bd_addr = self.cmd_sender.read_bd_addr()?;\n        #[cfg(feature = \"defmt\")]\n        defmt::info!(\n            \"BD Address: {:02X}:{:02X}:{:02X}:{:02X}:{:02X}:{:02X}\",\n            bd_addr[5],\n            bd_addr[4],\n            bd_addr[3],\n            bd_addr[2],\n            bd_addr[1],\n            bd_addr[0]\n        );\n\n        // 4. Set event mask (enable all events)\n        // Note: The ST BLE stack handles event masks internally, so these calls\n        // may not be needed. Skip if they fail with UnknownCommand.\n        #[cfg(feature = \"defmt\")]\n        defmt::info!(\"Calling set_event_mask...\");\n        if let Err(e) = self.cmd_sender.set_event_mask(0xFFFF_FFFF_FFFF_FFFF) {\n            #[cfg(feature = \"defmt\")]\n            defmt::warn!(\"set_event_mask failed: {:?} (may be handled internally)\", e);\n        } else {\n            #[cfg(feature = \"defmt\")]\n            defmt::info!(\"set_event_mask OK\");\n        }\n\n        #[cfg(feature = \"defmt\")]\n        defmt::info!(\"Calling le_set_event_mask...\");\n        if let Err(e) = self.cmd_sender.le_set_event_mask(0xFFFF_FFFF_FFFF_FFFF) {\n            #[cfg(feature = \"defmt\")]\n            defmt::warn!(\"le_set_event_mask failed: {:?} (may be handled internally)\", e);\n        } else {\n            #[cfg(feature = \"defmt\")]\n            defmt::info!(\"le_set_event_mask OK\");\n        }\n\n        // 5. Read buffer sizes (optional - skip if not available)\n        #[cfg(feature = \"defmt\")]\n        defmt::info!(\"Calling le_read_buffer_size...\");\n        match self.cmd_sender.le_read_buffer_size() {\n            Ok((acl_len, acl_num, iso_len, iso_num)) => {\n                #[cfg(feature = \"defmt\")]\n                defmt::info!(\n                    \"Buffer sizes - ACL: {} bytes x {} packets, ISO: {} bytes x {} packets\",\n                    acl_len,\n                    acl_num,\n                    iso_len,\n                    iso_num\n                );\n            }\n            Err(e) => {\n                #[cfg(feature = \"defmt\")]\n                defmt::warn!(\"le_read_buffer_size failed: {:?} (skipping)\", e);\n            }\n        }\n\n        // 6. Read supported features (optional - skip if not available)\n        #[cfg(feature = \"defmt\")]\n        defmt::info!(\"Calling le_read_local_supported_features...\");\n        match self.cmd_sender.le_read_local_supported_features() {\n            Ok(features) => {\n                #[cfg(feature = \"defmt\")]\n                defmt::info!(\"Supported LE features: {=[u8]:#02X}\", features);\n            }\n            Err(e) => {\n                #[cfg(feature = \"defmt\")]\n                defmt::warn!(\"le_read_local_supported_features failed: {:?} (skipping)\", e);\n            }\n        }\n\n        self.initialized.store(true, Ordering::Release);\n\n        #[cfg(feature = \"defmt\")]\n        defmt::info!(\"BLE stack initialized successfully\");\n\n        Ok(())\n    }\n\n    /// Check if the BLE stack is initialized\n    pub fn is_initialized(&self) -> bool {\n        self.initialized.load(Ordering::Acquire)\n    }\n\n    /// Set a random address for the device\n    ///\n    /// This must be called before advertising with OwnAddressType::Random.\n    /// The random address must follow Bluetooth specification requirements.\n    ///\n    /// # Parameters\n    ///\n    /// - `address`: 6-byte random address\n    pub fn set_random_address(&self, address: Address) -> Result<(), BleError> {\n        if !self.initialized.load(Ordering::Acquire) {\n            return Err(BleError::NotInitialized);\n        }\n\n        self.cmd_sender.le_set_random_address(&address.0)\n    }\n\n    /// Get a reference to the command sender\n    ///\n    /// This allows direct access to HCI commands for advanced use cases.\n    pub fn command_sender(&self) -> &CommandSender {\n        &self.cmd_sender\n    }\n\n    /// Create an advertiser\n    ///\n    /// # Returns\n    ///\n    /// An `Advertiser` instance that can be used to start/stop BLE advertising.\n    ///\n    /// # Note\n    ///\n    /// The BLE stack must be initialized before creating an advertiser.\n    pub fn advertiser(&self) -> Advertiser<'_> {\n        Advertiser::new(&self.cmd_sender)\n    }\n\n    /// Create a scanner\n    ///\n    /// # Returns\n    ///\n    /// A `Scanner` instance that can be used to scan for nearby BLE devices.\n    ///\n    /// # Note\n    ///\n    /// The BLE stack must be initialized before creating a scanner.\n    /// Advertising reports will be received through the main event loop\n    /// as `LeAdvertisingReport` events.\n    pub fn scanner(&self) -> Scanner<'_> {\n        Scanner::new(&self.cmd_sender)\n    }\n\n    // ===== Connection Management =====\n\n    /// Get a reference to the connection manager\n    pub fn connections(&self) -> &ConnectionManager<MAX_CONNECTIONS> {\n        &self.connections\n    }\n\n    /// Get a mutable reference to the connection manager\n    pub fn connections_mut(&mut self) -> &mut ConnectionManager<MAX_CONNECTIONS> {\n        &mut self.connections\n    }\n\n    /// Get a connection by handle\n    pub fn get_connection(&self, handle: Handle) -> Option<&Connection> {\n        self.connections.get_by_handle(handle)\n    }\n\n    /// Get a mutable connection by handle\n    pub fn get_connection_mut(&mut self, handle: Handle) -> Option<&mut Connection> {\n        self.connections.get_by_handle_mut(handle)\n    }\n\n    /// Disconnect a connection\n    ///\n    /// # Parameters\n    ///\n    /// - `handle`: Connection handle to disconnect\n    /// - `reason`: Reason for disconnection\n    pub fn disconnect(&self, handle: Handle, reason: DisconnectReason) -> Result<(), BleError> {\n        if !self.initialized.load(Ordering::Acquire) {\n            return Err(BleError::NotInitialized);\n        }\n\n        self.cmd_sender.disconnect(handle.as_u16(), reason.as_u8())\n    }\n\n    /// Initiate a connection to a peripheral device (Central role)\n    ///\n    /// This starts the connection process. The connection complete event\n    /// will be received when the connection is established.\n    ///\n    /// # Parameters\n    ///\n    /// - `params`: Connection initiation parameters\n    pub fn connect(&self, params: &ConnectionInitParams) -> Result<(), BleError> {\n        if !self.initialized.load(Ordering::Acquire) {\n            return Err(BleError::NotInitialized);\n        }\n\n        self.cmd_sender.le_create_connection(\n            params.scan_interval,\n            params.scan_window,\n            params.use_filter_accept_list,\n            params.peer_address_type as u8,\n            params.peer_address.as_bytes(),\n            params.own_address_type,\n            params.conn_interval_min,\n            params.conn_interval_max,\n            params.max_latency,\n            params.supervision_timeout,\n            params.min_ce_length,\n            params.max_ce_length,\n        )\n    }\n\n    /// Cancel an ongoing connection attempt\n    pub fn cancel_connect(&self) -> Result<(), BleError> {\n        if !self.initialized.load(Ordering::Acquire) {\n            return Err(BleError::NotInitialized);\n        }\n\n        self.cmd_sender.le_create_connection_cancel()\n    }\n\n    /// Request connection parameter update\n    ///\n    /// # Parameters\n    ///\n    /// - `handle`: Connection handle\n    /// - `interval_min`: Minimum connection interval (units of 1.25ms)\n    /// - `interval_max`: Maximum connection interval (units of 1.25ms)\n    /// - `latency`: Slave latency\n    /// - `supervision_timeout`: Supervision timeout (units of 10ms)\n    pub fn update_connection_params(\n        &self,\n        handle: Handle,\n        interval_min: u16,\n        interval_max: u16,\n        latency: u16,\n        supervision_timeout: u16,\n    ) -> Result<(), BleError> {\n        if !self.initialized.load(Ordering::Acquire) {\n            return Err(BleError::NotInitialized);\n        }\n\n        self.cmd_sender.le_connection_update(\n            handle.as_u16(),\n            interval_min,\n            interval_max,\n            latency,\n            supervision_timeout,\n            0,      // min CE length\n            0xFFFF, // max CE length\n        )\n    }\n\n    /// Read the current PHY for a connection\n    ///\n    /// # Returns\n    ///\n    /// Tuple of (tx_phy, rx_phy)\n    pub fn read_phy(&self, handle: Handle) -> Result<(LePhy, LePhy), BleError> {\n        if !self.initialized.load(Ordering::Acquire) {\n            return Err(BleError::NotInitialized);\n        }\n\n        let (tx, rx) = self.cmd_sender.le_read_phy(handle.as_u16())?;\n        Ok((LePhy::from_u8(tx), LePhy::from_u8(rx)))\n    }\n\n    /// Process an HCI event and update internal state\n    ///\n    /// This method processes connection-related events and updates the\n    /// connection manager. It returns a GAP event if the event is\n    /// connection-related.\n    ///\n    /// # Returns\n    ///\n    /// - `Some(GapEvent)` if this was a connection-related event\n    /// - `None` if not a connection event\n    pub fn process_event(&mut self, event: &Event) -> Option<GapEvent> {\n        match &event.params {\n            EventParams::LeConnectionComplete {\n                status,\n                handle,\n                role,\n                peer_address_type,\n                peer_address,\n                conn_interval,\n                conn_latency,\n                supervision_timeout,\n                ..\n            } => {\n                if *status == Status::Success {\n                    let role = ConnectionRole::from_u8(*role)?;\n                    let conn = Connection::new(\n                        *handle,\n                        role,\n                        *peer_address_type,\n                        *peer_address,\n                        *conn_interval,\n                        *conn_latency,\n                        *supervision_timeout,\n                    );\n                    if let Some(stored_conn) = self.connections.allocate(conn.clone()) {\n                        // Read PHY after connection\n                        if let Ok((tx_phy, rx_phy)) = self.cmd_sender.le_read_phy(handle.as_u16()) {\n                            stored_conn.update_phy(LePhy::from_u8(tx_phy), LePhy::from_u8(rx_phy));\n                        }\n                    }\n                    Some(GapEvent::Connected(conn))\n                } else {\n                    None\n                }\n            }\n\n            EventParams::LeEnhancedConnectionComplete {\n                status,\n                handle,\n                role,\n                peer_address_type,\n                peer_address,\n                local_resolvable_private_address,\n                peer_resolvable_private_address,\n                conn_interval,\n                conn_latency,\n                supervision_timeout,\n                ..\n            } => {\n                if *status == Status::Success {\n                    let role = ConnectionRole::from_u8(*role)?;\n                    let conn = Connection::new_enhanced(\n                        *handle,\n                        role,\n                        *peer_address_type,\n                        *peer_address,\n                        *local_resolvable_private_address,\n                        *peer_resolvable_private_address,\n                        *conn_interval,\n                        *conn_latency,\n                        *supervision_timeout,\n                    );\n                    if let Some(stored_conn) = self.connections.allocate(conn.clone()) {\n                        // Read PHY after connection\n                        if let Ok((tx_phy, rx_phy)) = self.cmd_sender.le_read_phy(handle.as_u16()) {\n                            stored_conn.update_phy(LePhy::from_u8(tx_phy), LePhy::from_u8(rx_phy));\n                        }\n                    }\n                    Some(GapEvent::Connected(conn))\n                } else {\n                    None\n                }\n            }\n\n            EventParams::DisconnectionComplete { status, handle, reason } => {\n                if *status == Status::Success {\n                    self.connections.remove(*handle);\n                    Some(GapEvent::Disconnected {\n                        handle: *handle,\n                        reason: *reason,\n                    })\n                } else {\n                    None\n                }\n            }\n\n            EventParams::LeConnectionUpdateComplete {\n                status,\n                handle,\n                conn_interval,\n                conn_latency,\n                supervision_timeout,\n            } => {\n                if *status == Status::Success {\n                    if let Some(conn) = self.connections.get_by_handle_mut(*handle) {\n                        conn.update_params(*conn_interval, *conn_latency, *supervision_timeout);\n                    }\n                    Some(GapEvent::ConnectionParamsUpdated {\n                        handle: *handle,\n                        interval: *conn_interval,\n                        latency: *conn_latency,\n                        supervision_timeout: *supervision_timeout,\n                    })\n                } else {\n                    None\n                }\n            }\n\n            EventParams::LePhyUpdateComplete {\n                status,\n                handle,\n                tx_phy,\n                rx_phy,\n            } => {\n                if *status == Status::Success {\n                    let tx = LePhy::from_u8(*tx_phy);\n                    let rx = LePhy::from_u8(*rx_phy);\n                    if let Some(conn) = self.connections.get_by_handle_mut(*handle) {\n                        conn.update_phy(tx, rx);\n                    }\n                    Some(GapEvent::PhyUpdated {\n                        handle: *handle,\n                        tx_phy: tx,\n                        rx_phy: rx,\n                    })\n                } else {\n                    None\n                }\n            }\n\n            EventParams::LeDataLengthChange {\n                handle,\n                max_tx_octets,\n                max_tx_time,\n                max_rx_octets,\n                max_rx_time,\n            } => Some(GapEvent::DataLengthChanged {\n                handle: *handle,\n                max_tx_octets: *max_tx_octets,\n                max_tx_time: *max_tx_time,\n                max_rx_octets: *max_rx_octets,\n                max_rx_time: *max_rx_time,\n            }),\n\n            _ => None,\n        }\n    }\n\n    /// Read the next BLE event\n    ///\n    /// This function blocks until an event is available.\n    /// Events include connection complete, disconnection, etc.\n    ///\n    /// # Returns\n    ///\n    /// The next BLE event from the controller.\n    ///\n    /// # Note\n    ///\n    /// Most applications don't need to call this directly. Events are\n    /// processed automatically by the stack for operations like advertising\n    /// and scanning. This is provided for applications that need to handle\n    /// raw events (e.g., for connection management).\n    pub async fn read_event(&self) -> Event {\n        read_event().await\n    }\n}\n\n/// Version information from the BLE controller\n#[derive(Debug, Clone, Copy)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub struct VersionInfo {\n    pub hci_version: u8,\n    pub hci_revision: u16,\n    pub lmp_version: u8,\n    pub manufacturer_name: u16,\n    pub lmp_subversion: u16,\n}\n"
  },
  {
    "path": "embassy-stm32-wpan/src/wba/context.rs",
    "content": "//! Context switching primitives for the BLE sequencer\n//!\n//! This module provides low-level context switching between the embassy executor\n//! and the BLE sequencer. The sequencer runs on a separate stack and can yield\n//! back to the executor when it would otherwise block (WFE).\n//!\n//! # Architecture\n//!\n//! ```text\n//! ┌────────────────────────────────────────────────────────────┐\n//! │                    Embassy Executor                        │\n//! │  ┌────────────────────────────────────────────────────┐    │\n//! │  │ Runner Task (uses executor's stack)                │    │\n//! │  │                                                    │    │\n//! │  │  loop {                                            │    │\n//! │  │    util_seq::seq_resume();  // switch to sequencer │    │\n//! │  │    // returns when sequencer yields                │    │\n//! │  │    yield_now().await;   // let other tasks run     │    │\n//! │  │  }                                                 │    │\n//! │  └────────────────────────────────────────────────────┘    │\n//! └────────────────────────────────────────────────────────────┘\n//!\n//! ┌────────────────────────────────────────────────────────────┐\n//! │  Sequencer Context (separate stack)                        │\n//! │                                                            │\n//! │  - Runs BLE stack C code via poll_pending_tasks()          │\n//! │  - When idle, calls sequencer_yield() to return            │\n//! └────────────────────────────────────────────────────────────┘\n//! ```\n\nuse core::arch::asm;\nuse core::cell::UnsafeCell;\nuse core::sync::atomic::{AtomicU8, Ordering};\n\nuse aligned::{A8, Aligned};\n\n/// Size of the sequencer stack in bytes (8KB)\n/// This needs to be large enough for the C BLE stack's call depth\nconst SEQUENCER_CTX_STACK_SIZE: usize = 8 * 1024;\n\n/// Saved CPU context for context switching\n#[repr(C)]\nstruct Context {\n    /// Saved stack pointer\n    sp: u32,\n}\n\nimpl Context {\n    const fn new() -> Self {\n        Self { sp: 0 }\n    }\n}\n\n/// Sequencer state\n#[derive(Debug, Clone, Copy, PartialEq, Eq)]\nenum ContextManagerState {\n    /// Not yet started\n    Uninitialized,\n    /// Running (in sequencer context)\n    Running,\n    /// Yielded back to runner\n    Yielded,\n    /// Stopped/finished\n    Stopped,\n}\n\nenum ContextSwitchType {\n    RunnerToTask,\n    TaskToRunner,\n}\n\nimpl From<u8> for ContextManagerState {\n    fn from(value: u8) -> Self {\n        match value {\n            0 => Self::Uninitialized,\n            1 => Self::Running,\n            2 => Self::Yielded,\n            3 => Self::Stopped,\n            _ => Self::Uninitialized,\n        }\n    }\n}\n\nimpl From<ContextManagerState> for u8 {\n    fn from(value: ContextManagerState) -> Self {\n        match value {\n            ContextManagerState::Uninitialized => 0,\n            ContextManagerState::Running => 1,\n            ContextManagerState::Yielded => 2,\n            ContextManagerState::Stopped => 3,\n        }\n    }\n}\n\n/// Global sequencer state\npub(crate) struct ContextManager {\n    /// The sequencer's saved context (SP when yielded)\n    task_ctx: UnsafeCell<Context>,\n    /// The runner's saved context (SP when in sequencer)\n    runner_ctx: UnsafeCell<Context>,\n    /// Current state\n    state: AtomicU8,\n    /// The sequencer's stack (must be 8-byte aligned)\n    task_stack: Aligned<A8, UnsafeCell<[u8; SEQUENCER_CTX_STACK_SIZE]>>,\n    task_entry: extern \"C\" fn() -> !,\n}\n\nunsafe impl Sync for ContextManager {}\n\nimpl ContextManager {\n    pub(crate) const fn new(task_entry: extern \"C\" fn() -> !) -> Self {\n        Self {\n            task_ctx: UnsafeCell::new(Context::new()),\n            runner_ctx: UnsafeCell::new(Context::new()),\n            state: AtomicU8::new(ContextManagerState::Uninitialized as u8),\n            task_stack: Aligned(UnsafeCell::new([0u8; SEQUENCER_CTX_STACK_SIZE])),\n            task_entry: task_entry,\n        }\n    }\n\n    /// Initialize the sequencer stack\n    ///\n    /// This must be called once before using the sequencer.\n    /// Returns the initial stack pointer for the sequencer.\n    fn init_sequencer_stack(&'static self) {\n        // Stack grows downward, so we start at the top\n        // Ensure 8-byte alignment (ARM requirement for function calls)\n        let stack_top = &raw const self.task_stack as usize + SEQUENCER_CTX_STACK_SIZE;\n        let stack_top = (stack_top & !0x7) as u32; // 8-byte align\n\n        // Set up initial context for sequencer\n        // We need to set up the stack so that when we \"restore\" to it,\n        // it will start executing sequencer_entry\n        unsafe {\n            let task_ctx = self.task_ctx.get();\n\n            // Set up fake saved context on sequencer stack\n            // Stack layout (growing down):\n            // [stack_top - 0]:  (8-byte alignment padding if needed)\n            // [stack_top - 4]:  Initial LR (not used, sequencer_entry is noreturn)\n            // [stack_top - 8]:  R11\n            // [stack_top - 12]: R10\n            // [stack_top - 16]: R9\n            // [stack_top - 20]: R8\n            // [stack_top - 24]: R7\n            // [stack_top - 28]: R6\n            // [stack_top - 32]: R5\n            // [stack_top - 36]: R4\n            // SP points here after \"restore\"\n\n            let mut sp = stack_top;\n\n            // Push a fake return address (entry point)\n            sp -= 4;\n            core::ptr::write_volatile(sp as *mut u32, self.task_entry as u32);\n\n            // Push fake saved registers R11-R4 (all zeros is fine)\n            for _ in 0..8 {\n                sp -= 4;\n                core::ptr::write_volatile(sp as *mut u32, 0);\n            }\n\n            (*task_ctx).sp = sp;\n        }\n\n        self.set_state(ContextManagerState::Yielded);\n    }\n\n    /// Resume the sequencer from the runner context\n    ///\n    /// This function switches from the runner's context to the sequencer's context.\n    /// It returns when the sequencer yields (calls `sequencer_yield`).\n    ///\n    /// # Safety\n    ///\n    /// Must be called from a proper task context with a valid stack.\n    pub(crate) fn task_resume(&'static self) {\n        match self.get_state() {\n            ContextManagerState::Uninitialized => {\n                self.init_sequencer_stack();\n                self.switch_context(ContextSwitchType::RunnerToTask);\n            }\n            ContextManagerState::Yielded => {\n                self.switch_context(ContextSwitchType::RunnerToTask);\n            }\n            ContextManagerState::Running => {\n                // Already running - shouldn't happen\n                #[cfg(feature = \"defmt\")]\n                defmt::warn!(\"sequencer_resume called while already running\");\n            }\n            ContextManagerState::Stopped => {\n                // Sequencer has stopped - shouldn't happen normally\n                #[cfg(feature = \"defmt\")]\n                defmt::warn!(\"sequencer_resume called after stop\");\n            }\n        }\n    }\n\n    /// Yield from the sequencer back to the runner\n    ///\n    /// This is called from within the sequencer context when it has no more\n    /// work to do (would otherwise WFE).\n    ///\n    /// This must not be called from an interrupt handler.\n    pub(crate) fn task_yield(&'static self) {\n        if self.get_state() == ContextManagerState::Running {\n            self.switch_context(ContextSwitchType::TaskToRunner);\n        }\n    }\n\n    /// Perform the actual context switch\n    ///\n    /// This saves the current context and restores the other context.\n    /// Works bidirectionally - runner->sequencer and sequencer->runner.\n    #[inline(never)]\n    fn switch_context(&'static self, switch_type: ContextSwitchType) {\n        self.set_state(match switch_type {\n            ContextSwitchType::RunnerToTask => ContextManagerState::Running,\n            ContextSwitchType::TaskToRunner => ContextManagerState::Yielded,\n        });\n\n        let (save_ctx, restore_ctx) = match switch_type {\n            ContextSwitchType::RunnerToTask => (self.runner_ctx.get(), self.task_ctx.get()),\n            ContextSwitchType::TaskToRunner => (self.task_ctx.get(), self.runner_ctx.get()),\n        };\n\n        unsafe {\n            self.switch_context_inner(save_ctx, restore_ctx);\n        }\n    }\n\n    /// Low-level context switch using inline assembly\n    ///\n    /// Saves R4-R11 and SP to `save_ctx`, then restores from `restore_ctx`.\n    ///\n    /// # Safety\n    ///\n    /// Both pointers must be valid SavedContext structures.\n    #[inline(never)]\n    unsafe fn switch_context_inner(&'static self, save_ctx: *mut Context, restore_ctx: *mut Context) {\n        #[cfg(target_arch = \"arm\")]\n        {\n            asm!(\n                // Save current context\n                // Push callee-saved registers R4-R11 onto current stack\n                \"push {{r4-r11, lr}}\",\n\n                // Save current SP to save_ctx->sp\n                \"str sp, [{save_ctx}]\",\n\n                // Restore new context\n                // Load SP from restore_ctx->sp\n                \"ldr sp, [{restore_ctx}]\",\n\n                // Pop callee-saved registers R4-R11 from new stack\n                \"pop {{r4-r11, lr}}\",\n\n                // Return to new context (via LR we just popped)\n                \"bx lr\",\n\n                save_ctx = in(reg) save_ctx,\n                restore_ctx = in(reg) restore_ctx,\n                options(noreturn)\n            );\n        }\n\n        #[cfg(not(target_arch = \"arm\"))]\n        {\n            // Fallback for non-ARM (e.g., testing on host)\n            let _ = (save_ctx, restore_ctx);\n            panic!(\"Context switching only supported on ARM\");\n        }\n    }\n\n    pub fn in_task_context(&self) -> bool {\n        self.get_state() == ContextManagerState::Running\n    }\n\n    fn get_state(&self) -> ContextManagerState {\n        self.state.load(Ordering::Acquire).into()\n    }\n\n    fn set_state(&self, state: ContextManagerState) {\n        self.state.store(state.into(), Ordering::Release);\n    }\n}\n"
  },
  {
    "path": "embassy-stm32-wpan/src/wba/error.rs",
    "content": "//! BLE Error types\n\nuse super::hci::types::Status;\n\n/// BLE Stack Errors\n#[derive(Debug, Clone, Copy, PartialEq, Eq)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub enum BleError {\n    /// BLE stack not initialized\n    NotInitialized,\n\n    /// BLE stack initialization failed\n    InitializationFailed,\n\n    /// HCI command failed with status code\n    CommandFailed(Status),\n\n    /// Operation timed out\n    Timeout,\n\n    /// Invalid parameter provided\n    InvalidParameter,\n\n    /// Buffer full (event queue, command queue, etc.)\n    BufferFull,\n\n    /// Hardware error\n    HardwareError(u8),\n\n    /// Connection error\n    ConnectionError,\n\n    /// Unknown or unspecified error\n    Unknown,\n}\n\nimpl From<Status> for BleError {\n    fn from(status: Status) -> Self {\n        match status {\n            Status::Success => BleError::Unknown, // Shouldn't convert success to error\n            Status::HardwareFailure => BleError::HardwareError(0x03),\n            Status::InvalidHciCommandParameters => BleError::InvalidParameter,\n            Status::ConnectionTimeout => BleError::Timeout,\n            Status::ConnectionLimitExceeded => BleError::ConnectionError,\n            Status::ConnectionFailedToBeEstablished => BleError::ConnectionError,\n            _ => BleError::CommandFailed(status),\n        }\n    }\n}\n\n#[cfg(feature = \"defmt\")]\nimpl defmt::Format for Status {\n    fn format(&self, fmt: defmt::Formatter) {\n        match self {\n            Status::Success => defmt::write!(fmt, \"Success\"),\n            Status::UnknownCommand => defmt::write!(fmt, \"UnknownCommand\"),\n            Status::UnknownConnectionId => defmt::write!(fmt, \"UnknownConnectionId\"),\n            Status::HardwareFailure => defmt::write!(fmt, \"HardwareFailure\"),\n            Status::PageTimeout => defmt::write!(fmt, \"PageTimeout\"),\n            Status::AuthenticationFailure => defmt::write!(fmt, \"AuthenticationFailure\"),\n            Status::PinOrKeyMissing => defmt::write!(fmt, \"PinOrKeyMissing\"),\n            Status::MemoryCapacityExceeded => defmt::write!(fmt, \"MemoryCapacityExceeded\"),\n            Status::ConnectionTimeout => defmt::write!(fmt, \"ConnectionTimeout\"),\n            Status::ConnectionLimitExceeded => defmt::write!(fmt, \"ConnectionLimitExceeded\"),\n            Status::InvalidHciCommandParameters => defmt::write!(fmt, \"InvalidHciCommandParameters\"),\n            Status::RemoteUserTerminatedConnection => defmt::write!(fmt, \"RemoteUserTerminatedConnection\"),\n            Status::ConnectionTerminatedByLocalHost => defmt::write!(fmt, \"ConnectionTerminatedByLocalHost\"),\n            Status::UnsupportedRemoteFeature => defmt::write!(fmt, \"UnsupportedRemoteFeature\"),\n            Status::InvalidLmpParameters => defmt::write!(fmt, \"InvalidLmpParameters\"),\n            Status::UnspecifiedError => defmt::write!(fmt, \"UnspecifiedError\"),\n            Status::UnsupportedLmpParameterValue => defmt::write!(fmt, \"UnsupportedLmpParameterValue\"),\n            Status::RoleChangeNotAllowed => defmt::write!(fmt, \"RoleChangeNotAllowed\"),\n            Status::LmpResponseTimeout => defmt::write!(fmt, \"LmpResponseTimeout\"),\n            Status::ControllerBusy => defmt::write!(fmt, \"ControllerBusy\"),\n            Status::UnacceptableConnectionParameters => defmt::write!(fmt, \"UnacceptableConnectionParameters\"),\n            Status::AdvertisingTimeout => defmt::write!(fmt, \"AdvertisingTimeout\"),\n            Status::ConnectionTerminatedDueToMicFailure => defmt::write!(fmt, \"ConnectionTerminatedDueToMicFailure\"),\n            Status::ConnectionFailedToBeEstablished => defmt::write!(fmt, \"ConnectionFailedToBeEstablished\"),\n        }\n    }\n}\n"
  },
  {
    "path": "embassy-stm32-wpan/src/wba/gap/advertiser.rs",
    "content": "//! GAP Advertiser implementation\n\nuse super::types::{AdvData, AdvParams};\nuse crate::wba::error::BleError;\nuse crate::wba::hci::command::CommandSender;\nuse crate::wba::hci::types::AddressType;\n\n/// BLE Advertiser\n///\n/// Provides high-level API for BLE advertising (broadcaster/peripheral role).\n///\n/// # Example\n///\n/// ```no_run\n/// use embassy_stm32_wpan::wba::gap::{Advertiser, AdvData, AdvParams};\n///\n/// let cmd_sender = CommandSender::new();\n/// let mut advertiser = Advertiser::new(&cmd_sender);\n///\n/// // Create advertising data\n/// let mut adv_data = AdvData::new();\n/// adv_data.add_flags(0x06).unwrap();  // General discoverable, BR/EDR not supported\n/// adv_data.add_name(\"MyDevice\").unwrap();\n///\n/// // Start advertising with default parameters\n/// advertiser.start(AdvParams::default(), adv_data, None).await.unwrap();\n/// ```\npub struct Advertiser<'d> {\n    cmd: &'d CommandSender,\n    is_advertising: bool,\n}\n\nimpl<'d> Advertiser<'d> {\n    /// Create a new advertiser\n    pub fn new(cmd: &'d CommandSender) -> Self {\n        Self {\n            cmd,\n            is_advertising: false,\n        }\n    }\n\n    /// Start advertising\n    ///\n    /// # Parameters\n    ///\n    /// - `params`: Advertising parameters (interval, type, address type, etc.)\n    /// - `adv_data`: Advertising data (up to 31 bytes)\n    /// - `scan_rsp_data`: Optional scan response data (up to 31 bytes)\n    ///\n    /// # Returns\n    ///\n    /// - `Ok(())` if advertising started successfully\n    /// - `Err(BleError)` if an error occurred\n    ///\n    /// # Notes\n    ///\n    /// This function will stop any ongoing advertising before starting new advertising.\n    pub fn start(\n        &mut self,\n        params: AdvParams,\n        adv_data: AdvData,\n        scan_rsp_data: Option<AdvData>,\n    ) -> Result<(), BleError> {\n        // Stop advertising if already advertising\n        if self.is_advertising {\n            self.stop()?;\n        }\n\n        // Validate advertising data length\n        if adv_data.len() > 31 {\n            return Err(BleError::InvalidParameter);\n        }\n\n        if let Some(ref scan_rsp) = scan_rsp_data {\n            if scan_rsp.len() > 31 {\n                return Err(BleError::InvalidParameter);\n            }\n        }\n\n        // Set advertising parameters\n        self.cmd.le_set_advertising_parameters(\n            params.interval_min,\n            params.interval_max,\n            params.adv_type.into(),\n            params.own_addr_type,\n            AddressType::Public as u8, // Peer address type (not used for undirected advertising)\n            &[0; 6],                   // Peer address (not used for undirected advertising)\n            params.channel_map,\n            params.filter_policy,\n        )?;\n\n        // Set advertising data\n        self.cmd.le_set_advertising_data(adv_data.build())?;\n\n        // Set scan response data if provided\n        if let Some(scan_rsp) = scan_rsp_data {\n            self.cmd.le_set_scan_response_data(scan_rsp.build())?;\n        }\n\n        // Enable advertising\n        self.cmd.le_set_advertise_enable(true)?;\n\n        self.is_advertising = true;\n\n        Ok(())\n    }\n\n    /// Stop advertising\n    ///\n    /// # Returns\n    ///\n    /// - `Ok(())` if advertising stopped successfully\n    /// - `Err(BleError)` if an error occurred\n    pub fn stop(&mut self) -> Result<(), BleError> {\n        if !self.is_advertising {\n            return Ok(());\n        }\n\n        self.cmd.le_set_advertise_enable(false)?;\n        self.is_advertising = false;\n\n        Ok(())\n    }\n\n    /// Check if currently advertising\n    pub fn is_advertising(&self) -> bool {\n        self.is_advertising\n    }\n\n    /// Update advertising data without stopping advertising\n    ///\n    /// Note: Some BLE controllers may not support updating advertising data\n    /// while advertising is active. If this fails, consider stopping and\n    /// restarting advertising.\n    pub fn update_adv_data(&mut self, adv_data: AdvData) -> Result<(), BleError> {\n        if adv_data.len() > 31 {\n            return Err(BleError::InvalidParameter);\n        }\n\n        self.cmd.le_set_advertising_data(adv_data.build())\n    }\n\n    /// Update scan response data without stopping advertising\n    ///\n    /// Note: Some BLE controllers may not support updating scan response data\n    /// while advertising is active. If this fails, consider stopping and\n    /// restarting advertising.\n    pub fn update_scan_rsp_data(&mut self, scan_rsp_data: AdvData) -> Result<(), BleError> {\n        if scan_rsp_data.len() > 31 {\n            return Err(BleError::InvalidParameter);\n        }\n\n        self.cmd.le_set_scan_response_data(scan_rsp_data.build())\n    }\n}\n\nimpl<'d> Drop for Advertiser<'d> {\n    fn drop(&mut self) {\n        // Best effort to stop advertising when advertiser is dropped\n        let _ = self.stop();\n    }\n}\n"
  },
  {
    "path": "embassy-stm32-wpan/src/wba/gap/connection.rs",
    "content": "//! BLE Connection Management\n//!\n//! This module provides types and utilities for managing BLE connections.\n//! It supports both peripheral and central roles.\n\nuse crate::wba::hci::types::{Address, AddressType, Handle};\n\n/// Maximum number of simultaneous BLE connections supported\npub const MAX_CONNECTIONS: usize = 4;\n\n/// Connection role (as reported in connection complete event)\n#[repr(u8)]\n#[derive(Debug, Clone, Copy, PartialEq, Eq)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub enum ConnectionRole {\n    /// Central role - initiated the connection\n    Central = 0x00,\n    /// Peripheral role - accepted the connection\n    Peripheral = 0x01,\n}\n\nimpl ConnectionRole {\n    /// Create from raw u8 value\n    pub fn from_u8(value: u8) -> Option<Self> {\n        match value {\n            0x00 => Some(ConnectionRole::Central),\n            0x01 => Some(ConnectionRole::Peripheral),\n            _ => None,\n        }\n    }\n}\n\n/// Physical layer type for BLE connections\n#[repr(u8)]\n#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub enum LePhy {\n    /// 1 Mbps PHY\n    #[default]\n    Phy1M = 0x01,\n    /// 2 Mbps PHY\n    Phy2M = 0x02,\n    /// Coded PHY (long range)\n    PhyCoded = 0x03,\n}\n\nimpl LePhy {\n    /// Create from raw u8 value\n    pub fn from_u8(value: u8) -> Self {\n        match value {\n            0x01 => LePhy::Phy1M,\n            0x02 => LePhy::Phy2M,\n            0x03 => LePhy::PhyCoded,\n            _ => LePhy::Phy1M,\n        }\n    }\n}\n\n/// Reason for disconnection\n#[repr(u8)]\n#[derive(Debug, Clone, Copy, PartialEq, Eq)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub enum DisconnectReason {\n    /// Remote user terminated connection\n    RemoteUserTerminated = 0x13,\n    /// Remote device terminated due to low resources\n    RemoteLowResources = 0x14,\n    /// Remote device terminated due to power off\n    RemotePowerOff = 0x15,\n    /// Connection terminated by local host\n    LocalHostTerminated = 0x16,\n    /// Unacceptable connection parameters\n    UnacceptableParameters = 0x3B,\n}\n\nimpl DisconnectReason {\n    /// Convert to raw u8 value for HCI command\n    pub fn as_u8(self) -> u8 {\n        self as u8\n    }\n}\n\n/// Connection parameters\n#[derive(Debug, Clone, Copy)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub struct ConnectionParams {\n    /// Connection interval in units of 1.25ms (Range: 6 to 3200, i.e., 7.5ms to 4s)\n    pub interval: u16,\n    /// Slave latency (number of connection events the slave can skip)\n    pub latency: u16,\n    /// Supervision timeout in units of 10ms (Range: 10 to 3200, i.e., 100ms to 32s)\n    pub supervision_timeout: u16,\n}\n\nimpl Default for ConnectionParams {\n    fn default() -> Self {\n        Self {\n            interval: 80,             // 100ms\n            latency: 0,               // No latency\n            supervision_timeout: 400, // 4s\n        }\n    }\n}\n\n/// Parameters for connection initiation (central role)\n#[derive(Debug, Clone)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub struct ConnectionInitParams {\n    /// Scan interval in units of 0.625ms\n    pub scan_interval: u16,\n    /// Scan window in units of 0.625ms\n    pub scan_window: u16,\n    /// Use filter accept list instead of peer address\n    pub use_filter_accept_list: bool,\n    /// Peer address type\n    pub peer_address_type: AddressType,\n    /// Peer address\n    pub peer_address: Address,\n    /// Own address type\n    pub own_address_type: OwnAddressType,\n    /// Minimum connection interval in units of 1.25ms\n    pub conn_interval_min: u16,\n    /// Maximum connection interval in units of 1.25ms\n    pub conn_interval_max: u16,\n    /// Maximum slave latency\n    pub max_latency: u16,\n    /// Supervision timeout in units of 10ms\n    pub supervision_timeout: u16,\n    /// Minimum CE length in units of 0.625ms\n    pub min_ce_length: u16,\n    /// Maximum CE length in units of 0.625ms\n    pub max_ce_length: u16,\n}\n\nuse crate::wba::gap::types::OwnAddressType;\n\nimpl Default for ConnectionInitParams {\n    fn default() -> Self {\n        Self {\n            scan_interval: 0x0010, // 10ms\n            scan_window: 0x0010,   // 10ms\n            use_filter_accept_list: false,\n            peer_address_type: AddressType::Public,\n            peer_address: Address::new([0; 6]),\n            own_address_type: OwnAddressType::Public,\n            conn_interval_min: 0x0018, // 30ms\n            conn_interval_max: 0x0028, // 50ms\n            max_latency: 0,\n            supervision_timeout: 0x01F4, // 5s\n            min_ce_length: 0x0000,\n            max_ce_length: 0xFFFF,\n        }\n    }\n}\n\n/// Active BLE connection context\n///\n/// Contains all relevant information about an active connection.\n#[derive(Debug, Clone)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub struct Connection {\n    /// Connection handle assigned by the controller\n    pub handle: Handle,\n    /// Role in this connection\n    pub role: ConnectionRole,\n    /// Peer device address type\n    pub peer_address_type: AddressType,\n    /// Peer device address\n    pub peer_address: Address,\n    /// Local resolvable private address (if used)\n    pub local_rpa: Option<Address>,\n    /// Peer resolvable private address (if used)\n    pub peer_rpa: Option<Address>,\n    /// Current ATT MTU size\n    pub mtu: u16,\n    /// Connection parameters\n    pub params: ConnectionParams,\n    /// TX PHY\n    pub tx_phy: LePhy,\n    /// RX PHY\n    pub rx_phy: LePhy,\n    /// Whether this connection is encrypted\n    pub encrypted: bool,\n}\n\nimpl Connection {\n    /// Create a new connection from connection complete event data\n    pub fn new(\n        handle: Handle,\n        role: ConnectionRole,\n        peer_address_type: AddressType,\n        peer_address: Address,\n        conn_interval: u16,\n        conn_latency: u16,\n        supervision_timeout: u16,\n    ) -> Self {\n        Self {\n            handle,\n            role,\n            peer_address_type,\n            peer_address,\n            local_rpa: None,\n            peer_rpa: None,\n            mtu: 23, // Default ATT MTU\n            params: ConnectionParams {\n                interval: conn_interval,\n                latency: conn_latency,\n                supervision_timeout,\n            },\n            tx_phy: LePhy::Phy1M,\n            rx_phy: LePhy::Phy1M,\n            encrypted: false,\n        }\n    }\n\n    /// Create from enhanced connection complete event (includes RPA)\n    pub fn new_enhanced(\n        handle: Handle,\n        role: ConnectionRole,\n        peer_address_type: AddressType,\n        peer_address: Address,\n        local_rpa: Address,\n        peer_rpa: Address,\n        conn_interval: u16,\n        conn_latency: u16,\n        supervision_timeout: u16,\n    ) -> Self {\n        let local_rpa_opt = if local_rpa.0 != [0; 6] { Some(local_rpa) } else { None };\n        let peer_rpa_opt = if peer_rpa.0 != [0; 6] { Some(peer_rpa) } else { None };\n\n        Self {\n            handle,\n            role,\n            peer_address_type,\n            peer_address,\n            local_rpa: local_rpa_opt,\n            peer_rpa: peer_rpa_opt,\n            mtu: 23,\n            params: ConnectionParams {\n                interval: conn_interval,\n                latency: conn_latency,\n                supervision_timeout,\n            },\n            tx_phy: LePhy::Phy1M,\n            rx_phy: LePhy::Phy1M,\n            encrypted: false,\n        }\n    }\n\n    /// Update connection parameters\n    pub fn update_params(&mut self, interval: u16, latency: u16, supervision_timeout: u16) {\n        self.params.interval = interval;\n        self.params.latency = latency;\n        self.params.supervision_timeout = supervision_timeout;\n    }\n\n    /// Update MTU\n    pub fn update_mtu(&mut self, mtu: u16) {\n        self.mtu = mtu;\n    }\n\n    /// Update PHY\n    pub fn update_phy(&mut self, tx_phy: LePhy, rx_phy: LePhy) {\n        self.tx_phy = tx_phy;\n        self.rx_phy = rx_phy;\n    }\n\n    /// Set encryption status\n    pub fn set_encrypted(&mut self, encrypted: bool) {\n        self.encrypted = encrypted;\n    }\n}\n\n/// Connection manager for tracking active connections\n///\n/// This maintains a pool of connection contexts and provides\n/// methods for looking up connections by handle or address.\npub struct ConnectionManager<const N: usize = MAX_CONNECTIONS> {\n    connections: [Option<Connection>; N],\n}\n\nimpl<const N: usize> ConnectionManager<N> {\n    /// Create a new empty connection manager\n    pub const fn new() -> Self {\n        // const fn compatible array initialization\n        Self {\n            connections: [const { None }; N],\n        }\n    }\n\n    /// Get a free slot and initialize it with the given handle\n    ///\n    /// Returns a mutable reference to the connection context if a slot is available.\n    pub fn allocate(&mut self, connection: Connection) -> Option<&mut Connection> {\n        for slot in self.connections.iter_mut() {\n            if slot.is_none() {\n                *slot = Some(connection);\n                return slot.as_mut();\n            }\n        }\n        None\n    }\n\n    /// Get a connection by its handle\n    pub fn get_by_handle(&self, handle: Handle) -> Option<&Connection> {\n        self.connections\n            .iter()\n            .filter_map(|c| c.as_ref())\n            .find(|c| c.handle == handle)\n    }\n\n    /// Get a mutable connection by its handle\n    pub fn get_by_handle_mut(&mut self, handle: Handle) -> Option<&mut Connection> {\n        self.connections\n            .iter_mut()\n            .filter_map(|c| c.as_mut())\n            .find(|c| c.handle == handle)\n    }\n\n    /// Get a connection by peer address\n    pub fn get_by_address(&self, address: &Address) -> Option<&Connection> {\n        self.connections\n            .iter()\n            .filter_map(|c| c.as_ref())\n            .find(|c| &c.peer_address == address)\n    }\n\n    /// Remove a connection by its handle\n    ///\n    /// Returns the removed connection if it existed.\n    pub fn remove(&mut self, handle: Handle) -> Option<Connection> {\n        for slot in self.connections.iter_mut() {\n            if let Some(conn) = slot {\n                if conn.handle == handle {\n                    return slot.take();\n                }\n            }\n        }\n        None\n    }\n\n    /// Get the number of active connections\n    pub fn count(&self) -> usize {\n        self.connections.iter().filter(|c| c.is_some()).count()\n    }\n\n    /// Check if there are any active connections\n    pub fn is_empty(&self) -> bool {\n        self.count() == 0\n    }\n\n    /// Check if the connection manager is at capacity\n    pub fn is_full(&self) -> bool {\n        self.count() >= N\n    }\n\n    /// Iterate over all active connections\n    pub fn iter(&self) -> impl Iterator<Item = &Connection> {\n        self.connections.iter().filter_map(|c| c.as_ref())\n    }\n\n    /// Iterate over all active connections mutably\n    pub fn iter_mut(&mut self) -> impl Iterator<Item = &mut Connection> {\n        self.connections.iter_mut().filter_map(|c| c.as_mut())\n    }\n}\n\nimpl<const N: usize> Default for ConnectionManager<N> {\n    fn default() -> Self {\n        Self::new()\n    }\n}\n\n/// GAP events related to connection management\n#[derive(Debug, Clone)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub enum GapEvent {\n    /// A new connection has been established\n    Connected(Connection),\n\n    /// A connection has been terminated\n    Disconnected {\n        /// Handle of the terminated connection\n        handle: Handle,\n        /// Reason for disconnection\n        reason: u8,\n    },\n\n    /// Connection parameters have been updated\n    ConnectionParamsUpdated {\n        /// Connection handle\n        handle: Handle,\n        /// New connection interval\n        interval: u16,\n        /// New slave latency\n        latency: u16,\n        /// New supervision timeout\n        supervision_timeout: u16,\n    },\n\n    /// PHY has been updated\n    PhyUpdated {\n        /// Connection handle\n        handle: Handle,\n        /// New TX PHY\n        tx_phy: LePhy,\n        /// New RX PHY\n        rx_phy: LePhy,\n    },\n\n    /// Data length has changed\n    DataLengthChanged {\n        /// Connection handle\n        handle: Handle,\n        /// Maximum TX octets\n        max_tx_octets: u16,\n        /// Maximum TX time in microseconds\n        max_tx_time: u16,\n        /// Maximum RX octets\n        max_rx_octets: u16,\n        /// Maximum RX time in microseconds\n        max_rx_time: u16,\n    },\n}\n"
  },
  {
    "path": "embassy-stm32-wpan/src/wba/gap/mod.rs",
    "content": "//! GAP (Generic Access Profile) implementation for WBA BLE stack\n//!\n//! This module provides high-level APIs for:\n//! - Advertising (broadcaster/peripheral role)\n//! - Scanning (observer role)\n//! - Connection management (central/peripheral roles)\n\npub mod advertiser;\npub mod connection;\npub mod scanner;\npub mod types;\n\npub use advertiser::Advertiser;\npub use connection::{\n    Connection, ConnectionInitParams, ConnectionManager, ConnectionParams, ConnectionRole, DisconnectReason, GapEvent,\n    LePhy, MAX_CONNECTIONS,\n};\npub use scanner::{ParsedAdvData, ScanFilterPolicy, ScanParams, ScanType, Scanner};\npub use types::{AdvData, AdvParams, AdvType, OwnAddressType};\n"
  },
  {
    "path": "embassy-stm32-wpan/src/wba/gap/scanner.rs",
    "content": "//! BLE Scanner implementation\n//!\n//! This module provides functionality for scanning (observer role) to discover\n//! nearby BLE devices that are advertising.\n\nuse crate::wba::error::BleError;\nuse crate::wba::gap::types::OwnAddressType;\nuse crate::wba::hci::command::CommandSender;\n\n/// Scan type\n#[repr(u8)]\n#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub enum ScanType {\n    /// Passive scanning - only listen for advertising packets\n    #[default]\n    Passive = 0x00,\n    /// Active scanning - send scan requests to get scan response data\n    Active = 0x01,\n}\n\n/// Scan filter policy\n#[repr(u8)]\n#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub enum ScanFilterPolicy {\n    /// Accept all advertising packets\n    #[default]\n    AcceptAll = 0x00,\n    /// Accept only advertising packets from devices in the filter accept list\n    AcceptFilterListOnly = 0x01,\n    /// Accept all advertising packets, but filter directed ads not addressed to us\n    AcceptAllFilterDirected = 0x02,\n    /// Accept only filter accept list, and filter directed ads\n    AcceptFilterListFilterDirected = 0x03,\n}\n\n/// Scan parameters\n#[derive(Debug, Clone)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub struct ScanParams {\n    /// Type of scanning (passive or active)\n    pub scan_type: ScanType,\n\n    /// Scan interval in units of 0.625ms (Range: 0x0004 to 0x4000, i.e., 2.5ms to 10.24s)\n    /// Default: 0x0010 (10ms)\n    pub scan_interval: u16,\n\n    /// Scan window in units of 0.625ms (Range: 0x0004 to 0x4000)\n    /// Must be <= scan_interval\n    /// Default: 0x0010 (10ms)\n    pub scan_window: u16,\n\n    /// Own address type to use\n    pub own_address_type: OwnAddressType,\n\n    /// Scanning filter policy\n    pub filter_policy: ScanFilterPolicy,\n\n    /// Filter duplicate advertising reports\n    pub filter_duplicates: bool,\n}\n\nimpl Default for ScanParams {\n    fn default() -> Self {\n        Self {\n            scan_type: ScanType::Passive,\n            scan_interval: 0x0010, // 10ms\n            scan_window: 0x0010,   // 10ms\n            own_address_type: OwnAddressType::Public,\n            filter_policy: ScanFilterPolicy::AcceptAll,\n            filter_duplicates: true,\n        }\n    }\n}\n\nimpl ScanParams {\n    /// Create new scan parameters with default values\n    pub fn new() -> Self {\n        Self::default()\n    }\n\n    /// Set scan type\n    pub fn with_scan_type(mut self, scan_type: ScanType) -> Self {\n        self.scan_type = scan_type;\n        self\n    }\n\n    /// Set scan interval (in units of 0.625ms)\n    pub fn with_interval(mut self, interval: u16) -> Self {\n        self.scan_interval = interval;\n        self\n    }\n\n    /// Set scan window (in units of 0.625ms)\n    pub fn with_window(mut self, window: u16) -> Self {\n        self.scan_window = window;\n        self\n    }\n\n    /// Set filter policy\n    pub fn with_filter_policy(mut self, policy: ScanFilterPolicy) -> Self {\n        self.filter_policy = policy;\n        self\n    }\n\n    /// Set duplicate filtering\n    pub fn with_filter_duplicates(mut self, filter: bool) -> Self {\n        self.filter_duplicates = filter;\n        self\n    }\n\n    /// Fast scan parameters (shorter intervals for quick discovery)\n    pub fn fast() -> Self {\n        Self {\n            scan_type: ScanType::Active,\n            scan_interval: 0x0030, // 30ms\n            scan_window: 0x0030,   // 30ms (100% duty cycle)\n            own_address_type: OwnAddressType::Public,\n            filter_policy: ScanFilterPolicy::AcceptAll,\n            filter_duplicates: true,\n        }\n    }\n\n    /// Low power scan parameters (longer intervals, shorter windows)\n    pub fn low_power() -> Self {\n        Self {\n            scan_type: ScanType::Passive,\n            scan_interval: 0x0800, // 1.28s\n            scan_window: 0x0012,   // 11.25ms (~0.9% duty cycle)\n            own_address_type: OwnAddressType::Public,\n            filter_policy: ScanFilterPolicy::AcceptAll,\n            filter_duplicates: true,\n        }\n    }\n}\n\n/// BLE Scanner for discovering nearby devices\n///\n/// The Scanner provides methods for starting and stopping BLE scanning.\n/// Advertising reports are received through the main event loop.\npub struct Scanner<'d> {\n    cmd: &'d CommandSender,\n    is_scanning: bool,\n}\n\nimpl<'d> Scanner<'d> {\n    /// Create a new Scanner\n    pub(crate) fn new(cmd: &'d CommandSender) -> Self {\n        Self {\n            cmd,\n            is_scanning: false,\n        }\n    }\n\n    /// Start scanning with the given parameters\n    ///\n    /// Advertising reports will be received as `LeAdvertisingReport` events\n    /// through the main BLE event loop.\n    ///\n    /// # Parameters\n    ///\n    /// - `params`: Scan parameters\n    ///\n    /// # Example\n    ///\n    /// ```no_run\n    /// let mut scanner = ble.scanner();\n    /// scanner.start(ScanParams::fast())?;\n    ///\n    /// // Advertising reports received in event loop\n    /// loop {\n    ///     let event = ble.read_event().await;\n    ///     if let EventParams::LeAdvertisingReport { reports } = event.params {\n    ///         for report in reports {\n    ///             // Process advertising report\n    ///         }\n    ///     }\n    /// }\n    /// ```\n    pub fn start(&mut self, params: ScanParams) -> Result<(), BleError> {\n        // Set scan parameters\n        self.cmd.le_set_scan_parameters(\n            params.scan_type as u8,\n            params.scan_interval,\n            params.scan_window,\n            params.own_address_type,\n            params.filter_policy as u8,\n        )?;\n\n        // Enable scanning\n        self.cmd.le_set_scan_enable(true, params.filter_duplicates)?;\n\n        self.is_scanning = true;\n        Ok(())\n    }\n\n    /// Stop scanning\n    pub fn stop(&mut self) -> Result<(), BleError> {\n        if self.is_scanning {\n            self.cmd.le_set_scan_enable(false, false)?;\n            self.is_scanning = false;\n        }\n        Ok(())\n    }\n\n    /// Check if currently scanning\n    pub fn is_scanning(&self) -> bool {\n        self.is_scanning\n    }\n}\n\n/// Parsed advertising data from a scan report\n#[derive(Debug, Default)]\npub struct ParsedAdvData<'a> {\n    /// Device name (complete or shortened)\n    pub name: Option<&'a str>,\n    /// TX power level in dBm\n    pub tx_power: Option<i8>,\n    /// 16-bit service UUIDs\n    pub service_uuids_16: heapless::Vec<u16, 8>,\n    /// 128-bit service UUIDs\n    pub service_uuids_128: heapless::Vec<[u8; 16], 2>,\n    /// Manufacturer-specific data (company ID, data)\n    pub manufacturer_data: Option<(u16, &'a [u8])>,\n    /// Flags byte\n    pub flags: Option<u8>,\n}\n\nimpl<'a> ParsedAdvData<'a> {\n    /// Parse advertising data bytes into structured format\n    pub fn parse(data: &'a [u8]) -> Self {\n        let mut result = Self::default();\n        let mut offset = 0;\n\n        while offset < data.len() {\n            if offset + 1 >= data.len() {\n                break;\n            }\n\n            let length = data[offset] as usize;\n            if length == 0 || offset + length >= data.len() {\n                break;\n            }\n\n            let ad_type = data[offset + 1];\n            let ad_data = &data[offset + 2..offset + 1 + length];\n\n            match ad_type {\n                0x01 => {\n                    // Flags\n                    if !ad_data.is_empty() {\n                        result.flags = Some(ad_data[0]);\n                    }\n                }\n                0x02 | 0x03 => {\n                    // Incomplete/Complete list of 16-bit UUIDs\n                    for chunk in ad_data.chunks(2) {\n                        if chunk.len() == 2 {\n                            let uuid = u16::from_le_bytes([chunk[0], chunk[1]]);\n                            let _ = result.service_uuids_16.push(uuid);\n                        }\n                    }\n                }\n                0x06 | 0x07 => {\n                    // Incomplete/Complete list of 128-bit UUIDs\n                    for chunk in ad_data.chunks(16) {\n                        if chunk.len() == 16 {\n                            let mut uuid = [0u8; 16];\n                            uuid.copy_from_slice(chunk);\n                            let _ = result.service_uuids_128.push(uuid);\n                        }\n                    }\n                }\n                0x08 | 0x09 => {\n                    // Shortened/Complete local name\n                    if let Ok(name) = core::str::from_utf8(ad_data) {\n                        result.name = Some(name);\n                    }\n                }\n                0x0A => {\n                    // TX Power Level\n                    if !ad_data.is_empty() {\n                        result.tx_power = Some(ad_data[0] as i8);\n                    }\n                }\n                0xFF => {\n                    // Manufacturer Specific Data\n                    if ad_data.len() >= 2 {\n                        let company_id = u16::from_le_bytes([ad_data[0], ad_data[1]]);\n                        result.manufacturer_data = Some((company_id, &ad_data[2..]));\n                    }\n                }\n                _ => {\n                    // Unknown AD type, skip\n                }\n            }\n\n            offset += 1 + length;\n        }\n\n        result\n    }\n}\n"
  },
  {
    "path": "embassy-stm32-wpan/src/wba/gap/types.rs",
    "content": "//! GAP types and constants\n\nuse crate::wba::error::BleError;\n// Re-export HCI types for convenience\npub use crate::wba::hci::types::OwnAddressType;\nuse crate::wba::hci::types::{AdvFilterPolicy, AdvType as HciAdvType};\n\n/// Advertising type\n#[derive(Debug, Clone, Copy, PartialEq, Eq)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub enum AdvType {\n    /// Connectable undirected advertising\n    ConnectableUndirected,\n    /// Connectable directed advertising (high duty cycle)\n    ConnectableDirectedHighDuty,\n    /// Scannable undirected advertising\n    ScannableUndirected,\n    /// Non-connectable undirected advertising\n    NonConnectableUndirected,\n    /// Connectable directed advertising (low duty cycle)\n    ConnectableDirectedLowDuty,\n}\n\nimpl From<AdvType> for HciAdvType {\n    fn from(adv_type: AdvType) -> Self {\n        match adv_type {\n            AdvType::ConnectableUndirected => HciAdvType::ConnectableUndirected,\n            AdvType::ConnectableDirectedHighDuty => HciAdvType::ConnectableDirectedHighDutyCycle,\n            AdvType::ScannableUndirected => HciAdvType::ScannableUndirected,\n            AdvType::NonConnectableUndirected => HciAdvType::NonConnectableUndirected,\n            AdvType::ConnectableDirectedLowDuty => HciAdvType::ConnectableDirectedLowDutyCycle,\n        }\n    }\n}\n\n/// Advertising parameters\n#[derive(Debug, Clone)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub struct AdvParams {\n    /// Minimum advertising interval in units of 0.625ms (Range: 0x0020 to 0x4000)\n    /// Default: 0x0800 (1.28 seconds)\n    pub interval_min: u16,\n\n    /// Maximum advertising interval in units of 0.625ms (Range: 0x0020 to 0x4000)\n    /// Default: 0x0800 (1.28 seconds)\n    pub interval_max: u16,\n\n    /// Advertising type\n    pub adv_type: AdvType,\n\n    /// Own address type\n    pub own_addr_type: OwnAddressType,\n\n    /// Advertising filter policy\n    pub filter_policy: AdvFilterPolicy,\n\n    /// Advertising channel map (bit 0: channel 37, bit 1: channel 38, bit 2: channel 39)\n    /// Default: 0x07 (all channels)\n    pub channel_map: u8,\n}\n\nimpl Default for AdvParams {\n    fn default() -> Self {\n        Self {\n            interval_min: 0x0800, // 1.28 seconds\n            interval_max: 0x0800, // 1.28 seconds\n            adv_type: AdvType::ConnectableUndirected,\n            own_addr_type: OwnAddressType::Public,\n            filter_policy: AdvFilterPolicy::All,\n            channel_map: 0x07, // All channels\n        }\n    }\n}\n\n/// AD Type constants for advertising data formatting\n#[allow(dead_code)]\nmod ad_type {\n    pub const FLAGS: u8 = 0x01;\n    pub const INCOMPLETE_LIST_16BIT_SERVICE_UUIDS: u8 = 0x02;\n    pub const COMPLETE_LIST_16BIT_SERVICE_UUIDS: u8 = 0x03;\n    pub const INCOMPLETE_LIST_32BIT_SERVICE_UUIDS: u8 = 0x04;\n    pub const COMPLETE_LIST_32BIT_SERVICE_UUIDS: u8 = 0x05;\n    pub const INCOMPLETE_LIST_128BIT_SERVICE_UUIDS: u8 = 0x06;\n    pub const COMPLETE_LIST_128BIT_SERVICE_UUIDS: u8 = 0x07;\n    pub const SHORTENED_LOCAL_NAME: u8 = 0x08;\n    pub const COMPLETE_LOCAL_NAME: u8 = 0x09;\n    pub const TX_POWER_LEVEL: u8 = 0x0A;\n    pub const MANUFACTURER_SPECIFIC_DATA: u8 = 0xFF;\n}\n\n/// Advertising data builder\n///\n/// Constructs advertising data according to the Bluetooth Core Specification.\n/// Each data element consists of:\n/// - Length (1 byte): length of type + data\n/// - Type (1 byte): AD type\n/// - Data (0-29 bytes): type-specific data\n///\n/// Maximum advertising data length is 31 bytes.\n#[derive(Clone)]\npub struct AdvData {\n    data: heapless::Vec<u8, 31>,\n}\n\nimpl AdvData {\n    /// Create a new empty advertising data builder\n    pub fn new() -> Self {\n        Self {\n            data: heapless::Vec::new(),\n        }\n    }\n\n    /// Add flags to advertising data\n    ///\n    /// Common flag values:\n    /// - 0x01: LE Limited Discoverable Mode\n    /// - 0x02: LE General Discoverable Mode\n    /// - 0x04: BR/EDR Not Supported\n    /// - 0x05: Simultaneous LE and BR/EDR to Same Device Capable (Controller)\n    /// - 0x06: Simultaneous LE and BR/EDR to Same Device Capable (Host)\n    ///\n    /// Typical value: 0x06 (General Discoverable + BR/EDR Not Supported)\n    pub fn add_flags(&mut self, flags: u8) -> Result<&mut Self, BleError> {\n        self.add_field(ad_type::FLAGS, &[flags])\n    }\n\n    /// Add complete local name to advertising data\n    pub fn add_name(&mut self, name: &str) -> Result<&mut Self, BleError> {\n        self.add_field(ad_type::COMPLETE_LOCAL_NAME, name.as_bytes())\n    }\n\n    /// Add shortened local name to advertising data\n    pub fn add_short_name(&mut self, name: &str) -> Result<&mut Self, BleError> {\n        self.add_field(ad_type::SHORTENED_LOCAL_NAME, name.as_bytes())\n    }\n\n    /// Add 16-bit service UUID (complete list)\n    pub fn add_service_uuid_16(&mut self, uuid: u16) -> Result<&mut Self, BleError> {\n        let uuid_bytes = uuid.to_le_bytes();\n        self.add_field(ad_type::COMPLETE_LIST_16BIT_SERVICE_UUIDS, &uuid_bytes)\n    }\n\n    /// Add multiple 16-bit service UUIDs (complete list)\n    pub fn add_service_uuids_16(&mut self, uuids: &[u16]) -> Result<&mut Self, BleError> {\n        let mut uuid_bytes = heapless::Vec::<u8, 30>::new();\n        for uuid in uuids {\n            uuid_bytes\n                .extend_from_slice(&uuid.to_le_bytes())\n                .map_err(|_| BleError::BufferFull)?;\n        }\n        self.add_field(ad_type::COMPLETE_LIST_16BIT_SERVICE_UUIDS, &uuid_bytes)\n    }\n\n    /// Add 128-bit service UUID (complete list)\n    pub fn add_service_uuid_128(&mut self, uuid: &[u8; 16]) -> Result<&mut Self, BleError> {\n        self.add_field(ad_type::COMPLETE_LIST_128BIT_SERVICE_UUIDS, uuid)\n    }\n\n    /// Add TX power level\n    pub fn add_tx_power(&mut self, power: i8) -> Result<&mut Self, BleError> {\n        self.add_field(ad_type::TX_POWER_LEVEL, &[power as u8])\n    }\n\n    /// Add manufacturer-specific data\n    ///\n    /// Format: 2-byte company ID (little-endian) followed by data\n    pub fn add_manufacturer_data(&mut self, company_id: u16, data: &[u8]) -> Result<&mut Self, BleError> {\n        let mut mfg_data = heapless::Vec::<u8, 29>::new();\n        mfg_data\n            .extend_from_slice(&company_id.to_le_bytes())\n            .map_err(|_| BleError::BufferFull)?;\n        mfg_data.extend_from_slice(data).map_err(|_| BleError::BufferFull)?;\n        self.add_field(ad_type::MANUFACTURER_SPECIFIC_DATA, &mfg_data)\n    }\n\n    /// Add raw field to advertising data\n    fn add_field(&mut self, ad_type: u8, data: &[u8]) -> Result<&mut Self, BleError> {\n        let field_len = 1 + data.len(); // Type + data\n\n        if field_len > 255 {\n            return Err(BleError::InvalidParameter);\n        }\n\n        if self.data.len() + field_len + 1 > 31 {\n            return Err(BleError::BufferFull);\n        }\n\n        // Add length byte\n        self.data.push(field_len as u8).map_err(|_| BleError::BufferFull)?;\n\n        // Add type byte\n        self.data.push(ad_type).map_err(|_| BleError::BufferFull)?;\n\n        // Add data\n        self.data.extend_from_slice(data).map_err(|_| BleError::BufferFull)?;\n\n        Ok(self)\n    }\n\n    /// Get the advertising data as a byte slice\n    pub fn build(&self) -> &[u8] {\n        &self.data\n    }\n\n    /// Get the length of the advertising data\n    pub fn len(&self) -> usize {\n        self.data.len()\n    }\n\n    /// Check if the advertising data is empty\n    pub fn is_empty(&self) -> bool {\n        self.data.is_empty()\n    }\n}\n\nimpl Default for AdvData {\n    fn default() -> Self {\n        Self::new()\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n\n    #[test]\n    fn test_adv_data_flags() {\n        let mut adv_data = AdvData::new();\n        adv_data.add_flags(0x06).unwrap();\n\n        assert_eq!(adv_data.build(), &[0x02, 0x01, 0x06]);\n    }\n\n    #[test]\n    fn test_adv_data_name() {\n        let mut adv_data = AdvData::new();\n        adv_data.add_name(\"Test\").unwrap();\n\n        assert_eq!(adv_data.build(), &[0x05, 0x09, b'T', b'e', b's', b't']);\n    }\n\n    #[test]\n    fn test_adv_data_multiple_fields() {\n        let mut adv_data = AdvData::new();\n        adv_data.add_flags(0x06).unwrap();\n        adv_data.add_name(\"BLE\").unwrap();\n\n        assert_eq!(\n            adv_data.build(),\n            &[\n                0x02, 0x01, 0x06, // Flags\n                0x04, 0x09, b'B', b'L', b'E' // Name\n            ]\n        );\n    }\n\n    #[test]\n    fn test_adv_data_buffer_full() {\n        let mut adv_data = AdvData::new();\n        let long_name = \"This is a very long name that exceeds the maximum advertising data length\";\n\n        assert!(adv_data.add_name(long_name).is_err());\n    }\n}\n"
  },
  {
    "path": "embassy-stm32-wpan/src/wba/gatt/events.rs",
    "content": "//! GATT Event types and handling\n//!\n//! This module defines events generated by GATT operations such as\n//! characteristic writes, notification confirmations, and MTU exchanges.\n\nuse crate::wba::hci::types::Handle;\n\n/// GATT events generated by remote device operations\n#[derive(Debug, Clone)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub enum GattEvent {\n    /// A characteristic attribute was modified by the remote device\n    ///\n    /// This event is generated when:\n    /// - A client writes to a characteristic\n    /// - A client writes to the CCCD (Client Characteristic Configuration Descriptor)\n    AttributeModified {\n        /// Connection handle\n        conn_handle: Handle,\n        /// Attribute handle that was modified\n        attr_handle: u16,\n        /// Offset within the attribute value\n        offset: u16,\n        /// The written data\n        data: heapless::Vec<u8, 247>,\n    },\n\n    /// Notification was sent successfully\n    NotificationComplete {\n        /// Connection handle\n        conn_handle: Handle,\n        /// Attribute handle\n        attr_handle: u16,\n    },\n\n    /// Indication was confirmed by the client\n    IndicationComplete {\n        /// Connection handle\n        conn_handle: Handle,\n        /// Attribute handle\n        attr_handle: u16,\n    },\n\n    /// MTU exchange completed\n    MtuExchangeComplete {\n        /// Connection handle\n        conn_handle: Handle,\n        /// Server MTU\n        server_mtu: u16,\n    },\n\n    /// GATT procedure completed\n    ProcedureComplete {\n        /// Connection handle\n        conn_handle: Handle,\n        /// Error code (0 = success)\n        error_code: u8,\n    },\n\n    /// GATT procedure timed out\n    ProcedureTimeout {\n        /// Connection handle\n        conn_handle: Handle,\n    },\n\n    /// Write request confirmation needed\n    ///\n    /// Generated for writes that require confirmation (Write Request vs Write Command)\n    WritePermitRequest {\n        /// Connection handle\n        conn_handle: Handle,\n        /// Attribute handle\n        attr_handle: u16,\n        /// Offset within the attribute\n        offset: u16,\n        /// Data to be written\n        data: heapless::Vec<u8, 247>,\n    },\n\n    /// Read request (for dynamic value generation)\n    ReadPermitRequest {\n        /// Connection handle\n        conn_handle: Handle,\n        /// Attribute handle\n        attr_handle: u16,\n        /// Offset for read\n        offset: u16,\n    },\n\n    /// TX buffer available for more data\n    TxPoolAvailable {\n        /// Connection handle\n        conn_handle: Handle,\n        /// Number of available buffers\n        available_buffers: u16,\n    },\n}\n\n/// ACI GATT event codes (vendor-specific events)\npub mod aci_event_code {\n    /// Attribute was modified by remote device\n    pub const GATT_ATTRIBUTE_MODIFIED: u16 = 0x0C01;\n    /// GATT procedure completed\n    pub const GATT_PROC_COMPLETE: u16 = 0x0C02;\n    /// Notification sent\n    pub const GATT_NOTIFICATION_COMPLETE: u16 = 0x0C03;\n    /// Indication confirmed\n    pub const GATT_INDICATION_COMPLETE: u16 = 0x0C04;\n    /// ATT MTU exchange response\n    pub const ATT_EXCHANGE_MTU_RESP: u16 = 0x0802;\n    /// GATT procedure timeout\n    pub const GATT_PROC_TIMEOUT: u16 = 0x0C05;\n    /// Write permit request\n    pub const GATT_WRITE_PERMIT_REQ: u16 = 0x0C06;\n    /// Read permit request\n    pub const GATT_READ_PERMIT_REQ: u16 = 0x0C07;\n    /// TX pool available\n    pub const GATT_TX_POOL_AVAILABLE: u16 = 0x0C08;\n    /// Server confirmation needed\n    pub const GATT_SERVER_CONFIRMATION_NEEDED: u16 = 0x0C09;\n    /// Prepare write permit request\n    pub const GATT_PREPARE_WRITE_PERMIT_REQ: u16 = 0x0C0A;\n}\n\n/// CCCD (Client Characteristic Configuration Descriptor) values\n#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub struct CccdValue {\n    /// Notifications enabled\n    pub notifications: bool,\n    /// Indications enabled\n    pub indications: bool,\n}\n\nimpl CccdValue {\n    /// Create from raw CCCD bytes (little-endian u16)\n    pub fn from_bytes(bytes: &[u8]) -> Self {\n        if bytes.len() >= 2 {\n            let value = u16::from_le_bytes([bytes[0], bytes[1]]);\n            Self {\n                notifications: (value & 0x0001) != 0,\n                indications: (value & 0x0002) != 0,\n            }\n        } else if bytes.len() == 1 {\n            Self {\n                notifications: (bytes[0] & 0x01) != 0,\n                indications: (bytes[0] & 0x02) != 0,\n            }\n        } else {\n            Self::default()\n        }\n    }\n\n    /// Convert to raw CCCD bytes\n    pub fn to_bytes(&self) -> [u8; 2] {\n        let mut value: u16 = 0;\n        if self.notifications {\n            value |= 0x0001;\n        }\n        if self.indications {\n            value |= 0x0002;\n        }\n        value.to_le_bytes()\n    }\n}\n\n/// Offset from characteristic declaration handle to value handle\npub const CHAR_VALUE_HANDLE_OFFSET: u16 = 1;\n\n/// Offset from characteristic declaration handle to CCCD handle\npub const CHAR_CCCD_HANDLE_OFFSET: u16 = 2;\n\n/// Check if an attribute handle is a CCCD based on characteristic handle\npub fn is_cccd_handle(char_handle: u16, attr_handle: u16) -> bool {\n    attr_handle == char_handle + CHAR_CCCD_HANDLE_OFFSET\n}\n\n/// Check if an attribute handle is a characteristic value based on characteristic handle\npub fn is_value_handle(char_handle: u16, attr_handle: u16) -> bool {\n    attr_handle == char_handle + CHAR_VALUE_HANDLE_OFFSET\n}\n"
  },
  {
    "path": "embassy-stm32-wpan/src/wba/gatt/mod.rs",
    "content": "//! GATT (Generic Attribute Profile) implementation for WBA BLE stack\n//!\n//! This module provides a Rust wrapper around ST's GATT implementation,\n//! including server functionality and event handling.\n\npub mod events;\npub mod server;\npub mod types;\n\npub use events::{\n    CHAR_CCCD_HANDLE_OFFSET, CHAR_VALUE_HANDLE_OFFSET, CccdValue, GattEvent, aci_event_code, is_cccd_handle,\n    is_value_handle,\n};\npub use server::GattServer;\npub use types::{\n    CharProperties, CharacteristicHandle, GattEventMask, SecurityPermissions, ServiceHandle, ServiceType, Uuid,\n    UuidType,\n};\n"
  },
  {
    "path": "embassy-stm32-wpan/src/wba/gatt/server.rs",
    "content": "//! GATT Server implementation\n//!\n//! This is a thin Rust wrapper around ST's GATT server implementation.\n\nuse super::types::{\n    CharProperties, CharacteristicHandle, GattEventMask, SecurityPermissions, ServiceHandle, ServiceType, Uuid,\n    UuidType,\n};\nuse crate::wba::bindings::ble;\nuse crate::wba::error::BleError;\n\n// The C library exports uppercase function names\n#[allow(non_camel_case_types)]\ntype tBleStatus = u8;\n\n#[link(name = \"stm32wba_ble_stack_basic\")]\nunsafe extern \"C\" {\n    #[link_name = \"ACI_GATT_INIT\"]\n    fn aci_gatt_init() -> tBleStatus;\n\n    #[link_name = \"ACI_GATT_ADD_SERVICE\"]\n    fn aci_gatt_add_service(\n        service_uuid_type: u8,\n        service_uuid: *const u8,\n        service_type: u8,\n        max_attribute_records: u8,\n        service_handle: *mut u16,\n    ) -> tBleStatus;\n\n    #[link_name = \"ACI_GATT_ADD_CHAR\"]\n    fn aci_gatt_add_char(\n        service_handle: u16,\n        char_uuid_type: u8,\n        char_uuid: *const u8,\n        char_value_length: u16,\n        char_properties: u8,\n        security_permissions: u8,\n        gatt_evt_mask: u8,\n        enc_key_size: u8,\n        is_variable: u8,\n        char_handle: *mut u16,\n    ) -> tBleStatus;\n\n    #[link_name = \"ACI_GATT_UPDATE_CHAR_VALUE\"]\n    fn aci_gatt_update_char_value(\n        service_handle: u16,\n        char_handle: u16,\n        val_offset: u8,\n        char_value_length: u8,\n        char_value: *const u8,\n    ) -> tBleStatus;\n\n    #[link_name = \"ACI_GATT_UPDATE_CHAR_VALUE_EXT\"]\n    fn aci_gatt_update_char_value_ext(\n        conn_handle: u16,\n        service_handle: u16,\n        char_handle: u16,\n        update_type: u8,\n        char_length: u16,\n        value_offset: u16,\n        value_length: u8,\n        value: *const u8,\n    ) -> tBleStatus;\n\n    #[link_name = \"ACI_GATT_READ_HANDLE_VALUE\"]\n    fn aci_gatt_read_handle_value(\n        attr_handle: u16,\n        offset: u16,\n        value_length_requested: u16,\n        value_length: *mut u16,\n        value_offset: *mut u16,\n        value: *mut u8,\n    ) -> tBleStatus;\n\n    #[link_name = \"ACI_GATT_SET_EVENT_MASK\"]\n    fn aci_gatt_set_event_mask(event_mask: u32) -> tBleStatus;\n}\n\n/// Update type for aci_gatt_update_char_value_ext\n#[allow(dead_code)]\nmod update_type {\n    /// Update locally only, do not notify/indicate\n    pub const LOCAL_ONLY: u8 = 0x00;\n    /// Send notification to connected client\n    pub const NOTIFICATION: u8 = 0x01;\n    /// Send indication to connected client\n    pub const INDICATION: u8 = 0x02;\n}\n\nconst BLE_STATUS_SUCCESS: u8 = 0x00;\n\n/// GATT Server\n///\n/// Provides methods for creating and managing GATT services and characteristics.\npub struct GattServer {\n    initialized: bool,\n}\n\nimpl GattServer {\n    /// Create a new GATT server instance\n    ///\n    /// Note: You must call `init()` before adding services or characteristics.\n    pub fn new() -> Self {\n        Self { initialized: false }\n    }\n\n    /// Initialize the GATT server\n    ///\n    /// This adds the GATT service with Service Changed Characteristic.\n    /// Must be called before using any GATT features.\n    pub fn init(&mut self) -> Result<(), BleError> {\n        if self.initialized {\n            return Ok(());\n        }\n\n        unsafe {\n            let status = aci_gatt_init();\n            if status == BLE_STATUS_SUCCESS {\n                self.initialized = true;\n                Ok(())\n            } else {\n                Err(BleError::CommandFailed(crate::wba::hci::types::Status::from_u8(status)))\n            }\n        }\n    }\n\n    /// Add a service to the GATT database\n    ///\n    /// # Parameters\n    ///\n    /// - `uuid`: Service UUID (16-bit or 128-bit)\n    /// - `service_type`: Primary or secondary service\n    /// - `max_attribute_records`: Maximum number of attribute records (characteristics + descriptors)\n    ///\n    /// # Returns\n    ///\n    /// Service handle on success\n    ///\n    /// # Example\n    ///\n    /// ```no_run\n    /// let service_uuid = Uuid::from_u16(0x1234);\n    /// let handle = gatt.add_service(service_uuid, ServiceType::Primary, 10)?;\n    /// ```\n    pub fn add_service(\n        &mut self,\n        uuid: Uuid,\n        service_type: ServiceType,\n        max_attribute_records: u8,\n    ) -> Result<ServiceHandle, BleError> {\n        if !self.initialized {\n            return Err(BleError::NotInitialized);\n        }\n\n        let mut service_handle: u16 = 0;\n\n        unsafe {\n            let status = match uuid {\n                Uuid::Uuid16(uuid16) => {\n                    let uuid_bytes = uuid16.to_le_bytes();\n                    aci_gatt_add_service(\n                        UuidType::Uuid16 as u8,\n                        uuid_bytes.as_ptr() as *const _,\n                        service_type as u8,\n                        max_attribute_records,\n                        &mut service_handle,\n                    )\n                }\n                Uuid::Uuid128(uuid128) => aci_gatt_add_service(\n                    UuidType::Uuid128 as u8,\n                    uuid128.as_ptr() as *const _,\n                    service_type as u8,\n                    max_attribute_records,\n                    &mut service_handle,\n                ),\n            };\n\n            if status == BLE_STATUS_SUCCESS {\n                Ok(ServiceHandle(service_handle))\n            } else {\n                Err(BleError::CommandFailed(crate::wba::hci::types::Status::from_u8(status)))\n            }\n        }\n    }\n\n    /// Add a characteristic to a service\n    ///\n    /// # Parameters\n    ///\n    /// - `service_handle`: Handle of the service\n    /// - `uuid`: Characteristic UUID (16-bit or 128-bit)\n    /// - `value_length`: Maximum length of characteristic value\n    /// - `properties`: Characteristic properties (read, write, notify, etc.)\n    /// - `security`: Security permissions\n    /// - `event_mask`: Events to be notified about\n    /// - `encryption_key_size`: Encryption key size (7-16 bytes, 0 = no encryption)\n    /// - `is_variable`: If true, the characteristic value can have variable length\n    ///\n    /// # Returns\n    ///\n    /// Characteristic handle on success\n    ///\n    /// # Example\n    ///\n    /// ```no_run\n    /// let char_uuid = Uuid::from_u16(0x2A00); // Device Name\n    /// let props = CharProperties::READ | CharProperties::WRITE;\n    /// let handle = gatt.add_characteristic(\n    ///     service_handle,\n    ///     char_uuid,\n    ///     20,\n    ///     props,\n    ///     SecurityPermissions::NONE,\n    ///     GattEventMask::ATTRIBUTE_MODIFIED,\n    ///     0,\n    ///     true,\n    /// )?;\n    /// ```\n    pub fn add_characteristic(\n        &mut self,\n        service_handle: ServiceHandle,\n        uuid: Uuid,\n        value_length: u16,\n        properties: CharProperties,\n        security: SecurityPermissions,\n        event_mask: GattEventMask,\n        encryption_key_size: u8,\n        is_variable: bool,\n    ) -> Result<CharacteristicHandle, BleError> {\n        if !self.initialized {\n            return Err(BleError::NotInitialized);\n        }\n\n        let mut char_handle: u16 = 0;\n\n        unsafe {\n            let status = match uuid {\n                Uuid::Uuid16(uuid16) => {\n                    let uuid_bytes = uuid16.to_le_bytes();\n                    aci_gatt_add_char(\n                        service_handle.0,\n                        UuidType::Uuid16 as u8,\n                        uuid_bytes.as_ptr() as *const _,\n                        value_length,\n                        properties.0,\n                        security.0,\n                        event_mask.0,\n                        encryption_key_size,\n                        is_variable as u8,\n                        &mut char_handle,\n                    )\n                }\n                Uuid::Uuid128(uuid128) => aci_gatt_add_char(\n                    service_handle.0,\n                    UuidType::Uuid128 as u8,\n                    uuid128.as_ptr() as *const _,\n                    value_length,\n                    properties.0,\n                    security.0,\n                    event_mask.0,\n                    encryption_key_size,\n                    is_variable as u8,\n                    &mut char_handle,\n                ),\n            };\n\n            if status == BLE_STATUS_SUCCESS {\n                Ok(CharacteristicHandle(char_handle))\n            } else {\n                Err(BleError::CommandFailed(crate::wba::hci::types::Status::from_u8(status)))\n            }\n        }\n    }\n\n    /// Update a characteristic value\n    ///\n    /// # Parameters\n    ///\n    /// - `service_handle`: Handle of the service\n    /// - `char_handle`: Handle of the characteristic\n    /// - `offset`: Offset at which to write the value\n    /// - `value`: Value to write\n    ///\n    /// # Example\n    ///\n    /// ```no_run\n    /// let value = b\"Hello\";\n    /// gatt.update_characteristic_value(service_handle, char_handle, 0, value)?;\n    /// ```\n    pub fn update_characteristic_value(\n        &mut self,\n        service_handle: ServiceHandle,\n        char_handle: CharacteristicHandle,\n        offset: u8,\n        value: &[u8],\n    ) -> Result<(), BleError> {\n        if !self.initialized {\n            return Err(BleError::NotInitialized);\n        }\n\n        if value.len() > 255 {\n            return Err(BleError::InvalidParameter);\n        }\n\n        unsafe {\n            let status = aci_gatt_update_char_value(\n                service_handle.0,\n                char_handle.0,\n                offset,\n                value.len() as u8,\n                value.as_ptr(),\n            );\n\n            if status == BLE_STATUS_SUCCESS {\n                Ok(())\n            } else {\n                Err(BleError::CommandFailed(crate::wba::hci::types::Status::from_u8(status)))\n            }\n        }\n    }\n\n    /// Delete a service from the GATT database\n    ///\n    /// # Parameters\n    ///\n    /// - `service_handle`: Handle of the service to delete\n    pub fn delete_service(&mut self, service_handle: ServiceHandle) -> Result<(), BleError> {\n        if !self.initialized {\n            return Err(BleError::NotInitialized);\n        }\n\n        unsafe {\n            let status = ble::aci_gatt_del_service(service_handle.0);\n\n            if status == BLE_STATUS_SUCCESS {\n                Ok(())\n            } else {\n                Err(BleError::CommandFailed(crate::wba::hci::types::Status::from_u8(status)))\n            }\n        }\n    }\n\n    /// Delete a characteristic from a service\n    ///\n    /// # Parameters\n    ///\n    /// - `service_handle`: Handle of the service\n    /// - `char_handle`: Handle of the characteristic to delete\n    pub fn delete_characteristic(\n        &mut self,\n        service_handle: ServiceHandle,\n        char_handle: CharacteristicHandle,\n    ) -> Result<(), BleError> {\n        if !self.initialized {\n            return Err(BleError::NotInitialized);\n        }\n\n        unsafe {\n            let status = ble::aci_gatt_del_char(service_handle.0, char_handle.0);\n\n            if status == BLE_STATUS_SUCCESS {\n                Ok(())\n            } else {\n                Err(BleError::CommandFailed(crate::wba::hci::types::Status::from_u8(status)))\n            }\n        }\n    }\n\n    /// Send a notification to a connected client\n    ///\n    /// This sends a notification (unconfirmed) containing the characteristic value\n    /// to the specified connection. The client must have enabled notifications\n    /// via the CCCD for this characteristic.\n    ///\n    /// # Parameters\n    ///\n    /// - `conn_handle`: Connection handle to send notification to\n    /// - `service_handle`: Handle of the service\n    /// - `char_handle`: Handle of the characteristic\n    /// - `value`: Value to send in the notification\n    ///\n    /// # Returns\n    ///\n    /// - `Ok(())` if the notification was queued successfully\n    /// - `Err(BleError)` if the notification failed\n    ///\n    /// # Note\n    ///\n    /// The `GattNotificationComplete` event will be generated when the notification\n    /// has been transmitted.\n    pub fn notify(\n        &self,\n        conn_handle: u16,\n        service_handle: ServiceHandle,\n        char_handle: CharacteristicHandle,\n        value: &[u8],\n    ) -> Result<(), BleError> {\n        if !self.initialized {\n            return Err(BleError::NotInitialized);\n        }\n\n        if value.len() > 251 {\n            return Err(BleError::InvalidParameter);\n        }\n\n        unsafe {\n            let status = aci_gatt_update_char_value_ext(\n                conn_handle,\n                service_handle.0,\n                char_handle.0,\n                update_type::NOTIFICATION,\n                value.len() as u16,\n                0, // offset\n                value.len() as u8,\n                value.as_ptr(),\n            );\n\n            if status == BLE_STATUS_SUCCESS {\n                Ok(())\n            } else {\n                Err(BleError::CommandFailed(crate::wba::hci::types::Status::from_u8(status)))\n            }\n        }\n    }\n\n    /// Send an indication to a connected client\n    ///\n    /// This sends an indication (confirmed) containing the characteristic value\n    /// to the specified connection. The client must have enabled indications\n    /// via the CCCD for this characteristic. The server will wait for confirmation\n    /// from the client.\n    ///\n    /// # Parameters\n    ///\n    /// - `conn_handle`: Connection handle to send indication to\n    /// - `service_handle`: Handle of the service\n    /// - `char_handle`: Handle of the characteristic\n    /// - `value`: Value to send in the indication\n    ///\n    /// # Returns\n    ///\n    /// - `Ok(())` if the indication was queued successfully\n    /// - `Err(BleError)` if the indication failed\n    ///\n    /// # Note\n    ///\n    /// The `GattIndicationComplete` event will be generated when the client\n    /// confirms receipt of the indication.\n    pub fn indicate(\n        &self,\n        conn_handle: u16,\n        service_handle: ServiceHandle,\n        char_handle: CharacteristicHandle,\n        value: &[u8],\n    ) -> Result<(), BleError> {\n        if !self.initialized {\n            return Err(BleError::NotInitialized);\n        }\n\n        if value.len() > 251 {\n            return Err(BleError::InvalidParameter);\n        }\n\n        unsafe {\n            let status = aci_gatt_update_char_value_ext(\n                conn_handle,\n                service_handle.0,\n                char_handle.0,\n                update_type::INDICATION,\n                value.len() as u16,\n                0, // offset\n                value.len() as u8,\n                value.as_ptr(),\n            );\n\n            if status == BLE_STATUS_SUCCESS {\n                Ok(())\n            } else {\n                Err(BleError::CommandFailed(crate::wba::hci::types::Status::from_u8(status)))\n            }\n        }\n    }\n\n    /// Read a characteristic value from the local GATT database\n    ///\n    /// # Parameters\n    ///\n    /// - `attr_handle`: Attribute handle to read\n    /// - `buffer`: Buffer to store the value\n    ///\n    /// # Returns\n    ///\n    /// Number of bytes read on success\n    pub fn read_value(&self, attr_handle: u16, buffer: &mut [u8]) -> Result<usize, BleError> {\n        if !self.initialized {\n            return Err(BleError::NotInitialized);\n        }\n\n        unsafe {\n            let mut value_length: u16 = 0;\n            let mut value_offset: u16 = 0;\n\n            let status = aci_gatt_read_handle_value(\n                attr_handle,\n                0,\n                buffer.len() as u16,\n                &mut value_length,\n                &mut value_offset,\n                buffer.as_mut_ptr(),\n            );\n\n            if status == BLE_STATUS_SUCCESS {\n                Ok(value_length as usize)\n            } else {\n                Err(BleError::CommandFailed(crate::wba::hci::types::Status::from_u8(status)))\n            }\n        }\n    }\n\n    /// Set the GATT event mask\n    ///\n    /// Controls which GATT events are reported to the application.\n    ///\n    /// # Parameters\n    ///\n    /// - `mask`: Bitmask of events to enable (use GattEventMaskBits constants)\n    pub fn set_event_mask(&self, mask: u32) -> Result<(), BleError> {\n        if !self.initialized {\n            return Err(BleError::NotInitialized);\n        }\n\n        unsafe {\n            let status = aci_gatt_set_event_mask(mask);\n\n            if status == BLE_STATUS_SUCCESS {\n                Ok(())\n            } else {\n                Err(BleError::CommandFailed(crate::wba::hci::types::Status::from_u8(status)))\n            }\n        }\n    }\n}\n\nimpl Default for GattServer {\n    fn default() -> Self {\n        Self::new()\n    }\n}\n"
  },
  {
    "path": "embassy-stm32-wpan/src/wba/gatt/types.rs",
    "content": "//! GATT types and constants\n\n/// Service handle (returned by add_service)\n#[derive(Debug, Clone, Copy, PartialEq, Eq)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\n#[repr(transparent)]\npub struct ServiceHandle(pub u16);\n\n/// Characteristic handle (returned by add_characteristic)\n#[derive(Debug, Clone, Copy, PartialEq, Eq)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\n#[repr(transparent)]\npub struct CharacteristicHandle(pub u16);\n\n/// UUID type\n#[repr(u8)]\n#[derive(Debug, Clone, Copy, PartialEq, Eq)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub enum UuidType {\n    /// 16-bit UUID\n    Uuid16 = 0x01,\n    /// 128-bit UUID\n    Uuid128 = 0x02,\n}\n\n/// Service type\n#[repr(u8)]\n#[derive(Debug, Clone, Copy, PartialEq, Eq)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub enum ServiceType {\n    /// Primary service\n    Primary = 0x01,\n    /// Secondary service\n    Secondary = 0x02,\n}\n\n/// Characteristic properties (as per Bluetooth spec)\n#[derive(Debug, Clone, Copy, PartialEq, Eq)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub struct CharProperties(pub u8);\n\nimpl CharProperties {\n    pub const BROADCAST: Self = Self(0x01);\n    pub const READ: Self = Self(0x02);\n    pub const WRITE_WITHOUT_RESPONSE: Self = Self(0x04);\n    pub const WRITE: Self = Self(0x08);\n    pub const NOTIFY: Self = Self(0x10);\n    pub const INDICATE: Self = Self(0x20);\n    pub const AUTHENTICATED_SIGNED_WRITES: Self = Self(0x40);\n    pub const EXTENDED_PROPERTIES: Self = Self(0x80);\n\n    /// Create empty properties\n    pub const fn empty() -> Self {\n        Self(0)\n    }\n\n    /// Combine properties using bitwise OR\n    pub const fn or(self, other: Self) -> Self {\n        Self(self.0 | other.0)\n    }\n\n    /// Check if a property is set\n    pub const fn contains(self, other: Self) -> bool {\n        (self.0 & other.0) == other.0\n    }\n}\n\nimpl core::ops::BitOr for CharProperties {\n    type Output = Self;\n\n    fn bitor(self, rhs: Self) -> Self::Output {\n        Self(self.0 | rhs.0)\n    }\n}\n\nimpl core::ops::BitOrAssign for CharProperties {\n    fn bitor_assign(&mut self, rhs: Self) {\n        self.0 |= rhs.0;\n    }\n}\n\n/// Security permissions\n#[derive(Debug, Clone, Copy, PartialEq, Eq)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub struct SecurityPermissions(pub u8);\n\nimpl SecurityPermissions {\n    pub const NONE: Self = Self(0x00);\n    pub const AUTHEN_READ: Self = Self(0x01);\n    pub const AUTHOR_READ: Self = Self(0x02);\n    pub const ENCRY_READ: Self = Self(0x04);\n    pub const AUTHEN_WRITE: Self = Self(0x08);\n    pub const AUTHOR_WRITE: Self = Self(0x10);\n    pub const ENCRY_WRITE: Self = Self(0x20);\n\n    /// Create empty permissions\n    pub const fn empty() -> Self {\n        Self(0)\n    }\n\n    /// Combine permissions using bitwise OR\n    pub const fn or(self, other: Self) -> Self {\n        Self(self.0 | other.0)\n    }\n}\n\nimpl core::ops::BitOr for SecurityPermissions {\n    type Output = Self;\n\n    fn bitor(self, rhs: Self) -> Self::Output {\n        Self(self.0 | rhs.0)\n    }\n}\n\nimpl core::ops::BitOrAssign for SecurityPermissions {\n    fn bitor_assign(&mut self, rhs: Self) {\n        self.0 |= rhs.0;\n    }\n}\n\n/// GATT event mask\n#[derive(Debug, Clone, Copy, PartialEq, Eq)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub struct GattEventMask(pub u8);\n\nimpl GattEventMask {\n    pub const NONE: Self = Self(0x00);\n    pub const ATTRIBUTE_MODIFIED: Self = Self(0x01);\n\n    /// Create empty event mask\n    pub const fn empty() -> Self {\n        Self(0)\n    }\n\n    /// Combine event masks using bitwise OR\n    pub const fn or(self, other: Self) -> Self {\n        Self(self.0 | other.0)\n    }\n}\n\nimpl core::ops::BitOr for GattEventMask {\n    type Output = Self;\n\n    fn bitor(self, rhs: Self) -> Self::Output {\n        Self(self.0 | rhs.0)\n    }\n}\n\n/// UUID wrapper\n#[derive(Debug, Clone, Copy)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub enum Uuid {\n    /// 16-bit UUID\n    Uuid16(u16),\n    /// 128-bit UUID (stored in little-endian)\n    Uuid128([u8; 16]),\n}\n\nimpl Uuid {\n    /// Create a 16-bit UUID\n    pub const fn from_u16(uuid: u16) -> Self {\n        Self::Uuid16(uuid)\n    }\n\n    /// Create a 128-bit UUID from bytes (little-endian)\n    pub const fn from_u128_le(uuid: [u8; 16]) -> Self {\n        Self::Uuid128(uuid)\n    }\n\n    /// Get the UUID type\n    pub const fn uuid_type(&self) -> UuidType {\n        match self {\n            Uuid::Uuid16(_) => UuidType::Uuid16,\n            Uuid::Uuid128(_) => UuidType::Uuid128,\n        }\n    }\n\n    /// Get pointer and length for passing to C functions\n    pub fn as_ptr_and_type(&self) -> (*const u8, UuidType) {\n        match self {\n            Uuid::Uuid16(uuid) => {\n                let bytes = uuid.to_le_bytes();\n                (bytes.as_ptr(), UuidType::Uuid16)\n            }\n            Uuid::Uuid128(uuid) => (uuid.as_ptr(), UuidType::Uuid128),\n        }\n    }\n}\n\n// Common 16-bit UUIDs\nimpl Uuid {\n    /// Generic Access service\n    pub const GAP_SERVICE: Self = Self::Uuid16(0x1800);\n    /// Generic Attribute service\n    pub const GATT_SERVICE: Self = Self::Uuid16(0x1801);\n    /// Device Name characteristic\n    pub const DEVICE_NAME: Self = Self::Uuid16(0x2A00);\n    /// Appearance characteristic\n    pub const APPEARANCE: Self = Self::Uuid16(0x2A01);\n    /// Service Changed characteristic\n    pub const SERVICE_CHANGED: Self = Self::Uuid16(0x2A05);\n}\n"
  },
  {
    "path": "embassy-stm32-wpan/src/wba/hci/command.rs",
    "content": "//! HCI Command sending using WBA BLE stack C functions\n//!\n//! The WBA BLE stack provides high-level HCI command functions that we can call directly.\n//! These functions are synchronous and return a status code immediately, which simplifies\n//! the implementation compared to packet-based HCI.\n\nuse super::types::{AdvFilterPolicy, AdvType, OwnAddressType};\nuse crate::wba::bindings::ble;\nuse crate::wba::ble::VersionInfo;\nuse crate::wba::error::BleError;\n\n// The C library exports uppercase function names (HCI_RESET, etc.)\n// but the bindings declare lowercase names. We need to link to the actual symbols.\n#[allow(non_camel_case_types)]\ntype tBleStatus = u8;\n\n#[link(name = \"stm32wba_ble_stack_basic\")]\nunsafe extern \"C\" {\n    #[link_name = \"HCI_RESET\"]\n    fn hci_reset() -> tBleStatus;\n\n    #[link_name = \"HCI_READ_LOCAL_VERSION_INFORMATION\"]\n    fn hci_read_local_version_information(\n        hci_version: *mut u8,\n        hci_revision: *mut u16,\n        lmp_pal_version: *mut u8,\n        manufacturer_name: *mut u16,\n        lmp_pal_subversion: *mut u16,\n    ) -> tBleStatus;\n\n    #[link_name = \"HCI_READ_BD_ADDR\"]\n    fn hci_read_bd_addr(bd_addr: *mut [u8; 6]) -> tBleStatus;\n\n    #[link_name = \"HCI_SET_EVENT_MASK\"]\n    fn hci_set_event_mask(event_mask: *const [u8; 8]) -> tBleStatus;\n\n    #[link_name = \"HCI_LE_SET_EVENT_MASK\"]\n    fn hci_le_set_event_mask(le_event_mask: *const [u8; 8]) -> tBleStatus;\n\n    #[link_name = \"HCI_LE_SET_ADVERTISING_PARAMETERS\"]\n    fn hci_le_set_advertising_parameters(\n        advertising_interval_min: u16,\n        advertising_interval_max: u16,\n        advertising_type: u8,\n        own_address_type: u8,\n        peer_address_type: u8,\n        peer_address: *const [u8; 6],\n        advertising_channel_map: u8,\n        advertising_filter_policy: u8,\n    ) -> tBleStatus;\n\n    #[link_name = \"HCI_LE_SET_ADVERTISING_DATA\"]\n    fn hci_le_set_advertising_data(advertising_data_length: u8, advertising_data: *const u8) -> tBleStatus;\n\n    #[link_name = \"HCI_LE_SET_SCAN_RESPONSE_DATA\"]\n    fn hci_le_set_scan_response_data(scan_response_data_length: u8, scan_response_data: *const u8) -> tBleStatus;\n\n    #[link_name = \"HCI_LE_SET_SCAN_PARAMETERS\"]\n    fn hci_le_set_scan_parameters(\n        le_scan_type: u8,\n        le_scan_interval: u16,\n        le_scan_window: u16,\n        own_address_type: u8,\n        scanning_filter_policy: u8,\n    ) -> tBleStatus;\n\n    #[link_name = \"HCI_LE_SET_SCAN_ENABLE\"]\n    fn hci_le_set_scan_enable(le_scan_enable: u8, filter_duplicates: u8) -> tBleStatus;\n\n    #[link_name = \"HCI_LE_SET_ADVERTISING_ENABLE\"]\n    fn hci_le_set_advertising_enable(advertising_enable: u8) -> tBleStatus;\n\n    #[link_name = \"HCI_LE_READ_BUFFER_SIZE_V2\"]\n    fn hci_le_read_buffer_size_v2(\n        le_acl_data_packet_length: *mut u16,\n        total_num_le_acl_data_packets: *mut u8,\n        iso_data_packet_length: *mut u16,\n        total_num_iso_data_packets: *mut u8,\n    ) -> tBleStatus;\n\n    #[link_name = \"HCI_LE_READ_LOCAL_SUPPORTED_FEATURES_PAGE_0\"]\n    fn hci_le_read_local_supported_features_page_0(le_features: *mut [u8; 8]) -> tBleStatus;\n\n    // Connection management commands\n    #[link_name = \"HCI_DISCONNECT\"]\n    fn hci_disconnect(connection_handle: u16, reason: u8) -> tBleStatus;\n\n    #[link_name = \"HCI_LE_READ_PHY\"]\n    fn hci_le_read_phy(connection_handle: u16, tx_phy: *mut u8, rx_phy: *mut u8) -> tBleStatus;\n\n    #[link_name = \"HCI_LE_SET_PHY\"]\n    fn hci_le_set_phy(connection_handle: u16, all_phys: u8, tx_phys: u8, rx_phys: u8, phy_options: u16) -> tBleStatus;\n\n    #[link_name = \"HCI_LE_CONNECTION_UPDATE\"]\n    fn hci_le_connection_update(\n        connection_handle: u16,\n        conn_interval_min: u16,\n        conn_interval_max: u16,\n        conn_latency: u16,\n        supervision_timeout: u16,\n        minimum_ce_length: u16,\n        maximum_ce_length: u16,\n    ) -> tBleStatus;\n\n    #[link_name = \"HCI_LE_CREATE_CONNECTION\"]\n    fn hci_le_create_connection(\n        le_scan_interval: u16,\n        le_scan_window: u16,\n        initiator_filter_policy: u8,\n        peer_address_type: u8,\n        peer_address: *const [u8; 6],\n        own_address_type: u8,\n        conn_interval_min: u16,\n        conn_interval_max: u16,\n        max_latency: u16,\n        supervision_timeout: u16,\n        min_ce_length: u16,\n        max_ce_length: u16,\n    ) -> tBleStatus;\n\n    #[link_name = \"HCI_LE_CREATE_CONNECTION_CANCEL\"]\n    fn hci_le_create_connection_cancel() -> tBleStatus;\n\n    #[link_name = \"HCI_LE_SET_DATA_LENGTH\"]\n    fn hci_le_set_data_length(connection_handle: u16, tx_octets: u16, tx_time: u16) -> tBleStatus;\n}\n\n/// BLE Success status code\nconst BLE_STATUS_SUCCESS: u8 = 0x00;\n\n/// Command sender for HCI commands\n///\n/// This uses the WBA BLE stack's built-in HCI command functions rather than\n/// sending raw HCI packets. The C functions handle packet formatting and\n/// communication with the controller.\npub struct CommandSender {\n    // Zero-sized type - all operations use C functions\n}\n\nimpl CommandSender {\n    /// Create a new CommandSender\n    pub fn new() -> Self {\n        Self {}\n    }\n\n    /// Check if a BLE status indicates success\n    fn check_status(status: u8) -> Result<(), BleError> {\n        if status == BLE_STATUS_SUCCESS {\n            Ok(())\n        } else {\n            // Map BLE status to HCI status for error reporting\n            Err(BleError::CommandFailed(super::types::Status::from_u8(status)))\n        }\n    }\n\n    // ===== Controller & Baseband Commands =====\n\n    /// Reset the BLE controller\n    pub fn reset(&self) -> Result<(), BleError> {\n        unsafe {\n            let status = hci_reset();\n            Self::check_status(status)\n        }\n    }\n\n    /// Read local version information\n    pub fn read_local_version(&self) -> Result<VersionInfo, BleError> {\n        unsafe {\n            let mut hci_version = 0u8;\n            let mut hci_revision = 0u16;\n            let mut lmp_pal_version = 0u8;\n            let mut manufacturer_name = 0u16;\n            let mut lmp_pal_subversion = 0u16;\n\n            let status = hci_read_local_version_information(\n                &mut hci_version,\n                &mut hci_revision,\n                &mut lmp_pal_version,\n                &mut manufacturer_name,\n                &mut lmp_pal_subversion,\n            );\n\n            Self::check_status(status)?;\n            Ok(VersionInfo {\n                hci_version,\n                hci_revision,\n                lmp_version: lmp_pal_version,\n                manufacturer_name,\n                lmp_subversion: lmp_pal_subversion,\n            })\n        }\n    }\n\n    /// Set event mask\n    pub fn set_event_mask(&self, mask: u64) -> Result<(), BleError> {\n        unsafe {\n            let mask_bytes = mask.to_le_bytes();\n            let status = hci_set_event_mask(&mask_bytes as *const [u8; 8]);\n            Self::check_status(status)\n        }\n    }\n\n    /// Read BD_ADDR (Bluetooth device address)\n    pub fn read_bd_addr(&self) -> Result<[u8; 6], BleError> {\n        unsafe {\n            let mut addr = [0u8; 6];\n            let status = hci_read_bd_addr(&mut addr as *mut [u8; 6]);\n            Self::check_status(status)?;\n            Ok(addr)\n        }\n    }\n\n    // ===== LE Controller Commands =====\n\n    /// Set LE event mask\n    pub fn le_set_event_mask(&self, mask: u64) -> Result<(), BleError> {\n        unsafe {\n            let mask_bytes = mask.to_le_bytes();\n            let status = hci_le_set_event_mask(&mask_bytes as *const [u8; 8]);\n            Self::check_status(status)\n        }\n    }\n\n    /// Set random address\n    pub fn le_set_random_address(&self, address: &[u8; 6]) -> Result<(), BleError> {\n        unsafe {\n            let status = ble::hci_le_set_random_address(address.as_ptr());\n            Self::check_status(status)\n        }\n    }\n\n    /// Set advertising parameters\n    pub fn le_set_advertising_parameters(\n        &self,\n        interval_min: u16,\n        interval_max: u16,\n        adv_type: AdvType,\n        own_addr_type: OwnAddressType,\n        peer_addr_type: u8,\n        peer_addr: &[u8; 6],\n        channel_map: u8,\n        filter_policy: AdvFilterPolicy,\n    ) -> Result<(), BleError> {\n        unsafe {\n            let status = hci_le_set_advertising_parameters(\n                interval_min,\n                interval_max,\n                adv_type as u8,\n                own_addr_type as u8,\n                peer_addr_type,\n                peer_addr as *const [u8; 6],\n                channel_map,\n                filter_policy as u8,\n            );\n            Self::check_status(status)\n        }\n    }\n\n    /// Read advertising physical channel TX power\n    pub fn le_read_advertising_channel_tx_power(&self) -> Result<i8, BleError> {\n        unsafe {\n            let mut tx_power = 0u8;\n            let status = ble::hci_le_read_advertising_physical_channel_tx_power(&mut tx_power);\n            Self::check_status(status)?;\n            Ok(tx_power as i8)\n        }\n    }\n\n    /// Set advertising data\n    pub fn le_set_advertising_data(&self, data: &[u8]) -> Result<(), BleError> {\n        if data.len() > 31 {\n            return Err(BleError::InvalidParameter);\n        }\n\n        unsafe {\n            let status = hci_le_set_advertising_data(data.len() as u8, data.as_ptr());\n            Self::check_status(status)\n        }\n    }\n\n    /// Set scan response data\n    pub fn le_set_scan_response_data(&self, data: &[u8]) -> Result<(), BleError> {\n        if data.len() > 31 {\n            return Err(BleError::InvalidParameter);\n        }\n\n        unsafe {\n            let status = hci_le_set_scan_response_data(data.len() as u8, data.as_ptr());\n            Self::check_status(status)\n        }\n    }\n\n    /// Enable or disable advertising\n    pub fn le_set_advertise_enable(&self, enable: bool) -> Result<(), BleError> {\n        unsafe {\n            let status = hci_le_set_advertising_enable(if enable { 1 } else { 0 });\n            Self::check_status(status)\n        }\n    }\n\n    /// Set scan parameters\n    pub fn le_set_scan_parameters(\n        &self,\n        scan_type: u8,\n        scan_interval: u16,\n        scan_window: u16,\n        own_addr_type: OwnAddressType,\n        filter_policy: u8,\n    ) -> Result<(), BleError> {\n        unsafe {\n            let status = hci_le_set_scan_parameters(\n                scan_type,\n                scan_interval,\n                scan_window,\n                own_addr_type as u8,\n                filter_policy,\n            );\n            Self::check_status(status)\n        }\n    }\n\n    /// Enable or disable scanning\n    pub fn le_set_scan_enable(&self, enable: bool, filter_duplicates: bool) -> Result<(), BleError> {\n        unsafe {\n            let status = hci_le_set_scan_enable(if enable { 1 } else { 0 }, if filter_duplicates { 1 } else { 0 });\n            Self::check_status(status)\n        }\n    }\n\n    /// Read buffer size (v2 - includes ISO parameters)\n    pub fn le_read_buffer_size(&self) -> Result<(u16, u8, u16, u8), BleError> {\n        unsafe {\n            let mut acl_packet_length = 0u16;\n            let mut acl_num_packets = 0u8;\n            let mut iso_packet_length = 0u16;\n            let mut iso_num_packets = 0u8;\n            let status = hci_le_read_buffer_size_v2(\n                &mut acl_packet_length,\n                &mut acl_num_packets,\n                &mut iso_packet_length,\n                &mut iso_num_packets,\n            );\n            Self::check_status(status)?;\n            Ok((acl_packet_length, acl_num_packets, iso_packet_length, iso_num_packets))\n        }\n    }\n\n    /// Read local supported LE features (page 0)\n    pub fn le_read_local_supported_features(&self) -> Result<[u8; 8], BleError> {\n        unsafe {\n            let mut features = [0u8; 8];\n            let status = hci_le_read_local_supported_features_page_0(&mut features as *mut [u8; 8]);\n            Self::check_status(status)?;\n            Ok(features)\n        }\n    }\n\n    // ===== Connection Management Commands =====\n\n    /// Disconnect a connection\n    ///\n    /// # Parameters\n    ///\n    /// - `handle`: Connection handle\n    /// - `reason`: Disconnect reason (e.g., 0x13 = Remote User Terminated, 0x16 = Local Host Terminated)\n    pub fn disconnect(&self, handle: u16, reason: u8) -> Result<(), BleError> {\n        unsafe {\n            let status = hci_disconnect(handle, reason);\n            Self::check_status(status)\n        }\n    }\n\n    /// Read the current PHY for a connection\n    ///\n    /// # Returns\n    ///\n    /// Tuple of (tx_phy, rx_phy) where:\n    /// - 0x01: LE 1M PHY\n    /// - 0x02: LE 2M PHY\n    /// - 0x03: LE Coded PHY\n    pub fn le_read_phy(&self, handle: u16) -> Result<(u8, u8), BleError> {\n        unsafe {\n            let mut tx_phy = 0u8;\n            let mut rx_phy = 0u8;\n            let status = hci_le_read_phy(handle, &mut tx_phy, &mut rx_phy);\n            Self::check_status(status)?;\n            Ok((tx_phy, rx_phy))\n        }\n    }\n\n    /// Request a PHY change for a connection\n    ///\n    /// # Parameters\n    ///\n    /// - `handle`: Connection handle\n    /// - `all_phys`: Bit field: bit 0 = no TX PHY preference, bit 1 = no RX PHY preference\n    /// - `tx_phys`: Bitmask of preferred TX PHYs (0x01=1M, 0x02=2M, 0x04=Coded)\n    /// - `rx_phys`: Bitmask of preferred RX PHYs\n    /// - `phy_options`: Coded PHY options (0=no preference, 1=S=2, 2=S=8)\n    pub fn le_set_phy(\n        &self,\n        handle: u16,\n        all_phys: u8,\n        tx_phys: u8,\n        rx_phys: u8,\n        phy_options: u16,\n    ) -> Result<(), BleError> {\n        unsafe {\n            let status = hci_le_set_phy(handle, all_phys, tx_phys, rx_phys, phy_options);\n            Self::check_status(status)\n        }\n    }\n\n    /// Request connection parameter update\n    ///\n    /// # Parameters\n    ///\n    /// - `handle`: Connection handle\n    /// - `interval_min`: Minimum connection interval (units of 1.25ms, range: 6-3200)\n    /// - `interval_max`: Maximum connection interval (units of 1.25ms, range: 6-3200)\n    /// - `latency`: Slave latency (range: 0-499)\n    /// - `supervision_timeout`: Supervision timeout (units of 10ms, range: 10-3200)\n    /// - `ce_length_min`: Minimum connection event length (units of 0.625ms)\n    /// - `ce_length_max`: Maximum connection event length (units of 0.625ms)\n    pub fn le_connection_update(\n        &self,\n        handle: u16,\n        interval_min: u16,\n        interval_max: u16,\n        latency: u16,\n        supervision_timeout: u16,\n        ce_length_min: u16,\n        ce_length_max: u16,\n    ) -> Result<(), BleError> {\n        unsafe {\n            let status = hci_le_connection_update(\n                handle,\n                interval_min,\n                interval_max,\n                latency,\n                supervision_timeout,\n                ce_length_min,\n                ce_length_max,\n            );\n            Self::check_status(status)\n        }\n    }\n\n    /// Create a connection to a peripheral device (Central role)\n    ///\n    /// This initiates a connection to an advertising peripheral device.\n    /// The connection complete event will be received when connection is established.\n    ///\n    /// # Parameters\n    ///\n    /// - `scan_interval`: Scan interval (units of 0.625ms)\n    /// - `scan_window`: Scan window (units of 0.625ms)\n    /// - `use_filter_accept_list`: If true, connect to any device in filter accept list\n    /// - `peer_addr_type`: Peer address type (0=Public, 1=Random)\n    /// - `peer_addr`: Peer device address\n    /// - `own_addr_type`: Own address type\n    /// - `interval_min`: Minimum connection interval (units of 1.25ms)\n    /// - `interval_max`: Maximum connection interval (units of 1.25ms)\n    /// - `latency`: Maximum slave latency\n    /// - `supervision_timeout`: Supervision timeout (units of 10ms)\n    /// - `ce_length_min`: Minimum connection event length\n    /// - `ce_length_max`: Maximum connection event length\n    #[allow(clippy::too_many_arguments)]\n    pub fn le_create_connection(\n        &self,\n        scan_interval: u16,\n        scan_window: u16,\n        use_filter_accept_list: bool,\n        peer_addr_type: u8,\n        peer_addr: &[u8; 6],\n        own_addr_type: OwnAddressType,\n        interval_min: u16,\n        interval_max: u16,\n        latency: u16,\n        supervision_timeout: u16,\n        ce_length_min: u16,\n        ce_length_max: u16,\n    ) -> Result<(), BleError> {\n        unsafe {\n            let filter_policy = if use_filter_accept_list { 1 } else { 0 };\n            let status = hci_le_create_connection(\n                scan_interval,\n                scan_window,\n                filter_policy,\n                peer_addr_type,\n                peer_addr as *const [u8; 6],\n                own_addr_type as u8,\n                interval_min,\n                interval_max,\n                latency,\n                supervision_timeout,\n                ce_length_min,\n                ce_length_max,\n            );\n            Self::check_status(status)\n        }\n    }\n\n    /// Cancel an ongoing connection attempt\n    pub fn le_create_connection_cancel(&self) -> Result<(), BleError> {\n        unsafe {\n            let status = hci_le_create_connection_cancel();\n            Self::check_status(status)\n        }\n    }\n\n    /// Set data length for a connection\n    ///\n    /// Allows the Host to suggest maximum transmission packet size and maximum packet transmission time.\n    ///\n    /// # Parameters\n    ///\n    /// - `handle`: Connection handle\n    /// - `tx_octets`: Preferred maximum number of payload octets (27-251)\n    /// - `tx_time`: Preferred maximum number of microseconds for TX (328-17040)\n    pub fn le_set_data_length(&self, handle: u16, tx_octets: u16, tx_time: u16) -> Result<(), BleError> {\n        unsafe {\n            let status = hci_le_set_data_length(handle, tx_octets, tx_time);\n            Self::check_status(status)\n        }\n    }\n}\n\nimpl Default for CommandSender {\n    fn default() -> Self {\n        Self::new()\n    }\n}\n"
  },
  {
    "path": "embassy-stm32-wpan/src/wba/hci/event.rs",
    "content": "//! HCI Event parsing and handling\n\nuse embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;\nuse embassy_sync::channel::Channel;\n\nuse super::types::{Address, AddressType, Handle, Status};\n\n/// HCI Event Codes\n#[repr(u8)]\n#[derive(Debug, Clone, Copy, PartialEq, Eq)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub enum EventCode {\n    DisconnectionComplete = 0x05,\n    EncryptionChange = 0x08,\n    ReadRemoteVersionInformationComplete = 0x0C,\n    CommandComplete = 0x0E,\n    CommandStatus = 0x0F,\n    HardwareError = 0x10,\n    NumberOfCompletedPackets = 0x13,\n    DataBufferOverflow = 0x1A,\n    EncryptionKeyRefreshComplete = 0x30,\n    LeMetaEvent = 0x3E,\n    VendorSpecific = 0xFF,\n}\n\nimpl EventCode {\n    pub fn from_u8(value: u8) -> Option<Self> {\n        match value {\n            0x05 => Some(EventCode::DisconnectionComplete),\n            0x08 => Some(EventCode::EncryptionChange),\n            0x0C => Some(EventCode::ReadRemoteVersionInformationComplete),\n            0x0E => Some(EventCode::CommandComplete),\n            0x0F => Some(EventCode::CommandStatus),\n            0x10 => Some(EventCode::HardwareError),\n            0x13 => Some(EventCode::NumberOfCompletedPackets),\n            0x1A => Some(EventCode::DataBufferOverflow),\n            0x30 => Some(EventCode::EncryptionKeyRefreshComplete),\n            0x3E => Some(EventCode::LeMetaEvent),\n            0xFF => Some(EventCode::VendorSpecific),\n            _ => None,\n        }\n    }\n}\n\n/// LE Subevent Codes (for LE Meta Event)\n#[repr(u8)]\n#[derive(Debug, Clone, Copy, PartialEq, Eq)]\npub enum LeSubevent {\n    ConnectionComplete = 0x01,\n    AdvertisingReport = 0x02,\n    ConnectionUpdateComplete = 0x03,\n    ReadRemoteFeaturesComplete = 0x04,\n    LongTermKeyRequest = 0x05,\n    RemoteConnectionParameterRequest = 0x06,\n    DataLengthChange = 0x07,\n    ReadLocalP256PublicKeyComplete = 0x08,\n    GenerateDhKeyComplete = 0x09,\n    EnhancedConnectionComplete = 0x0A,\n    DirectedAdvertisingReport = 0x0B,\n    PhyUpdateComplete = 0x0C,\n}\n\nimpl LeSubevent {\n    pub fn from_u8(value: u8) -> Option<Self> {\n        match value {\n            0x01 => Some(LeSubevent::ConnectionComplete),\n            0x02 => Some(LeSubevent::AdvertisingReport),\n            0x03 => Some(LeSubevent::ConnectionUpdateComplete),\n            0x04 => Some(LeSubevent::ReadRemoteFeaturesComplete),\n            0x05 => Some(LeSubevent::LongTermKeyRequest),\n            0x06 => Some(LeSubevent::RemoteConnectionParameterRequest),\n            0x07 => Some(LeSubevent::DataLengthChange),\n            0x08 => Some(LeSubevent::ReadLocalP256PublicKeyComplete),\n            0x09 => Some(LeSubevent::GenerateDhKeyComplete),\n            0x0A => Some(LeSubevent::EnhancedConnectionComplete),\n            0x0B => Some(LeSubevent::DirectedAdvertisingReport),\n            0x0C => Some(LeSubevent::PhyUpdateComplete),\n            _ => None,\n        }\n    }\n}\n\n/// Parsed HCI Event\n#[derive(Debug, Clone)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub struct Event {\n    pub code: EventCode,\n    pub params: EventParams,\n}\n\n/// Event Parameters\n#[derive(Debug, Clone)]\npub enum EventParams {\n    /// Command Complete event\n    CommandComplete {\n        num_hci_command_packets: u8,\n        opcode: u16,\n        status: Status,\n        return_params: heapless::Vec<u8, 255>,\n    },\n\n    /// Command Status event\n    CommandStatus {\n        status: Status,\n        num_hci_command_packets: u8,\n        opcode: u16,\n    },\n\n    /// Disconnection Complete event\n    DisconnectionComplete { status: Status, handle: Handle, reason: u8 },\n\n    /// Encryption Change event\n    EncryptionChange {\n        status: Status,\n        handle: Handle,\n        enabled: bool,\n    },\n\n    /// LE Connection Complete event\n    LeConnectionComplete {\n        status: Status,\n        handle: Handle,\n        role: u8,\n        peer_address_type: AddressType,\n        peer_address: Address,\n        conn_interval: u16,\n        conn_latency: u16,\n        supervision_timeout: u16,\n        master_clock_accuracy: u8,\n    },\n\n    /// LE Enhanced Connection Complete event (includes RPA)\n    LeEnhancedConnectionComplete {\n        status: Status,\n        handle: Handle,\n        role: u8,\n        peer_address_type: AddressType,\n        peer_address: Address,\n        local_resolvable_private_address: Address,\n        peer_resolvable_private_address: Address,\n        conn_interval: u16,\n        conn_latency: u16,\n        supervision_timeout: u16,\n        master_clock_accuracy: u8,\n    },\n\n    /// LE Advertising Report event\n    LeAdvertisingReport {\n        reports: heapless::Vec<AdvertisingReport, 10>,\n    },\n\n    /// LE Connection Update Complete event\n    LeConnectionUpdateComplete {\n        status: Status,\n        handle: Handle,\n        conn_interval: u16,\n        conn_latency: u16,\n        supervision_timeout: u16,\n    },\n\n    /// LE PHY Update Complete event\n    LePhyUpdateComplete {\n        status: Status,\n        handle: Handle,\n        tx_phy: u8,\n        rx_phy: u8,\n    },\n\n    /// LE Data Length Change event\n    LeDataLengthChange {\n        handle: Handle,\n        max_tx_octets: u16,\n        max_tx_time: u16,\n        max_rx_octets: u16,\n        max_rx_time: u16,\n    },\n\n    /// Hardware Error event\n    HardwareError { hardware_code: u8 },\n\n    /// Number of Completed Packets event\n    NumberOfCompletedPackets { handles: heapless::Vec<(Handle, u16), 8> },\n\n    /// Vendor Specific event (unparsed)\n    VendorSpecific { data: heapless::Vec<u8, 255> },\n\n    // ===== ACI GATT Events (parsed from Vendor Specific) =====\n    /// GATT Attribute Modified event\n    GattAttributeModified {\n        conn_handle: Handle,\n        attr_handle: u16,\n        offset: u16,\n        data: heapless::Vec<u8, 247>,\n    },\n\n    /// GATT Notification Complete event\n    GattNotificationComplete { conn_handle: Handle, attr_handle: u16 },\n\n    /// GATT Indication Complete event\n    GattIndicationComplete { conn_handle: Handle, attr_handle: u16 },\n\n    /// ATT Exchange MTU Response event\n    AttExchangeMtuResponse { conn_handle: Handle, server_mtu: u16 },\n\n    /// GATT Procedure Complete event\n    GattProcedureComplete { conn_handle: Handle, error_code: u8 },\n\n    /// GATT Procedure Timeout event\n    GattProcedureTimeout { conn_handle: Handle },\n\n    /// GATT TX Pool Available event\n    GattTxPoolAvailable {\n        conn_handle: Handle,\n        available_buffers: u16,\n    },\n\n    // ===== ACI GAP Security Events =====\n    /// GAP Pairing Complete event\n    GapPairingComplete {\n        conn_handle: Handle,\n        status: u8,\n        reason: u8,\n    },\n\n    /// GAP Passkey Request event\n    GapPasskeyRequest { conn_handle: Handle },\n\n    /// GAP Numeric Comparison Value event\n    GapNumericComparisonRequest { conn_handle: Handle, numeric_value: u32 },\n\n    /// GAP Bond Lost event\n    GapBondLost { conn_handle: Handle },\n\n    /// GAP Pairing Request event\n    GapPairingRequest { conn_handle: Handle, is_bonded: bool },\n\n    /// Unknown/Unparsed event\n    Unknown { data: heapless::Vec<u8, 255> },\n}\n\n/// Advertising Report (part of LE Advertising Report event)\n#[derive(Debug, Clone)]\npub struct AdvertisingReport {\n    pub event_type: u8,\n    pub address_type: AddressType,\n    pub address: Address,\n    pub data: heapless::Vec<u8, 31>,\n    pub rssi: i8,\n}\n\n#[cfg(feature = \"defmt\")]\nimpl defmt::Format for AdvertisingReport {\n    fn format(&self, f: defmt::Formatter) {\n        defmt::write!(\n            f,\n            \"AdvertisingReport {{ event_type: {}, address_type: {}, address: {}, data: [..{}], rssi: {} }}\",\n            self.event_type,\n            self.address_type,\n            self.address,\n            self.data.len(),\n            self.rssi\n        )\n    }\n}\n\n#[cfg(feature = \"defmt\")]\nimpl defmt::Format for EventParams {\n    fn format(&self, f: defmt::Formatter) {\n        match self {\n            EventParams::CommandComplete {\n                num_hci_command_packets,\n                opcode,\n                status,\n                ..\n            } => {\n                defmt::write!(\n                    f,\n                    \"CommandComplete {{ opcode: 0x{:04X}, status: {}, num_packets: {} }}\",\n                    opcode,\n                    status,\n                    num_hci_command_packets\n                )\n            }\n            EventParams::CommandStatus { status, opcode, .. } => {\n                defmt::write!(f, \"CommandStatus {{ opcode: 0x{:04X}, status: {} }}\", opcode, status)\n            }\n            EventParams::LeConnectionComplete { status, handle, .. } => {\n                defmt::write!(f, \"LeConnectionComplete {{ handle: {}, status: {} }}\", handle, status)\n            }\n            EventParams::LeEnhancedConnectionComplete {\n                status, handle, role, ..\n            } => {\n                defmt::write!(\n                    f,\n                    \"LeEnhancedConnectionComplete {{ handle: {}, status: {}, role: {} }}\",\n                    handle,\n                    status,\n                    role\n                )\n            }\n            EventParams::DisconnectionComplete { status, handle, reason } => {\n                defmt::write!(\n                    f,\n                    \"DisconnectionComplete {{ handle: {}, status: {}, reason: {} }}\",\n                    handle,\n                    status,\n                    reason\n                )\n            }\n            EventParams::EncryptionChange {\n                status,\n                handle,\n                enabled,\n            } => {\n                defmt::write!(\n                    f,\n                    \"EncryptionChange {{ handle: {}, status: {}, enabled: {} }}\",\n                    handle,\n                    status,\n                    enabled\n                )\n            }\n            EventParams::LeAdvertisingReport { reports } => {\n                defmt::write!(f, \"LeAdvertisingReport {{ num_reports: {} }}\", reports.len())\n            }\n            EventParams::LeConnectionUpdateComplete { status, handle, .. } => {\n                defmt::write!(\n                    f,\n                    \"LeConnectionUpdateComplete {{ handle: {}, status: {} }}\",\n                    handle,\n                    status\n                )\n            }\n            EventParams::LePhyUpdateComplete {\n                status,\n                handle,\n                tx_phy,\n                rx_phy,\n            } => {\n                defmt::write!(\n                    f,\n                    \"LePhyUpdateComplete {{ handle: {}, status: {}, tx: {}, rx: {} }}\",\n                    handle,\n                    status,\n                    tx_phy,\n                    rx_phy\n                )\n            }\n            EventParams::LeDataLengthChange {\n                handle,\n                max_tx_octets,\n                max_rx_octets,\n                ..\n            } => {\n                defmt::write!(\n                    f,\n                    \"LeDataLengthChange {{ handle: {}, tx: {}, rx: {} }}\",\n                    handle,\n                    max_tx_octets,\n                    max_rx_octets\n                )\n            }\n            EventParams::HardwareError { hardware_code } => {\n                defmt::write!(f, \"HardwareError {{ code: {} }}\", hardware_code)\n            }\n            EventParams::NumberOfCompletedPackets { handles } => {\n                defmt::write!(f, \"NumberOfCompletedPackets {{ count: {} }}\", handles.len())\n            }\n            EventParams::VendorSpecific { data } => {\n                defmt::write!(f, \"VendorSpecific {{ len: {} }}\", data.len())\n            }\n            EventParams::GattAttributeModified {\n                conn_handle,\n                attr_handle,\n                offset,\n                data,\n            } => {\n                defmt::write!(\n                    f,\n                    \"GattAttributeModified {{ conn: {}, attr: 0x{:04X}, offset: {}, len: {} }}\",\n                    conn_handle,\n                    attr_handle,\n                    offset,\n                    data.len()\n                )\n            }\n            EventParams::GattNotificationComplete {\n                conn_handle,\n                attr_handle,\n            } => {\n                defmt::write!(\n                    f,\n                    \"GattNotificationComplete {{ conn: {}, attr: 0x{:04X} }}\",\n                    conn_handle,\n                    attr_handle\n                )\n            }\n            EventParams::GattIndicationComplete {\n                conn_handle,\n                attr_handle,\n            } => {\n                defmt::write!(\n                    f,\n                    \"GattIndicationComplete {{ conn: {}, attr: 0x{:04X} }}\",\n                    conn_handle,\n                    attr_handle\n                )\n            }\n            EventParams::AttExchangeMtuResponse {\n                conn_handle,\n                server_mtu,\n            } => {\n                defmt::write!(\n                    f,\n                    \"AttExchangeMtuResponse {{ conn: {}, mtu: {} }}\",\n                    conn_handle,\n                    server_mtu\n                )\n            }\n            EventParams::GattProcedureComplete {\n                conn_handle,\n                error_code,\n            } => {\n                defmt::write!(\n                    f,\n                    \"GattProcedureComplete {{ conn: {}, error: 0x{:02X} }}\",\n                    conn_handle,\n                    error_code\n                )\n            }\n            EventParams::GattProcedureTimeout { conn_handle } => {\n                defmt::write!(f, \"GattProcedureTimeout {{ conn: {} }}\", conn_handle)\n            }\n            EventParams::GattTxPoolAvailable {\n                conn_handle,\n                available_buffers,\n            } => {\n                defmt::write!(\n                    f,\n                    \"GattTxPoolAvailable {{ conn: {}, buffers: {} }}\",\n                    conn_handle,\n                    available_buffers\n                )\n            }\n            EventParams::GapPairingComplete {\n                conn_handle,\n                status,\n                reason,\n            } => {\n                defmt::write!(\n                    f,\n                    \"GapPairingComplete {{ conn: {}, status: {}, reason: 0x{:02X} }}\",\n                    conn_handle,\n                    status,\n                    reason\n                )\n            }\n            EventParams::GapPasskeyRequest { conn_handle } => {\n                defmt::write!(f, \"GapPasskeyRequest {{ conn: {} }}\", conn_handle)\n            }\n            EventParams::GapNumericComparisonRequest {\n                conn_handle,\n                numeric_value,\n            } => {\n                defmt::write!(\n                    f,\n                    \"GapNumericComparisonRequest {{ conn: {}, value: {} }}\",\n                    conn_handle,\n                    numeric_value\n                )\n            }\n            EventParams::GapBondLost { conn_handle } => {\n                defmt::write!(f, \"GapBondLost {{ conn: {} }}\", conn_handle)\n            }\n            EventParams::GapPairingRequest { conn_handle, is_bonded } => {\n                defmt::write!(\n                    f,\n                    \"GapPairingRequest {{ conn: {}, bonded: {} }}\",\n                    conn_handle,\n                    is_bonded\n                )\n            }\n            EventParams::Unknown { data } => {\n                defmt::write!(f, \"Unknown {{ len: {} }}\", data.len())\n            }\n        }\n    }\n}\n\nimpl Event {\n    /// Parse an HCI event from raw bytes\n    ///\n    /// Event packet format:\n    /// - Byte 0: Event code\n    /// - Byte 1: Parameter length\n    /// - Bytes 2+: Parameters\n    pub fn parse(data: &[u8]) -> Option<Self> {\n        if data.len() < 2 {\n            return None;\n        }\n\n        let code = EventCode::from_u8(data[0])?;\n        let param_len = data[1] as usize;\n\n        if data.len() < 2 + param_len {\n            return None;\n        }\n\n        let params = &data[2..2 + param_len];\n\n        let event_params = match code {\n            EventCode::CommandComplete => Self::parse_command_complete(params)?,\n            EventCode::CommandStatus => Self::parse_command_status(params)?,\n            EventCode::DisconnectionComplete => Self::parse_disconnection_complete(params)?,\n            EventCode::EncryptionChange => Self::parse_encryption_change(params)?,\n            EventCode::LeMetaEvent => Self::parse_le_meta_event(params)?,\n            EventCode::HardwareError => Self::parse_hardware_error(params)?,\n            EventCode::NumberOfCompletedPackets => Self::parse_number_of_completed_packets(params)?,\n            EventCode::VendorSpecific => Self::parse_vendor_specific(params)?,\n            _ => EventParams::Unknown {\n                data: heapless::Vec::from_slice(params).ok()?,\n            },\n        };\n\n        Some(Event {\n            code,\n            params: event_params,\n        })\n    }\n\n    fn parse_command_complete(params: &[u8]) -> Option<EventParams> {\n        if params.len() < 3 {\n            return None;\n        }\n\n        let num_hci_command_packets = params[0];\n        let opcode = u16::from_le_bytes([params[1], params[2]]);\n        let status = if params.len() > 3 {\n            Status::from_u8(params[3])\n        } else {\n            Status::Success\n        };\n\n        let return_params = if params.len() > 4 {\n            heapless::Vec::from_slice(&params[4..]).ok()?\n        } else {\n            heapless::Vec::new()\n        };\n\n        Some(EventParams::CommandComplete {\n            num_hci_command_packets,\n            opcode,\n            status,\n            return_params,\n        })\n    }\n\n    fn parse_command_status(params: &[u8]) -> Option<EventParams> {\n        if params.len() < 4 {\n            return None;\n        }\n\n        let status = Status::from_u8(params[0]);\n        let num_hci_command_packets = params[1];\n        let opcode = u16::from_le_bytes([params[2], params[3]]);\n\n        Some(EventParams::CommandStatus {\n            status,\n            num_hci_command_packets,\n            opcode,\n        })\n    }\n\n    fn parse_disconnection_complete(params: &[u8]) -> Option<EventParams> {\n        if params.len() < 4 {\n            return None;\n        }\n\n        let status = Status::from_u8(params[0]);\n        let handle = Handle::new(u16::from_le_bytes([params[1], params[2]]));\n        let reason = params[3];\n\n        Some(EventParams::DisconnectionComplete { status, handle, reason })\n    }\n\n    fn parse_encryption_change(params: &[u8]) -> Option<EventParams> {\n        // Format: status(1) + handle(2) + enabled(1)\n        if params.len() < 4 {\n            return None;\n        }\n\n        let status = Status::from_u8(params[0]);\n        let handle = Handle::new(u16::from_le_bytes([params[1], params[2]]));\n        let enabled = params[3] != 0;\n\n        Some(EventParams::EncryptionChange {\n            status,\n            handle,\n            enabled,\n        })\n    }\n\n    fn parse_le_meta_event(params: &[u8]) -> Option<EventParams> {\n        if params.is_empty() {\n            return None;\n        }\n\n        let subevent = LeSubevent::from_u8(params[0])?;\n        let subevent_params = &params[1..];\n\n        match subevent {\n            LeSubevent::ConnectionComplete => Self::parse_le_connection_complete(subevent_params),\n            LeSubevent::EnhancedConnectionComplete => Self::parse_le_enhanced_connection_complete(subevent_params),\n            LeSubevent::AdvertisingReport => Self::parse_le_advertising_report(subevent_params),\n            LeSubevent::ConnectionUpdateComplete => Self::parse_le_connection_update_complete(subevent_params),\n            LeSubevent::PhyUpdateComplete => Self::parse_le_phy_update_complete(subevent_params),\n            LeSubevent::DataLengthChange => Self::parse_le_data_length_change(subevent_params),\n            _ => Some(EventParams::Unknown {\n                data: heapless::Vec::from_slice(params).ok()?,\n            }),\n        }\n    }\n\n    fn parse_le_connection_complete(params: &[u8]) -> Option<EventParams> {\n        if params.len() < 18 {\n            return None;\n        }\n\n        let status = Status::from_u8(params[0]);\n        let handle = Handle::new(u16::from_le_bytes([params[1], params[2]]));\n        let role = params[3];\n        let peer_address_type = match params[4] {\n            0 => AddressType::Public,\n            1 => AddressType::Random,\n            2 => AddressType::PublicIdentity,\n            3 => AddressType::RandomIdentity,\n            _ => return None,\n        };\n\n        let mut peer_address = [0u8; 6];\n        peer_address.copy_from_slice(&params[5..11]);\n\n        let conn_interval = u16::from_le_bytes([params[11], params[12]]);\n        let conn_latency = u16::from_le_bytes([params[13], params[14]]);\n        let supervision_timeout = u16::from_le_bytes([params[15], params[16]]);\n        let master_clock_accuracy = params[17];\n\n        Some(EventParams::LeConnectionComplete {\n            status,\n            handle,\n            role,\n            peer_address_type,\n            peer_address: Address::new(peer_address),\n            conn_interval,\n            conn_latency,\n            supervision_timeout,\n            master_clock_accuracy,\n        })\n    }\n\n    fn parse_le_advertising_report(params: &[u8]) -> Option<EventParams> {\n        if params.is_empty() {\n            return None;\n        }\n\n        let num_reports = params[0] as usize;\n        let mut reports = heapless::Vec::new();\n        let mut offset = 1;\n\n        for _ in 0..num_reports {\n            if offset + 9 > params.len() {\n                break;\n            }\n\n            let event_type = params[offset];\n            let address_type = match params[offset + 1] {\n                0 => AddressType::Public,\n                1 => AddressType::Random,\n                2 => AddressType::PublicIdentity,\n                3 => AddressType::RandomIdentity,\n                _ => return None,\n            };\n\n            let mut address = [0u8; 6];\n            address.copy_from_slice(&params[offset + 2..offset + 8]);\n\n            let data_len = params[offset + 8] as usize;\n            offset += 9;\n\n            if offset + data_len + 1 > params.len() {\n                break;\n            }\n\n            let data = heapless::Vec::from_slice(&params[offset..offset + data_len]).ok()?;\n            offset += data_len;\n\n            let rssi = params[offset] as i8;\n            offset += 1;\n\n            let _ = reports.push(AdvertisingReport {\n                event_type,\n                address_type,\n                address: Address::new(address),\n                data,\n                rssi,\n            });\n        }\n\n        Some(EventParams::LeAdvertisingReport { reports })\n    }\n\n    fn parse_le_connection_update_complete(params: &[u8]) -> Option<EventParams> {\n        if params.len() < 9 {\n            return None;\n        }\n\n        let status = Status::from_u8(params[0]);\n        let handle = Handle::new(u16::from_le_bytes([params[1], params[2]]));\n        let conn_interval = u16::from_le_bytes([params[3], params[4]]);\n        let conn_latency = u16::from_le_bytes([params[5], params[6]]);\n        let supervision_timeout = u16::from_le_bytes([params[7], params[8]]);\n\n        Some(EventParams::LeConnectionUpdateComplete {\n            status,\n            handle,\n            conn_interval,\n            conn_latency,\n            supervision_timeout,\n        })\n    }\n\n    fn parse_le_enhanced_connection_complete(params: &[u8]) -> Option<EventParams> {\n        // Enhanced Connection Complete has 30 bytes of parameters\n        if params.len() < 30 {\n            return None;\n        }\n\n        let status = Status::from_u8(params[0]);\n        let handle = Handle::new(u16::from_le_bytes([params[1], params[2]]));\n        let role = params[3];\n        let peer_address_type = match params[4] {\n            0 => AddressType::Public,\n            1 => AddressType::Random,\n            2 => AddressType::PublicIdentity,\n            3 => AddressType::RandomIdentity,\n            _ => return None,\n        };\n\n        let mut peer_address = [0u8; 6];\n        peer_address.copy_from_slice(&params[5..11]);\n\n        let mut local_rpa = [0u8; 6];\n        local_rpa.copy_from_slice(&params[11..17]);\n\n        let mut peer_rpa = [0u8; 6];\n        peer_rpa.copy_from_slice(&params[17..23]);\n\n        let conn_interval = u16::from_le_bytes([params[23], params[24]]);\n        let conn_latency = u16::from_le_bytes([params[25], params[26]]);\n        let supervision_timeout = u16::from_le_bytes([params[27], params[28]]);\n        let master_clock_accuracy = params[29];\n\n        Some(EventParams::LeEnhancedConnectionComplete {\n            status,\n            handle,\n            role,\n            peer_address_type,\n            peer_address: Address::new(peer_address),\n            local_resolvable_private_address: Address::new(local_rpa),\n            peer_resolvable_private_address: Address::new(peer_rpa),\n            conn_interval,\n            conn_latency,\n            supervision_timeout,\n            master_clock_accuracy,\n        })\n    }\n\n    fn parse_le_phy_update_complete(params: &[u8]) -> Option<EventParams> {\n        if params.len() < 5 {\n            return None;\n        }\n\n        let status = Status::from_u8(params[0]);\n        let handle = Handle::new(u16::from_le_bytes([params[1], params[2]]));\n        let tx_phy = params[3];\n        let rx_phy = params[4];\n\n        Some(EventParams::LePhyUpdateComplete {\n            status,\n            handle,\n            tx_phy,\n            rx_phy,\n        })\n    }\n\n    fn parse_le_data_length_change(params: &[u8]) -> Option<EventParams> {\n        if params.len() < 10 {\n            return None;\n        }\n\n        let handle = Handle::new(u16::from_le_bytes([params[0], params[1]]));\n        let max_tx_octets = u16::from_le_bytes([params[2], params[3]]);\n        let max_tx_time = u16::from_le_bytes([params[4], params[5]]);\n        let max_rx_octets = u16::from_le_bytes([params[6], params[7]]);\n        let max_rx_time = u16::from_le_bytes([params[8], params[9]]);\n\n        Some(EventParams::LeDataLengthChange {\n            handle,\n            max_tx_octets,\n            max_tx_time,\n            max_rx_octets,\n            max_rx_time,\n        })\n    }\n\n    fn parse_vendor_specific(params: &[u8]) -> Option<EventParams> {\n        // Vendor specific events have format:\n        // - Bytes 0-1: Event code (little-endian)\n        // - Bytes 2+: Event-specific data\n        if params.len() < 2 {\n            return Some(EventParams::VendorSpecific {\n                data: heapless::Vec::from_slice(params).ok()?,\n            });\n        }\n\n        let ecode = u16::from_le_bytes([params[0], params[1]]);\n        let event_data = &params[2..];\n\n        // ACI GATT event codes\n        const ACI_GATT_ATTRIBUTE_MODIFIED: u16 = 0x0C01;\n        const ACI_GATT_PROC_COMPLETE: u16 = 0x0C02;\n        const ACI_GATT_NOTIFICATION_COMPLETE: u16 = 0x0C03;\n        const ACI_GATT_INDICATION_COMPLETE: u16 = 0x0C04;\n        const ACI_ATT_EXCHANGE_MTU_RESP: u16 = 0x0802;\n        const ACI_GATT_PROC_TIMEOUT: u16 = 0x0C05;\n        const ACI_GATT_TX_POOL_AVAILABLE: u16 = 0x0C08;\n\n        // ACI GAP security event codes\n        const ACI_GAP_PAIRING_COMPLETE: u16 = 0x0401;\n        const ACI_GAP_PASS_KEY_REQ: u16 = 0x0402;\n        const ACI_GAP_BOND_LOST: u16 = 0x0405;\n        const ACI_GAP_NUMERIC_COMPARISON_VALUE: u16 = 0x0409;\n        const ACI_GAP_PAIRING_REQUEST: u16 = 0x040B;\n\n        match ecode {\n            ACI_GATT_ATTRIBUTE_MODIFIED => Self::parse_gatt_attribute_modified(event_data),\n            ACI_GATT_PROC_COMPLETE => Self::parse_gatt_proc_complete(event_data),\n            ACI_GATT_NOTIFICATION_COMPLETE => Self::parse_gatt_notification_complete(event_data),\n            ACI_GATT_INDICATION_COMPLETE => Self::parse_gatt_indication_complete(event_data),\n            ACI_ATT_EXCHANGE_MTU_RESP => Self::parse_att_exchange_mtu_resp(event_data),\n            ACI_GATT_PROC_TIMEOUT => Self::parse_gatt_proc_timeout(event_data),\n            ACI_GATT_TX_POOL_AVAILABLE => Self::parse_gatt_tx_pool_available(event_data),\n            ACI_GAP_PAIRING_COMPLETE => Self::parse_gap_pairing_complete(event_data),\n            ACI_GAP_PASS_KEY_REQ => Self::parse_gap_passkey_request(event_data),\n            ACI_GAP_BOND_LOST => Self::parse_gap_bond_lost(event_data),\n            ACI_GAP_NUMERIC_COMPARISON_VALUE => Self::parse_gap_numeric_comparison(event_data),\n            ACI_GAP_PAIRING_REQUEST => Self::parse_gap_pairing_request(event_data),\n            _ => Some(EventParams::VendorSpecific {\n                data: heapless::Vec::from_slice(params).ok()?,\n            }),\n        }\n    }\n\n    fn parse_gatt_attribute_modified(params: &[u8]) -> Option<EventParams> {\n        // Format: conn_handle(2) + attr_handle(2) + offset(2) + data_length(2) + data(N)\n        if params.len() < 8 {\n            return None;\n        }\n\n        let conn_handle = Handle::new(u16::from_le_bytes([params[0], params[1]]));\n        let attr_handle = u16::from_le_bytes([params[2], params[3]]);\n        let offset = u16::from_le_bytes([params[4], params[5]]);\n        let data_length = u16::from_le_bytes([params[6], params[7]]) as usize;\n\n        // Note: Bit 15 of offset may indicate \"more data pending\" - mask it off\n        let actual_offset = offset & 0x7FFF;\n\n        let data = if params.len() >= 8 + data_length {\n            heapless::Vec::from_slice(&params[8..8 + data_length]).ok()?\n        } else {\n            heapless::Vec::from_slice(&params[8..]).ok()?\n        };\n\n        Some(EventParams::GattAttributeModified {\n            conn_handle,\n            attr_handle,\n            offset: actual_offset,\n            data,\n        })\n    }\n\n    fn parse_gatt_proc_complete(params: &[u8]) -> Option<EventParams> {\n        // Format: conn_handle(2) + error_code(1)\n        if params.len() < 3 {\n            return None;\n        }\n\n        let conn_handle = Handle::new(u16::from_le_bytes([params[0], params[1]]));\n        let error_code = params[2];\n\n        Some(EventParams::GattProcedureComplete {\n            conn_handle,\n            error_code,\n        })\n    }\n\n    fn parse_gatt_notification_complete(params: &[u8]) -> Option<EventParams> {\n        // Format: conn_handle(2) + attr_handle(2)\n        if params.len() < 4 {\n            return None;\n        }\n\n        let conn_handle = Handle::new(u16::from_le_bytes([params[0], params[1]]));\n        let attr_handle = u16::from_le_bytes([params[2], params[3]]);\n\n        Some(EventParams::GattNotificationComplete {\n            conn_handle,\n            attr_handle,\n        })\n    }\n\n    fn parse_gatt_indication_complete(params: &[u8]) -> Option<EventParams> {\n        // Format: conn_handle(2) + attr_handle(2)\n        if params.len() < 4 {\n            return None;\n        }\n\n        let conn_handle = Handle::new(u16::from_le_bytes([params[0], params[1]]));\n        let attr_handle = u16::from_le_bytes([params[2], params[3]]);\n\n        Some(EventParams::GattIndicationComplete {\n            conn_handle,\n            attr_handle,\n        })\n    }\n\n    fn parse_att_exchange_mtu_resp(params: &[u8]) -> Option<EventParams> {\n        // Format: conn_handle(2) + server_mtu(2)\n        if params.len() < 4 {\n            return None;\n        }\n\n        let conn_handle = Handle::new(u16::from_le_bytes([params[0], params[1]]));\n        let server_mtu = u16::from_le_bytes([params[2], params[3]]);\n\n        Some(EventParams::AttExchangeMtuResponse {\n            conn_handle,\n            server_mtu,\n        })\n    }\n\n    fn parse_gatt_proc_timeout(params: &[u8]) -> Option<EventParams> {\n        // Format: conn_handle(2)\n        if params.len() < 2 {\n            return None;\n        }\n\n        let conn_handle = Handle::new(u16::from_le_bytes([params[0], params[1]]));\n\n        Some(EventParams::GattProcedureTimeout { conn_handle })\n    }\n\n    fn parse_gatt_tx_pool_available(params: &[u8]) -> Option<EventParams> {\n        // Format: conn_handle(2) + available_buffers(2)\n        if params.len() < 4 {\n            return None;\n        }\n\n        let conn_handle = Handle::new(u16::from_le_bytes([params[0], params[1]]));\n        let available_buffers = u16::from_le_bytes([params[2], params[3]]);\n\n        Some(EventParams::GattTxPoolAvailable {\n            conn_handle,\n            available_buffers,\n        })\n    }\n\n    fn parse_hardware_error(params: &[u8]) -> Option<EventParams> {\n        if params.is_empty() {\n            return None;\n        }\n\n        Some(EventParams::HardwareError {\n            hardware_code: params[0],\n        })\n    }\n\n    fn parse_number_of_completed_packets(params: &[u8]) -> Option<EventParams> {\n        if params.is_empty() {\n            return None;\n        }\n\n        let num_handles = params[0] as usize;\n        let mut handles = heapless::Vec::new();\n\n        for i in 0..num_handles {\n            let offset = 1 + i * 4;\n            if offset + 4 > params.len() {\n                break;\n            }\n\n            let handle = Handle::new(u16::from_le_bytes([params[offset], params[offset + 1]]));\n            let num_completed = u16::from_le_bytes([params[offset + 2], params[offset + 3]]);\n\n            let _ = handles.push((handle, num_completed));\n        }\n\n        Some(EventParams::NumberOfCompletedPackets { handles })\n    }\n\n    // ===== Security Event Parsing =====\n\n    fn parse_gap_pairing_complete(params: &[u8]) -> Option<EventParams> {\n        // Format: conn_handle(2) + status(1) + reason(1)\n        if params.len() < 4 {\n            return None;\n        }\n\n        let conn_handle = Handle::new(u16::from_le_bytes([params[0], params[1]]));\n        let status = params[2];\n        let reason = params[3];\n\n        Some(EventParams::GapPairingComplete {\n            conn_handle,\n            status,\n            reason,\n        })\n    }\n\n    fn parse_gap_passkey_request(params: &[u8]) -> Option<EventParams> {\n        // Format: conn_handle(2)\n        if params.len() < 2 {\n            return None;\n        }\n\n        let conn_handle = Handle::new(u16::from_le_bytes([params[0], params[1]]));\n\n        Some(EventParams::GapPasskeyRequest { conn_handle })\n    }\n\n    fn parse_gap_bond_lost(params: &[u8]) -> Option<EventParams> {\n        // Format: conn_handle(2)\n        if params.len() < 2 {\n            return None;\n        }\n\n        let conn_handle = Handle::new(u16::from_le_bytes([params[0], params[1]]));\n\n        Some(EventParams::GapBondLost { conn_handle })\n    }\n\n    fn parse_gap_numeric_comparison(params: &[u8]) -> Option<EventParams> {\n        // Format: conn_handle(2) + numeric_value(4)\n        if params.len() < 6 {\n            return None;\n        }\n\n        let conn_handle = Handle::new(u16::from_le_bytes([params[0], params[1]]));\n        let numeric_value = u32::from_le_bytes([params[2], params[3], params[4], params[5]]);\n\n        Some(EventParams::GapNumericComparisonRequest {\n            conn_handle,\n            numeric_value,\n        })\n    }\n\n    fn parse_gap_pairing_request(params: &[u8]) -> Option<EventParams> {\n        // Format: conn_handle(2) + bonded(1) + ...\n        if params.len() < 3 {\n            return None;\n        }\n\n        let conn_handle = Handle::new(u16::from_le_bytes([params[0], params[1]]));\n        let is_bonded = params[2] != 0;\n\n        Some(EventParams::GapPairingRequest { conn_handle, is_bonded })\n    }\n}\n\n/// Global event channel for passing events from C callback to Rust async code\n/// Size of 8 events should be sufficient for most cases\npub(crate) static EVENT_CHANNEL: Channel<CriticalSectionRawMutex, Event, 8> = Channel::new();\n\n/// Receive the next HCI event (async)\npub async fn read_event() -> Event {\n    EVENT_CHANNEL.receive().await\n}\n\n/// Try to send an event to the channel (non-blocking, for use in C callbacks)\npub(crate) fn try_send_event(event: Event) -> Result<(), ()> {\n    EVENT_CHANNEL.try_send(event).map_err(|_| ())\n}\n"
  },
  {
    "path": "embassy-stm32-wpan/src/wba/hci/host_if.rs",
    "content": "//! HCI Host Stack Interface\n//!\n//! This module provides the interface between the C link layer and the Rust HCI event handling.\n//! The C layer calls into Rust via the `HostStack_Process` function to deliver HCI events.\n\nuse core::ptr;\n\nuse super::event::{Event, try_send_event};\nuse crate::bindings::link_layer::ble_buff_hdr_t;\n\n/// Static buffer for receiving HCI event packets from C layer\n/// Maximum HCI event packet size is 257 bytes (1 byte event code + 1 byte length + up to 255 bytes data)\nstatic mut HCI_EVENT_BUFFER: [u8; 260] = [0u8; 260];\nstatic mut HCI_EVENT_LEN: u16 = 0;\n\n/// Flag to track if we have a pending event to process\nstatic mut HAS_PENDING_EVENT: bool = false;\n\n/// Initialize the HCI host interface\n/// This should be called during BLE stack initialization\npub unsafe fn init() {\n    // The host callback will be registered in ll_sys_ble_cntrl_init\n    // Here we just ensure our buffers are ready\n    HAS_PENDING_EVENT = false;\n    HCI_EVENT_LEN = 0;\n}\n\n/// Host callback function called by the C link layer when an HCI event is available\n///\n/// This function is called from C code via the hst_cbk callback registered in ll_sys_ble_cntrl_init.\n/// The ptr_evnt_hdr points to a ble_buff_hdr_t structure containing:\n/// - buff_start: Pointer to the data buffer\n/// - data_offset: Offset from buff_start to actual data\n/// - data_size: Size of the data\n///\n/// Returns 0 on success, non-zero on failure.\n#[unsafe(no_mangle)]\npub unsafe extern \"C\" fn hci_host_callback(ptr_evnt_hdr: *mut ble_buff_hdr_t) -> u8 {\n    if ptr_evnt_hdr.is_null() {\n        return 1;\n    }\n\n    let hdr = &*ptr_evnt_hdr;\n    let data_ptr = hdr.buff_start.add(hdr.data_offset as usize);\n    let data_len = hdr.data_size;\n\n    if data_ptr.is_null() || data_len == 0 || data_len > 260 {\n        return 1;\n    }\n\n    // Copy event data to our buffer\n    ptr::copy_nonoverlapping(data_ptr, HCI_EVENT_BUFFER.as_mut_ptr(), data_len as usize);\n    HCI_EVENT_LEN = data_len;\n    HAS_PENDING_EVENT = true;\n\n    // Note: The actual processing happens in HostStack_Process\n    // which is called from the background task\n\n    0 // Success\n}\n\n/// Process pending HCI events\n///\n/// This function is called from ll_sys_bg_process (the link layer background task).\n/// It checks if there are pending events from the C layer and processes them.\n/// This is the implementation of the weak HostStack_Process function in the C code.\n#[unsafe(no_mangle)]\npub unsafe extern \"C\" fn HostStack_Process() {\n    if !HAS_PENDING_EVENT {\n        return;\n    }\n\n    // Parse the event using the stored length\n    let event_len = HCI_EVENT_LEN as usize;\n    if event_len > 0 && event_len <= HCI_EVENT_BUFFER.len() {\n        let event_data = &HCI_EVENT_BUFFER[..event_len];\n        if let Some(event) = Event::parse(event_data) {\n            // Send all events to the general event channel\n            // Note: CommandComplete/CommandStatus events are now handled synchronously\n            // by the C HCI functions, so these events are mainly for informational purposes\n            let _ = try_send_event(event);\n        }\n    }\n\n    HAS_PENDING_EVENT = false;\n    HCI_EVENT_LEN = 0;\n}\n\n// Note: For WBA, the callback mechanism above is the primary event delivery method.\n// The C link layer calls hci_host_callback() when events are available, and\n// HostStack_Process() is called from the background task to process them.\n"
  },
  {
    "path": "embassy-stm32-wpan/src/wba/hci/mod.rs",
    "content": "//! HCI (Host Controller Interface) layer for STM32WBA BLE stack\n//!\n//! This module provides the low-level interface between the BLE host and controller.\n//! Unlike WB55 which uses IPCC for inter-processor communication, WBA uses direct\n//! C function calls since it's a single-core architecture.\n\npub mod command;\npub mod event;\npub mod host_if;\npub mod types;\n\npub use command::CommandSender;\npub use event::{Event, EventCode, EventParams, read_event};\npub use host_if::HostStack_Process;\npub use types::*;\n"
  },
  {
    "path": "embassy-stm32-wpan/src/wba/hci/types.rs",
    "content": "//! HCI types, constants, and opcodes\n\n/// HCI Packet types\n#[repr(u8)]\n#[derive(Debug, Clone, Copy, PartialEq, Eq)]\npub enum PacketType {\n    Command = 0x01,\n    AclData = 0x02,\n    SyncData = 0x03,\n    Event = 0x04,\n}\n\n/// HCI Opcode Group Field (OGF)\n#[repr(u16)]\n#[derive(Debug, Clone, Copy, PartialEq, Eq)]\npub enum OpcodeGroup {\n    LinkControl = 0x01,\n    LinkPolicy = 0x02,\n    ControllerAndBaseband = 0x03,\n    InformationalParameters = 0x04,\n    StatusParameters = 0x05,\n    Testing = 0x06,\n    LeController = 0x08,\n    VendorSpecific = 0x3F,\n}\n\n/// Construct HCI opcode from OGF and OCF\npub const fn opcode(ogf: u16, ocf: u16) -> u16 {\n    (ogf << 10) | ocf\n}\n\n/// HCI Command Opcodes - Link Control\npub mod link_control {\n    use super::*;\n\n    pub const DISCONNECT: u16 = opcode(OpcodeGroup::LinkControl as u16, 0x0006);\n}\n\n/// HCI Command Opcodes - Controller & Baseband\npub mod controller {\n    use super::*;\n\n    pub const RESET: u16 = opcode(OpcodeGroup::ControllerAndBaseband as u16, 0x0003);\n    pub const SET_EVENT_MASK: u16 = opcode(OpcodeGroup::ControllerAndBaseband as u16, 0x0001);\n    pub const READ_TRANSMIT_POWER_LEVEL: u16 = opcode(OpcodeGroup::ControllerAndBaseband as u16, 0x002D);\n}\n\n/// HCI Command Opcodes - Informational Parameters\npub mod info {\n    use super::*;\n\n    pub const READ_LOCAL_VERSION: u16 = opcode(OpcodeGroup::InformationalParameters as u16, 0x0001);\n    pub const READ_LOCAL_SUPPORTED_COMMANDS: u16 = opcode(OpcodeGroup::InformationalParameters as u16, 0x0002);\n    pub const READ_LOCAL_SUPPORTED_FEATURES: u16 = opcode(OpcodeGroup::InformationalParameters as u16, 0x0003);\n    pub const READ_BD_ADDR: u16 = opcode(OpcodeGroup::InformationalParameters as u16, 0x0009);\n}\n\n/// HCI Command Opcodes - LE Controller\npub mod le {\n    use super::*;\n\n    pub const SET_EVENT_MASK: u16 = opcode(OpcodeGroup::LeController as u16, 0x0001);\n    pub const READ_BUFFER_SIZE: u16 = opcode(OpcodeGroup::LeController as u16, 0x0002);\n    pub const READ_LOCAL_SUPPORTED_FEATURES: u16 = opcode(OpcodeGroup::LeController as u16, 0x0003);\n    pub const SET_RANDOM_ADDRESS: u16 = opcode(OpcodeGroup::LeController as u16, 0x0005);\n    pub const SET_ADVERTISING_PARAMETERS: u16 = opcode(OpcodeGroup::LeController as u16, 0x0006);\n    pub const READ_ADVERTISING_CHANNEL_TX_POWER: u16 = opcode(OpcodeGroup::LeController as u16, 0x0007);\n    pub const SET_ADVERTISING_DATA: u16 = opcode(OpcodeGroup::LeController as u16, 0x0008);\n    pub const SET_SCAN_RESPONSE_DATA: u16 = opcode(OpcodeGroup::LeController as u16, 0x0009);\n    pub const SET_ADVERTISE_ENABLE: u16 = opcode(OpcodeGroup::LeController as u16, 0x000A);\n    pub const SET_SCAN_PARAMETERS: u16 = opcode(OpcodeGroup::LeController as u16, 0x000B);\n    pub const SET_SCAN_ENABLE: u16 = opcode(OpcodeGroup::LeController as u16, 0x000C);\n    pub const CREATE_CONNECTION: u16 = opcode(OpcodeGroup::LeController as u16, 0x000D);\n    pub const CREATE_CONNECTION_CANCEL: u16 = opcode(OpcodeGroup::LeController as u16, 0x000E);\n    pub const READ_WHITE_LIST_SIZE: u16 = opcode(OpcodeGroup::LeController as u16, 0x000F);\n    pub const CLEAR_WHITE_LIST: u16 = opcode(OpcodeGroup::LeController as u16, 0x0010);\n    pub const ADD_DEVICE_TO_WHITE_LIST: u16 = opcode(OpcodeGroup::LeController as u16, 0x0011);\n    pub const REMOVE_DEVICE_FROM_WHITE_LIST: u16 = opcode(OpcodeGroup::LeController as u16, 0x0012);\n    pub const CONNECTION_UPDATE: u16 = opcode(OpcodeGroup::LeController as u16, 0x0013);\n    pub const READ_REMOTE_FEATURES: u16 = opcode(OpcodeGroup::LeController as u16, 0x0016);\n    pub const ENCRYPT: u16 = opcode(OpcodeGroup::LeController as u16, 0x0017);\n    pub const RAND: u16 = opcode(OpcodeGroup::LeController as u16, 0x0018);\n    pub const START_ENCRYPTION: u16 = opcode(OpcodeGroup::LeController as u16, 0x0019);\n    pub const LONG_TERM_KEY_REQUEST_REPLY: u16 = opcode(OpcodeGroup::LeController as u16, 0x001A);\n    pub const LONG_TERM_KEY_REQUEST_NEGATIVE_REPLY: u16 = opcode(OpcodeGroup::LeController as u16, 0x001B);\n    pub const READ_SUPPORTED_STATES: u16 = opcode(OpcodeGroup::LeController as u16, 0x001C);\n}\n\n/// HCI Status Codes\n#[repr(u8)]\n#[derive(Debug, Clone, Copy, PartialEq, Eq)]\npub enum Status {\n    Success = 0x00,\n    UnknownCommand = 0x01,\n    UnknownConnectionId = 0x02,\n    HardwareFailure = 0x03,\n    PageTimeout = 0x04,\n    AuthenticationFailure = 0x05,\n    PinOrKeyMissing = 0x06,\n    MemoryCapacityExceeded = 0x07,\n    ConnectionTimeout = 0x08,\n    ConnectionLimitExceeded = 0x09,\n    InvalidHciCommandParameters = 0x12,\n    RemoteUserTerminatedConnection = 0x13,\n    ConnectionTerminatedByLocalHost = 0x16,\n    UnsupportedRemoteFeature = 0x1A,\n    InvalidLmpParameters = 0x1E,\n    UnspecifiedError = 0x1F,\n    UnsupportedLmpParameterValue = 0x20,\n    RoleChangeNotAllowed = 0x21,\n    LmpResponseTimeout = 0x22,\n    ControllerBusy = 0x3A,\n    UnacceptableConnectionParameters = 0x3B,\n    AdvertisingTimeout = 0x3C,\n    ConnectionTerminatedDueToMicFailure = 0x3D,\n    ConnectionFailedToBeEstablished = 0x3E,\n}\n\nimpl Status {\n    pub fn from_u8(value: u8) -> Self {\n        match value {\n            0x00 => Status::Success,\n            0x01 => Status::UnknownCommand,\n            0x02 => Status::UnknownConnectionId,\n            0x03 => Status::HardwareFailure,\n            0x04 => Status::PageTimeout,\n            0x05 => Status::AuthenticationFailure,\n            0x06 => Status::PinOrKeyMissing,\n            0x07 => Status::MemoryCapacityExceeded,\n            0x08 => Status::ConnectionTimeout,\n            0x09 => Status::ConnectionLimitExceeded,\n            0x12 => Status::InvalidHciCommandParameters,\n            0x13 => Status::RemoteUserTerminatedConnection,\n            0x16 => Status::ConnectionTerminatedByLocalHost,\n            0x1A => Status::UnsupportedRemoteFeature,\n            0x1E => Status::InvalidLmpParameters,\n            0x1F => Status::UnspecifiedError,\n            0x20 => Status::UnsupportedLmpParameterValue,\n            0x21 => Status::RoleChangeNotAllowed,\n            0x22 => Status::LmpResponseTimeout,\n            0x3A => Status::ControllerBusy,\n            0x3B => Status::UnacceptableConnectionParameters,\n            0x3C => Status::AdvertisingTimeout,\n            0x3D => Status::ConnectionTerminatedDueToMicFailure,\n            0x3E => Status::ConnectionFailedToBeEstablished,\n            _ => Status::UnspecifiedError,\n        }\n    }\n}\n\n/// BLE Address Type\n#[repr(u8)]\n#[derive(Debug, Clone, Copy, PartialEq, Eq)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub enum AddressType {\n    Public = 0x00,\n    Random = 0x01,\n    PublicIdentity = 0x02,\n    RandomIdentity = 0x03,\n}\n\n/// BLE Address (6 bytes)\n#[derive(Debug, Clone, Copy, PartialEq, Eq)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub struct Address(pub [u8; 6]);\n\nimpl Address {\n    pub const fn new(bytes: [u8; 6]) -> Self {\n        Self(bytes)\n    }\n\n    pub fn as_bytes(&self) -> &[u8; 6] {\n        &self.0\n    }\n}\n\n/// Connection Handle (12-bit value)\n#[derive(Debug, Clone, Copy, PartialEq, Eq)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub struct Handle(pub u16);\n\nimpl Handle {\n    pub const fn new(handle: u16) -> Self {\n        Self(handle & 0x0FFF)\n    }\n\n    pub fn as_u16(&self) -> u16 {\n        self.0\n    }\n}\n\n/// Advertising Type\n#[repr(u8)]\n#[derive(Debug, Clone, Copy, PartialEq, Eq)]\npub enum AdvType {\n    /// Connectable and scannable undirected advertising\n    ConnectableUndirected = 0x00,\n    /// Connectable high duty cycle directed advertising\n    ConnectableDirectedHighDutyCycle = 0x01,\n    /// Scannable undirected advertising\n    ScannableUndirected = 0x02,\n    /// Non-connectable undirected advertising\n    NonConnectableUndirected = 0x03,\n    /// Connectable low duty cycle directed advertising\n    ConnectableDirectedLowDutyCycle = 0x04,\n}\n\n/// Own Address Type for advertising/scanning\n#[repr(u8)]\n#[derive(Debug, Clone, Copy, PartialEq, Eq)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub enum OwnAddressType {\n    Public = 0x00,\n    Random = 0x01,\n    ResolvableOrPublic = 0x02,\n    ResolvableOrRandom = 0x03,\n}\n\n/// Advertising Filter Policy\n#[repr(u8)]\n#[derive(Debug, Clone, Copy, PartialEq, Eq)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub enum AdvFilterPolicy {\n    /// Process scan and connection requests from all devices\n    All = 0x00,\n    /// Process connection requests from all devices and scan requests only from white list\n    ConnAllScanWhiteList = 0x01,\n    /// Process scan requests from all devices and connection requests only from white list\n    ScanAllConnWhiteList = 0x02,\n    /// Process scan and connection requests only from white list\n    WhiteListOnly = 0x03,\n}\n"
  },
  {
    "path": "embassy-stm32-wpan/src/wba/linklayer_plat.rs",
    "content": "// /* USER CODE BEGIN Header */\n// /**\n//   ******************************************************************************\n//   * @file    linklayer_plat.c\n//   * @author  MCD Application Team\n//   * @brief   Source file for the linklayer plateform adaptation layer\n//   ******************************************************************************\n//   * @attention\n//   *\n//   * Copyright (c) 2024 STMicroelectronics.\n//   * All rights reserved.\n//   *\n//   * This software is licensed under terms that can be found in the LICENSE file\n//   * in the root directory of this software component.\n//   * If no LICENSE file comes with this software, it is provided AS-IS.\n//   *\n//   ******************************************************************************\n//   */\n// /* USER CODE END Header */\n//\n// #include \"stm32wbaxx_hal.h\"\n// #include \"stm32wbaxx_hal_conf.h\"\n// #include \"stm32wbaxx_ll_rcc.h\"\n//\n// #include \"app_common.h\"\n// #include \"app_conf.h\"\n// #include \"linklayer_plat.h\"\n// #include \"scm.h\"\n// #include \"log_module.h\"\n// #if (USE_TEMPERATURE_BASED_RADIO_CALIBRATION == 1)\n// #include \"adc_ctrl.h\"\n// #endif /* (USE_TEMPERATURE_BASED_RADIO_CALIBRATION == 1) */\n//\n// #if (CFG_LPM_LEVEL != 0)\n// #include \"stm32_lpm.h\"\n// #include \"stm32_lpm_if.h\"\n// #endif /* (CFG_LPM_LEVEL != 0) */\n//\n// /* USER CODE BEGIN Includes */\n//\n// /* USER CODE END Includes */\n//\n// #define max(a,b) ((a) > (b) ? a : b)\n//\n// /* 2.4GHz RADIO ISR callbacks */\n// void (*radio_callback)(void) = NULL;\n// void (*low_isr_callback)(void) = NULL;\n//\n// /* RNG handle */\n// extern RNG_HandleTypeDef hrng;\n//\n// #if (USE_TEMPERATURE_BASED_RADIO_CALIBRATION == 1)\n// /* Link Layer temperature request from background */\n// extern void ll_sys_bg_temperature_measurement(void);\n// #endif /* (USE_TEMPERATURE_BASED_RADIO_CALIBRATION == 1) */\n//\n// /* Radio critical sections */\n// static uint32_t primask_bit = 0;\n// volatile int32_t prio_high_isr_counter = 0;\n// volatile int32_t prio_low_isr_counter = 0;\n// volatile int32_t prio_sys_isr_counter = 0;\n// volatile int32_t irq_counter = 0;\n// volatile uint32_t local_basepri_value = 0;\n//\n// /* Radio SW low ISR global variable */\n// volatile uint8_t radio_sw_low_isr_is_running_high_prio = 0;\n//\n// /* Radio bus clock control variables */\n// uint8_t AHB5_SwitchedOff = 0;\n// uint32_t radio_sleep_timer_val = 0;\n//\n// /* USER CODE BEGIN LINKLAYER_PLAT 0 */\n//\n// /* USER CODE END LINKLAYER_PLAT 0 */\n#![cfg(feature = \"wba\")]\n#![allow(clippy::missing_safety_doc)]\n\nuse core::cell::RefCell;\nuse core::ptr;\nuse core::sync::atomic::{AtomicBool, AtomicI32, AtomicPtr, AtomicU32, Ordering, compiler_fence};\n\nuse cortex_m::interrupt::InterruptNumber;\nuse cortex_m::peripheral::NVIC;\nuse cortex_m::register::basepri;\nuse critical_section;\n#[cfg(feature = \"defmt\")]\nuse defmt::{error, trace};\nuse embassy_sync::blocking_mutex::Mutex;\n#[cfg(not(feature = \"defmt\"))]\nmacro_rules! trace {\n    ($($arg:tt)*) => {{}};\n}\n#[cfg(not(feature = \"defmt\"))]\nmacro_rules! error {\n    ($($arg:tt)*) => {{}};\n}\nuse embassy_stm32::NVIC_PRIO_BITS;\nuse embassy_stm32::pac::RCC;\nuse embassy_stm32::peripherals::RNG;\nuse embassy_stm32::rng::Rng;\nuse embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;\nuse embassy_time::{Duration, block_for};\n\nuse super::bindings::{link_layer, mac};\n\n// Missing constants from stm32-bindings - RADIO interrupt numbers\n// For STM32WBA65RI, the RADIO interrupt is position 66 (between ADC4=65 and WKUP=67)\n// Note: mac::RADIO_INTR_NUM is incorrectly set to 0 in stm32-bindings, so we override it here\nconst RADIO_INTR_NUM: u32 = 66; // 2.4 GHz RADIO global interrupt\nconst RADIO_SW_LOW_INTR_NUM: u32 = 67; // WKUP used as SW low interrupt\n\ntype Callback = unsafe extern \"C\" fn();\n\n#[derive(Clone, Copy, Debug, Eq, PartialEq)]\n#[repr(transparent)]\nstruct RawInterrupt(u16);\n\nimpl RawInterrupt {\n    fn new(irq: u32) -> Self {\n        debug_assert!(irq <= u16::MAX as u32);\n        Self(irq as u16)\n    }\n}\n\nimpl From<u32> for RawInterrupt {\n    fn from(value: u32) -> Self {\n        Self::new(value)\n    }\n}\n\nunsafe impl InterruptNumber for RawInterrupt {\n    fn number(self) -> u16 {\n        self.0\n    }\n}\n\nstatic RADIO_CALLBACK: AtomicPtr<()> = AtomicPtr::new(ptr::null_mut());\nstatic LOW_ISR_CALLBACK: AtomicPtr<()> = AtomicPtr::new(ptr::null_mut());\n\nstatic IRQ_COUNTER: AtomicI32 = AtomicI32::new(0);\n\nstatic PRIO_HIGH_ISR_COUNTER: AtomicI32 = AtomicI32::new(0);\nstatic PRIO_LOW_ISR_COUNTER: AtomicI32 = AtomicI32::new(0);\nstatic PRIO_SYS_ISR_COUNTER: AtomicI32 = AtomicI32::new(0);\nstatic LOCAL_BASEPRI_VALUE: AtomicU32 = AtomicU32::new(0);\n\nstatic RADIO_SW_LOW_ISR_RUNNING_HIGH_PRIO: AtomicBool = AtomicBool::new(false);\nstatic AHB5_SWITCHED_OFF: AtomicBool = AtomicBool::new(false);\nstatic RADIO_SLEEP_TIMER_VAL: AtomicU32 = AtomicU32::new(0);\n\n// Critical-section restore token for IRQ enable/disable pairing.\n// Only written when the IRQ disable counter transitions 0->1, and consumed when it transitions 1->0.\nstatic mut CS_RESTORE_STATE: Option<critical_section::RestoreState> = None;\n\n// Optional hardware RNG instance for true random number generation.\n// The RNG peripheral pointer is stored here to be used by LINKLAYER_PLAT_GetRNG.\n// This must be set by the application using `set_rng_instance` before the link layer requests random numbers.\npub(crate) static mut HARDWARE_RNG: Option<&'static Mutex<CriticalSectionRawMutex, RefCell<Rng<'static, RNG>>>> = None;\n\nfn store_callback(slot: &AtomicPtr<()>, cb: Option<Callback>) {\n    let ptr = cb.map_or(ptr::null_mut(), |f| f as *mut ());\n    slot.store(ptr, Ordering::Release);\n}\n\nfn load_callback(slot: &AtomicPtr<()>) -> Option<Callback> {\n    let ptr = slot.load(Ordering::Acquire);\n    if ptr.is_null() {\n        None\n    } else {\n        Some(unsafe { core::mem::transmute::<*mut (), Callback>(ptr) })\n    }\n}\n\nfn priority_shift() -> u8 {\n    8 - NVIC_PRIO_BITS as u8\n}\n\nfn pack_priority(raw: u32) -> u8 {\n    let shift = priority_shift();\n    let priority_bits = NVIC_PRIO_BITS as u32;\n    let mask = if priority_bits >= 32 {\n        u32::MAX\n    } else {\n        (1u32 << priority_bits) - 1\n    };\n    let clamped = raw & mask;\n    (clamped << u32::from(shift)) as u8\n}\n\nfn counter_release(counter: &AtomicI32) -> bool {\n    counter.fetch_sub(1, Ordering::SeqCst) <= 1\n}\n\nfn counter_acquire(counter: &AtomicI32) -> bool {\n    counter.fetch_add(1, Ordering::SeqCst) == 0\n}\n\nunsafe fn nvic_enable(irq: u32) {\n    NVIC::unmask(RawInterrupt::new(irq));\n    compiler_fence(Ordering::SeqCst);\n}\n\nunsafe fn nvic_disable(irq: u32) {\n    NVIC::mask(RawInterrupt::new(irq));\n    compiler_fence(Ordering::SeqCst);\n}\n\nunsafe fn nvic_set_pending(irq: u32) {\n    NVIC::pend(RawInterrupt::new(irq));\n    compiler_fence(Ordering::SeqCst);\n}\n\nunsafe fn nvic_get_active(irq: u32) -> bool {\n    NVIC::is_active(RawInterrupt::new(irq))\n}\n\nunsafe fn nvic_set_priority(irq: u32, priority: u8) {\n    // STM32WBA is ARMv8-M, which uses byte-accessible IPR registers\n    let nvic = &*NVIC::PTR;\n    nvic.ipr[irq as usize].write(priority);\n\n    compiler_fence(Ordering::SeqCst);\n}\n\nfn set_basepri_max(value: u8) {\n    unsafe {\n        if basepri::read() < value {\n            basepri::write(value);\n        }\n    }\n}\n\npub unsafe fn run_radio_high_isr() {\n    if let Some(cb) = load_callback(&RADIO_CALLBACK) {\n        cb();\n    }\n    // Wake the BLE runner task to process any resulting events\n    super::runner::on_radio_interrupt();\n}\n\npub unsafe fn run_radio_sw_low_isr() {\n    if let Some(cb) = load_callback(&LOW_ISR_CALLBACK) {\n        cb();\n    }\n\n    if RADIO_SW_LOW_ISR_RUNNING_HIGH_PRIO.swap(false, Ordering::AcqRel) {\n        nvic_set_priority(RADIO_SW_LOW_INTR_NUM, pack_priority(mac::RADIO_SW_LOW_INTR_PRIO));\n    }\n\n    // Wake the BLE runner task to process any resulting events\n    super::runner::on_radio_interrupt();\n}\n\n// /**\n//   * @brief  Configure the necessary clock sources for the radio.\n//   * @param  None\n//   * @retval None\n//   */\n#[unsafe(no_mangle)]\npub unsafe extern \"C\" fn LINKLAYER_PLAT_ClockInit() {\n    trace!(\"LINKLAYER_PLAT_ClockInit\");\n\n    // Enable AHB5ENR peripheral clock (bus CLK) for the radio\n    // For STM32WBA65xx: RCC base = 0x4602_0C00, AHB5ENR offset = 0x098\n    // RADIOEN bit = bit 0\n    RCC.ahb5enr().modify(|w| w.set_radioen(true));\n\n    // Memory barrier to ensure clock is enabled before proceeding\n    compiler_fence(Ordering::SeqCst);\n\n    trace!(\"LINKLAYER_PLAT_ClockInit: radio clock enabled\");\n}\n\n// /**\n//   * @brief  Link Layer active waiting loop.\n//   * @param  delay: delay in us\n//   * @retval None\n//   */\n#[unsafe(no_mangle)]\npub unsafe extern \"C\" fn LINKLAYER_PLAT_DelayUs(delay: u32) {\n    //   static uint8_t lock = 0;\n    //   uint32_t t0;\n    //   uint32_t primask_bit;\n    //\n    //   /* Enter critical section */\n    //   primask_bit= __get_PRIMASK();\n    //   __disable_irq();\n    //\n    //   if (lock == 0U)\n    //   {\n    //     /* Initialize counter */\n    //     /* Reset cycle counter to prevent overflow\n    //        As a us counter, it is assumed than even with re-entrancy,\n    //        overflow will never happen before re-initializing this counter */\n    //     DWT->CYCCNT = 0U;\n    //     /* Enable DWT by safety but should be useless (as already set) */\n    //     SET_BIT(DCB->DEMCR, DCB_DEMCR_TRCENA_Msk);\n    //     /* Enable counter */\n    //     SET_BIT(DWT->CTRL, DWT_CTRL_CYCCNTENA_Msk);\n    //   }\n    //   /* Increment 're-entrance' counter */\n    //   lock++;\n    //   /* Get starting time stamp */\n    //   t0 = DWT->CYCCNT;\n    //   /* Exit critical section */\n    //  __set_PRIMASK(primask_bit);\n    //\n    //   /* Turn us into cycles */\n    //   delay = delay * (SystemCoreClock / 1000000U);\n    //   delay += t0;\n    //\n    //   /* Busy waiting loop */\n    //   while (DWT->CYCCNT < delay)\n    //   {\n    //   };\n    //\n    //   /* Enter critical section */\n    //   primask_bit= __get_PRIMASK();\n    //   __disable_irq();\n    //   if (lock == 1U)\n    //   {\n    //     /* Disable counter */\n    //     CLEAR_BIT(DWT->CTRL, DWT_CTRL_CYCCNTENA_Msk);\n    //   }\n    //   /* Decrement 're-entrance' counter */\n    //   lock--;\n    //   /* Exit critical section */\n    //  __set_PRIMASK(primask_bit);\n    //\n    trace!(\"LINKLAYER_PLAT_DelayUs: delay={}\", delay);\n    block_for(Duration::from_micros(u64::from(delay)));\n}\n\n// /**\n//   * @brief  Link Layer assertion API\n//   * @param  condition: conditional statement to be checked.\n//   * @retval None\n//   */\n#[unsafe(no_mangle)]\npub unsafe extern \"C\" fn LINKLAYER_PLAT_Assert(condition: u8) {\n    if condition == 0 {\n        panic!(\"LINKLAYER_PLAT assertion failed\");\n    }\n}\n\n// /**\n//   * @brief  Enable/disable the Link Layer active clock (baseband clock).\n//   * @param  enable: boolean value to enable (1) or disable (0) the clock.\n//   * @retval None\n//   */\n#[unsafe(no_mangle)]\npub unsafe extern \"C\" fn LINKLAYER_PLAT_WaitHclkRdy() {\n    trace!(\"LINKLAYER_PLAT_WaitHclkRdy\");\n    if AHB5_SWITCHED_OFF.swap(false, Ordering::AcqRel) {\n        let reference = RADIO_SLEEP_TIMER_VAL.load(Ordering::Acquire);\n        trace!(\"LINKLAYER_PLAT_WaitHclkRdy: reference={}\", reference);\n        while reference == link_layer::ll_intf_cmn_get_slptmr_value() {}\n    }\n}\n\n// /**\n//   * @brief  Notify the Link Layer platform layer the system will enter in WFI\n//   *         and AHB5 clock may be turned of regarding the 2.4Ghz radio state.\n//   * @param  None\n//   * @retval None\n//   */\n#[unsafe(no_mangle)]\npub unsafe extern \"C\" fn LINKLAYER_PLAT_NotifyWFIEnter() {\n    //   /* Check if Radio state will allow the AHB5 clock to be cut */\n    //\n    //   /* AHB5 clock will be cut in the following cases:\n    //    * - 2.4GHz radio is not in ACTIVE mode (in SLEEP or DEEPSLEEP mode).\n    //    * - RADIOSMEN and STRADIOCLKON bits are at 0.\n    //    */\n    //   if((LL_PWR_GetRadioMode() != LL_PWR_RADIO_ACTIVE_MODE) ||\n    //      ((__HAL_RCC_RADIO_IS_CLK_SLEEP_ENABLED() == 0) && (LL_RCC_RADIO_IsEnabledSleepTimerClock() == 0)))\n    //   {\n    //     AHB5_SwitchedOff = 1;\n    //   }\n    trace!(\"LINKLAYER_PLAT_NotifyWFIEnter\");\n    AHB5_SWITCHED_OFF.store(true, Ordering::Release);\n}\n\n// /**\n//   * @brief  Notify the Link Layer platform layer the system exited WFI and AHB5\n//   *         clock may be resynchronized as is may have been turned of during\n//   *         low power mode entry.\n//   * @param  None\n//   * @retval None\n//   */\n#[unsafe(no_mangle)]\npub unsafe extern \"C\" fn LINKLAYER_PLAT_NotifyWFIExit() {\n    trace!(\"LINKLAYER_PLAT_NotifyWFIExit\");\n    //   /* Check if AHB5 clock has been turned of and needs resynchronisation */\n    if AHB5_SWITCHED_OFF.load(Ordering::Acquire) {\n        //     /* Read sleep register as earlier as possible */\n        let value = link_layer::ll_intf_cmn_get_slptmr_value();\n        RADIO_SLEEP_TIMER_VAL.store(value, Ordering::Release);\n    }\n}\n\n// /**\n//   * @brief  Enable/disable the Link Layer active clock (baseband clock).\n//   * @param  enable: boolean value to enable (1) or disable (0) the clock.\n//   * @retval None\n//   */\n#[unsafe(no_mangle)]\npub unsafe extern \"C\" fn LINKLAYER_PLAT_AclkCtrl(enable: u8) {\n    trace!(\"LINKLAYER_PLAT_AclkCtrl: enable={}\", enable);\n\n    if enable != 0 {\n        // Wait for HSE to be ready before enabling radio baseband clock\n        // HSE (High-Speed External) oscillator is required for radio operation\n        while !RCC.cr().read().hserdy() {}\n\n        // Enable RADIO baseband clock (active clock)\n        RCC.radioenr().modify(|w| w.set_bbclken(true));\n\n        // Memory barrier to ensure clock is enabled before proceeding\n        compiler_fence(Ordering::SeqCst);\n\n        trace!(\"LINKLAYER_PLAT_AclkCtrl: radio baseband clock enabled\");\n    } else {\n        // Disable RADIO baseband clock (active clock)\n        RCC.radioenr().modify(|w| w.set_bbclken(false));\n\n        // Memory barrier\n        compiler_fence(Ordering::SeqCst);\n\n        trace!(\"LINKLAYER_PLAT_AclkCtrl: radio baseband clock disabled\");\n    }\n}\n\n// /**\n//   * @brief  Link Layer RNG request.\n//   * @param  ptr_rnd: pointer to the variable that hosts the number.\n//   * @param  len: number of byte of anthropy to get.\n//   * @retval None\n//   */\n#[unsafe(no_mangle)]\npub unsafe extern \"C\" fn LINKLAYER_PLAT_GetRNG(ptr_rnd: *mut u8, len: u32) {\n    trace!(\"LINKLAYER_PLAT_GetRNG: ptr_rnd={:?}, len={}\", ptr_rnd, len);\n    if ptr_rnd.is_null() || len == 0 {\n        return;\n    }\n\n    critical_section::with(|cs| {\n        HARDWARE_RNG\n            .as_ref()\n            .unwrap()\n            .borrow(cs)\n            .borrow_mut()\n            .fill_bytes(core::slice::from_raw_parts_mut(ptr_rnd, len as usize))\n    });\n\n    trace!(\"LINKLAYER_PLAT_GetRNG: generated {} random bytes\", len);\n}\n\n// /**\n//   * @brief  Initialize Link Layer radio high priority interrupt.\n//   * @param  intr_cb: function pointer to assign for the radio high priority ISR routine.\n//   * @retval None\n//   */\n#[unsafe(no_mangle)]\npub unsafe extern \"C\" fn LINKLAYER_PLAT_SetupRadioIT(intr_cb: Option<Callback>) {\n    trace!(\"LINKLAYER_PLAT_SetupRadioIT: intr_cb={:?}\", intr_cb);\n    store_callback(&RADIO_CALLBACK, intr_cb);\n\n    if intr_cb.is_some() {\n        nvic_set_priority(RADIO_INTR_NUM, pack_priority(mac::RADIO_INTR_PRIO_HIGH));\n        nvic_enable(RADIO_INTR_NUM);\n    } else {\n        nvic_disable(RADIO_INTR_NUM);\n    }\n}\n\n// /**\n//   * @brief  Initialize Link Layer SW low priority interrupt.\n//   * @param  intr_cb: function pointer to assign for the SW low priority ISR routine.\n//   * @retval None\n//   */\n#[unsafe(no_mangle)]\npub unsafe extern \"C\" fn LINKLAYER_PLAT_SetupSwLowIT(intr_cb: Option<Callback>) {\n    trace!(\"LINKLAYER_PLAT_SetupSwLowIT: intr_cb={:?}\", intr_cb);\n    store_callback(&LOW_ISR_CALLBACK, intr_cb);\n\n    if intr_cb.is_some() {\n        nvic_set_priority(RADIO_SW_LOW_INTR_NUM, pack_priority(mac::RADIO_SW_LOW_INTR_PRIO));\n        nvic_enable(RADIO_SW_LOW_INTR_NUM);\n    } else {\n        nvic_disable(RADIO_SW_LOW_INTR_NUM);\n    }\n}\n\n// /**\n//   * @brief  Trigger the link layer SW low interrupt.\n//   * @param  None\n//   * @retval None\n//   */\n#[unsafe(no_mangle)]\npub unsafe extern \"C\" fn LINKLAYER_PLAT_TriggerSwLowIT(priority: u8) {\n    trace!(\"LINKLAYER_PLAT_TriggerSwLowIT: priority={}\", priority);\n    let active = nvic_get_active(RADIO_SW_LOW_INTR_NUM);\n\n    //   /* Check if a SW low interrupt as already been raised.\n    //    * Nested call far radio low isr are not supported\n    //    **/\n    if !active {\n        let prio = if priority == 0 {\n            //     /* No nested SW low ISR, default behavior */\n            pack_priority(mac::RADIO_SW_LOW_INTR_PRIO)\n        } else {\n            pack_priority(mac::RADIO_INTR_PRIO_LOW)\n        };\n        nvic_set_priority(RADIO_SW_LOW_INTR_NUM, prio);\n    } else if priority != 0 {\n        //     /* Nested call detected */\n        //     /* No change for SW radio low interrupt priority for the moment */\n        //\n        //     if(priority != 0)\n        //     {\n        //       /* At the end of current SW radio low ISR, this pending SW low interrupt\n        //        * will run with RADIO_INTR_PRIO_LOW priority\n        //        **/\n        //       radio_sw_low_isr_is_running_high_prio = 1;\n        //     }\n        RADIO_SW_LOW_ISR_RUNNING_HIGH_PRIO.store(true, Ordering::Release);\n    }\n\n    nvic_set_pending(RADIO_SW_LOW_INTR_NUM);\n}\n\n// /**\n//   * @brief  Enable interrupts.\n//   * @param  None\n//   * @retval None\n//   */\n#[unsafe(no_mangle)]\npub unsafe extern \"C\" fn LINKLAYER_PLAT_EnableIRQ() {\n    trace!(\"LINKLAYER_PLAT_EnableIRQ\");\n    //   irq_counter = max(0,irq_counter-1);\n    //\n    //   if(irq_counter == 0)\n    //   {\n    //     /* When irq_counter reaches 0, restore primask bit */\n    //     __set_PRIMASK(primask_bit);\n    //   }\n    if counter_release(&IRQ_COUNTER) {\n        // When the counter reaches zero, restore prior interrupt state using the captured token.\n        if let Some(token) = CS_RESTORE_STATE.take() {\n            critical_section::release(token);\n        }\n    }\n}\n\n// /**\n//   * @brief  Disable interrupts.\n//   * @param  None\n//   * @retval None\n//   */\n#[unsafe(no_mangle)]\npub unsafe extern \"C\" fn LINKLAYER_PLAT_DisableIRQ() {\n    trace!(\"LINKLAYER_PLAT_DisableIRQ\");\n    //   if(irq_counter == 0)\n    //   {\n    //     /* Save primask bit at first interrupt disablement */\n    //     primask_bit= __get_PRIMASK();\n    //   }\n    //   __disable_irq();\n    //   irq_counter ++;\n    if counter_acquire(&IRQ_COUNTER) {\n        // Capture and disable using critical-section API on first disable.\n        CS_RESTORE_STATE = Some(critical_section::acquire());\n    }\n}\n\n// /**\n//   * @brief  Enable specific interrupt group.\n//   * @param  isr_type: mask for interrupt group to enable.\n//   *         This parameter can be one of the following:\n//   *         @arg LL_HIGH_ISR_ONLY: enable link layer high priority ISR.\n//   *         @arg LL_LOW_ISR_ONLY: enable link layer SW low priority ISR.\n//   *         @arg SYS_LOW_ISR: mask interrupts for all the other system ISR with\n//   *              lower priority that link layer SW low interrupt.\n//   * @retval None\n//   */\n#[unsafe(no_mangle)]\npub unsafe extern \"C\" fn LINKLAYER_PLAT_EnableSpecificIRQ(isr_type: u8) {\n    trace!(\"LINKLAYER_PLAT_EnableSpecificIRQ: isr_type={}\", isr_type);\n    //   if( (isr_type & LL_HIGH_ISR_ONLY) != 0 )\n    //   {\n    //     prio_high_isr_counter--;\n    //     if(prio_high_isr_counter == 0)\n    //     {\n    //       /* When specific counter for link layer high ISR reaches 0, interrupt is enabled */\n    //       HAL_NVIC_EnableIRQ(RADIO_INTR_NUM);\n    //       /* USER CODE BEGIN LINKLAYER_PLAT_EnableSpecificIRQ_1 */\n    //\n    //       /* USER CODE END LINKLAYER_PLAT_EnableSpecificIRQ_1 */\n    //     }\n    //   }\n    //\n    //   if( (isr_type & LL_LOW_ISR_ONLY) != 0 )\n    //   {\n    //     prio_low_isr_counter--;\n    //     if(prio_low_isr_counter == 0)\n    //     {\n    //       /* When specific counter for link layer SW low ISR reaches 0, interrupt is enabled */\n    //       HAL_NVIC_EnableIRQ(RADIO_SW_LOW_INTR_NUM);\n    //     }\n    //\n    //   }\n    //\n    //   if( (isr_type & SYS_LOW_ISR) != 0 )\n    //   {\n    //     prio_sys_isr_counter--;\n    //     if(prio_sys_isr_counter == 0)\n    //     {\n    //       /* Restore basepri value */\n    //       __set_BASEPRI(local_basepri_value);\n    //     }\n    //   }\n    if (isr_type & link_layer::LL_HIGH_ISR_ONLY as u8) != 0 {\n        if counter_release(&PRIO_HIGH_ISR_COUNTER) {\n            nvic_enable(RADIO_INTR_NUM);\n        }\n    }\n\n    if (isr_type & link_layer::LL_LOW_ISR_ONLY as u8) != 0 {\n        if counter_release(&PRIO_LOW_ISR_COUNTER) {\n            nvic_enable(RADIO_SW_LOW_INTR_NUM);\n        }\n    }\n\n    if (isr_type & link_layer::SYS_LOW_ISR as u8) != 0 {\n        if counter_release(&PRIO_SYS_ISR_COUNTER) {\n            let stored = LOCAL_BASEPRI_VALUE.load(Ordering::Relaxed) as u8;\n            basepri::write(stored);\n        }\n    }\n}\n\n// /**\n//   * @brief  Disable specific interrupt group.\n//   * @param  isr_type: mask for interrupt group to disable.\n//   *         This parameter can be one of the following:\n//   *         @arg LL_HIGH_ISR_ONLY: disable link layer high priority ISR.\n//   *         @arg LL_LOW_ISR_ONLY: disable link layer SW low priority ISR.\n//   *         @arg SYS_LOW_ISR: unmask interrupts for all the other system ISR with\n//   *              lower priority that link layer SW low interrupt.\n//   * @retval None\n//   */\n#[unsafe(no_mangle)]\npub unsafe extern \"C\" fn LINKLAYER_PLAT_DisableSpecificIRQ(isr_type: u8) {\n    //   if( (isr_type & LL_HIGH_ISR_ONLY) != 0 )\n    //   {\n    //     prio_high_isr_counter++;\n    //     if(prio_high_isr_counter == 1)\n    //     {\n    //       /* USER CODE BEGIN LINKLAYER_PLAT_DisableSpecificIRQ_1 */\n    //\n    //       /* USER CODE END LINKLAYER_PLAT_DisableSpecificIRQ_1 */\n    //       /* When specific counter for link layer high ISR value is 1, interrupt is disabled */\n    //       HAL_NVIC_DisableIRQ(RADIO_INTR_NUM);\n    //     }\n    //   }\n    //\n    //   if( (isr_type & LL_LOW_ISR_ONLY) != 0 )\n    //   {\n    //     prio_low_isr_counter++;\n    //     if(prio_low_isr_counter == 1)\n    //     {\n    //       /* When specific counter for link layer SW low ISR value is 1, interrupt is disabled */\n    //       HAL_NVIC_DisableIRQ(RADIO_SW_LOW_INTR_NUM);\n    //     }\n    //   }\n    //\n    //   if( (isr_type & SYS_LOW_ISR) != 0 )\n    //   {\n    //     prio_sys_isr_counter++;\n    //     if(prio_sys_isr_counter == 1)\n    //     {\n    //       /* Save basepri register value */\n    //       local_basepri_value = __get_BASEPRI();\n    //\n    //       /* Mask all other interrupts with lower priority that link layer SW low ISR */\n    //       __set_BASEPRI_MAX(RADIO_INTR_PRIO_LOW<<4);\n    //     }\n    //   }\n    trace!(\"LINKLAYER_PLAT_DisableSpecificIRQ: isr_type={}\", isr_type);\n    if (isr_type & link_layer::LL_HIGH_ISR_ONLY as u8) != 0 {\n        if counter_acquire(&PRIO_HIGH_ISR_COUNTER) {\n            nvic_disable(RADIO_INTR_NUM);\n        }\n    }\n\n    if (isr_type & link_layer::LL_LOW_ISR_ONLY as u8) != 0 {\n        if counter_acquire(&PRIO_LOW_ISR_COUNTER) {\n            nvic_disable(RADIO_SW_LOW_INTR_NUM);\n        }\n    }\n\n    if (isr_type & link_layer::SYS_LOW_ISR as u8) != 0 {\n        if counter_acquire(&PRIO_SYS_ISR_COUNTER) {\n            let current = basepri::read();\n            LOCAL_BASEPRI_VALUE.store(current.into(), Ordering::Relaxed);\n            set_basepri_max(pack_priority(mac::RADIO_INTR_PRIO_LOW));\n        }\n    }\n}\n\n// /**\n//   * @brief  Enable link layer high priority ISR only.\n//   * @param  None\n//   * @retval None\n//   */\n#[unsafe(no_mangle)]\npub unsafe extern \"C\" fn LINKLAYER_PLAT_EnableRadioIT() {\n    trace!(\"LINKLAYER_PLAT_EnableRadioIT\");\n    nvic_enable(RADIO_INTR_NUM);\n}\n\n// /**\n//   * @brief  Disable link layer high priority ISR only.\n//   * @param  None\n//   * @retval None\n//   */\n#[unsafe(no_mangle)]\npub unsafe extern \"C\" fn LINKLAYER_PLAT_DisableRadioIT() {\n    trace!(\"LINKLAYER_PLAT_DisableRadioIT\");\n    nvic_disable(RADIO_INTR_NUM);\n}\n\n// /**\n//   * @brief  Link Layer notification for radio activity start.\n//   * @param  None\n//   * @retval None\n//   */\n#[unsafe(no_mangle)]\npub unsafe extern \"C\" fn LINKLAYER_PLAT_StartRadioEvt() {\n    trace!(\"LINKLAYER_PLAT_StartRadioEvt\");\n    //   __HAL_RCC_RADIO_CLK_SLEEP_ENABLE();\n    //   NVIC_SetPriority(RADIO_INTR_NUM, RADIO_INTR_PRIO_HIGH);\n    // #if (CFG_SCM_SUPPORTED == 1)\n    //   scm_notifyradiostate(SCM_RADIO_ACTIVE);\n    // #endif /* CFG_SCM_SUPPORTED */\n    nvic_set_priority(RADIO_INTR_NUM, pack_priority(mac::RADIO_INTR_PRIO_HIGH));\n    nvic_enable(RADIO_INTR_NUM);\n}\n\n// /**\n//   * @brief  Link Layer notification for radio activity end.\n//   * @param  None\n//   * @retval None\n//   */\n#[unsafe(no_mangle)]\npub unsafe extern \"C\" fn LINKLAYER_PLAT_StopRadioEvt() {\n    trace!(\"LINKLAYER_PLAT_StopRadioEvt\");\n    // {\n    //   __HAL_RCC_RADIO_CLK_SLEEP_DISABLE();\n    //   NVIC_SetPriority(RADIO_INTR_NUM, RADIO_INTR_PRIO_LOW);\n    // #if (CFG_SCM_SUPPORTED == 1)\n    //   scm_notifyradiostate(SCM_RADIO_NOT_ACTIVE);\n    // #endif /* CFG_SCM_SUPPORTED */\n    nvic_set_priority(RADIO_INTR_NUM, pack_priority(mac::RADIO_INTR_PRIO_LOW));\n}\n\n// /**\n//   * @brief  Link Layer notification for RCO calibration start.\n//   * @param  None\n//   * @retval None\n//   */\n#[unsafe(no_mangle)]\npub unsafe extern \"C\" fn LINKLAYER_PLAT_RCOStartClbr() {\n    trace!(\"LINKLAYER_PLAT_RCOStartClbr\");\n    // #if (CFG_LPM_LEVEL != 0)\n    //   PWR_DisableSleepMode();\n    //   /* Disabling stop mode prevents also from entering in standby */\n    //   UTIL_LPM_SetStopMode(1U << CFG_LPM_LL_HW_RCO_CLBR, UTIL_LPM_DISABLE);\n    // #endif /* (CFG_LPM_LEVEL != 0) */\n    // #if (CFG_SCM_SUPPORTED == 1)\n    //   scm_setsystemclock(SCM_USER_LL_HW_RCO_CLBR, HSE_32MHZ);\n    //   while (LL_PWR_IsActiveFlag_VOS() == 0);\n    // #endif /* (CFG_SCM_SUPPORTED == 1) */\n}\n\n// /**\n//   * @brief  Link Layer notification for RCO calibration end.\n//   * @param  None\n//   * @retval None\n//   */\n#[unsafe(no_mangle)]\npub unsafe extern \"C\" fn LINKLAYER_PLAT_RCOStopClbr() {\n    trace!(\"LINKLAYER_PLAT_RCOStopClbr\");\n    // #if (CFG_LPM_LEVEL != 0)\n    //   PWR_EnableSleepMode();\n    //   UTIL_LPM_SetStopMode(1U << CFG_LPM_LL_HW_RCO_CLBR, UTIL_LPM_ENABLE);\n    // #endif /* (CFG_LPM_LEVEL != 0) */\n    // #if (CFG_SCM_SUPPORTED == 1)\n    //   scm_setsystemclock(SCM_USER_LL_HW_RCO_CLBR, HSE_16MHZ);\n    // #endif /* (CFG_SCM_SUPPORTED == 1) */\n}\n\n// /**\n//   * @brief  Link Layer requests temperature.\n//   * @param  None\n//   * @retval None\n//   */\n#[unsafe(no_mangle)]\npub unsafe extern \"C\" fn LINKLAYER_PLAT_RequestTemperature() {\n    trace!(\"LINKLAYER_PLAT_RequestTemperature\");\n    // #if (USE_TEMPERATURE_BASED_RADIO_CALIBRATION == 1)\n    //   ll_sys_bg_temperature_measurement();\n    // #endif /* USE_TEMPERATURE_BASED_RADIO_CALIBRATION */\n}\n\n// /**\n//   * @brief  PHY Start calibration.\n//   * @param  None\n//   * @retval None\n//   */\n#[unsafe(no_mangle)]\npub unsafe extern \"C\" fn LINKLAYER_PLAT_PhyStartClbr() {\n    trace!(\"LINKLAYER_PLAT_PhyStartClbr\");\n}\n\n// /**\n//   * @brief  PHY Stop calibration.\n//   * @param  None\n//   * @retval None\n//   */\n#[unsafe(no_mangle)]\npub unsafe extern \"C\" fn LINKLAYER_PLAT_PhyStopClbr() {\n    trace!(\"LINKLAYER_PLAT_PhyStopClbr\");\n}\n\n// /**\n//  * @brief Notify the upper layer that new Link Layer timings have been applied.\n//  * @param evnt_timing[in]: Evnt_timing_t pointer to structure contains drift time , execution time and scheduling time\n//  * @retval None.\n//  */\n#[unsafe(no_mangle)]\npub unsafe extern \"C\" fn LINKLAYER_PLAT_SCHLDR_TIMING_UPDATE_NOT(_timings: *const link_layer::Evnt_timing_t) {\n    trace!(\"LINKLAYER_PLAT_SCHLDR_TIMING_UPDATE_NOT: timings={:?}\", _timings);\n}\n\n// /**\n//   * @brief  Get the ST company ID.\n//   * @param  None\n//   * @retval Company ID\n//   */\n#[unsafe(no_mangle)]\npub unsafe extern \"C\" fn LINKLAYER_PLAT_GetSTCompanyID() -> u32 {\n    trace!(\"LINKLAYER_PLAT_GetSTCompanyID\");\n    // STMicroelectronics Bluetooth SIG Company Identifier\n    // TODO: Pull in update from latest stm32-generated-data\n    0x0030\n}\n\n// /**\n//   * @brief  Get the Unique Device Number (UDN).\n//   * @param  None\n//   * @retval UDN\n//   */\n#[unsafe(no_mangle)]\npub unsafe extern \"C\" fn LINKLAYER_PLAT_GetUDN() -> u32 {\n    trace!(\"LINKLAYER_PLAT_GetUDN\");\n    // Read the first 32 bits of the STM32 unique 96-bit ID\n    let uid = embassy_stm32::uid::uid();\n    u32::from_le_bytes([uid[0], uid[1], uid[2], uid[3]])\n}\n\n#[unsafe(no_mangle)]\npub unsafe extern \"C\" fn LINKLAYER_DEBUG_SIGNAL_SET() {\n    // Debug signal - no-op in release builds\n}\n\n#[unsafe(no_mangle)]\npub unsafe extern \"C\" fn LINKLAYER_DEBUG_SIGNAL_RESET() {\n    // Debug signal - no-op in release builds\n}\n\n#[unsafe(no_mangle)]\npub unsafe extern \"C\" fn LINKLAYER_DEBUG_SIGNAL_TOGGLE() {\n    // Debug signal - no-op in release builds\n}\n\n// BLE Platform functions required by BLE stack\n\n/// Initialize BLE platform layer\n#[unsafe(no_mangle)]\npub unsafe extern \"C\" fn BLEPLAT_Init() {\n    trace!(\"BLEPLAT_Init\");\n    // Platform initialization is already done in linklayer_plat_init()\n    // This function is called by BLE stack init\n}\n\n/// Get random numbers from RNG\n///\n/// # Arguments\n/// * `n` - Number of 32-bit random values to generate (1-4)\n/// * `val` - Pointer to array where random values will be stored\n#[unsafe(no_mangle)]\npub unsafe extern \"C\" fn BLEPLAT_RngGet(n: u8, val: *mut u32) {\n    trace!(\"BLEPLAT_RngGet: n={}\", n);\n\n    if val.is_null() || n == 0 {\n        return;\n    }\n\n    critical_section::with(|cs| {\n        HARDWARE_RNG\n            .as_ref()\n            .unwrap()\n            .borrow(cs)\n            .borrow_mut()\n            .fill_bytes(core::slice::from_raw_parts_mut(val as *mut u8, n as usize * 4));\n    });\n}\n\n/// AES ECB encrypt function\n///\n/// Used by the BLE stack for random address hash calculation.\n///\n/// # Arguments\n/// * `key` - 16-byte AES key\n/// * `input` - 16-byte input plaintext\n/// * `output` - 16-byte output ciphertext\n#[unsafe(no_mangle)]\npub unsafe extern \"C\" fn BLEPLAT_AesEcbEncrypt(key: *const u8, input: *const u8, output: *mut u8) {\n    trace!(\"BLEPLAT_AesEcbEncrypt\");\n\n    if key.is_null() || input.is_null() || output.is_null() {\n        error!(\"BLEPLAT_AesEcbEncrypt: null pointer\");\n        return;\n    }\n\n    // Use the STM32 AES hardware peripheral\n    // For now, use software AES as a fallback since we don't have async context\n    // In a production implementation, you'd want to use the hardware AES peripheral\n\n    // Simple software AES-128 ECB encryption using the AES peripheral in blocking mode\n    // Note: This is a simplified implementation. A proper implementation would use\n    // the STM32 AES hardware peripheral.\n\n    // Copy input to output as placeholder (real impl would do actual AES)\n    // For security-sensitive operations, implement proper AES here\n    core::ptr::copy_nonoverlapping(input, output, 16);\n\n    // TODO: Implement proper AES-128 ECB encryption using hardware AES peripheral\n    // For now, we use a stub that just copies data\n    // This is NOT secure and needs to be replaced with actual AES encryption\n    trace!(\"BLEPLAT_AesEcbEncrypt: WARNING - using stub implementation\");\n}\n\n/// AES CMAC set key function\n#[unsafe(no_mangle)]\npub unsafe extern \"C\" fn BLEPLAT_AesCmacSetKey(_key: *const u8) {\n    trace!(\"BLEPLAT_AesCmacSetKey\");\n    // TODO: Implement CMAC key setup\n}\n\n/// AES CMAC compute function\n#[unsafe(no_mangle)]\npub unsafe extern \"C\" fn BLEPLAT_AesCmacCompute(_input: *const u8, _input_length: u32, _output_tag: *mut u8) {\n    trace!(\"BLEPLAT_AesCmacCompute\");\n    // TODO: Implement CMAC computation\n}\n\n/// Start a BLE stack timer\n///\n/// # Arguments\n/// * `id` - Timer ID\n/// * `timeout` - Timeout in milliseconds\n///\n/// # Returns\n/// 0 on success, non-zero on error\n#[unsafe(no_mangle)]\npub unsafe extern \"C\" fn BLEPLAT_TimerStart(_id: u16, _timeout: u32) -> u8 {\n    trace!(\"BLEPLAT_TimerStart: id={}, timeout={}\", _id, _timeout);\n    // BLE timer implementation\n    // The BLE stack uses timers for various protocol timeouts\n    // For embassy integration, these would typically be handled by the async executor\n    // For now, we return success and let the BLE stack handle timeouts via polling\n    0 // Success\n}\n\n/// Stop a BLE stack timer\n///\n/// # Arguments\n/// * `id` - Timer ID to stop\n#[unsafe(no_mangle)]\npub unsafe extern \"C\" fn BLEPLAT_TimerStop(_id: u16) {\n    trace!(\"BLEPLAT_TimerStop: id={}\", _id);\n    // Stop the specified timer\n    // For embassy integration, this would cancel any pending timer\n}\n\n/// NVM store function for BLE stack\n#[unsafe(no_mangle)]\npub unsafe extern \"C\" fn BLEPLAT_NvmStore(_ptr: *const u64, _size: u16) {\n    trace!(\"BLEPLAT_NvmStore: size={}\", _size);\n    // NVM storage for BLE bonding data, etc.\n    // TODO: Implement persistent storage if needed\n}\n\n// BLEPLAT return codes\nconst BLEPLAT_BUSY: i32 = -2;\n\n/// Start P-256 public key generation\n/// This is used for BLE secure connections\n#[unsafe(no_mangle)]\npub unsafe extern \"C\" fn BLEPLAT_PkaStartP256Key(_local_private_key: *const u32) -> i32 {\n    trace!(\"BLEPLAT_PkaStartP256Key\");\n    // PKA (Public Key Accelerator) not implemented yet\n    // Return BUSY to indicate operation not supported\n    BLEPLAT_BUSY\n}\n\n/// Read result of P-256 public key generation\n#[unsafe(no_mangle)]\npub unsafe extern \"C\" fn BLEPLAT_PkaReadP256Key(_local_public_key: *mut u32) -> i32 {\n    trace!(\"BLEPLAT_PkaReadP256Key\");\n    // PKA not implemented\n    BLEPLAT_BUSY\n}\n\n/// Start DH key computation\n/// This is used for BLE secure connections\n#[unsafe(no_mangle)]\npub unsafe extern \"C\" fn BLEPLAT_PkaStartDhKey(_local_private_key: *const u32, _remote_public_key: *const u32) -> i32 {\n    trace!(\"BLEPLAT_PkaStartDhKey\");\n    // PKA not implemented\n    BLEPLAT_BUSY\n}\n\n/// Read result of DH key computation\n#[unsafe(no_mangle)]\npub unsafe extern \"C\" fn BLEPLAT_PkaReadDhKey(_dh_key: *mut u32) -> i32 {\n    trace!(\"BLEPLAT_PkaReadDhKey\");\n    // PKA not implemented\n    BLEPLAT_BUSY\n}\n\n/// BLE stack HCI event indication callback\n/// This is called by the BLE stack when HCI events arrive\n#[unsafe(no_mangle)]\npub unsafe extern \"C\" fn BLECB_Indication(data: *const u8, length: u16, _ext_data: *const u8, _ext_length: u16) -> u8 {\n    if data.is_null() || length == 0 {\n        return 1; // Error\n    }\n\n    // Convert to slice\n    let event_data = core::slice::from_raw_parts(data, length as usize);\n\n    #[cfg(feature = \"defmt\")]\n    defmt::trace!(\n        \"BLECB_Indication: event_code=0x{:02X}, length={}\",\n        event_data[0],\n        length\n    );\n\n    // Parse and queue the event for processing\n    if let Some(event) = super::hci::event::Event::parse(event_data) {\n        match super::hci::event::try_send_event(event) {\n            Ok(_) => {\n                #[cfg(feature = \"defmt\")]\n                defmt::trace!(\"Event queued successfully\");\n\n                // Signal BleStack_Process to run again\n                // This is equivalent to Sidewalk SDK's osSemaphoreRelease(BleHostSemaphore)\n                super::runner::BLE_WAKER.wake();\n            }\n            Err(_) => {\n                #[cfg(feature = \"defmt\")]\n                defmt::warn!(\"Event queue full, dropping event\");\n            }\n        }\n    } else {\n        #[cfg(feature = \"defmt\")]\n        defmt::warn!(\"Failed to parse HCI event\");\n    }\n\n    0 // Success\n}\n"
  },
  {
    "path": "embassy-stm32-wpan/src/wba/ll_sys/ll_sys_cs.rs",
    "content": "use crate::bindings::link_layer::{\n    LINKLAYER_PLAT_DisableIRQ, LINKLAYER_PLAT_DisableSpecificIRQ, LINKLAYER_PLAT_EnableIRQ,\n    LINKLAYER_PLAT_EnableSpecificIRQ, LINKLAYER_PLAT_PhyStartClbr, LINKLAYER_PLAT_PhyStopClbr,\n};\n\n// /**\n//   ******************************************************************************\n//   * @file    ll_sys_cs.c\n//   * @author  MCD Application Team\n//   * @brief   Link Layer IP system interface critical sections management\n//   ******************************************************************************\n//   * @attention\n//   *\n//   * Copyright (c) 2022 STMicroelectronics.\n//   * All rights reserved.\n//   *\n//   * This software is licensed under terms that can be found in the LICENSE file\n//   * in the root directory of this software component.\n//   * If no LICENSE file comes with this software, it is provided AS-IS.\n//   *\n//   ******************************************************************************\n//   */\n//\n// #include \"linklayer_plat.h\"\n// #include \"ll_sys.h\"\n// #include <stdint.h>\n//\n/**\n * @brief  Enable interrupts\n * @param  None\n * @retval None\n */\n#[unsafe(no_mangle)]\nunsafe extern \"C\" fn ll_sys_enable_irq() {\n    LINKLAYER_PLAT_EnableIRQ();\n}\n//\n// /**\n//   * @brief  Disable interrupts\n//   * @param  None\n//   * @retval None\n//   */\n#[unsafe(no_mangle)]\nunsafe extern \"C\" fn ll_sys_disable_irq() {\n    LINKLAYER_PLAT_DisableIRQ();\n}\n//\n// /**\n//   * @brief  Set the Current Interrupt Priority Mask.\n//   *         All interrupts with low priority level will be masked.\n//   * @param  None\n//   * @retval None\n//   */\n#[unsafe(no_mangle)]\nunsafe extern \"C\" fn ll_sys_enable_specific_irq(isr_type: u8) {\n    LINKLAYER_PLAT_EnableSpecificIRQ(isr_type);\n}\n//\n// /**\n//   * @brief  Restore the previous interrupt priority level\n//   * @param  None\n//   * @retval None\n//   */\n#[unsafe(no_mangle)]\nunsafe extern \"C\" fn ll_sys_disable_specific_irq(isr_type: u8) {\n    LINKLAYER_PLAT_DisableSpecificIRQ(isr_type);\n}\n//\n#[unsafe(no_mangle)]\nunsafe extern \"C\" fn ll_sys_phy_start_clbr() {\n    LINKLAYER_PLAT_PhyStartClbr();\n}\n//\n#[unsafe(no_mangle)]\nunsafe extern \"C\" fn ll_sys_phy_stop_clbr() {\n    LINKLAYER_PLAT_PhyStopClbr();\n}\n"
  },
  {
    "path": "embassy-stm32-wpan/src/wba/ll_sys/ll_sys_dp_slp.rs",
    "content": "use crate::bindings::link_layer::{\n    DPSLP_STATE_DEEP_SLEEP_DISABLE, DPSLP_STATE_DEEP_SLEEP_ENABLE, LINKLAYER_PLAT_DisableRadioIT,\n    LINKLAYER_PLAT_EnableRadioIT, LL_SYS_DP_SLP_STATE_T_LL_SYS_DP_SLP_DISABLED,\n    LL_SYS_DP_SLP_STATE_T_LL_SYS_DP_SLP_ENABLED, LL_SYS_STATUS_T_LL_SYS_ERROR, LL_SYS_STATUS_T_LL_SYS_OK,\n    OS_TIMER_PRIO_HG_PRIO_TMR, OS_TIMER_STATE_OSTIMERSTOPPED, OS_TIMER_TYPE_OS_TIMER_ONCE, SUCCESS, ble_stat_t,\n    ll_intf_cmn_le_set_dp_slp_mode, ll_sys_dp_slp_state_t, ll_sys_status_t, os_get_tmr_state, os_timer_create,\n    os_timer_id, os_timer_set_prio, os_timer_start, os_timer_stop,\n};\n\nmacro_rules! LL_DP_SLP_NO_WAKEUP {\n    () => {\n        !0u32\n    };\n}\n\nmacro_rules! LL_INTERNAL_TMR_US_TO_STEPS {\n    ($us:expr) => {\n        ((($us) * 4) / 125)\n    };\n}\n\n// /**\n//   ******************************************************************************\n//   * @file    ll_sys_dp_slp.c\n//   * @author  MCD Application Team\n//   * @brief   Link Layer IP system interface deep sleep management\n//   ******************************************************************************\n//   * @attention\n//   *\n//   * Copyright (c) 2022 STMicroelectronics.\n//   * All rights reserved.\n//   *\n//   * This software is licensed under terms that can be found in the LICENSE file\n//   * in the root directory of this software component.\n//   * If no LICENSE file comes with this software, it is provided AS-IS.\n//   *\n//   ******************************************************************************\n//   */\n//\n// #include \"linklayer_plat.h\"\n// #include \"ll_sys.h\"\n// #include \"ll_intf_cmn.h\"\n//\n// /* Link Layer deep sleep timer */\nstatic mut RADIO_DP_SLP_TMR_ID: os_timer_id = core::ptr::null_mut();\n//\n// /* Link Layer deep sleep state */\nstatic mut LINKLAYER_DP_SLP_STATE: ll_sys_dp_slp_state_t = LL_SYS_DP_SLP_STATE_T_LL_SYS_DP_SLP_DISABLED;\n//\n// /**\n//   * @brief  Initialize resources to handle deep sleep entry/exit\n//   * @param  None\n//   * @retval LL_SYS status\n//   */\n#[unsafe(no_mangle)]\nunsafe extern \"C\" fn ll_sys_dp_slp_init() -> ll_sys_status_t {\n    let mut return_status: ll_sys_status_t = LL_SYS_STATUS_T_LL_SYS_ERROR;\n\n    /* Create link layer timer for handling IP DEEP SLEEP mode */\n    RADIO_DP_SLP_TMR_ID = os_timer_create(\n        Some(ll_sys_dp_slp_wakeup_evt_clbk),\n        OS_TIMER_TYPE_OS_TIMER_ONCE,\n        core::ptr::null_mut(),\n    );\n\n    /* Set priority of deep sleep timer */\n    os_timer_set_prio(RADIO_DP_SLP_TMR_ID, OS_TIMER_PRIO_HG_PRIO_TMR);\n\n    if !RADIO_DP_SLP_TMR_ID.is_null() {\n        return_status = LL_SYS_STATUS_T_LL_SYS_OK;\n    }\n\n    return return_status;\n}\n//\n// /**\n//   * @brief  Link Layer deep sleep status getter\n//   * @param  None\n//   * @retval Link Layer deep sleep state\n//   */\n#[unsafe(no_mangle)]\nunsafe extern \"C\" fn ll_sys_dp_slp_get_state() -> ll_sys_dp_slp_state_t {\n    return LINKLAYER_DP_SLP_STATE;\n}\n//\n// /**\n//   * @brief  The Link Layer IP enters deep sleep mode\n//   * @param  dp_slp_duration    deep sleep duration in us\n//   * @retval LL_SYS status\n//   */\n#[unsafe(no_mangle)]\nunsafe extern \"C\" fn ll_sys_dp_slp_enter(dp_slp_duration: u32) -> ll_sys_status_t {\n    let cmd_status: ble_stat_t;\n    let os_status: i32;\n    let mut return_status: ll_sys_status_t = LL_SYS_STATUS_T_LL_SYS_ERROR;\n\n    /* Check if deep sleep timer has to be started */\n    if dp_slp_duration < LL_DP_SLP_NO_WAKEUP!() {\n        /* Start deep sleep timer */\n        os_status = os_timer_start(RADIO_DP_SLP_TMR_ID, LL_INTERNAL_TMR_US_TO_STEPS!(dp_slp_duration));\n    } else {\n        /* No timer started */\n        os_status = SUCCESS as i32;\n    }\n\n    if os_status == SUCCESS as i32 {\n        /* Switch Link Layer IP to DEEP SLEEP mode */\n        cmd_status = ll_intf_cmn_le_set_dp_slp_mode(DPSLP_STATE_DEEP_SLEEP_ENABLE as u8);\n        if cmd_status == SUCCESS {\n            LINKLAYER_DP_SLP_STATE = LL_SYS_DP_SLP_STATE_T_LL_SYS_DP_SLP_ENABLED;\n            return_status = LL_SYS_STATUS_T_LL_SYS_OK;\n        }\n    }\n\n    return return_status;\n}\n//\n// /**\n//   * @brief  The Link Layer IP exits deep sleep mode\n//   * @param  None\n//   * @retval LL_SYS status\n//   */\n#[unsafe(no_mangle)]\nunsafe extern \"C\" fn ll_sys_dp_slp_exit() -> ll_sys_status_t {\n    let cmd_status: ble_stat_t;\n    let mut return_status: ll_sys_status_t = LL_SYS_STATUS_T_LL_SYS_ERROR;\n\n    /* Disable radio interrupt */\n    LINKLAYER_PLAT_DisableRadioIT();\n\n    if LINKLAYER_DP_SLP_STATE == LL_SYS_DP_SLP_STATE_T_LL_SYS_DP_SLP_DISABLED {\n        /* Radio not in sleep mode */\n        return_status = LL_SYS_STATUS_T_LL_SYS_OK;\n    } else {\n        /* Switch Link Layer IP to SLEEP mode (by deactivate DEEP SLEEP mode) */\n        cmd_status = ll_intf_cmn_le_set_dp_slp_mode(DPSLP_STATE_DEEP_SLEEP_DISABLE as u8);\n        if cmd_status == SUCCESS {\n            LINKLAYER_DP_SLP_STATE = LL_SYS_DP_SLP_STATE_T_LL_SYS_DP_SLP_DISABLED;\n            return_status = LL_SYS_STATUS_T_LL_SYS_OK;\n        }\n\n        /* Stop the deep sleep wake-up timer if running */\n        if os_get_tmr_state(RADIO_DP_SLP_TMR_ID) != OS_TIMER_STATE_OSTIMERSTOPPED {\n            os_timer_stop(RADIO_DP_SLP_TMR_ID);\n        }\n    }\n\n    /* Re-enable radio interrupt */\n    LINKLAYER_PLAT_EnableRadioIT();\n\n    return return_status;\n}\n\n/**\n * @brief  Link Layer deep sleep wake-up timer callback\n * @param  ptr_arg    pointer passed through the callback\n * @retval LL_SYS status\n */\n#[unsafe(no_mangle)]\nunsafe extern \"C\" fn ll_sys_dp_slp_wakeup_evt_clbk(_ptr_arg: *const ::core::ffi::c_void) {\n    /* Link Layer IP exits from DEEP SLEEP mode */\n    ll_sys_dp_slp_exit();\n}\n"
  },
  {
    "path": "embassy-stm32-wpan/src/wba/ll_sys/ll_sys_intf.rs",
    "content": "use crate::bindings::link_layer::{\n    Evnt_timing_t, HostStack_Process, LINKLAYER_PLAT_AclkCtrl, LINKLAYER_PLAT_Assert, LINKLAYER_PLAT_ClockInit,\n    LINKLAYER_PLAT_DelayUs, LINKLAYER_PLAT_GetRNG, LINKLAYER_PLAT_RCOStartClbr, LINKLAYER_PLAT_RCOStopClbr,\n    LINKLAYER_PLAT_RequestTemperature, LINKLAYER_PLAT_SCHLDR_TIMING_UPDATE_NOT, LINKLAYER_PLAT_SetupRadioIT,\n    LINKLAYER_PLAT_SetupSwLowIT, LINKLAYER_PLAT_StartRadioEvt, LINKLAYER_PLAT_StopRadioEvt,\n    LINKLAYER_PLAT_TriggerSwLowIT, LINKLAYER_PLAT_WaitHclkRdy, MAX_NUM_CNCRT_STAT_MCHNS, emngr_can_mcu_sleep,\n    emngr_handle_all_events, ll_sys_schedule_bg_process,\n};\n\n// /**\n//   ******************************************************************************\n//   * @file    ll_sys_intf.c\n//   * @author  MCD Application Team\n//   * @brief   Link Layer IP general system interface\n//   ******************************************************************************\n//   * @attention\n//   *\n//   * Copyright (c) 2022 STMicroelectronics.\n//   * All rights reserved.\n//   *\n//   * This software is licensed under terms that can be found in the LICENSE file\n//   * in the root directory of this software component.\n//   * If no LICENSE file comes with this software, it is provided AS-IS.\n//   *\n//   ******************************************************************************\n//   */\n// #include <stdint.h>\n//\n// #include \"ll_sys.h\"\n// #include \"linklayer_plat.h\"\n// #include \"event_manager.h\"\n// #include \"ll_intf.h\"\n//\n/**\n * @brief  Initialize the Link Layer SoC dependencies\n * @param  None\n * @retval None\n */\n#[unsafe(no_mangle)]\nunsafe extern \"C\" fn ll_sys_init() {\n    LINKLAYER_PLAT_ClockInit();\n}\n//\n/**\n * @brief  Blocking delay in us\n * @param  None\n * @retval None\n */\n#[unsafe(no_mangle)]\nunsafe extern \"C\" fn ll_sys_delay_us(delay: u32) {\n    LINKLAYER_PLAT_DelayUs(delay);\n}\n\n/**\n * @brief  Assert checking\n * @param  None\n * @retval None\n */\n#[unsafe(no_mangle)]\nunsafe extern \"C\" fn ll_sys_assert(condition: u8) {\n    LINKLAYER_PLAT_Assert(condition);\n}\n\n/**\n * @brief  Radio active clock management\n * @param  None\n * @retval None\n */\n#[unsafe(no_mangle)]\nunsafe extern \"C\" fn ll_sys_radio_ack_ctrl(enable: u8) {\n    LINKLAYER_PLAT_AclkCtrl(enable);\n}\n\n/**\n * @brief  Link Layer waits for radio bus clock ready\n * @param  None\n * @retval None\n */\n#[unsafe(no_mangle)]\nunsafe extern \"C\" fn ll_sys_radio_wait_for_busclkrdy() {\n    LINKLAYER_PLAT_WaitHclkRdy();\n}\n\n/**\n * @brief  Get RNG number for the Link Layer IP\n * @param  None\n * @retval None\n */\n#[unsafe(no_mangle)]\nunsafe extern \"C\" fn ll_sys_get_rng(ptr_rnd: *mut u8, len: u32) {\n    LINKLAYER_PLAT_GetRNG(ptr_rnd, len);\n}\n\n/**\n * @brief  Initialize the main radio interrupt\n * @param  intr_cb    radio interrupt callback to link with the radio IRQ\n * @retval None\n */\n#[unsafe(no_mangle)]\nunsafe extern \"C\" fn ll_sys_setup_radio_intr(intr_cb: ::core::option::Option<unsafe extern \"C\" fn()>) {\n    LINKLAYER_PLAT_SetupRadioIT(intr_cb);\n}\n\n/**\n * @brief  Initialize the radio SW low interrupt\n * @param  intr_cb    radio SW low interrupt interrupt callback to link\n *                    with the defined interrupt vector\n * @retval None\n */\n#[unsafe(no_mangle)]\nunsafe extern \"C\" fn ll_sys_setup_radio_sw_low_intr(intr_cb: ::core::option::Option<unsafe extern \"C\" fn()>) {\n    LINKLAYER_PLAT_SetupSwLowIT(intr_cb);\n}\n\n/**\n * @brief  Trigger the radio SW low interrupt\n * @param  None\n * @retval None\n */\n#[unsafe(no_mangle)]\nunsafe extern \"C\" fn ll_sys_radio_sw_low_intr_trigger(priority: u8) {\n    LINKLAYER_PLAT_TriggerSwLowIT(priority);\n}\n\n/**\n * @brief  Link Layer radio activity event notification\n * @param  start      start/end of radio event\n * @retval None\n */\n#[unsafe(no_mangle)]\nunsafe extern \"C\" fn ll_sys_radio_evt_not(start: u8) {\n    if start != 0 {\n        LINKLAYER_PLAT_StartRadioEvt();\n    } else {\n        LINKLAYER_PLAT_StopRadioEvt();\n    }\n}\n\n/**\n * @brief  Link Layer RCO calibration notification\n * @param  start      start/end of RCO calibration\n * @retval None\n */\n#[unsafe(no_mangle)]\nunsafe extern \"C\" fn ll_sys_rco_clbr_not(start: u8) {\n    if start != 0 {\n        LINKLAYER_PLAT_RCOStartClbr();\n    } else {\n        LINKLAYER_PLAT_RCOStopClbr();\n    }\n}\n\n/**\n * @brief  Link Layer temperature request\n * @param  None\n * @retval None\n */\n#[unsafe(no_mangle)]\nunsafe extern \"C\" fn ll_sys_request_temperature() {\n    LINKLAYER_PLAT_RequestTemperature();\n}\n\n/**\n * @brief  Link Layer background task pcoessing procedure\n * @param  None\n * @retval None\n */\n#[unsafe(no_mangle)]\nunsafe extern \"C\" fn ll_sys_bg_process() {\n    if emngr_can_mcu_sleep() == 0 {\n        emngr_handle_all_events();\n\n        HostStack_Process();\n    }\n\n    if emngr_can_mcu_sleep() == 0 {\n        ll_sys_schedule_bg_process();\n    }\n}\n\n#[unsafe(no_mangle)]\nunsafe extern \"C\" fn ll_sys_schldr_timing_update_not(p_evnt_timing: *mut Evnt_timing_t) {\n    LINKLAYER_PLAT_SCHLDR_TIMING_UPDATE_NOT(p_evnt_timing);\n}\n\n/**\n * @brief  Get the number of concurrent state machines for the Link Layer\n * @param  None\n * @retval Supported number of concurrent state machines\n */\n#[unsafe(no_mangle)]\nunsafe extern \"C\" fn ll_sys_get_concurrent_state_machines_num() -> u8 {\n    return MAX_NUM_CNCRT_STAT_MCHNS as u8;\n}\n//\n// __WEAK void HostStack_Process(void)\n// {\n//\n// }\n"
  },
  {
    "path": "embassy-stm32-wpan/src/wba/ll_sys/ll_sys_startup.rs",
    "content": "#[cfg(feature = \"wba_ble\")]\nuse crate::bindings::ble::{BleStack_Init, BleStack_init_t, tBleStatus};\nuse crate::bindings::link_layer::{\n    LL_SYS_STATUS_T_LL_SYS_OK, ll_sys_assert, ll_sys_bg_process_init, ll_sys_config_params, ll_sys_dp_slp_init,\n    ll_sys_status_t,\n};\n#[cfg(feature = \"wba_ble\")]\nuse crate::bindings::link_layer::{\n    ble_buff_hdr_p, hci_dispatch_tbl, hci_get_dis_tbl, hst_cbk, ll_intf_init, ll_intf_rgstr_hst_cbk,\n    ll_intf_rgstr_hst_cbk_ll_queue_full,\n};\n\n/// BLE status code for success\n#[cfg(feature = \"wba_ble\")]\nconst BLE_STATUS_SUCCESS: tBleStatus = 0;\n#[cfg(feature = \"wba_mac\")]\nuse crate::bindings::mac::ST_MAC_preInit;\n// /**\n//   ******************************************************************************\n//   * @file    ll_sys_startup.c\n//   * @author  MCD Application Team\n//   * @brief   Link Layer IP system interface startup module\n//   ******************************************************************************\n//   * @attention\n//   *\n//   * Copyright (c) 2022 STMicroelectronics.\n//   * All rights reserved.\n//   *\n//   * This software is licensed under terms that can be found in the LICENSE file\n//   * in the root directory of this software component.\n//   * If no LICENSE file comes with this software, it is provided AS-IS.\n//   *\n//   ******************************************************************************\n//   */\n//\n// #include \"ll_fw_config.h\"\n// #include \"ll_sys.h\"\n// #include \"ll_intf.h\"\n// #include \"ll_sys_startup.h\"\n// #include \"common_types.h\"\n// #if defined(MAC)\n// #ifndef OPENTHREAD_CONFIG_FILE\n// /* Projects with MAC Layer (i.e. 15.4 except Thread) */\n// #include \"st_mac_802_15_4_sap.h\"\n// #endif /* OPENTHREAD_CONFIG_FILE */\n// #endif /* MAC */\n//\n\n#[allow(dead_code)]\n/**\n * @brief  Missed HCI event flag\n */\nstatic mut MISSED_HCI_EVENT_FLAG: u8 = 0;\n\n// static void ll_sys_dependencies_init(void);\n// #if SUPPORT_BLE\n\n#[cfg(feature = \"wba_ble\")]\n#[allow(dead_code)]\nunsafe extern \"C\" fn ll_sys_event_missed_cb(_ptr_evnt_hdr: ble_buff_hdr_p) {\n    MISSED_HCI_EVENT_FLAG = 1;\n}\n\n// ll_sys_ble_cntrl_init is called by BleStack_Init from the library.\n// We must provide this function as it's expected as a callback.\n\n#[cfg(feature = \"wba_ble\")]\n/**\n * @brief  Initialize the Link Layer IP BLE controller\n * @param  host_callback - callback function for HCI events\n * @retval None\n *\n * This function is called by BleStack_Init internally.\n */\n#[unsafe(no_mangle)]\npub unsafe extern \"C\" fn ll_sys_ble_cntrl_init(host_callback: hst_cbk) {\n    #[cfg(feature = \"defmt\")]\n    defmt::trace!(\"ll_sys_ble_cntrl_init: starting\");\n\n    let p_hci_dis_tbl: *const hci_dispatch_tbl = core::ptr::null();\n\n    hci_get_dis_tbl(&p_hci_dis_tbl as *const *const _ as *mut *const _);\n    #[cfg(feature = \"defmt\")]\n    defmt::trace!(\"ll_sys_ble_cntrl_init: hci_get_dis_tbl done\");\n\n    ll_intf_init(p_hci_dis_tbl);\n    #[cfg(feature = \"defmt\")]\n    defmt::trace!(\"ll_sys_ble_cntrl_init: ll_intf_init done\");\n\n    ll_intf_rgstr_hst_cbk(host_callback);\n    #[cfg(feature = \"defmt\")]\n    defmt::trace!(\"ll_sys_ble_cntrl_init: ll_intf_rgstr_hst_cbk done\");\n\n    ll_intf_rgstr_hst_cbk_ll_queue_full(Some(ll_sys_event_missed_cb));\n    #[cfg(feature = \"defmt\")]\n    defmt::trace!(\"ll_sys_ble_cntrl_init: ll_intf_rgstr_hst_cbk_ll_queue_full done\");\n\n    ll_sys_dependencies_init();\n    #[cfg(feature = \"defmt\")]\n    defmt::trace!(\"ll_sys_ble_cntrl_init: ll_sys_dependencies_init done\");\n}\n\n// NOTE: init_ble_link_layer and init_ble_link_layer_minimal have been removed.\n// Use init_ble_stack() instead, which uses BleStack_Init for proper initialization.\n\n#[cfg(feature = \"wba_ble\")]\n/// Complete the BLE link layer initialization\n/// This should be called after the sequencer is running\npub fn complete_ble_link_layer_init() {\n    #[cfg(feature = \"defmt\")]\n    defmt::trace!(\"complete_ble_link_layer_init: starting\");\n\n    unsafe {\n        ll_sys_dependencies_init();\n    }\n\n    #[cfg(feature = \"defmt\")]\n    defmt::trace!(\"complete_ble_link_layer_init: done\");\n}\n\n// ========================================================================\n// BleStack_Init based initialization (recommended approach)\n// ========================================================================\n\n/// BLE stack configuration parameters\n#[cfg(feature = \"wba_ble\")]\npub mod ble_config {\n    /// Maximum number of simultaneous BLE connections\n    pub const CFG_BLE_NUM_LINK: u8 = 2;\n    /// Maximum ATT MTU size\n    pub const CFG_BLE_ATT_MTU_MAX: u16 = 247;\n    /// Number of GATT attributes\n    pub const CFG_BLE_NUM_GATT_ATTRIBUTES: u16 = 68;\n    /// Number of GATT services\n    pub const CFG_BLE_NUM_GATT_SERVICES: u16 = 8;\n    /// GATT attribute value array size\n    pub const CFG_BLE_ATT_VALUE_ARRAY_SIZE: u16 = 1344;\n    /// Prepare write list size (BLE_DEFAULT_PREP_WRITE_LIST_SIZE = BLE_PREP_WRITE_X_ATT(512) = DIVC(512,18)*2 = 58)\n    pub const CFG_BLE_PREPARE_WRITE_LIST_SIZE: u8 = 58;\n    /// Memory block count margin\n    pub const CFG_BLE_MBLOCK_COUNT_MARGIN: u16 = 0x15; // 21\n    /// Maximum COC number\n    pub const CFG_BLE_COC_NBR_MAX: u8 = 0;\n    /// Maximum COC MPS\n    pub const CFG_BLE_COC_MPS_MAX: u16 = 0;\n    /// Maximum COC initiator number\n    pub const CFG_BLE_COC_INITIATOR_NBR_MAX: u8 = 0;\n    /// Number of EATT bearers per link\n    pub const CFG_BLE_EATT_BEARER_PER_LINK: u8 = 0;\n    /// NVM maximum size (in 64-bit words)\n    pub const CFG_BLE_NVM_SIZE_MAX: u16 = 256;\n    /// BLE options\n    pub const CFG_BLE_OPTIONS: u16 = 0x0D; // DEV_NAME_READ_ONLY | REDUCED_DB_IN_NVM | CS_ALGO_2\n\n    // Memory block size (from ble_bufsize.h)\n    const BLE_MEM_BLOCK_SIZE: usize = 32;\n    // Fixed buffer size for full stack (from ble_bufsize.h)\n    const BLE_FIXED_BUFFER_SIZE_BYTES: usize = 516;\n    // Per-link size for full stack (from ble_bufsize.h)\n    const BLE_PER_LINK_SIZE_BYTES: usize = 192;\n\n    /// Helper: divide and round up\n    const fn divc(a: usize, b: usize) -> usize {\n        (a + b - 1) / b\n    }\n\n    /// Calculate memory blocks for TX\n    const fn mem_block_x_tx(mtu: usize) -> usize {\n        divc(mtu + 4, BLE_MEM_BLOCK_SIZE) + 1\n    }\n\n    /// Calculate memory blocks for PTX (parallel TX)\n    const fn mem_block_x_ptx(n_link: usize) -> usize {\n        n_link // Full stack\n    }\n\n    /// Calculate memory blocks for RX\n    const fn mem_block_x_rx(mtu: usize, n_link: usize) -> usize {\n        (divc(mtu + 4, BLE_MEM_BLOCK_SIZE) + 2) * n_link + 1\n    }\n\n    /// Calculate total memory blocks needed\n    const fn mem_block_x_mtu(mtu: usize, n_link: usize) -> usize {\n        mem_block_x_tx(mtu) + mem_block_x_ptx(n_link) + mem_block_x_rx(mtu, n_link)\n    }\n\n    /// Calculate MBLOCK_COUNT based on ble_bufsize.h formula\n    pub const fn mblock_count() -> usize {\n        let pw = CFG_BLE_PREPARE_WRITE_LIST_SIZE as usize;\n        let mtu = CFG_BLE_ATT_MTU_MAX as usize;\n        let n_link = CFG_BLE_NUM_LINK as usize;\n        let margin = CFG_BLE_MBLOCK_COUNT_MARGIN as usize;\n\n        let mem_blocks = mem_block_x_mtu(mtu, n_link);\n        let secure_conn_min = 4;\n        let max_blocks = if mem_blocks > secure_conn_min {\n            mem_blocks\n        } else {\n            secure_conn_min\n        };\n\n        pw + max_blocks + margin\n    }\n\n    /// Calculate GATT buffer size using BLE_TOTAL_BUFFER_SIZE_GATT macro formula\n    /// Formula: ((((att_value_array_size) - 1) | 3) + 1) + (40 * num_gatt_attributes) + (48 * num_gatt_services)\n    pub const fn gatt_buffer_size() -> usize {\n        let att_val_size = CFG_BLE_ATT_VALUE_ARRAY_SIZE as usize;\n        let num_attr = CFG_BLE_NUM_GATT_ATTRIBUTES as usize;\n        let num_serv = CFG_BLE_NUM_GATT_SERVICES as usize;\n\n        // Align att_value_array_size to 4-byte boundary\n        let aligned_att_val = ((att_val_size - 1) | 3) + 1;\n\n        aligned_att_val + (40 * num_attr) + (48 * num_serv)\n    }\n\n    /// Calculate dynamic allocation buffer size using BLE_TOTAL_BUFFER_SIZE macro formula\n    /// Formula: 16 + BLE_FIXED_BUFFER_SIZE_BYTES + (BLE_PER_LINK_SIZE_BYTES * n_link) + ((BLE_MEM_BLOCK_SIZE + 8) * mblocks_count)\n    pub const fn dyn_alloc_buffer_size() -> usize {\n        let n_link = CFG_BLE_NUM_LINK as usize;\n        let mblocks = mblock_count();\n\n        16 + BLE_FIXED_BUFFER_SIZE_BYTES + (BLE_PER_LINK_SIZE_BYTES * n_link) + ((BLE_MEM_BLOCK_SIZE + 8) * mblocks)\n    }\n}\n\n/// Static buffers for BLE stack\n#[cfg(feature = \"wba_ble\")]\nmod ble_buffers {\n    use super::ble_config;\n\n    /// Dynamic allocation buffer for BLE stack\n    #[repr(align(4))]\n    pub struct DynAllocBuffer(pub [u8; ble_config::dyn_alloc_buffer_size()]);\n\n    /// GATT buffer\n    #[repr(align(4))]\n    pub struct GattBuffer(pub [u8; ble_config::gatt_buffer_size()]);\n\n    /// NVM cache buffer\n    #[repr(align(8))]\n    pub struct NvmCacheBuffer(pub [u64; (ble_config::CFG_BLE_NVM_SIZE_MAX as usize + 7) / 8]);\n\n    pub static mut DYN_ALLOC_BUFFER: DynAllocBuffer = DynAllocBuffer([0u8; ble_config::dyn_alloc_buffer_size()]);\n    pub static mut GATT_BUFFER: GattBuffer = GattBuffer([0u8; ble_config::gatt_buffer_size()]);\n    pub static mut NVM_CACHE_BUFFER: NvmCacheBuffer =\n        NvmCacheBuffer([0u64; (ble_config::CFG_BLE_NVM_SIZE_MAX as usize + 7) / 8]);\n}\n\n#[cfg(feature = \"wba_ble\")]\n/// Initialize the BLE stack using the high-level BleStack_Init API\n///\n/// This is the recommended initialization method as it properly sets up\n/// all memory management and internal state before calling ll_intf_init.\n///\n/// Returns Ok(()) on success, Err with status code on failure.\npub fn init_ble_stack() -> Result<(), u8> {\n    use ble_config::*;\n\n    use crate::wba::linklayer_plat::LINKLAYER_PLAT_ClockInit;\n\n    #[cfg(feature = \"defmt\")]\n    defmt::info!(\"init_ble_stack: starting BLE stack initialization\");\n\n    #[cfg(feature = \"defmt\")]\n    {\n        defmt::debug!(\"init_ble_stack: buffer sizes:\");\n        defmt::debug!(\"  DYN_ALLOC_BUFFER: {} bytes\", dyn_alloc_buffer_size());\n        defmt::debug!(\"  GATT_BUFFER: {} bytes\", gatt_buffer_size());\n        defmt::debug!(\"  mblockCount: {}\", mblock_count());\n        defmt::debug!(\"  numOfLinks: {}\", CFG_BLE_NUM_LINK);\n        defmt::debug!(\"  attMtu: {}\", CFG_BLE_ATT_MTU_MAX);\n    }\n\n    unsafe {\n        // 1. Enable radio clock first\n        LINKLAYER_PLAT_ClockInit();\n\n        #[cfg(feature = \"defmt\")]\n        defmt::trace!(\"init_ble_stack: clock init done\");\n\n        // 2. Prepare BleStack_init_t structure\n        let init_params = BleStack_init_t {\n            bleStartRamAddress: ble_buffers::DYN_ALLOC_BUFFER.0.as_mut_ptr(),\n            total_buffer_size: ble_buffers::DYN_ALLOC_BUFFER.0.len() as u32,\n            nvm_cache_buffer: ble_buffers::NVM_CACHE_BUFFER.0.as_mut_ptr(),\n            nvm_cache_size: CFG_BLE_NVM_SIZE_MAX - 1,\n            nvm_cache_max_size: CFG_BLE_NVM_SIZE_MAX,\n            bleStartRamAddress_GATT: ble_buffers::GATT_BUFFER.0.as_mut_ptr(),\n            total_buffer_size_GATT: ble_buffers::GATT_BUFFER.0.len() as u32,\n            gatt_long_write_buffer: core::ptr::null_mut(),\n            extra_data_buffer: core::ptr::null_mut(),\n            extra_data_buffer_size: 0,\n            host_event_fifo_buffer: core::ptr::null_mut(),\n            host_event_fifo_buffer_size: 0,\n            numAttrRecord: CFG_BLE_NUM_GATT_ATTRIBUTES,\n            numAttrServ: CFG_BLE_NUM_GATT_SERVICES,\n            attrValueArrSize: CFG_BLE_ATT_VALUE_ARRAY_SIZE,\n            numOfLinks: CFG_BLE_NUM_LINK,\n            prWriteListSize: CFG_BLE_PREPARE_WRITE_LIST_SIZE,\n            mblockCount: mblock_count() as u16,\n            max_add_eatt_bearers: CFG_BLE_EATT_BEARER_PER_LINK * CFG_BLE_NUM_LINK,\n            attMtu: CFG_BLE_ATT_MTU_MAX,\n            max_coc_mps: CFG_BLE_COC_MPS_MAX,\n            max_coc_nbr: CFG_BLE_COC_NBR_MAX,\n            max_coc_initiator_nbr: CFG_BLE_COC_INITIATOR_NBR_MAX,\n            options: CFG_BLE_OPTIONS,\n            debug: 0x10, // BLE_DEBUG_RAND_ADDR_INIT - required for random address support\n        };\n\n        #[cfg(feature = \"defmt\")]\n        defmt::trace!(\"init_ble_stack: calling BleStack_Init\");\n\n        // 3. Initialize the BLE stack\n        let status: tBleStatus = BleStack_Init(&init_params);\n\n        if status != BLE_STATUS_SUCCESS {\n            #[cfg(feature = \"defmt\")]\n            defmt::error!(\"init_ble_stack: BleStack_Init failed with status 0x{:02X}\", status);\n            return Err(status);\n        }\n\n        #[cfg(feature = \"defmt\")]\n        defmt::trace!(\"init_ble_stack: BleStack_Init succeeded\");\n\n        // 4. Call ll_sys_dependencies_init after BleStack_Init\n        // This is required for deep sleep, background tasks, etc.\n        ll_sys_dependencies_init();\n\n        #[cfg(feature = \"defmt\")]\n        defmt::info!(\"init_ble_stack: BLE stack initialized successfully\");\n    }\n\n    Ok(())\n}\n// #endif /* SUPPORT_BLE */\n// #if defined(MAC)\n// #ifndef OPENTHREAD_CONFIG_FILE\n#[cfg(feature = \"wba_mac\")]\n/**\n * @brief  Initialize the Link Layer IP 802.15.4 MAC controller\n * @param  None\n * @retval None\n */\n#[unsafe(no_mangle)]\nunsafe extern \"C\" fn ll_sys_mac_cntrl_init() {\n    ST_MAC_preInit();\n    ll_sys_dependencies_init();\n}\n// #endif /* OPENTHREAD_CONFIG_FILE */\n// #endif /* MAC */\n/**\n * @brief  Start the Link Layer IP in OpenThread configuration\n * @param  None\n * @retval None\n */\n#[unsafe(no_mangle)]\nunsafe extern \"C\" fn ll_sys_thread_init() {\n    ll_sys_dependencies_init();\n}\n\n/**\n * @brief  Initialize the Link Layer resources for startup.\n *         This includes: - Deep Sleep feature resources\n *                        - Link Layer background task\n * @param  None\n * @retval None\n */\nunsafe fn ll_sys_dependencies_init() {\n    static mut IS_LL_INITIALIZED: u8 = 0;\n    let dp_slp_status: ll_sys_status_t;\n\n    #[cfg(feature = \"defmt\")]\n    defmt::trace!(\"ll_sys_dependencies_init: starting\");\n\n    /* Ensure Link Layer resources are created only once */\n    if IS_LL_INITIALIZED == 1 {\n        #[cfg(feature = \"defmt\")]\n        defmt::trace!(\"ll_sys_dependencies_init: already initialized\");\n        return;\n    }\n    IS_LL_INITIALIZED = 1;\n\n    /* Deep sleep feature initialization */\n    #[cfg(feature = \"defmt\")]\n    defmt::trace!(\"ll_sys_dependencies_init: calling ll_sys_dp_slp_init\");\n    dp_slp_status = ll_sys_dp_slp_init();\n    #[cfg(feature = \"defmt\")]\n    defmt::trace!(\n        \"ll_sys_dependencies_init: ll_sys_dp_slp_init done, status={}\",\n        dp_slp_status\n    );\n    ll_sys_assert((dp_slp_status == LL_SYS_STATUS_T_LL_SYS_OK) as u8);\n\n    /* Background task initialization */\n    #[cfg(feature = \"defmt\")]\n    defmt::trace!(\"ll_sys_dependencies_init: calling ll_sys_bg_process_init\");\n    ll_sys_bg_process_init();\n    #[cfg(feature = \"defmt\")]\n    defmt::trace!(\"ll_sys_dependencies_init: ll_sys_bg_process_init done\");\n\n    /* Link Layer user parameters application */\n    #[cfg(feature = \"defmt\")]\n    defmt::trace!(\"ll_sys_dependencies_init: calling ll_sys_config_params\");\n    ll_sys_config_params();\n    #[cfg(feature = \"defmt\")]\n    defmt::trace!(\"ll_sys_dependencies_init: ll_sys_config_params done\");\n}\n"
  },
  {
    "path": "embassy-stm32-wpan/src/wba/ll_sys/ll_version.rs",
    "content": "use crate::bindings::link_layer::{\n    LL_SYS_BRIEF_VERSION_MAJOR, LL_SYS_BRIEF_VERSION_MAJOR_MASK, LL_SYS_BRIEF_VERSION_MAJOR_POS,\n    LL_SYS_BRIEF_VERSION_MINOR, LL_SYS_BRIEF_VERSION_MINOR_MASK, LL_SYS_BRIEF_VERSION_MINOR_POS,\n    LL_SYS_BRIEF_VERSION_PATCH, LL_SYS_BRIEF_VERSION_PATCH_MASK, LL_SYS_BRIEF_VERSION_PATCH_POS,\n};\n\n// /**\n//   ******************************************************************************\n//   * @file    ll_version.c\n//   * @author  MCD Application Team\n//   * @brief   Link Layer version interface\n//   ******************************************************************************\n//   * @attention\n//   *\n//   * Copyright (c) 2025 STMicroelectronics.\n//   * All rights reserved.\n//   *\n//   * This software is licensed under terms that can be found in the LICENSE file\n//   * in the root directory of this software component.\n//   * If no LICENSE file comes with this software, it is provided AS-IS.\n//   *\n//   ******************************************************************************\n//   */\n//\n// /* Includes ------------------------------------------------------------------*/\n// /* Integer types */\n// #include <stdint.h>\n//\n// /* Own header file  */\n// #include \"ll_version.h\"\n//\n// /* Temporary header file for version tracking */\n// #include \"ll_tmp_version.h\"\n//\n// /* Private defines -----------------------------------------------------------*/\n// /**\n//  * @brief Magic keyword to identify the system version when debugging\n//  */\n//  #define LL_SYS_MAGIC_KEYWORD  0xDEADBEEF\n\nconst LL_SYS_MAGIC_KEYWORD: u32 = 0xDEADBEEF;\n\n//\n// /* Private macros ------------------------------------------------------------*/\n// /* Macro to set a specific field value */\n// #define LL_SYS_SET_FIELD_VALUE(value, mask, pos) \\\n//   (((value) << (pos)) & (mask))\n\nmacro_rules! LL_SYS_SET_FIELD_VALUE {\n    ($value:expr, $mask:expr, $pos:expr) => {\n        ((($value) << ($pos)) & ($mask))\n    };\n}\n\n//\n// /* Private typedef -----------------------------------------------------------*/\n// /**\n//   * @brief Link Layer system version structure definition\n//   */\n#[allow(non_camel_case_types)]\nstruct ll_sys_version_t {\n    #[allow(unused)]\n    magic_key_word: u32, /* Magic key word to identify the system version */\n    version: u32, /* System version - i.e.: short hash of latest commit */\n}\n//\n// /* Private variables ---------------------------------------------------------*/\n// /**\n//  * @brief Link Layer brief version definition\n//  */\nconst LL_SYS_BRIEF_VERSION: u8 = LL_SYS_SET_FIELD_VALUE!(\n    LL_SYS_BRIEF_VERSION_MAJOR as u8,\n    LL_SYS_BRIEF_VERSION_MAJOR_MASK as u8,\n    LL_SYS_BRIEF_VERSION_MAJOR_POS as u8\n) | LL_SYS_SET_FIELD_VALUE!(\n    LL_SYS_BRIEF_VERSION_MINOR as u8,\n    LL_SYS_BRIEF_VERSION_MINOR_MASK as u8,\n    LL_SYS_BRIEF_VERSION_MINOR_POS as u8\n) | LL_SYS_SET_FIELD_VALUE!(\n    LL_SYS_BRIEF_VERSION_PATCH as u8,\n    LL_SYS_BRIEF_VERSION_PATCH_MASK as u8,\n    LL_SYS_BRIEF_VERSION_PATCH_POS as u8\n);\n//\n// /**\n//  * @brief Link Layer system version structure definition\n//  */\nconst LL_SYS_SYSTEM_VERSION: ll_sys_version_t = ll_sys_version_t {\n    magic_key_word: LL_SYS_MAGIC_KEYWORD,\n    version: 0, // LL_SYS_SYSTEM_VERSION,\n};\n//\n// /**\n//  * @brief Link Layer source version structure definition\n//  */\nconst LL_SYS_SOURCE_VERSION: ll_sys_version_t = ll_sys_version_t {\n    magic_key_word: LL_SYS_MAGIC_KEYWORD,\n    version: 0, // LL_SYS_SOURCE_VERSION\n};\n//\n// /* Functions Definition ------------------------------------------------------*/\n#[unsafe(no_mangle)]\nunsafe extern \"C\" fn ll_sys_get_brief_fw_version() -> u8 {\n    return LL_SYS_BRIEF_VERSION;\n}\n\n#[unsafe(no_mangle)]\nunsafe extern \"C\" fn ll_sys_get_system_fw_version() -> u32 {\n    return LL_SYS_SYSTEM_VERSION.version;\n}\n\n#[unsafe(no_mangle)]\nunsafe extern \"C\" fn ll_sys_get_source_fw_version() -> u32 {\n    return LL_SYS_SOURCE_VERSION.version;\n}\n"
  },
  {
    "path": "embassy-stm32-wpan/src/wba/ll_sys/mod.rs",
    "content": "mod ll_sys_cs;\nmod ll_sys_dp_slp;\nmod ll_sys_intf;\nmod ll_sys_startup;\nmod ll_version;\n\n#[cfg(feature = \"wba_ble\")]\npub use ll_sys_startup::{complete_ble_link_layer_init, init_ble_stack};\n"
  },
  {
    "path": "embassy-stm32-wpan/src/wba/ll_sys_if.rs",
    "content": "#![cfg(feature = \"wba\")]\n// /* USER CODE BEGIN Header */\n// /**\n//   ******************************************************************************\n//   * @file    ll_sys_if.c\n//   * @author  MCD Application Team\n//   * @brief   Source file for initiating system\n//   ******************************************************************************\n//   * @attention\n//   *\n//   * Copyright (c) 2022 STMicroelectronics.\n//   * All rights reserved.\n//   *\n//   * This software is licensed under terms that can be found in the LICENSE file\n//   * in the root directory of this software component.\n//   * If no LICENSE file comes with this software, it is provided AS-IS.\n//   *\n//   ******************************************************************************\n//   */\n// /* USER CODE END Header */\n//\n// #include \"main.h\"\n// #include \"app_common.h\"\n// #include \"app_conf.h\"\n// #include \"log_module.h\"\n// #include \"ll_intf_cmn.h\"\n// #include \"ll_sys.h\"\n// #include \"ll_sys_if.h\"\n// #include \"stm32_rtos.h\"\n// #include \"utilities_common.h\"\n// #if (USE_TEMPERATURE_BASED_RADIO_CALIBRATION == 1)\n// #include \"temp_measurement.h\"\n// #endif /* (USE_TEMPERATURE_BASED_RADIO_CALIBRATION == 1) */\n// #if (CFG_LPM_STANDBY_SUPPORTED == 0)\n// extern void profile_reset(void);\n// #endif\n// /* Private defines -----------------------------------------------------------*/\n// /* Radio event scheduling method - must be set at 1 */\n// #define USE_RADIO_LOW_ISR                   (1)\n// #define NEXT_EVENT_SCHEDULING_FROM_ISR      (1)\n//\n// /* USER CODE BEGIN PD */\n//\n// /* USER CODE END PD */\n//\n// /* Private macros ------------------------------------------------------------*/\n// /* USER CODE BEGIN PM */\n//\n// /* USER CODE END PM */\n//\n// /* Private constants ---------------------------------------------------------*/\n// /* USER CODE BEGIN PC */\n//\n// /* USER CODE END PC */\n//\n// /* Private variables ---------------------------------------------------------*/\n// /* USER CODE BEGIN PV */\n//\n// /* USER CODE END PV */\n//\n// /* Global variables ----------------------------------------------------------*/\n//\n// /* USER CODE BEGIN GV */\n//\n// /* USER CODE END GV */\n//\n// /* Private functions prototypes-----------------------------------------------*/\n// #if (USE_TEMPERATURE_BASED_RADIO_CALIBRATION == 1)\n// static void ll_sys_bg_temperature_measurement_init(void);\n// #endif /* USE_TEMPERATURE_BASED_RADIO_CALIBRATION */\n// static void ll_sys_sleep_clock_source_selection(void);\n// static uint8_t ll_sys_BLE_sleep_clock_accuracy_selection(void);\n// void ll_sys_reset(void);\n//\n// /* USER CODE BEGIN PFP */\n//\n// /* USER CODE END PFP */\n//\n// /* External variables --------------------------------------------------------*/\n//\n// /* USER CODE BEGIN EV */\n//\n// /* USER CODE END EV */\n//\n// /* Functions Definition ------------------------------------------------------*/\n//\n// /**\n//   * @brief  Link Layer background process initialization\n//   * @param  None\n//   * @retval None\n//   */\n// void ll_sys_bg_process_init(void)\n// {\n//   /* Register Link Layer task */\n//   UTIL_SEQ_RegTask(1U << CFG_TASK_LINK_LAYER, UTIL_SEQ_RFU, ll_sys_bg_process);\n// }\n//\n// /**\n//   * @brief  Link Layer background process next iteration scheduling\n//   * @param  None\n//   * @retval None\n//   */\n// void ll_sys_schedule_bg_process(void)\n// {\n//   UTIL_SEQ_SetTask(1U << CFG_TASK_LINK_LAYER, TASK_PRIO_LINK_LAYER);\n// }\n//\n// /**\n//   * @brief  Link Layer background process next iteration scheduling from ISR\n//   * @param  None\n//   * @retval None\n//   */\n// void ll_sys_schedule_bg_process_isr(void)\n// {\n//   UTIL_SEQ_SetTask(1U << CFG_TASK_LINK_LAYER, TASK_PRIO_LINK_LAYER);\n// }\n//\n// /**\n//   * @brief  Link Layer configuration phase before application startup.\n//   * @param  None\n//   * @retval None\n//   */\n// void ll_sys_config_params(void)\n// {\n// /* USER CODE BEGIN ll_sys_config_params_0 */\n//\n// /* USER CODE END ll_sys_config_params_0 */\n//\n//   /* Configure link layer behavior for low ISR use and next event scheduling method:\n//    * - SW low ISR is used.\n//    * - Next event is scheduled from ISR.\n//    */\n//   ll_intf_cmn_config_ll_ctx_params(USE_RADIO_LOW_ISR, NEXT_EVENT_SCHEDULING_FROM_ISR);\n//   /* Apply the selected link layer sleep timer source */\n//   ll_sys_sleep_clock_source_selection();\n//\n// /* USER CODE BEGIN ll_sys_config_params_1 */\n//\n// /* USER CODE END ll_sys_config_params_1 */\n//\n// #if (USE_TEMPERATURE_BASED_RADIO_CALIBRATION == 1)\n//   /* Initialize link layer temperature measurement background task */\n//   ll_sys_bg_temperature_measurement_init();\n//\n//   /* Link layer IP uses temperature based calibration instead of periodic one */\n//   ll_intf_cmn_set_temperature_sensor_state();\n// #endif /* USE_TEMPERATURE_BASED_RADIO_CALIBRATION */\n//\n//   /* Link Layer power table */\n//   ll_intf_cmn_select_tx_power_table(CFG_RF_TX_POWER_TABLE_ID);\n//\n// #if (USE_CTE_DEGRADATION == 1u)\n//   /* Apply CTE degradation */\n//   ll_sys_apply_cte_settings ();\n// #endif /* (USE_CTE_DEGRADATION == 1u) */\n//\n// /* USER CODE BEGIN ll_sys_config_params_2 */\n//\n// /* USER CODE END ll_sys_config_params_2 */\n// }\n//\n// #if (USE_TEMPERATURE_BASED_RADIO_CALIBRATION == 1)\n//\n// /**\n//   * @brief  Link Layer temperature request background process initialization\n//   * @param  None\n//   * @retval None\n//   */\n// void ll_sys_bg_temperature_measurement_init(void)\n// {\n//   /* Register Temperature Measurement task */\n//   UTIL_SEQ_RegTask(1U << CFG_TASK_TEMP_MEAS, UTIL_SEQ_RFU, TEMPMEAS_RequestTemperatureMeasurement);\n// }\n//\n// /**\n//   * @brief  Request backroud task processing for temperature measurement\n//   * @param  None\n//   * @retval None\n//   */\n// void ll_sys_bg_temperature_measurement(void)\n// {\n//   static uint8_t initial_temperature_acquisition = 0;\n//\n//   if(initial_temperature_acquisition == 0)\n//   {\n//     TEMPMEAS_RequestTemperatureMeasurement();\n//     initial_temperature_acquisition = 1;\n//   }\n//   else\n//   {\n//     UTIL_SEQ_SetTask(1U << CFG_TASK_TEMP_MEAS, CFG_SEQ_PRIO_0);\n//   }\n// }\n//\n// #endif /* USE_TEMPERATURE_BASED_RADIO_CALIBRATION */\n//\n// uint8_t ll_sys_BLE_sleep_clock_accuracy_selection(void)\n// {\n//   uint8_t BLE_sleep_clock_accuracy = 0;\n// #if (CFG_RADIO_LSE_SLEEP_TIMER_CUSTOM_SCA_RANGE == 0)\n//   uint32_t RevID = LL_DBGMCU_GetRevisionID();\n// #endif\n//   uint32_t linklayer_slp_clk_src = LL_RCC_RADIO_GetSleepTimerClockSource();\n//\n//   if(linklayer_slp_clk_src == LL_RCC_RADIOSLEEPSOURCE_LSE)\n//   {\n//     /* LSE selected as Link Layer sleep clock source.\n//        Sleep clock accuracy is different regarding the WBA device ID and revision\n//      */\n// #if (CFG_RADIO_LSE_SLEEP_TIMER_CUSTOM_SCA_RANGE == 0)\n// #if defined(STM32WBA52xx) || defined(STM32WBA54xx) || defined(STM32WBA55xx)\n//     if(RevID == REV_ID_A)\n//     {\n//       BLE_sleep_clock_accuracy = STM32WBA5x_REV_ID_A_SCA_RANGE;\n//     }\n//     else if(RevID == REV_ID_B)\n//     {\n//       BLE_sleep_clock_accuracy = STM32WBA5x_REV_ID_B_SCA_RANGE;\n//     }\n//     else\n//     {\n//       /* Revision ID not supported, default value of 500ppm applied */\n//       BLE_sleep_clock_accuracy = STM32WBA5x_DEFAULT_SCA_RANGE;\n//     }\n// #elif defined(STM32WBA65xx)\n//     BLE_sleep_clock_accuracy = STM32WBA6x_SCA_RANGE;\n//     UNUSED(RevID);\n// #else\n//     UNUSED(RevID);\n// #endif /* defined(STM32WBA52xx) || defined(STM32WBA54xx) || defined(STM32WBA55xx) */\n// #else /* CFG_RADIO_LSE_SLEEP_TIMER_CUSTOM_SCA_RANGE */\n//     BLE_sleep_clock_accuracy = CFG_RADIO_LSE_SLEEP_TIMER_CUSTOM_SCA_RANGE;\n// #endif /* CFG_RADIO_LSE_SLEEP_TIMER_CUSTOM_SCA_RANGE */\n//   }\n//   else\n//   {\n//     /* LSE is not the Link Layer sleep clock source, sleep clock accurcay default value is 500 ppm */\n//     BLE_sleep_clock_accuracy = STM32WBA5x_DEFAULT_SCA_RANGE;\n//   }\n//\n//   return BLE_sleep_clock_accuracy;\n// }\n//\n// void ll_sys_sleep_clock_source_selection(void)\n// {\n//   uint16_t freq_value = 0;\n//   uint32_t linklayer_slp_clk_src = LL_RCC_RADIOSLEEPSOURCE_NONE;\n//\n//   linklayer_slp_clk_src = LL_RCC_RADIO_GetSleepTimerClockSource();\n//   switch(linklayer_slp_clk_src)\n//   {\n//     case LL_RCC_RADIOSLEEPSOURCE_LSE:\n//       linklayer_slp_clk_src = RTC_SLPTMR;\n//       break;\n//\n//     case LL_RCC_RADIOSLEEPSOURCE_LSI:\n//       linklayer_slp_clk_src = RCO_SLPTMR;\n//       break;\n//\n//     case LL_RCC_RADIOSLEEPSOURCE_HSE_DIV1000:\n//       linklayer_slp_clk_src = CRYSTAL_OSCILLATOR_SLPTMR;\n//       break;\n//\n//     case LL_RCC_RADIOSLEEPSOURCE_NONE:\n//       /* No Link Layer sleep clock source selected */\n//       assert_param(0);\n//       break;\n//   }\n//   ll_intf_cmn_le_select_slp_clk_src((uint8_t)linklayer_slp_clk_src, &freq_value);\n// }\n//\n// void ll_sys_reset(void)\n// {\n//   uint8_t bsca = 0;\n//   /* Link layer timings */\n//   uint8_t drift_time = DRIFT_TIME_DEFAULT;\n//   uint8_t exec_time = EXEC_TIME_DEFAULT;\n//\n// /* USER CODE BEGIN ll_sys_reset_0 */\n//\n// /* USER CODE END ll_sys_reset_0 */\n//\n//   /* Apply the selected link layer sleep timer source */\n//   ll_sys_sleep_clock_source_selection();\n//\n//   /* Configure the link layer sleep clock accuracy */\n//   bsca = ll_sys_BLE_sleep_clock_accuracy_selection();\n//   ll_intf_le_set_sleep_clock_accuracy(bsca);\n//\n//   /* Update link layer timings depending on selected configuration */\n//   if(LL_RCC_RADIO_GetSleepTimerClockSource() == LL_RCC_RADIOSLEEPSOURCE_LSI)\n//   {\n//     drift_time += DRIFT_TIME_EXTRA_LSI2;\n//     exec_time += EXEC_TIME_EXTRA_LSI2;\n//   }\n//   else\n//   {\n// #if defined(__GNUC__) && defined(DEBUG)\n//     drift_time += DRIFT_TIME_EXTRA_GCC_DEBUG;\n//     exec_time += EXEC_TIME_EXTRA_GCC_DEBUG;\n// #endif\n//   }\n//\n//   /* USER CODE BEGIN ll_sys_reset_1 */\n//\n//   /* USER CODE END ll_sys_reset_1 */\n//\n//   if((drift_time != DRIFT_TIME_DEFAULT) || (exec_time != EXEC_TIME_DEFAULT))\n//   {\n//     ll_sys_config_BLE_schldr_timings(drift_time, exec_time);\n//   }\n//   /* USER CODE BEGIN ll_sys_reset_2 */\n//\n//   /* USER CODE END ll_sys_reset_2 */\n// }\n// #if defined(STM32WBA52xx) || defined(STM32WBA54xx) || defined(STM32WBA55xx) || defined(STM32WBA65xx)\n// void ll_sys_apply_cte_settings(void)\n// {\n//   ll_intf_apply_cte_degrad_change();\n// }\n// #endif /* defined(STM32WBA52xx) || defined(STM32WBA54xx) || defined(STM32WBA55xx) || defined(STM32WBA65xx) */\n//\n// #if (CFG_LPM_STANDBY_SUPPORTED == 0)\n// void ll_sys_get_ble_profile_statistics(uint32_t* exec_time, uint32_t* drift_time, uint32_t* average_drift_time, uint8_t reset)\n// {\n//   if (reset != 0U)\n//   {\n//     profile_reset();\n//   }\n//   ll_intf_get_profile_statistics(exec_time, drift_time, average_drift_time);\n// }\n// #endif\n//\nuse super::bindings::{link_layer, mac};\nuse super::util_seq;\n\nconst UTIL_SEQ_RFU: u32 = 0;\nconst TASK_LINK_LAYER_MASK: u32 = 1 << mac::CFG_TASK_ID_T_CFG_TASK_LINK_LAYER;\nconst TASK_PRIO_LINK_LAYER: u32 = mac::CFG_SEQ_PRIO_ID_T_CFG_SEQ_PRIO_0 as u32;\n\n/**\n * @brief  Link Layer background process initialization\n * @param  None\n * @retval None\n */\n#[unsafe(no_mangle)]\npub unsafe extern \"C\" fn ll_sys_bg_process_init() {\n    util_seq::UTIL_SEQ_RegTask(TASK_LINK_LAYER_MASK, UTIL_SEQ_RFU, Some(link_layer::ll_sys_bg_process));\n}\n\n/**\n * @brief  Link Layer background process next iteration scheduling\n * @param  None\n * @retval None\n */\n#[unsafe(no_mangle)]\npub unsafe extern \"C\" fn ll_sys_schedule_bg_process() {\n    util_seq::UTIL_SEQ_SetTask(TASK_LINK_LAYER_MASK, TASK_PRIO_LINK_LAYER);\n}\n\n/**\n * @brief  Link Layer background process next iteration scheduling from ISR\n * @param  None\n * @retval None\n */\n#[unsafe(no_mangle)]\npub unsafe extern \"C\" fn ll_sys_schedule_bg_process_isr() {\n    util_seq::UTIL_SEQ_SetTask(TASK_LINK_LAYER_MASK, TASK_PRIO_LINK_LAYER);\n}\n\n/**\n * @brief  Link Layer configuration phase before application startup.\n * @param  None\n * @retval None\n */\n#[unsafe(no_mangle)]\npub unsafe extern \"C\" fn ll_sys_config_params() {\n    let allow_low_isr = mac::USE_RADIO_LOW_ISR as u8;\n    let run_from_isr = mac::NEXT_EVENT_SCHEDULING_FROM_ISR as u8;\n    let _ = link_layer::ll_intf_cmn_config_ll_ctx_params(allow_low_isr, run_from_isr);\n\n    ll_sys_sleep_clock_source_selection();\n    let _ = link_layer::ll_intf_cmn_select_tx_power_table(mac::CFG_RF_TX_POWER_TABLE_ID as u8);\n}\n\n/**\n * @brief  Reset Link Layer timing parameters to their default configuration.\n * @param  None\n * @retval None\n */\n#[unsafe(no_mangle)]\npub unsafe extern \"C\" fn ll_sys_reset() {\n    ll_sys_sleep_clock_source_selection();\n\n    let sleep_accuracy = ll_sys_BLE_sleep_clock_accuracy_selection();\n    let _ = link_layer::ll_intf_le_set_sleep_clock_accuracy(sleep_accuracy);\n}\n\n/// Select the sleep-clock source used by the Link Layer.\n/// Defaults to the crystal oscillator when no explicit configuration is available.\n#[unsafe(no_mangle)]\npub unsafe extern \"C\" fn ll_sys_sleep_clock_source_selection() {\n    let mut frequency: u16 = 0;\n    let _ = link_layer::ll_intf_cmn_le_select_slp_clk_src(\n        link_layer::_SLPTMR_SRC_TYPE_E_CRYSTAL_OSCILLATOR_SLPTMR as u8,\n        &mut frequency as *mut u16,\n    );\n}\n\n/// Determine the BLE sleep-clock accuracy used by the stack.\n/// Returns zero when board-specific calibration data is unavailable.\n#[unsafe(no_mangle)]\npub unsafe extern \"C\" fn ll_sys_BLE_sleep_clock_accuracy_selection() -> u8 {\n    // TODO: derive the board-specific sleep clock accuracy once calibration data is available.\n    0\n}\n"
  },
  {
    "path": "embassy-stm32-wpan/src/wba/mac_sys_if.rs",
    "content": "#![cfg(feature = \"wba\")]\n#![allow(non_snake_case)]\n\n//\n// /* USER CODE BEGIN Header */\n// /**\n//   ******************************************************************************\n//   * @file    mac_sys_if.c\n//   * @author  MCD Application Team\n//   * @brief   Source file for using MAC Layer with a RTOS\n//   ******************************************************************************\n//   * @attention\n//   *\n//   * Copyright (c) 2025 STMicroelectronics.\n//   * All rights reserved.\n//   *\n//   * This software is licensed under terms that can be found in the LICENSE file\n//   * in the root directory of this software component.\n//   * If no LICENSE file comes with this software, it is provided AS-IS.\n//   *\n//   ******************************************************************************\n//   */\n// /* USER CODE END Header */\n//\n// #include \"main.h\"\n// #include \"app_common.h\"\n// #include \"app_conf.h\"\n// #include \"log_module.h\"\n// #include \"stm32_rtos.h\"\n// #include \"st_mac_802_15_4_sys.h\"\n//\n// extern void mac_baremetal_run(void);\n//\n// /* Private defines -----------------------------------------------------------*/\n// /* USER CODE BEGIN PD */\n//\n// /* USER CODE END PD */\n//\n// /* Private macros ------------------------------------------------------------*/\n// /* USER CODE BEGIN PM */\n//\n// /* USER CODE END PM */\n//\n// /* Private variables ---------------------------------------------------------*/\n// /* USER CODE BEGIN PV */\n//\n// /* USER CODE END PV */\n//\n// /* Global variables ----------------------------------------------------------*/\n// /* USER CODE BEGIN GV */\n//\n// /* USER CODE END GV */\n//\n// /* Functions Definition ------------------------------------------------------*/\n//\n// /**\n//   * @brief  Mac Layer Initialisation\n//   * @param  None\n//   * @retval None\n//   */\n// void MacSys_Init(void)\n// {\n//   /* Register tasks */\n//   UTIL_SEQ_RegTask( TASK_MAC_LAYER, UTIL_SEQ_RFU, mac_baremetal_run);\n// }\n//\n// /**\n//   * @brief  Mac Layer Resume\n//   * @param  None\n//   * @retval None\n//   */\n// void MacSys_Resume(void)\n// {\n//   UTIL_SEQ_ResumeTask( TASK_MAC_LAYER );\n// }\n//\n// /**\n//   * @brief  MAC Layer set Task.\n//   * @param  None\n//   * @retval None\n//   */\n// void MacSys_SemaphoreSet(void)\n// {\n//   UTIL_SEQ_SetTask( TASK_MAC_LAYER, TASK_PRIO_MAC_LAYER );\n// }\n//\n// /**\n//   * @brief  MAC Layer Task wait.\n//   * @param  None\n//   * @retval None\n//   */\n// void MacSys_SemaphoreWait( void )\n// {\n//   /* Not used */\n// }\n//\n// /**\n//   * @brief  MAC Layer set Event.\n//   * @param  None\n//   * @retval None\n//   */\n// void MacSys_EventSet( void )\n// {\n//   UTIL_SEQ_SetEvt( EVENT_MAC_LAYER );\n// }\n//\n// /**\n//   * @brief  MAC Layer wait Event.\n//   * @param  None\n//   * @retval None\n//   */\n// void MacSys_EventWait( void )\n// {\n//   UTIL_SEQ_WaitEvt( EVENT_MAC_LAYER );\n// }\n//\n\nuse super::util_seq;\nuse crate::bindings::mac;\n\n/// Placeholder value used by the original ST middleware when registering tasks.\nconst UTIL_SEQ_RFU: u32 = 0;\n\n/// Bit mask identifying the MAC layer task within the sequencer.\nconst TASK_MAC_LAYER_MASK: u32 = 1 << mac::CFG_TASK_ID_T_CFG_TASK_MAC_LAYER;\n\n/// Sequencer priority assigned to the MAC layer task.\nconst TASK_PRIO_MAC_LAYER: u32 = mac::CFG_SEQ_PRIO_ID_T_CFG_SEQ_PRIO_0 as u32;\n\n/// Event flag consumed by the MAC task while waiting on notifications.\nconst EVENT_MAC_LAYER_MASK: u32 = 1 << 0;\n\n/// Registers the MAC bare-metal runner with the lightweight sequencer.\n///\n/// Mirrors the behaviour of the reference implementation:\n/// `UTIL_SEQ_RegTask(TASK_MAC_LAYER, UTIL_SEQ_RFU, mac_baremetal_run);`\n#[unsafe(no_mangle)]\npub unsafe extern \"C\" fn MacSys_Init() {\n    util_seq::UTIL_SEQ_RegTask(TASK_MAC_LAYER_MASK, UTIL_SEQ_RFU, Some(mac::mac_baremetal_run));\n}\n\n/**\n * @brief  Mac Layer Resume\n * @param  None\n * @retval None\n */\n#[unsafe(no_mangle)]\npub unsafe extern \"C\" fn MacSys_Resume() {\n    util_seq::UTIL_SEQ_ResumeTask(TASK_MAC_LAYER_MASK);\n}\n\n/**\n * @brief  MAC Layer set Task.\n * @param  None\n * @retval None\n */\n#[unsafe(no_mangle)]\npub unsafe extern \"C\" fn MacSys_SemaphoreSet() {\n    util_seq::UTIL_SEQ_SetTask(TASK_MAC_LAYER_MASK, TASK_PRIO_MAC_LAYER);\n}\n\n/**\n * @brief  MAC Layer Task wait.\n * @param  None\n * @retval None\n */\n#[unsafe(no_mangle)]\npub unsafe extern \"C\" fn MacSys_SemaphoreWait() {}\n\n/**\n * @brief  MAC Layer set Event.\n * @param  None\n * @retval None\n */\n#[unsafe(no_mangle)]\npub unsafe extern \"C\" fn MacSys_EventSet() {\n    util_seq::UTIL_SEQ_SetEvt(EVENT_MAC_LAYER_MASK);\n}\n\n/**\n * @brief  MAC Layer wait Event.\n * @param  None\n * @retval None\n */\n#[unsafe(no_mangle)]\npub unsafe extern \"C\" fn MacSys_EventWait() {\n    util_seq::UTIL_SEQ_WaitEvt(EVENT_MAC_LAYER_MASK);\n}\n"
  },
  {
    "path": "embassy-stm32-wpan/src/wba/mod.rs",
    "content": "pub mod bindings;\npub mod ble;\npub mod context;\npub mod error;\npub mod gap;\npub mod gatt;\npub mod hci;\npub mod linklayer_plat;\npub mod ll_sys;\npub mod ll_sys_if;\npub mod mac_sys_if;\npub mod power_table;\npub mod runner;\npub mod security;\npub mod util_seq;\n\n// Re-export main types\npub use ble::{Ble, VersionInfo};\npub use error::BleError;\npub use linklayer_plat::{run_radio_high_isr, run_radio_sw_low_isr};\npub use runner::ble_runner;\n"
  },
  {
    "path": "embassy-stm32-wpan/src/wba/power_table.rs",
    "content": "//! TX Power Tables for STM32WBA BLE\n//!\n//! This module defines the TX power tables required by the BLE link layer.\n\nuse crate::bindings::link_layer::{_power_table_id_t, power_table_entry};\n\n/// VDD LDO value for maximum power mode\nconst VDD_LDO_VALUE_MAX_POWER: u8 = 0x70;\n/// VDD LDO value for low power mode\nconst VDD_LDO_VALUE_LOW_POWER: u8 = 0x20;\n/// VDD LDO value 2 ID 0\nconst VDD_LDO_VALUE_2_ID_0: u8 = 0x00;\n\n/// TX power table for maximum power mode\n#[unsafe(link_section = \".rodata\")]\nstatic LL_TX_POWER_TABLE_MAX_POWER: [power_table_entry; 31] = [\n    power_table_entry {\n        vddh_pa: 0x02,\n        internal_pa_code: 0x02,\n        epa_bypass: 0x01,\n        tx_pwr: -20,\n    },\n    power_table_entry {\n        vddh_pa: 0x02,\n        internal_pa_code: 0x03,\n        epa_bypass: 0x01,\n        tx_pwr: -19,\n    },\n    power_table_entry {\n        vddh_pa: 0x02,\n        internal_pa_code: 0x04,\n        epa_bypass: 0x01,\n        tx_pwr: -18,\n    },\n    power_table_entry {\n        vddh_pa: 0x02,\n        internal_pa_code: 0x05,\n        epa_bypass: 0x01,\n        tx_pwr: -17,\n    },\n    power_table_entry {\n        vddh_pa: 0x02,\n        internal_pa_code: 0x06,\n        epa_bypass: 0x01,\n        tx_pwr: -16,\n    },\n    power_table_entry {\n        vddh_pa: 0x02,\n        internal_pa_code: 0x07,\n        epa_bypass: 0x01,\n        tx_pwr: -15,\n    },\n    power_table_entry {\n        vddh_pa: 0x02,\n        internal_pa_code: 0x08,\n        epa_bypass: 0x01,\n        tx_pwr: -14,\n    },\n    power_table_entry {\n        vddh_pa: 0x02,\n        internal_pa_code: 0x09,\n        epa_bypass: 0x01,\n        tx_pwr: -13,\n    },\n    power_table_entry {\n        vddh_pa: 0x02,\n        internal_pa_code: 0x0A,\n        epa_bypass: 0x01,\n        tx_pwr: -12,\n    },\n    power_table_entry {\n        vddh_pa: 0x02,\n        internal_pa_code: 0x0B,\n        epa_bypass: 0x01,\n        tx_pwr: -11,\n    },\n    power_table_entry {\n        vddh_pa: 0x02,\n        internal_pa_code: 0x0C,\n        epa_bypass: 0x01,\n        tx_pwr: -10,\n    },\n    power_table_entry {\n        vddh_pa: 0x02,\n        internal_pa_code: 0x0D,\n        epa_bypass: 0x01,\n        tx_pwr: -9,\n    },\n    power_table_entry {\n        vddh_pa: 0x02,\n        internal_pa_code: 0x0E,\n        epa_bypass: 0x01,\n        tx_pwr: -8,\n    },\n    power_table_entry {\n        vddh_pa: 0x00,\n        internal_pa_code: 0x0F,\n        epa_bypass: 0x01,\n        tx_pwr: -7,\n    },\n    power_table_entry {\n        vddh_pa: 0x00,\n        internal_pa_code: 0x10,\n        epa_bypass: 0x01,\n        tx_pwr: -6,\n    },\n    power_table_entry {\n        vddh_pa: 0x00,\n        internal_pa_code: 0x11,\n        epa_bypass: 0x01,\n        tx_pwr: -5,\n    },\n    power_table_entry {\n        vddh_pa: 0x00,\n        internal_pa_code: 0x12,\n        epa_bypass: 0x01,\n        tx_pwr: -4,\n    },\n    power_table_entry {\n        vddh_pa: 0x00,\n        internal_pa_code: 0x13,\n        epa_bypass: 0x01,\n        tx_pwr: -3,\n    },\n    power_table_entry {\n        vddh_pa: 0x02,\n        internal_pa_code: 0x14,\n        epa_bypass: 0x01,\n        tx_pwr: -2,\n    },\n    power_table_entry {\n        vddh_pa: 0x00,\n        internal_pa_code: 0x15,\n        epa_bypass: 0x01,\n        tx_pwr: -1,\n    },\n    power_table_entry {\n        vddh_pa: 0x02,\n        internal_pa_code: 0x16,\n        epa_bypass: 0x01,\n        tx_pwr: 0,\n    },\n    power_table_entry {\n        vddh_pa: 0x00,\n        internal_pa_code: 0x17,\n        epa_bypass: 0x01,\n        tx_pwr: 1,\n    },\n    power_table_entry {\n        vddh_pa: 0x00,\n        internal_pa_code: 0x18,\n        epa_bypass: 0x01,\n        tx_pwr: 2,\n    },\n    power_table_entry {\n        vddh_pa: 0x02,\n        internal_pa_code: 0x18,\n        epa_bypass: 0x01,\n        tx_pwr: 3,\n    },\n    power_table_entry {\n        vddh_pa: 0x00,\n        internal_pa_code: 0x19,\n        epa_bypass: 0x01,\n        tx_pwr: 4,\n    },\n    power_table_entry {\n        vddh_pa: 0x02,\n        internal_pa_code: 0x19,\n        epa_bypass: 0x01,\n        tx_pwr: 5,\n    },\n    power_table_entry {\n        vddh_pa: 0x03,\n        internal_pa_code: 0x19,\n        epa_bypass: 0x01,\n        tx_pwr: 6,\n    },\n    power_table_entry {\n        vddh_pa: 0x05,\n        internal_pa_code: 0x19,\n        epa_bypass: 0x01,\n        tx_pwr: 7,\n    },\n    power_table_entry {\n        vddh_pa: 0x06,\n        internal_pa_code: 0x19,\n        epa_bypass: 0x01,\n        tx_pwr: 8,\n    },\n    power_table_entry {\n        vddh_pa: 0x08,\n        internal_pa_code: 0x19,\n        epa_bypass: 0x01,\n        tx_pwr: 9,\n    },\n    power_table_entry {\n        vddh_pa: 0x0D,\n        internal_pa_code: 0x19,\n        epa_bypass: 0x01,\n        tx_pwr: 10,\n    },\n];\n\n/// TX power table for low power mode\n#[unsafe(link_section = \".rodata\")]\nstatic LL_TX_POWER_TABLE_LOW_POWER: [power_table_entry; 24] = [\n    power_table_entry {\n        vddh_pa: 0x02,\n        internal_pa_code: 0x02,\n        epa_bypass: 0x01,\n        tx_pwr: -20,\n    },\n    power_table_entry {\n        vddh_pa: 0x02,\n        internal_pa_code: 0x03,\n        epa_bypass: 0x01,\n        tx_pwr: -19,\n    },\n    power_table_entry {\n        vddh_pa: 0x00,\n        internal_pa_code: 0x05,\n        epa_bypass: 0x01,\n        tx_pwr: -18,\n    },\n    power_table_entry {\n        vddh_pa: 0x00,\n        internal_pa_code: 0x06,\n        epa_bypass: 0x01,\n        tx_pwr: -17,\n    },\n    power_table_entry {\n        vddh_pa: 0x00,\n        internal_pa_code: 0x07,\n        epa_bypass: 0x01,\n        tx_pwr: -16,\n    },\n    power_table_entry {\n        vddh_pa: 0x00,\n        internal_pa_code: 0x08,\n        epa_bypass: 0x01,\n        tx_pwr: -15,\n    },\n    power_table_entry {\n        vddh_pa: 0x00,\n        internal_pa_code: 0x09,\n        epa_bypass: 0x01,\n        tx_pwr: -14,\n    },\n    power_table_entry {\n        vddh_pa: 0x00,\n        internal_pa_code: 0x0A,\n        epa_bypass: 0x01,\n        tx_pwr: -13,\n    },\n    power_table_entry {\n        vddh_pa: 0x00,\n        internal_pa_code: 0x0B,\n        epa_bypass: 0x01,\n        tx_pwr: -12,\n    },\n    power_table_entry {\n        vddh_pa: 0x00,\n        internal_pa_code: 0x0C,\n        epa_bypass: 0x01,\n        tx_pwr: -11,\n    },\n    power_table_entry {\n        vddh_pa: 0x00,\n        internal_pa_code: 0x0D,\n        epa_bypass: 0x01,\n        tx_pwr: -10,\n    },\n    power_table_entry {\n        vddh_pa: 0x00,\n        internal_pa_code: 0x0E,\n        epa_bypass: 0x01,\n        tx_pwr: -9,\n    },\n    power_table_entry {\n        vddh_pa: 0x00,\n        internal_pa_code: 0x0F,\n        epa_bypass: 0x01,\n        tx_pwr: -8,\n    },\n    power_table_entry {\n        vddh_pa: 0x00,\n        internal_pa_code: 0x10,\n        epa_bypass: 0x01,\n        tx_pwr: -7,\n    },\n    power_table_entry {\n        vddh_pa: 0x00,\n        internal_pa_code: 0x11,\n        epa_bypass: 0x01,\n        tx_pwr: -6,\n    },\n    power_table_entry {\n        vddh_pa: 0x00,\n        internal_pa_code: 0x12,\n        epa_bypass: 0x01,\n        tx_pwr: -5,\n    },\n    power_table_entry {\n        vddh_pa: 0x00,\n        internal_pa_code: 0x13,\n        epa_bypass: 0x01,\n        tx_pwr: -4,\n    },\n    power_table_entry {\n        vddh_pa: 0x02,\n        internal_pa_code: 0x14,\n        epa_bypass: 0x01,\n        tx_pwr: -3,\n    },\n    power_table_entry {\n        vddh_pa: 0x02,\n        internal_pa_code: 0x15,\n        epa_bypass: 0x01,\n        tx_pwr: -2,\n    },\n    power_table_entry {\n        vddh_pa: 0x00,\n        internal_pa_code: 0x17,\n        epa_bypass: 0x01,\n        tx_pwr: -1,\n    },\n    power_table_entry {\n        vddh_pa: 0x02,\n        internal_pa_code: 0x17,\n        epa_bypass: 0x01,\n        tx_pwr: 0,\n    },\n    power_table_entry {\n        vddh_pa: 0x00,\n        internal_pa_code: 0x18,\n        epa_bypass: 0x01,\n        tx_pwr: 1,\n    },\n    power_table_entry {\n        vddh_pa: 0x00,\n        internal_pa_code: 0x19,\n        epa_bypass: 0x01,\n        tx_pwr: 2,\n    },\n    power_table_entry {\n        vddh_pa: 0x02,\n        internal_pa_code: 0x19,\n        epa_bypass: 0x01,\n        tx_pwr: 3,\n    },\n];\n\n/// Wrapper type for _power_table_id_t that implements Sync\n/// SAFETY: The contained data is read-only and only accessed from C code\n#[repr(transparent)]\npub struct SyncPowerTableId(_power_table_id_t);\n\n// SAFETY: The power table is only read, never mutated, and is accessed\n// from a single-threaded embedded context\nunsafe impl Sync for SyncPowerTableId {}\n\n/// Supported TX power tables\n#[unsafe(no_mangle)]\n#[unsafe(link_section = \".rodata\")]\npub static ll_tx_power_tables: [SyncPowerTableId; 2] = [\n    SyncPowerTableId(_power_table_id_t {\n        ptr_tx_power_table: LL_TX_POWER_TABLE_MAX_POWER.as_ptr(),\n        tx_power_levels_count: LL_TX_POWER_TABLE_MAX_POWER.len() as u8,\n        g_vdd_ldo_value_1: VDD_LDO_VALUE_MAX_POWER,\n        g_vdd_ldo_value_2: VDD_LDO_VALUE_2_ID_0,\n        power_table_id: 0,\n    }),\n    SyncPowerTableId(_power_table_id_t {\n        ptr_tx_power_table: LL_TX_POWER_TABLE_LOW_POWER.as_ptr(),\n        tx_power_levels_count: LL_TX_POWER_TABLE_LOW_POWER.len() as u8,\n        g_vdd_ldo_value_1: VDD_LDO_VALUE_LOW_POWER,\n        g_vdd_ldo_value_2: VDD_LDO_VALUE_2_ID_0,\n        power_table_id: 1,\n    }),\n];\n\n/// Number of supported TX power tables\n#[unsafe(no_mangle)]\n#[unsafe(link_section = \".rodata\")]\npub static num_of_supported_power_tables: u8 = 2;\n"
  },
  {
    "path": "embassy-stm32-wpan/src/wba/runner.rs",
    "content": "//! BLE Stack Runner for Embassy Integration\n//!\n//! This module provides the runner that drives the BLE sequencer while\n//! integrating properly with the embassy async executor.\n//!\n//! # Architecture\n//!\n//! The BLE stack runs in a separate context (with its own stack) managed by\n//! the context switching module. The runner:\n//!\n//! 1. Resumes the sequencer context\n//! 2. The sequencer processes pending tasks\n//! 3. When idle, the sequencer yields back\n//! 4. The runner yields to the embassy executor\n//! 5. When woken (by interrupt), repeats from step 1\n//!\n//! # Usage\n//!\n//! The runner must be spawned as a separate embassy task:\n//!\n//! ```no_run\n//! use embassy_executor::Spawner;\n//! use embassy_stm32_wpan::wba::ble_runner;\n//!\n//! #[embassy_executor::task]\n//! async fn ble_task() {\n//!     ble_runner().await\n//! }\n//!\n//! #[embassy_executor::main]\n//! async fn main(spawner: Spawner) {\n//!     // Initialize BLE stack first...\n//!\n//!     // Spawn the BLE runner task\n//!     spawner.spawn(ble_task()).unwrap();\n//!\n//!     // Your application logic...\n//! }\n//! ```\n\nuse core::future::poll_fn;\nuse core::sync::atomic::{AtomicBool, Ordering, compiler_fence};\nuse core::task::Poll;\n\nuse embassy_futures::join;\nuse embassy_sync::waitqueue::AtomicWaker;\n\n// Note: complete_ble_link_layer_init is now called as part of init_ble_stack()\n// in Ble::init(), so we no longer need to call it from the runner.\nuse super::util_seq;\n\n// BleStack_Process return values\nconst BLE_SLEEPMODE_RUNNING: u8 = 0;\n\n// External BLE stack process function\n#[link(name = \"stm32wba_ble_stack_basic\")]\nunsafe extern \"C\" {\n    /// BLE stack process function - must be called to process BLE events\n    fn BleStack_Process() -> u8;\n}\n\n/// Call BleStack_Process until it returns CPU_HALT\n/// Per ST docs: \"When BleStack_Process returns BLE_SLEEPMODE_RUNNING, it shall be re-called\"\nfn process_ble_stack() {\n    unsafe {\n        let mut iterations = 0;\n        loop {\n            let result = BleStack_Process();\n\n            #[cfg(feature = \"defmt\")]\n            if iterations == 0 {\n                defmt::trace!(\"BleStack_Process called, result={}\", result);\n            }\n\n            if result != BLE_SLEEPMODE_RUNNING {\n                // CPU can halt, no more work to do\n                break;\n            }\n\n            iterations += 1;\n\n            // Safety limit to prevent infinite loop\n            if iterations > 1000 {\n                #[cfg(feature = \"defmt\")]\n                defmt::warn!(\"BleStack_Process called {} times, breaking to prevent hang\", iterations);\n                break;\n            }\n        }\n\n        #[cfg(feature = \"defmt\")]\n        if iterations > 10 {\n            defmt::debug!(\"BleStack_Process completed after {} iterations\", iterations);\n        }\n    }\n}\n\n/// Whether the link layer init has been completed\nstatic LL_INIT_COMPLETED: AtomicBool = AtomicBool::new(false);\n\n/// Signal to trigger BleStack_Process (equivalent to Sidewalk SDK's BleHostSemaphore)\npub(crate) static BLE_WAKER: AtomicWaker = AtomicWaker::new();\n\n/// BLE stack runner function\n///\n/// This async function drives the BLE stack. It must be spawned as a task\n/// to enable proper BLE operation.\n///\n/// # Example\n///\n/// ```no_run\n/// use embassy_executor::Spawner;\n/// use embassy_stm32_wpan::wba::ble_runner;\n///\n/// #[embassy_executor::task]\n/// async fn ble_runner_task() {\n///     ble_runner().await\n/// }\n///\n/// #[embassy_executor::main]\n/// async fn main(spawner: Spawner) {\n///     // Initialize BLE...\n///\n///     // Spawn the runner\n///     spawner.spawn(ble_runner_task()).unwrap();\n///\n///     // Your BLE application logic...\n/// }\n/// ```\npub async fn ble_runner() -> ! {\n    #[cfg(feature = \"defmt\")]\n    defmt::info!(\"BLE runner started\");\n\n    // Mark that the runner has started (BLE init is now done via init_ble_stack())\n    if !LL_INIT_COMPLETED.load(Ordering::Acquire) {\n        #[cfg(feature = \"defmt\")]\n        defmt::trace!(\"BLE runner: first run, initializing sequencer context\");\n\n        // Do one context switch to initialize the sequencer\n        util_seq::seq_resume();\n\n        LL_INIT_COMPLETED.store(true, Ordering::Release);\n\n        #[cfg(feature = \"defmt\")]\n        defmt::trace!(\"BLE runner: sequencer context initialized\");\n    }\n\n    join::join(\n        async {\n            loop {\n                util_seq::wait_for_event().await;\n\n                // Resume the sequencer context\n                // This will run BLE stack tasks until the sequencer yields\n                util_seq::seq_resume();\n                BLE_WAKER.wake();\n            }\n        },\n        poll_fn(|cx| {\n            BLE_WAKER.register(cx.waker());\n            compiler_fence(Ordering::Release);\n\n            process_ble_stack();\n\n            Poll::<()>::Pending\n        }),\n    )\n    .await;\n\n    loop {}\n}\n\n/// Integrate with the link layer ISR to wake the runner\n///\n/// This should be called from the radio interrupt handler.\npub fn on_radio_interrupt() {\n    util_seq::seq_pend();\n}\n"
  },
  {
    "path": "embassy-stm32-wpan/src/wba/security/mod.rs",
    "content": "//! BLE Security Manager implementation\n//!\n//! This module provides security functionality for BLE connections including:\n//! - Pairing and bonding\n//! - MITM (Man-in-the-Middle) protection\n//! - Secure Connections (SC) support\n//! - Passkey entry and numeric comparison\n//!\n//! # Example\n//!\n//! ```no_run\n//! use embassy_stm32_wpan::security::{SecurityManager, SecurityParams, IoCapability};\n//!\n//! let mut security = SecurityManager::new();\n//!\n//! // Configure security requirements\n//! let params = SecurityParams::default()\n//!     .with_io_capability(IoCapability::DisplayYesNo)\n//!     .with_bonding(true)\n//!     .with_mitm_protection(true);\n//!\n//! security.set_authentication_requirements(params)?;\n//! ```\n\nuse crate::wba::error::BleError;\nuse crate::wba::hci::types::Status;\n\n// C library exports uppercase function names\n#[allow(non_camel_case_types)]\ntype tBleStatus = u8;\n\n#[link(name = \"stm32wba_ble_stack_basic\")]\nunsafe extern \"C\" {\n    #[link_name = \"ACI_GAP_SET_AUTHENTICATION_REQUIREMENT\"]\n    fn aci_gap_set_authentication_requirement(\n        bonding_mode: u8,\n        mitm_mode: u8,\n        sc_support: u8,\n        keypress_notification_support: u8,\n        min_encryption_key_size: u8,\n        max_encryption_key_size: u8,\n        use_fixed_pin: u8,\n        fixed_pin: u32,\n        identity_address_type: u8,\n    ) -> tBleStatus;\n\n    #[link_name = \"ACI_GAP_PASS_KEY_RESP\"]\n    fn aci_gap_pass_key_resp(connection_handle: u16, pass_key: u32) -> tBleStatus;\n\n    #[link_name = \"ACI_GAP_NUMERIC_COMPARISON_VALUE_CONFIRM_YESNO\"]\n    fn aci_gap_numeric_comparison_value_confirm_yesno(connection_handle: u16, confirm_yes_no: u8) -> tBleStatus;\n\n    #[link_name = \"ACI_GAP_ALLOW_REBOND\"]\n    fn aci_gap_allow_rebond(connection_handle: u16) -> tBleStatus;\n\n    #[link_name = \"ACI_GAP_CLEAR_SECURITY_DATABASE\"]\n    fn aci_gap_clear_security_database() -> tBleStatus;\n\n    #[link_name = \"ACI_GAP_REMOVE_BONDED_DEVICE\"]\n    fn aci_gap_remove_bonded_device(peer_identity_address_type: u8, peer_identity_address: *const u8) -> tBleStatus;\n\n    #[link_name = \"ACI_GAP_IS_DEVICE_BONDED\"]\n    fn aci_gap_is_device_bonded(peer_identity_address_type: u8, peer_identity_address: *const u8) -> tBleStatus;\n\n    #[link_name = \"HCI_LE_SET_ADDRESS_RESOLUTION_ENABLE\"]\n    fn hci_le_set_address_resolution_enable(enable: u8) -> tBleStatus;\n\n    #[link_name = \"HCI_LE_SET_RESOLVABLE_PRIVATE_ADDRESS_TIMEOUT\"]\n    fn hci_le_set_resolvable_private_address_timeout(timeout: u16) -> tBleStatus;\n}\n\nconst BLE_STATUS_SUCCESS: u8 = 0x00;\n\n/// IO Capability for pairing\n#[repr(u8)]\n#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub enum IoCapability {\n    /// Display only - can display a passkey but not receive input\n    DisplayOnly = 0x00,\n    /// Display with Yes/No buttons - can display and confirm numeric comparison\n    DisplayYesNo = 0x01,\n    /// Keyboard only - can input passkey but no display\n    KeyboardOnly = 0x02,\n    /// No input or output capability\n    #[default]\n    NoInputNoOutput = 0x03,\n    /// Keyboard and display capability\n    KeyboardDisplay = 0x04,\n}\n\n/// Secure Connections support mode\n#[repr(u8)]\n#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub enum SecureConnectionsSupport {\n    /// Secure Connections not supported (legacy pairing only)\n    NotSupported = 0x00,\n    /// Secure Connections supported but optional (can fall back to legacy)\n    #[default]\n    Optional = 0x01,\n    /// Secure Connections required (will fail if peer doesn't support)\n    Required = 0x02,\n}\n\n/// Bonding mode\n#[repr(u8)]\n#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub enum BondingMode {\n    /// No bonding - keys are not stored after pairing\n    NoBonding = 0x00,\n    /// Bonding enabled - keys are stored for future reconnection\n    #[default]\n    Bonding = 0x01,\n}\n\n/// Identity address type\n#[repr(u8)]\n#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub enum IdentityAddressType {\n    /// Public identity address\n    #[default]\n    Public = 0x00,\n    /// Static random identity address\n    StaticRandom = 0x01,\n}\n\n/// Security parameters for authentication\n#[derive(Debug, Clone)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub struct SecurityParams {\n    /// Bonding mode\n    pub bonding_mode: BondingMode,\n    /// MITM (Man-in-the-Middle) protection required\n    pub mitm_protection: bool,\n    /// Secure Connections support level\n    pub secure_connections: SecureConnectionsSupport,\n    /// Keypress notification support\n    pub keypress_notification: bool,\n    /// Minimum encryption key size (7-16 bytes)\n    pub min_encryption_key_size: u8,\n    /// Maximum encryption key size (7-16 bytes)\n    pub max_encryption_key_size: u8,\n    /// Use fixed PIN (for testing only, not recommended for production)\n    pub use_fixed_pin: bool,\n    /// Fixed PIN value (only used if use_fixed_pin is true)\n    pub fixed_pin: u32,\n    /// Identity address type\n    pub identity_address_type: IdentityAddressType,\n}\n\nimpl Default for SecurityParams {\n    fn default() -> Self {\n        Self {\n            bonding_mode: BondingMode::Bonding,\n            mitm_protection: false,\n            secure_connections: SecureConnectionsSupport::Optional,\n            keypress_notification: false,\n            min_encryption_key_size: 7,\n            max_encryption_key_size: 16,\n            use_fixed_pin: false,\n            fixed_pin: 0,\n            identity_address_type: IdentityAddressType::Public,\n        }\n    }\n}\n\nimpl SecurityParams {\n    /// Create new security parameters with defaults\n    pub fn new() -> Self {\n        Self::default()\n    }\n\n    /// Set bonding mode\n    pub fn with_bonding_mode(mut self, mode: BondingMode) -> Self {\n        self.bonding_mode = mode;\n        self\n    }\n\n    /// Enable or disable bonding\n    pub fn with_bonding(mut self, enabled: bool) -> Self {\n        self.bonding_mode = if enabled {\n            BondingMode::Bonding\n        } else {\n            BondingMode::NoBonding\n        };\n        self\n    }\n\n    /// Set MITM protection requirement\n    pub fn with_mitm_protection(mut self, required: bool) -> Self {\n        self.mitm_protection = required;\n        self\n    }\n\n    /// Set Secure Connections support level\n    pub fn with_secure_connections(mut self, support: SecureConnectionsSupport) -> Self {\n        self.secure_connections = support;\n        self\n    }\n\n    /// Set encryption key size range\n    pub fn with_key_size_range(mut self, min: u8, max: u8) -> Self {\n        self.min_encryption_key_size = min.clamp(7, 16);\n        self.max_encryption_key_size = max.clamp(7, 16);\n        self\n    }\n\n    /// Use a fixed PIN (for testing only)\n    pub fn with_fixed_pin(mut self, pin: u32) -> Self {\n        self.use_fixed_pin = true;\n        self.fixed_pin = pin % 1_000_000; // Ensure 6 digits max\n        self\n    }\n\n    /// Set identity address type\n    pub fn with_identity_address_type(mut self, addr_type: IdentityAddressType) -> Self {\n        self.identity_address_type = addr_type;\n        self\n    }\n\n    /// Preset for \"Just Works\" pairing (no MITM protection)\n    pub fn just_works() -> Self {\n        Self {\n            bonding_mode: BondingMode::Bonding,\n            mitm_protection: false,\n            secure_connections: SecureConnectionsSupport::Optional,\n            keypress_notification: false,\n            min_encryption_key_size: 7,\n            max_encryption_key_size: 16,\n            use_fixed_pin: false,\n            fixed_pin: 0,\n            identity_address_type: IdentityAddressType::Public,\n        }\n    }\n\n    /// Preset for passkey entry pairing\n    pub fn passkey_entry() -> Self {\n        Self {\n            bonding_mode: BondingMode::Bonding,\n            mitm_protection: true,\n            secure_connections: SecureConnectionsSupport::Optional,\n            keypress_notification: false,\n            min_encryption_key_size: 7,\n            max_encryption_key_size: 16,\n            use_fixed_pin: false,\n            fixed_pin: 0,\n            identity_address_type: IdentityAddressType::Public,\n        }\n    }\n\n    /// Preset for secure connections with numeric comparison\n    pub fn secure_numeric_comparison() -> Self {\n        Self {\n            bonding_mode: BondingMode::Bonding,\n            mitm_protection: true,\n            secure_connections: SecureConnectionsSupport::Required,\n            keypress_notification: false,\n            min_encryption_key_size: 16,\n            max_encryption_key_size: 16,\n            use_fixed_pin: false,\n            fixed_pin: 0,\n            identity_address_type: IdentityAddressType::Public,\n        }\n    }\n}\n\n/// Security event types\n#[derive(Debug, Clone)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub enum SecurityEvent {\n    /// Pairing has completed (successfully or with error)\n    PairingComplete {\n        conn_handle: u16,\n        status: PairingStatus,\n        reason: u8,\n    },\n    /// Passkey request - application must provide passkey via pass_key_response()\n    PasskeyRequest { conn_handle: u16 },\n    /// Numeric comparison request - application must confirm via numeric_comparison_response()\n    NumericComparisonRequest { conn_handle: u16, numeric_value: u32 },\n    /// Bond lost event - need to allow rebond via allow_rebond()\n    BondLost { conn_handle: u16 },\n    /// Pairing request received (when using SMP mode bit 3)\n    PairingRequest { conn_handle: u16, is_bonded: bool },\n}\n\n/// Pairing completion status\n#[repr(u8)]\n#[derive(Debug, Clone, Copy, PartialEq, Eq)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub enum PairingStatus {\n    /// Pairing completed successfully\n    Success = 0x00,\n    /// Pairing timed out\n    Timeout = 0x01,\n    /// Pairing failed\n    Failed = 0x02,\n}\n\nimpl PairingStatus {\n    /// Convert from u8\n    pub fn from_u8(value: u8) -> Self {\n        match value {\n            0x00 => Self::Success,\n            0x01 => Self::Timeout,\n            _ => Self::Failed,\n        }\n    }\n}\n\n/// Pairing failure reason codes\n#[repr(u8)]\n#[derive(Debug, Clone, Copy, PartialEq, Eq)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub enum PairingFailureReason {\n    PasskeyEntryFailed = 0x01,\n    OobNotAvailable = 0x02,\n    AuthenticationRequirements = 0x03,\n    ConfirmValueFailed = 0x04,\n    PairingNotSupported = 0x05,\n    EncryptionKeySize = 0x06,\n    CommandNotSupported = 0x07,\n    UnspecifiedReason = 0x08,\n    RepeatedAttempts = 0x09,\n    InvalidParameters = 0x0A,\n    DhKeyCheckFailed = 0x0B,\n    NumericComparisonFailed = 0x0C,\n    PairingInProgress = 0x0D,\n    CrossTransportKeyDerivationNotAllowed = 0x0E,\n    Unknown = 0xFF,\n}\n\nimpl PairingFailureReason {\n    /// Convert from u8\n    pub fn from_u8(value: u8) -> Self {\n        match value {\n            0x01 => Self::PasskeyEntryFailed,\n            0x02 => Self::OobNotAvailable,\n            0x03 => Self::AuthenticationRequirements,\n            0x04 => Self::ConfirmValueFailed,\n            0x05 => Self::PairingNotSupported,\n            0x06 => Self::EncryptionKeySize,\n            0x07 => Self::CommandNotSupported,\n            0x08 => Self::UnspecifiedReason,\n            0x09 => Self::RepeatedAttempts,\n            0x0A => Self::InvalidParameters,\n            0x0B => Self::DhKeyCheckFailed,\n            0x0C => Self::NumericComparisonFailed,\n            0x0D => Self::PairingInProgress,\n            0x0E => Self::CrossTransportKeyDerivationNotAllowed,\n            _ => Self::Unknown,\n        }\n    }\n\n    /// Get human-readable description\n    pub fn description(&self) -> &'static str {\n        match self {\n            Self::PasskeyEntryFailed => \"Passkey entry failed\",\n            Self::OobNotAvailable => \"OOB data not available\",\n            Self::AuthenticationRequirements => \"Authentication requirements not met\",\n            Self::ConfirmValueFailed => \"Confirm value failed\",\n            Self::PairingNotSupported => \"Pairing not supported\",\n            Self::EncryptionKeySize => \"Encryption key size too short\",\n            Self::CommandNotSupported => \"Command not supported\",\n            Self::UnspecifiedReason => \"Unspecified reason\",\n            Self::RepeatedAttempts => \"Repeated pairing attempts\",\n            Self::InvalidParameters => \"Invalid parameters\",\n            Self::DhKeyCheckFailed => \"DH key check failed\",\n            Self::NumericComparisonFailed => \"Numeric comparison failed\",\n            Self::PairingInProgress => \"Pairing already in progress\",\n            Self::CrossTransportKeyDerivationNotAllowed => \"Cross-transport key derivation not allowed\",\n            Self::Unknown => \"Unknown reason\",\n        }\n    }\n}\n\n/// Security Manager\n///\n/// Manages BLE security including pairing, bonding, and encryption.\npub struct SecurityManager {\n    initialized: bool,\n}\n\nimpl SecurityManager {\n    /// Create a new Security Manager\n    pub fn new() -> Self {\n        Self { initialized: false }\n    }\n\n    /// Set authentication requirements\n    ///\n    /// This configures the security parameters for all future pairing operations.\n    /// Must be called before connections are established.\n    pub fn set_authentication_requirements(&mut self, params: SecurityParams) -> Result<(), BleError> {\n        unsafe {\n            let status = aci_gap_set_authentication_requirement(\n                params.bonding_mode as u8,\n                params.mitm_protection as u8,\n                params.secure_connections as u8,\n                params.keypress_notification as u8,\n                params.min_encryption_key_size,\n                params.max_encryption_key_size,\n                params.use_fixed_pin as u8,\n                params.fixed_pin,\n                params.identity_address_type as u8,\n            );\n\n            if status == BLE_STATUS_SUCCESS {\n                self.initialized = true;\n                Ok(())\n            } else {\n                Err(BleError::CommandFailed(Status::from_u8(status)))\n            }\n        }\n    }\n\n    /// Respond to a passkey request\n    ///\n    /// Call this when you receive a `SecurityEvent::PasskeyRequest`.\n    /// The passkey must be a 6-digit decimal number (0-999999).\n    pub fn pass_key_response(&self, conn_handle: u16, passkey: u32) -> Result<(), BleError> {\n        if passkey > 999_999 {\n            return Err(BleError::InvalidParameter);\n        }\n\n        unsafe {\n            let status = aci_gap_pass_key_resp(conn_handle, passkey);\n\n            if status == BLE_STATUS_SUCCESS {\n                Ok(())\n            } else {\n                Err(BleError::CommandFailed(Status::from_u8(status)))\n            }\n        }\n    }\n\n    /// Respond to a numeric comparison request\n    ///\n    /// Call this when you receive a `SecurityEvent::NumericComparisonRequest`.\n    /// Pass `true` if the numeric values match, `false` otherwise.\n    pub fn numeric_comparison_response(&self, conn_handle: u16, confirm: bool) -> Result<(), BleError> {\n        unsafe {\n            let status = aci_gap_numeric_comparison_value_confirm_yesno(conn_handle, confirm as u8);\n\n            if status == BLE_STATUS_SUCCESS {\n                Ok(())\n            } else {\n                Err(BleError::CommandFailed(Status::from_u8(status)))\n            }\n        }\n    }\n\n    /// Allow rebonding after receiving BondLost event\n    ///\n    /// Call this when you receive a `SecurityEvent::BondLost` to allow\n    /// the pairing process to continue.\n    pub fn allow_rebond(&self, conn_handle: u16) -> Result<(), BleError> {\n        unsafe {\n            let status = aci_gap_allow_rebond(conn_handle);\n\n            if status == BLE_STATUS_SUCCESS {\n                Ok(())\n            } else {\n                Err(BleError::CommandFailed(Status::from_u8(status)))\n            }\n        }\n    }\n\n    /// Clear all bonding information from the security database\n    ///\n    /// This removes all stored bonds. Use with caution.\n    pub fn clear_security_database(&self) -> Result<(), BleError> {\n        unsafe {\n            let status = aci_gap_clear_security_database();\n\n            if status == BLE_STATUS_SUCCESS {\n                Ok(())\n            } else {\n                Err(BleError::CommandFailed(Status::from_u8(status)))\n            }\n        }\n    }\n\n    /// Remove a specific bonded device\n    pub fn remove_bonded_device(&self, address_type: IdentityAddressType, address: &[u8; 6]) -> Result<(), BleError> {\n        unsafe {\n            let status = aci_gap_remove_bonded_device(address_type as u8, address.as_ptr());\n\n            if status == BLE_STATUS_SUCCESS {\n                Ok(())\n            } else {\n                Err(BleError::CommandFailed(Status::from_u8(status)))\n            }\n        }\n    }\n\n    /// Check if a device is bonded\n    pub fn is_device_bonded(&self, address_type: IdentityAddressType, address: &[u8; 6]) -> Result<bool, BleError> {\n        unsafe {\n            let status = aci_gap_is_device_bonded(address_type as u8, address.as_ptr());\n\n            // BLE_STATUS_SUCCESS means bonded, error code 0x42 means not bonded\n            Ok(status == BLE_STATUS_SUCCESS)\n        }\n    }\n\n    /// Enable or disable address resolution\n    ///\n    /// When enabled, the controller will automatically resolve resolvable\n    /// private addresses (RPA) using stored IRKs.\n    pub fn set_address_resolution_enable(&self, enable: bool) -> Result<(), BleError> {\n        unsafe {\n            let status = hci_le_set_address_resolution_enable(enable as u8);\n\n            if status == BLE_STATUS_SUCCESS {\n                Ok(())\n            } else {\n                Err(BleError::CommandFailed(Status::from_u8(status)))\n            }\n        }\n    }\n\n    /// Set RPA (Resolvable Private Address) timeout\n    ///\n    /// Sets the time interval after which a new RPA is generated.\n    /// Valid range: 1 to 41400 seconds (approximately 11.5 hours).\n    /// Default is usually 900 seconds (15 minutes).\n    pub fn set_rpa_timeout(&self, timeout_seconds: u16) -> Result<(), BleError> {\n        let timeout = timeout_seconds.clamp(1, 41400);\n\n        unsafe {\n            let status = hci_le_set_resolvable_private_address_timeout(timeout);\n\n            if status == BLE_STATUS_SUCCESS {\n                Ok(())\n            } else {\n                Err(BleError::CommandFailed(Status::from_u8(status)))\n            }\n        }\n    }\n\n    /// Check if security has been initialized\n    pub fn is_initialized(&self) -> bool {\n        self.initialized\n    }\n}\n\nimpl Default for SecurityManager {\n    fn default() -> Self {\n        Self::new()\n    }\n}\n\n// ACI event codes for security events\npub mod event_codes {\n    pub const ACI_GAP_PAIRING_COMPLETE: u16 = 0x0401;\n    pub const ACI_GAP_PASS_KEY_REQ: u16 = 0x0402;\n    pub const ACI_GAP_BOND_LOST: u16 = 0x0405;\n    pub const ACI_GAP_NUMERIC_COMPARISON_VALUE: u16 = 0x0409;\n    pub const ACI_GAP_PAIRING_REQUEST: u16 = 0x040B;\n}\n"
  },
  {
    "path": "embassy-stm32-wpan/src/wba/util_seq.rs",
    "content": "#![cfg(feature = \"wba\")]\n//! UTIL Sequencer implementation for STM32WBA BLE stack\n//!\n//! This module provides the sequencer functions required by the ST BLE stack.\n//! The sequencer manages background tasks and event waiting.\n//!\n//! # Context Switching Architecture\n//!\n//! The key insight is that `UTIL_SEQ_WaitEvt` would normally call WFE (wait for event),\n//! which blocks the entire CPU. Instead, we use context switching to yield back to\n//! the embassy executor, allowing other async tasks to run.\n//!\n//! When the sequencer has no work to do, instead of WFE, it yields to the runner task.\n//! The runner task can then yield to the embassy executor, and resume the sequencer\n//! when there's new work (signaled by interrupts).\n\nuse core::cell::UnsafeCell;\nuse core::future::poll_fn;\nuse core::sync::atomic::{AtomicU32, Ordering, compiler_fence};\nuse core::task::Poll;\n\nuse embassy_sync::waitqueue::AtomicWaker;\n\nuse crate::context::ContextManager;\n\ntype TaskFn = unsafe extern \"C\" fn();\n\nunsafe extern \"Rust\" {\n    fn __pender(context: *mut ());\n}\n\nconst MAX_TASKS: usize = 32;\nconst DEFAULT_PRIORITY: u8 = u8::MAX;\n\nstruct TaskTable {\n    funcs: UnsafeCell<[Option<TaskFn>; MAX_TASKS]>,\n    priorities: UnsafeCell<[u8; MAX_TASKS]>,\n}\n\nimpl TaskTable {\n    const fn new() -> Self {\n        Self {\n            funcs: UnsafeCell::new([None; MAX_TASKS]),\n            priorities: UnsafeCell::new([DEFAULT_PRIORITY; MAX_TASKS]),\n        }\n    }\n\n    unsafe fn set_task(&self, idx: usize, func: Option<TaskFn>, priority: u8) {\n        (*self.funcs.get())[idx] = func;\n        (*self.priorities.get())[idx] = priority;\n    }\n\n    unsafe fn update_priority(&self, idx: usize, priority: u8) {\n        (*self.priorities.get())[idx] = priority;\n    }\n\n    unsafe fn task(&self, idx: usize) -> Option<TaskFn> {\n        (*self.funcs.get())[idx]\n    }\n\n    unsafe fn priority(&self, idx: usize) -> u8 {\n        (*self.priorities.get())[idx]\n    }\n}\n\nunsafe impl Sync for TaskTable {}\n\nstruct Sequencer {\n    context: ContextManager,\n    tasks: TaskTable,\n    pending_tasks: AtomicU32,\n    events: AtomicU32,\n    waker: AtomicWaker,\n}\n\nstatic SEQUENCER: Sequencer = Sequencer {\n    context: ContextManager::new(task_entry),\n    tasks: TaskTable::new(),\n    pending_tasks: AtomicU32::new(0),\n    events: AtomicU32::new(0),\n    waker: AtomicWaker::new(),\n};\n\nfn mask_to_index(mask: u32) -> Option<usize> {\n    if mask == 0 {\n        return None;\n    }\n    let idx = mask.trailing_zeros() as usize;\n    if idx < MAX_TASKS { Some(idx) } else { None }\n}\n\npub fn seq_pend() {\n    SEQUENCER.seq_pend();\n}\n\npub fn seq_resume() {\n    SEQUENCER.seq_resume();\n}\n\npub async fn wait_for_event() {\n    SEQUENCER.wait_for_event().await\n}\n\n/// Entry point for the sequencer context\n///\n/// This function runs in the sequencer's stack context and repeatedly\n/// polls for pending tasks, yielding when there's nothing to do.\nextern \"C\" fn task_entry() -> ! {\n    loop {\n        // Poll and execute any pending sequencer tasks\n        SEQUENCER.run();\n\n        // Yield back to the runner\n        // This will return when the runner resumes us\n        SEQUENCER.context.task_yield();\n    }\n}\n\nimpl Sequencer {\n    pub async fn wait_for_event(&self) {\n        poll_fn(|cx| {\n            self.waker.register(cx.waker());\n\n            compiler_fence(Ordering::Acquire);\n\n            if self.has_work() {\n                Poll::Ready(())\n            } else {\n                Poll::Pending\n            }\n        })\n        .await;\n    }\n\n    pub fn seq_resume(&'static self) {\n        self.context.task_resume();\n    }\n\n    fn seq_pend(&self) {\n        self.waker.wake();\n        // TODO: does the waker fire the pender automatically ? We may not need this.\n        unsafe { __pender(u32::MAX as *mut _) };\n    }\n\n    /// Wait for an event\n    ///\n    /// Instead of blocking with WFE, this yields back to the runner context\n    /// so that the embassy executor can run other tasks.\n    #[inline(always)]\n    fn seq_yield(&'static self) {\n        // If we're in the sequencer context, yield back to the runner\n        // If we're not (e.g., during initialization), use actual WFE\n        if self.context.in_task_context() {\n            self.context.task_yield();\n        } else {\n            #[cfg(target_arch = \"arm\")]\n            {\n                cortex_m::asm::wfe();\n            }\n\n            #[cfg(not(target_arch = \"arm\"))]\n            {\n                core::hint::spin_loop();\n            }\n        }\n    }\n\n    /// Check if there are any pending tasks or events\n    pub fn has_work(&self) -> bool {\n        self.pending_tasks.load(Ordering::Acquire) != 0 || self.events.load(Ordering::Acquire) != 0\n    }\n\n    fn select_next_task(&self) -> Option<(usize, TaskFn)> {\n        let pending = self.pending_tasks.load(Ordering::Acquire);\n        if pending == 0 {\n            return None;\n        }\n\n        let mut remaining = pending;\n        let mut best_idx: Option<usize> = None;\n        let mut best_priority = DEFAULT_PRIORITY;\n        let mut best_fn: Option<TaskFn> = None;\n\n        while remaining != 0 {\n            let idx = remaining.trailing_zeros() as usize;\n            remaining &= remaining - 1;\n\n            if idx >= MAX_TASKS {\n                continue;\n            }\n\n            unsafe {\n                if let Some(func) = self.tasks.task(idx) {\n                    let prio = self.tasks.priority(idx);\n                    if prio <= best_priority {\n                        if prio < best_priority || best_idx.map_or(true, |current| idx < current) {\n                            best_priority = prio;\n                            best_idx = Some(idx);\n                            best_fn = Some(func);\n                        }\n                    }\n                } else {\n                    self.pending_tasks.fetch_and(!(1u32 << idx), Ordering::AcqRel);\n                }\n            }\n        }\n\n        if let (Some(idx), Some(func)) = (best_idx, best_fn) {\n            self.pending_tasks.fetch_and(!(1u32 << idx), Ordering::AcqRel);\n            Some((idx, func))\n        } else {\n            None\n        }\n    }\n\n    /// Poll and execute any tasks that have been scheduled via the UTIL sequencer API.\n    pub fn run(&self) {\n        compiler_fence(Ordering::Acquire);\n        loop {\n            loop {\n                let next = critical_section::with(|_| self.select_next_task());\n                match next {\n                    Some((idx, task)) => unsafe {\n                        task();\n                        // Force a fresh read of the pending bitmask after each task completion.\n                        // TODO: this appears to do nothing (will be optimized away)\n                        let _ = idx;\n                    },\n                    None => break,\n                }\n            }\n\n            if self.pending_tasks.load(Ordering::Acquire) == 0 {\n                break;\n            }\n        }\n\n        compiler_fence(Ordering::Release);\n    }\n}\n\n#[unsafe(no_mangle)]\npub extern \"C\" fn UTIL_SEQ_RegTask(task_mask: u32, _flags: u32, task: Option<TaskFn>) {\n    #[cfg(feature = \"defmt\")]\n    defmt::trace!(\"UTIL_SEQ_RegTask: mask=0x{:08X}, task={:?}\", task_mask, task);\n\n    if let Some(idx) = mask_to_index(task_mask) {\n        critical_section::with(|_| unsafe {\n            SEQUENCER.tasks.set_task(idx, task, DEFAULT_PRIORITY);\n        });\n    }\n}\n\n#[unsafe(no_mangle)]\npub extern \"C\" fn UTIL_SEQ_UnregTask(task_mask: u32) {\n    if let Some(idx) = mask_to_index(task_mask) {\n        critical_section::with(|_| unsafe {\n            SEQUENCER.tasks.set_task(idx, None, DEFAULT_PRIORITY);\n        });\n        SEQUENCER.pending_tasks.fetch_and(!(task_mask), Ordering::AcqRel);\n    }\n}\n\n#[unsafe(no_mangle)]\npub extern \"C\" fn UTIL_SEQ_SetTask(task_mask: u32, priority: u32) {\n    #[cfg(feature = \"defmt\")]\n    defmt::trace!(\"UTIL_SEQ_SetTask: mask=0x{:08X}, prio={}\", task_mask, priority);\n\n    let prio = (priority & 0xFF) as u8;\n\n    if let Some(idx) = mask_to_index(task_mask) {\n        let registered = critical_section::with(|_| unsafe {\n            if SEQUENCER.tasks.task(idx).is_some() {\n                SEQUENCER.tasks.update_priority(idx, prio);\n                true\n            } else {\n                false\n            }\n        });\n\n        if registered {\n            SEQUENCER.pending_tasks.fetch_or(task_mask, Ordering::Release);\n            SEQUENCER.seq_pend();\n        }\n    }\n}\n\n#[unsafe(no_mangle)]\npub extern \"C\" fn UTIL_SEQ_ResumeTask(task_mask: u32) {\n    SEQUENCER.pending_tasks.fetch_or(task_mask, Ordering::Release);\n    SEQUENCER.seq_pend();\n}\n\n#[unsafe(no_mangle)]\npub extern \"C\" fn UTIL_SEQ_PauseTask(task_mask: u32) {\n    SEQUENCER.pending_tasks.fetch_and(!task_mask, Ordering::AcqRel);\n}\n\n#[unsafe(no_mangle)]\npub extern \"C\" fn UTIL_SEQ_SetEvt(event_mask: u32) {\n    #[cfg(feature = \"defmt\")]\n    defmt::trace!(\"UTIL_SEQ_SetEvt: mask=0x{:08X}\", event_mask);\n\n    SEQUENCER.events.fetch_or(event_mask, Ordering::Release);\n    SEQUENCER.seq_pend();\n}\n\n#[unsafe(no_mangle)]\npub extern \"C\" fn UTIL_SEQ_ClrEvt(event_mask: u32) {\n    SEQUENCER.events.fetch_and(!event_mask, Ordering::AcqRel);\n}\n\n#[unsafe(no_mangle)]\npub extern \"C\" fn UTIL_SEQ_IsEvtSet(event_mask: u32) -> u32 {\n    let state = SEQUENCER.events.load(Ordering::Acquire);\n    if (state & event_mask) == event_mask { 1 } else { 0 }\n}\n\n#[unsafe(no_mangle)]\npub extern \"C\" fn UTIL_SEQ_WaitEvt(event_mask: u32) {\n    #[cfg(feature = \"defmt\")]\n    defmt::trace!(\"UTIL_SEQ_WaitEvt: mask=0x{:08X}\", event_mask);\n\n    loop {\n        SEQUENCER.run();\n\n        let current = SEQUENCER.events.load(Ordering::Acquire);\n        if (current & event_mask) == event_mask {\n            SEQUENCER.events.fetch_and(!event_mask, Ordering::AcqRel);\n            #[cfg(feature = \"defmt\")]\n            defmt::trace!(\"UTIL_SEQ_WaitEvt: event received\");\n            break;\n        }\n\n        #[cfg(feature = \"defmt\")]\n        defmt::trace!(\n            \"UTIL_SEQ_WaitEvt: waiting (in_seq_ctx={})\",\n            SEQUENCER.context.in_task_context()\n        );\n\n        SEQUENCER.seq_yield();\n    }\n}\n"
  },
  {
    "path": "embassy-stm32-wpan/tl_mbox.x.in",
    "content": "MEMORY \n{\n    RAM_SHARED (xrw)           : ORIGIN = 0x20030000, LENGTH = 10K\n}\n\n/*\n * Scatter the mailbox interface memory sections in shared memory\n */\nSECTIONS\n{\n    TL_REF_TABLE                     (NOLOAD) : { *(TL_REF_TABLE) } >RAM_SHARED\n\n    MB_MEM1 (NOLOAD)                          : { *(MB_MEM1) } >RAM_SHARED\n    MB_MEM2 (NOLOAD)                          : { _sMB_MEM2 = . ; *(MB_MEM2) ; _eMB_MEM2 = . ; } >RAM_SHARED\n}\n"
  },
  {
    "path": "embassy-stm32-wpan/tl_mbox_extended_wb1.x.in",
    "content": "MEMORY \n{\n    RAM_SHARED (xrw)           : ORIGIN = 0x20030000, LENGTH = 4K\n    RAMB_SHARED (xrw)          : ORIGIN = 0x20030028, LENGTH = 4K\n}\n\n/*\n * Scatter the mailbox interface memory sections in shared memory\n */\nSECTIONS\n{\n    TL_REF_TABLE                     (NOLOAD) : { *(TL_REF_TABLE) } >RAM_SHARED\n\n    MB_MEM1 (NOLOAD)                          : { *(MB_MEM1) } >RAMB_SHARED\n    MB_MEM2 (NOLOAD)                          : { _sMB_MEM2 = . ; *(MB_MEM2) ; _eMB_MEM2 = . ; } >RAMB_SHARED\n}\n"
  },
  {
    "path": "embassy-stm32-wpan/tl_mbox_extended_wbx5.x.in",
    "content": "MEMORY \n{\n    RAM_SHARED (xrw)           : ORIGIN = 0x20030000, LENGTH = 2K\n    RAMB_SHARED (xrw)          : ORIGIN = 0x20038000, LENGTH = 10K\n}\n\n/*\n * Scatter the mailbox interface memory sections in shared memory\n */\nSECTIONS\n{\n    TL_REF_TABLE                     (NOLOAD) : { *(TL_REF_TABLE) } >RAM_SHARED\n\n    MB_MEM1 (NOLOAD)                          : { *(MB_MEM1) } >RAMB_SHARED\n    MB_MEM2 (NOLOAD)                          : { _sMB_MEM2 = . ; *(MB_MEM2) ; _eMB_MEM2 = . ; } >RAMB_SHARED\n}\n"
  },
  {
    "path": "embassy-sync/CHANGELOG.md",
    "content": "# Changelog\n\nAll notable changes to this project will be documented in this file.\n\nThe format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),\nand this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).\n\n<!-- next-header -->\n## Unreleased - ReleaseDate\n\n## 0.8.0 - 2026-03-10\n- Fix wakers getting dropped by `Signal::reset`\n- Remove `Sized` trait bound from `MutexGuard::map`\n- Update to `embedded-io-async` 0.7.0\n- Fix `Pipe::try_write` docs\n- Implement `futures_sink::Sink` for `Channel` and `channel::Sender`\n\n## 0.7.2 - 2025-08-26\n\n- Add `get_mut` to `LazyLock`\n- Add more `Debug` impls to `embassy-sync`, particularly on `OnceLock`\n\n## 0.7.0 - 2025-05-28\n\n- Add `remove_if` to `priority_channel::{Receiver, PriorityChannel}`.\n- impl `Stream` for `channel::{Receiver, Channel}`.\n- Fix channels to wake senders on `clear()`.\n  For `Channel`, `PriorityChannel`, `PubSub`, `zerocopy_channel::Channel`.\n- Allow `zerocopy_channel::Channel` to auto-implement `Sync`/`Send`.\n- Add `must_use` to `MutexGuard`.\n- Add a `RwLock`.\n- Add `lock_mut` to `blocking_mutex::Mutex`.\n- Don't select a critical-section implementation when `std` feature is enabled.\n- Improve waker documentation.\n- Improve `Signal` and `Watch` documentation.\n- Update to defmt 1.0. This remains compatible with latest defmt 0.3.\n- Add `peek` method on `channel` and `priority_channel`.\n- Add dynamic sender and receiver that are Send + Sync for `channel`.\n\n## 0.6.2 - 2025-01-15\n\n- Add dynamic dispatch variant of `Pipe`.\n\n## 0.6.1 - 2024-11-22\n\n- Add `LazyLock` sync primitive.\n- Add `Watch` sync primitive.\n- Add `clear`, `len`, `is_empty` and `is_full` functions to `zerocopy_channel`.\n- Add `capacity`, `free_capacity`, `clear`, `len`, `is_empty` and `is_full` functions to `channel::{Sender, Receiver}`.\n- Add `capacity`, `free_capacity`, `clear`, `len`, `is_empty` and `is_full` functions to `priority_channel::{Sender, Receiver}`.\n- Add `GenericAtomicWaker` utility.\n\n## 0.6.0 - 2024-05-29\n\n- Add `capacity`, `free_capacity`, `clear`, `len`, `is_empty` and `is_full` functions to `Channel`.\n- Add `capacity`, `free_capacity`, `clear`, `len`, `is_empty` and `is_full` functions to `PriorityChannel`.\n- Add `capacity`, `free_capacity`, `clear`, `len`, `is_empty` and `is_full` functions to `PubSubChannel`.\n- Made `PubSubBehavior` sealed\n  - If you called `.publish_immediate(...)` on the queue directly before, then now call `.immediate_publisher().publish_immediate(...)`\n- Add `OnceLock` sync primitive.\n- Add constructor for `DynamicChannel`\n- Add ready_to_receive functions to `Channel` and `Receiver`.\n\n## 0.5.0 - 2023-12-04\n\n- Add a `PriorityChannel`.\n- Remove `nightly` and `unstable-traits` features in preparation for 1.75.\n- Upgrade `heapless` to 0.8.\n- Upgrade `static-cell` to 2.0.\n\n## 0.4.0 - 2023-10-31\n\n- Re-add `impl_trait_projections`\n- switch to `embedded-io 0.6`\n\n## 0.3.0 - 2023-09-14\n\n- switch to `embedded-io 0.5`\n- add api for polling channels with context\n- standardise fn names on channels\n- add zero-copy channel\n\n## 0.2.0 - 2023-04-13\n\n- pubsub: Fix messages not getting popped when the last subscriber that needed them gets dropped.\n- pubsub: Move instead of clone messages when the last subscriber pops them.\n- pubsub: Pop messages which count is 0 after unsubscribe.\n- Update `embedded-io` from `0.3` to `0.4` (uses `async fn` in traits).\n- impl `Default` for `WakerRegistration`\n- impl `Default` for `Signal`\n- Remove unnecessary uses of `atomic-polyfill`\n- Add `#[must_use]` to all futures.\n\n## 0.1.0 - 2022-08-26\n\n- First release\n"
  },
  {
    "path": "embassy-sync/Cargo.toml",
    "content": "[package]\nname = \"embassy-sync\"\nversion = \"0.8.0\"\nedition = \"2024\"\ndescription = \"no-std, no-alloc synchronization primitives with async support\"\nrepository = \"https://github.com/embassy-rs/embassy\"\ndocumentation = \"https://docs.embassy.dev/embassy-sync\"\nreadme = \"README.md\"\nlicense = \"MIT OR Apache-2.0\"\ncategories = [\n    \"embedded\",\n    \"no-std\",\n    \"concurrency\",\n    \"asynchronous\",\n]\n\n[package.metadata.embassy]\nbuild = [\n    {target = \"thumbv6m-none-eabi\", features = [\"defmt\"]},\n    # Xtensa builds\n    {group = \"xtensa\", build-std = [\"core\", \"alloc\"],  target = \"xtensa-esp32s2-none-elf\", features = [\"defmt\"]},\n]\n\n[package.metadata.embassy_docs]\nsrc_base = \"https://github.com/embassy-rs/embassy/blob/embassy-sync-v$VERSION/embassy-sync/src/\"\nsrc_base_git = \"https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-sync/src/\"\ntarget = \"thumbv7em-none-eabi\"\n\n[features]\ndefmt = [\"dep:defmt\"]\nlog = [\"dep:log\"]\nstd = []\nturbowakers = []\n\n[dependencies]\ndefmt = { version = \"1.0.1\", optional = true }\nlog = { version = \"0.4.14\", optional = true }\n\nfutures-sink = { version = \"0.3\", default-features = false, features = [] }\nfutures-core = { version = \"0.3.31\", default-features = false }\ncritical-section = \"1.1\"\nheapless = \"0.9\"\ncfg-if = \"1.0.0\"\nembedded-io-async = { version = \"0.7.0\" }\n\n[dev-dependencies]\nfutures-executor = { version = \"0.3.17\", features = [ \"thread-pool\" ] }\nfutures-test = \"0.3.17\"\nfutures-timer = \"3.0.2\"\nfutures-util = { version = \"0.3.17\", features = [ \"channel\", \"sink\" ] }\n\n# Enable critical-section implementation for std, for tests\ncritical-section = { version = \"1.1\", features = [\"std\"] }\nstatic_cell = { version = \"2\" }\ntrybuild = \"1.0.105\"\n"
  },
  {
    "path": "embassy-sync/README.md",
    "content": "# embassy-sync\n\nAn [Embassy](https://embassy.dev) project.\n\nSynchronization primitives and data structures with async support:\n\n- [`Channel`](channel::Channel) - A Multiple Producer Multiple Consumer (MPMC) channel. Each message is only received by a single consumer.\n- [`PriorityChannel`](priority_channel::PriorityChannel) - A Multiple Producer Multiple Consumer (MPMC) channel. Each message is only received by a single consumer. Higher priority items are shifted to the front of the channel.\n- [`PubSubChannel`](pubsub::PubSubChannel) - A broadcast channel (publish-subscribe) channel. Each message is received by all consumers.\n- [`Signal`](signal::Signal) - Signalling latest value to a single consumer.\n- [`Watch`](watch::Watch) - Signalling latest value to multiple consumers.\n- [`Mutex`](mutex::Mutex) - Mutex for synchronizing state between asynchronous tasks.\n- [`Pipe`](pipe::Pipe) - Byte stream implementing `embedded_io` traits.\n- [`WakerRegistration`](waitqueue::WakerRegistration) - Utility to register and wake a `Waker`.\n- [`AtomicWaker`](waitqueue::AtomicWaker) - Utility to register and wake a `Waker` from interrupt context.\n- [`MultiWakerRegistration`](waitqueue::MultiWakerRegistration) - Utility registering and waking multiple `Waker`'s.\n- [`LazyLock`](lazy_lock::LazyLock) - A value which is initialized on the first access\n\n## Interoperability\n\nFutures from this crate can run on any executor.\n"
  },
  {
    "path": "embassy-sync/build.rs",
    "content": "#[path = \"./build_common.rs\"]\nmod common;\n\nfn main() {\n    let mut cfgs = common::CfgSet::new();\n    common::set_target_cfgs(&mut cfgs);\n}\n"
  },
  {
    "path": "embassy-sync/build_common.rs",
    "content": "// NOTE: this file is copy-pasted between several Embassy crates, because there is no\n// straightforward way to share this code:\n// - it cannot be placed into the root of the repo and linked from each build.rs using `#[path =\n// \"../build_common.rs\"]`, because `cargo publish` requires that all files published with a crate\n// reside in the crate's directory,\n// - it cannot be symlinked from `embassy-xxx/build_common.rs` to `../build_common.rs`, because\n// symlinks don't work on Windows.\n\nuse std::collections::HashSet;\nuse std::env;\n\n/// Helper for emitting cargo instruction for enabling configs (`cargo:rustc-cfg=X`) and declaring\n/// them (`cargo:rust-check-cfg=cfg(X)`).\n#[derive(Debug)]\npub struct CfgSet {\n    enabled: HashSet<String>,\n    declared: HashSet<String>,\n}\n\nimpl CfgSet {\n    pub fn new() -> Self {\n        Self {\n            enabled: HashSet::new(),\n            declared: HashSet::new(),\n        }\n    }\n\n    /// Enable a config, which can then be used in `#[cfg(...)]` for conditional compilation.\n    ///\n    /// All configs that can potentially be enabled should be unconditionally declared using\n    /// [`Self::declare()`].\n    pub fn enable(&mut self, cfg: impl AsRef<str>) {\n        if self.enabled.insert(cfg.as_ref().to_owned()) {\n            println!(\"cargo:rustc-cfg={}\", cfg.as_ref());\n        }\n    }\n\n    pub fn enable_all(&mut self, cfgs: &[impl AsRef<str>]) {\n        for cfg in cfgs.iter() {\n            self.enable(cfg.as_ref());\n        }\n    }\n\n    /// Declare a valid config for conditional compilation, without enabling it.\n    ///\n    /// This enables rustc to check that the configs in `#[cfg(...)]` attributes are valid.\n    pub fn declare(&mut self, cfg: impl AsRef<str>) {\n        if self.declared.insert(cfg.as_ref().to_owned()) {\n            println!(\"cargo:rustc-check-cfg=cfg({})\", cfg.as_ref());\n        }\n    }\n\n    pub fn declare_all(&mut self, cfgs: &[impl AsRef<str>]) {\n        for cfg in cfgs.iter() {\n            self.declare(cfg.as_ref());\n        }\n    }\n\n    pub fn set(&mut self, cfg: impl Into<String>, enable: bool) {\n        let cfg = cfg.into();\n        if enable {\n            self.enable(cfg.clone());\n        }\n        self.declare(cfg);\n    }\n}\n\n/// Sets configs that describe the target platform.\npub fn set_target_cfgs(cfgs: &mut CfgSet) {\n    let target = env::var(\"TARGET\").unwrap();\n\n    if target.starts_with(\"thumbv6m-\") {\n        cfgs.enable_all(&[\"cortex_m\", \"armv6m\"]);\n    } else if target.starts_with(\"thumbv7m-\") {\n        cfgs.enable_all(&[\"cortex_m\", \"armv7m\"]);\n    } else if target.starts_with(\"thumbv7em-\") {\n        cfgs.enable_all(&[\"cortex_m\", \"armv7m\", \"armv7em\"]);\n    } else if target.starts_with(\"thumbv8m.base\") {\n        cfgs.enable_all(&[\"cortex_m\", \"armv8m\", \"armv8m_base\"]);\n    } else if target.starts_with(\"thumbv8m.main\") {\n        cfgs.enable_all(&[\"cortex_m\", \"armv8m\", \"armv8m_main\"]);\n    }\n    cfgs.declare_all(&[\n        \"cortex_m\",\n        \"armv6m\",\n        \"armv7m\",\n        \"armv7em\",\n        \"armv8m\",\n        \"armv8m_base\",\n        \"armv8m_main\",\n    ]);\n\n    cfgs.set(\"has_fpu\", target.ends_with(\"-eabihf\"));\n}\n"
  },
  {
    "path": "embassy-sync/src/blocking_mutex/mod.rs",
    "content": "//! Blocking mutex.\n//!\n//! This module provides a blocking mutex that can be used to synchronize data.\npub mod raw;\n\nuse core::cell::UnsafeCell;\n\nuse self::raw::RawMutex;\n\n/// Blocking mutex (not async)\n///\n/// Provides a blocking mutual exclusion primitive backed by an implementation of [`raw::RawMutex`].\n///\n/// Which implementation you select depends on the context in which you're using the mutex, and you can choose which kind\n/// of interior mutability fits your use case.\n///\n/// Use [`CriticalSectionMutex`] when data can be shared between threads and interrupts.\n///\n/// Use [`NoopMutex`] when data is only shared between tasks running on the same executor.\n///\n/// Use [`ThreadModeMutex`] when data is shared between tasks running on the same executor but you want a global singleton.\n///\n/// In all cases, the blocking mutex is intended to be short lived and not held across await points.\n/// Use the async [`Mutex`](crate::mutex::Mutex) if you need a lock that is held across await points.\n#[derive(Debug)]\npub struct Mutex<R, T: ?Sized> {\n    // NOTE: `raw` must be FIRST, so when using ThreadModeMutex the \"can't drop in non-thread-mode\" gets\n    // to run BEFORE dropping `data`.\n    raw: R,\n    data: UnsafeCell<T>,\n}\n\nunsafe impl<R: RawMutex + Send, T: ?Sized + Send> Send for Mutex<R, T> {}\nunsafe impl<R: RawMutex + Sync, T: ?Sized + Send> Sync for Mutex<R, T> {}\n\nimpl<R: RawMutex, T> Mutex<R, T> {\n    /// Creates a new mutex in an unlocked state ready for use.\n    #[inline]\n    pub const fn new(val: T) -> Mutex<R, T> {\n        Mutex {\n            raw: R::INIT,\n            data: UnsafeCell::new(val),\n        }\n    }\n\n    /// Creates a critical section and grants temporary access to the protected data.\n    pub fn lock<U>(&self, f: impl FnOnce(&T) -> U) -> U {\n        self.raw.lock(|| {\n            let ptr = self.data.get() as *const T;\n            let inner = unsafe { &*ptr };\n            f(inner)\n        })\n    }\n\n    /// Creates a critical section and grants temporary mutable access to the protected data.\n    ///\n    /// # Safety\n    ///\n    /// This method is marked unsafe because calling this method re-entrantly, i.e. within\n    /// another `lock_mut` or `lock` closure, violates Rust's aliasing rules. Calling this\n    /// method at the same time from different tasks is safe. For a safe alternative with\n    /// mutable access that never causes UB, use a `RefCell` in a `Mutex`.\n    pub unsafe fn lock_mut<U>(&self, f: impl FnOnce(&mut T) -> U) -> U {\n        self.raw.lock(|| {\n            let ptr = self.data.get() as *mut T;\n            // Safety: we have exclusive access to the data, as long as this mutex is not locked re-entrantly\n            let inner = unsafe { &mut *ptr };\n            f(inner)\n        })\n    }\n}\n\nimpl<R, T> Mutex<R, T> {\n    /// Creates a new mutex based on a pre-existing raw mutex.\n    ///\n    /// This allows creating a mutex in a constant context on stable Rust.\n    #[inline]\n    pub const fn const_new(raw_mutex: R, val: T) -> Mutex<R, T> {\n        Mutex {\n            raw: raw_mutex,\n            data: UnsafeCell::new(val),\n        }\n    }\n\n    /// Consumes this mutex, returning the underlying data.\n    #[inline]\n    pub fn into_inner(self) -> T {\n        self.data.into_inner()\n    }\n\n    /// Returns a mutable reference to the underlying data.\n    ///\n    /// Since this call borrows the `Mutex` mutably, no actual locking needs to\n    /// take place---the mutable borrow statically guarantees no locks exist.\n    #[inline]\n    pub fn get_mut(&mut self) -> &mut T {\n        unsafe { &mut *self.data.get() }\n    }\n}\n\n/// A mutex that allows borrowing data across executors and interrupts.\n///\n/// # Safety\n///\n/// This mutex is safe to share between different executors and interrupts.\npub type CriticalSectionMutex<T> = Mutex<raw::CriticalSectionRawMutex, T>;\n\n/// A mutex that allows borrowing data in the context of a single executor.\n///\n/// # Safety\n///\n/// **This Mutex is only safe within a single executor.**\npub type NoopMutex<T> = Mutex<raw::NoopRawMutex, T>;\n\nimpl<T> Mutex<raw::CriticalSectionRawMutex, T> {\n    /// Borrows the data for the duration of the critical section\n    pub fn borrow<'cs>(&'cs self, _cs: critical_section::CriticalSection<'cs>) -> &'cs T {\n        let ptr = self.data.get() as *const T;\n        unsafe { &*ptr }\n    }\n}\n\nimpl<T> Mutex<raw::NoopRawMutex, T> {\n    /// Borrows the data\n    #[allow(clippy::should_implement_trait)]\n    pub fn borrow(&self) -> &T {\n        let ptr = self.data.get() as *const T;\n        unsafe { &*ptr }\n    }\n}\n\n// ThreadModeMutex does NOT use the generic mutex from above because it's special:\n// it's Send+Sync even if T: !Send. There's no way to do that without specialization (I think?).\n//\n// There's still a ThreadModeRawMutex for use with the generic Mutex (handy with Channel, for example),\n// but that will require T: Send even though it shouldn't be needed.\n\n#[cfg(any(cortex_m, doc, feature = \"std\"))]\npub use thread_mode_mutex::*;\n#[cfg(any(cortex_m, doc, feature = \"std\"))]\nmod thread_mode_mutex {\n    use super::*;\n\n    /// A \"mutex\" that only allows borrowing from thread mode.\n    ///\n    /// # Safety\n    ///\n    /// **This Mutex is only safe on single-core systems.**\n    ///\n    /// On multi-core systems, a `ThreadModeMutex` **is not sufficient** to ensure exclusive access.\n    pub struct ThreadModeMutex<T: ?Sized> {\n        inner: UnsafeCell<T>,\n    }\n\n    // NOTE: ThreadModeMutex only allows borrowing from one execution context ever: thread mode.\n    // Therefore it cannot be used to send non-sendable stuff between execution contexts, so it can\n    // be Send+Sync even if T is not Send (unlike CriticalSectionMutex)\n    unsafe impl<T: ?Sized> Sync for ThreadModeMutex<T> {}\n    unsafe impl<T: ?Sized> Send for ThreadModeMutex<T> {}\n\n    impl<T> ThreadModeMutex<T> {\n        /// Creates a new mutex\n        pub const fn new(value: T) -> Self {\n            ThreadModeMutex {\n                inner: UnsafeCell::new(value),\n            }\n        }\n    }\n\n    impl<T: ?Sized> ThreadModeMutex<T> {\n        /// Lock the `ThreadModeMutex`, granting access to the data.\n        ///\n        /// # Panics\n        ///\n        /// This will panic if not currently running in thread mode.\n        pub fn lock<R>(&self, f: impl FnOnce(&T) -> R) -> R {\n            f(self.borrow())\n        }\n\n        /// Borrows the data\n        ///\n        /// # Panics\n        ///\n        /// This will panic if not currently running in thread mode.\n        pub fn borrow(&self) -> &T {\n            assert!(\n                raw::in_thread_mode(),\n                \"ThreadModeMutex can only be borrowed from thread mode.\"\n            );\n            unsafe { &*self.inner.get() }\n        }\n    }\n\n    impl<T: ?Sized> Drop for ThreadModeMutex<T> {\n        fn drop(&mut self) {\n            // Only allow dropping from thread mode. Dropping calls drop on the inner `T`, so\n            // `drop` needs the same guarantees as `lock`. `ThreadModeMutex<T>` is Send even if\n            // T isn't, so without this check a user could create a ThreadModeMutex in thread mode,\n            // send it to interrupt context and drop it there, which would \"send\" a T even if T is not Send.\n            assert!(\n                raw::in_thread_mode(),\n                \"ThreadModeMutex can only be dropped from thread mode.\"\n            );\n\n            // Drop of the inner `T` happens after this.\n        }\n    }\n}\n"
  },
  {
    "path": "embassy-sync/src/blocking_mutex/raw.rs",
    "content": "//! Mutex primitives.\n//!\n//! This module provides a trait for mutexes that can be used in different contexts.\nuse core::marker::PhantomData;\n\n/// Raw mutex trait.\n///\n/// This mutex is \"raw\", which means it does not actually contain the protected data, it\n/// just implements the mutex mechanism. For most uses you should use [`super::Mutex`] instead,\n/// which is generic over a RawMutex and contains the protected data.\n///\n/// Note that, unlike other mutexes, implementations only guarantee no\n/// concurrent access from other threads: concurrent access from the current\n/// thread is allowed. For example, it's possible to lock the same mutex multiple times reentrantly.\n///\n/// Therefore, locking a `RawMutex` is only enough to guarantee safe shared (`&`) access\n/// to the data, it is not enough to guarantee exclusive (`&mut`) access.\n///\n/// # Safety\n///\n/// RawMutex implementations must ensure that, while locked, no other thread can lock\n/// the RawMutex concurrently.\n///\n/// Unsafe code is allowed to rely on this fact, so incorrect implementations will cause undefined behavior.\npub unsafe trait RawMutex {\n    /// Create a new `RawMutex` instance.\n    ///\n    /// This is a const instead of a method to allow creating instances in const context.\n    const INIT: Self;\n\n    /// Lock this `RawMutex`.\n    fn lock<R>(&self, f: impl FnOnce() -> R) -> R;\n}\n\n/// A mutex that allows borrowing data across executors and interrupts.\n///\n/// # Safety\n///\n/// This mutex is safe to share between different executors and interrupts.\n#[derive(Debug)]\npub struct CriticalSectionRawMutex {\n    _phantom: PhantomData<()>,\n}\nunsafe impl Send for CriticalSectionRawMutex {}\nunsafe impl Sync for CriticalSectionRawMutex {}\n\nimpl CriticalSectionRawMutex {\n    /// Create a new `CriticalSectionRawMutex`.\n    pub const fn new() -> Self {\n        Self { _phantom: PhantomData }\n    }\n}\n\nunsafe impl RawMutex for CriticalSectionRawMutex {\n    const INIT: Self = Self::new();\n\n    fn lock<R>(&self, f: impl FnOnce() -> R) -> R {\n        critical_section::with(|_| f())\n    }\n}\n\n// ================\n\n/// A mutex that allows borrowing data in the context of a single executor.\n///\n/// # Safety\n///\n/// **This Mutex is only safe within a single executor.**\n#[derive(Debug)]\npub struct NoopRawMutex {\n    _phantom: PhantomData<*mut ()>,\n}\n\nunsafe impl Send for NoopRawMutex {}\n\nimpl NoopRawMutex {\n    /// Create a new `NoopRawMutex`.\n    pub const fn new() -> Self {\n        Self { _phantom: PhantomData }\n    }\n}\n\nunsafe impl RawMutex for NoopRawMutex {\n    const INIT: Self = Self::new();\n    fn lock<R>(&self, f: impl FnOnce() -> R) -> R {\n        f()\n    }\n}\n\n// ================\n\n#[cfg(any(cortex_m, doc, feature = \"std\"))]\nmod thread_mode {\n    use super::*;\n\n    /// A \"mutex\" that only allows borrowing from thread mode.\n    ///\n    /// # Safety\n    ///\n    /// **This Mutex is only safe on single-core systems.**\n    ///\n    /// On multi-core systems, a `ThreadModeRawMutex` **is not sufficient** to ensure exclusive access.\n    pub struct ThreadModeRawMutex {\n        _phantom: PhantomData<()>,\n    }\n\n    unsafe impl Send for ThreadModeRawMutex {}\n    unsafe impl Sync for ThreadModeRawMutex {}\n\n    impl ThreadModeRawMutex {\n        /// Create a new `ThreadModeRawMutex`.\n        pub const fn new() -> Self {\n            Self { _phantom: PhantomData }\n        }\n    }\n\n    unsafe impl RawMutex for ThreadModeRawMutex {\n        const INIT: Self = Self::new();\n        fn lock<R>(&self, f: impl FnOnce() -> R) -> R {\n            assert!(in_thread_mode(), \"ThreadModeMutex can only be locked from thread mode.\");\n\n            f()\n        }\n    }\n\n    impl Drop for ThreadModeRawMutex {\n        fn drop(&mut self) {\n            // Only allow dropping from thread mode. Dropping calls drop on the inner `T`, so\n            // `drop` needs the same guarantees as `lock`. `ThreadModeMutex<T>` is Send even if\n            // T isn't, so without this check a user could create a ThreadModeMutex in thread mode,\n            // send it to interrupt context and drop it there, which would \"send\" a T even if T is not Send.\n            assert!(\n                in_thread_mode(),\n                \"ThreadModeMutex can only be dropped from thread mode.\"\n            );\n\n            // Drop of the inner `T` happens after this.\n        }\n    }\n\n    pub(crate) fn in_thread_mode() -> bool {\n        #[cfg(feature = \"std\")]\n        return Some(\"main\") == std::thread::current().name();\n\n        #[cfg(not(feature = \"std\"))]\n        // ICSR.VECTACTIVE == 0\n        return unsafe { (0xE000ED04 as *const u32).read_volatile() } & 0x1FF == 0;\n    }\n}\n#[cfg(any(cortex_m, doc, feature = \"std\"))]\npub use thread_mode::*;\n"
  },
  {
    "path": "embassy-sync/src/channel.rs",
    "content": "//! A queue for sending values between asynchronous tasks.\n//!\n//! It can be used concurrently by multiple producers (senders) and multiple\n//! consumers (receivers), i.e. it is an  \"MPMC channel\".\n//!\n//! Receivers are competing for messages. So a message that is received by\n//! one receiver is not received by any other.\n//!\n//! This queue takes a Mutex type so that various\n//! targets can be attained. For example, a ThreadModeMutex can be used\n//! for single-core Cortex-M targets where messages are only passed\n//! between tasks running in thread mode. Similarly, a CriticalSectionMutex\n//! can also be used for single-core targets where messages are to be\n//! passed from exception mode e.g. out of an interrupt handler.\n//!\n//! This module provides a bounded channel that has a limit on the number of\n//! messages that it can store, and if this limit is reached, trying to send\n//! another message will result in an error being returned.\n//!\n//! # Example: Message passing between task and interrupt handler\n//!\n//! ```rust\n//! use embassy_sync::channel::Channel;\n//! use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;\n//!\n//! static SHARED_CHANNEL: Channel<CriticalSectionRawMutex, u32, 8> = Channel::new();\n//!\n//! fn my_interrupt_handler() {\n//!     // Do some work..\n//!     // ...\n//!     if let Err(e) = SHARED_CHANNEL.sender().try_send(42) {\n//!         // Channel is full..\n//!     }\n//! }\n//!\n//! async fn my_async_task() {\n//!     // ...\n//!     let receiver = SHARED_CHANNEL.receiver();\n//!     loop {\n//!         let data_from_interrupt = receiver.receive().await;\n//!         // Do something with the data.\n//!     }\n//! }\n//! ```\n\nuse core::cell::RefCell;\nuse core::future::Future;\nuse core::pin::Pin;\nuse core::task::{Context, Poll};\n\nuse heapless::Deque;\n\nuse crate::blocking_mutex::Mutex;\nuse crate::blocking_mutex::raw::RawMutex;\nuse crate::waitqueue::WakerRegistration;\n\n/// Send-only access to a [`Channel`].\n#[derive(Debug)]\npub struct Sender<'ch, M, T, const N: usize>\nwhere\n    M: RawMutex,\n{\n    channel: &'ch Channel<M, T, N>,\n}\n\nimpl<'ch, M, T, const N: usize> Clone for Sender<'ch, M, T, N>\nwhere\n    M: RawMutex,\n{\n    fn clone(&self) -> Self {\n        *self\n    }\n}\n\nimpl<'ch, M, T, const N: usize> Copy for Sender<'ch, M, T, N> where M: RawMutex {}\n\nimpl<'ch, M, T, const N: usize> futures_sink::Sink<T> for Sender<'ch, M, T, N>\nwhere\n    M: RawMutex,\n{\n    type Error = TrySendError<T>;\n\n    fn poll_ready(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {\n        self.poll_ready_to_send(cx).map(|()| Ok(()))\n    }\n\n    fn start_send(self: Pin<&mut Self>, item: T) -> Result<(), Self::Error> {\n        self.try_send(item)\n    }\n\n    fn poll_flush(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {\n        Poll::Ready(Ok(()))\n    }\n\n    fn poll_close(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {\n        Poll::Ready(Ok(()))\n    }\n}\n\nimpl<'ch, M, T, const N: usize> Sender<'ch, M, T, N>\nwhere\n    M: RawMutex,\n{\n    /// Sends a value.\n    ///\n    /// See [`Channel::send()`]\n    pub fn send(&self, message: T) -> SendFuture<'ch, M, T, N> {\n        self.channel.send(message)\n    }\n\n    /// Attempt to immediately send a message.\n    ///\n    /// See [`Channel::send()`]\n    pub fn try_send(&self, message: T) -> Result<(), TrySendError<T>> {\n        self.channel.try_send(message)\n    }\n\n    /// Allows a poll_fn to poll until the channel is ready to send\n    ///\n    /// See [`Channel::poll_ready_to_send()`]\n    pub fn poll_ready_to_send(&self, cx: &mut Context<'_>) -> Poll<()> {\n        self.channel.poll_ready_to_send(cx)\n    }\n\n    /// Returns the maximum number of elements the channel can hold.\n    ///\n    /// See [`Channel::capacity()`]\n    pub const fn capacity(&self) -> usize {\n        self.channel.capacity()\n    }\n\n    /// Returns the free capacity of the channel.\n    ///\n    /// See [`Channel::free_capacity()`]\n    pub fn free_capacity(&self) -> usize {\n        self.channel.free_capacity()\n    }\n\n    /// Clears all elements in the channel.\n    ///\n    /// See [`Channel::clear()`]\n    pub fn clear(&self) {\n        self.channel.clear();\n    }\n\n    /// Returns the number of elements currently in the channel.\n    ///\n    /// See [`Channel::len()`]\n    pub fn len(&self) -> usize {\n        self.channel.len()\n    }\n\n    /// Returns whether the channel is empty.\n    ///\n    /// See [`Channel::is_empty()`]\n    pub fn is_empty(&self) -> bool {\n        self.channel.is_empty()\n    }\n\n    /// Returns whether the channel is full.\n    ///\n    /// See [`Channel::is_full()`]\n    pub fn is_full(&self) -> bool {\n        self.channel.is_full()\n    }\n}\n\n/// Send-only access to a [`Channel`] without knowing channel size.\npub struct DynamicSender<'ch, T> {\n    pub(crate) channel: &'ch dyn DynamicChannel<T>,\n}\n\nimpl<'ch, T> Clone for DynamicSender<'ch, T> {\n    fn clone(&self) -> Self {\n        *self\n    }\n}\n\nimpl<'ch, T> Copy for DynamicSender<'ch, T> {}\n\nimpl<'ch, M, T, const N: usize> From<Sender<'ch, M, T, N>> for DynamicSender<'ch, T>\nwhere\n    M: RawMutex,\n{\n    fn from(s: Sender<'ch, M, T, N>) -> Self {\n        Self { channel: s.channel }\n    }\n}\n\nimpl<'ch, T> DynamicSender<'ch, T> {\n    /// Sends a value.\n    ///\n    /// See [`Channel::send()`]\n    pub fn send(&self, message: T) -> DynamicSendFuture<'ch, T> {\n        DynamicSendFuture {\n            channel: self.channel,\n            message: Some(message),\n        }\n    }\n\n    /// Attempt to immediately send a message.\n    ///\n    /// See [`Channel::send()`]\n    pub fn try_send(&self, message: T) -> Result<(), TrySendError<T>> {\n        self.channel.try_send_with_context(message, None)\n    }\n\n    /// Allows a poll_fn to poll until the channel is ready to send\n    ///\n    /// See [`Channel::poll_ready_to_send()`]\n    pub fn poll_ready_to_send(&self, cx: &mut Context<'_>) -> Poll<()> {\n        self.channel.poll_ready_to_send(cx)\n    }\n}\n\n/// Send-only access to a [`Channel`] without knowing channel size.\n/// This version can be sent between threads but can only be created if the underlying mutex is Sync.\npub struct SendDynamicSender<'ch, T> {\n    pub(crate) channel: &'ch dyn DynamicChannel<T>,\n}\n\nimpl<'ch, T> Clone for SendDynamicSender<'ch, T> {\n    fn clone(&self) -> Self {\n        *self\n    }\n}\n\nimpl<'ch, T> Copy for SendDynamicSender<'ch, T> {}\nunsafe impl<'ch, T: Send> Send for SendDynamicSender<'ch, T> {}\nunsafe impl<'ch, T: Send> Sync for SendDynamicSender<'ch, T> {}\n\nimpl<'ch, M, T, const N: usize> From<Sender<'ch, M, T, N>> for SendDynamicSender<'ch, T>\nwhere\n    M: RawMutex + Sync + Send,\n{\n    fn from(s: Sender<'ch, M, T, N>) -> Self {\n        Self { channel: s.channel }\n    }\n}\n\nimpl<'ch, T> SendDynamicSender<'ch, T> {\n    /// Sends a value.\n    ///\n    /// See [`Channel::send()`]\n    pub fn send(&self, message: T) -> DynamicSendFuture<'ch, T> {\n        DynamicSendFuture {\n            channel: self.channel,\n            message: Some(message),\n        }\n    }\n\n    /// Attempt to immediately send a message.\n    ///\n    /// See [`Channel::send()`]\n    pub fn try_send(&self, message: T) -> Result<(), TrySendError<T>> {\n        self.channel.try_send_with_context(message, None)\n    }\n\n    /// Allows a poll_fn to poll until the channel is ready to send\n    ///\n    /// See [`Channel::poll_ready_to_send()`]\n    pub fn poll_ready_to_send(&self, cx: &mut Context<'_>) -> Poll<()> {\n        self.channel.poll_ready_to_send(cx)\n    }\n}\n\n/// Receive-only access to a [`Channel`].\n#[derive(Debug)]\npub struct Receiver<'ch, M, T, const N: usize>\nwhere\n    M: RawMutex,\n{\n    channel: &'ch Channel<M, T, N>,\n}\n\nimpl<'ch, M, T, const N: usize> Clone for Receiver<'ch, M, T, N>\nwhere\n    M: RawMutex,\n{\n    fn clone(&self) -> Self {\n        *self\n    }\n}\n\nimpl<'ch, M, T, const N: usize> Copy for Receiver<'ch, M, T, N> where M: RawMutex {}\n\nimpl<'ch, M, T, const N: usize> Receiver<'ch, M, T, N>\nwhere\n    M: RawMutex,\n{\n    /// Receive the next value.\n    ///\n    /// See [`Channel::receive()`].\n    pub fn receive(&self) -> ReceiveFuture<'_, M, T, N> {\n        self.channel.receive()\n    }\n\n    /// Is a value ready to be received in the channel\n    ///\n    /// See [`Channel::ready_to_receive()`].\n    pub fn ready_to_receive(&self) -> ReceiveReadyFuture<'_, M, T, N> {\n        self.channel.ready_to_receive()\n    }\n\n    /// Attempt to immediately receive the next value.\n    ///\n    /// See [`Channel::try_receive()`]\n    pub fn try_receive(&self) -> Result<T, TryReceiveError> {\n        self.channel.try_receive()\n    }\n\n    /// Peek at the next value without removing it from the queue.\n    ///\n    /// See [`Channel::try_peek()`]\n    pub fn try_peek(&self) -> Result<T, TryReceiveError>\n    where\n        T: Clone,\n    {\n        self.channel.try_peek()\n    }\n\n    /// Allows a poll_fn to poll until the channel is ready to receive\n    ///\n    /// See [`Channel::poll_ready_to_receive()`]\n    pub fn poll_ready_to_receive(&self, cx: &mut Context<'_>) -> Poll<()> {\n        self.channel.poll_ready_to_receive(cx)\n    }\n\n    /// Poll the channel for the next item\n    ///\n    /// See [`Channel::poll_receive()`]\n    pub fn poll_receive(&self, cx: &mut Context<'_>) -> Poll<T> {\n        self.channel.poll_receive(cx)\n    }\n\n    /// Returns the maximum number of elements the channel can hold.\n    ///\n    /// See [`Channel::capacity()`]\n    pub const fn capacity(&self) -> usize {\n        self.channel.capacity()\n    }\n\n    /// Returns the free capacity of the channel.\n    ///\n    /// See [`Channel::free_capacity()`]\n    pub fn free_capacity(&self) -> usize {\n        self.channel.free_capacity()\n    }\n\n    /// Clears all elements in the channel.\n    ///\n    /// See [`Channel::clear()`]\n    pub fn clear(&self) {\n        self.channel.clear();\n    }\n\n    /// Returns the number of elements currently in the channel.\n    ///\n    /// See [`Channel::len()`]\n    pub fn len(&self) -> usize {\n        self.channel.len()\n    }\n\n    /// Returns whether the channel is empty.\n    ///\n    /// See [`Channel::is_empty()`]\n    pub fn is_empty(&self) -> bool {\n        self.channel.is_empty()\n    }\n\n    /// Returns whether the channel is full.\n    ///\n    /// See [`Channel::is_full()`]\n    pub fn is_full(&self) -> bool {\n        self.channel.is_full()\n    }\n}\n\n/// Receive-only access to a [`Channel`] without knowing channel size.\npub struct DynamicReceiver<'ch, T> {\n    pub(crate) channel: &'ch dyn DynamicChannel<T>,\n}\n\nimpl<'ch, T> Clone for DynamicReceiver<'ch, T> {\n    fn clone(&self) -> Self {\n        *self\n    }\n}\n\nimpl<'ch, T> Copy for DynamicReceiver<'ch, T> {}\n\nimpl<'ch, T> DynamicReceiver<'ch, T> {\n    /// Receive the next value.\n    ///\n    /// See [`Channel::receive()`].\n    pub fn receive(&self) -> DynamicReceiveFuture<'_, T> {\n        DynamicReceiveFuture { channel: self.channel }\n    }\n\n    /// Attempt to immediately receive the next value.\n    ///\n    /// See [`Channel::try_receive()`]\n    pub fn try_receive(&self) -> Result<T, TryReceiveError> {\n        self.channel.try_receive_with_context(None)\n    }\n\n    /// Peek at the next value without removing it from the queue.\n    ///\n    /// See [`Channel::try_peek()`]\n    pub fn try_peek(&self) -> Result<T, TryReceiveError>\n    where\n        T: Clone,\n    {\n        self.channel.try_peek_with_context(None)\n    }\n\n    /// Allows a poll_fn to poll until the channel is ready to receive\n    ///\n    /// See [`Channel::poll_ready_to_receive()`]\n    pub fn poll_ready_to_receive(&self, cx: &mut Context<'_>) -> Poll<()> {\n        self.channel.poll_ready_to_receive(cx)\n    }\n\n    /// Poll the channel for the next item\n    ///\n    /// See [`Channel::poll_receive()`]\n    pub fn poll_receive(&self, cx: &mut Context<'_>) -> Poll<T> {\n        self.channel.poll_receive(cx)\n    }\n}\n\nimpl<'ch, M, T, const N: usize> From<Receiver<'ch, M, T, N>> for DynamicReceiver<'ch, T>\nwhere\n    M: RawMutex,\n{\n    fn from(s: Receiver<'ch, M, T, N>) -> Self {\n        Self { channel: s.channel }\n    }\n}\n\n/// Receive-only access to a [`Channel`] without knowing channel size.\n/// This version can be sent between threads but can only be created if the underlying mutex is Sync.\npub struct SendDynamicReceiver<'ch, T> {\n    pub(crate) channel: &'ch dyn DynamicChannel<T>,\n}\n\n/// Receive-only access to a [`Channel`] without knowing channel size.\n/// This version can be sent between threads but can only be created if the underlying mutex is Sync.\n#[deprecated(since = \"0.7.1\", note = \"please use `SendDynamicReceiver` instead\")]\npub type SendableDynamicReceiver<'ch, T> = SendDynamicReceiver<'ch, T>;\n\nimpl<'ch, T> Clone for SendDynamicReceiver<'ch, T> {\n    fn clone(&self) -> Self {\n        *self\n    }\n}\n\nimpl<'ch, T> Copy for SendDynamicReceiver<'ch, T> {}\nunsafe impl<'ch, T: Send> Send for SendDynamicReceiver<'ch, T> {}\nunsafe impl<'ch, T: Send> Sync for SendDynamicReceiver<'ch, T> {}\n\nimpl<'ch, T> SendDynamicReceiver<'ch, T> {\n    /// Receive the next value.\n    ///\n    /// See [`Channel::receive()`].\n    pub fn receive(&self) -> DynamicReceiveFuture<'_, T> {\n        DynamicReceiveFuture { channel: self.channel }\n    }\n\n    /// Attempt to immediately receive the next value.\n    ///\n    /// See [`Channel::try_receive()`]\n    pub fn try_receive(&self) -> Result<T, TryReceiveError> {\n        self.channel.try_receive_with_context(None)\n    }\n\n    /// Allows a poll_fn to poll until the channel is ready to receive\n    ///\n    /// See [`Channel::poll_ready_to_receive()`]\n    pub fn poll_ready_to_receive(&self, cx: &mut Context<'_>) -> Poll<()> {\n        self.channel.poll_ready_to_receive(cx)\n    }\n\n    /// Poll the channel for the next item\n    ///\n    /// See [`Channel::poll_receive()`]\n    pub fn poll_receive(&self, cx: &mut Context<'_>) -> Poll<T> {\n        self.channel.poll_receive(cx)\n    }\n}\n\nimpl<'ch, M, T, const N: usize> From<Receiver<'ch, M, T, N>> for SendDynamicReceiver<'ch, T>\nwhere\n    M: RawMutex + Sync + Send,\n{\n    fn from(s: Receiver<'ch, M, T, N>) -> Self {\n        Self { channel: s.channel }\n    }\n}\n\nimpl<'ch, M, T, const N: usize> futures_core::Stream for Receiver<'ch, M, T, N>\nwhere\n    M: RawMutex,\n{\n    type Item = T;\n\n    fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {\n        self.channel.poll_receive(cx).map(Some)\n    }\n}\n\n/// Future returned by [`Channel::receive`] and  [`Receiver::receive`].\n#[must_use = \"futures do nothing unless you `.await` or poll them\"]\n#[derive(Debug)]\npub struct ReceiveFuture<'ch, M, T, const N: usize>\nwhere\n    M: RawMutex,\n{\n    channel: &'ch Channel<M, T, N>,\n}\n\nimpl<'ch, M, T, const N: usize> Future for ReceiveFuture<'ch, M, T, N>\nwhere\n    M: RawMutex,\n{\n    type Output = T;\n\n    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<T> {\n        self.channel.poll_receive(cx)\n    }\n}\n\n/// Future returned by [`Channel::ready_to_receive`] and  [`Receiver::ready_to_receive`].\n#[must_use = \"futures do nothing unless you `.await` or poll them\"]\n#[derive(Debug)]\npub struct ReceiveReadyFuture<'ch, M, T, const N: usize>\nwhere\n    M: RawMutex,\n{\n    channel: &'ch Channel<M, T, N>,\n}\n\nimpl<'ch, M, T, const N: usize> Future for ReceiveReadyFuture<'ch, M, T, N>\nwhere\n    M: RawMutex,\n{\n    type Output = ();\n\n    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<()> {\n        self.channel.poll_ready_to_receive(cx)\n    }\n}\n\n/// Future returned by [`DynamicReceiver::receive`].\n#[must_use = \"futures do nothing unless you `.await` or poll them\"]\npub struct DynamicReceiveFuture<'ch, T> {\n    channel: &'ch dyn DynamicChannel<T>,\n}\n\nimpl<'ch, T> Future for DynamicReceiveFuture<'ch, T> {\n    type Output = T;\n\n    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<T> {\n        match self.channel.try_receive_with_context(Some(cx)) {\n            Ok(v) => Poll::Ready(v),\n            Err(TryReceiveError::Empty) => Poll::Pending,\n        }\n    }\n}\n\nimpl<'ch, M: RawMutex, T, const N: usize> From<ReceiveFuture<'ch, M, T, N>> for DynamicReceiveFuture<'ch, T> {\n    fn from(value: ReceiveFuture<'ch, M, T, N>) -> Self {\n        Self { channel: value.channel }\n    }\n}\n\n/// Future returned by [`Channel::send`] and  [`Sender::send`].\n#[must_use = \"futures do nothing unless you `.await` or poll them\"]\n#[derive(Debug)]\npub struct SendFuture<'ch, M, T, const N: usize>\nwhere\n    M: RawMutex,\n{\n    channel: &'ch Channel<M, T, N>,\n    message: Option<T>,\n}\n\nimpl<'ch, M, T, const N: usize> Future for SendFuture<'ch, M, T, N>\nwhere\n    M: RawMutex,\n{\n    type Output = ();\n\n    fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {\n        match self.message.take() {\n            Some(m) => match self.channel.try_send_with_context(m, Some(cx)) {\n                Ok(..) => Poll::Ready(()),\n                Err(TrySendError::Full(m)) => {\n                    self.message = Some(m);\n                    Poll::Pending\n                }\n            },\n            None => panic!(\"Message cannot be None\"),\n        }\n    }\n}\n\nimpl<'ch, M, T, const N: usize> Unpin for SendFuture<'ch, M, T, N> where M: RawMutex {}\n\n/// Future returned by [`DynamicSender::send`].\n#[must_use = \"futures do nothing unless you `.await` or poll them\"]\npub struct DynamicSendFuture<'ch, T> {\n    channel: &'ch dyn DynamicChannel<T>,\n    message: Option<T>,\n}\n\nimpl<'ch, T> Future for DynamicSendFuture<'ch, T> {\n    type Output = ();\n\n    fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {\n        match self.message.take() {\n            Some(m) => match self.channel.try_send_with_context(m, Some(cx)) {\n                Ok(..) => Poll::Ready(()),\n                Err(TrySendError::Full(m)) => {\n                    self.message = Some(m);\n                    Poll::Pending\n                }\n            },\n            None => panic!(\"Message cannot be None\"),\n        }\n    }\n}\n\nimpl<'ch, T> Unpin for DynamicSendFuture<'ch, T> {}\n\nimpl<'ch, M: RawMutex, T, const N: usize> From<SendFuture<'ch, M, T, N>> for DynamicSendFuture<'ch, T> {\n    fn from(value: SendFuture<'ch, M, T, N>) -> Self {\n        Self {\n            channel: value.channel,\n            message: value.message,\n        }\n    }\n}\n\npub(crate) trait DynamicChannel<T> {\n    fn try_send_with_context(&self, message: T, cx: Option<&mut Context<'_>>) -> Result<(), TrySendError<T>>;\n\n    fn try_receive_with_context(&self, cx: Option<&mut Context<'_>>) -> Result<T, TryReceiveError>;\n\n    fn try_peek_with_context(&self, cx: Option<&mut Context<'_>>) -> Result<T, TryReceiveError>\n    where\n        T: Clone;\n\n    fn poll_ready_to_send(&self, cx: &mut Context<'_>) -> Poll<()>;\n    fn poll_ready_to_receive(&self, cx: &mut Context<'_>) -> Poll<()>;\n\n    fn poll_receive(&self, cx: &mut Context<'_>) -> Poll<T>;\n}\n\n/// Error returned by [`try_receive`](Channel::try_receive).\n#[derive(PartialEq, Eq, Clone, Copy, Debug)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub enum TryReceiveError {\n    /// A message could not be received because the channel is empty.\n    Empty,\n}\n\n/// Error returned by [`try_send`](Channel::try_send).\n#[derive(PartialEq, Eq, Clone, Copy, Debug)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub enum TrySendError<T> {\n    /// The data could not be sent on the channel because the channel is\n    /// currently full and sending would require blocking.\n    Full(T),\n}\n\n#[derive(Debug)]\nstruct ChannelState<T, const N: usize> {\n    queue: Deque<T, N>,\n    receiver_waker: WakerRegistration,\n    senders_waker: WakerRegistration,\n}\n\nimpl<T, const N: usize> ChannelState<T, N> {\n    const fn new() -> Self {\n        ChannelState {\n            queue: Deque::new(),\n            receiver_waker: WakerRegistration::new(),\n            senders_waker: WakerRegistration::new(),\n        }\n    }\n\n    fn try_receive(&mut self) -> Result<T, TryReceiveError> {\n        self.try_receive_with_context(None)\n    }\n\n    fn try_peek(&mut self) -> Result<T, TryReceiveError>\n    where\n        T: Clone,\n    {\n        self.try_peek_with_context(None)\n    }\n\n    fn try_peek_with_context(&mut self, cx: Option<&mut Context<'_>>) -> Result<T, TryReceiveError>\n    where\n        T: Clone,\n    {\n        if self.queue.is_full() {\n            self.senders_waker.wake();\n        }\n\n        if let Some(message) = self.queue.front() {\n            Ok(message.clone())\n        } else {\n            if let Some(cx) = cx {\n                self.receiver_waker.register(cx.waker());\n            }\n            Err(TryReceiveError::Empty)\n        }\n    }\n\n    fn try_receive_with_context(&mut self, cx: Option<&mut Context<'_>>) -> Result<T, TryReceiveError> {\n        if self.queue.is_full() {\n            self.senders_waker.wake();\n        }\n\n        if let Some(message) = self.queue.pop_front() {\n            Ok(message)\n        } else {\n            if let Some(cx) = cx {\n                self.receiver_waker.register(cx.waker());\n            }\n            Err(TryReceiveError::Empty)\n        }\n    }\n\n    fn poll_receive(&mut self, cx: &mut Context<'_>) -> Poll<T> {\n        if self.queue.is_full() {\n            self.senders_waker.wake();\n        }\n\n        if let Some(message) = self.queue.pop_front() {\n            Poll::Ready(message)\n        } else {\n            self.receiver_waker.register(cx.waker());\n            Poll::Pending\n        }\n    }\n\n    fn poll_ready_to_receive(&mut self, cx: &mut Context<'_>) -> Poll<()> {\n        self.receiver_waker.register(cx.waker());\n\n        if !self.queue.is_empty() {\n            Poll::Ready(())\n        } else {\n            Poll::Pending\n        }\n    }\n\n    fn try_send(&mut self, message: T) -> Result<(), TrySendError<T>> {\n        self.try_send_with_context(message, None)\n    }\n\n    fn try_send_with_context(&mut self, message: T, cx: Option<&mut Context<'_>>) -> Result<(), TrySendError<T>> {\n        match self.queue.push_back(message) {\n            Ok(()) => {\n                self.receiver_waker.wake();\n                Ok(())\n            }\n            Err(message) => {\n                if let Some(cx) = cx {\n                    self.senders_waker.register(cx.waker());\n                }\n                Err(TrySendError::Full(message))\n            }\n        }\n    }\n\n    fn poll_ready_to_send(&mut self, cx: &mut Context<'_>) -> Poll<()> {\n        self.senders_waker.register(cx.waker());\n\n        if !self.queue.is_full() {\n            Poll::Ready(())\n        } else {\n            Poll::Pending\n        }\n    }\n\n    fn clear(&mut self) {\n        if self.queue.is_full() {\n            self.senders_waker.wake();\n        }\n        self.queue.clear();\n    }\n\n    fn len(&self) -> usize {\n        self.queue.len()\n    }\n\n    fn is_empty(&self) -> bool {\n        self.queue.is_empty()\n    }\n\n    fn is_full(&self) -> bool {\n        self.queue.is_full()\n    }\n}\n\n/// A bounded channel for communicating between asynchronous tasks\n/// with backpressure.\n///\n/// The channel will buffer up to the provided number of messages.  Once the\n/// buffer is full, attempts to `send` new messages will wait until a message is\n/// received from the channel.\n///\n/// All data sent will become available in the same order as it was sent.\n#[derive(Debug)]\npub struct Channel<M, T, const N: usize>\nwhere\n    M: RawMutex,\n{\n    inner: Mutex<M, RefCell<ChannelState<T, N>>>,\n}\n\nimpl<M, T, const N: usize> Channel<M, T, N>\nwhere\n    M: RawMutex,\n{\n    /// Establish a new bounded channel. For example, to create one with a NoopMutex:\n    ///\n    /// ```\n    /// use embassy_sync::channel::Channel;\n    /// use embassy_sync::blocking_mutex::raw::NoopRawMutex;\n    ///\n    /// // Declare a bounded channel of 3 u32s.\n    /// let mut channel = Channel::<NoopRawMutex, u32, 3>::new();\n    /// ```\n    pub const fn new() -> Self {\n        Self {\n            inner: Mutex::new(RefCell::new(ChannelState::new())),\n        }\n    }\n\n    fn lock<R>(&self, f: impl FnOnce(&mut ChannelState<T, N>) -> R) -> R {\n        self.inner.lock(|rc| f(&mut *unwrap!(rc.try_borrow_mut())))\n    }\n\n    fn try_receive_with_context(&self, cx: Option<&mut Context<'_>>) -> Result<T, TryReceiveError> {\n        self.lock(|c| c.try_receive_with_context(cx))\n    }\n\n    fn try_peek_with_context(&self, cx: Option<&mut Context<'_>>) -> Result<T, TryReceiveError>\n    where\n        T: Clone,\n    {\n        self.lock(|c| c.try_peek_with_context(cx))\n    }\n\n    /// Poll the channel for the next message\n    pub fn poll_receive(&self, cx: &mut Context<'_>) -> Poll<T> {\n        self.lock(|c| c.poll_receive(cx))\n    }\n\n    fn try_send_with_context(&self, m: T, cx: Option<&mut Context<'_>>) -> Result<(), TrySendError<T>> {\n        self.lock(|c| c.try_send_with_context(m, cx))\n    }\n\n    /// Allows a poll_fn to poll until the channel is ready to receive\n    pub fn poll_ready_to_receive(&self, cx: &mut Context<'_>) -> Poll<()> {\n        self.lock(|c| c.poll_ready_to_receive(cx))\n    }\n\n    /// Allows a poll_fn to poll until the channel is ready to send\n    pub fn poll_ready_to_send(&self, cx: &mut Context<'_>) -> Poll<()> {\n        self.lock(|c| c.poll_ready_to_send(cx))\n    }\n\n    /// Get a sender for this channel.\n    pub fn sender(&self) -> Sender<'_, M, T, N> {\n        Sender { channel: self }\n    }\n\n    /// Get a receiver for this channel.\n    pub fn receiver(&self) -> Receiver<'_, M, T, N> {\n        Receiver { channel: self }\n    }\n\n    /// Get a sender for this channel using dynamic dispatch.\n    pub fn dyn_sender(&self) -> DynamicSender<'_, T> {\n        DynamicSender { channel: self }\n    }\n\n    /// Get a receiver for this channel using dynamic dispatch.\n    pub fn dyn_receiver(&self) -> DynamicReceiver<'_, T> {\n        DynamicReceiver { channel: self }\n    }\n\n    /// Send a value, waiting until there is capacity.\n    ///\n    /// Sending completes when the value has been pushed to the channel's queue.\n    /// This doesn't mean the value has been received yet.\n    pub fn send(&self, message: T) -> SendFuture<'_, M, T, N> {\n        SendFuture {\n            channel: self,\n            message: Some(message),\n        }\n    }\n\n    /// Attempt to immediately send a message.\n    ///\n    /// This method differs from [`send`](Channel::send) by returning immediately if the channel's\n    /// buffer is full, instead of waiting.\n    ///\n    /// # Errors\n    ///\n    /// If the channel capacity has been reached, i.e., the channel has `n`\n    /// buffered values where `n` is the argument passed to [`Channel`], then an\n    /// error is returned.\n    pub fn try_send(&self, message: T) -> Result<(), TrySendError<T>> {\n        self.lock(|c| c.try_send(message))\n    }\n\n    /// Receive the next value.\n    ///\n    /// If there are no messages in the channel's buffer, this method will\n    /// wait until a message is sent.\n    pub fn receive(&self) -> ReceiveFuture<'_, M, T, N> {\n        ReceiveFuture { channel: self }\n    }\n\n    /// Is a value ready to be received in the channel\n    ///\n    /// If there are no messages in the channel's buffer, this method will\n    /// wait until there is at least one\n    pub fn ready_to_receive(&self) -> ReceiveReadyFuture<'_, M, T, N> {\n        ReceiveReadyFuture { channel: self }\n    }\n\n    /// Attempt to immediately receive a message.\n    ///\n    /// This method will either receive a message from the channel immediately or return an error\n    /// if the channel is empty.\n    pub fn try_receive(&self) -> Result<T, TryReceiveError> {\n        self.lock(|c| c.try_receive())\n    }\n\n    /// Peek at the next value without removing it from the queue.\n    ///\n    /// This method will either receive a copy of the message from the channel immediately or return\n    /// an error if the channel is empty.\n    pub fn try_peek(&self) -> Result<T, TryReceiveError>\n    where\n        T: Clone,\n    {\n        self.lock(|c| c.try_peek())\n    }\n\n    /// Returns the maximum number of elements the channel can hold.\n    pub const fn capacity(&self) -> usize {\n        N\n    }\n\n    /// Returns the free capacity of the channel.\n    ///\n    /// This is equivalent to `capacity() - len()`\n    pub fn free_capacity(&self) -> usize {\n        N - self.len()\n    }\n\n    /// Clears all elements in the channel.\n    pub fn clear(&self) {\n        self.lock(|c| c.clear());\n    }\n\n    /// Returns the number of elements currently in the channel.\n    pub fn len(&self) -> usize {\n        self.lock(|c| c.len())\n    }\n\n    /// Returns whether the channel is empty.\n    pub fn is_empty(&self) -> bool {\n        self.lock(|c| c.is_empty())\n    }\n\n    /// Returns whether the channel is full.\n    pub fn is_full(&self) -> bool {\n        self.lock(|c| c.is_full())\n    }\n}\n\n/// Implements the DynamicChannel to allow creating types that are unaware of the queue size with the\n/// tradeoff cost of dynamic dispatch.\nimpl<M, T, const N: usize> DynamicChannel<T> for Channel<M, T, N>\nwhere\n    M: RawMutex,\n{\n    fn try_send_with_context(&self, m: T, cx: Option<&mut Context<'_>>) -> Result<(), TrySendError<T>> {\n        Channel::try_send_with_context(self, m, cx)\n    }\n\n    fn try_receive_with_context(&self, cx: Option<&mut Context<'_>>) -> Result<T, TryReceiveError> {\n        Channel::try_receive_with_context(self, cx)\n    }\n\n    fn try_peek_with_context(&self, cx: Option<&mut Context<'_>>) -> Result<T, TryReceiveError>\n    where\n        T: Clone,\n    {\n        Channel::try_peek_with_context(self, cx)\n    }\n\n    fn poll_ready_to_send(&self, cx: &mut Context<'_>) -> Poll<()> {\n        Channel::poll_ready_to_send(self, cx)\n    }\n\n    fn poll_ready_to_receive(&self, cx: &mut Context<'_>) -> Poll<()> {\n        Channel::poll_ready_to_receive(self, cx)\n    }\n\n    fn poll_receive(&self, cx: &mut Context<'_>) -> Poll<T> {\n        Channel::poll_receive(self, cx)\n    }\n}\n\nimpl<M, T, const N: usize> futures_core::Stream for Channel<M, T, N>\nwhere\n    M: RawMutex,\n{\n    type Item = T;\n\n    fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {\n        self.poll_receive(cx).map(Some)\n    }\n}\n\nimpl<M, T, const N: usize> futures_sink::Sink<T> for Channel<M, T, N>\nwhere\n    M: RawMutex,\n{\n    type Error = TrySendError<T>;\n\n    fn poll_ready(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {\n        self.poll_ready_to_send(cx).map(|()| Ok(()))\n    }\n\n    fn start_send(self: Pin<&mut Self>, item: T) -> Result<(), Self::Error> {\n        self.try_send(item)\n    }\n\n    fn poll_flush(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {\n        Poll::Ready(Ok(()))\n    }\n\n    fn poll_close(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {\n        Poll::Ready(Ok(()))\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use core::time::Duration;\n\n    use futures_executor::ThreadPool;\n    use futures_timer::Delay;\n    use futures_util::task::SpawnExt;\n    use static_cell::StaticCell;\n\n    use super::*;\n    use crate::blocking_mutex::raw::{CriticalSectionRawMutex, NoopRawMutex};\n\n    fn capacity<T, const N: usize>(c: &ChannelState<T, N>) -> usize {\n        c.queue.capacity() - c.queue.len()\n    }\n\n    #[test]\n    fn sending_once() {\n        let mut c = ChannelState::<u32, 3>::new();\n        assert!(c.try_send(1).is_ok());\n        assert_eq!(capacity(&c), 2);\n    }\n\n    #[test]\n    fn sending_when_full() {\n        let mut c = ChannelState::<u32, 3>::new();\n        let _ = c.try_send(1);\n        let _ = c.try_send(1);\n        let _ = c.try_send(1);\n        match c.try_send(2) {\n            Err(TrySendError::Full(2)) => assert!(true),\n            _ => assert!(false),\n        }\n        assert_eq!(capacity(&c), 0);\n    }\n\n    #[test]\n    fn receiving_once_with_one_send() {\n        let mut c = ChannelState::<u32, 3>::new();\n        assert!(c.try_send(1).is_ok());\n        assert_eq!(c.try_receive().unwrap(), 1);\n        assert_eq!(capacity(&c), 3);\n    }\n\n    #[test]\n    fn receiving_when_empty() {\n        let mut c = ChannelState::<u32, 3>::new();\n        match c.try_receive() {\n            Err(TryReceiveError::Empty) => assert!(true),\n            _ => assert!(false),\n        }\n        assert_eq!(capacity(&c), 3);\n    }\n\n    #[test]\n    fn simple_send_and_receive() {\n        let c = Channel::<NoopRawMutex, u32, 3>::new();\n        assert!(c.try_send(1).is_ok());\n        assert_eq!(c.try_peek().unwrap(), 1);\n        assert_eq!(c.try_peek().unwrap(), 1);\n        assert_eq!(c.try_receive().unwrap(), 1);\n    }\n\n    #[test]\n    fn cloning() {\n        let c = Channel::<NoopRawMutex, u32, 3>::new();\n        let r1 = c.receiver();\n        let s1 = c.sender();\n\n        let _ = r1.clone();\n        let _ = s1.clone();\n    }\n\n    #[test]\n    fn dynamic_dispatch_into() {\n        let c = Channel::<NoopRawMutex, u32, 3>::new();\n        let s: DynamicSender<'_, u32> = c.sender().into();\n        let r: DynamicReceiver<'_, u32> = c.receiver().into();\n\n        assert!(s.try_send(1).is_ok());\n        assert_eq!(r.try_receive().unwrap(), 1);\n    }\n\n    #[test]\n    fn dynamic_dispatch_constructor() {\n        let c = Channel::<NoopRawMutex, u32, 3>::new();\n        let s = c.dyn_sender();\n        let r = c.dyn_receiver();\n\n        assert!(s.try_send(1).is_ok());\n        assert_eq!(r.try_peek().unwrap(), 1);\n        assert_eq!(r.try_peek().unwrap(), 1);\n        assert_eq!(r.try_receive().unwrap(), 1);\n    }\n\n    #[futures_test::test]\n    async fn receiver_receives_given_try_send_async() {\n        let executor = ThreadPool::new().unwrap();\n\n        static CHANNEL: StaticCell<Channel<CriticalSectionRawMutex, u32, 3>> = StaticCell::new();\n        let c = &*CHANNEL.init(Channel::new());\n        let c2 = c;\n        assert!(\n            executor\n                .spawn(async move {\n                    assert!(c2.try_send(1).is_ok());\n                })\n                .is_ok()\n        );\n        assert_eq!(c.receive().await, 1);\n    }\n\n    #[futures_test::test]\n    async fn sender_send_completes_if_capacity() {\n        let c = Channel::<CriticalSectionRawMutex, u32, 1>::new();\n        c.send(1).await;\n        assert_eq!(c.receive().await, 1);\n    }\n\n    #[futures_test::test]\n    async fn senders_sends_wait_until_capacity() {\n        let executor = ThreadPool::new().unwrap();\n\n        static CHANNEL: StaticCell<Channel<CriticalSectionRawMutex, u32, 1>> = StaticCell::new();\n        let c = &*CHANNEL.init(Channel::new());\n        assert!(c.try_send(1).is_ok());\n\n        let c2 = c;\n        let send_task_1 = executor.spawn_with_handle(async move { c2.send(2).await });\n        let c2 = c;\n        let send_task_2 = executor.spawn_with_handle(async move { c2.send(3).await });\n        // Wish I could think of a means of determining that the async send is waiting instead.\n        // However, I've used the debugger to observe that the send does indeed wait.\n        Delay::new(Duration::from_millis(500)).await;\n        assert_eq!(c.receive().await, 1);\n        assert!(\n            executor\n                .spawn(async move {\n                    loop {\n                        c.receive().await;\n                    }\n                })\n                .is_ok()\n        );\n        send_task_1.unwrap().await;\n        send_task_2.unwrap().await;\n    }\n}\n"
  },
  {
    "path": "embassy-sync/src/fmt.rs",
    "content": "#![macro_use]\n#![allow(unused)]\n\nuse core::fmt::{Debug, Display, LowerHex};\n\n#[cfg(all(feature = \"defmt\", feature = \"log\"))]\ncompile_error!(\"You may not enable both `defmt` and `log` features.\");\n\n#[collapse_debuginfo(yes)]\nmacro_rules! assert {\n    ($($x:tt)*) => {\n        {\n            #[cfg(not(feature = \"defmt\"))]\n            ::core::assert!($($x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::assert!($($x)*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! assert_eq {\n    ($($x:tt)*) => {\n        {\n            #[cfg(not(feature = \"defmt\"))]\n            ::core::assert_eq!($($x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::assert_eq!($($x)*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! assert_ne {\n    ($($x:tt)*) => {\n        {\n            #[cfg(not(feature = \"defmt\"))]\n            ::core::assert_ne!($($x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::assert_ne!($($x)*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! debug_assert {\n    ($($x:tt)*) => {\n        {\n            #[cfg(not(feature = \"defmt\"))]\n            ::core::debug_assert!($($x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::debug_assert!($($x)*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! debug_assert_eq {\n    ($($x:tt)*) => {\n        {\n            #[cfg(not(feature = \"defmt\"))]\n            ::core::debug_assert_eq!($($x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::debug_assert_eq!($($x)*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! debug_assert_ne {\n    ($($x:tt)*) => {\n        {\n            #[cfg(not(feature = \"defmt\"))]\n            ::core::debug_assert_ne!($($x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::debug_assert_ne!($($x)*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! todo {\n    ($($x:tt)*) => {\n        {\n            #[cfg(not(feature = \"defmt\"))]\n            ::core::todo!($($x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::todo!($($x)*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! unreachable {\n    ($($x:tt)*) => {\n        {\n            #[cfg(not(feature = \"defmt\"))]\n            ::core::unreachable!($($x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::unreachable!($($x)*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! panic {\n    ($($x:tt)*) => {\n        {\n            #[cfg(not(feature = \"defmt\"))]\n            ::core::panic!($($x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::panic!($($x)*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! trace {\n    ($s:literal $(, $x:expr)* $(,)?) => {\n        {\n            #[cfg(feature = \"log\")]\n            ::log::trace!($s $(, $x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::trace!($s $(, $x)*);\n            #[cfg(not(any(feature = \"log\", feature=\"defmt\")))]\n            let _ = ($( & $x ),*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! debug {\n    ($s:literal $(, $x:expr)* $(,)?) => {\n        {\n            #[cfg(feature = \"log\")]\n            ::log::debug!($s $(, $x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::debug!($s $(, $x)*);\n            #[cfg(not(any(feature = \"log\", feature=\"defmt\")))]\n            let _ = ($( & $x ),*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! info {\n    ($s:literal $(, $x:expr)* $(,)?) => {\n        {\n            #[cfg(feature = \"log\")]\n            ::log::info!($s $(, $x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::info!($s $(, $x)*);\n            #[cfg(not(any(feature = \"log\", feature=\"defmt\")))]\n            let _ = ($( & $x ),*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! warn {\n    ($s:literal $(, $x:expr)* $(,)?) => {\n        {\n            #[cfg(feature = \"log\")]\n            ::log::warn!($s $(, $x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::warn!($s $(, $x)*);\n            #[cfg(not(any(feature = \"log\", feature=\"defmt\")))]\n            let _ = ($( & $x ),*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! error {\n    ($s:literal $(, $x:expr)* $(,)?) => {\n        {\n            #[cfg(feature = \"log\")]\n            ::log::error!($s $(, $x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::error!($s $(, $x)*);\n            #[cfg(not(any(feature = \"log\", feature=\"defmt\")))]\n            let _ = ($( & $x ),*);\n        }\n    };\n}\n\n#[cfg(feature = \"defmt\")]\n#[collapse_debuginfo(yes)]\nmacro_rules! unwrap {\n    ($($x:tt)*) => {\n        ::defmt::unwrap!($($x)*)\n    };\n}\n\n#[cfg(not(feature = \"defmt\"))]\n#[collapse_debuginfo(yes)]\nmacro_rules! unwrap {\n    ($arg:expr) => {\n        match $crate::fmt::Try::into_result($arg) {\n            ::core::result::Result::Ok(t) => t,\n            ::core::result::Result::Err(e) => {\n                ::core::panic!(\"unwrap of `{}` failed: {:?}\", ::core::stringify!($arg), e);\n            }\n        }\n    };\n    ($arg:expr, $($msg:expr),+ $(,)? ) => {\n        match $crate::fmt::Try::into_result($arg) {\n            ::core::result::Result::Ok(t) => t,\n            ::core::result::Result::Err(e) => {\n                ::core::panic!(\"unwrap of `{}` failed: {}: {:?}\", ::core::stringify!($arg), ::core::format_args!($($msg,)*), e);\n            }\n        }\n    }\n}\n\n#[derive(Debug, Copy, Clone, Eq, PartialEq)]\npub struct NoneError;\n\npub trait Try {\n    type Ok;\n    type Error;\n    fn into_result(self) -> Result<Self::Ok, Self::Error>;\n}\n\nimpl<T> Try for Option<T> {\n    type Ok = T;\n    type Error = NoneError;\n\n    #[inline]\n    fn into_result(self) -> Result<T, NoneError> {\n        self.ok_or(NoneError)\n    }\n}\n\nimpl<T, E> Try for Result<T, E> {\n    type Ok = T;\n    type Error = E;\n\n    #[inline]\n    fn into_result(self) -> Self {\n        self\n    }\n}\n\npub(crate) struct Bytes<'a>(pub &'a [u8]);\n\nimpl<'a> Debug for Bytes<'a> {\n    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {\n        write!(f, \"{:#02x?}\", self.0)\n    }\n}\n\nimpl<'a> Display for Bytes<'a> {\n    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {\n        write!(f, \"{:#02x?}\", self.0)\n    }\n}\n\nimpl<'a> LowerHex for Bytes<'a> {\n    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {\n        write!(f, \"{:#02x?}\", self.0)\n    }\n}\n\n#[cfg(feature = \"defmt\")]\nimpl<'a> defmt::Format for Bytes<'a> {\n    fn format(&self, fmt: defmt::Formatter) {\n        defmt::write!(fmt, \"{:02x}\", self.0)\n    }\n}\n"
  },
  {
    "path": "embassy-sync/src/lazy_lock.rs",
    "content": "//! Synchronization primitive for initializing a value once, allowing others to get a reference to the value.\n\nuse core::cell::UnsafeCell;\nuse core::mem::ManuallyDrop;\nuse core::sync::atomic::{AtomicBool, Ordering};\n\n/// The `LazyLock` is a synchronization primitive that allows for\n/// initializing a value once, and allowing others to obtain a\n/// reference to the value. This is useful for lazy initialization of\n/// a static value.\n///\n/// # Example\n/// ```\n/// use futures_executor::block_on;\n/// use embassy_sync::lazy_lock::LazyLock;\n///\n/// // Define a static value that will be lazily initialized\n/// // at runtime at the first access.\n/// static VALUE: LazyLock<u32> = LazyLock::new(|| 20);\n///\n/// let reference = VALUE.get();\n/// assert_eq!(reference, &20);\n/// ```\n#[derive(Debug)]\npub struct LazyLock<T, F = fn() -> T> {\n    init: AtomicBool,\n    data: UnsafeCell<Data<T, F>>,\n}\n\nunion Data<T, F> {\n    value: ManuallyDrop<T>,\n    f: ManuallyDrop<F>,\n}\n\nunsafe impl<T, F> Sync for LazyLock<T, F>\nwhere\n    T: Sync,\n    F: Sync,\n{\n}\n\nimpl<T, F: FnOnce() -> T> LazyLock<T, F> {\n    /// Create a new uninitialized `StaticLock`.\n    pub const fn new(init_fn: F) -> Self {\n        Self {\n            init: AtomicBool::new(false),\n            data: UnsafeCell::new(Data {\n                f: ManuallyDrop::new(init_fn),\n            }),\n        }\n    }\n\n    /// Get a reference to the underlying value, initializing it if it\n    /// has not been done already.\n    #[inline]\n    pub fn get(&self) -> &T {\n        self.ensure_init_fast();\n        unsafe { &(*self.data.get()).value }\n    }\n\n    /// Get a mutable reference to the underlying value, initializing it if it\n    /// has not been done already.\n    #[inline]\n    pub fn get_mut(&mut self) -> &mut T {\n        self.ensure_init_fast();\n        unsafe { &mut (*self.data.get()).value }\n    }\n\n    /// Consume the `LazyLock`, returning the underlying value. The\n    /// initialization function will be called if it has not been\n    /// already.\n    #[inline]\n    pub fn into_inner(self) -> T {\n        self.ensure_init_fast();\n        let this = ManuallyDrop::new(self);\n        let data = unsafe { core::ptr::read(&this.data) }.into_inner();\n\n        ManuallyDrop::into_inner(unsafe { data.value })\n    }\n\n    /// Initialize the `LazyLock` if it has not been initialized yet.\n    /// This function is a fast track to [`Self::ensure_init`]\n    /// which does not require a critical section in most cases when\n    /// the value has been initialized already.\n    /// When this function returns, `self.data` is guaranteed to be\n    /// initialized and visible on the current core.\n    #[inline]\n    fn ensure_init_fast(&self) {\n        if !self.init.load(Ordering::Acquire) {\n            self.ensure_init();\n        }\n    }\n\n    /// Initialize the `LazyLock` if it has not been initialized yet.\n    /// When this function returns, `self.data` is guaranteed to be\n    /// initialized and visible on the current core.\n    fn ensure_init(&self) {\n        critical_section::with(|_| {\n            if !self.init.load(Ordering::Acquire) {\n                let data = unsafe { &mut *self.data.get() };\n                let f = unsafe { ManuallyDrop::take(&mut data.f) };\n                let value = f();\n                data.value = ManuallyDrop::new(value);\n\n                self.init.store(true, Ordering::Release);\n            }\n        });\n    }\n}\n\nimpl<T, F> Drop for LazyLock<T, F> {\n    fn drop(&mut self) {\n        if self.init.load(Ordering::Acquire) {\n            unsafe { ManuallyDrop::drop(&mut self.data.get_mut().value) };\n        } else {\n            unsafe { ManuallyDrop::drop(&mut self.data.get_mut().f) };\n        }\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use core::sync::atomic::{AtomicU32, Ordering};\n\n    use super::*;\n\n    #[test]\n    fn test_lazy_lock() {\n        static VALUE: LazyLock<u32> = LazyLock::new(|| 20);\n        let reference = VALUE.get();\n        assert_eq!(reference, &20);\n    }\n    #[test]\n    fn test_lazy_lock_mutation() {\n        let mut value: LazyLock<u32> = LazyLock::new(|| 20);\n        *value.get_mut() = 21;\n        let reference = value.get();\n        assert_eq!(reference, &21);\n    }\n    #[test]\n    fn test_lazy_lock_into_inner() {\n        let lazy: LazyLock<u32> = LazyLock::new(|| 20);\n        let value = lazy.into_inner();\n        assert_eq!(value, 20);\n    }\n\n    static DROP_CHECKER: AtomicU32 = AtomicU32::new(0);\n    #[derive(Debug)]\n    struct DropCheck;\n\n    impl Drop for DropCheck {\n        fn drop(&mut self) {\n            DROP_CHECKER.fetch_add(1, Ordering::Acquire);\n        }\n    }\n\n    #[test]\n    fn test_lazy_drop() {\n        let lazy: LazyLock<DropCheck> = LazyLock::new(|| DropCheck);\n        assert_eq!(DROP_CHECKER.load(Ordering::Acquire), 0);\n        lazy.get();\n        drop(lazy);\n        assert_eq!(DROP_CHECKER.load(Ordering::Acquire), 1);\n\n        let dropper = DropCheck;\n        let lazy_fn: LazyLock<u32, _> = LazyLock::new(move || {\n            let _a = dropper;\n            20\n        });\n        assert_eq!(DROP_CHECKER.load(Ordering::Acquire), 1);\n        drop(lazy_fn);\n        assert_eq!(DROP_CHECKER.load(Ordering::Acquire), 2);\n    }\n}\n"
  },
  {
    "path": "embassy-sync/src/lib.rs",
    "content": "#![cfg_attr(not(feature = \"std\"), no_std)]\n#![allow(async_fn_in_trait)]\n#![allow(clippy::new_without_default)]\n#![allow(unsafe_op_in_unsafe_fn)]\n#![doc = include_str!(\"../README.md\")]\n#![warn(missing_docs)]\n\n// This mod MUST go first, so that the others see its macros.\npub(crate) mod fmt;\n\n// internal use\nmod ring_buffer;\n\npub mod blocking_mutex;\npub mod channel;\npub mod lazy_lock;\npub mod mutex;\npub mod once_lock;\npub mod pipe;\npub mod priority_channel;\npub mod pubsub;\npub mod rwlock;\npub mod semaphore;\npub mod signal;\npub mod waitqueue;\npub mod watch;\npub mod zerocopy_channel;\n"
  },
  {
    "path": "embassy-sync/src/mutex.rs",
    "content": "//! Async mutex.\n//!\n//! This module provides a mutex that can be used to synchronize data between asynchronous tasks.\nuse core::cell::{RefCell, UnsafeCell};\nuse core::future::{Future, poll_fn};\nuse core::ops::{Deref, DerefMut};\nuse core::task::Poll;\nuse core::{fmt, mem};\n\nuse crate::blocking_mutex::Mutex as BlockingMutex;\nuse crate::blocking_mutex::raw::RawMutex;\nuse crate::waitqueue::WakerRegistration;\n\n/// Error returned by [`Mutex::try_lock`]\n#[derive(PartialEq, Eq, Clone, Copy, Debug)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub struct TryLockError;\n\n#[derive(Debug)]\nstruct State {\n    locked: bool,\n    waker: WakerRegistration,\n}\n\n/// Async mutex.\n///\n/// The mutex is generic over a blocking [`RawMutex`].\n/// The raw mutex is used to guard access to the internal \"is locked\" flag. It\n/// is held for very short periods only, while locking and unlocking. It is *not* held\n/// for the entire time the async Mutex is locked.\n///\n/// Which implementation you select depends on the context in which you're using the mutex.\n///\n/// Use [`CriticalSectionRawMutex`](crate::blocking_mutex::raw::CriticalSectionRawMutex) when data can be shared between threads and interrupts.\n///\n/// Use [`NoopRawMutex`](crate::blocking_mutex::raw::NoopRawMutex) when data is only shared between tasks running on the same executor.\n///\n/// Use [`ThreadModeRawMutex`](crate::blocking_mutex::raw::ThreadModeRawMutex) when data is shared between tasks running on the same executor but you want a singleton.\n///\npub struct Mutex<M, T>\nwhere\n    M: RawMutex,\n    T: ?Sized,\n{\n    state: BlockingMutex<M, RefCell<State>>,\n    inner: UnsafeCell<T>,\n}\n\nunsafe impl<M: RawMutex + Send, T: ?Sized + Send> Send for Mutex<M, T> {}\nunsafe impl<M: RawMutex + Sync, T: ?Sized + Send> Sync for Mutex<M, T> {}\n\n/// Async mutex.\nimpl<M, T> Mutex<M, T>\nwhere\n    M: RawMutex,\n{\n    /// Create a new mutex with the given value.\n    pub const fn new(value: T) -> Self {\n        Self {\n            inner: UnsafeCell::new(value),\n            state: BlockingMutex::new(RefCell::new(State {\n                locked: false,\n                waker: WakerRegistration::new(),\n            })),\n        }\n    }\n}\n\nimpl<M, T> Mutex<M, T>\nwhere\n    M: RawMutex,\n    T: ?Sized,\n{\n    /// Lock the mutex.\n    ///\n    /// This will wait for the mutex to be unlocked if it's already locked.\n    pub fn lock(&self) -> impl Future<Output = MutexGuard<'_, M, T>> {\n        poll_fn(|cx| {\n            let ready = self.state.lock(|s| {\n                let mut s = s.borrow_mut();\n                if s.locked {\n                    s.waker.register(cx.waker());\n                    false\n                } else {\n                    s.locked = true;\n                    true\n                }\n            });\n\n            if ready {\n                Poll::Ready(MutexGuard { mutex: self })\n            } else {\n                Poll::Pending\n            }\n        })\n    }\n\n    /// Attempt to immediately lock the mutex.\n    ///\n    /// If the mutex is already locked, this will return an error instead of waiting.\n    pub fn try_lock(&self) -> Result<MutexGuard<'_, M, T>, TryLockError> {\n        self.state.lock(|s| {\n            let mut s = s.borrow_mut();\n            if s.locked {\n                Err(TryLockError)\n            } else {\n                s.locked = true;\n                Ok(())\n            }\n        })?;\n\n        Ok(MutexGuard { mutex: self })\n    }\n\n    /// Consumes this mutex, returning the underlying data.\n    pub fn into_inner(self) -> T\n    where\n        T: Sized,\n    {\n        self.inner.into_inner()\n    }\n\n    /// Returns a mutable reference to the underlying data.\n    ///\n    /// Since this call borrows the Mutex mutably, no actual locking needs to\n    /// take place -- the mutable borrow statically guarantees no locks exist.\n    pub fn get_mut(&mut self) -> &mut T {\n        self.inner.get_mut()\n    }\n}\n\nimpl<M: RawMutex, T> From<T> for Mutex<M, T> {\n    fn from(from: T) -> Self {\n        Self::new(from)\n    }\n}\n\nimpl<M, T> Default for Mutex<M, T>\nwhere\n    M: RawMutex,\n    T: Default,\n{\n    fn default() -> Self {\n        Self::new(Default::default())\n    }\n}\n\nimpl<M, T> fmt::Debug for Mutex<M, T>\nwhere\n    M: RawMutex,\n    T: ?Sized + fmt::Debug,\n{\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        let mut d = f.debug_struct(\"Mutex\");\n        match self.try_lock() {\n            Ok(value) => {\n                d.field(\"inner\", &&*value);\n            }\n            Err(TryLockError) => {\n                d.field(\"inner\", &format_args!(\"<locked>\"));\n            }\n        }\n\n        d.finish_non_exhaustive()\n    }\n}\n\n/// Async mutex guard.\n///\n/// Owning an instance of this type indicates having\n/// successfully locked the mutex, and grants access to the contents.\n///\n/// Dropping it unlocks the mutex.\n#[clippy::has_significant_drop]\n#[must_use = \"if unused the Mutex will immediately unlock\"]\npub struct MutexGuard<'a, M, T>\nwhere\n    M: RawMutex,\n    T: ?Sized,\n{\n    mutex: &'a Mutex<M, T>,\n}\n\nimpl<'a, M, T> MutexGuard<'a, M, T>\nwhere\n    M: RawMutex,\n    T: ?Sized,\n{\n    /// Returns a locked view over a portion of the locked data.\n    pub fn map<U: ?Sized>(this: Self, fun: impl FnOnce(&mut T) -> &mut U) -> MappedMutexGuard<'a, M, U> {\n        let mutex = this.mutex;\n        let value = fun(unsafe { &mut *this.mutex.inner.get() });\n        // Don't run the `drop` method for MutexGuard. The ownership of the underlying\n        // locked state is being moved to the returned MappedMutexGuard.\n        mem::forget(this);\n        MappedMutexGuard {\n            state: &mutex.state,\n            value,\n        }\n    }\n}\n\nimpl<'a, M, T> Drop for MutexGuard<'a, M, T>\nwhere\n    M: RawMutex,\n    T: ?Sized,\n{\n    fn drop(&mut self) {\n        self.mutex.state.lock(|s| {\n            let mut s = unwrap!(s.try_borrow_mut());\n            s.locked = false;\n            s.waker.wake();\n        })\n    }\n}\n\nimpl<'a, M, T> Deref for MutexGuard<'a, M, T>\nwhere\n    M: RawMutex,\n    T: ?Sized,\n{\n    type Target = T;\n    fn deref(&self) -> &Self::Target {\n        // Safety: the MutexGuard represents exclusive access to the contents\n        // of the mutex, so it's OK to get it.\n        unsafe { &*(self.mutex.inner.get() as *const T) }\n    }\n}\n\nimpl<'a, M, T> DerefMut for MutexGuard<'a, M, T>\nwhere\n    M: RawMutex,\n    T: ?Sized,\n{\n    fn deref_mut(&mut self) -> &mut Self::Target {\n        // Safety: the MutexGuard represents exclusive access to the contents\n        // of the mutex, so it's OK to get it.\n        unsafe { &mut *(self.mutex.inner.get()) }\n    }\n}\n\nimpl<'a, M, T> fmt::Debug for MutexGuard<'a, M, T>\nwhere\n    M: RawMutex,\n    T: ?Sized + fmt::Debug,\n{\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        fmt::Debug::fmt(&**self, f)\n    }\n}\n\nimpl<'a, M, T> fmt::Display for MutexGuard<'a, M, T>\nwhere\n    M: RawMutex,\n    T: ?Sized + fmt::Display,\n{\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        fmt::Display::fmt(&**self, f)\n    }\n}\n\n/// A handle to a held `Mutex` that has had a function applied to it via [`MutexGuard::map`] or\n/// [`MappedMutexGuard::map`].\n///\n/// This can be used to hold a subfield of the protected data.\n#[clippy::has_significant_drop]\npub struct MappedMutexGuard<'a, M, T>\nwhere\n    M: RawMutex,\n    T: ?Sized,\n{\n    state: &'a BlockingMutex<M, RefCell<State>>,\n    value: *mut T,\n}\n\nimpl<'a, M, T> MappedMutexGuard<'a, M, T>\nwhere\n    M: RawMutex,\n    T: ?Sized,\n{\n    /// Returns a locked view over a portion of the locked data.\n    pub fn map<U: ?Sized>(this: Self, fun: impl FnOnce(&mut T) -> &mut U) -> MappedMutexGuard<'a, M, U> {\n        let state = this.state;\n        let value = fun(unsafe { &mut *this.value });\n        // Don't run the `drop` method for MutexGuard. The ownership of the underlying\n        // locked state is being moved to the returned MappedMutexGuard.\n        mem::forget(this);\n        MappedMutexGuard { state, value }\n    }\n}\n\nimpl<'a, M, T> Deref for MappedMutexGuard<'a, M, T>\nwhere\n    M: RawMutex,\n    T: ?Sized,\n{\n    type Target = T;\n    fn deref(&self) -> &Self::Target {\n        // Safety: the MutexGuard represents exclusive access to the contents\n        // of the mutex, so it's OK to get it.\n        unsafe { &*self.value }\n    }\n}\n\nimpl<'a, M, T> DerefMut for MappedMutexGuard<'a, M, T>\nwhere\n    M: RawMutex,\n    T: ?Sized,\n{\n    fn deref_mut(&mut self) -> &mut Self::Target {\n        // Safety: the MutexGuard represents exclusive access to the contents\n        // of the mutex, so it's OK to get it.\n        unsafe { &mut *self.value }\n    }\n}\n\nimpl<'a, M, T> Drop for MappedMutexGuard<'a, M, T>\nwhere\n    M: RawMutex,\n    T: ?Sized,\n{\n    fn drop(&mut self) {\n        self.state.lock(|s| {\n            let mut s = unwrap!(s.try_borrow_mut());\n            s.locked = false;\n            s.waker.wake();\n        })\n    }\n}\n\nunsafe impl<M, T> Send for MappedMutexGuard<'_, M, T>\nwhere\n    M: RawMutex + Sync,\n    T: Send + ?Sized,\n{\n}\n\nunsafe impl<M, T> Sync for MappedMutexGuard<'_, M, T>\nwhere\n    M: RawMutex + Sync,\n    T: Sync + ?Sized,\n{\n}\n\nimpl<'a, M, T> fmt::Debug for MappedMutexGuard<'a, M, T>\nwhere\n    M: RawMutex,\n    T: ?Sized + fmt::Debug,\n{\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        fmt::Debug::fmt(&**self, f)\n    }\n}\n\nimpl<'a, M, T> fmt::Display for MappedMutexGuard<'a, M, T>\nwhere\n    M: RawMutex,\n    T: ?Sized + fmt::Display,\n{\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        fmt::Display::fmt(&**self, f)\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use crate::blocking_mutex::raw::NoopRawMutex;\n    use crate::mutex::{Mutex, MutexGuard};\n\n    #[futures_test::test]\n    async fn mapped_guard_releases_lock_when_dropped() {\n        let mutex: Mutex<NoopRawMutex, [i32; 2]> = Mutex::new([0, 1]);\n\n        {\n            let guard = mutex.lock().await;\n            assert_eq!(*guard, [0, 1]);\n            let mut mapped = MutexGuard::map(guard, |this| &mut this[1]);\n            assert_eq!(*mapped, 1);\n            *mapped = 2;\n        }\n\n        {\n            let guard = mutex.lock().await;\n            assert_eq!(*guard, [0, 2]);\n            let mut mapped = MutexGuard::map(guard, |this| &mut this[1]);\n            assert_eq!(*mapped, 2);\n            *mapped = 3;\n        }\n\n        assert_eq!(*mutex.lock().await, [0, 3]);\n    }\n}\n"
  },
  {
    "path": "embassy-sync/src/once_lock.rs",
    "content": "//! Synchronization primitive for initializing a value once, allowing others to await a reference to the value.\n\nuse core::cell::Cell;\nuse core::fmt::{Debug, Formatter};\nuse core::future::{Future, poll_fn};\nuse core::mem::MaybeUninit;\nuse core::sync::atomic::{AtomicBool, Ordering};\nuse core::task::Poll;\n\n/// The `OnceLock` is a synchronization primitive that allows for\n/// initializing a value once, and allowing others to `.await` a\n/// reference to the value. This is useful for lazy initialization of\n/// a static value.\n///\n/// **Note**: this implementation uses a busy loop to poll the value,\n/// which is not as efficient as registering a dedicated `Waker`.\n/// However, if the usecase for it is to initialize a static variable\n/// relatively early in the program life cycle, it should be fine.\n///\n/// # Example\n/// ```\n/// use futures_executor::block_on;\n/// use embassy_sync::once_lock::OnceLock;\n///\n/// // Define a static value that will be lazily initialized\n/// static VALUE: OnceLock<u32> = OnceLock::new();\n///\n/// let f = async {\n///\n/// // Initialize the value\n/// let reference = VALUE.get_or_init(|| 20);\n/// assert_eq!(reference, &20);\n///\n/// // Wait for the value to be initialized\n/// // and get a static reference it\n/// assert_eq!(VALUE.get().await, &20);\n///\n/// };\n/// block_on(f)\n/// ```\npub struct OnceLock<T> {\n    init: AtomicBool,\n    data: Cell<MaybeUninit<T>>,\n}\n\nimpl<T> Debug for OnceLock<T> {\n    fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {\n        f.debug_struct(\"OnceLock\")\n            .field(\"init\", &self.init)\n            .field(\"data\", &\"Cell<MaybeUninit<{unprintable}>>\")\n            .finish()\n    }\n}\n\nunsafe impl<T> Sync for OnceLock<T> where T: Sync {}\n\nimpl<T> OnceLock<T> {\n    /// Create a new uninitialized `OnceLock`.\n    pub const fn new() -> Self {\n        Self {\n            init: AtomicBool::new(false),\n            data: Cell::new(MaybeUninit::zeroed()),\n        }\n    }\n\n    /// Get a reference to the underlying value, waiting for it to be set.\n    /// If the value is already set, this will return immediately.\n    pub fn get(&self) -> impl Future<Output = &T> {\n        poll_fn(|cx| match self.try_get() {\n            Some(data) => Poll::Ready(data),\n            None => {\n                cx.waker().wake_by_ref();\n                Poll::Pending\n            }\n        })\n    }\n\n    /// Try to get a reference to the underlying value if it exists.\n    pub fn try_get(&self) -> Option<&T> {\n        if self.init.load(Ordering::Relaxed) {\n            Some(unsafe { self.get_ref_unchecked() })\n        } else {\n            None\n        }\n    }\n\n    /// Set the underlying value. If the value is already set, this will return an error with the given value.\n    pub fn init(&self, value: T) -> Result<(), T> {\n        // Critical section is required to ensure that the value is\n        // not simultaneously initialized elsewhere at the same time.\n        critical_section::with(|_| {\n            // If the value is not set, set it and return Ok.\n            if !self.init.load(Ordering::Relaxed) {\n                self.data.set(MaybeUninit::new(value));\n                self.init.store(true, Ordering::Relaxed);\n                Ok(())\n\n            // Otherwise return an error with the given value.\n            } else {\n                Err(value)\n            }\n        })\n    }\n\n    /// Get a reference to the underlying value, initializing it if it does not exist.\n    pub fn get_or_init<F>(&self, f: F) -> &T\n    where\n        F: FnOnce() -> T,\n    {\n        // Critical section is required to ensure that the value is\n        // not simultaneously initialized elsewhere at the same time.\n        critical_section::with(|_| {\n            // If the value is not set, set it.\n            if !self.init.load(Ordering::Relaxed) {\n                self.data.set(MaybeUninit::new(f()));\n                self.init.store(true, Ordering::Relaxed);\n            }\n        });\n\n        // Return a reference to the value.\n        unsafe { self.get_ref_unchecked() }\n    }\n\n    /// Consume the `OnceLock`, returning the underlying value if it was initialized.\n    pub fn into_inner(self) -> Option<T> {\n        if self.init.load(Ordering::Relaxed) {\n            Some(unsafe { self.data.into_inner().assume_init() })\n        } else {\n            None\n        }\n    }\n\n    /// Take the underlying value if it was initialized, uninitializing the `OnceLock` in the process.\n    pub fn take(&mut self) -> Option<T> {\n        // If the value is set, uninitialize the lock and return the value.\n        critical_section::with(|_| {\n            if self.init.load(Ordering::Relaxed) {\n                let val = unsafe { self.data.replace(MaybeUninit::zeroed()).assume_init() };\n                self.init.store(false, Ordering::Relaxed);\n                Some(val)\n\n            // Otherwise return None.\n            } else {\n                None\n            }\n        })\n    }\n\n    /// Check if the value has been set.\n    pub fn is_set(&self) -> bool {\n        self.init.load(Ordering::Relaxed)\n    }\n\n    /// Get a reference to the underlying value.\n    /// # Safety\n    /// Must only be used if a value has been set.\n    unsafe fn get_ref_unchecked(&self) -> &T {\n        (*self.data.as_ptr()).assume_init_ref()\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n\n    #[test]\n    fn once_lock() {\n        let lock = OnceLock::new();\n        assert_eq!(lock.try_get(), None);\n        assert_eq!(lock.is_set(), false);\n\n        let v = 42;\n        assert_eq!(lock.init(v), Ok(()));\n        assert_eq!(lock.is_set(), true);\n        assert_eq!(lock.try_get(), Some(&v));\n        assert_eq!(lock.try_get(), Some(&v));\n\n        let v = 43;\n        assert_eq!(lock.init(v), Err(v));\n        assert_eq!(lock.is_set(), true);\n        assert_eq!(lock.try_get(), Some(&42));\n    }\n\n    #[test]\n    fn once_lock_get_or_init() {\n        let lock = OnceLock::new();\n        assert_eq!(lock.try_get(), None);\n        assert_eq!(lock.is_set(), false);\n\n        let v = lock.get_or_init(|| 42);\n        assert_eq!(v, &42);\n        assert_eq!(lock.is_set(), true);\n        assert_eq!(lock.try_get(), Some(&42));\n\n        let v = lock.get_or_init(|| 43);\n        assert_eq!(v, &42);\n        assert_eq!(lock.is_set(), true);\n        assert_eq!(lock.try_get(), Some(&42));\n    }\n\n    #[test]\n    fn once_lock_static() {\n        static LOCK: OnceLock<i32> = OnceLock::new();\n\n        let v: &'static i32 = LOCK.get_or_init(|| 42);\n        assert_eq!(v, &42);\n\n        let v: &'static i32 = LOCK.get_or_init(|| 43);\n        assert_eq!(v, &42);\n    }\n\n    #[futures_test::test]\n    async fn once_lock_async() {\n        static LOCK: OnceLock<i32> = OnceLock::new();\n\n        assert!(LOCK.init(42).is_ok());\n\n        let v: &'static i32 = LOCK.get().await;\n        assert_eq!(v, &42);\n    }\n\n    #[test]\n    fn once_lock_into_inner() {\n        let lock: OnceLock<i32> = OnceLock::new();\n\n        let v = lock.get_or_init(|| 42);\n        assert_eq!(v, &42);\n\n        assert_eq!(lock.into_inner(), Some(42));\n    }\n\n    #[test]\n    fn once_lock_take_init() {\n        let mut lock: OnceLock<i32> = OnceLock::new();\n\n        assert_eq!(lock.get_or_init(|| 42), &42);\n        assert_eq!(lock.is_set(), true);\n\n        assert_eq!(lock.take(), Some(42));\n        assert_eq!(lock.is_set(), false);\n\n        assert_eq!(lock.get_or_init(|| 43), &43);\n        assert_eq!(lock.is_set(), true);\n    }\n}\n"
  },
  {
    "path": "embassy-sync/src/pipe.rs",
    "content": "//! Async byte stream pipe.\n\nuse core::cell::{RefCell, UnsafeCell};\nuse core::convert::Infallible;\nuse core::future::Future;\nuse core::ops::Range;\nuse core::pin::Pin;\nuse core::task::{Context, Poll};\n\nuse crate::blocking_mutex::Mutex;\nuse crate::blocking_mutex::raw::RawMutex;\nuse crate::ring_buffer::RingBuffer;\nuse crate::waitqueue::WakerRegistration;\n\n/// Write-only access to a [`Pipe`].\n#[derive(Debug)]\npub struct Writer<'p, M, const N: usize>\nwhere\n    M: RawMutex,\n{\n    pipe: &'p Pipe<M, N>,\n}\n\nimpl<'p, M, const N: usize> Clone for Writer<'p, M, N>\nwhere\n    M: RawMutex,\n{\n    fn clone(&self) -> Self {\n        *self\n    }\n}\n\nimpl<'p, M, const N: usize> Copy for Writer<'p, M, N> where M: RawMutex {}\n\nimpl<'p, M, const N: usize> Writer<'p, M, N>\nwhere\n    M: RawMutex,\n{\n    /// Write some bytes to the pipe.\n    ///\n    /// See [`Pipe::write()`]\n    pub fn write<'a>(&'a self, buf: &'a [u8]) -> WriteFuture<'a, M, N> {\n        self.pipe.write(buf)\n    }\n\n    /// Attempt to immediately write some bytes to the pipe.\n    ///\n    /// See [`Pipe::try_write()`]\n    pub fn try_write(&self, buf: &[u8]) -> Result<usize, TryWriteError> {\n        self.pipe.try_write(buf)\n    }\n}\n\n/// Future returned by [`Pipe::write`] and  [`Writer::write`].\n#[must_use = \"futures do nothing unless you `.await` or poll them\"]\n#[derive(Debug)]\npub struct WriteFuture<'p, M, const N: usize>\nwhere\n    M: RawMutex,\n{\n    pipe: &'p Pipe<M, N>,\n    buf: &'p [u8],\n}\n\nimpl<'p, M, const N: usize> Future for WriteFuture<'p, M, N>\nwhere\n    M: RawMutex,\n{\n    type Output = usize;\n\n    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {\n        match self.pipe.try_write_with_context(Some(cx), self.buf) {\n            Ok(n) => Poll::Ready(n),\n            Err(TryWriteError::Full) => Poll::Pending,\n        }\n    }\n}\n\nimpl<'p, M, const N: usize> Unpin for WriteFuture<'p, M, N> where M: RawMutex {}\n\n/// Read-only access to a [`Pipe`].\n#[derive(Debug)]\npub struct Reader<'p, M, const N: usize>\nwhere\n    M: RawMutex,\n{\n    pipe: &'p Pipe<M, N>,\n}\n\nimpl<'p, M, const N: usize> Reader<'p, M, N>\nwhere\n    M: RawMutex,\n{\n    /// Read some bytes from the pipe.\n    ///\n    /// See [`Pipe::read()`]\n    pub fn read<'a>(&'a self, buf: &'a mut [u8]) -> ReadFuture<'a, M, N> {\n        self.pipe.read(buf)\n    }\n\n    /// Attempt to immediately read some bytes from the pipe.\n    ///\n    /// See [`Pipe::try_read()`]\n    pub fn try_read(&self, buf: &mut [u8]) -> Result<usize, TryReadError> {\n        self.pipe.try_read(buf)\n    }\n\n    /// Return the contents of the internal buffer, filling it with more data from the inner reader if it is empty.\n    ///\n    /// If no bytes are currently available to read, this function waits until at least one byte is available.\n    ///\n    /// If the reader is at end-of-file (EOF), an empty slice is returned.\n    pub fn fill_buf(&mut self) -> FillBufFuture<'_, M, N> {\n        FillBufFuture { pipe: Some(self.pipe) }\n    }\n\n    /// Try returning contents of the internal buffer.\n    ///\n    /// If no bytes are currently available to read, this function returns `Err(TryReadError::Empty)`.\n    ///\n    /// If the reader is at end-of-file (EOF), an empty slice is returned.\n    pub fn try_fill_buf(&mut self) -> Result<&[u8], TryReadError> {\n        unsafe { self.pipe.try_fill_buf_with_context(None) }\n    }\n\n    /// Tell this buffer that `amt` bytes have been consumed from the buffer, so they should no longer be returned in calls to `fill_buf`.\n    pub fn consume(&mut self, amt: usize) {\n        self.pipe.consume(amt)\n    }\n}\n\n/// Future returned by [`Pipe::read`] and  [`Reader::read`].\n#[must_use = \"futures do nothing unless you `.await` or poll them\"]\n#[derive(Debug)]\npub struct ReadFuture<'p, M, const N: usize>\nwhere\n    M: RawMutex,\n{\n    pipe: &'p Pipe<M, N>,\n    buf: &'p mut [u8],\n}\n\nimpl<'p, M, const N: usize> Future for ReadFuture<'p, M, N>\nwhere\n    M: RawMutex,\n{\n    type Output = usize;\n\n    fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {\n        match self.pipe.try_read_with_context(Some(cx), self.buf) {\n            Ok(n) => Poll::Ready(n),\n            Err(TryReadError::Empty) => Poll::Pending,\n        }\n    }\n}\n\nimpl<'p, M, const N: usize> Unpin for ReadFuture<'p, M, N> where M: RawMutex {}\n\n/// Future returned by [`Reader::fill_buf`].\n#[must_use = \"futures do nothing unless you `.await` or poll them\"]\n#[derive(Debug)]\npub struct FillBufFuture<'p, M, const N: usize>\nwhere\n    M: RawMutex,\n{\n    pipe: Option<&'p Pipe<M, N>>,\n}\n\nimpl<'p, M, const N: usize> Future for FillBufFuture<'p, M, N>\nwhere\n    M: RawMutex,\n{\n    type Output = &'p [u8];\n\n    fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {\n        let pipe = self.pipe.take().unwrap();\n        match unsafe { pipe.try_fill_buf_with_context(Some(cx)) } {\n            Ok(buf) => Poll::Ready(buf),\n            Err(TryReadError::Empty) => {\n                self.pipe = Some(pipe);\n                Poll::Pending\n            }\n        }\n    }\n}\n\nimpl<'p, M, const N: usize> Unpin for FillBufFuture<'p, M, N> where M: RawMutex {}\n\n/// Error returned by [`try_read`](Pipe::try_read).\n#[derive(PartialEq, Eq, Clone, Copy, Debug)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub enum TryReadError {\n    /// No data could be read from the pipe because it is currently\n    /// empty, and reading would require blocking.\n    Empty,\n}\n\n/// Error returned by [`try_write`](Pipe::try_write).\n#[derive(PartialEq, Eq, Clone, Copy, Debug)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub enum TryWriteError {\n    /// No data could be written to the pipe because it is\n    /// currently full, and writing would require blocking.\n    Full,\n}\n\n#[derive(Debug)]\nstruct PipeState<const N: usize> {\n    buffer: RingBuffer<N>,\n    read_waker: WakerRegistration,\n    write_waker: WakerRegistration,\n}\n\n#[repr(transparent)]\n#[derive(Debug)]\nstruct Buffer<const N: usize>(UnsafeCell<[u8; N]>);\n\nimpl<const N: usize> Buffer<N> {\n    unsafe fn get<'a>(&self, r: Range<usize>) -> &'a [u8] {\n        let p = self.0.get() as *const u8;\n        core::slice::from_raw_parts(p.add(r.start), r.end - r.start)\n    }\n\n    unsafe fn get_mut<'a>(&self, r: Range<usize>) -> &'a mut [u8] {\n        let p = self.0.get() as *mut u8;\n        core::slice::from_raw_parts_mut(p.add(r.start), r.end - r.start)\n    }\n}\n\nunsafe impl<const N: usize> Send for Buffer<N> {}\nunsafe impl<const N: usize> Sync for Buffer<N> {}\n\n/// A bounded byte-oriented pipe for communicating between asynchronous tasks\n/// with backpressure.\n///\n/// The pipe will buffer up to the provided number of bytes. Once the\n/// buffer is full, attempts to `write` new bytes will wait until buffer space is freed up.\n///\n/// All data written will become available in the same order as it was written.\n#[derive(Debug)]\npub struct Pipe<M, const N: usize>\nwhere\n    M: RawMutex,\n{\n    buf: Buffer<N>,\n    inner: Mutex<M, RefCell<PipeState<N>>>,\n}\n\nimpl<M, const N: usize> Pipe<M, N>\nwhere\n    M: RawMutex,\n{\n    /// Establish a new bounded pipe. For example, to create one with a NoopMutex:\n    ///\n    /// ```\n    /// use embassy_sync::pipe::Pipe;\n    /// use embassy_sync::blocking_mutex::raw::NoopRawMutex;\n    ///\n    /// // Declare a bounded pipe, with a buffer of 256 bytes.\n    /// let mut pipe = Pipe::<NoopRawMutex, 256>::new();\n    /// ```\n    pub const fn new() -> Self {\n        Self {\n            buf: Buffer(UnsafeCell::new([0; N])),\n            inner: Mutex::new(RefCell::new(PipeState {\n                buffer: RingBuffer::new(),\n                read_waker: WakerRegistration::new(),\n                write_waker: WakerRegistration::new(),\n            })),\n        }\n    }\n\n    fn lock<R>(&self, f: impl FnOnce(&mut PipeState<N>) -> R) -> R {\n        self.inner.lock(|rc| f(&mut *rc.borrow_mut()))\n    }\n\n    fn try_read_with_context(&self, cx: Option<&mut Context<'_>>, buf: &mut [u8]) -> Result<usize, TryReadError> {\n        self.inner.lock(|rc: &RefCell<PipeState<N>>| {\n            let s = &mut *rc.borrow_mut();\n\n            if s.buffer.is_full() {\n                s.write_waker.wake();\n            }\n\n            let available = unsafe { self.buf.get(s.buffer.pop_buf()) };\n            if available.is_empty() {\n                if let Some(cx) = cx {\n                    s.read_waker.register(cx.waker());\n                }\n                return Err(TryReadError::Empty);\n            }\n\n            let n = available.len().min(buf.len());\n            buf[..n].copy_from_slice(&available[..n]);\n            s.buffer.pop(n);\n            Ok(n)\n        })\n    }\n\n    // safety: While the returned slice is alive,\n    // no `read` or `consume` methods in the pipe must be called.\n    unsafe fn try_fill_buf_with_context(&self, cx: Option<&mut Context<'_>>) -> Result<&[u8], TryReadError> {\n        self.inner.lock(|rc: &RefCell<PipeState<N>>| {\n            let s = &mut *rc.borrow_mut();\n\n            if s.buffer.is_full() {\n                s.write_waker.wake();\n            }\n\n            let available = unsafe { self.buf.get(s.buffer.pop_buf()) };\n            if available.is_empty() {\n                if let Some(cx) = cx {\n                    s.read_waker.register(cx.waker());\n                }\n                return Err(TryReadError::Empty);\n            }\n\n            Ok(available)\n        })\n    }\n\n    fn consume(&self, amt: usize) {\n        self.inner.lock(|rc: &RefCell<PipeState<N>>| {\n            let s = &mut *rc.borrow_mut();\n            let available = s.buffer.pop_buf();\n            assert!(amt <= available.len());\n            s.buffer.pop(amt);\n        })\n    }\n\n    fn try_write_with_context(&self, cx: Option<&mut Context<'_>>, buf: &[u8]) -> Result<usize, TryWriteError> {\n        self.inner.lock(|rc: &RefCell<PipeState<N>>| {\n            let s = &mut *rc.borrow_mut();\n\n            if s.buffer.is_empty() {\n                s.read_waker.wake();\n            }\n\n            let available = unsafe { self.buf.get_mut(s.buffer.push_buf()) };\n            if available.is_empty() {\n                if let Some(cx) = cx {\n                    s.write_waker.register(cx.waker());\n                }\n                return Err(TryWriteError::Full);\n            }\n\n            let n = available.len().min(buf.len());\n            available[..n].copy_from_slice(&buf[..n]);\n            s.buffer.push(n);\n            Ok(n)\n        })\n    }\n\n    /// Split this pipe into a BufRead-capable reader and a writer.\n    ///\n    /// The reader and writer borrow the current pipe mutably, so it is not\n    /// possible to use it directly while they exist. This is needed because\n    /// implementing `BufRead` requires there is a single reader.\n    ///\n    /// The writer is cloneable, the reader is not.\n    pub fn split(&mut self) -> (Reader<'_, M, N>, Writer<'_, M, N>) {\n        (Reader { pipe: self }, Writer { pipe: self })\n    }\n\n    /// Write some bytes to the pipe.\n    ///\n    /// This method writes a nonzero amount of bytes from `buf` into the pipe, and\n    /// returns the amount of bytes written.\n    ///\n    /// If it is not possible to write a nonzero amount of bytes because the pipe's buffer is full,\n    /// this method will wait until it isn't. See [`try_write`](Self::try_write) for a variant that\n    /// returns an error instead of waiting.\n    ///\n    /// It is not guaranteed that all bytes in the buffer are written, even if there's enough\n    /// free space in the pipe buffer for all. In other words, it is possible for `write` to return\n    /// without writing all of `buf` (returning a number less than `buf.len()`) and still leave\n    /// free space in the pipe buffer. You should always `write` in a loop, or use helpers like\n    /// `write_all` from the `embedded-io` crate.\n    pub fn write<'a>(&'a self, buf: &'a [u8]) -> WriteFuture<'a, M, N> {\n        WriteFuture { pipe: self, buf }\n    }\n\n    /// Write all bytes to the pipe.\n    ///\n    /// This method writes all bytes from `buf` into the pipe\n    pub async fn write_all(&self, mut buf: &[u8]) {\n        while !buf.is_empty() {\n            let n = self.write(buf).await;\n            buf = &buf[n..];\n        }\n    }\n\n    /// Attempt to immediately write some bytes to the pipe.\n    ///\n    /// This method will either write a nonzero amount of bytes to the pipe immediately,\n    /// or return an error if the pipe is full. See [`write`](Self::write) for a variant\n    /// that waits instead of returning an error.\n    pub fn try_write(&self, buf: &[u8]) -> Result<usize, TryWriteError> {\n        self.try_write_with_context(None, buf)\n    }\n\n    /// Read some bytes from the pipe.\n    ///\n    /// This method reads a nonzero amount of bytes from the pipe into `buf` and\n    /// returns the amount of bytes read.\n    ///\n    /// If it is not possible to read a nonzero amount of bytes because the pipe's buffer is empty,\n    /// this method will wait until it isn't. See [`try_read`](Self::try_read) for a variant that\n    /// returns an error instead of waiting.\n    ///\n    /// It is not guaranteed that all bytes in the buffer are read, even if there's enough\n    /// space in `buf` for all. In other words, it is possible for `read` to return\n    /// without filling `buf` (returning a number less than `buf.len()`) and still leave bytes\n    /// in the pipe buffer. You should always `read` in a loop, or use helpers like\n    /// `read_exact` from the `embedded-io` crate.\n    pub fn read<'a>(&'a self, buf: &'a mut [u8]) -> ReadFuture<'a, M, N> {\n        ReadFuture { pipe: self, buf }\n    }\n\n    /// Attempt to immediately read some bytes from the pipe.\n    ///\n    /// This method will either read a nonzero amount of bytes from the pipe immediately,\n    /// or return an error if the pipe is empty. See [`read`](Self::read) for a variant\n    /// that waits instead of returning an error.\n    pub fn try_read(&self, buf: &mut [u8]) -> Result<usize, TryReadError> {\n        self.try_read_with_context(None, buf)\n    }\n\n    /// Clear the data in the pipe's buffer.\n    pub fn clear(&self) {\n        self.inner.lock(|rc: &RefCell<PipeState<N>>| {\n            let s = &mut *rc.borrow_mut();\n\n            s.buffer.clear();\n            s.write_waker.wake();\n        })\n    }\n\n    /// Return whether the pipe is full (no free space in the buffer)\n    pub fn is_full(&self) -> bool {\n        self.len() == N\n    }\n\n    /// Return whether the pipe is empty (no data buffered)\n    pub fn is_empty(&self) -> bool {\n        self.len() == 0\n    }\n\n    /// Total byte capacity.\n    ///\n    /// This is the same as the `N` generic param.\n    pub fn capacity(&self) -> usize {\n        N\n    }\n\n    /// Used byte capacity.\n    pub fn len(&self) -> usize {\n        self.lock(|c| c.buffer.len())\n    }\n\n    /// Free byte capacity.\n    ///\n    /// This is equivalent to `capacity() - len()`\n    pub fn free_capacity(&self) -> usize {\n        N - self.len()\n    }\n}\n\nimpl<M: RawMutex, const N: usize> embedded_io_async::ErrorType for Pipe<M, N> {\n    type Error = Infallible;\n}\n\nimpl<M: RawMutex, const N: usize> embedded_io_async::Read for Pipe<M, N> {\n    async fn read(&mut self, buf: &mut [u8]) -> Result<usize, Self::Error> {\n        Ok(Pipe::read(self, buf).await)\n    }\n}\n\nimpl<M: RawMutex, const N: usize> embedded_io_async::Write for Pipe<M, N> {\n    async fn write(&mut self, buf: &[u8]) -> Result<usize, Self::Error> {\n        Ok(Pipe::write(self, buf).await)\n    }\n\n    async fn flush(&mut self) -> Result<(), Self::Error> {\n        Ok(())\n    }\n}\n\nimpl<M: RawMutex, const N: usize> embedded_io_async::ErrorType for &Pipe<M, N> {\n    type Error = Infallible;\n}\n\nimpl<M: RawMutex, const N: usize> embedded_io_async::Read for &Pipe<M, N> {\n    async fn read(&mut self, buf: &mut [u8]) -> Result<usize, Self::Error> {\n        Ok(Pipe::read(self, buf).await)\n    }\n}\n\nimpl<M: RawMutex, const N: usize> embedded_io_async::Write for &Pipe<M, N> {\n    async fn write(&mut self, buf: &[u8]) -> Result<usize, Self::Error> {\n        Ok(Pipe::write(self, buf).await)\n    }\n\n    async fn flush(&mut self) -> Result<(), Self::Error> {\n        Ok(())\n    }\n}\n\nimpl<M: RawMutex, const N: usize> embedded_io_async::ErrorType for Reader<'_, M, N> {\n    type Error = Infallible;\n}\n\nimpl<M: RawMutex, const N: usize> embedded_io_async::Read for Reader<'_, M, N> {\n    async fn read(&mut self, buf: &mut [u8]) -> Result<usize, Self::Error> {\n        Ok(Reader::read(self, buf).await)\n    }\n}\n\nimpl<M: RawMutex, const N: usize> embedded_io_async::BufRead for Reader<'_, M, N> {\n    async fn fill_buf(&mut self) -> Result<&[u8], Self::Error> {\n        Ok(Reader::fill_buf(self).await)\n    }\n\n    fn consume(&mut self, amt: usize) {\n        Reader::consume(self, amt)\n    }\n}\n\nimpl<M: RawMutex, const N: usize> embedded_io_async::ErrorType for Writer<'_, M, N> {\n    type Error = Infallible;\n}\n\nimpl<M: RawMutex, const N: usize> embedded_io_async::Write for Writer<'_, M, N> {\n    async fn write(&mut self, buf: &[u8]) -> Result<usize, Self::Error> {\n        Ok(Writer::write(self, buf).await)\n    }\n\n    async fn flush(&mut self) -> Result<(), Self::Error> {\n        Ok(())\n    }\n}\n\n//\n// Type-erased variants\n//\n\npub(crate) trait DynamicPipe {\n    fn write<'a>(&'a self, buf: &'a [u8]) -> DynamicWriteFuture<'a>;\n    fn read<'a>(&'a self, buf: &'a mut [u8]) -> DynamicReadFuture<'a>;\n\n    fn try_read(&self, buf: &mut [u8]) -> Result<usize, TryReadError>;\n    fn try_write(&self, buf: &[u8]) -> Result<usize, TryWriteError>;\n\n    fn try_write_with_context(&self, cx: Option<&mut Context<'_>>, buf: &[u8]) -> Result<usize, TryWriteError>;\n    fn try_read_with_context(&self, cx: Option<&mut Context<'_>>, buf: &mut [u8]) -> Result<usize, TryReadError>;\n\n    fn consume(&self, amt: usize);\n    unsafe fn try_fill_buf_with_context(&self, cx: Option<&mut Context<'_>>) -> Result<&[u8], TryReadError>;\n}\n\nimpl<M, const N: usize> DynamicPipe for Pipe<M, N>\nwhere\n    M: RawMutex,\n{\n    fn consume(&self, amt: usize) {\n        Pipe::consume(self, amt)\n    }\n\n    unsafe fn try_fill_buf_with_context(&self, cx: Option<&mut Context<'_>>) -> Result<&[u8], TryReadError> {\n        Pipe::try_fill_buf_with_context(self, cx)\n    }\n\n    fn write<'a>(&'a self, buf: &'a [u8]) -> DynamicWriteFuture<'a> {\n        Pipe::write(self, buf).into()\n    }\n\n    fn read<'a>(&'a self, buf: &'a mut [u8]) -> DynamicReadFuture<'a> {\n        Pipe::read(self, buf).into()\n    }\n\n    fn try_read(&self, buf: &mut [u8]) -> Result<usize, TryReadError> {\n        Pipe::try_read(self, buf)\n    }\n\n    fn try_write(&self, buf: &[u8]) -> Result<usize, TryWriteError> {\n        Pipe::try_write(self, buf)\n    }\n\n    fn try_write_with_context(&self, cx: Option<&mut Context<'_>>, buf: &[u8]) -> Result<usize, TryWriteError> {\n        Pipe::try_write_with_context(self, cx, buf)\n    }\n\n    fn try_read_with_context(&self, cx: Option<&mut Context<'_>>, buf: &mut [u8]) -> Result<usize, TryReadError> {\n        Pipe::try_read_with_context(self, cx, buf)\n    }\n}\n\n/// Write-only access to the dynamic pipe.\npub struct DynamicWriter<'p> {\n    pipe: &'p dyn DynamicPipe,\n}\n\nimpl<'p> Clone for DynamicWriter<'p> {\n    fn clone(&self) -> Self {\n        *self\n    }\n}\n\nimpl<'p> Copy for DynamicWriter<'p> {}\n\nimpl<'p> DynamicWriter<'p> {\n    /// Write some bytes to the pipe.\n    ///\n    /// See [`Pipe::write()`]\n    pub fn write<'a>(&'a self, buf: &'a [u8]) -> DynamicWriteFuture<'a> {\n        self.pipe.write(buf)\n    }\n\n    /// Attempt to immediately write some bytes to the pipe.\n    ///\n    /// See [`Pipe::try_write()`]\n    pub fn try_write(&self, buf: &[u8]) -> Result<usize, TryWriteError> {\n        self.pipe.try_write(buf)\n    }\n}\n\nimpl<'p, M, const N: usize> From<Writer<'p, M, N>> for DynamicWriter<'p>\nwhere\n    M: RawMutex,\n{\n    fn from(value: Writer<'p, M, N>) -> Self {\n        Self { pipe: value.pipe }\n    }\n}\n\n/// Future returned by [`DynamicWriter::write`].\n#[must_use = \"futures do nothing unless you `.await` or poll them\"]\npub struct DynamicWriteFuture<'p> {\n    pipe: &'p dyn DynamicPipe,\n    buf: &'p [u8],\n}\n\nimpl<'p> Future for DynamicWriteFuture<'p> {\n    type Output = usize;\n\n    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {\n        match self.pipe.try_write_with_context(Some(cx), self.buf) {\n            Ok(n) => Poll::Ready(n),\n            Err(TryWriteError::Full) => Poll::Pending,\n        }\n    }\n}\n\nimpl<'p> Unpin for DynamicWriteFuture<'p> {}\n\nimpl<'p, M, const N: usize> From<WriteFuture<'p, M, N>> for DynamicWriteFuture<'p>\nwhere\n    M: RawMutex,\n{\n    fn from(value: WriteFuture<'p, M, N>) -> Self {\n        Self {\n            pipe: value.pipe,\n            buf: value.buf,\n        }\n    }\n}\n\n/// Read-only access to a dynamic pipe.\npub struct DynamicReader<'p> {\n    pipe: &'p dyn DynamicPipe,\n}\n\nimpl<'p> DynamicReader<'p> {\n    /// Read some bytes from the pipe.\n    ///\n    /// See [`Pipe::read()`]\n    pub fn read<'a>(&'a self, buf: &'a mut [u8]) -> DynamicReadFuture<'a> {\n        self.pipe.read(buf)\n    }\n\n    /// Attempt to immediately read some bytes from the pipe.\n    ///\n    /// See [`Pipe::try_read()`]\n    pub fn try_read(&self, buf: &mut [u8]) -> Result<usize, TryReadError> {\n        self.pipe.try_read(buf)\n    }\n\n    /// Return the contents of the internal buffer, filling it with more data from the inner reader if it is empty.\n    ///\n    /// If no bytes are currently available to read, this function waits until at least one byte is available.\n    ///\n    /// If the reader is at end-of-file (EOF), an empty slice is returned.\n    pub fn fill_buf(&mut self) -> DynamicFillBufFuture<'_> {\n        DynamicFillBufFuture { pipe: Some(self.pipe) }\n    }\n\n    /// Try returning contents of the internal buffer.\n    ///\n    /// If no bytes are currently available to read, this function returns `Err(TryReadError::Empty)`.\n    ///\n    /// If the reader is at end-of-file (EOF), an empty slice is returned.\n    pub fn try_fill_buf(&mut self) -> Result<&[u8], TryReadError> {\n        unsafe { self.pipe.try_fill_buf_with_context(None) }\n    }\n\n    /// Tell this buffer that `amt` bytes have been consumed from the buffer, so they should no longer be returned in calls to `fill_buf`.\n    pub fn consume(&mut self, amt: usize) {\n        self.pipe.consume(amt)\n    }\n}\n\nimpl<'p, M, const N: usize> From<Reader<'p, M, N>> for DynamicReader<'p>\nwhere\n    M: RawMutex,\n{\n    fn from(value: Reader<'p, M, N>) -> Self {\n        Self { pipe: value.pipe }\n    }\n}\n\n/// Future returned by [`Pipe::read`] and  [`Reader::read`].\n#[must_use = \"futures do nothing unless you `.await` or poll them\"]\npub struct DynamicReadFuture<'p> {\n    pipe: &'p dyn DynamicPipe,\n    buf: &'p mut [u8],\n}\n\nimpl<'p> Future for DynamicReadFuture<'p> {\n    type Output = usize;\n\n    fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {\n        match self.pipe.try_read_with_context(Some(cx), self.buf) {\n            Ok(n) => Poll::Ready(n),\n            Err(TryReadError::Empty) => Poll::Pending,\n        }\n    }\n}\n\nimpl<'p> Unpin for DynamicReadFuture<'p> {}\n\nimpl<'p, M, const N: usize> From<ReadFuture<'p, M, N>> for DynamicReadFuture<'p>\nwhere\n    M: RawMutex,\n{\n    fn from(value: ReadFuture<'p, M, N>) -> Self {\n        Self {\n            pipe: value.pipe,\n            buf: value.buf,\n        }\n    }\n}\n\n/// Future returned by [`DynamicReader::fill_buf`].\n#[must_use = \"futures do nothing unless you `.await` or poll them\"]\npub struct DynamicFillBufFuture<'p> {\n    pipe: Option<&'p dyn DynamicPipe>,\n}\n\nimpl<'p> Future for DynamicFillBufFuture<'p> {\n    type Output = &'p [u8];\n\n    fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {\n        let pipe = self.pipe.take().unwrap();\n        match unsafe { pipe.try_fill_buf_with_context(Some(cx)) } {\n            Ok(buf) => Poll::Ready(buf),\n            Err(TryReadError::Empty) => {\n                self.pipe = Some(pipe);\n                Poll::Pending\n            }\n        }\n    }\n}\n\nimpl<'p> Unpin for DynamicFillBufFuture<'p> {}\n\nimpl<'p, M, const N: usize> From<FillBufFuture<'p, M, N>> for DynamicFillBufFuture<'p>\nwhere\n    M: RawMutex,\n{\n    fn from(value: FillBufFuture<'p, M, N>) -> Self {\n        Self {\n            pipe: value.pipe.map(|p| p as &dyn DynamicPipe),\n        }\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use futures_executor::ThreadPool;\n    use futures_util::task::SpawnExt;\n    use static_cell::StaticCell;\n\n    use super::*;\n    use crate::blocking_mutex::raw::{CriticalSectionRawMutex, NoopRawMutex};\n\n    #[test]\n    fn writing_once() {\n        let c = Pipe::<NoopRawMutex, 3>::new();\n        assert!(c.try_write(&[1]).is_ok());\n        assert_eq!(c.free_capacity(), 2);\n    }\n\n    #[test]\n    fn writing_when_full() {\n        let c = Pipe::<NoopRawMutex, 3>::new();\n        assert_eq!(c.try_write(&[42]), Ok(1));\n        assert_eq!(c.try_write(&[43]), Ok(1));\n        assert_eq!(c.try_write(&[44]), Ok(1));\n        assert_eq!(c.try_write(&[45]), Err(TryWriteError::Full));\n        assert_eq!(c.free_capacity(), 0);\n    }\n\n    #[test]\n    fn receiving_once_with_one_send() {\n        let c = Pipe::<NoopRawMutex, 3>::new();\n        assert!(c.try_write(&[42]).is_ok());\n        let mut buf = [0; 16];\n        assert_eq!(c.try_read(&mut buf), Ok(1));\n        assert_eq!(buf[0], 42);\n        assert_eq!(c.free_capacity(), 3);\n    }\n\n    #[test]\n    fn receiving_when_empty() {\n        let c = Pipe::<NoopRawMutex, 3>::new();\n        let mut buf = [0; 16];\n        assert_eq!(c.try_read(&mut buf), Err(TryReadError::Empty));\n        assert_eq!(c.free_capacity(), 3);\n    }\n\n    #[test]\n    fn simple_send_and_receive() {\n        let c = Pipe::<NoopRawMutex, 3>::new();\n        assert!(c.try_write(&[42]).is_ok());\n        let mut buf = [0; 16];\n        assert_eq!(c.try_read(&mut buf), Ok(1));\n        assert_eq!(buf[0], 42);\n    }\n\n    #[test]\n    fn read_buf() {\n        let mut c = Pipe::<NoopRawMutex, 3>::new();\n        let (mut r, w) = c.split();\n        assert!(w.try_write(&[42, 43]).is_ok());\n        let buf = r.try_fill_buf().unwrap();\n        assert_eq!(buf, &[42, 43]);\n        let buf = r.try_fill_buf().unwrap();\n        assert_eq!(buf, &[42, 43]);\n        r.consume(1);\n        let buf = r.try_fill_buf().unwrap();\n        assert_eq!(buf, &[43]);\n        r.consume(1);\n        assert_eq!(r.try_fill_buf(), Err(TryReadError::Empty));\n        assert_eq!(w.try_write(&[44, 45, 46]), Ok(1));\n        assert_eq!(w.try_write(&[45, 46]), Ok(2));\n        let buf = r.try_fill_buf().unwrap();\n        assert_eq!(buf, &[44]); // only one byte due to wraparound.\n        r.consume(1);\n        let buf = r.try_fill_buf().unwrap();\n        assert_eq!(buf, &[45, 46]);\n        assert!(w.try_write(&[47]).is_ok());\n        let buf = r.try_fill_buf().unwrap();\n        assert_eq!(buf, &[45, 46, 47]);\n        r.consume(3);\n    }\n\n    #[test]\n    fn writer_is_cloneable() {\n        let mut c = Pipe::<NoopRawMutex, 3>::new();\n        let (_r, w) = c.split();\n        let _ = w.clone();\n    }\n\n    #[test]\n    fn dynamic_dispatch_pipe() {\n        let mut c = Pipe::<NoopRawMutex, 3>::new();\n        let (r, w) = c.split();\n        let (mut r, w): (DynamicReader<'_>, DynamicWriter<'_>) = (r.into(), w.into());\n\n        assert!(w.try_write(&[42, 43]).is_ok());\n        let buf = r.try_fill_buf().unwrap();\n        assert_eq!(buf, &[42, 43]);\n        let buf = r.try_fill_buf().unwrap();\n        assert_eq!(buf, &[42, 43]);\n        r.consume(1);\n        let buf = r.try_fill_buf().unwrap();\n        assert_eq!(buf, &[43]);\n        r.consume(1);\n        assert_eq!(r.try_fill_buf(), Err(TryReadError::Empty));\n        assert_eq!(w.try_write(&[44, 45, 46]), Ok(1));\n        assert_eq!(w.try_write(&[45, 46]), Ok(2));\n        let buf = r.try_fill_buf().unwrap();\n        assert_eq!(buf, &[44]); // only one byte due to wraparound.\n        r.consume(1);\n        let buf = r.try_fill_buf().unwrap();\n        assert_eq!(buf, &[45, 46]);\n        assert!(w.try_write(&[47]).is_ok());\n        let buf = r.try_fill_buf().unwrap();\n        assert_eq!(buf, &[45, 46, 47]);\n        r.consume(3);\n    }\n\n    #[futures_test::test]\n    async fn receiver_receives_given_try_write_async() {\n        let executor = ThreadPool::new().unwrap();\n\n        static CHANNEL: StaticCell<Pipe<CriticalSectionRawMutex, 3>> = StaticCell::new();\n        let c = &*CHANNEL.init(Pipe::new());\n        let c2 = c;\n        let f = async move {\n            assert_eq!(c2.try_write(&[42]), Ok(1));\n        };\n        executor.spawn(f).unwrap();\n        let mut buf = [0; 16];\n        assert_eq!(c.read(&mut buf).await, 1);\n        assert_eq!(buf[0], 42);\n    }\n\n    #[futures_test::test]\n    async fn sender_send_completes_if_capacity() {\n        let c = Pipe::<CriticalSectionRawMutex, 1>::new();\n        c.write(&[42]).await;\n        let mut buf = [0; 16];\n        assert_eq!(c.read(&mut buf).await, 1);\n        assert_eq!(buf[0], 42);\n    }\n}\n"
  },
  {
    "path": "embassy-sync/src/priority_channel.rs",
    "content": "//! A queue for sending values between asynchronous tasks.\n//!\n//! Similar to a [`Channel`](crate::channel::Channel), however [`PriorityChannel`] sifts higher priority items to the front of the queue.\n//! Priority is determined by the `Ord` trait. Priority behavior is determined by the [`Kind`] parameter of the channel.\n\nuse core::cell::RefCell;\nuse core::future::Future;\nuse core::pin::Pin;\nuse core::task::{Context, Poll};\n\nuse heapless::BinaryHeap;\npub use heapless::binary_heap::{Kind, Max, Min};\n\nuse crate::blocking_mutex::Mutex;\nuse crate::blocking_mutex::raw::RawMutex;\nuse crate::channel::{DynamicChannel, DynamicReceiver, DynamicSender, TryReceiveError, TrySendError};\nuse crate::waitqueue::WakerRegistration;\n\n/// Send-only access to a [`PriorityChannel`].\npub struct Sender<'ch, M, T, K, const N: usize>\nwhere\n    T: Ord,\n    K: Kind,\n    M: RawMutex,\n{\n    channel: &'ch PriorityChannel<M, T, K, N>,\n}\n\nimpl<'ch, M, T, K, const N: usize> Clone for Sender<'ch, M, T, K, N>\nwhere\n    T: Ord,\n    K: Kind,\n    M: RawMutex,\n{\n    fn clone(&self) -> Self {\n        *self\n    }\n}\n\nimpl<'ch, M, T, K, const N: usize> Copy for Sender<'ch, M, T, K, N>\nwhere\n    T: Ord,\n    K: Kind,\n    M: RawMutex,\n{\n}\n\nimpl<'ch, M, T, K, const N: usize> Sender<'ch, M, T, K, N>\nwhere\n    T: Ord,\n    K: Kind,\n    M: RawMutex,\n{\n    /// Sends a value.\n    ///\n    /// See [`PriorityChannel::send()`]\n    pub fn send(&self, message: T) -> SendFuture<'ch, M, T, K, N> {\n        self.channel.send(message)\n    }\n\n    /// Attempt to immediately send a message.\n    ///\n    /// See [`PriorityChannel::send()`]\n    pub fn try_send(&self, message: T) -> Result<(), TrySendError<T>> {\n        self.channel.try_send(message)\n    }\n\n    /// Allows a poll_fn to poll until the channel is ready to send\n    ///\n    /// See [`PriorityChannel::poll_ready_to_send()`]\n    pub fn poll_ready_to_send(&self, cx: &mut Context<'_>) -> Poll<()> {\n        self.channel.poll_ready_to_send(cx)\n    }\n\n    /// Returns the maximum number of elements the channel can hold.\n    ///\n    /// See [`PriorityChannel::capacity()`]\n    pub const fn capacity(&self) -> usize {\n        self.channel.capacity()\n    }\n\n    /// Returns the free capacity of the channel.\n    ///\n    /// See [`PriorityChannel::free_capacity()`]\n    pub fn free_capacity(&self) -> usize {\n        self.channel.free_capacity()\n    }\n\n    /// Clears all elements in the channel.\n    ///\n    /// See [`PriorityChannel::clear()`]\n    pub fn clear(&self) {\n        self.channel.clear();\n    }\n\n    /// Returns the number of elements currently in the channel.\n    ///\n    /// See [`PriorityChannel::len()`]\n    pub fn len(&self) -> usize {\n        self.channel.len()\n    }\n\n    /// Returns whether the channel is empty.\n    ///\n    /// See [`PriorityChannel::is_empty()`]\n    pub fn is_empty(&self) -> bool {\n        self.channel.is_empty()\n    }\n\n    /// Returns whether the channel is full.\n    ///\n    /// See [`PriorityChannel::is_full()`]\n    pub fn is_full(&self) -> bool {\n        self.channel.is_full()\n    }\n}\n\nimpl<'ch, M, T, K, const N: usize> From<Sender<'ch, M, T, K, N>> for DynamicSender<'ch, T>\nwhere\n    T: Ord,\n    K: Kind,\n    M: RawMutex,\n{\n    fn from(s: Sender<'ch, M, T, K, N>) -> Self {\n        Self { channel: s.channel }\n    }\n}\n\n/// Receive-only access to a [`PriorityChannel`].\npub struct Receiver<'ch, M, T, K, const N: usize>\nwhere\n    T: Ord,\n    K: Kind,\n    M: RawMutex,\n{\n    channel: &'ch PriorityChannel<M, T, K, N>,\n}\n\nimpl<'ch, M, T, K, const N: usize> Clone for Receiver<'ch, M, T, K, N>\nwhere\n    T: Ord,\n    K: Kind,\n    M: RawMutex,\n{\n    fn clone(&self) -> Self {\n        *self\n    }\n}\n\nimpl<'ch, M, T, K, const N: usize> Copy for Receiver<'ch, M, T, K, N>\nwhere\n    T: Ord,\n    K: Kind,\n    M: RawMutex,\n{\n}\n\nimpl<'ch, M, T, K, const N: usize> Receiver<'ch, M, T, K, N>\nwhere\n    T: Ord,\n    K: Kind,\n    M: RawMutex,\n{\n    /// Receive the next value.\n    ///\n    /// See [`PriorityChannel::receive()`].\n    pub fn receive(&self) -> ReceiveFuture<'_, M, T, K, N> {\n        self.channel.receive()\n    }\n\n    /// Attempt to immediately receive the next value.\n    ///\n    /// See [`PriorityChannel::try_receive()`]\n    pub fn try_receive(&self) -> Result<T, TryReceiveError> {\n        self.channel.try_receive()\n    }\n\n    /// Peek at the next value without removing it from the queue.\n    ///\n    /// See [`PriorityChannel::try_peek()`]\n    pub fn try_peek(&self) -> Result<T, TryReceiveError>\n    where\n        T: Clone,\n    {\n        self.channel.try_peek_with_context(None)\n    }\n\n    /// Allows a poll_fn to poll until the channel is ready to receive\n    ///\n    /// See [`PriorityChannel::poll_ready_to_receive()`]\n    pub fn poll_ready_to_receive(&self, cx: &mut Context<'_>) -> Poll<()> {\n        self.channel.poll_ready_to_receive(cx)\n    }\n\n    /// Poll the channel for the next item\n    ///\n    /// See [`PriorityChannel::poll_receive()`]\n    pub fn poll_receive(&self, cx: &mut Context<'_>) -> Poll<T> {\n        self.channel.poll_receive(cx)\n    }\n\n    /// Removes the elements from the channel that satisfy the predicate.\n    ///\n    /// See [`PriorityChannel::remove_if()`]\n    pub fn remove_if<F>(&self, predicate: F)\n    where\n        F: Fn(&T) -> bool,\n        T: Clone,\n    {\n        self.channel.remove_if(predicate)\n    }\n\n    /// Returns the maximum number of elements the channel can hold.\n    ///\n    /// See [`PriorityChannel::capacity()`]\n    pub const fn capacity(&self) -> usize {\n        self.channel.capacity()\n    }\n\n    /// Returns the free capacity of the channel.\n    ///\n    /// See [`PriorityChannel::free_capacity()`]\n    pub fn free_capacity(&self) -> usize {\n        self.channel.free_capacity()\n    }\n\n    /// Clears all elements in the channel.\n    ///\n    /// See [`PriorityChannel::clear()`]\n    pub fn clear(&self) {\n        self.channel.clear();\n    }\n\n    /// Returns the number of elements currently in the channel.\n    ///\n    /// See [`PriorityChannel::len()`]\n    pub fn len(&self) -> usize {\n        self.channel.len()\n    }\n\n    /// Returns whether the channel is empty.\n    ///\n    /// See [`PriorityChannel::is_empty()`]\n    pub fn is_empty(&self) -> bool {\n        self.channel.is_empty()\n    }\n\n    /// Returns whether the channel is full.\n    ///\n    /// See [`PriorityChannel::is_full()`]\n    pub fn is_full(&self) -> bool {\n        self.channel.is_full()\n    }\n}\n\nimpl<'ch, M, T, K, const N: usize> From<Receiver<'ch, M, T, K, N>> for DynamicReceiver<'ch, T>\nwhere\n    T: Ord,\n    K: Kind,\n    M: RawMutex,\n{\n    fn from(s: Receiver<'ch, M, T, K, N>) -> Self {\n        Self { channel: s.channel }\n    }\n}\n\n/// Future returned by [`PriorityChannel::receive`] and  [`Receiver::receive`].\n#[must_use = \"futures do nothing unless you `.await` or poll them\"]\npub struct ReceiveFuture<'ch, M, T, K, const N: usize>\nwhere\n    T: Ord,\n    K: Kind,\n    M: RawMutex,\n{\n    channel: &'ch PriorityChannel<M, T, K, N>,\n}\n\nimpl<'ch, M, T, K, const N: usize> Future for ReceiveFuture<'ch, M, T, K, N>\nwhere\n    T: Ord,\n    K: Kind,\n    M: RawMutex,\n{\n    type Output = T;\n\n    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<T> {\n        self.channel.poll_receive(cx)\n    }\n}\n\n/// Future returned by [`PriorityChannel::send`] and  [`Sender::send`].\n#[must_use = \"futures do nothing unless you `.await` or poll them\"]\npub struct SendFuture<'ch, M, T, K, const N: usize>\nwhere\n    T: Ord,\n    K: Kind,\n    M: RawMutex,\n{\n    channel: &'ch PriorityChannel<M, T, K, N>,\n    message: Option<T>,\n}\n\nimpl<'ch, M, T, K, const N: usize> Future for SendFuture<'ch, M, T, K, N>\nwhere\n    T: Ord,\n    K: Kind,\n    M: RawMutex,\n{\n    type Output = ();\n\n    fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {\n        match self.message.take() {\n            Some(m) => match self.channel.try_send_with_context(m, Some(cx)) {\n                Ok(..) => Poll::Ready(()),\n                Err(TrySendError::Full(m)) => {\n                    self.message = Some(m);\n                    Poll::Pending\n                }\n            },\n            None => panic!(\"Message cannot be None\"),\n        }\n    }\n}\n\nimpl<'ch, M, T, K, const N: usize> Unpin for SendFuture<'ch, M, T, K, N>\nwhere\n    T: Ord,\n    K: Kind,\n    M: RawMutex,\n{\n}\n\nstruct ChannelState<T, K, const N: usize> {\n    queue: BinaryHeap<T, K, N>,\n    receiver_waker: WakerRegistration,\n    senders_waker: WakerRegistration,\n}\n\nimpl<T, K, const N: usize> ChannelState<T, K, N>\nwhere\n    T: Ord,\n    K: Kind,\n{\n    const fn new() -> Self {\n        ChannelState {\n            queue: BinaryHeap::new(),\n            receiver_waker: WakerRegistration::new(),\n            senders_waker: WakerRegistration::new(),\n        }\n    }\n\n    fn try_receive(&mut self) -> Result<T, TryReceiveError> {\n        self.try_receive_with_context(None)\n    }\n\n    fn try_peek(&mut self) -> Result<T, TryReceiveError>\n    where\n        T: Clone,\n    {\n        self.try_peek_with_context(None)\n    }\n\n    fn try_peek_with_context(&mut self, cx: Option<&mut Context<'_>>) -> Result<T, TryReceiveError>\n    where\n        T: Clone,\n    {\n        if self.queue.len() == self.queue.capacity() {\n            self.senders_waker.wake();\n        }\n\n        if let Some(message) = self.queue.peek() {\n            Ok(message.clone())\n        } else {\n            if let Some(cx) = cx {\n                self.receiver_waker.register(cx.waker());\n            }\n            Err(TryReceiveError::Empty)\n        }\n    }\n\n    fn try_receive_with_context(&mut self, cx: Option<&mut Context<'_>>) -> Result<T, TryReceiveError> {\n        if self.queue.len() == self.queue.capacity() {\n            self.senders_waker.wake();\n        }\n\n        if let Some(message) = self.queue.pop() {\n            Ok(message)\n        } else {\n            if let Some(cx) = cx {\n                self.receiver_waker.register(cx.waker());\n            }\n            Err(TryReceiveError::Empty)\n        }\n    }\n\n    fn poll_receive(&mut self, cx: &mut Context<'_>) -> Poll<T> {\n        if self.queue.len() == self.queue.capacity() {\n            self.senders_waker.wake();\n        }\n\n        if let Some(message) = self.queue.pop() {\n            Poll::Ready(message)\n        } else {\n            self.receiver_waker.register(cx.waker());\n            Poll::Pending\n        }\n    }\n\n    fn poll_ready_to_receive(&mut self, cx: &mut Context<'_>) -> Poll<()> {\n        self.receiver_waker.register(cx.waker());\n\n        if !self.queue.is_empty() {\n            Poll::Ready(())\n        } else {\n            Poll::Pending\n        }\n    }\n\n    fn try_send(&mut self, message: T) -> Result<(), TrySendError<T>> {\n        self.try_send_with_context(message, None)\n    }\n\n    fn try_send_with_context(&mut self, message: T, cx: Option<&mut Context<'_>>) -> Result<(), TrySendError<T>> {\n        match self.queue.push(message) {\n            Ok(()) => {\n                self.receiver_waker.wake();\n                Ok(())\n            }\n            Err(message) => {\n                if let Some(cx) = cx {\n                    self.senders_waker.register(cx.waker());\n                }\n                Err(TrySendError::Full(message))\n            }\n        }\n    }\n\n    fn poll_ready_to_send(&mut self, cx: &mut Context<'_>) -> Poll<()> {\n        self.senders_waker.register(cx.waker());\n\n        if !self.queue.len() == self.queue.capacity() {\n            Poll::Ready(())\n        } else {\n            Poll::Pending\n        }\n    }\n\n    fn clear(&mut self) {\n        if self.queue.len() == self.queue.capacity() {\n            self.senders_waker.wake();\n        }\n        self.queue.clear();\n    }\n\n    fn len(&self) -> usize {\n        self.queue.len()\n    }\n\n    fn is_empty(&self) -> bool {\n        self.queue.is_empty()\n    }\n\n    fn is_full(&self) -> bool {\n        self.queue.len() == self.queue.capacity()\n    }\n}\n\n/// A bounded channel for communicating between asynchronous tasks\n/// with backpressure.\n///\n/// The channel will buffer up to the provided number of messages.  Once the\n/// buffer is full, attempts to `send` new messages will wait until a message is\n/// received from the channel.\n///\n/// Sent data may be reordered based on their priority within the channel.\n/// For example, in a [`Max`] [`PriorityChannel`]\n/// containing `u32`'s, data sent in the following order `[1, 2, 3]` will be received as `[3, 2, 1]`.\npub struct PriorityChannel<M, T, K, const N: usize>\nwhere\n    T: Ord,\n    K: Kind,\n    M: RawMutex,\n{\n    inner: Mutex<M, RefCell<ChannelState<T, K, N>>>,\n}\n\nimpl<M, T, K, const N: usize> PriorityChannel<M, T, K, N>\nwhere\n    T: Ord,\n    K: Kind,\n    M: RawMutex,\n{\n    /// Establish a new bounded channel. For example, to create one with a NoopMutex:\n    ///\n    /// ```\n    /// use embassy_sync::priority_channel::{PriorityChannel, Max};\n    /// use embassy_sync::blocking_mutex::raw::NoopRawMutex;\n    ///\n    /// // Declare a bounded channel of 3 u32s.\n    /// let mut channel = PriorityChannel::<NoopRawMutex, u32, Max, 3>::new();\n    /// ```\n    pub const fn new() -> Self {\n        Self {\n            inner: Mutex::new(RefCell::new(ChannelState::new())),\n        }\n    }\n\n    fn lock<R>(&self, f: impl FnOnce(&mut ChannelState<T, K, N>) -> R) -> R {\n        self.inner.lock(|rc| f(&mut *unwrap!(rc.try_borrow_mut())))\n    }\n\n    fn try_receive_with_context(&self, cx: Option<&mut Context<'_>>) -> Result<T, TryReceiveError> {\n        self.lock(|c| c.try_receive_with_context(cx))\n    }\n\n    fn try_peek_with_context(&self, cx: Option<&mut Context<'_>>) -> Result<T, TryReceiveError>\n    where\n        T: Clone,\n    {\n        self.lock(|c| c.try_peek_with_context(cx))\n    }\n\n    /// Poll the channel for the next message\n    pub fn poll_receive(&self, cx: &mut Context<'_>) -> Poll<T> {\n        self.lock(|c| c.poll_receive(cx))\n    }\n\n    fn try_send_with_context(&self, m: T, cx: Option<&mut Context<'_>>) -> Result<(), TrySendError<T>> {\n        self.lock(|c| c.try_send_with_context(m, cx))\n    }\n\n    /// Allows a poll_fn to poll until the channel is ready to receive\n    pub fn poll_ready_to_receive(&self, cx: &mut Context<'_>) -> Poll<()> {\n        self.lock(|c| c.poll_ready_to_receive(cx))\n    }\n\n    /// Allows a poll_fn to poll until the channel is ready to send\n    pub fn poll_ready_to_send(&self, cx: &mut Context<'_>) -> Poll<()> {\n        self.lock(|c| c.poll_ready_to_send(cx))\n    }\n\n    /// Get a sender for this channel.\n    pub fn sender(&self) -> Sender<'_, M, T, K, N> {\n        Sender { channel: self }\n    }\n\n    /// Get a receiver for this channel.\n    pub fn receiver(&self) -> Receiver<'_, M, T, K, N> {\n        Receiver { channel: self }\n    }\n\n    /// Send a value, waiting until there is capacity.\n    ///\n    /// Sending completes when the value has been pushed to the channel's queue.\n    /// This doesn't mean the value has been received yet.\n    pub fn send(&self, message: T) -> SendFuture<'_, M, T, K, N> {\n        SendFuture {\n            channel: self,\n            message: Some(message),\n        }\n    }\n\n    /// Attempt to immediately send a message.\n    ///\n    /// This method differs from [`send`](PriorityChannel::send) by returning immediately if the channel's\n    /// buffer is full, instead of waiting.\n    ///\n    /// # Errors\n    ///\n    /// If the channel capacity has been reached, i.e., the channel has `n`\n    /// buffered values where `n` is the argument passed to [`PriorityChannel`], then an\n    /// error is returned.\n    pub fn try_send(&self, message: T) -> Result<(), TrySendError<T>> {\n        self.lock(|c| c.try_send(message))\n    }\n\n    /// Receive the next value.\n    ///\n    /// If there are no messages in the channel's buffer, this method will\n    /// wait until a message is sent.\n    pub fn receive(&self) -> ReceiveFuture<'_, M, T, K, N> {\n        ReceiveFuture { channel: self }\n    }\n\n    /// Attempt to immediately receive a message.\n    ///\n    /// This method will either receive a message from the channel immediately or return an error\n    /// if the channel is empty.\n    pub fn try_receive(&self) -> Result<T, TryReceiveError> {\n        self.lock(|c| c.try_receive())\n    }\n\n    /// Peek at the next value without removing it from the queue.\n    ///\n    /// This method will either receive a copy of the message from the channel immediately or return\n    /// an error if the channel is empty.\n    pub fn try_peek(&self) -> Result<T, TryReceiveError>\n    where\n        T: Clone,\n    {\n        self.lock(|c| c.try_peek())\n    }\n\n    /// Removes elements from the channel based on the given predicate.\n    pub fn remove_if<F>(&self, predicate: F)\n    where\n        F: Fn(&T) -> bool,\n        T: Clone,\n    {\n        self.lock(|c| {\n            let mut new_heap = BinaryHeap::<T, K, N>::new();\n            for item in c.queue.iter() {\n                if !predicate(item) {\n                    match new_heap.push(item.clone()) {\n                        Ok(_) => (),\n                        Err(_) => panic!(\"Error pushing item to heap\"),\n                    }\n                }\n            }\n            c.queue = new_heap;\n        });\n    }\n\n    /// Returns the maximum number of elements the channel can hold.\n    pub const fn capacity(&self) -> usize {\n        N\n    }\n\n    /// Returns the free capacity of the channel.\n    ///\n    /// This is equivalent to `capacity() - len()`\n    pub fn free_capacity(&self) -> usize {\n        N - self.len()\n    }\n\n    /// Clears all elements in the channel.\n    pub fn clear(&self) {\n        self.lock(|c| c.clear());\n    }\n\n    /// Returns the number of elements currently in the channel.\n    pub fn len(&self) -> usize {\n        self.lock(|c| c.len())\n    }\n\n    /// Returns whether the channel is empty.\n    pub fn is_empty(&self) -> bool {\n        self.lock(|c| c.is_empty())\n    }\n\n    /// Returns whether the channel is full.\n    pub fn is_full(&self) -> bool {\n        self.lock(|c| c.is_full())\n    }\n}\n\n/// Implements the DynamicChannel to allow creating types that are unaware of the queue size with the\n/// tradeoff cost of dynamic dispatch.\nimpl<M, T, K, const N: usize> DynamicChannel<T> for PriorityChannel<M, T, K, N>\nwhere\n    T: Ord,\n    K: Kind,\n    M: RawMutex,\n{\n    fn try_send_with_context(&self, m: T, cx: Option<&mut Context<'_>>) -> Result<(), TrySendError<T>> {\n        PriorityChannel::try_send_with_context(self, m, cx)\n    }\n\n    fn try_receive_with_context(&self, cx: Option<&mut Context<'_>>) -> Result<T, TryReceiveError> {\n        PriorityChannel::try_receive_with_context(self, cx)\n    }\n\n    fn try_peek_with_context(&self, cx: Option<&mut Context<'_>>) -> Result<T, TryReceiveError>\n    where\n        T: Clone,\n    {\n        PriorityChannel::try_peek_with_context(self, cx)\n    }\n\n    fn poll_ready_to_send(&self, cx: &mut Context<'_>) -> Poll<()> {\n        PriorityChannel::poll_ready_to_send(self, cx)\n    }\n\n    fn poll_ready_to_receive(&self, cx: &mut Context<'_>) -> Poll<()> {\n        PriorityChannel::poll_ready_to_receive(self, cx)\n    }\n\n    fn poll_receive(&self, cx: &mut Context<'_>) -> Poll<T> {\n        PriorityChannel::poll_receive(self, cx)\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use core::time::Duration;\n\n    use futures_executor::ThreadPool;\n    use futures_timer::Delay;\n    use futures_util::task::SpawnExt;\n    use heapless::binary_heap::{Kind, Max};\n    use static_cell::StaticCell;\n\n    use super::*;\n    use crate::blocking_mutex::raw::{CriticalSectionRawMutex, NoopRawMutex};\n\n    fn capacity<T, K, const N: usize>(c: &ChannelState<T, K, N>) -> usize\n    where\n        T: Ord,\n        K: Kind,\n    {\n        c.queue.capacity() - c.queue.len()\n    }\n\n    #[test]\n    fn sending_once() {\n        let mut c = ChannelState::<u32, Max, 3>::new();\n        assert!(c.try_send(1).is_ok());\n        assert_eq!(capacity(&c), 2);\n    }\n\n    #[test]\n    fn sending_when_full() {\n        let mut c = ChannelState::<u32, Max, 3>::new();\n        let _ = c.try_send(1);\n        let _ = c.try_send(1);\n        let _ = c.try_send(1);\n        match c.try_send(2) {\n            Err(TrySendError::Full(2)) => assert!(true),\n            _ => assert!(false),\n        }\n        assert_eq!(capacity(&c), 0);\n    }\n\n    #[test]\n    fn send_priority() {\n        // Prio channel with kind `Max` sifts larger numbers to the front of the queue\n        let mut c = ChannelState::<u32, Max, 3>::new();\n        assert!(c.try_send(1).is_ok());\n        assert!(c.try_send(2).is_ok());\n        assert!(c.try_send(3).is_ok());\n        assert_eq!(c.try_receive().unwrap(), 3);\n        assert_eq!(c.try_receive().unwrap(), 2);\n        assert_eq!(c.try_receive().unwrap(), 1);\n    }\n\n    #[test]\n    fn receiving_once_with_one_send() {\n        let mut c = ChannelState::<u32, Max, 3>::new();\n        assert!(c.try_send(1).is_ok());\n        assert_eq!(c.try_receive().unwrap(), 1);\n        assert_eq!(capacity(&c), 3);\n    }\n\n    #[test]\n    fn receiving_when_empty() {\n        let mut c = ChannelState::<u32, Max, 3>::new();\n        match c.try_receive() {\n            Err(TryReceiveError::Empty) => assert!(true),\n            _ => assert!(false),\n        }\n        assert_eq!(capacity(&c), 3);\n    }\n\n    #[test]\n    fn simple_send_and_receive() {\n        let c = PriorityChannel::<NoopRawMutex, u32, Max, 3>::new();\n        assert!(c.try_send(1).is_ok());\n        assert_eq!(c.try_peek().unwrap(), 1);\n        assert_eq!(c.try_peek().unwrap(), 1);\n        assert_eq!(c.try_receive().unwrap(), 1);\n    }\n\n    #[test]\n    fn cloning() {\n        let c = PriorityChannel::<NoopRawMutex, u32, Max, 3>::new();\n        let r1 = c.receiver();\n        let s1 = c.sender();\n\n        let _ = r1.clone();\n        let _ = s1.clone();\n    }\n\n    #[test]\n    fn dynamic_dispatch() {\n        let c = PriorityChannel::<NoopRawMutex, u32, Max, 3>::new();\n        let s: DynamicSender<'_, u32> = c.sender().into();\n        let r: DynamicReceiver<'_, u32> = c.receiver().into();\n\n        assert!(s.try_send(1).is_ok());\n        assert_eq!(r.try_peek().unwrap(), 1);\n        assert_eq!(r.try_peek().unwrap(), 1);\n        assert_eq!(r.try_receive().unwrap(), 1);\n    }\n\n    #[futures_test::test]\n    async fn receiver_receives_given_try_send_async() {\n        let executor = ThreadPool::new().unwrap();\n\n        static CHANNEL: StaticCell<PriorityChannel<CriticalSectionRawMutex, u32, Max, 3>> = StaticCell::new();\n        let c = &*CHANNEL.init(PriorityChannel::new());\n        let c2 = c;\n        assert!(\n            executor\n                .spawn(async move {\n                    assert!(c2.try_send(1).is_ok());\n                })\n                .is_ok()\n        );\n        assert_eq!(c.receive().await, 1);\n    }\n\n    #[futures_test::test]\n    async fn sender_send_completes_if_capacity() {\n        let c = PriorityChannel::<CriticalSectionRawMutex, u32, Max, 1>::new();\n        c.send(1).await;\n        assert_eq!(c.receive().await, 1);\n    }\n\n    #[futures_test::test]\n    async fn senders_sends_wait_until_capacity() {\n        let executor = ThreadPool::new().unwrap();\n\n        static CHANNEL: StaticCell<PriorityChannel<CriticalSectionRawMutex, u32, Max, 1>> = StaticCell::new();\n        let c = &*CHANNEL.init(PriorityChannel::new());\n        assert!(c.try_send(1).is_ok());\n\n        let c2 = c;\n        let send_task_1 = executor.spawn_with_handle(async move { c2.send(2).await });\n        let c2 = c;\n        let send_task_2 = executor.spawn_with_handle(async move { c2.send(3).await });\n        // Wish I could think of a means of determining that the async send is waiting instead.\n        // However, I've used the debugger to observe that the send does indeed wait.\n        Delay::new(Duration::from_millis(500)).await;\n        assert_eq!(c.receive().await, 1);\n        assert!(\n            executor\n                .spawn(async move {\n                    loop {\n                        c.receive().await;\n                    }\n                })\n                .is_ok()\n        );\n        send_task_1.unwrap().await;\n        send_task_2.unwrap().await;\n    }\n}\n"
  },
  {
    "path": "embassy-sync/src/pubsub/mod.rs",
    "content": "//! Implementation of [PubSubChannel], a queue where published messages get received by all subscribers.\n\n#![deny(missing_docs)]\n\nuse core::cell::RefCell;\nuse core::fmt::Debug;\nuse core::task::{Context, Poll};\n\nuse heapless::Deque;\n\nuse self::publisher::{ImmediatePub, Pub};\nuse self::subscriber::Sub;\nuse crate::blocking_mutex::Mutex;\nuse crate::blocking_mutex::raw::RawMutex;\nuse crate::waitqueue::MultiWakerRegistration;\n\npub mod publisher;\npub mod subscriber;\n\npub use publisher::{DynImmediatePublisher, DynPublisher, ImmediatePublisher, Publisher};\npub use subscriber::{DynSubscriber, Subscriber};\n\n/// A broadcast channel implementation where multiple publishers can send messages to multiple subscribers\n///\n/// Any published message can be read by all subscribers.\n/// A publisher can choose how it sends its message.\n///\n/// - With [Pub::publish()] the publisher has to wait until there is space in the internal message queue.\n/// - With [Pub::publish_immediate()] the publisher doesn't await and instead lets the oldest message\n///   in the queue drop if necessary. This will cause any [Subscriber] that missed the message to receive\n///   an error to indicate that it has lagged.\n///\n/// ## Example\n///\n/// ```\n/// # use embassy_sync::blocking_mutex::raw::NoopRawMutex;\n/// # use embassy_sync::pubsub::WaitResult;\n/// # use embassy_sync::pubsub::PubSubChannel;\n/// # use futures_executor::block_on;\n/// # let test = async {\n/// // Create the channel. This can be static as well\n/// let channel = PubSubChannel::<NoopRawMutex, u32, 4, 4, 4>::new();\n///\n/// // This is a generic subscriber with a direct reference to the channel\n/// let mut sub0 = channel.subscriber().unwrap();\n/// // This is a dynamic subscriber with a dynamic (trait object) reference to the channel\n/// let mut sub1 = channel.dyn_subscriber().unwrap();\n///\n/// let pub0 = channel.publisher().unwrap();\n///\n/// // Publish a message, but wait if the queue is full\n/// pub0.publish(42).await;\n///\n/// // Publish a message, but if the queue is full, just kick out the oldest message.\n/// // This may cause some subscribers to miss a message\n/// pub0.publish_immediate(43);\n///\n/// // Wait for a new message. If the subscriber missed a message, the WaitResult will be a Lag result\n/// assert_eq!(sub0.next_message().await, WaitResult::Message(42));\n/// assert_eq!(sub1.next_message().await, WaitResult::Message(42));\n///\n/// // Wait again, but this time ignore any Lag results\n/// assert_eq!(sub0.next_message_pure().await, 43);\n/// assert_eq!(sub1.next_message_pure().await, 43);\n///\n/// // There's also a polling interface\n/// assert_eq!(sub0.try_next_message(), None);\n/// assert_eq!(sub1.try_next_message(), None);\n/// # };\n/// #\n/// # block_on(test);\n/// ```\n///\n#[derive(Debug)]\npub struct PubSubChannel<M: RawMutex, T: Clone, const CAP: usize, const SUBS: usize, const PUBS: usize> {\n    inner: Mutex<M, RefCell<PubSubState<T, CAP, SUBS, PUBS>>>,\n}\n\nimpl<M: RawMutex, T: Clone, const CAP: usize, const SUBS: usize, const PUBS: usize>\n    PubSubChannel<M, T, CAP, SUBS, PUBS>\n{\n    /// Create a new channel\n    pub const fn new() -> Self {\n        Self {\n            inner: Mutex::const_new(M::INIT, RefCell::new(PubSubState::new())),\n        }\n    }\n\n    /// Create a new subscriber. It will only receive messages that are published after its creation.\n    ///\n    /// If there are no subscriber slots left, an error will be returned.\n    pub fn subscriber(&self) -> Result<Subscriber<'_, M, T, CAP, SUBS, PUBS>, Error> {\n        self.inner.lock(|inner| {\n            let mut s = inner.borrow_mut();\n\n            if s.subscriber_count >= SUBS {\n                Err(Error::MaximumSubscribersReached)\n            } else {\n                s.subscriber_count += 1;\n                Ok(Subscriber(Sub::new(s.next_message_id, self)))\n            }\n        })\n    }\n\n    /// Create a new subscriber. It will only receive messages that are published after its creation.\n    ///\n    /// If there are no subscriber slots left, an error will be returned.\n    pub fn dyn_subscriber(&self) -> Result<DynSubscriber<'_, T>, Error> {\n        self.inner.lock(|inner| {\n            let mut s = inner.borrow_mut();\n\n            if s.subscriber_count >= SUBS {\n                Err(Error::MaximumSubscribersReached)\n            } else {\n                s.subscriber_count += 1;\n                Ok(DynSubscriber(Sub::new(s.next_message_id, self)))\n            }\n        })\n    }\n\n    /// Create a new publisher\n    ///\n    /// If there are no publisher slots left, an error will be returned.\n    pub fn publisher(&self) -> Result<Publisher<'_, M, T, CAP, SUBS, PUBS>, Error> {\n        self.inner.lock(|inner| {\n            let mut s = inner.borrow_mut();\n\n            if s.publisher_count >= PUBS {\n                Err(Error::MaximumPublishersReached)\n            } else {\n                s.publisher_count += 1;\n                Ok(Publisher(Pub::new(self)))\n            }\n        })\n    }\n\n    /// Create a new publisher\n    ///\n    /// If there are no publisher slots left, an error will be returned.\n    pub fn dyn_publisher(&self) -> Result<DynPublisher<'_, T>, Error> {\n        self.inner.lock(|inner| {\n            let mut s = inner.borrow_mut();\n\n            if s.publisher_count >= PUBS {\n                Err(Error::MaximumPublishersReached)\n            } else {\n                s.publisher_count += 1;\n                Ok(DynPublisher(Pub::new(self)))\n            }\n        })\n    }\n\n    /// Create a new publisher that can only send immediate messages.\n    /// This kind of publisher does not take up a publisher slot.\n    pub fn immediate_publisher(&self) -> ImmediatePublisher<'_, M, T, CAP, SUBS, PUBS> {\n        ImmediatePublisher(ImmediatePub::new(self))\n    }\n\n    /// Create a new publisher that can only send immediate messages.\n    /// This kind of publisher does not take up a publisher slot.\n    pub fn dyn_immediate_publisher(&self) -> DynImmediatePublisher<'_, T> {\n        DynImmediatePublisher(ImmediatePub::new(self))\n    }\n\n    /// Returns the maximum number of elements the channel can hold.\n    pub const fn capacity(&self) -> usize {\n        CAP\n    }\n\n    /// Returns the free capacity of the channel.\n    ///\n    /// This is equivalent to `capacity() - len()`\n    pub fn free_capacity(&self) -> usize {\n        CAP - self.len()\n    }\n\n    /// Clears all elements in the channel.\n    pub fn clear(&self) {\n        self.inner.lock(|inner| inner.borrow_mut().clear());\n    }\n\n    /// Returns the number of elements currently in the channel.\n    pub fn len(&self) -> usize {\n        self.inner.lock(|inner| inner.borrow().len())\n    }\n\n    /// Returns whether the channel is empty.\n    pub fn is_empty(&self) -> bool {\n        self.inner.lock(|inner| inner.borrow().is_empty())\n    }\n\n    /// Returns whether the channel is full.\n    pub fn is_full(&self) -> bool {\n        self.inner.lock(|inner| inner.borrow().is_full())\n    }\n}\n\nimpl<M: RawMutex, T: Clone, const CAP: usize, const SUBS: usize, const PUBS: usize> crate::pubsub::PubSubBehavior<T>\n    for PubSubChannel<M, T, CAP, SUBS, PUBS>\n{\n    fn publish_immediate(&self, message: T) {\n        self.inner.lock(|s| {\n            let mut s = s.borrow_mut();\n            s.publish_immediate(message)\n        })\n    }\n\n    fn capacity(&self) -> usize {\n        self.capacity()\n    }\n\n    fn is_full(&self) -> bool {\n        self.is_full()\n    }\n}\n\nimpl<M: RawMutex, T: Clone, const CAP: usize, const SUBS: usize, const PUBS: usize> SealedPubSubBehavior<T>\n    for PubSubChannel<M, T, CAP, SUBS, PUBS>\n{\n    fn get_message_with_context(&self, next_message_id: &mut u64, cx: Option<&mut Context<'_>>) -> Poll<WaitResult<T>> {\n        self.inner.lock(|s| {\n            let mut s = s.borrow_mut();\n\n            // Check if we can read a message\n            match s.get_message(*next_message_id) {\n                // Yes, so we are done polling\n                Some(WaitResult::Message(message)) => {\n                    *next_message_id += 1;\n                    Poll::Ready(WaitResult::Message(message))\n                }\n                // No, so we need to reregister our waker and sleep again\n                None => {\n                    if let Some(cx) = cx {\n                        s.subscriber_wakers.register(cx.waker());\n                    }\n                    Poll::Pending\n                }\n                // We missed a couple of messages. We must do our internal bookkeeping and return that we lagged\n                Some(WaitResult::Lagged(amount)) => {\n                    *next_message_id += amount;\n                    Poll::Ready(WaitResult::Lagged(amount))\n                }\n            }\n        })\n    }\n\n    fn available(&self, next_message_id: u64) -> u64 {\n        self.inner.lock(|s| s.borrow().next_message_id - next_message_id)\n    }\n\n    fn publish_with_context(&self, message: T, cx: Option<&mut Context<'_>>) -> Result<(), T> {\n        self.inner.lock(|s| {\n            let mut s = s.borrow_mut();\n            // Try to publish the message\n            match s.try_publish(message) {\n                // We did it, we are ready\n                Ok(()) => Ok(()),\n                // The queue is full, so we need to reregister our waker and go to sleep\n                Err(message) => {\n                    if let Some(cx) = cx {\n                        s.publisher_wakers.register(cx.waker());\n                    }\n                    Err(message)\n                }\n            }\n        })\n    }\n\n    fn unregister_subscriber(&self, subscriber_next_message_id: u64) {\n        self.inner.lock(|s| {\n            let mut s = s.borrow_mut();\n            s.unregister_subscriber(subscriber_next_message_id)\n        })\n    }\n\n    fn unregister_publisher(&self) {\n        self.inner.lock(|s| {\n            let mut s = s.borrow_mut();\n            s.unregister_publisher()\n        })\n    }\n\n    fn free_capacity(&self) -> usize {\n        self.free_capacity()\n    }\n\n    fn clear(&self) {\n        self.clear();\n    }\n\n    fn len(&self) -> usize {\n        self.len()\n    }\n\n    fn is_empty(&self) -> bool {\n        self.is_empty()\n    }\n}\n\n/// Internal state for the PubSub channel\n#[derive(Debug)]\nstruct PubSubState<T: Clone, const CAP: usize, const SUBS: usize, const PUBS: usize> {\n    /// The queue contains the last messages that have been published and a countdown of how many subscribers are yet to read it\n    queue: Deque<(T, usize), CAP>,\n    /// Every message has an id.\n    /// Don't worry, we won't run out.\n    /// If a million messages were published every second, then the ID's would run out in about 584942 years.\n    next_message_id: u64,\n    /// Collection of wakers for Subscribers that are waiting.  \n    subscriber_wakers: MultiWakerRegistration<SUBS>,\n    /// Collection of wakers for Publishers that are waiting.  \n    publisher_wakers: MultiWakerRegistration<PUBS>,\n    /// The amount of subscribers that are active\n    subscriber_count: usize,\n    /// The amount of publishers that are active\n    publisher_count: usize,\n}\n\nimpl<T: Clone, const CAP: usize, const SUBS: usize, const PUBS: usize> PubSubState<T, CAP, SUBS, PUBS> {\n    /// Create a new internal channel state\n    const fn new() -> Self {\n        Self {\n            queue: Deque::new(),\n            next_message_id: 0,\n            subscriber_wakers: MultiWakerRegistration::new(),\n            publisher_wakers: MultiWakerRegistration::new(),\n            subscriber_count: 0,\n            publisher_count: 0,\n        }\n    }\n\n    fn try_publish(&mut self, message: T) -> Result<(), T> {\n        if self.subscriber_count == 0 {\n            // We don't need to publish anything because there is no one to receive it\n            return Ok(());\n        }\n\n        if self.queue.is_full() {\n            return Err(message);\n        }\n        // We just did a check for this\n        self.queue.push_back((message, self.subscriber_count)).ok().unwrap();\n\n        self.next_message_id += 1;\n\n        // Wake all of the subscribers\n        self.subscriber_wakers.wake();\n\n        Ok(())\n    }\n\n    fn publish_immediate(&mut self, message: T) {\n        // Make space in the queue if required\n        if self.queue.is_full() {\n            self.queue.pop_front();\n        }\n\n        // This will succeed because we made sure there is space\n        self.try_publish(message).ok().unwrap();\n    }\n\n    fn get_message(&mut self, message_id: u64) -> Option<WaitResult<T>> {\n        let start_id = self.next_message_id - self.queue.len() as u64;\n\n        if message_id < start_id {\n            return Some(WaitResult::Lagged(start_id - message_id));\n        }\n\n        let current_message_index = (message_id - start_id) as usize;\n\n        if current_message_index >= self.queue.len() {\n            return None;\n        }\n\n        // We've checked that the index is valid\n        let queue_item = self.queue.iter_mut().nth(current_message_index).unwrap();\n\n        // We're reading this item, so decrement the counter\n        queue_item.1 -= 1;\n\n        let message = if current_message_index == 0 && queue_item.1 == 0 {\n            let (message, _) = self.queue.pop_front().unwrap();\n            self.publisher_wakers.wake();\n            // Return pop'd message without clone\n            message\n        } else {\n            queue_item.0.clone()\n        };\n\n        Some(WaitResult::Message(message))\n    }\n\n    fn unregister_subscriber(&mut self, subscriber_next_message_id: u64) {\n        self.subscriber_count -= 1;\n\n        // All messages that haven't been read yet by this subscriber must have their counter decremented\n        let start_id = self.next_message_id - self.queue.len() as u64;\n        if subscriber_next_message_id >= start_id {\n            let current_message_index = (subscriber_next_message_id - start_id) as usize;\n            self.queue\n                .iter_mut()\n                .skip(current_message_index)\n                .for_each(|(_, counter)| *counter -= 1);\n\n            let mut wake_publishers = false;\n            while let Some((_, count)) = self.queue.front() {\n                if *count == 0 {\n                    self.queue.pop_front().unwrap();\n                    wake_publishers = true;\n                } else {\n                    break;\n                }\n            }\n\n            if wake_publishers {\n                self.publisher_wakers.wake();\n            }\n        }\n    }\n\n    fn unregister_publisher(&mut self) {\n        self.publisher_count -= 1;\n    }\n\n    fn clear(&mut self) {\n        if self.is_full() {\n            self.publisher_wakers.wake();\n        }\n        self.queue.clear();\n    }\n\n    fn len(&self) -> usize {\n        self.queue.len()\n    }\n\n    fn is_empty(&self) -> bool {\n        self.queue.is_empty()\n    }\n\n    fn is_full(&self) -> bool {\n        self.queue.is_full()\n    }\n}\n\n/// Error type for the [PubSubChannel]\n#[derive(Debug, PartialEq, Eq, Clone, Copy)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub enum Error {\n    /// All subscriber slots are used. To add another subscriber, first another subscriber must be dropped or\n    /// the capacity of the channels must be increased.\n    MaximumSubscribersReached,\n    /// All publisher slots are used. To add another publisher, first another publisher must be dropped or\n    /// the capacity of the channels must be increased.\n    MaximumPublishersReached,\n}\n\ntrait SealedPubSubBehavior<T> {\n    /// Try to get a message from the queue with the given message id.\n    ///\n    /// If the message is not yet present and a context is given, then its waker is registered in the subscriber wakers.\n    fn get_message_with_context(&self, next_message_id: &mut u64, cx: Option<&mut Context<'_>>) -> Poll<WaitResult<T>>;\n\n    /// Get the amount of messages that are between the given the next_message_id and the most recent message.\n    /// This is not necessarily the amount of messages a subscriber can still received as it may have lagged.\n    fn available(&self, next_message_id: u64) -> u64;\n\n    /// Try to publish a message to the queue.\n    ///\n    /// If the queue is full and a context is given, then its waker is registered in the publisher wakers.\n    fn publish_with_context(&self, message: T, cx: Option<&mut Context<'_>>) -> Result<(), T>;\n\n    /// Returns the free capacity of the channel.\n    ///\n    /// This is equivalent to `capacity() - len()`\n    fn free_capacity(&self) -> usize;\n\n    /// Clears all elements in the channel.\n    fn clear(&self);\n\n    /// Returns the number of elements currently in the channel.\n    fn len(&self) -> usize;\n\n    /// Returns whether the channel is empty.\n    fn is_empty(&self) -> bool;\n\n    /// Let the channel know that a subscriber has dropped\n    fn unregister_subscriber(&self, subscriber_next_message_id: u64);\n\n    /// Let the channel know that a publisher has dropped\n    fn unregister_publisher(&self);\n}\n\n/// 'Middle level' behaviour of the pubsub channel.\n/// This trait is used so that Sub and Pub can be generic over the channel.\n#[allow(private_bounds)]\npub trait PubSubBehavior<T>: SealedPubSubBehavior<T> {\n    /// Publish a message immediately\n    fn publish_immediate(&self, message: T);\n\n    /// Returns the maximum number of elements the channel can hold.\n    fn capacity(&self) -> usize;\n\n    /// Returns whether the channel is full.\n    fn is_full(&self) -> bool;\n}\n\n/// The result of the subscriber wait procedure\n#[derive(Debug, Clone, PartialEq, Eq)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub enum WaitResult<T> {\n    /// The subscriber did not receive all messages and lagged by the given amount of messages.\n    /// (This is the amount of messages that were missed)\n    Lagged(u64),\n    /// A message was received\n    Message(T),\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n    use crate::blocking_mutex::raw::NoopRawMutex;\n\n    #[futures_test::test]\n    async fn dyn_pub_sub_works() {\n        let channel = PubSubChannel::<NoopRawMutex, u32, 4, 4, 4>::new();\n\n        let mut sub0 = channel.dyn_subscriber().unwrap();\n        let mut sub1 = channel.dyn_subscriber().unwrap();\n        let pub0 = channel.dyn_publisher().unwrap();\n\n        pub0.publish(42).await;\n\n        assert_eq!(sub0.next_message().await, WaitResult::Message(42));\n        assert_eq!(sub1.next_message().await, WaitResult::Message(42));\n\n        assert_eq!(sub0.try_next_message(), None);\n        assert_eq!(sub1.try_next_message(), None);\n    }\n\n    #[futures_test::test]\n    async fn all_subscribers_receive() {\n        let channel = PubSubChannel::<NoopRawMutex, u32, 4, 4, 4>::new();\n\n        let mut sub0 = channel.subscriber().unwrap();\n        let mut sub1 = channel.subscriber().unwrap();\n        let pub0 = channel.publisher().unwrap();\n\n        pub0.publish(42).await;\n\n        assert_eq!(sub0.next_message().await, WaitResult::Message(42));\n        assert_eq!(sub1.next_message().await, WaitResult::Message(42));\n\n        assert_eq!(sub0.try_next_message(), None);\n        assert_eq!(sub1.try_next_message(), None);\n    }\n\n    #[futures_test::test]\n    async fn lag_when_queue_full_on_immediate_publish() {\n        let channel = PubSubChannel::<NoopRawMutex, u32, 4, 4, 4>::new();\n\n        let mut sub0 = channel.subscriber().unwrap();\n        let pub0 = channel.publisher().unwrap();\n\n        pub0.publish_immediate(42);\n        pub0.publish_immediate(43);\n        pub0.publish_immediate(44);\n        pub0.publish_immediate(45);\n        pub0.publish_immediate(46);\n        pub0.publish_immediate(47);\n\n        assert_eq!(sub0.try_next_message(), Some(WaitResult::Lagged(2)));\n        assert_eq!(sub0.next_message().await, WaitResult::Message(44));\n        assert_eq!(sub0.next_message().await, WaitResult::Message(45));\n        assert_eq!(sub0.next_message().await, WaitResult::Message(46));\n        assert_eq!(sub0.next_message().await, WaitResult::Message(47));\n        assert_eq!(sub0.try_next_message(), None);\n    }\n\n    #[test]\n    fn limited_subs_and_pubs() {\n        let channel = PubSubChannel::<NoopRawMutex, u32, 4, 4, 4>::new();\n\n        let sub0 = channel.subscriber();\n        let sub1 = channel.subscriber();\n        let sub2 = channel.subscriber();\n        let sub3 = channel.subscriber();\n        let sub4 = channel.subscriber();\n\n        assert!(sub0.is_ok());\n        assert!(sub1.is_ok());\n        assert!(sub2.is_ok());\n        assert!(sub3.is_ok());\n        assert_eq!(sub4.err().unwrap(), Error::MaximumSubscribersReached);\n\n        drop(sub0);\n\n        let sub5 = channel.subscriber();\n        assert!(sub5.is_ok());\n\n        // publishers\n\n        let pub0 = channel.publisher();\n        let pub1 = channel.publisher();\n        let pub2 = channel.publisher();\n        let pub3 = channel.publisher();\n        let pub4 = channel.publisher();\n\n        assert!(pub0.is_ok());\n        assert!(pub1.is_ok());\n        assert!(pub2.is_ok());\n        assert!(pub3.is_ok());\n        assert_eq!(pub4.err().unwrap(), Error::MaximumPublishersReached);\n\n        drop(pub0);\n\n        let pub5 = channel.publisher();\n        assert!(pub5.is_ok());\n    }\n\n    #[test]\n    fn publisher_wait_on_full_queue() {\n        let channel = PubSubChannel::<NoopRawMutex, u32, 4, 4, 4>::new();\n\n        let pub0 = channel.publisher().unwrap();\n\n        // There are no subscribers, so the queue will never be full\n        assert_eq!(pub0.try_publish(0), Ok(()));\n        assert_eq!(pub0.try_publish(0), Ok(()));\n        assert_eq!(pub0.try_publish(0), Ok(()));\n        assert_eq!(pub0.try_publish(0), Ok(()));\n        assert_eq!(pub0.try_publish(0), Ok(()));\n\n        let sub0 = channel.subscriber().unwrap();\n\n        assert_eq!(pub0.try_publish(0), Ok(()));\n        assert_eq!(pub0.try_publish(0), Ok(()));\n        assert_eq!(pub0.try_publish(0), Ok(()));\n        assert_eq!(pub0.try_publish(0), Ok(()));\n        assert!(pub0.is_full());\n        assert_eq!(pub0.try_publish(0), Err(0));\n\n        drop(sub0);\n    }\n\n    #[futures_test::test]\n    async fn correct_available() {\n        let channel = PubSubChannel::<NoopRawMutex, u32, 4, 4, 4>::new();\n\n        let sub0 = channel.subscriber().unwrap();\n        let mut sub1 = channel.subscriber().unwrap();\n        let pub0 = channel.publisher().unwrap();\n\n        assert_eq!(sub0.available(), 0);\n        assert_eq!(sub1.available(), 0);\n\n        pub0.publish(42).await;\n\n        assert_eq!(sub0.available(), 1);\n        assert_eq!(sub1.available(), 1);\n\n        sub1.next_message().await;\n\n        assert_eq!(sub1.available(), 0);\n\n        pub0.publish(42).await;\n\n        assert_eq!(sub0.available(), 2);\n        assert_eq!(sub1.available(), 1);\n    }\n\n    #[futures_test::test]\n    async fn correct_len() {\n        let channel = PubSubChannel::<NoopRawMutex, u32, 4, 4, 4>::new();\n\n        let mut sub0 = channel.subscriber().unwrap();\n        let mut sub1 = channel.subscriber().unwrap();\n        let pub0 = channel.publisher().unwrap();\n\n        assert!(sub0.is_empty());\n        assert!(sub1.is_empty());\n        assert!(pub0.is_empty());\n        assert_eq!(pub0.free_capacity(), 4);\n        assert_eq!(pub0.len(), 0);\n\n        pub0.publish(42).await;\n\n        assert_eq!(pub0.free_capacity(), 3);\n        assert_eq!(pub0.len(), 1);\n\n        pub0.publish(42).await;\n\n        assert_eq!(pub0.free_capacity(), 2);\n        assert_eq!(pub0.len(), 2);\n\n        sub0.next_message().await;\n        sub0.next_message().await;\n\n        assert_eq!(pub0.free_capacity(), 2);\n        assert_eq!(pub0.len(), 2);\n\n        sub1.next_message().await;\n        assert_eq!(pub0.free_capacity(), 3);\n        assert_eq!(pub0.len(), 1);\n\n        sub1.next_message().await;\n        assert_eq!(pub0.free_capacity(), 4);\n        assert_eq!(pub0.len(), 0);\n    }\n\n    #[futures_test::test]\n    async fn empty_channel_when_last_subscriber_is_dropped() {\n        let channel = PubSubChannel::<NoopRawMutex, u32, 4, 4, 4>::new();\n\n        let pub0 = channel.publisher().unwrap();\n        let mut sub0 = channel.subscriber().unwrap();\n        let mut sub1 = channel.subscriber().unwrap();\n\n        assert_eq!(4, pub0.free_capacity());\n\n        pub0.publish(1).await;\n        pub0.publish(2).await;\n\n        assert_eq!(2, channel.free_capacity());\n\n        assert_eq!(1, sub0.try_next_message_pure().unwrap());\n        assert_eq!(2, sub0.try_next_message_pure().unwrap());\n\n        assert_eq!(2, channel.free_capacity());\n\n        drop(sub0);\n\n        assert_eq!(2, channel.free_capacity());\n\n        assert_eq!(1, sub1.try_next_message_pure().unwrap());\n\n        assert_eq!(3, channel.free_capacity());\n\n        drop(sub1);\n\n        assert_eq!(4, channel.free_capacity());\n    }\n\n    struct CloneCallCounter(usize);\n\n    impl Clone for CloneCallCounter {\n        fn clone(&self) -> Self {\n            Self(self.0 + 1)\n        }\n    }\n\n    #[futures_test::test]\n    async fn skip_clone_for_last_message() {\n        let channel = PubSubChannel::<NoopRawMutex, CloneCallCounter, 1, 2, 1>::new();\n        let pub0 = channel.publisher().unwrap();\n        let mut sub0 = channel.subscriber().unwrap();\n        let mut sub1 = channel.subscriber().unwrap();\n\n        pub0.publish(CloneCallCounter(0)).await;\n\n        assert_eq!(1, sub0.try_next_message_pure().unwrap().0);\n        assert_eq!(0, sub1.try_next_message_pure().unwrap().0);\n    }\n\n    #[futures_test::test]\n    async fn publisher_sink() {\n        use futures_util::{SinkExt, StreamExt};\n\n        let channel = PubSubChannel::<NoopRawMutex, u32, 4, 4, 4>::new();\n\n        let mut sub = channel.subscriber().unwrap();\n\n        let publ = channel.publisher().unwrap();\n        let mut sink = publ.sink();\n\n        sink.send(0).await.unwrap();\n        assert_eq!(0, sub.try_next_message_pure().unwrap());\n\n        sink.send(1).await.unwrap();\n        assert_eq!(1, sub.try_next_message_pure().unwrap());\n\n        sink.send_all(&mut futures_util::stream::iter(0..4).map(Ok))\n            .await\n            .unwrap();\n        assert_eq!(0, sub.try_next_message_pure().unwrap());\n        assert_eq!(1, sub.try_next_message_pure().unwrap());\n        assert_eq!(2, sub.try_next_message_pure().unwrap());\n        assert_eq!(3, sub.try_next_message_pure().unwrap());\n    }\n}\n"
  },
  {
    "path": "embassy-sync/src/pubsub/publisher.rs",
    "content": "//! Implementation of anything directly publisher related\n\nuse core::future::Future;\nuse core::marker::PhantomData;\nuse core::ops::{Deref, DerefMut};\nuse core::pin::Pin;\nuse core::task::{Context, Poll};\n\nuse super::{PubSubBehavior, PubSubChannel};\nuse crate::blocking_mutex::raw::RawMutex;\n\n/// A publisher to a channel\n#[derive(Debug)]\npub struct Pub<'a, PSB: PubSubBehavior<T> + ?Sized, T: Clone> {\n    /// The channel we are a publisher for\n    channel: &'a PSB,\n    _phantom: PhantomData<T>,\n}\n\nimpl<'a, PSB: PubSubBehavior<T> + ?Sized, T: Clone> Pub<'a, PSB, T> {\n    pub(super) fn new(channel: &'a PSB) -> Self {\n        Self {\n            channel,\n            _phantom: Default::default(),\n        }\n    }\n\n    /// Publish a message right now even when the queue is full.\n    /// This may cause a subscriber to miss an older message.\n    pub fn publish_immediate(&self, message: T) {\n        self.channel.publish_immediate(message)\n    }\n\n    /// Publish a message. But if the message queue is full, wait for all subscribers to have read the last message\n    pub fn publish<'s>(&'s self, message: T) -> PublisherWaitFuture<'s, 'a, PSB, T> {\n        PublisherWaitFuture {\n            message: Some(message),\n            publisher: self,\n        }\n    }\n\n    /// Publish a message if there is space in the message queue\n    pub fn try_publish(&self, message: T) -> Result<(), T> {\n        self.channel.publish_with_context(message, None)\n    }\n\n    /// Returns the maximum number of elements the ***channel*** can hold.\n    pub fn capacity(&self) -> usize {\n        self.channel.capacity()\n    }\n\n    /// Returns the free capacity of the ***channel***.\n    ///\n    /// This is equivalent to `capacity() - len()`\n    pub fn free_capacity(&self) -> usize {\n        self.channel.free_capacity()\n    }\n\n    /// Clears all elements in the ***channel***.\n    pub fn clear(&self) {\n        self.channel.clear();\n    }\n\n    /// Returns the number of elements currently in the ***channel***.\n    pub fn len(&self) -> usize {\n        self.channel.len()\n    }\n\n    /// Returns whether the ***channel*** is empty.\n    pub fn is_empty(&self) -> bool {\n        self.channel.is_empty()\n    }\n\n    /// Returns whether the ***channel*** is full.\n    pub fn is_full(&self) -> bool {\n        self.channel.is_full()\n    }\n\n    /// Create a [`futures_sink::Sink`] adapter for this publisher.\n    #[inline]\n    pub const fn sink(&self) -> PubSink<'a, '_, PSB, T> {\n        PubSink { publ: self, fut: None }\n    }\n}\n\nimpl<'a, PSB: PubSubBehavior<T> + ?Sized, T: Clone> Drop for Pub<'a, PSB, T> {\n    fn drop(&mut self) {\n        self.channel.unregister_publisher()\n    }\n}\n\n/// A publisher that holds a dynamic reference to the channel\npub struct DynPublisher<'a, T: Clone>(pub(super) Pub<'a, dyn PubSubBehavior<T> + 'a, T>);\n\nimpl<'a, T: Clone> Deref for DynPublisher<'a, T> {\n    type Target = Pub<'a, dyn PubSubBehavior<T> + 'a, T>;\n\n    fn deref(&self) -> &Self::Target {\n        &self.0\n    }\n}\n\nimpl<'a, T: Clone> DerefMut for DynPublisher<'a, T> {\n    fn deref_mut(&mut self) -> &mut Self::Target {\n        &mut self.0\n    }\n}\n\n/// A publisher that holds a generic reference to the channel\n#[derive(Debug)]\npub struct Publisher<'a, M: RawMutex, T: Clone, const CAP: usize, const SUBS: usize, const PUBS: usize>(\n    pub(super) Pub<'a, PubSubChannel<M, T, CAP, SUBS, PUBS>, T>,\n);\n\nimpl<'a, M: RawMutex, T: Clone, const CAP: usize, const SUBS: usize, const PUBS: usize> Deref\n    for Publisher<'a, M, T, CAP, SUBS, PUBS>\n{\n    type Target = Pub<'a, PubSubChannel<M, T, CAP, SUBS, PUBS>, T>;\n\n    fn deref(&self) -> &Self::Target {\n        &self.0\n    }\n}\n\nimpl<'a, M: RawMutex, T: Clone, const CAP: usize, const SUBS: usize, const PUBS: usize> DerefMut\n    for Publisher<'a, M, T, CAP, SUBS, PUBS>\n{\n    fn deref_mut(&mut self) -> &mut Self::Target {\n        &mut self.0\n    }\n}\n\n/// A publisher that can only use the `publish_immediate` function, but it doesn't have to be registered with the channel.\n/// (So an infinite amount is possible)\n#[derive(Debug)]\npub struct ImmediatePub<'a, PSB: PubSubBehavior<T> + ?Sized, T: Clone> {\n    /// The channel we are a publisher for\n    channel: &'a PSB,\n    _phantom: PhantomData<T>,\n}\n\nimpl<'a, PSB: PubSubBehavior<T> + ?Sized, T: Clone> ImmediatePub<'a, PSB, T> {\n    pub(super) fn new(channel: &'a PSB) -> Self {\n        Self {\n            channel,\n            _phantom: Default::default(),\n        }\n    }\n    /// Publish the message right now even when the queue is full.\n    /// This may cause a subscriber to miss an older message.\n    pub fn publish_immediate(&self, message: T) {\n        self.channel.publish_immediate(message)\n    }\n\n    /// Publish a message if there is space in the message queue\n    pub fn try_publish(&self, message: T) -> Result<(), T> {\n        self.channel.publish_with_context(message, None)\n    }\n\n    /// Returns the maximum number of elements the ***channel*** can hold.\n    pub fn capacity(&self) -> usize {\n        self.channel.capacity()\n    }\n\n    /// Returns the free capacity of the ***channel***.\n    ///\n    /// This is equivalent to `capacity() - len()`\n    pub fn free_capacity(&self) -> usize {\n        self.channel.free_capacity()\n    }\n\n    /// Clears all elements in the ***channel***.\n    pub fn clear(&self) {\n        self.channel.clear();\n    }\n\n    /// Returns the number of elements currently in the ***channel***.\n    pub fn len(&self) -> usize {\n        self.channel.len()\n    }\n\n    /// Returns whether the ***channel*** is empty.\n    pub fn is_empty(&self) -> bool {\n        self.channel.is_empty()\n    }\n\n    /// Returns whether the ***channel*** is full.\n    pub fn is_full(&self) -> bool {\n        self.channel.is_full()\n    }\n}\n\n/// An immediate publisher that holds a dynamic reference to the channel\npub struct DynImmediatePublisher<'a, T: Clone>(pub(super) ImmediatePub<'a, dyn PubSubBehavior<T> + 'a, T>);\n\nimpl<'a, T: Clone> Deref for DynImmediatePublisher<'a, T> {\n    type Target = ImmediatePub<'a, dyn PubSubBehavior<T> + 'a, T>;\n\n    fn deref(&self) -> &Self::Target {\n        &self.0\n    }\n}\n\nimpl<'a, T: Clone> DerefMut for DynImmediatePublisher<'a, T> {\n    fn deref_mut(&mut self) -> &mut Self::Target {\n        &mut self.0\n    }\n}\n\n/// An immediate publisher that holds a generic reference to the channel\n#[derive(Debug)]\npub struct ImmediatePublisher<'a, M: RawMutex, T: Clone, const CAP: usize, const SUBS: usize, const PUBS: usize>(\n    pub(super) ImmediatePub<'a, PubSubChannel<M, T, CAP, SUBS, PUBS>, T>,\n);\n\nimpl<'a, M: RawMutex, T: Clone, const CAP: usize, const SUBS: usize, const PUBS: usize> Deref\n    for ImmediatePublisher<'a, M, T, CAP, SUBS, PUBS>\n{\n    type Target = ImmediatePub<'a, PubSubChannel<M, T, CAP, SUBS, PUBS>, T>;\n\n    fn deref(&self) -> &Self::Target {\n        &self.0\n    }\n}\n\nimpl<'a, M: RawMutex, T: Clone, const CAP: usize, const SUBS: usize, const PUBS: usize> DerefMut\n    for ImmediatePublisher<'a, M, T, CAP, SUBS, PUBS>\n{\n    fn deref_mut(&mut self) -> &mut Self::Target {\n        &mut self.0\n    }\n}\n\n#[must_use = \"Sinks do nothing unless polled\"]\n/// [`futures_sink::Sink`] adapter for [`Pub`].\n#[derive(Debug)]\npub struct PubSink<'a, 'p, PSB, T>\nwhere\n    T: Clone,\n    PSB: PubSubBehavior<T> + ?Sized,\n{\n    publ: &'p Pub<'a, PSB, T>,\n    fut: Option<PublisherWaitFuture<'p, 'a, PSB, T>>,\n}\n\nimpl<'a, 'p, PSB, T> PubSink<'a, 'p, PSB, T>\nwhere\n    PSB: PubSubBehavior<T> + ?Sized,\n    T: Clone,\n{\n    /// Try to make progress on the pending future if we have one.\n    fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<()> {\n        let Some(mut fut) = self.fut.take() else {\n            return Poll::Ready(());\n        };\n\n        if Pin::new(&mut fut).poll(cx).is_pending() {\n            self.fut = Some(fut);\n            return Poll::Pending;\n        }\n\n        Poll::Ready(())\n    }\n}\n\nimpl<'a, 'p, PSB, T> futures_sink::Sink<T> for PubSink<'a, 'p, PSB, T>\nwhere\n    PSB: PubSubBehavior<T> + ?Sized,\n    T: Clone,\n{\n    type Error = core::convert::Infallible;\n\n    #[inline]\n    fn poll_ready(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {\n        self.poll(cx).map(Ok)\n    }\n\n    #[inline]\n    fn start_send(mut self: Pin<&mut Self>, item: T) -> Result<(), Self::Error> {\n        self.fut = Some(self.publ.publish(item));\n\n        Ok(())\n    }\n\n    #[inline]\n    fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {\n        self.poll(cx).map(Ok)\n    }\n\n    #[inline]\n    fn poll_close(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {\n        self.poll(cx).map(Ok)\n    }\n}\n\n/// Future for the publisher wait action\n#[must_use = \"futures do nothing unless you `.await` or poll them\"]\n#[derive(Debug)]\npub struct PublisherWaitFuture<'s, 'a, PSB: PubSubBehavior<T> + ?Sized, T: Clone> {\n    /// The message we need to publish\n    message: Option<T>,\n    publisher: &'s Pub<'a, PSB, T>,\n}\n\nimpl<'s, 'a, PSB: PubSubBehavior<T> + ?Sized, T: Clone> Future for PublisherWaitFuture<'s, 'a, PSB, T> {\n    type Output = ();\n\n    fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {\n        let message = self.message.take().unwrap();\n        match self.publisher.channel.publish_with_context(message, Some(cx)) {\n            Ok(()) => Poll::Ready(()),\n            Err(message) => {\n                self.message = Some(message);\n                Poll::Pending\n            }\n        }\n    }\n}\n\nimpl<'s, 'a, PSB: PubSubBehavior<T> + ?Sized, T: Clone> Unpin for PublisherWaitFuture<'s, 'a, PSB, T> {}\n"
  },
  {
    "path": "embassy-sync/src/pubsub/subscriber.rs",
    "content": "//! Implementation of anything directly subscriber related\n\nuse core::future::Future;\nuse core::marker::PhantomData;\nuse core::ops::{Deref, DerefMut};\nuse core::pin::Pin;\nuse core::task::{Context, Poll};\n\nuse super::{PubSubBehavior, PubSubChannel, WaitResult};\nuse crate::blocking_mutex::raw::RawMutex;\n\n/// A subscriber to a channel\n#[derive(Debug)]\npub struct Sub<'a, PSB: PubSubBehavior<T> + ?Sized, T: Clone> {\n    /// The message id of the next message we are yet to receive\n    next_message_id: u64,\n    /// The channel we are a subscriber to\n    channel: &'a PSB,\n    _phantom: PhantomData<T>,\n}\n\nimpl<'a, PSB: PubSubBehavior<T> + ?Sized, T: Clone> Sub<'a, PSB, T> {\n    pub(super) fn new(next_message_id: u64, channel: &'a PSB) -> Self {\n        Self {\n            next_message_id,\n            channel,\n            _phantom: Default::default(),\n        }\n    }\n\n    /// Wait for a published message\n    pub fn next_message<'s>(&'s mut self) -> SubscriberWaitFuture<'s, 'a, PSB, T> {\n        SubscriberWaitFuture { subscriber: self }\n    }\n\n    /// Wait for a published message (ignoring lag results)\n    pub async fn next_message_pure(&mut self) -> T {\n        loop {\n            match self.next_message().await {\n                WaitResult::Lagged(_) => continue,\n                WaitResult::Message(message) => break message,\n            }\n        }\n    }\n\n    /// Try to see if there's a published message we haven't received yet.\n    ///\n    /// This function does not peek. The message is received if there is one.\n    pub fn try_next_message(&mut self) -> Option<WaitResult<T>> {\n        match self.channel.get_message_with_context(&mut self.next_message_id, None) {\n            Poll::Ready(result) => Some(result),\n            Poll::Pending => None,\n        }\n    }\n\n    /// Try to see if there's a published message we haven't received yet (ignoring lag results).\n    ///\n    /// This function does not peek. The message is received if there is one.\n    pub fn try_next_message_pure(&mut self) -> Option<T> {\n        loop {\n            match self.try_next_message() {\n                Some(WaitResult::Lagged(_)) => continue,\n                Some(WaitResult::Message(message)) => break Some(message),\n                None => break None,\n            }\n        }\n    }\n\n    /// The amount of messages this subscriber hasn't received yet. This is like [Self::len] but specifically\n    /// for this subscriber.\n    pub fn available(&self) -> u64 {\n        self.channel.available(self.next_message_id)\n    }\n\n    /// Returns the maximum number of elements the ***channel*** can hold.\n    pub fn capacity(&self) -> usize {\n        self.channel.capacity()\n    }\n\n    /// Returns the free capacity of the ***channel***.\n    ///\n    /// This is equivalent to `capacity() - len()`\n    pub fn free_capacity(&self) -> usize {\n        self.channel.free_capacity()\n    }\n\n    /// Clears all elements in the ***channel***.\n    pub fn clear(&self) {\n        self.channel.clear();\n    }\n\n    /// Returns the number of elements currently in the ***channel***.\n    /// See [Self::available] for how many messages are available for this subscriber.\n    pub fn len(&self) -> usize {\n        self.channel.len()\n    }\n\n    /// Returns whether the ***channel*** is empty.\n    pub fn is_empty(&self) -> bool {\n        self.channel.is_empty()\n    }\n\n    /// Returns whether the ***channel*** is full.\n    pub fn is_full(&self) -> bool {\n        self.channel.is_full()\n    }\n}\n\nimpl<'a, PSB: PubSubBehavior<T> + ?Sized, T: Clone> Drop for Sub<'a, PSB, T> {\n    fn drop(&mut self) {\n        self.channel.unregister_subscriber(self.next_message_id)\n    }\n}\n\nimpl<'a, PSB: PubSubBehavior<T> + ?Sized, T: Clone> Unpin for Sub<'a, PSB, T> {}\n\n/// Warning: The stream implementation ignores lag results and returns all messages.\n/// This might miss some messages without you knowing it.\nimpl<'a, PSB: PubSubBehavior<T> + ?Sized, T: Clone> futures_core::Stream for Sub<'a, PSB, T> {\n    type Item = T;\n\n    fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {\n        match self\n            .channel\n            .get_message_with_context(&mut self.next_message_id, Some(cx))\n        {\n            Poll::Ready(WaitResult::Message(message)) => Poll::Ready(Some(message)),\n            Poll::Ready(WaitResult::Lagged(_)) => {\n                cx.waker().wake_by_ref();\n                Poll::Pending\n            }\n            Poll::Pending => Poll::Pending,\n        }\n    }\n}\n\n/// A subscriber that holds a dynamic reference to the channel\npub struct DynSubscriber<'a, T: Clone>(pub(super) Sub<'a, dyn PubSubBehavior<T> + 'a, T>);\n\nimpl<'a, T: Clone> Deref for DynSubscriber<'a, T> {\n    type Target = Sub<'a, dyn PubSubBehavior<T> + 'a, T>;\n\n    fn deref(&self) -> &Self::Target {\n        &self.0\n    }\n}\n\nimpl<'a, T: Clone> DerefMut for DynSubscriber<'a, T> {\n    fn deref_mut(&mut self) -> &mut Self::Target {\n        &mut self.0\n    }\n}\n\n/// A subscriber that holds a generic reference to the channel\n#[derive(Debug)]\npub struct Subscriber<'a, M: RawMutex, T: Clone, const CAP: usize, const SUBS: usize, const PUBS: usize>(\n    pub(super) Sub<'a, PubSubChannel<M, T, CAP, SUBS, PUBS>, T>,\n);\n\nimpl<'a, M: RawMutex, T: Clone, const CAP: usize, const SUBS: usize, const PUBS: usize> Deref\n    for Subscriber<'a, M, T, CAP, SUBS, PUBS>\n{\n    type Target = Sub<'a, PubSubChannel<M, T, CAP, SUBS, PUBS>, T>;\n\n    fn deref(&self) -> &Self::Target {\n        &self.0\n    }\n}\n\nimpl<'a, M: RawMutex, T: Clone, const CAP: usize, const SUBS: usize, const PUBS: usize> DerefMut\n    for Subscriber<'a, M, T, CAP, SUBS, PUBS>\n{\n    fn deref_mut(&mut self) -> &mut Self::Target {\n        &mut self.0\n    }\n}\n\n/// Future for the subscriber wait action\n#[must_use = \"futures do nothing unless you `.await` or poll them\"]\n#[derive(Debug)]\npub struct SubscriberWaitFuture<'s, 'a, PSB: PubSubBehavior<T> + ?Sized, T: Clone> {\n    subscriber: &'s mut Sub<'a, PSB, T>,\n}\n\nimpl<'s, 'a, PSB: PubSubBehavior<T> + ?Sized, T: Clone> Future for SubscriberWaitFuture<'s, 'a, PSB, T> {\n    type Output = WaitResult<T>;\n\n    fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {\n        self.subscriber\n            .channel\n            .get_message_with_context(&mut self.subscriber.next_message_id, Some(cx))\n    }\n}\n\nimpl<'s, 'a, PSB: PubSubBehavior<T> + ?Sized, T: Clone> Unpin for SubscriberWaitFuture<'s, 'a, PSB, T> {}\n"
  },
  {
    "path": "embassy-sync/src/ring_buffer.rs",
    "content": "use core::ops::Range;\n\n#[derive(Debug)]\npub struct RingBuffer<const N: usize> {\n    start: usize,\n    end: usize,\n    full: bool,\n}\n\nimpl<const N: usize> RingBuffer<N> {\n    pub const fn new() -> Self {\n        Self {\n            start: 0,\n            end: 0,\n            full: false,\n        }\n    }\n\n    pub fn push_buf(&mut self) -> Range<usize> {\n        if self.is_full() {\n            trace!(\"  ringbuf: push_buf full\");\n            return 0..0;\n        }\n\n        let n = if self.start <= self.end {\n            N - self.end\n        } else {\n            self.start - self.end\n        };\n\n        trace!(\"  ringbuf: push_buf {:?}..{:?}\", self.end, self.end + n);\n        self.end..self.end + n\n    }\n\n    pub fn push(&mut self, n: usize) {\n        trace!(\"  ringbuf: push {:?}\", n);\n        if n == 0 {\n            return;\n        }\n\n        self.end = self.wrap(self.end + n);\n        self.full = self.start == self.end;\n    }\n\n    pub fn pop_buf(&mut self) -> Range<usize> {\n        if self.is_empty() {\n            trace!(\"  ringbuf: pop_buf empty\");\n            return 0..0;\n        }\n\n        let n = if self.end <= self.start {\n            N - self.start\n        } else {\n            self.end - self.start\n        };\n\n        trace!(\"  ringbuf: pop_buf {:?}..{:?}\", self.start, self.start + n);\n        self.start..self.start + n\n    }\n\n    pub fn pop(&mut self, n: usize) {\n        trace!(\"  ringbuf: pop {:?}\", n);\n        if n == 0 {\n            return;\n        }\n\n        self.start = self.wrap(self.start + n);\n        self.full = false;\n    }\n\n    pub fn is_full(&self) -> bool {\n        self.full\n    }\n\n    pub fn is_empty(&self) -> bool {\n        self.start == self.end && !self.full\n    }\n\n    #[allow(unused)]\n    pub fn len(&self) -> usize {\n        if self.is_empty() {\n            0\n        } else if self.start < self.end {\n            self.end - self.start\n        } else {\n            N + self.end - self.start\n        }\n    }\n\n    pub fn clear(&mut self) {\n        self.start = 0;\n        self.end = 0;\n        self.full = false;\n    }\n\n    fn wrap(&self, n: usize) -> usize {\n        assert!(n <= N);\n        if n == N { 0 } else { n }\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n\n    #[test]\n    fn push_pop() {\n        let mut rb: RingBuffer<4> = RingBuffer::new();\n        let buf = rb.push_buf();\n        assert_eq!(0..4, buf);\n        rb.push(4);\n\n        let buf = rb.pop_buf();\n        assert_eq!(0..4, buf);\n        rb.pop(1);\n\n        let buf = rb.pop_buf();\n        assert_eq!(1..4, buf);\n        rb.pop(1);\n\n        let buf = rb.pop_buf();\n        assert_eq!(2..4, buf);\n        rb.pop(1);\n\n        let buf = rb.pop_buf();\n        assert_eq!(3..4, buf);\n        rb.pop(1);\n\n        let buf = rb.pop_buf();\n        assert_eq!(0..0, buf);\n\n        let buf = rb.push_buf();\n        assert_eq!(0..4, buf);\n    }\n}\n"
  },
  {
    "path": "embassy-sync/src/rwlock.rs",
    "content": "//! Async read-write lock.\n//!\n//! This module provides a read-write lock that can be used to synchronize data between asynchronous tasks.\nuse core::cell::{RefCell, UnsafeCell};\nuse core::fmt;\nuse core::future::{Future, poll_fn};\nuse core::ops::{Deref, DerefMut};\nuse core::task::Poll;\n\nuse crate::blocking_mutex::Mutex as BlockingMutex;\nuse crate::blocking_mutex::raw::RawMutex;\nuse crate::waitqueue::WakerRegistration;\n\n/// Error returned by [`RwLock::try_read`] and [`RwLock::try_write`] when the lock is already held.\n#[derive(PartialEq, Eq, Clone, Copy, Debug)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub struct TryLockError;\n\n#[derive(Debug)]\nstruct State {\n    readers: usize,\n    writer: bool,\n    waker: WakerRegistration,\n}\n\n/// Async read-write lock.\n///\n/// The read-write lock is generic over the raw mutex implementation `M` and the data `T` it protects.\n/// The raw read-write lock is used to guard access to the internal state. It\n/// is held for very short periods only, while locking and unlocking. It is *not* held\n/// for the entire time the async RwLock is locked.\n///\n/// Which implementation you select depends on the context in which you're using the read-write lock.\n///\n/// Use [`CriticalSectionRawMutex`](crate::blocking_mutex::raw::CriticalSectionRawMutex) when data can be shared between threads and interrupts.\n///\n/// Use [`NoopRawMutex`](crate::blocking_mutex::raw::NoopRawMutex) when data is only shared between tasks running on the same executor.\n///\n/// Use [`ThreadModeRawMutex`](crate::blocking_mutex::raw::ThreadModeRawMutex) when data is shared between tasks running on the same executor but you want a singleton.\npub struct RwLock<M, T>\nwhere\n    M: RawMutex,\n    T: ?Sized,\n{\n    state: BlockingMutex<M, RefCell<State>>,\n    inner: UnsafeCell<T>,\n}\n\nunsafe impl<M: RawMutex + Send, T: ?Sized + Send> Send for RwLock<M, T> {}\nunsafe impl<M: RawMutex + Sync, T: ?Sized + Send> Sync for RwLock<M, T> {}\n\n/// Async read-write lock.\nimpl<M, T> RwLock<M, T>\nwhere\n    M: RawMutex,\n{\n    /// Create a new read-write lock with the given value.\n    pub const fn new(value: T) -> Self {\n        Self {\n            inner: UnsafeCell::new(value),\n            state: BlockingMutex::new(RefCell::new(State {\n                readers: 0,\n                writer: false,\n                waker: WakerRegistration::new(),\n            })),\n        }\n    }\n}\n\nimpl<M, T> RwLock<M, T>\nwhere\n    M: RawMutex,\n    T: ?Sized,\n{\n    /// Lock the read-write lock for reading.\n    ///\n    /// This will wait for the lock to be available if it's already locked for writing.\n    pub fn read(&self) -> impl Future<Output = RwLockReadGuard<'_, M, T>> {\n        poll_fn(|cx| {\n            let ready = self.state.lock(|s| {\n                let mut s = s.borrow_mut();\n                if s.writer {\n                    s.waker.register(cx.waker());\n                    false\n                } else {\n                    s.readers += 1;\n                    true\n                }\n            });\n\n            if ready {\n                Poll::Ready(RwLockReadGuard { rwlock: self })\n            } else {\n                Poll::Pending\n            }\n        })\n    }\n\n    /// Lock the read-write lock for writing.\n    ///\n    /// This will wait for the lock to be available if it's already locked for reading or writing.\n    pub fn write(&self) -> impl Future<Output = RwLockWriteGuard<'_, M, T>> {\n        poll_fn(|cx| {\n            let ready = self.state.lock(|s| {\n                let mut s = s.borrow_mut();\n                if s.writer || s.readers > 0 {\n                    s.waker.register(cx.waker());\n                    false\n                } else {\n                    s.writer = true;\n                    true\n                }\n            });\n\n            if ready {\n                Poll::Ready(RwLockWriteGuard { rwlock: self })\n            } else {\n                Poll::Pending\n            }\n        })\n    }\n\n    /// Attempt to immediately lock the rwlock.\n    ///\n    /// If the rwlock is already locked, this will return an error instead of waiting.\n    pub fn try_read(&self) -> Result<RwLockReadGuard<'_, M, T>, TryLockError> {\n        self.state\n            .lock(|s| {\n                let mut s = s.borrow_mut();\n                if s.writer {\n                    return Err(());\n                }\n                s.readers += 1;\n                Ok(())\n            })\n            .map_err(|_| TryLockError)?;\n\n        Ok(RwLockReadGuard { rwlock: self })\n    }\n\n    /// Attempt to immediately lock the rwlock.\n    ///\n    /// If the rwlock is already locked, this will return an error instead of waiting.\n    pub fn try_write(&self) -> Result<RwLockWriteGuard<'_, M, T>, TryLockError> {\n        self.state\n            .lock(|s| {\n                let mut s = s.borrow_mut();\n                if s.writer || s.readers > 0 {\n                    return Err(());\n                }\n                s.writer = true;\n                Ok(())\n            })\n            .map_err(|_| TryLockError)?;\n\n        Ok(RwLockWriteGuard { rwlock: self })\n    }\n\n    /// Consumes this read-write lock, returning the underlying data.\n    pub fn into_inner(self) -> T\n    where\n        T: Sized,\n    {\n        self.inner.into_inner()\n    }\n\n    /// Returns a mutable reference to the underlying data.\n    ///\n    /// Since this call borrows the RwLock mutably, no actual locking needs to\n    /// take place -- the mutable borrow statically guarantees no locks exist.\n    pub fn get_mut(&mut self) -> &mut T {\n        self.inner.get_mut()\n    }\n}\n\nimpl<M: RawMutex, T> From<T> for RwLock<M, T> {\n    fn from(from: T) -> Self {\n        Self::new(from)\n    }\n}\n\nimpl<M, T> Default for RwLock<M, T>\nwhere\n    M: RawMutex,\n    T: Default,\n{\n    fn default() -> Self {\n        Self::new(Default::default())\n    }\n}\n\nimpl<M, T> fmt::Debug for RwLock<M, T>\nwhere\n    M: RawMutex,\n    T: ?Sized + fmt::Debug,\n{\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        let mut d = f.debug_struct(\"RwLock\");\n        match self.try_read() {\n            Ok(guard) => d.field(\"inner\", &&*guard),\n            Err(TryLockError) => d.field(\"inner\", &\"Locked\"),\n        }\n        .finish_non_exhaustive()\n    }\n}\n\n/// Async read lock guard.\n///\n/// Owning an instance of this type indicates having\n/// successfully locked the read-write lock for reading, and grants access to the contents.\n///\n/// Dropping it unlocks the read-write lock.\n#[clippy::has_significant_drop]\n#[must_use = \"if unused the RwLock will immediately unlock\"]\npub struct RwLockReadGuard<'a, R, T>\nwhere\n    R: RawMutex,\n    T: ?Sized,\n{\n    rwlock: &'a RwLock<R, T>,\n}\n\nimpl<'a, M, T> Drop for RwLockReadGuard<'a, M, T>\nwhere\n    M: RawMutex,\n    T: ?Sized,\n{\n    fn drop(&mut self) {\n        self.rwlock.state.lock(|s| {\n            let mut s = unwrap!(s.try_borrow_mut());\n            s.readers -= 1;\n            if s.readers == 0 {\n                s.waker.wake();\n            }\n        })\n    }\n}\n\nimpl<'a, M, T> Deref for RwLockReadGuard<'a, M, T>\nwhere\n    M: RawMutex,\n    T: ?Sized,\n{\n    type Target = T;\n    fn deref(&self) -> &Self::Target {\n        // Safety: the RwLockReadGuard represents shared access to the contents\n        // of the read-write lock, so it's OK to get it.\n        unsafe { &*(self.rwlock.inner.get() as *const T) }\n    }\n}\n\nimpl<'a, M, T> fmt::Debug for RwLockReadGuard<'a, M, T>\nwhere\n    M: RawMutex,\n    T: ?Sized + fmt::Debug,\n{\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        fmt::Debug::fmt(&**self, f)\n    }\n}\n\nimpl<'a, M, T> fmt::Display for RwLockReadGuard<'a, M, T>\nwhere\n    M: RawMutex,\n    T: ?Sized + fmt::Display,\n{\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        fmt::Display::fmt(&**self, f)\n    }\n}\n\n/// Async write lock guard.\n///\n/// Owning an instance of this type indicates having\n/// successfully locked the read-write lock for writing, and grants access to the contents.\n///\n/// Dropping it unlocks the read-write lock.\n#[clippy::has_significant_drop]\n#[must_use = \"if unused the RwLock will immediately unlock\"]\npub struct RwLockWriteGuard<'a, R, T>\nwhere\n    R: RawMutex,\n    T: ?Sized,\n{\n    rwlock: &'a RwLock<R, T>,\n}\n\nimpl<'a, R, T> Drop for RwLockWriteGuard<'a, R, T>\nwhere\n    R: RawMutex,\n    T: ?Sized,\n{\n    fn drop(&mut self) {\n        self.rwlock.state.lock(|s| {\n            let mut s = unwrap!(s.try_borrow_mut());\n            s.writer = false;\n            s.waker.wake();\n        })\n    }\n}\n\nimpl<'a, R, T> Deref for RwLockWriteGuard<'a, R, T>\nwhere\n    R: RawMutex,\n    T: ?Sized,\n{\n    type Target = T;\n    fn deref(&self) -> &Self::Target {\n        // Safety: the RwLockWriteGuard represents exclusive access to the contents\n        // of the read-write lock, so it's OK to get it.\n        unsafe { &*(self.rwlock.inner.get() as *mut T) }\n    }\n}\n\nimpl<'a, R, T> DerefMut for RwLockWriteGuard<'a, R, T>\nwhere\n    R: RawMutex,\n    T: ?Sized,\n{\n    fn deref_mut(&mut self) -> &mut Self::Target {\n        // Safety: the RwLockWriteGuard represents exclusive access to the contents\n        // of the read-write lock, so it's OK to get it.\n        unsafe { &mut *(self.rwlock.inner.get()) }\n    }\n}\n\nimpl<'a, R, T> fmt::Debug for RwLockWriteGuard<'a, R, T>\nwhere\n    R: RawMutex,\n    T: ?Sized + fmt::Debug,\n{\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        fmt::Debug::fmt(&**self, f)\n    }\n}\n\nimpl<'a, R, T> fmt::Display for RwLockWriteGuard<'a, R, T>\nwhere\n    R: RawMutex,\n    T: ?Sized + fmt::Display,\n{\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        fmt::Display::fmt(&**self, f)\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use crate::blocking_mutex::raw::NoopRawMutex;\n    use crate::rwlock::RwLock;\n\n    #[futures_test::test]\n    async fn read_guard_releases_lock_when_dropped() {\n        let rwlock: RwLock<NoopRawMutex, [i32; 2]> = RwLock::new([0, 1]);\n\n        {\n            let guard = rwlock.read().await;\n            assert_eq!(*guard, [0, 1]);\n        }\n\n        {\n            let guard = rwlock.read().await;\n            assert_eq!(*guard, [0, 1]);\n        }\n\n        assert_eq!(*rwlock.read().await, [0, 1]);\n    }\n\n    #[futures_test::test]\n    async fn write_guard_releases_lock_when_dropped() {\n        let rwlock: RwLock<NoopRawMutex, [i32; 2]> = RwLock::new([0, 1]);\n\n        {\n            let mut guard = rwlock.write().await;\n            assert_eq!(*guard, [0, 1]);\n            guard[1] = 2;\n        }\n\n        {\n            let guard = rwlock.read().await;\n            assert_eq!(*guard, [0, 2]);\n        }\n\n        assert_eq!(*rwlock.read().await, [0, 2]);\n    }\n}\n"
  },
  {
    "path": "embassy-sync/src/semaphore.rs",
    "content": "//! A synchronization primitive for controlling access to a pool of resources.\nuse core::cell::{Cell, RefCell};\nuse core::convert::Infallible;\nuse core::future::{Future, poll_fn};\nuse core::task::{Poll, Waker};\n\nuse heapless::Deque;\n\nuse crate::blocking_mutex::Mutex;\nuse crate::blocking_mutex::raw::RawMutex;\nuse crate::waitqueue::WakerRegistration;\n\n/// An asynchronous semaphore.\n///\n/// A semaphore tracks a number of permits, typically representing a pool of shared resources.\n/// Users can acquire permits to synchronize access to those resources. The semaphore does not\n/// contain the resources themselves, only the count of available permits.\npub trait Semaphore: Sized {\n    /// The error returned when the semaphore is unable to acquire the requested permits.\n    type Error;\n\n    /// Asynchronously acquire one or more permits from the semaphore.\n    async fn acquire(&self, permits: usize) -> Result<SemaphoreReleaser<'_, Self>, Self::Error>;\n\n    /// Try to immediately acquire one or more permits from the semaphore.\n    fn try_acquire(&self, permits: usize) -> Option<SemaphoreReleaser<'_, Self>>;\n\n    /// Asynchronously acquire all permits controlled by the semaphore.\n    ///\n    /// This method will wait until at least `min` permits are available, then acquire all available permits\n    /// from the semaphore. Note that other tasks may have already acquired some permits which could be released\n    /// back to the semaphore at any time. The number of permits actually acquired may be determined by calling\n    /// [`SemaphoreReleaser::permits`].\n    async fn acquire_all(&self, min: usize) -> Result<SemaphoreReleaser<'_, Self>, Self::Error>;\n\n    /// Try to immediately acquire all available permits from the semaphore, if at least `min` permits are available.\n    fn try_acquire_all(&self, min: usize) -> Option<SemaphoreReleaser<'_, Self>>;\n\n    /// Release `permits` back to the semaphore, making them available to be acquired.\n    fn release(&self, permits: usize);\n\n    /// Reset the number of available permints in the semaphore to `permits`.\n    fn set(&self, permits: usize);\n}\n\n/// A representation of a number of acquired permits.\n///\n/// The acquired permits will be released back to the [`Semaphore`] when this is dropped.\n#[derive(Debug)]\npub struct SemaphoreReleaser<'a, S: Semaphore> {\n    semaphore: &'a S,\n    permits: usize,\n}\n\nimpl<'a, S: Semaphore> Drop for SemaphoreReleaser<'a, S> {\n    fn drop(&mut self) {\n        self.semaphore.release(self.permits);\n    }\n}\n\nimpl<'a, S: Semaphore> SemaphoreReleaser<'a, S> {\n    /// The number of acquired permits.\n    pub fn permits(&self) -> usize {\n        self.permits\n    }\n\n    /// Prevent the acquired permits from being released on drop.\n    ///\n    /// Returns the number of acquired permits.\n    pub fn disarm(self) -> usize {\n        let permits = self.permits;\n        core::mem::forget(self);\n        permits\n    }\n}\n\n/// A greedy [`Semaphore`] implementation.\n///\n/// Tasks can acquire permits as soon as they become available, even if another task\n/// is waiting on a larger number of permits.\npub struct GreedySemaphore<M: RawMutex> {\n    state: Mutex<M, Cell<SemaphoreState>>,\n}\n\nimpl<M: RawMutex> Default for GreedySemaphore<M> {\n    fn default() -> Self {\n        Self::new(0)\n    }\n}\n\nimpl<M: RawMutex> GreedySemaphore<M> {\n    /// Create a new `Semaphore`.\n    pub const fn new(permits: usize) -> Self {\n        Self {\n            state: Mutex::new(Cell::new(SemaphoreState {\n                permits,\n                waker: WakerRegistration::new(),\n            })),\n        }\n    }\n\n    #[cfg(test)]\n    fn permits(&self) -> usize {\n        self.state.lock(|cell| {\n            let state = cell.replace(SemaphoreState::EMPTY);\n            let permits = state.permits;\n            cell.replace(state);\n            permits\n        })\n    }\n\n    fn poll_acquire(\n        &self,\n        permits: usize,\n        acquire_all: bool,\n        waker: Option<&Waker>,\n    ) -> Poll<Result<SemaphoreReleaser<'_, Self>, Infallible>> {\n        self.state.lock(|cell| {\n            let mut state = cell.replace(SemaphoreState::EMPTY);\n            if let Some(permits) = state.take(permits, acquire_all) {\n                cell.set(state);\n                Poll::Ready(Ok(SemaphoreReleaser {\n                    semaphore: self,\n                    permits,\n                }))\n            } else {\n                if let Some(waker) = waker {\n                    state.register(waker);\n                }\n                cell.set(state);\n                Poll::Pending\n            }\n        })\n    }\n}\n\nimpl<M: RawMutex> Semaphore for GreedySemaphore<M> {\n    type Error = Infallible;\n\n    async fn acquire(&self, permits: usize) -> Result<SemaphoreReleaser<'_, Self>, Self::Error> {\n        poll_fn(|cx| self.poll_acquire(permits, false, Some(cx.waker()))).await\n    }\n\n    fn try_acquire(&self, permits: usize) -> Option<SemaphoreReleaser<'_, Self>> {\n        match self.poll_acquire(permits, false, None) {\n            Poll::Ready(Ok(n)) => Some(n),\n            _ => None,\n        }\n    }\n\n    async fn acquire_all(&self, min: usize) -> Result<SemaphoreReleaser<'_, Self>, Self::Error> {\n        poll_fn(|cx| self.poll_acquire(min, true, Some(cx.waker()))).await\n    }\n\n    fn try_acquire_all(&self, min: usize) -> Option<SemaphoreReleaser<'_, Self>> {\n        match self.poll_acquire(min, true, None) {\n            Poll::Ready(Ok(n)) => Some(n),\n            _ => None,\n        }\n    }\n\n    fn release(&self, permits: usize) {\n        if permits > 0 {\n            self.state.lock(|cell| {\n                let mut state = cell.replace(SemaphoreState::EMPTY);\n                state.permits += permits;\n                state.wake();\n                cell.set(state);\n            });\n        }\n    }\n\n    fn set(&self, permits: usize) {\n        self.state.lock(|cell| {\n            let mut state = cell.replace(SemaphoreState::EMPTY);\n            if permits > state.permits {\n                state.wake();\n            }\n            state.permits = permits;\n            cell.set(state);\n        });\n    }\n}\n\n#[derive(Debug)]\nstruct SemaphoreState {\n    permits: usize,\n    waker: WakerRegistration,\n}\n\nimpl SemaphoreState {\n    const EMPTY: SemaphoreState = SemaphoreState {\n        permits: 0,\n        waker: WakerRegistration::new(),\n    };\n\n    fn register(&mut self, w: &Waker) {\n        self.waker.register(w);\n    }\n\n    fn take(&mut self, mut permits: usize, acquire_all: bool) -> Option<usize> {\n        if self.permits < permits {\n            None\n        } else {\n            if acquire_all {\n                permits = self.permits;\n            }\n            self.permits -= permits;\n            Some(permits)\n        }\n    }\n\n    fn wake(&mut self) {\n        self.waker.wake();\n    }\n}\n\n/// A fair [`Semaphore`] implementation.\n///\n/// Tasks are allowed to acquire permits in FIFO order. A task waiting to acquire\n/// a large number of permits will prevent other tasks from acquiring any permits\n/// until its request is satisfied.\n///\n/// Up to `N` tasks may attempt to acquire permits concurrently. If additional\n/// tasks attempt to acquire a permit, a [`WaitQueueFull`] error will be returned.\n#[derive(Debug)]\npub struct FairSemaphore<M, const N: usize>\nwhere\n    M: RawMutex,\n{\n    state: Mutex<M, RefCell<FairSemaphoreState<N>>>,\n}\n\nimpl<M, const N: usize> Default for FairSemaphore<M, N>\nwhere\n    M: RawMutex,\n{\n    fn default() -> Self {\n        Self::new(0)\n    }\n}\n\nimpl<M, const N: usize> FairSemaphore<M, N>\nwhere\n    M: RawMutex,\n{\n    /// Create a new `FairSemaphore`.\n    pub const fn new(permits: usize) -> Self {\n        Self {\n            state: Mutex::new(RefCell::new(FairSemaphoreState::new(permits))),\n        }\n    }\n\n    #[cfg(test)]\n    fn permits(&self) -> usize {\n        self.state.lock(|cell| cell.borrow().permits)\n    }\n\n    fn poll_acquire(\n        &self,\n        permits: usize,\n        acquire_all: bool,\n        cx: Option<(&mut Option<usize>, &Waker)>,\n    ) -> Poll<Result<SemaphoreReleaser<'_, Self>, WaitQueueFull>> {\n        let ticket = cx.as_ref().map(|(x, _)| **x).unwrap_or(None);\n        self.state.lock(|cell| {\n            let mut state = cell.borrow_mut();\n            if let Some(permits) = state.take(ticket, permits, acquire_all) {\n                Poll::Ready(Ok(SemaphoreReleaser {\n                    semaphore: self,\n                    permits,\n                }))\n            } else if let Some((ticket_ref, waker)) = cx {\n                match state.register(ticket, waker) {\n                    Ok(ticket) => {\n                        *ticket_ref = Some(ticket);\n                        Poll::Pending\n                    }\n                    Err(err) => Poll::Ready(Err(err)),\n                }\n            } else {\n                Poll::Pending\n            }\n        })\n    }\n}\n\n/// An error indicating the [`FairSemaphore`]'s wait queue is full.\n#[derive(Debug, Clone, Copy, PartialEq, Eq)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub struct WaitQueueFull;\n\nimpl<M: RawMutex, const N: usize> Semaphore for FairSemaphore<M, N> {\n    type Error = WaitQueueFull;\n\n    fn acquire(&self, permits: usize) -> impl Future<Output = Result<SemaphoreReleaser<'_, Self>, Self::Error>> {\n        FairAcquire {\n            sema: self,\n            permits,\n            ticket: None,\n        }\n    }\n\n    fn try_acquire(&self, permits: usize) -> Option<SemaphoreReleaser<'_, Self>> {\n        match self.poll_acquire(permits, false, None) {\n            Poll::Ready(Ok(x)) => Some(x),\n            _ => None,\n        }\n    }\n\n    fn acquire_all(&self, min: usize) -> impl Future<Output = Result<SemaphoreReleaser<'_, Self>, Self::Error>> {\n        FairAcquireAll {\n            sema: self,\n            min,\n            ticket: None,\n        }\n    }\n\n    fn try_acquire_all(&self, min: usize) -> Option<SemaphoreReleaser<'_, Self>> {\n        match self.poll_acquire(min, true, None) {\n            Poll::Ready(Ok(x)) => Some(x),\n            _ => None,\n        }\n    }\n\n    fn release(&self, permits: usize) {\n        if permits > 0 {\n            self.state.lock(|cell| {\n                let mut state = cell.borrow_mut();\n                state.permits += permits;\n                state.wake();\n            });\n        }\n    }\n\n    fn set(&self, permits: usize) {\n        self.state.lock(|cell| {\n            let mut state = cell.borrow_mut();\n            if permits > state.permits {\n                state.wake();\n            }\n            state.permits = permits;\n        });\n    }\n}\n\n#[derive(Debug)]\nstruct FairAcquire<'a, M: RawMutex, const N: usize> {\n    sema: &'a FairSemaphore<M, N>,\n    permits: usize,\n    ticket: Option<usize>,\n}\n\nimpl<'a, M: RawMutex, const N: usize> Drop for FairAcquire<'a, M, N> {\n    fn drop(&mut self) {\n        self.sema\n            .state\n            .lock(|cell| cell.borrow_mut().cancel(self.ticket.take()));\n    }\n}\n\nimpl<'a, M: RawMutex, const N: usize> core::future::Future for FairAcquire<'a, M, N> {\n    type Output = Result<SemaphoreReleaser<'a, FairSemaphore<M, N>>, WaitQueueFull>;\n\n    fn poll(mut self: core::pin::Pin<&mut Self>, cx: &mut core::task::Context<'_>) -> Poll<Self::Output> {\n        self.sema\n            .poll_acquire(self.permits, false, Some((&mut self.ticket, cx.waker())))\n    }\n}\n\n#[derive(Debug)]\nstruct FairAcquireAll<'a, M: RawMutex, const N: usize> {\n    sema: &'a FairSemaphore<M, N>,\n    min: usize,\n    ticket: Option<usize>,\n}\n\nimpl<'a, M: RawMutex, const N: usize> Drop for FairAcquireAll<'a, M, N> {\n    fn drop(&mut self) {\n        self.sema\n            .state\n            .lock(|cell| cell.borrow_mut().cancel(self.ticket.take()));\n    }\n}\n\nimpl<'a, M: RawMutex, const N: usize> core::future::Future for FairAcquireAll<'a, M, N> {\n    type Output = Result<SemaphoreReleaser<'a, FairSemaphore<M, N>>, WaitQueueFull>;\n\n    fn poll(mut self: core::pin::Pin<&mut Self>, cx: &mut core::task::Context<'_>) -> Poll<Self::Output> {\n        self.sema\n            .poll_acquire(self.min, true, Some((&mut self.ticket, cx.waker())))\n    }\n}\n\n#[derive(Debug)]\nstruct FairSemaphoreState<const N: usize> {\n    permits: usize,\n    next_ticket: usize,\n    wakers: Deque<Option<Waker>, N>,\n}\n\nimpl<const N: usize> FairSemaphoreState<N> {\n    /// Create a new empty instance\n    const fn new(permits: usize) -> Self {\n        Self {\n            permits,\n            next_ticket: 0,\n            wakers: Deque::new(),\n        }\n    }\n\n    /// Register a waker. If the queue is full the function returns an error\n    fn register(&mut self, ticket: Option<usize>, w: &Waker) -> Result<usize, WaitQueueFull> {\n        self.pop_canceled();\n\n        match ticket {\n            None => {\n                let ticket = self.next_ticket.wrapping_add(self.wakers.len());\n                self.wakers.push_back(Some(w.clone())).or(Err(WaitQueueFull))?;\n                Ok(ticket)\n            }\n            Some(ticket) => {\n                self.set_waker(ticket, Some(w.clone()));\n                Ok(ticket)\n            }\n        }\n    }\n\n    fn cancel(&mut self, ticket: Option<usize>) {\n        if let Some(ticket) = ticket {\n            self.set_waker(ticket, None);\n        }\n    }\n\n    fn set_waker(&mut self, ticket: usize, waker: Option<Waker>) {\n        let i = ticket.wrapping_sub(self.next_ticket);\n        if i < self.wakers.len() {\n            let (a, b) = self.wakers.as_mut_slices();\n            let x = if i < a.len() { &mut a[i] } else { &mut b[i - a.len()] };\n            *x = waker;\n        }\n    }\n\n    fn take(&mut self, ticket: Option<usize>, mut permits: usize, acquire_all: bool) -> Option<usize> {\n        self.pop_canceled();\n\n        if permits > self.permits {\n            return None;\n        }\n\n        match ticket {\n            Some(n) if n != self.next_ticket => return None,\n            None if !self.wakers.is_empty() => return None,\n            _ => (),\n        }\n\n        if acquire_all {\n            permits = self.permits;\n        }\n        self.permits -= permits;\n\n        if ticket.is_some() {\n            self.pop();\n            if self.permits > 0 {\n                self.wake();\n            }\n        }\n\n        Some(permits)\n    }\n\n    fn pop_canceled(&mut self) {\n        while let Some(None) = self.wakers.front() {\n            self.pop();\n        }\n    }\n\n    /// Panics if `self.wakers` is empty\n    fn pop(&mut self) {\n        self.wakers.pop_front().unwrap();\n        self.next_ticket = self.next_ticket.wrapping_add(1);\n    }\n\n    fn wake(&mut self) {\n        self.pop_canceled();\n\n        if let Some(Some(waker)) = self.wakers.front() {\n            waker.wake_by_ref();\n        }\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    mod greedy {\n        use core::pin::pin;\n\n        use futures_util::poll;\n\n        use super::super::*;\n        use crate::blocking_mutex::raw::NoopRawMutex;\n\n        #[test]\n        fn try_acquire() {\n            let semaphore = GreedySemaphore::<NoopRawMutex>::new(3);\n\n            let a = semaphore.try_acquire(1).unwrap();\n            assert_eq!(a.permits(), 1);\n            assert_eq!(semaphore.permits(), 2);\n\n            core::mem::drop(a);\n            assert_eq!(semaphore.permits(), 3);\n        }\n\n        #[test]\n        fn disarm() {\n            let semaphore = GreedySemaphore::<NoopRawMutex>::new(3);\n\n            let a = semaphore.try_acquire(1).unwrap();\n            assert_eq!(a.disarm(), 1);\n            assert_eq!(semaphore.permits(), 2);\n        }\n\n        #[futures_test::test]\n        async fn acquire() {\n            let semaphore = GreedySemaphore::<NoopRawMutex>::new(3);\n\n            let a = semaphore.acquire(1).await.unwrap();\n            assert_eq!(a.permits(), 1);\n            assert_eq!(semaphore.permits(), 2);\n\n            core::mem::drop(a);\n            assert_eq!(semaphore.permits(), 3);\n        }\n\n        #[test]\n        fn try_acquire_all() {\n            let semaphore = GreedySemaphore::<NoopRawMutex>::new(3);\n\n            let a = semaphore.try_acquire_all(1).unwrap();\n            assert_eq!(a.permits(), 3);\n            assert_eq!(semaphore.permits(), 0);\n        }\n\n        #[futures_test::test]\n        async fn acquire_all() {\n            let semaphore = GreedySemaphore::<NoopRawMutex>::new(3);\n\n            let a = semaphore.acquire_all(1).await.unwrap();\n            assert_eq!(a.permits(), 3);\n            assert_eq!(semaphore.permits(), 0);\n        }\n\n        #[test]\n        fn release() {\n            let semaphore = GreedySemaphore::<NoopRawMutex>::new(3);\n            assert_eq!(semaphore.permits(), 3);\n            semaphore.release(2);\n            assert_eq!(semaphore.permits(), 5);\n        }\n\n        #[test]\n        fn set() {\n            let semaphore = GreedySemaphore::<NoopRawMutex>::new(3);\n            assert_eq!(semaphore.permits(), 3);\n            semaphore.set(2);\n            assert_eq!(semaphore.permits(), 2);\n        }\n\n        #[test]\n        fn contested() {\n            let semaphore = GreedySemaphore::<NoopRawMutex>::new(3);\n\n            let a = semaphore.try_acquire(1).unwrap();\n            let b = semaphore.try_acquire(3);\n            assert!(b.is_none());\n\n            core::mem::drop(a);\n\n            let b = semaphore.try_acquire(3);\n            assert!(b.is_some());\n        }\n\n        #[futures_test::test]\n        async fn greedy() {\n            let semaphore = GreedySemaphore::<NoopRawMutex>::new(3);\n\n            let a = semaphore.try_acquire(1).unwrap();\n\n            let b_fut = semaphore.acquire(3);\n            let mut b_fut = pin!(b_fut);\n            let b = poll!(b_fut.as_mut());\n            assert!(b.is_pending());\n\n            // Succeed even through `b` is waiting\n            let c = semaphore.try_acquire(1);\n            assert!(c.is_some());\n\n            let b = poll!(b_fut.as_mut());\n            assert!(b.is_pending());\n\n            core::mem::drop(a);\n\n            let b = poll!(b_fut.as_mut());\n            assert!(b.is_pending());\n\n            core::mem::drop(c);\n\n            let b = poll!(b_fut.as_mut());\n            assert!(b.is_ready());\n        }\n    }\n\n    mod fair {\n        use core::pin::pin;\n        use core::time::Duration;\n\n        use futures_executor::ThreadPool;\n        use futures_timer::Delay;\n        use futures_util::poll;\n        use futures_util::task::SpawnExt;\n        use static_cell::StaticCell;\n\n        use super::super::*;\n        use crate::blocking_mutex::raw::{CriticalSectionRawMutex, NoopRawMutex};\n\n        #[test]\n        fn try_acquire() {\n            let semaphore = FairSemaphore::<NoopRawMutex, 2>::new(3);\n\n            let a = semaphore.try_acquire(1).unwrap();\n            assert_eq!(a.permits(), 1);\n            assert_eq!(semaphore.permits(), 2);\n\n            core::mem::drop(a);\n            assert_eq!(semaphore.permits(), 3);\n        }\n\n        #[test]\n        fn disarm() {\n            let semaphore = FairSemaphore::<NoopRawMutex, 2>::new(3);\n\n            let a = semaphore.try_acquire(1).unwrap();\n            assert_eq!(a.disarm(), 1);\n            assert_eq!(semaphore.permits(), 2);\n        }\n\n        #[futures_test::test]\n        async fn acquire() {\n            let semaphore = FairSemaphore::<NoopRawMutex, 2>::new(3);\n\n            let a = semaphore.acquire(1).await.unwrap();\n            assert_eq!(a.permits(), 1);\n            assert_eq!(semaphore.permits(), 2);\n\n            core::mem::drop(a);\n            assert_eq!(semaphore.permits(), 3);\n        }\n\n        #[test]\n        fn try_acquire_all() {\n            let semaphore = FairSemaphore::<NoopRawMutex, 2>::new(3);\n\n            let a = semaphore.try_acquire_all(1).unwrap();\n            assert_eq!(a.permits(), 3);\n            assert_eq!(semaphore.permits(), 0);\n        }\n\n        #[futures_test::test]\n        async fn acquire_all() {\n            let semaphore = FairSemaphore::<NoopRawMutex, 2>::new(3);\n\n            let a = semaphore.acquire_all(1).await.unwrap();\n            assert_eq!(a.permits(), 3);\n            assert_eq!(semaphore.permits(), 0);\n        }\n\n        #[test]\n        fn release() {\n            let semaphore = FairSemaphore::<NoopRawMutex, 2>::new(3);\n            assert_eq!(semaphore.permits(), 3);\n            semaphore.release(2);\n            assert_eq!(semaphore.permits(), 5);\n        }\n\n        #[test]\n        fn set() {\n            let semaphore = FairSemaphore::<NoopRawMutex, 2>::new(3);\n            assert_eq!(semaphore.permits(), 3);\n            semaphore.set(2);\n            assert_eq!(semaphore.permits(), 2);\n        }\n\n        #[test]\n        fn contested() {\n            let semaphore = FairSemaphore::<NoopRawMutex, 2>::new(3);\n\n            let a = semaphore.try_acquire(1).unwrap();\n            let b = semaphore.try_acquire(3);\n            assert!(b.is_none());\n\n            core::mem::drop(a);\n\n            let b = semaphore.try_acquire(3);\n            assert!(b.is_some());\n        }\n\n        #[futures_test::test]\n        async fn fairness() {\n            let semaphore = FairSemaphore::<NoopRawMutex, 2>::new(3);\n\n            let a = semaphore.try_acquire(1);\n            assert!(a.is_some());\n\n            let b_fut = semaphore.acquire(3);\n            let mut b_fut = pin!(b_fut);\n            let b = poll!(b_fut.as_mut()); // Poll `b_fut` once so it is registered\n            assert!(b.is_pending());\n\n            let c = semaphore.try_acquire(1);\n            assert!(c.is_none());\n\n            let c_fut = semaphore.acquire(1);\n            let mut c_fut = pin!(c_fut);\n            let c = poll!(c_fut.as_mut()); // Poll `c_fut` once so it is registered\n            assert!(c.is_pending()); // `c` is blocked behind `b`\n\n            let d = semaphore.acquire(1).await;\n            assert!(matches!(d, Err(WaitQueueFull)));\n\n            core::mem::drop(a);\n\n            let c = poll!(c_fut.as_mut());\n            assert!(c.is_pending()); // `c` is still blocked behind `b`\n\n            let b = poll!(b_fut.as_mut());\n            assert!(b.is_ready());\n\n            let c = poll!(c_fut.as_mut());\n            assert!(c.is_pending()); // `c` is still blocked behind `b`\n\n            core::mem::drop(b);\n\n            let c = poll!(c_fut.as_mut());\n            assert!(c.is_ready());\n        }\n\n        #[futures_test::test]\n        async fn wakers() {\n            let executor = ThreadPool::new().unwrap();\n\n            static SEMAPHORE: StaticCell<FairSemaphore<CriticalSectionRawMutex, 2>> = StaticCell::new();\n            let semaphore = &*SEMAPHORE.init(FairSemaphore::new(3));\n\n            let a = semaphore.try_acquire(2);\n            assert!(a.is_some());\n\n            let b_task = executor\n                .spawn_with_handle(async move { semaphore.acquire(2).await })\n                .unwrap();\n            while semaphore.state.lock(|x| x.borrow().wakers.is_empty()) {\n                Delay::new(Duration::from_millis(50)).await;\n            }\n\n            let c_task = executor\n                .spawn_with_handle(async move { semaphore.acquire(1).await })\n                .unwrap();\n\n            core::mem::drop(a);\n\n            let b = b_task.await.unwrap();\n            assert_eq!(b.permits(), 2);\n\n            let c = c_task.await.unwrap();\n            assert_eq!(c.permits(), 1);\n        }\n    }\n}\n"
  },
  {
    "path": "embassy-sync/src/signal.rs",
    "content": "//! A synchronization primitive for passing the latest value to a task.\nuse core::cell::Cell;\nuse core::future::{Future, poll_fn};\nuse core::task::{Context, Poll, Waker};\n\nuse crate::blocking_mutex::Mutex;\nuse crate::blocking_mutex::raw::RawMutex;\n\n/// Single-slot signaling primitive for a _single_ consumer.\n///\n/// This is similar to a [`Channel`](crate::channel::Channel) with a buffer size of 1, except\n/// \"sending\" to it (calling [`Signal::signal`]) when full will overwrite the previous value instead\n/// of waiting for the receiver to pop the previous value.\n///\n/// It is useful for sending data between tasks when the receiver only cares about\n/// the latest data, and therefore it's fine to \"lose\" messages. This is often the case for \"state\"\n/// updates.\n///\n/// For more advanced use cases, you might want to use [`Channel`](crate::channel::Channel) instead.\n/// For multiple consumers, use [`Watch`](crate::watch::Watch) instead.\n///\n/// Signals are generally declared as `static`s and then borrowed as required.\n///\n/// ```\n/// use embassy_sync::signal::Signal;\n/// use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;\n///\n/// enum SomeCommand {\n///   On,\n///   Off,\n/// }\n///\n/// static SOME_SIGNAL: Signal<CriticalSectionRawMutex, SomeCommand> = Signal::new();\n/// ```\npub struct Signal<M, T>\nwhere\n    M: RawMutex,\n{\n    state: Mutex<M, Cell<State<T>>>,\n}\n\n#[derive(Debug)]\nenum State<T> {\n    None,\n    Waiting(Waker),\n    Signaled(T),\n}\n\nimpl<M, T> Signal<M, T>\nwhere\n    M: RawMutex,\n{\n    /// Create a new `Signal`.\n    pub const fn new() -> Self {\n        Self {\n            state: Mutex::new(Cell::new(State::None)),\n        }\n    }\n}\n\nimpl<M, T> Default for Signal<M, T>\nwhere\n    M: RawMutex,\n{\n    fn default() -> Self {\n        Self::new()\n    }\n}\n\nimpl<M, T> Signal<M, T>\nwhere\n    M: RawMutex,\n{\n    /// Mark this Signal as signaled.\n    pub fn signal(&self, val: T) {\n        self.state.lock(|cell| {\n            let state = cell.replace(State::Signaled(val));\n            if let State::Waiting(waker) = state {\n                waker.wake();\n            }\n        })\n    }\n\n    /// Remove the queued value in this `Signal`, if any.\n    pub fn reset(&self) {\n        self.try_take();\n    }\n\n    fn poll_wait(&self, cx: &mut Context<'_>) -> Poll<T> {\n        self.state.lock(|cell| {\n            let state = cell.replace(State::None);\n            match state {\n                State::None => {\n                    cell.set(State::Waiting(cx.waker().clone()));\n                    Poll::Pending\n                }\n                State::Waiting(w) if w.will_wake(cx.waker()) => {\n                    cell.set(State::Waiting(w));\n                    Poll::Pending\n                }\n                State::Waiting(w) => {\n                    cell.set(State::Waiting(cx.waker().clone()));\n                    w.wake();\n                    Poll::Pending\n                }\n                State::Signaled(res) => Poll::Ready(res),\n            }\n        })\n    }\n\n    /// Future that completes when this Signal has been signaled, taking the value out of the signal.\n    ///\n    /// The returned Future is cancel-safe. No value will be lost even if it isn't polled to completion.\n    pub fn wait(&self) -> impl Future<Output = T> + '_ {\n        poll_fn(move |cx| self.poll_wait(cx))\n    }\n\n    /// non-blocking method to try and take the signal value.\n    pub fn try_take(&self) -> Option<T> {\n        self.state.lock(|cell| {\n            let state = cell.replace(State::None);\n            match state {\n                State::Signaled(res) => Some(res),\n                state => {\n                    cell.set(state);\n                    None\n                }\n            }\n        })\n    }\n\n    /// non-blocking method to check whether this signal has been signaled. This does not clear the signal.  \n    pub fn signaled(&self) -> bool {\n        self.state.lock(|cell| {\n            let state = cell.replace(State::None);\n\n            let res = matches!(state, State::Signaled(_));\n\n            cell.set(state);\n\n            res\n        })\n    }\n}\n"
  },
  {
    "path": "embassy-sync/src/waitqueue/atomic_waker.rs",
    "content": "use core::cell::Cell;\nuse core::task::Waker;\n\nuse crate::blocking_mutex::Mutex;\nuse crate::blocking_mutex::raw::{CriticalSectionRawMutex, RawMutex};\n\n/// Utility struct to register and wake a waker.\n/// If a waker is registered, registering another waker will replace the previous one without waking it.\n/// Intended to wake a task from an interrupt. Therefore, it is generally not expected,\n/// that multiple tasks register try to register a waker simultaneously.\npub struct GenericAtomicWaker<M: RawMutex> {\n    waker: Mutex<M, Cell<Option<Waker>>>,\n}\n\nimpl<M: RawMutex> GenericAtomicWaker<M> {\n    /// Create a new `AtomicWaker`.\n    pub const fn new(mutex: M) -> Self {\n        Self {\n            waker: Mutex::const_new(mutex, Cell::new(None)),\n        }\n    }\n\n    /// Register a waker. Overwrites the previous waker, if any.\n    pub fn register(&self, w: &Waker) {\n        self.waker.lock(|cell| {\n            cell.set(match cell.replace(None) {\n                Some(w2) if (w2.will_wake(w)) => Some(w2),\n                _ => Some(w.clone()),\n            })\n        })\n    }\n\n    /// Wake the registered waker, if any.\n    pub fn wake(&self) {\n        self.waker.lock(|cell| {\n            if let Some(w) = cell.replace(None) {\n                w.wake_by_ref();\n                cell.set(Some(w));\n            }\n        })\n    }\n}\n\n/// Utility struct to register and wake a waker.\npub struct AtomicWaker {\n    waker: GenericAtomicWaker<CriticalSectionRawMutex>,\n}\n\nimpl AtomicWaker {\n    /// Create a new `AtomicWaker`.\n    pub const fn new() -> Self {\n        Self {\n            waker: GenericAtomicWaker::new(CriticalSectionRawMutex::new()),\n        }\n    }\n\n    /// Register a waker. Overwrites the previous waker, if any.\n    pub fn register(&self, w: &Waker) {\n        self.waker.register(w);\n    }\n\n    /// Wake the registered waker, if any.\n    pub fn wake(&self) {\n        self.waker.wake();\n    }\n}\n"
  },
  {
    "path": "embassy-sync/src/waitqueue/atomic_waker_turbo.rs",
    "content": "use core::ptr;\nuse core::ptr::NonNull;\nuse core::sync::atomic::{AtomicPtr, Ordering};\nuse core::task::Waker;\n\n/// Utility struct to register and wake a waker.\n/// If a waker is registered, registering another waker will replace the previous one without waking it.\n/// The intended use case is to wake tasks from interrupts. Therefore, it is generally not expected,\n/// that multiple tasks register try to register a waker simultaneously.\n#[derive(Debug)]\npub struct AtomicWaker {\n    waker: AtomicPtr<()>,\n}\n\nimpl AtomicWaker {\n    /// Create a new `AtomicWaker`.\n    pub const fn new() -> Self {\n        Self {\n            waker: AtomicPtr::new(ptr::null_mut()),\n        }\n    }\n\n    /// Register a waker. Overwrites the previous waker, if any.\n    pub fn register(&self, w: &Waker) {\n        self.waker.store(w.as_turbo_ptr().as_ptr() as _, Ordering::Release);\n    }\n\n    /// Wake the registered waker, if any.\n    pub fn wake(&self) {\n        if let Some(ptr) = NonNull::new(self.waker.load(Ordering::Acquire)) {\n            unsafe { Waker::from_turbo_ptr(ptr) }.wake();\n        }\n    }\n}\n"
  },
  {
    "path": "embassy-sync/src/waitqueue/mod.rs",
    "content": "//! Async low-level wait queues\n\n#[cfg_attr(feature = \"turbowakers\", path = \"atomic_waker_turbo.rs\")]\nmod atomic_waker;\npub use atomic_waker::*;\n\nmod waker_registration;\npub use waker_registration::*;\n\nmod multi_waker;\npub use multi_waker::*;\n"
  },
  {
    "path": "embassy-sync/src/waitqueue/multi_waker.rs",
    "content": "use core::task::Waker;\n\nuse heapless::Vec;\n\n/// Utility struct to register and wake multiple wakers.\n/// Queue of wakers with a maximum length of `N`.\n/// Intended for waking multiple tasks.\n#[derive(Debug)]\npub struct MultiWakerRegistration<const N: usize> {\n    wakers: Vec<Waker, N>,\n}\n\nimpl<const N: usize> MultiWakerRegistration<N> {\n    /// Create a new empty instance\n    pub const fn new() -> Self {\n        Self { wakers: Vec::new() }\n    }\n\n    /// Register a waker.\n    ///\n    /// If the buffer is full, [wakes all the wakers](Self::wake), clears its buffer and registers the waker.\n    pub fn register(&mut self, w: &Waker) {\n        // If we already have some waker that wakes the same task as `w`, do nothing.\n        // This avoids cloning wakers, and avoids unnecessary mass-wakes.\n        for w2 in &self.wakers {\n            if w.will_wake(w2) {\n                return;\n            }\n        }\n\n        if self.wakers.is_full() {\n            // All waker slots were full. It's a bit inefficient, but we can wake everything.\n            // Any future that is still active will simply reregister.\n            // This won't happen a lot, so it's ok.\n            self.wake();\n        }\n\n        if self.wakers.push(w.clone()).is_err() {\n            // This can't happen unless N=0\n            // (Either `wakers` wasn't full, or it was in which case `wake()` empied it)\n            panic!(\"tried to push a waker to a zero-length MultiWakerRegistration\")\n        }\n    }\n\n    /// Wake all registered wakers. This clears the buffer\n    pub fn wake(&mut self) {\n        // heapless::Vec has no `drain()`, do it unsafely ourselves...\n\n        // First set length to 0, without dropping the contents.\n        // This is necessary for soundness: if wake() panics and we're using panic=unwind.\n        // Setting len=0 upfront ensures other code can't observe the vec in an inconsistent state.\n        // (it'll leak wakers, but that's not UB)\n        let len = self.wakers.len();\n        unsafe { self.wakers.set_len(0) }\n\n        for i in 0..len {\n            // Move a waker out of the vec.\n            let waker = unsafe { self.wakers.as_mut_ptr().add(i).read() };\n            // Wake it by value, which consumes (drops) it.\n            waker.wake();\n        }\n    }\n}\n"
  },
  {
    "path": "embassy-sync/src/waitqueue/waker_registration.rs",
    "content": "use core::mem;\nuse core::task::Waker;\n\n/// Utility struct to register and wake a waker.\n/// If a waker is registered, registering another waker will replace the previous one.\n/// The previous waker will be woken in this case, giving it a chance to reregister itself.\n/// Although it is possible to wake multiple tasks this way,\n/// this will cause them to wake each other in a loop registering themselves.\n#[derive(Debug, Default)]\npub struct WakerRegistration {\n    waker: Option<Waker>,\n}\n\nimpl WakerRegistration {\n    /// Create a new `WakerRegistration`.\n    pub const fn new() -> Self {\n        Self { waker: None }\n    }\n\n    /// Register a waker. Overwrites the previous waker, if any.\n    pub fn register(&mut self, w: &Waker) {\n        match self.waker {\n            // Optimization: If both the old and new Wakers wake the same task, we can simply\n            // keep the old waker, skipping the clone. (In most executor implementations,\n            // cloning a waker is somewhat expensive, comparable to cloning an Arc).\n            Some(ref w2) if (w2.will_wake(w)) => {}\n            _ => {\n                // clone the new waker and store it\n                if let Some(old_waker) = mem::replace(&mut self.waker, Some(w.clone())) {\n                    // We had a waker registered for another task. Wake it, so the other task can\n                    // reregister itself if it's still interested.\n                    //\n                    // If two tasks are waiting on the same thing concurrently, this will cause them\n                    // to wake each other in a loop fighting over this WakerRegistration. This wastes\n                    // CPU but things will still work.\n                    //\n                    // If the user wants to have two tasks waiting on the same thing they should use\n                    // a more appropriate primitive that can store multiple wakers.\n                    old_waker.wake()\n                }\n            }\n        }\n    }\n\n    /// Wake the registered waker, if any.\n    pub fn wake(&mut self) {\n        if let Some(w) = self.waker.take() {\n            w.wake()\n        }\n    }\n\n    /// Returns true if a waker is currently registered\n    pub fn occupied(&self) -> bool {\n        self.waker.is_some()\n    }\n}\n"
  },
  {
    "path": "embassy-sync/src/watch.rs",
    "content": "//! A synchronization primitive for passing the latest value to **multiple** receivers.\n\nuse core::cell::RefCell;\nuse core::future::{Future, poll_fn};\nuse core::marker::PhantomData;\nuse core::ops::{Deref, DerefMut};\nuse core::task::{Context, Poll};\n\nuse crate::blocking_mutex::Mutex;\nuse crate::blocking_mutex::raw::RawMutex;\nuse crate::waitqueue::MultiWakerRegistration;\n\n/// The `Watch` is a single-slot signaling primitive that allows _multiple_ (`N`) receivers to concurrently await\n/// changes to the value. Unlike a [`Signal`](crate::signal::Signal), `Watch` supports multiple receivers,\n/// and unlike a [`PubSubChannel`](crate::pubsub::PubSubChannel), `Watch` immediately overwrites the previous\n/// value when a new one is sent, without waiting for all receivers to read the previous value.\n///\n/// This makes `Watch` particularly useful when a single task updates a value or \"state\", and multiple other tasks\n/// need to be notified about changes to this value asynchronously. Receivers may \"lose\" stale values, as they are\n/// always provided with the latest value.\n///\n/// Typically, `Watch` instances are declared as `static`, and a [`Sender`] and [`Receiver`]\n/// (or [`DynSender`] and/or [`DynReceiver`]) are obtained where relevant. An [`AnonReceiver`]\n/// and [`DynAnonReceiver`] are also available, which do not increase the receiver count for the\n/// channel, and unwrapping is therefore not required, but it is not possible to `.await` the channel.\n/// ```\n///\n/// use futures_executor::block_on;\n/// use embassy_sync::watch::Watch;\n/// use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;\n///\n/// let f = async {\n///\n/// static WATCH: Watch<CriticalSectionRawMutex, u8, 2> = Watch::new();\n///\n/// // Obtain receivers and sender\n/// let mut rcv0 = WATCH.receiver().unwrap();\n/// let mut rcv1 = WATCH.dyn_receiver().unwrap();\n/// let mut snd = WATCH.sender();\n///\n/// // No more receivers, and no update\n/// assert!(WATCH.receiver().is_none());\n/// assert_eq!(rcv1.try_changed(), None);\n///\n/// snd.send(10);\n///\n/// // Receive the new value (async or try)\n/// assert_eq!(rcv0.changed().await, 10);\n/// assert_eq!(rcv1.try_changed(), Some(10));\n///\n/// // No update\n/// assert_eq!(rcv0.try_changed(), None);\n/// assert_eq!(rcv1.try_changed(), None);\n///\n/// snd.send(20);\n///\n/// // Using `get` marks the value as seen\n/// assert_eq!(rcv1.get().await, 20);\n/// assert_eq!(rcv1.try_changed(), None);\n///\n/// // But `get` also returns when unchanged\n/// assert_eq!(rcv1.get().await, 20);\n/// assert_eq!(rcv1.get().await, 20);\n///\n/// };\n/// block_on(f);\n/// ```\n#[derive(Debug)]\npub struct Watch<M: RawMutex, T: Clone, const N: usize> {\n    mutex: Mutex<M, RefCell<WatchState<T, N>>>,\n}\n\n#[derive(Debug)]\nstruct WatchState<T: Clone, const N: usize> {\n    data: Option<T>,\n    current_id: u64,\n    wakers: MultiWakerRegistration<N>,\n    receiver_count: usize,\n}\n\ntrait SealedWatchBehavior<T> {\n    /// Poll the `Watch` for the current value, making it as seen.\n    fn poll_get(&self, id: &mut u64, cx: &mut Context<'_>) -> Poll<T>;\n\n    /// Poll the `Watch` for the value if it matches the predicate function\n    /// `f`, making it as seen.\n    fn poll_get_and(&self, id: &mut u64, f: &mut dyn Fn(&T) -> bool, cx: &mut Context<'_>) -> Poll<T>;\n\n    /// Poll the `Watch` for a changed value, marking it as seen, if an id is given.\n    fn poll_changed(&self, id: &mut u64, cx: &mut Context<'_>) -> Poll<T>;\n\n    /// Tries to retrieve the value of the `Watch` if it has changed, marking it as seen.\n    fn try_changed(&self, id: &mut u64) -> Option<T>;\n\n    /// Poll the `Watch` for a changed value that matches the predicate function\n    /// `f`, marking it as seen.\n    fn poll_changed_and(&self, id: &mut u64, f: &mut dyn Fn(&T) -> bool, cx: &mut Context<'_>) -> Poll<T>;\n\n    /// Tries to retrieve the value of the `Watch` if it has changed and matches the\n    /// predicate function `f`, marking it as seen.\n    fn try_changed_and(&self, id: &mut u64, f: &mut dyn Fn(&T) -> bool) -> Option<T>;\n\n    /// Used when a receiver is dropped to decrement the receiver count.\n    ///\n    /// ## This method should not be called by the user.\n    fn drop_receiver(&self);\n\n    /// Clears the value of the `Watch`.\n    fn clear(&self);\n\n    /// Sends a new value to the `Watch`.\n    fn send(&self, val: T);\n\n    /// Modify the value of the `Watch` using a closure. Returns `false` if the\n    /// `Watch` does not already contain a value.\n    fn send_modify(&self, f: &mut dyn Fn(&mut Option<T>));\n\n    /// Modify the value of the `Watch` using a closure. Returns `false` if the\n    /// `Watch` does not already contain a value.\n    fn send_if_modified(&self, f: &mut dyn Fn(&mut Option<T>) -> bool);\n}\n\n/// A trait representing the 'inner' behavior of the `Watch`.\n#[allow(private_bounds)]\npub trait WatchBehavior<T: Clone>: SealedWatchBehavior<T> {\n    /// Tries to get the value of the `Watch`, marking it as seen, if an id is given.\n    fn try_get(&self, id: Option<&mut u64>) -> Option<T>;\n\n    /// Tries to get the value of the `Watch` if it matches the predicate function\n    /// `f`, marking it as seen.\n    fn try_get_and(&self, id: Option<&mut u64>, f: &mut dyn Fn(&T) -> bool) -> Option<T>;\n\n    /// Checks if the `Watch` is been initialized with a value.\n    fn contains_value(&self) -> bool;\n}\n\nimpl<M: RawMutex, T: Clone, const N: usize> SealedWatchBehavior<T> for Watch<M, T, N> {\n    fn poll_get(&self, id: &mut u64, cx: &mut Context<'_>) -> Poll<T> {\n        self.mutex.lock(|state| {\n            let mut s = state.borrow_mut();\n            match &s.data {\n                Some(data) => {\n                    *id = s.current_id;\n                    Poll::Ready(data.clone())\n                }\n                None => {\n                    s.wakers.register(cx.waker());\n                    Poll::Pending\n                }\n            }\n        })\n    }\n\n    fn poll_get_and(&self, id: &mut u64, f: &mut dyn Fn(&T) -> bool, cx: &mut Context<'_>) -> Poll<T> {\n        self.mutex.lock(|state| {\n            let mut s = state.borrow_mut();\n            match s.data {\n                Some(ref data) if f(data) => {\n                    *id = s.current_id;\n                    Poll::Ready(data.clone())\n                }\n                _ => {\n                    s.wakers.register(cx.waker());\n                    Poll::Pending\n                }\n            }\n        })\n    }\n\n    fn poll_changed(&self, id: &mut u64, cx: &mut Context<'_>) -> Poll<T> {\n        self.mutex.lock(|state| {\n            let mut s = state.borrow_mut();\n            match (&s.data, s.current_id > *id) {\n                (Some(data), true) => {\n                    *id = s.current_id;\n                    Poll::Ready(data.clone())\n                }\n                _ => {\n                    s.wakers.register(cx.waker());\n                    Poll::Pending\n                }\n            }\n        })\n    }\n\n    fn try_changed(&self, id: &mut u64) -> Option<T> {\n        self.mutex.lock(|state| {\n            let s = state.borrow();\n            match s.current_id > *id {\n                true => {\n                    *id = s.current_id;\n                    s.data.clone()\n                }\n                false => None,\n            }\n        })\n    }\n\n    fn poll_changed_and(&self, id: &mut u64, f: &mut dyn Fn(&T) -> bool, cx: &mut Context<'_>) -> Poll<T> {\n        self.mutex.lock(|state| {\n            let mut s = state.borrow_mut();\n            match (&s.data, s.current_id > *id) {\n                (Some(data), true) if f(data) => {\n                    *id = s.current_id;\n                    Poll::Ready(data.clone())\n                }\n                _ => {\n                    s.wakers.register(cx.waker());\n                    Poll::Pending\n                }\n            }\n        })\n    }\n\n    fn try_changed_and(&self, id: &mut u64, f: &mut dyn Fn(&T) -> bool) -> Option<T> {\n        self.mutex.lock(|state| {\n            let s = state.borrow();\n            match (&s.data, s.current_id > *id) {\n                (Some(data), true) if f(data) => {\n                    *id = s.current_id;\n                    s.data.clone()\n                }\n                _ => None,\n            }\n        })\n    }\n\n    fn drop_receiver(&self) {\n        self.mutex.lock(|state| {\n            let mut s = state.borrow_mut();\n            s.receiver_count -= 1;\n        })\n    }\n\n    fn clear(&self) {\n        self.mutex.lock(|state| {\n            let mut s = state.borrow_mut();\n            s.data = None;\n        })\n    }\n\n    fn send(&self, val: T) {\n        self.mutex.lock(|state| {\n            let mut s = state.borrow_mut();\n            s.data = Some(val);\n            s.current_id += 1;\n            s.wakers.wake();\n        })\n    }\n\n    fn send_modify(&self, f: &mut dyn Fn(&mut Option<T>)) {\n        self.mutex.lock(|state| {\n            let mut s = state.borrow_mut();\n            f(&mut s.data);\n            s.current_id += 1;\n            s.wakers.wake();\n        })\n    }\n\n    fn send_if_modified(&self, f: &mut dyn Fn(&mut Option<T>) -> bool) {\n        self.mutex.lock(|state| {\n            let mut s = state.borrow_mut();\n            if f(&mut s.data) {\n                s.current_id += 1;\n                s.wakers.wake();\n            }\n        })\n    }\n}\n\nimpl<M: RawMutex, T: Clone, const N: usize> WatchBehavior<T> for Watch<M, T, N> {\n    fn try_get(&self, id: Option<&mut u64>) -> Option<T> {\n        self.mutex.lock(|state| {\n            let s = state.borrow();\n            if let Some(id) = id {\n                *id = s.current_id;\n            }\n            s.data.clone()\n        })\n    }\n\n    fn try_get_and(&self, id: Option<&mut u64>, f: &mut dyn Fn(&T) -> bool) -> Option<T> {\n        self.mutex.lock(|state| {\n            let s = state.borrow();\n            match s.data {\n                Some(ref data) if f(data) => {\n                    if let Some(id) = id {\n                        *id = s.current_id;\n                    }\n                    Some(data.clone())\n                }\n                _ => None,\n            }\n        })\n    }\n\n    fn contains_value(&self) -> bool {\n        self.mutex.lock(|state| state.borrow().data.is_some())\n    }\n}\n\nimpl<M: RawMutex, T: Clone, const N: usize> Watch<M, T, N> {\n    /// Create a new `Watch` channel for `N` receivers.\n    pub const fn new() -> Self {\n        Self {\n            mutex: Mutex::new(RefCell::new(WatchState {\n                data: None,\n                current_id: 0,\n                wakers: MultiWakerRegistration::new(),\n                receiver_count: 0,\n            })),\n        }\n    }\n\n    /// Create a new `Watch` channel with default data.\n    pub const fn new_with(data: T) -> Self {\n        Self {\n            mutex: Mutex::new(RefCell::new(WatchState {\n                data: Some(data),\n                current_id: 0,\n                wakers: MultiWakerRegistration::new(),\n                receiver_count: 0,\n            })),\n        }\n    }\n\n    /// Create a new [`Sender`] for the `Watch`.\n    pub fn sender(&self) -> Sender<'_, M, T, N> {\n        Sender(Snd::new(self))\n    }\n\n    /// Create a new [`DynSender`] for the `Watch`.\n    pub fn dyn_sender(&self) -> DynSender<'_, T> {\n        DynSender(Snd::new(self))\n    }\n\n    /// Try to create a new [`Receiver`] for the `Watch`. If the\n    /// maximum number of receivers has been reached, `None` is returned.\n    pub fn receiver(&self) -> Option<Receiver<'_, M, T, N>> {\n        self.mutex.lock(|state| {\n            let mut s = state.borrow_mut();\n            if s.receiver_count < N {\n                s.receiver_count += 1;\n                Some(Receiver(Rcv::new(self, 0)))\n            } else {\n                None\n            }\n        })\n    }\n\n    /// Try to create a new [`DynReceiver`] for the `Watch`. If the\n    /// maximum number of receivers has been reached, `None` is returned.\n    pub fn dyn_receiver(&self) -> Option<DynReceiver<'_, T>> {\n        self.mutex.lock(|state| {\n            let mut s = state.borrow_mut();\n            if s.receiver_count < N {\n                s.receiver_count += 1;\n                Some(DynReceiver(Rcv::new(self, 0)))\n            } else {\n                None\n            }\n        })\n    }\n\n    /// Try to create a new [`AnonReceiver`] for the `Watch`.\n    pub fn anon_receiver(&self) -> AnonReceiver<'_, M, T, N> {\n        AnonReceiver(AnonRcv::new(self, 0))\n    }\n\n    /// Try to create a new [`DynAnonReceiver`] for the `Watch`.\n    pub fn dyn_anon_receiver(&self) -> DynAnonReceiver<'_, T> {\n        DynAnonReceiver(AnonRcv::new(self, 0))\n    }\n\n    /// Returns the message ID of the latest message sent to the `Watch`.\n    ///\n    /// This counter is monotonic, and is incremented every time a new message is sent.\n    pub fn get_msg_id(&self) -> u64 {\n        self.mutex.lock(|state| state.borrow().current_id)\n    }\n\n    /// Tries to get the value of the `Watch`.\n    pub fn try_get(&self) -> Option<T> {\n        WatchBehavior::try_get(self, None)\n    }\n\n    /// Tries to get the value of the `Watch` if it matches the predicate function `f`.\n    pub fn try_get_and<F>(&self, mut f: F) -> Option<T>\n    where\n        F: Fn(&T) -> bool,\n    {\n        WatchBehavior::try_get_and(self, None, &mut f)\n    }\n}\n\n/// A receiver can `.await` a change in the `Watch` value.\n#[derive(Debug)]\npub struct Snd<'a, T: Clone, W: WatchBehavior<T> + ?Sized> {\n    watch: &'a W,\n    _phantom: PhantomData<T>,\n}\n\nimpl<'a, T: Clone, W: WatchBehavior<T> + ?Sized> Clone for Snd<'a, T, W> {\n    fn clone(&self) -> Self {\n        Self {\n            watch: self.watch,\n            _phantom: PhantomData,\n        }\n    }\n}\n\nimpl<'a, T: Clone, W: WatchBehavior<T> + ?Sized> Snd<'a, T, W> {\n    /// Creates a new `Receiver` with a reference to the `Watch`.\n    fn new(watch: &'a W) -> Self {\n        Self {\n            watch,\n            _phantom: PhantomData,\n        }\n    }\n\n    /// Sends a new value to the `Watch`.\n    pub fn send(&self, val: T) {\n        self.watch.send(val)\n    }\n\n    /// Clears the value of the `Watch`.\n    /// This will cause calls to [`Rcv::get`] to be pending.\n    pub fn clear(&self) {\n        self.watch.clear()\n    }\n\n    /// Tries to retrieve the value of the `Watch`.\n    pub fn try_get(&self) -> Option<T> {\n        self.watch.try_get(None)\n    }\n\n    /// Tries to peek the current value of the `Watch` if it matches the predicate\n    /// function `f`.\n    pub fn try_get_and<F>(&self, mut f: F) -> Option<T>\n    where\n        F: Fn(&T) -> bool,\n    {\n        self.watch.try_get_and(None, &mut f)\n    }\n\n    /// Returns true if the `Watch` contains a value.\n    pub fn contains_value(&self) -> bool {\n        self.watch.contains_value()\n    }\n\n    /// Modify the value of the `Watch` using a closure.\n    pub fn send_modify<F>(&self, mut f: F)\n    where\n        F: Fn(&mut Option<T>),\n    {\n        self.watch.send_modify(&mut f)\n    }\n\n    /// Modify the value of the `Watch` using a closure. The closure must return\n    /// `true` if the value was modified, which notifies all receivers.\n    pub fn send_if_modified<F>(&self, mut f: F)\n    where\n        F: Fn(&mut Option<T>) -> bool,\n    {\n        self.watch.send_if_modified(&mut f)\n    }\n}\n\n/// A sender of a `Watch` channel.\n///\n/// For a simpler type definition, consider [`DynSender`] at the expense of\n/// some runtime performance due to dynamic dispatch.\n#[derive(Debug)]\npub struct Sender<'a, M: RawMutex, T: Clone, const N: usize>(Snd<'a, T, Watch<M, T, N>>);\n\nimpl<'a, M: RawMutex, T: Clone, const N: usize> Clone for Sender<'a, M, T, N> {\n    fn clone(&self) -> Self {\n        Self(self.0.clone())\n    }\n}\n\nimpl<'a, M: RawMutex, T: Clone, const N: usize> Sender<'a, M, T, N> {\n    /// Converts the `Sender` into a [`DynSender`].\n    pub fn as_dyn(self) -> DynSender<'a, T> {\n        DynSender(Snd::new(self.watch))\n    }\n}\n\nimpl<'a, M: RawMutex, T: Clone, const N: usize> Into<DynSender<'a, T>> for Sender<'a, M, T, N> {\n    fn into(self) -> DynSender<'a, T> {\n        self.as_dyn()\n    }\n}\n\nimpl<'a, M: RawMutex, T: Clone, const N: usize> Deref for Sender<'a, M, T, N> {\n    type Target = Snd<'a, T, Watch<M, T, N>>;\n\n    fn deref(&self) -> &Self::Target {\n        &self.0\n    }\n}\n\nimpl<'a, M: RawMutex, T: Clone, const N: usize> DerefMut for Sender<'a, M, T, N> {\n    fn deref_mut(&mut self) -> &mut Self::Target {\n        &mut self.0\n    }\n}\n\n/// A sender which holds a **dynamic** reference to a `Watch` channel.\n///\n/// This is an alternative to [`Sender`] with a simpler type definition,\npub struct DynSender<'a, T: Clone>(Snd<'a, T, dyn WatchBehavior<T> + 'a>);\n\nimpl<'a, T: Clone> Clone for DynSender<'a, T> {\n    fn clone(&self) -> Self {\n        Self(self.0.clone())\n    }\n}\n\nimpl<'a, T: Clone> Deref for DynSender<'a, T> {\n    type Target = Snd<'a, T, dyn WatchBehavior<T> + 'a>;\n\n    fn deref(&self) -> &Self::Target {\n        &self.0\n    }\n}\n\nimpl<'a, T: Clone> DerefMut for DynSender<'a, T> {\n    fn deref_mut(&mut self) -> &mut Self::Target {\n        &mut self.0\n    }\n}\n\n/// A receiver can `.await` a change in the `Watch` value.\npub struct Rcv<'a, T: Clone, W: WatchBehavior<T> + ?Sized> {\n    watch: &'a W,\n    at_id: u64,\n    _phantom: PhantomData<T>,\n}\n\nimpl<'a, T: Clone, W: WatchBehavior<T> + ?Sized> Rcv<'a, T, W> {\n    /// Creates a new `Receiver` with a reference to the `Watch`.\n    fn new(watch: &'a W, at_id: u64) -> Self {\n        Self {\n            watch,\n            at_id,\n            _phantom: PhantomData,\n        }\n    }\n\n    /// Returns the current value of the `Watch` once it is initialized, marking it as seen.\n    ///\n    /// **Note**: Futures do nothing unless you `.await` or poll them.\n    pub fn get(&mut self) -> impl Future<Output = T> + '_ {\n        poll_fn(|cx| self.watch.poll_get(&mut self.at_id, cx))\n    }\n\n    /// Tries to get the current value of the `Watch` without waiting, marking it as seen.\n    pub fn try_get(&mut self) -> Option<T> {\n        self.watch.try_get(Some(&mut self.at_id))\n    }\n\n    /// Returns the value of the `Watch` if it matches the predicate function `f`,\n    /// or waits for it to match, marking it as seen.\n    ///\n    /// **Note**: Futures do nothing unless you `.await` or poll them.\n    pub async fn get_and<F>(&mut self, mut f: F) -> T\n    where\n        F: Fn(&T) -> bool,\n    {\n        poll_fn(|cx| self.watch.poll_get_and(&mut self.at_id, &mut f, cx)).await\n    }\n\n    /// Tries to get the current value of the `Watch` if it matches the predicate\n    /// function `f` without waiting, marking it as seen.\n    pub fn try_get_and<F>(&mut self, mut f: F) -> Option<T>\n    where\n        F: Fn(&T) -> bool,\n    {\n        self.watch.try_get_and(Some(&mut self.at_id), &mut f)\n    }\n\n    /// Waits for the `Watch` to change and returns the new value, marking it as seen.\n    ///\n    /// **Note**: Futures do nothing unless you `.await` or poll them.\n    pub async fn changed(&mut self) -> T {\n        poll_fn(|cx| self.watch.poll_changed(&mut self.at_id, cx)).await\n    }\n\n    /// Tries to get the new value of the watch without waiting, marking it as seen.\n    pub fn try_changed(&mut self) -> Option<T> {\n        self.watch.try_changed(&mut self.at_id)\n    }\n\n    /// Waits for the `Watch` to change to a value which satisfies the predicate\n    /// function `f` and returns the new value, marking it as seen.\n    ///\n    /// **Note**: Futures do nothing unless you `.await` or poll them.\n    pub async fn changed_and<F>(&mut self, mut f: F) -> T\n    where\n        F: Fn(&T) -> bool,\n    {\n        poll_fn(|cx| self.watch.poll_changed_and(&mut self.at_id, &mut f, cx)).await\n    }\n\n    /// Tries to get the new value of the watch which satisfies the predicate\n    /// function `f` and returns the new value without waiting, marking it as seen.\n    pub fn try_changed_and<F>(&mut self, mut f: F) -> Option<T>\n    where\n        F: Fn(&T) -> bool,\n    {\n        self.watch.try_changed_and(&mut self.at_id, &mut f)\n    }\n\n    /// Checks if the `Watch` contains a value. If this returns true,\n    /// then awaiting [`Rcv::get`] will return immediately.\n    pub fn contains_value(&self) -> bool {\n        self.watch.contains_value()\n    }\n}\n\nimpl<'a, T: Clone, W: WatchBehavior<T> + ?Sized> Drop for Rcv<'a, T, W> {\n    fn drop(&mut self) {\n        self.watch.drop_receiver();\n    }\n}\n\n/// A anonymous receiver can NOT `.await` a change in the `Watch` value.\n#[derive(Debug)]\npub struct AnonRcv<'a, T: Clone, W: WatchBehavior<T> + ?Sized> {\n    watch: &'a W,\n    at_id: u64,\n    _phantom: PhantomData<T>,\n}\n\nimpl<'a, T: Clone, W: WatchBehavior<T> + ?Sized> AnonRcv<'a, T, W> {\n    /// Creates a new `Receiver` with a reference to the `Watch`.\n    fn new(watch: &'a W, at_id: u64) -> Self {\n        Self {\n            watch,\n            at_id,\n            _phantom: PhantomData,\n        }\n    }\n\n    /// Tries to get the current value of the `Watch` without waiting, marking it as seen.\n    pub fn try_get(&mut self) -> Option<T> {\n        self.watch.try_get(Some(&mut self.at_id))\n    }\n\n    /// Tries to get the current value of the `Watch` if it matches the predicate\n    /// function `f` without waiting, marking it as seen.\n    pub fn try_get_and<F>(&mut self, mut f: F) -> Option<T>\n    where\n        F: Fn(&T) -> bool,\n    {\n        self.watch.try_get_and(Some(&mut self.at_id), &mut f)\n    }\n\n    /// Tries to get the new value of the watch without waiting, marking it as seen.\n    pub fn try_changed(&mut self) -> Option<T> {\n        self.watch.try_changed(&mut self.at_id)\n    }\n\n    /// Tries to get the new value of the watch which satisfies the predicate\n    /// function `f` and returns the new value without waiting, marking it as seen.\n    pub fn try_changed_and<F>(&mut self, mut f: F) -> Option<T>\n    where\n        F: Fn(&T) -> bool,\n    {\n        self.watch.try_changed_and(&mut self.at_id, &mut f)\n    }\n\n    /// Checks if the `Watch` contains a value. If this returns true,\n    /// then awaiting [`Rcv::get`] will return immediately.\n    pub fn contains_value(&self) -> bool {\n        self.watch.contains_value()\n    }\n}\n\n/// A receiver of a `Watch` channel.\npub struct Receiver<'a, M: RawMutex, T: Clone, const N: usize>(Rcv<'a, T, Watch<M, T, N>>);\n\nimpl<'a, M: RawMutex, T: Clone, const N: usize> Receiver<'a, M, T, N> {\n    /// Converts the `Receiver` into a [`DynReceiver`].\n    pub fn as_dyn(self) -> DynReceiver<'a, T> {\n        let rcv = DynReceiver(Rcv::new(self.0.watch, self.at_id));\n        core::mem::forget(self); // Ensures the destructor is not called\n        rcv\n    }\n}\n\nimpl<'a, M: RawMutex, T: Clone, const N: usize> Into<DynReceiver<'a, T>> for Receiver<'a, M, T, N> {\n    fn into(self) -> DynReceiver<'a, T> {\n        self.as_dyn()\n    }\n}\n\nimpl<'a, M: RawMutex, T: Clone, const N: usize> Deref for Receiver<'a, M, T, N> {\n    type Target = Rcv<'a, T, Watch<M, T, N>>;\n\n    fn deref(&self) -> &Self::Target {\n        &self.0\n    }\n}\n\nimpl<'a, M: RawMutex, T: Clone, const N: usize> DerefMut for Receiver<'a, M, T, N> {\n    fn deref_mut(&mut self) -> &mut Self::Target {\n        &mut self.0\n    }\n}\n\n/// A receiver which holds a **dynamic** reference to a `Watch` channel.\n///\n/// This is an alternative to [`Receiver`] with a simpler type definition, at the expense of\n/// some runtime performance due to dynamic dispatch.\npub struct DynReceiver<'a, T: Clone>(Rcv<'a, T, dyn WatchBehavior<T> + 'a>);\n\nimpl<'a, T: Clone> Deref for DynReceiver<'a, T> {\n    type Target = Rcv<'a, T, dyn WatchBehavior<T> + 'a>;\n\n    fn deref(&self) -> &Self::Target {\n        &self.0\n    }\n}\n\nimpl<'a, T: Clone> DerefMut for DynReceiver<'a, T> {\n    fn deref_mut(&mut self) -> &mut Self::Target {\n        &mut self.0\n    }\n}\n\n/// A receiver of a `Watch` channel that cannot `.await` values.\n#[derive(Debug)]\npub struct AnonReceiver<'a, M: RawMutex, T: Clone, const N: usize>(AnonRcv<'a, T, Watch<M, T, N>>);\n\nimpl<'a, M: RawMutex, T: Clone, const N: usize> AnonReceiver<'a, M, T, N> {\n    /// Converts the `Receiver` into a [`DynReceiver`].\n    pub fn as_dyn(self) -> DynAnonReceiver<'a, T> {\n        let rcv = DynAnonReceiver(AnonRcv::new(self.0.watch, self.at_id));\n        core::mem::forget(self); // Ensures the destructor is not called\n        rcv\n    }\n}\n\nimpl<'a, M: RawMutex, T: Clone, const N: usize> Into<DynAnonReceiver<'a, T>> for AnonReceiver<'a, M, T, N> {\n    fn into(self) -> DynAnonReceiver<'a, T> {\n        self.as_dyn()\n    }\n}\n\nimpl<'a, M: RawMutex, T: Clone, const N: usize> Deref for AnonReceiver<'a, M, T, N> {\n    type Target = AnonRcv<'a, T, Watch<M, T, N>>;\n\n    fn deref(&self) -> &Self::Target {\n        &self.0\n    }\n}\n\nimpl<'a, M: RawMutex, T: Clone, const N: usize> DerefMut for AnonReceiver<'a, M, T, N> {\n    fn deref_mut(&mut self) -> &mut Self::Target {\n        &mut self.0\n    }\n}\n\n/// A receiver that cannot `.await` value, which holds a **dynamic** reference to a `Watch` channel.\n///\n/// This is an alternative to [`AnonReceiver`] with a simpler type definition, at the expense of\n/// some runtime performance due to dynamic dispatch.\npub struct DynAnonReceiver<'a, T: Clone>(AnonRcv<'a, T, dyn WatchBehavior<T> + 'a>);\n\nimpl<'a, T: Clone> Deref for DynAnonReceiver<'a, T> {\n    type Target = AnonRcv<'a, T, dyn WatchBehavior<T> + 'a>;\n\n    fn deref(&self) -> &Self::Target {\n        &self.0\n    }\n}\n\nimpl<'a, T: Clone> DerefMut for DynAnonReceiver<'a, T> {\n    fn deref_mut(&mut self) -> &mut Self::Target {\n        &mut self.0\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use futures_executor::block_on;\n\n    use super::Watch;\n    use crate::blocking_mutex::raw::CriticalSectionRawMutex;\n\n    #[test]\n    fn multiple_sends() {\n        let f = async {\n            static WATCH: Watch<CriticalSectionRawMutex, u8, 1> = Watch::new();\n\n            // Obtain receiver and sender\n            let mut rcv = WATCH.receiver().unwrap();\n            let snd = WATCH.sender();\n\n            // Not initialized\n            assert_eq!(rcv.try_changed(), None);\n\n            // Receive the new value\n            snd.send(10);\n            assert_eq!(rcv.changed().await, 10);\n\n            // Receive another value\n            snd.send(20);\n            assert_eq!(rcv.try_changed(), Some(20));\n\n            // No update\n            assert_eq!(rcv.try_changed(), None);\n        };\n        block_on(f);\n    }\n\n    #[test]\n    fn all_try_get() {\n        let f = async {\n            static WATCH: Watch<CriticalSectionRawMutex, u8, 1> = Watch::new();\n\n            // Obtain receiver and sender\n            let mut rcv = WATCH.receiver().unwrap();\n            let snd = WATCH.sender();\n\n            // Not initialized\n            assert_eq!(WATCH.try_get(), None);\n            assert_eq!(rcv.try_get(), None);\n            assert_eq!(snd.try_get(), None);\n\n            // Receive the new value\n            snd.send(10);\n            assert_eq!(WATCH.try_get(), Some(10));\n            assert_eq!(rcv.try_get(), Some(10));\n            assert_eq!(snd.try_get(), Some(10));\n\n            assert_eq!(WATCH.try_get_and(|x| x > &5), Some(10));\n            assert_eq!(rcv.try_get_and(|x| x > &5), Some(10));\n            assert_eq!(snd.try_get_and(|x| x > &5), Some(10));\n\n            assert_eq!(WATCH.try_get_and(|x| x < &5), None);\n            assert_eq!(rcv.try_get_and(|x| x < &5), None);\n            assert_eq!(snd.try_get_and(|x| x < &5), None);\n        };\n        block_on(f);\n    }\n\n    #[test]\n    fn once_lock_like() {\n        let f = async {\n            static CONFIG0: u8 = 10;\n            static CONFIG1: u8 = 20;\n\n            static WATCH: Watch<CriticalSectionRawMutex, &'static u8, 1> = Watch::new();\n\n            // Obtain receiver and sender\n            let mut rcv = WATCH.receiver().unwrap();\n            let snd = WATCH.sender();\n\n            // Not initialized\n            assert_eq!(rcv.try_changed(), None);\n\n            // Receive the new value\n            snd.send(&CONFIG0);\n            let rcv0 = rcv.changed().await;\n            assert_eq!(rcv0, &10);\n\n            // Receive another value\n            snd.send(&CONFIG1);\n            let rcv1 = rcv.try_changed();\n            assert_eq!(rcv1, Some(&20));\n\n            // No update\n            assert_eq!(rcv.try_changed(), None);\n\n            // Ensure similarity with original static\n            assert_eq!(rcv0, &CONFIG0);\n            assert_eq!(rcv1, Some(&CONFIG1));\n        };\n        block_on(f);\n    }\n\n    #[test]\n    fn sender_modify() {\n        let f = async {\n            static WATCH: Watch<CriticalSectionRawMutex, u8, 1> = Watch::new();\n\n            // Obtain receiver and sender\n            let mut rcv = WATCH.receiver().unwrap();\n            let snd = WATCH.sender();\n\n            // Receive the new value\n            snd.send(10);\n            assert_eq!(rcv.try_changed(), Some(10));\n\n            // Modify the value inplace\n            snd.send_modify(|opt| {\n                if let Some(inner) = opt {\n                    *inner += 5;\n                }\n            });\n\n            // Get the modified value\n            assert_eq!(rcv.try_changed(), Some(15));\n            assert_eq!(rcv.try_changed(), None);\n        };\n        block_on(f);\n    }\n\n    #[test]\n    fn predicate_fn() {\n        let f = async {\n            static WATCH: Watch<CriticalSectionRawMutex, u8, 1> = Watch::new();\n\n            // Obtain receiver and sender\n            let mut rcv = WATCH.receiver().unwrap();\n            let snd = WATCH.sender();\n\n            snd.send(15);\n            assert_eq!(rcv.try_get_and(|x| x > &5), Some(15));\n            assert_eq!(rcv.try_get_and(|x| x < &5), None);\n            assert!(rcv.try_changed().is_none());\n\n            snd.send(20);\n            assert_eq!(rcv.try_changed_and(|x| x > &5), Some(20));\n            assert_eq!(rcv.try_changed_and(|x| x > &5), None);\n\n            snd.send(25);\n            assert_eq!(rcv.try_changed_and(|x| x < &5), None);\n            assert_eq!(rcv.try_changed(), Some(25));\n\n            snd.send(30);\n            assert_eq!(rcv.changed_and(|x| x > &5).await, 30);\n            assert_eq!(rcv.get_and(|x| x > &5).await, 30);\n        };\n        block_on(f);\n    }\n\n    #[test]\n    fn receive_after_create() {\n        let f = async {\n            static WATCH: Watch<CriticalSectionRawMutex, u8, 1> = Watch::new();\n\n            // Obtain sender and send value\n            let snd = WATCH.sender();\n            snd.send(10);\n\n            // Obtain receiver and receive value\n            let mut rcv = WATCH.receiver().unwrap();\n            assert_eq!(rcv.try_changed(), Some(10));\n        };\n        block_on(f);\n    }\n\n    #[test]\n    fn max_receivers_drop() {\n        let f = async {\n            static WATCH: Watch<CriticalSectionRawMutex, u8, 2> = Watch::new();\n\n            // Try to create 3 receivers (only 2 can exist at once)\n            let rcv0 = WATCH.receiver();\n            let rcv1 = WATCH.receiver();\n            let rcv2 = WATCH.receiver();\n\n            // Ensure the first two are successful and the third is not\n            assert!(rcv0.is_some());\n            assert!(rcv1.is_some());\n            assert!(rcv2.is_none());\n\n            // Drop the first receiver\n            drop(rcv0);\n\n            // Create another receiver and ensure it is successful\n            let rcv3 = WATCH.receiver();\n            assert!(rcv3.is_some());\n        };\n        block_on(f);\n    }\n\n    #[test]\n    fn multiple_receivers() {\n        let f = async {\n            static WATCH: Watch<CriticalSectionRawMutex, u8, 2> = Watch::new();\n\n            // Obtain receivers and sender\n            let mut rcv0 = WATCH.receiver().unwrap();\n            let mut rcv1 = WATCH.anon_receiver();\n            let snd = WATCH.sender();\n\n            // No update for both\n            assert_eq!(rcv0.try_changed(), None);\n            assert_eq!(rcv1.try_changed(), None);\n\n            // Send a new value\n            snd.send(0);\n\n            // Both receivers receive the new value\n            assert_eq!(rcv0.try_changed(), Some(0));\n            assert_eq!(rcv1.try_changed(), Some(0));\n        };\n        block_on(f);\n    }\n\n    #[test]\n    fn clone_senders() {\n        let f = async {\n            // Obtain different ways to send\n            static WATCH: Watch<CriticalSectionRawMutex, u8, 1> = Watch::new();\n            let snd0 = WATCH.sender();\n            let snd1 = snd0.clone();\n\n            // Obtain Receiver\n            let mut rcv = WATCH.receiver().unwrap().as_dyn();\n\n            // Send a value from first sender\n            snd0.send(10);\n            assert_eq!(rcv.try_changed(), Some(10));\n\n            // Send a value from second sender\n            snd1.send(20);\n            assert_eq!(rcv.try_changed(), Some(20));\n        };\n        block_on(f);\n    }\n\n    #[test]\n    fn use_dynamics() {\n        let f = async {\n            static WATCH: Watch<CriticalSectionRawMutex, u8, 2> = Watch::new();\n\n            // Obtain receiver and sender\n            let mut anon_rcv = WATCH.dyn_anon_receiver();\n            let mut dyn_rcv = WATCH.dyn_receiver().unwrap();\n            let dyn_snd = WATCH.dyn_sender();\n\n            // Send a value\n            dyn_snd.send(10);\n\n            // Ensure the dynamic receiver receives the value\n            assert_eq!(anon_rcv.try_changed(), Some(10));\n            assert_eq!(dyn_rcv.try_changed(), Some(10));\n            assert_eq!(dyn_rcv.try_changed(), None);\n        };\n        block_on(f);\n    }\n\n    #[test]\n    fn convert_to_dyn() {\n        let f = async {\n            static WATCH: Watch<CriticalSectionRawMutex, u8, 2> = Watch::new();\n\n            // Obtain receiver and sender\n            let anon_rcv = WATCH.anon_receiver();\n            let rcv = WATCH.receiver().unwrap();\n            let snd = WATCH.sender();\n\n            // Convert to dynamic\n            let mut dyn_anon_rcv = anon_rcv.as_dyn();\n            let mut dyn_rcv = rcv.as_dyn();\n            let dyn_snd = snd.as_dyn();\n\n            // Send a value\n            dyn_snd.send(10);\n\n            // Ensure the dynamic receiver receives the value\n            assert_eq!(dyn_anon_rcv.try_changed(), Some(10));\n            assert_eq!(dyn_rcv.try_changed(), Some(10));\n            assert_eq!(dyn_rcv.try_changed(), None);\n        };\n        block_on(f);\n    }\n\n    #[test]\n    fn dynamic_receiver_count() {\n        let f = async {\n            static WATCH: Watch<CriticalSectionRawMutex, u8, 2> = Watch::new();\n\n            // Obtain receiver and sender\n            let rcv0 = WATCH.receiver();\n            let rcv1 = WATCH.receiver();\n            let rcv2 = WATCH.receiver();\n\n            // Ensure the first two are successful and the third is not\n            assert!(rcv0.is_some());\n            assert!(rcv1.is_some());\n            assert!(rcv2.is_none());\n\n            // Convert to dynamic\n            let dyn_rcv0 = rcv0.unwrap().as_dyn();\n\n            // Drop the (now dynamic) receiver\n            drop(dyn_rcv0);\n\n            // Create another receiver and ensure it is successful\n            let rcv3 = WATCH.receiver();\n            let rcv4 = WATCH.receiver();\n            assert!(rcv3.is_some());\n            assert!(rcv4.is_none());\n        };\n        block_on(f);\n    }\n\n    #[test]\n    fn contains_value() {\n        let f = async {\n            static WATCH: Watch<CriticalSectionRawMutex, u8, 2> = Watch::new();\n\n            // Obtain receiver and sender\n            let rcv = WATCH.receiver().unwrap();\n            let snd = WATCH.sender();\n\n            // check if the watch contains a value\n            assert_eq!(rcv.contains_value(), false);\n            assert_eq!(snd.contains_value(), false);\n\n            // Send a value\n            snd.send(10);\n\n            // check if the watch contains a value\n            assert_eq!(rcv.contains_value(), true);\n            assert_eq!(snd.contains_value(), true);\n        };\n        block_on(f);\n    }\n}\n"
  },
  {
    "path": "embassy-sync/src/zerocopy_channel.rs",
    "content": "//! A zero-copy queue for sending values between asynchronous tasks.\n//!\n//! It can be used concurrently by a producer (sender) and a\n//! consumer (receiver), i.e. it is an  \"SPSC channel\".\n//!\n//! This queue takes a Mutex type so that various\n//! targets can be attained. For example, a ThreadModeMutex can be used\n//! for single-core Cortex-M targets where messages are only passed\n//! between tasks running in thread mode. Similarly, a CriticalSectionMutex\n//! can also be used for single-core targets where messages are to be\n//! passed from exception mode e.g. out of an interrupt handler.\n//!\n//! This module provides a bounded channel that has a limit on the number of\n//! messages that it can store, and if this limit is reached, trying to send\n//! another message will result in an error being returned.\n\nuse core::cell::RefCell;\nuse core::future::{Future, poll_fn};\nuse core::marker::PhantomData;\nuse core::task::{Context, Poll};\n\nuse crate::blocking_mutex::Mutex;\nuse crate::blocking_mutex::raw::RawMutex;\nuse crate::waitqueue::WakerRegistration;\n\n/// A bounded zero-copy channel for communicating between asynchronous tasks\n/// with backpressure.\n///\n/// The channel will buffer up to the provided number of messages.  Once the\n/// buffer is full, attempts to `send` new messages will wait until a message is\n/// received from the channel.\n///\n/// All data sent will become available in the same order as it was sent.\n///\n/// The channel requires a buffer of recyclable elements.  Writing to the channel is done through\n/// an `&mut T`.\n#[derive(Debug)]\npub struct Channel<'a, M: RawMutex, T> {\n    buf: BufferPtr<T>,\n    phantom: PhantomData<&'a mut T>,\n    state: Mutex<M, RefCell<State>>,\n}\n\nimpl<'a, M: RawMutex, T> Channel<'a, M, T> {\n    /// Initialize a new [`Channel`].\n    ///\n    /// The provided buffer will be used and reused by the channel's logic, and thus dictates the\n    /// channel's capacity.\n    pub fn new(buf: &'a mut [T]) -> Self {\n        let len = buf.len();\n        assert!(len != 0);\n\n        Self {\n            buf: BufferPtr(buf.as_mut_ptr()),\n            phantom: PhantomData,\n            state: Mutex::new(RefCell::new(State {\n                capacity: len,\n                front: 0,\n                back: 0,\n                full: false,\n                send_waker: WakerRegistration::new(),\n                receive_waker: WakerRegistration::new(),\n            })),\n        }\n    }\n\n    /// Creates a [`Sender`] and [`Receiver`] from an existing channel.\n    ///\n    /// Further Senders and Receivers can be created through [`Sender::borrow`] and\n    /// [`Receiver::borrow`] respectively.\n    pub fn split(&mut self) -> (Sender<'_, M, T>, Receiver<'_, M, T>) {\n        (Sender { channel: self }, Receiver { channel: self })\n    }\n\n    /// Clears all elements in the channel.\n    pub fn clear(&mut self) {\n        self.state.lock(|s| {\n            s.borrow_mut().clear();\n        });\n    }\n\n    /// Returns the number of elements currently in the channel.\n    pub fn len(&self) -> usize {\n        self.state.lock(|s| s.borrow().len())\n    }\n\n    /// Returns whether the channel is empty.\n    pub fn is_empty(&self) -> bool {\n        self.state.lock(|s| s.borrow().is_empty())\n    }\n\n    /// Returns whether the channel is full.\n    pub fn is_full(&self) -> bool {\n        self.state.lock(|s| s.borrow().is_full())\n    }\n}\n\n#[repr(transparent)]\n#[derive(Debug)]\nstruct BufferPtr<T>(*mut T);\n\nimpl<T> BufferPtr<T> {\n    unsafe fn add(&self, count: usize) -> *mut T {\n        self.0.add(count)\n    }\n}\n\nunsafe impl<T> Send for BufferPtr<T> {}\nunsafe impl<T> Sync for BufferPtr<T> {}\n\n/// Send-only access to a [`Channel`].\n#[derive(Debug)]\npub struct Sender<'a, M: RawMutex, T> {\n    channel: &'a Channel<'a, M, T>,\n}\n\nimpl<'a, M: RawMutex, T> Sender<'a, M, T> {\n    /// Creates one further [`Sender`] over the same channel.\n    pub fn borrow(&mut self) -> Sender<'_, M, T> {\n        Sender { channel: self.channel }\n    }\n\n    /// Attempts to send a value over the channel.\n    pub fn try_send(&mut self) -> Option<&mut T> {\n        self.channel.state.lock(|s| {\n            let s = &mut *s.borrow_mut();\n            match s.push_index() {\n                Some(i) => Some(unsafe { &mut *self.channel.buf.add(i) }),\n                None => None,\n            }\n        })\n    }\n\n    /// Attempts to send a value over the channel.\n    pub fn poll_send(&mut self, cx: &mut Context) -> Poll<&mut T> {\n        self.channel.state.lock(|s| {\n            let s = &mut *s.borrow_mut();\n            match s.push_index() {\n                Some(i) => Poll::Ready(unsafe { &mut *self.channel.buf.add(i) }),\n                None => {\n                    s.receive_waker.register(cx.waker());\n                    Poll::Pending\n                }\n            }\n        })\n    }\n\n    /// Asynchronously send a value over the channel.\n    pub fn send(&mut self) -> impl Future<Output = &mut T> {\n        poll_fn(|cx| {\n            self.channel.state.lock(|s| {\n                let s = &mut *s.borrow_mut();\n                match s.push_index() {\n                    Some(i) => {\n                        let r = unsafe { &mut *self.channel.buf.add(i) };\n                        Poll::Ready(r)\n                    }\n                    None => {\n                        s.receive_waker.register(cx.waker());\n                        Poll::Pending\n                    }\n                }\n            })\n        })\n    }\n\n    /// Notify the channel that the sending of the value has been finalized.\n    pub fn send_done(&mut self) {\n        self.channel.state.lock(|s| s.borrow_mut().push_done())\n    }\n\n    /// Clears all elements in the channel.\n    pub fn clear(&mut self) {\n        self.channel.state.lock(|s| {\n            s.borrow_mut().clear();\n        });\n    }\n\n    /// Returns the number of elements currently in the channel.\n    pub fn len(&self) -> usize {\n        self.channel.state.lock(|s| s.borrow().len())\n    }\n\n    /// Returns whether the channel is empty.\n    pub fn is_empty(&self) -> bool {\n        self.channel.state.lock(|s| s.borrow().is_empty())\n    }\n\n    /// Returns whether the channel is full.\n    pub fn is_full(&self) -> bool {\n        self.channel.state.lock(|s| s.borrow().is_full())\n    }\n}\n\n/// Receive-only access to a [`Channel`].\n#[derive(Debug)]\npub struct Receiver<'a, M: RawMutex, T> {\n    channel: &'a Channel<'a, M, T>,\n}\n\nimpl<'a, M: RawMutex, T> Receiver<'a, M, T> {\n    /// Creates one further [`Receiver`] over the same channel.\n    pub fn borrow(&mut self) -> Receiver<'_, M, T> {\n        Receiver { channel: self.channel }\n    }\n\n    /// Attempts to receive a value over the channel.\n    pub fn try_receive(&mut self) -> Option<&mut T> {\n        self.channel.state.lock(|s| {\n            let s = &mut *s.borrow_mut();\n            match s.pop_index() {\n                Some(i) => Some(unsafe { &mut *self.channel.buf.add(i) }),\n                None => None,\n            }\n        })\n    }\n\n    /// Attempts to asynchronously receive a value over the channel.\n    pub fn poll_receive(&mut self, cx: &mut Context) -> Poll<&mut T> {\n        self.channel.state.lock(|s| {\n            let s = &mut *s.borrow_mut();\n            match s.pop_index() {\n                Some(i) => Poll::Ready(unsafe { &mut *self.channel.buf.add(i) }),\n                None => {\n                    s.send_waker.register(cx.waker());\n                    Poll::Pending\n                }\n            }\n        })\n    }\n\n    /// Asynchronously receive a value over the channel.\n    pub fn receive(&mut self) -> impl Future<Output = &mut T> {\n        poll_fn(|cx| {\n            self.channel.state.lock(|s| {\n                let s = &mut *s.borrow_mut();\n                match s.pop_index() {\n                    Some(i) => {\n                        let r = unsafe { &mut *self.channel.buf.add(i) };\n                        Poll::Ready(r)\n                    }\n                    None => {\n                        s.send_waker.register(cx.waker());\n                        Poll::Pending\n                    }\n                }\n            })\n        })\n    }\n\n    /// Notify the channel that the receiving of the value has been finalized.\n    pub fn receive_done(&mut self) {\n        self.channel.state.lock(|s| s.borrow_mut().pop_done())\n    }\n\n    /// Clears all elements in the channel.\n    pub fn clear(&mut self) {\n        self.channel.state.lock(|s| {\n            s.borrow_mut().clear();\n        });\n    }\n\n    /// Returns the number of elements currently in the channel.\n    pub fn len(&self) -> usize {\n        self.channel.state.lock(|s| s.borrow().len())\n    }\n\n    /// Returns whether the channel is empty.\n    pub fn is_empty(&self) -> bool {\n        self.channel.state.lock(|s| s.borrow().is_empty())\n    }\n\n    /// Returns whether the channel is full.\n    pub fn is_full(&self) -> bool {\n        self.channel.state.lock(|s| s.borrow().is_full())\n    }\n}\n\n#[derive(Debug)]\nstruct State {\n    /// Maximum number of elements the channel can hold.\n    capacity: usize,\n\n    /// Front index. Always 0..=(N-1)\n    front: usize,\n    /// Back index. Always 0..=(N-1).\n    back: usize,\n\n    /// Used to distinguish \"empty\" and \"full\" cases when `front == back`.\n    /// May only be `true` if `front == back`, always `false` otherwise.\n    full: bool,\n\n    send_waker: WakerRegistration,\n    receive_waker: WakerRegistration,\n}\n\nimpl State {\n    fn increment(&self, i: usize) -> usize {\n        if i + 1 == self.capacity { 0 } else { i + 1 }\n    }\n\n    fn clear(&mut self) {\n        if self.full {\n            self.receive_waker.wake();\n        }\n        self.front = 0;\n        self.back = 0;\n        self.full = false;\n    }\n\n    fn len(&self) -> usize {\n        if !self.full {\n            if self.back >= self.front {\n                self.back - self.front\n            } else {\n                self.capacity + self.back - self.front\n            }\n        } else {\n            self.capacity\n        }\n    }\n\n    fn is_full(&self) -> bool {\n        self.full\n    }\n\n    fn is_empty(&self) -> bool {\n        self.front == self.back && !self.full\n    }\n\n    fn push_index(&mut self) -> Option<usize> {\n        match self.is_full() {\n            true => None,\n            false => Some(self.back),\n        }\n    }\n\n    fn push_done(&mut self) {\n        assert!(!self.is_full());\n        self.back = self.increment(self.back);\n        if self.back == self.front {\n            self.full = true;\n        }\n        self.send_waker.wake();\n    }\n\n    fn pop_index(&mut self) -> Option<usize> {\n        match self.is_empty() {\n            true => None,\n            false => Some(self.front),\n        }\n    }\n\n    fn pop_done(&mut self) {\n        assert!(!self.is_empty());\n        self.front = self.increment(self.front);\n        self.full = false;\n        self.receive_waker.wake();\n    }\n}\n"
  },
  {
    "path": "embassy-sync/tests/ui/sync_impl/lazy_lock_function.rs",
    "content": "use embassy_sync::lazy_lock::LazyLock;\n\nfn main() {\n    let x = 128u8;\n    let x_ptr: *const u8 = core::ptr::addr_of!(x);\n    let closure_capturing_non_sync_variable = || unsafe { core::ptr::read(x_ptr) };\n\n    check_sync(LazyLock::new(closure_capturing_non_sync_variable));\n}\n\nfn check_sync<T: Sync>(_lazy_lock: T) {}\n"
  },
  {
    "path": "embassy-sync/tests/ui/sync_impl/lazy_lock_function.stderr",
    "content": "error[E0277]: `*const u8` cannot be shared between threads safely\n  --> tests/ui/sync_impl/lazy_lock_function.rs:8:16\n   |\n 6 |     let closure_capturing_non_sync_variable = || unsafe { core::ptr::read(x_ptr) };\n   |                                               -- within this `{closure@$DIR/tests/ui/sync_impl/lazy_lock_function.rs:6:47: 6:49}`\n 7 |\n 8 |     check_sync(LazyLock::new(closure_capturing_non_sync_variable));\n   |     ---------- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `*const u8` cannot be shared between threads safely\n   |     |\n   |     required by a bound introduced by this call\n   |\n   = help: within `{closure@$DIR/tests/ui/sync_impl/lazy_lock_function.rs:6:47: 6:49}`, the trait `Sync` is not implemented for `*const u8`\n   = note: required because it appears within the type `&*const u8`\nnote: required because it's used within this closure\n  --> tests/ui/sync_impl/lazy_lock_function.rs:6:47\n   |\n 6 |     let closure_capturing_non_sync_variable = || unsafe { core::ptr::read(x_ptr) };\n   |                                               ^^\n   = note: required for `embassy_sync::lazy_lock::LazyLock<u8, {closure@$DIR/tests/ui/sync_impl/lazy_lock_function.rs:6:47: 6:49}>` to implement `Sync`\nnote: required by a bound in `check_sync`\n  --> tests/ui/sync_impl/lazy_lock_function.rs:11:18\n   |\n11 | fn check_sync<T: Sync>(_lazy_lock: T) {}\n   |                  ^^^^ required by this bound in `check_sync`\n"
  },
  {
    "path": "embassy-sync/tests/ui/sync_impl/lazy_lock_type.rs",
    "content": "use embassy_sync::lazy_lock::LazyLock;\n\n// *mut u8 is not Sync, so LazyLock should not implement Sync for this type. This should fail to compile.\nstatic GLOBAL: LazyLock<*mut u8> = LazyLock::new(|| core::ptr::null_mut());\n\nfn main() {}\n"
  },
  {
    "path": "embassy-sync/tests/ui/sync_impl/lazy_lock_type.stderr",
    "content": "error[E0277]: `*mut u8` cannot be shared between threads safely\n --> tests/ui/sync_impl/lazy_lock_type.rs:4:16\n  |\n4 | static GLOBAL: LazyLock<*mut u8> = LazyLock::new(|| core::ptr::null_mut());\n  |                ^^^^^^^^^^^^^^^^^ `*mut u8` cannot be shared between threads safely\n  |\n  = help: the trait `Sync` is not implemented for `*mut u8`\n  = note: required for `embassy_sync::lazy_lock::LazyLock<*mut u8>` to implement `Sync`\n  = note: shared static variables must have a type that implements `Sync`\n"
  },
  {
    "path": "embassy-sync/tests/ui/sync_impl/once_lock.rs",
    "content": "use embassy_sync::once_lock::OnceLock;\n\n// *mut u8 is not Sync, so OnceLock should not implement Sync for this type. This should fail to compile.\nstatic GLOBAL: OnceLock<*mut u8> = OnceLock::new();\n\nfn main() {}\n"
  },
  {
    "path": "embassy-sync/tests/ui/sync_impl/once_lock.stderr",
    "content": "error[E0277]: `*mut u8` cannot be shared between threads safely\n --> tests/ui/sync_impl/once_lock.rs:4:16\n  |\n4 | static GLOBAL: OnceLock<*mut u8> = OnceLock::new();\n  |                ^^^^^^^^^^^^^^^^^ `*mut u8` cannot be shared between threads safely\n  |\n  = help: the trait `Sync` is not implemented for `*mut u8`\n  = note: required for `embassy_sync::once_lock::OnceLock<*mut u8>` to implement `Sync`\n  = note: shared static variables must have a type that implements `Sync`\n"
  },
  {
    "path": "embassy-sync/tests/ui.rs",
    "content": "#[cfg(not(miri))]\n#[test]\nfn ui() {\n    let t = trybuild::TestCases::new();\n\n    // These test cases should fail to compile since OnceLock and LazyLock should not unconditionally implement sync\n    // for all types. These tests are regression tests against the following issues:\n    // * https://github.com/embassy-rs/embassy/issues/4307\n    // * https://github.com/embassy-rs/embassy/issues/3904\n    t.compile_fail(\"tests/ui/sync_impl/lazy_lock_function.rs\");\n    t.compile_fail(\"tests/ui/sync_impl/lazy_lock_type.rs\");\n    t.compile_fail(\"tests/ui/sync_impl/once_lock.rs\");\n}\n"
  },
  {
    "path": "embassy-time/CHANGELOG.md",
    "content": "# Changelog\n\nAll notable changes to this project will be documented in this file.\n\nThe format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),\nand this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).\n\n<!-- next-header -->\n## Unreleased - ReleaseDate\n\n## 0.5.1 - 2026-03-11\n\n- Add `as_nanos` and `from_nanos` where missing\n- Add `Instant::try_from_nanos`\n- Added 375KHz tick rate support\n- Upgrade embassy-executor to 0.10.0\n\n## 0.5.0 - 2025-08-26\n\n- Allow inlining on time driver boundary\n- Add `saturating_add` and `saturating_sub` to `Instant`\n- Add `Instant::try_from_*` constructor functions\n- Add `Duration::try_from_*` constructor functions\n- Don't select `critical-section` impl for `std`\n- Manually implement the future for `with_timeout`\n- Add 133MHz tick rate to support PR2040 @ 133MHz when `TIMERx`'s `SOURCE` is set to `SYSCLK`\n- Implement Sum for Duration\n\n## 0.4.0 - 2025-01-02\n\n- `embassy-time-driver` updated from v0.1 to v0.2.\n- embassy-time no longer provides an `embassy-time-queue-driver` implementation\n\n## 0.3.2 - 2024-08-05\n\n- Implement with_timeout()/with_deadline() method style call on Future\n- Add collapse_debuginfo to fmt.rs macros.\n\n## 0.3.1 - 2024-01-11\n\n- Add with\\_deadline convenience function and example\n- Implement Clone for Delay\n- Make Ticker::next Send+Sync\n- Add timestamp features\n\n## 0.3.0 - 2024-01-11\n\n- Update `embedded-hal-async` to `1.0.0`\n- Update `embedded-hal v1` to `1.0.0`\n- Split the time driver to a separate `embassy-time-driver` crate.\n\n## 0.2.0 - 2023-12-04\n\n- Added tick rates in multiples of 10 kHz\n- Remove nightly and unstable-traits features in preparation for 1.75.\n- Update heapless to 0.8.\n\n## 0.1.5 - 2023-10-16\n\n- Added `links` key to Cargo.toml, to prevent multiple copies of this crate in the same binary.\n  Needed because different copies might get different tick rates, causing\n  wrong delays if the time driver is using one copy and user code is using another.\n  This is especially common when mixing crates from crates.io and git.\n\n## 0.1.4 - 2023-10-12\n\n- Added more tick rates\n\n## 0.1.3 - 2023-08-28\n\n- Update `embedded-hal-async` to `1.0.0-rc.2`\n- Update `embedded-hal v1` to `1.0.0-rc.2`\n\n## 0.1.2 - 2023-07-05\n\n- Update `embedded-hal-async` to `0.2.0-alpha.2`.\n- Update `embedded-hal v1` to `1.0.0-alpha.11`. (Note: v0.2 support is kept unchanged).\n\n## 0.1.1 - 2023-04-13\n\n- Update `embedded-hal-async` to `0.2.0-alpha.1` (uses `async fn` in traits).\n- Update `embedded-hal v1` to `1.0.0-alpha.10`. (Note: v0.2 support is kept unchanged).\n- Remove dep on `embassy-sync`.\n- Fix reentrancy issues in the `std` time driver (#1177)\n- Add `Duration::from_hz()`.\n- impl `From` conversions to/from `core::time::Duration`.\n- Add `#[must_use]` to all futures.\n- Add inherent `async fn tick()` to `Ticker`, so you can use it directly without the `Stream` trait.\n- Add more tick rates.\n- impl `Default` for `Signal`\n- Remove unnecessary uses of `atomic-polyfill`\n\n## 0.1.0 - 2022-08-26\n\n- First release\n"
  },
  {
    "path": "embassy-time/Cargo.toml",
    "content": "[package]\nname = \"embassy-time\"\nversion = \"0.5.1\"\nedition = \"2024\"\ndescription = \"Instant and Duration for embedded no-std systems, with async timer support\"\nrepository = \"https://github.com/embassy-rs/embassy\"\ndocumentation = \"https://docs.embassy.dev/embassy-time\"\nreadme = \"README.md\"\nlicense = \"MIT OR Apache-2.0\"\ncategories = [\n    \"embedded\",\n    \"no-std\",\n    \"concurrency\",\n    \"asynchronous\",\n]\n\n[package.metadata.embassy]\nbuild = [\n    {target = \"thumbv6m-none-eabi\", features = [\"defmt\", \"defmt-timestamp-uptime\", \"mock-driver\"]},\n    # Xtensa builds\n    {group = \"xtensa\", build-std = [\"core\", \"alloc\"],  target = \"xtensa-esp32s2-none-elf\", features = [\"defmt\", \"defmt-timestamp-uptime\", \"mock-driver\"]},\n]\n\n[package.metadata.embassy_docs]\nsrc_base = \"https://github.com/embassy-rs/embassy/blob/embassy-time-v$VERSION/embassy-time/src/\"\nsrc_base_git = \"https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-time/src/\"\nfeatures = [\"defmt\", \"std\"]\ntarget = \"x86_64-unknown-linux-gnu\"\n\n[package.metadata.docs.rs]\nfeatures = [\"defmt\", \"std\"]\n\n[features]\n## Enable defmt\ndefmt = [\"dep:defmt\"]\n## Enable log  \nlog = [\"dep:log\"]\n\n## Display the time since startup next to defmt log messages.\n## At most 1 `defmt-timestamp-uptime-*` feature can be used.\n## `defmt-timestamp-uptime` is provided for backwards compatibility (provides the same format as `uptime-us`).\n## To use this you must have a time driver provided.\ndefmt-timestamp-uptime = [\"defmt\"]\ndefmt-timestamp-uptime-s = [\"defmt\"]\ndefmt-timestamp-uptime-ms = [\"defmt\"]\ndefmt-timestamp-uptime-us = [\"defmt\"]\ndefmt-timestamp-uptime-ts = [\"defmt\"]\ndefmt-timestamp-uptime-tms = [\"defmt\"]\ndefmt-timestamp-uptime-tus = [\"defmt\"]\n\n#! ### Time Drivers\n\n#! Usually, time drivers are defined by a HAL, or a companion crate to the HAL. For `std` and WASM\n#! environments, as well as for testing purposes, `embassy-time` provides some default time drivers\n#! that may be suitable for your use case. You can enable one of the following features to use them.\n\n## Create a `MockDriver` that can be manually advanced for testing purposes.\nmock-driver = [\"tick-hz-1_000_000\", \"dep:embassy-time-queue-utils\"]\n## Create a time driver for `std` environments.\nstd = [\"tick-hz-1_000_000\", \"dep:embassy-time-queue-utils\"]\n## Create a time driver for WASM.\nwasm = [\"dep:wasm-bindgen\", \"dep:js-sys\", \"dep:wasm-timer\", \"tick-hz-1_000_000\", \"dep:embassy-time-queue-utils\"]\n\n#! ### Generic Queue\n\n#! By default embassy-time uses a timer queue implementation that is faster but depends on `embassy-executor`.\n#! It will panic if you try to await any timer when using another executor.\n#! \n#! Alternatively, you can choose to use a \"generic\" timer queue implementation that works on any executor.\n#! To enable it, enable any of the features below.\n#! \n#! The features also set how many timers are used for the generic queue. At most one\n#! `generic-queue-*` feature can be enabled. If none is enabled, `queue_integrated` is used.\n#!\n#! When using embassy-time from libraries, you should *not* enable any `generic-queue-*` feature, to allow the\n#! end user to pick.\n\n## Generic Queue with 8 timers\ngeneric-queue-8 = [\"embassy-time-queue-utils/generic-queue-8\"]\n## Generic Queue with 16 timers\ngeneric-queue-16 = [\"embassy-time-queue-utils/generic-queue-16\"]\n## Generic Queue with 32 timers\ngeneric-queue-32 = [\"embassy-time-queue-utils/generic-queue-32\"]\n## Generic Queue with 64 timers\ngeneric-queue-64 = [\"embassy-time-queue-utils/generic-queue-64\"]\n## Generic Queue with 128 timers\ngeneric-queue-128 = [\"embassy-time-queue-utils/generic-queue-128\"]\n\n#! ### Tick Rate\n#!\n#! At most 1 `tick-*` feature can be enabled. If none is enabled, a default of 1MHz is used.\n#!\n#! If the time driver in use supports using arbitrary tick rates, you can enable one `tick-*`\n#! feature from your binary crate to set the tick rate. The driver will use configured tick rate.\n#! If the time driver supports a fixed tick rate, it will enable one feature itself, so you should\n#! not enable one. Check the time driver documentation for details.\n#!\n#! When using embassy-time from libraries, you should *not* enable any `tick-*` feature, to allow the\n#! end user or the driver to pick.\n#! <details>\n#!   <summary>Available tick rates:</summary>\n#! <!-- Next line must be left empty for the features to render correctly! -->\n#!\n\n# BEGIN TICKS\n# Generated by gen_tick.py. DO NOT EDIT.\n## 1Hz Tick Rate\ntick-hz-1 = [\"embassy-time-driver/tick-hz-1\"]\n## 2Hz Tick Rate\ntick-hz-2 = [\"embassy-time-driver/tick-hz-2\"]\n## 4Hz Tick Rate\ntick-hz-4 = [\"embassy-time-driver/tick-hz-4\"]\n## 8Hz Tick Rate\ntick-hz-8 = [\"embassy-time-driver/tick-hz-8\"]\n## 10Hz Tick Rate\ntick-hz-10 = [\"embassy-time-driver/tick-hz-10\"]\n## 16Hz Tick Rate\ntick-hz-16 = [\"embassy-time-driver/tick-hz-16\"]\n## 32Hz Tick Rate\ntick-hz-32 = [\"embassy-time-driver/tick-hz-32\"]\n## 64Hz Tick Rate\ntick-hz-64 = [\"embassy-time-driver/tick-hz-64\"]\n## 100Hz Tick Rate\ntick-hz-100 = [\"embassy-time-driver/tick-hz-100\"]\n## 128Hz Tick Rate\ntick-hz-128 = [\"embassy-time-driver/tick-hz-128\"]\n## 256Hz Tick Rate\ntick-hz-256 = [\"embassy-time-driver/tick-hz-256\"]\n## 512Hz Tick Rate\ntick-hz-512 = [\"embassy-time-driver/tick-hz-512\"]\n## 1.0kHz Tick Rate\ntick-hz-1_000 = [\"embassy-time-driver/tick-hz-1_000\"]\n## 1.024kHz Tick Rate\ntick-hz-1_024 = [\"embassy-time-driver/tick-hz-1_024\"]\n## 2.0kHz Tick Rate\ntick-hz-2_000 = [\"embassy-time-driver/tick-hz-2_000\"]\n## 2.048kHz Tick Rate\ntick-hz-2_048 = [\"embassy-time-driver/tick-hz-2_048\"]\n## 4.0kHz Tick Rate\ntick-hz-4_000 = [\"embassy-time-driver/tick-hz-4_000\"]\n## 4.096kHz Tick Rate\ntick-hz-4_096 = [\"embassy-time-driver/tick-hz-4_096\"]\n## 8.0kHz Tick Rate\ntick-hz-8_000 = [\"embassy-time-driver/tick-hz-8_000\"]\n## 8.192kHz Tick Rate\ntick-hz-8_192 = [\"embassy-time-driver/tick-hz-8_192\"]\n## 10.0kHz Tick Rate\ntick-hz-10_000 = [\"embassy-time-driver/tick-hz-10_000\"]\n## 16.0kHz Tick Rate\ntick-hz-16_000 = [\"embassy-time-driver/tick-hz-16_000\"]\n## 16.384kHz Tick Rate\ntick-hz-16_384 = [\"embassy-time-driver/tick-hz-16_384\"]\n## 20.0kHz Tick Rate\ntick-hz-20_000 = [\"embassy-time-driver/tick-hz-20_000\"]\n## 32.0kHz Tick Rate\ntick-hz-32_000 = [\"embassy-time-driver/tick-hz-32_000\"]\n## 32.768kHz Tick Rate\ntick-hz-32_768 = [\"embassy-time-driver/tick-hz-32_768\"]\n## 40.0kHz Tick Rate\ntick-hz-40_000 = [\"embassy-time-driver/tick-hz-40_000\"]\n## 64.0kHz Tick Rate\ntick-hz-64_000 = [\"embassy-time-driver/tick-hz-64_000\"]\n## 65.536kHz Tick Rate\ntick-hz-65_536 = [\"embassy-time-driver/tick-hz-65_536\"]\n## 80.0kHz Tick Rate\ntick-hz-80_000 = [\"embassy-time-driver/tick-hz-80_000\"]\n## 100.0kHz Tick Rate\ntick-hz-100_000 = [\"embassy-time-driver/tick-hz-100_000\"]\n## 128.0kHz Tick Rate\ntick-hz-128_000 = [\"embassy-time-driver/tick-hz-128_000\"]\n## 131.072kHz Tick Rate\ntick-hz-131_072 = [\"embassy-time-driver/tick-hz-131_072\"]\n## 160.0kHz Tick Rate\ntick-hz-160_000 = [\"embassy-time-driver/tick-hz-160_000\"]\n## 256.0kHz Tick Rate\ntick-hz-256_000 = [\"embassy-time-driver/tick-hz-256_000\"]\n## 262.144kHz Tick Rate\ntick-hz-262_144 = [\"embassy-time-driver/tick-hz-262_144\"]\n## 320.0kHz Tick Rate\ntick-hz-320_000 = [\"embassy-time-driver/tick-hz-320_000\"]\n## 375.0kHz Tick Rate\ntick-hz-375_000 = [\"embassy-time-driver/tick-hz-375_000\"]\n## 512.0kHz Tick Rate\ntick-hz-512_000 = [\"embassy-time-driver/tick-hz-512_000\"]\n## 524.288kHz Tick Rate\ntick-hz-524_288 = [\"embassy-time-driver/tick-hz-524_288\"]\n## 640.0kHz Tick Rate\ntick-hz-640_000 = [\"embassy-time-driver/tick-hz-640_000\"]\n## 1.0MHz Tick Rate\ntick-hz-1_000_000 = [\"embassy-time-driver/tick-hz-1_000_000\"]\n## 1.024MHz Tick Rate\ntick-hz-1_024_000 = [\"embassy-time-driver/tick-hz-1_024_000\"]\n## 1.048576MHz Tick Rate\ntick-hz-1_048_576 = [\"embassy-time-driver/tick-hz-1_048_576\"]\n## 1.28MHz Tick Rate\ntick-hz-1_280_000 = [\"embassy-time-driver/tick-hz-1_280_000\"]\n## 2.0MHz Tick Rate\ntick-hz-2_000_000 = [\"embassy-time-driver/tick-hz-2_000_000\"]\n## 2.048MHz Tick Rate\ntick-hz-2_048_000 = [\"embassy-time-driver/tick-hz-2_048_000\"]\n## 2.097152MHz Tick Rate\ntick-hz-2_097_152 = [\"embassy-time-driver/tick-hz-2_097_152\"]\n## 2.56MHz Tick Rate\ntick-hz-2_560_000 = [\"embassy-time-driver/tick-hz-2_560_000\"]\n## 3.0MHz Tick Rate\ntick-hz-3_000_000 = [\"embassy-time-driver/tick-hz-3_000_000\"]\n## 4.0MHz Tick Rate\ntick-hz-4_000_000 = [\"embassy-time-driver/tick-hz-4_000_000\"]\n## 4.096MHz Tick Rate\ntick-hz-4_096_000 = [\"embassy-time-driver/tick-hz-4_096_000\"]\n## 4.194304MHz Tick Rate\ntick-hz-4_194_304 = [\"embassy-time-driver/tick-hz-4_194_304\"]\n## 5.12MHz Tick Rate\ntick-hz-5_120_000 = [\"embassy-time-driver/tick-hz-5_120_000\"]\n## 6.0MHz Tick Rate\ntick-hz-6_000_000 = [\"embassy-time-driver/tick-hz-6_000_000\"]\n## 8.0MHz Tick Rate\ntick-hz-8_000_000 = [\"embassy-time-driver/tick-hz-8_000_000\"]\n## 8.192MHz Tick Rate\ntick-hz-8_192_000 = [\"embassy-time-driver/tick-hz-8_192_000\"]\n## 8.388608MHz Tick Rate\ntick-hz-8_388_608 = [\"embassy-time-driver/tick-hz-8_388_608\"]\n## 9.0MHz Tick Rate\ntick-hz-9_000_000 = [\"embassy-time-driver/tick-hz-9_000_000\"]\n## 10.0MHz Tick Rate\ntick-hz-10_000_000 = [\"embassy-time-driver/tick-hz-10_000_000\"]\n## 10.24MHz Tick Rate\ntick-hz-10_240_000 = [\"embassy-time-driver/tick-hz-10_240_000\"]\n## 12.0MHz Tick Rate\ntick-hz-12_000_000 = [\"embassy-time-driver/tick-hz-12_000_000\"]\n## 16.0MHz Tick Rate\ntick-hz-16_000_000 = [\"embassy-time-driver/tick-hz-16_000_000\"]\n## 16.384MHz Tick Rate\ntick-hz-16_384_000 = [\"embassy-time-driver/tick-hz-16_384_000\"]\n## 16.777216MHz Tick Rate\ntick-hz-16_777_216 = [\"embassy-time-driver/tick-hz-16_777_216\"]\n## 18.0MHz Tick Rate\ntick-hz-18_000_000 = [\"embassy-time-driver/tick-hz-18_000_000\"]\n## 20.0MHz Tick Rate\ntick-hz-20_000_000 = [\"embassy-time-driver/tick-hz-20_000_000\"]\n## 20.48MHz Tick Rate\ntick-hz-20_480_000 = [\"embassy-time-driver/tick-hz-20_480_000\"]\n## 24.0MHz Tick Rate\ntick-hz-24_000_000 = [\"embassy-time-driver/tick-hz-24_000_000\"]\n## 30.0MHz Tick Rate\ntick-hz-30_000_000 = [\"embassy-time-driver/tick-hz-30_000_000\"]\n## 32.0MHz Tick Rate\ntick-hz-32_000_000 = [\"embassy-time-driver/tick-hz-32_000_000\"]\n## 32.768MHz Tick Rate\ntick-hz-32_768_000 = [\"embassy-time-driver/tick-hz-32_768_000\"]\n## 36.0MHz Tick Rate\ntick-hz-36_000_000 = [\"embassy-time-driver/tick-hz-36_000_000\"]\n## 40.0MHz Tick Rate\ntick-hz-40_000_000 = [\"embassy-time-driver/tick-hz-40_000_000\"]\n## 40.96MHz Tick Rate\ntick-hz-40_960_000 = [\"embassy-time-driver/tick-hz-40_960_000\"]\n## 48.0MHz Tick Rate\ntick-hz-48_000_000 = [\"embassy-time-driver/tick-hz-48_000_000\"]\n## 50.0MHz Tick Rate\ntick-hz-50_000_000 = [\"embassy-time-driver/tick-hz-50_000_000\"]\n## 60.0MHz Tick Rate\ntick-hz-60_000_000 = [\"embassy-time-driver/tick-hz-60_000_000\"]\n## 64.0MHz Tick Rate\ntick-hz-64_000_000 = [\"embassy-time-driver/tick-hz-64_000_000\"]\n## 65.536MHz Tick Rate\ntick-hz-65_536_000 = [\"embassy-time-driver/tick-hz-65_536_000\"]\n## 70.0MHz Tick Rate\ntick-hz-70_000_000 = [\"embassy-time-driver/tick-hz-70_000_000\"]\n## 72.0MHz Tick Rate\ntick-hz-72_000_000 = [\"embassy-time-driver/tick-hz-72_000_000\"]\n## 80.0MHz Tick Rate\ntick-hz-80_000_000 = [\"embassy-time-driver/tick-hz-80_000_000\"]\n## 81.92MHz Tick Rate\ntick-hz-81_920_000 = [\"embassy-time-driver/tick-hz-81_920_000\"]\n## 90.0MHz Tick Rate\ntick-hz-90_000_000 = [\"embassy-time-driver/tick-hz-90_000_000\"]\n## 96.0MHz Tick Rate\ntick-hz-96_000_000 = [\"embassy-time-driver/tick-hz-96_000_000\"]\n## 100.0MHz Tick Rate\ntick-hz-100_000_000 = [\"embassy-time-driver/tick-hz-100_000_000\"]\n## 110.0MHz Tick Rate\ntick-hz-110_000_000 = [\"embassy-time-driver/tick-hz-110_000_000\"]\n## 120.0MHz Tick Rate\ntick-hz-120_000_000 = [\"embassy-time-driver/tick-hz-120_000_000\"]\n## 128.0MHz Tick Rate\ntick-hz-128_000_000 = [\"embassy-time-driver/tick-hz-128_000_000\"]\n## 130.0MHz Tick Rate\ntick-hz-130_000_000 = [\"embassy-time-driver/tick-hz-130_000_000\"]\n## 131.072MHz Tick Rate\ntick-hz-131_072_000 = [\"embassy-time-driver/tick-hz-131_072_000\"]\n## 133.0MHz Tick Rate\ntick-hz-133_000_000 = [\"embassy-time-driver/tick-hz-133_000_000\"]\n## 140.0MHz Tick Rate\ntick-hz-140_000_000 = [\"embassy-time-driver/tick-hz-140_000_000\"]\n## 144.0MHz Tick Rate\ntick-hz-144_000_000 = [\"embassy-time-driver/tick-hz-144_000_000\"]\n## 150.0MHz Tick Rate\ntick-hz-150_000_000 = [\"embassy-time-driver/tick-hz-150_000_000\"]\n## 160.0MHz Tick Rate\ntick-hz-160_000_000 = [\"embassy-time-driver/tick-hz-160_000_000\"]\n## 163.84MHz Tick Rate\ntick-hz-163_840_000 = [\"embassy-time-driver/tick-hz-163_840_000\"]\n## 170.0MHz Tick Rate\ntick-hz-170_000_000 = [\"embassy-time-driver/tick-hz-170_000_000\"]\n## 180.0MHz Tick Rate\ntick-hz-180_000_000 = [\"embassy-time-driver/tick-hz-180_000_000\"]\n## 190.0MHz Tick Rate\ntick-hz-190_000_000 = [\"embassy-time-driver/tick-hz-190_000_000\"]\n## 192.0MHz Tick Rate\ntick-hz-192_000_000 = [\"embassy-time-driver/tick-hz-192_000_000\"]\n## 200.0MHz Tick Rate\ntick-hz-200_000_000 = [\"embassy-time-driver/tick-hz-200_000_000\"]\n## 210.0MHz Tick Rate\ntick-hz-210_000_000 = [\"embassy-time-driver/tick-hz-210_000_000\"]\n## 220.0MHz Tick Rate\ntick-hz-220_000_000 = [\"embassy-time-driver/tick-hz-220_000_000\"]\n## 230.0MHz Tick Rate\ntick-hz-230_000_000 = [\"embassy-time-driver/tick-hz-230_000_000\"]\n## 240.0MHz Tick Rate\ntick-hz-240_000_000 = [\"embassy-time-driver/tick-hz-240_000_000\"]\n## 250.0MHz Tick Rate\ntick-hz-250_000_000 = [\"embassy-time-driver/tick-hz-250_000_000\"]\n## 256.0MHz Tick Rate\ntick-hz-256_000_000 = [\"embassy-time-driver/tick-hz-256_000_000\"]\n## 260.0MHz Tick Rate\ntick-hz-260_000_000 = [\"embassy-time-driver/tick-hz-260_000_000\"]\n## 262.144MHz Tick Rate\ntick-hz-262_144_000 = [\"embassy-time-driver/tick-hz-262_144_000\"]\n## 270.0MHz Tick Rate\ntick-hz-270_000_000 = [\"embassy-time-driver/tick-hz-270_000_000\"]\n## 280.0MHz Tick Rate\ntick-hz-280_000_000 = [\"embassy-time-driver/tick-hz-280_000_000\"]\n## 288.0MHz Tick Rate\ntick-hz-288_000_000 = [\"embassy-time-driver/tick-hz-288_000_000\"]\n## 290.0MHz Tick Rate\ntick-hz-290_000_000 = [\"embassy-time-driver/tick-hz-290_000_000\"]\n## 300.0MHz Tick Rate\ntick-hz-300_000_000 = [\"embassy-time-driver/tick-hz-300_000_000\"]\n## 320.0MHz Tick Rate\ntick-hz-320_000_000 = [\"embassy-time-driver/tick-hz-320_000_000\"]\n## 327.68MHz Tick Rate\ntick-hz-327_680_000 = [\"embassy-time-driver/tick-hz-327_680_000\"]\n## 340.0MHz Tick Rate\ntick-hz-340_000_000 = [\"embassy-time-driver/tick-hz-340_000_000\"]\n## 360.0MHz Tick Rate\ntick-hz-360_000_000 = [\"embassy-time-driver/tick-hz-360_000_000\"]\n## 380.0MHz Tick Rate\ntick-hz-380_000_000 = [\"embassy-time-driver/tick-hz-380_000_000\"]\n## 384.0MHz Tick Rate\ntick-hz-384_000_000 = [\"embassy-time-driver/tick-hz-384_000_000\"]\n## 400.0MHz Tick Rate\ntick-hz-400_000_000 = [\"embassy-time-driver/tick-hz-400_000_000\"]\n## 420.0MHz Tick Rate\ntick-hz-420_000_000 = [\"embassy-time-driver/tick-hz-420_000_000\"]\n## 440.0MHz Tick Rate\ntick-hz-440_000_000 = [\"embassy-time-driver/tick-hz-440_000_000\"]\n## 460.0MHz Tick Rate\ntick-hz-460_000_000 = [\"embassy-time-driver/tick-hz-460_000_000\"]\n## 480.0MHz Tick Rate\ntick-hz-480_000_000 = [\"embassy-time-driver/tick-hz-480_000_000\"]\n## 500.0MHz Tick Rate\ntick-hz-500_000_000 = [\"embassy-time-driver/tick-hz-500_000_000\"]\n## 512.0MHz Tick Rate\ntick-hz-512_000_000 = [\"embassy-time-driver/tick-hz-512_000_000\"]\n## 520.0MHz Tick Rate\ntick-hz-520_000_000 = [\"embassy-time-driver/tick-hz-520_000_000\"]\n## 524.288MHz Tick Rate\ntick-hz-524_288_000 = [\"embassy-time-driver/tick-hz-524_288_000\"]\n## 540.0MHz Tick Rate\ntick-hz-540_000_000 = [\"embassy-time-driver/tick-hz-540_000_000\"]\n## 560.0MHz Tick Rate\ntick-hz-560_000_000 = [\"embassy-time-driver/tick-hz-560_000_000\"]\n## 576.0MHz Tick Rate\ntick-hz-576_000_000 = [\"embassy-time-driver/tick-hz-576_000_000\"]\n## 580.0MHz Tick Rate\ntick-hz-580_000_000 = [\"embassy-time-driver/tick-hz-580_000_000\"]\n## 600.0MHz Tick Rate\ntick-hz-600_000_000 = [\"embassy-time-driver/tick-hz-600_000_000\"]\n## 620.0MHz Tick Rate\ntick-hz-620_000_000 = [\"embassy-time-driver/tick-hz-620_000_000\"]\n## 640.0MHz Tick Rate\ntick-hz-640_000_000 = [\"embassy-time-driver/tick-hz-640_000_000\"]\n## 655.36MHz Tick Rate\ntick-hz-655_360_000 = [\"embassy-time-driver/tick-hz-655_360_000\"]\n## 660.0MHz Tick Rate\ntick-hz-660_000_000 = [\"embassy-time-driver/tick-hz-660_000_000\"]\n## 680.0MHz Tick Rate\ntick-hz-680_000_000 = [\"embassy-time-driver/tick-hz-680_000_000\"]\n## 700.0MHz Tick Rate\ntick-hz-700_000_000 = [\"embassy-time-driver/tick-hz-700_000_000\"]\n## 720.0MHz Tick Rate\ntick-hz-720_000_000 = [\"embassy-time-driver/tick-hz-720_000_000\"]\n## 740.0MHz Tick Rate\ntick-hz-740_000_000 = [\"embassy-time-driver/tick-hz-740_000_000\"]\n## 760.0MHz Tick Rate\ntick-hz-760_000_000 = [\"embassy-time-driver/tick-hz-760_000_000\"]\n## 768.0MHz Tick Rate\ntick-hz-768_000_000 = [\"embassy-time-driver/tick-hz-768_000_000\"]\n## 780.0MHz Tick Rate\ntick-hz-780_000_000 = [\"embassy-time-driver/tick-hz-780_000_000\"]\n## 800.0MHz Tick Rate\ntick-hz-800_000_000 = [\"embassy-time-driver/tick-hz-800_000_000\"]\n## 820.0MHz Tick Rate\ntick-hz-820_000_000 = [\"embassy-time-driver/tick-hz-820_000_000\"]\n## 840.0MHz Tick Rate\ntick-hz-840_000_000 = [\"embassy-time-driver/tick-hz-840_000_000\"]\n## 860.0MHz Tick Rate\ntick-hz-860_000_000 = [\"embassy-time-driver/tick-hz-860_000_000\"]\n## 880.0MHz Tick Rate\ntick-hz-880_000_000 = [\"embassy-time-driver/tick-hz-880_000_000\"]\n## 900.0MHz Tick Rate\ntick-hz-900_000_000 = [\"embassy-time-driver/tick-hz-900_000_000\"]\n## 920.0MHz Tick Rate\ntick-hz-920_000_000 = [\"embassy-time-driver/tick-hz-920_000_000\"]\n## 940.0MHz Tick Rate\ntick-hz-940_000_000 = [\"embassy-time-driver/tick-hz-940_000_000\"]\n## 960.0MHz Tick Rate\ntick-hz-960_000_000 = [\"embassy-time-driver/tick-hz-960_000_000\"]\n## 980.0MHz Tick Rate\ntick-hz-980_000_000 = [\"embassy-time-driver/tick-hz-980_000_000\"]\n## 1.0GHz Tick Rate\ntick-hz-1_000_000_000 = [\"embassy-time-driver/tick-hz-1_000_000_000\"]\n## 1.31072GHz Tick Rate\ntick-hz-1_310_720_000 = [\"embassy-time-driver/tick-hz-1_310_720_000\"]\n## 2.62144GHz Tick Rate\ntick-hz-2_621_440_000 = [\"embassy-time-driver/tick-hz-2_621_440_000\"]\n## 5.24288GHz Tick Rate\ntick-hz-5_242_880_000 = [\"embassy-time-driver/tick-hz-5_242_880_000\"]\n# END TICKS\n\n#! </details>\n\n[dependencies]\nembassy-time-driver = { version = \"0.2.2\", path = \"../embassy-time-driver\" }\nembassy-time-queue-utils = { version = \"0.3.0\", path = \"../embassy-time-queue-utils\", optional = true}\n\ndefmt = { version = \"1.0.1\", optional = true }\nlog = { version = \"0.4.14\", optional = true }\n\nembedded-hal-02 = { package = \"embedded-hal\", version = \"0.2.6\" }\nembedded-hal-1 = { package = \"embedded-hal\", version = \"1.0\" }\nembedded-hal-async = { version = \"1.0\" }\n\nfutures-core = { version = \"0.3.31\", default-features = false }\ncritical-section = \"1.1\"\ncfg-if = \"1.0.0\"\n\ndocument-features = \"0.2.7\"\n\n# WASM dependencies\nwasm-bindgen = { version = \"0.2.81\", optional = true }\njs-sys = { version = \"0.3\", optional = true }\nwasm-timer = { version = \"0.2.5\", optional = true }\n\n[dev-dependencies]\nserial_test = \"0.9\"\ncritical-section = { version = \"1.1\", features = [\"std\"] }\nembassy-executor = { version = \"0.10.0\", path = \"../embassy-executor\" }\n"
  },
  {
    "path": "embassy-time/README.md",
    "content": "# embassy-time\n\nTimekeeping, delays and timeouts.\n\nTimekeeping is done with elapsed time since system boot. Time is represented in\nticks, where the tick rate is defined either by the driver (in the case of a fixed-rate\ntick) or chosen by the user with a [tick rate](#tick-rate) feature. The chosen\ntick rate applies to everything in `embassy-time` and thus determines the maximum\ntiming resolution of <code>(1 / tick_rate) seconds</code>.\n\nTick counts are 64 bits. The default tick rate of 1Mhz supports\nrepresenting time spans of up to ~584558 years, which is big enough for all practical\npurposes and allows not having to worry about overflows.\n\n## Global time driver\n\nThe `time` module is backed by a global \"time driver\" specified at build time.\nOnly one driver can be active in a program.\n\nAll methods and structs transparently call into the active driver. This makes it\npossible for libraries to use `embassy_time` in a driver-agnostic way without\nrequiring generic parameters.\n\nFor more details, check the [`embassy_time_driver`](https://crates.io/crates/embassy-time-driver) crate.\n\n## Instants and Durations\n\n[`Instant`] represents a given instant of time (relative to system boot), and [`Duration`]\nrepresents the duration of a span of time. They implement the math operations you'd expect,\nlike addition and substraction.\n\n## Delays and timeouts\n\n[`Timer`] allows performing async delays. [`Ticker`] allows periodic delays without drifting over time.\n\nAn implementation of the `embedded-hal` delay traits is provided by [`Delay`], for compatibility\nwith libraries from the ecosystem.\n\n## Wall-clock time\n\nThe `time` module deals exclusively with a monotonically increasing tick count.\nTherefore it has no direct support for wall-clock time (\"real life\" datetimes\nlike `2021-08-24 13:33:21`).\n\nIf persistence across reboots is not needed, support can be built on top of\n`embassy_time` by storing the offset between \"seconds elapsed since boot\"\nand \"seconds since unix epoch\".\n"
  },
  {
    "path": "embassy-time/src/delay.rs",
    "content": "use core::future::Future;\n\nuse super::{Duration, Instant};\nuse crate::Timer;\n\n/// Blocks for at least `duration`.\npub fn block_for(duration: Duration) {\n    let expires_at = Instant::now() + duration;\n    while Instant::now() < expires_at {}\n}\n\n/// Type implementing async delays and blocking `embedded-hal` delays.\n///\n/// The delays are implemented in a \"best-effort\" way, meaning that the cpu will block for at least\n/// the amount provided, but accuracy can be affected by many factors, including interrupt usage.\n/// Make sure to use a suitable tick rate for your use case. The tick rate is defined by the currently\n/// active driver.\n#[derive(Clone, Debug)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub struct Delay;\n\nimpl embedded_hal_1::delay::DelayNs for Delay {\n    fn delay_ns(&mut self, ns: u32) {\n        block_for(Duration::from_nanos(ns as u64))\n    }\n\n    fn delay_us(&mut self, us: u32) {\n        block_for(Duration::from_micros(us as u64))\n    }\n\n    fn delay_ms(&mut self, ms: u32) {\n        block_for(Duration::from_millis(ms as u64))\n    }\n}\n\nimpl embedded_hal_async::delay::DelayNs for Delay {\n    fn delay_ns(&mut self, ns: u32) -> impl Future<Output = ()> {\n        Timer::after_nanos(ns as _)\n    }\n\n    fn delay_us(&mut self, us: u32) -> impl Future<Output = ()> {\n        Timer::after_micros(us as _)\n    }\n\n    fn delay_ms(&mut self, ms: u32) -> impl Future<Output = ()> {\n        Timer::after_millis(ms as _)\n    }\n}\n\nimpl embedded_hal_02::blocking::delay::DelayMs<u8> for Delay {\n    fn delay_ms(&mut self, ms: u8) {\n        block_for(Duration::from_millis(ms as u64))\n    }\n}\n\nimpl embedded_hal_02::blocking::delay::DelayMs<u16> for Delay {\n    fn delay_ms(&mut self, ms: u16) {\n        block_for(Duration::from_millis(ms as u64))\n    }\n}\n\nimpl embedded_hal_02::blocking::delay::DelayMs<u32> for Delay {\n    fn delay_ms(&mut self, ms: u32) {\n        block_for(Duration::from_millis(ms as u64))\n    }\n}\n\nimpl embedded_hal_02::blocking::delay::DelayUs<u8> for Delay {\n    fn delay_us(&mut self, us: u8) {\n        block_for(Duration::from_micros(us as u64))\n    }\n}\n\nimpl embedded_hal_02::blocking::delay::DelayUs<u16> for Delay {\n    fn delay_us(&mut self, us: u16) {\n        block_for(Duration::from_micros(us as u64))\n    }\n}\n\nimpl embedded_hal_02::blocking::delay::DelayUs<u32> for Delay {\n    fn delay_us(&mut self, us: u32) {\n        block_for(Duration::from_micros(us as u64))\n    }\n}\n"
  },
  {
    "path": "embassy-time/src/driver_mock.rs",
    "content": "use core::cell::RefCell;\nuse core::task::Waker;\n\nuse critical_section::Mutex as CsMutex;\nuse embassy_time_driver::Driver;\nuse embassy_time_queue_utils::Queue;\n\nuse crate::{Duration, Instant};\n\n/// A mock driver that can be manually advanced.\n/// This is useful for testing code that works with [`Instant`] and [`Duration`].\n///\n/// This driver can also be used to test runtime functionality, such as\n/// timers, delays, etc.\n///\n/// # Example\n///\n/// ```ignore\n/// fn has_a_second_passed(reference: Instant) -> bool {\n///     Instant::now().duration_since(reference) >= Duration::from_secs(1)\n/// }\n///\n/// fn test_second_passed() {\n///     let driver = embassy_time::MockDriver::get();\n///     let reference = Instant::now();\n///     assert_eq!(false, has_a_second_passed(reference));\n///     driver.advance(Duration::from_secs(1));\n///     assert_eq!(true, has_a_second_passed(reference));\n/// }\n/// ```\n#[derive(Debug)]\npub struct MockDriver(CsMutex<RefCell<InnerMockDriver>>);\n\nembassy_time_driver::time_driver_impl!(static DRIVER: MockDriver = MockDriver::new());\n\nimpl MockDriver {\n    /// Creates a new mock driver.\n    pub const fn new() -> Self {\n        Self(CsMutex::new(RefCell::new(InnerMockDriver::new())))\n    }\n\n    /// Gets a reference to the global mock driver.\n    pub fn get() -> &'static MockDriver {\n        &DRIVER\n    }\n\n    /// Resets the internal state of the mock driver\n    /// This will clear and deallocate all alarms, and reset the current time to 0.\n    pub fn reset(&self) {\n        critical_section::with(|cs| {\n            self.0.borrow(cs).replace(InnerMockDriver::new());\n        });\n    }\n\n    /// Advances the time by the specified [`Duration`].\n    /// Calling any alarm callbacks that are due.\n    pub fn advance(&self, duration: Duration) {\n        critical_section::with(|cs| {\n            let inner = &mut *self.0.borrow_ref_mut(cs);\n\n            inner.now += duration;\n            // wake expired tasks.\n            inner.queue.next_expiration(inner.now.as_ticks());\n        })\n    }\n}\n\nimpl Driver for MockDriver {\n    fn now(&self) -> u64 {\n        critical_section::with(|cs| self.0.borrow_ref(cs).now).as_ticks()\n    }\n\n    fn schedule_wake(&self, at: u64, waker: &Waker) {\n        critical_section::with(|cs| {\n            let inner = &mut *self.0.borrow_ref_mut(cs);\n            // enqueue it\n            inner.queue.schedule_wake(at, waker);\n            // wake it if it's in the past.\n            inner.queue.next_expiration(inner.now.as_ticks());\n        })\n    }\n}\n\n#[derive(Debug)]\nstruct InnerMockDriver {\n    now: Instant,\n    queue: Queue,\n}\n\nimpl InnerMockDriver {\n    const fn new() -> Self {\n        Self {\n            now: Instant::from_ticks(0),\n            queue: Queue::new(),\n        }\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use core::sync::atomic::{AtomicBool, Ordering};\n    use std::sync::Arc;\n    use std::task::Wake;\n\n    use serial_test::serial;\n\n    use super::*;\n\n    fn setup() {\n        DRIVER.reset();\n    }\n\n    #[test]\n    #[serial]\n    fn test_advance() {\n        setup();\n\n        let driver = MockDriver::get();\n        let reference = driver.now();\n        driver.advance(Duration::from_secs(1));\n        assert_eq!(Duration::from_secs(1).as_ticks(), driver.now() - reference);\n    }\n\n    #[test]\n    #[serial]\n    fn test_schedule_wake() {\n        setup();\n\n        static CALLBACK_CALLED: AtomicBool = AtomicBool::new(false);\n\n        struct MockWaker;\n\n        impl Wake for MockWaker {\n            fn wake(self: Arc<Self>) {\n                CALLBACK_CALLED.store(true, Ordering::Relaxed);\n            }\n        }\n        let waker = Arc::new(MockWaker).into();\n\n        let driver = MockDriver::get();\n\n        driver.schedule_wake(driver.now() + 1, &waker);\n        assert_eq!(false, CALLBACK_CALLED.load(Ordering::Relaxed));\n        driver.advance(Duration::from_secs(1));\n        assert_eq!(true, CALLBACK_CALLED.load(Ordering::Relaxed));\n    }\n}\n"
  },
  {
    "path": "embassy-time/src/driver_std.rs",
    "content": "use std::sync::{Condvar, Mutex};\nuse std::thread;\nuse std::time::{Duration as StdDuration, Instant as StdInstant};\n\nuse embassy_time_driver::Driver;\nuse embassy_time_queue_utils::Queue;\n\n#[derive(Debug)]\nstruct TimeDriver {\n    signaler: Signaler,\n    inner: Mutex<Inner>,\n}\n\n#[derive(Debug)]\nstruct Inner {\n    zero_instant: Option<StdInstant>,\n    queue: Queue,\n}\n\nembassy_time_driver::time_driver_impl!(static DRIVER: TimeDriver = TimeDriver {\n    inner: Mutex::new(Inner{\n        zero_instant: None,\n        queue: Queue::new(),\n    }),\n    signaler: Signaler::new(),\n});\n\nimpl Inner {\n    fn init(&mut self) -> StdInstant {\n        *self.zero_instant.get_or_insert_with(|| {\n            thread::spawn(alarm_thread);\n            StdInstant::now()\n        })\n    }\n}\n\nimpl Driver for TimeDriver {\n    fn now(&self) -> u64 {\n        let mut inner = self.inner.lock().unwrap();\n        let zero = inner.init();\n        StdInstant::now().duration_since(zero).as_micros() as u64\n    }\n\n    fn schedule_wake(&self, at: u64, waker: &core::task::Waker) {\n        let mut inner = self.inner.lock().unwrap();\n        inner.init();\n        if inner.queue.schedule_wake(at, waker) {\n            self.signaler.signal();\n        }\n    }\n}\n\nfn alarm_thread() {\n    let zero = DRIVER.inner.lock().unwrap().zero_instant.unwrap();\n    loop {\n        let now = DRIVER.now();\n\n        let next_alarm = DRIVER.inner.lock().unwrap().queue.next_expiration(now);\n\n        // Ensure we don't overflow\n        let until = zero\n            .checked_add(StdDuration::from_micros(next_alarm))\n            .unwrap_or_else(|| StdInstant::now() + StdDuration::from_secs(1));\n\n        DRIVER.signaler.wait_until(until);\n    }\n}\n\n#[derive(Debug)]\nstruct Signaler {\n    mutex: Mutex<bool>,\n    condvar: Condvar,\n}\n\nimpl Signaler {\n    const fn new() -> Self {\n        Self {\n            mutex: Mutex::new(false),\n            condvar: Condvar::new(),\n        }\n    }\n\n    fn wait_until(&self, until: StdInstant) {\n        let mut signaled = self.mutex.lock().unwrap();\n        while !*signaled {\n            let now = StdInstant::now();\n\n            if now >= until {\n                break;\n            }\n\n            let dur = until - now;\n            let (signaled2, timeout) = self.condvar.wait_timeout(signaled, dur).unwrap();\n            signaled = signaled2;\n            if timeout.timed_out() {\n                break;\n            }\n        }\n        *signaled = false;\n    }\n\n    fn signal(&self) {\n        let mut signaled = self.mutex.lock().unwrap();\n        *signaled = true;\n        self.condvar.notify_one();\n    }\n}\n"
  },
  {
    "path": "embassy-time/src/driver_wasm.rs",
    "content": "use std::sync::Mutex;\n\nuse embassy_time_driver::Driver;\nuse embassy_time_queue_utils::Queue;\nuse wasm_bindgen::prelude::*;\nuse wasm_timer::Instant as StdInstant;\n\n#[derive(Debug)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\nstruct AlarmState {\n    token: Option<f64>,\n}\n\nimpl AlarmState {\n    const fn new() -> Self {\n        Self { token: None }\n    }\n}\n\n#[wasm_bindgen]\nextern \"C\" {\n    fn setTimeout(closure: &Closure<dyn FnMut()>, millis: u32) -> f64;\n    fn clearTimeout(token: f64);\n}\n\n#[derive(Debug)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\nstruct TimeDriver {\n    inner: Mutex<Inner>,\n}\n\n#[derive(Debug)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\nstruct Inner {\n    alarm: AlarmState,\n    zero_instant: Option<StdInstant>,\n    queue: Queue,\n    closure: Option<Closure<dyn FnMut()>>,\n}\n\nunsafe impl Send for Inner {}\n\nembassy_time_driver::time_driver_impl!(static DRIVER: TimeDriver = TimeDriver {\n    inner: Mutex::new(Inner{\n        zero_instant: None,\n        queue: Queue::new(),\n        alarm: AlarmState::new(),\n        closure: None,\n    }),\n});\n\nimpl Inner {\n    fn init(&mut self) -> StdInstant {\n        *self.zero_instant.get_or_insert_with(StdInstant::now)\n    }\n\n    fn now(&mut self) -> u64 {\n        StdInstant::now().duration_since(self.zero_instant.unwrap()).as_micros() as u64\n    }\n\n    fn set_alarm(&mut self, timestamp: u64) -> bool {\n        if let Some(token) = self.alarm.token {\n            clearTimeout(token);\n        }\n\n        let now = self.now();\n        if timestamp <= now {\n            false\n        } else {\n            let timeout = (timestamp - now) as u32;\n            let closure = self.closure.get_or_insert_with(|| Closure::new(dispatch));\n            self.alarm.token = Some(setTimeout(closure, timeout / 1000));\n\n            true\n        }\n    }\n}\n\nimpl Driver for TimeDriver {\n    fn now(&self) -> u64 {\n        let mut inner = self.inner.lock().unwrap();\n        let zero = inner.init();\n        StdInstant::now().duration_since(zero).as_micros() as u64\n    }\n\n    fn schedule_wake(&self, at: u64, waker: &core::task::Waker) {\n        let mut inner = self.inner.lock().unwrap();\n        inner.init();\n        if inner.queue.schedule_wake(at, waker) {\n            let now = inner.now();\n            let mut next = inner.queue.next_expiration(now);\n            while !inner.set_alarm(next) {\n                let now = inner.now();\n                next = inner.queue.next_expiration(now);\n            }\n        }\n    }\n}\n\nfn dispatch() {\n    let inner = &mut *DRIVER.inner.lock().unwrap();\n\n    let now = inner.now();\n    let mut next = inner.queue.next_expiration(now);\n    while !inner.set_alarm(next) {\n        let now = inner.now();\n        next = inner.queue.next_expiration(now);\n    }\n}\n"
  },
  {
    "path": "embassy-time/src/duration.rs",
    "content": "use core::fmt;\nuse core::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Sub, SubAssign};\n\nuse super::{GCD_1K, GCD_1M, TICK_HZ};\nuse crate::GCD_1G;\n\n#[derive(Debug, Default, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\n/// Represents the difference between two [Instant](struct.Instant.html)s\npub struct Duration {\n    pub(crate) ticks: u64,\n}\n\nimpl Duration {\n    /// The smallest value that can be represented by the `Duration` type.\n    pub const MIN: Duration = Duration { ticks: u64::MIN };\n    /// The largest value that can be represented by the `Duration` type.\n    pub const MAX: Duration = Duration { ticks: u64::MAX };\n\n    /// Tick count of the `Duration`.\n    pub const fn as_ticks(&self) -> u64 {\n        self.ticks\n    }\n\n    /// Convert the `Duration` to seconds, rounding down.\n    pub const fn as_secs(&self) -> u64 {\n        self.ticks / TICK_HZ\n    }\n\n    /// Convert the `Duration` to milliseconds, rounding down.\n    pub const fn as_millis(&self) -> u64 {\n        self.ticks * (1000 / GCD_1K) / (TICK_HZ / GCD_1K)\n    }\n\n    /// Convert the `Duration` to microseconds, rounding down.\n    pub const fn as_micros(&self) -> u64 {\n        self.ticks * (1_000_000 / GCD_1M) / (TICK_HZ / GCD_1M)\n    }\n\n    /// Convert the `Duration` to nanoseconds, rounding down.\n    pub const fn as_nanos(&self) -> u64 {\n        self.ticks * (1_000_000_000 / GCD_1G) / (TICK_HZ / GCD_1G)\n    }\n\n    /// Creates a duration from the specified number of clock ticks\n    pub const fn from_ticks(ticks: u64) -> Duration {\n        Duration { ticks }\n    }\n\n    /// Creates a duration from the specified number of seconds, rounding up.\n    pub const fn from_secs(secs: u64) -> Duration {\n        Duration { ticks: secs * TICK_HZ }\n    }\n\n    /// Creates a duration from the specified number of milliseconds, rounding up.\n    pub const fn from_millis(millis: u64) -> Duration {\n        Duration {\n            ticks: div_ceil(millis * (TICK_HZ / GCD_1K), 1000 / GCD_1K),\n        }\n    }\n\n    /// Creates a duration from the specified number of microseconds, rounding up.\n    /// NOTE: Delays this small may be inaccurate.\n    pub const fn from_micros(micros: u64) -> Duration {\n        Duration {\n            ticks: div_ceil(micros * (TICK_HZ / GCD_1M), 1_000_000 / GCD_1M),\n        }\n    }\n\n    /// Creates a duration from the specified number of nanoseconds, rounding up.\n    /// NOTE: Delays this small may be inaccurate.\n    pub const fn from_nanos(nanoseconds: u64) -> Duration {\n        Duration {\n            ticks: div_ceil(nanoseconds * (TICK_HZ / GCD_1G), 1_000_000_000 / GCD_1G),\n        }\n    }\n\n    /// Creates a duration from the specified number of seconds, rounding down.\n    pub const fn from_secs_floor(secs: u64) -> Duration {\n        Duration { ticks: secs * TICK_HZ }\n    }\n\n    /// Creates a duration from the specified number of milliseconds, rounding down.\n    pub const fn from_millis_floor(millis: u64) -> Duration {\n        Duration {\n            ticks: millis * (TICK_HZ / GCD_1K) / (1000 / GCD_1K),\n        }\n    }\n\n    /// Creates a duration from the specified number of microseconds, rounding down.\n    /// NOTE: Delays this small may be inaccurate.\n    pub const fn from_micros_floor(micros: u64) -> Duration {\n        Duration {\n            ticks: micros * (TICK_HZ / GCD_1M) / (1_000_000 / GCD_1M),\n        }\n    }\n\n    /// Try to create a duration from the specified number of seconds, rounding up.\n    /// Fails if the number of seconds is too large.\n    pub const fn try_from_secs(secs: u64) -> Option<Duration> {\n        let Some(ticks) = secs.checked_mul(TICK_HZ) else {\n            return None;\n        };\n        Some(Duration { ticks })\n    }\n\n    /// Try to create a duration from the specified number of milliseconds, rounding up.\n    /// Fails if the number of milliseconds is too large.\n    pub const fn try_from_millis(millis: u64) -> Option<Duration> {\n        let Some(value) = millis.checked_mul(TICK_HZ / GCD_1K) else {\n            return None;\n        };\n        Some(Duration {\n            ticks: div_ceil(value, 1000 / GCD_1K),\n        })\n    }\n\n    /// Try to create a duration from the specified number of microseconds, rounding up.\n    /// Fails if the number of microseconds is too large.\n    /// NOTE: Delays this small may be inaccurate.\n    pub const fn try_from_micros(micros: u64) -> Option<Duration> {\n        let Some(value) = micros.checked_mul(TICK_HZ / GCD_1M) else {\n            return None;\n        };\n        Some(Duration {\n            ticks: div_ceil(value, 1_000_000 / GCD_1M),\n        })\n    }\n\n    /// Try to create a duration from the specified number of nanoseconds, rounding up.\n    /// Fails if the number of nanoseconds is too large.\n    /// NOTE: Delays this small may be inaccurate.\n    pub const fn try_from_nanos(nanoseconds: u64) -> Option<Duration> {\n        let Some(value) = nanoseconds.checked_mul(TICK_HZ / GCD_1G) else {\n            return None;\n        };\n        Some(Duration {\n            ticks: div_ceil(value, 1_000_000_000 / GCD_1G),\n        })\n    }\n\n    /// Try to create a duration from the specified number of seconds, rounding down.\n    /// Fails if the number of seconds is too large.\n    pub const fn try_from_secs_floor(secs: u64) -> Option<Duration> {\n        let Some(ticks) = secs.checked_mul(TICK_HZ) else {\n            return None;\n        };\n        Some(Duration { ticks })\n    }\n\n    /// Try to create a duration from the specified number of milliseconds, rounding down.\n    /// Fails if the number of milliseconds is too large.\n    pub const fn try_from_millis_floor(millis: u64) -> Option<Duration> {\n        let Some(value) = millis.checked_mul(TICK_HZ / GCD_1K) else {\n            return None;\n        };\n        Some(Duration {\n            ticks: value / (1000 / GCD_1K),\n        })\n    }\n\n    /// Try to create a duration from the specified number of microseconds, rounding down.\n    /// Fails if the number of microseconds is too large.\n    /// NOTE: Delays this small may be inaccurate.\n    pub const fn try_from_micros_floor(micros: u64) -> Option<Duration> {\n        let Some(value) = micros.checked_mul(TICK_HZ / GCD_1M) else {\n            return None;\n        };\n        Some(Duration {\n            ticks: value / (1_000_000 / GCD_1M),\n        })\n    }\n\n    /// Creates a duration corresponding to the specified Hz.\n    /// NOTE: Giving this function a hz >= the TICK_HZ of your platform will clamp the Duration to 1\n    /// tick. Doing so will not deadlock, but will certainly not produce the desired output.\n    pub const fn from_hz(hz: u64) -> Duration {\n        let ticks = { if hz >= TICK_HZ { 1 } else { (TICK_HZ + hz / 2) / hz } };\n        Duration { ticks }\n    }\n\n    /// Adds one `Duration` to another, returning a new `Duration` or `None` in the event of an overflow.\n    pub fn checked_add(self, rhs: Duration) -> Option<Duration> {\n        self.ticks.checked_add(rhs.ticks).map(|ticks| Duration { ticks })\n    }\n\n    /// Subtracts one `Duration` from another, returning a new `Duration` or `None` in the event of an overflow.\n    pub fn checked_sub(self, rhs: Duration) -> Option<Duration> {\n        self.ticks.checked_sub(rhs.ticks).map(|ticks| Duration { ticks })\n    }\n\n    /// Multiplies one `Duration` by a scalar `u32`, returning a new `Duration` or `None` in the event of an overflow.\n    pub fn checked_mul(self, rhs: u32) -> Option<Duration> {\n        self.ticks.checked_mul(rhs as _).map(|ticks| Duration { ticks })\n    }\n\n    /// Divides one `Duration` by a scalar `u32`, returning a new `Duration` or `None` in the event of an overflow.\n    pub fn checked_div(self, rhs: u32) -> Option<Duration> {\n        self.ticks.checked_div(rhs as _).map(|ticks| Duration { ticks })\n    }\n}\n\nimpl Add for Duration {\n    type Output = Duration;\n\n    fn add(self, rhs: Duration) -> Duration {\n        self.checked_add(rhs).expect(\"overflow when adding durations\")\n    }\n}\n\nimpl AddAssign for Duration {\n    fn add_assign(&mut self, rhs: Duration) {\n        *self = *self + rhs;\n    }\n}\n\nimpl Sub for Duration {\n    type Output = Duration;\n\n    fn sub(self, rhs: Duration) -> Duration {\n        self.checked_sub(rhs).expect(\"overflow when subtracting durations\")\n    }\n}\n\nimpl SubAssign for Duration {\n    fn sub_assign(&mut self, rhs: Duration) {\n        *self = *self - rhs;\n    }\n}\n\nimpl Mul<u32> for Duration {\n    type Output = Duration;\n\n    fn mul(self, rhs: u32) -> Duration {\n        self.checked_mul(rhs)\n            .expect(\"overflow when multiplying duration by scalar\")\n    }\n}\n\nimpl Mul<Duration> for u32 {\n    type Output = Duration;\n\n    fn mul(self, rhs: Duration) -> Duration {\n        rhs * self\n    }\n}\n\nimpl MulAssign<u32> for Duration {\n    fn mul_assign(&mut self, rhs: u32) {\n        *self = *self * rhs;\n    }\n}\n\nimpl Div<u32> for Duration {\n    type Output = Duration;\n\n    fn div(self, rhs: u32) -> Duration {\n        self.checked_div(rhs)\n            .expect(\"divide by zero error when dividing duration by scalar\")\n    }\n}\n\nimpl DivAssign<u32> for Duration {\n    fn div_assign(&mut self, rhs: u32) {\n        *self = *self / rhs;\n    }\n}\n\nimpl<'a> fmt::Display for Duration {\n    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {\n        write!(f, \"{} ticks\", self.ticks)\n    }\n}\n\n#[inline]\nconst fn div_ceil(num: u64, den: u64) -> u64 {\n    (num + den - 1) / den\n}\n\nimpl TryFrom<core::time::Duration> for Duration {\n    type Error = <u64 as TryFrom<u128>>::Error;\n\n    /// Converts using [`Duration::from_micros`]. Fails if value can not be represented as u64.\n    fn try_from(value: core::time::Duration) -> Result<Self, Self::Error> {\n        Ok(Self::from_micros(value.as_micros().try_into()?))\n    }\n}\n\nimpl From<Duration> for core::time::Duration {\n    /// Converts using [`Duration::as_micros`].\n    fn from(value: Duration) -> Self {\n        core::time::Duration::from_micros(value.as_micros())\n    }\n}\n\nimpl core::iter::Sum for Duration {\n    fn sum<I>(iter: I) -> Self\n    where\n        I: Iterator<Item = Duration>,\n    {\n        Duration::from_ticks(iter.map(|d| d.as_ticks()).sum())\n    }\n}\n"
  },
  {
    "path": "embassy-time/src/fmt.rs",
    "content": "#![macro_use]\n#![allow(unused)]\n\nuse core::fmt::{Debug, Display, LowerHex};\n\n#[cfg(all(feature = \"defmt\", feature = \"log\"))]\ncompile_error!(\"You may not enable both `defmt` and `log` features.\");\n\n#[collapse_debuginfo(yes)]\nmacro_rules! assert {\n    ($($x:tt)*) => {\n        {\n            #[cfg(not(feature = \"defmt\"))]\n            ::core::assert!($($x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::assert!($($x)*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! assert_eq {\n    ($($x:tt)*) => {\n        {\n            #[cfg(not(feature = \"defmt\"))]\n            ::core::assert_eq!($($x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::assert_eq!($($x)*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! assert_ne {\n    ($($x:tt)*) => {\n        {\n            #[cfg(not(feature = \"defmt\"))]\n            ::core::assert_ne!($($x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::assert_ne!($($x)*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! debug_assert {\n    ($($x:tt)*) => {\n        {\n            #[cfg(not(feature = \"defmt\"))]\n            ::core::debug_assert!($($x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::debug_assert!($($x)*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! debug_assert_eq {\n    ($($x:tt)*) => {\n        {\n            #[cfg(not(feature = \"defmt\"))]\n            ::core::debug_assert_eq!($($x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::debug_assert_eq!($($x)*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! debug_assert_ne {\n    ($($x:tt)*) => {\n        {\n            #[cfg(not(feature = \"defmt\"))]\n            ::core::debug_assert_ne!($($x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::debug_assert_ne!($($x)*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! todo {\n    ($($x:tt)*) => {\n        {\n            #[cfg(not(feature = \"defmt\"))]\n            ::core::todo!($($x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::todo!($($x)*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! unreachable {\n    ($($x:tt)*) => {\n        {\n            #[cfg(not(feature = \"defmt\"))]\n            ::core::unreachable!($($x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::unreachable!($($x)*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! panic {\n    ($($x:tt)*) => {\n        {\n            #[cfg(not(feature = \"defmt\"))]\n            ::core::panic!($($x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::panic!($($x)*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! trace {\n    ($s:literal $(, $x:expr)* $(,)?) => {\n        {\n            #[cfg(feature = \"log\")]\n            ::log::trace!($s $(, $x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::trace!($s $(, $x)*);\n            #[cfg(not(any(feature = \"log\", feature=\"defmt\")))]\n            let _ = ($( & $x ),*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! debug {\n    ($s:literal $(, $x:expr)* $(,)?) => {\n        {\n            #[cfg(feature = \"log\")]\n            ::log::debug!($s $(, $x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::debug!($s $(, $x)*);\n            #[cfg(not(any(feature = \"log\", feature=\"defmt\")))]\n            let _ = ($( & $x ),*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! info {\n    ($s:literal $(, $x:expr)* $(,)?) => {\n        {\n            #[cfg(feature = \"log\")]\n            ::log::info!($s $(, $x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::info!($s $(, $x)*);\n            #[cfg(not(any(feature = \"log\", feature=\"defmt\")))]\n            let _ = ($( & $x ),*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! warn {\n    ($s:literal $(, $x:expr)* $(,)?) => {\n        {\n            #[cfg(feature = \"log\")]\n            ::log::warn!($s $(, $x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::warn!($s $(, $x)*);\n            #[cfg(not(any(feature = \"log\", feature=\"defmt\")))]\n            let _ = ($( & $x ),*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! error {\n    ($s:literal $(, $x:expr)* $(,)?) => {\n        {\n            #[cfg(feature = \"log\")]\n            ::log::error!($s $(, $x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::error!($s $(, $x)*);\n            #[cfg(not(any(feature = \"log\", feature=\"defmt\")))]\n            let _ = ($( & $x ),*);\n        }\n    };\n}\n\n#[cfg(feature = \"defmt\")]\n#[collapse_debuginfo(yes)]\nmacro_rules! unwrap {\n    ($($x:tt)*) => {\n        ::defmt::unwrap!($($x)*)\n    };\n}\n\n#[cfg(not(feature = \"defmt\"))]\n#[collapse_debuginfo(yes)]\nmacro_rules! unwrap {\n    ($arg:expr) => {\n        match $crate::fmt::Try::into_result($arg) {\n            ::core::result::Result::Ok(t) => t,\n            ::core::result::Result::Err(e) => {\n                ::core::panic!(\"unwrap of `{}` failed: {:?}\", ::core::stringify!($arg), e);\n            }\n        }\n    };\n    ($arg:expr, $($msg:expr),+ $(,)? ) => {\n        match $crate::fmt::Try::into_result($arg) {\n            ::core::result::Result::Ok(t) => t,\n            ::core::result::Result::Err(e) => {\n                ::core::panic!(\"unwrap of `{}` failed: {}: {:?}\", ::core::stringify!($arg), ::core::format_args!($($msg,)*), e);\n            }\n        }\n    }\n}\n\n#[derive(Debug, Copy, Clone, Eq, PartialEq)]\npub struct NoneError;\n\npub trait Try {\n    type Ok;\n    type Error;\n    fn into_result(self) -> Result<Self::Ok, Self::Error>;\n}\n\nimpl<T> Try for Option<T> {\n    type Ok = T;\n    type Error = NoneError;\n\n    #[inline]\n    fn into_result(self) -> Result<T, NoneError> {\n        self.ok_or(NoneError)\n    }\n}\n\nimpl<T, E> Try for Result<T, E> {\n    type Ok = T;\n    type Error = E;\n\n    #[inline]\n    fn into_result(self) -> Self {\n        self\n    }\n}\n\npub(crate) struct Bytes<'a>(pub &'a [u8]);\n\nimpl<'a> Debug for Bytes<'a> {\n    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {\n        write!(f, \"{:#02x?}\", self.0)\n    }\n}\n\nimpl<'a> Display for Bytes<'a> {\n    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {\n        write!(f, \"{:#02x?}\", self.0)\n    }\n}\n\nimpl<'a> LowerHex for Bytes<'a> {\n    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {\n        write!(f, \"{:#02x?}\", self.0)\n    }\n}\n\n#[cfg(feature = \"defmt\")]\nimpl<'a> defmt::Format for Bytes<'a> {\n    fn format(&self, fmt: defmt::Formatter) {\n        defmt::write!(fmt, \"{:02x}\", self.0)\n    }\n}\n"
  },
  {
    "path": "embassy-time/src/instant.rs",
    "content": "use core::fmt;\nuse core::ops::{Add, AddAssign, Sub, SubAssign};\n\nuse super::{Duration, GCD_1G, GCD_1K, GCD_1M, TICK_HZ};\n\n#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\n/// An Instant in time, based on the MCU's clock ticks since startup.\npub struct Instant {\n    ticks: u64,\n}\n\nimpl Instant {\n    /// The smallest (earliest) value that can be represented by the `Instant` type.\n    pub const MIN: Instant = Instant { ticks: u64::MIN };\n    /// The largest (latest) value that can be represented by the `Instant` type.\n    pub const MAX: Instant = Instant { ticks: u64::MAX };\n\n    /// Returns an Instant representing the current time.\n    #[inline]\n    pub fn now() -> Instant {\n        Instant {\n            ticks: embassy_time_driver::now(),\n        }\n    }\n\n    /// Create an Instant from a tick count since system boot.\n    pub const fn from_ticks(ticks: u64) -> Self {\n        Self { ticks }\n    }\n\n    /// Create an Instant from a nanosecond count since system boot.\n    pub const fn from_nanos(nanos: u64) -> Self {\n        Self {\n            ticks: nanos * (TICK_HZ / GCD_1G) / (1_000_000_000 / GCD_1G),\n        }\n    }\n\n    /// Create an Instant from a microsecond count since system boot.\n    pub const fn from_micros(micros: u64) -> Self {\n        Self {\n            ticks: micros * (TICK_HZ / GCD_1M) / (1_000_000 / GCD_1M),\n        }\n    }\n\n    /// Create an Instant from a millisecond count since system boot.\n    pub const fn from_millis(millis: u64) -> Self {\n        Self {\n            ticks: millis * (TICK_HZ / GCD_1K) / (1000 / GCD_1K),\n        }\n    }\n\n    /// Create an Instant from a second count since system boot.\n    pub const fn from_secs(seconds: u64) -> Self {\n        Self {\n            ticks: seconds * TICK_HZ,\n        }\n    }\n\n    /// Try to create an Instant from a nanosecond count since system boot.\n    /// Fails if the number of nanoseconds is too large.\n    pub const fn try_from_nanos(nanos: u64) -> Option<Self> {\n        let Some(value) = nanos.checked_mul(TICK_HZ / GCD_1G) else {\n            return None;\n        };\n        Some(Self {\n            ticks: value / (1_000_000_000 / GCD_1G),\n        })\n    }\n\n    /// Try to create an Instant from a microsecond count since system boot.\n    /// Fails if the number of microseconds is too large.\n    pub const fn try_from_micros(micros: u64) -> Option<Self> {\n        let Some(value) = micros.checked_mul(TICK_HZ / GCD_1M) else {\n            return None;\n        };\n        Some(Self {\n            ticks: value / (1_000_000 / GCD_1M),\n        })\n    }\n\n    /// Try to create an Instant from a millisecond count since system boot.\n    /// Fails if the number of milliseconds is too large.\n    pub const fn try_from_millis(millis: u64) -> Option<Self> {\n        let Some(value) = millis.checked_mul(TICK_HZ / GCD_1K) else {\n            return None;\n        };\n        Some(Self {\n            ticks: value / (1000 / GCD_1K),\n        })\n    }\n\n    /// Try to create an Instant from a second count since system boot.\n    /// Fails if the number of seconds is too large.\n    pub const fn try_from_secs(seconds: u64) -> Option<Self> {\n        let Some(ticks) = seconds.checked_mul(TICK_HZ) else {\n            return None;\n        };\n        Some(Self { ticks })\n    }\n\n    /// Tick count since system boot.\n    pub const fn as_ticks(&self) -> u64 {\n        self.ticks\n    }\n\n    /// Seconds since system boot.\n    pub const fn as_secs(&self) -> u64 {\n        self.ticks / TICK_HZ\n    }\n\n    /// Milliseconds since system boot.\n    pub const fn as_millis(&self) -> u64 {\n        self.ticks * (1000 / GCD_1K) / (TICK_HZ / GCD_1K)\n    }\n\n    /// Microseconds since system boot.\n    pub const fn as_micros(&self) -> u64 {\n        self.ticks * (1_000_000 / GCD_1M) / (TICK_HZ / GCD_1M)\n    }\n\n    /// Nanoseconds since system boot.\n    pub const fn as_nanos(&self) -> u64 {\n        self.ticks * (1_000_000_000 / GCD_1G) / (TICK_HZ / GCD_1G)\n    }\n\n    /// Duration between this Instant and another Instant\n    /// Panics on over/underflow.\n    pub fn duration_since(&self, earlier: Instant) -> Duration {\n        Duration {\n            ticks: unwrap!(self.ticks.checked_sub(earlier.ticks)),\n        }\n    }\n\n    /// Duration between this Instant and another Instant\n    pub fn checked_duration_since(&self, earlier: Instant) -> Option<Duration> {\n        if self.ticks < earlier.ticks {\n            None\n        } else {\n            Some(Duration {\n                ticks: self.ticks - earlier.ticks,\n            })\n        }\n    }\n\n    /// Returns the duration since the \"earlier\" Instant.\n    /// If the \"earlier\" instant is in the future, the duration is set to zero.\n    pub fn saturating_duration_since(&self, earlier: Instant) -> Duration {\n        Duration {\n            ticks: if self.ticks < earlier.ticks {\n                0\n            } else {\n                self.ticks - earlier.ticks\n            },\n        }\n    }\n\n    /// Duration elapsed since this Instant.\n    pub fn elapsed(&self) -> Duration {\n        Instant::now() - *self\n    }\n\n    /// Adds one Duration to self, returning a new `Instant` or None in the event of an overflow.\n    pub fn checked_add(&self, duration: Duration) -> Option<Instant> {\n        self.ticks.checked_add(duration.ticks).map(|ticks| Instant { ticks })\n    }\n\n    /// Subtracts one Duration to self, returning a new `Instant` or None in the event of an overflow.\n    pub fn checked_sub(&self, duration: Duration) -> Option<Instant> {\n        self.ticks.checked_sub(duration.ticks).map(|ticks| Instant { ticks })\n    }\n\n    /// Adds a Duration to self. In case of overflow, the maximum value is returned.\n    pub fn saturating_add(mut self, duration: Duration) -> Self {\n        self.ticks = self.ticks.saturating_add(duration.ticks);\n        self\n    }\n\n    /// Subtracts a Duration from self. In case of overflow, the minimum value is returned.\n    pub fn saturating_sub(mut self, duration: Duration) -> Self {\n        self.ticks = self.ticks.saturating_sub(duration.ticks);\n        self\n    }\n}\n\nimpl Add<Duration> for Instant {\n    type Output = Instant;\n\n    fn add(self, other: Duration) -> Instant {\n        self.checked_add(other)\n            .expect(\"overflow when adding duration to instant\")\n    }\n}\n\nimpl AddAssign<Duration> for Instant {\n    fn add_assign(&mut self, other: Duration) {\n        *self = *self + other;\n    }\n}\n\nimpl Sub<Duration> for Instant {\n    type Output = Instant;\n\n    fn sub(self, other: Duration) -> Instant {\n        self.checked_sub(other)\n            .expect(\"overflow when subtracting duration from instant\")\n    }\n}\n\nimpl SubAssign<Duration> for Instant {\n    fn sub_assign(&mut self, other: Duration) {\n        *self = *self - other;\n    }\n}\n\nimpl Sub<Instant> for Instant {\n    type Output = Duration;\n\n    fn sub(self, other: Instant) -> Duration {\n        self.duration_since(other)\n    }\n}\n\nimpl<'a> fmt::Display for Instant {\n    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {\n        write!(f, \"{} ticks\", self.ticks)\n    }\n}\n"
  },
  {
    "path": "embassy-time/src/lib.rs",
    "content": "#![cfg_attr(not(any(feature = \"std\", feature = \"wasm\", test)), no_std)]\n#![allow(async_fn_in_trait)]\n#![allow(unsafe_op_in_unsafe_fn)]\n#![doc = include_str!(\"../README.md\")]\n#![allow(clippy::new_without_default)]\n#![warn(missing_docs)]\n#![deny(missing_debug_implementations)]\n\n//! ## Feature flags\n#![doc = document_features::document_features!(feature_label = r#\"<span class=\"stab portability\"><code>{feature}</code></span>\"#)]\n\n// This mod MUST go first, so that the others see its macros.\npub(crate) mod fmt;\n\nmod delay;\nmod duration;\nmod instant;\nmod timer;\n\n#[cfg(feature = \"mock-driver\")]\nmod driver_mock;\n\n#[cfg(feature = \"mock-driver\")]\npub use driver_mock::MockDriver;\n\n#[cfg(feature = \"std\")]\nmod driver_std;\n#[cfg(feature = \"wasm\")]\nmod driver_wasm;\n\npub use delay::{Delay, block_for};\npub use duration::Duration;\npub use embassy_time_driver::TICK_HZ;\npub use instant::Instant;\npub use timer::{Ticker, TimeoutError, Timer, WithTimeout, with_deadline, with_timeout};\n\nconst fn gcd(a: u64, b: u64) -> u64 {\n    if b == 0 { a } else { gcd(b, a % b) }\n}\n\npub(crate) const GCD_1K: u64 = gcd(TICK_HZ, 1_000);\npub(crate) const GCD_1M: u64 = gcd(TICK_HZ, 1_000_000);\npub(crate) const GCD_1G: u64 = gcd(TICK_HZ, 1_000_000_000);\n\n#[cfg(feature = \"defmt-timestamp-uptime-s\")]\ndefmt::timestamp! {\"{=u64}\", Instant::now().as_secs() }\n\n#[cfg(feature = \"defmt-timestamp-uptime-ms\")]\ndefmt::timestamp! {\"{=u64:ms}\", Instant::now().as_millis() }\n\n#[cfg(any(feature = \"defmt-timestamp-uptime\", feature = \"defmt-timestamp-uptime-us\"))]\ndefmt::timestamp! {\"{=u64:us}\", Instant::now().as_micros() }\n\n#[cfg(feature = \"defmt-timestamp-uptime-ts\")]\ndefmt::timestamp! {\"{=u64:ts}\", Instant::now().as_secs() }\n\n#[cfg(feature = \"defmt-timestamp-uptime-tms\")]\ndefmt::timestamp! {\"{=u64:tms}\", Instant::now().as_millis() }\n\n#[cfg(feature = \"defmt-timestamp-uptime-tus\")]\ndefmt::timestamp! {\"{=u64:tus}\", Instant::now().as_micros() }\n"
  },
  {
    "path": "embassy-time/src/timer.rs",
    "content": "use core::future::{Future, poll_fn};\nuse core::pin::Pin;\nuse core::task::{Context, Poll};\n\nuse futures_core::Stream;\nuse futures_core::stream::FusedStream;\n\nuse crate::{Duration, Instant};\n\n/// Error returned by [`with_timeout`] and [`with_deadline`] on timeout.\n#[derive(Debug, Clone, PartialEq, Eq)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub struct TimeoutError;\n\n/// Runs a given future with a timeout.\n///\n/// If the future completes before the timeout, its output is returned. Otherwise, on timeout,\n/// work on the future is stopped (`poll` is no longer called), the future is dropped and `Err(TimeoutError)` is returned.\npub fn with_timeout<F: Future>(timeout: Duration, fut: F) -> TimeoutFuture<F> {\n    TimeoutFuture {\n        timer: Timer::after(timeout),\n        fut,\n    }\n}\n\n/// Runs a given future with a deadline time.\n///\n/// If the future completes before the deadline, its output is returned. Otherwise, on timeout,\n/// work on the future is stopped (`poll` is no longer called), the future is dropped and `Err(TimeoutError)` is returned.\npub fn with_deadline<F: Future>(at: Instant, fut: F) -> TimeoutFuture<F> {\n    TimeoutFuture {\n        timer: Timer::at(at),\n        fut,\n    }\n}\n\n/// Provides functions to run a given future with a timeout or a deadline.\npub trait WithTimeout: Sized {\n    /// Output type of the future.\n    type Output;\n\n    /// Runs a given future with a timeout.\n    ///\n    /// If the future completes before the timeout, its output is returned. Otherwise, on timeout,\n    /// work on the future is stopped (`poll` is no longer called), the future is dropped and `Err(TimeoutError)` is returned.\n    fn with_timeout(self, timeout: Duration) -> TimeoutFuture<Self>;\n\n    /// Runs a given future with a deadline time.\n    ///\n    /// If the future completes before the deadline, its output is returned. Otherwise, on timeout,\n    /// work on the future is stopped (`poll` is no longer called), the future is dropped and `Err(TimeoutError)` is returned.\n    fn with_deadline(self, at: Instant) -> TimeoutFuture<Self>;\n}\n\nimpl<F: Future> WithTimeout for F {\n    type Output = F::Output;\n\n    fn with_timeout(self, timeout: Duration) -> TimeoutFuture<Self> {\n        with_timeout(timeout, self)\n    }\n\n    fn with_deadline(self, at: Instant) -> TimeoutFuture<Self> {\n        with_deadline(at, self)\n    }\n}\n\n/// Future for the [`with_timeout`] and [`with_deadline`] functions.\n#[must_use = \"futures do nothing unless you `.await` or poll them\"]\n#[derive(Debug)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub struct TimeoutFuture<F> {\n    timer: Timer,\n    fut: F,\n}\n\nimpl<F: Unpin> Unpin for TimeoutFuture<F> {}\n\nimpl<F: Future> Future for TimeoutFuture<F> {\n    type Output = Result<F::Output, TimeoutError>;\n\n    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {\n        let this = unsafe { self.get_unchecked_mut() };\n        let fut = unsafe { Pin::new_unchecked(&mut this.fut) };\n        let timer = unsafe { Pin::new_unchecked(&mut this.timer) };\n        if let Poll::Ready(x) = fut.poll(cx) {\n            return Poll::Ready(Ok(x));\n        }\n        if let Poll::Ready(_) = timer.poll(cx) {\n            return Poll::Ready(Err(TimeoutError));\n        }\n        Poll::Pending\n    }\n}\n\n/// A future that completes at a specified [Instant](struct.Instant.html).\n#[must_use = \"futures do nothing unless you `.await` or poll them\"]\n#[derive(Debug)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub struct Timer {\n    expires_at: Instant,\n    yielded_once: bool,\n}\n\nimpl Timer {\n    /// Expire at specified [Instant](struct.Instant.html)\n    /// Will expire immediately if the Instant is in the past.\n    pub fn at(expires_at: Instant) -> Self {\n        Self {\n            expires_at,\n            yielded_once: false,\n        }\n    }\n\n    /// Expire after specified [Duration](struct.Duration.html).\n    /// This can be used as a `sleep` abstraction.\n    ///\n    /// Example:\n    /// ``` no_run\n    /// use embassy_time::{Duration, Timer};\n    ///\n    /// #[embassy_executor::task]\n    /// async fn demo_sleep_seconds() {\n    ///     // suspend this task for one second.\n    ///     Timer::after(Duration::from_secs(1)).await;\n    /// }\n    /// ```\n    pub fn after(duration: Duration) -> Self {\n        Self {\n            expires_at: Instant::now() + duration,\n            yielded_once: false,\n        }\n    }\n\n    /// Expire after the specified number of ticks.\n    ///\n    /// This method is a convenience wrapper for calling `Timer::after(Duration::from_ticks())`.\n    /// For more details, refer to [`Timer::after()`] and [`Duration::from_ticks()`].\n    #[inline]\n    pub fn after_ticks(ticks: u64) -> Self {\n        Self::after(Duration::from_ticks(ticks))\n    }\n\n    /// Expire after the specified number of nanoseconds.\n    ///\n    /// This method is a convenience wrapper for calling `Timer::after(Duration::from_nanos())`.\n    /// For more details, refer to [`Timer::after()`] and [`Duration::from_nanos()`].\n    #[inline]\n    pub fn after_nanos(nanos: u64) -> Self {\n        Self::after(Duration::from_nanos(nanos))\n    }\n\n    /// Expire after the specified number of microseconds.\n    ///\n    /// This method is a convenience wrapper for calling `Timer::after(Duration::from_micros())`.\n    /// For more details, refer to [`Timer::after()`] and [`Duration::from_micros()`].\n    #[inline]\n    pub fn after_micros(micros: u64) -> Self {\n        Self::after(Duration::from_micros(micros))\n    }\n\n    /// Expire after the specified number of milliseconds.\n    ///\n    /// This method is a convenience wrapper for calling `Timer::after(Duration::from_millis())`.\n    /// For more details, refer to [`Timer::after`] and [`Duration::from_millis()`].\n    #[inline]\n    pub fn after_millis(millis: u64) -> Self {\n        Self::after(Duration::from_millis(millis))\n    }\n\n    /// Expire after the specified number of seconds.\n    ///\n    /// This method is a convenience wrapper for calling `Timer::after(Duration::from_secs())`.\n    /// For more details, refer to [`Timer::after`] and [`Duration::from_secs()`].\n    #[inline]\n    pub fn after_secs(secs: u64) -> Self {\n        Self::after(Duration::from_secs(secs))\n    }\n}\n\nimpl Unpin for Timer {}\n\nimpl Future for Timer {\n    type Output = ();\n    fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {\n        if self.yielded_once && self.expires_at <= Instant::now() {\n            Poll::Ready(())\n        } else {\n            embassy_time_driver::schedule_wake(self.expires_at.as_ticks(), cx.waker());\n            self.yielded_once = true;\n            Poll::Pending\n        }\n    }\n}\n\n/// Asynchronous stream that yields every Duration, indefinitely.\n///\n/// This stream will tick at uniform intervals, even if blocking work is performed between ticks.\n///\n/// For instance, consider the following code fragment.\n/// ``` no_run\n/// use embassy_time::{Duration, Timer};\n/// # fn foo() {}\n///\n/// #[embassy_executor::task]\n/// async fn ticker_example_0() {\n///     loop {\n///         foo();\n///         Timer::after(Duration::from_secs(1)).await;\n///     }\n/// }\n/// ```\n///\n/// This fragment will not call `foo` every second.\n/// Instead, it will call it every second + the time it took to previously call `foo`.\n///\n/// Example using ticker, which will consistently call `foo` once a second.\n///\n/// ``` no_run\n/// use embassy_time::{Duration, Ticker};\n/// # fn foo(){}\n///\n/// #[embassy_executor::task]\n/// async fn ticker_example_1() {\n///     let mut ticker = Ticker::every(Duration::from_secs(1));\n///     loop {\n///         foo();\n///         ticker.next().await;\n///     }\n/// }\n/// ```\n///\n/// ## Cancel safety\n/// It is safe to cancel waiting for the next tick,\n/// meaning no tick is lost if the Future is dropped.\n#[derive(Debug)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub struct Ticker {\n    expires_at: Instant,\n    duration: Duration,\n}\n\nimpl Ticker {\n    /// Creates a new ticker that ticks at the specified duration interval.\n    pub fn every(duration: Duration) -> Self {\n        let expires_at = Instant::now() + duration;\n        Self { expires_at, duration }\n    }\n\n    /// Resets the ticker back to its original state.\n    /// This causes the ticker to go back to zero, even if the current tick isn't over yet.\n    pub fn reset(&mut self) {\n        self.expires_at = Instant::now() + self.duration;\n    }\n\n    /// Reset the ticker at the deadline.\n    /// If the deadline is in the past, the ticker will fire before the next scheduled tick.\n    pub fn reset_at(&mut self, deadline: Instant) {\n        self.expires_at = deadline + self.duration;\n    }\n\n    /// Resets the ticker, after the specified duration has passed.\n    /// If the specified duration is zero, the next tick will be after the duration of the ticker.\n    pub fn reset_after(&mut self, after: Duration) {\n        self.expires_at = Instant::now() + after + self.duration;\n    }\n\n    /// Waits for the next tick.\n    ///\n    /// ## Cancel safety\n    /// The produced Future is cancel safe, meaning no tick is lost if the Future is dropped.\n    pub fn next(&mut self) -> impl Future<Output = ()> + Send + Sync + '_ {\n        poll_fn(|cx| {\n            if self.expires_at <= Instant::now() {\n                let dur = self.duration;\n                self.expires_at += dur;\n                Poll::Ready(())\n            } else {\n                embassy_time_driver::schedule_wake(self.expires_at.as_ticks(), cx.waker());\n                Poll::Pending\n            }\n        })\n    }\n}\n\nimpl Unpin for Ticker {}\n\nimpl Stream for Ticker {\n    type Item = ();\n    fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {\n        if self.expires_at <= Instant::now() {\n            let dur = self.duration;\n            self.expires_at += dur;\n            Poll::Ready(Some(()))\n        } else {\n            embassy_time_driver::schedule_wake(self.expires_at.as_ticks(), cx.waker());\n            Poll::Pending\n        }\n    }\n}\n\nimpl FusedStream for Ticker {\n    fn is_terminated(&self) -> bool {\n        // `Ticker` keeps yielding values until dropped, it never terminates.\n        false\n    }\n}\n"
  },
  {
    "path": "embassy-time-driver/CHANGELOG.md",
    "content": "# Changelog for embassy-time-driver\n\nAll notable changes to this project will be documented in this file.\n\nThe format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),\nand this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).\n\n<!-- next-header -->\n## Unreleased - ReleaseDate\n\n## 0.2.2 - 2026-03-20\n\n- Add 375KHz tick rate support\n\n## 0.2.1 - 2025-08-26\n\n- Allow inlining on time driver boundary\n- Add 133MHz tick rate to support PR2040 @ 133MHz when `TIMERx`'s `SOURCE` is set to `SYSCLK`\n\n## 0.2.0 - 2025-01-02\n\n- The `allocate_alarm`, `set_alarm_callback`, `set_alarm` functions have been removed.\n- `schedule_wake` has been added to the `Driver` trait.\n\n## 0.1.0 - 2024-01-11\n\nInitial release\n"
  },
  {
    "path": "embassy-time-driver/Cargo.toml",
    "content": "[package]\nname = \"embassy-time-driver\"\nversion = \"0.2.2\"\nedition = \"2024\"\ndescription = \"Driver trait for embassy-time\"\nrepository = \"https://github.com/embassy-rs/embassy\"\ndocumentation = \"https://docs.embassy.dev/embassy-time-driver\"\nreadme = \"README.md\"\nlicense = \"MIT OR Apache-2.0\"\ncategories = [\n    \"embedded\",\n    \"no-std\",\n    \"concurrency\",\n    \"asynchronous\",\n]\n\n# Prevent multiple copies of this crate in the same binary.\n# Needed because different copies might get different tick rates, causing\n# wrong delays if the time driver is using one copy and user code is using another.\n# This is especially common when mixing crates from crates.io and git.\nlinks = \"embassy-time\"\n\n[package.metadata.embassy_docs]\nsrc_base = \"https://github.com/embassy-rs/embassy/blob/embassy-time-driver-v$VERSION/embassy-time-driver/src/\"\nsrc_base_git = \"https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-time-driver/src/\"\ntarget = \"x86_64-unknown-linux-gnu\"\n\n[features]\n#! ### Tick Rate\n#!\n#! At most 1 `tick-*` feature can be enabled. If none is enabled, a default of 1MHz is used.\n#! \n#! If the time driver in use supports using arbitrary tick rates, you can enable one `tick-*`\n#! feature from your binary crate to set the tick rate. The driver will use configured tick rate.\n#! If the time driver supports a fixed tick rate, it will enable one feature itself, so you should\n#! not enable one. Check the time driver documentation for details.\n#!\n#! When using embassy-time from libraries, you should *not* enable any `tick-*` feature, to allow the\n#! end user or the driver to pick.\n#! <details>\n#!   <summary>Available tick rates:</summary>\n#! <!-- Next line must be left empty for the features to render correctly! -->\n#! \n\n# BEGIN TICKS\n# Generated by gen_tick.py. DO NOT EDIT.\n## 1Hz Tick Rate\ntick-hz-1 = []\n## 2Hz Tick Rate\ntick-hz-2 = []\n## 4Hz Tick Rate\ntick-hz-4 = []\n## 8Hz Tick Rate\ntick-hz-8 = []\n## 10Hz Tick Rate\ntick-hz-10 = []\n## 16Hz Tick Rate\ntick-hz-16 = []\n## 32Hz Tick Rate\ntick-hz-32 = []\n## 64Hz Tick Rate\ntick-hz-64 = []\n## 100Hz Tick Rate\ntick-hz-100 = []\n## 128Hz Tick Rate\ntick-hz-128 = []\n## 256Hz Tick Rate\ntick-hz-256 = []\n## 512Hz Tick Rate\ntick-hz-512 = []\n## 1.0kHz Tick Rate\ntick-hz-1_000 = []\n## 1.024kHz Tick Rate\ntick-hz-1_024 = []\n## 2.0kHz Tick Rate\ntick-hz-2_000 = []\n## 2.048kHz Tick Rate\ntick-hz-2_048 = []\n## 4.0kHz Tick Rate\ntick-hz-4_000 = []\n## 4.096kHz Tick Rate\ntick-hz-4_096 = []\n## 8.0kHz Tick Rate\ntick-hz-8_000 = []\n## 8.192kHz Tick Rate\ntick-hz-8_192 = []\n## 10.0kHz Tick Rate\ntick-hz-10_000 = []\n## 16.0kHz Tick Rate\ntick-hz-16_000 = []\n## 16.384kHz Tick Rate\ntick-hz-16_384 = []\n## 20.0kHz Tick Rate\ntick-hz-20_000 = []\n## 32.0kHz Tick Rate\ntick-hz-32_000 = []\n## 32.768kHz Tick Rate\ntick-hz-32_768 = []\n## 40.0kHz Tick Rate\ntick-hz-40_000 = []\n## 64.0kHz Tick Rate\ntick-hz-64_000 = []\n## 65.536kHz Tick Rate\ntick-hz-65_536 = []\n## 80.0kHz Tick Rate\ntick-hz-80_000 = []\n## 100.0kHz Tick Rate\ntick-hz-100_000 = []\n## 128.0kHz Tick Rate\ntick-hz-128_000 = []\n## 131.072kHz Tick Rate\ntick-hz-131_072 = []\n## 160.0kHz Tick Rate\ntick-hz-160_000 = []\n## 256.0kHz Tick Rate\ntick-hz-256_000 = []\n## 262.144kHz Tick Rate\ntick-hz-262_144 = []\n## 320.0kHz Tick Rate\ntick-hz-320_000 = []\n## 375.0kHz Tick Rate\ntick-hz-375_000 = []\n## 512.0kHz Tick Rate\ntick-hz-512_000 = []\n## 524.288kHz Tick Rate\ntick-hz-524_288 = []\n## 640.0kHz Tick Rate\ntick-hz-640_000 = []\n## 1.0MHz Tick Rate\ntick-hz-1_000_000 = []\n## 1.024MHz Tick Rate\ntick-hz-1_024_000 = []\n## 1.048576MHz Tick Rate\ntick-hz-1_048_576 = []\n## 1.28MHz Tick Rate\ntick-hz-1_280_000 = []\n## 2.0MHz Tick Rate\ntick-hz-2_000_000 = []\n## 2.048MHz Tick Rate\ntick-hz-2_048_000 = []\n## 2.097152MHz Tick Rate\ntick-hz-2_097_152 = []\n## 2.56MHz Tick Rate\ntick-hz-2_560_000 = []\n## 3.0MHz Tick Rate\ntick-hz-3_000_000 = []\n## 4.0MHz Tick Rate\ntick-hz-4_000_000 = []\n## 4.096MHz Tick Rate\ntick-hz-4_096_000 = []\n## 4.194304MHz Tick Rate\ntick-hz-4_194_304 = []\n## 5.12MHz Tick Rate\ntick-hz-5_120_000 = []\n## 6.0MHz Tick Rate\ntick-hz-6_000_000 = []\n## 8.0MHz Tick Rate\ntick-hz-8_000_000 = []\n## 8.192MHz Tick Rate\ntick-hz-8_192_000 = []\n## 8.388608MHz Tick Rate\ntick-hz-8_388_608 = []\n## 9.0MHz Tick Rate\ntick-hz-9_000_000 = []\n## 10.0MHz Tick Rate\ntick-hz-10_000_000 = []\n## 10.24MHz Tick Rate\ntick-hz-10_240_000 = []\n## 12.0MHz Tick Rate\ntick-hz-12_000_000 = []\n## 16.0MHz Tick Rate\ntick-hz-16_000_000 = []\n## 16.384MHz Tick Rate\ntick-hz-16_384_000 = []\n## 16.777216MHz Tick Rate\ntick-hz-16_777_216 = []\n## 18.0MHz Tick Rate\ntick-hz-18_000_000 = []\n## 20.0MHz Tick Rate\ntick-hz-20_000_000 = []\n## 20.48MHz Tick Rate\ntick-hz-20_480_000 = []\n## 24.0MHz Tick Rate\ntick-hz-24_000_000 = []\n## 30.0MHz Tick Rate\ntick-hz-30_000_000 = []\n## 32.0MHz Tick Rate\ntick-hz-32_000_000 = []\n## 32.768MHz Tick Rate\ntick-hz-32_768_000 = []\n## 36.0MHz Tick Rate\ntick-hz-36_000_000 = []\n## 40.0MHz Tick Rate\ntick-hz-40_000_000 = []\n## 40.96MHz Tick Rate\ntick-hz-40_960_000 = []\n## 48.0MHz Tick Rate\ntick-hz-48_000_000 = []\n## 50.0MHz Tick Rate\ntick-hz-50_000_000 = []\n## 60.0MHz Tick Rate\ntick-hz-60_000_000 = []\n## 64.0MHz Tick Rate\ntick-hz-64_000_000 = []\n## 65.536MHz Tick Rate\ntick-hz-65_536_000 = []\n## 70.0MHz Tick Rate\ntick-hz-70_000_000 = []\n## 72.0MHz Tick Rate\ntick-hz-72_000_000 = []\n## 80.0MHz Tick Rate\ntick-hz-80_000_000 = []\n## 81.92MHz Tick Rate\ntick-hz-81_920_000 = []\n## 90.0MHz Tick Rate\ntick-hz-90_000_000 = []\n## 96.0MHz Tick Rate\ntick-hz-96_000_000 = []\n## 100.0MHz Tick Rate\ntick-hz-100_000_000 = []\n## 110.0MHz Tick Rate\ntick-hz-110_000_000 = []\n## 120.0MHz Tick Rate\ntick-hz-120_000_000 = []\n## 128.0MHz Tick Rate\ntick-hz-128_000_000 = []\n## 130.0MHz Tick Rate\ntick-hz-130_000_000 = []\n## 131.072MHz Tick Rate\ntick-hz-131_072_000 = []\n## 133.0MHz Tick Rate\ntick-hz-133_000_000 = []\n## 140.0MHz Tick Rate\ntick-hz-140_000_000 = []\n## 144.0MHz Tick Rate\ntick-hz-144_000_000 = []\n## 150.0MHz Tick Rate\ntick-hz-150_000_000 = []\n## 160.0MHz Tick Rate\ntick-hz-160_000_000 = []\n## 163.84MHz Tick Rate\ntick-hz-163_840_000 = []\n## 170.0MHz Tick Rate\ntick-hz-170_000_000 = []\n## 180.0MHz Tick Rate\ntick-hz-180_000_000 = []\n## 190.0MHz Tick Rate\ntick-hz-190_000_000 = []\n## 192.0MHz Tick Rate\ntick-hz-192_000_000 = []\n## 200.0MHz Tick Rate\ntick-hz-200_000_000 = []\n## 210.0MHz Tick Rate\ntick-hz-210_000_000 = []\n## 220.0MHz Tick Rate\ntick-hz-220_000_000 = []\n## 230.0MHz Tick Rate\ntick-hz-230_000_000 = []\n## 240.0MHz Tick Rate\ntick-hz-240_000_000 = []\n## 250.0MHz Tick Rate\ntick-hz-250_000_000 = []\n## 256.0MHz Tick Rate\ntick-hz-256_000_000 = []\n## 260.0MHz Tick Rate\ntick-hz-260_000_000 = []\n## 262.144MHz Tick Rate\ntick-hz-262_144_000 = []\n## 270.0MHz Tick Rate\ntick-hz-270_000_000 = []\n## 280.0MHz Tick Rate\ntick-hz-280_000_000 = []\n## 288.0MHz Tick Rate\ntick-hz-288_000_000 = []\n## 290.0MHz Tick Rate\ntick-hz-290_000_000 = []\n## 300.0MHz Tick Rate\ntick-hz-300_000_000 = []\n## 320.0MHz Tick Rate\ntick-hz-320_000_000 = []\n## 327.68MHz Tick Rate\ntick-hz-327_680_000 = []\n## 340.0MHz Tick Rate\ntick-hz-340_000_000 = []\n## 360.0MHz Tick Rate\ntick-hz-360_000_000 = []\n## 380.0MHz Tick Rate\ntick-hz-380_000_000 = []\n## 384.0MHz Tick Rate\ntick-hz-384_000_000 = []\n## 400.0MHz Tick Rate\ntick-hz-400_000_000 = []\n## 420.0MHz Tick Rate\ntick-hz-420_000_000 = []\n## 440.0MHz Tick Rate\ntick-hz-440_000_000 = []\n## 460.0MHz Tick Rate\ntick-hz-460_000_000 = []\n## 480.0MHz Tick Rate\ntick-hz-480_000_000 = []\n## 500.0MHz Tick Rate\ntick-hz-500_000_000 = []\n## 512.0MHz Tick Rate\ntick-hz-512_000_000 = []\n## 520.0MHz Tick Rate\ntick-hz-520_000_000 = []\n## 524.288MHz Tick Rate\ntick-hz-524_288_000 = []\n## 540.0MHz Tick Rate\ntick-hz-540_000_000 = []\n## 560.0MHz Tick Rate\ntick-hz-560_000_000 = []\n## 576.0MHz Tick Rate\ntick-hz-576_000_000 = []\n## 580.0MHz Tick Rate\ntick-hz-580_000_000 = []\n## 600.0MHz Tick Rate\ntick-hz-600_000_000 = []\n## 620.0MHz Tick Rate\ntick-hz-620_000_000 = []\n## 640.0MHz Tick Rate\ntick-hz-640_000_000 = []\n## 655.36MHz Tick Rate\ntick-hz-655_360_000 = []\n## 660.0MHz Tick Rate\ntick-hz-660_000_000 = []\n## 680.0MHz Tick Rate\ntick-hz-680_000_000 = []\n## 700.0MHz Tick Rate\ntick-hz-700_000_000 = []\n## 720.0MHz Tick Rate\ntick-hz-720_000_000 = []\n## 740.0MHz Tick Rate\ntick-hz-740_000_000 = []\n## 760.0MHz Tick Rate\ntick-hz-760_000_000 = []\n## 768.0MHz Tick Rate\ntick-hz-768_000_000 = []\n## 780.0MHz Tick Rate\ntick-hz-780_000_000 = []\n## 800.0MHz Tick Rate\ntick-hz-800_000_000 = []\n## 820.0MHz Tick Rate\ntick-hz-820_000_000 = []\n## 840.0MHz Tick Rate\ntick-hz-840_000_000 = []\n## 860.0MHz Tick Rate\ntick-hz-860_000_000 = []\n## 880.0MHz Tick Rate\ntick-hz-880_000_000 = []\n## 900.0MHz Tick Rate\ntick-hz-900_000_000 = []\n## 920.0MHz Tick Rate\ntick-hz-920_000_000 = []\n## 940.0MHz Tick Rate\ntick-hz-940_000_000 = []\n## 960.0MHz Tick Rate\ntick-hz-960_000_000 = []\n## 980.0MHz Tick Rate\ntick-hz-980_000_000 = []\n## 1.0GHz Tick Rate\ntick-hz-1_000_000_000 = []\n## 1.31072GHz Tick Rate\ntick-hz-1_310_720_000 = []\n## 2.62144GHz Tick Rate\ntick-hz-2_621_440_000 = []\n## 5.24288GHz Tick Rate\ntick-hz-5_242_880_000 = []\n# END TICKS\n\n#! </details>\n\n[dependencies]\ndocument-features = \"0.2.7\"\n\n[dev-dependencies]\ncritical-section = \"1\"\nembassy-time-queue-utils = { path = \"../embassy-time-queue-utils\" }\n"
  },
  {
    "path": "embassy-time-driver/README.md",
    "content": "# embassy-time-driver\n\nThis crate contains the driver trait necessary for adding [`embassy-time`](https://crates.io/crates/embassy-time) support\nfor a new hardware platform.\n\nIf you want to *use* `embassy-time` with already made drivers, you should depend on the main `embassy-time` crate, not on this crate.\n\nIf you are writing a driver, you  should depend only on this crate, not on the main `embassy-time` crate.\nThis will allow your driver to continue working for newer `embassy-time` major versions, without needing an update,\nif the driver trait has not had breaking changes.\n\n## How it works\n\n`embassy-time` is backed by a global \"time driver\" specified at build time.\nOnly one driver can be active in a program.\n\nAll methods and structs transparently call into the active driver. This makes it\npossible for libraries to use `embassy-time` in a driver-agnostic way without\nrequiring generic parameters.\n"
  },
  {
    "path": "embassy-time-driver/build.rs",
    "content": "fn main() {}\n"
  },
  {
    "path": "embassy-time-driver/gen_tick.py",
    "content": "import os\n\nabspath = os.path.abspath(__file__)\ndname = os.path.dirname(abspath)\nos.chdir(dname)\n\nticks = []\nfor i in range(10):\n    ticks.append(10**i)\nfor i in range(1, 25):\n    ticks.append(2**i)\nfor i in range(1, 20):\n    ticks.append(2**i * 1000)\nfor i in range(1, 20):\n    ticks.append(2**i * 10000)\nfor i in range(1, 10):\n    ticks.append(2**i * 1000000)\n    ticks.append(2**i * 9 // 8 * 1000000)\n    ticks.append(2**i * 3 // 2 * 1000000)\nfor i in range(1, 30):\n    ticks.append(10 * i * 1_000_000)\nfor i in range(15, 50):\n    ticks.append(20 * i * 1_000_000)\n\nticks.append(375 * 1000)\nticks.append(133 * 1_000_000)\n\nseen = set()\nticks = sorted([x for x in ticks if not (x in seen or seen.add(x))])\n\n# ========= Update Cargo.toml\n\nSEPARATOR_START = '# BEGIN TICKS\\n'\nSEPARATOR_END = '# END TICKS\\n'\nHELP = '# Generated by gen_tick.py. DO NOT EDIT.\\n'\n\nfeats_time = ''\nfeats_driver = ''\nfor freq in ticks:\n    feature = f'tick-hz-{freq:_}'\n    if freq >= 1_000_000_000:\n        freq_human = f\"{freq / 1_000_000_000}GHz\"\n    elif freq >= 1_000_000:\n        freq_human = f\"{freq / 1_000_000}MHz\"\n    elif freq >= 1_000:\n        freq_human = f\"{freq / 1000}kHz\"\n    else:\n        freq_human = f\"{freq}Hz\"\n\n    feats_time += f\"## {freq_human} Tick Rate\\n\"\n    feats_time += f\"{feature} = [\\\"embassy-time-driver/{feature}\\\"]\\n\"\n    feats_driver += f\"## {freq_human} Tick Rate\\n\"\n    feats_driver += f\"{feature} = []\\n\"\n\nwith open('Cargo.toml', 'r') as f:\n    data = f.read()\nbefore, data = data.split(SEPARATOR_START, maxsplit=1)\n_, after = data.split(SEPARATOR_END, maxsplit=1)\nwith open('Cargo.toml', 'w') as f:\n    f.write(before + SEPARATOR_START + HELP + feats_driver + SEPARATOR_END + after)\n\nwith open('../embassy-time/Cargo.toml', 'r') as f:\n    data = f.read()\nbefore, data = data.split(SEPARATOR_START, maxsplit=1)\n_, after = data.split(SEPARATOR_END, maxsplit=1)\nwith open('../embassy-time/Cargo.toml', 'w') as f:\n    f.write(before + SEPARATOR_START + HELP + feats_time + SEPARATOR_END + after)\n\n# ========= Update src/tick.rs\n\nwith open('src/tick.rs', 'w') as f:\n\n    f.write('// Generated by gen_tick.py. DO NOT EDIT.\\n\\n')\n    for hz in ticks:\n        f.write(\n            f'#[cfg(feature = \"tick-hz-{hz:_}\")] pub const TICK_HZ: u64 = {hz:_};\\n')\n    f.write('#[cfg(not(any(\\n')\n    for hz in ticks:\n        f.write(f'feature = \"tick-hz-{hz:_}\",\\n')\n    f.write(')))] pub const TICK_HZ: u64 = 1_000_000;')\n\n\nos.system('rustfmt src/tick.rs')\n"
  },
  {
    "path": "embassy-time-driver/src/lib.rs",
    "content": "#![no_std]\n#![doc = include_str!(\"../README.md\")]\n#![warn(missing_docs)]\n\n//! ## Implementing a driver\n//!\n//! - Define a struct `MyDriver`\n//! - Implement [`Driver`] for it\n//! - Register it as the global driver with [`time_driver_impl`].\n//!\n//! If your driver has a single set tick rate, enable the corresponding [`tick-hz-*`](crate#tick-rate) feature,\n//! which will prevent users from needing to configure it themselves (or selecting an incorrect configuration).\n//!\n//! If your driver supports a small number of set tick rates, expose your own cargo features and have each one\n//! enable the corresponding `embassy-time-driver/tick-*`.\n//!\n//! Otherwise, don’t enable any `tick-hz-*` feature to let the user configure the tick rate themselves by\n//! enabling a feature on `embassy-time`.\n//!\n//! ### Example\n//!\n//! ```\n//! use core::task::Waker;\n//!\n//! use embassy_time_driver::Driver;\n//!\n//! struct MyDriver{} // not public!\n//!\n//! impl Driver for MyDriver {\n//!     fn now(&self) -> u64 {\n//!         todo!()\n//!     }\n//!\n//!     fn schedule_wake(&self, at: u64, waker: &Waker) {\n//!         todo!()\n//!     }\n//! }\n//!\n//! embassy_time_driver::time_driver_impl!(static DRIVER: MyDriver = MyDriver{});\n//! ```\n//!\n//! ## Implementing the timer queue\n//!\n//! The simplest (but suboptimal) way to implement a timer queue is to define a single queue in the\n//! time driver. Declare a field protected by an appropriate mutex (e.g. `critical_section::Mutex`).\n//!\n//! Then, you'll need to adapt the `schedule_wake` method to use this queue.\n//!\n//! Note that if you are using multiple queues, you will need to ensure that a single timer\n//! queue item is only ever enqueued into a single queue at a time.\n//!\n//! ```\n//! use core::cell::RefCell;\n//! use core::task::Waker;\n//!\n//! use critical_section::{CriticalSection, Mutex};\n//! use embassy_time_queue_utils::Queue;\n//! use embassy_time_driver::Driver;\n//!\n//! struct MyDriver {\n//!     queue: Mutex<RefCell<Queue>>,\n//! }\n//!\n//! impl MyDriver {\n//!    fn set_alarm(&self, cs: &CriticalSection, at: u64) -> bool {\n//!        todo!()\n//!    }\n//! }\n//!\n//! impl Driver for MyDriver {\n//!     fn now(&self) -> u64 { todo!() }\n//!\n//!     fn schedule_wake(&self, at: u64, waker: &Waker) {\n//!         critical_section::with(|cs| {\n//!             let mut queue = self.queue.borrow(cs).borrow_mut();\n//!             if queue.schedule_wake(at, waker) {\n//!                 let mut next = queue.next_expiration(self.now());\n//!                 while !self.set_alarm(&cs, next) {\n//!                     next = queue.next_expiration(self.now());\n//!                 }\n//!             }\n//!         });\n//!     }\n//! }\n//! ```\n//!\n//! # Linkage details\n//!\n//! Instead of the usual \"trait + generic params\" approach, calls from embassy to the driver are done via `extern` functions.\n//!\n//! `embassy` internally defines the driver function as `extern \"Rust\" { fn _embassy_time_now() -> u64; }` and calls it.\n//! The driver crate defines the function as `#[unsafe(no_mangle)] fn _embassy_time_now() -> u64`. The linker will resolve the\n//! calls from the `embassy` crate to call into the driver crate.\n//!\n//! If there is none or multiple drivers in the crate tree, linking will fail.\n//!\n//! This method has a few key advantages for something as foundational as timekeeping:\n//!\n//! - The time driver is available everywhere easily, without having to thread the implementation\n//!   through generic parameters. This is especially helpful for libraries.\n//! - It means comparing `Instant`s will always make sense: if there were multiple drivers\n//!   active, one could compare an `Instant` from driver A to an `Instant` from driver B, which\n//!   would yield incorrect results.\n\n//! ## Feature flags\n#![doc = document_features::document_features!(feature_label = r#\"<span class=\"stab portability\"><code>{feature}</code></span>\"#)]\n\nuse core::task::Waker;\n\nmod tick;\n\n/// Ticks per second of the global timebase.\n///\n/// This value is specified by the [`tick-*` Cargo features](crate#tick-rate)\npub const TICK_HZ: u64 = tick::TICK_HZ;\n\n/// Time driver\npub trait Driver: Send + Sync + 'static {\n    /// Return the current timestamp in ticks.\n    ///\n    /// Implementations MUST ensure that:\n    /// - This is guaranteed to be monotonic, i.e. a call to now() will always return\n    ///   a greater or equal value than earlier calls. Time can't \"roll backwards\".\n    /// - It \"never\" overflows. It must not overflow in a sufficiently long time frame, say\n    ///   in 10_000 years (Human civilization is likely to already have self-destructed\n    ///   10_000 years from now.). This means if your hardware only has 16bit/32bit timers\n    ///   you MUST extend them to 64-bit, for example by counting overflows in software,\n    ///   or chaining multiple timers together.\n    /// - It never fails, including any kind of access fault, even if the underlying\n    ///   hardware has not yet been initialized. In these cases, it may be necessary to\n    ///   check if the hardware is initialized (using an atomic boolean, or similar),\n    ///   and if not, return a default value, such as zero (while still respecting\n    ///   the other requirements above).\n    fn now(&self) -> u64;\n\n    /// Schedules a waker to be awoken at moment `at`.\n    /// If this moment is in the past, the waker might be awoken immediately.\n    fn schedule_wake(&self, at: u64, waker: &Waker);\n}\n\nunsafe extern \"Rust\" {\n    fn _embassy_time_now() -> u64;\n    fn _embassy_time_schedule_wake(at: u64, waker: &Waker);\n}\n\n/// See [`Driver::now`]\n#[inline]\npub fn now() -> u64 {\n    unsafe { _embassy_time_now() }\n}\n\n/// Schedule the given waker to be woken at `at`.\n#[inline]\npub fn schedule_wake(at: u64, waker: &Waker) {\n    unsafe { _embassy_time_schedule_wake(at, waker) }\n}\n\n/// Set the time Driver implementation.\n///\n/// See the module documentation for an example.\n#[macro_export]\nmacro_rules! time_driver_impl {\n    (static $name:ident: $t: ty = $val:expr) => {\n        static $name: $t = $val;\n\n        #[unsafe(no_mangle)]\n        #[inline]\n        fn _embassy_time_now() -> u64 {\n            <$t as $crate::Driver>::now(&$name)\n        }\n\n        #[unsafe(no_mangle)]\n        #[inline]\n        fn _embassy_time_schedule_wake(at: u64, waker: &core::task::Waker) {\n            <$t as $crate::Driver>::schedule_wake(&$name, at, waker);\n        }\n    };\n}\n"
  },
  {
    "path": "embassy-time-driver/src/tick.rs",
    "content": "// Generated by gen_tick.py. DO NOT EDIT.\n\n#[cfg(feature = \"tick-hz-1\")]\npub const TICK_HZ: u64 = 1;\n#[cfg(feature = \"tick-hz-2\")]\npub const TICK_HZ: u64 = 2;\n#[cfg(feature = \"tick-hz-4\")]\npub const TICK_HZ: u64 = 4;\n#[cfg(feature = \"tick-hz-8\")]\npub const TICK_HZ: u64 = 8;\n#[cfg(feature = \"tick-hz-10\")]\npub const TICK_HZ: u64 = 10;\n#[cfg(feature = \"tick-hz-16\")]\npub const TICK_HZ: u64 = 16;\n#[cfg(feature = \"tick-hz-32\")]\npub const TICK_HZ: u64 = 32;\n#[cfg(feature = \"tick-hz-64\")]\npub const TICK_HZ: u64 = 64;\n#[cfg(feature = \"tick-hz-100\")]\npub const TICK_HZ: u64 = 100;\n#[cfg(feature = \"tick-hz-128\")]\npub const TICK_HZ: u64 = 128;\n#[cfg(feature = \"tick-hz-256\")]\npub const TICK_HZ: u64 = 256;\n#[cfg(feature = \"tick-hz-512\")]\npub const TICK_HZ: u64 = 512;\n#[cfg(feature = \"tick-hz-1_000\")]\npub const TICK_HZ: u64 = 1_000;\n#[cfg(feature = \"tick-hz-1_024\")]\npub const TICK_HZ: u64 = 1_024;\n#[cfg(feature = \"tick-hz-2_000\")]\npub const TICK_HZ: u64 = 2_000;\n#[cfg(feature = \"tick-hz-2_048\")]\npub const TICK_HZ: u64 = 2_048;\n#[cfg(feature = \"tick-hz-4_000\")]\npub const TICK_HZ: u64 = 4_000;\n#[cfg(feature = \"tick-hz-4_096\")]\npub const TICK_HZ: u64 = 4_096;\n#[cfg(feature = \"tick-hz-8_000\")]\npub const TICK_HZ: u64 = 8_000;\n#[cfg(feature = \"tick-hz-8_192\")]\npub const TICK_HZ: u64 = 8_192;\n#[cfg(feature = \"tick-hz-10_000\")]\npub const TICK_HZ: u64 = 10_000;\n#[cfg(feature = \"tick-hz-16_000\")]\npub const TICK_HZ: u64 = 16_000;\n#[cfg(feature = \"tick-hz-16_384\")]\npub const TICK_HZ: u64 = 16_384;\n#[cfg(feature = \"tick-hz-20_000\")]\npub const TICK_HZ: u64 = 20_000;\n#[cfg(feature = \"tick-hz-32_000\")]\npub const TICK_HZ: u64 = 32_000;\n#[cfg(feature = \"tick-hz-32_768\")]\npub const TICK_HZ: u64 = 32_768;\n#[cfg(feature = \"tick-hz-40_000\")]\npub const TICK_HZ: u64 = 40_000;\n#[cfg(feature = \"tick-hz-64_000\")]\npub const TICK_HZ: u64 = 64_000;\n#[cfg(feature = \"tick-hz-65_536\")]\npub const TICK_HZ: u64 = 65_536;\n#[cfg(feature = \"tick-hz-80_000\")]\npub const TICK_HZ: u64 = 80_000;\n#[cfg(feature = \"tick-hz-100_000\")]\npub const TICK_HZ: u64 = 100_000;\n#[cfg(feature = \"tick-hz-128_000\")]\npub const TICK_HZ: u64 = 128_000;\n#[cfg(feature = \"tick-hz-131_072\")]\npub const TICK_HZ: u64 = 131_072;\n#[cfg(feature = \"tick-hz-160_000\")]\npub const TICK_HZ: u64 = 160_000;\n#[cfg(feature = \"tick-hz-256_000\")]\npub const TICK_HZ: u64 = 256_000;\n#[cfg(feature = \"tick-hz-262_144\")]\npub const TICK_HZ: u64 = 262_144;\n#[cfg(feature = \"tick-hz-320_000\")]\npub const TICK_HZ: u64 = 320_000;\n#[cfg(feature = \"tick-hz-375_000\")]\npub const TICK_HZ: u64 = 375_000;\n#[cfg(feature = \"tick-hz-512_000\")]\npub const TICK_HZ: u64 = 512_000;\n#[cfg(feature = \"tick-hz-524_288\")]\npub const TICK_HZ: u64 = 524_288;\n#[cfg(feature = \"tick-hz-640_000\")]\npub const TICK_HZ: u64 = 640_000;\n#[cfg(feature = \"tick-hz-1_000_000\")]\npub const TICK_HZ: u64 = 1_000_000;\n#[cfg(feature = \"tick-hz-1_024_000\")]\npub const TICK_HZ: u64 = 1_024_000;\n#[cfg(feature = \"tick-hz-1_048_576\")]\npub const TICK_HZ: u64 = 1_048_576;\n#[cfg(feature = \"tick-hz-1_280_000\")]\npub const TICK_HZ: u64 = 1_280_000;\n#[cfg(feature = \"tick-hz-2_000_000\")]\npub const TICK_HZ: u64 = 2_000_000;\n#[cfg(feature = \"tick-hz-2_048_000\")]\npub const TICK_HZ: u64 = 2_048_000;\n#[cfg(feature = \"tick-hz-2_097_152\")]\npub const TICK_HZ: u64 = 2_097_152;\n#[cfg(feature = \"tick-hz-2_560_000\")]\npub const TICK_HZ: u64 = 2_560_000;\n#[cfg(feature = \"tick-hz-3_000_000\")]\npub const TICK_HZ: u64 = 3_000_000;\n#[cfg(feature = \"tick-hz-4_000_000\")]\npub const TICK_HZ: u64 = 4_000_000;\n#[cfg(feature = \"tick-hz-4_096_000\")]\npub const TICK_HZ: u64 = 4_096_000;\n#[cfg(feature = \"tick-hz-4_194_304\")]\npub const TICK_HZ: u64 = 4_194_304;\n#[cfg(feature = \"tick-hz-5_120_000\")]\npub const TICK_HZ: u64 = 5_120_000;\n#[cfg(feature = \"tick-hz-6_000_000\")]\npub const TICK_HZ: u64 = 6_000_000;\n#[cfg(feature = \"tick-hz-8_000_000\")]\npub const TICK_HZ: u64 = 8_000_000;\n#[cfg(feature = \"tick-hz-8_192_000\")]\npub const TICK_HZ: u64 = 8_192_000;\n#[cfg(feature = \"tick-hz-8_388_608\")]\npub const TICK_HZ: u64 = 8_388_608;\n#[cfg(feature = \"tick-hz-9_000_000\")]\npub const TICK_HZ: u64 = 9_000_000;\n#[cfg(feature = \"tick-hz-10_000_000\")]\npub const TICK_HZ: u64 = 10_000_000;\n#[cfg(feature = \"tick-hz-10_240_000\")]\npub const TICK_HZ: u64 = 10_240_000;\n#[cfg(feature = \"tick-hz-12_000_000\")]\npub const TICK_HZ: u64 = 12_000_000;\n#[cfg(feature = \"tick-hz-16_000_000\")]\npub const TICK_HZ: u64 = 16_000_000;\n#[cfg(feature = \"tick-hz-16_384_000\")]\npub const TICK_HZ: u64 = 16_384_000;\n#[cfg(feature = \"tick-hz-16_777_216\")]\npub const TICK_HZ: u64 = 16_777_216;\n#[cfg(feature = \"tick-hz-18_000_000\")]\npub const TICK_HZ: u64 = 18_000_000;\n#[cfg(feature = \"tick-hz-20_000_000\")]\npub const TICK_HZ: u64 = 20_000_000;\n#[cfg(feature = \"tick-hz-20_480_000\")]\npub const TICK_HZ: u64 = 20_480_000;\n#[cfg(feature = \"tick-hz-24_000_000\")]\npub const TICK_HZ: u64 = 24_000_000;\n#[cfg(feature = \"tick-hz-30_000_000\")]\npub const TICK_HZ: u64 = 30_000_000;\n#[cfg(feature = \"tick-hz-32_000_000\")]\npub const TICK_HZ: u64 = 32_000_000;\n#[cfg(feature = \"tick-hz-32_768_000\")]\npub const TICK_HZ: u64 = 32_768_000;\n#[cfg(feature = \"tick-hz-36_000_000\")]\npub const TICK_HZ: u64 = 36_000_000;\n#[cfg(feature = \"tick-hz-40_000_000\")]\npub const TICK_HZ: u64 = 40_000_000;\n#[cfg(feature = \"tick-hz-40_960_000\")]\npub const TICK_HZ: u64 = 40_960_000;\n#[cfg(feature = \"tick-hz-48_000_000\")]\npub const TICK_HZ: u64 = 48_000_000;\n#[cfg(feature = \"tick-hz-50_000_000\")]\npub const TICK_HZ: u64 = 50_000_000;\n#[cfg(feature = \"tick-hz-60_000_000\")]\npub const TICK_HZ: u64 = 60_000_000;\n#[cfg(feature = \"tick-hz-64_000_000\")]\npub const TICK_HZ: u64 = 64_000_000;\n#[cfg(feature = \"tick-hz-65_536_000\")]\npub const TICK_HZ: u64 = 65_536_000;\n#[cfg(feature = \"tick-hz-70_000_000\")]\npub const TICK_HZ: u64 = 70_000_000;\n#[cfg(feature = \"tick-hz-72_000_000\")]\npub const TICK_HZ: u64 = 72_000_000;\n#[cfg(feature = \"tick-hz-80_000_000\")]\npub const TICK_HZ: u64 = 80_000_000;\n#[cfg(feature = \"tick-hz-81_920_000\")]\npub const TICK_HZ: u64 = 81_920_000;\n#[cfg(feature = \"tick-hz-90_000_000\")]\npub const TICK_HZ: u64 = 90_000_000;\n#[cfg(feature = \"tick-hz-96_000_000\")]\npub const TICK_HZ: u64 = 96_000_000;\n#[cfg(feature = \"tick-hz-100_000_000\")]\npub const TICK_HZ: u64 = 100_000_000;\n#[cfg(feature = \"tick-hz-110_000_000\")]\npub const TICK_HZ: u64 = 110_000_000;\n#[cfg(feature = \"tick-hz-120_000_000\")]\npub const TICK_HZ: u64 = 120_000_000;\n#[cfg(feature = \"tick-hz-128_000_000\")]\npub const TICK_HZ: u64 = 128_000_000;\n#[cfg(feature = \"tick-hz-130_000_000\")]\npub const TICK_HZ: u64 = 130_000_000;\n#[cfg(feature = \"tick-hz-131_072_000\")]\npub const TICK_HZ: u64 = 131_072_000;\n#[cfg(feature = \"tick-hz-133_000_000\")]\npub const TICK_HZ: u64 = 133_000_000;\n#[cfg(feature = \"tick-hz-140_000_000\")]\npub const TICK_HZ: u64 = 140_000_000;\n#[cfg(feature = \"tick-hz-144_000_000\")]\npub const TICK_HZ: u64 = 144_000_000;\n#[cfg(feature = \"tick-hz-150_000_000\")]\npub const TICK_HZ: u64 = 150_000_000;\n#[cfg(feature = \"tick-hz-160_000_000\")]\npub const TICK_HZ: u64 = 160_000_000;\n#[cfg(feature = \"tick-hz-163_840_000\")]\npub const TICK_HZ: u64 = 163_840_000;\n#[cfg(feature = \"tick-hz-170_000_000\")]\npub const TICK_HZ: u64 = 170_000_000;\n#[cfg(feature = \"tick-hz-180_000_000\")]\npub const TICK_HZ: u64 = 180_000_000;\n#[cfg(feature = \"tick-hz-190_000_000\")]\npub const TICK_HZ: u64 = 190_000_000;\n#[cfg(feature = \"tick-hz-192_000_000\")]\npub const TICK_HZ: u64 = 192_000_000;\n#[cfg(feature = \"tick-hz-200_000_000\")]\npub const TICK_HZ: u64 = 200_000_000;\n#[cfg(feature = \"tick-hz-210_000_000\")]\npub const TICK_HZ: u64 = 210_000_000;\n#[cfg(feature = \"tick-hz-220_000_000\")]\npub const TICK_HZ: u64 = 220_000_000;\n#[cfg(feature = \"tick-hz-230_000_000\")]\npub const TICK_HZ: u64 = 230_000_000;\n#[cfg(feature = \"tick-hz-240_000_000\")]\npub const TICK_HZ: u64 = 240_000_000;\n#[cfg(feature = \"tick-hz-250_000_000\")]\npub const TICK_HZ: u64 = 250_000_000;\n#[cfg(feature = \"tick-hz-256_000_000\")]\npub const TICK_HZ: u64 = 256_000_000;\n#[cfg(feature = \"tick-hz-260_000_000\")]\npub const TICK_HZ: u64 = 260_000_000;\n#[cfg(feature = \"tick-hz-262_144_000\")]\npub const TICK_HZ: u64 = 262_144_000;\n#[cfg(feature = \"tick-hz-270_000_000\")]\npub const TICK_HZ: u64 = 270_000_000;\n#[cfg(feature = \"tick-hz-280_000_000\")]\npub const TICK_HZ: u64 = 280_000_000;\n#[cfg(feature = \"tick-hz-288_000_000\")]\npub const TICK_HZ: u64 = 288_000_000;\n#[cfg(feature = \"tick-hz-290_000_000\")]\npub const TICK_HZ: u64 = 290_000_000;\n#[cfg(feature = \"tick-hz-300_000_000\")]\npub const TICK_HZ: u64 = 300_000_000;\n#[cfg(feature = \"tick-hz-320_000_000\")]\npub const TICK_HZ: u64 = 320_000_000;\n#[cfg(feature = \"tick-hz-327_680_000\")]\npub const TICK_HZ: u64 = 327_680_000;\n#[cfg(feature = \"tick-hz-340_000_000\")]\npub const TICK_HZ: u64 = 340_000_000;\n#[cfg(feature = \"tick-hz-360_000_000\")]\npub const TICK_HZ: u64 = 360_000_000;\n#[cfg(feature = \"tick-hz-380_000_000\")]\npub const TICK_HZ: u64 = 380_000_000;\n#[cfg(feature = \"tick-hz-384_000_000\")]\npub const TICK_HZ: u64 = 384_000_000;\n#[cfg(feature = \"tick-hz-400_000_000\")]\npub const TICK_HZ: u64 = 400_000_000;\n#[cfg(feature = \"tick-hz-420_000_000\")]\npub const TICK_HZ: u64 = 420_000_000;\n#[cfg(feature = \"tick-hz-440_000_000\")]\npub const TICK_HZ: u64 = 440_000_000;\n#[cfg(feature = \"tick-hz-460_000_000\")]\npub const TICK_HZ: u64 = 460_000_000;\n#[cfg(feature = \"tick-hz-480_000_000\")]\npub const TICK_HZ: u64 = 480_000_000;\n#[cfg(feature = \"tick-hz-500_000_000\")]\npub const TICK_HZ: u64 = 500_000_000;\n#[cfg(feature = \"tick-hz-512_000_000\")]\npub const TICK_HZ: u64 = 512_000_000;\n#[cfg(feature = \"tick-hz-520_000_000\")]\npub const TICK_HZ: u64 = 520_000_000;\n#[cfg(feature = \"tick-hz-524_288_000\")]\npub const TICK_HZ: u64 = 524_288_000;\n#[cfg(feature = \"tick-hz-540_000_000\")]\npub const TICK_HZ: u64 = 540_000_000;\n#[cfg(feature = \"tick-hz-560_000_000\")]\npub const TICK_HZ: u64 = 560_000_000;\n#[cfg(feature = \"tick-hz-576_000_000\")]\npub const TICK_HZ: u64 = 576_000_000;\n#[cfg(feature = \"tick-hz-580_000_000\")]\npub const TICK_HZ: u64 = 580_000_000;\n#[cfg(feature = \"tick-hz-600_000_000\")]\npub const TICK_HZ: u64 = 600_000_000;\n#[cfg(feature = \"tick-hz-620_000_000\")]\npub const TICK_HZ: u64 = 620_000_000;\n#[cfg(feature = \"tick-hz-640_000_000\")]\npub const TICK_HZ: u64 = 640_000_000;\n#[cfg(feature = \"tick-hz-655_360_000\")]\npub const TICK_HZ: u64 = 655_360_000;\n#[cfg(feature = \"tick-hz-660_000_000\")]\npub const TICK_HZ: u64 = 660_000_000;\n#[cfg(feature = \"tick-hz-680_000_000\")]\npub const TICK_HZ: u64 = 680_000_000;\n#[cfg(feature = \"tick-hz-700_000_000\")]\npub const TICK_HZ: u64 = 700_000_000;\n#[cfg(feature = \"tick-hz-720_000_000\")]\npub const TICK_HZ: u64 = 720_000_000;\n#[cfg(feature = \"tick-hz-740_000_000\")]\npub const TICK_HZ: u64 = 740_000_000;\n#[cfg(feature = \"tick-hz-760_000_000\")]\npub const TICK_HZ: u64 = 760_000_000;\n#[cfg(feature = \"tick-hz-768_000_000\")]\npub const TICK_HZ: u64 = 768_000_000;\n#[cfg(feature = \"tick-hz-780_000_000\")]\npub const TICK_HZ: u64 = 780_000_000;\n#[cfg(feature = \"tick-hz-800_000_000\")]\npub const TICK_HZ: u64 = 800_000_000;\n#[cfg(feature = \"tick-hz-820_000_000\")]\npub const TICK_HZ: u64 = 820_000_000;\n#[cfg(feature = \"tick-hz-840_000_000\")]\npub const TICK_HZ: u64 = 840_000_000;\n#[cfg(feature = \"tick-hz-860_000_000\")]\npub const TICK_HZ: u64 = 860_000_000;\n#[cfg(feature = \"tick-hz-880_000_000\")]\npub const TICK_HZ: u64 = 880_000_000;\n#[cfg(feature = \"tick-hz-900_000_000\")]\npub const TICK_HZ: u64 = 900_000_000;\n#[cfg(feature = \"tick-hz-920_000_000\")]\npub const TICK_HZ: u64 = 920_000_000;\n#[cfg(feature = \"tick-hz-940_000_000\")]\npub const TICK_HZ: u64 = 940_000_000;\n#[cfg(feature = \"tick-hz-960_000_000\")]\npub const TICK_HZ: u64 = 960_000_000;\n#[cfg(feature = \"tick-hz-980_000_000\")]\npub const TICK_HZ: u64 = 980_000_000;\n#[cfg(feature = \"tick-hz-1_000_000_000\")]\npub const TICK_HZ: u64 = 1_000_000_000;\n#[cfg(feature = \"tick-hz-1_310_720_000\")]\npub const TICK_HZ: u64 = 1_310_720_000;\n#[cfg(feature = \"tick-hz-2_621_440_000\")]\npub const TICK_HZ: u64 = 2_621_440_000;\n#[cfg(feature = \"tick-hz-5_242_880_000\")]\npub const TICK_HZ: u64 = 5_242_880_000;\n#[cfg(not(any(\n    feature = \"tick-hz-1\",\n    feature = \"tick-hz-2\",\n    feature = \"tick-hz-4\",\n    feature = \"tick-hz-8\",\n    feature = \"tick-hz-10\",\n    feature = \"tick-hz-16\",\n    feature = \"tick-hz-32\",\n    feature = \"tick-hz-64\",\n    feature = \"tick-hz-100\",\n    feature = \"tick-hz-128\",\n    feature = \"tick-hz-256\",\n    feature = \"tick-hz-512\",\n    feature = \"tick-hz-1_000\",\n    feature = \"tick-hz-1_024\",\n    feature = \"tick-hz-2_000\",\n    feature = \"tick-hz-2_048\",\n    feature = \"tick-hz-4_000\",\n    feature = \"tick-hz-4_096\",\n    feature = \"tick-hz-8_000\",\n    feature = \"tick-hz-8_192\",\n    feature = \"tick-hz-10_000\",\n    feature = \"tick-hz-16_000\",\n    feature = \"tick-hz-16_384\",\n    feature = \"tick-hz-20_000\",\n    feature = \"tick-hz-32_000\",\n    feature = \"tick-hz-32_768\",\n    feature = \"tick-hz-40_000\",\n    feature = \"tick-hz-64_000\",\n    feature = \"tick-hz-65_536\",\n    feature = \"tick-hz-80_000\",\n    feature = \"tick-hz-100_000\",\n    feature = \"tick-hz-128_000\",\n    feature = \"tick-hz-131_072\",\n    feature = \"tick-hz-160_000\",\n    feature = \"tick-hz-256_000\",\n    feature = \"tick-hz-262_144\",\n    feature = \"tick-hz-320_000\",\n    feature = \"tick-hz-375_000\",\n    feature = \"tick-hz-512_000\",\n    feature = \"tick-hz-524_288\",\n    feature = \"tick-hz-640_000\",\n    feature = \"tick-hz-1_000_000\",\n    feature = \"tick-hz-1_024_000\",\n    feature = \"tick-hz-1_048_576\",\n    feature = \"tick-hz-1_280_000\",\n    feature = \"tick-hz-2_000_000\",\n    feature = \"tick-hz-2_048_000\",\n    feature = \"tick-hz-2_097_152\",\n    feature = \"tick-hz-2_560_000\",\n    feature = \"tick-hz-3_000_000\",\n    feature = \"tick-hz-4_000_000\",\n    feature = \"tick-hz-4_096_000\",\n    feature = \"tick-hz-4_194_304\",\n    feature = \"tick-hz-5_120_000\",\n    feature = \"tick-hz-6_000_000\",\n    feature = \"tick-hz-8_000_000\",\n    feature = \"tick-hz-8_192_000\",\n    feature = \"tick-hz-8_388_608\",\n    feature = \"tick-hz-9_000_000\",\n    feature = \"tick-hz-10_000_000\",\n    feature = \"tick-hz-10_240_000\",\n    feature = \"tick-hz-12_000_000\",\n    feature = \"tick-hz-16_000_000\",\n    feature = \"tick-hz-16_384_000\",\n    feature = \"tick-hz-16_777_216\",\n    feature = \"tick-hz-18_000_000\",\n    feature = \"tick-hz-20_000_000\",\n    feature = \"tick-hz-20_480_000\",\n    feature = \"tick-hz-24_000_000\",\n    feature = \"tick-hz-30_000_000\",\n    feature = \"tick-hz-32_000_000\",\n    feature = \"tick-hz-32_768_000\",\n    feature = \"tick-hz-36_000_000\",\n    feature = \"tick-hz-40_000_000\",\n    feature = \"tick-hz-40_960_000\",\n    feature = \"tick-hz-48_000_000\",\n    feature = \"tick-hz-50_000_000\",\n    feature = \"tick-hz-60_000_000\",\n    feature = \"tick-hz-64_000_000\",\n    feature = \"tick-hz-65_536_000\",\n    feature = \"tick-hz-70_000_000\",\n    feature = \"tick-hz-72_000_000\",\n    feature = \"tick-hz-80_000_000\",\n    feature = \"tick-hz-81_920_000\",\n    feature = \"tick-hz-90_000_000\",\n    feature = \"tick-hz-96_000_000\",\n    feature = \"tick-hz-100_000_000\",\n    feature = \"tick-hz-110_000_000\",\n    feature = \"tick-hz-120_000_000\",\n    feature = \"tick-hz-128_000_000\",\n    feature = \"tick-hz-130_000_000\",\n    feature = \"tick-hz-131_072_000\",\n    feature = \"tick-hz-133_000_000\",\n    feature = \"tick-hz-140_000_000\",\n    feature = \"tick-hz-144_000_000\",\n    feature = \"tick-hz-150_000_000\",\n    feature = \"tick-hz-160_000_000\",\n    feature = \"tick-hz-163_840_000\",\n    feature = \"tick-hz-170_000_000\",\n    feature = \"tick-hz-180_000_000\",\n    feature = \"tick-hz-190_000_000\",\n    feature = \"tick-hz-192_000_000\",\n    feature = \"tick-hz-200_000_000\",\n    feature = \"tick-hz-210_000_000\",\n    feature = \"tick-hz-220_000_000\",\n    feature = \"tick-hz-230_000_000\",\n    feature = \"tick-hz-240_000_000\",\n    feature = \"tick-hz-250_000_000\",\n    feature = \"tick-hz-256_000_000\",\n    feature = \"tick-hz-260_000_000\",\n    feature = \"tick-hz-262_144_000\",\n    feature = \"tick-hz-270_000_000\",\n    feature = \"tick-hz-280_000_000\",\n    feature = \"tick-hz-288_000_000\",\n    feature = \"tick-hz-290_000_000\",\n    feature = \"tick-hz-300_000_000\",\n    feature = \"tick-hz-320_000_000\",\n    feature = \"tick-hz-327_680_000\",\n    feature = \"tick-hz-340_000_000\",\n    feature = \"tick-hz-360_000_000\",\n    feature = \"tick-hz-380_000_000\",\n    feature = \"tick-hz-384_000_000\",\n    feature = \"tick-hz-400_000_000\",\n    feature = \"tick-hz-420_000_000\",\n    feature = \"tick-hz-440_000_000\",\n    feature = \"tick-hz-460_000_000\",\n    feature = \"tick-hz-480_000_000\",\n    feature = \"tick-hz-500_000_000\",\n    feature = \"tick-hz-512_000_000\",\n    feature = \"tick-hz-520_000_000\",\n    feature = \"tick-hz-524_288_000\",\n    feature = \"tick-hz-540_000_000\",\n    feature = \"tick-hz-560_000_000\",\n    feature = \"tick-hz-576_000_000\",\n    feature = \"tick-hz-580_000_000\",\n    feature = \"tick-hz-600_000_000\",\n    feature = \"tick-hz-620_000_000\",\n    feature = \"tick-hz-640_000_000\",\n    feature = \"tick-hz-655_360_000\",\n    feature = \"tick-hz-660_000_000\",\n    feature = \"tick-hz-680_000_000\",\n    feature = \"tick-hz-700_000_000\",\n    feature = \"tick-hz-720_000_000\",\n    feature = \"tick-hz-740_000_000\",\n    feature = \"tick-hz-760_000_000\",\n    feature = \"tick-hz-768_000_000\",\n    feature = \"tick-hz-780_000_000\",\n    feature = \"tick-hz-800_000_000\",\n    feature = \"tick-hz-820_000_000\",\n    feature = \"tick-hz-840_000_000\",\n    feature = \"tick-hz-860_000_000\",\n    feature = \"tick-hz-880_000_000\",\n    feature = \"tick-hz-900_000_000\",\n    feature = \"tick-hz-920_000_000\",\n    feature = \"tick-hz-940_000_000\",\n    feature = \"tick-hz-960_000_000\",\n    feature = \"tick-hz-980_000_000\",\n    feature = \"tick-hz-1_000_000_000\",\n    feature = \"tick-hz-1_310_720_000\",\n    feature = \"tick-hz-2_621_440_000\",\n    feature = \"tick-hz-5_242_880_000\",\n)))]\npub const TICK_HZ: u64 = 1_000_000;\n"
  },
  {
    "path": "embassy-time-queue-utils/CHANGELOG.md",
    "content": "# Changelog for embassy-time-queue-utils\n\nAll notable changes to this project will be documented in this file.\n\nThe format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),\nand this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).\n\n<!-- next-header -->\n## Unreleased - ReleaseDate\n\n- Fixed an issue where never-ending timers were not correctly removed from the timer queue\n- Both integrated and generic queue implementations are available for use, independent of their respective features.\n- Added `queue_integrated::Queue::schedule_wake_queue_item` to support timer queue item storage outside of embassy executor tasks.\n\n## 0.3.0 - 2025-08-26\n\n## 0.2.1 - 2025-08-26\n\n- Removed the embassy-executor dependency\n\n## 0.2.0 - 2025-08-04\n\nBumped embassy-executor\n\n## 0.1.0 - 2024-01-11\n\nInitial release\n"
  },
  {
    "path": "embassy-time-queue-utils/Cargo.toml",
    "content": "[package]\nname = \"embassy-time-queue-utils\"\nversion = \"0.3.0\"\nedition = \"2024\"\ndescription = \"Timer queue driver trait for embassy-time\"\nrepository = \"https://github.com/embassy-rs/embassy\"\ndocumentation = \"https://docs.embassy.dev/embassy-time-queue-utils\"\nreadme = \"README.md\"\nlicense = \"MIT OR Apache-2.0\"\ncategories = [\n    \"embedded\",\n    \"no-std\",\n    \"concurrency\",\n    \"asynchronous\",\n]\n\n# Prevent multiple copies of this crate in the same binary.\n# Needed because different copies might get different tick rates, causing\n# wrong delays if the time driver is using one copy and user code is using another.\n# This is especially common when mixing crates from crates.io and git.\nlinks = \"embassy-time-queue\"\n\n[dependencies]\nheapless = \"0.9\"\nembassy-executor-timer-queue = { version = \"0.1\", path = \"../embassy-executor-timer-queue\", features = [\"timer-item-size-6-words\"] }\n\n[features]\n#! ### Generic Queue\n\n#! By default this crate uses a timer queue implementation that is faster but depends on `embassy-executor`.\n#! It will panic if you try to await any timer when using another executor.\n#! \n#! Alternatively, you can choose to use a \"generic\" timer queue implementation that works on any executor.\n#! To enable it, enable any of the features below.\n#! \n#! The features also set how many timers are used for the generic queue. At most one\n#! `generic-queue-*` feature can be enabled. If none is enabled, a default of 64 timers is used.\n#!\n#! When using embassy-time-queue-driver from libraries, you should *not* enable any `generic-queue-*` feature, to allow the\n#! end user to pick.\n\n## Generic Queue with 8 timers\ngeneric-queue-8 = [\"_generic-queue\"]\n## Generic Queue with 16 timers\ngeneric-queue-16 = [\"_generic-queue\"]\n## Generic Queue with 32 timers\ngeneric-queue-32 = [\"_generic-queue\"]\n## Generic Queue with 64 timers\ngeneric-queue-64 = [\"_generic-queue\"]\n## Generic Queue with 128 timers\ngeneric-queue-128 = [\"_generic-queue\"]\n\n_generic-queue = []\n\n[package.metadata.embassy]\nbuild = [\n    {target = \"thumbv6m-none-eabi\", features = []},\n    {target = \"thumbv6m-none-eabi\", features = [\"generic-queue-8\"]},\n    # Xtensa builds\n    {group = \"xtensa\", build-std = [\"core\", \"alloc\"],  target = \"xtensa-esp32s2-none-elf\", features = []},\n    {group = \"xtensa\", build-std = [\"core\", \"alloc\"],  target = \"xtensa-esp32s2-none-elf\", features = [\"generic-queue-8\"]},\n]\n\n[package.metadata.embassy_docs]\nsrc_base = \"https://github.com/embassy-rs/embassy/blob/embassy-time-queue-utils-v$VERSION/embassy-time-queue-utils/src/\"\nsrc_base_git = \"https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-time-queue-utils/src/\"\ntarget = \"x86_64-unknown-linux-gnu\"\n"
  },
  {
    "path": "embassy-time-queue-utils/README.md",
    "content": "# embassy-time-queue-utils\n\nThis crate contains timer queues to help implementing an [`embassy-time-driver`](https://crates.io/crates/embassy-time-driver).\n\nAs a HAL user, you should not need to depend on this crate.\n\nAs a HAL implementer, you need to depend on this crate if you want to implement a time driver,\nbut how you should do so is documented in `embassy-time-driver`.\n"
  },
  {
    "path": "embassy-time-queue-utils/build.rs",
    "content": "fn main() {}\n"
  },
  {
    "path": "embassy-time-queue-utils/src/lib.rs",
    "content": "#![no_std]\n#![doc = include_str!(\"../README.md\")]\n#![warn(missing_docs)]\n\nuse core::task::Waker;\n\npub mod queue_generic;\npub mod queue_integrated;\n\n#[cfg(feature = \"_generic-queue\")]\ntype QueueImpl = queue_generic::Queue;\n#[cfg(not(feature = \"_generic-queue\"))]\ntype QueueImpl = queue_integrated::Queue;\n\n/// The default timer queue, configured by the crate's features.\n///\n/// If any of the `generic-queue-X` features are enabled, this implements a generic\n/// timer queue of capacity X. Otherwise, it implements an integrated timer queue.\n#[derive(Debug)]\npub struct Queue {\n    queue: QueueImpl,\n}\n\nimpl Queue {\n    /// Creates a new timer queue.\n    pub const fn new() -> Self {\n        Self {\n            queue: QueueImpl::new(),\n        }\n    }\n\n    /// Schedules a task to run at a specific time, and returns whether any changes were made.\n    ///\n    /// If this function returns `true`, the caller should find the next expiration time and set\n    /// a new alarm for that time.\n    pub fn schedule_wake(&mut self, at: u64, waker: &Waker) -> bool {\n        self.queue.schedule_wake(at, waker)\n    }\n\n    /// Dequeues expired timers and returns the next alarm time.\n    pub fn next_expiration(&mut self, now: u64) -> u64 {\n        self.queue.next_expiration(now)\n    }\n}\n"
  },
  {
    "path": "embassy-time-queue-utils/src/queue_generic.rs",
    "content": "//! Generic timer queue implementations.\n//!\n//! Time queue drivers may use this to simplify their implementation.\n\nuse core::cmp::{Ordering, min};\nuse core::task::Waker;\n\nuse heapless::Vec;\n\n#[derive(Debug)]\nstruct Timer {\n    at: u64,\n    waker: Waker,\n}\n\nimpl PartialEq for Timer {\n    fn eq(&self, other: &Self) -> bool {\n        self.at == other.at\n    }\n}\n\nimpl Eq for Timer {}\n\nimpl PartialOrd for Timer {\n    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {\n        self.at.partial_cmp(&other.at)\n    }\n}\n\nimpl Ord for Timer {\n    fn cmp(&self, other: &Self) -> Ordering {\n        self.at.cmp(&other.at)\n    }\n}\n\n/// A timer queue with a pre-determined capacity.\n#[derive(Debug)]\npub struct ConstGenericQueue<const QUEUE_SIZE: usize> {\n    queue: Vec<Timer, QUEUE_SIZE>,\n}\n\nimpl<const QUEUE_SIZE: usize> ConstGenericQueue<QUEUE_SIZE> {\n    /// Creates a new timer queue.\n    pub const fn new() -> Self {\n        Self { queue: Vec::new() }\n    }\n\n    /// Schedules a task to run at a specific time, and returns whether any changes were made.\n    ///\n    /// If this function returns `true`, the caller should find the next expiration time and set\n    /// a new alarm for that time.\n    pub fn schedule_wake(&mut self, at: u64, waker: &Waker) -> bool {\n        self.queue\n            .iter_mut()\n            .find(|timer| timer.waker.will_wake(waker))\n            .map(|timer| {\n                if timer.at > at {\n                    timer.at = at;\n                    true\n                } else {\n                    false\n                }\n            })\n            .unwrap_or_else(|| {\n                let mut timer = Timer {\n                    waker: waker.clone(),\n                    at,\n                };\n\n                loop {\n                    match self.queue.push(timer) {\n                        Ok(()) => break,\n                        Err(e) => timer = e,\n                    }\n\n                    self.queue.pop().unwrap().waker.wake();\n                }\n\n                true\n            })\n    }\n\n    /// Dequeues expired timers and returns the next alarm time.\n    pub fn next_expiration(&mut self, now: u64) -> u64 {\n        let mut next_alarm = u64::MAX;\n\n        let mut i = 0;\n        while i < self.queue.len() {\n            let timer = &self.queue[i];\n            if timer.at <= now {\n                let timer = self.queue.swap_remove(i);\n                timer.waker.wake();\n            } else {\n                next_alarm = min(next_alarm, timer.at);\n                i += 1;\n            }\n        }\n\n        next_alarm\n    }\n}\n\n#[cfg(feature = \"generic-queue-8\")]\nconst QUEUE_SIZE: usize = 8;\n#[cfg(feature = \"generic-queue-16\")]\nconst QUEUE_SIZE: usize = 16;\n#[cfg(feature = \"generic-queue-32\")]\nconst QUEUE_SIZE: usize = 32;\n#[cfg(feature = \"generic-queue-64\")]\nconst QUEUE_SIZE: usize = 64;\n#[cfg(feature = \"generic-queue-128\")]\nconst QUEUE_SIZE: usize = 128;\n#[cfg(not(any(\n    feature = \"generic-queue-8\",\n    feature = \"generic-queue-16\",\n    feature = \"generic-queue-32\",\n    feature = \"generic-queue-64\",\n    feature = \"generic-queue-128\"\n)))]\nconst QUEUE_SIZE: usize = 64;\n\n/// A timer queue with a pre-determined capacity.\n#[derive(Debug)]\npub struct Queue {\n    queue: ConstGenericQueue<QUEUE_SIZE>,\n}\n\nimpl Queue {\n    /// Creates a new timer queue.\n    pub const fn new() -> Self {\n        Self {\n            queue: ConstGenericQueue::new(),\n        }\n    }\n\n    /// Schedules a task to run at a specific time, and returns whether any changes were made.\n    ///\n    /// If this function returns `true`, the caller should find the next expiration time and set\n    /// a new alarm for that time.\n    pub fn schedule_wake(&mut self, at: u64, waker: &Waker) -> bool {\n        self.queue.schedule_wake(at, waker)\n    }\n\n    /// Dequeues expired timers and returns the next alarm time.\n    pub fn next_expiration(&mut self, now: u64) -> u64 {\n        self.queue.next_expiration(now)\n    }\n}\n"
  },
  {
    "path": "embassy-time-queue-utils/src/queue_integrated.rs",
    "content": "//! Timer queue operations.\nuse core::cell::Cell;\nuse core::cmp::min;\nuse core::ptr::NonNull;\nuse core::task::Waker;\n\nuse embassy_executor_timer_queue::TimerQueueItem;\n\n/// An item in the timer queue.\n#[derive(Default)]\nstruct QueueItem {\n    /// The next item in the queue.\n    ///\n    /// If this field contains `Some`, the item is in the queue. The last item in the queue has a\n    /// value of `Some(dangling_pointer)`\n    pub next: Cell<Option<NonNull<QueueItem>>>,\n\n    /// The time at which this item expires.\n    pub expires_at: u64,\n\n    /// The registered waker. If Some, the item is enqueued in the timer queue.\n    pub waker: Option<Waker>,\n}\n\nunsafe impl Sync for QueueItem {}\n\n/// A timer queue, with items integrated into tasks.\n///\n/// # Safety\n///\n/// **This Queue is only safe when there is a single integrated queue in the system.**\n///\n/// If there are multiple integrated queues, additional checks are necessary to ensure that a Waker\n/// is not attempted to be enqueued in multiple queues.\npub struct Queue {\n    head: Cell<Option<NonNull<QueueItem>>>,\n}\n\nimpl core::fmt::Debug for Queue {\n    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {\n        f.debug_struct(\"Queue\").finish()\n    }\n}\n\nunsafe impl Send for Queue {}\nunsafe impl Sync for Queue {}\n\nimpl Queue {\n    /// Creates a new timer queue.\n    pub const fn new() -> Self {\n        Self { head: Cell::new(None) }\n    }\n\n    /// Schedules a task to run at a specific time.\n    ///\n    /// If this function returns `true`, the caller should find the next expiration time and set\n    /// a new alarm for that time.\n    pub fn schedule_wake(&mut self, at: u64, waker: &Waker) -> bool {\n        let item = unsafe {\n            // Safety: the `&mut self`, along with the Safety note of the Queue, are sufficient to\n            // ensure that this function creates the only mutable reference to the queue item.\n            TimerQueueItem::from_embassy_waker(waker)\n        };\n        self.schedule_wake_queue_item(at, item, waker)\n    }\n\n    /// Schedules a task to run at a specific time, using a timer queue item as storage.\n    ///\n    /// If this function returns `true`, the caller should find the next expiration time and set\n    /// a new alarm for that time.\n    ///\n    /// This function assumes that the timer queue item is associated with the provided waker.\n    /// It can be used to support cases where there may not be an embassy task behind the waker.\n    /// One such case is integration with other async runtimes, be it a simple `block_on`\n    /// implementation or something more complex.\n    pub fn schedule_wake_queue_item(&mut self, at: u64, item: &mut TimerQueueItem, waker: &Waker) -> bool {\n        let item = unsafe { item.as_mut::<QueueItem>() };\n        match item.waker.as_ref() {\n            Some(_) if at <= item.expires_at => {\n                // If expiration is sooner than previously set, update.\n                item.expires_at = at;\n                // The waker is always stored in its own queue item, so we don't need to update it.\n\n                // Trigger a queue update in case this item can be immediately dequeued.\n                true\n            }\n            Some(_) => {\n                // Queue item does not need to be updated, the task will be scheduled to be woken\n                // before the new expiration.\n                false\n            }\n            None => {\n                // If not in the queue, add it and update.\n                let mut item_ptr = NonNull::from(item);\n                let prev = self.head.replace(Some(item_ptr));\n\n                let item = unsafe { item_ptr.as_mut() };\n\n                item.expires_at = at;\n                item.waker = Some(waker.clone());\n                item.next.set(prev);\n                // The default implementation doesn't care about the\n                // opaque payload, leave it unchanged.\n\n                true\n            }\n        }\n    }\n\n    /// Dequeues expired timers and returns the next alarm time.\n    ///\n    /// The provided callback will be called for each expired task. Tasks that never expire\n    /// will be removed, but the callback will not be called.\n    pub fn next_expiration(&mut self, now: u64) -> u64 {\n        let mut next_expiration = u64::MAX;\n\n        self.retain(|item| {\n            if item.expires_at <= now {\n                // Timer expired, process task.\n                if let Some(waker) = item.waker.take() {\n                    waker.wake();\n                }\n                false\n            } else {\n                // Timer didn't yet expire, or never expires.\n                next_expiration = min(next_expiration, item.expires_at);\n                item.expires_at != u64::MAX\n            }\n        });\n\n        next_expiration\n    }\n\n    fn retain(&mut self, mut f: impl FnMut(&mut QueueItem) -> bool) {\n        let mut prev = &self.head;\n        while let Some(mut p) = prev.get() {\n            let mut item = unsafe { p.as_mut() };\n\n            if f(&mut item) {\n                // Skip to next\n                prev = &item.next;\n            } else {\n                // Remove it\n                prev.set(item.next.get());\n                item.next.set(None);\n                // Presence of a waker is used by schedule_wake to determine\n                // if the item is part of the queue or not,\n                // so ensure there is no waker for items removed from the queue.\n                item.waker = None;\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "embassy-usb/CHANGELOG.md",
    "content": "# Changelog\n\nAll notable changes to this project will be documented in this file.\n\nThe format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),\nand this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).\n\n<!-- next-header -->\n## Unreleased - ReleaseDate\n\n- Bump usbd-hid from 0.9.0 to 0.10.0\n\n## 0.6.0 - 2026-03-10\n\n- Add support for USB HID Boot Protocol Mode\n- Bump usbd-hid from 0.8.1 to 0.9.0\n- Fix a bug where CDC ACM BufferedReceiver repeats data when its future is dropped\n- Expose `dtr()` and `rts()` on `cdc_acm::ControlChanged`\n- Add standalone DFU class implementation\n- Add method to signal firmware error in DFU\n- Allow `dfu_mode::Handler::start` to return a `Result` (fail gracefully)\n- Fix bug in USB DFU transition\n- Fix DFU GetStatus handler\n- Upgrade embassy-sync to 0.8.0\n- Upgrade embassy-net-driver-channel to 0.4.0\n\n## 0.5.1 - 2025-08-26\n\n## 0.5.0 - 2025-07-16\n\n- `UAC1`: unmute by default ([#3992](https://github.com/embassy-rs/embassy/pull/3992))\n- `cdc_acm`: `State::new` is now `const` ([#4000](https://github.com/embassy-rs/embassy/pull/4000))\n- Add support for CMSIS-DAP v2 USB class ([#4107](https://github.com/embassy-rs/embassy/pull/4107))\n- Reduce `UsbDevice` builder logs to `trace` ([#4130](https://github.com/embassy-rs/embassy/pull/4130))\n- Implement `embedded-io-async` traits for USB CDC ACM ([#4176](https://github.com/embassy-rs/embassy/pull/4176))\n- Update `embassy-sync` to v0.7.0\n- Fix CDC ACM BufferedReceiver buffer calculation\n\n## 0.4.0 - 2025-01-15\n\n- Change config defaults to to composite with IADs. This ensures embassy-usb Just Works in more cases when using classes with multiple interfaces, or multiple classes. (breaking change)\n    - `composite_with_iads` = `true`\n    - `device_class` = `0xEF`\n    - `device_sub_class` = `0x02`\n    - `device_protocol` = `0x01`\n- Add support for USB Audio Class 1.\n- Add support for isochronous endpoints.\n- Add support for setting the USB version number.\n- Add support for device qualifier descriptors.\n- Allow `bos_descriptor_buf` to be a zero length if BOS descriptors aren't used.\n\n## 0.3.0 - 2024-08-05\n\n- bump usbd-hid from 0.7.0 to 0.8.1\n- Add collapse_debuginfo to fmt.rs macros.\n- update embassy-sync dependency\n\n## 0.2.0 - 2024-05-20\n\n- [#2862](https://github.com/embassy-rs/embassy/pull/2862) WebUSB implementation by @chmanie\n- Removed dynamically sized `device_descriptor` fields\n\n## 0.1.0 - 2024-01-11\n\n- Initial Release\n"
  },
  {
    "path": "embassy-usb/Cargo.toml",
    "content": "[package]\nname = \"embassy-usb\"\nversion = \"0.6.0\"\nedition = \"2024\"\nlicense = \"MIT OR Apache-2.0\"\ndescription = \"Async USB device stack for embedded devices in Rust.\"\nkeywords = [\"embedded\", \"async\", \"usb\", \"hal\", \"embedded-hal\"]\ncategories = [\"embedded\", \"hardware-support\", \"no-std\", \"asynchronous\"]\nrepository = \"https://github.com/embassy-rs/embassy\"\ndocumentation = \"https://docs.embassy.dev/embassy-usb\"\n\n[package.metadata.embassy]\nbuild = [\n    {target = \"thumbv6m-none-eabi\", features = []},\n    {target = \"thumbv6m-none-eabi\", features = [\"log\"]},\n    {target = \"thumbv6m-none-eabi\", features = [\"defmt\"]},\n    {target = \"thumbv6m-none-eabi\", features = [\"usbd-hid\"]},\n    {target = \"thumbv6m-none-eabi\", features = [\"max-interface-count-1\"]},\n    {target = \"thumbv6m-none-eabi\", features = [\"max-interface-count-8\"]},\n    {target = \"thumbv6m-none-eabi\", features = [\"max-handler-count-8\"]},\n]\n\n[package.metadata.embassy_docs]\nsrc_base = \"https://github.com/embassy-rs/embassy/blob/embassy-usb-v$VERSION/embassy-usb/src/\"\nsrc_base_git = \"https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-usb/src/\"\nfeatures = [\"defmt\", \"usbd-hid\"]\ntarget = \"thumbv7em-none-eabi\"\n\n[package.metadata.docs.rs]\nfeatures = [\"defmt\", \"usbd-hid\"]\n\n[features]\ndefmt = [\"dep:defmt\", \"embassy-usb-driver/defmt\"]\nlog = [\"dep:log\"]\nusbd-hid = [\"dep:usbd-hid\", \"dep:ssmarshal\"]\ndefault = [\"usbd-hid\"]\n\n# BEGIN AUTOGENERATED CONFIG FEATURES\n# Generated by gen_config.py. DO NOT EDIT.\nmax-interface-count-1 = []\nmax-interface-count-2 = []\nmax-interface-count-3 = []\nmax-interface-count-4 = [] # Default\nmax-interface-count-5 = []\nmax-interface-count-6 = []\nmax-interface-count-7 = []\nmax-interface-count-8 = []\n\nmax-handler-count-1 = []\nmax-handler-count-2 = []\nmax-handler-count-3 = []\nmax-handler-count-4 = [] # Default\nmax-handler-count-5 = []\nmax-handler-count-6 = []\nmax-handler-count-7 = []\nmax-handler-count-8 = []\n\n# END AUTOGENERATED CONFIG FEATURES\n\n[dependencies]\nembassy-futures = { version = \"0.1.2\", path = \"../embassy-futures\" }\nembassy-usb-driver = { version = \"0.2.0\", path = \"../embassy-usb-driver\" }\nembassy-sync = { version = \"0.8.0\", path = \"../embassy-sync\" }\nembassy-net-driver-channel = { version = \"0.4.0\", path = \"../embassy-net-driver-channel\" }\nembassy-time = { version = \"0.5.1\", path = \"../embassy-time\" }\n\ndefmt = { version = \"1\", optional = true }\nlog = { version = \"0.4.14\", optional = true }\nheapless = \"0.9\"\nembedded-io-async = { version = \"0.7.0\" }\nbitflags = \"2.4.1\"\n\n# for HID\nusbd-hid = { version = \"0.10.0\", optional = true }\nssmarshal = { version = \"1.0\", default-features = false, optional = true }\n"
  },
  {
    "path": "embassy-usb/README.md",
    "content": "# embassy-usb\n\nAsync USB device stack for embedded devices in Rust.\n\n## Features\n\n- Native async.\n- Fully lock-free: endpoints are separate objects that can be used independently without needing a central mutex. If the driver supports it, they can even be used from different priority levels.\n- Suspend/resume, remote wakeup.\n- USB composite devices.\n- Ergonomic descriptor builder.\n- Ready-to-use implementations for a few USB classes (note you can still implement any class yourself outside the crate).\n    - Serial ports (CDC ACM)\n    - Ethernet (CDC NCM)\n    - Human Interface Devices (HID)\n    - MIDI\n\n## Adding support for new hardware\n\nTo add `embassy-usb` support for new hardware (i.e. a new MCU chip), you have to write a driver that implements\nthe [`embassy-usb-driver`](https://crates.io/crates/embassy-usb-driver) traits.\n\nBefore writing a new driver, you can first verify whether the chip uses a common USB IP. Several widely used USB IPs already have implementations available, such as:\n\n- **Synopsys OTG (dwc2)**: Available at [embassy-usb-synopsys-otg](https://crates.io/crates/embassy-usb-synopsys-otg). This IP is used by vendors like STMicroelectronics, Espressif, and others.\n- **Musbmhdrc (musb)**: Available at [musb](https://crates.io/crates/musb). This IP is used by vendors like TI, MediaTek, Puya, and others.\n\nDriver crates should depend only on `embassy-usb-driver`, not on the main `embassy-usb` crate.\nThis allows existing drivers to continue working for newer `embassy-usb` major versions, without needing an update, if the driver\ntrait has not had breaking changes.\n\n## Configuration\n\n`embassy-usb` has some configuration settings that are set at compile time, affecting sizes\nand counts of buffers.\n\nThey can be set in two ways:\n\n- Via Cargo features: enable a feature like `<name>-<value>`. `name` must be in lowercase and\nuse dashes instead of underscores. For example. `max-interface-count-3`. Only a selection of values\nis available, check `Cargo.toml` for the list.\n- Via environment variables at build time: set the variable named `EMBASSY_USB_<value>`. For example\n`EMBASSY_USB_MAX_INTERFACE_COUNT=3 cargo build`. You can also set them in the `[env]` section of `.cargo/config.toml`.\nAny value can be set, unlike with Cargo features.\n\nEnvironment variables take precedence over Cargo features. If two Cargo features are enabled for the same setting\nwith different values, compilation fails.\n\n### `MAX_INTERFACE_COUNT`\n\nMax amount of interfaces that can be created in one device. Default: 4.\n\n## Interoperability\n\nThis crate can run on any executor.\n"
  },
  {
    "path": "embassy-usb/build.rs",
    "content": "use std::collections::HashMap;\nuse std::fmt::Write;\nuse std::path::PathBuf;\nuse std::{env, fs};\n\nstatic CONFIGS: &[(&str, usize)] = &[\n    // BEGIN AUTOGENERATED CONFIG FEATURES\n    // Generated by gen_config.py. DO NOT EDIT.\n    (\"MAX_INTERFACE_COUNT\", 4),\n    (\"MAX_HANDLER_COUNT\", 4),\n    // END AUTOGENERATED CONFIG FEATURES\n];\n\nstruct ConfigState {\n    value: usize,\n    seen_feature: bool,\n    seen_env: bool,\n}\n\nfn main() {\n    let crate_name = env::var(\"CARGO_PKG_NAME\")\n        .unwrap()\n        .to_ascii_uppercase()\n        .replace('-', \"_\");\n\n    // only rebuild if build.rs changed. Otherwise Cargo will rebuild if any\n    // other file changed.\n    println!(\"cargo:rerun-if-changed=build.rs\");\n\n    // Rebuild if config envvar changed.\n    for (name, _) in CONFIGS {\n        println!(\"cargo:rerun-if-env-changed={crate_name}_{name}\");\n    }\n\n    let mut configs = HashMap::new();\n    for (name, default) in CONFIGS {\n        configs.insert(\n            *name,\n            ConfigState {\n                value: *default,\n                seen_env: false,\n                seen_feature: false,\n            },\n        );\n    }\n\n    let prefix = format!(\"{crate_name}_\");\n    for (var, value) in env::vars() {\n        if let Some(name) = var.strip_prefix(&prefix) {\n            let Some(cfg) = configs.get_mut(name) else {\n                panic!(\"Unknown env var {name}\")\n            };\n\n            let Ok(value) = value.parse::<usize>() else {\n                panic!(\"Invalid value for env var {name}: {value}\")\n            };\n\n            cfg.value = value;\n            cfg.seen_env = true;\n        }\n\n        if let Some(feature) = var.strip_prefix(\"CARGO_FEATURE_\") {\n            if let Some(i) = feature.rfind('_') {\n                let name = &feature[..i];\n                let value = &feature[i + 1..];\n                if let Some(cfg) = configs.get_mut(name) {\n                    let Ok(value) = value.parse::<usize>() else {\n                        panic!(\"Invalid value for feature {name}: {value}\")\n                    };\n\n                    // envvars take priority.\n                    if !cfg.seen_env {\n                        assert!(\n                            !cfg.seen_feature,\n                            \"multiple values set for feature {}: {} and {}\",\n                            name, cfg.value, value\n                        );\n\n                        cfg.value = value;\n                        cfg.seen_feature = true;\n                    }\n                }\n            }\n        }\n    }\n\n    let mut data = String::new();\n\n    for (name, cfg) in &configs {\n        writeln!(&mut data, \"pub const {}: usize = {};\", name, cfg.value).unwrap();\n    }\n\n    let out_dir = PathBuf::from(env::var_os(\"OUT_DIR\").unwrap());\n    let out_file = out_dir.join(\"config.rs\").to_string_lossy().to_string();\n    fs::write(out_file, data).unwrap();\n}\n"
  },
  {
    "path": "embassy-usb/gen_config.py",
    "content": "import os\n\nabspath = os.path.abspath(__file__)\ndname = os.path.dirname(abspath)\nos.chdir(dname)\n\nfeatures = []\n\n\ndef feature(name, default, min, max, pow2=None):\n    vals = set()\n    val = min\n    while val <= max:\n        vals.add(val)\n        if pow2 == True or (isinstance(pow2, int) and val >= pow2):\n            val *= 2\n        else:\n            val += 1\n    vals.add(default)\n\n    features.append(\n        {\n            \"name\": name,\n            \"default\": default,\n            \"vals\": sorted(list(vals)),\n        }\n    )\n\n\nfeature(\"max_interface_count\", default=4, min=1, max=8)\nfeature(\"max_handler_count\", default=4, min=1, max=8)\n\n# ========= Update Cargo.toml\n\nthings = \"\"\nfor f in features:\n    name = f[\"name\"].replace(\"_\", \"-\")\n    for val in f[\"vals\"]:\n        things += f\"{name}-{val} = []\"\n        if val == f[\"default\"]:\n            things += \" # Default\"\n        things += \"\\n\"\n    things += \"\\n\"\n\nSEPARATOR_START = \"# BEGIN AUTOGENERATED CONFIG FEATURES\\n\"\nSEPARATOR_END = \"# END AUTOGENERATED CONFIG FEATURES\\n\"\nHELP = \"# Generated by gen_config.py. DO NOT EDIT.\\n\"\nwith open(\"Cargo.toml\", \"r\") as f:\n    data = f.read()\nbefore, data = data.split(SEPARATOR_START, maxsplit=1)\n_, after = data.split(SEPARATOR_END, maxsplit=1)\ndata = before + SEPARATOR_START + HELP + things + SEPARATOR_END + after\nwith open(\"Cargo.toml\", \"w\") as f:\n    f.write(data)\n\n\n# ========= Update build.rs\n\nthings = \"\"\nfor f in features:\n    name = f[\"name\"].upper()\n    things += f'    (\"{name}\", {f[\"default\"]}),\\n'\n\nSEPARATOR_START = \"// BEGIN AUTOGENERATED CONFIG FEATURES\\n\"\nSEPARATOR_END = \"// END AUTOGENERATED CONFIG FEATURES\\n\"\nHELP = \"    // Generated by gen_config.py. DO NOT EDIT.\\n\"\nwith open(\"build.rs\", \"r\") as f:\n    data = f.read()\nbefore, data = data.split(SEPARATOR_START, maxsplit=1)\n_, after = data.split(SEPARATOR_END, maxsplit=1)\ndata = before + SEPARATOR_START + HELP + \\\n    things + \"    \" + SEPARATOR_END + after\nwith open(\"build.rs\", \"w\") as f:\n    f.write(data)\n"
  },
  {
    "path": "embassy-usb/src/builder.rs",
    "content": "use heapless::Vec;\n\nuse crate::config::MAX_HANDLER_COUNT;\nuse crate::descriptor::{BosWriter, DescriptorWriter, SynchronizationType, UsageType};\nuse crate::driver::{Driver, Endpoint, EndpointAddress, EndpointInfo, EndpointType};\nuse crate::msos::{DeviceLevelDescriptor, FunctionLevelDescriptor, MsOsDescriptorWriter};\nuse crate::types::{InterfaceNumber, StringIndex};\nuse crate::{Handler, Interface, MAX_INTERFACE_COUNT, STRING_INDEX_CUSTOM_START, UsbDevice};\n\n#[derive(Debug, Copy, Clone)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\n#[non_exhaustive]\n/// Allows Configuring the Bcd USB version below 2.1\npub enum UsbVersion {\n    /// Usb version 2.0\n    Two = 0x0200,\n    /// Usb version 2.1\n    TwoOne = 0x0210,\n}\n\n#[derive(Debug, Copy, Clone)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\n#[non_exhaustive]\n/// Configuration used when creating [`UsbDevice`].\npub struct Config<'a> {\n    pub(crate) vendor_id: u16,\n    pub(crate) product_id: u16,\n\n    /// Device BCD USB version.\n    ///\n    /// Default: `0x0210` (\"2.1\")\n    pub bcd_usb: UsbVersion,\n\n    /// Device class code assigned by USB.org. Set to `0xff` for vendor-specific\n    /// devices that do not conform to any class.\n    ///\n    /// Default: `0xEF`\n    /// See also: `composite_with_iads`\n    pub device_class: u8,\n\n    /// Device sub-class code. Depends on class.\n    ///\n    /// Default: `0x02`\n    /// See also: `composite_with_iads`\n    pub device_sub_class: u8,\n\n    /// Device protocol code. Depends on class and sub-class.\n    ///\n    /// Default: `0x01`\n    /// See also: `composite_with_iads`\n    pub device_protocol: u8,\n\n    /// Device release version in BCD.\n    ///\n    /// Default: `0x0010` (\"0.1\")\n    pub device_release: u16,\n\n    /// Maximum packet size in bytes for the control endpoint 0.\n    ///\n    /// Valid values depend on the speed at which the bus is enumerated.\n    /// - low speed: 8\n    /// - full speed: 8, 16, 32, or 64\n    /// - high speed: 64\n    ///\n    /// Default: 64 bytes\n    pub max_packet_size_0: u8,\n\n    /// Manufacturer name string descriptor.\n    ///\n    /// Default: (none)\n    pub manufacturer: Option<&'a str>,\n\n    /// Product name string descriptor.\n    ///\n    /// Default: (none)\n    pub product: Option<&'a str>,\n\n    /// Serial number string descriptor.\n    ///\n    /// Default: (none)\n    pub serial_number: Option<&'a str>,\n\n    /// Whether the device supports remotely waking up the host is requested.\n    ///\n    /// Default: `false`\n    pub supports_remote_wakeup: bool,\n\n    /// Configures the device as a composite device with interface association descriptors.\n    ///\n    /// If set to `true` (default), the following fields should have the given values:\n    ///\n    /// - `device_class` = `0xEF`\n    /// - `device_sub_class` = `0x02`\n    /// - `device_protocol` = `0x01`\n    ///\n    /// If set to `false`, those fields must be set correctly for the classes that will be\n    /// installed on the USB device.\n    pub composite_with_iads: bool,\n\n    /// Whether the device has its own power source.\n    ///\n    /// This should be set to `true` even if the device is sometimes self-powered and may not\n    /// always draw power from the USB bus.\n    ///\n    /// Default: `false`\n    ///\n    /// See also: `max_power`\n    pub self_powered: bool,\n\n    /// Maximum current drawn from the USB bus by the device, in milliamps.\n    ///\n    /// The default is 100 mA. If your device always uses an external power source and never draws\n    /// power from the USB bus, this can be set to 0.\n    ///\n    /// See also: `self_powered`\n    ///\n    /// Default: 100mA\n    /// Max: 500mA\n    pub max_power: u16,\n}\n\nimpl<'a> Config<'a> {\n    /// Create default configuration with the provided vid and pid values.\n    pub const fn new(vid: u16, pid: u16) -> Self {\n        Self {\n            device_class: 0xEF,\n            device_sub_class: 0x02,\n            device_protocol: 0x01,\n            max_packet_size_0: 64,\n            vendor_id: vid,\n            product_id: pid,\n            device_release: 0x0010,\n            bcd_usb: UsbVersion::TwoOne,\n            manufacturer: None,\n            product: None,\n            serial_number: None,\n            self_powered: false,\n            supports_remote_wakeup: false,\n            composite_with_iads: true,\n            max_power: 100,\n        }\n    }\n}\n\n/// [`UsbDevice`] builder.\npub struct Builder<'d, D: Driver<'d>> {\n    config: Config<'d>,\n    handlers: Vec<&'d mut dyn Handler, MAX_HANDLER_COUNT>,\n    interfaces: Vec<Interface, MAX_INTERFACE_COUNT>,\n    control_buf: &'d mut [u8],\n\n    driver: D,\n    next_string_index: u8,\n\n    config_descriptor: DescriptorWriter<'d>,\n    bos_descriptor: BosWriter<'d>,\n\n    msos_descriptor: MsOsDescriptorWriter<'d>,\n}\n\nimpl<'d, D: Driver<'d>> Builder<'d, D> {\n    /// Creates a builder for constructing a new [`UsbDevice`].\n    ///\n    /// `control_buf` is a buffer used for USB control request data. It should be sized\n    /// large enough for the length of the largest control request (in or out)\n    /// anticipated by any class added to the device.\n    pub fn new(\n        driver: D,\n        config: Config<'d>,\n        config_descriptor_buf: &'d mut [u8],\n        bos_descriptor_buf: &'d mut [u8],\n        msos_descriptor_buf: &'d mut [u8],\n        control_buf: &'d mut [u8],\n    ) -> Self {\n        // Magic values specified in USB-IF ECN on IADs.\n        if config.composite_with_iads\n            && (config.device_class != 0xEF || config.device_sub_class != 0x02 || config.device_protocol != 0x01)\n        {\n            panic!(\n                \"if composite_with_iads is set, you must set device_class = 0xEF, device_sub_class = 0x02, device_protocol = 0x01\"\n            );\n        }\n\n        assert!(\n            config.max_power <= 500,\n            \"The maximum allowed value for `max_power` is 500mA\"\n        );\n\n        match config.max_packet_size_0 {\n            8 | 16 | 32 | 64 => {}\n            _ => panic!(\"invalid max_packet_size_0, the allowed values are 8, 16, 32 or 64\"),\n        }\n\n        let mut config_descriptor = DescriptorWriter::new(config_descriptor_buf);\n        let mut bos_descriptor = BosWriter::new(DescriptorWriter::new(bos_descriptor_buf));\n\n        config_descriptor.configuration(&config);\n        bos_descriptor.bos();\n\n        Builder {\n            driver,\n            config,\n            interfaces: Vec::new(),\n            handlers: Vec::new(),\n            control_buf,\n            next_string_index: STRING_INDEX_CUSTOM_START,\n\n            config_descriptor,\n            bos_descriptor,\n\n            msos_descriptor: MsOsDescriptorWriter::new(msos_descriptor_buf),\n        }\n    }\n\n    /// Creates the [`UsbDevice`] instance with the configuration in this builder.\n    pub fn build(mut self) -> UsbDevice<'d, D> {\n        let msos_descriptor = self.msos_descriptor.build(&mut self.bos_descriptor);\n\n        self.config_descriptor.end_configuration();\n        self.bos_descriptor.end_bos();\n\n        // Log the number of allocator bytes actually used in descriptor buffers\n        trace!(\"USB: config_descriptor used: {}\", self.config_descriptor.position());\n        trace!(\"USB: bos_descriptor used: {}\", self.bos_descriptor.writer.position());\n        trace!(\"USB: msos_descriptor used: {}\", msos_descriptor.len());\n        trace!(\"USB: control_buf size: {}\", self.control_buf.len());\n\n        UsbDevice::build(\n            self.driver,\n            self.config,\n            self.handlers,\n            self.config_descriptor.into_buf(),\n            self.bos_descriptor.writer.into_buf(),\n            msos_descriptor,\n            self.interfaces,\n            self.control_buf,\n        )\n    }\n\n    /// Returns the size of the control request data buffer. Can be used by\n    /// classes to validate the buffer is large enough for their needs.\n    pub fn control_buf_len(&self) -> usize {\n        self.control_buf.len()\n    }\n\n    /// Add an USB function.\n    ///\n    /// If [`Config::composite_with_iads`] is set, this will add an IAD descriptor\n    /// with the given class/subclass/protocol, associating all the child interfaces.\n    ///\n    /// If it's not set, no IAD descriptor is added.\n    pub fn function(&mut self, class: u8, subclass: u8, protocol: u8) -> FunctionBuilder<'_, 'd, D> {\n        let first_interface = InterfaceNumber::new(self.interfaces.len() as u8);\n        let iface_count_index = if self.config.composite_with_iads {\n            self.config_descriptor\n                .iad(first_interface, 0, class, subclass, protocol);\n\n            Some(self.config_descriptor.position() - 5)\n        } else {\n            None\n        };\n\n        FunctionBuilder {\n            builder: self,\n            iface_count_index,\n\n            first_interface,\n        }\n    }\n\n    /// Add a Handler.\n    ///\n    /// The Handler is called on some USB bus events, and to handle all control requests not already\n    /// handled by the USB stack.\n    pub fn handler(&mut self, handler: &'d mut dyn Handler) {\n        assert!(\n            self.handlers.push(handler).is_ok(),\n            \"embassy-usb: handler list full. Increase the `max_handler_count` compile-time setting. Current value: {}\",\n            MAX_HANDLER_COUNT\n        );\n    }\n\n    /// Allocates a new string index.\n    pub fn string(&mut self) -> StringIndex {\n        let index = self.next_string_index;\n        self.next_string_index += 1;\n        StringIndex::new(index)\n    }\n\n    /// Add an MS OS 2.0 Descriptor Set.\n    ///\n    /// Panics if called more than once.\n    pub fn msos_descriptor(&mut self, windows_version: u32, vendor_code: u8) {\n        self.msos_descriptor.header(windows_version, vendor_code);\n    }\n\n    /// Add an MS OS 2.0 Device Level Feature Descriptor.\n    pub fn msos_feature<T: DeviceLevelDescriptor>(&mut self, desc: T) {\n        self.msos_descriptor.device_feature(desc);\n    }\n\n    /// Gets the underlying [`MsOsDescriptorWriter`] to allow adding subsets and features for classes that\n    /// do not add their own.\n    pub fn msos_writer(&mut self) -> &mut MsOsDescriptorWriter<'d> {\n        &mut self.msos_descriptor\n    }\n}\n\n/// Function builder.\n///\n/// A function is a logical grouping of interfaces that perform a given USB function.\n/// If [`Config::composite_with_iads`] is set, each function will have an IAD descriptor.\n/// If not, functions will not be visible as descriptors.\npub struct FunctionBuilder<'a, 'd, D: Driver<'d>> {\n    builder: &'a mut Builder<'d, D>,\n    iface_count_index: Option<usize>,\n\n    first_interface: InterfaceNumber,\n}\n\nimpl<'a, 'd, D: Driver<'d>> Drop for FunctionBuilder<'a, 'd, D> {\n    fn drop(&mut self) {\n        self.builder.msos_descriptor.end_function();\n    }\n}\n\nimpl<'a, 'd, D: Driver<'d>> FunctionBuilder<'a, 'd, D> {\n    /// Add an interface to the function.\n    ///\n    /// Interface numbers are guaranteed to be allocated consecutively, starting from 0.\n    pub fn interface(&mut self) -> InterfaceBuilder<'_, 'd, D> {\n        if let Some(i) = self.iface_count_index {\n            self.builder.config_descriptor.buf[i] += 1;\n        }\n\n        let number = self.builder.interfaces.len() as _;\n        let iface = Interface {\n            current_alt_setting: 0,\n            num_alt_settings: 0,\n        };\n\n        assert!(\n            self.builder.interfaces.push(iface).is_ok(),\n            \"embassy-usb: interface list full. Increase the `max_interface_count` compile-time setting. Current value: {}\",\n            MAX_INTERFACE_COUNT\n        );\n\n        InterfaceBuilder {\n            builder: self.builder,\n            interface_number: InterfaceNumber::new(number),\n            next_alt_setting_number: 0,\n        }\n    }\n\n    /// Add an MS OS 2.0 Function Level Feature Descriptor.\n    pub fn msos_feature<T: FunctionLevelDescriptor>(&mut self, desc: T) {\n        if !self.builder.msos_descriptor.is_in_config_subset() {\n            self.builder.msos_descriptor.configuration(0);\n        }\n\n        if !self.builder.msos_descriptor.is_in_function_subset() {\n            self.builder.msos_descriptor.function(self.first_interface);\n        }\n\n        self.builder.msos_descriptor.function_feature(desc);\n    }\n}\n\n/// Interface builder.\npub struct InterfaceBuilder<'a, 'd, D: Driver<'d>> {\n    builder: &'a mut Builder<'d, D>,\n    interface_number: InterfaceNumber,\n    next_alt_setting_number: u8,\n}\n\nimpl<'a, 'd, D: Driver<'d>> InterfaceBuilder<'a, 'd, D> {\n    /// Get the interface number.\n    pub const fn interface_number(&self) -> InterfaceNumber {\n        self.interface_number\n    }\n\n    /// Allocates a new string index.\n    pub fn string(&mut self) -> StringIndex {\n        self.builder.string()\n    }\n\n    /// Add an alternate setting to the interface and write its descriptor.\n    ///\n    /// Alternate setting numbers are guaranteed to be allocated consecutively, starting from 0.\n    ///\n    /// The first alternate setting, with number 0, is the default one.\n    pub fn alt_setting(\n        &mut self,\n        class: u8,\n        subclass: u8,\n        protocol: u8,\n        interface_string: Option<StringIndex>,\n    ) -> InterfaceAltBuilder<'_, 'd, D> {\n        let number = self.next_alt_setting_number;\n        self.next_alt_setting_number += 1;\n        self.builder.interfaces[self.interface_number.0 as usize].num_alt_settings += 1;\n\n        self.builder.config_descriptor.interface_alt(\n            self.interface_number,\n            number,\n            class,\n            subclass,\n            protocol,\n            interface_string,\n        );\n\n        InterfaceAltBuilder {\n            builder: self.builder,\n            interface_number: self.interface_number,\n            alt_setting_number: number,\n        }\n    }\n}\n\n/// Interface alternate setting builder.\npub struct InterfaceAltBuilder<'a, 'd, D: Driver<'d>> {\n    builder: &'a mut Builder<'d, D>,\n    interface_number: InterfaceNumber,\n    alt_setting_number: u8,\n}\n\nimpl<'a, 'd, D: Driver<'d>> InterfaceAltBuilder<'a, 'd, D> {\n    /// Get the interface number.\n    pub const fn interface_number(&self) -> InterfaceNumber {\n        self.interface_number\n    }\n\n    /// Get the alternate setting number.\n    pub const fn alt_setting_number(&self) -> u8 {\n        self.alt_setting_number\n    }\n\n    /// Add a custom descriptor to this alternate setting.\n    ///\n    /// Descriptors are written in the order builder functions are called. Note that some\n    /// classes care about the order.\n    pub fn descriptor(&mut self, descriptor_type: u8, descriptor: &[u8]) {\n        self.builder.config_descriptor.write(descriptor_type, descriptor, &[]);\n    }\n\n    /// Add a custom Binary Object Store (BOS) descriptor to this alternate setting.\n    pub fn bos_capability(&mut self, capability_type: u8, capability: &[u8]) {\n        self.builder.bos_descriptor.capability(capability_type, capability);\n    }\n\n    /// Write a custom endpoint descriptor for a certain endpoint.\n    ///\n    /// This can be necessary, if the endpoint descriptors can only be written\n    /// after the endpoint was created. As an example, an endpoint descriptor\n    /// may contain the address of an endpoint that was allocated earlier.\n    pub fn endpoint_descriptor(\n        &mut self,\n        endpoint: &EndpointInfo,\n        synchronization_type: SynchronizationType,\n        usage_type: UsageType,\n        extra_fields: &[u8],\n    ) {\n        self.builder\n            .config_descriptor\n            .endpoint(endpoint, synchronization_type, usage_type, extra_fields);\n    }\n\n    /// Allocate an IN endpoint, without writing its descriptor.\n    ///\n    /// Used for granular control over the order of endpoint and descriptor creation.\n    pub fn alloc_endpoint_in(\n        &mut self,\n        ep_type: EndpointType,\n        ep_addr: Option<EndpointAddress>,\n        max_packet_size: u16,\n        interval_ms: u8,\n    ) -> D::EndpointIn {\n        let ep = self\n            .builder\n            .driver\n            .alloc_endpoint_in(ep_type, ep_addr, max_packet_size, interval_ms)\n            .expect(\"alloc_endpoint_in failed\");\n\n        ep\n    }\n\n    fn endpoint_in(\n        &mut self,\n        ep_type: EndpointType,\n        ep_addr: Option<EndpointAddress>,\n        max_packet_size: u16,\n        interval_ms: u8,\n        synchronization_type: SynchronizationType,\n        usage_type: UsageType,\n        extra_fields: &[u8],\n    ) -> D::EndpointIn {\n        let ep = self.alloc_endpoint_in(ep_type, ep_addr, max_packet_size, interval_ms);\n        self.endpoint_descriptor(ep.info(), synchronization_type, usage_type, extra_fields);\n\n        ep\n    }\n\n    /// Allocate an OUT endpoint, without writing its descriptor.\n    ///\n    /// Use for granular control over the order of endpoint and descriptor creation.\n    pub fn alloc_endpoint_out(\n        &mut self,\n        ep_type: EndpointType,\n        ep_addr: Option<EndpointAddress>,\n        max_packet_size: u16,\n        interval_ms: u8,\n    ) -> D::EndpointOut {\n        let ep = self\n            .builder\n            .driver\n            .alloc_endpoint_out(ep_type, ep_addr, max_packet_size, interval_ms)\n            .expect(\"alloc_endpoint_out failed\");\n\n        ep\n    }\n\n    fn endpoint_out(\n        &mut self,\n        ep_type: EndpointType,\n        ep_addr: Option<EndpointAddress>,\n        max_packet_size: u16,\n        interval_ms: u8,\n        synchronization_type: SynchronizationType,\n        usage_type: UsageType,\n        extra_fields: &[u8],\n    ) -> D::EndpointOut {\n        let ep = self.alloc_endpoint_out(ep_type, ep_addr, max_packet_size, interval_ms);\n        self.endpoint_descriptor(ep.info(), synchronization_type, usage_type, extra_fields);\n\n        ep\n    }\n\n    /// Allocate a BULK IN endpoint and write its descriptor.\n    ///\n    /// Descriptors are written in the order builder functions are called. Note that some\n    /// classes care about the order.\n    pub fn endpoint_bulk_in(&mut self, ep_addr: Option<EndpointAddress>, max_packet_size: u16) -> D::EndpointIn {\n        self.endpoint_in(\n            EndpointType::Bulk,\n            ep_addr,\n            max_packet_size,\n            0,\n            SynchronizationType::NoSynchronization,\n            UsageType::DataEndpoint,\n            &[],\n        )\n    }\n\n    /// Allocate a BULK OUT endpoint and write its descriptor.\n    ///\n    /// Descriptors are written in the order builder functions are called. Note that some\n    /// classes care about the order.\n    pub fn endpoint_bulk_out(&mut self, ep_addr: Option<EndpointAddress>, max_packet_size: u16) -> D::EndpointOut {\n        self.endpoint_out(\n            EndpointType::Bulk,\n            ep_addr,\n            max_packet_size,\n            0,\n            SynchronizationType::NoSynchronization,\n            UsageType::DataEndpoint,\n            &[],\n        )\n    }\n\n    /// Allocate a INTERRUPT IN endpoint and write its descriptor.\n    ///\n    /// Descriptors are written in the order builder functions are called. Note that some\n    /// classes care about the order.\n    pub fn endpoint_interrupt_in(\n        &mut self,\n        ep_addr: Option<EndpointAddress>,\n        max_packet_size: u16,\n        interval_ms: u8,\n    ) -> D::EndpointIn {\n        self.endpoint_in(\n            EndpointType::Interrupt,\n            ep_addr,\n            max_packet_size,\n            interval_ms,\n            SynchronizationType::NoSynchronization,\n            UsageType::DataEndpoint,\n            &[],\n        )\n    }\n\n    /// Allocate a INTERRUPT OUT endpoint and write its descriptor.\n    pub fn endpoint_interrupt_out(\n        &mut self,\n        ep_addr: Option<EndpointAddress>,\n        max_packet_size: u16,\n        interval_ms: u8,\n    ) -> D::EndpointOut {\n        self.endpoint_out(\n            EndpointType::Interrupt,\n            ep_addr,\n            max_packet_size,\n            interval_ms,\n            SynchronizationType::NoSynchronization,\n            UsageType::DataEndpoint,\n            &[],\n        )\n    }\n\n    /// Allocate a ISOCHRONOUS IN endpoint and write its descriptor.\n    ///\n    /// Descriptors are written in the order builder functions are called. Note that some\n    /// classes care about the order.\n    pub fn endpoint_isochronous_in(\n        &mut self,\n        ep_addr: Option<EndpointAddress>,\n        max_packet_size: u16,\n        interval_ms: u8,\n        synchronization_type: SynchronizationType,\n        usage_type: UsageType,\n        extra_fields: &[u8],\n    ) -> D::EndpointIn {\n        self.endpoint_in(\n            EndpointType::Isochronous,\n            ep_addr,\n            max_packet_size,\n            interval_ms,\n            synchronization_type,\n            usage_type,\n            extra_fields,\n        )\n    }\n\n    /// Allocate a ISOCHRONOUS OUT endpoint and write its descriptor.\n    pub fn endpoint_isochronous_out(\n        &mut self,\n        ep_addr: Option<EndpointAddress>,\n        max_packet_size: u16,\n        interval_ms: u8,\n        synchronization_type: SynchronizationType,\n        usage_type: UsageType,\n        extra_fields: &[u8],\n    ) -> D::EndpointOut {\n        self.endpoint_out(\n            EndpointType::Isochronous,\n            ep_addr,\n            max_packet_size,\n            interval_ms,\n            synchronization_type,\n            usage_type,\n            extra_fields,\n        )\n    }\n}\n"
  },
  {
    "path": "embassy-usb/src/class/cdc_acm.rs",
    "content": "//! CDC-ACM class implementation, aka Serial over USB.\n\nuse core::cell::{Cell, RefCell};\nuse core::future::{Future, poll_fn};\nuse core::mem::{self, MaybeUninit};\nuse core::sync::atomic::{AtomicBool, Ordering};\nuse core::task::Poll;\n\nuse embassy_sync::blocking_mutex::CriticalSectionMutex;\nuse embassy_sync::waitqueue::WakerRegistration;\n\nuse crate::control::{self, InResponse, OutResponse, Recipient, Request, RequestType};\nuse crate::driver::{Driver, Endpoint, EndpointError, EndpointIn, EndpointOut};\nuse crate::types::InterfaceNumber;\nuse crate::{Builder, Handler};\n\n/// This should be used as `device_class` when building the `UsbDevice`.\npub const USB_CLASS_CDC: u8 = 0x02;\n\nconst USB_CLASS_CDC_DATA: u8 = 0x0a;\nconst CDC_SUBCLASS_ACM: u8 = 0x02;\nconst CDC_PROTOCOL_NONE: u8 = 0x00;\n\nconst CS_INTERFACE: u8 = 0x24;\nconst CDC_TYPE_HEADER: u8 = 0x00;\nconst CDC_TYPE_ACM: u8 = 0x02;\nconst CDC_TYPE_UNION: u8 = 0x06;\n\nconst REQ_SEND_ENCAPSULATED_COMMAND: u8 = 0x00;\n#[allow(unused)]\nconst REQ_GET_ENCAPSULATED_COMMAND: u8 = 0x01;\nconst REQ_SET_LINE_CODING: u8 = 0x20;\nconst REQ_GET_LINE_CODING: u8 = 0x21;\nconst REQ_SET_CONTROL_LINE_STATE: u8 = 0x22;\n\n/// CDC ACM error.\n#[derive(Clone, Debug)]\npub enum CdcAcmError {\n    /// USB is not connected.\n    NotConnected,\n}\n\nimpl core::fmt::Display for CdcAcmError {\n    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {\n        match *self {\n            Self::NotConnected => f.write_str(\"NotConnected\"),\n        }\n    }\n}\n\nimpl core::error::Error for CdcAcmError {}\nimpl embedded_io_async::Error for CdcAcmError {\n    fn kind(&self) -> embedded_io_async::ErrorKind {\n        match *self {\n            Self::NotConnected => embedded_io_async::ErrorKind::NotConnected,\n        }\n    }\n}\n\n/// Internal state for CDC-ACM\npub struct State<'a> {\n    control: MaybeUninit<Control<'a>>,\n    shared: ControlShared,\n}\n\nimpl<'a> Default for State<'a> {\n    fn default() -> Self {\n        Self::new()\n    }\n}\n\nimpl<'a> State<'a> {\n    /// Create a new `State`.\n    pub const fn new() -> Self {\n        Self {\n            control: MaybeUninit::uninit(),\n            shared: ControlShared::new(),\n        }\n    }\n}\n\n/// Packet level implementation of a CDC-ACM serial port.\n///\n/// This class can be used directly and it has the least overhead due to directly reading and\n/// writing USB packets with no intermediate buffers, but it will not act like a stream-like serial\n/// port. The following constraints must be followed if you use this class directly:\n///\n/// - `read_packet` must be called with a buffer large enough to hold `max_packet_size` bytes.\n/// - `write_packet` must not be called with a buffer larger than `max_packet_size` bytes.\n/// - If you write a packet that is exactly `max_packet_size` bytes long, it won't be processed by the\n///   host operating system until a subsequent shorter packet is sent. A zero-length packet (ZLP)\n///   can be sent if there is no other data to send. This is because USB bulk transactions must be\n///   terminated with a short packet, even if the bulk endpoint is used for stream-like data.\npub struct CdcAcmClass<'d, D: Driver<'d>> {\n    _comm_ep: D::EndpointIn,\n    _data_if: InterfaceNumber,\n    read_ep: D::EndpointOut,\n    write_ep: D::EndpointIn,\n    control: &'d ControlShared,\n}\n\nstruct Control<'a> {\n    comm_if: InterfaceNumber,\n    shared: &'a ControlShared,\n}\n\n/// Shared data between Control and CdcAcmClass\nstruct ControlShared {\n    line_coding: CriticalSectionMutex<Cell<LineCoding>>,\n    dtr: AtomicBool,\n    rts: AtomicBool,\n\n    waker: RefCell<WakerRegistration>,\n    changed: AtomicBool,\n}\n\nimpl Default for ControlShared {\n    fn default() -> Self {\n        Self::new()\n    }\n}\n\nimpl ControlShared {\n    const fn new() -> Self {\n        ControlShared {\n            dtr: AtomicBool::new(false),\n            rts: AtomicBool::new(false),\n            line_coding: CriticalSectionMutex::new(Cell::new(LineCoding {\n                stop_bits: StopBits::One,\n                data_bits: 8,\n                parity_type: ParityType::None,\n                data_rate: 8_000,\n            })),\n            waker: RefCell::new(WakerRegistration::new()),\n            changed: AtomicBool::new(false),\n        }\n    }\n\n    fn changed(&self) -> impl Future<Output = ()> + '_ {\n        poll_fn(|cx| {\n            if self.changed.load(Ordering::Relaxed) {\n                self.changed.store(false, Ordering::Relaxed);\n                Poll::Ready(())\n            } else {\n                self.waker.borrow_mut().register(cx.waker());\n                Poll::Pending\n            }\n        })\n    }\n}\n\nimpl<'a> Control<'a> {\n    fn shared(&mut self) -> &'a ControlShared {\n        self.shared\n    }\n}\n\nimpl<'d> Handler for Control<'d> {\n    fn reset(&mut self) {\n        let shared = self.shared();\n        shared.line_coding.lock(|x| x.set(LineCoding::default()));\n        shared.dtr.store(false, Ordering::Relaxed);\n        shared.rts.store(false, Ordering::Relaxed);\n\n        shared.changed.store(true, Ordering::Relaxed);\n        shared.waker.borrow_mut().wake();\n    }\n\n    fn control_out(&mut self, req: control::Request, data: &[u8]) -> Option<OutResponse> {\n        if (req.request_type, req.recipient, req.index)\n            != (RequestType::Class, Recipient::Interface, self.comm_if.0 as u16)\n        {\n            return None;\n        }\n\n        match req.request {\n            REQ_SEND_ENCAPSULATED_COMMAND => {\n                // We don't actually support encapsulated commands but pretend we do for standards\n                // compatibility.\n                Some(OutResponse::Accepted)\n            }\n            REQ_SET_LINE_CODING if data.len() >= 7 => {\n                let coding = LineCoding {\n                    data_rate: u32::from_le_bytes(data[0..4].try_into().unwrap()),\n                    stop_bits: data[4].into(),\n                    parity_type: data[5].into(),\n                    data_bits: data[6],\n                };\n                let shared = self.shared();\n                shared.line_coding.lock(|x| x.set(coding));\n                debug!(\"Set line coding to: {:?}\", coding);\n\n                shared.changed.store(true, Ordering::Relaxed);\n                shared.waker.borrow_mut().wake();\n\n                Some(OutResponse::Accepted)\n            }\n            REQ_SET_CONTROL_LINE_STATE => {\n                let dtr = (req.value & 0x0001) != 0;\n                let rts = (req.value & 0x0002) != 0;\n\n                let shared = self.shared();\n                shared.dtr.store(dtr, Ordering::Relaxed);\n                shared.rts.store(rts, Ordering::Relaxed);\n                debug!(\"Set dtr {}, rts {}\", dtr, rts);\n\n                shared.changed.store(true, Ordering::Relaxed);\n                shared.waker.borrow_mut().wake();\n\n                Some(OutResponse::Accepted)\n            }\n            _ => Some(OutResponse::Rejected),\n        }\n    }\n\n    fn control_in<'a>(&'a mut self, req: Request, buf: &'a mut [u8]) -> Option<InResponse<'a>> {\n        if (req.request_type, req.recipient, req.index)\n            != (RequestType::Class, Recipient::Interface, self.comm_if.0 as u16)\n        {\n            return None;\n        }\n\n        match req.request {\n            // REQ_GET_ENCAPSULATED_COMMAND is not really supported - it will be rejected below.\n            REQ_GET_LINE_CODING if req.length == 7 => {\n                debug!(\"Sending line coding\");\n                let coding = self.shared().line_coding.lock(Cell::get);\n                assert!(buf.len() >= 7);\n                buf[0..4].copy_from_slice(&coding.data_rate.to_le_bytes());\n                buf[4] = coding.stop_bits as u8;\n                buf[5] = coding.parity_type as u8;\n                buf[6] = coding.data_bits;\n                Some(InResponse::Accepted(&buf[0..7]))\n            }\n            _ => Some(InResponse::Rejected),\n        }\n    }\n}\n\nimpl<'d, D: Driver<'d>> CdcAcmClass<'d, D> {\n    /// Creates a new CdcAcmClass with the provided UsbBus and `max_packet_size` in bytes. For\n    /// full-speed devices, `max_packet_size` has to be one of 8, 16, 32 or 64.\n    pub fn new(builder: &mut Builder<'d, D>, state: &'d mut State<'d>, max_packet_size: u16) -> Self {\n        assert!(builder.control_buf_len() >= 7);\n\n        let mut func = builder.function(USB_CLASS_CDC, CDC_SUBCLASS_ACM, CDC_PROTOCOL_NONE);\n\n        // Control interface\n        let mut iface = func.interface();\n        let comm_if = iface.interface_number();\n        let data_if = u8::from(comm_if) + 1;\n        let mut alt = iface.alt_setting(USB_CLASS_CDC, CDC_SUBCLASS_ACM, CDC_PROTOCOL_NONE, None);\n\n        alt.descriptor(\n            CS_INTERFACE,\n            &[\n                CDC_TYPE_HEADER, // bDescriptorSubtype\n                0x10,\n                0x01, // bcdCDC (1.10)\n            ],\n        );\n        alt.descriptor(\n            CS_INTERFACE,\n            &[\n                CDC_TYPE_ACM, // bDescriptorSubtype\n                0x02,         // bmCapabilities:\n                              // D1: Device supports the request combination of\n                              // Set_Line_Coding, Set_Control_Line_State, Get_Line_Coding,\n                              // and the Notification Serial_State.\n            ],\n        );\n        alt.descriptor(\n            CS_INTERFACE,\n            &[\n                CDC_TYPE_UNION, // bDescriptorSubtype\n                comm_if.into(), // bControlInterface\n                data_if,        // bSubordinateInterface\n            ],\n        );\n\n        let comm_ep = alt.endpoint_interrupt_in(None, 8, 255);\n\n        // Data interface\n        let mut iface = func.interface();\n        let data_if = iface.interface_number();\n        let mut alt = iface.alt_setting(USB_CLASS_CDC_DATA, 0x00, CDC_PROTOCOL_NONE, None);\n        let read_ep = alt.endpoint_bulk_out(None, max_packet_size);\n        let write_ep = alt.endpoint_bulk_in(None, max_packet_size);\n\n        drop(func);\n\n        let control = state.control.write(Control {\n            shared: &state.shared,\n            comm_if,\n        });\n        builder.handler(control);\n\n        let control_shared = &state.shared;\n\n        CdcAcmClass {\n            _comm_ep: comm_ep,\n            _data_if: data_if,\n            read_ep,\n            write_ep,\n            control: control_shared,\n        }\n    }\n\n    /// Gets the maximum packet size in bytes.\n    pub fn max_packet_size(&self) -> u16 {\n        // The size is the same for both endpoints.\n        self.read_ep.info().max_packet_size\n    }\n\n    /// Gets the current line coding. The line coding contains information that's mainly relevant\n    /// for USB to UART serial port emulators, and can be ignored if not relevant.\n    pub fn line_coding(&self) -> LineCoding {\n        self.control.line_coding.lock(Cell::get)\n    }\n\n    /// Gets the DTR (data terminal ready) state\n    pub fn dtr(&self) -> bool {\n        self.control.dtr.load(Ordering::Relaxed)\n    }\n\n    /// Gets the RTS (request to send) state\n    pub fn rts(&self) -> bool {\n        self.control.rts.load(Ordering::Relaxed)\n    }\n\n    /// Writes a single packet into the IN endpoint.\n    pub async fn write_packet(&mut self, data: &[u8]) -> Result<(), EndpointError> {\n        self.write_ep.write(data).await\n    }\n\n    /// Reads a single packet from the OUT endpoint.\n    pub async fn read_packet(&mut self, data: &mut [u8]) -> Result<usize, EndpointError> {\n        self.read_ep.read(data).await\n    }\n\n    /// Waits for the USB host to enable this interface\n    pub async fn wait_connection(&mut self) {\n        self.read_ep.wait_enabled().await;\n    }\n\n    /// Split the class into a sender and receiver.\n    ///\n    /// This allows concurrently sending and receiving packets from separate tasks.\n    pub fn split(self) -> (Sender<'d, D>, Receiver<'d, D>) {\n        (\n            Sender {\n                write_ep: self.write_ep,\n                control: self.control,\n            },\n            Receiver {\n                read_ep: self.read_ep,\n                control: self.control,\n            },\n        )\n    }\n\n    /// Split the class into sender, receiver and control\n    ///\n    /// Allows concurrently sending and receiving packets whilst monitoring for\n    /// control changes (dtr, rts)\n    pub fn split_with_control(self) -> (Sender<'d, D>, Receiver<'d, D>, ControlChanged<'d>) {\n        (\n            Sender {\n                write_ep: self.write_ep,\n                control: self.control,\n            },\n            Receiver {\n                read_ep: self.read_ep,\n                control: self.control,\n            },\n            ControlChanged { control: self.control },\n        )\n    }\n}\n\n/// CDC ACM Control status change monitor\n///\n/// You can obtain a `ControlChanged` with [`CdcAcmClass::split_with_control`]\npub struct ControlChanged<'d> {\n    control: &'d ControlShared,\n}\n\nimpl<'d> ControlChanged<'d> {\n    /// Return a future for when the control settings change\n    pub async fn control_changed(&self) {\n        self.control.changed().await;\n    }\n\n    /// Gets the DTR (data terminal ready) state\n    pub fn dtr(&self) -> bool {\n        self.control.dtr.load(Ordering::Relaxed)\n    }\n\n    /// Gets the RTS (request to send) state\n    pub fn rts(&self) -> bool {\n        self.control.rts.load(Ordering::Relaxed)\n    }\n}\n\n/// CDC ACM class packet sender.\n///\n/// You can obtain a `Sender` with [`CdcAcmClass::split`]\npub struct Sender<'d, D: Driver<'d>> {\n    write_ep: D::EndpointIn,\n    control: &'d ControlShared,\n}\n\nimpl<'d, D: Driver<'d>> Sender<'d, D> {\n    /// Gets the maximum packet size in bytes.\n    pub fn max_packet_size(&self) -> u16 {\n        // The size is the same for both endpoints.\n        self.write_ep.info().max_packet_size\n    }\n\n    /// Gets the current line coding. The line coding contains information that's mainly relevant\n    /// for USB to UART serial port emulators, and can be ignored if not relevant.\n    pub fn line_coding(&self) -> LineCoding {\n        self.control.line_coding.lock(Cell::get)\n    }\n\n    /// Gets the DTR (data terminal ready) state\n    pub fn dtr(&self) -> bool {\n        self.control.dtr.load(Ordering::Relaxed)\n    }\n\n    /// Gets the RTS (request to send) state\n    pub fn rts(&self) -> bool {\n        self.control.rts.load(Ordering::Relaxed)\n    }\n\n    /// Writes a single packet into the IN endpoint.\n    pub async fn write_packet(&mut self, data: &[u8]) -> Result<(), EndpointError> {\n        self.write_ep.write(data).await\n    }\n\n    /// Waits for the USB host to enable this interface\n    pub async fn wait_connection(&mut self) {\n        self.write_ep.wait_enabled().await;\n    }\n}\n\nimpl<'d, D: Driver<'d>> embedded_io_async::ErrorType for Sender<'d, D> {\n    type Error = CdcAcmError;\n}\n\nimpl<'d, D: Driver<'d>> embedded_io_async::Write for Sender<'d, D> {\n    async fn write(&mut self, buf: &[u8]) -> Result<usize, Self::Error> {\n        let len = core::cmp::min(buf.len(), self.max_packet_size() as usize);\n        match self.write_packet(&buf[..len]).await {\n            Ok(()) => Ok(len),\n            Err(EndpointError::BufferOverflow) => unreachable!(),\n            Err(EndpointError::Disabled) => Err(CdcAcmError::NotConnected),\n        }\n    }\n\n    async fn flush(&mut self) -> Result<(), Self::Error> {\n        Ok(())\n    }\n}\n\n/// CDC ACM class packet receiver.\n///\n/// You can obtain a `Receiver` with [`CdcAcmClass::split`]\npub struct Receiver<'d, D: Driver<'d>> {\n    read_ep: D::EndpointOut,\n    control: &'d ControlShared,\n}\n\nimpl<'d, D: Driver<'d>> Receiver<'d, D> {\n    /// Gets the maximum packet size in bytes.\n    pub fn max_packet_size(&self) -> u16 {\n        // The size is the same for both endpoints.\n        self.read_ep.info().max_packet_size\n    }\n\n    /// Gets the current line coding. The line coding contains information that's mainly relevant\n    /// for USB to UART serial port emulators, and can be ignored if not relevant.\n    pub fn line_coding(&self) -> LineCoding {\n        self.control.line_coding.lock(Cell::get)\n    }\n\n    /// Gets the DTR (data terminal ready) state\n    pub fn dtr(&self) -> bool {\n        self.control.dtr.load(Ordering::Relaxed)\n    }\n\n    /// Gets the RTS (request to send) state\n    pub fn rts(&self) -> bool {\n        self.control.rts.load(Ordering::Relaxed)\n    }\n\n    /// Reads a single packet from the OUT endpoint.\n    /// Must be called with a buffer large enough to hold max_packet_size bytes.\n    pub async fn read_packet(&mut self, data: &mut [u8]) -> Result<usize, EndpointError> {\n        self.read_ep.read(data).await\n    }\n\n    /// Waits for the USB host to enable this interface\n    pub async fn wait_connection(&mut self) {\n        self.read_ep.wait_enabled().await;\n    }\n\n    /// Turn the `Receiver` into a [`BufferedReceiver`].\n    ///\n    /// The supplied buffer must be large enough to hold max_packet_size bytes.\n    pub fn into_buffered(self, buf: &'d mut [u8]) -> BufferedReceiver<'d, D> {\n        BufferedReceiver {\n            receiver: self,\n            buffer: buf,\n            start: 0,\n            end: 0,\n        }\n    }\n}\n\n/// CDC ACM class buffered receiver.\n///\n/// It is a requirement of the [`embedded_io_async::Read`] trait that arbitrarily small lengths of\n/// data can be read from the stream. The [`Receiver`] can only read full packets at a time. The\n/// `BufferedReceiver` instead buffers a single packet if the caller does not read all of the data,\n/// so that the remaining data can be returned in subsequent calls.\n///\n/// If you have no requirement to use the [`embedded_io_async::Read`] trait or to read a data length\n/// less than the packet length, then it is more efficient to use the [`Receiver`] directly.\n///\n/// You can obtain a `BufferedReceiver` with [`Receiver::into_buffered`].\n///\n/// [`embedded_io_async::Read`]: https://docs.rs/embedded-io-async/latest/embedded_io_async/trait.Read.html\npub struct BufferedReceiver<'d, D: Driver<'d>> {\n    receiver: Receiver<'d, D>,\n    buffer: &'d mut [u8],\n    start: usize,\n    end: usize,\n}\n\nimpl<'d, D: Driver<'d>> BufferedReceiver<'d, D> {\n    fn read_from_buffer(&mut self, buf: &mut [u8]) -> usize {\n        let available = &self.buffer[self.start..self.end];\n        let len = core::cmp::min(available.len(), buf.len());\n        buf[..len].copy_from_slice(&available[..len]);\n        self.start += len;\n        len\n    }\n\n    /// Gets the current line coding. The line coding contains information that's mainly relevant\n    /// for USB to UART serial port emulators, and can be ignored if not relevant.\n    pub fn line_coding(&self) -> LineCoding {\n        self.receiver.line_coding()\n    }\n\n    /// Gets the DTR (data terminal ready) state\n    pub fn dtr(&self) -> bool {\n        self.receiver.dtr()\n    }\n\n    /// Gets the RTS (request to send) state\n    pub fn rts(&self) -> bool {\n        self.receiver.rts()\n    }\n\n    /// Waits for the USB host to enable this interface\n    pub async fn wait_connection(&mut self) {\n        self.receiver.wait_connection().await;\n    }\n}\n\nimpl<'d, D: Driver<'d>> embedded_io_async::ErrorType for BufferedReceiver<'d, D> {\n    type Error = CdcAcmError;\n}\n\nimpl<'d, D: Driver<'d>> embedded_io_async::Read for BufferedReceiver<'d, D> {\n    async fn read(&mut self, buf: &mut [u8]) -> Result<usize, Self::Error> {\n        // If there is a buffered packet, return data from that first\n        if self.start != self.end {\n            return Ok(self.read_from_buffer(buf));\n        }\n\n        // If the caller's buffer is large enough to contain an entire packet, read directly into\n        // that instead of buffering the packet internally.\n        if buf.len() > self.receiver.max_packet_size() as usize {\n            return match self.receiver.read_packet(buf).await {\n                Ok(n) => Ok(n),\n                Err(EndpointError::BufferOverflow) => unreachable!(),\n                Err(EndpointError::Disabled) => Err(CdcAcmError::NotConnected),\n            };\n        }\n\n        // Otherwise read a packet into the internal buffer, and return some of it to the caller.\n        //\n        // It's important that `start` and `end` be updated in this order so they're left in a\n        // consistent state if the `read` future is dropped mid-execution, e.g. from a timeout.\n        match self.receiver.read_packet(&mut self.buffer).await {\n            Ok(n) => self.end = n,\n            Err(EndpointError::BufferOverflow) => unreachable!(),\n            Err(EndpointError::Disabled) => return Err(CdcAcmError::NotConnected),\n        }\n        self.start = 0;\n        return Ok(self.read_from_buffer(buf));\n    }\n}\n\n/// Number of stop bits for LineCoding\n#[derive(Copy, Clone, Debug, PartialEq, Eq)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub enum StopBits {\n    /// 1 stop bit\n    One = 0,\n\n    /// 1.5 stop bits\n    OnePointFive = 1,\n\n    /// 2 stop bits\n    Two = 2,\n}\n\nimpl From<u8> for StopBits {\n    fn from(value: u8) -> Self {\n        if value <= 2 {\n            unsafe { mem::transmute(value) }\n        } else {\n            StopBits::One\n        }\n    }\n}\n\n/// Parity for LineCoding\n#[derive(Copy, Clone, Debug, PartialEq, Eq)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub enum ParityType {\n    /// No parity bit.\n    None = 0,\n    /// Parity bit is 1 if the amount of `1` bits in the data byte is odd.\n    Odd = 1,\n    /// Parity bit is 1 if the amount of `1` bits in the data byte is even.\n    Even = 2,\n    /// Parity bit is always 1\n    Mark = 3,\n    /// Parity bit is always 0\n    Space = 4,\n}\n\nimpl From<u8> for ParityType {\n    fn from(value: u8) -> Self {\n        if value <= 4 {\n            unsafe { mem::transmute(value) }\n        } else {\n            ParityType::None\n        }\n    }\n}\n\n/// Line coding parameters\n///\n/// This is provided by the host for specifying the standard UART parameters such as baud rate. Can\n/// be ignored if you don't plan to interface with a physical UART.\n#[derive(Clone, Copy, Debug)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub struct LineCoding {\n    stop_bits: StopBits,\n    data_bits: u8,\n    parity_type: ParityType,\n    data_rate: u32,\n}\n\nimpl LineCoding {\n    /// Gets the number of stop bits for UART communication.\n    pub fn stop_bits(&self) -> StopBits {\n        self.stop_bits\n    }\n\n    /// Gets the number of data bits for UART communication.\n    pub const fn data_bits(&self) -> u8 {\n        self.data_bits\n    }\n\n    /// Gets the parity type for UART communication.\n    pub const fn parity_type(&self) -> ParityType {\n        self.parity_type\n    }\n\n    /// Gets the data rate in bits per second for UART communication.\n    pub const fn data_rate(&self) -> u32 {\n        self.data_rate\n    }\n}\n\nimpl Default for LineCoding {\n    fn default() -> Self {\n        LineCoding {\n            stop_bits: StopBits::One,\n            data_bits: 8,\n            parity_type: ParityType::None,\n            data_rate: 8_000,\n        }\n    }\n}\n"
  },
  {
    "path": "embassy-usb/src/class/cdc_ncm/embassy_net.rs",
    "content": "//! [`embassy-net`](https://crates.io/crates/embassy-net) driver for the CDC-NCM class.\n\nuse embassy_futures::select::{Either, select};\nuse embassy_net_driver_channel as ch;\nuse embassy_net_driver_channel::driver::LinkState;\nuse embassy_usb_driver::Driver;\n\nuse super::{CdcNcmClass, Receiver, Sender};\n\n/// Internal state for the embassy-net integration.\npub struct State<const MTU: usize, const N_RX: usize, const N_TX: usize> {\n    ch_state: ch::State<MTU, N_RX, N_TX>,\n}\n\nimpl<const MTU: usize, const N_RX: usize, const N_TX: usize> State<MTU, N_RX, N_TX> {\n    /// Create a new `State`.\n    pub const fn new() -> Self {\n        Self {\n            ch_state: ch::State::new(),\n        }\n    }\n}\n\n/// Background runner for the CDC-NCM class.\n///\n/// You must call `.run()` in a background task for the class to operate.\npub struct Runner<'d, D: Driver<'d>, const MTU: usize> {\n    tx_usb: Sender<'d, D>,\n    rx_usb: Receiver<'d, D>,\n    ch: ch::Runner<'d, MTU>,\n}\n\nimpl<'d, D: Driver<'d>, const MTU: usize> Runner<'d, D, MTU> {\n    /// Run the CDC-NCM class.\n    ///\n    /// You must call this in a background task for the class to operate.\n    pub async fn run(mut self) -> ! {\n        let (state_chan, mut rx_chan, mut tx_chan) = self.ch.split();\n        let rx_fut = async move {\n            loop {\n                trace!(\"WAITING for connection\");\n                state_chan.set_link_state(LinkState::Down);\n\n                self.rx_usb.wait_connection().await.unwrap();\n\n                trace!(\"Connected\");\n                state_chan.set_link_state(LinkState::Up);\n\n                loop {\n                    let p = rx_chan.rx_buf().await;\n                    match self.rx_usb.read_packet(p).await {\n                        Ok(n) => rx_chan.rx_done(n),\n                        Err(e) => {\n                            warn!(\"error reading packet: {:?}\", e);\n                            break;\n                        }\n                    };\n                }\n            }\n        };\n        let tx_fut = async move {\n            loop {\n                let p = tx_chan.tx_buf().await;\n                if let Err(e) = self.tx_usb.write_packet(p).await {\n                    warn!(\"Failed to TX packet: {:?}\", e);\n                }\n                tx_chan.tx_done();\n            }\n        };\n        match select(rx_fut, tx_fut).await {\n            Either::First(x) => x,\n            Either::Second(x) => x,\n        }\n    }\n}\n\n// would be cool to use a TAIT here, but it gives a \"may not live long enough\". rustc bug?\n//pub type Device<'d, const MTU: usize> = impl embassy_net_driver_channel::driver::Driver + 'd;\n/// Type alias for the embassy-net driver for CDC-NCM.\npub type Device<'d, const MTU: usize> = embassy_net_driver_channel::Device<'d, MTU>;\n\nimpl<'d, D: Driver<'d>> CdcNcmClass<'d, D> {\n    /// Obtain a driver for using the CDC-NCM class with [`embassy-net`](https://crates.io/crates/embassy-net).\n    pub fn into_embassy_net_device<const MTU: usize, const N_RX: usize, const N_TX: usize>(\n        self,\n        state: &'d mut State<MTU, N_RX, N_TX>,\n        ethernet_address: [u8; 6],\n    ) -> (Runner<'d, D, MTU>, Device<'d, MTU>) {\n        let (tx_usb, rx_usb) = self.split();\n        let (runner, device) = ch::new(\n            &mut state.ch_state,\n            ch::driver::HardwareAddress::Ethernet(ethernet_address),\n        );\n\n        (\n            Runner {\n                tx_usb,\n                rx_usb,\n                ch: runner,\n            },\n            device,\n        )\n    }\n}\n"
  },
  {
    "path": "embassy-usb/src/class/cdc_ncm/mod.rs",
    "content": "//! CDC-NCM class implementation, aka Ethernet over USB.\n//!\n//! # Compatibility\n//!\n//! Windows: NOT supported in Windows 10 (though there's apparently a driver you can install?). Supported out of the box in Windows 11.\n//!\n//! Linux: Well-supported since forever.\n//!\n//! Android: Support for CDC-NCM is spotty and varies across manufacturers.\n//!\n//! - On Pixel 4a, it refused to work on Android 11, worked on Android 12.\n//! - if the host's MAC address has the \"locally-administered\" bit set (bit 1 of first byte),\n//!   it doesn't work! The \"Ethernet tethering\" option in settings doesn't get enabled.\n//!   This is due to regex spaghetti: <https://android.googlesource.com/platform/frameworks/base/+/refs/tags/android-mainline-12.0.0_r84/core/res/res/values/config.xml#417>\n//!   and this nonsense in the linux kernel: <https://github.com/torvalds/linux/blob/c00c5e1d157bec0ef0b0b59aa5482eb8dc7e8e49/drivers/net/usb/usbnet.c#L1751-L1757>\n\nuse core::mem::{MaybeUninit, size_of};\nuse core::ptr::{addr_of, copy_nonoverlapping};\n\nuse crate::control::{self, InResponse, OutResponse, Recipient, Request, RequestType};\nuse crate::driver::{Driver, Endpoint, EndpointError, EndpointIn, EndpointOut};\nuse crate::types::{InterfaceNumber, StringIndex};\nuse crate::{Builder, Handler};\n\npub mod embassy_net;\n\n/// This should be used as `device_class` when building the `UsbDevice`.\npub const USB_CLASS_CDC: u8 = 0x02;\n\nconst USB_CLASS_CDC_DATA: u8 = 0x0a;\nconst CDC_SUBCLASS_NCM: u8 = 0x0d;\n\nconst CDC_PROTOCOL_NONE: u8 = 0x00;\nconst CDC_PROTOCOL_NTB: u8 = 0x01;\n\nconst CS_INTERFACE: u8 = 0x24;\nconst CDC_TYPE_HEADER: u8 = 0x00;\nconst CDC_TYPE_UNION: u8 = 0x06;\nconst CDC_TYPE_ETHERNET: u8 = 0x0F;\nconst CDC_TYPE_NCM: u8 = 0x1A;\n\nconst REQ_SEND_ENCAPSULATED_COMMAND: u8 = 0x00;\n//const REQ_GET_ENCAPSULATED_COMMAND: u8 = 0x01;\n//const REQ_SET_ETHERNET_MULTICAST_FILTERS: u8 = 0x40;\n//const REQ_SET_ETHERNET_POWER_MANAGEMENT_PATTERN_FILTER: u8 = 0x41;\n//const REQ_GET_ETHERNET_POWER_MANAGEMENT_PATTERN_FILTER: u8 = 0x42;\n//const REQ_SET_ETHERNET_PACKET_FILTER: u8 = 0x43;\n//const REQ_GET_ETHERNET_STATISTIC: u8 = 0x44;\nconst REQ_GET_NTB_PARAMETERS: u8 = 0x80;\n//const REQ_GET_NET_ADDRESS: u8 = 0x81;\n//const REQ_SET_NET_ADDRESS: u8 = 0x82;\n//const REQ_GET_NTB_FORMAT: u8 = 0x83;\n//const REQ_SET_NTB_FORMAT: u8 = 0x84;\n//const REQ_GET_NTB_INPUT_SIZE: u8 = 0x85;\nconst REQ_SET_NTB_INPUT_SIZE: u8 = 0x86;\n//const REQ_GET_MAX_DATAGRAM_SIZE: u8 = 0x87;\n//const REQ_SET_MAX_DATAGRAM_SIZE: u8 = 0x88;\n//const REQ_GET_CRC_MODE: u8 = 0x89;\n//const REQ_SET_CRC_MODE: u8 = 0x8A;\n\n//const NOTIF_MAX_PACKET_SIZE: u16 = 8;\n//const NOTIF_POLL_INTERVAL: u8 = 20;\n\nconst NTB_MAX_SIZE: usize = 2048;\nconst SIG_NTH: u32 = 0x484d_434e;\nconst SIG_NDP_NO_FCS: u32 = 0x304d_434e;\nconst SIG_NDP_WITH_FCS: u32 = 0x314d_434e;\n\nconst ALTERNATE_SETTING_DISABLED: u8 = 0x00;\nconst ALTERNATE_SETTING_ENABLED: u8 = 0x01;\n\n/// Simple NTB header (NTH+NDP all in one) for sending packets\n#[repr(packed)]\n#[allow(unused)]\nstruct NtbOutHeader {\n    // NTH\n    nth_sig: u32,\n    nth_len: u16,\n    nth_seq: u16,\n    nth_total_len: u16,\n    nth_first_index: u16,\n\n    // NDP\n    ndp_sig: u32,\n    ndp_len: u16,\n    ndp_next_index: u16,\n    ndp_datagram_index: u16,\n    ndp_datagram_len: u16,\n    ndp_term1: u16,\n    ndp_term2: u16,\n}\n\n#[repr(packed)]\n#[allow(unused)]\nstruct NtbParameters {\n    length: u16,\n    formats_supported: u16,\n    in_params: NtbParametersDir,\n    out_params: NtbParametersDir,\n}\n\n#[repr(packed)]\n#[allow(unused)]\nstruct NtbParametersDir {\n    max_size: u32,\n    divisor: u16,\n    payload_remainder: u16,\n    out_alignment: u16,\n    max_datagram_count: u16,\n}\n\nfn byteify<T>(buf: &mut [u8], data: T) -> &[u8] {\n    let len = size_of::<T>();\n    unsafe { copy_nonoverlapping(addr_of!(data).cast(), buf.as_mut_ptr(), len) }\n    &buf[..len]\n}\n\n/// Internal state for the CDC-NCM class.\npub struct State<'a> {\n    control: MaybeUninit<Control<'a>>,\n    shared: ControlShared,\n}\n\nimpl<'a> Default for State<'a> {\n    fn default() -> Self {\n        Self::new()\n    }\n}\n\nimpl<'a> State<'a> {\n    /// Create a new `State`.\n    pub fn new() -> Self {\n        Self {\n            control: MaybeUninit::uninit(),\n            shared: ControlShared::default(),\n        }\n    }\n}\n\n/// Shared data between Control and `CdcAcmClass`\n#[derive(Default)]\nstruct ControlShared {\n    mac_addr: [u8; 6],\n}\n\nstruct Control<'a> {\n    mac_addr_string: StringIndex,\n    shared: &'a ControlShared,\n    mac_addr_str: [u8; 12],\n    comm_if: InterfaceNumber,\n    data_if: InterfaceNumber,\n}\n\nimpl<'d> Handler for Control<'d> {\n    fn set_alternate_setting(&mut self, iface: InterfaceNumber, alternate_setting: u8) {\n        if iface != self.data_if {\n            return;\n        }\n\n        match alternate_setting {\n            ALTERNATE_SETTING_ENABLED => info!(\"ncm: interface enabled\"),\n            ALTERNATE_SETTING_DISABLED => info!(\"ncm: interface disabled\"),\n            _ => unreachable!(),\n        }\n    }\n\n    fn control_out(&mut self, req: control::Request, _data: &[u8]) -> Option<OutResponse> {\n        if (req.request_type, req.recipient, req.index)\n            != (RequestType::Class, Recipient::Interface, self.comm_if.0 as u16)\n        {\n            return None;\n        }\n\n        match req.request {\n            REQ_SEND_ENCAPSULATED_COMMAND => {\n                // We don't actually support encapsulated commands but pretend we do for standards\n                // compatibility.\n                Some(OutResponse::Accepted)\n            }\n            REQ_SET_NTB_INPUT_SIZE => {\n                // TODO\n                Some(OutResponse::Accepted)\n            }\n            _ => Some(OutResponse::Rejected),\n        }\n    }\n\n    fn control_in<'a>(&'a mut self, req: Request, buf: &'a mut [u8]) -> Option<InResponse<'a>> {\n        if (req.request_type, req.recipient, req.index)\n            != (RequestType::Class, Recipient::Interface, self.comm_if.0 as u16)\n        {\n            return None;\n        }\n\n        match req.request {\n            REQ_GET_NTB_PARAMETERS => {\n                let res = NtbParameters {\n                    length: size_of::<NtbParameters>() as _,\n                    formats_supported: 1, // only 16bit,\n                    in_params: NtbParametersDir {\n                        max_size: NTB_MAX_SIZE as _,\n                        divisor: 4,\n                        payload_remainder: 0,\n                        out_alignment: 4,\n                        max_datagram_count: 0, // not used\n                    },\n                    out_params: NtbParametersDir {\n                        max_size: NTB_MAX_SIZE as _,\n                        divisor: 4,\n                        payload_remainder: 0,\n                        out_alignment: 4,\n                        max_datagram_count: 1, // We only decode 1 packet per NTB\n                    },\n                };\n                Some(InResponse::Accepted(byteify(buf, res)))\n            }\n            _ => Some(InResponse::Rejected),\n        }\n    }\n\n    fn get_string(&mut self, index: StringIndex, _lang_id: u16) -> Option<&str> {\n        if index == self.mac_addr_string {\n            let mac_addr = self.shared.mac_addr;\n            let s = &mut self.mac_addr_str;\n            for i in 0..12 {\n                let n = (mac_addr[i / 2] >> ((1 - i % 2) * 4)) & 0xF;\n                s[i] = match n {\n                    0x0..=0x9 => b'0' + n,\n                    0xA..=0xF => b'A' + n - 0xA,\n                    _ => unreachable!(),\n                }\n            }\n\n            Some(unsafe { core::str::from_utf8_unchecked(s) })\n        } else {\n            warn!(\"unknown string index requested\");\n            None\n        }\n    }\n}\n\n/// CDC-NCM class\npub struct CdcNcmClass<'d, D: Driver<'d>> {\n    _comm_if: InterfaceNumber,\n    comm_ep: D::EndpointIn,\n\n    data_if: InterfaceNumber,\n    read_ep: D::EndpointOut,\n    write_ep: D::EndpointIn,\n\n    _control: &'d ControlShared,\n\n    max_packet_size: usize,\n}\n\nimpl<'d, D: Driver<'d>> CdcNcmClass<'d, D> {\n    /// Create a new CDC NCM class.\n    pub fn new(\n        builder: &mut Builder<'d, D>,\n        state: &'d mut State<'d>,\n        mac_address: [u8; 6],\n        max_packet_size: u16,\n    ) -> Self {\n        state.shared.mac_addr = mac_address;\n\n        let mut func = builder.function(USB_CLASS_CDC, CDC_SUBCLASS_NCM, CDC_PROTOCOL_NONE);\n\n        // Control interface\n        let mut iface = func.interface();\n        let mac_addr_string = iface.string();\n        let comm_if = iface.interface_number();\n        let mut alt = iface.alt_setting(USB_CLASS_CDC, CDC_SUBCLASS_NCM, CDC_PROTOCOL_NONE, None);\n\n        alt.descriptor(\n            CS_INTERFACE,\n            &[\n                CDC_TYPE_HEADER, // bDescriptorSubtype\n                0x10,\n                0x01, // bcdCDC (1.10)\n            ],\n        );\n        alt.descriptor(\n            CS_INTERFACE,\n            &[\n                CDC_TYPE_UNION,        // bDescriptorSubtype\n                comm_if.into(),        // bControlInterface\n                u8::from(comm_if) + 1, // bSubordinateInterface\n            ],\n        );\n        alt.descriptor(\n            CS_INTERFACE,\n            &[\n                CDC_TYPE_ETHERNET,      // bDescriptorSubtype\n                mac_addr_string.into(), // iMACAddress\n                0,                      // bmEthernetStatistics\n                0,                      // |\n                0,                      // |\n                0,                      // |\n                0xea,                   // wMaxSegmentSize = 1514\n                0x05,                   // |\n                0,                      // wNumberMCFilters\n                0,                      // |\n                0,                      // bNumberPowerFilters\n            ],\n        );\n        alt.descriptor(\n            CS_INTERFACE,\n            &[\n                CDC_TYPE_NCM, // bDescriptorSubtype\n                0x00,         // bcdNCMVersion\n                0x01,         // |\n                0,            // bmNetworkCapabilities\n            ],\n        );\n\n        let comm_ep = alt.endpoint_interrupt_in(None, 8, 255);\n\n        // Data interface\n        let mut iface = func.interface();\n        let data_if = iface.interface_number();\n        let _alt = iface.alt_setting(USB_CLASS_CDC_DATA, 0x00, CDC_PROTOCOL_NTB, None);\n        let mut alt = iface.alt_setting(USB_CLASS_CDC_DATA, 0x00, CDC_PROTOCOL_NTB, None);\n        let read_ep = alt.endpoint_bulk_out(None, max_packet_size);\n        let write_ep = alt.endpoint_bulk_in(None, max_packet_size);\n\n        drop(func);\n\n        let control = state.control.write(Control {\n            mac_addr_string,\n            shared: &state.shared,\n            mac_addr_str: [0; 12],\n            comm_if,\n            data_if,\n        });\n        builder.handler(control);\n\n        CdcNcmClass {\n            _comm_if: comm_if,\n            comm_ep,\n            data_if,\n            read_ep,\n            write_ep,\n            _control: &state.shared,\n            max_packet_size: max_packet_size as usize,\n        }\n    }\n\n    /// Split the class into a sender and receiver.\n    ///\n    /// This allows concurrently sending and receiving packets from separate tasks.\n    pub fn split(self) -> (Sender<'d, D>, Receiver<'d, D>) {\n        (\n            Sender {\n                write_ep: self.write_ep,\n                seq: 0,\n                max_packet_size: self.max_packet_size,\n            },\n            Receiver {\n                data_if: self.data_if,\n                comm_ep: self.comm_ep,\n                read_ep: self.read_ep,\n            },\n        )\n    }\n}\n\n/// CDC NCM class packet sender.\n///\n/// You can obtain a `Sender` with [`CdcNcmClass::split`]\npub struct Sender<'d, D: Driver<'d>> {\n    write_ep: D::EndpointIn,\n    seq: u16,\n    max_packet_size: usize,\n}\n\nimpl<'d, D: Driver<'d>> Sender<'d, D> {\n    /// Write a packet.\n    ///\n    /// This waits until the packet is successfully stored in the CDC-NCM endpoint buffers.\n    pub async fn write_packet(&mut self, data: &[u8]) -> Result<(), EndpointError> {\n        const OUT_HEADER_LEN: usize = 28;\n        const ABS_MAX_PACKET_SIZE: usize = 512;\n\n        let seq = self.seq;\n        self.seq = self.seq.wrapping_add(1);\n\n        let header = NtbOutHeader {\n            nth_sig: SIG_NTH,\n            nth_len: 0x0c,\n            nth_seq: seq,\n            nth_total_len: (data.len() + OUT_HEADER_LEN) as u16,\n            nth_first_index: 0x0c,\n\n            ndp_sig: SIG_NDP_NO_FCS,\n            ndp_len: 0x10,\n            ndp_next_index: 0x00,\n            ndp_datagram_index: OUT_HEADER_LEN as u16,\n            ndp_datagram_len: data.len() as u16,\n            ndp_term1: 0x00,\n            ndp_term2: 0x00,\n        };\n\n        // Build first packet on a buffer, send next packets straight from `data`.\n        let mut buf = [0; ABS_MAX_PACKET_SIZE];\n        let n = byteify(&mut buf, header);\n        assert_eq!(n.len(), OUT_HEADER_LEN);\n\n        if OUT_HEADER_LEN + data.len() < self.max_packet_size {\n            // First packet is not full, just send it.\n            // No need to send ZLP because it's short for sure.\n            buf[OUT_HEADER_LEN..][..data.len()].copy_from_slice(data);\n            self.write_ep.write(&buf[..OUT_HEADER_LEN + data.len()]).await?;\n        } else {\n            let (d1, d2) = data.split_at(self.max_packet_size - OUT_HEADER_LEN);\n\n            buf[OUT_HEADER_LEN..self.max_packet_size].copy_from_slice(d1);\n            self.write_ep.write(&buf[..self.max_packet_size]).await?;\n\n            for chunk in d2.chunks(self.max_packet_size) {\n                self.write_ep.write(chunk).await?;\n            }\n\n            // Send ZLP if needed.\n            if d2.len() % self.max_packet_size == 0 {\n                self.write_ep.write(&[]).await?;\n            }\n        }\n\n        Ok(())\n    }\n}\n\n/// CDC NCM class packet receiver.\n///\n/// You can obtain a `Receiver` with [`CdcNcmClass::split`]\npub struct Receiver<'d, D: Driver<'d>> {\n    data_if: InterfaceNumber,\n    comm_ep: D::EndpointIn,\n    read_ep: D::EndpointOut,\n}\n\nimpl<'d, D: Driver<'d>> Receiver<'d, D> {\n    /// Write a network packet.\n    ///\n    /// This waits until a packet is successfully received from the endpoint buffers.\n    pub async fn read_packet(&mut self, buf: &mut [u8]) -> Result<usize, EndpointError> {\n        // Retry loop\n        loop {\n            // read NTB\n            let mut ntb = [0u8; NTB_MAX_SIZE];\n            let mut pos = 0;\n            loop {\n                let n = self.read_ep.read(&mut ntb[pos..]).await?;\n                pos += n;\n                if n < self.read_ep.info().max_packet_size as usize || pos == NTB_MAX_SIZE {\n                    break;\n                }\n            }\n\n            let ntb = &ntb[..pos];\n\n            // Process NTB header (NTH)\n            let Some(nth) = ntb.get(..12) else {\n                warn!(\"Received too short NTB\");\n                continue;\n            };\n            let sig = u32::from_le_bytes(nth[0..4].try_into().unwrap());\n            if sig != SIG_NTH {\n                warn!(\"Received bad NTH sig.\");\n                continue;\n            }\n            let ndp_idx = u16::from_le_bytes(nth[10..12].try_into().unwrap()) as usize;\n\n            // Process NTB Datagram Pointer (NDP)\n            let Some(ndp) = ntb.get(ndp_idx..ndp_idx + 12) else {\n                warn!(\"NTH has an NDP pointer out of range.\");\n                continue;\n            };\n            let sig = u32::from_le_bytes(ndp[0..4].try_into().unwrap());\n            if sig != SIG_NDP_NO_FCS && sig != SIG_NDP_WITH_FCS {\n                warn!(\"Received bad NDP sig.\");\n                continue;\n            }\n            let datagram_index = u16::from_le_bytes(ndp[8..10].try_into().unwrap()) as usize;\n            let datagram_len = u16::from_le_bytes(ndp[10..12].try_into().unwrap()) as usize;\n\n            if datagram_index == 0 || datagram_len == 0 {\n                // empty, ignore. This is allowed by the spec, so don't warn.\n                continue;\n            }\n\n            // Process actual datagram, finally.\n            let Some(datagram) = ntb.get(datagram_index..datagram_index + datagram_len) else {\n                warn!(\"NDP has a datagram pointer out of range.\");\n                continue;\n            };\n            buf[..datagram_len].copy_from_slice(datagram);\n\n            return Ok(datagram_len);\n        }\n    }\n\n    /// Waits for the USB host to enable this interface\n    pub async fn wait_connection(&mut self) -> Result<(), EndpointError> {\n        loop {\n            self.read_ep.wait_enabled().await;\n            self.comm_ep.wait_enabled().await;\n\n            let buf = [\n                0xA1, //bmRequestType\n                0x00, //bNotificationType = NETWORK_CONNECTION\n                0x01, // wValue = connected\n                0x00,\n                self.data_if.into(), // wIndex = interface\n                0x00,\n                0x00, // wLength\n                0x00,\n            ];\n            match self.comm_ep.write(&buf).await {\n                Ok(()) => break,                   // Done!\n                Err(EndpointError::Disabled) => {} // Got disabled again, wait again.\n                Err(e) => return Err(e),\n            }\n        }\n\n        Ok(())\n    }\n}\n"
  },
  {
    "path": "embassy-usb/src/class/cmsis_dap_v2.rs",
    "content": "//! CMSIS-DAP V2 class implementation.\n\nuse core::mem::MaybeUninit;\n\nuse crate::driver::{Driver, Endpoint, EndpointError, EndpointIn, EndpointOut};\nuse crate::types::StringIndex;\nuse crate::{Builder, Handler, msos};\n\n/// State for the CMSIS-DAP v2 USB class.\npub struct State {\n    control: MaybeUninit<Control>,\n}\n\nstruct Control {\n    iface_string: StringIndex,\n}\n\nimpl Handler for Control {\n    fn get_string(&mut self, index: StringIndex, _lang_id: u16) -> Option<&str> {\n        if index == self.iface_string {\n            Some(\"CMSIS-DAP v2 Interface\")\n        } else {\n            warn!(\"unknown string index requested\");\n            None\n        }\n    }\n}\n\nimpl State {\n    /// Create a new `State`.\n    pub const fn new() -> Self {\n        Self {\n            control: MaybeUninit::uninit(),\n        }\n    }\n}\n\n/// USB device class for CMSIS-DAP v2 probes.\npub struct CmsisDapV2Class<'d, D: Driver<'d>> {\n    read_ep: D::EndpointOut,\n    write_ep: D::EndpointIn,\n    trace_ep: Option<D::EndpointIn>,\n    max_packet_size: u16,\n}\n\nimpl<'d, D: Driver<'d>> CmsisDapV2Class<'d, D> {\n    /// Creates a new CmsisDapV2Class with the provided UsbBus and `max_packet_size` in bytes. For\n    /// full-speed devices, `max_packet_size` has to be 64.\n    ///\n    /// The `trace` parameter enables the trace output endpoint. This is optional and can be\n    /// disabled if the probe does not support trace output.\n    pub fn new(builder: &mut Builder<'d, D>, state: &'d mut State, max_packet_size: u16, trace: bool) -> Self {\n        // DAP - Custom Class 0\n        let iface_string = builder.string();\n        let mut function = builder.function(0xFF, 0, 0);\n        function.msos_feature(msos::CompatibleIdFeatureDescriptor::new(\"WINUSB\", \"\"));\n        function.msos_feature(msos::RegistryPropertyFeatureDescriptor::new(\n            \"DeviceInterfaceGUIDs\",\n            // CMSIS-DAP standard GUID, from https://arm-software.github.io/CMSIS_5/DAP/html/group__DAP__ConfigUSB__gr.html\n            msos::PropertyData::RegMultiSz(&[\"{CDB3B5AD-293B-4663-AA36-1AAE46463776}\"]),\n        ));\n        let mut interface = function.interface();\n        let mut alt = interface.alt_setting(0xFF, 0, 0, Some(iface_string));\n        let read_ep = alt.endpoint_bulk_out(None, max_packet_size);\n        let write_ep = alt.endpoint_bulk_in(None, max_packet_size);\n        let trace_ep = if trace {\n            Some(alt.endpoint_bulk_in(None, max_packet_size))\n        } else {\n            None\n        };\n        drop(function);\n\n        builder.handler(state.control.write(Control { iface_string }));\n\n        CmsisDapV2Class {\n            read_ep,\n            write_ep,\n            trace_ep,\n            max_packet_size,\n        }\n    }\n\n    /// Waits for the USB host to enable this interface\n    pub async fn wait_connection(&mut self) {\n        self.read_ep.wait_enabled().await;\n    }\n\n    /// Write data to the host.\n    pub async fn write_packet(&mut self, data: &[u8]) -> Result<(), EndpointError> {\n        for chunk in data.chunks(self.max_packet_size as usize) {\n            self.write_ep.write(chunk).await?;\n        }\n        if data.len() % self.max_packet_size as usize == 0 {\n            self.write_ep.write(&[]).await?;\n        }\n        Ok(())\n    }\n\n    /// Write data to the host via the trace output endpoint.\n    ///\n    /// Returns `EndpointError::Disabled` if the trace output endpoint is not enabled.\n    pub async fn write_trace(&mut self, data: &[u8]) -> Result<(), EndpointError> {\n        let Some(ep) = self.trace_ep.as_mut() else {\n            return Err(EndpointError::Disabled);\n        };\n\n        for chunk in data.chunks(self.max_packet_size as usize) {\n            ep.write(chunk).await?;\n        }\n        if data.len() % self.max_packet_size as usize == 0 {\n            ep.write(&[]).await?;\n        }\n        Ok(())\n    }\n\n    /// Read data from the host.\n    pub async fn read_packet(&mut self, data: &mut [u8]) -> Result<usize, EndpointError> {\n        let mut n = 0;\n\n        loop {\n            let i = self.read_ep.read(&mut data[n..]).await?;\n            n += i;\n            if i < self.max_packet_size as usize {\n                return Ok(n);\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "embassy-usb/src/class/dfu/app_mode.rs",
    "content": "use embassy_time::{Duration, Instant};\nuse embassy_usb_driver::Driver;\n\nuse super::consts::{\n    APPN_SPEC_SUBCLASS_DFU, DESC_DFU_FUNCTIONAL, DFU_PROTOCOL_RT, DfuAttributes, Request, State, Status,\n    USB_CLASS_APPN_SPEC,\n};\nuse crate::control::{InResponse, OutResponse, Recipient, Request as ControlRequest, RequestType};\nuse crate::{Builder, FunctionBuilder};\n\n/// Handler trait for DFU runtime mode.\n///\n/// Implement this trait to handle entering DFU mode.\npub trait Handler {\n    /// Called when the device should enter DFU mode.\n    ///\n    /// This is called after a valid detach sequence (detach request followed by\n    /// USB reset within the timeout period). The implementation should mark the\n    /// device for DFU mode and perform a system reset.\n    fn enter_dfu(&mut self);\n}\n\n/// Internal state for the DFU class\npub struct DfuState<H: Handler> {\n    handler: H,\n    state: State,\n    attrs: DfuAttributes,\n    detach_start: Option<Instant>,\n    timeout: Duration,\n}\n\nimpl<H: Handler> DfuState<H> {\n    /// Create a new DFU instance to expose a DFU interface.\n    pub fn new(handler: H, attrs: DfuAttributes, timeout: Duration) -> Self {\n        DfuState {\n            handler,\n            state: State::AppIdle,\n            attrs,\n            detach_start: None,\n            timeout,\n        }\n    }\n\n    /// Get a mutable reference to the handler.\n    pub fn handler_mut(&mut self) -> &mut H {\n        &mut self.handler\n    }\n}\n\nimpl<H: Handler> crate::Handler for DfuState<H> {\n    fn reset(&mut self) {\n        if let Some(start) = self.detach_start {\n            let delta = Instant::now() - start;\n            trace!(\n                \"Received RESET with delta = {}, timeout = {}\",\n                delta.as_millis(),\n                self.timeout.as_millis()\n            );\n            if delta < self.timeout {\n                self.handler.enter_dfu();\n            }\n        }\n    }\n\n    fn control_out(&mut self, req: ControlRequest, _: &[u8]) -> Option<OutResponse> {\n        if (req.request_type, req.recipient) != (RequestType::Class, Recipient::Interface) {\n            return None;\n        }\n\n        trace!(\"Received out request {:?}\", req);\n\n        match Request::try_from(req.request) {\n            Ok(Request::Detach) => {\n                trace!(\"Received DETACH\");\n                self.state = State::AppDetach;\n                self.detach_start = Some(Instant::now());\n                if self.attrs.contains(DfuAttributes::WILL_DETACH) {\n                    trace!(\"WILL_DETACH set, performing reset\");\n                    self.handler.enter_dfu();\n                } else {\n                    trace!(\"Awaiting USB reset\");\n                }\n                Some(OutResponse::Accepted)\n            }\n            _ => None,\n        }\n    }\n\n    fn control_in<'a>(&'a mut self, req: ControlRequest, buf: &'a mut [u8]) -> Option<InResponse<'a>> {\n        if (req.request_type, req.recipient) != (RequestType::Class, Recipient::Interface) {\n            return None;\n        }\n\n        trace!(\"Received in request {:?}\", req);\n\n        match Request::try_from(req.request) {\n            Ok(Request::GetStatus) => {\n                let timeout_ms = self.timeout.as_millis() as u16;\n                buf[0..6].copy_from_slice(&[\n                    Status::Ok as u8,\n                    (timeout_ms & 0xff) as u8,\n                    ((timeout_ms >> 8) & 0xff) as u8,\n                    0x00,\n                    self.state as u8,\n                    0x00,\n                ]);\n                Some(InResponse::Accepted(buf))\n            }\n            _ => None,\n        }\n    }\n}\n\n/// An implementation of the USB DFU 1.1 runtime protocol\n///\n/// This function will add a DFU interface descriptor to the provided Builder, and register the provided Control as a handler for the USB device. The USB builder can be used as normal once this is complete.\n/// The handler is responsive to DFU GetStatus and Detach commands.\n///\n/// Once a detach command, followed by a USB reset is received by the host, a magic number will be written into the bootloader state partition to indicate that\n/// it should expose a DFU device, and a software reset will be issued.\n///\n/// To apply USB DFU updates, the bootloader must be capable of recognizing the DFU magic and exposing a device to handle the full DFU transaction with the host.\npub fn usb_dfu<'d, D: Driver<'d>, H: Handler>(\n    builder: &mut Builder<'d, D>,\n    state: &'d mut DfuState<H>,\n    func_modifier: impl Fn(&mut FunctionBuilder<'_, 'd, D>),\n) {\n    let mut func = builder.function(0x00, 0x00, 0x00);\n\n    // Here we give users the opportunity to add their own function level MSOS headers for instance.\n    // This is useful when DFU functionality is part of a composite USB device.\n    func_modifier(&mut func);\n\n    let timeout_ms = state.timeout.as_millis() as u16;\n    let mut iface = func.interface();\n    let mut alt = iface.alt_setting(USB_CLASS_APPN_SPEC, APPN_SPEC_SUBCLASS_DFU, DFU_PROTOCOL_RT, None);\n    alt.descriptor(\n        DESC_DFU_FUNCTIONAL,\n        &[\n            state.attrs.bits(),\n            (timeout_ms & 0xff) as u8,\n            ((timeout_ms >> 8) & 0xff) as u8,\n            0x40,\n            0x00, // 64B control buffer size for application side\n            0x10,\n            0x01, // DFU 1.1\n        ],\n    );\n\n    drop(func);\n    builder.handler(state);\n}\n"
  },
  {
    "path": "embassy-usb/src/class/dfu/consts.rs",
    "content": "//! USB DFU constants and types.\n\npub(crate) const USB_CLASS_APPN_SPEC: u8 = 0xFE;\npub(crate) const APPN_SPEC_SUBCLASS_DFU: u8 = 0x01;\n#[allow(unused)]\npub(crate) const DFU_PROTOCOL_DFU: u8 = 0x02;\n#[allow(unused)]\npub(crate) const DFU_PROTOCOL_RT: u8 = 0x01;\npub(crate) const DESC_DFU_FUNCTIONAL: u8 = 0x21;\n\n#[cfg(feature = \"defmt\")]\ndefmt::bitflags! {\n    /// Attributes supported by the DFU controller.\n    pub struct DfuAttributes: u8 {\n        /// Generate WillDetach sequence on bus.\n        const WILL_DETACH = 0b0000_1000;\n        /// Device can communicate during manifestation phase.\n        const MANIFESTATION_TOLERANT = 0b0000_0100;\n        /// Capable of upload.\n        const CAN_UPLOAD = 0b0000_0010;\n        /// Capable of download.\n        const CAN_DOWNLOAD = 0b0000_0001;\n    }\n}\n\n#[cfg(not(feature = \"defmt\"))]\nbitflags::bitflags! {\n    /// Attributes supported by the DFU controller.\n    pub struct DfuAttributes: u8 {\n        /// Generate WillDetach sequence on bus.\n        const WILL_DETACH = 0b0000_1000;\n        /// Device can communicate during manifestation phase.\n        const MANIFESTATION_TOLERANT = 0b0000_0100;\n        /// Capable of upload.\n        const CAN_UPLOAD = 0b0000_0010;\n        /// Capable of download.\n        const CAN_DOWNLOAD = 0b0000_0001;\n    }\n}\n\n#[derive(Copy, Clone, PartialEq, Eq)]\n#[repr(u8)]\n#[allow(unused)]\npub(crate) enum State {\n    AppIdle = 0,\n    AppDetach = 1,\n    DfuIdle = 2,\n    DlSync = 3,\n    DlBusy = 4,\n    Download = 5,\n    ManifestSync = 6,\n    Manifest = 7,\n    ManifestWaitReset = 8,\n    UploadIdle = 9,\n    Error = 10,\n}\n\n/// DFU status codes indicating the result of the most recent request.\n#[derive(Copy, Clone, PartialEq, Eq)]\n#[repr(u8)]\n#[allow(unused)]\npub enum Status {\n    /// No error.\n    Ok = 0x00,\n    /// File is not targeted for use by this device.\n    ErrTarget = 0x01,\n    /// File is for this device but fails some vendor-specific verification test.\n    ErrFile = 0x02,\n    /// Device is unable to write memory.\n    ErrWrite = 0x03,\n    /// Memory erase function failed.\n    ErrErase = 0x04,\n    /// Memory erase check failed.\n    ErrCheckErased = 0x05,\n    /// Program memory function failed.\n    ErrProg = 0x06,\n    /// Programmed memory failed verification.\n    ErrVerify = 0x07,\n    /// Cannot program memory due to received address that is out of range.\n    ErrAddress = 0x08,\n    /// Received DFU_DNLOAD with wLength = 0, but device does not think it has all of the data yet.\n    ErrNotDone = 0x09,\n    /// Device's firmware is corrupt. It cannot return to run-time (non-DFU) operations.\n    ErrFirmware = 0x0A,\n    /// iString indicates a vendor-specific error.\n    ErrVendor = 0x0B,\n    /// Device detected unexpected USB reset signaling.\n    ErrUsbr = 0x0C,\n    /// Device detected unexpected power on reset.\n    ErrPor = 0x0D,\n    /// Something went wrong, but the device does not know what.\n    ErrUnknown = 0x0E,\n    /// Device stalled an unexpected request.\n    ErrStalledPkt = 0x0F,\n}\n\n#[derive(Copy, Clone, PartialEq, Eq)]\n#[repr(u8)]\npub(crate) enum Request {\n    Detach = 0,\n    Dnload = 1,\n    Upload = 2,\n    GetStatus = 3,\n    ClrStatus = 4,\n    GetState = 5,\n    Abort = 6,\n}\n\nimpl TryFrom<u8> for Request {\n    type Error = ();\n\n    fn try_from(value: u8) -> Result<Self, Self::Error> {\n        match value {\n            0 => Ok(Request::Detach),\n            1 => Ok(Request::Dnload),\n            2 => Ok(Request::Upload),\n            3 => Ok(Request::GetStatus),\n            4 => Ok(Request::ClrStatus),\n            5 => Ok(Request::GetState),\n            6 => Ok(Request::Abort),\n            _ => Err(()),\n        }\n    }\n}\n"
  },
  {
    "path": "embassy-usb/src/class/dfu/dfu_mode.rs",
    "content": "use embassy_usb_driver::Driver;\n\nuse super::consts::{\n    APPN_SPEC_SUBCLASS_DFU, DESC_DFU_FUNCTIONAL, DFU_PROTOCOL_DFU, DfuAttributes, Request, State, Status,\n    USB_CLASS_APPN_SPEC,\n};\nuse crate::control::{InResponse, OutResponse, Recipient, Request as ControlRequest, RequestType};\nuse crate::{Builder, FunctionBuilder};\n\n/// Handler trait for DFU bootloader mode.\n///\n/// Implement this trait to handle firmware download operations.\npub trait Handler {\n    /// Called when a firmware download starts.\n    ///\n    /// This is called when the first DFU_DNLOAD request is received.\n    ///\n    /// Returns `Ok(())` on success, or a `Status` error on failure.\n    fn start(&mut self) -> Result<(), Status>;\n\n    /// Called to write a chunk of firmware data.\n    ///\n    /// Returns `Ok(())` on success, or a `Status` error on failure.\n    fn write(&mut self, data: &[u8]) -> Result<(), Status>;\n\n    /// Called when the firmware download is complete.\n    ///\n    /// This is called when a zero-length DFU_DNLOAD is received, indicating\n    /// the end of the firmware data. This is where you would typically\n    /// mark the firmware as ready to boot.\n    ///\n    /// Returns `Ok(())` on success, or a `Status` error on failure.\n    fn finish(&mut self) -> Result<(), Status>;\n\n    /// Called at the end of the DFU procedure.\n    ///\n    /// This is typically where you would perform a system reset to boot\n    /// the new firmware after a successful download.\n    fn system_reset(&mut self);\n}\n\n/// Internal state for USB DFU\npub struct DfuState<H: Handler> {\n    handler: H,\n    attrs: DfuAttributes,\n    state: State,\n    status: Status,\n    next_block_num: usize,\n}\n\nimpl<'d, H: Handler> DfuState<H> {\n    /// Create a new DFU instance to handle DFU transfers.\n    pub fn new(handler: H, attrs: DfuAttributes) -> Self {\n        Self {\n            handler,\n            attrs,\n            state: State::DfuIdle,\n            status: Status::Ok,\n            next_block_num: 0,\n        }\n    }\n\n    /// Set DFU instance into firmware error\n    ///\n    /// This is typically called when the stored firmware\n    /// is corrupt and can not be booted.\n    pub fn set_to_firmware_error(&mut self) {\n        self.reset_state();\n        self.state = State::Error;\n        self.status = Status::ErrFirmware;\n    }\n\n    fn reset_state(&mut self) {\n        self.next_block_num = 0;\n        self.state = State::DfuIdle;\n        self.status = Status::Ok;\n    }\n}\n\nimpl<H: Handler> crate::Handler for DfuState<H> {\n    fn reset(&mut self) {\n        if matches!(self.state, State::ManifestSync | State::Manifest) {\n            self.handler.system_reset();\n        }\n    }\n\n    fn control_out(&mut self, req: ControlRequest, data: &[u8]) -> Option<OutResponse> {\n        if (req.request_type, req.recipient) != (RequestType::Class, Recipient::Interface) {\n            return None;\n        }\n        match Request::try_from(req.request) {\n            Ok(Request::Abort) => {\n                info!(\"Abort requested\");\n                self.reset_state();\n                Some(OutResponse::Accepted)\n            }\n            Ok(Request::Dnload) if self.attrs.contains(DfuAttributes::CAN_DOWNLOAD) => {\n                if req.value as usize != self.next_block_num {\n                    error!(\"expected next block num {}, got {}\", self.next_block_num, req.value);\n                    self.state = State::Error;\n                    self.status = Status::ErrUnknown;\n                    return Some(OutResponse::Rejected);\n                }\n\n                if req.value == 0 {\n                    match self.handler.start() {\n                        Ok(_) => {\n                            self.state = State::Download;\n                        }\n                        Err(e) => {\n                            self.state = State::Error;\n                            self.status = e;\n                            return Some(OutResponse::Rejected);\n                        }\n                    }\n                }\n\n                if req.length == 0 {\n                    match self.handler.finish() {\n                        Ok(_) => {\n                            self.status = Status::Ok;\n                            self.state = State::ManifestSync;\n                        }\n                        Err(e) => {\n                            self.state = State::Error;\n                            self.status = e;\n                        }\n                    }\n                } else {\n                    if self.state != State::Download {\n                        error!(\"Unexpected DNLOAD while chip is waiting for a GETSTATUS\");\n                        self.status = Status::ErrUnknown;\n                        self.state = State::Error;\n                        return Some(OutResponse::Rejected);\n                    }\n                    match self.handler.write(data) {\n                        Ok(_) => {\n                            self.status = Status::Ok;\n                            self.state = State::DlSync;\n                            self.next_block_num += 1;\n                        }\n                        Err(e) => {\n                            self.state = State::Error;\n                            self.status = e;\n                        }\n                    }\n                }\n\n                Some(OutResponse::Accepted)\n            }\n            Ok(Request::Detach) => Some(OutResponse::Accepted), // Device is already in DFU mode\n            Ok(Request::ClrStatus) => {\n                info!(\"Clear status requested\");\n                self.reset_state();\n                Some(OutResponse::Accepted)\n            }\n            _ => {\n                debug!(\"Unknown OUT request {:?}\", req);\n                None\n            }\n        }\n    }\n\n    fn control_in<'a>(&'a mut self, req: ControlRequest, buf: &'a mut [u8]) -> Option<InResponse<'a>> {\n        if (req.request_type, req.recipient) != (RequestType::Class, Recipient::Interface) {\n            return None;\n        }\n        match Request::try_from(req.request) {\n            Ok(Request::GetStatus) => {\n                match self.state {\n                    State::DlSync => self.state = State::Download,\n                    State::ManifestSync if self.attrs.contains(DfuAttributes::MANIFESTATION_TOLERANT) => {\n                        self.state = State::DfuIdle\n                    }\n                    State::ManifestSync => {\n                        // Technically we would be in ManifestWaitReset after responding with\n                        // Manifest, but ManifestWaitReset isn't meant to be seen by the host\n                        // anyways.\n                        self.state = State::Manifest;\n                        if self.attrs.contains(DfuAttributes::WILL_DETACH) {\n                            self.reset();\n                        }\n                    }\n                    _ => {}\n                }\n                //TODO: Configurable poll timeout, ability to add string for Vendor error\n                buf[0..6].copy_from_slice(&[self.status as u8, 0x32, 0x00, 0x00, self.state as u8, 0x00]);\n                Some(InResponse::Accepted(&buf[0..6]))\n            }\n            Ok(Request::GetState) => {\n                buf[0] = self.state as u8;\n                Some(InResponse::Accepted(&buf[0..1]))\n            }\n            Ok(Request::Upload) if self.attrs.contains(DfuAttributes::CAN_UPLOAD) => {\n                //TODO: FirmwareUpdater does not provide a way of reading the active partition, can't upload.\n                Some(InResponse::Rejected)\n            }\n            _ => {\n                debug!(\"Unknown IN request {:?}\", req);\n                None\n            }\n        }\n    }\n}\n\n/// An implementation of the USB DFU 1.1 protocol\n///\n/// This function will add a DFU interface descriptor to the provided Builder, and register the provided Control as a handler for the USB device\n/// The handler is responsive to DFU GetState, GetStatus, Abort, and ClrStatus commands, as well as Download if configured by the user.\n///\n/// Once the host has initiated a DFU download operation, the chunks sent by the host will be written to the DFU partition.\n/// Once the final sync in the manifestation phase has been received, the handler will trigger a system reset to swap the new firmware.\npub fn usb_dfu<'d, D: Driver<'d>, H: Handler>(\n    builder: &mut Builder<'d, D>,\n    state: &'d mut DfuState<H>,\n    max_write_size: usize,\n    func_modifier: impl Fn(&mut FunctionBuilder<'_, 'd, D>),\n) {\n    let mut func = builder.function(0x00, 0x00, 0x00);\n\n    // Here we give users the opportunity to add their own function level MSOS headers for instance.\n    // This is useful when DFU functionality is part of a composite USB device.\n    func_modifier(&mut func);\n\n    let mut iface = func.interface();\n    let mut alt = iface.alt_setting(USB_CLASS_APPN_SPEC, APPN_SPEC_SUBCLASS_DFU, DFU_PROTOCOL_DFU, None);\n    alt.descriptor(\n        DESC_DFU_FUNCTIONAL,\n        &[\n            state.attrs.bits(),\n            0xc4,\n            0x09, // 2500ms timeout, doesn't affect operation as DETACH not necessary in bootloader code\n            (max_write_size & 0xff) as u8,\n            ((max_write_size & 0xff00) >> 8) as u8,\n            0x10,\n            0x01, // DFU 1.1\n        ],\n    );\n\n    drop(func);\n    builder.handler(state);\n}\n"
  },
  {
    "path": "embassy-usb/src/class/dfu/mod.rs",
    "content": "//! USB Device Firmware Upgrade (DFU) class implementation.\n//!\n//! This module provides USB DFU 1.1 protocol support, split into two modes:\n//! - `app_mode`: Runtime mode for applications to support detach requests\n//! - `dfu_mode`: Bootloader mode for handling firmware downloads\n\npub mod consts;\n\n/// DFU runtime mode (application side).\npub mod app_mode;\n/// DFU bootloader mode (firmware download).\npub mod dfu_mode;\n"
  },
  {
    "path": "embassy-usb/src/class/hid.rs",
    "content": "//! USB HID (Human Interface Device) class implementation.\n\nuse core::mem::MaybeUninit;\nuse core::ops::Range;\nuse core::sync::atomic::{AtomicUsize, Ordering};\n\n#[cfg(feature = \"usbd-hid\")]\nuse usbd_hid::descriptor::AsInputReport;\n\nuse crate::control::{InResponse, OutResponse, Recipient, Request, RequestType};\nuse crate::driver::{Driver, Endpoint, EndpointError, EndpointIn, EndpointOut};\nuse crate::types::InterfaceNumber;\nuse crate::{Builder, Handler};\n\nconst USB_CLASS_HID: u8 = 0x03;\n\n// HID\nconst HID_DESC_DESCTYPE_HID: u8 = 0x21;\nconst HID_DESC_DESCTYPE_HID_REPORT: u8 = 0x22;\nconst HID_DESC_SPEC_1_10: [u8; 2] = [0x10, 0x01];\nconst HID_DESC_COUNTRY_UNSPEC: u8 = 0x00;\n\nconst HID_REQ_SET_IDLE: u8 = 0x0a;\nconst HID_REQ_GET_IDLE: u8 = 0x02;\nconst HID_REQ_GET_REPORT: u8 = 0x01;\nconst HID_REQ_SET_REPORT: u8 = 0x09;\nconst HID_REQ_GET_PROTOCOL: u8 = 0x03;\nconst HID_REQ_SET_PROTOCOL: u8 = 0x0b;\n\n/// Get/Set Protocol mapping\n/// See (7.2.5 and 7.2.6): <https://www.usb.org/sites/default/files/hid1_11.pdf>\n#[derive(Copy, Clone, Debug, PartialEq, Eq)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\n#[repr(u8)]\npub enum HidProtocolMode {\n    /// Hid Boot Protocol Mode\n    Boot = 0,\n    /// Hid Report Protocol Mode\n    Report = 1,\n}\n\nimpl From<u8> for HidProtocolMode {\n    fn from(mode: u8) -> HidProtocolMode {\n        if mode == HidProtocolMode::Boot as u8 {\n            HidProtocolMode::Boot\n        } else {\n            HidProtocolMode::Report\n        }\n    }\n}\n\n/// USB HID interface subclass values.\n#[derive(Copy, Clone, Debug, PartialEq, Eq)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\n#[repr(u8)]\npub enum HidSubclass {\n    /// No subclass, standard HID device.\n    No = 0,\n    /// Boot interface subclass, supports BIOS boot protocol.\n    Boot = 1,\n}\n\n/// USB HID protocol values.\n#[derive(Copy, Clone, Debug, PartialEq, Eq)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\n#[repr(u8)]\npub enum HidBootProtocol {\n    /// No boot protocol.\n    None = 0,\n    /// Keyboard boot protocol.\n    Keyboard = 1,\n    /// Mouse boot protocol.\n    Mouse = 2,\n}\n\n/// Configuration for the HID class.\npub struct Config<'d> {\n    /// HID report descriptor.\n    pub report_descriptor: &'d [u8],\n\n    /// Handler for control requests.\n    pub request_handler: Option<&'d mut dyn RequestHandler>,\n\n    /// Configures how frequently the host should poll for reading/writing HID reports.\n    ///\n    /// A lower value means better throughput & latency, at the expense\n    /// of CPU on the device & bandwidth on the bus. A value of 10 is reasonable for\n    /// high performance uses, and a value of 255 is good for best-effort usecases.\n    pub poll_ms: u8,\n\n    /// Max packet size for both the IN and OUT endpoints.\n    pub max_packet_size: u16,\n\n    /// The HID subclass of this interface\n    pub hid_subclass: HidSubclass,\n\n    /// The HID boot protocol of this interface\n    pub hid_boot_protocol: HidBootProtocol,\n}\n\n/// Report ID\n#[derive(Debug, Clone, Copy, PartialEq, Eq)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub enum ReportId {\n    /// IN report\n    In(u8),\n    /// OUT report\n    Out(u8),\n    /// Feature report\n    Feature(u8),\n}\n\nimpl ReportId {\n    const fn try_from(value: u16) -> Result<Self, ()> {\n        match value >> 8 {\n            1 => Ok(ReportId::In(value as u8)),\n            2 => Ok(ReportId::Out(value as u8)),\n            3 => Ok(ReportId::Feature(value as u8)),\n            _ => Err(()),\n        }\n    }\n}\n\n/// Internal state for USB HID.\npub struct State<'d> {\n    control: MaybeUninit<Control<'d>>,\n    out_report_offset: AtomicUsize,\n}\n\nimpl<'d> Default for State<'d> {\n    fn default() -> Self {\n        Self::new()\n    }\n}\n\nimpl<'d> State<'d> {\n    /// Create a new `State`.\n    pub const fn new() -> Self {\n        State {\n            control: MaybeUninit::uninit(),\n            out_report_offset: AtomicUsize::new(0),\n        }\n    }\n}\n\n/// USB HID reader/writer.\npub struct HidReaderWriter<'d, D: Driver<'d>, const READ_N: usize, const WRITE_N: usize> {\n    reader: HidReader<'d, D, READ_N>,\n    writer: HidWriter<'d, D, WRITE_N>,\n}\n\nfn build<'d, D: Driver<'d>>(\n    builder: &mut Builder<'d, D>,\n    state: &'d mut State<'d>,\n    config: Config<'d>,\n    with_out_endpoint: bool,\n) -> (Option<D::EndpointOut>, D::EndpointIn, &'d AtomicUsize) {\n    let len = config.report_descriptor.len();\n\n    let mut func = builder.function(USB_CLASS_HID, config.hid_subclass as u8, config.hid_boot_protocol as u8);\n    let mut iface = func.interface();\n    let if_num = iface.interface_number();\n    let mut alt = iface.alt_setting(\n        USB_CLASS_HID,\n        config.hid_subclass as u8,\n        config.hid_boot_protocol as u8,\n        None,\n    );\n\n    // HID descriptor\n    alt.descriptor(\n        HID_DESC_DESCTYPE_HID,\n        &[\n            // HID Class spec version\n            HID_DESC_SPEC_1_10[0],\n            HID_DESC_SPEC_1_10[1],\n            // Country code not supported\n            HID_DESC_COUNTRY_UNSPEC,\n            // Number of following descriptors\n            1,\n            // We have a HID report descriptor the host should read\n            HID_DESC_DESCTYPE_HID_REPORT,\n            // HID report descriptor size,\n            (len & 0xFF) as u8,\n            (len >> 8 & 0xFF) as u8,\n        ],\n    );\n\n    let ep_in = alt.endpoint_interrupt_in(None, config.max_packet_size, config.poll_ms);\n    let ep_out = if with_out_endpoint {\n        Some(alt.endpoint_interrupt_out(None, config.max_packet_size, config.poll_ms))\n    } else {\n        None\n    };\n\n    drop(func);\n\n    let control = state.control.write(Control::new(\n        if_num,\n        config.report_descriptor,\n        config.request_handler,\n        &state.out_report_offset,\n    ));\n    builder.handler(control);\n\n    (ep_out, ep_in, &state.out_report_offset)\n}\n\nimpl<'d, D: Driver<'d>, const READ_N: usize, const WRITE_N: usize> HidReaderWriter<'d, D, READ_N, WRITE_N> {\n    /// Creates a new `HidReaderWriter`.\n    ///\n    /// This will allocate one IN and one OUT endpoints. If you only need writing (sending)\n    /// HID reports, consider using [`HidWriter::new`] instead, which allocates an IN endpoint only.\n    ///\n    pub fn new(builder: &mut Builder<'d, D>, state: &'d mut State<'d>, config: Config<'d>) -> Self {\n        let (ep_out, ep_in, offset) = build(builder, state, config, true);\n\n        Self {\n            reader: HidReader {\n                ep_out: ep_out.unwrap(),\n                offset,\n            },\n            writer: HidWriter { ep_in },\n        }\n    }\n\n    /// Splits into separate readers/writers for input and output reports.\n    pub fn split(self) -> (HidReader<'d, D, READ_N>, HidWriter<'d, D, WRITE_N>) {\n        (self.reader, self.writer)\n    }\n\n    /// Waits for both IN and OUT endpoints to be enabled.\n    pub async fn ready(&mut self) {\n        self.reader.ready().await;\n        self.writer.ready().await;\n    }\n\n    /// Writes an input report by serializing the given report structure.\n    #[cfg(feature = \"usbd-hid\")]\n    pub async fn write_serialize<IR: AsInputReport>(&mut self, r: &IR) -> Result<(), EndpointError> {\n        self.writer.write_serialize(r).await\n    }\n\n    /// Writes `report` to its interrupt endpoint.\n    pub async fn write(&mut self, report: &[u8]) -> Result<(), EndpointError> {\n        self.writer.write(report).await\n    }\n\n    /// Reads an output report from the Interrupt Out pipe.\n    ///\n    /// See [`HidReader::read`].\n    pub async fn read(&mut self, buf: &mut [u8]) -> Result<usize, ReadError> {\n        self.reader.read(buf).await\n    }\n}\n\n/// USB HID writer.\n///\n/// You can obtain a `HidWriter` using [`HidReaderWriter::split`].\npub struct HidWriter<'d, D: Driver<'d>, const N: usize> {\n    ep_in: D::EndpointIn,\n}\n\n/// USB HID reader.\n///\n/// You can obtain a `HidReader` using [`HidReaderWriter::split`].\npub struct HidReader<'d, D: Driver<'d>, const N: usize> {\n    ep_out: D::EndpointOut,\n    offset: &'d AtomicUsize,\n}\n\n/// Error when reading a HID report.\n#[derive(Debug, Clone, PartialEq, Eq)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub enum ReadError {\n    /// The given buffer was too small to read the received report.\n    BufferOverflow,\n    /// The endpoint is disabled.\n    Disabled,\n    /// The report was only partially read. See [`HidReader::read`] for details.\n    Sync(Range<usize>),\n}\n\nimpl From<EndpointError> for ReadError {\n    fn from(val: EndpointError) -> Self {\n        use EndpointError::{BufferOverflow, Disabled};\n        match val {\n            BufferOverflow => ReadError::BufferOverflow,\n            Disabled => ReadError::Disabled,\n        }\n    }\n}\n\nimpl<'d, D: Driver<'d>, const N: usize> HidWriter<'d, D, N> {\n    /// Creates a new HidWriter.\n    ///\n    /// This will allocate one IN endpoint only, so the host won't be able to send\n    /// reports to us. If you need that, consider using [`HidReaderWriter::new`] instead.\n    ///\n    /// poll_ms configures how frequently the host should poll for reading/writing\n    /// HID reports. A lower value means better throughput & latency, at the expense\n    /// of CPU on the device & bandwidth on the bus. A value of 10 is reasonable for\n    /// high performance uses, and a value of 255 is good for best-effort usecases.\n    pub fn new(builder: &mut Builder<'d, D>, state: &'d mut State<'d>, config: Config<'d>) -> Self {\n        let (ep_out, ep_in, _offset) = build(builder, state, config, false);\n\n        assert!(ep_out.is_none());\n\n        Self { ep_in }\n    }\n\n    /// Waits for the interrupt in endpoint to be enabled.\n    pub async fn ready(&mut self) {\n        self.ep_in.wait_enabled().await;\n    }\n\n    /// Writes an input report by serializing the given report structure.\n    #[cfg(feature = \"usbd-hid\")]\n    pub async fn write_serialize<IR: AsInputReport>(&mut self, r: &IR) -> Result<(), EndpointError> {\n        let mut buf: [u8; N] = [0; N];\n        let Ok(size) = r.serialize(&mut buf) else {\n            return Err(EndpointError::BufferOverflow);\n        };\n        self.write(&buf[0..size]).await\n    }\n\n    /// Writes `report` to its interrupt endpoint.\n    pub async fn write(&mut self, report: &[u8]) -> Result<(), EndpointError> {\n        assert!(report.len() <= N);\n\n        let max_packet_size = usize::from(self.ep_in.info().max_packet_size);\n        let zlp_needed = report.len() < N && (report.len() % max_packet_size == 0);\n        for chunk in report.chunks(max_packet_size) {\n            self.ep_in.write(chunk).await?;\n        }\n\n        if zlp_needed {\n            self.ep_in.write(&[]).await?;\n        }\n\n        Ok(())\n    }\n}\n\nimpl<'d, D: Driver<'d>, const N: usize> HidReader<'d, D, N> {\n    /// Waits for the interrupt out endpoint to be enabled.\n    pub async fn ready(&mut self) {\n        self.ep_out.wait_enabled().await;\n    }\n\n    /// Delivers output reports from the Interrupt Out pipe to `handler`.\n    ///\n    /// If `use_report_ids` is true, the first byte of the report will be used as\n    /// the `ReportId` value. Otherwise the `ReportId` value will be 0.\n    pub async fn run<T: RequestHandler>(mut self, use_report_ids: bool, handler: &mut T) -> ! {\n        let offset = self.offset.load(Ordering::Acquire);\n        assert!(offset == 0);\n        let mut buf = [0; N];\n        loop {\n            match self.read(&mut buf).await {\n                Ok(len) => {\n                    let id = if use_report_ids { buf[0] } else { 0 };\n                    handler.set_report(ReportId::Out(id), &buf[..len]);\n                }\n                Err(ReadError::BufferOverflow) => warn!(\n                    \"Host sent output report larger than the configured maximum output report length ({})\",\n                    N\n                ),\n                Err(ReadError::Disabled) => self.ep_out.wait_enabled().await,\n                Err(ReadError::Sync(_)) => unreachable!(),\n            }\n        }\n    }\n\n    /// Reads an output report from the Interrupt Out pipe.\n    ///\n    /// **Note:** Any reports sent from the host over the control pipe will be\n    /// passed to [`RequestHandler::set_report()`] for handling. The application\n    /// is responsible for ensuring output reports from both pipes are handled\n    /// correctly.\n    ///\n    /// **Note:** If `N` > the maximum packet size of the endpoint (i.e. output\n    /// reports may be split across multiple packets) and this method's future\n    /// is dropped after some packets have been read, the next call to `read()`\n    /// will return a [`ReadError::Sync`]. The range in the sync error\n    /// indicates the portion `buf` that was filled by the current call to\n    /// `read()`. If the dropped future used the same `buf`, then `buf` will\n    /// contain the full report.\n    pub async fn read(&mut self, buf: &mut [u8]) -> Result<usize, ReadError> {\n        assert!(N != 0);\n        assert!(buf.len() >= N);\n\n        // Read packets from the endpoint\n        let max_packet_size = usize::from(self.ep_out.info().max_packet_size);\n        let starting_offset = self.offset.load(Ordering::Acquire);\n        let mut total = starting_offset;\n        loop {\n            for chunk in buf[starting_offset..N].chunks_mut(max_packet_size) {\n                match self.ep_out.read(chunk).await {\n                    Ok(size) => {\n                        total += size;\n                        if size < max_packet_size || total == N {\n                            self.offset.store(0, Ordering::Release);\n                            break;\n                        }\n                        self.offset.store(total, Ordering::Release);\n                    }\n                    Err(err) => {\n                        self.offset.store(0, Ordering::Release);\n                        return Err(err.into());\n                    }\n                }\n            }\n\n            // Some hosts may send ZLPs even when not required by the HID spec, so we'll loop as long as total == 0.\n            if total > 0 {\n                break;\n            }\n        }\n\n        if starting_offset > 0 {\n            Err(ReadError::Sync(starting_offset..total))\n        } else {\n            Ok(total)\n        }\n    }\n}\n\n/// Handler for HID-related control requests.\npub trait RequestHandler {\n    /// Reads the value of report `id` into `buf` returning the size.\n    ///\n    /// Returns `None` if `id` is invalid or no data is available.\n    fn get_report(&mut self, id: ReportId, buf: &mut [u8]) -> Option<usize> {\n        let _ = (id, buf);\n        None\n    }\n\n    /// Sets the value of report `id` to `data`.\n    fn set_report(&mut self, id: ReportId, data: &[u8]) -> OutResponse {\n        let _ = (id, data);\n        OutResponse::Rejected\n    }\n\n    /// Gets the current hid protocol.\n    ///\n    /// Returns `Report` protocol by default.\n    fn get_protocol(&self) -> HidProtocolMode {\n        HidProtocolMode::Report\n    }\n\n    /// Sets the current hid protocol to `protocol`.\n    ///\n    /// Accepts only `Report` protocol by default.\n    fn set_protocol(&mut self, protocol: HidProtocolMode) -> OutResponse {\n        match protocol {\n            HidProtocolMode::Report => OutResponse::Accepted,\n            HidProtocolMode::Boot => OutResponse::Rejected,\n        }\n    }\n\n    /// Get the idle rate for `id`.\n    ///\n    /// If `id` is `None`, get the idle rate for all reports. Returning `None`\n    /// will reject the control request. Any duration at or above 1.024 seconds\n    /// or below 4ms will be returned as an indefinite idle rate.\n    fn get_idle_ms(&mut self, id: Option<ReportId>) -> Option<u32> {\n        let _ = id;\n        None\n    }\n\n    /// Set the idle rate for `id` to `dur`.\n    ///\n    /// If `id` is `None`, set the idle rate of all input reports to `dur`. If\n    /// an indefinite duration is requested, `dur` will be set to `u32::MAX`.\n    fn set_idle_ms(&mut self, id: Option<ReportId>, duration_ms: u32) {\n        let _ = (id, duration_ms);\n    }\n}\n\nstruct Control<'d> {\n    if_num: InterfaceNumber,\n    report_descriptor: &'d [u8],\n    request_handler: Option<&'d mut dyn RequestHandler>,\n    out_report_offset: &'d AtomicUsize,\n    hid_descriptor: [u8; 9],\n}\n\nimpl<'d> Control<'d> {\n    fn new(\n        if_num: InterfaceNumber,\n        report_descriptor: &'d [u8],\n        request_handler: Option<&'d mut dyn RequestHandler>,\n        out_report_offset: &'d AtomicUsize,\n    ) -> Self {\n        Control {\n            if_num,\n            report_descriptor,\n            request_handler,\n            out_report_offset,\n            hid_descriptor: [\n                // Length of buf inclusive of size prefix\n                9,\n                // Descriptor type\n                HID_DESC_DESCTYPE_HID,\n                // HID Class spec version\n                HID_DESC_SPEC_1_10[0],\n                HID_DESC_SPEC_1_10[1],\n                // Country code not supported\n                HID_DESC_COUNTRY_UNSPEC,\n                // Number of following descriptors\n                1,\n                // We have a HID report descriptor the host should read\n                HID_DESC_DESCTYPE_HID_REPORT,\n                // HID report descriptor size,\n                (report_descriptor.len() & 0xFF) as u8,\n                (report_descriptor.len() >> 8 & 0xFF) as u8,\n            ],\n        }\n    }\n}\n\nimpl<'d> Handler for Control<'d> {\n    fn reset(&mut self) {\n        self.out_report_offset.store(0, Ordering::Release);\n    }\n\n    fn control_out(&mut self, req: Request, data: &[u8]) -> Option<OutResponse> {\n        if (req.request_type, req.recipient, req.index)\n            != (RequestType::Class, Recipient::Interface, self.if_num.0 as u16)\n        {\n            return None;\n        }\n\n        // This uses a defmt-specific formatter that causes use of the `log`\n        // feature to fail to build, so leave it defmt-specific for now.\n        #[cfg(feature = \"defmt\")]\n        trace!(\"HID control_out {:?} {=[u8]:x}\", req, data);\n        match req.request {\n            HID_REQ_SET_IDLE => {\n                if let Some(handler) = self.request_handler.as_mut() {\n                    let id = req.value as u8;\n                    let id = (id != 0).then_some(ReportId::In(id));\n                    let dur = u32::from(req.value >> 8);\n                    let dur = if dur == 0 { u32::MAX } else { 4 * dur };\n                    handler.set_idle_ms(id, dur);\n                }\n                Some(OutResponse::Accepted)\n            }\n            HID_REQ_SET_REPORT => match (ReportId::try_from(req.value), self.request_handler.as_mut()) {\n                (Ok(id), Some(handler)) => Some(handler.set_report(id, data)),\n                _ => Some(OutResponse::Rejected),\n            },\n            HID_REQ_SET_PROTOCOL => {\n                let hid_protocol = HidProtocolMode::from(req.value as u8);\n                match (self.request_handler.as_mut(), hid_protocol) {\n                    (Some(request_handler), hid_protocol) => Some(request_handler.set_protocol(hid_protocol)),\n                    (None, HidProtocolMode::Report) => Some(OutResponse::Accepted),\n                    (None, HidProtocolMode::Boot) => {\n                        info!(\"Received request to switch to Boot protocol mode, but it is disabled by default.\");\n                        Some(OutResponse::Rejected)\n                    }\n                }\n            }\n            _ => Some(OutResponse::Rejected),\n        }\n    }\n\n    fn control_in<'a>(&'a mut self, req: Request, buf: &'a mut [u8]) -> Option<InResponse<'a>> {\n        if req.index != self.if_num.0 as u16 {\n            return None;\n        }\n\n        match (req.request_type, req.recipient) {\n            (RequestType::Standard, Recipient::Interface) => match req.request {\n                Request::GET_DESCRIPTOR => match (req.value >> 8) as u8 {\n                    HID_DESC_DESCTYPE_HID_REPORT => Some(InResponse::Accepted(self.report_descriptor)),\n                    HID_DESC_DESCTYPE_HID => Some(InResponse::Accepted(&self.hid_descriptor)),\n                    _ => Some(InResponse::Rejected),\n                },\n\n                _ => Some(InResponse::Rejected),\n            },\n            (RequestType::Class, Recipient::Interface) => {\n                trace!(\"HID control_in {:?}\", req);\n                match req.request {\n                    HID_REQ_GET_REPORT => {\n                        let size = match ReportId::try_from(req.value) {\n                            Ok(id) => self.request_handler.as_mut().and_then(|x| x.get_report(id, buf)),\n                            Err(_) => None,\n                        };\n\n                        if let Some(size) = size {\n                            Some(InResponse::Accepted(&buf[0..size]))\n                        } else {\n                            Some(InResponse::Rejected)\n                        }\n                    }\n                    HID_REQ_GET_IDLE => {\n                        if let Some(handler) = self.request_handler.as_mut() {\n                            let id = req.value as u8;\n                            let id = (id != 0).then_some(ReportId::In(id));\n                            if let Some(dur) = handler.get_idle_ms(id) {\n                                let dur = u8::try_from(dur / 4).unwrap_or(0);\n                                buf[0] = dur;\n                                Some(InResponse::Accepted(&buf[0..1]))\n                            } else {\n                                Some(InResponse::Rejected)\n                            }\n                        } else {\n                            Some(InResponse::Rejected)\n                        }\n                    }\n                    HID_REQ_GET_PROTOCOL => {\n                        if let Some(request_handler) = self.request_handler.as_mut() {\n                            buf[0] = request_handler.get_protocol() as u8;\n                        } else {\n                            // Return `Report` protocol mode by default\n                            buf[0] = HidProtocolMode::Report as u8;\n                        }\n                        Some(InResponse::Accepted(&buf[0..1]))\n                    }\n                    _ => Some(InResponse::Rejected),\n                }\n            }\n            _ => None,\n        }\n    }\n}\n"
  },
  {
    "path": "embassy-usb/src/class/midi.rs",
    "content": "//! MIDI class implementation.\n\nuse crate::Builder;\nuse crate::driver::{Driver, Endpoint, EndpointError, EndpointIn, EndpointOut};\n\n/// This should be used as `device_class` when building the `UsbDevice`.\npub const USB_AUDIO_CLASS: u8 = 0x01;\n\nconst USB_AUDIOCONTROL_SUBCLASS: u8 = 0x01;\nconst USB_MIDISTREAMING_SUBCLASS: u8 = 0x03;\nconst MIDI_IN_JACK_SUBTYPE: u8 = 0x02;\nconst MIDI_OUT_JACK_SUBTYPE: u8 = 0x03;\nconst EMBEDDED: u8 = 0x01;\nconst EXTERNAL: u8 = 0x02;\nconst CS_INTERFACE: u8 = 0x24;\nconst CS_ENDPOINT: u8 = 0x25;\nconst HEADER_SUBTYPE: u8 = 0x01;\nconst MS_HEADER_SUBTYPE: u8 = 0x01;\nconst MS_GENERAL: u8 = 0x01;\nconst PROTOCOL_NONE: u8 = 0x00;\nconst MIDI_IN_SIZE: u8 = 0x06;\nconst MIDI_OUT_SIZE: u8 = 0x09;\n\n/// Packet level implementation of a USB MIDI device.\n///\n/// This class can be used directly and it has the least overhead due to directly reading and\n/// writing USB packets with no intermediate buffers, but it will not act like a stream-like port.\n/// The following constraints must be followed if you use this class directly:\n///\n/// - `read_packet` must be called with a buffer large enough to hold `max_packet_size` bytes.\n/// - `write_packet` must not be called with a buffer larger than `max_packet_size` bytes.\n/// - If you write a packet that is exactly `max_packet_size` bytes long, it won't be processed by the\n///   host operating system until a subsequent shorter packet is sent. A zero-length packet (ZLP)\n///   can be sent if there is no other data to send. This is because USB bulk transactions must be\n///   terminated with a short packet, even if the bulk endpoint is used for stream-like data.\npub struct MidiClass<'d, D: Driver<'d>> {\n    read_ep: D::EndpointOut,\n    write_ep: D::EndpointIn,\n}\n\nimpl<'d, D: Driver<'d>> MidiClass<'d, D> {\n    /// Creates a new `MidiClass` with the provided UsbBus, number of input and output jacks and `max_packet_size` in bytes.\n    /// For full-speed devices, `max_packet_size` has to be one of 8, 16, 32 or 64.\n    pub fn new(builder: &mut Builder<'d, D>, n_in_jacks: u8, n_out_jacks: u8, max_packet_size: u16) -> Self {\n        let mut func = builder.function(USB_AUDIO_CLASS, USB_AUDIOCONTROL_SUBCLASS, PROTOCOL_NONE);\n\n        // Audio control interface\n        let mut iface = func.interface();\n        let audio_if = iface.interface_number();\n        let midi_if = u8::from(audio_if) + 1;\n        let mut alt = iface.alt_setting(USB_AUDIO_CLASS, USB_AUDIOCONTROL_SUBCLASS, PROTOCOL_NONE, None);\n        alt.descriptor(CS_INTERFACE, &[HEADER_SUBTYPE, 0x00, 0x01, 0x09, 0x00, 0x01, midi_if]);\n\n        // MIDIStreaming interface\n        let mut iface = func.interface();\n        let _midi_if = iface.interface_number();\n        let mut alt = iface.alt_setting(USB_AUDIO_CLASS, USB_MIDISTREAMING_SUBCLASS, PROTOCOL_NONE, None);\n\n        let midi_streaming_total_length = 7\n            + (n_in_jacks + n_out_jacks) as usize * (MIDI_IN_SIZE + MIDI_OUT_SIZE) as usize\n            + 7\n            + (4 + n_out_jacks as usize)\n            + 7\n            + (4 + n_in_jacks as usize);\n\n        alt.descriptor(\n            CS_INTERFACE,\n            &[\n                MS_HEADER_SUBTYPE,\n                0x00,\n                0x01,\n                (midi_streaming_total_length & 0xFF) as u8,\n                ((midi_streaming_total_length >> 8) & 0xFF) as u8,\n            ],\n        );\n\n        // Calculates the index'th external midi in jack id\n        let in_jack_id_ext = |index| 2 * index + 1;\n        // Calculates the index'th embedded midi out jack id\n        let out_jack_id_emb = |index| 2 * index + 2;\n        // Calculates the index'th external midi out jack id\n        let out_jack_id_ext = |index| 2 * n_in_jacks + 2 * index + 1;\n        // Calculates the index'th embedded midi in jack id\n        let in_jack_id_emb = |index| 2 * n_in_jacks + 2 * index + 2;\n\n        for i in 0..n_in_jacks {\n            alt.descriptor(CS_INTERFACE, &[MIDI_IN_JACK_SUBTYPE, EXTERNAL, in_jack_id_ext(i), 0x00]);\n        }\n\n        for i in 0..n_out_jacks {\n            alt.descriptor(CS_INTERFACE, &[MIDI_IN_JACK_SUBTYPE, EMBEDDED, in_jack_id_emb(i), 0x00]);\n        }\n\n        for i in 0..n_out_jacks {\n            alt.descriptor(\n                CS_INTERFACE,\n                &[\n                    MIDI_OUT_JACK_SUBTYPE,\n                    EXTERNAL,\n                    out_jack_id_ext(i),\n                    0x01,\n                    in_jack_id_emb(i),\n                    0x01,\n                    0x00,\n                ],\n            );\n        }\n\n        for i in 0..n_in_jacks {\n            alt.descriptor(\n                CS_INTERFACE,\n                &[\n                    MIDI_OUT_JACK_SUBTYPE,\n                    EMBEDDED,\n                    out_jack_id_emb(i),\n                    0x01,\n                    in_jack_id_ext(i),\n                    0x01,\n                    0x00,\n                ],\n            );\n        }\n\n        let mut endpoint_data = [\n            MS_GENERAL, 0, // Number of jacks\n            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Jack mappings\n        ];\n        endpoint_data[1] = n_out_jacks;\n        for i in 0..n_out_jacks {\n            endpoint_data[2 + i as usize] = in_jack_id_emb(i);\n        }\n        let read_ep = alt.endpoint_bulk_out(None, max_packet_size);\n        alt.descriptor(CS_ENDPOINT, &endpoint_data[0..2 + n_out_jacks as usize]);\n\n        endpoint_data[1] = n_in_jacks;\n        for i in 0..n_in_jacks {\n            endpoint_data[2 + i as usize] = out_jack_id_emb(i);\n        }\n        let write_ep = alt.endpoint_bulk_in(None, max_packet_size);\n        alt.descriptor(CS_ENDPOINT, &endpoint_data[0..2 + n_in_jacks as usize]);\n\n        MidiClass { read_ep, write_ep }\n    }\n\n    /// Gets the maximum packet size in bytes.\n    pub fn max_packet_size(&self) -> u16 {\n        // The size is the same for both endpoints.\n        self.read_ep.info().max_packet_size\n    }\n\n    /// Writes a single packet into the IN endpoint.\n    pub async fn write_packet(&mut self, data: &[u8]) -> Result<(), EndpointError> {\n        self.write_ep.write(data).await\n    }\n\n    /// Reads a single packet from the OUT endpoint.\n    pub async fn read_packet(&mut self, data: &mut [u8]) -> Result<usize, EndpointError> {\n        self.read_ep.read(data).await\n    }\n\n    /// Waits for the USB host to enable this interface\n    pub async fn wait_connection(&mut self) {\n        self.read_ep.wait_enabled().await;\n    }\n\n    /// Split the class into a sender and receiver.\n    ///\n    /// This allows concurrently sending and receiving packets from separate tasks.\n    pub fn split(self) -> (Sender<'d, D>, Receiver<'d, D>) {\n        (\n            Sender {\n                write_ep: self.write_ep,\n            },\n            Receiver { read_ep: self.read_ep },\n        )\n    }\n}\n\n/// Midi class packet sender.\n///\n/// You can obtain a `Sender` with [`MidiClass::split`]\npub struct Sender<'d, D: Driver<'d>> {\n    write_ep: D::EndpointIn,\n}\n\nimpl<'d, D: Driver<'d>> Sender<'d, D> {\n    /// Gets the maximum packet size in bytes.\n    pub fn max_packet_size(&self) -> u16 {\n        // The size is the same for both endpoints.\n        self.write_ep.info().max_packet_size\n    }\n\n    /// Writes a single packet.\n    pub async fn write_packet(&mut self, data: &[u8]) -> Result<(), EndpointError> {\n        self.write_ep.write(data).await\n    }\n\n    /// Waits for the USB host to enable this interface\n    pub async fn wait_connection(&mut self) {\n        self.write_ep.wait_enabled().await;\n    }\n}\n\n/// Midi class packet receiver.\n///\n/// You can obtain a `Receiver` with [`MidiClass::split`]\npub struct Receiver<'d, D: Driver<'d>> {\n    read_ep: D::EndpointOut,\n}\n\nimpl<'d, D: Driver<'d>> Receiver<'d, D> {\n    /// Gets the maximum packet size in bytes.\n    pub fn max_packet_size(&self) -> u16 {\n        // The size is the same for both endpoints.\n        self.read_ep.info().max_packet_size\n    }\n\n    /// Reads a single packet.\n    pub async fn read_packet(&mut self, data: &mut [u8]) -> Result<usize, EndpointError> {\n        self.read_ep.read(data).await\n    }\n\n    /// Waits for the USB host to enable this interface\n    pub async fn wait_connection(&mut self) {\n        self.read_ep.wait_enabled().await;\n    }\n}\n"
  },
  {
    "path": "embassy-usb/src/class/mod.rs",
    "content": "//! Implementations of well-known USB classes.\npub mod cdc_acm;\npub mod cdc_ncm;\npub mod cmsis_dap_v2;\npub mod dfu;\npub mod hid;\npub mod midi;\npub mod uac1;\npub mod web_usb;\n"
  },
  {
    "path": "embassy-usb/src/class/uac1/class_codes.rs",
    "content": "//! Audio Device Class Codes as defined in Universal Serial Bus Device Class\n//! Definition for Audio Devices, Release 1.0, Appendix A and Universal Serial\n//! Bus Device Class Definition for Audio Data Formats, Release 1.0, Appendix\n//! A.1.1 (Audio Data Format Type I Codes)\n#![allow(dead_code)]\n\n/// The current version of the ADC specification (1.0)\npub const ADC_VERSION: u16 = 0x0100;\n\n/// The current version of the USB device (1.0)\npub const DEVICE_VERSION: u16 = 0x0100;\n\n/// Audio Interface Class Code\npub const USB_AUDIO_CLASS: u8 = 0x01;\n\n// Audio Interface Subclass Codes\npub const USB_UNDEFINED_SUBCLASS: u8 = 0x00;\npub const USB_AUDIOCONTROL_SUBCLASS: u8 = 0x01;\npub const USB_AUDIOSTREAMING_SUBCLASS: u8 = 0x02;\npub const USB_MIDISTREAMING_SUBCLASS: u8 = 0x03;\n\n// Audio Protocol Code\npub const PROTOCOL_NONE: u8 = 0x00;\n\n// Audio Class-Specific Descriptor Types\npub const CS_UNDEFINED: u8 = 0x20;\npub const CS_DEVICE: u8 = 0x21;\npub const CS_CONFIGURATION: u8 = 0x22;\npub const CS_STRING: u8 = 0x23;\npub const CS_INTERFACE: u8 = 0x24;\npub const CS_ENDPOINT: u8 = 0x25;\n\n// Descriptor Subtype\npub const AC_DESCRIPTOR_UNDEFINED: u8 = 0x00;\npub const HEADER_SUBTYPE: u8 = 0x01;\npub const INPUT_TERMINAL: u8 = 0x02;\npub const OUTPUT_TERMINAL: u8 = 0x03;\npub const MIXER_UNIT: u8 = 0x04;\npub const SELECTOR_UNIT: u8 = 0x05;\npub const FEATURE_UNIT: u8 = 0x06;\npub const PROCESSING_UNIT: u8 = 0x07;\npub const EXTENSION_UNIT: u8 = 0x08;\n\n// Audio Class-Specific AS Interface Descriptor Subtypes\npub const AS_DESCRIPTOR_UNDEFINED: u8 = 0x00;\npub const AS_GENERAL: u8 = 0x01;\npub const FORMAT_TYPE: u8 = 0x02;\npub const FORMAT_SPECIFIC: u8 = 0x03;\n\n// Processing Unit Process Types\npub const PROCESS_UNDEFINED: u16 = 0x00;\npub const UP_DOWNMIX_PROCESS: u16 = 0x01;\npub const DOLBY_PROLOGIC_PROCESS: u16 = 0x02;\npub const DDD_STEREO_EXTENDER_PROCESS: u16 = 0x03;\npub const REVERBERATION_PROCESS: u16 = 0x04;\npub const CHORUS_PROCESS: u16 = 0x05;\npub const DYN_RANGE_COMP_PROCESS: u16 = 0x06;\n\n// Audio Class-Specific Endpoint Descriptor Subtypes\npub const EP_DESCRIPTOR_UNDEFINED: u8 = 0x00;\npub const EP_GENERAL: u8 = 0x01;\n\n// Audio Class-Specific Request Codes\npub const REQUEST_CODE_UNDEFINED: u8 = 0x00;\npub const SET_CUR: u8 = 0x01;\npub const GET_CUR: u8 = 0x81;\npub const SET_MIN: u8 = 0x02;\npub const GET_MIN: u8 = 0x82;\npub const SET_MAX: u8 = 0x03;\npub const GET_MAX: u8 = 0x83;\npub const SET_RES: u8 = 0x04;\npub const GET_RES: u8 = 0x84;\npub const SET_MEM: u8 = 0x05;\npub const GET_MEM: u8 = 0x85;\npub const GET_STAT: u8 = 0xFF;\n\n// Terminal Control Selectors\npub const TE_CONTROL_UNDEFINED: u8 = 0x00;\npub const COPY_PROTECT_CONTROL: u8 = 0x01;\n\n// Feature Unit Control Selectors\npub const FU_CONTROL_UNDEFINED: u8 = 0x00;\npub const MUTE_CONTROL: u8 = 0x01;\npub const VOLUME_CONTROL: u8 = 0x02;\npub const BASS_CONTROL: u8 = 0x03;\npub const MID_CONTROL: u8 = 0x04;\npub const TREBLE_CONTROL: u8 = 0x05;\npub const GRAPHIC_EQUALIZER_CONTROL: u8 = 0x06;\npub const AUTOMATIC_GAIN_CONTROL: u8 = 0x07;\npub const DELAY_CONTROL: u8 = 0x08;\npub const BASS_BOOST_CONTROL: u8 = 0x09;\npub const LOUDNESS_CONTROL: u8 = 0x0A;\n\n// Up/Down-mix Processing Unit Control Selectors\npub const UD_CONTROL_UNDEFINED: u8 = 0x00;\npub const UD_ENABLE_CONTROL: u8 = 0x01;\npub const UD_MODE_SELECT_CONTROL: u8 = 0x02;\n\n// Dolby Prologic Processing Unit Control Selectors\npub const DP_CONTROL_UNDEFINED: u8 = 0x00;\npub const DP_ENABLE_CONTROL: u8 = 0x01;\npub const DP_MODE_SELECT_CONTROL: u8 = 0x2;\n\n// 3D Stereo Extender Processing Unit Control Selectors\npub const DDD_CONTROL_UNDEFINED: u8 = 0x00;\npub const DDD_ENABLE_CONTROL: u8 = 0x01;\npub const DDD_SPACIOUSNESS_CONTROL: u8 = 0x03;\n\n// Reverberation Processing Unit Control Selectors\npub const RV_CONTROL_UNDEFINED: u8 = 0x00;\npub const RV_ENABLE_CONTROL: u8 = 0x01;\npub const REVERB_LEVEL_CONTROL: u8 = 0x02;\npub const REVERB_TIME_CONTROL: u8 = 0x03;\npub const REVERB_FEEDBACK_CONTROL: u8 = 0x04;\n\n// Chorus Processing Unit Control Selectors\npub const CH_CONTROL_UNDEFINED: u8 = 0x00;\npub const CH_ENABLE_CONTROL: u8 = 0x01;\npub const CHORUS_LEVEL_CONTROL: u8 = 0x02;\npub const CHORUS_RATE_CONTROL: u8 = 0x03;\npub const CHORUS_DEPTH_CONTROL: u8 = 0x04;\n\n// Dynamic Range Compressor Processing Unit Control Selectors\npub const DR_CONTROL_UNDEFINED: u8 = 0x00;\npub const DR_ENABLE_CONTROL: u8 = 0x01;\npub const COMPRESSION_RATE_CONTROL: u8 = 0x02;\npub const MAXAMPL_CONTROL: u8 = 0x03;\npub const THRESHOLD_CONTROL: u8 = 0x04;\npub const ATTACK_TIME: u8 = 0x05;\npub const RELEASE_TIME: u8 = 0x06;\n\n// Extension Unit Control Selectors\npub const XU_CONTROL_UNDEFINED: u16 = 0x00;\npub const XU_ENABLE_CONTROL: u16 = 0x01;\n\n// Endpoint Control Selectors\npub const EP_CONTROL_UNDEFINED: u8 = 0x00;\npub const SAMPLING_FREQ_CONTROL: u8 = 0x01;\npub const PITCH_CONTROL: u8 = 0x02;\n\n// Format Type Codes\npub const FORMAT_TYPE_UNDEFINED: u8 = 0x00;\npub const FORMAT_TYPE_I: u8 = 0x01;\n\n// Audio Data Format Type I Codes\npub const TYPE_I_UNDEFINED: u16 = 0x0000;\npub const PCM: u16 = 0x0001;\npub const PCM8: u16 = 0x0002;\npub const IEEE_FLOAT: u16 = 0x0003;\npub const ALAW: u16 = 0x0004;\npub const MULAW: u16 = 0x0005;\n"
  },
  {
    "path": "embassy-usb/src/class/uac1/mod.rs",
    "content": "//! USB Audio Class 1.0 implementations for different applications.\n//!\n//! Contains:\n//! - The `speaker` class with a single audio streaming interface (host to device)\n\npub mod speaker;\n\nmod class_codes;\nmod terminal_type;\n\n/// The maximum supported audio channel index (corresponds to `Top`).\n/// FIXME: Use `core::mem::variant_count(...)` when stabilized.\nconst MAX_AUDIO_CHANNEL_INDEX: usize = 12;\n\n/// The maximum number of supported audio channels.\n///\n/// Includes all twelve channels from `Channel`, plus the Master channel.\nconst MAX_AUDIO_CHANNEL_COUNT: usize = MAX_AUDIO_CHANNEL_INDEX + 1;\n\n/// USB Audio Channel\n#[derive(Debug, Clone, Copy, PartialEq)]\n#[allow(missing_docs)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub enum Channel {\n    LeftFront,\n    RightFront,\n    CenterFront,\n    Lfe,\n    LeftSurround,\n    RightSurround,\n    LeftOfCenter,\n    RightOfCenter,\n    Surround,\n    SideLeft,\n    SideRight,\n    Top,\n}\n\nimpl Channel {\n    /// Map a `Channel` to its corresponding USB Audio `ChannelConfig`.\n    fn get_channel_config(&self) -> ChannelConfig {\n        match self {\n            Channel::LeftFront => ChannelConfig::LeftFront,\n            Channel::RightFront => ChannelConfig::RightFront,\n            Channel::CenterFront => ChannelConfig::CenterFront,\n            Channel::Lfe => ChannelConfig::Lfe,\n            Channel::LeftSurround => ChannelConfig::LeftSurround,\n            Channel::RightSurround => ChannelConfig::RightSurround,\n            Channel::LeftOfCenter => ChannelConfig::LeftOfCenter,\n            Channel::RightOfCenter => ChannelConfig::RightOfCenter,\n            Channel::Surround => ChannelConfig::Surround,\n            Channel::SideLeft => ChannelConfig::SideLeft,\n            Channel::SideRight => ChannelConfig::SideRight,\n            Channel::Top => ChannelConfig::Top,\n        }\n    }\n}\n\n/// USB Audio Channel configuration\n#[repr(u16)]\n#[non_exhaustive]\n// #[derive(Copy, Clone, Eq, PartialEq, Debug)]\nenum ChannelConfig {\n    None = 0x0000,\n    LeftFront = 0x0001,\n    RightFront = 0x0002,\n    CenterFront = 0x0004,\n    Lfe = 0x0008,\n    LeftSurround = 0x0010,\n    RightSurround = 0x0020,\n    LeftOfCenter = 0x0040,\n    RightOfCenter = 0x0080,\n    Surround = 0x0100,\n    SideLeft = 0x0200,\n    SideRight = 0x0400,\n    Top = 0x0800,\n}\n\nimpl From<ChannelConfig> for u16 {\n    fn from(t: ChannelConfig) -> u16 {\n        t as u16\n    }\n}\n\n/// Feedback period adjustment `bRefresh` [UAC 3.7.2.2]\n///\n/// From the specification: \"A new Ff value is available every 2^(10 – P) frames with P ranging from 1 to 9. The\n/// bRefresh field of the synch standard endpoint descriptor is used to report the exponent (10-P) to the Host.\"\n///\n/// This means:\n/// - 512 ms (2^9 frames) to 2 ms (2^1 frames) for USB full-speed\n/// - 64 ms (2^9 microframes) to 0.25 ms (2^1 microframes) for USB high-speed\n#[repr(u8)]\n#[allow(missing_docs)]\n#[derive(Clone, Copy)]\npub enum FeedbackRefresh {\n    Period2Frames = 1,\n    Period4Frames = 2,\n    Period8Frames = 3,\n    Period16Frames = 4,\n    Period32Frames = 5,\n    Period64Frames = 6,\n    Period128Frames = 7,\n    Period256Frames = 8,\n    Period512Frames = 9,\n}\n\nimpl FeedbackRefresh {\n    /// Gets the number of frames, after which a new feedback frame is returned.\n    pub const fn frame_count(&self) -> usize {\n        1 << (*self as usize)\n    }\n}\n\n/// Audio sample width.\n///\n/// Stored in number of bytes per sample.\n#[repr(u8)]\n#[derive(Clone, Copy)]\npub enum SampleWidth {\n    /// 16 bit audio\n    Width2Byte = 2,\n    /// 24 bit audio\n    Width3Byte = 3,\n    /// 32 bit audio\n    Width4Byte = 4,\n}\n\nimpl SampleWidth {\n    /// Get the audio sample resolution in number of bit.\n    pub const fn in_bit(self) -> usize {\n        8 * self as usize\n    }\n}\n"
  },
  {
    "path": "embassy-usb/src/class/uac1/speaker.rs",
    "content": "//! USB Audio Class 1.0 - Speaker device\n//!\n//! Provides a class with a single audio streaming interface (host to device),\n//! that advertises itself as a speaker. Includes explicit sample rate feedback.\n//!\n//! Various aspects of the audio stream can be configured, for example:\n//! - sample rate\n//! - sample resolution\n//! - audio channel count and assignment\n//!\n//! The class provides volume and mute controls for each channel.\n\nuse core::cell::{Cell, RefCell};\nuse core::future::{Future, poll_fn};\nuse core::marker::PhantomData;\nuse core::sync::atomic::{AtomicBool, AtomicU32, Ordering};\nuse core::task::Poll;\n\nuse embassy_sync::blocking_mutex::CriticalSectionMutex;\nuse embassy_sync::waitqueue::WakerRegistration;\nuse heapless::Vec;\n\nuse super::class_codes::*;\nuse super::terminal_type::TerminalType;\nuse super::{Channel, ChannelConfig, FeedbackRefresh, MAX_AUDIO_CHANNEL_COUNT, MAX_AUDIO_CHANNEL_INDEX, SampleWidth};\nuse crate::control::{self, InResponse, OutResponse, Recipient, Request, RequestType};\nuse crate::descriptor::{SynchronizationType, UsageType};\nuse crate::driver::{Driver, Endpoint, EndpointError, EndpointIn, EndpointOut, EndpointType};\nuse crate::types::InterfaceNumber;\nuse crate::{Builder, Handler};\n\n/// Maximum allowed sampling rate (3 bytes) in Hz.\nconst MAX_SAMPLE_RATE_HZ: u32 = 0x7FFFFF;\n\n/// Arbitrary unique identifier for the input unit.\nconst INPUT_UNIT_ID: u8 = 0x01;\n\n/// Arbitrary unique identifier for the feature unit.\nconst FEATURE_UNIT_ID: u8 = 0x02;\n\n/// Arbitrary unique identifier for the output unit.\nconst OUTPUT_UNIT_ID: u8 = 0x03;\n\n// Volume settings go from -25600 to 0, in steps of 256.\n// Therefore, the volume settings are 8q8 values in units of dB.\nconst VOLUME_STEPS_PER_DB: i16 = 256;\nconst MIN_VOLUME_DB: i16 = -100;\nconst MAX_VOLUME_DB: i16 = 0;\n\n// Maximum number of supported discrete sample rates.\nconst MAX_SAMPLE_RATE_COUNT: usize = 10;\n\n/// The volume of an audio channel.\n#[derive(Debug, Clone, Copy)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub enum Volume {\n    /// The channel is muted.\n    Muted,\n    /// The channel volume in dB. Ranges from `MIN_VOLUME_DB` (quietest) to `MAX_VOLUME_DB` (loudest).\n    DeciBel(f32),\n}\n\n/// Internal state for the USB Audio Class.\npub struct State<'d> {\n    control: Option<Control<'d>>,\n    shared: SharedControl<'d>,\n}\n\nimpl<'d> Default for State<'d> {\n    fn default() -> Self {\n        Self::new()\n    }\n}\n\nimpl<'d> State<'d> {\n    /// Create a new `State`.\n    pub fn new() -> Self {\n        Self {\n            control: None,\n            shared: SharedControl::default(),\n        }\n    }\n}\n\n/// Implementation of the USB audio class 1.0.\npub struct Speaker<'d, D: Driver<'d>> {\n    phantom: PhantomData<&'d D>,\n}\n\nimpl<'d, D: Driver<'d>> Speaker<'d, D> {\n    /// Creates a new [`Speaker`] device, split into a stream, feedback, and a control change notifier.\n    ///\n    /// The packet size should be chosen, based on the expected transfer size of samples per (micro)frame.\n    /// For example, a stereo stream at 32 bit resolution and 48 kHz sample rate yields packets of 384 byte for\n    /// full-speed USB (1 ms frame interval) or 48 byte for high-speed USB (125 us microframe interval).\n    /// When using feedback, the packet size varies and thus, the `max_packet_size` should be increased (e.g. to double).\n    ///\n    /// # Arguments\n    ///\n    /// * `builder` - The builder for the class.\n    /// * `state` - The internal state of the class.\n    /// * `max_packet_size` - The maximum packet size per (micro)frame.\n    /// * `resolution` - The audio sample resolution.\n    /// * `sample_rates_hz` - The supported sample rates in Hz.\n    /// * `channels` - The advertised audio channels (up to 12). Entries must be unique, or this function panics.\n    /// * `feedback_refresh_period` - The refresh period for the feedback value.\n    pub fn new(\n        builder: &mut Builder<'d, D>,\n        state: &'d mut State<'d>,\n        max_packet_size: u16,\n        resolution: SampleWidth,\n        sample_rates_hz: &[u32],\n        channels: &'d [Channel],\n        feedback_refresh_period: FeedbackRefresh,\n    ) -> (Stream<'d, D>, Feedback<'d, D>, ControlMonitor<'d>) {\n        // The class and subclass fields of the IAD aren't required to match the class and subclass fields of\n        // the interfaces in the interface collection that the IAD describes. Microsoft recommends that\n        // the first interface of the collection has class and subclass fields that match the class and\n        // subclass fields of the IAD.\n        let mut func = builder.function(USB_AUDIO_CLASS, USB_AUDIOCONTROL_SUBCLASS, PROTOCOL_NONE);\n\n        // Audio control interface (mandatory) [UAC 4.3.1]\n        let mut interface = func.interface();\n        let control_interface = interface.interface_number().into();\n        let streaming_interface = u8::from(control_interface) + 1;\n        let mut alt = interface.alt_setting(USB_AUDIO_CLASS, USB_AUDIOCONTROL_SUBCLASS, PROTOCOL_NONE, None);\n\n        // Terminal topology:\n        // Input terminal (receives audio stream) -> Feature Unit (mute and volume) -> Output terminal (e.g. towards speaker)\n\n        // =======================================\n        // Input Terminal Descriptor [UAC 3.3.2.1]\n        // Audio input\n        let terminal_type: u16 = TerminalType::UsbStreaming.into();\n\n        // Assemble channel configuration field\n        let mut channel_config: u16 = ChannelConfig::None.into();\n        for channel in channels {\n            let channel: u16 = channel.get_channel_config().into();\n\n            if channel_config & channel != 0 {\n                panic!(\"Invalid channel config, duplicate channel {}.\", channel);\n            }\n            channel_config |= channel;\n        }\n\n        let input_terminal_descriptor = [\n            INPUT_TERMINAL, // bDescriptorSubtype\n            INPUT_UNIT_ID,  // bTerminalID\n            terminal_type as u8,\n            (terminal_type >> 8) as u8, // wTerminalType\n            0x00,                       // bAssocTerminal (none)\n            channels.len() as u8,       // bNrChannels\n            channel_config as u8,\n            (channel_config >> 8) as u8, // wChannelConfig\n            0x00,                        // iChannelNames (none)\n            0x00,                        // iTerminal (none)\n        ];\n\n        // ========================================\n        // Output Terminal Descriptor [UAC 4.3.2.2]\n        // Speaker output\n        let terminal_type: u16 = TerminalType::OutSpeaker.into();\n        let output_terminal_descriptor = [\n            OUTPUT_TERMINAL, // bDescriptorSubtype\n            OUTPUT_UNIT_ID,  // bTerminalID\n            terminal_type as u8,\n            (terminal_type >> 8) as u8, // wTerminalType\n            0x00,                       // bAssocTerminal (none)\n            FEATURE_UNIT_ID,            // bSourceID (the feature unit)\n            0x00,                       // iTerminal (none)\n        ];\n\n        // =====================================\n        // Feature Unit Descriptor [UAC 4.3.2.5]\n        // Mute and volume control\n        let controls = MUTE_CONTROL | VOLUME_CONTROL;\n\n        const FEATURE_UNIT_DESCRIPTOR_SIZE: usize = 5;\n        let mut feature_unit_descriptor: Vec<u8, { FEATURE_UNIT_DESCRIPTOR_SIZE + MAX_AUDIO_CHANNEL_COUNT + 1 }> =\n            Vec::from_slice(&[\n                FEATURE_UNIT,         // bDescriptorSubtype (Feature Unit)\n                FEATURE_UNIT_ID,      // bUnitID\n                INPUT_UNIT_ID,        // bSourceID\n                1,                    // bControlSize (one byte per control)\n                FU_CONTROL_UNDEFINED, // Master controls (disabled, use only per-channel control)\n            ])\n            .unwrap();\n\n        // Add per-channel controls\n        for _channel in channels {\n            feature_unit_descriptor.push(controls).unwrap();\n        }\n        feature_unit_descriptor.push(0x00).unwrap(); // iFeature (none)\n\n        // ===============================================\n        // Format desciptor [UAC 4.5.3]\n        // Used later, for operational streaming interface\n        let mut format_descriptor: Vec<u8, { 6 + 3 * MAX_SAMPLE_RATE_COUNT }> = Vec::from_slice(&[\n            FORMAT_TYPE,               // bDescriptorSubtype\n            FORMAT_TYPE_I,             // bFormatType\n            channels.len() as u8,      // bNrChannels\n            resolution as u8,          // bSubframeSize\n            resolution.in_bit() as u8, // bBitResolution\n        ])\n        .unwrap();\n\n        format_descriptor.push(sample_rates_hz.len() as u8).unwrap();\n\n        for sample_rate_hz in sample_rates_hz {\n            assert!(*sample_rate_hz <= MAX_SAMPLE_RATE_HZ);\n            format_descriptor.push((sample_rate_hz & 0xFF) as u8).unwrap();\n            format_descriptor.push(((sample_rate_hz >> 8) & 0xFF) as u8).unwrap();\n            format_descriptor.push(((sample_rate_hz >> 16) & 0xFF) as u8).unwrap();\n        }\n\n        // ==================================================\n        // Class-specific AC Interface Descriptor [UAC 4.3.2]\n        const DESCRIPTOR_HEADER_SIZE: usize = 2;\n        const INTERFACE_DESCRIPTOR_SIZE: usize = 7;\n\n        let mut total_descriptor_length = 0;\n\n        for size in [\n            INTERFACE_DESCRIPTOR_SIZE,\n            input_terminal_descriptor.len(),\n            feature_unit_descriptor.len(),\n            output_terminal_descriptor.len(),\n        ] {\n            total_descriptor_length += size + DESCRIPTOR_HEADER_SIZE;\n        }\n\n        let interface_descriptor: [u8; INTERFACE_DESCRIPTOR_SIZE] = [\n            HEADER_SUBTYPE, // bDescriptorSubtype (Header)\n            ADC_VERSION as u8,\n            (ADC_VERSION >> 8) as u8, // bcdADC\n            total_descriptor_length as u8,\n            (total_descriptor_length >> 8) as u8, // wTotalLength\n            0x01,                                 // bInCollection (1 streaming interface)\n            streaming_interface,                  // baInterfaceNr\n        ];\n\n        alt.descriptor(CS_INTERFACE, &interface_descriptor);\n        alt.descriptor(CS_INTERFACE, &input_terminal_descriptor);\n        alt.descriptor(CS_INTERFACE, &feature_unit_descriptor);\n        alt.descriptor(CS_INTERFACE, &output_terminal_descriptor);\n\n        // =====================================================\n        // Audio streaming interface, zero-bandwidth [UAC 4.5.1]\n        let mut interface = func.interface();\n        let alt = interface.alt_setting(USB_AUDIO_CLASS, USB_AUDIOSTREAMING_SUBCLASS, PROTOCOL_NONE, None);\n        drop(alt);\n\n        // ==================================================\n        // Audio streaming interface, operational [UAC 4.5.1]\n        let mut alt = interface.alt_setting(USB_AUDIO_CLASS, USB_AUDIOSTREAMING_SUBCLASS, PROTOCOL_NONE, None);\n\n        alt.descriptor(\n            CS_INTERFACE,\n            &[\n                AS_GENERAL,    // bDescriptorSubtype\n                INPUT_UNIT_ID, // bTerminalLink\n                0x00,          // bDelay (none)\n                PCM as u8,\n                (PCM >> 8) as u8, // wFormatTag (PCM format)\n            ],\n        );\n\n        alt.descriptor(CS_INTERFACE, &format_descriptor);\n\n        let streaming_endpoint = alt.alloc_endpoint_out(EndpointType::Isochronous, None, max_packet_size, 1);\n        let feedback_endpoint = alt.alloc_endpoint_in(\n            EndpointType::Isochronous,\n            None,\n            4, // Feedback packets are 24 bit (10.14 format).\n            1,\n        );\n\n        // Write the descriptor for the streaming endpoint, after knowing the address of the feedback endpoint.\n        alt.endpoint_descriptor(\n            streaming_endpoint.info(),\n            SynchronizationType::Asynchronous,\n            UsageType::DataEndpoint,\n            &[\n                0x00,                                 // bRefresh (0)\n                feedback_endpoint.info().addr.into(), // bSynchAddress (the feedback endpoint)\n            ],\n        );\n\n        alt.descriptor(\n            CS_ENDPOINT,\n            &[\n                AS_GENERAL,            // bDescriptorSubtype (General)\n                SAMPLING_FREQ_CONTROL, // bmAttributes (support sampling frequency control)\n                0x02,                  // bLockDelayUnits (PCM)\n                0x0000 as u8,\n                (0x0000 >> 8) as u8, // wLockDelay (0)\n            ],\n        );\n\n        // Write the feedback endpoint descriptor after the streaming endpoint descriptor\n        // This is demanded by the USB audio class specification.\n        alt.endpoint_descriptor(\n            feedback_endpoint.info(),\n            SynchronizationType::NoSynchronization,\n            UsageType::FeedbackEndpoint,\n            &[\n                feedback_refresh_period as u8, // bRefresh\n                0x00,                          // bSynchAddress (none)\n            ],\n        );\n\n        // Free up the builder.\n        drop(func);\n\n        // Store channel information\n        state.shared.channels = channels;\n\n        state.control = Some(Control {\n            shared: &state.shared,\n            streaming_endpoint_address: streaming_endpoint.info().addr.into(),\n            control_interface_number: control_interface,\n        });\n\n        builder.handler(state.control.as_mut().unwrap());\n\n        let control = &state.shared;\n\n        (\n            Stream { streaming_endpoint },\n            Feedback { feedback_endpoint },\n            ControlMonitor { shared: control },\n        )\n    }\n}\n\n/// Audio settings for the feature unit.\n///\n/// Contains volume and mute control.\n#[derive(Clone, Copy, Debug)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub struct AudioSettings {\n    /// Channel mute states.\n    muted: [bool; MAX_AUDIO_CHANNEL_COUNT],\n    /// Channel volume levels in 8.8 format (in dB).\n    volume_8q8_db: [i16; MAX_AUDIO_CHANNEL_COUNT],\n}\n\nimpl Default for AudioSettings {\n    fn default() -> Self {\n        AudioSettings {\n            muted: [false; MAX_AUDIO_CHANNEL_COUNT],\n            volume_8q8_db: [MAX_VOLUME_DB * VOLUME_STEPS_PER_DB; MAX_AUDIO_CHANNEL_COUNT],\n        }\n    }\n}\n\nstruct Control<'d> {\n    control_interface_number: InterfaceNumber,\n    streaming_endpoint_address: u8,\n    shared: &'d SharedControl<'d>,\n}\n\n/// Shared data between [`Control`] and the [`Speaker`] class.\nstruct SharedControl<'d> {\n    /// The collection of audio settings (volumes, mute states).\n    audio_settings: CriticalSectionMutex<Cell<AudioSettings>>,\n\n    /// Channel assignments.\n    channels: &'d [Channel],\n\n    /// The audio sample rate in Hz.\n    sample_rate_hz: AtomicU32,\n\n    // Notification mechanism.\n    waker: RefCell<WakerRegistration>,\n    changed: AtomicBool,\n}\n\nimpl<'d> Default for SharedControl<'d> {\n    fn default() -> Self {\n        SharedControl {\n            audio_settings: CriticalSectionMutex::new(Cell::new(AudioSettings::default())),\n            channels: &[],\n            sample_rate_hz: AtomicU32::new(0),\n            waker: RefCell::new(WakerRegistration::new()),\n            changed: AtomicBool::new(false),\n        }\n    }\n}\n\nimpl<'d> SharedControl<'d> {\n    fn changed(&self) -> impl Future<Output = ()> + '_ {\n        poll_fn(|context| {\n            if self.changed.load(Ordering::Relaxed) {\n                self.changed.store(false, Ordering::Relaxed);\n                Poll::Ready(())\n            } else {\n                self.waker.borrow_mut().register(context.waker());\n                Poll::Pending\n            }\n        })\n    }\n}\n\n/// Used for reading audio frames.\npub struct Stream<'d, D: Driver<'d>> {\n    streaming_endpoint: D::EndpointOut,\n}\n\nimpl<'d, D: Driver<'d>> Stream<'d, D> {\n    /// Reads a single packet from the OUT endpoint\n    pub async fn read_packet(&mut self, data: &mut [u8]) -> Result<usize, EndpointError> {\n        self.streaming_endpoint.read(data).await\n    }\n\n    /// Waits for the USB host to enable this interface\n    pub async fn wait_connection(&mut self) {\n        self.streaming_endpoint.wait_enabled().await;\n    }\n}\n\n/// Used for writing sample rate information over the feedback endpoint.\npub struct Feedback<'d, D: Driver<'d>> {\n    feedback_endpoint: D::EndpointIn,\n}\n\nimpl<'d, D: Driver<'d>> Feedback<'d, D> {\n    /// Writes a single packet into the IN endpoint.\n    pub async fn write_packet(&mut self, data: &[u8]) -> Result<(), EndpointError> {\n        self.feedback_endpoint.write(data).await\n    }\n\n    /// Waits for the USB host to enable this interface.\n    pub async fn wait_connection(&mut self) {\n        self.feedback_endpoint.wait_enabled().await;\n    }\n}\n\n/// Control status change monitor\n///\n/// Await [`ControlMonitor::changed`] for being notified of configuration changes. Afterwards, the updated\n/// configuration settings can be read with [`ControlMonitor::volume`] and [`ControlMonitor::sample_rate_hz`].\npub struct ControlMonitor<'d> {\n    shared: &'d SharedControl<'d>,\n}\n\nimpl<'d> ControlMonitor<'d> {\n    fn audio_settings(&self) -> AudioSettings {\n        let audio_settings = self.shared.audio_settings.lock(|x| x.get());\n\n        audio_settings\n    }\n\n    fn get_logical_channel(&self, search_channel: Channel) -> Option<usize> {\n        let index = self.shared.channels.iter().position(|&c| c == search_channel)?;\n\n        // The logical channels start at one (zero is the master channel).\n        Some(index + 1)\n    }\n\n    /// Get the volume of a selected channel.\n    pub fn volume(&self, channel: Channel) -> Option<Volume> {\n        let channel_index = self.get_logical_channel(channel)?;\n\n        if self.audio_settings().muted[channel_index] {\n            return Some(Volume::Muted);\n        }\n\n        Some(Volume::DeciBel(\n            (self.audio_settings().volume_8q8_db[channel_index] as f32) / 256.0f32,\n        ))\n    }\n\n    /// Get the streaming endpoint's sample rate in Hz.\n    pub fn sample_rate_hz(&self) -> u32 {\n        self.shared.sample_rate_hz.load(Ordering::Relaxed)\n    }\n\n    /// Return a future for when the control settings change.\n    pub async fn changed(&self) {\n        self.shared.changed().await;\n    }\n}\n\nimpl<'d> Control<'d> {\n    fn changed(&mut self) {\n        self.shared.changed.store(true, Ordering::Relaxed);\n        self.shared.waker.borrow_mut().wake();\n    }\n\n    fn interface_set_mute_state(\n        &mut self,\n        audio_settings: &mut AudioSettings,\n        channel_index: u8,\n        data: &[u8],\n    ) -> OutResponse {\n        let mute_state = data[0] != 0;\n\n        match channel_index as usize {\n            ..=MAX_AUDIO_CHANNEL_INDEX => {\n                audio_settings.muted[channel_index as usize] = mute_state;\n            }\n            _ => {\n                debug!(\"Failed to set channel {} mute state: {}\", channel_index, mute_state);\n                return OutResponse::Rejected;\n            }\n        }\n\n        debug!(\"Set channel {} mute state: {}\", channel_index, mute_state);\n        OutResponse::Accepted\n    }\n\n    fn interface_set_volume(\n        &mut self,\n        audio_settings: &mut AudioSettings,\n        channel_index: u8,\n        data: &[u8],\n    ) -> OutResponse {\n        let volume = i16::from_ne_bytes(data[..2].try_into().expect(\"Failed to read volume.\"));\n\n        match channel_index as usize {\n            ..=MAX_AUDIO_CHANNEL_INDEX => {\n                audio_settings.volume_8q8_db[channel_index as usize] = volume;\n            }\n            _ => {\n                debug!(\"Failed to set channel {} volume: {}\", channel_index, volume);\n                return OutResponse::Rejected;\n            }\n        }\n\n        debug!(\"Set channel {} volume: {}\", channel_index, volume);\n        OutResponse::Accepted\n    }\n\n    fn interface_set_request(&mut self, req: control::Request, data: &[u8]) -> Option<OutResponse> {\n        let interface_number = req.index as u8;\n        let entity_index = (req.index >> 8) as u8;\n        let channel_index = req.value as u8;\n        let control_unit = (req.value >> 8) as u8;\n\n        if interface_number != self.control_interface_number.into() {\n            debug!(\"Unhandled interface set request for interface {}\", interface_number);\n            return None;\n        }\n\n        if entity_index != FEATURE_UNIT_ID {\n            debug!(\"Unsupported interface set request for entity {}\", entity_index);\n            return Some(OutResponse::Rejected);\n        }\n\n        if req.request != SET_CUR {\n            debug!(\"Unsupported interface set request type {}\", req.request);\n            return Some(OutResponse::Rejected);\n        }\n\n        let mut audio_settings = self.shared.audio_settings.lock(|x| x.get());\n        let response = match control_unit {\n            MUTE_CONTROL => self.interface_set_mute_state(&mut audio_settings, channel_index, data),\n            VOLUME_CONTROL => self.interface_set_volume(&mut audio_settings, channel_index, data),\n            _ => OutResponse::Rejected,\n        };\n\n        if response == OutResponse::Rejected {\n            return Some(response);\n        }\n\n        // Store updated settings\n        self.shared.audio_settings.lock(|x| x.set(audio_settings));\n\n        self.changed();\n\n        Some(OutResponse::Accepted)\n    }\n\n    fn endpoint_set_request(&mut self, req: control::Request, data: &[u8]) -> Option<OutResponse> {\n        let control_selector = (req.value >> 8) as u8;\n        let endpoint_address = req.index as u8;\n\n        if endpoint_address != self.streaming_endpoint_address {\n            debug!(\n                \"Unhandled endpoint set request for endpoint {} and control {} with data {:?}\",\n                endpoint_address, control_selector, data\n            );\n            return None;\n        }\n\n        if control_selector != SAMPLING_FREQ_CONTROL {\n            debug!(\n                \"Unsupported endpoint set request for control selector {}\",\n                control_selector\n            );\n            return Some(OutResponse::Rejected);\n        }\n\n        let sample_rate_hz: u32 = (data[0] as u32) | (data[1] as u32) << 8 | (data[2] as u32) << 16;\n        self.shared.sample_rate_hz.store(sample_rate_hz, Ordering::Relaxed);\n\n        debug!(\"Set endpoint {} sample rate to {} Hz\", endpoint_address, sample_rate_hz);\n\n        self.changed();\n\n        Some(OutResponse::Accepted)\n    }\n\n    fn interface_get_request<'r>(&'r mut self, req: Request, buf: &'r mut [u8]) -> Option<InResponse<'r>> {\n        let interface_number = req.index as u8;\n        let entity_index = (req.index >> 8) as u8;\n        let channel_index = req.value as u8;\n        let control_unit = (req.value >> 8) as u8;\n\n        if interface_number != self.control_interface_number.into() {\n            debug!(\"Unhandled interface get request for interface {}.\", interface_number);\n            return None;\n        }\n\n        if entity_index != FEATURE_UNIT_ID {\n            // Only this function unit can be handled at the moment.\n            debug!(\"Unsupported interface get request for entity {}.\", entity_index);\n            return Some(InResponse::Rejected);\n        }\n\n        let audio_settings = self.shared.audio_settings.lock(|x| x.get());\n\n        match req.request {\n            GET_CUR => match control_unit {\n                VOLUME_CONTROL => {\n                    let volume: i16;\n\n                    match channel_index as usize {\n                        ..=MAX_AUDIO_CHANNEL_INDEX => volume = audio_settings.volume_8q8_db[channel_index as usize],\n                        _ => return Some(InResponse::Rejected),\n                    }\n\n                    buf[0] = volume as u8;\n                    buf[1] = (volume >> 8) as u8;\n\n                    debug!(\"Got channel {} volume: {}.\", channel_index, volume);\n                    return Some(InResponse::Accepted(&buf[..2]));\n                }\n                MUTE_CONTROL => {\n                    let mute_state: bool;\n\n                    match channel_index as usize {\n                        ..=MAX_AUDIO_CHANNEL_INDEX => mute_state = audio_settings.muted[channel_index as usize],\n                        _ => return Some(InResponse::Rejected),\n                    }\n\n                    buf[0] = mute_state.into();\n                    debug!(\"Got channel {} mute state: {}.\", channel_index, mute_state);\n                    return Some(InResponse::Accepted(&buf[..1]));\n                }\n                _ => return Some(InResponse::Rejected),\n            },\n            GET_MIN => match control_unit {\n                VOLUME_CONTROL => {\n                    let min_volume = MIN_VOLUME_DB * VOLUME_STEPS_PER_DB;\n                    buf[0] = min_volume as u8;\n                    buf[1] = (min_volume >> 8) as u8;\n                    return Some(InResponse::Accepted(&buf[..2]));\n                }\n                _ => return Some(InResponse::Rejected),\n            },\n            GET_MAX => match control_unit {\n                VOLUME_CONTROL => {\n                    let max_volume = MAX_VOLUME_DB * VOLUME_STEPS_PER_DB;\n                    buf[0] = max_volume as u8;\n                    buf[1] = (max_volume >> 8) as u8;\n                    return Some(InResponse::Accepted(&buf[..2]));\n                }\n                _ => return Some(InResponse::Rejected),\n            },\n            GET_RES => match control_unit {\n                VOLUME_CONTROL => {\n                    buf[0] = VOLUME_STEPS_PER_DB as u8;\n                    buf[1] = (VOLUME_STEPS_PER_DB >> 8) as u8;\n                    return Some(InResponse::Accepted(&buf[..2]));\n                }\n                _ => return Some(InResponse::Rejected),\n            },\n            _ => return Some(InResponse::Rejected),\n        }\n    }\n\n    fn endpoint_get_request<'r>(&'r mut self, req: Request, buf: &'r mut [u8]) -> Option<InResponse<'r>> {\n        let control_selector = (req.value >> 8) as u8;\n        let endpoint_address = req.index as u8;\n\n        if endpoint_address != self.streaming_endpoint_address {\n            debug!(\"Unhandled endpoint get request for endpoint {}.\", endpoint_address);\n            return None;\n        }\n\n        if control_selector != SAMPLING_FREQ_CONTROL as u8 {\n            debug!(\n                \"Unsupported endpoint get request for control selector {}.\",\n                control_selector\n            );\n            return Some(InResponse::Rejected);\n        }\n\n        let sample_rate_hz = self.shared.sample_rate_hz.load(Ordering::Relaxed);\n\n        buf[0] = (sample_rate_hz & 0xFF) as u8;\n        buf[1] = ((sample_rate_hz >> 8) & 0xFF) as u8;\n        buf[2] = ((sample_rate_hz >> 16) & 0xFF) as u8;\n\n        Some(InResponse::Accepted(&buf[..3]))\n    }\n}\n\nimpl<'d> Handler for Control<'d> {\n    /// Called when the USB device has been enabled or disabled.\n    fn enabled(&mut self, enabled: bool) {\n        debug!(\"USB device enabled: {}\", enabled);\n    }\n\n    /// Called when the host has set the address of the device to `addr`.\n    fn addressed(&mut self, addr: u8) {\n        debug!(\"Host set address to: {}\", addr);\n    }\n\n    /// Called when the host has enabled or disabled the configuration of the device.\n    fn configured(&mut self, configured: bool) {\n        debug!(\"USB device configured: {}\", configured);\n    }\n\n    /// Called when remote wakeup feature is enabled or disabled.\n    fn remote_wakeup_enabled(&mut self, enabled: bool) {\n        debug!(\"USB remote wakeup enabled: {}\", enabled);\n    }\n\n    /// Called when a \"set alternate setting\" control request is done on the interface.\n    fn set_alternate_setting(&mut self, iface: InterfaceNumber, alternate_setting: u8) {\n        debug!(\n            \"USB set interface number {} to alt setting {}.\",\n            iface, alternate_setting\n        );\n    }\n\n    /// Called after a USB reset after the bus reset sequence is complete.\n    fn reset(&mut self) {\n        let shared = self.shared;\n        shared.audio_settings.lock(|x| x.set(AudioSettings::default()));\n\n        shared.changed.store(true, Ordering::Relaxed);\n        shared.waker.borrow_mut().wake();\n    }\n\n    /// Called when the bus has entered or exited the suspend state.\n    fn suspended(&mut self, suspended: bool) {\n        debug!(\"USB device suspended: {}\", suspended);\n    }\n\n    // Handle control set requests.\n    fn control_out(&mut self, req: control::Request, data: &[u8]) -> Option<OutResponse> {\n        match req.request_type {\n            RequestType::Class => match req.recipient {\n                Recipient::Interface => self.interface_set_request(req, data),\n                Recipient::Endpoint => self.endpoint_set_request(req, data),\n                _ => Some(OutResponse::Rejected),\n            },\n            _ => None,\n        }\n    }\n\n    // Handle control get requests.\n    fn control_in<'a>(&'a mut self, req: Request, buf: &'a mut [u8]) -> Option<InResponse<'a>> {\n        match req.request_type {\n            RequestType::Class => match req.recipient {\n                Recipient::Interface => self.interface_get_request(req, buf),\n                Recipient::Endpoint => self.endpoint_get_request(req, buf),\n                _ => None,\n            },\n            _ => None,\n        }\n    }\n}\n"
  },
  {
    "path": "embassy-usb/src/class/uac1/terminal_type.rs",
    "content": "//! USB Audio Terminal Types from Universal Serial Bus Device Class Definition\n//! for Terminal Types, Release 1.0\n\n/// USB Audio Terminal Types from \"Universal Serial Bus Device Class Definition\n/// for Terminal Types, Release 1.0\"\n#[repr(u16)]\n#[non_exhaustive]\n#[derive(Copy, Clone, Eq, PartialEq, Debug)]\n#[allow(missing_docs)]\npub enum TerminalType {\n    // USB Terminal Types\n    UsbUndefined = 0x0100,\n    UsbStreaming = 0x0101,\n    UsbVendor = 0x01ff,\n\n    // Input Terminal Types\n    InUndefined = 0x0200,\n    InMicrophone = 0x0201,\n    InDesktopMicrophone = 0x0202,\n    InPersonalMicrophone = 0x0203,\n    InOmniDirectionalMicrophone = 0x0204,\n    InMicrophoneArray = 0x0205,\n    InProcessingMicrophoneArray = 0x0206,\n\n    // Output Terminal Types\n    OutUndefined = 0x0300,\n    OutSpeaker = 0x0301,\n    OutHeadphones = 0x0302,\n    OutHeadMountedDisplayAudio = 0x0303,\n    OutDesktopSpeaker = 0x0304,\n    OutRoomSpeaker = 0x0305,\n    OutCommunicationSpeaker = 0x0306,\n    OutLowFrequencyEffectsSpeaker = 0x0307,\n\n    // External Terminal Types\n    ExtUndefined = 0x0600,\n    ExtAnalogConnector = 0x0601,\n    ExtDigitalAudioInterface = 0x0602,\n    ExtLineConnector = 0x0603,\n    ExtLegacyAudioConnector = 0x0604,\n    ExtSpdifConnector = 0x0605,\n    Ext1394DaStream = 0x0606,\n    Ext1394DvStreamSoundtrack = 0x0607,\n}\n\nimpl From<TerminalType> for u16 {\n    fn from(t: TerminalType) -> u16 {\n        t as u16\n    }\n}\n"
  },
  {
    "path": "embassy-usb/src/class/web_usb.rs",
    "content": "//! WebUSB API capability implementation.\n//!\n//! See https://wicg.github.io/webusb\n\nuse core::mem::MaybeUninit;\n\nuse crate::control::{InResponse, Recipient, Request, RequestType};\nuse crate::descriptor::capability_type;\nuse crate::driver::Driver;\nuse crate::{Builder, Handler};\n\nconst USB_CLASS_VENDOR: u8 = 0xff;\nconst USB_SUBCLASS_NONE: u8 = 0x00;\nconst USB_PROTOCOL_NONE: u8 = 0x00;\n\nconst WEB_USB_REQUEST_GET_URL: u16 = 0x02;\nconst WEB_USB_DESCRIPTOR_TYPE_URL: u8 = 0x03;\n\n/// URL descriptor for WebUSB landing page.\n///\n/// An ecoded URL descriptor to point to a website that is suggested to the user when the device is connected.\npub struct Url<'d>(&'d str, u8);\n\nimpl<'d> Url<'d> {\n    /// Create a new WebUSB URL descriptor.\n    pub fn new(url: &'d str) -> Self {\n        let (prefix, stripped_url) = if let Some(stripped) = url.strip_prefix(\"https://\") {\n            (1, stripped)\n        } else if let Some(stripped) = url.strip_prefix(\"http://\") {\n            (0, stripped)\n        } else {\n            (255, url)\n        };\n        assert!(\n            stripped_url.len() <= 252,\n            \"URL too long. ({} bytes). Maximum length is 252 bytes.\",\n            stripped_url.len()\n        );\n        Self(stripped_url, prefix)\n    }\n\n    fn as_bytes(&self) -> &[u8] {\n        self.0.as_bytes()\n    }\n\n    fn scheme(&self) -> u8 {\n        self.1\n    }\n}\n\n/// Configuration for WebUSB.\npub struct Config<'d> {\n    /// Maximum packet size in bytes for the data endpoints.\n    ///\n    /// Valid values depend on the speed at which the bus is enumerated.\n    /// - low speed: 8\n    /// - full speed: 8, 16, 32, or 64\n    /// - high speed: 64\n    pub max_packet_size: u16,\n    /// URL to navigate to when the device is connected.\n    ///\n    /// If defined, shows a landing page which the device manufacturer would like the user to visit in order to control their device.\n    pub landing_url: Option<Url<'d>>,\n    /// Vendor code for the WebUSB request.\n    ///\n    /// This value defines the request id (bRequest) the device expects the host to use when issuing control transfers these requests. This can be an arbitrary u8 and is not to be confused with the USB Vendor ID.\n    pub vendor_code: u8,\n}\n\nstruct Control<'d> {\n    ep_buf: [u8; 128],\n    vendor_code: u8,\n    landing_url: Option<&'d Url<'d>>,\n}\n\nimpl<'d> Control<'d> {\n    fn new(config: &'d Config<'d>) -> Self {\n        Control {\n            ep_buf: [0u8; 128],\n            vendor_code: config.vendor_code,\n            landing_url: config.landing_url.as_ref(),\n        }\n    }\n}\n\nimpl<'d> Handler for Control<'d> {\n    fn control_in(&mut self, req: Request, _data: &mut [u8]) -> Option<InResponse<'_>> {\n        let landing_value = if self.landing_url.is_some() { 1 } else { 0 };\n        if req.request_type == RequestType::Vendor\n            && req.recipient == Recipient::Device\n            && req.request == self.vendor_code\n            && req.value == landing_value\n            && req.index == WEB_USB_REQUEST_GET_URL\n        {\n            if let Some(url) = self.landing_url {\n                let url_bytes = url.as_bytes();\n                let len = url_bytes.len();\n\n                self.ep_buf[0] = len as u8 + 3;\n                self.ep_buf[1] = WEB_USB_DESCRIPTOR_TYPE_URL;\n                self.ep_buf[2] = url.scheme();\n                self.ep_buf[3..3 + len].copy_from_slice(url_bytes);\n\n                return Some(InResponse::Accepted(&self.ep_buf[..3 + len]));\n            }\n        }\n        None\n    }\n}\n\n/// Internal state for WebUSB\npub struct State<'d> {\n    control: MaybeUninit<Control<'d>>,\n}\n\nimpl<'d> Default for State<'d> {\n    fn default() -> Self {\n        Self::new()\n    }\n}\n\nimpl<'d> State<'d> {\n    /// Create a new `State`.\n    pub const fn new() -> Self {\n        State {\n            control: MaybeUninit::uninit(),\n        }\n    }\n}\n\n/// WebUSB capability implementation.\n///\n/// WebUSB is a W3C standard that allows a web page to communicate with USB devices.\n/// See See https://wicg.github.io/webusb for more information and the browser API.\n/// This implementation provides one read and one write endpoint.\npub struct WebUsb<'d, D: Driver<'d>> {\n    _driver: core::marker::PhantomData<&'d D>,\n}\n\nimpl<'d, D: Driver<'d>> WebUsb<'d, D> {\n    /// Builder for the WebUSB capability implementation.\n    ///\n    /// Pass in a USB `Builder`, a `State`, which holds the control endpoint state, and a `Config` for the WebUSB configuration.\n    pub fn configure(builder: &mut Builder<'d, D>, state: &'d mut State<'d>, config: &'d Config<'d>) {\n        let mut func = builder.function(USB_CLASS_VENDOR, USB_SUBCLASS_NONE, USB_PROTOCOL_NONE);\n        let mut iface = func.interface();\n        let mut alt = iface.alt_setting(USB_CLASS_VENDOR, USB_SUBCLASS_NONE, USB_PROTOCOL_NONE, None);\n\n        alt.bos_capability(\n            capability_type::PLATFORM,\n            &[\n                // PlatformCapabilityUUID (3408b638-09a9-47a0-8bfd-a0768815b665)\n                0x0,\n                0x38,\n                0xb6,\n                0x08,\n                0x34,\n                0xa9,\n                0x09,\n                0xa0,\n                0x47,\n                0x8b,\n                0xfd,\n                0xa0,\n                0x76,\n                0x88,\n                0x15,\n                0xb6,\n                0x65,\n                // bcdVersion of WebUSB (1.0)\n                0x00,\n                0x01,\n                // bVendorCode\n                config.vendor_code,\n                // iLandingPage\n                if config.landing_url.is_some() { 1 } else { 0 },\n            ],\n        );\n\n        let control = state.control.write(Control::new(config));\n\n        drop(func);\n\n        builder.handler(control);\n    }\n}\n"
  },
  {
    "path": "embassy-usb/src/control.rs",
    "content": "//! USB control data types.\nuse core::mem;\n\nuse crate::driver::Direction;\n\n/// Control request type.\n#[repr(u8)]\n#[derive(Copy, Clone, Eq, PartialEq, Debug)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub enum RequestType {\n    /// Request is a USB standard request. Usually handled by\n    /// [`UsbDevice`](crate::UsbDevice).\n    Standard = 0,\n    /// Request is intended for a USB class.\n    Class = 1,\n    /// Request is vendor-specific.\n    Vendor = 2,\n    /// Reserved.\n    Reserved = 3,\n}\n\n/// Control request recipient.\n#[derive(Copy, Clone, Eq, PartialEq, Debug)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub enum Recipient {\n    /// Request is intended for the entire device.\n    Device = 0,\n    /// Request is intended for an interface. Generally, the `index` field of the request specifies\n    /// the interface number.\n    Interface = 1,\n    /// Request is intended for an endpoint. Generally, the `index` field of the request specifies\n    /// the endpoint address.\n    Endpoint = 2,\n    /// None of the above.\n    Other = 3,\n    /// Reserved.\n    Reserved = 4,\n}\n\n/// A control request read from a SETUP packet.\n#[derive(Copy, Clone, Eq, PartialEq, Debug)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub struct Request {\n    /// Direction of the request.\n    pub direction: Direction,\n    /// Type of the request.\n    pub request_type: RequestType,\n    /// Recipient of the request.\n    pub recipient: Recipient,\n    /// Request code. The meaning of the value depends on the previous fields.\n    pub request: u8,\n    /// Request value. The meaning of the value depends on the previous fields.\n    pub value: u16,\n    /// Request index. The meaning of the value depends on the previous fields.\n    pub index: u16,\n    /// Length of the DATA stage. For control OUT transfers this is the exact length of the data the\n    /// host sent. For control IN transfers this is the maximum length of data the device should\n    /// return.\n    pub length: u16,\n}\n\nimpl Request {\n    /// Standard USB control request Get Status\n    pub const GET_STATUS: u8 = 0;\n\n    /// Standard USB control request Clear Feature\n    pub const CLEAR_FEATURE: u8 = 1;\n\n    /// Standard USB control request Set Feature\n    pub const SET_FEATURE: u8 = 3;\n\n    /// Standard USB control request Set Address\n    pub const SET_ADDRESS: u8 = 5;\n\n    /// Standard USB control request Get Descriptor\n    pub const GET_DESCRIPTOR: u8 = 6;\n\n    /// Standard USB control request Set Descriptor\n    pub const SET_DESCRIPTOR: u8 = 7;\n\n    /// Standard USB control request Get Configuration\n    pub const GET_CONFIGURATION: u8 = 8;\n\n    /// Standard USB control request Set Configuration\n    pub const SET_CONFIGURATION: u8 = 9;\n\n    /// Standard USB control request Get Interface\n    pub const GET_INTERFACE: u8 = 10;\n\n    /// Standard USB control request Set Interface\n    pub const SET_INTERFACE: u8 = 11;\n\n    /// Standard USB control request Synch Frame\n    pub const SYNCH_FRAME: u8 = 12;\n\n    /// Standard USB feature Endpoint Halt for Set/Clear Feature\n    pub const FEATURE_ENDPOINT_HALT: u16 = 0;\n\n    /// Standard USB feature Device Remote Wakeup for Set/Clear Feature\n    pub const FEATURE_DEVICE_REMOTE_WAKEUP: u16 = 1;\n\n    /// Parses a USB control request from a byte array.\n    pub fn parse(buf: &[u8; 8]) -> Request {\n        let rt = buf[0];\n        let recipient = rt & 0b11111;\n\n        Request {\n            direction: if rt & 0x80 == 0 { Direction::Out } else { Direction::In },\n            request_type: unsafe { mem::transmute((rt >> 5) & 0b11) },\n            recipient: if recipient <= 3 {\n                unsafe { mem::transmute(recipient) }\n            } else {\n                Recipient::Reserved\n            },\n            request: buf[1],\n            value: (buf[2] as u16) | ((buf[3] as u16) << 8),\n            index: (buf[4] as u16) | ((buf[5] as u16) << 8),\n            length: (buf[6] as u16) | ((buf[7] as u16) << 8),\n        }\n    }\n\n    /// Gets the descriptor type and index from the value field of a GET_DESCRIPTOR request.\n    pub const fn descriptor_type_index(&self) -> (u8, u8) {\n        ((self.value >> 8) as u8, self.value as u8)\n    }\n}\n\n/// Response for a CONTROL OUT request.\n#[derive(Copy, Clone, Eq, PartialEq, Debug)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub enum OutResponse {\n    /// The request was accepted.\n    Accepted,\n    /// The request was rejected.\n    Rejected,\n}\n\n/// Response for a CONTROL IN request.\n#[derive(Copy, Clone, Eq, PartialEq, Debug)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub enum InResponse<'a> {\n    /// The request was accepted. The buffer contains the response data.\n    Accepted(&'a [u8]),\n    /// The request was rejected.\n    Rejected,\n}\n"
  },
  {
    "path": "embassy-usb/src/descriptor.rs",
    "content": "//! Utilities for writing USB descriptors.\nuse embassy_usb_driver::EndpointType;\n\nuse crate::CONFIGURATION_VALUE;\nuse crate::builder::Config;\nuse crate::driver::EndpointInfo;\nuse crate::types::{InterfaceNumber, StringIndex};\n\n/// Standard descriptor types\n#[allow(missing_docs)]\npub mod descriptor_type {\n    pub const DEVICE: u8 = 1;\n    pub const CONFIGURATION: u8 = 2;\n    pub const STRING: u8 = 3;\n    pub const INTERFACE: u8 = 4;\n    pub const ENDPOINT: u8 = 5;\n    pub const DEVICE_QUALIFIER: u8 = 6;\n    pub const OTHER_SPEED_CONFIGURATION: u8 = 7;\n    pub const IAD: u8 = 11;\n    pub const BOS: u8 = 15;\n    pub const CAPABILITY: u8 = 16;\n}\n\n/// String descriptor language IDs.\npub mod lang_id {\n    /// English (US)\n    ///\n    /// Recommended for use as the first language ID for compatibility.\n    pub const ENGLISH_US: u16 = 0x0409;\n}\n\n/// Standard capability descriptor types\n#[allow(missing_docs)]\npub mod capability_type {\n    pub const WIRELESS_USB: u8 = 1;\n    pub const USB_2_0_EXTENSION: u8 = 2;\n    pub const SS_USB_DEVICE: u8 = 3;\n    pub const CONTAINER_ID: u8 = 4;\n    pub const PLATFORM: u8 = 5;\n}\n\n/// USB endpoint synchronization type. The values of this enum can be directly\n/// cast into `u8` to get the bmAttributes synchronization type bits.\n/// Values other than `NoSynchronization` are only allowed on isochronous endpoints.\n#[repr(u8)]\n#[derive(Copy, Clone, Eq, PartialEq, Debug)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub enum SynchronizationType {\n    /// No synchronization is used.\n    NoSynchronization = 0b00,\n    /// Unsynchronized, although sinks provide data rate feedback.\n    Asynchronous = 0b01,\n    /// Synchronized using feedback or feedforward data rate information.\n    Adaptive = 0b10,\n    /// Synchronized to the USB’s SOF.\n    Synchronous = 0b11,\n}\n\n/// USB endpoint usage type. The values of this enum can be directly cast into\n/// `u8` to get the bmAttributes usage type bits.\n/// Values other than `DataEndpoint` are only allowed on isochronous endpoints.\n#[repr(u8)]\n#[derive(Copy, Clone, Eq, PartialEq, Debug)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub enum UsageType {\n    /// Use the endpoint for regular data transfer.\n    DataEndpoint = 0b00,\n    /// Endpoint conveys explicit feedback information for one or more data endpoints.\n    FeedbackEndpoint = 0b01,\n    /// A data endpoint that also serves as an implicit feedback endpoint for one or more data endpoints.\n    ImplicitFeedbackDataEndpoint = 0b10,\n    /// Reserved usage type.\n    Reserved = 0b11,\n}\n\n/// A writer for USB descriptors.\npub(crate) struct DescriptorWriter<'a> {\n    pub buf: &'a mut [u8],\n    position: usize,\n    num_interfaces_mark: Option<usize>,\n    num_endpoints_mark: Option<usize>,\n}\n\nimpl<'a> DescriptorWriter<'a> {\n    pub(crate) fn new(buf: &'a mut [u8]) -> Self {\n        DescriptorWriter {\n            buf,\n            position: 0,\n            num_interfaces_mark: None,\n            num_endpoints_mark: None,\n        }\n    }\n\n    pub fn into_buf(self) -> &'a mut [u8] {\n        &mut self.buf[..self.position]\n    }\n\n    /// Gets the current position in the buffer, i.e. the number of bytes written so far.\n    pub const fn position(&self) -> usize {\n        self.position\n    }\n\n    /// Writes an arbitrary (usually class-specific) descriptor with optional extra fields.\n    pub fn write(&mut self, descriptor_type: u8, descriptor: &[u8], extra_fields: &[u8]) {\n        let descriptor_length = descriptor.len();\n        let extra_fields_length = extra_fields.len();\n        let total_length = descriptor_length + extra_fields_length;\n\n        assert!(\n            (self.position + 2 + total_length) <= self.buf.len() && (total_length + 2) <= 255,\n            \"Descriptor buffer full\"\n        );\n\n        self.buf[self.position] = (total_length + 2) as u8;\n        self.buf[self.position + 1] = descriptor_type;\n\n        let start = self.position + 2;\n\n        self.buf[start..start + descriptor_length].copy_from_slice(descriptor);\n        self.buf[start + descriptor_length..start + total_length].copy_from_slice(extra_fields);\n\n        self.position = start + total_length;\n    }\n\n    pub(crate) fn configuration(&mut self, config: &Config) {\n        self.num_interfaces_mark = Some(self.position + 4);\n\n        self.write(\n            descriptor_type::CONFIGURATION,\n            &[\n                0,\n                0,                   // wTotalLength\n                0,                   // bNumInterfaces\n                CONFIGURATION_VALUE, // bConfigurationValue\n                0,                   // iConfiguration\n                0x80 | if config.self_powered { 0x40 } else { 0x00 }\n                    | if config.supports_remote_wakeup { 0x20 } else { 0x00 }, // bmAttributes\n                (config.max_power / 2) as u8, // bMaxPower\n            ],\n            &[],\n        );\n    }\n\n    #[allow(unused)]\n    pub(crate) fn end_class(&mut self) {\n        self.num_endpoints_mark = None;\n    }\n\n    pub(crate) fn end_configuration(&mut self) {\n        let position = self.position as u16;\n        self.buf[2..4].copy_from_slice(&position.to_le_bytes());\n    }\n\n    /// Writes a interface association descriptor. Call from `UsbClass::get_configuration_descriptors`\n    /// before writing the USB class or function's interface descriptors if your class has more than\n    /// one interface and wants to play nicely with composite devices on Windows. If the USB device\n    /// hosting the class was not configured as composite with IADs enabled, calling this function\n    /// does nothing, so it is safe to call from libraries.\n    ///\n    /// # Arguments\n    ///\n    /// * `first_interface` - Number of the function's first interface, previously allocated with\n    ///   [`UsbDeviceBuilder::interface`](crate::bus::UsbDeviceBuilder::interface).\n    /// * `interface_count` - Number of interfaces in the function.\n    /// * `function_class` - Class code assigned by USB.org. Use `0xff` for vendor-specific devices\n    ///   that do not conform to any class.\n    /// * `function_sub_class` - Sub-class code. Depends on class.\n    /// * `function_protocol` - Protocol code. Depends on class and sub-class.\n    pub fn iad(\n        &mut self,\n        first_interface: InterfaceNumber,\n        interface_count: u8,\n        function_class: u8,\n        function_sub_class: u8,\n        function_protocol: u8,\n    ) {\n        self.write(\n            descriptor_type::IAD,\n            &[\n                first_interface.into(), // bFirstInterface\n                interface_count,        // bInterfaceCount\n                function_class,\n                function_sub_class,\n                function_protocol,\n                0,\n            ],\n            &[],\n        );\n    }\n\n    /// Writes a interface descriptor with a specific alternate setting and\n    /// interface string identifier.\n    ///\n    /// # Arguments\n    ///\n    /// * `number` - Interface number previously allocated with\n    ///   [`UsbDeviceBuilder::interface`](crate::bus::UsbDeviceBuilder::interface).\n    /// * `alternate_setting` - Number of the alternate setting\n    /// * `interface_class` - Class code assigned by USB.org. Use `0xff` for vendor-specific devices\n    ///   that do not conform to any class.\n    /// * `interface_sub_class` - Sub-class code. Depends on class.\n    /// * `interface_protocol` - Protocol code. Depends on class and sub-class.\n    /// * `interface_string` - Index of string descriptor describing this interface\n\n    pub fn interface_alt(\n        &mut self,\n        number: InterfaceNumber,\n        alternate_setting: u8,\n        interface_class: u8,\n        interface_sub_class: u8,\n        interface_protocol: u8,\n        interface_string: Option<StringIndex>,\n    ) {\n        if alternate_setting == 0 {\n            match self.num_interfaces_mark {\n                Some(mark) => self.buf[mark] += 1,\n                None => {\n                    panic!(\"you can only call `interface/interface_alt` after `configuration`.\")\n                }\n            };\n        }\n\n        let str_index = interface_string.map_or(0, Into::into);\n\n        self.num_endpoints_mark = Some(self.position + 4);\n\n        self.write(\n            descriptor_type::INTERFACE,\n            &[\n                number.into(),       // bInterfaceNumber\n                alternate_setting,   // bAlternateSetting\n                0,                   // bNumEndpoints\n                interface_class,     // bInterfaceClass\n                interface_sub_class, // bInterfaceSubClass\n                interface_protocol,  // bInterfaceProtocol\n                str_index,           // iInterface\n            ],\n            &[],\n        );\n    }\n\n    /// Writes an endpoint descriptor.\n    ///\n    /// # Arguments\n    ///\n    /// * `endpoint` - Endpoint previously allocated with\n    ///   [`UsbDeviceBuilder`](crate::bus::UsbDeviceBuilder).\n    /// * `synchronization_type` - The synchronization type of the endpoint.\n    /// * `usage_type` - The usage type of the endpoint.\n    /// * `extra_fields` - Additional, class-specific entries at the end of the endpoint descriptor.\n    pub fn endpoint(\n        &mut self,\n        endpoint: &EndpointInfo,\n        synchronization_type: SynchronizationType,\n        usage_type: UsageType,\n        extra_fields: &[u8],\n    ) {\n        match self.num_endpoints_mark {\n            Some(mark) => self.buf[mark] += 1,\n            None => panic!(\"you can only call `endpoint` after `interface/interface_alt`.\"),\n        };\n\n        let mut bm_attributes = endpoint.ep_type as u8;\n\n        // Synchronization types other than `NoSynchronization`,\n        // and usage types other than `DataEndpoint`\n        // are only allowed for isochronous endpoints.\n        if endpoint.ep_type != EndpointType::Isochronous {\n            assert_eq!(synchronization_type, SynchronizationType::NoSynchronization);\n            assert_eq!(usage_type, UsageType::DataEndpoint);\n        } else {\n            if usage_type == UsageType::FeedbackEndpoint {\n                assert_eq!(synchronization_type, SynchronizationType::NoSynchronization)\n            }\n\n            let synchronization_bm_attibutes: u8 = (synchronization_type as u8) << 2;\n            let usage_bm_attibutes: u8 = (usage_type as u8) << 4;\n\n            bm_attributes |= usage_bm_attibutes | synchronization_bm_attibutes;\n        }\n\n        self.write(\n            descriptor_type::ENDPOINT,\n            &[\n                endpoint.addr.into(), // bEndpointAddress\n                bm_attributes,        // bmAttributes\n                endpoint.max_packet_size as u8,\n                (endpoint.max_packet_size >> 8) as u8, // wMaxPacketSize\n                endpoint.interval_ms,                  // bInterval\n            ],\n            extra_fields,\n        );\n    }\n\n    /// Writes a string descriptor.\n    #[allow(unused)]\n    pub(crate) fn string(&mut self, string: &str) {\n        let mut pos = self.position;\n\n        assert!(pos + 2 <= self.buf.len(), \"Descriptor buffer full\");\n\n        self.buf[pos] = 0; // length placeholder\n        self.buf[pos + 1] = descriptor_type::STRING;\n\n        pos += 2;\n\n        for c in string.encode_utf16() {\n            assert!(pos < self.buf.len(), \"Descriptor buffer full\");\n\n            self.buf[pos..pos + 2].copy_from_slice(&c.to_le_bytes());\n            pos += 2;\n        }\n\n        self.buf[self.position] = (pos - self.position) as u8;\n\n        self.position = pos;\n    }\n}\n\n/// Create a new Device Descriptor array.\n///\n/// All device descriptors are always 18 bytes, so there's no need for\n/// a variable-length buffer or DescriptorWriter.\npub(crate) fn device_descriptor(config: &Config) -> [u8; 18] {\n    [\n        18,   // bLength\n        0x01, // bDescriptorType\n        config.bcd_usb as u8,\n        (config.bcd_usb as u16 >> 8) as u8, // bcdUSB\n        config.device_class,                // bDeviceClass\n        config.device_sub_class,            // bDeviceSubClass\n        config.device_protocol,             // bDeviceProtocol\n        config.max_packet_size_0,           // bMaxPacketSize0\n        config.vendor_id as u8,\n        (config.vendor_id >> 8) as u8, // idVendor\n        config.product_id as u8,\n        (config.product_id >> 8) as u8, // idProduct\n        config.device_release as u8,\n        (config.device_release >> 8) as u8,    // bcdDevice\n        config.manufacturer.map_or(0, |_| 1),  // iManufacturer\n        config.product.map_or(0, |_| 2),       // iProduct\n        config.serial_number.map_or(0, |_| 3), // iSerialNumber\n        1,                                     // bNumConfigurations\n    ]\n}\n\n/// Create a new Device Qualifier Descriptor array.\n///\n/// All device qualifier descriptors are always 10 bytes, so there's no need for\n/// a variable-length buffer or DescriptorWriter.\npub(crate) fn device_qualifier_descriptor(config: &Config) -> [u8; 10] {\n    [\n        10,   // bLength\n        0x06, // bDescriptorType\n        config.bcd_usb as u8,\n        (config.bcd_usb as u16 >> 8) as u8, // bcdUSB\n        config.device_class,                // bDeviceClass\n        config.device_sub_class,            // bDeviceSubClass\n        config.device_protocol,             // bDeviceProtocol\n        config.max_packet_size_0,           // bMaxPacketSize0\n        1,                                  // bNumConfigurations\n        0,                                  // Reserved\n    ]\n}\n\n/// A writer for Binary Object Store descriptor.\npub struct BosWriter<'a> {\n    pub(crate) writer: DescriptorWriter<'a>,\n    num_caps_mark: Option<usize>,\n}\n\nimpl<'a> BosWriter<'a> {\n    pub(crate) const fn new(writer: DescriptorWriter<'a>) -> Self {\n        Self {\n            writer,\n            num_caps_mark: None,\n        }\n    }\n\n    pub(crate) fn bos(&mut self) {\n        if (self.writer.buf.len() - self.writer.position) < 5 {\n            return;\n        }\n        self.num_caps_mark = Some(self.writer.position + 4);\n        self.writer.write(\n            descriptor_type::BOS,\n            &[\n                0x00, 0x00, // wTotalLength\n                0x00, // bNumDeviceCaps\n            ],\n            &[],\n        );\n\n        self.capability(capability_type::USB_2_0_EXTENSION, &[0; 4]);\n    }\n\n    /// Writes capability descriptor to a BOS\n    ///\n    /// # Arguments\n    ///\n    /// * `capability_type` - Type of a capability\n    /// * `data` - Binary data of the descriptor\n    pub fn capability(&mut self, capability_type: u8, data: &[u8]) {\n        match self.num_caps_mark {\n            Some(mark) => self.writer.buf[mark] += 1,\n            None => panic!(\"called `capability` not between `bos` and `end_bos`.\"),\n        }\n\n        let mut start = self.writer.position;\n        let blen = data.len();\n\n        assert!(\n            (start + blen + 3) <= self.writer.buf.len() && (blen + 3) <= 255,\n            \"Descriptor buffer full\"\n        );\n\n        self.writer.buf[start] = (blen + 3) as u8;\n        self.writer.buf[start + 1] = descriptor_type::CAPABILITY;\n        self.writer.buf[start + 2] = capability_type;\n\n        start += 3;\n        self.writer.buf[start..start + blen].copy_from_slice(data);\n        self.writer.position = start + blen;\n    }\n\n    pub(crate) fn end_bos(&mut self) {\n        if self.writer.position == 0 {\n            return;\n        }\n        self.num_caps_mark = None;\n        let position = self.writer.position as u16;\n        self.writer.buf[2..4].copy_from_slice(&position.to_le_bytes());\n    }\n}\n"
  },
  {
    "path": "embassy-usb/src/descriptor_reader.rs",
    "content": "use crate::descriptor::descriptor_type;\nuse crate::driver::EndpointAddress;\nuse crate::types::InterfaceNumber;\n\n#[derive(Copy, Clone, PartialEq, Eq, Debug)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub struct ReadError;\n\npub struct Reader<'a> {\n    data: &'a [u8],\n}\n\nimpl<'a> Reader<'a> {\n    pub const fn new(data: &'a [u8]) -> Self {\n        Self { data }\n    }\n\n    pub const fn eof(&self) -> bool {\n        self.data.is_empty()\n    }\n\n    pub fn read<const N: usize>(&mut self) -> Result<[u8; N], ReadError> {\n        let n = self.data.get(0..N).ok_or(ReadError)?;\n        self.data = &self.data[N..];\n        Ok(n.try_into().unwrap())\n    }\n\n    pub fn read_u8(&mut self) -> Result<u8, ReadError> {\n        Ok(u8::from_le_bytes(self.read()?))\n    }\n    pub fn read_u16(&mut self) -> Result<u16, ReadError> {\n        Ok(u16::from_le_bytes(self.read()?))\n    }\n\n    pub fn read_slice(&mut self, len: usize) -> Result<&'a [u8], ReadError> {\n        let res = self.data.get(0..len).ok_or(ReadError)?;\n        self.data = &self.data[len..];\n        Ok(res)\n    }\n\n    pub fn read_descriptors(&mut self) -> DescriptorIter<'_, 'a> {\n        DescriptorIter { r: self }\n    }\n}\n\npub struct DescriptorIter<'a, 'b> {\n    r: &'a mut Reader<'b>,\n}\n\nimpl<'a, 'b> Iterator for DescriptorIter<'a, 'b> {\n    type Item = Result<(u8, Reader<'a>), ReadError>;\n\n    fn next(&mut self) -> Option<Self::Item> {\n        if self.r.eof() {\n            return None;\n        }\n\n        let len = match self.r.read_u8() {\n            Ok(x) => x,\n            Err(e) => return Some(Err(e)),\n        };\n        let type_ = match self.r.read_u8() {\n            Ok(x) => x,\n            Err(e) => return Some(Err(e)),\n        };\n        let data = match self.r.read_slice(len as usize - 2) {\n            Ok(x) => x,\n            Err(e) => return Some(Err(e)),\n        };\n\n        Some(Ok((type_, Reader::new(data))))\n    }\n}\n\n#[derive(Copy, Clone, PartialEq, Eq, Debug)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub struct EndpointInfo {\n    pub configuration: u8,\n    pub interface: InterfaceNumber,\n    pub interface_alt: u8,\n    pub ep_address: EndpointAddress,\n}\n\npub fn foreach_endpoint(data: &[u8], mut f: impl FnMut(EndpointInfo)) -> Result<(), ReadError> {\n    let mut ep = EndpointInfo {\n        configuration: 0,\n        interface: InterfaceNumber(0),\n        interface_alt: 0,\n        ep_address: EndpointAddress::from(0),\n    };\n    for res in Reader::new(data).read_descriptors() {\n        let (kind, mut r) = res?;\n        match kind {\n            descriptor_type::CONFIGURATION => {\n                let _total_length = r.read_u16()?;\n                let _total_length = r.read_u8()?;\n                ep.configuration = r.read_u8()?;\n            }\n            descriptor_type::INTERFACE => {\n                ep.interface = InterfaceNumber(r.read_u8()?);\n                ep.interface_alt = r.read_u8()?;\n            }\n            descriptor_type::ENDPOINT => {\n                ep.ep_address = EndpointAddress::from(r.read_u8()?);\n                f(ep);\n            }\n            _ => {}\n        }\n    }\n    Ok(())\n}\n"
  },
  {
    "path": "embassy-usb/src/fmt.rs",
    "content": "#![macro_use]\n#![allow(unused)]\n\nuse core::fmt::{Debug, Display, LowerHex};\n\n#[cfg(all(feature = \"defmt\", feature = \"log\"))]\ncompile_error!(\"You may not enable both `defmt` and `log` features.\");\n\n#[collapse_debuginfo(yes)]\nmacro_rules! assert {\n    ($($x:tt)*) => {\n        {\n            #[cfg(not(feature = \"defmt\"))]\n            ::core::assert!($($x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::assert!($($x)*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! assert_eq {\n    ($($x:tt)*) => {\n        {\n            #[cfg(not(feature = \"defmt\"))]\n            ::core::assert_eq!($($x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::assert_eq!($($x)*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! assert_ne {\n    ($($x:tt)*) => {\n        {\n            #[cfg(not(feature = \"defmt\"))]\n            ::core::assert_ne!($($x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::assert_ne!($($x)*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! debug_assert {\n    ($($x:tt)*) => {\n        {\n            #[cfg(not(feature = \"defmt\"))]\n            ::core::debug_assert!($($x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::debug_assert!($($x)*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! debug_assert_eq {\n    ($($x:tt)*) => {\n        {\n            #[cfg(not(feature = \"defmt\"))]\n            ::core::debug_assert_eq!($($x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::debug_assert_eq!($($x)*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! debug_assert_ne {\n    ($($x:tt)*) => {\n        {\n            #[cfg(not(feature = \"defmt\"))]\n            ::core::debug_assert_ne!($($x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::debug_assert_ne!($($x)*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! todo {\n    ($($x:tt)*) => {\n        {\n            #[cfg(not(feature = \"defmt\"))]\n            ::core::todo!($($x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::todo!($($x)*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! unreachable {\n    ($($x:tt)*) => {\n        {\n            #[cfg(not(feature = \"defmt\"))]\n            ::core::unreachable!($($x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::unreachable!($($x)*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! panic {\n    ($($x:tt)*) => {\n        {\n            #[cfg(not(feature = \"defmt\"))]\n            ::core::panic!($($x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::panic!($($x)*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! trace {\n    ($s:literal $(, $x:expr)* $(,)?) => {\n        {\n            #[cfg(feature = \"log\")]\n            ::log::trace!($s $(, $x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::trace!($s $(, $x)*);\n            #[cfg(not(any(feature = \"log\", feature=\"defmt\")))]\n            let _ = ($( & $x ),*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! debug {\n    ($s:literal $(, $x:expr)* $(,)?) => {\n        {\n            #[cfg(feature = \"log\")]\n            ::log::debug!($s $(, $x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::debug!($s $(, $x)*);\n            #[cfg(not(any(feature = \"log\", feature=\"defmt\")))]\n            let _ = ($( & $x ),*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! info {\n    ($s:literal $(, $x:expr)* $(,)?) => {\n        {\n            #[cfg(feature = \"log\")]\n            ::log::info!($s $(, $x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::info!($s $(, $x)*);\n            #[cfg(not(any(feature = \"log\", feature=\"defmt\")))]\n            let _ = ($( & $x ),*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! warn {\n    ($s:literal $(, $x:expr)* $(,)?) => {\n        {\n            #[cfg(feature = \"log\")]\n            ::log::warn!($s $(, $x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::warn!($s $(, $x)*);\n            #[cfg(not(any(feature = \"log\", feature=\"defmt\")))]\n            let _ = ($( & $x ),*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! error {\n    ($s:literal $(, $x:expr)* $(,)?) => {\n        {\n            #[cfg(feature = \"log\")]\n            ::log::error!($s $(, $x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::error!($s $(, $x)*);\n            #[cfg(not(any(feature = \"log\", feature=\"defmt\")))]\n            let _ = ($( & $x ),*);\n        }\n    };\n}\n\n#[cfg(feature = \"defmt\")]\n#[collapse_debuginfo(yes)]\nmacro_rules! unwrap {\n    ($($x:tt)*) => {\n        ::defmt::unwrap!($($x)*)\n    };\n}\n\n#[cfg(not(feature = \"defmt\"))]\n#[collapse_debuginfo(yes)]\nmacro_rules! unwrap {\n    ($arg:expr) => {\n        match $crate::fmt::Try::into_result($arg) {\n            ::core::result::Result::Ok(t) => t,\n            ::core::result::Result::Err(e) => {\n                ::core::panic!(\"unwrap of `{}` failed: {:?}\", ::core::stringify!($arg), e);\n            }\n        }\n    };\n    ($arg:expr, $($msg:expr),+ $(,)? ) => {\n        match $crate::fmt::Try::into_result($arg) {\n            ::core::result::Result::Ok(t) => t,\n            ::core::result::Result::Err(e) => {\n                ::core::panic!(\"unwrap of `{}` failed: {}: {:?}\", ::core::stringify!($arg), ::core::format_args!($($msg,)*), e);\n            }\n        }\n    }\n}\n\n#[derive(Debug, Copy, Clone, Eq, PartialEq)]\npub struct NoneError;\n\npub trait Try {\n    type Ok;\n    type Error;\n    fn into_result(self) -> Result<Self::Ok, Self::Error>;\n}\n\nimpl<T> Try for Option<T> {\n    type Ok = T;\n    type Error = NoneError;\n\n    #[inline]\n    fn into_result(self) -> Result<T, NoneError> {\n        self.ok_or(NoneError)\n    }\n}\n\nimpl<T, E> Try for Result<T, E> {\n    type Ok = T;\n    type Error = E;\n\n    #[inline]\n    fn into_result(self) -> Self {\n        self\n    }\n}\n\npub(crate) struct Bytes<'a>(pub &'a [u8]);\n\nimpl<'a> Debug for Bytes<'a> {\n    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {\n        write!(f, \"{:#02x?}\", self.0)\n    }\n}\n\nimpl<'a> Display for Bytes<'a> {\n    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {\n        write!(f, \"{:#02x?}\", self.0)\n    }\n}\n\nimpl<'a> LowerHex for Bytes<'a> {\n    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {\n        write!(f, \"{:#02x?}\", self.0)\n    }\n}\n\n#[cfg(feature = \"defmt\")]\nimpl<'a> defmt::Format for Bytes<'a> {\n    fn format(&self, fmt: defmt::Formatter) {\n        defmt::write!(fmt, \"{:02x}\", self.0)\n    }\n}\n"
  },
  {
    "path": "embassy-usb/src/lib.rs",
    "content": "#![no_std]\n#![allow(unsafe_op_in_unsafe_fn)]\n#![doc = include_str!(\"../README.md\")]\n#![warn(missing_docs)]\n\n// This mod MUST go first, so that the others see its macros.\npub(crate) mod fmt;\n\npub use embassy_usb_driver as driver;\n\nmod builder;\npub mod class;\npub mod control;\npub mod descriptor;\nmod descriptor_reader;\npub mod msos;\npub mod types;\n\nmod config {\n    #![allow(unused)]\n    include!(concat!(env!(\"OUT_DIR\"), \"/config.rs\"));\n}\n\nuse embassy_futures::select::{Either, select};\nuse heapless::Vec;\n\npub use crate::builder::{Builder, Config, FunctionBuilder, InterfaceAltBuilder, InterfaceBuilder, UsbVersion};\nuse crate::config::{MAX_HANDLER_COUNT, MAX_INTERFACE_COUNT};\nuse crate::control::{InResponse, OutResponse, Recipient, Request, RequestType};\nuse crate::descriptor::{descriptor_type, lang_id};\nuse crate::descriptor_reader::foreach_endpoint;\nuse crate::driver::{Bus, ControlPipe, Direction, Driver, EndpointAddress, Event};\nuse crate::types::{InterfaceNumber, StringIndex};\n\n/// The global state of the USB device.\n///\n/// In general class traffic is only possible in the `Configured` state.\n#[repr(u8)]\n#[derive(PartialEq, Eq, Copy, Clone, Debug)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub enum UsbDeviceState {\n    /// The USB device has no power.\n    Unpowered,\n\n    /// The USB device is disabled.\n    Disabled,\n\n    /// The USB device has just been enabled or reset.\n    Default,\n\n    /// The USB device has received an address from the host.\n    Addressed,\n\n    /// The USB device has been configured and is fully functional.\n    Configured,\n}\n\n/// Error returned by [`UsbDevice::remote_wakeup`].\n#[derive(PartialEq, Eq, Copy, Clone, Debug)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub enum RemoteWakeupError {\n    /// The USB device is not suspended, or remote wakeup was not enabled.\n    InvalidState,\n    /// The underlying driver doesn't support remote wakeup.\n    Unsupported,\n}\n\nimpl From<driver::Unsupported> for RemoteWakeupError {\n    fn from(_: driver::Unsupported) -> Self {\n        RemoteWakeupError::Unsupported\n    }\n}\n\n/// The bConfiguration value for the not configured state.\npub const CONFIGURATION_NONE: u8 = 0;\n\n/// The bConfiguration value for the single configuration supported by this device.\npub const CONFIGURATION_VALUE: u8 = 1;\n\nconst STRING_INDEX_MANUFACTURER: u8 = 1;\nconst STRING_INDEX_PRODUCT: u8 = 2;\nconst STRING_INDEX_SERIAL_NUMBER: u8 = 3;\nconst STRING_INDEX_CUSTOM_START: u8 = 4;\n\n/// Handler for device events and control requests.\n///\n/// All methods are optional callbacks that will be called by\n/// [`UsbDevice::run()`](crate::UsbDevice::run)\npub trait Handler {\n    /// Called when the USB device has been enabled or disabled.\n    fn enabled(&mut self, _enabled: bool) {}\n\n    /// Called after a USB reset after the bus reset sequence is complete.\n    fn reset(&mut self) {}\n\n    /// Called when the host has set the address of the device to `addr`.\n    fn addressed(&mut self, _addr: u8) {}\n\n    /// Called when the host has enabled or disabled the configuration of the device.\n    fn configured(&mut self, _configured: bool) {}\n\n    /// Called when the bus has entered or exited the suspend state.\n    fn suspended(&mut self, _suspended: bool) {}\n\n    /// Called when remote wakeup feature is enabled or disabled.\n    fn remote_wakeup_enabled(&mut self, _enabled: bool) {}\n\n    /// Called when a \"set alternate setting\" control request is done on the interface.\n    fn set_alternate_setting(&mut self, iface: InterfaceNumber, alternate_setting: u8) {\n        let _ = iface;\n        let _ = alternate_setting;\n    }\n\n    /// Called when a control request is received with direction HostToDevice.\n    ///\n    /// # Arguments\n    ///\n    /// * `req` - The request from the SETUP packet.\n    /// * `data` - The data from the request.\n    ///\n    /// # Returns\n    ///\n    /// If you didn't handle this request (for example if it's for the wrong interface), return\n    /// `None`. In this case, the USB stack will continue calling the other handlers, to see\n    /// if another handles it.\n    ///\n    /// If you did, return `Some` with either `Accepted` or `Rejected`. This will make the USB stack\n    /// respond to the control request, and stop calling other handlers.\n    fn control_out(&mut self, req: Request, data: &[u8]) -> Option<OutResponse> {\n        let _ = (req, data);\n        None\n    }\n\n    /// Called when a control request is received with direction DeviceToHost.\n    ///\n    /// You should write the response somewhere (usually to `buf`, but you may use another buffer\n    /// owned by yourself, or a static buffer), then return `InResponse::Accepted(data)`.\n    ///\n    /// # Arguments\n    ///\n    /// * `req` - The request from the SETUP packet.\n    ///\n    /// # Returns\n    ///\n    /// If you didn't handle this request (for example if it's for the wrong interface), return\n    /// `None`. In this case, the USB stack will continue calling the other handlers, to see\n    /// if another handles it.\n    ///\n    /// If you did, return `Some` with either `Accepted` or `Rejected`. This will make the USB stack\n    /// respond to the control request, and stop calling other handlers.\n    fn control_in<'a>(&'a mut self, req: Request, buf: &'a mut [u8]) -> Option<InResponse<'a>> {\n        let _ = (req, buf);\n        None\n    }\n\n    /// Called when a GET_DESCRIPTOR STRING control request is received.\n    fn get_string(&mut self, index: StringIndex, lang_id: u16) -> Option<&str> {\n        let _ = (index, lang_id);\n        None\n    }\n}\n\nstruct Interface {\n    current_alt_setting: u8,\n    num_alt_settings: u8,\n}\n\n/// A report of the used size of the runtime allocated buffers\n#[derive(PartialEq, Eq, Copy, Clone, Debug)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub struct UsbBufferReport {\n    /// Number of config descriptor bytes used\n    pub config_descriptor_used: usize,\n    /// Number of bos descriptor bytes used\n    pub bos_descriptor_used: usize,\n    /// Number of msos descriptor bytes used\n    pub msos_descriptor_used: usize,\n    /// Size of the control buffer\n    pub control_buffer_size: usize,\n}\n\n/// Main struct for the USB device stack.\npub struct UsbDevice<'d, D: Driver<'d>> {\n    control_buf: &'d mut [u8],\n    control: D::ControlPipe,\n    inner: Inner<'d, D>,\n}\n\nstruct Inner<'d, D: Driver<'d>> {\n    bus: D::Bus,\n\n    config: Config<'d>,\n    device_descriptor: [u8; 18],\n    device_qualifier_descriptor: [u8; 10],\n    config_descriptor: &'d [u8],\n    bos_descriptor: &'d [u8],\n    msos_descriptor: crate::msos::MsOsDescriptorSet<'d>,\n\n    device_state: UsbDeviceState,\n    suspended: bool,\n    remote_wakeup_enabled: bool,\n    self_powered: bool,\n\n    /// Our device address, or 0 if none.\n    address: u8,\n    /// SET_ADDRESS requests have special handling depending on the driver.\n    /// This flag indicates that requests must be handled by `ControlPipe::accept_set_address()`\n    /// instead of regular `accept()`.\n    set_address_pending: bool,\n\n    interfaces: Vec<Interface, MAX_INTERFACE_COUNT>,\n    handlers: Vec<&'d mut dyn Handler, MAX_HANDLER_COUNT>,\n}\n\nimpl<'d, D: Driver<'d>> UsbDevice<'d, D> {\n    pub(crate) fn build(\n        driver: D,\n        config: Config<'d>,\n        handlers: Vec<&'d mut dyn Handler, MAX_HANDLER_COUNT>,\n        config_descriptor: &'d [u8],\n        bos_descriptor: &'d [u8],\n        msos_descriptor: crate::msos::MsOsDescriptorSet<'d>,\n        interfaces: Vec<Interface, MAX_INTERFACE_COUNT>,\n        control_buf: &'d mut [u8],\n    ) -> UsbDevice<'d, D> {\n        // Start the USB bus.\n        // This prevent further allocation by consuming the driver.\n        let (bus, control) = driver.start(config.max_packet_size_0 as u16);\n        let device_descriptor = descriptor::device_descriptor(&config);\n        let device_qualifier_descriptor = descriptor::device_qualifier_descriptor(&config);\n\n        Self {\n            control_buf,\n            control,\n            inner: Inner {\n                bus,\n                config,\n                device_descriptor,\n                device_qualifier_descriptor,\n                config_descriptor,\n                bos_descriptor,\n                msos_descriptor,\n\n                device_state: UsbDeviceState::Unpowered,\n                suspended: false,\n                remote_wakeup_enabled: false,\n                self_powered: false,\n                address: 0,\n                set_address_pending: false,\n                interfaces,\n                handlers,\n            },\n        }\n    }\n\n    /// Returns a report of the consumed buffers\n    ///\n    /// Useful for tuning buffer sizes for actual usage\n    pub fn buffer_usage(&self) -> UsbBufferReport {\n        UsbBufferReport {\n            config_descriptor_used: self.inner.config_descriptor.len(),\n            bos_descriptor_used: self.inner.bos_descriptor.len(),\n            msos_descriptor_used: self.inner.msos_descriptor.len(),\n            control_buffer_size: self.control_buf.len(),\n        }\n    }\n\n    /// Runs the `UsbDevice` forever.\n    ///\n    /// This future may leave the bus in an invalid state if it is dropped.\n    /// After dropping the future, [`UsbDevice::disable()`] should be called\n    /// before calling any other `UsbDevice` methods to fully reset the\n    /// peripheral.\n    pub async fn run(&mut self) -> ! {\n        loop {\n            self.run_until_suspend().await;\n            self.wait_resume().await;\n        }\n    }\n\n    /// Runs the `UsbDevice` until the bus is suspended.\n    ///\n    /// This future may leave the bus in an invalid state if it is dropped.\n    /// After dropping the future, [`UsbDevice::disable()`] should be called\n    /// before calling any other `UsbDevice` methods to fully reset the\n    /// peripheral.\n    pub async fn run_until_suspend(&mut self) {\n        while !self.inner.suspended {\n            let control_fut = self.control.setup();\n            let bus_fut = self.inner.bus.poll();\n            match select(bus_fut, control_fut).await {\n                Either::First(evt) => self.inner.handle_bus_event(evt).await,\n                Either::Second(req) => self.handle_control(req).await,\n            }\n        }\n    }\n\n    /// Disables the USB peripheral.\n    pub async fn disable(&mut self) {\n        if self.inner.device_state != UsbDeviceState::Disabled {\n            self.inner.bus.disable().await;\n            self.inner.device_state = UsbDeviceState::Disabled;\n            self.inner.suspended = false;\n            self.inner.remote_wakeup_enabled = false;\n\n            for h in &mut self.inner.handlers {\n                h.enabled(false);\n            }\n        }\n    }\n\n    /// Waits for a resume condition on the USB bus.\n    ///\n    /// This future is cancel-safe.\n    pub async fn wait_resume(&mut self) {\n        while self.inner.suspended {\n            let evt = self.inner.bus.poll().await;\n            self.inner.handle_bus_event(evt).await;\n        }\n    }\n\n    /// Initiates a device remote wakeup on the USB bus.\n    ///\n    /// If the bus is not suspended or remote wakeup is not enabled, an error\n    /// will be returned.\n    ///\n    /// This future may leave the bus in an inconsistent state if dropped.\n    /// After dropping the future, [`UsbDevice::disable()`] should be called\n    /// before calling any other `UsbDevice` methods to fully reset the peripheral.\n    pub async fn remote_wakeup(&mut self) -> Result<(), RemoteWakeupError> {\n        if self.inner.suspended && self.inner.remote_wakeup_enabled {\n            self.inner.bus.remote_wakeup().await?;\n            self.inner.suspended = false;\n\n            for h in &mut self.inner.handlers {\n                h.suspended(false);\n            }\n\n            Ok(())\n        } else {\n            Err(RemoteWakeupError::InvalidState)\n        }\n    }\n\n    async fn handle_control(&mut self, req: [u8; 8]) {\n        let req = Request::parse(&req);\n\n        trace!(\"control request: {:?}\", req);\n\n        match req.direction {\n            Direction::In => self.handle_control_in(req).await,\n            Direction::Out => self.handle_control_out(req).await,\n        }\n    }\n\n    async fn handle_control_in(&mut self, req: Request) {\n        const DEVICE_DESCRIPTOR_LEN: usize = 18;\n\n        let mut resp_length = req.length as usize;\n        let max_packet_size = self.control.max_packet_size();\n\n        // If we don't have an address yet, respond with max 1 packet.\n        // The host doesn't know our EP0 max packet size yet, and might assume\n        // a full-length packet is a short packet, thinking we're done sending data.\n        // See https://github.com/hathach/tinyusb/issues/184\n        if self.inner.address == 0 && max_packet_size < DEVICE_DESCRIPTOR_LEN && max_packet_size < resp_length {\n            trace!(\"received control req while not addressed: capping response to 1 packet.\");\n            resp_length = max_packet_size;\n        }\n\n        match self.inner.handle_control_in(req, self.control_buf) {\n            InResponse::Accepted(data) => {\n                let len = data.len().min(resp_length);\n                let need_zlp = len != resp_length && (len % max_packet_size) == 0;\n\n                let chunks = data[0..len]\n                    .chunks(max_packet_size)\n                    .chain(need_zlp.then(|| -> &[u8] { &[] }));\n\n                for (first, last, chunk) in first_last(chunks) {\n                    match self.control.data_in(chunk, first, last).await {\n                        Ok(()) => {}\n                        Err(e) => {\n                            warn!(\"control accept_in failed: {:?}\", e);\n                            return;\n                        }\n                    }\n                }\n            }\n            InResponse::Rejected => self.control.reject().await,\n        }\n    }\n\n    async fn handle_control_out(&mut self, req: Request) {\n        let req_length = req.length as usize;\n        let max_packet_size = self.control.max_packet_size();\n        let mut total = 0;\n\n        if req_length > self.control_buf.len() {\n            warn!(\n                \"got CONTROL OUT with length {} higher than the control_buf len {}, rejecting.\",\n                req_length,\n                self.control_buf.len()\n            );\n            self.control.reject().await;\n            return;\n        }\n\n        let chunks = self.control_buf[..req_length].chunks_mut(max_packet_size);\n        for (first, last, chunk) in first_last(chunks) {\n            let size = match self.control.data_out(chunk, first, last).await {\n                Ok(x) => x,\n                Err(e) => {\n                    warn!(\"usb: failed to read CONTROL OUT data stage: {:?}\", e);\n                    return;\n                }\n            };\n            total += size;\n            if size < max_packet_size || total == req_length {\n                break;\n            }\n        }\n\n        let data = &self.control_buf[0..total];\n        #[cfg(feature = \"defmt\")]\n        trace!(\"  control out data: {:02x}\", data);\n        #[cfg(not(feature = \"defmt\"))]\n        trace!(\"  control out data: {:02x?}\", data);\n\n        match self.inner.handle_control_out(req, data) {\n            OutResponse::Accepted => {\n                if self.inner.set_address_pending {\n                    self.control.accept_set_address(self.inner.address).await;\n                    self.inner.set_address_pending = false;\n                } else {\n                    self.control.accept().await;\n                }\n            }\n            OutResponse::Rejected => self.control.reject().await,\n        }\n    }\n}\n\nimpl<'d, D: Driver<'d>> Inner<'d, D> {\n    async fn handle_bus_event(&mut self, evt: Event) {\n        match evt {\n            Event::Reset => {\n                trace!(\"usb: reset\");\n                self.device_state = UsbDeviceState::Default;\n                self.suspended = false;\n                self.remote_wakeup_enabled = false;\n                self.address = 0;\n\n                for h in &mut self.handlers {\n                    h.reset();\n                }\n\n                for (i, iface) in self.interfaces.iter_mut().enumerate() {\n                    iface.current_alt_setting = 0;\n\n                    for h in &mut self.handlers {\n                        h.set_alternate_setting(InterfaceNumber::new(i as _), 0);\n                    }\n                }\n            }\n            Event::Resume => {\n                trace!(\"usb: resume\");\n                self.suspended = false;\n                for h in &mut self.handlers {\n                    h.suspended(false);\n                }\n            }\n            Event::Suspend => {\n                trace!(\"usb: suspend\");\n                self.suspended = true;\n                for h in &mut self.handlers {\n                    h.suspended(true);\n                }\n            }\n            Event::PowerDetected => {\n                trace!(\"usb: power detected\");\n                self.bus.enable().await;\n                self.device_state = UsbDeviceState::Default;\n\n                for h in &mut self.handlers {\n                    h.enabled(true);\n                }\n            }\n            Event::PowerRemoved => {\n                trace!(\"usb: power removed\");\n                self.bus.disable().await;\n                self.device_state = UsbDeviceState::Unpowered;\n\n                for h in &mut self.handlers {\n                    h.enabled(false);\n                }\n            }\n        }\n    }\n\n    fn handle_control_out(&mut self, req: Request, data: &[u8]) -> OutResponse {\n        const CONFIGURATION_NONE_U16: u16 = CONFIGURATION_NONE as u16;\n        const CONFIGURATION_VALUE_U16: u16 = CONFIGURATION_VALUE as u16;\n\n        match (req.request_type, req.recipient) {\n            (RequestType::Standard, Recipient::Device) => match (req.request, req.value) {\n                (Request::CLEAR_FEATURE, Request::FEATURE_DEVICE_REMOTE_WAKEUP) => {\n                    self.remote_wakeup_enabled = false;\n                    for h in &mut self.handlers {\n                        h.remote_wakeup_enabled(false);\n                    }\n                    OutResponse::Accepted\n                }\n                (Request::SET_FEATURE, Request::FEATURE_DEVICE_REMOTE_WAKEUP) => {\n                    self.remote_wakeup_enabled = true;\n                    for h in &mut self.handlers {\n                        h.remote_wakeup_enabled(true);\n                    }\n                    OutResponse::Accepted\n                }\n                (Request::SET_ADDRESS, addr @ 1..=127) => {\n                    self.address = addr as u8;\n                    self.set_address_pending = true;\n                    self.device_state = UsbDeviceState::Addressed;\n                    for h in &mut self.handlers {\n                        h.addressed(self.address);\n                    }\n                    OutResponse::Accepted\n                }\n                (Request::SET_CONFIGURATION, CONFIGURATION_VALUE_U16) => {\n                    debug!(\"SET_CONFIGURATION: configured\");\n                    self.device_state = UsbDeviceState::Configured;\n\n                    // Enable all endpoints of selected alt settings.\n                    foreach_endpoint(self.config_descriptor, |ep| {\n                        let iface = &self.interfaces[ep.interface.0 as usize];\n                        self.bus\n                            .endpoint_set_enabled(ep.ep_address, iface.current_alt_setting == ep.interface_alt);\n                    })\n                    .unwrap();\n\n                    // Notify handlers.\n                    for h in &mut self.handlers {\n                        h.configured(true);\n                    }\n\n                    OutResponse::Accepted\n                }\n                (Request::SET_CONFIGURATION, CONFIGURATION_NONE_U16) => {\n                    if self.device_state != UsbDeviceState::Default {\n                        debug!(\"SET_CONFIGURATION: unconfigured\");\n                        self.device_state = UsbDeviceState::Addressed;\n\n                        // Disable all endpoints.\n                        foreach_endpoint(self.config_descriptor, |ep| {\n                            self.bus.endpoint_set_enabled(ep.ep_address, false);\n                        })\n                        .unwrap();\n\n                        // Notify handlers.\n                        for h in &mut self.handlers {\n                            h.configured(false);\n                        }\n                    }\n                    OutResponse::Accepted\n                }\n                _ => OutResponse::Rejected,\n            },\n            (RequestType::Standard, Recipient::Interface) => {\n                let iface_num = InterfaceNumber::new(req.index as _);\n                let Some(iface) = self.interfaces.get_mut(iface_num.0 as usize) else {\n                    return OutResponse::Rejected;\n                };\n\n                match req.request {\n                    Request::SET_INTERFACE => {\n                        let new_altsetting = req.value as u8;\n\n                        if new_altsetting >= iface.num_alt_settings {\n                            warn!(\"SET_INTERFACE: trying to select alt setting out of range.\");\n                            return OutResponse::Rejected;\n                        }\n\n                        iface.current_alt_setting = new_altsetting;\n\n                        // Enable/disable EPs of this interface as needed.\n                        foreach_endpoint(self.config_descriptor, |ep| {\n                            if ep.interface == iface_num {\n                                self.bus\n                                    .endpoint_set_enabled(ep.ep_address, iface.current_alt_setting == ep.interface_alt);\n                            }\n                        })\n                        .unwrap();\n\n                        // TODO check it is valid (not out of range)\n\n                        for h in &mut self.handlers {\n                            h.set_alternate_setting(iface_num, new_altsetting);\n                        }\n                        OutResponse::Accepted\n                    }\n                    _ => OutResponse::Rejected,\n                }\n            }\n            (RequestType::Standard, Recipient::Endpoint) => match (req.request, req.value) {\n                (Request::SET_FEATURE, Request::FEATURE_ENDPOINT_HALT) => {\n                    let ep_addr = ((req.index as u8) & 0x8f).into();\n                    self.bus.endpoint_set_stalled(ep_addr, true);\n                    OutResponse::Accepted\n                }\n                (Request::CLEAR_FEATURE, Request::FEATURE_ENDPOINT_HALT) => {\n                    let ep_addr = ((req.index as u8) & 0x8f).into();\n                    self.bus.endpoint_set_stalled(ep_addr, false);\n                    OutResponse::Accepted\n                }\n                _ => OutResponse::Rejected,\n            },\n            _ => self.handle_control_out_delegated(req, data),\n        }\n    }\n\n    fn handle_control_in<'a>(&'a mut self, req: Request, buf: &'a mut [u8]) -> InResponse<'a> {\n        match (req.request_type, req.recipient) {\n            (RequestType::Standard, Recipient::Device) => match req.request {\n                Request::GET_STATUS => {\n                    let mut status: u16 = 0x0000;\n                    if self.self_powered {\n                        status |= 0x0001;\n                    }\n                    if self.remote_wakeup_enabled {\n                        status |= 0x0002;\n                    }\n                    buf[..2].copy_from_slice(&status.to_le_bytes());\n                    InResponse::Accepted(&buf[..2])\n                }\n                Request::GET_DESCRIPTOR => self.handle_get_descriptor(req, buf),\n                Request::GET_CONFIGURATION => {\n                    let status = match self.device_state {\n                        UsbDeviceState::Configured => CONFIGURATION_VALUE,\n                        _ => CONFIGURATION_NONE,\n                    };\n                    buf[0] = status;\n                    InResponse::Accepted(&buf[..1])\n                }\n                _ => InResponse::Rejected,\n            },\n            (RequestType::Standard, Recipient::Interface) => {\n                let Some(iface) = self.interfaces.get_mut(req.index as usize) else {\n                    return InResponse::Rejected;\n                };\n\n                match req.request {\n                    Request::GET_STATUS => {\n                        let status: u16 = 0;\n                        buf[..2].copy_from_slice(&status.to_le_bytes());\n                        InResponse::Accepted(&buf[..2])\n                    }\n                    Request::GET_INTERFACE => {\n                        buf[0] = iface.current_alt_setting;\n                        InResponse::Accepted(&buf[..1])\n                    }\n                    _ => self.handle_control_in_delegated(req, buf),\n                }\n            }\n            (RequestType::Standard, Recipient::Endpoint) => match req.request {\n                Request::GET_STATUS => {\n                    let ep_addr: EndpointAddress = ((req.index as u8) & 0x8f).into();\n                    let mut status: u16 = 0x0000;\n                    if self.bus.endpoint_is_stalled(ep_addr) {\n                        status |= 0x0001;\n                    }\n                    buf[..2].copy_from_slice(&status.to_le_bytes());\n                    InResponse::Accepted(&buf[..2])\n                }\n                _ => InResponse::Rejected,\n            },\n\n            (RequestType::Vendor, Recipient::Device) => {\n                if !self.msos_descriptor.is_empty()\n                    && req.request == self.msos_descriptor.vendor_code()\n                    && req.index == 7\n                {\n                    // Index 7 retrieves the MS OS Descriptor Set\n                    InResponse::Accepted(self.msos_descriptor.descriptor())\n                } else {\n                    self.handle_control_in_delegated(req, buf)\n                }\n            }\n            _ => self.handle_control_in_delegated(req, buf),\n        }\n    }\n\n    fn handle_control_out_delegated(&mut self, req: Request, data: &[u8]) -> OutResponse {\n        for h in &mut self.handlers {\n            if let Some(res) = h.control_out(req, data) {\n                return res;\n            }\n        }\n        OutResponse::Rejected\n    }\n\n    fn handle_control_in_delegated<'a>(&'a mut self, req: Request, buf: &'a mut [u8]) -> InResponse<'a> {\n        unsafe fn extend_lifetime<'y>(r: InResponse<'_>) -> InResponse<'y> {\n            core::mem::transmute(r)\n        }\n\n        for h in &mut self.handlers {\n            if let Some(res) = h.control_in(req, buf) {\n                // safety: the borrow checker isn't smart enough to know this pattern (returning a\n                // borrowed value from inside the loop) is sound. Workaround by unsafely extending lifetime.\n                // Also, Polonius (the WIP new borrow checker) does accept it.\n\n                return unsafe { extend_lifetime(res) };\n            }\n        }\n        InResponse::Rejected\n    }\n\n    fn handle_get_descriptor<'a>(&'a mut self, req: Request, buf: &'a mut [u8]) -> InResponse<'a> {\n        let (dtype, index) = req.descriptor_type_index();\n\n        match dtype {\n            descriptor_type::BOS => InResponse::Accepted(self.bos_descriptor),\n            descriptor_type::DEVICE => InResponse::Accepted(&self.device_descriptor),\n            descriptor_type::CONFIGURATION => InResponse::Accepted(self.config_descriptor),\n            descriptor_type::STRING => {\n                if index == 0 {\n                    buf[0] = 4; // len\n                    buf[1] = descriptor_type::STRING;\n                    buf[2] = lang_id::ENGLISH_US as u8;\n                    buf[3] = (lang_id::ENGLISH_US >> 8) as u8;\n                    InResponse::Accepted(&buf[..4])\n                } else {\n                    let s = match index {\n                        STRING_INDEX_MANUFACTURER => self.config.manufacturer,\n                        STRING_INDEX_PRODUCT => self.config.product,\n                        STRING_INDEX_SERIAL_NUMBER => self.config.serial_number,\n                        _ => {\n                            let mut s = None;\n                            for handler in &mut self.handlers {\n                                let index = StringIndex::new(index);\n                                let lang_id = req.index;\n                                if let Some(res) = handler.get_string(index, lang_id) {\n                                    s = Some(res);\n                                    break;\n                                }\n                            }\n                            s\n                        }\n                    };\n\n                    if let Some(s) = s {\n                        assert!(buf.len() >= 2, \"control buffer too small\");\n\n                        buf[1] = descriptor_type::STRING;\n                        let mut pos = 2;\n                        for c in s.encode_utf16() {\n                            assert!(pos + 2 < buf.len(), \"control buffer too small\");\n\n                            buf[pos..pos + 2].copy_from_slice(&c.to_le_bytes());\n                            pos += 2;\n                        }\n\n                        buf[0] = pos as u8;\n                        InResponse::Accepted(&buf[..pos])\n                    } else {\n                        InResponse::Rejected\n                    }\n                }\n            }\n            descriptor_type::DEVICE_QUALIFIER => InResponse::Accepted(&self.device_qualifier_descriptor),\n            _ => InResponse::Rejected,\n        }\n    }\n}\n\nfn first_last<T: Iterator>(iter: T) -> impl Iterator<Item = (bool, bool, T::Item)> {\n    let mut iter = iter.peekable();\n    let mut first = true;\n    core::iter::from_fn(move || {\n        let val = iter.next()?;\n        let is_first = first;\n        first = false;\n        let is_last = iter.peek().is_none();\n        Some((is_first, is_last, val))\n    })\n}\n"
  },
  {
    "path": "embassy-usb/src/msos.rs",
    "content": "//! Microsoft OS Descriptors\n//!\n//! <https://docs.microsoft.com/en-us/windows-hardware/drivers/usbcon/microsoft-os-2-0-descriptors-specification>\n\nuse core::mem::size_of;\n\nuse crate::descriptor::{BosWriter, capability_type};\nuse crate::types::InterfaceNumber;\n\n/// A serialized Microsoft OS 2.0 Descriptor set.\n///\n/// Create with [`DeviceDescriptorSetBuilder`].\npub struct MsOsDescriptorSet<'d> {\n    descriptor: &'d [u8],\n    vendor_code: u8,\n}\n\nimpl<'d> MsOsDescriptorSet<'d> {\n    /// Gets the raw bytes of the MS OS descriptor\n    pub fn descriptor(&self) -> &[u8] {\n        self.descriptor\n    }\n\n    /// Gets the vendor code used by the host to retrieve the MS OS descriptor\n    pub fn vendor_code(&self) -> u8 {\n        self.vendor_code\n    }\n\n    /// Returns `true` if no MS OS descriptor data is available\n    pub fn is_empty(&self) -> bool {\n        self.descriptor.is_empty()\n    }\n\n    /// Returns the length of the descriptor field\n    pub fn len(&self) -> usize {\n        self.descriptor.len()\n    }\n}\n\n/// Writes a Microsoft OS 2.0 Descriptor set into a buffer.\npub struct MsOsDescriptorWriter<'d> {\n    buf: &'d mut [u8],\n\n    position: usize,\n    config_mark: Option<usize>,\n    function_mark: Option<usize>,\n    vendor_code: u8,\n}\n\nimpl<'d> MsOsDescriptorWriter<'d> {\n    pub(crate) fn new(buf: &'d mut [u8]) -> Self {\n        MsOsDescriptorWriter {\n            buf,\n            position: 0,\n            config_mark: None,\n            function_mark: None,\n            vendor_code: 0,\n        }\n    }\n\n    pub(crate) fn build(mut self, bos: &mut BosWriter) -> MsOsDescriptorSet<'d> {\n        self.end();\n\n        if self.is_empty() {\n            MsOsDescriptorSet {\n                descriptor: &[],\n                vendor_code: 0,\n            }\n        } else {\n            self.write_bos(bos);\n            MsOsDescriptorSet {\n                descriptor: &self.buf[..self.position],\n                vendor_code: self.vendor_code,\n            }\n        }\n    }\n\n    /// Returns `true` if the MS OS descriptor header has not yet been written\n    pub fn is_empty(&self) -> bool {\n        self.position == 0\n    }\n\n    /// Returns `true` if a configuration subset header has been started\n    pub fn is_in_config_subset(&self) -> bool {\n        self.config_mark.is_some()\n    }\n\n    /// Returns `true` if a function subset header has been started and not yet ended\n    pub fn is_in_function_subset(&self) -> bool {\n        self.function_mark.is_some()\n    }\n\n    /// Write the MS OS descriptor set header.\n    ///\n    /// - `windows_version` is an NTDDI version constant that describes a windows version. See the [`windows_version`]\n    /// module.\n    /// - `vendor_code` is the vendor request code used to read the MS OS descriptor set.\n    pub fn header(&mut self, windows_version: u32, vendor_code: u8) {\n        assert!(self.is_empty(), \"You can only call MsOsDescriptorWriter::header once\");\n        self.write(DescriptorSetHeader::new(windows_version));\n        self.vendor_code = vendor_code;\n    }\n\n    /// Add a device level feature descriptor.\n    ///\n    /// Note that some feature descriptors may only be used at the device level in non-composite devices.\n    /// Those features must be written before the first call to [`Self::configuration`].\n    pub fn device_feature<T: DeviceLevelDescriptor>(&mut self, desc: T) {\n        assert!(\n            !self.is_empty(),\n            \"device features may only be added after the header is written\"\n        );\n        self.write(desc);\n    }\n\n    /// Add a configuration subset.\n    pub fn configuration(&mut self, config: u8) {\n        assert!(\n            !self.is_empty(),\n            \"MsOsDescriptorWriter: configuration must be called after header\"\n        );\n        Self::end_subset::<ConfigurationSubsetHeader>(self.buf, self.position, &mut self.config_mark);\n        self.config_mark = Some(self.position);\n        self.write(ConfigurationSubsetHeader::new(config));\n    }\n\n    /// Add a function subset.\n    pub fn function(&mut self, first_interface: InterfaceNumber) {\n        assert!(\n            self.config_mark.is_some(),\n            \"MsOsDescriptorWriter: function subset requires a configuration subset\"\n        );\n        self.end_function();\n        self.function_mark = Some(self.position);\n        self.write(FunctionSubsetHeader::new(first_interface));\n    }\n\n    /// Add a function level feature descriptor.\n    ///\n    /// Note that some features may only be used at the function level. Those features must be written after a call\n    /// to [`Self::function`].\n    pub fn function_feature<T: FunctionLevelDescriptor>(&mut self, desc: T) {\n        assert!(\n            self.function_mark.is_some(),\n            \"function features may only be added to a function subset\"\n        );\n        self.write(desc);\n    }\n\n    /// Ends the current function subset (if any)\n    pub fn end_function(&mut self) {\n        Self::end_subset::<FunctionSubsetHeader>(self.buf, self.position, &mut self.function_mark);\n    }\n\n    fn write<T: Descriptor>(&mut self, desc: T) {\n        desc.write_to(&mut self.buf[self.position..]);\n        self.position += desc.size();\n    }\n\n    fn end_subset<T: DescriptorSet>(buf: &mut [u8], position: usize, mark: &mut Option<usize>) {\n        if let Some(mark) = mark.take() {\n            let len = position - mark;\n            let p = mark + T::LENGTH_OFFSET;\n            buf[p..(p + 2)].copy_from_slice(&(len as u16).to_le_bytes());\n        }\n    }\n\n    fn end(&mut self) {\n        if self.position > 0 {\n            Self::end_subset::<FunctionSubsetHeader>(self.buf, self.position, &mut self.function_mark);\n            Self::end_subset::<ConfigurationSubsetHeader>(self.buf, self.position, &mut self.config_mark);\n            Self::end_subset::<DescriptorSetHeader>(self.buf, self.position, &mut Some(0));\n        }\n    }\n\n    fn write_bos(&mut self, bos: &mut BosWriter) {\n        let windows_version = &self.buf[4..8];\n        let len = (self.position as u16).to_le_bytes();\n        bos.capability(\n            capability_type::PLATFORM,\n            &[\n                0, // reserved\n                // platform capability UUID, Microsoft OS 2.0 platform compatibility\n                0xdf,\n                0x60,\n                0xdd,\n                0xd8,\n                0x89,\n                0x45,\n                0xc7,\n                0x4c,\n                0x9c,\n                0xd2,\n                0x65,\n                0x9d,\n                0x9e,\n                0x64,\n                0x8a,\n                0x9f,\n                // Minimum compatible Windows version\n                windows_version[0],\n                windows_version[1],\n                windows_version[2],\n                windows_version[3],\n                // Descriptor set length\n                len[0],\n                len[1],\n                self.vendor_code,\n                0x0, // Device does not support alternate enumeration\n            ],\n        );\n    }\n}\n\n/// Microsoft Windows version codes\n///\n/// Windows 8.1 is the minimum version allowed for MS OS 2.0 descriptors.\npub mod windows_version {\n    /// Windows 8.1 (aka `NTDDI_WINBLUE`)\n    pub const WIN8_1: u32 = 0x06030000;\n    /// Windows 10\n    pub const WIN10: u32 = 0x0A000000;\n}\n\n/// A trait for descriptors\ntrait Descriptor: Sized {\n    const TYPE: DescriptorType;\n\n    /// The size of the descriptor's header.\n    fn size(&self) -> usize {\n        size_of::<Self>()\n    }\n\n    fn write_to(&self, buf: &mut [u8]);\n}\n\ntrait DescriptorSet: Descriptor {\n    const LENGTH_OFFSET: usize;\n}\n\n/// Copies the data of `t` into `buf`.\n///\n/// # Safety\n/// The type `T` must be able to be safely cast to `&[u8]`. (e.g. it is a `#[repr(packed)]` struct)\nunsafe fn transmute_write_to<T: Sized>(t: &T, buf: &mut [u8]) {\n    let bytes = core::slice::from_raw_parts((t as *const T) as *const u8, size_of::<T>());\n    assert!(buf.len() >= bytes.len(), \"MS OS descriptor buffer full\");\n    buf[..bytes.len()].copy_from_slice(bytes);\n}\n\n/// Table 9. Microsoft OS 2.0 descriptor wDescriptorType values.\n#[derive(Clone, Copy, PartialEq, Eq)]\n#[repr(u16)]\npub enum DescriptorType {\n    /// MS OS descriptor set header\n    SetHeaderDescriptor = 0,\n    /// Configuration subset header\n    SubsetHeaderConfiguration = 1,\n    /// Function subset header\n    SubsetHeaderFunction = 2,\n    /// Compatible device ID feature descriptor\n    FeatureCompatibleId = 3,\n    /// Registry property feature descriptor\n    FeatureRegProperty = 4,\n    /// Minimum USB resume time feature descriptor\n    FeatureMinResumeTime = 5,\n    /// Vendor revision feature descriptor\n    FeatureModelId = 6,\n    /// CCGP device descriptor feature descriptor\n    FeatureCcgpDevice = 7,\n    /// Vendor revision feature descriptor\n    FeatureVendorRevision = 8,\n}\n\n/// Table 5. Descriptor set information structure.\n#[allow(non_snake_case)]\n#[allow(unused)]\n#[repr(C, packed(1))]\npub struct DescriptorSetInformation {\n    dwWindowsVersion: u32,\n    wMSOSDescriptorSetTotalLength: u16,\n    bMS_VendorCode: u8,\n    bAltEnumCode: u8,\n}\n\n/// Table 4. Microsoft OS 2.0 platform capability descriptor header.\n#[allow(non_snake_case)]\n#[allow(unused)]\n#[repr(C, packed(1))]\npub struct PlatformDescriptor {\n    bLength: u8,\n    bDescriptorType: u8,\n    bDevCapabilityType: u8,\n    bReserved: u8,\n    platformCapabilityUUID: [u8; 16],\n    descriptor_set_information: DescriptorSetInformation,\n}\n\n/// Table 10. Microsoft OS 2.0 descriptor set header.\n#[allow(non_snake_case)]\n#[repr(C, packed(1))]\npub struct DescriptorSetHeader {\n    wLength: u16,\n    wDescriptorType: u16,\n    dwWindowsVersion: u32,\n    wTotalLength: u16,\n}\n\nimpl DescriptorSetHeader {\n    /// Creates a MS OS descriptor set header.\n    ///\n    /// `windows_version` is the minimum Windows version the descriptor set can apply to.\n    pub fn new(windows_version: u32) -> Self {\n        DescriptorSetHeader {\n            wLength: (size_of::<Self>() as u16).to_le(),\n            wDescriptorType: (Self::TYPE as u16).to_le(),\n            dwWindowsVersion: windows_version.to_le(),\n            wTotalLength: 0,\n        }\n    }\n}\n\nimpl Descriptor for DescriptorSetHeader {\n    const TYPE: DescriptorType = DescriptorType::SetHeaderDescriptor;\n    fn write_to(&self, buf: &mut [u8]) {\n        unsafe { transmute_write_to(self, buf) }\n    }\n}\n\nimpl DescriptorSet for DescriptorSetHeader {\n    const LENGTH_OFFSET: usize = 8;\n}\n\n/// Table 11. Configuration subset header.\n#[allow(non_snake_case)]\n#[repr(C, packed(1))]\npub struct ConfigurationSubsetHeader {\n    wLength: u16,\n    wDescriptorType: u16,\n    bConfigurationValue: u8,\n    bReserved: u8,\n    wTotalLength: u16,\n}\n\nimpl ConfigurationSubsetHeader {\n    /// Creates a configuration subset header\n    pub fn new(config: u8) -> Self {\n        ConfigurationSubsetHeader {\n            wLength: (size_of::<Self>() as u16).to_le(),\n            wDescriptorType: (Self::TYPE as u16).to_le(),\n            bConfigurationValue: config,\n            bReserved: 0,\n            wTotalLength: 0,\n        }\n    }\n}\n\nimpl Descriptor for ConfigurationSubsetHeader {\n    const TYPE: DescriptorType = DescriptorType::SubsetHeaderConfiguration;\n    fn write_to(&self, buf: &mut [u8]) {\n        unsafe { transmute_write_to(self, buf) }\n    }\n}\n\nimpl DescriptorSet for ConfigurationSubsetHeader {\n    const LENGTH_OFFSET: usize = 6;\n}\n\n/// Table 12. Function subset header.\n#[allow(non_snake_case)]\n#[repr(C, packed(1))]\npub struct FunctionSubsetHeader {\n    wLength: u16,\n    wDescriptorType: u16,\n    bFirstInterface: InterfaceNumber,\n    bReserved: u8,\n    wSubsetLength: u16,\n}\n\nimpl FunctionSubsetHeader {\n    /// Creates a function subset header\n    pub fn new(first_interface: InterfaceNumber) -> Self {\n        FunctionSubsetHeader {\n            wLength: (size_of::<Self>() as u16).to_le(),\n            wDescriptorType: (Self::TYPE as u16).to_le(),\n            bFirstInterface: first_interface,\n            bReserved: 0,\n            wSubsetLength: 0,\n        }\n    }\n}\n\nimpl Descriptor for FunctionSubsetHeader {\n    const TYPE: DescriptorType = DescriptorType::SubsetHeaderFunction;\n    fn write_to(&self, buf: &mut [u8]) {\n        unsafe { transmute_write_to(self, buf) }\n    }\n}\n\nimpl DescriptorSet for FunctionSubsetHeader {\n    const LENGTH_OFFSET: usize = 6;\n}\n\n// Feature Descriptors\n\n/// A marker trait for feature descriptors that are valid at the device level.\n#[allow(private_bounds)]\npub trait DeviceLevelDescriptor: Descriptor {}\n\n/// A marker trait for feature descriptors that are valid at the function level.\n#[allow(private_bounds)]\npub trait FunctionLevelDescriptor: Descriptor {}\n\n/// Table 13. Microsoft OS 2.0 compatible ID descriptor.\n#[allow(non_snake_case)]\n#[repr(C, packed(1))]\npub struct CompatibleIdFeatureDescriptor {\n    wLength: u16,\n    wDescriptorType: u16,\n    compatibleId: [u8; 8],\n    subCompatibleId: [u8; 8],\n}\n\nimpl DeviceLevelDescriptor for CompatibleIdFeatureDescriptor {}\nimpl FunctionLevelDescriptor for CompatibleIdFeatureDescriptor {}\n\nimpl Descriptor for CompatibleIdFeatureDescriptor {\n    const TYPE: DescriptorType = DescriptorType::FeatureCompatibleId;\n    fn write_to(&self, buf: &mut [u8]) {\n        unsafe { transmute_write_to(self, buf) }\n    }\n}\n\nimpl CompatibleIdFeatureDescriptor {\n    /// Creates a compatible ID feature descriptor\n    ///\n    /// The ids must be 8 ASCII bytes or fewer.\n    pub fn new(compatible_id: &str, sub_compatible_id: &str) -> Self {\n        assert!(compatible_id.len() <= 8 && sub_compatible_id.len() <= 8);\n        let mut cid = [0u8; 8];\n        cid[..compatible_id.len()].copy_from_slice(compatible_id.as_bytes());\n        let mut scid = [0u8; 8];\n        scid[..sub_compatible_id.len()].copy_from_slice(sub_compatible_id.as_bytes());\n        Self::new_raw(cid, scid)\n    }\n\n    fn new_raw(compatible_id: [u8; 8], sub_compatible_id: [u8; 8]) -> Self {\n        Self {\n            wLength: (size_of::<Self>() as u16).to_le(),\n            wDescriptorType: (Self::TYPE as u16).to_le(),\n            compatibleId: compatible_id,\n            subCompatibleId: sub_compatible_id,\n        }\n    }\n}\n\n/// Table 14. Microsoft OS 2.0 registry property descriptor\n#[allow(non_snake_case)]\npub struct RegistryPropertyFeatureDescriptor<'a> {\n    name: &'a str,\n    data: PropertyData<'a>,\n}\n\n/// Data values that can be encoded into a registry property descriptor\n#[derive(Debug, Clone, Copy, PartialEq, Eq)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub enum PropertyData<'a> {\n    /// A registry property containing a string.\n    Sz(&'a str),\n    /// A registry property containing a string that expands environment variables.\n    ExpandSz(&'a str),\n    /// A registry property containing binary data.\n    Binary(&'a [u8]),\n    /// A registry property containing a little-endian 32-bit integer.\n    DwordLittleEndian(u32),\n    /// A registry property containing a big-endian 32-bit integer.\n    DwordBigEndian(u32),\n    /// A registry property containing a string that contains a symbolic link.\n    Link(&'a str),\n    /// A registry property containing multiple strings.\n    RegMultiSz(&'a [&'a str]),\n}\n\nfn write_bytes(val: &[u8], buf: &mut [u8]) -> usize {\n    assert!(buf.len() >= val.len());\n    buf[..val.len()].copy_from_slice(val);\n    val.len()\n}\n\nfn write_utf16(val: &str, buf: &mut [u8]) -> usize {\n    let mut pos = 0;\n    for c in val.encode_utf16() {\n        pos += write_bytes(&c.to_le_bytes(), &mut buf[pos..]);\n    }\n    pos + write_bytes(&0u16.to_le_bytes(), &mut buf[pos..])\n}\n\nimpl<'a> PropertyData<'a> {\n    /// Gets the `PropertyDataType` for this property value\n    pub fn kind(&self) -> PropertyDataType {\n        match self {\n            PropertyData::Sz(_) => PropertyDataType::Sz,\n            PropertyData::ExpandSz(_) => PropertyDataType::ExpandSz,\n            PropertyData::Binary(_) => PropertyDataType::Binary,\n            PropertyData::DwordLittleEndian(_) => PropertyDataType::DwordLittleEndian,\n            PropertyData::DwordBigEndian(_) => PropertyDataType::DwordBigEndian,\n            PropertyData::Link(_) => PropertyDataType::Link,\n            PropertyData::RegMultiSz(_) => PropertyDataType::RegMultiSz,\n        }\n    }\n\n    /// Gets the size (in bytes) of this property value when encoded.\n    pub fn size(&self) -> usize {\n        match self {\n            PropertyData::Sz(val) | PropertyData::ExpandSz(val) | PropertyData::Link(val) => {\n                core::mem::size_of::<u16>() * (val.encode_utf16().count() + 1)\n            }\n            PropertyData::Binary(val) => val.len(),\n            PropertyData::DwordLittleEndian(val) | PropertyData::DwordBigEndian(val) => core::mem::size_of_val(val),\n            PropertyData::RegMultiSz(val) => {\n                core::mem::size_of::<u16>() * (val.iter().map(|x| x.encode_utf16().count() + 1).sum::<usize>() + 1)\n            }\n        }\n    }\n\n    /// Encodes the data for this property value and writes it to `buf`.\n    pub fn write(&self, buf: &mut [u8]) -> usize {\n        match self {\n            PropertyData::Sz(val) | PropertyData::ExpandSz(val) | PropertyData::Link(val) => write_utf16(val, buf),\n            PropertyData::Binary(val) => write_bytes(val, buf),\n            PropertyData::DwordLittleEndian(val) => write_bytes(&val.to_le_bytes(), buf),\n            PropertyData::DwordBigEndian(val) => write_bytes(&val.to_be_bytes(), buf),\n            PropertyData::RegMultiSz(val) => {\n                let mut pos = 0;\n                for s in *val {\n                    pos += write_utf16(s, &mut buf[pos..]);\n                }\n                pos + write_bytes(&0u16.to_le_bytes(), &mut buf[pos..])\n            }\n        }\n    }\n}\n\n/// Table 15. wPropertyDataType values for the Microsoft OS 2.0 registry property descriptor.\n#[derive(Clone, Copy, PartialEq, Eq)]\n#[repr(u16)]\npub enum PropertyDataType {\n    /// A registry property containing a string.\n    Sz = 1,\n    /// A registry property containing a string that expands environment variables.\n    ExpandSz = 2,\n    /// A registry property containing binary data.\n    Binary = 3,\n    /// A registry property containing a little-endian 32-bit integer.\n    DwordLittleEndian = 4,\n    /// A registry property containing a big-endian 32-bit integer.\n    DwordBigEndian = 5,\n    /// A registry property containing a string that contains a symbolic link.\n    Link = 6,\n    /// A registry property containing multiple strings.\n    RegMultiSz = 7,\n}\n\nimpl<'a> DeviceLevelDescriptor for RegistryPropertyFeatureDescriptor<'a> {}\nimpl<'a> FunctionLevelDescriptor for RegistryPropertyFeatureDescriptor<'a> {}\n\nimpl<'a> Descriptor for RegistryPropertyFeatureDescriptor<'a> {\n    const TYPE: DescriptorType = DescriptorType::FeatureRegProperty;\n\n    fn size(&self) -> usize {\n        10 + self.name_size() + self.data.size()\n    }\n\n    fn write_to(&self, buf: &mut [u8]) {\n        assert!(buf.len() >= self.size(), \"MS OS descriptor buffer full\");\n\n        let mut pos = 0;\n        pos += write_bytes(&(self.size() as u16).to_le_bytes(), &mut buf[pos..]);\n        pos += write_bytes(&(Self::TYPE as u16).to_le_bytes(), &mut buf[pos..]);\n        pos += write_bytes(&(self.data.kind() as u16).to_le_bytes(), &mut buf[pos..]);\n        pos += write_bytes(&(self.name_size() as u16).to_le_bytes(), &mut buf[pos..]);\n        pos += write_utf16(self.name, &mut buf[pos..]);\n        pos += write_bytes(&(self.data.size() as u16).to_le_bytes(), &mut buf[pos..]);\n        self.data.write(&mut buf[pos..]);\n    }\n}\n\nimpl<'a> RegistryPropertyFeatureDescriptor<'a> {\n    /// A registry property.\n    pub fn new(name: &'a str, data: PropertyData<'a>) -> Self {\n        Self { name, data }\n    }\n\n    fn name_size(&self) -> usize {\n        core::mem::size_of::<u16>() * (self.name.encode_utf16().count() + 1)\n    }\n}\n\n/// Table 16. Microsoft OS 2.0 minimum USB recovery time descriptor.\n#[allow(non_snake_case)]\n#[repr(C, packed(1))]\npub struct MinimumRecoveryTimeDescriptor {\n    wLength: u16,\n    wDescriptorType: u16,\n    bResumeRecoveryTime: u8,\n    bResumeSignalingTime: u8,\n}\n\nimpl DeviceLevelDescriptor for MinimumRecoveryTimeDescriptor {}\n\nimpl Descriptor for MinimumRecoveryTimeDescriptor {\n    const TYPE: DescriptorType = DescriptorType::FeatureMinResumeTime;\n    fn write_to(&self, buf: &mut [u8]) {\n        unsafe { transmute_write_to(self, buf) }\n    }\n}\n\nimpl MinimumRecoveryTimeDescriptor {\n    /// Times are in milliseconds.\n    ///\n    /// `resume_recovery_time` must be >= 0 and <= 10.\n    /// `resume_signaling_time` must be >= 1 and <= 20.\n    pub fn new(resume_recovery_time: u8, resume_signaling_time: u8) -> Self {\n        assert!(resume_recovery_time <= 10);\n        assert!(resume_signaling_time >= 1 && resume_signaling_time <= 20);\n        Self {\n            wLength: (size_of::<Self>() as u16).to_le(),\n            wDescriptorType: (Self::TYPE as u16).to_le(),\n            bResumeRecoveryTime: resume_recovery_time,\n            bResumeSignalingTime: resume_signaling_time,\n        }\n    }\n}\n\n/// Table 17. Microsoft OS 2.0 model ID descriptor.\n#[allow(non_snake_case)]\n#[repr(C, packed(1))]\npub struct ModelIdDescriptor {\n    wLength: u16,\n    wDescriptorType: u16,\n    modelId: [u8; 16],\n}\n\nimpl DeviceLevelDescriptor for ModelIdDescriptor {}\n\nimpl Descriptor for ModelIdDescriptor {\n    const TYPE: DescriptorType = DescriptorType::FeatureModelId;\n    fn write_to(&self, buf: &mut [u8]) {\n        unsafe { transmute_write_to(self, buf) }\n    }\n}\n\nimpl ModelIdDescriptor {\n    /// Creates a new model ID descriptor\n    ///\n    /// `model_id` should be a uuid that uniquely identifies a physical device.\n    pub fn new(model_id: u128) -> Self {\n        Self {\n            wLength: (size_of::<Self>() as u16).to_le(),\n            wDescriptorType: (Self::TYPE as u16).to_le(),\n            modelId: model_id.to_le_bytes(),\n        }\n    }\n}\n\n/// Table 18. Microsoft OS 2.0 CCGP device descriptor.\n#[allow(non_snake_case)]\n#[repr(C, packed(1))]\npub struct CcgpDeviceDescriptor {\n    wLength: u16,\n    wDescriptorType: u16,\n}\n\nimpl DeviceLevelDescriptor for CcgpDeviceDescriptor {}\n\nimpl Descriptor for CcgpDeviceDescriptor {\n    const TYPE: DescriptorType = DescriptorType::FeatureCcgpDevice;\n    fn write_to(&self, buf: &mut [u8]) {\n        unsafe { transmute_write_to(self, buf) }\n    }\n}\n\nimpl CcgpDeviceDescriptor {\n    /// Creates a new CCGP device descriptor\n    pub fn new() -> Self {\n        Self {\n            wLength: (size_of::<Self>() as u16).to_le(),\n            wDescriptorType: (Self::TYPE as u16).to_le(),\n        }\n    }\n}\n\n/// Table 19. Microsoft OS 2.0 vendor revision descriptor.\n#[allow(non_snake_case)]\n#[repr(C, packed(1))]\npub struct VendorRevisionDescriptor {\n    wLength: u16,\n    wDescriptorType: u16,\n    /// Revision number associated with the descriptor set. Modify it every time you add/modify a registry property or\n    /// other MS OS descriptor. Shell set to greater than or equal to 1.\n    VendorRevision: u16,\n}\n\nimpl DeviceLevelDescriptor for VendorRevisionDescriptor {}\nimpl FunctionLevelDescriptor for VendorRevisionDescriptor {}\n\nimpl Descriptor for VendorRevisionDescriptor {\n    const TYPE: DescriptorType = DescriptorType::FeatureVendorRevision;\n    fn write_to(&self, buf: &mut [u8]) {\n        unsafe { transmute_write_to(self, buf) }\n    }\n}\n\nimpl VendorRevisionDescriptor {\n    /// Creates a new vendor revision descriptor\n    pub fn new(revision: u16) -> Self {\n        assert!(revision >= 1);\n        Self {\n            wLength: (size_of::<Self>() as u16).to_le(),\n            wDescriptorType: (Self::TYPE as u16).to_le(),\n            VendorRevision: revision.to_le(),\n        }\n    }\n}\n"
  },
  {
    "path": "embassy-usb/src/types.rs",
    "content": "//! USB types.\n\n/// A handle for a USB interface that contains its number.\n#[derive(Debug, Copy, Clone, Eq, PartialEq)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\n#[repr(transparent)]\npub struct InterfaceNumber(pub u8);\n\nimpl InterfaceNumber {\n    pub(crate) const fn new(index: u8) -> InterfaceNumber {\n        InterfaceNumber(index)\n    }\n}\n\nimpl From<InterfaceNumber> for u8 {\n    fn from(n: InterfaceNumber) -> u8 {\n        n.0\n    }\n}\n\nimpl core::fmt::Display for InterfaceNumber {\n    fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {\n        write!(f, \"{}\", self.0)\n    }\n}\n\n/// A handle for a USB string descriptor that contains its index.\n#[derive(Copy, Clone, Eq, PartialEq)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\n#[repr(transparent)]\npub struct StringIndex(pub u8);\n\nimpl StringIndex {\n    pub(crate) const fn new(index: u8) -> StringIndex {\n        StringIndex(index)\n    }\n}\n\nimpl From<StringIndex> for u8 {\n    fn from(i: StringIndex) -> u8 {\n        i.0\n    }\n}\n"
  },
  {
    "path": "embassy-usb-dfu/CHANGELOG.md",
    "content": "# Changelog\n\nAll notable changes to this project will be documented in this file.\n\nThe format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),\nand this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).\n\n<!-- next-header -->\n## Unreleased - ReleaseDate\n\n## 0.3.0 - 2026-03-10\n\n- changed: Do not reset in the GetStatus request\n- Allow enabling the `application` and `dfu` feature at the same time\n- Use standalone DFU class from embassy-usb\n- Allow `dfu_mode::Handler::start` to return a `Result` (fail gracefully)\n- Upgrade embassy-boot to 0.7.0\n- Upgrade embassy-sync to 0.8.0\n- Upgrade embassy-usb to 0.6.0\n\n## 0.2.0 - 2025-08-27\n\n- First release with changelog.\n"
  },
  {
    "path": "embassy-usb-dfu/Cargo.toml",
    "content": "[package]\nedition = \"2024\"\nname = \"embassy-usb-dfu\"\nversion = \"0.3.0\"\ndescription = \"An implementation of the USB DFU 1.1 protocol, using embassy-boot\"\nlicense = \"MIT OR Apache-2.0\"\nrepository = \"https://github.com/embassy-rs/embassy\"\ndocumentation = \"https://docs.embassy.dev/embassy-usb-dfu\"\ncategories = [\n    \"embedded\",\n    \"no-std\",\n    \"asynchronous\"\n]\n\n[package.metadata.embassy]\nbuild = [\n    { target = \"thumbv7em-none-eabi\", features = [ \"defmt\", \"cortex-m\", \"dfu\" ] },\n    { target = \"thumbv7em-none-eabi\", features = [ \"defmt\", \"cortex-m\", \"application\" ] },\n]\n\n[package.metadata.embassy_docs]\nsrc_base = \"https://github.com/embassy-rs/embassy/blob/embassy-usb-v$VERSION/embassy-usb-dfu/src/\"\nsrc_base_git = \"https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-usb-dfu/src/\"\nfeatures = [\"defmt\", \"cortex-m\"]\ntarget = \"thumbv7em-none-eabi\"\nflavors = [\n    { name = \"dfu\",         features = [ \"dfu\" ] },\n    { name = \"application\", features = [ \"application\" ] },\n]\n\n[package.metadata.docs.rs]\nfeatures = [\"defmt\", \"cortex-m\", \"dfu\"]\n\n[dependencies]\ndefmt = { version = \"1.0.1\", optional = true }\nlog = { version = \"0.4.17\", optional = true }\n\nbitflags = \"2.10.0\"\ncortex-m = { version = \"0.7.7\", features = [\"inline-asm\"], optional = true }\nembassy-boot = { version = \"0.7.0\", path = \"../embassy-boot\" }\nembassy-futures = { version = \"0.1.2\", path = \"../embassy-futures\" }\nembassy-sync = { version = \"0.8.0\", path = \"../embassy-sync\" }\nembassy-time = { version = \"0.5.1\", path = \"../embassy-time\" }\nembassy-usb = { version = \"0.6.0\", path = \"../embassy-usb\", default-features = false }\nembedded-storage = { version = \"0.3.1\" }\nesp32c3-hal = { version = \"0.13.0\", optional = true, default-features = false }\n\n[dev-dependencies]\ndfu-core = { version = \"0.10.0\", features=[\"std\"] }\nanyhow = \"1\"\n\n[features]\ndfu = []\napplication = []\ndefmt = [\"dep:defmt\", \"embassy-boot/defmt\", \"embassy-usb/defmt\"]\nlog = [\"dep:log\"]\ncortex-m = [\"dep:cortex-m\"]  \nesp32c3-hal = [\"dep:esp32c3-hal\"]\ned25519-dalek = [\"embassy-boot/ed25519-dalek\", \"_verify\"]\ned25519-salty = [\"embassy-boot/ed25519-salty\", \"_verify\"]\n\n# Internal features\n_verify = []\n"
  },
  {
    "path": "embassy-usb-dfu/README.md",
    "content": "# embassy-usb-dfu\n\nAn implementation of the USB DFU 1.1 protocol using embassy-boot. It has 2 components depending on which feature is enabled by the user.\n\n* DFU protocol mode, enabled by the `dfu` feature. This mode corresponds to the transfer phase DFU protocol described by the USB IF. It supports DFU_DNLOAD requests if marked by the user, and will automatically reset the chip once a DFU transaction has been completed. It also responds to DFU_GETSTATUS, DFU_GETSTATE, DFU_ABORT, and DFU_CLRSTATUS with no user intervention.\n* DFU runtime mode, enabled by the `application feature`. This mode allows users to expose a DFU interface on their USB device, informing the host of the capability to DFU over USB, and allowing the host to reset the device into its bootloader to complete a DFU operation. Supports DFU_GETSTATUS and DFU_DETACH. When detach/reset is seen by the device as described by the standard, will write a new DFU magic number into the bootloader state in flash, and reset the system.\n\n## Verification\n\nEmbassy-boot provides functionality to verify that an update binary has been correctly signed using ed25519 as described in https://embassy.dev/book/#_verification. Even though the linked procedure describes the signature being concatenated to the end of the update binary, embassy-boot does not force this and is flexible in terms of how the signature for a binary is distributed. The current implementation in embassy-usb-dfu does however assume that the signature is 64 bytes long and concatenated to the end of the update binary since this is the simplest way to make it work with the usb-dfu mechanism. I.e. embassy-usb-dfu does not currently offer the same flexibility as embassy-boot.\n\nTo enable verification, you need to enable either the `ed25519-dalek` or the `ed25519-salty` feature with `ed25519-salty` being recommended.\n"
  },
  {
    "path": "embassy-usb-dfu/src/application.rs",
    "content": "//! Application part of DFU logic\n\npub use embassy_usb::class::dfu::app_mode::{DfuState, Handler, usb_dfu};\npub use embassy_usb::class::dfu::consts::DfuAttributes;\n"
  },
  {
    "path": "embassy-usb-dfu/src/dfu.rs",
    "content": "//! DFU bootloader part of DFU logic\nuse embassy_boot::{AlignedBuffer, BlockingFirmwareUpdater, FirmwareUpdaterError};\nuse embassy_usb::class::dfu::consts::{DfuAttributes, Status};\n/// Re-export DfuState from embassy-usb for convenience.\npub use embassy_usb::class::dfu::dfu_mode::DfuState as UsbDfuState;\nuse embassy_usb::class::dfu::dfu_mode::{self, DfuState};\nuse embassy_usb::driver::Driver;\nuse embassy_usb::{Builder, FunctionBuilder};\nuse embedded_storage::nor_flash::{NorFlash, NorFlashErrorKind};\n\nuse crate::Reset;\n\n/// Internal handler for USB DFU firmware updates.\n///\n/// This implements the `embassy_usb::class::dfu::dfu_mode::Handler` trait,\n/// providing the firmware write logic using `BlockingFirmwareUpdater`.\npub struct FirmwareHandler<'d, DFU: NorFlash, STATE: NorFlash, RST: Reset, const BLOCK_SIZE: usize> {\n    updater: BlockingFirmwareUpdater<'d, DFU, STATE>,\n    offset: usize,\n    buf: AlignedBuffer<BLOCK_SIZE>,\n    reset: RST,\n\n    #[cfg(feature = \"_verify\")]\n    public_key: &'static [u8; 32],\n}\n\nimpl<'d, DFU: NorFlash, STATE: NorFlash, RST: Reset, const BLOCK_SIZE: usize>\n    FirmwareHandler<'d, DFU, STATE, RST, BLOCK_SIZE>\n{\n    /// Create a new firmware handler.\n    pub fn new(\n        updater: BlockingFirmwareUpdater<'d, DFU, STATE>,\n        reset: RST,\n        #[cfg(feature = \"_verify\")] public_key: &'static [u8; 32],\n    ) -> Self {\n        Self {\n            updater,\n            offset: 0,\n            buf: AlignedBuffer([0; BLOCK_SIZE]),\n            reset,\n\n            #[cfg(feature = \"_verify\")]\n            public_key,\n        }\n    }\n}\n\nfn firmware_error_to_status(e: FirmwareUpdaterError) -> Status {\n    match e {\n        FirmwareUpdaterError::Flash(e) => match e {\n            NorFlashErrorKind::NotAligned => Status::ErrWrite,\n            NorFlashErrorKind::OutOfBounds => Status::ErrAddress,\n            _ => Status::ErrUnknown,\n        },\n        FirmwareUpdaterError::Signature(_) => Status::ErrVerify,\n        FirmwareUpdaterError::BadState => Status::ErrUnknown,\n    }\n}\n\nimpl<'d, DFU: NorFlash, STATE: NorFlash, RST: Reset, const BLOCK_SIZE: usize> dfu_mode::Handler\n    for FirmwareHandler<'d, DFU, STATE, RST, BLOCK_SIZE>\n{\n    fn start(&mut self) -> Result<(), Status> {\n        info!(\"Download starting\");\n        self.offset = 0;\n        Ok(())\n    }\n\n    fn write(&mut self, data: &[u8]) -> Result<(), Status> {\n        if data.len() > BLOCK_SIZE {\n            error!(\"USB data len exceeded block size\");\n            return Err(Status::ErrUnknown);\n        }\n\n        debug!(\"Copying {} bytes to buffer\", data.len());\n        self.buf.as_mut()[..data.len()].copy_from_slice(data);\n\n        debug!(\"Writing {} bytes at {}\", data.len(), self.offset);\n        match self.updater.write_firmware(self.offset, self.buf.as_ref()) {\n            Ok(_) => {\n                self.offset += data.len();\n                Ok(())\n            }\n            Err(e) => {\n                error!(\"Error writing firmware: {:?}\", e);\n                Err(firmware_error_to_status(e))\n            }\n        }\n    }\n\n    fn finish(&mut self) -> Result<(), Status> {\n        debug!(\"Receiving final transfer\");\n\n        #[cfg(feature = \"_verify\")]\n        let update_res: Result<(), FirmwareUpdaterError> = {\n            const SIGNATURE_LEN: usize = 64;\n\n            let mut signature = [0; SIGNATURE_LEN];\n            let update_len = (self.offset - SIGNATURE_LEN) as u32;\n\n            self.updater.read_dfu(update_len, &mut signature).and_then(|_| {\n                self.updater\n                    .verify_and_mark_updated(self.public_key, &signature, update_len)\n            })\n        };\n\n        #[cfg(not(feature = \"_verify\"))]\n        let update_res = self.updater.mark_updated();\n\n        match update_res {\n            Ok(_) => {\n                info!(\"Update complete\");\n                Ok(())\n            }\n            Err(e) => {\n                error!(\"Error completing update: {}\", e);\n                Err(firmware_error_to_status(e))\n            }\n        }\n    }\n\n    fn system_reset(&mut self) {\n        self.reset.sys_reset()\n    }\n}\n\n/// Convenience type alias for the DFU state with firmware handler.\npub type State<'d, DFU, STATE, RST, const BLOCK_SIZE: usize> =\n    DfuState<FirmwareHandler<'d, DFU, STATE, RST, BLOCK_SIZE>>;\n\n/// Create a new DFU state instance.\n///\n/// This creates a `DfuState` with a `FirmwareHandler` inside, ready to be\n/// used with `usb_dfu`.\npub fn new_state<'d, DFU: NorFlash, STATE: NorFlash, RST: Reset, const BLOCK_SIZE: usize>(\n    updater: BlockingFirmwareUpdater<'d, DFU, STATE>,\n    attrs: DfuAttributes,\n    reset: RST,\n    #[cfg(feature = \"_verify\")] public_key: &'static [u8; 32],\n) -> State<'d, DFU, STATE, RST, BLOCK_SIZE> {\n    let handler = FirmwareHandler::new(\n        updater,\n        reset,\n        #[cfg(feature = \"_verify\")]\n        public_key,\n    );\n    DfuState::new(handler, attrs)\n}\n\n/// An implementation of the USB DFU 1.1 protocol\n///\n/// This function will add a DFU interface descriptor to the provided Builder, and register the provided Control as a handler for the USB device\n/// The handler is responsive to DFU GetState, GetStatus, Abort, and ClrStatus commands, as well as Download if configured by the user.\n///\n/// Once the host has initiated a DFU download operation, the chunks sent by the host will be written to the DFU partition.\n/// Once the final sync in the manifestation phase has been received, the handler will trigger a system reset to swap the new firmware.\npub fn usb_dfu<'d, D: Driver<'d>, DFU: NorFlash, STATE: NorFlash, RST: Reset, const BLOCK_SIZE: usize>(\n    builder: &mut Builder<'d, D>,\n    state: &'d mut State<'d, DFU, STATE, RST, BLOCK_SIZE>,\n    func_modifier: impl Fn(&mut FunctionBuilder<'_, 'd, D>),\n) {\n    dfu_mode::usb_dfu(builder, state, BLOCK_SIZE, func_modifier);\n}\n"
  },
  {
    "path": "embassy-usb-dfu/src/fmt.rs",
    "content": "#![macro_use]\n#![allow(unused)]\n\nuse core::fmt::{Debug, Display, LowerHex};\n\n#[cfg(all(feature = \"defmt\", feature = \"log\"))]\ncompile_error!(\"You may not enable both `defmt` and `log` features.\");\n\n#[collapse_debuginfo(yes)]\nmacro_rules! assert {\n    ($($x:tt)*) => {\n        {\n            #[cfg(not(feature = \"defmt\"))]\n            ::core::assert!($($x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::assert!($($x)*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! assert_eq {\n    ($($x:tt)*) => {\n        {\n            #[cfg(not(feature = \"defmt\"))]\n            ::core::assert_eq!($($x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::assert_eq!($($x)*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! assert_ne {\n    ($($x:tt)*) => {\n        {\n            #[cfg(not(feature = \"defmt\"))]\n            ::core::assert_ne!($($x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::assert_ne!($($x)*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! debug_assert {\n    ($($x:tt)*) => {\n        {\n            #[cfg(not(feature = \"defmt\"))]\n            ::core::debug_assert!($($x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::debug_assert!($($x)*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! debug_assert_eq {\n    ($($x:tt)*) => {\n        {\n            #[cfg(not(feature = \"defmt\"))]\n            ::core::debug_assert_eq!($($x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::debug_assert_eq!($($x)*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! debug_assert_ne {\n    ($($x:tt)*) => {\n        {\n            #[cfg(not(feature = \"defmt\"))]\n            ::core::debug_assert_ne!($($x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::debug_assert_ne!($($x)*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! todo {\n    ($($x:tt)*) => {\n        {\n            #[cfg(not(feature = \"defmt\"))]\n            ::core::todo!($($x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::todo!($($x)*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! unreachable {\n    ($($x:tt)*) => {\n        {\n            #[cfg(not(feature = \"defmt\"))]\n            ::core::unreachable!($($x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::unreachable!($($x)*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! panic {\n    ($($x:tt)*) => {\n        {\n            #[cfg(not(feature = \"defmt\"))]\n            ::core::panic!($($x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::panic!($($x)*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! trace {\n    ($s:literal $(, $x:expr)* $(,)?) => {\n        {\n            #[cfg(feature = \"log\")]\n            ::log::trace!($s $(, $x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::trace!($s $(, $x)*);\n            #[cfg(not(any(feature = \"log\", feature=\"defmt\")))]\n            let _ = ($( & $x ),*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! debug {\n    ($s:literal $(, $x:expr)* $(,)?) => {\n        {\n            #[cfg(feature = \"log\")]\n            ::log::debug!($s $(, $x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::debug!($s $(, $x)*);\n            #[cfg(not(any(feature = \"log\", feature=\"defmt\")))]\n            let _ = ($( & $x ),*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! info {\n    ($s:literal $(, $x:expr)* $(,)?) => {\n        {\n            #[cfg(feature = \"log\")]\n            ::log::info!($s $(, $x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::info!($s $(, $x)*);\n            #[cfg(not(any(feature = \"log\", feature=\"defmt\")))]\n            let _ = ($( & $x ),*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! warn {\n    ($s:literal $(, $x:expr)* $(,)?) => {\n        {\n            #[cfg(feature = \"log\")]\n            ::log::warn!($s $(, $x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::warn!($s $(, $x)*);\n            #[cfg(not(any(feature = \"log\", feature=\"defmt\")))]\n            let _ = ($( & $x ),*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! error {\n    ($s:literal $(, $x:expr)* $(,)?) => {\n        {\n            #[cfg(feature = \"log\")]\n            ::log::error!($s $(, $x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::error!($s $(, $x)*);\n            #[cfg(not(any(feature = \"log\", feature=\"defmt\")))]\n            let _ = ($( & $x ),*);\n        }\n    };\n}\n\n#[cfg(feature = \"defmt\")]\n#[collapse_debuginfo(yes)]\nmacro_rules! unwrap {\n    ($($x:tt)*) => {\n        ::defmt::unwrap!($($x)*)\n    };\n}\n\n#[cfg(not(feature = \"defmt\"))]\n#[collapse_debuginfo(yes)]\nmacro_rules! unwrap {\n    ($arg:expr) => {\n        match $crate::fmt::Try::into_result($arg) {\n            ::core::result::Result::Ok(t) => t,\n            ::core::result::Result::Err(e) => {\n                ::core::panic!(\"unwrap of `{}` failed: {:?}\", ::core::stringify!($arg), e);\n            }\n        }\n    };\n    ($arg:expr, $($msg:expr),+ $(,)? ) => {\n        match $crate::fmt::Try::into_result($arg) {\n            ::core::result::Result::Ok(t) => t,\n            ::core::result::Result::Err(e) => {\n                ::core::panic!(\"unwrap of `{}` failed: {}: {:?}\", ::core::stringify!($arg), ::core::format_args!($($msg,)*), e);\n            }\n        }\n    }\n}\n\n#[derive(Debug, Copy, Clone, Eq, PartialEq)]\npub struct NoneError;\n\npub trait Try {\n    type Ok;\n    type Error;\n    fn into_result(self) -> Result<Self::Ok, Self::Error>;\n}\n\nimpl<T> Try for Option<T> {\n    type Ok = T;\n    type Error = NoneError;\n\n    #[inline]\n    fn into_result(self) -> Result<T, NoneError> {\n        self.ok_or(NoneError)\n    }\n}\n\nimpl<T, E> Try for Result<T, E> {\n    type Ok = T;\n    type Error = E;\n\n    #[inline]\n    fn into_result(self) -> Self {\n        self\n    }\n}\n\npub(crate) struct Bytes<'a>(pub &'a [u8]);\n\nimpl<'a> Debug for Bytes<'a> {\n    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {\n        write!(f, \"{:#02x?}\", self.0)\n    }\n}\n\nimpl<'a> Display for Bytes<'a> {\n    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {\n        write!(f, \"{:#02x?}\", self.0)\n    }\n}\n\nimpl<'a> LowerHex for Bytes<'a> {\n    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {\n        write!(f, \"{:#02x?}\", self.0)\n    }\n}\n\n#[cfg(feature = \"defmt\")]\nimpl<'a> defmt::Format for Bytes<'a> {\n    fn format(&self, fmt: defmt::Formatter) {\n        defmt::write!(fmt, \"{:02x}\", self.0)\n    }\n}\n"
  },
  {
    "path": "embassy-usb-dfu/src/lib.rs",
    "content": "#![no_std]\n#![doc = include_str!(\"../README.md\")]\n#![warn(missing_docs)]\nmod fmt;\n\n/// Re-export DFU constants from embassy-usb.\npub mod consts {\n    pub use embassy_usb::class::dfu::consts::*;\n}\n\n#[cfg(feature = \"dfu\")]\npub mod dfu;\n#[cfg(all(feature = \"dfu\", not(feature = \"application\")))]\npub use self::dfu::*;\n\n#[cfg(feature = \"application\")]\npub mod application;\n#[cfg(all(feature = \"application\", not(feature = \"dfu\")))]\npub use self::application::*;\n\n/// Provides a platform-agnostic interface for initiating a system reset.\n///\n/// This crate exposes `ResetImmediate` when compiled with cortex-m or esp32c3 support, which immediately issues a\n/// reset request without interfacing with any other peripherals.\n///\n/// If alternate behaviour is desired, a custom implementation of Reset can be provided as an argument to the usb_dfu function.\npub trait Reset {\n    /// Reset the device.\n    fn sys_reset(&self);\n}\n\n/// Reset immediately.\n#[cfg(feature = \"esp32c3-hal\")]\npub struct ResetImmediate;\n\n#[cfg(feature = \"esp32c3-hal\")]\nimpl Reset for ResetImmediate {\n    fn sys_reset(&self) {\n        esp32c3_hal::reset::software_reset();\n        loop {}\n    }\n}\n\n/// Reset immediately.\n#[cfg(feature = \"cortex-m\")]\npub struct ResetImmediate;\n\n#[cfg(feature = \"cortex-m\")]\nimpl Reset for ResetImmediate {\n    fn sys_reset(&self) {\n        cortex_m::peripheral::SCB::sys_reset()\n    }\n}\n"
  },
  {
    "path": "embassy-usb-dfu/tests/usb_dfu_test.rs",
    "content": "use core::cell::RefCell;\nuse core::convert::Infallible;\n\nuse dfu_core::DfuIo;\nuse embassy_boot::{BlockingFirmwareUpdater, FirmwareUpdaterConfig};\nuse embassy_usb::Handler;\nuse embassy_usb::class::dfu::dfu_mode::Handler as DfuModeHandler;\nuse embassy_usb::control::{InResponse, OutResponse, Recipient, Request as ControlRequest, RequestType};\nuse embassy_usb::driver::Direction;\nuse embassy_usb_dfu::consts::DfuAttributes;\nuse embassy_usb_dfu::{Reset, UsbDfuState, new_state};\nuse embedded_storage::nor_flash::{ErrorType, NorFlash, ReadNorFlash};\n\nconst READ_WRITE_SIZE: usize = 8;\n\nstruct InMemoryFlashPartition<'a, const SIZE: usize> {\n    buffer: &'a RefCell<[u8; SIZE]>,\n}\n\nimpl<'a, const SIZE: usize> ReadNorFlash for InMemoryFlashPartition<'a, SIZE> {\n    const READ_SIZE: usize = READ_WRITE_SIZE;\n\n    fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Self::Error> {\n        let start = offset as usize;\n        bytes.copy_from_slice(&self.buffer.borrow()[start..start + bytes.len()]);\n        Ok(())\n    }\n\n    fn capacity(&self) -> usize {\n        self.buffer.borrow().len()\n    }\n}\n\nimpl<'a, const SIZE: usize> ErrorType for InMemoryFlashPartition<'a, SIZE> {\n    type Error = Infallible;\n}\n\nimpl<'a, const SIZE: usize> NorFlash for InMemoryFlashPartition<'a, SIZE> {\n    const WRITE_SIZE: usize = READ_WRITE_SIZE;\n\n    const ERASE_SIZE: usize = READ_WRITE_SIZE;\n\n    fn erase(&mut self, from: u32, to: u32) -> Result<(), Self::Error> {\n        let (from, to) = (from as usize, to as usize);\n        self.buffer.borrow_mut()[from..to].fill(0);\n        Ok(())\n    }\n\n    fn write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Self::Error> {\n        let start = offset as usize;\n        self.buffer.borrow_mut()[start..start + bytes.len()].copy_from_slice(bytes);\n        Ok(())\n    }\n}\n\nstruct NoopReset {}\n\nimpl Reset for NoopReset {\n    fn sys_reset(&self) {\n        // noop\n    }\n}\n\nstruct InMemoryDfu<H: DfuModeHandler> {\n    functional_descriptor: dfu_core::functional_descriptor::FunctionalDescriptor,\n    dfu_state: RefCell<UsbDfuState<H>>,\n}\n\nimpl<H: DfuModeHandler> DfuIo for InMemoryDfu<H> {\n    type Read = usize;\n\n    type Write = usize;\n\n    type Reset = ();\n\n    type Error = anyhow::Error;\n\n    type MemoryLayout = dfu_core::memory_layout::MemoryLayout;\n\n    fn read_control(\n        &self,\n        _request_type: u8,\n        request: u8,\n        value: u16,\n        buffer: &mut [u8],\n    ) -> Result<Self::Read, Self::Error> {\n        let req = ControlRequest {\n            direction: Direction::In,\n            request_type: RequestType::Class,\n            recipient: Recipient::Interface,\n            request,\n            value,\n            index: 0,\n            length: buffer.len() as u16,\n        };\n        let mut state = self.dfu_state.borrow_mut();\n        match state.control_in(req, buffer) {\n            Some(InResponse::Accepted(data)) => Ok(data.len()),\n            Some(InResponse::Rejected) => Err(anyhow::anyhow!(\"control in rejected\")),\n            None => Err(anyhow::anyhow!(\"control in returned None\")),\n        }\n    }\n\n    fn write_control(\n        &self,\n        _request_type: u8,\n        request: u8,\n        value: u16,\n        buffer: &[u8],\n    ) -> Result<Self::Write, Self::Error> {\n        let req = ControlRequest {\n            direction: Direction::Out,\n            request_type: RequestType::Class,\n            recipient: Recipient::Interface,\n            request,\n            value,\n            index: 0,\n            length: buffer.len() as u16,\n        };\n        let mut state = self.dfu_state.borrow_mut();\n        match state.control_out(req, buffer) {\n            Some(OutResponse::Accepted) => Ok(buffer.len()),\n            Some(OutResponse::Rejected) => Err(anyhow::anyhow!(\"control out rejected\")),\n            None => Err(anyhow::anyhow!(\"control out returned None\")),\n        }\n    }\n\n    fn usb_reset(&mut self) -> Result<Self::Reset, Self::Error> {\n        Ok(())\n    }\n\n    fn protocol(&self) -> &dfu_core::DfuProtocol<Self::MemoryLayout> {\n        &dfu_core::DfuProtocol::Dfu\n    }\n\n    fn functional_descriptor(&self) -> &dfu_core::functional_descriptor::FunctionalDescriptor {\n        &self.functional_descriptor\n    }\n}\n\nfn usb_dfu(dfu_attributes: DfuAttributes) {\n    let mut aligned_buffer = [0; READ_WRITE_SIZE];\n\n    const BLOCK_SIZE: usize = 128;\n\n    let dfu_buffer = RefCell::new([0; { BLOCK_SIZE * 2 }]);\n    let dfu_partition = InMemoryFlashPartition { buffer: &dfu_buffer };\n    let state_buffer = RefCell::new([0; { READ_WRITE_SIZE * 2 }]);\n    let state_partition = InMemoryFlashPartition { buffer: &state_buffer };\n    let fw_config = FirmwareUpdaterConfig {\n        dfu: dfu_partition,\n        state: state_partition,\n    };\n    let updater = BlockingFirmwareUpdater::new(fw_config, &mut aligned_buffer);\n\n    let functional_descriptor = dfu_core::functional_descriptor::FunctionalDescriptor {\n        can_download: true,\n        can_upload: false,\n        manifestation_tolerant: dfu_attributes.contains(DfuAttributes::MANIFESTATION_TOLERANT),\n        will_detach: dfu_attributes.contains(DfuAttributes::WILL_DETACH),\n        detach_timeout: 10,\n        transfer_size: READ_WRITE_SIZE as u16,\n        dfu_version: (1, 1),\n    };\n    let dfu_state = new_state::<_, _, _, BLOCK_SIZE>(updater, dfu_attributes, NoopReset {});\n    let mut dfu = dfu_core::sync::DfuSync::new(InMemoryDfu {\n        functional_descriptor,\n        dfu_state: RefCell::new(dfu_state),\n    });\n\n    let firmware = [42; BLOCK_SIZE];\n    let err = dfu.download_from_slice(&firmware);\n    println!(\"{:?}\", err);\n    assert_eq!(&dfu_buffer.borrow()[..firmware.len()], firmware);\n    assert!(err.is_ok());\n}\n\n#[test]\nfn test_usb_dfu_manifestation_tolerant_will_detach() {\n    usb_dfu(DfuAttributes::CAN_DOWNLOAD | DfuAttributes::MANIFESTATION_TOLERANT | DfuAttributes::WILL_DETACH);\n}\n\n#[test]\nfn test_usb_dfu_manifestation_tolerant() {\n    usb_dfu(DfuAttributes::CAN_DOWNLOAD | DfuAttributes::MANIFESTATION_TOLERANT);\n}\n\n#[test]\nfn test_usb_dfu_will_detach() {\n    usb_dfu(DfuAttributes::CAN_DOWNLOAD | DfuAttributes::WILL_DETACH);\n}\n\n#[test]\nfn test_usb_dfu() {\n    usb_dfu(DfuAttributes::CAN_DOWNLOAD);\n}\n"
  },
  {
    "path": "embassy-usb-driver/CHANGELOG.md",
    "content": "# Changelog for embassy-usb-driver\n\nAll notable changes to this project will be documented in this file.\n\nThe format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),\nand this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).\n\n<!-- next-header -->\n## Unreleased - ReleaseDate\n\n- Add `EndpointOut::read_data()` and `EndpointIn::write_data()` provided methods.\n\n## 0.2.0 - 2025-07-16\n\n- Make USB endpoint allocator methods accept an optional `EndpointAddress`.\n\n## 0.1.1 - 2025-07-15\n\n- Add `embedded_io_async::Error` implementation for `EndpointError` ([#4176](https://github.com/embassy-rs/embassy/pull/4176))\n"
  },
  {
    "path": "embassy-usb-driver/Cargo.toml",
    "content": "[package]\nname = \"embassy-usb-driver\"\nversion = \"0.2.0\"\nedition = \"2024\"\nlicense = \"MIT OR Apache-2.0\"\ndescription = \"Driver trait for `embassy-usb`, an async USB device stack for embedded devices.\"\nkeywords = [\"embedded\", \"async\", \"usb\", \"hal\", \"embedded-hal\"]\ncategories = [\"embedded\", \"hardware-support\", \"no-std\", \"asynchronous\"]\nrepository = \"https://github.com/embassy-rs/embassy\"\ndocumentation = \"https://docs.embassy.dev/embassy-usb-driver\"\n\n[package.metadata.embassy_docs]\nsrc_base = \"https://github.com/embassy-rs/embassy/blob/embassy-usb-driver-v$VERSION/embassy-usb-driver/src/\"\nsrc_base_git = \"https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-usb-driver/src/\"\nfeatures = [\"defmt\"]\ntarget = \"thumbv7em-none-eabi\"\n\n[package.metadata.docs.rs]\nfeatures = [\"defmt\"]\n\n[dependencies]\n# TODO: remove before releasing embassy-usb-driver v0.3\nembedded-io-async = \"0.7.0\"\ndefmt = { version = \"1\", optional = true }\n\n[features]\ndefmt = [\"dep:defmt\"]\n"
  },
  {
    "path": "embassy-usb-driver/README.md",
    "content": "# embassy-usb-driver\n\nThis crate contains the driver traits for [`embassy-usb`]. HAL/BSP crates can implement these\ntraits to add support for using `embassy-usb` for a given chip/platform.\n\nThe traits are kept in a separate crate so that breaking changes in the higher-level [`embassy-usb`]\nAPIs don't cause a semver-major bump of this crate. This allows existing HALs/BSPs to be used\nwith the newer `embassy-usb` without needing updates.\n\nIf you're writing an application using USB, you should depend on the main [`embassy-usb`] crate\ninstead of this one.\n\n[`embassy-usb`]: https://crates.io/crates/embassy-usb\n\n## Interoperability\n\nThis crate can run on any executor.\n"
  },
  {
    "path": "embassy-usb-driver/src/lib.rs",
    "content": "#![no_std]\n#![allow(async_fn_in_trait)]\n#![allow(unsafe_op_in_unsafe_fn)]\n#![doc = include_str!(\"../README.md\")]\n#![warn(missing_docs)]\n\n/// Direction of USB traffic. Note that in the USB standard the direction is always indicated from\n/// the perspective of the host, which is backward for devices, but the standard directions are used\n/// for consistency.\n///\n/// The values of the enum also match the direction bit used in endpoint addresses and control\n/// request types.\n#[derive(Copy, Clone, Eq, PartialEq, Debug)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub enum Direction {\n    /// Host to device (OUT)\n    Out,\n    /// Device to host (IN)\n    In,\n}\n\n/// USB endpoint transfer type. The values of this enum can be directly cast into `u8` to get the\n/// transfer bmAttributes transfer type bits.\n#[repr(u8)]\n#[derive(Copy, Clone, Eq, PartialEq, Debug)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub enum EndpointType {\n    /// Control endpoint. Used for device management. Only the host can initiate requests. Usually\n    /// used only endpoint 0.\n    Control = 0b00,\n    /// Isochronous endpoint. Used for time-critical unreliable data. Not implemented yet.\n    Isochronous = 0b01,\n    /// Bulk endpoint. Used for large amounts of best-effort reliable data.\n    Bulk = 0b10,\n    /// Interrupt endpoint. Used for small amounts of time-critical reliable data.\n    Interrupt = 0b11,\n}\n\n/// Type-safe endpoint address.\n#[derive(Debug, Clone, Copy, Eq, PartialEq)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub struct EndpointAddress(u8);\n\nimpl From<u8> for EndpointAddress {\n    #[inline]\n    fn from(addr: u8) -> EndpointAddress {\n        EndpointAddress(addr)\n    }\n}\n\nimpl From<EndpointAddress> for u8 {\n    #[inline]\n    fn from(addr: EndpointAddress) -> u8 {\n        addr.0\n    }\n}\n\nimpl EndpointAddress {\n    const INBITS: u8 = 0x80;\n\n    /// Constructs a new EndpointAddress with the given index and direction.\n    #[inline]\n    pub fn from_parts(index: usize, dir: Direction) -> Self {\n        let dir_u8 = match dir {\n            Direction::Out => 0x00,\n            Direction::In => Self::INBITS,\n        };\n        EndpointAddress(index as u8 | dir_u8)\n    }\n\n    /// Gets the direction part of the address.\n    #[inline]\n    pub fn direction(&self) -> Direction {\n        if (self.0 & Self::INBITS) != 0 {\n            Direction::In\n        } else {\n            Direction::Out\n        }\n    }\n\n    /// Returns true if the direction is IN, otherwise false.\n    #[inline]\n    pub fn is_in(&self) -> bool {\n        (self.0 & Self::INBITS) != 0\n    }\n\n    /// Returns true if the direction is OUT, otherwise false.\n    #[inline]\n    pub fn is_out(&self) -> bool {\n        (self.0 & Self::INBITS) == 0\n    }\n\n    /// Gets the index part of the endpoint address.\n    #[inline]\n    pub fn index(&self) -> usize {\n        (self.0 & !Self::INBITS) as usize\n    }\n}\n\n/// Information for an endpoint.\n#[derive(Copy, Clone, Eq, PartialEq, Debug)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub struct EndpointInfo {\n    /// Endpoint's address.\n    pub addr: EndpointAddress,\n    /// Endpoint's type.\n    pub ep_type: EndpointType,\n    /// Max packet size, in bytes.\n    pub max_packet_size: u16,\n    /// Polling interval, in milliseconds.\n    pub interval_ms: u8,\n}\n\n/// Main USB driver trait.\n///\n/// Implement this to add support for a new hardware platform.\npub trait Driver<'a> {\n    /// Type of the OUT endpoints for this driver.\n    type EndpointOut: EndpointOut + 'a;\n    /// Type of the IN endpoints for this driver.\n    type EndpointIn: EndpointIn + 'a;\n    /// Type of the control pipe for this driver.\n    type ControlPipe: ControlPipe + 'a;\n    /// Type for bus control for this driver.\n    type Bus: Bus + 'a;\n\n    /// Allocates an OUT endpoint.\n    ///\n    /// This method is called by the USB stack to allocate endpoints.\n    /// It can only be called before [`start`](Self::start) is called.\n    ///\n    /// # Arguments\n    ///\n    /// * `ep_type` - the endpoint's type.\n    /// * `max_packet_size` - Maximum packet size in bytes.\n    /// * `interval_ms` - Polling interval parameter for interrupt endpoints.\n    fn alloc_endpoint_out(\n        &mut self,\n        ep_type: EndpointType,\n        ep_addr: Option<EndpointAddress>,\n        max_packet_size: u16,\n        interval_ms: u8,\n    ) -> Result<Self::EndpointOut, EndpointAllocError>;\n\n    /// Allocates an IN endpoint.\n    ///\n    /// This method is called by the USB stack to allocate endpoints.\n    /// It can only be called before [`start`](Self::start) is called.\n    ///\n    /// # Arguments\n    ///\n    /// * `ep_type` - the endpoint's type.\n    /// * `max_packet_size` - Maximum packet size in bytes.\n    /// * `interval_ms` - Polling interval parameter for interrupt endpoints.\n    fn alloc_endpoint_in(\n        &mut self,\n        ep_type: EndpointType,\n        ep_addr: Option<EndpointAddress>,\n        max_packet_size: u16,\n        interval_ms: u8,\n    ) -> Result<Self::EndpointIn, EndpointAllocError>;\n\n    /// Start operation of the USB device.\n    ///\n    /// This returns the `Bus` and `ControlPipe` instances that are used to operate\n    /// the USB device. Additionally, this makes all the previously allocated endpoints\n    /// start operating.\n    ///\n    /// This consumes the `Driver` instance, so it's no longer possible to allocate more\n    /// endpoints.\n    fn start(self, control_max_packet_size: u16) -> (Self::Bus, Self::ControlPipe);\n}\n\n/// USB bus trait.\n///\n/// This trait provides methods that act on the whole bus. It is kept owned by\n/// the main USB task, and used to manage the bus.\npub trait Bus {\n    /// Enable the USB peripheral.\n    async fn enable(&mut self);\n\n    /// Disable and powers down the USB peripheral.\n    async fn disable(&mut self);\n\n    /// Wait for a bus-related event.\n    ///\n    /// This method should asynchronously wait for an event to happen, then\n    /// return it. See [`Event`] for the list of events this method should return.\n    async fn poll(&mut self) -> Event;\n\n    /// Enable or disable an endpoint.\n    fn endpoint_set_enabled(&mut self, ep_addr: EndpointAddress, enabled: bool);\n\n    /// Set or clear the STALL condition for an endpoint.\n    ///\n    /// If the endpoint is an OUT endpoint, it should be prepared to receive data again.\n    fn endpoint_set_stalled(&mut self, ep_addr: EndpointAddress, stalled: bool);\n\n    /// Get whether the STALL condition is set for an endpoint.\n    fn endpoint_is_stalled(&mut self, ep_addr: EndpointAddress) -> bool;\n\n    /// Simulate a disconnect from the USB bus, causing the host to reset and re-enumerate the\n    /// device.\n    ///\n    /// The default implementation just returns `Unsupported`.\n    ///\n    /// # Errors\n    ///\n    /// * [`Unsupported`] - This UsbBus implementation doesn't support\n    ///   simulating a disconnect or it has not been enabled at creation time.\n    fn force_reset(&mut self) -> Result<(), Unsupported> {\n        Err(Unsupported)\n    }\n\n    /// Initiate a remote wakeup of the host by the device.\n    ///\n    /// # Errors\n    ///\n    /// * [`Unsupported`] - This UsbBus implementation doesn't support\n    ///   remote wakeup or it has not been enabled at creation time.\n    async fn remote_wakeup(&mut self) -> Result<(), Unsupported>;\n}\n\n/// Endpoint trait, common for OUT and IN.\npub trait Endpoint {\n    /// Get the endpoint address\n    fn info(&self) -> &EndpointInfo;\n\n    /// Wait for the endpoint to be enabled.\n    async fn wait_enabled(&mut self);\n}\n\n/// OUT Endpoint trait.\npub trait EndpointOut: Endpoint {\n    /// Read a single packet of data from the endpoint, and return the actual length of\n    /// the packet.\n    ///\n    /// This should also clear any NAK flags and prepare the endpoint to receive the next packet.\n    async fn read(&mut self, buf: &mut [u8]) -> Result<usize, EndpointError>;\n\n    /// Read until the buffer is full or we receive a short packet from the USB host returning the\n    /// actual length of the entire data block.\n    ///\n    /// This should also clear any NAK flags and prepare the endpoint to receive the next packet or\n    /// data block.\n    async fn read_transfer(&mut self, buf: &mut [u8]) -> Result<usize, EndpointError> {\n        let mut n = 0;\n        loop {\n            let i = self.read(&mut buf[n..]).await?;\n            n += i;\n            if i < self.info().max_packet_size as usize {\n                return Ok(n);\n            }\n        }\n    }\n}\n\n/// USB control pipe trait.\n///\n/// The USB control pipe owns both OUT endpoint 0 and IN endpoint 0 in a single\n/// unit, and manages them together to implement the control pipe state machine.\n///\n/// The reason this is a separate trait instead of using EndpointOut/EndpointIn is that\n/// many USB peripherals treat the control pipe endpoints differently (different registers,\n/// different procedures), usually to accelerate processing in hardware somehow. A separate\n/// trait allows the driver to handle it specially.\n///\n/// The call sequences made by the USB stack to the ControlPipe are the following:\n///\n/// - control in/out with len=0:\n///\n/// ```not_rust\n/// setup()\n/// (...processing...)\n/// accept() or reject()\n/// ```\n///\n/// - control out for setting the device address:\n///\n/// ```not_rust\n/// setup()\n/// (...processing...)\n/// accept_set_address(addr) or reject()\n/// ```\n///\n/// - control out with len != 0:\n///\n/// ```not_rust\n/// setup()\n/// data_out(first=true, last=false)\n/// data_out(first=false, last=false)\n/// ...\n/// data_out(first=false, last=false)\n/// data_out(first=false, last=true)\n/// (...processing...)\n/// accept() or reject()\n/// ```\n///\n/// - control in with len != 0, accepted:\n///\n/// ```not_rust\n/// setup()\n/// (...processing...)\n/// data_in(first=true, last=false)\n/// data_in(first=false, last=false)\n/// ...\n/// data_in(first=false, last=false)\n/// data_in(first=false, last=true)\n/// (NO `accept()`!!! This is because calling `data_in` already implies acceptance.)\n/// ```\n///\n/// - control in with len != 0, rejected:\n///\n/// ```not_rust\n/// setup()\n/// (...processing...)\n/// reject()\n/// ```\n///\n/// The driver is responsible for handling the status stage. The stack DOES NOT do zero-length\n/// calls to `data_in` or `data_out` for the status zero-length packet. The status stage should\n/// be triggered by either `accept()`, or `data_in` with `last = true`.\n///\n/// Note that the host can abandon a control request and send a new SETUP packet any time. If\n/// a SETUP packet arrives at any time during `data_out`, `data_in`, `accept` or `reject`,\n/// the driver must immediately return (with `EndpointError::Disabled` from `data_in`, `data_out`)\n/// to let the stack call `setup()` again to start handling the new control request. Not doing\n/// so will cause things to get stuck, because the host will never read/send the packet we're\n/// waiting for.\npub trait ControlPipe {\n    /// Maximum packet size for the control pipe\n    fn max_packet_size(&self) -> usize;\n\n    /// Read a single setup packet from the endpoint.\n    async fn setup(&mut self) -> [u8; 8];\n\n    /// Read a DATA OUT packet into `buf` in response to a control write request.\n    ///\n    /// Must be called after `setup()` for requests with `direction` of `Out`\n    /// and `length` greater than zero.\n    async fn data_out(&mut self, buf: &mut [u8], first: bool, last: bool) -> Result<usize, EndpointError>;\n\n    /// Send a DATA IN packet with `data` in response to a control read request.\n    ///\n    /// If `last_packet` is true, the STATUS packet will be ACKed following the transfer of `data`.\n    async fn data_in(&mut self, data: &[u8], first: bool, last: bool) -> Result<(), EndpointError>;\n\n    /// Accept a control request.\n    ///\n    /// Causes the STATUS packet for the current request to be ACKed.\n    async fn accept(&mut self);\n\n    /// Reject a control request.\n    ///\n    /// Sets a STALL condition on the pipe to indicate an error.\n    async fn reject(&mut self);\n\n    /// Accept SET_ADDRESS control and change bus address.\n    ///\n    /// For most drivers this function should firstly call `accept()` and then change the bus address.\n    /// However, there are peripherals (Synopsys USB OTG) that have reverse order.\n    async fn accept_set_address(&mut self, addr: u8);\n}\n\n/// IN Endpoint trait.\npub trait EndpointIn: Endpoint {\n    /// Write a single packet of data to the endpoint.\n    async fn write(&mut self, buf: &[u8]) -> Result<(), EndpointError>;\n\n    /// Write all the data from buf to the endpoint one wMaxPacketSize chunk at a time.\n    ///\n    /// If the buffer size is evenly divisible by wMaxPacketSize, this will also ensure the\n    /// terminating zero-length-packet is transmitted.\n    async fn write_transfer(&mut self, buf: &[u8], needs_zlp: bool) -> Result<(), EndpointError> {\n        for chunk in buf.chunks(self.info().max_packet_size as usize) {\n            self.write(chunk).await?;\n        }\n        if needs_zlp && buf.len() % self.info().max_packet_size as usize == 0 {\n            self.write(&[]).await?;\n        }\n        Ok(())\n    }\n}\n\n#[derive(Copy, Clone, Eq, PartialEq, Debug)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\n/// Event returned by [`Bus::poll`].\npub enum Event {\n    /// The USB reset condition has been detected.\n    Reset,\n\n    /// A USB suspend request has been detected or, in the case of self-powered devices, the device\n    /// has been disconnected from the USB bus.\n    Suspend,\n\n    /// A USB resume request has been detected after being suspended or, in the case of self-powered\n    /// devices, the device has been connected to the USB bus.\n    Resume,\n\n    /// The USB power has been detected.\n    PowerDetected,\n\n    /// The USB power has been removed. Not supported by all devices.\n    PowerRemoved,\n}\n\n/// Allocating an endpoint failed.\n///\n/// This can be due to running out of endpoints, or out of endpoint memory,\n/// or because the hardware doesn't support the requested combination of features.\n#[derive(Copy, Clone, Eq, PartialEq, Debug)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub struct EndpointAllocError;\n\n/// Operation is unsupported by the driver.\n#[derive(Copy, Clone, Eq, PartialEq, Debug)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub struct Unsupported;\n\n/// Errors returned by [`EndpointIn::write`] and [`EndpointOut::read`]\n#[derive(Copy, Clone, Eq, PartialEq, Debug)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub enum EndpointError {\n    /// Either the packet to be written is too long to fit in the transmission\n    /// buffer or the received packet is too long to fit in `buf`.\n    BufferOverflow,\n\n    /// The endpoint is disabled.\n    Disabled,\n}\n\n// TODO: remove before releasing embassy-usb-driver v0.3\nimpl embedded_io_async::Error for EndpointError {\n    fn kind(&self) -> embedded_io_async::ErrorKind {\n        match self {\n            Self::BufferOverflow => embedded_io_async::ErrorKind::OutOfMemory,\n            Self::Disabled => embedded_io_async::ErrorKind::NotConnected,\n        }\n    }\n}\n\nimpl core::fmt::Display for EndpointError {\n    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {\n        match self {\n            Self::BufferOverflow => write!(f, \"Buffer overflow\"),\n            Self::Disabled => write!(f, \"Endpoint disabled\"),\n        }\n    }\n}\nimpl core::error::Error for EndpointError {}\n"
  },
  {
    "path": "embassy-usb-logger/CHANGELOG.md",
    "content": "# Changelog\n\nAll notable changes to this project will be documented in this file.\n\nThe format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),\nand this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).\n\n<!-- next-header -->\n## Unreleased - ReleaseDate\n\n## 0.6.0 - 2026-03-10\n\n- Fixed panic in `UsbLogger` when usb is disconnected\n- Upgrade embassy-usb to 0.6.0\n- Upgrade embassy-sync to 0.8.0\n\n## 0.5.1 - 2025-08-26\n\n## 0.5.0 - 2025-07-22\n\n- Update `embassy-usb` to 0.5.0\n\n## 0.4.0 - 2025-01-15\n\n- Update `embassy-usb` to 0.4.0\n\n(skipped v0.3.0 to align version numbers with `embassy-usb`)\n\n## 0.2.0 - 2024-05-20\n\n- Update `embassy-usb` to 0.2.0\n- Add support for using an existing USB device ([#2414](https://github.com/embassy-rs/embassy/pull/2414), @JomerDev)\n- Fix data loss at `Pipe` wraparound\n- Messages that are exactly `MAX_PACKET_SIZE` long are no longer delayed ([#2414](https://github.com/embassy-rs/embassy/pull/2414), @JomerDev)\n\n## 0.1.0 - 2024-01-14\n\n- Initial Release\n"
  },
  {
    "path": "embassy-usb-logger/Cargo.toml",
    "content": "[package]\nname = \"embassy-usb-logger\"\nversion = \"0.6.0\"\nedition = \"2024\"\nlicense = \"MIT OR Apache-2.0\"\ndescription = \"`log` implementation for USB serial using `embassy-usb`.\"\nkeywords = [\"embedded\", \"log\", \"usb\", \"hal\", \"serial\"]\ncategories = [\"embedded\", \"hardware-support\", \"no-std\", \"asynchronous\"]\nrepository = \"https://github.com/embassy-rs/embassy\"\ndocumentation = \"https://docs.embassy.dev/embassy-usb-logger\"\n\n[package.metadata.embassy_docs]\nsrc_base = \"https://github.com/embassy-rs/embassy/blob/embassy-usb-logger-v$VERSION/embassy-usb-logger/src/\"\nsrc_base_git = \"https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-usb-logger/src/\"\ntarget = \"thumbv7em-none-eabi\"\n\n[dependencies]\nembassy-usb = { version = \"0.6.0\", path = \"../embassy-usb\" }\nembassy-sync = { version = \"0.8.0\", path = \"../embassy-sync\" }\nembassy-futures = { version = \"0.1.2\", path = \"../embassy-futures\" }\nlog = \"0.4\"\n"
  },
  {
    "path": "embassy-usb-logger/README.md",
    "content": "# embassy-usb-logger\n\nUSB implementation of the `log` crate. This logger can be used by any device that implements `embassy-usb`. When running,\nit will output all logging done through the `log` facade to the USB serial peripheral.\n\n## Usage\n\nAdd the following embassy task to your application. The `Driver` type is different depending on which HAL you use.\n\n ```rust\n#[embassy_executor::task]\nasync fn logger_task(driver: Driver<'static, USB>) {\n    embassy_usb_logger::run!(1024, log::LevelFilter::Info, driver);\n}\n```\n"
  },
  {
    "path": "embassy-usb-logger/src/lib.rs",
    "content": "#![no_std]\n#![doc = include_str!(\"../README.md\")]\n#![warn(missing_docs)]\n\nuse core::fmt::Write as _;\nuse core::future::Future;\n\nuse embassy_futures::join::join;\nuse embassy_sync::pipe::Pipe;\nuse embassy_usb::class::cdc_acm::{CdcAcmClass, Receiver, Sender, State};\nuse embassy_usb::driver::{Driver, EndpointError};\nuse embassy_usb::{Builder, Config};\nuse log::{Metadata, Record};\n\ntype CS = embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;\n\n/// A trait that can be implemented and then passed to the\npub trait ReceiverHandler {\n    /// Data comes in from the serial port with each command and runs this function\n    fn handle_data(&self, data: &[u8]) -> impl Future<Output = ()> + Send;\n\n    /// Create a new instance of the Handler\n    fn new() -> Self;\n}\n\n/// Use this Handler if you don't wish to use any handler\npub struct DummyHandler;\n\nimpl ReceiverHandler for DummyHandler {\n    async fn handle_data(&self, _data: &[u8]) {}\n    fn new() -> Self {\n        Self {}\n    }\n}\n\n/// The logger state containing buffers that must live as long as the USB peripheral.\npub struct LoggerState<'d> {\n    state: State<'d>,\n    config_descriptor: [u8; 128],\n    bos_descriptor: [u8; 16],\n    msos_descriptor: [u8; 256],\n    control_buf: [u8; 64],\n}\n\nimpl<'d> LoggerState<'d> {\n    /// Create a new instance of the logger state.\n    pub fn new() -> Self {\n        Self {\n            state: State::new(),\n            config_descriptor: [0; 128],\n            bos_descriptor: [0; 16],\n            msos_descriptor: [0; 256],\n            control_buf: [0; 64],\n        }\n    }\n}\n\n/// The packet size used in the usb logger, to be used with `create_future_from_class`\npub const MAX_PACKET_SIZE: u8 = 64;\n\n/// The logger handle, which contains a pipe with configurable size for buffering log messages.\npub struct UsbLogger<const N: usize, T: ReceiverHandler + Send + Sync> {\n    buffer: Pipe<CS, N>,\n    custom_style: Option<fn(&Record, &mut Writer<'_, N>) -> ()>,\n    recieve_handler: Option<T>,\n}\n\nimpl<const N: usize, T: ReceiverHandler + Send + Sync> UsbLogger<N, T> {\n    /// Create a new logger instance.\n    pub const fn new() -> Self {\n        Self {\n            buffer: Pipe::new(),\n            custom_style: None,\n            recieve_handler: None,\n        }\n    }\n\n    /// Create a new logger instance with a custom formatter.\n    pub const fn with_custom_style(custom_style: fn(&Record, &mut Writer<'_, N>) -> ()) -> Self {\n        Self {\n            buffer: Pipe::new(),\n            custom_style: Some(custom_style),\n            recieve_handler: None,\n        }\n    }\n\n    /// Add a command handler to the logger\n    pub fn with_handler(&mut self, handler: T) {\n        self.recieve_handler = Some(handler);\n    }\n\n    /// Run the USB logger using the state and USB driver. Never returns.\n    pub async fn run<'d, D>(&'d self, state: &'d mut LoggerState<'d>, driver: D) -> !\n    where\n        D: Driver<'d>,\n        Self: 'd,\n    {\n        let mut config = Config::new(0xc0de, 0xcafe);\n        config.manufacturer = Some(\"Embassy\");\n        config.product = Some(\"USB-serial logger\");\n        config.serial_number = None;\n        config.max_power = 100;\n        config.max_packet_size_0 = MAX_PACKET_SIZE;\n\n        let mut builder = Builder::new(\n            driver,\n            config,\n            &mut state.config_descriptor,\n            &mut state.bos_descriptor,\n            &mut state.msos_descriptor,\n            &mut state.control_buf,\n        );\n\n        // Create classes on the builder.\n        let class = CdcAcmClass::new(&mut builder, &mut state.state, MAX_PACKET_SIZE as u16);\n        let (mut sender, mut receiver) = class.split();\n\n        // Build the builder.\n        let mut device = builder.build();\n        loop {\n            let run_fut = device.run();\n            let class_fut = self.run_logger_class(&mut sender, &mut receiver);\n            join(run_fut, class_fut).await;\n        }\n    }\n\n    async fn run_logger_class<'d, D>(&self, sender: &mut Sender<'d, D>, receiver: &mut Receiver<'d, D>)\n    where\n        D: Driver<'d>,\n    {\n        let log_fut = async {\n            let mut rx: [u8; MAX_PACKET_SIZE as usize] = [0; MAX_PACKET_SIZE as usize];\n            sender.wait_connection().await;\n            loop {\n                let len = self.buffer.read(&mut rx[..]).await;\n                if Err(EndpointError::Disabled) == sender.write_packet(&rx[..len]).await\n                    || len as u8 == MAX_PACKET_SIZE && Err(EndpointError::Disabled) == sender.write_packet(&[]).await\n                {\n                    sender.wait_connection().await;\n                };\n            }\n        };\n        let reciever_fut = async {\n            let mut reciever_buf: [u8; MAX_PACKET_SIZE as usize] = [0; MAX_PACKET_SIZE as usize];\n            receiver.wait_connection().await;\n            loop {\n                let n = match receiver.read_packet(&mut reciever_buf).await {\n                    Err(EndpointError::Disabled) => {\n                        receiver.wait_connection().await;\n                        continue;\n                    }\n                    Err(_) => continue,\n                    Ok(n) => n,\n                };\n                match &self.recieve_handler {\n                    Some(handler) => {\n                        let data = &reciever_buf[..n];\n                        handler.handle_data(data).await;\n                    }\n                    None => (),\n                }\n            }\n        };\n\n        join(log_fut, reciever_fut).await;\n    }\n\n    /// Creates the futures needed for the logger from a given class\n    /// This can be used in cases where the usb device is already in use for another connection\n    /// Never returns.\n    pub async fn create_future_from_class<'d, D>(&'d self, class: CdcAcmClass<'d, D>) -> !\n    where\n        D: Driver<'d>,\n    {\n        let (mut sender, mut receiver) = class.split();\n        loop {\n            self.run_logger_class(&mut sender, &mut receiver).await;\n        }\n    }\n}\n\nimpl<const N: usize, T: ReceiverHandler + Send + Sync> log::Log for UsbLogger<N, T> {\n    fn enabled(&self, _metadata: &Metadata) -> bool {\n        true\n    }\n\n    fn log(&self, record: &Record) {\n        if self.enabled(record.metadata()) {\n            if let Some(custom_style) = self.custom_style {\n                custom_style(record, &mut Writer(&self.buffer));\n            } else {\n                let _ = write!(Writer(&self.buffer), \"{}\\r\\n\", record.args());\n            }\n        }\n    }\n\n    fn flush(&self) {}\n}\n\n/// A writer that writes to the USB logger buffer.\npub struct Writer<'d, const N: usize>(&'d Pipe<CS, N>);\n\nimpl<'d, const N: usize> core::fmt::Write for Writer<'d, N> {\n    fn write_str(&mut self, s: &str) -> Result<(), core::fmt::Error> {\n        // The Pipe is implemented in such way that we cannot\n        // write across the wraparound discontinuity.\n        let b = s.as_bytes();\n        if let Ok(n) = self.0.try_write(b) {\n            if n < b.len() {\n                // We wrote some data but not all, attempt again\n                // as the reason might be a wraparound in the\n                // ring buffer, which resolves on second attempt.\n                let _ = self.0.try_write(&b[n..]);\n            }\n        }\n        Ok(())\n    }\n}\n\n/// Initialize and run the USB serial logger, never returns.\n///\n/// Arguments specify the buffer size, log level and the USB driver, respectively. You can optionally add a RecieverHandler.\n///\n/// # Usage\n///\n/// ```\n/// embassy_usb_logger::run!(1024, log::LevelFilter::Info, driver); //never returns\n/// ```\n///\n/// # Safety\n///\n/// This macro should only be invoked only once since it is setting the global logging state of the application.\n#[macro_export]\nmacro_rules! run {\n    ( $x:expr, $l:expr, $p:ident ) => {\n        static LOGGER: ::embassy_usb_logger::UsbLogger<$x, ::embassy_usb_logger::DummyHandler> =\n            ::embassy_usb_logger::UsbLogger::new();\n        unsafe {\n            let _ = ::log::set_logger_racy(&LOGGER).map(|()| log::set_max_level_racy($l));\n        }\n        let _ = LOGGER.run(&mut ::embassy_usb_logger::LoggerState::new(), $p).await;\n    };\n\n    ( $x:expr, $l:expr, $p:ident, $h:ty ) => {\n        unsafe {\n            static mut LOGGER: ::embassy_usb_logger::UsbLogger<$x, $h> = ::embassy_usb_logger::UsbLogger::new();\n            LOGGER.with_handler(<$h>::new());\n            let _ = ::log::set_logger_racy(&LOGGER).map(|()| log::set_max_level_racy($l));\n            let _ = LOGGER.run(&mut ::embassy_usb_logger::LoggerState::new(), $p).await;\n        }\n    };\n}\n\n/// Initialize the USB serial logger from a serial class and return the future to run it.\n/// The future never returns.\n///\n/// Arguments specify the buffer size, log level and the serial class, respectively. You can optionally add a RecieverHandler.\n///\n/// # Usage\n///\n/// ```\n/// embassy_usb_logger::with_class!(1024, log::LevelFilter::Info, class).await; //never returns.\n/// ```\n///\n/// # Safety\n///\n/// This macro should only be invoked only once since it is setting the global logging state of the application.\n#[macro_export]\nmacro_rules! with_class {\n    ( $x:expr, $l:expr, $p:ident ) => {{\n        static LOGGER: ::embassy_usb_logger::UsbLogger<$x, ::embassy_usb_logger::DummyHandler> =\n            ::embassy_usb_logger::UsbLogger::new();\n        unsafe {\n            let _ = ::log::set_logger_racy(&LOGGER).map(|()| log::set_max_level_racy($l));\n        }\n        LOGGER.create_future_from_class($p)\n    }};\n\n    ( $x:expr, $l:expr, $p:ident, $h:ty ) => {{\n        unsafe {\n            static mut LOGGER: ::embassy_usb_logger::UsbLogger<$x, $h> = ::embassy_usb_logger::UsbLogger::new();\n            LOGGER.with_handler(<$h>::new());\n            let _ = ::log::set_logger_racy(&LOGGER).map(|()| log::set_max_level_racy($l));\n            LOGGER.create_future_from_class($p)\n        }\n    }};\n}\n\n/// Initialize the USB serial logger from a serial class and return the future to run it.\n/// The future never returns.\n/// This version of the macro allows for a custom style function to be passed in.\n/// The custom style function will be called for each log record and is responsible for writing the log message to the buffer.\n///\n/// Arguments specify the buffer size, log level, the serial class and the custom style function, respectively. You can optionally add a RecieverHandler.\n///\n/// # Usage\n///\n/// ```\n/// let log_fut = embassy_usb_logger::with_custom_style!(1024, log::LevelFilter::Info, logger_class, |record, writer| {\n///     use core::fmt::Write;\n///     let level = record.level().as_str();\n///     write!(writer, \"[{level}] {}\\r\\n\", record.args()).unwrap();\n/// });\n/// log_fut.await; // never returns\n/// ```\n///\n/// # Safety\n///\n/// This macro should only be invoked only once since it is setting the global logging state of the application.\n#[macro_export]\nmacro_rules! with_custom_style {\n    ( $x:expr, $l:expr, $p:ident, $s:expr ) => {{\n        static LOGGER: ::embassy_usb_logger::UsbLogger<$x, ::embassy_usb_logger::DummyHandler> =\n            ::embassy_usb_logger::UsbLogger::with_custom_style($s);\n        unsafe {\n            let _ = ::log::set_logger_racy(&LOGGER).map(|()| log::set_max_level_racy($l));\n        }\n        LOGGER.create_future_from_class($p)\n    }};\n\n    ( $x:expr, $l:expr, $p:ident, $s:expr, $h:ty ) => {{\n        unsafe {\n            static mut LOGGER: ::embassy_usb_logger::UsbLogger<$x, $h> =\n                ::embassy_usb_logger::UsbLogger::with_custom_style($s);\n            LOGGER.with_handler(<$h>::new());\n            let _ = ::log::set_logger_racy(&LOGGER).map(|()| log::set_max_level_racy($l));\n            LOGGER.create_future_from_class($p)\n        }\n    }};\n}\n"
  },
  {
    "path": "embassy-usb-synopsys-otg/CHANGELOG.md",
    "content": "# Changelog for embassy-usb-synopsys-otg\n\nAll notable changes to this project will be documented in this file.\n\nThe format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),\nand this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).\n\n<!-- next-header -->\n## Unreleased - ReleaseDate\n\n## 0.3.2 - 2026-03-10\n\n- Disabling an OUT endpoint no longer flushes the TX FIFO of the corresponding IN endpoint\n- Upgrade embassy-sync to 0.8.0\n\n## 0.3.1 - 2025-08-26\n\n- Improve receive performance, more efficient copy from FIFO\n\n## 0.3.0 - 2025-07-22\n\n- Bump `embassy-usb-driver` to v0.2.0\n\n## 0.2.0 - 2024-12-06\n\n- Fix corruption in CONTROL OUT transfers (and remove `quirk_setup_late_cnak`)\n- Fix build with `defmt` enabled\n- Add USBPHYC clock configuration for H7RS series\n- Add support for ISO endpoints\n- Add support for a full-speed ULPI mode\n- Add OTG core DMA address registers\n- Ensure endpoint allocation fails when `endpoint_count < MAX_EP_COUNT`.\n- New configuration option: `xcvrdly` (transceiver delay).\n- `EpState` now implements `Send` and `Sync`.\n- The default value of `vbus_detection` is now `false`.\n\n## 0.1.0 - 2024-04-30\n\nInitial release.\n"
  },
  {
    "path": "embassy-usb-synopsys-otg/Cargo.toml",
    "content": "[package]\nname = \"embassy-usb-synopsys-otg\"\nversion = \"0.3.2\"\nedition = \"2024\"\nlicense = \"MIT OR Apache-2.0\"\ndescription = \"`embassy-usb-driver` implementation for Synopsys OTG USB controllers\"\nkeywords = [\"embedded\", \"async\", \"usb\", \"hal\", \"embedded-hal\"]\ncategories = [\"embedded\", \"hardware-support\", \"no-std\", \"asynchronous\"]\nrepository = \"https://github.com/embassy-rs/embassy\"\ndocumentation = \"https://docs.embassy.dev/embassy-usb-synopsys-otg\"\n\n[package.metadata.embassy_docs]\nsrc_base = \"https://github.com/embassy-rs/embassy/blob/embassy-usb-synopsys-otg-v$VERSION/embassy-usb-synopsys-otg/src/\"\nsrc_base_git = \"https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-usb-synopsys-otg/src/\"\nfeatures = [\"defmt\"]\ntarget = \"thumbv7em-none-eabi\"\n\n[dependencies]\ncritical-section = \"1.1\"\n\nembassy-sync = { version = \"0.8.0\", path = \"../embassy-sync\" }\nembassy-usb-driver = { version = \"0.2.0\", path = \"../embassy-usb-driver\" }\n\ndefmt = { version = \"1.0.1\", optional = true }\nlog = { version = \"0.4.14\", optional = true }\n\n[features]\ndefmt = [\"dep:defmt\", \"embassy-usb-driver/defmt\"]\nlog = [\"dep:log\"]\n"
  },
  {
    "path": "embassy-usb-synopsys-otg/README.md",
    "content": "# Embassy USB driver for the Synopsys USB OTG core\n\nThis crate implements [`embassy-usb-driver`](https://crates.io/crates/embassy-usb-driver) for Synopsys USB OTG devices.\n\nIt contains the \"core\" of the driver that is common across all chips using\nthe Synopsys OTG IP, but it doesn't contain chip-specific initialization such\nas clock setup and GPIO muxing. You most likely don't want to use this crate\ndirectly, but use it through a HAL that does the initialization for you.\n\nList of HALs integrating this driver:\n\n- [`embassy-stm32`](https://crates.io/crates/embassy-stm32), for STMicroelectronics STM32 chips.\n- [`esp-hal`](https://crates.io/crates/esp-hal), for Espressif ESP32 chips.\n\nIf you wish to integrate this crate into your device's HAL, you will need to add the\ndevice-specific initialization. See the above crates for examples on how to do it."
  },
  {
    "path": "embassy-usb-synopsys-otg/src/fmt.rs",
    "content": "#![macro_use]\n#![allow(unused)]\n\nuse core::fmt::{Debug, Display, LowerHex};\n\n#[cfg(all(feature = \"defmt\", feature = \"log\"))]\ncompile_error!(\"You may not enable both `defmt` and `log` features.\");\n\n#[collapse_debuginfo(yes)]\nmacro_rules! assert {\n    ($($x:tt)*) => {\n        {\n            #[cfg(not(feature = \"defmt\"))]\n            ::core::assert!($($x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::assert!($($x)*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! assert_eq {\n    ($($x:tt)*) => {\n        {\n            #[cfg(not(feature = \"defmt\"))]\n            ::core::assert_eq!($($x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::assert_eq!($($x)*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! assert_ne {\n    ($($x:tt)*) => {\n        {\n            #[cfg(not(feature = \"defmt\"))]\n            ::core::assert_ne!($($x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::assert_ne!($($x)*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! debug_assert {\n    ($($x:tt)*) => {\n        {\n            #[cfg(not(feature = \"defmt\"))]\n            ::core::debug_assert!($($x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::debug_assert!($($x)*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! debug_assert_eq {\n    ($($x:tt)*) => {\n        {\n            #[cfg(not(feature = \"defmt\"))]\n            ::core::debug_assert_eq!($($x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::debug_assert_eq!($($x)*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! debug_assert_ne {\n    ($($x:tt)*) => {\n        {\n            #[cfg(not(feature = \"defmt\"))]\n            ::core::debug_assert_ne!($($x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::debug_assert_ne!($($x)*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! todo {\n    ($($x:tt)*) => {\n        {\n            #[cfg(not(feature = \"defmt\"))]\n            ::core::todo!($($x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::todo!($($x)*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! unreachable {\n    ($($x:tt)*) => {\n        {\n            #[cfg(not(feature = \"defmt\"))]\n            ::core::unreachable!($($x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::unreachable!($($x)*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! panic {\n    ($($x:tt)*) => {\n        {\n            #[cfg(not(feature = \"defmt\"))]\n            ::core::panic!($($x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::panic!($($x)*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! trace {\n    ($s:literal $(, $x:expr)* $(,)?) => {\n        {\n            #[cfg(feature = \"log\")]\n            ::log::trace!($s $(, $x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::trace!($s $(, $x)*);\n            #[cfg(not(any(feature = \"log\", feature=\"defmt\")))]\n            let _ = ($( & $x ),*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! debug {\n    ($s:literal $(, $x:expr)* $(,)?) => {\n        {\n            #[cfg(feature = \"log\")]\n            ::log::debug!($s $(, $x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::debug!($s $(, $x)*);\n            #[cfg(not(any(feature = \"log\", feature=\"defmt\")))]\n            let _ = ($( & $x ),*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! info {\n    ($s:literal $(, $x:expr)* $(,)?) => {\n        {\n            #[cfg(feature = \"log\")]\n            ::log::info!($s $(, $x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::info!($s $(, $x)*);\n            #[cfg(not(any(feature = \"log\", feature=\"defmt\")))]\n            let _ = ($( & $x ),*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! warn {\n    ($s:literal $(, $x:expr)* $(,)?) => {\n        {\n            #[cfg(feature = \"log\")]\n            ::log::warn!($s $(, $x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::warn!($s $(, $x)*);\n            #[cfg(not(any(feature = \"log\", feature=\"defmt\")))]\n            let _ = ($( & $x ),*);\n        }\n    };\n}\n\n#[collapse_debuginfo(yes)]\nmacro_rules! error {\n    ($s:literal $(, $x:expr)* $(,)?) => {\n        {\n            #[cfg(feature = \"log\")]\n            ::log::error!($s $(, $x)*);\n            #[cfg(feature = \"defmt\")]\n            ::defmt::error!($s $(, $x)*);\n            #[cfg(not(any(feature = \"log\", feature=\"defmt\")))]\n            let _ = ($( & $x ),*);\n        }\n    };\n}\n\n#[cfg(feature = \"defmt\")]\n#[collapse_debuginfo(yes)]\nmacro_rules! unwrap {\n    ($($x:tt)*) => {\n        ::defmt::unwrap!($($x)*)\n    };\n}\n\n#[cfg(not(feature = \"defmt\"))]\n#[collapse_debuginfo(yes)]\nmacro_rules! unwrap {\n    ($arg:expr) => {\n        match $crate::fmt::Try::into_result($arg) {\n            ::core::result::Result::Ok(t) => t,\n            ::core::result::Result::Err(e) => {\n                ::core::panic!(\"unwrap of `{}` failed: {:?}\", ::core::stringify!($arg), e);\n            }\n        }\n    };\n    ($arg:expr, $($msg:expr),+ $(,)? ) => {\n        match $crate::fmt::Try::into_result($arg) {\n            ::core::result::Result::Ok(t) => t,\n            ::core::result::Result::Err(e) => {\n                ::core::panic!(\"unwrap of `{}` failed: {}: {:?}\", ::core::stringify!($arg), ::core::format_args!($($msg,)*), e);\n            }\n        }\n    }\n}\n\n#[derive(Debug, Copy, Clone, Eq, PartialEq)]\npub struct NoneError;\n\npub trait Try {\n    type Ok;\n    type Error;\n    fn into_result(self) -> Result<Self::Ok, Self::Error>;\n}\n\nimpl<T> Try for Option<T> {\n    type Ok = T;\n    type Error = NoneError;\n\n    #[inline]\n    fn into_result(self) -> Result<T, NoneError> {\n        self.ok_or(NoneError)\n    }\n}\n\nimpl<T, E> Try for Result<T, E> {\n    type Ok = T;\n    type Error = E;\n\n    #[inline]\n    fn into_result(self) -> Self {\n        self\n    }\n}\n\npub(crate) struct Bytes<'a>(pub &'a [u8]);\n\nimpl<'a> Debug for Bytes<'a> {\n    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {\n        write!(f, \"{:#02x?}\", self.0)\n    }\n}\n\nimpl<'a> Display for Bytes<'a> {\n    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {\n        write!(f, \"{:#02x?}\", self.0)\n    }\n}\n\nimpl<'a> LowerHex for Bytes<'a> {\n    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {\n        write!(f, \"{:#02x?}\", self.0)\n    }\n}\n\n#[cfg(feature = \"defmt\")]\nimpl<'a> defmt::Format for Bytes<'a> {\n    fn format(&self, fmt: defmt::Formatter) {\n        defmt::write!(fmt, \"{:02x}\", self.0)\n    }\n}\n"
  },
  {
    "path": "embassy-usb-synopsys-otg/src/lib.rs",
    "content": "#![cfg_attr(not(test), no_std)]\n#![allow(async_fn_in_trait)]\n#![allow(unsafe_op_in_unsafe_fn)]\n#![doc = include_str!(\"../README.md\")]\n#![warn(missing_docs)]\n\n// This must go FIRST so that all the other modules see its macros.\nmod fmt;\n\nuse core::cell::UnsafeCell;\nuse core::future::poll_fn;\nuse core::marker::PhantomData;\nuse core::sync::atomic::{AtomicBool, AtomicU16, AtomicU32, Ordering};\nuse core::task::Poll;\n\nuse embassy_sync::waitqueue::AtomicWaker;\nuse embassy_usb_driver::{\n    Bus as _, Direction, EndpointAddress, EndpointAllocError, EndpointError, EndpointIn, EndpointInfo, EndpointOut,\n    EndpointType, Event, Unsupported,\n};\n\nuse crate::fmt::Bytes;\n\npub mod otg_v1;\n\nuse otg_v1::{Otg, regs, vals};\n\n/// Handle interrupts.\npub unsafe fn on_interrupt<const MAX_EP_COUNT: usize>(r: Otg, state: &State<MAX_EP_COUNT>, ep_count: usize) {\n    trace!(\"irq\");\n\n    let ints = r.gintsts().read();\n    if ints.wkupint() || ints.usbsusp() || ints.usbrst() || ints.enumdne() || ints.otgint() || ints.srqint() {\n        // Mask interrupts and notify `Bus` to process them\n        r.gintmsk().write(|w| {\n            w.set_iepint(true);\n            w.set_oepint(true);\n            w.set_rxflvlm(true);\n        });\n        state.bus_waker.wake();\n    }\n\n    // Handle RX\n    while r.gintsts().read().rxflvl() {\n        let status = r.grxstsp().read();\n        trace!(\"=== status {:08x}\", status.0);\n        let ep_num = status.epnum() as usize;\n        let len = status.bcnt() as usize;\n\n        assert!(ep_num < ep_count);\n\n        match status.pktstsd() {\n            vals::Pktstsd::SETUP_DATA_RX => {\n                trace!(\"SETUP_DATA_RX\");\n                assert!(len == 8, \"invalid SETUP packet length={}\", len);\n                assert!(ep_num == 0, \"invalid SETUP packet endpoint={}\", ep_num);\n\n                // flushing TX if something stuck in control endpoint\n                if r.dieptsiz(ep_num).read().pktcnt() != 0 {\n                    r.grstctl().modify(|w| {\n                        w.set_txfnum(ep_num as _);\n                        w.set_txfflsh(true);\n                    });\n                    while r.grstctl().read().txfflsh() {}\n                }\n\n                let data = &state.cp_state.setup_data;\n                data[0].store(r.fifo(0).read().data(), Ordering::Relaxed);\n                data[1].store(r.fifo(0).read().data(), Ordering::Relaxed);\n            }\n            vals::Pktstsd::OUT_DATA_RX => {\n                trace!(\"OUT_DATA_RX ep={} len={}\", ep_num, len);\n\n                if state.ep_states[ep_num].out_size.load(Ordering::Acquire) == EP_OUT_BUFFER_EMPTY {\n                    // SAFETY: Buffer size is allocated to be equal to endpoint's maximum packet size\n                    // We trust the peripheral to not exceed its configured MPSIZ\n                    let buf =\n                        unsafe { core::slice::from_raw_parts_mut(*state.ep_states[ep_num].out_buffer.get(), len) };\n\n                    let mut chunks = buf.chunks_exact_mut(4);\n                    for chunk in &mut chunks {\n                        // RX FIFO is shared so always read from fifo(0)\n                        let data = r.fifo(0).read().0;\n                        chunk.copy_from_slice(&data.to_ne_bytes());\n                    }\n                    let rem = chunks.into_remainder();\n                    if !rem.is_empty() {\n                        let data = r.fifo(0).read().0;\n                        rem.copy_from_slice(&data.to_ne_bytes()[0..rem.len()]);\n                    }\n\n                    state.ep_states[ep_num].out_size.store(len as u16, Ordering::Release);\n                    state.ep_states[ep_num].out_waker.wake();\n                } else {\n                    error!(\"ep_out buffer overflow index={}\", ep_num);\n\n                    // discard FIFO data\n                    let len_words = (len + 3) / 4;\n                    for _ in 0..len_words {\n                        r.fifo(0).read().data();\n                    }\n                }\n            }\n            vals::Pktstsd::OUT_DATA_DONE => {\n                trace!(\"OUT_DATA_DONE ep={}\", ep_num);\n            }\n            vals::Pktstsd::SETUP_DATA_DONE => {\n                trace!(\"SETUP_DATA_DONE ep={}\", ep_num);\n            }\n            x => trace!(\"unknown PKTSTS: {}\", x.to_bits()),\n        }\n    }\n\n    // IN endpoint interrupt\n    if ints.iepint() {\n        let mut ep_mask = r.daint().read().iepint();\n        let mut ep_num = 0;\n\n        // Iterate over endpoints while there are non-zero bits in the mask\n        while ep_mask != 0 {\n            if ep_mask & 1 != 0 {\n                let ep_ints = r.diepint(ep_num).read();\n\n                // clear all\n                r.diepint(ep_num).write_value(ep_ints);\n\n                // TXFE is cleared in DIEPEMPMSK\n                if ep_ints.txfe() {\n                    critical_section::with(|_| {\n                        r.diepempmsk().modify(|w| {\n                            w.set_ineptxfem(w.ineptxfem() & !(1 << ep_num));\n                        });\n                    });\n                }\n\n                state.ep_states[ep_num].in_waker.wake();\n                trace!(\"in ep={} irq val={:08x}\", ep_num, ep_ints.0);\n            }\n\n            ep_mask >>= 1;\n            ep_num += 1;\n        }\n    }\n\n    // out endpoint interrupt\n    if ints.oepint() {\n        trace!(\"oepint\");\n        let mut ep_mask = r.daint().read().oepint();\n        let mut ep_num = 0;\n\n        // Iterate over endpoints while there are non-zero bits in the mask\n        while ep_mask != 0 {\n            if ep_mask & 1 != 0 {\n                let ep_ints = r.doepint(ep_num).read();\n                // clear all\n                r.doepint(ep_num).write_value(ep_ints);\n\n                if ep_ints.stup() {\n                    state.cp_state.setup_ready.store(true, Ordering::Release);\n                }\n                state.ep_states[ep_num].out_waker.wake();\n                trace!(\"out ep={} irq val={:08x}\", ep_num, ep_ints.0);\n            }\n\n            ep_mask >>= 1;\n            ep_num += 1;\n        }\n    }\n\n    if ints.eopf() {\n        let frame_number = r.dsts().read().fnsof();\n        let frame_is_odd = frame_number & 0x01 == 1;\n\n        // If an isochronous endpoint has an IN message waiting in its FIFO, but the host didn't poll for it before eof,\n        // switch the packet polarity, in the hope that it will be polled for in the next frame.\n        for ep_num in (0..ep_count).into_iter().filter(|ep_num| {\n            let diepctl = r.diepctl(*ep_num).read();\n            // Find iso endpoints\n            diepctl.eptyp() == vals::Eptyp::ISOCHRONOUS\n                // That have and unsent IN message\n                && diepctl.epena()\n                // Where the frame polarity matches the current frame\n                && diepctl.eonum_dpid() == frame_is_odd\n        }) {\n            trace!(\"Unsent message at EOF for ep: {}, frame: {}\", ep_num, frame_number);\n\n            let ep_diepctl = r.diepctl(ep_num);\n            let ep_diepint = r.diepint(ep_num);\n\n            // Set NAK\n            ep_diepctl.modify(|m| m.set_snak(true));\n            while !ep_diepint.read().inepne() {}\n\n            // Disable the endpoint\n            ep_diepctl.modify(|m| {\n                m.set_snak(true);\n                m.set_epdis(true);\n            });\n            while !ep_diepint.read().epdisd() {}\n            ep_diepint.modify(|m| m.set_epdisd(true));\n\n            // Switch the packet polarity\n            ep_diepctl.modify(|r| {\n                if frame_is_odd {\n                    r.set_sd0pid_sevnfrm(true);\n                } else {\n                    r.set_sd1pid_soddfrm(true);\n                }\n            });\n\n            // Enable the endpoint again\n            ep_diepctl.modify(|w| {\n                w.set_cnak(true);\n                w.set_epena(true);\n            });\n        }\n    }\n}\n\n/// USB PHY type\n#[derive(Copy, Clone, Debug, Eq, PartialEq)]\npub enum PhyType {\n    /// Internal Full-Speed PHY\n    ///\n    /// Available on most High-Speed peripherals.\n    InternalFullSpeed,\n    /// Internal High-Speed PHY\n    ///\n    /// Available on a few STM32 chips.\n    InternalHighSpeed,\n    /// External ULPI Full-Speed PHY (or High-Speed PHY in Full-Speed mode)\n    ExternalFullSpeed,\n    /// External ULPI High-Speed PHY\n    ExternalHighSpeed,\n}\n\nimpl PhyType {\n    /// Get whether this PHY is any of the internal types.\n    pub fn internal(&self) -> bool {\n        match self {\n            PhyType::InternalFullSpeed | PhyType::InternalHighSpeed => true,\n            PhyType::ExternalHighSpeed | PhyType::ExternalFullSpeed => false,\n        }\n    }\n\n    /// Get whether this PHY is any of the high-speed types.\n    pub fn high_speed(&self) -> bool {\n        match self {\n            PhyType::InternalFullSpeed | PhyType::ExternalFullSpeed => false,\n            PhyType::ExternalHighSpeed | PhyType::InternalHighSpeed => true,\n        }\n    }\n\n    fn to_dspd(&self) -> vals::Dspd {\n        match self {\n            PhyType::InternalFullSpeed => vals::Dspd::FULL_SPEED_INTERNAL,\n            PhyType::InternalHighSpeed => vals::Dspd::HIGH_SPEED,\n            PhyType::ExternalFullSpeed => vals::Dspd::FULL_SPEED_EXTERNAL,\n            PhyType::ExternalHighSpeed => vals::Dspd::HIGH_SPEED,\n        }\n    }\n}\n\n/// Indicates that [State::ep_out_buffers] is empty.\nconst EP_OUT_BUFFER_EMPTY: u16 = u16::MAX;\n\nstruct EpState {\n    in_waker: AtomicWaker,\n    out_waker: AtomicWaker,\n    /// RX FIFO is shared so extra buffers are needed to dequeue all data without waiting on each endpoint.\n    /// Buffers are ready when associated [State::ep_out_size] != [EP_OUT_BUFFER_EMPTY].\n    out_buffer: UnsafeCell<*mut u8>,\n    out_size: AtomicU16,\n}\n\n// SAFETY: The EndpointAllocator ensures that the buffer points to valid memory exclusive for each endpoint and is\n// large enough to hold the maximum packet size. Access to the buffer is synchronized between the USB interrupt and the\n// EndpointOut impl using the out_size atomic variable.\nunsafe impl Send for EpState {}\nunsafe impl Sync for EpState {}\n\nstruct ControlPipeSetupState {\n    /// Holds received SETUP packets. Available if [Ep0State::setup_ready] is true.\n    setup_data: [AtomicU32; 2],\n    setup_ready: AtomicBool,\n}\n\n/// USB OTG driver state.\npub struct State<const EP_COUNT: usize> {\n    cp_state: ControlPipeSetupState,\n    ep_states: [EpState; EP_COUNT],\n    bus_waker: AtomicWaker,\n}\n\nunsafe impl<const EP_COUNT: usize> Send for State<EP_COUNT> {}\nunsafe impl<const EP_COUNT: usize> Sync for State<EP_COUNT> {}\n\nimpl<const EP_COUNT: usize> State<EP_COUNT> {\n    /// Create a new State.\n    pub const fn new() -> Self {\n        Self {\n            cp_state: ControlPipeSetupState {\n                setup_data: [const { AtomicU32::new(0) }; 2],\n                setup_ready: AtomicBool::new(false),\n            },\n            ep_states: [const {\n                EpState {\n                    in_waker: AtomicWaker::new(),\n                    out_waker: AtomicWaker::new(),\n                    out_buffer: UnsafeCell::new(0 as _),\n                    out_size: AtomicU16::new(EP_OUT_BUFFER_EMPTY),\n                }\n            }; EP_COUNT],\n            bus_waker: AtomicWaker::new(),\n        }\n    }\n}\n\n#[derive(Debug, Clone, Copy)]\nstruct EndpointData {\n    ep_type: EndpointType,\n    max_packet_size: u16,\n    fifo_size_words: u16,\n}\n\n/// USB driver config.\n#[non_exhaustive]\n#[derive(Clone, Copy, PartialEq, Eq, Debug)]\npub struct Config {\n    /// Enable VBUS detection.\n    ///\n    /// The USB spec requires USB devices monitor for USB cable plug/unplug and react accordingly.\n    /// This is done by checking whether there is 5V on the VBUS pin or not.\n    ///\n    /// If your device is bus-powered (powers itself from the USB host via VBUS), then this is optional.\n    /// (If there's no power in VBUS your device would be off anyway, so it's fine to always assume\n    /// there's power in VBUS, i.e. the USB cable is always plugged in.)\n    ///\n    /// If your device is self-powered (i.e. it gets power from a source other than the USB cable, and\n    /// therefore can stay powered through USB cable plug/unplug) then you MUST set this to true.\n    ///\n    /// If you set this to true, you must connect VBUS to PA9 for FS, PB13 for HS, possibly with a\n    /// voltage divider. See ST application note AN4879 and the reference manual for more details.\n    pub vbus_detection: bool,\n\n    /// Enable transceiver delay.\n    ///\n    /// Some ULPI PHYs like the Microchip USB334x series require a delay between the ULPI register write that initiates\n    /// the HS Chirp and the subsequent transmit command, otherwise the HS Chirp does not get executed and the deivce\n    /// enumerates in FS mode. Some USB Link IP like those in the STM32H7 series support adding this delay to work with\n    /// the affected PHYs.\n    pub xcvrdly: bool,\n}\n\nimpl Default for Config {\n    fn default() -> Self {\n        Self {\n            vbus_detection: false,\n            xcvrdly: false,\n        }\n    }\n}\n\n/// USB OTG driver.\npub struct Driver<'d, const MAX_EP_COUNT: usize> {\n    config: Config,\n    ep_in: [Option<EndpointData>; MAX_EP_COUNT],\n    ep_out: [Option<EndpointData>; MAX_EP_COUNT],\n    ep_out_buffer: &'d mut [u8],\n    ep_out_buffer_offset: usize,\n    instance: OtgInstance<'d, MAX_EP_COUNT>,\n}\n\nimpl<'d, const MAX_EP_COUNT: usize> Driver<'d, MAX_EP_COUNT> {\n    /// Initializes the USB OTG peripheral.\n    ///\n    /// # Arguments\n    ///\n    /// * `ep_out_buffer` - An internal buffer used to temporarily store received packets.\n    /// Must be large enough to fit all OUT endpoint max packet sizes.\n    /// Endpoint allocation will fail if it is too small.\n    /// * `instance` - The USB OTG peripheral instance and its configuration.\n    /// * `config` - The USB driver configuration.\n    pub fn new(ep_out_buffer: &'d mut [u8], instance: OtgInstance<'d, MAX_EP_COUNT>, config: Config) -> Self {\n        Self {\n            config,\n            ep_in: [None; MAX_EP_COUNT],\n            ep_out: [None; MAX_EP_COUNT],\n            ep_out_buffer,\n            ep_out_buffer_offset: 0,\n            instance,\n        }\n    }\n\n    /// Returns the total amount of words (u32) allocated in dedicated FIFO.\n    fn allocated_fifo_words(&self) -> u16 {\n        self.instance.extra_rx_fifo_words + ep_fifo_size(&self.ep_out) + ep_fifo_size(&self.ep_in)\n    }\n\n    /// Creates an [`Endpoint`] with the given parameters.\n    fn alloc_endpoint<D: Dir>(\n        &mut self,\n        ep_type: EndpointType,\n        ep_addr: Option<EndpointAddress>,\n        max_packet_size: u16,\n        interval_ms: u8,\n    ) -> Result<Endpoint<'d, D>, EndpointAllocError> {\n        trace!(\n            \"allocating type={:?} mps={:?} interval_ms={}, dir={:?}\",\n            ep_type,\n            max_packet_size,\n            interval_ms,\n            D::dir()\n        );\n\n        if D::dir() == Direction::Out {\n            if self.ep_out_buffer_offset + max_packet_size as usize > self.ep_out_buffer.len() {\n                error!(\"Not enough endpoint out buffer capacity\");\n                return Err(EndpointAllocError);\n            }\n        };\n\n        let fifo_size_words = match D::dir() {\n            Direction::Out => (max_packet_size + 3) / 4,\n            // INEPTXFD requires minimum size of 16 words\n            Direction::In => u16::max((max_packet_size + 3) / 4, 16),\n        };\n\n        if fifo_size_words + self.allocated_fifo_words() > self.instance.fifo_depth_words {\n            error!(\"Not enough FIFO capacity\");\n            return Err(EndpointAllocError);\n        }\n\n        let eps = match D::dir() {\n            Direction::Out => &mut self.ep_out[..self.instance.endpoint_count],\n            Direction::In => &mut self.ep_in[..self.instance.endpoint_count],\n        };\n\n        // Find endpoint slot\n        let slot = if let Some(addr) = ep_addr {\n            // Use the specified endpoint address\n            let requested_index = addr.index();\n            if requested_index >= self.instance.endpoint_count {\n                return Err(EndpointAllocError);\n            }\n            if requested_index == 0 && ep_type != EndpointType::Control {\n                return Err(EndpointAllocError); // EP0 is reserved for control\n            }\n            if eps[requested_index].is_some() {\n                return Err(EndpointAllocError); // Already allocated\n            }\n            Some((requested_index, &mut eps[requested_index]))\n        } else {\n            // Find any free endpoint slot\n            eps.iter_mut().enumerate().find(|(i, ep)| {\n                if *i == 0 && ep_type != EndpointType::Control {\n                    // reserved for control pipe\n                    false\n                } else {\n                    ep.is_none()\n                }\n            })\n        };\n\n        let index = match slot {\n            Some((index, ep)) => {\n                *ep = Some(EndpointData {\n                    ep_type,\n                    max_packet_size,\n                    fifo_size_words,\n                });\n                index\n            }\n            None => {\n                error!(\"No free endpoints available\");\n                return Err(EndpointAllocError);\n            }\n        };\n\n        trace!(\"  index={}\", index);\n\n        let state = &self.instance.state.ep_states[index];\n        if D::dir() == Direction::Out {\n            // Buffer capacity check was done above, now allocation cannot fail\n            unsafe {\n                *state.out_buffer.get() = self.ep_out_buffer.as_mut_ptr().offset(self.ep_out_buffer_offset as _);\n            }\n            self.ep_out_buffer_offset += max_packet_size as usize;\n        }\n\n        Ok(Endpoint {\n            _phantom: PhantomData,\n            regs: self.instance.regs,\n            state,\n            info: EndpointInfo {\n                addr: EndpointAddress::from_parts(index, D::dir()),\n                ep_type,\n                max_packet_size,\n                interval_ms,\n            },\n        })\n    }\n}\n\nimpl<'d, const MAX_EP_COUNT: usize> embassy_usb_driver::Driver<'d> for Driver<'d, MAX_EP_COUNT> {\n    type EndpointOut = Endpoint<'d, Out>;\n    type EndpointIn = Endpoint<'d, In>;\n    type ControlPipe = ControlPipe<'d>;\n    type Bus = Bus<'d, MAX_EP_COUNT>;\n\n    fn alloc_endpoint_in(\n        &mut self,\n        ep_type: EndpointType,\n        ep_addr: Option<EndpointAddress>,\n        max_packet_size: u16,\n        interval_ms: u8,\n    ) -> Result<Self::EndpointIn, EndpointAllocError> {\n        self.alloc_endpoint(ep_type, ep_addr, max_packet_size, interval_ms)\n    }\n\n    fn alloc_endpoint_out(\n        &mut self,\n        ep_type: EndpointType,\n        ep_addr: Option<EndpointAddress>,\n        max_packet_size: u16,\n        interval_ms: u8,\n    ) -> Result<Self::EndpointOut, EndpointAllocError> {\n        self.alloc_endpoint(ep_type, ep_addr, max_packet_size, interval_ms)\n    }\n\n    fn start(mut self, control_max_packet_size: u16) -> (Self::Bus, Self::ControlPipe) {\n        let ep_out = self\n            .alloc_endpoint(EndpointType::Control, None, control_max_packet_size, 0)\n            .unwrap();\n        let ep_in = self\n            .alloc_endpoint(EndpointType::Control, None, control_max_packet_size, 0)\n            .unwrap();\n        assert_eq!(ep_out.info.addr.index(), 0);\n        assert_eq!(ep_in.info.addr.index(), 0);\n\n        trace!(\"start\");\n\n        let regs = self.instance.regs;\n        let cp_setup_state = &self.instance.state.cp_state;\n        (\n            Bus {\n                config: self.config,\n                ep_in: self.ep_in,\n                ep_out: self.ep_out,\n                inited: false,\n                instance: self.instance,\n            },\n            ControlPipe {\n                max_packet_size: control_max_packet_size,\n                setup_state: cp_setup_state,\n                ep_out,\n                ep_in,\n                regs,\n            },\n        )\n    }\n}\n\n/// USB bus.\npub struct Bus<'d, const MAX_EP_COUNT: usize> {\n    config: Config,\n    ep_in: [Option<EndpointData>; MAX_EP_COUNT],\n    ep_out: [Option<EndpointData>; MAX_EP_COUNT],\n    instance: OtgInstance<'d, MAX_EP_COUNT>,\n    inited: bool,\n}\n\nimpl<'d, const MAX_EP_COUNT: usize> Bus<'d, MAX_EP_COUNT> {\n    fn restore_irqs(&mut self) {\n        self.instance.regs.gintmsk().write(|w| {\n            w.set_usbrst(true);\n            w.set_enumdnem(true);\n            w.set_usbsuspm(true);\n            w.set_wuim(true);\n            w.set_iepint(true);\n            w.set_oepint(true);\n            w.set_rxflvlm(true);\n            w.set_srqim(true);\n            w.set_otgint(true);\n        });\n    }\n\n    /// Returns the PHY type.\n    pub fn phy_type(&self) -> PhyType {\n        self.instance.phy_type\n    }\n\n    /// Configures the PHY as a device.\n    pub fn configure_as_device(&mut self) {\n        let r = self.instance.regs;\n        let phy_type = self.instance.phy_type;\n        r.gusbcfg().write(|w| {\n            // Force device mode\n            w.set_fdmod(true);\n            // Enable internal full-speed PHY\n            w.set_physel(phy_type.internal() && !phy_type.high_speed());\n        });\n    }\n\n    /// Applies configuration specific to\n    /// Core ID 0x0000_1100 and 0x0000_1200\n    pub fn config_v1(&mut self) {\n        let r = self.instance.regs;\n        let phy_type = self.instance.phy_type;\n        assert!(phy_type != PhyType::InternalHighSpeed);\n\n        r.gccfg_v1().modify(|w| {\n            // Enable internal full-speed PHY, logic is inverted\n            w.set_pwrdwn(phy_type.internal());\n        });\n\n        // F429-like chips have the GCCFG.NOVBUSSENS bit\n        r.gccfg_v1().modify(|w| {\n            w.set_novbussens(!self.config.vbus_detection);\n            w.set_vbusasen(false);\n            w.set_vbusbsen(self.config.vbus_detection);\n            w.set_sofouten(false);\n        });\n    }\n\n    /// Applies configuration specific to\n    /// Core ID 0x0000_2000, 0x0000_2100, 0x0000_2300, 0x0000_3000 and 0x0000_3100\n    pub fn config_v2v3(&mut self) {\n        let r = self.instance.regs;\n        let phy_type = self.instance.phy_type;\n\n        // F446-like chips have the GCCFG.VBDEN bit with the opposite meaning\n        r.gccfg_v2().modify(|w| {\n            // Enable internal full-speed PHY, logic is inverted\n            w.set_pwrdwn(phy_type.internal() && !phy_type.high_speed());\n            w.set_phyhsen(phy_type.internal() && phy_type.high_speed());\n        });\n\n        r.gccfg_v2().modify(|w| {\n            w.set_vbden(self.config.vbus_detection);\n        });\n\n        // Force B-peripheral session\n        r.gotgctl().modify(|w| {\n            w.set_bvaloen(!self.config.vbus_detection);\n            w.set_bvaloval(true);\n        });\n    }\n\n    /// Applies configuration specific to\n    /// Core ID 0x0000_5000\n    pub fn config_v5(&mut self) {\n        let r = self.instance.regs;\n        let phy_type = self.instance.phy_type;\n\n        if phy_type == PhyType::InternalHighSpeed {\n            r.gccfg_v3().modify(|w| {\n                w.set_vbvaloven(!self.config.vbus_detection);\n                w.set_vbvaloval(!self.config.vbus_detection);\n                w.set_vbden(self.config.vbus_detection);\n            });\n        } else {\n            r.gotgctl().modify(|w| {\n                w.set_bvaloen(!self.config.vbus_detection);\n                w.set_bvaloval(!self.config.vbus_detection);\n            });\n            r.gccfg_v3().modify(|w| {\n                w.set_vbden(self.config.vbus_detection);\n            });\n        }\n    }\n\n    fn init(&mut self) {\n        let r = self.instance.regs;\n        let phy_type = self.instance.phy_type;\n\n        // Soft disconnect.\n        r.dctl().write(|w| w.set_sdis(true));\n\n        // Set speed.\n        r.dcfg().write(|w| {\n            w.set_pfivl(vals::Pfivl::FRAME_INTERVAL_80);\n            w.set_dspd(phy_type.to_dspd());\n            if self.config.xcvrdly {\n                w.set_xcvrdly(true);\n            }\n        });\n\n        // Unmask transfer complete EP interrupt\n        r.diepmsk().write(|w| {\n            w.set_xfrcm(true);\n        });\n\n        // Unmask SETUP received EP interrupt\n        r.doepmsk().write(|w| {\n            w.set_stupm(true);\n        });\n\n        // Unmask and clear core interrupts\n        self.restore_irqs();\n        r.gintsts().write_value(regs::Gintsts(0xFFFF_FFFF));\n\n        // Unmask global interrupt\n        r.gahbcfg().write(|w| {\n            w.set_gint(true); // unmask global interrupt\n        });\n\n        // Connect\n        r.dctl().write(|w| w.set_sdis(false));\n    }\n\n    fn init_fifo(&mut self) {\n        trace!(\"init_fifo\");\n\n        let regs = self.instance.regs;\n        // ERRATA NOTE: Don't interrupt FIFOs being written to. The interrupt\n        // handler COULD interrupt us here and do FIFO operations, so ensure\n        // the interrupt does not occur.\n        critical_section::with(|_| {\n            // Configure RX fifo size. All endpoints share the same FIFO area.\n            let rx_fifo_size_words = self.instance.extra_rx_fifo_words + ep_fifo_size(&self.ep_out);\n            trace!(\"configuring rx fifo size={}\", rx_fifo_size_words);\n\n            regs.grxfsiz().modify(|w| w.set_rxfd(rx_fifo_size_words));\n\n            // Configure TX (USB in direction) fifo size for each endpoint\n            let mut fifo_top = rx_fifo_size_words;\n            for i in 0..self.instance.endpoint_count {\n                if let Some(ep) = self.ep_in[i] {\n                    trace!(\n                        \"configuring tx fifo ep={}, offset={}, size={}\",\n                        i, fifo_top, ep.fifo_size_words\n                    );\n\n                    let dieptxf = if i == 0 { regs.dieptxf0() } else { regs.dieptxf(i - 1) };\n\n                    dieptxf.write(|w| {\n                        w.set_fd(ep.fifo_size_words);\n                        w.set_sa(fifo_top);\n                    });\n\n                    fifo_top += ep.fifo_size_words;\n                }\n            }\n\n            assert!(\n                fifo_top <= self.instance.fifo_depth_words,\n                \"FIFO allocations exceeded maximum capacity\"\n            );\n\n            // Flush fifos\n            regs.grstctl().write(|w| {\n                w.set_rxfflsh(true);\n                w.set_txfflsh(true);\n                w.set_txfnum(0x10);\n            });\n        });\n\n        loop {\n            let x = regs.grstctl().read();\n            if !x.rxfflsh() && !x.txfflsh() {\n                break;\n            }\n        }\n    }\n\n    fn configure_endpoints(&mut self) {\n        trace!(\"configure_endpoints\");\n\n        let regs = self.instance.regs;\n\n        // Configure IN endpoints\n        for (index, ep) in self.ep_in.iter().enumerate() {\n            if let Some(ep) = ep {\n                critical_section::with(|_| {\n                    regs.diepctl(index).write(|w| {\n                        if index == 0 {\n                            w.set_mpsiz(ep0_mpsiz(ep.max_packet_size));\n                        } else {\n                            w.set_mpsiz(ep.max_packet_size);\n                            w.set_eptyp(to_eptyp(ep.ep_type));\n                            w.set_sd0pid_sevnfrm(true);\n                            w.set_txfnum(index as _);\n                            w.set_snak(true);\n                        }\n                    });\n                });\n            }\n        }\n\n        // Configure OUT endpoints\n        for (index, ep) in self.ep_out.iter().enumerate() {\n            if let Some(ep) = ep {\n                critical_section::with(|_| {\n                    regs.doepctl(index).write(|w| {\n                        if index == 0 {\n                            w.set_mpsiz(ep0_mpsiz(ep.max_packet_size));\n                        } else {\n                            w.set_mpsiz(ep.max_packet_size);\n                            w.set_eptyp(to_eptyp(ep.ep_type));\n                            w.set_sd0pid_sevnfrm(true);\n                        }\n                    });\n\n                    regs.doeptsiz(index).modify(|w| {\n                        w.set_xfrsiz(ep.max_packet_size as _);\n                        if index == 0 {\n                            w.set_rxdpid_stupcnt(3);\n                        } else {\n                            w.set_pktcnt(1);\n                        }\n                    });\n                });\n            }\n        }\n\n        // Enable IRQs for allocated endpoints\n        regs.daintmsk().modify(|w| {\n            w.set_iepm(ep_irq_mask(&self.ep_in));\n            w.set_oepm(ep_irq_mask(&self.ep_out));\n        });\n    }\n\n    fn disable_all_endpoints(&mut self) {\n        for i in 0..self.instance.endpoint_count {\n            self.endpoint_set_enabled(EndpointAddress::from_parts(i, Direction::In), false);\n            self.endpoint_set_enabled(EndpointAddress::from_parts(i, Direction::Out), false);\n        }\n    }\n}\n\nimpl<'d, const MAX_EP_COUNT: usize> embassy_usb_driver::Bus for Bus<'d, MAX_EP_COUNT> {\n    async fn poll(&mut self) -> Event {\n        poll_fn(move |cx| {\n            if !self.inited {\n                self.init();\n                self.inited = true;\n\n                // If no vbus detection, just return a single PowerDetected event at startup.\n                if !self.config.vbus_detection {\n                    return Poll::Ready(Event::PowerDetected);\n                }\n            }\n\n            let regs = self.instance.regs;\n            self.instance.state.bus_waker.register(cx.waker());\n\n            let ints = regs.gintsts().read();\n\n            if ints.srqint() {\n                trace!(\"vbus detected\");\n\n                regs.gintsts().write(|w| w.set_srqint(true)); // clear\n                self.restore_irqs();\n\n                if self.config.vbus_detection {\n                    return Poll::Ready(Event::PowerDetected);\n                }\n            }\n\n            if ints.otgint() {\n                let otgints = regs.gotgint().read();\n                regs.gotgint().write_value(otgints); // clear all\n                self.restore_irqs();\n\n                if otgints.sedet() {\n                    trace!(\"vbus removed\");\n                    if self.config.vbus_detection {\n                        self.disable_all_endpoints();\n                        return Poll::Ready(Event::PowerRemoved);\n                    }\n                }\n            }\n\n            if ints.usbrst() {\n                trace!(\"reset\");\n\n                self.init_fifo();\n                self.configure_endpoints();\n\n                // Reset address\n                critical_section::with(|_| {\n                    regs.dcfg().modify(|w| {\n                        w.set_dad(0);\n                    });\n                });\n\n                regs.gintsts().write(|w| w.set_usbrst(true)); // clear\n                self.restore_irqs();\n            }\n\n            if ints.enumdne() {\n                trace!(\"enumdne\");\n\n                let speed = regs.dsts().read().enumspd();\n                let trdt = (self.instance.calculate_trdt_fn)(speed);\n                trace!(\"  speed={} trdt={}\", speed.to_bits(), trdt);\n                regs.gusbcfg().modify(|w| w.set_trdt(trdt));\n\n                regs.gintsts().write(|w| w.set_enumdne(true)); // clear\n                self.restore_irqs();\n\n                return Poll::Ready(Event::Reset);\n            }\n\n            if ints.usbsusp() {\n                trace!(\"suspend\");\n                regs.gintsts().write(|w| w.set_usbsusp(true)); // clear\n                self.restore_irqs();\n                return Poll::Ready(Event::Suspend);\n            }\n\n            if ints.wkupint() {\n                trace!(\"resume\");\n                regs.gintsts().write(|w| w.set_wkupint(true)); // clear\n                self.restore_irqs();\n                return Poll::Ready(Event::Resume);\n            }\n\n            Poll::Pending\n        })\n        .await\n    }\n\n    fn endpoint_set_stalled(&mut self, ep_addr: EndpointAddress, stalled: bool) {\n        trace!(\"endpoint_set_stalled ep={:?} en={}\", ep_addr, stalled);\n\n        assert!(\n            ep_addr.index() < self.instance.endpoint_count,\n            \"endpoint_set_stalled index {} out of range\",\n            ep_addr.index()\n        );\n\n        let regs = self.instance.regs;\n        let state = self.instance.state;\n        match ep_addr.direction() {\n            Direction::Out => {\n                critical_section::with(|_| {\n                    regs.doepctl(ep_addr.index()).modify(|w| {\n                        w.set_stall(stalled);\n                    });\n                });\n\n                state.ep_states[ep_addr.index()].out_waker.wake();\n            }\n            Direction::In => {\n                critical_section::with(|_| {\n                    regs.diepctl(ep_addr.index()).modify(|w| {\n                        w.set_stall(stalled);\n                    });\n                });\n\n                state.ep_states[ep_addr.index()].in_waker.wake();\n            }\n        }\n    }\n\n    fn endpoint_is_stalled(&mut self, ep_addr: EndpointAddress) -> bool {\n        assert!(\n            ep_addr.index() < self.instance.endpoint_count,\n            \"endpoint_is_stalled index {} out of range\",\n            ep_addr.index()\n        );\n\n        let regs = self.instance.regs;\n        match ep_addr.direction() {\n            Direction::Out => regs.doepctl(ep_addr.index()).read().stall(),\n            Direction::In => regs.diepctl(ep_addr.index()).read().stall(),\n        }\n    }\n\n    fn endpoint_set_enabled(&mut self, ep_addr: EndpointAddress, enabled: bool) {\n        trace!(\"endpoint_set_enabled ep={:?} en={}\", ep_addr, enabled);\n\n        assert!(\n            ep_addr.index() < self.instance.endpoint_count,\n            \"endpoint_set_enabled index {} out of range\",\n            ep_addr.index()\n        );\n\n        let regs = self.instance.regs;\n        let state = self.instance.state;\n        match ep_addr.direction() {\n            Direction::Out => {\n                critical_section::with(|_| {\n                    // cancel transfer if active\n                    if !enabled && regs.doepctl(ep_addr.index()).read().epena() {\n                        regs.doepctl(ep_addr.index()).modify(|w| {\n                            w.set_snak(true);\n                            w.set_epdis(true);\n                        })\n                    }\n\n                    regs.doepctl(ep_addr.index()).modify(|w| {\n                        w.set_usbaep(enabled);\n                    });\n                });\n\n                // Wake `Endpoint::wait_enabled()`\n                state.ep_states[ep_addr.index()].out_waker.wake();\n            }\n            Direction::In => {\n                critical_section::with(|_| {\n                    // cancel transfer if active\n                    if !enabled && regs.diepctl(ep_addr.index()).read().epena() {\n                        regs.diepctl(ep_addr.index()).modify(|w| {\n                            w.set_snak(true); // set NAK\n                            w.set_epdis(true);\n                        })\n                    }\n\n                    regs.diepctl(ep_addr.index()).modify(|w| {\n                        w.set_usbaep(enabled);\n                        w.set_cnak(enabled); // clear NAK that might've been set by SNAK above.\n                    });\n\n                    // Flush tx fifo\n                    regs.grstctl().write(|w| {\n                        w.set_txfflsh(true);\n                        w.set_txfnum(ep_addr.index() as _);\n                    });\n                    loop {\n                        let x = regs.grstctl().read();\n                        if !x.txfflsh() {\n                            break;\n                        }\n                    }\n                });\n\n                // Wake `Endpoint::wait_enabled()`\n                state.ep_states[ep_addr.index()].in_waker.wake();\n            }\n        }\n    }\n\n    async fn enable(&mut self) {\n        trace!(\"enable\");\n        // TODO: enable the peripheral once enable/disable semantics are cleared up in embassy-usb\n    }\n\n    async fn disable(&mut self) {\n        trace!(\"disable\");\n\n        // TODO: disable the peripheral once enable/disable semantics are cleared up in embassy-usb\n        //Bus::disable(self);\n    }\n\n    async fn remote_wakeup(&mut self) -> Result<(), Unsupported> {\n        Err(Unsupported)\n    }\n}\n\n/// USB endpoint direction.\ntrait Dir {\n    /// Returns the direction value.\n    fn dir() -> Direction;\n}\n\n/// Marker type for the \"IN\" direction.\npub enum In {}\nimpl Dir for In {\n    fn dir() -> Direction {\n        Direction::In\n    }\n}\n\n/// Marker type for the \"OUT\" direction.\npub enum Out {}\nimpl Dir for Out {\n    fn dir() -> Direction {\n        Direction::Out\n    }\n}\n\n/// USB endpoint.\npub struct Endpoint<'d, D> {\n    _phantom: PhantomData<D>,\n    regs: Otg,\n    info: EndpointInfo,\n    state: &'d EpState,\n}\n\nimpl<'d> embassy_usb_driver::Endpoint for Endpoint<'d, In> {\n    fn info(&self) -> &EndpointInfo {\n        &self.info\n    }\n\n    async fn wait_enabled(&mut self) {\n        poll_fn(|cx| {\n            let ep_index = self.info.addr.index();\n\n            self.state.in_waker.register(cx.waker());\n\n            if self.regs.diepctl(ep_index).read().usbaep() {\n                Poll::Ready(())\n            } else {\n                Poll::Pending\n            }\n        })\n        .await\n    }\n}\n\nimpl<'d> embassy_usb_driver::Endpoint for Endpoint<'d, Out> {\n    fn info(&self) -> &EndpointInfo {\n        &self.info\n    }\n\n    async fn wait_enabled(&mut self) {\n        poll_fn(|cx| {\n            let ep_index = self.info.addr.index();\n\n            self.state.out_waker.register(cx.waker());\n\n            if self.regs.doepctl(ep_index).read().usbaep() {\n                Poll::Ready(())\n            } else {\n                Poll::Pending\n            }\n        })\n        .await\n    }\n}\n\nimpl<'d> embassy_usb_driver::EndpointOut for Endpoint<'d, Out> {\n    async fn read(&mut self, buf: &mut [u8]) -> Result<usize, EndpointError> {\n        trace!(\"read start len={}\", buf.len());\n\n        poll_fn(|cx| {\n            let index = self.info.addr.index();\n            self.state.out_waker.register(cx.waker());\n\n            let doepctl = self.regs.doepctl(index).read();\n            trace!(\"read ep={:?}: doepctl {:08x}\", self.info.addr, doepctl.0,);\n            if !doepctl.usbaep() {\n                trace!(\"read ep={:?} error disabled\", self.info.addr);\n                return Poll::Ready(Err(EndpointError::Disabled));\n            }\n\n            let len = self.state.out_size.load(Ordering::Relaxed);\n            if len != EP_OUT_BUFFER_EMPTY {\n                trace!(\"read ep={:?} done len={}\", self.info.addr, len);\n\n                if len as usize > buf.len() {\n                    return Poll::Ready(Err(EndpointError::BufferOverflow));\n                }\n\n                // SAFETY: exclusive access ensured by `out_size` atomic variable\n                let data = unsafe { core::slice::from_raw_parts(*self.state.out_buffer.get(), len as usize) };\n                buf[..len as usize].copy_from_slice(data);\n\n                // Release buffer\n                self.state.out_size.store(EP_OUT_BUFFER_EMPTY, Ordering::Release);\n\n                critical_section::with(|_| {\n                    // Receive 1 packet\n                    self.regs.doeptsiz(index).modify(|w| {\n                        w.set_xfrsiz(self.info.max_packet_size as _);\n                        w.set_pktcnt(1);\n                    });\n\n                    if self.info.ep_type == EndpointType::Isochronous {\n                        // Isochronous endpoints must set the correct even/odd frame bit to\n                        // correspond with the next frame's number.\n                        let frame_number = self.regs.dsts().read().fnsof();\n                        let frame_is_odd = frame_number & 0x01 == 1;\n\n                        self.regs.doepctl(index).modify(|r| {\n                            if frame_is_odd {\n                                r.set_sd0pid_sevnfrm(true);\n                            } else {\n                                r.set_sd1pid_soddfrm(true);\n                            }\n                        });\n                    }\n\n                    // Clear NAK to indicate we are ready to receive more data\n                    self.regs.doepctl(index).modify(|w| {\n                        w.set_cnak(true);\n                    });\n                });\n\n                Poll::Ready(Ok(len as usize))\n            } else {\n                Poll::Pending\n            }\n        })\n        .await\n    }\n}\n\nimpl<'d> embassy_usb_driver::EndpointIn for Endpoint<'d, In> {\n    async fn write(&mut self, buf: &[u8]) -> Result<(), EndpointError> {\n        trace!(\"write ep={:?} data={:?}\", self.info.addr, Bytes(buf));\n\n        if buf.len() > self.info.max_packet_size as usize {\n            return Err(EndpointError::BufferOverflow);\n        }\n\n        let index = self.info.addr.index();\n        // Wait for previous transfer to complete and check if endpoint is disabled\n        poll_fn(|cx| {\n            self.state.in_waker.register(cx.waker());\n\n            let diepctl = self.regs.diepctl(index).read();\n            let dtxfsts = self.regs.dtxfsts(index).read();\n            trace!(\n                \"write ep={:?}: diepctl {:08x} ftxfsts {:08x}\",\n                self.info.addr, diepctl.0, dtxfsts.0\n            );\n            if !diepctl.usbaep() {\n                trace!(\"write ep={:?} wait for prev: error disabled\", self.info.addr);\n                Poll::Ready(Err(EndpointError::Disabled))\n            } else if !diepctl.epena() {\n                trace!(\"write ep={:?} wait for prev: ready\", self.info.addr);\n                Poll::Ready(Ok(()))\n            } else {\n                trace!(\"write ep={:?} wait for prev: pending\", self.info.addr);\n                Poll::Pending\n            }\n        })\n        .await?;\n\n        if buf.len() > 0 {\n            poll_fn(|cx| {\n                self.state.in_waker.register(cx.waker());\n\n                let size_words = (buf.len() + 3) / 4;\n\n                let fifo_space = self.regs.dtxfsts(index).read().ineptfsav() as usize;\n                if size_words > fifo_space {\n                    // Not enough space in fifo, enable tx fifo empty interrupt\n                    critical_section::with(|_| {\n                        self.regs.diepempmsk().modify(|w| {\n                            w.set_ineptxfem(w.ineptxfem() | (1 << index));\n                        });\n                    });\n\n                    trace!(\"tx fifo for ep={} full, waiting for txfe\", index);\n\n                    Poll::Pending\n                } else {\n                    trace!(\"write ep={:?} wait for fifo: ready\", self.info.addr);\n                    Poll::Ready(())\n                }\n            })\n            .await\n        }\n\n        // ERRATA: Transmit data FIFO is corrupted when a write sequence to the FIFO is interrupted with\n        // accesses to certain OTG_FS registers.\n        //\n        // Prevent the interrupt (which might poke FIFOs) from executing while copying data to FIFOs.\n        critical_section::with(|_| {\n            // Setup transfer size\n            self.regs.dieptsiz(index).write(|w| {\n                w.set_mcnt(1);\n                w.set_pktcnt(1);\n                w.set_xfrsiz(buf.len() as _);\n            });\n\n            if self.info.ep_type == EndpointType::Isochronous {\n                // Isochronous endpoints must set the correct even/odd frame bit to\n                // correspond with the next frame's number.\n                let frame_number = self.regs.dsts().read().fnsof();\n                let frame_is_odd = frame_number & 0x01 == 1;\n\n                self.regs.diepctl(index).modify(|r| {\n                    if frame_is_odd {\n                        r.set_sd0pid_sevnfrm(true);\n                    } else {\n                        r.set_sd1pid_soddfrm(true);\n                    }\n                });\n            }\n\n            // Enable endpoint\n            self.regs.diepctl(index).modify(|w| {\n                w.set_cnak(true);\n                w.set_epena(true);\n            });\n\n            // Write data to FIFO\n            let fifo = self.regs.fifo(index);\n            let mut chunks = buf.chunks_exact(4);\n            for chunk in &mut chunks {\n                let val = u32::from_ne_bytes(chunk.try_into().unwrap());\n                fifo.write_value(regs::Fifo(val));\n            }\n            // Write any last chunk\n            let rem = chunks.remainder();\n            if !rem.is_empty() {\n                let mut tmp = [0u8; 4];\n                tmp[0..rem.len()].copy_from_slice(rem);\n                let tmp = u32::from_ne_bytes(tmp);\n                fifo.write_value(regs::Fifo(tmp));\n            }\n        });\n\n        trace!(\"write done ep={:?}\", self.info.addr);\n\n        Ok(())\n    }\n}\n\n/// USB control pipe.\npub struct ControlPipe<'d> {\n    max_packet_size: u16,\n    regs: Otg,\n    setup_state: &'d ControlPipeSetupState,\n    ep_in: Endpoint<'d, In>,\n    ep_out: Endpoint<'d, Out>,\n}\n\nimpl<'d> embassy_usb_driver::ControlPipe for ControlPipe<'d> {\n    fn max_packet_size(&self) -> usize {\n        usize::from(self.max_packet_size)\n    }\n\n    async fn setup(&mut self) -> [u8; 8] {\n        poll_fn(|cx| {\n            self.ep_out.state.out_waker.register(cx.waker());\n\n            if self.setup_state.setup_ready.load(Ordering::Relaxed) {\n                let mut data = [0; 8];\n                data[0..4].copy_from_slice(&self.setup_state.setup_data[0].load(Ordering::Relaxed).to_ne_bytes());\n                data[4..8].copy_from_slice(&self.setup_state.setup_data[1].load(Ordering::Relaxed).to_ne_bytes());\n                self.setup_state.setup_ready.store(false, Ordering::Release);\n\n                // EP0 should not be controlled by `Bus` so this RMW does not need a critical section\n                self.regs.doeptsiz(self.ep_out.info.addr.index()).modify(|w| {\n                    w.set_rxdpid_stupcnt(3);\n                });\n\n                // Clear NAK to indicate we are ready to receive more data\n                self.regs\n                    .doepctl(self.ep_out.info.addr.index())\n                    .modify(|w| w.set_cnak(true));\n\n                trace!(\"SETUP received: {:?}\", Bytes(&data));\n                Poll::Ready(data)\n            } else {\n                trace!(\"SETUP waiting\");\n                Poll::Pending\n            }\n        })\n        .await\n    }\n\n    async fn data_out(&mut self, buf: &mut [u8], _first: bool, _last: bool) -> Result<usize, EndpointError> {\n        trace!(\"control: data_out\");\n        let len = self.ep_out.read(buf).await?;\n        trace!(\"control: data_out read: {:?}\", Bytes(&buf[..len]));\n        Ok(len)\n    }\n\n    async fn data_in(&mut self, data: &[u8], _first: bool, last: bool) -> Result<(), EndpointError> {\n        trace!(\"control: data_in write: {:?}\", Bytes(data));\n        self.ep_in.write(data).await?;\n\n        // wait for status response from host after sending the last packet\n        if last {\n            trace!(\"control: data_in waiting for status\");\n            self.ep_out.read(&mut []).await?;\n            trace!(\"control: complete\");\n        }\n\n        Ok(())\n    }\n\n    async fn accept(&mut self) {\n        trace!(\"control: accept\");\n\n        self.ep_in.write(&[]).await.ok();\n\n        trace!(\"control: accept OK\");\n    }\n\n    async fn reject(&mut self) {\n        trace!(\"control: reject\");\n\n        // EP0 should not be controlled by `Bus` so this RMW does not need a critical section\n        self.regs.diepctl(self.ep_in.info.addr.index()).modify(|w| {\n            w.set_stall(true);\n        });\n        self.regs.doepctl(self.ep_out.info.addr.index()).modify(|w| {\n            w.set_stall(true);\n        });\n    }\n\n    async fn accept_set_address(&mut self, addr: u8) {\n        trace!(\"setting addr: {}\", addr);\n        critical_section::with(|_| {\n            self.regs.dcfg().modify(|w| {\n                w.set_dad(addr);\n            });\n        });\n\n        // synopsys driver requires accept to be sent after changing address\n        self.accept().await\n    }\n}\n\n/// Translates HAL [EndpointType] into PAC [vals::Eptyp]\nfn to_eptyp(ep_type: EndpointType) -> vals::Eptyp {\n    match ep_type {\n        EndpointType::Control => vals::Eptyp::CONTROL,\n        EndpointType::Isochronous => vals::Eptyp::ISOCHRONOUS,\n        EndpointType::Bulk => vals::Eptyp::BULK,\n        EndpointType::Interrupt => vals::Eptyp::INTERRUPT,\n    }\n}\n\n/// Calculates total allocated FIFO size in words\nfn ep_fifo_size(eps: &[Option<EndpointData>]) -> u16 {\n    eps.iter().map(|ep| ep.map(|ep| ep.fifo_size_words).unwrap_or(0)).sum()\n}\n\n/// Generates IRQ mask for enabled endpoints\nfn ep_irq_mask(eps: &[Option<EndpointData>]) -> u16 {\n    eps.iter().enumerate().fold(\n        0,\n        |mask, (index, ep)| {\n            if ep.is_some() { mask | (1 << index) } else { mask }\n        },\n    )\n}\n\n/// Calculates MPSIZ value for EP0, which uses special values.\nfn ep0_mpsiz(max_packet_size: u16) -> u16 {\n    match max_packet_size {\n        8 => 0b11,\n        16 => 0b10,\n        32 => 0b01,\n        64 => 0b00,\n        other => panic!(\"Unsupported EP0 size: {}\", other),\n    }\n}\n\n/// Hardware-dependent USB IP configuration.\npub struct OtgInstance<'d, const MAX_EP_COUNT: usize> {\n    /// The USB peripheral.\n    pub regs: Otg,\n    /// The USB state.\n    pub state: &'d State<MAX_EP_COUNT>,\n    /// FIFO depth in words.\n    pub fifo_depth_words: u16,\n    /// Number of used endpoints.\n    pub endpoint_count: usize,\n    /// The PHY type.\n    pub phy_type: PhyType,\n    /// Extra RX FIFO words needed by some implementations.\n    pub extra_rx_fifo_words: u16,\n    /// Function to calculate TRDT value based on some internal clock speed.\n    pub calculate_trdt_fn: fn(speed: vals::Dspd) -> u8,\n}\n"
  },
  {
    "path": "embassy-usb-synopsys-otg/src/otg_v1.rs",
    "content": "//! Register definitions for Synopsys DesignWare USB OTG core\n\n#![allow(missing_docs)]\n\nuse core::marker::PhantomData;\n\n#[derive(Copy, Clone, PartialEq, Eq)]\npub struct RW;\n#[derive(Copy, Clone, PartialEq, Eq)]\npub struct R;\n#[derive(Copy, Clone, PartialEq, Eq)]\npub struct W;\n\nmod sealed {\n    use super::*;\n    pub trait Access {}\n    impl Access for R {}\n    impl Access for W {}\n    impl Access for RW {}\n}\n\npub trait Access: sealed::Access + Copy {}\nimpl Access for R {}\nimpl Access for W {}\nimpl Access for RW {}\n\npub trait Read: Access {}\nimpl Read for RW {}\nimpl Read for R {}\n\npub trait Write: Access {}\nimpl Write for RW {}\nimpl Write for W {}\n\n#[derive(Copy, Clone, PartialEq, Eq)]\npub struct Reg<T: Copy, A: Access> {\n    ptr: *mut u8,\n    phantom: PhantomData<*mut (T, A)>,\n}\nunsafe impl<T: Copy, A: Access> Send for Reg<T, A> {}\nunsafe impl<T: Copy, A: Access> Sync for Reg<T, A> {}\n\nimpl<T: Copy, A: Access> Reg<T, A> {\n    #[allow(clippy::missing_safety_doc)]\n    #[inline(always)]\n    pub const unsafe fn from_ptr(ptr: *mut T) -> Self {\n        Self {\n            ptr: ptr as _,\n            phantom: PhantomData,\n        }\n    }\n\n    #[inline(always)]\n    pub const fn as_ptr(&self) -> *mut T {\n        self.ptr as _\n    }\n}\n\nimpl<T: Copy, A: Read> Reg<T, A> {\n    #[inline(always)]\n    pub fn read(&self) -> T {\n        unsafe { (self.ptr as *mut T).read_volatile() }\n    }\n}\n\nimpl<T: Copy, A: Write> Reg<T, A> {\n    #[inline(always)]\n    pub fn write_value(&self, val: T) {\n        unsafe { (self.ptr as *mut T).write_volatile(val) }\n    }\n}\n\nimpl<T: Default + Copy, A: Write> Reg<T, A> {\n    #[inline(always)]\n    pub fn write<R>(&self, f: impl FnOnce(&mut T) -> R) -> R {\n        let mut val = Default::default();\n        let res = f(&mut val);\n        self.write_value(val);\n        res\n    }\n}\n\nimpl<T: Copy, A: Read + Write> Reg<T, A> {\n    #[inline(always)]\n    pub fn modify<R>(&self, f: impl FnOnce(&mut T) -> R) -> R {\n        let mut val = self.read();\n        let res = f(&mut val);\n        self.write_value(val);\n        res\n    }\n}\n\n#[doc = \"USB on the go\"]\n#[derive(Copy, Clone, Eq, PartialEq)]\npub struct Otg {\n    ptr: *mut u8,\n}\nunsafe impl Send for Otg {}\nunsafe impl Sync for Otg {}\nimpl Otg {\n    #[inline(always)]\n    pub const unsafe fn from_ptr(ptr: *mut ()) -> Self {\n        Self { ptr: ptr as _ }\n    }\n    #[inline(always)]\n    pub const fn as_ptr(&self) -> *mut () {\n        self.ptr as _\n    }\n    #[doc = \"Control and status register\"]\n    #[inline(always)]\n    pub fn gotgctl(self) -> Reg<regs::Gotgctl, RW> {\n        unsafe { Reg::from_ptr(self.ptr.add(0x0usize) as _) }\n    }\n    #[doc = \"Interrupt register\"]\n    #[inline(always)]\n    pub fn gotgint(self) -> Reg<regs::Gotgint, RW> {\n        unsafe { Reg::from_ptr(self.ptr.add(0x04usize) as _) }\n    }\n    #[doc = \"AHB configuration register\"]\n    #[inline(always)]\n    pub fn gahbcfg(self) -> Reg<regs::Gahbcfg, RW> {\n        unsafe { Reg::from_ptr(self.ptr.add(0x08usize) as _) }\n    }\n    #[doc = \"USB configuration register\"]\n    #[inline(always)]\n    pub fn gusbcfg(self) -> Reg<regs::Gusbcfg, RW> {\n        unsafe { Reg::from_ptr(self.ptr.add(0x0cusize) as _) }\n    }\n    #[doc = \"Reset register\"]\n    #[inline(always)]\n    pub fn grstctl(self) -> Reg<regs::Grstctl, RW> {\n        unsafe { Reg::from_ptr(self.ptr.add(0x10usize) as _) }\n    }\n    #[doc = \"Core interrupt register\"]\n    #[inline(always)]\n    pub fn gintsts(self) -> Reg<regs::Gintsts, RW> {\n        unsafe { Reg::from_ptr(self.ptr.add(0x14usize) as _) }\n    }\n    #[doc = \"Interrupt mask register\"]\n    #[inline(always)]\n    pub fn gintmsk(self) -> Reg<regs::Gintmsk, RW> {\n        unsafe { Reg::from_ptr(self.ptr.add(0x18usize) as _) }\n    }\n    #[doc = \"Receive status debug read register\"]\n    #[inline(always)]\n    pub fn grxstsr(self) -> Reg<regs::Grxsts, R> {\n        unsafe { Reg::from_ptr(self.ptr.add(0x1cusize) as _) }\n    }\n    #[doc = \"Status read and pop register\"]\n    #[inline(always)]\n    pub fn grxstsp(self) -> Reg<regs::Grxsts, R> {\n        unsafe { Reg::from_ptr(self.ptr.add(0x20usize) as _) }\n    }\n    #[doc = \"Receive FIFO size register\"]\n    #[inline(always)]\n    pub fn grxfsiz(self) -> Reg<regs::Grxfsiz, RW> {\n        unsafe { Reg::from_ptr(self.ptr.add(0x24usize) as _) }\n    }\n    #[doc = \"Endpoint 0 transmit FIFO size register (device mode)\"]\n    #[inline(always)]\n    pub fn dieptxf0(self) -> Reg<regs::Fsiz, RW> {\n        unsafe { Reg::from_ptr(self.ptr.add(0x28usize) as _) }\n    }\n    #[doc = \"Non-periodic transmit FIFO size register (host mode)\"]\n    #[inline(always)]\n    pub fn hnptxfsiz(self) -> Reg<regs::Fsiz, RW> {\n        unsafe { Reg::from_ptr(self.ptr.add(0x28usize) as _) }\n    }\n    #[doc = \"Non-periodic transmit FIFO/queue status register (host mode)\"]\n    #[inline(always)]\n    pub fn hnptxsts(self) -> Reg<regs::Hnptxsts, R> {\n        unsafe { Reg::from_ptr(self.ptr.add(0x2cusize) as _) }\n    }\n    #[doc = \"OTG I2C access register\"]\n    #[inline(always)]\n    pub fn gi2cctl(self) -> Reg<regs::Gi2cctl, RW> {\n        unsafe { Reg::from_ptr(self.ptr.add(0x30usize) as _) }\n    }\n    #[doc = \"General core configuration register, for core_id 0x0000_1xxx\"]\n    #[inline(always)]\n    pub fn gccfg_v1(self) -> Reg<regs::GccfgV1, RW> {\n        unsafe { Reg::from_ptr(self.ptr.add(0x38usize) as _) }\n    }\n    #[doc = \"General core configuration register, for core_id 0x0000_\\\\[23\\\\]xxx\"]\n    #[inline(always)]\n    pub fn gccfg_v2(self) -> Reg<regs::GccfgV2, RW> {\n        unsafe { Reg::from_ptr(self.ptr.add(0x38usize) as _) }\n    }\n    #[doc = \"General core configuration register, for core_id 0x0000_5xxx\"]\n    #[inline(always)]\n    pub fn gccfg_v3(self) -> Reg<regs::GccfgV3, RW> {\n        unsafe { Reg::from_ptr(self.ptr.add(0x38usize) as _) }\n    }\n    #[doc = \"Core ID register\"]\n    #[inline(always)]\n    pub fn cid(self) -> Reg<regs::Cid, RW> {\n        unsafe { Reg::from_ptr(self.ptr.add(0x3cusize) as _) }\n    }\n    #[doc = \"OTG core LPM configuration register\"]\n    #[inline(always)]\n    pub fn glpmcfg(self) -> Reg<regs::Glpmcfg, RW> {\n        unsafe { Reg::from_ptr(self.ptr.add(0x54usize) as _) }\n    }\n    #[doc = \"Host periodic transmit FIFO size register\"]\n    #[inline(always)]\n    pub fn hptxfsiz(self) -> Reg<regs::Fsiz, RW> {\n        unsafe { Reg::from_ptr(self.ptr.add(0x0100usize) as _) }\n    }\n    #[doc = \"Device IN endpoint transmit FIFO size register\"]\n    #[inline(always)]\n    pub fn dieptxf(self, n: usize) -> Reg<regs::Fsiz, RW> {\n        assert!(n < 7usize);\n        unsafe { Reg::from_ptr(self.ptr.add(0x0104usize + n * 4usize) as _) }\n    }\n    #[doc = \"Host configuration register\"]\n    #[inline(always)]\n    pub fn hcfg(self) -> Reg<regs::Hcfg, RW> {\n        unsafe { Reg::from_ptr(self.ptr.add(0x0400usize) as _) }\n    }\n    #[doc = \"Host frame interval register\"]\n    #[inline(always)]\n    pub fn hfir(self) -> Reg<regs::Hfir, RW> {\n        unsafe { Reg::from_ptr(self.ptr.add(0x0404usize) as _) }\n    }\n    #[doc = \"Host frame number/frame time remaining register\"]\n    #[inline(always)]\n    pub fn hfnum(self) -> Reg<regs::Hfnum, R> {\n        unsafe { Reg::from_ptr(self.ptr.add(0x0408usize) as _) }\n    }\n    #[doc = \"Periodic transmit FIFO/queue status register\"]\n    #[inline(always)]\n    pub fn hptxsts(self) -> Reg<regs::Hptxsts, RW> {\n        unsafe { Reg::from_ptr(self.ptr.add(0x0410usize) as _) }\n    }\n    #[doc = \"Host all channels interrupt register\"]\n    #[inline(always)]\n    pub fn haint(self) -> Reg<regs::Haint, R> {\n        unsafe { Reg::from_ptr(self.ptr.add(0x0414usize) as _) }\n    }\n    #[doc = \"Host all channels interrupt mask register\"]\n    #[inline(always)]\n    pub fn haintmsk(self) -> Reg<regs::Haintmsk, RW> {\n        unsafe { Reg::from_ptr(self.ptr.add(0x0418usize) as _) }\n    }\n    #[doc = \"Host port control and status register\"]\n    #[inline(always)]\n    pub fn hprt(self) -> Reg<regs::Hprt, RW> {\n        unsafe { Reg::from_ptr(self.ptr.add(0x0440usize) as _) }\n    }\n    #[doc = \"Host channel characteristics register\"]\n    #[inline(always)]\n    pub fn hcchar(self, n: usize) -> Reg<regs::Hcchar, RW> {\n        assert!(n < 12usize);\n        unsafe { Reg::from_ptr(self.ptr.add(0x0500usize + n * 32usize) as _) }\n    }\n    #[doc = \"Host channel split control register\"]\n    #[inline(always)]\n    pub fn hcsplt(self, n: usize) -> Reg<u32, RW> {\n        assert!(n < 12usize);\n        unsafe { Reg::from_ptr(self.ptr.add(0x0504usize + n * 32usize) as _) }\n    }\n    #[doc = \"Host channel interrupt register\"]\n    #[inline(always)]\n    pub fn hcint(self, n: usize) -> Reg<regs::Hcint, RW> {\n        assert!(n < 12usize);\n        unsafe { Reg::from_ptr(self.ptr.add(0x0508usize + n * 32usize) as _) }\n    }\n    #[doc = \"Host channel mask register\"]\n    #[inline(always)]\n    pub fn hcintmsk(self, n: usize) -> Reg<regs::Hcintmsk, RW> {\n        assert!(n < 12usize);\n        unsafe { Reg::from_ptr(self.ptr.add(0x050cusize + n * 32usize) as _) }\n    }\n    #[doc = \"Host channel transfer size register\"]\n    #[inline(always)]\n    pub fn hctsiz(self, n: usize) -> Reg<regs::Hctsiz, RW> {\n        assert!(n < 12usize);\n        unsafe { Reg::from_ptr(self.ptr.add(0x0510usize + n * 32usize) as _) }\n    }\n    #[doc = \"Host channel DMA address register\"]\n    #[inline(always)]\n    pub fn hcdma(self, n: usize) -> Reg<u32, RW> {\n        assert!(n < 12usize);\n        unsafe { Reg::from_ptr(self.ptr.add(0x0514usize + n * 32usize) as _) }\n    }\n    #[doc = \"Device configuration register\"]\n    #[inline(always)]\n    pub fn dcfg(self) -> Reg<regs::Dcfg, RW> {\n        unsafe { Reg::from_ptr(self.ptr.add(0x0800usize) as _) }\n    }\n    #[doc = \"Device control register\"]\n    #[inline(always)]\n    pub fn dctl(self) -> Reg<regs::Dctl, RW> {\n        unsafe { Reg::from_ptr(self.ptr.add(0x0804usize) as _) }\n    }\n    #[doc = \"Device status register\"]\n    #[inline(always)]\n    pub fn dsts(self) -> Reg<regs::Dsts, R> {\n        unsafe { Reg::from_ptr(self.ptr.add(0x0808usize) as _) }\n    }\n    #[doc = \"Device IN endpoint common interrupt mask register\"]\n    #[inline(always)]\n    pub fn diepmsk(self) -> Reg<regs::Diepmsk, RW> {\n        unsafe { Reg::from_ptr(self.ptr.add(0x0810usize) as _) }\n    }\n    #[doc = \"Device OUT endpoint common interrupt mask register\"]\n    #[inline(always)]\n    pub fn doepmsk(self) -> Reg<regs::Doepmsk, RW> {\n        unsafe { Reg::from_ptr(self.ptr.add(0x0814usize) as _) }\n    }\n    #[doc = \"Device all endpoints interrupt register\"]\n    #[inline(always)]\n    pub fn daint(self) -> Reg<regs::Daint, R> {\n        unsafe { Reg::from_ptr(self.ptr.add(0x0818usize) as _) }\n    }\n    #[doc = \"All endpoints interrupt mask register\"]\n    #[inline(always)]\n    pub fn daintmsk(self) -> Reg<regs::Daintmsk, RW> {\n        unsafe { Reg::from_ptr(self.ptr.add(0x081cusize) as _) }\n    }\n    #[doc = \"Device VBUS discharge time register\"]\n    #[inline(always)]\n    pub fn dvbusdis(self) -> Reg<regs::Dvbusdis, RW> {\n        unsafe { Reg::from_ptr(self.ptr.add(0x0828usize) as _) }\n    }\n    #[doc = \"Device VBUS pulsing time register\"]\n    #[inline(always)]\n    pub fn dvbuspulse(self) -> Reg<regs::Dvbuspulse, RW> {\n        unsafe { Reg::from_ptr(self.ptr.add(0x082cusize) as _) }\n    }\n    #[doc = \"Device IN endpoint FIFO empty interrupt mask register\"]\n    #[inline(always)]\n    pub fn diepempmsk(self) -> Reg<regs::Diepempmsk, RW> {\n        unsafe { Reg::from_ptr(self.ptr.add(0x0834usize) as _) }\n    }\n    #[doc = \"Device IN endpoint control register\"]\n    #[inline(always)]\n    pub fn diepctl(self, n: usize) -> Reg<regs::Diepctl, RW> {\n        assert!(n < 16usize);\n        unsafe { Reg::from_ptr(self.ptr.add(0x0900usize + n * 32usize) as _) }\n    }\n    #[doc = \"Device IN endpoint interrupt register\"]\n    #[inline(always)]\n    pub fn diepint(self, n: usize) -> Reg<regs::Diepint, RW> {\n        assert!(n < 16usize);\n        unsafe { Reg::from_ptr(self.ptr.add(0x0908usize + n * 32usize) as _) }\n    }\n    #[doc = \"Device IN endpoint transfer size register\"]\n    #[inline(always)]\n    pub fn dieptsiz(self, n: usize) -> Reg<regs::Dieptsiz, RW> {\n        assert!(n < 16usize);\n        unsafe { Reg::from_ptr(self.ptr.add(0x0910usize + n * 32usize) as _) }\n    }\n    #[doc = \"Device IN endpoint transmit FIFO status register\"]\n    #[inline(always)]\n    pub fn dtxfsts(self, n: usize) -> Reg<regs::Dtxfsts, R> {\n        assert!(n < 16usize);\n        unsafe { Reg::from_ptr(self.ptr.add(0x0918usize + n * 32usize) as _) }\n    }\n    #[doc = \"Device OUT endpoint control register\"]\n    #[inline(always)]\n    pub fn doepctl(self, n: usize) -> Reg<regs::Doepctl, RW> {\n        assert!(n < 16usize);\n        unsafe { Reg::from_ptr(self.ptr.add(0x0b00usize + n * 32usize) as _) }\n    }\n    #[doc = \"Device OUT endpoint interrupt register\"]\n    #[inline(always)]\n    pub fn doepint(self, n: usize) -> Reg<regs::Doepint, RW> {\n        assert!(n < 16usize);\n        unsafe { Reg::from_ptr(self.ptr.add(0x0b08usize + n * 32usize) as _) }\n    }\n    #[doc = \"Device OUT endpoint transfer size register\"]\n    #[inline(always)]\n    pub fn doeptsiz(self, n: usize) -> Reg<regs::Doeptsiz, RW> {\n        assert!(n < 16usize);\n        unsafe { Reg::from_ptr(self.ptr.add(0x0b10usize + n * 32usize) as _) }\n    }\n    #[doc = \"Device OUT/IN endpoint DMA address register\"]\n    #[inline(always)]\n    pub fn doepdma(self, n: usize) -> Reg<u32, RW> {\n        assert!(n < 16usize);\n        unsafe { Reg::from_ptr(self.ptr.add(0x0b14usize + n * 32usize) as _) }\n    }\n    #[doc = \"Power and clock gating control register\"]\n    #[inline(always)]\n    pub fn pcgcctl(self) -> Reg<regs::Pcgcctl, RW> {\n        unsafe { Reg::from_ptr(self.ptr.add(0x0e00usize) as _) }\n    }\n    #[doc = \"Device endpoint / host channel FIFO register\"]\n    #[inline(always)]\n    pub fn fifo(self, n: usize) -> Reg<regs::Fifo, RW> {\n        assert!(n < 16usize);\n        unsafe { Reg::from_ptr(self.ptr.add(0x1000usize + n * 4096usize) as _) }\n    }\n}\npub mod regs {\n    #[doc = \"Core ID register\"]\n    #[repr(transparent)]\n    #[derive(Copy, Clone, Eq, PartialEq)]\n    pub struct Cid(pub u32);\n    impl Cid {\n        #[doc = \"Product ID field\"]\n        #[inline(always)]\n        pub const fn product_id(&self) -> u32 {\n            let val = (self.0 >> 0usize) & 0xffff_ffff;\n            val as u32\n        }\n        #[doc = \"Product ID field\"]\n        #[inline(always)]\n        pub fn set_product_id(&mut self, val: u32) {\n            self.0 = (self.0 & !(0xffff_ffff << 0usize)) | (((val as u32) & 0xffff_ffff) << 0usize);\n        }\n    }\n    impl Default for Cid {\n        #[inline(always)]\n        fn default() -> Cid {\n            Cid(0)\n        }\n    }\n    #[doc = \"Device all endpoints interrupt register\"]\n    #[repr(transparent)]\n    #[derive(Copy, Clone, Eq, PartialEq)]\n    pub struct Daint(pub u32);\n    impl Daint {\n        #[doc = \"IN endpoint interrupt bits\"]\n        #[inline(always)]\n        pub const fn iepint(&self) -> u16 {\n            let val = (self.0 >> 0usize) & 0xffff;\n            val as u16\n        }\n        #[doc = \"IN endpoint interrupt bits\"]\n        #[inline(always)]\n        pub fn set_iepint(&mut self, val: u16) {\n            self.0 = (self.0 & !(0xffff << 0usize)) | (((val as u32) & 0xffff) << 0usize);\n        }\n        #[doc = \"OUT endpoint interrupt bits\"]\n        #[inline(always)]\n        pub const fn oepint(&self) -> u16 {\n            let val = (self.0 >> 16usize) & 0xffff;\n            val as u16\n        }\n        #[doc = \"OUT endpoint interrupt bits\"]\n        #[inline(always)]\n        pub fn set_oepint(&mut self, val: u16) {\n            self.0 = (self.0 & !(0xffff << 16usize)) | (((val as u32) & 0xffff) << 16usize);\n        }\n    }\n    impl Default for Daint {\n        #[inline(always)]\n        fn default() -> Daint {\n            Daint(0)\n        }\n    }\n    #[doc = \"All endpoints interrupt mask register\"]\n    #[repr(transparent)]\n    #[derive(Copy, Clone, Eq, PartialEq)]\n    pub struct Daintmsk(pub u32);\n    impl Daintmsk {\n        #[doc = \"IN EP interrupt mask bits\"]\n        #[inline(always)]\n        pub const fn iepm(&self) -> u16 {\n            let val = (self.0 >> 0usize) & 0xffff;\n            val as u16\n        }\n        #[doc = \"IN EP interrupt mask bits\"]\n        #[inline(always)]\n        pub fn set_iepm(&mut self, val: u16) {\n            self.0 = (self.0 & !(0xffff << 0usize)) | (((val as u32) & 0xffff) << 0usize);\n        }\n        #[doc = \"OUT EP interrupt mask bits\"]\n        #[inline(always)]\n        pub const fn oepm(&self) -> u16 {\n            let val = (self.0 >> 16usize) & 0xffff;\n            val as u16\n        }\n        #[doc = \"OUT EP interrupt mask bits\"]\n        #[inline(always)]\n        pub fn set_oepm(&mut self, val: u16) {\n            self.0 = (self.0 & !(0xffff << 16usize)) | (((val as u32) & 0xffff) << 16usize);\n        }\n    }\n    impl Default for Daintmsk {\n        #[inline(always)]\n        fn default() -> Daintmsk {\n            Daintmsk(0)\n        }\n    }\n    #[doc = \"Device configuration register\"]\n    #[repr(transparent)]\n    #[derive(Copy, Clone, Eq, PartialEq)]\n    pub struct Dcfg(pub u32);\n    impl Dcfg {\n        #[doc = \"Device speed\"]\n        #[inline(always)]\n        pub const fn dspd(&self) -> super::vals::Dspd {\n            let val = (self.0 >> 0usize) & 0x03;\n            super::vals::Dspd::from_bits(val as u8)\n        }\n        #[doc = \"Device speed\"]\n        #[inline(always)]\n        pub fn set_dspd(&mut self, val: super::vals::Dspd) {\n            self.0 = (self.0 & !(0x03 << 0usize)) | (((val.to_bits() as u32) & 0x03) << 0usize);\n        }\n        #[doc = \"Non-zero-length status OUT handshake\"]\n        #[inline(always)]\n        pub const fn nzlsohsk(&self) -> bool {\n            let val = (self.0 >> 2usize) & 0x01;\n            val != 0\n        }\n        #[doc = \"Non-zero-length status OUT handshake\"]\n        #[inline(always)]\n        pub fn set_nzlsohsk(&mut self, val: bool) {\n            self.0 = (self.0 & !(0x01 << 2usize)) | (((val as u32) & 0x01) << 2usize);\n        }\n        #[doc = \"Device address\"]\n        #[inline(always)]\n        pub const fn dad(&self) -> u8 {\n            let val = (self.0 >> 4usize) & 0x7f;\n            val as u8\n        }\n        #[doc = \"Device address\"]\n        #[inline(always)]\n        pub fn set_dad(&mut self, val: u8) {\n            self.0 = (self.0 & !(0x7f << 4usize)) | (((val as u32) & 0x7f) << 4usize);\n        }\n        #[doc = \"Periodic frame interval\"]\n        #[inline(always)]\n        pub const fn pfivl(&self) -> super::vals::Pfivl {\n            let val = (self.0 >> 11usize) & 0x03;\n            super::vals::Pfivl::from_bits(val as u8)\n        }\n        #[doc = \"Periodic frame interval\"]\n        #[inline(always)]\n        pub fn set_pfivl(&mut self, val: super::vals::Pfivl) {\n            self.0 = (self.0 & !(0x03 << 11usize)) | (((val.to_bits() as u32) & 0x03) << 11usize);\n        }\n        #[doc = \"Transceiver delay\"]\n        #[inline(always)]\n        pub const fn xcvrdly(&self) -> bool {\n            let val = (self.0 >> 14usize) & 0x01;\n            val != 0\n        }\n        #[doc = \"Transceiver delay\"]\n        #[inline(always)]\n        pub fn set_xcvrdly(&mut self, val: bool) {\n            self.0 = (self.0 & !(0x01 << 14usize)) | (((val as u32) & 0x01) << 14usize);\n        }\n    }\n    impl Default for Dcfg {\n        #[inline(always)]\n        fn default() -> Dcfg {\n            Dcfg(0)\n        }\n    }\n    #[doc = \"Device control register\"]\n    #[repr(transparent)]\n    #[derive(Copy, Clone, Eq, PartialEq)]\n    pub struct Dctl(pub u32);\n    impl Dctl {\n        #[doc = \"Remote wakeup signaling\"]\n        #[inline(always)]\n        pub const fn rwusig(&self) -> bool {\n            let val = (self.0 >> 0usize) & 0x01;\n            val != 0\n        }\n        #[doc = \"Remote wakeup signaling\"]\n        #[inline(always)]\n        pub fn set_rwusig(&mut self, val: bool) {\n            self.0 = (self.0 & !(0x01 << 0usize)) | (((val as u32) & 0x01) << 0usize);\n        }\n        #[doc = \"Soft disconnect\"]\n        #[inline(always)]\n        pub const fn sdis(&self) -> bool {\n            let val = (self.0 >> 1usize) & 0x01;\n            val != 0\n        }\n        #[doc = \"Soft disconnect\"]\n        #[inline(always)]\n        pub fn set_sdis(&mut self, val: bool) {\n            self.0 = (self.0 & !(0x01 << 1usize)) | (((val as u32) & 0x01) << 1usize);\n        }\n        #[doc = \"Global IN NAK status\"]\n        #[inline(always)]\n        pub const fn ginsts(&self) -> bool {\n            let val = (self.0 >> 2usize) & 0x01;\n            val != 0\n        }\n        #[doc = \"Global IN NAK status\"]\n        #[inline(always)]\n        pub fn set_ginsts(&mut self, val: bool) {\n            self.0 = (self.0 & !(0x01 << 2usize)) | (((val as u32) & 0x01) << 2usize);\n        }\n        #[doc = \"Global OUT NAK status\"]\n        #[inline(always)]\n        pub const fn gonsts(&self) -> bool {\n            let val = (self.0 >> 3usize) & 0x01;\n            val != 0\n        }\n        #[doc = \"Global OUT NAK status\"]\n        #[inline(always)]\n        pub fn set_gonsts(&mut self, val: bool) {\n            self.0 = (self.0 & !(0x01 << 3usize)) | (((val as u32) & 0x01) << 3usize);\n        }\n        #[doc = \"Test control\"]\n        #[inline(always)]\n        pub const fn tctl(&self) -> u8 {\n            let val = (self.0 >> 4usize) & 0x07;\n            val as u8\n        }\n        #[doc = \"Test control\"]\n        #[inline(always)]\n        pub fn set_tctl(&mut self, val: u8) {\n            self.0 = (self.0 & !(0x07 << 4usize)) | (((val as u32) & 0x07) << 4usize);\n        }\n        #[doc = \"Set global IN NAK\"]\n        #[inline(always)]\n        pub const fn sginak(&self) -> bool {\n            let val = (self.0 >> 7usize) & 0x01;\n            val != 0\n        }\n        #[doc = \"Set global IN NAK\"]\n        #[inline(always)]\n        pub fn set_sginak(&mut self, val: bool) {\n            self.0 = (self.0 & !(0x01 << 7usize)) | (((val as u32) & 0x01) << 7usize);\n        }\n        #[doc = \"Clear global IN NAK\"]\n        #[inline(always)]\n        pub const fn cginak(&self) -> bool {\n            let val = (self.0 >> 8usize) & 0x01;\n            val != 0\n        }\n        #[doc = \"Clear global IN NAK\"]\n        #[inline(always)]\n        pub fn set_cginak(&mut self, val: bool) {\n            self.0 = (self.0 & !(0x01 << 8usize)) | (((val as u32) & 0x01) << 8usize);\n        }\n        #[doc = \"Set global OUT NAK\"]\n        #[inline(always)]\n        pub const fn sgonak(&self) -> bool {\n            let val = (self.0 >> 9usize) & 0x01;\n            val != 0\n        }\n        #[doc = \"Set global OUT NAK\"]\n        #[inline(always)]\n        pub fn set_sgonak(&mut self, val: bool) {\n            self.0 = (self.0 & !(0x01 << 9usize)) | (((val as u32) & 0x01) << 9usize);\n        }\n        #[doc = \"Clear global OUT NAK\"]\n        #[inline(always)]\n        pub const fn cgonak(&self) -> bool {\n            let val = (self.0 >> 10usize) & 0x01;\n            val != 0\n        }\n        #[doc = \"Clear global OUT NAK\"]\n        #[inline(always)]\n        pub fn set_cgonak(&mut self, val: bool) {\n            self.0 = (self.0 & !(0x01 << 10usize)) | (((val as u32) & 0x01) << 10usize);\n        }\n        #[doc = \"Power-on programming done\"]\n        #[inline(always)]\n        pub const fn poprgdne(&self) -> bool {\n            let val = (self.0 >> 11usize) & 0x01;\n            val != 0\n        }\n        #[doc = \"Power-on programming done\"]\n        #[inline(always)]\n        pub fn set_poprgdne(&mut self, val: bool) {\n            self.0 = (self.0 & !(0x01 << 11usize)) | (((val as u32) & 0x01) << 11usize);\n        }\n    }\n    impl Default for Dctl {\n        #[inline(always)]\n        fn default() -> Dctl {\n            Dctl(0)\n        }\n    }\n    #[doc = \"Device endpoint control register\"]\n    #[repr(transparent)]\n    #[derive(Copy, Clone, Eq, PartialEq)]\n    pub struct Diepctl(pub u32);\n    impl Diepctl {\n        #[doc = \"MPSIZ\"]\n        #[inline(always)]\n        pub const fn mpsiz(&self) -> u16 {\n            let val = (self.0 >> 0usize) & 0x07ff;\n            val as u16\n        }\n        #[doc = \"MPSIZ\"]\n        #[inline(always)]\n        pub fn set_mpsiz(&mut self, val: u16) {\n            self.0 = (self.0 & !(0x07ff << 0usize)) | (((val as u32) & 0x07ff) << 0usize);\n        }\n        #[doc = \"USBAEP\"]\n        #[inline(always)]\n        pub const fn usbaep(&self) -> bool {\n            let val = (self.0 >> 15usize) & 0x01;\n            val != 0\n        }\n        #[doc = \"USBAEP\"]\n        #[inline(always)]\n        pub fn set_usbaep(&mut self, val: bool) {\n            self.0 = (self.0 & !(0x01 << 15usize)) | (((val as u32) & 0x01) << 15usize);\n        }\n        #[doc = \"EONUM/DPID\"]\n        #[inline(always)]\n        pub const fn eonum_dpid(&self) -> bool {\n            let val = (self.0 >> 16usize) & 0x01;\n            val != 0\n        }\n        #[doc = \"EONUM/DPID\"]\n        #[inline(always)]\n        pub fn set_eonum_dpid(&mut self, val: bool) {\n            self.0 = (self.0 & !(0x01 << 16usize)) | (((val as u32) & 0x01) << 16usize);\n        }\n        #[doc = \"NAKSTS\"]\n        #[inline(always)]\n        pub const fn naksts(&self) -> bool {\n            let val = (self.0 >> 17usize) & 0x01;\n            val != 0\n        }\n        #[doc = \"NAKSTS\"]\n        #[inline(always)]\n        pub fn set_naksts(&mut self, val: bool) {\n            self.0 = (self.0 & !(0x01 << 17usize)) | (((val as u32) & 0x01) << 17usize);\n        }\n        #[doc = \"EPTYP\"]\n        #[inline(always)]\n        pub const fn eptyp(&self) -> super::vals::Eptyp {\n            let val = (self.0 >> 18usize) & 0x03;\n            super::vals::Eptyp::from_bits(val as u8)\n        }\n        #[doc = \"EPTYP\"]\n        #[inline(always)]\n        pub fn set_eptyp(&mut self, val: super::vals::Eptyp) {\n            self.0 = (self.0 & !(0x03 << 18usize)) | (((val.to_bits() as u32) & 0x03) << 18usize);\n        }\n        #[doc = \"SNPM\"]\n        #[inline(always)]\n        pub const fn snpm(&self) -> bool {\n            let val = (self.0 >> 20usize) & 0x01;\n            val != 0\n        }\n        #[doc = \"SNPM\"]\n        #[inline(always)]\n        pub fn set_snpm(&mut self, val: bool) {\n            self.0 = (self.0 & !(0x01 << 20usize)) | (((val as u32) & 0x01) << 20usize);\n        }\n        #[doc = \"STALL\"]\n        #[inline(always)]\n        pub const fn stall(&self) -> bool {\n            let val = (self.0 >> 21usize) & 0x01;\n            val != 0\n        }\n        #[doc = \"STALL\"]\n        #[inline(always)]\n        pub fn set_stall(&mut self, val: bool) {\n            self.0 = (self.0 & !(0x01 << 21usize)) | (((val as u32) & 0x01) << 21usize);\n        }\n        #[doc = \"TXFNUM\"]\n        #[inline(always)]\n        pub const fn txfnum(&self) -> u8 {\n            let val = (self.0 >> 22usize) & 0x0f;\n            val as u8\n        }\n        #[doc = \"TXFNUM\"]\n        #[inline(always)]\n        pub fn set_txfnum(&mut self, val: u8) {\n            self.0 = (self.0 & !(0x0f << 22usize)) | (((val as u32) & 0x0f) << 22usize);\n        }\n        #[doc = \"CNAK\"]\n        #[inline(always)]\n        pub const fn cnak(&self) -> bool {\n            let val = (self.0 >> 26usize) & 0x01;\n            val != 0\n        }\n        #[doc = \"CNAK\"]\n        #[inline(always)]\n        pub fn set_cnak(&mut self, val: bool) {\n            self.0 = (self.0 & !(0x01 << 26usize)) | (((val as u32) & 0x01) << 26usize);\n        }\n        #[doc = \"SNAK\"]\n        #[inline(always)]\n        pub const fn snak(&self) -> bool {\n            let val = (self.0 >> 27usize) & 0x01;\n            val != 0\n        }\n        #[doc = \"SNAK\"]\n        #[inline(always)]\n        pub fn set_snak(&mut self, val: bool) {\n            self.0 = (self.0 & !(0x01 << 27usize)) | (((val as u32) & 0x01) << 27usize);\n        }\n        #[doc = \"SD0PID/SEVNFRM\"]\n        #[inline(always)]\n        pub const fn sd0pid_sevnfrm(&self) -> bool {\n            let val = (self.0 >> 28usize) & 0x01;\n            val != 0\n        }\n        #[doc = \"SD0PID/SEVNFRM\"]\n        #[inline(always)]\n        pub fn set_sd0pid_sevnfrm(&mut self, val: bool) {\n            self.0 = (self.0 & !(0x01 << 28usize)) | (((val as u32) & 0x01) << 28usize);\n        }\n        #[doc = \"SD1PID/SODDFRM\"]\n        #[inline(always)]\n        pub const fn sd1pid_soddfrm(&self) -> bool {\n            let val = (self.0 >> 29usize) & 0x01;\n            val != 0\n        }\n        #[doc = \"SD1PID/SODDFRM\"]\n        #[inline(always)]\n        pub fn set_sd1pid_soddfrm(&mut self, val: bool) {\n            self.0 = (self.0 & !(0x01 << 29usize)) | (((val as u32) & 0x01) << 29usize);\n        }\n        #[doc = \"EPDIS\"]\n        #[inline(always)]\n        pub const fn epdis(&self) -> bool {\n            let val = (self.0 >> 30usize) & 0x01;\n            val != 0\n        }\n        #[doc = \"EPDIS\"]\n        #[inline(always)]\n        pub fn set_epdis(&mut self, val: bool) {\n            self.0 = (self.0 & !(0x01 << 30usize)) | (((val as u32) & 0x01) << 30usize);\n        }\n        #[doc = \"EPENA\"]\n        #[inline(always)]\n        pub const fn epena(&self) -> bool {\n            let val = (self.0 >> 31usize) & 0x01;\n            val != 0\n        }\n        #[doc = \"EPENA\"]\n        #[inline(always)]\n        pub fn set_epena(&mut self, val: bool) {\n            self.0 = (self.0 & !(0x01 << 31usize)) | (((val as u32) & 0x01) << 31usize);\n        }\n    }\n    impl Default for Diepctl {\n        #[inline(always)]\n        fn default() -> Diepctl {\n            Diepctl(0)\n        }\n    }\n    #[doc = \"Device IN endpoint FIFO empty interrupt mask register\"]\n    #[repr(transparent)]\n    #[derive(Copy, Clone, Eq, PartialEq)]\n    pub struct Diepempmsk(pub u32);\n    impl Diepempmsk {\n        #[doc = \"IN EP Tx FIFO empty interrupt mask bits\"]\n        #[inline(always)]\n        pub const fn ineptxfem(&self) -> u16 {\n            let val = (self.0 >> 0usize) & 0xffff;\n            val as u16\n        }\n        #[doc = \"IN EP Tx FIFO empty interrupt mask bits\"]\n        #[inline(always)]\n        pub fn set_ineptxfem(&mut self, val: u16) {\n            self.0 = (self.0 & !(0xffff << 0usize)) | (((val as u32) & 0xffff) << 0usize);\n        }\n    }\n    impl Default for Diepempmsk {\n        #[inline(always)]\n        fn default() -> Diepempmsk {\n            Diepempmsk(0)\n        }\n    }\n    #[doc = \"Device endpoint interrupt register\"]\n    #[repr(transparent)]\n    #[derive(Copy, Clone, Eq, PartialEq)]\n    pub struct Diepint(pub u32);\n    impl Diepint {\n        #[doc = \"XFRC\"]\n        #[inline(always)]\n        pub const fn xfrc(&self) -> bool {\n            let val = (self.0 >> 0usize) & 0x01;\n            val != 0\n        }\n        #[doc = \"XFRC\"]\n        #[inline(always)]\n        pub fn set_xfrc(&mut self, val: bool) {\n            self.0 = (self.0 & !(0x01 << 0usize)) | (((val as u32) & 0x01) << 0usize);\n        }\n        #[doc = \"EPDISD\"]\n        #[inline(always)]\n        pub const fn epdisd(&self) -> bool {\n            let val = (self.0 >> 1usize) & 0x01;\n            val != 0\n        }\n        #[doc = \"EPDISD\"]\n        #[inline(always)]\n        pub fn set_epdisd(&mut self, val: bool) {\n            self.0 = (self.0 & !(0x01 << 1usize)) | (((val as u32) & 0x01) << 1usize);\n        }\n        #[doc = \"TOC\"]\n        #[inline(always)]\n        pub const fn toc(&self) -> bool {\n            let val = (self.0 >> 3usize) & 0x01;\n            val != 0\n        }\n        #[doc = \"TOC\"]\n        #[inline(always)]\n        pub fn set_toc(&mut self, val: bool) {\n            self.0 = (self.0 & !(0x01 << 3usize)) | (((val as u32) & 0x01) << 3usize);\n        }\n        #[doc = \"ITTXFE\"]\n        #[inline(always)]\n        pub const fn ittxfe(&self) -> bool {\n            let val = (self.0 >> 4usize) & 0x01;\n            val != 0\n        }\n        #[doc = \"ITTXFE\"]\n        #[inline(always)]\n        pub fn set_ittxfe(&mut self, val: bool) {\n            self.0 = (self.0 & !(0x01 << 4usize)) | (((val as u32) & 0x01) << 4usize);\n        }\n        #[doc = \"INEPNE\"]\n        #[inline(always)]\n        pub const fn inepne(&self) -> bool {\n            let val = (self.0 >> 6usize) & 0x01;\n            val != 0\n        }\n        #[doc = \"INEPNE\"]\n        #[inline(always)]\n        pub fn set_inepne(&mut self, val: bool) {\n            self.0 = (self.0 & !(0x01 << 6usize)) | (((val as u32) & 0x01) << 6usize);\n        }\n        #[doc = \"TXFE\"]\n        #[inline(always)]\n        pub const fn txfe(&self) -> bool {\n            let val = (self.0 >> 7usize) & 0x01;\n            val != 0\n        }\n        #[doc = \"TXFE\"]\n        #[inline(always)]\n        pub fn set_txfe(&mut self, val: bool) {\n            self.0 = (self.0 & !(0x01 << 7usize)) | (((val as u32) & 0x01) << 7usize);\n        }\n    }\n    impl Default for Diepint {\n        #[inline(always)]\n        fn default() -> Diepint {\n            Diepint(0)\n        }\n    }\n    #[doc = \"Device IN endpoint common interrupt mask register\"]\n    #[repr(transparent)]\n    #[derive(Copy, Clone, Eq, PartialEq)]\n    pub struct Diepmsk(pub u32);\n    impl Diepmsk {\n        #[doc = \"Transfer completed interrupt mask\"]\n        #[inline(always)]\n        pub const fn xfrcm(&self) -> bool {\n            let val = (self.0 >> 0usize) & 0x01;\n            val != 0\n        }\n        #[doc = \"Transfer completed interrupt mask\"]\n        #[inline(always)]\n        pub fn set_xfrcm(&mut self, val: bool) {\n            self.0 = (self.0 & !(0x01 << 0usize)) | (((val as u32) & 0x01) << 0usize);\n        }\n        #[doc = \"Endpoint disabled interrupt mask\"]\n        #[inline(always)]\n        pub const fn epdm(&self) -> bool {\n            let val = (self.0 >> 1usize) & 0x01;\n            val != 0\n        }\n        #[doc = \"Endpoint disabled interrupt mask\"]\n        #[inline(always)]\n        pub fn set_epdm(&mut self, val: bool) {\n            self.0 = (self.0 & !(0x01 << 1usize)) | (((val as u32) & 0x01) << 1usize);\n        }\n        #[doc = \"Timeout condition mask (Non-isochronous endpoints)\"]\n        #[inline(always)]\n        pub const fn tom(&self) -> bool {\n            let val = (self.0 >> 3usize) & 0x01;\n            val != 0\n        }\n        #[doc = \"Timeout condition mask (Non-isochronous endpoints)\"]\n        #[inline(always)]\n        pub fn set_tom(&mut self, val: bool) {\n            self.0 = (self.0 & !(0x01 << 3usize)) | (((val as u32) & 0x01) << 3usize);\n        }\n        #[doc = \"IN token received when TxFIFO empty mask\"]\n        #[inline(always)]\n        pub const fn ittxfemsk(&self) -> bool {\n            let val = (self.0 >> 4usize) & 0x01;\n            val != 0\n        }\n        #[doc = \"IN token received when TxFIFO empty mask\"]\n        #[inline(always)]\n        pub fn set_ittxfemsk(&mut self, val: bool) {\n            self.0 = (self.0 & !(0x01 << 4usize)) | (((val as u32) & 0x01) << 4usize);\n        }\n        #[doc = \"IN token received with EP mismatch mask\"]\n        #[inline(always)]\n        pub const fn inepnmm(&self) -> bool {\n            let val = (self.0 >> 5usize) & 0x01;\n            val != 0\n        }\n        #[doc = \"IN token received with EP mismatch mask\"]\n        #[inline(always)]\n        pub fn set_inepnmm(&mut self, val: bool) {\n            self.0 = (self.0 & !(0x01 << 5usize)) | (((val as u32) & 0x01) << 5usize);\n        }\n        #[doc = \"IN endpoint NAK effective mask\"]\n        #[inline(always)]\n        pub const fn inepnem(&self) -> bool {\n            let val = (self.0 >> 6usize) & 0x01;\n            val != 0\n        }\n        #[doc = \"IN endpoint NAK effective mask\"]\n        #[inline(always)]\n        pub fn set_inepnem(&mut self, val: bool) {\n            self.0 = (self.0 & !(0x01 << 6usize)) | (((val as u32) & 0x01) << 6usize);\n        }\n    }\n    impl Default for Diepmsk {\n        #[inline(always)]\n        fn default() -> Diepmsk {\n            Diepmsk(0)\n        }\n    }\n    #[doc = \"Device endpoint transfer size register\"]\n    #[repr(transparent)]\n    #[derive(Copy, Clone, Eq, PartialEq)]\n    pub struct Dieptsiz(pub u32);\n    impl Dieptsiz {\n        #[doc = \"Transfer size\"]\n        #[inline(always)]\n        pub const fn xfrsiz(&self) -> u32 {\n            let val = (self.0 >> 0usize) & 0x0007_ffff;\n            val as u32\n        }\n        #[doc = \"Transfer size\"]\n        #[inline(always)]\n        pub fn set_xfrsiz(&mut self, val: u32) {\n            self.0 = (self.0 & !(0x0007_ffff << 0usize)) | (((val as u32) & 0x0007_ffff) << 0usize);\n        }\n        #[doc = \"Packet count\"]\n        #[inline(always)]\n        pub const fn pktcnt(&self) -> u16 {\n            let val = (self.0 >> 19usize) & 0x03ff;\n            val as u16\n        }\n        #[doc = \"Packet count\"]\n        #[inline(always)]\n        pub fn set_pktcnt(&mut self, val: u16) {\n            self.0 = (self.0 & !(0x03ff << 19usize)) | (((val as u32) & 0x03ff) << 19usize);\n        }\n        #[doc = \"Multi count\"]\n        #[inline(always)]\n        pub const fn mcnt(&self) -> u8 {\n            let val = (self.0 >> 29usize) & 0x03;\n            val as u8\n        }\n        #[doc = \"Multi count\"]\n        #[inline(always)]\n        pub fn set_mcnt(&mut self, val: u8) {\n            self.0 = (self.0 & !(0x03 << 29usize)) | (((val as u32) & 0x03) << 29usize);\n        }\n    }\n    impl Default for Dieptsiz {\n        #[inline(always)]\n        fn default() -> Dieptsiz {\n            Dieptsiz(0)\n        }\n    }\n    #[doc = \"Device endpoint control register\"]\n    #[repr(transparent)]\n    #[derive(Copy, Clone, Eq, PartialEq)]\n    pub struct Doepctl(pub u32);\n    impl Doepctl {\n        #[doc = \"MPSIZ\"]\n        #[inline(always)]\n        pub const fn mpsiz(&self) -> u16 {\n            let val = (self.0 >> 0usize) & 0x07ff;\n            val as u16\n        }\n        #[doc = \"MPSIZ\"]\n        #[inline(always)]\n        pub fn set_mpsiz(&mut self, val: u16) {\n            self.0 = (self.0 & !(0x07ff << 0usize)) | (((val as u32) & 0x07ff) << 0usize);\n        }\n        #[doc = \"USBAEP\"]\n        #[inline(always)]\n        pub const fn usbaep(&self) -> bool {\n            let val = (self.0 >> 15usize) & 0x01;\n            val != 0\n        }\n        #[doc = \"USBAEP\"]\n        #[inline(always)]\n        pub fn set_usbaep(&mut self, val: bool) {\n            self.0 = (self.0 & !(0x01 << 15usize)) | (((val as u32) & 0x01) << 15usize);\n        }\n        #[doc = \"EONUM/DPID\"]\n        #[inline(always)]\n        pub const fn eonum_dpid(&self) -> bool {\n            let val = (self.0 >> 16usize) & 0x01;\n            val != 0\n        }\n        #[doc = \"EONUM/DPID\"]\n        #[inline(always)]\n        pub fn set_eonum_dpid(&mut self, val: bool) {\n            self.0 = (self.0 & !(0x01 << 16usize)) | (((val as u32) & 0x01) << 16usize);\n        }\n        #[doc = \"NAKSTS\"]\n        #[inline(always)]\n        pub const fn naksts(&self) -> bool {\n            let val = (self.0 >> 17usize) & 0x01;\n            val != 0\n        }\n        #[doc = \"NAKSTS\"]\n        #[inline(always)]\n        pub fn set_naksts(&mut self, val: bool) {\n            self.0 = (self.0 & !(0x01 << 17usize)) | (((val as u32) & 0x01) << 17usize);\n        }\n        #[doc = \"EPTYP\"]\n        #[inline(always)]\n        pub const fn eptyp(&self) -> super::vals::Eptyp {\n            let val = (self.0 >> 18usize) & 0x03;\n            super::vals::Eptyp::from_bits(val as u8)\n        }\n        #[doc = \"EPTYP\"]\n        #[inline(always)]\n        pub fn set_eptyp(&mut self, val: super::vals::Eptyp) {\n            self.0 = (self.0 & !(0x03 << 18usize)) | (((val.to_bits() as u32) & 0x03) << 18usize);\n        }\n        #[doc = \"SNPM\"]\n        #[inline(always)]\n        pub const fn snpm(&self) -> bool {\n            let val = (self.0 >> 20usize) & 0x01;\n            val != 0\n        }\n        #[doc = \"SNPM\"]\n        #[inline(always)]\n        pub fn set_snpm(&mut self, val: bool) {\n            self.0 = (self.0 & !(0x01 << 20usize)) | (((val as u32) & 0x01) << 20usize);\n        }\n        #[doc = \"STALL\"]\n        #[inline(always)]\n        pub const fn stall(&self) -> bool {\n            let val = (self.0 >> 21usize) & 0x01;\n            val != 0\n        }\n        #[doc = \"STALL\"]\n        #[inline(always)]\n        pub fn set_stall(&mut self, val: bool) {\n            self.0 = (self.0 & !(0x01 << 21usize)) | (((val as u32) & 0x01) << 21usize);\n        }\n        #[doc = \"CNAK\"]\n        #[inline(always)]\n        pub const fn cnak(&self) -> bool {\n            let val = (self.0 >> 26usize) & 0x01;\n            val != 0\n        }\n        #[doc = \"CNAK\"]\n        #[inline(always)]\n        pub fn set_cnak(&mut self, val: bool) {\n            self.0 = (self.0 & !(0x01 << 26usize)) | (((val as u32) & 0x01) << 26usize);\n        }\n        #[doc = \"SNAK\"]\n        #[inline(always)]\n        pub const fn snak(&self) -> bool {\n            let val = (self.0 >> 27usize) & 0x01;\n            val != 0\n        }\n        #[doc = \"SNAK\"]\n        #[inline(always)]\n        pub fn set_snak(&mut self, val: bool) {\n            self.0 = (self.0 & !(0x01 << 27usize)) | (((val as u32) & 0x01) << 27usize);\n        }\n        #[doc = \"SD0PID/SEVNFRM\"]\n        #[inline(always)]\n        pub const fn sd0pid_sevnfrm(&self) -> bool {\n            let val = (self.0 >> 28usize) & 0x01;\n            val != 0\n        }\n        #[doc = \"SD0PID/SEVNFRM\"]\n        #[inline(always)]\n        pub fn set_sd0pid_sevnfrm(&mut self, val: bool) {\n            self.0 = (self.0 & !(0x01 << 28usize)) | (((val as u32) & 0x01) << 28usize);\n        }\n        #[doc = \"SD1PID/SODDFRM\"]\n        #[inline(always)]\n        pub const fn sd1pid_soddfrm(&self) -> bool {\n            let val = (self.0 >> 29usize) & 0x01;\n            val != 0\n        }\n        #[doc = \"SD1PID/SODDFRM\"]\n        #[inline(always)]\n        pub fn set_sd1pid_soddfrm(&mut self, val: bool) {\n            self.0 = (self.0 & !(0x01 << 29usize)) | (((val as u32) & 0x01) << 29usize);\n        }\n        #[doc = \"EPDIS\"]\n        #[inline(always)]\n        pub const fn epdis(&self) -> bool {\n            let val = (self.0 >> 30usize) & 0x01;\n            val != 0\n        }\n        #[doc = \"EPDIS\"]\n        #[inline(always)]\n        pub fn set_epdis(&mut self, val: bool) {\n            self.0 = (self.0 & !(0x01 << 30usize)) | (((val as u32) & 0x01) << 30usize);\n        }\n        #[doc = \"EPENA\"]\n        #[inline(always)]\n        pub const fn epena(&self) -> bool {\n            let val = (self.0 >> 31usize) & 0x01;\n            val != 0\n        }\n        #[doc = \"EPENA\"]\n        #[inline(always)]\n        pub fn set_epena(&mut self, val: bool) {\n            self.0 = (self.0 & !(0x01 << 31usize)) | (((val as u32) & 0x01) << 31usize);\n        }\n    }\n    impl Default for Doepctl {\n        #[inline(always)]\n        fn default() -> Doepctl {\n            Doepctl(0)\n        }\n    }\n    #[doc = \"Device endpoint interrupt register\"]\n    #[repr(transparent)]\n    #[derive(Copy, Clone, Eq, PartialEq)]\n    pub struct Doepint(pub u32);\n    impl Doepint {\n        #[doc = \"XFRC\"]\n        #[inline(always)]\n        pub const fn xfrc(&self) -> bool {\n            let val = (self.0 >> 0usize) & 0x01;\n            val != 0\n        }\n        #[doc = \"XFRC\"]\n        #[inline(always)]\n        pub fn set_xfrc(&mut self, val: bool) {\n            self.0 = (self.0 & !(0x01 << 0usize)) | (((val as u32) & 0x01) << 0usize);\n        }\n        #[doc = \"EPDISD\"]\n        #[inline(always)]\n        pub const fn epdisd(&self) -> bool {\n            let val = (self.0 >> 1usize) & 0x01;\n            val != 0\n        }\n        #[doc = \"EPDISD\"]\n        #[inline(always)]\n        pub fn set_epdisd(&mut self, val: bool) {\n            self.0 = (self.0 & !(0x01 << 1usize)) | (((val as u32) & 0x01) << 1usize);\n        }\n        #[doc = \"STUP\"]\n        #[inline(always)]\n        pub const fn stup(&self) -> bool {\n            let val = (self.0 >> 3usize) & 0x01;\n            val != 0\n        }\n        #[doc = \"STUP\"]\n        #[inline(always)]\n        pub fn set_stup(&mut self, val: bool) {\n            self.0 = (self.0 & !(0x01 << 3usize)) | (((val as u32) & 0x01) << 3usize);\n        }\n        #[doc = \"OTEPDIS\"]\n        #[inline(always)]\n        pub const fn otepdis(&self) -> bool {\n            let val = (self.0 >> 4usize) & 0x01;\n            val != 0\n        }\n        #[doc = \"OTEPDIS\"]\n        #[inline(always)]\n        pub fn set_otepdis(&mut self, val: bool) {\n            self.0 = (self.0 & !(0x01 << 4usize)) | (((val as u32) & 0x01) << 4usize);\n        }\n        #[doc = \"B2BSTUP\"]\n        #[inline(always)]\n        pub const fn b2bstup(&self) -> bool {\n            let val = (self.0 >> 6usize) & 0x01;\n            val != 0\n        }\n        #[doc = \"B2BSTUP\"]\n        #[inline(always)]\n        pub fn set_b2bstup(&mut self, val: bool) {\n            self.0 = (self.0 & !(0x01 << 6usize)) | (((val as u32) & 0x01) << 6usize);\n        }\n    }\n    impl Default for Doepint {\n        #[inline(always)]\n        fn default() -> Doepint {\n            Doepint(0)\n        }\n    }\n    #[doc = \"Device OUT endpoint common interrupt mask register\"]\n    #[repr(transparent)]\n    #[derive(Copy, Clone, Eq, PartialEq)]\n    pub struct Doepmsk(pub u32);\n    impl Doepmsk {\n        #[doc = \"Transfer completed interrupt mask\"]\n        #[inline(always)]\n        pub const fn xfrcm(&self) -> bool {\n            let val = (self.0 >> 0usize) & 0x01;\n            val != 0\n        }\n        #[doc = \"Transfer completed interrupt mask\"]\n        #[inline(always)]\n        pub fn set_xfrcm(&mut self, val: bool) {\n            self.0 = (self.0 & !(0x01 << 0usize)) | (((val as u32) & 0x01) << 0usize);\n        }\n        #[doc = \"Endpoint disabled interrupt mask\"]\n        #[inline(always)]\n        pub const fn epdm(&self) -> bool {\n            let val = (self.0 >> 1usize) & 0x01;\n            val != 0\n        }\n        #[doc = \"Endpoint disabled interrupt mask\"]\n        #[inline(always)]\n        pub fn set_epdm(&mut self, val: bool) {\n            self.0 = (self.0 & !(0x01 << 1usize)) | (((val as u32) & 0x01) << 1usize);\n        }\n        #[doc = \"SETUP phase done mask\"]\n        #[inline(always)]\n        pub const fn stupm(&self) -> bool {\n            let val = (self.0 >> 3usize) & 0x01;\n            val != 0\n        }\n        #[doc = \"SETUP phase done mask\"]\n        #[inline(always)]\n        pub fn set_stupm(&mut self, val: bool) {\n            self.0 = (self.0 & !(0x01 << 3usize)) | (((val as u32) & 0x01) << 3usize);\n        }\n        #[doc = \"OUT token received when endpoint disabled mask\"]\n        #[inline(always)]\n        pub const fn otepdm(&self) -> bool {\n            let val = (self.0 >> 4usize) & 0x01;\n            val != 0\n        }\n        #[doc = \"OUT token received when endpoint disabled mask\"]\n        #[inline(always)]\n        pub fn set_otepdm(&mut self, val: bool) {\n            self.0 = (self.0 & !(0x01 << 4usize)) | (((val as u32) & 0x01) << 4usize);\n        }\n    }\n    impl Default for Doepmsk {\n        #[inline(always)]\n        fn default() -> Doepmsk {\n            Doepmsk(0)\n        }\n    }\n    #[doc = \"Device OUT endpoint transfer size register\"]\n    #[repr(transparent)]\n    #[derive(Copy, Clone, Eq, PartialEq)]\n    pub struct Doeptsiz(pub u32);\n    impl Doeptsiz {\n        #[doc = \"Transfer size\"]\n        #[inline(always)]\n        pub const fn xfrsiz(&self) -> u32 {\n            let val = (self.0 >> 0usize) & 0x0007_ffff;\n            val as u32\n        }\n        #[doc = \"Transfer size\"]\n        #[inline(always)]\n        pub fn set_xfrsiz(&mut self, val: u32) {\n            self.0 = (self.0 & !(0x0007_ffff << 0usize)) | (((val as u32) & 0x0007_ffff) << 0usize);\n        }\n        #[doc = \"Packet count\"]\n        #[inline(always)]\n        pub const fn pktcnt(&self) -> u16 {\n            let val = (self.0 >> 19usize) & 0x03ff;\n            val as u16\n        }\n        #[doc = \"Packet count\"]\n        #[inline(always)]\n        pub fn set_pktcnt(&mut self, val: u16) {\n            self.0 = (self.0 & !(0x03ff << 19usize)) | (((val as u32) & 0x03ff) << 19usize);\n        }\n        #[doc = \"Received data PID/SETUP packet count\"]\n        #[inline(always)]\n        pub const fn rxdpid_stupcnt(&self) -> u8 {\n            let val = (self.0 >> 29usize) & 0x03;\n            val as u8\n        }\n        #[doc = \"Received data PID/SETUP packet count\"]\n        #[inline(always)]\n        pub fn set_rxdpid_stupcnt(&mut self, val: u8) {\n            self.0 = (self.0 & !(0x03 << 29usize)) | (((val as u32) & 0x03) << 29usize);\n        }\n    }\n    impl Default for Doeptsiz {\n        #[inline(always)]\n        fn default() -> Doeptsiz {\n            Doeptsiz(0)\n        }\n    }\n    #[doc = \"Device status register\"]\n    #[repr(transparent)]\n    #[derive(Copy, Clone, Eq, PartialEq)]\n    pub struct Dsts(pub u32);\n    impl Dsts {\n        #[doc = \"Suspend status\"]\n        #[inline(always)]\n        pub const fn suspsts(&self) -> bool {\n            let val = (self.0 >> 0usize) & 0x01;\n            val != 0\n        }\n        #[doc = \"Suspend status\"]\n        #[inline(always)]\n        pub fn set_suspsts(&mut self, val: bool) {\n            self.0 = (self.0 & !(0x01 << 0usize)) | (((val as u32) & 0x01) << 0usize);\n        }\n        #[doc = \"Enumerated speed\"]\n        #[inline(always)]\n        pub const fn enumspd(&self) -> super::vals::Dspd {\n            let val = (self.0 >> 1usize) & 0x03;\n            super::vals::Dspd::from_bits(val as u8)\n        }\n        #[doc = \"Enumerated speed\"]\n        #[inline(always)]\n        pub fn set_enumspd(&mut self, val: super::vals::Dspd) {\n            self.0 = (self.0 & !(0x03 << 1usize)) | (((val.to_bits() as u32) & 0x03) << 1usize);\n        }\n        #[doc = \"Erratic error\"]\n        #[inline(always)]\n        pub const fn eerr(&self) -> bool {\n            let val = (self.0 >> 3usize) & 0x01;\n            val != 0\n        }\n        #[doc = \"Erratic error\"]\n        #[inline(always)]\n        pub fn set_eerr(&mut self, val: bool) {\n            self.0 = (self.0 & !(0x01 << 3usize)) | (((val as u32) & 0x01) << 3usize);\n        }\n        #[doc = \"Frame number of the received SOF\"]\n        #[inline(always)]\n        pub const fn fnsof(&self) -> u16 {\n            let val = (self.0 >> 8usize) & 0x3fff;\n            val as u16\n        }\n        #[doc = \"Frame number of the received SOF\"]\n        #[inline(always)]\n        pub fn set_fnsof(&mut self, val: u16) {\n            self.0 = (self.0 & !(0x3fff << 8usize)) | (((val as u32) & 0x3fff) << 8usize);\n        }\n    }\n    impl Default for Dsts {\n        #[inline(always)]\n        fn default() -> Dsts {\n            Dsts(0)\n        }\n    }\n    #[doc = \"Device IN endpoint transmit FIFO status register\"]\n    #[repr(transparent)]\n    #[derive(Copy, Clone, Eq, PartialEq)]\n    pub struct Dtxfsts(pub u32);\n    impl Dtxfsts {\n        #[doc = \"IN endpoint TxFIFO space available\"]\n        #[inline(always)]\n        pub const fn ineptfsav(&self) -> u16 {\n            let val = (self.0 >> 0usize) & 0xffff;\n            val as u16\n        }\n        #[doc = \"IN endpoint TxFIFO space available\"]\n        #[inline(always)]\n        pub fn set_ineptfsav(&mut self, val: u16) {\n            self.0 = (self.0 & !(0xffff << 0usize)) | (((val as u32) & 0xffff) << 0usize);\n        }\n    }\n    impl Default for Dtxfsts {\n        #[inline(always)]\n        fn default() -> Dtxfsts {\n            Dtxfsts(0)\n        }\n    }\n    #[doc = \"Device VBUS discharge time register\"]\n    #[repr(transparent)]\n    #[derive(Copy, Clone, Eq, PartialEq)]\n    pub struct Dvbusdis(pub u32);\n    impl Dvbusdis {\n        #[doc = \"Device VBUS discharge time\"]\n        #[inline(always)]\n        pub const fn vbusdt(&self) -> u16 {\n            let val = (self.0 >> 0usize) & 0xffff;\n            val as u16\n        }\n        #[doc = \"Device VBUS discharge time\"]\n        #[inline(always)]\n        pub fn set_vbusdt(&mut self, val: u16) {\n            self.0 = (self.0 & !(0xffff << 0usize)) | (((val as u32) & 0xffff) << 0usize);\n        }\n    }\n    impl Default for Dvbusdis {\n        #[inline(always)]\n        fn default() -> Dvbusdis {\n            Dvbusdis(0)\n        }\n    }\n    #[doc = \"Device VBUS pulsing time register\"]\n    #[repr(transparent)]\n    #[derive(Copy, Clone, Eq, PartialEq)]\n    pub struct Dvbuspulse(pub u32);\n    impl Dvbuspulse {\n        #[doc = \"Device VBUS pulsing time\"]\n        #[inline(always)]\n        pub const fn dvbusp(&self) -> u16 {\n            let val = (self.0 >> 0usize) & 0x0fff;\n            val as u16\n        }\n        #[doc = \"Device VBUS pulsing time\"]\n        #[inline(always)]\n        pub fn set_dvbusp(&mut self, val: u16) {\n            self.0 = (self.0 & !(0x0fff << 0usize)) | (((val as u32) & 0x0fff) << 0usize);\n        }\n    }\n    impl Default for Dvbuspulse {\n        #[inline(always)]\n        fn default() -> Dvbuspulse {\n            Dvbuspulse(0)\n        }\n    }\n    #[doc = \"FIFO register\"]\n    #[repr(transparent)]\n    #[derive(Copy, Clone, Eq, PartialEq)]\n    pub struct Fifo(pub u32);\n    impl Fifo {\n        #[doc = \"Data\"]\n        #[inline(always)]\n        pub const fn data(&self) -> u32 {\n            let val = (self.0 >> 0usize) & 0xffff_ffff;\n            val as u32\n        }\n        #[doc = \"Data\"]\n        #[inline(always)]\n        pub fn set_data(&mut self, val: u32) {\n            self.0 = (self.0 & !(0xffff_ffff << 0usize)) | (((val as u32) & 0xffff_ffff) << 0usize);\n        }\n    }\n    impl Default for Fifo {\n        #[inline(always)]\n        fn default() -> Fifo {\n            Fifo(0)\n        }\n    }\n    #[doc = \"FIFO size register\"]\n    #[repr(transparent)]\n    #[derive(Copy, Clone, Eq, PartialEq)]\n    pub struct Fsiz(pub u32);\n    impl Fsiz {\n        #[doc = \"RAM start address\"]\n        #[inline(always)]\n        pub const fn sa(&self) -> u16 {\n            let val = (self.0 >> 0usize) & 0xffff;\n            val as u16\n        }\n        #[doc = \"RAM start address\"]\n        #[inline(always)]\n        pub fn set_sa(&mut self, val: u16) {\n            self.0 = (self.0 & !(0xffff << 0usize)) | (((val as u32) & 0xffff) << 0usize);\n        }\n        #[doc = \"FIFO depth\"]\n        #[inline(always)]\n        pub const fn fd(&self) -> u16 {\n            let val = (self.0 >> 16usize) & 0xffff;\n            val as u16\n        }\n        #[doc = \"FIFO depth\"]\n        #[inline(always)]\n        pub fn set_fd(&mut self, val: u16) {\n            self.0 = (self.0 & !(0xffff << 16usize)) | (((val as u32) & 0xffff) << 16usize);\n        }\n    }\n    impl Default for Fsiz {\n        #[inline(always)]\n        fn default() -> Fsiz {\n            Fsiz(0)\n        }\n    }\n    #[doc = \"AHB configuration register\"]\n    #[repr(transparent)]\n    #[derive(Copy, Clone, Eq, PartialEq)]\n    pub struct Gahbcfg(pub u32);\n    impl Gahbcfg {\n        #[doc = \"Global interrupt mask\"]\n        #[inline(always)]\n        pub const fn gint(&self) -> bool {\n            let val = (self.0 >> 0usize) & 0x01;\n            val != 0\n        }\n        #[doc = \"Global interrupt mask\"]\n        #[inline(always)]\n        pub fn set_gint(&mut self, val: bool) {\n            self.0 = (self.0 & !(0x01 << 0usize)) | (((val as u32) & 0x01) << 0usize);\n        }\n        #[doc = \"Burst length/type\"]\n        #[inline(always)]\n        pub const fn hbstlen(&self) -> u8 {\n            let val = (self.0 >> 1usize) & 0x0f;\n            val as u8\n        }\n        #[doc = \"Burst length/type\"]\n        #[inline(always)]\n        pub fn set_hbstlen(&mut self, val: u8) {\n            self.0 = (self.0 & !(0x0f << 1usize)) | (((val as u32) & 0x0f) << 1usize);\n        }\n        #[doc = \"DMA enable\"]\n        #[inline(always)]\n        pub const fn dmaen(&self) -> bool {\n            let val = (self.0 >> 5usize) & 0x01;\n            val != 0\n        }\n        #[doc = \"DMA enable\"]\n        #[inline(always)]\n        pub fn set_dmaen(&mut self, val: bool) {\n            self.0 = (self.0 & !(0x01 << 5usize)) | (((val as u32) & 0x01) << 5usize);\n        }\n        #[doc = \"TxFIFO empty level\"]\n        #[inline(always)]\n        pub const fn txfelvl(&self) -> bool {\n            let val = (self.0 >> 7usize) & 0x01;\n            val != 0\n        }\n        #[doc = \"TxFIFO empty level\"]\n        #[inline(always)]\n        pub fn set_txfelvl(&mut self, val: bool) {\n            self.0 = (self.0 & !(0x01 << 7usize)) | (((val as u32) & 0x01) << 7usize);\n        }\n        #[doc = \"Periodic TxFIFO empty level\"]\n        #[inline(always)]\n        pub const fn ptxfelvl(&self) -> bool {\n            let val = (self.0 >> 8usize) & 0x01;\n            val != 0\n        }\n        #[doc = \"Periodic TxFIFO empty level\"]\n        #[inline(always)]\n        pub fn set_ptxfelvl(&mut self, val: bool) {\n            self.0 = (self.0 & !(0x01 << 8usize)) | (((val as u32) & 0x01) << 8usize);\n        }\n    }\n    impl Default for Gahbcfg {\n        #[inline(always)]\n        fn default() -> Gahbcfg {\n            Gahbcfg(0)\n        }\n    }\n    #[doc = \"General core configuration register\"]\n    #[repr(transparent)]\n    #[derive(Copy, Clone, Eq, PartialEq)]\n    pub struct GccfgV1(pub u32);\n    impl GccfgV1 {\n        #[doc = \"Power down\"]\n        #[inline(always)]\n        pub const fn pwrdwn(&self) -> bool {\n            let val = (self.0 >> 16usize) & 0x01;\n            val != 0\n        }\n        #[doc = \"Power down\"]\n        #[inline(always)]\n        pub fn set_pwrdwn(&mut self, val: bool) {\n            self.0 = (self.0 & !(0x01 << 16usize)) | (((val as u32) & 0x01) << 16usize);\n        }\n        #[doc = \"Enable the VBUS \\\"A\\\" sensing device\"]\n        #[inline(always)]\n        pub const fn vbusasen(&self) -> bool {\n            let val = (self.0 >> 18usize) & 0x01;\n            val != 0\n        }\n        #[doc = \"Enable the VBUS \\\"A\\\" sensing device\"]\n        #[inline(always)]\n        pub fn set_vbusasen(&mut self, val: bool) {\n            self.0 = (self.0 & !(0x01 << 18usize)) | (((val as u32) & 0x01) << 18usize);\n        }\n        #[doc = \"Enable the VBUS \\\"B\\\" sensing device\"]\n        #[inline(always)]\n        pub const fn vbusbsen(&self) -> bool {\n            let val = (self.0 >> 19usize) & 0x01;\n            val != 0\n        }\n        #[doc = \"Enable the VBUS \\\"B\\\" sensing device\"]\n        #[inline(always)]\n        pub fn set_vbusbsen(&mut self, val: bool) {\n            self.0 = (self.0 & !(0x01 << 19usize)) | (((val as u32) & 0x01) << 19usize);\n        }\n        #[doc = \"SOF output enable\"]\n        #[inline(always)]\n        pub const fn sofouten(&self) -> bool {\n            let val = (self.0 >> 20usize) & 0x01;\n            val != 0\n        }\n        #[doc = \"SOF output enable\"]\n        #[inline(always)]\n        pub fn set_sofouten(&mut self, val: bool) {\n            self.0 = (self.0 & !(0x01 << 20usize)) | (((val as u32) & 0x01) << 20usize);\n        }\n        #[doc = \"VBUS sensing disable\"]\n        #[inline(always)]\n        pub const fn novbussens(&self) -> bool {\n            let val = (self.0 >> 21usize) & 0x01;\n            val != 0\n        }\n        #[doc = \"VBUS sensing disable\"]\n        #[inline(always)]\n        pub fn set_novbussens(&mut self, val: bool) {\n            self.0 = (self.0 & !(0x01 << 21usize)) | (((val as u32) & 0x01) << 21usize);\n        }\n    }\n    impl Default for GccfgV1 {\n        #[inline(always)]\n        fn default() -> GccfgV1 {\n            GccfgV1(0)\n        }\n    }\n    #[doc = \"General core configuration register\"]\n    #[repr(transparent)]\n    #[derive(Copy, Clone, Eq, PartialEq)]\n    pub struct GccfgV2(pub u32);\n    impl GccfgV2 {\n        #[doc = \"Data contact detection (DCD) status\"]\n        #[inline(always)]\n        pub const fn dcdet(&self) -> bool {\n            let val = (self.0 >> 0usize) & 0x01;\n            val != 0\n        }\n        #[doc = \"Data contact detection (DCD) status\"]\n        #[inline(always)]\n        pub fn set_dcdet(&mut self, val: bool) {\n            self.0 = (self.0 & !(0x01 << 0usize)) | (((val as u32) & 0x01) << 0usize);\n        }\n        #[doc = \"Primary detection (PD) status\"]\n        #[inline(always)]\n        pub const fn pdet(&self) -> bool {\n            let val = (self.0 >> 1usize) & 0x01;\n            val != 0\n        }\n        #[doc = \"Primary detection (PD) status\"]\n        #[inline(always)]\n        pub fn set_pdet(&mut self, val: bool) {\n            self.0 = (self.0 & !(0x01 << 1usize)) | (((val as u32) & 0x01) << 1usize);\n        }\n        #[doc = \"Secondary detection (SD) status\"]\n        #[inline(always)]\n        pub const fn sdet(&self) -> bool {\n            let val = (self.0 >> 2usize) & 0x01;\n            val != 0\n        }\n        #[doc = \"Secondary detection (SD) status\"]\n        #[inline(always)]\n        pub fn set_sdet(&mut self, val: bool) {\n            self.0 = (self.0 & !(0x01 << 2usize)) | (((val as u32) & 0x01) << 2usize);\n        }\n        #[doc = \"DM pull-up detection status\"]\n        #[inline(always)]\n        pub const fn ps2det(&self) -> bool {\n            let val = (self.0 >> 3usize) & 0x01;\n            val != 0\n        }\n        #[doc = \"DM pull-up detection status\"]\n        #[inline(always)]\n        pub fn set_ps2det(&mut self, val: bool) {\n            self.0 = (self.0 & !(0x01 << 3usize)) | (((val as u32) & 0x01) << 3usize);\n        }\n        #[doc = \"Power down\"]\n        #[inline(always)]\n        pub const fn pwrdwn(&self) -> bool {\n            let val = (self.0 >> 16usize) & 0x01;\n            val != 0\n        }\n        #[doc = \"Power down\"]\n        #[inline(always)]\n        pub fn set_pwrdwn(&mut self, val: bool) {\n            self.0 = (self.0 & !(0x01 << 16usize)) | (((val as u32) & 0x01) << 16usize);\n        }\n        #[doc = \"Battery charging detector (BCD) enable\"]\n        #[inline(always)]\n        pub const fn bcden(&self) -> bool {\n            let val = (self.0 >> 17usize) & 0x01;\n            val != 0\n        }\n        #[doc = \"Battery charging detector (BCD) enable\"]\n        #[inline(always)]\n        pub fn set_bcden(&mut self, val: bool) {\n            self.0 = (self.0 & !(0x01 << 17usize)) | (((val as u32) & 0x01) << 17usize);\n        }\n        #[doc = \"Data contact detection (DCD) mode enable\"]\n        #[inline(always)]\n        pub const fn dcden(&self) -> bool {\n            let val = (self.0 >> 18usize) & 0x01;\n            val != 0\n        }\n        #[doc = \"Data contact detection (DCD) mode enable\"]\n        #[inline(always)]\n        pub fn set_dcden(&mut self, val: bool) {\n            self.0 = (self.0 & !(0x01 << 18usize)) | (((val as u32) & 0x01) << 18usize);\n        }\n        #[doc = \"Primary detection (PD) mode enable\"]\n        #[inline(always)]\n        pub const fn pden(&self) -> bool {\n            let val = (self.0 >> 19usize) & 0x01;\n            val != 0\n        }\n        #[doc = \"Primary detection (PD) mode enable\"]\n        #[inline(always)]\n        pub fn set_pden(&mut self, val: bool) {\n            self.0 = (self.0 & !(0x01 << 19usize)) | (((val as u32) & 0x01) << 19usize);\n        }\n        #[doc = \"Secondary detection (SD) mode enable\"]\n        #[inline(always)]\n        pub const fn sden(&self) -> bool {\n            let val = (self.0 >> 20usize) & 0x01;\n            val != 0\n        }\n        #[doc = \"Secondary detection (SD) mode enable\"]\n        #[inline(always)]\n        pub fn set_sden(&mut self, val: bool) {\n            self.0 = (self.0 & !(0x01 << 20usize)) | (((val as u32) & 0x01) << 20usize);\n        }\n        #[doc = \"USB VBUS detection enable\"]\n        #[inline(always)]\n        pub const fn vbden(&self) -> bool {\n            let val = (self.0 >> 21usize) & 0x01;\n            val != 0\n        }\n        #[doc = \"USB VBUS detection enable\"]\n        #[inline(always)]\n        pub fn set_vbden(&mut self, val: bool) {\n            self.0 = (self.0 & !(0x01 << 21usize)) | (((val as u32) & 0x01) << 21usize);\n        }\n        #[doc = \"Internal high-speed PHY enable.\"]\n        #[inline(always)]\n        pub const fn phyhsen(&self) -> bool {\n            let val = (self.0 >> 23usize) & 0x01;\n            val != 0\n        }\n        #[doc = \"Internal high-speed PHY enable.\"]\n        #[inline(always)]\n        pub fn set_phyhsen(&mut self, val: bool) {\n            self.0 = (self.0 & !(0x01 << 23usize)) | (((val as u32) & 0x01) << 23usize);\n        }\n    }\n    impl Default for GccfgV2 {\n        #[inline(always)]\n        fn default() -> GccfgV2 {\n            GccfgV2(0)\n        }\n    }\n    #[doc = \"OTG general core configuration register.\"]\n    #[repr(transparent)]\n    #[derive(Copy, Clone, Eq, PartialEq)]\n    pub struct GccfgV3(pub u32);\n    impl GccfgV3 {\n        #[doc = \"Charger detection, result of the current mode (primary or secondary).\"]\n        #[inline(always)]\n        pub const fn chgdet(&self) -> bool {\n            let val = (self.0 >> 0usize) & 0x01;\n            val != 0\n        }\n        #[doc = \"Charger detection, result of the current mode (primary or secondary).\"]\n        #[inline(always)]\n        pub fn set_chgdet(&mut self, val: bool) {\n            self.0 = (self.0 & !(0x01 << 0usize)) | (((val as u32) & 0x01) << 0usize);\n        }\n        #[doc = \"Single-Ended DP indicator This bit gives the voltage level on DP (also result of the comparison with V<sub>LGC</sub> threshold as defined in BC v1.2 standard).\"]\n        #[inline(always)]\n        pub const fn fsvplus(&self) -> bool {\n            let val = (self.0 >> 1usize) & 0x01;\n            val != 0\n        }\n        #[doc = \"Single-Ended DP indicator This bit gives the voltage level on DP (also result of the comparison with V<sub>LGC</sub> threshold as defined in BC v1.2 standard).\"]\n        #[inline(always)]\n        pub fn set_fsvplus(&mut self, val: bool) {\n            self.0 = (self.0 & !(0x01 << 1usize)) | (((val as u32) & 0x01) << 1usize);\n        }\n        #[doc = \"Single-Ended DM indicator This bit gives the voltage level on DM (also result of the comparison with V<sub>LGC</sub> threshold as defined in BC v1.2 standard).\"]\n        #[inline(always)]\n        pub const fn fsvminus(&self) -> bool {\n            let val = (self.0 >> 2usize) & 0x01;\n            val != 0\n        }\n        #[doc = \"Single-Ended DM indicator This bit gives the voltage level on DM (also result of the comparison with V<sub>LGC</sub> threshold as defined in BC v1.2 standard).\"]\n        #[inline(always)]\n        pub fn set_fsvminus(&mut self, val: bool) {\n            self.0 = (self.0 & !(0x01 << 2usize)) | (((val as u32) & 0x01) << 2usize);\n        }\n        #[doc = \"VBUS session indicator Indicates if VBUS is above VBUS session threshold.\"]\n        #[inline(always)]\n        pub const fn sessvld(&self) -> bool {\n            let val = (self.0 >> 3usize) & 0x01;\n            val != 0\n        }\n        #[doc = \"VBUS session indicator Indicates if VBUS is above VBUS session threshold.\"]\n        #[inline(always)]\n        pub fn set_sessvld(&mut self, val: bool) {\n            self.0 = (self.0 & !(0x01 << 3usize)) | (((val as u32) & 0x01) << 3usize);\n        }\n        #[doc = \"Host CDP behavior enable.\"]\n        #[inline(always)]\n        pub const fn hcdpen(&self) -> bool {\n            let val = (self.0 >> 16usize) & 0x01;\n            val != 0\n        }\n        #[doc = \"Host CDP behavior enable.\"]\n        #[inline(always)]\n        pub fn set_hcdpen(&mut self, val: bool) {\n            self.0 = (self.0 & !(0x01 << 16usize)) | (((val as u32) & 0x01) << 16usize);\n        }\n        #[doc = \"Host CDP port voltage detector enable on DP.\"]\n        #[inline(always)]\n        pub const fn hcdpdeten(&self) -> bool {\n            let val = (self.0 >> 17usize) & 0x01;\n            val != 0\n        }\n        #[doc = \"Host CDP port voltage detector enable on DP.\"]\n        #[inline(always)]\n        pub fn set_hcdpdeten(&mut self, val: bool) {\n            self.0 = (self.0 & !(0x01 << 17usize)) | (((val as u32) & 0x01) << 17usize);\n        }\n        #[doc = \"Host CDP port Voltage source enable on DM.\"]\n        #[inline(always)]\n        pub const fn hvdmsrcen(&self) -> bool {\n            let val = (self.0 >> 18usize) & 0x01;\n            val != 0\n        }\n        #[doc = \"Host CDP port Voltage source enable on DM.\"]\n        #[inline(always)]\n        pub fn set_hvdmsrcen(&mut self, val: bool) {\n            self.0 = (self.0 & !(0x01 << 18usize)) | (((val as u32) & 0x01) << 18usize);\n        }\n        #[doc = \"Data Contact Detection enable.\"]\n        #[inline(always)]\n        pub const fn dcden(&self) -> bool {\n            let val = (self.0 >> 19usize) & 0x01;\n            val != 0\n        }\n        #[doc = \"Data Contact Detection enable.\"]\n        #[inline(always)]\n        pub fn set_dcden(&mut self, val: bool) {\n            self.0 = (self.0 & !(0x01 << 19usize)) | (((val as u32) & 0x01) << 19usize);\n        }\n        #[doc = \"Primary detection enable.\"]\n        #[inline(always)]\n        pub const fn pden(&self) -> bool {\n            let val = (self.0 >> 20usize) & 0x01;\n            val != 0\n        }\n        #[doc = \"Primary detection enable.\"]\n        #[inline(always)]\n        pub fn set_pden(&mut self, val: bool) {\n            self.0 = (self.0 & !(0x01 << 20usize)) | (((val as u32) & 0x01) << 20usize);\n        }\n        #[doc = \"VBUS detection enable Enables VBUS Sensing Comparators in order to detect VBUS presence and/or perform OTG operation.\"]\n        #[inline(always)]\n        pub const fn vbden(&self) -> bool {\n            let val = (self.0 >> 21usize) & 0x01;\n            val != 0\n        }\n        #[doc = \"VBUS detection enable Enables VBUS Sensing Comparators in order to detect VBUS presence and/or perform OTG operation.\"]\n        #[inline(always)]\n        pub fn set_vbden(&mut self, val: bool) {\n            self.0 = (self.0 & !(0x01 << 21usize)) | (((val as u32) & 0x01) << 21usize);\n        }\n        #[doc = \"Secondary detection enable.\"]\n        #[inline(always)]\n        pub const fn sden(&self) -> bool {\n            let val = (self.0 >> 22usize) & 0x01;\n            val != 0\n        }\n        #[doc = \"Secondary detection enable.\"]\n        #[inline(always)]\n        pub fn set_sden(&mut self, val: bool) {\n            self.0 = (self.0 & !(0x01 << 22usize)) | (((val as u32) & 0x01) << 22usize);\n        }\n        #[doc = \"Software override value of the VBUS B-session detection.\"]\n        #[inline(always)]\n        pub const fn vbvaloval(&self) -> bool {\n            let val = (self.0 >> 23usize) & 0x01;\n            val != 0\n        }\n        #[doc = \"Software override value of the VBUS B-session detection.\"]\n        #[inline(always)]\n        pub fn set_vbvaloval(&mut self, val: bool) {\n            self.0 = (self.0 & !(0x01 << 23usize)) | (((val as u32) & 0x01) << 23usize);\n        }\n        #[doc = \"Enables a software override of the VBUS B-session detection.\"]\n        #[inline(always)]\n        pub const fn vbvaloven(&self) -> bool {\n            let val = (self.0 >> 24usize) & 0x01;\n            val != 0\n        }\n        #[doc = \"Enables a software override of the VBUS B-session detection.\"]\n        #[inline(always)]\n        pub fn set_vbvaloven(&mut self, val: bool) {\n            self.0 = (self.0 & !(0x01 << 24usize)) | (((val as u32) & 0x01) << 24usize);\n        }\n        #[doc = \"Force host mode pull-downs If the ID pin functions are enabled, the host mode pull-downs on DP and DM activate automatically. However, whenever that is not the case, yet host mode is required, this bit must be used to force the pull-downs active.\"]\n        #[inline(always)]\n        pub const fn forcehostpd(&self) -> bool {\n            let val = (self.0 >> 25usize) & 0x01;\n            val != 0\n        }\n        #[doc = \"Force host mode pull-downs If the ID pin functions are enabled, the host mode pull-downs on DP and DM activate automatically. However, whenever that is not the case, yet host mode is required, this bit must be used to force the pull-downs active.\"]\n        #[inline(always)]\n        pub fn set_forcehostpd(&mut self, val: bool) {\n            self.0 = (self.0 & !(0x01 << 25usize)) | (((val as u32) & 0x01) << 25usize);\n        }\n    }\n    impl Default for GccfgV3 {\n        #[inline(always)]\n        fn default() -> GccfgV3 {\n            GccfgV3(0)\n        }\n    }\n    #[doc = \"I2C access register\"]\n    #[repr(transparent)]\n    #[derive(Copy, Clone, Eq, PartialEq)]\n    pub struct Gi2cctl(pub u32);\n    impl Gi2cctl {\n        #[doc = \"I2C Read/Write Data\"]\n        #[inline(always)]\n        pub const fn rwdata(&self) -> u8 {\n            let val = (self.0 >> 0usize) & 0xff;\n            val as u8\n        }\n        #[doc = \"I2C Read/Write Data\"]\n        #[inline(always)]\n        pub fn set_rwdata(&mut self, val: u8) {\n            self.0 = (self.0 & !(0xff << 0usize)) | (((val as u32) & 0xff) << 0usize);\n        }\n        #[doc = \"I2C Register Address\"]\n        #[inline(always)]\n        pub const fn regaddr(&self) -> u8 {\n            let val = (self.0 >> 8usize) & 0xff;\n            val as u8\n        }\n        #[doc = \"I2C Register Address\"]\n        #[inline(always)]\n        pub fn set_regaddr(&mut self, val: u8) {\n            self.0 = (self.0 & !(0xff << 8usize)) | (((val as u32) & 0xff) << 8usize);\n        }\n        #[doc = \"I2C Address\"]\n        #[inline(always)]\n        pub const fn addr(&self) -> u8 {\n            let val = (self.0 >> 16usize) & 0x7f;\n            val as u8\n        }\n        #[doc = \"I2C Address\"]\n        #[inline(always)]\n        pub fn set_addr(&mut self, val: u8) {\n            self.0 = (self.0 & !(0x7f << 16usize)) | (((val as u32) & 0x7f) << 16usize);\n        }\n        #[doc = \"I2C Enable\"]\n        #[inline(always)]\n        pub const fn i2cen(&self) -> bool {\n            let val = (self.0 >> 23usize) & 0x01;\n            val != 0\n        }\n        #[doc = \"I2C Enable\"]\n        #[inline(always)]\n        pub fn set_i2cen(&mut self, val: bool) {\n            self.0 = (self.0 & !(0x01 << 23usize)) | (((val as u32) & 0x01) << 23usize);\n        }\n        #[doc = \"I2C ACK\"]\n        #[inline(always)]\n        pub const fn ack(&self) -> bool {\n            let val = (self.0 >> 24usize) & 0x01;\n            val != 0\n        }\n        #[doc = \"I2C ACK\"]\n        #[inline(always)]\n        pub fn set_ack(&mut self, val: bool) {\n            self.0 = (self.0 & !(0x01 << 24usize)) | (((val as u32) & 0x01) << 24usize);\n        }\n        #[doc = \"I2C Device Address\"]\n        #[inline(always)]\n        pub const fn i2cdevadr(&self) -> u8 {\n            let val = (self.0 >> 26usize) & 0x03;\n            val as u8\n        }\n        #[doc = \"I2C Device Address\"]\n        #[inline(always)]\n        pub fn set_i2cdevadr(&mut self, val: u8) {\n            self.0 = (self.0 & !(0x03 << 26usize)) | (((val as u32) & 0x03) << 26usize);\n        }\n        #[doc = \"I2C DatSe0 USB mode\"]\n        #[inline(always)]\n        pub const fn i2cdatse0(&self) -> bool {\n            let val = (self.0 >> 28usize) & 0x01;\n            val != 0\n        }\n        #[doc = \"I2C DatSe0 USB mode\"]\n        #[inline(always)]\n        pub fn set_i2cdatse0(&mut self, val: bool) {\n            self.0 = (self.0 & !(0x01 << 28usize)) | (((val as u32) & 0x01) << 28usize);\n        }\n        #[doc = \"Read/Write Indicator\"]\n        #[inline(always)]\n        pub const fn rw(&self) -> bool {\n            let val = (self.0 >> 30usize) & 0x01;\n            val != 0\n        }\n        #[doc = \"Read/Write Indicator\"]\n        #[inline(always)]\n        pub fn set_rw(&mut self, val: bool) {\n            self.0 = (self.0 & !(0x01 << 30usize)) | (((val as u32) & 0x01) << 30usize);\n        }\n        #[doc = \"I2C Busy/Done\"]\n        #[inline(always)]\n        pub const fn bsydne(&self) -> bool {\n            let val = (self.0 >> 31usize) & 0x01;\n            val != 0\n        }\n        #[doc = \"I2C Busy/Done\"]\n        #[inline(always)]\n        pub fn set_bsydne(&mut self, val: bool) {\n            self.0 = (self.0 & !(0x01 << 31usize)) | (((val as u32) & 0x01) << 31usize);\n        }\n    }\n    impl Default for Gi2cctl {\n        #[inline(always)]\n        fn default() -> Gi2cctl {\n            Gi2cctl(0)\n        }\n    }\n    #[doc = \"Interrupt mask register\"]\n    #[repr(transparent)]\n    #[derive(Copy, Clone, Eq, PartialEq)]\n    pub struct Gintmsk(pub u32);\n    impl Gintmsk {\n        #[doc = \"Mode mismatch interrupt mask\"]\n        #[inline(always)]\n        pub const fn mmism(&self) -> bool {\n            let val = (self.0 >> 1usize) & 0x01;\n            val != 0\n        }\n        #[doc = \"Mode mismatch interrupt mask\"]\n        #[inline(always)]\n        pub fn set_mmism(&mut self, val: bool) {\n            self.0 = (self.0 & !(0x01 << 1usize)) | (((val as u32) & 0x01) << 1usize);\n        }\n        #[doc = \"OTG interrupt mask\"]\n        #[inline(always)]\n        pub const fn otgint(&self) -> bool {\n            let val = (self.0 >> 2usize) & 0x01;\n            val != 0\n        }\n        #[doc = \"OTG interrupt mask\"]\n        #[inline(always)]\n        pub fn set_otgint(&mut self, val: bool) {\n            self.0 = (self.0 & !(0x01 << 2usize)) | (((val as u32) & 0x01) << 2usize);\n        }\n        #[doc = \"Start of frame mask\"]\n        #[inline(always)]\n        pub const fn sofm(&self) -> bool {\n            let val = (self.0 >> 3usize) & 0x01;\n            val != 0\n        }\n        #[doc = \"Start of frame mask\"]\n        #[inline(always)]\n        pub fn set_sofm(&mut self, val: bool) {\n            self.0 = (self.0 & !(0x01 << 3usize)) | (((val as u32) & 0x01) << 3usize);\n        }\n        #[doc = \"Receive FIFO non-empty mask\"]\n        #[inline(always)]\n        pub const fn rxflvlm(&self) -> bool {\n            let val = (self.0 >> 4usize) & 0x01;\n            val != 0\n        }\n        #[doc = \"Receive FIFO non-empty mask\"]\n        #[inline(always)]\n        pub fn set_rxflvlm(&mut self, val: bool) {\n            self.0 = (self.0 & !(0x01 << 4usize)) | (((val as u32) & 0x01) << 4usize);\n        }\n        #[doc = \"Non-periodic TxFIFO empty mask\"]\n        #[inline(always)]\n        pub const fn nptxfem(&self) -> bool {\n            let val = (self.0 >> 5usize) & 0x01;\n            val != 0\n        }\n        #[doc = \"Non-periodic TxFIFO empty mask\"]\n        #[inline(always)]\n        pub fn set_nptxfem(&mut self, val: bool) {\n            self.0 = (self.0 & !(0x01 << 5usize)) | (((val as u32) & 0x01) << 5usize);\n        }\n        #[doc = \"Global non-periodic IN NAK effective mask\"]\n        #[inline(always)]\n        pub const fn ginakeffm(&self) -> bool {\n            let val = (self.0 >> 6usize) & 0x01;\n            val != 0\n        }\n        #[doc = \"Global non-periodic IN NAK effective mask\"]\n        #[inline(always)]\n        pub fn set_ginakeffm(&mut self, val: bool) {\n            self.0 = (self.0 & !(0x01 << 6usize)) | (((val as u32) & 0x01) << 6usize);\n        }\n        #[doc = \"Global OUT NAK effective mask\"]\n        #[inline(always)]\n        pub const fn gonakeffm(&self) -> bool {\n            let val = (self.0 >> 7usize) & 0x01;\n            val != 0\n        }\n        #[doc = \"Global OUT NAK effective mask\"]\n        #[inline(always)]\n        pub fn set_gonakeffm(&mut self, val: bool) {\n            self.0 = (self.0 & !(0x01 << 7usize)) | (((val as u32) & 0x01) << 7usize);\n        }\n        #[doc = \"Early suspend mask\"]\n        #[inline(always)]\n        pub const fn esuspm(&self) -> bool {\n            let val = (self.0 >> 10usize) & 0x01;\n            val != 0\n        }\n        #[doc = \"Early suspend mask\"]\n        #[inline(always)]\n        pub fn set_esuspm(&mut self, val: bool) {\n            self.0 = (self.0 & !(0x01 << 10usize)) | (((val as u32) & 0x01) << 10usize);\n        }\n        #[doc = \"USB suspend mask\"]\n        #[inline(always)]\n        pub const fn usbsuspm(&self) -> bool {\n            let val = (self.0 >> 11usize) & 0x01;\n            val != 0\n        }\n        #[doc = \"USB suspend mask\"]\n        #[inline(always)]\n        pub fn set_usbsuspm(&mut self, val: bool) {\n            self.0 = (self.0 & !(0x01 << 11usize)) | (((val as u32) & 0x01) << 11usize);\n        }\n        #[doc = \"USB reset mask\"]\n        #[inline(always)]\n        pub const fn usbrst(&self) -> bool {\n            let val = (self.0 >> 12usize) & 0x01;\n            val != 0\n        }\n        #[doc = \"USB reset mask\"]\n        #[inline(always)]\n        pub fn set_usbrst(&mut self, val: bool) {\n            self.0 = (self.0 & !(0x01 << 12usize)) | (((val as u32) & 0x01) << 12usize);\n        }\n        #[doc = \"Enumeration done mask\"]\n        #[inline(always)]\n        pub const fn enumdnem(&self) -> bool {\n            let val = (self.0 >> 13usize) & 0x01;\n            val != 0\n        }\n        #[doc = \"Enumeration done mask\"]\n        #[inline(always)]\n        pub fn set_enumdnem(&mut self, val: bool) {\n            self.0 = (self.0 & !(0x01 << 13usize)) | (((val as u32) & 0x01) << 13usize);\n        }\n        #[doc = \"Isochronous OUT packet dropped interrupt mask\"]\n        #[inline(always)]\n        pub const fn isoodrpm(&self) -> bool {\n            let val = (self.0 >> 14usize) & 0x01;\n            val != 0\n        }\n        #[doc = \"Isochronous OUT packet dropped interrupt mask\"]\n        #[inline(always)]\n        pub fn set_isoodrpm(&mut self, val: bool) {\n            self.0 = (self.0 & !(0x01 << 14usize)) | (((val as u32) & 0x01) << 14usize);\n        }\n        #[doc = \"End of periodic frame interrupt mask\"]\n        #[inline(always)]\n        pub const fn eopfm(&self) -> bool {\n            let val = (self.0 >> 15usize) & 0x01;\n            val != 0\n        }\n        #[doc = \"End of periodic frame interrupt mask\"]\n        #[inline(always)]\n        pub fn set_eopfm(&mut self, val: bool) {\n            self.0 = (self.0 & !(0x01 << 15usize)) | (((val as u32) & 0x01) << 15usize);\n        }\n        #[doc = \"Endpoint mismatch interrupt mask\"]\n        #[inline(always)]\n        pub const fn epmism(&self) -> bool {\n            let val = (self.0 >> 17usize) & 0x01;\n            val != 0\n        }\n        #[doc = \"Endpoint mismatch interrupt mask\"]\n        #[inline(always)]\n        pub fn set_epmism(&mut self, val: bool) {\n            self.0 = (self.0 & !(0x01 << 17usize)) | (((val as u32) & 0x01) << 17usize);\n        }\n        #[doc = \"IN endpoints interrupt mask\"]\n        #[inline(always)]\n        pub const fn iepint(&self) -> bool {\n            let val = (self.0 >> 18usize) & 0x01;\n            val != 0\n        }\n        #[doc = \"IN endpoints interrupt mask\"]\n        #[inline(always)]\n        pub fn set_iepint(&mut self, val: bool) {\n            self.0 = (self.0 & !(0x01 << 18usize)) | (((val as u32) & 0x01) << 18usize);\n        }\n        #[doc = \"OUT endpoints interrupt mask\"]\n        #[inline(always)]\n        pub const fn oepint(&self) -> bool {\n            let val = (self.0 >> 19usize) & 0x01;\n            val != 0\n        }\n        #[doc = \"OUT endpoints interrupt mask\"]\n        #[inline(always)]\n        pub fn set_oepint(&mut self, val: bool) {\n            self.0 = (self.0 & !(0x01 << 19usize)) | (((val as u32) & 0x01) << 19usize);\n        }\n        #[doc = \"Incomplete isochronous IN transfer mask\"]\n        #[inline(always)]\n        pub const fn iisoixfrm(&self) -> bool {\n            let val = (self.0 >> 20usize) & 0x01;\n            val != 0\n        }\n        #[doc = \"Incomplete isochronous IN transfer mask\"]\n        #[inline(always)]\n        pub fn set_iisoixfrm(&mut self, val: bool) {\n            self.0 = (self.0 & !(0x01 << 20usize)) | (((val as u32) & 0x01) << 20usize);\n        }\n        #[doc = \"Incomplete periodic transfer mask (host mode) / Incomplete isochronous OUT transfer mask (device mode)\"]\n        #[inline(always)]\n        pub const fn ipxfrm_iisooxfrm(&self) -> bool {\n            let val = (self.0 >> 21usize) & 0x01;\n            val != 0\n        }\n        #[doc = \"Incomplete periodic transfer mask (host mode) / Incomplete isochronous OUT transfer mask (device mode)\"]\n        #[inline(always)]\n        pub fn set_ipxfrm_iisooxfrm(&mut self, val: bool) {\n            self.0 = (self.0 & !(0x01 << 21usize)) | (((val as u32) & 0x01) << 21usize);\n        }\n        #[doc = \"Data fetch suspended mask\"]\n        #[inline(always)]\n        pub const fn fsuspm(&self) -> bool {\n            let val = (self.0 >> 22usize) & 0x01;\n            val != 0\n        }\n        #[doc = \"Data fetch suspended mask\"]\n        #[inline(always)]\n        pub fn set_fsuspm(&mut self, val: bool) {\n            self.0 = (self.0 & !(0x01 << 22usize)) | (((val as u32) & 0x01) << 22usize);\n        }\n        #[doc = \"Reset detected interrupt mask\"]\n        #[inline(always)]\n        pub const fn rstde(&self) -> bool {\n            let val = (self.0 >> 23usize) & 0x01;\n            val != 0\n        }\n        #[doc = \"Reset detected interrupt mask\"]\n        #[inline(always)]\n        pub fn set_rstde(&mut self, val: bool) {\n            self.0 = (self.0 & !(0x01 << 23usize)) | (((val as u32) & 0x01) << 23usize);\n        }\n        #[doc = \"Host port interrupt mask\"]\n        #[inline(always)]\n        pub const fn prtim(&self) -> bool {\n            let val = (self.0 >> 24usize) & 0x01;\n            val != 0\n        }\n        #[doc = \"Host port interrupt mask\"]\n        #[inline(always)]\n        pub fn set_prtim(&mut self, val: bool) {\n            self.0 = (self.0 & !(0x01 << 24usize)) | (((val as u32) & 0x01) << 24usize);\n        }\n        #[doc = \"Host channels interrupt mask\"]\n        #[inline(always)]\n        pub const fn hcim(&self) -> bool {\n            let val = (self.0 >> 25usize) & 0x01;\n            val != 0\n        }\n        #[doc = \"Host channels interrupt mask\"]\n        #[inline(always)]\n        pub fn set_hcim(&mut self, val: bool) {\n            self.0 = (self.0 & !(0x01 << 25usize)) | (((val as u32) & 0x01) << 25usize);\n        }\n        #[doc = \"Periodic TxFIFO empty mask\"]\n        #[inline(always)]\n        pub const fn ptxfem(&self) -> bool {\n            let val = (self.0 >> 26usize) & 0x01;\n            val != 0\n        }\n        #[doc = \"Periodic TxFIFO empty mask\"]\n        #[inline(always)]\n        pub fn set_ptxfem(&mut self, val: bool) {\n            self.0 = (self.0 & !(0x01 << 26usize)) | (((val as u32) & 0x01) << 26usize);\n        }\n        #[doc = \"LPM interrupt mask\"]\n        #[inline(always)]\n        pub const fn lpmintm(&self) -> bool {\n            let val = (self.0 >> 27usize) & 0x01;\n            val != 0\n        }\n        #[doc = \"LPM interrupt mask\"]\n        #[inline(always)]\n        pub fn set_lpmintm(&mut self, val: bool) {\n            self.0 = (self.0 & !(0x01 << 27usize)) | (((val as u32) & 0x01) << 27usize);\n        }\n        #[doc = \"Connector ID status change mask\"]\n        #[inline(always)]\n        pub const fn cidschgm(&self) -> bool {\n            let val = (self.0 >> 28usize) & 0x01;\n            val != 0\n        }\n        #[doc = \"Connector ID status change mask\"]\n        #[inline(always)]\n        pub fn set_cidschgm(&mut self, val: bool) {\n            self.0 = (self.0 & !(0x01 << 28usize)) | (((val as u32) & 0x01) << 28usize);\n        }\n        #[doc = \"Disconnect detected interrupt mask\"]\n        #[inline(always)]\n        pub const fn discint(&self) -> bool {\n            let val = (self.0 >> 29usize) & 0x01;\n            val != 0\n        }\n        #[doc = \"Disconnect detected interrupt mask\"]\n        #[inline(always)]\n        pub fn set_discint(&mut self, val: bool) {\n            self.0 = (self.0 & !(0x01 << 29usize)) | (((val as u32) & 0x01) << 29usize);\n        }\n        #[doc = \"Session request/new session detected interrupt mask\"]\n        #[inline(always)]\n        pub const fn srqim(&self) -> bool {\n            let val = (self.0 >> 30usize) & 0x01;\n            val != 0\n        }\n        #[doc = \"Session request/new session detected interrupt mask\"]\n        #[inline(always)]\n        pub fn set_srqim(&mut self, val: bool) {\n            self.0 = (self.0 & !(0x01 << 30usize)) | (((val as u32) & 0x01) << 30usize);\n        }\n        #[doc = \"Resume/remote wakeup detected interrupt mask\"]\n        #[inline(always)]\n        pub const fn wuim(&self) -> bool {\n            let val = (self.0 >> 31usize) & 0x01;\n            val != 0\n        }\n        #[doc = \"Resume/remote wakeup detected interrupt mask\"]\n        #[inline(always)]\n        pub fn set_wuim(&mut self, val: bool) {\n            self.0 = (self.0 & !(0x01 << 31usize)) | (((val as u32) & 0x01) << 31usize);\n        }\n    }\n    impl Default for Gintmsk {\n        #[inline(always)]\n        fn default() -> Gintmsk {\n            Gintmsk(0)\n        }\n    }\n    #[doc = \"Core interrupt register\"]\n    #[repr(transparent)]\n    #[derive(Copy, Clone, Eq, PartialEq)]\n    pub struct Gintsts(pub u32);\n    impl Gintsts {\n        #[doc = \"Current mode of operation\"]\n        #[inline(always)]\n        pub const fn cmod(&self) -> bool {\n            let val = (self.0 >> 0usize) & 0x01;\n            val != 0\n        }\n        #[doc = \"Current mode of operation\"]\n        #[inline(always)]\n        pub fn set_cmod(&mut self, val: bool) {\n            self.0 = (self.0 & !(0x01 << 0usize)) | (((val as u32) & 0x01) << 0usize);\n        }\n        #[doc = \"Mode mismatch interrupt\"]\n        #[inline(always)]\n        pub const fn mmis(&self) -> bool {\n            let val = (self.0 >> 1usize) & 0x01;\n            val != 0\n        }\n        #[doc = \"Mode mismatch interrupt\"]\n        #[inline(always)]\n        pub fn set_mmis(&mut self, val: bool) {\n            self.0 = (self.0 & !(0x01 << 1usize)) | (((val as u32) & 0x01) << 1usize);\n        }\n        #[doc = \"OTG interrupt\"]\n        #[inline(always)]\n        pub const fn otgint(&self) -> bool {\n            let val = (self.0 >> 2usize) & 0x01;\n            val != 0\n        }\n        #[doc = \"OTG interrupt\"]\n        #[inline(always)]\n        pub fn set_otgint(&mut self, val: bool) {\n            self.0 = (self.0 & !(0x01 << 2usize)) | (((val as u32) & 0x01) << 2usize);\n        }\n        #[doc = \"Start of frame\"]\n        #[inline(always)]\n        pub const fn sof(&self) -> bool {\n            let val = (self.0 >> 3usize) & 0x01;\n            val != 0\n        }\n        #[doc = \"Start of frame\"]\n        #[inline(always)]\n        pub fn set_sof(&mut self, val: bool) {\n            self.0 = (self.0 & !(0x01 << 3usize)) | (((val as u32) & 0x01) << 3usize);\n        }\n        #[doc = \"RxFIFO non-empty\"]\n        #[inline(always)]\n        pub const fn rxflvl(&self) -> bool {\n            let val = (self.0 >> 4usize) & 0x01;\n            val != 0\n        }\n        #[doc = \"RxFIFO non-empty\"]\n        #[inline(always)]\n        pub fn set_rxflvl(&mut self, val: bool) {\n            self.0 = (self.0 & !(0x01 << 4usize)) | (((val as u32) & 0x01) << 4usize);\n        }\n        #[doc = \"Non-periodic TxFIFO empty\"]\n        #[inline(always)]\n        pub const fn nptxfe(&self) -> bool {\n            let val = (self.0 >> 5usize) & 0x01;\n            val != 0\n        }\n        #[doc = \"Non-periodic TxFIFO empty\"]\n        #[inline(always)]\n        pub fn set_nptxfe(&mut self, val: bool) {\n            self.0 = (self.0 & !(0x01 << 5usize)) | (((val as u32) & 0x01) << 5usize);\n        }\n        #[doc = \"Global IN non-periodic NAK effective\"]\n        #[inline(always)]\n        pub const fn ginakeff(&self) -> bool {\n            let val = (self.0 >> 6usize) & 0x01;\n            val != 0\n        }\n        #[doc = \"Global IN non-periodic NAK effective\"]\n        #[inline(always)]\n        pub fn set_ginakeff(&mut self, val: bool) {\n            self.0 = (self.0 & !(0x01 << 6usize)) | (((val as u32) & 0x01) << 6usize);\n        }\n        #[doc = \"Global OUT NAK effective\"]\n        #[inline(always)]\n        pub const fn goutnakeff(&self) -> bool {\n            let val = (self.0 >> 7usize) & 0x01;\n            val != 0\n        }\n        #[doc = \"Global OUT NAK effective\"]\n        #[inline(always)]\n        pub fn set_goutnakeff(&mut self, val: bool) {\n            self.0 = (self.0 & !(0x01 << 7usize)) | (((val as u32) & 0x01) << 7usize);\n        }\n        #[doc = \"Early suspend\"]\n        #[inline(always)]\n        pub const fn esusp(&self) -> bool {\n            let val = (self.0 >> 10usize) & 0x01;\n            val != 0\n        }\n        #[doc = \"Early suspend\"]\n        #[inline(always)]\n        pub fn set_esusp(&mut self, val: bool) {\n            self.0 = (self.0 & !(0x01 << 10usize)) | (((val as u32) & 0x01) << 10usize);\n        }\n        #[doc = \"USB suspend\"]\n        #[inline(always)]\n        pub const fn usbsusp(&self) -> bool {\n            let val = (self.0 >> 11usize) & 0x01;\n            val != 0\n        }\n        #[doc = \"USB suspend\"]\n        #[inline(always)]\n        pub fn set_usbsusp(&mut self, val: bool) {\n            self.0 = (self.0 & !(0x01 << 11usize)) | (((val as u32) & 0x01) << 11usize);\n        }\n        #[doc = \"USB reset\"]\n        #[inline(always)]\n        pub const fn usbrst(&self) -> bool {\n            let val = (self.0 >> 12usize) & 0x01;\n            val != 0\n        }\n        #[doc = \"USB reset\"]\n        #[inline(always)]\n        pub fn set_usbrst(&mut self, val: bool) {\n            self.0 = (self.0 & !(0x01 << 12usize)) | (((val as u32) & 0x01) << 12usize);\n        }\n        #[doc = \"Enumeration done\"]\n        #[inline(always)]\n        pub const fn enumdne(&self) -> bool {\n            let val = (self.0 >> 13usize) & 0x01;\n            val != 0\n        }\n        #[doc = \"Enumeration done\"]\n        #[inline(always)]\n        pub fn set_enumdne(&mut self, val: bool) {\n            self.0 = (self.0 & !(0x01 << 13usize)) | (((val as u32) & 0x01) << 13usize);\n        }\n        #[doc = \"Isochronous OUT packet dropped interrupt\"]\n        #[inline(always)]\n        pub const fn isoodrp(&self) -> bool {\n            let val = (self.0 >> 14usize) & 0x01;\n            val != 0\n        }\n        #[doc = \"Isochronous OUT packet dropped interrupt\"]\n        #[inline(always)]\n        pub fn set_isoodrp(&mut self, val: bool) {\n            self.0 = (self.0 & !(0x01 << 14usize)) | (((val as u32) & 0x01) << 14usize);\n        }\n        #[doc = \"End of periodic frame interrupt\"]\n        #[inline(always)]\n        pub const fn eopf(&self) -> bool {\n            let val = (self.0 >> 15usize) & 0x01;\n            val != 0\n        }\n        #[doc = \"End of periodic frame interrupt\"]\n        #[inline(always)]\n        pub fn set_eopf(&mut self, val: bool) {\n            self.0 = (self.0 & !(0x01 << 15usize)) | (((val as u32) & 0x01) << 15usize);\n        }\n        #[doc = \"IN endpoint interrupt\"]\n        #[inline(always)]\n        pub const fn iepint(&self) -> bool {\n            let val = (self.0 >> 18usize) & 0x01;\n            val != 0\n        }\n        #[doc = \"IN endpoint interrupt\"]\n        #[inline(always)]\n        pub fn set_iepint(&mut self, val: bool) {\n            self.0 = (self.0 & !(0x01 << 18usize)) | (((val as u32) & 0x01) << 18usize);\n        }\n        #[doc = \"OUT endpoint interrupt\"]\n        #[inline(always)]\n        pub const fn oepint(&self) -> bool {\n            let val = (self.0 >> 19usize) & 0x01;\n            val != 0\n        }\n        #[doc = \"OUT endpoint interrupt\"]\n        #[inline(always)]\n        pub fn set_oepint(&mut self, val: bool) {\n            self.0 = (self.0 & !(0x01 << 19usize)) | (((val as u32) & 0x01) << 19usize);\n        }\n        #[doc = \"Incomplete isochronous IN transfer\"]\n        #[inline(always)]\n        pub const fn iisoixfr(&self) -> bool {\n            let val = (self.0 >> 20usize) & 0x01;\n            val != 0\n        }\n        #[doc = \"Incomplete isochronous IN transfer\"]\n        #[inline(always)]\n        pub fn set_iisoixfr(&mut self, val: bool) {\n            self.0 = (self.0 & !(0x01 << 20usize)) | (((val as u32) & 0x01) << 20usize);\n        }\n        #[doc = \"Incomplete periodic transfer (host mode) / Incomplete isochronous OUT transfer (device mode)\"]\n        #[inline(always)]\n        pub const fn ipxfr_incompisoout(&self) -> bool {\n            let val = (self.0 >> 21usize) & 0x01;\n            val != 0\n        }\n        #[doc = \"Incomplete periodic transfer (host mode) / Incomplete isochronous OUT transfer (device mode)\"]\n        #[inline(always)]\n        pub fn set_ipxfr_incompisoout(&mut self, val: bool) {\n            self.0 = (self.0 & !(0x01 << 21usize)) | (((val as u32) & 0x01) << 21usize);\n        }\n        #[doc = \"Data fetch suspended\"]\n        #[inline(always)]\n        pub const fn datafsusp(&self) -> bool {\n            let val = (self.0 >> 22usize) & 0x01;\n            val != 0\n        }\n        #[doc = \"Data fetch suspended\"]\n        #[inline(always)]\n        pub fn set_datafsusp(&mut self, val: bool) {\n            self.0 = (self.0 & !(0x01 << 22usize)) | (((val as u32) & 0x01) << 22usize);\n        }\n        #[doc = \"Host port interrupt\"]\n        #[inline(always)]\n        pub const fn hprtint(&self) -> bool {\n            let val = (self.0 >> 24usize) & 0x01;\n            val != 0\n        }\n        #[doc = \"Host port interrupt\"]\n        #[inline(always)]\n        pub fn set_hprtint(&mut self, val: bool) {\n            self.0 = (self.0 & !(0x01 << 24usize)) | (((val as u32) & 0x01) << 24usize);\n        }\n        #[doc = \"Host channels interrupt\"]\n        #[inline(always)]\n        pub const fn hcint(&self) -> bool {\n            let val = (self.0 >> 25usize) & 0x01;\n            val != 0\n        }\n        #[doc = \"Host channels interrupt\"]\n        #[inline(always)]\n        pub fn set_hcint(&mut self, val: bool) {\n            self.0 = (self.0 & !(0x01 << 25usize)) | (((val as u32) & 0x01) << 25usize);\n        }\n        #[doc = \"Periodic TxFIFO empty\"]\n        #[inline(always)]\n        pub const fn ptxfe(&self) -> bool {\n            let val = (self.0 >> 26usize) & 0x01;\n            val != 0\n        }\n        #[doc = \"Periodic TxFIFO empty\"]\n        #[inline(always)]\n        pub fn set_ptxfe(&mut self, val: bool) {\n            self.0 = (self.0 & !(0x01 << 26usize)) | (((val as u32) & 0x01) << 26usize);\n        }\n        #[doc = \"Connector ID status change\"]\n        #[inline(always)]\n        pub const fn cidschg(&self) -> bool {\n            let val = (self.0 >> 28usize) & 0x01;\n            val != 0\n        }\n        #[doc = \"Connector ID status change\"]\n        #[inline(always)]\n        pub fn set_cidschg(&mut self, val: bool) {\n            self.0 = (self.0 & !(0x01 << 28usize)) | (((val as u32) & 0x01) << 28usize);\n        }\n        #[doc = \"Disconnect detected interrupt\"]\n        #[inline(always)]\n        pub const fn discint(&self) -> bool {\n            let val = (self.0 >> 29usize) & 0x01;\n            val != 0\n        }\n        #[doc = \"Disconnect detected interrupt\"]\n        #[inline(always)]\n        pub fn set_discint(&mut self, val: bool) {\n            self.0 = (self.0 & !(0x01 << 29usize)) | (((val as u32) & 0x01) << 29usize);\n        }\n        #[doc = \"Session request/new session detected interrupt\"]\n        #[inline(always)]\n        pub const fn srqint(&self) -> bool {\n            let val = (self.0 >> 30usize) & 0x01;\n            val != 0\n        }\n        #[doc = \"Session request/new session detected interrupt\"]\n        #[inline(always)]\n        pub fn set_srqint(&mut self, val: bool) {\n            self.0 = (self.0 & !(0x01 << 30usize)) | (((val as u32) & 0x01) << 30usize);\n        }\n        #[doc = \"Resume/remote wakeup detected interrupt\"]\n        #[inline(always)]\n        pub const fn wkupint(&self) -> bool {\n            let val = (self.0 >> 31usize) & 0x01;\n            val != 0\n        }\n        #[doc = \"Resume/remote wakeup detected interrupt\"]\n        #[inline(always)]\n        pub fn set_wkupint(&mut self, val: bool) {\n            self.0 = (self.0 & !(0x01 << 31usize)) | (((val as u32) & 0x01) << 31usize);\n        }\n    }\n    impl Default for Gintsts {\n        #[inline(always)]\n        fn default() -> Gintsts {\n            Gintsts(0)\n        }\n    }\n    #[doc = \"Core LPM configuration register\"]\n    #[repr(transparent)]\n    #[derive(Copy, Clone, Eq, PartialEq)]\n    pub struct Glpmcfg(pub u32);\n    impl Glpmcfg {\n        #[doc = \"LPM support enable\"]\n        #[inline(always)]\n        pub const fn lpmen(&self) -> bool {\n            let val = (self.0 >> 0usize) & 0x01;\n            val != 0\n        }\n        #[doc = \"LPM support enable\"]\n        #[inline(always)]\n        pub fn set_lpmen(&mut self, val: bool) {\n            self.0 = (self.0 & !(0x01 << 0usize)) | (((val as u32) & 0x01) << 0usize);\n        }\n        #[doc = \"LPM token acknowledge enable\"]\n        #[inline(always)]\n        pub const fn lpmack(&self) -> bool {\n            let val = (self.0 >> 1usize) & 0x01;\n            val != 0\n        }\n        #[doc = \"LPM token acknowledge enable\"]\n        #[inline(always)]\n        pub fn set_lpmack(&mut self, val: bool) {\n            self.0 = (self.0 & !(0x01 << 1usize)) | (((val as u32) & 0x01) << 1usize);\n        }\n        #[doc = \"Best effort service latency\"]\n        #[inline(always)]\n        pub const fn besl(&self) -> u8 {\n            let val = (self.0 >> 2usize) & 0x0f;\n            val as u8\n        }\n        #[doc = \"Best effort service latency\"]\n        #[inline(always)]\n        pub fn set_besl(&mut self, val: u8) {\n            self.0 = (self.0 & !(0x0f << 2usize)) | (((val as u32) & 0x0f) << 2usize);\n        }\n        #[doc = \"bRemoteWake value\"]\n        #[inline(always)]\n        pub const fn remwake(&self) -> bool {\n            let val = (self.0 >> 6usize) & 0x01;\n            val != 0\n        }\n        #[doc = \"bRemoteWake value\"]\n        #[inline(always)]\n        pub fn set_remwake(&mut self, val: bool) {\n            self.0 = (self.0 & !(0x01 << 6usize)) | (((val as u32) & 0x01) << 6usize);\n        }\n        #[doc = \"L1 Shallow Sleep enable\"]\n        #[inline(always)]\n        pub const fn l1ssen(&self) -> bool {\n            let val = (self.0 >> 7usize) & 0x01;\n            val != 0\n        }\n        #[doc = \"L1 Shallow Sleep enable\"]\n        #[inline(always)]\n        pub fn set_l1ssen(&mut self, val: bool) {\n            self.0 = (self.0 & !(0x01 << 7usize)) | (((val as u32) & 0x01) << 7usize);\n        }\n        #[doc = \"BESL threshold\"]\n        #[inline(always)]\n        pub const fn beslthrs(&self) -> u8 {\n            let val = (self.0 >> 8usize) & 0x0f;\n            val as u8\n        }\n        #[doc = \"BESL threshold\"]\n        #[inline(always)]\n        pub fn set_beslthrs(&mut self, val: u8) {\n            self.0 = (self.0 & !(0x0f << 8usize)) | (((val as u32) & 0x0f) << 8usize);\n        }\n        #[doc = \"L1 deep sleep enable\"]\n        #[inline(always)]\n        pub const fn l1dsen(&self) -> bool {\n            let val = (self.0 >> 12usize) & 0x01;\n            val != 0\n        }\n        #[doc = \"L1 deep sleep enable\"]\n        #[inline(always)]\n        pub fn set_l1dsen(&mut self, val: bool) {\n            self.0 = (self.0 & !(0x01 << 12usize)) | (((val as u32) & 0x01) << 12usize);\n        }\n        #[doc = \"LPM response\"]\n        #[inline(always)]\n        pub const fn lpmrst(&self) -> u8 {\n            let val = (self.0 >> 13usize) & 0x03;\n            val as u8\n        }\n        #[doc = \"LPM response\"]\n        #[inline(always)]\n        pub fn set_lpmrst(&mut self, val: u8) {\n            self.0 = (self.0 & !(0x03 << 13usize)) | (((val as u32) & 0x03) << 13usize);\n        }\n        #[doc = \"Port sleep status\"]\n        #[inline(always)]\n        pub const fn slpsts(&self) -> bool {\n            let val = (self.0 >> 15usize) & 0x01;\n            val != 0\n        }\n        #[doc = \"Port sleep status\"]\n        #[inline(always)]\n        pub fn set_slpsts(&mut self, val: bool) {\n            self.0 = (self.0 & !(0x01 << 15usize)) | (((val as u32) & 0x01) << 15usize);\n        }\n        #[doc = \"Sleep State Resume OK\"]\n        #[inline(always)]\n        pub const fn l1rsmok(&self) -> bool {\n            let val = (self.0 >> 16usize) & 0x01;\n            val != 0\n        }\n        #[doc = \"Sleep State Resume OK\"]\n        #[inline(always)]\n        pub fn set_l1rsmok(&mut self, val: bool) {\n            self.0 = (self.0 & !(0x01 << 16usize)) | (((val as u32) & 0x01) << 16usize);\n        }\n        #[doc = \"LPM Channel Index\"]\n        #[inline(always)]\n        pub const fn lpmchidx(&self) -> u8 {\n            let val = (self.0 >> 17usize) & 0x0f;\n            val as u8\n        }\n        #[doc = \"LPM Channel Index\"]\n        #[inline(always)]\n        pub fn set_lpmchidx(&mut self, val: u8) {\n            self.0 = (self.0 & !(0x0f << 17usize)) | (((val as u32) & 0x0f) << 17usize);\n        }\n        #[doc = \"LPM retry count\"]\n        #[inline(always)]\n        pub const fn lpmrcnt(&self) -> u8 {\n            let val = (self.0 >> 21usize) & 0x07;\n            val as u8\n        }\n        #[doc = \"LPM retry count\"]\n        #[inline(always)]\n        pub fn set_lpmrcnt(&mut self, val: u8) {\n            self.0 = (self.0 & !(0x07 << 21usize)) | (((val as u32) & 0x07) << 21usize);\n        }\n        #[doc = \"Send LPM transaction\"]\n        #[inline(always)]\n        pub const fn sndlpm(&self) -> bool {\n            let val = (self.0 >> 24usize) & 0x01;\n            val != 0\n        }\n        #[doc = \"Send LPM transaction\"]\n        #[inline(always)]\n        pub fn set_sndlpm(&mut self, val: bool) {\n            self.0 = (self.0 & !(0x01 << 24usize)) | (((val as u32) & 0x01) << 24usize);\n        }\n        #[doc = \"LPM retry count status\"]\n        #[inline(always)]\n        pub const fn lpmrcntsts(&self) -> u8 {\n            let val = (self.0 >> 25usize) & 0x07;\n            val as u8\n        }\n        #[doc = \"LPM retry count status\"]\n        #[inline(always)]\n        pub fn set_lpmrcntsts(&mut self, val: u8) {\n            self.0 = (self.0 & !(0x07 << 25usize)) | (((val as u32) & 0x07) << 25usize);\n        }\n        #[doc = \"Enable best effort service latency\"]\n        #[inline(always)]\n        pub const fn enbesl(&self) -> bool {\n            let val = (self.0 >> 28usize) & 0x01;\n            val != 0\n        }\n        #[doc = \"Enable best effort service latency\"]\n        #[inline(always)]\n        pub fn set_enbesl(&mut self, val: bool) {\n            self.0 = (self.0 & !(0x01 << 28usize)) | (((val as u32) & 0x01) << 28usize);\n        }\n    }\n    impl Default for Glpmcfg {\n        #[inline(always)]\n        fn default() -> Glpmcfg {\n            Glpmcfg(0)\n        }\n    }\n    #[doc = \"Control and status register\"]\n    #[repr(transparent)]\n    #[derive(Copy, Clone, Eq, PartialEq)]\n    pub struct Gotgctl(pub u32);\n    impl Gotgctl {\n        #[doc = \"Session request success\"]\n        #[inline(always)]\n        pub const fn srqscs(&self) -> bool {\n            let val = (self.0 >> 0usize) & 0x01;\n            val != 0\n        }\n        #[doc = \"Session request success\"]\n        #[inline(always)]\n        pub fn set_srqscs(&mut self, val: bool) {\n            self.0 = (self.0 & !(0x01 << 0usize)) | (((val as u32) & 0x01) << 0usize);\n        }\n        #[doc = \"Session request\"]\n        #[inline(always)]\n        pub const fn srq(&self) -> bool {\n            let val = (self.0 >> 1usize) & 0x01;\n            val != 0\n        }\n        #[doc = \"Session request\"]\n        #[inline(always)]\n        pub fn set_srq(&mut self, val: bool) {\n            self.0 = (self.0 & !(0x01 << 1usize)) | (((val as u32) & 0x01) << 1usize);\n        }\n        #[doc = \"VBUS valid override enable\"]\n        #[inline(always)]\n        pub const fn vbvaloen(&self) -> bool {\n            let val = (self.0 >> 2usize) & 0x01;\n            val != 0\n        }\n        #[doc = \"VBUS valid override enable\"]\n        #[inline(always)]\n        pub fn set_vbvaloen(&mut self, val: bool) {\n            self.0 = (self.0 & !(0x01 << 2usize)) | (((val as u32) & 0x01) << 2usize);\n        }\n        #[doc = \"VBUS valid override value\"]\n        #[inline(always)]\n        pub const fn vbvaloval(&self) -> bool {\n            let val = (self.0 >> 3usize) & 0x01;\n            val != 0\n        }\n        #[doc = \"VBUS valid override value\"]\n        #[inline(always)]\n        pub fn set_vbvaloval(&mut self, val: bool) {\n            self.0 = (self.0 & !(0x01 << 3usize)) | (((val as u32) & 0x01) << 3usize);\n        }\n        #[doc = \"A-peripheral session valid override enable\"]\n        #[inline(always)]\n        pub const fn avaloen(&self) -> bool {\n            let val = (self.0 >> 4usize) & 0x01;\n            val != 0\n        }\n        #[doc = \"A-peripheral session valid override enable\"]\n        #[inline(always)]\n        pub fn set_avaloen(&mut self, val: bool) {\n            self.0 = (self.0 & !(0x01 << 4usize)) | (((val as u32) & 0x01) << 4usize);\n        }\n        #[doc = \"A-peripheral session valid override value\"]\n        #[inline(always)]\n        pub const fn avaloval(&self) -> bool {\n            let val = (self.0 >> 5usize) & 0x01;\n            val != 0\n        }\n        #[doc = \"A-peripheral session valid override value\"]\n        #[inline(always)]\n        pub fn set_avaloval(&mut self, val: bool) {\n            self.0 = (self.0 & !(0x01 << 5usize)) | (((val as u32) & 0x01) << 5usize);\n        }\n        #[doc = \"B-peripheral session valid override enable\"]\n        #[inline(always)]\n        pub const fn bvaloen(&self) -> bool {\n            let val = (self.0 >> 6usize) & 0x01;\n            val != 0\n        }\n        #[doc = \"B-peripheral session valid override enable\"]\n        #[inline(always)]\n        pub fn set_bvaloen(&mut self, val: bool) {\n            self.0 = (self.0 & !(0x01 << 6usize)) | (((val as u32) & 0x01) << 6usize);\n        }\n        #[doc = \"B-peripheral session valid override value\"]\n        #[inline(always)]\n        pub const fn bvaloval(&self) -> bool {\n            let val = (self.0 >> 7usize) & 0x01;\n            val != 0\n        }\n        #[doc = \"B-peripheral session valid override value\"]\n        #[inline(always)]\n        pub fn set_bvaloval(&mut self, val: bool) {\n            self.0 = (self.0 & !(0x01 << 7usize)) | (((val as u32) & 0x01) << 7usize);\n        }\n        #[doc = \"Host negotiation success\"]\n        #[inline(always)]\n        pub const fn hngscs(&self) -> bool {\n            let val = (self.0 >> 8usize) & 0x01;\n            val != 0\n        }\n        #[doc = \"Host negotiation success\"]\n        #[inline(always)]\n        pub fn set_hngscs(&mut self, val: bool) {\n            self.0 = (self.0 & !(0x01 << 8usize)) | (((val as u32) & 0x01) << 8usize);\n        }\n        #[doc = \"HNP request\"]\n        #[inline(always)]\n        pub const fn hnprq(&self) -> bool {\n            let val = (self.0 >> 9usize) & 0x01;\n            val != 0\n        }\n        #[doc = \"HNP request\"]\n        #[inline(always)]\n        pub fn set_hnprq(&mut self, val: bool) {\n            self.0 = (self.0 & !(0x01 << 9usize)) | (((val as u32) & 0x01) << 9usize);\n        }\n        #[doc = \"Host set HNP enable\"]\n        #[inline(always)]\n        pub const fn hshnpen(&self) -> bool {\n            let val = (self.0 >> 10usize) & 0x01;\n            val != 0\n        }\n        #[doc = \"Host set HNP enable\"]\n        #[inline(always)]\n        pub fn set_hshnpen(&mut self, val: bool) {\n            self.0 = (self.0 & !(0x01 << 10usize)) | (((val as u32) & 0x01) << 10usize);\n        }\n        #[doc = \"Device HNP enabled\"]\n        #[inline(always)]\n        pub const fn dhnpen(&self) -> bool {\n            let val = (self.0 >> 11usize) & 0x01;\n            val != 0\n        }\n        #[doc = \"Device HNP enabled\"]\n        #[inline(always)]\n        pub fn set_dhnpen(&mut self, val: bool) {\n            self.0 = (self.0 & !(0x01 << 11usize)) | (((val as u32) & 0x01) << 11usize);\n        }\n        #[doc = \"Embedded host enable\"]\n        #[inline(always)]\n        pub const fn ehen(&self) -> bool {\n            let val = (self.0 >> 12usize) & 0x01;\n            val != 0\n        }\n        #[doc = \"Embedded host enable\"]\n        #[inline(always)]\n        pub fn set_ehen(&mut self, val: bool) {\n            self.0 = (self.0 & !(0x01 << 12usize)) | (((val as u32) & 0x01) << 12usize);\n        }\n        #[doc = \"Connector ID status\"]\n        #[inline(always)]\n        pub const fn cidsts(&self) -> bool {\n            let val = (self.0 >> 16usize) & 0x01;\n            val != 0\n        }\n        #[doc = \"Connector ID status\"]\n        #[inline(always)]\n        pub fn set_cidsts(&mut self, val: bool) {\n            self.0 = (self.0 & !(0x01 << 16usize)) | (((val as u32) & 0x01) << 16usize);\n        }\n        #[doc = \"Long/short debounce time\"]\n        #[inline(always)]\n        pub const fn dbct(&self) -> bool {\n            let val = (self.0 >> 17usize) & 0x01;\n            val != 0\n        }\n        #[doc = \"Long/short debounce time\"]\n        #[inline(always)]\n        pub fn set_dbct(&mut self, val: bool) {\n            self.0 = (self.0 & !(0x01 << 17usize)) | (((val as u32) & 0x01) << 17usize);\n        }\n        #[doc = \"A-session valid\"]\n        #[inline(always)]\n        pub const fn asvld(&self) -> bool {\n            let val = (self.0 >> 18usize) & 0x01;\n            val != 0\n        }\n        #[doc = \"A-session valid\"]\n        #[inline(always)]\n        pub fn set_asvld(&mut self, val: bool) {\n            self.0 = (self.0 & !(0x01 << 18usize)) | (((val as u32) & 0x01) << 18usize);\n        }\n        #[doc = \"B-session valid\"]\n        #[inline(always)]\n        pub const fn bsvld(&self) -> bool {\n            let val = (self.0 >> 19usize) & 0x01;\n            val != 0\n        }\n        #[doc = \"B-session valid\"]\n        #[inline(always)]\n        pub fn set_bsvld(&mut self, val: bool) {\n            self.0 = (self.0 & !(0x01 << 19usize)) | (((val as u32) & 0x01) << 19usize);\n        }\n    }\n    impl Default for Gotgctl {\n        #[inline(always)]\n        fn default() -> Gotgctl {\n            Gotgctl(0)\n        }\n    }\n    #[doc = \"Interrupt register\"]\n    #[repr(transparent)]\n    #[derive(Copy, Clone, Eq, PartialEq)]\n    pub struct Gotgint(pub u32);\n    impl Gotgint {\n        #[doc = \"Session end detected\"]\n        #[inline(always)]\n        pub const fn sedet(&self) -> bool {\n            let val = (self.0 >> 2usize) & 0x01;\n            val != 0\n        }\n        #[doc = \"Session end detected\"]\n        #[inline(always)]\n        pub fn set_sedet(&mut self, val: bool) {\n            self.0 = (self.0 & !(0x01 << 2usize)) | (((val as u32) & 0x01) << 2usize);\n        }\n        #[doc = \"Session request success status change\"]\n        #[inline(always)]\n        pub const fn srsschg(&self) -> bool {\n            let val = (self.0 >> 8usize) & 0x01;\n            val != 0\n        }\n        #[doc = \"Session request success status change\"]\n        #[inline(always)]\n        pub fn set_srsschg(&mut self, val: bool) {\n            self.0 = (self.0 & !(0x01 << 8usize)) | (((val as u32) & 0x01) << 8usize);\n        }\n        #[doc = \"Host negotiation success status change\"]\n        #[inline(always)]\n        pub const fn hnsschg(&self) -> bool {\n            let val = (self.0 >> 9usize) & 0x01;\n            val != 0\n        }\n        #[doc = \"Host negotiation success status change\"]\n        #[inline(always)]\n        pub fn set_hnsschg(&mut self, val: bool) {\n            self.0 = (self.0 & !(0x01 << 9usize)) | (((val as u32) & 0x01) << 9usize);\n        }\n        #[doc = \"Host negotiation detected\"]\n        #[inline(always)]\n        pub const fn hngdet(&self) -> bool {\n            let val = (self.0 >> 17usize) & 0x01;\n            val != 0\n        }\n        #[doc = \"Host negotiation detected\"]\n        #[inline(always)]\n        pub fn set_hngdet(&mut self, val: bool) {\n            self.0 = (self.0 & !(0x01 << 17usize)) | (((val as u32) & 0x01) << 17usize);\n        }\n        #[doc = \"A-device timeout change\"]\n        #[inline(always)]\n        pub const fn adtochg(&self) -> bool {\n            let val = (self.0 >> 18usize) & 0x01;\n            val != 0\n        }\n        #[doc = \"A-device timeout change\"]\n        #[inline(always)]\n        pub fn set_adtochg(&mut self, val: bool) {\n            self.0 = (self.0 & !(0x01 << 18usize)) | (((val as u32) & 0x01) << 18usize);\n        }\n        #[doc = \"Debounce done\"]\n        #[inline(always)]\n        pub const fn dbcdne(&self) -> bool {\n            let val = (self.0 >> 19usize) & 0x01;\n            val != 0\n        }\n        #[doc = \"Debounce done\"]\n        #[inline(always)]\n        pub fn set_dbcdne(&mut self, val: bool) {\n            self.0 = (self.0 & !(0x01 << 19usize)) | (((val as u32) & 0x01) << 19usize);\n        }\n        #[doc = \"ID input pin changed\"]\n        #[inline(always)]\n        pub const fn idchng(&self) -> bool {\n            let val = (self.0 >> 20usize) & 0x01;\n            val != 0\n        }\n        #[doc = \"ID input pin changed\"]\n        #[inline(always)]\n        pub fn set_idchng(&mut self, val: bool) {\n            self.0 = (self.0 & !(0x01 << 20usize)) | (((val as u32) & 0x01) << 20usize);\n        }\n    }\n    impl Default for Gotgint {\n        #[inline(always)]\n        fn default() -> Gotgint {\n            Gotgint(0)\n        }\n    }\n    #[doc = \"Reset register\"]\n    #[repr(transparent)]\n    #[derive(Copy, Clone, Eq, PartialEq)]\n    pub struct Grstctl(pub u32);\n    impl Grstctl {\n        #[doc = \"Core soft reset\"]\n        #[inline(always)]\n        pub const fn csrst(&self) -> bool {\n            let val = (self.0 >> 0usize) & 0x01;\n            val != 0\n        }\n        #[doc = \"Core soft reset\"]\n        #[inline(always)]\n        pub fn set_csrst(&mut self, val: bool) {\n            self.0 = (self.0 & !(0x01 << 0usize)) | (((val as u32) & 0x01) << 0usize);\n        }\n        #[doc = \"HCLK soft reset\"]\n        #[inline(always)]\n        pub const fn hsrst(&self) -> bool {\n            let val = (self.0 >> 1usize) & 0x01;\n            val != 0\n        }\n        #[doc = \"HCLK soft reset\"]\n        #[inline(always)]\n        pub fn set_hsrst(&mut self, val: bool) {\n            self.0 = (self.0 & !(0x01 << 1usize)) | (((val as u32) & 0x01) << 1usize);\n        }\n        #[doc = \"Host frame counter reset\"]\n        #[inline(always)]\n        pub const fn fcrst(&self) -> bool {\n            let val = (self.0 >> 2usize) & 0x01;\n            val != 0\n        }\n        #[doc = \"Host frame counter reset\"]\n        #[inline(always)]\n        pub fn set_fcrst(&mut self, val: bool) {\n            self.0 = (self.0 & !(0x01 << 2usize)) | (((val as u32) & 0x01) << 2usize);\n        }\n        #[doc = \"RxFIFO flush\"]\n        #[inline(always)]\n        pub const fn rxfflsh(&self) -> bool {\n            let val = (self.0 >> 4usize) & 0x01;\n            val != 0\n        }\n        #[doc = \"RxFIFO flush\"]\n        #[inline(always)]\n        pub fn set_rxfflsh(&mut self, val: bool) {\n            self.0 = (self.0 & !(0x01 << 4usize)) | (((val as u32) & 0x01) << 4usize);\n        }\n        #[doc = \"TxFIFO flush\"]\n        #[inline(always)]\n        pub const fn txfflsh(&self) -> bool {\n            let val = (self.0 >> 5usize) & 0x01;\n            val != 0\n        }\n        #[doc = \"TxFIFO flush\"]\n        #[inline(always)]\n        pub fn set_txfflsh(&mut self, val: bool) {\n            self.0 = (self.0 & !(0x01 << 5usize)) | (((val as u32) & 0x01) << 5usize);\n        }\n        #[doc = \"TxFIFO number\"]\n        #[inline(always)]\n        pub const fn txfnum(&self) -> u8 {\n            let val = (self.0 >> 6usize) & 0x1f;\n            val as u8\n        }\n        #[doc = \"TxFIFO number\"]\n        #[inline(always)]\n        pub fn set_txfnum(&mut self, val: u8) {\n            self.0 = (self.0 & !(0x1f << 6usize)) | (((val as u32) & 0x1f) << 6usize);\n        }\n        #[doc = \"DMA request signal enabled for USB OTG HS\"]\n        #[inline(always)]\n        pub const fn dmareq(&self) -> bool {\n            let val = (self.0 >> 30usize) & 0x01;\n            val != 0\n        }\n        #[doc = \"DMA request signal enabled for USB OTG HS\"]\n        #[inline(always)]\n        pub fn set_dmareq(&mut self, val: bool) {\n            self.0 = (self.0 & !(0x01 << 30usize)) | (((val as u32) & 0x01) << 30usize);\n        }\n        #[doc = \"AHB master idle\"]\n        #[inline(always)]\n        pub const fn ahbidl(&self) -> bool {\n            let val = (self.0 >> 31usize) & 0x01;\n            val != 0\n        }\n        #[doc = \"AHB master idle\"]\n        #[inline(always)]\n        pub fn set_ahbidl(&mut self, val: bool) {\n            self.0 = (self.0 & !(0x01 << 31usize)) | (((val as u32) & 0x01) << 31usize);\n        }\n    }\n    impl Default for Grstctl {\n        #[inline(always)]\n        fn default() -> Grstctl {\n            Grstctl(0)\n        }\n    }\n    #[doc = \"Receive FIFO size register\"]\n    #[repr(transparent)]\n    #[derive(Copy, Clone, Eq, PartialEq)]\n    pub struct Grxfsiz(pub u32);\n    impl Grxfsiz {\n        #[doc = \"RxFIFO depth\"]\n        #[inline(always)]\n        pub const fn rxfd(&self) -> u16 {\n            let val = (self.0 >> 0usize) & 0xffff;\n            val as u16\n        }\n        #[doc = \"RxFIFO depth\"]\n        #[inline(always)]\n        pub fn set_rxfd(&mut self, val: u16) {\n            self.0 = (self.0 & !(0xffff << 0usize)) | (((val as u32) & 0xffff) << 0usize);\n        }\n    }\n    impl Default for Grxfsiz {\n        #[inline(always)]\n        fn default() -> Grxfsiz {\n            Grxfsiz(0)\n        }\n    }\n    #[doc = \"Status read and pop register\"]\n    #[repr(transparent)]\n    #[derive(Copy, Clone, Eq, PartialEq)]\n    pub struct Grxsts(pub u32);\n    impl Grxsts {\n        #[doc = \"Endpoint number (device mode) / Channel number (host mode)\"]\n        #[inline(always)]\n        pub const fn epnum(&self) -> u8 {\n            let val = (self.0 >> 0usize) & 0x0f;\n            val as u8\n        }\n        #[doc = \"Endpoint number (device mode) / Channel number (host mode)\"]\n        #[inline(always)]\n        pub fn set_epnum(&mut self, val: u8) {\n            self.0 = (self.0 & !(0x0f << 0usize)) | (((val as u32) & 0x0f) << 0usize);\n        }\n        #[doc = \"Byte count\"]\n        #[inline(always)]\n        pub const fn bcnt(&self) -> u16 {\n            let val = (self.0 >> 4usize) & 0x07ff;\n            val as u16\n        }\n        #[doc = \"Byte count\"]\n        #[inline(always)]\n        pub fn set_bcnt(&mut self, val: u16) {\n            self.0 = (self.0 & !(0x07ff << 4usize)) | (((val as u32) & 0x07ff) << 4usize);\n        }\n        #[doc = \"Data PID\"]\n        #[inline(always)]\n        pub const fn dpid(&self) -> super::vals::Dpid {\n            let val = (self.0 >> 15usize) & 0x03;\n            super::vals::Dpid::from_bits(val as u8)\n        }\n        #[doc = \"Data PID\"]\n        #[inline(always)]\n        pub fn set_dpid(&mut self, val: super::vals::Dpid) {\n            self.0 = (self.0 & !(0x03 << 15usize)) | (((val.to_bits() as u32) & 0x03) << 15usize);\n        }\n        #[doc = \"Packet status (device mode)\"]\n        #[inline(always)]\n        pub const fn pktstsd(&self) -> super::vals::Pktstsd {\n            let val = (self.0 >> 17usize) & 0x0f;\n            super::vals::Pktstsd::from_bits(val as u8)\n        }\n        #[doc = \"Packet status (device mode)\"]\n        #[inline(always)]\n        pub fn set_pktstsd(&mut self, val: super::vals::Pktstsd) {\n            self.0 = (self.0 & !(0x0f << 17usize)) | (((val.to_bits() as u32) & 0x0f) << 17usize);\n        }\n        #[doc = \"Packet status (host mode)\"]\n        #[inline(always)]\n        pub const fn pktstsh(&self) -> super::vals::Pktstsh {\n            let val = (self.0 >> 17usize) & 0x0f;\n            super::vals::Pktstsh::from_bits(val as u8)\n        }\n        #[doc = \"Packet status (host mode)\"]\n        #[inline(always)]\n        pub fn set_pktstsh(&mut self, val: super::vals::Pktstsh) {\n            self.0 = (self.0 & !(0x0f << 17usize)) | (((val.to_bits() as u32) & 0x0f) << 17usize);\n        }\n        #[doc = \"Frame number (device mode)\"]\n        #[inline(always)]\n        pub const fn frmnum(&self) -> u8 {\n            let val = (self.0 >> 21usize) & 0x0f;\n            val as u8\n        }\n        #[doc = \"Frame number (device mode)\"]\n        #[inline(always)]\n        pub fn set_frmnum(&mut self, val: u8) {\n            self.0 = (self.0 & !(0x0f << 21usize)) | (((val as u32) & 0x0f) << 21usize);\n        }\n    }\n    impl Default for Grxsts {\n        #[inline(always)]\n        fn default() -> Grxsts {\n            Grxsts(0)\n        }\n    }\n    #[doc = \"USB configuration register\"]\n    #[repr(transparent)]\n    #[derive(Copy, Clone, Eq, PartialEq)]\n    pub struct Gusbcfg(pub u32);\n    impl Gusbcfg {\n        #[doc = \"FS timeout calibration\"]\n        #[inline(always)]\n        pub const fn tocal(&self) -> u8 {\n            let val = (self.0 >> 0usize) & 0x07;\n            val as u8\n        }\n        #[doc = \"FS timeout calibration\"]\n        #[inline(always)]\n        pub fn set_tocal(&mut self, val: u8) {\n            self.0 = (self.0 & !(0x07 << 0usize)) | (((val as u32) & 0x07) << 0usize);\n        }\n        #[doc = \"Full-speed internal serial transceiver enable\"]\n        #[inline(always)]\n        pub const fn physel(&self) -> bool {\n            let val = (self.0 >> 6usize) & 0x01;\n            val != 0\n        }\n        #[doc = \"Full-speed internal serial transceiver enable\"]\n        #[inline(always)]\n        pub fn set_physel(&mut self, val: bool) {\n            self.0 = (self.0 & !(0x01 << 6usize)) | (((val as u32) & 0x01) << 6usize);\n        }\n        #[doc = \"SRP-capable\"]\n        #[inline(always)]\n        pub const fn srpcap(&self) -> bool {\n            let val = (self.0 >> 8usize) & 0x01;\n            val != 0\n        }\n        #[doc = \"SRP-capable\"]\n        #[inline(always)]\n        pub fn set_srpcap(&mut self, val: bool) {\n            self.0 = (self.0 & !(0x01 << 8usize)) | (((val as u32) & 0x01) << 8usize);\n        }\n        #[doc = \"HNP-capable\"]\n        #[inline(always)]\n        pub const fn hnpcap(&self) -> bool {\n            let val = (self.0 >> 9usize) & 0x01;\n            val != 0\n        }\n        #[doc = \"HNP-capable\"]\n        #[inline(always)]\n        pub fn set_hnpcap(&mut self, val: bool) {\n            self.0 = (self.0 & !(0x01 << 9usize)) | (((val as u32) & 0x01) << 9usize);\n        }\n        #[doc = \"USB turnaround time\"]\n        #[inline(always)]\n        pub const fn trdt(&self) -> u8 {\n            let val = (self.0 >> 10usize) & 0x0f;\n            val as u8\n        }\n        #[doc = \"USB turnaround time\"]\n        #[inline(always)]\n        pub fn set_trdt(&mut self, val: u8) {\n            self.0 = (self.0 & !(0x0f << 10usize)) | (((val as u32) & 0x0f) << 10usize);\n        }\n        #[doc = \"PHY Low-power clock select\"]\n        #[inline(always)]\n        pub const fn phylpcs(&self) -> bool {\n            let val = (self.0 >> 15usize) & 0x01;\n            val != 0\n        }\n        #[doc = \"PHY Low-power clock select\"]\n        #[inline(always)]\n        pub fn set_phylpcs(&mut self, val: bool) {\n            self.0 = (self.0 & !(0x01 << 15usize)) | (((val as u32) & 0x01) << 15usize);\n        }\n        #[doc = \"ULPI FS/LS select\"]\n        #[inline(always)]\n        pub const fn ulpifsls(&self) -> bool {\n            let val = (self.0 >> 17usize) & 0x01;\n            val != 0\n        }\n        #[doc = \"ULPI FS/LS select\"]\n        #[inline(always)]\n        pub fn set_ulpifsls(&mut self, val: bool) {\n            self.0 = (self.0 & !(0x01 << 17usize)) | (((val as u32) & 0x01) << 17usize);\n        }\n        #[doc = \"ULPI Auto-resume\"]\n        #[inline(always)]\n        pub const fn ulpiar(&self) -> bool {\n            let val = (self.0 >> 18usize) & 0x01;\n            val != 0\n        }\n        #[doc = \"ULPI Auto-resume\"]\n        #[inline(always)]\n        pub fn set_ulpiar(&mut self, val: bool) {\n            self.0 = (self.0 & !(0x01 << 18usize)) | (((val as u32) & 0x01) << 18usize);\n        }\n        #[doc = \"ULPI Clock SuspendM\"]\n        #[inline(always)]\n        pub const fn ulpicsm(&self) -> bool {\n            let val = (self.0 >> 19usize) & 0x01;\n            val != 0\n        }\n        #[doc = \"ULPI Clock SuspendM\"]\n        #[inline(always)]\n        pub fn set_ulpicsm(&mut self, val: bool) {\n            self.0 = (self.0 & !(0x01 << 19usize)) | (((val as u32) & 0x01) << 19usize);\n        }\n        #[doc = \"ULPI External VBUS Drive\"]\n        #[inline(always)]\n        pub const fn ulpievbusd(&self) -> bool {\n            let val = (self.0 >> 20usize) & 0x01;\n            val != 0\n        }\n        #[doc = \"ULPI External VBUS Drive\"]\n        #[inline(always)]\n        pub fn set_ulpievbusd(&mut self, val: bool) {\n            self.0 = (self.0 & !(0x01 << 20usize)) | (((val as u32) & 0x01) << 20usize);\n        }\n        #[doc = \"ULPI external VBUS indicator\"]\n        #[inline(always)]\n        pub const fn ulpievbusi(&self) -> bool {\n            let val = (self.0 >> 21usize) & 0x01;\n            val != 0\n        }\n        #[doc = \"ULPI external VBUS indicator\"]\n        #[inline(always)]\n        pub fn set_ulpievbusi(&mut self, val: bool) {\n            self.0 = (self.0 & !(0x01 << 21usize)) | (((val as u32) & 0x01) << 21usize);\n        }\n        #[doc = \"TermSel DLine pulsing selection\"]\n        #[inline(always)]\n        pub const fn tsdps(&self) -> bool {\n            let val = (self.0 >> 22usize) & 0x01;\n            val != 0\n        }\n        #[doc = \"TermSel DLine pulsing selection\"]\n        #[inline(always)]\n        pub fn set_tsdps(&mut self, val: bool) {\n            self.0 = (self.0 & !(0x01 << 22usize)) | (((val as u32) & 0x01) << 22usize);\n        }\n        #[doc = \"Indicator complement\"]\n        #[inline(always)]\n        pub const fn pcci(&self) -> bool {\n            let val = (self.0 >> 23usize) & 0x01;\n            val != 0\n        }\n        #[doc = \"Indicator complement\"]\n        #[inline(always)]\n        pub fn set_pcci(&mut self, val: bool) {\n            self.0 = (self.0 & !(0x01 << 23usize)) | (((val as u32) & 0x01) << 23usize);\n        }\n        #[doc = \"Indicator pass through\"]\n        #[inline(always)]\n        pub const fn ptci(&self) -> bool {\n            let val = (self.0 >> 24usize) & 0x01;\n            val != 0\n        }\n        #[doc = \"Indicator pass through\"]\n        #[inline(always)]\n        pub fn set_ptci(&mut self, val: bool) {\n            self.0 = (self.0 & !(0x01 << 24usize)) | (((val as u32) & 0x01) << 24usize);\n        }\n        #[doc = \"ULPI interface protect disable\"]\n        #[inline(always)]\n        pub const fn ulpiipd(&self) -> bool {\n            let val = (self.0 >> 25usize) & 0x01;\n            val != 0\n        }\n        #[doc = \"ULPI interface protect disable\"]\n        #[inline(always)]\n        pub fn set_ulpiipd(&mut self, val: bool) {\n            self.0 = (self.0 & !(0x01 << 25usize)) | (((val as u32) & 0x01) << 25usize);\n        }\n        #[doc = \"Force host mode\"]\n        #[inline(always)]\n        pub const fn fhmod(&self) -> bool {\n            let val = (self.0 >> 29usize) & 0x01;\n            val != 0\n        }\n        #[doc = \"Force host mode\"]\n        #[inline(always)]\n        pub fn set_fhmod(&mut self, val: bool) {\n            self.0 = (self.0 & !(0x01 << 29usize)) | (((val as u32) & 0x01) << 29usize);\n        }\n        #[doc = \"Force device mode\"]\n        #[inline(always)]\n        pub const fn fdmod(&self) -> bool {\n            let val = (self.0 >> 30usize) & 0x01;\n            val != 0\n        }\n        #[doc = \"Force device mode\"]\n        #[inline(always)]\n        pub fn set_fdmod(&mut self, val: bool) {\n            self.0 = (self.0 & !(0x01 << 30usize)) | (((val as u32) & 0x01) << 30usize);\n        }\n        #[doc = \"Corrupt Tx packet\"]\n        #[inline(always)]\n        pub const fn ctxpkt(&self) -> bool {\n            let val = (self.0 >> 31usize) & 0x01;\n            val != 0\n        }\n        #[doc = \"Corrupt Tx packet\"]\n        #[inline(always)]\n        pub fn set_ctxpkt(&mut self, val: bool) {\n            self.0 = (self.0 & !(0x01 << 31usize)) | (((val as u32) & 0x01) << 31usize);\n        }\n    }\n    impl Default for Gusbcfg {\n        #[inline(always)]\n        fn default() -> Gusbcfg {\n            Gusbcfg(0)\n        }\n    }\n    #[doc = \"Host all channels interrupt register\"]\n    #[repr(transparent)]\n    #[derive(Copy, Clone, Eq, PartialEq)]\n    pub struct Haint(pub u32);\n    impl Haint {\n        #[doc = \"Channel interrupts\"]\n        #[inline(always)]\n        pub const fn haint(&self) -> u16 {\n            let val = (self.0 >> 0usize) & 0xffff;\n            val as u16\n        }\n        #[doc = \"Channel interrupts\"]\n        #[inline(always)]\n        pub fn set_haint(&mut self, val: u16) {\n            self.0 = (self.0 & !(0xffff << 0usize)) | (((val as u32) & 0xffff) << 0usize);\n        }\n    }\n    impl Default for Haint {\n        #[inline(always)]\n        fn default() -> Haint {\n            Haint(0)\n        }\n    }\n    #[doc = \"Host all channels interrupt mask register\"]\n    #[repr(transparent)]\n    #[derive(Copy, Clone, Eq, PartialEq)]\n    pub struct Haintmsk(pub u32);\n    impl Haintmsk {\n        #[doc = \"Channel interrupt mask\"]\n        #[inline(always)]\n        pub const fn haintm(&self) -> u16 {\n            let val = (self.0 >> 0usize) & 0xffff;\n            val as u16\n        }\n        #[doc = \"Channel interrupt mask\"]\n        #[inline(always)]\n        pub fn set_haintm(&mut self, val: u16) {\n            self.0 = (self.0 & !(0xffff << 0usize)) | (((val as u32) & 0xffff) << 0usize);\n        }\n    }\n    impl Default for Haintmsk {\n        #[inline(always)]\n        fn default() -> Haintmsk {\n            Haintmsk(0)\n        }\n    }\n    #[doc = \"Host channel characteristics register\"]\n    #[repr(transparent)]\n    #[derive(Copy, Clone, Eq, PartialEq)]\n    pub struct Hcchar(pub u32);\n    impl Hcchar {\n        #[doc = \"Maximum packet size\"]\n        #[inline(always)]\n        pub const fn mpsiz(&self) -> u16 {\n            let val = (self.0 >> 0usize) & 0x07ff;\n            val as u16\n        }\n        #[doc = \"Maximum packet size\"]\n        #[inline(always)]\n        pub fn set_mpsiz(&mut self, val: u16) {\n            self.0 = (self.0 & !(0x07ff << 0usize)) | (((val as u32) & 0x07ff) << 0usize);\n        }\n        #[doc = \"Endpoint number\"]\n        #[inline(always)]\n        pub const fn epnum(&self) -> u8 {\n            let val = (self.0 >> 11usize) & 0x0f;\n            val as u8\n        }\n        #[doc = \"Endpoint number\"]\n        #[inline(always)]\n        pub fn set_epnum(&mut self, val: u8) {\n            self.0 = (self.0 & !(0x0f << 11usize)) | (((val as u32) & 0x0f) << 11usize);\n        }\n        #[doc = \"Endpoint direction\"]\n        #[inline(always)]\n        pub const fn epdir(&self) -> bool {\n            let val = (self.0 >> 15usize) & 0x01;\n            val != 0\n        }\n        #[doc = \"Endpoint direction\"]\n        #[inline(always)]\n        pub fn set_epdir(&mut self, val: bool) {\n            self.0 = (self.0 & !(0x01 << 15usize)) | (((val as u32) & 0x01) << 15usize);\n        }\n        #[doc = \"Low-speed device\"]\n        #[inline(always)]\n        pub const fn lsdev(&self) -> bool {\n            let val = (self.0 >> 17usize) & 0x01;\n            val != 0\n        }\n        #[doc = \"Low-speed device\"]\n        #[inline(always)]\n        pub fn set_lsdev(&mut self, val: bool) {\n            self.0 = (self.0 & !(0x01 << 17usize)) | (((val as u32) & 0x01) << 17usize);\n        }\n        #[doc = \"Endpoint type\"]\n        #[inline(always)]\n        pub const fn eptyp(&self) -> super::vals::Eptyp {\n            let val = (self.0 >> 18usize) & 0x03;\n            super::vals::Eptyp::from_bits(val as u8)\n        }\n        #[doc = \"Endpoint type\"]\n        #[inline(always)]\n        pub fn set_eptyp(&mut self, val: super::vals::Eptyp) {\n            self.0 = (self.0 & !(0x03 << 18usize)) | (((val.to_bits() as u32) & 0x03) << 18usize);\n        }\n        #[doc = \"Multicount\"]\n        #[inline(always)]\n        pub const fn mcnt(&self) -> u8 {\n            let val = (self.0 >> 20usize) & 0x03;\n            val as u8\n        }\n        #[doc = \"Multicount\"]\n        #[inline(always)]\n        pub fn set_mcnt(&mut self, val: u8) {\n            self.0 = (self.0 & !(0x03 << 20usize)) | (((val as u32) & 0x03) << 20usize);\n        }\n        #[doc = \"Device address\"]\n        #[inline(always)]\n        pub const fn dad(&self) -> u8 {\n            let val = (self.0 >> 22usize) & 0x7f;\n            val as u8\n        }\n        #[doc = \"Device address\"]\n        #[inline(always)]\n        pub fn set_dad(&mut self, val: u8) {\n            self.0 = (self.0 & !(0x7f << 22usize)) | (((val as u32) & 0x7f) << 22usize);\n        }\n        #[doc = \"Odd frame\"]\n        #[inline(always)]\n        pub const fn oddfrm(&self) -> bool {\n            let val = (self.0 >> 29usize) & 0x01;\n            val != 0\n        }\n        #[doc = \"Odd frame\"]\n        #[inline(always)]\n        pub fn set_oddfrm(&mut self, val: bool) {\n            self.0 = (self.0 & !(0x01 << 29usize)) | (((val as u32) & 0x01) << 29usize);\n        }\n        #[doc = \"Channel disable\"]\n        #[inline(always)]\n        pub const fn chdis(&self) -> bool {\n            let val = (self.0 >> 30usize) & 0x01;\n            val != 0\n        }\n        #[doc = \"Channel disable\"]\n        #[inline(always)]\n        pub fn set_chdis(&mut self, val: bool) {\n            self.0 = (self.0 & !(0x01 << 30usize)) | (((val as u32) & 0x01) << 30usize);\n        }\n        #[doc = \"Channel enable\"]\n        #[inline(always)]\n        pub const fn chena(&self) -> bool {\n            let val = (self.0 >> 31usize) & 0x01;\n            val != 0\n        }\n        #[doc = \"Channel enable\"]\n        #[inline(always)]\n        pub fn set_chena(&mut self, val: bool) {\n            self.0 = (self.0 & !(0x01 << 31usize)) | (((val as u32) & 0x01) << 31usize);\n        }\n    }\n    impl Default for Hcchar {\n        #[inline(always)]\n        fn default() -> Hcchar {\n            Hcchar(0)\n        }\n    }\n    #[doc = \"Host configuration register\"]\n    #[repr(transparent)]\n    #[derive(Copy, Clone, Eq, PartialEq)]\n    pub struct Hcfg(pub u32);\n    impl Hcfg {\n        #[doc = \"FS/LS PHY clock select\"]\n        #[inline(always)]\n        pub const fn fslspcs(&self) -> u8 {\n            let val = (self.0 >> 0usize) & 0x03;\n            val as u8\n        }\n        #[doc = \"FS/LS PHY clock select\"]\n        #[inline(always)]\n        pub fn set_fslspcs(&mut self, val: u8) {\n            self.0 = (self.0 & !(0x03 << 0usize)) | (((val as u32) & 0x03) << 0usize);\n        }\n        #[doc = \"FS- and LS-only support\"]\n        #[inline(always)]\n        pub const fn fslss(&self) -> bool {\n            let val = (self.0 >> 2usize) & 0x01;\n            val != 0\n        }\n        #[doc = \"FS- and LS-only support\"]\n        #[inline(always)]\n        pub fn set_fslss(&mut self, val: bool) {\n            self.0 = (self.0 & !(0x01 << 2usize)) | (((val as u32) & 0x01) << 2usize);\n        }\n    }\n    impl Default for Hcfg {\n        #[inline(always)]\n        fn default() -> Hcfg {\n            Hcfg(0)\n        }\n    }\n    #[doc = \"Host channel interrupt register\"]\n    #[repr(transparent)]\n    #[derive(Copy, Clone, Eq, PartialEq)]\n    pub struct Hcint(pub u32);\n    impl Hcint {\n        #[doc = \"Transfer completed\"]\n        #[inline(always)]\n        pub const fn xfrc(&self) -> bool {\n            let val = (self.0 >> 0usize) & 0x01;\n            val != 0\n        }\n        #[doc = \"Transfer completed\"]\n        #[inline(always)]\n        pub fn set_xfrc(&mut self, val: bool) {\n            self.0 = (self.0 & !(0x01 << 0usize)) | (((val as u32) & 0x01) << 0usize);\n        }\n        #[doc = \"Channel halted\"]\n        #[inline(always)]\n        pub const fn chh(&self) -> bool {\n            let val = (self.0 >> 1usize) & 0x01;\n            val != 0\n        }\n        #[doc = \"Channel halted\"]\n        #[inline(always)]\n        pub fn set_chh(&mut self, val: bool) {\n            self.0 = (self.0 & !(0x01 << 1usize)) | (((val as u32) & 0x01) << 1usize);\n        }\n        #[doc = \"STALL response received interrupt\"]\n        #[inline(always)]\n        pub const fn stall(&self) -> bool {\n            let val = (self.0 >> 3usize) & 0x01;\n            val != 0\n        }\n        #[doc = \"STALL response received interrupt\"]\n        #[inline(always)]\n        pub fn set_stall(&mut self, val: bool) {\n            self.0 = (self.0 & !(0x01 << 3usize)) | (((val as u32) & 0x01) << 3usize);\n        }\n        #[doc = \"NAK response received interrupt\"]\n        #[inline(always)]\n        pub const fn nak(&self) -> bool {\n            let val = (self.0 >> 4usize) & 0x01;\n            val != 0\n        }\n        #[doc = \"NAK response received interrupt\"]\n        #[inline(always)]\n        pub fn set_nak(&mut self, val: bool) {\n            self.0 = (self.0 & !(0x01 << 4usize)) | (((val as u32) & 0x01) << 4usize);\n        }\n        #[doc = \"ACK response received/transmitted interrupt\"]\n        #[inline(always)]\n        pub const fn ack(&self) -> bool {\n            let val = (self.0 >> 5usize) & 0x01;\n            val != 0\n        }\n        #[doc = \"ACK response received/transmitted interrupt\"]\n        #[inline(always)]\n        pub fn set_ack(&mut self, val: bool) {\n            self.0 = (self.0 & !(0x01 << 5usize)) | (((val as u32) & 0x01) << 5usize);\n        }\n        #[doc = \"Transaction error\"]\n        #[inline(always)]\n        pub const fn txerr(&self) -> bool {\n            let val = (self.0 >> 7usize) & 0x01;\n            val != 0\n        }\n        #[doc = \"Transaction error\"]\n        #[inline(always)]\n        pub fn set_txerr(&mut self, val: bool) {\n            self.0 = (self.0 & !(0x01 << 7usize)) | (((val as u32) & 0x01) << 7usize);\n        }\n        #[doc = \"Babble error\"]\n        #[inline(always)]\n        pub const fn bberr(&self) -> bool {\n            let val = (self.0 >> 8usize) & 0x01;\n            val != 0\n        }\n        #[doc = \"Babble error\"]\n        #[inline(always)]\n        pub fn set_bberr(&mut self, val: bool) {\n            self.0 = (self.0 & !(0x01 << 8usize)) | (((val as u32) & 0x01) << 8usize);\n        }\n        #[doc = \"Frame overrun\"]\n        #[inline(always)]\n        pub const fn frmor(&self) -> bool {\n            let val = (self.0 >> 9usize) & 0x01;\n            val != 0\n        }\n        #[doc = \"Frame overrun\"]\n        #[inline(always)]\n        pub fn set_frmor(&mut self, val: bool) {\n            self.0 = (self.0 & !(0x01 << 9usize)) | (((val as u32) & 0x01) << 9usize);\n        }\n        #[doc = \"Data toggle error\"]\n        #[inline(always)]\n        pub const fn dterr(&self) -> bool {\n            let val = (self.0 >> 10usize) & 0x01;\n            val != 0\n        }\n        #[doc = \"Data toggle error\"]\n        #[inline(always)]\n        pub fn set_dterr(&mut self, val: bool) {\n            self.0 = (self.0 & !(0x01 << 10usize)) | (((val as u32) & 0x01) << 10usize);\n        }\n    }\n    impl Default for Hcint {\n        #[inline(always)]\n        fn default() -> Hcint {\n            Hcint(0)\n        }\n    }\n    #[doc = \"Host channel mask register\"]\n    #[repr(transparent)]\n    #[derive(Copy, Clone, Eq, PartialEq)]\n    pub struct Hcintmsk(pub u32);\n    impl Hcintmsk {\n        #[doc = \"Transfer completed mask\"]\n        #[inline(always)]\n        pub const fn xfrcm(&self) -> bool {\n            let val = (self.0 >> 0usize) & 0x01;\n            val != 0\n        }\n        #[doc = \"Transfer completed mask\"]\n        #[inline(always)]\n        pub fn set_xfrcm(&mut self, val: bool) {\n            self.0 = (self.0 & !(0x01 << 0usize)) | (((val as u32) & 0x01) << 0usize);\n        }\n        #[doc = \"Channel halted mask\"]\n        #[inline(always)]\n        pub const fn chhm(&self) -> bool {\n            let val = (self.0 >> 1usize) & 0x01;\n            val != 0\n        }\n        #[doc = \"Channel halted mask\"]\n        #[inline(always)]\n        pub fn set_chhm(&mut self, val: bool) {\n            self.0 = (self.0 & !(0x01 << 1usize)) | (((val as u32) & 0x01) << 1usize);\n        }\n        #[doc = \"STALL response received interrupt mask\"]\n        #[inline(always)]\n        pub const fn stallm(&self) -> bool {\n            let val = (self.0 >> 3usize) & 0x01;\n            val != 0\n        }\n        #[doc = \"STALL response received interrupt mask\"]\n        #[inline(always)]\n        pub fn set_stallm(&mut self, val: bool) {\n            self.0 = (self.0 & !(0x01 << 3usize)) | (((val as u32) & 0x01) << 3usize);\n        }\n        #[doc = \"NAK response received interrupt mask\"]\n        #[inline(always)]\n        pub const fn nakm(&self) -> bool {\n            let val = (self.0 >> 4usize) & 0x01;\n            val != 0\n        }\n        #[doc = \"NAK response received interrupt mask\"]\n        #[inline(always)]\n        pub fn set_nakm(&mut self, val: bool) {\n            self.0 = (self.0 & !(0x01 << 4usize)) | (((val as u32) & 0x01) << 4usize);\n        }\n        #[doc = \"ACK response received/transmitted interrupt mask\"]\n        #[inline(always)]\n        pub const fn ackm(&self) -> bool {\n            let val = (self.0 >> 5usize) & 0x01;\n            val != 0\n        }\n        #[doc = \"ACK response received/transmitted interrupt mask\"]\n        #[inline(always)]\n        pub fn set_ackm(&mut self, val: bool) {\n            self.0 = (self.0 & !(0x01 << 5usize)) | (((val as u32) & 0x01) << 5usize);\n        }\n        #[doc = \"Response received interrupt mask\"]\n        #[inline(always)]\n        pub const fn nyet(&self) -> bool {\n            let val = (self.0 >> 6usize) & 0x01;\n            val != 0\n        }\n        #[doc = \"Response received interrupt mask\"]\n        #[inline(always)]\n        pub fn set_nyet(&mut self, val: bool) {\n            self.0 = (self.0 & !(0x01 << 6usize)) | (((val as u32) & 0x01) << 6usize);\n        }\n        #[doc = \"Transaction error mask\"]\n        #[inline(always)]\n        pub const fn txerrm(&self) -> bool {\n            let val = (self.0 >> 7usize) & 0x01;\n            val != 0\n        }\n        #[doc = \"Transaction error mask\"]\n        #[inline(always)]\n        pub fn set_txerrm(&mut self, val: bool) {\n            self.0 = (self.0 & !(0x01 << 7usize)) | (((val as u32) & 0x01) << 7usize);\n        }\n        #[doc = \"Babble error mask\"]\n        #[inline(always)]\n        pub const fn bberrm(&self) -> bool {\n            let val = (self.0 >> 8usize) & 0x01;\n            val != 0\n        }\n        #[doc = \"Babble error mask\"]\n        #[inline(always)]\n        pub fn set_bberrm(&mut self, val: bool) {\n            self.0 = (self.0 & !(0x01 << 8usize)) | (((val as u32) & 0x01) << 8usize);\n        }\n        #[doc = \"Frame overrun mask\"]\n        #[inline(always)]\n        pub const fn frmorm(&self) -> bool {\n            let val = (self.0 >> 9usize) & 0x01;\n            val != 0\n        }\n        #[doc = \"Frame overrun mask\"]\n        #[inline(always)]\n        pub fn set_frmorm(&mut self, val: bool) {\n            self.0 = (self.0 & !(0x01 << 9usize)) | (((val as u32) & 0x01) << 9usize);\n        }\n        #[doc = \"Data toggle error mask\"]\n        #[inline(always)]\n        pub const fn dterrm(&self) -> bool {\n            let val = (self.0 >> 10usize) & 0x01;\n            val != 0\n        }\n        #[doc = \"Data toggle error mask\"]\n        #[inline(always)]\n        pub fn set_dterrm(&mut self, val: bool) {\n            self.0 = (self.0 & !(0x01 << 10usize)) | (((val as u32) & 0x01) << 10usize);\n        }\n    }\n    impl Default for Hcintmsk {\n        #[inline(always)]\n        fn default() -> Hcintmsk {\n            Hcintmsk(0)\n        }\n    }\n    #[doc = \"Host channel transfer size register\"]\n    #[repr(transparent)]\n    #[derive(Copy, Clone, Eq, PartialEq)]\n    pub struct Hctsiz(pub u32);\n    impl Hctsiz {\n        #[doc = \"Transfer size\"]\n        #[inline(always)]\n        pub const fn xfrsiz(&self) -> u32 {\n            let val = (self.0 >> 0usize) & 0x0007_ffff;\n            val as u32\n        }\n        #[doc = \"Transfer size\"]\n        #[inline(always)]\n        pub fn set_xfrsiz(&mut self, val: u32) {\n            self.0 = (self.0 & !(0x0007_ffff << 0usize)) | (((val as u32) & 0x0007_ffff) << 0usize);\n        }\n        #[doc = \"Packet count\"]\n        #[inline(always)]\n        pub const fn pktcnt(&self) -> u16 {\n            let val = (self.0 >> 19usize) & 0x03ff;\n            val as u16\n        }\n        #[doc = \"Packet count\"]\n        #[inline(always)]\n        pub fn set_pktcnt(&mut self, val: u16) {\n            self.0 = (self.0 & !(0x03ff << 19usize)) | (((val as u32) & 0x03ff) << 19usize);\n        }\n        #[doc = \"Data PID\"]\n        #[inline(always)]\n        pub const fn dpid(&self) -> u8 {\n            let val = (self.0 >> 29usize) & 0x03;\n            val as u8\n        }\n        #[doc = \"Data PID\"]\n        #[inline(always)]\n        pub fn set_dpid(&mut self, val: u8) {\n            self.0 = (self.0 & !(0x03 << 29usize)) | (((val as u32) & 0x03) << 29usize);\n        }\n    }\n    impl Default for Hctsiz {\n        #[inline(always)]\n        fn default() -> Hctsiz {\n            Hctsiz(0)\n        }\n    }\n    #[doc = \"Host frame interval register\"]\n    #[repr(transparent)]\n    #[derive(Copy, Clone, Eq, PartialEq)]\n    pub struct Hfir(pub u32);\n    impl Hfir {\n        #[doc = \"Frame interval\"]\n        #[inline(always)]\n        pub const fn frivl(&self) -> u16 {\n            let val = (self.0 >> 0usize) & 0xffff;\n            val as u16\n        }\n        #[doc = \"Frame interval\"]\n        #[inline(always)]\n        pub fn set_frivl(&mut self, val: u16) {\n            self.0 = (self.0 & !(0xffff << 0usize)) | (((val as u32) & 0xffff) << 0usize);\n        }\n    }\n    impl Default for Hfir {\n        #[inline(always)]\n        fn default() -> Hfir {\n            Hfir(0)\n        }\n    }\n    #[doc = \"Host frame number/frame time remaining register\"]\n    #[repr(transparent)]\n    #[derive(Copy, Clone, Eq, PartialEq)]\n    pub struct Hfnum(pub u32);\n    impl Hfnum {\n        #[doc = \"Frame number\"]\n        #[inline(always)]\n        pub const fn frnum(&self) -> u16 {\n            let val = (self.0 >> 0usize) & 0xffff;\n            val as u16\n        }\n        #[doc = \"Frame number\"]\n        #[inline(always)]\n        pub fn set_frnum(&mut self, val: u16) {\n            self.0 = (self.0 & !(0xffff << 0usize)) | (((val as u32) & 0xffff) << 0usize);\n        }\n        #[doc = \"Frame time remaining\"]\n        #[inline(always)]\n        pub const fn ftrem(&self) -> u16 {\n            let val = (self.0 >> 16usize) & 0xffff;\n            val as u16\n        }\n        #[doc = \"Frame time remaining\"]\n        #[inline(always)]\n        pub fn set_ftrem(&mut self, val: u16) {\n            self.0 = (self.0 & !(0xffff << 16usize)) | (((val as u32) & 0xffff) << 16usize);\n        }\n    }\n    impl Default for Hfnum {\n        #[inline(always)]\n        fn default() -> Hfnum {\n            Hfnum(0)\n        }\n    }\n    #[doc = \"Non-periodic transmit FIFO/queue status register\"]\n    #[repr(transparent)]\n    #[derive(Copy, Clone, Eq, PartialEq)]\n    pub struct Hnptxsts(pub u32);\n    impl Hnptxsts {\n        #[doc = \"Non-periodic TxFIFO space available\"]\n        #[inline(always)]\n        pub const fn nptxfsav(&self) -> u16 {\n            let val = (self.0 >> 0usize) & 0xffff;\n            val as u16\n        }\n        #[doc = \"Non-periodic TxFIFO space available\"]\n        #[inline(always)]\n        pub fn set_nptxfsav(&mut self, val: u16) {\n            self.0 = (self.0 & !(0xffff << 0usize)) | (((val as u32) & 0xffff) << 0usize);\n        }\n        #[doc = \"Non-periodic transmit request queue space available\"]\n        #[inline(always)]\n        pub const fn nptqxsav(&self) -> u8 {\n            let val = (self.0 >> 16usize) & 0xff;\n            val as u8\n        }\n        #[doc = \"Non-periodic transmit request queue space available\"]\n        #[inline(always)]\n        pub fn set_nptqxsav(&mut self, val: u8) {\n            self.0 = (self.0 & !(0xff << 16usize)) | (((val as u32) & 0xff) << 16usize);\n        }\n        #[doc = \"Top of the non-periodic transmit request queue\"]\n        #[inline(always)]\n        pub const fn nptxqtop(&self) -> u8 {\n            let val = (self.0 >> 24usize) & 0x7f;\n            val as u8\n        }\n        #[doc = \"Top of the non-periodic transmit request queue\"]\n        #[inline(always)]\n        pub fn set_nptxqtop(&mut self, val: u8) {\n            self.0 = (self.0 & !(0x7f << 24usize)) | (((val as u32) & 0x7f) << 24usize);\n        }\n    }\n    impl Default for Hnptxsts {\n        #[inline(always)]\n        fn default() -> Hnptxsts {\n            Hnptxsts(0)\n        }\n    }\n    #[doc = \"Host port control and status register\"]\n    #[repr(transparent)]\n    #[derive(Copy, Clone, Eq, PartialEq)]\n    pub struct Hprt(pub u32);\n    impl Hprt {\n        #[doc = \"Port connect status\"]\n        #[inline(always)]\n        pub const fn pcsts(&self) -> bool {\n            let val = (self.0 >> 0usize) & 0x01;\n            val != 0\n        }\n        #[doc = \"Port connect status\"]\n        #[inline(always)]\n        pub fn set_pcsts(&mut self, val: bool) {\n            self.0 = (self.0 & !(0x01 << 0usize)) | (((val as u32) & 0x01) << 0usize);\n        }\n        #[doc = \"Port connect detected\"]\n        #[inline(always)]\n        pub const fn pcdet(&self) -> bool {\n            let val = (self.0 >> 1usize) & 0x01;\n            val != 0\n        }\n        #[doc = \"Port connect detected\"]\n        #[inline(always)]\n        pub fn set_pcdet(&mut self, val: bool) {\n            self.0 = (self.0 & !(0x01 << 1usize)) | (((val as u32) & 0x01) << 1usize);\n        }\n        #[doc = \"Port enable\"]\n        #[inline(always)]\n        pub const fn pena(&self) -> bool {\n            let val = (self.0 >> 2usize) & 0x01;\n            val != 0\n        }\n        #[doc = \"Port enable\"]\n        #[inline(always)]\n        pub fn set_pena(&mut self, val: bool) {\n            self.0 = (self.0 & !(0x01 << 2usize)) | (((val as u32) & 0x01) << 2usize);\n        }\n        #[doc = \"Port enable/disable change\"]\n        #[inline(always)]\n        pub const fn penchng(&self) -> bool {\n            let val = (self.0 >> 3usize) & 0x01;\n            val != 0\n        }\n        #[doc = \"Port enable/disable change\"]\n        #[inline(always)]\n        pub fn set_penchng(&mut self, val: bool) {\n            self.0 = (self.0 & !(0x01 << 3usize)) | (((val as u32) & 0x01) << 3usize);\n        }\n        #[doc = \"Port overcurrent active\"]\n        #[inline(always)]\n        pub const fn poca(&self) -> bool {\n            let val = (self.0 >> 4usize) & 0x01;\n            val != 0\n        }\n        #[doc = \"Port overcurrent active\"]\n        #[inline(always)]\n        pub fn set_poca(&mut self, val: bool) {\n            self.0 = (self.0 & !(0x01 << 4usize)) | (((val as u32) & 0x01) << 4usize);\n        }\n        #[doc = \"Port overcurrent change\"]\n        #[inline(always)]\n        pub const fn pocchng(&self) -> bool {\n            let val = (self.0 >> 5usize) & 0x01;\n            val != 0\n        }\n        #[doc = \"Port overcurrent change\"]\n        #[inline(always)]\n        pub fn set_pocchng(&mut self, val: bool) {\n            self.0 = (self.0 & !(0x01 << 5usize)) | (((val as u32) & 0x01) << 5usize);\n        }\n        #[doc = \"Port resume\"]\n        #[inline(always)]\n        pub const fn pres(&self) -> bool {\n            let val = (self.0 >> 6usize) & 0x01;\n            val != 0\n        }\n        #[doc = \"Port resume\"]\n        #[inline(always)]\n        pub fn set_pres(&mut self, val: bool) {\n            self.0 = (self.0 & !(0x01 << 6usize)) | (((val as u32) & 0x01) << 6usize);\n        }\n        #[doc = \"Port suspend\"]\n        #[inline(always)]\n        pub const fn psusp(&self) -> bool {\n            let val = (self.0 >> 7usize) & 0x01;\n            val != 0\n        }\n        #[doc = \"Port suspend\"]\n        #[inline(always)]\n        pub fn set_psusp(&mut self, val: bool) {\n            self.0 = (self.0 & !(0x01 << 7usize)) | (((val as u32) & 0x01) << 7usize);\n        }\n        #[doc = \"Port reset\"]\n        #[inline(always)]\n        pub const fn prst(&self) -> bool {\n            let val = (self.0 >> 8usize) & 0x01;\n            val != 0\n        }\n        #[doc = \"Port reset\"]\n        #[inline(always)]\n        pub fn set_prst(&mut self, val: bool) {\n            self.0 = (self.0 & !(0x01 << 8usize)) | (((val as u32) & 0x01) << 8usize);\n        }\n        #[doc = \"Port line status\"]\n        #[inline(always)]\n        pub const fn plsts(&self) -> u8 {\n            let val = (self.0 >> 10usize) & 0x03;\n            val as u8\n        }\n        #[doc = \"Port line status\"]\n        #[inline(always)]\n        pub fn set_plsts(&mut self, val: u8) {\n            self.0 = (self.0 & !(0x03 << 10usize)) | (((val as u32) & 0x03) << 10usize);\n        }\n        #[doc = \"Port power\"]\n        #[inline(always)]\n        pub const fn ppwr(&self) -> bool {\n            let val = (self.0 >> 12usize) & 0x01;\n            val != 0\n        }\n        #[doc = \"Port power\"]\n        #[inline(always)]\n        pub fn set_ppwr(&mut self, val: bool) {\n            self.0 = (self.0 & !(0x01 << 12usize)) | (((val as u32) & 0x01) << 12usize);\n        }\n        #[doc = \"Port test control\"]\n        #[inline(always)]\n        pub const fn ptctl(&self) -> u8 {\n            let val = (self.0 >> 13usize) & 0x0f;\n            val as u8\n        }\n        #[doc = \"Port test control\"]\n        #[inline(always)]\n        pub fn set_ptctl(&mut self, val: u8) {\n            self.0 = (self.0 & !(0x0f << 13usize)) | (((val as u32) & 0x0f) << 13usize);\n        }\n        #[doc = \"Port speed\"]\n        #[inline(always)]\n        pub const fn pspd(&self) -> u8 {\n            let val = (self.0 >> 17usize) & 0x03;\n            val as u8\n        }\n        #[doc = \"Port speed\"]\n        #[inline(always)]\n        pub fn set_pspd(&mut self, val: u8) {\n            self.0 = (self.0 & !(0x03 << 17usize)) | (((val as u32) & 0x03) << 17usize);\n        }\n    }\n    impl Default for Hprt {\n        #[inline(always)]\n        fn default() -> Hprt {\n            Hprt(0)\n        }\n    }\n    #[doc = \"Periodic transmit FIFO/queue status register\"]\n    #[repr(transparent)]\n    #[derive(Copy, Clone, Eq, PartialEq)]\n    pub struct Hptxsts(pub u32);\n    impl Hptxsts {\n        #[doc = \"Periodic transmit data FIFO space available\"]\n        #[inline(always)]\n        pub const fn ptxfsavl(&self) -> u16 {\n            let val = (self.0 >> 0usize) & 0xffff;\n            val as u16\n        }\n        #[doc = \"Periodic transmit data FIFO space available\"]\n        #[inline(always)]\n        pub fn set_ptxfsavl(&mut self, val: u16) {\n            self.0 = (self.0 & !(0xffff << 0usize)) | (((val as u32) & 0xffff) << 0usize);\n        }\n        #[doc = \"Periodic transmit request queue space available\"]\n        #[inline(always)]\n        pub const fn ptxqsav(&self) -> u8 {\n            let val = (self.0 >> 16usize) & 0xff;\n            val as u8\n        }\n        #[doc = \"Periodic transmit request queue space available\"]\n        #[inline(always)]\n        pub fn set_ptxqsav(&mut self, val: u8) {\n            self.0 = (self.0 & !(0xff << 16usize)) | (((val as u32) & 0xff) << 16usize);\n        }\n        #[doc = \"Top of the periodic transmit request queue\"]\n        #[inline(always)]\n        pub const fn ptxqtop(&self) -> u8 {\n            let val = (self.0 >> 24usize) & 0xff;\n            val as u8\n        }\n        #[doc = \"Top of the periodic transmit request queue\"]\n        #[inline(always)]\n        pub fn set_ptxqtop(&mut self, val: u8) {\n            self.0 = (self.0 & !(0xff << 24usize)) | (((val as u32) & 0xff) << 24usize);\n        }\n    }\n    impl Default for Hptxsts {\n        #[inline(always)]\n        fn default() -> Hptxsts {\n            Hptxsts(0)\n        }\n    }\n    #[doc = \"Power and clock gating control register\"]\n    #[repr(transparent)]\n    #[derive(Copy, Clone, Eq, PartialEq)]\n    pub struct Pcgcctl(pub u32);\n    impl Pcgcctl {\n        #[doc = \"Stop PHY clock\"]\n        #[inline(always)]\n        pub const fn stppclk(&self) -> bool {\n            let val = (self.0 >> 0usize) & 0x01;\n            val != 0\n        }\n        #[doc = \"Stop PHY clock\"]\n        #[inline(always)]\n        pub fn set_stppclk(&mut self, val: bool) {\n            self.0 = (self.0 & !(0x01 << 0usize)) | (((val as u32) & 0x01) << 0usize);\n        }\n        #[doc = \"Gate HCLK\"]\n        #[inline(always)]\n        pub const fn gatehclk(&self) -> bool {\n            let val = (self.0 >> 1usize) & 0x01;\n            val != 0\n        }\n        #[doc = \"Gate HCLK\"]\n        #[inline(always)]\n        pub fn set_gatehclk(&mut self, val: bool) {\n            self.0 = (self.0 & !(0x01 << 1usize)) | (((val as u32) & 0x01) << 1usize);\n        }\n        #[doc = \"PHY Suspended\"]\n        #[inline(always)]\n        pub const fn physusp(&self) -> bool {\n            let val = (self.0 >> 4usize) & 0x01;\n            val != 0\n        }\n        #[doc = \"PHY Suspended\"]\n        #[inline(always)]\n        pub fn set_physusp(&mut self, val: bool) {\n            self.0 = (self.0 & !(0x01 << 4usize)) | (((val as u32) & 0x01) << 4usize);\n        }\n    }\n    impl Default for Pcgcctl {\n        #[inline(always)]\n        fn default() -> Pcgcctl {\n            Pcgcctl(0)\n        }\n    }\n}\npub mod vals {\n    #[repr(u8)]\n    #[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd)]\n    #[allow(non_camel_case_types)]\n    pub enum Dpid {\n        DATA0 = 0x0,\n        DATA2 = 0x01,\n        DATA1 = 0x02,\n        MDATA = 0x03,\n    }\n    impl Dpid {\n        #[inline(always)]\n        pub const fn from_bits(val: u8) -> Dpid {\n            unsafe { core::mem::transmute(val & 0x03) }\n        }\n        #[inline(always)]\n        pub const fn to_bits(self) -> u8 {\n            unsafe { core::mem::transmute(self) }\n        }\n    }\n    impl From<u8> for Dpid {\n        #[inline(always)]\n        fn from(val: u8) -> Dpid {\n            Dpid::from_bits(val)\n        }\n    }\n    impl From<Dpid> for u8 {\n        #[inline(always)]\n        fn from(val: Dpid) -> u8 {\n            Dpid::to_bits(val)\n        }\n    }\n    #[repr(u8)]\n    #[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd)]\n    #[allow(non_camel_case_types)]\n    pub enum Dspd {\n        #[doc = \"High speed\"]\n        HIGH_SPEED = 0x0,\n        #[doc = \"Full speed using external ULPI PHY\"]\n        FULL_SPEED_EXTERNAL = 0x01,\n        _RESERVED_2 = 0x02,\n        #[doc = \"Full speed using internal embedded PHY\"]\n        FULL_SPEED_INTERNAL = 0x03,\n    }\n    impl Dspd {\n        #[inline(always)]\n        pub const fn from_bits(val: u8) -> Dspd {\n            unsafe { core::mem::transmute(val & 0x03) }\n        }\n        #[inline(always)]\n        pub const fn to_bits(self) -> u8 {\n            unsafe { core::mem::transmute(self) }\n        }\n    }\n    impl From<u8> for Dspd {\n        #[inline(always)]\n        fn from(val: u8) -> Dspd {\n            Dspd::from_bits(val)\n        }\n    }\n    impl From<Dspd> for u8 {\n        #[inline(always)]\n        fn from(val: Dspd) -> u8 {\n            Dspd::to_bits(val)\n        }\n    }\n    #[repr(u8)]\n    #[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd)]\n    #[allow(non_camel_case_types)]\n    pub enum Eptyp {\n        CONTROL = 0x0,\n        ISOCHRONOUS = 0x01,\n        BULK = 0x02,\n        INTERRUPT = 0x03,\n    }\n    impl Eptyp {\n        #[inline(always)]\n        pub const fn from_bits(val: u8) -> Eptyp {\n            unsafe { core::mem::transmute(val & 0x03) }\n        }\n        #[inline(always)]\n        pub const fn to_bits(self) -> u8 {\n            unsafe { core::mem::transmute(self) }\n        }\n    }\n    impl From<u8> for Eptyp {\n        #[inline(always)]\n        fn from(val: u8) -> Eptyp {\n            Eptyp::from_bits(val)\n        }\n    }\n    impl From<Eptyp> for u8 {\n        #[inline(always)]\n        fn from(val: Eptyp) -> u8 {\n            Eptyp::to_bits(val)\n        }\n    }\n    #[repr(u8)]\n    #[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd)]\n    #[allow(non_camel_case_types)]\n    pub enum Pfivl {\n        #[doc = \"80% of the frame interval\"]\n        FRAME_INTERVAL_80 = 0x0,\n        #[doc = \"85% of the frame interval\"]\n        FRAME_INTERVAL_85 = 0x01,\n        #[doc = \"90% of the frame interval\"]\n        FRAME_INTERVAL_90 = 0x02,\n        #[doc = \"95% of the frame interval\"]\n        FRAME_INTERVAL_95 = 0x03,\n    }\n    impl Pfivl {\n        #[inline(always)]\n        pub const fn from_bits(val: u8) -> Pfivl {\n            unsafe { core::mem::transmute(val & 0x03) }\n        }\n        #[inline(always)]\n        pub const fn to_bits(self) -> u8 {\n            unsafe { core::mem::transmute(self) }\n        }\n    }\n    impl From<u8> for Pfivl {\n        #[inline(always)]\n        fn from(val: u8) -> Pfivl {\n            Pfivl::from_bits(val)\n        }\n    }\n    impl From<Pfivl> for u8 {\n        #[inline(always)]\n        fn from(val: Pfivl) -> u8 {\n            Pfivl::to_bits(val)\n        }\n    }\n    #[repr(u8)]\n    #[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd)]\n    #[allow(non_camel_case_types)]\n    pub enum Pktstsd {\n        _RESERVED_0 = 0x0,\n        #[doc = \"Global OUT NAK (triggers an interrupt)\"]\n        OUT_NAK = 0x01,\n        #[doc = \"OUT data packet received\"]\n        OUT_DATA_RX = 0x02,\n        #[doc = \"OUT transfer completed (triggers an interrupt)\"]\n        OUT_DATA_DONE = 0x03,\n        #[doc = \"SETUP transaction completed (triggers an interrupt)\"]\n        SETUP_DATA_DONE = 0x04,\n        _RESERVED_5 = 0x05,\n        #[doc = \"SETUP data packet received\"]\n        SETUP_DATA_RX = 0x06,\n        _RESERVED_7 = 0x07,\n        _RESERVED_8 = 0x08,\n        _RESERVED_9 = 0x09,\n        _RESERVED_a = 0x0a,\n        _RESERVED_b = 0x0b,\n        _RESERVED_c = 0x0c,\n        _RESERVED_d = 0x0d,\n        _RESERVED_e = 0x0e,\n        _RESERVED_f = 0x0f,\n    }\n    impl Pktstsd {\n        #[inline(always)]\n        pub const fn from_bits(val: u8) -> Pktstsd {\n            unsafe { core::mem::transmute(val & 0x0f) }\n        }\n        #[inline(always)]\n        pub const fn to_bits(self) -> u8 {\n            unsafe { core::mem::transmute(self) }\n        }\n    }\n    impl From<u8> for Pktstsd {\n        #[inline(always)]\n        fn from(val: u8) -> Pktstsd {\n            Pktstsd::from_bits(val)\n        }\n    }\n    impl From<Pktstsd> for u8 {\n        #[inline(always)]\n        fn from(val: Pktstsd) -> u8 {\n            Pktstsd::to_bits(val)\n        }\n    }\n    #[repr(u8)]\n    #[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd)]\n    #[allow(non_camel_case_types)]\n    pub enum Pktstsh {\n        _RESERVED_0 = 0x0,\n        _RESERVED_1 = 0x01,\n        #[doc = \"IN data packet received\"]\n        IN_DATA_RX = 0x02,\n        #[doc = \"IN transfer completed (triggers an interrupt)\"]\n        IN_DATA_DONE = 0x03,\n        _RESERVED_4 = 0x04,\n        #[doc = \"Data toggle error (triggers an interrupt)\"]\n        DATA_TOGGLE_ERR = 0x05,\n        _RESERVED_6 = 0x06,\n        #[doc = \"Channel halted (triggers an interrupt)\"]\n        CHANNEL_HALTED = 0x07,\n        _RESERVED_8 = 0x08,\n        _RESERVED_9 = 0x09,\n        _RESERVED_a = 0x0a,\n        _RESERVED_b = 0x0b,\n        _RESERVED_c = 0x0c,\n        _RESERVED_d = 0x0d,\n        _RESERVED_e = 0x0e,\n        _RESERVED_f = 0x0f,\n    }\n    impl Pktstsh {\n        #[inline(always)]\n        pub const fn from_bits(val: u8) -> Pktstsh {\n            unsafe { core::mem::transmute(val & 0x0f) }\n        }\n        #[inline(always)]\n        pub const fn to_bits(self) -> u8 {\n            unsafe { core::mem::transmute(self) }\n        }\n    }\n    impl From<u8> for Pktstsh {\n        #[inline(always)]\n        fn from(val: u8) -> Pktstsh {\n            Pktstsh::from_bits(val)\n        }\n    }\n    impl From<Pktstsh> for u8 {\n        #[inline(always)]\n        fn from(val: Pktstsh) -> u8 {\n            Pktstsh::to_bits(val)\n        }\n    }\n}\n"
  },
  {
    "path": "examples/.cargo/config.toml",
    "content": "[profile.release]\n# Allows defmt to display log locations even in release\ndebug = true"
  },
  {
    "path": "examples/boot/.cargo/config.toml",
    "content": "[unstable]\n#build-std = [\"core\"]\n#build-std-features = [\"panic_immediate_abort\"]\n\n[build]\ntarget = \"thumbv7em-none-eabi\"\n\n[env]\nDEFMT_LOG = \"trace\"\n"
  },
  {
    "path": "examples/boot/application/nrf/.cargo/config.toml",
    "content": "[target.'cfg(all(target_arch = \"arm\", target_os = \"none\"))']\n# replace nRF82840_xxAA with your chip as listed in `probe-rs chip list`\nrunner = \"probe-rs run --chip nRF52840_xxAA\"\n\n[build]\ntarget = \"thumbv7em-none-eabi\"\n\n[env]\nDEFMT_LOG = \"trace\"\n"
  },
  {
    "path": "examples/boot/application/nrf/Cargo.toml",
    "content": "[package]\nedition = \"2024\"\nname = \"embassy-boot-nrf-examples\"\nversion = \"0.1.0\"\nlicense = \"MIT OR Apache-2.0\"\npublish = false\n\n[dependencies]\nembassy-sync = { version = \"0.8.0\", path = \"../../../../embassy-sync\" }\nembassy-executor = { version = \"0.10.0\", path = \"../../../../embassy-executor\", features = [\"platform-cortex-m\", \"executor-thread\", \"platform-cortex-m\", \"executor-thread\"] }\nembassy-time = { version = \"0.5.1\", path = \"../../../../embassy-time\", features = [] }\nembassy-nrf = { version = \"0.10.0\", path = \"../../../../embassy-nrf\", features = [\"gpiote\", ] }\nembassy-boot = { version = \"0.7.0\", path = \"../../../../embassy-boot\", features = [] }\nembassy-boot-nrf = { version = \"0.11.0\", path = \"../../../../embassy-boot-nrf\", features = [] }\nembassy-embedded-hal = { version = \"0.6.0\", path = \"../../../../embassy-embedded-hal\" }\n\ndefmt = { version = \"1.0.1\", optional = true }\ndefmt-rtt = { version = \"1.0.0\", optional = true }\npanic-reset = { version = \"0.1.1\" }\nembedded-hal = { version = \"0.2.6\" }\n\ncortex-m = { version = \"0.7.6\", features = [\"inline-asm\", \"critical-section-single-core\"] }\ncortex-m-rt = \"0.7.0\"\n\n[features]\ned25519-dalek = [\"embassy-boot/ed25519-dalek\"]\ned25519-salty = [\"embassy-boot/ed25519-salty\"]\nskip-include = []\ndefmt = [\n      \"dep:defmt\",\n      \"dep:defmt-rtt\",\n      \"embassy-nrf/defmt\",\n      \"embassy-boot-nrf/defmt\",\n      \"embassy-sync/defmt\",\n]\nnrf54 = [\"embassy-nrf/time-driver-grtc\"]\n\n[package.metadata.embassy]\nbuild = [\n  { target = \"thumbv7em-none-eabi\", features = [\"embassy-nrf/nrf52840\", \"embassy-nrf/time-driver-rtc1\", \"skip-include\"], artifact-dir = \"out/examples/boot/nrf52840\" },\n  { target = \"thumbv8m.main-none-eabihf\", features = [\"embassy-nrf/nrf9160-ns\", \"embassy-nrf/time-driver-rtc1\", \"skip-include\"], artifact-dir = \"out/examples/boot/nrf9160\" },\n  { target = \"thumbv8m.main-none-eabihf\", features = [\"embassy-nrf/nrf9120-ns\", \"embassy-nrf/time-driver-rtc1\", \"skip-include\"], artifact-dir = \"out/examples/boot/nrf9120\" },\n  { target = \"thumbv8m.main-none-eabihf\", features = [\"embassy-nrf/nrf9151-ns\", \"embassy-nrf/time-driver-rtc1\", \"skip-include\"], artifact-dir = \"out/examples/boot/nrf9151\" },\n  { target = \"thumbv8m.main-none-eabihf\", features = [\"embassy-nrf/nrf9161-ns\", \"embassy-nrf/time-driver-rtc1\", \"skip-include\"], artifact-dir = \"out/examples/boot/nrf9161\" },\n  { target = \"thumbv8m.main-none-eabihf\", features = [\"embassy-nrf/nrf54l15-app-s\", \"nrf54\", \"skip-include\"], artifact-dir = \"out/examples/boot/nrf54l15\" }\n]\n"
  },
  {
    "path": "examples/boot/application/nrf/README.md",
    "content": "# Examples using bootloader\n\nExample for nRF demonstrating the bootloader. The example consists of application binaries, 'a'\nwhich allows you to press a button to start the DFU process, and 'b' which is the updated\napplication.\n\n\n## Prerequisites\n\n* `cargo-binutils`\n* `cargo-flash`\n* `embassy-boot-nrf`\n\n## Usage\n\n\n\n```\n# Use bare metal linker script\ncp memory-bl.x ../../bootloader/nrf/memory.x\n\n# Flash bootloader\ncargo flash --manifest-path ../../bootloader/nrf/Cargo.toml --features embassy-nrf/nrf52840 --target thumbv7em-none-eabi --release --chip nRF52840_xxAA\n# Build 'b'\ncargo build --release --bin b --features embassy-nrf/nrf52840,time-driver-rtc1\n# Generate binary for 'b'\ncargo objcopy --release --bin b --features embassy-nrf/nrf52840 --target thumbv7em-none-eabi -- -O binary b.bin\n```\n\n# Flash `a` (which includes b.bin)\n\n```\ncargo flash --release --bin a --features embassy-nrf/nrf52840 --target thumbv7em-none-eabi --chip nRF52840_xxAA\n```\n\nYou should then see a solid LED. Pressing button 1 will cause the DFU to be loaded by the bootloader. Upon\nsuccessfully loading, you'll see the LED flash. After 5 seconds, because there is no petting of the watchdog,\nyou'll see the LED go solid again. This indicates that the bootloader has reverted the update.\n"
  },
  {
    "path": "examples/boot/application/nrf/build.rs",
    "content": "//! This build script copies the `memory.x` file from the crate root into\n//! a directory where the linker can always find it at build time.\n//! For many projects this is optional, as the linker always searches the\n//! project root directory -- wherever `Cargo.toml` is. However, if you\n//! are using a workspace or have a more complicated build setup, this\n//! build script becomes required. Additionally, by requesting that\n//! Cargo re-run the build script whenever `memory.x` is changed,\n//! updating `memory.x` ensures a rebuild of the application with the\n//! new memory settings.\n\nuse std::env;\nuse std::fs::File;\nuse std::io::Write;\nuse std::path::PathBuf;\n\nfn main() {\n    // Put `memory.x` in our output directory and ensure it's\n    // on the linker search path.\n    let out = &PathBuf::from(env::var_os(\"OUT_DIR\").unwrap());\n    File::create(out.join(\"memory.x\"))\n        .unwrap()\n        .write_all(include_bytes!(\"memory.x\"))\n        .unwrap();\n    println!(\"cargo:rustc-link-search={}\", out.display());\n\n    // By default, Cargo will re-run a build script whenever\n    // any file in the project changes. By specifying `memory.x`\n    // here, we ensure the build script is only re-run when\n    // `memory.x` is changed.\n    println!(\"cargo:rerun-if-changed=memory.x\");\n\n    println!(\"cargo:rustc-link-arg-bins=--nmagic\");\n    println!(\"cargo:rustc-link-arg-bins=-Tlink.x\");\n    if env::var(\"CARGO_FEATURE_DEFMT\").is_ok() {\n        println!(\"cargo:rustc-link-arg-bins=-Tdefmt.x\");\n    }\n}\n"
  },
  {
    "path": "examples/boot/application/nrf/memory-bl-nrf91.x",
    "content": "MEMORY\n{\n  /* NOTE 1 K = 1 KiBi = 1024 bytes */\n  /* Assumes Secure Partition Manager (SPM) flashed at the start */\n  FLASH                             : ORIGIN = 0x00050000, LENGTH = 24K\n  BOOTLOADER_STATE                  : ORIGIN = 0x00056000, LENGTH = 4K\n  ACTIVE                            : ORIGIN = 0x00057000, LENGTH = 64K\n  DFU                               : ORIGIN = 0x00067000, LENGTH = 68K\n  RAM                         (rwx) : ORIGIN = 0x20018000, LENGTH = 32K\n}\n\n__bootloader_state_start = ORIGIN(BOOTLOADER_STATE);\n__bootloader_state_end = ORIGIN(BOOTLOADER_STATE) + LENGTH(BOOTLOADER_STATE);\n\n__bootloader_active_start = ORIGIN(ACTIVE);\n__bootloader_active_end = ORIGIN(ACTIVE) + LENGTH(ACTIVE);\n\n__bootloader_dfu_start = ORIGIN(DFU);\n__bootloader_dfu_end = ORIGIN(DFU) + LENGTH(DFU);\n"
  },
  {
    "path": "examples/boot/application/nrf/memory-bl.x",
    "content": "MEMORY\n{\n  /* NOTE 1 K = 1 KiBi = 1024 bytes */\n  FLASH                             : ORIGIN = 0x00000000, LENGTH = 24K\n  BOOTLOADER_STATE                  : ORIGIN = 0x00006000, LENGTH = 4K\n  ACTIVE                            : ORIGIN = 0x00007000, LENGTH = 64K\n  DFU                               : ORIGIN = 0x00017000, LENGTH = 68K\n  RAM                         (rwx) : ORIGIN = 0x20000000, LENGTH = 32K\n}\n\n__bootloader_state_start = ORIGIN(BOOTLOADER_STATE);\n__bootloader_state_end = ORIGIN(BOOTLOADER_STATE) + LENGTH(BOOTLOADER_STATE);\n\n__bootloader_active_start = ORIGIN(ACTIVE);\n__bootloader_active_end = ORIGIN(ACTIVE) + LENGTH(ACTIVE);\n\n__bootloader_dfu_start = ORIGIN(DFU);\n__bootloader_dfu_end = ORIGIN(DFU) + LENGTH(DFU);\n"
  },
  {
    "path": "examples/boot/application/nrf/memory-nrf91.x",
    "content": "MEMORY\n{\n  /* NOTE 1 K = 1 KiBi = 1024 bytes */\n  /* Assumes Secure Partition Manager (SPM) flashed at the start */\n  BOOTLOADER                        : ORIGIN = 0x00050000, LENGTH = 24K\n  BOOTLOADER_STATE                  : ORIGIN = 0x00056000, LENGTH = 4K\n  FLASH                             : ORIGIN = 0x00057000, LENGTH = 64K\n  DFU                               : ORIGIN = 0x00067000, LENGTH = 68K\n  RAM                         (rwx) : ORIGIN = 0x20018000, LENGTH = 32K\n}\n\n__bootloader_state_start = ORIGIN(BOOTLOADER_STATE);\n__bootloader_state_end = ORIGIN(BOOTLOADER_STATE) + LENGTH(BOOTLOADER_STATE);\n\n__bootloader_dfu_start = ORIGIN(DFU);\n__bootloader_dfu_end = ORIGIN(DFU) + LENGTH(DFU);\n"
  },
  {
    "path": "examples/boot/application/nrf/memory.x",
    "content": "MEMORY\n{\n  /* NOTE 1 K = 1 KiBi = 1024 bytes */\n  BOOTLOADER                        : ORIGIN = 0x00000000, LENGTH = 24K\n  BOOTLOADER_STATE                  : ORIGIN = 0x00006000, LENGTH = 4K\n  FLASH                             : ORIGIN = 0x00007000, LENGTH = 64K\n  DFU                               : ORIGIN = 0x00017000, LENGTH = 68K\n  RAM                         (rwx) : ORIGIN = 0x20000000, LENGTH = 32K\n}\n\n__bootloader_state_start = ORIGIN(BOOTLOADER_STATE);\n__bootloader_state_end = ORIGIN(BOOTLOADER_STATE) + LENGTH(BOOTLOADER_STATE);\n\n__bootloader_dfu_start = ORIGIN(DFU);\n__bootloader_dfu_end = ORIGIN(DFU) + LENGTH(DFU);\n"
  },
  {
    "path": "examples/boot/application/nrf/src/bin/a.rs",
    "content": "#![no_std]\n#![no_main]\n#![macro_use]\n\n#[cfg(feature = \"defmt\")]\nuse defmt_rtt as _;\nuse embassy_boot::State;\nuse embassy_boot_nrf::{FirmwareUpdater, FirmwareUpdaterConfig};\nuse embassy_embedded_hal::adapter::BlockingAsync;\nuse embassy_executor::Spawner;\nuse embassy_nrf::gpio::{Input, Level, Output, OutputDrive, Pull};\nuse embassy_nrf::nvmc::Nvmc;\nuse embassy_nrf::wdt::{self, Watchdog};\nuse embassy_sync::mutex::Mutex;\nuse panic_reset as _;\n\n#[cfg(feature = \"skip-include\")]\nstatic APP_B: &[u8] = &[0, 1, 2, 3];\n#[cfg(not(feature = \"skip-include\"))]\nstatic APP_B: &[u8] = include_bytes!(\"../../b.bin\");\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let p = embassy_nrf::init(Default::default());\n\n    #[cfg(not(feature = \"nrf54\"))]\n    let mut button = Input::new(p.P0_11, Pull::Up);\n    #[cfg(not(feature = \"nrf54\"))]\n    let mut led = Output::new(p.P0_13, Level::Low, OutputDrive::Standard);\n    #[cfg(not(feature = \"nrf54\"))]\n    let mut led_reverted = Output::new(p.P0_14, Level::High, OutputDrive::Standard);\n\n    // nRF54 DK\n    #[cfg(feature = \"nrf54\")]\n    let mut button = Input::new(p.P1_13, Pull::Up);\n    #[cfg(feature = \"nrf54\")]\n    let mut led = Output::new(p.P1_14, Level::Low, OutputDrive::Standard);\n    #[cfg(feature = \"nrf54\")]\n    let mut led_reverted = Output::new(p.P2_09, Level::High, OutputDrive::Standard);\n\n    //let mut led = Output::new(p.P1_10, Level::Low, OutputDrive::Standard);\n    //let mut button = Input::new(p.P1_02, Pull::Up);\n\n    // nRF91 DK\n    // let mut led = Output::new(p.P0_02, Level::Low, OutputDrive::Standard);\n    // let mut button = Input::new(p.P0_06, Pull::Up);\n\n    // The following code block illustrates how to obtain a watchdog that is configured\n    // as per the existing watchdog. Ordinarily, we'd use the handle returned to \"pet\" the\n    // watchdog periodically. If we don't, and we're not going to for this example, then\n    // the watchdog will cause the device to reset as per its configured timeout in the bootloader.\n    // This helps is avoid a situation where new firmware might be bad and block our executor.\n    // If firmware is bad in this way then the bootloader will revert to any previous version.\n    #[cfg(feature = \"nrf54\")]\n    let wdt = p.WDT0;\n    #[cfg(not(feature = \"nrf54\"))]\n    let wdt = p.WDT;\n    let wdt_config = wdt::Config::try_new(&wdt).unwrap();\n    let (_wdt, [_wdt_handle]) = match Watchdog::try_new(wdt, wdt_config) {\n        Ok(x) => x,\n        Err(_) => {\n            // Watchdog already active with the wrong number of handles, waiting for it to timeout...\n            loop {\n                cortex_m::asm::wfe();\n            }\n        }\n    };\n\n    // RRAMC for nRF54\n    #[cfg(feature = \"nrf54\")]\n    let nvmc = Nvmc::new(p.RRAMC);\n    #[cfg(not(feature = \"nrf54\"))]\n    let nvmc = Nvmc::new(p.NVMC);\n    let nvmc = Mutex::new(BlockingAsync::new(nvmc));\n\n    let config = FirmwareUpdaterConfig::from_linkerfile(&nvmc, &nvmc);\n    let mut magic = [0; 16];\n    let mut updater = FirmwareUpdater::new(config, &mut magic);\n    let state = updater.get_state().await.unwrap();\n    if state == State::Revert {\n        led_reverted.set_low();\n    } else {\n        led_reverted.set_high();\n    }\n\n    loop {\n        led.set_low();\n        button.wait_for_any_edge().await;\n        if button.is_low() {\n            let mut offset = 0;\n            for chunk in APP_B.chunks(4096) {\n                let mut buf: [u8; 4096] = [0; 4096];\n                buf[..chunk.len()].copy_from_slice(chunk);\n                updater.write_firmware(offset, &buf).await.unwrap();\n                offset += chunk.len();\n            }\n            updater.mark_updated().await.unwrap();\n            led.set_high();\n            cortex_m::peripheral::SCB::sys_reset();\n        }\n    }\n}\n"
  },
  {
    "path": "examples/boot/application/nrf/src/bin/b.rs",
    "content": "#![no_std]\n#![no_main]\n#![macro_use]\n\nuse embassy_executor::Spawner;\nuse embassy_nrf::gpio::{Level, Output, OutputDrive};\nuse embassy_time::Timer;\nuse panic_reset as _;\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let p = embassy_nrf::init(Default::default());\n    #[cfg(not(feature = \"nrf54\"))]\n    let mut led = Output::new(p.P0_13, Level::Low, OutputDrive::Standard);\n    // let mut led = Output::new(p.P1_10, Level::Low, OutputDrive::Standard);\n\n    // nRF91 DK\n    // let mut led = Output::new(p.P0_02, Level::Low, OutputDrive::Standard);\n    // nrf54l15 dk\n    #[cfg(feature = \"nrf54\")]\n    let mut led = Output::new(p.P1_10, Level::Low, OutputDrive::Standard);\n\n    loop {\n        led.set_high();\n        Timer::after_millis(300).await;\n        led.set_low();\n        Timer::after_millis(300).await;\n    }\n}\n"
  },
  {
    "path": "examples/boot/application/rp/.cargo/config.toml",
    "content": "[unstable]\n#build-std = [\"core\"]\n#build-std-features = [\"panic_immediate_abort\"]\n\n[target.'cfg(all(target_arch = \"arm\", target_os = \"none\"))']\nrunner = \"probe-rs run --chip RP2040\"\n\n[build]\ntarget = \"thumbv6m-none-eabi\"\n\n[env]\nDEFMT_LOG = \"trace\"\n"
  },
  {
    "path": "examples/boot/application/rp/Cargo.toml",
    "content": "[package]\nedition = \"2024\"\nname = \"embassy-boot-rp-examples\"\nversion = \"0.1.0\"\nlicense = \"MIT OR Apache-2.0\"\npublish = false\n\n[dependencies]\nembassy-sync = { version = \"0.8.0\", path = \"../../../../embassy-sync\" }\nembassy-executor = { version = \"0.10.0\", path = \"../../../../embassy-executor\", features = [\"platform-cortex-m\", \"executor-thread\"] }\nembassy-time = { version = \"0.5.1\", path = \"../../../../embassy-time\", features = [] }\nembassy-rp = { version = \"0.10.0\", path = \"../../../../embassy-rp\", features = [\"time-driver\", \"rp2040\"] }\nembassy-boot-rp = { version = \"0.10.0\", path = \"../../../../embassy-boot-rp\", features = [] }\nembassy-embedded-hal = { version = \"0.6.0\", path = \"../../../../embassy-embedded-hal\" }\n\ndefmt = \"1.0.1\"\ndefmt-rtt = \"1.0.0\"\npanic-probe = { version = \"1.0.0\", features = [\"print-defmt\"], optional = true }\npanic-reset = { version = \"0.1.1\", optional = true }\nembedded-hal = { version = \"0.2.6\" }\n\ncortex-m = { version = \"0.7.6\", features = [\"inline-asm\", \"critical-section-single-core\"] }\ncortex-m-rt = \"0.7.0\"\nembedded-storage = \"0.3.1\"\n\n[features]\ndefault = [\"panic-reset\"]\ndebug = [\n    \"embassy-rp/defmt\",\n    \"embassy-boot-rp/defmt\",\n    \"embassy-sync/defmt\",\n    \"panic-probe\"\n]\nskip-include = []\n\n[profile.release]\ndebug = true\n\n[package.metadata.embassy]\nbuild = [\n  { target = \"thumbv6m-none-eabi\", features = [\"skip-include\"], artifact-dir = \"out/examples/boot/rp\" }\n]\n"
  },
  {
    "path": "examples/boot/application/rp/README.md",
    "content": "# Examples using bootloader\n\nExample for Raspberry Pi Pico demonstrating the bootloader. The example consists of application binaries, 'a'\nwhich waits for 5 seconds before flashing the 'b' binary, which blinks the LED.\n\nNOTE: The 'b' binary does not mark the new binary as active, so if you reset the device, it will roll back to the 'a' binary before automatically updating it again.\n\n## Prerequisites\n\n* `cargo-binutils`\n* `cargo-flash`\n* `embassy-boot-rp`\n\n## Usage\n\n```\n# Flash bootloader\ncargo flash --manifest-path ../../bootloader/rp/Cargo.toml --release --chip RP2040\n\n# Build 'b'\ncargo build --release --bin b\n\n# Generate binary for 'b'\ncargo objcopy --release --bin b -- -O binary b.bin\n\n# Flash `a` (which includes b.bin)\ncargo flash --release --bin a --chip RP2040\n```\n"
  },
  {
    "path": "examples/boot/application/rp/build.rs",
    "content": "//! This build script copies the `memory.x` file from the crate root into\n//! a directory where the linker can always find it at build time.\n//! For many projects this is optional, as the linker always searches the\n//! project root directory -- wherever `Cargo.toml` is. However, if you\n//! are using a workspace or have a more complicated build setup, this\n//! build script becomes required. Additionally, by requesting that\n//! Cargo re-run the build script whenever `memory.x` is changed,\n//! updating `memory.x` ensures a rebuild of the application with the\n//! new memory settings.\n\nuse std::env;\nuse std::fs::File;\nuse std::io::Write;\nuse std::path::PathBuf;\n\nfn main() {\n    // Put `memory.x` in our output directory and ensure it's\n    // on the linker search path.\n    let out = &PathBuf::from(env::var_os(\"OUT_DIR\").unwrap());\n    File::create(out.join(\"memory.x\"))\n        .unwrap()\n        .write_all(include_bytes!(\"memory.x\"))\n        .unwrap();\n    println!(\"cargo:rustc-link-search={}\", out.display());\n\n    // By default, Cargo will re-run a build script whenever\n    // any file in the project changes. By specifying `memory.x`\n    // here, we ensure the build script is only re-run when\n    // `memory.x` is changed.\n    println!(\"cargo:rerun-if-changed=memory.x\");\n\n    println!(\"cargo:rustc-link-arg-bins=--nmagic\");\n    println!(\"cargo:rustc-link-arg-bins=-Tlink.x\");\n    println!(\"cargo:rustc-link-arg-bins=-Tdefmt.x\");\n}\n"
  },
  {
    "path": "examples/boot/application/rp/memory.x",
    "content": "MEMORY\n{\n  /* NOTE 1 K = 1 KiBi = 1024 bytes */\n  BOOT2                             : ORIGIN = 0x10000000, LENGTH = 0x100\n  BOOTLOADER_STATE                  : ORIGIN = 0x10006000, LENGTH = 4K\n  FLASH                             : ORIGIN = 0x10007000, LENGTH = 512K\n  DFU                               : ORIGIN = 0x10087000, LENGTH = 516K\n\n  /* Pick one of the two options for RAM layout     */\n\n  /* OPTION A: Use all RAM banks as one big block   */\n  /* Reasonable, unless you are doing something     */\n  /* really particular with DMA or other concurrent */\n  /* access that would benefit from striping        */\n  RAM   : ORIGIN = 0x20000000, LENGTH = 264K\n\n  /* OPTION B: Keep the unstriped sections separate */\n  /* RAM: ORIGIN = 0x20000000, LENGTH = 256K        */\n  /* SCRATCH_A: ORIGIN = 0x20040000, LENGTH = 4K    */\n  /* SCRATCH_B: ORIGIN = 0x20041000, LENGTH = 4K    */\n}\n\n__bootloader_state_start = ORIGIN(BOOTLOADER_STATE) - ORIGIN(BOOT2);\n__bootloader_state_end = ORIGIN(BOOTLOADER_STATE) + LENGTH(BOOTLOADER_STATE) - ORIGIN(BOOT2);\n\n__bootloader_dfu_start = ORIGIN(DFU) - ORIGIN(BOOT2);\n__bootloader_dfu_end = ORIGIN(DFU) + LENGTH(DFU) - ORIGIN(BOOT2);\n"
  },
  {
    "path": "examples/boot/application/rp/src/bin/a.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse core::cell::RefCell;\n\nuse defmt_rtt as _;\nuse embassy_boot_rp::*;\nuse embassy_executor::Spawner;\nuse embassy_rp::flash::Flash;\nuse embassy_rp::gpio::{Level, Output};\nuse embassy_rp::watchdog::Watchdog;\nuse embassy_sync::blocking_mutex::Mutex;\nuse embassy_time::{Duration, Timer};\nuse embedded_storage::nor_flash::NorFlash;\n#[cfg(feature = \"panic-probe\")]\nuse panic_probe as _;\n#[cfg(feature = \"panic-reset\")]\nuse panic_reset as _;\n\n#[cfg(feature = \"skip-include\")]\nstatic APP_B: &[u8] = &[0, 1, 2, 3];\n#[cfg(not(feature = \"skip-include\"))]\nstatic APP_B: &[u8] = include_bytes!(\"../../b.bin\");\n\nconst FLASH_SIZE: usize = 2 * 1024 * 1024;\nconst WATCHDOG_TIMEOUT: Duration = Duration::from_secs(8);\n\n#[embassy_executor::main]\nasync fn main(_s: Spawner) {\n    let p = embassy_rp::init(Default::default());\n    let mut led = Output::new(p.PIN_25, Level::Low);\n\n    // Override bootloader watchdog\n    let mut watchdog = Watchdog::new(p.WATCHDOG);\n    watchdog.start(WATCHDOG_TIMEOUT);\n\n    let flash = Flash::<_, _, FLASH_SIZE>::new_blocking(p.FLASH);\n    let flash = Mutex::new(RefCell::new(flash));\n\n    let config = FirmwareUpdaterConfig::from_linkerfile_blocking(&flash, &flash);\n    let mut aligned = AlignedBuffer([0; 1]);\n    let mut updater = BlockingFirmwareUpdater::new(config, &mut aligned.0);\n\n    Timer::after_secs(5).await;\n    watchdog.feed(WATCHDOG_TIMEOUT);\n    led.set_high();\n    let mut offset = 0;\n    let mut buf: AlignedBuffer<4096> = AlignedBuffer([0; 4096]);\n    defmt::info!(\"preparing update\");\n    let writer = updater\n        .prepare_update()\n        .map_err(|e| defmt::warn!(\"E: {:?}\", defmt::Debug2Format(&e)))\n        .unwrap();\n    defmt::info!(\"writer created, starting write\");\n    for chunk in APP_B.chunks(4096) {\n        buf.0[..chunk.len()].copy_from_slice(chunk);\n        defmt::info!(\"writing block at offset {}\", offset);\n        writer.write(offset, &buf.0[..chunk.len()]).unwrap();\n        offset += chunk.len() as u32;\n    }\n    watchdog.feed(WATCHDOG_TIMEOUT);\n    defmt::info!(\"firmware written, marking update\");\n    updater.mark_updated().unwrap();\n    Timer::after_secs(2).await;\n    led.set_low();\n    defmt::info!(\"update marked, resetting\");\n    cortex_m::peripheral::SCB::sys_reset();\n}\n"
  },
  {
    "path": "examples/boot/application/rp/src/bin/b.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse embassy_executor::Spawner;\nuse embassy_rp::gpio;\nuse embassy_time::Timer;\nuse gpio::{Level, Output};\nuse {defmt_rtt as _, panic_reset as _};\n\n#[embassy_executor::main]\nasync fn main(_s: Spawner) {\n    let p = embassy_rp::init(Default::default());\n    let mut led = Output::new(p.PIN_25, Level::Low);\n\n    loop {\n        led.set_high();\n        Timer::after_millis(100).await;\n\n        led.set_low();\n        Timer::after_millis(100).await;\n    }\n}\n"
  },
  {
    "path": "examples/boot/application/stm32f3/.cargo/config.toml",
    "content": "[target.'cfg(all(target_arch = \"arm\", target_os = \"none\"))']\n# replace STM32F429ZITx with your chip as listed in `probe-rs chip list`\nrunner = \"probe-rs run --chip STM32F303VCTx\"\n\n[build]\ntarget = \"thumbv7em-none-eabihf\"\n\n[env]\nDEFMT_LOG = \"trace\"\n"
  },
  {
    "path": "examples/boot/application/stm32f3/Cargo.toml",
    "content": "[package]\nedition = \"2024\"\nname = \"embassy-boot-stm32f3-examples\"\nversion = \"0.1.0\"\nlicense = \"MIT OR Apache-2.0\"\npublish = false\n\n[dependencies]\nembassy-sync = { version = \"0.8.0\", path = \"../../../../embassy-sync\" }\nembassy-executor = { version = \"0.10.0\", path = \"../../../../embassy-executor\", features = [\"platform-cortex-m\", \"executor-thread\"] }\nembassy-time = { version = \"0.5.1\", path = \"../../../../embassy-time\", features = [ \"tick-hz-32_768\"] }\nembassy-stm32 = { version = \"0.6.0\", path = \"../../../../embassy-stm32\", features = [\"stm32f303re\", \"time-driver-any\", \"exti\"]  }\nembassy-boot-stm32 = { version = \"0.8.0\", path = \"../../../../embassy-boot-stm32\" }\nembassy-embedded-hal = { version = \"0.6.0\", path = \"../../../../embassy-embedded-hal\" }\n\ndefmt = { version = \"1.0.1\", optional = true }\ndefmt-rtt = { version = \"1.0.0\", optional = true }\npanic-reset = { version = \"0.1.1\" }\nembedded-hal = { version = \"0.2.6\" }\n\ncortex-m = { version = \"0.7.6\", features = [\"inline-asm\", \"critical-section-single-core\"] }\ncortex-m-rt = \"0.7.0\"\n\n[features]\ndefmt = [\n      \"dep:defmt\",\n      \"dep:defmt-rtt\",\n      \"embassy-stm32/defmt\",\n      \"embassy-boot-stm32/defmt\",\n      \"embassy-sync/defmt\",\n]\nskip-include = []\n\n[package.metadata.embassy]\nbuild = [\n  { target = \"thumbv7em-none-eabi\", features = [\"skip-include\"], artifact-dir = \"out/examples/boot/stm32f3\" }\n]\n"
  },
  {
    "path": "examples/boot/application/stm32f3/README.md",
    "content": "# Examples using bootloader\n\nExample for STM32F3 demonstrating the bootloader. The example consists of application binaries, 'a'\nwhich allows you to press a button to start the DFU process, and 'b' which is the updated\napplication.\n\n\n## Prerequisites\n\n* `cargo-binutils`\n* `cargo-flash`\n* `embassy-boot-stm32`\n\n## Usage\n\n```\n# Flash bootloader\ncargo flash --manifest-path ../../bootloader/stm32/Cargo.toml --release --features embassy-stm32/stm32f303re --chip STM32F303RETx\n# Build 'b'\ncargo build --release --bin b\n# Generate binary for 'b'\ncargo objcopy --release --bin b -- -O binary b.bin\n```\n\n# Flash `a` (which includes b.bin)\n\n```\ncargo flash --release --bin a --chip STM32F303RETx\n```\n"
  },
  {
    "path": "examples/boot/application/stm32f3/build.rs",
    "content": "//! This build script copies the `memory.x` file from the crate root into\n//! a directory where the linker can always find it at build time.\n//! For many projects this is optional, as the linker always searches the\n//! project root directory -- wherever `Cargo.toml` is. However, if you\n//! are using a workspace or have a more complicated build setup, this\n//! build script becomes required. Additionally, by requesting that\n//! Cargo re-run the build script whenever `memory.x` is changed,\n//! updating `memory.x` ensures a rebuild of the application with the\n//! new memory settings.\n\nuse std::env;\nuse std::fs::File;\nuse std::io::Write;\nuse std::path::PathBuf;\n\nfn main() {\n    // Put `memory.x` in our output directory and ensure it's\n    // on the linker search path.\n    let out = &PathBuf::from(env::var_os(\"OUT_DIR\").unwrap());\n    File::create(out.join(\"memory.x\"))\n        .unwrap()\n        .write_all(include_bytes!(\"memory.x\"))\n        .unwrap();\n    println!(\"cargo:rustc-link-search={}\", out.display());\n\n    // By default, Cargo will re-run a build script whenever\n    // any file in the project changes. By specifying `memory.x`\n    // here, we ensure the build script is only re-run when\n    // `memory.x` is changed.\n    println!(\"cargo:rerun-if-changed=memory.x\");\n\n    println!(\"cargo:rustc-link-arg-bins=--nmagic\");\n    println!(\"cargo:rustc-link-arg-bins=-Tlink.x\");\n    if env::var(\"CARGO_FEATURE_DEFMT\").is_ok() {\n        println!(\"cargo:rustc-link-arg-bins=-Tdefmt.x\");\n    }\n}\n"
  },
  {
    "path": "examples/boot/application/stm32f3/memory.x",
    "content": "MEMORY\n{\n  /* NOTE 1 K = 1 KiBi = 1024 bytes */\n  BOOTLOADER                        : ORIGIN = 0x08000000, LENGTH = 24K\n  BOOTLOADER_STATE                  : ORIGIN = 0x08006000, LENGTH = 4K\n  FLASH                             : ORIGIN = 0x08008000, LENGTH = 64K\n  DFU                               : ORIGIN = 0x08018000, LENGTH = 66K\n  RAM                         (rwx) : ORIGIN = 0x20000000, LENGTH = 32K\n}\n\n__bootloader_state_start = ORIGIN(BOOTLOADER_STATE) - ORIGIN(BOOTLOADER);\n__bootloader_state_end = ORIGIN(BOOTLOADER_STATE) + LENGTH(BOOTLOADER_STATE) - ORIGIN(BOOTLOADER);\n\n__bootloader_dfu_start = ORIGIN(DFU) - ORIGIN(BOOTLOADER);\n__bootloader_dfu_end = ORIGIN(DFU) + LENGTH(DFU) - ORIGIN(BOOTLOADER);\n"
  },
  {
    "path": "examples/boot/application/stm32f3/src/bin/a.rs",
    "content": "#![no_std]\n#![no_main]\n\n#[cfg(feature = \"defmt\")]\nuse defmt_rtt::*;\nuse embassy_boot_stm32::{AlignedBuffer, FirmwareUpdater, FirmwareUpdaterConfig};\nuse embassy_embedded_hal::adapter::BlockingAsync;\nuse embassy_executor::Spawner;\nuse embassy_stm32::exti::{self, ExtiInput};\nuse embassy_stm32::flash::{Flash, WRITE_SIZE};\nuse embassy_stm32::gpio::{Level, Output, Pull, Speed};\nuse embassy_stm32::{bind_interrupts, interrupt};\nuse embassy_sync::mutex::Mutex;\nuse panic_reset as _;\n\nbind_interrupts!(\n    pub struct Irqs{\n        EXTI15_10 => exti::InterruptHandler<interrupt::typelevel::EXTI15_10>;\n});\n\n#[cfg(feature = \"skip-include\")]\nstatic APP_B: &[u8] = &[0, 1, 2, 3];\n#[cfg(not(feature = \"skip-include\"))]\nstatic APP_B: &[u8] = include_bytes!(\"../../b.bin\");\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let p = embassy_stm32::init(Default::default());\n    let flash = Flash::new_blocking(p.FLASH);\n    let flash = Mutex::new(BlockingAsync::new(flash));\n\n    let mut button = ExtiInput::new(p.PC13, p.EXTI13, Pull::Up, Irqs);\n\n    let mut led = Output::new(p.PA5, Level::Low, Speed::Low);\n    led.set_high();\n\n    let config = FirmwareUpdaterConfig::from_linkerfile(&flash, &flash);\n    let mut magic = AlignedBuffer([0; WRITE_SIZE]);\n    let mut updater = FirmwareUpdater::new(config, &mut magic.0);\n    button.wait_for_falling_edge().await;\n    let mut offset = 0;\n    for chunk in APP_B.chunks(2048) {\n        let mut buf: [u8; 2048] = [0; 2048];\n        buf[..chunk.len()].copy_from_slice(chunk);\n        updater.write_firmware(offset, &buf).await.unwrap();\n        offset += chunk.len();\n    }\n    updater.mark_updated().await.unwrap();\n    led.set_low();\n    cortex_m::peripheral::SCB::sys_reset();\n}\n"
  },
  {
    "path": "examples/boot/application/stm32f3/src/bin/b.rs",
    "content": "#![no_std]\n#![no_main]\n\n#[cfg(feature = \"defmt\")]\nuse defmt_rtt::*;\nuse embassy_executor::Spawner;\nuse embassy_stm32::gpio::{Level, Output, Speed};\nuse embassy_time::Timer;\nuse panic_reset as _;\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let p = embassy_stm32::init(Default::default());\n    let mut led = Output::new(p.PA5, Level::High, Speed::Low);\n\n    loop {\n        led.set_high();\n        Timer::after_millis(500).await;\n\n        led.set_low();\n        Timer::after_millis(500).await;\n    }\n}\n"
  },
  {
    "path": "examples/boot/application/stm32f7/.cargo/config.toml",
    "content": "[target.'cfg(all(target_arch = \"arm\", target_os = \"none\"))']\n# replace STM32F429ZITx with your chip as listed in `probe-rs chip list`\nrunner = \"probe-rs run --chip STM32F767ZITx\"\n\n[build]\ntarget = \"thumbv7em-none-eabihf\"\n\n[env]\nDEFMT_LOG = \"trace\"\n"
  },
  {
    "path": "examples/boot/application/stm32f7/Cargo.toml",
    "content": "[package]\nedition = \"2024\"\nname = \"embassy-boot-stm32f7-examples\"\nversion = \"0.1.0\"\nlicense = \"MIT OR Apache-2.0\"\npublish = false\n\n[dependencies]\nembassy-sync = { version = \"0.8.0\", path = \"../../../../embassy-sync\" }\nembassy-executor = { version = \"0.10.0\", path = \"../../../../embassy-executor\", features = [\"platform-cortex-m\", \"executor-thread\"] }\nembassy-time = { version = \"0.5.1\", path = \"../../../../embassy-time\", features = [ \"tick-hz-32_768\"] }\nembassy-stm32 = { version = \"0.6.0\", path = \"../../../../embassy-stm32\", features = [\"stm32f767zi\", \"time-driver-any\", \"exti\", \"single-bank\"]  }\nembassy-boot-stm32 = { version = \"0.8.0\", path = \"../../../../embassy-boot-stm32\", features = [] }\nembassy-embedded-hal = { version = \"0.6.0\", path = \"../../../../embassy-embedded-hal\" }\n\ndefmt = { version = \"1.0.1\", optional = true }\ndefmt-rtt = { version = \"1.0.0\", optional = true }\npanic-reset = { version = \"0.1.1\" }\nembedded-hal = { version = \"0.2.6\" }\nembedded-storage = \"0.3.1\"\n\ncortex-m = { version = \"0.7.6\", features = [\"inline-asm\", \"critical-section-single-core\"] }\ncortex-m-rt = \"0.7.0\"\n\n[features]\ndefmt = [\n      \"dep:defmt\",\n      \"dep:defmt-rtt\",\n      \"embassy-stm32/defmt\",\n      \"embassy-boot-stm32/defmt\",\n      \"embassy-sync/defmt\",\n]\nskip-include = []\n\n[package.metadata.embassy]\nbuild = [\n  { target = \"thumbv7em-none-eabi\", features = [\"skip-include\"], artifact-dir = \"out/examples/boot/stm32f7\" }\n]\n"
  },
  {
    "path": "examples/boot/application/stm32f7/README.md",
    "content": "# Examples using bootloader\n\nExample for STM32F7 demonstrating the bootloader. The example consists of application binaries, 'a'\nwhich allows you to press a button to start the DFU process, and 'b' which is the updated\napplication.\n\n\n## Prerequisites\n\n* `cargo-binutils`\n* `cargo-flash`\n* `embassy-boot-stm32`\n\n## Usage\n\n```\n# Flash bootloader\n./flash-boot.sh\n# Build 'b'\ncargo build --release --bin b\n# Generate binary for 'b'\ncargo objcopy --release --bin b -- -O binary b.bin\n```\n\n# Flash `a` (which includes b.bin)\n\n```\ncargo flash --release --bin a --chip STM32F767ZITx\n```\n"
  },
  {
    "path": "examples/boot/application/stm32f7/build.rs",
    "content": "//! This build script copies the `memory.x` file from the crate root into\n//! a directory where the linker can always find it at build time.\n//! For many projects this is optional, as the linker always searches the\n//! project root directory -- wherever `Cargo.toml` is. However, if you\n//! are using a workspace or have a more complicated build setup, this\n//! build script becomes required. Additionally, by requesting that\n//! Cargo re-run the build script whenever `memory.x` is changed,\n//! updating `memory.x` ensures a rebuild of the application with the\n//! new memory settings.\n\nuse std::env;\nuse std::fs::File;\nuse std::io::Write;\nuse std::path::PathBuf;\n\nfn main() {\n    // Put `memory.x` in our output directory and ensure it's\n    // on the linker search path.\n    let out = &PathBuf::from(env::var_os(\"OUT_DIR\").unwrap());\n    File::create(out.join(\"memory.x\"))\n        .unwrap()\n        .write_all(include_bytes!(\"memory.x\"))\n        .unwrap();\n    println!(\"cargo:rustc-link-search={}\", out.display());\n\n    // By default, Cargo will re-run a build script whenever\n    // any file in the project changes. By specifying `memory.x`\n    // here, we ensure the build script is only re-run when\n    // `memory.x` is changed.\n    println!(\"cargo:rerun-if-changed=memory.x\");\n\n    println!(\"cargo:rustc-link-arg-bins=--nmagic\");\n    println!(\"cargo:rustc-link-arg-bins=-Tlink.x\");\n    if env::var(\"CARGO_FEATURE_DEFMT\").is_ok() {\n        println!(\"cargo:rustc-link-arg-bins=-Tdefmt.x\");\n    }\n}\n"
  },
  {
    "path": "examples/boot/application/stm32f7/flash-boot.sh",
    "content": "#!/bin/bash\nmv ../../bootloader/stm32/memory.x ../../bootloader/stm32/memory-old.x\ncp memory-bl.x ../../bootloader/stm32/memory.x\n\ncargo flash --manifest-path ../../bootloader/stm32/Cargo.toml --release --features embassy-stm32/stm32f767zi --chip STM32F767ZITx --target thumbv7em-none-eabihf\n\nrm ../../bootloader/stm32/memory.x\nmv ../../bootloader/stm32/memory-old.x ../../bootloader/stm32/memory.x\n"
  },
  {
    "path": "examples/boot/application/stm32f7/memory-bl.x",
    "content": "MEMORY\n{\n  /* NOTE 1 K = 1 KiBi = 1024 bytes */\n  FLASH                             : ORIGIN = 0x08000000, LENGTH = 256K\n  BOOTLOADER_STATE                  : ORIGIN = 0x08040000, LENGTH = 256K\n  ACTIVE                            : ORIGIN = 0x08080000, LENGTH = 256K\n  DFU                               : ORIGIN = 0x080c0000, LENGTH = 512K\n  RAM                         (rwx) : ORIGIN = 0x20000000, LENGTH = 368K + 16K\n}\n\n__bootloader_state_start = ORIGIN(BOOTLOADER_STATE) - ORIGIN(FLASH);\n__bootloader_state_end = ORIGIN(BOOTLOADER_STATE) + LENGTH(BOOTLOADER_STATE) - ORIGIN(FLASH);\n\n__bootloader_active_start = ORIGIN(ACTIVE) - ORIGIN(FLASH);\n__bootloader_active_end = ORIGIN(ACTIVE) + LENGTH(ACTIVE) - ORIGIN(FLASH);\n\n__bootloader_dfu_start = ORIGIN(DFU) - ORIGIN(FLASH);\n__bootloader_dfu_end = ORIGIN(DFU) + LENGTH(DFU) - ORIGIN(FLASH);\n"
  },
  {
    "path": "examples/boot/application/stm32f7/memory.x",
    "content": "MEMORY\n{\n  /* NOTE 1 K = 1 KiBi = 1024 bytes */\n  BOOTLOADER                        : ORIGIN = 0x08000000, LENGTH = 256K\n  BOOTLOADER_STATE                  : ORIGIN = 0x08040000, LENGTH = 256K\n  FLASH                             : ORIGIN = 0x08080000, LENGTH = 256K\n  DFU                               : ORIGIN = 0x080c0000, LENGTH = 512K\n  RAM                         (rwx) : ORIGIN = 0x20000000, LENGTH = 368K + 16K\n}\n\n__bootloader_state_start = ORIGIN(BOOTLOADER_STATE) - ORIGIN(BOOTLOADER);\n__bootloader_state_end = ORIGIN(BOOTLOADER_STATE) + LENGTH(BOOTLOADER_STATE) - ORIGIN(BOOTLOADER);\n\n__bootloader_dfu_start = ORIGIN(DFU) - ORIGIN(BOOTLOADER);\n__bootloader_dfu_end = ORIGIN(DFU) + LENGTH(DFU) - ORIGIN(BOOTLOADER);\n"
  },
  {
    "path": "examples/boot/application/stm32f7/src/bin/a.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse core::cell::RefCell;\n\n#[cfg(feature = \"defmt\")]\nuse defmt_rtt::*;\nuse embassy_boot_stm32::{AlignedBuffer, BlockingFirmwareUpdater, FirmwareUpdaterConfig};\nuse embassy_executor::Spawner;\nuse embassy_stm32::exti::{self, ExtiInput};\nuse embassy_stm32::flash::{Flash, WRITE_SIZE};\nuse embassy_stm32::gpio::{Level, Output, Pull, Speed};\nuse embassy_stm32::{bind_interrupts, interrupt};\nuse embassy_sync::blocking_mutex::Mutex;\nuse embedded_storage::nor_flash::NorFlash;\nuse panic_reset as _;\n\n#[cfg(feature = \"skip-include\")]\nstatic APP_B: &[u8] = &[0, 1, 2, 3];\n#[cfg(not(feature = \"skip-include\"))]\nstatic APP_B: &[u8] = include_bytes!(\"../../b.bin\");\n\nbind_interrupts!(\n    pub struct Irqs{\n        EXTI15_10 => exti::InterruptHandler<interrupt::typelevel::EXTI15_10>;\n});\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let p = embassy_stm32::init(Default::default());\n    let flash = Flash::new_blocking(p.FLASH);\n    let flash = Mutex::new(RefCell::new(flash));\n\n    let mut button = ExtiInput::new(p.PC13, p.EXTI13, Pull::Down, Irqs);\n\n    let mut led = Output::new(p.PB7, Level::Low, Speed::Low);\n    led.set_high();\n\n    let config = FirmwareUpdaterConfig::from_linkerfile_blocking(&flash, &flash);\n    let mut magic = AlignedBuffer([0; WRITE_SIZE]);\n    let mut updater = BlockingFirmwareUpdater::new(config, &mut magic.0);\n    let writer = updater.prepare_update().unwrap();\n    button.wait_for_rising_edge().await;\n    let mut offset = 0;\n    let mut buf = AlignedBuffer([0; 4096]);\n    for chunk in APP_B.chunks(4096) {\n        buf.as_mut()[..chunk.len()].copy_from_slice(chunk);\n        writer.write(offset, buf.as_ref()).unwrap();\n        offset += chunk.len() as u32;\n    }\n    updater.mark_updated().unwrap();\n    led.set_low();\n    cortex_m::peripheral::SCB::sys_reset();\n}\n"
  },
  {
    "path": "examples/boot/application/stm32f7/src/bin/b.rs",
    "content": "#![no_std]\n#![no_main]\n\n#[cfg(feature = \"defmt\")]\nuse defmt_rtt::*;\nuse embassy_executor::Spawner;\nuse embassy_stm32::gpio::{Level, Output, Speed};\nuse embassy_time::Timer;\nuse panic_reset as _;\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let p = embassy_stm32::init(Default::default());\n    Timer::after_millis(300).await;\n    let mut led = Output::new(p.PB7, Level::High, Speed::Low);\n    led.set_high();\n\n    loop {\n        led.set_high();\n        Timer::after_millis(500).await;\n\n        led.set_low();\n        Timer::after_millis(500).await;\n    }\n}\n"
  },
  {
    "path": "examples/boot/application/stm32h7/.cargo/config.toml",
    "content": "[target.'cfg(all(target_arch = \"arm\", target_os = \"none\"))']\n# replace STM32F429ZITx with your chip as listed in `probe-rs chip list`\nrunner = \"probe-rs run --chip STM32H743ZITx\"\n\n[build]\ntarget = \"thumbv7em-none-eabihf\"\n\n[env]\nDEFMT_LOG = \"trace\"\n"
  },
  {
    "path": "examples/boot/application/stm32h7/Cargo.toml",
    "content": "[package]\nedition = \"2024\"\nname = \"embassy-boot-stm32h7-examples\"\nversion = \"0.1.0\"\nlicense = \"MIT OR Apache-2.0\"\npublish = false\n\n[dependencies]\nembassy-sync = { version = \"0.8.0\", path = \"../../../../embassy-sync\" }\nembassy-executor = { version = \"0.10.0\", path = \"../../../../embassy-executor\", features = [\"platform-cortex-m\", \"executor-thread\"] }\nembassy-time = { version = \"0.5.1\", path = \"../../../../embassy-time\", features = [ \"tick-hz-32_768\"] }\nembassy-stm32 = { version = \"0.6.0\", path = \"../../../../embassy-stm32\", features = [\"stm32h743zi\", \"time-driver-any\", \"exti\"]  }\nembassy-boot-stm32 = { version = \"0.8.0\", path = \"../../../../embassy-boot-stm32\", features = [] }\nembassy-embedded-hal = { version = \"0.6.0\", path = \"../../../../embassy-embedded-hal\" }\n\ndefmt = { version = \"1.0.1\", optional = true }\ndefmt-rtt = { version = \"1.0.0\", optional = true }\npanic-reset = { version = \"0.1.1\" }\nembedded-hal = { version = \"0.2.6\" }\nembedded-storage = \"0.3.1\"\n\ncortex-m = { version = \"0.7.6\", features = [\"inline-asm\", \"critical-section-single-core\"] }\ncortex-m-rt = \"0.7.0\"\n\n[features]\ndefmt = [\n      \"dep:defmt\",\n      \"dep:defmt-rtt\",\n      \"embassy-stm32/defmt\",\n      \"embassy-boot-stm32/defmt\",\n      \"embassy-sync/defmt\",\n]\nskip-include = []\n\n[package.metadata.embassy]\nbuild = [\n  { target = \"thumbv7em-none-eabi\", features = [\"skip-include\"], artifact-dir = \"out/examples/boot/stm32h7\" }\n]\n"
  },
  {
    "path": "examples/boot/application/stm32h7/README.md",
    "content": "# Examples using bootloader\n\nExample for STM32H7 demonstrating the bootloader. The example consists of application binaries, 'a'\nwhich allows you to press a button to start the DFU process, and 'b' which is the updated\napplication.\n\n\n## Prerequisites\n\n* `cargo-binutils`\n* `cargo-flash`\n* `embassy-boot-stm32`\n\n## Usage\n\n```\n# Flash bootloader\n./flash-boot.sh\n# Build 'b'\ncargo build --release --bin b\n# Generate binary for 'b'\ncargo objcopy --release --bin b -- -O binary b.bin\n```\n\n# Flash `a` (which includes b.bin)\n\n```\ncargo flash --release --bin a --chip STM32H743ZITx\n```\n"
  },
  {
    "path": "examples/boot/application/stm32h7/build.rs",
    "content": "//! This build script copies the `memory.x` file from the crate root into\n//! a directory where the linker can always find it at build time.\n//! For many projects this is optional, as the linker always searches the\n//! project root directory -- wherever `Cargo.toml` is. However, if you\n//! are using a workspace or have a more complicated build setup, this\n//! build script becomes required. Additionally, by requesting that\n//! Cargo re-run the build script whenever `memory.x` is changed,\n//! updating `memory.x` ensures a rebuild of the application with the\n//! new memory settings.\n\nuse std::env;\nuse std::fs::File;\nuse std::io::Write;\nuse std::path::PathBuf;\n\nfn main() {\n    // Put `memory.x` in our output directory and ensure it's\n    // on the linker search path.\n    let out = &PathBuf::from(env::var_os(\"OUT_DIR\").unwrap());\n    File::create(out.join(\"memory.x\"))\n        .unwrap()\n        .write_all(include_bytes!(\"memory.x\"))\n        .unwrap();\n    println!(\"cargo:rustc-link-search={}\", out.display());\n\n    // By default, Cargo will re-run a build script whenever\n    // any file in the project changes. By specifying `memory.x`\n    // here, we ensure the build script is only re-run when\n    // `memory.x` is changed.\n    println!(\"cargo:rerun-if-changed=memory.x\");\n\n    println!(\"cargo:rustc-link-arg-bins=--nmagic\");\n    println!(\"cargo:rustc-link-arg-bins=-Tlink.x\");\n    if env::var(\"CARGO_FEATURE_DEFMT\").is_ok() {\n        println!(\"cargo:rustc-link-arg-bins=-Tdefmt.x\");\n    }\n}\n"
  },
  {
    "path": "examples/boot/application/stm32h7/flash-boot.sh",
    "content": "#!/bin/bash\nprobe-rs erase --chip STM32H743ZITx\nmv ../../bootloader/stm32/memory.x ../../bootloader/stm32/memory-old.x\ncp memory-bl.x ../../bootloader/stm32/memory.x\n\ncargo flash --manifest-path ../../bootloader/stm32/Cargo.toml --release --features embassy-stm32/stm32h743zi --chip STM32H743ZITx --target thumbv7em-none-eabihf\n\nrm ../../bootloader/stm32/memory.x\nmv ../../bootloader/stm32/memory-old.x ../../bootloader/stm32/memory.x\n"
  },
  {
    "path": "examples/boot/application/stm32h7/memory-bl.x",
    "content": "MEMORY\n{\n  /* NOTE 1 K = 1 KiBi = 1024 bytes */\n  FLASH                             : ORIGIN = 0x08000000, LENGTH = 128K\n  BOOTLOADER_STATE                  : ORIGIN = 0x08020000, LENGTH = 128K\n  ACTIVE                            : ORIGIN = 0x08040000, LENGTH = 128K\n  DFU                               : ORIGIN = 0x08100000, LENGTH = 512K\n  RAM                         (rwx) : ORIGIN = 0x24000000, LENGTH = 368K\n}\n\n__bootloader_state_start = ORIGIN(BOOTLOADER_STATE) - ORIGIN(FLASH);\n__bootloader_state_end = ORIGIN(BOOTLOADER_STATE) + LENGTH(BOOTLOADER_STATE) - ORIGIN(FLASH);\n\n__bootloader_active_start = ORIGIN(ACTIVE) - ORIGIN(FLASH);\n__bootloader_active_end = ORIGIN(ACTIVE) + LENGTH(ACTIVE) - ORIGIN(FLASH);\n\n__bootloader_dfu_start = ORIGIN(DFU) - ORIGIN(FLASH);\n__bootloader_dfu_end = ORIGIN(DFU) + LENGTH(DFU) - ORIGIN(FLASH);\n"
  },
  {
    "path": "examples/boot/application/stm32h7/memory.x",
    "content": "MEMORY\n{\n  /* NOTE 1 K = 1 KiBi = 1024 bytes */\n  BOOTLOADER                        : ORIGIN = 0x08000000, LENGTH = 128K\n  BOOTLOADER_STATE                  : ORIGIN = 0x08020000, LENGTH = 128K\n  FLASH                             : ORIGIN = 0x08040000, LENGTH = 256K\n  DFU                               : ORIGIN = 0x08100000, LENGTH = 512K\n  RAM                         (rwx) : ORIGIN = 0x24000000, LENGTH = 368K\n}\n\n__bootloader_state_start = ORIGIN(BOOTLOADER_STATE) - ORIGIN(BOOTLOADER);\n__bootloader_state_end = ORIGIN(BOOTLOADER_STATE) + LENGTH(BOOTLOADER_STATE) - ORIGIN(BOOTLOADER);\n\n__bootloader_dfu_start = ORIGIN(DFU) - ORIGIN(BOOTLOADER);\n__bootloader_dfu_end = ORIGIN(DFU) + LENGTH(DFU) - ORIGIN(BOOTLOADER);\n"
  },
  {
    "path": "examples/boot/application/stm32h7/src/bin/a.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse core::cell::RefCell;\n\n#[cfg(feature = \"defmt\")]\nuse defmt_rtt::*;\nuse embassy_boot_stm32::{AlignedBuffer, BlockingFirmwareUpdater, FirmwareUpdaterConfig};\nuse embassy_executor::Spawner;\nuse embassy_stm32::exti::{self, ExtiInput};\nuse embassy_stm32::flash::{Flash, WRITE_SIZE};\nuse embassy_stm32::gpio::{Level, Output, Pull, Speed};\nuse embassy_stm32::{bind_interrupts, interrupt};\nuse embassy_sync::blocking_mutex::Mutex;\nuse embedded_storage::nor_flash::NorFlash;\nuse panic_reset as _;\n\nbind_interrupts!(\n    pub struct Irqs{\n        EXTI15_10 => exti::InterruptHandler<interrupt::typelevel::EXTI15_10>;\n});\n\n#[cfg(feature = \"skip-include\")]\nstatic APP_B: &[u8] = &[0, 1, 2, 3];\n#[cfg(not(feature = \"skip-include\"))]\nstatic APP_B: &[u8] = include_bytes!(\"../../b.bin\");\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let p = embassy_stm32::init(Default::default());\n    let flash = Flash::new_blocking(p.FLASH);\n    let flash = Mutex::new(RefCell::new(flash));\n\n    let mut button = ExtiInput::new(p.PC13, p.EXTI13, Pull::Down, Irqs);\n\n    let mut led = Output::new(p.PB14, Level::Low, Speed::Low);\n    led.set_high();\n\n    let config = FirmwareUpdaterConfig::from_linkerfile_blocking(&flash, &flash);\n    let mut magic = AlignedBuffer([0; WRITE_SIZE]);\n    let mut updater = BlockingFirmwareUpdater::new(config, &mut magic.0);\n    let writer = updater.prepare_update().unwrap();\n    button.wait_for_rising_edge().await;\n    let mut offset = 0;\n    let mut buf = AlignedBuffer([0; 4096]);\n    for chunk in APP_B.chunks(4096) {\n        buf.as_mut()[..chunk.len()].copy_from_slice(chunk);\n        writer.write(offset, buf.as_ref()).unwrap();\n        offset += chunk.len() as u32;\n    }\n    updater.mark_updated().unwrap();\n    led.set_low();\n    cortex_m::peripheral::SCB::sys_reset();\n}\n"
  },
  {
    "path": "examples/boot/application/stm32h7/src/bin/b.rs",
    "content": "#![no_std]\n#![no_main]\n\n#[cfg(feature = \"defmt\")]\nuse defmt_rtt::*;\nuse embassy_executor::Spawner;\nuse embassy_stm32::gpio::{Level, Output, Speed};\nuse embassy_time::Timer;\nuse panic_reset as _;\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let p = embassy_stm32::init(Default::default());\n    Timer::after_millis(300).await;\n    let mut led = Output::new(p.PB14, Level::High, Speed::Low);\n    led.set_high();\n\n    loop {\n        led.set_high();\n        Timer::after_millis(500).await;\n\n        led.set_low();\n        Timer::after_millis(500).await;\n    }\n}\n"
  },
  {
    "path": "examples/boot/application/stm32l0/.cargo/config.toml",
    "content": "[target.'cfg(all(target_arch = \"arm\", target_os = \"none\"))']\n# replace your chip as listed in `probe-rs chip list`\nrunner = \"probe-rs run --chip STM32L072CZTx\"\n\n[build]\ntarget = \"thumbv6m-none-eabi\"\n\n[env]\nDEFMT_LOG = \"trace\"\n"
  },
  {
    "path": "examples/boot/application/stm32l0/Cargo.toml",
    "content": "[package]\nedition = \"2024\"\nname = \"embassy-boot-stm32l0-examples\"\nversion = \"0.1.0\"\nlicense = \"MIT OR Apache-2.0\"\npublish = false\n\n[dependencies]\nembassy-sync = { version = \"0.8.0\", path = \"../../../../embassy-sync\" }\nembassy-executor = { version = \"0.10.0\", path = \"../../../../embassy-executor\", features = [\"platform-cortex-m\", \"executor-thread\"] }\nembassy-time = { version = \"0.5.1\", path = \"../../../../embassy-time\", features = [ \"tick-hz-32_768\"] }\nembassy-stm32 = { version = \"0.6.0\", path = \"../../../../embassy-stm32\", features = [\"stm32l072cz\", \"time-driver-any\", \"exti\", \"memory-x\"]  }\nembassy-boot-stm32 = { version = \"0.8.0\", path = \"../../../../embassy-boot-stm32\", features = [] }\nembassy-embedded-hal = { version = \"0.6.0\", path = \"../../../../embassy-embedded-hal\" }\n\ndefmt = { version = \"1.0.1\", optional = true }\ndefmt-rtt = { version = \"1.0.0\", optional = true }\npanic-reset = { version = \"0.1.1\" }\nembedded-hal = { version = \"0.2.6\" }\n\ncortex-m = { version = \"0.7.6\", features = [\"inline-asm\", \"critical-section-single-core\"] }\ncortex-m-rt = \"0.7.0\"\n\n[features]\ndefmt = [\n      \"dep:defmt\",\n      \"dep:defmt-rtt\",\n      \"embassy-stm32/defmt\",\n      \"embassy-boot-stm32/defmt\",\n      \"embassy-sync/defmt\",\n]\nskip-include = []\n\n[package.metadata.embassy]\nbuild = [\n  { target = \"thumbv6m-none-eabi\", features = [\"skip-include\"], artifact-dir = \"out/examples/boot/stm32l0\" }\n]\n"
  },
  {
    "path": "examples/boot/application/stm32l0/README.md",
    "content": "# Examples using bootloader\n\nExample for STM32L0 demonstrating the bootloader. The example consists of application binaries, 'a'\nwhich allows you to press a button to start the DFU process, and 'b' which is the updated\napplication.\n\n\n## Prerequisites\n\n* `cargo-binutils`\n* `cargo-flash`\n* `embassy-boot-stm32`\n\n## Usage\n\n```\n# Flash bootloader\ncargo flash --manifest-path ../../bootloader/stm32/Cargo.toml --release --features embassy-stm32/stm32l072cz --chip STM32L072CZTx\n# Build 'b'\ncargo build --release --bin b\n# Generate binary for 'b'\ncargo objcopy --release --bin b -- -O binary b.bin\n```\n\n# Flash `a` (which includes b.bin)\n\n```\ncargo flash --release --bin a --chip STM32L072CZTx\n```\n"
  },
  {
    "path": "examples/boot/application/stm32l0/build.rs",
    "content": "//! This build script copies the `memory.x` file from the crate root into\n//! a directory where the linker can always find it at build time.\n//! For many projects this is optional, as the linker always searches the\n//! project root directory -- wherever `Cargo.toml` is. However, if you\n//! are using a workspace or have a more complicated build setup, this\n//! build script becomes required. Additionally, by requesting that\n//! Cargo re-run the build script whenever `memory.x` is changed,\n//! updating `memory.x` ensures a rebuild of the application with the\n//! new memory settings.\n\nuse std::env;\nuse std::fs::File;\nuse std::io::Write;\nuse std::path::PathBuf;\n\nfn main() {\n    // Put `memory.x` in our output directory and ensure it's\n    // on the linker search path.\n    let out = &PathBuf::from(env::var_os(\"OUT_DIR\").unwrap());\n    File::create(out.join(\"memory.x\"))\n        .unwrap()\n        .write_all(include_bytes!(\"memory.x\"))\n        .unwrap();\n    println!(\"cargo:rustc-link-search={}\", out.display());\n\n    // By default, Cargo will re-run a build script whenever\n    // any file in the project changes. By specifying `memory.x`\n    // here, we ensure the build script is only re-run when\n    // `memory.x` is changed.\n    println!(\"cargo:rerun-if-changed=memory.x\");\n\n    println!(\"cargo:rustc-link-arg-bins=--nmagic\");\n    println!(\"cargo:rustc-link-arg-bins=-Tlink.x\");\n    if env::var(\"CARGO_FEATURE_DEFMT\").is_ok() {\n        println!(\"cargo:rustc-link-arg-bins=-Tdefmt.x\");\n    }\n}\n"
  },
  {
    "path": "examples/boot/application/stm32l0/memory.x",
    "content": "MEMORY\n{\n  /* NOTE 1 K = 1 KiBi = 1024 bytes */\n  BOOTLOADER                        : ORIGIN = 0x08000000, LENGTH = 24K\n  BOOTLOADER_STATE                  : ORIGIN = 0x08006000, LENGTH = 4K\n  FLASH                             : ORIGIN = 0x08008000, LENGTH = 64K\n  DFU                               : ORIGIN = 0x08018000, LENGTH = 66K\n  RAM                         (rwx) : ORIGIN = 0x20000000, LENGTH = 16K\n}\n\n__bootloader_state_start = ORIGIN(BOOTLOADER_STATE) - ORIGIN(BOOTLOADER);\n__bootloader_state_end = ORIGIN(BOOTLOADER_STATE) + LENGTH(BOOTLOADER_STATE) - ORIGIN(BOOTLOADER);\n\n__bootloader_dfu_start = ORIGIN(DFU) - ORIGIN(BOOTLOADER);\n__bootloader_dfu_end = ORIGIN(DFU) + LENGTH(DFU) - ORIGIN(BOOTLOADER);\n"
  },
  {
    "path": "examples/boot/application/stm32l0/src/bin/a.rs",
    "content": "#![no_std]\n#![no_main]\n\n#[cfg(feature = \"defmt\")]\nuse defmt_rtt::*;\nuse embassy_boot_stm32::{AlignedBuffer, FirmwareUpdater, FirmwareUpdaterConfig};\nuse embassy_embedded_hal::adapter::BlockingAsync;\nuse embassy_executor::Spawner;\nuse embassy_stm32::exti::{self, ExtiInput};\nuse embassy_stm32::flash::{Flash, WRITE_SIZE};\nuse embassy_stm32::gpio::{Level, Output, Pull, Speed};\nuse embassy_stm32::{bind_interrupts, interrupt};\nuse embassy_sync::mutex::Mutex;\nuse embassy_time::Timer;\nuse panic_reset as _;\n\nbind_interrupts!(\n    pub struct Irqs{\n        EXTI2_3 => exti::InterruptHandler<interrupt::typelevel::EXTI2_3>;\n});\n\n#[cfg(feature = \"skip-include\")]\nstatic APP_B: &[u8] = &[0, 1, 2, 3];\n#[cfg(not(feature = \"skip-include\"))]\nstatic APP_B: &[u8] = include_bytes!(\"../../b.bin\");\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let p = embassy_stm32::init(Default::default());\n    let flash = Flash::new_blocking(p.FLASH);\n    let flash = Mutex::new(BlockingAsync::new(flash));\n\n    let mut button = ExtiInput::new(p.PB2, p.EXTI2, Pull::Up, Irqs);\n\n    let mut led = Output::new(p.PB5, Level::Low, Speed::Low);\n\n    led.set_high();\n\n    let config = FirmwareUpdaterConfig::from_linkerfile(&flash, &flash);\n    let mut magic = AlignedBuffer([0; WRITE_SIZE]);\n    let mut updater = FirmwareUpdater::new(config, &mut magic.0);\n    button.wait_for_falling_edge().await;\n    let mut offset = 0;\n    for chunk in APP_B.chunks(128) {\n        let mut buf: [u8; 128] = [0; 128];\n        buf[..chunk.len()].copy_from_slice(chunk);\n        updater.write_firmware(offset, &buf).await.unwrap();\n        offset += chunk.len();\n    }\n\n    updater.mark_updated().await.unwrap();\n    led.set_low();\n    Timer::after_secs(1).await;\n    cortex_m::peripheral::SCB::sys_reset();\n}\n"
  },
  {
    "path": "examples/boot/application/stm32l0/src/bin/b.rs",
    "content": "#![no_std]\n#![no_main]\n\n#[cfg(feature = \"defmt\")]\nuse defmt_rtt::*;\nuse embassy_executor::Spawner;\nuse embassy_stm32::gpio::{Level, Output, Speed};\nuse embassy_time::Timer;\nuse panic_reset as _;\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let p = embassy_stm32::init(Default::default());\n    let mut led = Output::new(p.PB6, Level::High, Speed::Low);\n\n    loop {\n        led.set_high();\n        Timer::after_millis(500).await;\n\n        led.set_low();\n        Timer::after_millis(500).await;\n    }\n}\n"
  },
  {
    "path": "examples/boot/application/stm32l1/.cargo/config.toml",
    "content": "[target.'cfg(all(target_arch = \"arm\", target_os = \"none\"))']\n# replace your chip as listed in `probe-rs chip list`\nrunner = \"probe-rs run --chip STM32L151CBxxA\"\n\n[build]\ntarget = \"thumbv7m-none-eabi\"\n\n[env]\nDEFMT_LOG = \"trace\"\n"
  },
  {
    "path": "examples/boot/application/stm32l1/Cargo.toml",
    "content": "[package]\nedition = \"2024\"\nname = \"embassy-boot-stm32l1-examples\"\nversion = \"0.1.0\"\nlicense = \"MIT OR Apache-2.0\"\npublish = false\n\n[dependencies]\nembassy-sync = { version = \"0.8.0\", path = \"../../../../embassy-sync\" }\nembassy-executor = { version = \"0.10.0\", path = \"../../../../embassy-executor\", features = [\"platform-cortex-m\", \"executor-thread\"] }\nembassy-time = { version = \"0.5.1\", path = \"../../../../embassy-time\", features = [ \"tick-hz-32_768\"] }\nembassy-stm32 = { version = \"0.6.0\", path = \"../../../../embassy-stm32\", features = [\"stm32l151cb-a\", \"time-driver-any\", \"exti\"]  }\nembassy-boot-stm32 = { version = \"0.8.0\", path = \"../../../../embassy-boot-stm32\", features = [] }\nembassy-embedded-hal = { version = \"0.6.0\", path = \"../../../../embassy-embedded-hal\" }\n\ndefmt = { version = \"1.0.1\", optional = true }\ndefmt-rtt = { version = \"1.0.0\", optional = true }\npanic-reset = { version = \"0.1.1\" }\nembedded-hal = { version = \"0.2.6\" }\n\ncortex-m = { version = \"0.7.6\", features = [\"inline-asm\", \"critical-section-single-core\"] }\ncortex-m-rt = \"0.7.0\"\n\n[features]\ndefmt = [\n      \"dep:defmt\",\n      \"dep:defmt-rtt\",\n      \"embassy-stm32/defmt\",\n      \"embassy-boot-stm32/defmt\",\n      \"embassy-sync/defmt\",\n]\nskip-include = []\n\n[package.metadata.embassy]\nbuild = [\n  { target = \"thumbv7m-none-eabi\", features = [\"skip-include\"], artifact-dir = \"out/examples/boot/stm32l1\" }\n]\n"
  },
  {
    "path": "examples/boot/application/stm32l1/README.md",
    "content": "# Examples using bootloader\n\nExample for STM32L1 demonstrating the bootloader. The example consists of application binaries, 'a'\nwhich allows you to press a button to start the DFU process, and 'b' which is the updated\napplication.\n\n\n## Prerequisites\n\n* `cargo-binutils`\n* `cargo-flash`\n* `embassy-boot-stm32`\n\n## Usage\n\n```\n# Flash bootloader\ncargo flash --manifest-path ../../bootloader/stm32/Cargo.toml --release --features embassy-stm32/stm32l151cb-a --chip STM32L151CBxxA\n# Build 'b'\ncargo build --release --bin b\n# Generate binary for 'b'\ncargo objcopy --release --bin b -- -O binary b.bin\n```\n\n# Flash `a` (which includes b.bin)\n\n```\ncargo flash --release --bin a --chip STM32L151CBxxA\n```\n"
  },
  {
    "path": "examples/boot/application/stm32l1/build.rs",
    "content": "//! This build script copies the `memory.x` file from the crate root into\n//! a directory where the linker can always find it at build time.\n//! For many projects this is optional, as the linker always searches the\n//! project root directory -- wherever `Cargo.toml` is. However, if you\n//! are using a workspace or have a more complicated build setup, this\n//! build script becomes required. Additionally, by requesting that\n//! Cargo re-run the build script whenever `memory.x` is changed,\n//! updating `memory.x` ensures a rebuild of the application with the\n//! new memory settings.\n\nuse std::env;\nuse std::fs::File;\nuse std::io::Write;\nuse std::path::PathBuf;\n\nfn main() {\n    // Put `memory.x` in our output directory and ensure it's\n    // on the linker search path.\n    let out = &PathBuf::from(env::var_os(\"OUT_DIR\").unwrap());\n    File::create(out.join(\"memory.x\"))\n        .unwrap()\n        .write_all(include_bytes!(\"memory.x\"))\n        .unwrap();\n    println!(\"cargo:rustc-link-search={}\", out.display());\n\n    // By default, Cargo will re-run a build script whenever\n    // any file in the project changes. By specifying `memory.x`\n    // here, we ensure the build script is only re-run when\n    // `memory.x` is changed.\n    println!(\"cargo:rerun-if-changed=memory.x\");\n\n    println!(\"cargo:rustc-link-arg-bins=--nmagic\");\n    println!(\"cargo:rustc-link-arg-bins=-Tlink.x\");\n    if env::var(\"CARGO_FEATURE_DEFMT\").is_ok() {\n        println!(\"cargo:rustc-link-arg-bins=-Tdefmt.x\");\n    }\n}\n"
  },
  {
    "path": "examples/boot/application/stm32l1/memory.x",
    "content": "MEMORY\n{\n  /* NOTE 1 K = 1 KiBi = 1024 bytes */\n  BOOTLOADER                        : ORIGIN = 0x08000000, LENGTH = 24K\n  BOOTLOADER_STATE                  : ORIGIN = 0x08006000, LENGTH = 4K\n  FLASH                             : ORIGIN = 0x08008000, LENGTH = 46K\n  DFU                               : ORIGIN = 0x08013800, LENGTH = 54K\n  RAM                         (rwx) : ORIGIN = 0x20000000, LENGTH = 16K\n}\n\n__bootloader_state_start = ORIGIN(BOOTLOADER_STATE) - ORIGIN(BOOTLOADER);\n__bootloader_state_end = ORIGIN(BOOTLOADER_STATE) + LENGTH(BOOTLOADER_STATE) - ORIGIN(BOOTLOADER);\n\n__bootloader_dfu_start = ORIGIN(DFU) - ORIGIN(BOOTLOADER);\n__bootloader_dfu_end = ORIGIN(DFU) + LENGTH(DFU) - ORIGIN(BOOTLOADER);\n"
  },
  {
    "path": "examples/boot/application/stm32l1/src/bin/a.rs",
    "content": "#![no_std]\n#![no_main]\n\n#[cfg(feature = \"defmt\")]\nuse defmt_rtt::*;\nuse embassy_boot_stm32::{AlignedBuffer, FirmwareUpdater, FirmwareUpdaterConfig};\nuse embassy_embedded_hal::adapter::BlockingAsync;\nuse embassy_executor::Spawner;\nuse embassy_stm32::exti::{self, ExtiInput};\nuse embassy_stm32::flash::{Flash, WRITE_SIZE};\nuse embassy_stm32::gpio::{Level, Output, Pull, Speed};\nuse embassy_stm32::{bind_interrupts, interrupt};\nuse embassy_sync::mutex::Mutex;\nuse embassy_time::Timer;\nuse panic_reset as _;\n\n#[cfg(feature = \"skip-include\")]\nstatic APP_B: &[u8] = &[0, 1, 2, 3];\n#[cfg(not(feature = \"skip-include\"))]\nstatic APP_B: &[u8] = include_bytes!(\"../../b.bin\");\n\nbind_interrupts!(\n    pub struct Irqs{\n        EXTI2 => exti::InterruptHandler<interrupt::typelevel::EXTI2>;\n});\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let p = embassy_stm32::init(Default::default());\n    let flash = Flash::new_blocking(p.FLASH);\n    let flash = Mutex::new(BlockingAsync::new(flash));\n\n    let mut button = ExtiInput::new(p.PB2, p.EXTI2, Pull::Up, Irqs);\n\n    let mut led = Output::new(p.PB5, Level::Low, Speed::Low);\n\n    led.set_high();\n\n    let config = FirmwareUpdaterConfig::from_linkerfile(&flash, &flash);\n    let mut magic = AlignedBuffer([0; WRITE_SIZE]);\n    let mut updater = FirmwareUpdater::new(config, &mut magic.0);\n    button.wait_for_falling_edge().await;\n    let mut offset = 0;\n    for chunk in APP_B.chunks(128) {\n        let mut buf: [u8; 128] = [0; 128];\n        buf[..chunk.len()].copy_from_slice(chunk);\n        updater.write_firmware(offset, &buf).await.unwrap();\n        offset += chunk.len();\n    }\n\n    updater.mark_updated().await.unwrap();\n    led.set_low();\n    Timer::after_secs(1).await;\n    cortex_m::peripheral::SCB::sys_reset();\n}\n"
  },
  {
    "path": "examples/boot/application/stm32l1/src/bin/b.rs",
    "content": "#![no_std]\n#![no_main]\n\n#[cfg(feature = \"defmt\")]\nuse defmt_rtt::*;\nuse embassy_executor::Spawner;\nuse embassy_stm32::gpio::{Level, Output, Speed};\nuse embassy_time::Timer;\nuse panic_reset as _;\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let p = embassy_stm32::init(Default::default());\n    let mut led = Output::new(p.PB6, Level::High, Speed::Low);\n\n    loop {\n        led.set_high();\n        Timer::after_millis(500).await;\n\n        led.set_low();\n        Timer::after_millis(500).await;\n    }\n}\n"
  },
  {
    "path": "examples/boot/application/stm32l4/.cargo/config.toml",
    "content": "[target.'cfg(all(target_arch = \"arm\", target_os = \"none\"))']\n# replace your chip as listed in `probe-rs chip list`\nrunner = \"probe-rs run --chip STM32L475VG\"\n\n[build]\ntarget = \"thumbv7em-none-eabihf\"\n\n[env]\nDEFMT_LOG = \"trace\"\n"
  },
  {
    "path": "examples/boot/application/stm32l4/Cargo.toml",
    "content": "[package]\nedition = \"2024\"\nname = \"embassy-boot-stm32l4-examples\"\nversion = \"0.1.0\"\nlicense = \"MIT OR Apache-2.0\"\npublish = false\n\n[dependencies]\nembassy-sync = { version = \"0.8.0\", path = \"../../../../embassy-sync\" }\nembassy-executor = { version = \"0.10.0\", path = \"../../../../embassy-executor\", features = [\"platform-cortex-m\", \"executor-thread\"] }\nembassy-time = { version = \"0.5.1\", path = \"../../../../embassy-time\", features = [ \"tick-hz-32_768\"] }\nembassy-stm32 = { version = \"0.6.0\", path = \"../../../../embassy-stm32\", features = [\"stm32l475vg\", \"time-driver-any\", \"exti\"]  }\nembassy-boot-stm32 = { version = \"0.8.0\", path = \"../../../../embassy-boot-stm32\", features = [] }\nembassy-embedded-hal = { version = \"0.6.0\", path = \"../../../../embassy-embedded-hal\" }\n\ndefmt = { version = \"1.0.1\", optional = true }\ndefmt-rtt = { version = \"1.0.0\", optional = true }\npanic-reset = { version = \"0.1.1\" }\nembedded-hal = { version = \"0.2.6\" }\n\ncortex-m = { version = \"0.7.6\", features = [\"inline-asm\", \"critical-section-single-core\"] }\ncortex-m-rt = \"0.7.0\"\n\n[features]\ndefmt = [\n      \"dep:defmt\",\n      \"dep:defmt-rtt\",\n      \"embassy-stm32/defmt\",\n      \"embassy-boot-stm32/defmt\",\n      \"embassy-sync/defmt\",\n]\nskip-include = []\n\n[package.metadata.embassy]\nbuild = [\n  { target = \"thumbv7em-none-eabi\", features = [\"skip-include\"], artifact-dir = \"out/examples/boot/stm32l4\" }\n]\n"
  },
  {
    "path": "examples/boot/application/stm32l4/README.md",
    "content": "# Examples using bootloader\n\nExample for STM32L4 demonstrating the bootloader. The example consists of application binaries, 'a'\nwhich allows you to press a button to start the DFU process, and 'b' which is the updated\napplication.\n\n\n## Prerequisites\n\n* `cargo-binutils`\n* `cargo-flash`\n* `embassy-boot-stm32`\n\n## Usage\n\n```\n# Flash bootloader\ncargo flash --manifest-path ../../bootloader/stm32/Cargo.toml --release --features embassy-stm32/stm32l475vg --chip STM32L475VG\n# Build 'b'\ncargo build --release --bin b\n# Generate binary for 'b'\ncargo objcopy --release --bin b -- -O binary b.bin\n```\n\n# Flash `a` (which includes b.bin)\n\n```\ncargo flash --release --bin a --chip STM32L475VG\n```\n"
  },
  {
    "path": "examples/boot/application/stm32l4/build.rs",
    "content": "//! This build script copies the `memory.x` file from the crate root into\n//! a directory where the linker can always find it at build time.\n//! For many projects this is optional, as the linker always searches the\n//! project root directory -- wherever `Cargo.toml` is. However, if you\n//! are using a workspace or have a more complicated build setup, this\n//! build script becomes required. Additionally, by requesting that\n//! Cargo re-run the build script whenever `memory.x` is changed,\n//! updating `memory.x` ensures a rebuild of the application with the\n//! new memory settings.\n\nuse std::env;\nuse std::fs::File;\nuse std::io::Write;\nuse std::path::PathBuf;\n\nfn main() {\n    // Put `memory.x` in our output directory and ensure it's\n    // on the linker search path.\n    let out = &PathBuf::from(env::var_os(\"OUT_DIR\").unwrap());\n    File::create(out.join(\"memory.x\"))\n        .unwrap()\n        .write_all(include_bytes!(\"memory.x\"))\n        .unwrap();\n    println!(\"cargo:rustc-link-search={}\", out.display());\n\n    // By default, Cargo will re-run a build script whenever\n    // any file in the project changes. By specifying `memory.x`\n    // here, we ensure the build script is only re-run when\n    // `memory.x` is changed.\n    println!(\"cargo:rerun-if-changed=memory.x\");\n\n    println!(\"cargo:rustc-link-arg-bins=--nmagic\");\n    println!(\"cargo:rustc-link-arg-bins=-Tlink.x\");\n    if env::var(\"CARGO_FEATURE_DEFMT\").is_ok() {\n        println!(\"cargo:rustc-link-arg-bins=-Tdefmt.x\");\n    }\n}\n"
  },
  {
    "path": "examples/boot/application/stm32l4/memory.x",
    "content": "MEMORY\n{\n  /* NOTE 1 K = 1 KiBi = 1024 bytes */\n  BOOTLOADER                        : ORIGIN = 0x08000000, LENGTH = 24K\n  BOOTLOADER_STATE                  : ORIGIN = 0x08006000, LENGTH = 4K\n  FLASH                             : ORIGIN = 0x08008000, LENGTH = 64K\n  DFU                               : ORIGIN = 0x08018000, LENGTH = 68K\n  RAM                         (rwx) : ORIGIN = 0x20000000, LENGTH = 32K\n}\n\n__bootloader_state_start = ORIGIN(BOOTLOADER_STATE) - ORIGIN(BOOTLOADER);\n__bootloader_state_end = ORIGIN(BOOTLOADER_STATE) + LENGTH(BOOTLOADER_STATE) - ORIGIN(BOOTLOADER);\n\n__bootloader_dfu_start = ORIGIN(DFU) - ORIGIN(BOOTLOADER);\n__bootloader_dfu_end = ORIGIN(DFU) + LENGTH(DFU) - ORIGIN(BOOTLOADER);\n"
  },
  {
    "path": "examples/boot/application/stm32l4/src/bin/a.rs",
    "content": "#![no_std]\n#![no_main]\n\n#[cfg(feature = \"defmt\")]\nuse defmt_rtt::*;\nuse embassy_boot_stm32::{AlignedBuffer, FirmwareUpdater, FirmwareUpdaterConfig};\nuse embassy_embedded_hal::adapter::BlockingAsync;\nuse embassy_executor::Spawner;\nuse embassy_stm32::exti::{self, ExtiInput};\nuse embassy_stm32::flash::{Flash, WRITE_SIZE};\nuse embassy_stm32::gpio::{Level, Output, Pull, Speed};\nuse embassy_stm32::{bind_interrupts, interrupt};\nuse embassy_sync::mutex::Mutex;\nuse panic_reset as _;\n\n#[cfg(feature = \"skip-include\")]\nstatic APP_B: &[u8] = &[0, 1, 2, 3];\n#[cfg(not(feature = \"skip-include\"))]\nstatic APP_B: &[u8] = include_bytes!(\"../../b.bin\");\n\nbind_interrupts!(\n    pub struct Irqs{\n        EXTI15_10 => exti::InterruptHandler<interrupt::typelevel::EXTI15_10>;\n});\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let p = embassy_stm32::init(Default::default());\n    let flash = Flash::new_blocking(p.FLASH);\n    let flash = Mutex::new(BlockingAsync::new(flash));\n\n    let mut button = ExtiInput::new(p.PC13, p.EXTI13, Pull::Up, Irqs);\n\n    let mut led = Output::new(p.PB14, Level::Low, Speed::Low);\n    led.set_high();\n\n    let config = FirmwareUpdaterConfig::from_linkerfile(&flash, &flash);\n    let mut magic = AlignedBuffer([0; WRITE_SIZE]);\n    let mut updater = FirmwareUpdater::new(config, &mut magic.0);\n    button.wait_for_falling_edge().await;\n    let mut offset = 0;\n    for chunk in APP_B.chunks(2048) {\n        let mut buf: [u8; 2048] = [0; 2048];\n        buf[..chunk.len()].copy_from_slice(chunk);\n        updater.write_firmware(offset, &buf).await.unwrap();\n        offset += chunk.len();\n    }\n    updater.mark_updated().await.unwrap();\n    led.set_low();\n    cortex_m::peripheral::SCB::sys_reset();\n}\n"
  },
  {
    "path": "examples/boot/application/stm32l4/src/bin/b.rs",
    "content": "#![no_std]\n#![no_main]\n\n#[cfg(feature = \"defmt\")]\nuse defmt_rtt::*;\nuse embassy_executor::Spawner;\nuse embassy_stm32::gpio::{Level, Output, Speed};\nuse embassy_time::Timer;\nuse panic_reset as _;\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let p = embassy_stm32::init(Default::default());\n    let mut led = Output::new(p.PA5, Level::High, Speed::Low);\n\n    loop {\n        led.set_high();\n        Timer::after_millis(500).await;\n\n        led.set_low();\n        Timer::after_millis(500).await;\n    }\n}\n"
  },
  {
    "path": "examples/boot/application/stm32wb-dfu/.cargo/config.toml",
    "content": "[target.'cfg(all(target_arch = \"arm\", target_os = \"none\"))']\n# replace your chip as listed in `probe-rs chip list`\nrunner = \"probe-rs run --chip STM32WLE5JCIx\"\n\n[build]\ntarget = \"thumbv7em-none-eabihf\"\n\n[env]\nDEFMT_LOG = \"trace\"\n"
  },
  {
    "path": "examples/boot/application/stm32wb-dfu/Cargo.toml",
    "content": "[package]\nedition = \"2024\"\nname = \"embassy-boot-stm32wb-dfu-examples\"\nversion = \"0.1.0\"\nlicense = \"MIT OR Apache-2.0\"\npublish = false\n\n[dependencies]\nembassy-sync = { version = \"0.8.0\", path = \"../../../../embassy-sync\" }\nembassy-executor = { version = \"0.10.0\", path = \"../../../../embassy-executor\", features = [\"platform-cortex-m\", \"executor-thread\"] }\nembassy-time = { version = \"0.5.1\", path = \"../../../../embassy-time\", features = [ \"tick-hz-32_768\"] }\nembassy-stm32 = { version = \"0.6.0\", path = \"../../../../embassy-stm32\", features = [\"stm32wb55rg\", \"time-driver-any\", \"exti\"]  }\nembassy-boot-stm32 = { version = \"0.8.0\", path = \"../../../../embassy-boot-stm32\", features = [] }\nembassy-embedded-hal = { version = \"0.6.0\", path = \"../../../../embassy-embedded-hal\" }\nembassy-usb = { version = \"0.6.0\", path = \"../../../../embassy-usb\" }\nembassy-usb-dfu = { version = \"0.3.0\", path = \"../../../../embassy-usb-dfu\", features = [\"application\", \"cortex-m\"] }\n\ndefmt = { version = \"1.0.1\", optional = true }\ndefmt-rtt = { version = \"1.0.0\", optional = true }\npanic-reset = { version = \"0.1.1\" }\nembedded-hal = { version = \"0.2.6\" }\nembedded-storage = \"0.3.1\"\n\ncortex-m = { version = \"0.7.6\", features = [\"inline-asm\", \"critical-section-single-core\"] }\ncortex-m-rt = \"0.7.0\"\n\n[features]\ndefmt = [\n      \"dep:defmt\",\n      \"dep:defmt-rtt\",\n      \"embassy-stm32/defmt\",\n      \"embassy-boot-stm32/defmt\",\n      \"embassy-sync/defmt\",\n]\n\n[package.metadata.embassy]\nbuild = [\n  { target = \"thumbv7em-none-eabi\", artifact-dir = \"out/examples/boot/stm32wb-dfu\" }\n]\n"
  },
  {
    "path": "examples/boot/application/stm32wb-dfu/README.md",
    "content": "# Examples using bootloader\n\nExample for STM32WB demonstrating the USB DFU application.\n\n## Usage\n\n```\ncargo flash --release --chip STM32WB55RGVx\n```\n"
  },
  {
    "path": "examples/boot/application/stm32wb-dfu/build.rs",
    "content": "//! This build script copies the `memory.x` file from the crate root into\n//! a directory where the linker can always find it at build time.\n//! For many projects this is optional, as the linker always searches the\n//! project root directory -- wherever `Cargo.toml` is. However, if you\n//! are using a workspace or have a more complicated build setup, this\n//! build script becomes required. Additionally, by requesting that\n//! Cargo re-run the build script whenever `memory.x` is changed,\n//! updating `memory.x` ensures a rebuild of the application with the\n//! new memory settings.\n\nuse std::env;\nuse std::fs::File;\nuse std::io::Write;\nuse std::path::PathBuf;\n\nfn main() {\n    // Put `memory.x` in our output directory and ensure it's\n    // on the linker search path.\n    let out = &PathBuf::from(env::var_os(\"OUT_DIR\").unwrap());\n    File::create(out.join(\"memory.x\"))\n        .unwrap()\n        .write_all(include_bytes!(\"memory.x\"))\n        .unwrap();\n    println!(\"cargo:rustc-link-search={}\", out.display());\n\n    // By default, Cargo will re-run a build script whenever\n    // any file in the project changes. By specifying `memory.x`\n    // here, we ensure the build script is only re-run when\n    // `memory.x` is changed.\n    println!(\"cargo:rerun-if-changed=memory.x\");\n\n    println!(\"cargo:rustc-link-arg-bins=--nmagic\");\n    println!(\"cargo:rustc-link-arg-bins=-Tlink.x\");\n    if env::var(\"CARGO_FEATURE_DEFMT\").is_ok() {\n        println!(\"cargo:rustc-link-arg-bins=-Tdefmt.x\");\n    }\n}\n"
  },
  {
    "path": "examples/boot/application/stm32wb-dfu/memory.x",
    "content": "MEMORY\n{\n  /* NOTE 1 K = 1 KiBi = 1024 bytes */\n  BOOTLOADER                        : ORIGIN = 0x08000000, LENGTH = 48K\n  BOOTLOADER_STATE                  : ORIGIN = 0x0800C000, LENGTH = 4K\n  FLASH                             : ORIGIN = 0x0800D000, LENGTH = 120K\n  DFU                               : ORIGIN = 0x0802B000, LENGTH = 120K\n  RAM                         (rwx) : ORIGIN = 0x20000000, LENGTH = 32K\n}\n\n__bootloader_state_start = ORIGIN(BOOTLOADER_STATE) - ORIGIN(BOOTLOADER);\n__bootloader_state_end = ORIGIN(BOOTLOADER_STATE) + LENGTH(BOOTLOADER_STATE) - ORIGIN(BOOTLOADER);\n\n__bootloader_dfu_start = ORIGIN(DFU) - ORIGIN(BOOTLOADER);\n__bootloader_dfu_end = ORIGIN(DFU) + LENGTH(DFU) - ORIGIN(BOOTLOADER);\n"
  },
  {
    "path": "examples/boot/application/stm32wb-dfu/secrets/key.sec",
    "content": "untrusted comment: signify secret key\nRWRCSwAAAAATdHQF3B4jEIoNZrjADRp2LbjJjNdNNzKwTCe4IB6mDNq96pe53nbNxwbdCc/T4hrz7W+Kx1MwrZ0Yz5xebSK5Z0Kh/3Cdf039U5f+eoTDS2fIGbohyUbrtwKzjyE0qXI=\n"
  },
  {
    "path": "examples/boot/application/stm32wb-dfu/src/main.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse core::cell::RefCell;\n\n#[cfg(feature = \"defmt\")]\nuse defmt_rtt::*;\nuse embassy_boot_stm32::{AlignedBuffer, BlockingFirmwareState, FirmwareUpdaterConfig};\nuse embassy_executor::Spawner;\nuse embassy_stm32::flash::{Flash, WRITE_SIZE};\nuse embassy_stm32::rcc::WPAN_DEFAULT;\nuse embassy_stm32::usb::{self, Driver};\nuse embassy_stm32::{bind_interrupts, peripherals};\nuse embassy_sync::blocking_mutex::Mutex;\nuse embassy_time::Duration;\nuse embassy_usb::{Builder, msos};\nuse embassy_usb_dfu::application::{DfuAttributes, DfuState, Handler, usb_dfu};\nuse panic_reset as _;\n\nbind_interrupts!(struct Irqs {\n    USB_LP => usb::InterruptHandler<peripherals::USB>;\n});\n\n// This is a randomly generated GUID to allow clients on Windows to find your device.\n//\n// N.B. update to a custom GUID for your own device!\nconst DEVICE_INTERFACE_GUIDS: &[&str] = &[\"{EAA9A5DC-30BA-44BC-9232-606CDC875321}\"];\n\nstruct DfuHandler<'d, FLASH: embedded_storage::nor_flash::NorFlash> {\n    firmware_state: BlockingFirmwareState<'d, FLASH>,\n}\n\nimpl<FLASH: embedded_storage::nor_flash::NorFlash> Handler for DfuHandler<'_, FLASH> {\n    fn enter_dfu(&mut self) {\n        self.firmware_state.mark_dfu().expect(\"Failed to mark DFU mode\");\n        cortex_m::peripheral::SCB::sys_reset();\n    }\n}\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let mut config = embassy_stm32::Config::default();\n    config.rcc = WPAN_DEFAULT;\n    let p = embassy_stm32::init(config);\n    let flash = Flash::new_blocking(p.FLASH);\n    let flash = Mutex::new(RefCell::new(flash));\n\n    let config = FirmwareUpdaterConfig::from_linkerfile_blocking(&flash, &flash);\n    let mut magic = AlignedBuffer([0; WRITE_SIZE]);\n    let mut firmware_state = BlockingFirmwareState::from_config(config, &mut magic.0);\n    firmware_state.mark_booted().expect(\"Failed to mark booted\");\n\n    let driver = Driver::new(p.USB, Irqs, p.PA12, p.PA11);\n    let mut config = embassy_usb::Config::new(0xc0de, 0xcafe);\n    config.manufacturer = Some(\"Embassy\");\n    config.product = Some(\"USB-DFU Runtime example\");\n    config.serial_number = Some(\"1235678\");\n\n    let mut config_descriptor = [0; 256];\n    let mut bos_descriptor = [0; 256];\n    let mut control_buf = [0; 64];\n\n    let handler = DfuHandler { firmware_state };\n    let mut state = DfuState::new(handler, DfuAttributes::CAN_DOWNLOAD, Duration::from_millis(2500));\n\n    let mut builder = Builder::new(\n        driver,\n        config,\n        &mut config_descriptor,\n        &mut bos_descriptor,\n        &mut [],\n        &mut control_buf,\n    );\n\n    // We add MSOS headers so that the device automatically gets assigned the WinUSB driver on Windows.\n    // Otherwise users need to do this manually using a tool like Zadig.\n    //\n    // It seems these always need to be at added at the device level for this to work and for\n    // composite devices they also need to be added on the function level (as shown later).\n    //\n    builder.msos_descriptor(msos::windows_version::WIN8_1, 2);\n    builder.msos_feature(msos::CompatibleIdFeatureDescriptor::new(\"WINUSB\", \"\"));\n    builder.msos_feature(msos::RegistryPropertyFeatureDescriptor::new(\n        \"DeviceInterfaceGUIDs\",\n        msos::PropertyData::RegMultiSz(DEVICE_INTERFACE_GUIDS),\n    ));\n\n    usb_dfu(&mut builder, &mut state, |func| {\n        // You likely don't have to add these function level headers if your USB device is not composite\n        // (i.e. if your device does not expose another interface in addition to DFU)\n        func.msos_feature(msos::CompatibleIdFeatureDescriptor::new(\"WINUSB\", \"\"));\n        func.msos_feature(msos::RegistryPropertyFeatureDescriptor::new(\n            \"DeviceInterfaceGUIDs\",\n            msos::PropertyData::RegMultiSz(DEVICE_INTERFACE_GUIDS),\n        ));\n    });\n\n    let mut dev = builder.build();\n    dev.run().await\n}\n"
  },
  {
    "path": "examples/boot/application/stm32wba-dfu/.cargo/config.toml",
    "content": "[target.'cfg(all(target_arch = \"arm\", target_os = \"none\"))']\n# replace your chip as listed in `probe-rs chip list`\nrunner = \"probe-rs run --chip STM32WBA65RI\"\n\n[build]\ntarget = \"thumbv8m.main-none-eabihf\"\n\n[env]\nDEFMT_LOG = \"trace\"\n"
  },
  {
    "path": "examples/boot/application/stm32wba-dfu/Cargo.toml",
    "content": "[package]\nedition = \"2024\"\nname = \"embassy-boot-stm32wba-dfu-examples\"\nversion = \"0.1.0\"\nlicense = \"MIT OR Apache-2.0\"\npublish = false\n\n[dependencies]\nembassy-sync = { version = \"0.8.0\", path = \"../../../../embassy-sync\" }\nembassy-executor = { version = \"0.10.0\", path = \"../../../../embassy-executor\", features = [\"platform-cortex-m\", \"executor-thread\"] }\nembassy-time = { version = \"0.5.1\", path = \"../../../../embassy-time\", features = [ \"tick-hz-32_768\"] }\nembassy-stm32 = { version = \"0.6.0\", path = \"../../../../embassy-stm32\", features = [\"stm32wba65ri\", \"time-driver-any\", \"exti\"]  }\nembassy-boot-stm32 = { version = \"0.8.0\", path = \"../../../../embassy-boot-stm32\", features = [] }\nembassy-embedded-hal = { version = \"0.6.0\", path = \"../../../../embassy-embedded-hal\" }\nembassy-usb = { version = \"0.6.0\", path = \"../../../../embassy-usb\" }\nembassy-usb-dfu = { version = \"0.3.0\", path = \"../../../../embassy-usb-dfu\", features = [\"application\", \"cortex-m\"] }\n\ndefmt = { version = \"1.0.1\", optional = true }\ndefmt-rtt = { version = \"1.0.0\", optional = true }\npanic-reset = { version = \"0.1.1\" }\nembedded-hal = { version = \"0.2.6\" }\nembedded-storage = \"0.3.1\"\n\ncortex-m = { version = \"0.7.6\", features = [\"inline-asm\", \"critical-section-single-core\"] }\ncortex-m-rt = \"0.7.0\"\n\n[features]\ndefmt = [\n      \"dep:defmt\",\n      \"dep:defmt-rtt\",\n      \"embassy-stm32/defmt\",\n      \"embassy-boot-stm32/defmt\",\n      \"embassy-sync/defmt\",\n]\n\n[package.metadata.embassy]\nbuild = [\n  { target = \"thumbv8m.main-none-eabihf\", artifact-dir = \"out/examples/boot/stm32wba-dfu\" }\n]\n"
  },
  {
    "path": "examples/boot/application/stm32wba-dfu/README.md",
    "content": "# Examples using bootloader\n\nExample for STM32WBA demonstrating the USB DFU application.\n\n## Usage\n\n```\ncargo flash --release --chip STM32WBA65RI\n```\n"
  },
  {
    "path": "examples/boot/application/stm32wba-dfu/build.rs",
    "content": "//! This build script copies the `memory.x` file from the crate root into\n//! a directory where the linker can always find it at build time.\n//! For many projects this is optional, as the linker always searches the\n//! project root directory -- wherever `Cargo.toml` is. However, if you\n//! are using a workspace or have a more complicated build setup, this\n//! build script becomes required. Additionally, by requesting that\n//! Cargo re-run the build script whenever `memory.x` is changed,\n//! updating `memory.x` ensures a rebuild of the application with the\n//! new memory settings.\n\nuse std::env;\nuse std::fs::File;\nuse std::io::Write;\nuse std::path::PathBuf;\n\nfn main() {\n    // Put `memory.x` in our output directory and ensure it's\n    // on the linker search path.\n    let out = &PathBuf::from(env::var_os(\"OUT_DIR\").unwrap());\n    File::create(out.join(\"memory.x\"))\n        .unwrap()\n        .write_all(include_bytes!(\"memory.x\"))\n        .unwrap();\n    println!(\"cargo:rustc-link-search={}\", out.display());\n\n    // By default, Cargo will re-run a build script whenever\n    // any file in the project changes. By specifying `memory.x`\n    // here, we ensure the build script is only re-run when\n    // `memory.x` is changed.\n    println!(\"cargo:rerun-if-changed=memory.x\");\n\n    println!(\"cargo:rustc-link-arg-bins=--nmagic\");\n    println!(\"cargo:rustc-link-arg-bins=-Tlink.x\");\n    if env::var(\"CARGO_FEATURE_DEFMT\").is_ok() {\n        println!(\"cargo:rustc-link-arg-bins=-Tdefmt.x\");\n    }\n}\n"
  },
  {
    "path": "examples/boot/application/stm32wba-dfu/memory.x",
    "content": "MEMORY\n{\n  /* NOTE 1 K = 1 KiBi = 1024 bytes */\n  BOOTLOADER                        : ORIGIN = 0x08000000, LENGTH = 80K\n  BOOTLOADER_STATE                  : ORIGIN = 0x08014000, LENGTH = 8K\n  FLASH                             : ORIGIN = 0x08016000, LENGTH = 120K\n  DFU                               : ORIGIN = 0x0803C000, LENGTH = 160K\n  RAM                         (rwx) : ORIGIN = 0x20000000, LENGTH = 400K\n}\n\n__bootloader_state_start = ORIGIN(BOOTLOADER_STATE) - ORIGIN(BOOTLOADER);\n__bootloader_state_end = ORIGIN(BOOTLOADER_STATE) + LENGTH(BOOTLOADER_STATE) - ORIGIN(BOOTLOADER);\n\n__bootloader_dfu_start = ORIGIN(DFU) - ORIGIN(BOOTLOADER);\n__bootloader_dfu_end = ORIGIN(DFU) + LENGTH(DFU) - ORIGIN(BOOTLOADER);\n"
  },
  {
    "path": "examples/boot/application/stm32wba-dfu/secrets/key.sec",
    "content": "untrusted comment: signify secret key\nRWRCSwAAAAATdHQF3B4jEIoNZrjADRp2LbjJjNdNNzKwTCe4IB6mDNq96pe53nbNxwbdCc/T4hrz7W+Kx1MwrZ0Yz5xebSK5Z0Kh/3Cdf039U5f+eoTDS2fIGbohyUbrtwKzjyE0qXI=\n"
  },
  {
    "path": "examples/boot/application/stm32wba-dfu/src/main.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse core::cell::RefCell;\n\n#[cfg(feature = \"defmt\")]\nuse defmt_rtt as _;\nuse embassy_boot_stm32::{AlignedBuffer, BlockingFirmwareState, FirmwareUpdaterConfig};\nuse embassy_executor::Spawner;\nuse embassy_stm32::flash::{Flash, WRITE_SIZE};\nuse embassy_stm32::usb::{self, Driver};\nuse embassy_stm32::{bind_interrupts, peripherals};\nuse embassy_sync::blocking_mutex::Mutex;\nuse embassy_time::Duration;\nuse embassy_usb::{Builder, msos};\nuse embassy_usb_dfu::application::{DfuAttributes, DfuState, Handler, usb_dfu};\nuse panic_reset as _;\n\nbind_interrupts!(struct Irqs {\n    USB_OTG_HS => usb::InterruptHandler<peripherals::USB_OTG_HS>;\n});\n\n// This is a randomly generated GUID to allow clients on Windows to find your device.\n//\n// N.B. update to a custom GUID for your own device!\nconst DEVICE_INTERFACE_GUIDS: &[&str] = &[\"{EAA9A5DC-30BA-44BC-9232-606CDC875321}\"];\n\nstruct DfuHandler<'d, FLASH: embedded_storage::nor_flash::NorFlash> {\n    firmware_state: BlockingFirmwareState<'d, FLASH>,\n}\n\nimpl<FLASH: embedded_storage::nor_flash::NorFlash> Handler for DfuHandler<'_, FLASH> {\n    fn enter_dfu(&mut self) {\n        self.firmware_state.mark_dfu().expect(\"Failed to mark DFU mode\");\n        cortex_m::peripheral::SCB::sys_reset();\n    }\n}\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let mut config = embassy_stm32::Config::default();\n\n    {\n        use embassy_stm32::rcc::*;\n        config.rcc.pll1 = Some(Pll {\n            source: PllSource::HSI,\n            prediv: PllPreDiv::DIV1,   // PLLM = 1 → HSI / 1 = 16 MHz\n            mul: PllMul::MUL30,        // PLLN = 30 → 16 MHz * 30 = 480 MHz VCO\n            divr: Some(PllDiv::DIV5),  // PLLR = 5 → 96 MHz (Sysclk)\n            divq: Some(PllDiv::DIV10), // PLLQ = 10 → 48 MHz\n            divp: Some(PllDiv::DIV30), // PLLP = 30 → 16 MHz (USB_OTG_HS)\n            frac: Some(0),             // Fractional part (disabled)\n        });\n\n        config.rcc.ahb_pre = AHBPrescaler::DIV1;\n        config.rcc.apb1_pre = APBPrescaler::DIV1;\n        config.rcc.apb2_pre = APBPrescaler::DIV1;\n        config.rcc.apb7_pre = APBPrescaler::DIV1;\n        config.rcc.ahb5_pre = AHB5Prescaler::DIV4;\n\n        config.rcc.voltage_scale = VoltageScale::RANGE1;\n        config.rcc.mux.otghssel = mux::Otghssel::PLL1_P;\n        config.rcc.sys = Sysclk::PLL1_R;\n    }\n\n    let p = embassy_stm32::init(config);\n    let flash = Flash::new_blocking(p.FLASH);\n    let flash = Mutex::new(RefCell::new(flash));\n\n    let config = FirmwareUpdaterConfig::from_linkerfile_blocking(&flash, &flash);\n    let mut magic = AlignedBuffer([0; WRITE_SIZE]);\n    let mut firmware_state = BlockingFirmwareState::from_config(config, &mut magic.0);\n    firmware_state.mark_booted().expect(\"Failed to mark booted\");\n\n    // Create the driver, from the HAL.\n    let mut ep_out_buffer = [0u8; 256];\n    let mut config = embassy_stm32::usb::Config::default();\n    config.vbus_detection = false;\n\n    let driver = Driver::new_hs(p.USB_OTG_HS, Irqs, p.PD6, p.PD7, &mut ep_out_buffer, config);\n    let mut config = embassy_usb::Config::new(0xc0de, 0xcafe);\n    config.manufacturer = Some(\"Embassy\");\n    config.product = Some(\"USB-DFU Runtime example\");\n    config.serial_number = Some(\"1235678\");\n\n    let mut config_descriptor = [0; 256];\n    let mut bos_descriptor = [0; 256];\n    let mut control_buf = [0; 64];\n\n    let handler = DfuHandler { firmware_state };\n    let mut state = DfuState::new(handler, DfuAttributes::CAN_DOWNLOAD, Duration::from_millis(2500));\n\n    let mut builder = Builder::new(\n        driver,\n        config,\n        &mut config_descriptor,\n        &mut bos_descriptor,\n        &mut [],\n        &mut control_buf,\n    );\n\n    // We add MSOS headers so that the device automatically gets assigned the WinUSB driver on Windows.\n    // Otherwise users need to do this manually using a tool like Zadig.\n    //\n    // It seems these always need to be at added at the device level for this to work and for\n    // composite devices they also need to be added on the function level (as shown later).\n\n    builder.msos_descriptor(msos::windows_version::WIN8_1, 2);\n    builder.msos_feature(msos::CompatibleIdFeatureDescriptor::new(\"WINUSB\", \"\"));\n    builder.msos_feature(msos::RegistryPropertyFeatureDescriptor::new(\n        \"DeviceInterfaceGUIDs\",\n        msos::PropertyData::RegMultiSz(DEVICE_INTERFACE_GUIDS),\n    ));\n\n    usb_dfu(&mut builder, &mut state, |func| {\n        // You likely don't have to add these function level headers if your USB device is not composite\n        // (i.e. if your device does not expose another interface in addition to DFU)\n        func.msos_feature(msos::CompatibleIdFeatureDescriptor::new(\"WINUSB\", \"\"));\n        func.msos_feature(msos::RegistryPropertyFeatureDescriptor::new(\n            \"DeviceInterfaceGUIDs\",\n            msos::PropertyData::RegMultiSz(DEVICE_INTERFACE_GUIDS),\n        ));\n    });\n\n    let mut dev = builder.build();\n    dev.run().await\n}\n"
  },
  {
    "path": "examples/boot/application/stm32wl/.cargo/config.toml",
    "content": "[target.'cfg(all(target_arch = \"arm\", target_os = \"none\"))']\n# replace your chip as listed in `probe-rs chip list`\nrunner = \"probe-rs run --chip STM32WLE5JCIx\"\n\n[build]\ntarget = \"thumbv7em-none-eabihf\"\n\n[env]\nDEFMT_LOG = \"trace\"\n"
  },
  {
    "path": "examples/boot/application/stm32wl/Cargo.toml",
    "content": "[package]\nedition = \"2024\"\nname = \"embassy-boot-stm32wl-examples\"\nversion = \"0.1.0\"\nlicense = \"MIT OR Apache-2.0\"\npublish = false\n\n[dependencies]\nembassy-sync = { version = \"0.8.0\", path = \"../../../../embassy-sync\" }\nembassy-executor = { version = \"0.10.0\", path = \"../../../../embassy-executor\", features = [\"platform-cortex-m\", \"executor-thread\"] }\nembassy-time = { version = \"0.5.1\", path = \"../../../../embassy-time\", features = [ \"tick-hz-32_768\"] }\nembassy-stm32 = { version = \"0.6.0\", path = \"../../../../embassy-stm32\", features = [\"stm32wl55jc-cm4\", \"time-driver-any\", \"exti\"]  }\nembassy-boot-stm32 = { version = \"0.8.0\", path = \"../../../../embassy-boot-stm32\", features = [] }\nembassy-embedded-hal = { version = \"0.6.0\", path = \"../../../../embassy-embedded-hal\" }\n\ndefmt = { version = \"1.0.1\", optional = true }\ndefmt-rtt = { version = \"1.0.0\", optional = true }\npanic-reset = { version = \"0.1.1\" }\nembedded-hal = { version = \"0.2.6\" }\n\ncortex-m = { version = \"0.7.6\", features = [\"inline-asm\", \"critical-section-single-core\"] }\ncortex-m-rt = \"0.7.0\"\n\n[features]\ndefmt = [\n      \"dep:defmt\",\n      \"dep:defmt-rtt\",\n      \"embassy-stm32/defmt\",\n      \"embassy-boot-stm32/defmt\",\n      \"embassy-sync/defmt\",\n]\nskip-include = []\n\n[package.metadata.embassy]\nbuild = [\n  { target = \"thumbv7em-none-eabi\", features = [\"skip-include\"], artifact-dir = \"out/examples/boot/stm32wl\" }\n]\n"
  },
  {
    "path": "examples/boot/application/stm32wl/README.md",
    "content": "# Examples using bootloader\n\nExample for STM32WL demonstrating the bootloader. The example consists of application binaries, 'a'\nwhich allows you to press a button to start the DFU process, and 'b' which is the updated\napplication.\n\n\n## Prerequisites\n\n* `cargo-binutils`\n* `cargo-flash`\n* `embassy-boot-stm32`\n\n## Usage\n\n```\n# Flash bootloader\ncargo flash --manifest-path ../../bootloader/stm32/Cargo.toml --release --features embassy-stm32/stm32wl55jc-cm4 --chip STM32WLE5JCIx\n# Build 'b'\ncargo build --release --bin b\n# Generate binary for 'b'\ncargo objcopy --release --bin b -- -O binary b.bin\n```\n\n# Flash `a` (which includes b.bin)\n\n```\ncargo flash --release --bin a --chip STM32WLE5JCIx\n```\n"
  },
  {
    "path": "examples/boot/application/stm32wl/build.rs",
    "content": "//! This build script copies the `memory.x` file from the crate root into\n//! a directory where the linker can always find it at build time.\n//! For many projects this is optional, as the linker always searches the\n//! project root directory -- wherever `Cargo.toml` is. However, if you\n//! are using a workspace or have a more complicated build setup, this\n//! build script becomes required. Additionally, by requesting that\n//! Cargo re-run the build script whenever `memory.x` is changed,\n//! updating `memory.x` ensures a rebuild of the application with the\n//! new memory settings.\n\nuse std::env;\nuse std::fs::File;\nuse std::io::Write;\nuse std::path::PathBuf;\n\nfn main() {\n    // Put `memory.x` in our output directory and ensure it's\n    // on the linker search path.\n    let out = &PathBuf::from(env::var_os(\"OUT_DIR\").unwrap());\n    File::create(out.join(\"memory.x\"))\n        .unwrap()\n        .write_all(include_bytes!(\"memory.x\"))\n        .unwrap();\n    println!(\"cargo:rustc-link-search={}\", out.display());\n\n    // By default, Cargo will re-run a build script whenever\n    // any file in the project changes. By specifying `memory.x`\n    // here, we ensure the build script is only re-run when\n    // `memory.x` is changed.\n    println!(\"cargo:rerun-if-changed=memory.x\");\n\n    println!(\"cargo:rustc-link-arg-bins=--nmagic\");\n    println!(\"cargo:rustc-link-arg-bins=-Tlink.x\");\n    if env::var(\"CARGO_FEATURE_DEFMT\").is_ok() {\n        println!(\"cargo:rustc-link-arg-bins=-Tdefmt.x\");\n    }\n}\n"
  },
  {
    "path": "examples/boot/application/stm32wl/memory.x",
    "content": "MEMORY\n{\n  /* NOTE 1 K = 1 KiBi = 1024 bytes */\n  BOOTLOADER                        : ORIGIN = 0x08000000, LENGTH = 24K\n  BOOTLOADER_STATE                  : ORIGIN = 0x08006000, LENGTH = 4K\n  FLASH                             : ORIGIN = 0x08008000, LENGTH = 64K\n  DFU                               : ORIGIN = 0x08018000, LENGTH = 68K\n  SHARED_RAM                  (rwx) : ORIGIN = 0x20000000, LENGTH = 128\n  RAM                         (rwx) : ORIGIN = 0x20000080, LENGTH = 32K - 128\n}\n\n__bootloader_state_start = ORIGIN(BOOTLOADER_STATE) - ORIGIN(BOOTLOADER);\n__bootloader_state_end = ORIGIN(BOOTLOADER_STATE) + LENGTH(BOOTLOADER_STATE) - ORIGIN(BOOTLOADER);\n\n__bootloader_dfu_start = ORIGIN(DFU) - ORIGIN(BOOTLOADER);\n__bootloader_dfu_end = ORIGIN(DFU) + LENGTH(DFU) - ORIGIN(BOOTLOADER);\n\nSECTIONS\n{\n    .shared_data :\n    {\n        *(.shared_data)\n    } > SHARED_RAM\n}\n"
  },
  {
    "path": "examples/boot/application/stm32wl/src/bin/a.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse core::mem::MaybeUninit;\n\n#[cfg(feature = \"defmt\")]\nuse defmt_rtt::*;\nuse embassy_boot_stm32::{AlignedBuffer, FirmwareUpdater, FirmwareUpdaterConfig};\nuse embassy_embedded_hal::adapter::BlockingAsync;\nuse embassy_executor::Spawner;\nuse embassy_stm32::exti::{self, ExtiInput};\nuse embassy_stm32::flash::{Flash, WRITE_SIZE};\nuse embassy_stm32::gpio::{Level, Output, Pull, Speed};\nuse embassy_stm32::{SharedData, bind_interrupts, interrupt};\nuse embassy_sync::mutex::Mutex;\nuse panic_reset as _;\n\n#[cfg(feature = \"skip-include\")]\nstatic APP_B: &[u8] = &[0, 1, 2, 3];\n#[cfg(not(feature = \"skip-include\"))]\nstatic APP_B: &[u8] = include_bytes!(\"../../b.bin\");\n\nbind_interrupts!(\n    pub struct Irqs{\n        EXTI0 => exti::InterruptHandler<interrupt::typelevel::EXTI0>;\n});\n\n#[unsafe(link_section = \".shared_data\")]\nstatic SHARED_DATA: MaybeUninit<SharedData> = MaybeUninit::uninit();\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let p = embassy_stm32::init_primary(Default::default(), &SHARED_DATA);\n    let flash = Flash::new_blocking(p.FLASH);\n    let flash = Mutex::new(BlockingAsync::new(flash));\n\n    let mut button = ExtiInput::new(p.PA0, p.EXTI0, Pull::Up, Irqs);\n\n    let mut led = Output::new(p.PB9, Level::Low, Speed::Low);\n    led.set_high();\n\n    let config = FirmwareUpdaterConfig::from_linkerfile(&flash, &flash);\n    let mut magic = AlignedBuffer([0; WRITE_SIZE]);\n    let mut updater = FirmwareUpdater::new(config, &mut magic.0);\n    button.wait_for_falling_edge().await;\n    //defmt::info!(\"Starting update\");\n    let mut offset = 0;\n    for chunk in APP_B.chunks(2048) {\n        let mut buf: [u8; 2048] = [0; 2048];\n        buf[..chunk.len()].copy_from_slice(chunk);\n        //        defmt::info!(\"Writing chunk at 0x{:x}\", offset);\n        updater.write_firmware(offset, &buf).await.unwrap();\n        offset += chunk.len();\n    }\n    updater.mark_updated().await.unwrap();\n    //defmt::info!(\"Marked as updated\");\n    led.set_low();\n    cortex_m::peripheral::SCB::sys_reset();\n}\n"
  },
  {
    "path": "examples/boot/application/stm32wl/src/bin/b.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse core::mem::MaybeUninit;\n\n#[cfg(feature = \"defmt\")]\nuse defmt_rtt::*;\nuse embassy_executor::Spawner;\nuse embassy_stm32::SharedData;\nuse embassy_stm32::gpio::{Level, Output, Speed};\nuse embassy_time::Timer;\nuse panic_reset as _;\n\n#[unsafe(link_section = \".shared_data\")]\nstatic SHARED_DATA: MaybeUninit<SharedData> = MaybeUninit::uninit();\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let p = embassy_stm32::init_primary(Default::default(), &SHARED_DATA);\n    let mut led = Output::new(p.PB15, Level::High, Speed::Low);\n\n    loop {\n        led.set_high();\n        Timer::after_millis(500).await;\n\n        led.set_low();\n        Timer::after_millis(500).await;\n    }\n}\n"
  },
  {
    "path": "examples/boot/bootloader/nrf/.cargo/config.toml",
    "content": "[unstable]\n#build-std = [\"core\"]\n#build-std-features = [\"panic_immediate_abort\"]\n\n[target.'cfg(all(target_arch = \"arm\", target_os = \"none\"))']\n#runner = \"./fruitrunner\"\nrunner = \"probe-rs run --chip nrf52840_xxAA\"\n\nrustflags = [\n  # Code-size optimizations.\n  #\"-Z\", \"trap-unreachable=no\",\n  #\"-C\", \"no-vectorize-loops\",\n  \"-C\", \"force-frame-pointers=yes\",\n]\n\n[build]\ntarget = \"thumbv7em-none-eabi\"\n\n[env]\nDEFMT_LOG = \"trace\"\n"
  },
  {
    "path": "examples/boot/bootloader/nrf/Cargo.toml",
    "content": "[package]\nedition = \"2024\"\nname = \"nrf-bootloader-example\"\nversion = \"0.1.0\"\ndescription = \"Bootloader for nRF chips\"\nlicense = \"MIT OR Apache-2.0\"\npublish = false\n\n[dependencies]\ndefmt = { version = \"1.0.1\", optional = true }\ndefmt-rtt = { version = \"1.0.0\", optional = true }\n\nembassy-nrf = { path = \"../../../../embassy-nrf\", features = [] }\nembassy-boot-nrf = { path = \"../../../../embassy-boot-nrf\" }\ncortex-m = { version = \"0.7.6\", features = [\"inline-asm\", \"critical-section-single-core\"] }\nembassy-sync = { version = \"0.8.0\", path = \"../../../../embassy-sync\" }\ncortex-m-rt = { version = \"0.7\" }\ncfg-if = \"1.0.0\"\n\n[features]\ndefmt = [\n    \"dep:defmt\",\n    \"dep:defmt-rtt\",\n    \"embassy-boot-nrf/defmt\",\n    \"embassy-nrf/defmt\",\n]\nsoftdevice = [\n    \"embassy-boot-nrf/softdevice\",\n]\nnrf54 = []\n\n[profile.dev]\ndebug = 2\ndebug-assertions = true\nincremental = false\nopt-level = 'z'\noverflow-checks = true\n\n[profile.release]\ncodegen-units = 1\ndebug = 2\ndebug-assertions = false\nincremental = false\nlto = 'fat'\nopt-level = 'z'\noverflow-checks = false\n\n# do not optimize proc-macro crates = faster builds from scratch\n[profile.dev.build-override]\ncodegen-units = 8\ndebug = false\ndebug-assertions = false\nopt-level = 0\noverflow-checks = false\n\n[profile.release.build-override]\ncodegen-units = 8\ndebug = false\ndebug-assertions = false\nopt-level = 0\noverflow-checks = false\n\n[package.metadata.embassy]\nbuild = [\n  { target = \"thumbv7em-none-eabi\", features = [\"embassy-nrf/nrf52840\"] },\n  { target = \"thumbv8m.main-none-eabihf\", features = [\"embassy-nrf/nrf9160-ns\"] },\n  { target = \"thumbv8m.main-none-eabihf\", features = [\"embassy-nrf/nrf9120-ns\"] },\n  { target = \"thumbv8m.main-none-eabihf\", features = [\"embassy-nrf/nrf9151-ns\"] },\n  { target = \"thumbv8m.main-none-eabihf\", features = [\"embassy-nrf/nrf9161-ns\"] },\n  { target = \"thumbv8m.main-none-eabihf\", features = [\"embassy-nrf/nrf54l15-app-s\", \"nrf54\"] }\n]\n"
  },
  {
    "path": "examples/boot/bootloader/nrf/README.md",
    "content": "# Bootloader for nRF\n\nThe bootloader uses `embassy-boot` to interact with the flash.\n\n# Usage\n\nFlash the bootloader\n\n```\ncargo flash --features embassy-nrf/nrf52832 --release --chip nRF52832_xxAA\n```\n"
  },
  {
    "path": "examples/boot/bootloader/nrf/build.rs",
    "content": "//! This build script copies the `memory.x` file from the crate root into\n//! a directory where the linker can always find it at build time.\n//! For many projects this is optional, as the linker always searches the\n//! project root directory -- wherever `Cargo.toml` is. However, if you\n//! are using a workspace or have a more complicated build setup, this\n//! build script becomes required. Additionally, by requesting that\n//! Cargo re-run the build script whenever `memory.x` is changed,\n//! updating `memory.x` ensures a rebuild of the application with the\n//! new memory settings.\n\nuse std::env;\nuse std::fs::File;\nuse std::io::Write;\nuse std::path::PathBuf;\n\nfn main() {\n    // Put `memory.x` in our output directory and ensure it's\n    // on the linker search path.\n    let out = &PathBuf::from(env::var_os(\"OUT_DIR\").unwrap());\n    File::create(out.join(\"memory.x\"))\n        .unwrap()\n        .write_all(include_bytes!(\"memory.x\"))\n        .unwrap();\n    println!(\"cargo:rustc-link-search={}\", out.display());\n\n    // By default, Cargo will re-run a build script whenever\n    // any file in the project changes. By specifying `memory.x`\n    // here, we ensure the build script is only re-run when\n    // `memory.x` is changed.\n    println!(\"cargo:rerun-if-changed=memory.x\");\n\n    println!(\"cargo:rustc-link-arg-bins=--nmagic\");\n    println!(\"cargo:rustc-link-arg-bins=-Tlink.x\");\n    if env::var(\"CARGO_FEATURE_DEFMT\").is_ok() {\n        println!(\"cargo:rustc-link-arg-bins=-Tdefmt.x\");\n    }\n}\n"
  },
  {
    "path": "examples/boot/bootloader/nrf/memory-bm.x",
    "content": "MEMORY\n{\n  /* NOTE 1 K = 1 KiBi = 1024 bytes */\n  FLASH                             : ORIGIN = 0x00000000, LENGTH = 24K\n  BOOTLOADER_STATE                  : ORIGIN = 0x00006000, LENGTH = 4K\n  ACTIVE                            : ORIGIN = 0x00007000, LENGTH = 64K\n  DFU                               : ORIGIN = 0x00017000, LENGTH = 68K\n  RAM                         (rwx) : ORIGIN = 0x20000000, LENGTH = 32K\n}\n\n__bootloader_state_start = ORIGIN(BOOTLOADER_STATE);\n__bootloader_state_end = ORIGIN(BOOTLOADER_STATE) + LENGTH(BOOTLOADER_STATE);\n\n__bootloader_active_start = ORIGIN(ACTIVE);\n__bootloader_active_end = ORIGIN(ACTIVE) + LENGTH(ACTIVE);\n\n__bootloader_dfu_start = ORIGIN(DFU);\n__bootloader_dfu_end = ORIGIN(DFU) + LENGTH(DFU);\n"
  },
  {
    "path": "examples/boot/bootloader/nrf/memory-s140.x",
    "content": "MEMORY\n{\n  /* NOTE 1 K = 1 KiBi = 1024 bytes */\n  MBR                               : ORIGIN = 0x00000000, LENGTH = 4K\n  SOFTDEVICE                        : ORIGIN = 0x00001000, LENGTH = 155648\n  ACTIVE                            : ORIGIN = 0x00027000, LENGTH = 425984\n  DFU                               : ORIGIN = 0x0008F000, LENGTH = 430080\n  FLASH                             : ORIGIN = 0x000f9000, LENGTH = 24K\n  BOOTLOADER_STATE                  : ORIGIN = 0x000ff000, LENGTH = 4K\n  RAM                         (rwx) : ORIGIN = 0x20000008, LENGTH = 0x2fff8\n  uicr_bootloader_start_address (r) : ORIGIN = 0x10001014, LENGTH = 0x4\n}\n\n__bootloader_state_start = ORIGIN(BOOTLOADER_STATE);\n__bootloader_state_end = ORIGIN(BOOTLOADER_STATE) + LENGTH(BOOTLOADER_STATE);\n\n__bootloader_active_start = ORIGIN(ACTIVE);\n__bootloader_active_end = ORIGIN(ACTIVE) + LENGTH(ACTIVE);\n\n__bootloader_dfu_start = ORIGIN(DFU);\n__bootloader_dfu_end = ORIGIN(DFU) + LENGTH(DFU);\n\n__bootloader_start = ORIGIN(FLASH);\n\nSECTIONS\n{\n  .uicr_bootloader_start_address :\n  {\n    LONG(__bootloader_start)\n  } > uicr_bootloader_start_address\n}\n"
  },
  {
    "path": "examples/boot/bootloader/nrf/memory.x",
    "content": "MEMORY\n{\n  /* NOTE 1 K = 1 KiBi = 1024 bytes */\n  FLASH                             : ORIGIN = 0x00000000, LENGTH = 24K\n  BOOTLOADER_STATE                  : ORIGIN = 0x00006000, LENGTH = 4K\n  ACTIVE                            : ORIGIN = 0x00007000, LENGTH = 64K\n  DFU                               : ORIGIN = 0x00017000, LENGTH = 68K\n  RAM                         (rwx) : ORIGIN = 0x20000000, LENGTH = 32K\n}\n\n__bootloader_state_start = ORIGIN(BOOTLOADER_STATE);\n__bootloader_state_end = ORIGIN(BOOTLOADER_STATE) + LENGTH(BOOTLOADER_STATE);\n\n__bootloader_active_start = ORIGIN(ACTIVE);\n__bootloader_active_end = ORIGIN(ACTIVE) + LENGTH(ACTIVE);\n\n__bootloader_dfu_start = ORIGIN(DFU);\n__bootloader_dfu_end = ORIGIN(DFU) + LENGTH(DFU);\n"
  },
  {
    "path": "examples/boot/bootloader/nrf/src/main.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse core::cell::RefCell;\n\nuse cortex_m_rt::{entry, exception};\n#[cfg(feature = \"defmt\")]\nuse defmt_rtt as _;\nuse embassy_boot_nrf::*;\nuse embassy_nrf::nvmc::Nvmc;\nuse embassy_nrf::wdt::{self, HaltConfig, SleepConfig};\nuse embassy_sync::blocking_mutex::Mutex;\n\n#[entry]\nfn main() -> ! {\n    let p = embassy_nrf::init(Default::default());\n\n    // Uncomment this if you are debugging the bootloader with debugger/RTT attached,\n    // as it prevents a hard fault when accessing flash 'too early' after boot.\n    /*\n        for i in 0..10000000 {\n            cortex_m::asm::nop();\n        }\n    */\n\n    let mut wdt_config = wdt::Config::default();\n    wdt_config.timeout_ticks = 32768 * 5; // timeout seconds\n    wdt_config.action_during_sleep = SleepConfig::RUN;\n    wdt_config.action_during_debug_halt = HaltConfig::PAUSE;\n\n    #[cfg(not(feature = \"nrf54\"))]\n    let flash = WatchdogFlash::start(Nvmc::new(p.NVMC), p.WDT, wdt_config);\n    #[cfg(feature = \"nrf54\")]\n    let flash = WatchdogFlash::start(Nvmc::new(p.RRAMC), p.WDT0, wdt_config);\n    let flash = Mutex::new(RefCell::new(flash));\n\n    let config = BootLoaderConfig::from_linkerfile_blocking(&flash, &flash, &flash);\n    let active_offset = config.active.offset();\n    let bl: BootLoader = BootLoader::prepare(config);\n\n    unsafe { bl.load(active_offset) }\n}\n\n#[unsafe(no_mangle)]\n#[cfg_attr(target_os = \"none\", unsafe(link_section = \".HardFault.user\"))]\nunsafe extern \"C\" fn HardFault() {\n    cortex_m::peripheral::SCB::sys_reset();\n}\n\n#[exception]\nunsafe fn DefaultHandler(_: i16) -> ! {\n    const SCB_ICSR: *const u32 = 0xE000_ED04 as *const u32;\n    let irqn = unsafe { core::ptr::read_volatile(SCB_ICSR) } as u8 as i16 - 16;\n\n    panic!(\"DefaultHandler #{:?}\", irqn);\n}\n\n#[panic_handler]\nfn panic(_info: &core::panic::PanicInfo) -> ! {\n    cortex_m::asm::udf();\n}\n"
  },
  {
    "path": "examples/boot/bootloader/rp/.cargo/config.toml",
    "content": "[target.'cfg(all(target_arch = \"arm\", target_os = \"none\"))']\nrunner = \"probe-rs run --chip RP2040\"\n\n[build]\ntarget = \"thumbv6m-none-eabi\"\n\n[env]\nDEFMT_LOG = \"trace\"\n"
  },
  {
    "path": "examples/boot/bootloader/rp/Cargo.toml",
    "content": "[package]\nedition = \"2024\"\nname = \"rp-bootloader-example\"\nversion = \"0.1.0\"\ndescription = \"Example bootloader for RP2040 chips\"\nlicense = \"MIT OR Apache-2.0\"\npublish = false\n\n[dependencies]\ndefmt = { version = \"1.0.1\", optional = true }\ndefmt-rtt = { version = \"1.0.0\", optional = true }\n\nembassy-rp = { path = \"../../../../embassy-rp\", features = [\"rp2040\"] }\nembassy-boot-rp = { path = \"../../../../embassy-boot-rp\" }\nembassy-sync = { version = \"0.8.0\", path = \"../../../../embassy-sync\" }\nembassy-time = { path = \"../../../../embassy-time\", features = [] }\n\ncortex-m = { version = \"0.7.6\", features = [\"inline-asm\", \"critical-section-single-core\"] }\ncortex-m-rt = { version = \"0.7\" }\nembedded-storage = \"0.3.1\"\nembedded-storage-async = \"0.4.0\"\ncfg-if = \"1.0.0\"\n\n[features]\ndefmt = [\n    \"dep:defmt\",\n    \"dep:defmt-rtt\",\n    \"embassy-boot-rp/defmt\",\n    \"embassy-rp/defmt\",\n]\n\n[profile.release]\ndebug = true\nopt-level = 's'\n\n[package.metadata.embassy]\nbuild = [\n  { target = \"thumbv6m-none-eabi\" }\n]\n"
  },
  {
    "path": "examples/boot/bootloader/rp/README.md",
    "content": "# Bootloader for RP2040\n\nThe bootloader uses `embassy-boot` to interact with the flash.\n\n# Usage\n\nFlashing the bootloader\n\n```\ncargo flash --release --chip RP2040\n```\n\nTo debug, use `cargo run` and enable the debug feature flag\n\n``` rust\ncargo run --release --features debug\n```\n"
  },
  {
    "path": "examples/boot/bootloader/rp/build.rs",
    "content": "use std::env;\nuse std::fs::File;\nuse std::io::Write;\nuse std::path::PathBuf;\n\nfn main() {\n    // Put `memory.x` in our output directory and ensure it's\n    // on the linker search path.\n    let out = &PathBuf::from(env::var_os(\"OUT_DIR\").unwrap());\n    File::create(out.join(\"memory.x\"))\n        .unwrap()\n        .write_all(include_bytes!(\"memory.x\"))\n        .unwrap();\n    println!(\"cargo:rustc-link-search={}\", out.display());\n\n    // By default, Cargo will re-run a build script whenever\n    // any file in the project changes. By specifying `memory.x`\n    // here, we ensure the build script is only re-run when\n    // `memory.x` is changed.\n    println!(\"cargo:rerun-if-changed=memory.x\");\n\n    println!(\"cargo:rustc-link-arg-bins=--nmagic\");\n    println!(\"cargo:rustc-link-arg-bins=-Tlink.x\");\n    println!(\"cargo:rustc-link-arg-bins=-Tlink-rp.x\");\n    if env::var(\"CARGO_FEATURE_DEFMT\").is_ok() {\n        println!(\"cargo:rustc-link-arg-bins=-Tdefmt.x\");\n    }\n}\n"
  },
  {
    "path": "examples/boot/bootloader/rp/memory.x",
    "content": "MEMORY\n{\n  /* NOTE 1 K = 1 KiBi = 1024 bytes */\n  BOOT2                             : ORIGIN = 0x10000000, LENGTH = 0x100\n  FLASH                             : ORIGIN = 0x10000100, LENGTH = 24K - 0x100\n  BOOTLOADER_STATE                  : ORIGIN = 0x10006000, LENGTH = 4K\n  ACTIVE                            : ORIGIN = 0x10007000, LENGTH = 512K\n  DFU                               : ORIGIN = 0x10087000, LENGTH = 516K\n\n  /* Pick one of the two options for RAM layout     */\n\n  /* OPTION A: Use all RAM banks as one big block   */\n  /* Reasonable, unless you are doing something     */\n  /* really particular with DMA or other concurrent */\n  /* access that would benefit from striping        */\n  RAM   : ORIGIN = 0x20000000, LENGTH = 264K\n\n  /* OPTION B: Keep the unstriped sections separate */\n  /* RAM: ORIGIN = 0x20000000, LENGTH = 256K        */\n  /* SCRATCH_A: ORIGIN = 0x20040000, LENGTH = 4K    */\n  /* SCRATCH_B: ORIGIN = 0x20041000, LENGTH = 4K    */\n}\n\n__bootloader_state_start = ORIGIN(BOOTLOADER_STATE) - ORIGIN(BOOT2);\n__bootloader_state_end = ORIGIN(BOOTLOADER_STATE) + LENGTH(BOOTLOADER_STATE) - ORIGIN(BOOT2);\n\n__bootloader_active_start = ORIGIN(ACTIVE) - ORIGIN(BOOT2);\n__bootloader_active_end = ORIGIN(ACTIVE) + LENGTH(ACTIVE) - ORIGIN(BOOT2);\n\n__bootloader_dfu_start = ORIGIN(DFU) - ORIGIN(BOOT2);\n__bootloader_dfu_end = ORIGIN(DFU) + LENGTH(DFU) - ORIGIN(BOOT2);\n"
  },
  {
    "path": "examples/boot/bootloader/rp/src/main.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse core::cell::RefCell;\n\nuse cortex_m_rt::{entry, exception};\n#[cfg(feature = \"defmt\")]\nuse defmt_rtt as _;\nuse embassy_boot_rp::*;\nuse embassy_sync::blocking_mutex::Mutex;\nuse embassy_time::Duration;\n\nconst FLASH_SIZE: usize = 2 * 1024 * 1024;\n\n#[entry]\nfn main() -> ! {\n    let p = embassy_rp::init(Default::default());\n\n    // Uncomment this if you are debugging the bootloader with debugger/RTT attached,\n    // as it prevents a hard fault when accessing flash 'too early' after boot.\n    /*\n    for i in 0..10000000 {\n        cortex_m::asm::nop();\n    }\n    */\n\n    let flash = WatchdogFlash::<FLASH_SIZE>::start(p.FLASH, p.WATCHDOG, Duration::from_secs(8));\n    let flash = Mutex::new(RefCell::new(flash));\n\n    let config = BootLoaderConfig::from_linkerfile_blocking(&flash, &flash, &flash);\n    let active_offset = config.active.offset();\n    let bl: BootLoader = BootLoader::prepare(config);\n\n    unsafe { bl.load(embassy_rp::flash::FLASH_BASE as u32 + active_offset) }\n}\n\n#[unsafe(no_mangle)]\n#[cfg_attr(target_os = \"none\", unsafe(link_section = \".HardFault.user\"))]\nunsafe extern \"C\" fn HardFault() {\n    cortex_m::peripheral::SCB::sys_reset();\n}\n\n#[exception]\nunsafe fn DefaultHandler(_: i16) -> ! {\n    const SCB_ICSR: *const u32 = 0xE000_ED04 as *const u32;\n    let irqn = unsafe { core::ptr::read_volatile(SCB_ICSR) } as u8 as i16 - 16;\n\n    panic!(\"DefaultHandler #{:?}\", irqn);\n}\n\n#[panic_handler]\nfn panic(_info: &core::panic::PanicInfo) -> ! {\n    cortex_m::asm::udf();\n}\n"
  },
  {
    "path": "examples/boot/bootloader/stm32/Cargo.toml",
    "content": "[package]\nedition = \"2024\"\nname = \"stm32-bootloader-example\"\nversion = \"0.1.0\"\ndescription = \"Example bootloader for STM32 chips\"\nlicense = \"MIT OR Apache-2.0\"\npublish = false\n\n[dependencies]\ndefmt = { version = \"1.0.1\", optional = true }\ndefmt-rtt = { version = \"1.0.0\", optional = true }\n\nembassy-stm32 = { path = \"../../../../embassy-stm32\", features = [] }\nembassy-boot-stm32 = { path = \"../../../../embassy-boot-stm32\" }\ncortex-m = { version = \"0.7.6\", features = [\"inline-asm\", \"critical-section-single-core\"] }\nembassy-sync = { version = \"0.8.0\", path = \"../../../../embassy-sync\" }\ncortex-m-rt = { version = \"0.7\" }\nembedded-storage = \"0.3.1\"\nembedded-storage-async = \"0.4.0\"\ncfg-if = \"1.0.0\"\n\n[features]\ndefmt = [\n    \"dep:defmt\",\n    \"dep:defmt-rtt\",\n    \"embassy-boot-stm32/defmt\",\n    \"embassy-stm32/defmt\",\n]\n\n[profile.dev]\ndebug = 2\ndebug-assertions = true\nincremental = false\nopt-level = 'z'\noverflow-checks = true\n\n[profile.release]\ncodegen-units = 1\ndebug = 2\ndebug-assertions = false\nincremental = false\nlto = 'fat'\nopt-level = 'z'\noverflow-checks = false\n\n# do not optimize proc-macro crates = faster builds from scratch\n[profile.dev.build-override]\ncodegen-units = 8\ndebug = false\ndebug-assertions = false\nopt-level = 0\noverflow-checks = false\n\n[profile.release.build-override]\ncodegen-units = 8\ndebug = false\ndebug-assertions = false\nopt-level = 0\noverflow-checks = false\n\n[package.metadata.embassy]\nbuild = [\n  { target = \"thumbv7em-none-eabi\", features = [\"embassy-stm32/stm32l496zg\"] }\n]\n"
  },
  {
    "path": "examples/boot/bootloader/stm32/README.md",
    "content": "# Bootloader for STM32\n\nThe bootloader uses `embassy-boot` to interact with the flash.\n\n# Usage\n\nFlash the bootloader\n\n```\ncargo flash --features embassy-stm32/stm32wl55jc-cm4 --release --chip STM32WLE5JCIx\n```\n"
  },
  {
    "path": "examples/boot/bootloader/stm32/build.rs",
    "content": "use std::env;\nuse std::fs::File;\nuse std::io::Write;\nuse std::path::PathBuf;\n\nfn main() {\n    // Put `memory.x` in our output directory and ensure it's\n    // on the linker search path.\n    let out = &PathBuf::from(env::var_os(\"OUT_DIR\").unwrap());\n    File::create(out.join(\"memory.x\"))\n        .unwrap()\n        .write_all(include_bytes!(\"memory.x\"))\n        .unwrap();\n    println!(\"cargo:rustc-link-search={}\", out.display());\n\n    // By default, Cargo will re-run a build script whenever\n    // any file in the project changes. By specifying `memory.x`\n    // here, we ensure the build script is only re-run when\n    // `memory.x` is changed.\n    println!(\"cargo:rerun-if-changed=memory.x\");\n\n    println!(\"cargo:rustc-link-arg-bins=--nmagic\");\n    println!(\"cargo:rustc-link-arg-bins=-Tlink.x\");\n    if env::var(\"CARGO_FEATURE_DEFMT\").is_ok() {\n        println!(\"cargo:rustc-link-arg-bins=-Tdefmt.x\");\n    }\n}\n"
  },
  {
    "path": "examples/boot/bootloader/stm32/memory.x",
    "content": "MEMORY\n{\n  /* NOTE 1 K = 1 KiBi = 1024 bytes */\n  FLASH                             : ORIGIN = 0x08000000, LENGTH = 24K\n  BOOTLOADER_STATE                  : ORIGIN = 0x08006000, LENGTH = 8K\n  ACTIVE                            : ORIGIN = 0x08008000, LENGTH = 32K\n  DFU                               : ORIGIN = 0x08010000, LENGTH = 36K\n  RAM                         (rwx) : ORIGIN = 0x20000000, LENGTH = 16K\n}\n\n__bootloader_state_start = ORIGIN(BOOTLOADER_STATE) - ORIGIN(FLASH);\n__bootloader_state_end = ORIGIN(BOOTLOADER_STATE) + LENGTH(BOOTLOADER_STATE) - ORIGIN(FLASH);\n\n__bootloader_active_start = ORIGIN(ACTIVE) - ORIGIN(FLASH);\n__bootloader_active_end = ORIGIN(ACTIVE) + LENGTH(ACTIVE) - ORIGIN(FLASH);\n\n__bootloader_dfu_start = ORIGIN(DFU) - ORIGIN(FLASH);\n__bootloader_dfu_end = ORIGIN(DFU) + LENGTH(DFU) - ORIGIN(FLASH);\n"
  },
  {
    "path": "examples/boot/bootloader/stm32/src/main.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse core::cell::RefCell;\n\nuse cortex_m_rt::{entry, exception};\n#[cfg(feature = \"defmt\")]\nuse defmt_rtt as _;\nuse embassy_boot_stm32::*;\nuse embassy_stm32::flash::{BANK1_REGION, Flash};\nuse embassy_sync::blocking_mutex::Mutex;\n\n#[entry]\nfn main() -> ! {\n    let p = embassy_stm32::init(Default::default());\n\n    // Uncomment this if you are debugging the bootloader with debugger/RTT attached,\n    // as it prevents a hard fault when accessing flash 'too early' after boot.\n    /*\n        for i in 0..10000000 {\n            cortex_m::asm::nop();\n        }\n    */\n\n    let layout = Flash::new_blocking(p.FLASH).into_blocking_regions();\n    let flash = Mutex::new(RefCell::new(layout.bank1_region));\n\n    let config = BootLoaderConfig::from_linkerfile_blocking(&flash, &flash, &flash);\n    let active_offset = config.active.offset();\n    let bl = BootLoader::prepare::<_, _, _, 2048>(config);\n\n    unsafe { bl.load(BANK1_REGION.base() + active_offset) }\n}\n\n#[unsafe(no_mangle)]\n#[cfg_attr(target_os = \"none\", unsafe(link_section = \".HardFault.user\"))]\nunsafe extern \"C\" fn HardFault() {\n    cortex_m::peripheral::SCB::sys_reset();\n}\n\n#[exception]\nunsafe fn DefaultHandler(_: i16) -> ! {\n    const SCB_ICSR: *const u32 = 0xE000_ED04 as *const u32;\n    let irqn = unsafe { core::ptr::read_volatile(SCB_ICSR) } as u8 as i16 - 16;\n\n    panic!(\"DefaultHandler #{:?}\", irqn);\n}\n\n#[panic_handler]\nfn panic(_info: &core::panic::PanicInfo) -> ! {\n    cortex_m::asm::udf();\n}\n"
  },
  {
    "path": "examples/boot/bootloader/stm32-dual-bank/Cargo.toml",
    "content": "[package]\nedition = \"2024\"\nname = \"stm32-bootloader-dual-bank-flash-example\"\nversion = \"0.1.0\"\ndescription = \"Example bootloader for dual-bank flash STM32 chips\"\nlicense = \"MIT OR Apache-2.0\"\npublish = false\n\n[dependencies]\ndefmt = { version = \"1.0.1\", optional = true }\ndefmt-rtt = { version = \"1.0.0\", optional = true }\n\nembassy-stm32 = { path = \"../../../../embassy-stm32\", features = [] }\nembassy-boot-stm32 = { path = \"../../../../embassy-boot-stm32\" }\ncortex-m = { version = \"0.7.6\", features = [\n  \"inline-asm\",\n  \"critical-section-single-core\",\n] }\nembassy-sync = { version = \"0.8.0\", path = \"../../../../embassy-sync\" }\ncortex-m-rt = { version = \"0.7\" }\nembedded-storage = \"0.3.1\"\nembedded-storage-async = \"0.4.0\"\ncfg-if = \"1.0.0\"\n\n[features]\ndefmt = [\"dep:defmt\", \"dep:defmt-rtt\", \"embassy-boot-stm32/defmt\", \"embassy-stm32/defmt\"]\n\n[profile.dev]\ndebug = 2\ndebug-assertions = true\nincremental = false\nopt-level = 'z'\noverflow-checks = true\n\n[profile.release]\ncodegen-units = 1\ndebug = 2\ndebug-assertions = false\nincremental = false\nlto = 'fat'\nopt-level = 'z'\noverflow-checks = false\n\n# do not optimize proc-macro crates = faster builds from scratch\n[profile.dev.build-override]\ncodegen-units = 8\ndebug = false\ndebug-assertions = false\nopt-level = 0\noverflow-checks = false\n\n[profile.release.build-override]\ncodegen-units = 8\ndebug = false\ndebug-assertions = false\nopt-level = 0\noverflow-checks = false\n\n[package.metadata.embassy]\nbuild = [\n  { target = \"thumbv7em-none-eabi\", features = [\"embassy-stm32/stm32h743zi\"] }\n]\n"
  },
  {
    "path": "examples/boot/bootloader/stm32-dual-bank/README.md",
    "content": "# STM32 dual-bank flash Bootloader\n\n## Overview\n\nThis bootloader leverages `embassy-boot` to interact with the flash.\nThis example targets STM32 devices with dual-bank flash memory, with a primary focus on the STM32H747XI series.\nUsers must modify the `memory.x` configuration file to match with the memory layout of their specific STM32 device.\n\nAdditionally, this example can be extended to utilize external flash memory, such as QSPI, for storing partitions.\n\n## Memory Configuration\n\nIn this example's `memory.x` file, various symbols are defined to assist in effective memory management within the bootloader environment.\nFor dual-bank STM32 devices, it's crucial to assign these symbols correctly to their respective memory banks.\n\n### Symbol Definitions\n\nThe bootloader's state and active symbols are anchored to the flash origin of **bank 1**:\n\n- `__bootloader_state_start` and `__bootloader_state_end`\n- `__bootloader_active_start` and `__bootloader_active_end`\n\nIn contrast, the Device Firmware Upgrade (DFU) symbols are aligned with the DFU flash origin in **bank 2**:\n\n- `__bootloader_dfu_start` and `__bootloader_dfu_end`\n\n```rust\n__bootloader_state_start = ORIGIN(BOOTLOADER_STATE) - ORIGIN(**FLASH**);\n__bootloader_state_end = ORIGIN(BOOTLOADER_STATE) + LENGTH(BOOTLOADER_STATE) - ORIGIN(**FLASH**);\n\n__bootloader_active_start = ORIGIN(ACTIVE) - ORIGIN(**FLASH**);\n__bootloader_active_end = ORIGIN(ACTIVE) + LENGTH(ACTIVE) - ORIGIN(**FLASH**);\n\n__bootloader_dfu_start = ORIGIN(DFU) - ORIGIN(**DFU**);\n__bootloader_dfu_end = ORIGIN(DFU) + LENGTH(DFU) - ORIGIN(**DFU**);\n```\n\n## Flashing the Bootloader\n\nTo flash the bootloader onto your STM32H747XI device, use the following command:\n\n```bash\ncargo flash --features embassy-stm32/stm32h747xi-cm7 --release --chip STM32H747XIHx\n```\n"
  },
  {
    "path": "examples/boot/bootloader/stm32-dual-bank/build.rs",
    "content": "use std::env;\nuse std::fs::File;\nuse std::io::Write;\nuse std::path::PathBuf;\n\nfn main() {\n    // Put `memory.x` in our output directory and ensure it's\n    // on the linker search path.\n    let out = &PathBuf::from(env::var_os(\"OUT_DIR\").unwrap());\n    File::create(out.join(\"memory.x\"))\n        .unwrap()\n        .write_all(include_bytes!(\"memory.x\"))\n        .unwrap();\n    println!(\"cargo:rustc-link-search={}\", out.display());\n\n    // By default, Cargo will re-run a build script whenever\n    // any file in the project changes. By specifying `memory.x`\n    // here, we ensure the build script is only re-run when\n    // `memory.x` is changed.\n    println!(\"cargo:rerun-if-changed=memory.x\");\n\n    println!(\"cargo:rustc-link-arg-bins=--nmagic\");\n    println!(\"cargo:rustc-link-arg-bins=-Tlink.x\");\n    if env::var(\"CARGO_FEATURE_DEFMT\").is_ok() {\n        println!(\"cargo:rustc-link-arg-bins=-Tdefmt.x\");\n    }\n}\n"
  },
  {
    "path": "examples/boot/bootloader/stm32-dual-bank/memory.x",
    "content": "MEMORY\n{\n  /* NOTE 1 K = 1 KiBi = 1024 bytes */\n  FLASH                             : ORIGIN = 0x08000000, LENGTH = 128K\n  BOOTLOADER_STATE                  : ORIGIN = 0x08020000, LENGTH = 128K\n  ACTIVE                            : ORIGIN = 0x08040000, LENGTH = 512K\n  DFU                               : ORIGIN = 0x08100000, LENGTH = 640K\n  RAM                         (rwx) : ORIGIN = 0x24000000, LENGTH = 512K\n}\n\n__bootloader_state_start = ORIGIN(BOOTLOADER_STATE) - ORIGIN(FLASH);\n__bootloader_state_end = ORIGIN(BOOTLOADER_STATE) + LENGTH(BOOTLOADER_STATE) - ORIGIN(FLASH);\n\n__bootloader_active_start = ORIGIN(ACTIVE) - ORIGIN(FLASH);\n__bootloader_active_end = ORIGIN(ACTIVE) + LENGTH(ACTIVE) - ORIGIN(FLASH);\n\n__bootloader_dfu_start = ORIGIN(DFU) - ORIGIN(DFU);\n__bootloader_dfu_end = ORIGIN(DFU) + LENGTH(DFU) - ORIGIN(DFU);\n"
  },
  {
    "path": "examples/boot/bootloader/stm32-dual-bank/src/main.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse core::cell::RefCell;\n\nuse cortex_m_rt::{entry, exception};\n#[cfg(feature = \"defmt\")]\nuse defmt_rtt as _;\nuse embassy_boot_stm32::*;\nuse embassy_stm32::flash::{BANK1_REGION, Flash};\nuse embassy_sync::blocking_mutex::Mutex;\n\n#[entry]\nfn main() -> ! {\n    let p = embassy_stm32::init(Default::default());\n\n    // Uncomment this if you are debugging the bootloader with debugger/RTT attached,\n    // as it prevents a hard fault when accessing flash 'too early' after boot.\n    /*\n        for i in 0..10000000 {\n            cortex_m::asm::nop();\n        }\n    */\n\n    let layout = Flash::new_blocking(p.FLASH).into_blocking_regions();\n    let flash_bank1 = Mutex::new(RefCell::new(layout.bank1_region));\n    let flash_bank2 = Mutex::new(RefCell::new(layout.bank2_region));\n\n    let config = BootLoaderConfig::from_linkerfile_blocking(&flash_bank1, &flash_bank2, &flash_bank1);\n    let active_offset = config.active.offset();\n    let bl = BootLoader::prepare::<_, _, _, 2048>(config);\n\n    unsafe { bl.load(BANK1_REGION.base() + active_offset) }\n}\n\n#[unsafe(no_mangle)]\n#[cfg_attr(target_os = \"none\", unsafe(link_section = \".HardFault.user\"))]\nunsafe extern \"C\" fn HardFault() {\n    cortex_m::peripheral::SCB::sys_reset();\n}\n\n#[exception]\nunsafe fn DefaultHandler(_: i16) -> ! {\n    const SCB_ICSR: *const u32 = 0xE000_ED04 as *const u32;\n    let irqn = unsafe { core::ptr::read_volatile(SCB_ICSR) } as u8 as i16 - 16;\n\n    panic!(\"DefaultHandler #{:?}\", irqn);\n}\n\n#[panic_handler]\nfn panic(_info: &core::panic::PanicInfo) -> ! {\n    cortex_m::asm::udf();\n}\n"
  },
  {
    "path": "examples/boot/bootloader/stm32wb-dfu/Cargo.toml",
    "content": "[package]\nedition = \"2024\"\nname = \"stm32wb-dfu-bootloader-example\"\nversion = \"0.1.0\"\ndescription = \"Example USB DFUbootloader for the STM32WB series of chips\"\nlicense = \"MIT OR Apache-2.0\"\npublish = false\n\n[dependencies]\ndefmt = { version = \"1.0.1\", optional = true }\ndefmt-rtt = { version = \"1.0.0\", optional = true }\n\nembassy-stm32 = { path = \"../../../../embassy-stm32\", features = [] }\nembassy-boot-stm32 = { path = \"../../../../embassy-boot-stm32\" }\ncortex-m = { version = \"0.7.6\", features = [\"inline-asm\", \"critical-section-single-core\"] }\nembassy-sync = { version = \"0.8.0\", path = \"../../../../embassy-sync\" }\ncortex-m-rt = { version = \"0.7\" }\nembedded-storage = \"0.3.1\"\nembedded-storage-async = \"0.4.0\"\ncfg-if = \"1.0.0\"\nembassy-usb-dfu = { version = \"0.3.0\", path = \"../../../../embassy-usb-dfu\", features = [\"dfu\", \"cortex-m\"] }\nembassy-usb = { version = \"0.6.0\", path = \"../../../../embassy-usb\", default-features = false }\nembassy-futures = { version = \"0.1.2\", path = \"../../../../embassy-futures\" }\n\n[features]\ndefmt = [\n    \"dep:defmt\",\n    \"dep:defmt-rtt\",\n    \"embassy-boot-stm32/defmt\",\n    \"embassy-stm32/defmt\",\n    \"embassy-usb/defmt\",\n    \"embassy-usb-dfu/defmt\"\n]\nverify = [\"embassy-usb-dfu/ed25519-salty\"]\n\n[profile.dev]\ndebug = 2\ndebug-assertions = true\nincremental = false\nopt-level = 'z'\noverflow-checks = true\n\n[profile.release]\ncodegen-units = 1\ndebug = 2\ndebug-assertions = false\nincremental = false\nlto = 'fat'\nopt-level = 'z'\noverflow-checks = false\n\n# do not optimize proc-macro crates = faster builds from scratch\n[profile.dev.build-override]\ncodegen-units = 8\ndebug = false\ndebug-assertions = false\nopt-level = 0\noverflow-checks = false\n\n[profile.release.build-override]\ncodegen-units = 8\ndebug = false\ndebug-assertions = false\nopt-level = 0\noverflow-checks = false\n\n[package.metadata.embassy]\nbuild = [\n  { target = \"thumbv7em-none-eabi\", features = [\"embassy-stm32/stm32wb55rg\"] },\n  { target = \"thumbv7em-none-eabi\", features = [\"embassy-stm32/stm32wb55rg\", \"verify\"] }\n]\n"
  },
  {
    "path": "examples/boot/bootloader/stm32wb-dfu/README.md",
    "content": "# Bootloader for STM32\n\nThis bootloader implementation uses `embassy-boot` and `embassy-usb-dfu` to manage firmware updates and interact with the flash memory on STM32WB55 devices.\n\n## Prerequisites\n\n- Rust toolchain with `cargo` installed\n- `cargo-flash` for flashing the bootloader\n- `dfu-util` for firmware updates\n- `cargo-binutils` for binary generation\n\n## Usage\n\n### 1. Flash the Bootloader\n\nFirst, flash the bootloader to your device:\n\n```\ncargo flash --features embassy-stm32/stm32wb55rg --release --chip STM32WB55RGVx\n```\n\n### 2. Build and Flash Application\n\nGenerate your application binary and flash it using DFU:\n\n```\ncargo objcopy --release -- -O binary fw.bin\ndfu-util -d c0de:cafe -w -D fw.bin\n```\n\n### 3. Sign Updates Before Flashing (Optional)\n\nCurrently, embassy-usb-dfu only supports a limited implementation of the generic support for ed25519-based update verfication in embassy-boot. This implementation assumes that a signature is simply concatenated to the end of an update binary. For more details, please see https://embassy.dev/book/#_verification and/or refer to the documentation for embassy-boot-dfu.\n\nTo sign (and then verify) application updates, you will first need to generate a key pair:\n\n```\nsignify-openbsd -G -n -p secrets/key.pub -s secrets/key.sec\ntail -n1 secrets/key.pub | base64 -d -i - | dd ibs=10 skip=1 > secrets/key.pub.short\n```\n\nThen you will need to sign all you binaries with the private key:\n\n```\ncargo objcopy --release -- -O binary fw.bin\nshasum -a 512 -b fw.bin | head -c128 | xxd -p -r > target/fw-hash.txt\nsignify-openbsd -S -s secrets/key.sec -m target/fw-hash.txt -x target/fw-hash.sig\ncp fw.bin fw-signed.bin\ntail -n1 target/fw-hash.sig | base64 -d -i - | dd ibs=10 skip=1 >> fw-signed.bin\ndfu-util -d c0de:cafe -w -D fw-signed.bin\n```\n\nFinally, as shown in this example with the `verify` feature flag enabled, you then need to embed the public key into your bootloader so that it can verify update signatures.\n\nN.B. Please note that the exact steps above are NOT a good example of how to manage your keys securely. In a production environment, you should take great care to ensure that (at least the private key) is protected and not leaked into your version control system.\n\n## Troubleshooting\n\n- Make sure your device is in DFU mode before flashing\n- Verify the USB VID:PID matches your device (c0de:cafe)\n- Check USB connections if the device is not detected\n- Make sure the transfer size option of `dfu-util` matches the bootloader configuration. By default, `dfu-util` will use the transfer size reported by the device, but you can override it with the `-t` option if needed.\n- Make sure `control_buf` size is larger than or equal to the `usb_dfu` `BLOCK_SIZE` parameter (in this example, both are set to 4096 bytes).\n"
  },
  {
    "path": "examples/boot/bootloader/stm32wb-dfu/build.rs",
    "content": "use std::env;\nuse std::fs::File;\nuse std::io::Write;\nuse std::path::PathBuf;\n\nfn main() {\n    // Put `memory.x` in our output directory and ensure it's\n    // on the linker search path.\n    let out = &PathBuf::from(env::var_os(\"OUT_DIR\").unwrap());\n    File::create(out.join(\"memory.x\"))\n        .unwrap()\n        .write_all(include_bytes!(\"memory.x\"))\n        .unwrap();\n    println!(\"cargo:rustc-link-search={}\", out.display());\n\n    // By default, Cargo will re-run a build script whenever\n    // any file in the project changes. By specifying `memory.x`\n    // here, we ensure the build script is only re-run when\n    // `memory.x` is changed.\n    println!(\"cargo:rerun-if-changed=memory.x\");\n\n    println!(\"cargo:rustc-link-arg-bins=--nmagic\");\n    println!(\"cargo:rustc-link-arg-bins=-Tlink.x\");\n    if env::var(\"CARGO_FEATURE_DEFMT\").is_ok() {\n        println!(\"cargo:rustc-link-arg-bins=-Tdefmt.x\");\n    }\n}\n"
  },
  {
    "path": "examples/boot/bootloader/stm32wb-dfu/memory.x",
    "content": "MEMORY\n{\n  /* NOTE 1 K = 1 KiBi = 1024 bytes */\n  FLASH                             : ORIGIN = 0x08000000, LENGTH = 48K\n  BOOTLOADER_STATE                  : ORIGIN = 0x0800C000, LENGTH = 4K\n  ACTIVE                            : ORIGIN = 0x0800D000, LENGTH = 120K\n  DFU                               : ORIGIN = 0x0802B000, LENGTH = 120K\n  RAM                         (rwx) : ORIGIN = 0x20000000, LENGTH = 16K\n}\n\n__bootloader_state_start = ORIGIN(BOOTLOADER_STATE) - ORIGIN(FLASH);\n__bootloader_state_end = ORIGIN(BOOTLOADER_STATE) + LENGTH(BOOTLOADER_STATE) - ORIGIN(FLASH);\n\n__bootloader_active_start = ORIGIN(ACTIVE) - ORIGIN(FLASH);\n__bootloader_active_end = ORIGIN(ACTIVE) + LENGTH(ACTIVE) - ORIGIN(FLASH);\n\n__bootloader_dfu_start = ORIGIN(DFU) - ORIGIN(FLASH);\n__bootloader_dfu_end = ORIGIN(DFU) + LENGTH(DFU) - ORIGIN(FLASH);\n"
  },
  {
    "path": "examples/boot/bootloader/stm32wb-dfu/secrets/key.pub.short",
    "content": "gBpMSzKg\u0019!F\u0002!4r"
  },
  {
    "path": "examples/boot/bootloader/stm32wb-dfu/src/main.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse core::cell::RefCell;\n\nuse cortex_m_rt::{entry, exception};\n#[cfg(feature = \"defmt\")]\nuse defmt_rtt as _;\nuse embassy_boot_stm32::*;\nuse embassy_stm32::flash::{BANK1_REGION, Flash, WRITE_SIZE};\nuse embassy_stm32::rcc::WPAN_DEFAULT;\nuse embassy_stm32::usb::Driver;\nuse embassy_stm32::{bind_interrupts, peripherals, usb};\nuse embassy_sync::blocking_mutex::Mutex;\nuse embassy_usb::{Builder, msos};\nuse embassy_usb_dfu::consts::DfuAttributes;\nuse embassy_usb_dfu::{ResetImmediate, new_state, usb_dfu};\n\nbind_interrupts!(struct Irqs {\n    USB_LP => usb::InterruptHandler<peripherals::USB>;\n});\n\n// This is a randomly generated GUID to allow clients on Windows to find your device.\n//\n// N.B. update to a custom GUID for your own device!\nconst DEVICE_INTERFACE_GUIDS: &[&str] = &[\"{EAA9A5DC-30BA-44BC-9232-606CDC875321}\"];\n\n// This is a randomly generated example key.\n//\n// N.B. Please replace with your own!\n#[cfg(feature = \"verify\")]\nstatic PUBLIC_SIGNING_KEY: &[u8; 32] = include_bytes!(\"../secrets/key.pub.short\");\n\n#[entry]\nfn main() -> ! {\n    let mut config = embassy_stm32::Config::default();\n    config.rcc = WPAN_DEFAULT;\n    let p = embassy_stm32::init(config);\n\n    // Prevent a hard fault when accessing flash 'too early' after boot.\n    #[cfg(feature = \"defmt\")]\n    for _ in 0..10000000 {\n        cortex_m::asm::nop();\n    }\n\n    let layout = Flash::new_blocking(p.FLASH).into_blocking_regions();\n    let flash = Mutex::new(RefCell::new(layout.bank1_region));\n\n    let config = BootLoaderConfig::from_linkerfile_blocking(&flash, &flash, &flash);\n    let active_offset = config.active.offset();\n    let bl = BootLoader::prepare::<_, _, _, 2048>(config);\n    if bl.state == State::DfuDetach {\n        let driver = Driver::new(p.USB, Irqs, p.PA12, p.PA11);\n        let mut config = embassy_usb::Config::new(0xc0de, 0xcafe);\n        config.manufacturer = Some(\"Embassy\");\n        config.product = Some(\"USB-DFU Bootloader example\");\n        config.serial_number = Some(\"1235678\");\n\n        let fw_config = FirmwareUpdaterConfig::from_linkerfile_blocking(&flash, &flash);\n        let mut buffer = AlignedBuffer([0; WRITE_SIZE]);\n        let updater = BlockingFirmwareUpdater::new(fw_config, &mut buffer.0[..]);\n\n        let mut config_descriptor = [0; 256];\n        let mut bos_descriptor = [0; 256];\n        let mut control_buf = [0; 4096];\n\n        #[cfg(not(feature = \"verify\"))]\n        let mut state = new_state(updater, DfuAttributes::CAN_DOWNLOAD, ResetImmediate);\n\n        #[cfg(feature = \"verify\")]\n        let mut state = new_state(updater, DfuAttributes::CAN_DOWNLOAD, ResetImmediate, PUBLIC_SIGNING_KEY);\n\n        let mut builder = Builder::new(\n            driver,\n            config,\n            &mut config_descriptor,\n            &mut bos_descriptor,\n            &mut [],\n            &mut control_buf,\n        );\n\n        // We add MSOS headers so that the device automatically gets assigned the WinUSB driver on Windows.\n        // Otherwise users need to do this manually using a tool like Zadig.\n        //\n        // It seems these always need to be at added at the device level for this to work and for\n        // composite devices they also need to be added on the function level (as shown later).\n        //\n        builder.msos_descriptor(msos::windows_version::WIN8_1, 2);\n        builder.msos_feature(msos::CompatibleIdFeatureDescriptor::new(\"WINUSB\", \"\"));\n        builder.msos_feature(msos::RegistryPropertyFeatureDescriptor::new(\n            \"DeviceInterfaceGUIDs\",\n            msos::PropertyData::RegMultiSz(DEVICE_INTERFACE_GUIDS),\n        ));\n\n        usb_dfu::<_, _, _, _, 4096>(&mut builder, &mut state, |func| {\n            // You likely don't have to add these function level headers if your USB device is not composite\n            // (i.e. if your device does not expose another interface in addition to DFU)\n            func.msos_feature(msos::CompatibleIdFeatureDescriptor::new(\"WINUSB\", \"\"));\n            func.msos_feature(msos::RegistryPropertyFeatureDescriptor::new(\n                \"DeviceInterfaceGUIDs\",\n                msos::PropertyData::RegMultiSz(DEVICE_INTERFACE_GUIDS),\n            ));\n        });\n\n        let mut dev = builder.build();\n        embassy_futures::block_on(dev.run());\n    }\n\n    unsafe { bl.load(BANK1_REGION.base() + active_offset) }\n}\n\n#[unsafe(no_mangle)]\n#[cfg_attr(target_os = \"none\", unsafe(link_section = \".HardFault.user\"))]\nunsafe extern \"C\" fn HardFault() {\n    cortex_m::peripheral::SCB::sys_reset();\n}\n\n#[exception]\nunsafe fn DefaultHandler(_: i16) -> ! {\n    const SCB_ICSR: *const u32 = 0xE000_ED04 as *const u32;\n    let irqn = unsafe { core::ptr::read_volatile(SCB_ICSR) } as u8 as i16 - 16;\n\n    panic!(\"DefaultHandler #{:?}\", irqn);\n}\n\n#[panic_handler]\nfn panic(_info: &core::panic::PanicInfo) -> ! {\n    cortex_m::asm::udf();\n}\n"
  },
  {
    "path": "examples/boot/bootloader/stm32wba-dfu/.cargo/config.toml",
    "content": "[target.'cfg(all(target_arch = \"arm\", target_os = \"none\"))']\nrunner = \"probe-rs run --chip STM32WBA65RI\"\n\n[build]\ntarget = \"thumbv8m.main-none-eabihf\"\n\n[env]\nDEFMT_LOG = \"trace\"\n"
  },
  {
    "path": "examples/boot/bootloader/stm32wba-dfu/Cargo.toml",
    "content": "[package]\nedition = \"2024\"\nname = \"stm32wba6-dfu-bootloader-example\"\nversion = \"0.1.0\"\ndescription = \"Example USB DFUbootloader for the STM32WBA series of chips\"\nlicense = \"MIT OR Apache-2.0\"\npublish = false\n\n[dependencies]\ndefmt = { version = \"1.0.1\", optional = true }\ndefmt-rtt = { version = \"1.0.0\", optional = true }\n\nembassy-stm32 = { path = \"../../../../embassy-stm32\", features = [] }\nembassy-boot-stm32 = { path = \"../../../../embassy-boot-stm32\" }\ncortex-m = { version = \"0.7.6\", features = [\"inline-asm\", \"critical-section-single-core\"] }\nembassy-sync = { version = \"0.8.0\", path = \"../../../../embassy-sync\" }\ncortex-m-rt = { version = \"0.7\" }\nembedded-storage = \"0.3.1\"\nembedded-storage-async = \"0.4.0\"\ncfg-if = \"1.0.0\"\nembassy-usb-dfu = { version = \"0.3.0\", path = \"../../../../embassy-usb-dfu\", features = [\"dfu\", \"cortex-m\"] }\nembassy-usb = { version = \"0.6.0\", path = \"../../../../embassy-usb\", default-features = false }\nembassy-futures = { version = \"0.1.2\", path = \"../../../../embassy-futures\" }\n\n[features]\ndefmt = [\n    \"dep:defmt\",\n    \"dep:defmt-rtt\",\n    \"embassy-boot-stm32/defmt\",\n    \"embassy-stm32/defmt\",\n    \"embassy-usb/defmt\",\n    \"embassy-usb-dfu/defmt\"\n]\nverify = [\"embassy-usb-dfu/ed25519-salty\"]\n\n[profile.dev]\ndebug = 2\ndebug-assertions = true\nincremental = false\nopt-level = 'z'\noverflow-checks = true\n\n[profile.release]\ncodegen-units = 1\ndebug = 2\ndebug-assertions = false\nincremental = false\nlto = 'fat'\nopt-level = 'z'\noverflow-checks = false\n\n# do not optimize proc-macro crates = faster builds from scratch\n[profile.dev.build-override]\ncodegen-units = 8\ndebug = false\ndebug-assertions = false\nopt-level = 0\noverflow-checks = false\n\n[profile.release.build-override]\ncodegen-units = 8\ndebug = false\ndebug-assertions = false\nopt-level = 0\noverflow-checks = false\n\n[package.metadata.embassy]\nbuild = [\n  { target = \"thumbv8m.main-none-eabihf\", features = [\"embassy-stm32/stm32wba65ri\", \"verify\"] }\n]\n"
  },
  {
    "path": "examples/boot/bootloader/stm32wba-dfu/README.md",
    "content": "# Bootloader for STM32\n\nThis bootloader implementation uses `embassy-boot` and `embassy-usb-dfu` to manage firmware updates and interact with the flash memory on STM32WB55 devices.\n\n## Prerequisites\n\n- Rust toolchain with `cargo` installed\n- `cargo-flash` for flashing the bootloader\n- `dfu-util` for firmware updates\n- `cargo-binutils` for binary generation\n\n## Usage\n\n### 1. Flash the Bootloader\n\nFirst, flash the bootloader to your device:\n\n```\ncargo flash --features embassy-stm32/stm32wba65ri --release --chip STM32WBA65RI\n```\n\n### 2. Build and Flash Application\n\nGenerate your application binary and flash it using DFU:\n\n```\ncargo objcopy --release -- -O binary fw.bin\ndfu-util -d c0de:cafe -w -D fw.bin\n```\n\n### 3. Sign Updates Before Flashing (Optional)\n\nCurrently, embassy-usb-dfu only supports a limited implementation of the generic support for ed25519-based update verfication in embassy-boot. This implementation assumes that a signature is simply concatenated to the end of an update binary. For more details, please see https://embassy.dev/book/#_verification and/or refer to the documentation for embassy-boot-dfu.\n\nTo sign (and then verify) application updates, you will first need to generate a key pair:\n\n```\nsignify-openbsd -G -n -p secrets/key.pub -s secrets/key.sec\ntail -n1 secrets/key.pub | base64 -d -i - | dd ibs=10 skip=1 > secrets/key.pub.short\n```\n\nThen you will need to sign all you binaries with the private key:\n\n```\ncargo objcopy --release -- -O binary fw.bin\nshasum -a 512 -b fw.bin | head -c128 | xxd -p -r > target/fw-hash.txt\nsignify-openbsd -S -s secrets/key.sec -m target/fw-hash.txt -x target/fw-hash.sig\ncp fw.bin fw-signed.bin\ntail -n1 target/fw-hash.sig | base64 -d -i - | dd ibs=10 skip=1 >> fw-signed.bin\ndfu-util -d c0de:cafe -w -D fw-signed.bin\n```\n\nFinally, as shown in this example with the `verify` feature flag enabled, you then need to embed the public key into your bootloader so that it can verify update signatures.\n\nN.B. Please note that the exact steps above are NOT a good example of how to manage your keys securely. In a production environment, you should take great care to ensure that (at least the private key) is protected and not leaked into your version control system.\n\n## Troubleshooting\n\n- Make sure your device is in DFU mode before flashing\n- Verify the USB VID:PID matches your device (c0de:cafe)\n- Check USB connections if the device is not detected\n- Make sure the transfer size option of `dfu-util` matches the bootloader configuration. By default, `dfu-util` will use the transfer size reported by the device, but you can override it with the `-t` option if needed.\n- Make sure `control_buf` size is larger than or equal to the `usb_dfu` `BLOCK_SIZE` parameter (in this example, both are set to 4096 bytes).\n"
  },
  {
    "path": "examples/boot/bootloader/stm32wba-dfu/build.rs",
    "content": "use std::env;\nuse std::fs::File;\nuse std::io::Write;\nuse std::path::PathBuf;\n\nfn main() {\n    // Put `memory.x` in our output directory and ensure it's\n    // on the linker search path.\n    let out = &PathBuf::from(env::var_os(\"OUT_DIR\").unwrap());\n    File::create(out.join(\"memory.x\"))\n        .unwrap()\n        .write_all(include_bytes!(\"memory.x\"))\n        .unwrap();\n    println!(\"cargo:rustc-link-search={}\", out.display());\n\n    // By default, Cargo will re-run a build script whenever\n    // any file in the project changes. By specifying `memory.x`\n    // here, we ensure the build script is only re-run when\n    // `memory.x` is changed.\n    println!(\"cargo:rerun-if-changed=memory.x\");\n\n    println!(\"cargo:rustc-link-arg-bins=--nmagic\");\n    println!(\"cargo:rustc-link-arg-bins=-Tlink.x\");\n    if env::var(\"CARGO_FEATURE_DEFMT\").is_ok() {\n        println!(\"cargo:rustc-link-arg-bins=-Tdefmt.x\");\n    }\n}\n"
  },
  {
    "path": "examples/boot/bootloader/stm32wba-dfu/memory.x",
    "content": "MEMORY\n{\n  /* NOTE 1 K = 1 KiBi = 1024 bytes */\n  FLASH                             : ORIGIN = 0x08000000, LENGTH = 80K\n  BOOTLOADER_STATE                  : ORIGIN = 0x08014000, LENGTH = 8K\n  ACTIVE                            : ORIGIN = 0x08016000, LENGTH = 120K\n  DFU                               : ORIGIN = 0x0803C000, LENGTH = 160K\n  RAM                         (rwx) : ORIGIN = 0x20000000, LENGTH = 400K\n}\n\n__bootloader_state_start = ORIGIN(BOOTLOADER_STATE) - ORIGIN(FLASH);\n__bootloader_state_end = ORIGIN(BOOTLOADER_STATE) + LENGTH(BOOTLOADER_STATE) - ORIGIN(FLASH);\n\n__bootloader_active_start = ORIGIN(ACTIVE) - ORIGIN(FLASH);\n__bootloader_active_end = ORIGIN(ACTIVE) + LENGTH(ACTIVE) - ORIGIN(FLASH);\n\n__bootloader_dfu_start = ORIGIN(DFU) - ORIGIN(FLASH);\n__bootloader_dfu_end = ORIGIN(DFU) + LENGTH(DFU) - ORIGIN(FLASH);"
  },
  {
    "path": "examples/boot/bootloader/stm32wba-dfu/secrets/key.pub.short",
    "content": "gBpMSzKg\u0019!F\u0002!4r"
  },
  {
    "path": "examples/boot/bootloader/stm32wba-dfu/src/main.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse core::cell::RefCell;\n\nuse cortex_m_rt::{entry, exception};\n#[cfg(feature = \"defmt\")]\nuse defmt_rtt as _;\nuse embassy_boot_stm32::*;\nuse embassy_stm32::flash::{BANK1_REGION, Flash, WRITE_SIZE};\nuse embassy_stm32::usb::Driver;\nuse embassy_stm32::{Config, bind_interrupts, peripherals, usb};\nuse embassy_sync::blocking_mutex::Mutex;\nuse embassy_usb::{Builder, msos};\nuse embassy_usb_dfu::consts::DfuAttributes;\nuse embassy_usb_dfu::{ResetImmediate, new_state, usb_dfu};\n\nbind_interrupts!(struct Irqs {\n    USB_OTG_HS => usb::InterruptHandler<peripherals::USB_OTG_HS>;\n});\n\n// This is a randomly generated GUID to allow clients on Windows to find your device.\n//\n// N.B. update to a custom GUID for your own device!\nconst DEVICE_INTERFACE_GUIDS: &[&str] = &[\"{EAA9A5DC-30BA-44BC-9232-606CDC875321}\"];\n\n// This is a randomly generated example key.\n//\n// N.B. Please replace with your own!\n#[cfg(feature = \"verify\")]\nstatic PUBLIC_SIGNING_KEY: &[u8; 32] = include_bytes!(\"../secrets/key.pub.short\");\n\n#[entry]\nfn main() -> ! {\n    let mut config = Config::default();\n\n    {\n        use embassy_stm32::rcc::*;\n        config.rcc.pll1 = Some(Pll {\n            source: PllSource::HSI,\n            prediv: PllPreDiv::DIV1,   // PLLM = 1 → HSI / 1 = 16 MHz\n            mul: PllMul::MUL30,        // PLLN = 30 → 16 MHz * 30 = 480 MHz VCO\n            divr: Some(PllDiv::DIV5),  // PLLR = 5 → 96 MHz (Sysclk)\n            divq: Some(PllDiv::DIV10), // PLLQ = 10 → 48 MHz\n            divp: Some(PllDiv::DIV30), // PLLP = 30 → 16 MHz (USB_OTG_HS)\n            frac: Some(0),             // Fractional part (disabled)\n        });\n\n        config.rcc.ahb_pre = AHBPrescaler::DIV1;\n        config.rcc.apb1_pre = APBPrescaler::DIV1;\n        config.rcc.apb2_pre = APBPrescaler::DIV1;\n        config.rcc.apb7_pre = APBPrescaler::DIV1;\n        config.rcc.ahb5_pre = AHB5Prescaler::DIV4;\n\n        config.rcc.voltage_scale = VoltageScale::RANGE1;\n        config.rcc.mux.otghssel = mux::Otghssel::PLL1_P;\n        config.rcc.sys = Sysclk::PLL1_R;\n    }\n\n    let p = embassy_stm32::init(config);\n\n    // Prevent a hard fault when accessing flash 'too early' after boot.\n    #[cfg(feature = \"defmt\")]\n    for _ in 0..10000000 {\n        cortex_m::asm::nop();\n    }\n\n    let layout = Flash::new_blocking(p.FLASH).into_blocking_regions();\n    let flash = Mutex::new(RefCell::new(layout.bank1_region));\n\n    let config = BootLoaderConfig::from_linkerfile_blocking(&flash, &flash, &flash);\n    let active_offset = config.active.offset();\n    let bl = BootLoader::prepare::<_, _, _, 2048>(config);\n\n    // Create the driver, from the HAL.\n    let mut ep_out_buffer = [0u8; 256];\n    let mut config = embassy_stm32::usb::Config::default();\n\n    config.vbus_detection = false;\n\n    if bl.state == State::DfuDetach {\n        let driver = Driver::new_hs(p.USB_OTG_HS, Irqs, p.PD6, p.PD7, &mut ep_out_buffer, config);\n        let mut config = embassy_usb::Config::new(0xc0de, 0xcafe);\n        config.manufacturer = Some(\"Embassy\");\n        config.product = Some(\"USB-DFU Bootloader example\");\n        config.serial_number = Some(\"1235678\");\n\n        let fw_config = FirmwareUpdaterConfig::from_linkerfile_blocking(&flash, &flash);\n        let mut buffer = AlignedBuffer([0; WRITE_SIZE]);\n        let updater = BlockingFirmwareUpdater::new(fw_config, &mut buffer.0[..]);\n\n        let mut config_descriptor = [0; 256];\n        let mut bos_descriptor = [0; 256];\n        let mut control_buf = [0; 4096];\n\n        #[cfg(not(feature = \"verify\"))]\n        let mut state = new_state(updater, DfuAttributes::CAN_DOWNLOAD, ResetImmediate);\n\n        #[cfg(feature = \"verify\")]\n        let mut state = new_state(updater, DfuAttributes::CAN_DOWNLOAD, ResetImmediate, PUBLIC_SIGNING_KEY);\n\n        let mut builder = Builder::new(\n            driver,\n            config,\n            &mut config_descriptor,\n            &mut bos_descriptor,\n            &mut [],\n            &mut control_buf,\n        );\n\n        // We add MSOS headers so that the device automatically gets assigned the WinUSB driver on Windows.\n        // Otherwise users need to do this manually using a tool like Zadig.\n        //\n        // It seems these always need to be at added at the device level for this to work and for\n        // composite devices they also need to be added on the function level (as shown later).\n\n        builder.msos_descriptor(msos::windows_version::WIN8_1, 2);\n        builder.msos_feature(msos::CompatibleIdFeatureDescriptor::new(\"WINUSB\", \"\"));\n        builder.msos_feature(msos::RegistryPropertyFeatureDescriptor::new(\n            \"DeviceInterfaceGUIDs\",\n            msos::PropertyData::RegMultiSz(DEVICE_INTERFACE_GUIDS),\n        ));\n\n        usb_dfu::<_, _, _, _, 4096>(&mut builder, &mut state, |func| {\n            // You likely don't have to add these function level headers if your USB device is not composite\n            // (i.e. if your device does not expose another interface in addition to DFU)\n            func.msos_feature(msos::CompatibleIdFeatureDescriptor::new(\"WINUSB\", \"\"));\n            func.msos_feature(msos::RegistryPropertyFeatureDescriptor::new(\n                \"DeviceInterfaceGUIDs\",\n                msos::PropertyData::RegMultiSz(DEVICE_INTERFACE_GUIDS),\n            ));\n        });\n\n        let mut dev = builder.build();\n        embassy_futures::block_on(dev.run());\n    }\n\n    unsafe { bl.load(BANK1_REGION.base() + active_offset) }\n}\n\n#[unsafe(no_mangle)]\n#[cfg_attr(target_os = \"none\", unsafe(link_section = \".HardFault.user\"))]\nunsafe extern \"C\" fn HardFault() {\n    cortex_m::peripheral::SCB::sys_reset();\n}\n\n#[exception]\nunsafe fn DefaultHandler(_: i16) -> ! {\n    const SCB_ICSR: *const u32 = 0xE000_ED04 as *const u32;\n    let irqn = unsafe { core::ptr::read_volatile(SCB_ICSR) } as u8 as i16 - 16;\n\n    panic!(\"DefaultHandler #{:?}\", irqn);\n}\n\n#[panic_handler]\nfn panic(_info: &core::panic::PanicInfo) -> ! {\n    cortex_m::asm::udf();\n}\n"
  },
  {
    "path": "examples/lpc55s69/.cargo/config.toml",
    "content": "[target.'cfg(all(target_arch = \"arm\", target_os = \"none\"))']\nrunner = \"probe-rs run --chip LPC55S69JBD100\"\n\n[build]\ntarget = \"thumbv8m.main-none-eabihf\"\n\n[env]\nDEFMT_LOG = \"debug\"\n"
  },
  {
    "path": "examples/lpc55s69/Cargo.toml",
    "content": "[package]\nedition = \"2024\"\nname = \"embassy-nxp-lpc55s69-examples\"\nversion = \"0.1.0\"\nlicense = \"MIT OR Apache-2.0\"\n\npublish = false\n\n[dependencies]\nembassy-nxp = { version = \"0.1.0\", path = \"../../embassy-nxp\", features = [\"lpc55-core0\", \"rt\", \"defmt\", \"time-driver-rtc\"] }\nembassy-executor = { version = \"0.10.0\", path = \"../../embassy-executor\", features = [\"platform-cortex-m\", \"executor-thread\", \"executor-interrupt\"] }\nembassy-sync = { version = \"0.8.0\", path = \"../../embassy-sync\", features = [\"defmt\"] }\nembassy-time = { version = \"0.5.1\", path = \"../../embassy-time\", features = [\"defmt\", \"tick-hz-32_768\"] }\npanic-halt = \"1.0.0\"\ncortex-m = { version = \"0.7.6\", features = [\"inline-asm\", \"critical-section-single-core\"] }\ncortex-m-rt = { version = \"0.7.0\"}\ndefmt = \"1.0.1\"\ndefmt-rtt = \"1.0.0\"\npanic-probe = { version = \"1.0.0\", features = [\"print-defmt\"] }\npanic-semihosting = \"0.6.0\"\n\n[profile.release]\ndebug = 2\n\n[package.metadata.embassy]\nbuild = [\n  { target = \"thumbv8m.main-none-eabihf\", artifact-dir = \"out/examples/lpc55s69\" }\n]\n"
  },
  {
    "path": "examples/lpc55s69/README.md",
    "content": "# LPC55S69 Examples\n\n## Available examples:\n- blinky_nop: Blink the integrated RED LED using nops as delay. Useful for flashing simple and known-good software on board.\n- button_executor: Turn on/off an LED by pressing the USER button. Demonstrates how to use the PINT and GPIO drivers.\n- blinky_embassy_time: Blink the integrated RED LED using `embassy-time`. Demonstrates how to use the time-driver that uses RTC. \n\n## Important Notes\n\nOn older version of probe-rs, some examples (such as `blinky_embassy_time`) do not work directly after flashing and the board must be reset after flashing. It is reccomended to update the version of probe-rs to the latest one.\n\nWhen developing drivers for this board, probe-rs might not be able to flash the board after entering a fault. Either reset the board to clear the fault, or use NXP's proprietary software `LinkServer`/`LinkFlash` to bring the board back to a known-good state."
  },
  {
    "path": "examples/lpc55s69/build.rs",
    "content": "//! This build script copies the `memory.x` file from the crate root into\n//! a directory where the linker can always find it at build time.\n//! For many projects this is optional, as the linker always searches the\n//! project root directory -- wherever `Cargo.toml` is. However, if you\n//! are using a workspace or have a more complicated build setup, this\n//! build script becomes required. Additionally, by requesting that\n//! Cargo re-run the build script whenever `memory.x` is changed,\n//! updating `memory.x` ensures a rebuild of the application with the\n//! new memory settings.\n\nuse std::env;\nuse std::fs::File;\nuse std::io::Write;\nuse std::path::PathBuf;\n\nfn main() {\n    // Put `memory.x` in our output directory and ensure it's\n    // on the linker search path.\n    let out = &PathBuf::from(env::var_os(\"OUT_DIR\").unwrap());\n    File::create(out.join(\"memory.x\"))\n        .unwrap()\n        .write_all(include_bytes!(\"memory.x\"))\n        .unwrap();\n    println!(\"cargo:rustc-link-search={}\", out.display());\n\n    // By default, Cargo will re-run a build script whenever\n    // any file in the project changes. By specifying `memory.x`\n    // here, we ensure the build script is only re-run when\n    // `memory.x` is changed.\n    println!(\"cargo:rerun-if-changed=memory.x\");\n\n    println!(\"cargo:rustc-link-arg-bins=--nmagic\");\n    println!(\"cargo:rustc-link-arg-bins=-Tlink.x\");\n    println!(\"cargo:rustc-link-arg-bins=-Tdefmt.x\");\n}\n"
  },
  {
    "path": "examples/lpc55s69/memory.x",
    "content": "/* File originally from lpc55-hal repo: https://github.com/lpc55/lpc55-hal/blob/main/memory.x */ \nMEMORY\n{\n  FLASH : ORIGIN = 0x00000000, LENGTH = 512K\n\n  /* for use with standard link.x */\n  RAM : ORIGIN = 0x20000000, LENGTH = 256K\n\n  /* would be used with proper link.x */\n  /* needs changes to r0 (initialization code) */\n  /* SRAM0 : ORIGIN = 0x20000000, LENGTH = 64K */\n  /* SRAM1 : ORIGIN = 0x20010000, LENGTH = 64K */\n  /* SRAM2 : ORIGIN = 0x20020000, LENGTH = 64K */\n  /* SRAM3 : ORIGIN = 0x20030000, LENGTH = 64K */\n\n  /* CASPER SRAM regions */\n  /* SRAMX0: ORIGIN = 0x1400_0000, LENGTH = 4K /1* to 0x1400_0FFF *1/ */\n  /* SRAMX1: ORIGIN = 0x1400_4000, LENGTH = 4K /1* to 0x1400_4FFF *1/ */\n\n  /* USB1 SRAM regin */\n  /* USB1_SRAM : ORIGIN = 0x40100000, LENGTH = 16K */\n\n  /* To define our own USB RAM section in one regular */\n  /* RAM, probably easiest to shorten length of RAM */\n  /* above, and use this freed RAM section */\n\n}\n\n"
  },
  {
    "path": "examples/lpc55s69/src/bin/blinky_embassy_time.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_nxp::gpio::{Level, Output};\nuse embassy_time::Timer;\nuse {defmt_rtt as _, panic_halt as _};\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let p = embassy_nxp::init(Default::default());\n    info!(\"Initialization complete\");\n    let mut led = Output::new(p.PIO1_6, Level::Low);\n\n    info!(\"Entering main loop\");\n    loop {\n        info!(\"led off!\");\n        led.set_high();\n        Timer::after_millis(500).await;\n\n        info!(\"led on!\");\n        led.set_low();\n        Timer::after_millis(500).await;\n    }\n}\n"
  },
  {
    "path": "examples/lpc55s69/src/bin/blinky_nop.rs",
    "content": "//! This example has been made with the LPCXpresso55S69 board in mind, which has a built-in LED on PIO1_6.\n\n#![no_std]\n#![no_main]\n\nuse cortex_m::asm::nop;\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_nxp::gpio::{Level, Output};\nuse {defmt_rtt as _, panic_halt as _};\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let p = embassy_nxp::init(Default::default());\n\n    let mut led = Output::new(p.PIO1_6, Level::Low);\n\n    loop {\n        info!(\"led off!\");\n        led.set_high();\n\n        for _ in 0..200_000 {\n            nop();\n        }\n\n        info!(\"led on!\");\n        led.set_low();\n\n        for _ in 0..200_000 {\n            nop();\n        }\n    }\n}\n"
  },
  {
    "path": "examples/lpc55s69/src/bin/button_executor.rs",
    "content": "//! This example has been made with the LPCXpresso55S69 board in mind, which has a built-in LED on\n//! PIO1_6 and a button (labeled \"user\") on PIO1_9.\n\n#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_nxp::gpio::{Input, Level, Output, Pull};\nuse {defmt_rtt as _, panic_halt as _};\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) -> ! {\n    let p = embassy_nxp::init(Default::default());\n\n    let mut led = Output::new(p.PIO1_6, Level::Low);\n    let mut button = Input::new(p.PIO1_9, Pull::Up);\n\n    info!(\"Entered main loop\");\n    loop {\n        button.wait_for_rising_edge().await;\n        info!(\"Button pressed\");\n        led.toggle();\n    }\n}\n"
  },
  {
    "path": "examples/lpc55s69/src/bin/pwm.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_nxp::pwm::{Config, Pwm};\nuse embassy_time::Timer;\nuse {defmt_rtt as _, panic_halt as _};\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let p = embassy_nxp::init(Default::default());\n    let pwm = Pwm::new_output(p.SCT0_OUT1, p.PIO0_18, Config::new(1_000_000_000, 2_000_000_000));\n    loop {\n        info!(\"Counter: {}\", pwm.counter());\n        Timer::after_millis(50).await;\n    }\n}\n"
  },
  {
    "path": "examples/lpc55s69/src/bin/usart_async.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse core::str::from_utf8_mut;\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_nxp::bind_interrupts;\nuse embassy_nxp::gpio::{Level, Output};\nuse embassy_nxp::peripherals::USART2;\nuse embassy_nxp::usart::{Config, InterruptHandler, Usart};\nuse embassy_time::Timer;\nuse {defmt_rtt as _, panic_halt as _};\n\nbind_interrupts!(struct Irqs {\n        FLEXCOMM2 => InterruptHandler<USART2>;\n    }\n);\n\n#[embassy_executor::task]\nasync fn blinky_task(mut led: Output<'static>) {\n    loop {\n        info!(\"[TASK] led off!\");\n        led.set_high();\n        Timer::after_millis(500).await;\n\n        info!(\"[TASK] led on!\");\n        led.set_low();\n        Timer::after_millis(500).await;\n    }\n}\n\n#[embassy_executor::main]\nasync fn main(spawner: Spawner) {\n    let p = embassy_nxp::init(Default::default());\n    let mut usart = Usart::new(\n        p.USART2,\n        p.PIO0_27,\n        p.PIO1_24,\n        Irqs,\n        p.DMA0_CH11,\n        p.DMA0_CH10,\n        Config::default(),\n    );\n    let led = Output::new(p.PIO1_6, Level::Low);\n    spawner.spawn(blinky_task(led).unwrap());\n    info!(\"[MAIN] Entering main loop\");\n    loop {\n        let tx_buf = b\"Hello, Ferris!\";\n        let mut rx_buf = [0u8; 14];\n        info!(\"[MAIN] Write a message\");\n        usart.write(tx_buf).await.unwrap();\n        Timer::after_millis(500).await;\n\n        info!(\"[MAIN] Read a message\");\n        match usart.read(&mut rx_buf).await {\n            Ok(_) => match from_utf8_mut(&mut rx_buf) {\n                Ok(str) => {\n                    info!(\"[MAIN] The message is: {}\", str);\n                }\n                Err(_) => {\n                    error!(\"[MAIN] Error in converting to UTF8\");\n                }\n            },\n            Err(e) => warn!(\"[MAIN] Error: {}\", e),\n        }\n\n        Timer::after_millis(500).await;\n    }\n}\n"
  },
  {
    "path": "examples/lpc55s69/src/bin/usart_blocking.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse core::str::from_utf8_mut;\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_nxp::usart::{Config, Usart};\nuse embassy_time::Timer;\nuse {defmt_rtt as _, panic_halt as _};\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let p = embassy_nxp::init(Default::default());\n    let mut usart = Usart::new_blocking(p.USART2, p.PIO0_27, p.PIO1_24, Config::default());\n    let tx_buf = b\"Hello, Ferris!\";\n    let mut rx_buf = [0u8; 14];\n\n    loop {\n        info!(\"Write a message\");\n        usart.blocking_write(tx_buf).unwrap();\n        usart.blocking_flush().unwrap();\n\n        Timer::after_millis(500).await;\n\n        info!(\"Read a message\");\n        usart.blocking_read(&mut rx_buf).unwrap();\n\n        match from_utf8_mut(&mut rx_buf) {\n            Ok(str) => {\n                info!(\"The message is: {}\", str);\n            }\n            Err(_) => {\n                error!(\"Error in converting to UTF8\");\n            }\n        }\n\n        Timer::after_millis(500).await;\n    }\n}\n"
  },
  {
    "path": "examples/mcxa2xx/.cargo/config.toml",
    "content": "[target.thumbv8m.main-none-eabihf]\nrunner = 'probe-rs run --chip MCXA276 --preverify --verify --protocol swd --speed 12000'\n\nrustflags = [\n  \"-C\", \"linker=flip-link\",\n  \"-C\", \"link-arg=-Tlink.x\",\n  \"-C\", \"link-arg=-Tdefmt.x\",\n  # This is needed if your flash or ram addresses are not aligned to 0x10000 in memory.x\n  # See https://github.com/rust-embedded/cortex-m-quickstart/pull/95\n  \"-C\", \"link-arg=--nmagic\",\n]\n\n[build]\ntarget = \"thumbv8m.main-none-eabihf\" # Cortex-M33\n\n[env]\nDEFMT_LOG = \"trace\"\n"
  },
  {
    "path": "examples/mcxa2xx/.gitignore",
    "content": "target/\n"
  },
  {
    "path": "examples/mcxa2xx/Cargo.toml",
    "content": "[package]\nname = \"embassy-mcxa2xx-examples\"\nversion = \"0.1.0\"\nedition = \"2024\"\nlicense = \"MIT OR Apache-2.0\"\npublish = false\n\n[dependencies]\ncortex-m = { version = \"0.7\", features = [\"critical-section-single-core\"] }\ncortex-m-rt = { version = \"0.7\", features = [\"set-sp\", \"set-vtor\"] }\ncrc = \"3.4.0\"\ncritical-section = \"1.2.0\"\ndefmt = \"1.0\"\ndefmt-rtt = \"1.0\"\nembassy-embedded-hal = { version = \"0.6.0\", path = \"../../embassy-embedded-hal\" }\nembassy-executor = { version = \"0.10.0\", path = \"../../embassy-executor\", default-features = false }\nembassy-mcxa = { version = \"0.1.0\", path = \"../../embassy-mcxa\", features = [\"defmt\", \"unstable-pac\", \"mcxa2xx\"] }\nembassy-sync = { version = \"0.8.0\", path = \"../../embassy-sync\" }\nembassy-time = { version = \"0.5.1\", path = \"../../embassy-time\", features = [\"defmt\", \"defmt-timestamp-uptime\"] }\nembassy-time-driver = { version = \"0.2.2\", path = \"../../embassy-time-driver\", optional = true }\nembedded-io-async = \"0.7.0\"\nembedded-storage = \"0.3.1\"\nheapless = \"0.9.2\"\npanic-probe = { version = \"1.0\", features = [\"print-defmt\"] }\nrand_core = \"0.9\"\nstatic_cell = \"2.1.1\"\ntmp108 = \"0.4.0\"\n\n[features]\ndefault = [\"default-executor\"]\ndefault-executor = [\n  \"embassy-executor/platform-cortex-m\",\n  \"embassy-executor/executor-interrupt\",\n  \"embassy-executor/executor-thread\",\n]\nexecutor-platform = [\n  \"embassy-mcxa/executor-platform\"\n]\n\n[profile.release]\nlto = true # better optimizations\ndebug = 2  # enough information for defmt/rtt locations\n\n[package.metadata.embassy]\nbuild = [\n  { target = \"thumbv8m.main-none-eabihf\", artifact-dir = \"out/examples/mcxa2xx\" }\n]\n"
  },
  {
    "path": "examples/mcxa2xx/build.rs",
    "content": "use std::env;\nuse std::fs::File;\nuse std::io::Write;\nuse std::path::PathBuf;\n\nfn main() {\n    let out = &PathBuf::from(env::var_os(\"OUT_DIR\").unwrap());\n\n    // Generate memory.x - put \"FLASH\" at start of RAM, RAM after \"FLASH\"\n    // cortex-m-rt expects FLASH for code, RAM for data/bss/stack\n    // Both are in RAM, but separated to satisfy cortex-m-rt's expectations\n    // MCXA256 has 128KB RAM total\n    File::create(out.join(\"memory.x\"))\n        .unwrap()\n        .write_all(include_bytes!(\"memory.x\"))\n        .unwrap();\n\n    println!(\"cargo:rustc-link-search={}\", out.display());\n    println!(\"cargo:rerun-if-changed=memory.x\");\n}\n"
  },
  {
    "path": "examples/mcxa2xx/memory.x",
    "content": "MEMORY\n{\n    FLASH : ORIGIN = 0x00000000, LENGTH = 1M\n    RAM   : ORIGIN = 0x20000000, LENGTH = 128K\n}\n"
  },
  {
    "path": "examples/mcxa2xx/src/bin/adc-async.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse embassy_executor::Spawner;\nuse embassy_mcxa::adc::{Command, CommandConfig, CommandId, Trigger};\nuse embassy_mcxa::{bind_interrupts, peripherals};\nuse embassy_time::{Duration, Ticker};\nuse hal::adc::{self, Adc};\nuse hal::clocks::config::Div8;\nuse hal::config::Config;\nuse {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _};\n\nbind_interrupts!(struct Irqs {\n    ADC1 => adc::InterruptHandler<peripherals::ADC1>;\n});\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let mut config = Config::default();\n    config.clock_cfg.sirc.fro_lf_div = Div8::from_divisor(1);\n\n    let p = hal::init(config);\n\n    defmt::info!(\"=== ADC async Example ===\");\n\n    let commands = &[\n        Command::new_single(\n            p.P1_10,\n            CommandConfig {\n                chained_command: Some(CommandId::Cmd2), // Command 2 is executed after this command is done\n                ..Default::default()\n            },\n        ),\n        Command::new_looping(\n            p.P1_11,\n            3, // Command is run 3 times\n            CommandConfig {\n                chained_command: None, // Terminate the conversion after command is done\n                ..Default::default()\n            },\n        )\n        .unwrap(),\n    ];\n\n    let mut adc = Adc::new_async(\n        p.ADC1,\n        Irqs,\n        commands,\n        &[Trigger {\n            target_command_id: CommandId::Cmd1,\n            enable_hardware_trigger: false,\n            ..Default::default()\n        }],\n        adc::Config::default(),\n    )\n    .unwrap();\n\n    adc.do_offset_calibration();\n    adc.do_auto_calibration();\n\n    defmt::info!(\"=== ADC configuration done... ===\");\n    let mut tick = Ticker::every(Duration::from_millis(1000));\n\n    loop {\n        tick.next().await;\n        adc.do_software_trigger(0b0001).unwrap();\n\n        while let Some(res) = adc.wait_get_conversion().await {\n            defmt::info!(\"ADC result: {}\", res);\n        }\n    }\n}\n"
  },
  {
    "path": "examples/mcxa2xx/src/bin/adc-blocking.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse embassy_executor::Spawner;\nuse embassy_mcxa::adc::{Command, CommandConfig, CommandId, Trigger};\nuse embassy_time::{Duration, Ticker};\nuse hal::adc::{self, Adc};\nuse hal::clocks::config::Div8;\nuse hal::config::Config;\nuse {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _};\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let mut config = Config::default();\n    config.clock_cfg.sirc.fro_lf_div = Div8::from_divisor(1);\n\n    let p = hal::init(config);\n\n    defmt::info!(\"=== ADC polling Example ===\");\n\n    let commands = &[\n        Command::new_single(\n            p.P1_10,\n            CommandConfig {\n                chained_command: Some(CommandId::Cmd2), // Command 2 is executed after this command is done\n                ..Default::default()\n            },\n        ),\n        Command::new_looping(\n            p.P1_11,\n            3, // Command is run 3 times\n            CommandConfig {\n                chained_command: None, // Terminate the conversion after command is done\n                ..Default::default()\n            },\n        )\n        .unwrap(),\n    ];\n\n    let mut adc = Adc::new_blocking(\n        p.ADC1,\n        commands,\n        &[Trigger {\n            target_command_id: CommandId::Cmd1,\n            enable_hardware_trigger: false,\n            ..Default::default()\n        }],\n        adc::Config::default(),\n    )\n    .unwrap();\n\n    adc.do_offset_calibration();\n    adc.do_auto_calibration();\n\n    defmt::info!(\"=== ADC configuration done... ===\");\n    let mut tick = Ticker::every(Duration::from_millis(1000));\n\n    loop {\n        tick.next().await;\n        adc.do_software_trigger(0b0001).unwrap();\n\n        loop {\n            match adc.try_get_conversion() {\n                Ok(res) => {\n                    defmt::info!(\"ADC result: {}\", res);\n                }\n                Err(adc::Error::FifoPending) => {\n                    // Conversion not ready, continue polling\n                }\n                Err(_) => {\n                    // We're done\n                    break;\n                }\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "examples/mcxa2xx/src/bin/adc-temperature.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse embassy_executor::Spawner;\nuse embassy_mcxa::adc::{AnyAdcPin, Command, CommandConfig, CommandId, Trigger};\nuse embassy_mcxa::pac::adc::vals::{Avgs, Mode, Sts};\nuse embassy_mcxa::{bind_interrupts, peripherals};\nuse embassy_time::{Duration, Ticker};\nuse hal::adc::{self, Adc};\nuse hal::clocks::config::Div8;\nuse hal::config::Config;\nuse {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _};\n\nbind_interrupts!(struct Irqs {\n    ADC1 => adc::InterruptHandler<peripherals::ADC1>;\n});\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let mut config = Config::default();\n    config.clock_cfg.sirc.fro_lf_div = Div8::from_divisor(1);\n\n    let p = hal::init(config);\n\n    defmt::info!(\"=== ADC temperature Example ===\");\n\n    let commands = &[Command::new_looping(\n        AnyAdcPin::temperature(), // Use the temperature channel\n        2,\n        CommandConfig {\n            chained_command: None,\n            averaging: Avgs::AVERAGE_1024,  // Max average\n            sample_time: Sts::SAMPLE_131P5, // Max sample time\n            compare: adc::Compare::Disabled,\n            resolution: Mode::DATA_16_BITS,\n            wait_for_trigger: false,\n        },\n    )\n    .unwrap()];\n\n    let mut adc = Adc::new_async(\n        p.ADC1,\n        Irqs,\n        commands,\n        &[Trigger {\n            target_command_id: CommandId::Cmd1,\n            ..Default::default()\n        }],\n        adc::Config::default(),\n    )\n    .unwrap();\n\n    adc.do_offset_calibration();\n    adc.do_auto_calibration();\n\n    defmt::info!(\"=== ADC configuration done... ===\");\n    let mut tick = Ticker::every(Duration::from_millis(1000));\n\n    loop {\n        tick.next().await;\n        adc.do_software_trigger(0b0001).unwrap();\n        let conversion1 = adc.wait_get_conversion().await.unwrap();\n        let conversion2 = adc.wait_get_conversion().await.unwrap();\n\n        let celsius = calculate_temperature(conversion1.conv_value, conversion2.conv_value);\n        defmt::info!(\"Current temperature: {=f32}\", celsius);\n    }\n}\n\nfn calculate_temperature(conv1: u16, conv2: u16) -> f32 {\n    // Constants from the datasheet. May differ per device/family\n    const A: f32 = 738.0;\n    const B: f32 = 287.5;\n    const ALPHA: f32 = 10.06;\n\n    A * (ALPHA * (conv2 as f32 - conv1 as f32) / (conv2 as f32 + (ALPHA * (conv2 as f32 - conv1 as f32)))) - B\n}\n"
  },
  {
    "path": "examples/mcxa2xx/src/bin/blinky.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse embassy_executor::Spawner;\nuse embassy_time::Timer;\nuse hal::gpio::{DriveStrength, Level, Output, SlewRate};\nuse {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _};\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let p = hal::init(hal::config::Config::default());\n\n    defmt::info!(\"Blink example\");\n\n    let mut red = Output::new(p.P3_18, Level::High, DriveStrength::Normal, SlewRate::Fast);\n    let mut green = Output::new(p.P3_19, Level::High, DriveStrength::Normal, SlewRate::Fast);\n    let mut blue = Output::new(p.P3_21, Level::High, DriveStrength::Normal, SlewRate::Fast);\n\n    loop {\n        defmt::info!(\"Toggle LEDs\");\n\n        red.toggle();\n        Timer::after_millis(250).await;\n\n        red.toggle();\n        green.toggle();\n        Timer::after_millis(250).await;\n\n        green.toggle();\n        blue.toggle();\n        Timer::after_millis(250).await;\n        blue.toggle();\n\n        Timer::after_millis(250).await;\n    }\n}\n"
  },
  {
    "path": "examples/mcxa2xx/src/bin/button.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse embassy_executor::Spawner;\nuse embassy_time::Timer;\nuse hal::gpio::{Input, Pull};\nuse {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _};\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let p = hal::init(hal::config::Config::default());\n\n    defmt::info!(\"Button example\");\n\n    // This button is labeled \"WAKEUP\" on the FRDM-MCXA276\n    // The board already has a 10K pullup\n    let monitor = Input::new(p.P1_7, Pull::Disabled);\n\n    loop {\n        defmt::info!(\"Pin level is {:?}\", monitor.get_level());\n        Timer::after_millis(1000).await;\n    }\n}\n"
  },
  {
    "path": "examples/mcxa2xx/src/bin/button_async.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse embassy_executor::Spawner;\nuse embassy_mcxa::{bind_interrupts, gpio, peripherals};\nuse embassy_time::Timer;\nuse hal::gpio::{Input, Pull};\nuse {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _};\n\nbind_interrupts!(struct Irqs {\n    GPIO1 => gpio::InterruptHandler<peripherals::GPIO1>;\n});\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let p = hal::init(hal::config::Config::default());\n\n    defmt::info!(\"GPIO interrupt example\");\n\n    // This button is labeled \"WAKEUP\" on the FRDM-MCXA276\n    // The board already has a 10K pullup\n    let mut pin = Input::new_async(p.P1_7, Irqs, Pull::Disabled);\n\n    let mut press_count = 0u32;\n\n    loop {\n        pin.wait_for_falling_edge().await;\n\n        press_count += 1;\n\n        defmt::info!(\"Button pressed! Count: {}\", press_count);\n        Timer::after_millis(50).await;\n    }\n}\n"
  },
  {
    "path": "examples/mcxa2xx/src/bin/capture.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse embassy_executor::Spawner;\nuse embassy_time::Timer;\nuse hal::bind_interrupts;\nuse hal::clocks::config::Div8;\nuse hal::config::Config;\nuse hal::ctimer::CTimer;\nuse hal::ctimer::capture::{self, Capture, Edge, InterruptHandler};\nuse hal::gpio::{DriveStrength, Level, Output, SlewRate};\nuse hal::peripherals::CTIMER2;\nuse {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _};\n\nbind_interrupts!(struct Irqs {\n    CTIMER2 => InterruptHandler<CTIMER2>;\n});\n\n#[embassy_executor::main]\nasync fn main(spawner: Spawner) {\n    let mut config = Config::default();\n    config.clock_cfg.sirc.fro_lf_div = Div8::from_divisor(1);\n\n    let p = hal::init(config);\n\n    defmt::info!(\"Capture example\");\n\n    let pin = Output::new(p.P3_12, Level::Low, DriveStrength::Normal, SlewRate::Fast);\n\n    let ctimer = CTimer::new(p.CTIMER2, Default::default()).unwrap();\n    let mut config = capture::Config::default();\n    config.edge = Edge::RisingEdge;\n    let mut capture = Capture::new_with_input_pin(ctimer, p.CTIMER2_CH0, p.P3_14, Irqs, config).unwrap();\n\n    spawner.spawn(pin_task(pin).unwrap());\n\n    loop {\n        let one = capture.capture().await.unwrap();\n        let two = capture.capture().await.unwrap();\n        let diff = two - one;\n        defmt::info!(\n            \"{} s {} Hz\",\n            diff.to_period(capture.frequency()),\n            diff.to_frequency(capture.frequency())\n        );\n    }\n}\n\n#[embassy_executor::task]\nasync fn pin_task(mut pin: Output<'static>) {\n    Timer::after_secs(1).await;\n\n    loop {\n        pin.set_high();\n        pin.set_low();\n        Timer::after_secs(1).await;\n    }\n}\n"
  },
  {
    "path": "examples/mcxa2xx/src/bin/cdog.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse embassy_executor::Spawner;\nuse embassy_time::Timer;\nuse hal::bind_interrupts;\nuse hal::cdog::{FaultControl, InterruptHandler, LockControl, PauseControl, Watchdog};\nuse hal::config::Config;\nuse {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _};\n\nbind_interrupts!(\n    struct Irqs {\n        CDOG0 => InterruptHandler;\n    }\n);\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let config = Config::default();\n    let p = hal::init(config);\n\n    defmt::info!(\"** Code watchdog example **\");\n\n    let cdog_config = hal::cdog::Config {\n        timeout: FaultControl::EnableInterrupt,\n        miscompare: FaultControl::EnableInterrupt,\n        sequence: FaultControl::EnableInterrupt,\n        state: FaultControl::EnableInterrupt,\n        address: FaultControl::EnableInterrupt,\n        irq_pause: PauseControl::PauseTimer,\n        debug_halt: PauseControl::PauseTimer,\n        lock: LockControl::Unlocked,\n    };\n\n    let mut watchdog = Watchdog::new(p.CDOG0, Irqs, cdog_config).unwrap();\n\n    defmt::info!(\"Watchdog initialized\");\n\n    // First part of the example is to demonstrate how the secure counter feature of the cdog works.\n    watchdog.start(0xFFFFFF, 0);\n    watchdog.add(42);\n    watchdog.check(42);\n    watchdog.sub(2);\n    watchdog.check(40);\n    watchdog.start(0xFFFFFFFF, 0);\n    watchdog.check(0);\n    defmt::info!(\n        \"Next check should generate an interrupt as checked value (=1) is different than the secure counter (=0)\"\n    );\n    watchdog.check(1);\n\n    // Now demonstrating how the instruction timer feature of the cdog works.\n    defmt::info!(\"Start again the code watchdog to generate a timeout interrupt\");\n    watchdog.start(0xFFFFF, 0);\n    while watchdog.get_instruction_timer() != 0 {\n        defmt::info!(\"Instruction timer : {:08x}\", watchdog.get_instruction_timer());\n        Timer::after_millis(100).await;\n    }\n\n    defmt::info!(\"** End of example **\");\n}\n"
  },
  {
    "path": "examples/mcxa2xx/src/bin/clkout.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse embassy_executor::Spawner;\nuse embassy_mcxa::clkout::{ClockOut, ClockOutSel, Config, Div4};\nuse embassy_mcxa::clocks::PoweredClock;\nuse embassy_mcxa::clocks::config::{SoscConfig, SoscMode, SpllConfig, SpllMode, SpllSource};\nuse embassy_mcxa::gpio::{DriveStrength, Level, Output, SlewRate};\nuse embassy_time::Timer;\nuse {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _};\n\n/// Demonstrate CLKOUT, using Pin P4.2\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let mut cfg = hal::config::Config::default();\n    cfg.clock_cfg.sosc = Some(SoscConfig {\n        mode: SoscMode::CrystalOscillator,\n        frequency: 8_000_000,\n        power: PoweredClock::NormalEnabledDeepSleepDisabled,\n    });\n    cfg.clock_cfg.spll = Some(SpllConfig {\n        source: SpllSource::Sirc,\n        // 12MHz\n        // 12 x 32 => 384MHz\n        // 384 / (16 x 2) => 12.0MHz\n        mode: SpllMode::Mode1b {\n            m_mult: 32,\n            p_div: 16,\n            bypass_p2_div: false,\n        },\n        power: PoweredClock::NormalEnabledDeepSleepDisabled,\n        pll1_clk_div: None,\n    });\n\n    let p = hal::init(cfg);\n\n    let mut pin = p.P4_2;\n    let mut clkout = p.CLKOUT;\n\n    const K16_CONFIG: Config = Config {\n        sel: ClockOutSel::Clk16K,\n        div: Div4::no_div(),\n        level: PoweredClock::NormalEnabledDeepSleepDisabled,\n    };\n    const M4_CONFIG: Config = Config {\n        sel: ClockOutSel::Fro12M,\n        div: const { Div4::from_divisor(3).unwrap() },\n        level: PoweredClock::NormalEnabledDeepSleepDisabled,\n    };\n    const K512_CONFIG: Config = Config {\n        sel: ClockOutSel::ClkIn,\n        div: const { Div4::from_divisor(16).unwrap() },\n        level: PoweredClock::NormalEnabledDeepSleepDisabled,\n    };\n    const M1_CONFIG: Config = Config {\n        sel: ClockOutSel::Pll1Clk,\n        div: const { Div4::from_divisor(12).unwrap() },\n        level: PoweredClock::NormalEnabledDeepSleepDisabled,\n    };\n\n    #[rustfmt::skip]\n    let configs = [\n        (\"16K -> /1 = 16K\",  K16_CONFIG),\n        (\"12M -> /3 = 4M\",   M4_CONFIG),\n        (\"8M -> /16 = 512K\", K512_CONFIG),\n        (\"12M-> /12 = 1M\",   M1_CONFIG),\n    ];\n\n    loop {\n        defmt::info!(\"Set High...\");\n        let mut output = Output::new(pin.reborrow(), Level::High, DriveStrength::Normal, SlewRate::Slow);\n        Timer::after_millis(250).await;\n\n        defmt::info!(\"Set Low...\");\n        output.set_low();\n        Timer::after_millis(750).await;\n\n        drop(output);\n\n        for (name, conf) in configs.iter() {\n            defmt::info!(\"Running {=str}\", name);\n\n            let _clock_out = ClockOut::new(clkout.reborrow(), pin.reborrow(), *conf).unwrap();\n\n            Timer::after_millis(3000).await;\n\n            defmt::info!(\"Set Low...\");\n            drop(_clock_out);\n\n            let _output = Output::new(pin.reborrow(), Level::Low, DriveStrength::Normal, SlewRate::Slow);\n            Timer::after_millis(500).await;\n        }\n    }\n}\n"
  },
  {
    "path": "examples/mcxa2xx/src/bin/cpu-clocks.rs",
    "content": "//! Example exercising advanced clock control, including changing the main CPU clocks\n\n#![no_std]\n#![no_main]\n\nuse embassy_executor::Spawner;\nuse embassy_mcxa::clkout::{ClockOut, ClockOutSel, Div4};\nuse embassy_mcxa::clocks::PoweredClock;\nuse embassy_mcxa::clocks::config::{Div8, MainClockSource, SoscConfig, SoscMode, SpllConfig, SpllMode, SpllSource};\nuse embassy_time::Timer;\nuse hal::gpio::{DriveStrength, Level, Output, SlewRate};\nuse {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _};\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let mut cfg = hal::config::Config::default();\n\n    // FIRC completely disabled\n    cfg.clock_cfg.firc = None;\n\n    // SIRC output gated\n    cfg.clock_cfg.sirc.fro_12m_enabled = false;\n    cfg.clock_cfg.sirc.fro_lf_div = None;\n\n    // SOSC enabled\n    cfg.clock_cfg.sosc = Some(SoscConfig {\n        mode: SoscMode::CrystalOscillator,\n        frequency: 8_000_000,\n        power: PoweredClock::NormalEnabledDeepSleepDisabled,\n    });\n\n    // SPLL enabled\n    cfg.clock_cfg.spll = Some(SpllConfig {\n        source: SpllSource::Sosc,\n        // 8MHz\n        // 8 x 48 => 384MHz\n        // 384 / (16 x 2) => 12.0MHz\n        mode: SpllMode::Mode1b {\n            m_mult: 48,\n            p_div: 16,\n            bypass_p2_div: false,\n        },\n        power: PoweredClock::NormalEnabledDeepSleepDisabled,\n        pll1_clk_div: None,\n    });\n\n    cfg.clock_cfg.main_clock.source = MainClockSource::SPll1;\n    cfg.clock_cfg.main_clock.ahb_clk_div = const { Div8::from_divisor(256).unwrap() };\n    let p = hal::init(cfg);\n\n    let clkout_cfg = hal::clkout::Config {\n        sel: ClockOutSel::SlowClk, // Main system clock, /6\n        div: Div4::no_div(),\n        level: PoweredClock::NormalEnabledDeepSleepDisabled,\n    };\n\n    // SPLL                => 12.000 MHz\n    // AHB_CLK_DIV is /256 => 46.875 kHz\n    // Slow clock is /6    =>  7.812 kHz\n    let pin = p.P4_2;\n    let clkout = p.CLKOUT;\n    let _clock_out = ClockOut::new(clkout, pin, clkout_cfg).unwrap();\n\n    defmt::info!(\"Blink example\");\n\n    let mut red = Output::new(p.P3_18, Level::High, DriveStrength::Normal, SlewRate::Fast);\n    let mut green = Output::new(p.P3_19, Level::High, DriveStrength::Normal, SlewRate::Fast);\n    let mut blue = Output::new(p.P3_21, Level::High, DriveStrength::Normal, SlewRate::Fast);\n\n    loop {\n        defmt::info!(\"Toggle LEDs\");\n\n        red.toggle();\n        Timer::after_millis(250).await;\n\n        red.toggle();\n        green.toggle();\n        Timer::after_millis(250).await;\n\n        green.toggle();\n        blue.toggle();\n        Timer::after_millis(250).await;\n        blue.toggle();\n\n        Timer::after_millis(250).await;\n    }\n}\n"
  },
  {
    "path": "examples/mcxa2xx/src/bin/crc.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse embassy_executor::Spawner;\nuse hal::config::Config;\nuse hal::crc::Crc;\nuse {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _};\n\nconst CCITT_FALSE: crc::Algorithm<u16> = crc::Algorithm {\n    width: 16,\n    poly: 0x1021,\n    init: 0xffff,\n    refin: false,\n    refout: false,\n    xorout: 0,\n    check: 0x29b1,\n    residue: 0x0000,\n};\n\nconst POSIX: crc::Algorithm<u32> = crc::Algorithm {\n    width: 32,\n    poly: 0x04c1_1db7,\n    init: 0,\n    refin: false,\n    refout: false,\n    xorout: 0xffff_ffff,\n    check: 0x765e_7680,\n    residue: 0x0000,\n};\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let config = Config::default();\n    let mut p = hal::init(config);\n\n    defmt::info!(\"CRC example\");\n\n    let buf_u8 = [0x00u8, 0x11, 0x22, 0x33];\n    let buf_u16 = [0x0000u16, 0x1111, 0x2222, 0x3333];\n    let buf_u32 = [0x0000_0000u32, 0x1111_1111, 0x2222_2222, 0x3333_3333];\n\n    // CCITT False\n\n    let sw_crc = crc::Crc::<u16>::new(&CCITT_FALSE);\n    let mut digest = sw_crc.digest();\n    digest.update(&buf_u8);\n    let sw_sum = digest.finalize();\n\n    let mut crc = Crc::new_ccitt_false(p.CRC0.reborrow());\n    crc.feed(&buf_u8);\n    let sum = crc.finalize();\n    assert_eq!(sum, sw_sum);\n\n    let mut crc = Crc::new_ccitt_false(p.CRC0.reborrow());\n    crc.feed(&buf_u16);\n    let sum = crc.finalize();\n    assert_eq!(sum, 0xa467);\n\n    let mut crc = Crc::new_ccitt_false(p.CRC0.reborrow());\n    crc.feed(&buf_u32);\n    let sum = crc.finalize();\n    assert_eq!(sum, 0xe5c7);\n\n    // Maxim\n\n    let sw_crc = crc::Crc::<u16>::new(&crc::CRC_16_MAXIM_DOW);\n    let mut digest = sw_crc.digest();\n    digest.update(&buf_u8);\n    let sw_sum = digest.finalize();\n\n    let mut crc = Crc::new_maxim(p.CRC0.reborrow());\n    crc.feed(&buf_u8);\n    let sum = crc.finalize();\n    assert_eq!(sum, sw_sum);\n\n    let mut crc = Crc::new_maxim(p.CRC0.reborrow());\n    crc.feed(&buf_u16);\n    let sum = crc.finalize();\n    assert_eq!(sum, 0x2afe);\n\n    let mut crc = Crc::new_maxim(p.CRC0.reborrow());\n    crc.feed(&buf_u32);\n    let sum = crc.finalize();\n    assert_eq!(sum, 0x17d7);\n\n    // Kermit\n\n    let sw_crc = crc::Crc::<u16>::new(&crc::CRC_16_KERMIT);\n    let mut digest = sw_crc.digest();\n    digest.update(&buf_u8);\n    let sw_sum = digest.finalize();\n\n    let mut crc = Crc::new_kermit(p.CRC0.reborrow());\n    crc.feed(&buf_u8);\n    let sum = crc.finalize();\n    assert_eq!(sum, sw_sum);\n\n    let mut crc = Crc::new_kermit(p.CRC0.reborrow());\n    crc.feed(&buf_u16);\n    let sum = crc.finalize();\n    assert_eq!(sum, 0x66eb);\n\n    let mut crc = Crc::new_kermit(p.CRC0.reborrow());\n    crc.feed(&buf_u32);\n    let sum = crc.finalize();\n    assert_eq!(sum, 0x75ea);\n\n    // ISO HDLC\n\n    let sw_crc = crc::Crc::<u32>::new(&crc::CRC_32_ISO_HDLC);\n    let mut digest = sw_crc.digest();\n    digest.update(&buf_u8);\n    let sw_sum = digest.finalize();\n\n    let mut crc = Crc::new_iso_hdlc(p.CRC0.reborrow());\n    crc.feed(&buf_u8);\n    let sum = crc.finalize();\n    assert_eq!(sum, sw_sum);\n\n    let mut crc = Crc::new_iso_hdlc(p.CRC0.reborrow());\n    crc.feed(&buf_u16);\n    let sum = crc.finalize();\n    assert_eq!(sum, 0x8a61_4178);\n\n    let mut crc = Crc::new_iso_hdlc(p.CRC0.reborrow());\n    crc.feed(&buf_u32);\n    let sum = crc.finalize();\n    assert_eq!(sum, 0xfab5_d04e);\n\n    // POSIX\n\n    let sw_crc = crc::Crc::<u32>::new(&POSIX);\n    let mut digest = sw_crc.digest();\n    digest.update(&buf_u8);\n    let sw_sum = digest.finalize();\n\n    let mut crc = Crc::new_posix(p.CRC0.reborrow());\n    crc.feed(&buf_u8);\n    let sum = crc.finalize();\n\n    assert_eq!(sum, sw_sum);\n\n    let mut crc = Crc::new_posix(p.CRC0.reborrow());\n    crc.feed(&buf_u16);\n    let sum = crc.finalize();\n    assert_eq!(sum, 0x6d76_4f58);\n\n    let mut crc = Crc::new_posix(p.CRC0.reborrow());\n    crc.feed(&buf_u32);\n    let sum = crc.finalize();\n    assert_eq!(sum, 0x2a5b_cb90);\n\n    defmt::info!(\"CRC successful\");\n}\n"
  },
  {
    "path": "examples/mcxa2xx/src/bin/dma_mem_to_mem.rs",
    "content": "//! DMA memory-to-memory transfer example for MCXA276.\n//!\n//! This example demonstrates using DMA to copy data between memory buffers\n//! using the Embassy-style async API with type-safe transfers.\n//!\n//! # Embassy-style features demonstrated:\n//! - `TransferOptions` for configuration\n//! - Type-safe `mem_to_mem<u32>()` method with async `.await`\n//! - `Transfer` Future that can be `.await`ed\n//! - `Word` trait for automatic transfer width detection\n//! - `memset()` method for filling memory with a pattern\n\n#![no_std]\n#![no_main]\n\nuse embassy_executor::Spawner;\nuse embassy_mcxa::clocks::config::Div8;\nuse embassy_mcxa::dma::{DmaChannel, TransferOptions};\nuse static_cell::ConstStaticCell;\nuse {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _};\n\nconst BUFFER_LENGTH: usize = 4;\n\n// Buffers in RAM (static mut is automatically placed in .bss/.data)\nstatic SRC_BUFFER: ConstStaticCell<[u32; BUFFER_LENGTH]> = ConstStaticCell::new([1, 2, 3, 4]);\nstatic DEST_BUFFER: ConstStaticCell<[u32; BUFFER_LENGTH]> = ConstStaticCell::new([0; BUFFER_LENGTH]);\nstatic MEMSET_BUFFER: ConstStaticCell<[u32; BUFFER_LENGTH]> = ConstStaticCell::new([0; BUFFER_LENGTH]);\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    // Small delay to allow probe-rs to attach after reset\n    for _ in 0..100_000 {\n        cortex_m::asm::nop();\n    }\n\n    let mut cfg = hal::config::Config::default();\n    cfg.clock_cfg.sirc.fro_12m_enabled = true;\n    cfg.clock_cfg.sirc.fro_lf_div = Some(Div8::no_div());\n    let p = hal::init(cfg);\n\n    defmt::info!(\"DMA memory-to-memory example starting...\");\n\n    defmt::info!(\"EDMA memory to memory example begin.\");\n\n    let src = SRC_BUFFER.take();\n    let dst = DEST_BUFFER.take();\n    let mst = MEMSET_BUFFER.take();\n\n    defmt::info!(\"Source Buffer: {=[?]}\", src.as_slice());\n    defmt::info!(\"Destination Buffer (before): {=[?]}\", dst.as_slice());\n    defmt::info!(\"Configuring DMA with Embassy-style API...\");\n\n    // Create DMA channel\n    let mut dma_ch0 = DmaChannel::new(p.DMA_CH0);\n\n    // Configure transfer options\n    let options = TransferOptions::COMPLETE_INTERRUPT;\n\n    // =========================================================================\n    // Part 1: Embassy-style async API demonstration (mem_to_mem)\n    // =========================================================================\n    //\n    // Use the new type-safe `mem_to_mem<u32>()` method:\n    // - Automatically determines transfer width from buffer element type (u32)\n    // - Returns a `Transfer` future that can be `.await`ed\n    // - Uses TransferOptions for consistent configuration\n    //\n    // Using async `.await` - the executor can run other tasks while waiting!\n\n    // Perform type-safe memory-to-memory transfer using Embassy-style async API\n    // Using async `.await` - the executor can run other tasks while waiting!\n    let transfer = dma_ch0.mem_to_mem(src, dst, options).unwrap();\n    transfer.await.unwrap();\n\n    defmt::info!(\"DMA mem-to-mem transfer complete!\");\n    defmt::info!(\"Destination Buffer (after): {=[?]}\", dst.as_slice());\n\n    // Verify data\n    if src != dst {\n        defmt::error!(\"FAIL: mem_to_mem mismatch!\");\n    } else {\n        defmt::info!(\"PASS: mem_to_mem verified.\");\n    }\n\n    // =========================================================================\n    // Part 2: memset() demonstration\n    // =========================================================================\n    //\n    // The `memset()` method fills a buffer with a pattern value:\n    // - Fixed source address (pattern is read repeatedly)\n    // - Incrementing destination address\n    // - Uses the same Transfer future pattern\n\n    defmt::info!(\"--- Demonstrating memset() feature ---\");\n\n    defmt::info!(\"Memset Buffer (before): {=[?]}\", mst.as_slice());\n\n    // Fill buffer with a pattern value using DMA memset\n    let pattern: u32 = 0xDEADBEEF;\n    defmt::info!(\"Filling with pattern 0xDEADBEEF...\");\n\n    // Using blocking_wait() for demonstration - also shows non-async usage\n    let transfer = dma_ch0.memset(&pattern, mst, options).unwrap();\n    transfer.blocking_wait();\n\n    defmt::info!(\"DMA memset complete!\");\n    defmt::info!(\"Memset Buffer (after): {=[?]}\", mst.as_slice());\n\n    // Verify memset result\n    if !mst.iter().all(|&v| v == pattern) {\n        defmt::error!(\"FAIL: memset mismatch!\");\n    } else {\n        defmt::info!(\"PASS: memset verified.\");\n    }\n\n    defmt::info!(\"=== All DMA tests complete ===\");\n}\n"
  },
  {
    "path": "examples/mcxa2xx/src/bin/dma_scatter_gather_builder.rs",
    "content": "//! DMA Scatter-Gather Builder example for MCXA276.\n//!\n//! This example demonstrates using the new `ScatterGatherBuilder` API for\n//! chaining multiple DMA transfers with a type-safe builder pattern.\n//!\n//! # Features demonstrated:\n//! - `ScatterGatherBuilder::new()` for creating a builder\n//! - `add_transfer()` for adding memory-to-memory segments\n//! - `build()` to start the chained transfer\n//! - Automatic TCD linking and ESG bit management\n//!\n//! # Comparison with manual scatter-gather:\n//! The manual approach (see `dma_scatter_gather.rs`) requires:\n//! - Manual TCD pool allocation and alignment\n//! - Manual CSR/ESG/INTMAJOR bit manipulation\n//! - Manual dlast_sga address calculations\n//!\n//! The builder approach handles all of this automatically!\n\n#![no_std]\n#![no_main]\n\nuse embassy_executor::Spawner;\nuse embassy_mcxa::clocks::config::Div8;\nuse embassy_mcxa::dma::{DmaChannel, ScatterGatherBuilder};\nuse static_cell::ConstStaticCell;\nuse {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _};\n\n// Source buffers (multiple segments)\nstatic SRC1: ConstStaticCell<[u32; 4]> = ConstStaticCell::new([0x11111111, 0x22222222, 0x33333333, 0x44444444]);\nstatic SRC2: ConstStaticCell<[u32; 4]> = ConstStaticCell::new([0xAAAAAAAA, 0xBBBBBBBB, 0xCCCCCCCC, 0xDDDDDDDD]);\nstatic SRC3: ConstStaticCell<[u32; 4]> = ConstStaticCell::new([0x12345678, 0x9ABCDEF0, 0xFEDCBA98, 0x76543210]);\n\n// Destination buffers (one per segment)\nstatic DST1: ConstStaticCell<[u32; 4]> = ConstStaticCell::new([0; 4]);\nstatic DST2: ConstStaticCell<[u32; 4]> = ConstStaticCell::new([0; 4]);\nstatic DST3: ConstStaticCell<[u32; 4]> = ConstStaticCell::new([0; 4]);\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    // Small delay to allow probe-rs to attach after reset\n    for _ in 0..100_000 {\n        cortex_m::asm::nop();\n    }\n\n    let mut cfg = hal::config::Config::default();\n    cfg.clock_cfg.sirc.fro_12m_enabled = true;\n    cfg.clock_cfg.sirc.fro_lf_div = Some(Div8::no_div());\n    let p = hal::init(cfg);\n\n    defmt::info!(\"DMA Scatter-Gather Builder example starting...\");\n\n    defmt::info!(\"DMA Scatter-Gather Builder Example\");\n    defmt::info!(\"===================================\");\n    let src1 = SRC1.take();\n    let src2 = SRC2.take();\n    let src3 = SRC3.take();\n    let dst1 = DST1.take();\n    let dst2 = DST2.take();\n    let dst3 = DST3.take();\n\n    // Show source buffers\n    defmt::info!(\"Source buffers:\");\n    defmt::info!(\"  SRC1: {=[?]}\", src1.as_slice());\n    defmt::info!(\"  SRC2: {=[?]}\", src2.as_slice());\n    defmt::info!(\"  SRC3: {=[?]}\", src3.as_slice());\n\n    defmt::info!(\"Destination buffers (before):\");\n    defmt::info!(\"  DST1: {=[?]}\", dst1.as_slice());\n    defmt::info!(\"  DST2: {=[?]}\", dst2.as_slice());\n    defmt::info!(\"  DST3: {=[?]}\", dst3.as_slice());\n\n    // Create DMA channel\n    let dma_ch0 = DmaChannel::new(p.DMA_CH0);\n\n    defmt::info!(\"Building scatter-gather chain with builder API...\");\n\n    // =========================================================================\n    // ScatterGatherBuilder API demonstration\n    // =========================================================================\n    //\n    // The builder pattern makes scatter-gather transfers much easier:\n    // 1. Create a builder\n    // 2. Add transfer segments with add_transfer()\n    // 3. Call build() to start the entire chain\n    // No manual TCD manipulation required!\n\n    let mut builder = ScatterGatherBuilder::<u32>::new();\n\n    // Add three transfer segments - the builder handles TCD linking automatically\n    builder.add_transfer(src1, dst1);\n    builder.add_transfer(src2, dst2);\n    builder.add_transfer(src3, dst3);\n\n    defmt::info!(\"Added 3 transfer segments to chain.\");\n    defmt::info!(\"Starting scatter-gather transfer with .await...\");\n\n    // Build and execute the scatter-gather chain\n    // The build() method:\n    // - Links all TCDs together with ESG bit\n    // - Sets INTMAJOR on all TCDs\n    // - Loads the first TCD into hardware\n    // - Returns a Transfer future\n    let transfer = builder.build(dma_ch0).expect(\"Failed to build scatter-gather\");\n    transfer.blocking_wait();\n\n    defmt::info!(\"Scatter-gather transfer complete!\");\n\n    // Show results\n    defmt::info!(\"Destination buffers (after):\");\n    defmt::info!(\"  DST1: {=[?]}\", dst1.as_slice());\n    defmt::info!(\"  DST2: {=[?]}\", dst2.as_slice());\n    defmt::info!(\"  DST3: {=[?]}\", dst3.as_slice());\n\n    let comps = [(src1, dst1), (src2, dst2), (src3, dst3)];\n\n    // Verify all three segments\n    let mut all_ok = true;\n    for (src, dst) in comps {\n        all_ok &= src == dst;\n    }\n\n    if all_ok {\n        defmt::info!(\"PASS: All segments verified!\");\n    } else {\n        defmt::error!(\"FAIL: Mismatch detected!\");\n    }\n\n    defmt::info!(\"=== Scatter-Gather Builder example complete ===\");\n}\n"
  },
  {
    "path": "examples/mcxa2xx/src/bin/flash_iap.rs",
    "content": "//! Flash IAP (In-Application Programming) example for MCXA276.\n//!\n//! Demonstrates the ROM API flash driver: init, erase, program, verify, and read.\n//!\n//! This example operates on sector `0x0C0000` (`SECTOR_INDEX_FROM_END = 32`),\n//! matching the C SDK `frdmmcxa276_romapi_flashiap` demo. The top-of-flash\n//! sectors (0x0FC000+) are avoided because they overlap the protected\n//! FFR / CFPA / CFPB configuration region on MCXA276.\n\n#![no_std]\n#![no_main]\n\nuse embassy_executor::Spawner;\nuse embedded_storage::nor_flash::{NorFlash, ReadNorFlash};\nuse hal::config::Config;\nuse hal::flash::{Flash, FlashProperty};\nuse {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _};\n\n/// Number of sectors from the end of flash to use as the test target.\n///\n/// The C SDK header defaults to 2, but on MCXA276 the top-of-flash region\n/// (0x0FC000+) is the protected FFR / CFPA / CFPB configuration area.\n/// Accessing it causes a fault. The C SDK demo for MCXA276 therefore uses\n/// 32, which targets sector 0x0C0000 — well below the protected zone.\nconst SECTOR_INDEX_FROM_END: u32 = 32;\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let _p = hal::init(Config::default());\n\n    defmt::info!(\"Flash driver API tree demo application.\");\n\n    // -----------------------------------------------------------------------\n    // 1. Initialise the flash driver via ROM API\n    // -----------------------------------------------------------------------\n    defmt::info!(\"\\n Initializing flash driver.\");\n    let mut flash = defmt::unwrap!(Flash::new());\n    defmt::info!(\"\\n Flash init successfull!\\n\");\n\n    defmt::info!(\"\\n Config flash memory access time.\\n\");\n\n    let pflash_block_base = defmt::unwrap!(flash.get_property(FlashProperty::PflashBlockBaseAddr));\n    let pflash_sector_size = defmt::unwrap!(flash.get_property(FlashProperty::PflashSectorSize));\n    let pflash_total_size = defmt::unwrap!(flash.get_property(FlashProperty::PflashTotalSize));\n    let pflash_page_size = defmt::unwrap!(flash.get_property(FlashProperty::PflashPageSize));\n\n    // -----------------------------------------------------------------------\n    // 2. Compute the test address (second-to-last sector)\n    // -----------------------------------------------------------------------\n    defmt::info!(\"\\n PFlash Information:\");\n    defmt::info!(\"\\n kFLASH_PropertyPflashBlockBaseAddr = 0x{:X}\", pflash_block_base);\n    defmt::info!(\"\\n kFLASH_PropertyPflashSectorSize = {}\", pflash_sector_size);\n    defmt::info!(\"\\n kFLASH_PropertyPflashTotalSize = {}\", pflash_total_size);\n    defmt::info!(\"\\n kFLASH_PropertyPflashPageSize = 0x{:X}\", pflash_page_size);\n\n    let dest_addr: u32 = pflash_block_base + (pflash_total_size - (SECTOR_INDEX_FROM_END * pflash_sector_size));\n\n    // -----------------------------------------------------------------------\n    // 4. Erase the sector\n    // -----------------------------------------------------------------------\n    defmt::info!(\"\\n Erase a sector of flash\");\n    defmt::unwrap!(flash.blocking_erase(dest_addr, pflash_sector_size));\n\n    defmt::info!(\"\\n Calling flash_verify_erase_sector() API.\");\n    defmt::unwrap!(flash.verify_erase_sector(dest_addr, pflash_sector_size));\n    defmt::info!(\n        \"\\n Successfully erased sector: 0x{:x} -> 0x{:x}\\n\",\n        dest_addr,\n        dest_addr + pflash_sector_size\n    );\n\n    // Prepare user buffer (512 bytes = 128 u32s), matching the C example.\n    let mut write_buf = [0u8; 512];\n    for (i, chunk) in write_buf.chunks_exact_mut(4).enumerate() {\n        let val = (i as u32).to_le_bytes();\n        chunk.copy_from_slice(&val);\n    }\n\n    defmt::info!(\"\\n Calling FLASH_Program() API.\");\n    defmt::unwrap!(flash.blocking_program(dest_addr, &write_buf));\n\n    defmt::info!(\"\\n Calling FLASH_VerifyProgram() API.\");\n    defmt::unwrap!(flash.verify_program(dest_addr, &write_buf));\n\n    let mut read_buf = [0u8; 512];\n    let read_offset = dest_addr - pflash_block_base;\n    defmt::unwrap!(flash.blocking_read(read_offset, &mut read_buf));\n    defmt::assert_eq!(read_buf, write_buf);\n\n    defmt::info!(\n        \"\\n Successfully programmed and verified location: 0x{:x} -> 0x{:x} \\n\",\n        dest_addr,\n        dest_addr + (write_buf.len() as u32)\n    );\n\n    defmt::unwrap!(flash.blocking_erase(dest_addr, pflash_sector_size));\n\n    // -----------------------------------------------------------------------\n    // 5. NorFlash trait\n    // -----------------------------------------------------------------------\n\n    defmt::info!(\"\\n Pflash using NorFlash trait\");\n    let dest_offset: u32 = pflash_total_size - ((SECTOR_INDEX_FROM_END + 1) * pflash_sector_size);\n    let dest_addr: u32 = pflash_block_base + dest_offset;\n\n    defmt::info!(\"\\n Erase another sector of flash using NorFlash trait\",);\n    defmt::unwrap!(flash.erase(dest_offset, dest_offset + pflash_sector_size));\n\n    defmt::info!(\"\\n Calling flash_verify_erase_sector() API for the second sector.\");\n    defmt::unwrap!(flash.verify_erase_sector(dest_addr, pflash_sector_size));\n    defmt::info!(\n        \"\\n Successfully erased sector: 0x{:x} -> 0x{:x}\\n\",\n        dest_addr,\n        dest_addr + pflash_sector_size\n    );\n\n    defmt::info!(\"\\n Calling NorFlash::write().\");\n    defmt::unwrap!(flash.write(dest_offset, &write_buf));\n\n    defmt::info!(\"\\n Calling FLASH_VerifyProgram() API.\");\n    defmt::unwrap!(flash.verify_program(dest_addr, &write_buf));\n\n    let mut read_buf = [0u8; 512];\n    defmt::unwrap!(flash.read(dest_offset, &mut read_buf));\n    defmt::assert_eq!(read_buf, write_buf);\n\n    defmt::info!(\n        \"\\n Successfully programmed and verified location: 0x{:x} -> 0x{:x} using NorFlash trait \",\n        dest_addr,\n        dest_addr + (write_buf.len() as u32)\n    );\n\n    // -----------------------------------------------------------------------\n    // 5. 16 byte write\n    // -----------------------------------------------------------------------\n\n    let dest_offset: u32 = pflash_total_size - ((SECTOR_INDEX_FROM_END + 1) * pflash_sector_size) + 512;\n    defmt::info!(\"\\n Single 16 byte write using NorFlash trait: {:x}\", dest_offset);\n    let dest_addr: u32 = pflash_block_base + dest_offset;\n\n    let mut write_buf = [0xFFu8; 32];\n    for i in 0..16 {\n        write_buf[i] = i as u8;\n    }\n\n    defmt::unwrap!(flash.write(dest_offset, &write_buf[..16]));\n\n    let mut read_buf = [0u8; 32];\n    defmt::unwrap!(flash.read(dest_offset, &mut read_buf));\n    defmt::assert_eq!(read_buf, write_buf);\n\n    defmt::info!(\n        \"\\n Successfully programmed and verified location: 0x{:x} -> 0x{:x} using NorFlash trait \",\n        dest_addr,\n        dest_addr + (write_buf.len() as u32)\n    );\n\n    defmt::unwrap!(flash.blocking_erase(\n        pflash_total_size - ((SECTOR_INDEX_FROM_END + 1) * pflash_sector_size),\n        pflash_sector_size\n    ));\n\n    defmt::info!(\"\\n End of PFlash Example! \\n\");\n\n    loop {\n        cortex_m::asm::wfi();\n    }\n}\n"
  },
  {
    "path": "examples/mcxa2xx/src/bin/hello.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse embassy_executor::Spawner;\nuse embassy_mcxa::clocks::config::Div8;\nuse hal::lpuart::{Blocking, Config, Lpuart};\nuse {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _};\n\n/// Simple helper to write a byte as hex to UART\nfn write_hex_byte(uart: &mut Lpuart<'_, Blocking>, byte: u8) {\n    const HEX_DIGITS: &[u8] = b\"0123456789ABCDEF\";\n    let _ = uart.write_byte(HEX_DIGITS[(byte >> 4) as usize]);\n    let _ = uart.write_byte(HEX_DIGITS[(byte & 0xF) as usize]);\n}\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let mut cfg = hal::config::Config::default();\n    cfg.clock_cfg.sirc.fro_12m_enabled = true;\n    cfg.clock_cfg.sirc.fro_lf_div = Some(Div8::no_div());\n    let p = hal::init(cfg);\n\n    defmt::info!(\"boot\");\n\n    // Create UART configuration\n    let config = Config {\n        baudrate_bps: 115_200,\n        ..Default::default()\n    };\n\n    // Create UART instance using LPUART2 with P2_2 as TX and P2_3 as RX\n    let mut uart = Lpuart::new_blocking(\n        p.LPUART2, // Peripheral\n        p.P2_2,    // TX pin\n        p.P2_3,    // RX pin\n        config,\n    )\n    .unwrap();\n\n    // Print welcome message before any async delays to guarantee early console output\n    uart.write_str_blocking(\"\\r\\n=== MCXA276 UART Echo Demo ===\\r\\n\");\n    uart.write_str_blocking(\"Available commands:\\r\\n\");\n    uart.write_str_blocking(\"  help     - Show this help\\r\\n\");\n    uart.write_str_blocking(\"  echo <text> - Echo back the text\\r\\n\");\n    uart.write_str_blocking(\"  hex <byte> - Display byte in hex (0-255)\\r\\n\");\n    uart.write_str_blocking(\"Type a command: \");\n\n    let mut buffer = [0u8; 64];\n    let mut buf_idx = 0;\n\n    loop {\n        // Read a byte from UART\n        let byte = uart.read_byte_blocking();\n\n        // Echo the character back\n        if byte == b'\\r' || byte == b'\\n' {\n            // Enter pressed - process command\n            uart.write_str_blocking(\"\\r\\n\");\n\n            if buf_idx > 0 {\n                let command = &buffer[0..buf_idx];\n\n                if command == b\"help\" {\n                    uart.write_str_blocking(\"Available commands:\\r\\n\");\n                    uart.write_str_blocking(\"  help     - Show this help\\r\\n\");\n                    uart.write_str_blocking(\"  echo <text> - Echo back the text\\r\\n\");\n                    uart.write_str_blocking(\"  hex <byte> - Display byte in hex (0-255)\\r\\n\");\n                } else if command.starts_with(b\"echo \") && command.len() > 5 {\n                    uart.write_str_blocking(\"Echo: \");\n                    uart.write_str_blocking(core::str::from_utf8(&command[5..]).unwrap_or(\"\"));\n                    uart.write_str_blocking(\"\\r\\n\");\n                } else if command.starts_with(b\"hex \") && command.len() > 4 {\n                    // Parse the byte value\n                    let num_str = &command[4..];\n                    if let Ok(num) = parse_u8(num_str) {\n                        uart.write_str_blocking(\"Hex: 0x\");\n                        write_hex_byte(&mut uart, num);\n                        uart.write_str_blocking(\"\\r\\n\");\n                    } else {\n                        uart.write_str_blocking(\"Invalid number for hex command\\r\\n\");\n                    }\n                } else if !command.is_empty() {\n                    uart.write_str_blocking(\"Unknown command: \");\n                    uart.write_str_blocking(core::str::from_utf8(command).unwrap_or(\"\"));\n                    uart.write_str_blocking(\"\\r\\n\");\n                }\n            }\n\n            // Reset buffer and prompt\n            buf_idx = 0;\n            uart.write_str_blocking(\"Type a command: \");\n        } else if byte == 8 || byte == 127 {\n            // Backspace\n            if buf_idx > 0 {\n                buf_idx -= 1;\n                uart.write_str_blocking(\"\\x08 \\x08\"); // Erase character\n            }\n        } else if buf_idx < buffer.len() - 1 {\n            // Regular character\n            buffer[buf_idx] = byte;\n            buf_idx += 1;\n            let _ = uart.write_byte(byte);\n        }\n    }\n}\n\n/// Simple parser for u8 from ASCII bytes\nfn parse_u8(bytes: &[u8]) -> Result<u8, ()> {\n    let mut result = 0u8;\n    for &b in bytes {\n        if b.is_ascii_digit() {\n            result = result.checked_mul(10).ok_or(())?;\n            result = result.checked_add(b - b'0').ok_or(())?;\n        } else {\n            return Err(());\n        }\n    }\n    Ok(result)\n}\n"
  },
  {
    "path": "examples/mcxa2xx/src/bin/i2c-async.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse embassy_executor::Spawner;\nuse embassy_time::Timer;\nuse hal::bind_interrupts;\nuse hal::clocks::config::Div8;\nuse hal::config::Config;\nuse hal::i2c::controller::{self, I2c, InterruptHandler, Speed};\nuse hal::peripherals::LPI2C2;\nuse {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _};\n\nbind_interrupts!(\n    struct Irqs {\n        LPI2C2 => InterruptHandler<LPI2C2>;\n    }\n);\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let mut config = Config::default();\n    config.clock_cfg.sirc.fro_lf_div = Div8::from_divisor(1);\n\n    let p = hal::init(config);\n\n    defmt::info!(\"I2C example\");\n\n    let mut config = controller::Config::default();\n    config.speed = Speed::Standard;\n    let mut i2c = I2c::new_async(p.LPI2C2, p.P1_9, p.P1_8, Irqs, config).unwrap();\n    let mut buf = [0u8; 2];\n\n    loop {\n        i2c.async_write_read(0x48, &[0x00], &mut buf).await.unwrap();\n        defmt::info!(\"Buffer: {:02x}\", buf);\n        Timer::after_secs(1).await;\n    }\n}\n"
  },
  {
    "path": "examples/mcxa2xx/src/bin/i2c-blocking.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse embassy_executor::Spawner;\nuse embassy_time::Timer;\nuse hal::clocks::config::Div8;\nuse hal::config::Config;\nuse hal::i2c::controller::{self, I2c, Speed};\nuse tmp108::Tmp108;\nuse {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _};\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let mut config = Config::default();\n    config.clock_cfg.sirc.fro_lf_div = Div8::from_divisor(1);\n\n    let p = hal::init(config);\n\n    defmt::info!(\"I2C example\");\n\n    let mut config = controller::Config::default();\n    config.speed = Speed::Standard;\n    let i2c = I2c::new_blocking(p.LPI2C2, p.P1_9, p.P1_8, config).unwrap();\n    let mut tmp = Tmp108::new_with_a0_gnd(i2c);\n\n    loop {\n        let temperature = tmp.temperature().unwrap();\n        defmt::info!(\"Temperature: {}C\", temperature);\n        Timer::after_secs(1).await;\n    }\n}\n"
  },
  {
    "path": "examples/mcxa2xx/src/bin/i2c-dma.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse embassy_executor::Spawner;\nuse embassy_time::Timer;\nuse hal::bind_interrupts;\nuse hal::clocks::config::Div8;\nuse hal::config::Config;\nuse hal::i2c::controller::{self, I2c, InterruptHandler, Speed};\nuse hal::peripherals::LPI2C2;\nuse {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _};\n\nbind_interrupts!(\n    struct Irqs {\n        LPI2C2 => InterruptHandler<LPI2C2>;\n    }\n);\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let mut config = Config::default();\n    config.clock_cfg.sirc.fro_lf_div = Div8::from_divisor(1);\n\n    let p = hal::init(config);\n\n    defmt::info!(\"I2C example\");\n\n    let mut config = controller::Config::default();\n    config.speed = Speed::Standard;\n    let mut i2c = I2c::new_async_with_dma(p.LPI2C2, p.P1_9, p.P1_8, p.DMA_CH0, p.DMA_CH1, Irqs, config).unwrap();\n    let mut buf = [0u8; 2];\n\n    loop {\n        i2c.async_write_read(0x48, &[0x00], &mut buf).await.unwrap();\n        defmt::info!(\"Buffer: {:02x}\", buf);\n        Timer::after_secs(1).await;\n    }\n}\n"
  },
  {
    "path": "examples/mcxa2xx/src/bin/i2c-scan-blocking.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse embassy_executor::Spawner;\nuse hal::clocks::config::Div8;\nuse hal::config::Config;\nuse hal::i2c::controller::{self, I2c, Speed};\nuse {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _};\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let mut config = Config::default();\n    config.clock_cfg.sirc.fro_lf_div = Div8::from_divisor(1);\n\n    let p = hal::init(config);\n\n    defmt::info!(\"I2C example\");\n\n    let mut config = controller::Config::default();\n    config.speed = Speed::Standard;\n\n    let mut i2c = I2c::new_blocking(p.LPI2C2, p.P1_9, p.P1_8, config).unwrap();\n\n    for addr in 0x01..=0x7f {\n        let result = i2c.blocking_write(addr, &[]);\n        if result.is_ok() {\n            defmt::info!(\"Device found at addr {:02x}\", addr);\n        }\n    }\n}\n"
  },
  {
    "path": "examples/mcxa2xx/src/bin/i2c-target-async.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse embassy_executor::Spawner;\nuse hal::bind_interrupts;\nuse hal::clocks::config::Div8;\nuse hal::config::Config;\nuse hal::i2c::target::{self, InterruptHandler};\nuse hal::peripherals::LPI2C3;\nuse {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _};\n\nbind_interrupts!(\n    struct Irqs {\n        LPI2C3 => InterruptHandler<LPI2C3>;\n    }\n);\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let mut config = Config::default();\n    config.clock_cfg.sirc.fro_lf_div = Div8::from_divisor(1);\n\n    let p = hal::init(config);\n\n    defmt::info!(\"I2C target example\");\n\n    let mut config = target::Config::default();\n    config.address = target::Address::Single(0x2a);\n\n    // Other possible address configurations\n    // config.address = target::Address::Dual(0x2a, 0x31);\n    // config.address = target::Address::Range(0x20..0x30);\n\n    let mut target = target::I2c::new_async(p.LPI2C3, p.P3_27, p.P3_28, Irqs, config).unwrap();\n    let mut buf = [0u8; 32];\n\n    loop {\n        let request = target.async_listen().await.unwrap();\n        defmt::info!(\"Received event {}\", request);\n        match request {\n            target::Request::Read(_addr) => {\n                buf.fill(0x55);\n                let count = target.async_respond_to_read(&buf).await.unwrap();\n                defmt::info!(\"T [R]: {:02x} -> {:02x}\", _addr, buf[..count]);\n            }\n            target::Request::Write(_addr) => {\n                let count = target.async_respond_to_write(&mut buf).await.unwrap();\n                defmt::info!(\"T [W]: {:02x} <- {:02x}\", _addr, buf[..count]);\n            }\n            _ => {}\n        }\n    }\n}\n"
  },
  {
    "path": "examples/mcxa2xx/src/bin/i2c-target-blocking.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse embassy_executor::Spawner;\nuse hal::clocks::config::Div8;\nuse hal::config::Config;\nuse hal::i2c::target;\nuse {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _};\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let mut config = Config::default();\n    config.clock_cfg.sirc.fro_lf_div = Div8::from_divisor(1);\n\n    let p = hal::init(config);\n\n    defmt::info!(\"I2C target example\");\n\n    let mut config = target::Config::default();\n    config.address = target::Address::Single(42);\n\n    // Other possible address configurations\n    // config.address = target::Address::Dual(0x2a, 0x31);\n    // config.address = target::Address::Range(0x20..0x30);\n\n    let mut target = target::I2c::new_blocking(p.LPI2C3, p.P3_27, p.P3_28, config).unwrap();\n    let mut buf = [0u8; 32];\n\n    loop {\n        let request = target.blocking_listen().unwrap();\n        defmt::info!(\"Received event {}\", request);\n        match request {\n            target::Request::Read(_addr) => {\n                buf.fill(0x55);\n                let count = target.blocking_respond_to_read(&buf).unwrap();\n                defmt::info!(\"T [R]: {:02x} -> {:02x}\", _addr, buf[..count]);\n            }\n            target::Request::Write(_addr) => {\n                let count = target.blocking_respond_to_write(&mut buf).unwrap();\n                defmt::info!(\"T [W]: {:02x} <- {:02x}\", _addr, buf[..count]);\n            }\n            _ => {}\n        }\n    }\n}\n"
  },
  {
    "path": "examples/mcxa2xx/src/bin/i2c-target-dma.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse embassy_executor::Spawner;\nuse hal::bind_interrupts;\nuse hal::clocks::config::Div8;\nuse hal::config::Config;\nuse hal::i2c::target::{self, InterruptHandler};\nuse hal::peripherals::LPI2C3;\nuse {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _};\n\nbind_interrupts!(\n    struct Irqs {\n        LPI2C3 => InterruptHandler<LPI2C3>;\n    }\n);\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let mut config = Config::default();\n    config.clock_cfg.sirc.fro_lf_div = Div8::from_divisor(1);\n\n    let p = hal::init(config);\n\n    defmt::info!(\"I2C target example\");\n\n    let mut config = target::Config::default();\n    config.address = target::Address::Range(0x20..0x30);\n\n    // Other possible address configurations\n    // config.address = target::Address::Single(0x2a);\n    // config.address = target::Address::Dual(0x2a, 0x31);\n    // config.address = target::Address::Range(0x20..0x30);\n\n    let mut target =\n        target::I2c::new_async_with_dma(p.LPI2C3, p.P3_27, p.P3_28, p.DMA_CH0, p.DMA_CH1, Irqs, config).unwrap();\n    let mut buf = [0u8; 256];\n\n    loop {\n        let request = target.async_listen().await.unwrap();\n        defmt::info!(\"Received event {}\", request);\n        match request {\n            target::Request::Read(_addr) => {\n                buf.fill(0x55);\n                let count = target.async_respond_to_read(&buf).await.unwrap();\n                defmt::info!(\"T [R]: {:02x} -> {:02x}\", _addr, buf[..count]);\n            }\n            target::Request::Write(_addr) => {\n                let count = target.async_respond_to_write(&mut buf).await.unwrap();\n                defmt::info!(\"T [W]: {:02x} <- {:02x}\", _addr, buf[..count]);\n            }\n            _ => {}\n        }\n    }\n}\n"
  },
  {
    "path": "examples/mcxa2xx/src/bin/i3c-async.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse embassy_executor::Spawner;\nuse embassy_time::Timer;\nuse hal::bind_interrupts;\nuse hal::clocks::config::Div8;\nuse hal::config::Config;\nuse hal::i3c::InterruptHandler;\nuse hal::i3c::controller::{self, BusType, I3c};\nuse hal::peripherals::I3C0;\nuse {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _};\n\nbind_interrupts!(\n    struct Irqs {\n        I3C0 => InterruptHandler<I3C0>;\n    }\n);\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let mut config = Config::default();\n    config.clock_cfg.sirc.fro_lf_div = Div8::from_divisor(1);\n\n    let p = hal::init(config);\n\n    defmt::info!(\"I3C example\");\n\n    let config = controller::Config::default();\n    let mut i3c = I3c::new_async(p.I3C0, p.P1_9, p.P1_8, Irqs, config).unwrap();\n    let mut buf = [0u8; 2];\n\n    loop {\n        // ~~~~~~~~ //\n        // I2C mode //\n        // ~~~~~~~~ //\n\n        // RSTDAA: reset first to make sure device responds to I2c requests.\n        i3c.async_write(0x7e, &[0x06], BusType::I3cSdr).await.unwrap();\n\n        i3c.async_write_read(0x48, &[0x00], &mut buf, BusType::I2c)\n            .await\n            .unwrap();\n        let raw = f32::from(i16::from_be_bytes(buf) / 16);\n        let temp_i2c = raw * 0.0625;\n        defmt::info!(\"P3T1755 via I2C: {}C\", temp_i2c);\n        Timer::after_secs(1).await;\n\n        // ~~~~~~~~~~~~ //\n        // I3C SDR mode //\n        // ~~~~~~~~~~~~ //\n\n        // RSTDAA\n        i3c.async_write(0x7e, &[0x06], BusType::I3cSdr).await.unwrap();\n\n        Timer::after_micros(100).await;\n\n        // ENTDAA\n        i3c.async_write(0x7e, &[0x07], BusType::I3cSdr).await.unwrap();\n\n        // P3T1755 temperature register = 0x00\n        i3c.async_write_read(0x48, &[0x00], &mut buf, BusType::I3cSdr)\n            .await\n            .unwrap();\n        let raw = f32::from(i16::from_be_bytes(buf) / 16);\n        let temp_i3c_sdr = raw * 0.0625;\n        defmt::info!(\"P3T1755 via I3C SDR: {}C\", temp_i3c_sdr);\n        Timer::after_secs(1).await;\n    }\n}\n"
  },
  {
    "path": "examples/mcxa2xx/src/bin/i3c-blocking.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse embassy_executor::Spawner;\nuse embassy_time::Timer;\nuse hal::clocks::config::Div8;\nuse hal::config::Config;\nuse hal::i3c::controller::{self, BusType, I3c};\nuse {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _};\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let mut config = Config::default();\n    config.clock_cfg.sirc.fro_lf_div = Div8::from_divisor(1);\n\n    let p = hal::init(config);\n\n    defmt::info!(\"I3C example\");\n\n    let config = controller::Config::default();\n    let mut i3c = I3c::new_blocking(p.I3C0, p.P1_9, p.P1_8, config).unwrap();\n    let mut buf = [0u8; 2];\n\n    loop {\n        // ~~~~~~~~ //\n        // I2C mode //\n        // ~~~~~~~~ //\n\n        // RSTDAA: reset first to make sure device responds to I2c requests.\n        i3c.blocking_write(0x7e, &[0x06], BusType::I3cSdr).unwrap();\n\n        i3c.blocking_write_read(0x48, &[0x00], &mut buf, BusType::I2c).unwrap();\n        let raw = f32::from(i16::from_be_bytes(buf) / 16);\n        let temp_i2c = raw * 0.0625;\n        defmt::info!(\"P3T1755 via I2C: {}C\", temp_i2c);\n        Timer::after_secs(1).await;\n\n        // ~~~~~~~~~~~~ //\n        // I3C SDR mode //\n        // ~~~~~~~~~~~~ //\n\n        // RSTDAA\n        i3c.blocking_write(0x7e, &[0x06], BusType::I3cSdr).unwrap();\n\n        Timer::after_micros(100).await;\n\n        // ENTDAA\n        i3c.blocking_write(0x7e, &[0x07], BusType::I3cSdr).unwrap();\n\n        // P3T1755 temperature register = 0x00\n        i3c.blocking_write_read(0x48, &[0x00], &mut buf, BusType::I3cSdr)\n            .unwrap();\n        let raw = f32::from(i16::from_be_bytes(buf) / 16);\n        let temp_i3c = raw * 0.0625;\n        defmt::info!(\"P3T1755 via I3C SDR: {}C\", temp_i3c);\n        Timer::after_secs(1).await;\n    }\n}\n"
  },
  {
    "path": "examples/mcxa2xx/src/bin/i3c-dma.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse embassy_executor::Spawner;\nuse embassy_time::Timer;\nuse hal::bind_interrupts;\nuse hal::clocks::config::Div8;\nuse hal::config::Config;\nuse hal::i3c::InterruptHandler;\nuse hal::i3c::controller::{self, BusType, I3c};\nuse hal::peripherals::I3C0;\nuse {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _};\n\nbind_interrupts!(\n    struct Irqs {\n        I3C0 => InterruptHandler<I3C0>;\n    }\n);\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let mut config = Config::default();\n    config.clock_cfg.sirc.fro_lf_div = Div8::from_divisor(1);\n\n    let p = hal::init(config);\n\n    defmt::info!(\"I3C example\");\n\n    let config = controller::Config::default();\n    let mut i3c = I3c::new_async_with_dma(p.I3C0, p.P1_9, p.P1_8, p.DMA_CH0, p.DMA_CH1, Irqs, config).unwrap();\n    let mut buf = [0u8; 2];\n\n    loop {\n        // ~~~~~~~~ //\n        // I2C mode //\n        // ~~~~~~~~ //\n\n        // RSTDAA: reset first to make sure device responds to I2c requests.\n        i3c.async_write(0x7e, &[0x06], BusType::I3cSdr).await.unwrap();\n\n        i3c.async_write_read(0x48, &[0x00], &mut buf, BusType::I2c)\n            .await\n            .unwrap();\n        let raw = f32::from(i16::from_be_bytes(buf) / 16);\n        let temp_i2c = raw * 0.0625;\n        defmt::info!(\"P3T1755 via I2C: {}C\", temp_i2c);\n        Timer::after_secs(1).await;\n\n        // ~~~~~~~~~~~~ //\n        // I3C SDR mode //\n        // ~~~~~~~~~~~~ //\n\n        // RSTDAA\n        i3c.async_write(0x7e, &[0x06], BusType::I3cSdr).await.unwrap();\n\n        Timer::after_micros(100).await;\n\n        // ENTDAA\n        i3c.async_write(0x7e, &[0x07], BusType::I3cSdr).await.unwrap();\n\n        // P3T1755 temperature register = 0x00\n        i3c.async_write_read(0x48, &[0x00], &mut buf, BusType::I3cSdr)\n            .await\n            .unwrap();\n        let raw = f32::from(i16::from_be_bytes(buf) / 16);\n        let temp_i3c_sdr = raw * 0.0625;\n        defmt::info!(\"P3T1755 via I3C SDR: {}C\", temp_i3c_sdr);\n        Timer::after_secs(1).await;\n    }\n}\n"
  },
  {
    "path": "examples/mcxa2xx/src/bin/lpuart_bbq_rx.rs",
    "content": "//! LPUART BBQueue example for MCXA276.\n//!\n//! This scenario is meant to be coupled with another device sending using the\n//! `lpuart_bbq_tx` example. In this scenario:\n//!\n//! * We set the device up to have a high power FRO180M clock in active mode\n//! * We set up the device to disable that FRO180M clock in deep sleep\n//! * We use a GPIO interrupt to detect the falling edge on the UART RX line while in deep sleep\n//! * We wake up, start the lpuart from the FRO180M clock source (which keeps us from entering deep sleep)\n//! * We process any incoming data, until the line is silent for 100ms\n//! * We then repeat the processs\n//!\n//! This is meant to model a scenario where the other device is talking to us, and sends\n//! a short \"dummy\"/\"knock\" packet to wake us up, then sends a relatively large burst of data\n//! for some amount of time, before timing out.\n//!\n//! By using a GPIO interrupt and releasing the lpuart, we are able to return to deep sleep mode, at the\n//! cost of losing any data sent while we are \"waking up\". Experimentally, this is approximately 80us, or\n//! about 32 bytes of data at 4mbaud. In the tx half of this example, the other device waits a full millisecond\n//! between the \"knock\" and the \"data\" packets.\n//!\n//! The benefit to this approach is that the CPU uses ~18mA at \"cpu working\" level, ~8mA at \"cpu WFE, lpuart+FRO180M active\" level,\n//! And ~160uA at \"waiting for GPIO interrupt\" level.\n\n#![no_std]\n#![no_main]\n\nuse embassy_executor::Spawner;\nuse embassy_mcxa::clocks::PoweredClock;\nuse embassy_mcxa::clocks::config::{\n    CoreSleep, Div8, FircConfig, FircFreqSel, FlashSleep, MainClockConfig, MainClockSource, VddDriveStrength, VddLevel,\n};\nuse embassy_mcxa::clocks::periph_helpers::LpuartClockSel;\nuse embassy_mcxa::dma::DmaChannel;\nuse embassy_mcxa::gpio::{DriveStrength, Input, Level, Output, Pull, SlewRate};\nuse embassy_mcxa::lpuart::{BbqConfig, BbqHalfParts, BbqRxMode, LpuartBbqRx};\nuse embassy_mcxa::{bind_interrupts, gpio, lpuart, peripherals};\nuse embassy_time::{Duration, WithTimeout};\nuse static_cell::ConstStaticCell;\nuse {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _};\n\nbind_interrupts!(struct Irqs {\n    LPUART3 => lpuart::BbqInterruptHandler::<hal::peripherals::LPUART3>;\n    GPIO4 => gpio::InterruptHandler<peripherals::GPIO4>;\n});\n\nconst SIZE: usize = 4096;\nstatic RX_BUF: ConstStaticCell<[u8; SIZE]> = ConstStaticCell::new([0u8; SIZE]);\n\n#[cfg_attr(\n    feature = \"executor-platform\",\n    embassy_executor::main(executor = \"embassy_mcxa::executor::Executor\", entry = \"cortex_m_rt::entry\")\n)]\n#[cfg_attr(not(feature = \"executor-platform\"), embassy_executor::main)]\nasync fn main(_spawner: Spawner) {\n    let mut cfg = hal::config::Config::default();\n\n    // Enable 180MHz clock source\n    let mut fcfg = FircConfig::default();\n    fcfg.frequency = FircFreqSel::Mhz180;\n    fcfg.power = PoweredClock::NormalEnabledDeepSleepDisabled;\n    fcfg.fro_hf_enabled = true;\n    fcfg.clk_hf_fundamental_enabled = false;\n    fcfg.fro_hf_div = Some(Div8::no_div());\n    cfg.clock_cfg.firc = Some(fcfg);\n\n    // Enable 12M osc\n    cfg.clock_cfg.sirc.fro_12m_enabled = true;\n    cfg.clock_cfg.sirc.fro_lf_div = Some(Div8::no_div());\n    cfg.clock_cfg.sirc.power = PoweredClock::AlwaysEnabled;\n\n    // Disable 16K osc\n    cfg.clock_cfg.fro16k = None;\n\n    // Disable external osc\n    cfg.clock_cfg.sosc = None;\n\n    // Disable PLL\n    cfg.clock_cfg.spll = None;\n\n    // Feed core from 180M osc\n    cfg.clock_cfg.main_clock = MainClockConfig {\n        source: MainClockSource::FircHfRoot,\n        power: PoweredClock::NormalEnabledDeepSleepDisabled,\n        ahb_clk_div: Div8::no_div(),\n    };\n\n    // Active mode: High power, Deep Sleep: Low power\n    cfg.clock_cfg.vdd_power.active_mode.level = VddLevel::OverDriveMode;\n    cfg.clock_cfg.vdd_power.active_mode.drive = VddDriveStrength::Normal;\n    cfg.clock_cfg.vdd_power.low_power_mode.level = VddLevel::MidDriveMode;\n    cfg.clock_cfg.vdd_power.low_power_mode.drive = VddDriveStrength::Low { enable_bandgap: false };\n\n    // Set to deep sleep for measuring power. Set to WfeUngated to see defmt logs\n    cfg.clock_cfg.vdd_power.core_sleep = CoreSleep::DeepSleep;\n\n    // Set flash doze, allowing internal flash clocks to be gated on sleep\n    cfg.clock_cfg.vdd_power.flash_sleep = FlashSleep::FlashDoze;\n\n    let p = hal::init(cfg);\n\n    defmt::info!(\"LPUART DMA example starting...\");\n\n    // Create UART configuration\n    let mut config = BbqConfig::default();\n    config.baudrate_bps = 4_000_000;\n    config.power = PoweredClock::NormalEnabledDeepSleepDisabled;\n    config.source = LpuartClockSel::FroHfDiv;\n\n    let rx_buf = RX_BUF.take();\n\n    // Create UART instance with DMA channels\n    let dma = DmaChannel::new(p.DMA_CH0);\n\n    let mut parts = BbqHalfParts::new_rx_half_async(p.LPUART3, Irqs, p.P4_2, rx_buf, dma);\n    let mut red = Output::new(p.P3_18, Level::High, DriveStrength::Normal, SlewRate::Fast);\n    let mut debug = Output::new(p.P3_28, Level::High, DriveStrength::Normal, SlewRate::Fast);\n\n    #[cfg(feature = \"executor-platform\")]\n    embassy_mcxa::executor::set_executor_debug_gpio(p.P1_0);\n\n    loop {\n        defmt::info!(\"Waiting for falling edge\");\n        red.set_high();\n        debug.set_high();\n        {\n            let mut input = defmt::unwrap!(Input::async_from_anypin(parts.pin(), Pull::Up));\n            input.wait_for_low().await;\n        }\n\n        let mode = BbqRxMode::MaxFrame { size: 1024 };\n        let mut lpuart = LpuartBbqRx::new(parts, config, mode).unwrap();\n        red.set_low();\n        debug.set_low();\n        defmt::info!(\"got wake, listening\");\n\n        let mut streak = 0;\n        let mut idx = 0u8;\n        let mut buf = [0u8; 64];\n        let mut got = 0;\n        let mut dummies = 0;\n\n        'wake: loop {\n            match lpuart.read(&mut buf).with_timeout(Duration::from_millis(100)).await {\n                Ok(res) => {\n                    let used = res.unwrap();\n                    for byte in &buf[..used] {\n                        if *byte > 0x7F {\n                            dummies += 1;\n                        } else if *byte == idx {\n                            streak += 1;\n                            idx = idx.wrapping_add(1) & 0x7F;\n                        } else {\n                            streak = 0;\n                            idx = (*byte).wrapping_add(1) & 0x7F;\n                        }\n                    }\n                    got += used;\n                }\n                Err(_) => {\n                    break 'wake;\n                }\n            }\n        }\n\n        defmt::info!(\n            \"Going to sleep, got: {=usize}, streak: {=usize}, dummies: {=usize}\",\n            got,\n            streak,\n            dummies\n        );\n\n        parts = lpuart.teardown();\n    }\n}\n"
  },
  {
    "path": "examples/mcxa2xx/src/bin/lpuart_bbq_tx.rs",
    "content": "//! LPUART BBQueue example for MCXA276.\n//!\n//! This scenario is meant to be coupled with another device receiving using the\n//! `lpuart_bbq_rx` example. In this scenario:\n//!\n//! * We send a short 16-byte \"knock\" packet\n//! * We wait 1ms for the other device to wake up\n//! * We send a larger 768-byte \"data\" packet\n//!\n//! See `lpuart_bbq_rx` for more information. This half is not sleepy, it just sends\n//! regularly without concern for power consumption.\n\n#![no_std]\n#![no_main]\n\nuse embassy_executor::Spawner;\nuse embassy_mcxa::clocks::PoweredClock;\nuse embassy_mcxa::clocks::config::{\n    CoreSleep, Div8, FircConfig, FircFreqSel, FlashSleep, MainClockConfig, MainClockSource, VddDriveStrength, VddLevel,\n};\nuse embassy_mcxa::clocks::periph_helpers::LpuartClockSel;\nuse embassy_mcxa::dma::DmaChannel;\nuse embassy_mcxa::gpio::{DriveStrength, Level, Output, SlewRate};\nuse embassy_mcxa::lpuart::{BbqConfig, BbqHalfParts, LpuartBbqTx};\nuse embassy_mcxa::{bind_interrupts, lpuart};\nuse embassy_time::Timer;\nuse static_cell::ConstStaticCell;\nuse {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _};\n\nbind_interrupts!(struct Irqs {\n    LPUART3 => lpuart::BbqInterruptHandler::<hal::peripherals::LPUART3>;\n});\n\nconst SIZE: usize = 4096;\nstatic TX_BUF: ConstStaticCell<[u8; SIZE]> = ConstStaticCell::new([0u8; SIZE]);\n\n#[cfg_attr(\n    feature = \"executor-platform\",\n    embassy_executor::main(executor = \"embassy_mcxa::executor::Executor\", entry = \"cortex_m_rt::entry\")\n)]\n#[cfg_attr(not(feature = \"executor-platform\"), embassy_executor::main)]\nasync fn main(_spawner: Spawner) {\n    let mut cfg = hal::config::Config::default();\n\n    // Enable 180MHz clock source\n    let mut fcfg = FircConfig::default();\n    fcfg.frequency = FircFreqSel::Mhz180;\n    fcfg.power = PoweredClock::NormalEnabledDeepSleepDisabled;\n    fcfg.fro_hf_enabled = true;\n    fcfg.clk_hf_fundamental_enabled = false;\n    fcfg.fro_hf_div = Some(const { Div8::from_divisor(4).unwrap() });\n    cfg.clock_cfg.firc = Some(fcfg);\n\n    // Enable 12M osc\n    cfg.clock_cfg.sirc.fro_12m_enabled = true;\n    cfg.clock_cfg.sirc.fro_lf_div = Some(Div8::no_div());\n    cfg.clock_cfg.sirc.power = PoweredClock::AlwaysEnabled;\n\n    // Disable 16K osc\n    cfg.clock_cfg.fro16k = None;\n\n    // Disable external osc\n    cfg.clock_cfg.sosc = None;\n\n    // Disable PLL\n    cfg.clock_cfg.spll = None;\n\n    // Feed core from 180M osc\n    cfg.clock_cfg.main_clock = MainClockConfig {\n        source: MainClockSource::FircHfRoot,\n        power: PoweredClock::NormalEnabledDeepSleepDisabled,\n        ahb_clk_div: Div8::no_div(),\n    };\n\n    // We don't sleep, set relatively high power\n    cfg.clock_cfg.vdd_power.active_mode.level = VddLevel::OverDriveMode;\n    cfg.clock_cfg.vdd_power.low_power_mode.level = VddLevel::MidDriveMode;\n    cfg.clock_cfg.vdd_power.active_mode.drive = VddDriveStrength::Normal;\n    cfg.clock_cfg.vdd_power.low_power_mode.drive = VddDriveStrength::Low { enable_bandgap: false };\n\n    // Set \"never sleep\" mode\n    cfg.clock_cfg.vdd_power.core_sleep = CoreSleep::WfeUngated;\n\n    // Set flash doze, allowing internal flash clocks to be gated on sleep\n    cfg.clock_cfg.vdd_power.flash_sleep = FlashSleep::FlashDoze;\n\n    let p = hal::init(cfg);\n\n    defmt::info!(\"LPUART DMA example starting...\");\n\n    // Create UART configuration\n    let mut config = BbqConfig::default();\n    config.baudrate_bps = 4_000_000;\n    config.power = PoweredClock::NormalEnabledDeepSleepDisabled;\n    config.source = LpuartClockSel::FroHfDiv;\n\n    let tx_buf = TX_BUF.take();\n\n    // Create UART instance with DMA channels\n    let dma_channel = DmaChannel::new(p.DMA_CH0);\n    let parts = BbqHalfParts::new_tx_half(p.LPUART3, Irqs, p.P4_5, tx_buf, dma_channel);\n    let mut lpuart = LpuartBbqTx::new(parts, config).unwrap();\n    let mut to_knock = [0u8; 16];\n    let mut to_send = [0u8; 768];\n    to_knock.iter_mut().for_each(|b| *b = 0xFF);\n    to_send.iter_mut().enumerate().for_each(|(i, b)| *b = (i as u8) & 0x7F);\n\n    Timer::after_millis(1000).await;\n\n    let mut red = Output::new(p.P3_18, Level::High, DriveStrength::Normal, SlewRate::Fast);\n\n    #[cfg(feature = \"executor-platform\")]\n    embassy_mcxa::executor::set_executor_debug_gpio(p.P4_2);\n\n    loop {\n        // Send a small 16-byte \"knock\" packet in case the other device is sleeping\n        defmt::info!(\"knock!\");\n        let mut window = to_knock.as_slice();\n        while !window.is_empty() {\n            let sent = lpuart.write(window).await.unwrap();\n            let (_now, later) = window.split_at(sent);\n            window = later;\n        }\n        defmt::info!(\"Knocked, flushing...\");\n        lpuart.flush().await;\n        // Wait a small amount of time AFTER knocking to allow the device to wake up\n        Timer::after_millis(1).await;\n\n        defmt::info!(\"Sending!\");\n        let mut window = to_send.as_slice();\n        while !window.is_empty() {\n            let sent = lpuart.write(window).await.unwrap();\n            let (_now, later) = window.split_at(sent);\n            window = later;\n        }\n        defmt::info!(\"Sent, flushing...\");\n        lpuart.flush().await;\n\n        defmt::info!(\"Flushed.\");\n\n        // Now wait a bit to let the other device go back to sleep\n        Timer::after_millis(3000).await;\n        red.toggle();\n    }\n}\n"
  },
  {
    "path": "examples/mcxa2xx/src/bin/lpuart_buffered.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse embassy_executor::Spawner;\nuse embassy_mcxa::clocks::config::Div8;\nuse embassy_mcxa::lpuart::{Config, Lpuart};\nuse embassy_mcxa::{bind_interrupts, lpuart};\nuse embedded_io_async::Write;\nuse {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _};\n\n// Bind OS_EVENT for timers plus LPUART2 IRQ for the buffered driver\nbind_interrupts!(struct Irqs {\n    LPUART2 => lpuart::BufferedInterruptHandler::<hal::peripherals::LPUART2>;\n});\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let mut cfg = hal::config::Config::default();\n    cfg.clock_cfg.sirc.fro_12m_enabled = true;\n    cfg.clock_cfg.sirc.fro_lf_div = Some(Div8::no_div());\n    let p = hal::init(cfg);\n\n    // UART configuration (enable both TX and RX)\n    let config = Config {\n        baudrate_bps: 115_200,\n        rx_fifo_watermark: 0,\n        tx_fifo_watermark: 0,\n        ..Default::default()\n    };\n\n    let mut tx_buf = [0u8; 256];\n    let mut rx_buf = [0u8; 256];\n\n    // Create a buffered LPUART2 instance with both TX and RX\n    let mut uart = Lpuart::new_buffered(\n        p.LPUART2,\n        p.P2_2, // TX pin\n        p.P2_3, // RX pin\n        Irqs,\n        &mut tx_buf,\n        &mut rx_buf,\n        config,\n    )\n    .unwrap();\n\n    // Split into TX and RX parts\n    let (tx, rx) = uart.split_ref();\n\n    tx.write(b\"Hello buffered LPUART.\\r\\n\").await.unwrap();\n    tx.write(b\"Type characters to echo them back.\\r\\n\").await.unwrap();\n\n    // Echo loop\n    let mut buf = [0u8; 4];\n    loop {\n        let used = rx.read(&mut buf).await.unwrap();\n        tx.write_all(&buf[..used]).await.unwrap();\n    }\n}\n"
  },
  {
    "path": "examples/mcxa2xx/src/bin/lpuart_dma.rs",
    "content": "//! LPUART DMA example for MCXA276.\n//!\n//! This example demonstrates using DMA for UART TX and RX operations.\n//! It sends a message using DMA, then waits for 16 characters to be received\n//! via DMA and echoes them back.\n//!\n//! The DMA request sources are automatically derived from the LPUART instance type.\n//! DMA clock/reset/init is handled automatically by the HAL.\n\n#![no_std]\n#![no_main]\n\nuse embassy_executor::Spawner;\nuse embassy_mcxa::clocks::config::Div8;\nuse embassy_mcxa::lpuart::{Config, Lpuart};\nuse {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _};\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let mut cfg = hal::config::Config::default();\n    cfg.clock_cfg.sirc.fro_12m_enabled = true;\n    cfg.clock_cfg.sirc.fro_lf_div = Some(Div8::no_div());\n    let p = hal::init(cfg);\n\n    defmt::info!(\"LPUART DMA example starting...\");\n\n    // Create UART configuration\n    let config = Config {\n        baudrate_bps: 115_200,\n        ..Default::default()\n    };\n\n    // Create UART instance with DMA channels\n    let mut lpuart = Lpuart::new_async_with_dma(\n        p.LPUART2, // Instance\n        p.P2_2,    // TX pin\n        p.P2_3,    // RX pin\n        p.DMA_CH0, // TX DMA channel\n        p.DMA_CH1, // RX DMA channel\n        config,\n    )\n    .unwrap();\n\n    // Send a message using DMA (DMA request source is automatically derived from LPUART2)\n    let tx_msg = b\"Hello from LPUART2 DMA TX!\\r\\n\";\n    lpuart.write(tx_msg).await.unwrap();\n\n    defmt::info!(\"TX DMA complete\");\n\n    // Send prompt\n    let prompt = b\"Type 16 characters to echo via DMA:\\r\\n\";\n    lpuart.write(prompt).await.unwrap();\n\n    // Receive 16 characters using DMA\n    let mut rx_buf = [0u8; 16];\n    lpuart.read(&mut rx_buf).await.unwrap();\n\n    defmt::info!(\"RX DMA complete\");\n\n    // Echo back the received data\n    let echo_prefix = b\"\\r\\nReceived: \";\n    lpuart.write(echo_prefix).await.unwrap();\n    lpuart.write(&rx_buf).await.unwrap();\n    let done_msg = b\"\\r\\nDone!\\r\\n\";\n    lpuart.write(done_msg).await.unwrap();\n\n    defmt::info!(\"Example complete\");\n}\n"
  },
  {
    "path": "examples/mcxa2xx/src/bin/lpuart_polling.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse embassy_executor::Spawner;\nuse embassy_mcxa::clocks::config::Div8;\nuse {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _};\n\nuse crate::hal::lpuart::{Config, Lpuart};\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let mut cfg = hal::config::Config::default();\n    cfg.clock_cfg.sirc.fro_12m_enabled = true;\n    cfg.clock_cfg.sirc.fro_lf_div = Some(Div8::no_div());\n    let p = hal::init(cfg);\n\n    defmt::info!(\"boot\");\n\n    // Create UART configuration\n    let config = Config {\n        baudrate_bps: 115_200,\n        ..Default::default()\n    };\n\n    // Create UART instance using LPUART2 with P2_2 as TX and P2_3 as RX\n    let lpuart = Lpuart::new_blocking(\n        p.LPUART2, // Peripheral\n        p.P2_2,    // TX pin\n        p.P2_3,    // RX pin\n        config,\n    )\n    .unwrap();\n\n    // Split into separate TX and RX parts\n    let (mut tx, mut rx) = lpuart.split();\n\n    // Write hello messages\n    tx.blocking_write(b\"Hello world.\\r\\n\").unwrap();\n    tx.blocking_write(b\"Echoing. Type characters...\\r\\n\").unwrap();\n\n    // Echo loop\n    loop {\n        let mut buf = [0u8; 1];\n        rx.blocking_read(&mut buf).unwrap();\n        tx.blocking_write(&buf).unwrap();\n    }\n}\n"
  },
  {
    "path": "examples/mcxa2xx/src/bin/lpuart_ring_buffer.rs",
    "content": "//! LPUART Ring Buffer DMA example for MCXA276.\n//!\n//! This example demonstrates using the high-level `LpuartRxDma::setup_ring_buffer()`\n//! API for continuous circular DMA reception from a UART peripheral.\n//!\n//! # Features demonstrated:\n//! - `LpuartRxDma::setup_ring_buffer()` for continuous peripheral-to-memory DMA\n//! - `RingBuffer` for async reading of received data\n//! - Handling of potential overrun conditions\n//! - Half-transfer and complete-transfer interrupts for timely wakeups\n//!\n//! # How it works:\n//! 1. Create an `LpuartRxDma` driver with a DMA channel\n//! 2. Call `setup_ring_buffer()` which handles all low-level DMA configuration\n//! 3. Application asynchronously reads data as it arrives via `ring_buf.read()`\n//! 4. Both half-transfer and complete-transfer interrupts wake the reader\n\n#![no_std]\n#![no_main]\n\nuse embassy_executor::Spawner;\nuse embassy_mcxa::clocks::config::Div8;\nuse embassy_mcxa::lpuart::{Config, Dma, Lpuart, LpuartTx};\nuse static_cell::ConstStaticCell;\nuse {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _};\n\n// Ring buffer for RX - power of 2 is ideal for modulo efficiency\nstatic RX_RING_BUFFER: ConstStaticCell<[u8; 64]> = ConstStaticCell::new([0; 64]);\n\n/// Helper to write a byte as hex to UART\nasync fn write_hex<'a>(tx: &mut LpuartTx<'a, Dma<'a>>, byte: u8) {\n    const HEX: &[u8; 16] = b\"0123456789ABCDEF\";\n    let buf = [HEX[(byte >> 4) as usize], HEX[(byte & 0x0F) as usize]];\n    tx.write(&buf).await.unwrap();\n}\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    // Small delay to allow probe-rs to attach after reset\n    for _ in 0..100_000 {\n        cortex_m::asm::nop();\n    }\n\n    let mut cfg = hal::config::Config::default();\n    cfg.clock_cfg.sirc.fro_12m_enabled = true;\n    cfg.clock_cfg.sirc.fro_lf_div = Some(Div8::no_div());\n    let p = hal::init(cfg);\n\n    defmt::info!(\"LPUART Ring Buffer DMA example starting...\");\n\n    // Create UART configuration\n    let config = Config {\n        baudrate_bps: 115_200,\n        ..Default::default()\n    };\n\n    // Create LPUART with DMA support for both TX and RX, then split\n    let lpuart = Lpuart::new_async_with_dma(p.LPUART2, p.P2_2, p.P2_3, p.DMA_CH1, p.DMA_CH0, config).unwrap();\n    let (mut tx, mut rx) = lpuart.split();\n\n    tx.write(b\"LPUART Ring Buffer DMA Example\\r\\n\").await.unwrap();\n    tx.write(b\"==============================\\r\\n\\r\\n\").await.unwrap();\n\n    tx.write(b\"Setting up circular DMA for UART RX...\\r\\n\").await.unwrap();\n\n    let buf = RX_RING_BUFFER.take();\n    // Set up the ring buffer with circular DMA\n    let mut ring_buf = rx.into_ring_dma_rx(buf).unwrap();\n\n    tx.write(b\"Ring buffer ready! Type characters to see them echoed.\\r\\n\")\n        .await\n        .unwrap();\n    tx.write(b\"The DMA continuously receives in the background.\\r\\n\\r\\n\")\n        .await\n        .unwrap();\n\n    // Main loop: read from ring buffer and echo back\n    let mut read_buf = [0u8; 16];\n    let mut total_received: usize = 0;\n\n    loop {\n        // Async read - waits until data is available\n        match ring_buf.read(&mut read_buf).await {\n            Ok(n) if n > 0 => {\n                total_received += n;\n\n                // Echo back what we received\n                tx.write(b\"RX[\").await.unwrap();\n                for (i, &byte) in read_buf.iter().enumerate().take(n) {\n                    write_hex(&mut tx, byte).await;\n                    if i < n - 1 {\n                        tx.write(b\" \").await.unwrap();\n                    }\n                }\n                tx.write(b\"]: \").await.unwrap();\n                tx.write(&read_buf[..n]).await.unwrap();\n                tx.write(b\"\\r\\n\").await.unwrap();\n\n                defmt::info!(\"Received {} bytes, total: {}\", n, total_received);\n            }\n            Ok(_) => {\n                // No data, shouldn't happen with async read\n            }\n            Err(_) => {\n                // Overrun detected\n                tx.write(b\"ERROR: Ring buffer overrun!\\r\\n\").await.unwrap();\n                defmt::error!(\"Ring buffer overrun!\");\n                ring_buf.clear();\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "examples/mcxa2xx/src/bin/power-deepsleep-big-jump.rs",
    "content": "//! This example demonstrates automatic clock gating. This example needs to be run\n//! with:\n//!\n//! ```sh\n//! cargo run --release --no-default-features --features=executor-platform --bin power-deepsleep-big-jump\n//! ```\n//!\n//! **NOTE: This requires rework of the board! You must remove R26 (used for the on\n//! board op-amp), remove R52, and bodge the pad of R52 that is closest to R61 to TP9\n//! (VDD_MCU_LINK). Without these reworks, you will see much higher current consumption.**\n//!\n//! As of 2026-02-04, UM12439 ONLY mentions the R52 errata, but the removal of R26 (as\n//! described in AN14765 for the MCXA346) is also necessary for the FRDM-MCXA266.\n\n#![no_std]\n#![no_main]\n\nuse embassy_executor::Spawner;\nuse embassy_mcxa::clkout::{self, ClockOut, ClockOutSel, Div4};\nuse embassy_mcxa::clocks::config::{\n    CoreSleep, Div8, FircConfig, FircFreqSel, FlashSleep, MainClockConfig, MainClockSource, VddDriveStrength, VddLevel,\n};\nuse embassy_mcxa::clocks::{PoweredClock, WakeGuard};\nuse embassy_time::{Duration, Instant, Timer};\nuse hal::gpio::{DriveStrength, Level, Output, SlewRate};\nuse {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _};\n\n#[cfg_attr(\n    feature = \"executor-platform\",\n    embassy_executor::main(executor = \"embassy_mcxa::executor::Executor\", entry = \"cortex_m_rt::entry\")\n)]\n#[cfg_attr(not(feature = \"executor-platform\"), embassy_executor::main)]\nasync fn main(_spawner: Spawner) {\n    // Do a short delay in order to allow for us to attach the debugger/start\n    // a flash in case some setting below is wrong, and the CPU gets stuck\n    // in deep sleep with debugging disabled.\n    defmt::info!(\"Pre-power delay!\");\n    // Experimentally: about 5-6s or so.\n    cortex_m::asm::delay(45_000_000);\n    defmt::info!(\"Pre-power delay complete!\");\n    let mut cfg = hal::config::Config::default();\n\n    // Enable 180M FIRC\n    let mut fcfg = FircConfig::default();\n    fcfg.frequency = FircFreqSel::Mhz180;\n    fcfg.power = PoweredClock::NormalEnabledDeepSleepDisabled;\n    fcfg.fro_hf_enabled = true;\n    fcfg.clk_hf_fundamental_enabled = false;\n    fcfg.fro_hf_div = Some(const { Div8::from_divisor(180).unwrap() });\n    cfg.clock_cfg.firc = Some(fcfg);\n\n    // Enable 12M osc to use as ostimer clock\n    cfg.clock_cfg.sirc.fro_12m_enabled = true;\n    cfg.clock_cfg.sirc.fro_lf_div = None;\n    cfg.clock_cfg.sirc.power = PoweredClock::AlwaysEnabled;\n\n    // Disable 16K osc\n    cfg.clock_cfg.fro16k = None;\n\n    // Disable external osc\n    cfg.clock_cfg.sosc = None;\n\n    // Disable PLL\n    cfg.clock_cfg.spll = None;\n\n    // Feed core from 180M osc\n    cfg.clock_cfg.main_clock = MainClockConfig {\n        source: MainClockSource::FircHfRoot,\n        power: PoweredClock::NormalEnabledDeepSleepDisabled,\n        ahb_clk_div: Div8::no_div(),\n    };\n\n    // Set the core in high power active mode\n    cfg.clock_cfg.vdd_power.active_mode.level = VddLevel::OverDriveMode;\n    cfg.clock_cfg.vdd_power.active_mode.drive = VddDriveStrength::Normal;\n    // Set the core in low power sleep mode\n    cfg.clock_cfg.vdd_power.low_power_mode.level = VddLevel::MidDriveMode;\n    cfg.clock_cfg.vdd_power.low_power_mode.drive = VddDriveStrength::Low { enable_bandgap: false };\n\n    // Set \"deep sleep\" mode\n    cfg.clock_cfg.vdd_power.core_sleep = CoreSleep::DeepSleep;\n\n    // Set flash doze, allowing internal flash clocks to be gated on sleep\n    cfg.clock_cfg.vdd_power.flash_sleep = FlashSleep::FlashDoze;\n\n    let p = hal::init(cfg);\n\n    #[cfg(feature = \"executor-platform\")]\n    embassy_mcxa::executor::set_executor_debug_gpio(p.P1_12);\n\n    let mut pin = p.P4_2;\n    let mut clkout = p.CLKOUT;\n    const K250_CONFIG: clkout::Config = clkout::Config {\n        // 180MHz / 180 -> 1MHz\n        sel: ClockOutSel::FroHfDiv,\n        // 1MHz / 4 -> 250kHz\n        div: const { Div4::from_divisor(4).unwrap() },\n        level: PoweredClock::NormalEnabledDeepSleepDisabled,\n    };\n\n    // We create a clkout peripheral so that we can view the activity of the clock\n    // in a logic analyzer. We use `new_unchecked` here, which doesn't participate\n    // in verifying that the clock sources are always valid, and does not take\n    // a WaitGuard token, which would prevent us from entering deep sleep.\n    let _clock_out = unsafe { ClockOut::new_unchecked(clkout.reborrow(), pin.reborrow(), K250_CONFIG) };\n\n    defmt::info!(\"Going to sleep shortly...\");\n    cortex_m::asm::delay(45_000_000 / 4);\n\n    let mut red = Output::new(p.P3_18, Level::High, DriveStrength::Normal, SlewRate::Slow);\n    loop {\n        Timer::after_millis(900).await;\n\n        // For the 100ms the LED is low, we manually take a wakeguard to prevent the\n        // system from returning to deep sleep, which drastically increases our power\n        // usage, but also prevents these clock sources from being disabled automatically.\n        red.set_low();\n        let _wg = WakeGuard::new();\n        let start = Instant::now();\n\n        // for the first 20ms, busyloop\n        while start.elapsed() < Duration::from_millis(20) {}\n\n        // then wfe sleep for 80ms\n        Timer::after_millis(80).await;\n\n        red.set_high();\n        // The WakeGuard is dropped here before returning to the top of the loop. When this\n        // happens, we will enter deep sleep automatically on our next .await.\n    }\n}\n"
  },
  {
    "path": "examples/mcxa2xx/src/bin/power-deepsleep-gating.rs",
    "content": "//! This example demonstrates automatic clock gating. This example needs to be run\n//! with:\n//!\n//! ```sh\n//! cargo run --release --no-default-features --features=executor-platform --bin power-deepsleep-gating\n//! ```\n//!\n//! **NOTE: This requires rework of the board! You must remove R26 (used for the on\n//! board op-amp), remove R52, and bodge the pad of R52 that is closest to R61 to TP9\n//! (VDD_MCU_LINK). Without these reworks, you will see much higher current consumption.**\n//!\n//! As of 2026-02-04, UM12439 ONLY mentions the R52 errata, but the removal of R26 (as\n//! described in AN14765 for the MCXA346) is also necessary for the FRDM-MCXA266.\n\n#![no_std]\n#![no_main]\n\nuse embassy_executor::Spawner;\nuse embassy_mcxa::clkout::{self, ClockOut, ClockOutSel, Div4};\nuse embassy_mcxa::clocks::config::{\n    CoreSleep, Div8, FlashSleep, MainClockConfig, MainClockSource, SoscConfig, SoscMode, SpllConfig, SpllMode,\n    SpllSource, VddDriveStrength, VddLevel,\n};\nuse embassy_mcxa::clocks::{PoweredClock, WakeGuard};\nuse embassy_time::Timer;\nuse hal::gpio::{DriveStrength, Level, Output, SlewRate};\nuse {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _};\n\n#[cfg_attr(\n    feature = \"executor-platform\",\n    embassy_executor::main(executor = \"embassy_mcxa::executor::Executor\", entry = \"cortex_m_rt::entry\")\n)]\n#[cfg_attr(not(feature = \"executor-platform\"), embassy_executor::main)]\nasync fn main(_spawner: Spawner) {\n    // Do a short delay in order to allow for us to attach the debugger/start\n    // a flash in case some setting below is wrong, and the CPU gets stuck\n    // in deep sleep with debugging disabled.\n    defmt::info!(\"Pre-power delay!\");\n    // Experimentally: about 5-6s or so.\n    cortex_m::asm::delay(45_000_000);\n    defmt::info!(\"Pre-power delay complete!\");\n    let mut cfg = hal::config::Config::default();\n\n    // Disable 45M osc\n    cfg.clock_cfg.firc = None;\n\n    // Enable 12M osc to use as core clock\n    cfg.clock_cfg.sirc.fro_12m_enabled = true;\n    cfg.clock_cfg.sirc.fro_lf_div = None;\n    cfg.clock_cfg.sirc.power = PoweredClock::AlwaysEnabled;\n\n    // Disable 16K osc\n    cfg.clock_cfg.fro16k = None;\n\n    // Enable external osc, but ONLY in high-power mode\n    cfg.clock_cfg.sosc = Some(SoscConfig {\n        mode: SoscMode::CrystalOscillator,\n        frequency: 8_000_000,\n        power: PoweredClock::NormalEnabledDeepSleepDisabled,\n    });\n\n    // Enable PLL, but ONLY in high-power mode\n    cfg.clock_cfg.spll = Some(SpllConfig {\n        source: SpllSource::Sosc,\n        // 8MHz\n        // 8 x 48 => 384MHz\n        // 384 / (16 x 2) => 12.0MHz\n        mode: SpllMode::Mode1b {\n            m_mult: 48,\n            p_div: 16,\n            bypass_p2_div: false,\n        },\n        power: PoweredClock::NormalEnabledDeepSleepDisabled,\n        pll1_clk_div: None,\n    });\n\n    // Feed core from 12M osc\n    cfg.clock_cfg.main_clock = MainClockConfig {\n        source: MainClockSource::SircFro12M,\n        power: PoweredClock::AlwaysEnabled,\n        ahb_clk_div: Div8::no_div(),\n    };\n\n    // Set lowest core power, disable bandgap LDO reference\n    cfg.clock_cfg.vdd_power.active_mode.level = VddLevel::MidDriveMode;\n    cfg.clock_cfg.vdd_power.low_power_mode.level = VddLevel::MidDriveMode;\n    cfg.clock_cfg.vdd_power.low_power_mode.drive = VddDriveStrength::Low { enable_bandgap: false };\n\n    // We DO need to enable the core bandgap while in active mode, as the SOSC and SPLL clock\n    // sources require this to operate. If we wanted these clocks active in low power mode,\n    // we would need to enable it there too.\n    cfg.clock_cfg.vdd_power.active_mode.drive = VddDriveStrength::Low { enable_bandgap: true };\n\n    // Set \"deep sleep\" mode\n    cfg.clock_cfg.vdd_power.core_sleep = CoreSleep::DeepSleep;\n\n    // Set flash doze, allowing internal flash clocks to be gated on sleep\n    cfg.clock_cfg.vdd_power.flash_sleep = FlashSleep::FlashDoze;\n\n    let p = hal::init(cfg);\n\n    #[cfg(feature = \"executor-platform\")]\n    embassy_mcxa::executor::set_executor_debug_gpio(p.P1_12);\n\n    let mut pin = p.P4_2;\n    let mut clkout = p.CLKOUT;\n    const M1_CONFIG: clkout::Config = clkout::Config {\n        sel: ClockOutSel::Pll1Clk,\n        div: const { Div4::from_divisor(12).unwrap() },\n        level: PoweredClock::NormalEnabledDeepSleepDisabled,\n    };\n\n    // We create a clkout peripheral so that we can view the activity of the clock\n    // in a logic analyzer. We use `new_unchecked` here, which doesn't participate\n    // in verifying that the clock sources are always valid, and does not take\n    // a WaitGuard token, which would prevent us from entering deep sleep.\n    let _clock_out = unsafe { ClockOut::new_unchecked(clkout.reborrow(), pin.reborrow(), M1_CONFIG) };\n\n    defmt::info!(\"Going to sleep shortly...\");\n    cortex_m::asm::delay(45_000_000 / 4);\n\n    let mut red = Output::new(p.P3_18, Level::High, DriveStrength::Normal, SlewRate::Slow);\n    loop {\n        Timer::after_millis(900).await;\n\n        // For the 100ms the LED is low, we manually take a wakeguard to prevent the\n        // system from returning to deep sleep, which drastically increases our power\n        // usage (experimentally: 1660uA vs the 154uA visible with these clocks *not*\n        // running in deep sleep), but also prevents these clock sources from being\n        // disabled automatically.\n        red.set_low();\n        let _wg = WakeGuard::new();\n        Timer::after_millis(100).await;\n\n        red.set_high();\n        // The WakeGuard is dropped here before returning to the top of the loop. When this\n        // happens, we will enter deep sleep automatically on our next .await.\n    }\n}\n"
  },
  {
    "path": "examples/mcxa2xx/src/bin/power-deepsleep-gpio-int.rs",
    "content": "//! This example demonstrates automatic clock gating. This example needs to be run\n//! with:\n//!\n//! ```sh\n//! cargo run --release --no-default-features --features=executor-platform --bin power-deepsleep-gpio-int\n//! ```\n//!\n//! **NOTE: This requires rework of the board! You must remove R26 (used for the on\n//! board op-amp), remove R52, and bodge the pad of R52 that is closest to R61 to TP9\n//! (VDD_MCU_LINK). Without these reworks, you will see much higher current consumption.**\n//!\n//! As of 2026-02-04, UM12439 ONLY mentions the R52 errata, but the removal of R26 (as\n//! described in AN14765 for the MCXA346) is also necessary for the FRDM-MCXA266.\n\n#![no_std]\n#![no_main]\n\nuse embassy_executor::Spawner;\nuse embassy_mcxa::clkout::{self, ClockOut, ClockOutSel, Div4};\nuse embassy_mcxa::clocks::config::{\n    CoreSleep, Div8, FircConfig, FircFreqSel, FlashSleep, MainClockConfig, MainClockSource, VddDriveStrength, VddLevel,\n};\nuse embassy_mcxa::clocks::{PoweredClock, WakeGuard};\nuse embassy_mcxa::gpio::{Async, Input, Pull};\nuse embassy_mcxa::{bind_interrupts, gpio, peripherals};\nuse embassy_time::{Duration, Instant, Timer};\nuse hal::gpio::{DriveStrength, Level, Output, SlewRate};\nuse {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _};\n\nbind_interrupts!(struct Irqs {\n    GPIO1 => gpio::InterruptHandler<peripherals::GPIO1>;\n});\n\n#[cfg_attr(\n    feature = \"executor-platform\",\n    embassy_executor::main(executor = \"embassy_mcxa::executor::Executor\", entry = \"cortex_m_rt::entry\")\n)]\n#[cfg_attr(not(feature = \"executor-platform\"), embassy_executor::main)]\nasync fn main(spawner: Spawner) {\n    // Do a short delay in order to allow for us to attach the debugger/start\n    // a flash in case some setting below is wrong, and the CPU gets stuck\n    // in deep sleep with debugging disabled.\n    defmt::info!(\"Pre-power delay!\");\n    // Experimentally: about 5-6s or so.\n    cortex_m::asm::delay(45_000_000);\n    defmt::info!(\"Pre-power delay complete!\");\n    let mut cfg = hal::config::Config::default();\n\n    // Enable 180M FIRC\n    let mut fcfg = FircConfig::default();\n    fcfg.frequency = FircFreqSel::Mhz180;\n    fcfg.power = PoweredClock::NormalEnabledDeepSleepDisabled;\n    fcfg.fro_hf_enabled = true;\n    fcfg.clk_hf_fundamental_enabled = false;\n    fcfg.fro_hf_div = Some(const { Div8::from_divisor(180).unwrap() });\n    cfg.clock_cfg.firc = Some(fcfg);\n\n    // Enable 12M osc to use as ostimer clock\n    cfg.clock_cfg.sirc.fro_12m_enabled = true;\n    cfg.clock_cfg.sirc.fro_lf_div = None;\n    cfg.clock_cfg.sirc.power = PoweredClock::AlwaysEnabled;\n\n    // Disable 16K osc\n    cfg.clock_cfg.fro16k = None;\n\n    // Disable external osc\n    cfg.clock_cfg.sosc = None;\n\n    // Disable PLL\n    cfg.clock_cfg.spll = None;\n\n    // Feed core from 180M osc\n    cfg.clock_cfg.main_clock = MainClockConfig {\n        source: MainClockSource::FircHfRoot,\n        power: PoweredClock::NormalEnabledDeepSleepDisabled,\n        ahb_clk_div: Div8::no_div(),\n    };\n\n    // Set the core in high power active mode\n    cfg.clock_cfg.vdd_power.active_mode.level = VddLevel::OverDriveMode;\n    cfg.clock_cfg.vdd_power.active_mode.drive = VddDriveStrength::Normal;\n    // Set the core in low power sleep mode\n    cfg.clock_cfg.vdd_power.low_power_mode.level = VddLevel::MidDriveMode;\n    cfg.clock_cfg.vdd_power.low_power_mode.drive = VddDriveStrength::Low { enable_bandgap: false };\n\n    // Set \"deep sleep\" mode\n    cfg.clock_cfg.vdd_power.core_sleep = CoreSleep::DeepSleep;\n\n    // Set flash doze, allowing internal flash clocks to be gated on sleep\n    cfg.clock_cfg.vdd_power.flash_sleep = FlashSleep::FlashDoze;\n\n    let p = hal::init(cfg);\n\n    #[cfg(feature = \"executor-platform\")]\n    embassy_mcxa::executor::set_executor_debug_gpio(p.P1_12);\n\n    let mut pin = p.P4_2;\n    let mut clkout = p.CLKOUT;\n    const K250_CONFIG: clkout::Config = clkout::Config {\n        // 180MHz / 180 -> 1MHz\n        sel: ClockOutSel::FroHfDiv,\n        // 1MHz / 4 -> 250kHz\n        div: const { Div4::from_divisor(4).unwrap() },\n        level: PoweredClock::NormalEnabledDeepSleepDisabled,\n    };\n\n    // We create a clkout peripheral so that we can view the activity of the clock\n    // in a logic analyzer. We use `new_unchecked` here, which doesn't participate\n    // in verifying that the clock sources are always valid, and does not take\n    // a WaitGuard token, which would prevent us from entering deep sleep.\n    let _clock_out = unsafe { ClockOut::new_unchecked(clkout.reborrow(), pin.reborrow(), K250_CONFIG) };\n\n    defmt::info!(\"Going to sleep shortly...\");\n    cortex_m::asm::delay(45_000_000 / 4);\n\n    let mut red = Output::new(p.P3_18, Level::High, DriveStrength::Normal, SlewRate::Slow);\n\n    // Setup a second LED, and use the button labeled \"WAKEUP\" as an input source\n    let blue = Output::new(p.P3_21, Level::High, DriveStrength::Normal, SlewRate::Slow);\n    let btn = Input::new_async(p.P1_7, Irqs, Pull::Up);\n    spawner.spawn(press_toggler(btn, blue).unwrap());\n\n    loop {\n        // We sleep a little longer than usual, to make it easier to distinguish between\n        // timer wakeups and GPIO wakeups.\n        Timer::after_millis(4900).await;\n\n        // For the 100ms the LED is low, we manually take a wakeguard to prevent the\n        // system from returning to deep sleep, which drastically increases our power\n        // usage, but also prevents these clock sources from being disabled automatically.\n        red.set_low();\n        let _wg = WakeGuard::new();\n        let start = Instant::now();\n\n        // for the first 20ms, busyloop\n        while start.elapsed() < Duration::from_millis(20) {}\n\n        // then wfe sleep for 80ms\n        Timer::after_millis(80).await;\n\n        red.set_high();\n        // The WakeGuard is dropped here before returning to the top of the loop. When this\n        // happens, we will enter deep sleep automatically on our next .await.\n    }\n}\n\n/// A task that toggles the given LED every time the button falls low. No fancy\n/// debouncing, but useful to look at with the scope and the custom executor\n/// debug pin (or a power analyzer) to measure the time delta between the\n/// WAKEUP pin going low and the executor resuming from deep sleep.\n///\n/// At the time of writing, it takes us roughly 20us from the GPIO falling to\n/// the assertion of the executor debug gpio pin. This button can be observed\n/// using the mikro-bus header pin labeled \"RST\".\n#[embassy_executor::task]\nasync fn press_toggler(mut button: Input<'static, Async>, mut led: Output<'static>) {\n    loop {\n        button.wait_for_low().await;\n        led.toggle();\n        button.wait_for_high().await;\n    }\n}\n"
  },
  {
    "path": "examples/mcxa2xx/src/bin/power-deepsleep.rs",
    "content": "//! This example roughly emulates the `IDD_DEEP_SLEEP_MD_2` scenario from the datasheet.\n//!\n//! This example needs to be run with:\n//!\n//! ```sh\n//! cargo run --release --no-default-features --features=executor-platform --bin power-deepsleep\n//! ```\n//!\n//! As written, this achieves 153uA average current when measured with a Nordic PPK2.\n//!\n//! **NOTE: This requires rework of the board! You must remove R26 (used for the on\n//! board op-amp), remove R52, and bodge the pad of R52 that is closest to R61 to TP9\n//! (VDD_MCU_LINK). Without these reworks, you will see much higher current consumption.**\n//!\n//! As of 2026-02-04, UM12439 ONLY mentions the R52 errata, but the removal of R26 (as\n//! described in AN14765 for the MCXA346) is also necessary for the FRDM-MCXA266.\n\n#![no_std]\n#![no_main]\n\nuse embassy_executor::Spawner;\nuse embassy_mcxa::clocks::PoweredClock;\nuse embassy_mcxa::clocks::config::{\n    CoreSleep, Div8, FlashSleep, MainClockConfig, MainClockSource, VddDriveStrength, VddLevel,\n};\nuse embassy_time::Timer;\nuse hal::gpio::{DriveStrength, Level, Output, SlewRate};\nuse {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _};\n\n#[cfg_attr(\n    feature = \"executor-platform\",\n    embassy_executor::main(executor = \"embassy_mcxa::executor::Executor\", entry = \"cortex_m_rt::entry\")\n)]\n#[cfg_attr(not(feature = \"executor-platform\"), embassy_executor::main)]\nasync fn main(_spawner: Spawner) {\n    // Do a short delay in order to allow for us to attach the debugger/start\n    // a flash in case some setting below is wrong, and the CPU gets stuck\n    // in deep sleep with debugging disabled.\n    defmt::info!(\"Pre-power delay!\");\n    // Experimentally: about 5-6s or so.\n    cortex_m::asm::delay(45_000_000);\n    defmt::info!(\"Pre-power delay complete!\");\n    let mut cfg = hal::config::Config::default();\n\n    // Disable 45M osc\n    cfg.clock_cfg.firc = None;\n\n    // Enable 12M osc to use as core clock\n    cfg.clock_cfg.sirc.fro_12m_enabled = true;\n    cfg.clock_cfg.sirc.fro_lf_div = Some(Div8::no_div());\n    cfg.clock_cfg.sirc.power = PoweredClock::AlwaysEnabled;\n\n    // Disable 16K osc\n    cfg.clock_cfg.fro16k = None;\n\n    // Disable external osc\n    cfg.clock_cfg.sosc = None;\n\n    // Disable PLL\n    cfg.clock_cfg.spll = None;\n\n    // Feed core from 12M osc\n    cfg.clock_cfg.main_clock = MainClockConfig {\n        source: MainClockSource::SircFro12M,\n        power: PoweredClock::AlwaysEnabled,\n        ahb_clk_div: Div8::no_div(),\n    };\n\n    // Set lowest core power, disable bandgap LDO reference\n    cfg.clock_cfg.vdd_power.active_mode.level = VddLevel::MidDriveMode;\n    cfg.clock_cfg.vdd_power.low_power_mode.level = VddLevel::MidDriveMode;\n    cfg.clock_cfg.vdd_power.active_mode.drive = VddDriveStrength::Low { enable_bandgap: false };\n    cfg.clock_cfg.vdd_power.low_power_mode.drive = VddDriveStrength::Low { enable_bandgap: false };\n\n    // Set \"deep sleep\" mode\n    cfg.clock_cfg.vdd_power.core_sleep = CoreSleep::DeepSleep;\n\n    // Set flash doze, allowing internal flash clocks to be gated on sleep\n    cfg.clock_cfg.vdd_power.flash_sleep = FlashSleep::FlashDoze;\n\n    let p = hal::init(cfg);\n\n    defmt::info!(\"Going to sleep shortly...\");\n    cortex_m::asm::delay(45_000_000 / 4);\n\n    let mut red = Output::new(p.P3_18, Level::High, DriveStrength::Normal, SlewRate::Slow);\n    loop {\n        Timer::after_millis(900).await;\n        red.set_low();\n        Timer::after_millis(100).await;\n        red.set_high();\n    }\n}\n"
  },
  {
    "path": "examples/mcxa2xx/src/bin/power-wfe-gated.rs",
    "content": "//! This example roughly emulates the `IDD_SLEEP_MD_3` scenario from the datasheet.\n//!\n//! As written, this achieves 579uA average current when measured with a Nordic PPK2.\n//!\n//! **NOTE: This requires rework of the board! You must remove R26 (used for the on\n//! board op-amp), remove R52, and bodge the pad of R52 that is closest to R61 to TP9\n//! (VDD_MCU_LINK). Without these reworks, you will see much higher current consumption.**\n//!\n//! As of 2026-02-04, UM12439 ONLY mentions the R52 errata, but the removal of R26 (as\n//! described in AN14765 for the MCXA346) is also necessary for the FRDM-MCXA266.\n\n#![no_std]\n#![no_main]\n\nuse embassy_executor::Spawner;\nuse embassy_mcxa::clocks::PoweredClock;\nuse embassy_mcxa::clocks::config::{\n    CoreSleep, Div8, FlashSleep, MainClockConfig, MainClockSource, VddDriveStrength, VddLevel,\n};\nuse embassy_time::Timer;\nuse hal::gpio::{DriveStrength, Level, Output, SlewRate};\nuse {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _};\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    // Do a short delay in order to allow for us to attach the debugger/start\n    // a flash in case some setting below is wrong, and the CPU gets stuck\n    // in deep sleep with debugging disabled.\n    defmt::info!(\"Pre-power delay!\");\n    // Experimentally: about 5-6s or so.\n    cortex_m::asm::delay(45_000_000 * 2);\n    defmt::info!(\"Pre-power delay complete!\");\n    let mut cfg = hal::config::Config::default();\n\n    // Disable 45M osc\n    cfg.clock_cfg.firc = None;\n\n    // Enable 12M osc to use as core clock\n    cfg.clock_cfg.sirc.fro_12m_enabled = true;\n    cfg.clock_cfg.sirc.fro_lf_div = None;\n\n    // Disable 16K osc\n    cfg.clock_cfg.fro16k = None;\n\n    // Disable external osc\n    cfg.clock_cfg.sosc = None;\n\n    // Disable PLL\n    cfg.clock_cfg.spll = None;\n\n    // Feed core from 12M osc\n    cfg.clock_cfg.main_clock = MainClockConfig {\n        source: MainClockSource::SircFro12M,\n        power: PoweredClock::NormalEnabledDeepSleepDisabled,\n        ahb_clk_div: Div8::no_div(),\n    };\n\n    // Set lowest core power, disable bandgap LDO reference\n    cfg.clock_cfg.vdd_power.active_mode.level = VddLevel::MidDriveMode;\n    cfg.clock_cfg.vdd_power.low_power_mode.level = VddLevel::MidDriveMode;\n    cfg.clock_cfg.vdd_power.active_mode.drive = VddDriveStrength::Low { enable_bandgap: false };\n    cfg.clock_cfg.vdd_power.low_power_mode.drive = VddDriveStrength::Low { enable_bandgap: false };\n\n    // Set \"gated\" core mode, allowing clock to the core to be gated on sleep\n    cfg.clock_cfg.vdd_power.core_sleep = CoreSleep::WfeGated;\n\n    // Set flash doze, allowing internal flash clocks to be gated on sleep\n    cfg.clock_cfg.vdd_power.flash_sleep = FlashSleep::FlashDoze;\n\n    let p = hal::init(cfg);\n\n    defmt::info!(\"Going to sleep shortly...\");\n    cortex_m::asm::delay(45_000_000 / 2);\n\n    let mut red = Output::new(p.P3_18, Level::High, DriveStrength::Normal, SlewRate::Slow);\n    loop {\n        Timer::after_millis(900).await;\n        red.set_low();\n        Timer::after_millis(100).await;\n        red.set_high();\n    }\n}\n"
  },
  {
    "path": "examples/mcxa2xx/src/bin/pwm.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse embassy_executor::Spawner;\nuse embassy_time::Timer;\nuse hal::clocks::config::Div8;\nuse hal::clocks::periph_helpers::CTimerClockSel;\nuse hal::config::Config;\nuse hal::ctimer::pwm::{SetDutyCycle, SinglePwm, TriplePwm};\nuse hal::ctimer::{self, CTimer};\nuse {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _};\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let mut config = Config::default();\n    config.clock_cfg.sirc.fro_lf_div = Div8::from_divisor(1);\n    config.clock_cfg.fro16k = None;\n\n    let mut p = hal::init(config);\n\n    defmt::info!(\"Pwm example\");\n\n    let mut config = ctimer::Config::default();\n    config.source = CTimerClockSel::Clk1M;\n    let led_ctimer = CTimer::new(p.CTIMER2.reborrow(), config).unwrap();\n    let mut pwm = TriplePwm::new(\n        led_ctimer,\n        p.CTIMER2_CH0,\n        p.CTIMER2_CH1,\n        p.CTIMER2_CH3,\n        p.CTIMER2_CH2,\n        p.P3_18,\n        p.P3_19,\n        p.P3_21,\n        Default::default(),\n    )\n    .unwrap();\n\n    let pin_ctimer = CTimer::new(p.CTIMER1.reborrow(), Default::default()).unwrap();\n    let mut pin_pwm = SinglePwm::new(pin_ctimer, p.CTIMER1_CH2, p.CTIMER1_CH0, p.P3_12, Default::default()).unwrap();\n\n    defmt::info!(\"Before split\");\n    for _ in 0..10 {\n        for duty in (0u8..=100).chain((0..=100).rev()) {\n            pin_pwm.set_duty_cycle_percent(duty).unwrap();\n            pwm.pwm0.set_duty_cycle_percent(duty).unwrap();\n            pwm.pwm1.set_duty_cycle_percent(100 - duty).unwrap();\n            pwm.pwm2.set_duty_cycle_percent(duty).unwrap();\n            Timer::after_millis(10).await;\n        }\n    }\n\n    let (mut red, mut green, mut blue) = pwm.split();\n\n    defmt::info!(\"After split\");\n\n    for _ in 0..10 {\n        for duty in (0u8..=100).chain((0..=100).rev()) {\n            pin_pwm.set_duty_cycle_percent(duty).unwrap();\n            red.set_duty_cycle_percent(duty).unwrap();\n            green.set_duty_cycle_percent(100 - duty).unwrap();\n            blue.set_duty_cycle_percent(duty).unwrap();\n            Timer::after_millis(10).await;\n        }\n    }\n}\n"
  },
  {
    "path": "examples/mcxa2xx/src/bin/reset-reason.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse embassy_executor::Spawner;\nuse hal::config::Config;\nuse hal::reset_reason::reset_reason;\nuse {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _};\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let config = Config::default();\n    let _p = hal::init(config);\n\n    for reason in reset_reason().into_iter() {\n        defmt::info!(\"Reset Reason: '{}'\", reason);\n    }\n}\n"
  },
  {
    "path": "examples/mcxa2xx/src/bin/rtc_alarm.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse embassy_executor::Spawner;\nuse embassy_mcxa::bind_interrupts;\nuse hal::peripherals::RTC0;\nuse hal::rtc::{DateTime, InterruptHandler, Rtc};\nuse {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _};\n\nbind_interrupts!(struct Irqs {\n    RTC => InterruptHandler<RTC0>;\n});\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let p = hal::init(hal::config::Config::default());\n\n    defmt::info!(\"=== RTC Alarm Example ===\");\n\n    let mut rtc = Rtc::new(p.RTC0, Irqs, Default::default());\n\n    let now = DateTime {\n        year: 2025,\n        month: 10,\n        day: 15,\n        hour: 14,\n        minute: 30,\n        second: 0,\n    };\n\n    rtc.stop();\n\n    defmt::info!(\"Time set to: 2025-10-15 14:30:00\");\n    rtc.set_datetime(now);\n\n    let mut alarm = now;\n    alarm.second += 10;\n\n    defmt::info!(\"Alarm set for: 2025-10-15 14:30:10 (+10 seconds)\");\n    defmt::info!(\"RTC started, waiting for alarm...\");\n\n    rtc.wait_for_alarm(alarm).await;\n    defmt::info!(\"*** ALARM TRIGGERED! ***\");\n\n    defmt::info!(\"Example complete - Test PASSED!\");\n}\n"
  },
  {
    "path": "examples/mcxa2xx/src/bin/spi-async.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse defmt::info;\nuse embassy_executor::Spawner;\nuse embassy_time::Timer;\nuse hal::bind_interrupts;\nuse hal::clocks::config::Div8;\nuse hal::config::Config;\nuse hal::peripherals::LPSPI1;\nuse hal::spi::controller::{self, InterruptHandler, Spi};\nuse {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _};\n\nbind_interrupts!(\n    struct Irqs {\n        LPSPI1 => InterruptHandler<LPSPI1>;\n    }\n);\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let mut config = Config::default();\n    config.clock_cfg.sirc.fro_lf_div = Div8::from_divisor(1);\n\n    let p = hal::init(config);\n\n    info!(\"SPI example\");\n\n    let mut config = controller::Config::default();\n    config.frequency = 1_000_000;\n    let mut spi = Spi::new_async(p.LPSPI1, p.P3_10, p.P3_8, p.P3_9, Irqs, config).unwrap();\n\n    let mut rx_buf = [0u8; 32];\n    let tx_buf = [0x55u8; 32];\n\n    loop {\n        spi.async_transfer(&mut rx_buf, &tx_buf).await.unwrap();\n        assert!(rx_buf.iter().all(|b| *b == 0x55));\n        Timer::after_secs(1).await;\n    }\n}\n"
  },
  {
    "path": "examples/mcxa2xx/src/bin/spi-blocking.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse defmt::info;\nuse embassy_executor::Spawner;\nuse embassy_time::Timer;\nuse hal::clocks::config::Div8;\nuse hal::config::Config;\nuse hal::spi::controller::{self, Spi};\nuse {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _};\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let mut config = Config::default();\n    config.clock_cfg.sirc.fro_lf_div = Div8::from_divisor(1);\n\n    let p = hal::init(config);\n\n    info!(\"SPI example\");\n\n    let mut config = controller::Config::default();\n    config.frequency = 1_000_000;\n    let mut spi = Spi::new_blocking(p.LPSPI1, p.P3_10, p.P3_8, p.P3_9, config).unwrap();\n\n    let mut rx_buf = [0u8; 32];\n    let tx_buf = [0x55u8; 32];\n\n    loop {\n        spi.blocking_transfer(&mut rx_buf, &tx_buf).unwrap();\n        assert!(rx_buf.iter().all(|b| *b == 0x55));\n        Timer::after_secs(1).await;\n    }\n}\n"
  },
  {
    "path": "examples/mcxa2xx/src/bin/spi-dma.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse defmt::info;\nuse embassy_executor::Spawner;\nuse embassy_time::Timer;\nuse hal::bind_interrupts;\nuse hal::clocks::config::Div8;\nuse hal::config::Config;\nuse hal::peripherals::LPSPI1;\nuse hal::spi::controller::{self, InterruptHandler, Spi};\nuse {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _};\n\nbind_interrupts!(\n    struct Irqs {\n        LPSPI1 => InterruptHandler<LPSPI1>;\n    }\n);\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let mut config = Config::default();\n    config.clock_cfg.sirc.fro_lf_div = Div8::from_divisor(1);\n\n    let p = hal::init(config);\n\n    info!(\"SPI example\");\n\n    let mut config = controller::Config::default();\n    config.frequency = 1_000_000;\n    let mut spi =\n        Spi::new_async_with_dma(p.LPSPI1, p.P3_10, p.P3_8, p.P3_9, p.DMA_CH0, p.DMA_CH1, Irqs, config).unwrap();\n\n    let mut rx_buf = [0u8; 32];\n    let tx_buf = [0xa5u8; 32];\n\n    loop {\n        // Same sized buffers\n        spi.async_transfer(&mut rx_buf, &tx_buf).await.unwrap();\n\n        // TX buffer larger than RX buffer\n        spi.async_transfer(&mut rx_buf[..31], &tx_buf).await.unwrap();\n\n        // RX buffer larger than TX buffer\n        spi.async_transfer(&mut rx_buf, &tx_buf[..31]).await.unwrap();\n\n        Timer::after_secs(1).await;\n    }\n}\n"
  },
  {
    "path": "examples/mcxa2xx/src/bin/trng.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse embassy_executor::Spawner;\nuse hal::bind_interrupts;\nuse hal::config::Config;\nuse hal::peripherals::TRNG0;\nuse hal::trng::{self, InterruptHandler, Trng};\nuse rand_core::RngCore;\nuse rand_core::block::BlockRngCore;\nuse {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _};\n\nbind_interrupts!(\n    struct Irqs {\n        TRNG0 => InterruptHandler<TRNG0>;\n    }\n);\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let config = Config::default();\n    let mut p = hal::init(config);\n\n    defmt::info!(\"TRNG example\");\n\n    let mut trng = Trng::new_blocking_128(p.TRNG0.reborrow());\n    let rand = trng.blocking_next_u32();\n    defmt::info!(\"128-bit {}\", rand);\n\n    drop(trng);\n\n    let mut trng = Trng::new_blocking_256(p.TRNG0.reborrow());\n    let rand = trng.blocking_next_u32();\n    defmt::info!(\"256-bit {}\", rand);\n\n    drop(trng);\n\n    let mut trng = Trng::new_blocking_512(p.TRNG0.reborrow());\n    let rand = trng.blocking_next_u32();\n    defmt::info!(\"512-bit {}\", rand);\n\n    drop(trng);\n\n    let config = trng::Config::default();\n    let mut trng = Trng::new_blocking_with_custom_config(p.TRNG0.reborrow(), config);\n\n    defmt::info!(\"========== BLOCKING ==========\");\n\n    defmt::info!(\"Generate 10 u32\");\n    for _ in 0..10 {\n        let rand = trng.blocking_next_u32();\n        defmt::info!(\"{}\", rand);\n    }\n\n    defmt::info!(\"Generate 10 u64\");\n    for _ in 0..10 {\n        let rand = trng.blocking_next_u64();\n        defmt::info!(\"{}\", rand);\n    }\n\n    let mut buf = [0_u8; 256];\n\n    defmt::info!(\"Generate 10 256-byte buffers\");\n    for _ in 0..10 {\n        trng.blocking_fill_bytes(&mut buf);\n        defmt::info!(\"{:02x}\", buf);\n    }\n\n    defmt::info!(\"RngCore\");\n\n    for _ in 0..10 {\n        defmt::info!(\"u32: {}\", trng.next_u32());\n        defmt::info!(\"u64: {}\", trng.next_u64());\n    }\n\n    defmt::info!(\"Generate full block\");\n\n    let mut block = [0_u32; 8];\n    trng.generate(&mut block);\n    defmt::info!(\"Block: {}\", block);\n\n    drop(trng);\n\n    defmt::info!(\"========== ASYNC ==========\");\n\n    let mut trng = Trng::new_with_custom_config(p.TRNG0.reborrow(), Irqs, config);\n\n    defmt::info!(\"Generate 10 u32\");\n    for _ in 0..10 {\n        let rand = trng.async_next_u32().await.unwrap();\n        defmt::info!(\"{}\", rand);\n    }\n\n    defmt::info!(\"Generate 10 u64\");\n    for _ in 0..10 {\n        let rand = trng.async_next_u64().await.unwrap();\n        defmt::info!(\"{}\", rand);\n    }\n\n    let mut buf = [0_u8; 256];\n\n    defmt::info!(\"Generate 10 256-byte buffers\");\n    for _ in 0..10 {\n        trng.async_fill_bytes(&mut buf).await.unwrap();\n        defmt::info!(\"{:02x}\", buf);\n    }\n\n    defmt::info!(\"RngCore\");\n\n    for _ in 0..10 {\n        defmt::info!(\"u32: {}\", trng.next_u32());\n        defmt::info!(\"u64: {}\", trng.next_u64());\n    }\n\n    defmt::info!(\"Generate full block\");\n\n    let mut block = [0_u32; 8];\n    trng.async_next_block(&mut block).await.unwrap();\n    defmt::info!(\"Block: {}\", block);\n}\n"
  },
  {
    "path": "examples/mcxa2xx/src/bin/wwdt_interrupt.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse embassy_executor::Spawner;\nuse embassy_time::{Duration, Timer};\nuse hal::bind_interrupts;\nuse hal::config::Config;\nuse hal::gpio::{DriveStrength, Level, Output, SlewRate};\nuse hal::peripherals::WWDT0;\nuse hal::wwdt::{InterruptHandler, Watchdog};\nuse {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _};\n\nbind_interrupts!(\n    struct Irqs {\n        WWDT0 => InterruptHandler<WWDT0>;\n    }\n);\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let config = Config::default();\n    let p = hal::init(config);\n\n    defmt::info!(\"Watchdog example\");\n\n    let wwdt_config = hal::wwdt::Config {\n        timeout: Duration::from_millis(1050),\n        warning: Some(Duration::from_micros(4000)),\n    };\n\n    let mut watchdog = Watchdog::new(p.WWDT0, Irqs, wwdt_config).unwrap();\n    let mut led = Output::new(p.P3_18, Level::High, DriveStrength::Normal, SlewRate::Fast);\n\n    // Set the LED high for 2 seconds so we know when we're about to start the watchdog\n    led.toggle();\n    Timer::after_secs(2).await;\n\n    // Set to watchdog to generate interrupt if it's not fed within 1.05 seconds, and start it.\n    // The warning interrupt will trigger 4ms before the timeout.\n    watchdog.start();\n    defmt::info!(\"Started the watchdog timer\");\n\n    // Blink once a second for 5 seconds, feed the watchdog timer once a second to avoid a reset\n    for _ in 1..=5 {\n        led.toggle();\n        Timer::after_millis(500).await;\n        led.toggle();\n        Timer::after_millis(500).await;\n        defmt::info!(\"Feeding watchdog\");\n        watchdog.feed();\n    }\n\n    defmt::info!(\"Stopped feeding, watchdog interrupt will be triggered in 1 second\");\n    // Blink 10 times per second, not feeding the watchdog.\n    // Watchdog timer will trigger after 1.0 second as warning is set to 50ms.\n    loop {\n        led.toggle();\n        Timer::after_millis(100).await;\n        led.toggle();\n        Timer::after_millis(100).await;\n    }\n}\n"
  },
  {
    "path": "examples/mcxa2xx/src/bin/wwdt_reset.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse embassy_executor::Spawner;\nuse embassy_time::{Duration, Timer};\nuse hal::bind_interrupts;\nuse hal::config::Config;\nuse hal::gpio::{DriveStrength, Level, Output, SlewRate};\nuse hal::peripherals::WWDT0;\nuse hal::wwdt::{InterruptHandler, Watchdog};\nuse {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _};\n\nbind_interrupts!(\n    struct Irqs {\n        WWDT0 => InterruptHandler<WWDT0>;\n    }\n);\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let config = Config::default();\n    let p = hal::init(config);\n\n    defmt::info!(\"Watchdog example\");\n\n    let wwdt_config = hal::wwdt::Config {\n        timeout: Duration::from_millis(4000),\n        warning: None,\n    };\n\n    let mut watchdog = Watchdog::new(p.WWDT0, Irqs, wwdt_config).unwrap();\n    let mut led = Output::new(p.P3_18, Level::High, DriveStrength::Normal, SlewRate::Fast);\n\n    // Set the LED high for 2 seconds so we know when we're about to start the watchdog\n    led.toggle();\n    Timer::after_secs(2).await;\n\n    // Set to watchdog to reset if it's not fed within 4 seconds, and start it\n    watchdog.start();\n    defmt::info!(\"Started the watchdog timer\");\n\n    // Blink once a second for 5 seconds, feed the watchdog timer once a second to avoid a reset\n    for _ in 1..=5 {\n        led.toggle();\n        Timer::after_millis(500).await;\n        led.toggle();\n        Timer::after_millis(500).await;\n        defmt::info!(\"Feeding watchdog\");\n        watchdog.feed();\n    }\n\n    defmt::info!(\"Stopped feeding, device will reset in 4 seconds\");\n    // Blink 10 times per second, not feeding the watchdog.\n    // The processor should reset in 4 seconds.\n    loop {\n        led.toggle();\n        Timer::after_millis(100).await;\n        led.toggle();\n        Timer::after_millis(100).await;\n    }\n}\n"
  },
  {
    "path": "examples/mcxa5xx/.cargo/config.toml",
    "content": "[target.thumbv8m.main-none-eabihf]\nrunner = 'probe-rs run --chip MCXA577 --preverify --verify --protocol swd --speed 12000'\n\nrustflags = [\n  \"-C\", \"linker=flip-link\",\n  \"-C\", \"link-arg=-Tlink.x\",\n  \"-C\", \"link-arg=-Tdefmt.x\",\n  # This is needed if your flash or ram addresses are not aligned to 0x10000 in memory.x\n  # See https://github.com/rust-embedded/cortex-m-quickstart/pull/95\n  \"-C\", \"link-arg=--nmagic\",\n]\n\n[build]\ntarget = \"thumbv8m.main-none-eabihf\" # Cortex-M33\n\n[env]\nDEFMT_LOG = \"trace\"\n"
  },
  {
    "path": "examples/mcxa5xx/.gitignore",
    "content": "target/\n"
  },
  {
    "path": "examples/mcxa5xx/Cargo.toml",
    "content": "[package]\nname = \"embassy-mcxa5xx-examples\"\nversion = \"0.1.0\"\nedition = \"2024\"\nlicense = \"MIT OR Apache-2.0\"\npublish = false\n\n[dependencies]\ncortex-m = { version = \"0.7\", features = [\"critical-section-single-core\"] }\ncortex-m-rt = { version = \"0.7\", features = [\"set-sp\", \"set-vtor\"] }\ncrc = \"3.4.0\"\ncritical-section = \"1.2.0\"\ndefmt = \"1.0\"\ndefmt-rtt = \"1.0\"\nembassy-embedded-hal = { version = \"0.6.0\", path = \"../../embassy-embedded-hal\" }\nembassy-executor = { version = \"0.10.0\", path = \"../../embassy-executor\", default-features = false }\nembassy-mcxa = { version = \"0.1.0\", path = \"../../embassy-mcxa\", features = [\"defmt\", \"unstable-pac\", \"mcxa5xx\"] }\nembassy-sync = { version = \"0.8.0\", path = \"../../embassy-sync\" }\nembassy-time = { version = \"0.5.1\", path = \"../../embassy-time\", features = [\"defmt\", \"defmt-timestamp-uptime\"] }\nembassy-time-driver = { version = \"0.2.2\", path = \"../../embassy-time-driver\", optional = true }\nembedded-io-async = \"0.7.0\"\nembedded-storage = \"0.3.1\"\nheapless = \"0.9.2\"\npanic-probe = { version = \"1.0\", features = [\"print-defmt\"] }\nrand_core = \"0.9\"\nstatic_cell = \"2.1.1\"\ntmp108 = \"0.4.0\"\n\n[features]\ndefault = [\"default-executor\"]\ndefault-executor = [\n  \"embassy-executor/platform-cortex-m\",\n  \"embassy-executor/executor-interrupt\",\n  \"embassy-executor/executor-thread\",\n]\nexecutor-platform = [\n  \"embassy-mcxa/executor-platform\"\n]\nmcxa5xx = []\nunstable-pac = []\n\n[profile.release]\nlto = true # better optimizations\ndebug = 2  # enough information for defmt/rtt locations\n\n[package.metadata.embassy]\nbuild = [\n  { target = \"thumbv8m.main-none-eabihf\", artifact-dir = \"out/examples/mcxa5xx\" }\n]\n"
  },
  {
    "path": "examples/mcxa5xx/build.rs",
    "content": "use std::env;\nuse std::fs::File;\nuse std::io::Write;\nuse std::path::PathBuf;\n\nfn main() {\n    let out = &PathBuf::from(env::var_os(\"OUT_DIR\").unwrap());\n\n    // Generate memory.x - put \"FLASH\" at start of RAM, RAM after \"FLASH\"\n    // cortex-m-rt expects FLASH for code, RAM for data/bss/stack\n    // Both are in RAM, but separated to satisfy cortex-m-rt's expectations\n    // MCXA256 has 128KB RAM total\n    File::create(out.join(\"memory.x\"))\n        .unwrap()\n        .write_all(include_bytes!(\"memory.x\"))\n        .unwrap();\n\n    println!(\"cargo:rustc-link-search={}\", out.display());\n    println!(\"cargo:rerun-if-changed=memory.x\");\n}\n"
  },
  {
    "path": "examples/mcxa5xx/memory.x",
    "content": "MEMORY\n{\n    FLASH : ORIGIN = 0x00000000, LENGTH = 2M\n    RAM   : ORIGIN = 0x20000000, LENGTH = 640K\n}\n"
  },
  {
    "path": "examples/mcxa5xx/src/bin/adc-async.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse embassy_executor::Spawner;\nuse embassy_mcxa::adc::{Command, CommandConfig, CommandId, Trigger};\nuse embassy_mcxa::{bind_interrupts, peripherals};\nuse embassy_time::{Duration, Ticker};\nuse hal::adc::{self, Adc};\nuse hal::clocks::config::Div8;\nuse hal::config::Config;\nuse {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _};\n\nbind_interrupts!(struct Irqs {\n    ADC1 => adc::InterruptHandler<peripherals::ADC1>;\n});\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let mut config = Config::default();\n    config.clock_cfg.sirc.fro_lf_div = Div8::from_divisor(1);\n\n    let p = hal::init(config);\n\n    defmt::info!(\"=== ADC async Example ===\");\n\n    let commands = &[\n        Command::new_single(\n            p.P1_14,\n            CommandConfig {\n                chained_command: Some(CommandId::Cmd2), // Command 2 is executed after this command is done\n                ..Default::default()\n            },\n        ),\n        Command::new_looping(\n            p.P1_15,\n            3, // Command is run 3 times\n            CommandConfig {\n                chained_command: None, // Terminate the conversion after command is done\n                ..Default::default()\n            },\n        )\n        .unwrap(),\n    ];\n\n    let mut adc = Adc::new_async(\n        p.ADC1,\n        Irqs,\n        commands,\n        &[Trigger {\n            target_command_id: CommandId::Cmd1,\n            enable_hardware_trigger: false,\n            ..Default::default()\n        }],\n        adc::Config::default(),\n    )\n    .unwrap();\n\n    adc.do_offset_calibration();\n    adc.do_auto_calibration();\n\n    defmt::info!(\"=== ADC configuration done... ===\");\n    let mut tick = Ticker::every(Duration::from_millis(1000));\n\n    loop {\n        tick.next().await;\n        adc.do_software_trigger(0b0001).unwrap();\n\n        while let Some(res) = adc.wait_get_conversion().await {\n            defmt::info!(\"ADC result: {}\", res);\n        }\n    }\n}\n"
  },
  {
    "path": "examples/mcxa5xx/src/bin/adc-blocking.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse embassy_executor::Spawner;\nuse embassy_mcxa::adc::{Command, CommandConfig, CommandId, Trigger};\nuse embassy_time::{Duration, Ticker};\nuse hal::adc::{self, Adc};\nuse hal::clocks::config::Div8;\nuse hal::config::Config;\nuse {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _};\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let mut config = Config::default();\n    config.clock_cfg.sirc.fro_lf_div = Div8::from_divisor(1);\n\n    let p = hal::init(config);\n\n    defmt::info!(\"=== ADC polling Example ===\");\n\n    let commands = &[\n        Command::new_single(\n            p.P1_14,\n            CommandConfig {\n                chained_command: Some(CommandId::Cmd2), // Command 2 is executed after this command is done\n                ..Default::default()\n            },\n        ),\n        Command::new_looping(\n            p.P1_15,\n            3, // Command is run 3 times\n            CommandConfig {\n                chained_command: None, // Terminate the conversion after command is done\n                ..Default::default()\n            },\n        )\n        .unwrap(),\n    ];\n\n    let mut adc = Adc::new_blocking(\n        p.ADC1,\n        commands,\n        &[Trigger {\n            target_command_id: CommandId::Cmd1,\n            enable_hardware_trigger: false,\n            ..Default::default()\n        }],\n        adc::Config::default(),\n    )\n    .unwrap();\n\n    adc.do_offset_calibration();\n    adc.do_auto_calibration();\n\n    defmt::info!(\"=== ADC configuration done... ===\");\n    let mut tick = Ticker::every(Duration::from_millis(1000));\n\n    loop {\n        tick.next().await;\n        adc.do_software_trigger(0b0001).unwrap();\n\n        loop {\n            match adc.try_get_conversion() {\n                Ok(res) => {\n                    defmt::info!(\"ADC result: {}\", res);\n                }\n                Err(adc::Error::FifoPending) => {\n                    // Conversion not ready, continue polling\n                }\n                Err(_) => {\n                    // We're done\n                    break;\n                }\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "examples/mcxa5xx/src/bin/adc-temperature.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse embassy_executor::Spawner;\nuse embassy_mcxa::adc::{AnyAdcPin, Command, CommandConfig, CommandId, Trigger};\nuse embassy_mcxa::pac::adc::vals::{Avgs, Mode, Sts};\nuse embassy_mcxa::{bind_interrupts, peripherals};\nuse embassy_time::{Duration, Ticker};\nuse hal::adc::{self, Adc};\nuse hal::clocks::config::Div8;\nuse hal::config::Config;\nuse {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _};\n\nbind_interrupts!(struct Irqs {\n    ADC0 => adc::InterruptHandler<peripherals::ADC0>;\n});\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let mut config = Config::default();\n    config.clock_cfg.sirc.fro_lf_div = Div8::from_divisor(1);\n\n    let p = hal::init(config);\n\n    defmt::info!(\"=== ADC temperature Example ===\");\n\n    let commands = &[Command::new_looping(\n        AnyAdcPin::temperature(), // Use the temperature channel\n        2,\n        CommandConfig {\n            chained_command: None,\n            averaging: Avgs::AVERAGE_1024,  // Max average\n            sample_time: Sts::SAMPLE_131P5, // Max sample time\n            compare: adc::Compare::Disabled,\n            resolution: Mode::DATA_16_BITS,\n            wait_for_trigger: false,\n        },\n    )\n    .unwrap()];\n\n    let mut adc = Adc::new_async(\n        p.ADC0,\n        Irqs,\n        commands,\n        &[Trigger {\n            target_command_id: CommandId::Cmd1,\n            ..Default::default()\n        }],\n        adc::Config::default(),\n    )\n    .unwrap();\n\n    adc.do_offset_calibration();\n    adc.do_auto_calibration();\n\n    defmt::info!(\"=== ADC configuration done... ===\");\n    let mut tick = Ticker::every(Duration::from_millis(1000));\n\n    loop {\n        tick.next().await;\n        adc.do_software_trigger(0b0001).unwrap();\n        let conversion1 = adc.wait_get_conversion().await.unwrap();\n        let conversion2 = adc.wait_get_conversion().await.unwrap();\n\n        let celsius = calculate_temperature(conversion1.conv_value, conversion2.conv_value);\n        defmt::info!(\"Current temperature: {=f32}\", celsius);\n    }\n}\n\nfn calculate_temperature(conv1: u16, conv2: u16) -> f32 {\n    // Constants from the datasheet. May differ per device/family\n    const A: f32 = 738.0;\n    const B: f32 = 287.5;\n    const ALPHA: f32 = 10.06;\n\n    A * (ALPHA * (conv2 as f32 - conv1 as f32) / (conv2 as f32 + (ALPHA * (conv2 as f32 - conv1 as f32)))) - B\n}\n"
  },
  {
    "path": "examples/mcxa5xx/src/bin/blinky-firc.rs",
    "content": "//! Similar to blinky, but clocked with FIRC\n//!\n//! This will probably go away once we have the CLKOUT peripheral supported.\n\n#![no_std]\n#![no_main]\n\nuse embassy_executor::Spawner;\nuse embassy_mcxa::clocks::config::{FircConfig, FircFreqSel, MainClockSource, VddDriveStrength};\nuse embassy_mcxa::clocks::{PoweredClock, VddLevel};\nuse embassy_time::Timer;\nuse hal::gpio::{DriveStrength, Level, Output, SlewRate};\nuse {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _};\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let mut cfg = hal::config::Config::default();\n\n    // Setup FIRC at 192MHz\n    let mut firc = FircConfig::default();\n    firc.frequency = FircFreqSel::Mhz192;\n    firc.power = PoweredClock::NormalEnabledDeepSleepDisabled;\n    cfg.clock_cfg.firc = Some(firc);\n\n    // Set CPU to OverDrive mode to allow for 192MHz cpu clock\n    cfg.clock_cfg.main_clock.source = MainClockSource::FircHfRoot;\n    cfg.clock_cfg.vdd_power.active_mode.level = VddLevel::OverDriveMode;\n    cfg.clock_cfg.vdd_power.active_mode.drive = VddDriveStrength::Normal;\n    cfg.clock_cfg.vdd_power.low_power_mode.level = VddLevel::OverDriveMode;\n    cfg.clock_cfg.vdd_power.low_power_mode.drive = VddDriveStrength::Normal;\n    let p = hal::init(cfg);\n\n    defmt::info!(\"Blink example\");\n\n    let mut red = Output::new(p.P2_14, Level::High, DriveStrength::Normal, SlewRate::Fast);\n    let mut green = Output::new(p.P2_22, Level::High, DriveStrength::Normal, SlewRate::Fast);\n    let mut blue = Output::new(p.P2_23, Level::High, DriveStrength::Normal, SlewRate::Fast);\n\n    loop {\n        defmt::info!(\"Toggle LEDs\");\n\n        red.toggle();\n        Timer::after_millis(250).await;\n\n        red.toggle();\n        green.toggle();\n        Timer::after_millis(250).await;\n\n        green.toggle();\n        blue.toggle();\n        Timer::after_millis(250).await;\n        blue.toggle();\n\n        Timer::after_millis(250).await;\n    }\n}\n"
  },
  {
    "path": "examples/mcxa5xx/src/bin/blinky-sosc.rs",
    "content": "//! Similar to blinky, but clocked with external SOSC\n//!\n//! This will probably go away once we have the CLKOUT peripheral supported.\n\n#![no_std]\n#![no_main]\n\nuse embassy_executor::Spawner;\nuse embassy_mcxa::clocks::PoweredClock;\nuse embassy_mcxa::clocks::config::{MainClockSource, SoscConfig, SoscMode};\nuse embassy_time::Timer;\nuse hal::gpio::{DriveStrength, Level, Output, SlewRate};\nuse {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _};\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let mut cfg = hal::config::Config::default();\n    let osc = SoscConfig {\n        mode: SoscMode::CrystalOscillator,\n        frequency: 24_000_000,\n        power: PoweredClock::NormalEnabledDeepSleepDisabled,\n    };\n    cfg.clock_cfg.sosc = Some(osc);\n    cfg.clock_cfg.main_clock.source = MainClockSource::SoscClkIn;\n    let p = hal::init(cfg);\n\n    defmt::info!(\"Blink example\");\n\n    let mut red = Output::new(p.P2_14, Level::High, DriveStrength::Normal, SlewRate::Fast);\n    let mut green = Output::new(p.P2_22, Level::High, DriveStrength::Normal, SlewRate::Fast);\n    let mut blue = Output::new(p.P2_23, Level::High, DriveStrength::Normal, SlewRate::Fast);\n\n    loop {\n        defmt::info!(\"Toggle LEDs\");\n\n        red.toggle();\n        Timer::after_millis(250).await;\n\n        red.toggle();\n        green.toggle();\n        Timer::after_millis(250).await;\n\n        green.toggle();\n        blue.toggle();\n        Timer::after_millis(250).await;\n        blue.toggle();\n\n        Timer::after_millis(250).await;\n    }\n}\n"
  },
  {
    "path": "examples/mcxa5xx/src/bin/blinky-spll.rs",
    "content": "//! Similar to blinky, but clocked with external SOSC\n//!\n//! This will probably go away once we have the CLKOUT peripheral supported.\n\n#![no_std]\n#![no_main]\n\nuse embassy_executor::Spawner;\nuse embassy_mcxa::clocks::PoweredClock;\nuse embassy_mcxa::clocks::config::{MainClockSource, SpllConfig, SpllMode, SpllSource};\nuse embassy_time::Timer;\nuse hal::gpio::{DriveStrength, Level, Output, SlewRate};\nuse {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _};\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let mut cfg = hal::config::Config::default();\n    cfg.clock_cfg.spll = Some(SpllConfig {\n        source: SpllSource::Sirc,\n        // 12MHz\n        // 12 x 32 => 384MHz\n        // 384 / (16 x 2) => 12.0MHz\n        mode: SpllMode::Mode1b {\n            m_mult: 32,\n            p_div: 16,\n            bypass_p2_div: false,\n        },\n        power: PoweredClock::NormalEnabledDeepSleepDisabled,\n        pll1_clk_div: None,\n    });\n    cfg.clock_cfg.main_clock.source = MainClockSource::SPll1;\n    let p = hal::init(cfg);\n\n    defmt::info!(\"Blink example\");\n\n    let mut red = Output::new(p.P2_14, Level::High, DriveStrength::Normal, SlewRate::Fast);\n    let mut green = Output::new(p.P2_22, Level::High, DriveStrength::Normal, SlewRate::Fast);\n    let mut blue = Output::new(p.P2_23, Level::High, DriveStrength::Normal, SlewRate::Fast);\n\n    loop {\n        defmt::info!(\"Toggle LEDs\");\n\n        red.toggle();\n        Timer::after_millis(250).await;\n\n        red.toggle();\n        green.toggle();\n        Timer::after_millis(250).await;\n\n        green.toggle();\n        blue.toggle();\n        Timer::after_millis(250).await;\n        blue.toggle();\n\n        Timer::after_millis(250).await;\n    }\n}\n"
  },
  {
    "path": "examples/mcxa5xx/src/bin/blinky.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse embassy_executor::Spawner;\nuse embassy_time::Timer;\nuse hal::gpio::{DriveStrength, Level, Output, SlewRate};\nuse {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _};\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let p = hal::init(hal::config::Config::default());\n\n    defmt::info!(\"Blink example\");\n\n    let mut red = Output::new(p.P2_14, Level::High, DriveStrength::Normal, SlewRate::Fast);\n    let mut green = Output::new(p.P2_22, Level::High, DriveStrength::Normal, SlewRate::Fast);\n    let mut blue = Output::new(p.P2_23, Level::High, DriveStrength::Normal, SlewRate::Fast);\n\n    loop {\n        defmt::info!(\"Toggle LEDs\");\n\n        red.toggle();\n        Timer::after_millis(250).await;\n\n        red.toggle();\n        green.toggle();\n        Timer::after_millis(250).await;\n\n        green.toggle();\n        blue.toggle();\n        Timer::after_millis(250).await;\n        blue.toggle();\n\n        Timer::after_millis(250).await;\n    }\n}\n"
  },
  {
    "path": "examples/mcxa5xx/src/bin/button.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse embassy_executor::Spawner;\nuse embassy_time::Timer;\nuse hal::gpio::{Input, Pull};\nuse {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _};\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let p = hal::init(hal::config::Config::default());\n\n    defmt::info!(\"Button example\");\n\n    // This button is labeled \"WAKEUP\" on the FRDM-MCXA577\n    // The board already has a 10K pullup\n    let monitor = Input::new(p.P3_17, Pull::Disabled);\n\n    loop {\n        defmt::info!(\"Pin level is {:?}\", monitor.get_level());\n        Timer::after_millis(1000).await;\n    }\n}\n"
  },
  {
    "path": "examples/mcxa5xx/src/bin/button_async.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse embassy_executor::Spawner;\nuse embassy_mcxa::{bind_interrupts, gpio, peripherals};\nuse embassy_time::Timer;\nuse hal::gpio::{Input, Pull};\nuse {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _};\n\nbind_interrupts!(struct Irqs {\n    GPIO3 => gpio::InterruptHandler<peripherals::GPIO3>;\n});\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let p = hal::init(hal::config::Config::default());\n\n    defmt::info!(\"GPIO interrupt example\");\n\n    // This button is labeled \"WAKEUP\" on the FRDM-MCXA577\n    // The board already has a 10K pullup\n    let mut pin = Input::new_async(p.P3_17, Irqs, Pull::Disabled);\n\n    let mut press_count = 0u32;\n\n    loop {\n        pin.wait_for_falling_edge().await;\n\n        press_count += 1;\n\n        defmt::info!(\"Button pressed! Count: {}\", press_count);\n        Timer::after_millis(50).await;\n    }\n}\n"
  },
  {
    "path": "examples/mcxa5xx/src/bin/capture.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse embassy_executor::Spawner;\nuse embassy_time::Timer;\nuse hal::bind_interrupts;\nuse hal::clocks::config::Div8;\nuse hal::config::Config;\nuse hal::ctimer::CTimer;\nuse hal::ctimer::capture::{self, Capture, Edge, InterruptHandler};\nuse hal::gpio::{DriveStrength, Level, Output, SlewRate};\nuse hal::peripherals::CTIMER2;\nuse {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _};\n\nbind_interrupts!(struct Irqs {\n    CTIMER2 => InterruptHandler<CTIMER2>;\n});\n\n#[embassy_executor::main]\nasync fn main(spawner: Spawner) {\n    let mut config = Config::default();\n    config.clock_cfg.sirc.fro_lf_div = Div8::from_divisor(1);\n\n    let p = hal::init(config);\n\n    defmt::info!(\"Capture example\");\n\n    let pin = Output::new(p.P3_12, Level::Low, DriveStrength::Normal, SlewRate::Fast);\n\n    let ctimer = CTimer::new(p.CTIMER2, Default::default()).unwrap();\n    let mut config = capture::Config::default();\n    config.edge = Edge::RisingEdge;\n    let mut capture = Capture::new_with_input_pin(ctimer, p.CTIMER2_CH0, p.P3_14, Irqs, config).unwrap();\n\n    spawner.spawn(pin_task(pin).unwrap());\n\n    loop {\n        let one = capture.capture().await.unwrap();\n        let two = capture.capture().await.unwrap();\n        let diff = two - one;\n        defmt::info!(\n            \"{} s {} Hz\",\n            diff.to_period(capture.frequency()),\n            diff.to_frequency(capture.frequency())\n        );\n    }\n}\n\n#[embassy_executor::task]\nasync fn pin_task(mut pin: Output<'static>) {\n    Timer::after_secs(1).await;\n\n    loop {\n        pin.set_high();\n        pin.set_low();\n        Timer::after_secs(1).await;\n    }\n}\n"
  },
  {
    "path": "examples/mcxa5xx/src/bin/cdog.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse embassy_executor::Spawner;\nuse embassy_time::Timer;\nuse hal::bind_interrupts;\nuse hal::cdog::{FaultControl, InterruptHandler, LockControl, PauseControl, Watchdog};\nuse hal::config::Config;\nuse {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _};\n\nbind_interrupts!(\n    struct Irqs {\n        CDOG0 => InterruptHandler;\n    }\n);\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let config = Config::default();\n    let p = hal::init(config);\n\n    defmt::info!(\"** Code watchdog example **\");\n\n    let cdog_config = hal::cdog::Config {\n        timeout: FaultControl::EnableInterrupt,\n        miscompare: FaultControl::EnableInterrupt,\n        sequence: FaultControl::EnableInterrupt,\n        state: FaultControl::EnableInterrupt,\n        address: FaultControl::EnableInterrupt,\n        irq_pause: PauseControl::PauseTimer,\n        debug_halt: PauseControl::PauseTimer,\n        lock: LockControl::Unlocked,\n    };\n\n    let mut watchdog = Watchdog::new(p.CDOG0, Irqs, cdog_config).unwrap();\n\n    defmt::info!(\"Watchdog initialized\");\n\n    // First part of the example is to demonstrate how the secure counter feature of the cdog works.\n    watchdog.start(0xFFFFFF, 0);\n    watchdog.add(42);\n    watchdog.check(42);\n    watchdog.sub(2);\n    watchdog.check(40);\n    watchdog.start(0xFFFFFFFF, 0);\n    watchdog.check(0);\n    defmt::info!(\n        \"Next check should generate an interrupt as checked value (=1) is different than the secure counter (=0)\"\n    );\n    watchdog.check(1);\n\n    // Now demonstrating how the instruction timer feature of the cdog works.\n    defmt::info!(\"Start again the code watchdog to generate a timeout interrupt\");\n    watchdog.start(0xFFFFF, 0);\n    while watchdog.get_instruction_timer() != 0 {\n        defmt::info!(\"Instruction timer : {:08x}\", watchdog.get_instruction_timer());\n        Timer::after_millis(100).await;\n    }\n\n    defmt::info!(\"** End of example **\");\n}\n"
  },
  {
    "path": "examples/mcxa5xx/src/bin/clkout.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse embassy_executor::Spawner;\nuse embassy_mcxa::clkout::{ClockOut, ClockOutSel, Config, Div4};\nuse embassy_mcxa::clocks::PoweredClock;\nuse embassy_mcxa::clocks::config::{Div8, SoscConfig, SoscMode, SpllConfig, SpllMode, SpllSource};\nuse embassy_mcxa::gpio::{DriveStrength, Level, Output, SlewRate};\nuse embassy_time::Timer;\nuse {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _};\n\n/// Demonstrate CLKOUT, using Pin P4.2\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    defmt::info!(\"START\");\n    let mut cfg = hal::config::Config::default();\n    cfg.clock_cfg.sosc = Some(SoscConfig {\n        mode: SoscMode::CrystalOscillator,\n        frequency: 24_000_000,\n        power: PoweredClock::NormalEnabledDeepSleepDisabled,\n    });\n    cfg.clock_cfg.spll = Some(SpllConfig {\n        source: SpllSource::Sirc,\n        // 12MHz\n        // 12 x 32 => 384MHz\n        // 384 / (16 x 2) => 12.0MHz\n        mode: SpllMode::Mode1b {\n            m_mult: 32,\n            p_div: 16,\n            bypass_p2_div: false,\n        },\n        power: PoweredClock::NormalEnabledDeepSleepDisabled,\n        pll1_clk_div: Some(Div8::no_div()),\n    });\n    // TODO: Figure out OSC32K\n    // let mut osc = Osc32KConfig::default();\n    // osc.mode = Osc32KMode::HighPower {\n    //     coarse_amp_gain: Osc32KCoarseGain::EsrRange0,\n    //     xtal_cap_sel: Osc32KCapSel::Cap12PicoF,\n    //     extal_cap_sel: Osc32KCapSel::Cap12PicoF,\n    // };\n    // osc.vsys_domain_active = true;\n    // osc.vdd_core_domain_active = true;\n    // osc.vbat_domain_active = true;\n    // cfg.clock_cfg.osc32k = Some(osc);\n\n    defmt::info!(\"init...\");\n    let p = hal::init(cfg);\n    defmt::info!(\"inited\");\n\n    let mut pin = p.P4_2;\n    let mut clkout = p.CLKOUT;\n\n    // const K32_CONFIG: Config = Config {\n    //     sel: ClockOutSel::LpOsc,\n    //     div: Div4::no_div(),\n    //     level: PoweredClock::NormalEnabledDeepSleepDisabled,\n    // };\n    const M4_CONFIG: Config = Config {\n        sel: ClockOutSel::Fro12M,\n        div: const { Div4::from_divisor(3).unwrap() },\n        level: PoweredClock::NormalEnabledDeepSleepDisabled,\n    };\n    const M2_CONFIG: Config = Config {\n        sel: ClockOutSel::ClkIn,\n        div: const { Div4::from_divisor(12).unwrap() },\n        level: PoweredClock::NormalEnabledDeepSleepDisabled,\n    };\n    const M1_CONFIG: Config = Config {\n        sel: ClockOutSel::Pll1ClkDiv,\n        div: const { Div4::from_divisor(12).unwrap() },\n        level: PoweredClock::NormalEnabledDeepSleepDisabled,\n    };\n\n    #[rustfmt::skip]\n    let configs = [\n        // TODO: re-enable\n        // (\"32K -> /1 = 32K\",  K32_CONFIG), // no output\n        (\"12M -> /3 = 4M\",   M4_CONFIG), // good\n        (\"24M -> /12 = 2M\", M2_CONFIG), // good\n        (\"12M-> /12 = 1M\",   M1_CONFIG), // good\n    ];\n\n    loop {\n        defmt::info!(\"Set High...\");\n        let mut output = Output::new(pin.reborrow(), Level::High, DriveStrength::Normal, SlewRate::Slow);\n        Timer::after_millis(250).await;\n\n        defmt::info!(\"Set Low...\");\n        output.set_low();\n        Timer::after_millis(750).await;\n\n        drop(output);\n\n        for (name, conf) in configs.iter() {\n            defmt::info!(\"Running {=str}\", name);\n\n            let _clock_out = ClockOut::new(clkout.reborrow(), pin.reborrow(), *conf).unwrap();\n\n            Timer::after_millis(3000).await;\n\n            defmt::info!(\"Set Low...\");\n            drop(_clock_out);\n\n            let _output = Output::new(pin.reborrow(), Level::Low, DriveStrength::Normal, SlewRate::Slow);\n            Timer::after_millis(500).await;\n        }\n    }\n}\n"
  },
  {
    "path": "examples/mcxa5xx/src/bin/crc.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse embassy_executor::Spawner;\nuse hal::config::Config;\nuse hal::crc::Crc;\nuse {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _};\n\nconst CCITT_FALSE: crc::Algorithm<u16> = crc::Algorithm {\n    width: 16,\n    poly: 0x1021,\n    init: 0xffff,\n    refin: false,\n    refout: false,\n    xorout: 0,\n    check: 0x29b1,\n    residue: 0x0000,\n};\n\nconst POSIX: crc::Algorithm<u32> = crc::Algorithm {\n    width: 32,\n    poly: 0x04c1_1db7,\n    init: 0,\n    refin: false,\n    refout: false,\n    xorout: 0xffff_ffff,\n    check: 0x765e_7680,\n    residue: 0x0000,\n};\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let config = Config::default();\n    let mut p = hal::init(config);\n\n    defmt::info!(\"CRC example\");\n\n    let buf_u8 = [0x00u8, 0x11, 0x22, 0x33];\n    let buf_u16 = [0x0000u16, 0x1111, 0x2222, 0x3333];\n    let buf_u32 = [0x0000_0000u32, 0x1111_1111, 0x2222_2222, 0x3333_3333];\n\n    // CCITT False\n\n    let sw_crc = crc::Crc::<u16>::new(&CCITT_FALSE);\n    let mut digest = sw_crc.digest();\n    digest.update(&buf_u8);\n    let sw_sum = digest.finalize();\n\n    let mut crc = Crc::new_ccitt_false(p.CRC0.reborrow());\n    crc.feed(&buf_u8);\n    let sum = crc.finalize();\n    assert_eq!(sum, sw_sum);\n\n    let mut crc = Crc::new_ccitt_false(p.CRC0.reborrow());\n    crc.feed(&buf_u16);\n    let sum = crc.finalize();\n    assert_eq!(sum, 0xa467);\n\n    let mut crc = Crc::new_ccitt_false(p.CRC0.reborrow());\n    crc.feed(&buf_u32);\n    let sum = crc.finalize();\n    assert_eq!(sum, 0xe5c7);\n\n    // Maxim\n\n    let sw_crc = crc::Crc::<u16>::new(&crc::CRC_16_MAXIM_DOW);\n    let mut digest = sw_crc.digest();\n    digest.update(&buf_u8);\n    let sw_sum = digest.finalize();\n\n    let mut crc = Crc::new_maxim(p.CRC0.reborrow());\n    crc.feed(&buf_u8);\n    let sum = crc.finalize();\n    assert_eq!(sum, sw_sum);\n\n    let mut crc = Crc::new_maxim(p.CRC0.reborrow());\n    crc.feed(&buf_u16);\n    let sum = crc.finalize();\n    assert_eq!(sum, 0x2afe);\n\n    let mut crc = Crc::new_maxim(p.CRC0.reborrow());\n    crc.feed(&buf_u32);\n    let sum = crc.finalize();\n    assert_eq!(sum, 0x17d7);\n\n    // Kermit\n\n    let sw_crc = crc::Crc::<u16>::new(&crc::CRC_16_KERMIT);\n    let mut digest = sw_crc.digest();\n    digest.update(&buf_u8);\n    let sw_sum = digest.finalize();\n\n    let mut crc = Crc::new_kermit(p.CRC0.reborrow());\n    crc.feed(&buf_u8);\n    let sum = crc.finalize();\n    assert_eq!(sum, sw_sum);\n\n    let mut crc = Crc::new_kermit(p.CRC0.reborrow());\n    crc.feed(&buf_u16);\n    let sum = crc.finalize();\n    assert_eq!(sum, 0x66eb);\n\n    let mut crc = Crc::new_kermit(p.CRC0.reborrow());\n    crc.feed(&buf_u32);\n    let sum = crc.finalize();\n    assert_eq!(sum, 0x75ea);\n\n    // ISO HDLC\n\n    let sw_crc = crc::Crc::<u32>::new(&crc::CRC_32_ISO_HDLC);\n    let mut digest = sw_crc.digest();\n    digest.update(&buf_u8);\n    let sw_sum = digest.finalize();\n\n    let mut crc = Crc::new_iso_hdlc(p.CRC0.reborrow());\n    crc.feed(&buf_u8);\n    let sum = crc.finalize();\n    assert_eq!(sum, sw_sum);\n\n    let mut crc = Crc::new_iso_hdlc(p.CRC0.reborrow());\n    crc.feed(&buf_u16);\n    let sum = crc.finalize();\n    assert_eq!(sum, 0x8a61_4178);\n\n    let mut crc = Crc::new_iso_hdlc(p.CRC0.reborrow());\n    crc.feed(&buf_u32);\n    let sum = crc.finalize();\n    assert_eq!(sum, 0xfab5_d04e);\n\n    // POSIX\n\n    let sw_crc = crc::Crc::<u32>::new(&POSIX);\n    let mut digest = sw_crc.digest();\n    digest.update(&buf_u8);\n    let sw_sum = digest.finalize();\n\n    let mut crc = Crc::new_posix(p.CRC0.reborrow());\n    crc.feed(&buf_u8);\n    let sum = crc.finalize();\n\n    assert_eq!(sum, sw_sum);\n\n    let mut crc = Crc::new_posix(p.CRC0.reborrow());\n    crc.feed(&buf_u16);\n    let sum = crc.finalize();\n    assert_eq!(sum, 0x6d76_4f58);\n\n    let mut crc = Crc::new_posix(p.CRC0.reborrow());\n    crc.feed(&buf_u32);\n    let sum = crc.finalize();\n    assert_eq!(sum, 0x2a5b_cb90);\n\n    defmt::info!(\"CRC successful\");\n}\n"
  },
  {
    "path": "examples/mcxa5xx/src/bin/dma_mem_to_mem.rs",
    "content": "//! DMA memory-to-memory transfer example for MCXA276.\n//!\n//! This example demonstrates using DMA to copy data between memory buffers\n//! using the Embassy-style async API with type-safe transfers.\n//!\n//! # Embassy-style features demonstrated:\n//! - `TransferOptions` for configuration\n//! - Type-safe `mem_to_mem<u32>()` method with async `.await`\n//! - `Transfer` Future that can be `.await`ed\n//! - `Word` trait for automatic transfer width detection\n//! - `memset()` method for filling memory with a pattern\n\n#![no_std]\n#![no_main]\n\nuse embassy_executor::Spawner;\nuse embassy_mcxa::clocks::config::Div8;\nuse embassy_mcxa::dma::{DmaChannel, TransferOptions};\nuse static_cell::ConstStaticCell;\nuse {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _};\n\nconst BUFFER_LENGTH: usize = 4;\n\n// Buffers in RAM (static mut is automatically placed in .bss/.data)\nstatic SRC_BUFFER: ConstStaticCell<[u32; BUFFER_LENGTH]> = ConstStaticCell::new([1, 2, 3, 4]);\nstatic DEST_BUFFER: ConstStaticCell<[u32; BUFFER_LENGTH]> = ConstStaticCell::new([0; BUFFER_LENGTH]);\nstatic MEMSET_BUFFER: ConstStaticCell<[u32; BUFFER_LENGTH]> = ConstStaticCell::new([0; BUFFER_LENGTH]);\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    // Small delay to allow probe-rs to attach after reset\n    for _ in 0..100_000 {\n        cortex_m::asm::nop();\n    }\n\n    let mut cfg = hal::config::Config::default();\n    cfg.clock_cfg.sirc.fro_12m_enabled = true;\n    cfg.clock_cfg.sirc.fro_lf_div = Some(Div8::no_div());\n    let p = hal::init(cfg);\n\n    defmt::info!(\"DMA memory-to-memory example starting...\");\n\n    defmt::info!(\"EDMA memory to memory example begin.\");\n\n    let src = SRC_BUFFER.take();\n    let dst = DEST_BUFFER.take();\n    let mst = MEMSET_BUFFER.take();\n\n    defmt::info!(\"Source Buffer: {=[?]}\", src.as_slice());\n    defmt::info!(\"Destination Buffer (before): {=[?]}\", dst.as_slice());\n    defmt::info!(\"Configuring DMA with Embassy-style API...\");\n\n    // Create DMA channel\n    let mut dma_ch0 = DmaChannel::new(p.DMA_CH0);\n\n    // Configure transfer options (Embassy-style)\n    // TransferOptions defaults to: complete_transfer_interrupt = true\n    let options = TransferOptions::COMPLETE_INTERRUPT;\n\n    // =========================================================================\n    // Part 1: Embassy-style async API demonstration (mem_to_mem)\n    // =========================================================================\n    //\n    // Use the new type-safe `mem_to_mem<u32>()` method:\n    // - Automatically determines transfer width from buffer element type (u32)\n    // - Returns a `Transfer` future that can be `.await`ed\n    // - Uses TransferOptions for consistent configuration\n    //\n    // Using async `.await` - the executor can run other tasks while waiting!\n\n    // Perform type-safe memory-to-memory transfer using Embassy-style async API\n    // Using async `.await` - the executor can run other tasks while waiting!\n    let transfer = dma_ch0.mem_to_mem(src, dst, options).unwrap();\n    transfer.await.unwrap();\n\n    defmt::info!(\"DMA mem-to-mem transfer complete!\");\n    defmt::info!(\"Destination Buffer (after): {=[?]}\", dst.as_slice());\n\n    // Verify data\n    if src != dst {\n        defmt::error!(\"FAIL: mem_to_mem mismatch!\");\n    } else {\n        defmt::info!(\"PASS: mem_to_mem verified.\");\n    }\n\n    // =========================================================================\n    // Part 2: memset() demonstration\n    // =========================================================================\n    //\n    // The `memset()` method fills a buffer with a pattern value:\n    // - Fixed source address (pattern is read repeatedly)\n    // - Incrementing destination address\n    // - Uses the same Transfer future pattern\n\n    defmt::info!(\"--- Demonstrating memset() feature ---\");\n\n    defmt::info!(\"Memset Buffer (before): {=[?]}\", mst.as_slice());\n\n    // Fill buffer with a pattern value using DMA memset\n    let pattern: u32 = 0xDEADBEEF;\n    defmt::info!(\"Filling with pattern 0xDEADBEEF...\");\n\n    // Using blocking_wait() for demonstration - also shows non-async usage\n    let transfer = dma_ch0.memset(&pattern, mst, options).unwrap();\n    transfer.blocking_wait();\n\n    defmt::info!(\"DMA memset complete!\");\n    defmt::info!(\"Memset Buffer (after): {=[?]}\", mst.as_slice());\n\n    // Verify memset result\n    if !mst.iter().all(|&v| v == pattern) {\n        defmt::error!(\"FAIL: memset mismatch!\");\n    } else {\n        defmt::info!(\"PASS: memset verified.\");\n    }\n\n    defmt::info!(\"=== All DMA tests complete ===\");\n}\n"
  },
  {
    "path": "examples/mcxa5xx/src/bin/dma_scatter_gather_builder.rs",
    "content": "//! DMA Scatter-Gather Builder example for MCXA276.\n//!\n//! This example demonstrates using the new `ScatterGatherBuilder` API for\n//! chaining multiple DMA transfers with a type-safe builder pattern.\n//!\n//! # Features demonstrated:\n//! - `ScatterGatherBuilder::new()` for creating a builder\n//! - `add_transfer()` for adding memory-to-memory segments\n//! - `build()` to start the chained transfer\n//! - Automatic TCD linking and ESG bit management\n//!\n//! # Comparison with manual scatter-gather:\n//! The manual approach (see `dma_scatter_gather.rs`) requires:\n//! - Manual TCD pool allocation and alignment\n//! - Manual CSR/ESG/INTMAJOR bit manipulation\n//! - Manual dlast_sga address calculations\n//!\n//! The builder approach handles all of this automatically!\n\n#![no_std]\n#![no_main]\n\nuse embassy_executor::Spawner;\nuse embassy_mcxa::clocks::config::Div8;\nuse embassy_mcxa::dma::{DmaChannel, ScatterGatherBuilder};\nuse static_cell::ConstStaticCell;\nuse {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _};\n\n// Source buffers (multiple segments)\nstatic SRC1: ConstStaticCell<[u32; 4]> = ConstStaticCell::new([0x11111111, 0x22222222, 0x33333333, 0x44444444]);\nstatic SRC2: ConstStaticCell<[u32; 4]> = ConstStaticCell::new([0xAAAAAAAA, 0xBBBBBBBB, 0xCCCCCCCC, 0xDDDDDDDD]);\nstatic SRC3: ConstStaticCell<[u32; 4]> = ConstStaticCell::new([0x12345678, 0x9ABCDEF0, 0xFEDCBA98, 0x76543210]);\n\n// Destination buffers (one per segment)\nstatic DST1: ConstStaticCell<[u32; 4]> = ConstStaticCell::new([0; 4]);\nstatic DST2: ConstStaticCell<[u32; 4]> = ConstStaticCell::new([0; 4]);\nstatic DST3: ConstStaticCell<[u32; 4]> = ConstStaticCell::new([0; 4]);\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    // Small delay to allow probe-rs to attach after reset\n    for _ in 0..100_000 {\n        cortex_m::asm::nop();\n    }\n\n    let mut cfg = hal::config::Config::default();\n    cfg.clock_cfg.sirc.fro_12m_enabled = true;\n    cfg.clock_cfg.sirc.fro_lf_div = Some(Div8::no_div());\n    let p = hal::init(cfg);\n\n    defmt::info!(\"DMA Scatter-Gather Builder example starting...\");\n\n    defmt::info!(\"DMA Scatter-Gather Builder Example\");\n    defmt::info!(\"===================================\");\n    let src1 = SRC1.take();\n    let src2 = SRC2.take();\n    let src3 = SRC3.take();\n    let dst1 = DST1.take();\n    let dst2 = DST2.take();\n    let dst3 = DST3.take();\n\n    // Show source buffers\n    defmt::info!(\"Source buffers:\");\n    defmt::info!(\"  SRC1: {=[?]}\", src1.as_slice());\n    defmt::info!(\"  SRC2: {=[?]}\", src2.as_slice());\n    defmt::info!(\"  SRC3: {=[?]}\", src3.as_slice());\n\n    defmt::info!(\"Destination buffers (before):\");\n    defmt::info!(\"  DST1: {=[?]}\", dst1.as_slice());\n    defmt::info!(\"  DST2: {=[?]}\", dst2.as_slice());\n    defmt::info!(\"  DST3: {=[?]}\", dst3.as_slice());\n\n    // Create DMA channel\n    let dma_ch0 = DmaChannel::new(p.DMA_CH0);\n\n    defmt::info!(\"Building scatter-gather chain with builder API...\");\n\n    // =========================================================================\n    // ScatterGatherBuilder API demonstration\n    // =========================================================================\n    //\n    // The builder pattern makes scatter-gather transfers much easier:\n    // 1. Create a builder\n    // 2. Add transfer segments with add_transfer()\n    // 3. Call build() to start the entire chain\n    // No manual TCD manipulation required!\n\n    let mut builder = ScatterGatherBuilder::<u32>::new();\n\n    // Add three transfer segments - the builder handles TCD linking automatically\n    builder.add_transfer(src1, dst1);\n    builder.add_transfer(src2, dst2);\n    builder.add_transfer(src3, dst3);\n\n    defmt::info!(\"Added 3 transfer segments to chain.\");\n    defmt::info!(\"Starting scatter-gather transfer with .await...\");\n\n    // Build and execute the scatter-gather chain\n    // The build() method:\n    // - Links all TCDs together with ESG bit\n    // - Sets INTMAJOR on all TCDs\n    // - Loads the first TCD into hardware\n    // - Returns a Transfer future\n    let transfer = builder.build(dma_ch0).expect(\"Failed to build scatter-gather\");\n    transfer.blocking_wait();\n\n    defmt::info!(\"Scatter-gather transfer complete!\");\n\n    // Show results\n    defmt::info!(\"Destination buffers (after):\");\n    defmt::info!(\"  DST1: {=[?]}\", dst1.as_slice());\n    defmt::info!(\"  DST2: {=[?]}\", dst2.as_slice());\n    defmt::info!(\"  DST3: {=[?]}\", dst3.as_slice());\n\n    let comps = [(src1, dst1), (src2, dst2), (src3, dst3)];\n\n    // Verify all three segments\n    let mut all_ok = true;\n    for (src, dst) in comps {\n        all_ok &= src == dst;\n    }\n\n    if all_ok {\n        defmt::info!(\"PASS: All segments verified!\");\n    } else {\n        defmt::error!(\"FAIL: Mismatch detected!\");\n    }\n\n    defmt::info!(\"=== Scatter-Gather Builder example complete ===\");\n}\n"
  },
  {
    "path": "examples/mcxa5xx/src/bin/hello.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse embassy_executor::Spawner;\nuse {defmt_rtt as _, embassy_mcxa as _, panic_probe as _};\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    loop {\n        defmt::info!(\"Hello, world!\");\n        cortex_m::asm::delay(10_000_000);\n    }\n}\n"
  },
  {
    "path": "examples/mcxa5xx/src/bin/i2c-async.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse embassy_executor::Spawner;\nuse embassy_time::Timer;\nuse hal::bind_interrupts;\nuse hal::clocks::config::Div8;\nuse hal::config::Config;\nuse hal::i2c::controller::{self, I2c, InterruptHandler, Speed};\nuse hal::peripherals::LPI2C3;\nuse {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _};\n\nbind_interrupts!(\n    struct Irqs {\n        LPI2C3 => InterruptHandler<LPI2C3>;\n    }\n);\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let mut config = Config::default();\n    config.clock_cfg.sirc.fro_lf_div = Div8::from_divisor(1);\n\n    let p = hal::init(config);\n\n    defmt::info!(\"I2C example\");\n\n    let mut config = controller::Config::default();\n    config.speed = Speed::Standard;\n    let mut i2c = I2c::new_async(p.LPI2C3, p.P3_21, p.P3_20, Irqs, config).unwrap();\n    let mut buf = [0u8; 2];\n\n    loop {\n        i2c.async_write_read(0x48, &[0x00], &mut buf).await.unwrap();\n        defmt::info!(\"Buffer: {:02x}\", buf);\n        Timer::after_secs(1).await;\n    }\n}\n"
  },
  {
    "path": "examples/mcxa5xx/src/bin/i2c-blocking.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse embassy_executor::Spawner;\nuse embassy_time::Timer;\nuse hal::clocks::config::Div8;\nuse hal::config::Config;\nuse hal::i2c::controller::{self, I2c, Speed};\nuse tmp108::Tmp108;\nuse {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _};\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let mut config = Config::default();\n    config.clock_cfg.sirc.fro_lf_div = Div8::from_divisor(1);\n\n    let p = hal::init(config);\n\n    defmt::info!(\"I2C example\");\n\n    let mut config = controller::Config::default();\n    config.speed = Speed::Standard;\n    let i2c = I2c::new_blocking(p.LPI2C3, p.P3_21, p.P3_20, config).unwrap();\n    let mut tmp = Tmp108::new_with_a0_gnd(i2c);\n\n    loop {\n        let temperature = tmp.temperature().unwrap();\n        defmt::info!(\"Temperature: {}C\", temperature);\n        Timer::after_secs(1).await;\n    }\n}\n"
  },
  {
    "path": "examples/mcxa5xx/src/bin/i2c-dma.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse embassy_executor::Spawner;\nuse embassy_time::Timer;\nuse hal::bind_interrupts;\nuse hal::clocks::config::{Div8, MainClockSource};\nuse hal::config::Config;\nuse hal::i2c::controller::{self, I2c, InterruptHandler, Speed};\nuse hal::peripherals::LPI2C3;\nuse {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _};\n\nbind_interrupts!(\n    struct Irqs {\n        LPI2C3 => InterruptHandler<LPI2C3>;\n    }\n);\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let mut config = Config::default();\n    config.clock_cfg.main_clock.source = MainClockSource::SircFro12M;\n    config.clock_cfg.sirc.fro_lf_div = Div8::from_divisor(1);\n\n    let p = hal::init(config);\n\n    defmt::info!(\"I2C example\");\n\n    let mut config = controller::Config::default();\n    config.speed = Speed::Standard;\n    let mut i2c = I2c::new_async_with_dma(p.LPI2C3, p.P3_21, p.P3_20, p.DMA_CH0, p.DMA_CH1, Irqs, config).unwrap();\n    let mut buf = [0u8; 2];\n\n    loop {\n        i2c.async_write_read(0x48, &[0x00], &mut buf).await.unwrap();\n        defmt::info!(\"Buffer: {:02x}\", buf);\n        Timer::after_secs(1).await;\n    }\n}\n"
  },
  {
    "path": "examples/mcxa5xx/src/bin/i2c-scan-blocking.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse embassy_executor::Spawner;\nuse hal::clocks::config::Div8;\nuse hal::config::Config;\nuse hal::i2c::controller::{self, I2c, Speed};\nuse {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _};\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let mut config = Config::default();\n    config.clock_cfg.sirc.fro_lf_div = Div8::from_divisor(1);\n\n    let p = hal::init(config);\n\n    defmt::info!(\"I2C example\");\n\n    let mut config = controller::Config::default();\n    config.speed = Speed::Standard;\n\n    let mut i2c = I2c::new_blocking(p.LPI2C3, p.P3_21, p.P3_20, config).unwrap();\n\n    for addr in 0x01..=0x7f {\n        let result = i2c.blocking_write(addr, &[]);\n        if result.is_ok() {\n            defmt::info!(\"Device found at addr {:02x}\", addr);\n        }\n    }\n}\n"
  },
  {
    "path": "examples/mcxa5xx/src/bin/i2c-target-async.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse embassy_executor::Spawner;\nuse hal::bind_interrupts;\nuse hal::clocks::config::Div8;\nuse hal::config::Config;\nuse hal::i2c::target::{self, InterruptHandler};\nuse hal::peripherals::LPI2C3;\nuse {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _};\n\nbind_interrupts!(\n    struct Irqs {\n        LPI2C3 => InterruptHandler<LPI2C3>;\n    }\n);\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let mut config = Config::default();\n    config.clock_cfg.sirc.fro_lf_div = Div8::from_divisor(1);\n\n    let p = hal::init(config);\n\n    defmt::info!(\"I2C target example\");\n\n    let mut config = target::Config::default();\n    config.address = target::Address::Single(0x2a);\n\n    // Other possible address configurations\n    // config.address = target::Address::Dual(0x2a, 0x31);\n    // config.address = target::Address::Range(0x20..0x30);\n\n    let mut target = target::I2c::new_async(p.LPI2C3, p.P3_21, p.P3_20, Irqs, config).unwrap();\n    let mut buf = [0u8; 32];\n\n    loop {\n        let request = target.async_listen().await.unwrap();\n        defmt::info!(\"Received event {}\", request);\n        match request {\n            target::Request::Read(_addr) => {\n                buf.fill(0x55);\n                let count = target.async_respond_to_read(&buf).await.unwrap();\n                defmt::info!(\"T [R]: {:02x} -> {:02x}\", _addr, buf[..count]);\n            }\n            target::Request::Write(_addr) => {\n                let count = target.async_respond_to_write(&mut buf).await.unwrap();\n                defmt::info!(\"T [W]: {:02x} <- {:02x}\", _addr, buf[..count]);\n            }\n            _ => {}\n        }\n    }\n}\n"
  },
  {
    "path": "examples/mcxa5xx/src/bin/i2c-target-blocking.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse embassy_executor::Spawner;\nuse hal::clocks::config::Div8;\nuse hal::config::Config;\nuse hal::i2c::target;\nuse {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _};\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let mut config = Config::default();\n    config.clock_cfg.sirc.fro_lf_div = Div8::from_divisor(1);\n\n    let p = hal::init(config);\n\n    defmt::info!(\"I2C target example\");\n\n    let mut config = target::Config::default();\n    config.address = target::Address::Single(42);\n\n    // Other possible address configurations\n    // config.address = target::Address::Dual(0x2a, 0x31);\n    // config.address = target::Address::Range(0x20..0x30);\n\n    let mut target = target::I2c::new_blocking(p.LPI2C3, p.P3_21, p.P3_20, config).unwrap();\n    let mut buf = [0u8; 32];\n\n    loop {\n        let request = target.blocking_listen().unwrap();\n        defmt::info!(\"Received event {}\", request);\n        match request {\n            target::Request::Read(_addr) => {\n                buf.fill(0x55);\n                let count = target.blocking_respond_to_read(&buf).unwrap();\n                defmt::info!(\"T [R]: {:02x} -> {:02x}\", _addr, buf[..count]);\n            }\n            target::Request::Write(_addr) => {\n                let count = target.blocking_respond_to_write(&mut buf).unwrap();\n                defmt::info!(\"T [W]: {:02x} <- {:02x}\", _addr, buf[..count]);\n            }\n            _ => {}\n        }\n    }\n}\n"
  },
  {
    "path": "examples/mcxa5xx/src/bin/i2c-target-dma.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse embassy_executor::Spawner;\nuse hal::bind_interrupts;\nuse hal::clocks::config::Div8;\nuse hal::config::Config;\nuse hal::i2c::target::{self, InterruptHandler};\nuse hal::peripherals::LPI2C3;\nuse {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _};\n\nbind_interrupts!(\n    struct Irqs {\n        LPI2C3 => InterruptHandler<LPI2C3>;\n    }\n);\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let mut config = Config::default();\n    config.clock_cfg.sirc.fro_lf_div = Div8::from_divisor(1);\n\n    let p = hal::init(config);\n\n    defmt::info!(\"I2C target example\");\n\n    let mut config = target::Config::default();\n    config.address = target::Address::Range(0x20..0x30);\n\n    // Other possible address configurations\n    // config.address = target::Address::Single(0x2a);\n    // config.address = target::Address::Dual(0x2a, 0x31);\n    // config.address = target::Address::Range(0x20..0x30);\n\n    let mut target =\n        target::I2c::new_async_with_dma(p.LPI2C3, p.P3_27, p.P3_28, p.DMA_CH0, p.DMA_CH1, Irqs, config).unwrap();\n    let mut buf = [0u8; 256];\n\n    loop {\n        let request = target.async_listen().await.unwrap();\n        defmt::info!(\"Received event {}\", request);\n        match request {\n            target::Request::Read(_addr) => {\n                buf.fill(0x55);\n                let count = target.async_respond_to_read(&buf).await.unwrap();\n                defmt::info!(\"T [R]: {:02x} -> {:02x}\", _addr, buf[..count]);\n            }\n            target::Request::Write(_addr) => {\n                let count = target.async_respond_to_write(&mut buf).await.unwrap();\n                defmt::info!(\"T [W]: {:02x} <- {:02x}\", _addr, buf[..count]);\n            }\n            _ => {}\n        }\n    }\n}\n"
  },
  {
    "path": "examples/mcxa5xx/src/bin/i3c-async.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse embassy_executor::Spawner;\nuse embassy_time::Timer;\nuse hal::bind_interrupts;\nuse hal::clocks::config::Div8;\nuse hal::config::Config;\nuse hal::i3c::InterruptHandler;\nuse hal::i3c::controller::{self, BusType, I3c};\nuse hal::peripherals::I3C0;\nuse {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _};\n\nbind_interrupts!(\n    struct Irqs {\n        I3C0 => InterruptHandler<I3C0>;\n    }\n);\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let mut config = Config::default();\n    config.clock_cfg.sirc.fro_lf_div = Div8::from_divisor(1);\n\n    let p = hal::init(config);\n\n    defmt::info!(\"I3C example\");\n\n    let config = controller::Config::default();\n    let mut i3c = I3c::new_async(p.I3C0, p.P0_21, p.P0_20, Irqs, config).unwrap();\n    let mut buf = [0u8; 2];\n\n    loop {\n        // ~~~~~~~~ //\n        // I2C mode //\n        // ~~~~~~~~ //\n\n        // RSTDAA: reset first to make sure device responds to I2c requests.\n        i3c.async_write(0x7e, &[0x06], BusType::I3cSdr).await.unwrap();\n\n        i3c.async_write_read(0x48, &[0x00], &mut buf, BusType::I2c)\n            .await\n            .unwrap();\n        let raw = f32::from(i16::from_be_bytes(buf) / 16);\n        let temp_i2c = raw * 0.0625;\n        defmt::info!(\"P3T1755 via I2C: {}C\", temp_i2c);\n        Timer::after_secs(1).await;\n\n        // ~~~~~~~~~~~~ //\n        // I3C SDR mode //\n        // ~~~~~~~~~~~~ //\n\n        // RSTDAA\n        i3c.async_write(0x7e, &[0x06], BusType::I3cSdr).await.unwrap();\n\n        Timer::after_micros(100).await;\n\n        // ENTDAA\n        i3c.async_write(0x7e, &[0x07], BusType::I3cSdr).await.unwrap();\n\n        // P3T1755 temperature register = 0x00\n        i3c.async_write_read(0x48, &[0x00], &mut buf, BusType::I3cSdr)\n            .await\n            .unwrap();\n        let raw = f32::from(i16::from_be_bytes(buf) / 16);\n        let temp_i3c_sdr = raw * 0.0625;\n        defmt::info!(\"P3T1755 via I3C SDR: {}C\", temp_i3c_sdr);\n        Timer::after_secs(1).await;\n    }\n}\n"
  },
  {
    "path": "examples/mcxa5xx/src/bin/i3c-blocking.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse embassy_executor::Spawner;\nuse embassy_time::Timer;\nuse hal::clocks::config::Div8;\nuse hal::config::Config;\nuse hal::i3c::controller::{self, BusType, I3c};\nuse {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _};\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let mut config = Config::default();\n    config.clock_cfg.sirc.fro_lf_div = Div8::from_divisor(1);\n\n    let p = hal::init(config);\n\n    defmt::info!(\"I3C example\");\n\n    let config = controller::Config::default();\n    let mut i3c = I3c::new_blocking(p.I3C0, p.P0_21, p.P0_20, config).unwrap();\n    let mut buf = [0u8; 2];\n\n    loop {\n        // ~~~~~~~~ //\n        // I2C mode //\n        // ~~~~~~~~ //\n\n        // RSTDAA: reset first to make sure device responds to I2c requests.\n        i3c.blocking_write(0x7e, &[0x06], BusType::I3cSdr).unwrap();\n\n        i3c.blocking_write_read(0x48, &[0x00], &mut buf, BusType::I2c).unwrap();\n        let raw = f32::from(i16::from_be_bytes(buf) / 16);\n        let temp_i2c = raw * 0.0625;\n        defmt::info!(\"P3T1755 via I2C: {}C\", temp_i2c);\n        Timer::after_secs(1).await;\n\n        // ~~~~~~~~~~~~ //\n        // I3C SDR mode //\n        // ~~~~~~~~~~~~ //\n\n        // RSTDAA\n        i3c.blocking_write(0x7e, &[0x06], BusType::I3cSdr).unwrap();\n\n        Timer::after_micros(100).await;\n\n        // ENTDAA\n        i3c.blocking_write(0x7e, &[0x07], BusType::I3cSdr).unwrap();\n\n        // P3T1755 temperature register = 0x00\n        i3c.blocking_write_read(0x48, &[0x00], &mut buf, BusType::I3cSdr)\n            .unwrap();\n        let raw = f32::from(i16::from_be_bytes(buf) / 16);\n        let temp_i3c = raw * 0.0625;\n        defmt::info!(\"P3T1755 via I3C SDR: {}C\", temp_i3c);\n        Timer::after_secs(1).await;\n    }\n}\n"
  },
  {
    "path": "examples/mcxa5xx/src/bin/i3c-dma.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse embassy_executor::Spawner;\nuse embassy_time::Timer;\nuse hal::bind_interrupts;\nuse hal::clocks::config::Div8;\nuse hal::config::Config;\nuse hal::i3c::InterruptHandler;\nuse hal::i3c::controller::{self, BusType, I3c};\nuse hal::peripherals::I3C0;\nuse {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _};\n\nbind_interrupts!(\n    struct Irqs {\n        I3C0 => InterruptHandler<I3C0>;\n    }\n);\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let mut config = Config::default();\n    config.clock_cfg.sirc.fro_lf_div = Div8::from_divisor(1);\n\n    let p = hal::init(config);\n\n    defmt::info!(\"I3C example\");\n\n    let config = controller::Config::default();\n    let mut i3c = I3c::new_async_with_dma(p.I3C0, p.P0_21, p.P0_20, p.DMA_CH0, p.DMA_CH1, Irqs, config).unwrap();\n    let mut buf = [0u8; 2];\n\n    loop {\n        // ~~~~~~~~ //\n        // I2C mode //\n        // ~~~~~~~~ //\n\n        // RSTDAA: reset first to make sure device responds to I2c requests.\n        i3c.async_write(0x7e, &[0x06], BusType::I3cSdr).await.unwrap();\n\n        i3c.async_write_read(0x48, &[0x00], &mut buf, BusType::I2c)\n            .await\n            .unwrap();\n        let raw = f32::from(i16::from_be_bytes(buf) / 16);\n        let temp_i2c = raw * 0.0625;\n        defmt::info!(\"P3T1755 via I2C: {}C\", temp_i2c);\n        Timer::after_secs(1).await;\n\n        // ~~~~~~~~~~~~ //\n        // I3C SDR mode //\n        // ~~~~~~~~~~~~ //\n\n        // RSTDAA\n        i3c.async_write(0x7e, &[0x06], BusType::I3cSdr).await.unwrap();\n\n        Timer::after_micros(100).await;\n\n        // ENTDAA\n        i3c.async_write(0x7e, &[0x07], BusType::I3cSdr).await.unwrap();\n\n        // P3T1755 temperature register = 0x00\n        i3c.async_write_read(0x48, &[0x00], &mut buf, BusType::I3cSdr)\n            .await\n            .unwrap();\n        let raw = f32::from(i16::from_be_bytes(buf) / 16);\n        let temp_i3c_sdr = raw * 0.0625;\n        defmt::info!(\"P3T1755 via I3C SDR: {}C\", temp_i3c_sdr);\n        Timer::after_secs(1).await;\n    }\n}\n"
  },
  {
    "path": "examples/mcxa5xx/src/bin/lpuart_bbq_rx.rs",
    "content": "//! LPUART BBQueue example for MCXA577.\n//!\n//! This scenario is meant to be coupled with another device sending using the\n//! `lpuart_bbq_tx` example. In this scenario:\n//!\n//! * We set the device up to have a high power FRO192M clock in active mode\n//! * We set up the device to disable that FRO192M clock in deep sleep\n//! * We use a GPIO interrupt to detect the falling edge on the UART RX line while in deep sleep\n//! * We wake up, start the lpuart from the FRO192M clock source (which keeps us from entering deep sleep)\n//! * We process any incoming data, until the line is silent for 100ms\n//! * We then repeat the processs\n//!\n//! This is meant to model a scenario where the other device is talking to us, and sends\n//! a short \"dummy\"/\"knock\" packet to wake us up, then sends a relatively large burst of data\n//! for some amount of time, before timing out.\n//!\n//! By using a GPIO interrupt and releasing the lpuart, we are able to return to deep sleep mode, at the\n//! cost of losing any data sent while we are \"waking up\". Experimentally, this is approximately TBD, or\n//! about TBD bytes of data at 4mbaud. In the tx half of this example, the other device waits a full millisecond\n//! between the \"knock\" and the \"data\" packets.\n//!\n//! The benefit to this approach is that the CPU uses TBD at \"cpu working\" level, TBD at \"cpu WFE, lpuart+FRO192M active\" level,\n//! And TBD at \"waiting for GPIO interrupt\" level.\n\n#![no_std]\n#![no_main]\n\nuse embassy_executor::Spawner;\nuse embassy_mcxa::clocks::PoweredClock;\nuse embassy_mcxa::clocks::config::{\n    CoreSleep, Div8, FircConfig, FircFreqSel, FlashSleep, MainClockConfig, MainClockSource, VddDriveStrength, VddLevel,\n};\nuse embassy_mcxa::clocks::periph_helpers::LpuartClockSel;\nuse embassy_mcxa::dma::DmaChannel;\nuse embassy_mcxa::gpio::{DriveStrength, Input, Level, Output, Pull, SlewRate};\nuse embassy_mcxa::lpuart::{BbqConfig, BbqHalfParts, BbqRxMode, LpuartBbqRx};\nuse embassy_mcxa::{bind_interrupts, gpio, lpuart};\nuse embassy_time::{Duration, WithTimeout};\nuse static_cell::ConstStaticCell;\nuse {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _};\n\nbind_interrupts!(struct Irqs {\n    LPUART5 => lpuart::BbqInterruptHandler::<hal::peripherals::LPUART5>;\n    GPIO0 => gpio::InterruptHandler<hal::peripherals::GPIO0>;\n});\n\nconst SIZE: usize = 4096;\nstatic RX_BUF: ConstStaticCell<[u8; SIZE]> = ConstStaticCell::new([0u8; SIZE]);\n\n#[cfg_attr(\n    feature = \"executor-platform\",\n    embassy_executor::main(executor = \"embassy_mcxa::executor::Executor\", entry = \"cortex_m_rt::entry\")\n)]\n#[cfg_attr(not(feature = \"executor-platform\"), embassy_executor::main)]\nasync fn main(_spawner: Spawner) {\n    let mut cfg = hal::config::Config::default();\n\n    // Enable 192MHz clock source\n    let mut fcfg = FircConfig::default();\n    fcfg.frequency = FircFreqSel::Mhz192;\n    fcfg.power = PoweredClock::NormalEnabledDeepSleepDisabled;\n    fcfg.fro_hf_enabled = true;\n    fcfg.clk_hf_fundamental_enabled = false;\n    fcfg.fro_hf_div = Some(Div8::no_div());\n    cfg.clock_cfg.firc = Some(fcfg);\n\n    // Enable 12M osc\n    cfg.clock_cfg.sirc.fro_12m_enabled = true;\n    cfg.clock_cfg.sirc.fro_lf_div = Some(Div8::no_div());\n    cfg.clock_cfg.sirc.power = PoweredClock::AlwaysEnabled;\n\n    // Disable 16K osc\n    cfg.clock_cfg.fro16k = None;\n\n    // Disable external osc\n    cfg.clock_cfg.sosc = None;\n\n    // Disable PLL\n    cfg.clock_cfg.spll = None;\n\n    // Feed core from 192M osc\n    cfg.clock_cfg.main_clock = MainClockConfig {\n        source: MainClockSource::FircHfRoot,\n        power: PoweredClock::NormalEnabledDeepSleepDisabled,\n        ahb_clk_div: Div8::no_div(),\n    };\n\n    // Active mode: High power, Deep Sleep: Low power\n    cfg.clock_cfg.vdd_power.active_mode.level = VddLevel::OverDriveMode;\n    cfg.clock_cfg.vdd_power.active_mode.drive = VddDriveStrength::Normal;\n    cfg.clock_cfg.vdd_power.low_power_mode.level = VddLevel::MidDriveMode;\n    cfg.clock_cfg.vdd_power.low_power_mode.drive = VddDriveStrength::Low { enable_bandgap: false };\n\n    // Set to deep sleep for measuring power. Set to WfeUngated to see defmt logs\n    cfg.clock_cfg.vdd_power.core_sleep = CoreSleep::DeepSleep;\n    // cfg.clock_cfg.vdd_power.core_sleep = CoreSleep::WfeUngated;\n\n    // Set flash doze, allowing internal flash clocks to be gated on sleep\n    cfg.clock_cfg.vdd_power.flash_sleep = FlashSleep::FlashDoze;\n\n    let p = hal::init(cfg);\n\n    defmt::info!(\"LPUART DMA example starting...\");\n\n    // Create UART configuration\n    let mut config = BbqConfig::default();\n    config.baudrate_bps = 4_000_000;\n    config.power = PoweredClock::NormalEnabledDeepSleepDisabled;\n    config.source = LpuartClockSel::FroHfDiv;\n\n    let rx_buf = RX_BUF.take();\n\n    // Create UART instance with DMA channels\n    let dma = DmaChannel::new(p.DMA_CH0);\n    let mut parts = BbqHalfParts::new_rx_half_async(p.LPUART5, Irqs, p.P0_24, rx_buf, dma);\n    let mut red = Output::new(p.P2_14, Level::High, DriveStrength::Normal, SlewRate::Fast);\n    let mut debug = Output::new(p.P5_8, Level::High, DriveStrength::Normal, SlewRate::Fast);\n\n    #[cfg(feature = \"executor-platform\")]\n    embassy_mcxa::executor::set_executor_debug_gpio(p.P3_27);\n\n    loop {\n        defmt::info!(\"Waiting for falling edge\");\n        red.set_high();\n        debug.set_high();\n        {\n            let mut input = defmt::unwrap!(Input::async_from_anypin(parts.pin(), Pull::Up));\n            input.wait_for_low().await;\n        }\n\n        let mode = BbqRxMode::MaxFrame { size: 1024 };\n        let mut lpuart = LpuartBbqRx::new(parts, config, mode).unwrap();\n        red.set_low();\n        debug.set_low();\n        defmt::info!(\"got wake, listening\");\n\n        let mut streak = 0;\n        let mut idx = 0u8;\n        let mut buf = [0u8; 64];\n        let mut got = 0;\n        let mut dummies = 0;\n\n        'wake: loop {\n            match lpuart.read(&mut buf).with_timeout(Duration::from_millis(100)).await {\n                Ok(res) => {\n                    let used = res.unwrap();\n                    for byte in &buf[..used] {\n                        if *byte > 0x7F {\n                            dummies += 1;\n                        } else if *byte == idx {\n                            streak += 1;\n                            idx = idx.wrapping_add(1) & 0x7F;\n                        } else {\n                            streak = 0;\n                            idx = (*byte).wrapping_add(1) & 0x7F;\n                        }\n                    }\n                    got += used;\n                }\n                Err(_) => {\n                    break 'wake;\n                }\n            }\n        }\n\n        defmt::info!(\n            \"Going to sleep, got: {=usize}, streak: {=usize}, dummies: {=usize}\",\n            got,\n            streak,\n            dummies\n        );\n\n        parts = lpuart.teardown();\n    }\n}\n"
  },
  {
    "path": "examples/mcxa5xx/src/bin/lpuart_bbq_tx.rs",
    "content": "//! LPUART BBQueue example for MCXA577.\n//!\n//! This scenario is meant to be coupled with another device receiving using the\n//! `lpuart_bbq_rx` example. In this scenario:\n//!\n//! * We send a short 16-byte \"knock\" packet\n//! * We wait 1ms for the other device to wake up\n//! * We send a larger 768-byte \"data\" packet\n//!\n//! See `lpuart_bbq_rx` for more information. This half is not sleepy, it just sends\n//! regularly without concern for power consumption.\n\n#![no_std]\n#![no_main]\n\nuse embassy_executor::Spawner;\nuse embassy_mcxa::clocks::PoweredClock;\nuse embassy_mcxa::clocks::config::{\n    CoreSleep, Div8, FircConfig, FircFreqSel, FlashSleep, MainClockConfig, MainClockSource, VddDriveStrength, VddLevel,\n};\nuse embassy_mcxa::clocks::periph_helpers::LpuartClockSel;\nuse embassy_mcxa::dma::DmaChannel;\nuse embassy_mcxa::gpio::{DriveStrength, Level, Output, SlewRate};\nuse embassy_mcxa::lpuart::{BbqConfig, BbqHalfParts, LpuartBbqTx};\nuse embassy_mcxa::{bind_interrupts, lpuart};\nuse embassy_time::Timer;\nuse static_cell::ConstStaticCell;\nuse {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _};\n\nbind_interrupts!(struct Irqs {\n    LPUART5 => lpuart::BbqInterruptHandler::<hal::peripherals::LPUART5>;\n});\n\nconst SIZE: usize = 4096;\nstatic TX_BUF: ConstStaticCell<[u8; SIZE]> = ConstStaticCell::new([0u8; SIZE]);\n\n#[cfg_attr(\n    feature = \"executor-platform\",\n    embassy_executor::main(executor = \"embassy_mcxa::executor::Executor\", entry = \"cortex_m_rt::entry\")\n)]\n#[cfg_attr(not(feature = \"executor-platform\"), embassy_executor::main)]\nasync fn main(_spawner: Spawner) {\n    let mut cfg = hal::config::Config::default();\n\n    // Enable 192MHz clock source\n    let mut fcfg = FircConfig::default();\n    fcfg.frequency = FircFreqSel::Mhz192;\n    fcfg.power = PoweredClock::NormalEnabledDeepSleepDisabled;\n    fcfg.fro_hf_enabled = true;\n    fcfg.clk_hf_fundamental_enabled = false;\n    fcfg.fro_hf_div = Some(const { Div8::from_divisor(4).unwrap() });\n    cfg.clock_cfg.firc = Some(fcfg);\n\n    // Enable 12M osc\n    cfg.clock_cfg.sirc.fro_12m_enabled = true;\n    cfg.clock_cfg.sirc.fro_lf_div = Some(Div8::no_div());\n    cfg.clock_cfg.sirc.power = PoweredClock::AlwaysEnabled;\n\n    // Disable 16K osc\n    cfg.clock_cfg.fro16k = None;\n\n    // Disable external osc\n    cfg.clock_cfg.sosc = None;\n\n    // Disable PLL\n    cfg.clock_cfg.spll = None;\n\n    // Feed core from 192M osc\n    cfg.clock_cfg.main_clock = MainClockConfig {\n        source: MainClockSource::FircHfRoot,\n        power: PoweredClock::NormalEnabledDeepSleepDisabled,\n        ahb_clk_div: Div8::no_div(),\n    };\n\n    // We don't sleep, set relatively high power\n    cfg.clock_cfg.vdd_power.active_mode.level = VddLevel::OverDriveMode;\n    cfg.clock_cfg.vdd_power.low_power_mode.level = VddLevel::MidDriveMode;\n    cfg.clock_cfg.vdd_power.active_mode.drive = VddDriveStrength::Normal;\n    cfg.clock_cfg.vdd_power.low_power_mode.drive = VddDriveStrength::Low { enable_bandgap: false };\n\n    // Set \"never sleep\" mode\n    cfg.clock_cfg.vdd_power.core_sleep = CoreSleep::WfeUngated;\n\n    // Set flash doze, allowing internal flash clocks to be gated on sleep\n    cfg.clock_cfg.vdd_power.flash_sleep = FlashSleep::FlashDoze;\n\n    let p = hal::init(cfg);\n\n    defmt::info!(\"LPUART DMA example starting...\");\n\n    // Create UART configuration\n    let mut config = BbqConfig::default();\n    config.baudrate_bps = 4_000_000;\n    config.power = PoweredClock::NormalEnabledDeepSleepDisabled;\n    config.source = LpuartClockSel::FroHfDiv;\n\n    let tx_buf = TX_BUF.take();\n\n    // Create UART instance with DMA channels\n    let dma_channel = DmaChannel::new(p.DMA_CH0);\n    let parts = BbqHalfParts::new_tx_half(p.LPUART5, Irqs, p.P0_25, tx_buf, dma_channel);\n    let mut lpuart = LpuartBbqTx::new(parts, config).unwrap();\n    let mut to_knock = [0u8; 16];\n    let mut to_send = [0u8; 768];\n    to_knock.iter_mut().for_each(|b| *b = 0xFF);\n    to_send.iter_mut().enumerate().for_each(|(i, b)| *b = (i as u8) & 0x7F);\n\n    Timer::after_millis(1000).await;\n\n    let mut red = Output::new(p.P2_14, Level::High, DriveStrength::Normal, SlewRate::Fast);\n\n    #[cfg(feature = \"executor-platform\")]\n    embassy_mcxa::executor::set_executor_debug_gpio(p.P3_27);\n\n    loop {\n        // Send a small 16-byte \"knock\" packet in case the other device is sleeping\n        defmt::info!(\"knock!\");\n        let mut window = to_knock.as_slice();\n        while !window.is_empty() {\n            let sent = lpuart.write(window).await.unwrap();\n            let (_now, later) = window.split_at(sent);\n            window = later;\n        }\n        defmt::info!(\"Knocked, flushing...\");\n        lpuart.flush().await;\n        // Wait a small amount of time AFTER knocking to allow the device to wake up\n        Timer::after_millis(1).await;\n\n        defmt::info!(\"Sending!\");\n        let mut window = to_send.as_slice();\n        while !window.is_empty() {\n            let sent = lpuart.write(window).await.unwrap();\n            let (_now, later) = window.split_at(sent);\n            window = later;\n        }\n        defmt::info!(\"Sent, flushing...\");\n        lpuart.flush().await;\n\n        defmt::info!(\"Flushed.\");\n\n        // Now wait a bit to let the other device go back to sleep\n        Timer::after_millis(3000).await;\n        red.toggle();\n    }\n}\n"
  },
  {
    "path": "examples/mcxa5xx/src/bin/lpuart_buffered.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse embassy_executor::Spawner;\nuse embassy_mcxa::clocks::config::Div8;\nuse embassy_mcxa::lpuart::{Config, Lpuart};\nuse embassy_mcxa::{bind_interrupts, lpuart};\nuse embedded_io_async::Write;\nuse {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _};\n\n// Bind LPUART1 IRQ for the buffered driver\nbind_interrupts!(struct Irqs {\n    LPUART1 => lpuart::BufferedInterruptHandler::<hal::peripherals::LPUART1>;\n});\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let mut cfg = hal::config::Config::default();\n    cfg.clock_cfg.sirc.fro_12m_enabled = true;\n    cfg.clock_cfg.sirc.fro_lf_div = Some(Div8::no_div());\n    let p = hal::init(cfg);\n\n    // UART configuration (enable both TX and RX)\n    let config = Config {\n        baudrate_bps: 115_200,\n        rx_fifo_watermark: 0,\n        tx_fifo_watermark: 0,\n        ..Default::default()\n    };\n\n    let mut tx_buf = [0u8; 256];\n    let mut rx_buf = [0u8; 256];\n\n    // Create a buffered LPUART2 instance with both TX and RX\n    let mut uart = Lpuart::new_buffered(\n        p.LPUART1,\n        p.P1_9, // TX pin\n        p.P1_8, // RX pin\n        Irqs,\n        &mut tx_buf,\n        &mut rx_buf,\n        config,\n    )\n    .unwrap();\n\n    // Split into TX and RX parts\n    let (tx, rx) = uart.split_ref();\n\n    tx.write(b\"Hello buffered LPUART.\\r\\n\").await.unwrap();\n    tx.write(b\"Type characters to echo them back.\\r\\n\").await.unwrap();\n\n    // Echo loop\n    let mut buf = [0u8; 4];\n    loop {\n        let used = rx.read(&mut buf).await.unwrap();\n        tx.write_all(&buf[..used]).await.unwrap();\n    }\n}\n"
  },
  {
    "path": "examples/mcxa5xx/src/bin/lpuart_dma.rs",
    "content": "//! LPUART DMA example for MCXA577.\n//!\n//! This example demonstrates using DMA for UART TX and RX operations.\n//! It sends a message using DMA, then waits for 16 characters to be received\n//! via DMA and echoes them back.\n//!\n//! The DMA request sources are automatically derived from the LPUART instance type.\n//! DMA clock/reset/init is handled automatically by the HAL.\n\n#![no_std]\n#![no_main]\n\nuse embassy_executor::Spawner;\nuse embassy_mcxa::clocks::config::Div8;\nuse embassy_mcxa::lpuart::{Config, Lpuart};\nuse {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _};\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let mut cfg = hal::config::Config::default();\n    cfg.clock_cfg.sirc.fro_12m_enabled = true;\n    cfg.clock_cfg.sirc.fro_lf_div = Some(Div8::no_div());\n    let p = hal::init(cfg);\n\n    defmt::info!(\"LPUART DMA example starting...\");\n\n    // Create UART configuration\n    let config = Config {\n        baudrate_bps: 115_200,\n        ..Default::default()\n    };\n\n    // Create UART instance with DMA channels\n    let mut lpuart = Lpuart::new_async_with_dma(\n        p.LPUART1, // Instance\n        p.P1_9,    // TX pin\n        p.P1_8,    // RX pin\n        p.DMA_CH0, // TX DMA channel\n        p.DMA_CH1, // RX DMA channel\n        config,\n    )\n    .unwrap();\n\n    // Send a message using DMA (DMA request source is automatically derived from LPUART2)\n    let tx_msg = b\"Hello from LPUART2 DMA TX!\\r\\n\";\n    lpuart.write(tx_msg).await.unwrap();\n\n    defmt::info!(\"TX DMA complete\");\n\n    // Send prompt\n    let prompt = b\"Type 16 characters to echo via DMA:\\r\\n\";\n    lpuart.write(prompt).await.unwrap();\n\n    // Receive 16 characters using DMA\n    let mut rx_buf = [0u8; 16];\n    lpuart.read(&mut rx_buf).await.unwrap();\n\n    defmt::info!(\"RX DMA complete\");\n\n    // Echo back the received data\n    let echo_prefix = b\"\\r\\nReceived: \";\n    lpuart.write(echo_prefix).await.unwrap();\n    lpuart.write(&rx_buf).await.unwrap();\n    let done_msg = b\"\\r\\nDone!\\r\\n\";\n    lpuart.write(done_msg).await.unwrap();\n\n    defmt::info!(\"Example complete\");\n}\n"
  },
  {
    "path": "examples/mcxa5xx/src/bin/lpuart_polling.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse embassy_executor::Spawner;\nuse embassy_mcxa::clocks::config::Div8;\nuse {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _};\n\nuse crate::hal::lpuart::{Config, Lpuart};\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let mut cfg = hal::config::Config::default();\n    cfg.clock_cfg.sirc.fro_12m_enabled = true;\n    cfg.clock_cfg.sirc.fro_lf_div = Some(Div8::no_div());\n    let p = hal::init(cfg);\n\n    defmt::info!(\"boot\");\n\n    // Create UART configuration\n    let config = Config {\n        baudrate_bps: 115_200,\n        ..Default::default()\n    };\n\n    // Create UART instance using LPUART1 with P1_9 as TX and P1_8 as RX\n    let lpuart = Lpuart::new_blocking(\n        p.LPUART1, // Peripheral\n        p.P1_9,    // TX pin\n        p.P1_8,    // RX pin\n        config,\n    )\n    .unwrap();\n\n    // Split into separate TX and RX parts\n    let (mut tx, mut rx) = lpuart.split();\n\n    // Write hello messages\n    tx.blocking_write(b\"Hello world.\\r\\n\").unwrap();\n    tx.blocking_write(b\"Echoing. Type characters...\\r\\n\").unwrap();\n\n    // Echo loop\n    loop {\n        let mut buf = [0u8; 1];\n        rx.blocking_read(&mut buf).unwrap();\n        tx.blocking_write(&buf).unwrap();\n    }\n}\n"
  },
  {
    "path": "examples/mcxa5xx/src/bin/lpuart_ring_buffer.rs",
    "content": "//! LPUART Ring Buffer DMA example for MCXA276.\n//!\n//! This example demonstrates using the high-level `LpuartRxDma::setup_ring_buffer()`\n//! API for continuous circular DMA reception from a UART peripheral.\n//!\n//! # Features demonstrated:\n//! - `LpuartRxDma::setup_ring_buffer()` for continuous peripheral-to-memory DMA\n//! - `RingBuffer` for async reading of received data\n//! - Handling of potential overrun conditions\n//! - Half-transfer and complete-transfer interrupts for timely wakeups\n//!\n//! # How it works:\n//! 1. Create an `LpuartRxDma` driver with a DMA channel\n//! 2. Call `setup_ring_buffer()` which handles all low-level DMA configuration\n//! 3. Application asynchronously reads data as it arrives via `ring_buf.read()`\n//! 4. Both half-transfer and complete-transfer interrupts wake the reader\n\n#![no_std]\n#![no_main]\n\nuse embassy_executor::Spawner;\nuse embassy_mcxa::clocks::config::Div8;\nuse embassy_mcxa::lpuart::{Config, Dma, Lpuart, LpuartTx};\nuse static_cell::ConstStaticCell;\nuse {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _};\n\n// Ring buffer for RX - power of 2 is ideal for modulo efficiency\nstatic RX_RING_BUFFER: ConstStaticCell<[u8; 64]> = ConstStaticCell::new([0; 64]);\n\n/// Helper to write a byte as hex to UART\nasync fn write_hex<'a>(tx: &mut LpuartTx<'a, Dma<'a>>, byte: u8) {\n    const HEX: &[u8; 16] = b\"0123456789ABCDEF\";\n    let buf = [HEX[(byte >> 4) as usize], HEX[(byte & 0x0F) as usize]];\n    tx.write(&buf).await.unwrap();\n}\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    // Small delay to allow probe-rs to attach after reset\n    for _ in 0..100_000 {\n        cortex_m::asm::nop();\n    }\n\n    let mut cfg = hal::config::Config::default();\n    cfg.clock_cfg.sirc.fro_12m_enabled = true;\n    cfg.clock_cfg.sirc.fro_lf_div = Some(Div8::no_div());\n    let p = hal::init(cfg);\n\n    defmt::info!(\"LPUART Ring Buffer DMA example starting...\");\n\n    // Create UART configuration\n    let config = Config {\n        baudrate_bps: 115_200,\n        ..Default::default()\n    };\n\n    // Create LPUART with DMA support for both TX and RX, then split\n    let lpuart = Lpuart::new_async_with_dma(p.LPUART1, p.P1_9, p.P1_8, p.DMA_CH1, p.DMA_CH0, config).unwrap();\n    let (mut tx, mut rx) = lpuart.split();\n\n    tx.write(b\"LPUART Ring Buffer DMA Example\\r\\n\").await.unwrap();\n    tx.write(b\"==============================\\r\\n\\r\\n\").await.unwrap();\n\n    tx.write(b\"Setting up circular DMA for UART RX...\\r\\n\").await.unwrap();\n\n    let buf = RX_RING_BUFFER.take();\n    // Set up the ring buffer with circular DMA\n    let mut ring_buf = rx.into_ring_dma_rx(buf).unwrap();\n\n    tx.write(b\"Ring buffer ready! Type characters to see them echoed.\\r\\n\")\n        .await\n        .unwrap();\n    tx.write(b\"The DMA continuously receives in the background.\\r\\n\\r\\n\")\n        .await\n        .unwrap();\n\n    // Main loop: read from ring buffer and echo back\n    let mut read_buf = [0u8; 16];\n    let mut total_received: usize = 0;\n\n    loop {\n        // Async read - waits until data is available\n        match ring_buf.read(&mut read_buf).await {\n            Ok(n) if n > 0 => {\n                total_received += n;\n\n                // Echo back what we received\n                tx.write(b\"RX[\").await.unwrap();\n                for (i, &byte) in read_buf.iter().enumerate().take(n) {\n                    write_hex(&mut tx, byte).await;\n                    if i < n - 1 {\n                        tx.write(b\" \").await.unwrap();\n                    }\n                }\n                tx.write(b\"]: \").await.unwrap();\n                tx.write(&read_buf[..n]).await.unwrap();\n                tx.write(b\"\\r\\n\").await.unwrap();\n\n                defmt::info!(\"Received {} bytes, total: {}\", n, total_received);\n            }\n            Ok(_) => {\n                // No data, shouldn't happen with async read\n            }\n            Err(_) => {\n                // Overrun detected\n                tx.write(b\"ERROR: Ring buffer overrun!\\r\\n\").await.unwrap();\n                defmt::error!(\"Ring buffer overrun!\");\n                ring_buf.clear();\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "examples/mcxa5xx/src/bin/power-deepsleep-big-jump.rs",
    "content": "//! This example demonstrates automatic clock gating. This example needs to be run\n//! with:\n//!\n//! ```sh\n//! cargo run --release --no-default-features --features=executor-platform --bin power-deepsleep-big-jump\n//! ```\n\n#![no_std]\n#![no_main]\n\nuse embassy_executor::Spawner;\nuse embassy_mcxa::clkout::{self, ClockOut, ClockOutSel, Div4};\nuse embassy_mcxa::clocks::config::{\n    CoreSleep, Div8, FircConfig, FircFreqSel, FlashSleep, MainClockConfig, MainClockSource, VddDriveStrength, VddLevel,\n};\nuse embassy_mcxa::clocks::{PoweredClock, WakeGuard};\nuse embassy_time::{Duration, Instant, Timer};\nuse hal::gpio::{DriveStrength, Level, Output, SlewRate};\nuse {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _};\n\n#[cfg_attr(\n    feature = \"executor-platform\",\n    embassy_executor::main(executor = \"embassy_mcxa::executor::Executor\", entry = \"cortex_m_rt::entry\")\n)]\n#[cfg_attr(not(feature = \"executor-platform\"), embassy_executor::main)]\nasync fn main(_spawner: Spawner) {\n    // Do a short delay in order to allow for us to attach the debugger/start\n    // a flash in case some setting below is wrong, and the CPU gets stuck\n    // in deep sleep with debugging disabled.\n    defmt::info!(\"Pre-power delay!\");\n    // Experimentally: about 5-6s or so.\n    cortex_m::asm::delay(45_000_000);\n    defmt::info!(\"Pre-power delay complete!\");\n    let mut cfg = hal::config::Config::default();\n\n    // Enable 192M FIRC\n    let mut fcfg = FircConfig::default();\n    fcfg.frequency = FircFreqSel::Mhz192;\n    fcfg.power = PoweredClock::NormalEnabledDeepSleepDisabled;\n    fcfg.fro_hf_enabled = true;\n    fcfg.clk_hf_fundamental_enabled = false;\n    fcfg.fro_hf_div = Some(const { Div8::from_divisor(192).unwrap() });\n    cfg.clock_cfg.firc = Some(fcfg);\n\n    // Enable 12M osc to use as ostimer clock\n    cfg.clock_cfg.sirc.fro_12m_enabled = true;\n    cfg.clock_cfg.sirc.fro_lf_div = None;\n    cfg.clock_cfg.sirc.power = PoweredClock::AlwaysEnabled;\n\n    // Disable 16K osc\n    cfg.clock_cfg.fro16k = None;\n\n    // Disable external osc\n    cfg.clock_cfg.sosc = None;\n\n    // Disable PLL\n    cfg.clock_cfg.spll = None;\n\n    // Feed core from 192M osc\n    cfg.clock_cfg.main_clock = MainClockConfig {\n        source: MainClockSource::FircHfRoot,\n        power: PoweredClock::NormalEnabledDeepSleepDisabled,\n        ahb_clk_div: Div8::no_div(),\n    };\n\n    // Set the core in high power active mode\n    cfg.clock_cfg.vdd_power.active_mode.level = VddLevel::OverDriveMode;\n    cfg.clock_cfg.vdd_power.active_mode.drive = VddDriveStrength::Normal;\n    // Set the core in low power sleep mode\n    cfg.clock_cfg.vdd_power.low_power_mode.level = VddLevel::MidDriveMode;\n    cfg.clock_cfg.vdd_power.low_power_mode.drive = VddDriveStrength::Low { enable_bandgap: false };\n\n    // Set \"deep sleep\" mode\n    cfg.clock_cfg.vdd_power.core_sleep = CoreSleep::DeepSleep;\n\n    // Set flash doze, allowing internal flash clocks to be gated on sleep\n    cfg.clock_cfg.vdd_power.flash_sleep = FlashSleep::FlashDoze;\n\n    let p = hal::init(cfg);\n\n    #[cfg(feature = \"executor-platform\")]\n    embassy_mcxa::executor::set_executor_debug_gpio(p.P2_3);\n\n    let mut pin = p.P4_2;\n    let mut clkout = p.CLKOUT;\n    const K250_CONFIG: clkout::Config = clkout::Config {\n        // 180MHz / 180 -> 1MHz\n        sel: ClockOutSel::FroHfDiv,\n        // 1MHz / 4 -> 250kHz\n        div: const { Div4::from_divisor(4).unwrap() },\n        level: PoweredClock::NormalEnabledDeepSleepDisabled,\n    };\n\n    // We create a clkout peripheral so that we can view the activity of the clock\n    // in a logic analyzer. We use `new_unchecked` here, which doesn't participate\n    // in verifying that the clock sources are always valid, and does not take\n    // a WaitGuard token, which would prevent us from entering deep sleep.\n    let _clock_out = unsafe { ClockOut::new_unchecked(clkout.reborrow(), pin.reborrow(), K250_CONFIG) };\n\n    defmt::info!(\"Going to sleep shortly...\");\n    cortex_m::asm::delay(45_000_000 / 4);\n\n    let mut red = Output::new(p.P2_14, Level::High, DriveStrength::Normal, SlewRate::Slow);\n    loop {\n        Timer::after_millis(900).await;\n\n        // For the 100ms the LED is low, we manually take a wakeguard to prevent the\n        // system from returning to deep sleep, which drastically increases our power\n        // usage, but also prevents these clock sources from being disabled automatically.\n        red.set_low();\n        let _wg = WakeGuard::new();\n        let start = Instant::now();\n\n        // for the first 20ms, busyloop\n        while start.elapsed() < Duration::from_millis(20) {}\n\n        // then wfe sleep for 80ms\n        Timer::after_millis(80).await;\n\n        red.set_high();\n        // The WakeGuard is dropped here before returning to the top of the loop. When this\n        // happens, we will enter deep sleep automatically on our next .await.\n    }\n}\n"
  },
  {
    "path": "examples/mcxa5xx/src/bin/power-deepsleep-gating.rs",
    "content": "//! This example demonstrates automatic clock gating. This example needs to be run\n//! with:\n//!\n//! ```sh\n//! cargo run --release --no-default-features --features=executor-platform --bin power-deepsleep-gating\n//! ```\n\n#![no_std]\n#![no_main]\n\nuse embassy_executor::Spawner;\nuse embassy_mcxa::clkout::{self, ClockOut, ClockOutSel, Div4};\nuse embassy_mcxa::clocks::config::{\n    CoreSleep, Div8, FlashSleep, MainClockConfig, MainClockSource, SoscConfig, SoscMode, SpllConfig, SpllMode,\n    SpllSource, VddDriveStrength, VddLevel,\n};\nuse embassy_mcxa::clocks::{PoweredClock, WakeGuard};\nuse embassy_time::Timer;\nuse hal::gpio::{DriveStrength, Level, Output, SlewRate};\nuse {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _};\n\n#[cfg_attr(\n    feature = \"executor-platform\",\n    embassy_executor::main(executor = \"embassy_mcxa::executor::Executor\", entry = \"cortex_m_rt::entry\")\n)]\n#[cfg_attr(not(feature = \"executor-platform\"), embassy_executor::main)]\nasync fn main(_spawner: Spawner) {\n    // Do a short delay in order to allow for us to attach the debugger/start\n    // a flash in case some setting below is wrong, and the CPU gets stuck\n    // in deep sleep with debugging disabled.\n    defmt::info!(\"Pre-power delay!\");\n    // Experimentally: about 5-6s or so.\n    cortex_m::asm::delay(45_000_000);\n    defmt::info!(\"Pre-power delay complete!\");\n    let mut cfg = hal::config::Config::default();\n\n    // Disable 45M osc\n    cfg.clock_cfg.firc = None;\n\n    // Enable 12M osc to use as core clock\n    cfg.clock_cfg.sirc.fro_12m_enabled = true;\n    cfg.clock_cfg.sirc.fro_lf_div = None;\n    cfg.clock_cfg.sirc.power = PoweredClock::AlwaysEnabled;\n\n    // Disable 16K osc\n    cfg.clock_cfg.fro16k = None;\n\n    // Enable external osc, but ONLY in high-power mode\n    cfg.clock_cfg.sosc = Some(SoscConfig {\n        mode: SoscMode::CrystalOscillator,\n        frequency: 24_000_000,\n        power: PoweredClock::NormalEnabledDeepSleepDisabled,\n    });\n\n    // Enable PLL, but ONLY in high-power mode\n    cfg.clock_cfg.spll = Some(SpllConfig {\n        source: SpllSource::Sosc,\n        // 24MHz\n        // 24 x 16 => 384MHz\n        // 384 / (16 x 2) => 12.0MHz\n        mode: SpllMode::Mode1b {\n            m_mult: 16,\n            p_div: 16,\n            bypass_p2_div: false,\n        },\n        power: PoweredClock::NormalEnabledDeepSleepDisabled,\n        pll1_clk_div: Some(Div8::no_div()),\n    });\n\n    // Feed core from 12M osc\n    cfg.clock_cfg.main_clock = MainClockConfig {\n        source: MainClockSource::SircFro12M,\n        power: PoweredClock::AlwaysEnabled,\n        ahb_clk_div: Div8::no_div(),\n    };\n\n    // Set lowest core power, disable bandgap LDO reference\n    cfg.clock_cfg.vdd_power.active_mode.level = VddLevel::MidDriveMode;\n    cfg.clock_cfg.vdd_power.low_power_mode.level = VddLevel::MidDriveMode;\n    cfg.clock_cfg.vdd_power.low_power_mode.drive = VddDriveStrength::Low { enable_bandgap: false };\n\n    // We DO need to enable the core bandgap while in active mode, as the SOSC and SPLL clock\n    // sources require this to operate. If we wanted these clocks active in low power mode,\n    // we would need to enable it there too.\n    cfg.clock_cfg.vdd_power.active_mode.drive = VddDriveStrength::Low { enable_bandgap: true };\n\n    // Set \"deep sleep\" mode\n    cfg.clock_cfg.vdd_power.core_sleep = CoreSleep::DeepSleep;\n\n    // Set flash doze, allowing internal flash clocks to be gated on sleep\n    cfg.clock_cfg.vdd_power.flash_sleep = FlashSleep::FlashDoze;\n\n    let p = hal::init(cfg);\n\n    #[cfg(feature = \"executor-platform\")]\n    embassy_mcxa::executor::set_executor_debug_gpio(p.P2_3);\n\n    let mut pin = p.P4_2;\n    let mut clkout = p.CLKOUT;\n    const M1_CONFIG: clkout::Config = clkout::Config {\n        sel: ClockOutSel::Pll1ClkDiv,\n        div: const { Div4::from_divisor(12).unwrap() },\n        level: PoweredClock::NormalEnabledDeepSleepDisabled,\n    };\n\n    // We create a clkout peripheral so that we can view the activity of the clock\n    // in a logic analyzer. We use `new_unchecked` here, which doesn't participate\n    // in verifying that the clock sources are always valid, and does not take\n    // a WaitGuard token, which would prevent us from entering deep sleep.\n    let _clock_out = unsafe { ClockOut::new_unchecked(clkout.reborrow(), pin.reborrow(), M1_CONFIG) };\n\n    defmt::info!(\"Going to sleep shortly...\");\n    cortex_m::asm::delay(45_000_000 / 4);\n\n    let mut red = Output::new(p.P2_14, Level::High, DriveStrength::Normal, SlewRate::Slow);\n    loop {\n        Timer::after_millis(900).await;\n\n        // For the 100ms the LED is low, we manually take a wakeguard to prevent the\n        // system from returning to deep sleep, which drastically increases our power\n        // usage (experimentally: 1660uA vs the 154uA visible with these clocks *not*\n        // running in deep sleep), but also prevents these clock sources from being\n        // disabled automatically.\n        red.set_low();\n        let _wg = WakeGuard::new();\n        Timer::after_millis(100).await;\n\n        red.set_high();\n        // The WakeGuard is dropped here before returning to the top of the loop. When this\n        // happens, we will enter deep sleep automatically on our next .await.\n    }\n}\n"
  },
  {
    "path": "examples/mcxa5xx/src/bin/power-deepsleep-gpio-int.rs",
    "content": "//! This example demonstrates automatic clock gating. This example needs to be run\n//! with:\n//!\n//! ```sh\n//! cargo run --release --no-default-features --features=executor-platform --bin power-deepsleep-gpio-int\n//! ```\n\n#![no_std]\n#![no_main]\n\nuse embassy_executor::Spawner;\nuse embassy_mcxa::clkout::{self, ClockOut, ClockOutSel, Div4};\nuse embassy_mcxa::clocks::config::{\n    CoreSleep, Div8, FircConfig, FircFreqSel, FlashSleep, MainClockConfig, MainClockSource, VddDriveStrength, VddLevel,\n};\nuse embassy_mcxa::clocks::{PoweredClock, WakeGuard};\nuse embassy_mcxa::gpio::{self, Async, Input, Pull};\nuse embassy_mcxa::{bind_interrupts, peripherals};\nuse embassy_time::{Duration, Instant, Timer};\nuse hal::gpio::{DriveStrength, Level, Output, SlewRate};\nuse {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _};\n\nbind_interrupts!(struct Irqs {\n    GPIO3 => gpio::InterruptHandler<peripherals::GPIO3>;\n});\n\n#[cfg_attr(\n    feature = \"executor-platform\",\n    embassy_executor::main(executor = \"embassy_mcxa::executor::Executor\", entry = \"cortex_m_rt::entry\")\n)]\n#[cfg_attr(not(feature = \"executor-platform\"), embassy_executor::main)]\nasync fn main(spawner: Spawner) {\n    // Do a short delay in order to allow for us to attach the debugger/start\n    // a flash in case some setting below is wrong, and the CPU gets stuck\n    // in deep sleep with debugging disabled.\n    defmt::info!(\"Pre-power delay!\");\n    // Experimentally: about 5-6s or so.\n    cortex_m::asm::delay(48_000_000);\n    defmt::info!(\"Pre-power delay complete!\");\n    let mut cfg = hal::config::Config::default();\n\n    // Enable 192M FIRC\n    let mut fcfg = FircConfig::default();\n    fcfg.frequency = FircFreqSel::Mhz192;\n    fcfg.power = PoweredClock::NormalEnabledDeepSleepDisabled;\n    fcfg.fro_hf_enabled = true;\n    fcfg.clk_hf_fundamental_enabled = false;\n    fcfg.fro_hf_div = Some(const { Div8::from_divisor(192).unwrap() });\n    cfg.clock_cfg.firc = Some(fcfg);\n\n    // Enable 12M osc to use as ostimer clock\n    cfg.clock_cfg.sirc.fro_12m_enabled = true;\n    cfg.clock_cfg.sirc.fro_lf_div = None;\n    cfg.clock_cfg.sirc.power = PoweredClock::AlwaysEnabled;\n\n    // Disable 16K osc\n    cfg.clock_cfg.fro16k = None;\n\n    // Disable external osc\n    cfg.clock_cfg.sosc = None;\n\n    // Disable PLL\n    cfg.clock_cfg.spll = None;\n\n    // Feed core from 180M osc\n    cfg.clock_cfg.main_clock = MainClockConfig {\n        source: MainClockSource::FircHfRoot,\n        power: PoweredClock::NormalEnabledDeepSleepDisabled,\n        ahb_clk_div: Div8::no_div(),\n    };\n\n    // Set the core in high power active mode\n    cfg.clock_cfg.vdd_power.active_mode.level = VddLevel::OverDriveMode;\n    cfg.clock_cfg.vdd_power.active_mode.drive = VddDriveStrength::Normal;\n    // Set the core in low power sleep mode\n    cfg.clock_cfg.vdd_power.low_power_mode.level = VddLevel::MidDriveMode;\n    cfg.clock_cfg.vdd_power.low_power_mode.drive = VddDriveStrength::Low { enable_bandgap: false };\n\n    // Set \"deep sleep\" mode\n    cfg.clock_cfg.vdd_power.core_sleep = CoreSleep::DeepSleep;\n\n    // Set flash doze, allowing internal flash clocks to be gated on sleep\n    cfg.clock_cfg.vdd_power.flash_sleep = FlashSleep::FlashDoze;\n\n    let p = hal::init(cfg);\n\n    #[cfg(feature = \"executor-platform\")]\n    embassy_mcxa::executor::set_executor_debug_gpio(p.P2_3);\n\n    let mut pin = p.P4_2;\n    let mut clkout = p.CLKOUT;\n    const K250_CONFIG: clkout::Config = clkout::Config {\n        // 192MHz / 192 -> 1MHz\n        sel: ClockOutSel::FroHfDiv,\n        // 1MHz / 4 -> 250kHz\n        div: const { Div4::from_divisor(4).unwrap() },\n        level: PoweredClock::NormalEnabledDeepSleepDisabled,\n    };\n\n    // We create a clkout peripheral so that we can view the activity of the clock\n    // in a logic analyzer. We use `new_unchecked` here, which doesn't participate\n    // in verifying that the clock sources are always valid, and does not take\n    // a WaitGuard token, which would prevent us from entering deep sleep.\n    let _clock_out = unsafe { ClockOut::new_unchecked(clkout.reborrow(), pin.reborrow(), K250_CONFIG) };\n\n    defmt::info!(\"Going to sleep shortly...\");\n    cortex_m::asm::delay(48_000_000 / 4);\n\n    let mut red = Output::new(p.P2_14, Level::High, DriveStrength::Normal, SlewRate::Slow);\n\n    // Setup a second LED, and use the button labeled \"WAKEUP\" as an input source\n    let blue = Output::new(p.P2_23, Level::High, DriveStrength::Normal, SlewRate::Slow);\n    let btn = Input::new_async(p.P3_17, Irqs, Pull::Up);\n    spawner.spawn(press_toggler(btn, blue).unwrap());\n\n    loop {\n        // We sleep a little longer than usual, to make it easier to distinguish between\n        // timer wakeups and GPIO wakeups.\n        Timer::after_millis(4900).await;\n\n        // For the 100ms the LED is low, we manually take a wakeguard to prevent the\n        // system from returning to deep sleep, which drastically increases our power\n        // usage, but also prevents these clock sources from being disabled automatically.\n        red.set_low();\n        let _wg = WakeGuard::new();\n        let start = Instant::now();\n\n        // for the first 20ms, busyloop\n        while start.elapsed() < Duration::from_millis(20) {}\n\n        // then wfe sleep for 80ms\n        Timer::after_millis(80).await;\n\n        red.set_high();\n        // The WakeGuard is dropped here before returning to the top of the loop. When this\n        // happens, we will enter deep sleep automatically on our next .await.\n    }\n}\n\n/// A task that toggles the given LED every time the button falls low. No fancy\n/// debouncing, but useful to look at with the scope and the custom executor\n/// debug pin (or a power analyzer) to measure the time delta between the\n/// WAKEUP pin going low and the executor resuming from deep sleep.\n///\n/// At the time of writing, it takes us roughly 20us from the GPIO falling to\n/// the assertion of the executor debug gpio pin. This button can be observed\n/// using the mikro-bus header pin labeled \"RST\".\n#[embassy_executor::task]\nasync fn press_toggler(mut button: Input<'static, Async>, mut led: Output<'static>) {\n    loop {\n        button.wait_for_low().await;\n        led.toggle();\n        button.wait_for_high().await;\n    }\n}\n"
  },
  {
    "path": "examples/mcxa5xx/src/bin/power-deepsleep.rs",
    "content": "//! This example roughly emulates the `IDD_DEEP_SLEEP_MD_2` scenario from the datasheet.\n//!\n//! This example needs to be run with:\n//!\n//! ```sh\n//! cargo run --release --no-default-features --features=executor-platform --bin power-deepsleep\n//! ```\n//!\n//! As written, this achieves TBD average current when measured with a Nordic PPK2.\n\n#![no_std]\n#![no_main]\n\nuse embassy_executor::Spawner;\nuse embassy_mcxa::clocks::PoweredClock;\nuse embassy_mcxa::clocks::config::{\n    CoreSleep, Div8, FlashSleep, MainClockConfig, MainClockSource, VddDriveStrength, VddLevel,\n};\nuse embassy_time::Timer;\nuse hal::gpio::{DriveStrength, Level, Output, SlewRate};\nuse {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _};\n\n#[cfg_attr(\n    feature = \"executor-platform\",\n    embassy_executor::main(executor = \"embassy_mcxa::executor::Executor\", entry = \"cortex_m_rt::entry\")\n)]\n#[cfg_attr(not(feature = \"executor-platform\"), embassy_executor::main)]\nasync fn main(_spawner: Spawner) {\n    // Do a short delay in order to allow for us to attach the debugger/start\n    // a flash in case some setting below is wrong, and the CPU gets stuck\n    // in deep sleep with debugging disabled.\n    defmt::info!(\"Pre-power delay!\");\n    // Experimentally: about 5-6s or so.\n    cortex_m::asm::delay(48_000_000);\n    defmt::info!(\"Pre-power delay complete!\");\n    let mut cfg = hal::config::Config::default();\n\n    // Disable 48M osc\n    cfg.clock_cfg.firc = None;\n\n    // Enable 12M osc to use as core clock\n    cfg.clock_cfg.sirc.fro_12m_enabled = true;\n    cfg.clock_cfg.sirc.fro_lf_div = Some(Div8::no_div());\n    cfg.clock_cfg.sirc.power = PoweredClock::AlwaysEnabled;\n\n    // Disable 16K osc\n    cfg.clock_cfg.fro16k = None;\n\n    // Disable external osc\n    cfg.clock_cfg.sosc = None;\n\n    // Disable PLL\n    cfg.clock_cfg.spll = None;\n\n    // Feed core from 12M osc\n    cfg.clock_cfg.main_clock = MainClockConfig {\n        source: MainClockSource::SircFro12M,\n        power: PoweredClock::AlwaysEnabled,\n        ahb_clk_div: Div8::no_div(),\n    };\n\n    // Set lowest core power, disable bandgap LDO reference\n    cfg.clock_cfg.vdd_power.active_mode.level = VddLevel::MidDriveMode;\n    cfg.clock_cfg.vdd_power.low_power_mode.level = VddLevel::MidDriveMode;\n    cfg.clock_cfg.vdd_power.active_mode.drive = VddDriveStrength::Low { enable_bandgap: false };\n    cfg.clock_cfg.vdd_power.low_power_mode.drive = VddDriveStrength::Low { enable_bandgap: false };\n\n    // Set \"deep sleep\" mode\n    cfg.clock_cfg.vdd_power.core_sleep = CoreSleep::DeepSleep;\n\n    // Set flash doze, allowing internal flash clocks to be gated on sleep\n    cfg.clock_cfg.vdd_power.flash_sleep = FlashSleep::FlashDoze;\n\n    let p = hal::init(cfg);\n\n    defmt::info!(\"Going to sleep shortly...\");\n    cortex_m::asm::delay(48_000_000 / 4);\n\n    let mut red = Output::new(p.P3_18, Level::High, DriveStrength::Normal, SlewRate::Slow);\n    loop {\n        Timer::after_millis(900).await;\n        red.set_low();\n        Timer::after_millis(100).await;\n        red.set_high();\n    }\n}\n"
  },
  {
    "path": "examples/mcxa5xx/src/bin/power-wfe-gated.rs",
    "content": "//! This example roughly emulates the `IDD_SLEEP_MD_3` scenario from the datasheet.\n//!\n//! As written, this achieves TBD average current when measured with a Nordic PPK2.\n\n#![no_std]\n#![no_main]\n\nuse embassy_executor::Spawner;\nuse embassy_mcxa::clocks::PoweredClock;\nuse embassy_mcxa::clocks::config::{\n    CoreSleep, Div8, FlashSleep, MainClockConfig, MainClockSource, VddDriveStrength, VddLevel,\n};\nuse embassy_time::Timer;\nuse hal::gpio::{DriveStrength, Level, Output, SlewRate};\nuse {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _};\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    // Do a short delay in order to allow for us to attach the debugger/start\n    // a flash in case some setting below is wrong, and the CPU gets stuck\n    // in deep sleep with debugging disabled.\n    defmt::info!(\"Pre-power delay!\");\n    // Experimentally: about 5-6s or so.\n    cortex_m::asm::delay(45_000_000 * 2);\n    defmt::info!(\"Pre-power delay complete!\");\n    let mut cfg = hal::config::Config::default();\n\n    // Disable 45M osc\n    cfg.clock_cfg.firc = None;\n\n    // Enable 12M osc to use as core clock\n    cfg.clock_cfg.sirc.fro_12m_enabled = true;\n    cfg.clock_cfg.sirc.fro_lf_div = None;\n\n    // Disable 16K osc\n    cfg.clock_cfg.fro16k = None;\n\n    // Disable external osc\n    cfg.clock_cfg.sosc = None;\n\n    // Disable PLL\n    cfg.clock_cfg.spll = None;\n\n    // Feed core from 12M osc\n    cfg.clock_cfg.main_clock = MainClockConfig {\n        source: MainClockSource::SircFro12M,\n        power: PoweredClock::NormalEnabledDeepSleepDisabled,\n        ahb_clk_div: Div8::no_div(),\n    };\n\n    // Set lowest core power, disable bandgap LDO reference\n    cfg.clock_cfg.vdd_power.active_mode.level = VddLevel::MidDriveMode;\n    cfg.clock_cfg.vdd_power.low_power_mode.level = VddLevel::MidDriveMode;\n    cfg.clock_cfg.vdd_power.active_mode.drive = VddDriveStrength::Low { enable_bandgap: false };\n    cfg.clock_cfg.vdd_power.low_power_mode.drive = VddDriveStrength::Low { enable_bandgap: false };\n\n    // Set \"gated\" core mode, allowing clock to the core to be gated on sleep\n    cfg.clock_cfg.vdd_power.core_sleep = CoreSleep::WfeGated;\n\n    // Set flash doze, allowing internal flash clocks to be gated on sleep\n    cfg.clock_cfg.vdd_power.flash_sleep = FlashSleep::FlashDoze;\n\n    let p = hal::init(cfg);\n\n    defmt::info!(\"Going to sleep shortly...\");\n    cortex_m::asm::delay(45_000_000 / 2);\n\n    let mut red = Output::new(p.P3_18, Level::High, DriveStrength::Normal, SlewRate::Slow);\n    loop {\n        Timer::after_millis(900).await;\n        red.set_low();\n        Timer::after_millis(100).await;\n        red.set_high();\n    }\n}\n"
  },
  {
    "path": "examples/mcxa5xx/src/bin/pwm.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse embassy_executor::Spawner;\nuse embassy_time::Timer;\nuse hal::clocks::config::Div8;\nuse hal::clocks::periph_helpers::CTimerClockSel;\nuse hal::config::Config;\nuse hal::ctimer::pwm::{DualPwm, SetDutyCycle, SinglePwm};\nuse hal::ctimer::{self, CTimer};\nuse {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _};\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let mut config = Config::default();\n    config.clock_cfg.sirc.fro_lf_div = Div8::from_divisor(1);\n\n    let mut p = hal::init(config);\n\n    defmt::info!(\"Pwm example\");\n\n    let mut config = ctimer::Config::default();\n    config.source = CTimerClockSel::Clk1M;\n    let red_ctimer = CTimer::new(p.CTIMER4.reborrow(), config.clone()).unwrap();\n    let mut red = SinglePwm::new(red_ctimer, p.CTIMER4_CH2, p.CTIMER4_CH0, p.P2_14, Default::default()).unwrap();\n\n    let green_blue_ctimer = CTimer::new(p.CTIMER2.reborrow(), config).unwrap();\n    let green_blue = DualPwm::new(\n        green_blue_ctimer,\n        p.CTIMER2_CH2,\n        p.CTIMER2_CH3,\n        p.CTIMER2_CH0,\n        p.P2_22,\n        p.P2_23,\n        Default::default(),\n    )\n    .unwrap();\n\n    let (mut green, mut blue) = green_blue.split();\n    red.set_duty_cycle_fully_off().unwrap();\n    green.set_duty_cycle_fully_off().unwrap();\n    blue.set_duty_cycle_fully_off().unwrap();\n\n    loop {\n        defmt::info!(\"Red\");\n        for duty in (0u8..=100).chain((0..=100).rev()) {\n            red.set_duty_cycle_percent(duty).unwrap();\n            Timer::after_millis(10).await;\n        }\n\n        defmt::info!(\"Green\");\n        for duty in (0u8..=100).chain((0..=100).rev()) {\n            green.set_duty_cycle_percent(duty).unwrap();\n            Timer::after_millis(10).await;\n        }\n\n        defmt::info!(\"Blue\");\n        for duty in (0u8..=100).chain((0..=100).rev()) {\n            blue.set_duty_cycle_percent(duty).unwrap();\n            Timer::after_millis(10).await;\n        }\n    }\n}\n"
  },
  {
    "path": "examples/mcxa5xx/src/bin/reset-reason.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse embassy_executor::Spawner;\nuse hal::config::Config;\nuse hal::reset_reason::reset_reason;\nuse {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _};\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let config = Config::default();\n    let _p = hal::init(config);\n\n    for reason in reset_reason().into_iter() {\n        defmt::info!(\"Reset Reason: '{}'\", reason);\n    }\n}\n"
  },
  {
    "path": "examples/mcxa5xx/src/bin/rtc-alarm.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse embassy_executor::Spawner;\nuse embassy_mcxa::bind_interrupts;\nuse hal::peripherals::RTC0;\nuse hal::rtc::{DateTime, InterruptHandler, Month, Rtc, Weekday};\nuse {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _};\n\nbind_interrupts!(struct Irqs {\n    RTC => InterruptHandler<RTC0>;\n});\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let p = hal::init(hal::config::Config::default());\n\n    defmt::info!(\"=== RTC Alarm Example ===\");\n\n    let mut rtc = Rtc::new(p.RTC0, Irqs, Default::default()).unwrap();\n\n    let now = DateTime {\n        year: 2026,\n        month: Month::March,\n        dow: Weekday::Wednesday,\n        day: 11,\n        hour: 14,\n        minute: 30,\n        second: 42,\n    };\n\n    defmt::info!(\"Time set to: 2026-03-11 14:30:42\");\n    rtc.set_datetime(now).unwrap();\n\n    let mut alarm = now;\n    alarm.second += 10;\n\n    defmt::info!(\"Alarm set for: 2026-03-11 14:30:52 (+10 seconds)\");\n    rtc.wait_for_alarm(alarm).await.unwrap();\n\n    defmt::info!(\"Example complete - Test PASSED!\");\n}\n"
  },
  {
    "path": "examples/mcxa5xx/src/bin/spi-async.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse defmt::info;\nuse embassy_executor::Spawner;\nuse embassy_time::Timer;\nuse hal::bind_interrupts;\nuse hal::clocks::config::Div8;\nuse hal::config::Config;\nuse hal::peripherals::LPSPI2;\nuse hal::spi::controller::{self, InterruptHandler, Spi};\nuse {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _};\n\nbind_interrupts!(\n    struct Irqs {\n        LPSPI2 => InterruptHandler<LPSPI2>;\n    }\n);\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let mut config = Config::default();\n    config.clock_cfg.sirc.fro_lf_div = Div8::from_divisor(1);\n\n    let p = hal::init(config);\n\n    info!(\"SPI example\");\n\n    let mut config = controller::Config::default();\n    config.frequency = 1_000_000;\n    let mut spi = Spi::new_async(p.LPSPI2, p.P4_3, p.P4_5, p.P4_4, Irqs, config).unwrap();\n\n    let mut rx_buf = [0u8; 32];\n    let tx_buf = [0x55u8; 32];\n\n    loop {\n        spi.async_transfer(&mut rx_buf, &tx_buf).await.unwrap();\n        assert!(rx_buf.iter().all(|b| *b == 0x55));\n        Timer::after_secs(1).await;\n    }\n}\n"
  },
  {
    "path": "examples/mcxa5xx/src/bin/spi-blocking.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse defmt::info;\nuse embassy_executor::Spawner;\nuse embassy_time::Timer;\nuse hal::clocks::config::Div8;\nuse hal::config::Config;\nuse hal::spi::controller::{self, Spi};\nuse {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _};\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let mut config = Config::default();\n    config.clock_cfg.sirc.fro_lf_div = Div8::from_divisor(1);\n\n    let p = hal::init(config);\n\n    info!(\"SPI example\");\n\n    let mut config = controller::Config::default();\n    config.frequency = 1_000_000;\n    let mut spi = Spi::new_blocking(p.LPSPI2, p.P4_3, p.P4_5, p.P4_4, config).unwrap();\n\n    let mut rx_buf = [0u8; 32];\n    let tx_buf = [0x55u8; 32];\n\n    loop {\n        spi.blocking_transfer(&mut rx_buf, &tx_buf).unwrap();\n        assert!(rx_buf.iter().all(|b| *b == 0x55));\n        Timer::after_secs(1).await;\n    }\n}\n"
  },
  {
    "path": "examples/mcxa5xx/src/bin/spi-dma.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse defmt::info;\nuse embassy_executor::Spawner;\nuse embassy_time::Timer;\nuse hal::bind_interrupts;\nuse hal::clocks::config::Div8;\nuse hal::config::Config;\nuse hal::peripherals::LPSPI2;\nuse hal::spi::controller::{self, InterruptHandler, Spi};\nuse {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _};\n\nbind_interrupts!(\n    struct Irqs {\n        LPSPI2 => InterruptHandler<LPSPI2>;\n    }\n);\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let mut config = Config::default();\n    config.clock_cfg.sirc.fro_lf_div = Div8::from_divisor(1);\n\n    let p = hal::init(config);\n\n    info!(\"SPI example\");\n\n    let mut config = controller::Config::default();\n    config.frequency = 1_000_000;\n    let mut spi =\n        Spi::new_async_with_dma(p.LPSPI2, p.P4_3, p.P4_5, p.P4_4, p.DMA_CH0, p.DMA_CH1, Irqs, config).unwrap();\n\n    let mut rx_buf = [0u8; 32];\n    let tx_buf = [0xa5u8; 32];\n\n    loop {\n        // Same sized buffers\n        spi.async_transfer(&mut rx_buf, &tx_buf).await.unwrap();\n\n        // TX buffer larger than RX buffer\n        spi.async_transfer(&mut rx_buf[..31], &tx_buf).await.unwrap();\n\n        // RX buffer larger than TX buffer\n        spi.async_transfer(&mut rx_buf, &tx_buf[..31]).await.unwrap();\n\n        Timer::after_secs(1).await;\n    }\n}\n"
  },
  {
    "path": "examples/mcxa5xx/src/bin/trng.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse embassy_executor::Spawner;\nuse hal::bind_interrupts;\nuse hal::config::Config;\nuse hal::peripherals::TRNG0;\nuse hal::trng::{self, InterruptHandler, Trng};\nuse rand_core::RngCore;\nuse rand_core::block::BlockRngCore;\nuse {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _};\n\nbind_interrupts!(\n    struct Irqs {\n        TRNG0 => InterruptHandler<TRNG0>;\n    }\n);\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let config = Config::default();\n    let mut p = hal::init(config);\n\n    defmt::info!(\"TRNG example\");\n\n    let mut trng = Trng::new_blocking_128(p.TRNG0.reborrow());\n    let rand = trng.blocking_next_u32();\n    defmt::info!(\"128-bit {}\", rand);\n\n    drop(trng);\n\n    let mut trng = Trng::new_blocking_256(p.TRNG0.reborrow());\n    let rand = trng.blocking_next_u32();\n    defmt::info!(\"256-bit {}\", rand);\n\n    drop(trng);\n\n    let mut trng = Trng::new_blocking_512(p.TRNG0.reborrow());\n    let rand = trng.blocking_next_u32();\n    defmt::info!(\"512-bit {}\", rand);\n\n    drop(trng);\n\n    let config = trng::Config::default();\n    let mut trng = Trng::new_blocking_with_custom_config(p.TRNG0.reborrow(), config);\n\n    defmt::info!(\"========== BLOCKING ==========\");\n\n    defmt::info!(\"Generate 10 u32\");\n    for _ in 0..10 {\n        let rand = trng.blocking_next_u32();\n        defmt::info!(\"{}\", rand);\n    }\n\n    defmt::info!(\"Generate 10 u64\");\n    for _ in 0..10 {\n        let rand = trng.blocking_next_u64();\n        defmt::info!(\"{}\", rand);\n    }\n\n    let mut buf = [0_u8; 256];\n\n    defmt::info!(\"Generate 10 256-byte buffers\");\n    for _ in 0..10 {\n        trng.blocking_fill_bytes(&mut buf);\n        defmt::info!(\"{:02x}\", buf);\n    }\n\n    defmt::info!(\"RngCore\");\n\n    for _ in 0..10 {\n        defmt::info!(\"u32: {}\", trng.next_u32());\n        defmt::info!(\"u64: {}\", trng.next_u64());\n    }\n\n    defmt::info!(\"Generate full block\");\n\n    let mut block = [0_u32; 8];\n    trng.generate(&mut block);\n    defmt::info!(\"Block: {}\", block);\n\n    drop(trng);\n\n    defmt::info!(\"========== ASYNC ==========\");\n\n    let mut trng = Trng::new_with_custom_config(p.TRNG0.reborrow(), Irqs, config);\n\n    defmt::info!(\"Generate 10 u32\");\n    for _ in 0..10 {\n        let rand = trng.async_next_u32().await.unwrap();\n        defmt::info!(\"{}\", rand);\n    }\n\n    defmt::info!(\"Generate 10 u64\");\n    for _ in 0..10 {\n        let rand = trng.async_next_u64().await.unwrap();\n        defmt::info!(\"{}\", rand);\n    }\n\n    let mut buf = [0_u8; 256];\n\n    defmt::info!(\"Generate 10 256-byte buffers\");\n    for _ in 0..10 {\n        trng.async_fill_bytes(&mut buf).await.unwrap();\n        defmt::info!(\"{:02x}\", buf);\n    }\n\n    defmt::info!(\"RngCore\");\n\n    for _ in 0..10 {\n        defmt::info!(\"u32: {}\", trng.next_u32());\n        defmt::info!(\"u64: {}\", trng.next_u64());\n    }\n\n    defmt::info!(\"Generate full block\");\n\n    let mut block = [0_u32; 8];\n    trng.async_next_block(&mut block).await.unwrap();\n    defmt::info!(\"Block: {}\", block);\n}\n"
  },
  {
    "path": "examples/mcxa5xx/src/bin/wwdt_interrupt.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse embassy_executor::Spawner;\nuse embassy_time::{Duration, Timer};\nuse hal::bind_interrupts;\nuse hal::config::Config;\nuse hal::gpio::{DriveStrength, Level, Output, SlewRate};\nuse hal::peripherals::WWDT0;\nuse hal::wwdt::{InterruptHandler, Watchdog};\nuse {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _};\n\nbind_interrupts!(\n    struct Irqs {\n        WWDT0 => InterruptHandler<WWDT0>;\n    }\n);\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let config = Config::default();\n    let p = hal::init(config);\n\n    defmt::info!(\"Watchdog example\");\n\n    let wwdt_config = hal::wwdt::Config {\n        timeout: Duration::from_millis(1050),\n        warning: Some(Duration::from_micros(4000)),\n    };\n\n    let mut watchdog = Watchdog::new(p.WWDT0, Irqs, wwdt_config).unwrap();\n    let mut led = Output::new(p.P2_14, Level::High, DriveStrength::Normal, SlewRate::Fast);\n\n    // Set the LED high for 2 seconds so we know when we're about to start the watchdog\n    led.toggle();\n    Timer::after_secs(2).await;\n\n    // Set to watchdog to generate interrupt if it's not fed within 1.05 seconds, and start it.\n    // The warning interrupt will trigger 4ms before the timeout.\n    watchdog.start();\n    defmt::info!(\"Started the watchdog timer\");\n\n    // Blink once a second for 5 seconds, feed the watchdog timer once a second to avoid a reset\n    for _ in 1..=5 {\n        led.toggle();\n        Timer::after_millis(500).await;\n        led.toggle();\n        Timer::after_millis(500).await;\n        defmt::info!(\"Feeding watchdog\");\n        watchdog.feed();\n    }\n\n    defmt::info!(\"Stopped feeding, watchdog interrupt will be triggered in 1 second\");\n    // Blink 10 times per second, not feeding the watchdog.\n    // Watchdog timer will trigger after 1.0 second as warning is set to 50ms.\n    loop {\n        led.toggle();\n        Timer::after_millis(100).await;\n        led.toggle();\n        Timer::after_millis(100).await;\n    }\n}\n"
  },
  {
    "path": "examples/mcxa5xx/src/bin/wwdt_reset.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse embassy_executor::Spawner;\nuse embassy_time::{Duration, Timer};\nuse hal::bind_interrupts;\nuse hal::config::Config;\nuse hal::gpio::{DriveStrength, Level, Output, SlewRate};\nuse hal::peripherals::WWDT0;\nuse hal::wwdt::{InterruptHandler, Watchdog};\nuse {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _};\n\nbind_interrupts!(\n    struct Irqs {\n        WWDT0 => InterruptHandler<WWDT0>;\n    }\n);\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let config = Config::default();\n    let p = hal::init(config);\n\n    defmt::info!(\"Watchdog example\");\n\n    let wwdt_config = hal::wwdt::Config {\n        timeout: Duration::from_millis(4000),\n        warning: None,\n    };\n\n    let mut watchdog = Watchdog::new(p.WWDT0, Irqs, wwdt_config).unwrap();\n    let mut led = Output::new(p.P3_18, Level::High, DriveStrength::Normal, SlewRate::Fast);\n\n    // Set the LED high for 2 seconds so we know when we're about to start the watchdog\n    led.toggle();\n    Timer::after_secs(2).await;\n\n    // Set to watchdog to reset if it's not fed within 4 seconds, and start it\n    watchdog.start();\n    defmt::info!(\"Started the watchdog timer\");\n\n    // Blink once a second for 5 seconds, feed the watchdog timer once a second to avoid a reset\n    for _ in 1..=5 {\n        led.toggle();\n        Timer::after_millis(500).await;\n        led.toggle();\n        Timer::after_millis(500).await;\n        defmt::info!(\"Feeding watchdog\");\n        watchdog.feed();\n    }\n\n    defmt::info!(\"Stopped feeding, device will reset in 4 seconds\");\n    // Blink 10 times per second, not feeding the watchdog.\n    // The processor should reset in 4 seconds.\n    loop {\n        led.toggle();\n        Timer::after_millis(100).await;\n        led.toggle();\n        Timer::after_millis(100).await;\n    }\n}\n"
  },
  {
    "path": "examples/microchip/.cargo/config.toml",
    "content": "[target.thumbv7em-none-eabihf]\nrunner = 'probe-rs run --chip MEC1723N_B0_SZ'\n\nrustflags = [\n  # \"-C\", \"linker=flip-link\",\n  \"-C\", \"link-arg=-Tlink_ram.x\",\n  \"-C\", \"link-arg=-Tdefmt.x\",\n  # This is needed if your flash or ram addresses are not aligned to 0x10000 in memory.x\n  # See https://github.com/rust-embedded/cortex-m-quickstart/pull/95\n  \"-C\", \"link-arg=--nmagic\",\n]\n\n[build]\ntarget = \"thumbv7em-none-eabihf\" # Cortex-M4F\n\n[env]\nDEFMT_LOG = \"trace\"\n"
  },
  {
    "path": "examples/microchip/Cargo.toml",
    "content": "[package]\nname = \"embassy-microchip-examples\"\nversion = \"0.1.0\"\nedition = \"2024\"\nlicense = \"MIT OR Apache-2.0\"\npublish = false\n\n[dependencies]\ncortex-m = { version = \"0.7\", features = [\"critical-section-single-core\", \"inline-asm\"] }\ncortex-m-rt = { version = \"0.7\", features = [\"set-vtor\"] }\ndefmt = \"1.0\"\ndefmt-rtt = \"1.0\"\nembassy-executor = { version = \"0.10.0\", path = \"../../embassy-executor\", features = [\"platform-cortex-m\", \"executor-thread\", \"executor-interrupt\", \"defmt\"] }\nembassy-microchip = { version = \"0.1.0\", path = \"../../embassy-microchip\", features = [\"mec1723n_b0_sz\", \"rt\", \"defmt\", \"unstable-pac\"] }\nembassy-time = { version = \"0.5.1\", path = \"../../embassy-time\", features = [\"defmt\", \"defmt-timestamp-uptime\"] }\npanic-probe = { version = \"1.0.0\", features = [\"print-defmt\"] }\n\n[package.metadata.embassy]\nbuild = [\n  { target = \"thumbv7em-none-eabihf\", artifact-dir = \"out/examples/microchip\" }\n]\n"
  },
  {
    "path": "examples/microchip/build.rs",
    "content": "use std::env;\nuse std::fs::File;\nuse std::io::Write;\nuse std::path::PathBuf;\n\nfn main() {\n    // Put `memory.x` in our output directory and ensure it's\n    // on the linker search path.\n    let out = &PathBuf::from(env::var_os(\"OUT_DIR\").unwrap());\n    File::create(out.join(\"link_ram.x\"))\n        .unwrap()\n        .write_all(include_bytes!(\"link_ram.x\"))\n        .unwrap();\n    File::create(out.join(\"memory.x\"))\n        .unwrap()\n        .write_all(include_bytes!(\"memory.x\"))\n        .unwrap();\n    println!(\"cargo:rustc-link-search={}\", out.display());\n\n    // By default, Cargo will re-run a build script whenever\n    // any file in the project changes. By specifying `memory.x`\n    // here, we ensure the build script is only re-run when\n    // `memory.x` is changed.\n    println!(\"cargo:rerun-if-changed=memory.x\");\n    println!(\"cargo:rerun-if-changed=link_ram.x\");\n}\n"
  },
  {
    "path": "examples/microchip/link_ram.x",
    "content": "/* ##### EMBASSY NOTE\n    Originally from https://github.com/rust-embedded/cortex-m/blob/master/cortex-m-rt/link.x.in\n    Adjusted to put everything in RAM\n*/\n\n/* # Developer notes\n\n- Symbols that start with a double underscore (__) are considered \"private\"\n\n- Symbols that start with a single underscore (_) are considered \"semi-public\"; they can be\n  overridden in a user linker script, but should not be referred from user code (e.g. `extern \"C\" {\n  static mut __sbss }`).\n\n- `EXTERN` forces the linker to keep a symbol in the final binary. We use this to make sure a\n  symbol if not dropped if it appears in or near the front of the linker arguments and \"it's not\n  needed\" by any of the preceding objects (linker arguments)\n\n- `PROVIDE` is used to provide default values that can be overridden by a user linker script\n\n- On alignment: it's important for correctness that the VMA boundaries of both .bss and .data *and*\n  the LMA of .data are all 4-byte aligned. These alignments are assumed by the RAM initialization\n  routine. There's also a second benefit: 4-byte aligned boundaries means that you won't see\n  \"Address (..) is out of bounds\" in the disassembly produced by `objdump`.\n*/\n\n/* Provides information about the memory layout of the device */\n/* This will be provided by the user (see `memory.x`) or by a Board Support Crate */\nINCLUDE memory.x\n\n/* # Entry point = reset vector */\nEXTERN(__RESET_VECTOR);\nEXTERN(Reset);\nENTRY(Reset);\n\n/* # Exception vectors */\n/* This is effectively weak aliasing at the linker level */\n/* The user can override any of these aliases by defining the corresponding symbol themselves (cf.\n   the `exception!` macro) */\nEXTERN(__EXCEPTIONS); /* depends on all the these PROVIDED symbols */\n\nEXTERN(DefaultHandler);\n\nPROVIDE(NonMaskableInt = DefaultHandler);\nEXTERN(HardFaultTrampoline);\nPROVIDE(MemoryManagement = DefaultHandler);\nPROVIDE(BusFault = DefaultHandler);\nPROVIDE(UsageFault = DefaultHandler);\nPROVIDE(SecureFault = DefaultHandler);\nPROVIDE(SVCall = DefaultHandler);\nPROVIDE(DebugMonitor = DefaultHandler);\nPROVIDE(PendSV = DefaultHandler);\nPROVIDE(SysTick = DefaultHandler);\n\nPROVIDE(DefaultHandler = DefaultHandler_);\nPROVIDE(HardFault = HardFault_);\n\n/* # Interrupt vectors */\nEXTERN(__INTERRUPTS); /* `static` variable similar to `__EXCEPTIONS` */\n\n/* # Pre-initialization function */\n/* If the user overrides this using the `pre_init!` macro or by creating a `__pre_init` function,\n   then the function this points to will be called before the RAM is initialized. */\nPROVIDE(__pre_init = DefaultPreInit);\n\n/* # Sections */\nSECTIONS\n{\n  PROVIDE(_ram_start = ORIGIN(RAM));\n  PROVIDE(_ram_end = ORIGIN(RAM) + LENGTH(RAM));\n  PROVIDE(_stack_start = _ram_end);\n\n  /* ## Sections in RAM */\n  /* ### Vector table */\n  .vector_table ORIGIN(RAM) :\n  {\n    __vector_table = .;\n\n    /* Initial Stack Pointer (SP) value.\n     * We mask the bottom three bits to force 8-byte alignment.\n     * Despite having an assert for this later, it's possible that a separate\n     * linker script could override _stack_start after the assert is checked.\n     */\n    LONG(_stack_start & 0xFFFFFFF8);\n\n    /* Reset vector */\n    KEEP(*(.vector_table.reset_vector)); /* this is the `__RESET_VECTOR` symbol */\n\n    /* Exceptions */\n    __exceptions = .; /* start of exceptions */\n    KEEP(*(.vector_table.exceptions)); /* this is the `__EXCEPTIONS` symbol */\n    __eexceptions = .; /* end of exceptions */\n\n    /* Device specific interrupts */\n    KEEP(*(.vector_table.interrupts)); /* this is the `__INTERRUPTS` symbol */\n  } > RAM\n\n  PROVIDE(_stext = ADDR(.vector_table) + SIZEOF(.vector_table));\n\n  /* ### .text */\n  .text _stext :\n  {\n    __stext = .;\n    *(.Reset);\n\n    *(.text .text.*);\n\n    /* The HardFaultTrampoline uses the `b` instruction to enter `HardFault`,\n       so must be placed close to it. */\n    *(.HardFaultTrampoline);\n    *(.HardFault.*);\n\n    . = ALIGN(4); /* Pad .text to the alignment to workaround overlapping load section bug in old lld */\n    __etext = .;\n  } > RAM\n\n  /* ### .rodata */\n  .rodata : ALIGN(4)\n  {\n    . = ALIGN(4);\n    __srodata = .;\n    *(.rodata .rodata.*);\n\n    /* 4-byte align the end (VMA) of this section.\n       This is required by LLD to ensure the LMA of the following .data\n       section will have the correct alignment. */\n    . = ALIGN(4);\n    __erodata = .;\n  } > RAM\n\n  /* ## Sections in RAM */\n  /* ### .data */\n  .data : ALIGN(4)\n  {\n    . = ALIGN(4);\n    __sdata = .;\n    __edata = .; /* RAM: By setting __sdata=__edata cortex-m-rt has to copy 0 bytes as .data is already in RAM */\n\n    *(.data .data.*);\n    . = ALIGN(4); /* 4-byte align the end (VMA) of this section */\n  } > RAM\n  /* Allow sections from user `memory.x` injected using `INSERT AFTER .data` to\n   * use the .data loading mechanism by pushing __edata. Note: do not change\n   * output region or load region in those user sections! */\n  /* Link from RAM: Disabled, now __sdata == __edata\n  . = ALIGN(4);\n  __edata = .;\n  */\n\n  /* LMA of .data */\n  __sidata = LOADADDR(.data);\n\n  /* ### .gnu.sgstubs\n     This section contains the TrustZone-M veneers put there by the Arm GNU linker. */\n  /* Security Attribution Unit blocks must be 32 bytes aligned. */\n  /* Note that this pads the RAM usage to 32 byte alignment. */\n  .gnu.sgstubs : ALIGN(32)\n  {\n    . = ALIGN(32);\n    __veneer_base = .;\n    *(.gnu.sgstubs*)\n    . = ALIGN(32);\n  } > RAM\n  /* Place `__veneer_limit` outside the `.gnu.sgstubs` section because veneers are\n   * always inserted last in the section, which would otherwise be _after_ the `__veneer_limit` symbol.\n   */\n  . = ALIGN(32);\n  __veneer_limit = .;\n\n  /* ### .bss */\n  .bss (NOLOAD) : ALIGN(4)\n  {\n    . = ALIGN(4);\n    __sbss = .;\n    *(.bss .bss.*);\n    *(COMMON); /* Uninitialized C statics */\n    . = ALIGN(4); /* 4-byte align the end (VMA) of this section */\n  } > RAM\n  /* Allow sections from user `memory.x` injected using `INSERT AFTER .bss` to\n   * use the .bss zeroing mechanism by pushing __ebss. Note: do not change\n   * output region or load region in those user sections! */\n  . = ALIGN(4);\n  __ebss = .;\n\n  /* ### .uninit */\n  .uninit (NOLOAD) : ALIGN(4)\n  {\n    . = ALIGN(4);\n    __suninit = .;\n    *(.uninit .uninit.*);\n    . = ALIGN(4);\n    __euninit = .;\n  } > RAM\n\n  /* Place the heap right after `.uninit` in RAM */\n  PROVIDE(__sheap = __euninit);\n\n  /* ## .got */\n  /* Dynamic relocations are unsupported. This section is only used to detect relocatable code in\n     the input files and raise an error if relocatable code is found */\n  .got (NOLOAD) :\n  {\n    KEEP(*(.got .got.*));\n  }\n\n  /* ## Discarded sections */\n  /DISCARD/ :\n  {\n    /* Unused exception related info that only wastes space */\n    *(.ARM.exidx);\n    *(.ARM.exidx.*);\n    *(.ARM.extab.*);\n  }\n}\n\n/* Do not exceed this mark in the error messages below                                    | */\n/* # Alignment checks */\nASSERT(ORIGIN(RAM) % 4 == 0, \"\nERROR(cortex-m-rt): the start of the RAM region must be 4-byte aligned\");\n\nASSERT(__sdata % 4 == 0 && __edata % 4 == 0, \"\nBUG(cortex-m-rt): .data is not 4-byte aligned\");\n\nASSERT(__sidata % 4 == 0, \"\nBUG(cortex-m-rt): the LMA of .data is not 4-byte aligned\");\n\nASSERT(__sbss % 4 == 0 && __ebss % 4 == 0, \"\nBUG(cortex-m-rt): .bss is not 4-byte aligned\");\n\nASSERT(__sheap % 4 == 0, \"\nBUG(cortex-m-rt): start of .heap is not 4-byte aligned\");\n\nASSERT(_stack_start % 8 == 0, \"\nERROR(cortex-m-rt): stack start address is not 8-byte aligned.\nIf you have set _stack_start, check it's set to an address which is a multiple of 8 bytes.\nIf you haven't, stack starts at the end of RAM by default. Check that both RAM\norigin and length are set to multiples of 8 in the `memory.x` file.\");\n\n/* # Position checks */\n\n/* ## .vector_table\n *\n * If the *start* of exception vectors is not 8 bytes past the start of the\n * vector table, then we somehow did not place the reset vector, which should\n * live 4 bytes past the start of the vector table.\n */\nASSERT(__exceptions == ADDR(.vector_table) + 0x8, \"\nBUG(cortex-m-rt): the reset vector is missing\");\n\nASSERT(__eexceptions == ADDR(.vector_table) + 0x40, \"\nBUG(cortex-m-rt): the exception vectors are missing\");\n\nASSERT(SIZEOF(.vector_table) > 0x40, \"\nERROR(cortex-m-rt): The interrupt vectors are missing.\nPossible solutions, from most likely to less likely:\n- Link to a svd2rust generated device crate\n- Check that you actually use the device/hal/bsp crate in your code\n- Disable the 'device' feature of cortex-m-rt to build a generic application (a dependency\nmay be enabling it)\n- Supply the interrupt handlers yourself. Check the documentation for details.\");\n\n/* ## .text */\nASSERT(ADDR(.vector_table) + SIZEOF(.vector_table) <= _stext, \"\nERROR(cortex-m-rt): The .text section can't be placed inside the .vector_table section\nSet _stext to an address greater than the end of .vector_table (See output of `nm`)\");\n\nASSERT(_stext + SIZEOF(.text) < ORIGIN(RAM) + LENGTH(RAM), \"\nERROR(cortex-m-rt): The .text section must be placed inside the RAM memory.\nSet _stext to an address smaller than 'ORIGIN(RAM) + LENGTH(RAM)'\");\n\n/* # Other checks */\nASSERT(SIZEOF(.got) == 0, \"\nERROR(cortex-m-rt): .got section detected in the input object files\nDynamic relocations are not supported. If you are linking to C code compiled using\nthe 'cc' crate then modify your build script to compile the C code _without_\nthe -fPIC flag. See the documentation of the `cc::Build.pic` method for details.\");\n/* Do not exceed this mark in the error messages above                                    | */\n\n/* Provides weak aliases (cf. PROVIDED) for device specific interrupt handlers */\n/* This will usually be provided by a device crate generated using svd2rust (see `device.x`) */\nINCLUDE device.x\n"
  },
  {
    "path": "examples/microchip/memory.x",
    "content": "MEMORY {\n\tRAM      : ORIGIN = 0x000c0000, LENGTH = 384K\n}\n"
  },
  {
    "path": "examples/microchip/src/bin/blinky.rs",
    "content": "#![no_main]\n#![no_std]\n\nuse defmt::info;\nuse embassy_executor::Spawner;\nuse embassy_microchip::gpio::{Level, Output};\nuse embassy_time::Timer;\nuse {defmt_rtt as _, panic_probe as _};\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let p = embassy_microchip::init(Default::default());\n\n    info!(\"Hello, world!\");\n\n    let mut led1 = Output::new(p.GPIO157, Level::Low);\n    let mut led2 = Output::new(p.GPIO153, Level::High);\n\n    loop {\n        info!(\"toggle leds\");\n        led1.toggle();\n        led2.toggle();\n        Timer::after_secs(1).await;\n    }\n}\n"
  },
  {
    "path": "examples/microchip/src/bin/button-async.rs",
    "content": "#![no_main]\n#![no_std]\n\nuse defmt::info;\nuse embassy_executor::Spawner;\nuse embassy_microchip::gpio::{Input, Pull};\nuse {defmt_rtt as _, panic_probe as _};\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    info!(\"Hello, world!\");\n    let p = embassy_microchip::init(Default::default());\n    let mut btn = Input::new(p.GPIO141, Pull::None);\n\n    loop {\n        if btn.is_high() {\n            info!(\"Press button...\");\n        } else {\n            info!(\"Release button...\");\n        }\n        btn.wait_for_any_edge().await;\n    }\n}\n"
  },
  {
    "path": "examples/microchip/src/bin/button.rs",
    "content": "#![no_main]\n#![no_std]\n\nuse defmt::info;\nuse embassy_executor::Spawner;\nuse embassy_microchip::gpio::{Input, Pull};\nuse embassy_time::Timer;\nuse {defmt_rtt as _, panic_probe as _};\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let p = embassy_microchip::init(Default::default());\n\n    info!(\"Hello, world!\");\n    let btn = Input::new(p.GPIO141, Pull::None);\n\n    loop {\n        info!(\"Button State: {}\", btn.get_level());\n        Timer::after_secs(1).await;\n    }\n}\n"
  },
  {
    "path": "examples/microchip/src/bin/i2c-async.rs",
    "content": "#![no_main]\n#![no_std]\n\nuse defmt::info;\nuse embassy_executor::Spawner;\nuse embassy_microchip::i2c::{I2c, InterruptHandler};\nuse embassy_microchip::{bind_interrupts, peripherals};\nuse embassy_time::Timer;\nuse {defmt_rtt as _, panic_probe as _};\n\nbind_interrupts!(\n    struct Irqs {\n        I2CSMB0 => InterruptHandler<peripherals::SMB0>;\n    }\n);\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let p = embassy_microchip::init(Default::default());\n\n    info!(\"Hello, world!\");\n\n    let mut i2c = I2c::new_async(p.SMB0, p.GPIO73, p.GPIO72, Irqs, Default::default());\n\n    Timer::after_secs(1).await;\n\n    let mut read = [0_u8; 1];\n\n    loop {\n        for addr in (0..0x7f_u8).into_iter() {\n            if i2c.read_async(addr, &mut read).await.is_ok() {\n                info!(\"Found device at addr {:02x}\", addr);\n            }\n        }\n\n        Timer::after_secs(1).await;\n    }\n}\n"
  },
  {
    "path": "examples/microchip/src/bin/i2c.rs",
    "content": "#![no_main]\n#![no_std]\n\nuse defmt::info;\nuse embassy_executor::Spawner;\nuse embassy_microchip::i2c::I2c;\nuse embassy_time::Timer;\nuse {defmt_rtt as _, panic_probe as _};\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let p = embassy_microchip::init(Default::default());\n\n    info!(\"Hello, world!\");\n\n    let mut i2c = I2c::new_blocking(p.SMB0, p.GPIO73, p.GPIO72, Default::default());\n\n    Timer::after_secs(1).await;\n\n    let mut read = [0_u8; 1];\n\n    loop {\n        for addr in (0..0x7f_u8).into_iter() {\n            if i2c.blocking_read(addr, &mut read).is_ok() {\n                info!(\"Found device at addr {:02x}\", addr);\n            }\n        }\n\n        Timer::after_secs(1).await;\n    }\n}\n"
  },
  {
    "path": "examples/microchip/src/bin/pwm.rs",
    "content": "#![no_main]\n#![no_std]\n\nuse defmt::info;\nuse embassy_executor::Spawner;\nuse embassy_microchip::gpio::{Input, Pull};\nuse embassy_microchip::pwm::{Pwm, SetDutyCycle};\nuse {defmt_rtt as _, panic_probe as _};\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let p = embassy_microchip::init(Default::default());\n\n    info!(\"Hello, world!\");\n\n    let mut pwm = Pwm::new(p.PWM0, p.GPIO53, Default::default());\n    let mut btn = Input::new(p.GPIO141, Pull::None);\n\n    let max_duty = pwm.max_duty_cycle();\n    let step = max_duty / 10;\n\n    loop {\n        for duty in (1..max_duty).step_by(step.into()) {\n            pwm.set_duty_cycle(duty).unwrap();\n\n            info!(\"Duty cycle sent to {}. Press button for next step...\", duty);\n            btn.wait_for_low().await;\n            btn.wait_for_high().await;\n        }\n    }\n}\n"
  },
  {
    "path": "examples/microchip/src/bin/uart_async.rs",
    "content": "#![no_main]\n#![no_std]\n\nuse defmt::info;\nuse embassy_executor::Spawner;\nuse embassy_microchip::uart::{self, Uart};\nuse embassy_microchip::{bind_interrupts, peripherals};\nuse {defmt_rtt as _, panic_probe as _};\n\nbind_interrupts!(\n    struct Irqs {\n        UART1 => uart::InterruptHandler<peripherals::UART1>;\n    }\n);\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let p = embassy_microchip::init(Default::default());\n    let config = uart::Config::default();\n    let uart = Uart::new_async(p.UART1, p.GPIO171, p.GPIO170, Irqs, config).unwrap();\n\n    let (mut rx, mut tx) = uart.split();\n\n    info!(\"Starting async UART example.\");\n\n    let description = b\"\\\n    Before you appear the Doors of Durin, providing passage into Moria.\\n\\\n    Upon the doors is a single Elvish inscription, roughly translated as:\\n\\\n    Speak, friend, and enter.\\n\\n\\\n    What do you say?\\n\\\n    \";\n    tx.write(description).await;\n\n    // Read characters from player until buffer is full\n    let mut friend = [0; 6];\n    rx.read(&mut friend).await.unwrap();\n\n    // Did they speak the Elvish word for friend?\n    if let Ok(friend) = str::from_utf8(&friend)\n        && friend == \"Mellon\"\n    {\n        tx.write(b\"The doors begin to open.\\n\").await;\n    } else {\n        tx.write(b\"The doors remain closed.\\n\").await;\n    }\n\n    tx.flush().await;\n    info!(\"Async UART example complete.\");\n}\n"
  },
  {
    "path": "examples/microchip/src/bin/uart_blocking.rs",
    "content": "#![no_main]\n#![no_std]\n\nuse defmt::info;\nuse embassy_executor::Spawner;\nuse embassy_microchip::uart::{self, Uart};\nuse {defmt_rtt as _, panic_probe as _};\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let p = embassy_microchip::init(Default::default());\n    let config = uart::Config::default();\n    let uart = Uart::new_blocking(p.UART1, p.GPIO171, p.GPIO170, config).unwrap();\n\n    let (mut rx, mut tx) = uart.split();\n\n    info!(\"Starting blocking UART example.\");\n\n    let description = b\"\\\n    Before you appear the Doors of Durin, providing passage into Moria.\\n\\\n    Upon the doors is a single Elvish inscription, roughly translated as:\\n\\\n    Speak, friend, and enter.\\n\\n\\\n    What do you say?\\n\\\n    \";\n    tx.blocking_write(description);\n\n    // Read characters from player until buffer is full\n    let mut friend = [0; 6];\n    rx.blocking_read(&mut friend).unwrap();\n\n    // Did they speak the Elvish word for friend?\n    if let Ok(friend) = str::from_utf8(&friend)\n        && friend == \"Mellon\"\n    {\n        tx.blocking_write(b\"The doors begin to open.\\n\");\n    } else {\n        tx.blocking_write(b\"The doors remain closed.\\n\");\n    }\n\n    tx.blocking_flush();\n    info!(\"Blocking UART example complete.\");\n}\n"
  },
  {
    "path": "examples/mimxrt1011/.cargo/config.toml",
    "content": "[target.thumbv7em-none-eabihf]\nrunner = 'probe-rs run --chip MIMXRT1010'\n\n[build]\ntarget = \"thumbv7em-none-eabihf\" # Cortex-M7\n\n[env]\nDEFMT_LOG = \"trace\"\n"
  },
  {
    "path": "examples/mimxrt1011/Cargo.toml",
    "content": "[package]\nname = \"embassy-imxrt1011-examples\"\nversion = \"0.1.0\"\nedition = \"2024\"\nlicense = \"MIT OR Apache-2.0\"\npublish = false\n\n[dependencies]\ncortex-m = { version = \"0.7.7\", features = [\"inline-asm\", \"critical-section-single-core\"] }\ncortex-m-rt = \"0.7.3\"\ndefmt = \"1.0.1\"\ndefmt-rtt = \"1.0.0\"\n\nembassy-executor = { version = \"0.10.0\", path = \"../../embassy-executor\", features = [\"platform-cortex-m\", \"executor-thread\", \"executor-interrupt\", \"defmt\"] }\nembassy-futures = { version = \"0.1.2\", path = \"../../embassy-futures\" }\nembassy-nxp = { version = \"0.1.0\", path = \"../../embassy-nxp\", features = [\"defmt\", \"mimxrt1011\", \"unstable-pac\", \"time-driver-pit\"] }\nembassy-time = { version = \"0.5.1\", path = \"../../embassy-time\", features = [\"defmt\", ] } # \"defmt-timestamp-uptime\" # RT1011 hard faults currently with this enabled.\nembassy-sync = { version = \"0.8.0\", path = \"../../embassy-sync\", features = [\"defmt\"] }\nembedded-hal-1 = { package = \"embedded-hal\", version = \"1.0\" }\nembedded-hal-async = \"1.0.0\"\n\nimxrt-boot-gen = { version = \"0.3.4\", features = [\"imxrt1010\"] }\npanic-probe = { version = \"1.0.0\", features = [\"print-defmt\"] }\npanic-semihosting = \"0.6.0\"\n\n[build-dependencies]\nimxrt-rt = { version = \"0.1.7\", features = [\"device\"] }\n\n[profile.release]\ndebug = 2\n\n[package.metadata.embassy]\nbuild = [\n  { target = \"thumbv7em-none-eabihf\", artifact-dir = \"out/examples/mimxrt1011\" }\n]\n"
  },
  {
    "path": "examples/mimxrt1011/build.rs",
    "content": "use imxrt_rt::{Family, RuntimeBuilder};\n\nfn main() {\n    // The IMXRT1010-EVK technically has 128M of flash, but we only ever use 8MB so that the examples\n    // will build fine on the Adafruit Metro M7 boards.\n    RuntimeBuilder::from_flexspi(Family::Imxrt1010, 8 * 1024 * 1024)\n        .build()\n        .unwrap();\n\n    println!(\"cargo:rustc-link-arg-bins=--nmagic\");\n    println!(\"cargo:rustc-link-arg-bins=-Tdefmt.x\");\n    // Not link.x, as imxrt-rt needs to do some special things\n    println!(\"cargo:rustc-link-arg-bins=-Timxrt-link.x\");\n}\n"
  },
  {
    "path": "examples/mimxrt1011/src/bin/blinky.rs",
    "content": "//! This example works on the following boards:\n//! - IMXRT1010-EVK\n//! - Adafruit Metro M7 (with microSD or with AirLift), requires an external button\n//! - Makerdiary iMX RT1011 Nano Kit (TODO: currently untested, please change this)\n//!\n//! Although beware you will need to change the GPIO pins being used (scroll down).\n\n#![no_std]\n#![no_main]\n\nuse defmt::info;\nuse embassy_executor::Spawner;\nuse embassy_nxp::gpio::{Level, Output};\nuse embassy_time::Timer;\n// Must include `embassy_imxrt1011_examples` to ensure the FCB gets linked.\nuse {defmt_rtt as _, embassy_imxrt1011_examples as _, panic_probe as _};\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) -> ! {\n    let p = embassy_nxp::init(Default::default());\n    info!(\"Hello world!\");\n\n    /* Pick the pins to use depending on your board. */\n\n    // IMXRT1010-EVK\n    //\n    // LED (D25)\n    let led = p.GPIO_11;\n\n    // Adafruit Metro M7 (both microSD and AirLift variants)\n    //\n    // The LED is connected to D13 on the board.\n    // let led = p.GPIO_03;\n\n    // Makerdiary iMX RT1011 Nano Kit\n    //\n    // LED0\n    // let led = p.GPIO_SD_04;\n\n    let mut led = Output::new(led, Level::Low);\n\n    loop {\n        Timer::after_millis(500).await;\n\n        info!(\"Toggle\");\n        led.toggle();\n    }\n}\n"
  },
  {
    "path": "examples/mimxrt1011/src/bin/button.rs",
    "content": "//! This example works on the following boards:\n//! - IMXRT1010-EVK\n//! - Adafruit Metro M7 (with microSD or with AirLift), requires an external button\n//! - Makerdiary iMX RT1011 Nano Kit (TODO: currently untested, please change this)\n//!\n//! Although beware you will need to change the GPIO pins being used (scroll down).\n\n#![no_std]\n#![no_main]\n\nuse defmt::info;\nuse embassy_executor::Spawner;\nuse embassy_nxp::gpio::{Input, Level, Output, Pull};\n// Must include `embassy_imxrt1011_examples` to ensure the FCB gets linked.\nuse {defmt_rtt as _, embassy_imxrt1011_examples as _, panic_probe as _};\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) -> ! {\n    let p = embassy_nxp::init(Default::default());\n    info!(\"Hello world!\");\n\n    /* Pick the pins to use depending on your board. */\n\n    // IMXRT1010-EVK\n    //\n    // LED (D25) and user button (SW4)\n    let (led, button) = (p.GPIO_11, p.GPIO_SD_05);\n\n    // Adafruit Metro M7 (both microSD and AirLift variants)\n    //\n    // The LED is connected to D13 on the board.\n    //\n    // In particular the Metro M7 has no board user buttons, so you will need to connect a button.\n    // Any other GPIO pin can be used. GPIO_04 is used for example since it is on pin D12.\n    // let (led, button) = (p.GPIO_03, p.GPIO_04);\n\n    // Makerdiary iMX RT1011 Nano Kit\n    //\n    // LED0 and user button.\n    // let (led, button) = (p.GPIO_SD_04, p.GPIO_SD_03);\n\n    let mut button = Input::new(button, Pull::Up100K);\n    let mut led = Output::new(led, Level::Low);\n    led.set_high();\n\n    loop {\n        button.wait_for_falling_edge().await;\n\n        info!(\"Toggled\");\n        led.toggle();\n\n        // The RT1010EVK has a 100 nF debouncing capacitor which results in false positive events\n        // when listening for a falling edge in a loop, wait for the rising edge and then wait for\n        // stabilization.\n        button.wait_for_rising_edge().await;\n\n        // Stabilization.\n        for _ in 0..100_000 {\n            cortex_m::asm::nop();\n        }\n    }\n}\n"
  },
  {
    "path": "examples/mimxrt1011/src/lib.rs",
    "content": "//! FlexSPI configuration block (FCB) for iMXRT1011 boards.\n//!\n//! This is a generic FCB that should work with most QSPI flash.\n\n#![no_std]\n\nuse imxrt_boot_gen::flexspi;\nuse imxrt_boot_gen::flexspi::opcodes::sdr::*;\nuse imxrt_boot_gen::flexspi::{\n    ColumnAddressWidth, Command, DeviceModeConfiguration, FlashPadType, Instr, LookupTable, Pads,\n    ReadSampleClockSource, Sequence, SequenceBuilder, SerialClockFrequency, SerialFlashRegion,\n    WaitTimeConfigurationCommands,\n};\nuse imxrt_boot_gen::serial_flash::nor;\n\n/// While the IMXRT1010-EVK and Makerdiary iMX RT1011 Nano Kit have 128MBit of flash we limit to 64Mbit\n/// to allow the Metro M7 boards to use the same FCB configuration.\nconst DENSITY_BITS: u32 = 64 * 1024 * 1024;\nconst DENSITY_BYTES: u32 = DENSITY_BITS / 8;\n\nconst SEQ_READ: Sequence = SequenceBuilder::new()\n    .instr(Instr::new(CMD, Pads::One, 0xEB))\n    .instr(Instr::new(RADDR, Pads::Four, 0x18))\n    .instr(Instr::new(DUMMY, Pads::Four, 0x06))\n    .instr(Instr::new(READ, Pads::Four, 0x04))\n    .build();\n\nconst SEQ_READ_STATUS: Sequence = SequenceBuilder::new()\n    .instr(Instr::new(CMD, Pads::One, 0x05))\n    .instr(Instr::new(READ, Pads::One, 0x01))\n    .build();\n\nconst SEQ_WRITE_ENABLE: Sequence = SequenceBuilder::new().instr(Instr::new(CMD, Pads::One, 0x06)).build();\n\nconst SEQ_ERASE_SECTOR: Sequence = SequenceBuilder::new()\n    .instr(Instr::new(CMD, Pads::One, 0x20))\n    .instr(Instr::new(RADDR, Pads::One, 0x18))\n    .build();\n\nconst SEQ_PAGE_PROGRAM: Sequence = SequenceBuilder::new()\n    .instr(Instr::new(CMD, Pads::One, 0x02))\n    .instr(Instr::new(RADDR, Pads::One, 0x18))\n    .instr(Instr::new(WRITE, Pads::One, 0x04))\n    .build();\n\nconst SEQ_CHIP_ERASE: Sequence = SequenceBuilder::new().instr(Instr::new(CMD, Pads::One, 0x60)).build();\n\nconst LUT: LookupTable = LookupTable::new()\n    .command(Command::Read, SEQ_READ)\n    .command(Command::ReadStatus, SEQ_READ_STATUS)\n    .command(Command::WriteEnable, SEQ_WRITE_ENABLE)\n    .command(Command::EraseSector, SEQ_ERASE_SECTOR)\n    .command(Command::PageProgram, SEQ_PAGE_PROGRAM)\n    .command(Command::ChipErase, SEQ_CHIP_ERASE);\n\nconst COMMON_CONFIGURATION_BLOCK: flexspi::ConfigurationBlock = flexspi::ConfigurationBlock::new(LUT)\n    .read_sample_clk_src(ReadSampleClockSource::LoopbackFromDQSPad)\n    .cs_hold_time(0x03)\n    .cs_setup_time(0x03)\n    .column_address_width(ColumnAddressWidth::OtherDevices)\n    .device_mode_configuration(DeviceModeConfiguration::Disabled)\n    .wait_time_cfg_commands(WaitTimeConfigurationCommands::disable())\n    .flash_size(SerialFlashRegion::A1, DENSITY_BYTES)\n    .serial_clk_freq(SerialClockFrequency::MHz120)\n    .serial_flash_pad_type(FlashPadType::Quad);\n\npub const SERIAL_NOR_CONFIGURATION_BLOCK: nor::ConfigurationBlock =\n    nor::ConfigurationBlock::new(COMMON_CONFIGURATION_BLOCK)\n        .page_size(256)\n        .sector_size(4096)\n        .ip_cmd_serial_clk_freq(nor::SerialClockFrequency::MHz30);\n\n#[unsafe(no_mangle)]\n#[cfg_attr(all(target_arch = \"arm\", target_os = \"none\"), unsafe(link_section = \".fcb\"))]\npub static FLEXSPI_CONFIGURATION_BLOCK: nor::ConfigurationBlock = SERIAL_NOR_CONFIGURATION_BLOCK;\n"
  },
  {
    "path": "examples/mimxrt1062-evk/.cargo/config.toml",
    "content": "[target.thumbv7em-none-eabihf]\nrunner = 'probe-rs run --chip MIMXRT1060'\n\n[build]\ntarget = \"thumbv7em-none-eabihf\" # Cortex-M7\n\n[env]\nDEFMT_LOG = \"trace\"\n"
  },
  {
    "path": "examples/mimxrt1062-evk/Cargo.toml",
    "content": "[package]\nname = \"embassy-imxrt1062-evk-examples\"\nversion = \"0.1.0\"\nedition = \"2024\"\nlicense = \"MIT OR Apache-2.0\"\npublish = false\n\n[dependencies]\ncortex-m = { version = \"0.7.7\", features = [\"inline-asm\", \"critical-section-single-core\"] }\ncortex-m-rt = \"0.7.3\"\ndefmt = \"1.0.1\"\ndefmt-rtt = \"1.0.0\"\n\nembassy-executor = { version = \"0.10.0\", path = \"../../embassy-executor\", features = [\"platform-cortex-m\", \"executor-thread\", \"executor-interrupt\", \"defmt\"] }\nembassy-futures = { version = \"0.1.2\", path = \"../../embassy-futures\" }\nembassy-nxp = { version = \"0.1.0\", path = \"../../embassy-nxp\", features = [\"defmt\", \"mimxrt1062\", \"unstable-pac\", \"time-driver-pit\"] }\nembassy-time = { version = \"0.5.1\", path = \"../../embassy-time\", features = [\"defmt\", ] } # \"defmt-timestamp-uptime\"\nembassy-sync = { version = \"0.8.0\", path = \"../../embassy-sync\", features = [\"defmt\"] }\nembedded-hal-1 = { package = \"embedded-hal\", version = \"1.0\" }\nembedded-hal-async = \"1.0.0\"\n\nimxrt-boot-gen = { version = \"0.3.4\", features = [\"imxrt1060\"] }\npanic-probe = { version = \"1.0.0\", features = [\"print-defmt\"] }\npanic-semihosting = \"0.6.0\"\n\n[build-dependencies]\nimxrt-rt = { version = \"0.1.7\", features = [\"device\"] }\n\n[profile.release]\ndebug = 2\n\n[package.metadata.embassy]\nbuild = [\n  { target = \"thumbv7em-none-eabihf\", artifact-dir = \"out/examples/mimxrt1062-evk\" }\n]\n"
  },
  {
    "path": "examples/mimxrt1062-evk/build.rs",
    "content": "use imxrt_rt::{Family, RuntimeBuilder};\n\nfn main() {\n    RuntimeBuilder::from_flexspi(Family::Imxrt1060, 8 * 1024 * 1024)\n        .build()\n        .unwrap();\n\n    println!(\"cargo:rustc-link-arg-bins=--nmagic\");\n    println!(\"cargo:rustc-link-arg-bins=-Tdefmt.x\");\n    // Not link.x, as imxrt-rt needs to do some special things\n    println!(\"cargo:rustc-link-arg-bins=-Timxrt-link.x\");\n}\n"
  },
  {
    "path": "examples/mimxrt1062-evk/src/bin/blinky.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse defmt::info;\nuse embassy_executor::Spawner;\nuse embassy_nxp::gpio::{Level, Output};\nuse embassy_time::Timer;\n// Must include `embassy_imxrt1062_evk_examples` to ensure the FCB gets linked.\nuse {defmt_rtt as _, embassy_imxrt1062_evk_examples as _, panic_probe as _};\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) -> ! {\n    let p = embassy_nxp::init(Default::default());\n    info!(\"Hello world!\");\n\n    let led = p.GPIO_AD_B0_08;\n    let mut led = Output::new(led, Level::Low);\n\n    loop {\n        Timer::after_millis(500).await;\n\n        info!(\"Toggle\");\n        led.toggle();\n    }\n}\n"
  },
  {
    "path": "examples/mimxrt1062-evk/src/bin/button.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse defmt::info;\nuse embassy_executor::Spawner;\nuse embassy_nxp::gpio::{Input, Level, Output, Pull};\nuse {defmt_rtt as _, embassy_imxrt1062_evk_examples as _, panic_probe as _};\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) -> ! {\n    let p = embassy_nxp::init(Default::default());\n    info!(\"Hello world!\");\n\n    // User LED (D8)\n    let led = p.GPIO_AD_B0_08;\n    // User button (SW5)\n    let button = p.WAKEUP;\n    let mut button = Input::new(button, Pull::Up100K);\n    let mut led = Output::new(led, Level::Low);\n    led.set_high();\n\n    loop {\n        button.wait_for_falling_edge().await;\n\n        info!(\"Toggled\");\n        led.toggle();\n\n        // Software debounce.\n        button.wait_for_rising_edge().await;\n\n        // Stabilization.\n        for _ in 0..100_000 {\n            cortex_m::asm::nop();\n        }\n    }\n}\n"
  },
  {
    "path": "examples/mimxrt1062-evk/src/lib.rs",
    "content": "//! FlexSPI configuration block (FCB) for the iMXRT1060-EVK\n//!\n//! This uses IS25WP QuadSPI flash.\n\n#![no_std]\n\nuse imxrt_boot_gen::flexspi::opcodes::sdr::*;\nuse imxrt_boot_gen::flexspi::{self, FlashPadType, ReadSampleClockSource, SerialClockFrequency, SerialFlashRegion, *};\nuse imxrt_boot_gen::serial_flash::*;\npub use nor::ConfigurationBlock;\n\nconst SEQ_READ: Sequence = SequenceBuilder::new()\n    .instr(Instr::new(CMD, Pads::One, 0xEB))\n    .instr(Instr::new(RADDR, Pads::Four, 0x18))\n    .instr(Instr::new(DUMMY, Pads::Four, 0x06))\n    .instr(Instr::new(READ, Pads::Four, 0x04))\n    .build();\nconst SEQ_READ_STATUS: Sequence = SequenceBuilder::new()\n    .instr(Instr::new(CMD, Pads::One, 0x05))\n    .instr(Instr::new(READ, Pads::One, 0x04))\n    .build();\nconst SEQ_WRITE_ENABLE: Sequence = SequenceBuilder::new().instr(Instr::new(CMD, Pads::One, 0x06)).build();\nconst SEQ_ERASE_SECTOR: Sequence = SequenceBuilder::new()\n    .instr(Instr::new(CMD, Pads::One, 0x20))\n    .instr(Instr::new(RADDR, Pads::One, 0x18))\n    .build();\nconst SEQ_PAGE_PROGRAM: Sequence = SequenceBuilder::new()\n    .instr(Instr::new(CMD, Pads::One, 0x02))\n    .instr(Instr::new(RADDR, Pads::One, 0x18))\n    .instr(Instr::new(WRITE, Pads::One, 0x04))\n    .build();\nconst SEQ_CHIP_ERASE: Sequence = SequenceBuilder::new().instr(Instr::new(CMD, Pads::One, 0x60)).build();\n\nconst LUT: LookupTable = LookupTable::new()\n    .command(Command::Read, SEQ_READ)\n    .command(Command::ReadStatus, SEQ_READ_STATUS)\n    .command(Command::WriteEnable, SEQ_WRITE_ENABLE)\n    .command(Command::EraseSector, SEQ_ERASE_SECTOR)\n    .command(Command::PageProgram, SEQ_PAGE_PROGRAM)\n    .command(Command::ChipErase, SEQ_CHIP_ERASE);\n\nconst COMMON_CONFIGURATION_BLOCK: flexspi::ConfigurationBlock = flexspi::ConfigurationBlock::new(LUT)\n    .version(Version::new(1, 4, 0))\n    .read_sample_clk_src(ReadSampleClockSource::LoopbackFromDQSPad)\n    .cs_hold_time(3)\n    .cs_setup_time(3)\n    .controller_misc_options(0x10)\n    .serial_flash_pad_type(FlashPadType::Quad)\n    .serial_clk_freq(SerialClockFrequency::MHz133)\n    .flash_size(SerialFlashRegion::A1, 8 * 1024 * 1024);\n\npub const SERIAL_NOR_CONFIGURATION_BLOCK: nor::ConfigurationBlock =\n    nor::ConfigurationBlock::new(COMMON_CONFIGURATION_BLOCK)\n        .page_size(256)\n        .sector_size(4096)\n        .ip_cmd_serial_clk_freq(nor::SerialClockFrequency::MHz30);\n\n#[unsafe(no_mangle)]\n#[cfg_attr(all(target_arch = \"arm\", target_os = \"none\"), unsafe(link_section = \".fcb\"))]\npub static FLEXSPI_CONFIGURATION_BLOCK: nor::ConfigurationBlock = SERIAL_NOR_CONFIGURATION_BLOCK;\n"
  },
  {
    "path": "examples/mimxrt6/.cargo/config.toml",
    "content": "[target.thumbv8m.main-none-eabihf]\nrunner = 'probe-rs run --chip MIMXRT685SFVKB'\n\nrustflags = [\n  \"-C\", \"linker=flip-link\",\n  \"-C\", \"link-arg=-Tlink.x\",\n  \"-C\", \"link-arg=-Tdefmt.x\",\n  # This is needed if your flash or ram addresses are not aligned to 0x10000 in memory.x\n  # See https://github.com/rust-embedded/cortex-m-quickstart/pull/95\n  \"-C\", \"link-arg=--nmagic\",\n]\n\n[build]\ntarget = \"thumbv8m.main-none-eabihf\" # Cortex-M33\n\n[env]\nDEFMT_LOG = \"trace\"\n"
  },
  {
    "path": "examples/mimxrt6/.gitignore",
    "content": "# Generated by Cargo\n# will have compiled files and executables\n/debug\n/target\n\n# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries\n# More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html\nCargo.lock\n\n# These are backup files generated by rustfmt\n**/*.rs.bk\n\n# MSVC Windows builds of rustc generate these, which store debugging information\n*.pdb\n"
  },
  {
    "path": "examples/mimxrt6/Cargo.toml",
    "content": "[package]\nname = \"embassy-imxrt-examples\"\nversion = \"0.1.0\"\nedition = \"2024\"\nlicense = \"MIT OR Apache-2.0\"\npublish = false\n\n[dependencies]\ncortex-m = { version = \"0.7.7\", features = [\"inline-asm\", \"critical-section-single-core\"] }\ncortex-m-rt = \"0.7.3\"\ndefmt = \"1.0.1\"\ndefmt-rtt = \"1.0.0\"\n\nembassy-executor = { version = \"0.10.0\", path = \"../../embassy-executor\", features = [\"platform-cortex-m\", \"executor-thread\", \"executor-interrupt\", \"defmt\"] }\nembassy-futures = { version = \"0.1.2\", path = \"../../embassy-futures\" }\nembassy-imxrt = { version = \"0.1.0\", path = \"../../embassy-imxrt\", features = [\"defmt\", \"mimxrt685s\", \"unstable-pac\", \"time\", \"time-driver-os-timer\"] }\nembassy-time = { version = \"0.5.1\", path = \"../../embassy-time\", features = [\"defmt\", \"defmt-timestamp-uptime\"] }\nembassy-sync = { version = \"0.8.0\", path = \"../../embassy-sync\", features = [\"defmt\"] }\nembedded-hal-1 = { package = \"embedded-hal\", version = \"1.0\" }\nembedded-hal-async = \"1.0.0\"\n\nmimxrt600-fcb = \"0.2.2\"\npanic-probe = { version = \"1.0.0\", features = [\"print-defmt\"] }\nembedded-hal-bus = \"0.3.0\"\nis31fl3743b-driver = { version = \"0.1.1\", features = [\"is_blocking\"] }\n\n# cargo build/run\n[profile.dev]\ncodegen-units = 1\ndebug = 2\ndebug-assertions = true # <-\nincremental = false\nopt-level = 3 # <-\noverflow-checks = true # <-\n\n# cargo test\n[profile.test]\ncodegen-units = 1\ndebug = 2\ndebug-assertions = true # <-\nincremental = false\nopt-level = 3 # <-\noverflow-checks = true # <-\n\n# cargo build/run --release\n[profile.release]\ncodegen-units = 1\ndebug = 2\ndebug-assertions = false # <-\nincremental = false\nlto = 'fat'\nopt-level = 3 # <-\noverflow-checks = false # <-\n\n# cargo test --release\n[profile.bench]\ncodegen-units = 1\ndebug = 2\ndebug-assertions = false # <-\nincremental = false\nlto = 'fat'\nopt-level = 3 # <-\noverflow-checks = false # <-\n\n[package.metadata.embassy]\nbuild = [\n  { target = \"thumbv8m.main-none-eabihf\", artifact-dir = \"out/examples/mimxrt6\" }\n]\n"
  },
  {
    "path": "examples/mimxrt6/README.md",
    "content": "# embassy-imxrt-examples\n\n## Introduction\n\nThese examples illustrates how to use the embassy-imxrt HAL.\n\n## Adding Examples\nAdd uniquely named example to `src/bin` like `adc.rs`\n\n## Build\n`cd` to examples folder\n`cargo build --bin <example_name>` for example, `cargo build --bin adc`\n\n## Run\nAssuming RT685 is powered and connected to Jlink debug probe and the latest probe-rs is installed via  \n  `$ cargo install probe-rs-tools --git https://github.com/probe-rs/probe-rs --locked`  \n`cd` to examples folder  \n`cargo run --bin <example_name>` for example, `cargo run --bin adc`"
  },
  {
    "path": "examples/mimxrt6/build.rs",
    "content": "use std::env;\nuse std::fs::File;\nuse std::io::Write;\nuse std::path::PathBuf;\n\nfn main() {\n    // Put `memory.x` in our output directory and ensure it's\n    // on the linker search path.\n    let out = &PathBuf::from(env::var_os(\"OUT_DIR\").unwrap());\n    File::create(out.join(\"memory.x\"))\n        .unwrap()\n        .write_all(include_bytes!(\"memory.x\"))\n        .unwrap();\n    println!(\"cargo:rustc-link-search={}\", out.display());\n\n    // By default, Cargo will re-run a build script whenever\n    // any file in the project changes. By specifying `memory.x`\n    // here, we ensure the build script is only re-run when\n    // `memory.x` is changed.\n    println!(\"cargo:rerun-if-changed=memory.x\");\n\n    // Inject crate version into the .biv section.\n    File::create(out.join(\"biv.rs\"))\n        .unwrap()\n        .write_all(\n            format!(\n                r##\"\n#[unsafe(link_section = \".biv\")]\n#[used]\nstatic BOOT_IMAGE_VERSION: u32 = 0x{:02x}{:02x}{:02x}00;\n\"##,\n                env!(\"CARGO_PKG_VERSION_MAJOR\")\n                    .parse::<u8>()\n                    .expect(\"should have major version\"),\n                env!(\"CARGO_PKG_VERSION_MINOR\")\n                    .parse::<u8>()\n                    .expect(\"should have minor version\"),\n                env!(\"CARGO_PKG_VERSION_PATCH\")\n                    .parse::<u8>()\n                    .expect(\"should have patch version\"),\n            )\n            .as_bytes(),\n        )\n        .unwrap();\n}\n"
  },
  {
    "path": "examples/mimxrt6/memory.x",
    "content": "MEMORY {\n        OTFAD    : ORIGIN = 0x08000000, LENGTH = 256\n        FCB      : ORIGIN = 0x08000400, LENGTH = 512\n        BIV      : ORIGIN = 0x08000600, LENGTH = 4\n        KEYSTORE : ORIGIN = 0x08000800, LENGTH = 2K\n        FLASH    : ORIGIN = 0x08001000, LENGTH = 1M\n        RAM      : ORIGIN = 0x20080000, LENGTH = 1536K\n}\n\nSECTIONS {\n        .otfad : {\n                . = ALIGN(4);\n                KEEP(* (.otfad))\n                . = ALIGN(4);\n        } > OTFAD\n\n        .fcb : {\n                . = ALIGN(4);\n                KEEP(* (.fcb))\n                . = ALIGN(4);\n        } > FCB\n\n        .biv : {\n                . = ALIGN(4);\n                KEEP(* (.biv))\n                . = ALIGN(4);\n        } > BIV\n\n        .keystore : {\n                . = ALIGN(4);\n                KEEP(* (.keystore))\n                . = ALIGN(4);\n        } > KEYSTORE\n}\n"
  },
  {
    "path": "examples/mimxrt6/src/bin/blinky.rs",
    "content": "#![no_std]\n#![no_main]\n\nextern crate embassy_imxrt_examples;\n\nuse defmt::info;\nuse embassy_executor::Spawner;\nuse embassy_imxrt::gpio;\nuse embassy_time::Timer;\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let p = embassy_imxrt::init(Default::default());\n\n    info!(\"Initializing GPIO\");\n\n    let mut led = gpio::Output::new(\n        p.PIO0_26,\n        gpio::Level::Low,\n        gpio::DriveMode::PushPull,\n        gpio::DriveStrength::Normal,\n        gpio::SlewRate::Standard,\n    );\n\n    loop {\n        info!(\"Toggling LED\");\n        led.toggle();\n        Timer::after_secs(1).await;\n    }\n}\n"
  },
  {
    "path": "examples/mimxrt6/src/bin/button.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse defmt::info;\nuse embassy_executor::Spawner;\nuse embassy_futures::select::{Either, select};\nuse embassy_imxrt::gpio;\nuse {defmt_rtt as _, embassy_imxrt_examples as _, panic_probe as _};\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let p = embassy_imxrt::init(Default::default());\n\n    let mut user1 = gpio::Input::new(p.PIO1_1, gpio::Pull::None, gpio::Inverter::Disabled);\n    let mut user2 = gpio::Input::new(p.PIO0_10, gpio::Pull::None, gpio::Inverter::Disabled);\n\n    loop {\n        let res = select(user1.wait_for_falling_edge(), user2.wait_for_falling_edge()).await;\n\n        match res {\n            Either::First(()) => {\n                info!(\"Button `USER1' pressed\");\n            }\n            Either::Second(()) => {\n                info!(\"Button `USER2' pressed\");\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "examples/mimxrt6/src/bin/crc.rs",
    "content": "#![no_std]\n#![no_main]\n\nextern crate embassy_imxrt_examples;\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_imxrt::crc::{Config, Crc, Polynomial};\nuse {defmt_rtt as _, panic_probe as _};\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let mut p = embassy_imxrt::init(Default::default());\n    let data = b\"123456789\";\n\n    info!(\"Initializing CRC\");\n\n    // CRC-CCITT\n    let mut crc = Crc::new(p.CRC.reborrow(), Default::default());\n    let output = crc.feed_bytes(data);\n    defmt::assert_eq!(output, 0x29b1);\n\n    // CRC16-ARC\n    let mut crc = Crc::new(\n        p.CRC.reborrow(),\n        Config {\n            polynomial: Polynomial::Crc16,\n            reverse_in: true,\n            reverse_out: true,\n            complement_out: false,\n            seed: 0,\n            ..Default::default()\n        },\n    );\n    let output = crc.feed_bytes(data);\n    defmt::assert_eq!(output, 0xbb3d);\n\n    // CRC16-CMS\n    let mut crc = Crc::new(\n        p.CRC.reborrow(),\n        Config {\n            polynomial: Polynomial::Crc16,\n            reverse_in: false,\n            reverse_out: false,\n            complement_out: false,\n            seed: 0xffff,\n            ..Default::default()\n        },\n    );\n    let output = crc.feed_bytes(data);\n    defmt::assert_eq!(output, 0xaee7);\n\n    // CRC16-DDS-110\n    let mut crc = Crc::new(\n        p.CRC.reborrow(),\n        Config {\n            polynomial: Polynomial::Crc16,\n            reverse_in: false,\n            reverse_out: false,\n            complement_out: false,\n            seed: 0x800d,\n            ..Default::default()\n        },\n    );\n    let output = crc.feed_bytes(data);\n    defmt::assert_eq!(output, 0x9ecf);\n\n    // CRC16-MAXIM-DOW\n    let mut crc = Crc::new(\n        p.CRC.reborrow(),\n        Config {\n            polynomial: Polynomial::Crc16,\n            reverse_in: true,\n            reverse_out: true,\n            complement_out: true,\n            seed: 0,\n            ..Default::default()\n        },\n    );\n    let output = crc.feed_bytes(data);\n    defmt::assert_eq!(output, 0x44c2);\n\n    // CRC16-MODBUS\n    let mut crc = Crc::new(\n        p.CRC.reborrow(),\n        Config {\n            polynomial: Polynomial::Crc16,\n            reverse_in: true,\n            reverse_out: true,\n            complement_out: false,\n            seed: 0xffff,\n            ..Default::default()\n        },\n    );\n    let output = crc.feed_bytes(data);\n    defmt::assert_eq!(output, 0x4b37);\n\n    // CRC32-BZIP2\n    let mut crc = Crc::new(\n        p.CRC.reborrow(),\n        Config {\n            polynomial: Polynomial::Crc32,\n            reverse_in: false,\n            reverse_out: false,\n            complement_out: true,\n            seed: 0xffff_ffff,\n            ..Default::default()\n        },\n    );\n    let output = crc.feed_bytes(data);\n    defmt::assert_eq!(output, 0xfc89_1918);\n\n    // CRC32-CKSUM\n    let mut crc = Crc::new(\n        p.CRC.reborrow(),\n        Config {\n            polynomial: Polynomial::Crc32,\n            reverse_in: false,\n            reverse_out: false,\n            complement_out: true,\n            seed: 0,\n            ..Default::default()\n        },\n    );\n    let output = crc.feed_bytes(data);\n    defmt::assert_eq!(output, 0x765e_7680);\n\n    // CRC32-ISO-HDLC\n    let mut crc = Crc::new(\n        p.CRC.reborrow(),\n        Config {\n            polynomial: Polynomial::Crc32,\n            reverse_in: true,\n            reverse_out: true,\n            complement_out: true,\n            seed: 0xffff_ffff,\n            ..Default::default()\n        },\n    );\n    let output = crc.feed_bytes(data);\n    defmt::assert_eq!(output, 0xcbf4_3926);\n\n    // CRC32-JAMCRC\n    let mut crc = Crc::new(\n        p.CRC.reborrow(),\n        Config {\n            polynomial: Polynomial::Crc32,\n            reverse_in: true,\n            reverse_out: true,\n            complement_out: false,\n            seed: 0xffff_ffff,\n            ..Default::default()\n        },\n    );\n    let output = crc.feed_bytes(data);\n    defmt::assert_eq!(output, 0x340b_c6d9);\n\n    // CRC32-MPEG-2\n    let mut crc = Crc::new(\n        p.CRC.reborrow(),\n        Config {\n            polynomial: Polynomial::Crc32,\n            reverse_in: false,\n            reverse_out: false,\n            complement_out: false,\n            seed: 0xffff_ffff,\n            ..Default::default()\n        },\n    );\n    let output = crc.feed_bytes(data);\n    defmt::assert_eq!(output, 0x0376_e6e7);\n\n    info!(\"end program\");\n    cortex_m::asm::bkpt();\n}\n"
  },
  {
    "path": "examples/mimxrt6/src/bin/dma.rs",
    "content": "#![no_std]\n#![no_main]\n\nextern crate embassy_imxrt_examples;\n\nuse defmt::info;\nuse embassy_executor::Spawner;\nuse embassy_imxrt::dma::copy;\nuse {defmt_rtt as _, panic_probe as _};\n\nconst BUFLEN: usize = 1024;\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let p = embassy_imxrt::init(Default::default());\n\n    info!(\"Test memory-to-memory DMA transfers\");\n\n    let src = [0x55u8; BUFLEN];\n    let mut dst = [0u8; BUFLEN];\n\n    unsafe { copy(p.DMA0_CH0, &src, &mut dst) }.await;\n    assert!(dst == src);\n\n    info!(\"DMA copy succeeded\");\n}\n"
  },
  {
    "path": "examples/mimxrt6/src/bin/hello.rs",
    "content": "#![no_std]\n#![no_main]\n\nextern crate embassy_imxrt_examples;\n\nuse defmt::info;\nuse embassy_executor::Spawner;\nuse {defmt_rtt as _, panic_probe as _};\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) -> ! {\n    let _p = embassy_imxrt::init(Default::default());\n    loop {\n        info!(\"Hello\");\n        cortex_m::asm::delay(5_000_000);\n    }\n}\n"
  },
  {
    "path": "examples/mimxrt6/src/bin/rng.rs",
    "content": "#![no_std]\n#![no_main]\n\nextern crate embassy_imxrt_examples;\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_imxrt::rng::Rng;\nuse embassy_imxrt::{bind_interrupts, peripherals, rng};\nuse {defmt_rtt as _, panic_probe as _};\n\nbind_interrupts!(struct Irqs {\n    RNG => rng::InterruptHandler<peripherals::RNG>;\n});\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let p = embassy_imxrt::init(Default::default());\n\n    info!(\"Initializing RNG\");\n    let mut rng = Rng::new(p.RNG, Irqs);\n    let mut buf = [0u8; 65];\n\n    // Async interface\n    unwrap!(rng.async_fill_bytes(&mut buf).await);\n    info!(\"random bytes: {:02x}\", buf);\n\n    // RngCore interface\n    let mut random_bytes = [0; 16];\n\n    let random_u32 = rng.blocking_next_u32();\n    let random_u64 = rng.blocking_next_u64();\n\n    rng.blocking_fill_bytes(&mut random_bytes);\n\n    info!(\"random_u32 {}\", random_u32);\n    info!(\"random_u64 {}\", random_u64);\n    info!(\"random_bytes {}\", random_bytes);\n}\n"
  },
  {
    "path": "examples/mimxrt6/src/bin/spi-async.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse defmt::info;\nuse embassy_executor::Spawner;\nuse embassy_imxrt::bind_interrupts;\nuse embassy_imxrt::flexcomm::spi::{InterruptHandler, Spi};\nuse embassy_imxrt::peripherals::FLEXCOMM5;\nuse {defmt_rtt as _, embassy_imxrt_examples as _, panic_probe as _};\n\nbind_interrupts!(struct Irqs {\n    FLEXCOMM5 => InterruptHandler<FLEXCOMM5>;\n});\n\nconst BUFLEN: usize = 1024;\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let p = embassy_imxrt::init(Default::default());\n\n    info!(\"Initializing SPI\");\n\n    let mut spi = Spi::new_async(p.FLEXCOMM5, p.PIO1_3, p.PIO1_5, p.PIO1_4, Irqs, Default::default());\n\n    let mut rxbuf = [0x55; BUFLEN];\n    let txbuf = [0xaa; BUFLEN];\n\n    for _ in 0..10 {\n        spi.async_transfer(&mut rxbuf, &txbuf).await.unwrap();\n        assert!(rxbuf.iter().all(|b| *b == 0xaa));\n        rxbuf.fill(0x55);\n    }\n\n    info!(\"SPI transfers succeeded\");\n}\n"
  },
  {
    "path": "examples/mimxrt6/src/bin/spi.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse defmt::info;\nuse embassy_executor::Spawner;\nuse embassy_imxrt::flexcomm::spi::Spi;\nuse embassy_imxrt::gpio;\nuse embassy_time::{Delay, Timer};\nuse embedded_hal_bus::spi::ExclusiveDevice;\nuse is31fl3743b_driver::{CSy, Is31fl3743b, SWx};\nuse {defmt_rtt as _, embassy_imxrt_examples as _, panic_probe as _};\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let p = embassy_imxrt::init(Default::default());\n\n    info!(\"Initializing SPI\");\n\n    let cs = gpio::Output::new(\n        p.PIO1_6,\n        gpio::Level::Low,\n        gpio::DriveMode::PushPull,\n        gpio::DriveStrength::Normal,\n        gpio::SlewRate::Standard,\n    );\n\n    let spi = Spi::new_blocking(p.FLEXCOMM5, p.PIO1_3, p.PIO1_5, p.PIO1_4, Default::default());\n    let delay = Delay;\n\n    // One SPI device only on the SPI bus\n    let spi_dev = ExclusiveDevice::new(spi, cs, delay).unwrap();\n\n    // Instantiate IS31FL3743B device\n    let mut driver = Is31fl3743b::new(spi_dev).unwrap();\n\n    // Enable phase delay to help reduce power noise\n    let _ = driver.enable_phase_delay();\n    // Set global current, check method documentation for more info\n    let _ = driver.set_global_current(90);\n\n    let _ = driver.set_led_peak_current_bulk(SWx::SW1, CSy::CS1, &[100; 11 * 18]);\n\n    // Driver is fully set up, we can now start turning on LEDs!\n    // Create a white breathing effect\n    loop {\n        for brightness in (0..=255_u8).chain((0..=255).rev()) {\n            let _ = driver.set_led_brightness_bulk(SWx::SW1, CSy::CS1, &[brightness; 11 * 18]);\n            Timer::after_micros(1).await;\n        }\n    }\n}\n"
  },
  {
    "path": "examples/mimxrt6/src/bin/uart-async.rs",
    "content": "#![no_std]\n#![no_main]\n\nextern crate embassy_imxrt_examples;\n\nuse defmt::info;\nuse embassy_executor::Spawner;\nuse embassy_imxrt::flexcomm::uart::{self, Async, Uart};\nuse embassy_imxrt::{bind_interrupts, peripherals};\nuse embassy_time::Timer;\nuse {defmt_rtt as _, panic_probe as _};\n\nbind_interrupts!(struct Irqs {\n    FLEXCOMM2 => uart::InterruptHandler<peripherals::FLEXCOMM2>;\n    FLEXCOMM4 => uart::InterruptHandler<peripherals::FLEXCOMM4>;\n});\n\nconst BUFLEN: usize = 16;\n\n#[embassy_executor::task]\nasync fn usart4_task(mut uart: Uart<'static, Async>) {\n    info!(\"RX Task\");\n\n    loop {\n        let mut rx_buf = [0; BUFLEN];\n        uart.read(&mut rx_buf).await.unwrap();\n        info!(\"usart4: rx_buf {:02x}\", rx_buf);\n\n        Timer::after_millis(10).await;\n\n        let tx_buf = [0xaa; BUFLEN];\n        uart.write(&tx_buf).await.unwrap();\n        info!(\"usart4: tx_buf {:02x}\", tx_buf);\n    }\n}\n\n#[embassy_executor::task]\nasync fn usart2_task(mut uart: Uart<'static, Async>) {\n    info!(\"TX Task\");\n\n    loop {\n        let tx_buf = [0x55; BUFLEN];\n        uart.write(&tx_buf).await.unwrap();\n        info!(\"usart2: tx_buf {:02x}\", tx_buf);\n\n        Timer::after_millis(10).await;\n\n        let mut rx_buf = [0x00; BUFLEN];\n        uart.read(&mut rx_buf).await.unwrap();\n        info!(\"usart2: rx_buf {:02x}\", rx_buf);\n    }\n}\n\n#[embassy_executor::main]\nasync fn main(spawner: Spawner) {\n    let p = embassy_imxrt::init(Default::default());\n\n    info!(\"UART test start\");\n\n    let usart4 = Uart::new_with_rtscts(\n        p.FLEXCOMM4,\n        p.PIO0_29,\n        p.PIO0_30,\n        p.PIO1_0,\n        p.PIO0_31,\n        Irqs,\n        p.DMA0_CH9,\n        p.DMA0_CH8,\n        Default::default(),\n    )\n    .unwrap();\n    spawner.spawn(usart4_task(usart4).unwrap());\n\n    let usart2 = Uart::new_with_rtscts(\n        p.FLEXCOMM2,\n        p.PIO0_15,\n        p.PIO0_16,\n        p.PIO0_18,\n        p.PIO0_17,\n        Irqs,\n        p.DMA0_CH5,\n        p.DMA0_CH4,\n        Default::default(),\n    )\n    .unwrap();\n    spawner.spawn(usart2_task(usart2).unwrap());\n}\n"
  },
  {
    "path": "examples/mimxrt6/src/bin/uart.rs",
    "content": "#![no_std]\n#![no_main]\n\nextern crate embassy_imxrt_examples;\n\nuse defmt::info;\nuse embassy_executor::Spawner;\nuse embassy_imxrt::flexcomm::uart::{Blocking, Uart, UartRx, UartTx};\nuse embassy_time::Timer;\nuse {defmt_rtt as _, panic_probe as _};\n\n#[embassy_executor::task]\nasync fn usart4_task(mut uart: UartRx<'static, Blocking>) {\n    info!(\"RX Task\");\n\n    loop {\n        let mut buf = [0; 8];\n\n        Timer::after_millis(10).await;\n\n        uart.blocking_read(&mut buf).unwrap();\n\n        let s = core::str::from_utf8(&buf).unwrap();\n\n        info!(\"Received '{}'\", s);\n    }\n}\n\n#[embassy_executor::task]\nasync fn usart2_task(mut uart: UartTx<'static, Blocking>) {\n    info!(\"TX Task\");\n\n    loop {\n        let buf = \"Testing\\0\".as_bytes();\n\n        uart.blocking_write(buf).unwrap();\n\n        Timer::after_millis(10).await;\n    }\n}\n\n#[embassy_executor::main]\nasync fn main(spawner: Spawner) {\n    let p = embassy_imxrt::init(Default::default());\n\n    info!(\"UART test start\");\n\n    let usart4 = Uart::new_blocking(p.FLEXCOMM4, p.PIO0_29, p.PIO0_30, Default::default()).unwrap();\n\n    let (_, usart4) = usart4.split();\n    spawner.spawn(usart4_task(usart4).unwrap());\n\n    let usart2 = UartTx::new_blocking(p.FLEXCOMM2, p.PIO0_15, Default::default()).unwrap();\n    spawner.spawn(usart2_task(usart2).unwrap());\n}\n"
  },
  {
    "path": "examples/mimxrt6/src/lib.rs",
    "content": "#![no_std]\n\nuse mimxrt600_fcb::FlexSPIFlashConfigurationBlock;\nuse {defmt_rtt as _, panic_probe as _};\n\n// auto-generated version information from Cargo.toml\ninclude!(concat!(env!(\"OUT_DIR\"), \"/biv.rs\"));\n\n#[unsafe(link_section = \".otfad\")]\n#[used]\nstatic OTFAD: [u8; 256] = [0; 256];\n\n#[rustfmt::skip]\n#[unsafe(link_section = \".fcb\")]\n#[used]\nstatic FCB: FlexSPIFlashConfigurationBlock = FlexSPIFlashConfigurationBlock::build();\n\n#[unsafe(link_section = \".keystore\")]\n#[used]\nstatic KEYSTORE: [u8; 2048] = [0; 2048];\n"
  },
  {
    "path": "examples/mspm0c1104/.cargo/config.toml",
    "content": "[target.'cfg(all(target_arch = \"arm\", target_os = \"none\"))']\n# replace MSPM0C1104 with your chip as listed in `probe-rs chip list`\nrunner = \"probe-rs run --chip MSPM0C1104 --protocol=swd\"\n\n[build]\ntarget = \"thumbv6m-none-eabi\"\n\n[env]\nDEFMT_LOG = \"debug\"\n# defmt's buffer needs to be shrunk since the MSPM0C1104 only has 1KB of ram.\nDEFMT_RTT_BUFFER_SIZE = \"72\"\n"
  },
  {
    "path": "examples/mspm0c1104/Cargo.toml",
    "content": "[package]\nedition = \"2024\"\nname = \"embassy-mspm0-c1104-examples\"\nversion = \"0.1.0\"\nlicense = \"MIT OR Apache-2.0\"\npublish = false\n\n[dependencies]\nembassy-mspm0 = { version = \"0.1.0\", path = \"../../embassy-mspm0\", features = [\"mspm0c1104dgs20\", \"defmt\", \"rt\", \"time-driver-any\"] }\nembassy-executor = { version = \"0.10.0\", path = \"../../embassy-executor\", features = [\"platform-cortex-m\", \"executor-thread\", \"executor-interrupt\"] }\nembassy-sync = { version = \"0.8.0\", path = \"../../embassy-sync\", features = [\"defmt\"] }\nembassy-time = { version = \"0.5.1\", path = \"../../embassy-time\", features = [\"defmt\"] }\npanic-halt = \"1.0.0\"\ncortex-m = { version = \"0.7.6\", features = [\"inline-asm\", \"critical-section-single-core\"] }\ncortex-m-rt = { version = \"0.7.0\"}\ndefmt = \"1.0.1\"\ndefmt-rtt = \"1.0.0\"\npanic-probe = { version = \"1.0.0\", features = [\"print-defmt\"] }\npanic-semihosting = \"0.6.0\"\n\n# The chip only has 1KB of ram, so we must optimize binaries regardless\n[profile.dev]\ndebug = 0\nopt-level = \"z\"\nlto = true\ncodegen-units = 1\n# strip = true\n\n[profile.release]\ndebug = 0\nopt-level = \"z\"\nlto = true\ncodegen-units = 1\n\n[package.metadata.embassy]\nbuild = [\n  { target = \"thumbv6m-none-eabi\", artifact-dir = \"out/examples/mspm0c1104\", env = { DEFMT_RTT_BUFFER_SIZE = \"72\" }}\n]\n"
  },
  {
    "path": "examples/mspm0c1104/README.md",
    "content": "# Examples for MSPM0C1104\n\nRun individual examples with\n```\ncargo run --bin <module-name>\n```\nfor example\n```\ncargo run --bin blinky\n```\n\n## Checklist before running examples\nA large number of the examples are written for the [LP-MSPM0C1104](https://www.ti.com/tool/LP-MSPM0C1104) board.\n\nYou might need to adjust `.cargo/config.toml`, `Cargo.toml` and possibly update pin numbers or peripherals to match the specific MCU or board you are using.\n\n* [ ] Update .cargo/config.toml with the correct probe-rs command to use your specific MCU. For example for C1104 it should be `probe-rs run --chip MSPM0C1104`. (use `probe-rs chip list` to find your chip)\n* [ ] Update Cargo.toml to have the correct `embassy-mspm0` feature. For the LP-MSPM0C1104 it should be `mspm0c1104dgs20`. Look in the `Cargo.toml` file of the `embassy-mspm0` project to find the correct feature flag for your chip.\n* [ ] If your board has a special clock or power configuration, make sure that it is set up appropriately.\n* [ ] If your board has different pin mapping, update any pin numbers or peripherals in the given example code to match your schematic\n\nIf you are unsure, please drop by the Embassy Matrix chat for support, and let us know:\n\n* Which example you are trying to run\n* Which chip and board you are using\n\nEmbassy Chat: https://matrix.to/#/#embassy-rs:matrix.org\n"
  },
  {
    "path": "examples/mspm0c1104/build.rs",
    "content": "//! This build script copies the `memory.x` file from the crate root into\n//! a directory where the linker can always find it at build time.\n//! For many projects this is optional, as the linker always searches the\n//! project root directory -- wherever `Cargo.toml` is. However, if you\n//! are using a workspace or have a more complicated build setup, this\n//! build script becomes required. Additionally, by requesting that\n//! Cargo re-run the build script whenever `memory.x` is changed,\n//! updating `memory.x` ensures a rebuild of the application with the\n//! new memory settings.\n\nuse std::env;\nuse std::fs::File;\nuse std::io::Write;\nuse std::path::PathBuf;\n\nfn main() {\n    // Put `memory.x` in our output directory and ensure it's\n    // on the linker search path.\n    let out = &PathBuf::from(env::var_os(\"OUT_DIR\").unwrap());\n    File::create(out.join(\"memory.x\"))\n        .unwrap()\n        .write_all(include_bytes!(\"memory.x\"))\n        .unwrap();\n    println!(\"cargo:rustc-link-search={}\", out.display());\n\n    // By default, Cargo will re-run a build script whenever\n    // any file in the project changes. By specifying `memory.x`\n    // here, we ensure the build script is only re-run when\n    // `memory.x` is changed.\n    println!(\"cargo:rerun-if-changed=memory.x\");\n\n    println!(\"cargo:rustc-link-arg-bins=--nmagic\");\n    println!(\"cargo:rustc-link-arg-bins=-Tlink.x\");\n    println!(\"cargo:rustc-link-arg-bins=-Tdefmt.x\");\n    // You must tell cargo to link interrupt groups if the rt feature is enabled.\n    println!(\"cargo:rustc-link-arg-bins=-Tinterrupt_group.x\");\n}\n"
  },
  {
    "path": "examples/mspm0c1104/memory.x",
    "content": "MEMORY\n{\n  FLASH : ORIGIN = 0x00000000, LENGTH = 16K\n  RAM   : ORIGIN = 0x20000000, LENGTH = 1K\n}\n"
  },
  {
    "path": "examples/mspm0c1104/src/bin/blinky.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_mspm0::Config;\nuse embassy_mspm0::gpio::{Level, Output};\nuse embassy_time::Timer;\nuse {defmt_rtt as _, panic_halt as _};\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) -> ! {\n    info!(\"Hello world!\");\n    let p = embassy_mspm0::init(Config::default());\n\n    let mut led1 = Output::new(p.PA22, Level::Low);\n    led1.set_inversion(true);\n\n    loop {\n        Timer::after_millis(400).await;\n\n        info!(\"Toggle\");\n        led1.toggle();\n    }\n}\n"
  },
  {
    "path": "examples/mspm0c1104/src/bin/button.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_mspm0::Config;\nuse embassy_mspm0::gpio::{Input, Level, Output, Pull};\nuse {defmt_rtt as _, panic_halt as _};\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) -> ! {\n    info!(\"Hello world!\");\n\n    let p = embassy_mspm0::init(Config::default());\n\n    let led1 = p.PA22;\n    let s2 = p.PA16;\n\n    let mut led1 = Output::new(led1, Level::Low);\n\n    let mut s2 = Input::new(s2, Pull::Up);\n\n    // led1 is active low\n    led1.set_high();\n\n    loop {\n        s2.wait_for_falling_edge().await;\n\n        info!(\"Switch 2 was pressed\");\n\n        led1.toggle();\n    }\n}\n"
  },
  {
    "path": "examples/mspm0c1104/src/bin/uart.rs",
    "content": "//! Example of using blocking uart\n//!\n//! This uses the virtual COM port provided on the LP-MSPM0C1104 board.\n\n#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_mspm0::uart::{Config, Uart};\nuse {defmt_rtt as _, panic_halt as _};\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) -> ! {\n    info!(\"Hello world!\");\n\n    let p = embassy_mspm0::init(Default::default());\n\n    let instance = p.UART0;\n    let tx = p.PA27;\n    let rx = p.PA26;\n\n    let config = Config::default();\n    let mut uart = unwrap!(Uart::new_blocking(instance, rx, tx, config));\n\n    unwrap!(uart.blocking_write(b\"Hello Embassy World!\\r\\n\"));\n    info!(\"wrote Hello, starting echo\");\n\n    let mut buf = [0u8; 1];\n\n    loop {\n        unwrap!(uart.blocking_read(&mut buf));\n        unwrap!(uart.blocking_write(&buf));\n    }\n}\n"
  },
  {
    "path": "examples/mspm0c1104/src/bin/wwdt.rs",
    "content": "//! Example of using window watchdog timer in the MSPM0C1104 chip.\n//!\n//! It tests the use case when watchdog timer is expired and when watchdog is pet too early.\n\n#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_mspm0::gpio::{Level, Output};\nuse embassy_mspm0::wwdt::{ClosedWindowPercentage, Config, Timeout, Watchdog};\nuse embassy_time::Timer;\nuse {defmt_rtt as _, panic_halt as _};\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) -> ! {\n    info!(\"Hello world!\");\n\n    let p = embassy_mspm0::init(Default::default());\n    let mut conf = Config::default();\n    conf.timeout = Timeout::Sec1;\n\n    // watchdog also resets the system if the pet comes too early,\n    // less than 250 msec == 25% from 1 sec\n    conf.closed_window = ClosedWindowPercentage::TwentyFive;\n    let mut wdt = Watchdog::new(p.WWDT0, conf);\n    info!(\"Started the watchdog timer\");\n\n    let mut led1 = Output::new(p.PA0, Level::High);\n    led1.set_inversion(true);\n    Timer::after_millis(900).await;\n\n    for _ in 1..=5 {\n        info!(\"pet watchdog\");\n        led1.toggle();\n        wdt.pet();\n        Timer::after_millis(500).await;\n    }\n\n    // watchdog timeout test\n    info!(\"Stopped the pet command, device will reset in less than 1 second\");\n    loop {\n        led1.toggle();\n        Timer::after_millis(500).await;\n    }\n\n    // watchdog \"too early\" test\n    // info!(\"Device will reset when the pet comes too early\");\n    // loop {\n    //     led1.toggle();\n    //     wdt.pet();\n    //     Timer::after_millis(200).await;\n    // }\n}\n"
  },
  {
    "path": "examples/mspm0g3507/.cargo/config.toml",
    "content": "[target.'cfg(all(target_arch = \"arm\", target_os = \"none\"))']\n# replace MSPM0G3507 with your chip as listed in `probe-rs chip list`\nrunner = \"probe-rs run --chip MSPM0G3507 --protocol=swd\"\n\n[build]\ntarget = \"thumbv6m-none-eabi\"\n\n[env]\nDEFMT_LOG = \"trace\"\n"
  },
  {
    "path": "examples/mspm0g3507/Cargo.toml",
    "content": "[package]\nedition = \"2024\"\nname = \"embassy-mspm0-g3507-examples\"\nversion = \"0.1.0\"\nlicense = \"MIT OR Apache-2.0\"\npublish = false\n\n[dependencies]\nembassy-mspm0 = { version = \"0.1.0\", path = \"../../embassy-mspm0\", features = [\"mspm0g3507pm\", \"defmt\", \"rt\", \"time-driver-any\"] }\nembassy-executor = { version = \"0.10.0\", path = \"../../embassy-executor\", features = [\"platform-cortex-m\", \"executor-thread\", \"executor-interrupt\"] }\nembassy-sync = { version = \"0.8.0\", path = \"../../embassy-sync\", features = [\"defmt\"] }\nembassy-time = { version = \"0.5.1\", path = \"../../embassy-time\", features = [\"defmt\"] }\npanic-halt = \"1.0.0\"\ncortex-m = { version = \"0.7.6\", features = [\"inline-asm\", \"critical-section-single-core\"] }\ncortex-m-rt = { version = \"0.7.0\"}\ndefmt = \"1.0.1\"\ndefmt-rtt = \"1.0.0\"\npanic-probe = { version = \"1.0.0\", features = [\"print-defmt\"] }\npanic-semihosting = \"0.6.0\"\n\nembedded-io-async = { version = \"0.7.0\" }\n\n[profile.release]\ndebug = 2\n\n[package.metadata.embassy]\nbuild = [\n  { target = \"thumbv6m-none-eabi\", artifact-dir = \"out/examples/mspm0g3507\" }\n]\n"
  },
  {
    "path": "examples/mspm0g3507/README.md",
    "content": "# Examples for MSPM0M3507\n\nRun individual examples with\n```\ncargo run --bin <module-name>\n```\nfor example\n```\ncargo run --bin blinky\n```\n\n## Checklist before running examples\nA large number of the examples are written for the [LP-MSPM0G3507](https://www.ti.com/tool/LP-MSPM0G3507) board.\n\nYou might need to adjust `.cargo/config.toml`, `Cargo.toml` and possibly update pin numbers or peripherals to match the specific MCU or board you are using.\n\n* [ ] Update .cargo/config.toml with the correct probe-rs command to use your specific MCU. For example for G3507 it should be `probe-rs run --chip MSPM0G3507`. (use `probe-rs chip list` to find your chip)\n* [ ] Update Cargo.toml to have the correct `embassy-mspm0` feature. For the LP-MSPM0G3507 it should be `mspm0g3507pm`. Look in the `Cargo.toml` file of the `embassy-mspm0` project to find the correct feature flag for your chip.\n* [ ] If your board has a special clock or power configuration, make sure that it is set up appropriately.\n* [ ] If your board has different pin mapping, update any pin numbers or peripherals in the given example code to match your schematic\n\nIf you are unsure, please drop by the Embassy Matrix chat for support, and let us know:\n\n* Which example you are trying to run\n* Which chip and board you are using\n\nEmbassy Chat: https://matrix.to/#/#embassy-rs:matrix.org\n"
  },
  {
    "path": "examples/mspm0g3507/build.rs",
    "content": "//! This build script copies the `memory.x` file from the crate root into\n//! a directory where the linker can always find it at build time.\n//! For many projects this is optional, as the linker always searches the\n//! project root directory -- wherever `Cargo.toml` is. However, if you\n//! are using a workspace or have a more complicated build setup, this\n//! build script becomes required. Additionally, by requesting that\n//! Cargo re-run the build script whenever `memory.x` is changed,\n//! updating `memory.x` ensures a rebuild of the application with the\n//! new memory settings.\n\nuse std::env;\nuse std::fs::File;\nuse std::io::Write;\nuse std::path::PathBuf;\n\nfn main() {\n    // Put `memory.x` in our output directory and ensure it's\n    // on the linker search path.\n    let out = &PathBuf::from(env::var_os(\"OUT_DIR\").unwrap());\n    File::create(out.join(\"memory.x\"))\n        .unwrap()\n        .write_all(include_bytes!(\"memory.x\"))\n        .unwrap();\n    println!(\"cargo:rustc-link-search={}\", out.display());\n\n    // By default, Cargo will re-run a build script whenever\n    // any file in the project changes. By specifying `memory.x`\n    // here, we ensure the build script is only re-run when\n    // `memory.x` is changed.\n    println!(\"cargo:rerun-if-changed=memory.x\");\n\n    println!(\"cargo:rustc-link-arg-bins=--nmagic\");\n    println!(\"cargo:rustc-link-arg-bins=-Tlink.x\");\n    println!(\"cargo:rustc-link-arg-bins=-Tdefmt.x\");\n    // You must tell cargo to link interrupt groups if the rt feature is enabled.\n    println!(\"cargo:rustc-link-arg-bins=-Tinterrupt_group.x\");\n}\n"
  },
  {
    "path": "examples/mspm0g3507/memory.x",
    "content": "MEMORY\n{\n  FLASH : ORIGIN = 0x00000000, LENGTH = 128K\n  /* Select non-parity range of SRAM due to SRAM_ERR_01 errata in SLAZ758 */\n  RAM   : ORIGIN = 0x20200000, LENGTH = 32K\n}\n"
  },
  {
    "path": "examples/mspm0g3507/src/bin/adc.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_mspm0::adc::{self, Adc, Vrsel};\nuse embassy_mspm0::{Config, bind_interrupts, peripherals};\nuse embassy_time::Timer;\nuse {defmt_rtt as _, panic_halt as _};\n\nbind_interrupts!(struct Irqs {\n    ADC0 => adc::InterruptHandler<peripherals::ADC0>;\n});\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) -> ! {\n    info!(\"Hello world!\");\n    let p = embassy_mspm0::init(Config::default());\n\n    // Configure adc with sequence 0 to 1\n    let mut adc = Adc::new_async(p.ADC0, Default::default(), Irqs);\n    let sequence = [(&p.PA22.into(), Vrsel::VddaVssa), (&p.PB20.into(), Vrsel::VddaVssa)];\n    let mut readings = [0u16; 2];\n\n    loop {\n        let r = adc.read_channel(&p.PA27).await;\n        info!(\"Raw adc PA27: {}\", r);\n        // With a voltage range of 0-3.3V and a resolution of 12 bits, the raw value can be\n        // approximated to voltage (~0.0008 per step).\n        let mut x = r as u32;\n        x = x * 8;\n        info!(\"Adc voltage PA27: {},{:#04}\", x / 10_000, x % 10_000);\n        // Read a sequence of channels\n        adc.read_sequence(sequence.into_iter(), &mut readings).await;\n        info!(\"Raw adc sequence: {}\", readings);\n\n        Timer::after_millis(400).await;\n    }\n}\n"
  },
  {
    "path": "examples/mspm0g3507/src/bin/blinky.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_mspm0::Config;\nuse embassy_mspm0::gpio::{Level, Output};\nuse embassy_time::Timer;\nuse {defmt_rtt as _, panic_halt as _};\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) -> ! {\n    info!(\"Hello world!\");\n    let p = embassy_mspm0::init(Config::default());\n\n    let mut led1 = Output::new(p.PA0, Level::Low);\n    led1.set_inversion(true);\n\n    loop {\n        Timer::after_millis(400).await;\n\n        info!(\"Toggle\");\n        led1.toggle();\n    }\n}\n"
  },
  {
    "path": "examples/mspm0g3507/src/bin/button.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_mspm0::Config;\nuse embassy_mspm0::gpio::{Input, Level, Output, Pull};\nuse {defmt_rtt as _, panic_halt as _};\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) -> ! {\n    info!(\"Hello world!\");\n\n    let p = embassy_mspm0::init(Config::default());\n\n    let led1 = p.PA0;\n    let s2 = p.PB21;\n\n    let mut led1 = Output::new(led1, Level::Low);\n\n    let mut s2 = Input::new(s2, Pull::Up);\n\n    // led1 is active low\n    led1.set_high();\n\n    loop {\n        s2.wait_for_falling_edge().await;\n\n        info!(\"Switch 2 was pressed\");\n\n        led1.toggle();\n    }\n}\n"
  },
  {
    "path": "examples/mspm0g3507/src/bin/i2c.rs",
    "content": "//! This example uses FIFO with polling, and the maximum FIFO size is 8.\n//! Refer to async example to handle larger packets.\n//!\n//! This example controls AD5171 digital potentiometer via I2C with the LP-MSPM0G3507 board.\n\n#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_mspm0::i2c::{Config, I2c};\nuse embassy_time::Timer;\nuse {defmt_rtt as _, panic_halt as _};\n\nconst ADDRESS: u8 = 0x6a;\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) -> ! {\n    let p = embassy_mspm0::init(Default::default());\n\n    let instance = p.I2C1;\n    let scl = p.PB2;\n    let sda = p.PB3;\n\n    let mut i2c = unwrap!(I2c::new_blocking(instance, scl, sda, Config::default()));\n\n    let mut pot_value: u8 = 0;\n\n    loop {\n        let to_write = [0u8, pot_value];\n\n        match i2c.blocking_write(ADDRESS, &to_write) {\n            Ok(()) => info!(\"New potentioemter value: {}\", pot_value),\n            Err(e) => error!(\"I2c Error: {:?}\", e),\n        }\n\n        pot_value += 1;\n        // if reached 64th position (max)\n        // start over from lowest value\n        if pot_value == 64 {\n            pot_value = 0;\n        }\n        Timer::after_millis(500).await;\n    }\n}\n"
  },
  {
    "path": "examples/mspm0g3507/src/bin/i2c_async.rs",
    "content": "//! The example uses FIFO and interrupts, wrapped in async API.\n//!\n//! This example controls AD5171 digital potentiometer via I2C with the LP-MSPM0G3507 board.\n\n#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_mspm0::bind_interrupts;\nuse embassy_mspm0::i2c::{Config, I2c, InterruptHandler};\nuse embassy_mspm0::peripherals::I2C1;\nuse embassy_time::Timer;\nuse {defmt_rtt as _, panic_halt as _};\n\nconst ADDRESS: u8 = 0x6a;\n\nbind_interrupts!(struct Irqs {\n    I2C1 => InterruptHandler<I2C1>;\n});\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) -> ! {\n    let p = embassy_mspm0::init(Default::default());\n\n    let instance = p.I2C1;\n    let scl = p.PB2;\n    let sda = p.PB3;\n\n    let mut i2c = unwrap!(I2c::new_async(instance, scl, sda, Irqs, Config::default()));\n\n    let mut pot_value: u8 = 0;\n\n    loop {\n        let to_write = [0u8, pot_value];\n\n        match i2c.async_write(ADDRESS, &to_write).await {\n            Ok(()) => info!(\"New potentioemter value: {}\", pot_value),\n            Err(e) => error!(\"I2c Error: {:?}\", e),\n        }\n\n        pot_value += 1;\n        // if reached 64th position (max)\n        // start over from lowest value\n        if pot_value == 64 {\n            pot_value = 0;\n        }\n        Timer::after_millis(500).await;\n    }\n}\n"
  },
  {
    "path": "examples/mspm0g3507/src/bin/i2c_target.rs",
    "content": "//! Example of using async I2C target\n//!\n//! This uses the virtual COM port provided on the LP-MSPM0G3507 board.\n\n#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_mspm0::i2c::Config;\nuse embassy_mspm0::i2c_target::{Command, Config as TargetConfig, I2cTarget, ReadStatus};\nuse embassy_mspm0::peripherals::I2C1;\nuse embassy_mspm0::{bind_interrupts, i2c};\nuse {defmt_rtt as _, panic_halt as _};\n\nbind_interrupts!(struct Irqs {\n    I2C1 => i2c::InterruptHandler<I2C1>;\n});\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) -> ! {\n    let p = embassy_mspm0::init(Default::default());\n\n    let instance = p.I2C1;\n    let scl = p.PB2;\n    let sda = p.PB3;\n\n    let config = Config::default();\n    let mut target_config = TargetConfig::default();\n    target_config.target_addr = 0x48;\n    target_config.general_call = true;\n    let mut i2c = I2cTarget::new(instance, scl, sda, Irqs, config, target_config).unwrap();\n\n    let mut read = [0u8; 8];\n    let data = [8u8; 2];\n    let data_wr = [9u8; 2];\n\n    loop {\n        match i2c.listen(&mut read).await {\n            Ok(Command::GeneralCall(_)) => info!(\"General call received\"),\n            Ok(Command::Read) => {\n                info!(\"Read command received\");\n                match i2c.respond_to_read(&data).await.unwrap() {\n                    ReadStatus::Done => info!(\"Finished reading\"),\n                    ReadStatus::NeedMoreBytes => {\n                        info!(\"Read needs more bytes - will reset\");\n                        i2c.reset().unwrap();\n                    }\n                    ReadStatus::LeftoverBytes(_) => {\n                        info!(\"Leftover bytes received\");\n                        i2c.flush_tx_fifo();\n                    }\n                }\n            }\n            Ok(Command::Write(_)) => info!(\"Write command received\"),\n            Ok(Command::WriteRead(_)) => {\n                info!(\"Write-Read command received\");\n                i2c.respond_and_fill(&data_wr, 0xFE).await.unwrap();\n            }\n            Err(e) => info!(\"Got error {}\", e),\n        }\n    }\n}\n"
  },
  {
    "path": "examples/mspm0g3507/src/bin/mathacl_ops.rs",
    "content": "//! Example of using mathematical calculations performed by the MSPM0G3507 chip.\n//!\n//! It prints the result of basics mathematical calculation.\n\n#![no_std]\n#![no_main]\n\nuse core::f32::consts::PI;\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_mspm0::mathacl::{IQType, Mathacl, Precision};\nuse embassy_time::Timer;\nuse {defmt_rtt as _, panic_halt as _};\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) -> ! {\n    info!(\"Hello world!\");\n\n    let d = embassy_mspm0::init(Default::default());\n\n    let mut macl = Mathacl::new(d.MATHACL);\n\n    // value radians [-PI; PI]\n    let op1 = PI * 0.5;\n    match macl.sin(op1, Precision::High) {\n        Ok(res) => info!(\"sin({}) = {}\", op1, res),\n        Err(e) => error!(\"sin Error: {:?}\", e),\n    }\n\n    match macl.cos(op1, Precision::Medium) {\n        Ok(res) => info!(\"cos({}) = {}\", op1, res),\n        Err(e) => error!(\"cos Error: {:?}\", e),\n    }\n\n    // 1 bit for the sign, 15 bits integer & 16 bits fractional part\n    let div_iq_s1 = IQType::from_f32(-1.0, 15, true).unwrap();\n    let div_iq_s2 = IQType::from_f32(3.0, 15, true).unwrap();\n    match macl.div_iq(div_iq_s1, div_iq_s2) {\n        Ok(res) => info!(\"div IQ signed {}/{} = {}\", div_iq_s1.to_f32(), div_iq_s2.to_f32(), res),\n        Err(e) => error!(\"div IQ signed Error: {:?}\", e),\n    }\n\n    // 16 bits integer & 16 bits fractional part\n    let div_iq_u1 = IQType::from_f32(1.0, 16, false).unwrap();\n    let div_iq_u2 = IQType::from_f32(3.0, 16, false).unwrap();\n    match macl.div_iq(div_iq_u1, div_iq_u2) {\n        Ok(res) => info!(\n            \"div IQ unsigned {}/{} = {}\",\n            div_iq_u1.to_f32(),\n            div_iq_u2.to_f32(),\n            res\n        ),\n        Err(e) => error!(\"div IQ unsigned Error: {:?}\", e),\n    }\n\n    let div1_i32 = -5;\n    let div2_i32 = -2;\n    match macl.div_i32(div1_i32, div2_i32) {\n        Ok(res) => info!(\"div i32 {}/{} = {}\", div1_i32, div2_i32, res),\n        Err(e) => error!(\"div i32 Error: {:?}\", e),\n    }\n\n    let div1_u32 = 5;\n    let div2_u32 = 2;\n    match macl.div_u32(div1_u32, div2_u32) {\n        Ok(res) => info!(\"div u32 {}/{} = {}\", div1_u32, div2_u32, res),\n        Err(e) => error!(\"div u32 Error: {:?}\", e),\n    }\n\n    loop {\n        Timer::after_millis(500).await;\n    }\n}\n"
  },
  {
    "path": "examples/mspm0g3507/src/bin/uart.rs",
    "content": "//! Example of using blocking uart\n//!\n//! This uses the virtual COM port provided on the LP-MSPM0G3507 board.\n\n#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_mspm0::uart::{Config, Uart};\nuse {defmt_rtt as _, panic_halt as _};\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) -> ! {\n    info!(\"Hello world!\");\n\n    let p = embassy_mspm0::init(Default::default());\n\n    let instance = p.UART0;\n    let tx = p.PA10;\n    let rx = p.PA11;\n\n    let config = Config::default();\n    let mut uart = unwrap!(Uart::new_blocking(instance, rx, tx, config));\n\n    unwrap!(uart.blocking_write(b\"Hello Embassy World!\\r\\n\"));\n    info!(\"wrote Hello, starting echo\");\n\n    let mut buf = [0u8; 1];\n\n    loop {\n        unwrap!(uart.blocking_read(&mut buf));\n        unwrap!(uart.blocking_write(&buf));\n    }\n}\n"
  },
  {
    "path": "examples/mspm0g3507/src/bin/uart_buffered.rs",
    "content": "//! Example of using buffered uart\n//!\n//! This uses the virtual COM port provided on the LP-MSPM0G3507 board.\n\n#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_mspm0::uart::{self, BufferedUart, Config};\nuse embassy_mspm0::{bind_interrupts, peripherals};\nuse embedded_io_async::{Read, Write};\nuse {defmt_rtt as _, panic_halt as _};\n\nbind_interrupts!(\n    struct Irqs {\n        UART0 => uart::BufferedInterruptHandler<peripherals::UART0>;\n    }\n);\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) -> ! {\n    info!(\"Hello world!\");\n\n    let p = embassy_mspm0::init(Default::default());\n\n    let instance = p.UART0;\n    let tx = p.PA10;\n    let rx = p.PA11;\n    let mut tx_buf = [0u8; 32];\n    let mut rx_buf = [0u8; 32];\n\n    let config = Config::default();\n    let mut uart = unwrap!(BufferedUart::new(\n        instance,\n        tx,\n        rx,\n        Irqs,\n        &mut tx_buf,\n        &mut rx_buf,\n        config\n    ));\n\n    unwrap!(uart.blocking_write(b\"Hello Embassy World (buffered)!\\r\\n\"));\n    info!(\"wrote Hello, starting echo\");\n\n    let mut buf = [0u8; 1];\n\n    loop {\n        unwrap!(uart.read(&mut buf).await);\n        unwrap!(uart.write(&buf).await);\n    }\n}\n"
  },
  {
    "path": "examples/mspm0g3507/src/bin/wwdt.rs",
    "content": "//! Example of using window watchdog timer in the MSPM0G3507 chip.\n//!\n//! It tests the use case when watchdog timer is expired and when watchdog is pet too early.\n\n#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_mspm0::gpio::{Level, Output};\nuse embassy_mspm0::wwdt::{ClosedWindowPercentage, Config, Timeout, Watchdog};\nuse embassy_time::Timer;\nuse {defmt_rtt as _, panic_halt as _};\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) -> ! {\n    info!(\"Hello world!\");\n\n    let p = embassy_mspm0::init(Default::default());\n    let mut conf = Config::default();\n    conf.timeout = Timeout::Sec1;\n\n    // watchdog also resets the system if the pet comes too early,\n    // less than 250 msec == 25% from 1 sec\n    conf.closed_window = ClosedWindowPercentage::TwentyFive;\n    let mut wdt = Watchdog::new(p.WWDT0, conf);\n    info!(\"Started the watchdog timer\");\n\n    let mut led1 = Output::new(p.PA0, Level::High);\n    led1.set_inversion(true);\n    Timer::after_millis(900).await;\n\n    for _ in 1..=5 {\n        info!(\"pet watchdog\");\n        led1.toggle();\n        wdt.pet();\n        Timer::after_millis(500).await;\n    }\n\n    // watchdog timeout test\n    info!(\"Stopped the pet command, device will reset in less than 1 second\");\n    loop {\n        led1.toggle();\n        Timer::after_millis(500).await;\n    }\n\n    // watchdog \"too early\" test\n    // info!(\"Device will reset when the pet comes too early\");\n    // loop {\n    //     led1.toggle();\n    //     wdt.pet();\n    //     Timer::after_millis(200).await;\n    // }\n}\n"
  },
  {
    "path": "examples/mspm0g3519/.cargo/config.toml",
    "content": "[target.'cfg(all(target_arch = \"arm\", target_os = \"none\"))']\n# replace MSPM0G3519 with your chip as listed in `probe-rs chip list`\nrunner = \"probe-rs run --restore-unwritten --verify --chip MSPM0G3519 --protocol=swd\"\n\n[build]\ntarget = \"thumbv6m-none-eabi\"\n\n[env]\nDEFMT_LOG = \"trace\"\n"
  },
  {
    "path": "examples/mspm0g3519/Cargo.toml",
    "content": "[package]\nedition = \"2024\"\nname = \"embassy-mspm0-g3519-examples\"\nversion = \"0.1.0\"\nlicense = \"MIT OR Apache-2.0\"\npublish = false\n\n[dependencies]\nembassy-mspm0 = { version = \"0.1.0\", path = \"../../embassy-mspm0\", features = [\"mspm0g3519pz\", \"defmt\", \"rt\", \"time-driver-any\"] }\nembassy-executor = { version = \"0.10.0\", path = \"../../embassy-executor\", features = [\"platform-cortex-m\", \"executor-thread\", \"executor-interrupt\"] }\nembassy-sync = { version = \"0.8.0\", path = \"../../embassy-sync\", features = [\"defmt\"] }\nembassy-time = { version = \"0.5.1\", path = \"../../embassy-time\", features = [\"defmt\"] }\npanic-halt = \"1.0.0\"\ncortex-m = { version = \"0.7.6\", features = [\"inline-asm\", \"critical-section-single-core\"] }\ncortex-m-rt = { version = \"0.7.0\"}\ndefmt = \"1.0.1\"\ndefmt-rtt = \"1.0.0\"\npanic-probe = { version = \"1.0.0\", features = [\"print-defmt\"] }\npanic-semihosting = \"0.6.0\"\n\n[profile.release]\ndebug = 2\n\n[package.metadata.embassy]\nbuild = [\n  { target = \"thumbv6m-none-eabi\", artifact-dir = \"out/examples/mspm0g3519\" }\n]\n"
  },
  {
    "path": "examples/mspm0g3519/README.md",
    "content": "# Examples for MSPM0G3519\n\nRun individual examples with\n```\ncargo run --bin <module-name>\n```\nfor example\n```\ncargo run --bin blinky\n```\n\n## Checklist before running examples\nA large number of the examples are written for the [LP-MSPM0G3519](https://www.ti.com/tool/LP-MSPM0G3519) board.\n\nYou might need to adjust `.cargo/config.toml`, `Cargo.toml` and possibly update pin numbers or peripherals to match the specific MCU or board you are using.\n\n* [ ] Update .cargo/config.toml with the correct probe-rs command to use your specific MCU. For example for G3519 it should be `probe-rs run --chip MSPM0G3519`. (use `probe-rs chip list` to find your chip)\n* [ ] Update Cargo.toml to have the correct `embassy-mspm0` feature. For the LP-MSPM0G3519 it should be `mspm0g3519pz`. Look in the `Cargo.toml` file of the `embassy-mspm0` project to find the correct feature flag for your chip.\n* [ ] If your board has a special clock or power configuration, make sure that it is set up appropriately.\n* [ ] If your board has different pin mapping, update any pin numbers or peripherals in the given example code to match your schematic\n\nIf you are unsure, please drop by the Embassy Matrix chat for support, and let us know:\n\n* Which example you are trying to run\n* Which chip and board you are using\n\nEmbassy Chat: https://matrix.to/#/#embassy-rs:matrix.org\n"
  },
  {
    "path": "examples/mspm0g3519/build.rs",
    "content": "//! This build script copies the `memory.x` file from the crate root into\n//! a directory where the linker can always find it at build time.\n//! For many projects this is optional, as the linker always searches the\n//! project root directory -- wherever `Cargo.toml` is. However, if you\n//! are using a workspace or have a more complicated build setup, this\n//! build script becomes required. Additionally, by requesting that\n//! Cargo re-run the build script whenever `memory.x` is changed,\n//! updating `memory.x` ensures a rebuild of the application with the\n//! new memory settings.\n\nuse std::env;\nuse std::fs::File;\nuse std::io::Write;\nuse std::path::PathBuf;\n\nfn main() {\n    // Put `memory.x` in our output directory and ensure it's\n    // on the linker search path.\n    let out = &PathBuf::from(env::var_os(\"OUT_DIR\").unwrap());\n    File::create(out.join(\"memory.x\"))\n        .unwrap()\n        .write_all(include_bytes!(\"memory.x\"))\n        .unwrap();\n    println!(\"cargo:rustc-link-search={}\", out.display());\n\n    // By default, Cargo will re-run a build script whenever\n    // any file in the project changes. By specifying `memory.x`\n    // here, we ensure the build script is only re-run when\n    // `memory.x` is changed.\n    println!(\"cargo:rerun-if-changed=memory.x\");\n\n    println!(\"cargo:rustc-link-arg-bins=--nmagic\");\n    println!(\"cargo:rustc-link-arg-bins=-Tlink.x\");\n    println!(\"cargo:rustc-link-arg-bins=-Tdefmt.x\");\n    // You must tell cargo to link interrupt groups if the rt feature is enabled.\n    println!(\"cargo:rustc-link-arg-bins=-Tinterrupt_group.x\");\n}\n"
  },
  {
    "path": "examples/mspm0g3519/memory.x",
    "content": "MEMORY\n{\n  FLASH : ORIGIN = 0x00000000, LENGTH = 512K\n  /* Select non-parity range of SRAM due to SRAM_ERR_01 errata in SLAZ758 */\n  RAM   : ORIGIN = 0x20200000, LENGTH = 128K\n}\n"
  },
  {
    "path": "examples/mspm0g3519/src/bin/blinky.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_mspm0::Config;\nuse embassy_mspm0::gpio::{Level, Output};\nuse embassy_time::Timer;\nuse {defmt_rtt as _, panic_halt as _};\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) -> ! {\n    info!(\"Hello world!\");\n    let p = embassy_mspm0::init(Config::default());\n\n    let mut led1 = Output::new(p.PA0, Level::Low);\n    led1.set_inversion(true);\n\n    loop {\n        Timer::after_millis(400).await;\n\n        info!(\"Toggle\");\n        led1.toggle();\n    }\n}\n"
  },
  {
    "path": "examples/mspm0g3519/src/bin/button.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_mspm0::Config;\nuse embassy_mspm0::gpio::{Input, Level, Output, Pull};\nuse {defmt_rtt as _, panic_halt as _};\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) -> ! {\n    info!(\"Hello world!\");\n\n    let p = embassy_mspm0::init(Config::default());\n\n    let led1 = p.PA0;\n    let s2 = p.PB3;\n\n    let mut led1 = Output::new(led1, Level::Low);\n\n    let mut s2 = Input::new(s2, Pull::Up);\n\n    // led1 is active low\n    led1.set_high();\n\n    loop {\n        s2.wait_for_falling_edge().await;\n\n        info!(\"Switch 2 was pressed\");\n\n        led1.toggle();\n    }\n}\n"
  },
  {
    "path": "examples/mspm0g3519/src/bin/uart.rs",
    "content": "//! Example of using blocking uart\n//!\n//! This uses the virtual COM port provided on the LP-MSPM0G3519 board.\n\n#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_mspm0::uart::{Config, Uart};\nuse {defmt_rtt as _, panic_halt as _};\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) -> ! {\n    info!(\"Hello world!\");\n\n    let p = embassy_mspm0::init(Default::default());\n\n    let instance = p.UART0;\n    let tx = p.PA10;\n    let rx = p.PA11;\n\n    let config = Config::default();\n    let mut uart = unwrap!(Uart::new_blocking(instance, rx, tx, config));\n\n    unwrap!(uart.blocking_write(b\"Hello Embassy World!\\r\\n\"));\n    info!(\"wrote Hello, starting echo\");\n\n    let mut buf = [0u8; 1];\n\n    loop {\n        unwrap!(uart.blocking_read(&mut buf));\n        unwrap!(uart.blocking_write(&buf));\n    }\n}\n"
  },
  {
    "path": "examples/mspm0g3519/src/bin/wwdt.rs",
    "content": "//! Example of using window watchdog timer in the MSPM0G3519 chip.\n//!\n//! It tests the use case when watchdog timer is expired and when watchdog is pet too early.\n\n#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_mspm0::gpio::{Level, Output};\nuse embassy_mspm0::wwdt::{ClosedWindowPercentage, Config, Timeout, Watchdog};\nuse embassy_time::Timer;\nuse {defmt_rtt as _, panic_halt as _};\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) -> ! {\n    info!(\"Hello world!\");\n\n    let p = embassy_mspm0::init(Default::default());\n    let mut conf = Config::default();\n    conf.timeout = Timeout::Sec1;\n\n    // watchdog also resets the system if the pet comes too early,\n    // less than 250 msec == 25% from 1 sec\n    conf.closed_window = ClosedWindowPercentage::TwentyFive;\n    let mut wdt = Watchdog::new(p.WWDT0, conf);\n    info!(\"Started the watchdog timer\");\n\n    let mut led1 = Output::new(p.PA0, Level::High);\n    led1.set_inversion(true);\n    Timer::after_millis(900).await;\n\n    for _ in 1..=5 {\n        info!(\"pet watchdog\");\n        led1.toggle();\n        wdt.pet();\n        Timer::after_millis(500).await;\n    }\n\n    // watchdog timeout test\n    info!(\"Stopped the pet command, device will reset in less than 1 second\");\n    loop {\n        led1.toggle();\n        Timer::after_millis(500).await;\n    }\n\n    // watchdog \"too early\" test\n    // info!(\"Device will reset when the pet comes too early\");\n    // loop {\n    //     led1.toggle();\n    //     wdt.pet();\n    //     Timer::after_millis(200).await;\n    // }\n}\n"
  },
  {
    "path": "examples/mspm0g5187/.cargo/config.toml",
    "content": "[target.'cfg(all(target_arch = \"arm\", target_os = \"none\"))']\n# replace MSPM0G5187 with your chip as listed in `probe-rs chip list`\nrunner = \"probe-rs run --restore-unwritten --verify --chip MSPM0G5187 --protocol=swd\"\n\n[build]\ntarget = \"thumbv6m-none-eabi\"\n\n[env]\nDEFMT_LOG = \"trace\"\n"
  },
  {
    "path": "examples/mspm0g5187/Cargo.toml",
    "content": "[package]\nedition = \"2024\"\nname = \"embassy-mspm0-g5187-examples\"\nversion = \"0.1.0\"\nlicense = \"MIT OR Apache-2.0\"\npublish = false\n\n[dependencies]\nembassy-mspm0 = { version = \"0.1.0\", path = \"../../embassy-mspm0\", features = [\"mspm0g5187pm\", \"defmt\", \"rt\", \"time-driver-any\"] }\nembassy-executor = { version = \"0.10.0\", path = \"../../embassy-executor\", features = [\"platform-cortex-m\", \"executor-thread\", \"executor-interrupt\"] }\nembassy-sync = { version = \"0.8.0\", path = \"../../embassy-sync\", features = [\"defmt\"] }\nembassy-time = { version = \"0.5.1\", path = \"../../embassy-time\", features = [\"defmt\"] }\npanic-halt = \"1.0.0\"\ncortex-m = { version = \"0.7.6\", features = [\"inline-asm\", \"critical-section-single-core\"] }\ncortex-m-rt = { version = \"0.7.0\"}\ndefmt = \"1.0.1\"\ndefmt-rtt = \"1.0.0\"\npanic-probe = { version = \"1.0.0\", features = [\"print-defmt\"] }\npanic-semihosting = \"0.6.0\"\n\n[profile.release]\ndebug = 2\n\n[package.metadata.embassy]\nbuild = [\n  { target = \"thumbv6m-none-eabi\", artifact-dir = \"out/examples/mspm0g5187\" }\n]\n"
  },
  {
    "path": "examples/mspm0g5187/README.md",
    "content": "# Examples for MSPM0G5187\n\nRun individual examples with\n```\ncargo run --bin <module-name>\n```\nfor example\n```\ncargo run --bin blinky\n```\n\n## Checklist before running examples\nA large number of the examples are written for the [LP-MSPM0G5187](https://www.ti.com/tool/LP-MSPM0G5187) board.\n\nYou might need to adjust `.cargo/config.toml`, `Cargo.toml` and possibly update pin numbers or peripherals to match the specific MCU or board you are using.\n\n* [ ] Update .cargo/config.toml with the correct probe-rs command to use your specific MCU. For example for G5187 it should be `probe-rs run --chip MSPM0G5187`. (use `probe-rs chip list` to find your chip)\n* [ ] Update Cargo.toml to have the correct `embassy-mspm0` feature. For the LP-MSPM0G3519 it should be `mspm0g3519pz`. Look in the `Cargo.toml` file of the `embassy-mspm0` project to find the correct feature flag for your chip.\n* [ ] If your board has a special clock or power configuration, make sure that it is set up appropriately.\n* [ ] If your board has different pin mapping, update any pin numbers or peripherals in the given example code to match your schematic\n\nIf you are unsure, please drop by the Embassy Matrix chat for support, and let us know:\n\n* Which example you are trying to run\n* Which chip and board you are using\n\nEmbassy Chat: https://matrix.to/#/#embassy-rs:matrix.org\n"
  },
  {
    "path": "examples/mspm0g5187/build.rs",
    "content": "//! This build script copies the `memory.x` file from the crate root into\n//! a directory where the linker can always find it at build time.\n//! For many projects this is optional, as the linker always searches the\n//! project root directory -- wherever `Cargo.toml` is. However, if you\n//! are using a workspace or have a more complicated build setup, this\n//! build script becomes required. Additionally, by requesting that\n//! Cargo re-run the build script whenever `memory.x` is changed,\n//! updating `memory.x` ensures a rebuild of the application with the\n//! new memory settings.\n\nuse std::env;\nuse std::fs::File;\nuse std::io::Write;\nuse std::path::PathBuf;\n\nfn main() {\n    // Put `memory.x` in our output directory and ensure it's\n    // on the linker search path.\n    let out = &PathBuf::from(env::var_os(\"OUT_DIR\").unwrap());\n    File::create(out.join(\"memory.x\"))\n        .unwrap()\n        .write_all(include_bytes!(\"memory.x\"))\n        .unwrap();\n    println!(\"cargo:rustc-link-search={}\", out.display());\n\n    // By default, Cargo will re-run a build script whenever\n    // any file in the project changes. By specifying `memory.x`\n    // here, we ensure the build script is only re-run when\n    // `memory.x` is changed.\n    println!(\"cargo:rerun-if-changed=memory.x\");\n\n    println!(\"cargo:rustc-link-arg-bins=--nmagic\");\n    println!(\"cargo:rustc-link-arg-bins=-Tlink.x\");\n    println!(\"cargo:rustc-link-arg-bins=-Tdefmt.x\");\n    // You must tell cargo to link interrupt groups if the rt feature is enabled.\n    println!(\"cargo:rustc-link-arg-bins=-Tinterrupt_group.x\");\n}\n"
  },
  {
    "path": "examples/mspm0g5187/memory.x",
    "content": "MEMORY\n{\n  FLASH : ORIGIN = 0x00000000, LENGTH = 128K\n  /* Select non-parity range of SRAM due to SRAM_ERR_01 errata in SLAZ758 */\n  RAM   : ORIGIN = 0x20200000, LENGTH = 32K\n}\n"
  },
  {
    "path": "examples/mspm0g5187/src/bin/blinky.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_mspm0::Config;\nuse embassy_mspm0::gpio::{Level, Output};\nuse embassy_time::Timer;\nuse {defmt_rtt as _, panic_halt as _};\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) -> ! {\n    info!(\"Hello world!\");\n    let p = embassy_mspm0::init(Config::default());\n\n    let mut led1 = Output::new(p.PA0, Level::Low);\n    led1.set_inversion(true);\n\n    loop {\n        Timer::after_millis(400).await;\n\n        info!(\"Toggle\");\n        led1.toggle();\n    }\n}\n"
  },
  {
    "path": "examples/mspm0g5187/src/bin/button.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_mspm0::Config;\nuse embassy_mspm0::gpio::{Input, Level, Output, Pull};\nuse {defmt_rtt as _, panic_halt as _};\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) -> ! {\n    info!(\"Hello world!\");\n\n    let p = embassy_mspm0::init(Config::default());\n\n    let led1 = p.PA0;\n    let s2 = p.PA7;\n\n    let mut led1 = Output::new(led1, Level::Low);\n\n    let mut s2 = Input::new(s2, Pull::Up);\n\n    // led1 is active low\n    led1.set_high();\n\n    loop {\n        s2.wait_for_falling_edge().await;\n\n        info!(\"Switch 2 was pressed\");\n\n        led1.toggle();\n    }\n}\n"
  },
  {
    "path": "examples/mspm0g5187/src/bin/wwdt.rs",
    "content": "//! Example of using window watchdog timer in the MSPM0G5187 chip.\n//!\n//! It tests the use case when watchdog timer is expired and when watchdog is pet too early.\n\n#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_mspm0::gpio::{Level, Output};\nuse embassy_mspm0::wwdt::{ClosedWindowPercentage, Config, Timeout, Watchdog};\nuse embassy_time::Timer;\nuse {defmt_rtt as _, panic_halt as _};\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) -> ! {\n    info!(\"Hello world!\");\n\n    let p = embassy_mspm0::init(Default::default());\n    let mut conf = Config::default();\n    conf.timeout = Timeout::Sec1;\n\n    // watchdog also resets the system if the pet comes too early,\n    // less than 250 msec == 25% from 1 sec\n    conf.closed_window = ClosedWindowPercentage::TwentyFive;\n    let mut wdt = Watchdog::new(p.WWDT0, conf);\n    info!(\"Started the watchdog timer\");\n\n    let mut led1 = Output::new(p.PA0, Level::High);\n    led1.set_inversion(true);\n    Timer::after_millis(900).await;\n\n    for _ in 1..=5 {\n        info!(\"pet watchdog\");\n        led1.toggle();\n        wdt.pet();\n        Timer::after_millis(500).await;\n    }\n\n    // watchdog timeout test\n    info!(\"Stopped the pet command, device will reset in less than 1 second\");\n    loop {\n        led1.toggle();\n        Timer::after_millis(500).await;\n    }\n\n    // watchdog \"too early\" test\n    // info!(\"Device will reset when the pet comes too early\");\n    // loop {\n    //     led1.toggle();\n    //     wdt.pet();\n    //     Timer::after_millis(200).await;\n    // }\n}\n"
  },
  {
    "path": "examples/mspm0l1306/.cargo/config.toml",
    "content": "[target.'cfg(all(target_arch = \"arm\", target_os = \"none\"))']\n# replace MSPM0L1306 with your chip as listed in `probe-rs chip list`\nrunner = \"probe-rs run --chip MSPM0L1306 --protocol=swd\"\n\n[build]\ntarget = \"thumbv6m-none-eabi\"\n\n[env]\nDEFMT_LOG = \"trace\"\n"
  },
  {
    "path": "examples/mspm0l1306/Cargo.toml",
    "content": "[package]\nedition = \"2024\"\nname = \"embassy-mspm0-l1306-examples\"\nversion = \"0.1.0\"\nlicense = \"MIT OR Apache-2.0\"\npublish = false\n\n[dependencies]\nembassy-mspm0 = { version = \"0.1.0\", path = \"../../embassy-mspm0\", features = [\"mspm0l1306rhb\", \"defmt\", \"rt\", \"time-driver-any\"] }\nembassy-executor = { version = \"0.10.0\", path = \"../../embassy-executor\", features = [\"platform-cortex-m\", \"executor-thread\", \"executor-interrupt\"] }\nembassy-sync = { version = \"0.8.0\", path = \"../../embassy-sync\", features = [\"defmt\"] }\nembassy-time = { version = \"0.5.1\", path = \"../../embassy-time\", features = [\"defmt\"] }\npanic-halt = \"1.0.0\"\ncortex-m = { version = \"0.7.6\", features = [\"inline-asm\", \"critical-section-single-core\"] }\ncortex-m-rt = { version = \"0.7.0\"}\ndefmt = \"1.0.1\"\ndefmt-rtt = \"1.0.0\"\npanic-probe = { version = \"1.0.0\", features = [\"print-defmt\"] }\npanic-semihosting = \"0.6.0\"\n\n[profile.release]\ndebug = 2\n\n[profile.dev]\ndebug = 2\nopt-level = 2\n\n[package.metadata.embassy]\nbuild = [\n  { target = \"thumbv6m-none-eabi\", artifact-dir = \"out/examples/mspm0l1306\" }\n]\n"
  },
  {
    "path": "examples/mspm0l1306/README.md",
    "content": "# Examples for MSPM0L1306\n\nRun individual examples with\n```\ncargo run --bin <module-name>\n```\nfor example\n```\ncargo run --bin blinky\n```\n\n## Checklist before running examples\nA large number of the examples are written for the [LP-MSPM0L1306](https://www.ti.com/tool/LP-MSPM0L1306) board.\n\nYou might need to adjust `.cargo/config.toml`, `Cargo.toml` and possibly update pin numbers or peripherals to match the specific MCU or board you are using.\n\n* [ ] Update .cargo/config.toml with the correct probe-rs command to use your specific MCU. For example for L1306 it should be `probe-rs run --chip MSPM0L1306`. (use `probe-rs chip list` to find your chip)\n* [ ] Update Cargo.toml to have the correct `embassy-mspm0` feature. For the LP-MSPM0L1306 it should be `mspm0l1306rhb`. Look in the `Cargo.toml` file of the `embassy-mspm0` project to find the correct feature flag for your chip.\n* [ ] If your board has a special clock or power configuration, make sure that it is set up appropriately.\n* [ ] If your board has different pin mapping, update any pin numbers or peripherals in the given example code to match your schematic\n\nIf you are unsure, please drop by the Embassy Matrix chat for support, and let us know:\n\n* Which example you are trying to run\n* Which chip and board you are using\n\nEmbassy Chat: https://matrix.to/#/#embassy-rs:matrix.org\n"
  },
  {
    "path": "examples/mspm0l1306/build.rs",
    "content": "//! This build script copies the `memory.x` file from the crate root into\n//! a directory where the linker can always find it at build time.\n//! For many projects this is optional, as the linker always searches the\n//! project root directory -- wherever `Cargo.toml` is. However, if you\n//! are using a workspace or have a more complicated build setup, this\n//! build script becomes required. Additionally, by requesting that\n//! Cargo re-run the build script whenever `memory.x` is changed,\n//! updating `memory.x` ensures a rebuild of the application with the\n//! new memory settings.\n\nuse std::env;\nuse std::fs::File;\nuse std::io::Write;\nuse std::path::PathBuf;\n\nfn main() {\n    // Put `memory.x` in our output directory and ensure it's\n    // on the linker search path.\n    let out = &PathBuf::from(env::var_os(\"OUT_DIR\").unwrap());\n    File::create(out.join(\"memory.x\"))\n        .unwrap()\n        .write_all(include_bytes!(\"memory.x\"))\n        .unwrap();\n    println!(\"cargo:rustc-link-search={}\", out.display());\n\n    // By default, Cargo will re-run a build script whenever\n    // any file in the project changes. By specifying `memory.x`\n    // here, we ensure the build script is only re-run when\n    // `memory.x` is changed.\n    println!(\"cargo:rerun-if-changed=memory.x\");\n\n    println!(\"cargo:rustc-link-arg-bins=--nmagic\");\n    println!(\"cargo:rustc-link-arg-bins=-Tlink.x\");\n    println!(\"cargo:rustc-link-arg-bins=-Tdefmt.x\");\n    // You must tell cargo to link interrupt groups if the rt feature is enabled.\n    println!(\"cargo:rustc-link-arg-bins=-Tinterrupt_group.x\");\n}\n"
  },
  {
    "path": "examples/mspm0l1306/memory.x",
    "content": "MEMORY\n{\n  FLASH : ORIGIN = 0x00000000, LENGTH = 64K\n  RAM   : ORIGIN = 0x20000000, LENGTH = 4K\n}\n"
  },
  {
    "path": "examples/mspm0l1306/src/bin/adc.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_mspm0::adc::{self, Adc, Vrsel};\nuse embassy_mspm0::{Config, bind_interrupts, peripherals};\nuse embassy_time::Timer;\nuse {defmt_rtt as _, panic_halt as _};\n\nbind_interrupts!(struct Irqs {\n    ADC0 => adc::InterruptHandler<peripherals::ADC0>;\n});\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) -> ! {\n    info!(\"Hello world!\");\n    let p = embassy_mspm0::init(Config::default());\n\n    // Configure adc with sequence 0 to 1\n    let mut adc = Adc::new_async(p.ADC0, Default::default(), Irqs);\n    let sequence = [(&p.PA22.into(), Vrsel::VddaVssa), (&p.PA15.into(), Vrsel::VddaVssa)];\n    let mut readings = [0u16; 2];\n\n    loop {\n        let r = adc.read_channel(&p.PA27).await;\n        info!(\"Raw adc PA27: {}\", r);\n        // With a voltage range of 0-3.3V and a resolution of 12 bits, the raw value can be\n        // approximated to voltage (~0.0008 per step).\n        let mut x = r as u32;\n        x = x * 8;\n        info!(\"Adc voltage PA27: {},{:#04}\", x / 10_000, x % 10_000);\n        // Read a sequence of channels\n        adc.read_sequence(sequence.into_iter(), &mut readings).await;\n        info!(\"Raw adc sequence: {}\", readings);\n\n        Timer::after_millis(400).await;\n    }\n}\n"
  },
  {
    "path": "examples/mspm0l1306/src/bin/blinky.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_mspm0::Config;\nuse embassy_mspm0::gpio::{Level, Output};\nuse embassy_time::Timer;\nuse {defmt_rtt as _, panic_halt as _};\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) -> ! {\n    info!(\"Hello world!\");\n    let p = embassy_mspm0::init(Config::default());\n\n    let mut led1 = Output::new(p.PA0, Level::Low);\n    led1.set_inversion(true);\n\n    loop {\n        Timer::after_millis(400).await;\n\n        info!(\"Toggle\");\n        led1.toggle();\n    }\n}\n"
  },
  {
    "path": "examples/mspm0l1306/src/bin/button.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_mspm0::Config;\nuse embassy_mspm0::gpio::{Input, Level, Output, Pull};\nuse {defmt_rtt as _, panic_halt as _};\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) -> ! {\n    info!(\"Hello world!\");\n\n    let p = embassy_mspm0::init(Config::default());\n\n    let led1 = p.PA0;\n    let s2 = p.PA14;\n\n    let mut led1 = Output::new(led1, Level::Low);\n\n    let mut s2 = Input::new(s2, Pull::Up);\n\n    // led1 is active low\n    led1.set_high();\n\n    loop {\n        s2.wait_for_falling_edge().await;\n\n        info!(\"Switch 2 was pressed\");\n\n        led1.toggle();\n    }\n}\n"
  },
  {
    "path": "examples/mspm0l1306/src/bin/i2c.rs",
    "content": "//! This example uses FIFO with polling, and the maximum FIFO size is 8.\n//! Refer to async example to handle larger packets.\n//!\n//! This example controls AD5171 digital potentiometer via I2C with the LP-MSPM0L1306 board.\n\n#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_mspm0::i2c::{Config, I2c};\nuse embassy_time::Timer;\nuse {defmt_rtt as _, panic_halt as _};\n\nconst ADDRESS: u8 = 0x2c;\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) -> ! {\n    let p = embassy_mspm0::init(Default::default());\n\n    let instance = p.I2C0;\n    let scl = p.PA1;\n    let sda = p.PA0;\n\n    let mut i2c = unwrap!(I2c::new_blocking(instance, scl, sda, Config::default()));\n\n    let mut pot_value: u8 = 0;\n\n    loop {\n        let to_write = [0u8, pot_value];\n\n        match i2c.blocking_write(ADDRESS, &to_write) {\n            Ok(()) => info!(\"New potentioemter value: {}\", pot_value),\n            Err(e) => error!(\"I2c Error: {:?}\", e),\n        }\n\n        pot_value += 1;\n        // if reached 64th position (max)\n        // start over from lowest value\n        if pot_value == 64 {\n            pot_value = 0;\n        }\n        Timer::after_millis(500).await;\n    }\n}\n"
  },
  {
    "path": "examples/mspm0l1306/src/bin/i2c_async.rs",
    "content": "//! The example uses FIFO and interrupts, wrapped in async API.\n//!\n//! This example controls AD5171 digital potentiometer via I2C with the LP-MSPM0L1306 board.\n\n#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_mspm0::bind_interrupts;\nuse embassy_mspm0::i2c::{Config, I2c, InterruptHandler};\nuse embassy_mspm0::peripherals::I2C0;\nuse embassy_time::Timer;\nuse {defmt_rtt as _, panic_halt as _};\n\nconst ADDRESS: u8 = 0x6a;\n\nbind_interrupts!(struct Irqs {\n    I2C0 => InterruptHandler<I2C0>;\n});\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) -> ! {\n    let p = embassy_mspm0::init(Default::default());\n\n    let instance = p.I2C0;\n    let scl = p.PA1;\n    let sda = p.PA0;\n\n    let mut i2c = unwrap!(I2c::new_async(instance, scl, sda, Irqs, Config::default()));\n\n    let mut pot_value: u8 = 0;\n\n    loop {\n        let to_write = [0u8, pot_value];\n\n        match i2c.async_write(ADDRESS, &to_write).await {\n            Ok(()) => info!(\"New potentioemter value: {}\", pot_value),\n            Err(e) => error!(\"I2c Error: {:?}\", e),\n        }\n\n        pot_value += 1;\n        // if reached 64th position (max)\n        // start over from lowest value\n        if pot_value == 64 {\n            pot_value = 0;\n        }\n        Timer::after_millis(500).await;\n    }\n}\n"
  },
  {
    "path": "examples/mspm0l1306/src/bin/i2c_target.rs",
    "content": "//! Example of using async I2C target\n//!\n//! This uses the virtual COM port provided on the LP-MSPM0L1306 board.\n\n#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_mspm0::i2c::Config;\nuse embassy_mspm0::i2c_target::{Command, Config as TargetConfig, I2cTarget, ReadStatus};\nuse embassy_mspm0::peripherals::I2C0;\nuse embassy_mspm0::{bind_interrupts, i2c};\nuse {defmt_rtt as _, panic_halt as _};\n\nbind_interrupts!(struct Irqs {\n    I2C0 => i2c::InterruptHandler<I2C0>;\n});\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) -> ! {\n    let p = embassy_mspm0::init(Default::default());\n\n    let instance = p.I2C0;\n    let scl = p.PA1;\n    let sda = p.PA0;\n\n    let config = Config::default();\n    let mut target_config = TargetConfig::default();\n    target_config.target_addr = 0x48;\n    target_config.general_call = true;\n    let mut i2c = I2cTarget::new(instance, scl, sda, Irqs, config, target_config).unwrap();\n\n    let mut read = [0u8; 8];\n    let data = [8u8; 2];\n    let data_wr = [9u8; 2];\n\n    loop {\n        match i2c.listen(&mut read).await {\n            Ok(Command::GeneralCall(_)) => info!(\"General call received\"),\n            Ok(Command::Read) => {\n                info!(\"Read command received\");\n                match i2c.respond_to_read(&data).await.unwrap() {\n                    ReadStatus::Done => info!(\"Finished reading\"),\n                    ReadStatus::NeedMoreBytes => {\n                        info!(\"Read needs more bytes - will reset\");\n                        i2c.reset().unwrap();\n                    }\n                    ReadStatus::LeftoverBytes(_) => {\n                        info!(\"Leftover bytes received\");\n                        i2c.flush_tx_fifo();\n                    }\n                }\n            }\n            Ok(Command::Write(_)) => info!(\"Write command received\"),\n            Ok(Command::WriteRead(_)) => {\n                info!(\"Write-Read command received\");\n                i2c.respond_and_fill(&data_wr, 0xFE).await.unwrap();\n            }\n            Err(e) => info!(\"Got error {}\", e),\n        }\n    }\n}\n"
  },
  {
    "path": "examples/mspm0l1306/src/bin/uart.rs",
    "content": "//! Example of using blocking uart\n//!\n//! This uses the virtual COM port provided on the LP-MSPM0L1306 board.\n\n#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_mspm0::uart::{Config, Uart};\nuse {defmt_rtt as _, panic_halt as _};\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) -> ! {\n    info!(\"Hello world!\");\n\n    let p = embassy_mspm0::init(Default::default());\n\n    let instance = p.UART0;\n    let tx = p.PA8;\n    let rx = p.PA9;\n\n    let config = Config::default();\n    let mut uart = unwrap!(Uart::new_blocking(instance, rx, tx, config));\n\n    unwrap!(uart.blocking_write(b\"Hello Embassy World!\\r\\n\"));\n    info!(\"wrote Hello, starting echo\");\n\n    let mut buf = [0u8; 1];\n\n    loop {\n        unwrap!(uart.blocking_read(&mut buf));\n        unwrap!(uart.blocking_write(&buf));\n    }\n}\n"
  },
  {
    "path": "examples/mspm0l1306/src/bin/wwdt.rs",
    "content": "//! Example of using window watchdog timer in the MSPM0L1306 chip.\n//!\n//! It tests the use case when watchdog timer is expired and when watchdog is pet too early.\n\n#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_mspm0::gpio::{Level, Output};\nuse embassy_mspm0::wwdt::{ClosedWindowPercentage, Config, Timeout, Watchdog};\nuse embassy_time::Timer;\nuse {defmt_rtt as _, panic_halt as _};\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) -> ! {\n    info!(\"Hello world!\");\n\n    let p = embassy_mspm0::init(Default::default());\n    let mut conf = Config::default();\n    conf.timeout = Timeout::Sec1;\n\n    // watchdog also resets the system if the pet comes too early,\n    // less than 250 msec == 25% from 1 sec\n    conf.closed_window = ClosedWindowPercentage::TwentyFive;\n    let mut wdt = Watchdog::new(p.WWDT0, conf);\n    info!(\"Started the watchdog timer\");\n\n    let mut led1 = Output::new(p.PA0, Level::High);\n    led1.set_inversion(true);\n    Timer::after_millis(900).await;\n\n    for _ in 1..=5 {\n        info!(\"pet watchdog\");\n        led1.toggle();\n        wdt.pet();\n        Timer::after_millis(500).await;\n    }\n\n    // watchdog timeout test\n    info!(\"Stopped the pet command, device will reset in less than 1 second\");\n    loop {\n        led1.toggle();\n        Timer::after_millis(500).await;\n    }\n\n    // watchdog \"too early\" test\n    // info!(\"Device will reset when the pet comes too early\");\n    // loop {\n    //     led1.toggle();\n    //     wdt.pet();\n    //     Timer::after_millis(200).await;\n    // }\n}\n"
  },
  {
    "path": "examples/mspm0l2228/.cargo/config.toml",
    "content": "[target.'cfg(all(target_arch = \"arm\", target_os = \"none\"))']\n# replace MSPM0L2228 with your chip as listed in `probe-rs chip list`\nrunner = \"probe-rs run --restore-unwritten --verify --chip MSPM0L2228 --protocol=swd\"\n\n[build]\ntarget = \"thumbv6m-none-eabi\"\n\n[env]\nDEFMT_LOG = \"trace\"\n"
  },
  {
    "path": "examples/mspm0l2228/Cargo.toml",
    "content": "[package]\nedition = \"2024\"\nname = \"embassy-mspm0-l2228-examples\"\nversion = \"0.1.0\"\nlicense = \"MIT OR Apache-2.0\"\npublish = false\n\n[dependencies]\nembassy-mspm0 = { version = \"0.1.0\", path = \"../../embassy-mspm0\", features = [\"mspm0l2228pn\", \"defmt\", \"rt\", \"time-driver-any\"] }\nembassy-executor = { version = \"0.10.0\", path = \"../../embassy-executor\", features = [\"platform-cortex-m\", \"executor-thread\", \"executor-interrupt\"] }\nembassy-sync = { version = \"0.8.0\", path = \"../../embassy-sync\", features = [\"defmt\"] }\nembassy-time = { version = \"0.5.1\", path = \"../../embassy-time\", features = [\"defmt\"] }\npanic-halt = \"1.0.0\"\ncortex-m = { version = \"0.7.6\", features = [\"inline-asm\", \"critical-section-single-core\"] }\ncortex-m-rt = { version = \"0.7.0\"}\ndefmt = \"1.0.1\"\ndefmt-rtt = \"1.0.0\"\npanic-probe = { version = \"1.0.0\", features = [\"print-defmt\"] }\npanic-semihosting = \"0.6.0\"\nrand_core = \"0.9.3\"\n\n[profile.release]\ndebug = 2\n\n[package.metadata.embassy]\nbuild = [\n  { target = \"thumbv6m-none-eabi\", artifact-dir = \"out/examples/mspm0l2228\" }\n]\n"
  },
  {
    "path": "examples/mspm0l2228/README.md",
    "content": "# Examples for MSPM0L2228\n\nRun individual examples with\n```\ncargo run --bin <module-name>\n```\nfor example\n```\ncargo run --bin blinky\n```\n\n## Checklist before running examples\nA large number of the examples are written for the [LP-MSPM0L2228](https://www.ti.com/tool/LP-MSPM0L2228) board.\n\nYou might need to adjust `.cargo/config.toml`, `Cargo.toml` and possibly update pin numbers or peripherals to match the specific MCU or board you are using.\n\n* [ ] Update .cargo/config.toml with the correct probe-rs command to use your specific MCU. For example for L2228 it should be `probe-rs run --chip MSPM0L2228`. (use `probe-rs chip list` to find your chip)\n* [ ] Update Cargo.toml to have the correct `embassy-mspm0` feature. For example for LP-MSPM0L2228 it should be `mspm0l2228pn`. Look in the `Cargo.toml` file of the `embassy-mspm0` project to find the correct feature flag for your chip.\n* [ ] If your board has a special clock or power configuration, make sure that it is set up appropriately.\n* [ ] If your board has different pin mapping, update any pin numbers or peripherals in the given example code to match your schematic\n\nIf you are unsure, please drop by the Embassy Matrix chat for support, and let us know:\n\n* Which example you are trying to run\n* Which chip and board you are using\n\nEmbassy Chat: https://matrix.to/#/#embassy-rs:matrix.org\n"
  },
  {
    "path": "examples/mspm0l2228/build.rs",
    "content": "//! This build script copies the `memory.x` file from the crate root into\n//! a directory where the linker can always find it at build time.\n//! For many projects this is optional, as the linker always searches the\n//! project root directory -- wherever `Cargo.toml` is. However, if you\n//! are using a workspace or have a more complicated build setup, this\n//! build script becomes required. Additionally, by requesting that\n//! Cargo re-run the build script whenever `memory.x` is changed,\n//! updating `memory.x` ensures a rebuild of the application with the\n//! new memory settings.\n\nuse std::env;\nuse std::fs::File;\nuse std::io::Write;\nuse std::path::PathBuf;\n\nfn main() {\n    // Put `memory.x` in our output directory and ensure it's\n    // on the linker search path.\n    let out = &PathBuf::from(env::var_os(\"OUT_DIR\").unwrap());\n    File::create(out.join(\"memory.x\"))\n        .unwrap()\n        .write_all(include_bytes!(\"memory.x\"))\n        .unwrap();\n    println!(\"cargo:rustc-link-search={}\", out.display());\n\n    // By default, Cargo will re-run a build script whenever\n    // any file in the project changes. By specifying `memory.x`\n    // here, we ensure the build script is only re-run when\n    // `memory.x` is changed.\n    println!(\"cargo:rerun-if-changed=memory.x\");\n\n    println!(\"cargo:rustc-link-arg-bins=--nmagic\");\n    println!(\"cargo:rustc-link-arg-bins=-Tlink.x\");\n    println!(\"cargo:rustc-link-arg-bins=-Tdefmt.x\");\n    // You must tell cargo to link interrupt groups if the rt feature is enabled.\n    println!(\"cargo:rustc-link-arg-bins=-Tinterrupt_group.x\");\n}\n"
  },
  {
    "path": "examples/mspm0l2228/memory.x",
    "content": "MEMORY\n{\n  FLASH : ORIGIN = 0x00000000, LENGTH = 256K\n  /* Select non-parity range of SRAM due to SRAM_ERR_01 errata in SLAZ758 */\n  RAM   : ORIGIN = 0x20200000, LENGTH = 32K\n}\n"
  },
  {
    "path": "examples/mspm0l2228/src/bin/blinky.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_mspm0::Config;\nuse embassy_mspm0::gpio::{Level, Output};\nuse embassy_time::Timer;\nuse {defmt_rtt as _, panic_halt as _};\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) -> ! {\n    info!(\"Hello world!\");\n    let p = embassy_mspm0::init(Config::default());\n\n    let mut led1 = Output::new(p.PA0, Level::Low);\n    led1.set_inversion(true);\n\n    loop {\n        Timer::after_millis(400).await;\n\n        info!(\"Toggle\");\n        led1.toggle();\n    }\n}\n"
  },
  {
    "path": "examples/mspm0l2228/src/bin/button.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_mspm0::Config;\nuse embassy_mspm0::gpio::{Input, Level, Output, Pull};\nuse {defmt_rtt as _, panic_halt as _};\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) -> ! {\n    info!(\"Hello world!\");\n\n    let p = embassy_mspm0::init(Config::default());\n\n    let led1 = p.PA0;\n    let s2 = p.PB8;\n\n    let mut led1 = Output::new(led1, Level::Low);\n\n    let mut s2 = Input::new(s2, Pull::Up);\n\n    // led1 is active low\n    led1.set_high();\n\n    loop {\n        s2.wait_for_falling_edge().await;\n\n        info!(\"Switch 2 was pressed\");\n\n        led1.toggle();\n    }\n}\n"
  },
  {
    "path": "examples/mspm0l2228/src/bin/trng.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_mspm0::Config;\nuse embassy_mspm0::gpio::{Level, Output};\nuse embassy_mspm0::trng::{CryptoDecimRate, Trng};\nuse embassy_time::Timer;\nuse rand_core::TryRngCore;\nuse {defmt_rtt as _, panic_halt as _};\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) -> ! {\n    let p = embassy_mspm0::init(Config::default());\n\n    let mut trng = expect!(\n        Trng::new_secure(p.TRNG, CryptoDecimRate::Decim6),\n        \"Failed to initialize RNG\"\n    );\n    // Alternatively, use the default crypto-secure decimation rate (Decim4).\n    // let mut trng = expect!(Trng::new(p.TRNG), \"Failed to initialize RNG\");\n\n    // A buffer to collect random bytes in.\n    let mut randomness = [0u8; 16];\n\n    let mut led1 = Output::new(p.PA0, Level::Low);\n    led1.set_inversion(true);\n\n    loop {\n        unwrap!(trng.try_fill_bytes(&mut randomness[..8]));\n        unwrap!(trng.async_read_bytes(&mut randomness[8..]).await);\n        info!(\"Random bytes {}\", &randomness);\n        let random_u32 = unwrap!(trng.try_next_u32());\n        let random_u64 = unwrap!(trng.try_next_u64());\n        info!(\"Random u32 {} u64 {}\", random_u32, random_u64);\n        // Random number of blinks between 0 and 31\n        let blinks = random_u32 % 32;\n        for _ in 0..blinks * 2 {\n            led1.toggle();\n            Timer::after_millis(20).await;\n        }\n        Timer::after_millis(1000).await;\n    }\n}\n"
  },
  {
    "path": "examples/mspm0l2228/src/bin/uart.rs",
    "content": "//! Example of using blocking uart\n//!\n//! This uses the virtual COM port provided on the LP-MSPM0L2228 board.\n\n#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_mspm0::uart::{Config, Uart};\nuse {defmt_rtt as _, panic_halt as _};\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) -> ! {\n    info!(\"Hello world!\");\n\n    let p = embassy_mspm0::init(Default::default());\n\n    let instance = p.UART0;\n    let tx = p.PA10;\n    let rx = p.PA11;\n\n    let config = Config::default();\n    let mut uart = unwrap!(Uart::new_blocking(instance, rx, tx, config));\n\n    unwrap!(uart.blocking_write(b\"Hello Embassy World!\\r\\n\"));\n    info!(\"wrote Hello, starting echo\");\n\n    let mut buf = [0u8; 1];\n\n    loop {\n        unwrap!(uart.blocking_read(&mut buf));\n        unwrap!(uart.blocking_write(&buf));\n    }\n}\n"
  },
  {
    "path": "examples/mspm0l2228/src/bin/wwdt.rs",
    "content": "//! Example of using window watchdog timer in the MSPM0L2228 chip.\n//!\n//! It tests the use case when watchdog timer is expired and when watchdog is pet too early.\n\n#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_mspm0::gpio::{Level, Output};\nuse embassy_mspm0::wwdt::{ClosedWindowPercentage, Config, Timeout, Watchdog};\nuse embassy_time::Timer;\nuse {defmt_rtt as _, panic_halt as _};\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) -> ! {\n    info!(\"Hello world!\");\n\n    let p = embassy_mspm0::init(Default::default());\n    let mut conf = Config::default();\n    conf.timeout = Timeout::Sec1;\n\n    // watchdog also resets the system if the pet comes too early,\n    // less than 250 msec == 25% from 1 sec\n    conf.closed_window = ClosedWindowPercentage::TwentyFive;\n    let mut wdt = Watchdog::new(p.WWDT0, conf);\n    info!(\"Started the watchdog timer\");\n\n    let mut led1 = Output::new(p.PA0, Level::High);\n    led1.set_inversion(true);\n    Timer::after_millis(900).await;\n\n    for _ in 1..=5 {\n        info!(\"pet watchdog\");\n        led1.toggle();\n        wdt.pet();\n        Timer::after_millis(500).await;\n    }\n\n    // watchdog timeout test\n    info!(\"Stopped the pet command, device will reset in less than 1 second\");\n    loop {\n        led1.toggle();\n        Timer::after_millis(500).await;\n    }\n\n    // watchdog \"too early\" test\n    // info!(\"Device will reset when the pet comes too early\");\n    // loop {\n    //     led1.toggle();\n    //     wdt.pet();\n    //     Timer::after_millis(200).await;\n    // }\n}\n"
  },
  {
    "path": "examples/nrf-rtos-trace/.cargo/config.toml",
    "content": "[target.'cfg(all(target_arch = \"arm\", target_os = \"none\"))']\n# replace nRF82840_xxAA with your chip as listed in `probe-rs chip list`\nrunner = \"probe-rs run --chip nRF52840_xxAA\"\n\n[build]\ntarget = \"thumbv7em-none-eabi\"\n\n[env]\nDEFMT_LOG = \"trace\"\n"
  },
  {
    "path": "examples/nrf-rtos-trace/Cargo.toml",
    "content": "[package]\nedition = \"2024\"\nname = \"embassy-nrf-rtos-trace-examples\"\nversion = \"0.1.0\"\nlicense = \"MIT OR Apache-2.0\"\npublish = false\n\n[features]\ndefault = [\"log\"]\nlog = [\n    \"dep:log\",\n    \"embassy-sync/log\",\n    \"embassy-executor/log\",\n    \"embassy-time/log\",\n    \"embassy-nrf/log\",\n]\n\n[dependencies]\nembassy-sync = { version = \"0.8.0\", path = \"../../embassy-sync\" }\nembassy-executor = { version = \"0.10.0\", path = \"../../embassy-executor\", features = [\"platform-cortex-m\", \"executor-thread\", \"rtos-trace\"] }\nembassy-time = { version = \"0.5.1\", path = \"../../embassy-time\" }\nembassy-nrf = { version = \"0.10.0\", path = \"../../embassy-nrf\", features = [\"nrf52840\", \"time-driver-rtc1\", \"gpiote\", \"unstable-pac\"] }\n\ncortex-m = { version = \"0.7.6\", features = [\"inline-asm\", \"critical-section-single-core\"] }\ncortex-m-rt = \"0.7.0\"\npanic-probe = \"1.0.0\"\nserde = { version = \"1.0.136\", default-features = false }\nrtos-trace = \"0.2\"\nsystemview-target = { version = \"0.2\", features = [\"callbacks-app\", \"callbacks-os\", \"log\", \"cortex-m\"] }\nlog = { version = \"0.4.17\", optional = true }\n\n[[bin]]\nname = \"rtos_trace\"\n\n\n[profile.release]\ndebug = 2\n\n[package.metadata.embassy]\nbuild = [\n  { target = \"thumbv7em-none-eabi\", artifact-dir = \"out/examples/nrf-rtos-trace\" }\n]\n"
  },
  {
    "path": "examples/nrf-rtos-trace/build.rs",
    "content": "//! This build script copies the `memory.x` file from the crate root into\n//! a directory where the linker can always find it at build time.\n//! For many projects this is optional, as the linker always searches the\n//! project root directory -- wherever `Cargo.toml` is. However, if you\n//! are using a workspace or have a more complicated build setup, this\n//! build script becomes required. Additionally, by requesting that\n//! Cargo re-run the build script whenever `memory.x` is changed,\n//! updating `memory.x` ensures a rebuild of the application with the\n//! new memory settings.\n\nuse std::env;\nuse std::fs::File;\nuse std::io::Write;\nuse std::path::PathBuf;\n\nfn main() {\n    // Put `memory.x` in our output directory and ensure it's\n    // on the linker search path.\n    let out = &PathBuf::from(env::var_os(\"OUT_DIR\").unwrap());\n    File::create(out.join(\"memory.x\"))\n        .unwrap()\n        .write_all(include_bytes!(\"memory.x\"))\n        .unwrap();\n    println!(\"cargo:rustc-link-search={}\", out.display());\n\n    // By default, Cargo will re-run a build script whenever\n    // any file in the project changes. By specifying `memory.x`\n    // here, we ensure the build script is only re-run when\n    // `memory.x` is changed.\n    println!(\"cargo:rerun-if-changed=memory.x\");\n\n    println!(\"cargo:rustc-link-arg-bins=--nmagic\");\n    println!(\"cargo:rustc-link-arg-bins=-Tlink.x\");\n}\n"
  },
  {
    "path": "examples/nrf-rtos-trace/memory.x",
    "content": "MEMORY\n{\n  /* NOTE 1 K = 1 KiBi = 1024 bytes */\n  /* These values correspond to the NRF52840 with Softdevices S140 7.0.1 */\n  FLASH : ORIGIN = 0x00000000, LENGTH = 1024K\n  RAM : ORIGIN = 0x20000000, LENGTH = 256K\n}\n"
  },
  {
    "path": "examples/nrf-rtos-trace/src/bin/rtos_trace.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse core::future::poll_fn;\nuse core::task::Poll;\n\nuse embassy_executor::Spawner;\nuse embassy_time::{Instant, Timer};\n#[cfg(feature = \"log\")]\nuse log::*;\nuse panic_probe as _;\n// N.B. systemview_target cannot be used at the same time as defmt_rtt.\nuse rtos_trace;\nuse systemview_target::SystemView;\n\nstatic LOGGER: systemview_target::SystemView = systemview_target::SystemView::new();\nrtos_trace::global_trace! {SystemView}\n\nstruct TraceInfo();\n\nimpl rtos_trace::RtosTraceApplicationCallbacks for TraceInfo {\n    fn system_description() {}\n    fn sysclock() -> u32 {\n        64000000\n    }\n}\nrtos_trace::global_application_callbacks! {TraceInfo}\n\n#[embassy_executor::task]\nasync fn run1() {\n    loop {\n        #[cfg(feature = \"log\")]\n        info!(\"DING DONG\");\n        #[cfg(not(feature = \"log\"))]\n        rtos_trace::trace::marker(13);\n        Timer::after_ticks(16000).await;\n    }\n}\n\n#[embassy_executor::task]\nasync fn run2() {\n    loop {\n        Timer::at(Instant::from_ticks(0)).await;\n    }\n}\n\n#[embassy_executor::task]\nasync fn run3() {\n    poll_fn(|cx| {\n        cx.waker().wake_by_ref();\n        Poll::<()>::Pending\n    })\n    .await;\n}\n\n#[embassy_executor::main]\nasync fn main(spawner: Spawner) {\n    let _p = embassy_nrf::init(Default::default());\n    LOGGER.init();\n    #[cfg(feature = \"log\")]\n    {\n        ::log::set_logger(&LOGGER).ok();\n        ::log::set_max_level(::log::LevelFilter::Trace);\n    }\n\n    spawner.spawn(run1().unwrap());\n    spawner.spawn(run2().unwrap());\n    spawner.spawn(run3().unwrap());\n}\n"
  },
  {
    "path": "examples/nrf51/.cargo/config.toml",
    "content": "[target.'cfg(all(target_arch = \"arm\", target_os = \"none\"))']\n# replace nRF51422_xxAA with your chip as listed in `probe-rs chip list`\nrunner = \"probe-rs run --chip nRF51422_xxAA\"\n\n[build]\ntarget = \"thumbv6m-none-eabi\"\n\n[env]\nDEFMT_LOG = \"trace\"\n"
  },
  {
    "path": "examples/nrf51/Cargo.toml",
    "content": "[package]\nedition = \"2024\"\nname = \"embassy-nrf51-examples\"\nversion = \"0.1.0\"\nlicense = \"MIT OR Apache-2.0\"\npublish = false\n\n[dependencies]\nembassy-executor = { version = \"0.10.0\", path = \"../../embassy-executor\", features = [\"platform-cortex-m\", \"executor-thread\", \"executor-interrupt\", \"defmt\"] }\nembassy-time = { version = \"0.5.1\", path = \"../../embassy-time\", features = [\"defmt\", \"defmt-timestamp-uptime\"] }\nembassy-nrf = { version = \"0.10.0\", path = \"../../embassy-nrf\", features = [\"defmt\", \"nrf51\", \"gpiote\", \"time-driver-rtc1\", \"unstable-pac\", \"time\", \"rt\"] }\n\ndefmt = \"1.0.1\"\ndefmt-rtt = \"1.0.0\"\n\ncortex-m = { version = \"0.7.6\", features = [\"inline-asm\", \"critical-section-single-core\"] }\ncortex-m-rt = \"0.7\"\npanic-probe = { version = \"1.0.0\", features = [\"print-defmt\"] }\n\n[profile.release]\ndebug = 2\n\n[package.metadata.embassy]\nbuild = [\n  { target = \"thumbv6m-none-eabi\", artifact-dir = \"out/examples/nrf51\" }\n]\n"
  },
  {
    "path": "examples/nrf51/build.rs",
    "content": "//! This build script copies the `memory.x` file from the crate root into\n//! a directory where the linker can always find it at build time.\n//! For many projects this is optional, as the linker always searches the\n//! project root directory -- wherever `Cargo.toml` is. However, if you\n//! are using a workspace or have a more complicated build setup, this\n//! build script becomes required. Additionally, by requesting that\n//! Cargo re-run the build script whenever `memory.x` is changed,\n//! updating `memory.x` ensures a rebuild of the application with the\n//! new memory settings.\n\nuse std::env;\nuse std::fs::File;\nuse std::io::Write;\nuse std::path::PathBuf;\n\nfn main() {\n    // Put `memory.x` in our output directory and ensure it's\n    // on the linker search path.\n    let out = &PathBuf::from(env::var_os(\"OUT_DIR\").unwrap());\n    File::create(out.join(\"memory.x\"))\n        .unwrap()\n        .write_all(include_bytes!(\"memory.x\"))\n        .unwrap();\n    println!(\"cargo:rustc-link-search={}\", out.display());\n\n    // By default, Cargo will re-run a build script whenever\n    // any file in the project changes. By specifying `memory.x`\n    // here, we ensure the build script is only re-run when\n    // `memory.x` is changed.\n    println!(\"cargo:rerun-if-changed=memory.x\");\n\n    println!(\"cargo:rustc-link-arg-bins=--nmagic\");\n    println!(\"cargo:rustc-link-arg-bins=-Tlink.x\");\n    println!(\"cargo:rustc-link-arg-bins=-Tdefmt.x\");\n}\n"
  },
  {
    "path": "examples/nrf51/memory.x",
    "content": "MEMORY\n{\n  FLASH                    : ORIGIN = 0x00000000, LENGTH = 128K\n  RAM                      : ORIGIN = 0x20000000, LENGTH = 16K\n}\n"
  },
  {
    "path": "examples/nrf51/src/bin/blinky.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse embassy_executor::Spawner;\nuse embassy_nrf::gpio::{Level, Output, OutputDrive};\nuse embassy_time::Timer;\nuse {defmt_rtt as _, panic_probe as _};\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let p = embassy_nrf::init(Default::default());\n    let mut led = Output::new(p.P0_21, Level::Low, OutputDrive::Standard);\n\n    loop {\n        led.set_high();\n        Timer::after_millis(300).await;\n        led.set_low();\n        Timer::after_millis(300).await;\n    }\n}\n"
  },
  {
    "path": "examples/nrf52810/.cargo/config.toml",
    "content": "[target.'cfg(all(target_arch = \"arm\", target_os = \"none\"))']\n# replace nRF82810_xxAA with your chip as listed in `probe-rs chip list`\nrunner = \"probe-rs run --chip nRF52810_xxAA\"\n\n[build]\ntarget = \"thumbv7em-none-eabi\"\n\n[env]\nDEFMT_LOG = \"trace\"\n"
  },
  {
    "path": "examples/nrf52810/Cargo.toml",
    "content": "[package]\nedition = \"2024\"\nname = \"embassy-nrf52810-examples\"\nversion = \"0.1.0\"\nlicense = \"MIT OR Apache-2.0\"\npublish = false\n\n[dependencies]\nembassy-futures = { version = \"0.1.2\", path = \"../../embassy-futures\" }\nembassy-sync = { version = \"0.8.0\", path = \"../../embassy-sync\", features = [\"defmt\"] }\nembassy-executor = { version = \"0.10.0\", path = \"../../embassy-executor\", features = [\"platform-cortex-m\", \"executor-thread\", \"executor-interrupt\", \"defmt\"] }\nembassy-time = { version = \"0.5.1\", path = \"../../embassy-time\", features = [\"defmt\", \"defmt-timestamp-uptime\"] }\nembassy-nrf = { version = \"0.10.0\", path = \"../../embassy-nrf\", features = [\"defmt\", \"nrf52810\", \"time-driver-rtc1\", \"gpiote\", \"unstable-pac\", \"time\"] }\n\ndefmt = \"1.0.1\"\ndefmt-rtt = \"1.0.0\"\n\nfixed = \"1.10.0\"\nstatic_cell = { version = \"2\" }\ncortex-m = { version = \"0.7.6\", features = [\"inline-asm\", \"critical-section-single-core\"] }\ncortex-m-rt = \"0.7.0\"\npanic-probe = { version = \"1.0.0\", features = [\"print-defmt\"] }\n\n[profile.release]\ndebug = 2\n\n[package.metadata.embassy]\nbuild = [\n  { target = \"thumbv7em-none-eabi\", artifact-dir = \"out/examples/nrf52810\" }\n]\n"
  },
  {
    "path": "examples/nrf52810/build.rs",
    "content": "//! This build script copies the `memory.x` file from the crate root into\n//! a directory where the linker can always find it at build time.\n//! For many projects this is optional, as the linker always searches the\n//! project root directory -- wherever `Cargo.toml` is. However, if you\n//! are using a workspace or have a more complicated build setup, this\n//! build script becomes required. Additionally, by requesting that\n//! Cargo re-run the build script whenever `memory.x` is changed,\n//! updating `memory.x` ensures a rebuild of the application with the\n//! new memory settings.\n\nuse std::env;\nuse std::fs::File;\nuse std::io::Write;\nuse std::path::PathBuf;\n\nfn main() {\n    // Put `memory.x` in our output directory and ensure it's\n    // on the linker search path.\n    let out = &PathBuf::from(env::var_os(\"OUT_DIR\").unwrap());\n    File::create(out.join(\"memory.x\"))\n        .unwrap()\n        .write_all(include_bytes!(\"memory.x\"))\n        .unwrap();\n    println!(\"cargo:rustc-link-search={}\", out.display());\n\n    // By default, Cargo will re-run a build script whenever\n    // any file in the project changes. By specifying `memory.x`\n    // here, we ensure the build script is only re-run when\n    // `memory.x` is changed.\n    println!(\"cargo:rerun-if-changed=memory.x\");\n\n    println!(\"cargo:rustc-link-arg-bins=--nmagic\");\n    println!(\"cargo:rustc-link-arg-bins=-Tlink.x\");\n    println!(\"cargo:rustc-link-arg-bins=-Tdefmt.x\");\n}\n"
  },
  {
    "path": "examples/nrf52810/memory.x",
    "content": "MEMORY\n{\n  /* NOTE 1 K = 1 KiBi = 1024 bytes */\n  FLASH : ORIGIN = 0x00000000, LENGTH = 256K\n  RAM : ORIGIN = 0x20000000, LENGTH = 24K\n\n}\n"
  },
  {
    "path": "examples/nrf52810/src/bin/blinky.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse embassy_executor::Spawner;\nuse embassy_nrf::gpio::{Level, Output, OutputDrive};\nuse embassy_time::Timer;\nuse {defmt_rtt as _, panic_probe as _};\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let p = embassy_nrf::init(Default::default());\n    let mut led = Output::new(p.P0_18, Level::Low, OutputDrive::Standard);\n\n    loop {\n        led.set_high();\n        Timer::after_millis(300).await;\n        led.set_low();\n        Timer::after_millis(300).await;\n    }\n}\n"
  },
  {
    "path": "examples/nrf52810/src/bin/saadc_lowpower.rs",
    "content": "//! Run SAADC on multiple pins only every 3rd time, to show anomaly 241 workaround.\n//!\n//! To correctly measure the MCU current on the NRF52DK follow the instructions\n//! <https://docs.nordicsemi.com/bundle/ug_nrf52832_dk/page/UG/dk/prepare_board.html>\n//! otherwise you will measure the whole board, including the segger j-link chip for example\n\n#![no_std]\n#![no_main]\n\nuse defmt::info;\nuse embassy_executor::Spawner;\nuse embassy_nrf::gpio::{Level, Output, OutputDrive};\nuse embassy_nrf::saadc::{Oversample, Saadc};\nuse embassy_nrf::{bind_interrupts, saadc};\nuse embassy_time::Timer;\nuse {defmt_rtt as _, panic_probe as _};\n\nbind_interrupts!(struct Irqs {\n        SAADC => saadc::InterruptHandler;\n});\n\n#[embassy_executor::main]\nasync fn main(_p: Spawner) {\n    let mut p = embassy_nrf::init(Default::default());\n\n    // For PPK2 digital channel plot to track when SAADC is on/off.\n    let mut ppk2_d0 = Output::new(p.P0_27, Level::Low, OutputDrive::Standard);\n    let mut num_loops: usize = 0;\n    loop {\n        num_loops += 1;\n        if num_loops.is_multiple_of(3) {\n            ppk2_d0.set_high();\n            let battery_pin = p.P0_02.reborrow();\n            let sensor1_pin = p.P0_03.reborrow();\n            let mut adc_config = saadc::Config::default();\n            adc_config.oversample = Oversample::OVER4X;\n            let battery = saadc::ChannelConfig::single_ended(battery_pin);\n            let sensor1 = saadc::ChannelConfig::single_ended(sensor1_pin);\n            let mut saadc = Saadc::new(p.SAADC.reborrow(), Irqs, adc_config, [battery, sensor1]);\n            // Indicated: wait for ADC calibration.\n            saadc.calibrate().await;\n            let mut buf = [0; 2];\n            info!(\"sampling...\");\n            saadc.sample(&mut buf).await;\n            info!(\"data: {:x}\", buf);\n\n            // Sleep to show the high power usage on the plot, even though sampling is done.\n            Timer::after_millis(100).await;\n            ppk2_d0.set_low();\n            // disable the following line to show the anomaly on the power profiler plot.\n            core::mem::drop(saadc);\n            // Sleep to show the power usage when drop did not happen.\n            Timer::after_millis(100).await;\n            // worst case drop happens here\n        } else {\n            info!(\"waiting\");\n        }\n        // Sleep for 1 second. The executor ensures the core sleeps with a WFE when it has nothing to do.\n        // During this sleep, the nRF chip should only use ~3uA\n        Timer::after_secs(1).await;\n    }\n}\n"
  },
  {
    "path": "examples/nrf52840/.cargo/config.toml",
    "content": "[target.'cfg(all(target_arch = \"arm\", target_os = \"none\"))']\n# replace nRF82840_xxAA with your chip as listed in `probe-rs chip list`\nrunner = \"probe-rs run --chip nRF52840_xxAA\"\n\n[build]\ntarget = \"thumbv7em-none-eabi\"\n\n[env]\nDEFMT_LOG = \"trace\"\n"
  },
  {
    "path": "examples/nrf52840/Cargo.toml",
    "content": "[package]\nedition = \"2024\"\nname = \"embassy-nrf52840-examples\"\nversion = \"0.1.0\"\nlicense = \"MIT OR Apache-2.0\"\npublish = false\n\n[dependencies]\nembassy-futures = { version = \"0.1.2\", path = \"../../embassy-futures\" }\nembassy-sync = { version = \"0.8.0\", path = \"../../embassy-sync\", features = [\"defmt\"] }\nembassy-executor = { version = \"0.10.0\", path = \"../../embassy-executor\", features = [\"platform-cortex-m\", \"executor-thread\", \"executor-interrupt\", \"defmt\"] }\nembassy-time = { version = \"0.5.1\", path = \"../../embassy-time\", features = [\"defmt\", \"defmt-timestamp-uptime\"] }\nembassy-nrf = { version = \"0.10.0\", path = \"../../embassy-nrf\", features = [\"defmt\", \"nrf52840\", \"time-driver-rtc1\", \"gpiote\", \"unstable-pac\", \"time\", \"net-driver\"] }\nembassy-net = { version = \"0.9.0\", path = \"../../embassy-net\", features = [\"defmt\", \"tcp\", \"dhcpv4\", \"medium-ethernet\",\"udp\", \"medium-ieee802154\", \"proto-ipv6\"] }\nembassy-usb = { version = \"0.6.0\", path = \"../../embassy-usb\", features = [\"defmt\"] }\nembedded-io = { version = \"0.7.1\", features = [\"defmt\"]  }\nembedded-io-async = { version = \"0.7.0\", features = [\"defmt\"] }\nembassy-net-esp-hosted = { version = \"0.3.0\", path = \"../../embassy-net-esp-hosted\", features = [\"defmt\"] }\nembassy-net-enc28j60 = { version = \"0.3.0\", path = \"../../embassy-net-enc28j60\", features = [\"defmt\"] }\n\ndefmt = \"1.0.1\"\ndefmt-rtt = \"1.0.0\"\n\nfixed = \"1.10.0\"\nstatic_cell = { version = \"2\" }\ncortex-m = { version = \"0.7.6\", features = [\"inline-asm\", \"critical-section-single-core\"] }\ncortex-m-rt = \"0.7.0\"\npanic-probe = { version = \"1.0.0\", features = [\"print-defmt\"] }\nrand = { version = \"0.9.0\", default-features = false }\nembedded-storage = \"0.3.1\"\nusbd-hid = \"0.10.0\"\nserde = { version = \"1.0.136\", default-features = false }\nembedded-hal = { version = \"1.0\" }\nembedded-hal-async = { version = \"1.0\" }\nembedded-hal-bus = { version = \"0.1\", features = [\"async\"] }\nnum-integer = { version = \"0.1.45\", default-features = false }\nmicrofft = \"0.5.0\"\nportable-atomic = \"1\"\n\n[profile.release]\ndebug = 2\n\n[package.metadata.embassy]\nbuild = [\n  { target = \"thumbv7em-none-eabi\", artifact-dir = \"out/examples/nrf52840\" }\n]\n"
  },
  {
    "path": "examples/nrf52840/build.rs",
    "content": "//! This build script copies the `memory.x` file from the crate root into\n//! a directory where the linker can always find it at build time.\n//! For many projects this is optional, as the linker always searches the\n//! project root directory -- wherever `Cargo.toml` is. However, if you\n//! are using a workspace or have a more complicated build setup, this\n//! build script becomes required. Additionally, by requesting that\n//! Cargo re-run the build script whenever `memory.x` is changed,\n//! updating `memory.x` ensures a rebuild of the application with the\n//! new memory settings.\n\nuse std::env;\nuse std::fs::File;\nuse std::io::Write;\nuse std::path::PathBuf;\n\nfn main() {\n    // Put `memory.x` in our output directory and ensure it's\n    // on the linker search path.\n    let out = &PathBuf::from(env::var_os(\"OUT_DIR\").unwrap());\n    File::create(out.join(\"memory.x\"))\n        .unwrap()\n        .write_all(include_bytes!(\"memory.x\"))\n        .unwrap();\n    println!(\"cargo:rustc-link-search={}\", out.display());\n\n    // By default, Cargo will re-run a build script whenever\n    // any file in the project changes. By specifying `memory.x`\n    // here, we ensure the build script is only re-run when\n    // `memory.x` is changed.\n    println!(\"cargo:rerun-if-changed=memory.x\");\n\n    println!(\"cargo:rustc-link-arg-bins=--nmagic\");\n    println!(\"cargo:rustc-link-arg-bins=-Tlink.x\");\n    println!(\"cargo:rustc-link-arg-bins=-Tdefmt.x\");\n}\n"
  },
  {
    "path": "examples/nrf52840/memory.x",
    "content": "MEMORY\n{\n  /* NOTE 1 K = 1 KiBi = 1024 bytes */\n  FLASH : ORIGIN = 0x00000000, LENGTH = 1024K\n  RAM : ORIGIN = 0x20000000, LENGTH = 256K\n\n  /* These values correspond to the NRF52840 with Softdevices S140 7.3.0 */\n  /*\n     FLASH : ORIGIN = 0x00027000, LENGTH = 868K\n     RAM : ORIGIN = 0x20020000, LENGTH = 128K\n  */\n}\n"
  },
  {
    "path": "examples/nrf52840/src/bin/blinky.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse embassy_executor::Spawner;\nuse embassy_nrf::gpio::{Level, Output, OutputDrive};\nuse embassy_time::Timer;\nuse {defmt_rtt as _, panic_probe as _};\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let p = embassy_nrf::init(Default::default());\n    let mut led = Output::new(p.P0_13, Level::Low, OutputDrive::Standard);\n\n    loop {\n        led.set_high();\n        Timer::after_millis(300).await;\n        led.set_low();\n        Timer::after_millis(300).await;\n    }\n}\n"
  },
  {
    "path": "examples/nrf52840/src/bin/buffered_uart.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_nrf::buffered_uarte::{self, BufferedUarte};\nuse embassy_nrf::{bind_interrupts, peripherals, uarte};\nuse embedded_io_async::Write;\nuse {defmt_rtt as _, panic_probe as _};\n\nbind_interrupts!(struct Irqs {\n    UARTE0 => buffered_uarte::InterruptHandler<peripherals::UARTE0>;\n});\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let p = embassy_nrf::init(Default::default());\n    let mut config = uarte::Config::default();\n    config.parity = uarte::Parity::EXCLUDED;\n    config.baudrate = uarte::Baudrate::BAUD115200;\n\n    let mut tx_buffer = [0u8; 4096];\n    let mut rx_buffer = [0u8; 4096];\n\n    let mut u = BufferedUarte::new(\n        p.UARTE0,\n        p.TIMER0,\n        p.PPI_CH0,\n        p.PPI_CH1,\n        p.PPI_GROUP0,\n        p.P0_08,\n        p.P0_06,\n        Irqs,\n        config,\n        &mut rx_buffer,\n        &mut tx_buffer,\n    );\n\n    info!(\"uarte initialized!\");\n\n    unwrap!(u.write_all(b\"Hello!\\r\\n\").await);\n    info!(\"wrote hello in uart!\");\n\n    loop {\n        info!(\"reading...\");\n        let buf = unwrap!(u.fill_buf().await);\n        info!(\"read done, got {}\", buf);\n\n        // Read bytes have to be explicitly consumed, otherwise fill_buf() will return them again\n        let n = buf.len();\n        u.consume(n);\n    }\n}\n"
  },
  {
    "path": "examples/nrf52840/src/bin/channel.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse defmt::unwrap;\nuse embassy_executor::Spawner;\nuse embassy_nrf::gpio::{Level, Output, OutputDrive};\nuse embassy_sync::blocking_mutex::raw::ThreadModeRawMutex;\nuse embassy_sync::channel::Channel;\nuse embassy_time::Timer;\nuse {defmt_rtt as _, panic_probe as _};\n\nenum LedState {\n    On,\n    Off,\n}\n\nstatic CHANNEL: Channel<ThreadModeRawMutex, LedState, 1> = Channel::new();\n\n#[embassy_executor::task]\nasync fn my_task() {\n    loop {\n        CHANNEL.send(LedState::On).await;\n        Timer::after_secs(1).await;\n        CHANNEL.send(LedState::Off).await;\n        Timer::after_secs(1).await;\n    }\n}\n\n#[embassy_executor::main]\nasync fn main(spawner: Spawner) {\n    let p = embassy_nrf::init(Default::default());\n    let mut led = Output::new(p.P0_13, Level::Low, OutputDrive::Standard);\n\n    spawner.spawn(unwrap!(my_task()));\n\n    loop {\n        match CHANNEL.receive().await {\n            LedState::On => led.set_low(),\n            LedState::Off => led.set_high(),\n        }\n    }\n}\n"
  },
  {
    "path": "examples/nrf52840/src/bin/channel_sender_receiver.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse defmt::unwrap;\nuse embassy_executor::Spawner;\nuse embassy_nrf::Peri;\nuse embassy_nrf::gpio::{AnyPin, Level, Output, OutputDrive};\nuse embassy_sync::blocking_mutex::raw::NoopRawMutex;\nuse embassy_sync::channel::{Channel, Receiver, Sender};\nuse embassy_time::Timer;\nuse static_cell::StaticCell;\nuse {defmt_rtt as _, panic_probe as _};\n\nenum LedState {\n    On,\n    Off,\n}\n\nstatic CHANNEL: StaticCell<Channel<NoopRawMutex, LedState, 1>> = StaticCell::new();\n\n#[embassy_executor::task]\nasync fn send_task(sender: Sender<'static, NoopRawMutex, LedState, 1>) {\n    loop {\n        sender.send(LedState::On).await;\n        Timer::after_secs(1).await;\n        sender.send(LedState::Off).await;\n        Timer::after_secs(1).await;\n    }\n}\n\n#[embassy_executor::task]\nasync fn recv_task(led: Peri<'static, AnyPin>, receiver: Receiver<'static, NoopRawMutex, LedState, 1>) {\n    let mut led = Output::new(led, Level::Low, OutputDrive::Standard);\n\n    loop {\n        match receiver.receive().await {\n            LedState::On => led.set_low(),\n            LedState::Off => led.set_high(),\n        }\n    }\n}\n\n#[embassy_executor::main]\nasync fn main(spawner: Spawner) {\n    let p = embassy_nrf::init(Default::default());\n    let channel = CHANNEL.init(Channel::new());\n\n    spawner.spawn(unwrap!(send_task(channel.sender())));\n    spawner.spawn(unwrap!(recv_task(p.P0_13.into(), channel.receiver())));\n}\n"
  },
  {
    "path": "examples/nrf52840/src/bin/egu.rs",
    "content": "//! This example shows the use of the EGU peripheral combined with PPI.\n//!\n//! It chains events from button -> egu0-trigger0 -> egu0-trigger1 -> led\n#![no_std]\n#![no_main]\n\nuse embassy_executor::Spawner;\nuse embassy_nrf::egu::{Egu, TriggerNumber};\nuse embassy_nrf::gpio::{Level, OutputDrive, Pull};\nuse embassy_nrf::gpiote::{InputChannel, InputChannelPolarity, OutputChannel, OutputChannelPolarity};\nuse embassy_nrf::peripherals::{PPI_CH0, PPI_CH1, PPI_CH2};\nuse embassy_nrf::ppi::Ppi;\nuse embassy_time::{Duration, Timer};\nuse {defmt_rtt as _, panic_probe as _};\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let p = embassy_nrf::init(Default::default());\n\n    let mut egu1 = Egu::new(p.EGU0);\n    let led1 = OutputChannel::new(\n        p.GPIOTE_CH0,\n        p.P0_13,\n        Level::High,\n        OutputDrive::Standard,\n        OutputChannelPolarity::Toggle,\n    );\n    let btn1 = InputChannel::new(p.GPIOTE_CH1, p.P0_11, Pull::Up, InputChannelPolarity::LoToHi);\n\n    let trigger0 = egu1.trigger(TriggerNumber::Trigger0);\n    let trigger1 = egu1.trigger(TriggerNumber::Trigger1);\n\n    let mut ppi1: Ppi<PPI_CH0, 1, 1> = Ppi::new_one_to_one(p.PPI_CH0, btn1.event_in(), trigger0.task());\n    ppi1.enable();\n\n    let mut ppi2: Ppi<PPI_CH1, 1, 1> = Ppi::new_one_to_one(p.PPI_CH1, trigger0.event(), trigger1.task());\n    ppi2.enable();\n\n    let mut ppi3: Ppi<PPI_CH2, 1, 1> = Ppi::new_one_to_one(p.PPI_CH2, trigger1.event(), led1.task_out());\n    ppi3.enable();\n\n    defmt::info!(\"Push the button to toggle the LED\");\n    loop {\n        Timer::after(Duration::from_secs(60)).await;\n    }\n}\n"
  },
  {
    "path": "examples/nrf52840/src/bin/ethernet_enc28j60.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_net::StackResources;\nuse embassy_net::tcp::TcpSocket;\nuse embassy_net_enc28j60::Enc28j60;\nuse embassy_nrf::gpio::{Level, Output, OutputDrive};\nuse embassy_nrf::rng::Rng;\nuse embassy_nrf::spim::Spim;\nuse embassy_nrf::{bind_interrupts, peripherals, spim};\nuse embassy_time::Delay;\nuse embedded_hal_bus::spi::ExclusiveDevice;\nuse embedded_io_async::Write;\nuse static_cell::StaticCell;\nuse {defmt_rtt as _, panic_probe as _};\n\nbind_interrupts!(struct Irqs {\n    SPIM3 => spim::InterruptHandler<peripherals::SPI3>;\n    RNG => embassy_nrf::rng::InterruptHandler<peripherals::RNG>;\n});\n\n#[embassy_executor::task]\nasync fn net_task(\n    mut runner: embassy_net::Runner<\n        'static,\n        Enc28j60<ExclusiveDevice<Spim<'static>, Output<'static>, Delay>, Output<'static>>,\n    >,\n) -> ! {\n    runner.run().await\n}\n\n#[embassy_executor::main]\nasync fn main(spawner: Spawner) {\n    let p = embassy_nrf::init(Default::default());\n    info!(\"running!\");\n\n    let eth_sck = p.P0_20;\n    let eth_mosi = p.P0_22;\n    let eth_miso = p.P0_24;\n    let eth_cs = p.P0_15;\n    let eth_rst = p.P0_13;\n    let _eth_irq = p.P0_12;\n\n    let mut config = spim::Config::default();\n    config.frequency = spim::Frequency::M16;\n    let spi = spim::Spim::new(p.SPI3, Irqs, eth_sck, eth_miso, eth_mosi, config);\n    let cs = Output::new(eth_cs, Level::High, OutputDrive::Standard);\n    let spi = ExclusiveDevice::new(spi, cs, Delay);\n\n    let rst = Output::new(eth_rst, Level::High, OutputDrive::Standard);\n    let mac_addr = [2, 3, 4, 5, 6, 7];\n    let device = Enc28j60::new(spi, Some(rst), mac_addr);\n\n    let config = embassy_net::Config::dhcpv4(Default::default());\n    // let config = embassy_net::Config::ipv4_static(embassy_net::StaticConfigV4 {\n    //    address: Ipv4Cidr::new(Ipv4Address::new(10, 42, 0, 61), 24),\n    //    dns_servers: Vec::new(),\n    //    gateway: Some(Ipv4Address::new(10, 42, 0, 1)),\n    // });\n\n    // Generate random seed\n    let mut rng = Rng::new(p.RNG, Irqs);\n    let mut seed = [0; 8];\n    rng.blocking_fill_bytes(&mut seed);\n    let seed = u64::from_le_bytes(seed);\n\n    // Init network stack\n    static RESOURCES: StaticCell<StackResources<3>> = StaticCell::new();\n    let (stack, runner) = embassy_net::new(device, config, RESOURCES.init(StackResources::new()), seed);\n\n    spawner.spawn(unwrap!(net_task(runner)));\n\n    // And now we can use it!\n\n    let mut rx_buffer = [0; 4096];\n    let mut tx_buffer = [0; 4096];\n    let mut buf = [0; 4096];\n\n    loop {\n        let mut socket = TcpSocket::new(stack, &mut rx_buffer, &mut tx_buffer);\n        socket.set_timeout(Some(embassy_time::Duration::from_secs(10)));\n\n        info!(\"Listening on TCP:1234...\");\n        if let Err(e) = socket.accept(1234).await {\n            warn!(\"accept error: {:?}\", e);\n            continue;\n        }\n\n        info!(\"Received connection from {:?}\", socket.remote_endpoint());\n\n        loop {\n            let n = match socket.read(&mut buf).await {\n                Ok(0) => {\n                    warn!(\"read EOF\");\n                    break;\n                }\n                Ok(n) => n,\n                Err(e) => {\n                    warn!(\"read error: {:?}\", e);\n                    break;\n                }\n            };\n\n            info!(\"rxd {:02x}\", &buf[..n]);\n\n            match socket.write_all(&buf[..n]).await {\n                Ok(()) => {}\n                Err(e) => {\n                    warn!(\"write error: {:?}\", e);\n                    break;\n                }\n            };\n        }\n    }\n}\n"
  },
  {
    "path": "examples/nrf52840/src/bin/executor_fairness_test.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse core::future::poll_fn;\nuse core::task::Poll;\n\nuse defmt::{info, unwrap};\nuse embassy_executor::Spawner;\nuse embassy_time::{Instant, Timer};\nuse {defmt_rtt as _, panic_probe as _};\n\n#[embassy_executor::task]\nasync fn run1() {\n    loop {\n        info!(\"DING DONG\");\n        Timer::after_ticks(16000).await;\n    }\n}\n\n#[embassy_executor::task]\nasync fn run2() {\n    loop {\n        Timer::at(Instant::from_ticks(0)).await;\n    }\n}\n\n#[embassy_executor::task]\nasync fn run3() {\n    poll_fn(|cx| {\n        cx.waker().wake_by_ref();\n        Poll::<()>::Pending\n    })\n    .await;\n}\n\n#[embassy_executor::main]\nasync fn main(spawner: Spawner) {\n    let _p = embassy_nrf::init(Default::default());\n    spawner.spawn(unwrap!(run1()));\n    spawner.spawn(unwrap!(run2()));\n    spawner.spawn(unwrap!(run3()));\n}\n"
  },
  {
    "path": "examples/nrf52840/src/bin/gpiote_channel.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse defmt::info;\nuse embassy_executor::Spawner;\nuse embassy_nrf::gpio::Pull;\nuse embassy_nrf::gpiote::{InputChannel, InputChannelPolarity};\nuse {defmt_rtt as _, panic_probe as _};\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let p = embassy_nrf::init(Default::default());\n    info!(\"Starting!\");\n\n    let mut ch1 = InputChannel::new(p.GPIOTE_CH0, p.P0_11, Pull::Up, InputChannelPolarity::HiToLo);\n    let mut ch2 = InputChannel::new(p.GPIOTE_CH1, p.P0_12, Pull::Up, InputChannelPolarity::LoToHi);\n    let mut ch3 = InputChannel::new(p.GPIOTE_CH2, p.P0_24, Pull::Up, InputChannelPolarity::Toggle);\n    let mut ch4 = InputChannel::new(p.GPIOTE_CH3, p.P0_25, Pull::Up, InputChannelPolarity::Toggle);\n\n    let button1 = async {\n        loop {\n            ch1.wait().await;\n            info!(\"Button 1 pressed\")\n        }\n    };\n\n    let button2 = async {\n        loop {\n            ch2.wait().await;\n            info!(\"Button 2 released\")\n        }\n    };\n\n    let button3 = async {\n        loop {\n            ch3.wait().await;\n            info!(\"Button 3 toggled\")\n        }\n    };\n\n    let button4 = async {\n        loop {\n            ch4.wait().await;\n            info!(\"Button 4 toggled\")\n        }\n    };\n\n    embassy_futures::join::join4(button1, button2, button3, button4).await;\n}\n"
  },
  {
    "path": "examples/nrf52840/src/bin/gpiote_port.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse defmt::{info, unwrap};\nuse embassy_executor::Spawner;\nuse embassy_nrf::gpio::{Input, Pull};\nuse {defmt_rtt as _, panic_probe as _};\n\n#[embassy_executor::task(pool_size = 4)]\nasync fn button_task(n: usize, mut pin: Input<'static>) {\n    loop {\n        pin.wait_for_low().await;\n        info!(\"Button {:?} pressed!\", n);\n        pin.wait_for_high().await;\n        info!(\"Button {:?} released!\", n);\n    }\n}\n\n#[embassy_executor::main]\nasync fn main(spawner: Spawner) {\n    let p = embassy_nrf::init(Default::default());\n    info!(\"Starting!\");\n\n    let btn1 = Input::new(p.P0_11, Pull::Up);\n    let btn2 = Input::new(p.P0_12, Pull::Up);\n    let btn3 = Input::new(p.P0_24, Pull::Up);\n    let btn4 = Input::new(p.P0_25, Pull::Up);\n\n    spawner.spawn(unwrap!(button_task(1, btn1)));\n    spawner.spawn(unwrap!(button_task(2, btn2)));\n    spawner.spawn(unwrap!(button_task(3, btn3)));\n    spawner.spawn(unwrap!(button_task(4, btn4)));\n}\n"
  },
  {
    "path": "examples/nrf52840/src/bin/i2s_effect.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse core::f32::consts::PI;\n\nuse defmt::{error, info};\nuse embassy_executor::Spawner;\nuse embassy_nrf::i2s::{self, Channels, Config, I2S, MasterClock, MultiBuffering, Sample as _, SampleWidth};\nuse embassy_nrf::{bind_interrupts, peripherals};\nuse {defmt_rtt as _, panic_probe as _};\n\ntype Sample = i16;\n\nconst NUM_BUFFERS: usize = 2;\nconst NUM_SAMPLES: usize = 4;\n\nbind_interrupts!(struct Irqs {\n    I2S => i2s::InterruptHandler<peripherals::I2S>;\n});\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let p = embassy_nrf::init(Default::default());\n\n    let master_clock: MasterClock = i2s::ExactSampleRate::_50000.into();\n\n    let sample_rate = master_clock.sample_rate();\n    info!(\"Sample rate: {}\", sample_rate);\n\n    let mut config = Config::default();\n    config.sample_width = SampleWidth::_16bit;\n    config.channels = Channels::MonoLeft;\n\n    let buffers_out = MultiBuffering::<Sample, NUM_BUFFERS, NUM_SAMPLES>::new();\n    let buffers_in = MultiBuffering::<Sample, NUM_BUFFERS, NUM_SAMPLES>::new();\n    let mut full_duplex_stream = I2S::new_master(p.I2S, Irqs, p.P0_25, p.P0_26, p.P0_27, master_clock, config)\n        .full_duplex(p.P0_29, p.P0_28, buffers_out, buffers_in);\n\n    let mut modulator = SineOsc::new();\n    modulator.set_frequency(8.0, 1.0 / sample_rate as f32);\n    modulator.set_amplitude(1.0);\n\n    full_duplex_stream.start().await.expect(\"I2S Start\");\n\n    loop {\n        let (buff_out, buff_in) = full_duplex_stream.buffers();\n        for i in 0..NUM_SAMPLES {\n            let modulation = (Sample::SCALE as f32 * bipolar_to_unipolar(modulator.generate())) as Sample;\n            buff_out[i] = buff_in[i] * modulation;\n        }\n\n        if let Err(err) = full_duplex_stream.send_and_receive().await {\n            error!(\"{}\", err);\n        }\n    }\n}\n\nstruct SineOsc {\n    amplitude: f32,\n    modulo: f32,\n    phase_inc: f32,\n}\n\nimpl SineOsc {\n    const B: f32 = 4.0 / PI;\n    const C: f32 = -4.0 / (PI * PI);\n    const P: f32 = 0.225;\n\n    pub fn new() -> Self {\n        Self {\n            amplitude: 1.0,\n            modulo: 0.0,\n            phase_inc: 0.0,\n        }\n    }\n\n    pub fn set_frequency(&mut self, freq: f32, inv_sample_rate: f32) {\n        self.phase_inc = freq * inv_sample_rate;\n    }\n\n    pub fn set_amplitude(&mut self, amplitude: f32) {\n        self.amplitude = amplitude;\n    }\n\n    pub fn generate(&mut self) -> f32 {\n        let signal = self.parabolic_sin(self.modulo);\n        self.modulo += self.phase_inc;\n        if self.modulo < 0.0 {\n            self.modulo += 1.0;\n        } else if self.modulo > 1.0 {\n            self.modulo -= 1.0;\n        }\n        signal * self.amplitude\n    }\n\n    fn parabolic_sin(&mut self, modulo: f32) -> f32 {\n        let angle = PI - modulo * 2.0 * PI;\n        let y = Self::B * angle + Self::C * angle * abs(angle);\n        Self::P * (y * abs(y) - y) + y\n    }\n}\n\n#[inline]\nfn abs(value: f32) -> f32 {\n    if value < 0.0 { -value } else { value }\n}\n\n#[inline]\nfn bipolar_to_unipolar(value: f32) -> f32 {\n    (value + 1.0) / 2.0\n}\n"
  },
  {
    "path": "examples/nrf52840/src/bin/i2s_monitor.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse defmt::{debug, error, info};\nuse embassy_executor::Spawner;\nuse embassy_nrf::i2s::{self, Channels, Config, DoubleBuffering, I2S, MasterClock, Sample as _, SampleWidth};\nuse embassy_nrf::pwm::{DutyCycle, Prescaler, SimplePwm};\nuse embassy_nrf::{bind_interrupts, peripherals};\nuse {defmt_rtt as _, panic_probe as _};\n\ntype Sample = i16;\n\nconst NUM_SAMPLES: usize = 500;\n\nbind_interrupts!(struct Irqs {\n    I2S => i2s::InterruptHandler<peripherals::I2S>;\n});\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let p = embassy_nrf::init(Default::default());\n\n    let master_clock: MasterClock = i2s::ExactSampleRate::_50000.into();\n\n    let sample_rate = master_clock.sample_rate();\n    info!(\"Sample rate: {}\", sample_rate);\n\n    let mut config = Config::default();\n    config.sample_width = SampleWidth::_16bit;\n    config.channels = Channels::MonoLeft;\n\n    let buffers = DoubleBuffering::<Sample, NUM_SAMPLES>::new();\n    let mut input_stream =\n        I2S::new_master(p.I2S, Irqs, p.P0_25, p.P0_26, p.P0_27, master_clock, config).input(p.P0_29, buffers);\n\n    // Configure the PWM to use the pins corresponding to the RGB leds\n    let mut pwm = SimplePwm::new_3ch(p.PWM0, p.P0_23, p.P0_22, p.P0_24, &Default::default());\n    pwm.set_prescaler(Prescaler::Div1);\n    pwm.set_max_duty(255);\n\n    let mut rms_online = RmsOnline::<NUM_SAMPLES>::default();\n\n    input_stream.start().await.expect(\"I2S Start\");\n\n    loop {\n        let rms = rms_online.process(input_stream.buffer());\n        let rgb = rgb_from_rms(rms);\n\n        debug!(\"RMS: {}, RGB: {:?}\", rms, rgb);\n        let duties = rgb.map(|byte| DutyCycle::normal(u16::from(byte)));\n        pwm.set_all_duties([duties[0], duties[1], duties[2], DutyCycle::normal(0)]);\n\n        if let Err(err) = input_stream.receive().await {\n            error!(\"{}\", err);\n        }\n    }\n}\n\n/// RMS from 0.0 until 0.75 will give green with a proportional intensity\n/// RMS from 0.75 until 0.9 will give a blend between orange and red proportionally to the intensity\n/// RMS above 0.9 will give a red with a proportional intensity\nfn rgb_from_rms(rms: f32) -> [u8; 3] {\n    if rms < 0.75 {\n        let intensity = rms / 0.75;\n        [0, (intensity * 165.0) as u8, 0]\n    } else if rms < 0.9 {\n        let intensity = (rms - 0.75) / 0.15;\n        [200, 165 - (165.0 * intensity) as u8, 0]\n    } else {\n        let intensity = (rms - 0.9) / 0.1;\n        [200 + (55.0 * intensity) as u8, 0, 0]\n    }\n}\n\npub struct RmsOnline<const N: usize> {\n    pub squares: [f32; N],\n    pub head: usize,\n}\n\nimpl<const N: usize> Default for RmsOnline<N> {\n    fn default() -> Self {\n        RmsOnline {\n            squares: [0.0; N],\n            head: 0,\n        }\n    }\n}\n\nimpl<const N: usize> RmsOnline<N> {\n    pub fn reset(&mut self) {\n        self.squares = [0.0; N];\n        self.head = 0;\n    }\n\n    pub fn process(&mut self, buf: &[Sample]) -> f32 {\n        buf.iter()\n            .for_each(|sample| self.push(*sample as f32 / Sample::SCALE as f32));\n\n        let sum_of_squares = self.squares.iter().fold(0.0, |acc, v| acc + *v);\n        Self::approx_sqrt(sum_of_squares / N as f32)\n    }\n\n    pub fn push(&mut self, signal: f32) {\n        let square = signal * signal;\n        self.squares[self.head] = square;\n        self.head = (self.head + 1) % N;\n    }\n\n    /// Approximated sqrt taken from [micromath]\n    ///\n    /// [micromath]: https://docs.rs/micromath/latest/src/micromath/float/sqrt.rs.html#11-17\n    ///\n    fn approx_sqrt(value: f32) -> f32 {\n        f32::from_bits((value.to_bits() + 0x3f80_0000) >> 1)\n    }\n}\n"
  },
  {
    "path": "examples/nrf52840/src/bin/i2s_waveform.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse core::f32::consts::PI;\n\nuse defmt::{error, info};\nuse embassy_executor::Spawner;\nuse embassy_nrf::i2s::{self, Channels, Config, DoubleBuffering, I2S, MasterClock, Sample as _, SampleWidth};\nuse embassy_nrf::{bind_interrupts, peripherals};\nuse {defmt_rtt as _, panic_probe as _};\n\ntype Sample = i16;\n\nconst NUM_SAMPLES: usize = 50;\n\nbind_interrupts!(struct Irqs {\n    I2S => i2s::InterruptHandler<peripherals::I2S>;\n});\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let p = embassy_nrf::init(Default::default());\n\n    let master_clock: MasterClock = i2s::ExactSampleRate::_50000.into();\n\n    let sample_rate = master_clock.sample_rate();\n    info!(\"Sample rate: {}\", sample_rate);\n\n    let mut config = Config::default();\n    config.sample_width = SampleWidth::_16bit;\n    config.channels = Channels::MonoLeft;\n\n    let buffers = DoubleBuffering::<Sample, NUM_SAMPLES>::new();\n    let mut output_stream =\n        I2S::new_master(p.I2S, Irqs, p.P0_25, p.P0_26, p.P0_27, master_clock, config).output(p.P0_28, buffers);\n\n    let mut waveform = Waveform::new(1.0 / sample_rate as f32);\n\n    waveform.process(output_stream.buffer());\n\n    output_stream.start().await.expect(\"I2S Start\");\n\n    loop {\n        waveform.process(output_stream.buffer());\n\n        if let Err(err) = output_stream.send().await {\n            error!(\"{}\", err);\n        }\n    }\n}\n\nstruct Waveform {\n    inv_sample_rate: f32,\n    carrier: SineOsc,\n    freq_mod: SineOsc,\n    amp_mod: SineOsc,\n}\n\nimpl Waveform {\n    fn new(inv_sample_rate: f32) -> Self {\n        let mut carrier = SineOsc::new();\n        carrier.set_frequency(110.0, inv_sample_rate);\n\n        let mut freq_mod = SineOsc::new();\n        freq_mod.set_frequency(1.0, inv_sample_rate);\n        freq_mod.set_amplitude(1.0);\n\n        let mut amp_mod = SineOsc::new();\n        amp_mod.set_frequency(16.0, inv_sample_rate);\n        amp_mod.set_amplitude(0.5);\n\n        Self {\n            inv_sample_rate,\n            carrier,\n            freq_mod,\n            amp_mod,\n        }\n    }\n\n    fn process(&mut self, buf: &mut [Sample]) {\n        for sample in buf.chunks_mut(1) {\n            let freq_modulation = bipolar_to_unipolar(self.freq_mod.generate());\n            self.carrier\n                .set_frequency(110.0 + 440.0 * freq_modulation, self.inv_sample_rate);\n\n            let amp_modulation = bipolar_to_unipolar(self.amp_mod.generate());\n            self.carrier.set_amplitude(amp_modulation);\n\n            let signal = self.carrier.generate();\n\n            sample[0] = (Sample::SCALE as f32 * signal) as Sample;\n        }\n    }\n}\n\nstruct SineOsc {\n    amplitude: f32,\n    modulo: f32,\n    phase_inc: f32,\n}\n\nimpl SineOsc {\n    const B: f32 = 4.0 / PI;\n    const C: f32 = -4.0 / (PI * PI);\n    const P: f32 = 0.225;\n\n    pub fn new() -> Self {\n        Self {\n            amplitude: 1.0,\n            modulo: 0.0,\n            phase_inc: 0.0,\n        }\n    }\n\n    pub fn set_frequency(&mut self, freq: f32, inv_sample_rate: f32) {\n        self.phase_inc = freq * inv_sample_rate;\n    }\n\n    pub fn set_amplitude(&mut self, amplitude: f32) {\n        self.amplitude = amplitude;\n    }\n\n    pub fn generate(&mut self) -> f32 {\n        let signal = self.parabolic_sin(self.modulo);\n        self.modulo += self.phase_inc;\n        if self.modulo < 0.0 {\n            self.modulo += 1.0;\n        } else if self.modulo > 1.0 {\n            self.modulo -= 1.0;\n        }\n        signal * self.amplitude\n    }\n\n    fn parabolic_sin(&mut self, modulo: f32) -> f32 {\n        let angle = PI - modulo * 2.0 * PI;\n        let y = Self::B * angle + Self::C * angle * abs(angle);\n        Self::P * (y * abs(y) - y) + y\n    }\n}\n\n#[inline]\nfn abs(value: f32) -> f32 {\n    if value < 0.0 { -value } else { value }\n}\n\n#[inline]\nfn bipolar_to_unipolar(value: f32) -> f32 {\n    (value + 1.0) / 2.0\n}\n"
  },
  {
    "path": "examples/nrf52840/src/bin/ieee802154_receive.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse embassy_executor::Spawner;\nuse embassy_nrf::config::{Config, HfclkSource};\nuse embassy_nrf::gpio::{Level, Output, OutputDrive};\nuse embassy_nrf::radio::ieee802154::{self, Packet};\nuse embassy_nrf::{peripherals, radio};\nuse embassy_time::Timer;\nuse {defmt_rtt as _, panic_probe as _};\n\nembassy_nrf::bind_interrupts!(struct Irqs {\n    RADIO => radio::InterruptHandler<peripherals::RADIO>;\n});\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let mut config = Config::default();\n    config.hfclk_source = HfclkSource::ExternalXtal;\n    let peripherals = embassy_nrf::init(config);\n\n    // assumes LED on P0_15 with active-high polarity\n    let mut gpo_led = Output::new(peripherals.P0_15, Level::Low, OutputDrive::Standard);\n\n    let mut radio = ieee802154::Radio::new(peripherals.RADIO, Irqs);\n    let mut packet = Packet::new();\n\n    loop {\n        gpo_led.set_low();\n        let rv = radio.receive(&mut packet).await;\n        gpo_led.set_high();\n        match rv {\n            Err(_) => defmt::error!(\"receive() Err\"),\n            Ok(_) => defmt::info!(\"receive() {:?}\", *packet),\n        }\n        Timer::after_millis(100u64).await;\n    }\n}\n"
  },
  {
    "path": "examples/nrf52840/src/bin/ieee802154_send.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse embassy_executor::Spawner;\nuse embassy_nrf::config::{Config, HfclkSource};\nuse embassy_nrf::gpio::{Level, Output, OutputDrive};\nuse embassy_nrf::radio::ieee802154::{self, Packet};\nuse embassy_nrf::{peripherals, radio};\nuse embassy_time::Timer;\nuse {defmt_rtt as _, panic_probe as _};\n\nembassy_nrf::bind_interrupts!(struct Irqs {\n    RADIO => radio::InterruptHandler<peripherals::RADIO>;\n});\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let mut config = Config::default();\n    config.hfclk_source = HfclkSource::ExternalXtal;\n    let peripherals = embassy_nrf::init(config);\n\n    // assumes LED on P0_15 with active-high polarity\n    let mut gpo_led = Output::new(peripherals.P0_15, Level::Low, OutputDrive::Standard);\n\n    let mut radio = ieee802154::Radio::new(peripherals.RADIO, Irqs);\n    let mut packet = Packet::new();\n\n    loop {\n        packet.copy_from_slice(&[0_u8; 16]);\n        gpo_led.set_high();\n        let rv = radio.try_send(&mut packet).await;\n        gpo_led.set_low();\n        match rv {\n            Err(_) => defmt::error!(\"try_send() Err\"),\n            Ok(_) => defmt::info!(\"try_send() {:?}\", *packet),\n        }\n        Timer::after_millis(1000u64).await;\n    }\n}\n"
  },
  {
    "path": "examples/nrf52840/src/bin/manually_create_executor.rs",
    "content": "// This example showcases how to manually create an executor.\n// This is what the #[embassy::main] macro does behind the scenes.\n\n#![no_std]\n#![no_main]\n\nuse cortex_m_rt::entry;\nuse defmt::{info, unwrap};\nuse embassy_executor::Executor;\nuse embassy_time::Timer;\nuse static_cell::StaticCell;\nuse {defmt_rtt as _, panic_probe as _};\n\n#[embassy_executor::task]\nasync fn run1() {\n    loop {\n        info!(\"BIG INFREQUENT TICK\");\n        Timer::after_ticks(64000).await;\n    }\n}\n\n#[embassy_executor::task]\nasync fn run2() {\n    loop {\n        info!(\"tick\");\n        Timer::after_ticks(13000).await;\n    }\n}\n\nstatic EXECUTOR: StaticCell<Executor> = StaticCell::new();\n\n#[entry]\nfn main() -> ! {\n    info!(\"Hello World!\");\n\n    let _p = embassy_nrf::init(Default::default());\n\n    // Create the executor and put it in a StaticCell, because `run` needs `&'static mut Executor`.\n    let executor = EXECUTOR.init(Executor::new());\n\n    // Run it.\n    // `run` calls the closure then runs the executor forever. It never returns.\n    executor.run(|spawner| {\n        // Here we get access to a spawner to spawn the initial tasks.\n        spawner.spawn(unwrap!(run1()));\n        spawner.spawn(unwrap!(run2()));\n    });\n}\n"
  },
  {
    "path": "examples/nrf52840/src/bin/multiprio.rs",
    "content": "//! This example showcases how to create multiple Executor instances to run tasks at\n//! different priority levels.\n//!\n//! Low priority executor runs in thread mode (not interrupt), and uses `sev` for signaling\n//! there's work in the queue, and `wfe` for waiting for work.\n//!\n//! Medium and high priority executors run in two interrupts with different priorities.\n//! Signaling work is done by pending the interrupt. No \"waiting\" needs to be done explicitly, since\n//! when there's work the interrupt will trigger and run the executor.\n//!\n//! Sample output below. Note that high priority ticks can interrupt everything else, and\n//! medium priority computations can interrupt low priority computations, making them to appear\n//! to take significantly longer time.\n//!\n//! ```not_rust\n//!     [med] Starting long computation\n//!     [med] done in 992 ms\n//!         [high] tick!\n//! [low] Starting long computation\n//!     [med] Starting long computation\n//!         [high] tick!\n//!         [high] tick!\n//!     [med] done in 993 ms\n//!     [med] Starting long computation\n//!         [high] tick!\n//!         [high] tick!\n//!     [med] done in 993 ms\n//! [low] done in 3972 ms\n//!     [med] Starting long computation\n//!         [high] tick!\n//!         [high] tick!\n//!     [med] done in 993 ms\n//! ```\n//!\n//! For comparison, try changing the code so all 3 tasks get spawned on the low priority executor.\n//! You will get an output like the following. Note that no computation is ever interrupted.\n//!\n//! ```not_rust\n//!         [high] tick!\n//!     [med] Starting long computation\n//!     [med] done in 496 ms\n//! [low] Starting long computation\n//! [low] done in 992 ms\n//!     [med] Starting long computation\n//!     [med] done in 496 ms\n//!         [high] tick!\n//! [low] Starting long computation\n//! [low] done in 992 ms\n//!         [high] tick!\n//!     [med] Starting long computation\n//!     [med] done in 496 ms\n//!         [high] tick!\n//! ```\n//!\n\n#![no_std]\n#![no_main]\n\nuse cortex_m_rt::entry;\nuse defmt::{info, unwrap};\nuse embassy_executor::{Executor, InterruptExecutor};\nuse embassy_nrf::interrupt;\nuse embassy_nrf::interrupt::{InterruptExt, Priority};\nuse embassy_time::{Instant, Timer};\nuse static_cell::StaticCell;\nuse {defmt_rtt as _, panic_probe as _};\n\n#[embassy_executor::task]\nasync fn run_high() {\n    loop {\n        info!(\"        [high] tick!\");\n        Timer::after_ticks(27374).await;\n    }\n}\n\n#[embassy_executor::task]\nasync fn run_med() {\n    loop {\n        let start = Instant::now();\n        info!(\"    [med] Starting long computation\");\n\n        // Spin-wait to simulate a long CPU computation\n        embassy_time::block_for(embassy_time::Duration::from_secs(1)); // ~1 second\n\n        let end = Instant::now();\n        let ms = end.duration_since(start).as_ticks() / 33;\n        info!(\"    [med] done in {} ms\", ms);\n\n        Timer::after_ticks(23421).await;\n    }\n}\n\n#[embassy_executor::task]\nasync fn run_low() {\n    loop {\n        let start = Instant::now();\n        info!(\"[low] Starting long computation\");\n\n        // Spin-wait to simulate a long CPU computation\n        embassy_time::block_for(embassy_time::Duration::from_secs(2)); // ~2 seconds\n\n        let end = Instant::now();\n        let ms = end.duration_since(start).as_ticks() / 33;\n        info!(\"[low] done in {} ms\", ms);\n\n        Timer::after_ticks(32983).await;\n    }\n}\n\nstatic EXECUTOR_HIGH: InterruptExecutor = InterruptExecutor::new();\nstatic EXECUTOR_MED: InterruptExecutor = InterruptExecutor::new();\nstatic EXECUTOR_LOW: StaticCell<Executor> = StaticCell::new();\n\n#[interrupt]\nunsafe fn EGU1_SWI1() {\n    unsafe { EXECUTOR_HIGH.on_interrupt() }\n}\n\n#[interrupt]\nunsafe fn EGU0_SWI0() {\n    unsafe { EXECUTOR_MED.on_interrupt() }\n}\n\n#[entry]\nfn main() -> ! {\n    info!(\"Hello World!\");\n\n    let _p = embassy_nrf::init(Default::default());\n\n    // High-priority executor: EGU1_SWI1, priority level 6\n    interrupt::EGU1_SWI1.set_priority(Priority::P6);\n    let spawner = EXECUTOR_HIGH.start(interrupt::EGU1_SWI1);\n    spawner.spawn(unwrap!(run_high()));\n\n    // Medium-priority executor: EGU0_SWI0, priority level 7\n    interrupt::EGU0_SWI0.set_priority(Priority::P7);\n    let spawner = EXECUTOR_MED.start(interrupt::EGU0_SWI0);\n    spawner.spawn(unwrap!(run_med()));\n\n    // Low priority executor: runs in thread mode, using WFE/SEV\n    let executor = EXECUTOR_LOW.init(Executor::new());\n    executor.run(|spawner| {\n        spawner.spawn(unwrap!(run_low()));\n    });\n}\n"
  },
  {
    "path": "examples/nrf52840/src/bin/mutex.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse defmt::{info, unwrap};\nuse embassy_executor::Spawner;\nuse embassy_sync::blocking_mutex::raw::ThreadModeRawMutex;\nuse embassy_sync::mutex::Mutex;\nuse embassy_time::Timer;\nuse {defmt_rtt as _, panic_probe as _};\n\nstatic MUTEX: Mutex<ThreadModeRawMutex, u32> = Mutex::new(0);\n\n#[embassy_executor::task]\nasync fn my_task() {\n    loop {\n        {\n            let mut m = MUTEX.lock().await;\n            info!(\"start long operation\");\n            *m += 1000;\n\n            // Hold the mutex for a long time.\n            Timer::after_secs(1).await;\n            info!(\"end long operation: count = {}\", *m);\n        }\n\n        Timer::after_secs(1).await;\n    }\n}\n\n#[embassy_executor::main]\nasync fn main(spawner: Spawner) {\n    let _p = embassy_nrf::init(Default::default());\n    spawner.spawn(unwrap!(my_task()));\n\n    loop {\n        Timer::after_millis(300).await;\n        let mut m = MUTEX.lock().await;\n        *m += 1;\n        info!(\"short operation: count = {}\", *m);\n    }\n}\n"
  },
  {
    "path": "examples/nrf52840/src/bin/nfct.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse defmt::{todo, *};\nuse embassy_executor::Spawner;\nuse embassy_nrf::config::HfclkSource;\nuse embassy_nrf::nfct::{Config as NfcConfig, NfcId, NfcT};\nuse embassy_nrf::{bind_interrupts, nfct};\nuse iso14443_4::{Card, IsoDep};\nuse {defmt_rtt as _, embassy_nrf as _, panic_probe as _};\n\nbind_interrupts!(struct Irqs {\n    NFCT => nfct::InterruptHandler;\n});\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let mut config = embassy_nrf::config::Config::default();\n    config.hfclk_source = HfclkSource::ExternalXtal;\n    let p = embassy_nrf::init(config);\n\n    dbg!(\"Setting up...\");\n    let config = NfcConfig {\n        nfcid1: NfcId::DoubleSize([0x04, 0x68, 0x95, 0x71, 0xFA, 0x5C, 0x64]),\n        sdd_pat: nfct::SddPat::SDD00100,\n        plat_conf: 0b0000,\n        protocol: nfct::SelResProtocol::Type4A,\n    };\n\n    let mut nfc = NfcT::new(p.NFCT, Irqs, &config);\n\n    let mut buf = [0u8; 256];\n\n    let cc = &[\n        0x00, 0x0f, /* CCEN_HI, CCEN_LOW */\n        0x20, /* VERSION */\n        0x00, 0x7f, /* MLe_HI, MLe_LOW */\n        0x00, 0x7f, /* MLc_HI, MLc_LOW */\n        /* TLV */\n        0x04, 0x06, 0xe1, 0x04, 0x00, 0x7f, 0x00, 0x00,\n    ];\n\n    let ndef = &[\n        0x00, 0x10, 0xd1, 0x1, 0xc, 0x55, 0x4, 0x65, 0x6d, 0x62, 0x61, 0x73, 0x73, 0x79, 0x2e, 0x64, 0x65, 0x76,\n    ];\n    let mut selected: &[u8] = cc;\n\n    loop {\n        info!(\"activating\");\n        nfc.activate().await;\n        info!(\"activated!\");\n\n        let mut nfc = IsoDep::new(iso14443_3::Logger(&mut nfc));\n\n        loop {\n            let n = match nfc.receive(&mut buf).await {\n                Ok(n) => n,\n                Err(e) => {\n                    error!(\"rx error {}\", e);\n                    break;\n                }\n            };\n            let req = &buf[..n];\n            info!(\"iso-dep rx {:02x}\", req);\n\n            let Ok(apdu) = Apdu::parse(req) else {\n                error!(\"apdu parse error\");\n                break;\n            };\n\n            info!(\"apdu: {:?}\", apdu);\n\n            let resp = match (apdu.cla, apdu.ins, apdu.p1, apdu.p2) {\n                (0, 0xa4, 4, 0) => {\n                    info!(\"select app\");\n                    &[0x90, 0x00][..]\n                }\n                (0, 0xa4, 0, 12) => {\n                    info!(\"select df\");\n                    match apdu.data {\n                        [0xe1, 0x03] => {\n                            selected = cc;\n                            &[0x90, 0x00][..]\n                        }\n                        [0xe1, 0x04] => {\n                            selected = ndef;\n                            &[0x90, 0x00][..]\n                        }\n                        _ => todo!(), // return NOT FOUND\n                    }\n                }\n                (0, 0xb0, p1, p2) => {\n                    info!(\"read\");\n                    let offs = u16::from_be_bytes([p1 & 0x7f, p2]) as usize;\n                    let len = if apdu.le == 0 { usize::MAX } else { apdu.le as usize };\n                    let n = len.min(selected.len() - offs);\n                    buf[..n].copy_from_slice(&selected[offs..][..n]);\n                    buf[n..][..2].copy_from_slice(&[0x90, 0x00]);\n                    &buf[..n + 2]\n                }\n                _ => {\n                    info!(\"Got unknown command!\");\n                    &[0xFF, 0xFF]\n                }\n            };\n\n            info!(\"iso-dep tx {:02x}\", resp);\n\n            match nfc.transmit(resp).await {\n                Ok(()) => {}\n                Err(e) => {\n                    error!(\"tx error {}\", e);\n                    break;\n                }\n            }\n        }\n    }\n}\n\n#[derive(Debug, Clone, defmt::Format)]\nstruct Apdu<'a> {\n    pub cla: u8,\n    pub ins: u8,\n    pub p1: u8,\n    pub p2: u8,\n    pub data: &'a [u8],\n    pub le: u16,\n}\n\n#[derive(Debug, Clone, Copy, PartialEq, Eq, defmt::Format)]\nstruct ApduParseError;\n\nimpl<'a> Apdu<'a> {\n    pub fn parse(apdu: &'a [u8]) -> Result<Self, ApduParseError> {\n        if apdu.len() < 4 {\n            return Err(ApduParseError);\n        }\n\n        let (data, le) = match apdu.len() - 4 {\n            0 => (&[][..], 0),\n            1 => (&[][..], apdu[4]),\n            n if n == 1 + apdu[4] as usize && apdu[4] != 0 => (&apdu[5..][..apdu[4] as usize], 0),\n            n if n == 2 + apdu[4] as usize && apdu[4] != 0 => (&apdu[5..][..apdu[4] as usize], apdu[apdu.len() - 1]),\n            _ => return Err(ApduParseError),\n        };\n\n        Ok(Apdu {\n            cla: apdu[0],\n            ins: apdu[1],\n            p1: apdu[2],\n            p2: apdu[3],\n            data,\n            le: le as _,\n        })\n    }\n}\n\nmod iso14443_3 {\n    use core::future::Future;\n\n    use defmt::info;\n    use embassy_nrf::nfct::{Error, NfcT};\n\n    pub trait Card {\n        type Error;\n        async fn receive(&mut self, buf: &mut [u8]) -> Result<usize, Self::Error>;\n        async fn transmit(&mut self, buf: &[u8]) -> Result<(), Self::Error>;\n    }\n\n    impl<'a, T: Card> Card for &'a mut T {\n        type Error = T::Error;\n\n        fn receive(&mut self, buf: &mut [u8]) -> impl Future<Output = Result<usize, Self::Error>> {\n            T::receive(self, buf)\n        }\n\n        fn transmit(&mut self, buf: &[u8]) -> impl Future<Output = Result<(), Self::Error>> {\n            T::transmit(self, buf)\n        }\n    }\n\n    impl<'a> Card for NfcT<'a> {\n        type Error = Error;\n\n        fn receive(&mut self, buf: &mut [u8]) -> impl Future<Output = Result<usize, Self::Error>> {\n            self.receive(buf)\n        }\n\n        fn transmit(&mut self, buf: &[u8]) -> impl Future<Output = Result<(), Self::Error>> {\n            self.transmit(buf)\n        }\n    }\n\n    pub struct Logger<T: Card>(pub T);\n\n    impl<T: Card> Card for Logger<T> {\n        type Error = T::Error;\n\n        async fn receive(&mut self, buf: &mut [u8]) -> Result<usize, Self::Error> {\n            let n = T::receive(&mut self.0, buf).await?;\n            info!(\"<- {:02x}\", &buf[..n]);\n            Ok(n)\n        }\n\n        fn transmit(&mut self, buf: &[u8]) -> impl Future<Output = Result<(), Self::Error>> {\n            info!(\"-> {:02x}\", buf);\n            T::transmit(&mut self.0, buf)\n        }\n    }\n}\n\nmod iso14443_4 {\n    use defmt::info;\n\n    use crate::iso14443_3;\n\n    #[derive(defmt::Format)]\n    pub enum Error<T> {\n        Deselected,\n        Protocol,\n        Lower(T),\n    }\n\n    pub trait Card {\n        type Error;\n        async fn receive(&mut self, buf: &mut [u8]) -> Result<usize, Self::Error>;\n        async fn transmit(&mut self, buf: &[u8]) -> Result<(), Self::Error>;\n    }\n\n    pub struct IsoDep<T: iso14443_3::Card> {\n        nfc: T,\n\n        /// Block count spin bit: 0 or 1\n        block_num: u8,\n\n        /// true if deselected. This is permanent, you must create another IsoDep\n        /// instance if we get selected again.\n        deselected: bool,\n\n        /// last response, in case we need to retransmit.\n        resp: [u8; 256],\n        resp_len: usize,\n    }\n\n    impl<T: iso14443_3::Card> IsoDep<T> {\n        pub fn new(nfc: T) -> Self {\n            Self {\n                nfc,\n                block_num: 1,\n                deselected: false,\n                resp: [0u8; 256],\n                resp_len: 0,\n            }\n        }\n    }\n\n    impl<T: iso14443_3::Card> Card for IsoDep<T> {\n        type Error = Error<T::Error>;\n\n        async fn receive(&mut self, buf: &mut [u8]) -> Result<usize, Self::Error> {\n            if self.deselected {\n                return Err(Error::Deselected);\n            }\n\n            let mut temp = [0u8; 256];\n\n            loop {\n                let n = self.nfc.receive(&mut temp).await.map_err(Error::Lower)?;\n                assert!(n != 0);\n                match temp[0] {\n                    0x02 | 0x03 => {\n                        self.block_num ^= 0x01;\n                        assert!(temp[0] == 0x02 | self.block_num);\n                        buf[..n - 1].copy_from_slice(&temp[1..n]);\n                        return Ok(n - 1);\n                    }\n                    0xb2 | 0xb3 => {\n                        if temp[0] & 0x01 != self.block_num {\n                            info!(\"Got NAK, transmitting ACK.\");\n                            let resp = &[0xA2 | self.block_num];\n                            self.nfc.transmit(resp).await.map_err(Error::Lower)?;\n                        } else {\n                            info!(\"Got NAK, retransmitting.\");\n                            let resp: &[u8] = &self.resp[..self.resp_len];\n                            self.nfc.transmit(resp).await.map_err(Error::Lower)?;\n                        }\n                    }\n                    0xe0 => {\n                        info!(\"Got RATS, tx'ing ATS\");\n                        let resp = &[0x06, 0x77, 0x77, 0x81, 0x02, 0x80];\n                        self.nfc.transmit(resp).await.map_err(Error::Lower)?;\n                    }\n                    0xc2 => {\n                        info!(\"Got deselect!\");\n                        self.deselected = true;\n                        let resp = &[0xC2];\n                        self.nfc.transmit(resp).await.map_err(Error::Lower)?;\n                        return Err(Error::Deselected);\n                    }\n                    _ => {\n                        info!(\"Got unknown command {:02x}!\", temp[0]);\n                        return Err(Error::Protocol);\n                    }\n                };\n            }\n        }\n\n        async fn transmit(&mut self, buf: &[u8]) -> Result<(), Self::Error> {\n            if self.deselected {\n                return Err(Error::Deselected);\n            }\n\n            self.resp[0] = 0x02 | self.block_num;\n            self.resp[1..][..buf.len()].copy_from_slice(buf);\n            self.resp_len = 1 + buf.len();\n\n            let resp: &[u8] = &self.resp[..self.resp_len];\n            self.nfc.transmit(resp).await.map_err(Error::Lower)?;\n\n            Ok(())\n        }\n    }\n}\n"
  },
  {
    "path": "examples/nrf52840/src/bin/nvmc.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse defmt::{info, unwrap};\nuse embassy_executor::Spawner;\nuse embassy_nrf::nvmc::Nvmc;\nuse embedded_storage::nor_flash::{NorFlash, ReadNorFlash};\nuse {defmt_rtt as _, panic_probe as _};\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let p = embassy_nrf::init(Default::default());\n    info!(\"Hello NVMC!\");\n\n    let mut f = Nvmc::new(p.NVMC);\n    const ADDR: u32 = 0x80000;\n\n    info!(\"Reading...\");\n    let mut buf = [0u8; 4];\n    unwrap!(f.read(ADDR, &mut buf));\n    info!(\"Read: {=[u8]:x}\", buf);\n\n    info!(\"Erasing...\");\n    unwrap!(f.erase(ADDR, ADDR + 4096));\n\n    info!(\"Reading...\");\n    let mut buf = [0u8; 4];\n    unwrap!(f.read(ADDR, &mut buf));\n    info!(\"Read: {=[u8]:x}\", buf);\n\n    info!(\"Writing...\");\n    unwrap!(f.write(ADDR, &[1, 2, 3, 4]));\n\n    info!(\"Reading...\");\n    let mut buf = [0u8; 4];\n    unwrap!(f.read(ADDR, &mut buf));\n    info!(\"Read: {=[u8]:x}\", buf);\n}\n"
  },
  {
    "path": "examples/nrf52840/src/bin/pdm.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse defmt::info;\nuse embassy_executor::Spawner;\nuse embassy_nrf::pdm::{self, Config, Pdm};\nuse embassy_nrf::{bind_interrupts, peripherals};\nuse embassy_time::Timer;\nuse fixed::types::I7F1;\nuse num_integer::Roots;\nuse {defmt_rtt as _, panic_probe as _};\n\nbind_interrupts!(struct Irqs {\n    PDM => pdm::InterruptHandler<peripherals::PDM>;\n});\n\n#[embassy_executor::main]\nasync fn main(_p: Spawner) {\n    let p = embassy_nrf::init(Default::default());\n    let config = Config::default();\n    let mut pdm = Pdm::new(p.PDM, Irqs, p.P0_01, p.P0_00, config);\n\n    loop {\n        for gain in [I7F1::from_num(-20), I7F1::from_num(0), I7F1::from_num(20)] {\n            pdm.set_gain(gain, gain);\n            info!(\"Gain = {} dB\", defmt::Debug2Format(&gain));\n            pdm.start().await;\n\n            // wait some time till the microphon settled\n            Timer::after_millis(1000).await;\n\n            const SAMPLES: usize = 2048;\n            let mut buf = [0i16; SAMPLES];\n            pdm.sample(&mut buf).await.unwrap();\n\n            let mean = (buf.iter().map(|v| i32::from(*v)).sum::<i32>() / buf.len() as i32) as i16;\n            info!(\n                \"{} samples, min {=i16}, max {=i16}, mean {=i16}, AC RMS {=i16}\",\n                buf.len(),\n                buf.iter().min().unwrap(),\n                buf.iter().max().unwrap(),\n                mean,\n                (buf.iter()\n                    .map(|v| i32::from(*v - mean).pow(2))\n                    .fold(0i32, |a, b| a.saturating_add(b))\n                    / buf.len() as i32)\n                    .sqrt() as i16,\n            );\n\n            info!(\"samples: {:?}\", &buf);\n\n            pdm.stop().await;\n            Timer::after_millis(100).await;\n        }\n    }\n}\n"
  },
  {
    "path": "examples/nrf52840/src/bin/pdm_continuous.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse core::cmp::Ordering;\n\nuse defmt::info;\nuse embassy_executor::Spawner;\nuse embassy_nrf::pdm::{self, Config, Frequency, OperationMode, Pdm, Ratio, SamplerState};\nuse embassy_nrf::{bind_interrupts, peripherals};\nuse fixed::types::I7F1;\nuse microfft::real::rfft_1024;\nuse num_integer::Roots;\nuse {defmt_rtt as _, panic_probe as _};\n\n// Demonstrates both continuous sampling and scanning multiple channels driven by a PPI linked timer\n\nbind_interrupts!(struct Irqs {\n    PDM => pdm::InterruptHandler<peripherals::PDM>;\n});\n\n#[embassy_executor::main]\nasync fn main(_p: Spawner) {\n    let p = embassy_nrf::init(Default::default());\n    let mut config = Config::default();\n    // Pins are correct for the onboard microphone on the Feather nRF52840 Sense.\n    config.frequency = Frequency::_1280K; // 16 kHz sample rate\n    config.ratio = Ratio::RATIO80;\n    config.operation_mode = OperationMode::Mono;\n    config.gain_left = I7F1::from_bits(5); // 2.5 dB\n    let mut pdm = Pdm::new(p.PDM, Irqs, p.P0_00, p.P0_01, config);\n\n    let mut bufs = [[0; 1024]; 2];\n\n    pdm.run_task_sampler(&mut bufs, move |buf| {\n        // NOTE: It is important that the time spent within this callback\n        // does not exceed the time taken to acquire the 1500 samples we\n        // have in this example, which would be 10us + 2us per\n        // sample * 1500 = 18ms. You need to measure the time taken here\n        // and set the sample buffer size accordingly. Exceeding this\n        // time can lead to the peripheral re-writing the other buffer.\n        let mean = (buf.iter().map(|v| i32::from(*v)).sum::<i32>() / buf.len() as i32) as i16;\n        let (peak_freq_index, peak_mag) = fft_peak_freq(&buf);\n        let peak_freq = peak_freq_index * 16000 / buf.len();\n        info!(\n            \"{} samples, min {=i16}, max {=i16}, mean {=i16}, AC RMS {=i16}, peak {} @ {} Hz\",\n            buf.len(),\n            buf.iter().min().unwrap(),\n            buf.iter().max().unwrap(),\n            mean,\n            (buf.iter()\n                .map(|v| i32::from(*v - mean).pow(2))\n                .fold(0i32, |a, b| a.saturating_add(b))\n                / buf.len() as i32)\n                .sqrt() as i16,\n            peak_mag,\n            peak_freq,\n        );\n        SamplerState::Sampled\n    })\n    .await\n    .unwrap();\n}\n\nfn fft_peak_freq(input: &[i16; 1024]) -> (usize, u32) {\n    let mut f = [0f32; 1024];\n    for i in 0..input.len() {\n        f[i] = (input[i] as f32) / 32768.0;\n    }\n    // N.B. rfft_1024 does the FFT in-place so result is actually also a reference to f.\n    let result = rfft_1024(&mut f);\n    result[0].im = 0.0;\n\n    result\n        .iter()\n        .map(|c| c.norm_sqr())\n        .enumerate()\n        .max_by(|(_, a), (_, b)| a.partial_cmp(b).unwrap_or(Ordering::Equal))\n        .map(|(i, v)| (i, ((v * 32768.0) as u32).sqrt()))\n        .unwrap()\n}\n"
  },
  {
    "path": "examples/nrf52840/src/bin/ppi.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse core::future::pending;\n\nuse defmt::info;\nuse embassy_executor::Spawner;\nuse embassy_nrf::gpio::{Level, OutputDrive, Pull};\nuse embassy_nrf::gpiote::{self, InputChannel, InputChannelPolarity};\nuse embassy_nrf::ppi::Ppi;\nuse gpiote::{OutputChannel, OutputChannelPolarity};\nuse {defmt_rtt as _, panic_probe as _};\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let p = embassy_nrf::init(Default::default());\n    info!(\"Starting!\");\n\n    let button1 = InputChannel::new(p.GPIOTE_CH0, p.P0_11, Pull::Up, InputChannelPolarity::HiToLo);\n    let button2 = InputChannel::new(p.GPIOTE_CH1, p.P0_12, Pull::Up, InputChannelPolarity::HiToLo);\n    let button3 = InputChannel::new(p.GPIOTE_CH2, p.P0_24, Pull::Up, InputChannelPolarity::HiToLo);\n    let button4 = InputChannel::new(p.GPIOTE_CH3, p.P0_25, Pull::Up, InputChannelPolarity::HiToLo);\n\n    let led1 = OutputChannel::new(\n        p.GPIOTE_CH4,\n        p.P0_13,\n        Level::Low,\n        OutputDrive::Standard,\n        OutputChannelPolarity::Toggle,\n    );\n\n    let led2 = OutputChannel::new(\n        p.GPIOTE_CH5,\n        p.P0_14,\n        Level::Low,\n        OutputDrive::Standard,\n        OutputChannelPolarity::Toggle,\n    );\n\n    let mut ppi = Ppi::new_one_to_one(p.PPI_CH0, button1.event_in(), led1.task_out());\n    ppi.enable();\n\n    let mut ppi = Ppi::new_one_to_one(p.PPI_CH1, button2.event_in(), led1.task_clr());\n    ppi.enable();\n\n    let mut ppi = Ppi::new_one_to_one(p.PPI_CH2, button3.event_in(), led1.task_set());\n    ppi.enable();\n\n    let mut ppi = Ppi::new_one_to_two(p.PPI_CH3, button4.event_in(), led1.task_out(), led2.task_out());\n    ppi.enable();\n\n    info!(\"PPI setup!\");\n    info!(\"Press button 1 to toggle LED 1\");\n    info!(\"Press button 2 to turn on LED 1\");\n    info!(\"Press button 3 to turn off LED 1\");\n    info!(\"Press button 4 to toggle LEDs 1 and 2\");\n\n    // Block forever so the above drivers don't get dropped\n    pending::<()>().await;\n}\n"
  },
  {
    "path": "examples/nrf52840/src/bin/pubsub.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse defmt::unwrap;\nuse embassy_executor::Spawner;\nuse embassy_sync::blocking_mutex::raw::ThreadModeRawMutex;\nuse embassy_sync::pubsub::{DynSubscriber, PubSubChannel, Subscriber};\nuse embassy_time::Timer;\nuse {defmt_rtt as _, panic_probe as _};\n\n/// Create the message bus. It has a queue of 4, supports 3 subscribers and 1 publisher\nstatic MESSAGE_BUS: PubSubChannel<ThreadModeRawMutex, Message, 4, 3, 1> = PubSubChannel::new();\n\n#[derive(Clone, defmt::Format)]\nenum Message {\n    A,\n    B,\n    C,\n}\n\n#[embassy_executor::main]\nasync fn main(spawner: Spawner) {\n    let _p = embassy_nrf::init(Default::default());\n    defmt::info!(\"Hello World!\");\n\n    // It's good to set up the subscribers before publishing anything.\n    // A subscriber will only yield messages that have been published after its creation.\n\n    spawner.spawn(fast_logger(unwrap!(MESSAGE_BUS.subscriber())).unwrap());\n    spawner.spawn(slow_logger(unwrap!(MESSAGE_BUS.dyn_subscriber())).unwrap());\n    spawner.spawn(slow_logger_pure(unwrap!(MESSAGE_BUS.dyn_subscriber())).unwrap());\n\n    // Get a publisher\n    let message_publisher = unwrap!(MESSAGE_BUS.publisher());\n    // We can't get more (normal) publishers\n    // We can have an infinite amount of immediate publishers. They can't await a publish, only do an immediate publish\n    defmt::assert!(MESSAGE_BUS.publisher().is_err());\n\n    let mut index = 0;\n    loop {\n        Timer::after_millis(500).await;\n\n        let message = match index % 3 {\n            0 => Message::A,\n            1 => Message::B,\n            2..=u32::MAX => Message::C,\n        };\n\n        // We publish immediately and don't await anything.\n        // If the queue is full, it will cause the oldest message to not be received by some/all subscribers\n        message_publisher.publish_immediate(message);\n\n        // Try to comment out the last one and uncomment this line below.\n        // The behaviour will change:\n        // - The subscribers won't miss any messages any more\n        // - Trying to publish now has some wait time when the queue is full\n\n        // message_publisher.publish(message).await;\n\n        index += 1;\n    }\n}\n\n/// A logger task that just awaits the messages it receives\n///\n/// This takes the generic `Subscriber`. This is most performant, but requires you to write down all of the generics\n#[embassy_executor::task]\nasync fn fast_logger(mut messages: Subscriber<'static, ThreadModeRawMutex, Message, 4, 3, 1>) {\n    loop {\n        let message = messages.next_message().await;\n        defmt::info!(\"Received message at fast logger: {:?}\", message);\n    }\n}\n\n/// A logger task that awaits the messages, but also does some other work.\n/// Because of this, depending on how the messages were published, the subscriber might miss some messages.\n///\n/// This takes the dynamic `DynSubscriber`. This is not as performant as the generic version, but let's you ignore some of the generics.\n#[embassy_executor::task]\nasync fn slow_logger(mut messages: DynSubscriber<'static, Message>) {\n    loop {\n        // Do some work\n        Timer::after_millis(2000).await;\n\n        // If the publisher has used the `publish_immediate` function, then we may receive a lag message here\n        let message = messages.next_message().await;\n        defmt::info!(\"Received message at slow logger: {:?}\", message);\n\n        // If the previous one was a lag message, then we should receive the next message here immediately\n        let message = messages.next_message().await;\n        defmt::info!(\"Received message at slow logger: {:?}\", message);\n    }\n}\n\n/// Same as `slow_logger` but it ignores lag results\n#[embassy_executor::task]\nasync fn slow_logger_pure(mut messages: DynSubscriber<'static, Message>) {\n    loop {\n        // Do some work\n        Timer::after_millis(2000).await;\n\n        // Instead of receiving lags here, we just ignore that and read the next message\n        let message = messages.next_message_pure().await;\n        defmt::info!(\"Received message at slow logger pure: {:?}\", message);\n    }\n}\n"
  },
  {
    "path": "examples/nrf52840/src/bin/pwm.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_nrf::pwm::{DutyCycle, Prescaler, SimplePwm};\nuse embassy_time::Timer;\nuse {defmt_rtt as _, panic_probe as _};\n\n// for i in range(1024): print(int((math.sin(i/512*math.pi)*0.4+0.5)**2*32767), ', ', end='')\nstatic DUTY: [u16; 1024] = [\n    8191, 8272, 8353, 8434, 8516, 8598, 8681, 8764, 8847, 8931, 9015, 9099, 9184, 9269, 9354, 9440, 9526, 9613, 9700,\n    9787, 9874, 9962, 10050, 10139, 10227, 10316, 10406, 10495, 10585, 10675, 10766, 10857, 10948, 11039, 11131, 11223,\n    11315, 11407, 11500, 11592, 11685, 11779, 11872, 11966, 12060, 12154, 12248, 12343, 12438, 12533, 12628, 12723,\n    12818, 12914, 13010, 13106, 13202, 13298, 13394, 13491, 13587, 13684, 13781, 13878, 13975, 14072, 14169, 14266,\n    14364, 14461, 14558, 14656, 14754, 14851, 14949, 15046, 15144, 15242, 15339, 15437, 15535, 15632, 15730, 15828,\n    15925, 16023, 16120, 16218, 16315, 16412, 16510, 16607, 16704, 16801, 16898, 16995, 17091, 17188, 17284, 17380,\n    17477, 17572, 17668, 17764, 17859, 17955, 18050, 18145, 18239, 18334, 18428, 18522, 18616, 18710, 18803, 18896,\n    18989, 19082, 19174, 19266, 19358, 19449, 19540, 19631, 19722, 19812, 19902, 19991, 20081, 20169, 20258, 20346,\n    20434, 20521, 20608, 20695, 20781, 20867, 20952, 21037, 21122, 21206, 21290, 21373, 21456, 21538, 21620, 21701,\n    21782, 21863, 21943, 22022, 22101, 22179, 22257, 22335, 22412, 22488, 22564, 22639, 22714, 22788, 22861, 22934,\n    23007, 23079, 23150, 23220, 23290, 23360, 23429, 23497, 23564, 23631, 23698, 23763, 23828, 23892, 23956, 24019,\n    24081, 24143, 24204, 24264, 24324, 24383, 24441, 24499, 24555, 24611, 24667, 24721, 24775, 24828, 24881, 24933,\n    24983, 25034, 25083, 25132, 25180, 25227, 25273, 25319, 25363, 25407, 25451, 25493, 25535, 25575, 25615, 25655,\n    25693, 25731, 25767, 25803, 25838, 25873, 25906, 25939, 25971, 26002, 26032, 26061, 26089, 26117, 26144, 26170,\n    26195, 26219, 26242, 26264, 26286, 26307, 26327, 26346, 26364, 26381, 26397, 26413, 26427, 26441, 26454, 26466,\n    26477, 26487, 26496, 26505, 26512, 26519, 26525, 26530, 26534, 26537, 26539, 26540, 26541, 26540, 26539, 26537,\n    26534, 26530, 26525, 26519, 26512, 26505, 26496, 26487, 26477, 26466, 26454, 26441, 26427, 26413, 26397, 26381,\n    26364, 26346, 26327, 26307, 26286, 26264, 26242, 26219, 26195, 26170, 26144, 26117, 26089, 26061, 26032, 26002,\n    25971, 25939, 25906, 25873, 25838, 25803, 25767, 25731, 25693, 25655, 25615, 25575, 25535, 25493, 25451, 25407,\n    25363, 25319, 25273, 25227, 25180, 25132, 25083, 25034, 24983, 24933, 24881, 24828, 24775, 24721, 24667, 24611,\n    24555, 24499, 24441, 24383, 24324, 24264, 24204, 24143, 24081, 24019, 23956, 23892, 23828, 23763, 23698, 23631,\n    23564, 23497, 23429, 23360, 23290, 23220, 23150, 23079, 23007, 22934, 22861, 22788, 22714, 22639, 22564, 22488,\n    22412, 22335, 22257, 22179, 22101, 22022, 21943, 21863, 21782, 21701, 21620, 21538, 21456, 21373, 21290, 21206,\n    21122, 21037, 20952, 20867, 20781, 20695, 20608, 20521, 20434, 20346, 20258, 20169, 20081, 19991, 19902, 19812,\n    19722, 19631, 19540, 19449, 19358, 19266, 19174, 19082, 18989, 18896, 18803, 18710, 18616, 18522, 18428, 18334,\n    18239, 18145, 18050, 17955, 17859, 17764, 17668, 17572, 17477, 17380, 17284, 17188, 17091, 16995, 16898, 16801,\n    16704, 16607, 16510, 16412, 16315, 16218, 16120, 16023, 15925, 15828, 15730, 15632, 15535, 15437, 15339, 15242,\n    15144, 15046, 14949, 14851, 14754, 14656, 14558, 14461, 14364, 14266, 14169, 14072, 13975, 13878, 13781, 13684,\n    13587, 13491, 13394, 13298, 13202, 13106, 13010, 12914, 12818, 12723, 12628, 12533, 12438, 12343, 12248, 12154,\n    12060, 11966, 11872, 11779, 11685, 11592, 11500, 11407, 11315, 11223, 11131, 11039, 10948, 10857, 10766, 10675,\n    10585, 10495, 10406, 10316, 10227, 10139, 10050, 9962, 9874, 9787, 9700, 9613, 9526, 9440, 9354, 9269, 9184, 9099,\n    9015, 8931, 8847, 8764, 8681, 8598, 8516, 8434, 8353, 8272, 8191, 8111, 8031, 7952, 7873, 7794, 7716, 7638, 7561,\n    7484, 7407, 7331, 7255, 7180, 7105, 7031, 6957, 6883, 6810, 6738, 6665, 6594, 6522, 6451, 6381, 6311, 6241, 6172,\n    6104, 6036, 5968, 5901, 5834, 5767, 5702, 5636, 5571, 5507, 5443, 5379, 5316, 5253, 5191, 5130, 5068, 5008, 4947,\n    4888, 4828, 4769, 4711, 4653, 4596, 4539, 4482, 4426, 4371, 4316, 4261, 4207, 4153, 4100, 4047, 3995, 3943, 3892,\n    3841, 3791, 3741, 3691, 3642, 3594, 3546, 3498, 3451, 3404, 3358, 3312, 3267, 3222, 3178, 3134, 3090, 3047, 3005,\n    2962, 2921, 2879, 2839, 2798, 2758, 2719, 2680, 2641, 2603, 2565, 2528, 2491, 2454, 2418, 2382, 2347, 2312, 2278,\n    2244, 2210, 2177, 2144, 2112, 2080, 2048, 2017, 1986, 1956, 1926, 1896, 1867, 1838, 1810, 1781, 1754, 1726, 1699,\n    1673, 1646, 1620, 1595, 1570, 1545, 1520, 1496, 1472, 1449, 1426, 1403, 1380, 1358, 1336, 1315, 1294, 1273, 1252,\n    1232, 1212, 1192, 1173, 1154, 1135, 1117, 1099, 1081, 1063, 1046, 1029, 1012, 996, 980, 964, 948, 933, 918, 903,\n    888, 874, 860, 846, 833, 819, 806, 793, 781, 768, 756, 744, 733, 721, 710, 699, 688, 677, 667, 657, 647, 637, 627,\n    618, 609, 599, 591, 582, 574, 565, 557, 549, 541, 534, 526, 519, 512, 505, 498, 492, 485, 479, 473, 467, 461, 455,\n    450, 444, 439, 434, 429, 424, 419, 415, 410, 406, 402, 398, 394, 390, 386, 383, 379, 376, 373, 370, 367, 364, 361,\n    359, 356, 354, 351, 349, 347, 345, 343, 342, 340, 338, 337, 336, 334, 333, 332, 331, 330, 330, 329, 328, 328, 328,\n    327, 327, 327, 327, 327, 328, 328, 328, 329, 330, 330, 331, 332, 333, 334, 336, 337, 338, 340, 342, 343, 345, 347,\n    349, 351, 354, 356, 359, 361, 364, 367, 370, 373, 376, 379, 383, 386, 390, 394, 398, 402, 406, 410, 415, 419, 424,\n    429, 434, 439, 444, 450, 455, 461, 467, 473, 479, 485, 492, 498, 505, 512, 519, 526, 534, 541, 549, 557, 565, 574,\n    582, 591, 599, 609, 618, 627, 637, 647, 657, 667, 677, 688, 699, 710, 721, 733, 744, 756, 768, 781, 793, 806, 819,\n    833, 846, 860, 874, 888, 903, 918, 933, 948, 964, 980, 996, 1012, 1029, 1046, 1063, 1081, 1099, 1117, 1135, 1154,\n    1173, 1192, 1212, 1232, 1252, 1273, 1294, 1315, 1336, 1358, 1380, 1403, 1426, 1449, 1472, 1496, 1520, 1545, 1570,\n    1595, 1620, 1646, 1673, 1699, 1726, 1754, 1781, 1810, 1838, 1867, 1896, 1926, 1956, 1986, 2017, 2048, 2080, 2112,\n    2144, 2177, 2210, 2244, 2278, 2312, 2347, 2382, 2418, 2454, 2491, 2528, 2565, 2603, 2641, 2680, 2719, 2758, 2798,\n    2839, 2879, 2921, 2962, 3005, 3047, 3090, 3134, 3178, 3222, 3267, 3312, 3358, 3404, 3451, 3498, 3546, 3594, 3642,\n    3691, 3741, 3791, 3841, 3892, 3943, 3995, 4047, 4100, 4153, 4207, 4261, 4316, 4371, 4426, 4482, 4539, 4596, 4653,\n    4711, 4769, 4828, 4888, 4947, 5008, 5068, 5130, 5191, 5253, 5316, 5379, 5443, 5507, 5571, 5636, 5702, 5767, 5834,\n    5901, 5968, 6036, 6104, 6172, 6241, 6311, 6381, 6451, 6522, 6594, 6665, 6738, 6810, 6883, 6957, 7031, 7105, 7180,\n    7255, 7331, 7407, 7484, 7561, 7638, 7716, 7794, 7873, 7952, 8031, 8111,\n];\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let p = embassy_nrf::init(Default::default());\n    let mut pwm = SimplePwm::new_4ch(p.PWM0, p.P0_13, p.P0_14, p.P0_16, p.P0_15, &Default::default());\n    pwm.set_prescaler(Prescaler::Div1);\n    pwm.set_max_duty(32767);\n    info!(\"pwm initialized!\");\n\n    let mut i = 0;\n    loop {\n        i += 1;\n        pwm.set_all_duties([\n            DutyCycle::normal(DUTY[i % 1024]),\n            DutyCycle::normal(DUTY[(i + 256) % 1024]),\n            DutyCycle::normal(DUTY[(i + 512) % 1024]),\n            DutyCycle::normal(DUTY[(i + 768) % 1024]),\n        ]);\n        Timer::after_millis(3).await;\n    }\n}\n"
  },
  {
    "path": "examples/nrf52840/src/bin/pwm_double_sequence.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_nrf::pwm::{\n    Config, Prescaler, Sequence, SequenceConfig, SequenceMode, SequencePwm, Sequencer, StartSequence,\n};\nuse embassy_time::Timer;\nuse {defmt_rtt as _, panic_probe as _};\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let p = embassy_nrf::init(Default::default());\n    let seq_words_0: [u16; 5] = [1000, 250, 100, 50, 0];\n    let seq_words_1: [u16; 4] = [50, 100, 250, 1000];\n\n    let mut config = Config::default();\n    config.prescaler = Prescaler::Div128;\n    // 1 period is 1000 * (128/16mhz = 0.000008s = 0.008ms) = 8us\n    // but say we want to hold the value for 5000ms\n    // so we want to repeat our value as many times as necessary until 5000ms passes\n    // want 5000/8 = 625 periods total to occur, so 624 (we get the one period for free remember)\n    let mut seq_config = SequenceConfig::default();\n    seq_config.refresh = 624;\n    // thus our sequence takes 5 * 5000ms or 25 seconds\n\n    let mut pwm = unwrap!(SequencePwm::new_1ch(p.PWM0, p.P0_13, config));\n\n    let sequence_0 = Sequence::new(&seq_words_0, seq_config.clone());\n    let sequence_1 = Sequence::new(&seq_words_1, seq_config);\n    let sequencer = Sequencer::new(&mut pwm, sequence_0, Some(sequence_1));\n    unwrap!(sequencer.start(StartSequence::Zero, SequenceMode::Loop(1)));\n\n    // we can abort a sequence if we need to before its complete with pwm.stop()\n    // or stop is also implicitly called when the pwm peripheral is dropped\n    // when it goes out of scope\n    Timer::after_millis(40000).await;\n    info!(\"pwm stopped early!\");\n}\n"
  },
  {
    "path": "examples/nrf52840/src/bin/pwm_sequence.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_nrf::pwm::{Config, Prescaler, SequenceConfig, SequencePwm, SingleSequenceMode, SingleSequencer};\nuse embassy_time::Timer;\nuse {defmt_rtt as _, panic_probe as _};\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let p = embassy_nrf::init(Default::default());\n    let seq_words: [u16; 5] = [1000, 250, 100, 50, 0];\n\n    let mut config = Config::default();\n    config.prescaler = Prescaler::Div128;\n    // 1 period is 1000 * (128/16mhz = 0.000008s = 0.008ms) = 8us\n    // but say we want to hold the value for 5000ms\n    // so we want to repeat our value as many times as necessary until 5000ms passes\n    // want 5000/8 = 625 periods total to occur, so 624 (we get the one period for free remember)\n    let mut seq_config = SequenceConfig::default();\n    seq_config.refresh = 624;\n    // thus our sequence takes 5 * 5000ms or 25 seconds\n\n    let mut pwm = unwrap!(SequencePwm::new_1ch(p.PWM0, p.P0_13, config,));\n\n    let sequencer = SingleSequencer::new(&mut pwm, &seq_words, seq_config);\n    unwrap!(sequencer.start(SingleSequenceMode::Times(1)));\n\n    // we can abort a sequence if we need to before its complete with pwm.stop()\n    // or stop is also implicitly called when the pwm peripheral is dropped\n    // when it goes out of scope\n    Timer::after_millis(20000).await;\n    info!(\"pwm stopped early!\");\n}\n"
  },
  {
    "path": "examples/nrf52840/src/bin/pwm_sequence_ppi.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse core::future::pending;\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_nrf::gpio::Pull;\nuse embassy_nrf::gpiote::{InputChannel, InputChannelPolarity};\nuse embassy_nrf::ppi::Ppi;\nuse embassy_nrf::pwm::{Config, Prescaler, SequenceConfig, SequencePwm, SingleSequenceMode, SingleSequencer};\nuse {defmt_rtt as _, panic_probe as _};\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let p = embassy_nrf::init(Default::default());\n    let seq_words: [u16; 5] = [1000, 250, 100, 50, 0];\n\n    let mut config = Config::default();\n    config.prescaler = Prescaler::Div128;\n    // 1 period is 1000 * (128/16mhz = 0.000008s = 0.008ms) = 8us\n    // but say we want to hold the value for 250ms 250ms/8 = 31.25 periods\n    // so round to 31 - 1 (we get the one period for free remember)\n    // thus our sequence takes 5 * 250ms or 1.25 seconds\n    let mut seq_config = SequenceConfig::default();\n    seq_config.refresh = 30;\n\n    let mut pwm = unwrap!(SequencePwm::new_1ch(p.PWM0, p.P0_13, config));\n\n    // pwm.stop() deconfigures pins, and then the task_start_seq0 task cant work\n    // so its going to have to start running in order load the configuration\n\n    let button1 = InputChannel::new(p.GPIOTE_CH0, p.P0_11, Pull::Up, InputChannelPolarity::HiToLo);\n\n    let button2 = InputChannel::new(p.GPIOTE_CH1, p.P0_12, Pull::Up, InputChannelPolarity::HiToLo);\n\n    // messing with the pwm tasks is ill advised\n    // Times::Ininite and Times even are seq0, Times odd is seq1\n    let start = unsafe { pwm.task_start_seq0() };\n    let stop = unsafe { pwm.task_stop() };\n\n    let sequencer = SingleSequencer::new(&mut pwm, &seq_words, seq_config);\n    unwrap!(sequencer.start(SingleSequenceMode::Infinite));\n\n    let mut ppi = Ppi::new_one_to_one(p.PPI_CH1, button1.event_in(), start);\n    ppi.enable();\n\n    let mut ppi2 = Ppi::new_one_to_one(p.PPI_CH0, button2.event_in(), stop);\n    ppi2.enable();\n\n    info!(\"PPI setup!\");\n    info!(\"Press button 1 to start LED 1\");\n    info!(\"Press button 2 to stop LED 1\");\n    info!(\"Note! task_stop stops the sequence, but not the pin output\");\n\n    // Block forever so the above drivers don't get dropped\n    pending::<()>().await;\n}\n"
  },
  {
    "path": "examples/nrf52840/src/bin/pwm_sequence_ws2812b.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_nrf::pwm::{\n    Config, Prescaler, SequenceConfig, SequenceLoad, SequencePwm, SingleSequenceMode, SingleSequencer,\n};\nuse embassy_time::Timer;\nuse {defmt_rtt as _, panic_probe as _};\n\n// WS2812B LED light demonstration. Drives just one light.\n// The following reference on WS2812B may be of use:\n// https://cdn-shop.adafruit.com/datasheets/WS2812B.pdf.\n// This demo lights up a single LED in blue. It then proceeds\n// to pulsate the LED rapidly.\n//\n// /!\\ NOTE FOR nRF52840-DK users /!\\\n//\n// If you're using the nRF52840-DK, the default \"Vdd\" power source\n// will set the GPIO I/O voltage to 3.0v, using the onboard regulator.\n// This can sometimes not be enough to drive the WS2812B signal if you\n// are not using an external level shifter. If you set the board to \"USB\"\n// power instead (and provide power via the \"nRF USB\" connector), the board\n// will instead power the I/Os at 3.3v, which is often enough (but still\n// out of official spec) for the WS2812Bs to work properly.\n\n// In the following declarations, setting the high bit tells the PWM\n// to reverse polarity, which is what the WS2812B expects.\n\nconst T1H: u16 = 0x8000 | 13; // Duty = 13/20 ticks (0.8us/1.25us) for a 1\nconst T0H: u16 = 0x8000 | 7; // Duty 7/20 ticks (0.4us/1.25us) for a 0\nconst RES: u16 = 0x8000;\n\n// Provides data to a WS2812b (Neopixel) LED and makes it go blue. The data\n// line is assumed to be P1_05.\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let p = embassy_nrf::init(Default::default());\n    let mut config = Config::default();\n    config.sequence_load = SequenceLoad::Common;\n    config.prescaler = Prescaler::Div1;\n    config.max_duty = 20; // 1.25us (1s / 16Mhz * 20)\n    let mut pwm = unwrap!(SequencePwm::new_1ch(p.PWM0, p.P1_05, config));\n\n    // Declare the bits of 24 bits in a buffer we'll be\n    // mutating later.\n    let mut seq_words = [\n        T0H, T0H, T0H, T0H, T0H, T0H, T0H, T0H, // G\n        T0H, T0H, T0H, T0H, T0H, T0H, T0H, T0H, // R\n        T1H, T1H, T1H, T1H, T1H, T1H, T1H, T1H, // B\n        RES,\n    ];\n    let mut seq_config = SequenceConfig::default();\n    seq_config.end_delay = 799; // 50us (20 ticks * 40) - 1 tick because we've already got one RES;\n\n    let mut color_bit = 16;\n    let mut bit_value = T0H;\n\n    loop {\n        let sequences = SingleSequencer::new(&mut pwm, &seq_words, seq_config.clone());\n        unwrap!(sequences.start(SingleSequenceMode::Times(1)));\n\n        Timer::after_millis(50).await;\n\n        if bit_value == T0H {\n            if color_bit == 20 {\n                bit_value = T1H;\n            } else {\n                color_bit += 1;\n            }\n        } else {\n            if color_bit == 16 {\n                bit_value = T0H;\n            } else {\n                color_bit -= 1;\n            }\n        }\n\n        drop(sequences);\n\n        seq_words[color_bit] = bit_value;\n    }\n}\n"
  },
  {
    "path": "examples/nrf52840/src/bin/pwm_servo.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_nrf::pwm::{DutyCycle, Prescaler, SimplePwm};\nuse embassy_time::Timer;\nuse {defmt_rtt as _, panic_probe as _};\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let p = embassy_nrf::init(Default::default());\n    let mut pwm = SimplePwm::new_1ch(p.PWM0, p.P0_05, &Default::default());\n    // sg90 microervo requires 50hz or 20ms period\n    // set_period can only set down to 125khz so we cant use it directly\n    // Div128 is 125khz or 0.000008s or 0.008ms, 20/0.008 is 2500 is top\n    pwm.set_prescaler(Prescaler::Div128);\n    pwm.set_max_duty(2500);\n    info!(\"pwm initialized!\");\n\n    Timer::after_millis(5000).await;\n\n    // 1ms 0deg (1/.008=125), 1.5ms 90deg (1.5/.008=187.5), 2ms 180deg (2/.008=250),\n    loop {\n        info!(\"45 deg\");\n        // poor mans inverting, subtract our value from max_duty\n        pwm.set_duty(0, DutyCycle::normal(2500 - 156));\n        Timer::after_millis(5000).await;\n\n        info!(\"90 deg\");\n        pwm.set_duty(0, DutyCycle::normal(2500 - 187));\n        Timer::after_millis(5000).await;\n\n        info!(\"135 deg\");\n        pwm.set_duty(0, DutyCycle::normal(2500 - 218));\n        Timer::after_millis(5000).await;\n\n        info!(\"180 deg\");\n        pwm.set_duty(0, DutyCycle::normal(2500 - 250));\n        Timer::after_millis(5000).await;\n\n        info!(\"0 deg\");\n        pwm.set_duty(0, DutyCycle::normal(2500 - 125));\n        Timer::after_millis(5000).await;\n    }\n}\n"
  },
  {
    "path": "examples/nrf52840/src/bin/qdec.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse defmt::info;\nuse embassy_executor::Spawner;\nuse embassy_nrf::qdec::{self, Qdec};\nuse embassy_nrf::{bind_interrupts, peripherals};\nuse {defmt_rtt as _, panic_probe as _};\n\nbind_interrupts!(struct Irqs {\n    QDEC => qdec::InterruptHandler<peripherals::QDEC>;\n});\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let p = embassy_nrf::init(Default::default());\n    let config = qdec::Config::default();\n    let mut rotary_enc = Qdec::new(p.QDEC, Irqs, p.P0_31, p.P0_30, config);\n\n    info!(\"Turn rotary encoder!\");\n    let mut value = 0;\n    loop {\n        value += rotary_enc.read().await;\n        info!(\"Value: {}\", value);\n    }\n}\n"
  },
  {
    "path": "examples/nrf52840/src/bin/qspi.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse defmt::{assert_eq, info, unwrap};\nuse embassy_executor::Spawner;\nuse embassy_nrf::qspi::Frequency;\nuse embassy_nrf::{bind_interrupts, peripherals, qspi};\nuse {defmt_rtt as _, panic_probe as _};\n\nconst PAGE_SIZE: usize = 4096;\n\n// Workaround for alignment requirements.\n// Nicer API will probably come in the future.\n#[repr(C, align(4))]\nstruct AlignedBuf([u8; 4096]);\n\nbind_interrupts!(struct Irqs {\n    QSPI => qspi::InterruptHandler<peripherals::QSPI>;\n});\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let p = embassy_nrf::init(Default::default());\n    // Config for the MX25R64 present in the nRF52840 DK\n    let mut config = qspi::Config::default();\n    config.capacity = 8 * 1024 * 1024; // 8 MB\n    config.frequency = Frequency::M32;\n    config.read_opcode = qspi::ReadOpcode::READ4IO;\n    config.write_opcode = qspi::WriteOpcode::PP4IO;\n    config.write_page_size = qspi::WritePageSize::_256BYTES;\n\n    let mut q = qspi::Qspi::new(\n        p.QSPI, Irqs, p.P0_19, p.P0_17, p.P0_20, p.P0_21, p.P0_22, p.P0_23, config,\n    );\n\n    let mut id = [1; 3];\n    unwrap!(q.custom_instruction(0x9F, &[], &mut id).await);\n    info!(\"id: {}\", id);\n\n    // Read status register\n    let mut status = [4; 1];\n    unwrap!(q.custom_instruction(0x05, &[], &mut status).await);\n\n    info!(\"status: {:?}\", status[0]);\n\n    if status[0] & 0x40 == 0 {\n        status[0] |= 0x40;\n\n        unwrap!(q.custom_instruction(0x01, &status, &mut []).await);\n\n        info!(\"enabled quad in status\");\n    }\n\n    let mut buf = AlignedBuf([0u8; PAGE_SIZE]);\n\n    let pattern = |a: u32| (a ^ (a >> 8) ^ (a >> 16) ^ (a >> 24)) as u8;\n\n    for i in 0..8 {\n        info!(\"page {:?}: erasing... \", i);\n        unwrap!(q.erase(i * PAGE_SIZE as u32).await);\n\n        for j in 0..PAGE_SIZE {\n            buf.0[j] = pattern((j as u32 + i * PAGE_SIZE as u32) as u32);\n        }\n\n        info!(\"programming...\");\n        unwrap!(q.write(i * PAGE_SIZE as u32, &buf.0).await);\n    }\n\n    for i in 0..8 {\n        info!(\"page {:?}: reading... \", i);\n        unwrap!(q.read(i * PAGE_SIZE as u32, &mut buf.0).await);\n\n        info!(\"verifying...\");\n        for j in 0..PAGE_SIZE {\n            assert_eq!(buf.0[j], pattern((j as u32 + i * PAGE_SIZE as u32) as u32));\n        }\n    }\n\n    info!(\"done!\")\n}\n"
  },
  {
    "path": "examples/nrf52840/src/bin/qspi_lowpower.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse core::mem;\n\nuse defmt::{info, unwrap};\nuse embassy_executor::Spawner;\nuse embassy_nrf::qspi::Frequency;\nuse embassy_nrf::{bind_interrupts, peripherals, qspi};\nuse embassy_time::Timer;\nuse {defmt_rtt as _, panic_probe as _};\n\n// Workaround for alignment requirements.\n// Nicer API will probably come in the future.\n#[repr(C, align(4))]\nstruct AlignedBuf([u8; 64]);\n\nbind_interrupts!(struct Irqs {\n    QSPI => qspi::InterruptHandler<peripherals::QSPI>;\n});\n\n#[embassy_executor::main]\nasync fn main(_p: Spawner) {\n    let mut p = embassy_nrf::init(Default::default());\n\n    loop {\n        // Config for the MX25R64 present in the nRF52840 DK\n        let mut config = qspi::Config::default();\n        config.capacity = 8 * 1024 * 1024; // 8 MB\n        config.frequency = Frequency::M32;\n        config.read_opcode = qspi::ReadOpcode::READ4IO;\n        config.write_opcode = qspi::WriteOpcode::PP4IO;\n        config.write_page_size = qspi::WritePageSize::_256BYTES;\n        config.deep_power_down = Some(qspi::DeepPowerDownConfig {\n            enter_time: 3, // tDP = 30uS\n            exit_time: 3,  // tRDP = 35uS\n        });\n\n        let mut q = qspi::Qspi::new(\n            p.QSPI.reborrow(),\n            Irqs,\n            p.P0_19.reborrow(),\n            p.P0_17.reborrow(),\n            p.P0_20.reborrow(),\n            p.P0_21.reborrow(),\n            p.P0_22.reborrow(),\n            p.P0_23.reborrow(),\n            config,\n        );\n\n        let mut id = [1; 3];\n        unwrap!(q.custom_instruction(0x9F, &[], &mut id).await);\n        info!(\"id: {}\", id);\n\n        // Read status register\n        let mut status = [4; 1];\n        unwrap!(q.custom_instruction(0x05, &[], &mut status).await);\n\n        info!(\"status: {:?}\", status[0]);\n\n        if status[0] & 0x40 == 0 {\n            status[0] |= 0x40;\n\n            unwrap!(q.custom_instruction(0x01, &status, &mut []).await);\n\n            info!(\"enabled quad in status\");\n        }\n\n        let mut buf = AlignedBuf([0u8; 64]);\n\n        info!(\"reading...\");\n        unwrap!(q.read(0, &mut buf.0).await);\n        info!(\"read: {=[u8]:x}\", buf.0);\n\n        // Drop the QSPI instance. This disables the peripehral and deconfigures the pins.\n        // This clears the borrow on the singletons, so they can now be used again.\n        mem::drop(q);\n\n        // Sleep for 1 second. The executor ensures the core sleeps with a WFE when it has nothing to do.\n        // During this sleep, the nRF chip should only use ~3uA\n        Timer::after_secs(1).await;\n    }\n}\n"
  },
  {
    "path": "examples/nrf52840/src/bin/raw_spawn.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse core::mem;\n\nuse cortex_m_rt::entry;\nuse defmt::{info, unwrap};\nuse embassy_executor::Executor;\nuse embassy_executor::raw::TaskStorage;\nuse embassy_time::Timer;\nuse static_cell::StaticCell;\nuse {defmt_rtt as _, panic_probe as _};\n\nasync fn run1() {\n    loop {\n        info!(\"BIG INFREQUENT TICK\");\n        Timer::after_ticks(64000).await;\n    }\n}\n\nasync fn run2() {\n    loop {\n        info!(\"tick\");\n        Timer::after_ticks(13000).await;\n    }\n}\n\nstatic EXECUTOR: StaticCell<Executor> = StaticCell::new();\n\n#[entry]\nfn main() -> ! {\n    info!(\"Hello World!\");\n\n    let _p = embassy_nrf::init(Default::default());\n    let executor = EXECUTOR.init(Executor::new());\n\n    let run1_task = TaskStorage::new();\n    let run2_task = TaskStorage::new();\n\n    // Safety: these variables do live forever if main never returns.\n    let run1_task = unsafe { make_static(&run1_task) };\n    let run2_task = unsafe { make_static(&run2_task) };\n\n    executor.run(|spawner| {\n        spawner.spawn(unwrap!(run1_task.spawn(|| run1())));\n        spawner.spawn(unwrap!(run2_task.spawn(|| run2())));\n    });\n}\n\nunsafe fn make_static<T>(t: &T) -> &'static T {\n    unsafe { mem::transmute(t) }\n}\n"
  },
  {
    "path": "examples/nrf52840/src/bin/rng.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse embassy_executor::Spawner;\nuse embassy_nrf::rng::Rng;\nuse embassy_nrf::{bind_interrupts, peripherals, rng};\nuse rand::Rng as _;\nuse {defmt_rtt as _, panic_probe as _};\n\nbind_interrupts!(struct Irqs {\n    RNG => rng::InterruptHandler<peripherals::RNG>;\n});\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let p = embassy_nrf::init(Default::default());\n    let mut rng = Rng::new(p.RNG, Irqs);\n\n    // Async API\n    let mut bytes = [0; 4];\n    rng.fill_bytes(&mut bytes).await;\n    defmt::info!(\"Some random bytes: {:?}\", bytes);\n\n    // Sync API with `rand`\n    defmt::info!(\"A random number from 1 to 10: {:?}\", rng.random_range(1..=10));\n\n    let mut bytes = [0; 1024];\n    rng.fill_bytes(&mut bytes).await;\n    let zero_count: u32 = bytes.iter().fold(0, |acc, val| acc + val.count_zeros());\n    let one_count: u32 = bytes.iter().fold(0, |acc, val| acc + val.count_ones());\n    defmt::info!(\"Chance of zero: {}%\", zero_count * 100 / (bytes.len() as u32 * 8));\n    defmt::info!(\"Chance of one: {}%\", one_count * 100 / (bytes.len() as u32 * 8));\n}\n"
  },
  {
    "path": "examples/nrf52840/src/bin/rtc.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse core::cell::RefCell;\n\nuse embassy_executor::Spawner;\nuse embassy_nrf::gpio::{Level, Output, OutputDrive};\nuse embassy_nrf::interrupt;\nuse embassy_nrf::rtc::Rtc;\nuse embassy_sync::blocking_mutex::Mutex;\nuse embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;\nuse portable_atomic::AtomicU64;\nuse {defmt_rtt as _, panic_probe as _};\n\n// 64 bit counter which will never overflow.\nstatic TICK_COUNTER: AtomicU64 = AtomicU64::new(0);\nstatic RTC: Mutex<CriticalSectionRawMutex, RefCell<Option<Rtc<'static>>>> = Mutex::new(RefCell::new(None));\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    defmt::println!(\"nRF52840 RTC example\");\n    let p = embassy_nrf::init(Default::default());\n    let mut led = Output::new(p.P0_13, Level::High, OutputDrive::Standard);\n    // Counter resolution is 125 ms.\n    let mut rtc = Rtc::new(p.RTC0, (1 << 12) - 1).unwrap();\n    rtc.enable_interrupt(embassy_nrf::rtc::Interrupt::Tick, true);\n    rtc.enable_event(embassy_nrf::rtc::Interrupt::Tick);\n    rtc.enable();\n    RTC.lock(|r| {\n        let mut rtc_borrow = r.borrow_mut();\n        *rtc_borrow = Some(rtc);\n    });\n\n    let mut last_counter_val = 0;\n    loop {\n        let current = TICK_COUNTER.load(core::sync::atomic::Ordering::Relaxed);\n        if current != last_counter_val {\n            led.toggle();\n            last_counter_val = current;\n        }\n    }\n}\n\n#[interrupt]\nfn RTC0() {\n    // For 64-bit, we do not need to worry about overflowing, at least not for realistic program\n    // lifetimes.\n    TICK_COUNTER.fetch_add(1, core::sync::atomic::Ordering::Relaxed);\n    RTC.lock(|r| {\n        let mut rtc_borrow = r.borrow_mut();\n        rtc_borrow\n            .as_mut()\n            .unwrap()\n            .reset_event(embassy_nrf::rtc::Interrupt::Tick);\n    });\n}\n"
  },
  {
    "path": "examples/nrf52840/src/bin/saadc.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse defmt::info;\nuse embassy_executor::Spawner;\nuse embassy_nrf::saadc::{ChannelConfig, Config, Saadc};\nuse embassy_nrf::{bind_interrupts, saadc};\nuse embassy_time::Timer;\nuse {defmt_rtt as _, panic_probe as _};\n\nbind_interrupts!(struct Irqs {\n    SAADC => saadc::InterruptHandler;\n});\n\n#[embassy_executor::main]\nasync fn main(_p: Spawner) {\n    let mut p = embassy_nrf::init(Default::default());\n    let config = Config::default();\n    let channel_config = ChannelConfig::single_ended(p.P0_02.reborrow());\n    let mut saadc = Saadc::new(p.SAADC, Irqs, config, [channel_config]);\n\n    loop {\n        let mut buf = [0; 1];\n        saadc.sample(&mut buf).await;\n        info!(\"sample: {=i16}\", &buf[0]);\n        Timer::after_millis(100).await;\n    }\n}\n"
  },
  {
    "path": "examples/nrf52840/src/bin/saadc_continuous.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse defmt::info;\nuse embassy_executor::Spawner;\nuse embassy_nrf::saadc::{CallbackResult, ChannelConfig, Config, Saadc};\nuse embassy_nrf::timer::Frequency;\nuse embassy_nrf::{bind_interrupts, saadc};\nuse {defmt_rtt as _, panic_probe as _};\n\n// Demonstrates both continuous sampling and scanning multiple channels driven by a PPI linked timer\n\nbind_interrupts!(struct Irqs {\n    SAADC => saadc::InterruptHandler;\n});\n\n#[embassy_executor::main]\nasync fn main(_p: Spawner) {\n    let mut p = embassy_nrf::init(Default::default());\n    let config = Config::default();\n    let channel_1_config = ChannelConfig::single_ended(p.P0_02.reborrow());\n    let channel_2_config = ChannelConfig::single_ended(p.P0_03.reborrow());\n    let channel_3_config = ChannelConfig::single_ended(p.P0_04.reborrow());\n    let mut saadc = Saadc::new(\n        p.SAADC,\n        Irqs,\n        config,\n        [channel_1_config, channel_2_config, channel_3_config],\n    );\n\n    // This delay demonstrates that starting the timer prior to running\n    // the task sampler is benign given the calibration that follows.\n    embassy_time::Timer::after_millis(500).await;\n    saadc.calibrate().await;\n\n    let mut bufs = [[[0; 3]; 500]; 2];\n\n    let mut c = 0;\n    let mut a: i32 = 0;\n\n    saadc\n        .run_task_sampler(\n            p.TIMER0.reborrow(),\n            p.PPI_CH0.reborrow(),\n            p.PPI_CH1.reborrow(),\n            Frequency::F1MHz,\n            1000, // We want to sample at 1KHz\n            &mut bufs,\n            move |buf| {\n                // NOTE: It is important that the time spent within this callback\n                // does not exceed the time taken to acquire the 1500 samples we\n                // have in this example, which would be 10us + 2us per\n                // sample * 1500 = 18ms. You need to measure the time taken here\n                // and set the sample buffer size accordingly. Exceeding this\n                // time can lead to the peripheral re-writing the other buffer.\n                for b in buf {\n                    a += b[0] as i32;\n                }\n                c += buf.len();\n                if c > 1000 {\n                    a = a / c as i32;\n                    info!(\"channel 1: {=i32}\", a);\n                    c = 0;\n                    a = 0;\n                }\n                CallbackResult::Continue\n            },\n        )\n        .await;\n}\n"
  },
  {
    "path": "examples/nrf52840/src/bin/self_spawn.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse defmt::{info, unwrap};\nuse embassy_executor::Spawner;\nuse embassy_time::Timer;\nuse {defmt_rtt as _, panic_probe as _};\n\nmod config {\n    pub const MY_TASK_POOL_SIZE: usize = 2;\n}\n\n#[embassy_executor::task(pool_size = config::MY_TASK_POOL_SIZE)]\nasync fn my_task(spawner: Spawner, n: u32) {\n    Timer::after_secs(1).await;\n    info!(\"Spawning self! {}\", n);\n    spawner.spawn(unwrap!(my_task(spawner, n + 1)));\n}\n\n#[embassy_executor::main]\nasync fn main(spawner: Spawner) {\n    let _p = embassy_nrf::init(Default::default());\n    info!(\"Hello World!\");\n    spawner.spawn(unwrap!(my_task(spawner, 0)));\n}\n"
  },
  {
    "path": "examples/nrf52840/src/bin/self_spawn_current_executor.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse defmt::{info, unwrap};\nuse embassy_executor::Spawner;\nuse embassy_time::Timer;\nuse {defmt_rtt as _, panic_probe as _};\n\n#[embassy_executor::task(pool_size = 2)]\nasync fn my_task(n: u32) {\n    Timer::after_secs(1).await;\n    info!(\"Spawning self! {}\", n);\n    let spawner = unsafe { Spawner::for_current_executor().await };\n    spawner.spawn(unwrap!(my_task(n + 1)));\n}\n\n#[embassy_executor::main]\nasync fn main(spawner: Spawner) {\n    let _p = embassy_nrf::init(Default::default());\n    info!(\"Hello World!\");\n    spawner.spawn(unwrap!(my_task(0)));\n}\n"
  },
  {
    "path": "examples/nrf52840/src/bin/sixlowpan.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse core::net::Ipv6Addr;\n\nuse defmt::{info, unwrap, warn};\nuse embassy_executor::Spawner;\nuse embassy_net::udp::{PacketMetadata, UdpMetadata, UdpSocket};\nuse embassy_net::{IpAddress, IpEndpoint, IpListenEndpoint, Ipv6Cidr, StackResources, StaticConfigV6};\nuse embassy_nrf::config::{Config, HfclkSource};\nuse embassy_nrf::rng::Rng;\nuse embassy_nrf::{bind_interrupts, embassy_net_802154_driver as net, peripherals, radio};\nuse embassy_time::Delay;\nuse embedded_hal_async::delay::DelayNs;\nuse static_cell::StaticCell;\nuse {defmt_rtt as _, panic_probe as _};\n\nbind_interrupts!(struct Irqs {\n    RADIO => radio::InterruptHandler<peripherals::RADIO>;\n    RNG => embassy_nrf::rng::InterruptHandler<peripherals::RNG>;\n});\n\n#[embassy_executor::task]\nasync fn ieee802154_task(runner: net::Runner<'static>) -> ! {\n    runner.run().await\n}\n\n#[embassy_executor::task]\nasync fn net_task(mut runner: embassy_net::Runner<'static, net::Device<'static>>) -> ! {\n    runner.run().await\n}\n\n#[embassy_executor::main]\nasync fn main(spawner: Spawner) {\n    let mut config = Config::default();\n    // Necessary to run the radio nrf52840 v1.11 5.4.1\n    config.hfclk_source = HfclkSource::ExternalXtal;\n    let p = embassy_nrf::init(config);\n\n    let mac_addr: [u8; 8] = [2, 3, 4, 5, 6, 7, 8, 9];\n    static NRF802154_STATE: StaticCell<net::State<20, 20>> = StaticCell::new();\n    let (device, runner) = net::new(mac_addr, p.RADIO, Irqs, NRF802154_STATE.init(net::State::new()))\n        .await\n        .unwrap();\n\n    spawner.spawn(unwrap!(ieee802154_task(runner)));\n\n    // Swap these when flashing a second board\n    let peer = Ipv6Addr::new(0xfe80, 0, 0, 0, 0xd701, 0xda3f, 0x3955, 0x82a4);\n    let local = Ipv6Addr::new(0xfe80, 0, 0, 0, 0xd701, 0xda3f, 0x3955, 0x82a5);\n\n    let config = embassy_net::Config::ipv6_static(StaticConfigV6 {\n        address: Ipv6Cidr::new(local, 64),\n        gateway: None,\n        dns_servers: Default::default(),\n    });\n\n    // Generate random seed\n    let mut rng = Rng::new(p.RNG, Irqs);\n    let mut seed = [0; 8];\n    rng.blocking_fill_bytes(&mut seed);\n    let seed = u64::from_le_bytes(seed);\n\n    // Init network stack\n    static RESOURCES: StaticCell<StackResources<3>> = StaticCell::new();\n    let (stack, runner) = embassy_net::new(device, config, RESOURCES.init(StackResources::new()), seed);\n\n    spawner.spawn(unwrap!(net_task(runner)));\n\n    let mut rx_buffer = [0; 2096];\n    let mut tx_buffer = [0; 2096];\n    let mut tx_m_buffer = [PacketMetadata::EMPTY; 5];\n    let mut rx_m_buffer = [PacketMetadata::EMPTY; 5];\n\n    let mut delay = Delay;\n    loop {\n        let mut socket = UdpSocket::new(\n            stack,\n            &mut tx_m_buffer,\n            &mut rx_buffer,\n            &mut rx_m_buffer,\n            &mut tx_buffer,\n        );\n        socket\n            .bind(IpListenEndpoint {\n                addr: Some(IpAddress::Ipv6(local)),\n                port: 1234,\n            })\n            .unwrap();\n        let rep = UdpMetadata {\n            endpoint: IpEndpoint {\n                addr: IpAddress::Ipv6(peer),\n                port: 1234,\n            },\n            local_address: Some(IpAddress::Ipv6(local)),\n            meta: Default::default(),\n        };\n\n        info!(\"Listening on {:?} UDP:1234...\", local);\n\n        let mut recv_buf = [0; 12];\n        loop {\n            delay.delay_ms(2000).await;\n            if socket.may_recv() {\n                let n = match socket.recv_from(&mut recv_buf).await {\n                    Ok((0, _)) => panic!(),\n                    Ok((n, _)) => n,\n                    Err(e) => {\n                        warn!(\"read error: {:?}\", e);\n                        break;\n                    }\n                };\n                info!(\"Received {:02x}\", &recv_buf[..n]);\n            }\n\n            info!(\"Sending\");\n            socket.send_to(b\"Hello World\", rep).await.unwrap();\n        }\n    }\n}\n"
  },
  {
    "path": "examples/nrf52840/src/bin/spim.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse defmt::{info, unwrap};\nuse embassy_executor::Spawner;\nuse embassy_nrf::gpio::{Level, Output, OutputDrive};\nuse embassy_nrf::{bind_interrupts, peripherals, spim};\nuse {defmt_rtt as _, panic_probe as _};\n\nbind_interrupts!(struct Irqs {\n    SPIM3 => spim::InterruptHandler<peripherals::SPI3>;\n});\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let p = embassy_nrf::init(Default::default());\n    info!(\"running!\");\n\n    let mut config = spim::Config::default();\n    config.frequency = spim::Frequency::M16;\n\n    let mut spim = spim::Spim::new(p.SPI3, Irqs, p.P0_29, p.P0_28, p.P0_30, config);\n\n    let mut ncs = Output::new(p.P0_31, Level::High, OutputDrive::Standard);\n\n    // Example on how to talk to an ENC28J60 chip\n\n    // softreset\n    cortex_m::asm::delay(10);\n    ncs.set_low();\n    cortex_m::asm::delay(5);\n    let tx = [0xFF];\n    unwrap!(spim.transfer(&mut [], &tx).await);\n    cortex_m::asm::delay(10);\n    ncs.set_high();\n\n    cortex_m::asm::delay(100000);\n\n    let mut rx = [0; 2];\n\n    // read ESTAT\n    cortex_m::asm::delay(5000);\n    ncs.set_low();\n    cortex_m::asm::delay(5000);\n    let tx = [0b000_11101, 0];\n    unwrap!(spim.transfer(&mut rx, &tx).await);\n    cortex_m::asm::delay(5000);\n    ncs.set_high();\n    info!(\"estat: {=[?]}\", rx);\n\n    // Switch to bank 3\n    cortex_m::asm::delay(10);\n    ncs.set_low();\n    cortex_m::asm::delay(5);\n    let tx = [0b100_11111, 0b11];\n    unwrap!(spim.transfer(&mut rx, &tx).await);\n    cortex_m::asm::delay(10);\n    ncs.set_high();\n\n    // read EREVID\n    cortex_m::asm::delay(10);\n    ncs.set_low();\n    cortex_m::asm::delay(5);\n    let tx = [0b000_10010, 0];\n    unwrap!(spim.transfer(&mut rx, &tx).await);\n    cortex_m::asm::delay(10);\n    ncs.set_high();\n\n    info!(\"erevid: {=[?]}\", rx);\n}\n"
  },
  {
    "path": "examples/nrf52840/src/bin/spis.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse defmt::info;\nuse embassy_executor::Spawner;\nuse embassy_nrf::spis::{Config, Spis};\nuse embassy_nrf::{bind_interrupts, peripherals, spis};\nuse {defmt_rtt as _, panic_probe as _};\n\nbind_interrupts!(struct Irqs {\n    SPI2 => spis::InterruptHandler<peripherals::SPI2>;\n});\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let p = embassy_nrf::init(Default::default());\n    info!(\"Running!\");\n\n    let mut spis = Spis::new(p.SPI2, Irqs, p.P0_31, p.P0_29, p.P0_28, p.P0_30, Config::default());\n\n    loop {\n        let mut rx_buf = [0_u8; 64];\n        let tx_buf = [1_u8, 2, 3, 4, 5, 6, 7, 8];\n        if let Ok((n_rx, n_tx)) = spis.transfer(&mut rx_buf, &tx_buf).await {\n            info!(\"RX: {:?}\", rx_buf[..n_rx]);\n            info!(\"TX: {:?}\", tx_buf[..n_tx]);\n        }\n    }\n}\n"
  },
  {
    "path": "examples/nrf52840/src/bin/temp.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse defmt::info;\nuse embassy_executor::Spawner;\nuse embassy_nrf::temp::Temp;\nuse embassy_nrf::{bind_interrupts, temp};\nuse embassy_time::Timer;\nuse {defmt_rtt as _, panic_probe as _};\n\nbind_interrupts!(struct Irqs {\n    TEMP => temp::InterruptHandler;\n});\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let p = embassy_nrf::init(Default::default());\n    let mut temp = Temp::new(p.TEMP, Irqs);\n\n    loop {\n        let value = temp.read().await;\n        info!(\"temperature: {}℃\", value.to_num::<u16>());\n        Timer::after_secs(1).await;\n    }\n}\n"
  },
  {
    "path": "examples/nrf52840/src/bin/timer.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse defmt::{info, unwrap};\nuse embassy_executor::Spawner;\nuse embassy_time::Timer;\nuse {defmt_rtt as _, panic_probe as _};\n\n#[embassy_executor::task]\nasync fn run1() {\n    loop {\n        info!(\"BIG INFREQUENT TICK\");\n        Timer::after_ticks(64000).await;\n    }\n}\n\n#[embassy_executor::task]\nasync fn run2() {\n    loop {\n        info!(\"tick\");\n        Timer::after_ticks(13000).await;\n    }\n}\n\n#[embassy_executor::main]\nasync fn main(spawner: Spawner) {\n    let _p = embassy_nrf::init(Default::default());\n    spawner.spawn(unwrap!(run1()));\n    spawner.spawn(unwrap!(run2()));\n}\n"
  },
  {
    "path": "examples/nrf52840/src/bin/twim.rs",
    "content": "//! Example on how to read a 24C/24LC i2c eeprom.\n//!\n//! Connect SDA to P0.03, SCL to P0.04\n\n#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_nrf::twim::{self, Twim};\nuse embassy_nrf::{bind_interrupts, peripherals};\nuse static_cell::ConstStaticCell;\nuse {defmt_rtt as _, panic_probe as _};\n\nconst ADDRESS: u8 = 0x50;\n\nbind_interrupts!(struct Irqs {\n    TWISPI0 => twim::InterruptHandler<peripherals::TWISPI0>;\n});\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let p = embassy_nrf::init(Default::default());\n    info!(\"Initializing TWI...\");\n    let config = twim::Config::default();\n    static RAM_BUFFER: ConstStaticCell<[u8; 16]> = ConstStaticCell::new([0; 16]);\n    let mut twi = Twim::new(p.TWISPI0, Irqs, p.P0_03, p.P0_04, config, RAM_BUFFER.take());\n\n    info!(\"Reading...\");\n\n    let mut buf = [0u8; 16];\n    unwrap!(twi.blocking_write_read(ADDRESS, &mut [0x00], &mut buf));\n\n    info!(\"Read: {=[u8]:x}\", buf);\n}\n"
  },
  {
    "path": "examples/nrf52840/src/bin/twim_lowpower.rs",
    "content": "//! Example on how to read a 24C/24LC i2c eeprom with low power consumption.\n//! The eeprom is read every 1 second, while ensuring lowest possible power while\n//! sleeping between reads.\n//!\n//! Connect SDA to P0.03, SCL to P0.04\n\n#![no_std]\n#![no_main]\n\nuse core::mem;\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_nrf::twim::{self, Twim};\nuse embassy_nrf::{bind_interrupts, peripherals};\nuse embassy_time::Timer;\nuse {defmt_rtt as _, panic_probe as _};\n\nconst ADDRESS: u8 = 0x50;\n\nbind_interrupts!(struct Irqs {\n    TWISPI0 => twim::InterruptHandler<peripherals::TWISPI0>;\n});\n\n#[embassy_executor::main]\nasync fn main(_p: Spawner) {\n    let mut p = embassy_nrf::init(Default::default());\n    info!(\"Started!\");\n\n    loop {\n        info!(\"Initializing TWI...\");\n        let config = twim::Config::default();\n        let mut ram_buffer = [0u8; 16];\n\n        // Create the TWIM instance with borrowed singletons, so they're not consumed.\n        let mut twi = Twim::new(\n            p.TWISPI0.reborrow(),\n            Irqs,\n            p.P0_03.reborrow(),\n            p.P0_04.reborrow(),\n            config,\n            &mut ram_buffer,\n        );\n\n        info!(\"Reading...\");\n\n        let mut buf = [0u8; 16];\n        unwrap!(twi.blocking_write_read(ADDRESS, &mut [0x00], &mut buf));\n\n        info!(\"Read: {=[u8]:x}\", buf);\n\n        // Drop the TWIM instance. This disables the peripehral and deconfigures the pins.\n        // This clears the borrow on the singletons, so they can now be used again.\n        mem::drop(twi);\n\n        // Sleep for 1 second. The executor ensures the core sleeps with a WFE when it has nothing to do.\n        // During this sleep, the nRF chip should only use ~3uA\n        Timer::after_secs(1).await;\n    }\n}\n"
  },
  {
    "path": "examples/nrf52840/src/bin/twis.rs",
    "content": "//! TWIS example\n\n#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_nrf::twis::{self, Command, Twis};\nuse embassy_nrf::{bind_interrupts, peripherals};\nuse {defmt_rtt as _, panic_probe as _};\n\nbind_interrupts!(struct Irqs {\n    TWISPI0 => twis::InterruptHandler<peripherals::TWISPI0>;\n});\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let p = embassy_nrf::init(Default::default());\n\n    let mut config = twis::Config::default();\n    config.address0 = 0x55; // Set i2c address\n    let mut i2c = Twis::new(p.TWISPI0, Irqs, p.P0_03, p.P0_04, config);\n\n    info!(\"Listening...\");\n    loop {\n        let response = [1, 2, 3, 4, 5, 6, 7, 8];\n        // This buffer is used if the i2c master performs a Write or WriteRead\n        let mut buf = [0u8; 16];\n        match i2c.listen(&mut buf).await {\n            Ok(Command::Read) => {\n                info!(\"Got READ command. Respond with data:\\n{:?}\\n\", response);\n                if let Err(e) = i2c.respond_to_read(&response).await {\n                    error!(\"{:?}\", e);\n                }\n            }\n            Ok(Command::Write(n)) => info!(\"Got WRITE command with data:\\n{:?}\\n\", buf[..n]),\n            Ok(Command::WriteRead(n)) => {\n                info!(\"Got WRITE/READ command with data:\\n{:?}\", buf[..n]);\n                info!(\"Respond with data:\\n{:?}\\n\", response);\n                if let Err(e) = i2c.respond_to_read(&response).await {\n                    error!(\"{:?}\", e);\n                }\n            }\n            Err(e) => error!(\"{:?}\", e),\n        }\n    }\n}\n"
  },
  {
    "path": "examples/nrf52840/src/bin/uart.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_nrf::{bind_interrupts, peripherals, uarte};\nuse {defmt_rtt as _, panic_probe as _};\n\nbind_interrupts!(struct Irqs {\n    UARTE0 => uarte::InterruptHandler<peripherals::UARTE0>;\n});\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let p = embassy_nrf::init(Default::default());\n    let mut config = uarte::Config::default();\n    config.parity = uarte::Parity::EXCLUDED;\n    config.baudrate = uarte::Baudrate::BAUD115200;\n\n    let mut uart = uarte::Uarte::new(p.UARTE0, p.P0_08, p.P0_06, Irqs, config);\n\n    info!(\"uarte initialized!\");\n\n    // Message must be in SRAM\n    let mut buf = [0; 8];\n    buf.copy_from_slice(b\"Hello!\\r\\n\");\n\n    unwrap!(uart.write(&buf).await);\n    info!(\"wrote hello in uart!\");\n\n    loop {\n        info!(\"reading...\");\n        unwrap!(uart.read(&mut buf).await);\n        info!(\"writing...\");\n        unwrap!(uart.write(&buf).await);\n    }\n}\n"
  },
  {
    "path": "examples/nrf52840/src/bin/uart_idle.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_nrf::peripherals::UARTE0;\nuse embassy_nrf::{bind_interrupts, uarte};\nuse {defmt_rtt as _, panic_probe as _};\n\nbind_interrupts!(struct Irqs {\n    UARTE0 => uarte::InterruptHandler<UARTE0>;\n});\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let p = embassy_nrf::init(Default::default());\n    let mut config = uarte::Config::default();\n    config.parity = uarte::Parity::EXCLUDED;\n    config.baudrate = uarte::Baudrate::BAUD115200;\n\n    let uart = uarte::Uarte::new(p.UARTE0, p.P0_08, p.P0_06, Irqs, config);\n    let (mut tx, mut rx) = uart.split_with_idle(p.TIMER0, p.PPI_CH0, p.PPI_CH1);\n\n    info!(\"uarte initialized!\");\n\n    // Message must be in SRAM\n    let mut buf = [0; 8];\n    buf.copy_from_slice(b\"Hello!\\r\\n\");\n\n    unwrap!(tx.write(&buf).await);\n    info!(\"wrote hello in uart!\");\n\n    loop {\n        info!(\"reading...\");\n        let n = unwrap!(rx.read_until_idle(&mut buf).await);\n        info!(\"got {} bytes\", n);\n    }\n}\n"
  },
  {
    "path": "examples/nrf52840/src/bin/uart_split.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_nrf::peripherals::UARTE0;\nuse embassy_nrf::uarte::UarteRx;\nuse embassy_nrf::{bind_interrupts, uarte};\nuse embassy_sync::blocking_mutex::raw::ThreadModeRawMutex;\nuse embassy_sync::channel::Channel;\nuse {defmt_rtt as _, panic_probe as _};\n\nstatic CHANNEL: Channel<ThreadModeRawMutex, [u8; 8], 1> = Channel::new();\n\nbind_interrupts!(struct Irqs {\n    UARTE0 => uarte::InterruptHandler<UARTE0>;\n});\n\n#[embassy_executor::main]\nasync fn main(spawner: Spawner) {\n    let p = embassy_nrf::init(Default::default());\n    let mut config = uarte::Config::default();\n    config.parity = uarte::Parity::EXCLUDED;\n    config.baudrate = uarte::Baudrate::BAUD115200;\n\n    let uart = uarte::Uarte::new(p.UARTE0, p.P0_08, p.P0_06, Irqs, config);\n    let (mut tx, rx) = uart.split();\n\n    info!(\"uarte initialized!\");\n\n    // Spawn a task responsible purely for reading\n\n    spawner.spawn(unwrap!(reader(rx)));\n\n    // Message must be in SRAM\n    {\n        let mut buf = [0; 23];\n        buf.copy_from_slice(b\"Type 8 chars to echo!\\r\\n\");\n\n        unwrap!(tx.write(&buf).await);\n        info!(\"wrote hello in uart!\");\n    }\n\n    // Continue reading in this main task and write\n    // back out the buffer we receive from the read\n    // task.\n    loop {\n        let buf = CHANNEL.receive().await;\n        info!(\"writing...\");\n        unwrap!(tx.write(&buf).await);\n    }\n}\n\n#[embassy_executor::task]\nasync fn reader(mut rx: UarteRx<'static>) {\n    let mut buf = [0; 8];\n    loop {\n        info!(\"reading...\");\n        unwrap!(rx.read(&mut buf).await);\n        CHANNEL.send(buf).await;\n    }\n}\n"
  },
  {
    "path": "examples/nrf52840/src/bin/usb_ethernet.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_net::StackResources;\nuse embassy_net::tcp::TcpSocket;\nuse embassy_nrf::rng::Rng;\nuse embassy_nrf::usb::Driver;\nuse embassy_nrf::usb::vbus_detect::HardwareVbusDetect;\nuse embassy_nrf::{bind_interrupts, pac, peripherals, rng, usb};\nuse embassy_usb::class::cdc_ncm::embassy_net::{Device, Runner, State as NetState};\nuse embassy_usb::class::cdc_ncm::{CdcNcmClass, State};\nuse embassy_usb::{Builder, Config, UsbDevice};\nuse embedded_io_async::Write;\nuse static_cell::StaticCell;\nuse {defmt_rtt as _, panic_probe as _};\n\nbind_interrupts!(struct Irqs {\n    USBD => usb::InterruptHandler<peripherals::USBD>;\n    CLOCK_POWER => usb::vbus_detect::InterruptHandler;\n    RNG => rng::InterruptHandler<peripherals::RNG>;\n});\n\ntype MyDriver = Driver<'static, HardwareVbusDetect>;\n\nconst MTU: usize = 1514;\n\n#[embassy_executor::task]\nasync fn usb_task(mut device: UsbDevice<'static, MyDriver>) -> ! {\n    device.run().await\n}\n\n#[embassy_executor::task]\nasync fn usb_ncm_task(class: Runner<'static, MyDriver, MTU>) -> ! {\n    class.run().await\n}\n\n#[embassy_executor::task]\nasync fn net_task(mut runner: embassy_net::Runner<'static, Device<'static, MTU>>) -> ! {\n    runner.run().await\n}\n\n#[embassy_executor::main]\nasync fn main(spawner: Spawner) {\n    let p = embassy_nrf::init(Default::default());\n\n    info!(\"Enabling ext hfosc...\");\n    pac::CLOCK.tasks_hfclkstart().write_value(1);\n    while pac::CLOCK.events_hfclkstarted().read() != 1 {}\n\n    // Create the driver, from the HAL.\n    let driver = Driver::new(p.USBD, Irqs, HardwareVbusDetect::new(Irqs));\n\n    // Create embassy-usb Config\n    let mut config = Config::new(0xc0de, 0xcafe);\n    config.manufacturer = Some(\"Embassy\");\n    config.product = Some(\"USB-Ethernet example\");\n    config.serial_number = Some(\"12345678\");\n    config.max_power = 100;\n    config.max_packet_size_0 = 64;\n\n    // Create embassy-usb DeviceBuilder using the driver and config.\n    static CONFIG_DESC: StaticCell<[u8; 256]> = StaticCell::new();\n    static BOS_DESC: StaticCell<[u8; 256]> = StaticCell::new();\n    static MSOS_DESC: StaticCell<[u8; 128]> = StaticCell::new();\n    static CONTROL_BUF: StaticCell<[u8; 128]> = StaticCell::new();\n    let mut builder = Builder::new(\n        driver,\n        config,\n        &mut CONFIG_DESC.init([0; 256])[..],\n        &mut BOS_DESC.init([0; 256])[..],\n        &mut MSOS_DESC.init([0; 128])[..],\n        &mut CONTROL_BUF.init([0; 128])[..],\n    );\n\n    // Our MAC addr.\n    let our_mac_addr = [0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC];\n    // Host's MAC addr. This is the MAC the host \"thinks\" its USB-to-ethernet adapter has.\n    let host_mac_addr = [0x88, 0x88, 0x88, 0x88, 0x88, 0x88];\n\n    // Create classes on the builder.\n    static STATE: StaticCell<State> = StaticCell::new();\n    let class = CdcNcmClass::new(&mut builder, STATE.init(State::new()), host_mac_addr, 64);\n\n    // Build the builder.\n    let usb = builder.build();\n\n    spawner.spawn(unwrap!(usb_task(usb)));\n\n    static NET_STATE: StaticCell<NetState<MTU, 4, 4>> = StaticCell::new();\n    let (runner, device) = class.into_embassy_net_device::<MTU, 4, 4>(NET_STATE.init(NetState::new()), our_mac_addr);\n    spawner.spawn(unwrap!(usb_ncm_task(runner)));\n\n    let config = embassy_net::Config::dhcpv4(Default::default());\n    // let config = embassy_net::Config::ipv4_static(embassy_net::StaticConfigV4 {\n    //    address: Ipv4Cidr::new(Ipv4Address::new(10, 42, 0, 61), 24),\n    //    dns_servers: Vec::new(),\n    //    gateway: Some(Ipv4Address::new(10, 42, 0, 1)),\n    // });\n\n    // Generate random seed\n    let mut rng = Rng::new(p.RNG, Irqs);\n    let mut seed = [0; 8];\n    rng.blocking_fill_bytes(&mut seed);\n    let seed = u64::from_le_bytes(seed);\n\n    // Init network stack\n    static RESOURCES: StaticCell<StackResources<3>> = StaticCell::new();\n    let (stack, runner) = embassy_net::new(device, config, RESOURCES.init(StackResources::new()), seed);\n\n    spawner.spawn(unwrap!(net_task(runner)));\n\n    // And now we can use it!\n\n    let mut rx_buffer = [0; 4096];\n    let mut tx_buffer = [0; 4096];\n    let mut buf = [0; 4096];\n\n    loop {\n        let mut socket = TcpSocket::new(stack, &mut rx_buffer, &mut tx_buffer);\n        socket.set_timeout(Some(embassy_time::Duration::from_secs(10)));\n\n        info!(\"Listening on TCP:1234...\");\n        if let Err(e) = socket.accept(1234).await {\n            warn!(\"accept error: {:?}\", e);\n            continue;\n        }\n\n        info!(\"Received connection from {:?}\", socket.remote_endpoint());\n\n        loop {\n            let n = match socket.read(&mut buf).await {\n                Ok(0) => {\n                    warn!(\"read EOF\");\n                    break;\n                }\n                Ok(n) => n,\n                Err(e) => {\n                    warn!(\"read error: {:?}\", e);\n                    break;\n                }\n            };\n\n            info!(\"rxd {:02x}\", &buf[..n]);\n\n            match socket.write_all(&buf[..n]).await {\n                Ok(()) => {}\n                Err(e) => {\n                    warn!(\"write error: {:?}\", e);\n                    break;\n                }\n            };\n        }\n    }\n}\n"
  },
  {
    "path": "examples/nrf52840/src/bin/usb_hid_keyboard.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse core::sync::atomic::{AtomicBool, AtomicU8, Ordering};\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_futures::join::join;\nuse embassy_futures::select::{Either, select};\nuse embassy_nrf::gpio::{Input, Pull};\nuse embassy_nrf::usb::Driver;\nuse embassy_nrf::usb::vbus_detect::HardwareVbusDetect;\nuse embassy_nrf::{bind_interrupts, pac, peripherals, usb};\nuse embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;\nuse embassy_sync::signal::Signal;\nuse embassy_usb::class::hid::{\n    HidBootProtocol, HidProtocolMode, HidReaderWriter, HidSubclass, ReportId, RequestHandler, State,\n};\nuse embassy_usb::control::OutResponse;\nuse embassy_usb::{Builder, Config, Handler};\nuse usbd_hid::descriptor::{KeyboardReport, SerializedDescriptor};\nuse {defmt_rtt as _, panic_probe as _};\n\nbind_interrupts!(struct Irqs {\n    USBD => usb::InterruptHandler<peripherals::USBD>;\n    CLOCK_POWER => usb::vbus_detect::InterruptHandler;\n});\n\nstatic SUSPENDED: AtomicBool = AtomicBool::new(false);\n\nstatic HID_PROTOCOL_MODE: AtomicU8 = AtomicU8::new(HidProtocolMode::Boot as u8);\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let p = embassy_nrf::init(Default::default());\n\n    info!(\"Enabling ext hfosc...\");\n    pac::CLOCK.tasks_hfclkstart().write_value(1);\n    while pac::CLOCK.events_hfclkstarted().read() != 1 {}\n\n    // Create the driver, from the HAL.\n    let driver = Driver::new(p.USBD, Irqs, HardwareVbusDetect::new(Irqs));\n\n    // Create embassy-usb Config\n    let mut config = Config::new(0xc0de, 0xcafe);\n    config.manufacturer = Some(\"Embassy\");\n    config.product = Some(\"HID keyboard example\");\n    config.serial_number = Some(\"12345678\");\n    config.max_power = 100;\n    config.max_packet_size_0 = 64;\n    config.supports_remote_wakeup = true;\n    config.composite_with_iads = false;\n    config.device_class = 0;\n    config.device_sub_class = 0;\n    config.device_protocol = 0;\n\n    // Create embassy-usb DeviceBuilder using the driver and config.\n    // It needs some buffers for building the descriptors.\n    let mut config_descriptor = [0; 256];\n    let mut bos_descriptor = [0; 256];\n    let mut msos_descriptor = [0; 256];\n    let mut control_buf = [0; 64];\n    let mut request_handler = MyRequestHandler {};\n    let mut device_handler = MyDeviceHandler::new();\n\n    let mut state = State::new();\n\n    let mut builder = Builder::new(\n        driver,\n        config,\n        &mut config_descriptor,\n        &mut bos_descriptor,\n        &mut msos_descriptor,\n        &mut control_buf,\n    );\n\n    builder.handler(&mut device_handler);\n\n    // Create classes on the builder.\n    let config = embassy_usb::class::hid::Config {\n        report_descriptor: KeyboardReport::desc(),\n        request_handler: None,\n        poll_ms: 60,\n        max_packet_size: 64,\n        hid_subclass: HidSubclass::Boot,\n        hid_boot_protocol: HidBootProtocol::Keyboard,\n    };\n    let hid = HidReaderWriter::<_, 1, 8>::new(&mut builder, &mut state, config);\n\n    // Build the builder.\n    let mut usb = builder.build();\n\n    let remote_wakeup: Signal<CriticalSectionRawMutex, _> = Signal::new();\n\n    // Run the USB device.\n    let usb_fut = async {\n        loop {\n            usb.run_until_suspend().await;\n            match select(usb.wait_resume(), remote_wakeup.wait()).await {\n                Either::First(_) => (),\n                Either::Second(_) => unwrap!(usb.remote_wakeup().await),\n            }\n        }\n    };\n\n    let mut button = Input::new(p.P0_11, Pull::Up);\n\n    let (reader, mut writer) = hid.split();\n\n    // Do stuff with the class!\n    let in_fut = async {\n        loop {\n            button.wait_for_low().await;\n            info!(\"PRESSED\");\n\n            if SUSPENDED.load(Ordering::Acquire) {\n                info!(\"Triggering remote wakeup\");\n                remote_wakeup.signal(());\n            } else if HID_PROTOCOL_MODE.load(Ordering::Relaxed) == HidProtocolMode::Boot as u8 {\n                match writer.write(&[0, 0, 4, 0, 0, 0, 0, 0]).await {\n                    Ok(()) => {}\n                    Err(e) => warn!(\"Failed to send boot report: {:?}\", e),\n                };\n            } else {\n                let report = KeyboardReport {\n                    keycodes: [4, 0, 0, 0, 0, 0],\n                    leds: 0,\n                    modifier: 0,\n                    reserved: 0,\n                };\n                match writer.write_serialize(&report).await {\n                    Ok(()) => {}\n                    Err(e) => warn!(\"Failed to send report: {:?}\", e),\n                };\n            }\n\n            button.wait_for_high().await;\n            info!(\"RELEASED\");\n            if HID_PROTOCOL_MODE.load(Ordering::Relaxed) == HidProtocolMode::Boot as u8 {\n                match writer.write(&[0, 0, 0, 0, 0, 0, 0, 0]).await {\n                    Ok(()) => {}\n                    Err(e) => warn!(\"Failed to send boot report: {:?}\", e),\n                };\n            } else {\n                let report = KeyboardReport {\n                    keycodes: [0, 0, 0, 0, 0, 0],\n                    leds: 0,\n                    modifier: 0,\n                    reserved: 0,\n                };\n                match writer.write_serialize(&report).await {\n                    Ok(()) => {}\n                    Err(e) => warn!(\"Failed to send report: {:?}\", e),\n                };\n            }\n        }\n    };\n\n    let out_fut = async {\n        reader.run(false, &mut request_handler).await;\n    };\n\n    // Run everything concurrently.\n    // If we had made everything `'static` above instead, we could do this using separate tasks instead.\n    join(usb_fut, join(in_fut, out_fut)).await;\n}\n\nstruct MyRequestHandler {}\n\nimpl RequestHandler for MyRequestHandler {\n    fn get_report(&mut self, id: ReportId, _buf: &mut [u8]) -> Option<usize> {\n        info!(\"Get report for {:?}\", id);\n        None\n    }\n\n    fn set_report(&mut self, id: ReportId, data: &[u8]) -> OutResponse {\n        info!(\"Set report for {:?}: {=[u8]}\", id, data);\n        OutResponse::Accepted\n    }\n\n    fn get_protocol(&self) -> HidProtocolMode {\n        let protocol = HidProtocolMode::from(HID_PROTOCOL_MODE.load(Ordering::Relaxed));\n        info!(\"The current HID protocol mode is: {}\", protocol);\n        protocol\n    }\n\n    fn set_protocol(&mut self, protocol: HidProtocolMode) -> OutResponse {\n        info!(\"Switching to HID protocol mode: {}\", protocol);\n        HID_PROTOCOL_MODE.store(protocol as u8, Ordering::Relaxed);\n        OutResponse::Accepted\n    }\n\n    fn set_idle_ms(&mut self, id: Option<ReportId>, dur: u32) {\n        info!(\"Set idle rate for {:?} to {:?}\", id, dur);\n    }\n\n    fn get_idle_ms(&mut self, id: Option<ReportId>) -> Option<u32> {\n        info!(\"Get idle rate for {:?}\", id);\n        None\n    }\n}\n\nstruct MyDeviceHandler {\n    configured: AtomicBool,\n}\n\nimpl MyDeviceHandler {\n    fn new() -> Self {\n        MyDeviceHandler {\n            configured: AtomicBool::new(false),\n        }\n    }\n}\n\nimpl Handler for MyDeviceHandler {\n    fn enabled(&mut self, enabled: bool) {\n        self.configured.store(false, Ordering::Relaxed);\n        SUSPENDED.store(false, Ordering::Release);\n        if enabled {\n            info!(\"Device enabled\");\n        } else {\n            info!(\"Device disabled\");\n        }\n    }\n\n    fn reset(&mut self) {\n        self.configured.store(false, Ordering::Relaxed);\n        info!(\"Bus reset, the Vbus current limit is 100mA\");\n    }\n\n    fn addressed(&mut self, addr: u8) {\n        self.configured.store(false, Ordering::Relaxed);\n        info!(\"USB address set to: {}\", addr);\n    }\n\n    fn configured(&mut self, configured: bool) {\n        self.configured.store(configured, Ordering::Relaxed);\n        if configured {\n            info!(\"Device configured, it may now draw up to the configured current limit from Vbus.\")\n        } else {\n            info!(\"Device is no longer configured, the Vbus current limit is 100mA.\");\n        }\n    }\n\n    fn suspended(&mut self, suspended: bool) {\n        if suspended {\n            info!(\n                \"Device suspended, the Vbus current limit is 500µA (or 2.5mA for high-power devices with remote wakeup enabled).\"\n            );\n            SUSPENDED.store(true, Ordering::Release);\n        } else {\n            SUSPENDED.store(false, Ordering::Release);\n            if self.configured.load(Ordering::Relaxed) {\n                info!(\"Device resumed, it may now draw up to the configured current limit from Vbus\");\n            } else {\n                info!(\"Device resumed, the Vbus current limit is 100mA\");\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "examples/nrf52840/src/bin/usb_hid_mouse.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse core::sync::atomic::{AtomicU8, Ordering};\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_futures::join::join;\nuse embassy_nrf::usb::Driver;\nuse embassy_nrf::usb::vbus_detect::HardwareVbusDetect;\nuse embassy_nrf::{bind_interrupts, pac, peripherals, usb};\nuse embassy_time::Timer;\nuse embassy_usb::class::hid::{\n    HidBootProtocol, HidProtocolMode, HidSubclass, HidWriter, ReportId, RequestHandler, State,\n};\nuse embassy_usb::control::OutResponse;\nuse embassy_usb::{Builder, Config};\nuse usbd_hid::descriptor::{MouseReport, SerializedDescriptor};\nuse {defmt_rtt as _, panic_probe as _};\n\nbind_interrupts!(struct Irqs {\n    USBD => usb::InterruptHandler<peripherals::USBD>;\n    CLOCK_POWER => usb::vbus_detect::InterruptHandler;\n});\n\nstatic HID_PROTOCOL_MODE: AtomicU8 = AtomicU8::new(HidProtocolMode::Boot as u8);\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let p = embassy_nrf::init(Default::default());\n\n    info!(\"Enabling ext hfosc...\");\n    pac::CLOCK.tasks_hfclkstart().write_value(1);\n    while pac::CLOCK.events_hfclkstarted().read() != 1 {}\n\n    // Create the driver, from the HAL.\n    let driver = Driver::new(p.USBD, Irqs, HardwareVbusDetect::new(Irqs));\n\n    // Create embassy-usb Config\n    let mut config = Config::new(0xc0de, 0xcafe);\n    config.manufacturer = Some(\"Embassy\");\n    config.product = Some(\"HID mouse example\");\n    config.serial_number = Some(\"12345678\");\n    config.max_power = 100;\n    config.max_packet_size_0 = 64;\n    config.composite_with_iads = false;\n    config.device_class = 0;\n    config.device_sub_class = 0;\n    config.device_protocol = 0;\n\n    // Create embassy-usb DeviceBuilder using the driver and config.\n    // It needs some buffers for building the descriptors.\n    let mut config_descriptor = [0; 256];\n    let mut bos_descriptor = [0; 256];\n    let mut msos_descriptor = [0; 256];\n    let mut control_buf = [0; 64];\n    let mut request_handler = MyRequestHandler {};\n\n    let mut state = State::new();\n\n    let mut builder = Builder::new(\n        driver,\n        config,\n        &mut config_descriptor,\n        &mut bos_descriptor,\n        &mut msos_descriptor,\n        &mut control_buf,\n    );\n\n    // Create classes on the builder.\n    let config = embassy_usb::class::hid::Config {\n        report_descriptor: MouseReport::desc(),\n        request_handler: Some(&mut request_handler),\n        poll_ms: 60,\n        max_packet_size: 8,\n        hid_subclass: HidSubclass::Boot,\n        hid_boot_protocol: HidBootProtocol::Mouse,\n    };\n\n    let mut writer = HidWriter::<_, 5>::new(&mut builder, &mut state, config);\n\n    // Build the builder.\n    let mut usb = builder.build();\n\n    // Run the USB device.\n    let usb_fut = usb.run();\n\n    // Do stuff with the class!\n    let hid_fut = async {\n        let mut y: i8 = 5;\n        loop {\n            Timer::after_millis(500).await;\n\n            y = -y;\n\n            if HID_PROTOCOL_MODE.load(Ordering::Relaxed) == HidProtocolMode::Boot as u8 {\n                let buttons = 0u8;\n                let x = 0i8;\n                match writer.write(&[buttons, x as u8, y as u8]).await {\n                    Ok(()) => {}\n                    Err(e) => warn!(\"Failed to send boot report: {:?}\", e),\n                }\n            } else {\n                let report = MouseReport {\n                    buttons: 0,\n                    x: 0,\n                    y,\n                    wheel: 0,\n                    pan: 0,\n                };\n                match writer.write_serialize(&report).await {\n                    Ok(()) => {}\n                    Err(e) => warn!(\"Failed to send report: {:?}\", e),\n                }\n            }\n        }\n    };\n\n    // Run everything concurrently.\n    // If we had made everything `'static` above instead, we could do this using separate tasks instead.\n    join(usb_fut, hid_fut).await;\n}\n\nstruct MyRequestHandler {}\n\nimpl RequestHandler for MyRequestHandler {\n    fn get_report(&mut self, id: ReportId, _buf: &mut [u8]) -> Option<usize> {\n        info!(\"Get report for {:?}\", id);\n        None\n    }\n\n    fn set_report(&mut self, id: ReportId, data: &[u8]) -> OutResponse {\n        info!(\"Set report for {:?}: {=[u8]}\", id, data);\n        OutResponse::Accepted\n    }\n\n    fn get_protocol(&self) -> HidProtocolMode {\n        let protocol = HidProtocolMode::from(HID_PROTOCOL_MODE.load(Ordering::Relaxed));\n        info!(\"The current HID protocol mode is: {}\", protocol);\n        protocol\n    }\n\n    fn set_protocol(&mut self, protocol: HidProtocolMode) -> OutResponse {\n        info!(\"Switching to HID protocol mode: {}\", protocol);\n        HID_PROTOCOL_MODE.store(protocol as u8, Ordering::Relaxed);\n        OutResponse::Accepted\n    }\n\n    fn set_idle_ms(&mut self, id: Option<ReportId>, dur: u32) {\n        info!(\"Set idle rate for {:?} to {:?}\", id, dur);\n    }\n\n    fn get_idle_ms(&mut self, id: Option<ReportId>) -> Option<u32> {\n        info!(\"Get idle rate for {:?}\", id);\n        None\n    }\n}\n"
  },
  {
    "path": "examples/nrf52840/src/bin/usb_serial.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse defmt::{info, panic};\nuse embassy_executor::Spawner;\nuse embassy_futures::join::join;\nuse embassy_nrf::usb::Driver;\nuse embassy_nrf::usb::vbus_detect::{HardwareVbusDetect, VbusDetect};\nuse embassy_nrf::{bind_interrupts, pac, peripherals, usb};\nuse embassy_usb::class::cdc_acm::{CdcAcmClass, State};\nuse embassy_usb::driver::EndpointError;\nuse embassy_usb::{Builder, Config};\nuse {defmt_rtt as _, panic_probe as _};\n\nbind_interrupts!(struct Irqs {\n    USBD => usb::InterruptHandler<peripherals::USBD>;\n    CLOCK_POWER => usb::vbus_detect::InterruptHandler;\n});\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let p = embassy_nrf::init(Default::default());\n\n    info!(\"Enabling ext hfosc...\");\n    pac::CLOCK.tasks_hfclkstart().write_value(1);\n    while pac::CLOCK.events_hfclkstarted().read() != 1 {}\n\n    // Create the driver, from the HAL.\n    let driver = Driver::new(p.USBD, Irqs, HardwareVbusDetect::new(Irqs));\n\n    // Create embassy-usb Config\n    let mut config = Config::new(0xc0de, 0xcafe);\n    config.manufacturer = Some(\"Embassy\");\n    config.product = Some(\"USB-serial example\");\n    config.serial_number = Some(\"12345678\");\n    config.max_power = 100;\n    config.max_packet_size_0 = 64;\n\n    // Create embassy-usb DeviceBuilder using the driver and config.\n    // It needs some buffers for building the descriptors.\n    let mut config_descriptor = [0; 256];\n    let mut bos_descriptor = [0; 256];\n    let mut msos_descriptor = [0; 256];\n    let mut control_buf = [0; 64];\n\n    let mut state = State::new();\n\n    let mut builder = Builder::new(\n        driver,\n        config,\n        &mut config_descriptor,\n        &mut bos_descriptor,\n        &mut msos_descriptor,\n        &mut control_buf,\n    );\n\n    // Create classes on the builder.\n    let mut class = CdcAcmClass::new(&mut builder, &mut state, 64);\n\n    // Build the builder.\n    let mut usb = builder.build();\n\n    // Run the USB device.\n    let usb_fut = usb.run();\n\n    // Do stuff with the class!\n    let echo_fut = async {\n        loop {\n            class.wait_connection().await;\n            info!(\"Connected\");\n            let _ = echo(&mut class).await;\n            info!(\"Disconnected\");\n        }\n    };\n\n    // Run everything concurrently.\n    // If we had made everything `'static` above instead, we could do this using separate tasks instead.\n    join(usb_fut, echo_fut).await;\n}\n\nstruct Disconnected {}\n\nimpl From<EndpointError> for Disconnected {\n    fn from(val: EndpointError) -> Self {\n        match val {\n            EndpointError::BufferOverflow => panic!(\"Buffer overflow\"),\n            EndpointError::Disabled => Disconnected {},\n        }\n    }\n}\n\nasync fn echo<'d, V: VbusDetect + 'd>(class: &mut CdcAcmClass<'d, Driver<'d, V>>) -> Result<(), Disconnected> {\n    let mut buf = [0; 64];\n    loop {\n        let n = class.read_packet(&mut buf).await?;\n        let data = &buf[..n];\n        info!(\"data: {:x}\", data);\n        class.write_packet(data).await?;\n    }\n}\n"
  },
  {
    "path": "examples/nrf52840/src/bin/usb_serial_multitask.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse defmt::{info, panic, unwrap};\nuse embassy_executor::Spawner;\nuse embassy_nrf::usb::Driver;\nuse embassy_nrf::usb::vbus_detect::HardwareVbusDetect;\nuse embassy_nrf::{bind_interrupts, pac, peripherals, usb};\nuse embassy_usb::class::cdc_acm::{CdcAcmClass, State};\nuse embassy_usb::driver::EndpointError;\nuse embassy_usb::{Builder, Config, UsbDevice};\nuse static_cell::StaticCell;\nuse {defmt_rtt as _, panic_probe as _};\n\nbind_interrupts!(struct Irqs {\n    USBD => usb::InterruptHandler<peripherals::USBD>;\n    CLOCK_POWER => usb::vbus_detect::InterruptHandler;\n});\n\ntype MyDriver = Driver<'static, HardwareVbusDetect>;\n\n#[embassy_executor::task]\nasync fn usb_task(mut device: UsbDevice<'static, MyDriver>) {\n    device.run().await;\n}\n\n#[embassy_executor::task]\nasync fn echo_task(mut class: CdcAcmClass<'static, MyDriver>) {\n    loop {\n        class.wait_connection().await;\n        info!(\"Connected\");\n        let _ = echo(&mut class).await;\n        info!(\"Disconnected\");\n    }\n}\n\n#[embassy_executor::main]\nasync fn main(spawner: Spawner) {\n    let p = embassy_nrf::init(Default::default());\n\n    info!(\"Enabling ext hfosc...\");\n    pac::CLOCK.tasks_hfclkstart().write_value(1);\n    while pac::CLOCK.events_hfclkstarted().read() != 1 {}\n\n    // Create the driver, from the HAL.\n    let driver = Driver::new(p.USBD, Irqs, HardwareVbusDetect::new(Irqs));\n\n    // Create embassy-usb Config\n    let mut config = Config::new(0xc0de, 0xcafe);\n    config.manufacturer = Some(\"Embassy\");\n    config.product = Some(\"USB-serial example\");\n    config.serial_number = Some(\"12345678\");\n    config.max_power = 100;\n    config.max_packet_size_0 = 64;\n\n    static STATE: StaticCell<State> = StaticCell::new();\n    let state = STATE.init(State::new());\n\n    // Create embassy-usb DeviceBuilder using the driver and config.\n    static CONFIG_DESC: StaticCell<[u8; 256]> = StaticCell::new();\n    static BOS_DESC: StaticCell<[u8; 256]> = StaticCell::new();\n    static MSOS_DESC: StaticCell<[u8; 128]> = StaticCell::new();\n    static CONTROL_BUF: StaticCell<[u8; 128]> = StaticCell::new();\n    let mut builder = Builder::new(\n        driver,\n        config,\n        &mut CONFIG_DESC.init([0; 256])[..],\n        &mut BOS_DESC.init([0; 256])[..],\n        &mut MSOS_DESC.init([0; 128])[..],\n        &mut CONTROL_BUF.init([0; 128])[..],\n    );\n\n    // Create classes on the builder.\n    let class = CdcAcmClass::new(&mut builder, state, 64);\n\n    // Build the builder.\n    let usb = builder.build();\n\n    spawner.spawn(unwrap!(usb_task(usb)));\n    spawner.spawn(unwrap!(echo_task(class)));\n}\n\nstruct Disconnected {}\n\nimpl From<EndpointError> for Disconnected {\n    fn from(val: EndpointError) -> Self {\n        match val {\n            EndpointError::BufferOverflow => panic!(\"Buffer overflow\"),\n            EndpointError::Disabled => Disconnected {},\n        }\n    }\n}\n\nasync fn echo(class: &mut CdcAcmClass<'static, MyDriver>) -> Result<(), Disconnected> {\n    let mut buf = [0; 64];\n    loop {\n        let n = class.read_packet(&mut buf).await?;\n        let data = &buf[..n];\n        info!(\"data: {:x}\", data);\n        class.write_packet(data).await?;\n    }\n}\n"
  },
  {
    "path": "examples/nrf52840/src/bin/usb_serial_winusb.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse defmt::{info, panic};\nuse embassy_executor::Spawner;\nuse embassy_futures::join::join;\nuse embassy_nrf::usb::Driver;\nuse embassy_nrf::usb::vbus_detect::{HardwareVbusDetect, VbusDetect};\nuse embassy_nrf::{bind_interrupts, pac, peripherals, usb};\nuse embassy_usb::class::cdc_acm::{CdcAcmClass, State};\nuse embassy_usb::driver::EndpointError;\nuse embassy_usb::msos::{self, windows_version};\nuse embassy_usb::types::InterfaceNumber;\nuse embassy_usb::{Builder, Config};\nuse {defmt_rtt as _, panic_probe as _};\n\nbind_interrupts!(struct Irqs {\n    USBD => usb::InterruptHandler<peripherals::USBD>;\n    CLOCK_POWER => usb::vbus_detect::InterruptHandler;\n});\n\n// This is a randomly generated GUID to allow clients on Windows to find our device\nconst DEVICE_INTERFACE_GUIDS: &[&str] = &[\"{EAA9A5DC-30BA-44BC-9232-606CDC875321}\"];\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let p = embassy_nrf::init(Default::default());\n\n    info!(\"Enabling ext hfosc...\");\n    pac::CLOCK.tasks_hfclkstart().write_value(1);\n    while pac::CLOCK.events_hfclkstarted().read() != 1 {}\n\n    // Create the driver, from the HAL.\n    let driver = Driver::new(p.USBD, Irqs, HardwareVbusDetect::new(Irqs));\n\n    // Create embassy-usb Config\n    let mut config = Config::new(0xc0de, 0xcafe);\n    config.manufacturer = Some(\"Embassy\");\n    config.product = Some(\"USB-serial example\");\n    config.serial_number = Some(\"12345678\");\n    config.max_power = 100;\n    config.max_packet_size_0 = 64;\n\n    // Create embassy-usb DeviceBuilder using the driver and config.\n    // It needs some buffers for building the descriptors.\n    let mut config_descriptor = [0; 256];\n    let mut bos_descriptor = [0; 256];\n    let mut msos_descriptor = [0; 256];\n    let mut control_buf = [0; 64];\n\n    let mut state = State::new();\n\n    let mut builder = Builder::new(\n        driver,\n        config,\n        &mut config_descriptor,\n        &mut bos_descriptor,\n        &mut msos_descriptor,\n        &mut control_buf,\n    );\n\n    builder.msos_descriptor(windows_version::WIN8_1, 2);\n\n    // Create classes on the builder.\n    let mut class = CdcAcmClass::new(&mut builder, &mut state, 64);\n\n    // Since we want to create MS OS feature descriptors that apply to a function that has already been added to the\n    // builder, need to get the MsOsDescriptorWriter from the builder and manually add those descriptors.\n    // Inside a class constructor, you would just need to call `FunctionBuilder::msos_feature` instead.\n    let msos_writer = builder.msos_writer();\n    msos_writer.configuration(0);\n    msos_writer.function(InterfaceNumber(0));\n    msos_writer.function_feature(msos::CompatibleIdFeatureDescriptor::new(\"WINUSB\", \"\"));\n    msos_writer.function_feature(msos::RegistryPropertyFeatureDescriptor::new(\n        \"DeviceInterfaceGUIDs\",\n        msos::PropertyData::RegMultiSz(DEVICE_INTERFACE_GUIDS),\n    ));\n\n    // Build the builder.\n    let mut usb = builder.build();\n\n    // Run the USB device.\n    let usb_fut = usb.run();\n\n    // Do stuff with the class!\n    let echo_fut = async {\n        loop {\n            class.wait_connection().await;\n            info!(\"Connected\");\n            let _ = echo(&mut class).await;\n            info!(\"Disconnected\");\n        }\n    };\n\n    // Run everything concurrently.\n    // If we had made everything `'static` above instead, we could do this using separate tasks instead.\n    join(usb_fut, echo_fut).await;\n}\n\nstruct Disconnected {}\n\nimpl From<EndpointError> for Disconnected {\n    fn from(val: EndpointError) -> Self {\n        match val {\n            EndpointError::BufferOverflow => panic!(\"Buffer overflow\"),\n            EndpointError::Disabled => Disconnected {},\n        }\n    }\n}\n\nasync fn echo<'d, V: VbusDetect + 'd>(class: &mut CdcAcmClass<'d, Driver<'d, V>>) -> Result<(), Disconnected> {\n    let mut buf = [0; 64];\n    loop {\n        let n = class.read_packet(&mut buf).await?;\n        let data = &buf[..n];\n        info!(\"data: {:x}\", data);\n        class.write_packet(data).await?;\n    }\n}\n"
  },
  {
    "path": "examples/nrf52840/src/bin/wdt.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_nrf::gpio::{Input, Pull};\nuse embassy_nrf::wdt::{Config, HaltConfig, Watchdog};\nuse {defmt_rtt as _, panic_probe as _};\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let p = embassy_nrf::init(Default::default());\n    info!(\"Hello World!\");\n\n    let mut config = Config::default();\n    config.timeout_ticks = 32768 * 3; // 3 seconds\n\n    // This is needed for `probe-rs run` to be able to catch the panic message\n    // in the WDT interrupt. The core resets 2 ticks after firing the interrupt.\n    config.action_during_debug_halt = HaltConfig::PAUSE;\n\n    let (_wdt, [mut handle]) = match Watchdog::try_new(p.WDT, config) {\n        Ok(x) => x,\n        Err(_) => {\n            info!(\"Watchdog already active with wrong config, waiting for it to timeout...\");\n            loop {}\n        }\n    };\n\n    let mut button = Input::new(p.P0_11, Pull::Up);\n\n    info!(\"Watchdog started, press button 1 to pet it or I'll reset in 3 seconds!\");\n\n    loop {\n        button.wait_for_high().await;\n        button.wait_for_low().await;\n        info!(\"Button pressed, petting watchdog!\");\n        handle.pet();\n    }\n}\n"
  },
  {
    "path": "examples/nrf52840/src/bin/wifi_esp_hosted.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse defmt::{info, unwrap, warn};\nuse embassy_executor::Spawner;\nuse embassy_net::StackResources;\nuse embassy_net::tcp::TcpSocket;\nuse embassy_nrf::gpio::{Input, Level, Output, OutputDrive, Pull};\nuse embassy_nrf::rng::Rng;\nuse embassy_nrf::spim::{self, Spim};\nuse embassy_nrf::{bind_interrupts, peripherals};\nuse embassy_time::Delay;\nuse embedded_hal_bus::spi::ExclusiveDevice;\nuse embedded_io_async::Write;\nuse static_cell::StaticCell;\nuse {defmt_rtt as _, embassy_net_esp_hosted as hosted, panic_probe as _};\n\nconst WIFI_NETWORK: &str = \"EmbassyTest\";\nconst WIFI_PASSWORD: &str = \"V8YxhKt5CdIAJFud\";\n\nbind_interrupts!(struct Irqs {\n    SPIM3 => spim::InterruptHandler<peripherals::SPI3>;\n    RNG => embassy_nrf::rng::InterruptHandler<peripherals::RNG>;\n});\n\n#[embassy_executor::task]\nasync fn wifi_task(\n    runner: hosted::Runner<\n        'static,\n        hosted::SpiInterface<ExclusiveDevice<Spim<'static>, Output<'static>, Delay>, Input<'static>>,\n        Output<'static>,\n    >,\n) -> ! {\n    runner.run().await\n}\n#[embassy_executor::task]\nasync fn net_task(mut runner: embassy_net::Runner<'static, hosted::NetDriver<'static>>) -> ! {\n    runner.run().await\n}\n\n#[embassy_executor::main]\nasync fn main(spawner: Spawner) {\n    info!(\"Hello World!\");\n\n    let p = embassy_nrf::init(Default::default());\n\n    let miso = p.P0_28;\n    let sck = p.P0_29;\n    let mosi = p.P0_30;\n    let cs = Output::new(p.P0_31, Level::High, OutputDrive::HighDrive);\n    let handshake = Input::new(p.P1_01, Pull::Up);\n    let ready = Input::new(p.P1_04, Pull::None);\n    let reset = Output::new(p.P1_05, Level::Low, OutputDrive::Standard);\n\n    let mut config = spim::Config::default();\n    config.frequency = spim::Frequency::M32;\n    config.mode = spim::MODE_2; // !!!\n    let spi = spim::Spim::new(p.SPI3, Irqs, sck, miso, mosi, config);\n    let spi = ExclusiveDevice::new(spi, cs, Delay);\n\n    let iface = hosted::SpiInterface::new(spi, handshake, ready);\n\n    static ESP_STATE: StaticCell<embassy_net_esp_hosted::State> = StaticCell::new();\n    let (device, mut control, runner) =\n        embassy_net_esp_hosted::new(ESP_STATE.init(embassy_net_esp_hosted::State::new()), iface, reset).await;\n\n    spawner.spawn(unwrap!(wifi_task(runner)));\n\n    unwrap!(control.init().await);\n    unwrap!(control.connect(WIFI_NETWORK, WIFI_PASSWORD).await);\n\n    let config = embassy_net::Config::dhcpv4(Default::default());\n    // let config = embassy_net::Config::ipv4_static(embassy_net::StaticConfigV4 {\n    //    address: Ipv4Cidr::new(Ipv4Address::new(10, 42, 0, 61), 24),\n    //    dns_servers: Vec::new(),\n    //    gateway: Some(Ipv4Address::new(10, 42, 0, 1)),\n    // });\n\n    // Generate random seed\n    let mut rng = Rng::new(p.RNG, Irqs);\n    let mut seed = [0; 8];\n    rng.blocking_fill_bytes(&mut seed);\n    let seed = u64::from_le_bytes(seed);\n\n    // Init network stack\n    static RESOURCES: StaticCell<StackResources<3>> = StaticCell::new();\n    let (stack, runner) = embassy_net::new(device, config, RESOURCES.init(StackResources::new()), seed);\n\n    spawner.spawn(unwrap!(net_task(runner)));\n\n    // And now we can use it!\n\n    let mut rx_buffer = [0; 4096];\n    let mut tx_buffer = [0; 4096];\n    let mut buf = [0; 4096];\n\n    loop {\n        let mut socket = TcpSocket::new(stack, &mut rx_buffer, &mut tx_buffer);\n        socket.set_timeout(Some(embassy_time::Duration::from_secs(10)));\n\n        info!(\"Listening on TCP:1234...\");\n        if let Err(e) = socket.accept(1234).await {\n            warn!(\"accept error: {:?}\", e);\n            continue;\n        }\n\n        info!(\"Received connection from {:?}\", socket.remote_endpoint());\n\n        loop {\n            let n = match socket.read(&mut buf).await {\n                Ok(0) => {\n                    warn!(\"read EOF\");\n                    break;\n                }\n                Ok(n) => n,\n                Err(e) => {\n                    warn!(\"read error: {:?}\", e);\n                    break;\n                }\n            };\n\n            info!(\"rxd {:02x}\", &buf[..n]);\n\n            match socket.write_all(&buf[..n]).await {\n                Ok(()) => {}\n                Err(e) => {\n                    warn!(\"write error: {:?}\", e);\n                    break;\n                }\n            };\n        }\n    }\n}\n"
  },
  {
    "path": "examples/nrf52840-edf/.cargo/config.toml",
    "content": "[target.'cfg(all(target_arch = \"arm\", target_os = \"none\"))']\n# replace nRF82840_xxAA with your chip as listed in `probe-rs chip list`\nrunner = \"probe-rs run --chip nRF52840_xxAA\"\n\n[build]\ntarget = \"thumbv7em-none-eabi\"\n\n[env]\nDEFMT_LOG = \"debug\"\n"
  },
  {
    "path": "examples/nrf52840-edf/Cargo.toml",
    "content": "[package]\nedition = \"2024\"\nname = \"embassy-nrf52840-edf-examples\"\nversion = \"0.1.0\"\nlicense = \"MIT OR Apache-2.0\"\npublish = false\n\n[dependencies]\n# NOTE: \"scheduler-deadline\" and \"embassy-time-driver\" features are enabled\nembassy-executor = { version = \"0.10.0\", path = \"../../embassy-executor\", features = [\"platform-cortex-m\", \"executor-thread\", \"executor-interrupt\", \"defmt\", \"scheduler-deadline\", \"embassy-time-driver\"] }\nembassy-time = { version = \"0.5.1\", path = \"../../embassy-time\", features = [\"defmt\", \"defmt-timestamp-uptime\"] }\nembassy-nrf = { version = \"0.10.0\", path = \"../../embassy-nrf\", features = [\"defmt\", \"nrf52840\", \"time-driver-rtc1\", \"gpiote\", \"unstable-pac\", \"time\"] }\n\ndefmt = \"1.0.1\"\ndefmt-rtt = \"1.0.0\"\n\ncortex-m = { version = \"0.7.6\", features = [\"inline-asm\", \"critical-section-single-core\"] }\ncortex-m-rt = \"0.7.0\"\npanic-probe = { version = \"1.0.0\", features = [\"print-defmt\"] }\n\n[profile.release]\ndebug = 2\n\n[package.metadata.embassy]\nbuild = [\n  { target = \"thumbv7em-none-eabi\", artifact-dir = \"out/examples/nrf52840-edf\" }\n]\n"
  },
  {
    "path": "examples/nrf52840-edf/build.rs",
    "content": "//! This build script copies the `memory.x` file from the crate root into\n//! a directory where the linker can always find it at build time.\n//! For many projects this is optional, as the linker always searches the\n//! project root directory -- wherever `Cargo.toml` is. However, if you\n//! are using a workspace or have a more complicated build setup, this\n//! build script becomes required. Additionally, by requesting that\n//! Cargo re-run the build script whenever `memory.x` is changed,\n//! updating `memory.x` ensures a rebuild of the application with the\n//! new memory settings.\n\nuse std::env;\nuse std::fs::File;\nuse std::io::Write;\nuse std::path::PathBuf;\n\nfn main() {\n    // Put `memory.x` in our output directory and ensure it's\n    // on the linker search path.\n    let out = &PathBuf::from(env::var_os(\"OUT_DIR\").unwrap());\n    File::create(out.join(\"memory.x\"))\n        .unwrap()\n        .write_all(include_bytes!(\"memory.x\"))\n        .unwrap();\n    println!(\"cargo:rustc-link-search={}\", out.display());\n\n    // By default, Cargo will re-run a build script whenever\n    // any file in the project changes. By specifying `memory.x`\n    // here, we ensure the build script is only re-run when\n    // `memory.x` is changed.\n    println!(\"cargo:rerun-if-changed=memory.x\");\n\n    println!(\"cargo:rustc-link-arg-bins=--nmagic\");\n    println!(\"cargo:rustc-link-arg-bins=-Tlink.x\");\n    println!(\"cargo:rustc-link-arg-bins=-Tdefmt.x\");\n}\n"
  },
  {
    "path": "examples/nrf52840-edf/memory.x",
    "content": "MEMORY\n{\n  /* NOTE 1 K = 1 KiBi = 1024 bytes */\n  FLASH : ORIGIN = 0x00000000, LENGTH = 1024K\n  RAM : ORIGIN = 0x20000000, LENGTH = 256K\n\n  /* These values correspond to the NRF52840 with Softdevices S140 7.3.0 */\n  /*\n     FLASH : ORIGIN = 0x00027000, LENGTH = 868K\n     RAM : ORIGIN = 0x20020000, LENGTH = 128K\n  */\n}\n"
  },
  {
    "path": "examples/nrf52840-edf/src/bin/basic.rs",
    "content": "//! Basic side-by-side example of the Earliest Deadline First scheduler\n//!\n//! This test spawns a number of background \"ambient system load\" workers\n//! that are constantly working, and runs two sets of trials.\n//!\n//! The first trial runs with no deadline set, so our trial task is at the\n//! same prioritization level as the background worker tasks.\n//!\n//! The second trial sets a deadline, meaning that it will be given higher\n//! scheduling priority than background tasks, that have no deadline set\n\n#![no_std]\n#![no_main]\n\nuse core::sync::atomic::{Ordering, compiler_fence};\n\nuse defmt::unwrap;\nuse embassy_executor::Spawner;\nuse embassy_time::{Duration, Instant, Timer};\nuse {defmt_rtt as _, panic_probe as _};\n\n#[embassy_executor::main]\nasync fn main(spawner: Spawner) {\n    embassy_nrf::init(Default::default());\n\n    // Enable flash cache to remove some flash latency jitter\n    compiler_fence(Ordering::SeqCst);\n    embassy_nrf::pac::NVMC.icachecnf().write(|w| {\n        w.set_cacheen(true);\n    });\n    compiler_fence(Ordering::SeqCst);\n\n    //\n    // Baseline system load tunables\n    //\n\n    // how many load tasks? More load tasks means more tasks contending\n    // for the runqueue\n    let tasks = 32;\n    // how long should each task work for? The longer the working time,\n    // the longer the max jitter possible, even when a task is prioritized,\n    // as EDF is still cooperative and not pre-emptive\n    //\n    // 33 ticks ~= 1ms\n    let work_time_ticks = 33;\n    // what fraction, 1/denominator, should the system be busy?\n    // bigger number means **less** busy\n    //\n    // 2  => 50%\n    // 4  => 25%\n    // 10 => 10%\n    let denominator = 2;\n\n    // Total time window, so each worker is working 1/denominator\n    // amount of the total time\n    let time_window = work_time_ticks * u64::from(tasks) * denominator;\n\n    // Spawn all of our load workers!\n    for i in 0..tasks {\n        spawner.spawn(unwrap!(load_task(i, work_time_ticks, time_window)));\n    }\n\n    // Let all the tasks spin up\n    defmt::println!(\"Spinning up load tasks...\");\n    Timer::after_secs(1).await;\n\n    //\n    // Trial task worker tunables\n    //\n\n    // How many steps should the workers under test run?\n    // More steps means more chances to have to wait for other tasks\n    // in line ahead of us.\n    let num_steps = 100;\n\n    // How many ticks should the worker take working on each step?\n    //\n    // 33 ticks ~= 1ms\n    let work_ticks = 33;\n    // How many ticks should the worker wait on each step?\n    //\n    // 66 ticks ~= 2ms\n    let idle_ticks = 66;\n\n    // How many times to repeat each trial?\n    let trials = 3;\n\n    // The total time a trial would take, in a perfect unloaded system\n    let theoretical = (num_steps * work_ticks) + (num_steps * idle_ticks);\n\n    defmt::println!(\"\");\n    defmt::println!(\"Starting UNPRIORITIZED worker trials\");\n    for _ in 0..trials {\n        //\n        // UNPRIORITIZED worker\n        //\n        defmt::println!(\"\");\n        defmt::println!(\"Starting unprioritized worker\");\n        let start = Instant::now();\n        for _ in 0..num_steps {\n            let now = Instant::now();\n            while now.elapsed().as_ticks() < work_ticks {}\n            Timer::after_ticks(idle_ticks).await;\n        }\n        let elapsed = start.elapsed().as_ticks();\n        defmt::println!(\n            \"Trial complete, theoretical ticks: {=u64}, actual ticks: {=u64}\",\n            theoretical,\n            elapsed\n        );\n        let ratio = ((elapsed as f32) / (theoretical as f32)) * 100.0;\n        defmt::println!(\"Took {=f32}% of ideal time\", ratio);\n        Timer::after_millis(500).await;\n    }\n\n    Timer::after_secs(1).await;\n\n    defmt::println!(\"\");\n    defmt::println!(\"Starting PRIORITIZED worker trials\");\n    for _ in 0..trials {\n        //\n        // PRIORITIZED worker\n        //\n        defmt::println!(\"\");\n        defmt::println!(\"Starting prioritized worker\");\n        let start = Instant::now();\n        // Set the deadline to ~2x the theoretical time. In practice, setting any deadline\n        // here elevates the current task above all other worker tasks.\n        let meta = embassy_executor::Metadata::for_current_task().await;\n        meta.set_deadline_after(theoretical * 2);\n\n        // Perform the trial\n        for _ in 0..num_steps {\n            let now = Instant::now();\n            while now.elapsed().as_ticks() < work_ticks {}\n            Timer::after_ticks(idle_ticks).await;\n        }\n\n        let elapsed = start.elapsed().as_ticks();\n        defmt::println!(\n            \"Trial complete, theoretical ticks: {=u64}, actual ticks: {=u64}\",\n            theoretical,\n            elapsed\n        );\n        let ratio = ((elapsed as f32) / (theoretical as f32)) * 100.0;\n        defmt::println!(\"Took {=f32}% of ideal time\", ratio);\n\n        // Unset the deadline, deadlines are not automatically cleared, and if our\n        // deadline is in the past, then we get very high priority!\n        meta.unset_deadline();\n\n        Timer::after_millis(500).await;\n    }\n\n    defmt::println!(\"\");\n    defmt::println!(\"Trials Complete.\");\n}\n\n#[embassy_executor::task(pool_size = 32)]\nasync fn load_task(id: u32, ticks_on: u64, ttl_ticks: u64) {\n    let mut last_print = Instant::now();\n    let mut last_tick = last_print;\n    let mut variance = 0;\n    let mut max_variance = 0;\n    loop {\n        let tgt = last_tick + Duration::from_ticks(ttl_ticks);\n        assert!(tgt > Instant::now(), \"fell too behind!\");\n\n        Timer::at(tgt).await;\n        let now = Instant::now();\n        // How late are we from the target?\n        let var = now.duration_since(tgt).as_ticks();\n        max_variance = max_variance.max(var);\n        variance += var;\n\n        // blocking work\n        while now.elapsed().as_ticks() < ticks_on {}\n\n        if last_print.elapsed() >= Duration::from_secs(1) {\n            defmt::trace!(\n                \"Task {=u32} variance ticks (1s): {=u64}, max: {=u64}, act: {=u64}\",\n                id,\n                variance,\n                max_variance,\n                ticks_on,\n            );\n            max_variance = 0;\n            variance = 0;\n            last_print = Instant::now();\n        }\n\n        last_tick = tgt;\n    }\n}\n"
  },
  {
    "path": "examples/nrf52840-rtic/.cargo/config.toml",
    "content": "[target.'cfg(all(target_arch = \"arm\", target_os = \"none\"))']\n# replace nRF82840_xxAA with your chip as listed in `probe-rs chip list`\nrunner = \"probe-rs run --chip nRF52840_xxAA\"\n\n[build]\ntarget = \"thumbv7em-none-eabi\"\n\n[env]\nDEFMT_LOG = \"trace\"\n"
  },
  {
    "path": "examples/nrf52840-rtic/Cargo.toml",
    "content": "[package]\nedition = \"2024\"\nname = \"embassy-nrf52840-rtic-examples\"\nversion = \"0.1.0\"\nlicense = \"MIT OR Apache-2.0\"\npublish = false\n\n[dependencies]\nrtic = { version = \"2\", features = [\"thumbv7-backend\"] }\n\nembassy-futures = { version = \"0.1.2\", path = \"../../embassy-futures\" }\nembassy-sync = { version = \"0.8.0\", path = \"../../embassy-sync\", features = [\"defmt\"] }\nembassy-time = { version = \"0.5.1\", path = \"../../embassy-time\", features = [ \"defmt\", \"defmt-timestamp-uptime\"] }\nembassy-time-queue-utils = { version = \"0.3.0\", path = \"../../embassy-time-queue-utils\", features = [\"generic-queue-8\"] }\nembassy-nrf = { version = \"0.10.0\", path = \"../../embassy-nrf\", features = [ \"defmt\", \"nrf52840\", \"time-driver-rtc1\", \"gpiote\", \"unstable-pac\", \"time\"] }\n\ndefmt = \"1.0.1\"\ndefmt-rtt = \"1.0.0\"\n\ncortex-m = { version = \"0.7.6\", features = [\"inline-asm\", \"critical-section-single-core\"] }\ncortex-m-rt = \"0.7.0\"\npanic-probe = { version = \"1.0.0\", features = [\"print-defmt\"] }\n\n[profile.release]\ndebug = 2\n\n[package.metadata.embassy]\nbuild = [\n  { target = \"thumbv7em-none-eabi\", artifact-dir = \"out/examples/nrf52840-rtic\" }\n]\n"
  },
  {
    "path": "examples/nrf52840-rtic/build.rs",
    "content": "//! This build script copies the `memory.x` file from the crate root into\n//! a directory where the linker can always find it at build time.\n//! For many projects this is optional, as the linker always searches the\n//! project root directory -- wherever `Cargo.toml` is. However, if you\n//! are using a workspace or have a more complicated build setup, this\n//! build script becomes required. Additionally, by requesting that\n//! Cargo re-run the build script whenever `memory.x` is changed,\n//! updating `memory.x` ensures a rebuild of the application with the\n//! new memory settings.\n\nuse std::env;\nuse std::fs::File;\nuse std::io::Write;\nuse std::path::PathBuf;\n\nfn main() {\n    // Put `memory.x` in our output directory and ensure it's\n    // on the linker search path.\n    let out = &PathBuf::from(env::var_os(\"OUT_DIR\").unwrap());\n    File::create(out.join(\"memory.x\"))\n        .unwrap()\n        .write_all(include_bytes!(\"memory.x\"))\n        .unwrap();\n    println!(\"cargo:rustc-link-search={}\", out.display());\n\n    // By default, Cargo will re-run a build script whenever\n    // any file in the project changes. By specifying `memory.x`\n    // here, we ensure the build script is only re-run when\n    // `memory.x` is changed.\n    println!(\"cargo:rerun-if-changed=memory.x\");\n\n    println!(\"cargo:rustc-link-arg-bins=--nmagic\");\n    println!(\"cargo:rustc-link-arg-bins=-Tlink.x\");\n    println!(\"cargo:rustc-link-arg-bins=-Tdefmt.x\");\n}\n"
  },
  {
    "path": "examples/nrf52840-rtic/memory.x",
    "content": "MEMORY\n{\n  /* NOTE 1 K = 1 KiBi = 1024 bytes */\n  FLASH : ORIGIN = 0x00000000, LENGTH = 1024K\n  RAM : ORIGIN = 0x20000000, LENGTH = 256K\n\n  /* These values correspond to the NRF52840 with Softdevices S140 7.3.0 */\n  /*\n     FLASH : ORIGIN = 0x00027000, LENGTH = 868K\n     RAM : ORIGIN = 0x20020000, LENGTH = 128K\n  */\n}\n"
  },
  {
    "path": "examples/nrf52840-rtic/src/bin/blinky.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse {defmt_rtt as _, panic_probe as _};\n\n#[rtic::app(device = embassy_nrf, peripherals = false, dispatchers = [EGU0_SWI0, EGU1_SWI1])]\nmod app {\n    use defmt::info;\n    use embassy_nrf::gpio::{Level, Output, OutputDrive};\n    use embassy_nrf::{Peri, peripherals};\n    use embassy_time::Timer;\n\n    #[shared]\n    struct Shared {}\n\n    #[local]\n    struct Local {}\n\n    #[init]\n    fn init(_: init::Context) -> (Shared, Local) {\n        info!(\"Hello World!\");\n\n        let p = embassy_nrf::init(Default::default());\n        blink::spawn(p.P0_13).map_err(|_| ()).unwrap();\n\n        (Shared {}, Local {})\n    }\n\n    #[task(priority = 1)]\n    async fn blink(_cx: blink::Context, pin: Peri<'static, peripherals::P0_13>) {\n        let mut led = Output::new(pin, Level::Low, OutputDrive::Standard);\n\n        loop {\n            info!(\"off!\");\n            led.set_high();\n            Timer::after_millis(300).await;\n            info!(\"on!\");\n            led.set_low();\n            Timer::after_millis(300).await;\n        }\n    }\n}\n"
  },
  {
    "path": "examples/nrf5340/.cargo/config.toml",
    "content": "[target.'cfg(all(target_arch = \"arm\", target_os = \"none\"))']\n# replace nRF5340_xxAA with your chip as listed in `probe-rs chip list`\nrunner = \"probe-rs run --chip nRF5340_xxAA\"\n\n[build]\ntarget = \"thumbv8m.main-none-eabihf\"\n\n[env]\nDEFMT_LOG = \"trace\"\n"
  },
  {
    "path": "examples/nrf5340/Cargo.toml",
    "content": "[package]\nedition = \"2024\"\nname = \"embassy-nrf5340-examples\"\nversion = \"0.1.0\"\nlicense = \"MIT OR Apache-2.0\"\npublish = false\n\n[dependencies]\nembassy-futures = { version = \"0.1.2\", path = \"../../embassy-futures\" }\nembassy-sync = { version = \"0.8.0\", path = \"../../embassy-sync\", features = [\"defmt\"] }\nembassy-executor = { version = \"0.10.0\", path = \"../../embassy-executor\", features = [\"platform-cortex-m\", \"executor-thread\", \"defmt\"] }\nembassy-time = { version = \"0.5.1\", path = \"../../embassy-time\", features = [\"defmt\", \"defmt-timestamp-uptime\"] }\nembassy-nrf = { version = \"0.10.0\", path = \"../../embassy-nrf\", features = [\"defmt\", \"nrf5340-app-s\", \"time-driver-rtc1\", \"gpiote\", \"unstable-pac\"] }\nembassy-net = { version = \"0.9.0\", path = \"../../embassy-net\", features = [\"defmt\", \"tcp\", \"dhcpv4\", \"medium-ethernet\"] }\nembassy-usb = { version = \"0.6.0\", path = \"../../embassy-usb\", features = [\"defmt\"] }\nembedded-io-async = { version = \"0.7.0\" }\n\ndefmt = \"1.0.1\"\ndefmt-rtt = \"1.0.0\"\n\nstatic_cell = \"2\"\ncortex-m = { version = \"0.7.6\", features = [\"inline-asm\", \"critical-section-single-core\"] }\ncortex-m-rt = \"0.7.0\"\npanic-probe = { version = \"1.0.0\", features = [\"print-defmt\"] }\nembedded-storage = \"0.3.1\"\nusbd-hid = \"0.10.0\"\nserde = { version = \"1.0.136\", default-features = false }\nrand = { version = \"0.9.0\", default-features = false }\n\n[profile.release]\ndebug = 2\n\n[package.metadata.embassy]\nbuild = [\n  { target = \"thumbv8m.main-none-eabihf\", artifact-dir = \"out/examples/nrf5340\" }\n]\n"
  },
  {
    "path": "examples/nrf5340/build.rs",
    "content": "//! This build script copies the `memory.x` file from the crate root into\n//! a directory where the linker can always find it at build time.\n//! For many projects this is optional, as the linker always searches the\n//! project root directory -- wherever `Cargo.toml` is. However, if you\n//! are using a workspace or have a more complicated build setup, this\n//! build script becomes required. Additionally, by requesting that\n//! Cargo re-run the build script whenever `memory.x` is changed,\n//! updating `memory.x` ensures a rebuild of the application with the\n//! new memory settings.\n\nuse std::env;\nuse std::fs::File;\nuse std::io::Write;\nuse std::path::PathBuf;\n\nfn main() {\n    // Put `memory.x` in our output directory and ensure it's\n    // on the linker search path.\n    let out = &PathBuf::from(env::var_os(\"OUT_DIR\").unwrap());\n    File::create(out.join(\"memory.x\"))\n        .unwrap()\n        .write_all(include_bytes!(\"memory.x\"))\n        .unwrap();\n    println!(\"cargo:rustc-link-search={}\", out.display());\n\n    // By default, Cargo will re-run a build script whenever\n    // any file in the project changes. By specifying `memory.x`\n    // here, we ensure the build script is only re-run when\n    // `memory.x` is changed.\n    println!(\"cargo:rerun-if-changed=memory.x\");\n\n    println!(\"cargo:rustc-link-arg-bins=--nmagic\");\n    println!(\"cargo:rustc-link-arg-bins=-Tlink.x\");\n    println!(\"cargo:rustc-link-arg-bins=-Tdefmt.x\");\n}\n"
  },
  {
    "path": "examples/nrf5340/memory.x",
    "content": "MEMORY\n{\n  /* NOTE 1 K = 1 KiBi = 1024 bytes */\n  /* These values correspond to the NRF5340 */\n  FLASH : ORIGIN = 0x00000000, LENGTH = 1024K\n  RAM : ORIGIN = 0x20000000, LENGTH = 256K\n}\n"
  },
  {
    "path": "examples/nrf5340/src/bin/blinky.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse embassy_executor::Spawner;\nuse embassy_nrf::gpio::{Level, Output, OutputDrive};\nuse embassy_time::Timer;\nuse {defmt_rtt as _, panic_probe as _};\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let p = embassy_nrf::init(Default::default());\n    let mut led = Output::new(p.P0_28, Level::Low, OutputDrive::Standard);\n\n    loop {\n        led.set_high();\n        Timer::after_millis(300).await;\n        led.set_low();\n        Timer::after_millis(300).await;\n    }\n}\n"
  },
  {
    "path": "examples/nrf5340/src/bin/cryptocell_rng.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse embassy_executor::Spawner;\nuse embassy_nrf::cryptocell_rng::{self, CcRng};\nuse embassy_nrf::{bind_interrupts, peripherals};\nuse rand::Rng as _;\nuse {defmt_rtt as _, panic_probe as _};\n\nbind_interrupts!(struct Irqs {\n    CRYPTOCELL => cryptocell_rng::InterruptHandler<peripherals::CC_RNG>;\n});\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let p = embassy_nrf::init(Default::default());\n\n    let mut rng = CcRng::new(p.CC_RNG, Irqs);\n\n    // Async API\n    let mut bytes = [0; 4];\n    rng.fill_bytes(&mut bytes).await;\n    defmt::info!(\"Some random bytes: {:?}\", bytes);\n\n    // Sync API with `rand`\n    defmt::info!(\"A random number from 1 to 10: {:?}\", rng.random_range(1..=10));\n\n    let mut bytes = [0; 1024];\n    rng.fill_bytes(&mut bytes).await;\n    let zero_count: u32 = bytes.iter().fold(0, |acc, val| acc + val.count_zeros());\n    let one_count: u32 = bytes.iter().fold(0, |acc, val| acc + val.count_ones());\n    defmt::info!(\"Chance of zero: {}%\", zero_count * 100 / (bytes.len() as u32 * 8));\n    defmt::info!(\"Chance of one: {}%\", one_count * 100 / (bytes.len() as u32 * 8));\n}\n"
  },
  {
    "path": "examples/nrf5340/src/bin/gpiote_channel.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse defmt::info;\nuse embassy_executor::Spawner;\nuse embassy_nrf::gpio::Pull;\nuse embassy_nrf::gpiote::{InputChannel, InputChannelPolarity};\nuse {defmt_rtt as _, panic_probe as _};\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let p = embassy_nrf::init(Default::default());\n    info!(\"Starting!\");\n\n    let mut ch1 = InputChannel::new(p.GPIOTE_CH0, p.P0_23, Pull::Up, InputChannelPolarity::HiToLo);\n    let mut ch2 = InputChannel::new(p.GPIOTE_CH1, p.P0_24, Pull::Up, InputChannelPolarity::LoToHi);\n    let mut ch3 = InputChannel::new(p.GPIOTE_CH2, p.P0_08, Pull::Up, InputChannelPolarity::Toggle);\n    let mut ch4 = InputChannel::new(p.GPIOTE_CH3, p.P0_09, Pull::Up, InputChannelPolarity::Toggle);\n\n    let button1 = async {\n        loop {\n            ch1.wait().await;\n            info!(\"Button 1 pressed\")\n        }\n    };\n\n    let button2 = async {\n        loop {\n            ch2.wait().await;\n            info!(\"Button 2 released\")\n        }\n    };\n\n    let button3 = async {\n        loop {\n            ch3.wait().await;\n            info!(\"Button 3 toggled\")\n        }\n    };\n\n    let button4 = async {\n        loop {\n            ch4.wait().await;\n            info!(\"Button 4 toggled\")\n        }\n    };\n\n    embassy_futures::join::join4(button1, button2, button3, button4).await;\n}\n"
  },
  {
    "path": "examples/nrf5340/src/bin/nrf5340dk_internal_caps.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse defmt::info;\nuse embassy_executor::Spawner;\nuse embassy_nrf::config::{Config, HfclkSource, LfclkSource, LfxoCapacitance};\nuse embassy_nrf::pac;\nuse {defmt_rtt as _, panic_probe as _};\n\nfn print_xosc32mcaps() {\n    let value = pac::OSCILLATORS.xosc32mcaps().read();\n    info!(\"XOSC32MCAPS.ENABLE = {}\", value.enable());\n    info!(\"XOSC32MCAPS.CAPVALUE = {}\", value.capvalue());\n}\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    info!(\"Before init:\");\n    print_xosc32mcaps();\n\n    let mut config = Config::default();\n    config.hfclk_source = HfclkSource::Internal;\n    config.lfclk_source = LfclkSource::ExternalXtal;\n    config.internal_capacitors.hfxo = None; // keep the value from the FICR\n    config.internal_capacitors.lfxo = Some(LfxoCapacitance::_7pF);\n    let _p = embassy_nrf::init(config);\n\n    info!(\"After init:\");\n    print_xosc32mcaps();\n}\n"
  },
  {
    "path": "examples/nrf5340/src/bin/uart.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_nrf::peripherals::SERIAL0;\nuse embassy_nrf::{bind_interrupts, uarte};\nuse {defmt_rtt as _, panic_probe as _};\n\nbind_interrupts!(struct Irqs {\n    SERIAL0 => uarte::InterruptHandler<SERIAL0>;\n});\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let p = embassy_nrf::init(Default::default());\n    let mut config = uarte::Config::default();\n    config.parity = uarte::Parity::EXCLUDED;\n    config.baudrate = uarte::Baudrate::BAUD115200;\n\n    let mut uart = uarte::Uarte::new(p.SERIAL0, p.P1_00, p.P1_01, Irqs, config);\n\n    info!(\"uarte initialized!\");\n\n    // Message must be in SRAM\n    let mut buf = [0; 8];\n    buf.copy_from_slice(b\"Hello!\\r\\n\");\n\n    unwrap!(uart.write(&buf).await);\n    info!(\"wrote hello in uart!\");\n\n    loop {\n        info!(\"reading...\");\n        unwrap!(uart.read(&mut buf).await);\n        info!(\"writing...\");\n        unwrap!(uart.write(&buf).await);\n    }\n}\n"
  },
  {
    "path": "examples/nrf54l15/.cargo/config.toml",
    "content": "[target.'cfg(all(target_arch = \"arm\", target_os = \"none\"))']\n# replace nRF82840_xxAA with your chip as listed in `probe-rs chip list`\nrunner = \"probe-rs run --chip nrf54l15\"\n\n[build]\ntarget = \"thumbv8m.main-none-eabihf\"\n\n[env]\nDEFMT_LOG = \"trace\"\n"
  },
  {
    "path": "examples/nrf54l15/Cargo.toml",
    "content": "[package]\nedition = \"2024\"\nname = \"embassy-nrf54l15-examples\"\nversion = \"0.1.0\"\nlicense = \"MIT OR Apache-2.0\"\npublish = false\n\n[dependencies]\nembassy-futures = { version = \"0.1.2\", path = \"../../embassy-futures\" }\nembassy-executor = { version = \"0.10.0\", path = \"../../embassy-executor\", features = [\"platform-cortex-m\", \"executor-thread\", \"executor-interrupt\", \"defmt\"] }\nembassy-time = { version = \"0.5.1\", path = \"../../embassy-time\", features = [\"defmt\", \"defmt-timestamp-uptime\"] }\nembassy-sync = { version = \"0.8.0\", path = \"../../embassy-sync\", features = [\"defmt\"] }\nembassy-nrf = { version = \"0.10.0\", path = \"../../embassy-nrf\", features = [\"defmt\", \"nrf54l15-app-s\", \"time-driver-grtc\", \"gpiote\", \"unstable-pac\", \"time\"] }\nembedded-io = { version = \"0.7.0\", features = [\"defmt\"]  }\nembedded-io-async = { version = \"0.7.0\", features = [\"defmt\"] }\n\nrand = { version = \"0.9.0\", default-features = false }\n\ndefmt = \"1.0.1\"\ndefmt-rtt = \"1.0.0\"\npanic-probe = { version = \"1.0.0\", features = [\"print-defmt\"] }\n\ncortex-m = { version = \"0.7.6\", features = [\"inline-asm\", \"critical-section-single-core\"] }\ncortex-m-rt = \"0.7.0\"\n\nembedded-storage = \"0.3.1\"\nportable-atomic = \"1\"\n\nstatic_cell = \"2\"\n\n[profile.release]\ndebug = 2\n\n[package.metadata.embassy]\nbuild = [\n  { target = \"thumbv8m.main-none-eabihf\", artifact-dir = \"out/examples/nrf54l15\" }\n]\n"
  },
  {
    "path": "examples/nrf54l15/build.rs",
    "content": "//! This build script copies the `memory.x` file from the crate root into\n//! a directory where the linker can always find it at build time.\n//! For many projects this is optional, as the linker always searches the\n//! project root directory -- wherever `Cargo.toml` is. However, if you\n//! are using a workspace or have a more complicated build setup, this\n//! build script becomes required. Additionally, by requesting that\n//! Cargo re-run the build script whenever `memory.x` is changed,\n//! updating `memory.x` ensures a rebuild of the application with the\n//! new memory settings.\n\nuse std::env;\nuse std::fs::File;\nuse std::io::Write;\nuse std::path::PathBuf;\n\nfn main() {\n    // Put `memory.x` in our output directory and ensure it's\n    // on the linker search path.\n    let out = &PathBuf::from(env::var_os(\"OUT_DIR\").unwrap());\n    File::create(out.join(\"memory.x\"))\n        .unwrap()\n        .write_all(include_bytes!(\"memory.x\"))\n        .unwrap();\n    println!(\"cargo:rustc-link-search={}\", out.display());\n\n    // By default, Cargo will re-run a build script whenever\n    // any file in the project changes. By specifying `memory.x`\n    // here, we ensure the build script is only re-run when\n    // `memory.x` is changed.\n    println!(\"cargo:rerun-if-changed=memory.x\");\n\n    println!(\"cargo:rustc-link-arg-bins=--nmagic\");\n    println!(\"cargo:rustc-link-arg-bins=-Tlink.x\");\n    println!(\"cargo:rustc-link-arg-bins=-Tdefmt.x\");\n}\n"
  },
  {
    "path": "examples/nrf54l15/memory.x",
    "content": "MEMORY\n{\n  FLASH : ORIGIN = 0x00000000, LENGTH = 1524K\n  RAM : ORIGIN = 0x20000000, LENGTH = 256K\n}\n"
  },
  {
    "path": "examples/nrf54l15/src/bin/blinky.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse defmt::info;\nuse embassy_executor::Spawner;\nuse embassy_nrf::gpio::{Level, Output, OutputDrive};\nuse embassy_time::Timer;\nuse {defmt_rtt as _, panic_probe as _};\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let p = embassy_nrf::init(Default::default());\n    let mut led = Output::new(p.P2_09, Level::Low, OutputDrive::Standard);\n\n    loop {\n        info!(\"high!\");\n        led.set_high();\n        Timer::after_millis(300).await;\n        info!(\"low!\");\n        led.set_low();\n        Timer::after_millis(300).await;\n    }\n}\n"
  },
  {
    "path": "examples/nrf54l15/src/bin/buffered_uart.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_nrf::buffered_uarte::{self, BufferedUarte};\nuse embassy_nrf::{bind_interrupts, peripherals, uarte};\nuse embedded_io_async::Write;\nuse {defmt_rtt as _, panic_probe as _};\n\nbind_interrupts!(struct Irqs {\n    SERIAL20 => buffered_uarte::InterruptHandler<peripherals::SERIAL20>;\n});\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let p = embassy_nrf::init(Default::default());\n    let mut config = uarte::Config::default();\n    config.parity = uarte::Parity::EXCLUDED;\n    config.baudrate = uarte::Baudrate::BAUD115200;\n\n    let mut tx_buffer = [0u8; 4096];\n    let mut rx_buffer = [0u8; 4096];\n\n    let mut u = BufferedUarte::new(\n        p.SERIAL20,\n        p.P1_12,\n        p.P1_13,\n        Irqs,\n        config,\n        &mut rx_buffer,\n        &mut tx_buffer,\n    );\n\n    info!(\"uarte initialized!\");\n\n    unwrap!(u.write_all(b\"Hello!\\r\\n\").await);\n    info!(\"wrote hello in uart!\");\n\n    loop {\n        info!(\"reading...\");\n        let buf = unwrap!(u.fill_buf().await);\n        info!(\"read done, got {}\", buf);\n\n        // Read bytes have to be explicitly consumed, otherwise fill_buf() will return them again\n        let n = buf.len();\n        u.consume(n);\n    }\n}\n"
  },
  {
    "path": "examples/nrf54l15/src/bin/gpiote_channel.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse defmt::info;\nuse embassy_executor::Spawner;\nuse embassy_nrf::gpio::Pull;\nuse embassy_nrf::gpiote::{InputChannel, InputChannelPolarity};\nuse {defmt_rtt as _, panic_probe as _};\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let p = embassy_nrf::init(Default::default());\n    info!(\"Starting!\");\n\n    let mut ch1 = InputChannel::new(p.GPIOTE20_CH0, p.P1_13, Pull::Up, InputChannelPolarity::HiToLo);\n    let mut ch2 = InputChannel::new(p.GPIOTE20_CH1, p.P1_09, Pull::Up, InputChannelPolarity::LoToHi);\n    let mut ch3 = InputChannel::new(p.GPIOTE20_CH2, p.P1_08, Pull::Up, InputChannelPolarity::Toggle);\n    let mut ch4 = InputChannel::new(p.GPIOTE30_CH0, p.P0_04, Pull::Up, InputChannelPolarity::Toggle);\n\n    let button1 = async {\n        loop {\n            ch1.wait().await;\n            info!(\"Button 1 pressed\")\n        }\n    };\n\n    let button2 = async {\n        loop {\n            ch2.wait().await;\n            info!(\"Button 2 released\")\n        }\n    };\n\n    let button3 = async {\n        loop {\n            ch3.wait().await;\n            info!(\"Button 3 toggled\")\n        }\n    };\n\n    let button4 = async {\n        loop {\n            ch4.wait().await;\n            info!(\"Button 4 toggled\")\n        }\n    };\n\n    embassy_futures::join::join4(button1, button2, button3, button4).await;\n}\n"
  },
  {
    "path": "examples/nrf54l15/src/bin/gpiote_port.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse defmt::{info, unwrap};\nuse embassy_executor::Spawner;\nuse embassy_nrf::gpio::{Input, Pull};\nuse {defmt_rtt as _, panic_probe as _};\n\n#[embassy_executor::task(pool_size = 4)]\nasync fn button_task(n: usize, mut pin: Input<'static>) {\n    loop {\n        pin.wait_for_low().await;\n        info!(\"Button {:?} pressed!\", n);\n        pin.wait_for_high().await;\n        info!(\"Button {:?} released!\", n);\n    }\n}\n\n#[embassy_executor::main]\nasync fn main(spawner: Spawner) {\n    let p = embassy_nrf::init(Default::default());\n    info!(\"Starting!\");\n\n    let btn1 = Input::new(p.P1_13, Pull::Up);\n    let btn2 = Input::new(p.P1_09, Pull::Up);\n    let btn3 = Input::new(p.P1_08, Pull::Up);\n    let btn4 = Input::new(p.P0_04, Pull::Up);\n\n    spawner.spawn(unwrap!(button_task(1, btn1)));\n    spawner.spawn(unwrap!(button_task(2, btn2)));\n    spawner.spawn(unwrap!(button_task(3, btn3)));\n    spawner.spawn(unwrap!(button_task(4, btn4)));\n}\n"
  },
  {
    "path": "examples/nrf54l15/src/bin/pwm.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_nrf::pwm::{DutyCycle, Prescaler, SimplePwm};\nuse embassy_time::Timer;\nuse {defmt_rtt as _, panic_probe as _};\n\n// for i in range(1024): print(int((math.sin(i/512*math.pi)*0.4+0.5)**2*32767), ', ', end='')\nstatic DUTY: [u16; 1024] = [\n    8191, 8272, 8353, 8434, 8516, 8598, 8681, 8764, 8847, 8931, 9015, 9099, 9184, 9269, 9354, 9440, 9526, 9613, 9700,\n    9787, 9874, 9962, 10050, 10139, 10227, 10316, 10406, 10495, 10585, 10675, 10766, 10857, 10948, 11039, 11131, 11223,\n    11315, 11407, 11500, 11592, 11685, 11779, 11872, 11966, 12060, 12154, 12248, 12343, 12438, 12533, 12628, 12723,\n    12818, 12914, 13010, 13106, 13202, 13298, 13394, 13491, 13587, 13684, 13781, 13878, 13975, 14072, 14169, 14266,\n    14364, 14461, 14558, 14656, 14754, 14851, 14949, 15046, 15144, 15242, 15339, 15437, 15535, 15632, 15730, 15828,\n    15925, 16023, 16120, 16218, 16315, 16412, 16510, 16607, 16704, 16801, 16898, 16995, 17091, 17188, 17284, 17380,\n    17477, 17572, 17668, 17764, 17859, 17955, 18050, 18145, 18239, 18334, 18428, 18522, 18616, 18710, 18803, 18896,\n    18989, 19082, 19174, 19266, 19358, 19449, 19540, 19631, 19722, 19812, 19902, 19991, 20081, 20169, 20258, 20346,\n    20434, 20521, 20608, 20695, 20781, 20867, 20952, 21037, 21122, 21206, 21290, 21373, 21456, 21538, 21620, 21701,\n    21782, 21863, 21943, 22022, 22101, 22179, 22257, 22335, 22412, 22488, 22564, 22639, 22714, 22788, 22861, 22934,\n    23007, 23079, 23150, 23220, 23290, 23360, 23429, 23497, 23564, 23631, 23698, 23763, 23828, 23892, 23956, 24019,\n    24081, 24143, 24204, 24264, 24324, 24383, 24441, 24499, 24555, 24611, 24667, 24721, 24775, 24828, 24881, 24933,\n    24983, 25034, 25083, 25132, 25180, 25227, 25273, 25319, 25363, 25407, 25451, 25493, 25535, 25575, 25615, 25655,\n    25693, 25731, 25767, 25803, 25838, 25873, 25906, 25939, 25971, 26002, 26032, 26061, 26089, 26117, 26144, 26170,\n    26195, 26219, 26242, 26264, 26286, 26307, 26327, 26346, 26364, 26381, 26397, 26413, 26427, 26441, 26454, 26466,\n    26477, 26487, 26496, 26505, 26512, 26519, 26525, 26530, 26534, 26537, 26539, 26540, 26541, 26540, 26539, 26537,\n    26534, 26530, 26525, 26519, 26512, 26505, 26496, 26487, 26477, 26466, 26454, 26441, 26427, 26413, 26397, 26381,\n    26364, 26346, 26327, 26307, 26286, 26264, 26242, 26219, 26195, 26170, 26144, 26117, 26089, 26061, 26032, 26002,\n    25971, 25939, 25906, 25873, 25838, 25803, 25767, 25731, 25693, 25655, 25615, 25575, 25535, 25493, 25451, 25407,\n    25363, 25319, 25273, 25227, 25180, 25132, 25083, 25034, 24983, 24933, 24881, 24828, 24775, 24721, 24667, 24611,\n    24555, 24499, 24441, 24383, 24324, 24264, 24204, 24143, 24081, 24019, 23956, 23892, 23828, 23763, 23698, 23631,\n    23564, 23497, 23429, 23360, 23290, 23220, 23150, 23079, 23007, 22934, 22861, 22788, 22714, 22639, 22564, 22488,\n    22412, 22335, 22257, 22179, 22101, 22022, 21943, 21863, 21782, 21701, 21620, 21538, 21456, 21373, 21290, 21206,\n    21122, 21037, 20952, 20867, 20781, 20695, 20608, 20521, 20434, 20346, 20258, 20169, 20081, 19991, 19902, 19812,\n    19722, 19631, 19540, 19449, 19358, 19266, 19174, 19082, 18989, 18896, 18803, 18710, 18616, 18522, 18428, 18334,\n    18239, 18145, 18050, 17955, 17859, 17764, 17668, 17572, 17477, 17380, 17284, 17188, 17091, 16995, 16898, 16801,\n    16704, 16607, 16510, 16412, 16315, 16218, 16120, 16023, 15925, 15828, 15730, 15632, 15535, 15437, 15339, 15242,\n    15144, 15046, 14949, 14851, 14754, 14656, 14558, 14461, 14364, 14266, 14169, 14072, 13975, 13878, 13781, 13684,\n    13587, 13491, 13394, 13298, 13202, 13106, 13010, 12914, 12818, 12723, 12628, 12533, 12438, 12343, 12248, 12154,\n    12060, 11966, 11872, 11779, 11685, 11592, 11500, 11407, 11315, 11223, 11131, 11039, 10948, 10857, 10766, 10675,\n    10585, 10495, 10406, 10316, 10227, 10139, 10050, 9962, 9874, 9787, 9700, 9613, 9526, 9440, 9354, 9269, 9184, 9099,\n    9015, 8931, 8847, 8764, 8681, 8598, 8516, 8434, 8353, 8272, 8191, 8111, 8031, 7952, 7873, 7794, 7716, 7638, 7561,\n    7484, 7407, 7331, 7255, 7180, 7105, 7031, 6957, 6883, 6810, 6738, 6665, 6594, 6522, 6451, 6381, 6311, 6241, 6172,\n    6104, 6036, 5968, 5901, 5834, 5767, 5702, 5636, 5571, 5507, 5443, 5379, 5316, 5253, 5191, 5130, 5068, 5008, 4947,\n    4888, 4828, 4769, 4711, 4653, 4596, 4539, 4482, 4426, 4371, 4316, 4261, 4207, 4153, 4100, 4047, 3995, 3943, 3892,\n    3841, 3791, 3741, 3691, 3642, 3594, 3546, 3498, 3451, 3404, 3358, 3312, 3267, 3222, 3178, 3134, 3090, 3047, 3005,\n    2962, 2921, 2879, 2839, 2798, 2758, 2719, 2680, 2641, 2603, 2565, 2528, 2491, 2454, 2418, 2382, 2347, 2312, 2278,\n    2244, 2210, 2177, 2144, 2112, 2080, 2048, 2017, 1986, 1956, 1926, 1896, 1867, 1838, 1810, 1781, 1754, 1726, 1699,\n    1673, 1646, 1620, 1595, 1570, 1545, 1520, 1496, 1472, 1449, 1426, 1403, 1380, 1358, 1336, 1315, 1294, 1273, 1252,\n    1232, 1212, 1192, 1173, 1154, 1135, 1117, 1099, 1081, 1063, 1046, 1029, 1012, 996, 980, 964, 948, 933, 918, 903,\n    888, 874, 860, 846, 833, 819, 806, 793, 781, 768, 756, 744, 733, 721, 710, 699, 688, 677, 667, 657, 647, 637, 627,\n    618, 609, 599, 591, 582, 574, 565, 557, 549, 541, 534, 526, 519, 512, 505, 498, 492, 485, 479, 473, 467, 461, 455,\n    450, 444, 439, 434, 429, 424, 419, 415, 410, 406, 402, 398, 394, 390, 386, 383, 379, 376, 373, 370, 367, 364, 361,\n    359, 356, 354, 351, 349, 347, 345, 343, 342, 340, 338, 337, 336, 334, 333, 332, 331, 330, 330, 329, 328, 328, 328,\n    327, 327, 327, 327, 327, 328, 328, 328, 329, 330, 330, 331, 332, 333, 334, 336, 337, 338, 340, 342, 343, 345, 347,\n    349, 351, 354, 356, 359, 361, 364, 367, 370, 373, 376, 379, 383, 386, 390, 394, 398, 402, 406, 410, 415, 419, 424,\n    429, 434, 439, 444, 450, 455, 461, 467, 473, 479, 485, 492, 498, 505, 512, 519, 526, 534, 541, 549, 557, 565, 574,\n    582, 591, 599, 609, 618, 627, 637, 647, 657, 667, 677, 688, 699, 710, 721, 733, 744, 756, 768, 781, 793, 806, 819,\n    833, 846, 860, 874, 888, 903, 918, 933, 948, 964, 980, 996, 1012, 1029, 1046, 1063, 1081, 1099, 1117, 1135, 1154,\n    1173, 1192, 1212, 1232, 1252, 1273, 1294, 1315, 1336, 1358, 1380, 1403, 1426, 1449, 1472, 1496, 1520, 1545, 1570,\n    1595, 1620, 1646, 1673, 1699, 1726, 1754, 1781, 1810, 1838, 1867, 1896, 1926, 1956, 1986, 2017, 2048, 2080, 2112,\n    2144, 2177, 2210, 2244, 2278, 2312, 2347, 2382, 2418, 2454, 2491, 2528, 2565, 2603, 2641, 2680, 2719, 2758, 2798,\n    2839, 2879, 2921, 2962, 3005, 3047, 3090, 3134, 3178, 3222, 3267, 3312, 3358, 3404, 3451, 3498, 3546, 3594, 3642,\n    3691, 3741, 3791, 3841, 3892, 3943, 3995, 4047, 4100, 4153, 4207, 4261, 4316, 4371, 4426, 4482, 4539, 4596, 4653,\n    4711, 4769, 4828, 4888, 4947, 5008, 5068, 5130, 5191, 5253, 5316, 5379, 5443, 5507, 5571, 5636, 5702, 5767, 5834,\n    5901, 5968, 6036, 6104, 6172, 6241, 6311, 6381, 6451, 6522, 6594, 6665, 6738, 6810, 6883, 6957, 7031, 7105, 7180,\n    7255, 7331, 7407, 7484, 7561, 7638, 7716, 7794, 7873, 7952, 8031, 8111,\n];\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let p = embassy_nrf::init(Default::default());\n    let mut pwm = SimplePwm::new_2ch(p.PWM20, p.P1_10, p.P1_14, &Default::default());\n    pwm.set_prescaler(Prescaler::Div1);\n    pwm.set_max_duty(32767);\n    info!(\"pwm initialized!\");\n\n    let mut i = 0;\n    loop {\n        i += 1;\n        pwm.set_duty(0, DutyCycle::inverted(DUTY[i % 1024]));\n        pwm.set_duty(1, DutyCycle::inverted(DUTY[(i + 512) % 1024]));\n        Timer::after_millis(3).await;\n    }\n}\n"
  },
  {
    "path": "examples/nrf54l15/src/bin/rng.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse embassy_executor::Spawner;\nuse embassy_nrf::cracen::Cracen;\nuse rand::Rng as _;\nuse {defmt_rtt as _, panic_probe as _};\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let p = embassy_nrf::init(Default::default());\n    let mut rng = Cracen::new_blocking(p.CRACEN);\n\n    // Async API\n    let mut bytes = [0; 4];\n    rng.blocking_fill_bytes(&mut bytes);\n    defmt::info!(\"Some random bytes: {:?}\", bytes);\n\n    // Sync API with `rand`\n    defmt::info!(\"A random number from 1 to 10: {:?}\", rng.random_range(1..=10));\n\n    let mut bytes = [0; 1024];\n    rng.blocking_fill_bytes(&mut bytes);\n    let zero_count: u32 = bytes.iter().fold(0, |acc, val| acc + val.count_zeros());\n    let one_count: u32 = bytes.iter().fold(0, |acc, val| acc + val.count_ones());\n    defmt::info!(\"Chance of zero: {}%\", zero_count * 100 / (bytes.len() as u32 * 8));\n    defmt::info!(\"Chance of one: {}%\", one_count * 100 / (bytes.len() as u32 * 8));\n}\n"
  },
  {
    "path": "examples/nrf54l15/src/bin/rramc.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse defmt::{info, unwrap};\nuse embassy_executor::Spawner;\nuse embassy_nrf::nvmc::{Nvmc, PAGE_SIZE};\nuse embedded_storage::nor_flash::{NorFlash, ReadNorFlash};\nuse {defmt_rtt as _, panic_probe as _};\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let p = embassy_nrf::init(Default::default());\n    info!(\"Hello RRAMC NVMC!\");\n\n    let mut f = Nvmc::new(p.RRAMC);\n\n    const ADDR: u32 = 0x80000;\n    let mut buf = [0u8; 4];\n\n    info!(\"Reading...\");\n    unwrap!(f.read(ADDR, &mut buf));\n    info!(\"Read: {=[u8]:x}\", buf);\n\n    info!(\"Erasing...\");\n    unwrap!(f.erase(ADDR, ADDR + PAGE_SIZE as u32));\n\n    info!(\"Reading...\");\n    unwrap!(f.read(ADDR, &mut buf));\n    info!(\"Read: {=[u8]:x}\", buf);\n\n    info!(\"Writing...\");\n    // 16 B (128-bit) write minimum\n    let out: [u8; 16] = [\n        0xaa, 0xaa, 0xaa, 0xaa, 0xbb, 0xbb, 0xbb, 0xbb, 0xcc, 0xcc, 0xcc, 0xcc, 0xdd, 0xdd, 0xdd, 0xdd,\n    ];\n    unwrap!(f.write(ADDR, &out));\n\n    info!(\"Reading...\");\n    // Can read arbitrary sizes\n    for addr in (ADDR..ADDR + 16).step_by(4) {\n        unwrap!(f.read(addr, &mut buf));\n        info!(\"Read: {=[u8]:x}\", buf);\n    }\n}\n"
  },
  {
    "path": "examples/nrf54l15/src/bin/rramc_buffered.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse defmt::{info, unwrap};\nuse embassy_executor::Spawner;\nuse embassy_nrf::rramc::{Buffered, PAGE_SIZE, Rramc};\nuse embedded_storage::nor_flash::{NorFlash, ReadNorFlash};\nuse {defmt_rtt as _, panic_probe as _};\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let p = embassy_nrf::init(Default::default());\n    info!(\"Hello RRAMC NVMC!\");\n\n    // Buffer 8 word lines\n    let mut f: Rramc<'_, Buffered<128>> = Rramc::new_buffered(p.RRAMC);\n\n    const ADDR: u32 = 0x80000;\n    let mut buf = [0u8; 4];\n\n    info!(\"Reading...\");\n    unwrap!(f.read(ADDR, &mut buf));\n    info!(\"Read: {=[u8]:x}\", buf);\n\n    info!(\"Erasing...\");\n    unwrap!(f.erase(ADDR, ADDR + PAGE_SIZE as u32));\n\n    info!(\"Reading...\");\n    unwrap!(f.read(ADDR, &mut buf));\n    info!(\"Read: {=[u8]:x}\", buf);\n\n    info!(\"Writing...\");\n    // 16 B (128-bit) write minimum\n    let out: [u8; 256] = [\n        0xaa, 0xaa, 0xaa, 0xaa, 0xbb, 0xbb, 0xbb, 0xbb, 0xcc, 0xcc, 0xcc, 0xcc, 0xdd, 0xdd, 0xdd, 0xdd, 0xaa, 0xaa,\n        0xaa, 0xaa, 0xbb, 0xbb, 0xbb, 0xbb, 0xcc, 0xcc, 0xcc, 0xcc, 0xdd, 0xdd, 0xdd, 0xdd, 0xaa, 0xaa, 0xaa, 0xaa,\n        0xbb, 0xbb, 0xbb, 0xbb, 0xcc, 0xcc, 0xcc, 0xcc, 0xdd, 0xdd, 0xdd, 0xdd, 0xaa, 0xaa, 0xaa, 0xaa, 0xbb, 0xbb,\n        0xbb, 0xbb, 0xcc, 0xcc, 0xcc, 0xcc, 0xdd, 0xdd, 0xdd, 0xdd, 0xaa, 0xaa, 0xaa, 0xaa, 0xbb, 0xbb, 0xbb, 0xbb,\n        0xcc, 0xcc, 0xcc, 0xcc, 0xdd, 0xdd, 0xdd, 0xdd, 0xaa, 0xaa, 0xaa, 0xaa, 0xbb, 0xbb, 0xbb, 0xbb, 0xcc, 0xcc,\n        0xcc, 0xcc, 0xdd, 0xdd, 0xdd, 0xdd, 0xaa, 0xaa, 0xaa, 0xaa, 0xbb, 0xbb, 0xbb, 0xbb, 0xcc, 0xcc, 0xcc, 0xcc,\n        0xdd, 0xdd, 0xdd, 0xdd, 0xaa, 0xaa, 0xaa, 0xaa, 0xbb, 0xbb, 0xbb, 0xbb, 0xcc, 0xcc, 0xcc, 0xcc, 0xdd, 0xdd,\n        0xdd, 0xdd, 0xaa, 0xaa, 0xaa, 0xaa, 0xbb, 0xbb, 0xbb, 0xbb, 0xcc, 0xcc, 0xcc, 0xcc, 0xdd, 0xdd, 0xdd, 0xdd,\n        0xaa, 0xaa, 0xaa, 0xaa, 0xbb, 0xbb, 0xbb, 0xbb, 0xcc, 0xcc, 0xcc, 0xcc, 0xdd, 0xdd, 0xdd, 0xdd, 0xaa, 0xaa,\n        0xaa, 0xaa, 0xbb, 0xbb, 0xbb, 0xbb, 0xcc, 0xcc, 0xcc, 0xcc, 0xdd, 0xdd, 0xdd, 0xdd, 0xaa, 0xaa, 0xaa, 0xaa,\n        0xbb, 0xbb, 0xbb, 0xbb, 0xcc, 0xcc, 0xcc, 0xcc, 0xdd, 0xdd, 0xdd, 0xdd, 0xaa, 0xaa, 0xaa, 0xaa, 0xbb, 0xbb,\n        0xbb, 0xbb, 0xcc, 0xcc, 0xcc, 0xcc, 0xdd, 0xdd, 0xdd, 0xdd, 0xaa, 0xaa, 0xaa, 0xaa, 0xbb, 0xbb, 0xbb, 0xbb,\n        0xcc, 0xcc, 0xcc, 0xcc, 0xdd, 0xdd, 0xdd, 0xdd, 0xaa, 0xaa, 0xaa, 0xaa, 0xbb, 0xbb, 0xbb, 0xbb, 0xcc, 0xcc,\n        0xcc, 0xcc, 0xdd, 0xdd, 0xdd, 0xdd, 0xaa, 0xaa, 0xaa, 0xaa, 0xbb, 0xbb, 0xbb, 0xbb, 0xcc, 0xcc, 0xcc, 0xcc,\n        0xdd, 0xdd, 0xdd, 0xdd,\n    ];\n    unwrap!(f.write(ADDR, &out));\n\n    info!(\"Reading...\");\n    // Can read arbitrary sizes\n    for addr in (ADDR..ADDR + 256).step_by(4) {\n        unwrap!(f.read(addr, &mut buf));\n        info!(\"Read: {=[u8]:x}\", buf);\n    }\n}\n"
  },
  {
    "path": "examples/nrf54l15/src/bin/saadc.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse defmt::info;\nuse embassy_executor::Spawner;\nuse embassy_nrf::saadc::{ChannelConfig, Config, Saadc};\nuse embassy_nrf::{bind_interrupts, saadc};\nuse embassy_time::Timer;\nuse {defmt_rtt as _, panic_probe as _};\n\nbind_interrupts!(struct Irqs {\n    SAADC => saadc::InterruptHandler;\n});\n\n#[embassy_executor::main]\nasync fn main(_p: Spawner) {\n    let mut p = embassy_nrf::init(Default::default());\n    let config = Config::default();\n    let channel_config = ChannelConfig::single_ended(p.P1_04.reborrow());\n    let mut saadc = Saadc::new(p.SAADC, Irqs, config, [channel_config]);\n\n    loop {\n        let mut buf = [0; 1];\n        saadc.sample(&mut buf).await;\n        info!(\"sample: {=i16}\", &buf[0]);\n        Timer::after_millis(100).await;\n    }\n}\n"
  },
  {
    "path": "examples/nrf54l15/src/bin/spim.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse embassy_executor::Spawner;\nuse embassy_nrf::{bind_interrupts, peripherals, spim};\nuse {defmt_rtt as _, panic_probe as _};\n\nbind_interrupts!(struct Irqs {\n    SERIAL00 => spim::InterruptHandler<peripherals::SERIAL00>;\n});\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let p = embassy_nrf::init(Default::default());\n    let mut config = spim::Config::default();\n    config.frequency = spim::Frequency::M32;\n    let mut spim = spim::Spim::new(p.SERIAL00, Irqs, p.P2_01, p.P2_04, p.P2_02, config.clone());\n    let data = [\n        0x42, 0x43, 0x44, 0x45, 0x66, 0x12, 0x23, 0x34, 0x45, 0x19, 0x91, 0xaa, 0xff, 0xa5, 0x5a, 0x77,\n    ];\n    let mut buf = [0u8; 16];\n\n    buf.fill(0);\n    spim.blocking_transfer(&mut buf, &data).unwrap();\n    assert_eq!(data, buf);\n\n    buf.fill(0);\n    spim.transfer(&mut buf, &data).await.unwrap();\n    assert_eq!(data, buf);\n}\n"
  },
  {
    "path": "examples/nrf54l15/src/bin/temp.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse defmt::info;\nuse embassy_executor::Spawner;\nuse embassy_nrf::temp::Temp;\nuse embassy_nrf::{bind_interrupts, temp};\nuse embassy_time::Timer;\nuse {defmt_rtt as _, panic_probe as _};\n\nbind_interrupts!(struct Irqs {\n    TEMP => temp::InterruptHandler;\n});\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let p = embassy_nrf::init(Default::default());\n    let mut temp = Temp::new(p.TEMP, Irqs);\n\n    loop {\n        let value = temp.read().await;\n        info!(\"temperature: {}℃\", value.to_num::<u16>());\n        Timer::after_secs(1).await;\n    }\n}\n"
  },
  {
    "path": "examples/nrf54l15/src/bin/timer.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse defmt::{info, unwrap};\nuse embassy_executor::Spawner;\nuse embassy_time::Timer;\nuse {defmt_rtt as _, panic_probe as _};\n\n#[embassy_executor::task]\nasync fn run1() {\n    loop {\n        info!(\"BIG INFREQUENT TICK\");\n        Timer::after_secs(10).await;\n    }\n}\n\n#[embassy_executor::task]\nasync fn run2() {\n    loop {\n        info!(\"tick\");\n        Timer::after_secs(1).await;\n    }\n}\n\n#[embassy_executor::main]\nasync fn main(spawner: Spawner) {\n    let _p = embassy_nrf::init(Default::default());\n    spawner.spawn(unwrap!(run1()));\n    spawner.spawn(unwrap!(run2()));\n}\n"
  },
  {
    "path": "examples/nrf54l15/src/bin/twim.rs",
    "content": "//! Example on how to read a 24C/24LC i2c eeprom.\n//!\n//! Connect SDA to P0.03, SCL to P0.04\n\n#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_nrf::twim::{self, Twim};\nuse embassy_nrf::{bind_interrupts, peripherals};\nuse static_cell::ConstStaticCell;\nuse {defmt_rtt as _, panic_probe as _};\n\nconst ADDRESS: u8 = 0x18;\nconst WHOAMI: u8 = 0x0F;\n\nbind_interrupts!(struct Irqs {\n    SERIAL20 => twim::InterruptHandler<peripherals::SERIAL20>;\n});\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let p = embassy_nrf::init(Default::default());\n    info!(\"Initializing TWI...\");\n    let config = twim::Config::default();\n    static RAM_BUFFER: ConstStaticCell<[u8; 16]> = ConstStaticCell::new([0; 16]);\n    let mut twi = Twim::new(p.SERIAL20, Irqs, p.P1_13, p.P1_12, config, RAM_BUFFER.take());\n\n    info!(\"Reading...\");\n\n    let mut data = [0u8; 1];\n    match twi.write_read(ADDRESS, &[WHOAMI], &mut data).await {\n        Ok(()) => info!(\"Whoami: {}\", data[0]),\n        Err(e) => error!(\"I2c Error: {:?}\", e),\n    }\n}\n"
  },
  {
    "path": "examples/nrf54l15/src/bin/twis.rs",
    "content": "//! TWIS example\n\n#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_nrf::twis::{self, Command, Twis};\nuse embassy_nrf::{bind_interrupts, peripherals};\nuse {defmt_rtt as _, panic_probe as _};\n\nbind_interrupts!(struct Irqs {\n    SERIAL20 => twis::InterruptHandler<peripherals::SERIAL20>;\n});\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let p = embassy_nrf::init(Default::default());\n\n    let mut config = twis::Config::default();\n    config.address0 = 0x55; // Set i2c address\n    let mut i2c = Twis::new(p.SERIAL20, Irqs, p.P0_03, p.P0_04, config);\n\n    info!(\"Listening...\");\n    loop {\n        let response = [1, 2, 3, 4, 5, 6, 7, 8];\n        // This buffer is used if the i2c master performs a Write or WriteRead\n        let mut buf = [0u8; 16];\n        match i2c.listen(&mut buf).await {\n            Ok(Command::Read) => {\n                info!(\"Got READ command. Respond with data:\\n{:?}\\n\", response);\n                if let Err(e) = i2c.respond_to_read(&response).await {\n                    error!(\"{:?}\", e);\n                }\n            }\n            Ok(Command::Write(n)) => info!(\"Got WRITE command with data:\\n{:?}\\n\", buf[..n]),\n            Ok(Command::WriteRead(n)) => {\n                info!(\"Got WRITE/READ command with data:\\n{:?}\", buf[..n]);\n                info!(\"Respond with data:\\n{:?}\\n\", response);\n                if let Err(e) = i2c.respond_to_read(&response).await {\n                    error!(\"{:?}\", e);\n                }\n            }\n            Err(e) => error!(\"{:?}\", e),\n        }\n    }\n}\n"
  },
  {
    "path": "examples/nrf54l15/src/bin/uart.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_nrf::{bind_interrupts, peripherals, uarte};\nuse {defmt_rtt as _, panic_probe as _};\n\nbind_interrupts!(struct Irqs {\n    SERIAL20 => uarte::InterruptHandler<peripherals::SERIAL20>;\n});\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let p = embassy_nrf::init(Default::default());\n    let mut config = uarte::Config::default();\n    config.parity = uarte::Parity::EXCLUDED;\n    config.baudrate = uarte::Baudrate::BAUD115200;\n\n    let mut uart = uarte::Uarte::new(p.SERIAL20, p.P1_12, p.P1_13, Irqs, config);\n\n    info!(\"uarte initialized!\");\n\n    // Message must be in SRAM\n    let mut buf = [0; 8];\n    buf.copy_from_slice(b\"Hello!\\r\\n\");\n\n    unwrap!(uart.write(&buf).await);\n    info!(\"wrote hello in uart!\");\n\n    loop {\n        info!(\"reading...\");\n        unwrap!(uart.read(&mut buf).await);\n        info!(\"writing...\");\n        unwrap!(uart.write(&buf).await);\n    }\n}\n"
  },
  {
    "path": "examples/nrf54l15/src/bin/wdt.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_nrf::wdt::{Config, HaltConfig, Watchdog};\nuse embassy_time::Timer;\nuse {defmt_rtt as _, panic_probe as _};\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let p = embassy_nrf::init(Default::default());\n    info!(\"Hello WDT\");\n\n    const TIMEOUT_S: u32 = 5;\n\n    let mut config = Config::default();\n    config.timeout_ticks = 32768 * TIMEOUT_S;\n\n    // This is needed for `probe-rs run` to be able to catch the panic message\n    // in the WDT interrupt. The core resets 2 ticks after firing the interrupt.\n    config.action_during_debug_halt = HaltConfig::PAUSE;\n\n    // The nrf54l15 has two watchdogs. Only one (WDT) is available in non-secure (ns) mode, as the\n    // other is reserved for the secure (s) environment. In secure mode, both are available as WDT0\n    // and WDT1.\n    info!(\"Watchdog launched with {} s timeout\", TIMEOUT_S);\n    let (_wdt, [mut handle]) = match Watchdog::try_new(p.WDT1, config) {\n        Ok(x) => x,\n        Err(_) => {\n            info!(\"Watchdog already active with wrong config, waiting for it to timeout...\");\n            loop {}\n        }\n    };\n\n    for wait in 1..=TIMEOUT_S {\n        info!(\"Waiting {} seconds ...\", wait);\n        Timer::after_secs(wait as u64).await;\n        handle.pet();\n        info!(\"Pet watchdog\");\n    }\n}\n"
  },
  {
    "path": "examples/nrf54lm20/.cargo/config.toml",
    "content": "[target.'cfg(all(target_arch = \"arm\", target_os = \"none\"))']\n# Note: it needs the latest git version of probe-rs\nrunner = \"probe-rs run --chip nrf54lm20a\"\n\n[build]\ntarget = \"thumbv8m.main-none-eabihf\"\n\n[env]\nDEFMT_LOG = \"trace\"\n"
  },
  {
    "path": "examples/nrf54lm20/Cargo.toml",
    "content": "[package]\nedition = \"2024\"\nname = \"embassy-nrf54lm20-examples\"\nversion = \"0.1.0\"\nlicense = \"MIT OR Apache-2.0\"\npublish = false\n\n[dependencies]\nembassy-futures = { version = \"0.1.2\", path = \"../../embassy-futures\" }\nembassy-executor = { version = \"0.10.0\", path = \"../../embassy-executor\", features = [\"platform-cortex-m\", \"executor-thread\", \"executor-interrupt\", \"defmt\"] }\nembassy-time = { version = \"0.5.1\", path = \"../../embassy-time\", features = [\"defmt\", \"defmt-timestamp-uptime\"] }\nembassy-sync = { version = \"0.8.0\", path = \"../../embassy-sync\", features = [\"defmt\"] }\nembassy-nrf = { version = \"0.10.0\", path = \"../../embassy-nrf\", features = [\"defmt\", \"nrf54lm20-app-s\", \"time-driver-grtc\", \"gpiote\", \"unstable-pac\", \"time\"] }\nembedded-io = { version = \"0.7.0\", features = [\"defmt\"]  }\nembedded-io-async = { version = \"0.7.0\", features = [\"defmt\"] }\n\nrand = { version = \"0.9.0\", default-features = false }\n\ndefmt = \"1.0.1\"\ndefmt-rtt = \"1.0.0\"\npanic-probe = { version = \"1.0.0\", features = [\"print-defmt\"] }\n\ncortex-m = { version = \"0.7.6\", features = [\"inline-asm\", \"critical-section-single-core\"] }\ncortex-m-rt = \"0.7.0\"\n\nembedded-storage = \"0.3.1\"\nportable-atomic = \"1\"\n\nstatic_cell = \"2\"\n\n[profile.release]\ndebug = 2\n\n[package.metadata.embassy]\nbuild = [\n  { target = \"thumbv8m.main-none-eabihf\", artifact-dir = \"out/examples/nrf54lm20\" }\n]\n"
  },
  {
    "path": "examples/nrf54lm20/build.rs",
    "content": "//! This build script copies the `memory.x` file from the crate root into\n//! a directory where the linker can always find it at build time.\n//! For many projects this is optional, as the linker always searches the\n//! project root directory -- wherever `Cargo.toml` is. However, if you\n//! are using a workspace or have a more complicated build setup, this\n//! build script becomes required. Additionally, by requesting that\n//! Cargo re-run the build script whenever `memory.x` is changed,\n//! updating `memory.x` ensures a rebuild of the application with the\n//! new memory settings.\n\nuse std::env;\nuse std::fs::File;\nuse std::io::Write;\nuse std::path::PathBuf;\n\nfn main() {\n    // Put `memory.x` in our output directory and ensure it's\n    // on the linker search path.\n    let out = &PathBuf::from(env::var_os(\"OUT_DIR\").unwrap());\n    File::create(out.join(\"memory.x\"))\n        .unwrap()\n        .write_all(include_bytes!(\"memory.x\"))\n        .unwrap();\n    println!(\"cargo:rustc-link-search={}\", out.display());\n\n    // By default, Cargo will re-run a build script whenever\n    // any file in the project changes. By specifying `memory.x`\n    // here, we ensure the build script is only re-run when\n    // `memory.x` is changed.\n    println!(\"cargo:rerun-if-changed=memory.x\");\n\n    println!(\"cargo:rustc-link-arg-bins=--nmagic\");\n    println!(\"cargo:rustc-link-arg-bins=-Tlink.x\");\n    println!(\"cargo:rustc-link-arg-bins=-Tdefmt.x\");\n}\n"
  },
  {
    "path": "examples/nrf54lm20/memory.x",
    "content": "MEMORY\n{\n  FLASH : ORIGIN = 0x00000000, LENGTH = 2036K\n\n  /*\n   * Actual RAM size is 512K, but:\n   *\n   * 0x2007df40 - 0x2007fe40: VPR saved context\n   * 0x2007ff00 - 0x2007ffff: Protected RAM\n   */\n  RAM : ORIGIN = 0x20000000, LENGTH = 0x7fd40\n}\n"
  },
  {
    "path": "examples/nrf54lm20/src/bin/blinky.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse defmt::info;\nuse embassy_executor::Spawner;\nuse embassy_nrf::gpio::{Level, Output, OutputDrive};\nuse embassy_time::Timer;\nuse {defmt_rtt as _, panic_probe as _};\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let p = embassy_nrf::init(Default::default());\n    let mut led = Output::new(p.P1_22, Level::Low, OutputDrive::HighDrive);\n\n    loop {\n        info!(\"high!\");\n        led.set_high();\n        Timer::after_millis(300).await;\n        info!(\"low!\");\n        led.set_low();\n        Timer::after_millis(300).await;\n    }\n}\n"
  },
  {
    "path": "examples/nrf54lm20/src/bin/gpio.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse defmt::info;\nuse embassy_executor::Spawner;\nuse embassy_nrf::gpio::{Input, Pull};\nuse embassy_time::Timer;\nuse {defmt_rtt as _, panic_probe as _};\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let p = embassy_nrf::init(Default::default());\n    let btn = Input::new(p.P1_26, Pull::Up);\n\n    loop {\n        if btn.is_high() {\n            info!(\"high\");\n        } else {\n            info!(\"low\");\n        }\n        Timer::after_millis(100).await;\n    }\n}\n"
  },
  {
    "path": "examples/nrf54lm20/src/bin/gpiote_channel.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse defmt::info;\nuse embassy_executor::Spawner;\nuse embassy_nrf::gpio::Pull;\nuse embassy_nrf::gpiote::{InputChannel, InputChannelPolarity};\nuse {defmt_rtt as _, panic_probe as _};\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let p = embassy_nrf::init(Default::default());\n    info!(\"Starting!\");\n\n    let mut ch1 = InputChannel::new(p.GPIOTE20_CH0, p.P1_26, Pull::Up, InputChannelPolarity::HiToLo);\n    let mut ch2 = InputChannel::new(p.GPIOTE20_CH1, p.P1_09, Pull::Up, InputChannelPolarity::LoToHi);\n    let mut ch3 = InputChannel::new(p.GPIOTE20_CH2, p.P1_08, Pull::Up, InputChannelPolarity::Toggle);\n    let mut ch4 = InputChannel::new(p.GPIOTE30_CH0, p.P0_05, Pull::Up, InputChannelPolarity::Toggle);\n\n    let button1 = async {\n        loop {\n            ch1.wait().await;\n            info!(\"Button 1 pressed\")\n        }\n    };\n\n    let button2 = async {\n        loop {\n            ch2.wait().await;\n            info!(\"Button 2 released\")\n        }\n    };\n\n    let button3 = async {\n        loop {\n            ch3.wait().await;\n            info!(\"Button 3 toggled\")\n        }\n    };\n\n    let button4 = async {\n        loop {\n            ch4.wait().await;\n            info!(\"Button 4 toggled\")\n        }\n    };\n\n    embassy_futures::join::join4(button1, button2, button3, button4).await;\n}\n"
  },
  {
    "path": "examples/nrf54lm20/src/bin/gpiote_port.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse defmt::{info, unwrap};\nuse embassy_executor::Spawner;\nuse embassy_nrf::gpio::{Input, Pull};\nuse {defmt_rtt as _, panic_probe as _};\n\n#[embassy_executor::task(pool_size = 4)]\nasync fn button_task(n: usize, mut pin: Input<'static>) {\n    loop {\n        pin.wait_for_low().await;\n        info!(\"Button {:?} pressed!\", n);\n        pin.wait_for_high().await;\n        info!(\"Button {:?} released!\", n);\n    }\n}\n\n#[embassy_executor::main]\nasync fn main(spawner: Spawner) {\n    let p = embassy_nrf::init(Default::default());\n    info!(\"Starting!\");\n\n    let btn1 = Input::new(p.P1_26, Pull::Up);\n    let btn2 = Input::new(p.P1_09, Pull::Up);\n    let btn3 = Input::new(p.P1_08, Pull::Up);\n    let btn4 = Input::new(p.P0_05, Pull::Up);\n\n    spawner.spawn(unwrap!(button_task(1, btn1)));\n    spawner.spawn(unwrap!(button_task(2, btn2)));\n    spawner.spawn(unwrap!(button_task(3, btn3)));\n    spawner.spawn(unwrap!(button_task(4, btn4)));\n}\n"
  },
  {
    "path": "examples/nrf54lm20/src/bin/timer.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse defmt::{info, unwrap};\nuse embassy_executor::Spawner;\nuse embassy_time::Timer;\nuse {defmt_rtt as _, panic_probe as _};\n\n#[embassy_executor::task]\nasync fn run1() {\n    loop {\n        info!(\"BIG INFREQUENT TICK\");\n        Timer::after_secs(10).await;\n    }\n}\n\n#[embassy_executor::task]\nasync fn run2() {\n    loop {\n        info!(\"tick\");\n        Timer::after_secs(1).await;\n    }\n}\n\n#[embassy_executor::main]\nasync fn main(spawner: Spawner) {\n    let _p = embassy_nrf::init(Default::default());\n    spawner.spawn(unwrap!(run1()));\n    spawner.spawn(unwrap!(run2()));\n}\n"
  },
  {
    "path": "examples/nrf9151/ns/.cargo/config.toml",
    "content": "[target.'cfg(all(target_arch = \"arm\", target_os = \"none\"))']\n# replace nRF82840_xxAA with your chip as listed in `probe-rs chip list`\nrunner = \"probe-rs run --chip nRF9160_xxAA\"\n\n[build]\ntarget = \"thumbv8m.main-none-eabihf\"\n\n[env]\nDEFMT_LOG = \"trace\"\n"
  },
  {
    "path": "examples/nrf9151/ns/Cargo.toml",
    "content": "[package]\nedition = \"2024\"\nname = \"embassy-nrf9151-non-secure-examples\"\nversion = \"0.1.0\"\nlicense = \"MIT OR Apache-2.0\"\npublish = false\n\n[dependencies]\nembassy-executor = { version = \"0.10.0\", path = \"../../../embassy-executor\", features = [\"platform-cortex-m\", \"executor-thread\", \"executor-interrupt\", \"defmt\"] }\nembassy-time = { version = \"0.5.1\", path = \"../../../embassy-time\", features = [\"defmt\", \"defmt-timestamp-uptime\"] }\nembassy-nrf = { version = \"0.10.0\", path = \"../../../embassy-nrf\", features = [\"defmt\", \"nrf9120-ns\", \"time-driver-rtc1\", \"gpiote\", \"unstable-pac\", \"time\"] }\n\ndefmt = \"1.0.1\"\ndefmt-rtt = \"1.0.0\"\n\ncortex-m = { version = \"0.7.6\", features = [\"inline-asm\", \"critical-section-single-core\"] }\ncortex-m-rt = \"0.7.0\"\npanic-probe = { version = \"1.0.0\", features = [\"print-defmt\"] }\n\n[profile.release]\ndebug = 2\n\n[package.metadata.embassy]\nbuild = [\n  { target = \"thumbv8m.main-none-eabihf\", artifact-dir = \"out/examples/nrf9151/ns\" }\n]\n"
  },
  {
    "path": "examples/nrf9151/ns/README.md",
    "content": "You must flash the TFM before running any non-secure examples. The TFM\nconfigures the secure and non-secure execution environments and then loads the\nnon-secure application. A reference TFM is included, and you can use the\nprovided helper script to flash it.\n"
  },
  {
    "path": "examples/nrf9151/ns/build.rs",
    "content": "//! This build script copies the `memory.x` file from the crate root into\n//! a directory where the linker can always find it at build time.\n//! For many projects this is optional, as the linker always searches the\n//! project root directory -- wherever `Cargo.toml` is. However, if you\n//! are using a workspace or have a more complicated build setup, this\n//! build script becomes required. Additionally, by requesting that\n//! Cargo re-run the build script whenever `memory.x` is changed,\n//! updating `memory.x` ensures a rebuild of the application with the\n//! new memory settings.\n\nuse std::env;\nuse std::fs::File;\nuse std::io::Write;\nuse std::path::PathBuf;\n\nfn main() {\n    // Put `memory.x` in our output directory and ensure it's\n    // on the linker search path.\n    let out = &PathBuf::from(env::var_os(\"OUT_DIR\").unwrap());\n    File::create(out.join(\"memory.x\"))\n        .unwrap()\n        .write_all(include_bytes!(\"memory.x\"))\n        .unwrap();\n    println!(\"cargo:rustc-link-search={}\", out.display());\n\n    // By default, Cargo will re-run a build script whenever\n    // any file in the project changes. By specifying `memory.x`\n    // here, we ensure the build script is only re-run when\n    // `memory.x` is changed.\n    println!(\"cargo:rerun-if-changed=memory.x\");\n\n    println!(\"cargo:rustc-link-arg-bins=--nmagic\");\n    println!(\"cargo:rustc-link-arg-bins=-Tlink.x\");\n    println!(\"cargo:rustc-link-arg-bins=-Tdefmt.x\");\n}\n"
  },
  {
    "path": "examples/nrf9151/ns/flash_tfm.sh",
    "content": "nrfjprog --family NRF91 --recover\nnrfjprog --family NRF91 --chiperase --verify --program tfm.hex\n"
  },
  {
    "path": "examples/nrf9151/ns/memory.x",
    "content": "MEMORY\n{\n  /* Trusted Firmware-M (TF-M) is flashed at the start */\n  FLASH                             : ORIGIN = 0x00008000, LENGTH = 0xf8000\n  RAM                         (rwx) : ORIGIN = 0x2000C568, LENGTH = 0x33a98\n}\n\n"
  },
  {
    "path": "examples/nrf9151/ns/src/bin/blinky.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse embassy_executor::Spawner;\nuse embassy_nrf::gpio::{Level, Output, OutputDrive};\nuse embassy_time::Timer;\nuse {defmt_rtt as _, panic_probe as _};\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let p = embassy_nrf::init(Default::default());\n    let mut led = Output::new(p.P0_00, Level::Low, OutputDrive::Standard);\n\n    loop {\n        led.set_high();\n        defmt::info!(\"high\");\n        Timer::after_millis(500).await;\n        led.set_low();\n        defmt::info!(\"low\");\n        Timer::after_millis(1000).await;\n    }\n}\n"
  },
  {
    "path": "examples/nrf9151/ns/src/bin/uart.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_nrf::{bind_interrupts, peripherals, uarte};\nuse {defmt_rtt as _, panic_probe as _};\n\nbind_interrupts!(struct Irqs {\n    SERIAL0 => uarte::InterruptHandler<peripherals::SERIAL0>;\n});\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let p = embassy_nrf::init(Default::default());\n    let mut config = uarte::Config::default();\n    config.parity = uarte::Parity::EXCLUDED;\n    config.baudrate = uarte::Baudrate::BAUD115200;\n\n    let mut uart = uarte::Uarte::new(p.SERIAL0, p.P0_26, p.P0_27, Irqs, config);\n\n    info!(\"uarte initialized!\");\n\n    // Message must be in SRAM\n    let mut buf = [0; 8];\n    buf.copy_from_slice(b\"Hello!\\r\\n\");\n\n    unwrap!(uart.write(&buf).await);\n    info!(\"wrote hello in uart!\");\n\n    loop {\n        info!(\"reading...\");\n        unwrap!(uart.read(&mut buf).await);\n        info!(\"writing...\");\n        unwrap!(uart.write(&buf).await);\n    }\n}\n"
  },
  {
    "path": "examples/nrf9151/ns/tfm.hex",
    "content": ":10000000F80B0020690600000D070000255A0000CB\n:10001000455A0000655A0000A55A0000855A0000A4\n:100020000000000000000000000000001D2700008C\n:100030000D070000000000000D0700000D07000084\n:10004000000000000000000000000000F13200008D\n:10005000000000000D07000000000000000000008C\n:100060000D0700000D0700000D0700000D07000040\n:10007000000000000D0700000D0700000D07000044\n:100080000D0700000D070000000000000000000048\n:100090000D0700000D070000000000000000000038\n:1000A0000D07000000000000000000000D07000028\n:1000B0000D0700000D0700000D0700000D070000F0\n:1000C0000D0700000D0700000D0700000D070000E0\n:1000D0000D070000000000000D07000000000000F8\n:1000E0000D070000000000000D07000000000000E8\n:1000F0000D070000000000000000000000000000EC\n:10010000000000000D0700000000000000000000DB\n:1001100000000000000000000000000000000000DF\n:10012000000000000D0700000000000000000000BB\n:1001300000000000000000000000000000000000BF\n:100140000D07000000015F5F00000000FE0B0000D3\n:10015000F9270000800600000000000000000000F9\n:10016000000000000000000000000000401200201D\n:100170000000000001015F5F030100001F0100009B\n:1001800085040000000000000000000001000000E5\n:10019000010000000000000000000000000000005E\n:1001A0000000000070000000CC5E00008000000035\n:1001B000000F000001000000D102000001015F5F9C\n:1001C000040100001F010000F30500000000000012\n:1001D000000000000000000001000000000000001E\n:1001E000000000000000000000000000D75E0000DA\n:1001F000400000000507000001000000DF040000CF\n:1002000038B50025054C29462046D4F80024C4F80A\n:10021000045405F0B0FCC4F8005438BD200C002094\n:1002200013B511460022CDE9002242680346382A60\n:10023000046844D194F82820022A19D01AB900F091\n:1002400047F902B010BD21482468D0F80404013AEF\n:10025000CDE90040082A35D8DFE802F0050A0E1380\n:10026000181D22272C006A46184600F02EF9E8E7F0\n:10027000184600F018F9E4E76A46184600F016F947\n:10028000DFE76A46184600F00BF9DAE76A461846D7\n:1002900000F00FF9D5E76A46184600F00DF9D0E7EF\n:1002A0006A46184600F00BF9CBE76A46184600F09C\n:1002B00009F9C6E76A46184600F00EF9C1E76FF083\n:1002C0008000BEE76FF08500BBE700BF200C002078\n:1002D0002DE9F04F05680446A1B0002D40F0AA803A\n:1002E0002022294602A805F046FC12AF202229460A\n:1002F0000AA805F040FC38222946384605F03BFCA8\n:10030000E26904F11C039846B2B10426E26A04F1E2\n:100310002C03EAB10425EEB96FF08306304621B014\n:10032000BDE8F08F002EF1D053F8041D3246013E97\n:100330000029F7D01646E9E70326F5E7002DEAD0B5\n:1003400053F8041D2A46013D0029F7D01546E2E77F\n:100350000325F5E738233A460021606805F048FC9C\n:100360003828D9D14FF00109CDE90270334F0DF192\n:10037000080A04F1100B4E45D7F800141CD10DF1FA\n:1003800028094B46002008EB850EC64533D108B13D\n:10039000C7F80014A3683146C7F804340AAA2B46EC\n:1003A00002A8FFF73DFF00270646BD4209F10809F4\n:1003B00038D1FFF725FFB1E75BF8043FC1F5806254\n:1003C000D81C20F00300904204D9FFF719FF6FF00A\n:1003D0008C06A3E70844CA19C7F8000449466068B8\n:1003E000019205F005FC019ACAF80C0009F1010917\n:1003F0004AF8082FBFE758F804CFC1F5806A0CF11E\n:10040000030222F00302524503F1080305D87818CD\n:1004100043E9020C11440120B7E70028D5D0C7F802\n:100420000014D2E7394659E90223606805F0F5FB6C\n:100430000137BAE76FF0850670E700BF200C002097\n:1004400008B528220021024805F095FB002008BDD0\n:100450002810002010B5044C0F222046034905F057\n:1004600065FB204610BD00BF51100020F6050000BE\n:1004700008B50121024803F011FB002008BD00BFB0\n:100480005010002008B5FFF7DBFF40B9FFF7E2FF8F\n:10049000FFF7EEFF18B9BDE8084003F06DBB08BDDB\n:1004A0006FF0850070476FF0850070476FF0850032\n:1004B00070476FF0850070476FF0850070476FF0F0\n:1004C000850070476FF0850070476FF085007047BA\n:1004D000D1E9000103F030BB6FF085007047F0B543\n:1004E00003680546B3F57D7FA7B007D040F2F5322B\n:1004F000934207D06FF0850027B0F0BD01F08EFC6D\n:100500000020F9E700243C22214607A8CDE9024457\n:10051000CDE90444069405F02EFB3C22214617A8A1\n:10052000169405F028FBEA69019405F11C0302B357\n:100530000426EA6A05F12C033AB30424731E012B46\n:1005400052D8012C50D80423002168680DEB030217\n:1005500005F04EFB04281AD06FF08000CCE7002E87\n:10056000E7D053F8041D3246013E0029F7D0164665\n:10057000DFE70326F5E7002CE0D053F8041D224600\n:10058000013C0029F7D01446D8E70324F5E7022EF2\n:1005900016D16E69402E29D806AF33463A4601215E\n:1005A000686805F025FB8642D6D1CDE9027602A91E\n:1005B00044B92246019801F01AFE044620469BE702\n:1005C0000021F5E72B6A402BC6D816AACDE90423F3\n:1005D000019804AA01F00BFEDDE904230446002182\n:1005E000686805F01AFBE9E7012085E76FF08900EC\n:1005F00082E7002070474D62656420544C532033DD\n:100600002E352E3000000000000000000000000029\n:1006100000000000000000000000000000000000DA\n:100620005431000001000000407C000080100020D8\n:100630004A000000407C0000200C00200000000068\n:10064000407C0000000C002000000000A8110020E9\n:10065000C4050000200C002018010000000C002040\n:100660000800000002F07ABC08B572B61E4A1F4BA3\n:100670009A601F4B83F309881E4B83F30A8883F328\n:100680000B881D4B16A1D1E90001C3E9000100F060\n:1006900089F81A4B1A498B4212D30020194B1A4978\n:1006A0008B4218D303F06AF8186850F82240586853\n:1006B00040F82240013298688242F5D30C33EAE7D1\n:1006C0000022F8E71C6844F8220001325C68A2426C\n:1006D000F8D30833E4E70022F8E700BFAFF3008067\n:1006E000A5EDF5FEA5EDF5FE0000000000ED00E033\n:1006F000F80B002000040020F80B00202806000062\n:100700004C0600004C06000064060000EFF3088071\n:10071000EFF309812DE9F00F6B46724600F038F9CE\n:1007200008B0FFF79FFFFEE708B5EFF31083012B3A\n:100730000C4A07D1126ABFF34F8F511C4A424A41FB\n:10074000104608BD01F0BEFB00B172B6126ABFF3DD\n:100750004F8F531C5A425A4101F0B4FB0028EFD08E\n:1007600062B6EDE70080FF0008B5EFF31083012BC0\n:100770000B4A06D1D269BFF34F8F02F00102104637\n:1007800008BD01F09FFB00B172B6D269BFF34F8F75\n:1007900002F0010201F096FB0028F0D062B6EEE70D\n:1007A0000080FF006F4A10B5D2F8D03043F002034A\n:1007B000C2F8D03001F086FB30B14FF05023002258\n:1007C000C3F81421C3F8182101F08BFB28B10122D2\n:1007D000654BC3F8382AC3F878254FF47F02D2F866\n:1007E0003031D2F83421BFF34F8FC3F30B03092B01\n:1007F00005D1012A1EBF01225B4BC3F8782501F009\n:1008000070FB18B10E22594BC3F8E42E01F05AFBCD\n:1008100030B10022544BC3F80C270122C3F8102733\n:100820000022D30003F57F03D3F80013BFF34F8FEB\n:10083000013108D0D3F800130132D3F80433B2F5F4\n:10084000807F0B60EDD1EFF31084FFF78DFF002860\n:100850004DD0BFF34F8F464BD3F80024012AFBD174\n:10086000C3F80425D3F80024012AFBD1012C03D0BE\n:1008700001F028FB00B172B6FFF776FF58B13D4A90\n:10088000D36923F00103D361BFF34F8F384AD2F805\n:100890000034012BFBD1FFF747FF68B1354A136ADB\n:1008A00023F0FF0343F020031362BFF34F8F304A5E\n:1008B000D2F80034012BFBD1012C03D001F002FB54\n:1008C00000B162B600222A4BC3F80425D3F80024F5\n:1008D000012AFBD1BFF34F8F2249274BCA6802F48C\n:1008E000E0621343CB60BFF34F8F00BFFDE7FFF71C\n:1008F0001BFF0028ADD11B4AD2F88C3043F4406373\n:10090000C2F88C304FF47F03D3F83021D3F8343160\n:10091000BFF34F8FC2F30B02092A16D1013B022B02\n:1009200002D8164AD35C83B1124815490368104AAD\n:100930008B420CBF5A230023C2F8103EC36A8B427D\n:100940000CBF5A230023C2F8003E064AD2F8883072\n:1009500043F47003C2F88830BFF34F8FBFF36F8F3B\n:1009600010BD00BF00ED00E00040005000A00350AB\n:10097000009003500080FF000400FA05C43300001B\n:10098000FA50FA507C22014905F0D0B8DC1800205A\n:1009900038B51D46374B384C5B68C3F308032360FA\n:1009A000EFF30383C4E901206365C2F3801302F00F\n:1009B0000C020C2AE16038D10BB9EFF389812022B7\n:1009C0002E48216105F0B2F825B1202229462C4895\n:1009D00005F0ACF8274B9A6AA265D96AE165986B75\n:1009E000206602F400406066586BA06602F080004A\n:1009F000E0669A62D962D3F8E4202267D3F8E8105F\n:100A0000616702F04001A167C3F8E42000F06AF8D2\n:100A10000E211C4801F077FA2268D31E042B1CD843\n:100A2000DFE803F0090F1215180013B9EFF38881FE\n:100A3000C5E70146C3E70C211348BDE8384001F083\n:100A400062BA12211148F8E70B211148F5E70D2190\n:100A50001048F2E70E211048EFE70F2A98BF142143\n:100A6000BDE8384089BF25210C480D48103A01F0F7\n:100A7000F5BB00BF00ED00E0DC180020F0180020FE\n:100A800010190020C7330000D5330000E133000007\n:100A9000F3330000FE3300000B3400001934000073\n:100AA0002D3400002DE9F34106460F460C4DD5F8D4\n:100AB0002C80C04710F00104FBD1394630466B69E9\n:100AC000984740B9C047C307FCD42B6A019302B0D2\n:100AD000BDE8F0411847204602B0BDE8F08100BFF4\n:100AE0005434000010B5094C0020A36898472369CE\n:100AF00002209847A46A4FF4E1310120A047234621\n:100B0000BDE8104001211520184700BF54340000F3\n:100B100010B5044C002023699847E368BDE81040F5\n:100B2000184700BF54340000064B82B018680023F9\n:100B300082B262F30F03000C60F31F43184602B049\n:100B4000704700BF9C340000014B1869704700BF1C\n:100B500084100020014B5869704700BF84100020AA\n:100B600038B50C4D0C4695F8203013B94FF0FF30D6\n:100B700038BD0A460146284600F06CFF064B9842F5\n:100B800005D00B3B9842F1D100206C61F0E76FF08B\n:100B90000100EDE7841000200B00AD0BF0B51B4EFB\n:100BA000054696F820300C4691B01BB94FF0FF3047\n:100BB00011B0F0BD00F06043B3F1005F21D100231C\n:100BC0000A460146304600F031FC114B98421AD0DB\n:100BD0000B3B9842EAD1346111E0A71B402F28BF9C\n:100BE0004027A9193A46684604F0A0FF39466846EE\n:100BF000FFF7D4FF0028DBD14036B442EDD8002007\n:100C0000D6E70026F9E76FF00100D1E78410002055\n:100C10000B00AD0BC3B2012B10B540F096804DF622\n:100C2000C023994270D028D8B1F5615F6FD011D838\n:100C3000B1F5965F6DD008D8B1F5966F6BD0B1F570\n:100C4000166F6BD06FF007005DE0B1F5165FF9D15C\n:100C5000454B33E047F61223994262D007D8B1F5ED\n:100C6000964F61D0B1F5E14FECD1404B26E0B1F5A4\n:100C7000164FE7D14FF41D0320E03D4B994255D06C\n:100C800011D8B1F5E13F54D007D8B1F5614F53D039\n:100C9000B1F5963FD6D1374B10E0B1F5613FD1D1D8\n:100CA0004FF06C730AE0B1F5612F48D0324B994296\n:100CB00048D0B1F5E12FC5D14FF0E86310F4E06FF3\n:100CC00046D110F4404103D0B1F5804F43D11021FB\n:100CD00010F4405203D0B2F5805F3FD10E2210F4E1\n:100CE000403003D0B0F5403F3BD10120234C20766B\n:100CF0006276E361A1760A4324681043C4F8243580\n:100D0000C4F86C05002010BD4FF46503D6E71C4BFA\n:100D1000D4E71C4BD2E74FF49E23CFE74FF41D23BB\n:100D2000CCE74FF40003C9E7174BC7E74FF08063E8\n:100D3000C4E74FF0EB73C1E74FF46B03BEE74FF02E\n:100D40007063BBE74FF08053B8E76FF00300DAE75A\n:100D50006FF00800D7E76FF00A00D4E76FF00900E2\n:100D6000D1E76FF00B00CEE70050270000C0750000\n:100D700090D0030000903A0140420F008410002000\n:100D800000F03A0000B0130000A04E0010B5114C66\n:100D9000204600F0D5FD0021206884F8201000F0E6\n:100DA00047FF226842F30732002A11DB0121530971\n:100DB0009B0003F1604303F56143D3F8800202F026\n:100DC0001F0201FA02F20243C3F88022D3F88032F4\n:100DD000002010BD8410002010B5264C0021206892\n:100DE0008EB000F017FF226842F30732002A12DBB0\n:100DF000012053099B0003F1604303F56143D3F8DD\n:100E0000801202F01F0200FA02F221EA0202C3F885\n:100E10008022D3F880322422002105A804F0ABFE02\n:100E2000D4E902124FF0FF33CDE90133CDE90333AA\n:100E30004FF0EB730B93072301A88DF8363001F0C8\n:100E400080F8014600220B4800F03AFA0A4B98421B\n:100E50000DD10C9B002023831B0CA3760B9BC4E9B4\n:100E60000400E361012384F820300EB010BD6FF060\n:100E70000100FAE7841000200000AD0B7FB50025CB\n:100E80000C46C0F808511646D0F80821D0F80022C8\n:100E900022F02002C0F80022104AC0F8082301F016\n:100EA000A0F8A560256104F14C03D3E8EF1F21F4FD\n:100EB0007F0121F47041C3E8E21F002AF5D110221E\n:100EC0002946684604F057FE042368468DF8003032\n:100ED0000196D4E90013984704B070BD10020A00CF\n:100EE00000F01F03400909D1074A02EB8303D3F83E\n:100EF0000012064A0A40C3F80022704700228033DD\n:100F000052F82330FFDE00BF00258450F0F8FCFFCC\n:100F10002DE9F04F804690F804A05020664F0E4611\n:100F200000FB0A70416885B049B113F0020304D098\n:100F3000836B002B00F0B2800123009300E000914E\n:100F40004FF0500909FB0A73DB6CD90600F1A6804B\n:100F50004FF0010BB3180293029BB34200F0A7803D\n:100F600006F06043039398F80450D8F8004009FB5A\n:100F700005739A6B9AB9039BB3F1005F40F09980B7\n:100F8000019200F0A1FD09FB0572D16C204601F031\n:100F9000010100F0F4FF28B900F09EFDE3E7012312\n:100FA0000193EEE7516C33460029BCBFD4F84C15D1\n:100FB0005164019A42B109FB05713078D1E90E23E1\n:100FC000D054D1E90E3213441BB1C4F84435C4F8EF\n:100FD00048B50022C4F82021D4F82011C4F85821C3\n:100FE000D4F85811C4F85021D4F850210822C4F87C\n:100FF0000025009AE2B14FF48003C4F80433C4F82A\n:1010000008B000F069FD502303FB0A725168002903\n:101010004BD1D26C120748D503FB0A734C33D3E88B\n:10102000EF1F21F00801C3E8E21F002A36D0F6E7DF\n:10103000C4F808B0019300F04FFD09FB0572D16CB4\n:10104000019B01F001012046019300F098FF8446C6\n:10105000019BD4F84C05D4F84425BCF1000F0DD108\n:101060009342F0D005EB850507EB0515D1B100F0F3\n:101070002BFD2946204600F0F9FFC2E705EB850568\n:10108000934207EB0515F1D10028EFD100F01CFDCC\n:101090002946204600F0EAFF00F01EFD074805B093\n:1010A000BDE8F08FC4F80CB0E1E7013654E704481E\n:1010B000F5E70448F3E700BF581900200800AD0B1E\n:1010C0000000AD0B0400AD0B430904D10123034A1A\n:1010D00083409360704700239B60FFDE00258450AF\n:1010E00000F01F0340090BD1084A02EB8302D2F83B\n:1010F0000012074B0B4043F00303C2F80032704765\n:101100000022803352F82330FFDE00BF00258450D8\n:10111000F0F8FCFF2DE9F84391F8303005460C4615\n:1011200090F804800F6853BB5023DFF85091781C6F\n:1011300003FB0893DE6C46F00072DA6404D0384694\n:10114000FFF7C2FFFFF7CCFF6068411C01D0FFF73B\n:10115000C7FE94F82C30012B11D1502303FB0893C8\n:10116000E06846F02066421CDE6401D0FFF7B8FE5E\n:10117000A068431C03D0FFF7A7FFFFF7B1FF94F867\n:101180002F0038B950223D4B02FB0833DA6C42F095\n:101190008072DA6494F82CC094F82D1094F82EE044\n:1011A000A26A2B6841EA0C01E668D4E90198C3F809\n:1011B00024254EEA0102C3F86C2550B9BCF1010F99\n:1011C000C3F80C75C3F8149504BFC3F80885C3F8B9\n:1011D0001065D3F8682503F26458012A08BFDA6065\n:1011E000D3F86425012A13D1082149F64046244F3B\n:1011F000C3F8001547F001075A60D8F800309BBBD0\n:101200002B68D3F88024C3F880240022C3F800257B\n:1012100000212B6894F83220C3F81011D3F8100184\n:10122000C3F82011D3F82001C3F82411D3F8240106\n:10123000C3F84411D3F84401C3F85811D3F8581136\n:1012400043F307335201002BD2B212DB0D49C81809\n:1012500080F800230122580903F01F0302FA03F368\n:1012600041F82030BDE8F8834020B847013EC4D1A2\n:10127000C6E7054903F00F03CA54F3E758190020E5\n:10128000A034000000E100E014ED00E000F01F03D6\n:1012900040090BD1084A02EB8302D2F80012074B37\n:1012A0000B4043F00203C2F8003270470022803343\n:1012B00052F82330FFDE00BF00258450F0F8FCFF19\n:1012C0002DE9F3479046502290F80490514C02FBD0\n:1012D00009FA04EB0A070D4697F84810064600295C\n:1012E00040F0858009EB890004EB001004F043FC1A\n:1012F0002B69AA6944F80A306B69BB6302B1013AF1\n:10130000502303FB0943DA632A6A042A81BFEA698E\n:101310009A610022DA6195F83130B8F1000F37D0C8\n:1013200083F001073F0233B1502303FB0943DA6C1A\n:1013300042F00102DA64502303FB09440123C4F89C\n:10134000048084F848303468D4F84C2112BB294614\n:101350003046FFF7DFFE0822002334688DF807309F\n:10136000C4F800250DF10702C4F84425C4F8483537\n:1013700001230A25A360E360D4F85831002B38D14B\n:10138000254B402043F001039847013DF4D128E06C\n:101390004746C8E708224FF06409C4F80025636097\n:1013A0001D4B43F0010AD4F84431F3B9D4F8243189\n:1013B000DBB94FF42070D047B9F10109F3D14B46A6\n:1013C0000022C4F84C21D4F84C11C4F81021D4F8F0\n:1013D0001011C4F84421D4F84411C4F80025002B9E\n:1013E000B5D10E4802B0BDE8F0870123E8E70C480C\n:1013F000F8E70023C4F820319845D4F82021C4F838\n:101400000035084B08BF00231F4333680648C3F864\n:101410000473E7E758190020A03400000100AD0B69\n:101420000C00AD0B10020A000000AD0B2DE9F047D7\n:1014300084460F469E4616460579002A51D04FF045\n:10144000500ADFF8BC80D0F800900AFB058ADAF871\n:10145000044074B96BB9DAF82C50002D43D1CAF8A6\n:101460002C20FFF755FD0446CAF82C502046BDE855\n:10147000F0871EF0030F07D0734632463946BDE8A9\n:10148000F0476046FFF744BD07F06043B3F1005FEB\n:101490004FF0500414D104FB058400F015FBE26A00\n:1014A0001AB1134C00F018FBE0E7C4E90A7605EB2B\n:1014B0008505484608EB051100F0E3FD0D4CF1E70A\n:1014C00004FB0584A36B83B100F0FEFAE36A002BF2\n:1014D000E7D1E36C012243F00203C4E90A76E36436\n:1014E000E5E7054CC2E7024CC0E7044CBEE700BF8D\n:1014F0000B00AD0B0000AD0B0600AD0B0A00AD0BF1\n:10150000581900202DE9F84350230679284D046826\n:1015100003FB0653D4F80473DA6AC4F8087322B9DB\n:101520002448C4F80473BDE8F88306EB860005F18F\n:101530004C024FEA001802EB0012D2E8EFCF4CF059\n:10154000080CC2E8E0CF0028F7D10122E26031B3F5\n:10155000D96C01EA02094946204600F010FD002836\n:10156000FAD000F0B1FAB9F1000F1CD1D4F8203153\n:1015700053B10123E360D4F85831002BFBD020464F\n:1015800005EB080100F072FD00F0A6FA0023C4F894\n:101590002031D4F82021502202FB0655EB62064888\n:1015A000C4F80473BFE7D4F85831002BE7D1EBE758\n:1015B000581900200500AD0B0000AD0B2DE9F843D4\n:1015C00090F80480484F08EB8806360106F14C0578\n:1015D00004683D44D5E8EF3F43F48052C5E8E02F6E\n:1015E0000028F7D1DB047BD4480705D5502303FB43\n:1015F00008739B69002B75D03C4B3E44C4F80833FC\n:101600000823C4F800350023C4F81031D4F81021A1\n:10161000C4F84C31D4F84C21C4F844310A03D4F84E\n:1016200044318B0303F4804302F400524903134313\n:1016300001F400410B43D5E8EF2F1A43C5E8E12F31\n:101640000029F8D14FF0500909FB0879D9F8083082\n:10165000BBB9D9F8043063B9D5E8EF3F23F47F0371\n:1016600023F47043C5E8E23F002AF5D12048BDE8E5\n:10167000F8830321304600F08FFCD9F80830002BA6\n:10168000EAD0D4F80433590713D5502303FB087369\n:101690005A6872B1DB6C1A030BD5D5E8EF3F23F41F\n:1016A0000023C5E8E23F002AF7D10621304600F0CA\n:1016B00073FC502303FB0873DB6CDB0401D40D487F\n:1016C000D5E7D4F84C310BB901232360502303FB39\n:1016D00008735B68002BF2D0044BC4F80433EEE7C8\n:1016E0000548C4E70548C2E75819002010020A005F\n:1016F0000200AD0B0000AD0B0B00AD0B0800AD0BF5\n:101700002DE9F84F056801F06043D5F804A3B3F163\n:10171000005F90F804B089461746C5F808A37CD14D\n:101720005023404E03FB0B639A6852B15B68002B59\n:101730005ED1D5F81021002A5AD0C5F81031D5F85D\n:101740001031502404FB0B64E269C4E90297002ABB\n:101750003CD00BEB8B0897424FEA081824D24FF08D\n:10176000000B3A46A1694846C4E902BB04F0DEF921\n:10177000E269A069D21BC119E26104F0A1F96368B2\n:101780006BB1B04449463A46404600F016FCE36C63\n:10179000990404D55A4641462846FFF76FFB224874\n:1017A000C5F804A3BDE8F88F4846A16904F0BEF966\n:1017B000E36963620023E3611C4B9844D8E8EF3F80\n:1017C00043F40023C8E8E23F002AF7D1502303FB8B\n:1017D0000B63586ADA689968DB6C121A0144C5F821\n:1017E0003415C5F83825DA04D9D501232B60D6E79E\n:1017F000502303FB0B6633698BB9F36CC5F8349542\n:101800005B04C6E90497C5F83875C8D5D5F8003229\n:1018100043F02003C5F80032C1E70548C0E705489A\n:10182000BEE700BF581900200000AD0BA41900202E\n:101830000A00AD0B0B00AD0B50232DE9F0413C4DE0\n:1018400090F80480046803FB0853DB6CD4F80002B2\n:1018500003F48223B3F5805F68D1D4F80463C4F83D\n:10186000086309B9830656D4D4F8003223F0200364\n:10187000C4F800324FF4842308EB8801090101F118\n:101880004C072F44D7E8EF0F1843C7E8EC0FBCF123\n:10189000000FF7D12AB9502303FB08535B68002BD4\n:1018A00041D1244BC4F8083301236360D4F8443198\n:1018B000002BFBD00023C4F84C31D4F84C21C4F8E1\n:1018C0001031D4F81021C4F84431D4F844315023F5\n:1018D00003FB0853DA6C12F400421FD1DA61204690\n:1018E00000F07FFB502303FB08550023AB602B6106\n:1018F000D7E8EF3F23F47F0323F47043C7E8E23FC8\n:10190000002AF5D126F4202626F404760A48C4F8E5\n:101910000463BDE8F0814FF48033ADE720462944ED\n:1019200000F071FBDBE701236360EFE70348F0E7BA\n:101930005819002010020A000000AD0B0500AD0B85\n:101940002DE9F8432F4A03680679C3F8082343F3C7\n:101950000733002B04460DDB01215A0903F01F0356\n:10196000994002F12003284A42F82310BFF34F8F19\n:10197000BFF36F8F012220461146FFF75DFF012163\n:101980002046FFF7BFFD50212279204D236801FB3F\n:101990000252D46CD3F80C05E201D3F81495D3F8B5\n:1019A0000875D3F8108509D54FF0FF32C3F80C2520\n:1019B000C3F81425C3F80825C3F81025A30116D5CC\n:1019C000411C01D0FFF762FCB9F1FF3F02D048464D\n:1019D000FFF75CFC22010AD5B8F1FF3F02D0404678\n:1019E000FFF754FC7B1C02D03846FFF74FFC502316\n:1019F000002203FB0653DA6483F848205A60BDE8EE\n:101A0000F88300BF10034A0000E100E058190020ED\n:101A100010B5502402790B4B04FB02335B686BB9A1\n:101A20000368D3F810211AB9D3F80025082A07D083\n:101A300011B1D3F83C350B60034810BD0348FCE7F7\n:101A40000348FAE7581900200000AD0B0800AD0B61\n:101A50000B00AD0BF7B505461646FFF751FE164BCA\n:101A60000446984222D1032128462F79FFF7A6FD8C\n:101A7000124B984201D0A0421BD15022104B02FBC6\n:101A800007335B6893B90C4C284601A9FFF7C0FFE8\n:101A9000A042F9D1019BB34207D301222846114647\n:101AA00003B0BDE8F040FFF7C7BE064C204603B0C8\n:101AB000F0BD0446FAE700BF0000AD0B0B00AD0B14\n:101AC000581900200800AD0B72B6024A13680133A2\n:101AD00013607047A8190020034A1368013B136084\n:101AE00003B962B6704700BFA81900200722024B55\n:101AF000C3F80423704700BF00300050084BD3F8F0\n:101B00000801D3F80021003818BF012080000AB175\n:101B100040F00100D3F804310BB140F002007047EF\n:101B2000003000500022074BC3F80021D3F8001109\n:101B3000C3F80421D3F80411C3F80821D3F80831FD\n:101B4000704700BF0030005000231720094A03F5FA\n:101B5000C0710133202B42F82100F8D10023172057\n:101B6000044A03F5E0710133202B42F82100F8D13B\n:101B7000704700BF0030005010B59DF80840C00B02\n:101B800043EA0423064CC90B43EA0213884200D9F6\n:101B900010BDC2B202F5C07244F822300130F5E740\n:101BA0000030005010B59DF8084000F1604043EA55\n:101BB000042301F16041074C400B490B43EA021337\n:101BC000884200D910BDC2B202F5E07244F822305A\n:101BD0000130F5E700300050C0F30E03C3F50043B9\n:101BE000B3FA83F3C3F11B03064AC0F3C730DBB279\n:101BF00040F4807043F48073C2F80005C2F80435E5\n:101C0000704700BF0030005043030148184470473C\n:101C1000FF1FF81F054BC0F30730090241F03001E8\n:101C200000F5007043F82010704700BF00300050EE\n:101C3000044BC0F30730090200F5007043F8201090\n:101C4000704700BF00300050003A18BF0122003931\n:101C500018BF012103688900074841EA420118635F\n:101C600059605A6842F001025A60BFF34F8FBFF3C8\n:101C70006F8F0020704700BF04AAFF0008B5FFF770\n:101C80003DFF044B162118600348BDE8084000F0F2\n:101C90003AB900BFEC1F0020A63400000C22014915\n:101CA00003F044BFEC1F002010B501F006FB044612\n:101CB00090B901F097FF044680B90A4A2E21D368F3\n:101CC000094823F008031B041B0C43F0BF6343F4D3\n:101CD0000033D36000F017F9204610BD45F22354BD\n:101CE000FAE700BF00ED00E0BC340000024B1860D2\n:101CF000024B002019607047FC1F0020F81F0020D5\n:101D00001FB5374C426923685A6282699A62C26978\n:101D1000DA62026A1A63426A5A63826A9A63C26A20\n:101D2000DA63026B1A64426B5A60826B9A60C26B10\n:101D3000DA60026C1A61426C5A61826C9A61C26C00\n:101D4000DA61026D1A62826D5A65C26D9A65026F20\n:101D5000DA65026E1A66826E5A66426F9A66EFF311\n:101D600088825A64EFF389829A644268DA64EFF3F6\n:101D700094821A65026801A81A67FFF78FFF23682B\n:101D8000019ADA6601221A70164B1C686408640016\n:101D90002046214622462346B7EE000AF7EE000A07\n:101DA000B7EE001AF7EE001AB7EE002AF7EE002A97\n:101DB000B7EE003AF7EE003AB7EE004AF7EE004A07\n:101DC000B7EE005AF7EE005AB7EE006AF7EE006A77\n:101DD000B7EE007AF7EE007A03F081FF04B010BD91\n:101DE000FC1F0020F81F002000B5A1B001A8FEF7DD\n:101DF000C9FD029B63B15B060AD4019B23F0020379\n:101E0000052B05D1044B1B6813B101A8FFF778FF20\n:101E100021B05DF804FB00BFF81F0020BFF34F8F17\n:101E20000549064BCA6802F4E0621343CB60BFF376\n:101E30004F8F00BFFDE700BF00ED00E00400FA0592\n:101E400038B543680C2B30D14B68042B2DD14FF0A3\n:101E5000FF3304680D6821682B6031B3A26822B398\n:101E60000623012001F081F9F8B96168104B99420D\n:101E70000DD9A2680F485318834202D9F43883421F\n:101E800010D8206803F052FE0020286038BD0A4BAD\n:101E900099420AD9A26809488B188342F1D90730C0\n:101EA000814202D906488342EBD90220EEE700BF07\n:101EB000FF01FF002C02FF002F01FF003801FF008F\n:101EC0004C01FF004FF47F03D3F83001D3F83431D5\n:101ED000BFF34F8FC0F30B00A0F1090358425841E4\n:101EE00070474FF47F02D2F83031D2F83401BFF39B\n:101EF0004F8FC3F30B03092B03D1431E58425841A4\n:101F0000704700207047FEF7CDBD012070476FF08D\n:101F100003007047002382B00193019802B070471C\n:101F20006FF003007047002382B00193019802B064\n:101F30007047022814BF6FF0030000207047FEF7BF\n:101F40002DBE30B501EB8202914200D130BD51F877\n:101F5000044B04F07F037F2B4FEA144408BF4FF07B\n:101F6000FF33032CF0D8DFE804F002040608036016\n:101F7000EAE74360E8E78360E6E7C360E4E7034637\n:101F8000D0F8580130B931B9D3F82001003818BF62\n:101F9000012070470120704730B504460D4685B0DA\n:101FA00010220021684603F0E6FDD4E900136846DC\n:101FB0008DF80050984705B030BD7FB504460E46F9\n:101FC000154600211022684603F0D5FD012368461E\n:101FD0008DF80030CDE90165D4E90013984704B0CD\n:101FE00070BD10B50446FFF76FFDD4F8583133B11A\n:101FF000D4F8043313F4800308BFC4F80035BDE8F7\n:102000001040FFF769BD70B50D4604460522AA21B0\n:10201000D0F83C65A86903F0AEFD0522AB69C4F8B1\n:102020003435C4F838250022C4F81021D4F8101132\n:102030000121E162D4F810110029FBD0C4F84C2131\n:10204000D4F84C11C4F81021D4F81021D4F83C2550\n:102050009642EA6109D15A1E043312F8011FAA29D7\n:1020600003D19A42F9D10023EB6170BDD0F8583109\n:1020700033B1CB6CC3F30032DB0458BFC0F800258A\n:10208000704770B5CE6C0D4606F001010446FFF7AF\n:1020900076FF38B94FF4800346F00406EE64C4F8C6\n:1020A000043370BDD5E90A365AB1296CEA6B761A49\n:1020B000964228BF164619443246A86B03F036FDF7\n:1020C000AB6B4FF0FF326A641BB1C4F84435C4F8FF\n:1020D00048650023C4F82031D4F82021C4F85831D1\n:1020E000D4F85821C4F85031D4F850310823C4F83A\n:1020F00000354FF48003C4F804330123A360D0E714\n:10210000C0037047C00300F5FF407F3070470020D8\n:1021100070471F2070474FF40040704700F57F4024\n:10212000C03040037047402070475F2070474FF435\n:10213000005070474B6830B513F01F052DD1026871\n:102140000C681068C0F30720844226D2506855609E\n:1021500094600C7C23F01F0304F001041C438B7C6F\n:10216000DB0003F018031C434B7C5B0003F0060309\n:102170002343D3608B68013B23F01F04CB685B00D3\n:1021800003F00E03234343F0010313615060BFF3D8\n:102190004F8FBFF36F8F284630BD0120FCE70368E7\n:1021A00000205A68C9B258609960D86018615A60B6\n:1021B000704770B50546002403681E68C6F30726FD\n:1021C000B44201D1002070BD21462846FFF7E7FF49\n:1021D0000134F5E70020704708B5FFF705FE72B639\n:1021E00020BFFDE7002070470020704770470B4676\n:1021F00070B5114618B1032806D0032070BD1846EB\n:10220000BDE87040FFF71CBE5A68082A01D00220C2\n:10221000F4E74C68042CFAD11D684FF0FF330E68C8\n:10222000296833600029F2D06B68002BEFD0262399\n:102230007422012000F099FF0028E8D122462323D0\n:102240000120696800F091FF04460028DFD1D5E93C\n:102250000001FFF74BFD30602046CFE730B50C465C\n:10226000154685B038B139B1FFF74DFE041E03DACB\n:10227000204605B030BD044640F60D23ADF80E30C3\n:1022800009230B4901A805F00F028A5C2D091A5495\n:10229000013B012BF7D147F630030C21ADF8043098\n:1022A000FFF731FE0028B4BF04462418E0E700BF62\n:1022B000EA34000008B5194B83F30A88184800F087\n:1022C000E7FF40BBFFF7F0FC28BBFFF783FF10BB25\n:1022D000FFF788FFD8B9FFF789FF00F067FA3521CB\n:1022E0001048FFF710FE17210F48FFF70CFE1821CA\n:1022F0000E48FFF708FE00F03FFC21210C48FFF7D5\n:1023000002FE00F03FFA00F0CDFE002008BDFFF70E\n:102310006BFF0028DFD000F021FEEEE70004002074\n:10232000AC190020FA3400002F35000046350000BB\n:102330005E3500002DE9F04F93B00093574B814676\n:10234000D3F800B00BB2002B0D461646C1F302685D\n:10235000C1F3024A04DA6FF0800013B0BDE8F08FD9\n:102360008B42C36001D10020F7E708EB0A03042B7E\n:10237000F1D8CC0F64014FEAC80731463A46DBF882\n:10238000080044F0020300F0F0FE01460028E2D10C\n:10239000202202A803F0EFFB3A46314602A84FEA9A\n:1023A000CA0603F0C3FB32460099DBF8080044F08C\n:1023B000060300F0DAFE07460028CCD101462022B1\n:1023C0000AA803F0D8FB324600990AA802AE03F02F\n:1023D000ADFB33460137B84514D91A46BC460DE06B\n:1023E000D2F808E0D16818687144884203D259686D\n:1023F00008448645AFD30CF1010C0832C445EFD137\n:102400000833E7E715F0006F18BF202409F118071B\n:1024100007EB880844F00203474513D1202C26D14E\n:10242000002D02DBC5F3C0456C0109F128050AAE99\n:1024300005EB8A0A44F0060455451AD1009BC9F8F9\n:102440007C3090E77268DBF8080056F8081B0193AF\n:1024500000F08BFE00287FF47EAF56F8042C019B21\n:1024600047F8042F56F8082C38633A62D4E7002462\n:10247000DBE77268234656F8081BDBF8080000F01B\n:1024800074FE00287FF467AF56F8043C45F8043F1B\n:1024900056F8083C28642B63CEE700BFB019002033\n:1024A0002DE9F0411F461A4B0D461646D3F8008021\n:1024B00000F051FF044608B900F050FDE368002B1E\n:1024C00001DA00F04BFD032D01D900F047FD04EBCC\n:1024D0008504E569E36CED1A16D006233A463146C9\n:1024E000D8F8080000F041FE08B100F037FDBD4209\n:1024F00028BF3D46E36CE16B2A461944304603F0A1\n:1025000015FBE36C2B44E3642846BDE8F08100BF73\n:10251000B01900202DE9F0411D461A4B0F46164612\n:10252000D3F8008000F017FF044608B900F016FD4C\n:10253000E368002B01DA00F011FD032F01D900F050\n:102540000DFD04EB8704E36AE26E9B1AAB4201D2F5\n:1025500000F004FD02232A463146D8F8080000F0B6\n:1025600004FE08B100F0FAFCE36EE06D2A46314645\n:10257000184403F0DBFAE36E00202B44E366BDE869\n:10258000F08100BFB01900202DE9F04105464FF061\n:1025900001080E4E0E4B37685C681CB92846376040\n:1025A000BDE8F0812268936813F400700CD1A36930\n:1025B000012B09D0D368346023B19847002801DA91\n:1025C00000F0CCFCC4F81880246AE6E7B0190020BB\n:1025D000B419002038B5044610B96FF0800038BD3A\n:1025E0008368002BF9D01A68002AF6D05D68002DA8\n:1025F000F3D00B4BE861A8691D6020B92B68DB683C\n:1026000053B90123AB61A36804F10C001B681B697B\n:10261000984701232360E2E798470028F1DADCE7D6\n:10262000B01900204268024B08461A60704700BF8C\n:10263000B0190020002330B505680446AA68C0E937\n:10264000063312F4406F85B01CD012F4006202D041\n:1026500000F0C2F90246A96A2B6968460B440731AB\n:1026600023F0070321F00701CDE9021300930023B3\n:1026700005490193EB6800F09FFA684600F04EFEB2\n:10268000024B1C6005B030BD89250000B019002048\n:1026900001F00C0370B50C2B0CBF154605464A061D\n:1026A0000C461ED58B0658BF2835AB6913F8026C53\n:1026B00084F00803C3F3C003C6F38012934201D031\n:1026C00000F04CFC16F0A00F1ED176B1032E17D0EF\n:1026D00032461C210E48FFF7C1FD6FF083032B60CB\n:1026E0000CE000F03BFC2026E2E700F03BFB044658\n:1026F000FEF70EFA2146074800F00AFE204670BD9C\n:10270000284600F03FFAF9E732461F210248E2E787\n:102710007F350000F80B00209B350000EFF30880A8\n:102720007146EFF30982EFF30B830CB406B4FFF7A5\n:10273000AFFF8646009900F0080001F00801401A3A\n:1027400002DC12DB04B070471EF020031CBF2DE931\n:10275000F00FBDF1080D0A4C254626462746A04637\n:10276000A146A246A34630B4704704B030BC1EF068\n:1027700020031CBF1DF1080DBDE8F00F04B0704729\n:10278000A5EDF5FE084BDA6882F07F4282F47F0205\n:1027900042F48042DA6000221A765A76DA76DA77E4\n:1027A000602283F82220704700ED00E0114BD3F83F\n:1027B000882042F47002C3F88820BFF34F8FBFF324\n:1027C0006F8FD3F88C2042F44062C3F88C20D3F88A\n:1027D000342242F08042C3F83422D3F8342242F04B\n:1027E0003C42C3F834224FF0E022D36843F4200384\n:1027F000D360704700ED00E0009A164B9A4227D153\n:1028000001229043014641EC100B41EC110B41ECCD\n:10281000120B41EC130B41EC140B41EC150B41EC8A\n:10282000160B41EC170BEFF3148222F0040282F333\n:102830001488BFF36F8F02460346044605460646DA\n:10284000074680468146824683468446864604473C\n:10285000FEE70000A5EDF5FE2DE9F041044618B9AC\n:1028600043F6DA30BDE8F0810029F9D0836800250D\n:1028700013F4406F14BF01230023134F43F00203EE\n:10288000DFF848800B6000F13006236AAB4201D8C4\n:102890000020E7E71421A369E26906EB830301FB4B\n:1028A00002330C226A4399188968090709D59B5895\n:1028B000BB4201D04345D3D1186810B10121FFF7C5\n:1028C000A9F90135E1E700BFB4100020AC100020E9\n:1028D00070B5324C86B03248FFF76BFC2368012597\n:1028E0005A1C01932F4B2260DA681B694FF4407623\n:1028F000039300232A4801A902920495ADF81460BD\n:102900008DF81630FFF716FC18B143F6DA3006B032\n:1029100070BD23688DF816005A1C0193224B204885\n:102920000293224B01A9CDE903352260ADF8146072\n:10293000FFF700FC0028E8D123688DF816005A1C28\n:1029400001931B4B164802931A4B01A9CDE903359D\n:102950002260ADF81460FFF7EDFB0028D5D12368A5\n:102960008DF816005A1C0193134B0D480293134B1C\n:1029700001A903930223049340F201132260ADF8EE\n:102980001430FFF7D7FB04460028BED12A4629465B\n:102990000348FFF759F92046B9E700BFBC190020EA\n:1029A000A81000202C36000060360000005F0000F8\n:1029B0002006000020060000000C0020200C002053\n:1029C000014B1868704700BF2C360000014B1B6894\n:1029D000186870472C360000014B1B685868704718\n:1029E0002C36000008B5FFF749F9FFF79BF80A4BB2\n:1029F0005A68103AD3B2120609D401215A0903F0D9\n:102A00001F03994002F16003044A42F82310BDE815\n:102A1000084000F0A3BA00BF00ED00E000E100E0D4\n:102A20000020034BD8765A6A42F470225A627047EB\n:102A300000ED00E00349044BCA68002092B2134342\n:102A4000CB60704700ED00E00800FA0500224FF06F\n:102A5000FF300D4B02F1A0010132102A43F8210092\n:102A6000F8D1D3F88022002022F00802C3F8802297\n:102A7000D3F88022D3F8802222F40072C3F8802297\n:102A8000D3F88032704700BF00E100E008B5FFF7DF\n:102A90002DF80822024BC3F880211A60002008BDDF\n:102AA00000E100E0064BD3F8D02022F00102C3F889\n:102AB000D020D3F8D02042F00202C3F8D0207047D3\n:102AC00000ED00E037B50124FFF73EF8134D224634\n:102AD000052347F6FF7100200094FFF74DF82246CA\n:102AE00007234FF000500E490094FFF75BF8D5E93B\n:102AF0000101072300220094FFF73EF8072300227C\n:102B0000084909480094FFF74DF82969E868013938\n:102B1000FFF762F8002003B030BD00BF2C36000084\n:102B2000FF7F0020FFFF03200080002038B5002435\n:102B30000E4D002155F8040B0134FFF779F81C2CD9\n:102B4000F7D10020012240F20111094BC3F88004A3\n:102B5000C3F88424C3F8C004C3F8C424054AC2F8E7\n:102B600040154FF48072C3F8402438BDBC350000D6\n:102B700000300050009003500122014B1A60704752\n:102B8000C019002010B50446094B228918686168F5\n:102B90000623806800F0E9FA28B9064B1B68012B70\n:102BA00001D100F087F86FF08603236010BD00BFED\n:102BB000B0190020C01900202DE9F84305461E4633\n:102BC0000068134B9046AA68A0F1080440E902335C\n:102BD000503890420F4601D200F0C0F9A4F14809E4\n:102BE00048220021484602F0C6FF002344E907338B\n:102BF00044F8143C4FF0807344F8043C6FF0020337\n:102C000044E9036744F8208CC5E90093BDE8F883E4\n:102C1000A5EDF5FE30B5094C024661680023084673\n:102C200040B105686D68954205D11BB1826860604E\n:102C30009A60816030BD03468068F1E7441A002045\n:102C400038B504462AB10B689B68DB0512D5002015\n:102C500038BD0B4B1D680DB900F080F900222B68C0\n:102C600099692C338A4203D053F8040FA04204D14F\n:102C70009142ECD16FF0FC00EAE70132F2E700BFCD\n:102C8000B019002070B504460E46154600213022CA\n:102C90000C3002F070FF064B20461B68A660636094\n:102CA000656100F001FA00232061236070BD00BF60\n:102CB000B0190020044B1B681BB11B680BB158688E\n:102CC00070474FF0FF307047B0190020064B10B529\n:102CD0001C680CB900F042F923689B6813F4406F3C\n:102CE00014BF0120002010BDB01900202DE9F843C9\n:102CF000904607460C46FFF7E9FF024624B96FF0FD\n:102D000080042046BDE8F8836300F8D5144B04F036\n:102D10001F0153F82150002DF1D02B682946586827\n:102D2000FFF78EFFB0B9C4F30721284600F001FB7E\n:102D300004460028E3D1EFF3108972B600F0C1F920\n:102D4000064689F3108840B142462946FFF79AFFAC\n:102D50003E60D6E76FF08104D3E76FF08204D0E7DE\n:102D6000C41900202DE9F04100F0AAF90023134E08\n:102D7000134F73607B6000F0E5F8DFF84880304661\n:102D800000F022F80446A8B139468023424600F0FC\n:102D90007FF80546204600F011FB206804F1080189\n:102DA000FFF75AFD08B100F0D9F829462046FFF791\n:102DB00041FCE4E7BDE8F04100F071B9B41900202E\n:102DC000441A0020C4190020F8B5054608B900F0DF\n:102DD000C5F82A4E346814F1290F4CD8284B1968CD\n:102DE00004F128038B4246D2236AA26903EB430312\n:102DF0000C321344606AE269024402EB820213441B\n:102E00009B00E31801D2994201D200F0A7F8236891\n:102E10001C4A1B0C1B04934201D000F09FF823882E\n:102E2000B3F5817F01D300F099F86368002B01DAD4\n:102E300000F094F8144A176807F124031360134A4A\n:102E40001268934201D900F089F8236AA26903EB62\n:102E500043030C32616A1344E2693C600A4402EBAA\n:102E600082021344326802EB83026B6832603B6279\n:102E70006F603846F8BD0027FBE700BFC410002094\n:102E80004836000000005F5FC0100020443600009C\n:102E90002DE9F74F0F4690460646009300B109B959\n:102EA00000F05CF8D6F800A0DAF818200AF1300338\n:102EB00003EB820BDAF81C40204B1D6854B10C2246\n:102EC00002FB04541C601E4B1B689C4201D900F09D\n:102ED00045F82C4625464FF00009DAF81C204A45F3\n:102EE00000D91CB9002003B0BDE8F08F142303FB08\n:102EF00009B20023C5E900269268AB60930516D598\n:102F0000B8F1000F02D0009B802B03D0019200F09B\n:102F100025F8019A12F0E00FD0B202D158F8202023\n:102F20001AB1019000F01AF8019848F820507A6818\n:102F300009F10109AA607D600C35CEE7BC100020C4\n:102F40004036000008B5FFF73BFD034B9860FFF7E4\n:102F50003DFD80F3888808BD00ED02E0FFF73CB935\n:102F60008230012808B506D8FFF7B0FE18B9BDE8D1\n:102F70000840FFF7F3BF08BDF8B506460D4600F060\n:102F8000EAF9044608B9FFF7E9FFA7680FB9FFF7A8\n:102F9000E5FFE368991C20D05A1C21D1A5B315F197\n:102FA000820F13D102232360EFF3108672B62946F5\n:102FB0002046FFF737FB054686F310882368022B6F\n:102FC00024D1204600F086F82846F8BD15F1830F7D\n:102FD000EAD0FFF7C3FF0025E6E702232360FAE704\n:102FE000002BF6DB204600F0DAF93B689B689B0576\n:102FF00001D50223236015F1810FD5D16369002B20\n:10300000D2DBFFF7ABFFCFE73546F4E700232360C1\n:10301000DAE708B5FFF7E0F86FF0830008BD2DE9A7\n:10302000F3410E46174698460546FFF74FFE00F05F\n:10303000A6F92946024601A8FFF758FE044660B9E2\n:1030400043463A4631460198FFF774F9044640B1C9\n:103050006B0002D5019800F03DF8204602B0BDE8B3\n:10306000F0810198FFF7B6FA0446F6E740F2011046\n:10307000704770B50546FFF729FE06462846FFF75C\n:10308000C9FD044608B9002070BD014632462846F5\n:10309000FFF7D6FD0028F6D12368D868F4E76FF073\n:1030A0000200704700DF70474FF480507047B0F562\n:1030B000805F03D1EFF30B808038704700207047AA\n:1030C0007047EFF30B8000F1800383F30B887047A8\n:1030D00000207047EFF30B83803B83F30B8870472E\n:1030E00080EA0000E1EE100A00EE100A00EE900AFD\n:1030F00001EE100A01EE900A02EE100A02EE900AAA\n:1031000003EE100A03EE900A04EE100A04EE900A91\n:1031100005EE100A05EE900A06EE100A06EE900A79\n:1031200007EE100A07EE900A08EE100A08EE900A61\n:1031300009EE100A09EE900A0AEE100A0AEE900A49\n:103140000BEE100A0BEE900A0CEE100A0CEE900A31\n:103150000DEE100A0DEE900A0EEE100A0EEE900A19\n:103160000FEE100A0FEE900A70472DE9F0410E465F\n:103170001546002A4AD0C4074BD480F00204C4F399\n:103180004004A40003F00602062A4AD144F00104D8\n:1031900022462946304602F077FBB8BB46E800F2EB\n:1031A00005EB0608100E120208F1FF3747E800F39E\n:1031B0004FEA136166D51B0264D5884262D046E8A7\n:1031C00000F547E800F7FEF7A2FF2D0E85424FEA13\n:1031D000176734D3FEF79DFF874230D82846FEF7A5\n:1031E00091FF2246C6F101010144304602F04CFB3A\n:1031F000002847D00135AF4218D83846FEF780FF87\n:103200002246A8EB000102F03FFBD8B30020BDE846\n:10321000F081EFF39484A40004F0040444F0120459\n:10322000B0E7990731D544F00804B1E72846FEF726\n:1032300067FF0646FEF76FFF22460146D5E7FEF719\n:1032400072FF85421ED3FEF770FF87421AD82846C8\n:10325000FEF7DAFC2246C6F101010144304602F0D5\n:1032600013FB78B10135AF4203D83846FEF756FF5D\n:10327000C6E72846FEF752FF0646FEF758FF2246ED\n:103280000146EBE76FF07F00C1E74CF2DA20BEE7C2\n:1032900010B50446FFF706FCFFF714FC10B143F627\n:1032A000DA3010BDFFF742FC0028F8D1FFF710FB21\n:1032B0000028F4D102232360F3E708B5FFF7B0FB41\n:1032C00010B143F6DA3008BDFFF7B4FB0028F8D19F\n:1032D00000F01CF80028F4D162B6FDF703FCFFF7FC\n:1032E000B5FB0028EDD1FFF7D1FB0028EBD0E8E7D4\n:1032F000EFF30880EFF309812DE9F00F6B4672467A\n:10330000FDF746FB08B0FFF76DFBFEE700207047B6\n:1033100004460D46FFF7E4FEA54628470368283318\n:1033200083F30988836883F30B88BFF36F8F40684A\n:10333000704703689A68DB6812F4806F05D18B428E\n:103340002CBF00206FF0FA0070478B420CBF0020AA\n:103350006FF0FA00704710B5FFF7A9FE0446FFF7BB\n:10336000B7FE50B9FFF7A6FCA3685B681B685B68F3\n:10337000834218BF0024204610BD0024FBE738B567\n:10338000054648B100F01BF8B5EBD07F044601D0EC\n:10339000FFF7E4FD204638BDFFF78CFCF4E700237F\n:1033A00010B500F1280252F8041F19B1C16F146C56\n:1033B00019444C600833202BF5D110BD70474FF0F5\n:1033C000FF307047000001464154414C20455252A5\n:1033D0004F523A2000486172644661756C740D0A60\n:1033E000004D656D4D616E616765206661756C7439\n:1033F0000D0A004275734661756C740D0A005573B1\n:103400006167654661756C740D0A0053656375727A\n:10341000654661756C740D0A005265736572766558\n:103420006420457863657074696F6E2000506C612C\n:1034300074666F726D2065787465726E616C206958\n:103440006E7465727275707420284952516E293AF3\n:1034500020000000290B00000B1F0000D90D000008\n:103460008D0D0000331F00003F1F0000610B0000A6\n:103470000F1F0000490B0000550B0000150C000049\n:10348000151F0000211F0000271F00001D00000065\n:10349000100004001C0002001100060003020202DA\n:1034A0000338FDD87047506C6174666F726D2045AB\n:1034B0007863657074696F6E3A0D0A00416C6C2018\n:1034C00070696E732068617665206265656E206341\n:1034D0006F6E66696775726564206173206E6F6ECA\n:1034E0002D7365637572650D0A00303132333435E2\n:1034F000363738394142434445461B5B313B333410\n:103500006D5B536563205468726561645D2053652B\n:103510006375726520696D61676520696E6974699C\n:10352000616C697A696E67211B5B306D0D0A00540E\n:10353000462D4D20466C6F6174204142493A204827\n:103540006172640D0A004C617A7920737461636B57\n:10355000696E6720656E61626C65640D0A001B5BB5\n:10356000313B33346D426F6F74696E672054462D62\n:103570004D2076322E302E301B5B306D0D0A0055FB\n:103580006E6B6E6F776E2053504D205356432072F2\n:1035900065717565737465643A2000556E6B6E6F66\n:1035A000776E20535643206E756D6265722072658A\n:1035B000717565737465643A200000000040005026\n:1035C000005000500080005000A0005000B000509B\n:1035D00000E0005000F000500000015000100150C9\n:1035E0000040015000500150007001500080015017\n:1035F00000B0015000C0015000D0015000E0015067\n:1036000000F0015000000250001002500020025053\n:103610000030025000400250006002500080025012\n:1036200000A002500090035000258450008000004C\n:1036300000800000FFFF0F00007C00000080000001\n:0C3640002C1200201412002000020000D8\n:10366000009A204B9A4208D110B502F0A7FA02F056\n:10367000A9F906BC96460C46744702F0E3FA009A94\n:10368000184B9A4208D110B502F0A4FA02F09AF948\n:1036900006BC96460C46744702F0D4FA0CB4029A63\n:1036A000104B9A420CD14FF0004319430CBC10B59B\n:1036B00000F09EF802F086F906BC96460C46744768\n:1036C00002F0C0FA009A074B9A4202D106490868F4\n:1036D000744702F0B7FA009A024B9A4200D174473D\n:1036E00002F0B0FAA5EDF5FE801000200348044B6F\n:1036F000834202D0034B03B118477047A811002042\n:10370000A8110020000000000548064B1B1AD90F25\n:1037100001EBA301491002D0034B03B118477047D6\n:10372000A8110020A81100200000000010B5064CD0\n:10373000237843B9FFF7DAFF044B13B10448AFF322\n:1037400000800123237010BDC0180020000000007D\n:10375000EC5E000008B5044B1BB104490448AFF30C\n:103760000080BDE80840CFE700000000C41800203A\n:10377000EC5E0000A3F5803A704700BF174B002BAA\n:1037800008BF134B9D46FFF7F5FF00218B460F4600\n:103790001348144A121A02F0EEF90E4B002B00D017\n:1037A00098470D4B002B00D09847002000210400C3\n:1037B0000D000D48002802D00C48AFF3008002F045\n:1037C000DBF820002900FEF775FD02F0C1F800BF0C\n:1037D000000008000000000000000000F80B0020BE\n:1037E000A8110020B8280020000000000000000000\n:1037F0002DE9F84304460D4616461F46EFF30583B0\n:10380000C3F308030BB1FFF7A9FBDFF834903B4685\n:10381000204632462946D9F80080FFF700FCD9F847\n:1038200000300446984506D0DB6901461869BDE8BA\n:10383000F843FFF7A1BBFFF793FB2046BDE8F883F1\n:10384000B019002001680E4A0346914215D1C169A2\n:10385000A2F11022A2F1EF1291420ED18268012A48\n:103860000BD8C26812B101698A4206D0586928B1E2\n:103870009B691B1A5842584170470120704700BF8E\n:1038800055AA00FF0D4B70B59E68A6B13046FFF7F4\n:10389000D9FF044678B9F3686BB935690DB920468C\n:1038A00070BD2846FFF7CEFF28B9EB68B34202D1BE\n:1038B0002E462D69F2E70124F1E700BF4C1A0020E3\n:1038C0002DE9F041394E3468F368D4B1B468C4B11D\n:1038D000002861D000295FD001FB00F5B5FBF0F0B6\n:1038E000884259D1291D57D8AA070DD025F00302C7\n:1038F00004321C4603E0616891424FD2A469002C57\n:10390000F9D12046BDE8F0812A46F2E7891A232939\n:1039100004F12007D4E9050C1AD80123A360A0B153\n:10392000C0F818C0A36903B158610023C4E9053386\n:103930003369DB0702D5FFF7A5FFA0BB2A460021AC\n:1039400038463C4602F017F9DBE7C6F80CC0E9E759\n:1039500002F1200804EB08032039C3E9011E2169A4\n:10396000DFF84CE0DC60196144F808E0AEF1102E9D\n:10397000AEF1EF1EC3F81CE001B1CB60C3E9050C4A\n:1039800040B18361996901B14B6101212361C4E9AF\n:103990000121CAE7F360F5E70024B2E7D4F808E0B4\n:1039A000BEF1000FB2D0012001F0D2FF4C1A00206E\n:1039B00055AA00FFF8B5054600283AD0364E3368C0\n:1039C000002B36D0B268002A33D0834203D8726805\n:1039D0001344984202D3012001F0BAFFA0F1200461\n:1039E0002046FFF72FFF0028F5D155F8183C012B92\n:1039F000F1D145F8180C55E90570DFB1BB68CBB9BA\n:103A000055F81C2C7B68203213447B60386100B170\n:103A1000C76020220021204602F0ADF8386908B1C5\n:103A200083682BB333699B0703D5FFF72BFF00286F\n:103A3000D1D1F8BD40B3836833BB42686368134497\n:103A4000203363600369D4E90521236192B9B9B9D0\n:103A5000426981696261A161A2B19461A26902B106\n:103A6000546103B1DC602022002102F084F8D9E720\n:103A70003C46E2E79161A1690029E9D04A61E7E7A4\n:103A8000F160F8E7F460E9E7F36845F8083C03B152\n:103A90005C61F460C6E700BF4C1A002038B50D46E3\n:103AA000142200210446124802F065F81149124818\n:103AB00000F02CF8232D1AD914F003031FBF043D86\n:103AC000ED18C3F10403E4182A460021204602F051\n:103AD00052F8074B094AC3E900459C602260A2F1F5\n:103AE0001022203DA2F1EF126560E261DC6038BD7A\n:103AF0004C1A0020B5390000C138000055AA00FF5B\n:103B0000014B1B68184700BFC8100020024B18600B\n:103B1000024B002019607047CC100020C810002014\n:103B200010B50A46044619B1024B00211B6898479C\n:103B300010BD00BFD0100020054B0A46197819B1FE\n:103B40000146181D02F0B8B86FF08800704700BF3A\n:103B5000601A002010B5074C00F032F8204602F041\n:103B6000BCF8201F0821FFF7DBFFBDE8104002F082\n:103B7000A0B800BF641A002070B50C4D2B787BB93B\n:103B800002F095F8044670B90126281D6E7002F007\n:103B900091F8044638B900F00BF8044618B92E70B5\n:103BA0000024204670BDFFF7D5FFFAE7601A002019\n:103BB0000122024B002083F880257047681A0020FC\n:103BC000F8B5074C2025264601272046276202F03B\n:103BD0005DF8013D04F12C04F7D186F88055F8BD5D\n:103BE000681A002010B50C4C236813B10B4B1B68EE\n:103BF0002BB900F089FC30B90122084B1A6001236F\n:103C00002360002010BD0138062801D9044810BDEA\n:103C1000044B53F8200010BD042000200020002099\n:103C2000FE8FFFFF545D0000144B2DE9F043002888\n:103C300014BF04461C46124F236883B0BB4219D000\n:103C400016460D4620464FF4147104F1040900F0A5\n:103C500047F804F58E78484600F0CEF8404600F06C\n:103C600019FA2B464A4640460649009600F034FBB6\n:103C700008B92760002003B0BDE8F08308200020C9\n:103C8000A5BCC95A7D3E000070B50D4C1D460028EC\n:103C900014BF064626460B4B306882B098420DD1C1\n:103CA00006F58E70002633460096144600F03EFB63\n:103CB000002818BF34462C6002B070BD0248FBE7F4\n:103CC00008200020A5BCC95AE88FFFFF38B131B1E8\n:103CD000002201440346013081421A70FAD1704734\n:103CE00038B131B1002201440346013088421A70D4\n:103CF000FAD170472DE9F043202B1D460646884631\n:103D000091468BB026D8DBB202AF8DF80480B04666\n:103D100058F8041B8DF8053021B9404601F038F9F8\n:103D2000044640B9012302224046336001A901F054\n:103D300043F9044638B138462021FFF7D1FF204629\n:103D40000BB0BDE8F0832A464946404601F034F9FD\n:103D50000446F0E702AF1946104600233A4601F048\n:103D6000B1FA04460028E6D12023B9461D46CCE727\n:103D70002DE9F041D0F8F830A6B0002BD8BF02AE44\n:103D800031DD05460027D0F80C4102AEA4F1010850\n:103D90009022D5F8FC403146D0F8000101AB0197E4\n:103DA000B8FA88F8A0474FEA5818044620B9019B92\n:103DB00053B9B8F1000F16D030469021FFF790FFAD\n:103DC000204626B0BDE8F081014632462846FFF77E\n:103DD00091FF58B9D5F80431019A1344C5F804315C\n:103DE000B8F1000FE8D16FF03C04E5E70446204647\n:103DF00026B0BDE8F08100BF70B50025044614224E\n:103E000004F588762946C0F8F850FC3001F0B3FE7E\n:103E100008222946304601F0AEFE144B30461B689E\n:103E20009847204640F8045B01F098F8104B304664\n:103E30001B689847C8B9D4F8F830AB420FDC03EBE5\n:103E4000830204EB82022021C2F800010120C2E9B2\n:103E5000421008490133C2F8FC10C4F8F830064B90\n:103E60003046BDE870401B68184770BDF010002058\n:103E7000EC100020614E0000E81000202DE9F04712\n:103E8000202A88B07AD83F4B00F5887904461B6811\n:103E900048461746884698470546F8B940F2011645\n:103EA000D4F8F830002B65D02046FFF761FF0546B7\n:103EB00038B9D4F8F830002B14DC013EF2D16FF0A1\n:103EC0003B056E4620213046FFF70AFF2E4B484641\n:103ED0001B689847002818BF6FF01D05284608B0DA\n:103EE000BDE8F087D4E94132934203D3D4F80C21E2\n:103EF000012A02D0013ED7D1E1E71F2BFAD96E4645\n:103F000001462022304604F1040A01F034FE314615\n:103F1000504601F07BF805460028D3D1504601F009\n:103F200031F8504601F01AF82946504601F030F8B1\n:103F300005460028C6D150462022314601F03CF803\n:103F400005460028BED1034632462021304601F006\n:103F5000B9F905460028B5D1D4F8F8303A46002B17\n:103F6000C8BF002340463146C8BFC4F8043101F041\n:103F7000DDFDA7E76FF03F056E46A3E76FF03B0559\n:103F8000ACE700BFEC100020E810002070B590B046\n:103F900000287AD03E4C0D46402236210646204667\n:103FA00001F0E9FD40225C21A01801F0E4FD2B1D89\n:103FB000A34203D904F144039D4252D32B682268E3\n:103FC000534023602B68226C534023646B68626803\n:103FD000534063606B68626C53406364AB68A26873\n:103FE0005340A360AB68A26C5340A364EB68E268E3\n:103FF0005340E360EB68E26C5340E3642B69226951\n:1040000053402361226D2B695340236562696B69BC\n:10401000534063616B69626D53406365AB69A2692C\n:104020005340A361AB69A26D5340A365EB69E2699C\n:104030005340E361EB69E26D5340E3650021304694\n:1040400000F0A6FF044628B940223046104900F08F\n:10405000B3FF044640216846FFF742FE204610B0F9\n:1040600070BD631E04F13F01013D1F3415F8010FBF\n:1040700013F8012F42401A7011F8012F2878A3423B\n:1040800082EA00020A70F1D1D8E76FF07304E5E725\n:104090005822002010B54FF49A720021044601F016\n:1040A0006AFD42F21072044BC4F8202104F59670A8\n:1040B0001B68BDE810401847F01000202DE9F04FB4\n:1040C00004468846914695B0002900F0A780002A52\n:1040D0000CBF0126022600238DF80C30002C00F0C6\n:1040E0009F80DFF844A104AF0AF1400B0021204675\n:1040F00000F04EFF034648B1384620210193FFF7F8\n:10410000EFFD019B184615B0BDE8F08F40225146E7\n:10411000204600F051FF03460028EDD104F1F405DC\n:1041200020222946204600F047FF03460028E3D11D\n:104130000122204603A900F03FFF03460028DBD1FF\n:10414000022E62D020460CA900F060FF0346002832\n:10415000D2D10146204600F01BFF03460028CBD1F8\n:1041600040225946204600F027FF03460028C3D1CD\n:10417000202220460CA900F01FFF03460028BBD1D7\n:104180003946204600F042FF03460028B4D13946A4\n:104190002046FFF7FBFE03460028ADD1202229462A\n:1041A000204600F009FF03460028A5D120460CA9AF\n:1041B00000F02CFF034600289ED10146204600F067\n:1041C000E7FE0346002897D140225946204600F0DA\n:1041D000F3FE034600288FD1202220460CA900F0D0\n:1041E000EBFE0346002887D12946204600F00EFF4B\n:1041F0000346002880D19DF80C200132D2B2B24291\n:104200008DF80C20FFF472AF76E74A46414620460F\n:1042100000F0D2FE0346002894D06DE701265AE74D\n:104220006FF0730304AF67E7582200202DE9F041D7\n:104230000446884615461F46D0F81801E0B013B171\n:1042400000EB40004008B5F5807F3ED82844B0F52B\n:10425000C07F3AD84FF4C0720021684601F08BFC51\n:104260006946D4F82431D4F81821D4F828019847A5\n:1042700070BBD4F8186157B1D4E9493072080DEB1E\n:104280000601984720BBD4F8183106EB5306B8F165\n:10429000000F00D08DB9324669462046FFF70EFF69\n:1042A000054610B90123C4F8143131466846FFF7BA\n:1042B00017FD284660B0BDE8F0810DEB06004146D1\n:1042C0002A462E4401F032FCE5E76FF00405F0E7E2\n:1042D0006FF00805EDE700BF2DE9F047044604F153\n:1042E000F4050F4691461E46DDF8208000F036FEAC\n:1042F00029462046FFF74AFE08B1BDE8F087284668\n:104300002022012101F037FCD4F81831C4F82471BF\n:10431000C4F8289113B92023C4F818314246314615\n:104320002046BDE8F0470123FFF780BFB2F5806F5C\n:1043300000F295802DE9F04F89B09A46129BB3F5B3\n:10434000807F00F29080D0F82431164604460F4654\n:104350004BB3D0F81C31012B05D0D0F81421D0F884\n:1043600020319A421FDD514600232046129AFFF762\n:104370005DFF014698B900231293002E5ED0DFF84E\n:10438000EC9009F1400B0021204600F001FE202EA8\n:104390003546014628BF202504F1F40890B10846AF\n:1043A00009B0BDE8F08FBAF1000FE6D0129B002BE8\n:1043B000E1D051461A462046FFF780FE014600280C\n:1043C000DBD0ECE749464022204600F0F5FD0146EF\n:1043D0000028E4D141462022204600F0EDFD0146B0\n:1043E0000028DCD16946204600F010FE0146002876\n:1043F000D5D1204600F0CCFD01460028CFD159464A\n:104400004022204600F0D8FD01460028C7D1694669\n:104410002022204600F0D0FD01460028BFD14146B1\n:10442000204600F0F3FD01460028B8D138462A4660\n:10443000414601F07BFB761B2F44A4D15146204618\n:10444000129AFFF73BFE01460028A8D10846D4F88F\n:1044500014310133C4F8143109B0BDE8F08F6FF0A6\n:104460000201084670476FF0040198E758220020C7\n:10447000002130B58DB0282202A8019101F07BFB0C\n:1044800002A800F047FF044610B120460DB030BD31\n:10449000174D4FF0FF31286800F042F9044620B173\n:1044A000144800F0D3F9012C18D0002000F068F96E\n:1044B000D0B9114A02A901A800F0C4FC04460120A9\n:1044C00000F05EF960B9286800F032F90028DCD00D\n:1044D0000A4800F0BBF920460DB030BD0524F2E7D4\n:1044E000074800F0B3F9EEE7064800F0AFF9E0E75F\n:1044F000D4100020705D0000D8220020C85D0000AC\n:10450000A85D0000885D000030B583B000F05CF865\n:1045100028B100F0ABF80324204603B030BD00F012\n:1045200077F8D8B900F02CF9E0B9224CD4F8283942\n:104530001B0EF02B0AD1D4F8242A1F4B9A4231D0FB\n:1045400000F044F8072400F091F8E5E7062400F0B5\n:104550003DF800F08BF8204603B030BD042420461F\n:1045600003B030BD00F032F800F080F8FFF780FFB4\n:10457000044698B9114D2B68002BCDD10246014657\n:10458000FFF752FB50B968220D4901ABFFF77CFBE6\n:1045900020B920460B4B2B6003B030BD05242046CC\n:1045A00003B030BD012000F0EBF80028DAD1C4F8E8\n:1045B0000C0ADBE7001084500000E020F824002003\n:1045C000FC2400205AEA5A5A002070470020704705\n:1045D000430504D54FF0FF32034BC3F80821024BCB\n:1045E000C3F8080A704700BF00108450014BC3F89D\n:1045F000040A70470010845008B100F06BB84FF403\n:104600007500704708B100F04FB84FF4750070475F\n:1046100010B5114800F070F800B110BD0F4800F05F\n:104620006BF80028F9D10E4C204600F065F8002800\n:10463000F3D10C4B0C481C6000F05EF801460028DA\n:10464000EBD100F02DF8044608B1204610BD00F073\n:1046500079F82046FAE700BFE4100020E0100020BF\n:10466000DC100020D4100020D810002008B5084825\n:1046700000F04CF8074800F049F8074800F046F809\n:10468000064800F043F8BDE8084000F00BB800BF52\n:10469000E4100020E0100020DC100020D8100020E2\n:1046A00000207047704700BF024610B4084CD4F891\n:1046B000003A1342FBD021B1D4F804310B60C4F8A6\n:1046C0000831034B0020C3F8082A5DF8044B7047FB\n:1046D00000108450044AD2F8003A1842FBD0C2F8C5\n:1046E000080A0020704700BF0010845001F0E8B9AC\n:1046F0000A46002101F03FBA08B5034B02681B6867\n:1047000010689847002008BD1011002008B5034B21\n:1047100002685B6810689847002008BD10110020EF\n:10472000024B02689B681068184700BF10110020F8\n:1047300008B5034B0268DB6810689847002008BD85\n:104740001011002070B5094C094D2069AB689847DD\n:1047500018B1084B08485B6898470021074AEB6886\n:1047600011602069BDE87040184700BFFC100020B0\n:1047700010110020F4100020E05D000064250020EE\n:10478000F8B51A4D1A4E0446B26828699047E8B940\n:10479000184F3B684CB1A3B1012B19D0013B3B60D2\n:1047A0002869F36898470020F8BD43B90121124AEF\n:1047B000C2F800151149D1F81029002AFBD10133A4\n:1047C0003B60F368286998470020F8BD0C48F8BDA5\n:1047D0000A4AD2F81039002BFBD1074A6FF07E400D\n:1047E000C2F80035FFF702FF3B68D7E7FC10002056\n:1047F0001011002064250020000084500010845017\n:10480000E98FFFFF014B1B68184700BF1011002004\n:10481000014B9B68184700BF10110020014BDB685B\n:10482000184700BF10110020BFF34F8F0549064BFA\n:10483000CA6802F4E0621343CB60BFF34F8F00BF3E\n:10484000FDE700BF00ED00E00400FA0530B44FF0D2\n:10485000FE320025074B084C08494968C3F800247C\n:10486000C3F80424C3F80824C3F80C24C4F8005582\n:1048700030BC08470010845000008450F410002021\n:1048800010B5044650B1636813F0685F05D0064A5E\n:10489000934202D000236260236010BD034B0448A2\n:1048A0005B689847EFE700BF2C5F5CA9F41000201D\n:1048B000005E0000C8B143680D4A934213D013F064\n:1048C000685F0ED0012350E8002F194640E8001C15\n:1048D0009CF0000FF7D1012AF5D0BFF35F8F0020C5\n:1048E000704704487047044870476FF4E0407047D1\n:1048F0002C5F5CA9E98FFFFFEA8FFFFF034680B1C1\n:10490000426809498A420AD012F0685F05D0BFF3B5\n:104910005F8F002210461A607047044870470448B1\n:1049200070476FF4E04070472C5F5CA9E98FFFFF90\n:10493000EA8FFFFF10B5044620B10023034A23602D\n:10494000626010BD0248FFF781FFF6E73A00003AC7\n:10495000285E00002DE9F047002878D00C46002999\n:1049600075D01D46002B72D007461AB101220023D4\n:104970002A6003602B68002B64D02946204600F093\n:10498000B7FD804600285FD12146286800F096FDDB\n:104990008046002858D12B68082B6CD0042B6DD092\n:1049A000A3F10209B9FA89F94FEA59190121354EE3\n:1049B000C6F8C411C6F84011C6F8C411636AC6F837\n:1049C0003031D6F830219342F6D14FF0000A4FF043\n:1049D000FF30C6F82CA1FFF7FBFD6FF01B0350461C\n:1049E000C6F80031FFF702FEC6F80C91D6F8040AAB\n:1049F00040F48060FFF7FAFD0A23C6F83831089BBF\n:104A0000012B29D02369204AA2FB03231B09626AD8\n:104A1000404602FB03F303EB43031A4ADB039B0903\n:104A2000C2F8D8310123C2F82C313B682A6823F040\n:104A30007F4343EA02633B602A6843EA02233B6008\n:104A4000BDE8F087DFF844804046BDE8F087DFF836\n:104A500040804046BDE8F0874FF47F03D3F80C2C2C\n:104A6000013206D0D3F80C3C074AA2FB03231B09F2\n:104A7000CDE71623CBE74FF0030997E74FF0020984\n:104A800094E700BF00108450ABAAAAAA310CF10031\n:104A9000350CF10058B34B1EB3F5047F27D230B567\n:104AA0004FF0000ECD00744671464B0901F01F0C0B\n:104AB00050F82330BCF11F0F23FA0CF306D003F09B\n:104AC000010319B901211C468E46EEE79C4201F113\n:104AD000010106D01C464FF0010EA942E5D100208D\n:104AE00030BD0EF1010E9645F7D1024830BD0148A8\n:104AF000704700BF360CF100C0B32DE9F0434C1EE7\n:104B0000B4F5047F1DD2E3B1DAB100252E46A846E4\n:104B10002C46CF0003F1FF394FEA541C04F01F0E5E\n:104B200050F82C10BEF11F0F21FA0EFC0CD00CF027\n:104B3000010C4CB90126E04634463546ECE7964276\n:104B400016D90C48BDE8F083AB420BD0C44508BF72\n:104B50000136A945F3D001350134BC42DCD1002037\n:104B6000BDE8F0830125E0462E46F5E70148704791\n:104B70001D46F1E7370CF1002DE9F04F91B0834667\n:104B8000DDE91B460D46002E6ED10F691C60002327\n:104B90001A990B60002A00F02F819D4B6A6AD3F8A6\n:104BA0003811D3F830319A4269D10A2967D19BF87C\n:104BB0000330089304F10803079304230593954BEE\n:104BC000934CA3FB07239B0803EB4303A7EB43038F\n:104BD000039306971A9B069E1E60002E00F0EA8043\n:104BE00000F096FC00230993BBF1000F00F0D58084\n:104BF00098464FF0010ADDF81C90002D00F0CD80A2\n:104C0000089B002B3DD0284608A900F071FC002825\n:104C100039D0814B984220D0089B082B00F0E480CB\n:104C200000225B000893294608AB58460092FFF724\n:104C300091FE7A4B984200F0D38070B9DBF80020E7\n:104C4000130A03F47F03134323F07F43CBF80030B0\n:104C5000059B013B0593BDD10020039000F058FC5B\n:104C6000039811B0BDE8F08F4FF47F01D1F80C0C20\n:104C700001300CBF4FF40477D1F80C7C86E768480C\n:104C8000EBE76648C5E72946089800F017FC0028BE\n:104C9000BFD1089B082B00F0A980042B00F0A9804D\n:104CA000A3F10203B3FA83F35B090493C4F8C4A12C\n:104CB000C4F840A1C4F8C4A16B6AC4F83031D4F878\n:104CC00030219342F6D100234FF0FF30C4F82C314D\n:104CD000FFF77EFC002318466FF01B03C4F8003179\n:104CE000FFF784FC049B4B4FC4F80C31D4F8040A42\n:104CF00040F48060FFF77AFC0A23C4F838312B694E\n:104D000009A8A7FB03236A6A1B0902FB03F303EB51\n:104D10004303DB039B09C4F8D831C4F82CA1DBF8AA\n:104D20000030089923F07F420B0243EA01631343EA\n:104D3000CBF8003000F0B2FB099B03F01A030343E9\n:104D40007FF46AAFA7FB0637D4F81431B8EB970F9E\n:104D50000A93D4F818310B93D4F81C310C93D4F87F\n:104D600020310D93D4F824310E93D4F828310F93C9\n:104D700001D1039B9BB9484618220AA9183EFFF7A8\n:104D8000B5FC09F1180908F101089EB100F0C0FB5B\n:104D900000230993002D7FF433AF224839E74846BA\n:104DA0001A46F61A0AA9994408F10108FFF79EFC71\n:104DB000002EEBD1069F079E6A6939463046FFF701\n:104DC00069FEAA6940B94FF4806339463046FFF75F\n:104DD00093FE00283FF440AF00231A9A136018E7AF\n:104DE0002B6A002B3FF439AF0F4836E703230493B7\n:104DF0005CE70223049359E70122294658460096AE\n:104E000008ABFFF7A7FD00283FF4D4AE25E700BFAD\n:104E100000108450ABAAAAAA020CF100310CF100D8\n:104E2000300CF100350CF100320CF10030B4DDE94A\n:104E3000023402940024049DCDE9035430BCFFF7F2\n:104E40009BBE00BF30B51546012487B005AB00936B\n:104E50000022CDE9015404ABFFF78EFE07B030BD50\n:104E6000F0B587B0002966D01E46002B63D01446EB\n:104E7000002A60D0314F0D4638684FF0FF31FFF700\n:104E80004FFC00284DD10020FFF77AFC002844D1C8\n:104E90002B4800F03FFAD8B101204FF0FF35FFF763\n:104EA0006FFC002835D14FF408712648FEF70EFF3D\n:104EB00004212548FEF70AFF21482821FEF706FFB6\n:104EC0003868FFF735FC00282FD1284607B0F0BD21\n:104ED0001C4A0346029205AA019204AA039000927A\n:104EE000174902461848FFF7A1FF03460028D3D10F\n:104EF00028461D46059B22469C4228BF1A46049917\n:104F000032600831FFF7F2FB0120FFF739FC00287F\n:104F1000C9D00E48FFF79AFCC5E70D48FFF796FC8D\n:104F2000B6E70C48FFF792FCADE70B48FFF78EFCA5\n:104F3000284607B0F0BD4FF0FF35C6E7D41000207B\n:104F40007C250020A425002078250020A85D0000F5\n:104F5000885D0000705D00004C5E000010B50446E6\n:104F600028B12046BDE81040F421FFF7C1BB04483A\n:104F7000FFF76CFC2046BDE81040F421FFF7B8BBFA\n:104F8000645E000010B1F421FEF7A0BE704700BFC0\n:104F900008B521B1012908D06FF0360008BD012104\n:104FA00000F058F80028F7D108BD022100F052F8AF\n:104FB0000028F9D0F0E700BF30B505468818B0F5F5\n:104FC000801F0B461446A1B001D8802A0CD9B3F536\n:104FD000801F17D922461946284600F07DF804465E\n:104FE00084B9204621B030BD6846FFF77FFB2246DA\n:104FF0006946284600F070F8802104466846FFF7AD\n:1050000077FBEDE76FF03604EBE700BF70B5E8B172\n:105010000E46D9B1044600F031F80546B0B9236810\n:10502000012B0BD0022B01D0284670BD1C2230462C\n:1050300004F10801FFF75AFB284670BD20223046D4\n:1050400004F10801FFF752FB284670BD6FF03605EA\n:10505000EAE700BF78B138B50D46F0210446FFF706\n:1050600047FB402320462560E36500F091FA0038B5\n:1050700018BF012038BD0120704700BFF0B5056E94\n:10508000A5B0044604AEE5B90023012701933046DC\n:105090006760294602AA009300F046FA10B1012089\n:1050A00025B0F0BD2A46204602A900F099FA002852\n:1050B000F5D1204600F0AAFB0028F0D1206625B0EB\n:1050C000F0BD802D2A46304628BF802204F16401BD\n:1050D000FFF70CFB256ED7E7002800F0AE802DE926\n:1050E000F04F1446A5B0002A3DD00F46002949D004\n:1050F000B2F5803F0546006E20D34FF6FF7805F1EC\n:10510000640AEB6D1A1AB2FBF3F103FB1122002AB9\n:1051100065D18342B9464FF6FF7672D0B6FBF3FBFA\n:1051200003FB0BFBBBF1000F30D1002E45D1A4F5E2\n:105130007F44FF3CB4F5803F4744E2D2EA6D161A43\n:10514000B6FBF2F302FB1366A64228BF2646002EEA\n:1051500075D1824200F08D80B4FBF2F602FB06F6B8\n:105160002EB9002C79D1002025B0BDE8F08F0023A6\n:1051700031463846CDE9003302AA00F0D5F90028BF\n:1051800000F09580012025B0BDE8F08F002359463E\n:105190004846CDE9003302AA00F0C6F90028F1D153\n:1051A0005A46284602A900F01BFA0028EAD1A6EBCD\n:1051B0000B06286ED944002EB9D019304946324624\n:1051C00005EB8000FFF792FAA4F57F44286EFF3CC0\n:1051D0003044B4F5803F4744286692D2AEE742455A\n:1051E00028BF4246164639465044FFF77FFA286EDC\n:1051F000EB6D3044834207EB06092866A8EB0606F0\n:105200008CD1802B28BF802351461A4604A8FFF773\n:105210006DFA0023E96D02AACDE9003304A800F07D\n:1052200083F90028AED12846EA6D02A900F0D8F92A\n:105230000028A7D1EB6D286670E70120704705F1C3\n:105240006403394632461844FFF750FA286EEA6D77\n:105250003044A41B374428667BE72B6E2246193363\n:1052600005EB83003946FFF741FA2B6E23442B668A\n:1052700079E7802A28BF802205F1640104A8FFF79E\n:1052800035FA0023E96D04A8CDE9003302AA00F045\n:105290004BF900287FF476AF2846EA6D02A900F0AA\n:1052A0009FF900287FF46EAFEA6D286654E7324616\n:1052B000284602A900F094F900287FF463AF374430\n:1052C000A41B4EE7F8B51C460546114816460F4686\n:1052D000FFF744FE21460E48FFF75AFE044620B170\n:1052E0000B48FFF74FFE2046F8BD3A4629460848CE\n:1052F000FFF762FE04460028F2D131460448FFF76A\n:1053000085FE04460248FFF73DFE2046F8BD00BF7B\n:10531000C42700201C2370B582B001A90546019363\n:1053200000F032F8044638B9019B1C2B07D0144C0E\n:105330001C212846FFF7DCF9204602B070BDD5E9F4\n:105340000112131E18BF0123003918BF01210126C5\n:1053500028689B00EA6843EA4103002818BF43F02D\n:105360000103EE612AB9C5E90834002BE4D1054CEC\n:10537000DEE7204643F00803C5E9083402B070BDFB\n:10538000370CF0000E0CF0000346002866D0002910\n:1053900064D00A681C2A61D14FF47F023B49D2F8DD\n:1053A000100C88425CD0D2F8101C013158D0D2F8D1\n:1053B000101C4FF47F021960D2F8140C3449884253\n:1053C00060D0D2F8141C01315CD0D2F8141C4FF418\n:1053D0007F025960D2F8181C11F5947F4FD0D2F893\n:1053E000181C01314BD0D2F8181C4FF47F02996081\n:1053F000D2F81C0C274988423ED0D2F81C1C01313F\n:105400003AD0D2F81C1C4FF47F02D960D2F8001CAD\n:1054100070312FD0D2F8001C01312BD0D2F8001CF3\n:105420004FF47F021961D2F8041CAF3120D0D2F8BA\n:10543000041C01311CD0D2F8041C4FF47F025961C6\n:10544000D2F8080C144988420DD0D2F8081C01315A\n:1054500009D0D2F8082C00209A61704701207047CB\n:1054600040F6FC01A5E740F2373200209A61704710\n:105470005121E2E79021D3E742F60411C3E740F659\n:10548000D861B2E742F2D001A1E700BFFCF8FFFF0C\n:10549000D020FFFF0429FFFF37F3FFFF014608B5C7\n:1054A0004FF48060FFF7AEF80022034BC3F8C82129\n:1054B000C3F82C2108BD00BF001084500138072814\n:1054C00005D8DFE800F0100E040A040404060748BB\n:1054D0007047CB6800204B6270478B6800204B629E\n:1054E00070474B68F6E70B68F4E700BF310CF1003A\n:1054F0000B6802E0082B0B6006D8026A13424FEAE1\n:105500004303F7D00020704700487047310CF1008A\n:105510000022044B4FF48060C3F82C21C3F8C4214F\n:10552000FFF756B800108450F0B5002483B0B0F1F6\n:10553000005F8DF8074025D38C468444BCF1804F32\n:1055400004461FD817461D4601220DF1070300F03F\n:1055500097F90646B0B99DF80730099A3C603B714F\n:1055600092B14DB10246089928460DF1070300F0AB\n:1055700087F938B99DF807303046099A15601371DC\n:1055800003B0F0BD4FF47506304603B0F0BD00BF68\n:10559000D8B108B50368012B04D0022B0FD043B15A\n:1055A0000B4808BD20220B490830FFF79FF8002068\n:1055B00008BD142208490830FFF798F8F7E72022C1\n:1055C00006490830FFF792F8F1E74FF4730070478F\n:1055D0000100F300745E0000B45E0000945E000001\n:1055E0002DE9F843002900F0F9800446002800F076\n:1055F000F98003680F469046002B00F0C380013B02\n:10560000012B00F2D7804FF0FF3178484FF00209AC\n:10561000FFF786F8002840F0BF800020FFF7B0F8C1\n:105620000646002840F0C280714AD2F81C39002B8F\n:10563000FBD16F4DD5F8203C002BFBD14FF0FF3054\n:10564000FEF7C6FFD5F8040A20F04000FEF7CEFFB3\n:1056500001230722C5F81838C5F80029C5F8C43752\n:10566000A36CC5F8CC37E36CC5F8D037C5F8C09744\n:1056700023685BB1013B012B18D8636AC5F85C361F\n:10568000236AC5F85836E369C5F85436A269584B01\n:10569000C3F850266269C3F84C262269C3F848262D\n:1056A000E268C3F84426A268C3F84026504AD2F8FC\n:1056B0001039002BFBD1B8F1000F5FD06368402098\n:1056C000012B08BFC2F884363A68494BC3F8282C2E\n:1056D000C3F82C8CFEF790FF0646454AD2F81039E5\n:1056E000002BFBD1424AD2F8203C002BFBD123688F\n:1056F0005BB1013B012B18D8D2F85C366362D2F85B\n:1057000058362362D2F85436E361394BD3F8502629\n:10571000A261D3F84C266261D3F848262261D3F8FF\n:105720004426E260D3F84036A36000230121304ACA\n:10573000D2F8CC07A064D2F8D007E064C2F8C4174E\n:10574000C2F88436C2F8C837D2F81039002BFBD122\n:10575000C2F81838002E3BD1254BD3F8040A40F08C\n:105760004000FEF743FF0120FFF70AF860BB1F4827\n:10577000FEF7DEFF10BB3046BDE8F8830423C2F815\n:10578000C837AAE74FF0FF3118484FF00109FEF77C\n:10579000C7FF00283FF441AF1648FFF757F8002035\n:1057A000FEF7EEFF064600283FF43EAF1248FFF733\n:1057B0004DF839E7114E3046BDE8F8831048FFF741\n:1057C00045F83046BDE8F8830E48FFF73FF8CEE7CE\n:1057D000102104F10800FEF78BFFBDE70A4E3046AA\n:1057E000BDE8F8834FF47306C5E700BFE41000205E\n:1057F00000108450705D0000885D00000100F3001F\n:10580000C85D0000A85D00000300F300A0B30346DC\n:1058100070B4D0E90240D3E9041226BA05BA0CBA32\n:1058200010BAC3E90440D3E90612986A09BAC3E979\n:10583000026512BA996100BAD3E9084125BA0CBAD7\n:10584000C3E90725D3E90B12C3E9094009BAD86BAC\n:1058500012BAD962D3E90D4125BAC3E90C250CBAB5\n:1058600000BAD3E9101209BA12BAC3E90E4019649A\n:1058700000205A6470BC70474FF47300704700BF3B\n:1058800000201870704700BF431810B501D21F2ABE\n:1058900001D9002010BD013902F014030144102B7E\n:1058A00080EA01043CD009DC8BB1042BF1D11F2C20\n:1058B00040E840F315D941E840F10EE0142BE8D15F\n:1058C0001F2C40E8C0F30CD941E8C0F105E01F2CC3\n:1058D00040E800F305D941E800F11C460B468C4234\n:1058E000D7D122F01402013A0A2AD2D801A151F8E4\n:1058F00022F000BF435900003D5900002F5900001D\n:1059000093580000935800009358000093580000EB\n:105910004959000043590000375900002F59000031\n:105920001F2C40E880F3DCD941E880F1D5E713F47F\n:10593000001FAFD1ADE713F4801FFAE75B02A9D5D2\n:10594000A7E713F4002FF4E713F4802FF1E700BF6B\n:1059500008B5074B044613B10021AFF30080054B97\n:105960001868836A03B19847204600F029F800BF01\n:1059700000000000C85E000070B50D4D00260D4C03\n:10598000641BA410A64209D10B4D00260B4C00F05D\n:10599000D5F9641BA410A64205D170BD55F8043B8F\n:1059A00001369847EEE755F8043B01369847F2E791\n:1059B000A0110020A0110020A0110020A41100209F\n:1059C000FEE700BFB7EE000AF7EE000AB7EE001AD6\n:1059D000F7EE001AB7EE002AF7EE002AB7EE003A0B\n:1059E000F7EE003AB7EE004AF7EE004AB7EE005A7B\n:1059F000F7EE005AB7EE006AF7EE006AB7EE007AEB\n:105A0000F7EE007AF1EE10CA40F29F01CFF20001EA\n:105A10003CEA010CE1EE10CA002383F300887047D2\n:105A2000FDF79CBAEFF30880EFF309812DE9F00F41\n:105A30006B467246FAF7ACFF08B0FFF7F1FFFEE7DE\n:105A4000FDF78CBAEFF30880EFF309812DE9F00F31\n:105A50006B467246FAF79CFF08B0FFF7F1FFFEE7CE\n:105A6000FDF77CBAEFF30880EFF309812DE9F00F21\n:105A70006B467246FAF78CFF08B0FFF7F1FFFEE7BE\n:105A8000FDF76CBAEFF30880EFF309812DE9F00F11\n:105A90006B467246FAF77CFF08B0FFF7F1FFFEE7AE\n:105AA000FDF75CBAEFF30880EFF309812DE9F00F01\n:105AB0006B467246FAF76CFF08B0FFF7F1FFFEE79E\n:105AC000814270B40546144602D370BC00F02EB873\n:105AD000821821443CB141EA020313F003030FD1C1\n:105AE000E018032812D86FF00300A30843430020F6\n:105AF00019441A442344984210D1284670BC704778\n:105B000011F8013D013C02F8013DE3E7581850F857\n:105B1000046C981840F8046C043BE1E70C1A14F884\n:105B2000016C141A04F8016C0130E4E770B5044606\n:105B30003AB141EA040313F003030CD1D518032D45\n:105B40000FD822F003031C441944002302F003027F\n:105B500093420CD170BD11F8013B013A04F8013BAE\n:105B6000E6E7CD1A2E68E51A2E60043BE6E75D5C99\n:105B7000E5540133ECE7F0B5044662B922F00303C3\n:105B8000234402F003021A44934214D1F0BD04F8F6\n:105B9000011B013AF1E7A307F9D115460B0443EACB\n:105BA00001630B4343EA01231619032DE6D9771B42\n:105BB0003B60043DF9E703F8011BE5E708B5EFF3A7\n:105BC0000583C3F308030BB1FDF7C8F9BDE808402E\n:105BD000FDF74CBA10B50446EFF30583C3F3080391\n:105BE0000BB1FDF7BBF92046BDE81040FDF741BA07\n:105BF0002DE9F04104460D4616461F46EFF3058396\n:105C0000C3F308030BB1FDF7A9F93B463246294619\n:105C10002046BDE8F041FCF743BC2DE9F0410446C5\n:105C20000D4616461F46EFF30583C3F308030BB179\n:105C3000FDF794F93B46324629462046BDE8F0413F\n:105C4000FCF768BC08B5EFF30583C3F308030BB199\n:105C5000FDF784F9BDE80840FDF7DBB90020704787\n:105C6000704710B5044608B1FDF75AFF2046BDE85D\n:105C70001040FDF745BF10B50446406A10B1A16A57\n:105C8000FFF7EFFF0020C4E9090010BD38B5044656\n:105C9000FFF7F1FF236A2C22012B0CBF05466FF0A2\n:105CA000960500212046FFF766FF284638BD0020F4\n:105CB000704770470020704713B5002001AB1446B1\n:105CC000FDF7E2FF30B9019BA34218BF6FF09300CC\n:105CD00002B010BD6FF09200FAE7002070472DE986\n:105CE000E04F2746A046A146A246A346A4462DED76\n:105CF000108B4FF0000545EC185B45EC195A45EC4C\n:105D00001A5A45EC1B5A45EC1C5A45EC1D5A45ECF9\n:105D10001E5A45EC1F5AF1EE105A4FF66076C0F647\n:105D2000FF763540E1EE105A84F30088254626467A\n:105D3000A447BDEC108BBDE8E08F0000F8B500BFB4\n:105D4000F8BC08BC9E467047F8B500BFF8BC08BC5C\n:105D50009E467047FF8FFFFFFF8FFFFFFE8FFFFF05\n:105D6000FE8FFFFFFD8FFFFFFC8FFFFFFC8FFFFF0C\n:105D70004661696C20746F2061637175697265207A\n:105D80006D757465780A00004661696C20746F2037\n:105D9000696E63726561736520504D20636F756E27\n:105DA0007465720A000000004661696C20746F20FF\n:105DB000646563726561736520504D20636F756E15\n:105DC0007465720A000000004661696C20746F20DF\n:105DD00072656C65617365206D757465780A000085\n:105DE000436F756C64206E6F74206C6F636B2070F2\n:105DF0006F7765722073617665206D7574657800C4\n:105E00006D757465785F667265652063616C6C653D\n:105E1000642077697468204E554C4C207061726123\n:105E20006D6574657200000043616E277420696EB1\n:105E3000697469616C697A65206D757465782C2068\n:105E4000776173204E554C4C0D0A00004661696C19\n:105E500020746F20756E6C6F636B206D7574657840\n:105E60000A0000000A637478206973204E554C4C78\n:105E70000A00000067E6096A85AE67BB72F36E3CF4\n:105E80003AF54FA57F520E518C68059BABD9831F05\n:105E900019CDE05BD89E05C107D57C3617DD703083\n:105EA00039590EF7310BC0FF11155868A78FF964E7\n:105EB000A44FFABE0123456789ABCDEFFEDCBA984B\n:105EC00076543210F0E1D2C34011002054464D5FA9\n:105ED00043525950544F0054464D5F504C41544624\n:105EE0004F524D5F53455256494345000000000054\n:105EF00000000000000000000000000000000000A2\n:107C00007FE97FE9FBF72CBD7FE97FE9FBF737BD13\n:107C10007FE97FE9FBF75FBD7FE97FE9FBF752BDB5\n:107C20007FE97FE9FBF73ABD00000000000000009B\n:107C30000000000000000000000000000000000044\n:107C40007AFFFFFF00900050000000008C3400001D\n:107C50000400000000000000000000000000000020\n:107C6000000000000000000090ED00E000A00350C4\n:107C70007FA60350009000506F9500501412002012\n:107C8000A811002044010000615C00005D5C000060\n:107C9000775B0000DC1000200C1100200411002094\n:107CA00000110020FC1000201D48000011480000B9\n:107CB000054800000000000029480000381100209D\n:107CC00030110020281100200000000020110020A9\n:107CD0003549000081480000B5480000FD4800001B\n:107CE000682500203A00003A6C2500203A00003A4E\n:107CF000702500203A00003A742500203A00003A2E\n:107D00000000000000000000000000000000000073\n:107D10000000000000000000000000000000000063\n:107D20000000000000000000000000000000000053\n:107D30000000000000000000000000000000000043\n:107D40000000000000000000000000000000000033\n:107D50000000000000000000000000000000000023\n:087D6000553700002D3700002B\n:00000001FF\n"
  },
  {
    "path": "examples/nrf9151/s/.cargo/config.toml",
    "content": "[target.'cfg(all(target_arch = \"arm\", target_os = \"none\"))']\nrunner = \"probe-rs run --chip nRF9160_xxAA\"\n\n[build]\ntarget = \"thumbv8m.main-none-eabihf\"\n\n[env]\nDEFMT_LOG = \"trace\"\n"
  },
  {
    "path": "examples/nrf9151/s/Cargo.toml",
    "content": "[package]\nedition = \"2024\"\nname = \"embassy-nrf9151-secure-examples\"\nversion = \"0.1.0\"\nlicense = \"MIT OR Apache-2.0\"\npublish = false\n\n[dependencies]\nembassy-executor = { version = \"0.10.0\", path = \"../../../embassy-executor\", features = [\"platform-cortex-m\", \"executor-thread\", \"executor-interrupt\", \"defmt\"] }\nembassy-time = { version = \"0.5.1\", path = \"../../../embassy-time\", features = [\"defmt\", \"defmt-timestamp-uptime\"] }\nembassy-nrf = { version = \"0.10.0\", path = \"../../../embassy-nrf\", features = [\"defmt\", \"nrf9120-s\", \"time-driver-rtc1\", \"gpiote\", \"unstable-pac\", \"time\"] }\n\ndefmt = \"1.0.1\"\ndefmt-rtt = \"1.0.0\"\n\ncortex-m = { version = \"0.7.6\", features = [\"inline-asm\", \"critical-section-single-core\"] }\ncortex-m-rt = \"0.7.0\"\npanic-probe = { version = \"1.0.0\", features = [\"print-defmt\"] }\nrand = { version = \"0.9.0\", default-features = false }\n\n[profile.release]\ndebug = 2\n\n[package.metadata.embassy]\nbuild = [\n  { target = \"thumbv8m.main-none-eabihf\", artifact-dir = \"out/examples/nrf9151/s\" }\n]\n"
  },
  {
    "path": "examples/nrf9151/s/build.rs",
    "content": "//! This build script copies the `memory.x` file from the crate root into\n//! a directory where the linker can always find it at build time.\n//! For many projects this is optional, as the linker always searches the\n//! project root directory -- wherever `Cargo.toml` is. However, if you\n//! are using a workspace or have a more complicated build setup, this\n//! build script becomes required. Additionally, by requesting that\n//! Cargo re-run the build script whenever `memory.x` is changed,\n//! updating `memory.x` ensures a rebuild of the application with the\n//! new memory settings.\n\nuse std::env;\nuse std::fs::File;\nuse std::io::Write;\nuse std::path::PathBuf;\n\nfn main() {\n    // Put `memory.x` in our output directory and ensure it's\n    // on the linker search path.\n    let out = &PathBuf::from(env::var_os(\"OUT_DIR\").unwrap());\n    File::create(out.join(\"memory.x\"))\n        .unwrap()\n        .write_all(include_bytes!(\"memory.x\"))\n        .unwrap();\n    println!(\"cargo:rustc-link-search={}\", out.display());\n\n    // By default, Cargo will re-run a build script whenever\n    // any file in the project changes. By specifying `memory.x`\n    // here, we ensure the build script is only re-run when\n    // `memory.x` is changed.\n    println!(\"cargo:rerun-if-changed=memory.x\");\n\n    println!(\"cargo:rustc-link-arg-bins=--nmagic\");\n    println!(\"cargo:rustc-link-arg-bins=-Tlink.x\");\n    println!(\"cargo:rustc-link-arg-bins=-Tdefmt.x\");\n}\n"
  },
  {
    "path": "examples/nrf9151/s/memory.x",
    "content": "MEMORY\n{\n  FLASH                    : ORIGIN = 0x00000000, LENGTH = 1024K\n  RAM                      : ORIGIN = 0x20018000, LENGTH = 160K\n}\n"
  },
  {
    "path": "examples/nrf9151/s/src/bin/blinky.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse embassy_executor::Spawner;\nuse embassy_nrf::gpio::{Level, Output, OutputDrive};\nuse embassy_time::Timer;\nuse {defmt_rtt as _, panic_probe as _};\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let p = embassy_nrf::init(Default::default());\n    let mut led = Output::new(p.P0_00, Level::Low, OutputDrive::Standard);\n\n    loop {\n        led.set_high();\n        defmt::info!(\"high\");\n        Timer::after_millis(500).await;\n        led.set_low();\n        defmt::info!(\"low\");\n        Timer::after_millis(1000).await;\n    }\n}\n"
  },
  {
    "path": "examples/nrf9151/s/src/bin/cryptocell_rng.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse embassy_executor::Spawner;\nuse embassy_nrf::cryptocell_rng::{self, CcRng};\nuse embassy_nrf::{bind_interrupts, peripherals};\nuse rand::Rng as _;\nuse {defmt_rtt as _, panic_probe as _};\n\nbind_interrupts!(struct Irqs {\n    CRYPTOCELL => cryptocell_rng::InterruptHandler<peripherals::CC_RNG>;\n});\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let p = embassy_nrf::init(Default::default());\n\n    let mut rng = CcRng::new(p.CC_RNG, Irqs);\n\n    // Async API\n    let mut bytes = [0; 4];\n    rng.fill_bytes(&mut bytes).await;\n    defmt::info!(\"Some random bytes: {:?}\", bytes);\n\n    // Sync API with `rand`\n    defmt::info!(\"A random number from 1 to 10: {:?}\", rng.random_range(1..=10));\n\n    let mut bytes = [0; 1024];\n    rng.fill_bytes(&mut bytes).await;\n    let zero_count: u32 = bytes.iter().fold(0, |acc, val| acc + val.count_zeros());\n    let one_count: u32 = bytes.iter().fold(0, |acc, val| acc + val.count_ones());\n    defmt::info!(\"Chance of zero: {}%\", zero_count * 100 / (bytes.len() as u32 * 8));\n    defmt::info!(\"Chance of one: {}%\", one_count * 100 / (bytes.len() as u32 * 8));\n}\n"
  },
  {
    "path": "examples/nrf9160/.cargo/config.toml",
    "content": "[target.'cfg(all(target_arch = \"arm\", target_os = \"none\"))']\n# runner = \"probe-rs run --chip nRF9160_xxAA\"\nrunner = [ \"probe-rs\", \"run\", \"--chip=nRF9160_xxAA\", \"--always-print-stacktrace\", \"--log-format={t} {[{L}]%bold} {s}  {{c} {ff}:{l:1}%dimmed}\" ]\n\n[build]\ntarget = \"thumbv8m.main-none-eabihf\"\n\n[env]\nDEFMT_LOG = \"trace\"\n"
  },
  {
    "path": "examples/nrf9160/Cargo.toml",
    "content": "[package]\nedition = \"2024\"\nname = \"embassy-nrf9160-examples\"\nversion = \"0.1.0\"\nlicense = \"MIT OR Apache-2.0\"\npublish = false\n\n[dependencies]\nembassy-executor = { version = \"0.10.0\", path = \"../../embassy-executor\", features = [\"platform-cortex-m\", \"executor-thread\", \"executor-interrupt\", \"defmt\"] }\nembassy-time = { version = \"0.5.1\", path = \"../../embassy-time\", features = [\"defmt\", \"defmt-timestamp-uptime\"] }\nembassy-nrf = { version = \"0.10.0\", path = \"../../embassy-nrf\", features = [\"defmt\", \"nrf9160-s\", \"time-driver-rtc1\", \"gpiote\", \"unstable-pac\", \"time\"] }\nembassy-net-nrf91 = { version = \"0.2.0\", path = \"../../embassy-net-nrf91\", features = [\"defmt\"] }\nembassy-net = { version = \"0.9.0\", path = \"../../embassy-net\", features = [\"defmt\", \"tcp\", \"proto-ipv4\", \"medium-ip\"] }\n\ndefmt = \"1.0.1\"\ndefmt-rtt = \"1.0.0\"\n\nheapless = \"0.9\"\ncortex-m = { version = \"0.7.6\", features = [\"inline-asm\", \"critical-section-single-core\"] }\ncortex-m-rt = \"0.7.0\"\npanic-probe = { version = \"1.0.0\", features = [\"print-defmt\"] }\nstatic_cell = { version = \"2\" }\nembedded-io = { version = \"0.7.1\" }\nembedded-io-async = { version = \"0.7.0\", features = [\"defmt\"] }\n\n[profile.release]\ndebug = 2\n\n[package.metadata.embassy]\nbuild = [\n  { target = \"thumbv8m.main-none-eabihf\", artifact-dir = \"out/examples/nrf9160\" }\n]\n"
  },
  {
    "path": "examples/nrf9160/build.rs",
    "content": "//! This build script copies the `memory.x` file from the crate root into\n//! a directory where the linker can always find it at build time.\n//! For many projects this is optional, as the linker always searches the\n//! project root directory -- wherever `Cargo.toml` is. However, if you\n//! are using a workspace or have a more complicated build setup, this\n//! build script becomes required. Additionally, by requesting that\n//! Cargo re-run the build script whenever `memory.x` is changed,\n//! updating `memory.x` ensures a rebuild of the application with the\n//! new memory settings.\n\nuse std::env;\nuse std::fs::File;\nuse std::io::Write;\nuse std::path::PathBuf;\n\nfn main() {\n    // Put `memory.x` in our output directory and ensure it's\n    // on the linker search path.\n    let out = &PathBuf::from(env::var_os(\"OUT_DIR\").unwrap());\n    File::create(out.join(\"memory.x\"))\n        .unwrap()\n        .write_all(include_bytes!(\"memory.x\"))\n        .unwrap();\n    println!(\"cargo:rustc-link-search={}\", out.display());\n\n    // By default, Cargo will re-run a build script whenever\n    // any file in the project changes. By specifying `memory.x`\n    // here, we ensure the build script is only re-run when\n    // `memory.x` is changed.\n    println!(\"cargo:rerun-if-changed=memory.x\");\n\n    println!(\"cargo:rustc-link-arg-bins=--nmagic\");\n    println!(\"cargo:rustc-link-arg-bins=-Tlink.x\");\n    println!(\"cargo:rustc-link-arg-bins=-Tdefmt.x\");\n}\n"
  },
  {
    "path": "examples/nrf9160/memory.x",
    "content": "MEMORY\n{\n    FLASH : ORIGIN = 0x00000000, LENGTH = 1024K\n    RAM   : ORIGIN = 0x20010000, LENGTH = 192K\n    IPC   : ORIGIN = 0x20000000, LENGTH = 64K\n}\n\nPROVIDE(__start_ipc = ORIGIN(IPC));\nPROVIDE(__end_ipc   = ORIGIN(IPC) + LENGTH(IPC));\n"
  },
  {
    "path": "examples/nrf9160/src/bin/blinky.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse embassy_executor::Spawner;\nuse embassy_nrf::gpio::{Level, Output, OutputDrive};\nuse embassy_time::Timer;\nuse {defmt_rtt as _, panic_probe as _};\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let p = embassy_nrf::init(Default::default());\n    let mut led = Output::new(p.P0_02, Level::Low, OutputDrive::Standard);\n\n    loop {\n        led.set_high();\n        Timer::after_millis(300).await;\n        led.set_low();\n        Timer::after_millis(300).await;\n    }\n}\n"
  },
  {
    "path": "examples/nrf9160/src/bin/modem_tcp_client.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse core::mem::MaybeUninit;\nuse core::net::IpAddr;\nuse core::ptr::addr_of_mut;\nuse core::slice;\nuse core::str::FromStr;\n\nuse defmt::{info, unwrap, warn};\nuse embassy_executor::Spawner;\nuse embassy_net::{Ipv4Cidr, Stack, StackResources};\nuse embassy_net_nrf91::context::Status;\nuse embassy_net_nrf91::{Runner, State, TraceBuffer, TraceReader, context};\nuse embassy_nrf::buffered_uarte::{self, BufferedUarteTx};\nuse embassy_nrf::cryptocell_rng::CcRng;\nuse embassy_nrf::gpio::{AnyPin, Level, Output, OutputDrive};\nuse embassy_nrf::uarte::Baudrate;\nuse embassy_nrf::{Peri, bind_interrupts, interrupt, peripherals, uarte};\nuse embassy_time::{Duration, Timer};\nuse embedded_io_async::Write;\nuse heapless::Vec;\nuse static_cell::StaticCell;\nuse {defmt_rtt as _, panic_probe as _};\n\n#[interrupt]\nfn IPC() {\n    embassy_net_nrf91::on_ipc_irq();\n}\n\nbind_interrupts!(struct Irqs {\n    SERIAL0 => buffered_uarte::InterruptHandler<peripherals::SERIAL0>;\n});\n\n#[embassy_executor::task]\nasync fn trace_task(mut uart: BufferedUarteTx<'static>, reader: TraceReader<'static>) -> ! {\n    let mut rx = [0u8; 1024];\n    loop {\n        let n = reader.read(&mut rx[..]).await;\n        unwrap!(uart.write_all(&rx[..n]).await);\n    }\n}\n\n#[embassy_executor::task]\nasync fn modem_task(runner: Runner<'static>) -> ! {\n    runner.run().await\n}\n\n#[embassy_executor::task]\nasync fn net_task(mut runner: embassy_net::Runner<'static, embassy_net_nrf91::NetDriver<'static>>) -> ! {\n    runner.run().await\n}\n\n#[embassy_executor::task]\nasync fn control_task(\n    control: &'static context::Control<'static>,\n    config: context::Config<'static>,\n    stack: Stack<'static>,\n) {\n    unwrap!(control.configure(&config).await);\n    unwrap!(\n        control\n            .run(|status| {\n                stack.set_config_v4(status_to_config(status));\n            })\n            .await\n    );\n}\n\nfn status_to_config(status: &Status) -> embassy_net::ConfigV4 {\n    let Some(IpAddr::V4(addr)) = status.ip else {\n        panic!(\"Unexpected IP address\");\n    };\n\n    let gateway = match status.gateway {\n        Some(IpAddr::V4(addr)) => Some(addr),\n        _ => None,\n    };\n\n    let mut dns_servers = Vec::new();\n    for dns in status.dns.iter() {\n        if let IpAddr::V4(ip) = dns {\n            unwrap!(dns_servers.push(*ip));\n        }\n    }\n\n    embassy_net::ConfigV4::Static(embassy_net::StaticConfigV4 {\n        address: Ipv4Cidr::new(addr, 32),\n        gateway,\n        dns_servers,\n    })\n}\n\n#[embassy_executor::task]\nasync fn blink_task(pin: Peri<'static, AnyPin>) {\n    let mut led = Output::new(pin, Level::Low, OutputDrive::Standard);\n    loop {\n        led.set_high();\n        Timer::after_millis(1000).await;\n        led.set_low();\n        Timer::after_millis(1000).await;\n    }\n}\n\nunsafe extern \"C\" {\n    static __start_ipc: u8;\n    static __end_ipc: u8;\n}\n\n#[embassy_executor::main]\nasync fn main(spawner: Spawner) {\n    let p = embassy_nrf::init(Default::default());\n\n    info!(\"Hello World!\");\n\n    spawner.spawn(unwrap!(blink_task(p.P0_02.into())));\n\n    let ipc_mem = unsafe {\n        let ipc_start = &__start_ipc as *const u8 as *mut MaybeUninit<u8>;\n        let ipc_end = &__end_ipc as *const u8 as *mut MaybeUninit<u8>;\n        let ipc_len = ipc_end.offset_from(ipc_start) as usize;\n        slice::from_raw_parts_mut(ipc_start, ipc_len)\n    };\n\n    static mut TRACE_BUF: [u8; 4096] = [0u8; 4096];\n    let mut config = uarte::Config::default();\n    config.baudrate = Baudrate::BAUD1M;\n    let uart = BufferedUarteTx::new(\n        //let trace_uart = BufferedUarteTx::new(\n        unsafe { peripherals::SERIAL0::steal() },\n        unsafe { peripherals::P0_01::steal() },\n        Irqs,\n        //unsafe { peripherals::P0_14::steal() },\n        config,\n        unsafe { &mut *addr_of_mut!(TRACE_BUF) },\n    );\n\n    static STATE: StaticCell<State> = StaticCell::new();\n    static TRACE: StaticCell<TraceBuffer> = StaticCell::new();\n    let (device, control, runner, tracer) =\n        embassy_net_nrf91::new_with_trace(STATE.init(State::new()), ipc_mem, TRACE.init(TraceBuffer::new())).await;\n    spawner.spawn(unwrap!(modem_task(runner)));\n    spawner.spawn(unwrap!(trace_task(uart, tracer)));\n\n    let config = embassy_net::Config::default();\n\n    // Generate random seed.\n    let mut rng = CcRng::new_blocking(p.CC_RNG);\n    let seed = rng.blocking_next_u64();\n\n    // Init network stack\n    static RESOURCES: StaticCell<StackResources<2>> = StaticCell::new();\n    let (stack, runner) = embassy_net::new(device, config, RESOURCES.init(StackResources::<2>::new()), seed);\n\n    spawner.spawn(unwrap!(net_task(runner)));\n\n    static CONTROL: StaticCell<context::Control<'static>> = StaticCell::new();\n    let control = CONTROL.init(context::Control::new(control, 0).await);\n\n    spawner.spawn(unwrap!(control_task(\n        control,\n        context::Config {\n            apn: b\"iot.nat.es\",\n            auth_prot: context::AuthProt::Pap,\n            auth: Some((b\"orange\", b\"orange\")),\n            pin: None,\n        },\n        stack\n    )));\n\n    stack.wait_config_up().await;\n\n    let mut rx_buffer = [0; 4096];\n    let mut tx_buffer = [0; 4096];\n    loop {\n        let mut socket = embassy_net::tcp::TcpSocket::new(stack, &mut rx_buffer, &mut tx_buffer);\n        socket.set_timeout(Some(Duration::from_secs(10)));\n\n        info!(\"Connecting...\");\n        let host_addr = embassy_net::Ipv4Address::from_str(\"45.79.112.203\").unwrap();\n        if let Err(e) = socket.connect((host_addr, 4242)).await {\n            warn!(\"connect error: {:?}\", e);\n            Timer::after_secs(10).await;\n            continue;\n        }\n        info!(\"Connected to {:?}\", socket.remote_endpoint());\n\n        let msg = b\"Hello world!\\n\";\n        for _ in 0..10 {\n            if let Err(e) = socket.write_all(msg).await {\n                warn!(\"write error: {:?}\", e);\n                break;\n            }\n            info!(\"txd: {}\", core::str::from_utf8(msg).unwrap());\n            Timer::after_secs(1).await;\n        }\n        Timer::after_secs(4).await;\n    }\n}\n"
  },
  {
    "path": "examples/rp/.cargo/config.toml",
    "content": "[target.'cfg(all(target_arch = \"arm\", target_os = \"none\"))']\nrunner = \"probe-rs run --chip RP2040\"\n\n[build]\ntarget = \"thumbv6m-none-eabi\"        # Cortex-M0 and Cortex-M0+\n\n[env]\nDEFMT_LOG = \"debug\"\n"
  },
  {
    "path": "examples/rp/Cargo.toml",
    "content": "[package]\nedition = \"2024\"\nname = \"embassy-rp-examples\"\nversion = \"0.1.0\"\nlicense = \"MIT OR Apache-2.0\"\n\npublish = false\n\n[dependencies]\nembassy-embedded-hal = { version = \"0.6.0\", path = \"../../embassy-embedded-hal\", features = [\"defmt\"] }\nembassy-sync = { version = \"0.8.0\", path = \"../../embassy-sync\", features = [\"defmt\"] }\nembassy-executor = { version = \"0.10.0\", path = \"../../embassy-executor\", features = [\"platform-cortex-m\", \"executor-thread\", \"executor-interrupt\", \"defmt\"] }\nembassy-time = { version = \"0.5.1\", path = \"../../embassy-time\", features = [\"defmt\", \"defmt-timestamp-uptime\"] }\nembassy-rp = { version = \"0.10.0\", path = \"../../embassy-rp\", features = [\"defmt\", \"unstable-pac\", \"time-driver\", \"critical-section-impl\", \"rp2040\"] }\nembassy-usb = { version = \"0.6.0\", path = \"../../embassy-usb\", features = [\"defmt\"] }\nembassy-net = { version = \"0.9.0\", path = \"../../embassy-net\", features = [\"defmt\", \"icmp\", \"tcp\", \"udp\", \"raw\", \"dhcpv4\", \"medium-ethernet\", \"dns\", \"proto-ipv4\", \"proto-ipv6\", \"multicast\"] }\nembassy-net-wiznet = { version = \"0.3.0\", path = \"../../embassy-net-wiznet\", features = [\"defmt\"] }\nembassy-futures = { version = \"0.1.2\", path = \"../../embassy-futures\" }\nembassy-usb-logger = { version = \"0.6.0\", path = \"../../embassy-usb-logger\" }\ncyw43 = { version = \"0.7.0\", path = \"../../cyw43\", features = [\"defmt\", \"firmware-logs\"] }\ncyw43-pio = { version = \"0.10.0\", path = \"../../cyw43-pio\", features = [\"defmt\"] }\n\ndefmt = \"1.0.1\"\ndefmt-rtt = \"1.0.0\"\nfixed = \"1.23.1\"\nfixed-macro = \"1.2\"\n\n# for web request example\nreqwless = { git = \"https://github.com/drogue-iot/reqwless\", rev = \"68f703dfc16e6f7f964b7238c922c0d433118872\", features = [\"defmt\"] }\nserde = { version = \"1.0.203\", default-features = false, features = [\"derive\"] }\nserde-json-core = \"0.5.1\"\n\n# for assign resources example\nassign-resources = { git = \"https://github.com/adamgreig/assign-resources\", rev = \"bd22cb7a92031fb16f74a5da42469d466c33383e\" }\n\n#cortex-m = { version = \"0.7.6\", features = [\"critical-section-single-core\"] }\ncortex-m = { version = \"0.7.6\", features = [\"inline-asm\"] }\ncortex-m-rt = \"0.7.0\"\ncritical-section = \"1.1\"\npanic-probe = { version = \"1.0.0\", features = [\"print-defmt\"] }\ndisplay-interface-spi = \"0.5.0\"\nembedded-graphics = \"0.8.1\"\nmipidsi = \"0.8.0\"\ndisplay-interface = \"0.5.0\"\nbyte-slice-cast = { version = \"1.2.0\", default-features = false }\nsmart-leds = \"0.4.0\"\nheapless = \"0.9\"\nusbd-hid = \"0.10.0\"\n\nembedded-hal-1 = { package = \"embedded-hal\", version = \"1.0\" }\nembedded-hal-async = \"1.0\"\nembedded-hal-bus = { version = \"0.1\", features = [\"async\"] }\nembedded-io-async = { version = \"0.7.0\", features = [\"defmt\"] }\nembedded-storage = { version = \"0.3\" }\nstatic_cell = \"2.1\"\nportable-atomic = { version = \"1.5\", features = [\"critical-section\"] }\nlog = \"0.4\"\nrand = { version = \"0.9.0\", default-features = false }\nembedded-sdmmc = \"0.7.0\"\n\n[profile.release]\n# Enable generation of debug symbols even on release builds\ndebug = true\n\n[package.metadata.embassy]\nbuild = [\n  { target = \"thumbv6m-none-eabi\", artifact-dir = \"out/examples/rp\" }\n]\n"
  },
  {
    "path": "examples/rp/build.rs",
    "content": "//! This build script copies the `memory.x` file from the crate root into\n//! a directory where the linker can always find it at build time.\n//! For many projects this is optional, as the linker always searches the\n//! project root directory -- wherever `Cargo.toml` is. However, if you\n//! are using a workspace or have a more complicated build setup, this\n//! build script becomes required. Additionally, by requesting that\n//! Cargo re-run the build script whenever `memory.x` is changed,\n//! updating `memory.x` ensures a rebuild of the application with the\n//! new memory settings.\n\nuse std::env;\nuse std::fs::File;\nuse std::io::Write;\nuse std::path::PathBuf;\n\nfn main() {\n    // Put `memory.x` in our output directory and ensure it's\n    // on the linker search path.\n    let out = &PathBuf::from(env::var_os(\"OUT_DIR\").unwrap());\n    File::create(out.join(\"memory.x\"))\n        .unwrap()\n        .write_all(include_bytes!(\"memory.x\"))\n        .unwrap();\n    println!(\"cargo:rustc-link-search={}\", out.display());\n\n    // By default, Cargo will re-run a build script whenever\n    // any file in the project changes. By specifying `memory.x`\n    // here, we ensure the build script is only re-run when\n    // `memory.x` is changed.\n    println!(\"cargo:rerun-if-changed=memory.x\");\n\n    println!(\"cargo:rustc-link-arg-bins=--nmagic\");\n    println!(\"cargo:rustc-link-arg-bins=-Tlink.x\");\n    println!(\"cargo:rustc-link-arg-bins=-Tlink-rp.x\");\n    println!(\"cargo:rustc-link-arg-bins=-Tdefmt.x\");\n}\n"
  },
  {
    "path": "examples/rp/memory.x",
    "content": "MEMORY {\n    BOOT2 : ORIGIN = 0x10000000, LENGTH = 0x100\n    FLASH : ORIGIN = 0x10000100, LENGTH = 2048K - 0x100\n\n    /* Pick one of the two options for RAM layout     */\n\n    /* OPTION A: Use all RAM banks as one big block   */\n    /* Reasonable, unless you are doing something     */\n    /* really particular with DMA or other concurrent */\n    /* access that would benefit from striping        */\n    RAM   : ORIGIN = 0x20000000, LENGTH = 264K\n\n    /* OPTION B: Keep the unstriped sections separate */\n    /* RAM: ORIGIN = 0x20000000, LENGTH = 256K        */\n    /* SCRATCH_A: ORIGIN = 0x20040000, LENGTH = 4K    */\n    /* SCRATCH_B: ORIGIN = 0x20041000, LENGTH = 4K    */\n}\n"
  },
  {
    "path": "examples/rp/src/bin/adc.rs",
    "content": "//! This example test the ADC (Analog to Digital Conversion) of the RS2040 pin 26, 27 and 28.\n//! It also reads the temperature sensor in the chip.\n\n#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_rp::adc::{Adc, Channel, Config, InterruptHandler};\nuse embassy_rp::bind_interrupts;\nuse embassy_rp::gpio::Pull;\nuse embassy_time::Timer;\nuse {defmt_rtt as _, panic_probe as _};\n\nbind_interrupts!(\n    /// Binds the ADC interrupts.\n    struct Irqs {\n        ADC_IRQ_FIFO => InterruptHandler;\n    }\n);\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let p = embassy_rp::init(Default::default());\n    let mut adc = Adc::new(p.ADC, Irqs, Config::default());\n\n    let mut p26 = Channel::new_pin(p.PIN_26, Pull::None);\n    let mut p27 = Channel::new_pin(p.PIN_27, Pull::None);\n    let mut p28 = Channel::new_pin(p.PIN_28, Pull::None);\n    let mut ts = Channel::new_temp_sensor(p.ADC_TEMP_SENSOR);\n\n    loop {\n        let level = adc.read(&mut p26).await.unwrap();\n        info!(\"Pin 26 ADC: {}\", level);\n        let level = adc.read(&mut p27).await.unwrap();\n        info!(\"Pin 27 ADC: {}\", level);\n        let level = adc.read(&mut p28).await.unwrap();\n        info!(\"Pin 28 ADC: {}\", level);\n        let temp = adc.read(&mut ts).await.unwrap();\n        info!(\"Temp: {} degrees\", convert_to_celsius(temp));\n        Timer::after_secs(1).await;\n    }\n}\n\nfn convert_to_celsius(raw_temp: u16) -> f32 {\n    // According to chapter 4.9.5. Temperature Sensor in RP2040 datasheet\n    let temp = 27.0 - (raw_temp as f32 * 3.3 / 4096.0 - 0.706) / 0.001721;\n    let sign = if temp < 0.0 { -1.0 } else { 1.0 };\n    let rounded_temp_x10: i16 = ((temp * 10.0) + 0.5 * sign) as i16;\n    (rounded_temp_x10 as f32) / 10.0\n}\n"
  },
  {
    "path": "examples/rp/src/bin/adc_dma.rs",
    "content": "//! This example shows how to use the RP2040 ADC with DMA, both single- and multichannel reads.\n//! For multichannel, the samples are interleaved in the buffer:\n//! `[ch1, ch2, ch3, ch4, ch1, ch2, ch3, ch4, ...]`\n#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_rp::adc::{Adc, Channel, Config, InterruptHandler};\nuse embassy_rp::gpio::Pull;\nuse embassy_rp::peripherals::DMA_CH0;\nuse embassy_rp::{bind_interrupts, dma};\nuse embassy_time::{Duration, Ticker};\nuse {defmt_rtt as _, panic_probe as _};\n\nbind_interrupts!(struct Irqs {\n    ADC_IRQ_FIFO => InterruptHandler;\n    DMA_IRQ_0 => dma::InterruptHandler<DMA_CH0>;\n});\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let p = embassy_rp::init(Default::default());\n    info!(\"Here we go!\");\n\n    let mut adc = Adc::new(p.ADC, Irqs, Config::default());\n    let mut dma_ch = dma::Channel::new(p.DMA_CH0, Irqs);\n    let mut pin = Channel::new_pin(p.PIN_26, Pull::Up);\n    let mut pins = [\n        Channel::new_pin(p.PIN_27, Pull::Down),\n        Channel::new_pin(p.PIN_28, Pull::None),\n        Channel::new_pin(p.PIN_29, Pull::Up),\n        Channel::new_temp_sensor(p.ADC_TEMP_SENSOR),\n    ];\n\n    const BLOCK_SIZE: usize = 100;\n    const NUM_CHANNELS: usize = 4;\n    let mut ticker = Ticker::every(Duration::from_secs(1));\n    loop {\n        // Read 100 samples from a single channel\n        let mut buf = [0_u16; BLOCK_SIZE];\n        let div = 479; // 100kHz sample rate (48Mhz / 100kHz - 1)\n        adc.read_many(&mut pin, &mut buf, div, &mut dma_ch).await.unwrap();\n        info!(\"single: {:?} ...etc\", buf[..8]);\n\n        // Read 100 samples from 4 channels interleaved\n        let mut buf = [0_u16; { BLOCK_SIZE * NUM_CHANNELS }];\n        let div = 119; // 100kHz sample rate (48Mhz / 100kHz * 4ch - 1)\n        adc.read_many_multichannel(&mut pins, &mut buf, div, &mut dma_ch)\n            .await\n            .unwrap();\n        info!(\"multi:  {:?} ...etc\", buf[..NUM_CHANNELS * 2]);\n\n        ticker.next().await;\n    }\n}\n"
  },
  {
    "path": "examples/rp/src/bin/assign_resources.rs",
    "content": "//! This example demonstrates how to assign resources to multiple tasks by splitting up the peripherals.\n//! It is not about sharing the same resources between tasks, see sharing.rs for that or head to https://embassy.dev/book/#_sharing_peripherals_between_tasks)\n//! Of course splitting up resources and sharing resources can be combined, yet this example is only about splitting up resources.\n//!\n//! There are basically two ways we demonstrate here:\n//! 1) Assigning resources to a task by passing parts of the peripherals\n//! 2) Assigning resources to a task by passing a struct with the split up peripherals, using the assign-resources macro\n//!\n//! using four LEDs on Pins 10, 11, 20 and 21\n\n#![no_std]\n#![no_main]\n\nuse assign_resources::assign_resources;\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_rp::Peri;\nuse embassy_rp::gpio::{Level, Output};\nuse embassy_rp::peripherals::{self, PIN_20, PIN_21};\nuse embassy_time::Timer;\nuse {defmt_rtt as _, panic_probe as _};\n\n#[embassy_executor::main]\nasync fn main(spawner: Spawner) {\n    // initialize the peripherals\n    let p = embassy_rp::init(Default::default());\n\n    // 1) Assigning a resource to a task by passing parts of the peripherals.\n    spawner.spawn(double_blinky_manually_assigned(spawner, p.PIN_20, p.PIN_21).unwrap());\n\n    // 2) Using the assign-resources macro to assign resources to a task.\n    // we perform the split, see further below for the definition of the resources struct\n    let r = split_resources!(p);\n    // and then we can use them\n    spawner.spawn(double_blinky_macro_assigned(spawner, r.leds).unwrap());\n}\n\n// 1) Assigning a resource to a task by passing parts of the peripherals.\n#[embassy_executor::task]\nasync fn double_blinky_manually_assigned(\n    _spawner: Spawner,\n    pin_20: Peri<'static, PIN_20>,\n    pin_21: Peri<'static, PIN_21>,\n) {\n    let mut led_20 = Output::new(pin_20, Level::Low);\n    let mut led_21 = Output::new(pin_21, Level::High);\n\n    loop {\n        info!(\"toggling leds\");\n        led_20.toggle();\n        led_21.toggle();\n        Timer::after_secs(1).await;\n    }\n}\n\n// 2) Using the assign-resources macro to assign resources to a task.\n// first we define the resources we want to assign to the task using the assign_resources! macro\n// basically this will split up the peripherals struct into smaller structs, that we define here\n// naming is up to you, make sure your future self understands what you did here\nassign_resources! {\n    leds: Leds{\n        led_10: PIN_10,\n        led_11: PIN_11,\n    }\n    // add more resources to more structs if needed, for example defining one struct for each task\n}\n// this could be done in another file and imported here, but for the sake of simplicity we do it here\n// see https://github.com/adamgreig/assign-resources for more information\n\n// 2) Using the split resources in a task\n#[embassy_executor::task]\nasync fn double_blinky_macro_assigned(_spawner: Spawner, r: Leds) {\n    let mut led_10 = Output::new(r.led_10, Level::Low);\n    let mut led_11 = Output::new(r.led_11, Level::High);\n\n    loop {\n        info!(\"toggling leds\");\n        led_10.toggle();\n        led_11.toggle();\n        Timer::after_secs(1).await;\n    }\n}\n"
  },
  {
    "path": "examples/rp/src/bin/blinky.rs",
    "content": "//! This example test the RP Pico on board LED.\n//!\n//! It does not work with the RP Pico W board. See wifi_blinky.rs.\n\n#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_rp::gpio;\nuse embassy_time::Timer;\nuse gpio::{Level, Output};\nuse {defmt_rtt as _, panic_probe as _};\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let p = embassy_rp::init(Default::default());\n    let mut led = Output::new(p.PIN_25, Level::Low);\n\n    loop {\n        info!(\"led on!\");\n        led.set_high();\n        Timer::after_secs(1).await;\n\n        info!(\"led off!\");\n        led.set_low();\n        Timer::after_secs(1).await;\n    }\n}\n"
  },
  {
    "path": "examples/rp/src/bin/blinky_two_channels.rs",
    "content": "#![no_std]\n#![no_main]\n/// This example demonstrates how to access a given pin from more than one embassy task\n/// The on-board LED is toggled by two tasks with slightly different periods, leading to the\n/// apparent duty cycle of the LED increasing, then decreasing, linearly. The phenomenon is similar\n/// to interference and the 'beats' you can hear if you play two frequencies close to one another\n/// [Link explaining it](https://www.physicsclassroom.com/class/sound/Lesson-3/Interference-and-Beats)\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_rp::gpio;\nuse embassy_sync::blocking_mutex::raw::ThreadModeRawMutex;\nuse embassy_sync::channel::{Channel, Sender};\nuse embassy_time::{Duration, Ticker};\nuse gpio::{Level, Output};\nuse {defmt_rtt as _, panic_probe as _};\n\nenum LedState {\n    Toggle,\n}\nstatic CHANNEL: Channel<ThreadModeRawMutex, LedState, 64> = Channel::new();\n\n#[embassy_executor::main]\nasync fn main(spawner: Spawner) {\n    let p = embassy_rp::init(Default::default());\n    let mut led = Output::new(p.PIN_25, Level::High);\n\n    let dt = 100 * 1_000_000;\n    let k = 1.003;\n\n    spawner.spawn(unwrap!(toggle_led(CHANNEL.sender(), Duration::from_nanos(dt))));\n    spawner.spawn(unwrap!(toggle_led(\n        CHANNEL.sender(),\n        Duration::from_nanos((dt as f64 * k) as u64)\n    )));\n\n    loop {\n        match CHANNEL.receive().await {\n            LedState::Toggle => led.toggle(),\n        }\n    }\n}\n\n#[embassy_executor::task(pool_size = 2)]\nasync fn toggle_led(control: Sender<'static, ThreadModeRawMutex, LedState, 64>, delay: Duration) {\n    let mut ticker = Ticker::every(delay);\n    loop {\n        control.send(LedState::Toggle).await;\n        ticker.next().await;\n    }\n}\n"
  },
  {
    "path": "examples/rp/src/bin/blinky_two_tasks.rs",
    "content": "#![no_std]\n#![no_main]\n/// This example demonstrates how to access a given pin from more than one embassy task\n/// The on-board LED is toggled by two tasks with slightly different periods, leading to the\n/// apparent duty cycle of the LED increasing, then decreasing, linearly. The phenomenon is similar\n/// to interference and the 'beats' you can hear if you play two frequencies close to one another\n/// [Link explaining it](https://www.physicsclassroom.com/class/sound/Lesson-3/Interference-and-Beats)\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_rp::gpio;\nuse embassy_sync::blocking_mutex::raw::ThreadModeRawMutex;\nuse embassy_sync::mutex::Mutex;\nuse embassy_time::{Duration, Ticker};\nuse gpio::{Level, Output};\nuse {defmt_rtt as _, panic_probe as _};\n\ntype LedType = Mutex<ThreadModeRawMutex, Option<Output<'static>>>;\nstatic LED: LedType = Mutex::new(None);\n\n#[embassy_executor::main]\nasync fn main(spawner: Spawner) {\n    let p = embassy_rp::init(Default::default());\n    // set the content of the global LED reference to the real LED pin\n    let led = Output::new(p.PIN_25, Level::High);\n    // inner scope is so that once the mutex is written to, the MutexGuard is dropped, thus the\n    // Mutex is released\n    {\n        *(LED.lock().await) = Some(led);\n    }\n    let dt = 100 * 1_000_000;\n    let k = 1.003;\n\n    spawner.spawn(unwrap!(toggle_led(&LED, Duration::from_nanos(dt))));\n    spawner.spawn(unwrap!(toggle_led(&LED, Duration::from_nanos((dt as f64 * k) as u64))));\n}\n\n#[embassy_executor::task(pool_size = 2)]\nasync fn toggle_led(led: &'static LedType, delay: Duration) {\n    let mut ticker = Ticker::every(delay);\n    loop {\n        {\n            let mut led_unlocked = led.lock().await;\n            if let Some(pin_ref) = led_unlocked.as_mut() {\n                pin_ref.toggle();\n            }\n        }\n        ticker.next().await;\n    }\n}\n"
  },
  {
    "path": "examples/rp/src/bin/button.rs",
    "content": "//! This example uses the RP Pico on board LED to test input pin 28. This is not the button on the board.\n//!\n//! It does not work with the RP Pico W board. Use wifi_blinky.rs and add input pin.\n\n#![no_std]\n#![no_main]\n\nuse embassy_executor::Spawner;\nuse embassy_rp::gpio::{Input, Level, Output, Pull};\nuse {defmt_rtt as _, panic_probe as _};\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let p = embassy_rp::init(Default::default());\n    let mut led = Output::new(p.PIN_25, Level::Low);\n\n    // Use PIN_28, Pin34 on J0 for RP Pico, as a input.\n    // You need to add your own button.\n    let button = Input::new(p.PIN_28, Pull::Up);\n\n    loop {\n        if button.is_high() {\n            led.set_high();\n        } else {\n            led.set_low();\n        }\n    }\n}\n"
  },
  {
    "path": "examples/rp/src/bin/debounce.rs",
    "content": "//! This example shows the ease of debouncing a button with async rust.\n//! Hook up a button or switch between pin 9 and ground.\n\n#![no_std]\n#![no_main]\n\nuse defmt::info;\nuse embassy_executor::Spawner;\nuse embassy_rp::gpio::{Input, Level, Pull};\nuse embassy_time::{Duration, Instant, Timer, with_deadline};\nuse {defmt_rtt as _, panic_probe as _};\n\npub struct Debouncer<'a> {\n    input: Input<'a>,\n    debounce: Duration,\n}\n\nimpl<'a> Debouncer<'a> {\n    pub fn new(input: Input<'a>, debounce: Duration) -> Self {\n        Self { input, debounce }\n    }\n\n    pub async fn debounce(&mut self) -> Level {\n        loop {\n            let l1 = self.input.get_level();\n\n            self.input.wait_for_any_edge().await;\n\n            Timer::after(self.debounce).await;\n\n            let l2 = self.input.get_level();\n            if l1 != l2 {\n                break l2;\n            }\n        }\n    }\n}\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let p = embassy_rp::init(Default::default());\n    let mut btn = Debouncer::new(Input::new(p.PIN_9, Pull::Up), Duration::from_millis(20));\n\n    info!(\"Debounce Demo\");\n\n    loop {\n        // button pressed\n        btn.debounce().await;\n        let start = Instant::now();\n        info!(\"Button Press\");\n\n        match with_deadline(start + Duration::from_secs(1), btn.debounce()).await {\n            // Button Released < 1s\n            Ok(_) => {\n                info!(\"Button pressed for: {}ms\", start.elapsed().as_millis());\n                continue;\n            }\n            // button held for > 1s\n            Err(_) => {\n                info!(\"Button Held\");\n            }\n        }\n\n        match with_deadline(start + Duration::from_secs(5), btn.debounce()).await {\n            // Button released <5s\n            Ok(_) => {\n                info!(\"Button pressed for: {}ms\", start.elapsed().as_millis());\n                continue;\n            }\n            // button held for > >5s\n            Err(_) => {\n                info!(\"Button Long Held\");\n            }\n        }\n\n        // wait for button release before handling another press\n        btn.debounce().await;\n        info!(\"Button pressed for: {}ms\", start.elapsed().as_millis());\n    }\n}\n"
  },
  {
    "path": "examples/rp/src/bin/ethernet_w5500_icmp.rs",
    "content": "//! This example implements an echo (ping) with an ICMP Socket and using defmt to report the results.\n//!\n//! Although there is a better way to execute pings using the child module ping of the icmp module,\n//! this example allows for other icmp messages like `Destination unreachable` to be sent aswell.\n//!\n//! Example written for the [`WIZnet W5500-EVB-Pico`](https://docs.wiznet.io/Product/iEthernet/W5500/w5500-evb-pico) board.\n\n#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_futures::yield_now;\nuse embassy_net::icmp::{ChecksumCapabilities, IcmpEndpoint, IcmpSocket, Icmpv4Packet, Icmpv4Repr, PacketMetadata};\nuse embassy_net::{Stack, StackResources};\nuse embassy_net_wiznet::chip::W5500;\nuse embassy_net_wiznet::*;\nuse embassy_rp::clocks::RoscRng;\nuse embassy_rp::gpio::{Input, Level, Output, Pull};\nuse embassy_rp::peripherals::{DMA_CH0, DMA_CH1, SPI0};\nuse embassy_rp::spi::{Async, Config as SpiConfig, Spi};\nuse embassy_rp::{bind_interrupts, dma};\nuse embassy_time::{Delay, Instant, Timer};\nuse embedded_hal_bus::spi::ExclusiveDevice;\nuse static_cell::StaticCell;\nuse {defmt_rtt as _, panic_probe as _};\n\nbind_interrupts!(struct Irqs {\n    DMA_IRQ_0 => dma::InterruptHandler<DMA_CH0>, dma::InterruptHandler<DMA_CH1>;\n});\n\ntype ExclusiveSpiDevice = ExclusiveDevice<Spi<'static, SPI0, Async>, Output<'static>, Delay>;\n\n#[embassy_executor::task]\nasync fn ethernet_task(runner: Runner<'static, W5500, ExclusiveSpiDevice, Input<'static>, Output<'static>>) -> ! {\n    runner.run().await\n}\n\n#[embassy_executor::task]\nasync fn net_task(mut runner: embassy_net::Runner<'static, Device<'static>>) -> ! {\n    runner.run().await\n}\n\n#[embassy_executor::main]\nasync fn main(spawner: Spawner) {\n    let p = embassy_rp::init(Default::default());\n    let mut rng = RoscRng;\n\n    let mut spi_cfg = SpiConfig::default();\n    spi_cfg.frequency = 50_000_000;\n    let (miso, mosi, clk) = (p.PIN_16, p.PIN_19, p.PIN_18);\n    let spi = Spi::new(p.SPI0, clk, mosi, miso, p.DMA_CH0, p.DMA_CH1, Irqs, spi_cfg);\n    let cs = Output::new(p.PIN_17, Level::High);\n    let w5500_int = Input::new(p.PIN_21, Pull::Up);\n    let w5500_reset = Output::new(p.PIN_20, Level::High);\n\n    let mac_addr = [0x02, 0x00, 0x00, 0x00, 0x00, 0x00];\n    static STATE: StaticCell<State<8, 8>> = StaticCell::new();\n    let state = STATE.init(State::<8, 8>::new());\n    let (device, runner) = embassy_net_wiznet::new(\n        mac_addr,\n        state,\n        ExclusiveDevice::new(spi, cs, Delay),\n        w5500_int,\n        w5500_reset,\n    )\n    .await\n    .unwrap();\n    spawner.spawn(unwrap!(ethernet_task(runner)));\n\n    // Generate random seed\n    let seed = rng.next_u64();\n\n    // Init network stack\n    static RESOURCES: StaticCell<StackResources<3>> = StaticCell::new();\n    let (stack, runner) = embassy_net::new(\n        device,\n        embassy_net::Config::dhcpv4(Default::default()),\n        RESOURCES.init(StackResources::new()),\n        seed,\n    );\n\n    // Launch network task\n    spawner.spawn(unwrap!(net_task(runner)));\n\n    info!(\"Waiting for DHCP...\");\n    let cfg = wait_for_config(stack).await;\n    let local_addr = cfg.address.address();\n    info!(\"IP address: {:?}\", local_addr);\n\n    // Then we can use it!\n    let mut rx_buffer = [0; 256];\n    let mut tx_buffer = [0; 256];\n    let mut rx_meta = [PacketMetadata::EMPTY];\n    let mut tx_meta = [PacketMetadata::EMPTY];\n\n    // Identifier used for the ICMP socket\n    let ident = 42;\n\n    // Create and bind the socket\n    let mut socket = IcmpSocket::new(stack, &mut rx_meta, &mut rx_buffer, &mut tx_meta, &mut tx_buffer);\n    socket.bind(IcmpEndpoint::Ident(ident)).unwrap();\n\n    // Create the repr for the packet\n    let icmp_repr = Icmpv4Repr::EchoRequest {\n        ident,\n        seq_no: 0,\n        data: b\"Hello, icmp!\",\n    };\n\n    // Send the packet and store the starting instant to mesure latency later\n    let start = socket\n        .send_to_with(icmp_repr.buffer_len(), cfg.gateway.unwrap(), |buf| {\n            // Create and populate the packet buffer allocated by `send_to_with`\n            let mut icmp_packet = Icmpv4Packet::new_unchecked(buf);\n            icmp_repr.emit(&mut icmp_packet, &ChecksumCapabilities::default());\n            (icmp_repr.buffer_len(), Instant::now()) // Return the instant where the packet was sent\n        })\n        .await\n        .unwrap();\n\n    // Recieve and log the data of the reply\n    socket\n        .recv_from_with(|(buf, addr)| {\n            let packet = Icmpv4Packet::new_checked(buf).unwrap();\n            info!(\n                \"Recieved {:?} from {} in {}ms\",\n                packet.data(),\n                addr,\n                start.elapsed().as_millis()\n            );\n        })\n        .await\n        .unwrap();\n\n    loop {\n        Timer::after_secs(10).await;\n    }\n}\n\nasync fn wait_for_config(stack: Stack<'static>) -> embassy_net::StaticConfigV4 {\n    loop {\n        if let Some(config) = stack.config_v4() {\n            return config.clone();\n        }\n        yield_now().await;\n    }\n}\n"
  },
  {
    "path": "examples/rp/src/bin/ethernet_w5500_icmp_ping.rs",
    "content": "//! This example implements a LAN ping scan with the ping utilities in the icmp module of embassy-net.\n//!\n//! Example written for the [`WIZnet W5500-EVB-Pico`](https://docs.wiznet.io/Product/iEthernet/W5500/w5500-evb-pico) board.\n\n#![no_std]\n#![no_main]\n\nuse core::net::Ipv4Addr;\nuse core::ops::Not;\nuse core::str::FromStr;\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_futures::yield_now;\nuse embassy_net::icmp::PacketMetadata;\nuse embassy_net::icmp::ping::{PingManager, PingParams};\nuse embassy_net::{Ipv4Cidr, Stack, StackResources};\nuse embassy_net_wiznet::chip::W5500;\nuse embassy_net_wiznet::*;\nuse embassy_rp::clocks::RoscRng;\nuse embassy_rp::gpio::{Input, Level, Output, Pull};\nuse embassy_rp::peripherals::{DMA_CH0, DMA_CH1, SPI0};\nuse embassy_rp::spi::{Async, Config as SpiConfig, Spi};\nuse embassy_rp::{bind_interrupts, dma};\nuse embassy_time::{Delay, Duration};\nuse embedded_hal_bus::spi::ExclusiveDevice;\nuse static_cell::StaticCell;\nuse {defmt_rtt as _, panic_probe as _};\n\nbind_interrupts!(struct Irqs {\n    DMA_IRQ_0 => dma::InterruptHandler<DMA_CH0>, dma::InterruptHandler<DMA_CH1>;\n});\n\ntype ExclusiveSpiDevice = ExclusiveDevice<Spi<'static, SPI0, Async>, Output<'static>, Delay>;\n\n#[embassy_executor::task]\nasync fn ethernet_task(runner: Runner<'static, W5500, ExclusiveSpiDevice, Input<'static>, Output<'static>>) -> ! {\n    runner.run().await\n}\n\n#[embassy_executor::task]\nasync fn net_task(mut runner: embassy_net::Runner<'static, Device<'static>>) -> ! {\n    runner.run().await\n}\n\n#[embassy_executor::main]\nasync fn main(spawner: Spawner) {\n    let p = embassy_rp::init(Default::default());\n    let mut rng = RoscRng;\n\n    let mut spi_cfg = SpiConfig::default();\n    spi_cfg.frequency = 50_000_000;\n    let (miso, mosi, clk) = (p.PIN_16, p.PIN_19, p.PIN_18);\n    let spi = Spi::new(p.SPI0, clk, mosi, miso, p.DMA_CH0, p.DMA_CH1, Irqs, spi_cfg);\n    let cs = Output::new(p.PIN_17, Level::High);\n    let w5500_int = Input::new(p.PIN_21, Pull::Up);\n    let w5500_reset = Output::new(p.PIN_20, Level::High);\n\n    let mac_addr = [0x02, 0x00, 0x00, 0x00, 0x00, 0x00];\n    static STATE: StaticCell<State<8, 8>> = StaticCell::new();\n    let state = STATE.init(State::<8, 8>::new());\n    let (device, runner) = embassy_net_wiznet::new(\n        mac_addr,\n        state,\n        ExclusiveDevice::new(spi, cs, Delay),\n        w5500_int,\n        w5500_reset,\n    )\n    .await\n    .unwrap();\n    spawner.spawn(unwrap!(ethernet_task(runner)));\n\n    // Generate random seed\n    let seed = rng.next_u64();\n\n    // Init network stack\n    static RESOURCES: StaticCell<StackResources<3>> = StaticCell::new();\n    let (stack, runner) = embassy_net::new(\n        device,\n        embassy_net::Config::dhcpv4(Default::default()),\n        RESOURCES.init(StackResources::new()),\n        seed,\n    );\n\n    // Launch network task\n    spawner.spawn(unwrap!(net_task(runner)));\n\n    info!(\"Waiting for DHCP...\");\n    let cfg = wait_for_config(stack).await;\n    let local_addr = cfg.address.address();\n    info!(\"IP address: {:?}\", local_addr);\n    let gateway = cfg.gateway.unwrap();\n    let mask = cfg.address.netmask();\n    let lower_bound = (gateway.to_bits() & mask.to_bits()) + 1;\n    let upper_bound = gateway.to_bits() | mask.to_bits().not();\n    let addr_range = lower_bound..=upper_bound;\n\n    // Then we can use it!\n    let mut rx_buffer = [0; 256];\n    let mut tx_buffer = [0; 256];\n    let mut rx_meta = [PacketMetadata::EMPTY];\n    let mut tx_meta = [PacketMetadata::EMPTY];\n\n    // Create the ping manager instance\n    let mut ping_manager = PingManager::new(stack, &mut rx_meta, &mut rx_buffer, &mut tx_meta, &mut tx_buffer);\n    let addr = \"192.168.8.1\"; // Address to ping to\n    // Create the PingParams with the target address\n    let mut ping_params = PingParams::new(Ipv4Addr::from_str(addr).unwrap());\n    // (optional) Set custom properties of the ping\n    ping_params.set_payload(b\"Hello, Ping!\"); // custom payload\n    ping_params.set_count(1); // ping 1 times per ping call\n    ping_params.set_timeout(Duration::from_millis(500)); // wait .5 seconds instead of 4\n\n    info!(\"Online hosts in {}:\", Ipv4Cidr::from_netmask(gateway, mask).unwrap());\n    let mut total_online_hosts = 0u32;\n    for addr in addr_range {\n        let ip_addr = Ipv4Addr::from_bits(addr);\n        // Set the target address in the ping params\n        ping_params.set_target(ip_addr);\n        // Execute the ping with the given parameters and wait for the reply\n        match ping_manager.ping(&ping_params).await {\n            Ok(time) => {\n                info!(\"{} is online\\n- latency: {}ms\\n\", ip_addr, time.as_millis());\n                total_online_hosts += 1;\n            }\n            _ => continue,\n        }\n    }\n    info!(\"Ping scan complete, total online hosts: {}\", total_online_hosts);\n}\n\nasync fn wait_for_config(stack: Stack<'static>) -> embassy_net::StaticConfigV4 {\n    loop {\n        if let Some(config) = stack.config_v4() {\n            return config.clone();\n        }\n        yield_now().await;\n    }\n}\n"
  },
  {
    "path": "examples/rp/src/bin/ethernet_w5500_multisocket.rs",
    "content": "//! This example shows how you can allow multiple simultaneous TCP connections, by having multiple sockets listening on the same port.\n//!\n//! Example written for the [`WIZnet W5500-EVB-Pico`](https://docs.wiznet.io/Product/iEthernet/W5500/w5500-evb-pico) board.\n\n#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_futures::yield_now;\nuse embassy_net::{Stack, StackResources};\nuse embassy_net_wiznet::chip::W5500;\nuse embassy_net_wiznet::*;\nuse embassy_rp::clocks::RoscRng;\nuse embassy_rp::gpio::{Input, Level, Output, Pull};\nuse embassy_rp::peripherals::{DMA_CH0, DMA_CH1, SPI0};\nuse embassy_rp::spi::{Async, Config as SpiConfig, Spi};\nuse embassy_rp::{bind_interrupts, dma};\nuse embassy_time::{Delay, Duration};\nuse embedded_hal_bus::spi::ExclusiveDevice;\nuse embedded_io_async::Write;\nuse static_cell::StaticCell;\nuse {defmt_rtt as _, panic_probe as _};\n\nbind_interrupts!(struct Irqs {\n    DMA_IRQ_0 => dma::InterruptHandler<DMA_CH0>, dma::InterruptHandler<DMA_CH1>;\n});\n\n#[embassy_executor::task]\nasync fn ethernet_task(\n    runner: Runner<\n        'static,\n        W5500,\n        ExclusiveDevice<Spi<'static, SPI0, Async>, Output<'static>, Delay>,\n        Input<'static>,\n        Output<'static>,\n    >,\n) -> ! {\n    runner.run().await\n}\n\n#[embassy_executor::task]\nasync fn net_task(mut runner: embassy_net::Runner<'static, Device<'static>>) -> ! {\n    runner.run().await\n}\n\n#[embassy_executor::main]\nasync fn main(spawner: Spawner) {\n    let p = embassy_rp::init(Default::default());\n    let mut rng = RoscRng;\n\n    let mut spi_cfg = SpiConfig::default();\n    spi_cfg.frequency = 50_000_000;\n    let (miso, mosi, clk) = (p.PIN_16, p.PIN_19, p.PIN_18);\n    let spi = Spi::new(p.SPI0, clk, mosi, miso, p.DMA_CH0, p.DMA_CH1, Irqs, spi_cfg);\n    let cs = Output::new(p.PIN_17, Level::High);\n    let w5500_int = Input::new(p.PIN_21, Pull::Up);\n    let w5500_reset = Output::new(p.PIN_20, Level::High);\n\n    let mac_addr = [0x02, 0x00, 0x00, 0x00, 0x00, 0x00];\n    static STATE: StaticCell<State<8, 8>> = StaticCell::new();\n    let state = STATE.init(State::<8, 8>::new());\n    let (device, runner) = embassy_net_wiznet::new(\n        mac_addr,\n        state,\n        ExclusiveDevice::new(spi, cs, Delay),\n        w5500_int,\n        w5500_reset,\n    )\n    .await\n    .unwrap();\n    spawner.spawn(unwrap!(ethernet_task(runner)));\n\n    // Generate random seed\n    let seed = rng.next_u64();\n\n    // Init network stack\n    static RESOURCES: StaticCell<StackResources<3>> = StaticCell::new();\n    let (stack, runner) = embassy_net::new(\n        device,\n        embassy_net::Config::dhcpv4(Default::default()),\n        RESOURCES.init(StackResources::new()),\n        seed,\n    );\n\n    // Launch network task\n    spawner.spawn(unwrap!(net_task(runner)));\n\n    info!(\"Waiting for DHCP...\");\n    let cfg = wait_for_config(stack).await;\n    let local_addr = cfg.address.address();\n    info!(\"IP address: {:?}\", local_addr);\n\n    // Create two sockets listening to the same port, to handle simultaneous connections\n    spawner.spawn(unwrap!(listen_task(stack, 0, 1234)));\n    spawner.spawn(unwrap!(listen_task(stack, 1, 1234)));\n}\n\n#[embassy_executor::task(pool_size = 2)]\nasync fn listen_task(stack: Stack<'static>, id: u8, port: u16) {\n    let mut rx_buffer = [0; 4096];\n    let mut tx_buffer = [0; 4096];\n    let mut buf = [0; 4096];\n    loop {\n        let mut socket = embassy_net::tcp::TcpSocket::new(stack, &mut rx_buffer, &mut tx_buffer);\n        socket.set_timeout(Some(Duration::from_secs(10)));\n\n        info!(\"SOCKET {}: Listening on TCP:{}...\", id, port);\n        if let Err(e) = socket.accept(port).await {\n            warn!(\"accept error: {:?}\", e);\n            continue;\n        }\n        info!(\"SOCKET {}: Received connection from {:?}\", id, socket.remote_endpoint());\n\n        loop {\n            let n = match socket.read(&mut buf).await {\n                Ok(0) => {\n                    warn!(\"read EOF\");\n                    break;\n                }\n                Ok(n) => n,\n                Err(e) => {\n                    warn!(\"SOCKET {}: {:?}\", id, e);\n                    break;\n                }\n            };\n            info!(\"SOCKET {}: rxd {}\", id, core::str::from_utf8(&buf[..n]).unwrap());\n\n            if let Err(e) = socket.write_all(&buf[..n]).await {\n                warn!(\"write error: {:?}\", e);\n                break;\n            }\n        }\n    }\n}\n\nasync fn wait_for_config(stack: Stack<'static>) -> embassy_net::StaticConfigV4 {\n    loop {\n        if let Some(config) = stack.config_v4() {\n            return config.clone();\n        }\n        yield_now().await;\n    }\n}\n"
  },
  {
    "path": "examples/rp/src/bin/ethernet_w5500_tcp_client.rs",
    "content": "//! This example implements a TCP client that attempts to connect to a host on port 1234 and send it some data once per second.\n//!\n//! Example written for the [`WIZnet W5500-EVB-Pico`](https://docs.wiznet.io/Product/iEthernet/W5500/w5500-evb-pico) board.\n\n#![no_std]\n#![no_main]\n\nuse core::str::FromStr;\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_futures::yield_now;\nuse embassy_net::{Stack, StackResources};\nuse embassy_net_wiznet::chip::W5500;\nuse embassy_net_wiznet::*;\nuse embassy_rp::clocks::RoscRng;\nuse embassy_rp::gpio::{Input, Level, Output, Pull};\nuse embassy_rp::peripherals::{DMA_CH0, DMA_CH1, SPI0};\nuse embassy_rp::spi::{Async, Config as SpiConfig, Spi};\nuse embassy_rp::{bind_interrupts, dma};\nuse embassy_time::{Delay, Duration, Timer};\nuse embedded_hal_bus::spi::ExclusiveDevice;\nuse embedded_io_async::Write;\nuse static_cell::StaticCell;\nuse {defmt_rtt as _, panic_probe as _};\n\nbind_interrupts!(struct Irqs {\n    DMA_IRQ_0 => dma::InterruptHandler<DMA_CH0>, dma::InterruptHandler<DMA_CH1>;\n});\n\n#[embassy_executor::task]\nasync fn ethernet_task(\n    runner: Runner<\n        'static,\n        W5500,\n        ExclusiveDevice<Spi<'static, SPI0, Async>, Output<'static>, Delay>,\n        Input<'static>,\n        Output<'static>,\n    >,\n) -> ! {\n    runner.run().await\n}\n\n#[embassy_executor::task]\nasync fn net_task(mut runner: embassy_net::Runner<'static, Device<'static>>) -> ! {\n    runner.run().await\n}\n\n#[embassy_executor::main]\nasync fn main(spawner: Spawner) {\n    let p = embassy_rp::init(Default::default());\n    let mut rng = RoscRng;\n    let mut led = Output::new(p.PIN_25, Level::Low);\n\n    let mut spi_cfg = SpiConfig::default();\n    spi_cfg.frequency = 50_000_000;\n    let (miso, mosi, clk) = (p.PIN_16, p.PIN_19, p.PIN_18);\n    let spi = Spi::new(p.SPI0, clk, mosi, miso, p.DMA_CH0, p.DMA_CH1, Irqs, spi_cfg);\n    let cs = Output::new(p.PIN_17, Level::High);\n    let w5500_int = Input::new(p.PIN_21, Pull::Up);\n    let w5500_reset = Output::new(p.PIN_20, Level::High);\n\n    let mac_addr = [0x02, 0x00, 0x00, 0x00, 0x00, 0x00];\n    static STATE: StaticCell<State<8, 8>> = StaticCell::new();\n    let state = STATE.init(State::<8, 8>::new());\n    let (device, runner) = embassy_net_wiznet::new(\n        mac_addr,\n        state,\n        ExclusiveDevice::new(spi, cs, Delay),\n        w5500_int,\n        w5500_reset,\n    )\n    .await\n    .unwrap();\n    spawner.spawn(unwrap!(ethernet_task(runner)));\n\n    // Generate random seed\n    let seed = rng.next_u64();\n\n    // Init network stack\n    static RESOURCES: StaticCell<StackResources<3>> = StaticCell::new();\n    let (stack, runner) = embassy_net::new(\n        device,\n        embassy_net::Config::dhcpv4(Default::default()),\n        RESOURCES.init(StackResources::new()),\n        seed,\n    );\n\n    // Launch network task\n    spawner.spawn(unwrap!(net_task(runner)));\n\n    info!(\"Waiting for DHCP...\");\n    let cfg = wait_for_config(stack).await;\n    let local_addr = cfg.address.address();\n    info!(\"IP address: {:?}\", local_addr);\n\n    let mut rx_buffer = [0; 4096];\n    let mut tx_buffer = [0; 4096];\n    loop {\n        let mut socket = embassy_net::tcp::TcpSocket::new(stack, &mut rx_buffer, &mut tx_buffer);\n        socket.set_timeout(Some(Duration::from_secs(10)));\n\n        led.set_low();\n        info!(\"Connecting...\");\n        let host_addr = embassy_net::Ipv4Address::from_str(\"192.168.1.110\").unwrap();\n        if let Err(e) = socket.connect((host_addr, 1234)).await {\n            warn!(\"connect error: {:?}\", e);\n            continue;\n        }\n        info!(\"Connected to {:?}\", socket.remote_endpoint());\n        led.set_high();\n\n        let msg = b\"Hello world!\\n\";\n        loop {\n            if let Err(e) = socket.write_all(msg).await {\n                warn!(\"write error: {:?}\", e);\n                break;\n            }\n            info!(\"txd: {}\", core::str::from_utf8(msg).unwrap());\n            Timer::after_secs(1).await;\n        }\n    }\n}\n\nasync fn wait_for_config(stack: Stack<'static>) -> embassy_net::StaticConfigV4 {\n    loop {\n        if let Some(config) = stack.config_v4() {\n            return config.clone();\n        }\n        yield_now().await;\n    }\n}\n"
  },
  {
    "path": "examples/rp/src/bin/ethernet_w5500_tcp_server.rs",
    "content": "//! This example implements a TCP echo server on port 1234 and using DHCP.\n//! Send it some data, you should see it echoed back and printed in the console.\n//!\n//! Example written for the [`WIZnet W5500-EVB-Pico`](https://docs.wiznet.io/Product/iEthernet/W5500/w5500-evb-pico) board.\n\n#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_futures::yield_now;\nuse embassy_net::{Stack, StackResources};\nuse embassy_net_wiznet::chip::W5500;\nuse embassy_net_wiznet::*;\nuse embassy_rp::clocks::RoscRng;\nuse embassy_rp::gpio::{Input, Level, Output, Pull};\nuse embassy_rp::peripherals::{DMA_CH0, DMA_CH1, SPI0};\nuse embassy_rp::spi::{Async, Config as SpiConfig, Spi};\nuse embassy_rp::{bind_interrupts, dma};\nuse embassy_time::{Delay, Duration};\nuse embedded_hal_bus::spi::ExclusiveDevice;\nuse embedded_io_async::Write;\nuse static_cell::StaticCell;\nuse {defmt_rtt as _, panic_probe as _};\n\nbind_interrupts!(struct Irqs {\n    DMA_IRQ_0 => dma::InterruptHandler<DMA_CH0>, dma::InterruptHandler<DMA_CH1>;\n});\n\n#[embassy_executor::task]\nasync fn ethernet_task(\n    runner: Runner<\n        'static,\n        W5500,\n        ExclusiveDevice<Spi<'static, SPI0, Async>, Output<'static>, Delay>,\n        Input<'static>,\n        Output<'static>,\n    >,\n) -> ! {\n    runner.run().await\n}\n\n#[embassy_executor::task]\nasync fn net_task(mut runner: embassy_net::Runner<'static, Device<'static>>) -> ! {\n    runner.run().await\n}\n\n#[embassy_executor::main]\nasync fn main(spawner: Spawner) {\n    let p = embassy_rp::init(Default::default());\n    let mut rng = RoscRng;\n    let mut led = Output::new(p.PIN_25, Level::Low);\n\n    let mut spi_cfg = SpiConfig::default();\n    spi_cfg.frequency = 50_000_000;\n    let (miso, mosi, clk) = (p.PIN_16, p.PIN_19, p.PIN_18);\n    let spi = Spi::new(p.SPI0, clk, mosi, miso, p.DMA_CH0, p.DMA_CH1, Irqs, spi_cfg);\n    let cs = Output::new(p.PIN_17, Level::High);\n    let w5500_int = Input::new(p.PIN_21, Pull::Up);\n    let w5500_reset = Output::new(p.PIN_20, Level::High);\n\n    let mac_addr = [0x02, 0x00, 0x00, 0x00, 0x00, 0x00];\n    static STATE: StaticCell<State<8, 8>> = StaticCell::new();\n    let state = STATE.init(State::<8, 8>::new());\n    let (device, runner) = embassy_net_wiznet::new(\n        mac_addr,\n        state,\n        ExclusiveDevice::new(spi, cs, Delay),\n        w5500_int,\n        w5500_reset,\n    )\n    .await\n    .unwrap();\n    spawner.spawn(unwrap!(ethernet_task(runner)));\n\n    // Generate random seed\n    let seed = rng.next_u64();\n\n    // Init network stack\n    static RESOURCES: StaticCell<StackResources<3>> = StaticCell::new();\n    let (stack, runner) = embassy_net::new(\n        device,\n        embassy_net::Config::dhcpv4(Default::default()),\n        RESOURCES.init(StackResources::new()),\n        seed,\n    );\n\n    // Launch network task\n    spawner.spawn(unwrap!(net_task(runner)));\n\n    info!(\"Waiting for DHCP...\");\n    let cfg = wait_for_config(stack).await;\n    let local_addr = cfg.address.address();\n    info!(\"IP address: {:?}\", local_addr);\n\n    let mut rx_buffer = [0; 4096];\n    let mut tx_buffer = [0; 4096];\n    let mut buf = [0; 4096];\n    loop {\n        let mut socket = embassy_net::tcp::TcpSocket::new(stack, &mut rx_buffer, &mut tx_buffer);\n        socket.set_timeout(Some(Duration::from_secs(10)));\n\n        led.set_low();\n        info!(\"Listening on TCP:1234...\");\n        if let Err(e) = socket.accept(1234).await {\n            warn!(\"accept error: {:?}\", e);\n            continue;\n        }\n        info!(\"Received connection from {:?}\", socket.remote_endpoint());\n        led.set_high();\n\n        loop {\n            let n = match socket.read(&mut buf).await {\n                Ok(0) => {\n                    warn!(\"read EOF\");\n                    break;\n                }\n                Ok(n) => n,\n                Err(e) => {\n                    warn!(\"{:?}\", e);\n                    break;\n                }\n            };\n            info!(\"rxd {}\", core::str::from_utf8(&buf[..n]).unwrap());\n\n            if let Err(e) = socket.write_all(&buf[..n]).await {\n                warn!(\"write error: {:?}\", e);\n                break;\n            }\n        }\n    }\n}\n\nasync fn wait_for_config(stack: Stack<'static>) -> embassy_net::StaticConfigV4 {\n    loop {\n        if let Some(config) = stack.config_v4() {\n            return config.clone();\n        }\n        yield_now().await;\n    }\n}\n"
  },
  {
    "path": "examples/rp/src/bin/ethernet_w5500_udp.rs",
    "content": "//! This example implements a UDP server listening on port 1234 and echoing back the data.\n//!\n//! Example written for the [`WIZnet W5500-EVB-Pico`](https://docs.wiznet.io/Product/iEthernet/W5500/w5500-evb-pico) board.\n\n#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_futures::yield_now;\nuse embassy_net::udp::{PacketMetadata, UdpSocket};\nuse embassy_net::{Stack, StackResources};\nuse embassy_net_wiznet::chip::W5500;\nuse embassy_net_wiznet::*;\nuse embassy_rp::clocks::RoscRng;\nuse embassy_rp::gpio::{Input, Level, Output, Pull};\nuse embassy_rp::peripherals::{DMA_CH0, DMA_CH1, SPI0};\nuse embassy_rp::spi::{Async, Config as SpiConfig, Spi};\nuse embassy_rp::{bind_interrupts, dma};\nuse embassy_time::Delay;\nuse embedded_hal_bus::spi::ExclusiveDevice;\nuse static_cell::StaticCell;\nuse {defmt_rtt as _, panic_probe as _};\n\nbind_interrupts!(struct Irqs {\n    DMA_IRQ_0 => dma::InterruptHandler<DMA_CH0>, dma::InterruptHandler<DMA_CH1>;\n});\n\n#[embassy_executor::task]\nasync fn ethernet_task(\n    runner: Runner<\n        'static,\n        W5500,\n        ExclusiveDevice<Spi<'static, SPI0, Async>, Output<'static>, Delay>,\n        Input<'static>,\n        Output<'static>,\n    >,\n) -> ! {\n    runner.run().await\n}\n\n#[embassy_executor::task]\nasync fn net_task(mut runner: embassy_net::Runner<'static, Device<'static>>) -> ! {\n    runner.run().await\n}\n\n#[embassy_executor::main]\nasync fn main(spawner: Spawner) {\n    let p = embassy_rp::init(Default::default());\n    let mut rng = RoscRng;\n\n    let mut spi_cfg = SpiConfig::default();\n    spi_cfg.frequency = 50_000_000;\n    let (miso, mosi, clk) = (p.PIN_16, p.PIN_19, p.PIN_18);\n    let spi = Spi::new(p.SPI0, clk, mosi, miso, p.DMA_CH0, p.DMA_CH1, Irqs, spi_cfg);\n    let cs = Output::new(p.PIN_17, Level::High);\n    let w5500_int = Input::new(p.PIN_21, Pull::Up);\n    let w5500_reset = Output::new(p.PIN_20, Level::High);\n\n    let mac_addr = [0x02, 0x00, 0x00, 0x00, 0x00, 0x00];\n    static STATE: StaticCell<State<8, 8>> = StaticCell::new();\n    let state = STATE.init(State::<8, 8>::new());\n    let (device, runner) = embassy_net_wiznet::new(\n        mac_addr,\n        state,\n        ExclusiveDevice::new(spi, cs, Delay),\n        w5500_int,\n        w5500_reset,\n    )\n    .await\n    .unwrap();\n    spawner.spawn(unwrap!(ethernet_task(runner)));\n\n    // Generate random seed\n    let seed = rng.next_u64();\n\n    // Init network stack\n    static RESOURCES: StaticCell<StackResources<3>> = StaticCell::new();\n    let (stack, runner) = embassy_net::new(\n        device,\n        embassy_net::Config::dhcpv4(Default::default()),\n        RESOURCES.init(StackResources::new()),\n        seed,\n    );\n\n    // Launch network task\n    spawner.spawn(unwrap!(net_task(runner)));\n\n    info!(\"Waiting for DHCP...\");\n    let cfg = wait_for_config(stack).await;\n    let local_addr = cfg.address.address();\n    info!(\"IP address: {:?}\", local_addr);\n\n    // Then we can use it!\n    let mut rx_buffer = [0; 4096];\n    let mut tx_buffer = [0; 4096];\n    let mut rx_meta = [PacketMetadata::EMPTY; 16];\n    let mut tx_meta = [PacketMetadata::EMPTY; 16];\n    let mut buf = [0; 4096];\n    loop {\n        let mut socket = UdpSocket::new(stack, &mut rx_meta, &mut rx_buffer, &mut tx_meta, &mut tx_buffer);\n        socket.bind(1234).unwrap();\n\n        loop {\n            let (n, ep) = socket.recv_from(&mut buf).await.unwrap();\n            if let Ok(s) = core::str::from_utf8(&buf[..n]) {\n                info!(\"rxd from {}: {}\", ep, s);\n            }\n            socket.send_to(&buf[..n], ep).await.unwrap();\n        }\n    }\n}\n\nasync fn wait_for_config(stack: Stack<'static>) -> embassy_net::StaticConfigV4 {\n    loop {\n        if let Some(config) = stack.config_v4() {\n            return config.clone();\n        }\n        yield_now().await;\n    }\n}\n"
  },
  {
    "path": "examples/rp/src/bin/ethernet_w55rp20_tcp_server.rs",
    "content": "//! This example implements a TCP echo server on port 1234 and using DHCP.\n//! Send it some data, you should see it echoed back and printed in the console.\n//!\n//! Example written for the [`WIZnet W55RP20-EVB-Pico`](https://docs.wiznet.io/Product/ioNIC/W55RP20/w55rp20-evb-pico) board.\n//! Note: the W55RP20 is a single package that contains both a RP2040 and the Wiznet W5500 ethernet\n//! controller\n\n#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_futures::yield_now;\nuse embassy_net::{Stack, StackResources};\nuse embassy_net_wiznet::chip::W5500;\nuse embassy_net_wiznet::*;\nuse embassy_rp::clocks::RoscRng;\nuse embassy_rp::gpio::{Input, Level, Output, Pull};\nuse embassy_rp::peripherals::{DMA_CH0, DMA_CH1, PIO0};\nuse embassy_rp::pio_programs::spi::Spi;\nuse embassy_rp::spi::{Async, Config as SpiConfig};\nuse embassy_rp::{bind_interrupts, pio};\nuse embassy_time::{Delay, Duration};\nuse embedded_hal_bus::spi::ExclusiveDevice;\nuse embedded_io_async::Write;\nuse static_cell::StaticCell;\nuse {defmt_rtt as _, panic_probe as _};\n\nbind_interrupts!(struct Irqs {\n    PIO0_IRQ_0 => pio::InterruptHandler<PIO0>;\n    DMA_IRQ_0 => embassy_rp::dma::InterruptHandler<DMA_CH0>, embassy_rp::dma::InterruptHandler<DMA_CH1>;\n});\n\n#[embassy_executor::task]\nasync fn ethernet_task(\n    runner: Runner<\n        'static,\n        W5500,\n        ExclusiveDevice<Spi<'static, PIO0, 0, Async>, Output<'static>, Delay>,\n        Input<'static>,\n        Output<'static>,\n    >,\n) -> ! {\n    runner.run().await\n}\n\n#[embassy_executor::task]\nasync fn net_task(mut runner: embassy_net::Runner<'static, Device<'static>>) -> ! {\n    runner.run().await\n}\n\n#[embassy_executor::main]\nasync fn main(spawner: Spawner) {\n    let p = embassy_rp::init(Default::default());\n    let mut rng = RoscRng;\n    let mut led = Output::new(p.PIN_19, Level::Low);\n\n    // The W55RP20 uses a PIO unit for SPI communication, once the SPI bus has been formed using a\n    // PIO statemachine everything else is generally unchanged from the other examples that use the W5500\n    let mosi = p.PIN_23;\n    let miso = p.PIN_22;\n    let clk = p.PIN_21;\n\n    let pio::Pio { mut common, sm0, .. } = pio::Pio::new(p.PIO0, Irqs);\n\n    // Construct an SPI driver backed by a PIO state machine\n    let mut spi_cfg = SpiConfig::default();\n    spi_cfg.frequency = 12_500_000; // The PIO SPI program is much less stable than the actual SPI\n    // peripheral, use higher speeds at your peril\n    let spi = Spi::new(&mut common, sm0, clk, mosi, miso, p.DMA_CH0, p.DMA_CH1, Irqs, spi_cfg);\n\n    // Further control pins\n    let cs = Output::new(p.PIN_20, Level::High);\n    let w5500_int = Input::new(p.PIN_24, Pull::Up);\n    let w5500_reset = Output::new(p.PIN_25, Level::High);\n\n    let mac_addr = [0x02, 0x00, 0x00, 0x00, 0x00, 0x00];\n    static STATE: StaticCell<State<8, 8>> = StaticCell::new();\n    let state = STATE.init(State::<8, 8>::new());\n    let (device, runner) = embassy_net_wiznet::new(\n        mac_addr,\n        state,\n        ExclusiveDevice::new(spi, cs, Delay),\n        w5500_int,\n        w5500_reset,\n    )\n    .await\n    .unwrap();\n    spawner.spawn(unwrap!(ethernet_task(runner)));\n\n    // Generate random seed\n    let seed = rng.next_u64();\n\n    // Init network stack\n    static RESOURCES: StaticCell<StackResources<3>> = StaticCell::new();\n    let (stack, runner) = embassy_net::new(\n        device,\n        embassy_net::Config::dhcpv4(Default::default()),\n        RESOURCES.init(StackResources::new()),\n        seed,\n    );\n\n    // Launch network task\n    spawner.spawn(unwrap!(net_task(runner)));\n\n    info!(\"Waiting for DHCP...\");\n    let cfg = wait_for_config(stack).await;\n    let local_addr = cfg.address.address();\n    info!(\"IP address: {:?}\", local_addr);\n\n    let mut rx_buffer = [0; 4096];\n    let mut tx_buffer = [0; 4096];\n    let mut buf = [0; 4096];\n    loop {\n        let mut socket = embassy_net::tcp::TcpSocket::new(stack, &mut rx_buffer, &mut tx_buffer);\n        socket.set_timeout(Some(Duration::from_secs(10)));\n\n        led.set_low();\n        info!(\"Listening on TCP:1234...\");\n        if let Err(e) = socket.accept(1234).await {\n            warn!(\"accept error: {:?}\", e);\n            continue;\n        }\n        info!(\"Received connection from {:?}\", socket.remote_endpoint());\n        led.set_high();\n\n        loop {\n            let n = match socket.read(&mut buf).await {\n                Ok(0) => {\n                    warn!(\"read EOF\");\n                    break;\n                }\n                Ok(n) => n,\n                Err(e) => {\n                    warn!(\"{:?}\", e);\n                    break;\n                }\n            };\n            info!(\"rxd {}\", core::str::from_utf8(&buf[..n]).unwrap());\n\n            if let Err(e) = socket.write_all(&buf[..n]).await {\n                warn!(\"write error: {:?}\", e);\n                break;\n            }\n        }\n    }\n}\n\nasync fn wait_for_config(stack: Stack<'static>) -> embassy_net::StaticConfigV4 {\n    loop {\n        if let Some(config) = stack.config_v4() {\n            return config.clone();\n        }\n        yield_now().await;\n    }\n}\n"
  },
  {
    "path": "examples/rp/src/bin/flash.rs",
    "content": "//! This example test the flash connected to the RP2040 chip.\n\n#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_rp::bind_interrupts;\nuse embassy_rp::flash::{Async, ERASE_SIZE, FLASH_BASE};\nuse embassy_rp::peripherals::{DMA_CH0, FLASH};\nuse embassy_time::Timer;\nuse {defmt_rtt as _, panic_probe as _};\n\nbind_interrupts!(struct Irqs {\n    DMA_IRQ_0 => embassy_rp::dma::InterruptHandler<DMA_CH0>;\n});\n\nconst ADDR_OFFSET: u32 = 0x100000;\nconst FLASH_SIZE: usize = 2 * 1024 * 1024;\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let p = embassy_rp::init(Default::default());\n    info!(\"Hello World!\");\n\n    // add some delay to give an attached debug probe time to parse the\n    // defmt RTT header. Reading that header might touch flash memory, which\n    // interferes with flash write operations.\n    // https://github.com/knurling-rs/defmt/pull/683\n    Timer::after_millis(10).await;\n\n    let mut flash = embassy_rp::flash::Flash::<_, Async, FLASH_SIZE>::new(p.FLASH, p.DMA_CH0, Irqs);\n\n    // Get JEDEC id\n    let jedec = flash.blocking_jedec_id().unwrap();\n    info!(\"jedec id: 0x{:x}\", jedec);\n\n    // Get unique id\n    let mut uid = [0; 8];\n    flash.blocking_unique_id(&mut uid).unwrap();\n    info!(\"unique id: {:?}\", uid);\n\n    erase_write_sector(&mut flash, 0x00);\n\n    multiwrite_bytes(&mut flash, ERASE_SIZE as u32);\n\n    background_read(&mut flash, (ERASE_SIZE * 2) as u32).await;\n\n    loop {}\n}\n\nfn multiwrite_bytes(flash: &mut embassy_rp::flash::Flash<'_, FLASH, Async, FLASH_SIZE>, offset: u32) {\n    info!(\">>>> [multiwrite_bytes]\");\n    let mut read_buf = [0u8; ERASE_SIZE];\n    defmt::unwrap!(flash.blocking_read(ADDR_OFFSET + offset, &mut read_buf));\n\n    info!(\"Addr of flash block is {:x}\", ADDR_OFFSET + offset + FLASH_BASE as u32);\n    info!(\"Contents start with {=[u8]}\", read_buf[0..4]);\n\n    defmt::unwrap!(flash.blocking_erase(ADDR_OFFSET + offset, ADDR_OFFSET + offset + ERASE_SIZE as u32));\n\n    defmt::unwrap!(flash.blocking_read(ADDR_OFFSET + offset, &mut read_buf));\n    info!(\"Contents after erase starts with {=[u8]}\", read_buf[0..4]);\n    if read_buf.iter().any(|x| *x != 0xFF) {\n        defmt::panic!(\"unexpected\");\n    }\n\n    defmt::unwrap!(flash.blocking_write(ADDR_OFFSET + offset, &[0x01]));\n    defmt::unwrap!(flash.blocking_write(ADDR_OFFSET + offset + 1, &[0x02]));\n    defmt::unwrap!(flash.blocking_write(ADDR_OFFSET + offset + 2, &[0x03]));\n    defmt::unwrap!(flash.blocking_write(ADDR_OFFSET + offset + 3, &[0x04]));\n\n    defmt::unwrap!(flash.blocking_read(ADDR_OFFSET + offset, &mut read_buf));\n    info!(\"Contents after write starts with {=[u8]}\", read_buf[0..4]);\n    if &read_buf[0..4] != &[0x01, 0x02, 0x03, 0x04] {\n        defmt::panic!(\"unexpected\");\n    }\n}\n\nfn erase_write_sector(flash: &mut embassy_rp::flash::Flash<'_, FLASH, Async, FLASH_SIZE>, offset: u32) {\n    info!(\">>>> [erase_write_sector]\");\n    let mut buf = [0u8; ERASE_SIZE];\n    defmt::unwrap!(flash.blocking_read(ADDR_OFFSET + offset, &mut buf));\n\n    info!(\"Addr of flash block is {:x}\", ADDR_OFFSET + offset + FLASH_BASE as u32);\n    info!(\"Contents start with {=[u8]}\", buf[0..4]);\n\n    defmt::unwrap!(flash.blocking_erase(ADDR_OFFSET + offset, ADDR_OFFSET + offset + ERASE_SIZE as u32));\n\n    defmt::unwrap!(flash.blocking_read(ADDR_OFFSET + offset, &mut buf));\n    info!(\"Contents after erase starts with {=[u8]}\", buf[0..4]);\n    if buf.iter().any(|x| *x != 0xFF) {\n        defmt::panic!(\"unexpected\");\n    }\n\n    for b in buf.iter_mut() {\n        *b = 0xDA;\n    }\n\n    defmt::unwrap!(flash.blocking_write(ADDR_OFFSET + offset, &buf));\n\n    defmt::unwrap!(flash.blocking_read(ADDR_OFFSET + offset, &mut buf));\n    info!(\"Contents after write starts with {=[u8]}\", buf[0..4]);\n    if buf.iter().any(|x| *x != 0xDA) {\n        defmt::panic!(\"unexpected\");\n    }\n}\n\nasync fn background_read(flash: &mut embassy_rp::flash::Flash<'_, FLASH, Async, FLASH_SIZE>, offset: u32) {\n    info!(\">>>> [background_read]\");\n\n    let mut buf = [0u32; 8];\n    defmt::unwrap!(flash.background_read(ADDR_OFFSET + offset, &mut buf)).await;\n\n    info!(\"Addr of flash block is {:x}\", ADDR_OFFSET + offset + FLASH_BASE as u32);\n    info!(\"Contents start with {=u32:x}\", buf[0]);\n\n    defmt::unwrap!(flash.blocking_erase(ADDR_OFFSET + offset, ADDR_OFFSET + offset + ERASE_SIZE as u32));\n\n    defmt::unwrap!(flash.background_read(ADDR_OFFSET + offset, &mut buf)).await;\n    info!(\"Contents after erase starts with {=u32:x}\", buf[0]);\n    if buf.iter().any(|x| *x != 0xFFFFFFFF) {\n        defmt::panic!(\"unexpected\");\n    }\n\n    for b in buf.iter_mut() {\n        *b = 0xDABA1234;\n    }\n\n    defmt::unwrap!(flash.blocking_write(ADDR_OFFSET + offset, unsafe {\n        core::slice::from_raw_parts(buf.as_ptr() as *const u8, buf.len() * 4)\n    }));\n\n    defmt::unwrap!(flash.background_read(ADDR_OFFSET + offset, &mut buf)).await;\n    info!(\"Contents after write starts with {=u32:x}\", buf[0]);\n    if buf.iter().any(|x| *x != 0xDABA1234) {\n        defmt::panic!(\"unexpected\");\n    }\n}\n"
  },
  {
    "path": "examples/rp/src/bin/gpio_async.rs",
    "content": "//! This example shows how async gpio can be used with a RP2040.\n//!\n//! The LED on the RP Pico W board is connected differently. See wifi_blinky.rs.\n\n#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_rp::gpio;\nuse embassy_time::Timer;\nuse gpio::{Input, Level, Output, Pull};\nuse {defmt_rtt as _, panic_probe as _};\n\n/// It requires an external signal to be manually triggered on PIN 16. For\n/// example, this could be accomplished using an external power source with a\n/// button so that it is possible to toggle the signal from low to high.\n///\n/// This example will begin with turning on the LED on the board and wait for a\n/// high signal on PIN 16. Once the high event/signal occurs the program will\n/// continue and turn off the LED, and then wait for 2 seconds before completing\n/// the loop and starting over again.\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let p = embassy_rp::init(Default::default());\n    let mut led = Output::new(p.PIN_25, Level::Low);\n    let mut async_input = Input::new(p.PIN_16, Pull::None);\n\n    loop {\n        info!(\"wait_for_high. Turn on LED\");\n        led.set_high();\n\n        async_input.wait_for_high().await;\n\n        info!(\"done wait_for_high. Turn off LED\");\n        led.set_low();\n\n        Timer::after_secs(2).await;\n    }\n}\n"
  },
  {
    "path": "examples/rp/src/bin/gpout.rs",
    "content": "//! This example shows how GPOUT (General purpose clock outputs) can toggle a output pin.\n//!\n//! The LED on the RP Pico W board is connected differently. Add a LED and resistor to another pin.\n\n#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_rp::clocks;\nuse embassy_time::Timer;\nuse {defmt_rtt as _, panic_probe as _};\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let p = embassy_rp::init(Default::default());\n\n    let gpout3 = clocks::Gpout::new(p.PIN_25);\n    gpout3.set_div(1000, 0);\n    gpout3.enable();\n\n    loop {\n        gpout3.set_src(clocks::GpoutSrc::Sys);\n        info!(\n            \"Pin 25 is now outputing CLK_SYS/1000, should be toggling at {}\",\n            gpout3.get_freq()\n        );\n        Timer::after_secs(2).await;\n\n        gpout3.set_src(clocks::GpoutSrc::Ref);\n        info!(\n            \"Pin 25 is now outputing CLK_REF/1000, should be toggling at {}\",\n            gpout3.get_freq()\n        );\n        Timer::after_secs(2).await;\n    }\n}\n"
  },
  {
    "path": "examples/rp/src/bin/i2c_async.rs",
    "content": "//! This example shows how to communicate asynchronous using i2c with external chips.\n//!\n//! Example written for the [`MCP23017 16-Bit I2C I/O Expander with Serial Interface`] chip.\n//! (https://www.microchip.com/en-us/product/mcp23017)\n\n#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_rp::bind_interrupts;\nuse embassy_rp::i2c::{self, Config, InterruptHandler};\nuse embassy_rp::peripherals::I2C1;\nuse embassy_time::Timer;\nuse embedded_hal_async::i2c::I2c;\nuse {defmt_rtt as _, panic_probe as _};\n\nbind_interrupts!(struct Irqs {\n    I2C1_IRQ => InterruptHandler<I2C1>;\n});\n\n#[allow(dead_code)]\nmod mcp23017 {\n    pub const ADDR: u8 = 0x20; // default addr\n\n    macro_rules! mcpregs {\n        ($($name:ident : $val:expr),* $(,)?) => {\n            $(\n                pub const $name: u8 = $val;\n            )*\n\n            pub fn regname(reg: u8) -> &'static str {\n                match reg {\n                    $(\n                        $val => stringify!($name),\n                    )*\n                    _ => panic!(\"bad reg\"),\n                }\n            }\n        }\n    }\n\n    // These are correct for IOCON.BANK=0\n    mcpregs! {\n        IODIRA: 0x00,\n        IPOLA: 0x02,\n        GPINTENA: 0x04,\n        DEFVALA: 0x06,\n        INTCONA: 0x08,\n        IOCONA: 0x0A,\n        GPPUA: 0x0C,\n        INTFA: 0x0E,\n        INTCAPA: 0x10,\n        GPIOA: 0x12,\n        OLATA: 0x14,\n        IODIRB: 0x01,\n        IPOLB: 0x03,\n        GPINTENB: 0x05,\n        DEFVALB: 0x07,\n        INTCONB: 0x09,\n        IOCONB: 0x0B,\n        GPPUB: 0x0D,\n        INTFB: 0x0F,\n        INTCAPB: 0x11,\n        GPIOB: 0x13,\n        OLATB: 0x15,\n    }\n}\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let p = embassy_rp::init(Default::default());\n\n    let sda = p.PIN_14;\n    let scl = p.PIN_15;\n\n    info!(\"set up i2c \");\n    let mut i2c = i2c::I2c::new_async(p.I2C1, scl, sda, Irqs, Config::default());\n\n    use mcp23017::*;\n\n    info!(\"init mcp23017 config for IxpandO\");\n    // init - a outputs, b inputs\n    i2c.write(ADDR, &[IODIRA, 0x00]).await.unwrap();\n    i2c.write(ADDR, &[IODIRB, 0xff]).await.unwrap();\n    i2c.write(ADDR, &[GPPUB, 0xff]).await.unwrap(); // pullups\n\n    let mut val = 1;\n    loop {\n        let mut portb = [0];\n\n        i2c.write_read(mcp23017::ADDR, &[GPIOB], &mut portb).await.unwrap();\n        info!(\"portb = {:02x}\", portb[0]);\n        i2c.write(mcp23017::ADDR, &[GPIOA, val | portb[0]]).await.unwrap();\n        val = val.rotate_left(1);\n\n        // get a register dump\n        info!(\"getting register dump\");\n        let mut regs = [0; 22];\n        i2c.write_read(ADDR, &[0], &mut regs).await.unwrap();\n        // always get the regdump but only display it if portb'0 is set\n        if portb[0] & 1 != 0 {\n            for (idx, reg) in regs.into_iter().enumerate() {\n                info!(\"{} => {:02x}\", regname(idx as u8), reg);\n            }\n        }\n\n        Timer::after_millis(100).await;\n    }\n}\n"
  },
  {
    "path": "examples/rp/src/bin/i2c_async_embassy.rs",
    "content": "//! This example shows how to communicate asynchronous using i2c with external chip.\n//!\n//! It's using embassy's functions directly instead of traits from embedded_hal_async::i2c::I2c.\n//! While most of i2c devices are addressed using 7 bits, an extension allows 10 bits too.\n\n#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_rp::i2c::InterruptHandler;\nuse {defmt_rtt as _, panic_probe as _};\n\n// Our anonymous hypotetical temperature sensor could be:\n// a 12-bit sensor, with 100ms startup time, range of -40*C - 125*C, and precision 0.25*C\n// It requires no configuration or calibration, works with all i2c bus speeds,\n// never stretches clock or does anything complicated. Replies with one u16.\n// It requires only one write to take it out of suspend mode, and stays on.\n// Often result would be just on 12 bits, but here we'll simplify it to 16.\n\nenum UncomplicatedSensorId {\n    A(UncomplicatedSensorU8),\n    B(UncomplicatedSensorU16),\n}\nenum UncomplicatedSensorU8 {\n    First = 0x48,\n}\nenum UncomplicatedSensorU16 {\n    Other = 0x0049,\n}\n\nimpl Into<u16> for UncomplicatedSensorU16 {\n    fn into(self) -> u16 {\n        self as u16\n    }\n}\nimpl Into<u16> for UncomplicatedSensorU8 {\n    fn into(self) -> u16 {\n        0x48\n    }\n}\nimpl From<UncomplicatedSensorId> for u16 {\n    fn from(t: UncomplicatedSensorId) -> Self {\n        match t {\n            UncomplicatedSensorId::A(x) => x.into(),\n            UncomplicatedSensorId::B(x) => x.into(),\n        }\n    }\n}\n\nembassy_rp::bind_interrupts!(struct Irqs {\n    I2C1_IRQ => InterruptHandler<embassy_rp::peripherals::I2C1>;\n});\n\n#[embassy_executor::main]\nasync fn main(_task_spawner: embassy_executor::Spawner) {\n    let p = embassy_rp::init(Default::default());\n    let sda = p.PIN_14;\n    let scl = p.PIN_15;\n    let config = embassy_rp::i2c::Config::default();\n    let mut bus = embassy_rp::i2c::I2c::new_async(p.I2C1, scl, sda, Irqs, config);\n\n    const WAKEYWAKEY: u16 = 0xBABE;\n    let mut result: [u8; 2] = [0, 0];\n    // wait for sensors to initialize\n    embassy_time::Timer::after(embassy_time::Duration::from_millis(100)).await;\n\n    let _res_1 = bus\n        .write_async(UncomplicatedSensorU8::First, WAKEYWAKEY.to_be_bytes())\n        .await;\n    let _res_2 = bus\n        .write_async(UncomplicatedSensorU16::Other, WAKEYWAKEY.to_be_bytes())\n        .await;\n\n    loop {\n        let s1 = UncomplicatedSensorId::A(UncomplicatedSensorU8::First);\n        let s2 = UncomplicatedSensorId::B(UncomplicatedSensorU16::Other);\n        let sensors = [s1, s2];\n        for sensor in sensors {\n            if bus.read_async(sensor, &mut result).await.is_ok() {\n                info!(\"Result {}\", u16::from_be_bytes(result.into()));\n            }\n        }\n        embassy_time::Timer::after(embassy_time::Duration::from_millis(200)).await;\n    }\n}\n"
  },
  {
    "path": "examples/rp/src/bin/i2c_blocking.rs",
    "content": "//! This example shows how to communicate using i2c with external chips.\n//!\n//! Example written for the [`MCP23017 16-Bit I2C I/O Expander with Serial Interface`] chip.\n//! (https://www.microchip.com/en-us/product/mcp23017)\n\n#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_rp::i2c::{self, Config};\nuse embassy_time::Timer;\nuse embedded_hal_1::i2c::I2c;\nuse {defmt_rtt as _, panic_probe as _};\n\n#[allow(dead_code)]\nmod mcp23017 {\n    pub const ADDR: u8 = 0x20; // default addr\n\n    pub const IODIRA: u8 = 0x00;\n    pub const IPOLA: u8 = 0x02;\n    pub const GPINTENA: u8 = 0x04;\n    pub const DEFVALA: u8 = 0x06;\n    pub const INTCONA: u8 = 0x08;\n    pub const IOCONA: u8 = 0x0A;\n    pub const GPPUA: u8 = 0x0C;\n    pub const INTFA: u8 = 0x0E;\n    pub const INTCAPA: u8 = 0x10;\n    pub const GPIOA: u8 = 0x12;\n    pub const OLATA: u8 = 0x14;\n    pub const IODIRB: u8 = 0x01;\n    pub const IPOLB: u8 = 0x03;\n    pub const GPINTENB: u8 = 0x05;\n    pub const DEFVALB: u8 = 0x07;\n    pub const INTCONB: u8 = 0x09;\n    pub const IOCONB: u8 = 0x0B;\n    pub const GPPUB: u8 = 0x0D;\n    pub const INTFB: u8 = 0x0F;\n    pub const INTCAPB: u8 = 0x11;\n    pub const GPIOB: u8 = 0x13;\n    pub const OLATB: u8 = 0x15;\n}\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let p = embassy_rp::init(Default::default());\n\n    let sda = p.PIN_14;\n    let scl = p.PIN_15;\n\n    info!(\"set up i2c \");\n    // Default I2C config enables internal pull-up resistors.\n    let config = Config::default();\n    let mut i2c = i2c::I2c::new_blocking(p.I2C1, scl, sda, config);\n\n    use mcp23017::*;\n\n    info!(\"init mcp23017 config for IxpandO\");\n    // init - a outputs, b inputs\n    i2c.write(ADDR, &[IODIRA, 0x00]).unwrap();\n    i2c.write(ADDR, &[IODIRB, 0xff]).unwrap();\n    i2c.write(ADDR, &[GPPUB, 0xff]).unwrap(); // pullups\n\n    let mut val = 0xaa;\n    loop {\n        let mut portb = [0];\n\n        i2c.write(mcp23017::ADDR, &[GPIOA, val]).unwrap();\n        i2c.write_read(mcp23017::ADDR, &[GPIOB], &mut portb).unwrap();\n\n        info!(\"portb = {:02x}\", portb[0]);\n        val = !val;\n\n        Timer::after_secs(1).await;\n    }\n}\n"
  },
  {
    "path": "examples/rp/src/bin/i2c_slave.rs",
    "content": "//! This example shows how to use the 2040 as an i2c slave.\n#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_rp::peripherals::{I2C0, I2C1};\nuse embassy_rp::{bind_interrupts, i2c, i2c_slave};\nuse embassy_time::Timer;\nuse embedded_hal_async::i2c::I2c;\nuse {defmt_rtt as _, panic_probe as _};\n\nbind_interrupts!(struct Irqs {\n    I2C0_IRQ => i2c::InterruptHandler<I2C0>;\n    I2C1_IRQ => i2c::InterruptHandler<I2C1>;\n});\n\nconst DEV_ADDR: u8 = 0x42;\n\n#[embassy_executor::task]\nasync fn device_task(mut dev: i2c_slave::I2cSlave<'static, I2C1>) -> ! {\n    info!(\"Device start\");\n\n    let mut state = 0;\n\n    loop {\n        let mut buf = [0u8; 128];\n        match dev.listen(&mut buf).await {\n            Ok(i2c_slave::Command::GeneralCall(len)) => info!(\"Device received general call write: {}\", buf[..len]),\n            Ok(i2c_slave::Command::Read) => loop {\n                match dev.respond_to_read(&[state]).await {\n                    Ok(x) => match x {\n                        i2c_slave::ReadStatus::Done => break,\n                        i2c_slave::ReadStatus::NeedMoreBytes => (),\n                        i2c_slave::ReadStatus::LeftoverBytes(x) => {\n                            info!(\"tried to write {} extra bytes\", x);\n                            break;\n                        }\n                    },\n                    Err(e) => error!(\"error while responding {}\", e),\n                }\n            },\n            Ok(i2c_slave::Command::Write(len)) => info!(\"Device received write: {}\", buf[..len]),\n            Ok(i2c_slave::Command::WriteRead(len)) => {\n                info!(\"device received write read: {:x}\", buf[..len]);\n                match buf[0] {\n                    // Set the state\n                    0xC2 => {\n                        state = buf[1];\n                        match dev.respond_and_fill(&[state], 0x00).await {\n                            Ok(read_status) => info!(\"response read status {}\", read_status),\n                            Err(e) => error!(\"error while responding {}\", e),\n                        }\n                    }\n                    // Reset State\n                    0xC8 => {\n                        state = 0;\n                        match dev.respond_and_fill(&[state], 0x00).await {\n                            Ok(read_status) => info!(\"response read status {}\", read_status),\n                            Err(e) => error!(\"error while responding {}\", e),\n                        }\n                    }\n                    x => error!(\"Invalid Write Read {:x}\", x),\n                }\n            }\n            Err(e) => error!(\"{}\", e),\n        }\n    }\n}\n\n#[embassy_executor::task]\nasync fn controller_task(mut con: i2c::I2c<'static, I2C0, i2c::Async>) {\n    info!(\"Controller start\");\n\n    loop {\n        let mut resp_buff = [0u8; 2];\n        for i in 0..10 {\n            match con.write_read(DEV_ADDR, &[0xC2, i], &mut resp_buff).await {\n                Ok(_) => info!(\"write_read response: {}\", resp_buff),\n                Err(e) => error!(\"Error writing {}\", e),\n            }\n\n            Timer::after_millis(100).await;\n        }\n        match con.read(DEV_ADDR, &mut resp_buff).await {\n            Ok(_) => info!(\"read response: {}\", resp_buff),\n            Err(e) => error!(\"Error writing {}\", e),\n        }\n        match con.write_read(DEV_ADDR, &[0xC8], &mut resp_buff).await {\n            Ok(_) => info!(\"write_read response: {}\", resp_buff),\n            Err(e) => error!(\"Error writing {}\", e),\n        }\n        Timer::after_millis(100).await;\n    }\n}\n\n#[embassy_executor::main]\nasync fn main(spawner: Spawner) {\n    let p = embassy_rp::init(Default::default());\n    info!(\"Hello World!\");\n\n    let d_sda = p.PIN_2;\n    let d_scl = p.PIN_3;\n    let mut config = i2c_slave::Config::default();\n    config.addr = DEV_ADDR as u16;\n    let device = i2c_slave::I2cSlave::new(p.I2C1, d_scl, d_sda, Irqs, config);\n\n    spawner.spawn(unwrap!(device_task(device)));\n\n    let c_sda = p.PIN_0;\n    let c_scl = p.PIN_1;\n    let mut config = i2c::Config::default();\n    config.frequency = 1_000_000;\n    let controller = i2c::I2c::new_async(p.I2C0, c_scl, c_sda, Irqs, config);\n\n    spawner.spawn(unwrap!(controller_task(controller)));\n}\n"
  },
  {
    "path": "examples/rp/src/bin/interrupt.rs",
    "content": "//! This example shows how you can use raw interrupt handlers alongside embassy.\n//! The example also showcases some of the options available for sharing resources/data.\n//!\n//! In the example, an ADC reading is triggered every time the PWM wraps around.\n//! The sample data is sent down a channel, to be processed inside a low priority task.\n//! The processed data is then used to adjust the PWM duty cycle, once every second.\n\n#![no_std]\n#![no_main]\n\nuse core::cell::{Cell, RefCell};\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_rp::adc::{self, Adc, Blocking};\nuse embassy_rp::gpio::Pull;\nuse embassy_rp::interrupt;\nuse embassy_rp::pwm::{Config, Pwm};\nuse embassy_sync::blocking_mutex::Mutex;\nuse embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;\nuse embassy_sync::channel::Channel;\nuse embassy_time::{Duration, Ticker};\nuse portable_atomic::{AtomicU32, Ordering};\nuse static_cell::StaticCell;\nuse {defmt_rtt as _, panic_probe as _};\n\nstatic COUNTER: AtomicU32 = AtomicU32::new(0);\nstatic PWM: Mutex<CriticalSectionRawMutex, RefCell<Option<Pwm>>> = Mutex::new(RefCell::new(None));\nstatic ADC: Mutex<CriticalSectionRawMutex, RefCell<Option<(Adc<Blocking>, adc::Channel)>>> =\n    Mutex::new(RefCell::new(None));\nstatic ADC_VALUES: Channel<CriticalSectionRawMutex, u16, 2048> = Channel::new();\n\n#[embassy_executor::main]\nasync fn main(spawner: Spawner) {\n    let p = embassy_rp::init(Default::default());\n\n    let adc = Adc::new_blocking(p.ADC, Default::default());\n    let p26 = adc::Channel::new_pin(p.PIN_26, Pull::None);\n    ADC.lock(|a| a.borrow_mut().replace((adc, p26)));\n\n    let pwm = Pwm::new_output_b(p.PWM_SLICE4, p.PIN_25, Default::default());\n    PWM.lock(|p| p.borrow_mut().replace(pwm));\n\n    // Enable the interrupt for pwm slice 4\n    embassy_rp::pac::PWM.inte().modify(|w| w.set_ch4(true));\n    unsafe {\n        cortex_m::peripheral::NVIC::unmask(interrupt::PWM_IRQ_WRAP);\n    }\n\n    // Tasks require their resources to have 'static lifetime\n    // No Mutex needed when sharing within the same executor/prio level\n    static AVG: StaticCell<Cell<u32>> = StaticCell::new();\n    let avg = AVG.init(Default::default());\n    spawner.spawn(processing(avg).unwrap());\n\n    let mut ticker = Ticker::every(Duration::from_secs(1));\n    loop {\n        ticker.next().await;\n        let freq = COUNTER.swap(0, Ordering::Relaxed);\n        info!(\"pwm freq: {:?} Hz\", freq);\n        info!(\"adc average: {:?}\", avg.get());\n\n        // Update the pwm duty cycle, based on the averaged adc reading\n        let mut config = Config::default();\n        config.compare_b = ((avg.get() as f32 / 4095.0) * config.top as f32) as _;\n        PWM.lock(|p| p.borrow_mut().as_mut().unwrap().set_config(&config));\n    }\n}\n\n#[embassy_executor::task]\nasync fn processing(avg: &'static Cell<u32>) {\n    let mut buffer: heapless::HistoryBuf<u16, 100> = Default::default();\n    loop {\n        let val = ADC_VALUES.receive().await;\n        buffer.write(val);\n        let sum: u32 = buffer.iter().map(|x| *x as u32).sum();\n        avg.set(sum / buffer.len() as u32);\n    }\n}\n\n#[interrupt]\nfn PWM_IRQ_WRAP() {\n    critical_section::with(|cs| {\n        let mut adc = ADC.borrow(cs).borrow_mut();\n        let (adc, p26) = adc.as_mut().unwrap();\n        let val = adc.blocking_read(p26).unwrap();\n        ADC_VALUES.try_send(val).ok();\n\n        // Clear the interrupt, so we don't immediately re-enter this irq handler\n        PWM.borrow(cs).borrow_mut().as_mut().unwrap().clear_wrapped();\n    });\n    COUNTER.fetch_add(1, Ordering::Relaxed);\n}\n"
  },
  {
    "path": "examples/rp/src/bin/multicore.rs",
    "content": "//! This example shows how to send messages between the two cores in the RP2040 chip.\n//!\n//! The LED on the RP Pico W board is connected differently. See wifi_blinky.rs.\n\n#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_executor::Executor;\nuse embassy_rp::gpio::{Level, Output};\nuse embassy_rp::multicore::{Stack, spawn_core1};\nuse embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;\nuse embassy_sync::channel::Channel;\nuse embassy_time::Timer;\nuse static_cell::StaticCell;\nuse {defmt_rtt as _, panic_probe as _};\n\nstatic mut CORE1_STACK: Stack<4096> = Stack::new();\nstatic EXECUTOR0: StaticCell<Executor> = StaticCell::new();\nstatic EXECUTOR1: StaticCell<Executor> = StaticCell::new();\nstatic CHANNEL: Channel<CriticalSectionRawMutex, LedState, 1> = Channel::new();\n\nenum LedState {\n    On,\n    Off,\n}\n\n#[cortex_m_rt::entry]\nfn main() -> ! {\n    let p = embassy_rp::init(Default::default());\n    let led = Output::new(p.PIN_25, Level::Low);\n\n    spawn_core1(\n        p.CORE1,\n        unsafe { &mut *core::ptr::addr_of_mut!(CORE1_STACK) },\n        move || {\n            let executor1 = EXECUTOR1.init(Executor::new());\n            executor1.run(|spawner| spawner.spawn(unwrap!(core1_task(led))));\n        },\n    );\n\n    let executor0 = EXECUTOR0.init(Executor::new());\n    executor0.run(|spawner| spawner.spawn(unwrap!(core0_task())));\n}\n\n#[embassy_executor::task]\nasync fn core0_task() {\n    info!(\"Hello from core 0\");\n    loop {\n        CHANNEL.send(LedState::On).await;\n        Timer::after_millis(100).await;\n        CHANNEL.send(LedState::Off).await;\n        Timer::after_millis(400).await;\n    }\n}\n\n#[embassy_executor::task]\nasync fn core1_task(mut led: Output<'static>) {\n    info!(\"Hello from core 1\");\n    loop {\n        match CHANNEL.receive().await {\n            LedState::On => led.set_high(),\n            LedState::Off => led.set_low(),\n        }\n    }\n}\n"
  },
  {
    "path": "examples/rp/src/bin/multiprio.rs",
    "content": "//! This example showcases how to create multiple Executor instances to run tasks at\n//! different priority levels.\n//!\n//! Low priority executor runs in thread mode (not interrupt), and uses `sev` for signaling\n//! there's work in the queue, and `wfe` for waiting for work.\n//!\n//! Medium and high priority executors run in two interrupts with different priorities.\n//! Signaling work is done by pending the interrupt. No \"waiting\" needs to be done explicitly, since\n//! when there's work the interrupt will trigger and run the executor.\n//!\n//! Sample output below. Note that high priority ticks can interrupt everything else, and\n//! medium priority computations can interrupt low priority computations, making them to appear\n//! to take significantly longer time.\n//!\n//! ```not_rust\n//!     [med] Starting long computation\n//!     [med] done in 992 ms\n//!         [high] tick!\n//! [low] Starting long computation\n//!     [med] Starting long computation\n//!         [high] tick!\n//!         [high] tick!\n//!     [med] done in 993 ms\n//!     [med] Starting long computation\n//!         [high] tick!\n//!         [high] tick!\n//!     [med] done in 993 ms\n//! [low] done in 3972 ms\n//!     [med] Starting long computation\n//!         [high] tick!\n//!         [high] tick!\n//!     [med] done in 993 ms\n//! ```\n//!\n//! For comparison, try changing the code so all 3 tasks get spawned on the low priority executor.\n//! You will get an output like the following. Note that no computation is ever interrupted.\n//!\n//! ```not_rust\n//!         [high] tick!\n//!     [med] Starting long computation\n//!     [med] done in 496 ms\n//! [low] Starting long computation\n//! [low] done in 992 ms\n//!     [med] Starting long computation\n//!     [med] done in 496 ms\n//!         [high] tick!\n//! [low] Starting long computation\n//! [low] done in 992 ms\n//!         [high] tick!\n//!     [med] Starting long computation\n//!     [med] done in 496 ms\n//!         [high] tick!\n//! ```\n//!\n\n#![no_std]\n#![no_main]\n\nuse cortex_m_rt::entry;\nuse defmt::{info, unwrap};\nuse embassy_executor::{Executor, InterruptExecutor};\nuse embassy_rp::interrupt;\nuse embassy_rp::interrupt::{InterruptExt, Priority};\nuse embassy_time::{Instant, TICK_HZ, Timer};\nuse static_cell::StaticCell;\nuse {defmt_rtt as _, panic_probe as _};\n\n#[embassy_executor::task]\nasync fn run_high() {\n    loop {\n        info!(\"        [high] tick!\");\n        Timer::after_ticks(673740).await;\n    }\n}\n\n#[embassy_executor::task]\nasync fn run_med() {\n    loop {\n        let start = Instant::now();\n        info!(\"    [med] Starting long computation\");\n\n        // Spin-wait to simulate a long CPU computation\n        embassy_time::block_for(embassy_time::Duration::from_secs(1)); // ~1 second\n\n        let end = Instant::now();\n        let ms = end.duration_since(start).as_ticks() * 1000 / TICK_HZ;\n        info!(\"    [med] done in {} ms\", ms);\n\n        Timer::after_ticks(53421).await;\n    }\n}\n\n#[embassy_executor::task]\nasync fn run_low() {\n    loop {\n        let start = Instant::now();\n        info!(\"[low] Starting long computation\");\n\n        // Spin-wait to simulate a long CPU computation\n        embassy_time::block_for(embassy_time::Duration::from_secs(2)); // ~2 seconds\n\n        let end = Instant::now();\n        let ms = end.duration_since(start).as_ticks() * 1000 / TICK_HZ;\n        info!(\"[low] done in {} ms\", ms);\n\n        Timer::after_ticks(82983).await;\n    }\n}\n\nstatic EXECUTOR_HIGH: InterruptExecutor = InterruptExecutor::new();\nstatic EXECUTOR_MED: InterruptExecutor = InterruptExecutor::new();\nstatic EXECUTOR_LOW: StaticCell<Executor> = StaticCell::new();\n\n#[interrupt]\nunsafe fn SWI_IRQ_1() {\n    unsafe { EXECUTOR_HIGH.on_interrupt() }\n}\n\n#[interrupt]\nunsafe fn SWI_IRQ_0() {\n    unsafe { EXECUTOR_MED.on_interrupt() }\n}\n\n#[entry]\nfn main() -> ! {\n    info!(\"Hello World!\");\n\n    let _p = embassy_rp::init(Default::default());\n\n    // High-priority executor: SWI_IRQ_1, priority level 2\n    interrupt::SWI_IRQ_1.set_priority(Priority::P2);\n    let spawner = EXECUTOR_HIGH.start(interrupt::SWI_IRQ_1);\n    spawner.spawn(unwrap!(run_high()));\n\n    // Medium-priority executor: SWI_IRQ_0, priority level 3\n    interrupt::SWI_IRQ_0.set_priority(Priority::P3);\n    let spawner = EXECUTOR_MED.start(interrupt::SWI_IRQ_0);\n    spawner.spawn(unwrap!(run_med()));\n\n    // Low priority executor: runs in thread mode, using WFE/SEV\n    let executor = EXECUTOR_LOW.init(Executor::new());\n    executor.run(|spawner| {\n        spawner.spawn(unwrap!(run_low()));\n    });\n}\n"
  },
  {
    "path": "examples/rp/src/bin/orchestrate_tasks.rs",
    "content": "//! This example demonstrates some approaches to communicate between tasks in order to orchestrate the state of the system.\n//!\n//! The system consists of several tasks:\n//! - Three tasks that generate random numbers at different intervals (simulating i.e. sensor readings)\n//! - A task that monitors USB power connection (hardware event handling)\n//! - A task that reads system voltage (ADC sampling)\n//! - A consumer task that processes all this information\n//!\n//! The system maintains state in a single place, wrapped in a Mutex.\n//!\n//! We demonstrate how to:\n//! - use a mutex to maintain shared state between tasks\n//! - use a channel to send events between tasks\n//! - use an orchestrator task to coordinate tasks and handle state transitions\n//! - use signals to notify about state changes and terminate tasks\n\n#![no_std]\n#![no_main]\n\nuse assign_resources::assign_resources;\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_futures::select::{Either, select};\nuse embassy_rp::adc::{Adc, Channel, Config, InterruptHandler};\nuse embassy_rp::clocks::RoscRng;\nuse embassy_rp::gpio::{Input, Pull};\nuse embassy_rp::{Peri, bind_interrupts, peripherals};\nuse embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;\nuse embassy_sync::mutex::Mutex;\nuse embassy_sync::{channel, signal};\nuse embassy_time::{Duration, Timer};\nuse {defmt_rtt as _, panic_probe as _};\n\n// Hardware resource assignment. See other examples for different ways of doing this.\nassign_resources! {\n    vsys: Vsys {\n        adc: ADC,\n        pin_29: PIN_29,\n    },\n    vbus: Vbus {\n        pin_24: PIN_24,\n    },\n}\n\n// Interrupt binding - required for hardware peripherals like ADC\nbind_interrupts!(struct Irqs {\n    ADC_IRQ_FIFO => InterruptHandler;\n});\n\n/// Events that worker tasks send to the orchestrator\nenum Events {\n    UsbPowered(bool),      // USB connection state changed\n    VsysVoltage(f32),      // New voltage reading\n    FirstRandomSeed(u32),  // Random number from 30s timer\n    SecondRandomSeed(u32), // Random number from 60s timer\n    ThirdRandomSeed(u32),  // Random number from 90s timer\n    ResetFirstRandomSeed,  // Signal to reset the first counter\n}\n\n/// Commands that can control task behavior.\n/// Currently only used to stop tasks, but could be extended for other controls.\nenum Commands {\n    /// Signals a task to stop execution\n    Stop,\n}\n\n/// The central state of our system, shared between tasks.\n#[derive(Clone, Format)]\nstruct State {\n    usb_powered: bool,\n    vsys_voltage: f32,\n    first_random_seed: u32,\n    second_random_seed: u32,\n    third_random_seed: u32,\n    first_random_seed_task_running: bool,\n    times_we_got_first_random_seed: u8,\n    maximum_times_we_want_first_random_seed: u8,\n}\n\n/// A formatted view of the system status, used for logging. Used for the below `get_system_summary` fn.\n#[derive(Format)]\nstruct SystemStatus {\n    power_source: &'static str,\n    voltage: f32,\n}\n\nimpl State {\n    const fn new() -> Self {\n        Self {\n            usb_powered: false,\n            vsys_voltage: 0.0,\n            first_random_seed: 0,\n            second_random_seed: 0,\n            third_random_seed: 0,\n            first_random_seed_task_running: false,\n            times_we_got_first_random_seed: 0,\n            maximum_times_we_want_first_random_seed: 3,\n        }\n    }\n\n    /// Returns a formatted summary of power state and voltage.\n    /// Shows how to create methods that work with shared state.\n    fn get_system_summary(&self) -> SystemStatus {\n        SystemStatus {\n            power_source: if self.usb_powered {\n                \"USB powered\"\n            } else {\n                \"Battery powered\"\n            },\n            voltage: self.vsys_voltage,\n        }\n    }\n}\n\n/// The shared state protected by a mutex\nstatic SYSTEM_STATE: Mutex<CriticalSectionRawMutex, State> = Mutex::new(State::new());\n\n/// Channel for events from worker tasks to the orchestrator\nstatic EVENT_CHANNEL: channel::Channel<CriticalSectionRawMutex, Events, 10> = channel::Channel::new();\n\n/// Signal used to stop the first random number task\nstatic STOP_FIRST_RANDOM_SIGNAL: signal::Signal<CriticalSectionRawMutex, Commands> = signal::Signal::new();\n\n/// Signal for notifying about state changes\nstatic STATE_CHANGED: signal::Signal<CriticalSectionRawMutex, ()> = signal::Signal::new();\n\n#[embassy_executor::main]\nasync fn main(spawner: Spawner) {\n    let p = embassy_rp::init(Default::default());\n    let r = split_resources! {p};\n\n    spawner.spawn(orchestrate(spawner).unwrap());\n    spawner.spawn(random_60s(spawner).unwrap());\n    spawner.spawn(random_90s(spawner).unwrap());\n    // `random_30s` is not spawned here, butin the orchestrate task depending on state\n    spawner.spawn(usb_power(spawner, r.vbus).unwrap());\n    spawner.spawn(vsys_voltage(spawner, r.vsys).unwrap());\n    spawner.spawn(consumer(spawner).unwrap());\n}\n\n/// Main task that processes all events and updates system state.\n#[embassy_executor::task]\nasync fn orchestrate(spawner: Spawner) {\n    let receiver = EVENT_CHANNEL.receiver();\n\n    loop {\n        // Do nothing until we receive any event\n        let event = receiver.receive().await;\n\n        // Scope in which we want to lock the system state. As an alternative we could also call `drop` on the state\n        {\n            let mut state = SYSTEM_STATE.lock().await;\n\n            match event {\n                Events::UsbPowered(usb_powered) => {\n                    state.usb_powered = usb_powered;\n                    info!(\"Usb powered: {}\", usb_powered);\n                    info!(\"System summary: {}\", state.get_system_summary());\n                }\n                Events::VsysVoltage(voltage) => {\n                    state.vsys_voltage = voltage;\n                    info!(\"Vsys voltage: {}\", voltage);\n                }\n                Events::FirstRandomSeed(seed) => {\n                    state.first_random_seed = seed;\n                    state.times_we_got_first_random_seed += 1;\n                    info!(\n                        \"First random seed: {}, and that was iteration {} of receiving this.\",\n                        seed, &state.times_we_got_first_random_seed\n                    );\n                }\n                Events::SecondRandomSeed(seed) => {\n                    state.second_random_seed = seed;\n                    info!(\"Second random seed: {}\", seed);\n                }\n                Events::ThirdRandomSeed(seed) => {\n                    state.third_random_seed = seed;\n                    info!(\"Third random seed: {}\", seed);\n                }\n                Events::ResetFirstRandomSeed => {\n                    state.times_we_got_first_random_seed = 0;\n                    state.first_random_seed = 0;\n                    info!(\"Resetting the first random seed counter\");\n                }\n            }\n\n            // Handle task orchestration based on state\n            // Just placed as an example here, could be hooked into the event system, puton a timer, ...\n            match state.times_we_got_first_random_seed {\n                max if max == state.maximum_times_we_want_first_random_seed => {\n                    info!(\"Stopping the first random signal task\");\n                    STOP_FIRST_RANDOM_SIGNAL.signal(Commands::Stop);\n                    EVENT_CHANNEL.sender().send(Events::ResetFirstRandomSeed).await;\n                }\n                0 => {\n                    let respawn_first_random_seed_task = !state.first_random_seed_task_running;\n                    // Deliberately dropping the Mutex lock here to release it before a lengthy operation\n                    drop(state);\n                    if respawn_first_random_seed_task {\n                        info!(\"(Re)-Starting the first random signal task\");\n                        spawner.spawn(random_30s(spawner).unwrap());\n                    }\n                }\n                _ => {}\n            }\n        }\n\n        STATE_CHANGED.signal(());\n    }\n}\n\n/// Task that monitors state changes and logs system status.\n#[embassy_executor::task]\nasync fn consumer(_spawner: Spawner) {\n    loop {\n        // Wait for state change notification\n        STATE_CHANGED.wait().await;\n\n        let state = SYSTEM_STATE.lock().await;\n        info!(\n            \"State update - {} | Seeds - First: {} (count: {}/{}, running: {}), Second: {}, Third: {}\",\n            state.get_system_summary(),\n            state.first_random_seed,\n            state.times_we_got_first_random_seed,\n            state.maximum_times_we_want_first_random_seed,\n            state.first_random_seed_task_running,\n            state.second_random_seed,\n            state.third_random_seed\n        );\n    }\n}\n\n/// Task that generates random numbers every 30 seconds until stopped.\n/// Shows how to handle both timer events and stop signals.\n/// As an example of some routine we want to be on or off depending on other needs.\n#[embassy_executor::task]\nasync fn random_30s(_spawner: Spawner) {\n    {\n        let mut state = SYSTEM_STATE.lock().await;\n        state.first_random_seed_task_running = true;\n    }\n\n    let mut rng = RoscRng;\n    let sender = EVENT_CHANNEL.sender();\n\n    loop {\n        // Wait for either 30s timer or stop signal (like select() in Go)\n        match select(Timer::after(Duration::from_secs(30)), STOP_FIRST_RANDOM_SIGNAL.wait()).await {\n            Either::First(_) => {\n                info!(\"30s are up, generating random number\");\n                let random_number = rng.next_u32();\n                sender.send(Events::FirstRandomSeed(random_number)).await;\n            }\n            Either::Second(_) => {\n                info!(\"Received signal to stop, goodbye!\");\n\n                let mut state = SYSTEM_STATE.lock().await;\n                state.first_random_seed_task_running = false;\n\n                break;\n            }\n        }\n    }\n}\n\n/// Task that generates random numbers every 60 seconds. As an example of some routine.\n#[embassy_executor::task]\nasync fn random_60s(_spawner: Spawner) {\n    let mut rng = RoscRng;\n    let sender = EVENT_CHANNEL.sender();\n\n    loop {\n        Timer::after(Duration::from_secs(60)).await;\n        let random_number = rng.next_u32();\n        sender.send(Events::SecondRandomSeed(random_number)).await;\n    }\n}\n\n/// Task that generates random numbers every 90 seconds. . As an example of some routine.\n#[embassy_executor::task]\nasync fn random_90s(_spawner: Spawner) {\n    let mut rng = RoscRng;\n    let sender = EVENT_CHANNEL.sender();\n\n    loop {\n        Timer::after(Duration::from_secs(90)).await;\n        let random_number = rng.next_u32();\n        sender.send(Events::ThirdRandomSeed(random_number)).await;\n    }\n}\n\n/// Task that monitors USB power connection. As an example of some Interrupt somewhere.\n#[embassy_executor::task]\npub async fn usb_power(_spawner: Spawner, r: Vbus) {\n    let mut vbus_in = Input::new(r.pin_24, Pull::None);\n    let sender = EVENT_CHANNEL.sender();\n\n    loop {\n        sender.send(Events::UsbPowered(vbus_in.is_high())).await;\n        vbus_in.wait_for_any_edge().await;\n    }\n}\n\n/// Task that reads system voltage through ADC. As an example of some continuous sensor reading.\n#[embassy_executor::task]\npub async fn vsys_voltage(_spawner: Spawner, r: Vsys) {\n    let mut adc = Adc::new(r.adc, Irqs, Config::default());\n    let vsys_in = r.pin_29;\n    let mut channel = Channel::new_pin(vsys_in, Pull::None);\n    let sender = EVENT_CHANNEL.sender();\n\n    loop {\n        Timer::after(Duration::from_secs(30)).await;\n        let adc_value = adc.read(&mut channel).await.unwrap();\n        let voltage = (adc_value as f32) * 3.3 * 3.0 / 4096.0;\n        sender.send(Events::VsysVoltage(voltage)).await;\n    }\n}\n"
  },
  {
    "path": "examples/rp/src/bin/overclock.rs",
    "content": "//! # Overclocking the RP2040 to 200 MHz\n//!\n//! This example demonstrates how to configure the RP2040 to run at 200 MHz.\n\n#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_rp::clocks::{ClockConfig, clk_sys_freq, core_voltage};\nuse embassy_rp::config::Config;\nuse embassy_rp::gpio::{Level, Output};\nuse embassy_time::{Duration, Instant, Timer};\nuse {defmt_rtt as _, panic_probe as _};\n\nconst COUNT_TO: i64 = 10_000_000;\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) -> ! {\n    // Set up for clock frequency of 200 MHz, setting all necessary defaults.\n    let config = Config::new(ClockConfig::system_freq(200_000_000).unwrap());\n\n    // Initialize the peripherals\n    let p = embassy_rp::init(config);\n\n    // Show CPU frequency for verification\n    let sys_freq = clk_sys_freq();\n    info!(\"System clock frequency: {} MHz\", sys_freq / 1_000_000);\n    // Show core voltage for verification\n    let core_voltage = core_voltage().unwrap();\n    info!(\"Core voltage: {}\", core_voltage);\n\n    // LED to indicate the system is running\n    let mut led = Output::new(p.PIN_25, Level::Low);\n\n    loop {\n        // Reset the counter at the start of measurement period\n        let mut counter = 0;\n\n        // Turn LED on while counting\n        led.set_high();\n\n        let start = Instant::now();\n\n        // This is a busy loop that will take some time to complete\n        while counter < COUNT_TO {\n            counter += 1;\n        }\n\n        let elapsed = Instant::now() - start;\n\n        // Report the elapsed time\n        led.set_low();\n        info!(\n            \"At {}Mhz: Elapsed time to count to {}: {}ms\",\n            sys_freq / 1_000_000,\n            counter,\n            elapsed.as_millis()\n        );\n\n        // Wait 2 seconds before starting the next measurement\n        Timer::after(Duration::from_secs(2)).await;\n    }\n}\n"
  },
  {
    "path": "examples/rp/src/bin/overclock_manual.rs",
    "content": "//! # Overclocking the RP2040 to 200 MHz manually\n//!\n//! This example demonstrates how to manually configure the RP2040 to run at 200 MHz.\n\n#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_rp::clocks::{ClockConfig, CoreVoltage, PllConfig, clk_sys_freq, core_voltage};\nuse embassy_rp::config::Config;\nuse embassy_rp::gpio::{Level, Output};\nuse embassy_time::{Duration, Instant, Timer};\nuse {defmt_rtt as _, panic_probe as _};\n\nconst COUNT_TO: i64 = 10_000_000;\n\n/// Configure the RP2040 for 200 MHz operation by manually specifying the PLL settings.\nfn configure_manual_overclock() -> Config {\n    // Set the PLL configuration manually, starting from default values\n    let mut config = Config::default();\n\n    // Set the system clock to 200 MHz\n    config.clocks = ClockConfig::manual_pll(\n        12_000_000, // Crystal frequency, 12 MHz is common. If using custom, set to your value.\n        PllConfig {\n            refdiv: 1,    // Reference divider\n            fbdiv: 100,   // Feedback divider\n            post_div1: 3, // Post divider 1\n            post_div2: 2, // Post divider 2\n        },\n        CoreVoltage::V1_15, // Core voltage, should be set to V1_15 for 200 MHz\n    );\n\n    config\n}\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) -> ! {\n    // Initialize with our manual overclock configuration\n    let p = embassy_rp::init(configure_manual_overclock());\n\n    // Show CPU frequency for verification\n    let sys_freq = clk_sys_freq();\n    info!(\"System clock frequency: {} MHz\", sys_freq / 1_000_000);\n    // Show core voltage for verification\n    let core_voltage = core_voltage().unwrap();\n    info!(\"Core voltage: {}\", core_voltage);\n\n    // LED to indicate the system is running\n    let mut led = Output::new(p.PIN_25, Level::Low);\n\n    loop {\n        // Reset the counter at the start of measurement period\n        let mut counter = 0;\n\n        // Turn LED on while counting\n        led.set_high();\n\n        let start = Instant::now();\n\n        // This is a busy loop that will take some time to complete\n        while counter < COUNT_TO {\n            counter += 1;\n        }\n\n        let elapsed = Instant::now() - start;\n\n        // Report the elapsed time\n        led.set_low();\n        info!(\n            \"At {}Mhz: Elapsed time to count to {}: {}ms\",\n            sys_freq / 1_000_000,\n            counter,\n            elapsed.as_millis()\n        );\n\n        // Wait 2 seconds before starting the next measurement\n        Timer::after(Duration::from_secs(2)).await;\n    }\n}\n"
  },
  {
    "path": "examples/rp/src/bin/pio_async.rs",
    "content": "//! This example shows powerful PIO module in the RP2040 chip.\n\n#![no_std]\n#![no_main]\nuse defmt::info;\nuse embassy_executor::Spawner;\nuse embassy_rp::peripherals::PIO0;\nuse embassy_rp::pio::program::pio_asm;\nuse embassy_rp::pio::{Common, Config, InterruptHandler, Irq, Pio, PioPin, ShiftDirection, StateMachine};\nuse embassy_rp::{Peri, bind_interrupts};\nuse fixed::traits::ToFixed;\nuse fixed_macro::types::U56F8;\nuse {defmt_rtt as _, panic_probe as _};\n\nbind_interrupts!(struct Irqs {\n    PIO0_IRQ_0 => InterruptHandler<PIO0>;\n});\n\nfn setup_pio_task_sm0<'d>(pio: &mut Common<'d, PIO0>, sm: &mut StateMachine<'d, PIO0, 0>, pin: Peri<'d, impl PioPin>) {\n    // Setup sm0\n\n    // Send data serially to pin\n    let prg = pio_asm!(\n        \".origin 16\",\n        \"set pindirs, 1\",\n        \".wrap_target\",\n        \"out pins,1 [19]\",\n        \".wrap\",\n    );\n\n    let mut cfg = Config::default();\n    cfg.use_program(&pio.load_program(&prg.program), &[]);\n    let out_pin = pio.make_pio_pin(pin);\n    cfg.set_out_pins(&[&out_pin]);\n    cfg.set_set_pins(&[&out_pin]);\n    cfg.clock_divider = (U56F8!(125_000_000) / 20 / 200).to_fixed();\n    cfg.shift_out.auto_fill = true;\n    sm.set_config(&cfg);\n}\n\n#[embassy_executor::task]\nasync fn pio_task_sm0(mut sm: StateMachine<'static, PIO0, 0>) {\n    sm.set_enable(true);\n\n    let mut v = 0x0f0caffa;\n    loop {\n        sm.tx().wait_push(v).await;\n        v ^= 0xffff;\n        info!(\"Pushed {:032b} to FIFO\", v);\n    }\n}\n\nfn setup_pio_task_sm1<'d>(pio: &mut Common<'d, PIO0>, sm: &mut StateMachine<'d, PIO0, 1>) {\n    // Setupm sm1\n\n    // Read 0b10101 repeatedly until ISR is full\n    let prg = pio_asm!(\n        //\n        \".origin 8\",\n        \"set x, 0x15\",\n        \".wrap_target\",\n        \"in x, 5 [31]\",\n        \".wrap\",\n    );\n\n    let mut cfg = Config::default();\n    cfg.use_program(&pio.load_program(&prg.program), &[]);\n    cfg.clock_divider = (U56F8!(125_000_000) / 2000).to_fixed();\n    cfg.shift_in.auto_fill = true;\n    cfg.shift_in.direction = ShiftDirection::Right;\n    sm.set_config(&cfg);\n}\n\n#[embassy_executor::task]\nasync fn pio_task_sm1(mut sm: StateMachine<'static, PIO0, 1>) {\n    sm.set_enable(true);\n    loop {\n        let rx = sm.rx().wait_pull().await;\n        info!(\"Pulled {:032b} from FIFO\", rx);\n    }\n}\n\nfn setup_pio_task_sm2<'d>(pio: &mut Common<'d, PIO0>, sm: &mut StateMachine<'d, PIO0, 2>) {\n    // Setup sm2\n\n    // Repeatedly trigger IRQ 3\n    let prg = pio_asm!(\n        \".origin 0\",\n        \".wrap_target\",\n        \"set x,10\",\n        \"delay:\",\n        \"jmp x-- delay [15]\",\n        \"irq 3 [15]\",\n        \".wrap\",\n    );\n    let mut cfg = Config::default();\n    cfg.use_program(&pio.load_program(&prg.program), &[]);\n    cfg.clock_divider = (U56F8!(125_000_000) / 2000).to_fixed();\n    sm.set_config(&cfg);\n}\n\n#[embassy_executor::task]\nasync fn pio_task_sm2(mut irq: Irq<'static, PIO0, 3>, mut sm: StateMachine<'static, PIO0, 2>) {\n    sm.set_enable(true);\n    loop {\n        irq.wait().await;\n        info!(\"IRQ trigged\");\n    }\n}\n\n#[embassy_executor::main]\nasync fn main(spawner: Spawner) {\n    let p = embassy_rp::init(Default::default());\n    let pio = p.PIO0;\n\n    let Pio {\n        mut common,\n        irq3,\n        mut sm0,\n        mut sm1,\n        mut sm2,\n        ..\n    } = Pio::new(pio, Irqs);\n\n    setup_pio_task_sm0(&mut common, &mut sm0, p.PIN_0);\n    setup_pio_task_sm1(&mut common, &mut sm1);\n    setup_pio_task_sm2(&mut common, &mut sm2);\n    spawner.spawn(pio_task_sm0(sm0).unwrap());\n    spawner.spawn(pio_task_sm1(sm1).unwrap());\n    spawner.spawn(pio_task_sm2(irq3, sm2).unwrap());\n}\n"
  },
  {
    "path": "examples/rp/src/bin/pio_clk.rs",
    "content": "//! This example shows how to output a clock signal on an output pin using the PIO module in the RP2040 chip.\n\n#![no_std]\n#![no_main]\n\nuse embassy_executor::Spawner;\nuse embassy_rp::bind_interrupts;\nuse embassy_rp::peripherals::PIO0;\nuse embassy_rp::pio::{InterruptHandler, Pio};\nuse embassy_rp::pio_programs::clk::{PioClk, PioClkProgram};\nuse embassy_time::Timer;\nuse {defmt_rtt as _, panic_probe as _};\n\nbind_interrupts!(struct Irqs {\n    PIO0_IRQ_0 => InterruptHandler<PIO0>;\n});\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let p = embassy_rp::init(Default::default());\n    let Pio { mut common, sm0, .. } = Pio::new(p.PIO0, Irqs);\n\n    let prg = PioClkProgram::new(&mut common);\n    let mut clk = PioClk::new(&mut common, sm0, p.PIN_18, &prg, 10_000);\n\n    loop {\n        clk.start();\n        Timer::after_millis(5000).await;\n        clk.stop();\n        Timer::after_millis(5000).await;\n    }\n}\n"
  },
  {
    "path": "examples/rp/src/bin/pio_dma.rs",
    "content": "//! This example shows powerful PIO module in the RP2040 chip.\n\n#![no_std]\n#![no_main]\nuse defmt::info;\nuse embassy_executor::Spawner;\nuse embassy_futures::join::join;\nuse embassy_rp::peripherals::{DMA_CH0, DMA_CH1, PIO0};\nuse embassy_rp::pio::program::pio_asm;\nuse embassy_rp::pio::{Config, InterruptHandler, Pio, ShiftConfig, ShiftDirection};\nuse embassy_rp::{bind_interrupts, dma};\nuse fixed::traits::ToFixed;\nuse fixed_macro::types::U56F8;\nuse {defmt_rtt as _, panic_probe as _};\n\nbind_interrupts!(struct Irqs {\n    PIO0_IRQ_0 => InterruptHandler<PIO0>;\n    DMA_IRQ_0 => dma::InterruptHandler<DMA_CH0>, dma::InterruptHandler<DMA_CH1>;\n});\n\nfn swap_nibbles(v: u32) -> u32 {\n    let v = (v & 0x0f0f_0f0f) << 4 | (v & 0xf0f0_f0f0) >> 4;\n    let v = (v & 0x00ff_00ff) << 8 | (v & 0xff00_ff00) >> 8;\n    (v & 0x0000_ffff) << 16 | (v & 0xffff_0000) >> 16\n}\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let p = embassy_rp::init(Default::default());\n    let pio = p.PIO0;\n    let Pio {\n        mut common,\n        sm0: mut sm,\n        ..\n    } = Pio::new(pio, Irqs);\n\n    let prg = pio_asm!(\n        \".origin 0\",\n        \"set pindirs,1\",\n        \".wrap_target\",\n        \"set y,7\",\n        \"loop:\",\n        \"out x,4\",\n        \"in x,4\",\n        \"jmp y--, loop\",\n        \".wrap\",\n    );\n\n    let mut cfg = Config::default();\n    cfg.use_program(&common.load_program(&prg.program), &[]);\n    cfg.clock_divider = (U56F8!(125_000_000) / U56F8!(10_000)).to_fixed();\n    cfg.shift_in = ShiftConfig {\n        auto_fill: true,\n        threshold: 32,\n        direction: ShiftDirection::Left,\n    };\n    cfg.shift_out = ShiftConfig {\n        auto_fill: true,\n        threshold: 32,\n        direction: ShiftDirection::Right,\n    };\n\n    sm.set_config(&cfg);\n    sm.set_enable(true);\n\n    let mut dma_out_ref = dma::Channel::new(p.DMA_CH0, Irqs);\n    let mut dma_in_ref = dma::Channel::new(p.DMA_CH1, Irqs);\n    let mut dout = [0x12345678u32; 29];\n    for i in 1..dout.len() {\n        dout[i] = (dout[i - 1] & 0x0fff_ffff) * 13 + 7;\n    }\n    let mut din = [0u32; 29];\n    loop {\n        let (rx, tx) = sm.rx_tx();\n        join(\n            tx.dma_push(&mut dma_out_ref, &dout, false),\n            rx.dma_pull(&mut dma_in_ref, &mut din, false),\n        )\n        .await;\n        for i in 0..din.len() {\n            assert_eq!(din[i], swap_nibbles(dout[i]));\n        }\n        info!(\"Swapped {} words\", dout.len());\n    }\n}\n"
  },
  {
    "path": "examples/rp/src/bin/pio_hd44780.rs",
    "content": "//! This example shows powerful PIO module in the RP2040 chip to communicate with a HD44780 display.\n//! See (https://www.sparkfun.com/datasheets/LCD/HD44780.pdf)\n\n#![no_std]\n#![no_main]\n\nuse core::fmt::Write;\n\nuse embassy_executor::Spawner;\nuse embassy_rp::peripherals::{DMA_CH3, PIO0};\nuse embassy_rp::pio::{InterruptHandler, Pio};\nuse embassy_rp::pio_programs::hd44780::{PioHD44780, PioHD44780CommandSequenceProgram, PioHD44780CommandWordProgram};\nuse embassy_rp::pwm::{self, Pwm};\nuse embassy_rp::{bind_interrupts, dma};\nuse embassy_time::{Instant, Timer};\nuse {defmt_rtt as _, panic_probe as _};\n\nbind_interrupts!(pub struct Irqs {\n    PIO0_IRQ_0 => InterruptHandler<PIO0>;\n    DMA_IRQ_0 => dma::InterruptHandler<DMA_CH3>;\n});\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    // this test assumes a 2x16 HD44780 display attached as follow:\n    //   rs  = PIN0\n    //   rw  = PIN1\n    //   e   = PIN2\n    //   db4 = PIN3\n    //   db5 = PIN4\n    //   db6 = PIN5\n    //   db7 = PIN6\n    // additionally a pwm signal for a bias voltage charge pump is provided on pin 15,\n    // allowing direct connection of the display to the RP2040 without level shifters.\n    let p = embassy_rp::init(Default::default());\n\n    let _pwm = Pwm::new_output_b(p.PWM_SLICE7, p.PIN_15, {\n        let mut c = pwm::Config::default();\n        c.divider = 125.into();\n        c.top = 100;\n        c.compare_b = 50;\n        c\n    });\n\n    let Pio {\n        mut common, sm0, irq0, ..\n    } = Pio::new(p.PIO0, Irqs);\n\n    let word_prg = PioHD44780CommandWordProgram::new(&mut common);\n    let seq_prg = PioHD44780CommandSequenceProgram::new(&mut common);\n\n    let mut hd = PioHD44780::new(\n        &mut common,\n        sm0,\n        irq0,\n        p.DMA_CH3,\n        Irqs,\n        p.PIN_0,\n        p.PIN_1,\n        p.PIN_2,\n        p.PIN_3,\n        p.PIN_4,\n        p.PIN_5,\n        p.PIN_6,\n        &word_prg,\n        &seq_prg,\n    )\n    .await;\n\n    loop {\n        struct Buf<const N: usize>([u8; N], usize);\n        impl<const N: usize> Write for Buf<N> {\n            fn write_str(&mut self, s: &str) -> Result<(), core::fmt::Error> {\n                for b in s.as_bytes() {\n                    if self.1 >= N {\n                        return Err(core::fmt::Error);\n                    }\n                    self.0[self.1] = *b;\n                    self.1 += 1;\n                }\n                Ok(())\n            }\n        }\n        let mut buf = Buf([0; 16], 0);\n        write!(buf, \"up {}s\", Instant::now().as_micros() as f32 / 1e6).unwrap();\n        hd.add_line(&buf.0[0..buf.1]).await;\n        Timer::after_secs(1).await;\n    }\n}\n"
  },
  {
    "path": "examples/rp/src/bin/pio_i2s.rs",
    "content": "//! This example shows generating audio and sending it to a connected i2s DAC using the PIO\n//! module of the RP2040.\n//!\n//! Connect the i2s DAC as follows:\n//!   bclk : GPIO 18\n//!   lrc  : GPIO 19\n//!   din  : GPIO 20\n//! Then hold down the boot select button to trigger a rising triangle waveform.\n\n#![no_std]\n#![no_main]\n\nuse core::mem;\n\nuse embassy_executor::Spawner;\nuse embassy_rp::bootsel::is_bootsel_pressed;\nuse embassy_rp::peripherals::{DMA_CH0, PIO0};\nuse embassy_rp::pio::{InterruptHandler, Pio};\nuse embassy_rp::pio_programs::i2s::{PioI2sOut, PioI2sOutProgram};\nuse embassy_rp::{bind_interrupts, dma};\nuse static_cell::StaticCell;\nuse {defmt_rtt as _, panic_probe as _};\n\nbind_interrupts!(struct Irqs {\n    PIO0_IRQ_0 => InterruptHandler<PIO0>;\n    DMA_IRQ_0 => dma::InterruptHandler<DMA_CH0>;\n});\n\nconst SAMPLE_RATE: u32 = 48_000;\nconst BIT_DEPTH: u32 = 16;\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let mut p = embassy_rp::init(Default::default());\n\n    // Setup pio state machine for i2s output\n    let Pio { mut common, sm0, .. } = Pio::new(p.PIO0, Irqs);\n\n    let bit_clock_pin = p.PIN_18;\n    let left_right_clock_pin = p.PIN_19;\n    let data_pin = p.PIN_20;\n\n    let program = PioI2sOutProgram::new(&mut common);\n    let mut i2s = PioI2sOut::new(\n        &mut common,\n        sm0,\n        p.DMA_CH0,\n        Irqs,\n        data_pin,\n        bit_clock_pin,\n        left_right_clock_pin,\n        SAMPLE_RATE,\n        BIT_DEPTH,\n        &program,\n    );\n    i2s.start();\n\n    // create two audio buffers (back and front) which will take turns being\n    // filled with new audio data and being sent to the pio fifo using dma\n    const BUFFER_SIZE: usize = 960;\n    static DMA_BUFFER: StaticCell<[u32; BUFFER_SIZE * 2]> = StaticCell::new();\n    let dma_buffer = DMA_BUFFER.init_with(|| [0u32; BUFFER_SIZE * 2]);\n    let (mut back_buffer, mut front_buffer) = dma_buffer.split_at_mut(BUFFER_SIZE);\n\n    // start pio state machine\n    let mut fade_value: i32 = 0;\n    let mut phase: i32 = 0;\n\n    loop {\n        // trigger transfer of front buffer data to the pio fifo\n        // but don't await the returned future, yet\n        let dma_future = i2s.write(front_buffer);\n\n        // fade in audio when bootsel is pressed\n        let fade_target = if is_bootsel_pressed(p.BOOTSEL.reborrow()) {\n            i32::MAX\n        } else {\n            0\n        };\n\n        // fill back buffer with fresh audio samples before awaiting the dma future\n        for s in back_buffer.iter_mut() {\n            // exponential approach of fade_value => fade_target\n            fade_value += (fade_target - fade_value) >> 14;\n            // generate triangle wave with amplitude and frequency based on fade value\n            phase = (phase + (fade_value >> 22)) & 0xffff;\n            let triangle_sample = (phase as i16 as i32).abs() - 16384;\n            let sample = (triangle_sample * (fade_value >> 15)) >> 16;\n            // duplicate mono sample into lower and upper half of dma word\n            *s = (sample as u16 as u32) * 0x10001;\n        }\n\n        // now await the dma future. once the dma finishes, the next buffer needs to be queued\n        // within DMA_DEPTH / SAMPLE_RATE = 8 / 48000 seconds = 166us\n        dma_future.await;\n        mem::swap(&mut back_buffer, &mut front_buffer);\n    }\n}\n"
  },
  {
    "path": "examples/rp/src/bin/pio_i2s_mclk.rs",
    "content": "//! This example shows generating audio and sending to DAC with i2s with mclk\n//! generation using the PIO module of the RP2040.\n//!\n//! Connect the i2s DAC as follows:\n//!   mclk : GPIO 17\n//!   bclk : GPIO 18\n//!   lrc  : GPIO 19\n//!   din  : GPIO 20\n//! Then hold down the boot select button to trigger a rising triangle waveform.\n\n#![no_std]\n#![no_main]\n\nuse core::mem;\n\nuse embassy_executor::Spawner;\nuse embassy_rp::bootsel::is_bootsel_pressed;\nuse embassy_rp::peripherals::{DMA_CH0, PIO0};\nuse embassy_rp::pio::{InterruptHandler, Pio, PioBatch};\nuse embassy_rp::pio_programs::clk::{PioClk, PioClkProgram};\nuse embassy_rp::pio_programs::i2s::{PioI2sOut, PioI2sOutProgram};\nuse embassy_rp::{bind_interrupts, dma};\nuse static_cell::StaticCell;\nuse {defmt_rtt as _, panic_probe as _};\n\nbind_interrupts!(struct Irqs {\n    PIO0_IRQ_0 => InterruptHandler<PIO0>;\n    DMA_IRQ_0 => dma::InterruptHandler<DMA_CH0>;\n});\n\nconst SAMPLE_RATE: u32 = 48_000;\nconst BIT_DEPTH: u32 = 16;\nconst MASTER_CLOCK_MULTIPLIER: u32 = 8;\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let mut p = embassy_rp::init(Default::default());\n\n    // Setup pio state machine for i2s output\n    let Pio {\n        mut common, sm0, sm1, ..\n    } = Pio::new(p.PIO0, Irqs);\n\n    let master_clock_pin = p.PIN_17;\n    let bit_clock_pin = p.PIN_18;\n    let left_right_clock_pin = p.PIN_19;\n    let data_pin = p.PIN_20;\n\n    let program_clk = PioClkProgram::new(&mut common);\n    let mut mclk = PioClk::new(\n        &mut common,\n        sm0,\n        master_clock_pin,\n        &program_clk,\n        SAMPLE_RATE * (BIT_DEPTH * 2) * MASTER_CLOCK_MULTIPLIER,\n    );\n\n    let program_i2s = PioI2sOutProgram::new(&mut common);\n    let mut i2s = PioI2sOut::new(\n        &mut common,\n        sm1,\n        p.DMA_CH0,\n        Irqs,\n        data_pin,\n        bit_clock_pin,\n        left_right_clock_pin,\n        SAMPLE_RATE,\n        BIT_DEPTH,\n        &program_i2s,\n    );\n\n    let mut batch = PioBatch::new();\n    mclk.start_batched(&mut batch);\n    i2s.start_batched(&mut batch);\n    batch.execute();\n\n    // create two audio buffers (back and front) which will take turns being\n    // filled with new audio data and being sent to the pio fifo using dma\n    const BUFFER_SIZE: usize = 960;\n    static DMA_BUFFER: StaticCell<[u32; BUFFER_SIZE * 2]> = StaticCell::new();\n    let dma_buffer = DMA_BUFFER.init_with(|| [0u32; BUFFER_SIZE * 2]);\n    let (mut back_buffer, mut front_buffer) = dma_buffer.split_at_mut(BUFFER_SIZE);\n\n    // start pio state machine\n    let mut fade_value: i32 = 0;\n    let mut phase: i32 = 0;\n\n    loop {\n        // trigger transfer of front buffer data to the pio fifo\n        // but don't await the returned future, yet\n        let dma_future = i2s.write(front_buffer);\n\n        // fade in audio when bootsel is pressed\n        let fade_target = if is_bootsel_pressed(p.BOOTSEL.reborrow()) {\n            i32::MAX\n        } else {\n            0\n        };\n\n        // fill back buffer with fresh audio samples before awaiting the dma future\n        for s in back_buffer.iter_mut() {\n            // exponential approach of fade_value => fade_target\n            fade_value += (fade_target - fade_value) >> 14;\n            // generate triangle wave with amplitude and frequency based on fade value\n            phase = (phase + (fade_value >> 22)) & 0xffff;\n            let triangle_sample = (phase as i16 as i32).abs() - 16384;\n            let sample = (triangle_sample * (fade_value >> 15)) >> 16;\n            // duplicate mono sample into lower and upper half of dma word\n            *s = (sample as u16 as u32) * 0x10001;\n        }\n\n        // now await the dma future. once the dma finishes, the next buffer needs to be queued\n        // within DMA_DEPTH / SAMPLE_RATE = 8 / 48000 seconds = 166us\n        dma_future.await;\n        mem::swap(&mut back_buffer, &mut front_buffer);\n    }\n}\n"
  },
  {
    "path": "examples/rp/src/bin/pio_onewire.rs",
    "content": "//! This example shows how you can use PIO to read one or more `DS18B20` one-wire temperature sensors.\n//! This uses externally powered sensors. For parasite power, see the pio_onewire_parasite.rs example.\n\n#![no_std]\n#![no_main]\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_rp::bind_interrupts;\nuse embassy_rp::peripherals::PIO0;\nuse embassy_rp::pio::{InterruptHandler, Pio};\nuse embassy_rp::pio_programs::onewire::{PioOneWire, PioOneWireProgram, PioOneWireSearch};\nuse embassy_time::Timer;\nuse heapless::Vec;\nuse {defmt_rtt as _, panic_probe as _};\n\nbind_interrupts!(struct Irqs {\n    PIO0_IRQ_0 => InterruptHandler<PIO0>;\n});\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let p = embassy_rp::init(Default::default());\n    let mut pio = Pio::new(p.PIO0, Irqs);\n\n    let prg = PioOneWireProgram::new(&mut pio.common);\n    let mut onewire = PioOneWire::new(&mut pio.common, pio.sm0, p.PIN_2, &prg);\n\n    info!(\"Starting onewire search\");\n\n    let mut devices = Vec::<u64, 10>::new();\n    let mut search = PioOneWireSearch::new();\n    for _ in 0..10 {\n        if !search.is_finished() {\n            if let Some(address) = search.next(&mut onewire).await {\n                if crc8(&address.to_le_bytes()) == 0 {\n                    info!(\"Found addres: {:x}\", address);\n                    let _ = devices.push(address);\n                } else {\n                    warn!(\"Found invalid address: {:x}\", address);\n                }\n            }\n        }\n    }\n\n    info!(\"Search done, found {} devices\", devices.len());\n\n    loop {\n        onewire.reset().await;\n        // Skip rom and trigger conversion, we can trigger all devices on the bus immediately\n        onewire.write_bytes(&[0xCC, 0x44]).await;\n\n        Timer::after_secs(1).await; // Allow 1s for the measurement to finish\n\n        // Read all devices one by one\n        for device in &devices {\n            onewire.reset().await;\n            onewire.write_bytes(&[0x55]).await; // Match rom\n            onewire.write_bytes(&device.to_le_bytes()).await;\n            onewire.write_bytes(&[0xBE]).await; // Read scratchpad\n\n            let mut data = [0; 9];\n            onewire.read_bytes(&mut data).await;\n            if crc8(&data) == 0 {\n                let temp = ((data[1] as i16) << 8 | data[0] as i16) as f32 / 16.;\n                info!(\"Read device {:x}: {} deg C\", device, temp);\n            } else {\n                warn!(\"Reading device {:x} failed\", device);\n            }\n        }\n        Timer::after_secs(1).await;\n    }\n}\n\nfn crc8(data: &[u8]) -> u8 {\n    let mut crc = 0;\n    for b in data {\n        let mut data_byte = *b;\n        for _ in 0..8 {\n            let temp = (crc ^ data_byte) & 0x01;\n            crc >>= 1;\n            if temp != 0 {\n                crc ^= 0x8C;\n            }\n            data_byte >>= 1;\n        }\n    }\n    crc\n}\n"
  },
  {
    "path": "examples/rp/src/bin/pio_onewire_parasite.rs",
    "content": "//! This example shows how you can use PIO to read one or more `DS18B20`\n//! one-wire temperature sensors using parasite power.\n//! It applies a strong pullup during conversion, see \"Powering the DS18B20\" in the datasheet.\n//! For externally powered sensors, use the pio_onewire.rs example.\n\n#![no_std]\n#![no_main]\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_rp::bind_interrupts;\nuse embassy_rp::peripherals::PIO0;\nuse embassy_rp::pio::{InterruptHandler, Pio};\nuse embassy_rp::pio_programs::onewire::{PioOneWire, PioOneWireProgram, PioOneWireSearch};\nuse embassy_time::Duration;\nuse heapless::Vec;\nuse {defmt_rtt as _, panic_probe as _};\n\nbind_interrupts!(struct Irqs {\n    PIO0_IRQ_0 => InterruptHandler<PIO0>;\n});\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let p = embassy_rp::init(Default::default());\n    let mut pio = Pio::new(p.PIO0, Irqs);\n\n    let prg = PioOneWireProgram::new(&mut pio.common);\n    let mut onewire = PioOneWire::new(&mut pio.common, pio.sm0, p.PIN_2, &prg);\n\n    info!(\"Starting onewire search\");\n\n    let mut devices = Vec::<u64, 10>::new();\n    let mut search = PioOneWireSearch::new();\n    for _ in 0..10 {\n        if !search.is_finished() {\n            if let Some(address) = search.next(&mut onewire).await {\n                if crc8(&address.to_le_bytes()) == 0 {\n                    info!(\"Found address: {:x}\", address);\n                    let _ = devices.push(address);\n                } else {\n                    warn!(\"Found invalid address: {:x}\", address);\n                }\n            }\n        }\n    }\n\n    info!(\"Search done, found {} devices\", devices.len());\n\n    loop {\n        // Read all devices one by one\n        for device in &devices {\n            onewire.reset().await;\n            onewire.write_bytes(&[0x55]).await; // Match rom\n            onewire.write_bytes(&device.to_le_bytes()).await;\n            // 750 ms delay required for default 12-bit resolution.\n            onewire.write_bytes_pullup(&[0x44], Duration::from_millis(750)).await;\n\n            onewire.reset().await;\n            onewire.write_bytes(&[0x55]).await; // Match rom\n            onewire.write_bytes(&device.to_le_bytes()).await;\n            onewire.write_bytes(&[0xBE]).await; // Read scratchpad\n\n            let mut data = [0; 9];\n            onewire.read_bytes(&mut data).await;\n            if crc8(&data) == 0 {\n                let temp = ((data[1] as i16) << 8 | data[0] as i16) as f32 / 16.;\n                info!(\"Read device {:x}: {} deg C\", device, temp);\n            } else {\n                warn!(\"Reading device {:x} failed. {:02x}\", device, data);\n            }\n        }\n    }\n}\n\nfn crc8(data: &[u8]) -> u8 {\n    let mut crc = 0;\n    for b in data {\n        let mut data_byte = *b;\n        for _ in 0..8 {\n            let temp = (crc ^ data_byte) & 0x01;\n            crc >>= 1;\n            if temp != 0 {\n                crc ^= 0x8C;\n            }\n            data_byte >>= 1;\n        }\n    }\n    crc\n}\n"
  },
  {
    "path": "examples/rp/src/bin/pio_pwm.rs",
    "content": "//! This example shows how to create a pwm using the PIO module in the RP2040 chip.\n\n#![no_std]\n#![no_main]\nuse core::time::Duration;\n\nuse embassy_executor::Spawner;\nuse embassy_rp::bind_interrupts;\nuse embassy_rp::peripherals::PIO0;\nuse embassy_rp::pio::{InterruptHandler, Pio};\nuse embassy_rp::pio_programs::pwm::{PioPwm, PioPwmProgram};\nuse embassy_time::Timer;\nuse {defmt_rtt as _, panic_probe as _};\n\nconst REFRESH_INTERVAL: u64 = 20000;\n\nbind_interrupts!(struct Irqs {\n    PIO0_IRQ_0 => InterruptHandler<PIO0>;\n});\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let p = embassy_rp::init(Default::default());\n    let Pio { mut common, sm0, .. } = Pio::new(p.PIO0, Irqs);\n\n    // Note that PIN_25 is the led pin on the Pico\n    let prg = PioPwmProgram::new(&mut common);\n    let mut pwm_pio = PioPwm::new(&mut common, sm0, p.PIN_25, &prg);\n    pwm_pio.set_period(Duration::from_micros(REFRESH_INTERVAL));\n    pwm_pio.start();\n\n    let mut duration = 0;\n    loop {\n        duration = (duration + 1) % 1000;\n        pwm_pio.write(Duration::from_micros(duration));\n        Timer::after_millis(1).await;\n    }\n}\n"
  },
  {
    "path": "examples/rp/src/bin/pio_rotary_encoder.rs",
    "content": "//! This example shows how to use the PIO module in the RP2040 to read a quadrature rotary encoder.\n\n#![no_std]\n#![no_main]\n\nuse defmt::info;\nuse embassy_executor::Spawner;\nuse embassy_rp::bind_interrupts;\nuse embassy_rp::peripherals::PIO0;\nuse embassy_rp::pio::{InterruptHandler, Pio};\nuse embassy_rp::pio_programs::rotary_encoder::{Direction, PioEncoder, PioEncoderProgram};\nuse {defmt_rtt as _, panic_probe as _};\n\nbind_interrupts!(struct Irqs {\n    PIO0_IRQ_0 => InterruptHandler<PIO0>;\n});\n\n#[embassy_executor::task]\nasync fn encoder_0(mut encoder: PioEncoder<'static, PIO0, 0>) {\n    let mut count = 0;\n    loop {\n        info!(\"Count: {}\", count);\n        count += match encoder.read().await {\n            Direction::Clockwise => 1,\n            Direction::CounterClockwise => -1,\n        };\n    }\n}\n\n#[embassy_executor::task]\nasync fn encoder_1(mut encoder: PioEncoder<'static, PIO0, 1>) {\n    let mut count = 0;\n    loop {\n        info!(\"Count: {}\", count);\n        count += match encoder.read().await {\n            Direction::Clockwise => 1,\n            Direction::CounterClockwise => -1,\n        };\n    }\n}\n\n#[embassy_executor::main]\nasync fn main(spawner: Spawner) {\n    let p = embassy_rp::init(Default::default());\n    let Pio {\n        mut common, sm0, sm1, ..\n    } = Pio::new(p.PIO0, Irqs);\n\n    let prg = PioEncoderProgram::new(&mut common);\n    let encoder0 = PioEncoder::new(&mut common, sm0, p.PIN_4, p.PIN_5, &prg);\n    let encoder1 = PioEncoder::new(&mut common, sm1, p.PIN_6, p.PIN_7, &prg);\n\n    spawner.spawn(encoder_0(encoder0).unwrap());\n    spawner.spawn(encoder_1(encoder1).unwrap());\n}\n"
  },
  {
    "path": "examples/rp/src/bin/pio_servo.rs",
    "content": "//! This example shows how to create a pwm using the PIO module in the RP2040 chip.\n\n#![no_std]\n#![no_main]\nuse core::time::Duration;\n\nuse embassy_executor::Spawner;\nuse embassy_rp::bind_interrupts;\nuse embassy_rp::peripherals::PIO0;\nuse embassy_rp::pio::{Instance, InterruptHandler, Pio};\nuse embassy_rp::pio_programs::pwm::{PioPwm, PioPwmProgram};\nuse embassy_time::Timer;\nuse {defmt_rtt as _, panic_probe as _};\n\nconst DEFAULT_MIN_PULSE_WIDTH: u64 = 1000; // uncalibrated default, the shortest duty cycle sent to a servo\nconst DEFAULT_MAX_PULSE_WIDTH: u64 = 2000; // uncalibrated default, the longest duty cycle sent to a servo\nconst DEFAULT_MAX_DEGREE_ROTATION: u64 = 160; // 160 degrees is typical\nconst REFRESH_INTERVAL: u64 = 20000; // The period of each cycle\n\nbind_interrupts!(struct Irqs {\n    PIO0_IRQ_0 => InterruptHandler<PIO0>;\n});\n\npub struct ServoBuilder<'d, T: Instance, const SM: usize> {\n    pwm: PioPwm<'d, T, SM>,\n    period: Duration,\n    min_pulse_width: Duration,\n    max_pulse_width: Duration,\n    max_degree_rotation: u64,\n}\n\nimpl<'d, T: Instance, const SM: usize> ServoBuilder<'d, T, SM> {\n    pub fn new(pwm: PioPwm<'d, T, SM>) -> Self {\n        Self {\n            pwm,\n            period: Duration::from_micros(REFRESH_INTERVAL),\n            min_pulse_width: Duration::from_micros(DEFAULT_MIN_PULSE_WIDTH),\n            max_pulse_width: Duration::from_micros(DEFAULT_MAX_PULSE_WIDTH),\n            max_degree_rotation: DEFAULT_MAX_DEGREE_ROTATION,\n        }\n    }\n\n    pub fn set_period(mut self, duration: Duration) -> Self {\n        self.period = duration;\n        self\n    }\n\n    pub fn set_min_pulse_width(mut self, duration: Duration) -> Self {\n        self.min_pulse_width = duration;\n        self\n    }\n\n    pub fn set_max_pulse_width(mut self, duration: Duration) -> Self {\n        self.max_pulse_width = duration;\n        self\n    }\n\n    pub fn set_max_degree_rotation(mut self, degree: u64) -> Self {\n        self.max_degree_rotation = degree;\n        self\n    }\n\n    pub fn build(mut self) -> Servo<'d, T, SM> {\n        self.pwm.set_period(self.period);\n        Servo {\n            pwm: self.pwm,\n            min_pulse_width: self.min_pulse_width,\n            max_pulse_width: self.max_pulse_width,\n            max_degree_rotation: self.max_degree_rotation,\n        }\n    }\n}\n\npub struct Servo<'d, T: Instance, const SM: usize> {\n    pwm: PioPwm<'d, T, SM>,\n    min_pulse_width: Duration,\n    max_pulse_width: Duration,\n    max_degree_rotation: u64,\n}\n\nimpl<'d, T: Instance, const SM: usize> Servo<'d, T, SM> {\n    pub fn start(&mut self) {\n        self.pwm.start();\n    }\n\n    pub fn stop(&mut self) {\n        self.pwm.stop();\n    }\n\n    pub fn write_time(&mut self, duration: Duration) {\n        self.pwm.write(duration);\n    }\n\n    pub fn rotate(&mut self, degree: u64) {\n        let degree_per_nano_second = (self.max_pulse_width.as_nanos() as u64 - self.min_pulse_width.as_nanos() as u64)\n            / self.max_degree_rotation;\n        let mut duration =\n            Duration::from_nanos(degree * degree_per_nano_second + self.min_pulse_width.as_nanos() as u64);\n        if self.max_pulse_width < duration {\n            duration = self.max_pulse_width;\n        }\n\n        self.pwm.write(duration);\n    }\n}\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let p = embassy_rp::init(Default::default());\n    let Pio { mut common, sm0, .. } = Pio::new(p.PIO0, Irqs);\n\n    let prg = PioPwmProgram::new(&mut common);\n    let pwm_pio = PioPwm::new(&mut common, sm0, p.PIN_1, &prg);\n    let mut servo = ServoBuilder::new(pwm_pio)\n        .set_max_degree_rotation(120) // Example of adjusting values for MG996R servo\n        .set_min_pulse_width(Duration::from_micros(350)) // This value was detemined by a rough experiment.\n        .set_max_pulse_width(Duration::from_micros(2600)) // Along with this value.\n        .build();\n\n    servo.start();\n\n    let mut degree = 0;\n    loop {\n        degree = (degree + 1) % 120;\n        servo.rotate(degree);\n        Timer::after_millis(50).await;\n    }\n}\n"
  },
  {
    "path": "examples/rp/src/bin/pio_spi.rs",
    "content": "//! This example shows how to use a PIO state machine as an additional SPI\n//! (Serial Peripheral Interface) on the RP2040 chip. No specific hardware is\n//! specified in this example.\n//!\n//! If you connect pin 6 and 7 you should get the same data back.\n\n#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_rp::peripherals::PIO0;\nuse embassy_rp::pio_programs::spi::Spi;\nuse embassy_rp::spi::Config;\nuse embassy_rp::{bind_interrupts, pio};\nuse embassy_time::Timer;\nuse {defmt_rtt as _, panic_probe as _};\n\nbind_interrupts!(struct Irqs {\n    PIO0_IRQ_0 => pio::InterruptHandler<PIO0>;\n});\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let p = embassy_rp::init(Default::default());\n    info!(\"Hello World!\");\n\n    // These pins are routed to different hardware SPI peripherals, but we can\n    // use them together regardless\n    let mosi = p.PIN_6; // SPI0 SCLK\n    let miso = p.PIN_7; // SPI0 MOSI\n    let clk = p.PIN_8; // SPI1 MISO\n\n    let pio::Pio { mut common, sm0, .. } = pio::Pio::new(p.PIO0, Irqs);\n\n    // Construct an SPI driver backed by a PIO state machine\n    let mut spi = Spi::new_blocking(&mut common, sm0, clk, mosi, miso, Config::default());\n\n    loop {\n        let tx_buf = [1_u8, 2, 3, 4, 5, 6];\n        let mut rx_buf = [0_u8; 6];\n\n        spi.blocking_transfer(&mut rx_buf, &tx_buf).unwrap();\n        info!(\"{:?}\", rx_buf);\n\n        Timer::after_secs(1).await;\n    }\n}\n"
  },
  {
    "path": "examples/rp/src/bin/pio_spi_async.rs",
    "content": "//! This example shows how to use a PIO state machine as an additional SPI\n//! (Serial Peripheral Interface) on the RP2040 chip. No specific hardware is\n//! specified in this example.\n//!\n//! If you connect pin 6 and 7 you should get the same data back.\n\n#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_rp::peripherals::{DMA_CH0, DMA_CH1, PIO0};\nuse embassy_rp::pio_programs::spi::Spi;\nuse embassy_rp::spi::Config;\nuse embassy_rp::{bind_interrupts, dma, pio};\nuse embassy_time::Timer;\nuse {defmt_rtt as _, panic_probe as _};\n\nbind_interrupts!(struct Irqs {\n    PIO0_IRQ_0 => pio::InterruptHandler<PIO0>;\n    DMA_IRQ_0 => dma::InterruptHandler<DMA_CH0>, dma::InterruptHandler<DMA_CH1>;\n});\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let p = embassy_rp::init(Default::default());\n    info!(\"Hello World!\");\n\n    // These pins are routed to different hardware SPI peripherals, but we can\n    // use them together regardless\n    let mosi = p.PIN_6; // SPI0 SCLK\n    let miso = p.PIN_7; // SPI0 MOSI\n    let clk = p.PIN_8; // SPI1 MISO\n\n    let pio::Pio { mut common, sm0, .. } = pio::Pio::new(p.PIO0, Irqs);\n\n    // Construct an SPI driver backed by a PIO state machine\n    let mut spi = Spi::new(\n        &mut common,\n        sm0,\n        clk,\n        mosi,\n        miso,\n        p.DMA_CH0,\n        p.DMA_CH1,\n        Irqs,\n        Config::default(),\n    );\n\n    loop {\n        let tx_buf = [1_u8, 2, 3, 4, 5, 6];\n        let mut rx_buf = [0_u8; 6];\n\n        spi.transfer(&mut rx_buf, &tx_buf).await.unwrap();\n        info!(\"{:?}\", rx_buf);\n\n        Timer::after_secs(1).await;\n    }\n}\n"
  },
  {
    "path": "examples/rp/src/bin/pio_stepper.rs",
    "content": "//! This example shows how to use the PIO module in the RP2040 to implement a stepper motor driver\n//! for a 5-wire stepper such as the 28BYJ-48. You can halt an ongoing rotation by dropping the future.\n\n#![no_std]\n#![no_main]\n\nuse defmt::info;\nuse embassy_executor::Spawner;\nuse embassy_rp::bind_interrupts;\nuse embassy_rp::peripherals::PIO0;\nuse embassy_rp::pio::{InterruptHandler, Pio};\nuse embassy_rp::pio_programs::stepper::{PioStepper, PioStepperProgram};\nuse embassy_time::{Duration, Timer, with_timeout};\nuse {defmt_rtt as _, panic_probe as _};\n\nbind_interrupts!(struct Irqs {\n    PIO0_IRQ_0 => InterruptHandler<PIO0>;\n});\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let p = embassy_rp::init(Default::default());\n    let Pio {\n        mut common, irq0, sm0, ..\n    } = Pio::new(p.PIO0, Irqs);\n\n    let prg = PioStepperProgram::new(&mut common);\n    let mut stepper = PioStepper::new(&mut common, sm0, irq0, p.PIN_4, p.PIN_5, p.PIN_6, p.PIN_7, &prg);\n    stepper.set_frequency(120);\n    loop {\n        info!(\"CW full steps\");\n        stepper.step(1000).await;\n\n        info!(\"CCW full steps, drop after 1 sec\");\n        if with_timeout(Duration::from_secs(1), stepper.step(-i32::MAX))\n            .await\n            .is_err()\n        {\n            info!(\"Time's up!\");\n            Timer::after(Duration::from_secs(1)).await;\n        }\n\n        info!(\"CW half steps\");\n        stepper.step_half(1000).await;\n\n        info!(\"CCW half steps\");\n        stepper.step_half(-1000).await;\n    }\n}\n"
  },
  {
    "path": "examples/rp/src/bin/pio_uart.rs",
    "content": "//! This example shows how to use the PIO module in the RP2040 chip to implement a duplex UART.\n//! The PIO module is a very powerful peripheral that can be used to implement many different\n//! protocols. It is a very flexible state machine that can be programmed to do almost anything.\n//!\n//! This example opens up a USB device that implements a CDC ACM serial port. It then uses the\n//! PIO module to implement a UART that is connected to the USB serial port. This allows you to\n//! communicate with a device connected to the RP2040 over USB serial.\n\n#![no_std]\n#![no_main]\n#![allow(async_fn_in_trait)]\n\nuse defmt::{info, panic, trace};\nuse embassy_executor::Spawner;\nuse embassy_futures::join::{join, join3};\nuse embassy_rp::peripherals::{PIO0, USB};\nuse embassy_rp::pio_programs::uart::{PioUartRx, PioUartRxProgram, PioUartTx, PioUartTxProgram};\nuse embassy_rp::usb::{Driver, Instance, InterruptHandler};\nuse embassy_rp::{bind_interrupts, pio};\nuse embassy_sync::blocking_mutex::raw::NoopRawMutex;\nuse embassy_sync::pipe::Pipe;\nuse embassy_usb::class::cdc_acm::{CdcAcmClass, Receiver, Sender, State};\nuse embassy_usb::driver::EndpointError;\nuse embassy_usb::{Builder, Config};\nuse embedded_io_async::{Read, Write};\nuse {defmt_rtt as _, panic_probe as _};\n\n//use crate::uart::PioUart;\n\nbind_interrupts!(struct Irqs {\n    USBCTRL_IRQ => InterruptHandler<USB>;\n    PIO0_IRQ_0 => pio::InterruptHandler<PIO0>;\n});\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    info!(\"Hello there!\");\n\n    let p = embassy_rp::init(Default::default());\n\n    // Create the driver, from the HAL.\n    let driver = Driver::new(p.USB, Irqs);\n\n    // Create embassy-usb Config\n    let mut config = Config::new(0xc0de, 0xcafe);\n    config.manufacturer = Some(\"Embassy\");\n    config.product = Some(\"PIO UART example\");\n    config.serial_number = Some(\"12345678\");\n    config.max_power = 100;\n    config.max_packet_size_0 = 64;\n\n    // Create embassy-usb DeviceBuilder using the driver and config.\n    // It needs some buffers for building the descriptors.\n    let mut config_descriptor = [0; 256];\n    let mut bos_descriptor = [0; 256];\n    let mut control_buf = [0; 64];\n\n    let mut state = State::new();\n\n    let mut builder = Builder::new(\n        driver,\n        config,\n        &mut config_descriptor,\n        &mut bos_descriptor,\n        &mut [], // no msos descriptors\n        &mut control_buf,\n    );\n\n    // Create classes on the builder.\n    let class = CdcAcmClass::new(&mut builder, &mut state, 64);\n\n    // Build the builder.\n    let mut usb = builder.build();\n\n    // Run the USB device.\n    let usb_fut = usb.run();\n\n    // PIO UART setup\n    let pio::Pio {\n        mut common, sm0, sm1, ..\n    } = pio::Pio::new(p.PIO0, Irqs);\n\n    let tx_program = PioUartTxProgram::new(&mut common);\n    let mut uart_tx = PioUartTx::new(9600, &mut common, sm0, p.PIN_4, &tx_program);\n\n    let rx_program = PioUartRxProgram::new(&mut common);\n    let mut uart_rx = PioUartRx::new(9600, &mut common, sm1, p.PIN_5, &rx_program);\n\n    // Pipe setup\n    let mut usb_pipe: Pipe<NoopRawMutex, 20> = Pipe::new();\n    let (mut usb_pipe_reader, mut usb_pipe_writer) = usb_pipe.split();\n\n    let mut uart_pipe: Pipe<NoopRawMutex, 20> = Pipe::new();\n    let (mut uart_pipe_reader, mut uart_pipe_writer) = uart_pipe.split();\n\n    let (mut usb_tx, mut usb_rx) = class.split();\n\n    // Read + write from USB\n    let usb_future = async {\n        loop {\n            info!(\"Wait for USB connection\");\n            usb_rx.wait_connection().await;\n            info!(\"Connected\");\n            let _ = join(\n                usb_read(&mut usb_rx, &mut uart_pipe_writer),\n                usb_write(&mut usb_tx, &mut usb_pipe_reader),\n            )\n            .await;\n            info!(\"Disconnected\");\n        }\n    };\n\n    // Read + write from UART\n    let uart_future = join(\n        uart_read(&mut uart_rx, &mut usb_pipe_writer),\n        uart_write(&mut uart_tx, &mut uart_pipe_reader),\n    );\n\n    // Run everything concurrently.\n    // If we had made everything `'static` above instead, we could do this using separate tasks instead.\n    join3(usb_fut, usb_future, uart_future).await;\n}\n\nstruct Disconnected {}\n\nimpl From<EndpointError> for Disconnected {\n    fn from(val: EndpointError) -> Self {\n        match val {\n            EndpointError::BufferOverflow => panic!(\"Buffer overflow\"),\n            EndpointError::Disabled => Disconnected {},\n        }\n    }\n}\n\n/// Read from the USB and write it to the UART TX pipe\nasync fn usb_read<'d, T: Instance + 'd>(\n    usb_rx: &mut Receiver<'d, Driver<'d, T>>,\n    uart_pipe_writer: &mut embassy_sync::pipe::Writer<'_, NoopRawMutex, 20>,\n) -> Result<(), Disconnected> {\n    let mut buf = [0; 64];\n    loop {\n        let n = usb_rx.read_packet(&mut buf).await?;\n        let data = &buf[..n];\n        trace!(\"USB IN: {:x}\", data);\n        (*uart_pipe_writer).write(data).await;\n    }\n}\n\n/// Read from the USB TX pipe and write it to the USB\nasync fn usb_write<'d, T: Instance + 'd>(\n    usb_tx: &mut Sender<'d, Driver<'d, T>>,\n    usb_pipe_reader: &mut embassy_sync::pipe::Reader<'_, NoopRawMutex, 20>,\n) -> Result<(), Disconnected> {\n    let mut buf = [0; 64];\n    loop {\n        let n = (*usb_pipe_reader).read(&mut buf).await;\n        let data = &buf[..n];\n        trace!(\"USB OUT: {:x}\", data);\n        usb_tx.write_packet(&data).await?;\n    }\n}\n\n/// Read from the UART and write it to the USB TX pipe\nasync fn uart_read<PIO: pio::Instance, const SM: usize>(\n    uart_rx: &mut PioUartRx<'_, PIO, SM>,\n    usb_pipe_writer: &mut embassy_sync::pipe::Writer<'_, NoopRawMutex, 20>,\n) -> ! {\n    let mut buf = [0; 64];\n    loop {\n        let n = uart_rx.read(&mut buf).await.expect(\"UART read error\");\n        if n == 0 {\n            continue;\n        }\n        let data = &buf[..n];\n        trace!(\"UART IN: {:x}\", buf);\n        (*usb_pipe_writer).write(data).await;\n    }\n}\n\n/// Read from the UART TX pipe and write it to the UART\nasync fn uart_write<PIO: pio::Instance, const SM: usize>(\n    uart_tx: &mut PioUartTx<'_, PIO, SM>,\n    uart_pipe_reader: &mut embassy_sync::pipe::Reader<'_, NoopRawMutex, 20>,\n) -> ! {\n    let mut buf = [0; 64];\n    loop {\n        let n = (*uart_pipe_reader).read(&mut buf).await;\n        let data = &buf[..n];\n        trace!(\"UART OUT: {:x}\", data);\n        let _ = uart_tx.write(&data).await;\n    }\n}\n"
  },
  {
    "path": "examples/rp/src/bin/pio_ws2812.rs",
    "content": "//! This example shows powerful PIO module in the RP2040 chip to communicate with WS2812 LED modules.\n//! See (https://www.sparkfun.com/categories/tags/ws2812)\n\n#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_rp::bind_interrupts;\nuse embassy_rp::peripherals::{DMA_CH0, PIO0};\nuse embassy_rp::pio::{InterruptHandler, Pio};\nuse embassy_rp::pio_programs::ws2812::{PioWs2812, PioWs2812Program};\nuse embassy_time::{Duration, Ticker};\nuse smart_leds::RGB8;\nuse {defmt_rtt as _, panic_probe as _};\n\nbind_interrupts!(struct Irqs {\n    PIO0_IRQ_0 => InterruptHandler<PIO0>;\n    DMA_IRQ_0 => embassy_rp::dma::InterruptHandler<DMA_CH0>;\n});\n\n/// Input a value 0 to 255 to get a color value\n/// The colours are a transition r - g - b - back to r.\nfn wheel(mut wheel_pos: u8) -> RGB8 {\n    wheel_pos = 255 - wheel_pos;\n    if wheel_pos < 85 {\n        return (255 - wheel_pos * 3, 0, wheel_pos * 3).into();\n    }\n    if wheel_pos < 170 {\n        wheel_pos -= 85;\n        return (0, wheel_pos * 3, 255 - wheel_pos * 3).into();\n    }\n    wheel_pos -= 170;\n    (wheel_pos * 3, 255 - wheel_pos * 3, 0).into()\n}\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    info!(\"Start\");\n    let p = embassy_rp::init(Default::default());\n\n    let Pio { mut common, sm0, .. } = Pio::new(p.PIO0, Irqs);\n\n    // This is the number of leds in the string. Helpfully, the sparkfun thing plus and adafruit\n    // feather boards for the 2040 both have one built in.\n    const NUM_LEDS: usize = 1;\n    let mut data = [RGB8::default(); NUM_LEDS];\n\n    // Common neopixel pins:\n    // Thing plus: 8\n    // Adafruit Feather: 16;  Adafruit Feather+RFM95: 4\n    let program = PioWs2812Program::new(&mut common);\n    let mut ws2812 = PioWs2812::new(&mut common, sm0, p.DMA_CH0, Irqs, p.PIN_16, &program);\n\n    // Loop forever making RGB values and pushing them out to the WS2812.\n    let mut ticker = Ticker::every(Duration::from_millis(10));\n    loop {\n        for j in 0..(256 * 5) {\n            debug!(\"New Colors:\");\n            for i in 0..NUM_LEDS {\n                data[i] = wheel((((i * 256) as u16 / NUM_LEDS as u16 + j as u16) & 255) as u8);\n                debug!(\"R: {} G: {} B: {}\", data[i].r, data[i].g, data[i].b);\n            }\n            ws2812.write(&data).await;\n\n            ticker.next().await;\n        }\n    }\n}\n"
  },
  {
    "path": "examples/rp/src/bin/pwm.rs",
    "content": "//! This example shows how to use PWM (Pulse Width Modulation) in the RP2040 chip.\n//!\n//! We demonstrate two ways of using PWM:\n//! 1. Via config\n//! 2. Via setting a duty cycle\n\n#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_rp::Peri;\nuse embassy_rp::peripherals::{PIN_4, PIN_25, PWM_SLICE2, PWM_SLICE4};\nuse embassy_rp::pwm::{Config, Pwm, SetDutyCycle};\nuse embassy_time::Timer;\nuse {defmt_rtt as _, panic_probe as _};\n\n#[embassy_executor::main]\nasync fn main(spawner: Spawner) {\n    let p = embassy_rp::init(Default::default());\n    spawner.spawn(pwm_set_config(p.PWM_SLICE4, p.PIN_25).unwrap());\n    spawner.spawn(pwm_set_dutycycle(p.PWM_SLICE2, p.PIN_4).unwrap());\n}\n\n/// Demonstrate PWM by modifying & applying the config\n///\n/// Using the onboard led, if You are using a different Board than plain Pico2 (i.e. W variant)\n/// you must use another slice & pin and an appropriate resistor.\n#[embassy_executor::task]\nasync fn pwm_set_config(slice4: Peri<'static, PWM_SLICE4>, pin25: Peri<'static, PIN_25>) {\n    let mut c = Config::default();\n    c.top = 32_768;\n    c.compare_b = 8;\n    let mut pwm = Pwm::new_output_b(slice4, pin25, c.clone());\n\n    loop {\n        info!(\"current LED duty cycle: {}/32768\", c.compare_b);\n        Timer::after_secs(1).await;\n        c.compare_b = c.compare_b.rotate_left(4);\n        pwm.set_config(&c);\n    }\n}\n\n/// Demonstrate PWM by setting duty cycle\n///\n/// Using GP4 in Slice2, make sure to use an appropriate resistor.\n#[embassy_executor::task]\nasync fn pwm_set_dutycycle(slice2: Peri<'static, PWM_SLICE2>, pin4: Peri<'static, PIN_4>) {\n    // If we aim for a specific frequency, here is how we can calculate the top value.\n    // The top value sets the period of the PWM cycle, so a counter goes from 0 to top and then wraps around to 0.\n    // Every such wraparound is one PWM cycle. So here is how we get 25KHz:\n    let desired_freq_hz = 25_000;\n    let clock_freq_hz = embassy_rp::clocks::clk_sys_freq();\n    let divider = 16u8;\n    let period = (clock_freq_hz / (desired_freq_hz * divider as u32)) as u16 - 1;\n\n    let mut c = Config::default();\n    c.top = period;\n    c.divider = divider.into();\n\n    let mut pwm = Pwm::new_output_a(slice2, pin4, c.clone());\n\n    loop {\n        // 100% duty cycle, fully on\n        pwm.set_duty_cycle_fully_on().unwrap();\n        Timer::after_secs(1).await;\n\n        // 66% duty cycle. Expressed as simple percentage.\n        pwm.set_duty_cycle_percent(66).unwrap();\n        Timer::after_secs(1).await;\n\n        // 25% duty cycle. Expressed as 32768/4 = 8192.\n        pwm.set_duty_cycle(c.top / 4).unwrap();\n        Timer::after_secs(1).await;\n\n        // 0% duty cycle, fully off.\n        pwm.set_duty_cycle_fully_off().unwrap();\n        Timer::after_secs(1).await;\n    }\n}\n"
  },
  {
    "path": "examples/rp/src/bin/pwm_input.rs",
    "content": "//! This example shows how to use the PWM module to measure the frequency of an input signal.\n\n#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_rp::gpio::Pull;\nuse embassy_rp::pwm::{Config, InputMode, Pwm};\nuse embassy_time::{Duration, Ticker};\nuse {defmt_rtt as _, panic_probe as _};\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let p = embassy_rp::init(Default::default());\n\n    let cfg: Config = Default::default();\n    let pwm = Pwm::new_input(p.PWM_SLICE2, p.PIN_5, Pull::None, InputMode::RisingEdge, cfg);\n\n    let mut ticker = Ticker::every(Duration::from_secs(1));\n    loop {\n        info!(\"Input frequency: {} Hz\", pwm.counter());\n        pwm.set_counter(0);\n        ticker.next().await;\n    }\n}\n"
  },
  {
    "path": "examples/rp/src/bin/rosc.rs",
    "content": "//! This example test the RP Pico on board LED.\n//!\n//! It does not work with the RP Pico W board. See wifi_blinky.rs.\n\n#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_rp::{clocks, gpio};\nuse embassy_time::Timer;\nuse gpio::{Level, Output};\nuse {defmt_rtt as _, panic_probe as _};\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let mut config = embassy_rp::config::Config::default();\n    config.clocks = clocks::ClockConfig::rosc();\n    let p = embassy_rp::init(config);\n    let mut led = Output::new(p.PIN_25, Level::Low);\n\n    loop {\n        info!(\"led on!\");\n        led.set_high();\n        Timer::after_secs(1).await;\n\n        info!(\"led off!\");\n        led.set_low();\n        Timer::after_secs(1).await;\n    }\n}\n"
  },
  {
    "path": "examples/rp/src/bin/rtc.rs",
    "content": "//! This example shows how to use RTC (Real Time Clock) in the RP2040 chip.\n\n#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_rp::bind_interrupts;\nuse embassy_rp::rtc::{DateTime, DayOfWeek, Rtc};\nuse embassy_time::Timer;\nuse {defmt_rtt as _, panic_probe as _};\n\n// Bind the RTC interrupt to the handler\nbind_interrupts!(struct Irqs {\n    RTC_IRQ => embassy_rp::rtc::InterruptHandler;\n});\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let p = embassy_rp::init(Default::default());\n    info!(\"Wait for 20s\");\n\n    let mut rtc = Rtc::new(p.RTC, Irqs);\n\n    if !rtc.is_running() {\n        info!(\"Start RTC\");\n        let now = DateTime {\n            year: 2000,\n            month: 1,\n            day: 1,\n            day_of_week: DayOfWeek::Saturday,\n            hour: 0,\n            minute: 0,\n            second: 0,\n        };\n        rtc.set_datetime(now).unwrap();\n    }\n\n    Timer::after_millis(20000).await;\n\n    if let Ok(dt) = rtc.now() {\n        info!(\n            \"Now: {}-{:02}-{:02} {}:{:02}:{:02}\",\n            dt.year, dt.month, dt.day, dt.hour, dt.minute, dt.second,\n        );\n    }\n\n    info!(\"Reboot.\");\n    Timer::after_millis(200).await;\n    cortex_m::peripheral::SCB::sys_reset();\n}\n"
  },
  {
    "path": "examples/rp/src/bin/rtc_alarm.rs",
    "content": "//! This example shows how to use RTC (Real Time Clock) for scheduling alarms and reacting to them.\n\n#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_futures::select::{Either, select};\nuse embassy_rp::bind_interrupts;\nuse embassy_rp::rtc::{DateTime, DateTimeFilter, DayOfWeek, Rtc};\nuse embassy_time::Timer;\nuse {defmt_rtt as _, panic_probe as _};\n\n// Bind the RTC interrupt to the handler\nbind_interrupts!(struct Irqs {\n    RTC_IRQ => embassy_rp::rtc::InterruptHandler;\n});\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let p = embassy_rp::init(Default::default());\n    let mut rtc = Rtc::new(p.RTC, Irqs);\n\n    if !rtc.is_running() {\n        info!(\"Start RTC\");\n        let now = DateTime {\n            year: 2000,\n            month: 1,\n            day: 1,\n            day_of_week: DayOfWeek::Saturday,\n            hour: 0,\n            minute: 0,\n            second: 0,\n        };\n        rtc.set_datetime(now).unwrap();\n    }\n\n    loop {\n        // Wait for 5 seconds or until the alarm is triggered\n        match select(Timer::after_secs(5), rtc.wait_for_alarm()).await {\n            // Timer expired\n            Either::First(_) => {\n                let dt = rtc.now().unwrap();\n                info!(\n                    \"Now: {}-{:02}-{:02} {}:{:02}:{:02}\",\n                    dt.year, dt.month, dt.day, dt.hour, dt.minute, dt.second,\n                );\n\n                // See if the alarm is already scheduled, if not, schedule it\n                if rtc.alarm_scheduled().is_none() {\n                    info!(\"Scheduling alarm for 30 seconds from now\");\n                    rtc.schedule_alarm(DateTimeFilter::default().second((dt.second + 30) % 60));\n                    info!(\"Alarm scheduled: {}\", rtc.alarm_scheduled().unwrap());\n                }\n            }\n            // Alarm triggered\n            Either::Second(_) => {\n                let dt = rtc.now().unwrap();\n                info!(\n                    \"ALARM TRIGGERED! Now: {}-{:02}-{:02} {}:{:02}:{:02}\",\n                    dt.year, dt.month, dt.day, dt.hour, dt.minute, dt.second,\n                );\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "examples/rp/src/bin/shared_bus.rs",
    "content": "//! This example shows how to share (async) I2C and SPI buses between multiple devices.\n\n#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_embedded_hal::shared_bus::asynch::i2c::I2cDevice;\nuse embassy_embedded_hal::shared_bus::asynch::spi::SpiDevice;\nuse embassy_executor::Spawner;\nuse embassy_rp::gpio::{Level, Output};\nuse embassy_rp::i2c::{self, I2c, InterruptHandler};\nuse embassy_rp::peripherals::{DMA_CH0, DMA_CH1, I2C1, SPI1};\nuse embassy_rp::spi::{self, Spi};\nuse embassy_rp::{bind_interrupts, dma};\nuse embassy_sync::blocking_mutex::raw::NoopRawMutex;\nuse embassy_sync::mutex::Mutex;\nuse embassy_time::Timer;\nuse static_cell::StaticCell;\nuse {defmt_rtt as _, panic_probe as _};\n\ntype Spi1Bus = Mutex<NoopRawMutex, Spi<'static, SPI1, spi::Async>>;\ntype I2c1Bus = Mutex<NoopRawMutex, I2c<'static, I2C1, i2c::Async>>;\n\nbind_interrupts!(struct Irqs {\n    I2C1_IRQ => InterruptHandler<I2C1>;\n    DMA_IRQ_0 => dma::InterruptHandler<DMA_CH0>, dma::InterruptHandler<DMA_CH1>;\n});\n\n#[embassy_executor::main]\nasync fn main(spawner: Spawner) {\n    let p = embassy_rp::init(Default::default());\n    info!(\"Here we go!\");\n\n    // Shared I2C bus\n    let i2c = I2c::new_async(p.I2C1, p.PIN_15, p.PIN_14, Irqs, i2c::Config::default());\n    static I2C_BUS: StaticCell<I2c1Bus> = StaticCell::new();\n    let i2c_bus = I2C_BUS.init(Mutex::new(i2c));\n\n    spawner.spawn(i2c_task_a(i2c_bus).unwrap());\n    spawner.spawn(i2c_task_b(i2c_bus).unwrap());\n\n    // Shared SPI bus\n    let spi_cfg = spi::Config::default();\n    let spi = Spi::new(\n        p.SPI1, p.PIN_10, p.PIN_11, p.PIN_12, p.DMA_CH0, p.DMA_CH1, Irqs, spi_cfg,\n    );\n    static SPI_BUS: StaticCell<Spi1Bus> = StaticCell::new();\n    let spi_bus = SPI_BUS.init(Mutex::new(spi));\n\n    // Chip select pins for the SPI devices\n    let cs_a = Output::new(p.PIN_0, Level::High);\n    let cs_b = Output::new(p.PIN_1, Level::High);\n\n    spawner.spawn(spi_task_a(spi_bus, cs_a).unwrap());\n    spawner.spawn(spi_task_b(spi_bus, cs_b).unwrap());\n}\n\n#[embassy_executor::task]\nasync fn i2c_task_a(i2c_bus: &'static I2c1Bus) {\n    let i2c_dev = I2cDevice::new(i2c_bus);\n    let _sensor = DummyI2cDeviceDriver::new(i2c_dev, 0xC0);\n    loop {\n        info!(\"i2c task A\");\n        Timer::after_secs(1).await;\n    }\n}\n\n#[embassy_executor::task]\nasync fn i2c_task_b(i2c_bus: &'static I2c1Bus) {\n    let i2c_dev = I2cDevice::new(i2c_bus);\n    let _sensor = DummyI2cDeviceDriver::new(i2c_dev, 0xDE);\n    loop {\n        info!(\"i2c task B\");\n        Timer::after_secs(1).await;\n    }\n}\n\n#[embassy_executor::task]\nasync fn spi_task_a(spi_bus: &'static Spi1Bus, cs: Output<'static>) {\n    let spi_dev = SpiDevice::new(spi_bus, cs);\n    let _sensor = DummySpiDeviceDriver::new(spi_dev);\n    loop {\n        info!(\"spi task A\");\n        Timer::after_secs(1).await;\n    }\n}\n\n#[embassy_executor::task]\nasync fn spi_task_b(spi_bus: &'static Spi1Bus, cs: Output<'static>) {\n    let spi_dev = SpiDevice::new(spi_bus, cs);\n    let _sensor = DummySpiDeviceDriver::new(spi_dev);\n    loop {\n        info!(\"spi task B\");\n        Timer::after_secs(1).await;\n    }\n}\n\n// Dummy I2C device driver, using `embedded-hal-async`\nstruct DummyI2cDeviceDriver<I2C: embedded_hal_async::i2c::I2c> {\n    _i2c: I2C,\n}\n\nimpl<I2C: embedded_hal_async::i2c::I2c> DummyI2cDeviceDriver<I2C> {\n    fn new(i2c_dev: I2C, _address: u8) -> Self {\n        Self { _i2c: i2c_dev }\n    }\n}\n\n// Dummy SPI device driver, using `embedded-hal-async`\nstruct DummySpiDeviceDriver<SPI: embedded_hal_async::spi::SpiDevice> {\n    _spi: SPI,\n}\n\nimpl<SPI: embedded_hal_async::spi::SpiDevice> DummySpiDeviceDriver<SPI> {\n    fn new(spi_dev: SPI) -> Self {\n        Self { _spi: spi_dev }\n    }\n}\n"
  },
  {
    "path": "examples/rp/src/bin/sharing.rs",
    "content": "//! This example shows some common strategies for sharing resources between tasks.\n//!\n//! We demonstrate five different ways of sharing, covering different use cases:\n//! - Atomics: This method is used for simple values, such as bool and u8..u32\n//! - Blocking Mutex: This is used for sharing non-async things, using Cell/RefCell for interior mutability.\n//! - Async Mutex: This is used for sharing async resources, where you need to hold the lock across await points.\n//!   The async Mutex has interior mutability built-in, so no RefCell is needed.\n//! - Cell: For sharing Copy types between tasks running on the same executor.\n//! - RefCell: When you want &mut access to a value shared between tasks running on the same executor.\n//!\n//! More information: https://embassy.dev/book/#_sharing_peripherals_between_tasks\n\n#![no_std]\n#![no_main]\n\nuse core::cell::{Cell, RefCell};\nuse core::sync::atomic::{AtomicU32, Ordering};\n\nuse cortex_m_rt::entry;\nuse defmt::info;\nuse embassy_executor::{Executor, InterruptExecutor};\nuse embassy_rp::clocks::RoscRng;\nuse embassy_rp::interrupt::{InterruptExt, Priority};\nuse embassy_rp::peripherals::{DMA_CH0, UART0};\nuse embassy_rp::uart::{self, InterruptHandler, UartTx};\nuse embassy_rp::{bind_interrupts, dma, interrupt};\nuse embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;\nuse embassy_sync::{blocking_mutex, mutex};\nuse embassy_time::{Duration, Ticker};\nuse static_cell::{ConstStaticCell, StaticCell};\nuse {defmt_rtt as _, panic_probe as _};\n\ntype UartAsyncMutex = mutex::Mutex<CriticalSectionRawMutex, UartTx<'static, uart::Async>>;\n\nstruct MyType {\n    inner: u32,\n}\n\nstatic EXECUTOR_HI: InterruptExecutor = InterruptExecutor::new();\nstatic EXECUTOR_LOW: StaticCell<Executor> = StaticCell::new();\n\n// Use Atomics for simple values\nstatic ATOMIC: AtomicU32 = AtomicU32::new(0);\n\n// Use blocking Mutex with Cell/RefCell for sharing non-async things\nstatic MUTEX_BLOCKING: blocking_mutex::Mutex<CriticalSectionRawMutex, RefCell<MyType>> =\n    blocking_mutex::Mutex::new(RefCell::new(MyType { inner: 0 }));\n\nbind_interrupts!(struct Irqs {\n    UART0_IRQ => InterruptHandler<UART0>;\n    DMA_IRQ_0 => dma::InterruptHandler<DMA_CH0>;\n});\n\n#[interrupt]\nunsafe fn SWI_IRQ_0() {\n    unsafe { EXECUTOR_HI.on_interrupt() }\n}\n\n#[entry]\nfn main() -> ! {\n    let p = embassy_rp::init(Default::default());\n    info!(\"Here we go!\");\n\n    let uart = UartTx::new(p.UART0, p.PIN_0, p.DMA_CH0, Irqs, uart::Config::default());\n    // Use the async Mutex for sharing async things (built-in interior mutability)\n    static UART: StaticCell<UartAsyncMutex> = StaticCell::new();\n    let uart = UART.init(mutex::Mutex::new(uart));\n\n    // High-priority executor: runs in interrupt mode\n    interrupt::SWI_IRQ_0.set_priority(Priority::P3);\n    let spawner = EXECUTOR_HI.start(interrupt::SWI_IRQ_0);\n    spawner.spawn(task_a(uart).unwrap());\n\n    // Low priority executor: runs in thread mode\n    let executor = EXECUTOR_LOW.init(Executor::new());\n    executor.run(|spawner| {\n        // No Mutex needed when sharing between tasks running on the same executor\n\n        // Use Cell for Copy-types\n        static CELL: ConstStaticCell<Cell<[u8; 4]>> = ConstStaticCell::new(Cell::new([0; 4]));\n        let cell = CELL.take();\n\n        // Use RefCell for &mut access\n        static REF_CELL: ConstStaticCell<RefCell<MyType>> = ConstStaticCell::new(RefCell::new(MyType { inner: 0 }));\n        let ref_cell = REF_CELL.take();\n\n        spawner.spawn(task_b(uart, cell, ref_cell).unwrap());\n        spawner.spawn(task_c(cell, ref_cell).unwrap());\n    });\n}\n\n#[embassy_executor::task]\nasync fn task_a(uart: &'static UartAsyncMutex) {\n    let mut ticker = Ticker::every(Duration::from_secs(1));\n    loop {\n        let random = RoscRng.next_u32();\n\n        {\n            let mut uart = uart.lock().await;\n            uart.write(b\"task a\").await.unwrap();\n            // The uart lock is released when it goes out of scope\n        }\n\n        ATOMIC.store(random, Ordering::Relaxed);\n\n        MUTEX_BLOCKING.lock(|x| x.borrow_mut().inner = random);\n\n        ticker.next().await;\n    }\n}\n\n#[embassy_executor::task]\nasync fn task_b(uart: &'static UartAsyncMutex, cell: &'static Cell<[u8; 4]>, ref_cell: &'static RefCell<MyType>) {\n    let mut ticker = Ticker::every(Duration::from_secs(1));\n    loop {\n        let random = RoscRng.next_u32();\n\n        uart.lock().await.write(b\"task b\").await.unwrap();\n\n        cell.set(random.to_be_bytes());\n\n        ref_cell.borrow_mut().inner = random;\n\n        ticker.next().await;\n    }\n}\n\n#[embassy_executor::task]\nasync fn task_c(cell: &'static Cell<[u8; 4]>, ref_cell: &'static RefCell<MyType>) {\n    let mut ticker = Ticker::every(Duration::from_secs(1));\n    loop {\n        info!(\"=======================\");\n\n        let atomic_val = ATOMIC.load(Ordering::Relaxed);\n        info!(\"atomic: {}\", atomic_val);\n\n        MUTEX_BLOCKING.lock(|x| {\n            let val = x.borrow().inner;\n            info!(\"blocking mutex: {}\", val);\n        });\n\n        let cell_val = cell.get();\n        info!(\"cell: {:?}\", cell_val);\n\n        let ref_cell_val = ref_cell.borrow().inner;\n        info!(\"ref_cell: {:?}\", ref_cell_val);\n\n        ticker.next().await;\n    }\n}\n"
  },
  {
    "path": "examples/rp/src/bin/spi.rs",
    "content": "//! This example shows how to use SPI (Serial Peripheral Interface) in the RP2040 chip.\n//!\n//! Example for resistive touch sensor in Waveshare Pico-ResTouch\n\n#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_rp::spi::Spi;\nuse embassy_rp::{gpio, spi};\nuse gpio::{Level, Output};\nuse {defmt_rtt as _, panic_probe as _};\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let p = embassy_rp::init(Default::default());\n    info!(\"Hello World!\");\n\n    // Example for resistive touch sensor in Waveshare Pico-ResTouch\n\n    let miso = p.PIN_12;\n    let mosi = p.PIN_11;\n    let clk = p.PIN_10;\n    let touch_cs = p.PIN_16;\n\n    // create SPI\n    let mut config = spi::Config::default();\n    config.frequency = 2_000_000;\n    let mut spi = Spi::new_blocking(p.SPI1, clk, mosi, miso, config);\n\n    // Configure CS\n    let mut cs = Output::new(touch_cs, Level::Low);\n\n    loop {\n        cs.set_low();\n        let mut buf = [0x90, 0x00, 0x00, 0xd0, 0x00, 0x00];\n        spi.blocking_transfer_in_place(&mut buf).unwrap();\n        cs.set_high();\n\n        let x = (buf[1] as u32) << 5 | (buf[2] as u32) >> 3;\n        let y = (buf[4] as u32) << 5 | (buf[5] as u32) >> 3;\n\n        info!(\"touch: {=u32} {=u32}\", x, y);\n    }\n}\n"
  },
  {
    "path": "examples/rp/src/bin/spi_async.rs",
    "content": "//! This example shows how to use SPI (Serial Peripheral Interface) in the RP2040 chip.\n//! No specific hardware is specified in this example. If you connect pin 11 and 12 you should get the same data back.\n\n#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_rp::peripherals::{DMA_CH0, DMA_CH1};\nuse embassy_rp::spi::{Config, Spi};\nuse embassy_rp::{bind_interrupts, dma};\nuse embassy_time::Timer;\nuse {defmt_rtt as _, panic_probe as _};\n\nbind_interrupts!(struct Irqs {\n    DMA_IRQ_0 => dma::InterruptHandler<DMA_CH0>, dma::InterruptHandler<DMA_CH1>;\n});\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let p = embassy_rp::init(Default::default());\n    info!(\"Hello World!\");\n\n    let miso = p.PIN_12;\n    let mosi = p.PIN_11;\n    let clk = p.PIN_10;\n\n    let mut spi = Spi::new(p.SPI1, clk, mosi, miso, p.DMA_CH0, p.DMA_CH1, Irqs, Config::default());\n\n    loop {\n        let tx_buf = [1_u8, 2, 3, 4, 5, 6];\n        let mut rx_buf = [0_u8; 6];\n        spi.transfer(&mut rx_buf, &tx_buf).await.unwrap();\n        info!(\"{:?}\", rx_buf);\n        Timer::after_secs(1).await;\n    }\n}\n"
  },
  {
    "path": "examples/rp/src/bin/spi_display.rs",
    "content": "//! This example shows how to use SPI (Serial Peripheral Interface) in the RP2040 chip.\n//!\n//! Example written for a display using the ST7789 chip. Possibly the Waveshare Pico-ResTouch\n//! (https://www.waveshare.com/wiki/Pico-ResTouch-LCD-2.8)\n\n#![no_std]\n#![no_main]\n\nuse core::cell::RefCell;\n\nuse defmt::*;\nuse display_interface_spi::SPIInterface;\nuse embassy_embedded_hal::shared_bus::blocking::spi::SpiDeviceWithConfig;\nuse embassy_executor::Spawner;\nuse embassy_rp::gpio::{Level, Output};\nuse embassy_rp::spi;\nuse embassy_rp::spi::Spi;\nuse embassy_sync::blocking_mutex::Mutex;\nuse embassy_sync::blocking_mutex::raw::NoopRawMutex;\nuse embassy_time::Delay;\nuse embedded_graphics::image::{Image, ImageRawLE};\nuse embedded_graphics::mono_font::MonoTextStyle;\nuse embedded_graphics::mono_font::ascii::FONT_10X20;\nuse embedded_graphics::pixelcolor::Rgb565;\nuse embedded_graphics::prelude::*;\nuse embedded_graphics::primitives::{PrimitiveStyleBuilder, Rectangle};\nuse embedded_graphics::text::Text;\nuse mipidsi::Builder;\nuse mipidsi::models::ST7789;\nuse mipidsi::options::{Orientation, Rotation};\nuse {defmt_rtt as _, panic_probe as _};\n\nuse crate::touch::Touch;\n\nconst DISPLAY_FREQ: u32 = 64_000_000;\nconst TOUCH_FREQ: u32 = 200_000;\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let p = embassy_rp::init(Default::default());\n    info!(\"Hello World!\");\n\n    let bl = p.PIN_13;\n    let rst = p.PIN_15;\n    let display_cs = p.PIN_9;\n    let dcx = p.PIN_8;\n    let miso = p.PIN_12;\n    let mosi = p.PIN_11;\n    let clk = p.PIN_10;\n    let touch_cs = p.PIN_16;\n    //let touch_irq = p.PIN_17;\n\n    // create SPI\n    let mut display_config = spi::Config::default();\n    display_config.frequency = DISPLAY_FREQ;\n    display_config.phase = spi::Phase::CaptureOnSecondTransition;\n    display_config.polarity = spi::Polarity::IdleHigh;\n    let mut touch_config = spi::Config::default();\n    touch_config.frequency = TOUCH_FREQ;\n    touch_config.phase = spi::Phase::CaptureOnSecondTransition;\n    touch_config.polarity = spi::Polarity::IdleHigh;\n\n    let spi = Spi::new_blocking(p.SPI1, clk, mosi, miso, touch_config.clone());\n    let spi_bus: Mutex<NoopRawMutex, _> = Mutex::new(RefCell::new(spi));\n\n    let display_spi = SpiDeviceWithConfig::new(&spi_bus, Output::new(display_cs, Level::High), display_config);\n    let touch_spi = SpiDeviceWithConfig::new(&spi_bus, Output::new(touch_cs, Level::High), touch_config);\n\n    let mut touch = Touch::new(touch_spi);\n\n    let dcx = Output::new(dcx, Level::Low);\n    let rst = Output::new(rst, Level::Low);\n    // dcx: 0 = command, 1 = data\n\n    // Enable LCD backlight\n    let _bl = Output::new(bl, Level::High);\n\n    // display interface abstraction from SPI and DC\n    let di = SPIInterface::new(display_spi, dcx);\n\n    // Define the display from the display interface and initialize it\n    let mut display = Builder::new(ST7789, di)\n        .display_size(240, 320)\n        .reset_pin(rst)\n        .orientation(Orientation::new().rotate(Rotation::Deg90))\n        .init(&mut Delay)\n        .unwrap();\n    display.clear(Rgb565::BLACK).unwrap();\n\n    let raw_image_data = ImageRawLE::new(include_bytes!(\"../../assets/ferris.raw\"), 86);\n    let ferris = Image::new(&raw_image_data, Point::new(34, 68));\n\n    // Display the image\n    ferris.draw(&mut display).unwrap();\n\n    let style = MonoTextStyle::new(&FONT_10X20, Rgb565::GREEN);\n    Text::new(\n        \"Hello embedded_graphics \\n + embassy + RP2040!\",\n        Point::new(20, 200),\n        style,\n    )\n    .draw(&mut display)\n    .unwrap();\n\n    loop {\n        if let Some((x, y)) = touch.read() {\n            let style = PrimitiveStyleBuilder::new().fill_color(Rgb565::BLUE).build();\n\n            Rectangle::new(Point::new(x - 1, y - 1), Size::new(3, 3))\n                .into_styled(style)\n                .draw(&mut display)\n                .unwrap();\n        }\n    }\n}\n\n/// Driver for the XPT2046 resistive touchscreen sensor\nmod touch {\n    use embedded_hal_1::spi::{Operation, SpiDevice};\n\n    struct Calibration {\n        x1: i32,\n        x2: i32,\n        y1: i32,\n        y2: i32,\n        sx: i32,\n        sy: i32,\n    }\n\n    const CALIBRATION: Calibration = Calibration {\n        x1: 3880,\n        x2: 340,\n        y1: 262,\n        y2: 3850,\n        sx: 320,\n        sy: 240,\n    };\n\n    pub struct Touch<SPI: SpiDevice> {\n        spi: SPI,\n    }\n\n    impl<SPI> Touch<SPI>\n    where\n        SPI: SpiDevice,\n    {\n        pub fn new(spi: SPI) -> Self {\n            Self { spi }\n        }\n\n        pub fn read(&mut self) -> Option<(i32, i32)> {\n            let mut x = [0; 2];\n            let mut y = [0; 2];\n            self.spi\n                .transaction(&mut [\n                    Operation::Write(&[0x90]),\n                    Operation::Read(&mut x),\n                    Operation::Write(&[0xd0]),\n                    Operation::Read(&mut y),\n                ])\n                .unwrap();\n\n            let x = (u16::from_be_bytes(x) >> 3) as i32;\n            let y = (u16::from_be_bytes(y) >> 3) as i32;\n\n            let cal = &CALIBRATION;\n\n            let x = ((x - cal.x1) * cal.sx / (cal.x2 - cal.x1)).clamp(0, cal.sx);\n            let y = ((y - cal.y1) * cal.sy / (cal.y2 - cal.y1)).clamp(0, cal.sy);\n            if x == 0 && y == 0 { None } else { Some((x, y)) }\n        }\n    }\n}\n"
  },
  {
    "path": "examples/rp/src/bin/spi_gc9a01.rs",
    "content": "//! This example shows how to use SPI (Serial Peripheral Interface) in the RP2040 chip.\n//!\n//! Example written for a display using the GC9A01 chip. Possibly the Waveshare RP2040-LCD-1.28\n//! (https://www.waveshare.com/wiki/RP2040-LCD-1.28)\n\n#![no_std]\n#![no_main]\n\nuse core::cell::RefCell;\n\nuse defmt::*;\nuse display_interface_spi::SPIInterface;\nuse embassy_embedded_hal::shared_bus::blocking::spi::SpiDeviceWithConfig;\nuse embassy_executor::Spawner;\nuse embassy_rp::clocks::RoscRng;\nuse embassy_rp::gpio::{Level, Output};\nuse embassy_rp::spi;\nuse embassy_rp::spi::{Blocking, Spi};\nuse embassy_sync::blocking_mutex::Mutex;\nuse embassy_sync::blocking_mutex::raw::NoopRawMutex;\nuse embassy_time::{Delay, Duration, Timer};\nuse embedded_graphics::image::{Image, ImageRawLE};\nuse embedded_graphics::pixelcolor::Rgb565;\nuse embedded_graphics::prelude::*;\nuse embedded_graphics::primitives::{PrimitiveStyleBuilder, Rectangle};\nuse mipidsi::Builder;\nuse mipidsi::models::GC9A01;\nuse mipidsi::options::{ColorInversion, ColorOrder};\nuse {defmt_rtt as _, panic_probe as _};\n\nconst DISPLAY_FREQ: u32 = 64_000_000;\nconst LCD_X_RES: i32 = 240;\nconst LCD_Y_RES: i32 = 240;\nconst FERRIS_WIDTH: u32 = 86;\nconst FERRIS_HEIGHT: u32 = 64;\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let p = embassy_rp::init(Default::default());\n    let mut rng = RoscRng;\n\n    info!(\"Hello World!\");\n\n    let bl = p.PIN_25;\n    let rst = p.PIN_12;\n    let display_cs = p.PIN_9;\n    let dcx = p.PIN_8;\n    let mosi = p.PIN_11;\n    let clk = p.PIN_10;\n\n    // create SPI\n    let mut display_config = spi::Config::default();\n    display_config.frequency = DISPLAY_FREQ;\n    display_config.phase = spi::Phase::CaptureOnSecondTransition;\n    display_config.polarity = spi::Polarity::IdleHigh;\n\n    let spi: Spi<'_, _, Blocking> = Spi::new_blocking_txonly(p.SPI1, clk, mosi, display_config.clone());\n    let spi_bus: Mutex<NoopRawMutex, _> = Mutex::new(RefCell::new(spi));\n\n    let display_spi = SpiDeviceWithConfig::new(&spi_bus, Output::new(display_cs, Level::High), display_config);\n    let dcx = Output::new(dcx, Level::Low);\n    let rst = Output::new(rst, Level::Low);\n    // dcx: 0 = command, 1 = data\n\n    // Enable LCD backlight\n    let _bl = Output::new(bl, Level::High);\n\n    // display interface abstraction from SPI and DC\n    let di = SPIInterface::new(display_spi, dcx);\n\n    // Define the display from the display interface and initialize it\n    let mut display = Builder::new(GC9A01, di)\n        .display_size(240, 240)\n        .reset_pin(rst)\n        .color_order(ColorOrder::Bgr)\n        .invert_colors(ColorInversion::Inverted)\n        .init(&mut Delay)\n        .unwrap();\n    display.clear(Rgb565::BLACK).unwrap();\n\n    let raw_image_data = ImageRawLE::new(include_bytes!(\"../../assets/ferris.raw\"), FERRIS_WIDTH);\n    let mut ferris = Image::new(&raw_image_data, Point::zero());\n\n    let r = rng.next_u32();\n    let mut delta = Point {\n        x: ((r % 10) + 5) as i32,\n        y: (((r >> 8) % 10) + 5) as i32,\n    };\n    loop {\n        // Move Ferris\n        let bb = ferris.bounding_box();\n        let tl = bb.top_left;\n        let br = bb.bottom_right().unwrap();\n        if tl.x < 0 || br.x > LCD_X_RES {\n            delta.x = -delta.x;\n        }\n        if tl.y < 0 || br.y > LCD_Y_RES {\n            delta.y = -delta.y;\n        }\n\n        // Erase ghosting\n        let style = PrimitiveStyleBuilder::new().fill_color(Rgb565::BLACK).build();\n        let mut off = Point { x: 0, y: 0 };\n        if delta.x < 0 {\n            off.x = FERRIS_WIDTH as i32;\n        }\n        Rectangle::new(tl + off, Size::new(delta.x as u32, FERRIS_HEIGHT))\n            .into_styled(style)\n            .draw(&mut display)\n            .unwrap();\n        off = Point { x: 0, y: 0 };\n        if delta.y < 0 {\n            off.y = FERRIS_HEIGHT as i32;\n        }\n        Rectangle::new(tl + off, Size::new(FERRIS_WIDTH, delta.y as u32))\n            .into_styled(style)\n            .draw(&mut display)\n            .unwrap();\n        // Translate Ferris\n        ferris.translate_mut(delta);\n        // Display the image\n        ferris.draw(&mut display).unwrap();\n        Timer::after(Duration::from_millis(50)).await;\n    }\n}\n"
  },
  {
    "path": "examples/rp/src/bin/spi_sdmmc.rs",
    "content": "//! This example shows how to use `embedded-sdmmc` with the RP2040 chip, over SPI.\n//!\n//! The example will attempt to read a file `MY_FILE.TXT` from the root directory\n//! of the SD card and print its contents.\n\n#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_rp::spi::Spi;\nuse embassy_rp::{gpio, spi};\nuse embedded_hal_bus::spi::ExclusiveDevice;\nuse embedded_sdmmc::sdcard::{DummyCsPin, SdCard};\nuse gpio::{Level, Output};\nuse {defmt_rtt as _, panic_probe as _};\n\nstruct DummyTimesource();\n\nimpl embedded_sdmmc::TimeSource for DummyTimesource {\n    fn get_timestamp(&self) -> embedded_sdmmc::Timestamp {\n        embedded_sdmmc::Timestamp {\n            year_since_1970: 0,\n            zero_indexed_month: 0,\n            zero_indexed_day: 0,\n            hours: 0,\n            minutes: 0,\n            seconds: 0,\n        }\n    }\n}\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let p = embassy_rp::init(Default::default());\n\n    // SPI clock needs to be running at <= 400kHz during initialization\n    let mut config = spi::Config::default();\n    config.frequency = 400_000;\n    let spi = Spi::new_blocking(p.SPI1, p.PIN_10, p.PIN_11, p.PIN_12, config);\n    // Use a dummy cs pin here, for embedded-hal SpiDevice compatibility reasons\n    let spi_dev = ExclusiveDevice::new_no_delay(spi, DummyCsPin);\n    // Real cs pin\n    let cs = Output::new(p.PIN_16, Level::High);\n\n    let sdcard = SdCard::new(spi_dev, cs, embassy_time::Delay);\n    info!(\"Card size is {} bytes\", sdcard.num_bytes().unwrap());\n\n    // Now that the card is initialized, the SPI clock can go faster\n    let mut config = spi::Config::default();\n    config.frequency = 16_000_000;\n    sdcard.spi(|dev| dev.bus_mut().set_config(&config));\n\n    // Now let's look for volumes (also known as partitions) on our block device.\n    // To do this we need a Volume Manager. It will take ownership of the block device.\n    let mut volume_mgr = embedded_sdmmc::VolumeManager::new(sdcard, DummyTimesource());\n\n    // Try and access Volume 0 (i.e. the first partition).\n    // The volume object holds information about the filesystem on that volume.\n    let mut volume0 = volume_mgr.open_volume(embedded_sdmmc::VolumeIdx(0)).unwrap();\n    info!(\"Volume 0: {:?}\", defmt::Debug2Format(&volume0));\n\n    // Open the root directory (mutably borrows from the volume).\n    let mut root_dir = volume0.open_root_dir().unwrap();\n\n    // Open a file called \"MY_FILE.TXT\" in the root directory\n    // This mutably borrows the directory.\n    let mut my_file = root_dir\n        .open_file_in_dir(\"MY_FILE.TXT\", embedded_sdmmc::Mode::ReadOnly)\n        .unwrap();\n\n    // Print the contents of the file\n    while !my_file.is_eof() {\n        let mut buf = [0u8; 32];\n        if let Ok(n) = my_file.read(&mut buf) {\n            info!(\"{:a}\", buf[..n]);\n        }\n    }\n\n    loop {}\n}\n"
  },
  {
    "path": "examples/rp/src/bin/uart.rs",
    "content": "//! This example shows how to use UART (Universal asynchronous receiver-transmitter) in the RP2040 chip.\n//!\n//! No specific hardware is specified in this example. Only output on pin 0 is tested.\n//! The Raspberry Pi Debug Probe (https://www.raspberrypi.com/products/debug-probe/) could be used\n//! with its UART port.\n\n#![no_std]\n#![no_main]\n\nuse embassy_executor::Spawner;\nuse embassy_rp::uart;\nuse {defmt_rtt as _, panic_probe as _};\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let p = embassy_rp::init(Default::default());\n    let config = uart::Config::default();\n    let mut uart = uart::Uart::new_with_rtscts_blocking(p.UART0, p.PIN_0, p.PIN_1, p.PIN_3, p.PIN_2, config);\n    uart.blocking_write(\"Hello World!\\r\\n\".as_bytes()).unwrap();\n\n    loop {\n        uart.blocking_write(\"hello there!\\r\\n\".as_bytes()).unwrap();\n        cortex_m::asm::delay(1_000_000);\n    }\n}\n"
  },
  {
    "path": "examples/rp/src/bin/uart_buffered_split.rs",
    "content": "//! This example shows how to use UART (Universal asynchronous receiver-transmitter) in the RP2040 chip.\n//!\n//! No specific hardware is specified in this example. If you connect pin 0 and 1 you should get the same data back.\n//! The Raspberry Pi Debug Probe (https://www.raspberrypi.com/products/debug-probe/) could be used\n//! with its UART port.\n\n#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_rp::bind_interrupts;\nuse embassy_rp::peripherals::UART0;\nuse embassy_rp::uart::{BufferedInterruptHandler, BufferedUart, BufferedUartRx, Config};\nuse embassy_time::Timer;\nuse embedded_io_async::{Read, Write};\nuse static_cell::StaticCell;\nuse {defmt_rtt as _, panic_probe as _};\n\nbind_interrupts!(struct Irqs {\n    UART0_IRQ => BufferedInterruptHandler<UART0>;\n});\n\n#[embassy_executor::main]\nasync fn main(spawner: Spawner) {\n    let p = embassy_rp::init(Default::default());\n    let (tx_pin, rx_pin, uart) = (p.PIN_0, p.PIN_1, p.UART0);\n\n    static TX_BUF: StaticCell<[u8; 16]> = StaticCell::new();\n    let tx_buf = &mut TX_BUF.init([0; 16])[..];\n    static RX_BUF: StaticCell<[u8; 16]> = StaticCell::new();\n    let rx_buf = &mut RX_BUF.init([0; 16])[..];\n    let uart = BufferedUart::new(uart, tx_pin, rx_pin, Irqs, tx_buf, rx_buf, Config::default());\n    let (mut tx, rx) = uart.split();\n\n    spawner.spawn(unwrap!(reader(rx)));\n\n    info!(\"Writing...\");\n    loop {\n        let data = [\n            1u8, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28,\n            29, 30, 31,\n        ];\n        info!(\"TX {:?}\", data);\n        tx.write_all(&data).await.unwrap();\n        Timer::after_secs(1).await;\n    }\n}\n\n#[embassy_executor::task]\nasync fn reader(mut rx: BufferedUartRx) {\n    info!(\"Reading...\");\n    loop {\n        let mut buf = [0; 31];\n        rx.read_exact(&mut buf).await.unwrap();\n        info!(\"RX {:?}\", buf);\n    }\n}\n"
  },
  {
    "path": "examples/rp/src/bin/uart_r503.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse defmt::{debug, error, info};\nuse embassy_executor::Spawner;\nuse embassy_rp::bind_interrupts;\nuse embassy_rp::peripherals::{DMA_CH0, DMA_CH1, UART0};\nuse embassy_rp::uart::{Config, DataBits, InterruptHandler as UARTInterruptHandler, Parity, StopBits, Uart};\nuse embassy_time::{Duration, Timer, with_timeout};\nuse heapless::Vec;\nuse {defmt_rtt as _, panic_probe as _};\n\nbind_interrupts!(pub struct Irqs {\n    UART0_IRQ  => UARTInterruptHandler<UART0>;\n    DMA_IRQ_0 => embassy_rp::dma::InterruptHandler<DMA_CH0>, embassy_rp::dma::InterruptHandler<DMA_CH1>;\n});\n\nconst START: u16 = 0xEF01;\nconst ADDRESS: u32 = 0xFFFFFFFF;\n\n// ================================================================================\n\n// Data package format\n// Name     Length          Description\n// ==========================================================================================================\n// Start    2 bytes         Fixed value of 0xEF01; High byte transferred first.\n// Address  4 bytes         Default value is 0xFFFFFFFF, which can be modified by command.\n//                          High byte transferred first and at wrong adder value, module\n//                          will reject to transfer.\n// PID      1 byte          01H     Command packet;\n//                          02H     Data packet; Data packet shall not appear alone in executing\n//                                  processs, must follow command packet or acknowledge packet.\n//                          07H     Acknowledge packet;\n//                          08H     End of Data packet.\n// LENGTH   2 bytes         Refers to the length of package content (command packets and data packets)\n//                          plus the length of Checksum (2 bytes). Unit is byte. Max length is 256 bytes.\n//                          And high byte is transferred first.\n// DATA     -               It can be commands, data, command’s parameters, acknowledge result, etc.\n//                          (fingerprint character value, template are all deemed as data);\n// SUM      2 bytes         The arithmetic sum of package identifier, package length and all package\n//                          contens. Overflowing bits are omitted. high byte is transferred first.\n\n// ================================================================================\n\n// Checksum is calculated on 'length (2 bytes) + data (??)'.\nfn compute_checksum(buf: Vec<u8, 32>) -> u16 {\n    let mut checksum = 0u16;\n\n    let check_end = buf.len();\n    let checked_bytes = &buf[6..check_end];\n    for byte in checked_bytes {\n        checksum += (*byte) as u16;\n    }\n    return checksum;\n}\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    info!(\"Start\");\n\n    let p = embassy_rp::init(Default::default());\n\n    // Initialize the fingerprint scanner.\n    let mut config = Config::default();\n    config.baudrate = 57600;\n    config.stop_bits = StopBits::STOP1;\n    config.data_bits = DataBits::DataBits8;\n    config.parity = Parity::ParityNone;\n\n    let (uart, tx_pin, tx_dma, rx_pin, rx_dma) = (p.UART0, p.PIN_16, p.DMA_CH0, p.PIN_17, p.DMA_CH1);\n    let uart = Uart::new(uart, tx_pin, rx_pin, Irqs, tx_dma, rx_dma, config);\n    let (mut tx, mut rx) = uart.split();\n\n    let mut vec_buf: Vec<u8, 32> = heapless::Vec::new();\n    let mut data: Vec<u8, 32> = heapless::Vec::new();\n\n    let mut speeds: Vec<u8, 3> = heapless::Vec::new();\n    let _ = speeds.push(0xC8); // Slow\n    let _ = speeds.push(0x20); // Medium\n    let _ = speeds.push(0x02); // Fast\n\n    // Cycle through the three colours Red, Blue and Purple forever.\n    loop {\n        for colour in 1..=3 {\n            for speed in &speeds {\n                // Set the data first, because the length is dependent on that.\n                // However, we write the length bits before we do the data.\n                data.clear();\n                let _ = data.push(0x01); // ctrl=Breathing light\n                let _ = data.push(*speed);\n                let _ = data.push(colour as u8); // colour=Red, Blue, Purple\n                let _ = data.push(0x00); // times=Infinite\n\n                // Clear buffers\n                vec_buf.clear();\n\n                // START\n                let _ = vec_buf.extend_from_slice(&START.to_be_bytes()[..]);\n\n                // ADDRESS\n                let _ = vec_buf.extend_from_slice(&ADDRESS.to_be_bytes()[..]);\n\n                // PID\n                let _ = vec_buf.extend_from_slice(&[0x01]);\n\n                // LENGTH\n                let len: u16 = (1 + data.len() + 2).try_into().unwrap();\n                let _ = vec_buf.extend_from_slice(&len.to_be_bytes()[..]);\n\n                // COMMAND\n                let _ = vec_buf.push(0x35); // Command: AuraLedConfig\n\n                // DATA\n                let _ = vec_buf.extend_from_slice(&data);\n\n                // SUM\n                let chk = compute_checksum(vec_buf.clone());\n                let _ = vec_buf.extend_from_slice(&chk.to_be_bytes()[..]);\n\n                // =====\n\n                // Send command buffer.\n                let data_write: [u8; 16] = vec_buf.clone().into_array().unwrap();\n                debug!(\"  write='{:?}'\", data_write[..]);\n                match tx.write(&data_write).await {\n                    Ok(..) => info!(\"Write successful.\"),\n                    Err(e) => error!(\"Write error: {:?}\", e),\n                }\n\n                // =====\n\n                // Read command buffer.\n                let mut read_buf: [u8; 1] = [0; 1]; // Can only read one byte at a time!\n                let mut data_read: Vec<u8, 32> = heapless::Vec::new(); // Save buffer.\n\n                info!(\"Attempting read.\");\n                loop {\n                    // Some commands, like `Img2Tz()` needs longer, but we hard-code this to 200ms\n                    // for this command.\n                    match with_timeout(Duration::from_millis(200), rx.read(&mut read_buf)).await {\n                        Ok(..) => {\n                            // Extract and save read byte.\n                            debug!(\"  r='{=u8:#04x}H' ({:03}D)\", read_buf[0], read_buf[0]);\n                            let _ = data_read.push(read_buf[0]).unwrap();\n                        }\n                        Err(..) => break, // TimeoutError -> Ignore.\n                    }\n                }\n                info!(\"Read successful\");\n                debug!(\"  read='{:?}'\", data_read[..]);\n\n                Timer::after_secs(3).await;\n                info!(\"Changing speed.\");\n            }\n\n            info!(\"Changing colour.\");\n        }\n    }\n}\n"
  },
  {
    "path": "examples/rp/src/bin/uart_unidir.rs",
    "content": "//! This example shows how to use UART (Universal asynchronous receiver-transmitter) in the RP2040 chip.\n//!\n//! Test TX-only and RX-only on two different UARTs. You need to connect GPIO0 to GPIO5 for\n//! this to work\n//! The Raspberry Pi Debug Probe (https://www.raspberrypi.com/products/debug-probe/) could be used\n//! with its UART port.\n\n#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_rp::peripherals::{DMA_CH0, DMA_CH1, UART1};\nuse embassy_rp::uart::{Async, Config, InterruptHandler, UartRx, UartTx};\nuse embassy_rp::{bind_interrupts, dma};\nuse embassy_time::Timer;\nuse {defmt_rtt as _, panic_probe as _};\n\nbind_interrupts!(struct Irqs {\n    UART1_IRQ => InterruptHandler<UART1>;\n    DMA_IRQ_0 => dma::InterruptHandler<DMA_CH0>, dma::InterruptHandler<DMA_CH1>;\n});\n\n#[embassy_executor::main]\nasync fn main(spawner: Spawner) {\n    let p = embassy_rp::init(Default::default());\n\n    let mut uart_tx = UartTx::new(p.UART0, p.PIN_0, p.DMA_CH0, Irqs, Config::default());\n    let uart_rx = UartRx::new(p.UART1, p.PIN_5, Irqs, p.DMA_CH1, Config::default());\n\n    spawner.spawn(unwrap!(reader(uart_rx)));\n\n    info!(\"Writing...\");\n    loop {\n        let data = [1u8, 2, 3, 4, 5, 6, 7, 8];\n        info!(\"TX {:?}\", data);\n        uart_tx.write(&data).await.unwrap();\n        Timer::after_secs(1).await;\n    }\n}\n\n#[embassy_executor::task]\nasync fn reader(mut rx: UartRx<'static, Async>) {\n    info!(\"Reading...\");\n    loop {\n        // read a total of 4 transmissions (32 / 8) and then print the result\n        let mut buf = [0; 32];\n        rx.read(&mut buf).await.unwrap();\n        info!(\"RX {:?}\", buf);\n    }\n}\n"
  },
  {
    "path": "examples/rp/src/bin/usb_ethernet.rs",
    "content": "//! This example shows how to use USB (Universal Serial Bus) in the RP2040 chip.\n//!\n//! This is a CDC-NCM class implementation, aka Ethernet over USB.\n\n#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_net::StackResources;\nuse embassy_net::tcp::TcpSocket;\nuse embassy_rp::clocks::RoscRng;\nuse embassy_rp::peripherals::USB;\nuse embassy_rp::usb::{Driver, InterruptHandler};\nuse embassy_rp::{bind_interrupts, peripherals};\nuse embassy_usb::class::cdc_ncm::embassy_net::{Device, Runner, State as NetState};\nuse embassy_usb::class::cdc_ncm::{CdcNcmClass, State};\nuse embassy_usb::{Builder, Config, UsbDevice};\nuse embedded_io_async::Write;\nuse static_cell::StaticCell;\nuse {defmt_rtt as _, panic_probe as _};\n\nbind_interrupts!(struct Irqs {\n    USBCTRL_IRQ => InterruptHandler<USB>;\n});\n\ntype MyDriver = Driver<'static, peripherals::USB>;\n\nconst MTU: usize = 1514;\n\n#[embassy_executor::task]\nasync fn usb_task(mut device: UsbDevice<'static, MyDriver>) -> ! {\n    device.run().await\n}\n\n#[embassy_executor::task]\nasync fn usb_ncm_task(class: Runner<'static, MyDriver, MTU>) -> ! {\n    class.run().await\n}\n\n#[embassy_executor::task]\nasync fn net_task(mut runner: embassy_net::Runner<'static, Device<'static, MTU>>) -> ! {\n    runner.run().await\n}\n\n#[embassy_executor::main]\nasync fn main(spawner: Spawner) {\n    let p = embassy_rp::init(Default::default());\n    let mut rng = RoscRng;\n\n    // Create the driver, from the HAL.\n    let driver = Driver::new(p.USB, Irqs);\n\n    // Create embassy-usb Config\n    let mut config = Config::new(0xc0de, 0xcafe);\n    config.manufacturer = Some(\"Embassy\");\n    config.product = Some(\"USB-Ethernet example\");\n    config.serial_number = Some(\"12345678\");\n    config.max_power = 100;\n    config.max_packet_size_0 = 64;\n\n    // Create embassy-usb DeviceBuilder using the driver and config.\n    static CONFIG_DESC: StaticCell<[u8; 256]> = StaticCell::new();\n    static BOS_DESC: StaticCell<[u8; 256]> = StaticCell::new();\n    static CONTROL_BUF: StaticCell<[u8; 128]> = StaticCell::new();\n    let mut builder = Builder::new(\n        driver,\n        config,\n        &mut CONFIG_DESC.init([0; 256])[..],\n        &mut BOS_DESC.init([0; 256])[..],\n        &mut [], // no msos descriptors\n        &mut CONTROL_BUF.init([0; 128])[..],\n    );\n\n    // Our MAC addr.\n    let our_mac_addr = [0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC];\n    // Host's MAC addr. This is the MAC the host \"thinks\" its USB-to-ethernet adapter has.\n    let host_mac_addr = [0x88, 0x88, 0x88, 0x88, 0x88, 0x88];\n\n    // Create classes on the builder.\n    static STATE: StaticCell<State> = StaticCell::new();\n    let class = CdcNcmClass::new(&mut builder, STATE.init(State::new()), host_mac_addr, 64);\n\n    // Build the builder.\n    let usb = builder.build();\n\n    spawner.spawn(unwrap!(usb_task(usb)));\n\n    static NET_STATE: StaticCell<NetState<MTU, 4, 4>> = StaticCell::new();\n    let (runner, device) = class.into_embassy_net_device::<MTU, 4, 4>(NET_STATE.init(NetState::new()), our_mac_addr);\n    spawner.spawn(unwrap!(usb_ncm_task(runner)));\n\n    let config = embassy_net::Config::dhcpv4(Default::default());\n    //let config = embassy_net::Config::ipv4_static(embassy_net::StaticConfigV4 {\n    //    address: Ipv4Cidr::new(Ipv4Address::new(10, 42, 0, 61), 24),\n    //    dns_servers: Vec::new(),\n    //    gateway: Some(Ipv4Address::new(10, 42, 0, 1)),\n    //});\n\n    // Generate random seed\n    let seed = rng.next_u64();\n\n    // Init network stack\n    static RESOURCES: StaticCell<StackResources<3>> = StaticCell::new();\n    let (stack, runner) = embassy_net::new(device, config, RESOURCES.init(StackResources::new()), seed);\n\n    spawner.spawn(unwrap!(net_task(runner)));\n\n    // And now we can use it!\n\n    let mut rx_buffer = [0; 4096];\n    let mut tx_buffer = [0; 4096];\n    let mut buf = [0; 4096];\n\n    loop {\n        let mut socket = TcpSocket::new(stack, &mut rx_buffer, &mut tx_buffer);\n        socket.set_timeout(Some(embassy_time::Duration::from_secs(10)));\n\n        info!(\"Listening on TCP:1234...\");\n        if let Err(e) = socket.accept(1234).await {\n            warn!(\"accept error: {:?}\", e);\n            continue;\n        }\n\n        info!(\"Received connection from {:?}\", socket.remote_endpoint());\n\n        loop {\n            let n = match socket.read(&mut buf).await {\n                Ok(0) => {\n                    warn!(\"read EOF\");\n                    break;\n                }\n                Ok(n) => n,\n                Err(e) => {\n                    warn!(\"read error: {:?}\", e);\n                    break;\n                }\n            };\n\n            info!(\"rxd {:02x}\", &buf[..n]);\n\n            match socket.write_all(&buf[..n]).await {\n                Ok(()) => {}\n                Err(e) => {\n                    warn!(\"write error: {:?}\", e);\n                    break;\n                }\n            };\n        }\n    }\n}\n"
  },
  {
    "path": "examples/rp/src/bin/usb_hid_keyboard.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse core::sync::atomic::{AtomicBool, AtomicU8, Ordering};\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_futures::join::join;\nuse embassy_rp::bind_interrupts;\nuse embassy_rp::gpio::{Input, Pull};\nuse embassy_rp::peripherals::USB;\nuse embassy_rp::usb::{Driver, InterruptHandler};\nuse embassy_usb::class::hid::{\n    HidBootProtocol, HidProtocolMode, HidReaderWriter, HidSubclass, ReportId, RequestHandler, State,\n};\nuse embassy_usb::control::OutResponse;\nuse embassy_usb::{Builder, Config, Handler};\nuse usbd_hid::descriptor::{KeyboardReport, SerializedDescriptor};\nuse {defmt_rtt as _, panic_probe as _};\n\nbind_interrupts!(struct Irqs {\n    USBCTRL_IRQ => InterruptHandler<USB>;\n});\n\nstatic HID_PROTOCOL_MODE: AtomicU8 = AtomicU8::new(HidProtocolMode::Boot as u8);\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let p = embassy_rp::init(Default::default());\n    // Create the driver, from the HAL.\n    let driver = Driver::new(p.USB, Irqs);\n\n    // Create embassy-usb Config\n    let mut config = Config::new(0xc0de, 0xcafe);\n    config.manufacturer = Some(\"Embassy\");\n    config.product = Some(\"HID keyboard example\");\n    config.serial_number = Some(\"12345678\");\n    config.max_power = 100;\n    config.max_packet_size_0 = 64;\n    config.composite_with_iads = false;\n    config.device_class = 0;\n    config.device_sub_class = 0;\n    config.device_protocol = 0;\n\n    // Create embassy-usb DeviceBuilder using the driver and config.\n    // It needs some buffers for building the descriptors.\n    let mut config_descriptor = [0; 256];\n    let mut bos_descriptor = [0; 256];\n    // You can also add a Microsoft OS descriptor.\n    let mut msos_descriptor = [0; 256];\n    let mut control_buf = [0; 64];\n    let mut request_handler = MyRequestHandler {};\n    let mut device_handler = MyDeviceHandler::new();\n\n    let mut state = State::new();\n\n    let mut builder = Builder::new(\n        driver,\n        config,\n        &mut config_descriptor,\n        &mut bos_descriptor,\n        &mut msos_descriptor,\n        &mut control_buf,\n    );\n\n    builder.handler(&mut device_handler);\n\n    // Create classes on the builder.\n    let config = embassy_usb::class::hid::Config {\n        report_descriptor: KeyboardReport::desc(),\n        request_handler: None,\n        poll_ms: 60,\n        max_packet_size: 64,\n        hid_subclass: HidSubclass::Boot,\n        hid_boot_protocol: HidBootProtocol::Keyboard,\n    };\n    let hid = HidReaderWriter::<_, 1, 8>::new(&mut builder, &mut state, config);\n\n    // Build the builder.\n    let mut usb = builder.build();\n\n    // Run the USB device.\n    let usb_fut = usb.run();\n\n    // Set up the signal pin that will be used to trigger the keyboard.\n    let mut signal_pin = Input::new(p.PIN_16, Pull::None);\n\n    // Enable the schmitt trigger to slightly debounce.\n    signal_pin.set_schmitt(true);\n\n    let (reader, mut writer) = hid.split();\n\n    // Do stuff with the class!\n    let in_fut = async {\n        loop {\n            info!(\"Waiting for HIGH on pin 16\");\n            signal_pin.wait_for_high().await;\n            info!(\"HIGH DETECTED\");\n\n            if HID_PROTOCOL_MODE.load(Ordering::Relaxed) == HidProtocolMode::Boot as u8 {\n                match writer.write(&[0, 0, 4, 0, 0, 0, 0, 0]).await {\n                    Ok(()) => {}\n                    Err(e) => warn!(\"Failed to send boot report: {:?}\", e),\n                };\n            } else {\n                // Create a report with the A key pressed. (no shift modifier)\n                let report = KeyboardReport {\n                    keycodes: [4, 0, 0, 0, 0, 0],\n                    leds: 0,\n                    modifier: 0,\n                    reserved: 0,\n                };\n                // Send the report.\n                match writer.write_serialize(&report).await {\n                    Ok(()) => {}\n                    Err(e) => warn!(\"Failed to send report: {:?}\", e),\n                };\n            }\n\n            signal_pin.wait_for_low().await;\n            info!(\"LOW DETECTED\");\n            if HID_PROTOCOL_MODE.load(Ordering::Relaxed) == HidProtocolMode::Boot as u8 {\n                match writer.write(&[0, 0, 0, 0, 0, 0, 0, 0]).await {\n                    Ok(()) => {}\n                    Err(e) => warn!(\"Failed to send boot report: {:?}\", e),\n                };\n            } else {\n                let report = KeyboardReport {\n                    keycodes: [0, 0, 0, 0, 0, 0],\n                    leds: 0,\n                    modifier: 0,\n                    reserved: 0,\n                };\n                match writer.write_serialize(&report).await {\n                    Ok(()) => {}\n                    Err(e) => warn!(\"Failed to send report: {:?}\", e),\n                };\n            }\n        }\n    };\n\n    let out_fut = async {\n        reader.run(false, &mut request_handler).await;\n    };\n\n    // Run everything concurrently.\n    // If we had made everything `'static` above instead, we could do this using separate tasks instead.\n    join(usb_fut, join(in_fut, out_fut)).await;\n}\n\nstruct MyRequestHandler {}\n\nimpl RequestHandler for MyRequestHandler {\n    fn get_report(&mut self, id: ReportId, _buf: &mut [u8]) -> Option<usize> {\n        info!(\"Get report for {:?}\", id);\n        None\n    }\n\n    fn set_report(&mut self, id: ReportId, data: &[u8]) -> OutResponse {\n        info!(\"Set report for {:?}: {=[u8]}\", id, data);\n        OutResponse::Accepted\n    }\n\n    fn get_protocol(&self) -> HidProtocolMode {\n        let protocol = HidProtocolMode::from(HID_PROTOCOL_MODE.load(Ordering::Relaxed));\n        info!(\"The current HID protocol mode is: {}\", protocol);\n        protocol\n    }\n\n    fn set_protocol(&mut self, protocol: HidProtocolMode) -> OutResponse {\n        info!(\"Switching to HID protocol mode: {}\", protocol);\n        HID_PROTOCOL_MODE.store(protocol as u8, Ordering::Relaxed);\n        OutResponse::Accepted\n    }\n\n    fn set_idle_ms(&mut self, id: Option<ReportId>, dur: u32) {\n        info!(\"Set idle rate for {:?} to {:?}\", id, dur);\n    }\n\n    fn get_idle_ms(&mut self, id: Option<ReportId>) -> Option<u32> {\n        info!(\"Get idle rate for {:?}\", id);\n        None\n    }\n}\n\nstruct MyDeviceHandler {\n    configured: AtomicBool,\n}\n\nimpl MyDeviceHandler {\n    fn new() -> Self {\n        MyDeviceHandler {\n            configured: AtomicBool::new(false),\n        }\n    }\n}\n\nimpl Handler for MyDeviceHandler {\n    fn enabled(&mut self, enabled: bool) {\n        self.configured.store(false, Ordering::Relaxed);\n        if enabled {\n            info!(\"Device enabled\");\n        } else {\n            info!(\"Device disabled\");\n        }\n    }\n\n    fn reset(&mut self) {\n        self.configured.store(false, Ordering::Relaxed);\n        info!(\"Bus reset, the Vbus current limit is 100mA\");\n    }\n\n    fn addressed(&mut self, addr: u8) {\n        self.configured.store(false, Ordering::Relaxed);\n        info!(\"USB address set to: {}\", addr);\n    }\n\n    fn configured(&mut self, configured: bool) {\n        self.configured.store(configured, Ordering::Relaxed);\n        if configured {\n            info!(\"Device configured, it may now draw up to the configured current limit from Vbus.\")\n        } else {\n            info!(\"Device is no longer configured, the Vbus current limit is 100mA.\");\n        }\n    }\n}\n"
  },
  {
    "path": "examples/rp/src/bin/usb_hid_mouse.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse core::sync::atomic::{AtomicBool, AtomicU8, Ordering};\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_futures::join::join;\nuse embassy_rp::bind_interrupts;\nuse embassy_rp::clocks::RoscRng;\nuse embassy_rp::peripherals::USB;\nuse embassy_rp::usb::{Driver, InterruptHandler};\nuse embassy_time::Timer;\nuse embassy_usb::class::hid::{\n    HidBootProtocol, HidProtocolMode, HidReaderWriter, HidSubclass, ReportId, RequestHandler, State,\n};\nuse embassy_usb::control::OutResponse;\nuse embassy_usb::{Builder, Config, Handler};\nuse rand::Rng;\nuse usbd_hid::descriptor::{MouseReport, SerializedDescriptor};\nuse {defmt_rtt as _, panic_probe as _};\n\nbind_interrupts!(struct Irqs {\n    USBCTRL_IRQ => InterruptHandler<USB>;\n});\n\nstatic HID_PROTOCOL_MODE: AtomicU8 = AtomicU8::new(HidProtocolMode::Boot as u8);\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let p = embassy_rp::init(Default::default());\n    // Create the driver, from the HAL.\n    let driver = Driver::new(p.USB, Irqs);\n\n    // Create embassy-usb Config\n    let mut config = Config::new(0xc0de, 0xcafe);\n    config.manufacturer = Some(\"Embassy\");\n    config.product = Some(\"HID keyboard example\");\n    config.serial_number = Some(\"12345678\");\n    config.max_power = 100;\n    config.max_packet_size_0 = 64;\n    config.composite_with_iads = false;\n    config.device_class = 0;\n    config.device_sub_class = 0;\n    config.device_protocol = 0;\n\n    // Create embassy-usb DeviceBuilder using the driver and config.\n    // It needs some buffers for building the descriptors.\n    let mut config_descriptor = [0; 256];\n    let mut bos_descriptor = [0; 256];\n    // You can also add a Microsoft OS descriptor.\n    let mut msos_descriptor = [0; 256];\n    let mut control_buf = [0; 64];\n    let mut request_handler = MyRequestHandler {};\n    let mut device_handler = MyDeviceHandler::new();\n\n    let mut state = State::new();\n\n    let mut builder = Builder::new(\n        driver,\n        config,\n        &mut config_descriptor,\n        &mut bos_descriptor,\n        &mut msos_descriptor,\n        &mut control_buf,\n    );\n\n    builder.handler(&mut device_handler);\n\n    // Create classes on the builder.\n    let config = embassy_usb::class::hid::Config {\n        report_descriptor: MouseReport::desc(),\n        request_handler: None,\n        poll_ms: 60,\n        max_packet_size: 64,\n        hid_subclass: HidSubclass::Boot,\n        hid_boot_protocol: HidBootProtocol::Mouse,\n    };\n    let hid = HidReaderWriter::<_, 1, 8>::new(&mut builder, &mut state, config);\n\n    // Build the builder.\n    let mut usb = builder.build();\n\n    // Run the USB device.\n    let usb_fut = usb.run();\n\n    let (reader, mut writer) = hid.split();\n\n    // Do stuff with the class!\n    let in_fut = async {\n        let mut rng = RoscRng;\n\n        loop {\n            // every 1 second\n            _ = Timer::after_secs(1).await;\n\n            let x = rng.random_range(-100..100); // random small x movement\n            let y = rng.random_range(-100..100); // random small y movement\n\n            if HID_PROTOCOL_MODE.load(Ordering::Relaxed) == HidProtocolMode::Boot as u8 {\n                let buttons = 0u8;\n                match writer.write(&[buttons, x as u8, y as u8]).await {\n                    Ok(()) => {}\n                    Err(e) => warn!(\"Failed to send boot report: {:?}\", e),\n                }\n            } else {\n                let report = MouseReport {\n                    buttons: 0,\n                    x,\n                    y,\n                    wheel: 0,\n                    pan: 0,\n                };\n                // Send the report.\n                match writer.write_serialize(&report).await {\n                    Ok(()) => {}\n                    Err(e) => warn!(\"Failed to send report: {:?}\", e),\n                }\n            }\n        }\n    };\n\n    let out_fut = async {\n        reader.run(false, &mut request_handler).await;\n    };\n\n    // Run everything concurrently.\n    // If we had made everything `'static` above instead, we could do this using separate tasks instead.\n    join(usb_fut, join(in_fut, out_fut)).await;\n}\n\nstruct MyRequestHandler {}\n\nimpl RequestHandler for MyRequestHandler {\n    fn get_report(&mut self, id: ReportId, _buf: &mut [u8]) -> Option<usize> {\n        info!(\"Get report for {:?}\", id);\n        None\n    }\n\n    fn set_report(&mut self, id: ReportId, data: &[u8]) -> OutResponse {\n        info!(\"Set report for {:?}: {=[u8]}\", id, data);\n        OutResponse::Accepted\n    }\n\n    fn get_protocol(&self) -> HidProtocolMode {\n        let protocol = HidProtocolMode::from(HID_PROTOCOL_MODE.load(Ordering::Relaxed));\n        info!(\"The current HID protocol mode is: {}\", protocol);\n        protocol\n    }\n\n    fn set_protocol(&mut self, protocol: HidProtocolMode) -> OutResponse {\n        info!(\"Switching to HID protocol mode: {}\", protocol);\n        HID_PROTOCOL_MODE.store(protocol as u8, Ordering::Relaxed);\n        OutResponse::Accepted\n    }\n\n    fn set_idle_ms(&mut self, id: Option<ReportId>, dur: u32) {\n        info!(\"Set idle rate for {:?} to {:?}\", id, dur);\n    }\n\n    fn get_idle_ms(&mut self, id: Option<ReportId>) -> Option<u32> {\n        info!(\"Get idle rate for {:?}\", id);\n        None\n    }\n}\n\nstruct MyDeviceHandler {\n    configured: AtomicBool,\n}\n\nimpl MyDeviceHandler {\n    fn new() -> Self {\n        MyDeviceHandler {\n            configured: AtomicBool::new(false),\n        }\n    }\n}\n\nimpl Handler for MyDeviceHandler {\n    fn enabled(&mut self, enabled: bool) {\n        self.configured.store(false, Ordering::Relaxed);\n        if enabled {\n            info!(\"Device enabled\");\n        } else {\n            info!(\"Device disabled\");\n        }\n    }\n\n    fn reset(&mut self) {\n        self.configured.store(false, Ordering::Relaxed);\n        info!(\"Bus reset, the Vbus current limit is 100mA\");\n    }\n\n    fn addressed(&mut self, addr: u8) {\n        self.configured.store(false, Ordering::Relaxed);\n        info!(\"USB address set to: {}\", addr);\n    }\n\n    fn configured(&mut self, configured: bool) {\n        self.configured.store(configured, Ordering::Relaxed);\n        if configured {\n            info!(\"Device configured, it may now draw up to the configured current limit from Vbus.\")\n        } else {\n            info!(\"Device is no longer configured, the Vbus current limit is 100mA.\");\n        }\n    }\n}\n"
  },
  {
    "path": "examples/rp/src/bin/usb_logger.rs",
    "content": "//! This example shows how to use USB (Universal Serial Bus) in the RP2040 chip.\n//!\n//! This creates the possibility to send log::info/warn/error/debug! to USB serial port.\n\n#![no_std]\n#![no_main]\n\nuse embassy_executor::Spawner;\nuse embassy_rp::bind_interrupts;\nuse embassy_rp::peripherals::USB;\nuse embassy_rp::usb::{Driver, InterruptHandler};\nuse embassy_time::Timer;\nuse {defmt_rtt as _, panic_probe as _};\n\nbind_interrupts!(struct Irqs {\n    USBCTRL_IRQ => InterruptHandler<USB>;\n});\n\n#[embassy_executor::task]\nasync fn logger_task(driver: Driver<'static, USB>) {\n    embassy_usb_logger::run!(1024, log::LevelFilter::Info, driver);\n}\n\n#[embassy_executor::main]\nasync fn main(spawner: Spawner) {\n    let p = embassy_rp::init(Default::default());\n    let driver = Driver::new(p.USB, Irqs);\n    spawner.spawn(logger_task(driver).unwrap());\n\n    let mut counter = 0;\n    loop {\n        counter += 1;\n        log::info!(\"Tick {}\", counter);\n        Timer::after_secs(1).await;\n    }\n}\n"
  },
  {
    "path": "examples/rp/src/bin/usb_midi.rs",
    "content": "//! This example shows how to use USB (Universal Serial Bus) in the RP2040 chip.\n//!\n//! This creates a USB MIDI device that echoes MIDI messages back to the host.\n\n#![no_std]\n#![no_main]\n\nuse defmt::{info, panic};\nuse embassy_executor::Spawner;\nuse embassy_futures::join::join;\nuse embassy_rp::bind_interrupts;\nuse embassy_rp::peripherals::USB;\nuse embassy_rp::usb::{Driver, Instance, InterruptHandler};\nuse embassy_usb::class::midi::MidiClass;\nuse embassy_usb::driver::EndpointError;\nuse embassy_usb::{Builder, Config};\nuse {defmt_rtt as _, panic_probe as _};\n\nbind_interrupts!(struct Irqs {\n    USBCTRL_IRQ => InterruptHandler<USB>;\n});\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    info!(\"Hello world!\");\n\n    let p = embassy_rp::init(Default::default());\n\n    // Create the driver, from the HAL.\n    let driver = Driver::new(p.USB, Irqs);\n\n    // Create embassy-usb Config\n    let mut config = Config::new(0xc0de, 0xcafe);\n    config.manufacturer = Some(\"Embassy\");\n    config.product = Some(\"USB-MIDI example\");\n    config.serial_number = Some(\"12345678\");\n    config.max_power = 100;\n    config.max_packet_size_0 = 64;\n\n    // Create embassy-usb DeviceBuilder using the driver and config.\n    // It needs some buffers for building the descriptors.\n    let mut config_descriptor = [0; 256];\n    let mut bos_descriptor = [0; 256];\n    let mut control_buf = [0; 64];\n\n    let mut builder = Builder::new(\n        driver,\n        config,\n        &mut config_descriptor,\n        &mut bos_descriptor,\n        &mut [], // no msos descriptors\n        &mut control_buf,\n    );\n\n    // Create classes on the builder.\n    let mut class = MidiClass::new(&mut builder, 1, 1, 64);\n\n    // The `MidiClass` can be split into `Sender` and `Receiver`, to be used in separate tasks.\n    // let (sender, receiver) = class.split();\n\n    // Build the builder.\n    let mut usb = builder.build();\n\n    // Run the USB device.\n    let usb_fut = usb.run();\n\n    // Use the Midi class!\n    let midi_fut = async {\n        loop {\n            class.wait_connection().await;\n            info!(\"Connected\");\n            let _ = midi_echo(&mut class).await;\n            info!(\"Disconnected\");\n        }\n    };\n\n    // Run everything concurrently.\n    // If we had made everything `'static` above instead, we could do this using separate tasks instead.\n    join(usb_fut, midi_fut).await;\n}\n\nstruct Disconnected {}\n\nimpl From<EndpointError> for Disconnected {\n    fn from(val: EndpointError) -> Self {\n        match val {\n            EndpointError::BufferOverflow => panic!(\"Buffer overflow\"),\n            EndpointError::Disabled => Disconnected {},\n        }\n    }\n}\n\nasync fn midi_echo<'d, T: Instance + 'd>(class: &mut MidiClass<'d, Driver<'d, T>>) -> Result<(), Disconnected> {\n    let mut buf = [0; 64];\n    loop {\n        let n = class.read_packet(&mut buf).await?;\n        let data = &buf[..n];\n        info!(\"data: {:x}\", data);\n        class.write_packet(data).await?;\n    }\n}\n"
  },
  {
    "path": "examples/rp/src/bin/usb_raw.rs",
    "content": "//! Example of using USB without a pre-defined class, but instead responding to\n//! raw USB control requests.\n//!\n//! The host computer can either:\n//! * send a command, with a 16-bit request ID, a 16-bit value, and an optional data buffer\n//! * request some data, with a 16-bit request ID, a 16-bit value, and a length of data to receive\n//!\n//! For higher throughput data, you can add some bulk endpoints after creating the alternate,\n//! but for low rate command/response, plain control transfers can be very simple and effective.\n//!\n//! Example code to send/receive data using `nusb`:\n//!\n//! ```ignore\n//! use futures_lite::future::block_on;\n//! use nusb::transfer::{ControlIn, ControlOut, ControlType, Recipient};\n//!\n//! fn main() {\n//!     let di = nusb::list_devices()\n//!         .unwrap()\n//!         .find(|d| d.vendor_id() == 0xc0de && d.product_id() == 0xcafe)\n//!         .expect(\"no device found\");\n//!     let device = di.open().expect(\"error opening device\");\n//!     let interface = device.claim_interface(0).expect(\"error claiming interface\");\n//!\n//!     // Send \"hello world\" to device\n//!     let result = block_on(interface.control_out(ControlOut {\n//!         control_type: ControlType::Vendor,\n//!         recipient: Recipient::Interface,\n//!         request: 100,\n//!         value: 200,\n//!         index: 0,\n//!         data: b\"hello world\",\n//!     }));\n//!     println!(\"{result:?}\");\n//!\n//!     // Receive \"hello\" from device\n//!     let result = block_on(interface.control_in(ControlIn {\n//!         control_type: ControlType::Vendor,\n//!         recipient: Recipient::Interface,\n//!         request: 101,\n//!         value: 201,\n//!         index: 0,\n//!         length: 5,\n//!     }));\n//!     println!(\"{result:?}\");\n//! }\n//! ```\n\n#![no_std]\n#![no_main]\n\nuse defmt::info;\nuse embassy_executor::Spawner;\nuse embassy_rp::bind_interrupts;\nuse embassy_rp::peripherals::USB;\nuse embassy_rp::usb::{Driver, InterruptHandler};\nuse embassy_usb::control::{InResponse, OutResponse, Recipient, Request, RequestType};\nuse embassy_usb::msos::{self, windows_version};\nuse embassy_usb::types::InterfaceNumber;\nuse embassy_usb::{Builder, Config, Handler};\nuse {defmt_rtt as _, panic_probe as _};\n\n// This is a randomly generated GUID to allow clients on Windows to find our device\nconst DEVICE_INTERFACE_GUIDS: &[&str] = &[\"{AFB9A6FB-30BA-44BC-9232-806CFC875321}\"];\n\nbind_interrupts!(struct Irqs {\n    USBCTRL_IRQ => InterruptHandler<USB>;\n});\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    info!(\"Hello there!\");\n\n    let p = embassy_rp::init(Default::default());\n\n    // Create the driver, from the HAL.\n    let driver = Driver::new(p.USB, Irqs);\n\n    // Create embassy-usb Config\n    let mut config = Config::new(0xc0de, 0xcafe);\n    config.manufacturer = Some(\"Embassy\");\n    config.product = Some(\"USB raw example\");\n    config.serial_number = Some(\"12345678\");\n    config.max_power = 100;\n    config.max_packet_size_0 = 64;\n\n    // Create embassy-usb DeviceBuilder using the driver and config.\n    // It needs some buffers for building the descriptors.\n    let mut config_descriptor = [0; 256];\n    let mut bos_descriptor = [0; 256];\n    let mut msos_descriptor = [0; 256];\n    let mut control_buf = [0; 64];\n\n    let mut handler = ControlHandler {\n        if_num: InterfaceNumber(0),\n    };\n\n    let mut builder = Builder::new(\n        driver,\n        config,\n        &mut config_descriptor,\n        &mut bos_descriptor,\n        &mut msos_descriptor,\n        &mut control_buf,\n    );\n\n    // Add the Microsoft OS Descriptor (MSOS/MOD) descriptor.\n    // We tell Windows that this entire device is compatible with the \"WINUSB\" feature,\n    // which causes it to use the built-in WinUSB driver automatically, which in turn\n    // can be used by libusb/rusb software without needing a custom driver or INF file.\n    // In principle you might want to call msos_feature() just on a specific function,\n    // if your device also has other functions that still use standard class drivers.\n    builder.msos_descriptor(windows_version::WIN8_1, 0);\n    builder.msos_feature(msos::CompatibleIdFeatureDescriptor::new(\"WINUSB\", \"\"));\n    builder.msos_feature(msos::RegistryPropertyFeatureDescriptor::new(\n        \"DeviceInterfaceGUIDs\",\n        msos::PropertyData::RegMultiSz(DEVICE_INTERFACE_GUIDS),\n    ));\n\n    // Add a vendor-specific function (class 0xFF), and corresponding interface,\n    // that uses our custom handler.\n    let mut function = builder.function(0xFF, 0, 0);\n    let mut interface = function.interface();\n    let _alt = interface.alt_setting(0xFF, 0, 0, None);\n    handler.if_num = interface.interface_number();\n    drop(function);\n    builder.handler(&mut handler);\n\n    // Build the builder.\n    let mut usb = builder.build();\n\n    // Run the USB device.\n    usb.run().await;\n}\n\n/// Handle CONTROL endpoint requests and responses. For many simple requests and responses\n/// you can get away with only using the control endpoint.\nstruct ControlHandler {\n    if_num: InterfaceNumber,\n}\n\nimpl Handler for ControlHandler {\n    /// Respond to HostToDevice control messages, where the host sends us a command and\n    /// optionally some data, and we can only acknowledge or reject it.\n    fn control_out<'a>(&'a mut self, req: Request, buf: &'a [u8]) -> Option<OutResponse> {\n        // Log the request before filtering to help with debugging.\n        info!(\"Got control_out, request={}, buf={:a}\", req, buf);\n\n        // Only handle Vendor request types to an Interface.\n        if req.request_type != RequestType::Vendor || req.recipient != Recipient::Interface {\n            return None;\n        }\n\n        // Ignore requests to other interfaces.\n        if req.index != self.if_num.0 as u16 {\n            return None;\n        }\n\n        // Accept request 100, value 200, reject others.\n        if req.request == 100 && req.value == 200 {\n            Some(OutResponse::Accepted)\n        } else {\n            Some(OutResponse::Rejected)\n        }\n    }\n\n    /// Respond to DeviceToHost control messages, where the host requests some data from us.\n    fn control_in<'a>(&'a mut self, req: Request, buf: &'a mut [u8]) -> Option<InResponse<'a>> {\n        info!(\"Got control_in, request={}\", req);\n\n        // Only handle Vendor request types to an Interface.\n        if req.request_type != RequestType::Vendor || req.recipient != Recipient::Interface {\n            return None;\n        }\n\n        // Ignore requests to other interfaces.\n        if req.index != self.if_num.0 as u16 {\n            return None;\n        }\n\n        // Respond \"hello\" to request 101, value 201, when asked for 5 bytes, otherwise reject.\n        if req.request == 101 && req.value == 201 && req.length == 5 {\n            buf[..5].copy_from_slice(b\"hello\");\n            Some(InResponse::Accepted(&buf[..5]))\n        } else {\n            Some(InResponse::Rejected)\n        }\n    }\n}\n"
  },
  {
    "path": "examples/rp/src/bin/usb_raw_bulk.rs",
    "content": "//! Example of using USB without a pre-defined class, but instead using raw USB bulk transfers.\n//!\n//! Example code to send/receive data using `nusb`:\n//!\n//! ```ignore\n//! use futures_lite::future::block_on;\n//! use nusb::transfer::RequestBuffer;\n//!\n//! const BULK_OUT_EP: u8 = 0x01;\n//! const BULK_IN_EP: u8 = 0x81;\n//!\n//! fn main() {\n//!     let di = nusb::list_devices()\n//!         .unwrap()\n//!         .find(|d| d.vendor_id() == 0xc0de && d.product_id() == 0xcafe)\n//!         .expect(\"no device found\");\n//!     let device = di.open().expect(\"error opening device\");\n//!     let interface = device.claim_interface(0).expect(\"error claiming interface\");\n//!\n//!     let result = block_on(interface.bulk_out(BULK_OUT_EP, b\"hello world\".into()));\n//!     println!(\"{result:?}\");\n//!     let result = block_on(interface.bulk_in(BULK_IN_EP, RequestBuffer::new(64)));\n//!     println!(\"{result:?}\");\n//! }\n//! ```\n\n#![no_std]\n#![no_main]\n\nuse defmt::info;\nuse embassy_executor::Spawner;\nuse embassy_futures::join::join;\nuse embassy_rp::bind_interrupts;\nuse embassy_rp::peripherals::USB;\nuse embassy_rp::usb::{Driver, InterruptHandler};\nuse embassy_usb::driver::{Endpoint, EndpointIn, EndpointOut};\nuse embassy_usb::msos::{self, windows_version};\nuse embassy_usb::{Builder, Config};\nuse {defmt_rtt as _, panic_probe as _};\n\n// This is a randomly generated GUID to allow clients on Windows to find our device\nconst DEVICE_INTERFACE_GUIDS: &[&str] = &[\"{AFB9A6FB-30BA-44BC-9232-806CFC875321}\"];\n\nbind_interrupts!(struct Irqs {\n    USBCTRL_IRQ => InterruptHandler<USB>;\n});\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    info!(\"Hello there!\");\n\n    let p = embassy_rp::init(Default::default());\n\n    // Create the driver, from the HAL.\n    let driver = Driver::new(p.USB, Irqs);\n\n    // Create embassy-usb Config\n    let mut config = Config::new(0xc0de, 0xcafe);\n    config.manufacturer = Some(\"Embassy\");\n    config.product = Some(\"USB raw example\");\n    config.serial_number = Some(\"12345678\");\n    config.max_power = 100;\n    config.max_packet_size_0 = 64;\n\n    // Create embassy-usb DeviceBuilder using the driver and config.\n    // It needs some buffers for building the descriptors.\n    let mut config_descriptor = [0; 256];\n    let mut bos_descriptor = [0; 256];\n    let mut msos_descriptor = [0; 256];\n    let mut control_buf = [0; 64];\n\n    let mut builder = Builder::new(\n        driver,\n        config,\n        &mut config_descriptor,\n        &mut bos_descriptor,\n        &mut msos_descriptor,\n        &mut control_buf,\n    );\n\n    // Add the Microsoft OS Descriptor (MSOS/MOD) descriptor.\n    // We tell Windows that this entire device is compatible with the \"WINUSB\" feature,\n    // which causes it to use the built-in WinUSB driver automatically, which in turn\n    // can be used by libusb/rusb software without needing a custom driver or INF file.\n    // In principle you might want to call msos_feature() just on a specific function,\n    // if your device also has other functions that still use standard class drivers.\n    builder.msos_descriptor(windows_version::WIN8_1, 0);\n    builder.msos_feature(msos::CompatibleIdFeatureDescriptor::new(\"WINUSB\", \"\"));\n    builder.msos_feature(msos::RegistryPropertyFeatureDescriptor::new(\n        \"DeviceInterfaceGUIDs\",\n        msos::PropertyData::RegMultiSz(DEVICE_INTERFACE_GUIDS),\n    ));\n\n    // Add a vendor-specific function (class 0xFF), and corresponding interface,\n    // that uses our custom handler.\n    let mut function = builder.function(0xFF, 0, 0);\n    let mut interface = function.interface();\n    let mut alt = interface.alt_setting(0xFF, 0, 0, None);\n    let mut read_ep = alt.endpoint_bulk_out(None, 64);\n    let mut write_ep = alt.endpoint_bulk_in(None, 64);\n    drop(function);\n\n    // Build the builder.\n    let mut usb = builder.build();\n\n    // Run the USB device.\n    let usb_fut = usb.run();\n\n    // Do stuff with the class!\n    let echo_fut = async {\n        loop {\n            read_ep.wait_enabled().await;\n            info!(\"Connected\");\n            loop {\n                let mut data = [0; 64];\n                match read_ep.read(&mut data).await {\n                    Ok(n) => {\n                        info!(\"Got bulk: {:a}\", data[..n]);\n                        // Echo back to the host:\n                        write_ep.write(&data[..n]).await.ok();\n                    }\n                    Err(_) => break,\n                }\n            }\n            info!(\"Disconnected\");\n        }\n    };\n\n    // Run everything concurrently.\n    // If we had made everything `'static` above instead, we could do this using separate tasks instead.\n    join(usb_fut, echo_fut).await;\n}\n"
  },
  {
    "path": "examples/rp/src/bin/usb_serial.rs",
    "content": "//! This example shows how to use USB (Universal Serial Bus) in the RP2040 chip.\n//!\n//! This creates a USB serial port that echos.\n\n#![no_std]\n#![no_main]\n\nuse defmt::{info, panic, unwrap};\nuse embassy_executor::Spawner;\nuse embassy_rp::bind_interrupts;\nuse embassy_rp::peripherals::USB;\nuse embassy_rp::usb::{Driver, Instance, InterruptHandler};\nuse embassy_usb::UsbDevice;\nuse embassy_usb::class::cdc_acm::{CdcAcmClass, State};\nuse embassy_usb::driver::EndpointError;\nuse static_cell::StaticCell;\nuse {defmt_rtt as _, panic_probe as _};\n\nbind_interrupts!(struct Irqs {\n    USBCTRL_IRQ => InterruptHandler<USB>;\n});\n\n#[embassy_executor::main]\nasync fn main(spawner: Spawner) {\n    info!(\"Hello there!\");\n\n    let p = embassy_rp::init(Default::default());\n\n    // Create the driver, from the HAL.\n    let driver = Driver::new(p.USB, Irqs);\n\n    // Create embassy-usb Config\n    let config = {\n        let mut config = embassy_usb::Config::new(0xc0de, 0xcafe);\n        config.manufacturer = Some(\"Embassy\");\n        config.product = Some(\"USB-serial example\");\n        config.serial_number = Some(\"12345678\");\n        config.max_power = 100;\n        config.max_packet_size_0 = 64;\n        config\n    };\n\n    // Create embassy-usb DeviceBuilder using the driver and config.\n    // It needs some buffers for building the descriptors.\n    let mut builder = {\n        static CONFIG_DESCRIPTOR: StaticCell<[u8; 256]> = StaticCell::new();\n        static BOS_DESCRIPTOR: StaticCell<[u8; 256]> = StaticCell::new();\n        static CONTROL_BUF: StaticCell<[u8; 64]> = StaticCell::new();\n\n        let builder = embassy_usb::Builder::new(\n            driver,\n            config,\n            CONFIG_DESCRIPTOR.init([0; 256]),\n            BOS_DESCRIPTOR.init([0; 256]),\n            &mut [], // no msos descriptors\n            CONTROL_BUF.init([0; 64]),\n        );\n        builder\n    };\n\n    // Create classes on the builder.\n    let mut class = {\n        static STATE: StaticCell<State> = StaticCell::new();\n        let state = STATE.init(State::new());\n        CdcAcmClass::new(&mut builder, state, 64)\n    };\n\n    // Build the builder.\n    let usb = builder.build();\n\n    // Run the USB device.\n    spawner.spawn(unwrap!(usb_task(usb)));\n\n    // Do stuff with the class!\n    loop {\n        class.wait_connection().await;\n        info!(\"Connected\");\n        let _ = echo(&mut class).await;\n        info!(\"Disconnected\");\n    }\n}\n\ntype MyUsbDriver = Driver<'static, USB>;\ntype MyUsbDevice = UsbDevice<'static, MyUsbDriver>;\n\n#[embassy_executor::task]\nasync fn usb_task(mut usb: MyUsbDevice) -> ! {\n    usb.run().await\n}\n\nstruct Disconnected {}\n\nimpl From<EndpointError> for Disconnected {\n    fn from(val: EndpointError) -> Self {\n        match val {\n            EndpointError::BufferOverflow => panic!(\"Buffer overflow\"),\n            EndpointError::Disabled => Disconnected {},\n        }\n    }\n}\n\nasync fn echo<'d, T: Instance + 'd>(class: &mut CdcAcmClass<'d, Driver<'d, T>>) -> Result<(), Disconnected> {\n    let mut buf = [0; 64];\n    loop {\n        let n = class.read_packet(&mut buf).await?;\n        let data = &buf[..n];\n        info!(\"data: {:x}\", data);\n        class.write_packet(data).await?;\n    }\n}\n"
  },
  {
    "path": "examples/rp/src/bin/usb_serial_with_handler.rs",
    "content": "//! This example shows how to use USB (Universal Serial Bus) in the RP2040 chip.\n//!\n//! This creates the possibility to send log::info/warn/error/debug! to USB serial port.\n\n#![no_std]\n#![no_main]\n\nuse core::str;\n\nuse embassy_executor::Spawner;\nuse embassy_rp::bind_interrupts;\nuse embassy_rp::peripherals::USB;\nuse embassy_rp::rom_data::reset_to_usb_boot;\nuse embassy_rp::usb::{Driver, InterruptHandler};\nuse embassy_time::Timer;\nuse embassy_usb_logger::ReceiverHandler;\nuse {defmt_rtt as _, panic_probe as _};\n\nbind_interrupts!(struct Irqs {\n    USBCTRL_IRQ => InterruptHandler<USB>;\n});\n\nstruct Handler;\n\nimpl ReceiverHandler for Handler {\n    async fn handle_data(&self, data: &[u8]) {\n        if let Ok(data) = str::from_utf8(data) {\n            let data = data.trim();\n\n            // If you are using elf2uf2-term with the '-t' flag, then when closing the serial monitor,\n            // this will automatically put the pico into boot mode\n            if data == \"q\" || data == \"elf2uf2-term\" {\n                reset_to_usb_boot(0, 0); // Restart the chip\n            } else if data.eq_ignore_ascii_case(\"hello\") {\n                log::info!(\"World!\");\n            } else {\n                log::info!(\"Recieved: {:?}\", data);\n            }\n        }\n    }\n\n    fn new() -> Self {\n        Self\n    }\n}\n\n#[embassy_executor::task]\nasync fn logger_task(driver: Driver<'static, USB>) {\n    embassy_usb_logger::run!(1024, log::LevelFilter::Info, driver, Handler);\n}\n\n#[embassy_executor::main]\nasync fn main(spawner: Spawner) {\n    let p = embassy_rp::init(Default::default());\n    let driver = Driver::new(p.USB, Irqs);\n    spawner.spawn(logger_task(driver).unwrap());\n\n    let mut counter = 0;\n    loop {\n        counter += 1;\n        log::info!(\"Tick {}\", counter);\n        Timer::after_secs(1).await;\n    }\n}\n"
  },
  {
    "path": "examples/rp/src/bin/usb_serial_with_logger.rs",
    "content": "//! This example shows how to use USB (Universal Serial Bus) in the RP2040 chip as well as how to create multiple usb classes for one device\n//!\n//! This creates a USB serial port that echos. It will also print out logging information on a separate serial device\n\n#![no_std]\n#![no_main]\n\nuse defmt::{info, panic};\nuse embassy_executor::Spawner;\nuse embassy_futures::join::join;\nuse embassy_rp::bind_interrupts;\nuse embassy_rp::peripherals::USB;\nuse embassy_rp::usb::{Driver, Instance, InterruptHandler};\nuse embassy_usb::class::cdc_acm::{CdcAcmClass, State};\nuse embassy_usb::driver::EndpointError;\nuse embassy_usb::{Builder, Config};\nuse {defmt_rtt as _, panic_probe as _};\n\nbind_interrupts!(struct Irqs {\n    USBCTRL_IRQ => InterruptHandler<USB>;\n});\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    info!(\"Hello there!\");\n\n    let p = embassy_rp::init(Default::default());\n\n    // Create the driver, from the HAL.\n    let driver = Driver::new(p.USB, Irqs);\n\n    // Create embassy-usb Config\n    let mut config = Config::new(0xc0de, 0xcafe);\n    config.manufacturer = Some(\"Embassy\");\n    config.product = Some(\"USB-serial example\");\n    config.serial_number = Some(\"12345678\");\n    config.max_power = 100;\n    config.max_packet_size_0 = 64;\n\n    // Create embassy-usb DeviceBuilder using the driver and config.\n    // It needs some buffers for building the descriptors.\n    let mut config_descriptor = [0; 256];\n    let mut bos_descriptor = [0; 256];\n    let mut control_buf = [0; 64];\n\n    let mut state = State::new();\n    let mut logger_state = State::new();\n\n    let mut builder = Builder::new(\n        driver,\n        config,\n        &mut config_descriptor,\n        &mut bos_descriptor,\n        &mut [], // no msos descriptors\n        &mut control_buf,\n    );\n\n    // Create classes on the builder.\n    let mut class = CdcAcmClass::new(&mut builder, &mut state, 64);\n\n    // Create a class for the logger\n    let logger_class = CdcAcmClass::new(&mut builder, &mut logger_state, 64);\n\n    // Creates the logger and returns the logger future\n    // Note: You'll need to use log::info! afterwards instead of info! for this to work (this also applies to all the other log::* macros)\n    let log_fut = embassy_usb_logger::with_class!(1024, log::LevelFilter::Info, logger_class);\n\n    // Build the builder.\n    let mut usb = builder.build();\n\n    // Run the USB device.\n    let usb_fut = usb.run();\n\n    // Do stuff with the class!\n    let echo_fut = async {\n        loop {\n            class.wait_connection().await;\n            log::info!(\"Connected\");\n            let _ = echo(&mut class).await;\n            log::info!(\"Disconnected\");\n        }\n    };\n\n    // Run everything concurrently.\n    // If we had made everything `'static` above instead, we could do this using separate tasks instead.\n    join(usb_fut, join(echo_fut, log_fut)).await;\n}\n\nstruct Disconnected {}\n\nimpl From<EndpointError> for Disconnected {\n    fn from(val: EndpointError) -> Self {\n        match val {\n            EndpointError::BufferOverflow => panic!(\"Buffer overflow\"),\n            EndpointError::Disabled => Disconnected {},\n        }\n    }\n}\n\nasync fn echo<'d, T: Instance + 'd>(class: &mut CdcAcmClass<'d, Driver<'d, T>>) -> Result<(), Disconnected> {\n    let mut buf = [0; 64];\n    loop {\n        let n = class.read_packet(&mut buf).await?;\n        let data = &buf[..n];\n        info!(\"data: {:x}\", data);\n        class.write_packet(data).await?;\n    }\n}\n"
  },
  {
    "path": "examples/rp/src/bin/usb_webusb.rs",
    "content": "//! This example shows how to use USB (Universal Serial Bus) in the RP2040 chip.\n//!\n//! This creates a WebUSB capable device that echoes data back to the host.\n//!\n//! To test this in the browser (ideally host this on localhost:8080, to test the landing page\n//! feature):\n//! ```js\n//! (async () => {\n//!     const device = await navigator.usb.requestDevice({ filters: [{ vendorId: 0xf569 }] });\n//!     await device.open();\n//!     await device.claimInterface(1);\n//!     device.transferIn(1, 64).then(data => console.log(data));\n//!     await device.transferOut(1, new Uint8Array([1,2,3]));\n//! })();\n//! ```\n\n#![no_std]\n#![no_main]\n\nuse defmt::info;\nuse embassy_executor::Spawner;\nuse embassy_futures::join::join;\nuse embassy_rp::bind_interrupts;\nuse embassy_rp::peripherals::USB;\nuse embassy_rp::usb::{Driver as UsbDriver, InterruptHandler};\nuse embassy_usb::class::web_usb::{Config as WebUsbConfig, State, Url, WebUsb};\nuse embassy_usb::driver::{Driver, Endpoint, EndpointIn, EndpointOut};\nuse embassy_usb::msos::{self, windows_version};\nuse embassy_usb::types::InterfaceNumber;\nuse embassy_usb::{Builder, Config};\nuse {defmt_rtt as _, panic_probe as _};\n\nbind_interrupts!(struct Irqs {\n    USBCTRL_IRQ => InterruptHandler<USB>;\n});\n\n// This is a randomly generated GUID to allow clients on Windows to find our device\nconst DEVICE_INTERFACE_GUIDS: &[&str] = &[\"{AFB9A6FB-30BA-44BC-9232-806CFC875321}\"];\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let p = embassy_rp::init(Default::default());\n\n    // Create the driver, from the HAL.\n    let driver = UsbDriver::new(p.USB, Irqs);\n\n    // Create embassy-usb Config\n    let mut config = Config::new(0xf569, 0x0001);\n    config.manufacturer = Some(\"Embassy\");\n    config.product = Some(\"WebUSB example\");\n    config.serial_number = Some(\"12345678\");\n    config.max_power = 100;\n    config.max_packet_size_0 = 64;\n\n    // Create embassy-usb DeviceBuilder using the driver and config.\n    // It needs some buffers for building the descriptors.\n    let mut config_descriptor = [0; 256];\n    let mut bos_descriptor = [0; 256];\n    let mut control_buf = [0; 64];\n    let mut msos_descriptor = [0; 512];\n\n    let webusb_config = WebUsbConfig {\n        max_packet_size: 64,\n        vendor_code: 1,\n        // If defined, shows a landing page which the device manufacturer would like the user to visit in order to control their device. Suggest the user to navigate to this URL when the device is connected.\n        landing_url: Some(Url::new(\"http://localhost:8080\")),\n    };\n\n    let mut state = State::new();\n\n    let mut builder = Builder::new(\n        driver,\n        config,\n        &mut config_descriptor,\n        &mut bos_descriptor,\n        &mut msos_descriptor,\n        &mut control_buf,\n    );\n\n    // Add the Microsoft OS Descriptor (MSOS/MOD) descriptor.\n    // We tell Windows that this entire device is compatible with the \"WINUSB\" feature,\n    // which causes it to use the built-in WinUSB driver automatically, which in turn\n    // can be used by libusb/rusb software without needing a custom driver or INF file.\n    // In principle you might want to call msos_feature() just on a specific function,\n    // if your device also has other functions that still use standard class drivers.\n    builder.msos_descriptor(windows_version::WIN8_1, 0);\n    builder.msos_writer().configuration(0);\n    builder.msos_writer().function(InterfaceNumber(0));\n    builder.msos_feature(msos::CompatibleIdFeatureDescriptor::new(\"WINUSB\", \"\"));\n    builder.msos_feature(msos::RegistryPropertyFeatureDescriptor::new(\n        \"DeviceInterfaceGUIDs\",\n        msos::PropertyData::RegMultiSz(DEVICE_INTERFACE_GUIDS),\n    ));\n    builder.msos_writer().function(InterfaceNumber(1));\n    builder.msos_feature(msos::CompatibleIdFeatureDescriptor::new(\"WINUSB\", \"\"));\n    builder.msos_feature(msos::RegistryPropertyFeatureDescriptor::new(\n        \"DeviceInterfaceGUIDs\",\n        msos::PropertyData::RegMultiSz(DEVICE_INTERFACE_GUIDS),\n    ));\n\n    // Create classes on the builder (WebUSB just needs some setup, but doesn't return anything)\n    WebUsb::configure(&mut builder, &mut state, &webusb_config);\n    // Create some USB bulk endpoints for testing.\n    let mut endpoints = WebEndpoints::new(&mut builder, &webusb_config);\n\n    // Build the builder.\n    let mut usb = builder.build();\n\n    // Run the USB device.\n    let usb_fut = usb.run();\n\n    // Do some WebUSB transfers.\n    let webusb_fut = async {\n        loop {\n            endpoints.wait_connected().await;\n            info!(\"Connected\");\n            endpoints.echo().await;\n        }\n    };\n\n    // Run everything concurrently.\n    // If we had made everything `'static` above instead, we could do this using separate tasks instead.\n    join(usb_fut, webusb_fut).await;\n}\n\nstruct WebEndpoints<'d, D: Driver<'d>> {\n    write_ep: D::EndpointIn,\n    read_ep: D::EndpointOut,\n}\n\nimpl<'d, D: Driver<'d>> WebEndpoints<'d, D> {\n    fn new(builder: &mut Builder<'d, D>, config: &'d WebUsbConfig<'d>) -> Self {\n        let mut func = builder.function(0xff, 0x00, 0x00);\n        let mut iface = func.interface();\n        let mut alt = iface.alt_setting(0xff, 0x00, 0x00, None);\n\n        let write_ep = alt.endpoint_bulk_in(None, config.max_packet_size);\n        let read_ep = alt.endpoint_bulk_out(None, config.max_packet_size);\n\n        WebEndpoints { write_ep, read_ep }\n    }\n\n    // Wait until the device's endpoints are enabled.\n    async fn wait_connected(&mut self) {\n        self.read_ep.wait_enabled().await\n    }\n\n    // Echo data back to the host.\n    async fn echo(&mut self) {\n        let mut buf = [0; 64];\n        loop {\n            let n = self.read_ep.read(&mut buf).await.unwrap();\n            let data = &buf[..n];\n            info!(\"Data read: {:x}\", data);\n            self.write_ep.write(data).await.unwrap();\n        }\n    }\n}\n"
  },
  {
    "path": "examples/rp/src/bin/watchdog.rs",
    "content": "//! This example shows how to use Watchdog in the RP2040 chip.\n//!\n//! It does not work with the RP Pico W board. See wifi_blinky.rs or connect external LED and resistor.\n\n#![no_std]\n#![no_main]\n\nuse defmt::info;\nuse embassy_executor::Spawner;\nuse embassy_rp::gpio;\nuse embassy_rp::watchdog::*;\nuse embassy_time::{Duration, Timer};\nuse gpio::{Level, Output};\nuse {defmt_rtt as _, panic_probe as _};\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let p = embassy_rp::init(Default::default());\n    info!(\"Hello world!\");\n\n    let mut watchdog = Watchdog::new(p.WATCHDOG);\n    let mut led = Output::new(p.PIN_25, Level::Low);\n\n    // Set the LED high for 2 seconds so we know when we're about to start the watchdog\n    led.set_high();\n    Timer::after_secs(2).await;\n\n    // Set to watchdog to reset if it's not fed within 1.05 seconds, and start it\n    watchdog.start(Duration::from_millis(5_050));\n    info!(\"Started the watchdog timer\");\n    Timer::after_millis(4_000).await;\n\n    // Blink once a second for 5 seconds, feed the watchdog timer once a second to avoid a reset\n    for _ in 1..=5 {\n        led.set_low();\n        Timer::after_millis(500).await;\n        led.set_high();\n        Timer::after_millis(500).await;\n        info!(\"Feeding watchdog\");\n        watchdog.feed(Duration::from_millis(1_050));\n    }\n\n    info!(\"Stopped feeding, device will reset in 1.05 seconds\");\n    // Blink 10 times per second, not feeding the watchdog.\n    // The processor should reset in 1.05 seconds.\n    loop {\n        led.set_low();\n        Timer::after_millis(100).await;\n        led.set_high();\n        Timer::after_millis(100).await;\n    }\n}\n"
  },
  {
    "path": "examples/rp/src/bin/wifi_ap_tcp_server.rs",
    "content": "//! This example uses the RP Pico W board Wifi chip (cyw43).\n//! Creates an Access point Wifi network and creates a TCP endpoint on port 1234.\n\n#![no_std]\n#![no_main]\n#![allow(async_fn_in_trait)]\n\nuse core::str::from_utf8;\n\nuse cyw43::aligned_bytes;\nuse cyw43_pio::{DEFAULT_CLOCK_DIVIDER, PioSpi};\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_net::tcp::TcpSocket;\nuse embassy_net::{Config, StackResources};\nuse embassy_rp::clocks::RoscRng;\nuse embassy_rp::gpio::{Level, Output};\nuse embassy_rp::peripherals::{DMA_CH0, PIO0};\nuse embassy_rp::pio::{InterruptHandler, Pio};\nuse embassy_rp::{bind_interrupts, dma};\nuse embassy_time::Duration;\nuse embedded_io_async::Write;\nuse static_cell::StaticCell;\nuse {defmt_rtt as _, panic_probe as _};\n\nbind_interrupts!(struct Irqs {\n    PIO0_IRQ_0 => InterruptHandler<PIO0>;\n    DMA_IRQ_0 => dma::InterruptHandler<DMA_CH0>;\n});\n\n#[embassy_executor::task]\nasync fn cyw43_task(runner: cyw43::Runner<'static, cyw43::SpiBus<Output<'static>, PioSpi<'static, PIO0, 0>>>) -> ! {\n    runner.run().await\n}\n\n#[embassy_executor::task]\nasync fn net_task(mut runner: embassy_net::Runner<'static, cyw43::NetDriver<'static>>) -> ! {\n    runner.run().await\n}\n\n#[embassy_executor::main]\nasync fn main(spawner: Spawner) {\n    info!(\"Hello World!\");\n\n    let p = embassy_rp::init(Default::default());\n    let mut rng = RoscRng;\n\n    let fw = aligned_bytes!(\"../../../../cyw43-firmware/43439A0.bin\");\n    let clm = aligned_bytes!(\"../../../../cyw43-firmware/43439A0_clm.bin\");\n    let nvram = aligned_bytes!(\"../../../../cyw43-firmware/nvram_rp2040.bin\");\n\n    // To make flashing faster for development, you may want to flash the firmwares independently\n    // at hardcoded addresses, instead of baking them into the program with `include_bytes!`:\n    //     probe-rs download 43439A0.bin --binary-format bin --chip RP2040 --base-address 0x10100000\n    //     probe-rs download 43439A0_clm.bin --binary-format bin --chip RP2040 --base-address 0x10140000\n    //let fw = unsafe { core::slice::from_raw_parts(0x10100000 as *const u8, 230321) };\n    //let clm = unsafe { core::slice::from_raw_parts(0x10140000 as *const u8, 4752) };\n\n    let pwr = Output::new(p.PIN_23, Level::Low);\n    let cs = Output::new(p.PIN_25, Level::High);\n    let mut pio = Pio::new(p.PIO0, Irqs);\n    let spi = PioSpi::new(\n        &mut pio.common,\n        pio.sm0,\n        DEFAULT_CLOCK_DIVIDER,\n        pio.irq0,\n        cs,\n        p.PIN_24,\n        p.PIN_29,\n        dma::Channel::new(p.DMA_CH0, Irqs),\n    );\n\n    static STATE: StaticCell<cyw43::State> = StaticCell::new();\n    let state = STATE.init(cyw43::State::new());\n    let (net_device, mut control, runner) = cyw43::new(state, pwr, spi, fw, nvram).await;\n    spawner.spawn(unwrap!(cyw43_task(runner)));\n\n    control.init(clm).await;\n    control\n        .set_power_management(cyw43::PowerManagementMode::PowerSave)\n        .await;\n\n    // Use a link-local address for communication without DHCP server\n    let config = Config::ipv4_static(embassy_net::StaticConfigV4 {\n        address: embassy_net::Ipv4Cidr::new(embassy_net::Ipv4Address::new(169, 254, 1, 1), 16),\n        dns_servers: heapless::Vec::new(),\n        gateway: None,\n    });\n\n    // Generate random seed\n    let seed = rng.next_u64();\n\n    // Init network stack\n    static RESOURCES: StaticCell<StackResources<3>> = StaticCell::new();\n    let (stack, runner) = embassy_net::new(net_device, config, RESOURCES.init(StackResources::new()), seed);\n\n    spawner.spawn(unwrap!(net_task(runner)));\n\n    //control.start_ap_open(\"cyw43\", 5).await;\n    control.start_ap_wpa2(\"cyw43\", \"password\", 5).await;\n\n    // And now we can use it!\n\n    let mut rx_buffer = [0; 4096];\n    let mut tx_buffer = [0; 4096];\n    let mut buf = [0; 4096];\n\n    loop {\n        let mut socket = TcpSocket::new(stack, &mut rx_buffer, &mut tx_buffer);\n        socket.set_timeout(Some(Duration::from_secs(10)));\n\n        control.gpio_set(0, false).await;\n        info!(\"Listening on TCP:1234...\");\n        if let Err(e) = socket.accept(1234).await {\n            warn!(\"accept error: {:?}\", e);\n            continue;\n        }\n\n        info!(\"Received connection from {:?}\", socket.remote_endpoint());\n        control.gpio_set(0, true).await;\n\n        loop {\n            let n = match socket.read(&mut buf).await {\n                Ok(0) => {\n                    warn!(\"read EOF\");\n                    break;\n                }\n                Ok(n) => n,\n                Err(e) => {\n                    warn!(\"read error: {:?}\", e);\n                    break;\n                }\n            };\n\n            info!(\"rxd {}\", from_utf8(&buf[..n]).unwrap());\n\n            match socket.write_all(&buf[..n]).await {\n                Ok(()) => {}\n                Err(e) => {\n                    warn!(\"write error: {:?}\", e);\n                    break;\n                }\n            };\n        }\n    }\n}\n"
  },
  {
    "path": "examples/rp/src/bin/wifi_blinky.rs",
    "content": "//! This example test the RP Pico W on board LED.\n//!\n//! It does not work with the RP Pico board. See blinky.rs.\n\n#![no_std]\n#![no_main]\n\nuse cyw43::aligned_bytes;\nuse cyw43_pio::{DEFAULT_CLOCK_DIVIDER, PioSpi};\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_rp::gpio::{Level, Output};\nuse embassy_rp::peripherals::{DMA_CH0, PIO0};\nuse embassy_rp::pio::{InterruptHandler, Pio};\nuse embassy_rp::{bind_interrupts, dma};\nuse embassy_time::{Duration, Timer};\nuse static_cell::StaticCell;\nuse {defmt_rtt as _, panic_probe as _};\n\nbind_interrupts!(struct Irqs {\n    PIO0_IRQ_0 => InterruptHandler<PIO0>;\n    DMA_IRQ_0 => dma::InterruptHandler<DMA_CH0>;\n});\n\n#[embassy_executor::task]\nasync fn cyw43_task(runner: cyw43::Runner<'static, cyw43::SpiBus<Output<'static>, PioSpi<'static, PIO0, 0>>>) -> ! {\n    runner.run().await\n}\n\n#[embassy_executor::main]\nasync fn main(spawner: Spawner) {\n    let p = embassy_rp::init(Default::default());\n    let fw = aligned_bytes!(\"../../../../cyw43-firmware/43439A0.bin\");\n    let clm = aligned_bytes!(\"../../../../cyw43-firmware/43439A0_clm.bin\");\n    let nvram = aligned_bytes!(\"../../../../cyw43-firmware/nvram_rp2040.bin\");\n\n    // To make flashing faster for development, you may want to flash the firmwares independently\n    // at hardcoded addresses, instead of baking them into the program with `include_bytes!`:\n    //     probe-rs download ../../cyw43-firmware/43439A0.bin --binary-format bin --chip RP2040 --base-address 0x10100000\n    //     probe-rs download ../../cyw43-firmware/43439A0_clm.bin --binary-format bin --chip RP2040 --base-address 0x10140000\n    //let fw = unsafe { core::slice::from_raw_parts(0x10100000 as *const u8, 230321) };\n    //let clm = unsafe { core::slice::from_raw_parts(0x10140000 as *const u8, 4752) };\n\n    let pwr = Output::new(p.PIN_23, Level::Low);\n    let cs = Output::new(p.PIN_25, Level::High);\n    let mut pio = Pio::new(p.PIO0, Irqs);\n    let spi = PioSpi::new(\n        &mut pio.common,\n        pio.sm0,\n        DEFAULT_CLOCK_DIVIDER,\n        pio.irq0,\n        cs,\n        p.PIN_24,\n        p.PIN_29,\n        dma::Channel::new(p.DMA_CH0, Irqs),\n    );\n\n    static STATE: StaticCell<cyw43::State> = StaticCell::new();\n    let state = STATE.init(cyw43::State::new());\n    let (_net_device, mut control, runner) = cyw43::new(state, pwr, spi, fw, nvram).await;\n    spawner.spawn(unwrap!(cyw43_task(runner)));\n\n    control.init(clm).await;\n    control\n        .set_power_management(cyw43::PowerManagementMode::PowerSave)\n        .await;\n\n    let delay = Duration::from_secs(1);\n    loop {\n        info!(\"led on!\");\n        control.gpio_set(0, true).await;\n        Timer::after(delay).await;\n\n        info!(\"led off!\");\n        control.gpio_set(0, false).await;\n        Timer::after(delay).await;\n    }\n}\n"
  },
  {
    "path": "examples/rp/src/bin/wifi_scan.rs",
    "content": "//! This example uses the RP Pico W board Wifi chip (cyw43).\n//! Scans Wifi for ssid names.\n\n#![no_std]\n#![no_main]\n#![allow(async_fn_in_trait)]\n\nuse core::str;\n\nuse cyw43::aligned_bytes;\nuse cyw43_pio::{DEFAULT_CLOCK_DIVIDER, PioSpi};\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_rp::gpio::{Level, Output};\nuse embassy_rp::peripherals::{DMA_CH0, PIO0};\nuse embassy_rp::pio::{InterruptHandler, Pio};\nuse embassy_rp::{bind_interrupts, dma};\nuse static_cell::StaticCell;\nuse {defmt_rtt as _, panic_probe as _};\n\nbind_interrupts!(struct Irqs {\n    PIO0_IRQ_0 => InterruptHandler<PIO0>;\n    DMA_IRQ_0 => dma::InterruptHandler<DMA_CH0>;\n});\n\n#[embassy_executor::task]\nasync fn cyw43_task(runner: cyw43::Runner<'static, cyw43::SpiBus<Output<'static>, PioSpi<'static, PIO0, 0>>>) -> ! {\n    runner.run().await\n}\n\n#[embassy_executor::main]\nasync fn main(spawner: Spawner) {\n    info!(\"Hello World!\");\n\n    let p = embassy_rp::init(Default::default());\n\n    let fw = aligned_bytes!(\"../../../../cyw43-firmware/43439A0.bin\");\n    let clm = aligned_bytes!(\"../../../../cyw43-firmware/43439A0_clm.bin\");\n    let nvram = aligned_bytes!(\"../../../../cyw43-firmware/nvram_rp2040.bin\");\n\n    // To make flashing faster for development, you may want to flash the firmwares independently\n    // at hardcoded addresses, instead of baking them into the program with `include_bytes!`:\n    //     probe-rs download 43439A0.bin --binary-format bin --chip RP2040 --base-address 0x10100000\n    //     probe-rs download 43439A0_clm.bin --binary-format bin --chip RP2040 --base-address 0x10140000\n    //let fw = unsafe { core::slice::from_raw_parts(0x10100000 as *const u8, 230321) };\n    //let clm = unsafe { core::slice::from_raw_parts(0x10140000 as *const u8, 4752) };\n\n    let pwr = Output::new(p.PIN_23, Level::Low);\n    let cs = Output::new(p.PIN_25, Level::High);\n    let mut pio = Pio::new(p.PIO0, Irqs);\n    let spi = PioSpi::new(\n        &mut pio.common,\n        pio.sm0,\n        DEFAULT_CLOCK_DIVIDER,\n        pio.irq0,\n        cs,\n        p.PIN_24,\n        p.PIN_29,\n        dma::Channel::new(p.DMA_CH0, Irqs),\n    );\n\n    static STATE: StaticCell<cyw43::State> = StaticCell::new();\n    let state = STATE.init(cyw43::State::new());\n    let (_net_device, mut control, runner) = cyw43::new(state, pwr, spi, fw, nvram).await;\n    spawner.spawn(unwrap!(cyw43_task(runner)));\n\n    control.init(clm).await;\n    control\n        .set_power_management(cyw43::PowerManagementMode::PowerSave)\n        .await;\n\n    let mut scanner = control.scan(Default::default()).await;\n    while let Some(bss) = scanner.next().await {\n        if let Ok(ssid_str) = str::from_utf8(&bss.ssid) {\n            info!(\"scanned {} == {:x}\", ssid_str, bss.bssid);\n        }\n    }\n}\n"
  },
  {
    "path": "examples/rp/src/bin/wifi_tcp_server.rs",
    "content": "//! This example uses the RP Pico W board Wifi chip (cyw43).\n//! Connects to specified Wifi network and creates a TCP endpoint on port 1234.\n\n#![no_std]\n#![no_main]\n#![allow(async_fn_in_trait)]\n\nuse core::str::from_utf8;\n\nuse cyw43::{JoinOptions, aligned_bytes};\nuse cyw43_pio::{DEFAULT_CLOCK_DIVIDER, PioSpi};\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_net::tcp::TcpSocket;\nuse embassy_net::{Config, StackResources};\nuse embassy_rp::clocks::RoscRng;\nuse embassy_rp::gpio::{Level, Output};\nuse embassy_rp::peripherals::{DMA_CH0, PIO0};\nuse embassy_rp::pio::{InterruptHandler, Pio};\nuse embassy_rp::{bind_interrupts, dma};\nuse embassy_time::Duration;\nuse embedded_io_async::Write;\nuse static_cell::StaticCell;\nuse {defmt_rtt as _, panic_probe as _};\n\nbind_interrupts!(struct Irqs {\n    PIO0_IRQ_0 => InterruptHandler<PIO0>;\n    DMA_IRQ_0 => dma::InterruptHandler<DMA_CH0>;\n});\n\nconst WIFI_NETWORK: &str = \"ssid\"; // change to your network SSID\nconst WIFI_PASSWORD: &str = \"pwd\"; // change to your network password\n\n#[embassy_executor::task]\nasync fn cyw43_task(runner: cyw43::Runner<'static, cyw43::SpiBus<Output<'static>, PioSpi<'static, PIO0, 0>>>) -> ! {\n    runner.run().await\n}\n\n#[embassy_executor::task]\nasync fn net_task(mut runner: embassy_net::Runner<'static, cyw43::NetDriver<'static>>) -> ! {\n    runner.run().await\n}\n\n#[embassy_executor::main]\nasync fn main(spawner: Spawner) {\n    info!(\"Hello World!\");\n\n    let p = embassy_rp::init(Default::default());\n    let mut rng = RoscRng;\n\n    let fw = aligned_bytes!(\"../../../../cyw43-firmware/43439A0.bin\");\n    let clm = aligned_bytes!(\"../../../../cyw43-firmware/43439A0_clm.bin\");\n    let nvram = aligned_bytes!(\"../../../../cyw43-firmware/nvram_rp2040.bin\");\n\n    // To make flashing faster for development, you may want to flash the firmwares independently\n    // at hardcoded addresses, instead of baking them into the program with `include_bytes!`:\n    //     probe-rs download 43439A0.bin --binary-format bin --chip RP2040 --base-address 0x10100000\n    //     probe-rs download 43439A0_clm.bin --binary-format bin --chip RP2040 --base-address 0x10140000\n    //let fw = unsafe { core::slice::from_raw_parts(0x10100000 as *const u8, 230321) };\n    //let clm = unsafe { core::slice::from_raw_parts(0x10140000 as *const u8, 4752) };\n\n    let pwr = Output::new(p.PIN_23, Level::Low);\n    let cs = Output::new(p.PIN_25, Level::High);\n    let mut pio = Pio::new(p.PIO0, Irqs);\n    let spi = PioSpi::new(\n        &mut pio.common,\n        pio.sm0,\n        DEFAULT_CLOCK_DIVIDER,\n        pio.irq0,\n        cs,\n        p.PIN_24,\n        p.PIN_29,\n        dma::Channel::new(p.DMA_CH0, Irqs),\n    );\n\n    static STATE: StaticCell<cyw43::State> = StaticCell::new();\n    let state = STATE.init(cyw43::State::new());\n    let (net_device, mut control, runner) = cyw43::new(state, pwr, spi, fw, nvram).await;\n    spawner.spawn(unwrap!(cyw43_task(runner)));\n\n    control.init(clm).await;\n    control\n        .set_power_management(cyw43::PowerManagementMode::PowerSave)\n        .await;\n\n    let config = Config::dhcpv4(Default::default());\n    //let config = embassy_net::Config::ipv4_static(embassy_net::StaticConfigV4 {\n    //    address: Ipv4Cidr::new(Ipv4Address::new(192, 168, 69, 2), 24),\n    //    dns_servers: Vec::new(),\n    //    gateway: Some(Ipv4Address::new(192, 168, 69, 1)),\n    //});\n\n    // Generate random seed\n    let seed = rng.next_u64();\n\n    // Init network stack\n    static RESOURCES: StaticCell<StackResources<3>> = StaticCell::new();\n    let (stack, runner) = embassy_net::new(net_device, config, RESOURCES.init(StackResources::new()), seed);\n\n    spawner.spawn(unwrap!(net_task(runner)));\n\n    while let Err(err) = control\n        .join(WIFI_NETWORK, JoinOptions::new(WIFI_PASSWORD.as_bytes()))\n        .await\n    {\n        info!(\"join failed: {:?}\", err);\n    }\n\n    info!(\"waiting for link...\");\n    stack.wait_link_up().await;\n\n    info!(\"waiting for DHCP...\");\n    stack.wait_config_up().await;\n\n    // And now we can use it!\n    info!(\"Stack is up!\");\n\n    let mut rx_buffer = [0; 4096];\n    let mut tx_buffer = [0; 4096];\n    let mut buf = [0; 4096];\n\n    loop {\n        let mut socket = TcpSocket::new(stack, &mut rx_buffer, &mut tx_buffer);\n        socket.set_timeout(Some(Duration::from_secs(10)));\n\n        control.gpio_set(0, false).await;\n        info!(\"Listening on TCP:1234...\");\n        if let Err(e) = socket.accept(1234).await {\n            warn!(\"accept error: {:?}\", e);\n            continue;\n        }\n\n        info!(\"Received connection from {:?}\", socket.remote_endpoint());\n        control.gpio_set(0, true).await;\n\n        loop {\n            let n = match socket.read(&mut buf).await {\n                Ok(0) => {\n                    warn!(\"read EOF\");\n                    break;\n                }\n                Ok(n) => n,\n                Err(e) => {\n                    warn!(\"read error: {:?}\", e);\n                    break;\n                }\n            };\n\n            info!(\"rxd {}\", from_utf8(&buf[..n]).unwrap());\n\n            match socket.write_all(&buf[..n]).await {\n                Ok(()) => {}\n                Err(e) => {\n                    warn!(\"write error: {:?}\", e);\n                    break;\n                }\n            };\n        }\n    }\n}\n"
  },
  {
    "path": "examples/rp/src/bin/wifi_webrequest.rs",
    "content": "//! This example uses the RP Pico W board Wifi chip (cyw43).\n//! Connects to Wifi network and makes a web request to httpbin.org.\n\n#![no_std]\n#![no_main]\n\nuse core::str::from_utf8;\n\nuse cyw43::{JoinOptions, aligned_bytes};\nuse cyw43_pio::{DEFAULT_CLOCK_DIVIDER, PioSpi};\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_net::dns::DnsSocket;\nuse embassy_net::tcp::client::{TcpClient, TcpClientState};\nuse embassy_net::{Config, StackResources};\nuse embassy_rp::clocks::RoscRng;\nuse embassy_rp::gpio::{Level, Output};\nuse embassy_rp::peripherals::{DMA_CH0, PIO0};\nuse embassy_rp::pio::{InterruptHandler, Pio};\nuse embassy_rp::{bind_interrupts, dma};\nuse embassy_time::{Duration, Timer};\nuse reqwless::client::HttpClient;\n// Uncomment these for TLS requests:\n// use reqwless::client::{HttpClient, TlsConfig, TlsVerify};\nuse reqwless::request::Method;\nuse serde::Deserialize;\nuse serde_json_core::from_slice;\nuse static_cell::StaticCell;\nuse {defmt_rtt as _, panic_probe as _};\n\nbind_interrupts!(struct Irqs {\n    PIO0_IRQ_0 => InterruptHandler<PIO0>;\n    DMA_IRQ_0 => dma::InterruptHandler<DMA_CH0>;\n});\n\nconst WIFI_NETWORK: &str = \"ssid\"; // change to your network SSID\nconst WIFI_PASSWORD: &str = \"pwd\"; // change to your network password\n\n#[embassy_executor::task]\nasync fn cyw43_task(runner: cyw43::Runner<'static, cyw43::SpiBus<Output<'static>, PioSpi<'static, PIO0, 0>>>) -> ! {\n    runner.run().await\n}\n\n#[embassy_executor::task]\nasync fn net_task(mut runner: embassy_net::Runner<'static, cyw43::NetDriver<'static>>) -> ! {\n    runner.run().await\n}\n\n#[embassy_executor::main]\nasync fn main(spawner: Spawner) {\n    info!(\"Hello World!\");\n\n    let p = embassy_rp::init(Default::default());\n    let mut rng = RoscRng;\n\n    let fw = aligned_bytes!(\"../../../../cyw43-firmware/43439A0.bin\");\n    let clm = aligned_bytes!(\"../../../../cyw43-firmware/43439A0_clm.bin\");\n    let nvram = aligned_bytes!(\"../../../../cyw43-firmware/nvram_rp2040.bin\");\n    // To make flashing faster for development, you may want to flash the firmwares independently\n    // at hardcoded addresses, instead of baking them into the program with `include_bytes!`:\n    //     probe-rs download 43439A0.bin --binary-format bin --chip RP2040 --base-address 0x10100000\n    //     probe-rs download 43439A0_clm.bin --binary-format bin --chip RP2040 --base-address 0x10140000\n    // let fw = unsafe { core::slice::from_raw_parts(0x10100000 as *const u8, 230321) };\n    // let clm = unsafe { core::slice::from_raw_parts(0x10140000 as *const u8, 4752) };\n\n    let pwr = Output::new(p.PIN_23, Level::Low);\n    let cs = Output::new(p.PIN_25, Level::High);\n    let mut pio = Pio::new(p.PIO0, Irqs);\n    let spi = PioSpi::new(\n        &mut pio.common,\n        pio.sm0,\n        DEFAULT_CLOCK_DIVIDER,\n        pio.irq0,\n        cs,\n        p.PIN_24,\n        p.PIN_29,\n        dma::Channel::new(p.DMA_CH0, Irqs),\n    );\n\n    static STATE: StaticCell<cyw43::State> = StaticCell::new();\n    let state = STATE.init(cyw43::State::new());\n    let (net_device, mut control, runner) = cyw43::new(state, pwr, spi, fw, nvram).await;\n    spawner.spawn(unwrap!(cyw43_task(runner)));\n\n    control.init(clm).await;\n    control\n        .set_power_management(cyw43::PowerManagementMode::PowerSave)\n        .await;\n\n    let config = Config::dhcpv4(Default::default());\n    // Use static IP configuration instead of DHCP\n    //let config = embassy_net::Config::ipv4_static(embassy_net::StaticConfigV4 {\n    //    address: Ipv4Cidr::new(Ipv4Address::new(192, 168, 69, 2), 24),\n    //    dns_servers: Vec::new(),\n    //    gateway: Some(Ipv4Address::new(192, 168, 69, 1)),\n    //});\n\n    // Generate random seed\n    let seed = rng.next_u64();\n\n    // Init network stack\n    static RESOURCES: StaticCell<StackResources<5>> = StaticCell::new();\n    let (stack, runner) = embassy_net::new(net_device, config, RESOURCES.init(StackResources::new()), seed);\n\n    spawner.spawn(unwrap!(net_task(runner)));\n\n    while let Err(err) = control\n        .join(WIFI_NETWORK, JoinOptions::new(WIFI_PASSWORD.as_bytes()))\n        .await\n    {\n        info!(\"join failed: {:?}\", err);\n    }\n\n    info!(\"waiting for link...\");\n    stack.wait_link_up().await;\n\n    info!(\"waiting for DHCP...\");\n    stack.wait_config_up().await;\n\n    // And now we can use it!\n    info!(\"Stack is up!\");\n\n    // And now we can use it!\n\n    loop {\n        let mut rx_buffer = [0; 4096];\n        // Uncomment these for TLS requests:\n        // let mut tls_read_buffer = [0; 16640];\n        // let mut tls_write_buffer = [0; 16640];\n\n        let client_state = TcpClientState::<1, 4096, 4096>::new();\n        let tcp_client = TcpClient::new(stack, &client_state);\n        let dns_client = DnsSocket::new(stack);\n        // Uncomment these for TLS requests:\n        // let tls_config = TlsConfig::new(seed, &mut tls_read_buffer, &mut tls_write_buffer, TlsVerify::None);\n\n        // Using non-TLS HTTP for this example\n        let mut http_client = HttpClient::new(&tcp_client, &dns_client);\n        let url = \"http://httpbin.org/json\";\n        // For TLS requests, use this instead:\n        // let mut http_client = HttpClient::new_with_tls(&tcp_client, &dns_client, tls_config);\n        // let url = \"https://httpbin.org/json\";\n\n        info!(\"connecting to {}\", &url);\n\n        let mut request = match http_client.request(Method::GET, url).await {\n            Ok(req) => req,\n            Err(e) => {\n                error!(\"Failed to make HTTP request: {:?}\", e);\n                Timer::after(Duration::from_secs(5)).await;\n                continue;\n            }\n        };\n\n        let response = match request.send(&mut rx_buffer).await {\n            Ok(resp) => resp,\n            Err(e) => {\n                error!(\"Failed to send HTTP request: {:?}\", e);\n                Timer::after(Duration::from_secs(5)).await;\n                continue;\n            }\n        };\n\n        info!(\"Response status: {}\", response.status.0);\n\n        let body_bytes = match response.body().read_to_end().await {\n            Ok(b) => b,\n            Err(_e) => {\n                error!(\"Failed to read response body\");\n                Timer::after(Duration::from_secs(5)).await;\n                continue;\n            }\n        };\n\n        let body = match from_utf8(body_bytes) {\n            Ok(b) => b,\n            Err(_e) => {\n                error!(\"Failed to parse response body as UTF-8\");\n                Timer::after(Duration::from_secs(5)).await;\n                continue;\n            }\n        };\n        info!(\"Response body length: {} bytes\", body.len());\n\n        // Parse the JSON response from httpbin.org/json\n        #[derive(Deserialize)]\n        struct SlideShow<'a> {\n            author: &'a str,\n            title: &'a str,\n        }\n\n        #[derive(Deserialize)]\n        struct HttpBinResponse<'a> {\n            #[serde(borrow)]\n            slideshow: SlideShow<'a>,\n        }\n\n        let bytes = body.as_bytes();\n        match from_slice::<HttpBinResponse>(bytes) {\n            Ok((output, _used)) => {\n                info!(\"Successfully parsed JSON response!\");\n                info!(\"Slideshow title: {:?}\", output.slideshow.title);\n                info!(\"Slideshow author: {:?}\", output.slideshow.author);\n            }\n            Err(e) => {\n                error!(\"Failed to parse JSON response: {}\", Debug2Format(&e));\n                // Log preview of response for debugging\n                let preview = if body.len() > 200 { &body[..200] } else { body };\n                info!(\"Response preview: {:?}\", preview);\n            }\n        }\n\n        Timer::after(Duration::from_secs(5)).await;\n    }\n}\n"
  },
  {
    "path": "examples/rp/src/bin/zerocopy.rs",
    "content": "//! This example shows how to use `zerocopy_channel` from `embassy_sync` for\n//! sending large values between two tasks without copying.\n//! The example also shows how to use the RP2040 ADC with DMA.\n#![no_std]\n#![no_main]\n\nuse core::sync::atomic::{AtomicU16, Ordering};\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_rp::adc::{self, Adc, Async, Config, InterruptHandler};\nuse embassy_rp::gpio::Pull;\nuse embassy_rp::peripherals::DMA_CH0;\nuse embassy_rp::{bind_interrupts, dma};\nuse embassy_sync::blocking_mutex::raw::NoopRawMutex;\nuse embassy_sync::zerocopy_channel::{Channel, Receiver, Sender};\nuse embassy_time::{Duration, Ticker, Timer};\nuse static_cell::StaticCell;\nuse {defmt_rtt as _, panic_probe as _};\n\ntype SampleBuffer = [u16; 512];\n\nbind_interrupts!(struct Irqs {\n    ADC_IRQ_FIFO => InterruptHandler;\n    DMA_IRQ_0 => dma::InterruptHandler<DMA_CH0>;\n});\n\nconst BLOCK_SIZE: usize = 512;\nconst NUM_BLOCKS: usize = 2;\nstatic MAX: AtomicU16 = AtomicU16::new(0);\n\nstruct AdcParts {\n    adc: Adc<'static, Async>,\n    pin: adc::Channel<'static>,\n    dma: dma::Channel<'static>,\n}\n\n#[embassy_executor::main]\nasync fn main(spawner: Spawner) {\n    let p = embassy_rp::init(Default::default());\n    info!(\"Here we go!\");\n\n    let adc_parts = AdcParts {\n        adc: Adc::new(p.ADC, Irqs, Config::default()),\n        pin: adc::Channel::new_pin(p.PIN_29, Pull::None),\n        dma: dma::Channel::new(p.DMA_CH0, Irqs),\n    };\n\n    static BUF: StaticCell<[SampleBuffer; NUM_BLOCKS]> = StaticCell::new();\n    let buf = BUF.init([[0; BLOCK_SIZE]; NUM_BLOCKS]);\n\n    static CHANNEL: StaticCell<Channel<'_, NoopRawMutex, SampleBuffer>> = StaticCell::new();\n    let channel = CHANNEL.init(Channel::new(buf));\n    let (sender, receiver) = channel.split();\n\n    spawner.spawn(consumer(receiver).unwrap());\n    spawner.spawn(producer(sender, adc_parts).unwrap());\n\n    let mut ticker = Ticker::every(Duration::from_secs(1));\n    loop {\n        ticker.next().await;\n        let max = MAX.load(Ordering::Relaxed);\n        info!(\"latest block's max value: {:?}\", max);\n    }\n}\n\n#[embassy_executor::task]\nasync fn producer(mut sender: Sender<'static, NoopRawMutex, SampleBuffer>, mut adc: AdcParts) {\n    loop {\n        // Obtain a free buffer from the channel\n        let buf = sender.send().await;\n\n        // Fill it with data\n        adc.adc.read_many(&mut adc.pin, buf, 1, &mut adc.dma).await.unwrap();\n\n        // Notify the channel that the buffer is now ready to be received\n        sender.send_done();\n    }\n}\n\n#[embassy_executor::task]\nasync fn consumer(mut receiver: Receiver<'static, NoopRawMutex, SampleBuffer>) {\n    loop {\n        // Receive a buffer from the channel\n        let buf = receiver.receive().await;\n\n        // Simulate using the data, while the producer is filling up the next buffer\n        Timer::after_micros(1000).await;\n        let max = buf.iter().max().unwrap();\n        MAX.store(*max, Ordering::Relaxed);\n\n        // Notify the channel that the buffer is now ready to be reused\n        receiver.receive_done();\n    }\n}\n"
  },
  {
    "path": "examples/rp235x/.cargo/config.toml",
    "content": "[target.'cfg(all(target_arch = \"arm\", target_os = \"none\"))']\nrunner = \"probe-rs run --chip RP235x\"\n#runner = \"elf2uf2-rs -d\"\n#runner = \"picotool load -u -v -x -t elf\"\n\n[build]\ntarget = \"thumbv8m.main-none-eabihf\"\n\n[env]\nDEFMT_LOG = \"debug\"\n"
  },
  {
    "path": "examples/rp235x/Cargo.toml",
    "content": "[package]\nedition = \"2024\"\nname = \"embassy-rp2350-examples\"\nversion = \"0.1.0\"\nlicense = \"MIT OR Apache-2.0\"\n\npublish = false\n\n[dependencies]\nembassy-embedded-hal = { version = \"0.6.0\", path = \"../../embassy-embedded-hal\", features = [\"defmt\"] }\nembassy-sync = { version = \"0.8.0\", path = \"../../embassy-sync\", features = [\"defmt\"] }\nembassy-executor = { version = \"0.10.0\", path = \"../../embassy-executor\", features = [\"platform-cortex-m\", \"executor-thread\", \"executor-interrupt\", \"defmt\"] }\nembassy-time = { version = \"0.5.1\", path = \"../../embassy-time\", features = [\"defmt\", \"defmt-timestamp-uptime\"] }\nembassy-rp = { version = \"0.10.0\", path = \"../../embassy-rp\", features = [\"defmt\", \"unstable-pac\", \"time-driver\", \"critical-section-impl\", \"rp235xa\", \"binary-info\"] }\nembassy-usb = { version = \"0.6.0\", path = \"../../embassy-usb\", features = [\"defmt\"] }\nembassy-net = { version = \"0.9.0\", path = \"../../embassy-net\", features = [\"defmt\", \"icmp\", \"tcp\", \"udp\", \"raw\", \"dhcpv4\", \"medium-ethernet\", \"dns\"] }\nembassy-net-wiznet = { version = \"0.3.0\", path = \"../../embassy-net-wiznet\", features = [\"defmt\"] }\nembassy-futures = { version = \"0.1.2\", path = \"../../embassy-futures\" }\nembassy-usb-logger = { version = \"0.6.0\", path = \"../../embassy-usb-logger\" }\ncyw43 = { version = \"0.7.0\", path = \"../../cyw43\", features = [\"defmt\", \"firmware-logs\"] }\ncyw43-pio = { version = \"0.10.0\", path = \"../../cyw43-pio\", features = [\"defmt\"] }\n\ndefmt = \"1.0.1\"\ndefmt-rtt = \"1.0.0\"\nfixed = \"1.23.1\"\nfixed-macro = \"1.2\"\n\nserde = { version = \"1.0.203\", default-features = false, features = [\"derive\"] }\nserde-json-core = \"0.5.1\"\n\n# for assign resources example\nassign-resources = { git = \"https://github.com/adamgreig/assign-resources\", rev = \"bd22cb7a92031fb16f74a5da42469d466c33383e\" }\n\n# for TB6612FNG example\ntb6612fng = \"1.0.0\"\n\n#cortex-m = { version = \"0.7.6\", features = [\"critical-section-single-core\"] }\ncortex-m = { version = \"0.7.6\", features = [\"inline-asm\"] }\ncortex-m-rt = \"0.7.0\"\ncritical-section = \"1.1\"\npanic-probe = { version = \"1.0.0\", features = [\"print-defmt\"] }\ndisplay-interface-spi = \"0.5.0\"\nembedded-graphics = \"0.8.1\"\nmipidsi = \"0.8.0\"\ndisplay-interface = \"0.5.0\"\nbyte-slice-cast = { version = \"1.2.0\", default-features = false }\nsmart-leds = \"0.3.0\"\nheapless = \"0.9\"\nusbd-hid = \"0.10.0\"\n\nembedded-hal-1 = { package = \"embedded-hal\", version = \"1.0\" }\nembedded-hal-async = \"1.0\"\nembedded-hal-bus = { version = \"0.1\", features = [\"async\"] }\nembedded-io-async = { version = \"0.7.0\", features = [\"defmt\"] }\nembedded-storage = { version = \"0.3\" }\nstatic_cell = \"2.1\"\nportable-atomic = { version = \"1.5\", features = [\"critical-section\"] }\nlog = \"0.4\"\nembedded-sdmmc = \"0.7.0\"\n\n[profile.release]\n# Enable generation of debug symbols even on release builds\ndebug = true\n\n[package.metadata.embassy]\nbuild = [\n  { target = \"thumbv8m.main-none-eabihf\", artifact-dir = \"out/examples/rp235x\" }\n]\n"
  },
  {
    "path": "examples/rp235x/build.rs",
    "content": "//! This build script copies the `memory.x` file from the crate root into\n//! a directory where the linker can always find it at build time.\n//! For many projects this is optional, as the linker always searches the\n//! project root directory -- wherever `Cargo.toml` is. However, if you\n//! are using a workspace or have a more complicated build setup, this\n//! build script becomes required. Additionally, by requesting that\n//! Cargo re-run the build script whenever `memory.x` is changed,\n//! updating `memory.x` ensures a rebuild of the application with the\n//! new memory settings.\n\nuse std::env;\nuse std::fs::File;\nuse std::io::Write;\nuse std::path::PathBuf;\n\nfn main() {\n    // Put `memory.x` in our output directory and ensure it's\n    // on the linker search path.\n    let out = &PathBuf::from(env::var_os(\"OUT_DIR\").unwrap());\n    File::create(out.join(\"memory.x\"))\n        .unwrap()\n        .write_all(include_bytes!(\"memory.x\"))\n        .unwrap();\n    println!(\"cargo:rustc-link-search={}\", out.display());\n\n    // By default, Cargo will re-run a build script whenever\n    // any file in the project changes. By specifying `memory.x`\n    // here, we ensure the build script is only re-run when\n    // `memory.x` is changed.\n    println!(\"cargo:rerun-if-changed=memory.x\");\n\n    println!(\"cargo:rustc-link-arg-bins=--nmagic\");\n    println!(\"cargo:rustc-link-arg-bins=-Tlink.x\");\n    println!(\"cargo:rustc-link-arg-bins=-Tdefmt.x\");\n}\n"
  },
  {
    "path": "examples/rp235x/memory.x",
    "content": "MEMORY {\n    /*\n     * The RP2350 has either external or internal flash.\n     *\n     * 2 MiB is a safe default here, although a Pico 2 has 4 MiB.\n     */\n    FLASH : ORIGIN = 0x10000000, LENGTH = 2048K\n    /*\n     * RAM consists of 8 banks, SRAM0-SRAM7, with a striped mapping.\n     * This is usually good for performance, as it distributes load on\n     * those banks evenly.\n     */\n    RAM : ORIGIN = 0x20000000, LENGTH = 512K\n    /*\n     * RAM banks 8 and 9 use a direct mapping. They can be used to have\n     * memory areas dedicated for some specific job, improving predictability\n     * of access times.\n     * Example: Separate stacks for core0 and core1.\n     */\n    SRAM8 : ORIGIN = 0x20080000, LENGTH = 4K\n    SRAM9 : ORIGIN = 0x20081000, LENGTH = 4K\n}\n\nSECTIONS {\n    /* ### Boot ROM info\n     *\n     * Goes after .vector_table, to keep it in the first 4K of flash\n     * where the Boot ROM (and picotool) can find it\n     */\n    .start_block : ALIGN(4)\n    {\n        __start_block_addr = .;\n        KEEP(*(.start_block));\n        KEEP(*(.boot_info));\n    } > FLASH\n\n} INSERT AFTER .vector_table;\n\n/* move .text to start /after/ the boot info */\n_stext = ADDR(.start_block) + SIZEOF(.start_block);\n\nSECTIONS {\n    /* ### Picotool 'Binary Info' Entries\n     *\n     * Picotool looks through this block (as we have pointers to it in our\n     * header) to find interesting information.\n     */\n    .bi_entries : ALIGN(4)\n    {\n        /* We put this in the header */\n        __bi_entries_start = .;\n        /* Here are the entries */\n        KEEP(*(.bi_entries));\n        /* Keep this block a nice round size */\n        . = ALIGN(4);\n        /* We put this in the header */\n        __bi_entries_end = .;\n    } > FLASH\n} INSERT AFTER .text;\n\nSECTIONS {\n    /* ### Boot ROM extra info\n     *\n     * Goes after everything in our program, so it can contain a signature.\n     */\n    .end_block : ALIGN(4)\n    {\n        __end_block_addr = .;\n        KEEP(*(.end_block));\n    } > FLASH\n\n} INSERT AFTER .uninit;\n\nPROVIDE(start_to_end = __end_block_addr - __start_block_addr);\nPROVIDE(end_to_start = __start_block_addr - __end_block_addr);\n"
  },
  {
    "path": "examples/rp235x/src/bin/adc.rs",
    "content": "//! This example test the ADC (Analog to Digital Conversion) of the RP2350A pins 26, 27 and 28.\n//! It also reads the temperature sensor in the chip.\n\n#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_rp::adc::{Adc, Channel, Config, InterruptHandler};\nuse embassy_rp::bind_interrupts;\nuse embassy_rp::gpio::Pull;\nuse embassy_time::Timer;\nuse {defmt_rtt as _, panic_probe as _};\n\nbind_interrupts!(struct Irqs {\n    ADC_IRQ_FIFO => InterruptHandler;\n});\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let p = embassy_rp::init(Default::default());\n    let mut adc = Adc::new(p.ADC, Irqs, Config::default());\n\n    let mut p26 = Channel::new_pin(p.PIN_26, Pull::None);\n    let mut p27 = Channel::new_pin(p.PIN_27, Pull::None);\n    let mut p28 = Channel::new_pin(p.PIN_28, Pull::None);\n    let mut ts = Channel::new_temp_sensor(p.ADC_TEMP_SENSOR);\n\n    loop {\n        let level = adc.read(&mut p26).await.unwrap();\n        info!(\"Pin 26 ADC: {}\", level);\n        let level = adc.read(&mut p27).await.unwrap();\n        info!(\"Pin 27 ADC: {}\", level);\n        let level = adc.read(&mut p28).await.unwrap();\n        info!(\"Pin 28 ADC: {}\", level);\n        let temp = adc.read(&mut ts).await.unwrap();\n        info!(\"Temp: {} degrees\", convert_to_celsius(temp));\n        Timer::after_secs(1).await;\n    }\n}\n\nfn convert_to_celsius(raw_temp: u16) -> f32 {\n    // According to chapter 12.4.6 Temperature Sensor in RP235x datasheet\n    let temp = 27.0 - (raw_temp as f32 * 3.3 / 4096.0 - 0.706) / 0.001721;\n    let sign = if temp < 0.0 { -1.0 } else { 1.0 };\n    let rounded_temp_x10: i16 = ((temp * 10.0) + 0.5 * sign) as i16;\n    (rounded_temp_x10 as f32) / 10.0\n}\n"
  },
  {
    "path": "examples/rp235x/src/bin/adc_dma.rs",
    "content": "//! This example shows how to use the RP235x ADC with DMA, both single- and multichannel reads.\n//! For multichannel, the samples are interleaved in the buffer:\n//! `[ch1, ch2, ch3, ch4, ch1, ch2, ch3, ch4, ...]`\n#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_rp::adc::{Adc, Channel, Config, InterruptHandler};\nuse embassy_rp::gpio::Pull;\nuse embassy_rp::peripherals::DMA_CH0;\nuse embassy_rp::{bind_interrupts, dma};\nuse embassy_time::{Duration, Ticker};\nuse {defmt_rtt as _, panic_probe as _};\n\nbind_interrupts!(struct Irqs {\n    ADC_IRQ_FIFO => InterruptHandler;\n    DMA_IRQ_0 => dma::InterruptHandler<DMA_CH0>;\n});\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let p = embassy_rp::init(Default::default());\n    info!(\"Here we go!\");\n\n    let mut adc = Adc::new(p.ADC, Irqs, Config::default());\n    let mut dma_ch = dma::Channel::new(p.DMA_CH0, Irqs);\n    let mut pin = Channel::new_pin(p.PIN_26, Pull::Up);\n    let mut pins = [\n        Channel::new_pin(p.PIN_27, Pull::Down),\n        Channel::new_pin(p.PIN_28, Pull::None),\n        Channel::new_pin(p.PIN_29, Pull::Up),\n        Channel::new_temp_sensor(p.ADC_TEMP_SENSOR),\n    ];\n\n    const BLOCK_SIZE: usize = 100;\n    const NUM_CHANNELS: usize = 4;\n    let mut ticker = Ticker::every(Duration::from_secs(1));\n    loop {\n        // Read 100 samples from a single channel\n        let mut buf = [0_u16; BLOCK_SIZE];\n        let div = 479; // 100kHz sample rate (48Mhz / 100kHz - 1)\n        adc.read_many(&mut pin, &mut buf, div, &mut dma_ch).await.unwrap();\n        info!(\"single: {:?} ...etc\", buf[..8]);\n\n        // Read 100 samples from 4 channels interleaved\n        let mut buf = [0_u16; { BLOCK_SIZE * NUM_CHANNELS }];\n        let div = 119; // 100kHz sample rate (48Mhz / 100kHz * 4ch - 1)\n        adc.read_many_multichannel(&mut pins, &mut buf, div, &mut dma_ch)\n            .await\n            .unwrap();\n        info!(\"multi:  {:?} ...etc\", buf[..NUM_CHANNELS * 2]);\n\n        ticker.next().await;\n    }\n}\n"
  },
  {
    "path": "examples/rp235x/src/bin/aon_timer_async.rs",
    "content": "//! AON (Always-On) Timer Example using Embassy Async API\n//!\n//! This example demonstrates async alarm support with the AON Timer.\n//! The alarm triggers an interrupt (POWMAN_IRQ_TIMER) which wakes the CPU\n//! from WFI (Wait For Interrupt) low-power mode.\n\n#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_rp::aon_timer::{AlarmWakeMode, AonTimer, ClockSource, Config};\nuse embassy_rp::{bind_interrupts, gpio};\nuse embassy_time::{Duration, Timer};\nuse gpio::{Level, Output};\nuse {defmt_rtt as _, panic_probe as _};\n\nbind_interrupts!(struct Irqs {\n    POWMAN_IRQ_TIMER => embassy_rp::aon_timer::InterruptHandler;\n});\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let p = embassy_rp::init(Default::default());\n    let mut led = Output::new(p.PIN_25, Level::Low);\n\n    info!(\"AON Timer Async example starting\");\n\n    // Small delay for debug probe\n    Timer::after_millis(10).await;\n\n    // Configure the AON Timer with XOSC at 12 MHz\n    let config = Config {\n        clock_source: ClockSource::Xosc,\n        clock_freq_khz: 12000,\n        alarm_wake_mode: AlarmWakeMode::WfiOnly,\n    };\n\n    let mut aon = AonTimer::new(p.POWMAN, Irqs, config);\n\n    // Set counter to 0 (start counting from boot)\n    info!(\"Setting counter to 0\");\n    aon.set_counter(0);\n\n    // Start the timer\n    aon.start();\n    info!(\"AON Timer started\");\n\n    // Verify timer is running\n    Timer::after_millis(100).await;\n    let initial_ms = aon.now();\n    info!(\"Counter value after 100ms: {} ms\", initial_ms);\n\n    // Main loop: set alarms and wait asynchronously\n    for i in 1..=5 {\n        info!(\"=== Round {} ===\", i);\n\n        // Set an alarm for 2 seconds from now\n        let alarm_duration = Duration::from_secs(2);\n        let current = aon.now();\n        info!(\"Current time: {} ms\", current);\n        info!(\"Setting alarm for {} seconds from now\", alarm_duration.as_secs());\n\n        aon.set_alarm_after(alarm_duration).unwrap();\n\n        // Blink LED while waiting\n        led.set_high();\n\n        // Wait asynchronously for the alarm\n        // The CPU will enter WFI low-power mode during this time\n        info!(\"Waiting for alarm...\");\n        aon.wait_for_alarm().await;\n\n        led.set_low();\n\n        // Alarm fired!\n        let elapsed = aon.elapsed();\n        info!(\n            \"ALARM FIRED! Counter: {} ms ({}.{:03} seconds)\",\n            aon.now(),\n            elapsed.as_secs(),\n            elapsed.as_millis() % 1000\n        );\n\n        // Wait a bit before next round\n        Timer::after_secs(1).await;\n    }\n\n    info!(\"Demo complete! Looping with longer alarms...\");\n\n    // Continue with longer alarms\n    loop {\n        info!(\"Setting alarm for 5 seconds\");\n        aon.set_alarm_after(Duration::from_secs(5)).unwrap();\n\n        led.toggle();\n        aon.wait_for_alarm().await;\n        led.toggle();\n\n        info!(\"Alarm fired at {} ms\", aon.now());\n        Timer::after_secs(1).await;\n    }\n}\n"
  },
  {
    "path": "examples/rp235x/src/bin/assign_resources.rs",
    "content": "//! This example demonstrates how to assign resources to multiple tasks by splitting up the peripherals.\n//! It is not about sharing the same resources between tasks, see sharing.rs for that or head to https://embassy.dev/book/#_sharing_peripherals_between_tasks)\n//! Of course splitting up resources and sharing resources can be combined, yet this example is only about splitting up resources.\n//!\n//! There are basically two ways we demonstrate here:\n//! 1) Assigning resources to a task by passing parts of the peripherals\n//! 2) Assigning resources to a task by passing a struct with the split up peripherals, using the assign-resources macro\n//!\n//! using four LEDs on Pins 10, 11, 20 and 21\n\n#![no_std]\n#![no_main]\n\nuse assign_resources::assign_resources;\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_rp::Peri;\nuse embassy_rp::gpio::{Level, Output};\nuse embassy_rp::peripherals::{self, PIN_20, PIN_21};\nuse embassy_time::Timer;\nuse {defmt_rtt as _, panic_probe as _};\n\n#[embassy_executor::main]\nasync fn main(spawner: Spawner) {\n    // initialize the peripherals\n    let p = embassy_rp::init(Default::default());\n\n    // 1) Assigning a resource to a task by passing parts of the peripherals.\n    spawner.spawn(double_blinky_manually_assigned(spawner, p.PIN_20, p.PIN_21).unwrap());\n\n    // 2) Using the assign-resources macro to assign resources to a task.\n    // we perform the split, see further below for the definition of the resources struct\n    let r = split_resources!(p);\n    // and then we can use them\n    spawner.spawn(double_blinky_macro_assigned(spawner, r.leds).unwrap());\n}\n\n// 1) Assigning a resource to a task by passing parts of the peripherals.\n#[embassy_executor::task]\nasync fn double_blinky_manually_assigned(\n    _spawner: Spawner,\n    pin_20: Peri<'static, PIN_20>,\n    pin_21: Peri<'static, PIN_21>,\n) {\n    let mut led_20 = Output::new(pin_20, Level::Low);\n    let mut led_21 = Output::new(pin_21, Level::High);\n\n    loop {\n        info!(\"toggling leds\");\n        led_20.toggle();\n        led_21.toggle();\n        Timer::after_secs(1).await;\n    }\n}\n\n// 2) Using the assign-resources macro to assign resources to a task.\n// first we define the resources we want to assign to the task using the assign_resources! macro\n// basically this will split up the peripherals struct into smaller structs, that we define here\n// naming is up to you, make sure your future self understands what you did here\nassign_resources! {\n    leds: Leds{\n        led_10: PIN_10,\n        led_11: PIN_11,\n    }\n    // add more resources to more structs if needed, for example defining one struct for each task\n}\n// this could be done in another file and imported here, but for the sake of simplicity we do it here\n// see https://github.com/adamgreig/assign-resources for more information\n\n// 2) Using the split resources in a task\n#[embassy_executor::task]\nasync fn double_blinky_macro_assigned(_spawner: Spawner, r: Leds) {\n    let mut led_10 = Output::new(r.led_10, Level::Low);\n    let mut led_11 = Output::new(r.led_11, Level::High);\n\n    loop {\n        info!(\"toggling leds\");\n        led_10.toggle();\n        led_11.toggle();\n        Timer::after_secs(1).await;\n    }\n}\n"
  },
  {
    "path": "examples/rp235x/src/bin/blinky.rs",
    "content": "//! This example test the RP Pico on board LED.\n//!\n//! It does not work with the RP Pico W board. See `blinky_wifi.rs`.\n\n#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_rp::gpio;\nuse embassy_time::Timer;\nuse gpio::{Level, Output};\nuse {defmt_rtt as _, panic_probe as _};\n\n// Program metadata for `picotool info`.\n// This isn't needed, but it's recommended to have these minimal entries.\n#[unsafe(link_section = \".bi_entries\")]\n#[used]\npub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [\n    embassy_rp::binary_info::rp_program_name!(c\"Blinky Example\"),\n    embassy_rp::binary_info::rp_program_description!(\n        c\"This example tests the RP Pico on board LED, connected to gpio 25\"\n    ),\n    embassy_rp::binary_info::rp_cargo_version!(),\n    embassy_rp::binary_info::rp_program_build_attribute!(),\n];\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let p = embassy_rp::init(Default::default());\n    let mut led = Output::new(p.PIN_25, Level::Low);\n\n    loop {\n        info!(\"led on!\");\n        led.set_high();\n        Timer::after_millis(250).await;\n\n        info!(\"led off!\");\n        led.set_low();\n        Timer::after_millis(250).await;\n    }\n}\n"
  },
  {
    "path": "examples/rp235x/src/bin/blinky_two_channels.rs",
    "content": "#![no_std]\n#![no_main]\n/// This example demonstrates how to access a given pin from more than one embassy task\n/// The on-board LED is toggled by two tasks with slightly different periods, leading to the\n/// apparent duty cycle of the LED increasing, then decreasing, linearly. The phenomenon is similar\n/// to interference and the 'beats' you can hear if you play two frequencies close to one another\n/// [Link explaining it](https://www.physicsclassroom.com/class/sound/Lesson-3/Interference-and-Beats)\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_rp::gpio;\nuse embassy_sync::blocking_mutex::raw::ThreadModeRawMutex;\nuse embassy_sync::channel::{Channel, Sender};\nuse embassy_time::{Duration, Ticker};\nuse gpio::{Level, Output};\nuse {defmt_rtt as _, panic_probe as _};\n\nenum LedState {\n    Toggle,\n}\nstatic CHANNEL: Channel<ThreadModeRawMutex, LedState, 64> = Channel::new();\n\n#[embassy_executor::main]\nasync fn main(spawner: Spawner) {\n    let p = embassy_rp::init(Default::default());\n    let mut led = Output::new(p.PIN_25, Level::High);\n\n    let dt = 100 * 1_000_000;\n    let k = 1.003;\n\n    spawner.spawn(unwrap!(toggle_led(CHANNEL.sender(), Duration::from_nanos(dt))));\n    spawner.spawn(unwrap!(toggle_led(\n        CHANNEL.sender(),\n        Duration::from_nanos((dt as f64 * k) as u64)\n    )));\n\n    loop {\n        match CHANNEL.receive().await {\n            LedState::Toggle => led.toggle(),\n        }\n    }\n}\n\n#[embassy_executor::task(pool_size = 2)]\nasync fn toggle_led(control: Sender<'static, ThreadModeRawMutex, LedState, 64>, delay: Duration) {\n    let mut ticker = Ticker::every(delay);\n    loop {\n        control.send(LedState::Toggle).await;\n        ticker.next().await;\n    }\n}\n"
  },
  {
    "path": "examples/rp235x/src/bin/blinky_two_tasks.rs",
    "content": "#![no_std]\n#![no_main]\n/// This example demonstrates how to access a given pin from more than one embassy task\n/// The on-board LED is toggled by two tasks with slightly different periods, leading to the\n/// apparent duty cycle of the LED increasing, then decreasing, linearly. The phenomenon is similar\n/// to interference and the 'beats' you can hear if you play two frequencies close to one another\n/// [Link explaining it](https://www.physicsclassroom.com/class/sound/Lesson-3/Interference-and-Beats)\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_rp::gpio;\nuse embassy_sync::blocking_mutex::raw::ThreadModeRawMutex;\nuse embassy_sync::mutex::Mutex;\nuse embassy_time::{Duration, Ticker};\nuse gpio::{Level, Output};\nuse {defmt_rtt as _, panic_probe as _};\n\ntype LedType = Mutex<ThreadModeRawMutex, Option<Output<'static>>>;\nstatic LED: LedType = Mutex::new(None);\n\n#[embassy_executor::main]\nasync fn main(spawner: Spawner) {\n    let p = embassy_rp::init(Default::default());\n    // set the content of the global LED reference to the real LED pin\n    let led = Output::new(p.PIN_25, Level::High);\n    // inner scope is so that once the mutex is written to, the MutexGuard is dropped, thus the\n    // Mutex is released\n    {\n        *(LED.lock().await) = Some(led);\n    }\n    let dt = 100 * 1_000_000;\n    let k = 1.003;\n\n    spawner.spawn(unwrap!(toggle_led(&LED, Duration::from_nanos(dt))));\n    spawner.spawn(unwrap!(toggle_led(&LED, Duration::from_nanos((dt as f64 * k) as u64))));\n}\n\n#[embassy_executor::task(pool_size = 2)]\nasync fn toggle_led(led: &'static LedType, delay: Duration) {\n    let mut ticker = Ticker::every(delay);\n    loop {\n        {\n            let mut led_unlocked = led.lock().await;\n            if let Some(pin_ref) = led_unlocked.as_mut() {\n                pin_ref.toggle();\n            }\n        }\n        ticker.next().await;\n    }\n}\n"
  },
  {
    "path": "examples/rp235x/src/bin/blinky_wifi.rs",
    "content": "//! This example tests the RP Pico 2 W onboard LED.\n//!\n//! It does not work with the RP Pico 2 board. See `blinky.rs`.\n\n#![no_std]\n#![no_main]\n\nuse cyw43::aligned_bytes;\nuse cyw43_pio::{PioSpi, RM2_CLOCK_DIVIDER};\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_rp::gpio::{Level, Output};\nuse embassy_rp::peripherals::{DMA_CH0, PIO0};\nuse embassy_rp::pio::{InterruptHandler, Pio};\nuse embassy_rp::{bind_interrupts, dma};\nuse embassy_time::{Duration, Timer};\nuse static_cell::StaticCell;\nuse {defmt_rtt as _, panic_probe as _};\n\n// Program metadata for `picotool info`.\n// This isn't needed, but it's recommended to have these minimal entries.\n#[unsafe(link_section = \".bi_entries\")]\n#[used]\npub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [\n    embassy_rp::binary_info::rp_program_name!(c\"Blinky Example\"),\n    embassy_rp::binary_info::rp_program_description!(\n        c\"This example tests the RP Pico 2 W's onboard LED, connected to GPIO 0 of the cyw43 \\\n        (WiFi chip) via PIO 0 over the SPI bus.\"\n    ),\n    embassy_rp::binary_info::rp_cargo_version!(),\n    embassy_rp::binary_info::rp_program_build_attribute!(),\n];\n\nbind_interrupts!(struct Irqs {\n    PIO0_IRQ_0 => InterruptHandler<PIO0>;\n    DMA_IRQ_0 => dma::InterruptHandler<DMA_CH0>;\n});\n\n#[embassy_executor::task]\nasync fn cyw43_task(runner: cyw43::Runner<'static, cyw43::SpiBus<Output<'static>, PioSpi<'static, PIO0, 0>>>) -> ! {\n    runner.run().await\n}\n\n#[embassy_executor::main]\nasync fn main(spawner: Spawner) {\n    let p = embassy_rp::init(Default::default());\n    let fw = aligned_bytes!(\"../../../../cyw43-firmware/43439A0.bin\");\n    let clm = aligned_bytes!(\"../../../../cyw43-firmware/43439A0_clm.bin\");\n    let nvram = aligned_bytes!(\"../../../../cyw43-firmware/nvram_rp2040.bin\");\n\n    // To make flashing faster for development, you may want to flash the firmwares independently\n    // at hardcoded addresses, instead of baking them into the program with `include_bytes!`:\n    //     probe-rs download ../../cyw43-firmware/43439A0.bin --binary-format bin --chip RP235x --base-address 0x10100000\n    //     probe-rs download ../../cyw43-firmware/43439A0_clm.bin --binary-format bin --chip RP235x --base-address 0x10140000\n    //let fw = unsafe { core::slice::from_raw_parts(0x10100000 as *const u8, 230321) };\n    //let clm = unsafe { core::slice::from_raw_parts(0x10140000 as *const u8, 4752) };\n\n    let pwr = Output::new(p.PIN_23, Level::Low);\n    let cs = Output::new(p.PIN_25, Level::High);\n    let mut pio = Pio::new(p.PIO0, Irqs);\n    let spi = PioSpi::new(\n        &mut pio.common,\n        pio.sm0,\n        // SPI communication won't work if the speed is too high, so we use a divider larger than `DEFAULT_CLOCK_DIVIDER`.\n        // See: https://github.com/embassy-rs/embassy/issues/3960.\n        RM2_CLOCK_DIVIDER,\n        pio.irq0,\n        cs,\n        p.PIN_24,\n        p.PIN_29,\n        dma::Channel::new(p.DMA_CH0, Irqs),\n    );\n\n    static STATE: StaticCell<cyw43::State> = StaticCell::new();\n    let state = STATE.init(cyw43::State::new());\n    let (_net_device, mut control, runner) = cyw43::new(state, pwr, spi, fw, nvram).await;\n    spawner.spawn(unwrap!(cyw43_task(runner)));\n\n    control.init(clm).await;\n    control\n        .set_power_management(cyw43::PowerManagementMode::PowerSave)\n        .await;\n\n    let delay = Duration::from_millis(250);\n    loop {\n        info!(\"led on!\");\n        control.gpio_set(0, true).await;\n        Timer::after(delay).await;\n\n        info!(\"led off!\");\n        control.gpio_set(0, false).await;\n        Timer::after(delay).await;\n    }\n}\n"
  },
  {
    "path": "examples/rp235x/src/bin/blinky_wifi_pico_plus_2.rs",
    "content": "//! This example test the Pimoroni Pico Plus 2 on board LED.\n//!\n//! It does not work with the RP Pico 2 board. See `blinky.rs`.\n\n#![no_std]\n#![no_main]\n\nuse cyw43::aligned_bytes;\nuse cyw43_pio::{PioSpi, RM2_CLOCK_DIVIDER};\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_rp::peripherals::{DMA_CH0, PIO0};\nuse embassy_rp::pio::{InterruptHandler, Pio};\nuse embassy_rp::{bind_interrupts, gpio};\nuse embassy_time::{Duration, Timer};\nuse gpio::{Level, Output};\nuse static_cell::StaticCell;\nuse {defmt_rtt as _, panic_probe as _};\n\n// Program metadata for `picotool info`.\n// This isn't needed, but it's recommended to have these minimal entries.\n#[unsafe(link_section = \".bi_entries\")]\n#[used]\npub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [\n    embassy_rp::binary_info::rp_program_name!(c\"Blinky Example\"),\n    embassy_rp::binary_info::rp_program_description!(\n        c\"This example tests the RP Pico on board LED, connected to gpio 25\"\n    ),\n    embassy_rp::binary_info::rp_cargo_version!(),\n    embassy_rp::binary_info::rp_program_build_attribute!(),\n];\n\nbind_interrupts!(struct Irqs {\n    PIO0_IRQ_0 => InterruptHandler<PIO0>;\n    DMA_IRQ_0 => embassy_rp::dma::InterruptHandler<DMA_CH0>;\n});\n\n#[embassy_executor::task]\nasync fn cyw43_task(runner: cyw43::Runner<'static, cyw43::SpiBus<Output<'static>, PioSpi<'static, PIO0, 0>>>) -> ! {\n    runner.run().await\n}\n\n#[embassy_executor::main]\nasync fn main(spawner: Spawner) {\n    let p = embassy_rp::init(Default::default());\n    let fw = aligned_bytes!(\"../../../../cyw43-firmware/43439A0.bin\");\n    let clm = aligned_bytes!(\"../../../../cyw43-firmware/43439A0_clm.bin\");\n    let nvram = aligned_bytes!(\"../../../../cyw43-firmware/nvram_rp2040.bin\");\n\n    // To make flashing faster for development, you may want to flash the firmwares independently\n    // at hardcoded addresses, instead of baking them into the program with `include_bytes!`:\n    //     probe-rs download ../../cyw43-firmware/43439A0.bin --binary-format bin --chip RP235x --base-address 0x10100000\n    //     probe-rs download ../../cyw43-firmware/43439A0_clm.bin --binary-format bin --chip RP235x --base-address 0x10140000\n    //let fw = unsafe { core::slice::from_raw_parts(0x10100000 as *const u8, 230321) };\n    //let clm = unsafe { core::slice::from_raw_parts(0x10140000 as *const u8, 4752) };\n\n    let pwr = Output::new(p.PIN_23, Level::Low);\n    let cs = Output::new(p.PIN_25, Level::High);\n    let mut pio = Pio::new(p.PIO0, Irqs);\n    let spi = PioSpi::new(\n        &mut pio.common,\n        pio.sm0,\n        RM2_CLOCK_DIVIDER,\n        pio.irq0,\n        cs,\n        p.PIN_24,\n        p.PIN_29,\n        embassy_rp::dma::Channel::new(p.DMA_CH0, Irqs),\n    );\n\n    static STATE: StaticCell<cyw43::State> = StaticCell::new();\n    let state = STATE.init(cyw43::State::new());\n    let (_net_device, mut control, runner) = cyw43::new(state, pwr, spi, fw, nvram).await;\n    spawner.spawn(unwrap!(cyw43_task(runner)));\n\n    control.init(clm).await;\n    control\n        .set_power_management(cyw43::PowerManagementMode::PowerSave)\n        .await;\n\n    let delay = Duration::from_secs(1);\n    loop {\n        info!(\"led on!\");\n        control.gpio_set(0, true).await;\n        Timer::after(delay).await;\n\n        info!(\"led off!\");\n        control.gpio_set(0, false).await;\n        Timer::after(delay).await;\n    }\n}\n"
  },
  {
    "path": "examples/rp235x/src/bin/button.rs",
    "content": "//! This example uses the RP Pico on board LED to test input pin 28. This is not the button on the board.\n//!\n//! It does not work with the RP Pico W board. Use wifi_blinky.rs and add input pin.\n\n#![no_std]\n#![no_main]\n\nuse embassy_executor::Spawner;\nuse embassy_rp::gpio::{Input, Level, Output, Pull};\nuse {defmt_rtt as _, panic_probe as _};\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let p = embassy_rp::init(Default::default());\n    let mut led = Output::new(p.PIN_25, Level::Low);\n\n    // Use PIN_28, Pin34 on J0 for RP Pico, as a input.\n    // You need to add your own button.\n    let button = Input::new(p.PIN_28, Pull::Up);\n\n    loop {\n        if button.is_high() {\n            led.set_high();\n        } else {\n            led.set_low();\n        }\n    }\n}\n"
  },
  {
    "path": "examples/rp235x/src/bin/debounce.rs",
    "content": "//! This example shows the ease of debouncing a button with async rust.\n//! Hook up a button or switch between pin 9 and ground.\n\n#![no_std]\n#![no_main]\n\nuse defmt::info;\nuse embassy_executor::Spawner;\nuse embassy_rp::gpio::{Input, Level, Pull};\nuse embassy_time::{Duration, Instant, Timer, with_deadline};\nuse {defmt_rtt as _, panic_probe as _};\n\npub struct Debouncer<'a> {\n    input: Input<'a>,\n    debounce: Duration,\n}\n\nimpl<'a> Debouncer<'a> {\n    pub fn new(input: Input<'a>, debounce: Duration) -> Self {\n        Self { input, debounce }\n    }\n\n    pub async fn debounce(&mut self) -> Level {\n        loop {\n            let l1 = self.input.get_level();\n\n            self.input.wait_for_any_edge().await;\n\n            Timer::after(self.debounce).await;\n\n            let l2 = self.input.get_level();\n            if l1 != l2 {\n                break l2;\n            }\n        }\n    }\n}\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let p = embassy_rp::init(Default::default());\n    let mut btn = Debouncer::new(Input::new(p.PIN_9, Pull::Up), Duration::from_millis(20));\n\n    info!(\"Debounce Demo\");\n\n    loop {\n        // button pressed\n        btn.debounce().await;\n        let start = Instant::now();\n        info!(\"Button Press\");\n\n        match with_deadline(start + Duration::from_secs(1), btn.debounce()).await {\n            // Button Released < 1s\n            Ok(_) => {\n                info!(\"Button pressed for: {}ms\", start.elapsed().as_millis());\n                continue;\n            }\n            // button held for > 1s\n            Err(_) => {\n                info!(\"Button Held\");\n            }\n        }\n\n        match with_deadline(start + Duration::from_secs(5), btn.debounce()).await {\n            // Button released <5s\n            Ok(_) => {\n                info!(\"Button pressed for: {}ms\", start.elapsed().as_millis());\n                continue;\n            }\n            // button held for > >5s\n            Err(_) => {\n                info!(\"Button Long Held\");\n            }\n        }\n\n        // wait for button release before handling another press\n        btn.debounce().await;\n        info!(\"Button pressed for: {}ms\", start.elapsed().as_millis());\n    }\n}\n"
  },
  {
    "path": "examples/rp235x/src/bin/ethernet_w5500_icmp.rs",
    "content": "//! This example implements an echo (ping) with an ICMP Socket and using defmt to report the results.\n//!\n//! Although there is a better way to execute pings using the child module ping of the icmp module,\n//! this example allows for other icmp messages like `Destination unreachable` to be sent aswell.\n//!\n//! Example written for the [`WIZnet W5500-EVB-Pico2`](https://docs.wiznet.io/Product/iEthernet/W5500/w5500-evb-pico2) board.\n\n#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_futures::yield_now;\nuse embassy_net::icmp::{ChecksumCapabilities, IcmpEndpoint, IcmpSocket, Icmpv4Packet, Icmpv4Repr, PacketMetadata};\nuse embassy_net::{Stack, StackResources};\nuse embassy_net_wiznet::chip::W5500;\nuse embassy_net_wiznet::*;\nuse embassy_rp::clocks::RoscRng;\nuse embassy_rp::gpio::{Input, Level, Output, Pull};\nuse embassy_rp::peripherals::{DMA_CH0, DMA_CH1, SPI0};\nuse embassy_rp::spi::{Async, Config as SpiConfig, Spi};\nuse embassy_rp::{bind_interrupts, dma};\nuse embassy_time::{Delay, Instant, Timer};\nuse embedded_hal_bus::spi::ExclusiveDevice;\nuse static_cell::StaticCell;\nuse {defmt_rtt as _, panic_probe as _};\n\nbind_interrupts!(struct Irqs {\n    DMA_IRQ_0 => dma::InterruptHandler<DMA_CH0>, dma::InterruptHandler<DMA_CH1>;\n});\n\ntype ExclusiveSpiDevice = ExclusiveDevice<Spi<'static, SPI0, Async>, Output<'static>, Delay>;\n\n#[embassy_executor::task]\nasync fn ethernet_task(runner: Runner<'static, W5500, ExclusiveSpiDevice, Input<'static>, Output<'static>>) -> ! {\n    runner.run().await\n}\n\n#[embassy_executor::task]\nasync fn net_task(mut runner: embassy_net::Runner<'static, Device<'static>>) -> ! {\n    runner.run().await\n}\n\n#[embassy_executor::main]\nasync fn main(spawner: Spawner) {\n    let p = embassy_rp::init(Default::default());\n    let mut rng = RoscRng;\n\n    let mut spi_cfg = SpiConfig::default();\n    spi_cfg.frequency = 50_000_000;\n    let (miso, mosi, clk) = (p.PIN_16, p.PIN_19, p.PIN_18);\n    let spi = Spi::new(p.SPI0, clk, mosi, miso, p.DMA_CH0, p.DMA_CH1, Irqs, spi_cfg);\n    let cs = Output::new(p.PIN_17, Level::High);\n    let w5500_int = Input::new(p.PIN_21, Pull::Up);\n    let w5500_reset = Output::new(p.PIN_20, Level::High);\n\n    let mac_addr = [0x02, 0x00, 0x00, 0x00, 0x00, 0x00];\n    static STATE: StaticCell<State<8, 8>> = StaticCell::new();\n    let state = STATE.init(State::<8, 8>::new());\n    let (device, runner) = embassy_net_wiznet::new(\n        mac_addr,\n        state,\n        ExclusiveDevice::new(spi, cs, Delay),\n        w5500_int,\n        w5500_reset,\n    )\n    .await\n    .unwrap();\n    spawner.spawn(unwrap!(ethernet_task(runner)));\n\n    // Generate random seed\n    let seed = rng.next_u64();\n\n    // Init network stack\n    static RESOURCES: StaticCell<StackResources<3>> = StaticCell::new();\n    let (stack, runner) = embassy_net::new(\n        device,\n        embassy_net::Config::dhcpv4(Default::default()),\n        RESOURCES.init(StackResources::new()),\n        seed,\n    );\n\n    // Launch network task\n    spawner.spawn(unwrap!(net_task(runner)));\n\n    info!(\"Waiting for DHCP...\");\n    let cfg = wait_for_config(stack).await;\n    let local_addr = cfg.address.address();\n    info!(\"IP address: {:?}\", local_addr);\n\n    // Then we can use it!\n    let mut rx_buffer = [0; 256];\n    let mut tx_buffer = [0; 256];\n    let mut rx_meta = [PacketMetadata::EMPTY];\n    let mut tx_meta = [PacketMetadata::EMPTY];\n\n    // Identifier used for the ICMP socket\n    let ident = 42;\n\n    // Create and bind the socket\n    let mut socket = IcmpSocket::new(stack, &mut rx_meta, &mut rx_buffer, &mut tx_meta, &mut tx_buffer);\n    socket.bind(IcmpEndpoint::Ident(ident)).unwrap();\n\n    // Create the repr for the packet\n    let icmp_repr = Icmpv4Repr::EchoRequest {\n        ident,\n        seq_no: 0,\n        data: b\"Hello, icmp!\",\n    };\n\n    // Send the packet and store the starting instant to mesure latency later\n    let start = socket\n        .send_to_with(icmp_repr.buffer_len(), cfg.gateway.unwrap(), |buf| {\n            // Create and populate the packet buffer allocated by `send_to_with`\n            let mut icmp_packet = Icmpv4Packet::new_unchecked(buf);\n            icmp_repr.emit(&mut icmp_packet, &ChecksumCapabilities::default());\n            (icmp_repr.buffer_len(), Instant::now()) // Return the instant where the packet was sent\n        })\n        .await\n        .unwrap();\n\n    // Recieve and log the data of the reply\n    socket\n        .recv_from_with(|(buf, addr)| {\n            let packet = Icmpv4Packet::new_checked(buf).unwrap();\n            info!(\n                \"Recieved {:?} from {} in {}ms\",\n                packet.data(),\n                addr,\n                start.elapsed().as_millis()\n            );\n        })\n        .await\n        .unwrap();\n\n    loop {\n        Timer::after_secs(10).await;\n    }\n}\n\nasync fn wait_for_config(stack: Stack<'static>) -> embassy_net::StaticConfigV4 {\n    loop {\n        if let Some(config) = stack.config_v4() {\n            return config.clone();\n        }\n        yield_now().await;\n    }\n}\n"
  },
  {
    "path": "examples/rp235x/src/bin/ethernet_w5500_icmp_ping.rs",
    "content": "//! This example implements a LAN ping scan with the ping utilities in the icmp module of embassy-net.\n//!\n//! Example written for the [`WIZnet W5500-EVB-Pico2`](https://docs.wiznet.io/Product/iEthernet/W5500/w5500-evb-pico2) board.\n\n#![no_std]\n#![no_main]\n\nuse core::net::Ipv4Addr;\nuse core::ops::Not;\nuse core::str::FromStr;\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_futures::yield_now;\nuse embassy_net::icmp::PacketMetadata;\nuse embassy_net::icmp::ping::{PingManager, PingParams};\nuse embassy_net::{Ipv4Cidr, Stack, StackResources};\nuse embassy_net_wiznet::chip::W5500;\nuse embassy_net_wiznet::*;\nuse embassy_rp::clocks::RoscRng;\nuse embassy_rp::gpio::{Input, Level, Output, Pull};\nuse embassy_rp::peripherals::{DMA_CH0, DMA_CH1, SPI0};\nuse embassy_rp::spi::{Async, Config as SpiConfig, Spi};\nuse embassy_rp::{bind_interrupts, dma};\nuse embassy_time::{Delay, Duration};\nuse embedded_hal_bus::spi::ExclusiveDevice;\nuse static_cell::StaticCell;\nuse {defmt_rtt as _, panic_probe as _};\n\nbind_interrupts!(struct Irqs {\n    DMA_IRQ_0 => dma::InterruptHandler<DMA_CH0>, dma::InterruptHandler<DMA_CH1>;\n});\n\ntype ExclusiveSpiDevice = ExclusiveDevice<Spi<'static, SPI0, Async>, Output<'static>, Delay>;\n\n#[embassy_executor::task]\nasync fn ethernet_task(runner: Runner<'static, W5500, ExclusiveSpiDevice, Input<'static>, Output<'static>>) -> ! {\n    runner.run().await\n}\n\n#[embassy_executor::task]\nasync fn net_task(mut runner: embassy_net::Runner<'static, Device<'static>>) -> ! {\n    runner.run().await\n}\n\n#[embassy_executor::main]\nasync fn main(spawner: Spawner) {\n    let p = embassy_rp::init(Default::default());\n    let mut rng = RoscRng;\n\n    let mut spi_cfg = SpiConfig::default();\n    spi_cfg.frequency = 50_000_000;\n    let (miso, mosi, clk) = (p.PIN_16, p.PIN_19, p.PIN_18);\n    let spi = Spi::new(p.SPI0, clk, mosi, miso, p.DMA_CH0, p.DMA_CH1, Irqs, spi_cfg);\n    let cs = Output::new(p.PIN_17, Level::High);\n    let w5500_int = Input::new(p.PIN_21, Pull::Up);\n    let w5500_reset = Output::new(p.PIN_20, Level::High);\n\n    let mac_addr = [0x02, 0x00, 0x00, 0x00, 0x00, 0x00];\n    static STATE: StaticCell<State<8, 8>> = StaticCell::new();\n    let state = STATE.init(State::<8, 8>::new());\n    let (device, runner) = embassy_net_wiznet::new(\n        mac_addr,\n        state,\n        ExclusiveDevice::new(spi, cs, Delay),\n        w5500_int,\n        w5500_reset,\n    )\n    .await\n    .unwrap();\n    spawner.spawn(unwrap!(ethernet_task(runner)));\n\n    // Generate random seed\n    let seed = rng.next_u64();\n\n    // Init network stack\n    static RESOURCES: StaticCell<StackResources<3>> = StaticCell::new();\n    let (stack, runner) = embassy_net::new(\n        device,\n        embassy_net::Config::dhcpv4(Default::default()),\n        RESOURCES.init(StackResources::new()),\n        seed,\n    );\n\n    // Launch network task\n    spawner.spawn(unwrap!(net_task(runner)));\n\n    info!(\"Waiting for DHCP...\");\n    let cfg = wait_for_config(stack).await;\n    let local_addr = cfg.address.address();\n    info!(\"IP address: {:?}\", local_addr);\n    let gateway = cfg.gateway.unwrap();\n    let mask = cfg.address.netmask();\n    let lower_bound = (gateway.to_bits() & mask.to_bits()) + 1;\n    let upper_bound = gateway.to_bits() | mask.to_bits().not();\n    let addr_range = lower_bound..=upper_bound;\n\n    // Then we can use it!\n    let mut rx_buffer = [0; 256];\n    let mut tx_buffer = [0; 256];\n    let mut rx_meta = [PacketMetadata::EMPTY];\n    let mut tx_meta = [PacketMetadata::EMPTY];\n\n    // Create the ping manager instance\n    let mut ping_manager = PingManager::new(stack, &mut rx_meta, &mut rx_buffer, &mut tx_meta, &mut tx_buffer);\n    let addr = \"192.168.8.1\"; // Address to ping to\n    // Create the PingParams with the target address\n    let mut ping_params = PingParams::new(Ipv4Addr::from_str(addr).unwrap());\n    // (optional) Set custom properties of the ping\n    ping_params.set_payload(b\"Hello, Ping!\"); // custom payload\n    ping_params.set_count(1); // ping 1 times per ping call\n    ping_params.set_timeout(Duration::from_millis(500)); // wait .5 seconds instead of 4\n\n    info!(\"Online hosts in {}:\", Ipv4Cidr::from_netmask(gateway, mask).unwrap());\n    let mut total_online_hosts = 0u32;\n    for addr in addr_range {\n        let ip_addr = Ipv4Addr::from_bits(addr);\n        // Set the target address in the ping params\n        ping_params.set_target(ip_addr);\n        // Execute the ping with the given parameters and wait for the reply\n        match ping_manager.ping(&ping_params).await {\n            Ok(time) => {\n                info!(\"{} is online\\n- latency: {}ms\\n\", ip_addr, time.as_millis());\n                total_online_hosts += 1;\n            }\n            _ => continue,\n        }\n    }\n    info!(\"Ping scan complete, total online hosts: {}\", total_online_hosts);\n}\n\nasync fn wait_for_config(stack: Stack<'static>) -> embassy_net::StaticConfigV4 {\n    loop {\n        if let Some(config) = stack.config_v4() {\n            return config.clone();\n        }\n        yield_now().await;\n    }\n}\n"
  },
  {
    "path": "examples/rp235x/src/bin/ethernet_w5500_multisocket.rs",
    "content": "//! This example shows how you can allow multiple simultaneous TCP connections, by having multiple sockets listening on the same port.\n//!\n//! Example written for the [`WIZnet W5500-EVB-Pico2`](https://docs.wiznet.io/Product/iEthernet/W5500/w5500-evb-pico2) board.\n\n#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_futures::yield_now;\nuse embassy_net::{Stack, StackResources};\nuse embassy_net_wiznet::chip::W5500;\nuse embassy_net_wiznet::*;\nuse embassy_rp::clocks::RoscRng;\nuse embassy_rp::gpio::{Input, Level, Output, Pull};\nuse embassy_rp::peripherals::{DMA_CH0, DMA_CH1, SPI0};\nuse embassy_rp::spi::{Async, Config as SpiConfig, Spi};\nuse embassy_rp::{bind_interrupts, dma};\nuse embassy_time::{Delay, Duration};\nuse embedded_hal_bus::spi::ExclusiveDevice;\nuse embedded_io_async::Write;\nuse static_cell::StaticCell;\nuse {defmt_rtt as _, panic_probe as _};\n\nbind_interrupts!(struct Irqs {\n    DMA_IRQ_0 => dma::InterruptHandler<DMA_CH0>, dma::InterruptHandler<DMA_CH1>;\n});\n\n#[embassy_executor::task]\nasync fn ethernet_task(\n    runner: Runner<\n        'static,\n        W5500,\n        ExclusiveDevice<Spi<'static, SPI0, Async>, Output<'static>, Delay>,\n        Input<'static>,\n        Output<'static>,\n    >,\n) -> ! {\n    runner.run().await\n}\n\n#[embassy_executor::task]\nasync fn net_task(mut runner: embassy_net::Runner<'static, Device<'static>>) -> ! {\n    runner.run().await\n}\n\n#[embassy_executor::main]\nasync fn main(spawner: Spawner) {\n    let p = embassy_rp::init(Default::default());\n    let mut rng = RoscRng;\n\n    let mut spi_cfg = SpiConfig::default();\n    spi_cfg.frequency = 50_000_000;\n    let (miso, mosi, clk) = (p.PIN_16, p.PIN_19, p.PIN_18);\n    let spi = Spi::new(p.SPI0, clk, mosi, miso, p.DMA_CH0, p.DMA_CH1, Irqs, spi_cfg);\n    let cs = Output::new(p.PIN_17, Level::High);\n    let w5500_int = Input::new(p.PIN_21, Pull::Up);\n    let w5500_reset = Output::new(p.PIN_20, Level::High);\n\n    let mac_addr = [0x02, 0x00, 0x00, 0x00, 0x00, 0x00];\n    static STATE: StaticCell<State<8, 8>> = StaticCell::new();\n    let state = STATE.init(State::<8, 8>::new());\n    let (device, runner) = embassy_net_wiznet::new(\n        mac_addr,\n        state,\n        ExclusiveDevice::new(spi, cs, Delay),\n        w5500_int,\n        w5500_reset,\n    )\n    .await\n    .unwrap();\n    spawner.spawn(unwrap!(ethernet_task(runner)));\n\n    // Generate random seed\n    let seed = rng.next_u64();\n\n    // Init network stack\n    static RESOURCES: StaticCell<StackResources<3>> = StaticCell::new();\n    let (stack, runner) = embassy_net::new(\n        device,\n        embassy_net::Config::dhcpv4(Default::default()),\n        RESOURCES.init(StackResources::new()),\n        seed,\n    );\n\n    // Launch network task\n    spawner.spawn(unwrap!(net_task(runner)));\n\n    info!(\"Waiting for DHCP...\");\n    let cfg = wait_for_config(stack).await;\n    let local_addr = cfg.address.address();\n    info!(\"IP address: {:?}\", local_addr);\n\n    // Create two sockets listening to the same port, to handle simultaneous connections\n    spawner.spawn(unwrap!(listen_task(stack, 0, 1234)));\n    spawner.spawn(unwrap!(listen_task(stack, 1, 1234)));\n}\n\n#[embassy_executor::task(pool_size = 2)]\nasync fn listen_task(stack: Stack<'static>, id: u8, port: u16) {\n    let mut rx_buffer = [0; 4096];\n    let mut tx_buffer = [0; 4096];\n    let mut buf = [0; 4096];\n    loop {\n        let mut socket = embassy_net::tcp::TcpSocket::new(stack, &mut rx_buffer, &mut tx_buffer);\n        socket.set_timeout(Some(Duration::from_secs(10)));\n\n        info!(\"SOCKET {}: Listening on TCP:{}...\", id, port);\n        if let Err(e) = socket.accept(port).await {\n            warn!(\"accept error: {:?}\", e);\n            continue;\n        }\n        info!(\"SOCKET {}: Received connection from {:?}\", id, socket.remote_endpoint());\n\n        loop {\n            let n = match socket.read(&mut buf).await {\n                Ok(0) => {\n                    warn!(\"read EOF\");\n                    break;\n                }\n                Ok(n) => n,\n                Err(e) => {\n                    warn!(\"SOCKET {}: {:?}\", id, e);\n                    break;\n                }\n            };\n            info!(\"SOCKET {}: rxd {}\", id, core::str::from_utf8(&buf[..n]).unwrap());\n\n            if let Err(e) = socket.write_all(&buf[..n]).await {\n                warn!(\"write error: {:?}\", e);\n                break;\n            }\n        }\n    }\n}\n\nasync fn wait_for_config(stack: Stack<'static>) -> embassy_net::StaticConfigV4 {\n    loop {\n        if let Some(config) = stack.config_v4() {\n            return config.clone();\n        }\n        yield_now().await;\n    }\n}\n"
  },
  {
    "path": "examples/rp235x/src/bin/ethernet_w5500_tcp_client.rs",
    "content": "//! This example implements a TCP client that attempts to connect to a host on port 1234 and send it some data once per second.\n//!\n//! Example written for the [`WIZnet W5500-EVB-Pico2`](https://docs.wiznet.io/Product/iEthernet/W5500/w5500-evb-pico2) board.\n\n#![no_std]\n#![no_main]\n\nuse core::str::FromStr;\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_futures::yield_now;\nuse embassy_net::{Stack, StackResources};\nuse embassy_net_wiznet::chip::W5500;\nuse embassy_net_wiznet::*;\nuse embassy_rp::clocks::RoscRng;\nuse embassy_rp::gpio::{Input, Level, Output, Pull};\nuse embassy_rp::peripherals::{DMA_CH0, DMA_CH1, SPI0};\nuse embassy_rp::spi::{Async, Config as SpiConfig, Spi};\nuse embassy_rp::{bind_interrupts, dma};\nuse embassy_time::{Delay, Duration, Timer};\nuse embedded_hal_bus::spi::ExclusiveDevice;\nuse embedded_io_async::Write;\nuse static_cell::StaticCell;\nuse {defmt_rtt as _, panic_probe as _};\n\nbind_interrupts!(struct Irqs {\n    DMA_IRQ_0 => dma::InterruptHandler<DMA_CH0>, dma::InterruptHandler<DMA_CH1>;\n});\n\n#[embassy_executor::task]\nasync fn ethernet_task(\n    runner: Runner<\n        'static,\n        W5500,\n        ExclusiveDevice<Spi<'static, SPI0, Async>, Output<'static>, Delay>,\n        Input<'static>,\n        Output<'static>,\n    >,\n) -> ! {\n    runner.run().await\n}\n\n#[embassy_executor::task]\nasync fn net_task(mut runner: embassy_net::Runner<'static, Device<'static>>) -> ! {\n    runner.run().await\n}\n\n#[embassy_executor::main]\nasync fn main(spawner: Spawner) {\n    let p = embassy_rp::init(Default::default());\n    let mut rng = RoscRng;\n    let mut led = Output::new(p.PIN_25, Level::Low);\n\n    let mut spi_cfg = SpiConfig::default();\n    spi_cfg.frequency = 50_000_000;\n    let (miso, mosi, clk) = (p.PIN_16, p.PIN_19, p.PIN_18);\n    let spi = Spi::new(p.SPI0, clk, mosi, miso, p.DMA_CH0, p.DMA_CH1, Irqs, spi_cfg);\n    let cs = Output::new(p.PIN_17, Level::High);\n    let w5500_int = Input::new(p.PIN_21, Pull::Up);\n    let w5500_reset = Output::new(p.PIN_20, Level::High);\n\n    let mac_addr = [0x02, 0x00, 0x00, 0x00, 0x00, 0x00];\n    static STATE: StaticCell<State<8, 8>> = StaticCell::new();\n    let state = STATE.init(State::<8, 8>::new());\n    let (device, runner) = embassy_net_wiznet::new(\n        mac_addr,\n        state,\n        ExclusiveDevice::new(spi, cs, Delay),\n        w5500_int,\n        w5500_reset,\n    )\n    .await\n    .unwrap();\n    spawner.spawn(unwrap!(ethernet_task(runner)));\n\n    // Generate random seed\n    let seed = rng.next_u64();\n\n    // Init network stack\n    static RESOURCES: StaticCell<StackResources<3>> = StaticCell::new();\n    let (stack, runner) = embassy_net::new(\n        device,\n        embassy_net::Config::dhcpv4(Default::default()),\n        RESOURCES.init(StackResources::new()),\n        seed,\n    );\n\n    // Launch network task\n    spawner.spawn(unwrap!(net_task(runner)));\n\n    info!(\"Waiting for DHCP...\");\n    let cfg = wait_for_config(stack).await;\n    let local_addr = cfg.address.address();\n    info!(\"IP address: {:?}\", local_addr);\n\n    let mut rx_buffer = [0; 4096];\n    let mut tx_buffer = [0; 4096];\n    loop {\n        let mut socket = embassy_net::tcp::TcpSocket::new(stack, &mut rx_buffer, &mut tx_buffer);\n        socket.set_timeout(Some(Duration::from_secs(10)));\n\n        led.set_low();\n        info!(\"Connecting...\");\n        let host_addr = embassy_net::Ipv4Address::from_str(\"192.168.0.118\").unwrap();\n        if let Err(e) = socket.connect((host_addr, 1234)).await {\n            warn!(\"connect error: {:?}\", e);\n            continue;\n        }\n        info!(\"Connected to {:?}\", socket.remote_endpoint());\n        led.set_high();\n\n        let msg = b\"Hello world!\\n\";\n        loop {\n            if let Err(e) = socket.write_all(msg).await {\n                warn!(\"write error: {:?}\", e);\n                break;\n            }\n            info!(\"txd: {}\", core::str::from_utf8(msg).unwrap());\n            Timer::after_secs(1).await;\n        }\n    }\n}\n\nasync fn wait_for_config(stack: Stack<'static>) -> embassy_net::StaticConfigV4 {\n    loop {\n        if let Some(config) = stack.config_v4() {\n            return config.clone();\n        }\n        yield_now().await;\n    }\n}\n"
  },
  {
    "path": "examples/rp235x/src/bin/ethernet_w5500_tcp_server.rs",
    "content": "//! This example implements a TCP echo server on port 1234 and using DHCP.\n//! Send it some data, you should see it echoed back and printed in the console.\n//!\n//! Example written for the [`WIZnet W5500-EVB-Pico2`](https://docs.wiznet.io/Product/iEthernet/W5500/w5500-evb-pico2) board.\n\n#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_futures::yield_now;\nuse embassy_net::{Stack, StackResources};\nuse embassy_net_wiznet::chip::W5500;\nuse embassy_net_wiznet::*;\nuse embassy_rp::clocks::RoscRng;\nuse embassy_rp::gpio::{Input, Level, Output, Pull};\nuse embassy_rp::peripherals::{DMA_CH0, DMA_CH1, SPI0};\nuse embassy_rp::spi::{Async, Config as SpiConfig, Spi};\nuse embassy_rp::{bind_interrupts, dma};\nuse embassy_time::{Delay, Duration};\nuse embedded_hal_bus::spi::ExclusiveDevice;\nuse embedded_io_async::Write;\nuse static_cell::StaticCell;\nuse {defmt_rtt as _, panic_probe as _};\n\nbind_interrupts!(struct Irqs {\n    DMA_IRQ_0 => dma::InterruptHandler<DMA_CH0>, dma::InterruptHandler<DMA_CH1>;\n});\n\n#[embassy_executor::task]\nasync fn ethernet_task(\n    runner: Runner<\n        'static,\n        W5500,\n        ExclusiveDevice<Spi<'static, SPI0, Async>, Output<'static>, Delay>,\n        Input<'static>,\n        Output<'static>,\n    >,\n) -> ! {\n    runner.run().await\n}\n\n#[embassy_executor::task]\nasync fn net_task(mut runner: embassy_net::Runner<'static, Device<'static>>) -> ! {\n    runner.run().await\n}\n\n#[embassy_executor::main]\nasync fn main(spawner: Spawner) {\n    let p = embassy_rp::init(Default::default());\n    let mut rng = RoscRng;\n    let mut led = Output::new(p.PIN_25, Level::Low);\n\n    let mut spi_cfg = SpiConfig::default();\n    spi_cfg.frequency = 50_000_000;\n    let (miso, mosi, clk) = (p.PIN_16, p.PIN_19, p.PIN_18);\n    let spi = Spi::new(p.SPI0, clk, mosi, miso, p.DMA_CH0, p.DMA_CH1, Irqs, spi_cfg);\n    let cs = Output::new(p.PIN_17, Level::High);\n    let w5500_int = Input::new(p.PIN_21, Pull::Up);\n    let w5500_reset = Output::new(p.PIN_20, Level::High);\n\n    let mac_addr = [0x02, 0x00, 0x00, 0x00, 0x00, 0x00];\n    static STATE: StaticCell<State<8, 8>> = StaticCell::new();\n    let state = STATE.init(State::<8, 8>::new());\n    let (device, runner) = embassy_net_wiznet::new(\n        mac_addr,\n        state,\n        ExclusiveDevice::new(spi, cs, Delay),\n        w5500_int,\n        w5500_reset,\n    )\n    .await\n    .unwrap();\n    spawner.spawn(unwrap!(ethernet_task(runner)));\n\n    // Generate random seed\n    let seed = rng.next_u64();\n\n    // Init network stack\n    static RESOURCES: StaticCell<StackResources<3>> = StaticCell::new();\n    let (stack, runner) = embassy_net::new(\n        device,\n        embassy_net::Config::dhcpv4(Default::default()),\n        RESOURCES.init(StackResources::new()),\n        seed,\n    );\n\n    // Launch network task\n    spawner.spawn(unwrap!(net_task(runner)));\n\n    info!(\"Waiting for DHCP...\");\n    let cfg = wait_for_config(stack).await;\n    let local_addr = cfg.address.address();\n    info!(\"IP address: {:?}\", local_addr);\n\n    let mut rx_buffer = [0; 4096];\n    let mut tx_buffer = [0; 4096];\n    let mut buf = [0; 4096];\n    loop {\n        let mut socket = embassy_net::tcp::TcpSocket::new(stack, &mut rx_buffer, &mut tx_buffer);\n        socket.set_timeout(Some(Duration::from_secs(10)));\n\n        led.set_low();\n        info!(\"Listening on TCP:1234...\");\n        if let Err(e) = socket.accept(1234).await {\n            warn!(\"accept error: {:?}\", e);\n            continue;\n        }\n        info!(\"Received connection from {:?}\", socket.remote_endpoint());\n        led.set_high();\n\n        loop {\n            let n = match socket.read(&mut buf).await {\n                Ok(0) => {\n                    warn!(\"read EOF\");\n                    break;\n                }\n                Ok(n) => n,\n                Err(e) => {\n                    warn!(\"{:?}\", e);\n                    break;\n                }\n            };\n            info!(\"rxd {}\", core::str::from_utf8(&buf[..n]).unwrap());\n\n            if let Err(e) = socket.write_all(&buf[..n]).await {\n                warn!(\"write error: {:?}\", e);\n                break;\n            }\n        }\n    }\n}\n\nasync fn wait_for_config(stack: Stack<'static>) -> embassy_net::StaticConfigV4 {\n    loop {\n        if let Some(config) = stack.config_v4() {\n            return config.clone();\n        }\n        yield_now().await;\n    }\n}\n"
  },
  {
    "path": "examples/rp235x/src/bin/ethernet_w5500_udp.rs",
    "content": "//! This example implements a UDP server listening on port 1234 and echoing back the data.\n//!\n//! Example written for the [`WIZnet W5500-EVB-Pico2`](https://docs.wiznet.io/Product/iEthernet/W5500/w5500-evb-pico2) board.\n\n#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_futures::yield_now;\nuse embassy_net::udp::{PacketMetadata, UdpSocket};\nuse embassy_net::{Stack, StackResources};\nuse embassy_net_wiznet::chip::W5500;\nuse embassy_net_wiznet::*;\nuse embassy_rp::clocks::RoscRng;\nuse embassy_rp::gpio::{Input, Level, Output, Pull};\nuse embassy_rp::peripherals::{DMA_CH0, DMA_CH1, SPI0};\nuse embassy_rp::spi::{Async, Config as SpiConfig, Spi};\nuse embassy_rp::{bind_interrupts, dma};\nuse embassy_time::Delay;\nuse embedded_hal_bus::spi::ExclusiveDevice;\nuse static_cell::StaticCell;\nuse {defmt_rtt as _, panic_probe as _};\n\nbind_interrupts!(struct Irqs {\n    DMA_IRQ_0 => dma::InterruptHandler<DMA_CH0>, dma::InterruptHandler<DMA_CH1>;\n});\n\n#[embassy_executor::task]\nasync fn ethernet_task(\n    runner: Runner<\n        'static,\n        W5500,\n        ExclusiveDevice<Spi<'static, SPI0, Async>, Output<'static>, Delay>,\n        Input<'static>,\n        Output<'static>,\n    >,\n) -> ! {\n    runner.run().await\n}\n\n#[embassy_executor::task]\nasync fn net_task(mut runner: embassy_net::Runner<'static, Device<'static>>) -> ! {\n    runner.run().await\n}\n\n#[embassy_executor::main]\nasync fn main(spawner: Spawner) {\n    let p = embassy_rp::init(Default::default());\n    let mut rng = RoscRng;\n\n    let mut spi_cfg = SpiConfig::default();\n    spi_cfg.frequency = 50_000_000;\n    let (miso, mosi, clk) = (p.PIN_16, p.PIN_19, p.PIN_18);\n    let spi = Spi::new(p.SPI0, clk, mosi, miso, p.DMA_CH0, p.DMA_CH1, Irqs, spi_cfg);\n    let cs = Output::new(p.PIN_17, Level::High);\n    let w5500_int = Input::new(p.PIN_21, Pull::Up);\n    let w5500_reset = Output::new(p.PIN_20, Level::High);\n\n    let mac_addr = [0x02, 0x00, 0x00, 0x00, 0x00, 0x00];\n    static STATE: StaticCell<State<8, 8>> = StaticCell::new();\n    let state = STATE.init(State::<8, 8>::new());\n    let (device, runner) = embassy_net_wiznet::new(\n        mac_addr,\n        state,\n        ExclusiveDevice::new(spi, cs, Delay),\n        w5500_int,\n        w5500_reset,\n    )\n    .await\n    .unwrap();\n    spawner.spawn(unwrap!(ethernet_task(runner)));\n\n    // Generate random seed\n    let seed = rng.next_u64();\n\n    // Init network stack\n    static RESOURCES: StaticCell<StackResources<3>> = StaticCell::new();\n    let (stack, runner) = embassy_net::new(\n        device,\n        embassy_net::Config::dhcpv4(Default::default()),\n        RESOURCES.init(StackResources::new()),\n        seed,\n    );\n\n    // Launch network task\n    spawner.spawn(unwrap!(net_task(runner)));\n\n    info!(\"Waiting for DHCP...\");\n    let cfg = wait_for_config(stack).await;\n    let local_addr = cfg.address.address();\n    info!(\"IP address: {:?}\", local_addr);\n\n    // Then we can use it!\n    let mut rx_buffer = [0; 4096];\n    let mut tx_buffer = [0; 4096];\n    let mut rx_meta = [PacketMetadata::EMPTY; 16];\n    let mut tx_meta = [PacketMetadata::EMPTY; 16];\n    let mut buf = [0; 4096];\n    loop {\n        let mut socket = UdpSocket::new(stack, &mut rx_meta, &mut rx_buffer, &mut tx_meta, &mut tx_buffer);\n        socket.bind(1234).unwrap();\n\n        loop {\n            let (n, ep) = socket.recv_from(&mut buf).await.unwrap();\n            if let Ok(s) = core::str::from_utf8(&buf[..n]) {\n                info!(\"rxd from {}: {}\", ep, s);\n            }\n            socket.send_to(&buf[..n], ep).await.unwrap();\n        }\n    }\n}\n\nasync fn wait_for_config(stack: Stack<'static>) -> embassy_net::StaticConfigV4 {\n    loop {\n        if let Some(config) = stack.config_v4() {\n            return config.clone();\n        }\n        yield_now().await;\n    }\n}\n"
  },
  {
    "path": "examples/rp235x/src/bin/ethernet_w6300_udp.rs",
    "content": "//! This example implements a UDP server listening on port 1234 and echoing back the data.\n//!\n//! Example written for the [`WIZnet W6300-EVB-Pico2`](https://wiznet.io/products/evaluation-boards/w6300-evb-pico2) board.\n\n#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_futures::yield_now;\nuse embassy_net::udp::{PacketMetadata, UdpSocket};\nuse embassy_net::{Stack, StackResources};\nuse embassy_net_wiznet::chip::W6300;\nuse embassy_net_wiznet::*;\nuse embassy_rp::clocks::RoscRng;\nuse embassy_rp::gpio::{Input, Level, Output, Pull};\nuse embassy_rp::peripherals::{DMA_CH0, DMA_CH1, PIO0};\nuse embassy_rp::pio::{InterruptHandler, Pio};\nuse embassy_rp::pio_programs::spi::Spi;\nuse embassy_rp::spi::{Async, Config as SpiConfig};\nuse embassy_rp::{bind_interrupts, dma};\nuse embassy_time::Delay;\nuse embedded_hal_bus::spi::ExclusiveDevice;\nuse static_cell::StaticCell;\nuse {defmt_rtt as _, panic_probe as _};\n\nbind_interrupts!(struct Irqs {\n    PIO0_IRQ_0 => InterruptHandler<PIO0>;\n    DMA_IRQ_0 => dma::InterruptHandler<DMA_CH0>, dma::InterruptHandler<DMA_CH1>;\n});\n\n#[embassy_executor::task]\nasync fn ethernet_task(\n    runner: Runner<\n        'static,\n        W6300,\n        ExclusiveDevice<Spi<'static, PIO0, 0, Async>, Output<'static>, Delay>,\n        Input<'static>,\n        Output<'static>,\n    >,\n) -> ! {\n    runner.run().await\n}\n\n#[embassy_executor::task]\nasync fn net_task(mut runner: embassy_net::Runner<'static, Device<'static>>) -> ! {\n    runner.run().await\n}\n\n#[embassy_executor::main]\nasync fn main(spawner: Spawner) {\n    let p = embassy_rp::init(Default::default());\n\n    let mut rng = RoscRng;\n    let mut spi_cfg = SpiConfig::default();\n    spi_cfg.frequency = 15_000_000;\n\n    let Pio { mut common, sm0, .. } = Pio::new(p.PIO0, Irqs);\n\n    let (miso, mosi, clk) = (p.PIN_19, p.PIN_18, p.PIN_17);\n\n    let spi = Spi::new(&mut common, sm0, clk, mosi, miso, p.DMA_CH0, p.DMA_CH1, Irqs, spi_cfg);\n\n    let cs = Output::new(p.PIN_16, Level::High);\n    let w6300_int = Input::new(p.PIN_15, Pull::Up);\n    let w6300_reset = Output::new(p.PIN_22, Level::High);\n\n    let mac_addr = [0x02, 0x00, 0x00, 0x00, 0x00, 0x00];\n\n    static STATE: StaticCell<State<8, 8>> = StaticCell::new();\n    let state = STATE.init(State::<8, 8>::new());\n\n    let (device, runner) = embassy_net_wiznet::new(\n        mac_addr,\n        state,\n        ExclusiveDevice::new(spi, cs, Delay),\n        w6300_int,\n        w6300_reset,\n    )\n    .await\n    .unwrap();\n    spawner.spawn(unwrap!(ethernet_task(runner)));\n\n    // Generate random seed\n    let seed = rng.next_u64();\n\n    // Init network stack\n    static RESOURCES: StaticCell<StackResources<3>> = StaticCell::new();\n    let (stack, runner) = embassy_net::new(\n        device,\n        embassy_net::Config::dhcpv4(Default::default()),\n        RESOURCES.init(StackResources::new()),\n        seed,\n    );\n\n    // Launch network task\n    spawner.spawn(unwrap!(net_task(runner)));\n\n    info!(\"Waiting for DHCP...\");\n    let cfg = wait_for_config(stack).await;\n    let local_addr = cfg.address.address();\n    info!(\"IP address: {:?}\", local_addr);\n\n    // Then we can use it!\n    let mut rx_buffer = [0; 4096];\n    let mut tx_buffer = [0; 4096];\n    let mut rx_meta = [PacketMetadata::EMPTY; 16];\n    let mut tx_meta = [PacketMetadata::EMPTY; 16];\n    let mut buf = [0; 4096];\n    loop {\n        let mut socket = UdpSocket::new(stack, &mut rx_meta, &mut rx_buffer, &mut tx_meta, &mut tx_buffer);\n        socket.bind(1234).unwrap();\n\n        loop {\n            let (n, ep) = socket.recv_from(&mut buf).await.unwrap();\n            if let Ok(s) = core::str::from_utf8(&buf[..n]) {\n                info!(\"rxd from {}: {}\", ep, s);\n            }\n            socket.send_to(&buf[..n], ep).await.unwrap();\n        }\n    }\n}\n\nasync fn wait_for_config(stack: Stack<'static>) -> embassy_net::StaticConfigV4 {\n    loop {\n        if let Some(config) = stack.config_v4() {\n            return config.clone();\n        }\n        yield_now().await;\n    }\n}\n"
  },
  {
    "path": "examples/rp235x/src/bin/flash.rs",
    "content": "//! This example test the flash connected to the RP2350 chip.\n\n#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_rp::bind_interrupts;\nuse embassy_rp::flash::{Async, ERASE_SIZE, FLASH_BASE};\nuse embassy_rp::peripherals::{DMA_CH0, FLASH};\nuse embassy_time::Timer;\nuse {defmt_rtt as _, panic_probe as _};\n\nbind_interrupts!(struct Irqs {\n    DMA_IRQ_0 => embassy_rp::dma::InterruptHandler<DMA_CH0>;\n});\n\nconst ADDR_OFFSET: u32 = 0x100000;\nconst FLASH_SIZE: usize = 2 * 1024 * 1024;\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let p = embassy_rp::init(Default::default());\n    info!(\"Hello World!\");\n\n    // add some delay to give an attached debug probe time to parse the\n    // defmt RTT header. Reading that header might touch flash memory, which\n    // interferes with flash write operations.\n    // https://github.com/knurling-rs/defmt/pull/683\n    Timer::after_millis(10).await;\n\n    let mut flash = embassy_rp::flash::Flash::<_, Async, FLASH_SIZE>::new(p.FLASH, p.DMA_CH0, Irqs);\n\n    erase_write_sector(&mut flash, 0x00);\n\n    multiwrite_bytes(&mut flash, ERASE_SIZE as u32);\n\n    background_read(&mut flash, (ERASE_SIZE * 2) as u32).await;\n\n    info!(\"Flash Works!\");\n}\n\nfn multiwrite_bytes(flash: &mut embassy_rp::flash::Flash<'_, FLASH, Async, FLASH_SIZE>, offset: u32) {\n    info!(\">>>> [multiwrite_bytes]\");\n    let mut read_buf = [0u8; ERASE_SIZE];\n    defmt::unwrap!(flash.blocking_read(ADDR_OFFSET + offset, &mut read_buf));\n\n    info!(\"Addr of flash block is {:x}\", ADDR_OFFSET + offset + FLASH_BASE as u32);\n    info!(\"Contents start with {=[u8]}\", read_buf[0..4]);\n\n    defmt::unwrap!(flash.blocking_erase(ADDR_OFFSET + offset, ADDR_OFFSET + offset + ERASE_SIZE as u32));\n\n    defmt::unwrap!(flash.blocking_read(ADDR_OFFSET + offset, &mut read_buf));\n    info!(\"Contents after erase starts with {=[u8]}\", read_buf[0..4]);\n    if read_buf.iter().any(|x| *x != 0xFF) {\n        defmt::panic!(\"unexpected\");\n    }\n\n    defmt::unwrap!(flash.blocking_write(ADDR_OFFSET + offset, &[0x01]));\n    defmt::unwrap!(flash.blocking_write(ADDR_OFFSET + offset + 1, &[0x02]));\n    defmt::unwrap!(flash.blocking_write(ADDR_OFFSET + offset + 2, &[0x03]));\n    defmt::unwrap!(flash.blocking_write(ADDR_OFFSET + offset + 3, &[0x04]));\n\n    defmt::unwrap!(flash.blocking_read(ADDR_OFFSET + offset, &mut read_buf));\n    info!(\"Contents after write starts with {=[u8]}\", read_buf[0..4]);\n    if read_buf[0..4] != [0x01, 0x02, 0x03, 0x04] {\n        defmt::panic!(\"unexpected\");\n    }\n}\n\nfn erase_write_sector(flash: &mut embassy_rp::flash::Flash<'_, FLASH, Async, FLASH_SIZE>, offset: u32) {\n    info!(\">>>> [erase_write_sector]\");\n    let mut buf = [0u8; ERASE_SIZE];\n    defmt::unwrap!(flash.blocking_read(ADDR_OFFSET + offset, &mut buf));\n\n    info!(\"Addr of flash block is {:x}\", ADDR_OFFSET + offset + FLASH_BASE as u32);\n    info!(\"Contents start with {=[u8]}\", buf[0..4]);\n\n    defmt::unwrap!(flash.blocking_erase(ADDR_OFFSET + offset, ADDR_OFFSET + offset + ERASE_SIZE as u32));\n\n    defmt::unwrap!(flash.blocking_read(ADDR_OFFSET + offset, &mut buf));\n    info!(\"Contents after erase starts with {=[u8]}\", buf[0..4]);\n    if buf.iter().any(|x| *x != 0xFF) {\n        defmt::panic!(\"unexpected\");\n    }\n\n    for b in buf.iter_mut() {\n        *b = 0xDA;\n    }\n\n    defmt::unwrap!(flash.blocking_write(ADDR_OFFSET + offset, &buf));\n\n    defmt::unwrap!(flash.blocking_read(ADDR_OFFSET + offset, &mut buf));\n    info!(\"Contents after write starts with {=[u8]}\", buf[0..4]);\n    if buf.iter().any(|x| *x != 0xDA) {\n        defmt::panic!(\"unexpected\");\n    }\n}\n\nasync fn background_read(flash: &mut embassy_rp::flash::Flash<'_, FLASH, Async, FLASH_SIZE>, offset: u32) {\n    info!(\">>>> [background_read]\");\n\n    let mut buf = [0u32; 8];\n    defmt::unwrap!(flash.background_read(ADDR_OFFSET + offset, &mut buf)).await;\n\n    info!(\"Addr of flash block is {:x}\", ADDR_OFFSET + offset + FLASH_BASE as u32);\n    info!(\"Contents start with {=u32:x}\", buf[0]);\n\n    defmt::unwrap!(flash.blocking_erase(ADDR_OFFSET + offset, ADDR_OFFSET + offset + ERASE_SIZE as u32));\n\n    defmt::unwrap!(flash.background_read(ADDR_OFFSET + offset, &mut buf)).await;\n    info!(\"Contents after erase starts with {=u32:x}\", buf[0]);\n    if buf.iter().any(|x| *x != 0xFFFFFFFF) {\n        defmt::panic!(\"unexpected\");\n    }\n\n    for b in buf.iter_mut() {\n        *b = 0xDABA1234;\n    }\n\n    defmt::unwrap!(flash.blocking_write(ADDR_OFFSET + offset, unsafe {\n        core::slice::from_raw_parts(buf.as_ptr() as *const u8, buf.len() * 4)\n    }));\n\n    defmt::unwrap!(flash.background_read(ADDR_OFFSET + offset, &mut buf)).await;\n    info!(\"Contents after write starts with {=u32:x}\", buf[0]);\n    if buf.iter().any(|x| *x != 0xDABA1234) {\n        defmt::panic!(\"unexpected\");\n    }\n}\n"
  },
  {
    "path": "examples/rp235x/src/bin/gpio_async.rs",
    "content": "//! This example shows how async gpio can be used with a RP235x.\n//!\n//! The LED on the RP Pico W board is connected differently. See wifi_blinky.rs.\n\n#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_rp::gpio;\nuse embassy_time::Timer;\nuse gpio::{Input, Level, Output, Pull};\nuse {defmt_rtt as _, panic_probe as _};\n\n/// It requires an external signal to be manually triggered on PIN 16. For\n/// example, this could be accomplished using an external power source with a\n/// button so that it is possible to toggle the signal from low to high.\n///\n/// This example will begin with turning on the LED on the board and wait for a\n/// high signal on PIN 16. Once the high event/signal occurs the program will\n/// continue and turn off the LED, and then wait for 2 seconds before completing\n/// the loop and starting over again.\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let p = embassy_rp::init(Default::default());\n    let mut led = Output::new(p.PIN_25, Level::Low);\n    let mut async_input = Input::new(p.PIN_16, Pull::None);\n\n    loop {\n        info!(\"wait_for_high. Turn on LED\");\n        led.set_high();\n\n        async_input.wait_for_high().await;\n\n        info!(\"done wait_for_high. Turn off LED\");\n        led.set_low();\n\n        Timer::after_secs(2).await;\n    }\n}\n"
  },
  {
    "path": "examples/rp235x/src/bin/gpout.rs",
    "content": "//! This example shows how GPOUT (General purpose clock outputs) can toggle a output pin.\n//!\n//! The LED on the RP Pico W board is connected differently. Add a LED and resistor to another pin.\n\n#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_rp::clocks;\nuse embassy_time::Timer;\nuse {defmt_rtt as _, panic_probe as _};\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let p = embassy_rp::init(Default::default());\n\n    let gpout3 = clocks::Gpout::new(p.PIN_25);\n    gpout3.set_div(1000, 0);\n    gpout3.enable();\n\n    loop {\n        gpout3.set_src(clocks::GpoutSrc::Sys);\n        info!(\n            \"Pin 25 is now outputing CLK_SYS/1000, should be toggling at {}\",\n            gpout3.get_freq()\n        );\n        Timer::after_secs(2).await;\n\n        gpout3.set_src(clocks::GpoutSrc::Ref);\n        info!(\n            \"Pin 25 is now outputing CLK_REF/1000, should be toggling at {}\",\n            gpout3.get_freq()\n        );\n        Timer::after_secs(2).await;\n    }\n}\n"
  },
  {
    "path": "examples/rp235x/src/bin/i2c_async.rs",
    "content": "//! This example shows how to communicate asynchronous using i2c with external chips.\n//!\n//! Example written for the [`MCP23017 16-Bit I2C I/O Expander with Serial Interface`] chip.\n//! (https://www.microchip.com/en-us/product/mcp23017)\n\n#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_rp::bind_interrupts;\nuse embassy_rp::i2c::{self, Config, InterruptHandler};\nuse embassy_rp::peripherals::I2C1;\nuse embassy_time::Timer;\nuse embedded_hal_async::i2c::I2c;\nuse {defmt_rtt as _, panic_probe as _};\n\nbind_interrupts!(struct Irqs {\n    I2C1_IRQ => InterruptHandler<I2C1>;\n});\n\n#[allow(dead_code)]\nmod mcp23017 {\n    pub const ADDR: u8 = 0x20; // default addr\n\n    macro_rules! mcpregs {\n        ($($name:ident : $val:expr),* $(,)?) => {\n            $(\n                pub const $name: u8 = $val;\n            )*\n\n            pub fn regname(reg: u8) -> &'static str {\n                match reg {\n                    $(\n                        $val => stringify!($name),\n                    )*\n                    _ => panic!(\"bad reg\"),\n                }\n            }\n        }\n    }\n\n    // These are correct for IOCON.BANK=0\n    mcpregs! {\n        IODIRA: 0x00,\n        IPOLA: 0x02,\n        GPINTENA: 0x04,\n        DEFVALA: 0x06,\n        INTCONA: 0x08,\n        IOCONA: 0x0A,\n        GPPUA: 0x0C,\n        INTFA: 0x0E,\n        INTCAPA: 0x10,\n        GPIOA: 0x12,\n        OLATA: 0x14,\n        IODIRB: 0x01,\n        IPOLB: 0x03,\n        GPINTENB: 0x05,\n        DEFVALB: 0x07,\n        INTCONB: 0x09,\n        IOCONB: 0x0B,\n        GPPUB: 0x0D,\n        INTFB: 0x0F,\n        INTCAPB: 0x11,\n        GPIOB: 0x13,\n        OLATB: 0x15,\n    }\n}\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let p = embassy_rp::init(Default::default());\n\n    let sda = p.PIN_14;\n    let scl = p.PIN_15;\n\n    info!(\"set up i2c \");\n    let mut i2c = i2c::I2c::new_async(p.I2C1, scl, sda, Irqs, Config::default());\n\n    use mcp23017::*;\n\n    info!(\"init mcp23017 config for IxpandO\");\n    // init - a outputs, b inputs\n    i2c.write(ADDR, &[IODIRA, 0x00]).await.unwrap();\n    i2c.write(ADDR, &[IODIRB, 0xff]).await.unwrap();\n    i2c.write(ADDR, &[GPPUB, 0xff]).await.unwrap(); // pullups\n\n    let mut val = 1;\n    loop {\n        let mut portb = [0];\n\n        i2c.write_read(mcp23017::ADDR, &[GPIOB], &mut portb).await.unwrap();\n        info!(\"portb = {:02x}\", portb[0]);\n        i2c.write(mcp23017::ADDR, &[GPIOA, val | portb[0]]).await.unwrap();\n        val = val.rotate_left(1);\n\n        // get a register dump\n        info!(\"getting register dump\");\n        let mut regs = [0; 22];\n        i2c.write_read(ADDR, &[0], &mut regs).await.unwrap();\n        // always get the regdump but only display it if portb'0 is set\n        if portb[0] & 1 != 0 {\n            for (idx, reg) in regs.into_iter().enumerate() {\n                info!(\"{} => {:02x}\", regname(idx as u8), reg);\n            }\n        }\n\n        Timer::after_millis(100).await;\n    }\n}\n"
  },
  {
    "path": "examples/rp235x/src/bin/i2c_async_embassy.rs",
    "content": "//! This example shows how to communicate asynchronous using i2c with external chip.\n//!\n//! It's using embassy's functions directly instead of traits from embedded_hal_async::i2c::I2c.\n//! While most of i2c devices are addressed using 7 bits, an extension allows 10 bits too.\n\n#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_rp::i2c::InterruptHandler;\nuse {defmt_rtt as _, panic_probe as _};\n\n// Our anonymous hypotetical temperature sensor could be:\n// a 12-bit sensor, with 100ms startup time, range of -40*C - 125*C, and precision 0.25*C\n// It requires no configuration or calibration, works with all i2c bus speeds,\n// never stretches clock or does anything complicated. Replies with one u16.\n// It requires only one write to take it out of suspend mode, and stays on.\n// Often result would be just on 12 bits, but here we'll simplify it to 16.\n\nenum UncomplicatedSensorId {\n    A(UncomplicatedSensorU8),\n    B(UncomplicatedSensorU16),\n}\nenum UncomplicatedSensorU8 {\n    First = 0x48,\n}\nenum UncomplicatedSensorU16 {\n    Other = 0x0049,\n}\n\nimpl Into<u16> for UncomplicatedSensorU16 {\n    fn into(self) -> u16 {\n        self as u16\n    }\n}\nimpl Into<u16> for UncomplicatedSensorU8 {\n    fn into(self) -> u16 {\n        0x48\n    }\n}\nimpl From<UncomplicatedSensorId> for u16 {\n    fn from(t: UncomplicatedSensorId) -> Self {\n        match t {\n            UncomplicatedSensorId::A(x) => x.into(),\n            UncomplicatedSensorId::B(x) => x.into(),\n        }\n    }\n}\n\nembassy_rp::bind_interrupts!(struct Irqs {\n    I2C1_IRQ => InterruptHandler<embassy_rp::peripherals::I2C1>;\n});\n\n#[embassy_executor::main]\nasync fn main(_task_spawner: embassy_executor::Spawner) {\n    let p = embassy_rp::init(Default::default());\n    let sda = p.PIN_14;\n    let scl = p.PIN_15;\n    let config = embassy_rp::i2c::Config::default();\n    let mut bus = embassy_rp::i2c::I2c::new_async(p.I2C1, scl, sda, Irqs, config);\n\n    const WAKEYWAKEY: u16 = 0xBABE;\n    let mut result: [u8; 2] = [0, 0];\n    // wait for sensors to initialize\n    embassy_time::Timer::after(embassy_time::Duration::from_millis(100)).await;\n\n    let _res_1 = bus\n        .write_async(UncomplicatedSensorU8::First, WAKEYWAKEY.to_be_bytes())\n        .await;\n    let _res_2 = bus\n        .write_async(UncomplicatedSensorU16::Other, WAKEYWAKEY.to_be_bytes())\n        .await;\n\n    loop {\n        let s1 = UncomplicatedSensorId::A(UncomplicatedSensorU8::First);\n        let s2 = UncomplicatedSensorId::B(UncomplicatedSensorU16::Other);\n        let sensors = [s1, s2];\n        for sensor in sensors {\n            if bus.read_async(sensor, &mut result).await.is_ok() {\n                info!(\"Result {}\", u16::from_be_bytes(result.into()));\n            }\n        }\n        embassy_time::Timer::after(embassy_time::Duration::from_millis(200)).await;\n    }\n}\n"
  },
  {
    "path": "examples/rp235x/src/bin/i2c_blocking.rs",
    "content": "//! This example shows how to communicate using i2c with external chips.\n//!\n//! Example written for the [`MCP23017 16-Bit I2C I/O Expander with Serial Interface`] chip.\n//! (https://www.microchip.com/en-us/product/mcp23017)\n\n#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_rp::i2c::{self, Config};\nuse embassy_time::Timer;\nuse embedded_hal_1::i2c::I2c;\nuse {defmt_rtt as _, panic_probe as _};\n\n#[allow(dead_code)]\nmod mcp23017 {\n    pub const ADDR: u8 = 0x20; // default addr\n\n    pub const IODIRA: u8 = 0x00;\n    pub const IPOLA: u8 = 0x02;\n    pub const GPINTENA: u8 = 0x04;\n    pub const DEFVALA: u8 = 0x06;\n    pub const INTCONA: u8 = 0x08;\n    pub const IOCONA: u8 = 0x0A;\n    pub const GPPUA: u8 = 0x0C;\n    pub const INTFA: u8 = 0x0E;\n    pub const INTCAPA: u8 = 0x10;\n    pub const GPIOA: u8 = 0x12;\n    pub const OLATA: u8 = 0x14;\n    pub const IODIRB: u8 = 0x01;\n    pub const IPOLB: u8 = 0x03;\n    pub const GPINTENB: u8 = 0x05;\n    pub const DEFVALB: u8 = 0x07;\n    pub const INTCONB: u8 = 0x09;\n    pub const IOCONB: u8 = 0x0B;\n    pub const GPPUB: u8 = 0x0D;\n    pub const INTFB: u8 = 0x0F;\n    pub const INTCAPB: u8 = 0x11;\n    pub const GPIOB: u8 = 0x13;\n    pub const OLATB: u8 = 0x15;\n}\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let p = embassy_rp::init(Default::default());\n\n    let sda = p.PIN_14;\n    let scl = p.PIN_15;\n\n    info!(\"set up i2c \");\n    let mut i2c = i2c::I2c::new_blocking(p.I2C1, scl, sda, Config::default());\n\n    use mcp23017::*;\n\n    info!(\"init mcp23017 config for IxpandO\");\n    // init - a outputs, b inputs\n    i2c.write(ADDR, &[IODIRA, 0x00]).unwrap();\n    i2c.write(ADDR, &[IODIRB, 0xff]).unwrap();\n    i2c.write(ADDR, &[GPPUB, 0xff]).unwrap(); // pullups\n\n    let mut val = 0xaa;\n    loop {\n        let mut portb = [0];\n\n        i2c.write(mcp23017::ADDR, &[GPIOA, val]).unwrap();\n        i2c.write_read(mcp23017::ADDR, &[GPIOB], &mut portb).unwrap();\n\n        info!(\"portb = {:02x}\", portb[0]);\n        val = !val;\n\n        Timer::after_secs(1).await;\n    }\n}\n"
  },
  {
    "path": "examples/rp235x/src/bin/i2c_slave.rs",
    "content": "//! This example shows how to use the 2040 as an i2c slave.\n#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_rp::peripherals::{I2C0, I2C1};\nuse embassy_rp::{bind_interrupts, i2c, i2c_slave};\nuse embassy_time::Timer;\nuse embedded_hal_async::i2c::I2c;\nuse {defmt_rtt as _, panic_probe as _};\n\nbind_interrupts!(struct Irqs {\n    I2C0_IRQ => i2c::InterruptHandler<I2C0>;\n    I2C1_IRQ => i2c::InterruptHandler<I2C1>;\n});\n\nconst DEV_ADDR: u8 = 0x42;\n\n#[embassy_executor::task]\nasync fn device_task(mut dev: i2c_slave::I2cSlave<'static, I2C1>) -> ! {\n    info!(\"Device start\");\n\n    let mut state = 0;\n\n    loop {\n        let mut buf = [0u8; 128];\n        match dev.listen(&mut buf).await {\n            Ok(i2c_slave::Command::GeneralCall(len)) => info!(\"Device received general call write: {}\", buf[..len]),\n            Ok(i2c_slave::Command::Read) => loop {\n                match dev.respond_to_read(&[state]).await {\n                    Ok(x) => match x {\n                        i2c_slave::ReadStatus::Done => break,\n                        i2c_slave::ReadStatus::NeedMoreBytes => (),\n                        i2c_slave::ReadStatus::LeftoverBytes(x) => {\n                            info!(\"tried to write {} extra bytes\", x);\n                            break;\n                        }\n                    },\n                    Err(e) => error!(\"error while responding {}\", e),\n                }\n            },\n            Ok(i2c_slave::Command::Write(len)) => info!(\"Device received write: {}\", buf[..len]),\n            Ok(i2c_slave::Command::WriteRead(len)) => {\n                info!(\"device received write read: {:x}\", buf[..len]);\n                match buf[0] {\n                    // Set the state\n                    0xC2 => {\n                        state = buf[1];\n                        match dev.respond_and_fill(&[state], 0x00).await {\n                            Ok(read_status) => info!(\"response read status {}\", read_status),\n                            Err(e) => error!(\"error while responding {}\", e),\n                        }\n                    }\n                    // Reset State\n                    0xC8 => {\n                        state = 0;\n                        match dev.respond_and_fill(&[state], 0x00).await {\n                            Ok(read_status) => info!(\"response read status {}\", read_status),\n                            Err(e) => error!(\"error while responding {}\", e),\n                        }\n                    }\n                    x => error!(\"Invalid Write Read {:x}\", x),\n                }\n            }\n            Err(e) => error!(\"{}\", e),\n        }\n    }\n}\n\n#[embassy_executor::task]\nasync fn controller_task(mut con: i2c::I2c<'static, I2C0, i2c::Async>) {\n    info!(\"Controller start\");\n\n    loop {\n        let mut resp_buff = [0u8; 2];\n        for i in 0..10 {\n            match con.write_read(DEV_ADDR, &[0xC2, i], &mut resp_buff).await {\n                Ok(_) => info!(\"write_read response: {}\", resp_buff),\n                Err(e) => error!(\"Error writing {}\", e),\n            }\n\n            Timer::after_millis(100).await;\n        }\n        match con.read(DEV_ADDR, &mut resp_buff).await {\n            Ok(_) => info!(\"read response: {}\", resp_buff),\n            Err(e) => error!(\"Error writing {}\", e),\n        }\n        match con.write_read(DEV_ADDR, &[0xC8], &mut resp_buff).await {\n            Ok(_) => info!(\"write_read response: {}\", resp_buff),\n            Err(e) => error!(\"Error writing {}\", e),\n        }\n        Timer::after_millis(100).await;\n    }\n}\n\n#[embassy_executor::main]\nasync fn main(spawner: Spawner) {\n    let p = embassy_rp::init(Default::default());\n    info!(\"Hello World!\");\n\n    let d_sda = p.PIN_3;\n    let d_scl = p.PIN_2;\n    let mut config = i2c_slave::Config::default();\n    config.addr = DEV_ADDR as u16;\n    let device = i2c_slave::I2cSlave::new(p.I2C1, d_sda, d_scl, Irqs, config);\n\n    spawner.spawn(unwrap!(device_task(device)));\n\n    let c_sda = p.PIN_1;\n    let c_scl = p.PIN_0;\n    let mut config = i2c::Config::default();\n    config.frequency = 1_000_000;\n    let controller = i2c::I2c::new_async(p.I2C0, c_sda, c_scl, Irqs, config);\n\n    spawner.spawn(unwrap!(controller_task(controller)));\n}\n"
  },
  {
    "path": "examples/rp235x/src/bin/interrupt.rs",
    "content": "//! This example shows how you can use raw interrupt handlers alongside embassy.\n//! The example also showcases some of the options available for sharing resources/data.\n//!\n//! In the example, an ADC reading is triggered every time the PWM wraps around.\n//! The sample data is sent down a channel, to be processed inside a low priority task.\n//! The processed data is then used to adjust the PWM duty cycle, once every second.\n\n#![no_std]\n#![no_main]\n\nuse core::cell::{Cell, RefCell};\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_rp::adc::{self, Adc, Blocking};\nuse embassy_rp::gpio::Pull;\nuse embassy_rp::interrupt;\nuse embassy_rp::pwm::{Config, Pwm};\nuse embassy_sync::blocking_mutex::Mutex;\nuse embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;\nuse embassy_sync::channel::Channel;\nuse embassy_time::{Duration, Ticker};\nuse portable_atomic::{AtomicU32, Ordering};\nuse static_cell::StaticCell;\nuse {defmt_rtt as _, panic_probe as _};\n\nstatic COUNTER: AtomicU32 = AtomicU32::new(0);\nstatic PWM: Mutex<CriticalSectionRawMutex, RefCell<Option<Pwm>>> = Mutex::new(RefCell::new(None));\nstatic ADC: Mutex<CriticalSectionRawMutex, RefCell<Option<(Adc<Blocking>, adc::Channel)>>> =\n    Mutex::new(RefCell::new(None));\nstatic ADC_VALUES: Channel<CriticalSectionRawMutex, u16, 2048> = Channel::new();\n\n#[embassy_executor::main]\nasync fn main(spawner: Spawner) {\n    let p = embassy_rp::init(Default::default());\n\n    let adc = Adc::new_blocking(p.ADC, Default::default());\n    let p26 = adc::Channel::new_pin(p.PIN_26, Pull::None);\n    ADC.lock(|a| a.borrow_mut().replace((adc, p26)));\n\n    let pwm = Pwm::new_output_b(p.PWM_SLICE4, p.PIN_25, Default::default());\n    PWM.lock(|p| p.borrow_mut().replace(pwm));\n\n    // Enable the interrupt for pwm slice 4\n    embassy_rp::pac::PWM.irq0_inte().modify(|w| w.set_ch4(true));\n    unsafe {\n        cortex_m::peripheral::NVIC::unmask(interrupt::PWM_IRQ_WRAP_0);\n    }\n\n    // Tasks require their resources to have 'static lifetime\n    // No Mutex needed when sharing within the same executor/prio level\n    static AVG: StaticCell<Cell<u32>> = StaticCell::new();\n    let avg = AVG.init(Default::default());\n    spawner.spawn(processing(avg).unwrap());\n\n    let mut ticker = Ticker::every(Duration::from_secs(1));\n    loop {\n        ticker.next().await;\n        let freq = COUNTER.swap(0, Ordering::Relaxed);\n        info!(\"pwm freq: {:?} Hz\", freq);\n        info!(\"adc average: {:?}\", avg.get());\n\n        // Update the pwm duty cycle, based on the averaged adc reading\n        let mut config = Config::default();\n        config.compare_b = ((avg.get() as f32 / 4095.0) * config.top as f32) as _;\n        PWM.lock(|p| p.borrow_mut().as_mut().unwrap().set_config(&config));\n    }\n}\n\n#[embassy_executor::task]\nasync fn processing(avg: &'static Cell<u32>) {\n    let mut buffer: heapless::HistoryBuf<u16, 100> = Default::default();\n    loop {\n        let val = ADC_VALUES.receive().await;\n        buffer.write(val);\n        let sum: u32 = buffer.iter().map(|x| *x as u32).sum();\n        avg.set(sum / buffer.len() as u32);\n    }\n}\n\n#[interrupt]\nfn PWM_IRQ_WRAP_0() {\n    critical_section::with(|cs| {\n        let mut adc = ADC.borrow(cs).borrow_mut();\n        let (adc, p26) = adc.as_mut().unwrap();\n        let val = adc.blocking_read(p26).unwrap();\n        ADC_VALUES.try_send(val).ok();\n\n        // Clear the interrupt, so we don't immediately re-enter this irq handler\n        PWM.borrow(cs).borrow_mut().as_mut().unwrap().clear_wrapped();\n    });\n    COUNTER.fetch_add(1, Ordering::Relaxed);\n}\n"
  },
  {
    "path": "examples/rp235x/src/bin/multicore.rs",
    "content": "//! This example shows how to send messages between the two cores in the RP235x chip.\n//!\n//! The LED on the RP Pico W board is connected differently. See wifi_blinky.rs.\n\n#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_executor::Executor;\nuse embassy_rp::gpio::{Level, Output};\nuse embassy_rp::multicore::{Stack, spawn_core1};\nuse embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;\nuse embassy_sync::channel::Channel;\nuse embassy_time::Timer;\nuse static_cell::StaticCell;\nuse {defmt_rtt as _, panic_probe as _};\n\nstatic mut CORE1_STACK: Stack<4096> = Stack::new();\nstatic EXECUTOR0: StaticCell<Executor> = StaticCell::new();\nstatic EXECUTOR1: StaticCell<Executor> = StaticCell::new();\nstatic CHANNEL: Channel<CriticalSectionRawMutex, LedState, 1> = Channel::new();\n\nenum LedState {\n    On,\n    Off,\n}\n\n#[cortex_m_rt::entry]\nfn main() -> ! {\n    let p = embassy_rp::init(Default::default());\n    let led = Output::new(p.PIN_25, Level::Low);\n\n    spawn_core1(\n        p.CORE1,\n        unsafe { &mut *core::ptr::addr_of_mut!(CORE1_STACK) },\n        move || {\n            let executor1 = EXECUTOR1.init(Executor::new());\n            executor1.run(|spawner| spawner.spawn(unwrap!(core1_task(led))));\n        },\n    );\n\n    let executor0 = EXECUTOR0.init(Executor::new());\n    executor0.run(|spawner| spawner.spawn(unwrap!(core0_task())));\n}\n\n#[embassy_executor::task]\nasync fn core0_task() {\n    info!(\"Hello from core 0\");\n    loop {\n        CHANNEL.send(LedState::On).await;\n        Timer::after_millis(100).await;\n        CHANNEL.send(LedState::Off).await;\n        Timer::after_millis(400).await;\n    }\n}\n\n#[embassy_executor::task]\nasync fn core1_task(mut led: Output<'static>) {\n    info!(\"Hello from core 1\");\n    loop {\n        match CHANNEL.receive().await {\n            LedState::On => led.set_high(),\n            LedState::Off => led.set_low(),\n        }\n    }\n}\n"
  },
  {
    "path": "examples/rp235x/src/bin/multicore_stack_overflow.rs",
    "content": "//! This example tests stack overflow handling on core1 of the RP235x chip.\n\n#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_executor::Executor;\nuse embassy_rp::gpio::{Level, Output};\nuse embassy_rp::multicore::{Stack, spawn_core1};\nuse embassy_time::Timer;\nuse static_cell::StaticCell;\nuse {defmt_rtt as _, panic_probe as _};\n\nconst CORE1_STACK_LENGTH: usize = 4096;\n\nstatic mut CORE1_STACK: Stack<CORE1_STACK_LENGTH> = Stack::new();\nstatic EXECUTOR0: StaticCell<Executor> = StaticCell::new();\nstatic EXECUTOR1: StaticCell<Executor> = StaticCell::new();\n\n#[cortex_m_rt::entry]\nfn main() -> ! {\n    let p = embassy_rp::init(Default::default());\n    let led = Output::new(p.PIN_25, Level::Low);\n\n    spawn_core1(\n        p.CORE1,\n        unsafe { &mut *core::ptr::addr_of_mut!(CORE1_STACK) },\n        move || {\n            let executor1 = EXECUTOR1.init(Executor::new());\n            executor1.run(|spawner| spawner.spawn(unwrap!(core1_task())));\n        },\n    );\n\n    let executor0 = EXECUTOR0.init(Executor::new());\n    executor0.run(|spawner| spawner.spawn(unwrap!(core0_task(led))));\n}\n\n#[embassy_executor::task]\nasync fn core0_task(mut led: Output<'static>) {\n    info!(\"Hello from core 0\");\n    loop {\n        info!(\"core 0 still alive\");\n        led.set_high();\n        Timer::after_millis(500).await;\n        led.set_low();\n        Timer::after_millis(500).await;\n    }\n}\n\nfn blow_my_stack() {\n    // Allocating an array a little larger than our stack should ensure a stack overflow when it is used.\n    let t = [0u8; CORE1_STACK_LENGTH + 64];\n\n    info!(\"Array initialised without error\");\n    // We need to use black_box to otherwise the compiler is too smart and will optimise all of this away.\n    // We shouldn't get to this code - the initialisation above will touch the stack guard.\n    for ref i in t {\n        let _data = core::hint::black_box(*i) + 1;\n    }\n}\n\n#[embassy_executor::task]\nasync fn core1_task() {\n    info!(\"Hello from core 1\");\n\n    blow_my_stack();\n\n    loop {\n        info!(\"core 1 still alive\");\n        Timer::after_millis(1000).await;\n    }\n}\n"
  },
  {
    "path": "examples/rp235x/src/bin/multiprio.rs",
    "content": "//! This example showcases how to create multiple Executor instances to run tasks at\n//! different priority levels.\n//!\n//! Low priority executor runs in thread mode (not interrupt), and uses `sev` for signaling\n//! there's work in the queue, and `wfe` for waiting for work.\n//!\n//! Medium and high priority executors run in two interrupts with different priorities.\n//! Signaling work is done by pending the interrupt. No \"waiting\" needs to be done explicitly, since\n//! when there's work the interrupt will trigger and run the executor.\n//!\n//! Sample output below. Note that high priority ticks can interrupt everything else, and\n//! medium priority computations can interrupt low priority computations, making them to appear\n//! to take significantly longer time.\n//!\n//! ```not_rust\n//!     [med] Starting long computation\n//!     [med] done in 992 ms\n//!         [high] tick!\n//! [low] Starting long computation\n//!     [med] Starting long computation\n//!         [high] tick!\n//!         [high] tick!\n//!     [med] done in 993 ms\n//!     [med] Starting long computation\n//!         [high] tick!\n//!         [high] tick!\n//!     [med] done in 993 ms\n//! [low] done in 3972 ms\n//!     [med] Starting long computation\n//!         [high] tick!\n//!         [high] tick!\n//!     [med] done in 993 ms\n//! ```\n//!\n//! For comparison, try changing the code so all 3 tasks get spawned on the low priority executor.\n//! You will get an output like the following. Note that no computation is ever interrupted.\n//!\n//! ```not_rust\n//!         [high] tick!\n//!     [med] Starting long computation\n//!     [med] done in 496 ms\n//! [low] Starting long computation\n//! [low] done in 992 ms\n//!     [med] Starting long computation\n//!     [med] done in 496 ms\n//!         [high] tick!\n//! [low] Starting long computation\n//! [low] done in 992 ms\n//!         [high] tick!\n//!     [med] Starting long computation\n//!     [med] done in 496 ms\n//!         [high] tick!\n//! ```\n//!\n\n#![no_std]\n#![no_main]\n\nuse cortex_m_rt::entry;\nuse defmt::{info, unwrap};\nuse embassy_executor::{Executor, InterruptExecutor};\nuse embassy_rp::interrupt;\nuse embassy_rp::interrupt::{InterruptExt, Priority};\nuse embassy_time::{Instant, TICK_HZ, Timer};\nuse static_cell::StaticCell;\nuse {defmt_rtt as _, panic_probe as _};\n\n#[embassy_executor::task]\nasync fn run_high() {\n    loop {\n        info!(\"        [high] tick!\");\n        Timer::after_ticks(673740).await;\n    }\n}\n\n#[embassy_executor::task]\nasync fn run_med() {\n    loop {\n        let start = Instant::now();\n        info!(\"    [med] Starting long computation\");\n\n        // Spin-wait to simulate a long CPU computation\n        embassy_time::block_for(embassy_time::Duration::from_secs(1)); // ~1 second\n\n        let end = Instant::now();\n        let ms = end.duration_since(start).as_ticks() * 1000 / TICK_HZ;\n        info!(\"    [med] done in {} ms\", ms);\n\n        Timer::after_ticks(53421).await;\n    }\n}\n\n#[embassy_executor::task]\nasync fn run_low() {\n    loop {\n        let start = Instant::now();\n        info!(\"[low] Starting long computation\");\n\n        // Spin-wait to simulate a long CPU computation\n        embassy_time::block_for(embassy_time::Duration::from_secs(2)); // ~2 seconds\n\n        let end = Instant::now();\n        let ms = end.duration_since(start).as_ticks() * 1000 / TICK_HZ;\n        info!(\"[low] done in {} ms\", ms);\n\n        Timer::after_ticks(82983).await;\n    }\n}\n\nstatic EXECUTOR_HIGH: InterruptExecutor = InterruptExecutor::new();\nstatic EXECUTOR_MED: InterruptExecutor = InterruptExecutor::new();\nstatic EXECUTOR_LOW: StaticCell<Executor> = StaticCell::new();\n\n#[interrupt]\nunsafe fn SWI_IRQ_1() {\n    unsafe { EXECUTOR_HIGH.on_interrupt() }\n}\n\n#[interrupt]\nunsafe fn SWI_IRQ_0() {\n    unsafe { EXECUTOR_MED.on_interrupt() }\n}\n\n#[entry]\nfn main() -> ! {\n    info!(\"Hello World!\");\n\n    let _p = embassy_rp::init(Default::default());\n\n    // High-priority executor: SWI_IRQ_1, priority level 2\n    interrupt::SWI_IRQ_1.set_priority(Priority::P2);\n    let spawner = EXECUTOR_HIGH.start(interrupt::SWI_IRQ_1);\n    spawner.spawn(unwrap!(run_high()));\n\n    // Medium-priority executor: SWI_IRQ_0, priority level 3\n    interrupt::SWI_IRQ_0.set_priority(Priority::P3);\n    let spawner = EXECUTOR_MED.start(interrupt::SWI_IRQ_0);\n    spawner.spawn(unwrap!(run_med()));\n\n    // Low priority executor: runs in thread mode, using WFE/SEV\n    let executor = EXECUTOR_LOW.init(Executor::new());\n    executor.run(|spawner| {\n        spawner.spawn(unwrap!(run_low()));\n    });\n}\n"
  },
  {
    "path": "examples/rp235x/src/bin/otp.rs",
    "content": "//! This example shows reading the OTP constants on the RP235x.\n\n#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_rp::otp;\nuse embassy_time::Timer;\nuse {defmt_rtt as _, panic_probe as _};\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let _ = embassy_rp::init(Default::default());\n    //\n    // add some delay to give an attached debug probe time to parse the\n    // defmt RTT header. Reading that header might touch flash memory, which\n    // interferes with flash write operations.\n    // https://github.com/knurling-rs/defmt/pull/683\n    Timer::after_millis(10).await;\n\n    let chip_id = unwrap!(otp::get_chipid());\n    info!(\"Unique id:{:X}\", chip_id);\n\n    let private_rand = unwrap!(otp::get_private_random_number());\n    info!(\"Private Rand:{:X}\", private_rand);\n\n    loop {\n        Timer::after_secs(1).await;\n    }\n}\n"
  },
  {
    "path": "examples/rp235x/src/bin/overclock.rs",
    "content": "//! # Overclocking the RP2350 to 200 MHz\n//!\n//! This example demonstrates how to configure the RP2350 to run at 200 MHz instead of the default 150 MHz.\n//!\n//! ## Note\n//!\n//! As of yet there is no official support for running the RP235x at higher clock frequencies and/or other core voltages than the default.\n//! Doing so may cause unexpected behavior and/or damage the chip.\n\n#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_rp::clocks::{ClockConfig, CoreVoltage, clk_sys_freq, core_voltage};\nuse embassy_rp::config::Config;\nuse embassy_rp::gpio::{Level, Output};\nuse embassy_time::{Duration, Instant, Timer};\nuse {defmt_rtt as _, panic_probe as _};\n\nconst COUNT_TO: i64 = 10_000_000;\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) -> ! {\n    // Set up for clock frequency of 200 MHz, setting all necessary defaults.\n    let mut config = Config::new(ClockConfig::system_freq(200_000_000).unwrap());\n\n    // since for the rp235x there is no official support for higher clock frequencies, `system_freq()` will not set a voltage for us.\n    // We need to guess the core voltage, that is needed for the higher clock frequency. Going with a small increase from the default 1.1V here, based on\n    // what we know about the RP2040. This is not guaranteed to be correct.\n    config.clocks.core_voltage = CoreVoltage::V1_15;\n\n    // Initialize the peripherals\n    let p = embassy_rp::init(config);\n\n    // Show CPU frequency for verification\n    let sys_freq = clk_sys_freq();\n    info!(\"System clock frequency: {} MHz\", sys_freq / 1_000_000);\n    // Show core voltage for verification\n    let core_voltage = core_voltage().unwrap();\n    info!(\"Core voltage: {}\", core_voltage);\n\n    // LED to indicate the system is running\n    let mut led = Output::new(p.PIN_25, Level::Low);\n\n    loop {\n        // Reset the counter at the start of measurement period\n        let mut counter = 0;\n\n        // Turn LED on while counting\n        led.set_high();\n\n        let start = Instant::now();\n\n        // This is a busy loop that will take some time to complete\n        while counter < COUNT_TO {\n            counter += 1;\n        }\n\n        let elapsed = Instant::now() - start;\n\n        // Report the elapsed time\n        led.set_low();\n        info!(\n            \"At {}Mhz: Elapsed time to count to {}: {}ms\",\n            sys_freq / 1_000_000,\n            counter,\n            elapsed.as_millis()\n        );\n\n        // Wait 2 seconds before starting the next measurement\n        Timer::after(Duration::from_secs(2)).await;\n    }\n}\n"
  },
  {
    "path": "examples/rp235x/src/bin/pio_async.rs",
    "content": "//! This example shows powerful PIO module in the RP235x chip.\n\n#![no_std]\n#![no_main]\nuse defmt::info;\nuse embassy_executor::Spawner;\nuse embassy_rp::peripherals::PIO0;\nuse embassy_rp::pio::program::pio_asm;\nuse embassy_rp::pio::{Common, Config, InterruptHandler, Irq, Pio, PioPin, ShiftDirection, StateMachine};\nuse embassy_rp::{Peri, bind_interrupts};\nuse fixed::traits::ToFixed;\nuse fixed_macro::types::U56F8;\nuse {defmt_rtt as _, panic_probe as _};\n\nbind_interrupts!(struct Irqs {\n    PIO0_IRQ_0 => InterruptHandler<PIO0>;\n});\n\nfn setup_pio_task_sm0<'a>(pio: &mut Common<'a, PIO0>, sm: &mut StateMachine<'a, PIO0, 0>, pin: Peri<'a, impl PioPin>) {\n    // Setup sm0\n\n    // Send data serially to pin\n    let prg = pio_asm!(\n        \".origin 16\",\n        \"set pindirs, 1\",\n        \".wrap_target\",\n        \"out pins,1 [19]\",\n        \".wrap\",\n    );\n\n    let mut cfg = Config::default();\n    cfg.use_program(&pio.load_program(&prg.program), &[]);\n    let out_pin = pio.make_pio_pin(pin);\n    cfg.set_out_pins(&[&out_pin]);\n    cfg.set_set_pins(&[&out_pin]);\n    cfg.clock_divider = (U56F8!(125_000_000) / 20 / 200).to_fixed();\n    cfg.shift_out.auto_fill = true;\n    sm.set_config(&cfg);\n}\n\n#[embassy_executor::task]\nasync fn pio_task_sm0(mut sm: StateMachine<'static, PIO0, 0>) {\n    sm.set_enable(true);\n\n    let mut v = 0x0f0caffa;\n    loop {\n        sm.tx().wait_push(v).await;\n        v ^= 0xffff;\n        info!(\"Pushed {:032b} to FIFO\", v);\n    }\n}\n\nfn setup_pio_task_sm1<'a>(pio: &mut Common<'a, PIO0>, sm: &mut StateMachine<'a, PIO0, 1>) {\n    // Setupm sm1\n\n    // Read 0b10101 repeatedly until ISR is full\n    let prg = pio_asm!(\n        //\n        \".origin 8\",\n        \"set x, 0x15\",\n        \".wrap_target\",\n        \"in x, 5 [31]\",\n        \".wrap\",\n    );\n\n    let mut cfg = Config::default();\n    cfg.use_program(&pio.load_program(&prg.program), &[]);\n    cfg.clock_divider = (U56F8!(125_000_000) / 2000).to_fixed();\n    cfg.shift_in.auto_fill = true;\n    cfg.shift_in.direction = ShiftDirection::Right;\n    sm.set_config(&cfg);\n}\n\n#[embassy_executor::task]\nasync fn pio_task_sm1(mut sm: StateMachine<'static, PIO0, 1>) {\n    sm.set_enable(true);\n    loop {\n        let rx = sm.rx().wait_pull().await;\n        info!(\"Pulled {:032b} from FIFO\", rx);\n    }\n}\n\nfn setup_pio_task_sm2<'a>(pio: &mut Common<'a, PIO0>, sm: &mut StateMachine<'a, PIO0, 2>) {\n    // Setup sm2\n\n    // Repeatedly trigger IRQ 3\n    let prg = pio_asm!(\n        \".origin 0\",\n        \".wrap_target\",\n        \"set x,10\",\n        \"delay:\",\n        \"jmp x-- delay [15]\",\n        \"irq 3 [15]\",\n        \".wrap\",\n    );\n    let mut cfg = Config::default();\n    cfg.use_program(&pio.load_program(&prg.program), &[]);\n    cfg.clock_divider = (U56F8!(125_000_000) / 2000).to_fixed();\n    sm.set_config(&cfg);\n}\n\n#[embassy_executor::task]\nasync fn pio_task_sm2(mut irq: Irq<'static, PIO0, 3>, mut sm: StateMachine<'static, PIO0, 2>) {\n    sm.set_enable(true);\n    loop {\n        irq.wait().await;\n        info!(\"IRQ trigged\");\n    }\n}\n\n#[embassy_executor::main]\nasync fn main(spawner: Spawner) {\n    let p = embassy_rp::init(Default::default());\n    let pio = p.PIO0;\n\n    let Pio {\n        mut common,\n        irq3,\n        mut sm0,\n        mut sm1,\n        mut sm2,\n        ..\n    } = Pio::new(pio, Irqs);\n\n    setup_pio_task_sm0(&mut common, &mut sm0, p.PIN_0);\n    setup_pio_task_sm1(&mut common, &mut sm1);\n    setup_pio_task_sm2(&mut common, &mut sm2);\n    spawner.spawn(pio_task_sm0(sm0).unwrap());\n    spawner.spawn(pio_task_sm1(sm1).unwrap());\n    spawner.spawn(pio_task_sm2(irq3, sm2).unwrap());\n}\n"
  },
  {
    "path": "examples/rp235x/src/bin/pio_dma.rs",
    "content": "//! This example shows powerful PIO module in the RP235x chip.\n\n#![no_std]\n#![no_main]\nuse defmt::info;\nuse embassy_executor::Spawner;\nuse embassy_futures::join::join;\nuse embassy_rp::peripherals::{DMA_CH0, DMA_CH1, PIO0};\nuse embassy_rp::pio::program::pio_asm;\nuse embassy_rp::pio::{Config, InterruptHandler, Pio, ShiftConfig, ShiftDirection};\nuse embassy_rp::{bind_interrupts, dma};\nuse fixed::traits::ToFixed;\nuse fixed_macro::types::U56F8;\nuse {defmt_rtt as _, panic_probe as _};\n\nbind_interrupts!(struct Irqs {\n    PIO0_IRQ_0 => InterruptHandler<PIO0>;\n    DMA_IRQ_0 => dma::InterruptHandler<DMA_CH0>, dma::InterruptHandler<DMA_CH1>;\n});\n\nfn swap_nibbles(v: u32) -> u32 {\n    let v = (v & 0x0f0f_0f0f) << 4 | (v & 0xf0f0_f0f0) >> 4;\n    let v = (v & 0x00ff_00ff) << 8 | (v & 0xff00_ff00) >> 8;\n    (v & 0x0000_ffff) << 16 | (v & 0xffff_0000) >> 16\n}\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let p = embassy_rp::init(Default::default());\n    let pio = p.PIO0;\n    let Pio {\n        mut common,\n        sm0: mut sm,\n        ..\n    } = Pio::new(pio, Irqs);\n\n    let prg = pio_asm!(\n        \".origin 0\",\n        \"set pindirs,1\",\n        \".wrap_target\",\n        \"set y,7\",\n        \"loop:\",\n        \"out x,4\",\n        \"in x,4\",\n        \"jmp y--, loop\",\n        \".wrap\",\n    );\n\n    let mut cfg = Config::default();\n    cfg.use_program(&common.load_program(&prg.program), &[]);\n    cfg.clock_divider = (U56F8!(125_000_000) / U56F8!(10_000)).to_fixed();\n    cfg.shift_in = ShiftConfig {\n        auto_fill: true,\n        threshold: 32,\n        direction: ShiftDirection::Left,\n    };\n    cfg.shift_out = ShiftConfig {\n        auto_fill: true,\n        threshold: 32,\n        direction: ShiftDirection::Right,\n    };\n\n    sm.set_config(&cfg);\n    sm.set_enable(true);\n\n    let mut dma_out_ref = dma::Channel::new(p.DMA_CH0, Irqs);\n    let mut dma_in_ref = dma::Channel::new(p.DMA_CH1, Irqs);\n    let mut dout = [0x12345678u32; 29];\n    for i in 1..dout.len() {\n        dout[i] = (dout[i - 1] & 0x0fff_ffff) * 13 + 7;\n    }\n    let mut din = [0u32; 29];\n    loop {\n        let (rx, tx) = sm.rx_tx();\n        join(\n            tx.dma_push(&mut dma_out_ref, &dout, false),\n            rx.dma_pull(&mut dma_in_ref, &mut din, false),\n        )\n        .await;\n        for i in 0..din.len() {\n            assert_eq!(din[i], swap_nibbles(dout[i]));\n        }\n        info!(\"Swapped {} words\", dout.len());\n    }\n}\n"
  },
  {
    "path": "examples/rp235x/src/bin/pio_hd44780.rs",
    "content": "//! This example shows powerful PIO module in the RP235x chip to communicate with a HD44780 display.\n//! See (https://www.sparkfun.com/datasheets/LCD/HD44780.pdf)\n\n#![no_std]\n#![no_main]\n\nuse core::fmt::Write;\n\nuse embassy_executor::Spawner;\nuse embassy_rp::peripherals::{DMA_CH3, PIO0};\nuse embassy_rp::pio::{InterruptHandler, Pio};\nuse embassy_rp::pio_programs::hd44780::{PioHD44780, PioHD44780CommandSequenceProgram, PioHD44780CommandWordProgram};\nuse embassy_rp::pwm::{self, Pwm};\nuse embassy_rp::{bind_interrupts, dma};\nuse embassy_time::{Instant, Timer};\nuse {defmt_rtt as _, panic_probe as _};\n\nbind_interrupts!(pub struct Irqs {\n    PIO0_IRQ_0 => InterruptHandler<PIO0>;\n    DMA_IRQ_0 => dma::InterruptHandler<DMA_CH3>;\n});\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    // this test assumes a 2x16 HD44780 display attached as follow:\n    //   rs  = PIN0\n    //   rw  = PIN1\n    //   e   = PIN2\n    //   db4 = PIN3\n    //   db5 = PIN4\n    //   db6 = PIN5\n    //   db7 = PIN6\n    // additionally a pwm signal for a bias voltage charge pump is provided on pin 15,\n    // allowing direct connection of the display to the RP235x without level shifters.\n    let p = embassy_rp::init(Default::default());\n\n    let _pwm = Pwm::new_output_b(p.PWM_SLICE7, p.PIN_15, {\n        let mut c = pwm::Config::default();\n        c.divider = 125.into();\n        c.top = 100;\n        c.compare_b = 50;\n        c\n    });\n\n    let Pio {\n        mut common, sm0, irq0, ..\n    } = Pio::new(p.PIO0, Irqs);\n\n    let word_prg = PioHD44780CommandWordProgram::new(&mut common);\n    let seq_prg = PioHD44780CommandSequenceProgram::new(&mut common);\n\n    let mut hd = PioHD44780::new(\n        &mut common,\n        sm0,\n        irq0,\n        p.DMA_CH3,\n        Irqs,\n        p.PIN_0,\n        p.PIN_1,\n        p.PIN_2,\n        p.PIN_3,\n        p.PIN_4,\n        p.PIN_5,\n        p.PIN_6,\n        &word_prg,\n        &seq_prg,\n    )\n    .await;\n\n    loop {\n        struct Buf<const N: usize>([u8; N], usize);\n        impl<const N: usize> Write for Buf<N> {\n            fn write_str(&mut self, s: &str) -> Result<(), core::fmt::Error> {\n                for b in s.as_bytes() {\n                    if self.1 >= N {\n                        return Err(core::fmt::Error);\n                    }\n                    self.0[self.1] = *b;\n                    self.1 += 1;\n                }\n                Ok(())\n            }\n        }\n        let mut buf = Buf([0; 16], 0);\n        write!(buf, \"up {}s\", Instant::now().as_micros() as f32 / 1e6).unwrap();\n        hd.add_line(&buf.0[0..buf.1]).await;\n        Timer::after_secs(1).await;\n    }\n}\n"
  },
  {
    "path": "examples/rp235x/src/bin/pio_i2s.rs",
    "content": "//! This example shows generating audio and sending it to a connected i2s DAC using the PIO\n//! module of the RP235x.\n//!\n//! Connect the i2s DAC as follows:\n//!   bclk : GPIO 18\n//!   lrc  : GPIO 19\n//!   din  : GPIO 20\n//! Then short GPIO 0 to GND to trigger a rising triangle waveform.\n\n#![no_std]\n#![no_main]\n\nuse core::mem;\n\nuse embassy_executor::Spawner;\nuse embassy_rp::gpio::{Input, Pull};\nuse embassy_rp::peripherals::{DMA_CH0, PIO0};\nuse embassy_rp::pio::{InterruptHandler, Pio};\nuse embassy_rp::pio_programs::i2s::{PioI2sOut, PioI2sOutProgram};\nuse embassy_rp::{bind_interrupts, dma};\nuse static_cell::StaticCell;\nuse {defmt_rtt as _, panic_probe as _};\n\nbind_interrupts!(struct Irqs {\n    PIO0_IRQ_0 => InterruptHandler<PIO0>;\n    DMA_IRQ_0 => dma::InterruptHandler<DMA_CH0>;\n});\n\nconst SAMPLE_RATE: u32 = 48_000;\nconst BIT_DEPTH: u32 = 16;\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let p = embassy_rp::init(Default::default());\n\n    // Setup pio state machine for i2s output\n    let Pio { mut common, sm0, .. } = Pio::new(p.PIO0, Irqs);\n\n    let bit_clock_pin = p.PIN_18;\n    let left_right_clock_pin = p.PIN_19;\n    let data_pin = p.PIN_20;\n\n    let program = PioI2sOutProgram::new(&mut common);\n    let mut i2s = PioI2sOut::new(\n        &mut common,\n        sm0,\n        p.DMA_CH0,\n        Irqs,\n        data_pin,\n        bit_clock_pin,\n        left_right_clock_pin,\n        SAMPLE_RATE,\n        BIT_DEPTH,\n        &program,\n    );\n    i2s.start();\n\n    let fade_input = Input::new(p.PIN_0, Pull::Up);\n\n    // create two audio buffers (back and front) which will take turns being\n    // filled with new audio data and being sent to the pio fifo using dma\n    const BUFFER_SIZE: usize = 960;\n    static DMA_BUFFER: StaticCell<[u32; BUFFER_SIZE * 2]> = StaticCell::new();\n    let dma_buffer = DMA_BUFFER.init_with(|| [0u32; BUFFER_SIZE * 2]);\n    let (mut back_buffer, mut front_buffer) = dma_buffer.split_at_mut(BUFFER_SIZE);\n\n    // start pio state machine\n    let mut fade_value: i32 = 0;\n    let mut phase: i32 = 0;\n\n    loop {\n        // trigger transfer of front buffer data to the pio fifo\n        // but don't await the returned future, yet\n        let dma_future = i2s.write(front_buffer);\n\n        // fade in audio when GPIO 0 pin is shorted to GND\n        let fade_target = if fade_input.is_low() { i32::MAX } else { 0 };\n\n        // fill back buffer with fresh audio samples before awaiting the dma future\n        for s in back_buffer.iter_mut() {\n            // exponential approach of fade_value => fade_target\n            fade_value += (fade_target - fade_value) >> 14;\n            // generate triangle wave with amplitude and frequency based on fade value\n            phase = (phase + (fade_value >> 22)) & 0xffff;\n            let triangle_sample = (phase as i16 as i32).abs() - 16384;\n            let sample = (triangle_sample * (fade_value >> 15)) >> 16;\n            // duplicate mono sample into lower and upper half of dma word\n            *s = (sample as u16 as u32) * 0x10001;\n        }\n\n        // now await the dma future. once the dma finishes, the next buffer needs to be queued\n        // within DMA_DEPTH / SAMPLE_RATE = 8 / 48000 seconds = 166us\n        dma_future.await;\n        mem::swap(&mut back_buffer, &mut front_buffer);\n    }\n}\n"
  },
  {
    "path": "examples/rp235x/src/bin/pio_i2s_rx.rs",
    "content": "//! This example shows receiving audio from a connected I2S microphone (or other audio source)\n//! using the PIO module of the RP235x.\n//!\n//!\n//! Connect the i2s microphone as follows:\n//!   bclk : GPIO 18\n//!   lrc  : GPIO 19\n//!   din  : GPIO 20\n//! Then hold down the boot select button to begin receiving audio. Received I2S words will be written to\n//! buffers for the left and right channels for use in your application, whether that's storage or\n//! further processing\n//!\n//!  Note the const USE_ONBOARD_PULLDOWN is by default set to false, meaning an external\n//!  pull-down resistor is being used on the data pin if required by the mic being used.\n\n#![no_std]\n#![no_main]\nuse core::mem;\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_rp::bind_interrupts;\nuse embassy_rp::peripherals::{DMA_CH0, PIO0};\nuse embassy_rp::pio::{InterruptHandler, Pio};\nuse embassy_rp::pio_programs::i2s::{PioI2sIn, PioI2sInProgram};\nuse static_cell::StaticCell;\nuse {defmt_rtt as _, panic_probe as _};\n\nbind_interrupts!(struct Irqs {\n    PIO0_IRQ_0 => InterruptHandler<PIO0>;\n    DMA_IRQ_0 => embassy_rp::dma::InterruptHandler<DMA_CH0>;\n});\n\nconst SAMPLE_RATE: u32 = 48_000;\nconst BIT_DEPTH: u32 = 16;\nconst CHANNELS: u32 = 2;\nconst USE_ONBOARD_PULLDOWN: bool = false; // whether or not to use the onboard pull-down resistor,\n// which has documented issues on many RP235x boards\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let p = embassy_rp::init(Default::default());\n\n    // Setup pio state machine for i2s input\n    let Pio { mut common, sm0, .. } = Pio::new(p.PIO0, Irqs);\n\n    let bit_clock_pin = p.PIN_18;\n    let left_right_clock_pin = p.PIN_19;\n    let data_pin = p.PIN_20;\n\n    let program = PioI2sInProgram::new(&mut common);\n    let mut i2s = PioI2sIn::new(\n        &mut common,\n        sm0,\n        p.DMA_CH0,\n        Irqs,\n        USE_ONBOARD_PULLDOWN,\n        data_pin,\n        bit_clock_pin,\n        left_right_clock_pin,\n        SAMPLE_RATE,\n        BIT_DEPTH,\n        CHANNELS,\n        &program,\n    );\n    i2s.start();\n\n    // create two audio buffers (back and front) which will take turns being\n    // filled with new audio data from the PIO fifo using DMA\n    const BUFFER_SIZE: usize = 960;\n    static DMA_BUFFER: StaticCell<[u32; BUFFER_SIZE * 2]> = StaticCell::new();\n    let dma_buffer = DMA_BUFFER.init_with(|| [0u32; BUFFER_SIZE * 2]);\n    let (mut back_buffer, mut front_buffer) = dma_buffer.split_at_mut(BUFFER_SIZE);\n\n    loop {\n        // trigger transfer of front buffer data to the pio fifo\n        // but don't await the returned future, yet\n        let dma_future = i2s.read(front_buffer);\n        // now await the dma future. once the dma finishes, the next buffer needs to be queued\n        // within DMA_DEPTH / SAMPLE_RATE = 8 / 48000 seconds = 166us\n        dma_future.await;\n        info!(\"Received I2S data word: {:?}\", &front_buffer);\n        mem::swap(&mut back_buffer, &mut front_buffer);\n    }\n}\n"
  },
  {
    "path": "examples/rp235x/src/bin/pio_onewire.rs",
    "content": "//! This example shows how you can use PIO to read one or more `DS18B20` one-wire temperature sensors.\n//! This uses externally powered sensors. For parasite power, see the pio_onewire_parasite.rs example.\n\n#![no_std]\n#![no_main]\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_rp::bind_interrupts;\nuse embassy_rp::peripherals::PIO0;\nuse embassy_rp::pio::{InterruptHandler, Pio};\nuse embassy_rp::pio_programs::onewire::{PioOneWire, PioOneWireProgram, PioOneWireSearch};\nuse embassy_time::Timer;\nuse heapless::Vec;\nuse {defmt_rtt as _, panic_probe as _};\n\nbind_interrupts!(struct Irqs {\n    PIO0_IRQ_0 => InterruptHandler<PIO0>;\n});\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let p = embassy_rp::init(Default::default());\n    let mut pio = Pio::new(p.PIO0, Irqs);\n\n    let prg = PioOneWireProgram::new(&mut pio.common);\n    let mut onewire = PioOneWire::new(&mut pio.common, pio.sm0, p.PIN_2, &prg);\n\n    info!(\"Starting onewire search\");\n\n    let mut devices = Vec::<u64, 10>::new();\n    let mut search = PioOneWireSearch::new();\n    for _ in 0..10 {\n        if !search.is_finished() {\n            if let Some(address) = search.next(&mut onewire).await {\n                if crc8(&address.to_le_bytes()) == 0 {\n                    info!(\"Found addres: {:x}\", address);\n                    let _ = devices.push(address);\n                } else {\n                    warn!(\"Found invalid address: {:x}\", address);\n                }\n            }\n        }\n    }\n\n    info!(\"Search done, found {} devices\", devices.len());\n\n    loop {\n        onewire.reset().await;\n        // Skip rom and trigger conversion, we can trigger all devices on the bus immediately\n        onewire.write_bytes(&[0xCC, 0x44]).await;\n\n        Timer::after_secs(1).await; // Allow 1s for the measurement to finish\n\n        // Read all devices one by one\n        for device in &devices {\n            onewire.reset().await;\n            onewire.write_bytes(&[0x55]).await; // Match rom\n            onewire.write_bytes(&device.to_le_bytes()).await;\n            onewire.write_bytes(&[0xBE]).await; // Read scratchpad\n\n            let mut data = [0; 9];\n            onewire.read_bytes(&mut data).await;\n            if crc8(&data) == 0 {\n                let temp = ((data[1] as i16) << 8 | data[0] as i16) as f32 / 16.;\n                info!(\"Read device {:x}: {} deg C\", device, temp);\n            } else {\n                warn!(\"Reading device {:x} failed\", device);\n            }\n        }\n        Timer::after_secs(1).await;\n    }\n}\n\nfn crc8(data: &[u8]) -> u8 {\n    let mut crc = 0;\n    for b in data {\n        let mut data_byte = *b;\n        for _ in 0..8 {\n            let temp = (crc ^ data_byte) & 0x01;\n            crc >>= 1;\n            if temp != 0 {\n                crc ^= 0x8C;\n            }\n            data_byte >>= 1;\n        }\n    }\n    crc\n}\n"
  },
  {
    "path": "examples/rp235x/src/bin/pio_onewire_parasite.rs",
    "content": "//! This example shows how you can use PIO to read one or more `DS18B20`\n//! one-wire temperature sensors using parasite power.\n//! It applies a strong pullup during conversion, see \"Powering the DS18B20\" in the datasheet.\n//! For externally powered sensors, use the pio_onewire.rs example.\n\n#![no_std]\n#![no_main]\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_rp::bind_interrupts;\nuse embassy_rp::peripherals::PIO0;\nuse embassy_rp::pio::{InterruptHandler, Pio};\nuse embassy_rp::pio_programs::onewire::{PioOneWire, PioOneWireProgram, PioOneWireSearch};\nuse embassy_time::Duration;\nuse heapless::Vec;\nuse {defmt_rtt as _, panic_probe as _};\n\nbind_interrupts!(struct Irqs {\n    PIO0_IRQ_0 => InterruptHandler<PIO0>;\n});\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let p = embassy_rp::init(Default::default());\n    let mut pio = Pio::new(p.PIO0, Irqs);\n\n    let prg = PioOneWireProgram::new(&mut pio.common);\n    let mut onewire = PioOneWire::new(&mut pio.common, pio.sm0, p.PIN_2, &prg);\n\n    info!(\"Starting onewire search\");\n\n    let mut devices = Vec::<u64, 10>::new();\n    let mut search = PioOneWireSearch::new();\n    for _ in 0..10 {\n        if !search.is_finished() {\n            if let Some(address) = search.next(&mut onewire).await {\n                if crc8(&address.to_le_bytes()) == 0 {\n                    info!(\"Found address: {:x}\", address);\n                    let _ = devices.push(address);\n                } else {\n                    warn!(\"Found invalid address: {:x}\", address);\n                }\n            }\n        }\n    }\n\n    info!(\"Search done, found {} devices\", devices.len());\n\n    loop {\n        // Read all devices one by one\n        for device in &devices {\n            onewire.reset().await;\n            onewire.write_bytes(&[0x55]).await; // Match rom\n            onewire.write_bytes(&device.to_le_bytes()).await;\n            // 750 ms delay required for default 12-bit resolution.\n            onewire.write_bytes_pullup(&[0x44], Duration::from_millis(750)).await;\n\n            onewire.reset().await;\n            onewire.write_bytes(&[0x55]).await; // Match rom\n            onewire.write_bytes(&device.to_le_bytes()).await;\n            onewire.write_bytes(&[0xBE]).await; // Read scratchpad\n\n            let mut data = [0; 9];\n            onewire.read_bytes(&mut data).await;\n            if crc8(&data) == 0 {\n                let temp = ((data[1] as i16) << 8 | data[0] as i16) as f32 / 16.;\n                info!(\"Read device {:x}: {} deg C\", device, temp);\n            } else {\n                warn!(\"Reading device {:x} failed. {:02x}\", device, data);\n            }\n        }\n    }\n}\n\nfn crc8(data: &[u8]) -> u8 {\n    let mut crc = 0;\n    for b in data {\n        let mut data_byte = *b;\n        for _ in 0..8 {\n            let temp = (crc ^ data_byte) & 0x01;\n            crc >>= 1;\n            if temp != 0 {\n                crc ^= 0x8C;\n            }\n            data_byte >>= 1;\n        }\n    }\n    crc\n}\n"
  },
  {
    "path": "examples/rp235x/src/bin/pio_pwm.rs",
    "content": "//! This example shows how to create a pwm using the PIO module in the RP235x chip.\n\n#![no_std]\n#![no_main]\nuse core::time::Duration;\n\nuse embassy_executor::Spawner;\nuse embassy_rp::bind_interrupts;\nuse embassy_rp::peripherals::PIO0;\nuse embassy_rp::pio::{InterruptHandler, Pio};\nuse embassy_rp::pio_programs::pwm::{PioPwm, PioPwmProgram};\nuse embassy_time::Timer;\nuse {defmt_rtt as _, panic_probe as _};\n\nconst REFRESH_INTERVAL: u64 = 20000;\n\nbind_interrupts!(struct Irqs {\n    PIO0_IRQ_0 => InterruptHandler<PIO0>;\n});\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let p = embassy_rp::init(Default::default());\n    let Pio { mut common, sm0, .. } = Pio::new(p.PIO0, Irqs);\n\n    // Note that PIN_25 is the led pin on the Pico\n    let prg = PioPwmProgram::new(&mut common);\n    let mut pwm_pio = PioPwm::new(&mut common, sm0, p.PIN_25, &prg);\n    pwm_pio.set_period(Duration::from_micros(REFRESH_INTERVAL));\n    pwm_pio.start();\n\n    let mut duration = 0;\n    loop {\n        duration = (duration + 1) % 1000;\n        pwm_pio.write(Duration::from_micros(duration));\n        Timer::after_millis(1).await;\n    }\n}\n"
  },
  {
    "path": "examples/rp235x/src/bin/pio_rotary_encoder.rs",
    "content": "//! This example shows how to use the PIO module in the RP235x to read a quadrature rotary encoder.\n\n#![no_std]\n#![no_main]\n\nuse defmt::info;\nuse embassy_executor::Spawner;\nuse embassy_rp::bind_interrupts;\nuse embassy_rp::peripherals::PIO0;\nuse embassy_rp::pio::{InterruptHandler, Pio};\nuse embassy_rp::pio_programs::rotary_encoder::{Direction, PioEncoder, PioEncoderProgram};\nuse {defmt_rtt as _, panic_probe as _};\n\nbind_interrupts!(struct Irqs {\n    PIO0_IRQ_0 => InterruptHandler<PIO0>;\n});\n\n#[embassy_executor::task]\nasync fn encoder_0(mut encoder: PioEncoder<'static, PIO0, 0>) {\n    let mut count = 0;\n    loop {\n        info!(\"Count: {}\", count);\n        count += match encoder.read().await {\n            Direction::Clockwise => 1,\n            Direction::CounterClockwise => -1,\n        };\n    }\n}\n\n#[embassy_executor::task]\nasync fn encoder_1(mut encoder: PioEncoder<'static, PIO0, 1>) {\n    let mut count = 0;\n    loop {\n        info!(\"Count: {}\", count);\n        count += match encoder.read().await {\n            Direction::Clockwise => 1,\n            Direction::CounterClockwise => -1,\n        };\n    }\n}\n\n#[embassy_executor::main]\nasync fn main(spawner: Spawner) {\n    let p = embassy_rp::init(Default::default());\n    let Pio {\n        mut common, sm0, sm1, ..\n    } = Pio::new(p.PIO0, Irqs);\n\n    let prg = PioEncoderProgram::new(&mut common);\n    let encoder0 = PioEncoder::new(&mut common, sm0, p.PIN_4, p.PIN_5, &prg);\n    let encoder1 = PioEncoder::new(&mut common, sm1, p.PIN_6, p.PIN_7, &prg);\n\n    spawner.spawn(encoder_0(encoder0).unwrap());\n    spawner.spawn(encoder_1(encoder1).unwrap());\n}\n"
  },
  {
    "path": "examples/rp235x/src/bin/pio_rotary_encoder_rxf.rs",
    "content": "//! This example shows how to use the PIO module in the RP235x to read a quadrature rotary encoder.\n//! It differs from the other example in that it uses the RX FIFO as a status register\n\n#![no_std]\n#![no_main]\n\nuse defmt::info;\nuse embassy_executor::Spawner;\nuse embassy_rp::gpio::Pull;\nuse embassy_rp::peripherals::PIO0;\nuse embassy_rp::pio::program::pio_asm;\nuse embassy_rp::{Peri, bind_interrupts, pio};\nuse embassy_time::Timer;\nuse fixed::traits::ToFixed;\nuse pio::{Common, Config, FifoJoin, Instance, InterruptHandler, Pio, PioPin, ShiftDirection, StateMachine};\nuse {defmt_rtt as _, panic_probe as _};\n\n// Program metadata for `picotool info`\n#[unsafe(link_section = \".bi_entries\")]\n#[used]\npub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [\n    embassy_rp::binary_info::rp_program_name!(c\"example_pio_rotary_encoder_rxf\"),\n    embassy_rp::binary_info::rp_cargo_version!(),\n    embassy_rp::binary_info::rp_program_description!(c\"Rotary encoder (RXF)\"),\n    embassy_rp::binary_info::rp_program_build_attribute!(),\n];\n\nbind_interrupts!(struct Irqs {\n    PIO0_IRQ_0 => InterruptHandler<PIO0>;\n});\n\npub struct PioEncoder<'d, T: Instance, const SM: usize> {\n    sm: StateMachine<'d, T, SM>,\n}\n\nimpl<'d, T: Instance, const SM: usize> PioEncoder<'d, T, SM> {\n    pub fn new(\n        pio: &mut Common<'d, T>,\n        mut sm: StateMachine<'d, T, SM>,\n        pin_a: Peri<'d, impl PioPin>,\n        pin_b: Peri<'d, impl PioPin>,\n    ) -> Self {\n        let mut pin_a = pio.make_pio_pin(pin_a);\n        let mut pin_b = pio.make_pio_pin(pin_b);\n        pin_a.set_pull(Pull::Up);\n        pin_b.set_pull(Pull::Up);\n\n        sm.set_pin_dirs(pio::Direction::In, &[&pin_a, &pin_b]);\n\n        let prg = pio_asm!(\n            \"start:\"\n            // encoder count is stored in X\n            \"mov isr, x\"\n            // and then moved to the RX FIFO register\n            \"mov rxfifo[0], isr\"\n\n            // wait for encoder transition\n            \"wait 1 pin 1\"\n            \"wait 0 pin 1\"\n\n            \"set y, 0\"\n            \"mov y, pins[1]\"\n\n            // update X depending on pin 1\n            \"jmp !y decr\"\n\n            // this is just a clever way of doing x++\n            \"mov x, ~x\"\n            \"jmp x--, incr\"\n            \"incr:\"\n            \"mov x, ~x\"\n            \"jmp start\"\n\n            // and this is x--\n            \"decr:\"\n            \"jmp x--, start\"\n        );\n\n        let mut cfg = Config::default();\n        cfg.set_in_pins(&[&pin_a, &pin_b]);\n        cfg.fifo_join = FifoJoin::RxAsStatus;\n        cfg.shift_in.direction = ShiftDirection::Left;\n        cfg.clock_divider = 10_000.to_fixed();\n        cfg.use_program(&pio.load_program(&prg.program), &[]);\n        sm.set_config(&cfg);\n\n        sm.set_enable(true);\n        Self { sm }\n    }\n\n    pub async fn read(&mut self) -> i32 {\n        self.sm.get_rxf_entry(0) as i32\n    }\n}\n\npub enum Direction {\n    Clockwise,\n    CounterClockwise,\n}\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let p = embassy_rp::init(Default::default());\n    let Pio { mut common, sm0, .. } = Pio::new(p.PIO0, Irqs);\n\n    let mut encoder = PioEncoder::new(&mut common, sm0, p.PIN_4, p.PIN_5);\n\n    loop {\n        info!(\"Count: {}\", encoder.read().await);\n        Timer::after_millis(1000).await;\n    }\n}\n"
  },
  {
    "path": "examples/rp235x/src/bin/pio_servo.rs",
    "content": "//! This example shows how to create a pwm using the PIO module in the RP235x chip.\n\n#![no_std]\n#![no_main]\nuse core::time::Duration;\n\nuse embassy_executor::Spawner;\nuse embassy_rp::bind_interrupts;\nuse embassy_rp::peripherals::PIO0;\nuse embassy_rp::pio::{Instance, InterruptHandler, Pio};\nuse embassy_rp::pio_programs::pwm::{PioPwm, PioPwmProgram};\nuse embassy_time::Timer;\nuse {defmt_rtt as _, panic_probe as _};\n\nconst DEFAULT_MIN_PULSE_WIDTH: u64 = 1000; // uncalibrated default, the shortest duty cycle sent to a servo\nconst DEFAULT_MAX_PULSE_WIDTH: u64 = 2000; // uncalibrated default, the longest duty cycle sent to a servo\nconst DEFAULT_MAX_DEGREE_ROTATION: u64 = 160; // 160 degrees is typical\nconst REFRESH_INTERVAL: u64 = 20000; // The period of each cycle\n\nbind_interrupts!(struct Irqs {\n    PIO0_IRQ_0 => InterruptHandler<PIO0>;\n});\n\npub struct ServoBuilder<'d, T: Instance, const SM: usize> {\n    pwm: PioPwm<'d, T, SM>,\n    period: Duration,\n    min_pulse_width: Duration,\n    max_pulse_width: Duration,\n    max_degree_rotation: u64,\n}\n\nimpl<'d, T: Instance, const SM: usize> ServoBuilder<'d, T, SM> {\n    pub fn new(pwm: PioPwm<'d, T, SM>) -> Self {\n        Self {\n            pwm,\n            period: Duration::from_micros(REFRESH_INTERVAL),\n            min_pulse_width: Duration::from_micros(DEFAULT_MIN_PULSE_WIDTH),\n            max_pulse_width: Duration::from_micros(DEFAULT_MAX_PULSE_WIDTH),\n            max_degree_rotation: DEFAULT_MAX_DEGREE_ROTATION,\n        }\n    }\n\n    pub fn set_period(mut self, duration: Duration) -> Self {\n        self.period = duration;\n        self\n    }\n\n    pub fn set_min_pulse_width(mut self, duration: Duration) -> Self {\n        self.min_pulse_width = duration;\n        self\n    }\n\n    pub fn set_max_pulse_width(mut self, duration: Duration) -> Self {\n        self.max_pulse_width = duration;\n        self\n    }\n\n    pub fn set_max_degree_rotation(mut self, degree: u64) -> Self {\n        self.max_degree_rotation = degree;\n        self\n    }\n\n    pub fn build(mut self) -> Servo<'d, T, SM> {\n        self.pwm.set_period(self.period);\n        Servo {\n            pwm: self.pwm,\n            min_pulse_width: self.min_pulse_width,\n            max_pulse_width: self.max_pulse_width,\n            max_degree_rotation: self.max_degree_rotation,\n        }\n    }\n}\n\npub struct Servo<'d, T: Instance, const SM: usize> {\n    pwm: PioPwm<'d, T, SM>,\n    min_pulse_width: Duration,\n    max_pulse_width: Duration,\n    max_degree_rotation: u64,\n}\n\nimpl<'d, T: Instance, const SM: usize> Servo<'d, T, SM> {\n    pub fn start(&mut self) {\n        self.pwm.start();\n    }\n\n    pub fn stop(&mut self) {\n        self.pwm.stop();\n    }\n\n    pub fn write_time(&mut self, duration: Duration) {\n        self.pwm.write(duration);\n    }\n\n    pub fn rotate(&mut self, degree: u64) {\n        let degree_per_nano_second = (self.max_pulse_width.as_nanos() as u64 - self.min_pulse_width.as_nanos() as u64)\n            / self.max_degree_rotation;\n        let mut duration =\n            Duration::from_nanos(degree * degree_per_nano_second + self.min_pulse_width.as_nanos() as u64);\n        if self.max_pulse_width < duration {\n            duration = self.max_pulse_width;\n        }\n\n        self.pwm.write(duration);\n    }\n}\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let p = embassy_rp::init(Default::default());\n    let Pio { mut common, sm0, .. } = Pio::new(p.PIO0, Irqs);\n\n    let prg = PioPwmProgram::new(&mut common);\n    let pwm_pio = PioPwm::new(&mut common, sm0, p.PIN_1, &prg);\n    let mut servo = ServoBuilder::new(pwm_pio)\n        .set_max_degree_rotation(120) // Example of adjusting values for MG996R servo\n        .set_min_pulse_width(Duration::from_micros(350)) // This value was detemined by a rough experiment.\n        .set_max_pulse_width(Duration::from_micros(2600)) // Along with this value.\n        .build();\n\n    servo.start();\n\n    let mut degree = 0;\n    loop {\n        degree = (degree + 1) % 120;\n        servo.rotate(degree);\n        Timer::after_millis(50).await;\n    }\n}\n"
  },
  {
    "path": "examples/rp235x/src/bin/pio_stepper.rs",
    "content": "//! This example shows how to use the PIO module in the RP235x to implement a stepper motor driver\n//! for a 5-wire stepper such as the 28BYJ-48. You can halt an ongoing rotation by dropping the future.\n\n#![no_std]\n#![no_main]\n\nuse defmt::info;\nuse embassy_executor::Spawner;\nuse embassy_rp::bind_interrupts;\nuse embassy_rp::peripherals::PIO0;\nuse embassy_rp::pio::{InterruptHandler, Pio};\nuse embassy_rp::pio_programs::stepper::{PioStepper, PioStepperProgram};\nuse embassy_time::{Duration, Timer, with_timeout};\nuse {defmt_rtt as _, panic_probe as _};\n\nbind_interrupts!(struct Irqs {\n    PIO0_IRQ_0 => InterruptHandler<PIO0>;\n});\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let p = embassy_rp::init(Default::default());\n    let Pio {\n        mut common, irq0, sm0, ..\n    } = Pio::new(p.PIO0, Irqs);\n\n    let prg = PioStepperProgram::new(&mut common);\n    let mut stepper = PioStepper::new(&mut common, sm0, irq0, p.PIN_4, p.PIN_5, p.PIN_6, p.PIN_7, &prg);\n    stepper.set_frequency(120);\n    loop {\n        info!(\"CW full steps\");\n        stepper.step(1000).await;\n\n        info!(\"CCW full steps, drop after 1 sec\");\n        if with_timeout(Duration::from_secs(1), stepper.step(-i32::MAX))\n            .await\n            .is_err()\n        {\n            info!(\"Time's up!\");\n            Timer::after(Duration::from_secs(1)).await;\n        }\n\n        info!(\"CW half steps\");\n        stepper.step_half(1000).await;\n\n        info!(\"CCW half steps\");\n        stepper.step_half(-1000).await;\n    }\n}\n"
  },
  {
    "path": "examples/rp235x/src/bin/pio_uart.rs",
    "content": "//! This example shows how to use the PIO module in the RP235x chip to implement a duplex UART.\n//! The PIO module is a very powerful peripheral that can be used to implement many different\n//! protocols. It is a very flexible state machine that can be programmed to do almost anything.\n//!\n//! This example opens up a USB device that implements a CDC ACM serial port. It then uses the\n//! PIO module to implement a UART that is connected to the USB serial port. This allows you to\n//! communicate with a device connected to the RP235x over USB serial.\n\n#![no_std]\n#![no_main]\n#![allow(async_fn_in_trait)]\n\nuse defmt::{info, panic, trace};\nuse embassy_executor::Spawner;\nuse embassy_futures::join::{join, join3};\nuse embassy_rp::peripherals::{PIO0, USB};\nuse embassy_rp::pio_programs::uart::{PioUartRx, PioUartRxProgram, PioUartTx, PioUartTxProgram};\nuse embassy_rp::usb::{Driver, Instance, InterruptHandler};\nuse embassy_rp::{bind_interrupts, pio};\nuse embassy_sync::blocking_mutex::raw::NoopRawMutex;\nuse embassy_sync::pipe::Pipe;\nuse embassy_usb::class::cdc_acm::{CdcAcmClass, Receiver, Sender, State};\nuse embassy_usb::driver::EndpointError;\nuse embassy_usb::{Builder, Config};\nuse embedded_io_async::{Read, Write};\nuse {defmt_rtt as _, panic_probe as _};\n\nbind_interrupts!(struct Irqs {\n    USBCTRL_IRQ => InterruptHandler<USB>;\n    PIO0_IRQ_0 => pio::InterruptHandler<PIO0>;\n});\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    info!(\"Hello there!\");\n\n    let p = embassy_rp::init(Default::default());\n\n    // Create the driver, from the HAL.\n    let driver = Driver::new(p.USB, Irqs);\n\n    // Create embassy-usb Config\n    let mut config = Config::new(0xc0de, 0xcafe);\n    config.manufacturer = Some(\"Embassy\");\n    config.product = Some(\"PIO UART example\");\n    config.serial_number = Some(\"12345678\");\n    config.max_power = 100;\n    config.max_packet_size_0 = 64;\n\n    // Create embassy-usb DeviceBuilder using the driver and config.\n    // It needs some buffers for building the descriptors.\n    let mut config_descriptor = [0; 256];\n    let mut bos_descriptor = [0; 256];\n    let mut control_buf = [0; 64];\n\n    let mut state = State::new();\n\n    let mut builder = Builder::new(\n        driver,\n        config,\n        &mut config_descriptor,\n        &mut bos_descriptor,\n        &mut [], // no msos descriptors\n        &mut control_buf,\n    );\n\n    // Create classes on the builder.\n    let class = CdcAcmClass::new(&mut builder, &mut state, 64);\n\n    // Build the builder.\n    let mut usb = builder.build();\n\n    // Run the USB device.\n    let usb_fut = usb.run();\n\n    // PIO UART setup\n    let pio::Pio {\n        mut common, sm0, sm1, ..\n    } = pio::Pio::new(p.PIO0, Irqs);\n\n    let tx_program = PioUartTxProgram::new(&mut common);\n    let mut uart_tx = PioUartTx::new(9600, &mut common, sm0, p.PIN_4, &tx_program);\n\n    let rx_program = PioUartRxProgram::new(&mut common);\n    let mut uart_rx = PioUartRx::new(9600, &mut common, sm1, p.PIN_5, &rx_program);\n\n    // Pipe setup\n    let mut usb_pipe: Pipe<NoopRawMutex, 20> = Pipe::new();\n    let (mut usb_pipe_reader, mut usb_pipe_writer) = usb_pipe.split();\n\n    let mut uart_pipe: Pipe<NoopRawMutex, 20> = Pipe::new();\n    let (mut uart_pipe_reader, mut uart_pipe_writer) = uart_pipe.split();\n\n    let (mut usb_tx, mut usb_rx) = class.split();\n\n    // Read + write from USB\n    let usb_future = async {\n        loop {\n            info!(\"Wait for USB connection\");\n            usb_rx.wait_connection().await;\n            info!(\"Connected\");\n            let _ = join(\n                usb_read(&mut usb_rx, &mut uart_pipe_writer),\n                usb_write(&mut usb_tx, &mut usb_pipe_reader),\n            )\n            .await;\n            info!(\"Disconnected\");\n        }\n    };\n\n    // Read + write from UART\n    let uart_future = join(\n        uart_read(&mut uart_rx, &mut usb_pipe_writer),\n        uart_write(&mut uart_tx, &mut uart_pipe_reader),\n    );\n\n    // Run everything concurrently.\n    // If we had made everything `'static` above instead, we could do this using separate tasks instead.\n    join3(usb_fut, usb_future, uart_future).await;\n}\n\nstruct Disconnected {}\n\nimpl From<EndpointError> for Disconnected {\n    fn from(val: EndpointError) -> Self {\n        match val {\n            EndpointError::BufferOverflow => panic!(\"Buffer overflow\"),\n            EndpointError::Disabled => Disconnected {},\n        }\n    }\n}\n\n/// Read from the USB and write it to the UART TX pipe\nasync fn usb_read<'d, T: Instance + 'd>(\n    usb_rx: &mut Receiver<'d, Driver<'d, T>>,\n    uart_pipe_writer: &mut embassy_sync::pipe::Writer<'_, NoopRawMutex, 20>,\n) -> Result<(), Disconnected> {\n    let mut buf = [0; 64];\n    loop {\n        let n = usb_rx.read_packet(&mut buf).await?;\n        let data = &buf[..n];\n        trace!(\"USB IN: {:x}\", data);\n        (*uart_pipe_writer).write(data).await;\n    }\n}\n\n/// Read from the USB TX pipe and write it to the USB\nasync fn usb_write<'d, T: Instance + 'd>(\n    usb_tx: &mut Sender<'d, Driver<'d, T>>,\n    usb_pipe_reader: &mut embassy_sync::pipe::Reader<'_, NoopRawMutex, 20>,\n) -> Result<(), Disconnected> {\n    let mut buf = [0; 64];\n    loop {\n        let n = (*usb_pipe_reader).read(&mut buf).await;\n        let data = &buf[..n];\n        trace!(\"USB OUT: {:x}\", data);\n        usb_tx.write_packet(&data).await?;\n    }\n}\n\n/// Read from the UART and write it to the USB TX pipe\nasync fn uart_read<PIO: pio::Instance, const SM: usize>(\n    uart_rx: &mut PioUartRx<'_, PIO, SM>,\n    usb_pipe_writer: &mut embassy_sync::pipe::Writer<'_, NoopRawMutex, 20>,\n) -> ! {\n    let mut buf = [0; 64];\n    loop {\n        let n = uart_rx.read(&mut buf).await.expect(\"UART read error\");\n        if n == 0 {\n            continue;\n        }\n        let data = &buf[..n];\n        trace!(\"UART IN: {:x}\", buf);\n        (*usb_pipe_writer).write(data).await;\n    }\n}\n\n/// Read from the UART TX pipe and write it to the UART\nasync fn uart_write<PIO: pio::Instance, const SM: usize>(\n    uart_tx: &mut PioUartTx<'_, PIO, SM>,\n    uart_pipe_reader: &mut embassy_sync::pipe::Reader<'_, NoopRawMutex, 20>,\n) -> ! {\n    let mut buf = [0; 64];\n    loop {\n        let n = (*uart_pipe_reader).read(&mut buf).await;\n        let data = &buf[..n];\n        trace!(\"UART OUT: {:x}\", data);\n        let _ = uart_tx.write(&data).await;\n    }\n}\n"
  },
  {
    "path": "examples/rp235x/src/bin/pio_ws2812.rs",
    "content": "//! This example shows powerful PIO module in the RP235x chip to communicate with WS2812 LED modules.\n//! See (https://www.sparkfun.com/categories/tags/ws2812)\n\n#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_rp::bind_interrupts;\nuse embassy_rp::peripherals::{DMA_CH0, PIO0};\nuse embassy_rp::pio::{InterruptHandler, Pio};\nuse embassy_rp::pio_programs::ws2812::{PioWs2812, PioWs2812Program};\nuse embassy_time::{Duration, Ticker};\nuse smart_leds::RGB8;\nuse {defmt_rtt as _, panic_probe as _};\n\nbind_interrupts!(struct Irqs {\n    PIO0_IRQ_0 => InterruptHandler<PIO0>;\n    DMA_IRQ_0 => embassy_rp::dma::InterruptHandler<DMA_CH0>;\n});\n\n/// Input a value 0 to 255 to get a color value\n/// The colours are a transition r - g - b - back to r.\nfn wheel(mut wheel_pos: u8) -> RGB8 {\n    wheel_pos = 255 - wheel_pos;\n    if wheel_pos < 85 {\n        return (255 - wheel_pos * 3, 0, wheel_pos * 3).into();\n    }\n    if wheel_pos < 170 {\n        wheel_pos -= 85;\n        return (0, wheel_pos * 3, 255 - wheel_pos * 3).into();\n    }\n    wheel_pos -= 170;\n    (wheel_pos * 3, 255 - wheel_pos * 3, 0).into()\n}\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    info!(\"Start\");\n    let p = embassy_rp::init(Default::default());\n\n    let Pio { mut common, sm0, .. } = Pio::new(p.PIO0, Irqs);\n\n    // This is the number of leds in the string. Helpfully, the sparkfun thing plus and adafruit\n    // feather boards for the 2040 both have one built in.\n    const NUM_LEDS: usize = 1;\n    let mut data = [RGB8::default(); NUM_LEDS];\n\n    // Common neopixel pins:\n    // Thing plus: 8\n    // Adafruit Feather: 16;  Adafruit Feather+RFM95: 4\n    let program = PioWs2812Program::new(&mut common);\n    let mut ws2812 = PioWs2812::new(&mut common, sm0, p.DMA_CH0, Irqs, p.PIN_16, &program);\n\n    // Loop forever making RGB values and pushing them out to the WS2812.\n    let mut ticker = Ticker::every(Duration::from_millis(10));\n    loop {\n        for j in 0..(256 * 5) {\n            debug!(\"New Colors:\");\n            for i in 0..NUM_LEDS {\n                data[i] = wheel((((i * 256) as u16 / NUM_LEDS as u16 + j as u16) & 255) as u8);\n                debug!(\"R: {} G: {} B: {}\", data[i].r, data[i].g, data[i].b);\n            }\n            ws2812.write(&data).await;\n\n            ticker.next().await;\n        }\n    }\n}\n"
  },
  {
    "path": "examples/rp235x/src/bin/psram.rs",
    "content": "//! This example tests an APS6404L PSRAM chip connected to the RP235x\n//! It fills the PSRAM with alternating patterns and reads back a value\n//!\n//! In this example, the PSRAM CS is connected to Pin 0.\n\n#![no_std]\n#![no_main]\n\nuse core::slice;\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_time::Timer;\nuse {defmt_rtt as _, panic_probe as _};\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let config = embassy_rp::config::Config::default();\n    let p = embassy_rp::init(config);\n    let psram_config = embassy_rp::psram::Config::aps6404l();\n    let psram = embassy_rp::psram::Psram::new(embassy_rp::qmi_cs1::QmiCs1::new(p.QMI_CS1, p.PIN_0), psram_config);\n\n    let Ok(psram) = psram else {\n        error!(\"PSRAM not found\");\n        loop {\n            Timer::after_secs(1).await;\n        }\n    };\n\n    let psram_slice = unsafe {\n        let psram_ptr = psram.base_address();\n        let slice: &'static mut [u8] = slice::from_raw_parts_mut(psram_ptr, psram.size() as usize);\n        slice\n    };\n\n    loop {\n        psram_slice.fill(0x55);\n        info!(\"PSRAM filled with 0x55\");\n        let at_addr = psram_slice[0x100];\n        info!(\"Read from PSRAM at address 0x100: 0x{:02x}\", at_addr);\n        Timer::after_secs(1).await;\n\n        psram_slice.fill(0xAA);\n        info!(\"PSRAM filled with 0xAA\");\n        let at_addr = psram_slice[0x100];\n        info!(\"Read from PSRAM at address 0x100: 0x{:02x}\", at_addr);\n        Timer::after_secs(1).await;\n    }\n}\n"
  },
  {
    "path": "examples/rp235x/src/bin/pwm.rs",
    "content": "//! This example shows how to use PWM (Pulse Width Modulation) in the RP235x chip.\n//!\n//! We demonstrate two ways of using PWM:\n//! 1. Via config\n//! 2. Via setting a duty cycle\n\n#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_rp::Peri;\nuse embassy_rp::peripherals::{PIN_4, PIN_25, PWM_SLICE2, PWM_SLICE4};\nuse embassy_rp::pwm::{Config, Pwm, SetDutyCycle};\nuse embassy_time::Timer;\nuse {defmt_rtt as _, panic_probe as _};\n\n#[embassy_executor::main]\nasync fn main(spawner: Spawner) {\n    let p = embassy_rp::init(Default::default());\n    spawner.spawn(pwm_set_config(p.PWM_SLICE4, p.PIN_25).unwrap());\n    spawner.spawn(pwm_set_dutycycle(p.PWM_SLICE2, p.PIN_4).unwrap());\n}\n\n/// Demonstrate PWM by modifying & applying the config\n///\n/// Using the onboard led, if You are using a different Board than plain Pico2 (i.e. W variant)\n/// you must use another slice & pin and an appropriate resistor.\n#[embassy_executor::task]\nasync fn pwm_set_config(slice4: Peri<'static, PWM_SLICE4>, pin25: Peri<'static, PIN_25>) {\n    let mut c = Config::default();\n    c.top = 32_768;\n    c.compare_b = 8;\n    let mut pwm = Pwm::new_output_b(slice4, pin25, c.clone());\n\n    loop {\n        info!(\"current LED duty cycle: {}/32768\", c.compare_b);\n        Timer::after_secs(1).await;\n        c.compare_b = c.compare_b.rotate_left(4);\n        pwm.set_config(&c);\n    }\n}\n\n/// Demonstrate PWM by setting duty cycle\n///\n/// Using GP4 in Slice2, make sure to use an appropriate resistor.\n#[embassy_executor::task]\nasync fn pwm_set_dutycycle(slice2: Peri<'static, PWM_SLICE2>, pin4: Peri<'static, PIN_4>) {\n    // If we aim for a specific frequency, here is how we can calculate the top value.\n    // The top value sets the period of the PWM cycle, so a counter goes from 0 to top and then wraps around to 0.\n    // Every such wraparound is one PWM cycle. So here is how we get 25KHz:\n    let desired_freq_hz = 25_000;\n    let clock_freq_hz = embassy_rp::clocks::clk_sys_freq();\n    let divider = 16u8;\n    let period = (clock_freq_hz / (desired_freq_hz * divider as u32)) as u16 - 1;\n\n    let mut c = Config::default();\n    c.top = period;\n    c.divider = divider.into();\n\n    let mut pwm = Pwm::new_output_a(slice2, pin4, c.clone());\n\n    loop {\n        // 100% duty cycle, fully on\n        pwm.set_duty_cycle_fully_on().unwrap();\n        Timer::after_secs(1).await;\n\n        // 66% duty cycle. Expressed as simple percentage.\n        pwm.set_duty_cycle_percent(66).unwrap();\n        Timer::after_secs(1).await;\n\n        // 25% duty cycle. Expressed as 32768/4 = 8192.\n        pwm.set_duty_cycle(c.top / 4).unwrap();\n        Timer::after_secs(1).await;\n\n        // 0% duty cycle, fully off.\n        pwm.set_duty_cycle_fully_off().unwrap();\n        Timer::after_secs(1).await;\n    }\n}\n"
  },
  {
    "path": "examples/rp235x/src/bin/pwm_input.rs",
    "content": "//! This example shows how to use the PWM module to measure the frequency of an input signal.\n\n#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_rp::gpio::Pull;\nuse embassy_rp::pwm::{Config, InputMode, Pwm};\nuse embassy_time::{Duration, Ticker};\nuse {defmt_rtt as _, panic_probe as _};\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let p = embassy_rp::init(Default::default());\n\n    let cfg: Config = Default::default();\n    let pwm = Pwm::new_input(p.PWM_SLICE2, p.PIN_5, Pull::None, InputMode::RisingEdge, cfg);\n\n    let mut ticker = Ticker::every(Duration::from_secs(1));\n    loop {\n        info!(\"Input frequency: {} Hz\", pwm.counter());\n        pwm.set_counter(0);\n        ticker.next().await;\n    }\n}\n"
  },
  {
    "path": "examples/rp235x/src/bin/pwm_tb6612fng_motor_driver.rs",
    "content": "//! # PWM TB6612FNG motor driver\n//!\n//! This example shows the use of a TB6612FNG motor driver. The driver is built on top of embedded_hal and the example demonstrates how embassy_rp can be used to interact with ist.\n\n#![no_std]\n#![no_main]\n\nuse assign_resources::assign_resources;\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_rp::config::Config;\nuse embassy_rp::gpio::Output;\nuse embassy_rp::{Peri, gpio, peripherals, pwm};\nuse embassy_time::{Duration, Timer};\nuse tb6612fng::{DriveCommand, Motor, Tb6612fng};\nuse {defmt_rtt as _, panic_probe as _};\n\nassign_resources! {\n    motor: MotorResources {\n        standby_pin: PIN_22,\n        left_slice: PWM_SLICE6,\n        left_pwm_pin: PIN_28,\n        left_forward_pin: PIN_21,\n        left_backward_pin: PIN_20,\n        right_slice: PWM_SLICE5,\n        right_pwm_pin: PIN_27,\n        right_forward_pin: PIN_19,\n        right_backward_pin: PIN_18,\n        },\n}\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let p = embassy_rp::init(Config::default());\n    let s = split_resources!(p);\n    let r = s.motor;\n\n    // we want a PWM frequency of 10KHz, especially cheaper motors do not respond well to higher frequencies\n    let desired_freq_hz = 10_000;\n    let clock_freq_hz = embassy_rp::clocks::clk_sys_freq();\n    let divider = 16u8;\n    let period = (clock_freq_hz / (desired_freq_hz * divider as u32)) as u16 - 1;\n\n    // we need a standby output and two motors to construct a full TB6612FNG\n\n    // standby pin\n    let stby = Output::new(r.standby_pin, gpio::Level::Low);\n\n    // motor A, here defined to be the left motor\n    let left_fwd = gpio::Output::new(r.left_forward_pin, gpio::Level::Low);\n    let left_bckw = gpio::Output::new(r.left_backward_pin, gpio::Level::Low);\n    let mut left_speed = pwm::Config::default();\n    left_speed.top = period;\n    left_speed.divider = divider.into();\n    let left_pwm = pwm::Pwm::new_output_a(r.left_slice, r.left_pwm_pin, left_speed);\n    let left_motor = Motor::new(left_fwd, left_bckw, left_pwm).unwrap();\n\n    // motor B, here defined to be the right motor\n    let right_fwd = gpio::Output::new(r.right_forward_pin, gpio::Level::Low);\n    let right_bckw = gpio::Output::new(r.right_backward_pin, gpio::Level::Low);\n    let mut right_speed = pwm::Config::default();\n    right_speed.top = period;\n    right_speed.divider = divider.into();\n    let right_pwm = pwm::Pwm::new_output_b(r.right_slice, r.right_pwm_pin, right_speed);\n    let right_motor = Motor::new(right_fwd, right_bckw, right_pwm).unwrap();\n\n    // construct the motor driver\n    let mut control = Tb6612fng::new(left_motor, right_motor, stby).unwrap();\n\n    loop {\n        // wake up the motor driver\n        info!(\"end standby\");\n        control.disable_standby().unwrap();\n        Timer::after(Duration::from_millis(100)).await;\n\n        // drive a straight line forward at 20% speed for 5s\n        info!(\"drive straight\");\n        control.motor_a.drive(DriveCommand::Forward(80)).unwrap();\n        control.motor_b.drive(DriveCommand::Forward(80)).unwrap();\n        Timer::after(Duration::from_secs(5)).await;\n\n        // coast for 2s\n        info!(\"coast\");\n        control.motor_a.drive(DriveCommand::Stop).unwrap();\n        control.motor_b.drive(DriveCommand::Stop).unwrap();\n        Timer::after(Duration::from_secs(2)).await;\n\n        // actively brake\n        info!(\"brake\");\n        control.motor_a.drive(DriveCommand::Brake).unwrap();\n        control.motor_b.drive(DriveCommand::Brake).unwrap();\n        Timer::after(Duration::from_secs(1)).await;\n\n        // slowly turn for 3s\n        info!(\"turn\");\n        control.motor_a.drive(DriveCommand::Backward(50)).unwrap();\n        control.motor_b.drive(DriveCommand::Forward(50)).unwrap();\n        Timer::after(Duration::from_secs(3)).await;\n\n        // and put the driver in standby mode and wait for 5s\n        info!(\"standby\");\n        control.enable_standby().unwrap();\n        Timer::after(Duration::from_secs(5)).await;\n    }\n}\n"
  },
  {
    "path": "examples/rp235x/src/bin/rosc.rs",
    "content": "//! This example test the RP Pico on board LED.\n//!\n//! It does not work with the RP Pico W board. See wifi_blinky.rs.\n\n#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_rp::{clocks, gpio};\nuse embassy_time::Timer;\nuse gpio::{Level, Output};\nuse {defmt_rtt as _, panic_probe as _};\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let mut config = embassy_rp::config::Config::default();\n    config.clocks = clocks::ClockConfig::rosc();\n    let p = embassy_rp::init(config);\n    let mut led = Output::new(p.PIN_25, Level::Low);\n\n    loop {\n        info!(\"led on!\");\n        led.set_high();\n        Timer::after_secs(1).await;\n\n        info!(\"led off!\");\n        led.set_low();\n        Timer::after_secs(1).await;\n    }\n}\n"
  },
  {
    "path": "examples/rp235x/src/bin/shared_bus.rs",
    "content": "//! This example shows how to share (async) I2C and SPI buses between multiple devices.\n\n#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_embedded_hal::shared_bus::asynch::i2c::I2cDevice;\nuse embassy_embedded_hal::shared_bus::asynch::spi::SpiDevice;\nuse embassy_executor::Spawner;\nuse embassy_rp::gpio::{Level, Output};\nuse embassy_rp::i2c::{self, I2c, InterruptHandler};\nuse embassy_rp::peripherals::{DMA_CH0, DMA_CH1, I2C1, SPI1};\nuse embassy_rp::spi::{self, Spi};\nuse embassy_rp::{bind_interrupts, dma};\nuse embassy_sync::blocking_mutex::raw::NoopRawMutex;\nuse embassy_sync::mutex::Mutex;\nuse embassy_time::Timer;\nuse static_cell::StaticCell;\nuse {defmt_rtt as _, panic_probe as _};\n\ntype Spi1Bus = Mutex<NoopRawMutex, Spi<'static, SPI1, spi::Async>>;\ntype I2c1Bus = Mutex<NoopRawMutex, I2c<'static, I2C1, i2c::Async>>;\n\nbind_interrupts!(struct Irqs {\n    I2C1_IRQ => InterruptHandler<I2C1>;\n    DMA_IRQ_0 => dma::InterruptHandler<DMA_CH0>, dma::InterruptHandler<DMA_CH1>;\n});\n\n#[embassy_executor::main]\nasync fn main(spawner: Spawner) {\n    let p = embassy_rp::init(Default::default());\n    info!(\"Here we go!\");\n\n    // Shared I2C bus\n    let i2c = I2c::new_async(p.I2C1, p.PIN_15, p.PIN_14, Irqs, i2c::Config::default());\n    static I2C_BUS: StaticCell<I2c1Bus> = StaticCell::new();\n    let i2c_bus = I2C_BUS.init(Mutex::new(i2c));\n\n    spawner.spawn(i2c_task_a(i2c_bus).unwrap());\n    spawner.spawn(i2c_task_b(i2c_bus).unwrap());\n\n    // Shared SPI bus\n    let spi_cfg = spi::Config::default();\n    let spi = Spi::new(\n        p.SPI1, p.PIN_10, p.PIN_11, p.PIN_12, p.DMA_CH0, p.DMA_CH1, Irqs, spi_cfg,\n    );\n    static SPI_BUS: StaticCell<Spi1Bus> = StaticCell::new();\n    let spi_bus = SPI_BUS.init(Mutex::new(spi));\n\n    // Chip select pins for the SPI devices\n    let cs_a = Output::new(p.PIN_0, Level::High);\n    let cs_b = Output::new(p.PIN_1, Level::High);\n\n    spawner.spawn(spi_task_a(spi_bus, cs_a).unwrap());\n    spawner.spawn(spi_task_b(spi_bus, cs_b).unwrap());\n}\n\n#[embassy_executor::task]\nasync fn i2c_task_a(i2c_bus: &'static I2c1Bus) {\n    let i2c_dev = I2cDevice::new(i2c_bus);\n    let _sensor = DummyI2cDeviceDriver::new(i2c_dev, 0xC0);\n    loop {\n        info!(\"i2c task A\");\n        Timer::after_secs(1).await;\n    }\n}\n\n#[embassy_executor::task]\nasync fn i2c_task_b(i2c_bus: &'static I2c1Bus) {\n    let i2c_dev = I2cDevice::new(i2c_bus);\n    let _sensor = DummyI2cDeviceDriver::new(i2c_dev, 0xDE);\n    loop {\n        info!(\"i2c task B\");\n        Timer::after_secs(1).await;\n    }\n}\n\n#[embassy_executor::task]\nasync fn spi_task_a(spi_bus: &'static Spi1Bus, cs: Output<'static>) {\n    let spi_dev = SpiDevice::new(spi_bus, cs);\n    let _sensor = DummySpiDeviceDriver::new(spi_dev);\n    loop {\n        info!(\"spi task A\");\n        Timer::after_secs(1).await;\n    }\n}\n\n#[embassy_executor::task]\nasync fn spi_task_b(spi_bus: &'static Spi1Bus, cs: Output<'static>) {\n    let spi_dev = SpiDevice::new(spi_bus, cs);\n    let _sensor = DummySpiDeviceDriver::new(spi_dev);\n    loop {\n        info!(\"spi task B\");\n        Timer::after_secs(1).await;\n    }\n}\n\n// Dummy I2C device driver, using `embedded-hal-async`\nstruct DummyI2cDeviceDriver<I2C: embedded_hal_async::i2c::I2c> {\n    _i2c: I2C,\n}\n\nimpl<I2C: embedded_hal_async::i2c::I2c> DummyI2cDeviceDriver<I2C> {\n    fn new(i2c_dev: I2C, _address: u8) -> Self {\n        Self { _i2c: i2c_dev }\n    }\n}\n\n// Dummy SPI device driver, using `embedded-hal-async`\nstruct DummySpiDeviceDriver<SPI: embedded_hal_async::spi::SpiDevice> {\n    _spi: SPI,\n}\n\nimpl<SPI: embedded_hal_async::spi::SpiDevice> DummySpiDeviceDriver<SPI> {\n    fn new(spi_dev: SPI) -> Self {\n        Self { _spi: spi_dev }\n    }\n}\n"
  },
  {
    "path": "examples/rp235x/src/bin/sharing.rs",
    "content": "//! This example shows some common strategies for sharing resources between tasks.\n//!\n//! We demonstrate five different ways of sharing, covering different use cases:\n//! - Atomics: This method is used for simple values, such as bool and u8..u32\n//! - Blocking Mutex: This is used for sharing non-async things, using Cell/RefCell for interior mutability.\n//! - Async Mutex: This is used for sharing async resources, where you need to hold the lock across await points.\n//!   The async Mutex has interior mutability built-in, so no RefCell is needed.\n//! - Cell: For sharing Copy types between tasks running on the same executor.\n//! - RefCell: When you want &mut access to a value shared between tasks running on the same executor.\n//!\n//! More information: https://embassy.dev/book/#_sharing_peripherals_between_tasks\n\n#![no_std]\n#![no_main]\n\nuse core::cell::{Cell, RefCell};\nuse core::sync::atomic::{AtomicU32, Ordering};\n\nuse cortex_m_rt::entry;\nuse defmt::info;\nuse embassy_executor::{Executor, InterruptExecutor};\nuse embassy_rp::clocks::RoscRng;\nuse embassy_rp::interrupt::{InterruptExt, Priority};\nuse embassy_rp::peripherals::{DMA_CH0, UART0};\nuse embassy_rp::uart::{self, InterruptHandler, UartTx};\nuse embassy_rp::{bind_interrupts, dma, interrupt};\nuse embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;\nuse embassy_sync::{blocking_mutex, mutex};\nuse embassy_time::{Duration, Ticker};\nuse static_cell::{ConstStaticCell, StaticCell};\nuse {defmt_rtt as _, panic_probe as _};\n\ntype UartAsyncMutex = mutex::Mutex<CriticalSectionRawMutex, UartTx<'static, uart::Async>>;\n\nstruct MyType {\n    inner: u32,\n}\n\nstatic EXECUTOR_HI: InterruptExecutor = InterruptExecutor::new();\nstatic EXECUTOR_LOW: StaticCell<Executor> = StaticCell::new();\n\n// Use Atomics for simple values\nstatic ATOMIC: AtomicU32 = AtomicU32::new(0);\n\n// Use blocking Mutex with Cell/RefCell for sharing non-async things\nstatic MUTEX_BLOCKING: blocking_mutex::Mutex<CriticalSectionRawMutex, RefCell<MyType>> =\n    blocking_mutex::Mutex::new(RefCell::new(MyType { inner: 0 }));\n\nbind_interrupts!(struct Irqs {\n    UART0_IRQ => InterruptHandler<UART0>;\n    DMA_IRQ_0 => dma::InterruptHandler<DMA_CH0>;\n});\n\n#[interrupt]\nunsafe fn SWI_IRQ_0() {\n    unsafe { EXECUTOR_HI.on_interrupt() }\n}\n\n#[entry]\nfn main() -> ! {\n    let p = embassy_rp::init(Default::default());\n    info!(\"Here we go!\");\n\n    let uart = UartTx::new(p.UART0, p.PIN_0, p.DMA_CH0, Irqs, uart::Config::default());\n    // Use the async Mutex for sharing async things (built-in interior mutability)\n    static UART: StaticCell<UartAsyncMutex> = StaticCell::new();\n    let uart = UART.init(mutex::Mutex::new(uart));\n\n    // High-priority executor: runs in interrupt mode\n    interrupt::SWI_IRQ_0.set_priority(Priority::P3);\n    let spawner = EXECUTOR_HI.start(interrupt::SWI_IRQ_0);\n    spawner.spawn(task_a(uart).unwrap());\n\n    // Low priority executor: runs in thread mode\n    let executor = EXECUTOR_LOW.init(Executor::new());\n    executor.run(|spawner| {\n        // No Mutex needed when sharing between tasks running on the same executor\n\n        // Use Cell for Copy-types\n        static CELL: ConstStaticCell<Cell<[u8; 4]>> = ConstStaticCell::new(Cell::new([0; 4]));\n        let cell = CELL.take();\n\n        // Use RefCell for &mut access\n        static REF_CELL: ConstStaticCell<RefCell<MyType>> = ConstStaticCell::new(RefCell::new(MyType { inner: 0 }));\n        let ref_cell = REF_CELL.take();\n\n        spawner.spawn(task_b(uart, cell, ref_cell).unwrap());\n        spawner.spawn(task_c(cell, ref_cell).unwrap());\n    });\n}\n\n#[embassy_executor::task]\nasync fn task_a(uart: &'static UartAsyncMutex) {\n    let mut ticker = Ticker::every(Duration::from_secs(1));\n    loop {\n        let random = RoscRng.next_u32();\n\n        {\n            let mut uart = uart.lock().await;\n            uart.write(b\"task a\").await.unwrap();\n            // The uart lock is released when it goes out of scope\n        }\n\n        ATOMIC.store(random, Ordering::Relaxed);\n\n        MUTEX_BLOCKING.lock(|x| x.borrow_mut().inner = random);\n\n        ticker.next().await;\n    }\n}\n\n#[embassy_executor::task]\nasync fn task_b(uart: &'static UartAsyncMutex, cell: &'static Cell<[u8; 4]>, ref_cell: &'static RefCell<MyType>) {\n    let mut ticker = Ticker::every(Duration::from_secs(1));\n    loop {\n        let random = RoscRng.next_u32();\n\n        uart.lock().await.write(b\"task b\").await.unwrap();\n\n        cell.set(random.to_be_bytes());\n\n        ref_cell.borrow_mut().inner = random;\n\n        ticker.next().await;\n    }\n}\n\n#[embassy_executor::task]\nasync fn task_c(cell: &'static Cell<[u8; 4]>, ref_cell: &'static RefCell<MyType>) {\n    let mut ticker = Ticker::every(Duration::from_secs(1));\n    loop {\n        info!(\"=======================\");\n\n        let atomic_val = ATOMIC.load(Ordering::Relaxed);\n        info!(\"atomic: {}\", atomic_val);\n\n        MUTEX_BLOCKING.lock(|x| {\n            let val = x.borrow().inner;\n            info!(\"blocking mutex: {}\", val);\n        });\n\n        let cell_val = cell.get();\n        info!(\"cell: {:?}\", cell_val);\n\n        let ref_cell_val = ref_cell.borrow().inner;\n        info!(\"ref_cell: {:?}\", ref_cell_val);\n\n        ticker.next().await;\n    }\n}\n"
  },
  {
    "path": "examples/rp235x/src/bin/spi.rs",
    "content": "//! This example shows how to use SPI (Serial Peripheral Interface) in the RP235x chip.\n//!\n//! Example for resistive touch sensor in Waveshare Pico-ResTouch\n\n#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_rp::spi::Spi;\nuse embassy_rp::{gpio, spi};\nuse gpio::{Level, Output};\nuse {defmt_rtt as _, panic_probe as _};\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let p = embassy_rp::init(Default::default());\n    info!(\"Hello World!\");\n\n    // Example for resistive touch sensor in Waveshare Pico-ResTouch\n\n    let miso = p.PIN_12;\n    let mosi = p.PIN_11;\n    let clk = p.PIN_10;\n    let touch_cs = p.PIN_16;\n\n    // create SPI\n    let mut config = spi::Config::default();\n    config.frequency = 2_000_000;\n    let mut spi = Spi::new_blocking(p.SPI1, clk, mosi, miso, config);\n\n    // Configure CS\n    let mut cs = Output::new(touch_cs, Level::Low);\n\n    loop {\n        cs.set_low();\n        let mut buf = [0x90, 0x00, 0x00, 0xd0, 0x00, 0x00];\n        spi.blocking_transfer_in_place(&mut buf).unwrap();\n        cs.set_high();\n\n        let x = (buf[1] as u32) << 5 | (buf[2] as u32) >> 3;\n        let y = (buf[4] as u32) << 5 | (buf[5] as u32) >> 3;\n\n        info!(\"touch: {=u32} {=u32}\", x, y);\n    }\n}\n"
  },
  {
    "path": "examples/rp235x/src/bin/spi_async.rs",
    "content": "//! This example shows how to use SPI (Serial Peripheral Interface) in the RP235x chip.\n//! No specific hardware is specified in this example. If you connect pin 11 and 12 you should get the same data back.\n\n#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_rp::bind_interrupts;\nuse embassy_rp::peripherals::{DMA_CH0, DMA_CH1};\nuse embassy_rp::spi::{Config, Spi};\nuse embassy_time::Timer;\nuse {defmt_rtt as _, panic_probe as _};\n\nbind_interrupts!(struct Irqs {\n    DMA_IRQ_0 => embassy_rp::dma::InterruptHandler<DMA_CH0>, embassy_rp::dma::InterruptHandler<DMA_CH1>;\n});\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let p = embassy_rp::init(Default::default());\n    info!(\"Hello World!\");\n\n    let miso = p.PIN_12;\n    let mosi = p.PIN_11;\n    let clk = p.PIN_10;\n\n    let mut spi = Spi::new(p.SPI1, clk, mosi, miso, p.DMA_CH0, p.DMA_CH1, Irqs, Config::default());\n\n    loop {\n        let tx_buf = [1_u8, 2, 3, 4, 5, 6];\n        let mut rx_buf = [0_u8; 6];\n        spi.transfer(&mut rx_buf, &tx_buf).await.unwrap();\n        info!(\"{:?}\", rx_buf);\n        Timer::after_secs(1).await;\n    }\n}\n"
  },
  {
    "path": "examples/rp235x/src/bin/spi_display.rs",
    "content": "//! This example shows how to use SPI (Serial Peripheral Interface) in the RP2350 chip.\n//!\n//! Example written for a display using the ST7789 chip. Possibly the Waveshare Pico-ResTouch\n//! (https://www.waveshare.com/wiki/Pico-ResTouch-LCD-2.8)\n\n#![no_std]\n#![no_main]\n\nuse core::cell::RefCell;\n\nuse defmt::*;\nuse display_interface_spi::SPIInterface;\nuse embassy_embedded_hal::shared_bus::blocking::spi::SpiDeviceWithConfig;\nuse embassy_executor::Spawner;\nuse embassy_rp::gpio::{Level, Output};\nuse embassy_rp::spi;\nuse embassy_rp::spi::{Blocking, Spi};\nuse embassy_sync::blocking_mutex::Mutex;\nuse embassy_sync::blocking_mutex::raw::NoopRawMutex;\nuse embassy_time::Delay;\nuse embedded_graphics::image::{Image, ImageRawLE};\nuse embedded_graphics::mono_font::MonoTextStyle;\nuse embedded_graphics::mono_font::ascii::FONT_10X20;\nuse embedded_graphics::pixelcolor::Rgb565;\nuse embedded_graphics::prelude::*;\nuse embedded_graphics::primitives::{PrimitiveStyleBuilder, Rectangle};\nuse embedded_graphics::text::Text;\nuse mipidsi::Builder;\nuse mipidsi::models::ST7789;\nuse mipidsi::options::{Orientation, Rotation};\nuse {defmt_rtt as _, panic_probe as _};\n\nuse crate::touch::Touch;\n\nconst DISPLAY_FREQ: u32 = 64_000_000;\nconst TOUCH_FREQ: u32 = 200_000;\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let p = embassy_rp::init(Default::default());\n    info!(\"Hello World!\");\n\n    let bl = p.PIN_13;\n    let rst = p.PIN_15;\n    let display_cs = p.PIN_9;\n    let dcx = p.PIN_8;\n    let miso = p.PIN_12;\n    let mosi = p.PIN_11;\n    let clk = p.PIN_10;\n    let touch_cs = p.PIN_16;\n    //let touch_irq = p.PIN_17;\n\n    // create SPI\n    let mut display_config = spi::Config::default();\n    display_config.frequency = DISPLAY_FREQ;\n    display_config.phase = spi::Phase::CaptureOnSecondTransition;\n    display_config.polarity = spi::Polarity::IdleHigh;\n    let mut touch_config = spi::Config::default();\n    touch_config.frequency = TOUCH_FREQ;\n    touch_config.phase = spi::Phase::CaptureOnSecondTransition;\n    touch_config.polarity = spi::Polarity::IdleHigh;\n\n    let spi: Spi<'_, _, Blocking> = Spi::new_blocking(p.SPI1, clk, mosi, miso, touch_config.clone());\n    let spi_bus: Mutex<NoopRawMutex, _> = Mutex::new(RefCell::new(spi));\n\n    let display_spi = SpiDeviceWithConfig::new(&spi_bus, Output::new(display_cs, Level::High), display_config);\n    let touch_spi = SpiDeviceWithConfig::new(&spi_bus, Output::new(touch_cs, Level::High), touch_config);\n\n    let mut touch = Touch::new(touch_spi);\n\n    let dcx = Output::new(dcx, Level::Low);\n    let rst = Output::new(rst, Level::Low);\n    // dcx: 0 = command, 1 = data\n\n    // Enable LCD backlight\n    let _bl = Output::new(bl, Level::High);\n\n    // display interface abstraction from SPI and DC\n    let di = SPIInterface::new(display_spi, dcx);\n\n    // Define the display from the display interface and initialize it\n    let mut display = Builder::new(ST7789, di)\n        .display_size(240, 320)\n        .reset_pin(rst)\n        .orientation(Orientation::new().rotate(Rotation::Deg90))\n        .init(&mut Delay)\n        .unwrap();\n    display.clear(Rgb565::BLACK).unwrap();\n\n    let raw_image_data = ImageRawLE::new(include_bytes!(\"../../assets/ferris.raw\"), 86);\n    let ferris = Image::new(&raw_image_data, Point::new(34, 68));\n\n    // Display the image\n    ferris.draw(&mut display).unwrap();\n\n    let style = MonoTextStyle::new(&FONT_10X20, Rgb565::GREEN);\n    Text::new(\n        \"Hello embedded_graphics \\n + embassy + RP235x!\",\n        Point::new(20, 200),\n        style,\n    )\n    .draw(&mut display)\n    .unwrap();\n\n    loop {\n        if let Some((x, y)) = touch.read() {\n            let style = PrimitiveStyleBuilder::new().fill_color(Rgb565::BLUE).build();\n\n            Rectangle::new(Point::new(x - 1, y - 1), Size::new(3, 3))\n                .into_styled(style)\n                .draw(&mut display)\n                .unwrap();\n        }\n    }\n}\n\n/// Driver for the XPT2046 resistive touchscreen sensor\nmod touch {\n    use embedded_hal_1::spi::{Operation, SpiDevice};\n\n    struct Calibration {\n        x1: i32,\n        x2: i32,\n        y1: i32,\n        y2: i32,\n        sx: i32,\n        sy: i32,\n    }\n\n    const CALIBRATION: Calibration = Calibration {\n        x1: 3880,\n        x2: 340,\n        y1: 262,\n        y2: 3850,\n        sx: 320,\n        sy: 240,\n    };\n\n    pub struct Touch<SPI: SpiDevice> {\n        spi: SPI,\n    }\n\n    impl<SPI> Touch<SPI>\n    where\n        SPI: SpiDevice,\n    {\n        pub fn new(spi: SPI) -> Self {\n            Self { spi }\n        }\n\n        pub fn read(&mut self) -> Option<(i32, i32)> {\n            let mut x = [0; 2];\n            let mut y = [0; 2];\n            self.spi\n                .transaction(&mut [\n                    Operation::Write(&[0x90]),\n                    Operation::Read(&mut x),\n                    Operation::Write(&[0xd0]),\n                    Operation::Read(&mut y),\n                ])\n                .unwrap();\n\n            let x = (u16::from_be_bytes(x) >> 3) as i32;\n            let y = (u16::from_be_bytes(y) >> 3) as i32;\n\n            let cal = &CALIBRATION;\n\n            let x = ((x - cal.x1) * cal.sx / (cal.x2 - cal.x1)).clamp(0, cal.sx);\n            let y = ((y - cal.y1) * cal.sy / (cal.y2 - cal.y1)).clamp(0, cal.sy);\n            if x == 0 && y == 0 { None } else { Some((x, y)) }\n        }\n    }\n}\n"
  },
  {
    "path": "examples/rp235x/src/bin/spi_sdmmc.rs",
    "content": "//! This example shows how to use `embedded-sdmmc` with the RP235x chip, over SPI.\n//!\n//! The example will attempt to read a file `MY_FILE.TXT` from the root directory\n//! of the SD card and print its contents.\n\n#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_embedded_hal::SetConfig;\nuse embassy_executor::Spawner;\nuse embassy_rp::spi::Spi;\nuse embassy_rp::{gpio, spi};\nuse embedded_hal_bus::spi::ExclusiveDevice;\nuse embedded_sdmmc::sdcard::{DummyCsPin, SdCard};\nuse gpio::{Level, Output};\nuse {defmt_rtt as _, panic_probe as _};\n\nstruct DummyTimesource();\n\nimpl embedded_sdmmc::TimeSource for DummyTimesource {\n    fn get_timestamp(&self) -> embedded_sdmmc::Timestamp {\n        embedded_sdmmc::Timestamp {\n            year_since_1970: 0,\n            zero_indexed_month: 0,\n            zero_indexed_day: 0,\n            hours: 0,\n            minutes: 0,\n            seconds: 0,\n        }\n    }\n}\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let p = embassy_rp::init(Default::default());\n\n    // SPI clock needs to be running at <= 400kHz during initialization\n    let mut config = spi::Config::default();\n    config.frequency = 400_000;\n    let spi = Spi::new_blocking(p.SPI1, p.PIN_10, p.PIN_11, p.PIN_12, config);\n    // Use a dummy cs pin here, for embedded-hal SpiDevice compatibility reasons\n    let spi_dev = ExclusiveDevice::new_no_delay(spi, DummyCsPin);\n    // Real cs pin\n    let cs = Output::new(p.PIN_16, Level::High);\n\n    let sdcard = SdCard::new(spi_dev, cs, embassy_time::Delay);\n    info!(\"Card size is {} bytes\", sdcard.num_bytes().unwrap());\n\n    // Now that the card is initialized, the SPI clock can go faster\n    let mut config = spi::Config::default();\n    config.frequency = 16_000_000;\n    sdcard.spi(|dev| SetConfig::set_config(dev.bus_mut(), &config)).ok();\n\n    // Now let's look for volumes (also known as partitions) on our block device.\n    // To do this we need a Volume Manager. It will take ownership of the block device.\n    let mut volume_mgr = embedded_sdmmc::VolumeManager::new(sdcard, DummyTimesource());\n\n    // Try and access Volume 0 (i.e. the first partition).\n    // The volume object holds information about the filesystem on that volume.\n    let mut volume0 = volume_mgr.open_volume(embedded_sdmmc::VolumeIdx(0)).unwrap();\n    info!(\"Volume 0: {:?}\", defmt::Debug2Format(&volume0));\n\n    // Open the root directory (mutably borrows from the volume).\n    let mut root_dir = volume0.open_root_dir().unwrap();\n\n    // Open a file called \"MY_FILE.TXT\" in the root directory\n    // This mutably borrows the directory.\n    let mut my_file = root_dir\n        .open_file_in_dir(\"MY_FILE.TXT\", embedded_sdmmc::Mode::ReadOnly)\n        .unwrap();\n\n    // Print the contents of the file\n    while !my_file.is_eof() {\n        let mut buf = [0u8; 32];\n        if let Ok(n) = my_file.read(&mut buf) {\n            info!(\"{:a}\", buf[..n]);\n        }\n    }\n\n    loop {}\n}\n"
  },
  {
    "path": "examples/rp235x/src/bin/trng.rs",
    "content": "//! This example shows TRNG usage\n\n#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_rp::bind_interrupts;\nuse embassy_rp::gpio::{Level, Output};\nuse embassy_rp::peripherals::TRNG;\nuse embassy_rp::trng::Trng;\nuse embassy_time::Timer;\nuse {defmt_rtt as _, panic_probe as _};\n\nbind_interrupts!(struct Irqs {\n    TRNG_IRQ => embassy_rp::trng::InterruptHandler<TRNG>;\n});\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let peripherals = embassy_rp::init(Default::default());\n\n    // Initialize the TRNG with default configuration\n    let mut trng = Trng::new(peripherals.TRNG, Irqs, embassy_rp::trng::Config::default());\n    // A buffer to collect random bytes in.\n    let mut randomness = [0u8; 58];\n\n    let mut led = Output::new(peripherals.PIN_25, Level::Low);\n\n    loop {\n        trng.fill_bytes(&mut randomness).await;\n        info!(\"Random bytes async {}\", &randomness);\n        trng.blocking_fill_bytes(&mut randomness);\n        info!(\"Random bytes blocking {}\", &randomness);\n        let random_u32 = trng.blocking_next_u32();\n        let random_u64 = trng.blocking_next_u64();\n        info!(\"Random u32 {} u64 {}\", random_u32, random_u64);\n        // Random number of blinks between 0 and 31\n        let blinks = random_u32 % 32;\n        for _ in 0..blinks {\n            led.set_high();\n            Timer::after_millis(20).await;\n            led.set_low();\n            Timer::after_millis(20).await;\n        }\n        Timer::after_millis(1000).await;\n    }\n}\n"
  },
  {
    "path": "examples/rp235x/src/bin/uart.rs",
    "content": "//! This example shows how to use UART (Universal asynchronous receiver-transmitter) in the RP235x chip.\n//!\n//! No specific hardware is specified in this example. Only output on pin 0 is tested.\n//! The Raspberry Pi Debug Probe (https://www.raspberrypi.com/products/debug-probe/) could be used\n//! with its UART port.\n\n#![no_std]\n#![no_main]\n\nuse embassy_executor::Spawner;\nuse embassy_rp::uart;\nuse {defmt_rtt as _, panic_probe as _};\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let p = embassy_rp::init(Default::default());\n    let config = uart::Config::default();\n    let mut uart = uart::Uart::new_blocking(p.UART1, p.PIN_4, p.PIN_5, config);\n    uart.blocking_write(\"Hello World!\\r\\n\".as_bytes()).unwrap();\n\n    loop {\n        uart.blocking_write(\"hello there!\\r\\n\".as_bytes()).unwrap();\n        cortex_m::asm::delay(1_000_000);\n    }\n}\n"
  },
  {
    "path": "examples/rp235x/src/bin/uart_buffered_split.rs",
    "content": "//! This example shows how to use UART (Universal asynchronous receiver-transmitter) in the RP235x chip.\n//!\n//! No specific hardware is specified in this example. If you connect pin 0 and 1 you should get the same data back.\n//! The Raspberry Pi Debug Probe (https://www.raspberrypi.com/products/debug-probe/) could be used\n//! with its UART port.\n\n#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_rp::bind_interrupts;\nuse embassy_rp::peripherals::UART0;\nuse embassy_rp::uart::{BufferedInterruptHandler, BufferedUart, BufferedUartRx, Config};\nuse embassy_time::Timer;\nuse embedded_io_async::{Read, Write};\nuse static_cell::StaticCell;\nuse {defmt_rtt as _, panic_probe as _};\n\nbind_interrupts!(struct Irqs {\n    UART0_IRQ => BufferedInterruptHandler<UART0>;\n});\n\n#[embassy_executor::main]\nasync fn main(spawner: Spawner) {\n    let p = embassy_rp::init(Default::default());\n    let (tx_pin, rx_pin, uart) = (p.PIN_0, p.PIN_1, p.UART0);\n\n    static TX_BUF: StaticCell<[u8; 16]> = StaticCell::new();\n    let tx_buf = &mut TX_BUF.init([0; 16])[..];\n    static RX_BUF: StaticCell<[u8; 16]> = StaticCell::new();\n    let rx_buf = &mut RX_BUF.init([0; 16])[..];\n    let uart = BufferedUart::new(uart, tx_pin, rx_pin, Irqs, tx_buf, rx_buf, Config::default());\n    let (mut tx, rx) = uart.split();\n\n    spawner.spawn(unwrap!(reader(rx)));\n\n    info!(\"Writing...\");\n    loop {\n        let data = [\n            1u8, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28,\n            29, 30, 31,\n        ];\n        info!(\"TX {:?}\", data);\n        tx.write_all(&data).await.unwrap();\n        Timer::after_secs(1).await;\n    }\n}\n\n#[embassy_executor::task]\nasync fn reader(mut rx: BufferedUartRx) {\n    info!(\"Reading...\");\n    loop {\n        let mut buf = [0; 31];\n        rx.read_exact(&mut buf).await.unwrap();\n        info!(\"RX {:?}\", buf);\n    }\n}\n"
  },
  {
    "path": "examples/rp235x/src/bin/uart_r503.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse defmt::{debug, error, info};\nuse embassy_executor::Spawner;\nuse embassy_rp::bind_interrupts;\nuse embassy_rp::peripherals::{DMA_CH0, DMA_CH1, UART0};\nuse embassy_rp::uart::{Config, DataBits, InterruptHandler as UARTInterruptHandler, Parity, StopBits, Uart};\nuse embassy_time::{Duration, Timer, with_timeout};\nuse heapless::Vec;\nuse {defmt_rtt as _, panic_probe as _};\n\nbind_interrupts!(pub struct Irqs {\n    UART0_IRQ  => UARTInterruptHandler<UART0>;\n    DMA_IRQ_0 => embassy_rp::dma::InterruptHandler<DMA_CH0>, embassy_rp::dma::InterruptHandler<DMA_CH1>;\n});\n\nconst START: u16 = 0xEF01;\nconst ADDRESS: u32 = 0xFFFFFFFF;\n\n// ================================================================================\n\n// Data package format\n// Name     Length          Description\n// ==========================================================================================================\n// Start    2 bytes         Fixed value of 0xEF01; High byte transferred first.\n// Address  4 bytes         Default value is 0xFFFFFFFF, which can be modified by command.\n//                          High byte transferred first and at wrong adder value, module\n//                          will reject to transfer.\n// PID      1 byte          01H     Command packet;\n//                          02H     Data packet; Data packet shall not appear alone in executing\n//                                  processs, must follow command packet or acknowledge packet.\n//                          07H     Acknowledge packet;\n//                          08H     End of Data packet.\n// LENGTH   2 bytes         Refers to the length of package content (command packets and data packets)\n//                          plus the length of Checksum (2 bytes). Unit is byte. Max length is 256 bytes.\n//                          And high byte is transferred first.\n// DATA     -               It can be commands, data, command’s parameters, acknowledge result, etc.\n//                          (fingerprint character value, template are all deemed as data);\n// SUM      2 bytes         The arithmetic sum of package identifier, package length and all package\n//                          contens. Overflowing bits are omitted. high byte is transferred first.\n\n// ================================================================================\n\n// Checksum is calculated on 'length (2 bytes) + data (??)'.\nfn compute_checksum(buf: Vec<u8, 32>) -> u16 {\n    let mut checksum = 0u16;\n\n    let check_end = buf.len();\n    let checked_bytes = &buf[6..check_end];\n    for byte in checked_bytes {\n        checksum += (*byte) as u16;\n    }\n    return checksum;\n}\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    info!(\"Start\");\n\n    let p = embassy_rp::init(Default::default());\n\n    // Initialize the fingerprint scanner.\n    let mut config = Config::default();\n    config.baudrate = 57600;\n    config.stop_bits = StopBits::STOP1;\n    config.data_bits = DataBits::DataBits8;\n    config.parity = Parity::ParityNone;\n\n    let (uart, tx_pin, tx_dma, rx_pin, rx_dma) = (p.UART0, p.PIN_16, p.DMA_CH0, p.PIN_17, p.DMA_CH1);\n    let uart = Uart::new(uart, tx_pin, rx_pin, Irqs, tx_dma, rx_dma, config);\n    let (mut tx, mut rx) = uart.split();\n\n    let mut vec_buf: Vec<u8, 32> = heapless::Vec::new();\n    let mut data: Vec<u8, 32> = heapless::Vec::new();\n\n    let mut speeds: Vec<u8, 3> = heapless::Vec::new();\n    let _ = speeds.push(0xC8); // Slow\n    let _ = speeds.push(0x20); // Medium\n    let _ = speeds.push(0x02); // Fast\n\n    // Cycle through the three colours Red, Blue and Purple forever.\n    loop {\n        for colour in 1..=3 {\n            for speed in &speeds {\n                // Set the data first, because the length is dependent on that.\n                // However, we write the length bits before we do the data.\n                data.clear();\n                let _ = data.push(0x01); // ctrl=Breathing light\n                let _ = data.push(*speed);\n                let _ = data.push(colour as u8); // colour=Red, Blue, Purple\n                let _ = data.push(0x00); // times=Infinite\n\n                // Clear buffers\n                vec_buf.clear();\n\n                // START\n                let _ = vec_buf.extend_from_slice(&START.to_be_bytes()[..]);\n\n                // ADDRESS\n                let _ = vec_buf.extend_from_slice(&ADDRESS.to_be_bytes()[..]);\n\n                // PID\n                let _ = vec_buf.extend_from_slice(&[0x01]);\n\n                // LENGTH\n                let len: u16 = (1 + data.len() + 2).try_into().unwrap();\n                let _ = vec_buf.extend_from_slice(&len.to_be_bytes()[..]);\n\n                // COMMAND\n                let _ = vec_buf.push(0x35); // Command: AuraLedConfig\n\n                // DATA\n                let _ = vec_buf.extend_from_slice(&data);\n\n                // SUM\n                let chk = compute_checksum(vec_buf.clone());\n                let _ = vec_buf.extend_from_slice(&chk.to_be_bytes()[..]);\n\n                // =====\n\n                // Send command buffer.\n                let data_write: [u8; 16] = vec_buf.clone().into_array().unwrap();\n                debug!(\"  write='{:?}'\", data_write[..]);\n                match tx.write(&data_write).await {\n                    Ok(..) => info!(\"Write successful.\"),\n                    Err(e) => error!(\"Write error: {:?}\", e),\n                }\n\n                // =====\n\n                // Read command buffer.\n                let mut read_buf: [u8; 1] = [0; 1]; // Can only read one byte at a time!\n                let mut data_read: Vec<u8, 32> = heapless::Vec::new(); // Save buffer.\n\n                info!(\"Attempting read.\");\n                loop {\n                    // Some commands, like `Img2Tz()` needs longer, but we hard-code this to 200ms\n                    // for this command.\n                    match with_timeout(Duration::from_millis(200), rx.read(&mut read_buf)).await {\n                        Ok(..) => {\n                            // Extract and save read byte.\n                            debug!(\"  r='{=u8:#04x}H' ({:03}D)\", read_buf[0], read_buf[0]);\n                            let _ = data_read.push(read_buf[0]).unwrap();\n                        }\n                        Err(..) => break, // TimeoutError -> Ignore.\n                    }\n                }\n                info!(\"Read successful\");\n                debug!(\"  read='{:?}'\", data_read[..]);\n\n                Timer::after_secs(3).await;\n                info!(\"Changing speed.\");\n            }\n\n            info!(\"Changing colour.\");\n        }\n    }\n}\n"
  },
  {
    "path": "examples/rp235x/src/bin/uart_unidir.rs",
    "content": "//! This example shows how to use UART (Universal asynchronous receiver-transmitter) in the RP235x chip.\n//!\n//! Test TX-only and RX-only on two different UARTs. You need to connect GPIO0 to GPIO5 for\n//! this to work\n//! The Raspberry Pi Debug Probe (https://www.raspberrypi.com/products/debug-probe/) could be used\n//! with its UART port.\n\n#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_rp::peripherals::{DMA_CH0, DMA_CH1, UART1};\nuse embassy_rp::uart::{Async, Config, InterruptHandler, UartRx, UartTx};\nuse embassy_rp::{bind_interrupts, dma};\nuse embassy_time::Timer;\nuse {defmt_rtt as _, panic_probe as _};\n\nbind_interrupts!(struct Irqs {\n    UART1_IRQ => InterruptHandler<UART1>;\n    DMA_IRQ_0 => dma::InterruptHandler<DMA_CH0>, dma::InterruptHandler<DMA_CH1>;\n});\n\n#[embassy_executor::main]\nasync fn main(spawner: Spawner) {\n    let p = embassy_rp::init(Default::default());\n\n    let mut uart_tx = UartTx::new(p.UART0, p.PIN_0, p.DMA_CH0, Irqs, Config::default());\n    let uart_rx = UartRx::new(p.UART1, p.PIN_5, Irqs, p.DMA_CH1, Config::default());\n\n    spawner.spawn(unwrap!(reader(uart_rx)));\n\n    info!(\"Writing...\");\n    loop {\n        let data = [1u8, 2, 3, 4, 5, 6, 7, 8];\n        info!(\"TX {:?}\", data);\n        uart_tx.write(&data).await.unwrap();\n        Timer::after_secs(1).await;\n    }\n}\n\n#[embassy_executor::task]\nasync fn reader(mut rx: UartRx<'static, Async>) {\n    info!(\"Reading...\");\n    loop {\n        // read a total of 4 transmissions (32 / 8) and then print the result\n        let mut buf = [0; 32];\n        rx.read(&mut buf).await.unwrap();\n        info!(\"RX {:?}\", buf);\n    }\n}\n"
  },
  {
    "path": "examples/rp235x/src/bin/usb_hid_keyboard.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse core::sync::atomic::{AtomicBool, AtomicU8, Ordering};\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_futures::join::join;\nuse embassy_rp::bind_interrupts;\nuse embassy_rp::gpio::{Input, Pull};\nuse embassy_rp::peripherals::USB;\nuse embassy_rp::usb::{Driver as UsbDriver, InterruptHandler};\nuse embassy_usb::class::hid::{\n    HidBootProtocol, HidProtocolMode, HidReaderWriter, HidSubclass, ReportId, RequestHandler, State as HidState,\n};\nuse embassy_usb::control::OutResponse;\nuse embassy_usb::{Builder, Config, Handler};\nuse usbd_hid::descriptor::{KeyboardReport, SerializedDescriptor};\nuse {defmt_rtt as _, panic_probe as _};\n\nbind_interrupts!(struct Irqs {\n    USBCTRL_IRQ => InterruptHandler<USB>;\n});\n\nstatic HID_PROTOCOL_MODE: AtomicU8 = AtomicU8::new(HidProtocolMode::Boot as u8);\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let p = embassy_rp::init(Default::default());\n    // Create the driver, from the HAL.\n    let driver = UsbDriver::new(p.USB, Irqs);\n\n    // Create embassy-usb Config\n    let mut config = Config::new(0xc0de, 0xcafe);\n    config.manufacturer = Some(\"Embassy\");\n    config.product = Some(\"HID keyboard example\");\n    config.serial_number = Some(\"12345678\");\n    config.max_power = 100;\n    config.max_packet_size_0 = 64;\n    config.composite_with_iads = false;\n    config.device_class = 0;\n    config.device_sub_class = 0;\n    config.device_protocol = 0;\n\n    // Create embassy-usb DeviceBuilder using the driver and config.\n    // It needs some buffers for building the descriptors.\n    let mut config_descriptor = [0; 256];\n    let mut bos_descriptor = [0; 256];\n    // You can also add a Microsoft OS descriptor.\n    let mut msos_descriptor = [0; 256];\n    let mut control_buf = [0; 64];\n    let mut request_handler = MyRequestHandler {};\n    let mut device_handler = MyDeviceHandler::new();\n\n    let mut state = HidState::new();\n\n    let mut builder = Builder::new(\n        driver,\n        config,\n        &mut config_descriptor,\n        &mut bos_descriptor,\n        &mut msos_descriptor,\n        &mut control_buf,\n    );\n\n    builder.handler(&mut device_handler);\n\n    // Create classes on the builder.\n    let config = embassy_usb::class::hid::Config {\n        report_descriptor: KeyboardReport::desc(),\n        request_handler: None,\n        poll_ms: 60,\n        max_packet_size: 64,\n        hid_subclass: HidSubclass::Boot,\n        hid_boot_protocol: HidBootProtocol::Keyboard,\n    };\n    let hid = HidReaderWriter::<_, 1, 8>::new(&mut builder, &mut state, config);\n\n    // Build the builder.\n    let mut usb = builder.build();\n\n    // Run the USB device.\n    let usb_fut = usb.run();\n\n    // Set up the signal pin that will be used to trigger the keyboard.\n    let mut signal_pin = Input::new(p.PIN_16, Pull::None);\n\n    // Enable the schmitt trigger to slightly debounce.\n    signal_pin.set_schmitt(true);\n\n    let (reader, mut writer) = hid.split();\n\n    // Do stuff with the class!\n    let in_fut = async {\n        loop {\n            info!(\"Waiting for HIGH on pin 16\");\n            signal_pin.wait_for_high().await;\n            info!(\"HIGH DETECTED\");\n\n            if HID_PROTOCOL_MODE.load(Ordering::Relaxed) == HidProtocolMode::Boot as u8 {\n                match writer.write(&[0, 0, 4, 0, 0, 0, 0, 0]).await {\n                    Ok(()) => {}\n                    Err(e) => warn!(\"Failed to send boot report: {:?}\", e),\n                };\n            } else {\n                // Create a report with the A key pressed. (no shift modifier)\n                let report = KeyboardReport {\n                    keycodes: [4, 0, 0, 0, 0, 0],\n                    leds: 0,\n                    modifier: 0,\n                    reserved: 0,\n                };\n                // Send the report.\n                match writer.write_serialize(&report).await {\n                    Ok(()) => {}\n                    Err(e) => warn!(\"Failed to send report: {:?}\", e),\n                };\n            }\n            signal_pin.wait_for_low().await;\n            info!(\"LOW DETECTED\");\n            if HID_PROTOCOL_MODE.load(Ordering::Relaxed) == HidProtocolMode::Boot as u8 {\n                match writer.write(&[0, 0, 0, 0, 0, 0, 0, 0]).await {\n                    Ok(()) => {}\n                    Err(e) => warn!(\"Failed to send boot report: {:?}\", e),\n                };\n            } else {\n                let report = KeyboardReport {\n                    keycodes: [0, 0, 0, 0, 0, 0],\n                    leds: 0,\n                    modifier: 0,\n                    reserved: 0,\n                };\n                match writer.write_serialize(&report).await {\n                    Ok(()) => {}\n                    Err(e) => warn!(\"Failed to send report: {:?}\", e),\n                };\n            }\n        }\n    };\n\n    let out_fut = async {\n        reader.run(false, &mut request_handler).await;\n    };\n\n    // Run everything concurrently.\n    // If we had made everything `'static` above instead, we could do this using separate tasks instead.\n    join(usb_fut, join(in_fut, out_fut)).await;\n}\n\nstruct MyRequestHandler {}\n\nimpl RequestHandler for MyRequestHandler {\n    fn get_report(&mut self, id: ReportId, _buf: &mut [u8]) -> Option<usize> {\n        info!(\"Get report for {:?}\", id);\n        None\n    }\n\n    fn set_report(&mut self, id: ReportId, data: &[u8]) -> OutResponse {\n        info!(\"Set report for {:?}: {=[u8]}\", id, data);\n        OutResponse::Accepted\n    }\n\n    fn get_protocol(&self) -> HidProtocolMode {\n        let protocol = HidProtocolMode::from(HID_PROTOCOL_MODE.load(Ordering::Relaxed));\n        info!(\"The current HID protocol mode is: {}\", protocol);\n        protocol\n    }\n\n    fn set_protocol(&mut self, protocol: HidProtocolMode) -> OutResponse {\n        info!(\"Switching to HID protocol mode: {}\", protocol);\n        HID_PROTOCOL_MODE.store(protocol as u8, Ordering::Relaxed);\n        OutResponse::Accepted\n    }\n\n    fn set_idle_ms(&mut self, id: Option<ReportId>, dur: u32) {\n        info!(\"Set idle rate for {:?} to {:?}\", id, dur);\n    }\n\n    fn get_idle_ms(&mut self, id: Option<ReportId>) -> Option<u32> {\n        info!(\"Get idle rate for {:?}\", id);\n        None\n    }\n}\n\nstruct MyDeviceHandler {\n    configured: AtomicBool,\n}\n\nimpl MyDeviceHandler {\n    fn new() -> Self {\n        MyDeviceHandler {\n            configured: AtomicBool::new(false),\n        }\n    }\n}\n\nimpl Handler for MyDeviceHandler {\n    fn enabled(&mut self, enabled: bool) {\n        self.configured.store(false, Ordering::Relaxed);\n        if enabled {\n            info!(\"Device enabled\");\n        } else {\n            info!(\"Device disabled\");\n        }\n    }\n\n    fn reset(&mut self) {\n        self.configured.store(false, Ordering::Relaxed);\n        info!(\"Bus reset, the Vbus current limit is 100mA\");\n    }\n\n    fn addressed(&mut self, addr: u8) {\n        self.configured.store(false, Ordering::Relaxed);\n        info!(\"USB address set to: {}\", addr);\n    }\n\n    fn configured(&mut self, configured: bool) {\n        self.configured.store(configured, Ordering::Relaxed);\n        if configured {\n            info!(\"Device configured, it may now draw up to the configured current limit from Vbus.\")\n        } else {\n            info!(\"Device is no longer configured, the Vbus current limit is 100mA.\");\n        }\n    }\n}\n"
  },
  {
    "path": "examples/rp235x/src/bin/usb_webusb.rs",
    "content": "//! This example shows how to use USB (Universal Serial Bus) in the RP235x chip.\n//!\n//! This creates a WebUSB capable device that echoes data back to the host.\n//!\n//! To test this in the browser (ideally host this on localhost:8080, to test the landing page\n//! feature):\n//! ```js\n//! (async () => {\n//!     const device = await navigator.usb.requestDevice({ filters: [{ vendorId: 0xf569 }] });\n//!     await device.open();\n//!     await device.claimInterface(1);\n//!     device.transferIn(1, 64).then(data => console.log(data));\n//!     await device.transferOut(1, new Uint8Array([1,2,3]));\n//! })();\n//! ```\n\n#![no_std]\n#![no_main]\n\nuse defmt::info;\nuse embassy_executor::Spawner;\nuse embassy_futures::join::join;\nuse embassy_rp::bind_interrupts;\nuse embassy_rp::peripherals::USB;\nuse embassy_rp::usb::{Driver as UsbDriver, InterruptHandler};\nuse embassy_usb::class::web_usb::{Config as WebUsbConfig, State, Url, WebUsb};\nuse embassy_usb::driver::{Driver, Endpoint, EndpointIn, EndpointOut};\nuse embassy_usb::msos::{self, windows_version};\nuse embassy_usb::{Builder, Config};\nuse {defmt_rtt as _, panic_probe as _};\n\nbind_interrupts!(struct Irqs {\n    USBCTRL_IRQ => InterruptHandler<USB>;\n});\n\n// This is a randomly generated GUID to allow clients on Windows to find our device\nconst DEVICE_INTERFACE_GUIDS: &[&str] = &[\"{AFB9A6FB-30BA-44BC-9232-806CFC875321}\"];\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let p = embassy_rp::init(Default::default());\n\n    // Create the driver, from the HAL.\n    let driver = UsbDriver::new(p.USB, Irqs);\n\n    // Create embassy-usb Config\n    let mut config = Config::new(0xf569, 0x0001);\n    config.manufacturer = Some(\"Embassy\");\n    config.product = Some(\"WebUSB example\");\n    config.serial_number = Some(\"12345678\");\n    config.max_power = 100;\n    config.max_packet_size_0 = 64;\n\n    // Create embassy-usb DeviceBuilder using the driver and config.\n    // It needs some buffers for building the descriptors.\n    let mut config_descriptor = [0; 256];\n    let mut bos_descriptor = [0; 256];\n    let mut control_buf = [0; 64];\n    let mut msos_descriptor = [0; 256];\n\n    let webusb_config = WebUsbConfig {\n        max_packet_size: 64,\n        vendor_code: 1,\n        // If defined, shows a landing page which the device manufacturer would like the user to visit in order to control their device. Suggest the user to navigate to this URL when the device is connected.\n        landing_url: Some(Url::new(\"http://localhost:8080\")),\n    };\n\n    let mut state = State::new();\n\n    let mut builder = Builder::new(\n        driver,\n        config,\n        &mut config_descriptor,\n        &mut bos_descriptor,\n        &mut msos_descriptor,\n        &mut control_buf,\n    );\n\n    // Add the Microsoft OS Descriptor (MSOS/MOD) descriptor.\n    // We tell Windows that this entire device is compatible with the \"WINUSB\" feature,\n    // which causes it to use the built-in WinUSB driver automatically, which in turn\n    // can be used by libusb/rusb software without needing a custom driver or INF file.\n    // In principle you might want to call msos_feature() just on a specific function,\n    // if your device also has other functions that still use standard class drivers.\n    builder.msos_descriptor(windows_version::WIN8_1, 0);\n    builder.msos_feature(msos::CompatibleIdFeatureDescriptor::new(\"WINUSB\", \"\"));\n    builder.msos_feature(msos::RegistryPropertyFeatureDescriptor::new(\n        \"DeviceInterfaceGUIDs\",\n        msos::PropertyData::RegMultiSz(DEVICE_INTERFACE_GUIDS),\n    ));\n\n    // Create classes on the builder (WebUSB just needs some setup, but doesn't return anything)\n    WebUsb::configure(&mut builder, &mut state, &webusb_config);\n    // Create some USB bulk endpoints for testing.\n    let mut endpoints = WebEndpoints::new(&mut builder, &webusb_config);\n\n    // Build the builder.\n    let mut usb = builder.build();\n\n    // Run the USB device.\n    let usb_fut = usb.run();\n\n    // Do some WebUSB transfers.\n    let webusb_fut = async {\n        loop {\n            endpoints.wait_connected().await;\n            info!(\"Connected\");\n            endpoints.echo().await;\n        }\n    };\n\n    // Run everything concurrently.\n    // If we had made everything `'static` above instead, we could do this using separate tasks instead.\n    join(usb_fut, webusb_fut).await;\n}\n\nstruct WebEndpoints<'d, D: Driver<'d>> {\n    write_ep: D::EndpointIn,\n    read_ep: D::EndpointOut,\n}\n\nimpl<'d, D: Driver<'d>> WebEndpoints<'d, D> {\n    fn new(builder: &mut Builder<'d, D>, config: &'d WebUsbConfig<'d>) -> Self {\n        let mut func = builder.function(0xff, 0x00, 0x00);\n        let mut iface = func.interface();\n        let mut alt = iface.alt_setting(0xff, 0x00, 0x00, None);\n\n        let write_ep = alt.endpoint_bulk_in(None, config.max_packet_size);\n        let read_ep = alt.endpoint_bulk_out(None, config.max_packet_size);\n\n        WebEndpoints { write_ep, read_ep }\n    }\n\n    // Wait until the device's endpoints are enabled.\n    async fn wait_connected(&mut self) {\n        self.read_ep.wait_enabled().await\n    }\n\n    // Echo data back to the host.\n    async fn echo(&mut self) {\n        let mut buf = [0; 64];\n        loop {\n            let n = self.read_ep.read(&mut buf).await.unwrap();\n            let data = &buf[..n];\n            info!(\"Data read: {:x}\", data);\n            self.write_ep.write(data).await.unwrap();\n        }\n    }\n}\n"
  },
  {
    "path": "examples/rp235x/src/bin/watchdog.rs",
    "content": "//! This example shows how to use Watchdog in the RP235x chip.\n//!\n//! It does not work with the RP Pico W board. See wifi_blinky.rs or connect external LED and resistor.\n\n#![no_std]\n#![no_main]\n\nuse defmt::info;\nuse embassy_executor::Spawner;\nuse embassy_rp::gpio;\nuse embassy_rp::watchdog::*;\nuse embassy_time::{Duration, Timer};\nuse gpio::{Level, Output};\nuse {defmt_rtt as _, panic_probe as _};\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let p = embassy_rp::init(Default::default());\n    info!(\"Hello world!\");\n\n    let mut watchdog = Watchdog::new(p.WATCHDOG);\n    let mut led = Output::new(p.PIN_25, Level::Low);\n\n    // Set the LED high for 2 seconds so we know when we're about to start the watchdog\n    led.set_high();\n    Timer::after_secs(2).await;\n\n    // Set to watchdog to reset if it's not fed within 1.05 seconds, and start it\n    watchdog.start(Duration::from_millis(5_050));\n    info!(\"Started the watchdog timer\");\n    Timer::after_millis(4000).await;\n\n    // Blink once a second for 5 seconds, feed the watchdog timer once a second to avoid a reset\n    for _ in 1..=5 {\n        led.set_low();\n        Timer::after_millis(500).await;\n        led.set_high();\n        Timer::after_millis(500).await;\n        info!(\"Feeding watchdog\");\n        watchdog.feed(Duration::from_millis(1_050));\n    }\n\n    info!(\"Stopped feeding, device will reset in 1.05 seconds\");\n    // Blink 10 times per second, not feeding the watchdog.\n    // The processor should reset in 1.05 seconds.\n    loop {\n        led.set_low();\n        Timer::after_millis(100).await;\n        led.set_high();\n        Timer::after_millis(100).await;\n    }\n}\n"
  },
  {
    "path": "examples/rp235x/src/bin/zerocopy.rs",
    "content": "//! This example shows how to use `zerocopy_channel` from `embassy_sync` for\n//! sending large values between two tasks without copying.\n//! The example also shows how to use the RP235x ADC with DMA.\n#![no_std]\n#![no_main]\n\nuse core::sync::atomic::{AtomicU16, Ordering};\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_rp::adc::{self, Adc, Async, Config, InterruptHandler};\nuse embassy_rp::gpio::Pull;\nuse embassy_rp::peripherals::DMA_CH0;\nuse embassy_rp::{bind_interrupts, dma};\nuse embassy_sync::blocking_mutex::raw::NoopRawMutex;\nuse embassy_sync::zerocopy_channel::{Channel, Receiver, Sender};\nuse embassy_time::{Duration, Ticker, Timer};\nuse static_cell::StaticCell;\nuse {defmt_rtt as _, panic_probe as _};\n\ntype SampleBuffer = [u16; 512];\n\nbind_interrupts!(struct Irqs {\n    ADC_IRQ_FIFO => InterruptHandler;\n    DMA_IRQ_0 => dma::InterruptHandler<DMA_CH0>;\n});\n\nconst BLOCK_SIZE: usize = 512;\nconst NUM_BLOCKS: usize = 2;\nstatic MAX: AtomicU16 = AtomicU16::new(0);\n\nstruct AdcParts {\n    adc: Adc<'static, Async>,\n    pin: adc::Channel<'static>,\n    dma: dma::Channel<'static>,\n}\n\n#[embassy_executor::main]\nasync fn main(spawner: Spawner) {\n    let p = embassy_rp::init(Default::default());\n    info!(\"Here we go!\");\n\n    let adc_parts = AdcParts {\n        adc: Adc::new(p.ADC, Irqs, Config::default()),\n        pin: adc::Channel::new_pin(p.PIN_29, Pull::None),\n        dma: dma::Channel::new(p.DMA_CH0, Irqs),\n    };\n\n    static BUF: StaticCell<[SampleBuffer; NUM_BLOCKS]> = StaticCell::new();\n    let buf = BUF.init([[0; BLOCK_SIZE]; NUM_BLOCKS]);\n\n    static CHANNEL: StaticCell<Channel<'_, NoopRawMutex, SampleBuffer>> = StaticCell::new();\n    let channel = CHANNEL.init(Channel::new(buf));\n    let (sender, receiver) = channel.split();\n\n    spawner.spawn(consumer(receiver).unwrap());\n    spawner.spawn(producer(sender, adc_parts).unwrap());\n\n    let mut ticker = Ticker::every(Duration::from_secs(1));\n    loop {\n        ticker.next().await;\n        let max = MAX.load(Ordering::Relaxed);\n        info!(\"latest block's max value: {:?}\", max);\n    }\n}\n\n#[embassy_executor::task]\nasync fn producer(mut sender: Sender<'static, NoopRawMutex, SampleBuffer>, mut adc: AdcParts) {\n    loop {\n        // Obtain a free buffer from the channel\n        let buf = sender.send().await;\n\n        // Fill it with data\n        adc.adc.read_many(&mut adc.pin, buf, 1, &mut adc.dma).await.unwrap();\n\n        // Notify the channel that the buffer is now ready to be received\n        sender.send_done();\n    }\n}\n\n#[embassy_executor::task]\nasync fn consumer(mut receiver: Receiver<'static, NoopRawMutex, SampleBuffer>) {\n    loop {\n        // Receive a buffer from the channel\n        let buf = receiver.receive().await;\n\n        // Simulate using the data, while the producer is filling up the next buffer\n        Timer::after_micros(1000).await;\n        let max = buf.iter().max().unwrap();\n        MAX.store(*max, Ordering::Relaxed);\n\n        // Notify the channel that the buffer is now ready to be reused\n        receiver.receive_done();\n    }\n}\n"
  },
  {
    "path": "examples/std/Cargo.toml",
    "content": "[package]\nedition = \"2024\"\nname = \"embassy-std-examples\"\nversion = \"0.1.0\"\nlicense = \"MIT OR Apache-2.0\"\npublish = false\n\n[dependencies]\nembassy-sync = { version = \"0.8.0\", path = \"../../embassy-sync\", features = [\"log\"] }\nembassy-executor = { version = \"0.10.0\", path = \"../../embassy-executor\", features = [\"platform-std\", \"executor-thread\", \"log\"] }\nembassy-time = { version = \"0.5.1\", path = \"../../embassy-time\", features = [\"log\", \"std\", ] }\nembassy-net = { version = \"0.9.0\", path = \"../../embassy-net\", features=[ \"log\", \"medium-ethernet\", \"medium-ip\", \"tcp\", \"udp\", \"dns\", \"dhcpv4\", \"proto-ipv6\"] }\nembassy-net-tuntap = { version = \"0.1.1\", path = \"../../embassy-net-tuntap\" }\nembassy-net-ppp = { version = \"0.3.0\", path = \"../../embassy-net-ppp\", features = [\"log\"]}\nembedded-io-async = { version = \"0.7.0\" }\nembedded-io-adapters = { version = \"0.7.0\", features = [\"futures-03\"] }\ncritical-section = { version = \"1.1\", features = [\"std\"] }\n\nasync-io = \"1.6.0\"\nenv_logger = \"0.9.0\"\nfutures = { version = \"0.3.17\" }\nlog = \"0.4.14\"\nnix = \"0.26.2\"\nclap = { version = \"3.0.0-beta.5\", features = [\"derive\"] }\nrand_core = { version = \"0.9.1\", features = [\"std\", \"os_rng\"] }\nheapless = { version = \"0.9\", default-features = false }\nstatic_cell = \"2\"\n\n[profile.release]\ndebug = 2\n\n[package.metadata.embassy]\nbuild = [\n  { artifact-dir = \"out/examples/std\" }\n]\n"
  },
  {
    "path": "examples/std/README.md",
    "content": "\n## Running the `embassy-net` examples\n\nTo run `net`, `tcp_accept`, `net_udp` and `net_dns` examples you will need a tap interface. Before running these examples, create the tap99 interface. (The number was chosen to\nhopefully not collide with anything.) You only need to do this once every time you reboot your computer.\n\n```sh\ncd $EMBASSY_ROOT/examples/std/\nsudo sh tap.sh\n```\n\nThe example `net_ppp` requires different steps that are detailed in its section.\n\n### `net` example\n\nFor this example, you need to have something listening in the correct port. For example `nc -lp 8000`.\n\nThen run the example located in the `examples` folder:\n\n```sh\ncd $EMBASSY_ROOT/examples/std/\ncargo run --bin net -- --tap tap99 --static-ip\n```\n### `tcp_accept` example\n\nThis example listen for a tcp connection.\n\nFirst run the example located in the `examples` folder:\n\n```sh\ncd $EMBASSY_ROOT/examples/std/\ncargo run --bin tcp_accept -- --tap tap99 --static-ip\n```\n\nThen open a connection to the port. For example `nc 192.168.69.2 9999`.\n\n### `net_udp` example\n\nThis example listen for a udp connection.\n\nFirst run the example located in the `examples` folder:\n\n```sh\ncd $EMBASSY_ROOT/examples/std/\ncargo run --bin net_udp -- --tap tap99 --static-ip\n```\n\nThen open a connection to the port. For example `nc -u 192.168.69.2 9400`.\n\n### `net_dns` example\n\nThis example queries a `DNS` for the IP address of `www.example.com`.\n\nIn order to achieve this, the `tap99` interface requires configuring tap99 as a gateway device temporarily.\n\nFor example, in Ubuntu you can do this by:\n\n1. Identifying your default route device. In the next example `eth0`\n\n```sh\nip r | grep \"default\"\ndefault via 192.168.2.1 dev eth0 proto kernel metric 35\n```\n\n2. Enabling temporarily IP Forwarding:\n\n```sh\nsudo sysctl -w net.ipv4.ip_forward=1\n```\n\n3. Configuring NAT to mascarade traffic from `tap99` to `eth0`\n\n```sh\nsudo iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE\nsudo iptables -A FORWARD -i tap99 -j ACCEPT\nsudo iptables -A FORWARD -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT\n```\n\n4. Then you can run the example located in the `examples` folder:\n\n```sh\ncd $EMBASSY_ROOT/examples/std/\ncargo run --bin net_dns -- --tap tap99 --static-ip\n```\n\n### `net_ppp` example\n\nThis example establish a Point-to-Point Protocol (PPP) connection that can be used, for example, for connecting to internet through a 4G modem via a serial channel.\n\nThe example creates a PPP bridge over a virtual serial channel between `pty1` and `pty2` for the example code and a PPP server running on the same computer. \n\nTo run this example you will need:\n- ppp (pppd server)\n- socat (socket CAT)\n\nTo run the examples you may follow the next steps:\n\n1. Save the PPP server configuration:\n```sh\nsudo sh -c 'echo \"myuser $(hostname) mypass 192.168.7.10\" >> /etc/ppp/pap-secrets'\n```\n\n2. Create a files `pty1` and `pty2` and link them \n```sh\ncd $EMBASSY_ROOT/examples/std/\nsocat -v -x PTY,link=pty1,rawer PTY,link=pty2,rawer\n```\n\n3. open a second terminal and start the PPP server:\n```sh\ncd $EMBASSY_ROOT/examples/std/\nsudo pppd $PWD/pty1 115200 192.168.7.1: ms-dns 8.8.4.4 ms-dns 8.8.8.8 nodetach debug local persist silent\n```\n\n4. Open a third terminal and run the example\n```sh\ncd $EMBASSY_ROOT/examples/std/\nRUST_LOG=trace cargo run --bin net_ppp -- --device pty2\n```\n5. Observe the output in the second and third terminal\n6. Open one last terminal to interact with `net_ppp` example through the PPP connection\n```sh\n# ping the net_ppp client\nping 192.168.7.10\n# open an tcp connection\nnc 192.168.7.10 1234\n# Type anything and observe the output in the different terminals\n```\n"
  },
  {
    "path": "examples/std/src/bin/net.rs",
    "content": "use core::fmt::Write as _;\n\nuse clap::Parser;\nuse embassy_executor::{Executor, Spawner};\nuse embassy_net::tcp::TcpSocket;\nuse embassy_net::{Config, Ipv4Address, Ipv4Cidr, StackResources};\nuse embassy_net_tuntap::TunTapDevice;\nuse embassy_time::Duration;\nuse embedded_io_async::Write;\nuse heapless::Vec;\nuse log::*;\nuse rand_core::{OsRng, TryRngCore};\nuse static_cell::StaticCell;\n\n#[derive(Parser)]\n#[clap(version = \"1.0\")]\nstruct Opts {\n    /// TAP device name\n    #[clap(long, default_value = \"tap0\")]\n    tap: String,\n    /// use a static IP instead of DHCP\n    #[clap(long)]\n    static_ip: bool,\n}\n\n#[embassy_executor::task]\nasync fn net_task(mut runner: embassy_net::Runner<'static, TunTapDevice>) -> ! {\n    runner.run().await\n}\n\n#[embassy_executor::task]\nasync fn main_task(spawner: Spawner) {\n    let opts: Opts = Opts::parse();\n\n    // Init network device\n    let device = TunTapDevice::new(&opts.tap).unwrap();\n\n    // Choose between dhcp or static ip\n    let config = if opts.static_ip {\n        Config::ipv4_static(embassy_net::StaticConfigV4 {\n            address: Ipv4Cidr::new(Ipv4Address::new(192, 168, 69, 2), 24),\n            dns_servers: Vec::new(),\n            gateway: Some(Ipv4Address::new(192, 168, 69, 1)),\n        })\n    } else {\n        Config::dhcpv4(Default::default())\n    };\n\n    // Generate random seed\n    let mut seed = [0; 8];\n    OsRng.try_fill_bytes(&mut seed).unwrap();\n    let seed = u64::from_le_bytes(seed);\n\n    // Init network stack\n    static RESOURCES: StaticCell<StackResources<3>> = StaticCell::new();\n    let (stack, runner) = embassy_net::new(device, config, RESOURCES.init(StackResources::new()), seed);\n\n    // Launch network task\n    spawner.spawn(net_task(runner).unwrap());\n\n    // Then we can use it!\n    let mut rx_buffer = [0; 4096];\n    let mut tx_buffer = [0; 4096];\n    let mut socket = TcpSocket::new(stack, &mut rx_buffer, &mut tx_buffer);\n\n    socket.set_timeout(Some(Duration::from_secs(10)));\n\n    let remote_endpoint = (Ipv4Address::new(192, 168, 69, 100), 8000);\n    info!(\"connecting to {:?}...\", remote_endpoint);\n    let r = socket.connect(remote_endpoint).await;\n    if let Err(e) = r {\n        warn!(\"connect error: {:?}\", e);\n        return;\n    }\n    info!(\"connected!\");\n    for i in 0.. {\n        let mut buf = heapless::String::<100>::new();\n        write!(buf, \"Hello! ({})\\r\\n\", i).unwrap();\n        let r = socket.write_all(buf.as_bytes()).await;\n        if let Err(e) = r {\n            warn!(\"write error: {:?}\", e);\n            return;\n        }\n    }\n}\n\nstatic EXECUTOR: StaticCell<Executor> = StaticCell::new();\n\nfn main() {\n    env_logger::builder()\n        .filter_level(log::LevelFilter::Debug)\n        .filter_module(\"async_io\", log::LevelFilter::Info)\n        .format_timestamp_nanos()\n        .init();\n\n    let executor = EXECUTOR.init(Executor::new());\n    executor.run(|spawner| {\n        spawner.spawn(main_task(spawner).unwrap());\n    });\n}\n"
  },
  {
    "path": "examples/std/src/bin/net_dns.rs",
    "content": "use clap::Parser;\nuse embassy_executor::{Executor, Spawner};\nuse embassy_net::dns::DnsQueryType;\nuse embassy_net::{Config, Ipv4Address, Ipv4Cidr, StackResources};\nuse embassy_net_tuntap::TunTapDevice;\nuse heapless::Vec;\nuse log::*;\nuse rand_core::{OsRng, TryRngCore};\nuse static_cell::StaticCell;\n\n#[derive(Parser)]\n#[clap(version = \"1.0\")]\nstruct Opts {\n    /// TAP device name\n    #[clap(long, default_value = \"tap0\")]\n    tap: String,\n    /// use a static IP instead of DHCP\n    #[clap(long)]\n    static_ip: bool,\n}\n\n#[embassy_executor::task]\nasync fn net_task(mut runner: embassy_net::Runner<'static, TunTapDevice>) -> ! {\n    runner.run().await\n}\n\n#[embassy_executor::task]\nasync fn main_task(spawner: Spawner) {\n    let opts: Opts = Opts::parse();\n\n    // Init network device\n    let device = TunTapDevice::new(&opts.tap).unwrap();\n\n    // Choose between dhcp or static ip\n    let config = if opts.static_ip {\n        Config::ipv4_static(embassy_net::StaticConfigV4 {\n            address: Ipv4Cidr::new(Ipv4Address::new(192, 168, 69, 1), 24),\n            dns_servers: Vec::from_slice(&[Ipv4Address::new(8, 8, 4, 4).into(), Ipv4Address::new(8, 8, 8, 8).into()])\n                .unwrap(),\n            gateway: Some(Ipv4Address::new(192, 168, 69, 100)),\n        })\n    } else {\n        Config::dhcpv4(Default::default())\n    };\n\n    // Generate random seed\n    let mut seed = [0; 8];\n    OsRng.try_fill_bytes(&mut seed).unwrap();\n    let seed = u64::from_le_bytes(seed);\n\n    // Init network stack\n    static RESOURCES: StaticCell<StackResources<3>> = StaticCell::new();\n    let (stack, runner) = embassy_net::new(device, config, RESOURCES.init(StackResources::new()), seed);\n\n    // Launch network task\n    spawner.spawn(net_task(runner).unwrap());\n\n    let host = \"example.com\";\n    info!(\"querying host {:?}...\", host);\n    match stack.dns_query(host, DnsQueryType::A).await {\n        Ok(r) => {\n            info!(\"query response: {:?}\", r);\n        }\n        Err(e) => {\n            warn!(\"query error: {:?}\", e);\n        }\n    };\n}\n\nstatic EXECUTOR: StaticCell<Executor> = StaticCell::new();\n\nfn main() {\n    env_logger::builder()\n        .filter_level(log::LevelFilter::Debug)\n        .filter_module(\"async_io\", log::LevelFilter::Info)\n        .format_timestamp_nanos()\n        .init();\n\n    let executor = EXECUTOR.init(Executor::new());\n    executor.run(|spawner| {\n        spawner.spawn(main_task(spawner).unwrap());\n    });\n}\n"
  },
  {
    "path": "examples/std/src/bin/net_ppp.rs",
    "content": "//! Testing against pppd:\n//!\n//!     echo myuser $(hostname) mypass 192.168.7.10 >> /etc/ppp/pap-secrets\n//!     socat -v -x PTY,link=pty1,rawer PTY,link=pty2,rawer\n//!     sudo pppd $PWD/pty1 115200 192.168.7.1: ms-dns 8.8.4.4 ms-dns 8.8.8.8 nodetach debug local persist silent noproxyarp\n//!     RUST_LOG=trace cargo run --bin net_ppp -- --device pty2\n//!     ping 192.168.7.10\n//!     nc 192.168.7.10 1234\n\n#![allow(async_fn_in_trait)]\n\n#[path = \"../serial_port.rs\"]\nmod serial_port;\n\nuse async_io::Async;\nuse clap::Parser;\nuse embassy_executor::{Executor, Spawner};\nuse embassy_net::tcp::TcpSocket;\nuse embassy_net::{Config, ConfigV4, Ipv4Cidr, Stack, StackResources};\nuse embassy_net_ppp::Runner;\nuse embedded_io_async::Write;\nuse futures::io::BufReader;\nuse heapless::Vec;\nuse log::*;\nuse nix::sys::termios;\nuse rand_core::{OsRng, TryRngCore};\nuse static_cell::StaticCell;\n\nuse crate::serial_port::SerialPort;\n\n#[derive(Parser)]\n#[clap(version = \"1.0\")]\nstruct Opts {\n    /// Serial port device name\n    #[clap(short, long)]\n    device: String,\n}\n\n#[embassy_executor::task]\nasync fn net_task(mut runner: embassy_net::Runner<'static, embassy_net_ppp::Device<'static>>) -> ! {\n    runner.run().await\n}\n\n#[embassy_executor::task]\nasync fn ppp_task(stack: Stack<'static>, mut runner: Runner<'static>, port: SerialPort) -> ! {\n    let port = Async::new(port).unwrap();\n    let port = BufReader::new(port);\n    let port = embedded_io_adapters::futures_03::FromFutures::new(port);\n\n    let config = embassy_net_ppp::Config {\n        username: b\"myuser\",\n        password: b\"mypass\",\n    };\n\n    let r = runner\n        .run(port, config, |ipv4| {\n            let Some(addr) = ipv4.address else {\n                warn!(\"PPP did not provide an IP address.\");\n                return;\n            };\n            let mut dns_servers = Vec::new();\n            for s in ipv4.dns_servers.iter().flatten() {\n                let _ = dns_servers.push(*s);\n            }\n            let config = ConfigV4::Static(embassy_net::StaticConfigV4 {\n                address: Ipv4Cidr::new(addr, 0),\n                gateway: None,\n                dns_servers,\n            });\n            stack.set_config_v4(config);\n        })\n        .await;\n    match r {\n        Err(e) => panic!(\"{:?}\", e),\n    }\n}\n\n#[embassy_executor::task]\nasync fn main_task(spawner: Spawner) {\n    let opts: Opts = Opts::parse();\n\n    // Open serial port\n    let baudrate = termios::BaudRate::B115200;\n    let port = SerialPort::new(opts.device.as_str(), baudrate).unwrap();\n\n    // Init network device\n    static STATE: StaticCell<embassy_net_ppp::State<4, 4>> = StaticCell::new();\n    let state = STATE.init(embassy_net_ppp::State::<4, 4>::new());\n    let (device, runner) = embassy_net_ppp::new(state);\n\n    // Generate random seed\n    let mut seed = [0; 8];\n    OsRng.try_fill_bytes(&mut seed).unwrap();\n    let seed = u64::from_le_bytes(seed);\n\n    // Init network stack\n    static RESOURCES: StaticCell<StackResources<3>> = StaticCell::new();\n    let (stack, net_runner) = embassy_net::new(\n        device,\n        Config::default(), // don't configure IP yet\n        RESOURCES.init(StackResources::new()),\n        seed,\n    );\n\n    // Launch network task\n    spawner.spawn(net_task(net_runner).unwrap());\n    spawner.spawn(ppp_task(stack, runner, port).unwrap());\n\n    // Then we can use it!\n    let mut rx_buffer = [0; 4096];\n    let mut tx_buffer = [0; 4096];\n    let mut buf = [0; 4096];\n\n    loop {\n        let mut socket = TcpSocket::new(stack, &mut rx_buffer, &mut tx_buffer);\n        socket.set_timeout(Some(embassy_time::Duration::from_secs(10)));\n\n        info!(\"Listening on TCP:1234...\");\n        if let Err(e) = socket.accept(1234).await {\n            warn!(\"accept error: {:?}\", e);\n            continue;\n        }\n\n        info!(\"Received connection from {:?}\", socket.remote_endpoint());\n\n        loop {\n            let n = match socket.read(&mut buf).await {\n                Ok(0) => {\n                    warn!(\"read EOF\");\n                    break;\n                }\n                Ok(n) => n,\n                Err(e) => {\n                    warn!(\"read error: {:?}\", e);\n                    break;\n                }\n            };\n\n            info!(\"rxd {:02x?}\", &buf[..n]);\n\n            match socket.write_all(&buf[..n]).await {\n                Ok(()) => {}\n                Err(e) => {\n                    warn!(\"write error: {:?}\", e);\n                    break;\n                }\n            };\n        }\n    }\n}\n\nstatic EXECUTOR: StaticCell<Executor> = StaticCell::new();\n\nfn main() {\n    env_logger::builder()\n        .filter_level(log::LevelFilter::Trace)\n        .filter_module(\"polling\", log::LevelFilter::Info)\n        .filter_module(\"async_io\", log::LevelFilter::Info)\n        .format_timestamp_nanos()\n        .init();\n\n    let executor = EXECUTOR.init(Executor::new());\n    executor.run(|spawner| {\n        spawner.spawn(main_task(spawner).unwrap());\n    });\n}\n"
  },
  {
    "path": "examples/std/src/bin/net_udp.rs",
    "content": "use clap::Parser;\nuse embassy_executor::{Executor, Spawner};\nuse embassy_net::udp::{PacketMetadata, UdpSocket};\nuse embassy_net::{Config, Ipv4Address, Ipv4Cidr, StackResources};\nuse embassy_net_tuntap::TunTapDevice;\nuse heapless::Vec;\nuse log::*;\nuse rand_core::{OsRng, TryRngCore};\nuse static_cell::StaticCell;\n\n#[derive(Parser)]\n#[clap(version = \"1.0\")]\nstruct Opts {\n    /// TAP device name\n    #[clap(long, default_value = \"tap0\")]\n    tap: String,\n    /// use a static IP instead of DHCP\n    #[clap(long)]\n    static_ip: bool,\n}\n\n#[embassy_executor::task]\nasync fn net_task(mut runner: embassy_net::Runner<'static, TunTapDevice>) -> ! {\n    runner.run().await\n}\n\n#[embassy_executor::task]\nasync fn main_task(spawner: Spawner) {\n    let opts: Opts = Opts::parse();\n\n    // Init network device\n    let device = TunTapDevice::new(&opts.tap).unwrap();\n\n    // Choose between dhcp or static ip\n    let config = if opts.static_ip {\n        Config::ipv4_static(embassy_net::StaticConfigV4 {\n            address: Ipv4Cidr::new(Ipv4Address::new(192, 168, 69, 2), 24),\n            dns_servers: Vec::new(),\n            gateway: Some(Ipv4Address::new(192, 168, 69, 1)),\n        })\n    } else {\n        Config::dhcpv4(Default::default())\n    };\n\n    // Generate random seed\n    let mut seed = [0; 8];\n    OsRng.try_fill_bytes(&mut seed).unwrap();\n    let seed = u64::from_le_bytes(seed);\n\n    // Init network stack\n    static RESOURCES: StaticCell<StackResources<3>> = StaticCell::new();\n    let (stack, runner) = embassy_net::new(device, config, RESOURCES.init(StackResources::new()), seed);\n\n    // Launch network task\n    spawner.spawn(net_task(runner).unwrap());\n\n    // Then we can use it!\n    let mut rx_meta = [PacketMetadata::EMPTY; 16];\n    let mut rx_buffer = [0; 4096];\n    let mut tx_meta = [PacketMetadata::EMPTY; 16];\n    let mut tx_buffer = [0; 4096];\n    let mut buf = [0; 4096];\n\n    let mut socket = UdpSocket::new(stack, &mut rx_meta, &mut rx_buffer, &mut tx_meta, &mut tx_buffer);\n    socket.bind(9400).unwrap();\n\n    loop {\n        let (n, ep) = socket.recv_from(&mut buf).await.unwrap();\n        if let Ok(s) = core::str::from_utf8(&buf[..n]) {\n            info!(\"ECHO (to {}): {}\", ep, s);\n        } else {\n            info!(\"ECHO (to {}): bytearray len {}\", ep, n);\n        }\n        socket.send_to(&buf[..n], ep).await.unwrap();\n    }\n}\n\nstatic EXECUTOR: StaticCell<Executor> = StaticCell::new();\n\nfn main() {\n    env_logger::builder()\n        .filter_level(log::LevelFilter::Debug)\n        .filter_module(\"async_io\", log::LevelFilter::Info)\n        .format_timestamp_nanos()\n        .init();\n\n    let executor = EXECUTOR.init(Executor::new());\n    executor.run(|spawner| {\n        spawner.spawn(main_task(spawner).unwrap());\n    });\n}\n"
  },
  {
    "path": "examples/std/src/bin/serial.rs",
    "content": "#[path = \"../serial_port.rs\"]\nmod serial_port;\n\nuse async_io::Async;\nuse embassy_executor::Executor;\nuse embassy_time as _;\nuse embedded_io_async::Read;\nuse log::*;\nuse nix::sys::termios;\nuse static_cell::StaticCell;\n\nuse self::serial_port::SerialPort;\n\n#[embassy_executor::task]\nasync fn run() {\n    // Open the serial port.\n    let baudrate = termios::BaudRate::B115200;\n    let port = SerialPort::new(\"/dev/ttyACM0\", baudrate).unwrap();\n    //let port = Spy::new(port);\n\n    // Use async_io's reactor for async IO.\n    // This demonstrates how embassy's executor can drive futures from another IO library.\n    // Essentially, async_io::Async converts from AsRawFd+Read+Write to futures's AsyncRead+AsyncWrite\n    let port = Async::new(port).unwrap();\n\n    // We can then use FromStdIo to convert from futures's AsyncRead+AsyncWrite\n    // to embedded_io's async Read+Write.\n    //\n    // This is not really needed, you could write the code below using futures::io directly.\n    // It's useful if you want to have portable code across embedded and std.\n    let mut port = embedded_io_adapters::futures_03::FromFutures::new(port);\n\n    info!(\"Serial opened!\");\n\n    loop {\n        let mut buf = [0u8; 256];\n        let n = port.read(&mut buf).await.unwrap();\n        info!(\"read {:?}\", &buf[..n]);\n    }\n}\n\nstatic EXECUTOR: StaticCell<Executor> = StaticCell::new();\n\nfn main() {\n    env_logger::builder()\n        .filter_level(log::LevelFilter::Debug)\n        .filter_module(\"async_io\", log::LevelFilter::Info)\n        .format_timestamp_nanos()\n        .init();\n\n    let executor = EXECUTOR.init(Executor::new());\n    executor.run(|spawner| {\n        spawner.spawn(run().unwrap());\n    });\n}\n"
  },
  {
    "path": "examples/std/src/bin/tcp_accept.rs",
    "content": "use clap::Parser;\nuse embassy_executor::{Executor, Spawner};\nuse embassy_net::tcp::TcpSocket;\nuse embassy_net::{Config, Ipv4Address, Ipv4Cidr, StackResources};\nuse embassy_net_tuntap::TunTapDevice;\nuse embassy_time::{Duration, Timer};\nuse embedded_io_async::Write as _;\nuse heapless::Vec;\nuse log::*;\nuse rand_core::{OsRng, TryRngCore};\nuse static_cell::StaticCell;\n\n#[derive(Parser)]\n#[clap(version = \"1.0\")]\nstruct Opts {\n    /// TAP device name\n    #[clap(long, default_value = \"tap0\")]\n    tap: String,\n    /// use a static IP instead of DHCP\n    #[clap(long)]\n    static_ip: bool,\n}\n\n#[embassy_executor::task]\nasync fn net_task(mut runner: embassy_net::Runner<'static, TunTapDevice>) -> ! {\n    runner.run().await\n}\n\n#[embassy_executor::task]\nasync fn main_task(spawner: Spawner) {\n    let opts: Opts = Opts::parse();\n\n    // Init network device\n    let device = TunTapDevice::new(&opts.tap).unwrap();\n\n    // Choose between dhcp or static ip\n    let config = if opts.static_ip {\n        Config::ipv4_static(embassy_net::StaticConfigV4 {\n            address: Ipv4Cidr::new(Ipv4Address::new(192, 168, 69, 2), 24),\n            dns_servers: Vec::new(),\n            gateway: Some(Ipv4Address::new(192, 168, 69, 1)),\n        })\n    } else {\n        Config::dhcpv4(Default::default())\n    };\n\n    // Generate random seed\n    let mut seed = [0; 8];\n    OsRng.try_fill_bytes(&mut seed).unwrap();\n    let seed = u64::from_le_bytes(seed);\n\n    // Init network stack\n    static RESOURCES: StaticCell<StackResources<3>> = StaticCell::new();\n    let (stack, runner) = embassy_net::new(device, config, RESOURCES.init(StackResources::new()), seed);\n\n    // Launch network task\n    spawner.spawn(net_task(runner).unwrap());\n\n    // Then we can use it!\n    let mut rx_buffer = [0; 4096];\n    let mut tx_buffer = [0; 4096];\n\n    loop {\n        let mut socket = TcpSocket::new(stack, &mut rx_buffer, &mut tx_buffer);\n        socket.set_timeout(Some(Duration::from_secs(10)));\n        info!(\"Listening on TCP:9999...\");\n        if let Err(_) = socket.accept(9999).await {\n            warn!(\"accept error\");\n            continue;\n        }\n\n        info!(\"Accepted a connection\");\n\n        // Write some quick output\n        for i in 1..=5 {\n            let s = format!(\"{}!  \", i);\n            let r = socket.write_all(s.as_bytes()).await;\n            if let Err(e) = r {\n                warn!(\"write error: {:?}\", e);\n                return;\n            }\n\n            Timer::after_millis(500).await;\n        }\n        info!(\"Closing the connection\");\n        socket.abort();\n        info!(\"Flushing the RST out...\");\n        _ = socket.flush().await;\n        info!(\"Finished with the socket\");\n    }\n}\n\nstatic EXECUTOR: StaticCell<Executor> = StaticCell::new();\n\nfn main() {\n    env_logger::builder()\n        .filter_level(log::LevelFilter::Debug)\n        .filter_module(\"async_io\", log::LevelFilter::Info)\n        .format_timestamp_nanos()\n        .init();\n\n    let executor = EXECUTOR.init(Executor::new());\n    executor.run(|spawner| {\n        spawner.spawn(main_task(spawner).unwrap());\n    });\n}\n"
  },
  {
    "path": "examples/std/src/bin/tick.rs",
    "content": "use embassy_executor::Spawner;\nuse embassy_time::Timer;\nuse log::*;\n\n#[embassy_executor::task]\nasync fn run() {\n    loop {\n        info!(\"tick\");\n        Timer::after_secs(1).await;\n    }\n}\n\n#[embassy_executor::main]\nasync fn main(spawner: Spawner) {\n    env_logger::builder()\n        .filter_level(log::LevelFilter::Debug)\n        .format_timestamp_nanos()\n        .init();\n\n    spawner.spawn(run().unwrap());\n}\n"
  },
  {
    "path": "examples/std/src/bin/tick_cancel.rs",
    "content": "use std::sync::atomic::{AtomicBool, Ordering};\nuse std::thread;\nuse std::time::Duration;\n\nuse embassy_executor::Executor;\nuse embassy_time::Timer;\nuse log::*;\nuse static_cell::StaticCell;\n\n#[embassy_executor::task]\nasync fn run() {\n    loop {\n        info!(\"tick\");\n        Timer::after_secs(1).await;\n    }\n}\n\nstatic DONE: StaticCell<AtomicBool> = StaticCell::new();\nstatic EXECUTOR: StaticCell<Executor> = StaticCell::new();\n\nfn main() {\n    env_logger::builder()\n        .filter_level(log::LevelFilter::Debug)\n        .format_timestamp_nanos()\n        .init();\n\n    let done = DONE.init(AtomicBool::new(false));\n    let done_cb = || done.load(Ordering::Relaxed);\n\n    let server_thread = thread::spawn(move || {\n        let executor = EXECUTOR.init(Executor::new());\n        executor.run_until(\n            |spawner| {\n                spawner.spawn(run().unwrap());\n            },\n            done_cb,\n        );\n        info!(\"Executor finished\");\n    });\n\n    thread::sleep(Duration::from_secs(5));\n\n    info!(\"Cancelling executor\");\n    done.store(true, Ordering::Relaxed);\n\n    server_thread.join().unwrap();\n}\n"
  },
  {
    "path": "examples/std/src/serial_port.rs",
    "content": "use std::io;\nuse std::os::unix::io::{AsRawFd, RawFd};\n\nuse nix::errno::Errno;\nuse nix::fcntl::OFlag;\nuse nix::sys::termios;\n\npub struct SerialPort {\n    fd: RawFd,\n}\n\nimpl SerialPort {\n    pub fn new<P: ?Sized + nix::NixPath>(path: &P, baudrate: termios::BaudRate) -> io::Result<Self> {\n        let fd = nix::fcntl::open(\n            path,\n            OFlag::O_RDWR | OFlag::O_NOCTTY | OFlag::O_NONBLOCK,\n            nix::sys::stat::Mode::empty(),\n        )\n        .map_err(to_io_error)?;\n\n        let mut cfg = termios::tcgetattr(fd).map_err(to_io_error)?;\n        cfg.input_flags = termios::InputFlags::empty();\n        cfg.output_flags = termios::OutputFlags::empty();\n        cfg.control_flags = termios::ControlFlags::empty();\n        cfg.local_flags = termios::LocalFlags::empty();\n        termios::cfmakeraw(&mut cfg);\n        cfg.input_flags |= termios::InputFlags::IGNBRK;\n        cfg.control_flags |= termios::ControlFlags::CREAD;\n        //cfg.control_flags |= termios::ControlFlags::CRTSCTS;\n        termios::cfsetospeed(&mut cfg, baudrate).map_err(to_io_error)?;\n        termios::cfsetispeed(&mut cfg, baudrate).map_err(to_io_error)?;\n        termios::cfsetspeed(&mut cfg, baudrate).map_err(to_io_error)?;\n        // Set VMIN = 1 to block until at least one character is received.\n        cfg.control_chars[termios::SpecialCharacterIndices::VMIN as usize] = 1;\n        termios::tcsetattr(fd, termios::SetArg::TCSANOW, &cfg).map_err(to_io_error)?;\n        termios::tcflush(fd, termios::FlushArg::TCIOFLUSH).map_err(to_io_error)?;\n\n        Ok(Self { fd })\n    }\n}\n\nimpl AsRawFd for SerialPort {\n    fn as_raw_fd(&self) -> RawFd {\n        self.fd\n    }\n}\n\nimpl io::Read for SerialPort {\n    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {\n        nix::unistd::read(self.fd, buf).map_err(to_io_error)\n    }\n}\n\nimpl io::Write for SerialPort {\n    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {\n        nix::unistd::write(self.fd, buf).map_err(to_io_error)\n    }\n\n    fn flush(&mut self) -> io::Result<()> {\n        Ok(())\n    }\n}\n\nfn to_io_error(e: Errno) -> io::Error {\n    e.into()\n}\n"
  },
  {
    "path": "examples/std/tap.sh",
    "content": "ip tuntap add name tap99 mode tap user $SUDO_USER\nip link set tap99 up\nip addr add 192.168.69.100/24 dev tap99\nip -6 addr add fe80::100/64 dev tap99\nip -6 addr add fdaa::100/64 dev tap99\nip -6 route add fe80::/64 dev tap99\nip -6 route add fdaa::/64 dev tap99\n"
  },
  {
    "path": "examples/stm32c0/.cargo/config.toml",
    "content": "[target.'cfg(all(target_arch = \"arm\", target_os = \"none\"))']\n# replace STM32G071C8Rx with your chip as listed in `probe-rs chip list`\nrunner = \"probe-rs run --speed 100 --chip STM32c031c6tx\"\n\n[build]\ntarget = \"thumbv6m-none-eabi\"\n\n[env]\nDEFMT_LOG = \"trace\"\n"
  },
  {
    "path": "examples/stm32c0/Cargo.toml",
    "content": "[package]\nedition = \"2024\"\nname = \"embassy-stm32c0-examples\"\nversion = \"0.1.0\"\nlicense = \"MIT OR Apache-2.0\"\npublish = false\n\n[dependencies]\n# Change stm32c031c6 to your chip name, if necessary.\nembassy-stm32 = { version = \"0.6.0\", path = \"../../embassy-stm32\", features = [ \"defmt\", \"time-driver-any\", \"stm32c031c6\", \"memory-x\", \"unstable-pac\", \"exti\", \"chrono\"]  }\nembassy-sync = { version = \"0.8.0\", path = \"../../embassy-sync\", features = [\"defmt\"] }\nembassy-executor = { version = \"0.10.0\", path = \"../../embassy-executor\", features = [\"platform-cortex-m\", \"executor-thread\", \"defmt\"] }\nembassy-time = { version = \"0.5.1\", path = \"../../embassy-time\", features = [\"defmt\", \"defmt-timestamp-uptime\", \"tick-hz-32_768\"] }\n\ndefmt = \"1.0.1\"\ndefmt-rtt = \"1.0.0\"\n\ncortex-m = { version = \"0.7.6\", features = [\"critical-section-single-core\"] }\ncortex-m-rt = \"0.7.0\"\nembedded-hal = \"0.2.6\"\npanic-probe = { version = \"1.0.0\", features = [\"print-defmt\"] }\nheapless = { version = \"0.9\", default-features = false }\nchrono = { version = \"^0.4\", default-features = false}\n\n[profile.release]\ndebug = 2\n\n[package.metadata.embassy]\nbuild = [\n  { target = \"thumbv6m-none-eabi\", artifact-dir = \"out/examples/stm32c0\" }\n]\n"
  },
  {
    "path": "examples/stm32c0/build.rs",
    "content": "fn main() {\n    println!(\"cargo:rustc-link-arg-bins=--nmagic\");\n    println!(\"cargo:rustc-link-arg-bins=-Tlink.x\");\n    println!(\"cargo:rustc-link-arg-bins=-Tdefmt.x\");\n}\n"
  },
  {
    "path": "examples/stm32c0/src/bin/adc.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_stm32::adc::{Adc, AdcChannel, AnyAdcChannel, Resolution, SampleTime};\nuse embassy_stm32::peripherals::{ADC1, DMA1_CH1};\nuse embassy_stm32::{bind_interrupts, dma};\nuse embassy_time::Timer;\nuse {defmt_rtt as _, panic_probe as _};\n\nbind_interrupts!(struct Irqs {\n    DMA1_CHANNEL1 => dma::InterruptHandler<DMA1_CH1>;\n});\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let config = Default::default();\n    let p = embassy_stm32::init(config);\n\n    info!(\"ADC STM32C0 example.\");\n\n    // We need to set certain sample time to be able to read temp sensor.\n    let mut adc = Adc::new(p.ADC1, Resolution::BITS12);\n    let mut temp = adc.enable_temperature().degrade_adc();\n    let mut vref = adc.enable_vrefint().degrade_adc();\n    let mut pin0 = p.PA0.degrade_adc();\n\n    let mut dma = p.DMA1_CH1;\n    let mut read_buffer: [u16; 3] = [0; 3];\n\n    loop {\n        info!(\"============================\");\n        let blocking_temp = adc.blocking_read(&mut temp, SampleTime::CYCLES12_5);\n        let blocking_vref = adc.blocking_read(&mut vref, SampleTime::CYCLES12_5);\n        let blocing_pin0 = adc.blocking_read(&mut pin0, SampleTime::CYCLES12_5);\n        info!(\n            \"Blocking ADC read: vref = {}, temp = {}, pin0 = {}.\",\n            blocking_vref, blocking_temp, blocing_pin0\n        );\n\n        let channels_sequence: [(&mut AnyAdcChannel<ADC1>, SampleTime); 3] = [\n            (&mut vref, SampleTime::CYCLES12_5),\n            (&mut temp, SampleTime::CYCLES12_5),\n            (&mut pin0, SampleTime::CYCLES12_5),\n        ];\n        adc.read(dma.reborrow(), Irqs, channels_sequence.into_iter(), &mut read_buffer)\n            .await;\n        // Values are ordered according to hardware ADC channel number!\n        info!(\n            \"DMA ADC read in set: vref = {}, temp = {}, pin0 = {}.\",\n            read_buffer[0], read_buffer[1], read_buffer[2]\n        );\n\n        Timer::after_millis(2000).await;\n    }\n}\n"
  },
  {
    "path": "examples/stm32c0/src/bin/adc_ring_buffered_timer.rs",
    "content": "//! ADC with DMA ring buffer triggered by timer\n//!\n//! This example demonstrates periodic ADC sampling using a timer trigger and DMA ring buffer.\n//! The timer generates regular ADC conversions at a controlled rate, and DMA automatically\n//! stores the samples in a circular buffer for efficient data acquisition.\n//!\n//! Hardware setup:\n//! - PA0: ADC input (connect to analog signal)\n//! - Internal VrefInt and Temperature sensors also monitored\n\n#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_stm32::adc::{Adc, AdcChannel as _, Resolution, SampleTime};\nuse embassy_stm32::pac::adc::vals::Exten;\nuse embassy_stm32::peripherals::DMA1_CH1;\nuse embassy_stm32::time::Hertz;\nuse embassy_stm32::timer::complementary_pwm::{ComplementaryPwm, Mms2};\nuse embassy_stm32::timer::low_level::CountingMode;\nuse embassy_stm32::triggers::TIM1_TRGO2;\nuse embassy_stm32::{bind_interrupts, dma};\nuse {defmt_rtt as _, panic_probe as _};\n\nbind_interrupts!(struct Irqs {\n    DMA1_CHANNEL1 => dma::InterruptHandler<DMA1_CH1>;\n});\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let config = Default::default();\n    let p = embassy_stm32::init(config);\n\n    info!(\"ADC Ring Buffer with Timer Trigger example for STM32C0\");\n\n    // Configure TIM1 to generate TRGO2 events at 10 Hz\n    // This will trigger ADC conversions periodically\n    let tim1 = p.TIM1;\n    let mut pwm = ComplementaryPwm::new(\n        tim1,\n        None,          // CH1\n        None,          // CH1N\n        None,          // CH2\n        None,          // CH2N\n        None,          // CH3\n        None,          // CH3N\n        None,          // CH4\n        None,          // CH4N\n        Hertz::hz(10), // 10 Hz trigger rate\n        CountingMode::EdgeAlignedUp,\n    );\n\n    // Configure TRGO2 to trigger on update event\n    pwm.set_mms2(Mms2::UPDATE);\n\n    // Configure ADC with DMA ring buffer\n    let adc = Adc::new(p.ADC1, Resolution::BITS12);\n\n    // Setup channels to measure\n    let vrefint_channel = adc.enable_vrefint().degrade_adc();\n    let temp_channel = adc.enable_temperature().degrade_adc();\n    let pa0 = p.PA0.degrade_adc();\n\n    let sequence = [\n        (vrefint_channel, SampleTime::CYCLES12_5),\n        (temp_channel, SampleTime::CYCLES12_5),\n        (pa0, SampleTime::CYCLES12_5),\n    ]\n    .into_iter();\n\n    // DMA buffer - using double size allows reading one half while DMA fills the other\n    // Buffer holds continuous samples\n    let mut dma_buf = [0u16; 12]; // 4 complete samples (3 channels each)\n\n    // Create ring-buffered ADC with timer trigger\n    let mut ring_buffered_adc = adc.into_ring_buffered(\n        p.DMA1_CH1,\n        &mut dma_buf,\n        Irqs,\n        sequence,\n        TIM1_TRGO2,         // Timer 1 TRGO2 as trigger source\n        Exten::RISING_EDGE, // Trigger on rising edge (can also use FALLING_EDGE or BOTH_EDGES)\n    );\n\n    // Start ADC conversions and DMA transfer\n    ring_buffered_adc.start();\n\n    info!(\"ADC configured with TIM1 trigger at 10 Hz\");\n    info!(\"Reading 3 channels: VrefInt, Temperature, PA0\");\n\n    // Buffer to read samples - must be half the size of dma_buf\n    let mut data = [0u16; 6]; // 2 complete samples\n\n    loop {\n        match ring_buffered_adc.read(&mut data).await {\n            Ok(remaining) => {\n                // Data contains interleaved samples: [vref0, temp0, pa0_0, vref1, temp1, pa0_1]\n                info!(\"Sample 1: VrefInt={}, Temp={}, PA0={}\", data[0], data[1], data[2]);\n                info!(\"Sample 2: VrefInt={}, Temp={}, PA0={}\", data[3], data[4], data[5]);\n                info!(\"Remaining samples in buffer: {}\", remaining);\n\n                // Convert VrefInt to millivolts (example calculation)\n                let vdda_mv = (3300 * 1210) / data[0] as u32; // Assuming VREFINT_CAL = 1210\n                info!(\"Estimated VDDA: {} mV\", vdda_mv);\n            }\n            Err(e) => {\n                error!(\"DMA error: {:?}\", e);\n                ring_buffered_adc.clear();\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "examples/stm32c0/src/bin/blinky.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_stm32::gpio::{Level, Output, Speed};\nuse embassy_time::Timer;\nuse {defmt_rtt as _, panic_probe as _};\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let p = embassy_stm32::init(Default::default());\n    info!(\"Hello World!\");\n\n    let mut led = Output::new(p.PA5, Level::High, Speed::Low);\n\n    loop {\n        info!(\"high\");\n        led.set_high();\n        Timer::after_millis(300).await;\n\n        info!(\"low\");\n        led.set_low();\n        Timer::after_millis(300).await;\n    }\n}\n"
  },
  {
    "path": "examples/stm32c0/src/bin/button.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse cortex_m_rt::entry;\nuse defmt::*;\nuse embassy_stm32::gpio::{Input, Pull};\nuse {defmt_rtt as _, panic_probe as _};\n\n#[entry]\nfn main() -> ! {\n    info!(\"Hello World!\");\n\n    let p = embassy_stm32::init(Default::default());\n\n    let button = Input::new(p.PC13, Pull::Up);\n\n    loop {\n        if button.is_high() {\n            info!(\"high\");\n        } else {\n            info!(\"low\");\n        }\n    }\n}\n"
  },
  {
    "path": "examples/stm32c0/src/bin/button_exti.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_stm32::exti::{self, ExtiInput};\nuse embassy_stm32::gpio::Pull;\nuse embassy_stm32::{bind_interrupts, interrupt};\nuse {defmt_rtt as _, panic_probe as _};\n\nbind_interrupts!(\n    pub struct Irqs{\n        EXTI4_15 => exti::InterruptHandler<interrupt::typelevel::EXTI4_15>;\n});\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let p = embassy_stm32::init(Default::default());\n    info!(\"Hello World!\");\n\n    let mut button = ExtiInput::new(p.PC13, p.EXTI13, Pull::Up, Irqs);\n\n    info!(\"Press the USER button...\");\n\n    loop {\n        button.wait_for_falling_edge().await;\n        info!(\"Pressed!\");\n        button.wait_for_rising_edge().await;\n        info!(\"Released!\");\n    }\n}\n"
  },
  {
    "path": "examples/stm32c0/src/bin/rtc.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse chrono::{NaiveDate, NaiveDateTime};\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_stm32::Config;\nuse embassy_stm32::rtc::{Rtc, RtcConfig};\nuse embassy_time::Timer;\nuse {defmt_rtt as _, panic_probe as _};\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let config = Config::default();\n    let p = embassy_stm32::init(config);\n\n    info!(\"Hello World!\");\n\n    let now = NaiveDate::from_ymd_opt(2020, 5, 15)\n        .unwrap()\n        .and_hms_opt(10, 30, 15)\n        .unwrap();\n\n    let (mut rtc, time_provider) = Rtc::new(p.RTC, RtcConfig::default());\n\n    rtc.set_datetime(now.into()).expect(\"datetime not set\");\n\n    loop {\n        let now: NaiveDateTime = time_provider.now().unwrap().into();\n\n        info!(\"{}\", now.and_utc().timestamp());\n\n        Timer::after_millis(1000).await;\n    }\n}\n"
  },
  {
    "path": "examples/stm32f0/.cargo/config.toml",
    "content": "[target.thumbv6m-none-eabi]\nrunner = 'probe-rs run --chip STM32F091RCTX'\n\n[build]\ntarget = \"thumbv6m-none-eabi\"\n\n[env]\nDEFMT_LOG = \"trace\"\n"
  },
  {
    "path": "examples/stm32f0/Cargo.toml",
    "content": "[package]\nname = \"embassy-stm32f0-examples\"\nversion = \"0.1.0\"\nedition = \"2024\"\nlicense = \"MIT OR Apache-2.0\"\npublish = false\n\n[dependencies]\n# Change stm32f091rc to your chip name, if necessary.\nembassy-stm32 = { version = \"0.6.0\", path = \"../../embassy-stm32\", features = [ \"defmt\", \"memory-x\", \"stm32f091rc\", \"time-driver-tim2\", \"exti\", \"unstable-pac\"] }\ncortex-m = { version = \"0.7.6\", features = [\"inline-asm\", \"critical-section-single-core\"] }\ncortex-m-rt = \"0.7.0\"\ndefmt = \"1.0.1\"\ndefmt-rtt = \"1.0.0\"\npanic-probe = { version = \"1.0.0\", features = [\"print-defmt\"] }\nembassy-sync = { version = \"0.8.0\", path = \"../../embassy-sync\", features = [\"defmt\"] }\nembassy-executor = { version = \"0.10.0\", path = \"../../embassy-executor\", features = [\"platform-cortex-m\", \"executor-thread\", \"executor-interrupt\", \"defmt\"] }\nembassy-time = { version = \"0.5.1\", path = \"../../embassy-time\", features = [\"defmt\", \"defmt-timestamp-uptime\", \"tick-hz-32_768\"] }\nembedded-hal-1 = { package = \"embedded-hal\", version = \"1.0\" }\nstatic_cell = \"2\"\nportable-atomic = { version = \"1.5\", features = [\"unsafe-assume-single-core\"] }\n\n[profile.release]\ndebug = 2\n\n[package.metadata.embassy]\nbuild = [\n  { target = \"thumbv6m-none-eabi\", artifact-dir = \"out/examples/stm32f0\" }\n]\n"
  },
  {
    "path": "examples/stm32f0/build.rs",
    "content": "fn main() {\n    println!(\"cargo:rustc-link-arg-bins=--nmagic\");\n    println!(\"cargo:rustc-link-arg-bins=-Tlink.x\");\n    println!(\"cargo:rustc-link-arg-bins=-Tdefmt.x\");\n}\n"
  },
  {
    "path": "examples/stm32f0/src/bin/adc-watchdog.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_stm32::adc::{self, Adc, SampleTime, WatchdogChannels};\nuse embassy_stm32::bind_interrupts;\nuse embassy_stm32::peripherals::ADC1;\nuse {defmt_rtt as _, panic_probe as _};\n\nbind_interrupts!(struct Irqs {\n    ADC1_COMP => adc::InterruptHandler<ADC1>;\n});\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let p = embassy_stm32::init(Default::default());\n    info!(\"ADC watchdog example\");\n\n    let mut adc = Adc::new(p.ADC1, Irqs);\n    let pin = p.PC1;\n\n    loop {\n        // Wait for pin to go high\n        adc.init_watchdog(WatchdogChannels::from_channel(&pin), 0, 0x07F);\n        let v_high = adc.monitor_watchdog(SampleTime::CYCLES13_5).await;\n        info!(\"ADC sample is high {}\", v_high);\n\n        // Wait for pin to go low\n        adc.init_watchdog(WatchdogChannels::from_channel(&pin), 0x01f, 0xFFF);\n        let v_low = adc.monitor_watchdog(SampleTime::CYCLES13_5).await;\n        info!(\"ADC sample is low {}\", v_low);\n    }\n}\n"
  },
  {
    "path": "examples/stm32f0/src/bin/adc.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_stm32::adc::{Adc, SampleTime};\nuse embassy_stm32::peripherals::ADC1;\nuse embassy_stm32::{adc, bind_interrupts};\nuse embassy_time::Timer;\nuse {defmt_rtt as _, panic_probe as _};\n\nbind_interrupts!(struct Irqs {\n    ADC1_COMP => adc::InterruptHandler<ADC1>;\n});\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let p = embassy_stm32::init(Default::default());\n    info!(\"Hello World!\");\n\n    let mut adc = Adc::new(p.ADC1, Irqs);\n    let mut pin = p.PA1;\n\n    let mut vrefint = adc.enable_vref();\n    let vrefint_sample = adc.read(&mut vrefint, SampleTime::CYCLES13_5).await;\n    let convert_to_millivolts = |sample| {\n        // From https://www.st.com/resource/en/datasheet/stm32f031c6.pdf\n        // 6.3.4 Embedded reference voltage\n        const VREFINT_MV: u32 = 1230; // mV\n\n        (u32::from(sample) * VREFINT_MV / u32::from(vrefint_sample)) as u16\n    };\n\n    loop {\n        let v = adc.read(&mut pin, SampleTime::CYCLES13_5).await;\n        info!(\"--> {} - {} mV\", v, convert_to_millivolts(v));\n        Timer::after_millis(100).await;\n    }\n}\n"
  },
  {
    "path": "examples/stm32f0/src/bin/blinky.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_stm32::gpio::{Level, Output, Speed};\nuse embassy_time::Timer;\nuse {defmt_rtt as _, panic_probe as _};\n\n// main is itself an async function.\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let p = embassy_stm32::init(Default::default());\n    info!(\"Hello World!\");\n    //PA5 is the onboard LED on the Nucleo F091RC\n    let mut led = Output::new(p.PA5, Level::High, Speed::Low);\n\n    loop {\n        info!(\"high\");\n        led.set_high();\n        Timer::after_millis(300).await;\n\n        info!(\"low\");\n        led.set_low();\n        Timer::after_millis(300).await;\n    }\n}\n"
  },
  {
    "path": "examples/stm32f0/src/bin/button_controlled_blink.rs",
    "content": "//! This example showcases how to create task\n\n#![no_std]\n#![no_main]\n\nuse core::sync::atomic::{AtomicU32, Ordering};\n\nuse defmt::info;\nuse embassy_executor::Spawner;\nuse embassy_stm32::exti::{self, ExtiInput};\nuse embassy_stm32::gpio::{AnyPin, Level, Output, Pull, Speed};\nuse embassy_stm32::{Peri, bind_interrupts, interrupt};\nuse embassy_time::Timer;\nuse {defmt_rtt as _, panic_probe as _};\n\nstatic BLINK_MS: AtomicU32 = AtomicU32::new(0);\n\nbind_interrupts!(\n    pub struct Irqs{\n        EXTI4_15 => exti::InterruptHandler<interrupt::typelevel::EXTI4_15>;\n});\n\n#[embassy_executor::task]\nasync fn led_task(led: Peri<'static, AnyPin>) {\n    // Configure the LED pin as a push pull output and obtain handler.\n    // On the Nucleo F091RC there's an on-board LED connected to pin PA5.\n    let mut led = Output::new(led, Level::Low, Speed::Low);\n\n    loop {\n        let del = BLINK_MS.load(Ordering::Relaxed);\n        info!(\"Value of del is {}\", del);\n        Timer::after_millis(del.into()).await;\n        info!(\"LED toggling\");\n        led.toggle();\n    }\n}\n\n#[embassy_executor::main]\nasync fn main(spawner: Spawner) {\n    // Initialize and create handle for devicer peripherals\n    let p = embassy_stm32::init(Default::default());\n\n    // Configure the button pin and obtain handler.\n    // On the Nucleo F091RC there is a button connected to pin PC13.\n    let mut button = ExtiInput::new(p.PC13, p.EXTI13, Pull::None, Irqs);\n\n    // Create and initialize a delay variable to manage delay loop\n    let mut del_var = 2000;\n\n    // Blink duration value to global context\n    BLINK_MS.store(del_var, Ordering::Relaxed);\n\n    // Spawn LED blinking task\n    spawner.spawn(led_task(p.PA5.into()).unwrap());\n\n    loop {\n        // Check if button got pressed\n        button.wait_for_rising_edge().await;\n        info!(\"rising_edge\");\n        del_var = del_var - 200;\n        // If updated delay value drops below 200 then reset it back to starting value\n        if del_var < 200 {\n            del_var = 2000;\n        }\n        // Updated delay value to global context\n        BLINK_MS.store(del_var, Ordering::Relaxed);\n    }\n}\n"
  },
  {
    "path": "examples/stm32f0/src/bin/button_exti.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_stm32::exti::{self, ExtiInput};\nuse embassy_stm32::gpio::Pull;\nuse embassy_stm32::{bind_interrupts, interrupt};\nuse {defmt_rtt as _, panic_probe as _};\n\nbind_interrupts!(\n    pub struct Irqs{\n        EXTI4_15 => exti::InterruptHandler<interrupt::typelevel::EXTI4_15>;\n});\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    // Initialize and create handle for devicer peripherals\n    let p = embassy_stm32::init(Default::default());\n    // Configure the button pin and obtain handler.\n    // On the Nucleo F091RC there is a button connected to pin PC13.\n    let mut button = ExtiInput::new(p.PC13, p.EXTI13, Pull::Down, Irqs);\n\n    info!(\"Press the USER button...\");\n    loop {\n        button.wait_for_falling_edge().await;\n        info!(\"Pressed!\");\n        button.wait_for_rising_edge().await;\n        info!(\"Released!\");\n    }\n}\n"
  },
  {
    "path": "examples/stm32f0/src/bin/hello.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse defmt::info;\nuse embassy_executor::Spawner;\nuse embassy_time::Timer;\nuse {defmt_rtt as _, panic_probe as _};\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) -> ! {\n    let _p = embassy_stm32::init(Default::default());\n    loop {\n        Timer::after_secs(1).await;\n        info!(\"Hello\");\n    }\n}\n"
  },
  {
    "path": "examples/stm32f0/src/bin/i2c_master.rs",
    "content": "#![no_std]\n#![no_main]\n\n// Hardware Setup for NUCLEO-F072RB:\n// - I2C1 pins: PB8 (SCL), PB9 (SDA) on CN5 connector\n// - Connect to I2C slave device (e.g., Digilent Analog Discovery I2C slave, or EEPROM)\n// - Default slave address: 0x50\n// - Pull-up resistors: 4.7kΩ on both SCL and SDA\n// - CN5 Pin 10 (PB8/SCL) and CN5 Pin 9 (PB9/SDA)\n//\n// Analog Discovery - Waveforms Setup:\n// - Increase buffer size: Settings -> Device Manager -> Option 4\n// - Run Protocol Analyzer\n// - Configure as I2C Slave at address 0x50\n// - Connect and configure DIO pins for SCL and SDA\n// - Frequency: 100kHz - [✓] Clock Stretching\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_stm32::i2c::{Config, I2c, Master};\nuse embassy_stm32::mode::{Async, Blocking};\nuse embassy_stm32::time::Hertz;\nuse embassy_stm32::{bind_interrupts, dma, i2c, peripherals};\nuse embassy_time::Timer;\nuse embedded_hal_1::i2c::Operation;\nuse {defmt_rtt as _, panic_probe as _};\n\nbind_interrupts!(struct Irqs {\n    I2C1 => i2c::EventInterruptHandler<peripherals::I2C1>, i2c::ErrorInterruptHandler<peripherals::I2C1>;\n    DMA1_CH2_3_DMA2_CH1_2 => dma::InterruptHandler<peripherals::DMA1_CH2>, dma::InterruptHandler<peripherals::DMA1_CH3>;\n});\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let p = embassy_stm32::init(Default::default());\n    info!(\"Run stm32 I2C v2 Master Tests...\");\n\n    let mut i2c_peri = p.I2C1;\n    let mut scl = p.PB8;\n    let mut sda = p.PB9;\n\n    let mut config = Config::default();\n    config.frequency = Hertz(100_000);\n\n    // I2C slave address for Analog Discovery or test EEPROM\n    let slave_addr = 0x50u8;\n\n    // Wait for slave device to be ready\n    Timer::after_millis(100).await;\n\n    // ========== BLOCKING DIRECT API TESTS ==========\n    info!(\"========== BLOCKING DIRECT API TESTS ==========\");\n    {\n        let mut i2c = I2c::new_blocking(i2c_peri.reborrow(), scl.reborrow(), sda.reborrow(), config);\n\n        info!(\"=== Test 1: Direct blocking_write ===\");\n        test_blocking_write(&mut i2c, slave_addr);\n\n        info!(\"=== Test 2: Direct blocking_read ===\");\n        test_blocking_read(&mut i2c, slave_addr);\n\n        info!(\"=== Test 3: Direct blocking_write_read ===\");\n        test_blocking_write_read(&mut i2c, slave_addr);\n\n        info!(\"=== Test 4: Direct blocking_write_vectored ===\");\n        test_blocking_write_vectored(&mut i2c, slave_addr);\n\n        info!(\"=== Test 5: Large buffer (>255 bytes) ===\");\n        test_blocking_large_buffer(&mut i2c, slave_addr);\n\n        info!(\"Blocking direct API tests OK\");\n    }\n\n    Timer::after_millis(100).await;\n\n    // ========== BLOCKING TRANSACTION TESTS ==========\n    info!(\"========== BLOCKING TRANSACTION TESTS ==========\");\n    {\n        let mut i2c = I2c::new_blocking(i2c_peri.reborrow(), scl.reborrow(), sda.reborrow(), config);\n\n        info!(\"=== Test 6: Consecutive Writes (Should Merge) ===\");\n        test_consecutive_writes_blocking(&mut i2c, slave_addr);\n\n        info!(\"=== Test 7: Consecutive Reads (Should Merge) ===\");\n        test_consecutive_reads_blocking(&mut i2c, slave_addr);\n\n        info!(\"=== Test 8: Write then Read (RESTART) ===\");\n        test_write_then_read_blocking(&mut i2c, slave_addr);\n\n        info!(\"=== Test 9: Read then Write (RESTART) ===\");\n        test_read_then_write_blocking(&mut i2c, slave_addr);\n\n        info!(\"=== Test 10: Complex Mixed Sequence ===\");\n        test_mixed_sequence_blocking(&mut i2c, slave_addr);\n\n        info!(\"=== Test 11: Single Operations ===\");\n        test_single_operations_blocking(&mut i2c, slave_addr);\n\n        info!(\"Blocking transaction tests OK\");\n    }\n\n    Timer::after_millis(100).await;\n\n    // ========== ASYNC TESTS (DMA) ==========\n    info!(\"========== ASYNC TESTS (DMA) ==========\");\n    {\n        let tx_dma = p.DMA1_CH2;\n        let rx_dma = p.DMA1_CH3;\n\n        let mut i2c = I2c::new(i2c_peri, scl, sda, tx_dma, rx_dma, Irqs, config);\n\n        // Direct API tests (reusing same I2C instance)\n        info!(\"=== Direct API Test 1: write() ===\");\n        test_async_write(&mut i2c, slave_addr).await;\n\n        info!(\"=== Direct API Test 2: read() ===\");\n        test_async_read(&mut i2c, slave_addr).await;\n\n        info!(\"=== Direct API Test 3: write_read() ===\");\n        test_async_write_read(&mut i2c, slave_addr).await;\n\n        info!(\"=== Direct API Test 4: write_vectored() ===\");\n        test_async_write_vectored(&mut i2c, slave_addr).await;\n\n        info!(\"=== Direct API Test 5: Large buffer (>255 bytes) ===\");\n        test_async_large_buffer(&mut i2c, slave_addr).await;\n\n        info!(\"Async Direct API tests OK\");\n\n        // Transaction tests\n        info!(\"=== Transaction Test 6: Consecutive Writes (Should Merge) ===\");\n        test_consecutive_writes_async(&mut i2c, slave_addr).await;\n\n        info!(\"=== Transaction Test 7: Consecutive Reads (Should Merge) ===\");\n        test_consecutive_reads_async(&mut i2c, slave_addr).await;\n\n        info!(\"=== Transaction Test 8: Write then Read (RESTART) ===\");\n        test_write_then_read_async(&mut i2c, slave_addr).await;\n\n        info!(\"=== Transaction Test 9: Read then Write (RESTART) ===\");\n        test_read_then_write_async(&mut i2c, slave_addr).await;\n\n        info!(\"=== Transaction Test 10: Complex Mixed Sequence ===\");\n        test_mixed_sequence_async(&mut i2c, slave_addr).await;\n\n        info!(\"=== Transaction Test 11: Single Operations ===\");\n        test_single_operations_async(&mut i2c, slave_addr).await;\n\n        info!(\"Async transaction tests OK\");\n    }\n\n    info!(\"All tests OK\");\n    cortex_m::asm::bkpt();\n}\n\n// ==================== BLOCKING DIRECT API TEST FUNCTIONS ====================\n\nfn test_blocking_write(i2c: &mut I2c<'_, Blocking, Master>, addr: u8) {\n    let write_data = [0x42, 0x43, 0x44, 0x45];\n\n    match i2c.blocking_write(addr, &write_data) {\n        Ok(_) => info!(\"✓ blocking_write succeeded: {:02x}\", write_data),\n        Err(e) => {\n            error!(\"✗ blocking_write failed: {:?}\", e);\n            defmt::panic!(\"Test failed: blocking_write\");\n        }\n    }\n}\n\nfn test_blocking_read(i2c: &mut I2c<'_, Blocking, Master>, addr: u8) {\n    let mut read_buf = [0u8; 8];\n\n    match i2c.blocking_read(addr, &mut read_buf) {\n        Ok(_) => info!(\"✓ blocking_read succeeded: {:02x}\", read_buf),\n        Err(e) => {\n            error!(\"✗ blocking_read failed: {:?}\", e);\n            defmt::panic!(\"Test failed: blocking_read\");\n        }\n    }\n}\n\nfn test_blocking_write_read(i2c: &mut I2c<'_, Blocking, Master>, addr: u8) {\n    let write_data = [0x50, 0x51];\n    let mut read_buf = [0u8; 6];\n\n    match i2c.blocking_write_read(addr, &write_data, &mut read_buf) {\n        Ok(_) => {\n            info!(\"✓ blocking_write_read succeeded\");\n            info!(\"  Written: {:02x}\", write_data);\n            info!(\"  Read: {:02x}\", read_buf);\n        }\n        Err(e) => {\n            error!(\"✗ blocking_write_read failed: {:?}\", e);\n            defmt::panic!(\"Test failed: blocking_write_read\");\n        }\n    }\n}\n\nfn test_blocking_write_vectored(i2c: &mut I2c<'_, Blocking, Master>, addr: u8) {\n    let buf1 = [0x60, 0x61, 0x62];\n    let buf2 = [0x70, 0x71];\n    let buf3 = [0x80, 0x81, 0x82, 0x83];\n    let bufs = [&buf1[..], &buf2[..], &buf3[..]];\n\n    match i2c.blocking_write_vectored(addr, &bufs) {\n        Ok(_) => info!(\"✓ blocking_write_vectored succeeded (9 bytes total)\"),\n        Err(e) => {\n            error!(\"✗ blocking_write_vectored failed: {:?}\", e);\n            defmt::panic!(\"Test failed: blocking_write_vectored\");\n        }\n    }\n}\n\nfn test_blocking_large_buffer(i2c: &mut I2c<'_, Blocking, Master>, addr: u8) {\n    // Test with 300 bytes to verify RELOAD mechanism works (needs chunking at 255 bytes)\n    let mut write_buf = [0u8; 300];\n    for (i, byte) in write_buf.iter_mut().enumerate() {\n        *byte = (i & 0xFF) as u8;\n    }\n\n    match i2c.blocking_write(addr, &write_buf) {\n        Ok(_) => info!(\"✓ Large buffer write succeeded (300 bytes, tests RELOAD)\"),\n        Err(e) => {\n            error!(\"✗ Large buffer write failed: {:?}\", e);\n            defmt::panic!(\"Test failed: large buffer write\");\n        }\n    }\n\n    // Test large read\n    let mut read_buf = [0u8; 300];\n    match i2c.blocking_read(addr, &mut read_buf) {\n        Ok(_) => info!(\"✓ Large buffer read succeeded (300 bytes, tests RELOAD)\"),\n        Err(e) => {\n            error!(\"✗ Large buffer read failed: {:?}\", e);\n            defmt::panic!(\"Test failed: large buffer read\");\n        }\n    }\n}\n\n// ==================== BLOCKING TRANSACTION TEST FUNCTIONS ====================\n\nfn test_consecutive_writes_blocking(i2c: &mut I2c<'_, Blocking, Master>, addr: u8) {\n    // Expected on bus: START, ADDR+W, data1, data2, data3, STOP\n    // NO intermediate RESTART/STOP between writes - they should be merged\n    let data1 = [0x10, 0x11, 0x12];\n    let data2 = [0x20, 0x21];\n    let data3 = [0x30, 0x31, 0x32, 0x33];\n\n    let mut ops = [\n        Operation::Write(&data1),\n        Operation::Write(&data2),\n        Operation::Write(&data3),\n    ];\n\n    match i2c.blocking_transaction(addr, &mut ops) {\n        Ok(_) => info!(\"✓ Consecutive writes succeeded (merged 9 bytes)\"),\n        Err(e) => {\n            error!(\"✗ Consecutive writes failed: {:?}\", e);\n            defmt::panic!(\"Test failed: consecutive writes\");\n        }\n    }\n}\n\nfn test_consecutive_reads_blocking(i2c: &mut I2c<'_, Blocking, Master>, addr: u8) {\n    // Expected on bus: START, ADDR+R, data1, data2, data3, NACK, STOP\n    // NO intermediate RESTART/STOP between reads - they should be merged\n    let mut buf1 = [0u8; 4];\n    let mut buf2 = [0u8; 3];\n    let mut buf3 = [0u8; 2];\n\n    let mut ops = [\n        Operation::Read(&mut buf1),\n        Operation::Read(&mut buf2),\n        Operation::Read(&mut buf3),\n    ];\n\n    match i2c.blocking_transaction(addr, &mut ops) {\n        Ok(_) => {\n            info!(\"✓ Consecutive reads succeeded (merged 9 bytes)\");\n            info!(\"  buf1: {:02x}\", buf1);\n            info!(\"  buf2: {:02x}\", buf2);\n            info!(\"  buf3: {:02x}\", buf3);\n        }\n        Err(e) => {\n            error!(\"✗ Consecutive reads failed: {:?}\", e);\n            defmt::panic!(\"Test failed: consecutive reads\");\n        }\n    }\n}\n\nfn test_write_then_read_blocking(i2c: &mut I2c<'_, Blocking, Master>, addr: u8) {\n    // Expected: START, ADDR+W, data, RESTART, ADDR+R, data, NACK, STOP\n    let write_data = [0xAA, 0xBB];\n    let mut read_buf = [0u8; 4];\n\n    let mut ops = [Operation::Write(&write_data), Operation::Read(&mut read_buf)];\n\n    match i2c.blocking_transaction(addr, &mut ops) {\n        Ok(_) => {\n            info!(\"✓ Write-then-read succeeded with RESTART\");\n            info!(\"  Written: {:02x}\", write_data);\n            info!(\"  Read: {:02x}\", read_buf);\n        }\n        Err(e) => {\n            error!(\"✗ Write-then-read failed: {:?}\", e);\n            defmt::panic!(\"Test failed: write-then-read\");\n        }\n    }\n}\n\nfn test_read_then_write_blocking(i2c: &mut I2c<'_, Blocking, Master>, addr: u8) {\n    // Expected: START, ADDR+R, data, NACK, RESTART, ADDR+W, data, STOP\n    let mut read_buf = [0u8; 3];\n    let write_data = [0xCC, 0xDD, 0xEE];\n\n    let mut ops = [Operation::Read(&mut read_buf), Operation::Write(&write_data)];\n\n    match i2c.blocking_transaction(addr, &mut ops) {\n        Ok(_) => {\n            info!(\"✓ Read-then-write succeeded with RESTART\");\n            info!(\"  Read: {:02x}\", read_buf);\n            info!(\"  Written: {:02x}\", write_data);\n        }\n        Err(e) => {\n            error!(\"✗ Read-then-write failed: {:?}\", e);\n            defmt::panic!(\"Test failed: read-then-write\");\n        }\n    }\n}\n\nfn test_mixed_sequence_blocking(i2c: &mut I2c<'_, Blocking, Master>, addr: u8) {\n    // Complex: W, W, R, R, W, R\n    // Groups: [W,W] RESTART [R,R] RESTART [W] RESTART [R]\n    let w1 = [0x01, 0x02];\n    let w2 = [0x03, 0x04];\n    let mut r1 = [0u8; 2];\n    let mut r2 = [0u8; 2];\n    let w3 = [0x05];\n    let mut r3 = [0u8; 1];\n\n    let mut ops = [\n        Operation::Write(&w1),\n        Operation::Write(&w2),\n        Operation::Read(&mut r1),\n        Operation::Read(&mut r2),\n        Operation::Write(&w3),\n        Operation::Read(&mut r3),\n    ];\n\n    match i2c.blocking_transaction(addr, &mut ops) {\n        Ok(_) => {\n            info!(\"✓ Mixed sequence succeeded\");\n            info!(\"  Groups: [W4] RESTART [R4] RESTART [W1] RESTART [R1]\");\n        }\n        Err(e) => {\n            error!(\"✗ Mixed sequence failed: {:?}\", e);\n            defmt::panic!(\"Test failed: mixed sequence\");\n        }\n    }\n}\n\nfn test_single_operations_blocking(i2c: &mut I2c<'_, Blocking, Master>, addr: u8) {\n    // Test single write\n    let write_data = [0xFF];\n    let mut ops = [Operation::Write(&write_data)];\n\n    match i2c.blocking_transaction(addr, &mut ops) {\n        Ok(_) => info!(\"✓ Single write succeeded\"),\n        Err(e) => {\n            error!(\"✗ Single write failed: {:?}\", e);\n            defmt::panic!(\"Test failed: single write\");\n        }\n    }\n\n    // Test single read\n    let mut read_buf = [0u8; 1];\n    let mut ops = [Operation::Read(&mut read_buf)];\n\n    match i2c.blocking_transaction(addr, &mut ops) {\n        Ok(_) => info!(\"✓ Single read succeeded, data: 0x{:02x}\", read_buf[0]),\n        Err(e) => {\n            error!(\"✗ Single read failed: {:?}\", e);\n            defmt::panic!(\"Test failed: single read\");\n        }\n    }\n}\n\n// ==================== ASYNC DIRECT API TEST FUNCTIONS ====================\n\nasync fn test_async_write(i2c: &mut I2c<'_, Async, Master>, addr: u8) {\n    let write_data = [0x42, 0x43, 0x44, 0x45];\n\n    match i2c.write(addr, &write_data).await {\n        Ok(_) => info!(\"✓ async write succeeded: {:02x}\", write_data),\n        Err(e) => {\n            error!(\"✗ async write failed: {:?}\", e);\n            defmt::panic!(\"Test failed: async write\");\n        }\n    }\n}\n\nasync fn test_async_read(i2c: &mut I2c<'_, Async, Master>, addr: u8) {\n    let mut read_buf = [0u8; 8];\n\n    match i2c.read(addr, &mut read_buf).await {\n        Ok(_) => info!(\"✓ async read succeeded: {:02x}\", read_buf),\n        Err(e) => {\n            error!(\"✗ async read failed: {:?}\", e);\n            defmt::panic!(\"Test failed: async read\");\n        }\n    }\n}\n\nasync fn test_async_write_read(i2c: &mut I2c<'_, Async, Master>, addr: u8) {\n    let write_data = [0x50, 0x51];\n    let mut read_buf = [0u8; 6];\n\n    match i2c.write_read(addr, &write_data, &mut read_buf).await {\n        Ok(_) => {\n            info!(\"✓ async write_read succeeded\");\n            info!(\"  Written: {:02x}\", write_data);\n            info!(\"  Read: {:02x}\", read_buf);\n        }\n        Err(e) => {\n            error!(\"✗ async write_read failed: {:?}\", e);\n            defmt::panic!(\"Test failed: async write_read\");\n        }\n    }\n}\n\nasync fn test_async_write_vectored(i2c: &mut I2c<'_, Async, Master>, addr: u8) {\n    let buf1 = [0x60, 0x61, 0x62];\n    let buf2 = [0x70, 0x71];\n    let buf3 = [0x80, 0x81, 0x82, 0x83];\n    let bufs = [&buf1[..], &buf2[..], &buf3[..]];\n\n    match i2c.write_vectored(addr.into(), &bufs).await {\n        Ok(_) => info!(\"✓ async write_vectored succeeded (9 bytes total)\"),\n        Err(e) => {\n            error!(\"✗ async write_vectored failed: {:?}\", e);\n            defmt::panic!(\"Test failed: async write_vectored\");\n        }\n    }\n}\n\nasync fn test_async_large_buffer(i2c: &mut I2c<'_, Async, Master>, addr: u8) {\n    // Test with 300 bytes to verify RELOAD mechanism works with DMA (needs chunking at 255 bytes)\n    let mut write_buf = [0u8; 300];\n    for (i, byte) in write_buf.iter_mut().enumerate() {\n        *byte = (i & 0xFF) as u8;\n    }\n\n    match i2c.write(addr, &write_buf).await {\n        Ok(_) => info!(\"✓ Large buffer async write succeeded (300 bytes, tests RELOAD with DMA)\"),\n        Err(e) => {\n            error!(\"✗ Large buffer async write failed: {:?}\", e);\n            defmt::panic!(\"Test failed: large buffer async write\");\n        }\n    }\n\n    // Test large read\n    let mut read_buf = [0u8; 300];\n    match i2c.read(addr, &mut read_buf).await {\n        Ok(_) => info!(\"✓ Large buffer async read succeeded (300 bytes, tests RELOAD with DMA)\"),\n        Err(e) => {\n            error!(\"✗ Large buffer async read failed: {:?}\", e);\n            defmt::panic!(\"Test failed: large buffer async read\");\n        }\n    }\n}\n\n// ==================== ASYNC TRANSACTION TEST FUNCTIONS ====================\n\nasync fn test_consecutive_writes_async(i2c: &mut I2c<'_, Async, Master>, addr: u8) {\n    let data1 = [0x10, 0x11, 0x12];\n    let data2 = [0x20, 0x21];\n    let data3 = [0x30, 0x31, 0x32, 0x33];\n\n    let mut ops = [\n        Operation::Write(&data1),\n        Operation::Write(&data2),\n        Operation::Write(&data3),\n    ];\n\n    match i2c.transaction(addr, &mut ops).await {\n        Ok(_) => info!(\"✓ Consecutive writes succeeded (merged 9 bytes)\"),\n        Err(e) => {\n            error!(\"✗ Consecutive writes failed: {:?}\", e);\n            defmt::panic!(\"Test failed: consecutive writes\");\n        }\n    }\n}\n\nasync fn test_consecutive_reads_async(i2c: &mut I2c<'_, Async, Master>, addr: u8) {\n    let mut buf1 = [0u8; 4];\n    let mut buf2 = [0u8; 3];\n    let mut buf3 = [0u8; 2];\n\n    let mut ops = [\n        Operation::Read(&mut buf1),\n        Operation::Read(&mut buf2),\n        Operation::Read(&mut buf3),\n    ];\n\n    match i2c.transaction(addr, &mut ops).await {\n        Ok(_) => {\n            info!(\"✓ Consecutive reads succeeded (merged 9 bytes)\");\n            info!(\"  buf1: {:02x}\", buf1);\n            info!(\"  buf2: {:02x}\", buf2);\n            info!(\"  buf3: {:02x}\", buf3);\n        }\n        Err(e) => {\n            error!(\"✗ Consecutive reads failed: {:?}\", e);\n            defmt::panic!(\"Test failed: consecutive reads\");\n        }\n    }\n}\n\nasync fn test_write_then_read_async(i2c: &mut I2c<'_, Async, Master>, addr: u8) {\n    let write_data = [0xAA, 0xBB];\n    let mut read_buf = [0u8; 4];\n\n    let mut ops = [Operation::Write(&write_data), Operation::Read(&mut read_buf)];\n\n    match i2c.transaction(addr, &mut ops).await {\n        Ok(_) => {\n            info!(\"✓ Write-then-read succeeded with RESTART\");\n            info!(\"  Written: {:02x}\", write_data);\n            info!(\"  Read: {:02x}\", read_buf);\n        }\n        Err(e) => {\n            error!(\"✗ Write-then-read failed: {:?}\", e);\n            defmt::panic!(\"Test failed: write-then-read\");\n        }\n    }\n}\n\nasync fn test_read_then_write_async(i2c: &mut I2c<'_, Async, Master>, addr: u8) {\n    let mut read_buf = [0u8; 3];\n    let write_data = [0xCC, 0xDD, 0xEE];\n\n    let mut ops = [Operation::Read(&mut read_buf), Operation::Write(&write_data)];\n\n    match i2c.transaction(addr, &mut ops).await {\n        Ok(_) => {\n            info!(\"✓ Read-then-write succeeded with RESTART\");\n            info!(\"  Read: {:02x}\", read_buf);\n            info!(\"  Written: {:02x}\", write_data);\n        }\n        Err(e) => {\n            error!(\"✗ Read-then-write failed: {:?}\", e);\n            defmt::panic!(\"Test failed: read-then-write\");\n        }\n    }\n}\n\nasync fn test_mixed_sequence_async(i2c: &mut I2c<'_, Async, Master>, addr: u8) {\n    let w1 = [0x01, 0x02];\n    let w2 = [0x03, 0x04];\n    let mut r1 = [0u8; 2];\n    let mut r2 = [0u8; 2];\n    let w3 = [0x05];\n    let mut r3 = [0u8; 1];\n\n    let mut ops = [\n        Operation::Write(&w1),\n        Operation::Write(&w2),\n        Operation::Read(&mut r1),\n        Operation::Read(&mut r2),\n        Operation::Write(&w3),\n        Operation::Read(&mut r3),\n    ];\n\n    match i2c.transaction(addr, &mut ops).await {\n        Ok(_) => {\n            info!(\"✓ Mixed sequence succeeded\");\n            info!(\"  Groups: [W4] RESTART [R4] RESTART [W1] RESTART [R1]\");\n        }\n        Err(e) => {\n            error!(\"✗ Mixed sequence failed: {:?}\", e);\n            defmt::panic!(\"Test failed: mixed sequence\");\n        }\n    }\n}\n\nasync fn test_single_operations_async(i2c: &mut I2c<'_, Async, Master>, addr: u8) {\n    // Test single write\n    let write_data = [0xFF];\n    let mut ops = [Operation::Write(&write_data)];\n\n    match i2c.transaction(addr, &mut ops).await {\n        Ok(_) => info!(\"✓ Single write succeeded\"),\n        Err(e) => {\n            error!(\"✗ Single write failed: {:?}\", e);\n            defmt::panic!(\"Test failed: single write\");\n        }\n    }\n\n    // Test single read\n    let mut read_buf = [0u8; 1];\n    let mut ops = [Operation::Read(&mut read_buf)];\n\n    match i2c.transaction(addr, &mut ops).await {\n        Ok(_) => info!(\"✓ Single read succeeded, data: 0x{:02x}\", read_buf[0]),\n        Err(e) => {\n            error!(\"✗ Single read failed: {:?}\", e);\n            defmt::panic!(\"Test failed: single read\");\n        }\n    }\n}\n"
  },
  {
    "path": "examples/stm32f0/src/bin/i2c_slave_async.rs",
    "content": "//! I2C Async Slave Example\n//!\n//! Demonstrates the async I2C slave (multimaster) implementation using DMA.\n//! The device listens for I2C transactions from an external master.\n//!\n//! # Hardware Setup\n//!\n//! - PB8 (SCL) and PB9 (SDA) with 4.7k pull-up resistors to 3.3V\n//! - Connect an I2C master device with clock stretching enabled\n//! - Default slave address: 0x42 (7-bit)\n//!\n//! # Behavior\n//!\n//! - Master WRITE: Receives up to BUFFER_SIZE bytes via DMA, excess bytes are drained\n//! - Master READ: Sends an 8-byte response pattern (0xA0-0xA7) via DMA\n\n#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_stm32::i2c::{self, I2c, SendStatus, SlaveAddrConfig, SlaveCommandKind};\nuse embassy_stm32::{bind_interrupts, dma, peripherals};\nuse embassy_time::Duration;\nuse {defmt_rtt as _, panic_probe as _};\n\nconst I2C_ADDR: u8 = 0x42;\nconst BUFFER_SIZE: usize = 32;\n\nbind_interrupts!(struct Irqs {\n    I2C1 => i2c::EventInterruptHandler<peripherals::I2C1>, i2c::ErrorInterruptHandler<peripherals::I2C1>;\n    DMA1_CH2_3_DMA2_CH1_2 => dma::InterruptHandler<peripherals::DMA1_CH2>, dma::InterruptHandler<peripherals::DMA1_CH3>;\n});\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let p = embassy_stm32::init(Default::default());\n\n    info!(\"I2C Async Slave Example\");\n    info!(\"Address: 0x{:02X}, Buffer: {} bytes\", I2C_ADDR, BUFFER_SIZE);\n\n    let mut config = i2c::Config::default();\n    config.timeout = Duration::from_secs(30);\n\n    let mut i2c = I2c::new(p.I2C1, p.PB8, p.PB9, p.DMA1_CH2, p.DMA1_CH3, Irqs, config)\n        .into_slave_multimaster(SlaveAddrConfig::basic(I2C_ADDR));\n\n    info!(\"Slave ready, listening...\");\n\n    let mut count: u32 = 0;\n    loop {\n        match i2c.listen().await {\n            Ok(cmd) => {\n                count += 1;\n                match cmd.kind {\n                    SlaveCommandKind::Write => {\n                        let mut buffer = [0u8; BUFFER_SIZE];\n                        match i2c.respond_to_write(&mut buffer).await {\n                            Ok(len) => info!(\"[{}] Write: {} bytes: {:02X}\", count, len, &buffer[..len]),\n                            Err(e) => error!(\"[{}] Write error: {:?}\", count, e),\n                        }\n                    }\n                    SlaveCommandKind::Read => {\n                        let response: [u8; 8] = [0xA0, 0xA1, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7];\n                        match i2c.respond_to_read(&response).await {\n                            Ok(SendStatus::Done) => info!(\"[{}] Read: {} bytes\", count, response.len()),\n                            Ok(SendStatus::LeftoverBytes(n)) => {\n                                info!(\"[{}] Read: {} of {} bytes\", count, response.len() - n, response.len())\n                            }\n                            Err(e) => error!(\"[{}] Read error: {:?}\", count, e),\n                        }\n                    }\n                }\n            }\n            Err(e) => error!(\"Listen error: {:?}\", e),\n        }\n    }\n}\n"
  },
  {
    "path": "examples/stm32f0/src/bin/i2c_slave_blocking.rs",
    "content": "//! I2C Blocking Slave Example\n//!\n//! Demonstrates the blocking I2C slave (multimaster) implementation.\n//! The device listens for I2C transactions from an external master.\n//!\n//! # Hardware Setup\n//!\n//! - PB8 (SCL) and PB9 (SDA) with 4.7k pull-up resistors to 3.3V\n//! - Connect an I2C master device with clock stretching enabled\n//! - Default slave address: 0x42 (7-bit)\n//!\n//! # Behavior\n//!\n//! - Master WRITE: Receives up to BUFFER_SIZE bytes, excess bytes are drained\n//! - Master READ: Sends an 8-byte response pattern (0xA0-0xA7)\n\n#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_stm32::i2c::{self, I2c, SendStatus, SlaveAddrConfig, SlaveCommandKind};\nuse embassy_stm32::{bind_interrupts, peripherals};\nuse embassy_time::Duration;\nuse {defmt_rtt as _, panic_probe as _};\n\nconst I2C_ADDR: u8 = 0x42;\nconst BUFFER_SIZE: usize = 32;\n\nbind_interrupts!(struct Irqs {\n    I2C1 => i2c::EventInterruptHandler<peripherals::I2C1>, i2c::ErrorInterruptHandler<peripherals::I2C1>;\n});\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let p = embassy_stm32::init(Default::default());\n\n    info!(\"I2C Blocking Slave Example\");\n    info!(\"Address: 0x{:02X}, Buffer: {} bytes\", I2C_ADDR, BUFFER_SIZE);\n\n    let mut config = i2c::Config::default();\n    config.timeout = Duration::from_secs(30);\n\n    let mut i2c =\n        I2c::new_blocking(p.I2C1, p.PB8, p.PB9, config).into_slave_multimaster(SlaveAddrConfig::basic(I2C_ADDR));\n\n    info!(\"Slave ready, listening...\");\n\n    let mut count: u32 = 0;\n    loop {\n        match i2c.blocking_listen() {\n            Ok(cmd) => {\n                count += 1;\n                match cmd.kind {\n                    SlaveCommandKind::Write => {\n                        let mut buffer = [0u8; BUFFER_SIZE];\n                        match i2c.blocking_respond_to_write(&mut buffer) {\n                            Ok(len) => info!(\"[{}] Write: {} bytes: {:02X}\", count, len, &buffer[..len]),\n                            Err(e) => error!(\"[{}] Write error: {:?}\", count, e),\n                        }\n                    }\n                    SlaveCommandKind::Read => {\n                        let response: [u8; 8] = [0xA0, 0xA1, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7];\n                        match i2c.blocking_respond_to_read(&response) {\n                            Ok(SendStatus::Done) => info!(\"[{}] Read: {} bytes\", count, response.len()),\n                            Ok(SendStatus::LeftoverBytes(n)) => {\n                                info!(\"[{}] Read: {} of {} bytes\", count, response.len() - n, response.len())\n                            }\n                            Err(e) => error!(\"[{}] Read error: {:?}\", count, e),\n                        }\n                    }\n                }\n            }\n            Err(e) => error!(\"Listen error: {:?}\", e),\n        }\n    }\n}\n"
  },
  {
    "path": "examples/stm32f0/src/bin/multiprio.rs",
    "content": "//! This example showcases how to create multiple Executor instances to run tasks at\n//! different priority levels.\n//!\n//! Low priority executor runs in thread mode (not interrupt), and uses `sev` for signaling\n//! there's work in the queue, and `wfe` for waiting for work.\n//!\n//! Medium and high priority executors run in two interrupts with different priorities.\n//! Signaling work is done by pending the interrupt. No \"waiting\" needs to be done explicitly, since\n//! when there's work the interrupt will trigger and run the executor.\n//!\n//! Sample output below. Note that high priority ticks can interrupt everything else, and\n//! medium priority computations can interrupt low priority computations, making them to appear\n//! to take significantly longer time.\n//!\n//! ```not_rust\n//!     [med] Starting long computation\n//!     [med] done in 992 ms\n//!         [high] tick!\n//! [low] Starting long computation\n//!     [med] Starting long computation\n//!         [high] tick!\n//!         [high] tick!\n//!     [med] done in 993 ms\n//!     [med] Starting long computation\n//!         [high] tick!\n//!         [high] tick!\n//!     [med] done in 993 ms\n//! [low] done in 3972 ms\n//!     [med] Starting long computation\n//!         [high] tick!\n//!         [high] tick!\n//!     [med] done in 993 ms\n//! ```\n//!\n//! For comparison, try changing the code so all 3 tasks get spawned on the low priority executor.\n//! You will get an output like the following. Note that no computation is ever interrupted.\n//!\n//! ```not_rust\n//!         [high] tick!\n//!     [med] Starting long computation\n//!     [med] done in 496 ms\n//! [low] Starting long computation\n//! [low] done in 992 ms\n//!     [med] Starting long computation\n//!     [med] done in 496 ms\n//!         [high] tick!\n//! [low] Starting long computation\n//! [low] done in 992 ms\n//!         [high] tick!\n//!     [med] Starting long computation\n//!     [med] done in 496 ms\n//!         [high] tick!\n//! ```\n//!\n\n#![no_std]\n#![no_main]\n\nuse cortex_m_rt::entry;\nuse defmt::*;\nuse embassy_executor::{Executor, InterruptExecutor};\nuse embassy_stm32::interrupt;\nuse embassy_stm32::interrupt::{InterruptExt, Priority};\nuse embassy_time::{Instant, Timer};\nuse static_cell::StaticCell;\nuse {defmt_rtt as _, panic_probe as _};\n\n#[embassy_executor::task]\nasync fn run_high() {\n    loop {\n        // info!(\"        [high] tick!\");\n        Timer::after_ticks(27374).await;\n    }\n}\n\n#[embassy_executor::task]\nasync fn run_med() {\n    loop {\n        let start = Instant::now();\n        info!(\"    [med] Starting long computation\");\n\n        // Spin-wait to simulate a long CPU computation\n        embassy_time::block_for(embassy_time::Duration::from_secs(1)); // ~1 second\n\n        let end = Instant::now();\n        let ms = end.duration_since(start).as_ticks() / 33;\n        info!(\"    [med] done in {} ms\", ms);\n\n        Timer::after_ticks(23421).await;\n    }\n}\n\n#[embassy_executor::task]\nasync fn run_low() {\n    loop {\n        let start = Instant::now();\n        info!(\"[low] Starting long computation\");\n\n        // Spin-wait to simulate a long CPU computation\n        embassy_time::block_for(embassy_time::Duration::from_secs(2)); // ~2 seconds\n\n        let end = Instant::now();\n        let ms = end.duration_since(start).as_ticks() / 33;\n        info!(\"[low] done in {} ms\", ms);\n\n        Timer::after_ticks(32983).await;\n    }\n}\n\nstatic EXECUTOR_HIGH: InterruptExecutor = InterruptExecutor::new();\nstatic EXECUTOR_MED: InterruptExecutor = InterruptExecutor::new();\nstatic EXECUTOR_LOW: StaticCell<Executor> = StaticCell::new();\n\n#[interrupt]\nunsafe fn USART1() {\n    unsafe { EXECUTOR_HIGH.on_interrupt() }\n}\n\n#[interrupt]\nunsafe fn USART2() {\n    unsafe { EXECUTOR_MED.on_interrupt() }\n}\n\n#[entry]\nfn main() -> ! {\n    // Initialize and create handle for devicer peripherals\n    let _p = embassy_stm32::init(Default::default());\n\n    // STM32s don’t have any interrupts exclusively for software use, but they can all be triggered by software as well as\n    // by the peripheral, so we can just use any free interrupt vectors which aren’t used by the rest of your application.\n    // In this case we’re using UART1 and UART2, but there’s nothing special about them. Any otherwise unused interrupt\n    // vector would work exactly the same.\n\n    // High-priority executor: USART1, priority level 6\n    interrupt::USART1.set_priority(Priority::P6);\n    let spawner = EXECUTOR_HIGH.start(interrupt::USART1);\n    spawner.spawn(unwrap!(run_high()));\n\n    // Medium-priority executor: USART2, priority level 7\n    interrupt::USART2.set_priority(Priority::P7);\n    let spawner = EXECUTOR_MED.start(interrupt::USART2);\n    spawner.spawn(unwrap!(run_med()));\n\n    // Low priority executor: runs in thread mode, using WFE/SEV\n    let executor = EXECUTOR_LOW.init(Executor::new());\n    executor.run(|spawner| {\n        spawner.spawn(unwrap!(run_low()));\n    });\n}\n"
  },
  {
    "path": "examples/stm32f0/src/bin/wdg.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_stm32::wdg::IndependentWatchdog;\nuse embassy_time::Timer;\nuse {defmt_rtt as _, panic_probe as _};\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    // Initialize and create handle for devicer peripherals\n    let p = embassy_stm32::init(Default::default());\n    // Configure the independent watchdog  timer\n    let mut wdg = IndependentWatchdog::new(p.IWDG, 20_000_00);\n\n    info!(\"Watchdog start\");\n    wdg.unleash();\n\n    loop {\n        Timer::after_secs(1).await;\n        wdg.pet();\n    }\n}\n"
  },
  {
    "path": "examples/stm32f072/.cargo/config.toml",
    "content": "[target.thumbv6m-none-eabi]\nrunner = 'probe-rs run --chip STM32F072RB'\n\n[build]\ntarget = \"thumbv6m-none-eabi\"\n\n[env]\nDEFMT_LOG = \"trace\"\n"
  },
  {
    "path": "examples/stm32f072/Cargo.toml",
    "content": "[package]\nname = \"embassy-stm32f0-examples\"\nversion = \"0.1.0\"\nedition = \"2024\"\nlicense = \"MIT OR Apache-2.0\"\npublish = false\n\n[dependencies]\n# Change stm32f091rc to your chip name, if necessary.\nembassy-stm32 = { version = \"0.6.0\", path = \"../../embassy-stm32\", features = [ \"defmt\", \"memory-x\", \"stm32f072rb\", \"time-driver-tim2\", \"exti\", \"unstable-pac\"] }\ncortex-m = { version = \"0.7.6\", features = [\"inline-asm\", \"critical-section-single-core\"] }\ncortex-m-rt = \"0.7.0\"\ndefmt = \"1.0.1\"\ndefmt-rtt = \"1.0.0\"\npanic-probe = { version = \"1.0.0\", features = [\"print-defmt\"] }\nembassy-sync = { version = \"0.8.0\", path = \"../../embassy-sync\", features = [\"defmt\"] }\nembassy-executor = { version = \"0.10.0\", path = \"../../embassy-executor\", features = [\"platform-cortex-m\", \"executor-thread\", \"executor-interrupt\", \"defmt\"] }\nembassy-time = { version = \"0.5.1\", path = \"../../embassy-time\", features = [\"defmt\", \"defmt-timestamp-uptime\", \"tick-hz-32_768\"] }\nembassy-futures = { version = \"0.1.0\", path = \"../../embassy-futures\" }\nembedded-hal-1 = { package = \"embedded-hal\", version = \"1.0\" }\nstatic_cell = \"2\"\nportable-atomic = { version = \"1.5\", features = [\"unsafe-assume-single-core\"] }\n\n[profile.release]\ndebug = 2\n\n[package.metadata.embassy]\nbuild = [\n  { target = \"thumbv6m-none-eabi\", artifact-dir = \"out/examples/stm32f0\" }\n]\n"
  },
  {
    "path": "examples/stm32f072/build.rs",
    "content": "fn main() {\n    println!(\"cargo:rustc-link-arg-bins=--nmagic\");\n    println!(\"cargo:rustc-link-arg-bins=-Tlink.x\");\n    println!(\"cargo:rustc-link-arg-bins=-Tdefmt.x\");\n}\n"
  },
  {
    "path": "examples/stm32f072/src/bin/i2c_loopback_test_async.rs",
    "content": "//! I2C Async Loopback Test - Comprehensive slave mode testing\n//!\n//! Tests the async I2C master and slave implementations using\n//! a loopback configuration with I2C1 as slave and I2C2 as master\n//! on the same device.\n//!\n//! Hardware setup (for NUCLEO-F072RB):\n//! - Connect PB8 (I2C1 SCL) to PB10 (I2C2 SCL)\n//! - Connect PB9 (I2C1 SDA) to PB11 (I2C2 SDA)\n//! - Add 4.7k pull-up resistors to 3.3V on both SCL and SDA lines\n//!\n//! Pin locations on NUCLEO-F072RB:\n//! - PB8:  CN5 pin 10 (D15)\n//! - PB9:  CN5 pin 9  (D14)\n//! - PB10: CN10 pin 25\n//! - PB11: CN10 pin 18\n//!\n//! # Test Coverage\n//!\n//! - W1: Basic Write Operations\n//! - W2: Early STOP (master sends less than buffer)\n//! - W3: Excess Data (master sends more than buffer) - SKIPPED (see below)\n//! - R1: Basic Read Operations\n//! - R2: Short Read (master reads less than slave offers)\n//! - M1: Mixed Operations\n//! - S1: Stress Tests\n//! - WR1: Write-Read with RESTART (varying write size)\n//! - WR2: Write-Read with RESTART (varying read size)\n//! - WR3: Multiple Write-Read Operations\n//!\n//! # Skipped Tests\n//!\n//! Some tests are skipped in loopback mode due to executor limitations:\n//!\n//! - **W2.4 (Empty write)**: The master's empty write uses a blocking path,\n//!   preventing the executor from running the slave task to release clock stretching.\n//!\n//! - **W3.x (Excess data)**: After the slave's DMA buffer is full, it busy-waits\n//!   in `drain_rxdr_until_stop()` for the STOP condition, but the master can't\n//!   send STOP because its poll_fn needs executor time.\n//!\n//! These tests pass with an external master (e.g., Analog Discovery) where master\n//! and slave run on separate processors.\n\n#![no_std]\n#![no_main]\n\nuse defmt::{error, info, warn};\nuse embassy_executor::Spawner;\nuse embassy_futures::join::join;\nuse embassy_stm32::i2c::{self, I2c, SlaveAddrConfig, SlaveCommandKind};\nuse embassy_stm32::time::khz;\nuse embassy_stm32::{bind_interrupts, dma, peripherals};\nuse embassy_time::Timer;\nuse {defmt_rtt as _, panic_probe as _};\n\nconst I2C_ADDR: u8 = 0x42;\nconst SLAVE_BUFFER_SIZE: usize = 32;\nconst EXPECTED_READ: [u8; 8] = [0xA0, 0xA1, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7];\n\nbind_interrupts!(struct Irqs {\n    I2C1 => i2c::EventInterruptHandler<peripherals::I2C1>, i2c::ErrorInterruptHandler<peripherals::I2C1>;\n    I2C2 => i2c::EventInterruptHandler<peripherals::I2C2>, i2c::ErrorInterruptHandler<peripherals::I2C2>;\n    DMA1_CHANNEL2_3 => dma::InterruptHandler<peripherals::DMA1_CH2>, dma::InterruptHandler<peripherals::DMA1_CH3>;\n    DMA1_CHANNEL4_5_6_7 => dma::InterruptHandler<peripherals::DMA1_CH4>, dma::InterruptHandler<peripherals::DMA1_CH5>;\n});\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let p = embassy_stm32::init(Default::default());\n\n    info!(\"==============================================\");\n    info!(\"I2C Async Loopback Test - Full Suite\");\n    info!(\"I2C1 (slave) PB8/PB9, I2C2 (master) PB10/PB11\");\n    info!(\"==============================================\");\n\n    let i2c_config = {\n        let mut config = i2c::Config::default();\n        config.frequency = khz(100);\n        config\n    };\n\n    let i2c_slave = I2c::new(p.I2C1, p.PB8, p.PB9, p.DMA1_CH2, p.DMA1_CH3, Irqs, i2c_config)\n        .into_slave_multimaster(SlaveAddrConfig::basic(I2C_ADDR));\n\n    let i2c_master = I2c::new(p.I2C2, p.PB10, p.PB11, p.DMA1_CH4, p.DMA1_CH5, Irqs, i2c_config);\n\n    join(slave_task(i2c_slave), master_task(i2c_master)).await;\n}\n\nasync fn slave_task(mut i2c: I2c<'static, embassy_stm32::mode::Async, i2c::mode::MultiMaster>) {\n    info!(\n        \"[Slave] Ready at 0x{:02X}, buffer {} bytes\",\n        I2C_ADDR, SLAVE_BUFFER_SIZE\n    );\n\n    loop {\n        match i2c.listen().await {\n            Ok(command) => match command.kind {\n                SlaveCommandKind::Write => {\n                    let mut buffer = [0u8; SLAVE_BUFFER_SIZE];\n                    match i2c.respond_to_write(&mut buffer).await {\n                        Ok(n) => info!(\"[Slave] RX {} bytes\", n),\n                        Err(e) => error!(\"[Slave] RX error: {:?}\", e),\n                    }\n                }\n                SlaveCommandKind::Read => match i2c.respond_to_read(&EXPECTED_READ).await {\n                    Ok(_) => info!(\"[Slave] TX complete\"),\n                    Err(e) => error!(\"[Slave] TX error: {:?}\", e),\n                },\n            },\n            Err(e) => {\n                warn!(\"[Slave] Listen error: {:?}\", e);\n                Timer::after_millis(100).await;\n            }\n        }\n    }\n}\n\nasync fn master_task(mut i2c: I2c<'static, embassy_stm32::mode::Async, i2c::mode::Master>) {\n    Timer::after_millis(100).await;\n\n    info!(\"\");\n    info!(\"Starting comprehensive test suite...\");\n    info!(\"\");\n\n    let mut passed = 0u32;\n    let mut failed = 0u32;\n    let skipped = 4u32; // W2.4 + W3.1-3 (blocking paths incompatible with loopback)\n\n    // Helper macro for write tests\n    macro_rules! test_write {\n        ($id:expr, $desc:expr, $data:expr) => {{\n            info!(\"--- {} ---\", $id);\n            match i2c.write(I2C_ADDR, $data).await {\n                Ok(()) => {\n                    info!(\"[PASS] {}: {}\", $id, $desc);\n                    passed += 1;\n                }\n                Err(e) => {\n                    error!(\"[FAIL] {}: {:?}\", $id, e);\n                    failed += 1;\n                }\n            }\n            Timer::after_millis(50).await;\n        }};\n    }\n\n    // Helper macro for read tests with validation\n    macro_rules! test_read {\n        ($id:expr, $desc:expr, $len:expr) => {{\n            info!(\"--- {} ---\", $id);\n            let mut buf = [0u8; $len];\n            match i2c.read(I2C_ADDR, &mut buf).await {\n                Ok(()) => {\n                    if buf == EXPECTED_READ[..$len] {\n                        info!(\"[PASS] {}: {}\", $id, $desc);\n                        passed += 1;\n                    } else {\n                        error!(\n                            \"[FAIL] {}: got {:02X}, expected {:02X}\",\n                            $id,\n                            buf,\n                            &EXPECTED_READ[..$len]\n                        );\n                        failed += 1;\n                    }\n                }\n                Err(e) => {\n                    error!(\"[FAIL] {}: {:?}\", $id, e);\n                    failed += 1;\n                }\n            }\n            Timer::after_millis(50).await;\n        }};\n    }\n\n    // Helper macro for write_read tests with validation\n    macro_rules! test_write_read {\n        ($id:expr, $desc:expr, $write:expr, $read_len:expr) => {{\n            info!(\"--- {} ---\", $id);\n            let mut buf = [0u8; $read_len];\n            match i2c.write_read(I2C_ADDR, $write, &mut buf).await {\n                Ok(()) => {\n                    if buf == EXPECTED_READ[..$read_len] {\n                        info!(\"[PASS] {}: {}\", $id, $desc);\n                        passed += 1;\n                    } else {\n                        error!(\n                            \"[FAIL] {}: got {:02X}, expected {:02X}\",\n                            $id,\n                            buf,\n                            &EXPECTED_READ[..$read_len]\n                        );\n                        failed += 1;\n                    }\n                }\n                Err(e) => {\n                    error!(\"[FAIL] {}: {:?}\", $id, e);\n                    failed += 1;\n                }\n            }\n            Timer::after_millis(50).await;\n        }};\n    }\n\n    // =========================================================================\n    // W1: Basic Write Operations\n    // =========================================================================\n    info!(\"========== W1: Basic Write Operations ==========\");\n    test_write!(\"W1.1\", \"Single byte write\", &[0x11]);\n    test_write!(\"W1.2\", \"4 bytes write\", &[0x11, 0x22, 0x33, 0x44]);\n    test_write!(\n        \"W1.3\",\n        \"8 bytes write\",\n        &[0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08]\n    );\n    test_write!(\n        \"W1.4\",\n        \"32 bytes write\",\n        &[\n            0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10, 0x11,\n            0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F,\n        ]\n    );\n\n    // =========================================================================\n    // W2: Early STOP (master sends less than buffer)\n    // =========================================================================\n    info!(\"========== W2: Early STOP ==========\");\n    test_write!(\"W2.1\", \"2 of 32 bytes\", &[0xAA, 0xBB]);\n    test_write!(\n        \"W2.2\",\n        \"10 of 32 bytes\",\n        &[0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A]\n    );\n    test_write!(\n        \"W2.3\",\n        \"31 of 32 bytes\",\n        &[\n            0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10, 0x11,\n            0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E,\n        ]\n    );\n    // W2.4 SKIPPED: Empty write uses blocking path, causes executor deadlock in loopback\n    info!(\"[SKIP] W2.4: Empty write - blocking path incompatible with loopback\");\n\n    // =========================================================================\n    // W3: Excess Data (master sends more than buffer)\n    // SKIPPED: drain_rxdr_until_stop() busy-waits, blocking master from sending STOP\n    // =========================================================================\n    info!(\"========== W3: Excess Data (SKIPPED) ==========\");\n    info!(\"[SKIP] W3.1-3: Excess data tests require external master\");\n\n    // =========================================================================\n    // R1: Basic Read Operations\n    // =========================================================================\n    info!(\"========== R1: Basic Read Operations ==========\");\n    test_read!(\"R1.1\", \"Read 1 byte\", 1);\n    test_read!(\"R1.2\", \"Read 4 bytes\", 4);\n    test_read!(\"R1.3\", \"Read 8 bytes\", 8);\n\n    // =========================================================================\n    // R2: Short Read (master reads less than slave offers)\n    // =========================================================================\n    info!(\"========== R2: Short Read ==========\");\n    test_read!(\"R2.1\", \"Read 2 of 8 bytes\", 2);\n    test_read!(\"R2.2\", \"Read 5 of 8 bytes\", 5);\n    test_read!(\"R2.3\", \"Read 7 of 8 bytes\", 7);\n\n    // =========================================================================\n    // M1: Mixed Operations\n    // =========================================================================\n    info!(\"========== M1: Mixed Operations ==========\");\n\n    // M1.1: Write then Read\n    info!(\"--- M1.1 ---\");\n    {\n        let mut ok = true;\n        if i2c.write(I2C_ADDR, &[0x11, 0x22, 0x33]).await.is_err() {\n            ok = false;\n        }\n        Timer::after_millis(50).await;\n        let mut buf = [0u8; 4];\n        if i2c.read(I2C_ADDR, &mut buf).await.is_err() || buf != EXPECTED_READ[..4] {\n            ok = false;\n        }\n        if ok {\n            info!(\"[PASS] M1.1: Write then Read\");\n            passed += 1;\n        } else {\n            error!(\"[FAIL] M1.1: Write then Read\");\n            failed += 1;\n        }\n    }\n    Timer::after_millis(50).await;\n\n    // M1.2: Read then Write\n    info!(\"--- M1.2 ---\");\n    {\n        let mut ok = true;\n        let mut buf = [0u8; 4];\n        if i2c.read(I2C_ADDR, &mut buf).await.is_err() || buf != EXPECTED_READ[..4] {\n            ok = false;\n        }\n        Timer::after_millis(50).await;\n        if i2c.write(I2C_ADDR, &[0xAA, 0xBB]).await.is_err() {\n            ok = false;\n        }\n        if ok {\n            info!(\"[PASS] M1.2: Read then Write\");\n            passed += 1;\n        } else {\n            error!(\"[FAIL] M1.2: Read then Write\");\n            failed += 1;\n        }\n    }\n    Timer::after_millis(50).await;\n\n    // M1.3: Multiple writes\n    info!(\"--- M1.3 ---\");\n    {\n        let mut ok = true;\n        for data in [&[0x11][..], &[0x22, 0x33], &[0x44, 0x55, 0x66]] {\n            if i2c.write(I2C_ADDR, data).await.is_err() {\n                ok = false;\n            }\n            Timer::after_millis(30).await;\n        }\n        if ok {\n            info!(\"[PASS] M1.3: Multiple writes\");\n            passed += 1;\n        } else {\n            error!(\"[FAIL] M1.3: Multiple writes\");\n            failed += 1;\n        }\n    }\n    Timer::after_millis(50).await;\n\n    // M1.4: Multiple reads\n    info!(\"--- M1.4 ---\");\n    {\n        let mut ok = true;\n        for len in [2usize, 4, 8] {\n            let mut buf = [0u8; 8];\n            if i2c.read(I2C_ADDR, &mut buf[..len]).await.is_err() || buf[..len] != EXPECTED_READ[..len] {\n                ok = false;\n            }\n            Timer::after_millis(30).await;\n        }\n        if ok {\n            info!(\"[PASS] M1.4: Multiple reads\");\n            passed += 1;\n        } else {\n            error!(\"[FAIL] M1.4: Multiple reads\");\n            failed += 1;\n        }\n    }\n    Timer::after_millis(50).await;\n\n    // =========================================================================\n    // S1: Stress Tests\n    // =========================================================================\n    info!(\"========== S1: Stress Tests ==========\");\n\n    // S1.1: 10x write same data\n    info!(\"--- S1.1 ---\");\n    {\n        let mut successes = 0;\n        for _ in 0..10 {\n            if i2c.write(I2C_ADDR, &[0x11, 0x22, 0x33, 0x44]).await.is_ok() {\n                successes += 1;\n            }\n            Timer::after_millis(20).await;\n        }\n        if successes == 10 {\n            info!(\"[PASS] S1.1: 10x write\");\n            passed += 1;\n        } else {\n            error!(\"[FAIL] S1.1: {}/10 writes\", successes);\n            failed += 1;\n        }\n    }\n    Timer::after_millis(50).await;\n\n    // S1.2: 10x read\n    info!(\"--- S1.2 ---\");\n    {\n        let mut successes = 0;\n        for _ in 0..10 {\n            let mut buf = [0u8; 4];\n            if i2c.read(I2C_ADDR, &mut buf).await.is_ok() && buf == EXPECTED_READ[..4] {\n                successes += 1;\n            }\n            Timer::after_millis(20).await;\n        }\n        if successes == 10 {\n            info!(\"[PASS] S1.2: 10x read\");\n            passed += 1;\n        } else {\n            error!(\"[FAIL] S1.2: {}/10 reads\", successes);\n            failed += 1;\n        }\n    }\n    Timer::after_millis(50).await;\n\n    // S1.3: Alternating W/R x10\n    info!(\"--- S1.3 ---\");\n    {\n        let mut successes = 0;\n        for i in 0..10u8 {\n            if i2c.write(I2C_ADDR, &[i]).await.is_ok() {\n                Timer::after_millis(20).await;\n                let mut buf = [0u8; 4];\n                if i2c.read(I2C_ADDR, &mut buf).await.is_ok() && buf == EXPECTED_READ[..4] {\n                    successes += 1;\n                }\n            }\n            Timer::after_millis(20).await;\n        }\n        if successes == 10 {\n            info!(\"[PASS] S1.3: 10x alternating W/R\");\n            passed += 1;\n        } else {\n            error!(\"[FAIL] S1.3: {}/10 pairs\", successes);\n            failed += 1;\n        }\n    }\n    Timer::after_millis(50).await;\n\n    // =========================================================================\n    // WR1: Write-Read with RESTART (varying write size)\n    // =========================================================================\n    info!(\"========== WR1: Write-Read RESTART ==========\");\n    test_write_read!(\"WR1.1\", \"Write 1, Read 4\", &[0x51], 4);\n    test_write_read!(\"WR1.2\", \"Write 2, Read 4\", &[0x61, 0x62], 4);\n    test_write_read!(\"WR1.3\", \"Write 4, Read 4\", &[0x71, 0x72, 0x73, 0x74], 4);\n    test_write_read!(\n        \"WR1.4\",\n        \"Write 8, Read 8\",\n        &[0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88],\n        8\n    );\n\n    // =========================================================================\n    // WR2: Write-Read with RESTART (varying read size)\n    // =========================================================================\n    info!(\"========== WR2: Write-Read varying read ==========\");\n    test_write_read!(\"WR2.1\", \"Write 1, Read 1\", &[0x91], 1);\n    test_write_read!(\"WR2.2\", \"Write 1, Read 8\", &[0xA1], 8);\n    test_write_read!(\"WR2.3\", \"Write 1, Read 2\", &[0xB1], 2);\n\n    // =========================================================================\n    // WR3: Multiple Write-Read Operations\n    // =========================================================================\n    info!(\"========== WR3: Multiple Write-Read ==========\");\n\n    // WR3.1: 5x consecutive write_read\n    info!(\"--- WR3.1 ---\");\n    {\n        let mut successes = 0;\n        for i in 0..5u8 {\n            let mut buf = [0u8; 4];\n            if i2c.write_read(I2C_ADDR, &[0x10 + i], &mut buf).await.is_ok() && buf == EXPECTED_READ[..4] {\n                successes += 1;\n            }\n            Timer::after_millis(30).await;\n        }\n        if successes == 5 {\n            info!(\"[PASS] WR3.1: 5x write_read\");\n            passed += 1;\n        } else {\n            error!(\"[FAIL] WR3.1: {}/5\", successes);\n            failed += 1;\n        }\n    }\n    Timer::after_millis(50).await;\n\n    // WR3.2: 10x consecutive write_read\n    info!(\"--- WR3.2 ---\");\n    {\n        let mut successes = 0;\n        for i in 0..10u8 {\n            let mut buf = [0u8; 4];\n            if i2c.write_read(I2C_ADDR, &[0x20 + i], &mut buf).await.is_ok() && buf == EXPECTED_READ[..4] {\n                successes += 1;\n            }\n            Timer::after_millis(20).await;\n        }\n        if successes == 10 {\n            info!(\"[PASS] WR3.2: 10x write_read\");\n            passed += 1;\n        } else {\n            error!(\"[FAIL] WR3.2: {}/10\", successes);\n            failed += 1;\n        }\n    }\n    Timer::after_millis(50).await;\n\n    // WR3.3: Alternating write_read and separate write+read\n    info!(\"--- WR3.3 ---\");\n    {\n        let mut ok = true;\n\n        // write_read (RESTART)\n        let mut buf = [0u8; 4];\n        if i2c.write_read(I2C_ADDR, &[0x30], &mut buf).await.is_err() || buf != EXPECTED_READ[..4] {\n            ok = false;\n        }\n        Timer::after_millis(30).await;\n\n        // Separate write + read (STOP between)\n        if i2c.write(I2C_ADDR, &[0x31, 0x32]).await.is_err() {\n            ok = false;\n        }\n        Timer::after_millis(30).await;\n        let mut buf = [0u8; 4];\n        if i2c.read(I2C_ADDR, &mut buf).await.is_err() || buf != EXPECTED_READ[..4] {\n            ok = false;\n        }\n        Timer::after_millis(30).await;\n\n        // write_read again\n        let mut buf = [0u8; 4];\n        if i2c.write_read(I2C_ADDR, &[0x33], &mut buf).await.is_err() || buf != EXPECTED_READ[..4] {\n            ok = false;\n        }\n\n        if ok {\n            info!(\"[PASS] WR3.3: Mixed RESTART/STOP\");\n            passed += 1;\n        } else {\n            error!(\"[FAIL] WR3.3: Mixed RESTART/STOP\");\n            failed += 1;\n        }\n    }\n    Timer::after_millis(50).await;\n\n    // =========================================================================\n    // TEST SUMMARY\n    // =========================================================================\n    info!(\"\");\n    info!(\"==============================================\");\n    info!(\"TOTAL: {} passed, {} failed, {} skipped\", passed, failed, skipped);\n    if failed == 0 {\n        info!(\"ALL TESTS PASSED!\");\n    } else {\n        error!(\"{} TESTS FAILED\", failed);\n    }\n    info!(\"==============================================\");\n\n    loop {\n        Timer::after_millis(1000).await;\n    }\n}\n"
  },
  {
    "path": "examples/stm32f1/.cargo/config.toml",
    "content": "[target.'cfg(all(target_arch = \"arm\", target_os = \"none\"))']\n# replace STM32F103C8 with your chip as listed in `probe-rs chip list`\nrunner = \"probe-rs run --chip STM32F103C8\"\n\n[build]\ntarget = \"thumbv7m-none-eabi\"\n\n[env]\nDEFMT_LOG = \"trace\"\n"
  },
  {
    "path": "examples/stm32f1/Cargo.toml",
    "content": "[package]\nedition = \"2024\"\nname = \"embassy-stm32f1-examples\"\nversion = \"0.1.0\"\nlicense = \"MIT OR Apache-2.0\"\npublish = false\n\n[dependencies]\n# Change stm32f103c8 to your chip name, if necessary.\nembassy-stm32 = { version = \"0.6.0\", path = \"../../embassy-stm32\", features = [ \"defmt\", \"stm32f103c8\", \"unstable-pac\", \"memory-x\", \"time-driver-any\" ]  }\nembassy-sync = { version = \"0.8.0\", path = \"../../embassy-sync\", features = [\"defmt\"] }\nembassy-executor = { version = \"0.10.0\", path = \"../../embassy-executor\", features = [\"platform-cortex-m\", \"executor-thread\", \"defmt\"] }\nembassy-time = { version = \"0.5.1\", path = \"../../embassy-time\", features = [\"defmt\", \"defmt-timestamp-uptime\", \"tick-hz-32_768\"] }\nembassy-usb = { version = \"0.6.0\", path = \"../../embassy-usb\", features = [\"defmt\"] }\nembassy-futures = { version = \"0.1.2\", path = \"../../embassy-futures\" }\n\ndefmt = \"1.0.1\"\ndefmt-rtt = \"1.0.0\"\n\ncortex-m = { version = \"0.7.6\", features = [\"inline-asm\", \"critical-section-single-core\"] }\ncortex-m-rt = \"0.7.0\"\nembedded-hal = \"0.2.6\"\npanic-probe = { version = \"1.0.0\", features = [\"print-defmt\"] }\nheapless = { version = \"0.9\", default-features = false }\nnb = \"1.0.0\"\nstatic_cell = \"2.0.0\"\n\n[profile.dev]\nopt-level = \"s\"\n\n[profile.release]\ndebug = 2\n\n[package.metadata.embassy]\nbuild = [\n  { target = \"thumbv7m-none-eabi\", artifact-dir = \"out/examples/stm32f1\" }\n]\n"
  },
  {
    "path": "examples/stm32f1/build.rs",
    "content": "fn main() {\n    println!(\"cargo:rustc-link-arg-bins=--nmagic\");\n    println!(\"cargo:rustc-link-arg-bins=-Tlink.x\");\n    println!(\"cargo:rustc-link-arg-bins=-Tdefmt.x\");\n}\n"
  },
  {
    "path": "examples/stm32f1/src/bin/adc.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_stm32::adc::{Adc, SampleTime};\nuse embassy_stm32::peripherals::ADC1;\nuse embassy_stm32::{adc, bind_interrupts};\nuse embassy_time::Timer;\nuse {defmt_rtt as _, panic_probe as _};\n\nbind_interrupts!(struct Irqs {\n    ADC1_2 => adc::InterruptHandler<ADC1>;\n});\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let p = embassy_stm32::init(Default::default());\n    info!(\"Hello World!\");\n\n    let mut adc = Adc::new(p.ADC1);\n    let mut pin = p.PB1;\n\n    let mut vrefint = adc.enable_vref();\n    let vrefint_sample = adc.read(&mut vrefint, SampleTime::CYCLES13_5).await;\n    let convert_to_millivolts = |sample| {\n        // From http://www.st.com/resource/en/datasheet/CD00161566.pdf\n        // 5.3.4 Embedded reference voltage\n        const VREFINT_MV: u32 = 1200; // mV\n\n        (u32::from(sample) * VREFINT_MV / u32::from(vrefint_sample)) as u16\n    };\n\n    loop {\n        let v = adc.read(&mut pin, SampleTime::CYCLES13_5).await;\n        info!(\"--> {} - {} mV\", v, convert_to_millivolts(v));\n        Timer::after_millis(100).await;\n    }\n}\n"
  },
  {
    "path": "examples/stm32f1/src/bin/blinky.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_stm32::gpio::{Level, Output, Speed};\nuse embassy_time::Timer;\nuse {defmt_rtt as _, panic_probe as _};\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let p = embassy_stm32::init(Default::default());\n    info!(\"Hello World!\");\n\n    let mut led = Output::new(p.PC13, Level::High, Speed::Low);\n\n    loop {\n        info!(\"high\");\n        led.set_high();\n        Timer::after_millis(300).await;\n\n        info!(\"low\");\n        led.set_low();\n        Timer::after_millis(300).await;\n    }\n}\n"
  },
  {
    "path": "examples/stm32f1/src/bin/can.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_stm32::can::frame::Envelope;\nuse embassy_stm32::can::{\n    Can, Fifo, Frame, Id, Rx0InterruptHandler, Rx1InterruptHandler, SceInterruptHandler, StandardId,\n    TxInterruptHandler, filter,\n};\nuse embassy_stm32::peripherals::CAN;\nuse embassy_stm32::{Config, bind_interrupts};\nuse static_cell::StaticCell;\nuse {defmt_rtt as _, panic_probe as _};\n\nbind_interrupts!(struct Irqs {\n    USB_LP_CAN1_RX0 => Rx0InterruptHandler<CAN>;\n    CAN1_RX1 => Rx1InterruptHandler<CAN>;\n    CAN1_SCE => SceInterruptHandler<CAN>;\n    USB_HP_CAN1_TX => TxInterruptHandler<CAN>;\n});\n\n// This example is configured to work with real CAN transceivers on B8/B9.\n// See other examples for loopback.\n\nfn handle_frame(env: Envelope, read_mode: &str) {\n    match env.frame.id() {\n        Id::Extended(id) => {\n            defmt::println!(\n                \"{} Extended Frame id={:x} {:02x}\",\n                read_mode,\n                id.as_raw(),\n                env.frame.data()\n            );\n        }\n        Id::Standard(id) => {\n            defmt::println!(\n                \"{} Standard Frame id={:x} {:02x}\",\n                read_mode,\n                id.as_raw(),\n                env.frame.data()\n            );\n        }\n    }\n}\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let p = embassy_stm32::init(Config::default());\n\n    // Set alternate pin mapping to B8/B9\n    embassy_stm32::pac::AFIO.mapr().modify(|w| w.set_can1_remap(2));\n\n    static RX_BUF: StaticCell<embassy_stm32::can::RxBuf<10>> = StaticCell::new();\n    static TX_BUF: StaticCell<embassy_stm32::can::TxBuf<10>> = StaticCell::new();\n\n    let mut can = Can::new(p.CAN, p.PB8, p.PB9, Irqs);\n\n    can.modify_filters()\n        .enable_bank(0, Fifo::Fifo0, filter::Mask32::accept_all());\n\n    can.modify_config()\n        .set_loopback(false)\n        .set_silent(false)\n        .set_bitrate(250_000);\n\n    can.enable().await;\n    let mut i: u8 = 0;\n\n    /*\n       // Example for using buffered Tx and Rx without needing to\n       // split first as is done below.\n       let mut can = can.buffered(\n           TX_BUF.init(embassy_stm32::can::TxBuf::<10>::new()),\n           RX_BUF.init(embassy_stm32::can::RxBuf::<10>::new()));\n       loop {\n           let tx_frame = Frame::new_data(unwrap!(StandardId::new(i as _)), &[i, 0, 1, 2, 3, 4, 5, 6]).unwrap();\n           can.write(&tx_frame).await;\n\n           match can.read().await {\n               Ok((frame, ts)) => {\n                   handle_frame(Envelope { ts, frame }, \"Buf\");\n               }\n               Err(err) => {\n                   defmt::println!(\"Error {}\", err);\n               }\n           }\n           i = i.wrapping_add(1);\n       }\n\n    */\n    let (mut tx, mut rx) = can.split();\n\n    // This example shows using the wait_not_empty API before try read\n    while i < 3 {\n        let tx_frame = Frame::new_data(unwrap!(StandardId::new(i as _)), &[i, 0, 1, 2, 3, 4, 5, 6]).unwrap();\n        tx.write(&tx_frame).await;\n\n        rx.wait_not_empty().await;\n        let env = rx.try_read().unwrap();\n        handle_frame(env, \"Wait\");\n        i += 1;\n    }\n\n    // This example shows using the full async non-buffered API\n    while i < 6 {\n        let tx_frame = Frame::new_data(unwrap!(StandardId::new(i as _)), &[i, 0, 1, 2, 3, 4, 5, 6]).unwrap();\n        tx.write(&tx_frame).await;\n\n        match rx.read().await {\n            Ok(env) => {\n                handle_frame(env, \"NoBuf\");\n            }\n            Err(err) => {\n                defmt::println!(\"Error {}\", err);\n            }\n        }\n        i += 1;\n    }\n\n    // This example shows using buffered RX and TX. User passes in desired buffer (size)\n    // It's possible this way to have just RX or TX buffered.\n    let mut rx = rx.buffered(RX_BUF.init(embassy_stm32::can::RxBuf::<10>::new()));\n    let mut tx = tx.buffered(TX_BUF.init(embassy_stm32::can::TxBuf::<10>::new()));\n\n    loop {\n        let tx_frame = Frame::new_data(unwrap!(StandardId::new(i as _)), &[i, 0, 1, 2, 3, 4, 5, 6]).unwrap();\n        tx.write(&tx_frame).await;\n\n        match rx.read().await {\n            Ok(envelope) => {\n                handle_frame(envelope, \"Buf\");\n            }\n            Err(err) => {\n                defmt::println!(\"Error {}\", err);\n            }\n        }\n        i = i.wrapping_add(1);\n    }\n}\n"
  },
  {
    "path": "examples/stm32f1/src/bin/hello.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse defmt::info;\nuse embassy_executor::Spawner;\nuse embassy_stm32::Config;\nuse embassy_time::Timer;\nuse {defmt_rtt as _, panic_probe as _};\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) -> ! {\n    let config = Config::default();\n    let _p = embassy_stm32::init(config);\n\n    loop {\n        info!(\"Hello World!\");\n        Timer::after_secs(1).await;\n    }\n}\n"
  },
  {
    "path": "examples/stm32f1/src/bin/input_capture.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_stm32::gpio::{AfioRemap, Level, Output, Pull, Speed};\nuse embassy_stm32::time::khz;\nuse embassy_stm32::timer::input_capture::{CapturePin, InputCapture};\nuse embassy_stm32::timer::{self, Channel};\nuse embassy_stm32::{Peri, bind_interrupts, peripherals};\nuse embassy_time::Timer;\nuse {defmt_rtt as _, panic_probe as _};\n\n/// Connect PA2 and PC13 with a 1k Ohm resistor\n\n#[embassy_executor::task]\nasync fn blinky(led: Peri<'static, peripherals::PC13>) {\n    let mut led = Output::new(led, Level::High, Speed::Low);\n\n    loop {\n        info!(\"high\");\n        led.set_high();\n        Timer::after_millis(300).await;\n\n        info!(\"low\");\n        led.set_low();\n        Timer::after_millis(300).await;\n    }\n}\n\nbind_interrupts!(struct Irqs {\n    TIM2 => timer::CaptureCompareInterruptHandler<peripherals::TIM2>;\n});\n\n#[embassy_executor::main]\nasync fn main(spawner: Spawner) {\n    let p = embassy_stm32::init(Default::default());\n    info!(\"Hello World!\");\n\n    spawner.spawn(unwrap!(blinky(p.PC13)));\n\n    let ch3 = CapturePin::new(p.PA2, Pull::None);\n    let mut ic =\n        InputCapture::new::<AfioRemap<0>>(p.TIM2, None, None, Some(ch3), None, Irqs, khz(1000), Default::default());\n\n    loop {\n        info!(\"wait for rising edge\");\n        ic.wait_for_rising_edge(Channel::Ch3).await;\n\n        let capture_value = ic.get_capture_value(Channel::Ch3);\n        info!(\"new capture! {}\", capture_value);\n    }\n}\n"
  },
  {
    "path": "examples/stm32f1/src/bin/pwm_input.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_stm32::gpio::{AfioRemap, Level, Output, Pull, Speed};\nuse embassy_stm32::time::khz;\nuse embassy_stm32::timer::pwm_input::PwmInput;\nuse embassy_stm32::{Peri, bind_interrupts, peripherals, timer};\nuse embassy_time::Timer;\nuse {defmt_rtt as _, panic_probe as _};\n\n/// Connect PA0 and PC13 with a 1k Ohm resistor\n\n#[embassy_executor::task]\nasync fn blinky(led: Peri<'static, peripherals::PC13>) {\n    let mut led = Output::new(led, Level::High, Speed::Low);\n\n    loop {\n        info!(\"high\");\n        led.set_high();\n        Timer::after_millis(300).await;\n\n        info!(\"low\");\n        led.set_low();\n        Timer::after_millis(300).await;\n    }\n}\n\nbind_interrupts!(struct Irqs {\n    TIM2 => timer::CaptureCompareInterruptHandler<peripherals::TIM2>;\n});\n\n#[embassy_executor::main]\nasync fn main(spawner: Spawner) {\n    let p = embassy_stm32::init(Default::default());\n    info!(\"Hello World!\");\n\n    spawner.spawn(unwrap!(blinky(p.PC13)));\n\n    let mut pwm_input = PwmInput::new_ch1::<AfioRemap<0>>(p.TIM2, p.PA0, Irqs, Pull::None, khz(10));\n    pwm_input.enable();\n\n    loop {\n        Timer::after_millis(500).await;\n        let period = pwm_input.get_period_ticks();\n        let width = pwm_input.get_width_ticks();\n        let duty_cycle = pwm_input.get_duty_cycle();\n        info!(\n            \"period ticks: {} width ticks: {} duty cycle: {}\",\n            period, width, duty_cycle\n        );\n    }\n}\n"
  },
  {
    "path": "examples/stm32f1/src/bin/usb_serial.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse defmt::{panic, *};\nuse embassy_executor::Spawner;\nuse embassy_futures::join::join;\nuse embassy_stm32::gpio::{Level, Output, Speed};\nuse embassy_stm32::time::Hertz;\nuse embassy_stm32::usb::{Driver, Instance};\nuse embassy_stm32::{Config, bind_interrupts, peripherals, usb};\nuse embassy_time::Timer;\nuse embassy_usb::Builder;\nuse embassy_usb::class::cdc_acm::{CdcAcmClass, State};\nuse embassy_usb::driver::EndpointError;\nuse {defmt_rtt as _, panic_probe as _};\n\nbind_interrupts!(struct Irqs {\n    USB_LP_CAN1_RX0 => usb::InterruptHandler<peripherals::USB>;\n});\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let mut config = Config::default();\n    {\n        use embassy_stm32::rcc::*;\n        config.rcc.hse = Some(Hse {\n            freq: Hertz(8_000_000),\n            // Oscillator for bluepill, Bypass for nucleos.\n            mode: HseMode::Oscillator,\n        });\n        config.rcc.pll = Some(Pll {\n            src: PllSource::HSE,\n            prediv: PllPreDiv::DIV1,\n            mul: PllMul::MUL9,\n        });\n        config.rcc.sys = Sysclk::PLL1_P;\n        config.rcc.ahb_pre = AHBPrescaler::DIV1;\n        config.rcc.apb1_pre = APBPrescaler::DIV2;\n        config.rcc.apb2_pre = APBPrescaler::DIV1;\n    }\n    let mut p = embassy_stm32::init(config);\n\n    info!(\"Hello World!\");\n\n    {\n        // BluePill board has a pull-up resistor on the D+ line.\n        // Pull the D+ pin down to send a RESET condition to the USB bus.\n        // This forced reset is needed only for development, without it host\n        // will not reset your device when you upload new firmware.\n        let _dp = Output::new(p.PA12.reborrow(), Level::Low, Speed::Low);\n        Timer::after_millis(10).await;\n    }\n\n    // Create the driver, from the HAL.\n    let driver = Driver::new(p.USB, Irqs, p.PA12, p.PA11);\n\n    // Create embassy-usb Config\n    let config = embassy_usb::Config::new(0xc0de, 0xcafe);\n    //config.max_packet_size_0 = 64;\n\n    // Create embassy-usb DeviceBuilder using the driver and config.\n    // It needs some buffers for building the descriptors.\n    let mut config_descriptor = [0; 256];\n    let mut bos_descriptor = [0; 256];\n    let mut control_buf = [0; 7];\n\n    let mut state = State::new();\n\n    let mut builder = Builder::new(\n        driver,\n        config,\n        &mut config_descriptor,\n        &mut bos_descriptor,\n        &mut [], // no msos descriptors\n        &mut control_buf,\n    );\n\n    // Create classes on the builder.\n    let mut class = CdcAcmClass::new(&mut builder, &mut state, 64);\n\n    // Build the builder.\n    let mut usb = builder.build();\n\n    // Run the USB device.\n    let usb_fut = usb.run();\n\n    // Do stuff with the class!\n    let echo_fut = async {\n        loop {\n            class.wait_connection().await;\n            info!(\"Connected\");\n            let _ = echo(&mut class).await;\n            info!(\"Disconnected\");\n        }\n    };\n\n    // Run everything concurrently.\n    // If we had made everything `'static` above instead, we could do this using separate tasks instead.\n    join(usb_fut, echo_fut).await;\n}\n\nstruct Disconnected {}\n\nimpl From<EndpointError> for Disconnected {\n    fn from(val: EndpointError) -> Self {\n        match val {\n            EndpointError::BufferOverflow => panic!(\"Buffer overflow\"),\n            EndpointError::Disabled => Disconnected {},\n        }\n    }\n}\n\nasync fn echo<'d, T: Instance + 'd>(class: &mut CdcAcmClass<'d, Driver<'d, T>>) -> Result<(), Disconnected> {\n    let mut buf = [0; 64];\n    loop {\n        let n = class.read_packet(&mut buf).await?;\n        let data = &buf[..n];\n        info!(\"data: {:x}\", data);\n        class.write_packet(data).await?;\n    }\n}\n"
  },
  {
    "path": "examples/stm32f105/.cargo/config.toml",
    "content": "[target.'cfg(all(target_arch = \"arm\", target_os = \"none\"))']\n# replace STM32F105VB with your chip as listed in `probe-rs chip list`\nrunner = \"probe-rs run --chip STM32F105VB\"\n\n[build]\ntarget = \"thumbv7m-none-eabi\"\n\n[env]\nDEFMT_LOG = \"trace\"\n"
  },
  {
    "path": "examples/stm32f105/Cargo.toml",
    "content": "[package]\nedition = \"2024\"\nname = \"embassy-stm32f105-examples\"\nversion = \"0.1.0\"\nlicense = \"MIT OR Apache-2.0\"\npublish = false\n\n[dependencies]\n# Change stm32f105vb to your chip name, if necessary.\nembassy-stm32 = { version = \"0.6.0\", path = \"../../embassy-stm32\", features = [ \"defmt\", \"stm32f105vb\", \"unstable-pac\", \"memory-x\", \"time-driver-any\" ]  }\nembassy-executor = { version = \"0.10.0\", path = \"../../embassy-executor\", features = [\"platform-cortex-m\", \"executor-thread\", \"defmt\"] }\nembassy-usb = { version = \"0.6.0\", path = \"../../embassy-usb\", features = [\"defmt\"] }\nembassy-futures = { version = \"0.1.2\", path = \"../../embassy-futures\" }\n\ndefmt = \"1.0.1\"\ndefmt-rtt = \"1.0.0\"\n\ncortex-m = { version = \"0.7.6\", features = [\"inline-asm\", \"critical-section-single-core\"] }\ncortex-m-rt = \"0.7.0\"\nembedded-hal = \"0.2.6\"\npanic-probe = { version = \"1.0.0\", features = [\"print-defmt\"] }\nheapless = { version = \"0.9\", default-features = false }\nnb = \"1.0.0\"\nstatic_cell = \"2.0.0\"\n\n[profile.dev]\nopt-level = \"s\"\n\n[profile.release]\ndebug = 2\n\n[package.metadata.embassy]\nbuild = [\n  { target = \"thumbv7m-none-eabi\", artifact-dir = \"out/examples/stm32f105\" }\n]\n"
  },
  {
    "path": "examples/stm32f105/build.rs",
    "content": "fn main() {\n    println!(\"cargo:rustc-link-arg-bins=--nmagic\");\n    println!(\"cargo:rustc-link-arg-bins=-Tlink.x\");\n    println!(\"cargo:rustc-link-arg-bins=-Tdefmt.x\");\n}\n"
  },
  {
    "path": "examples/stm32f105/src/bin/usb_serial.rs",
    "content": "#![no_std]\n#![no_main]\n\n// Adapted from examples/stm32f4/src/bin/usb_serial.rs for stm32f105\n\nuse defmt::{panic, *};\nuse embassy_executor::Spawner;\nuse embassy_futures::join::join;\nuse embassy_stm32::time::Hertz;\nuse embassy_stm32::usb::{self, Driver, Instance};\nuse embassy_stm32::{Config, bind_interrupts, peripherals};\nuse embassy_usb::Builder;\nuse embassy_usb::class::cdc_acm::{CdcAcmClass, State};\nuse embassy_usb::driver::EndpointError;\nuse {defmt_rtt as _, panic_probe as _};\n\nbind_interrupts!(struct Irqs {\n    OTG_FS => usb::InterruptHandler<peripherals::USB_OTG_FS>;\n});\n\n// If you are trying this and your USB device doesn't connect, the most\n// common issues are the RCC config and vbus_detection\n//\n// See https://embassy.dev/book/#_the_usb_examples_are_not_working_on_my_board_is_there_anything_else_i_need_to_configure\n// for more information.\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    info!(\"Hello World!\");\n\n    let mut config = Config::default();\n    {\n        // Configure clock tree\n        // PLL2: HSE 25MHz -> PREDIV2 (/5) = 5MHz -> PLL2MUL (x8) = 40MHz\n        // PLL: PLL2 40MHz -> PREDIV1 (/5) = 8MHz -> PLLMUL (x9) = 72MHz = SYSCLK\n        // PLLVCO (2x PLLCLK = 144MHz) -> USB prescaler (/3) -> 48MHz = OTGFSCLK\n        use embassy_stm32::rcc::*;\n        config.rcc.hse = Some(Hse {\n            freq: Hertz::mhz(25),\n            mode: HseMode::Oscillator,\n        });\n        config.rcc.pll = Some(Pll {\n            src: PllSource::PLL2,\n            prediv: PllPreDiv::DIV5,\n            mul: PllMul::MUL9,\n        });\n        config.rcc.prediv2 = PllPreDiv::DIV5;\n        config.rcc.pll2 = Some(Pll2Or3 { mul: Pll2Mul::MUL8 });\n        config.rcc.ahb_pre = AHBPrescaler::DIV1;\n        config.rcc.apb1_pre = APBPrescaler::DIV2;\n        config.rcc.apb2_pre = APBPrescaler::DIV2;\n        config.rcc.sys = Sysclk::PLL1_P;\n    }\n\n    let p = embassy_stm32::init(config);\n    info!(\"Initialized Embassy!\");\n\n    // Create the driver, from the HAL.\n    let mut ep_out_buffer = [0u8; 256];\n    let mut config = embassy_stm32::usb::Config::default();\n\n    // Do not enable vbus_detection. This is a safe default that works in all boards.\n    // However, if your USB device is self-powered (can stay powered on if USB is unplugged), you need\n    // to enable vbus_detection to comply with the USB spec. If you enable it, the board\n    // has to support it or USB won't work at all. See docs on `vbus_detection` for details.\n    config.vbus_detection = false;\n\n    let driver = Driver::new_fs(p.USB_OTG_FS, Irqs, p.PA12, p.PA11, &mut ep_out_buffer, config);\n\n    // Create embassy-usb Config\n    let mut config = embassy_usb::Config::new(0xc0de, 0xcafe);\n    config.manufacturer = Some(\"Embassy\");\n    config.product = Some(\"USB-serial example\");\n    config.serial_number = Some(\"12345678\");\n\n    // Create embassy-usb DeviceBuilder using the driver and config.\n    // It needs some buffers for building the descriptors.\n    let mut config_descriptor = [0; 256];\n    let mut bos_descriptor = [0; 256];\n    let mut control_buf = [0; 64];\n\n    let mut state = State::new();\n\n    let mut builder = Builder::new(\n        driver,\n        config,\n        &mut config_descriptor,\n        &mut bos_descriptor,\n        &mut [], // no msos descriptors\n        &mut control_buf,\n    );\n\n    // Create classes on the builder.\n    let mut class = CdcAcmClass::new(&mut builder, &mut state, 64);\n\n    // Build the builder.\n    let mut usb = builder.build();\n\n    // Run the USB device.\n    let usb_fut = usb.run();\n\n    // Do stuff with the class!\n    let echo_fut = async {\n        loop {\n            class.wait_connection().await;\n            info!(\"Connected\");\n            let _ = echo(&mut class).await;\n            info!(\"Disconnected\");\n        }\n    };\n\n    // Run everything concurrently.\n    // If we had made everything `'static` above instead, we could do this using separate tasks instead.\n    join(usb_fut, echo_fut).await;\n}\n\nstruct Disconnected {}\n\nimpl From<EndpointError> for Disconnected {\n    fn from(val: EndpointError) -> Self {\n        match val {\n            EndpointError::BufferOverflow => panic!(\"Buffer overflow\"),\n            EndpointError::Disabled => Disconnected {},\n        }\n    }\n}\n\nasync fn echo<'d, T: Instance + 'd>(class: &mut CdcAcmClass<'d, Driver<'d, T>>) -> Result<(), Disconnected> {\n    let mut buf = [0; 64];\n    loop {\n        let n = class.read_packet(&mut buf).await?;\n        let data = &buf[..n];\n        info!(\"data: {:x}\", data);\n        class.write_packet(data).await?;\n    }\n}\n"
  },
  {
    "path": "examples/stm32f107/.cargo/config.toml",
    "content": "[target.'cfg(all(target_arch = \"arm\", target_os = \"none\"))']\n# replace STM32F107RC with your chip as listed in `probe-rs chip list`\nrunner = \"probe-rs run --chip STM32F107RC\"\n\n[build]\ntarget = \"thumbv7m-none-eabi\"\n\n[env]\nDEFMT_LOG = \"trace\"\n"
  },
  {
    "path": "examples/stm32f107/Cargo.toml",
    "content": "[package]\nedition = \"2024\"\nname = \"embassy-stm32f107-examples\"\nversion = \"0.1.0\"\nlicense = \"MIT OR Apache-2.0\"\npublish = false\n\n[dependencies]\n# Change stm32f107rc to your chip name, if necessary.\nembassy-stm32 = { version = \"0.6.0\", path = \"../../embassy-stm32\", features = [ \"defmt\", \"stm32f107rc\", \"unstable-pac\", \"memory-x\", \"time-driver-any\" ]  }\nembassy-executor = { version = \"0.10.0\", path = \"../../embassy-executor\", features = [\"platform-cortex-m\", \"executor-thread\", \"defmt\"] }\nembassy-net = { version = \"0.9.0\", path = \"../../embassy-net\", features = [\"defmt\", \"tcp\", \"dhcpv4\"] }\n\ndefmt = \"1.0.1\"\ndefmt-rtt = \"1.0.0\"\n\ncortex-m = { version = \"0.7.6\", features = [\"inline-asm\", \"critical-section-single-core\"] }\ncortex-m-rt = \"0.7.0\"\nembedded-hal = \"0.2.6\"\npanic-probe = { version = \"1.0.0\", features = [\"print-defmt\"] }\nheapless = { version = \"0.9\", default-features = false }\nnb = \"1.0.0\"\nstatic_cell = \"2.0.0\"\n\n[profile.dev]\nopt-level = \"s\"\n\n[profile.release]\ndebug = 2\n\n[package.metadata.embassy]\nbuild = [\n  { target = \"thumbv7m-none-eabi\", artifact-dir = \"out/examples/stm32f107\" }\n]\n"
  },
  {
    "path": "examples/stm32f107/build.rs",
    "content": "fn main() {\n    println!(\"cargo:rustc-link-arg-bins=--nmagic\");\n    println!(\"cargo:rustc-link-arg-bins=-Tlink.x\");\n    println!(\"cargo:rustc-link-arg-bins=-Tdefmt.x\");\n}\n"
  },
  {
    "path": "examples/stm32f107/src/bin/eth.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse defmt::{info, unwrap, warn};\nuse embassy_executor::Spawner;\nuse embassy_net::tcp::{self, TcpSocket};\nuse embassy_net::{IpListenEndpoint, StackResources};\nuse embassy_stm32::eth::{Ethernet, GenericPhy, PacketQueue, Sma};\nuse embassy_stm32::peripherals::{ETH, ETH_SMA};\nuse embassy_stm32::rcc::{\n    AHBPrescaler, APBPrescaler, Hse, HseMode, Pll, Pll2Mul, Pll2Or3, PllMul, PllPreDiv, PllSource, Sysclk,\n};\nuse embassy_stm32::time::Hertz;\nuse embassy_stm32::{Config, bind_interrupts, eth};\nuse static_cell::StaticCell;\nuse {defmt_rtt as _, panic_probe as _};\n\nbind_interrupts!(struct Irqs {\n    ETH => eth::InterruptHandler;\n});\n\ntype Device = Ethernet<'static, ETH, GenericPhy<Sma<'static, ETH_SMA>>>;\n\n#[embassy_executor::task]\nasync fn net_task(mut runner: embassy_net::Runner<'static, Device>) -> ! {\n    runner.run().await\n}\n\n#[embassy_executor::main]\nasync fn main(spawner: Spawner) -> ! {\n    let mut config = Config::default();\n    config.rcc.hse = Some(Hse {\n        freq: Hertz::mhz(25),\n        mode: HseMode::Oscillator,\n    });\n    config.rcc.pll = Some(Pll {\n        src: PllSource::PLL2,\n        prediv: PllPreDiv::DIV5,\n        mul: PllMul::MUL9,\n    });\n    config.rcc.prediv2 = PllPreDiv::DIV5;\n    config.rcc.pll2 = Some(Pll2Or3 { mul: Pll2Mul::MUL8 });\n    config.rcc.pll3 = Some(Pll2Or3 { mul: Pll2Mul::MUL10 });\n    config.rcc.ahb_pre = AHBPrescaler::DIV1;\n    config.rcc.apb1_pre = APBPrescaler::DIV2;\n    config.rcc.apb2_pre = APBPrescaler::DIV1;\n    config.rcc.sys = Sysclk::PLL1_P;\n\n    let p = embassy_stm32::init(config);\n    let mac_addr = [0x00, 0x00, 0xDE, 0xAD, 0xBE, 0xEF];\n    static PACKETS: StaticCell<PacketQueue<4, 4>> = StaticCell::new();\n    let device = Ethernet::new(\n        PACKETS.init(PacketQueue::<4, 4>::new()),\n        p.ETH,\n        Irqs,\n        p.PA1,\n        p.PA7,\n        p.PC4,\n        p.PC5,\n        p.PB12,\n        p.PB13,\n        p.PB11,\n        mac_addr,\n        p.ETH_SMA,\n        p.PA2,\n        p.PC1,\n    );\n\n    let config = embassy_net::Config::dhcpv4(Default::default());\n    // Use the following instead to set a static IPv4 address.\n    // let config = embassy_net::Config::ipv4_static(StaticConfigV4 {\n    //     address: Ipv4Cidr::new(Ipv4Address::new(10, 42, 0, 62), 24),\n    //     dns_servers: Vec::new(),\n    //     gateway: Some(Ipv4Address::new(10, 42, 0, 1)),\n    // });\n\n    static RESOURCES: StaticCell<StackResources<2>> = StaticCell::new();\n    let (stack, runner) = embassy_net::new(device, config, RESOURCES.init(StackResources::new()), 3249);\n    spawner.spawn(unwrap!(net_task(runner)));\n    stack.wait_config_up().await;\n\n    info!(\"Network task initialized\");\n\n    let mut rx_buffer = [0; 1024];\n    let mut tx_buffer = [0; 1024];\n\n    loop {\n        let mut socket = TcpSocket::new(stack, &mut rx_buffer, &mut tx_buffer);\n        unwrap!(socket.accept(IpListenEndpoint { addr: None, port: 80 }).await);\n\n        let mut read_buffer = [0; 1024];\n        loop {\n            match socket.read(&mut read_buffer).await {\n                Ok(0) => break,\n                Ok(bytes) => {\n                    info!(\"Received {} bytes: {:a}\", bytes, read_buffer[..bytes]);\n                    unwrap!(socket.write(&read_buffer[..bytes]).await);\n                }\n                Err(tcp::Error::ConnectionReset) => {\n                    warn!(\"Error: connection reset\");\n                    break;\n                }\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "examples/stm32f2/.cargo/config.toml",
    "content": "[target.'cfg(all(target_arch = \"arm\", target_os = \"none\"))']\n# replace STM32F207ZGTx with your chip as listed in `probe-rs chip list`\nrunner = \"probe-rs run --chip STM32F207ZGTx\"\n\n[build]\ntarget = \"thumbv7m-none-eabi\"\n\n[env]\nDEFMT_LOG = \"trace\"\n"
  },
  {
    "path": "examples/stm32f2/Cargo.toml",
    "content": "[package]\nedition = \"2024\"\nname = \"embassy-stm32f2-examples\"\nversion = \"0.1.0\"\nlicense = \"MIT OR Apache-2.0\"\npublish = false\n\n[dependencies]\n# Change stm32f207zg to your chip name, if necessary.\nembassy-stm32 = { version = \"0.6.0\", path = \"../../embassy-stm32\", features = [ \"defmt\", \"stm32f207zg\", \"unstable-pac\", \"memory-x\", \"time-driver-any\", \"exti\"]  }\nembassy-sync = { version = \"0.8.0\", path = \"../../embassy-sync\", features = [\"defmt\"] }\nembassy-executor = { version = \"0.10.0\", path = \"../../embassy-executor\", features = [\"platform-cortex-m\", \"executor-thread\", \"defmt\"] }\nembassy-time = { version = \"0.5.1\", path = \"../../embassy-time\", features = [\"defmt\", \"defmt-timestamp-uptime\", \"tick-hz-32_768\"] }\n\ndefmt = \"1.0.1\"\ndefmt-rtt = \"1.0.0\"\n\ncortex-m = { version = \"0.7.6\", features = [\"inline-asm\", \"critical-section-single-core\"] }\ncortex-m-rt = \"0.7.0\"\nembedded-hal = \"0.2.6\"\npanic-probe = { version = \"1.0.0\", features = [\"print-defmt\"] }\nheapless = { version = \"0.9\", default-features = false }\nnb = \"1.0.0\"\n\n[profile.release]\ndebug = 2\n\n[package.metadata.embassy]\nbuild = [\n  { target = \"thumbv7m-none-eabi\", artifact-dir = \"out/examples/stm32f2\" }\n]\n"
  },
  {
    "path": "examples/stm32f2/build.rs",
    "content": "fn main() {\n    println!(\"cargo:rustc-link-arg-bins=--nmagic\");\n    println!(\"cargo:rustc-link-arg-bins=-Tlink.x\");\n    println!(\"cargo:rustc-link-arg-bins=-Tdefmt.x\");\n}\n"
  },
  {
    "path": "examples/stm32f2/src/bin/blinky.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_stm32::gpio::{Level, Output, Speed};\nuse embassy_time::Timer;\nuse {defmt_rtt as _, panic_probe as _};\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let p = embassy_stm32::init(Default::default());\n    info!(\"Hello World!\");\n\n    let mut led = Output::new(p.PB14, Level::High, Speed::Low);\n\n    loop {\n        info!(\"high\");\n        led.set_high();\n        Timer::after_millis(1000).await;\n\n        info!(\"low\");\n        led.set_low();\n        Timer::after_millis(1000).await;\n    }\n}\n"
  },
  {
    "path": "examples/stm32f2/src/bin/pll.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_stm32::Config;\nuse embassy_stm32::time::Hertz;\nuse embassy_time::Timer;\nuse {defmt_rtt as _, panic_probe as _};\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    // Example config for maximum performance on a NUCLEO-F207ZG board\n\n    let mut config = Config::default();\n\n    {\n        use embassy_stm32::rcc::*;\n\n        // By default, HSE on the board comes from a 8 MHz clock signal (not a crystal)\n        config.rcc.hse = Some(Hse {\n            freq: Hertz(8_000_000),\n            mode: HseMode::Bypass,\n        });\n        // PLL uses HSE as the clock source\n        config.rcc.pll_src = PllSource::HSE;\n        config.rcc.pll = Some(Pll {\n            // 8 MHz clock source / 8 = 1 MHz PLL input\n            prediv: unwrap!(PllPreDiv::try_from(8)),\n            // 1 MHz PLL input * 240 = 240 MHz PLL VCO\n            mul: unwrap!(PllMul::try_from(240)),\n            // 240 MHz PLL VCO / 2 = 120 MHz main PLL output\n            divp: Some(PllPDiv::DIV2),\n            // 240 MHz PLL VCO / 5 = 48 MHz PLL48 output\n            divq: Some(PllQDiv::DIV5),\n            divr: None,\n        });\n        // System clock comes from PLL (= the 120 MHz main PLL output)\n        config.rcc.sys = Sysclk::PLL1_P;\n        // 120 MHz / 4 = 30 MHz APB1 frequency\n        config.rcc.apb1_pre = APBPrescaler::DIV4;\n        // 120 MHz / 2 = 60 MHz APB2 frequency\n        config.rcc.apb2_pre = APBPrescaler::DIV2;\n    }\n\n    let _p = embassy_stm32::init(config);\n\n    loop {\n        Timer::after_millis(1000).await;\n        info!(\"1s elapsed\");\n    }\n}\n"
  },
  {
    "path": "examples/stm32f3/.cargo/config.toml",
    "content": "[target.'cfg(all(target_arch = \"arm\", target_os = \"none\"))']\n# replace STM32F429ZITx with your chip as listed in `probe-rs chip list`\nrunner = \"probe-rs run --chip STM32F303ZETx\"\n\n[build]\ntarget = \"thumbv7em-none-eabihf\"\n\n[env]\nDEFMT_LOG = \"trace\"\n"
  },
  {
    "path": "examples/stm32f3/Cargo.toml",
    "content": "[package]\nedition = \"2024\"\nname = \"embassy-stm32f3-examples\"\nversion = \"0.1.0\"\nlicense = \"MIT OR Apache-2.0\"\npublish = false\n\n[dependencies]\n# Change stm32f303ze to your chip name, if necessary.\nembassy-stm32 = { version = \"0.6.0\", path = \"../../embassy-stm32\", features = [ \"defmt\", \"stm32f303ze\", \"unstable-pac\", \"memory-x\", \"time-driver-tim2\", \"exti\"]  }\nembassy-sync = { version = \"0.8.0\", path = \"../../embassy-sync\", features = [\"defmt\"] }\nembassy-executor = { version = \"0.10.0\", path = \"../../embassy-executor\", features = [\"platform-cortex-m\", \"executor-thread\", \"executor-interrupt\", \"defmt\"] }\nembassy-time = { version = \"0.5.1\", path = \"../../embassy-time\", features = [\"defmt\", \"defmt-timestamp-uptime\", \"tick-hz-32_768\"] }\nembassy-usb = { version = \"0.6.0\", path = \"../../embassy-usb\", features = [\"defmt\"] }\nembassy-futures = { version = \"0.1.2\", path = \"../../embassy-futures\" }\n\ndefmt = \"1.0.1\"\ndefmt-rtt = \"1.0.0\"\n\ncortex-m = { version = \"0.7.6\", features = [\"inline-asm\", \"critical-section-single-core\"] }\ncortex-m-rt = \"0.7.0\"\nembedded-hal = \"0.2.6\"\npanic-probe = { version = \"1.0.0\", features = [\"print-defmt\"] }\nheapless = { version = \"0.9\", default-features = false }\nnb = \"1.0.0\"\nembedded-storage = \"0.3.1\"\nstatic_cell = \"2\"\n\n[profile.release]\ndebug = 2\n\n[package.metadata.embassy]\nbuild = [\n  { target = \"thumbv7em-none-eabi\", artifact-dir = \"out/examples/stm32f3\" }\n]\n"
  },
  {
    "path": "examples/stm32f3/README.md",
    "content": "# Examples for STM32F3 family\nRun individual examples with\n```\ncargo run --bin <module-name>\n```\nfor example\n```\ncargo run --bin blinky\n```\n\n## Checklist before running examples\nYou might need to adjust `.cargo/config.toml`, `Cargo.toml` and possibly update pin numbers or peripherals to match the specific MCU or board you are using.\n\n* [ ] Update .cargo/config.toml with the correct probe-rs command to use your specific MCU. For example for F303ZE it should be `probe-rs run --chip STM32F303ZETx`. (use `probe-rs chip list` to find your chip)\n* [ ] Update Cargo.toml to have the correct `embassy-stm32` feature. For example for F303ZE it should be `stm32f303ze`. Look in the `Cargo.toml` file of the `embassy-stm32` project to find the correct feature flag for your chip.\n* [ ] If your board has a special clock or power configuration, make sure that it is set up appropriately.\n* [ ] If your board has different pin mapping, update any pin numbers or peripherals in the given example code to match your schematic\n\nIf you are unsure, please drop by the Embassy Matrix chat for support, and let us know:\n\n* Which example you are trying to run\n* Which chip and board you are using\n\nEmbassy Chat: https://matrix.to/#/#embassy-rs:matrix.org\n"
  },
  {
    "path": "examples/stm32f3/build.rs",
    "content": "fn main() {\n    println!(\"cargo:rustc-link-arg-bins=--nmagic\");\n    println!(\"cargo:rustc-link-arg-bins=-Tlink.x\");\n    println!(\"cargo:rustc-link-arg-bins=-Tdefmt.x\");\n}\n"
  },
  {
    "path": "examples/stm32f3/src/bin/blinky.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_stm32::gpio::{Level, Output, Speed};\nuse embassy_time::Timer;\nuse {defmt_rtt as _, panic_probe as _};\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let p = embassy_stm32::init(Default::default());\n    info!(\"Hello World!\");\n\n    let mut led = Output::new(p.PA5, Level::High, Speed::Low);\n\n    loop {\n        info!(\"high\");\n        led.set_high();\n        Timer::after_millis(1000).await;\n\n        info!(\"low\");\n        led.set_low();\n        Timer::after_millis(1000).await;\n    }\n}\n"
  },
  {
    "path": "examples/stm32f3/src/bin/button.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse cortex_m_rt::entry;\nuse defmt::*;\nuse embassy_stm32::gpio::{Input, Level, Output, Pull, Speed};\nuse {defmt_rtt as _, panic_probe as _};\n\n#[entry]\nfn main() -> ! {\n    info!(\"Hello World!\");\n\n    let p = embassy_stm32::init(Default::default());\n\n    let button = Input::new(p.PA0, Pull::Down);\n    let mut led1 = Output::new(p.PE9, Level::High, Speed::Low);\n    let mut led2 = Output::new(p.PE15, Level::High, Speed::Low);\n\n    loop {\n        if button.is_high() {\n            info!(\"high\");\n            led1.set_high();\n            led2.set_low();\n        } else {\n            info!(\"low\");\n            led1.set_low();\n            led2.set_high();\n        }\n    }\n}\n"
  },
  {
    "path": "examples/stm32f3/src/bin/button_events.rs",
    "content": "//! This example showcases channels and timing utilities of embassy.\n//!\n//! This example works best on STM32F3DISCOVERY board. It flashes a single LED, then if the USER\n//! button is pressed the next LED is flashed (in clockwise fashion). If the USER button is double\n//! clicked, then the direction changes. If the USER button is pressed for 1 second, then all the\n//! LEDS flash 3 times.\n//!\n\n#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_stm32::exti::{self, ExtiInput};\nuse embassy_stm32::gpio::{Level, Output, Pull, Speed};\nuse embassy_stm32::mode::Async;\nuse embassy_stm32::{bind_interrupts, interrupt};\nuse embassy_sync::blocking_mutex::raw::ThreadModeRawMutex;\nuse embassy_sync::channel::Channel;\nuse embassy_time::{Duration, Timer, with_timeout};\nuse {defmt_rtt as _, panic_probe as _};\n\nbind_interrupts!(\n    pub struct Irqs{\n        EXTI0 => exti::InterruptHandler<interrupt::typelevel::EXTI0>;\n});\n\nstruct Leds<'a> {\n    leds: [Output<'a>; 8],\n    direction: i8,\n    current_led: usize,\n}\n\nimpl<'a> Leds<'a> {\n    fn new(pins: [Output<'a>; 8]) -> Self {\n        Self {\n            leds: pins,\n            direction: 1,\n            current_led: 0,\n        }\n    }\n\n    fn change_direction(&mut self) {\n        self.direction *= -1;\n    }\n\n    fn move_next(&mut self) {\n        if self.direction > 0 {\n            self.current_led = (self.current_led + 1) & 7;\n        } else {\n            self.current_led = (8 + self.current_led - 1) & 7;\n        }\n    }\n\n    async fn show(&mut self) {\n        self.leds[self.current_led].set_high();\n        if let Ok(new_message) = with_timeout(Duration::from_millis(500), CHANNEL.receive()).await {\n            self.leds[self.current_led].set_low();\n            self.process_event(new_message).await;\n        } else {\n            self.leds[self.current_led].set_low();\n            if let Ok(new_message) = with_timeout(Duration::from_millis(200), CHANNEL.receive()).await {\n                self.process_event(new_message).await;\n            }\n        }\n    }\n\n    async fn flash(&mut self) {\n        for _ in 0..3 {\n            for led in &mut self.leds {\n                led.set_high();\n            }\n            Timer::after_millis(500).await;\n            for led in &mut self.leds {\n                led.set_low();\n            }\n            Timer::after_millis(200).await;\n        }\n    }\n\n    async fn process_event(&mut self, event: ButtonEvent) {\n        match event {\n            ButtonEvent::SingleClick => {\n                self.move_next();\n            }\n            ButtonEvent::DoubleClick => {\n                self.change_direction();\n                self.move_next();\n            }\n            ButtonEvent::Hold => {\n                self.flash().await;\n            }\n        }\n    }\n}\n\n#[derive(Format)]\nenum ButtonEvent {\n    SingleClick,\n    DoubleClick,\n    Hold,\n}\n\nstatic CHANNEL: Channel<ThreadModeRawMutex, ButtonEvent, 4> = Channel::new();\n\n#[embassy_executor::main]\nasync fn main(spawner: Spawner) {\n    let p = embassy_stm32::init(Default::default());\n    let button = ExtiInput::new(p.PA0, p.EXTI0, Pull::Down, Irqs);\n    info!(\"Press the USER button...\");\n    let leds = [\n        Output::new(p.PE9, Level::Low, Speed::Low),\n        Output::new(p.PE10, Level::Low, Speed::Low),\n        Output::new(p.PE11, Level::Low, Speed::Low),\n        Output::new(p.PE12, Level::Low, Speed::Low),\n        Output::new(p.PE13, Level::Low, Speed::Low),\n        Output::new(p.PE14, Level::Low, Speed::Low),\n        Output::new(p.PE15, Level::Low, Speed::Low),\n        Output::new(p.PE8, Level::Low, Speed::Low),\n    ];\n    let leds = Leds::new(leds);\n\n    spawner.spawn(button_waiter(button).unwrap());\n    spawner.spawn(led_blinker(leds).unwrap());\n}\n\n#[embassy_executor::task]\nasync fn led_blinker(mut leds: Leds<'static>) {\n    loop {\n        leds.show().await;\n    }\n}\n\n#[embassy_executor::task]\nasync fn button_waiter(mut button: ExtiInput<'static, Async>) {\n    const DOUBLE_CLICK_DELAY: u64 = 250;\n    const HOLD_DELAY: u64 = 1000;\n\n    button.wait_for_rising_edge().await;\n    loop {\n        if with_timeout(Duration::from_millis(HOLD_DELAY), button.wait_for_falling_edge())\n            .await\n            .is_err()\n        {\n            info!(\"Hold\");\n            CHANNEL.send(ButtonEvent::Hold).await;\n            button.wait_for_falling_edge().await;\n        } else if with_timeout(Duration::from_millis(DOUBLE_CLICK_DELAY), button.wait_for_rising_edge())\n            .await\n            .is_err()\n        {\n            info!(\"Single click\");\n            CHANNEL.send(ButtonEvent::SingleClick).await;\n        } else {\n            info!(\"Double click\");\n            CHANNEL.send(ButtonEvent::DoubleClick).await;\n            button.wait_for_falling_edge().await;\n        }\n        button.wait_for_rising_edge().await;\n    }\n}\n"
  },
  {
    "path": "examples/stm32f3/src/bin/button_exti.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_stm32::exti::{self, ExtiInput};\nuse embassy_stm32::gpio::Pull;\nuse embassy_stm32::{bind_interrupts, interrupt};\nuse {defmt_rtt as _, panic_probe as _};\n\nbind_interrupts!(\n    pub struct Irqs{\n        EXTI0 => exti::InterruptHandler<interrupt::typelevel::EXTI0>;\n});\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let p = embassy_stm32::init(Default::default());\n    info!(\"Hello World!\");\n\n    let mut button = ExtiInput::new(p.PA0, p.EXTI0, Pull::Down, Irqs);\n\n    info!(\"Press the USER button...\");\n\n    loop {\n        button.wait_for_rising_edge().await;\n        info!(\"Pressed!\");\n        button.wait_for_falling_edge().await;\n        info!(\"Released!\");\n    }\n}\n"
  },
  {
    "path": "examples/stm32f3/src/bin/flash.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse defmt::{info, unwrap};\nuse embassy_executor::Spawner;\nuse embassy_stm32::flash::Flash;\nuse {defmt_rtt as _, panic_probe as _};\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let p = embassy_stm32::init(Default::default());\n    info!(\"Hello Flash!\");\n\n    const ADDR: u32 = 0x26000;\n\n    let mut f = Flash::new_blocking(p.FLASH).into_blocking_regions().bank1_region;\n\n    info!(\"Reading...\");\n    let mut buf = [0u8; 8];\n    unwrap!(f.blocking_read(ADDR, &mut buf));\n    info!(\"Read: {=[u8]:x}\", buf);\n\n    info!(\"Erasing...\");\n    unwrap!(f.blocking_erase(ADDR, ADDR + 2048));\n\n    info!(\"Reading...\");\n    let mut buf = [0u8; 8];\n    unwrap!(f.blocking_read(ADDR, &mut buf));\n    info!(\"Read after erase: {=[u8]:x}\", buf);\n\n    info!(\"Writing...\");\n    unwrap!(f.blocking_write(ADDR, &[1, 2, 3, 4, 5, 6, 7, 8]));\n\n    info!(\"Reading...\");\n    let mut buf = [0u8; 8];\n    unwrap!(f.blocking_read(ADDR, &mut buf));\n    info!(\"Read: {=[u8]:x}\", buf);\n    assert_eq!(&buf[..], &[1, 2, 3, 4, 5, 6, 7, 8]);\n}\n"
  },
  {
    "path": "examples/stm32f3/src/bin/hello.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse defmt::info;\nuse embassy_executor::Spawner;\nuse embassy_stm32::Config;\nuse embassy_time::Timer;\nuse {defmt_rtt as _, panic_probe as _};\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) -> ! {\n    let config = Config::default();\n    let _p = embassy_stm32::init(config);\n\n    loop {\n        info!(\"Hello World!\");\n        Timer::after_secs(1).await;\n    }\n}\n"
  },
  {
    "path": "examples/stm32f3/src/bin/multiprio.rs",
    "content": "//! This example showcases how to create multiple Executor instances to run tasks at\n//! different priority levels.\n//!\n//! Low priority executor runs in thread mode (not interrupt), and uses `sev` for signaling\n//! there's work in the queue, and `wfe` for waiting for work.\n//!\n//! Medium and high priority executors run in two interrupts with different priorities.\n//! Signaling work is done by pending the interrupt. No \"waiting\" needs to be done explicitly, since\n//! when there's work the interrupt will trigger and run the executor.\n//!\n//! Sample output below. Note that high priority ticks can interrupt everything else, and\n//! medium priority computations can interrupt low priority computations, making them to appear\n//! to take significantly longer time.\n//!\n//! ```not_rust\n//!     [med] Starting long computation\n//!     [med] done in 992 ms\n//!         [high] tick!\n//! [low] Starting long computation\n//!     [med] Starting long computation\n//!         [high] tick!\n//!         [high] tick!\n//!     [med] done in 993 ms\n//!     [med] Starting long computation\n//!         [high] tick!\n//!         [high] tick!\n//!     [med] done in 993 ms\n//! [low] done in 3972 ms\n//!     [med] Starting long computation\n//!         [high] tick!\n//!         [high] tick!\n//!     [med] done in 993 ms\n//! ```\n//!\n//! For comparison, try changing the code so all 3 tasks get spawned on the low priority executor.\n//! You will get an output like the following. Note that no computation is ever interrupted.\n//!\n//! ```not_rust\n//!         [high] tick!\n//!     [med] Starting long computation\n//!     [med] done in 496 ms\n//! [low] Starting long computation\n//! [low] done in 992 ms\n//!     [med] Starting long computation\n//!     [med] done in 496 ms\n//!         [high] tick!\n//! [low] Starting long computation\n//! [low] done in 992 ms\n//!         [high] tick!\n//!     [med] Starting long computation\n//!     [med] done in 496 ms\n//!         [high] tick!\n//! ```\n//!\n\n#![no_std]\n#![no_main]\n\nuse cortex_m_rt::entry;\nuse defmt::*;\nuse embassy_executor::{Executor, InterruptExecutor};\nuse embassy_stm32::interrupt;\nuse embassy_stm32::interrupt::{InterruptExt, Priority};\nuse embassy_time::{Instant, Timer};\nuse static_cell::StaticCell;\nuse {defmt_rtt as _, panic_probe as _};\n\n#[embassy_executor::task]\nasync fn run_high() {\n    loop {\n        info!(\"        [high] tick!\");\n        Timer::after_ticks(27374).await;\n    }\n}\n\n#[embassy_executor::task]\nasync fn run_med() {\n    loop {\n        let start = Instant::now();\n        info!(\"    [med] Starting long computation\");\n\n        // Spin-wait to simulate a long CPU computation\n        embassy_time::block_for(embassy_time::Duration::from_secs(1)); // ~1 second\n\n        let end = Instant::now();\n        let ms = end.duration_since(start).as_ticks() / 33;\n        info!(\"    [med] done in {} ms\", ms);\n\n        Timer::after_ticks(23421).await;\n    }\n}\n\n#[embassy_executor::task]\nasync fn run_low() {\n    loop {\n        let start = Instant::now();\n        info!(\"[low] Starting long computation\");\n\n        // Spin-wait to simulate a long CPU computation\n        embassy_time::block_for(embassy_time::Duration::from_secs(2)); // ~2 seconds\n\n        let end = Instant::now();\n        let ms = end.duration_since(start).as_ticks() / 33;\n        info!(\"[low] done in {} ms\", ms);\n\n        Timer::after_ticks(32983).await;\n    }\n}\n\nstatic EXECUTOR_HIGH: InterruptExecutor = InterruptExecutor::new();\nstatic EXECUTOR_MED: InterruptExecutor = InterruptExecutor::new();\nstatic EXECUTOR_LOW: StaticCell<Executor> = StaticCell::new();\n\n#[interrupt]\nunsafe fn UART4() {\n    unsafe { EXECUTOR_HIGH.on_interrupt() }\n}\n\n#[interrupt]\nunsafe fn UART5() {\n    unsafe { EXECUTOR_MED.on_interrupt() }\n}\n\n#[entry]\nfn main() -> ! {\n    info!(\"Hello World!\");\n\n    let _p = embassy_stm32::init(Default::default());\n\n    // STM32s don’t have any interrupts exclusively for software use, but they can all be triggered by software as well as\n    // by the peripheral, so we can just use any free interrupt vectors which aren’t used by the rest of your application.\n    // In this case we’re using UART4 and UART5, but there’s nothing special about them. Any otherwise unused interrupt\n    // vector would work exactly the same.\n\n    // High-priority executor: UART4, priority level 6\n    interrupt::UART4.set_priority(Priority::P6);\n    let spawner = EXECUTOR_HIGH.start(interrupt::UART4);\n    spawner.spawn(unwrap!(run_high()));\n\n    // Medium-priority executor: UART5, priority level 7\n    interrupt::UART5.set_priority(Priority::P7);\n    let spawner = EXECUTOR_MED.start(interrupt::UART5);\n    spawner.spawn(unwrap!(run_med()));\n\n    // Low priority executor: runs in thread mode, using WFE/SEV\n    let executor = EXECUTOR_LOW.init(Executor::new());\n    executor.run(|spawner| {\n        spawner.spawn(unwrap!(run_low()));\n    });\n}\n"
  },
  {
    "path": "examples/stm32f3/src/bin/spi_dma.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse core::fmt::Write;\nuse core::str::from_utf8;\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_stm32::peripherals::{DMA1_CH2, DMA1_CH3};\nuse embassy_stm32::spi::{Config, Spi};\nuse embassy_stm32::time::Hertz;\nuse embassy_stm32::{bind_interrupts, dma};\nuse heapless::String;\nuse {defmt_rtt as _, panic_probe as _};\n\nbind_interrupts!(struct Irqs {\n    DMA1_CHANNEL2 => dma::InterruptHandler<DMA1_CH2>;\n    DMA1_CHANNEL3 => dma::InterruptHandler<DMA1_CH3>;\n});\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let p = embassy_stm32::init(Default::default());\n    info!(\"Hello World!\");\n\n    let mut spi_config = Config::default();\n    spi_config.frequency = Hertz(1_000_000);\n\n    let mut spi = Spi::new(p.SPI1, p.PB3, p.PB5, p.PB4, p.DMA1_CH3, p.DMA1_CH2, Irqs, spi_config);\n\n    for n in 0u32.. {\n        let mut write: String<128> = String::new();\n        let mut read = [0; 128];\n        core::write!(&mut write, \"Hello DMA World {}!\\r\\n\", n).unwrap();\n        spi.transfer(&mut read[0..write.len()], write.as_bytes()).await.ok();\n        info!(\"read via spi+dma: {}\", from_utf8(&read).unwrap());\n    }\n}\n"
  },
  {
    "path": "examples/stm32f3/src/bin/tsc_blocking.rs",
    "content": "// Example of blocking TSC (Touch Sensing Controller) that lights an LED when touch is detected.\n//\n// This example demonstrates:\n// 1. Configuring a single TSC channel pin\n// 2. Using the blocking TSC interface with polling\n// 3. Waiting for acquisition completion using `poll_for_acquisition`\n// 4. Reading touch values and controlling an LED based on the results\n//\n// Suggested physical setup on STM32F303ZE Nucleo board:\n// - Connect a 1000pF capacitor between pin PA10 and GND. This is your sampling capacitor.\n// - Connect one end of a 1K resistor to pin PA9 and leave the other end loose.\n//   The loose end will act as the touch sensor which will register your touch.\n//\n// The example uses two pins from Group 4 of the TSC:\n// - PA10 as the sampling capacitor, TSC group 4 IO2 (D68 on the STM32F303ZE nucleo-board)\n// - PA9 as the channel pin, TSC group 4 IO1 (D69 on the STM32F303ZE nucleo-board)\n//\n// The program continuously reads the touch sensor value:\n// - It starts acquisition, waits for completion using `poll_for_acquisition`, and reads the value.\n// - The LED is turned on when touch is detected (sensor value < 40).\n// - Touch values are logged to the console.\n//\n// Troubleshooting:\n// - If touch is not detected, try adjusting the SENSOR_THRESHOLD value.\n// - Experiment with different values for ct_pulse_high_length, ct_pulse_low_length,\n//   pulse_generator_prescaler, max_count_value, and discharge_delay to optimize sensitivity.\n//\n// Note: Configuration values and sampling capacitor value have been determined experimentally.\n// Optimal values may vary based on your specific hardware setup.\n// Pins have been chosen for their convenient locations on the STM32F303ZE board. Refer to the\n// official relevant STM32 datasheets and user nucleo-board user manuals to find suitable\n// alternative pins.\n\n#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_stm32::gpio::{Level, Output, Speed};\nuse embassy_stm32::tsc::{self, *};\nuse embassy_stm32::{mode, peripherals};\nuse embassy_time::Timer;\nuse {defmt_rtt as _, panic_probe as _};\n\nconst SENSOR_THRESHOLD: u16 = 25; // Adjust this value based on your setup\n\n#[embassy_executor::main]\nasync fn main(_spawner: embassy_executor::Spawner) {\n    let device_config = embassy_stm32::Config::default();\n    let context = embassy_stm32::init(device_config);\n\n    let tsc_conf = Config {\n        ct_pulse_high_length: ChargeTransferPulseCycle::_4,\n        ct_pulse_low_length: ChargeTransferPulseCycle::_4,\n        spread_spectrum: false,\n        spread_spectrum_deviation: SSDeviation::new(2).unwrap(),\n        spread_spectrum_prescaler: false,\n        pulse_generator_prescaler: PGPrescalerDivider::_16,\n        max_count_value: MaxCount::_255,\n        io_default_mode: false,\n        synchro_pin_polarity: false,\n        acquisition_mode: false,\n        max_count_interrupt: false,\n    };\n\n    let mut g: PinGroupWithRoles<peripherals::TSC, G4> = PinGroupWithRoles::default();\n    // D68 on the STM32F303ZE nucleo-board\n    g.set_io2::<tsc::pin_roles::Sample>(context.PA10);\n    // D69 on the STM32F303ZE nucleo-board\n    let tsc_sensor = g.set_io1::<tsc::pin_roles::Channel>(context.PA9);\n\n    let pin_groups: PinGroups<peripherals::TSC> = PinGroups {\n        g4: Some(g.pin_group),\n        ..Default::default()\n    };\n\n    let mut touch_controller = tsc::Tsc::new_blocking(context.TSC, pin_groups, tsc_conf).unwrap();\n\n    // Check if TSC is ready\n    if touch_controller.get_state() != State::Ready {\n        crate::panic!(\"TSC not ready!\");\n    }\n    info!(\"TSC initialized successfully\");\n\n    // LED2 on the STM32F303ZE nucleo-board\n    let mut led = Output::new(context.PB7, Level::High, Speed::Low);\n\n    // smaller sample capacitor discharge faster and can be used with shorter delay.\n    let discharge_delay = 5; // ms\n\n    // the interval at which the loop polls for new touch sensor values\n    let polling_interval = 100; // ms\n\n    info!(\"polling for touch\");\n    loop {\n        touch_controller.set_active_channels_mask(tsc_sensor.pin.into());\n        touch_controller.start();\n        touch_controller.poll_for_acquisition();\n        touch_controller.discharge_io(true);\n        Timer::after_millis(discharge_delay).await;\n\n        match read_touch_value(&mut touch_controller, tsc_sensor.pin).await {\n            Some(v) => {\n                info!(\"sensor value {}\", v);\n                if v < SENSOR_THRESHOLD {\n                    led.set_high();\n                } else {\n                    led.set_low();\n                }\n            }\n            None => led.set_low(),\n        }\n\n        Timer::after_millis(polling_interval).await;\n    }\n}\n\nconst MAX_GROUP_STATUS_READ_ATTEMPTS: usize = 10;\n\n// attempt to read group status and delay when still ongoing\nasync fn read_touch_value(\n    touch_controller: &mut tsc::Tsc<'_, peripherals::TSC, mode::Blocking>,\n    sensor_pin: tsc::IOPin,\n) -> Option<u16> {\n    for _ in 0..MAX_GROUP_STATUS_READ_ATTEMPTS {\n        match touch_controller.group_get_status(sensor_pin.group()) {\n            GroupStatus::Complete => {\n                return Some(touch_controller.group_get_value(sensor_pin.group()));\n            }\n            GroupStatus::Ongoing => {\n                // if you end up here a lot, then you prob need to increase discharge_delay\n                // or consider changing the code to adjust the discharge_delay dynamically\n                info!(\"Acquisition still ongoing\");\n                Timer::after_millis(1).await;\n            }\n        }\n    }\n    None\n}\n"
  },
  {
    "path": "examples/stm32f3/src/bin/tsc_multipin.rs",
    "content": "// Example of TSC (Touch Sensing Controller) using multiple pins from the same tsc-group.\n//\n// What is special about using multiple TSC pins as sensor channels from the same TSC group,\n// is that only one TSC pin for each TSC group can be acquired and read at the time.\n// To control which channel pins are acquired and read, we must write a mask before initiating an\n// acquisition. To help manage and abstract all this business away, we can organize our channel\n// pins into acquisition banks. Each acquisition bank can contain exactly one channel pin per TSC\n// group and it will contain the relevant mask.\n//\n// This example demonstrates how to:\n// 1. Configure multiple channel pins within a single TSC group\n// 2. Use the set_active_channels_bank method to switch between sets of different channels (acquisition banks)\n// 3. Read and interpret touch values from multiple channels in the same group\n//\n// Suggested physical setup on STM32F303ZE Nucleo board:\n// - Connect a 1000pF capacitor between pin PA10 and GND. This is the sampling capacitor for TSC\n//   group 4.\n// - Connect one end of a 1K resistor to pin PA9 and leave the other end loose.\n//   The loose end will act as a touch sensor.\n//\n// - Connect a 1000pF capacitor between pin PA7 and GND. This is the sampling capacitor for TSC\n//   group 2.\n// - Connect one end of another 1K resistor to pin PA6 and leave the other end loose.\n//   The loose end will act as a touch sensor.\n// - Connect one end of another 1K resistor to pin PA5 and leave the other end loose.\n//   The loose end will act as a touch sensor.\n//\n// The example uses pins from two TSC groups.\n// - PA10 as sampling capacitor, TSC group 4 IO2\n// - PA9 as channel, TSC group 4 IO1\n// - PA7 as sampling capacitor, TSC group 2 IO4\n// - PA6 as channel, TSC group 2 IO3\n// - PA5 as channel, TSC group 2 IO2\n//\n// The pins have been chosen to make it easy to simply add capacitors directly onto the board and\n// connect one leg to GND, and to easily add resistors to the board with no special connectors,\n// breadboards, special wires or soldering required. All you need is the capacitors and resistors.\n//\n// The program reads the designated channel pins and adjusts the LED blinking\n// pattern based on which sensor(s) are touched:\n// - No touch: LED off\n// - one sensor touched: Slow blinking\n// - two sensors touched: Fast blinking\n// - three sensors touched: LED constantly on\n//\n// ## Troubleshooting:\n//\n// - If touch is not detected, try adjusting the SENSOR_THRESHOLD value (currently set to 20).\n// - Experiment with different values for ct_pulse_high_length, ct_pulse_low_length, pulse_generator_prescaler, max_count_value, and discharge_delay to optimize sensitivity.\n// - Be aware that for some boards there will be overlapping concerns between some pins, for\n//  example UART connection for the programmer to the MCU and a TSC pin. No errors or warning will\n//  be emitted if you try to use such a pin for TSC, but you will get strange sensor readings.\n//\n// Note: Configuration values and sampling capacitor values have been determined experimentally. Optimal values may vary based on your specific hardware setup. Refer to the official STM32 datasheet and user manuals for more information on pin configurations and TSC functionality.\n\n#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_stm32::gpio::{Level, Output, Speed};\nuse embassy_stm32::tsc::{self, *};\nuse embassy_stm32::{mode, peripherals};\nuse embassy_time::Timer;\nuse {defmt_rtt as _, panic_probe as _};\n\nconst SENSOR_THRESHOLD: u16 = 10;\n\nasync fn acquire_sensors(\n    touch_controller: &mut Tsc<'static, peripherals::TSC, mode::Blocking>,\n    tsc_acquisition_bank: &AcquisitionBank,\n) {\n    touch_controller.set_active_channels_bank(tsc_acquisition_bank);\n    touch_controller.start();\n    touch_controller.poll_for_acquisition();\n    touch_controller.discharge_io(true);\n    let discharge_delay = 5; // ms\n    Timer::after_millis(discharge_delay).await;\n}\n\n#[embassy_executor::main]\nasync fn main(_spawner: embassy_executor::Spawner) {\n    let device_config = embassy_stm32::Config::default();\n    let context = embassy_stm32::init(device_config);\n\n    // ---------- initial configuration of TSC ----------\n    //\n    let mut pin_group4: PinGroupWithRoles<peripherals::TSC, G4> = PinGroupWithRoles::default();\n    // D68 on the STM32F303ZE nucleo-board\n    pin_group4.set_io2::<tsc::pin_roles::Sample>(context.PA10);\n    // D69 on the STM32F303ZE nucleo-board\n    let tsc_sensor0 = pin_group4.set_io1(context.PA9);\n\n    let mut pin_group2: PinGroupWithRoles<peripherals::TSC, G2> = PinGroupWithRoles::default();\n    // D11 on the STM32F303ZE nucleo-board\n    pin_group2.set_io4::<tsc::pin_roles::Sample>(context.PA7);\n    // D12 on the STM32F303ZE nucleo-board\n    let tsc_sensor1 = pin_group2.set_io3(context.PA6);\n    // D13 on the STM32F303ZE nucleo-board\n    let tsc_sensor2 = pin_group2.set_io2(context.PA5);\n\n    let config = Config {\n        ct_pulse_high_length: ChargeTransferPulseCycle::_4,\n        ct_pulse_low_length: ChargeTransferPulseCycle::_4,\n        spread_spectrum: false,\n        spread_spectrum_deviation: SSDeviation::new(2).unwrap(),\n        spread_spectrum_prescaler: false,\n        pulse_generator_prescaler: PGPrescalerDivider::_16,\n        max_count_value: MaxCount::_255,\n        io_default_mode: false,\n        synchro_pin_polarity: false,\n        acquisition_mode: false,\n        max_count_interrupt: false,\n    };\n\n    let pin_groups: PinGroups<peripherals::TSC> = PinGroups {\n        g4: Some(pin_group4.pin_group),\n        g2: Some(pin_group2.pin_group),\n        ..Default::default()\n    };\n\n    let mut touch_controller = tsc::Tsc::new_blocking(context.TSC, pin_groups, config).unwrap();\n\n    // ---------- setting up acquisition banks ----------\n    // sensor0 and sensor1 in this example belong to different TSC-groups,\n    // therefore we can acquire and read them both in one go.\n    let bank1 = touch_controller.create_acquisition_bank(AcquisitionBankPins {\n        g4_pin: Some(tsc_sensor0),\n        g2_pin: Some(tsc_sensor1),\n        ..Default::default()\n    });\n    // `sensor1` and `sensor2` belongs to the same TSC-group, therefore we must make sure to\n    // acquire them one at the time. Therefore, we organize them into different acquisition banks.\n    let bank2 = touch_controller.create_acquisition_bank(AcquisitionBankPins {\n        g2_pin: Some(tsc_sensor2),\n        ..Default::default()\n    });\n\n    // Check if TSC is ready\n    if touch_controller.get_state() != State::Ready {\n        crate::panic!(\"TSC not ready!\");\n    }\n\n    info!(\"TSC initialized successfully\");\n\n    // LED2 on the STM32F303ZE nucleo-board\n    let mut led = Output::new(context.PB7, Level::High, Speed::Low);\n\n    let mut led_state = false;\n\n    loop {\n        acquire_sensors(&mut touch_controller, &bank1).await;\n        let readings1 = touch_controller.get_acquisition_bank_values(&bank1);\n        acquire_sensors(&mut touch_controller, &bank2).await;\n        let readings2 = touch_controller.get_acquisition_bank_values(&bank2);\n\n        let mut touched_sensors_count = 0;\n        for reading in readings1.iter() {\n            info!(\"{}\", reading);\n            if reading.sensor_value < SENSOR_THRESHOLD {\n                touched_sensors_count += 1;\n            }\n        }\n        for reading in readings2.iter() {\n            info!(\"{}\", reading);\n            if reading.sensor_value < SENSOR_THRESHOLD {\n                touched_sensors_count += 1;\n            }\n        }\n\n        match touched_sensors_count {\n            0 => {\n                // No sensors touched, turn off the LED\n                led.set_low();\n                led_state = false;\n            }\n            1 => {\n                // One sensor touched, blink slowly\n                led_state = !led_state;\n                if led_state {\n                    led.set_high();\n                } else {\n                    led.set_low();\n                }\n                Timer::after_millis(200).await;\n            }\n            2 => {\n                // Two sensors touched, blink faster\n                led_state = !led_state;\n                if led_state {\n                    led.set_high();\n                } else {\n                    led.set_low();\n                }\n                Timer::after_millis(50).await;\n            }\n            3 => {\n                // All three sensors touched, LED constantly on\n                led.set_high();\n                led_state = true;\n            }\n            _ => crate::unreachable!(), // This case should never occur with 3 sensors\n        }\n    }\n}\n"
  },
  {
    "path": "examples/stm32f3/src/bin/usart_dma.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse core::fmt::Write;\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_stm32::usart::{Config, Uart};\nuse embassy_stm32::{bind_interrupts, dma, peripherals, usart};\nuse heapless::String;\nuse {defmt_rtt as _, panic_probe as _};\n\nbind_interrupts!(struct Irqs {\n    USART1 => usart::InterruptHandler<peripherals::USART1>;\n    DMA1_CHANNEL4 => dma::InterruptHandler<peripherals::DMA1_CH4>;\n    DMA1_CHANNEL5 => dma::InterruptHandler<peripherals::DMA1_CH5>;\n});\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let p = embassy_stm32::init(Default::default());\n    info!(\"Hello World!\");\n\n    let config = Config::default();\n    let mut usart = Uart::new(p.USART1, p.PE1, p.PE0, p.DMA1_CH4, p.DMA1_CH5, Irqs, config).unwrap();\n\n    for n in 0u32.. {\n        let mut s: String<128> = String::new();\n        core::write!(&mut s, \"Hello DMA World {}!\\r\\n\", n).unwrap();\n\n        unwrap!(usart.write(s.as_bytes()).await);\n        info!(\"wrote DMA\");\n    }\n}\n"
  },
  {
    "path": "examples/stm32f3/src/bin/usb_serial.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse defmt::{panic, *};\nuse embassy_executor::Spawner;\nuse embassy_futures::join::join;\nuse embassy_stm32::gpio::{Level, Output, Speed};\nuse embassy_stm32::time::mhz;\nuse embassy_stm32::usb::{Driver, Instance};\nuse embassy_stm32::{Config, bind_interrupts, peripherals, usb};\nuse embassy_time::Timer;\nuse embassy_usb::Builder;\nuse embassy_usb::class::cdc_acm::{CdcAcmClass, State};\nuse embassy_usb::driver::EndpointError;\nuse {defmt_rtt as _, panic_probe as _};\n\nbind_interrupts!(struct Irqs {\n    USB_LP_CAN_RX0 => usb::InterruptHandler<peripherals::USB>;\n});\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let mut config = Config::default();\n    {\n        use embassy_stm32::rcc::*;\n        config.rcc.hse = Some(Hse {\n            freq: mhz(8),\n            mode: HseMode::Bypass,\n        });\n        config.rcc.pll = Some(Pll {\n            src: PllSource::HSE,\n            prediv: PllPreDiv::DIV1,\n            mul: PllMul::MUL9,\n        });\n        config.rcc.sys = Sysclk::PLL1_P;\n        config.rcc.ahb_pre = AHBPrescaler::DIV1;\n        config.rcc.apb1_pre = APBPrescaler::DIV2;\n        config.rcc.apb2_pre = APBPrescaler::DIV1;\n    }\n    let p = embassy_stm32::init(config);\n\n    info!(\"Hello World!\");\n\n    // Needed for nucleo-stm32f303ze\n    let mut dp_pullup = Output::new(p.PG6, Level::Low, Speed::Medium);\n    Timer::after_millis(10).await;\n    dp_pullup.set_high();\n\n    // Create the driver, from the HAL.\n    let driver = Driver::new(p.USB, Irqs, p.PA12, p.PA11);\n\n    // Create embassy-usb Config\n    let config = embassy_usb::Config::new(0xc0de, 0xcafe);\n\n    // Create embassy-usb DeviceBuilder using the driver and config.\n    // It needs some buffers for building the descriptors.\n    let mut config_descriptor = [0; 256];\n    let mut bos_descriptor = [0; 256];\n    let mut control_buf = [0; 7];\n\n    let mut state = State::new();\n\n    let mut builder = Builder::new(\n        driver,\n        config,\n        &mut config_descriptor,\n        &mut bos_descriptor,\n        &mut [], // no msos descriptors\n        &mut control_buf,\n    );\n\n    // Create classes on the builder.\n    let mut class = CdcAcmClass::new(&mut builder, &mut state, 64);\n\n    // Build the builder.\n    let mut usb = builder.build();\n\n    // Run the USB device.\n    let usb_fut = usb.run();\n\n    // Do stuff with the class!\n    let echo_fut = async {\n        loop {\n            class.wait_connection().await;\n            info!(\"Connected\");\n            let _ = echo(&mut class).await;\n            info!(\"Disconnected\");\n        }\n    };\n\n    // Run everything concurrently.\n    // If we had made everything `'static` above instead, we could do this using separate tasks instead.\n    join(usb_fut, echo_fut).await;\n}\n\nstruct Disconnected {}\n\nimpl From<EndpointError> for Disconnected {\n    fn from(val: EndpointError) -> Self {\n        match val {\n            EndpointError::BufferOverflow => panic!(\"Buffer overflow\"),\n            EndpointError::Disabled => Disconnected {},\n        }\n    }\n}\n\nasync fn echo<'d, T: Instance + 'd>(class: &mut CdcAcmClass<'d, Driver<'d, T>>) -> Result<(), Disconnected> {\n    let mut buf = [0; 64];\n    loop {\n        let n = class.read_packet(&mut buf).await?;\n        let data = &buf[..n];\n        info!(\"data: {:x}\", data);\n        class.write_packet(data).await?;\n    }\n}\n"
  },
  {
    "path": "examples/stm32f334/.cargo/config.toml",
    "content": "[target.'cfg(all(target_arch = \"arm\", target_os = \"none\"))']\n# replace STM32F429ZITx with your chip as listed in `probe-rs-cli chip list`\nrunner = \"probe-rs run --chip STM32F334R8\"\n\n[build]\ntarget = \"thumbv7em-none-eabihf\"\n\n[env]\nDEFMT_LOG = \"trace\"\n"
  },
  {
    "path": "examples/stm32f334/Cargo.toml",
    "content": "[package]\nedition = \"2024\"\nname = \"embassy-stm32f334-examples\"\nversion = \"0.1.0\"\nlicense = \"MIT OR Apache-2.0\"\npublish = false\n\n[dependencies]\nembassy-sync = { version = \"0.8.0\", path = \"../../embassy-sync\", features = [\"defmt\"] }\nembassy-executor = { version = \"0.10.0\", path = \"../../embassy-executor\", features = [\"platform-cortex-m\", \"executor-thread\", \"executor-interrupt\", \"defmt\"] }\nembassy-time = { version = \"0.5.1\", path = \"../../embassy-time\", features = [\"defmt\", \"defmt-timestamp-uptime\", \"tick-hz-32_768\"] }\nembassy-stm32 = { version = \"0.6.0\", path = \"../../embassy-stm32\", features = [ \"defmt\", \"stm32f334r8\", \"unstable-pac\", \"memory-x\", \"time-driver-any\", \"exti\"]  }\nembassy-usb = { version = \"0.6.0\", path = \"../../embassy-usb\", features = [\"defmt\"] }\nembassy-futures = { version = \"0.1.2\", path = \"../../embassy-futures\" }\n\ndefmt = \"1.0.1\"\ndefmt-rtt = \"1.0.0\"\n\ncortex-m = { version = \"0.7.6\", features = [\"inline-asm\", \"critical-section-single-core\"] }\ncortex-m-rt = \"0.7.0\"\nembedded-hal = \"0.2.6\"\npanic-probe = { version = \"1.0.0\", features = [\"print-defmt\"] }\nheapless = { version = \"0.9\", default-features = false }\nnb = \"1.0.0\"\nembedded-storage = \"0.3.1\"\nstatic_cell = \"2\"\n\n[package.metadata.embassy]\nbuild = [\n  { target = \"thumbv7em-none-eabi\", artifact-dir = \"out/examples/stm32f334\" }\n]\n"
  },
  {
    "path": "examples/stm32f334/build.rs",
    "content": "fn main() {\n    println!(\"cargo:rustc-link-arg-bins=--nmagic\");\n    println!(\"cargo:rustc-link-arg-bins=-Tlink.x\");\n    println!(\"cargo:rustc-link-arg-bins=-Tdefmt.x\");\n}\n"
  },
  {
    "path": "examples/stm32f334/src/bin/adc.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse defmt::info;\nuse embassy_executor::Spawner;\nuse embassy_stm32::adc::{Adc, SampleTime};\nuse embassy_stm32::peripherals::ADC1;\nuse embassy_stm32::time::mhz;\nuse embassy_stm32::{Config, adc, bind_interrupts};\nuse embassy_time::Timer;\nuse {defmt_rtt as _, panic_probe as _};\n\nbind_interrupts!(struct Irqs {\n    ADC1_2 => adc::InterruptHandler<ADC1>;\n});\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) -> ! {\n    let mut config = Config::default();\n    {\n        use embassy_stm32::rcc::*;\n        config.rcc.hse = Some(Hse {\n            freq: mhz(8),\n            mode: HseMode::Bypass,\n        });\n        config.rcc.pll = Some(Pll {\n            src: PllSource::HSE,\n            prediv: PllPreDiv::DIV1,\n            mul: PllMul::MUL9,\n        });\n        config.rcc.sys = Sysclk::PLL1_P;\n        config.rcc.ahb_pre = AHBPrescaler::DIV1;\n        config.rcc.apb1_pre = APBPrescaler::DIV2;\n        config.rcc.apb2_pre = APBPrescaler::DIV1;\n        config.rcc.adc = AdcClockSource::Pll(AdcPllPrescaler::DIV1);\n    }\n    let mut p = embassy_stm32::init(config);\n\n    info!(\"create adc...\");\n\n    let mut adc = Adc::new(p.ADC1, Irqs);\n\n    info!(\"enable vrefint...\");\n\n    let mut vrefint = adc.enable_vref();\n    let mut temperature = adc.enable_temperature();\n\n    loop {\n        let vref = adc.read(&mut vrefint, SampleTime::CYCLES601_5).await;\n        info!(\"read vref: {} (should be {})\", vref, vrefint.calibrated_value());\n\n        let temp = adc.read(&mut temperature, SampleTime::CYCLES601_5).await;\n        info!(\"read temperature: {}\", temp);\n\n        let pin = adc.read(&mut p.PA0, SampleTime::CYCLES601_5).await;\n        info!(\"read pin: {}\", pin);\n\n        let pin_mv = (pin as u32 * vrefint.calibrated_value() as u32 / vref as u32) * 3300 / 4095;\n        info!(\"computed pin mv: {}\", pin_mv);\n\n        Timer::after_millis(500).await;\n    }\n}\n"
  },
  {
    "path": "examples/stm32f334/src/bin/button.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_stm32::gpio::{Level, Output, Speed};\nuse embassy_time::Timer;\nuse {defmt_rtt as _, panic_probe as _};\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    info!(\"Hello World!\");\n\n    let p = embassy_stm32::init(Default::default());\n\n    let mut out1 = Output::new(p.PA8, Level::Low, Speed::High);\n\n    out1.set_high();\n    Timer::after_millis(500).await;\n    out1.set_low();\n\n    Timer::after_millis(500).await;\n    info!(\"end program\");\n\n    cortex_m::asm::bkpt();\n}\n"
  },
  {
    "path": "examples/stm32f334/src/bin/hello.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse defmt::info;\nuse embassy_executor::Spawner;\nuse embassy_stm32::Config;\nuse embassy_time::Timer;\nuse {defmt_rtt as _, panic_probe as _};\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) -> ! {\n    let config = Config::default();\n    let _p = embassy_stm32::init(config);\n\n    loop {\n        info!(\"Hello World!\");\n        Timer::after_secs(1).await;\n    }\n}\n"
  },
  {
    "path": "examples/stm32f334/src/bin/hrtim.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_stm32::gpio::Speed;\nuse embassy_stm32::hrtim::stm32_hrtim::compare_register::HrCompareRegister;\nuse embassy_stm32::hrtim::stm32_hrtim::output::HrOutput;\nuse embassy_stm32::hrtim::stm32_hrtim::timer::HrTimer;\nuse embassy_stm32::hrtim::stm32_hrtim::{HrParts, HrPwmAdvExt, PreloadSource};\nuse embassy_stm32::hrtim::{HrControltExt, HrPwmBuilderExt, Parts};\nuse embassy_stm32::time::mhz;\nuse embassy_stm32::{Config, hrtim};\nuse embassy_time::Timer;\nuse {defmt_rtt as _, panic_probe as _};\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let mut config = Config::default();\n    {\n        // Set PLL frequency to 8MHz * 9/1 = 72MHz\n        // This would lead to HrTim running at 72MHz * 2 * 32 = 4.608GHz...\n        use embassy_stm32::rcc::*;\n        config.rcc.hse = Some(Hse {\n            freq: mhz(8),\n            mode: HseMode::Bypass,\n        });\n        config.rcc.pll = Some(Pll {\n            src: PllSource::HSE,\n            prediv: PllPreDiv::DIV1,\n            mul: PllMul::MUL9,\n        });\n        config.rcc.sys = Sysclk::PLL1_P;\n        config.rcc.ahb_pre = AHBPrescaler::DIV1;\n        config.rcc.apb1_pre = APBPrescaler::DIV2;\n        config.rcc.apb2_pre = APBPrescaler::DIV1;\n\n        config.rcc.mux.hrtim1sw = embassy_stm32::rcc::mux::Timsw::PLL1_P;\n    }\n    let p = embassy_stm32::init(config);\n\n    info!(\"Hello World!\");\n\n    let pin1 = hrtim::Pin {\n        pin: p.PA8,\n        speed: Speed::Low,\n    };\n    let pin2 = hrtim::Pin {\n        pin: p.PA9,\n        speed: Speed::Low,\n    };\n\n    // ...with a prescaler of 4 this gives us a HrTimer with a tick rate of 1152MHz\n    // With max the max period set, this would be 1152MHz/2^16 ~= 17.6kHz...\n    let prescaler = hrtim::Pscl4;\n\n    let Parts { control, tima, .. } = p.HRTIM1.hr_control();\n    let (control, ..) = control.wait_for_calibration();\n    let mut control = control.constrain();\n\n    //        .               .               .               .\n    //        .  30%          .               .               .\n    //        .----           .               .----           .\n    //pin1    |    |          .               |    |          .\n    //        |    |          .               |    |          .\n    // --------    ----------------------------    --------------------\n    //        .               .----           .               .----\n    //pin2    .               |    |          .               |    |\n    //        .               |    |          .               |    |\n    // ------------------------    ----------------------------    ----\n    //        .               .               .               .\n    //        .               .               .               .\n\n    let HrParts {\n        mut timer,\n        mut cr1,\n        mut out1,\n        mut out2,\n        ..\n    } = tima\n        .pwm_advanced(pin1, pin2)\n        .prescaler(prescaler)\n        .period(0xFFFF)\n        .push_pull_mode(true) // Set push pull mode, out1 and out2 are\n        // alternated every period with one being\n        // inactive and the other getting to output its wave form\n        // as normal\n        .preload(PreloadSource::OnRepetitionUpdate)\n        .finalize(&mut control);\n\n    out1.enable_rst_event(&cr1); // Set low on compare match with cr1\n    out2.enable_rst_event(&cr1);\n\n    out1.enable_set_event(&timer); // Set high at new period\n    out2.enable_set_event(&timer);\n\n    info!(\"pwm constructed\");\n\n    out1.enable();\n    out2.enable();\n    timer.start(&mut control.control);\n\n    // Step frequency from 15.6kHz to about 156kHz(half of that when only looking at one pin)\n    for i in 1..=10 {\n        let new_period = u16::MAX / i;\n\n        cr1.set_duty(new_period / 3);\n        timer.set_period(new_period);\n\n        Timer::after_millis(1).await;\n    }\n\n    out1.disable();\n    out2.disable();\n\n    info!(\"end program\");\n\n    cortex_m::asm::bkpt();\n}\n"
  },
  {
    "path": "examples/stm32f334/src/bin/opamp.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse defmt::info;\nuse embassy_executor::Spawner;\nuse embassy_stm32::adc::{Adc, SampleTime};\nuse embassy_stm32::opamp::OpAmp;\nuse embassy_stm32::peripherals::ADC2;\nuse embassy_stm32::time::mhz;\nuse embassy_stm32::{Config, adc, bind_interrupts};\nuse embassy_time::Timer;\nuse {defmt_rtt as _, panic_probe as _};\n\nbind_interrupts!(struct Irqs {\n    ADC1_2 => adc::InterruptHandler<ADC2>;\n});\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) -> ! {\n    let mut config = Config::default();\n    {\n        use embassy_stm32::rcc::*;\n        config.rcc.hse = Some(Hse {\n            freq: mhz(8),\n            mode: HseMode::Bypass,\n        });\n        config.rcc.pll = Some(Pll {\n            src: PllSource::HSE,\n            prediv: PllPreDiv::DIV1,\n            mul: PllMul::MUL9,\n        });\n        config.rcc.sys = Sysclk::PLL1_P;\n        config.rcc.ahb_pre = AHBPrescaler::DIV1;\n        config.rcc.apb1_pre = APBPrescaler::DIV2;\n        config.rcc.apb2_pre = APBPrescaler::DIV1;\n        config.rcc.adc = AdcClockSource::Pll(AdcPllPrescaler::DIV1);\n    }\n    let mut p = embassy_stm32::init(config);\n\n    info!(\"create adc...\");\n\n    let mut adc = Adc::new(p.ADC2, Irqs);\n    let mut opamp = OpAmp::new(p.OPAMP2);\n\n    info!(\"enable vrefint...\");\n\n    let mut vrefint = adc.enable_vref();\n    let mut temperature = adc.enable_temperature();\n    let mut buffer = opamp.buffer_ext(p.PA7.reborrow(), p.PA6.reborrow());\n\n    loop {\n        let vref = adc.read(&mut vrefint, SampleTime::CYCLES601_5).await;\n        info!(\"read vref: {} (should be {})\", vref, vrefint.calibrated_value());\n\n        let temp = adc.read(&mut temperature, SampleTime::CYCLES601_5).await;\n        info!(\"read temperature: {}\", temp);\n\n        let buffer = adc.read(&mut buffer, SampleTime::CYCLES601_5).await;\n        info!(\"read buffer: {}\", buffer);\n\n        let pin_mv = (buffer as u32 * vrefint.calibrated_value() as u32 / vref as u32) * 3300 / 4095;\n        info!(\"computed pin mv: {}\", pin_mv);\n\n        Timer::after_millis(500).await;\n    }\n}\n"
  },
  {
    "path": "examples/stm32f334/src/bin/pwm.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_stm32::gpio::Speed;\nuse embassy_stm32::hrtim::bridge_converter::BridgeConverter;\nuse embassy_stm32::hrtim::*;\nuse embassy_stm32::time::{khz, mhz};\nuse embassy_stm32::{Config, hrtim};\nuse embassy_time::Timer;\nuse {defmt_rtt as _, panic_probe as _};\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let mut config = Config::default();\n    {\n        use embassy_stm32::rcc::*;\n        config.rcc.hse = Some(Hse {\n            freq: mhz(8),\n            mode: HseMode::Bypass,\n        });\n        config.rcc.pll = Some(Pll {\n            src: PllSource::HSE,\n            prediv: PllPreDiv::DIV1,\n            mul: PllMul::MUL9,\n        });\n        config.rcc.sys = Sysclk::PLL1_P;\n        config.rcc.ahb_pre = AHBPrescaler::DIV1;\n        config.rcc.apb1_pre = APBPrescaler::DIV2;\n        config.rcc.apb2_pre = APBPrescaler::DIV1;\n\n        config.rcc.mux.hrtim1sw = embassy_stm32::rcc::mux::Timsw::PLL1_P;\n    }\n    let p = embassy_stm32::init(config);\n\n    info!(\"Hello World!\");\n\n    let ch1 = hrtim::Pin {\n        pin: p.PA8,\n        speed: Speed::Low,\n    };\n    let ch1n = hrtim::Pin {\n        pin: p.PA9,\n        speed: Speed::Low,\n    };\n\n    // ...with a prescaler of 4 this gives us a HrTimer with a tick rate of 1152MHz\n    // With max the max period set, this would be 1152MHz/2^16 ~= 17.6kHz...\n    let prescaler = hrtim::Pscl4;\n\n    let Parts { control, tima, .. } = p.HRTIM1.hr_control();\n    let (control, ..) = control.wait_for_calibration();\n    let mut control = control.constrain();\n\n    info!(\"pwm constructed\");\n\n    let mut buck_converter = BridgeConverter::new(tima, ch1, ch1n, khz(5), prescaler, &mut control);\n\n    //    embassy_stm32::pac::HRTIM1\n    //        .tim(0)\n    //        .setr(0)\n    //        .modify(|w| w.set_sst(true));\n    //\n    //    Timer::after_millis(500).await;\n    //\n    //    embassy_stm32::pac::HRTIM1\n    //        .tim(0)\n    //        .rstr(0)\n    //        .modify(|w| w.set_srt(true));\n\n    let max_duty = buck_converter.get_max_compare_value();\n\n    info!(\"max compare value: {}\", max_duty);\n\n    buck_converter.set_dead_time(max_duty / 20);\n    buck_converter.set_primary_duty(max_duty / 2);\n    buck_converter.set_secondary_duty(3 * max_duty / 4);\n\n    buck_converter.start(&mut control.control);\n\n    Timer::after_millis(500).await;\n\n    info!(\"end program\");\n\n    cortex_m::asm::bkpt();\n}\n"
  },
  {
    "path": "examples/stm32f4/.cargo/config.toml",
    "content": "[target.'cfg(all(target_arch = \"arm\", target_os = \"none\"))']\n# replace STM32F429ZITx with your chip as listed in `probe-rs chip list`\nrunner = \"probe-rs run --chip STM32F429ZITx\"\n\n[build]\ntarget = \"thumbv7em-none-eabi\"\n\n[env]\nDEFMT_LOG = \"trace\"\n"
  },
  {
    "path": "examples/stm32f4/Cargo.toml",
    "content": "[package]\nedition = \"2024\"\nname = \"embassy-stm32f4-examples\"\nversion = \"0.1.0\"\nlicense = \"MIT OR Apache-2.0\"\npublish = false\n\n[dependencies]\n# Change stm32f429zi to your chip name, if necessary.\nembassy-stm32 = { version = \"0.6.0\", path = \"../../embassy-stm32\", features = [\"defmt\", \"stm32f429zi\", \"unstable-pac\", \"memory-x\", \"time-driver-tim4\", \"exti\", \"chrono\"] }\nembassy-sync = { version = \"0.8.0\", path = \"../../embassy-sync\", features = [\"defmt\"] }\nembassy-executor = { version = \"0.10.0\", path = \"../../embassy-executor\", features = [\"platform-cortex-m\", \"executor-thread\", \"executor-interrupt\", \"defmt\"] }\nembassy-time = { version = \"0.5.1\", path = \"../../embassy-time\", features = [\"defmt\", \"defmt-timestamp-uptime\", \"tick-hz-32_768\"] }\nembassy-usb = { version = \"0.6.0\", path = \"../../embassy-usb\", features = [\"defmt\" ] }\nembassy-net = { version = \"0.9.0\", path = \"../../embassy-net\", features = [\"defmt\", \"tcp\", \"dhcpv4\", \"medium-ethernet\", ] }\nembassy-net-wiznet = { version = \"0.3.0\", path = \"../../embassy-net-wiznet\", features = [\"defmt\"] }\nembassy-futures = { version = \"0.1.2\", path = \"../../embassy-futures\" }\n\ndefmt = \"1.0.1\"\ndefmt-rtt = \"1.0.0\"\n\ncortex-m = { version = \"0.7.6\", features = [\"inline-asm\", \"critical-section-single-core\"] }\ncortex-m-rt = \"0.7.0\"\nembedded-hal = \"0.2.6\"\nembedded-hal-bus = { version = \"0.2\", features = [\"async\"] }\nembedded-io = { version = \"0.7.1\" }\nembedded-io-async = { version = \"0.7.0\" }\npanic-probe = { version = \"1.0.0\", features = [\"print-defmt\"] }\nfutures-util = { version = \"0.3.30\", default-features = false }\nheapless = { version = \"0.9\", default-features = false }\ncritical-section = \"1.1\"\nnb = \"1.0.0\"\nembedded-storage = \"0.3.1\"\nmicromath = \"2.0.0\"\nusbd-hid = \"0.10.0\"\nstatic_cell = \"2\"\nchrono = { version = \"^0.4\", default-features = false}\n\n[profile.release]\ndebug = 2\n\n[package.metadata.embassy]\nbuild = [\n  { target = \"thumbv7em-none-eabi\", artifact-dir = \"out/examples/stm32f4\" }\n]\n"
  },
  {
    "path": "examples/stm32f4/build.rs",
    "content": "fn main() {\n    println!(\"cargo:rustc-link-arg-bins=--nmagic\");\n    println!(\"cargo:rustc-link-arg-bins=-Tlink.x\");\n    println!(\"cargo:rustc-link-arg-bins=-Tdefmt.x\");\n}\n"
  },
  {
    "path": "examples/stm32f4/src/bin/adc.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse cortex_m::prelude::_embedded_hal_blocking_delay_DelayUs;\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_stm32::adc::vals::Exten;\nuse embassy_stm32::adc::{Adc, AdcChannel, SampleTime, Temperature, VrefInt};\nuse embassy_stm32::triggers::TIM1_CH1;\nuse embassy_stm32::{bind_interrupts, dma, peripherals};\nuse embassy_time::{Delay, Timer};\nuse {defmt_rtt as _, panic_probe as _};\n\nbind_interrupts!(struct Irqs {\n    DMA2_STREAM2 => dma::InterruptHandler<peripherals::DMA2_CH2>;\n});\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let p = embassy_stm32::init(Default::default());\n    info!(\"Hello World!\");\n\n    let mut adc_dma_buf: [u16; 2] = [0; 2];\n\n    // TODO: impl. triggers for this\n    let mut adc_ring_buffered = Adc::new(p.ADC2).into_ring_buffered(\n        p.DMA2_CH2,\n        &mut adc_dma_buf,\n        Irqs,\n        [(p.PA0.degrade_adc(), SampleTime::CYCLES112)].into_iter(),\n        TIM1_CH1,\n        Exten::RISING_EDGE,\n    );\n    adc_ring_buffered.start();\n\n    let mut delay = Delay;\n    let mut adc = Adc::new_with_config(p.ADC1, Default::default());\n    let mut pin = p.PC1;\n\n    let mut vrefint = adc.enable_vrefint();\n    let mut temp = adc.enable_temperature();\n\n    // Startup delay can be combined to the maximum of either\n    delay.delay_us(Temperature::start_time_us().max(VrefInt::start_time_us()));\n\n    let vrefint_sample = adc.blocking_read(&mut vrefint, SampleTime::CYCLES112);\n\n    let convert_to_millivolts = |sample| {\n        // From http://www.st.com/resource/en/datasheet/DM00071990.pdf\n        // 6.3.24 Reference voltage\n        const VREFINT_MV: u32 = 1210; // mV\n\n        (u32::from(sample) * VREFINT_MV / u32::from(vrefint_sample)) as u16\n    };\n\n    let convert_to_celcius = |sample| {\n        // From http://www.st.com/resource/en/datasheet/DM00071990.pdf\n        // 6.3.22 Temperature sensor characteristics\n        const V25: i32 = 760; // mV\n        const AVG_SLOPE: f32 = 2.5; // mV/C\n\n        let sample_mv = convert_to_millivolts(sample) as i32;\n\n        (sample_mv - V25) as f32 / AVG_SLOPE + 25.0\n    };\n\n    info!(\"VrefInt: {}\", vrefint_sample);\n    const MAX_ADC_SAMPLE: u16 = (1 << 12) - 1;\n    info!(\"VCCA: {} mV\", convert_to_millivolts(MAX_ADC_SAMPLE));\n\n    loop {\n        // Read pin\n        let v = adc.blocking_read(&mut pin, SampleTime::CYCLES112);\n        info!(\"PC1: {} ({} mV)\", v, convert_to_millivolts(v));\n\n        // Read internal temperature\n        let v = adc.blocking_read(&mut temp, SampleTime::CYCLES112);\n        let celcius = convert_to_celcius(v);\n        info!(\"Internal temp: {} ({} C)\", v, celcius);\n\n        // Read internal voltage reference\n        let v = adc.blocking_read(&mut vrefint, SampleTime::CYCLES112);\n        info!(\"VrefInt: {}\", v);\n\n        Timer::after_millis(100).await;\n    }\n}\n"
  },
  {
    "path": "examples/stm32f4/src/bin/adc_dma.rs",
    "content": "#![no_std]\n#![no_main]\nuse cortex_m::singleton;\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_stm32::adc::{Adc, AdcChannel, CONTINUOUS, Exten, RingBufferedAdc, SampleTime};\nuse embassy_stm32::{Peripherals, bind_interrupts, dma, peripherals};\nuse embassy_time::Instant;\nuse {defmt_rtt as _, panic_probe as _};\n\nbind_interrupts!(struct Irqs {\n    DMA2_STREAM0 => dma::InterruptHandler<peripherals::DMA2_CH0>;\n    DMA2_STREAM2 => dma::InterruptHandler<peripherals::DMA2_CH2>;\n});\n\n#[embassy_executor::main]\nasync fn main(spawner: Spawner) {\n    let p = embassy_stm32::init(Default::default());\n    spawner.spawn(adc_task(p).unwrap());\n}\n\n#[embassy_executor::task]\nasync fn adc_task(p: Peripherals) {\n    const ADC_BUF_SIZE: usize = 1024;\n    let adc_data: &mut [u16; ADC_BUF_SIZE] = singleton!(ADCDAT : [u16; ADC_BUF_SIZE] = [0u16; ADC_BUF_SIZE]).unwrap();\n    let adc_data2: &mut [u16; ADC_BUF_SIZE] = singleton!(ADCDAT2 : [u16; ADC_BUF_SIZE] = [0u16; ADC_BUF_SIZE]).unwrap();\n\n    let adc = Adc::new_with_config(p.ADC1, Default::default());\n    let adc2 = Adc::new_with_config(p.ADC2, Default::default());\n\n    let mut adc: RingBufferedAdc<embassy_stm32::peripherals::ADC1> = adc.into_ring_buffered(\n        p.DMA2_CH0,\n        adc_data,\n        Irqs,\n        [\n            (p.PA0.degrade_adc(), SampleTime::CYCLES112),\n            (p.PA2.degrade_adc(), SampleTime::CYCLES112),\n        ]\n        .into_iter(),\n        CONTINUOUS,\n        Exten::DISABLED,\n    );\n    let mut adc2: RingBufferedAdc<embassy_stm32::peripherals::ADC2> = adc2.into_ring_buffered(\n        p.DMA2_CH2,\n        adc_data2,\n        Irqs,\n        [\n            (p.PA1.degrade_adc(), SampleTime::CYCLES112),\n            (p.PA3.degrade_adc(), SampleTime::CYCLES112),\n        ]\n        .into_iter(),\n        CONTINUOUS,\n        Exten::DISABLED,\n    );\n\n    // Note that overrun is a big consideration in this implementation. Whatever task is running the adc.read() calls absolutely must circle back around\n    // to the adc.read() call before the DMA buffer is wrapped around > 1 time. At this point, the overrun is so significant that the context of\n    // what channel is at what index is lost. The buffer must be cleared and reset. This *is* handled here, but allowing this to happen will cause\n    // a reduction of performance as each time the buffer is reset, the adc & dma buffer must be restarted.\n\n    // An interrupt executor with a higher priority than other tasks may be a good approach here, allowing this task to wake and read the buffer most\n    // frequently.\n    let mut tic = Instant::now();\n    let mut buffer1 = [0u16; 512];\n    let mut buffer2 = [0u16; 512];\n    let _ = adc.start();\n    let _ = adc2.start();\n    loop {\n        match adc.read(&mut buffer1).await {\n            Ok(_data) => {\n                let toc = Instant::now();\n                info!(\n                    \"\\n adc1: {} dt = {}, n = {}\",\n                    buffer1[0..16],\n                    (toc - tic).as_micros(),\n                    _data\n                );\n                tic = toc;\n            }\n            Err(e) => {\n                warn!(\"Error: {:?}\", e);\n                buffer1 = [0u16; 512];\n                let _ = adc.start();\n            }\n        }\n\n        match adc2.read(&mut buffer2).await {\n            Ok(_data) => {\n                let toc = Instant::now();\n                info!(\n                    \"\\n adc2: {} dt = {}, n = {}\",\n                    buffer2[0..16],\n                    (toc - tic).as_micros(),\n                    _data\n                );\n                tic = toc;\n            }\n            Err(e) => {\n                warn!(\"Error: {:?}\", e);\n                buffer2 = [0u16; 512];\n                let _ = adc2.start();\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "examples/stm32f4/src/bin/blinky.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_stm32::gpio::{Level, Output, Speed};\nuse embassy_time::Timer;\nuse {defmt_rtt as _, panic_probe as _};\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let p = embassy_stm32::init(Default::default());\n    info!(\"Hello World!\");\n\n    let mut led = Output::new(p.PB7, Level::High, Speed::Low);\n\n    loop {\n        info!(\"high\");\n        led.set_high();\n        Timer::after_millis(300).await;\n\n        info!(\"low\");\n        led.set_low();\n        Timer::after_millis(300).await;\n    }\n}\n"
  },
  {
    "path": "examples/stm32f4/src/bin/button.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse cortex_m_rt::entry;\nuse defmt::*;\nuse embassy_stm32::gpio::{Input, Level, Output, Pull, Speed};\nuse {defmt_rtt as _, panic_probe as _};\n\n#[entry]\nfn main() -> ! {\n    info!(\"Hello World!\");\n\n    let p = embassy_stm32::init(Default::default());\n\n    let button = Input::new(p.PC13, Pull::Down);\n    let mut led1 = Output::new(p.PB0, Level::High, Speed::Low);\n    let _led2 = Output::new(p.PB7, Level::High, Speed::Low);\n    let mut led3 = Output::new(p.PB14, Level::High, Speed::Low);\n\n    loop {\n        if button.is_high() {\n            info!(\"high\");\n            led1.set_high();\n            led3.set_low();\n        } else {\n            info!(\"low\");\n            led1.set_low();\n            led3.set_high();\n        }\n    }\n}\n"
  },
  {
    "path": "examples/stm32f4/src/bin/button_exti.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_stm32::exti::{self, ExtiInput};\nuse embassy_stm32::gpio::Pull;\nuse embassy_stm32::{bind_interrupts, interrupt};\nuse {defmt_rtt as _, panic_probe as _};\n\nbind_interrupts!(\n    pub struct Irqs{\n        EXTI15_10 => exti::InterruptHandler<interrupt::typelevel::EXTI15_10>;\n});\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let p = embassy_stm32::init(Default::default());\n    info!(\"Hello World!\");\n\n    let mut button = ExtiInput::new(p.PC13, p.EXTI13, Pull::Down, Irqs);\n\n    info!(\"Press the USER button...\");\n\n    loop {\n        button.wait_for_rising_edge().await;\n        info!(\"Pressed!\");\n        button.wait_for_falling_edge().await;\n        info!(\"Released!\");\n    }\n}\n"
  },
  {
    "path": "examples/stm32f4/src/bin/can.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_stm32::bind_interrupts;\nuse embassy_stm32::can::filter::Mask32;\nuse embassy_stm32::can::{\n    Can, Fifo, Frame, Rx0InterruptHandler, Rx1InterruptHandler, SceInterruptHandler, StandardId, TxInterruptHandler,\n};\nuse embassy_stm32::gpio::{Input, Pull};\nuse embassy_stm32::peripherals::CAN1;\nuse embassy_time::Instant;\nuse {defmt_rtt as _, panic_probe as _};\n\nbind_interrupts!(struct Irqs {\n    CAN1_RX0 => Rx0InterruptHandler<CAN1>;\n    CAN1_RX1 => Rx1InterruptHandler<CAN1>;\n    CAN1_SCE => SceInterruptHandler<CAN1>;\n    CAN1_TX => TxInterruptHandler<CAN1>;\n});\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    info!(\"Hello World!\");\n\n    let mut p = embassy_stm32::init(Default::default());\n\n    // The next two lines are a workaround for testing without transceiver.\n    // To synchronise to the bus the RX input needs to see a high level.\n    // Use `mem::forget()` to release the borrow on the pin but keep the\n    // pull-up resistor enabled.\n    let rx_pin = Input::new(p.PA11.reborrow(), Pull::Up);\n    core::mem::forget(rx_pin);\n\n    let mut can = Can::new(p.CAN1, p.PA11, p.PA12, Irqs);\n\n    can.modify_filters().enable_bank(0, Fifo::Fifo0, Mask32::accept_all());\n\n    can.modify_config()\n        .set_loopback(true) // Receive own frames\n        .set_silent(true)\n        .set_bitrate(1_000_000);\n\n    can.enable().await;\n\n    let mut i: u8 = 0;\n    loop {\n        let tx_frame = Frame::new_data(unwrap!(StandardId::new(i as _)), &[i]).unwrap();\n        let tx_ts = Instant::now();\n        can.write(&tx_frame).await;\n\n        let envelope = can.read().await.unwrap();\n\n        // We can measure loopback latency by using receive timestamp in the `Envelope`.\n        // Our frame is ~55 bits long (exlcuding bit stuffing), so at 1mbps loopback delay is at least 55 us.\n        // When measured with `tick-hz-1_000_000` actual latency is 80~83 us, giving a combined hardware and software\n        // overhead of ~25 us. Note that CPU frequency can greatly affect the result.\n        let latency = envelope.ts.saturating_duration_since(tx_ts);\n\n        info!(\n            \"loopback frame {=u8}, latency: {} us\",\n            envelope.frame.data()[0],\n            latency.as_micros()\n        );\n        i = i.wrapping_add(1);\n    }\n}\n"
  },
  {
    "path": "examples/stm32f4/src/bin/dac.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_stm32::dac::{DacChannel, Value};\nuse {defmt_rtt as _, panic_probe as _};\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) -> ! {\n    let p = embassy_stm32::init(Default::default());\n    info!(\"Hello World, dude!\");\n\n    let mut dac = DacChannel::new_blocking(p.DAC1, p.PA4);\n\n    loop {\n        for v in 0..=255 {\n            dac.set(Value::Bit8(to_sine_wave(v)));\n        }\n    }\n}\n\nuse micromath::F32Ext;\n\nfn to_sine_wave(v: u8) -> u8 {\n    if v >= 128 {\n        // top half\n        let r = 3.14 * ((v - 128) as f32 / 128.0);\n        (r.sin() * 128.0 + 127.0) as u8\n    } else {\n        // bottom half\n        let r = 3.14 + 3.14 * (v as f32 / 128.0);\n        (r.sin() * 128.0 + 127.0) as u8\n    }\n}\n"
  },
  {
    "path": "examples/stm32f4/src/bin/eth.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_net::tcp::TcpSocket;\nuse embassy_net::{Ipv4Address, StackResources};\nuse embassy_stm32::eth::{Ethernet, GenericPhy, PacketQueue, Sma};\nuse embassy_stm32::peripherals::{ETH, ETH_SMA};\nuse embassy_stm32::rng::Rng;\nuse embassy_stm32::time::Hertz;\nuse embassy_stm32::{Config, bind_interrupts, eth, peripherals, rng};\nuse embassy_time::Timer;\nuse embedded_io_async::Write;\nuse static_cell::StaticCell;\nuse {defmt_rtt as _, panic_probe as _};\n\nbind_interrupts!(struct Irqs {\n    ETH => eth::InterruptHandler;\n    HASH_RNG => rng::InterruptHandler<peripherals::RNG>;\n});\n\ntype Device = Ethernet<'static, ETH, GenericPhy<Sma<'static, ETH_SMA>>>;\n\n#[embassy_executor::task]\nasync fn net_task(mut runner: embassy_net::Runner<'static, Device>) -> ! {\n    runner.run().await\n}\n\n#[embassy_executor::main]\nasync fn main(spawner: Spawner) -> ! {\n    let mut config = Config::default();\n    {\n        use embassy_stm32::rcc::*;\n        config.rcc.hse = Some(Hse {\n            freq: Hertz(8_000_000),\n            mode: HseMode::Bypass,\n        });\n        config.rcc.pll_src = PllSource::HSE;\n        config.rcc.pll = Some(Pll {\n            prediv: PllPreDiv::DIV4,\n            mul: PllMul::MUL180,\n            divp: Some(PllPDiv::DIV2), // 8mhz / 4 * 180 / 2 = 180Mhz.\n            divq: None,\n            divr: None,\n        });\n        config.rcc.ahb_pre = AHBPrescaler::DIV1;\n        config.rcc.apb1_pre = APBPrescaler::DIV4;\n        config.rcc.apb2_pre = APBPrescaler::DIV2;\n        config.rcc.sys = Sysclk::PLL1_P;\n    }\n    let p = embassy_stm32::init(config);\n\n    info!(\"Hello World!\");\n\n    // Generate random seed.\n    let mut rng = Rng::new(p.RNG, Irqs);\n    let mut seed = [0; 8];\n    let _ = rng.async_fill_bytes(&mut seed).await;\n    let seed = u64::from_le_bytes(seed);\n\n    let mac_addr = [0x00, 0x00, 0xDE, 0xAD, 0xBE, 0xEF];\n\n    static PACKETS: StaticCell<PacketQueue<4, 4>> = StaticCell::new();\n    let device = Ethernet::new(\n        PACKETS.init(PacketQueue::<4, 4>::new()),\n        p.ETH,\n        Irqs,\n        p.PA1,\n        p.PA7,\n        p.PC4,\n        p.PC5,\n        p.PG13,\n        p.PB13,\n        p.PG11,\n        mac_addr,\n        p.ETH_SMA,\n        p.PA2,\n        p.PC1,\n    );\n\n    let config = embassy_net::Config::dhcpv4(Default::default());\n    //let config = embassy_net::Config::ipv4_static(embassy_net::StaticConfigV4 {\n    //    address: Ipv4Cidr::new(Ipv4Address::new(10, 42, 0, 61), 24),\n    //    dns_servers: Vec::new(),\n    //    gateway: Some(Ipv4Address::new(10, 42, 0, 1)),\n    //});\n\n    // Init network stack\n    static RESOURCES: StaticCell<StackResources<3>> = StaticCell::new();\n    let (stack, runner) = embassy_net::new(device, config, RESOURCES.init(StackResources::new()), seed);\n\n    // Launch network task\n    spawner.spawn(unwrap!(net_task(runner)));\n\n    // Ensure DHCP configuration is up before trying connect\n    stack.wait_config_up().await;\n\n    info!(\"Network task initialized\");\n\n    // Then we can use it!\n    let mut rx_buffer = [0; 4096];\n    let mut tx_buffer = [0; 4096];\n\n    loop {\n        let mut socket = TcpSocket::new(stack, &mut rx_buffer, &mut tx_buffer);\n\n        socket.set_timeout(Some(embassy_time::Duration::from_secs(10)));\n\n        let remote_endpoint = (Ipv4Address::new(10, 42, 0, 1), 8000);\n        info!(\"connecting...\");\n        let r = socket.connect(remote_endpoint).await;\n        if let Err(e) = r {\n            info!(\"connect error: {:?}\", e);\n            Timer::after_secs(1).await;\n            continue;\n        }\n        info!(\"connected!\");\n        let buf = [0; 1024];\n        loop {\n            let r = socket.write_all(&buf).await;\n            if let Err(e) = r {\n                info!(\"write error: {:?}\", e);\n                break;\n            }\n            Timer::after_secs(1).await;\n        }\n    }\n}\n"
  },
  {
    "path": "examples/stm32f4/src/bin/eth_compliance_test.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_stm32::eth::{Ethernet, PacketQueue, StationManagement};\nuse embassy_stm32::time::Hertz;\nuse embassy_stm32::{Config, bind_interrupts, eth, peripherals, rng};\nuse embassy_time::Timer;\nuse static_cell::StaticCell;\nuse {defmt_rtt as _, panic_probe as _};\n\nbind_interrupts!(struct Irqs {\n    ETH => eth::InterruptHandler;\n    HASH_RNG => rng::InterruptHandler<peripherals::RNG>;\n});\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) -> ! {\n    let mut config = Config::default();\n    {\n        use embassy_stm32::rcc::*;\n        config.rcc.hse = Some(Hse {\n            freq: Hertz(8_000_000),\n            mode: HseMode::Bypass,\n        });\n        config.rcc.pll_src = PllSource::HSE;\n        config.rcc.pll = Some(Pll {\n            prediv: PllPreDiv::DIV4,\n            mul: PllMul::MUL180,\n            divp: Some(PllPDiv::DIV2), // 8mhz / 4 * 180 / 2 = 180Mhz.\n            divq: None,\n            divr: None,\n        });\n        config.rcc.ahb_pre = AHBPrescaler::DIV1;\n        config.rcc.apb1_pre = APBPrescaler::DIV4;\n        config.rcc.apb2_pre = APBPrescaler::DIV2;\n        config.rcc.sys = Sysclk::PLL1_P;\n    }\n    let p = embassy_stm32::init(config);\n\n    info!(\"Hello Compliance World!\");\n\n    let mac_addr = [0x00, 0x00, 0xDE, 0xAD, 0xBE, 0xEF];\n\n    static PACKETS: StaticCell<PacketQueue<4, 4>> = StaticCell::new();\n    let mut device = Ethernet::new(\n        PACKETS.init(PacketQueue::<4, 4>::new()),\n        p.ETH,\n        Irqs,\n        p.PA1,\n        p.PA7,\n        p.PC4,\n        p.PC5,\n        p.PG13,\n        p.PB13,\n        p.PG11,\n        mac_addr,\n        p.ETH_SMA,\n        p.PA2,\n        p.PC1,\n    );\n\n    let sm = device.phy_mut().station_management();\n\n    const PHY_ADDR: u8 = 0;\n    // Just an example. Exact register settings depend on the specific PHY and test.\n    sm.smi_write(PHY_ADDR, 0, 0x2100);\n    sm.smi_write(PHY_ADDR, 11, 0xA000);\n\n    // NB: Remember to reset the PHY after testing before starting the networking stack\n\n    loop {\n        Timer::after_secs(1).await;\n    }\n}\n"
  },
  {
    "path": "examples/stm32f4/src/bin/eth_w5500.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_net::tcp::TcpSocket;\nuse embassy_net::{Ipv4Address, StackResources};\nuse embassy_net_wiznet::chip::W5500;\nuse embassy_net_wiznet::{Device, Runner, State};\nuse embassy_stm32::exti::{self, ExtiInput};\nuse embassy_stm32::gpio::{Level, Output, Pull, Speed};\nuse embassy_stm32::mode::Async;\nuse embassy_stm32::rng::Rng;\nuse embassy_stm32::spi::Spi;\nuse embassy_stm32::spi::mode::Master;\nuse embassy_stm32::time::Hertz;\nuse embassy_stm32::{Config, bind_interrupts, dma, interrupt, peripherals, rng, spi};\nuse embassy_time::{Delay, Timer};\nuse embedded_hal_bus::spi::ExclusiveDevice;\nuse embedded_io_async::Write;\nuse static_cell::StaticCell;\nuse {defmt_rtt as _, panic_probe as _};\n\nbind_interrupts!(struct Irqs {\n    HASH_RNG => rng::InterruptHandler<peripherals::RNG>;\n    EXTI0 => exti::InterruptHandler<interrupt::typelevel::EXTI0>;\n    DMA2_STREAM0 => dma::InterruptHandler<peripherals::DMA2_CH0>;\n    DMA2_STREAM3 => dma::InterruptHandler<peripherals::DMA2_CH3>;\n});\n\ntype EthernetSPI = ExclusiveDevice<Spi<'static, Async, Master>, Output<'static>, Delay>;\n#[embassy_executor::task]\nasync fn ethernet_task(runner: Runner<'static, W5500, EthernetSPI, ExtiInput<'static, Async>, Output<'static>>) -> ! {\n    runner.run().await\n}\n\n#[embassy_executor::task]\nasync fn net_task(mut runner: embassy_net::Runner<'static, Device<'static>>) -> ! {\n    runner.run().await\n}\n\n#[embassy_executor::main]\nasync fn main(spawner: Spawner) -> ! {\n    let mut config = Config::default();\n    {\n        use embassy_stm32::rcc::*;\n        config.rcc.hse = Some(Hse {\n            freq: Hertz(8_000_000),\n            mode: HseMode::Bypass,\n        });\n        config.rcc.pll_src = PllSource::HSE;\n        config.rcc.pll = Some(Pll {\n            prediv: PllPreDiv::DIV4,\n            mul: PllMul::MUL180,\n            divp: Some(PllPDiv::DIV2), // 8mhz / 4 * 180 / 2 = 180Mhz.\n            divq: None,\n            divr: None,\n        });\n        config.rcc.ahb_pre = AHBPrescaler::DIV1;\n        config.rcc.apb1_pre = APBPrescaler::DIV4;\n        config.rcc.apb2_pre = APBPrescaler::DIV2;\n        config.rcc.sys = Sysclk::PLL1_P;\n    }\n    let p = embassy_stm32::init(config);\n\n    info!(\"Hello World!\");\n\n    // Generate random seed\n    let mut rng = Rng::new(p.RNG, Irqs);\n    let mut seed = [0; 8];\n    unwrap!(rng.async_fill_bytes(&mut seed).await);\n    let seed = u64::from_le_bytes(seed);\n\n    let mut spi_cfg = spi::Config::default();\n    spi_cfg.frequency = Hertz(50_000_000); // up to 50m works\n    let (miso, mosi, clk) = (p.PA6, p.PA7, p.PA5);\n    let spi = Spi::new(p.SPI1, clk, mosi, miso, p.DMA2_CH3, p.DMA2_CH0, Irqs, spi_cfg);\n    let cs = Output::new(p.PA4, Level::High, Speed::VeryHigh);\n    let spi = unwrap!(ExclusiveDevice::new(spi, cs, Delay));\n\n    let w5500_int = ExtiInput::new(p.PB0, p.EXTI0, Pull::Up, Irqs);\n    let w5500_reset = Output::new(p.PB1, Level::High, Speed::VeryHigh);\n\n    let mac_addr = [0x02, 234, 3, 4, 82, 231];\n    static STATE: StaticCell<State<2, 2>> = StaticCell::new();\n    let state = STATE.init(State::<2, 2>::new());\n    let (device, runner) = embassy_net_wiznet::new(mac_addr, state, spi, w5500_int, w5500_reset)\n        .await\n        .unwrap();\n    spawner.spawn(unwrap!(ethernet_task(runner)));\n\n    let config = embassy_net::Config::dhcpv4(Default::default());\n    //let config = embassy_net::Config::ipv4_static(embassy_net::StaticConfigV4 {\n    //    address: Ipv4Cidr::new(Ipv4Address::new(10, 42, 0, 61), 24),\n    //    dns_servers: Vec::new(),\n    //    gateway: Some(Ipv4Address::new(10, 42, 0, 1)),\n    //});\n\n    static RESOURCES: StaticCell<StackResources<3>> = StaticCell::new();\n    let (stack, runner) = embassy_net::new(device, config, RESOURCES.init(StackResources::new()), seed);\n\n    // Launch network task\n    spawner.spawn(unwrap!(net_task(runner)));\n\n    // Ensure DHCP configuration is up before trying connect\n    stack.wait_config_up().await;\n\n    info!(\"Network task initialized\");\n\n    // Then we can use it!\n    let mut rx_buffer = [0; 1024];\n    let mut tx_buffer = [0; 1024];\n\n    loop {\n        let mut socket = TcpSocket::new(stack, &mut rx_buffer, &mut tx_buffer);\n\n        socket.set_timeout(Some(embassy_time::Duration::from_secs(10)));\n\n        let remote_endpoint = (Ipv4Address::new(10, 42, 0, 1), 8000);\n        info!(\"connecting...\");\n        let r = socket.connect(remote_endpoint).await;\n        if let Err(e) = r {\n            info!(\"connect error: {:?}\", e);\n            Timer::after_secs(1).await;\n            continue;\n        }\n        info!(\"connected!\");\n        let buf = [0; 1024];\n        loop {\n            let r = socket.write_all(&buf).await;\n            if let Err(e) = r {\n                info!(\"write error: {:?}\", e);\n                break;\n            }\n            Timer::after_secs(1).await;\n        }\n    }\n}\n"
  },
  {
    "path": "examples/stm32f4/src/bin/flash.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse defmt::{info, unwrap};\nuse embassy_executor::Spawner;\nuse embassy_stm32::flash::{Blocking, Flash};\nuse {defmt_rtt as _, panic_probe as _};\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let p = embassy_stm32::init(Default::default());\n    info!(\"Hello Flash!\");\n\n    // Once can also call `into_regions()` to get access to NorFlash implementations\n    // for each of the unique characteristics.\n    let mut f = Flash::new_blocking(p.FLASH);\n\n    // Sector 5\n    test_flash(&mut f, 128 * 1024, 128 * 1024);\n\n    // Sectors 11..=16, across banks (128K, 16K, 16K, 16K, 16K, 64K)\n    test_flash(&mut f, (1024 - 128) * 1024, 256 * 1024);\n\n    // Sectors 23, last in bank 2\n    test_flash(&mut f, (2048 - 128) * 1024, 128 * 1024);\n}\n\nfn test_flash(f: &mut Flash<'_, Blocking>, offset: u32, size: u32) {\n    info!(\"Testing offset: {=u32:#X}, size: {=u32:#X}\", offset, size);\n\n    info!(\"Reading...\");\n    let mut buf = [0u8; 32];\n    unwrap!(f.blocking_read(offset, &mut buf));\n    info!(\"Read: {=[u8]:x}\", buf);\n\n    info!(\"Erasing...\");\n    unwrap!(f.blocking_erase(offset, offset + size));\n\n    info!(\"Reading...\");\n    let mut buf = [0u8; 32];\n    unwrap!(f.blocking_read(offset, &mut buf));\n    info!(\"Read after erase: {=[u8]:x}\", buf);\n\n    info!(\"Writing...\");\n    unwrap!(f.blocking_write(\n        offset,\n        &[\n            1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29,\n            30, 31, 32\n        ]\n    ));\n\n    info!(\"Reading...\");\n    let mut buf = [0u8; 32];\n    unwrap!(f.blocking_read(offset, &mut buf));\n    info!(\"Read: {=[u8]:x}\", buf);\n    assert_eq!(\n        &buf[..],\n        &[\n            1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29,\n            30, 31, 32\n        ]\n    );\n}\n"
  },
  {
    "path": "examples/stm32f4/src/bin/flash_async.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse defmt::{info, unwrap};\nuse embassy_executor::Spawner;\nuse embassy_stm32::flash::{Flash, InterruptHandler};\nuse embassy_stm32::gpio::{AnyPin, Level, Output, Speed};\nuse embassy_stm32::{Peri, bind_interrupts};\nuse embassy_time::Timer;\nuse {defmt_rtt as _, panic_probe as _};\n\nbind_interrupts!(struct Irqs {\n    FLASH => InterruptHandler;\n});\n\n#[embassy_executor::main]\nasync fn main(spawner: Spawner) {\n    let p = embassy_stm32::init(Default::default());\n    info!(\"Hello Flash!\");\n\n    let mut f = Flash::new(p.FLASH, Irqs);\n\n    // Led should blink uninterrupted during ~2sec erase operation\n    spawner.spawn(blinky(p.PB7.into()).unwrap());\n\n    // Test on bank 2 in order not to stall CPU.\n    test_flash(&mut f, 1024 * 1024, 128 * 1024).await;\n}\n\n#[embassy_executor::task]\nasync fn blinky(p: Peri<'static, AnyPin>) {\n    let mut led = Output::new(p, Level::High, Speed::Low);\n\n    loop {\n        info!(\"high\");\n        led.set_high();\n        Timer::after_millis(300).await;\n\n        info!(\"low\");\n        led.set_low();\n        Timer::after_millis(300).await;\n    }\n}\n\nasync fn test_flash<'a>(f: &mut Flash<'a>, offset: u32, size: u32) {\n    info!(\"Testing offset: {=u32:#X}, size: {=u32:#X}\", offset, size);\n\n    info!(\"Reading...\");\n    let mut buf = [0u8; 32];\n    unwrap!(f.blocking_read(offset, &mut buf));\n    info!(\"Read: {=[u8]:x}\", buf);\n\n    info!(\"Erasing...\");\n    unwrap!(f.erase(offset, offset + size).await);\n\n    info!(\"Reading...\");\n    let mut buf = [0u8; 32];\n    unwrap!(f.blocking_read(offset, &mut buf));\n    info!(\"Read after erase: {=[u8]:x}\", buf);\n\n    info!(\"Writing...\");\n    unwrap!(\n        f.write(\n            offset,\n            &[\n                1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28,\n                29, 30, 31, 32\n            ]\n        )\n        .await\n    );\n\n    info!(\"Reading...\");\n    let mut buf = [0u8; 32];\n    unwrap!(f.blocking_read(offset, &mut buf));\n    info!(\"Read: {=[u8]:x}\", buf);\n    assert_eq!(\n        &buf[..],\n        &[\n            1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29,\n            30, 31, 32\n        ]\n    );\n}\n"
  },
  {
    "path": "examples/stm32f4/src/bin/hello.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse defmt::info;\nuse embassy_executor::Spawner;\nuse embassy_stm32::Config;\nuse embassy_time::Timer;\nuse {defmt_rtt as _, panic_probe as _};\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) -> ! {\n    let config = Config::default();\n    let _p = embassy_stm32::init(config);\n\n    loop {\n        info!(\"Hello World!\");\n        Timer::after_secs(1).await;\n    }\n}\n"
  },
  {
    "path": "examples/stm32f4/src/bin/i2c.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_stm32::i2c::{Error, I2c};\nuse {defmt_rtt as _, panic_probe as _};\n\nconst ADDRESS: u8 = 0x5F;\nconst WHOAMI: u8 = 0x0F;\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    info!(\"Hello world!\");\n    let p = embassy_stm32::init(Default::default());\n\n    let mut i2c = I2c::new_blocking(p.I2C2, p.PB10, p.PB11, Default::default());\n\n    let mut data = [0u8; 1];\n\n    match i2c.blocking_write_read(ADDRESS, &[WHOAMI], &mut data) {\n        Ok(()) => info!(\"Whoami: {}\", data[0]),\n        Err(Error::Timeout) => error!(\"Operation timed out\"),\n        Err(e) => error!(\"I2c Error: {:?}\", e),\n    }\n}\n"
  },
  {
    "path": "examples/stm32f4/src/bin/i2c_async.rs",
    "content": "#![no_std]\n#![no_main]\n\n// Example originally designed for stm32f411ceu6 reading an A1454 hall effect sensor on I2C1\n// DMA peripherals changed to compile for stm32f429zi, for the CI.\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_stm32::i2c::I2c;\nuse embassy_stm32::{bind_interrupts, dma, i2c, peripherals};\nuse {defmt_rtt as _, panic_probe as _};\n\nconst ADDRESS: u8 = 96;\n\nbind_interrupts!(struct Irqs {\n    I2C1_EV => i2c::EventInterruptHandler<peripherals::I2C1>;\n    I2C1_ER => i2c::ErrorInterruptHandler<peripherals::I2C1>;\n    DMA1_STREAM0 => dma::InterruptHandler<peripherals::DMA1_CH0>;\n    DMA1_STREAM6 => dma::InterruptHandler<peripherals::DMA1_CH6>;\n});\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    info!(\"Hello world!\");\n    let p = embassy_stm32::init(Default::default());\n\n    let mut i2c = I2c::new(p.I2C1, p.PB8, p.PB7, p.DMA1_CH6, p.DMA1_CH0, Irqs, Default::default());\n\n    loop {\n        let a1454_read_sensor_command = [0x1F];\n        let mut sensor_data_buffer: [u8; 4] = [0, 0, 0, 0];\n\n        match i2c\n            .write_read(ADDRESS, &a1454_read_sensor_command, &mut sensor_data_buffer)\n            .await\n        {\n            Ok(()) => {\n                // Convert 12-bit signed integer into 16-bit signed integer.\n                // Is the 12 bit number negative?\n                if (sensor_data_buffer[2] & 0b00001000) == 0b0001000 {\n                    sensor_data_buffer[2] = sensor_data_buffer[2] | 0b11110000;\n                }\n\n                let mut sensor_value_raw: u16 = sensor_data_buffer[3].into();\n                sensor_value_raw |= (sensor_data_buffer[2] as u16) << 8;\n                let sensor_value: u16 = sensor_value_raw.into();\n                let sensor_value = sensor_value as i16;\n                info!(\"Data: {}\", sensor_value);\n            }\n            Err(e) => error!(\"I2C Error during read: {:?}\", e),\n        }\n    }\n}\n"
  },
  {
    "path": "examples/stm32f4/src/bin/i2c_comparison.rs",
    "content": "#![no_std]\n#![no_main]\n\n// Example originally designed for stm32f411ceu6 with three A1454 hall effect sensors, connected to I2C1, 2 and 3\n// on the pins referenced in the peripheral definitions.\n// Pins and DMA peripherals changed to compile for stm32f429zi, to work with the CI.\n// MUST be compiled in release mode to see actual performance, otherwise the async transactions take 2x\n// as long to complete as the blocking ones!\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_stm32::i2c::I2c;\nuse embassy_stm32::{bind_interrupts, dma, i2c, peripherals};\nuse embassy_time::Instant;\nuse futures_util::future::try_join3;\nuse {defmt_rtt as _, panic_probe as _};\n\nconst ADDRESS: u8 = 96;\n\nbind_interrupts!(struct Irqs {\n    I2C1_EV => i2c::EventInterruptHandler<peripherals::I2C1>;\n    I2C1_ER => i2c::ErrorInterruptHandler<peripherals::I2C1>;\n    I2C2_EV => i2c::EventInterruptHandler<peripherals::I2C2>;\n    I2C2_ER => i2c::ErrorInterruptHandler<peripherals::I2C2>;\n    I2C3_EV => i2c::EventInterruptHandler<peripherals::I2C3>;\n    I2C3_ER => i2c::ErrorInterruptHandler<peripherals::I2C3>;\n    DMA1_STREAM0 => dma::InterruptHandler<peripherals::DMA1_CH0>;\n    DMA1_STREAM6 => dma::InterruptHandler<peripherals::DMA1_CH6>;\n    DMA1_STREAM7 => dma::InterruptHandler<peripherals::DMA1_CH7>;\n    DMA1_STREAM3 => dma::InterruptHandler<peripherals::DMA1_CH3>;\n    DMA1_STREAM4 => dma::InterruptHandler<peripherals::DMA1_CH4>;\n    DMA1_STREAM2 => dma::InterruptHandler<peripherals::DMA1_CH2>;\n});\n\n/// Convert 12-bit signed integer within a 4 byte long buffer into 16-bit signed integer.\nfn a1454_buf_to_i16(buffer: &[u8; 4]) -> i16 {\n    let lower = buffer[3];\n    let mut upper = buffer[2];\n    // Fill in additional 1s if the 12 bit number is negative.\n    if (upper & 0b00001000) == 0b0001000 {\n        upper = upper | 0b11110000;\n    }\n\n    let mut sensor_value_raw: u16 = lower.into();\n    sensor_value_raw |= (upper as u16) << 8;\n    let sensor_value: u16 = sensor_value_raw.into();\n    let sensor_value = sensor_value as i16;\n    sensor_value\n}\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    info!(\"Setting up peripherals.\");\n    let p = embassy_stm32::init(Default::default());\n\n    let mut i2c1 = I2c::new(p.I2C1, p.PB8, p.PB7, p.DMA1_CH6, p.DMA1_CH0, Irqs, Default::default());\n\n    let mut i2c2 = I2c::new(p.I2C2, p.PB10, p.PB11, p.DMA1_CH7, p.DMA1_CH3, Irqs, Default::default());\n\n    let mut i2c3 = I2c::new(p.I2C3, p.PA8, p.PC9, p.DMA1_CH4, p.DMA1_CH2, Irqs, Default::default());\n\n    let a1454_read_sensor_command = [0x1F];\n    let mut i2c1_buffer: [u8; 4] = [0, 0, 0, 0];\n    let mut i2c2_buffer: [u8; 4] = [0, 0, 0, 0];\n    let mut i2c3_buffer: [u8; 4] = [0, 0, 0, 0];\n    loop {\n        // Blocking reads one after the other. Completes in about 2000us.\n        let blocking_read_start_us = Instant::now().as_micros();\n        match i2c1.blocking_write_read(ADDRESS, &a1454_read_sensor_command, &mut i2c1_buffer) {\n            Ok(()) => {}\n            Err(e) => error!(\"I2C Error: {:?}\", e),\n        }\n        match i2c2.blocking_write_read(ADDRESS, &a1454_read_sensor_command, &mut i2c2_buffer) {\n            Ok(()) => {}\n            Err(e) => error!(\"I2C Error: {:?}\", e),\n        }\n        match i2c3.blocking_write_read(ADDRESS, &a1454_read_sensor_command, &mut i2c3_buffer) {\n            Ok(()) => {}\n            Err(e) => error!(\"I2C Error: {:?}\", e),\n        }\n        let blocking_read_total_us = Instant::now().as_micros() - blocking_read_start_us;\n        info!(\n            \"Blocking reads completed in {}us: i2c1: {} i2c2: {} i2c3: {}\",\n            blocking_read_total_us,\n            a1454_buf_to_i16(&i2c1_buffer),\n            a1454_buf_to_i16(&i2c2_buffer),\n            a1454_buf_to_i16(&i2c3_buffer)\n        );\n\n        // Async reads overlapping. Completes in about 1000us.\n        let async_read_start_us = Instant::now().as_micros();\n\n        let i2c1_result = i2c1.write_read(ADDRESS, &a1454_read_sensor_command, &mut i2c1_buffer);\n        let i2c2_result = i2c2.write_read(ADDRESS, &a1454_read_sensor_command, &mut i2c2_buffer);\n        let i2c3_result = i2c3.write_read(ADDRESS, &a1454_read_sensor_command, &mut i2c3_buffer);\n\n        // Wait for all three transactions to finish, or any one of them to fail.\n        match try_join3(i2c1_result, i2c2_result, i2c3_result).await {\n            Ok(_) => {\n                let async_read_total_us = Instant::now().as_micros() - async_read_start_us;\n                info!(\n                    \"Async reads completed in {}us: i2c1: {} i2c2: {} i2c3: {}\",\n                    async_read_total_us,\n                    a1454_buf_to_i16(&i2c1_buffer),\n                    a1454_buf_to_i16(&i2c2_buffer),\n                    a1454_buf_to_i16(&i2c3_buffer)\n                );\n            }\n            Err(e) => error!(\"I2C Error during async write-read: {}\", e),\n        };\n    }\n}\n"
  },
  {
    "path": "examples/stm32f4/src/bin/i2c_master_test_blocking.rs",
    "content": "//! I2C Blocking Master Test\n//!\n//! Tests the blocking I2C master implementation with an external I2C slave device.\n//! This is useful for testing with a logic analyzer or external slave like the\n//! Digilent Analog Discovery.\n//!\n//! Hardware setup (for NUCLEO-F401RE):\n//! - PB8: I2C1 SCL\n//! - PB9: I2C1 SDA\n//! - Add 4.7k pull-up resistors to 3.3V on both SCL and SDA lines\n//! - Connect to external I2C slave device\n//!\n//! # Test Coverage\n//!\n//! This test covers:\n//! - Write operations (slave receiving data from master)\n//! - Read operations (slave sending data to master)\n//! - Combined write_read transactions (RESTART condition)\n\n#![no_std]\n#![no_main]\n\nuse defmt::{error, info};\nuse embassy_executor::Spawner;\nuse embassy_stm32::i2c::{self, I2c};\nuse embassy_stm32::time::khz;\nuse embassy_stm32::{bind_interrupts, dma, peripherals};\nuse embassy_time::Timer;\nuse {defmt_rtt as _, panic_probe as _};\n\nconst I2C_ADDR: u8 = 0x42;\n\nbind_interrupts!(struct Irqs {\n    I2C1_EV => i2c::EventInterruptHandler<peripherals::I2C1>;\n    I2C1_ER => i2c::ErrorInterruptHandler<peripherals::I2C1>;\n    DMA1_STREAM0 => dma::InterruptHandler<peripherals::DMA1_CH0>;\n    DMA1_STREAM6 => dma::InterruptHandler<peripherals::DMA1_CH6>;\n});\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let p = embassy_stm32::init(Default::default());\n\n    let i2c_frequency = khz(100);\n\n    info!(\"==============================================\");\n    info!(\"I2C Blocking Master Test @ {} kHz\", i2c_frequency.0 / 1000);\n    info!(\"I2C1 (master) on PB8 (SCL) / PB9 (SDA)\");\n    info!(\"Slave address: 0x{:02X}\", I2C_ADDR);\n    info!(\"==============================================\");\n\n    // I2C1 as master (PB8=SCL, PB9=SDA)\n    // Note: We still need DMA channels for the I2c::new constructor, but blocking\n    // operations don't use them.\n    let i2c_config = {\n        let mut config = i2c::Config::default();\n        config.frequency = i2c_frequency;\n        config\n    };\n    let mut i2c = I2c::new(p.I2C1, p.PB8, p.PB9, p.DMA1_CH6, p.DMA1_CH0, Irqs, i2c_config);\n\n    // Give external slave time to initialize\n    Timer::after_millis(500).await;\n\n    info!(\"[Master] Starting test suite...\");\n    info!(\"\");\n\n    let mut test_num = 0;\n    let mut passed = 0;\n    let mut failed = 0;\n\n    // =========================================================================\n    // WRITE TESTS - Master writes to slave\n    // =========================================================================\n\n    // Test: Write 1 byte\n    test_num += 1;\n    info!(\"--- Test {}: Write 1 byte ---\", test_num);\n    {\n        let write_data = [0x11];\n        info!(\"[Master] Writing {:02X}\", write_data);\n        match i2c.blocking_write(I2C_ADDR, &write_data) {\n            Ok(()) => {\n                info!(\"[Master] Write successful\");\n                passed += 1;\n            }\n            Err(e) => {\n                error!(\"[Master] Write error: {:?}\", e);\n                failed += 1;\n            }\n        }\n    }\n    Timer::after_millis(100).await;\n\n    // Test: Write 2 bytes\n    test_num += 1;\n    info!(\"--- Test {}: Write 2 bytes ---\", test_num);\n    {\n        let write_data = [0x21, 0x22];\n        info!(\"[Master] Writing {:02X}\", write_data);\n        match i2c.blocking_write(I2C_ADDR, &write_data) {\n            Ok(()) => {\n                info!(\"[Master] Write successful\");\n                passed += 1;\n            }\n            Err(e) => {\n                error!(\"[Master] Write error: {:?}\", e);\n                failed += 1;\n            }\n        }\n    }\n    Timer::after_millis(100).await;\n\n    // Test: Write 4 bytes\n    test_num += 1;\n    info!(\"--- Test {}: Write 4 bytes ---\", test_num);\n    {\n        let write_data = [0x31, 0x32, 0x33, 0x34];\n        info!(\"[Master] Writing {:02X}\", write_data);\n        match i2c.blocking_write(I2C_ADDR, &write_data) {\n            Ok(()) => {\n                info!(\"[Master] Write successful\");\n                passed += 1;\n            }\n            Err(e) => {\n                error!(\"[Master] Write error: {:?}\", e);\n                failed += 1;\n            }\n        }\n    }\n    Timer::after_millis(100).await;\n\n    // Test: Write 8 bytes\n    test_num += 1;\n    info!(\"--- Test {}: Write 8 bytes ---\", test_num);\n    {\n        let write_data = [0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48];\n        info!(\"[Master] Writing {:02X}\", write_data);\n        match i2c.blocking_write(I2C_ADDR, &write_data) {\n            Ok(()) => {\n                info!(\"[Master] Write successful\");\n                passed += 1;\n            }\n            Err(e) => {\n                error!(\"[Master] Write error: {:?}\", e);\n                failed += 1;\n            }\n        }\n    }\n    Timer::after_millis(100).await;\n\n    // =========================================================================\n    // READ TESTS - Master reads from slave\n    // =========================================================================\n\n    // Test: Read 1 byte\n    test_num += 1;\n    info!(\"--- Test {}: Read 1 byte ---\", test_num);\n    {\n        let mut read_buf = [0u8; 1];\n        info!(\"[Master] Reading {} byte(s)\", read_buf.len());\n        match i2c.blocking_read(I2C_ADDR, &mut read_buf) {\n            Ok(()) => {\n                info!(\"[Master] Read result: {:02X}\", read_buf);\n                passed += 1;\n            }\n            Err(e) => {\n                error!(\"[Master] Read error: {:?}\", e);\n                failed += 1;\n            }\n        }\n    }\n    Timer::after_millis(100).await;\n\n    // Test: Read 2 bytes\n    test_num += 1;\n    info!(\"--- Test {}: Read 2 bytes ---\", test_num);\n    {\n        let mut read_buf = [0u8; 2];\n        info!(\"[Master] Reading {} byte(s)\", read_buf.len());\n        match i2c.blocking_read(I2C_ADDR, &mut read_buf) {\n            Ok(()) => {\n                info!(\"[Master] Read result: {:02X}\", read_buf);\n                passed += 1;\n            }\n            Err(e) => {\n                error!(\"[Master] Read error: {:?}\", e);\n                failed += 1;\n            }\n        }\n    }\n    Timer::after_millis(100).await;\n\n    // Test: Read 4 bytes\n    test_num += 1;\n    info!(\"--- Test {}: Read 4 bytes ---\", test_num);\n    {\n        let mut read_buf = [0u8; 4];\n        info!(\"[Master] Reading {} byte(s)\", read_buf.len());\n        match i2c.blocking_read(I2C_ADDR, &mut read_buf) {\n            Ok(()) => {\n                info!(\"[Master] Read result: {:02X}\", read_buf);\n                passed += 1;\n            }\n            Err(e) => {\n                error!(\"[Master] Read error: {:?}\", e);\n                failed += 1;\n            }\n        }\n    }\n    Timer::after_millis(100).await;\n\n    // Test: Read 8 bytes\n    test_num += 1;\n    info!(\"--- Test {}: Read 8 bytes ---\", test_num);\n    {\n        let mut read_buf = [0u8; 8];\n        info!(\"[Master] Reading {} byte(s)\", read_buf.len());\n        match i2c.blocking_read(I2C_ADDR, &mut read_buf) {\n            Ok(()) => {\n                info!(\"[Master] Read result: {:02X}\", read_buf);\n                passed += 1;\n            }\n            Err(e) => {\n                error!(\"[Master] Read error: {:?}\", e);\n                failed += 1;\n            }\n        }\n    }\n    Timer::after_millis(100).await;\n\n    // =========================================================================\n    // COMBINED WRITE_READ TESTS (RESTART condition)\n    // =========================================================================\n\n    // Test: Combined write_read (1 byte write, 4 byte read)\n    test_num += 1;\n    info!(\"--- Test {}: Combined write_read (1 byte write) ---\", test_num);\n    {\n        let write_data = [0x51];\n        let mut read_buf = [0u8; 4];\n        info!(\n            \"[Master] write_read: writing {:02X}, reading {} bytes\",\n            write_data,\n            read_buf.len()\n        );\n        match i2c.blocking_write_read(I2C_ADDR, &write_data, &mut read_buf) {\n            Ok(()) => {\n                info!(\"[Master] Read result: {:02X}\", read_buf);\n                passed += 1;\n            }\n            Err(e) => {\n                error!(\"[Master] write_read error: {:?}\", e);\n                failed += 1;\n            }\n        }\n    }\n    Timer::after_millis(100).await;\n\n    // Test: Combined write_read (2 byte write, 4 byte read)\n    test_num += 1;\n    info!(\"--- Test {}: Combined write_read (2 byte write) ---\", test_num);\n    {\n        let write_data = [0x61, 0x62];\n        let mut read_buf = [0u8; 4];\n        info!(\n            \"[Master] write_read: writing {:02X}, reading {} bytes\",\n            write_data,\n            read_buf.len()\n        );\n        match i2c.blocking_write_read(I2C_ADDR, &write_data, &mut read_buf) {\n            Ok(()) => {\n                info!(\"[Master] Read result: {:02X}\", read_buf);\n                passed += 1;\n            }\n            Err(e) => {\n                error!(\"[Master] write_read error: {:?}\", e);\n                failed += 1;\n            }\n        }\n    }\n    Timer::after_millis(100).await;\n\n    // Test: Combined write_read (4 byte write, 4 byte read)\n    test_num += 1;\n    info!(\"--- Test {}: Combined write_read (4 byte write) ---\", test_num);\n    {\n        let write_data = [0x71, 0x72, 0x73, 0x74];\n        let mut read_buf = [0u8; 4];\n        info!(\n            \"[Master] write_read: writing {:02X}, reading {} bytes\",\n            write_data,\n            read_buf.len()\n        );\n        match i2c.blocking_write_read(I2C_ADDR, &write_data, &mut read_buf) {\n            Ok(()) => {\n                info!(\"[Master] Read result: {:02X}\", read_buf);\n                passed += 1;\n            }\n            Err(e) => {\n                error!(\"[Master] write_read error: {:?}\", e);\n                failed += 1;\n            }\n        }\n    }\n    Timer::after_millis(100).await;\n\n    // Test: Combined write_read (8 byte write, 4 byte read)\n    test_num += 1;\n    info!(\"--- Test {}: Combined write_read (8 byte write) ---\", test_num);\n    {\n        let write_data = [0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88];\n        let mut read_buf = [0u8; 4];\n        info!(\n            \"[Master] write_read: writing {:02X}, reading {} bytes\",\n            write_data,\n            read_buf.len()\n        );\n        match i2c.blocking_write_read(I2C_ADDR, &write_data, &mut read_buf) {\n            Ok(()) => {\n                info!(\"[Master] Read result: {:02X}\", read_buf);\n                passed += 1;\n            }\n            Err(e) => {\n                error!(\"[Master] write_read error: {:?}\", e);\n                failed += 1;\n            }\n        }\n    }\n    Timer::after_millis(100).await;\n\n    // =========================================================================\n    // TEST SUMMARY\n    // =========================================================================\n\n    info!(\"\");\n    info!(\"==============================================\");\n    info!(\"Test Summary: {} passed, {} failed\", passed, failed);\n    info!(\"==============================================\");\n\n    // Keep running\n    loop {\n        Timer::after_millis(1000).await;\n    }\n}\n"
  },
  {
    "path": "examples/stm32f4/src/bin/i2c_slave_async.rs",
    "content": "//! I2C slave example using async operations with DMA\n//!\n//! This example demonstrates DMA-accelerated I2C slave operations,\n//! which provide better performance and lower CPU overhead for\n//! high-frequency I2C transactions.\n\n#![no_std]\n#![no_main]\n\nuse defmt::{error, info};\nuse embassy_executor::Spawner;\nuse embassy_stm32::i2c::{self, Address, I2c, SlaveAddrConfig, SlaveCommand, SlaveCommandKind};\nuse embassy_stm32::time::Hertz;\nuse embassy_stm32::{bind_interrupts, dma, peripherals};\nuse embassy_sync::blocking_mutex::raw::ThreadModeRawMutex;\nuse embassy_sync::mutex::Mutex;\nuse embassy_time::{Duration, Timer};\nuse {defmt_rtt as _, panic_probe as _};\n\npub const I2C_SLAVE_ADDR: u8 = 0x42;\npub const BUFFER_SIZE: usize = 8;\nstatic I2C_BUFFER: Mutex<ThreadModeRawMutex, [u8; BUFFER_SIZE]> = Mutex::new([0; BUFFER_SIZE]);\n\nbind_interrupts!(struct Irqs {\n    I2C1_EV => i2c::EventInterruptHandler<peripherals::I2C1>;\n    I2C1_ER => i2c::ErrorInterruptHandler<peripherals::I2C1>;\n    DMA1_STREAM0 => dma::InterruptHandler<peripherals::DMA1_CH0>;\n    DMA1_STREAM6 => dma::InterruptHandler<peripherals::DMA1_CH6>;\n});\n\n#[embassy_executor::main]\nasync fn main(spawner: Spawner) {\n    let p = embassy_stm32::init(Default::default());\n\n    // Configure I2C\n    let mut i2c_config = i2c::Config::default();\n    i2c_config.sda_pullup = false;\n    i2c_config.scl_pullup = false;\n    i2c_config.frequency = Hertz(100_000); // 100kHz I2C speed\n\n    // Initialize I2C as master first\n    let i2c_master = I2c::new(\n        p.I2C1, p.PB8,      // SCL\n        p.PB9,      // SDA\n        p.DMA1_CH6, // TX DMA\n        p.DMA1_CH0, // RX DMA\n        Irqs, i2c_config,\n    );\n\n    // Convert to MultiMaster mode\n    let slave_config = SlaveAddrConfig::basic(I2C_SLAVE_ADDR);\n    let i2c_slave = i2c_master.into_slave_multimaster(slave_config);\n\n    spawner.spawn(i2c_slave_task(i2c_slave).unwrap());\n}\n\n#[embassy_executor::task]\npub async fn i2c_slave_task(mut i2c_slave: I2c<'static, embassy_stm32::mode::Async, i2c::mode::MultiMaster>) {\n    info!(\"Async I2C slave ready at address 0x{:02X}\", I2C_SLAVE_ADDR);\n\n    loop {\n        match i2c_slave.listen().await {\n            Ok(SlaveCommand {\n                kind: SlaveCommandKind::Write,\n                address,\n            }) => {\n                let addr_val = match address {\n                    Address::SevenBit(addr) => addr,\n                    Address::TenBit(addr) => (addr & 0xFF) as u8,\n                };\n\n                info!(\"I2C: Received write command - Address 0x{:02X}\", addr_val);\n\n                let mut data_buffer = I2C_BUFFER.lock().await;\n\n                match i2c_slave.respond_to_write(&mut *data_buffer).await {\n                    Ok(bytes_received) => {\n                        info!(\n                            \"I2C: Received {} bytes: {:02X}\",\n                            bytes_received,\n                            &data_buffer[..bytes_received]\n                        );\n                    }\n                    Err(e) => {\n                        error!(\"I2C: Write error: {}\", format_i2c_error(&e));\n                    }\n                }\n            }\n\n            Ok(SlaveCommand {\n                kind: SlaveCommandKind::Read,\n                address,\n            }) => {\n                let addr_val = match address {\n                    Address::SevenBit(addr) => addr,\n                    Address::TenBit(addr) => (addr & 0xFF) as u8, // Show low byte for 10-bit\n                };\n\n                info!(\"I2C: Received read command - Address 0x{:02X}\", addr_val);\n\n                let data_buffer = I2C_BUFFER.lock().await;\n\n                match i2c_slave.respond_to_read(&data_buffer[..BUFFER_SIZE]).await {\n                    Ok(bytes_sent) => {\n                        info!(\"I2C: Sent {} bytes in response to read command\", bytes_sent);\n                    }\n                    Err(e) => {\n                        error!(\"I2C: Read error: {}\", format_i2c_error(&e));\n                    }\n                }\n            }\n\n            Err(e) => {\n                error!(\"I2C: Listen error: {}\", format_i2c_error(&e));\n                Timer::after(Duration::from_millis(100)).await;\n            }\n        }\n    }\n}\n\nfn format_i2c_error(e: &embassy_stm32::i2c::Error) -> &'static str {\n    match e {\n        embassy_stm32::i2c::Error::Bus => \"Bus\",\n        embassy_stm32::i2c::Error::Arbitration => \"Arbitration\",\n        embassy_stm32::i2c::Error::Nack => \"Nack\",\n        embassy_stm32::i2c::Error::Timeout => \"Timeout\",\n        embassy_stm32::i2c::Error::Crc => \"Crc\",\n        embassy_stm32::i2c::Error::Overrun => \"Overrun\",\n        embassy_stm32::i2c::Error::ZeroLengthTransfer => \"ZeroLengthTransfer\",\n    }\n}\n"
  },
  {
    "path": "examples/stm32f4/src/bin/i2c_slave_blocking.rs",
    "content": "//! Complete I2C slave example using blocking operations\n//!\n//! This example shows how to set up an STM32F4 as an I2C slave device\n//! that can handle both read and write transactions from master devices.\n\n#![no_std]\n#![no_main]\n\nuse defmt::{error, info};\nuse embassy_executor::Spawner;\nuse embassy_stm32::i2c::{self, Address, I2c, SlaveAddrConfig, SlaveCommand, SlaveCommandKind};\nuse embassy_stm32::time::Hertz;\nuse embassy_stm32::{bind_interrupts, peripherals};\nuse embassy_sync::blocking_mutex::raw::ThreadModeRawMutex;\nuse embassy_sync::mutex::Mutex;\nuse embassy_time::{Duration, Timer};\nuse {defmt_rtt as _, panic_probe as _};\n\npub const I2C_SLAVE_ADDR: u8 = 0x42;\npub const BUFFER_SIZE: usize = 8;\nstatic I2C_BUFFER: Mutex<ThreadModeRawMutex, [u8; BUFFER_SIZE]> = Mutex::new([0; BUFFER_SIZE]);\n\nbind_interrupts!(struct Irqs {\n    I2C1_EV => i2c::EventInterruptHandler<peripherals::I2C1>;\n    I2C1_ER => i2c::ErrorInterruptHandler<peripherals::I2C1>;\n});\n\n#[embassy_executor::main]\nasync fn main(spawner: Spawner) {\n    let p = embassy_stm32::init(Default::default());\n\n    // Configure I2C\n    let mut i2c_config = i2c::Config::default();\n    i2c_config.sda_pullup = false;\n    i2c_config.scl_pullup = false;\n    i2c_config.frequency = Hertz(100_000);\n    i2c_config.timeout = embassy_time::Duration::from_millis(30000);\n\n    // Initialize I2C as master first\n    let i2c_master = I2c::new_blocking(\n        p.I2C1, p.PB8, // SCL\n        p.PB9, // SDA\n        i2c_config,\n    );\n\n    // Convert to slave+master mode\n    let slave_config = SlaveAddrConfig::basic(I2C_SLAVE_ADDR);\n    let i2c_slave = i2c_master.into_slave_multimaster(slave_config);\n\n    spawner.spawn(i2c_slave_task(i2c_slave).unwrap());\n}\n\n#[embassy_executor::task]\npub async fn i2c_slave_task(mut i2c_slave: I2c<'static, embassy_stm32::mode::Blocking, i2c::mode::MultiMaster>) {\n    info!(\"Blocking I2C slave ready at address 0x{:02X}\", I2C_SLAVE_ADDR);\n\n    loop {\n        match i2c_slave.blocking_listen() {\n            Ok(SlaveCommand {\n                kind: SlaveCommandKind::Write,\n                address,\n            }) => {\n                let addr_val = match address {\n                    Address::SevenBit(addr) => addr,\n                    Address::TenBit(addr) => (addr & 0xFF) as u8,\n                };\n\n                info!(\"I2C: Received write command - Address 0x{:02X}\", addr_val);\n                let mut data_buffer = I2C_BUFFER.lock().await;\n\n                match i2c_slave.blocking_respond_to_write(&mut *data_buffer) {\n                    Ok(bytes_received) => {\n                        info!(\n                            \"I2C: Received {} bytes: {:02X}\",\n                            bytes_received,\n                            &data_buffer[..bytes_received]\n                        );\n                    }\n                    Err(e) => {\n                        error!(\"I2C: Write error: {}\", format_i2c_error(&e));\n                    }\n                }\n            }\n\n            Ok(SlaveCommand {\n                kind: SlaveCommandKind::Read,\n                address,\n            }) => {\n                let addr_val = match address {\n                    Address::SevenBit(addr) => addr,\n                    Address::TenBit(addr) => (addr & 0xFF) as u8, // Show low byte for 10-bit\n                };\n\n                info!(\"I2C: Received read command - Address 0x{:02X}\", addr_val);\n                let data_buffer = I2C_BUFFER.lock().await;\n\n                match i2c_slave.blocking_respond_to_read(&data_buffer[..BUFFER_SIZE]) {\n                    Ok(bytes_sent) => {\n                        info!(\"I2C: Sent {} bytes in response to read command\", bytes_sent);\n                    }\n                    Err(e) => {\n                        error!(\"I2C: Read error: {}\", format_i2c_error(&e));\n                    }\n                }\n            }\n\n            Err(e) => {\n                error!(\"I2C: Listen error: {}\", format_i2c_error(&e));\n                Timer::after(Duration::from_millis(100)).await;\n            }\n        }\n    }\n}\n\nfn format_i2c_error(e: &embassy_stm32::i2c::Error) -> &'static str {\n    match e {\n        embassy_stm32::i2c::Error::Bus => \"Bus\",\n        embassy_stm32::i2c::Error::Arbitration => \"Arbitration\",\n        embassy_stm32::i2c::Error::Nack => \"Nack\",\n        embassy_stm32::i2c::Error::Timeout => \"Timeout\",\n        embassy_stm32::i2c::Error::Crc => \"Crc\",\n        embassy_stm32::i2c::Error::Overrun => \"Overrun\",\n        embassy_stm32::i2c::Error::ZeroLengthTransfer => \"ZeroLengthTransfer\",\n    }\n}\n"
  },
  {
    "path": "examples/stm32f4/src/bin/i2s_dma.rs",
    "content": "// This example is written for an STM32F411 chip communicating with an external\n// PCM5102a DAC. Remap pins, change clock speeds, etc. as necessary for your own\n// hardware.\n//\n// NOTE: This example outputs potentially loud audio. Please run responsibly.\n\n#![no_std]\n#![no_main]\n\nuse embassy_executor::Spawner;\nuse embassy_stm32::i2s::{Config, Format, I2S};\nuse embassy_stm32::time::Hertz;\nuse embassy_stm32::{bind_interrupts, dma, peripherals};\nuse {defmt_rtt as _, panic_probe as _};\n\nbind_interrupts!(struct Irqs {\n    DMA1_STREAM7 => dma::InterruptHandler<peripherals::DMA1_CH7>;\n});\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let config = {\n        use embassy_stm32::rcc::*;\n\n        let mut config = embassy_stm32::Config::default();\n        config.rcc.hse = Some(Hse {\n            freq: Hertz::mhz(25),\n            mode: HseMode::Oscillator,\n        });\n        config.rcc.pll_src = PllSource::HSE;\n        config.rcc.pll = Some(Pll {\n            prediv: PllPreDiv::DIV25,\n            mul: PllMul::MUL192,\n            divp: Some(PllPDiv::DIV2),\n            divq: Some(PllQDiv::DIV4),\n            divr: None,\n        });\n        config.rcc.sys = Sysclk::PLL1_P;\n\n        config.rcc.ahb_pre = AHBPrescaler::DIV1;\n        config.rcc.apb1_pre = APBPrescaler::DIV2;\n        config.rcc.apb2_pre = APBPrescaler::DIV1;\n\n        // reference your chip's manual for proper clock settings; this config\n        // is recommended for a 32 bit frame at 48 kHz sample rate\n        config.rcc.plli2s = Some(Pll {\n            prediv: PllPreDiv::DIV25,\n            mul: PllMul::MUL384,\n            divp: None,\n            divq: None,\n            divr: Some(PllRDiv::DIV5),\n        });\n        config.enable_debug_during_sleep = true;\n\n        config\n    };\n\n    let p = embassy_stm32::init(config);\n\n    // stereo wavetable generation\n    let mut wavetable = [0u16; 1200];\n    for (i, frame) in wavetable.chunks_mut(2).enumerate() {\n        frame[0] = ((((i / 150) % 2) * 2048) as i16 - 1024) as u16; // 160 Hz square wave in left channel\n        frame[1] = ((((i / 100) % 2) * 2048) as i16 - 1024) as u16; // 240 Hz square wave in right channel\n    }\n\n    // i2s configuration\n    let mut dma_buffer = [0u16; 2400];\n\n    let mut i2s_config = Config::default();\n    i2s_config.format = Format::Data16Channel32;\n    i2s_config.master_clock = false;\n    let mut i2s = I2S::new_txonly_nomck(\n        p.SPI3,\n        p.PB5,  // sd\n        p.PA15, // ws\n        p.PB3,  // ck\n        p.DMA1_CH7,\n        &mut dma_buffer,\n        Irqs,\n        i2s_config,\n    );\n    i2s.start();\n\n    loop {\n        i2s.write(&wavetable).await.ok();\n    }\n}\n"
  },
  {
    "path": "examples/stm32f4/src/bin/input_capture.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_stm32::gpio::{Level, Output, Pull, Speed};\nuse embassy_stm32::time::khz;\nuse embassy_stm32::timer::input_capture::{CapturePin, InputCapture};\nuse embassy_stm32::timer::{self, Channel};\nuse embassy_stm32::{Peri, bind_interrupts, peripherals};\nuse embassy_time::Timer;\nuse {defmt_rtt as _, panic_probe as _};\n\n/// Connect PB2 and PB10 with a 1k Ohm resistor\n\n#[embassy_executor::task]\nasync fn blinky(led: Peri<'static, peripherals::PB2>) {\n    let mut led = Output::new(led, Level::High, Speed::Low);\n\n    loop {\n        info!(\"high\");\n        led.set_high();\n        Timer::after_millis(300).await;\n\n        info!(\"low\");\n        led.set_low();\n        Timer::after_millis(300).await;\n    }\n}\n\nbind_interrupts!(struct Irqs {\n    TIM2 => timer::CaptureCompareInterruptHandler<peripherals::TIM2>;\n});\n\n#[embassy_executor::main]\nasync fn main(spawner: Spawner) {\n    let p = embassy_stm32::init(Default::default());\n    info!(\"Hello World!\");\n\n    spawner.spawn(unwrap!(blinky(p.PB2)));\n\n    let ch3 = CapturePin::new(p.PB10, Pull::None);\n    let mut ic = InputCapture::new(p.TIM2, None, None, Some(ch3), None, Irqs, khz(1000), Default::default());\n\n    loop {\n        info!(\"wait for risign edge\");\n        ic.wait_for_rising_edge(Channel::Ch3).await;\n\n        let capture_value = ic.get_capture_value(Channel::Ch3);\n        info!(\"new capture! {}\", capture_value);\n    }\n}\n"
  },
  {
    "path": "examples/stm32f4/src/bin/mco.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_stm32::gpio::{Level, Output, Speed};\nuse embassy_stm32::rcc::{Mco, Mco1Source, Mco2Source, McoConfig, McoPrescaler};\nuse embassy_time::Timer;\nuse {defmt_rtt as _, panic_probe as _};\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let p = embassy_stm32::init(Default::default());\n    info!(\"Hello World!\");\n\n    let config_mco1 = {\n        let mut config = McoConfig::default();\n        config.prescaler = McoPrescaler::DIV1;\n        config\n    };\n\n    let config_mco2 = {\n        let mut config = McoConfig::default();\n        config.prescaler = McoPrescaler::DIV4;\n        config\n    };\n\n    let _mco1 = Mco::new(p.MCO1, p.PA8, Mco1Source::HSI, config_mco1);\n    let _mco2 = Mco::new(p.MCO2, p.PC9, Mco2Source::PLL, config_mco2);\n    let mut led = Output::new(p.PB7, Level::High, Speed::Low);\n\n    loop {\n        info!(\"high\");\n        led.set_high();\n        Timer::after_millis(300).await;\n\n        info!(\"low\");\n        led.set_low();\n        Timer::after_millis(300).await;\n    }\n}\n"
  },
  {
    "path": "examples/stm32f4/src/bin/multiprio.rs",
    "content": "//! This example showcases how to create multiple Executor instances to run tasks at\n//! different priority levels.\n//!\n//! Low priority executor runs in thread mode (not interrupt), and uses `sev` for signaling\n//! there's work in the queue, and `wfe` for waiting for work.\n//!\n//! Medium and high priority executors run in two interrupts with different priorities.\n//! Signaling work is done by pending the interrupt. No \"waiting\" needs to be done explicitly, since\n//! when there's work the interrupt will trigger and run the executor.\n//!\n//! Sample output below. Note that high priority ticks can interrupt everything else, and\n//! medium priority computations can interrupt low priority computations, making them to appear\n//! to take significantly longer time.\n//!\n//! ```not_rust\n//!     [med] Starting long computation\n//!     [med] done in 992 ms\n//!         [high] tick!\n//! [low] Starting long computation\n//!     [med] Starting long computation\n//!         [high] tick!\n//!         [high] tick!\n//!     [med] done in 993 ms\n//!     [med] Starting long computation\n//!         [high] tick!\n//!         [high] tick!\n//!     [med] done in 993 ms\n//! [low] done in 3972 ms\n//!     [med] Starting long computation\n//!         [high] tick!\n//!         [high] tick!\n//!     [med] done in 993 ms\n//! ```\n//!\n//! For comparison, try changing the code so all 3 tasks get spawned on the low priority executor.\n//! You will get an output like the following. Note that no computation is ever interrupted.\n//!\n//! ```not_rust\n//!         [high] tick!\n//!     [med] Starting long computation\n//!     [med] done in 496 ms\n//! [low] Starting long computation\n//! [low] done in 992 ms\n//!     [med] Starting long computation\n//!     [med] done in 496 ms\n//!         [high] tick!\n//! [low] Starting long computation\n//! [low] done in 992 ms\n//!         [high] tick!\n//!     [med] Starting long computation\n//!     [med] done in 496 ms\n//!         [high] tick!\n//! ```\n//!\n\n#![no_std]\n#![no_main]\n\nuse cortex_m_rt::entry;\nuse defmt::*;\nuse embassy_executor::{Executor, InterruptExecutor};\nuse embassy_stm32::interrupt;\nuse embassy_stm32::interrupt::{InterruptExt, Priority};\nuse embassy_time::{Instant, Timer};\nuse static_cell::StaticCell;\nuse {defmt_rtt as _, panic_probe as _};\n\n#[embassy_executor::task]\nasync fn run_high() {\n    loop {\n        info!(\"        [high] tick!\");\n        Timer::after_ticks(27374).await;\n    }\n}\n\n#[embassy_executor::task]\nasync fn run_med() {\n    loop {\n        let start = Instant::now();\n        info!(\"    [med] Starting long computation\");\n\n        // Spin-wait to simulate a long CPU computation\n        embassy_time::block_for(embassy_time::Duration::from_secs(1)); // ~1 second\n\n        let end = Instant::now();\n        let ms = end.duration_since(start).as_ticks() / 33;\n        info!(\"    [med] done in {} ms\", ms);\n\n        Timer::after_ticks(23421).await;\n    }\n}\n\n#[embassy_executor::task]\nasync fn run_low() {\n    loop {\n        let start = Instant::now();\n        info!(\"[low] Starting long computation\");\n\n        // Spin-wait to simulate a long CPU computation\n        embassy_time::block_for(embassy_time::Duration::from_secs(2)); // ~2 seconds\n\n        let end = Instant::now();\n        let ms = end.duration_since(start).as_ticks() / 33;\n        info!(\"[low] done in {} ms\", ms);\n\n        Timer::after_ticks(32983).await;\n    }\n}\n\nstatic EXECUTOR_HIGH: InterruptExecutor = InterruptExecutor::new();\nstatic EXECUTOR_MED: InterruptExecutor = InterruptExecutor::new();\nstatic EXECUTOR_LOW: StaticCell<Executor> = StaticCell::new();\n\n#[interrupt]\nunsafe fn UART4() {\n    unsafe { EXECUTOR_HIGH.on_interrupt() }\n}\n\n#[interrupt]\nunsafe fn UART5() {\n    unsafe { EXECUTOR_MED.on_interrupt() }\n}\n\n#[entry]\nfn main() -> ! {\n    info!(\"Hello World!\");\n\n    let _p = embassy_stm32::init(Default::default());\n\n    // STM32s don’t have any interrupts exclusively for software use, but they can all be triggered by software as well as\n    // by the peripheral, so we can just use any free interrupt vectors which aren’t used by the rest of your application.\n    // In this case we’re using UART4 and UART5, but there’s nothing special about them. Any otherwise unused interrupt\n    // vector would work exactly the same.\n\n    // High-priority executor: UART4, priority level 6\n    interrupt::UART4.set_priority(Priority::P6);\n    let spawner = EXECUTOR_HIGH.start(interrupt::UART4);\n    spawner.spawn(unwrap!(run_high()));\n\n    // Medium-priority executor: UART5, priority level 7\n    interrupt::UART5.set_priority(Priority::P7);\n    let spawner = EXECUTOR_MED.start(interrupt::UART5);\n    spawner.spawn(unwrap!(run_med()));\n\n    // Low priority executor: runs in thread mode, using WFE/SEV\n    let executor = EXECUTOR_LOW.init(Executor::new());\n    executor.run(|spawner| {\n        spawner.spawn(unwrap!(run_low()));\n    });\n}\n"
  },
  {
    "path": "examples/stm32f4/src/bin/pwm.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_stm32::gpio::OutputType;\nuse embassy_stm32::time::khz;\nuse embassy_stm32::timer::simple_pwm::{PwmPin, SimplePwm};\nuse embassy_time::Timer;\nuse {defmt_rtt as _, panic_probe as _};\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let p = embassy_stm32::init(Default::default());\n    info!(\"Hello World!\");\n\n    let ch1_pin = PwmPin::new(p.PE9, OutputType::PushPull);\n    let mut pwm = SimplePwm::new(p.TIM1, Some(ch1_pin), None, None, None, khz(10), Default::default());\n    let mut ch1 = pwm.ch1();\n    ch1.enable();\n\n    info!(\"PWM initialized\");\n    info!(\"PWM max duty {}\", ch1.max_duty_cycle());\n\n    loop {\n        ch1.set_duty_cycle_fully_off();\n        Timer::after_millis(300).await;\n        ch1.set_duty_cycle_fraction(1, 4);\n        Timer::after_millis(300).await;\n        ch1.set_duty_cycle_fraction(1, 2);\n        Timer::after_millis(300).await;\n        ch1.set_duty_cycle(ch1.max_duty_cycle() - 1);\n        Timer::after_millis(300).await;\n    }\n}\n"
  },
  {
    "path": "examples/stm32f4/src/bin/pwm_complementary.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_stm32::gpio::OutputType;\nuse embassy_stm32::time::khz;\nuse embassy_stm32::timer::Channel;\nuse embassy_stm32::timer::complementary_pwm::{ComplementaryPwm, ComplementaryPwmPin};\nuse embassy_stm32::timer::simple_pwm::PwmPin;\nuse embassy_time::Timer;\nuse {defmt_rtt as _, panic_probe as _};\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let p = embassy_stm32::init(Default::default());\n    info!(\"Hello World!\");\n\n    let ch1 = PwmPin::new(p.PE9, OutputType::PushPull);\n    let ch1n = ComplementaryPwmPin::new(p.PA7, OutputType::PushPull);\n    let mut pwm = ComplementaryPwm::new(\n        p.TIM1,\n        Some(ch1),\n        Some(ch1n),\n        None,\n        None,\n        None,\n        None,\n        None,\n        None,\n        khz(10),\n        Default::default(),\n    );\n\n    let max = pwm.get_max_duty();\n    pwm.set_dead_time((max / 1024) as u16);\n\n    pwm.enable(Channel::Ch1);\n\n    info!(\"PWM initialized\");\n    info!(\"PWM max duty {}\", max);\n\n    loop {\n        pwm.set_duty(Channel::Ch1, 0);\n        Timer::after_millis(300).await;\n        pwm.set_duty(Channel::Ch1, max / 4);\n        Timer::after_millis(300).await;\n        pwm.set_duty(Channel::Ch1, max / 2);\n        Timer::after_millis(300).await;\n        pwm.set_duty(Channel::Ch1, max - 1);\n        Timer::after_millis(300).await;\n    }\n}\n"
  },
  {
    "path": "examples/stm32f4/src/bin/pwm_input.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_stm32::gpio::{Level, Output, Pull, Speed};\nuse embassy_stm32::time::khz;\nuse embassy_stm32::timer::pwm_input::PwmInput;\nuse embassy_stm32::{Peri, bind_interrupts, peripherals, timer};\nuse embassy_time::Timer;\nuse {defmt_rtt as _, panic_probe as _};\n\n/// Connect PB2 and PA6 with a 1k Ohm resistor\n\n#[embassy_executor::task]\nasync fn blinky(led: Peri<'static, peripherals::PB2>) {\n    let mut led = Output::new(led, Level::High, Speed::Low);\n\n    loop {\n        info!(\"high\");\n        led.set_high();\n        Timer::after_millis(300).await;\n\n        info!(\"low\");\n        led.set_low();\n        Timer::after_millis(300).await;\n    }\n}\n\nbind_interrupts!(struct Irqs {\n    TIM3 => timer::CaptureCompareInterruptHandler<peripherals::TIM3>;\n});\n\n#[embassy_executor::main]\nasync fn main(spawner: Spawner) {\n    let p = embassy_stm32::init(Default::default());\n    info!(\"Hello World!\");\n\n    spawner.spawn(unwrap!(blinky(p.PB2)));\n\n    let mut pwm_input = PwmInput::new_ch1(p.TIM3, p.PA6, Irqs, Pull::None, khz(10));\n    pwm_input.enable();\n\n    loop {\n        Timer::after_millis(500).await;\n        let period = pwm_input.get_period_ticks();\n        let width = pwm_input.get_width_ticks();\n        let duty_cycle = pwm_input.get_duty_cycle();\n        info!(\n            \"period ticks: {} width ticks: {} duty cycle: {}\",\n            period, width, duty_cycle\n        );\n    }\n}\n"
  },
  {
    "path": "examples/stm32f4/src/bin/rtc.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse chrono::{NaiveDate, NaiveDateTime};\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_stm32::Config;\nuse embassy_stm32::rtc::{Rtc, RtcConfig};\nuse embassy_time::Timer;\nuse {defmt_rtt as _, panic_probe as _};\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let config = Config::default();\n    let p = embassy_stm32::init(config);\n\n    info!(\"Hello World!\");\n\n    let now = NaiveDate::from_ymd_opt(2020, 5, 15)\n        .unwrap()\n        .and_hms_opt(10, 30, 15)\n        .unwrap();\n\n    let (mut rtc, time_provider) = Rtc::new(p.RTC, RtcConfig::default());\n\n    rtc.set_datetime(now.into()).expect(\"datetime not set\");\n\n    loop {\n        let now: NaiveDateTime = time_provider.now().unwrap().into();\n\n        info!(\"{}\", now.and_utc().timestamp());\n\n        Timer::after_millis(1000).await;\n    }\n}\n"
  },
  {
    "path": "examples/stm32f4/src/bin/sdmmc.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_stm32::sdmmc::Sdmmc;\nuse embassy_stm32::sdmmc::sd::{Card, CmdBlock, DataBlock, StorageDevice};\nuse embassy_stm32::time::{Hertz, mhz};\nuse embassy_stm32::{Config, bind_interrupts, dma, peripherals, sdmmc};\nuse embassy_sync::blocking_mutex::raw::NoopRawMutex;\nuse embassy_sync::channel::Channel;\nuse embassy_time::{Duration, Timer};\nuse {defmt_rtt as _, panic_probe as _};\n\n/// This is a safeguard to not overwrite any data on the SD card.\n/// If you don't care about SD card contents, set this to `true` to test writes.\nconst ALLOW_WRITES: bool = false;\n\nbind_interrupts!(struct Irqs {\n    SDIO => sdmmc::InterruptHandler<peripherals::SDIO>;\n    DMA2_STREAM3 => dma::InterruptHandler<peripherals::DMA2_CH3>;\n});\n\npub enum StorageRequest {\n    WriteRequest(u32, &'static DataBlock),\n    ReadRequest,\n}\n\npub async fn run_storage<'a>(mut sdmmc: Sdmmc<'a>, channel: &'static Channel<NoopRawMutex, StorageRequest, 3>) {\n    loop {\n        let storage = loop {\n            if let Ok(storage) = StorageDevice::new_sd_card(&mut sdmmc, &mut CmdBlock::new(), mhz(24)).await {\n                break storage;\n            }\n\n            // Wait 1/2 second to avoid saturating the core\n            Timer::after(Duration::from_millis(500)).await;\n        };\n\n        let _ = run_storage_inner(storage, channel).await;\n    }\n}\n\npub async fn run_storage_inner<'a, 'b>(\n    mut storage: StorageDevice<'a, 'b, Card>,\n    channel: &'static Channel<NoopRawMutex, StorageRequest, 3>,\n) -> Result<(), ()> {\n    // Or, instead of receiving from a channel, you can read/write files here\n\n    loop {\n        match channel.receive().await {\n            StorageRequest::WriteRequest(block_idx, buffer) => {\n                storage.write_block(block_idx, buffer).await.map_err(|_| ())?;\n            }\n            StorageRequest::ReadRequest => {}\n        }\n    }\n}\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let mut config = Config::default();\n    {\n        use embassy_stm32::rcc::*;\n        config.rcc.hse = Some(Hse {\n            freq: Hertz(8_000_000),\n            mode: HseMode::Bypass,\n        });\n        config.rcc.pll_src = PllSource::HSE;\n        config.rcc.pll = Some(Pll {\n            prediv: PllPreDiv::DIV4,\n            mul: PllMul::MUL168,\n            divp: Some(PllPDiv::DIV2), // 8mhz / 4 * 168 / 2 = 168Mhz.\n            divq: Some(PllQDiv::DIV7), // 8mhz / 4 * 168 / 7 = 48Mhz.\n            divr: None,\n        });\n        config.rcc.ahb_pre = AHBPrescaler::DIV1;\n        config.rcc.apb1_pre = APBPrescaler::DIV4;\n        config.rcc.apb2_pre = APBPrescaler::DIV2;\n        config.rcc.sys = Sysclk::PLL1_P;\n    }\n    let p = embassy_stm32::init(config);\n    info!(\"Hello World!\");\n\n    let mut sdmmc = Sdmmc::new_4bit(\n        p.SDIO,\n        p.DMA2_CH3,\n        Irqs,\n        p.PC12,\n        p.PD2,\n        p.PC8,\n        p.PC9,\n        p.PC10,\n        p.PC11,\n        Default::default(),\n    );\n\n    let mut cmd_block = CmdBlock::new();\n\n    let mut storage = loop {\n        if let Ok(storage) = StorageDevice::new_sd_card(&mut sdmmc, &mut cmd_block, mhz(24)).await {\n            break storage;\n        }\n    };\n\n    let card = storage.card();\n\n    info!(\"Card: {:#?}\", Debug2Format(&card));\n\n    // Arbitrary block index\n    let block_idx = 16;\n\n    // SDMMC uses `DataBlock` instead of `&[u8]` to ensure 4 byte alignment required by the hardware.\n    let mut block = DataBlock::new();\n\n    storage.read_block(block_idx, &mut block).await.unwrap();\n    info!(\"Read: {=[u8]:X}...{=[u8]:X}\", block[..8], block[512 - 8..]);\n\n    if !ALLOW_WRITES {\n        info!(\"Writing is disabled.\");\n        loop {}\n    }\n\n    info!(\"Filling block with 0x55\");\n    block.fill(0x55);\n    storage.write_block(block_idx, &block).await.unwrap();\n    info!(\"Write done\");\n\n    storage.read_block(block_idx, &mut block).await.unwrap();\n    info!(\"Read: {=[u8]:X}...{=[u8]:X}\", block[..8], block[512 - 8..]);\n\n    info!(\"Filling block with 0xAA\");\n    block.fill(0xAA);\n    storage.write_block(block_idx, &block).await.unwrap();\n    info!(\"Write done\");\n\n    storage.read_block(block_idx, &mut block).await.unwrap();\n    info!(\"Read: {=[u8]:X}...{=[u8]:X}\", block[..8], block[512 - 8..]);\n}\n"
  },
  {
    "path": "examples/stm32f4/src/bin/spi.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse cortex_m_rt::entry;\nuse defmt::*;\nuse embassy_stm32::gpio::{Level, Output, Speed};\nuse embassy_stm32::spi::{Config, Spi};\nuse embassy_stm32::time::Hertz;\nuse {defmt_rtt as _, panic_probe as _};\n\n#[entry]\nfn main() -> ! {\n    info!(\"Hello World, dude!\");\n\n    let p = embassy_stm32::init(Default::default());\n\n    let mut spi_config = Config::default();\n    spi_config.frequency = Hertz(1_000_000);\n\n    let mut spi = Spi::new_blocking(p.SPI3, p.PC10, p.PC12, p.PC11, spi_config);\n\n    let mut cs = Output::new(p.PE0, Level::High, Speed::VeryHigh);\n\n    loop {\n        let mut buf = [0x0Au8; 4];\n        cs.set_low();\n        unwrap!(spi.blocking_transfer_in_place(&mut buf));\n        cs.set_high();\n        info!(\"xfer {=[u8]:x}\", buf);\n    }\n}\n"
  },
  {
    "path": "examples/stm32f4/src/bin/spi_dma.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse core::fmt::Write;\nuse core::str::from_utf8;\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_stm32::spi::{Config, Spi};\nuse embassy_stm32::time::Hertz;\nuse embassy_stm32::{bind_interrupts, dma, peripherals};\nuse heapless::String;\nuse {defmt_rtt as _, panic_probe as _};\n\nbind_interrupts!(struct Irqs {\n    DMA2_STREAM2 => dma::InterruptHandler<peripherals::DMA2_CH2>;\n    DMA2_STREAM3 => dma::InterruptHandler<peripherals::DMA2_CH3>;\n});\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let p = embassy_stm32::init(Default::default());\n    info!(\"Hello World!\");\n\n    let mut spi_config = Config::default();\n    spi_config.frequency = Hertz(1_000_000);\n\n    let mut spi = Spi::new(p.SPI1, p.PB3, p.PB5, p.PB4, p.DMA2_CH3, p.DMA2_CH2, Irqs, spi_config);\n\n    for n in 0u32.. {\n        let mut write: String<128> = String::new();\n        let mut read = [0; 128];\n        core::write!(&mut write, \"Hello DMA World {}!\\r\\n\", n).unwrap();\n        spi.transfer(&mut read[0..write.len()], write.as_bytes()).await.ok();\n        info!(\"read via spi+dma: {}\", from_utf8(&read).unwrap());\n    }\n}\n"
  },
  {
    "path": "examples/stm32f4/src/bin/usart.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse cortex_m_rt::entry;\nuse defmt::*;\nuse embassy_stm32::usart::{Config, Uart};\nuse embassy_stm32::{bind_interrupts, peripherals, usart};\nuse {defmt_rtt as _, panic_probe as _};\n\nbind_interrupts!(struct Irqs {\n    USART3 => usart::InterruptHandler<peripherals::USART3>;\n});\n\n#[entry]\nfn main() -> ! {\n    info!(\"Hello World!\");\n\n    let p = embassy_stm32::init(Default::default());\n\n    let config = Config::default();\n    let mut usart = Uart::new_blocking(p.USART3, p.PD9, p.PD8, config).unwrap();\n\n    unwrap!(usart.blocking_write(b\"Hello Embassy World!\\r\\n\"));\n    info!(\"wrote Hello, starting echo\");\n\n    let mut buf = [0u8; 1];\n    loop {\n        unwrap!(usart.blocking_read(&mut buf));\n        unwrap!(usart.blocking_write(&buf));\n    }\n}\n"
  },
  {
    "path": "examples/stm32f4/src/bin/usart_buffered.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_stm32::usart::{BufferedUart, Config};\nuse embassy_stm32::{bind_interrupts, peripherals, usart};\nuse embedded_io_async::BufRead;\nuse {defmt_rtt as _, panic_probe as _};\n\nbind_interrupts!(struct Irqs {\n    USART3 => usart::BufferedInterruptHandler<peripherals::USART3>;\n});\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let p = embassy_stm32::init(Default::default());\n    info!(\"Hello World!\");\n\n    let config = Config::default();\n\n    let mut tx_buf = [0u8; 32];\n    let mut rx_buf = [0u8; 32];\n    let mut buf_usart = BufferedUart::new(p.USART3, p.PD9, p.PD8, &mut tx_buf, &mut rx_buf, Irqs, config).unwrap();\n\n    loop {\n        let buf = buf_usart.fill_buf().await.unwrap();\n        info!(\"Received: {}\", buf);\n\n        // Read bytes have to be explicitly consumed, otherwise fill_buf() will return them again\n        let n = buf.len();\n        buf_usart.consume(n);\n    }\n}\n"
  },
  {
    "path": "examples/stm32f4/src/bin/usart_dma.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse core::fmt::Write;\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_stm32::usart::{Config, Uart};\nuse embassy_stm32::{bind_interrupts, dma, peripherals, usart};\nuse heapless::String;\nuse {defmt_rtt as _, panic_probe as _};\n\nbind_interrupts!(struct Irqs {\n    USART3 => usart::InterruptHandler<peripherals::USART3>;\n    DMA1_STREAM1 => dma::InterruptHandler<peripherals::DMA1_CH1>;\n    DMA1_STREAM3 => dma::InterruptHandler<peripherals::DMA1_CH3>;\n});\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let p = embassy_stm32::init(Default::default());\n    info!(\"Hello World!\");\n\n    let config = Config::default();\n    let mut usart = Uart::new(p.USART3, p.PD9, p.PD8, p.DMA1_CH3, p.DMA1_CH1, Irqs, config).unwrap();\n\n    for n in 0u32.. {\n        let mut s: String<128> = String::new();\n        core::write!(&mut s, \"Hello DMA World {}!\\r\\n\", n).unwrap();\n\n        unwrap!(usart.write(s.as_bytes()).await);\n        info!(\"wrote DMA\");\n    }\n}\n"
  },
  {
    "path": "examples/stm32f4/src/bin/usb_ethernet.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_net::StackResources;\nuse embassy_net::tcp::TcpSocket;\nuse embassy_stm32::rng::{self, Rng};\nuse embassy_stm32::time::Hertz;\nuse embassy_stm32::usb::Driver;\nuse embassy_stm32::{Config, bind_interrupts, peripherals, usb};\nuse embassy_usb::class::cdc_ncm::embassy_net::{Device, Runner, State as NetState};\nuse embassy_usb::class::cdc_ncm::{CdcNcmClass, State};\nuse embassy_usb::{Builder, UsbDevice};\nuse embedded_io_async::Write;\nuse static_cell::StaticCell;\nuse {defmt_rtt as _, panic_probe as _};\n\ntype UsbDriver = Driver<'static, embassy_stm32::peripherals::USB_OTG_FS>;\n\nconst MTU: usize = 1514;\n\n#[embassy_executor::task]\nasync fn usb_task(mut device: UsbDevice<'static, UsbDriver>) -> ! {\n    device.run().await\n}\n\n#[embassy_executor::task]\nasync fn usb_ncm_task(class: Runner<'static, UsbDriver, MTU>) -> ! {\n    class.run().await\n}\n\n#[embassy_executor::task]\nasync fn net_task(mut runner: embassy_net::Runner<'static, Device<'static, MTU>>) -> ! {\n    runner.run().await\n}\n\nbind_interrupts!(struct Irqs {\n    OTG_FS => usb::InterruptHandler<peripherals::USB_OTG_FS>;\n    HASH_RNG => rng::InterruptHandler<peripherals::RNG>;\n});\n\n// If you are trying this and your USB device doesn't connect, the most\n// common issues are the RCC config and vbus_detection\n//\n// See https://embassy.dev/book/#_the_usb_examples_are_not_working_on_my_board_is_there_anything_else_i_need_to_configure\n// for more information.\n#[embassy_executor::main]\nasync fn main(spawner: Spawner) {\n    info!(\"Hello World!\");\n\n    let mut config = Config::default();\n    {\n        use embassy_stm32::rcc::*;\n        config.rcc.hse = Some(Hse {\n            freq: Hertz(8_000_000),\n            mode: HseMode::Bypass,\n        });\n        config.rcc.pll_src = PllSource::HSE;\n        config.rcc.pll = Some(Pll {\n            prediv: PllPreDiv::DIV4,\n            mul: PllMul::MUL168,\n            divp: Some(PllPDiv::DIV2), // 8mhz / 4 * 168 / 2 = 168Mhz.\n            divq: Some(PllQDiv::DIV7), // 8mhz / 4 * 168 / 7 = 48Mhz.\n            divr: None,\n        });\n        config.rcc.ahb_pre = AHBPrescaler::DIV1;\n        config.rcc.apb1_pre = APBPrescaler::DIV4;\n        config.rcc.apb2_pre = APBPrescaler::DIV2;\n        config.rcc.sys = Sysclk::PLL1_P;\n        config.rcc.mux.clk48sel = mux::Clk48sel::PLL1_Q;\n    }\n    let p = embassy_stm32::init(config);\n\n    // Create the driver, from the HAL.\n    static OUTPUT_BUFFER: StaticCell<[u8; 256]> = StaticCell::new();\n    let ep_out_buffer = &mut OUTPUT_BUFFER.init([0; 256])[..];\n    let mut config = embassy_stm32::usb::Config::default();\n\n    // Do not enable vbus_detection. This is a safe default that works in all boards.\n    // However, if your USB device is self-powered (can stay powered on if USB is unplugged), you need\n    // to enable vbus_detection to comply with the USB spec. If you enable it, the board\n    // has to support it or USB won't work at all. See docs on `vbus_detection` for details.\n    config.vbus_detection = false;\n\n    let driver = Driver::new_fs(p.USB_OTG_FS, Irqs, p.PA12, p.PA11, ep_out_buffer, config);\n\n    // Create embassy-usb Config\n    let mut config = embassy_usb::Config::new(0xc0de, 0xcafe);\n    config.manufacturer = Some(\"Embassy\");\n    config.product = Some(\"USB-Ethernet example\");\n    config.serial_number = Some(\"12345678\");\n    config.max_power = 100;\n    config.max_packet_size_0 = 64;\n\n    // Create embassy-usb DeviceBuilder using the driver and config.\n    static CONFIG_DESC: StaticCell<[u8; 256]> = StaticCell::new();\n    static BOS_DESC: StaticCell<[u8; 256]> = StaticCell::new();\n    static CONTROL_BUF: StaticCell<[u8; 128]> = StaticCell::new();\n    let mut builder = Builder::new(\n        driver,\n        config,\n        &mut CONFIG_DESC.init([0; 256])[..],\n        &mut BOS_DESC.init([0; 256])[..],\n        &mut [], // no msos descriptors\n        &mut CONTROL_BUF.init([0; 128])[..],\n    );\n\n    // Our MAC addr.\n    let our_mac_addr = [0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC];\n    // Host's MAC addr. This is the MAC the host \"thinks\" its USB-to-ethernet adapter has.\n    let host_mac_addr = [0x88, 0x88, 0x88, 0x88, 0x88, 0x88];\n\n    // Create classes on the builder.\n    static STATE: StaticCell<State> = StaticCell::new();\n    let class = CdcNcmClass::new(&mut builder, STATE.init(State::new()), host_mac_addr, 64);\n\n    // Build the builder.\n    let usb = builder.build();\n\n    spawner.spawn(unwrap!(usb_task(usb)));\n\n    static NET_STATE: StaticCell<NetState<MTU, 4, 4>> = StaticCell::new();\n    let (runner, device) = class.into_embassy_net_device::<MTU, 4, 4>(NET_STATE.init(NetState::new()), our_mac_addr);\n    spawner.spawn(unwrap!(usb_ncm_task(runner)));\n\n    let config = embassy_net::Config::dhcpv4(Default::default());\n    //let config = embassy_net::Config::ipv4_static(embassy_net::StaticConfigV4 {\n    //    address: Ipv4Cidr::new(Ipv4Address::new(10, 42, 0, 61), 24),\n    //    dns_servers: Vec::new(),\n    //    gateway: Some(Ipv4Address::new(10, 42, 0, 1)),\n    //});\n\n    // Generate random seed\n    let mut rng = Rng::new(p.RNG, Irqs);\n    let mut seed = [0; 8];\n    unwrap!(rng.async_fill_bytes(&mut seed).await);\n    let seed = u64::from_le_bytes(seed);\n\n    // Init network stack\n    static RESOURCES: StaticCell<StackResources<3>> = StaticCell::new();\n    let (stack, runner) = embassy_net::new(device, config, RESOURCES.init(StackResources::new()), seed);\n\n    spawner.spawn(unwrap!(net_task(runner)));\n\n    // And now we can use it!\n\n    let mut rx_buffer = [0; 4096];\n    let mut tx_buffer = [0; 4096];\n    let mut buf = [0; 4096];\n\n    loop {\n        let mut socket = TcpSocket::new(stack, &mut rx_buffer, &mut tx_buffer);\n        socket.set_timeout(Some(embassy_time::Duration::from_secs(10)));\n\n        info!(\"Listening on TCP:1234...\");\n        if let Err(e) = socket.accept(1234).await {\n            warn!(\"accept error: {:?}\", e);\n            continue;\n        }\n\n        info!(\"Received connection from {:?}\", socket.remote_endpoint());\n\n        loop {\n            let n = match socket.read(&mut buf).await {\n                Ok(0) => {\n                    warn!(\"read EOF\");\n                    break;\n                }\n                Ok(n) => n,\n                Err(e) => {\n                    warn!(\"read error: {:?}\", e);\n                    break;\n                }\n            };\n\n            info!(\"rxd {:02x}\", &buf[..n]);\n\n            match socket.write_all(&buf[..n]).await {\n                Ok(()) => {}\n                Err(e) => {\n                    warn!(\"write error: {:?}\", e);\n                    break;\n                }\n            };\n        }\n    }\n}\n"
  },
  {
    "path": "examples/stm32f4/src/bin/usb_hid_keyboard.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse core::sync::atomic::{AtomicBool, AtomicU8, Ordering};\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_futures::join::join;\nuse embassy_stm32::exti::{self, ExtiInput};\nuse embassy_stm32::gpio::Pull;\nuse embassy_stm32::time::Hertz;\nuse embassy_stm32::usb::Driver;\nuse embassy_stm32::{Config, bind_interrupts, interrupt, peripherals, usb};\nuse embassy_usb::class::hid::{\n    HidBootProtocol, HidProtocolMode, HidReaderWriter, HidSubclass, ReportId, RequestHandler, State,\n};\nuse embassy_usb::control::OutResponse;\nuse embassy_usb::{Builder, Handler};\nuse usbd_hid::descriptor::{KeyboardReport, SerializedDescriptor};\nuse {defmt_rtt as _, panic_probe as _};\n\nbind_interrupts!(struct Irqs {\n    OTG_FS => usb::InterruptHandler<peripherals::USB_OTG_FS>;\n    EXTI15_10 => exti::InterruptHandler<interrupt::typelevel::EXTI15_10>;\n});\n\nstatic HID_PROTOCOL_MODE: AtomicU8 = AtomicU8::new(HidProtocolMode::Boot as u8);\n\n// If you are trying this and your USB device doesn't connect, the most\n// common issues are the RCC config and vbus_detection\n//\n// See https://embassy.dev/book/#_the_usb_examples_are_not_working_on_my_board_is_there_anything_else_i_need_to_configure\n// for more information.\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let mut config = Config::default();\n    {\n        use embassy_stm32::rcc::*;\n        config.rcc.hse = Some(Hse {\n            freq: Hertz(8_000_000),\n            mode: HseMode::Bypass,\n        });\n        config.rcc.pll_src = PllSource::HSE;\n        config.rcc.pll = Some(Pll {\n            prediv: PllPreDiv::DIV4,\n            mul: PllMul::MUL168,\n            divp: Some(PllPDiv::DIV2), // 8mhz / 4 * 168 / 2 = 168Mhz.\n            divq: Some(PllQDiv::DIV7), // 8mhz / 4 * 168 / 7 = 48Mhz.\n            divr: None,\n        });\n        config.rcc.ahb_pre = AHBPrescaler::DIV1;\n        config.rcc.apb1_pre = APBPrescaler::DIV4;\n        config.rcc.apb2_pre = APBPrescaler::DIV2;\n        config.rcc.sys = Sysclk::PLL1_P;\n        config.rcc.mux.clk48sel = mux::Clk48sel::PLL1_Q;\n    }\n    let p = embassy_stm32::init(config);\n\n    // Create the driver, from the HAL.\n    let mut ep_out_buffer = [0u8; 256];\n    let mut config = embassy_stm32::usb::Config::default();\n\n    // Do not enable vbus_detection. This is a safe default that works in all boards.\n    // However, if your USB device is self-powered (can stay powered on if USB is unplugged), you need\n    // to enable vbus_detection to comply with the USB spec. If you enable it, the board\n    // has to support it or USB won't work at all. See docs on `vbus_detection` for details.\n    config.vbus_detection = false;\n\n    let driver = Driver::new_fs(p.USB_OTG_FS, Irqs, p.PA12, p.PA11, &mut ep_out_buffer, config);\n\n    // Create embassy-usb Config\n    let mut config = embassy_usb::Config::new(0xc0de, 0xcafe);\n    config.manufacturer = Some(\"Embassy\");\n    config.product = Some(\"HID keyboard example\");\n    config.serial_number = Some(\"12345678\");\n    config.max_power = 100;\n    config.max_packet_size_0 = 64;\n    config.composite_with_iads = false;\n    config.device_class = 0;\n    config.device_sub_class = 0;\n    config.device_protocol = 0;\n\n    // Create embassy-usb DeviceBuilder using the driver and config.\n    // It needs some buffers for building the descriptors.\n    let mut config_descriptor = [0; 256];\n    let mut bos_descriptor = [0; 256];\n    // You can also add a Microsoft OS descriptor.\n    let mut msos_descriptor = [0; 256];\n    let mut control_buf = [0; 64];\n\n    let mut request_handler = MyRequestHandler {};\n    let mut device_handler = MyDeviceHandler::new();\n\n    let mut state = State::new();\n\n    let mut builder = Builder::new(\n        driver,\n        config,\n        &mut config_descriptor,\n        &mut bos_descriptor,\n        &mut msos_descriptor,\n        &mut control_buf,\n    );\n\n    builder.handler(&mut device_handler);\n\n    // Create classes on the builder.\n    let config = embassy_usb::class::hid::Config {\n        report_descriptor: KeyboardReport::desc(),\n        request_handler: None,\n        poll_ms: 60,\n        max_packet_size: 8,\n        hid_subclass: HidSubclass::Boot,\n        hid_boot_protocol: HidBootProtocol::Keyboard,\n    };\n\n    let hid = HidReaderWriter::<_, 1, 8>::new(&mut builder, &mut state, config);\n\n    // Build the builder.\n    let mut usb = builder.build();\n\n    // Run the USB device.\n    let usb_fut = usb.run();\n\n    let (reader, mut writer) = hid.split();\n\n    let mut button = ExtiInput::new(p.PC13, p.EXTI13, Pull::Down, Irqs);\n\n    // Do stuff with the class!\n    let in_fut = async {\n        loop {\n            button.wait_for_rising_edge().await;\n            // signal_pin.wait_for_high().await;\n            info!(\"Button pressed!\");\n            if HID_PROTOCOL_MODE.load(Ordering::Relaxed) == HidProtocolMode::Boot as u8 {\n                match writer.write(&[0, 0, 4, 0, 0, 0, 0, 0]).await {\n                    Ok(()) => {}\n                    Err(e) => warn!(\"Failed to send boot report: {:?}\", e),\n                };\n            } else {\n                // Create a report with the A key pressed. (no shift modifier)\n                let report = KeyboardReport {\n                    keycodes: [4, 0, 0, 0, 0, 0],\n                    leds: 0,\n                    modifier: 0,\n                    reserved: 0,\n                };\n                // Send the report.\n                match writer.write_serialize(&report).await {\n                    Ok(()) => {}\n                    Err(e) => warn!(\"Failed to send report: {:?}\", e),\n                };\n            }\n\n            button.wait_for_falling_edge().await;\n            // signal_pin.wait_for_low().await;\n            info!(\"Button released!\");\n            if HID_PROTOCOL_MODE.load(Ordering::Relaxed) == HidProtocolMode::Boot as u8 {\n                match writer.write(&[0, 0, 0, 0, 0, 0, 0, 0]).await {\n                    Ok(()) => {}\n                    Err(e) => warn!(\"Failed to send boot report: {:?}\", e),\n                };\n            } else {\n                let report = KeyboardReport {\n                    keycodes: [0, 0, 0, 0, 0, 0],\n                    leds: 0,\n                    modifier: 0,\n                    reserved: 0,\n                };\n                match writer.write_serialize(&report).await {\n                    Ok(()) => {}\n                    Err(e) => warn!(\"Failed to send report: {:?}\", e),\n                };\n            }\n        }\n    };\n\n    let out_fut = async {\n        reader.run(false, &mut request_handler).await;\n    };\n\n    // Run everything concurrently.\n    // If we had made everything `'static` above instead, we could do this using separate tasks instead.\n    join(usb_fut, join(in_fut, out_fut)).await;\n}\n\nstruct MyRequestHandler {}\n\nimpl RequestHandler for MyRequestHandler {\n    fn get_report(&mut self, id: ReportId, _buf: &mut [u8]) -> Option<usize> {\n        info!(\"Get report for {:?}\", id);\n        None\n    }\n\n    fn set_report(&mut self, id: ReportId, data: &[u8]) -> OutResponse {\n        info!(\"Set report for {:?}: {=[u8]}\", id, data);\n        OutResponse::Accepted\n    }\n\n    fn get_protocol(&self) -> HidProtocolMode {\n        let protocol = HidProtocolMode::from(HID_PROTOCOL_MODE.load(Ordering::Relaxed));\n        info!(\"The current HID protocol mode is: {}\", protocol);\n        protocol\n    }\n\n    fn set_protocol(&mut self, protocol: HidProtocolMode) -> OutResponse {\n        info!(\"Switching to HID protocol mode: {}\", protocol);\n        HID_PROTOCOL_MODE.store(protocol as u8, Ordering::Relaxed);\n        OutResponse::Accepted\n    }\n\n    fn set_idle_ms(&mut self, id: Option<ReportId>, dur: u32) {\n        info!(\"Set idle rate for {:?} to {:?}\", id, dur);\n    }\n\n    fn get_idle_ms(&mut self, id: Option<ReportId>) -> Option<u32> {\n        info!(\"Get idle rate for {:?}\", id);\n        None\n    }\n}\n\nstruct MyDeviceHandler {\n    configured: AtomicBool,\n}\n\nimpl MyDeviceHandler {\n    fn new() -> Self {\n        MyDeviceHandler {\n            configured: AtomicBool::new(false),\n        }\n    }\n}\n\nimpl Handler for MyDeviceHandler {\n    fn enabled(&mut self, enabled: bool) {\n        self.configured.store(false, Ordering::Relaxed);\n        if enabled {\n            info!(\"Device enabled\");\n        } else {\n            info!(\"Device disabled\");\n        }\n    }\n\n    fn reset(&mut self) {\n        self.configured.store(false, Ordering::Relaxed);\n        info!(\"Bus reset, the Vbus current limit is 100mA\");\n    }\n\n    fn addressed(&mut self, addr: u8) {\n        self.configured.store(false, Ordering::Relaxed);\n        info!(\"USB address set to: {}\", addr);\n    }\n\n    fn configured(&mut self, configured: bool) {\n        self.configured.store(configured, Ordering::Relaxed);\n        if configured {\n            info!(\"Device configured, it may now draw up to the configured current limit from Vbus.\")\n        } else {\n            info!(\"Device is no longer configured, the Vbus current limit is 100mA.\");\n        }\n    }\n}\n"
  },
  {
    "path": "examples/stm32f4/src/bin/usb_hid_mouse.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse core::sync::atomic::{AtomicU8, Ordering};\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_futures::join::join;\nuse embassy_stm32::time::Hertz;\nuse embassy_stm32::usb::Driver;\nuse embassy_stm32::{Config, bind_interrupts, peripherals, usb};\nuse embassy_time::Timer;\nuse embassy_usb::Builder;\nuse embassy_usb::class::hid::{\n    HidBootProtocol, HidProtocolMode, HidSubclass, HidWriter, ReportId, RequestHandler, State,\n};\nuse embassy_usb::control::OutResponse;\nuse usbd_hid::descriptor::{MouseReport, SerializedDescriptor};\nuse {defmt_rtt as _, panic_probe as _};\n\nbind_interrupts!(struct Irqs {\n    OTG_FS => usb::InterruptHandler<peripherals::USB_OTG_FS>;\n});\n\nstatic HID_PROTOCOL_MODE: AtomicU8 = AtomicU8::new(HidProtocolMode::Boot as u8);\n\n// If you are trying this and your USB device doesn't connect, the most\n// common issues are the RCC config and vbus_detection\n//\n// See https://embassy.dev/book/#_the_usb_examples_are_not_working_on_my_board_is_there_anything_else_i_need_to_configure\n// for more information.\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let mut config = Config::default();\n    {\n        use embassy_stm32::rcc::*;\n        config.rcc.hse = Some(Hse {\n            freq: Hertz(8_000_000),\n            mode: HseMode::Bypass,\n        });\n        config.rcc.pll_src = PllSource::HSE;\n        config.rcc.pll = Some(Pll {\n            prediv: PllPreDiv::DIV4,\n            mul: PllMul::MUL168,\n            divp: Some(PllPDiv::DIV2), // 8mhz / 4 * 168 / 2 = 168Mhz.\n            divq: Some(PllQDiv::DIV7), // 8mhz / 4 * 168 / 7 = 48Mhz.\n            divr: None,\n        });\n        config.rcc.ahb_pre = AHBPrescaler::DIV1;\n        config.rcc.apb1_pre = APBPrescaler::DIV4;\n        config.rcc.apb2_pre = APBPrescaler::DIV2;\n        config.rcc.sys = Sysclk::PLL1_P;\n        config.rcc.mux.clk48sel = mux::Clk48sel::PLL1_Q;\n    }\n    let p = embassy_stm32::init(config);\n\n    // Create the driver, from the HAL.\n    let mut ep_out_buffer = [0u8; 256];\n    let mut config = embassy_stm32::usb::Config::default();\n\n    // Do not enable vbus_detection. This is a safe default that works in all boards.\n    // However, if your USB device is self-powered (can stay powered on if USB is unplugged), you need\n    // to enable vbus_detection to comply with the USB spec. If you enable it, the board\n    // has to support it or USB won't work at all. See docs on `vbus_detection` for details.\n    config.vbus_detection = false;\n\n    let driver = Driver::new_fs(p.USB_OTG_FS, Irqs, p.PA12, p.PA11, &mut ep_out_buffer, config);\n\n    // Create embassy-usb Config\n    let mut config = embassy_usb::Config::new(0xc0de, 0xcafe);\n    config.manufacturer = Some(\"Embassy\");\n    config.product = Some(\"HID mouse example\");\n    config.serial_number = Some(\"12345678\");\n    config.composite_with_iads = false;\n    config.device_class = 0;\n    config.device_sub_class = 0;\n    config.device_protocol = 0;\n\n    // Create embassy-usb DeviceBuilder using the driver and config.\n    // It needs some buffers for building the descriptors.\n    let mut config_descriptor = [0; 256];\n    let mut bos_descriptor = [0; 256];\n    let mut control_buf = [0; 64];\n\n    let mut request_handler = MyRequestHandler {};\n\n    let mut state = State::new();\n\n    let mut builder = Builder::new(\n        driver,\n        config,\n        &mut config_descriptor,\n        &mut bos_descriptor,\n        &mut [], // no msos descriptors\n        &mut control_buf,\n    );\n\n    // Create classes on the builder.\n    let config = embassy_usb::class::hid::Config {\n        report_descriptor: MouseReport::desc(),\n        request_handler: Some(&mut request_handler),\n        poll_ms: 60,\n        max_packet_size: 8,\n        hid_subclass: HidSubclass::Boot,\n        hid_boot_protocol: HidBootProtocol::Mouse,\n    };\n\n    let mut writer = HidWriter::<_, 5>::new(&mut builder, &mut state, config);\n\n    // Build the builder.\n    let mut usb = builder.build();\n\n    // Run the USB device.\n    let usb_fut = usb.run();\n\n    // Do stuff with the class!\n    let hid_fut = async {\n        let mut y: i8 = 5;\n        loop {\n            Timer::after_millis(500).await;\n\n            y = -y;\n\n            if HID_PROTOCOL_MODE.load(Ordering::Relaxed) == HidProtocolMode::Boot as u8 {\n                let buttons = 0u8;\n                let x = 0i8;\n                match writer.write(&[buttons, x as u8, y as u8]).await {\n                    Ok(()) => {}\n                    Err(e) => warn!(\"Failed to send boot report: {:?}\", e),\n                }\n            } else {\n                let report = MouseReport {\n                    buttons: 0,\n                    x: 0,\n                    y,\n                    wheel: 0,\n                    pan: 0,\n                };\n                match writer.write_serialize(&report).await {\n                    Ok(()) => {}\n                    Err(e) => warn!(\"Failed to send report: {:?}\", e),\n                }\n            }\n        }\n    };\n\n    // Run everything concurrently.\n    // If we had made everything `'static` above instead, we could do this using separate tasks instead.\n    join(usb_fut, hid_fut).await;\n}\n\nstruct MyRequestHandler {}\n\nimpl RequestHandler for MyRequestHandler {\n    fn get_report(&mut self, id: ReportId, _buf: &mut [u8]) -> Option<usize> {\n        info!(\"Get report for {:?}\", id);\n        None\n    }\n\n    fn set_report(&mut self, id: ReportId, data: &[u8]) -> OutResponse {\n        info!(\"Set report for {:?}: {=[u8]}\", id, data);\n        OutResponse::Accepted\n    }\n\n    fn get_protocol(&self) -> HidProtocolMode {\n        let protocol = HidProtocolMode::from(HID_PROTOCOL_MODE.load(Ordering::Relaxed));\n        info!(\"The current HID protocol mode is: {}\", protocol);\n        protocol\n    }\n\n    fn set_protocol(&mut self, protocol: HidProtocolMode) -> OutResponse {\n        info!(\"Switching to HID protocol mode: {}\", protocol);\n        HID_PROTOCOL_MODE.store(protocol as u8, Ordering::Relaxed);\n        OutResponse::Accepted\n    }\n\n    fn set_idle_ms(&mut self, id: Option<ReportId>, dur: u32) {\n        info!(\"Set idle rate for {:?} to {:?}\", id, dur);\n    }\n\n    fn get_idle_ms(&mut self, id: Option<ReportId>) -> Option<u32> {\n        info!(\"Get idle rate for {:?}\", id);\n        None\n    }\n}\n"
  },
  {
    "path": "examples/stm32f4/src/bin/usb_raw.rs",
    "content": "//! Example of using USB without a pre-defined class, but instead responding to\n//! raw USB control requests.\n//!\n//! The host computer can either:\n//! * send a command, with a 16-bit request ID, a 16-bit value, and an optional data buffer\n//! * request some data, with a 16-bit request ID, a 16-bit value, and a length of data to receive\n//!\n//! For higher throughput data, you can add some bulk endpoints after creating the alternate,\n//! but for low rate command/response, plain control transfers can be very simple and effective.\n//!\n//! Example code to send/receive data using `nusb`:\n//!\n//! ```ignore\n//! use futures_lite::future::block_on;\n//! use nusb::transfer::{ControlIn, ControlOut, ControlType, Recipient};\n//!\n//! fn main() {\n//!     let di = nusb::list_devices()\n//!         .unwrap()\n//!         .find(|d| d.vendor_id() == 0xc0de && d.product_id() == 0xcafe)\n//!         .expect(\"no device found\");\n//!     let device = di.open().expect(\"error opening device\");\n//!     let interface = device.claim_interface(0).expect(\"error claiming interface\");\n//!\n//!     // Send \"hello world\" to device\n//!     let result = block_on(interface.control_out(ControlOut {\n//!         control_type: ControlType::Vendor,\n//!         recipient: Recipient::Interface,\n//!         request: 100,\n//!         value: 200,\n//!         index: 0,\n//!         data: b\"hello world\",\n//!     }));\n//!     println!(\"{result:?}\");\n//!\n//!     // Receive \"hello\" from device\n//!     let result = block_on(interface.control_in(ControlIn {\n//!         control_type: ControlType::Vendor,\n//!         recipient: Recipient::Interface,\n//!         request: 101,\n//!         value: 201,\n//!         index: 0,\n//!         length: 5,\n//!     }));\n//!     println!(\"{result:?}\");\n//! }\n//! ```\n\n#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_stm32::time::Hertz;\nuse embassy_stm32::usb::Driver;\nuse embassy_stm32::{Config, bind_interrupts, peripherals, usb};\nuse embassy_usb::control::{InResponse, OutResponse, Recipient, Request, RequestType};\nuse embassy_usb::msos::{self, windows_version};\nuse embassy_usb::types::InterfaceNumber;\nuse embassy_usb::{Builder, Handler};\nuse {defmt_rtt as _, panic_probe as _};\n\n// Randomly generated UUID because Windows requires you provide one to use WinUSB.\n// In principle WinUSB-using software could find this device (or a specific interface\n// on it) by its GUID instead of using the VID/PID, but in practice that seems unhelpful.\nconst DEVICE_INTERFACE_GUIDS: &[&str] = &[\"{DAC2087C-63FA-458D-A55D-827C0762DEC7}\"];\n\nbind_interrupts!(struct Irqs {\n    OTG_FS => usb::InterruptHandler<peripherals::USB_OTG_FS>;\n});\n\n// If you are trying this and your USB device doesn't connect, the most\n// common issues are the RCC config and vbus_detection\n//\n// See https://embassy.dev/book/#_the_usb_examples_are_not_working_on_my_board_is_there_anything_else_i_need_to_configure\n// for more information.\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    info!(\"Hello World!\");\n\n    let mut config = Config::default();\n    {\n        use embassy_stm32::rcc::*;\n        config.rcc.hse = Some(Hse {\n            freq: Hertz(8_000_000),\n            mode: HseMode::Bypass,\n        });\n        config.rcc.pll_src = PllSource::HSE;\n        config.rcc.pll = Some(Pll {\n            prediv: PllPreDiv::DIV4,\n            mul: PllMul::MUL168,\n            divp: Some(PllPDiv::DIV2), // 8mhz / 4 * 168 / 2 = 168Mhz.\n            divq: Some(PllQDiv::DIV7), // 8mhz / 4 * 168 / 7 = 48Mhz.\n            divr: None,\n        });\n        config.rcc.ahb_pre = AHBPrescaler::DIV1;\n        config.rcc.apb1_pre = APBPrescaler::DIV4;\n        config.rcc.apb2_pre = APBPrescaler::DIV2;\n        config.rcc.sys = Sysclk::PLL1_P;\n        config.rcc.mux.clk48sel = mux::Clk48sel::PLL1_Q;\n    }\n    let p = embassy_stm32::init(config);\n\n    // Create the driver, from the HAL.\n    let mut ep_out_buffer = [0u8; 256];\n    let mut config = embassy_stm32::usb::Config::default();\n\n    // Do not enable vbus_detection. This is a safe default that works in all boards.\n    // However, if your USB device is self-powered (can stay powered on if USB is unplugged), you need\n    // to enable vbus_detection to comply with the USB spec. If you enable it, the board\n    // has to support it or USB won't work at all. See docs on `vbus_detection` for details.\n    config.vbus_detection = false;\n\n    let driver = Driver::new_fs(p.USB_OTG_FS, Irqs, p.PA12, p.PA11, &mut ep_out_buffer, config);\n\n    // Create embassy-usb Config\n    let mut config = embassy_usb::Config::new(0xc0de, 0xcafe);\n    config.manufacturer = Some(\"Embassy\");\n    config.product = Some(\"USB-raw example\");\n    config.serial_number = Some(\"12345678\");\n\n    // Create embassy-usb DeviceBuilder using the driver and config.\n    // It needs some buffers for building the descriptors.\n    let mut config_descriptor = [0; 256];\n    let mut bos_descriptor = [0; 256];\n    let mut msos_descriptor = [0; 256];\n    let mut control_buf = [0; 64];\n\n    let mut handler = ControlHandler {\n        if_num: InterfaceNumber(0),\n    };\n\n    let mut builder = Builder::new(\n        driver,\n        config,\n        &mut config_descriptor,\n        &mut bos_descriptor,\n        &mut msos_descriptor,\n        &mut control_buf,\n    );\n\n    // Add the Microsoft OS Descriptor (MSOS/MOD) descriptor.\n    // We tell Windows that this entire device is compatible with the \"WINUSB\" feature,\n    // which causes it to use the built-in WinUSB driver automatically, which in turn\n    // can be used by libusb/rusb software without needing a custom driver or INF file.\n    // In principle you might want to call msos_feature() just on a specific function,\n    // if your device also has other functions that still use standard class drivers.\n    builder.msos_descriptor(windows_version::WIN8_1, 0);\n    builder.msos_feature(msos::CompatibleIdFeatureDescriptor::new(\"WINUSB\", \"\"));\n    builder.msos_feature(msos::RegistryPropertyFeatureDescriptor::new(\n        \"DeviceInterfaceGUIDs\",\n        msos::PropertyData::RegMultiSz(DEVICE_INTERFACE_GUIDS),\n    ));\n\n    // Add a vendor-specific function (class 0xFF), and corresponding interface,\n    // that uses our custom handler.\n    let mut function = builder.function(0xFF, 0, 0);\n    let mut interface = function.interface();\n    let _alternate = interface.alt_setting(0xFF, 0, 0, None);\n    handler.if_num = interface.interface_number();\n    drop(function);\n    builder.handler(&mut handler);\n\n    // Build the builder.\n    let mut usb = builder.build();\n\n    // Run the USB device.\n    usb.run().await;\n}\n\n/// Handle CONTROL endpoint requests and responses. For many simple requests and responses\n/// you can get away with only using the control endpoint.\nstruct ControlHandler {\n    if_num: InterfaceNumber,\n}\n\nimpl Handler for ControlHandler {\n    /// Respond to HostToDevice control messages, where the host sends us a command and\n    /// optionally some data, and we can only acknowledge or reject it.\n    fn control_out<'a>(&'a mut self, req: Request, buf: &'a [u8]) -> Option<OutResponse> {\n        // Log the request before filtering to help with debugging.\n        info!(\"Got control_out, request={}, buf={:a}\", req, buf);\n\n        // Only handle Vendor request types to an Interface.\n        if req.request_type != RequestType::Vendor || req.recipient != Recipient::Interface {\n            return None;\n        }\n\n        // Ignore requests to other interfaces.\n        if req.index != self.if_num.0 as u16 {\n            return None;\n        }\n\n        // Accept request 100, value 200, reject others.\n        if req.request == 100 && req.value == 200 {\n            Some(OutResponse::Accepted)\n        } else {\n            Some(OutResponse::Rejected)\n        }\n    }\n\n    /// Respond to DeviceToHost control messages, where the host requests some data from us.\n    fn control_in<'a>(&'a mut self, req: Request, buf: &'a mut [u8]) -> Option<InResponse<'a>> {\n        info!(\"Got control_in, request={}\", req);\n\n        // Only handle Vendor request types to an Interface.\n        if req.request_type != RequestType::Vendor || req.recipient != Recipient::Interface {\n            return None;\n        }\n\n        // Ignore requests to other interfaces.\n        if req.index != self.if_num.0 as u16 {\n            return None;\n        }\n\n        // Respond \"hello\" to request 101, value 201, when asked for 5 bytes, otherwise reject.\n        if req.request == 101 && req.value == 201 && req.length == 5 {\n            buf[..5].copy_from_slice(b\"hello\");\n            Some(InResponse::Accepted(&buf[..5]))\n        } else {\n            Some(InResponse::Rejected)\n        }\n    }\n}\n"
  },
  {
    "path": "examples/stm32f4/src/bin/usb_serial.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse defmt::{panic, *};\nuse embassy_executor::Spawner;\nuse embassy_futures::join::join;\nuse embassy_stm32::time::Hertz;\nuse embassy_stm32::usb::{Driver, Instance};\nuse embassy_stm32::{Config, bind_interrupts, peripherals, usb};\nuse embassy_usb::Builder;\nuse embassy_usb::class::cdc_acm::{CdcAcmClass, State};\nuse embassy_usb::driver::EndpointError;\nuse {defmt_rtt as _, panic_probe as _};\n\nbind_interrupts!(struct Irqs {\n    OTG_FS => usb::InterruptHandler<peripherals::USB_OTG_FS>;\n});\n\n// If you are trying this and your USB device doesn't connect, the most\n// common issues are the RCC config and vbus_detection\n//\n// See https://embassy.dev/book/#_the_usb_examples_are_not_working_on_my_board_is_there_anything_else_i_need_to_configure\n// for more information.\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    info!(\"Hello World!\");\n\n    let mut config = Config::default();\n    {\n        use embassy_stm32::rcc::*;\n        config.rcc.hse = Some(Hse {\n            freq: Hertz(8_000_000),\n            mode: HseMode::Bypass,\n        });\n        config.rcc.pll_src = PllSource::HSE;\n        config.rcc.pll = Some(Pll {\n            prediv: PllPreDiv::DIV4,\n            mul: PllMul::MUL168,\n            divp: Some(PllPDiv::DIV2), // 8mhz / 4 * 168 / 2 = 168Mhz.\n            divq: Some(PllQDiv::DIV7), // 8mhz / 4 * 168 / 7 = 48Mhz.\n            divr: None,\n        });\n        config.rcc.ahb_pre = AHBPrescaler::DIV1;\n        config.rcc.apb1_pre = APBPrescaler::DIV4;\n        config.rcc.apb2_pre = APBPrescaler::DIV2;\n        config.rcc.sys = Sysclk::PLL1_P;\n        config.rcc.mux.clk48sel = mux::Clk48sel::PLL1_Q;\n    }\n    let p = embassy_stm32::init(config);\n\n    // Create the driver, from the HAL.\n    let mut ep_out_buffer = [0u8; 256];\n    let mut config = embassy_stm32::usb::Config::default();\n\n    // Do not enable vbus_detection. This is a safe default that works in all boards.\n    // However, if your USB device is self-powered (can stay powered on if USB is unplugged), you need\n    // to enable vbus_detection to comply with the USB spec. If you enable it, the board\n    // has to support it or USB won't work at all. See docs on `vbus_detection` for details.\n    config.vbus_detection = false;\n\n    let driver = Driver::new_fs(p.USB_OTG_FS, Irqs, p.PA12, p.PA11, &mut ep_out_buffer, config);\n\n    // Create embassy-usb Config\n    let mut config = embassy_usb::Config::new(0xc0de, 0xcafe);\n    config.manufacturer = Some(\"Embassy\");\n    config.product = Some(\"USB-serial example\");\n    config.serial_number = Some(\"12345678\");\n\n    // Create embassy-usb DeviceBuilder using the driver and config.\n    // It needs some buffers for building the descriptors.\n    let mut config_descriptor = [0; 256];\n    let mut bos_descriptor = [0; 256];\n    let mut control_buf = [0; 64];\n\n    let mut state = State::new();\n\n    let mut builder = Builder::new(\n        driver,\n        config,\n        &mut config_descriptor,\n        &mut bos_descriptor,\n        &mut [], // no msos descriptors\n        &mut control_buf,\n    );\n\n    // Create classes on the builder.\n    let mut class = CdcAcmClass::new(&mut builder, &mut state, 64);\n\n    // Build the builder.\n    let mut usb = builder.build();\n\n    // Run the USB device.\n    let usb_fut = usb.run();\n\n    // Do stuff with the class!\n    let echo_fut = async {\n        loop {\n            class.wait_connection().await;\n            info!(\"Connected\");\n            let _ = echo(&mut class).await;\n            info!(\"Disconnected\");\n        }\n    };\n\n    // Run everything concurrently.\n    // If we had made everything `'static` above instead, we could do this using separate tasks instead.\n    join(usb_fut, echo_fut).await;\n}\n\nstruct Disconnected {}\n\nimpl From<EndpointError> for Disconnected {\n    fn from(val: EndpointError) -> Self {\n        match val {\n            EndpointError::BufferOverflow => panic!(\"Buffer overflow\"),\n            EndpointError::Disabled => Disconnected {},\n        }\n    }\n}\n\nasync fn echo<'d, T: Instance + 'd>(class: &mut CdcAcmClass<'d, Driver<'d, T>>) -> Result<(), Disconnected> {\n    let mut buf = [0; 64];\n    loop {\n        let n = class.read_packet(&mut buf).await?;\n        let data = &buf[..n];\n        info!(\"data: {:x}\", data);\n        class.write_packet(data).await?;\n    }\n}\n"
  },
  {
    "path": "examples/stm32f4/src/bin/usb_uac_speaker.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse core::cell::{Cell, RefCell};\n\nuse defmt::{panic, *};\nuse embassy_executor::Spawner;\nuse embassy_stm32::time::Hertz;\nuse embassy_stm32::{Config, bind_interrupts, interrupt, peripherals, timer, usb};\nuse embassy_sync::blocking_mutex::Mutex;\nuse embassy_sync::blocking_mutex::raw::{CriticalSectionRawMutex, NoopRawMutex};\nuse embassy_sync::signal::Signal;\nuse embassy_sync::zerocopy_channel;\nuse embassy_usb::class::uac1;\nuse embassy_usb::class::uac1::speaker::{self, Speaker};\nuse embassy_usb::driver::EndpointError;\nuse heapless::Vec;\nuse micromath::F32Ext;\nuse static_cell::StaticCell;\nuse {defmt_rtt as _, panic_probe as _};\n\nbind_interrupts!(struct Irqs {\n    OTG_FS => usb::InterruptHandler<peripherals::USB_OTG_FS>;\n});\n\nconst TIMER_CHANNEL: timer::Channel = timer::Channel::Ch1;\nstatic TIMER: Mutex<CriticalSectionRawMutex, RefCell<Option<timer::low_level::Timer<peripherals::TIM2>>>> =\n    Mutex::new(RefCell::new(None));\n\n// A counter signal that is written by the feedback timer, once every `FEEDBACK_REFRESH_PERIOD`.\n// At that point, a feedback value is sent to the host.\npub static FEEDBACK_SIGNAL: Signal<CriticalSectionRawMutex, u32> = Signal::new();\n\n// Stereo input\npub const INPUT_CHANNEL_COUNT: usize = 2;\n\n// This example uses a fixed sample rate of 48 kHz.\npub const SAMPLE_RATE_HZ: u32 = 48_000;\npub const FEEDBACK_COUNTER_TICK_RATE: u32 = 42_000_000;\n\n// Use 32 bit samples, which allow for a lot of (software) volume adjustment without degradation of quality.\npub const SAMPLE_WIDTH: uac1::SampleWidth = uac1::SampleWidth::Width4Byte;\npub const SAMPLE_WIDTH_BIT: usize = SAMPLE_WIDTH.in_bit();\npub const SAMPLE_SIZE: usize = SAMPLE_WIDTH as usize;\npub const SAMPLE_SIZE_PER_S: usize = (SAMPLE_RATE_HZ as usize) * INPUT_CHANNEL_COUNT * SAMPLE_SIZE;\n\n// Size of audio samples per 1 ms - for the full-speed USB frame period of 1 ms.\npub const USB_FRAME_SIZE: usize = SAMPLE_SIZE_PER_S.div_ceil(1000);\n\n// Select front left and right audio channels.\npub const AUDIO_CHANNELS: [uac1::Channel; INPUT_CHANNEL_COUNT] = [uac1::Channel::LeftFront, uac1::Channel::RightFront];\n\n// Factor of two as a margin for feedback (this is an excessive amount)\npub const USB_MAX_PACKET_SIZE: usize = 2 * USB_FRAME_SIZE;\npub const USB_MAX_SAMPLE_COUNT: usize = USB_MAX_PACKET_SIZE / SAMPLE_SIZE;\n\n// The data type that is exchanged via the zero-copy channel (a sample vector).\npub type SampleBlock = Vec<u32, USB_MAX_SAMPLE_COUNT>;\n\n// Feedback is provided in 10.14 format for full-speed endpoints.\npub const FEEDBACK_REFRESH_PERIOD: uac1::FeedbackRefresh = uac1::FeedbackRefresh::Period8Frames;\nconst FEEDBACK_SHIFT: usize = 14;\n\nconst TICKS_PER_SAMPLE: f32 = (FEEDBACK_COUNTER_TICK_RATE as f32) / (SAMPLE_RATE_HZ as f32);\n\nstruct Disconnected {}\n\nimpl From<EndpointError> for Disconnected {\n    fn from(val: EndpointError) -> Self {\n        match val {\n            EndpointError::BufferOverflow => panic!(\"Buffer overflow\"),\n            EndpointError::Disabled => Disconnected {},\n        }\n    }\n}\n\n/// Sends feedback messages to the host.\nasync fn feedback_handler<'d, T: usb::Instance + 'd>(\n    feedback: &mut speaker::Feedback<'d, usb::Driver<'d, T>>,\n    feedback_factor: f32,\n) -> Result<(), Disconnected> {\n    let mut packet: Vec<u8, 4> = Vec::new();\n\n    // Collects the fractional component of the feedback value that is lost by rounding.\n    let mut rest = 0.0_f32;\n\n    loop {\n        let counter = FEEDBACK_SIGNAL.wait().await;\n\n        packet.clear();\n\n        let raw_value = counter as f32 * feedback_factor + rest;\n        let value = raw_value.round();\n        rest = raw_value - value;\n\n        let value = value as u32;\n        packet.push(value as u8).unwrap();\n        packet.push((value >> 8) as u8).unwrap();\n        packet.push((value >> 16) as u8).unwrap();\n\n        feedback.write_packet(&packet).await?;\n        debug!(\"feedback sent {}\", value);\n    }\n}\n\n/// Handles streaming of audio data from the host.\nasync fn stream_handler<'d, T: usb::Instance + 'd>(\n    stream: &mut speaker::Stream<'d, usb::Driver<'d, T>>,\n    sender: &mut zerocopy_channel::Sender<'static, NoopRawMutex, SampleBlock>,\n) -> Result<(), Disconnected> {\n    loop {\n        let mut usb_data = [0u8; USB_MAX_PACKET_SIZE];\n        let data_size = stream.read_packet(&mut usb_data).await?;\n\n        let word_count = data_size / SAMPLE_SIZE;\n\n        if word_count * SAMPLE_SIZE == data_size {\n            // Obtain a buffer from the channel\n            let samples = sender.send().await;\n            samples.clear();\n\n            for w in 0..word_count {\n                let byte_offset = w * SAMPLE_SIZE;\n                let sample = u32::from_le_bytes(usb_data[byte_offset..byte_offset + SAMPLE_SIZE].try_into().unwrap());\n\n                // Fill the sample buffer with data.\n                samples.push(sample).unwrap();\n            }\n\n            sender.send_done();\n        } else {\n            debug!(\"Invalid USB buffer size of {}, skipped.\", data_size);\n        }\n    }\n}\n\n/// Receives audio samples from the USB streaming task and can play them back.\n#[embassy_executor::task]\nasync fn audio_receiver_task(mut usb_audio_receiver: zerocopy_channel::Receiver<'static, NoopRawMutex, SampleBlock>) {\n    loop {\n        let _samples = usb_audio_receiver.receive().await;\n        // Use the samples, for example play back via the SAI peripheral.\n\n        // Notify the channel that the buffer is now ready to be reused\n        usb_audio_receiver.receive_done();\n    }\n}\n\n/// Receives audio samples from the host.\n#[embassy_executor::task]\nasync fn usb_streaming_task(\n    mut stream: speaker::Stream<'static, usb::Driver<'static, peripherals::USB_OTG_FS>>,\n    mut sender: zerocopy_channel::Sender<'static, NoopRawMutex, SampleBlock>,\n) {\n    loop {\n        stream.wait_connection().await;\n        _ = stream_handler(&mut stream, &mut sender).await;\n    }\n}\n\n/// Sends sample rate feedback to the host.\n///\n/// The `feedback_factor` scales the feedback timer's counter value so that the result is the number of samples that\n/// this device played back or \"consumed\" during one SOF period (1 ms) - in 10.14 format.\n///\n/// Ideally, the `feedback_factor` that is calculated below would be an integer for avoiding numerical errors.\n/// This is achieved by having `TICKS_PER_SAMPLE` be a power of two. For audio applications at a sample rate of 48 kHz,\n/// 24.576 MHz would be one such option.\n///\n/// A good choice for the STM32F4, which also has to generate a 48 MHz clock from its HSE (e.g. running at 8 MHz)\n/// for USB, is to clock the feedback timer from the MCLK output of the SAI peripheral. The SAI peripheral then uses an\n/// external clock. In that case, wiring the MCLK output to the timer clock input is required.\n///\n/// This simple example just uses the internal clocks for supplying the feedback timer,\n/// and does not even set up a SAI peripheral.\n#[embassy_executor::task]\nasync fn usb_feedback_task(mut feedback: speaker::Feedback<'static, usb::Driver<'static, peripherals::USB_OTG_FS>>) {\n    let feedback_factor =\n        ((1 << FEEDBACK_SHIFT) as f32 / TICKS_PER_SAMPLE) / FEEDBACK_REFRESH_PERIOD.frame_count() as f32;\n\n    // Should be 2.3405714285714287...\n    info!(\"Using a feedback factor of {}.\", feedback_factor);\n\n    loop {\n        feedback.wait_connection().await;\n        _ = feedback_handler(&mut feedback, feedback_factor).await;\n    }\n}\n\n#[embassy_executor::task]\nasync fn usb_task(mut usb_device: embassy_usb::UsbDevice<'static, usb::Driver<'static, peripherals::USB_OTG_FS>>) {\n    usb_device.run().await;\n}\n\n/// Checks for changes on the control monitor of the class.\n///\n/// In this case, monitor changes of volume or mute state.\n#[embassy_executor::task]\nasync fn usb_control_task(control_monitor: speaker::ControlMonitor<'static>) {\n    loop {\n        control_monitor.changed().await;\n\n        for channel in AUDIO_CHANNELS {\n            let volume = control_monitor.volume(channel).unwrap();\n            info!(\"Volume changed to {} on channel {}.\", volume, channel);\n        }\n    }\n}\n\n/// Feedback value measurement and calculation\n///\n/// Used for measuring/calculating the number of samples that were received from the host during the\n/// `FEEDBACK_REFRESH_PERIOD`.\n///\n/// Configured in this example with\n/// - a refresh period of 8 ms, and\n/// - a tick rate of 42 MHz.\n///\n/// This gives an (ideal) counter value of 336.000 for every update of the `FEEDBACK_SIGNAL`.\n#[interrupt]\nfn TIM2() {\n    static LAST_TICKS: Mutex<CriticalSectionRawMutex, Cell<u32>> = Mutex::new(Cell::new(0));\n    static FRAME_COUNT: Mutex<CriticalSectionRawMutex, Cell<usize>> = Mutex::new(Cell::new(0));\n\n    critical_section::with(|cs| {\n        let mut guard = TIMER.borrow(cs).borrow_mut();\n        let timer = guard.as_mut().unwrap();\n        if timer.get_input_interrupt(TIMER_CHANNEL) {\n            let ticks = timer.get_capture_value(TIMER_CHANNEL);\n\n            let frame_count = FRAME_COUNT.borrow(cs);\n            let last_ticks = LAST_TICKS.borrow(cs);\n\n            frame_count.set(frame_count.get() + 1);\n            if frame_count.get() >= FEEDBACK_REFRESH_PERIOD.frame_count() {\n                frame_count.set(0);\n                FEEDBACK_SIGNAL.signal(ticks.wrapping_sub(last_ticks.get()));\n                last_ticks.set(ticks);\n            }\n            // Clear trigger interrupt flag.\n            timer.clear_input_interrupt(TIMER_CHANNEL);\n        };\n    });\n}\n\n// If you are trying this and your USB device doesn't connect, the most\n// common issues are the RCC config and vbus_detection\n//\n// See https://embassy.dev/book/#_the_usb_examples_are_not_working_on_my_board_is_there_anything_else_i_need_to_configure\n// for more information.\n#[embassy_executor::main]\nasync fn main(spawner: Spawner) {\n    info!(\"Hello World!\");\n\n    let mut config = Config::default();\n    {\n        use embassy_stm32::rcc::*;\n        config.rcc.hse = Some(Hse {\n            freq: embassy_stm32::time::Hertz::mhz(25),\n            mode: HseMode::Oscillator,\n        });\n        config.rcc.pll_src = PllSource::HSE;\n        config.rcc.pll = Some(Pll {\n            prediv: PllPreDiv::DIV25,\n            mul: PllMul::MUL336,\n            divp: Some(PllPDiv::DIV2), // ((8 MHz / 4) * 168) / 2 = 168 Mhz.\n            divq: Some(PllQDiv::DIV7), // ((8 MHz / 4) * 168) / 7 = 48 Mhz.\n            divr: None,\n        });\n        config.rcc.ahb_pre = AHBPrescaler::DIV1;\n        config.rcc.apb1_pre = APBPrescaler::DIV4;\n        config.rcc.apb2_pre = APBPrescaler::DIV2;\n        config.rcc.sys = Sysclk::PLL1_P;\n        config.rcc.mux.clk48sel = mux::Clk48sel::PLL1_Q;\n    }\n    let p = embassy_stm32::init(config);\n\n    // Configure all required buffers in a static way.\n    debug!(\"USB packet size is {} byte\", USB_MAX_PACKET_SIZE);\n    static CONFIG_DESCRIPTOR: StaticCell<[u8; 256]> = StaticCell::new();\n    let config_descriptor = CONFIG_DESCRIPTOR.init([0; 256]);\n\n    static BOS_DESCRIPTOR: StaticCell<[u8; 32]> = StaticCell::new();\n    let bos_descriptor = BOS_DESCRIPTOR.init([0; 32]);\n\n    const CONTROL_BUF_SIZE: usize = 64;\n    static CONTROL_BUF: StaticCell<[u8; CONTROL_BUF_SIZE]> = StaticCell::new();\n    let control_buf = CONTROL_BUF.init([0; CONTROL_BUF_SIZE]);\n\n    const FEEDBACK_BUF_SIZE: usize = 4;\n    static EP_OUT_BUFFER: StaticCell<[u8; FEEDBACK_BUF_SIZE + CONTROL_BUF_SIZE + USB_MAX_PACKET_SIZE]> =\n        StaticCell::new();\n    let ep_out_buffer = EP_OUT_BUFFER.init([0u8; FEEDBACK_BUF_SIZE + CONTROL_BUF_SIZE + USB_MAX_PACKET_SIZE]);\n\n    static STATE: StaticCell<speaker::State> = StaticCell::new();\n    let state = STATE.init(speaker::State::new());\n\n    // Create the driver, from the HAL.\n    let mut usb_config = usb::Config::default();\n\n    // Do not enable vbus_detection. This is a safe default that works in all boards.\n    // However, if your USB device is self-powered (can stay powered on if USB is unplugged), you need\n    // to enable vbus_detection to comply with the USB spec. If you enable it, the board\n    // has to support it or USB won't work at all. See docs on `vbus_detection` for details.\n    usb_config.vbus_detection = false;\n\n    let usb_driver = usb::Driver::new_fs(p.USB_OTG_FS, Irqs, p.PA12, p.PA11, ep_out_buffer, usb_config);\n\n    // Basic USB device configuration\n    let mut config = embassy_usb::Config::new(0xc0de, 0xcafe);\n    config.manufacturer = Some(\"Embassy\");\n    config.product = Some(\"USB-audio-speaker example\");\n    config.serial_number = Some(\"12345678\");\n\n    let mut builder = embassy_usb::Builder::new(\n        usb_driver,\n        config,\n        config_descriptor,\n        bos_descriptor,\n        &mut [], // no msos descriptors\n        control_buf,\n    );\n\n    // Create the UAC1 Speaker class components\n    let (stream, feedback, control_monitor) = Speaker::new(\n        &mut builder,\n        state,\n        USB_MAX_PACKET_SIZE as u16,\n        uac1::SampleWidth::Width4Byte,\n        &[SAMPLE_RATE_HZ],\n        &AUDIO_CHANNELS,\n        FEEDBACK_REFRESH_PERIOD,\n    );\n\n    // Create the USB device\n    let usb_device = builder.build();\n\n    // Establish a zero-copy channel for transferring received audio samples between tasks\n    static SAMPLE_BLOCKS: StaticCell<[SampleBlock; 2]> = StaticCell::new();\n    let sample_blocks = SAMPLE_BLOCKS.init([Vec::new(), Vec::new()]);\n\n    static CHANNEL: StaticCell<zerocopy_channel::Channel<'_, NoopRawMutex, SampleBlock>> = StaticCell::new();\n    let channel = CHANNEL.init(zerocopy_channel::Channel::new(sample_blocks));\n    let (sender, receiver) = channel.split();\n\n    // Run a timer for counting between SOF interrupts.\n    let mut tim2 = timer::low_level::Timer::new(p.TIM2);\n    tim2.set_tick_freq(Hertz(FEEDBACK_COUNTER_TICK_RATE));\n    tim2.set_trigger_source(timer::low_level::TriggerSource::ITR1); // The USB SOF signal.\n\n    tim2.set_input_ti_selection(TIMER_CHANNEL, timer::low_level::InputTISelection::TRC);\n    tim2.set_input_capture_prescaler(TIMER_CHANNEL, 0);\n    tim2.set_input_capture_filter(TIMER_CHANNEL, timer::low_level::FilterValue::FCK_INT_N2);\n\n    // Reset all interrupt flags.\n    tim2.regs_gp32().sr().write(|r| r.0 = 0);\n\n    // Enable routing of SOF to the timer.\n    tim2.regs_gp32().or().write(|r| *r = 0b10 << 10);\n\n    tim2.enable_channel(TIMER_CHANNEL, true);\n    tim2.enable_input_interrupt(TIMER_CHANNEL, true);\n\n    tim2.start();\n\n    TIMER.lock(|p| p.borrow_mut().replace(tim2));\n\n    // Unmask the TIM2 interrupt.\n    unsafe {\n        cortex_m::peripheral::NVIC::unmask(interrupt::TIM2);\n    }\n\n    // Launch USB audio tasks.\n    spawner.spawn(unwrap!(usb_control_task(control_monitor)));\n    spawner.spawn(unwrap!(usb_streaming_task(stream, sender)));\n    spawner.spawn(unwrap!(usb_feedback_task(feedback)));\n    spawner.spawn(unwrap!(usb_task(usb_device)));\n    spawner.spawn(unwrap!(audio_receiver_task(receiver)));\n}\n"
  },
  {
    "path": "examples/stm32f4/src/bin/wdt.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_stm32::gpio::{Level, Output, Speed};\nuse embassy_stm32::wdg::IndependentWatchdog;\nuse embassy_time::Timer;\nuse {defmt_rtt as _, panic_probe as _};\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let p = embassy_stm32::init(Default::default());\n    info!(\"Hello World!\");\n\n    let mut led = Output::new(p.PB7, Level::High, Speed::Low);\n\n    let mut wdt = IndependentWatchdog::new(p.IWDG, 1_000_000);\n    wdt.unleash();\n\n    let mut i = 0;\n\n    loop {\n        info!(\"high\");\n        led.set_high();\n        Timer::after_millis(300).await;\n\n        info!(\"low\");\n        led.set_low();\n        Timer::after_millis(300).await;\n\n        // Pet watchdog for 5 iterations and then stop.\n        // MCU should restart in 1 second after the last pet.\n        if i < 5 {\n            info!(\"Petting watchdog\");\n            wdt.pet();\n        }\n\n        i += 1;\n    }\n}\n"
  },
  {
    "path": "examples/stm32f4/src/bin/ws2812_pwm.rs",
    "content": "// Configure TIM3 in PWM mode, and start DMA Transfer(s) to send color data into ws2812.\n// We assume the DIN pin of ws2812 connect to GPIO PB4, and ws2812 is properly powered.\n//\n// The idea is that the data rate of ws2812 is 800 kHz, and it use different duty ratio to represent bit 0 and bit 1.\n// Thus we can set TIM overflow at 800 kHz, and change duty ratio of TIM to meet the bit representation of ws2812.\n//\n// you may also want to take a look at `ws2812_spi.rs` file, which make use of SPI instead.\n//\n// Warning:\n// DO NOT stare at ws2812 directy (especially after each MCU Reset), its (max) brightness could easily make your eyes feel burn.\n\n#![no_std]\n#![no_main]\n\nuse embassy_executor::Spawner;\nuse embassy_stm32::gpio::OutputType;\nuse embassy_stm32::time::khz;\nuse embassy_stm32::timer::Channel;\nuse embassy_stm32::timer::low_level::CountingMode;\nuse embassy_stm32::timer::simple_pwm::{PwmPin, SimplePwm};\nuse embassy_stm32::{bind_interrupts, dma, peripherals};\nuse embassy_time::{Duration, Ticker, Timer};\nuse {defmt_rtt as _, panic_probe as _};\n\nbind_interrupts!(struct Irqs {\n    DMA1_STREAM2 => dma::InterruptHandler<peripherals::DMA1_CH2>;\n});\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let mut device_config = embassy_stm32::Config::default();\n\n    // set SYSCLK/HCLK/PCLK2 to 20 MHz, thus each tick is 0.05 us,\n    // and ws2812 timings are integer multiples of 0.05 us\n    {\n        use embassy_stm32::rcc::*;\n        use embassy_stm32::time::*;\n        device_config.enable_debug_during_sleep = true;\n        device_config.rcc.hse = Some(Hse {\n            freq: mhz(12),\n            mode: HseMode::Oscillator,\n        });\n        device_config.rcc.pll_src = PllSource::HSE;\n        device_config.rcc.pll = Some(Pll {\n            prediv: PllPreDiv::DIV6,\n            mul: PllMul::MUL80,\n            divp: Some(PllPDiv::DIV8),\n            divq: None,\n            divr: None,\n        });\n        device_config.rcc.sys = Sysclk::PLL1_P;\n    }\n\n    let mut dp = embassy_stm32::init(device_config);\n\n    let mut ws2812_pwm = SimplePwm::new(\n        dp.TIM3,\n        Some(PwmPin::new(dp.PB4, OutputType::PushPull)),\n        None,\n        None,\n        None,\n        khz(800), // data rate of ws2812\n        CountingMode::EdgeAlignedUp,\n    );\n\n    // construct ws2812 non-return-to-zero (NRZ) code bit by bit\n    // ws2812 only need 24 bits for each LED, but we add one bit more to keep PWM output low\n\n    let max_duty = ws2812_pwm.max_duty_cycle() as u16;\n    let n0 = 8 * max_duty / 25; // ws2812 Bit 0 high level timing\n    let n1 = 2 * n0; // ws2812 Bit 1 high level timing\n\n    let turn_off = [\n        n0, n0, n0, n0, n0, n0, n0, n0, // Green\n        n0, n0, n0, n0, n0, n0, n0, n0, // Red\n        n0, n0, n0, n0, n0, n0, n0, n0, // Blue\n        0,  // keep PWM output low after a transfer\n    ];\n\n    let dim_white = [\n        n0, n0, n0, n0, n0, n0, n1, n0, // Green\n        n0, n0, n0, n0, n0, n0, n1, n0, // Red\n        n0, n0, n0, n0, n0, n0, n1, n0, // Blue\n        0,  // keep PWM output low after a transfer\n    ];\n\n    let color_list = &[&turn_off, &dim_white];\n\n    let pwm_channel = Channel::Ch1;\n\n    // make sure PWM output keep low on first start\n    ws2812_pwm.channel(pwm_channel).set_duty_cycle(0);\n\n    // flip color at 2 Hz\n    let mut ticker = Ticker::every(Duration::from_millis(500));\n\n    loop {\n        for &color in color_list {\n            // with &mut, we can easily reuse same DMA channel multiple times\n            ws2812_pwm\n                .waveform_up(dp.DMA1_CH2.reborrow(), Irqs, pwm_channel, color)\n                .await;\n            // ws2812 need at least 50 us low level input to confirm the input data and change it's state\n            Timer::after_micros(50).await;\n            // wait until ticker tick\n            ticker.next().await;\n        }\n    }\n}\n"
  },
  {
    "path": "examples/stm32f4/src/bin/ws2812_spi.rs",
    "content": "// Mimic PWM with SPI, to control ws2812\n// We assume the DIN pin of ws2812 connect to GPIO PB5, and ws2812 is properly powered.\n//\n// The idea is that the data rate of ws2812 is 800 kHz, and it use different duty ratio to represent bit 0 and bit 1.\n// Thus we can adjust SPI to send each *round* of data at 800 kHz, and in each *round*, we can adjust each *bit* to mimic 2 different PWM waveform.\n// such that the output waveform meet the bit representation of ws2812.\n//\n// If you want to save SPI for other purpose, you may want to take a look at `ws2812_pwm_dma.rs` file, which make use of TIM and DMA.\n//\n// Warning:\n// DO NOT stare at ws2812 directly (especially after each MCU Reset), its (max) brightness could easily make your eyes feel burn.\n\n#![no_std]\n#![no_main]\n\nuse embassy_stm32::time::khz;\nuse embassy_stm32::{bind_interrupts, dma, peripherals, spi};\nuse embassy_time::{Duration, Ticker, Timer};\nuse {defmt_rtt as _, panic_probe as _};\n\n// we use 16 bit data frame format of SPI, to let timing as accurate as possible.\n// thanks to loose tolerance of ws2812 timing, you can also use 8 bit data frame format, thus you will need to adjust the bit representation.\nconst N0: u16 = 0b1111100000000000u16; // ws2812 Bit 0 high level timing\nconst N1: u16 = 0b1111111111000000u16; // ws2812 Bit 1 high level timing\n\n// ws2812 only need 24 bits for each LED,\n// but we add one bit more to keep SPI output low at the end\n\nstatic TURN_OFF: [u16; 25] = [\n    N0, N0, N0, N0, N0, N0, N0, N0, // Green\n    N0, N0, N0, N0, N0, N0, N0, N0, // Red\n    N0, N0, N0, N0, N0, N0, N0, N0, // Blue\n    0,  // keep SPI output low after last bit\n];\n\nstatic DIM_WHITE: [u16; 25] = [\n    N0, N0, N0, N0, N0, N0, N1, N0, // Green\n    N0, N0, N0, N0, N0, N0, N1, N0, // Red\n    N0, N0, N0, N0, N0, N0, N1, N0, // Blue\n    0,  // keep SPI output low after last bit\n];\n\nstatic COLOR_LIST: &[&[u16]] = &[&TURN_OFF, &DIM_WHITE];\n\nbind_interrupts!(struct Irqs {\n    DMA2_STREAM3 => dma::InterruptHandler<peripherals::DMA2_CH3>;\n});\n\n#[embassy_executor::main]\nasync fn main(_spawner: embassy_executor::Spawner) {\n    let mut device_config = embassy_stm32::Config::default();\n\n    // Since we use 16 bit SPI, and we need each round 800 kHz,\n    // thus SPI output speed should be 800 kHz * 16 = 12.8 MHz, and APB clock should be 2 * 12.8 MHz = 25.6 MHz.\n    //\n    // As for my setup, with 12 MHz HSE, I got 25.5 MHz SYSCLK, which is slightly slower, but it's ok for ws2812.\n    {\n        use embassy_stm32::rcc::{Hse, HseMode, Pll, PllMul, PllPDiv, PllPreDiv, PllSource, Sysclk};\n        use embassy_stm32::time::mhz;\n        device_config.enable_debug_during_sleep = true;\n        device_config.rcc.hse = Some(Hse {\n            freq: mhz(12),\n            mode: HseMode::Oscillator,\n        });\n        device_config.rcc.pll_src = PllSource::HSE;\n        device_config.rcc.pll = Some(Pll {\n            prediv: PllPreDiv::DIV6,\n            mul: PllMul::MUL102,\n            divp: Some(PllPDiv::DIV8),\n            divq: None,\n            divr: None,\n        });\n        device_config.rcc.sys = Sysclk::PLL1_P;\n    }\n\n    let dp = embassy_stm32::init(device_config);\n\n    // Set SPI output speed.\n    // It's ok to blindly set frequency to 12800 kHz, the hal crate will take care of the SPI CR1 BR field.\n    // And in my case, the real bit rate will be 25.5 MHz / 2 = 12_750 kHz\n    let mut spi_config = spi::Config::default();\n    spi_config.frequency = khz(12_800);\n\n    // Since we only output waveform, then the Rx and Sck and RxDma it is not considered\n    let mut ws2812_spi = spi::Spi::new_txonly_nosck(dp.SPI1, dp.PB5, dp.DMA2_CH3, Irqs, spi_config);\n\n    // flip color at 2 Hz\n    let mut ticker = Ticker::every(Duration::from_millis(500));\n\n    loop {\n        for &color in COLOR_LIST {\n            ws2812_spi.write(color).await.unwrap();\n            // ws2812 need at least 50 us low level input to confirm the input data and change it's state\n            Timer::after_micros(50).await;\n            // wait until ticker tick\n            ticker.next().await;\n        }\n    }\n}\n"
  },
  {
    "path": "examples/stm32f401/.cargo/config.toml",
    "content": "[target.'cfg(all(target_arch = \"arm\", target_os = \"none\"))']\nrunner = \"probe-rs run --chip STM32F401RE\"\n\n[build]\ntarget = \"thumbv7em-none-eabi\"\n\n[env]\nDEFMT_LOG = \"trace\"\n"
  },
  {
    "path": "examples/stm32f401/Cargo.toml",
    "content": "[package]\nedition = \"2024\"\nname = \"embassy-stm32f401-examples\"\nversion = \"0.1.0\"\nlicense = \"MIT OR Apache-2.0\"\npublish = false\n\n[dependencies]\nembassy-stm32 = { version = \"0.6.0\", path = \"../../embassy-stm32\", features = [\"defmt\", \"stm32f401re\", \"unstable-pac\", \"memory-x\", \"time-driver-tim4\", \"exti\", \"chrono\"] }\nembassy-sync = { version = \"0.8.0\", path = \"../../embassy-sync\", features = [\"defmt\"] }\nembassy-executor = { version = \"0.10.0\", path = \"../../embassy-executor\", features = [\"platform-cortex-m\", \"executor-thread\", \"executor-interrupt\", \"defmt\"] }\nembassy-time = { version = \"0.5.1\", path = \"../../embassy-time\", features = [\"defmt\", \"defmt-timestamp-uptime\", \"tick-hz-32_768\"] }\nembassy-usb = { version = \"0.6.0\", path = \"../../embassy-usb\", features = [\"defmt\" ] }\nembassy-net = { version = \"0.9.0\", path = \"../../embassy-net\", features = [\"defmt\", \"tcp\", \"dhcpv4\", \"medium-ethernet\", ] }\nembassy-net-wiznet = { version = \"0.3.0\", path = \"../../embassy-net-wiznet\", features = [\"defmt\"] }\nembassy-futures = { version = \"0.1.2\", path = \"../../embassy-futures\" }\n\ndefmt = \"1.0.1\"\ndefmt-rtt = \"1.0.0\"\n\ncortex-m = { version = \"0.7.6\", features = [\"inline-asm\", \"critical-section-single-core\"] }\ncortex-m-rt = \"0.7.0\"\nembedded-hal = \"0.2.6\"\nembedded-hal-bus = { version = \"0.2\", features = [\"async\"] }\nembedded-io = { version = \"0.7.1\" }\nembedded-io-async = { version = \"0.7.0\" }\npanic-probe = { version = \"1.0.0\", features = [\"print-defmt\"] }\nfutures-util = { version = \"0.3.30\", default-features = false }\nheapless = { version = \"0.9\", default-features = false }\ncritical-section = \"1.1\"\nnb = \"1.0.0\"\nembedded-storage = \"0.3.1\"\nmicromath = \"2.0.0\"\nusbd-hid = \"0.10.0\"\nstatic_cell = \"2\"\nchrono = { version = \"^0.4\", default-features = false}\n\n[profile.release]\ndebug = 2\n\n[package.metadata.embassy]\nbuild = [\n  { target = \"thumbv7em-none-eabi\", artifact-dir = \"out/examples/stm32f401\" }\n]\n"
  },
  {
    "path": "examples/stm32f401/build.rs",
    "content": "fn main() {\n    println!(\"cargo:rustc-link-arg-bins=--nmagic\");\n    println!(\"cargo:rustc-link-arg-bins=-Tlink.x\");\n    println!(\"cargo:rustc-link-arg-bins=-Tdefmt.x\");\n}\n"
  },
  {
    "path": "examples/stm32f401/src/bin/i2c_loopback_test_async.rs",
    "content": "//! I2C Async Loopback Test - Comprehensive slave mode testing\n//!\n//! Tests the async I2C master and slave implementations using\n//! a loopback configuration with I2C1 as slave and I2C2 as master\n//! on the same device.\n//!\n//! Hardware setup (for NUCLEO-F401RE):\n//! - Connect PB8 (I2C1 SCL) to PB10 (I2C2 SCL)\n//! - Connect PB9 (I2C1 SDA) to PB3 (I2C2 SDA)\n//! - Add 4.7k pull-up resistors to 3.3V on both SCL and SDA lines\n//!\n//! # Test Coverage\n//!\n//! This test covers:\n//! - Write operations (slave receiving data from master)\n//! - Read operations (slave sending data to master)\n//! - Separate write + read transactions\n//! - Combined write_read transactions (RESTART condition)\n\n#![no_std]\n#![no_main]\n\nuse defmt::{error, info, warn};\nuse embassy_executor::Spawner;\nuse embassy_futures::join::join;\nuse embassy_stm32::i2c::{self, I2c, SlaveAddrConfig, SlaveCommandKind};\nuse embassy_stm32::time::khz;\nuse embassy_stm32::{bind_interrupts, dma, peripherals};\nuse embassy_time::Timer;\nuse {defmt_rtt as _, panic_probe as _};\n\nconst I2C_ADDR: u8 = 0x42;\n\nbind_interrupts!(struct Irqs {\n    I2C1_EV => i2c::EventInterruptHandler<peripherals::I2C1>;\n    I2C1_ER => i2c::ErrorInterruptHandler<peripherals::I2C1>;\n    I2C2_EV => i2c::EventInterruptHandler<peripherals::I2C2>;\n    I2C2_ER => i2c::ErrorInterruptHandler<peripherals::I2C2>;\n    DMA1_STREAM6 => dma::InterruptHandler<peripherals::DMA1_CH6>;\n    DMA1_STREAM0 => dma::InterruptHandler<peripherals::DMA1_CH0>;\n    DMA1_STREAM7 => dma::InterruptHandler<peripherals::DMA1_CH7>;\n    DMA1_STREAM3 => dma::InterruptHandler<peripherals::DMA1_CH3>;\n});\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let p = embassy_stm32::init(Default::default());\n\n    let i2c_frequency = khz(400);\n\n    info!(\"==============================================\");\n    info!(\"I2C Async Loopback Test @ {} kHz\", i2c_frequency.0 / 1000);\n    info!(\"I2C1 (slave) on PB8/PB9, I2C2 (master) on PB10/PB3\");\n    info!(\"==============================================\");\n\n    // I2C1 as slave (PB8=SCL, PB9=SDA)\n    let i2c1_config = {\n        let mut config = i2c::Config::default();\n        config.frequency = i2c_frequency;\n        config\n    };\n    let i2c_slave = I2c::new(p.I2C1, p.PB8, p.PB9, p.DMA1_CH6, p.DMA1_CH0, Irqs, i2c1_config)\n        .into_slave_multimaster(SlaveAddrConfig::basic(I2C_ADDR));\n\n    // I2C2 as master (PB10=SCL, PB3=SDA)\n    let i2c2_config = {\n        let mut config = i2c::Config::default();\n        config.frequency = i2c_frequency;\n        config\n    };\n    let i2c_master = I2c::new(p.I2C2, p.PB10, p.PB3, p.DMA1_CH7, p.DMA1_CH3, Irqs, i2c2_config);\n\n    join(slave_task(i2c_slave), master_task(i2c_master)).await;\n}\n\nasync fn slave_task(mut i2c: I2c<'static, embassy_stm32::mode::Async, i2c::mode::MultiMaster>) {\n    info!(\"[Slave] Ready at address 0x{:02X}\", I2C_ADDR);\n\n    loop {\n        match i2c.listen().await {\n            Ok(command) => match command.kind {\n                SlaveCommandKind::Write => {\n                    let mut buffer = [0u8; 32];\n                    match i2c.respond_to_write(&mut buffer).await {\n                        Ok(bytes_received) => {\n                            info!(\n                                \"[Slave] Received {} bytes: {:02X}\",\n                                bytes_received,\n                                &buffer[..bytes_received]\n                            );\n                        }\n                        Err(e) => {\n                            error!(\"[Slave] respond_to_write error: {:?}\", e);\n                        }\n                    }\n                }\n                SlaveCommandKind::Read => {\n                    // Send incrementing pattern for easy verification\n                    let response_data = [0xA0, 0xA1, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7];\n                    match i2c.respond_to_read(&response_data).await {\n                        Ok(bytes_sent) => {\n                            info!(\"[Slave] Sent {} bytes\", bytes_sent);\n                        }\n                        Err(e) => {\n                            error!(\"[Slave] respond_to_read error: {:?}\", e);\n                        }\n                    }\n                }\n            },\n            Err(e) => {\n                warn!(\"[Slave] Listen error: {:?}\", e);\n                Timer::after_millis(100).await;\n            }\n        }\n    }\n}\n\nasync fn master_task(mut i2c: I2c<'static, embassy_stm32::mode::Async, i2c::mode::Master>) {\n    // Give slave time to initialize\n    Timer::after_millis(100).await;\n\n    info!(\"[Master] Starting test suite...\");\n    info!(\"\");\n\n    let mut test_num = 0;\n    let mut passed = 0;\n    let mut failed = 0;\n\n    // =========================================================================\n    // WRITE TESTS - Master writes to slave\n    // =========================================================================\n\n    // Test: Write 1 byte\n    test_num += 1;\n    info!(\"--- Test {}: Write 1 byte ---\", test_num);\n    {\n        let write_data = [0x11];\n        info!(\"[Master] Writing {:02X}\", write_data);\n        match i2c.write(I2C_ADDR, &write_data).await {\n            Ok(()) => {\n                info!(\"[Master] Write successful\");\n                passed += 1;\n            }\n            Err(e) => {\n                error!(\"[Master] Write error: {:?}\", e);\n                failed += 1;\n            }\n        }\n    }\n    Timer::after_millis(100).await;\n\n    // Test: Write 4 bytes\n    test_num += 1;\n    info!(\"--- Test {}: Write 4 bytes ---\", test_num);\n    {\n        let write_data = [0x21, 0x22, 0x23, 0x24];\n        info!(\"[Master] Writing {:02X}\", write_data);\n        match i2c.write(I2C_ADDR, &write_data).await {\n            Ok(()) => {\n                info!(\"[Master] Write successful\");\n                passed += 1;\n            }\n            Err(e) => {\n                error!(\"[Master] Write error: {:?}\", e);\n                failed += 1;\n            }\n        }\n    }\n    Timer::after_millis(100).await;\n\n    // Test: Write 8 bytes\n    test_num += 1;\n    info!(\"--- Test {}: Write 8 bytes ---\", test_num);\n    {\n        let write_data = [0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38];\n        info!(\"[Master] Writing {:02X}\", write_data);\n        match i2c.write(I2C_ADDR, &write_data).await {\n            Ok(()) => {\n                info!(\"[Master] Write successful\");\n                passed += 1;\n            }\n            Err(e) => {\n                error!(\"[Master] Write error: {:?}\", e);\n                failed += 1;\n            }\n        }\n    }\n    Timer::after_millis(100).await;\n\n    // =========================================================================\n    // READ TESTS - Master reads from slave\n    // =========================================================================\n\n    // Test: Read 1 byte\n    test_num += 1;\n    info!(\"--- Test {}: Read 1 byte ---\", test_num);\n    {\n        let mut read_buf = [0u8; 1];\n        info!(\"[Master] Reading {} byte(s)\", read_buf.len());\n        match i2c.read(I2C_ADDR, &mut read_buf).await {\n            Ok(()) => {\n                info!(\"[Master] Read result: {:02X}\", read_buf);\n                passed += 1;\n            }\n            Err(e) => {\n                error!(\"[Master] Read error: {:?}\", e);\n                failed += 1;\n            }\n        }\n    }\n    Timer::after_millis(100).await;\n\n    // Test: Read 4 bytes\n    test_num += 1;\n    info!(\"--- Test {}: Read 4 bytes ---\", test_num);\n    {\n        let mut read_buf = [0u8; 4];\n        info!(\"[Master] Reading {} byte(s)\", read_buf.len());\n        match i2c.read(I2C_ADDR, &mut read_buf).await {\n            Ok(()) => {\n                info!(\"[Master] Read result: {:02X}\", read_buf);\n                passed += 1;\n            }\n            Err(e) => {\n                error!(\"[Master] Read error: {:?}\", e);\n                failed += 1;\n            }\n        }\n    }\n    Timer::after_millis(100).await;\n\n    // Test: Read 8 bytes\n    test_num += 1;\n    info!(\"--- Test {}: Read 8 bytes ---\", test_num);\n    {\n        let mut read_buf = [0u8; 8];\n        info!(\"[Master] Reading {} byte(s)\", read_buf.len());\n        match i2c.read(I2C_ADDR, &mut read_buf).await {\n            Ok(()) => {\n                info!(\"[Master] Read result: {:02X}\", read_buf);\n                passed += 1;\n            }\n            Err(e) => {\n                error!(\"[Master] Read error: {:?}\", e);\n                failed += 1;\n            }\n        }\n    }\n    Timer::after_millis(100).await;\n\n    // =========================================================================\n    // SEPARATE WRITE + READ TESTS (STOP between transactions)\n    // =========================================================================\n\n    // Test: Separate write (2 bytes) + read (4 bytes)\n    test_num += 1;\n    info!(\"--- Test {}: Separate write (2 bytes) + read (4 bytes) ---\", test_num);\n    {\n        let write_data = [0x41, 0x42];\n        info!(\"[Master] Writing {:02X}\", write_data);\n        if let Err(e) = i2c.write(I2C_ADDR, &write_data).await {\n            error!(\"[Master] Write error: {:?}\", e);\n            failed += 1;\n        } else {\n            Timer::after_millis(50).await;\n\n            let mut read_buf = [0u8; 4];\n            info!(\"[Master] Reading {} byte(s)\", read_buf.len());\n            match i2c.read(I2C_ADDR, &mut read_buf).await {\n                Ok(()) => {\n                    info!(\"[Master] Read result: {:02X}\", read_buf);\n                    passed += 1;\n                }\n                Err(e) => {\n                    error!(\"[Master] Read error: {:?}\", e);\n                    failed += 1;\n                }\n            }\n        }\n    }\n    Timer::after_millis(100).await;\n\n    // =========================================================================\n    // COMBINED WRITE_READ TESTS (RESTART condition)\n    // =========================================================================\n\n    // Test: Combined write_read (1 byte write, 4 byte read)\n    test_num += 1;\n    info!(\"--- Test {}: Combined write_read (1 byte write) ---\", test_num);\n    {\n        let write_data = [0x51];\n        let mut read_buf = [0u8; 4];\n        info!(\n            \"[Master] write_read: writing {:02X}, reading {} bytes\",\n            write_data,\n            read_buf.len()\n        );\n        match i2c.write_read(I2C_ADDR, &write_data, &mut read_buf).await {\n            Ok(()) => {\n                info!(\"[Master] Read result: {:02X}\", read_buf);\n                passed += 1;\n            }\n            Err(e) => {\n                error!(\"[Master] write_read error: {:?}\", e);\n                failed += 1;\n            }\n        }\n    }\n    Timer::after_millis(100).await;\n\n    // Test: Combined write_read (2 byte write, 4 byte read)\n    test_num += 1;\n    info!(\"--- Test {}: Combined write_read (2 byte write) ---\", test_num);\n    {\n        let write_data = [0x61, 0x62];\n        let mut read_buf = [0u8; 4];\n        info!(\n            \"[Master] write_read: writing {:02X}, reading {} bytes\",\n            write_data,\n            read_buf.len()\n        );\n        match i2c.write_read(I2C_ADDR, &write_data, &mut read_buf).await {\n            Ok(()) => {\n                info!(\"[Master] Read result: {:02X}\", read_buf);\n                passed += 1;\n            }\n            Err(e) => {\n                error!(\"[Master] write_read error: {:?}\", e);\n                failed += 1;\n            }\n        }\n    }\n    Timer::after_millis(100).await;\n\n    // Test: Combined write_read (4 byte write, 4 byte read)\n    test_num += 1;\n    info!(\"--- Test {}: Combined write_read (4 byte write) ---\", test_num);\n    {\n        let write_data = [0x71, 0x72, 0x73, 0x74];\n        let mut read_buf = [0u8; 4];\n        info!(\n            \"[Master] write_read: writing {:02X}, reading {} bytes\",\n            write_data,\n            read_buf.len()\n        );\n        match i2c.write_read(I2C_ADDR, &write_data, &mut read_buf).await {\n            Ok(()) => {\n                info!(\"[Master] Read result: {:02X}\", read_buf);\n                passed += 1;\n            }\n            Err(e) => {\n                error!(\"[Master] write_read error: {:?}\", e);\n                failed += 1;\n            }\n        }\n    }\n    Timer::after_millis(100).await;\n\n    // =========================================================================\n    // TEST SUMMARY\n    // =========================================================================\n\n    info!(\"\");\n    info!(\"==============================================\");\n    info!(\"Test Summary: {} passed, {} failed\", passed, failed);\n    info!(\"==============================================\");\n\n    // Keep running\n    loop {\n        Timer::after_millis(1000).await;\n    }\n}\n"
  },
  {
    "path": "examples/stm32f469/.cargo/config.toml",
    "content": "[target.'cfg(all(target_arch = \"arm\", target_os = \"none\"))']\n# replace STM32F429ZITx with your chip as listed in `probe-rs chip list`\nrunner = \"probe-rs run --chip STM32F469NIHx\"\n\n[build]\ntarget = \"thumbv7em-none-eabi\"\n\n[env]\nDEFMT_LOG = \"trace\"\n"
  },
  {
    "path": "examples/stm32f469/Cargo.toml",
    "content": "[package]\nedition = \"2024\"\nname = \"embassy-stm32f469-examples\"\nversion = \"0.1.0\"\nlicense = \"MIT OR Apache-2.0\"\npublish = false\n\n[dependencies]\n# Specific examples only for stm32f469\nembassy-stm32 = { version = \"0.6.0\", path = \"../../embassy-stm32\", features = [\"defmt\", \"stm32f469ni\", \"unstable-pac\", \"memory-x\", \"time-driver-any\", \"exti\", \"chrono\"] }\nembassy-executor = { version = \"0.10.0\", path = \"../../embassy-executor\", features = [\"platform-cortex-m\", \"executor-thread\", \"executor-interrupt\", \"defmt\"] }\nembassy-time = { version = \"0.5.1\", path = \"../../embassy-time\", features = [\"defmt\", \"defmt-timestamp-uptime\", \"tick-hz-32_768\"] }\n\ndefmt = \"1.0.1\"\ndefmt-rtt = \"1.0.0\"\n\ncortex-m = { version = \"0.7.6\", features = [\"inline-asm\", \"critical-section-single-core\"] }\ncortex-m-rt = \"0.7.0\"\nembedded-hal = \"1.0.0\"\npanic-probe = { version = \"1.0.0\", features = [\"print-defmt\"] }\n\n[profile.release]\ndebug = 2\n\n[package.metadata.embassy]\nbuild = [\n  { target = \"thumbv7em-none-eabi\", artifact-dir = \"out/examples/stm32f469\" }\n]\n"
  },
  {
    "path": "examples/stm32f469/build.rs",
    "content": "fn main() {\n    println!(\"cargo:rustc-link-arg-bins=--nmagic\");\n    println!(\"cargo:rustc-link-arg-bins=-Tlink.x\");\n    println!(\"cargo:rustc-link-arg-bins=-Tdefmt.x\");\n}\n"
  },
  {
    "path": "examples/stm32f469/src/bin/dsi_bsp.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_stm32::dsihost::{DsiHost, PacketType};\nuse embassy_stm32::gpio::{Level, Output, Speed};\nuse embassy_stm32::ltdc::Ltdc;\nuse embassy_stm32::pac::dsihost::regs::{Ier0, Ier1};\nuse embassy_stm32::pac::ltdc::vals::{Bf1, Bf2, Depol, Hspol, Imr, Pcpol, Pf, Vspol};\nuse embassy_stm32::pac::{DSIHOST, LTDC};\nuse embassy_stm32::rcc::{\n    AHBPrescaler, APBPrescaler, Hse, HseMode, Pll, PllMul, PllPDiv, PllPreDiv, PllQDiv, PllRDiv, PllSource, Sysclk,\n};\nuse embassy_stm32::time::mhz;\nuse embassy_time::{Duration, Timer, block_for};\nuse {defmt_rtt as _, panic_probe as _};\n\nenum _Orientation {\n    Landscape,\n    Portrait,\n}\n\nconst _LCD_ORIENTATION: _Orientation = _Orientation::Landscape;\nconst LCD_X_SIZE: u16 = 800;\nconst LCD_Y_SIZE: u16 = 480;\n\nstatic FERRIS_IMAGE: &[u8; 1536000] = include_bytes!(\"ferris.bin\");\n\n// This example allows to display an image on the STM32F469NI-DISCO boards\n// with the Revision C, that is at least the boards marked DK32F469I$AU1.\n// These boards have the NT35510 display driver. This example does not work\n// for the older revisions with  OTM8009A, though there are lots of C-examples\n// available online where the correct config for the OTM8009A could be gotten from.\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let mut config = embassy_stm32::Config::default();\n    config.rcc.sys = Sysclk::PLL1_P;\n    config.rcc.ahb_pre = AHBPrescaler::DIV1;\n    config.rcc.apb1_pre = APBPrescaler::DIV4;\n    config.rcc.apb2_pre = APBPrescaler::DIV2;\n\n    // HSE is on and ready\n    config.rcc.hse = Some(Hse {\n        freq: mhz(8),\n        mode: HseMode::Oscillator,\n    });\n    config.rcc.pll_src = PllSource::HSE;\n\n    config.rcc.pll = Some(Pll {\n        prediv: PllPreDiv::DIV8, // PLLM\n        mul: PllMul::MUL360,     // PLLN\n        divp: Some(PllPDiv::DIV2),\n        divq: Some(PllQDiv::DIV7), // was DIV4, but STM BSP example uses 7\n        divr: Some(PllRDiv::DIV6),\n    });\n\n    // This seems to be working, the values in the RCC.PLLSAICFGR are correct according to the debugger. Also on and ready according to CR\n    config.rcc.pllsai = Some(Pll {\n        prediv: PllPreDiv::DIV8,   // Actually ignored\n        mul: PllMul::MUL384,       // PLLN\n        divp: None,                // PLLP\n        divq: None,                // PLLQ\n        divr: Some(PllRDiv::DIV7), // PLLR (Sai actually has special clockdiv register)\n    });\n\n    let p = embassy_stm32::init(config);\n    info!(\"Starting...\");\n\n    let mut led = Output::new(p.PG6, Level::High, Speed::Low);\n\n    // According to UM for the discovery kit, PH7 is an active-low reset for the LCD and touchsensor\n    let mut reset = Output::new(p.PH7, Level::Low, Speed::High);\n\n    // CubeMX example waits 20 ms before de-asserting reset\n    embassy_time::block_for(embassy_time::Duration::from_millis(20));\n\n    // Disable the reset signal and wait 140ms as in the Linux driver (CubeMX waits only 20)\n    reset.set_high();\n    embassy_time::block_for(embassy_time::Duration::from_millis(140));\n\n    let mut ltdc = Ltdc::new(p.LTDC);\n    let mut dsi = DsiHost::new(p.DSIHOST, p.PJ2);\n    let version = dsi.get_version();\n    defmt::warn!(\"en: {:x}\", version);\n\n    // Disable the DSI wrapper\n    dsi.disable_wrapper_dsi();\n\n    // Disable the DSI host\n    dsi.disable();\n\n    // D-PHY clock and digital disable\n    DSIHOST.pctlr().modify(|w| {\n        w.set_cke(false);\n        w.set_den(false)\n    });\n\n    // Turn off the DSI PLL\n    DSIHOST.wrpcr().modify(|w| w.set_pllen(false));\n\n    // Disable the regulator\n    DSIHOST.wrpcr().write(|w| w.set_regen(false));\n\n    // Enable regulator\n    info!(\"DSIHOST: enabling regulator\");\n    DSIHOST.wrpcr().write(|w| w.set_regen(true));\n\n    for _ in 1..1000 {\n        // The regulator status (ready or not) can be monitored with the RRS flag in the DSI_WISR register.\n        // Once it is set, we stop waiting.\n        if DSIHOST.wisr().read().rrs() {\n            info!(\"DSIHOST Regulator ready\");\n            break;\n        }\n        embassy_time::block_for(embassy_time::Duration::from_millis(1));\n    }\n\n    if !DSIHOST.wisr().read().rrs() {\n        defmt::panic!(\"DSIHOST: enabling regulator FAILED\");\n    }\n\n    // Set up PLL and enable it\n    DSIHOST.wrpcr().modify(|w| {\n        w.set_pllen(true);\n        w.set_ndiv(125); // PLL loop division factor set to 125\n        w.set_idf(2); // PLL input divided by 2\n        w.set_odf(0); // PLL output divided by 1\n    });\n\n    /* 500 MHz / 8 = 62.5 MHz = 62500 kHz */\n    const LANE_BYTE_CLK_K_HZ: u16 = 62500; // https://github.com/STMicroelectronics/32f469idiscovery-bsp/blob/ec051de2bff3e1b73a9ccd49c9b85abf7320add9/stm32469i_discovery_lcd.c#L224C21-L224C26\n\n    const _LCD_CLOCK: u16 = 27429; // https://github.com/STMicroelectronics/32f469idiscovery-bsp/blob/ec051de2bff3e1b73a9ccd49c9b85abf7320add9/stm32469i_discovery_lcd.c#L183\n\n    /* TX_ESCAPE_CKDIV = f(LaneByteClk)/15.62 = 4 */\n    const TX_ESCAPE_CKDIV: u8 = (LANE_BYTE_CLK_K_HZ / 15620) as u8; // https://github.com/STMicroelectronics/32f469idiscovery-bsp/blob/ec051de2bff3e1b73a9ccd49c9b85abf7320add9/stm32469i_discovery_lcd.c#L230\n\n    for _ in 1..1000 {\n        embassy_time::block_for(embassy_time::Duration::from_millis(1));\n        // The PLL status (lock or unlock) can be monitored with the PLLLS flag in the DSI_WISR register.\n        // Once it is set, we stop waiting.\n        if DSIHOST.wisr().read().pllls() {\n            info!(\"DSIHOST PLL locked\");\n            break;\n        }\n    }\n\n    if !DSIHOST.wisr().read().pllls() {\n        defmt::panic!(\"DSIHOST: enabling PLL FAILED\");\n    }\n\n    // Set the PHY parameters\n\n    // D-PHY clock and digital enable\n    DSIHOST.pctlr().write(|w| {\n        w.set_cke(true);\n        w.set_den(true);\n    });\n\n    // Set Clock lane to high-speed mode and disable automatic clock lane control\n    DSIHOST.clcr().modify(|w| {\n        w.set_dpcc(true);\n        w.set_acr(false);\n    });\n\n    // Set number of active data lanes to two (lanes 0 and 1)\n    DSIHOST.pconfr().modify(|w| w.set_nl(1));\n\n    // Set the DSI clock parameters\n\n    // Set the TX escape clock division factor to 4\n    DSIHOST.ccr().modify(|w| w.set_txeckdiv(TX_ESCAPE_CKDIV));\n\n    // Calculate the bit period in high-speed mode in unit of 0.25 ns (UIX4)\n    // The equation is : UIX4 = IntegerPart( (1000/F_PHY_Mhz) * 4 )\n    // Where : F_PHY_Mhz = (NDIV * HSE_Mhz) / (IDF * ODF)\n    // Set the bit period in high-speed mode\n    DSIHOST.wpcr0().modify(|w| w.set_uix4(8)); // 8 is set in the BSP example (confirmed with Debugger)\n\n    // Disable all error interrupts and reset the Error Mask\n    DSIHOST.ier0().write_value(Ier0(0));\n    DSIHOST.ier1().write_value(Ier1(0));\n\n    // Enable this to fix read timeout\n    DSIHOST.pcr().modify(|w| w.set_btae(true));\n\n    const DSI_PIXEL_FORMAT_RGB888: u8 = 0x05;\n    const _DSI_PIXEL_FORMAT_ARGB888: u8 = 0x00;\n\n    const HACT: u16 = LCD_X_SIZE;\n    const VACT: u16 = LCD_Y_SIZE;\n\n    const VSA: u16 = 120;\n    const VBP: u16 = 150;\n    const VFP: u16 = 150;\n    const HSA: u16 = 2;\n    const HBP: u16 = 34;\n    const HFP: u16 = 34;\n\n    const VIRTUAL_CHANNEL_ID: u8 = 0;\n\n    const COLOR_CODING: u8 = DSI_PIXEL_FORMAT_RGB888;\n    const VS_POLARITY: bool = false; // DSI_VSYNC_ACTIVE_HIGH == 0\n    const HS_POLARITY: bool = false; // DSI_HSYNC_ACTIVE_HIGH == 0\n    const DE_POLARITY: bool = false; // DSI_DATA_ENABLE_ACTIVE_HIGH == 0\n    const MODE: u8 = 2; // DSI_VID_MODE_BURST; /* Mode Video burst ie : one LgP per line */\n    const NULL_PACKET_SIZE: u16 = 0xFFF;\n    const NUMBER_OF_CHUNKS: u16 = 0;\n    const PACKET_SIZE: u16 = HACT; /* Value depending on display orientation choice portrait/landscape */\n    const HORIZONTAL_SYNC_ACTIVE: u16 = 4; // ((HSA as u32 * LANE_BYTE_CLK_K_HZ as u32 ) / LCD_CLOCK as u32 ) as u16;\n    const HORIZONTAL_BACK_PORCH: u16 = 77; //((HBP as u32  * LANE_BYTE_CLK_K_HZ as u32 ) / LCD_CLOCK as u32) as u16;\n    const HORIZONTAL_LINE: u16 = 1982; //(((HACT + HSA + HBP + HFP) as u32  * LANE_BYTE_CLK_K_HZ as u32 ) / LCD_CLOCK as u32 ) as u16; /* Value depending on display orientation choice portrait/landscape */\n    // FIXME: Make depend on orientation\n    const VERTICAL_SYNC_ACTIVE: u16 = VSA;\n    const VERTICAL_BACK_PORCH: u16 = VBP;\n    const VERTICAL_FRONT_PORCH: u16 = VFP;\n    const VERTICAL_ACTIVE: u16 = VACT;\n    const LP_COMMAND_ENABLE: bool = true; /* Enable sending commands in mode LP (Low Power) */\n\n    /* Largest packet size possible to transmit in LP mode in VSA, VBP, VFP regions */\n    /* Only useful when sending LP packets is allowed while streaming is active in video mode */\n    const LP_LARGEST_PACKET_SIZE: u8 = 16;\n\n    /* Largest packet size possible to transmit in LP mode in HFP region during VACT period */\n    /* Only useful when sending LP packets is allowed while streaming is active in video mode */\n    const LPVACT_LARGEST_PACKET_SIZE: u8 = 0;\n\n    const LPHORIZONTAL_FRONT_PORCH_ENABLE: bool = true; /* Allow sending LP commands during HFP period */\n    const LPHORIZONTAL_BACK_PORCH_ENABLE: bool = true; /* Allow sending LP commands during HBP period */\n    const LPVERTICAL_ACTIVE_ENABLE: bool = true; /* Allow sending LP commands during VACT period */\n    const LPVERTICAL_FRONT_PORCH_ENABLE: bool = true; /* Allow sending LP commands during VFP period */\n    const LPVERTICAL_BACK_PORCH_ENABLE: bool = true; /* Allow sending LP commands during VBP period */\n    const LPVERTICAL_SYNC_ACTIVE_ENABLE: bool = true; /* Allow sending LP commands during VSync = VSA period */\n    const FRAME_BTAACKNOWLEDGE_ENABLE: bool = false; /* Frame bus-turn-around acknowledge enable => false according to debugger */\n\n    /* Select video mode by resetting CMDM and DSIM bits */\n    DSIHOST.mcr().modify(|w| w.set_cmdm(false));\n    DSIHOST.wcfgr().modify(|w| w.set_dsim(false));\n\n    /* Configure the video mode transmission type */\n    DSIHOST.vmcr().modify(|w| w.set_vmt(MODE));\n\n    /* Configure the video packet size */\n    DSIHOST.vpcr().modify(|w| w.set_vpsize(PACKET_SIZE));\n\n    /* Set the chunks number to be transmitted through the DSI link */\n    DSIHOST.vccr().modify(|w| w.set_numc(NUMBER_OF_CHUNKS));\n\n    /* Set the size of the null packet */\n    DSIHOST.vnpcr().modify(|w| w.set_npsize(NULL_PACKET_SIZE));\n\n    /* Select the virtual channel for the LTDC interface traffic */\n    DSIHOST.lvcidr().modify(|w| w.set_vcid(VIRTUAL_CHANNEL_ID));\n\n    /* Configure the polarity of control signals */\n    DSIHOST.lpcr().modify(|w| {\n        w.set_dep(DE_POLARITY);\n        w.set_hsp(HS_POLARITY);\n        w.set_vsp(VS_POLARITY);\n    });\n\n    /* Select the color coding for the host */\n    DSIHOST.lcolcr().modify(|w| w.set_colc(COLOR_CODING));\n\n    /* Select the color coding for the wrapper */\n    DSIHOST.wcfgr().modify(|w| w.set_colmux(COLOR_CODING));\n\n    /* Set the Horizontal Synchronization Active (HSA) in lane byte clock cycles */\n    DSIHOST.vhsacr().modify(|w| w.set_hsa(HORIZONTAL_SYNC_ACTIVE));\n\n    /* Set the Horizontal Back Porch (HBP) in lane byte clock cycles */\n    DSIHOST.vhbpcr().modify(|w| w.set_hbp(HORIZONTAL_BACK_PORCH));\n\n    /* Set the total line time (HLINE=HSA+HBP+HACT+HFP) in lane byte clock cycles */\n    DSIHOST.vlcr().modify(|w| w.set_hline(HORIZONTAL_LINE));\n\n    /* Set the Vertical Synchronization Active (VSA) */\n    DSIHOST.vvsacr().modify(|w| w.set_vsa(VERTICAL_SYNC_ACTIVE));\n\n    /* Set the Vertical Back Porch (VBP)*/\n    DSIHOST.vvbpcr().modify(|w| w.set_vbp(VERTICAL_BACK_PORCH));\n\n    /* Set the Vertical Front Porch (VFP)*/\n    DSIHOST.vvfpcr().modify(|w| w.set_vfp(VERTICAL_FRONT_PORCH));\n\n    /* Set the Vertical Active period*/\n    DSIHOST.vvacr().modify(|w| w.set_va(VERTICAL_ACTIVE));\n\n    /* Configure the command transmission mode */\n    DSIHOST.vmcr().modify(|w| w.set_lpce(LP_COMMAND_ENABLE));\n\n    /* Low power largest packet size */\n    DSIHOST.lpmcr().modify(|w| w.set_lpsize(LP_LARGEST_PACKET_SIZE));\n\n    /* Low power VACT largest packet size */\n    DSIHOST.lpmcr().modify(|w| w.set_lpsize(LP_LARGEST_PACKET_SIZE));\n    DSIHOST.lpmcr().modify(|w| w.set_vlpsize(LPVACT_LARGEST_PACKET_SIZE));\n\n    /* Enable LP transition in HFP period */\n    DSIHOST.vmcr().modify(|w| w.set_lphfpe(LPHORIZONTAL_FRONT_PORCH_ENABLE));\n\n    /* Enable LP transition in HBP period */\n    DSIHOST.vmcr().modify(|w| w.set_lphbpe(LPHORIZONTAL_BACK_PORCH_ENABLE));\n\n    /* Enable LP transition in VACT period */\n    DSIHOST.vmcr().modify(|w| w.set_lpvae(LPVERTICAL_ACTIVE_ENABLE));\n\n    /* Enable LP transition in VFP period */\n    DSIHOST.vmcr().modify(|w| w.set_lpvfpe(LPVERTICAL_FRONT_PORCH_ENABLE));\n\n    /* Enable LP transition in VBP period */\n    DSIHOST.vmcr().modify(|w| w.set_lpvbpe(LPVERTICAL_BACK_PORCH_ENABLE));\n\n    /* Enable LP transition in vertical sync period */\n    DSIHOST.vmcr().modify(|w| w.set_lpvsae(LPVERTICAL_SYNC_ACTIVE_ENABLE));\n\n    /* Enable the request for an acknowledge response at the end of a frame */\n    DSIHOST.vmcr().modify(|w| w.set_fbtaae(FRAME_BTAACKNOWLEDGE_ENABLE));\n\n    /* Configure DSI PHY HS2LP and LP2HS timings */\n    const CLOCK_LANE_HS2_LPTIME: u16 = 35;\n    const CLOCK_LANE_LP2_HSTIME: u16 = 35;\n    const DATA_LANE_HS2_LPTIME: u8 = 35;\n    const DATA_LANE_LP2_HSTIME: u8 = 35;\n    const DATA_LANE_MAX_READ_TIME: u16 = 0;\n    const STOP_WAIT_TIME: u8 = 10;\n\n    const MAX_TIME: u16 = if CLOCK_LANE_HS2_LPTIME > CLOCK_LANE_LP2_HSTIME {\n        CLOCK_LANE_HS2_LPTIME\n    } else {\n        CLOCK_LANE_LP2_HSTIME\n    };\n\n    /* Clock lane timer configuration */\n\n    /* In Automatic Clock Lane control mode, the DSI Host can turn off the clock lane between two\n     High-Speed transmission.\n     To do so, the DSI Host calculates the time required for the clock lane to change from HighSpeed\n     to Low-Power and from Low-Power to High-Speed.\n     This timings are configured by the HS2LP_TIME and LP2HS_TIME in the DSI Host Clock Lane Timer Configuration\n     Register (DSI_CLTCR).\n     But the DSI Host is not calculating LP2HS_TIME + HS2LP_TIME but 2 x HS2LP_TIME.\n\n     Workaround : Configure HS2LP_TIME and LP2HS_TIME with the same value being the max of HS2LP_TIME or LP2HS_TIME.\n    */\n\n    DSIHOST.cltcr().modify(|w| {\n        w.set_hs2lp_time(MAX_TIME);\n        w.set_lp2hs_time(MAX_TIME)\n    });\n\n    // Data lane timer configuration\n    DSIHOST.dltcr().modify(|w| {\n        w.set_hs2lp_time(DATA_LANE_HS2_LPTIME);\n        w.set_lp2hs_time(DATA_LANE_LP2_HSTIME);\n        w.set_mrd_time(DATA_LANE_MAX_READ_TIME);\n    });\n\n    // Configure the wait period to request HS transmission after a stop state\n    DSIHOST.pconfr().modify(|w| w.set_sw_time(STOP_WAIT_TIME));\n\n    const _PCPOLARITY: bool = false; // LTDC_PCPOLARITY_IPC == 0\n\n    const LTDC_DE_POLARITY: Depol = if !DE_POLARITY {\n        Depol::ACTIVE_LOW\n    } else {\n        Depol::ACTIVE_HIGH\n    };\n    const LTDC_VS_POLARITY: Vspol = if !VS_POLARITY {\n        Vspol::ACTIVE_HIGH\n    } else {\n        Vspol::ACTIVE_LOW\n    };\n\n    const LTDC_HS_POLARITY: Hspol = if !HS_POLARITY {\n        Hspol::ACTIVE_HIGH\n    } else {\n        Hspol::ACTIVE_LOW\n    };\n\n    /* Timing Configuration */\n    const HORIZONTAL_SYNC: u16 = HSA - 1;\n    const VERTICAL_SYNC: u16 = VERTICAL_SYNC_ACTIVE - 1;\n    const ACCUMULATED_HBP: u16 = HSA + HBP - 1;\n    const ACCUMULATED_VBP: u16 = VERTICAL_SYNC_ACTIVE + VERTICAL_BACK_PORCH - 1;\n    const ACCUMULATED_ACTIVE_W: u16 = LCD_X_SIZE + HSA + HBP - 1;\n    const ACCUMULATED_ACTIVE_H: u16 = VERTICAL_SYNC_ACTIVE + VERTICAL_BACK_PORCH + VERTICAL_ACTIVE - 1;\n    const TOTAL_WIDTH: u16 = LCD_X_SIZE + HSA + HBP + HFP - 1;\n    const TOTAL_HEIGHT: u16 = VERTICAL_SYNC_ACTIVE + VERTICAL_BACK_PORCH + VERTICAL_ACTIVE + VERTICAL_FRONT_PORCH - 1;\n\n    // DISABLE LTDC before making changes\n    ltdc.disable();\n\n    // Configure the HS, VS, DE and PC polarity\n    LTDC.gcr().modify(|w| {\n        w.set_hspol(LTDC_HS_POLARITY);\n        w.set_vspol(LTDC_VS_POLARITY);\n        w.set_depol(LTDC_DE_POLARITY);\n        w.set_pcpol(Pcpol::RISING_EDGE);\n    });\n\n    // Set Synchronization size\n    LTDC.sscr().modify(|w| {\n        w.set_hsw(HORIZONTAL_SYNC);\n        w.set_vsh(VERTICAL_SYNC)\n    });\n\n    // Set Accumulated Back porch\n    LTDC.bpcr().modify(|w| {\n        w.set_ahbp(ACCUMULATED_HBP);\n        w.set_avbp(ACCUMULATED_VBP);\n    });\n\n    // Set Accumulated Active Width\n    LTDC.awcr().modify(|w| {\n        w.set_aah(ACCUMULATED_ACTIVE_H);\n        w.set_aaw(ACCUMULATED_ACTIVE_W);\n    });\n\n    // Set Total Width\n    LTDC.twcr().modify(|w| {\n        w.set_totalh(TOTAL_HEIGHT);\n        w.set_totalw(TOTAL_WIDTH);\n    });\n\n    // Set the background color value\n    LTDC.bccr().modify(|w| {\n        w.set_bcred(0);\n        w.set_bcgreen(0);\n        w.set_bcblue(0)\n    });\n\n    // Enable the Transfer Error and FIFO underrun interrupts\n    LTDC.ier().modify(|w| {\n        w.set_terrie(true);\n        w.set_fuie(true);\n    });\n\n    // ENABLE LTDC after making changes\n    ltdc.enable();\n\n    dsi.enable();\n    dsi.enable_wrapper_dsi();\n\n    // First, delay 120 ms (reason unknown, STM32 Cube Example does it)\n    block_for(Duration::from_millis(120));\n\n    // 1 to 26\n    dsi.write_cmd(0, NT35510_WRITES_0[0], &NT35510_WRITES_0[1..]).unwrap();\n    dsi.write_cmd(0, NT35510_WRITES_1[0], &NT35510_WRITES_1[1..]).unwrap();\n    dsi.write_cmd(0, NT35510_WRITES_2[0], &NT35510_WRITES_2[1..]).unwrap();\n    dsi.write_cmd(0, NT35510_WRITES_3[0], &NT35510_WRITES_3[1..]).unwrap();\n    dsi.write_cmd(0, NT35510_WRITES_4[0], &NT35510_WRITES_4[1..]).unwrap();\n    dsi.write_cmd(0, NT35510_WRITES_5[0], &NT35510_WRITES_5[1..]).unwrap();\n    dsi.write_cmd(0, NT35510_WRITES_6[0], &NT35510_WRITES_6[1..]).unwrap();\n    dsi.write_cmd(0, NT35510_WRITES_7[0], &NT35510_WRITES_7[1..]).unwrap();\n    dsi.write_cmd(0, NT35510_WRITES_8[0], &NT35510_WRITES_8[1..]).unwrap();\n    dsi.write_cmd(0, NT35510_WRITES_9[0], &NT35510_WRITES_9[1..]).unwrap();\n    dsi.write_cmd(0, NT35510_WRITES_10[0], &NT35510_WRITES_10[1..]).unwrap();\n    // 11 missing\n    dsi.write_cmd(0, NT35510_WRITES_12[0], &NT35510_WRITES_12[1..]).unwrap();\n    dsi.write_cmd(0, NT35510_WRITES_13[0], &NT35510_WRITES_13[1..]).unwrap();\n    dsi.write_cmd(0, NT35510_WRITES_14[0], &NT35510_WRITES_14[1..]).unwrap();\n    dsi.write_cmd(0, NT35510_WRITES_15[0], &NT35510_WRITES_15[1..]).unwrap();\n    dsi.write_cmd(0, NT35510_WRITES_16[0], &NT35510_WRITES_16[1..]).unwrap();\n    dsi.write_cmd(0, NT35510_WRITES_17[0], &NT35510_WRITES_17[1..]).unwrap();\n    dsi.write_cmd(0, NT35510_WRITES_18[0], &NT35510_WRITES_18[1..]).unwrap();\n    dsi.write_cmd(0, NT35510_WRITES_19[0], &NT35510_WRITES_19[1..]).unwrap();\n    dsi.write_cmd(0, NT35510_WRITES_20[0], &NT35510_WRITES_20[1..]).unwrap();\n    dsi.write_cmd(0, NT35510_WRITES_21[0], &NT35510_WRITES_21[1..]).unwrap();\n    dsi.write_cmd(0, NT35510_WRITES_22[0], &NT35510_WRITES_22[1..]).unwrap();\n    dsi.write_cmd(0, NT35510_WRITES_23[0], &NT35510_WRITES_23[1..]).unwrap();\n    dsi.write_cmd(0, NT35510_WRITES_24[0], &NT35510_WRITES_24[1..]).unwrap();\n\n    // Tear on\n    dsi.write_cmd(0, NT35510_WRITES_26[0], &NT35510_WRITES_26[1..]).unwrap();\n\n    // Set Pixel color format to RGB888\n    dsi.write_cmd(0, NT35510_WRITES_37[0], &NT35510_WRITES_37[1..]).unwrap();\n\n    // Add a delay, otherwise MADCTL not taken\n    block_for(Duration::from_millis(200));\n\n    // Configure orientation as landscape\n    dsi.write_cmd(0, NT35510_MADCTL_LANDSCAPE[0], &NT35510_MADCTL_LANDSCAPE[1..])\n        .unwrap();\n    dsi.write_cmd(0, NT35510_CASET_LANDSCAPE[0], &NT35510_CASET_LANDSCAPE[1..])\n        .unwrap();\n    dsi.write_cmd(0, NT35510_RASET_LANDSCAPE[0], &NT35510_RASET_LANDSCAPE[1..])\n        .unwrap();\n\n    // Sleep out\n    dsi.write_cmd(0, NT35510_WRITES_27[0], &NT35510_WRITES_27[1..]).unwrap();\n\n    // Wait for sleep out exit\n    block_for(Duration::from_millis(120));\n\n    // Configure COLOR_CODING\n    dsi.write_cmd(0, NT35510_WRITES_37[0], &NT35510_WRITES_37[1..]).unwrap();\n\n    /* CABC : Content Adaptive Backlight Control section start >> */\n    /* Note : defaut is 0 (lowest Brightness), 0xFF is highest Brightness, try 0x7F : intermediate value */\n    dsi.write_cmd(0, NT35510_WRITES_31[0], &NT35510_WRITES_31[1..]).unwrap();\n    /* defaut is 0, try 0x2C - Brightness Control Block, Display Dimming & BackLight on */\n    dsi.write_cmd(0, NT35510_WRITES_32[0], &NT35510_WRITES_32[1..]).unwrap();\n    /* defaut is 0, try 0x02 - image Content based Adaptive Brightness [Still Picture] */\n    dsi.write_cmd(0, NT35510_WRITES_33[0], &NT35510_WRITES_33[1..]).unwrap();\n    /* defaut is 0 (lowest Brightness), 0xFF is highest Brightness */\n    dsi.write_cmd(0, NT35510_WRITES_34[0], &NT35510_WRITES_34[1..]).unwrap();\n    /* CABC : Content Adaptive Backlight Control section end << */\n    /* Display on */\n    dsi.write_cmd(0, NT35510_WRITES_30[0], &NT35510_WRITES_30[1..]).unwrap();\n\n    /* Send Command GRAM memory write (no parameters) : this initiates frame write via other DSI commands sent by */\n    /* DSI host from LTDC incoming pixels in video mode */\n    dsi.write_cmd(0, NT35510_WRITES_35[0], &NT35510_WRITES_35[1..]).unwrap();\n\n    /* Initialize the LCD pixel width and pixel height */\n    const WINDOW_X0: u16 = 0;\n    const WINDOW_X1: u16 = LCD_X_SIZE; // 480 for ferris\n    const WINDOW_Y0: u16 = 0;\n    const WINDOW_Y1: u16 = LCD_Y_SIZE; // 800 for ferris\n    const PIXEL_FORMAT: Pf = Pf::ARGB8888;\n    //const FBStartAdress: u16 = FB_Address;\n    const ALPHA: u8 = 255;\n    const ALPHA0: u8 = 0;\n    const BACKCOLOR_BLUE: u8 = 0;\n    const BACKCOLOR_GREEN: u8 = 0;\n    const BACKCOLOR_RED: u8 = 0;\n    const IMAGE_WIDTH: u16 = LCD_X_SIZE; // 480 for ferris\n    const IMAGE_HEIGHT: u16 = LCD_Y_SIZE; // 800 for ferris\n\n    const PIXEL_SIZE: u8 = match PIXEL_FORMAT {\n        Pf::ARGB8888 => 4,\n        Pf::RGB888 => 3,\n        Pf::ARGB4444 | Pf::RGB565 | Pf::ARGB1555 | Pf::AL88 => 2,\n        _ => 1,\n    };\n\n    // Configure the horizontal start and stop position\n    LTDC.layer(0).whpcr().write(|w| {\n        w.set_whstpos(LTDC.bpcr().read().ahbp() + 1 + WINDOW_X0);\n        w.set_whsppos(LTDC.bpcr().read().ahbp() + WINDOW_X1);\n    });\n\n    // Configures the vertical start and stop position\n    LTDC.layer(0).wvpcr().write(|w| {\n        w.set_wvstpos(LTDC.bpcr().read().avbp() + 1 + WINDOW_Y0);\n        w.set_wvsppos(LTDC.bpcr().read().avbp() + WINDOW_Y1);\n    });\n\n    // Specify the pixel format\n    LTDC.layer(0).pfcr().write(|w| w.set_pf(PIXEL_FORMAT));\n\n    // Configures the default color values as zero\n    LTDC.layer(0).dccr().modify(|w| {\n        w.set_dcblue(BACKCOLOR_BLUE);\n        w.set_dcgreen(BACKCOLOR_GREEN);\n        w.set_dcred(BACKCOLOR_RED);\n        w.set_dcalpha(ALPHA0);\n    });\n\n    // Specifies the constant ALPHA value\n    LTDC.layer(0).cacr().write(|w| w.set_consta(ALPHA));\n\n    // Specifies the blending factors\n    LTDC.layer(0).bfcr().write(|w| {\n        w.set_bf1(Bf1::CONSTANT);\n        w.set_bf2(Bf2::CONSTANT);\n    });\n\n    // Configure the color frame buffer start address\n    let fb_start_address: u32 = &FERRIS_IMAGE[0] as *const _ as u32;\n    info!(\"Setting Framebuffer Start Address: {:010x}\", fb_start_address);\n    LTDC.layer(0).cfbar().write(|w| w.set_cfbadd(fb_start_address));\n\n    // Configures the color frame buffer pitch in byte\n    LTDC.layer(0).cfblr().write(|w| {\n        w.set_cfbp(IMAGE_WIDTH * PIXEL_SIZE as u16);\n        w.set_cfbll(((WINDOW_X1 - WINDOW_X0) * PIXEL_SIZE as u16) + 3);\n    });\n\n    // Configures the frame buffer line number\n    LTDC.layer(0).cfblnr().write(|w| w.set_cfblnbr(IMAGE_HEIGHT));\n\n    // Enable LTDC_Layer by setting LEN bit\n    LTDC.layer(0).cr().modify(|w| w.set_len(true));\n\n    //LTDC->SRCR = LTDC_SRCR_IMR;\n    LTDC.srcr().modify(|w| w.set_imr(Imr::RELOAD));\n\n    block_for(Duration::from_millis(5000));\n\n    const READ_SIZE: u16 = 1;\n    let mut data = [1u8; READ_SIZE as usize];\n    dsi.read(0, PacketType::DcsShortPktRead(0xDA), READ_SIZE, &mut data)\n        .unwrap();\n    info!(\"Display ID1: {:#04x}\", data);\n\n    dsi.read(0, PacketType::DcsShortPktRead(0xDB), READ_SIZE, &mut data)\n        .unwrap();\n    info!(\"Display ID2: {:#04x}\", data);\n\n    dsi.read(0, PacketType::DcsShortPktRead(0xDC), READ_SIZE, &mut data)\n        .unwrap();\n    info!(\"Display ID3: {:#04x}\", data);\n\n    block_for(Duration::from_millis(500));\n\n    info!(\"Config done, start blinking LED\");\n    loop {\n        led.set_high();\n        Timer::after_millis(1000).await;\n\n        // Increase screen brightness\n        dsi.write_cmd(0, NT35510_CMD_WRDISBV, &[0xFF]).unwrap();\n\n        led.set_low();\n        Timer::after_millis(1000).await;\n\n        // Reduce screen brightness\n        dsi.write_cmd(0, NT35510_CMD_WRDISBV, &[0x50]).unwrap();\n    }\n}\n\nconst NT35510_WRITES_0: &[u8] = &[0xF0, 0x55, 0xAA, 0x52, 0x08, 0x01]; // LV2:  Page 1 enable\nconst NT35510_WRITES_1: &[u8] = &[0xB0, 0x03, 0x03, 0x03]; // AVDD: 5.2V\nconst NT35510_WRITES_2: &[u8] = &[0xB6, 0x46, 0x46, 0x46]; // AVDD: Ratio\nconst NT35510_WRITES_3: &[u8] = &[0xB1, 0x03, 0x03, 0x03]; // AVEE: -5.2V\nconst NT35510_WRITES_4: &[u8] = &[0xB7, 0x36, 0x36, 0x36]; // AVEE: Ratio\nconst NT35510_WRITES_5: &[u8] = &[0xB2, 0x00, 0x00, 0x02]; // VCL: -2.5V\nconst NT35510_WRITES_6: &[u8] = &[0xB8, 0x26, 0x26, 0x26]; // VCL: Ratio\nconst NT35510_WRITES_7: &[u8] = &[0xBF, 0x01]; // VGH: 15V (Free Pump)\nconst NT35510_WRITES_8: &[u8] = &[0xB3, 0x09, 0x09, 0x09];\nconst NT35510_WRITES_9: &[u8] = &[0xB9, 0x36, 0x36, 0x36]; // VGH: Ratio\nconst NT35510_WRITES_10: &[u8] = &[0xB5, 0x08, 0x08, 0x08]; // VGL_REG: -10V\nconst NT35510_WRITES_12: &[u8] = &[0xBA, 0x26, 0x26, 0x26]; // VGLX: Ratio\nconst NT35510_WRITES_13: &[u8] = &[0xBC, 0x00, 0x80, 0x00]; // VGMP/VGSP: 4.5V/0V\nconst NT35510_WRITES_14: &[u8] = &[0xBD, 0x00, 0x80, 0x00]; // VGMN/VGSN:-4.5V/0V\nconst NT35510_WRITES_15: &[u8] = &[0xBE, 0x00, 0x50]; // VCOM: -1.325V\nconst NT35510_WRITES_16: &[u8] = &[0xF0, 0x55, 0xAA, 0x52, 0x08, 0x00]; // LV2: Page 0 enable\nconst NT35510_WRITES_17: &[u8] = &[0xB1, 0xFC, 0x00]; // Display control\nconst NT35510_WRITES_18: &[u8] = &[0xB6, 0x03]; // Src hold time\nconst NT35510_WRITES_19: &[u8] = &[0xB5, 0x51];\nconst NT35510_WRITES_20: &[u8] = &[0x00, 0x00, 0xB7]; // Gate EQ control\nconst NT35510_WRITES_21: &[u8] = &[0xB8, 0x01, 0x02, 0x02, 0x02]; // Src EQ control(Mode2)\nconst NT35510_WRITES_22: &[u8] = &[0xBC, 0x00, 0x00, 0x00]; // Inv. mode(2-dot)\nconst NT35510_WRITES_23: &[u8] = &[0xCC, 0x03, 0x00, 0x00];\nconst NT35510_WRITES_24: &[u8] = &[0xBA, 0x01];\n\nconst _NT35510_MADCTL_PORTRAIT: &[u8] = &[NT35510_CMD_MADCTL, 0x00];\nconst _NT35510_CASET_PORTRAIT: &[u8] = &[NT35510_CMD_CASET, 0x00, 0x00, 0x01, 0xDF];\nconst _NT35510_RASET_PORTRAIT: &[u8] = &[NT35510_CMD_RASET, 0x00, 0x00, 0x03, 0x1F];\nconst NT35510_MADCTL_LANDSCAPE: &[u8] = &[NT35510_CMD_MADCTL, 0x60];\nconst NT35510_CASET_LANDSCAPE: &[u8] = &[NT35510_CMD_CASET, 0x00, 0x00, 0x03, 0x1F];\nconst NT35510_RASET_LANDSCAPE: &[u8] = &[NT35510_CMD_RASET, 0x00, 0x00, 0x01, 0xDF];\n\nconst NT35510_WRITES_26: &[u8] = &[NT35510_CMD_TEEON, 0x00]; // Tear on\nconst NT35510_WRITES_27: &[u8] = &[NT35510_CMD_SLPOUT, 0x00]; // Sleep out\n// 28,29 missing\nconst NT35510_WRITES_30: &[u8] = &[NT35510_CMD_DISPON, 0x00]; // Display on\n\nconst NT35510_WRITES_31: &[u8] = &[NT35510_CMD_WRDISBV, 0x7F];\nconst NT35510_WRITES_32: &[u8] = &[NT35510_CMD_WRCTRLD, 0x2C];\nconst NT35510_WRITES_33: &[u8] = &[NT35510_CMD_WRCABC, 0x02];\nconst NT35510_WRITES_34: &[u8] = &[NT35510_CMD_WRCABCMB, 0xFF];\nconst NT35510_WRITES_35: &[u8] = &[NT35510_CMD_RAMWR, 0x00];\n\n//const NT35510_WRITES_36: &[u8] = &[NT35510_CMD_COLMOD, NT35510_COLMOD_RGB565]; // FIXME: Example sets it to 888 but rest of the code seems to configure DSI for 565\nconst NT35510_WRITES_37: &[u8] = &[NT35510_CMD_COLMOD, NT35510_COLMOD_RGB888];\n\n// More of these: https://elixir.bootlin.com/linux/latest/source/include/video/mipi_display.h#L83\nconst _NT35510_CMD_TEEON_GET_DISPLAY_ID: u8 = 0x04;\n\nconst NT35510_CMD_TEEON: u8 = 0x35;\nconst NT35510_CMD_MADCTL: u8 = 0x36;\n\nconst NT35510_CMD_SLPOUT: u8 = 0x11;\nconst NT35510_CMD_DISPON: u8 = 0x29;\nconst NT35510_CMD_CASET: u8 = 0x2A;\nconst NT35510_CMD_RASET: u8 = 0x2B;\nconst NT35510_CMD_RAMWR: u8 = 0x2C; /* Memory write */\nconst NT35510_CMD_COLMOD: u8 = 0x3A;\n\nconst NT35510_CMD_WRDISBV: u8 = 0x51; /* Write display brightness */\nconst _NT35510_CMD_RDDISBV: u8 = 0x52; /* Read display brightness */\nconst NT35510_CMD_WRCTRLD: u8 = 0x53; /* Write CTRL display */\nconst _NT35510_CMD_RDCTRLD: u8 = 0x54; /* Read CTRL display value */\nconst NT35510_CMD_WRCABC: u8 = 0x55; /* Write content adaptative brightness control */\nconst NT35510_CMD_WRCABCMB: u8 = 0x5E; /* Write CABC minimum brightness */\n\nconst _NT35510_COLMOD_RGB565: u8 = 0x55;\nconst NT35510_COLMOD_RGB888: u8 = 0x77;\n"
  },
  {
    "path": "examples/stm32f7/.cargo/config.toml",
    "content": "[target.'cfg(all(target_arch = \"arm\", target_os = \"none\"))']\n# replace STM32F429ZITx with your chip as listed in `probe-rs chip list`\nrunner = \"probe-rs run --chip STM32F777ZITx\"\n\n[build]\ntarget = \"thumbv7em-none-eabihf\"\n\n[env]\nDEFMT_LOG = \"trace\"\n"
  },
  {
    "path": "examples/stm32f7/Cargo.toml",
    "content": "[package]\nedition = \"2024\"\nname = \"embassy-stm32f7-examples\"\nversion = \"0.1.0\"\nlicense = \"MIT OR Apache-2.0\"\npublish = false\n\n[dependencies]\n# Change stm32f777zi to your chip name, if necessary.\nembassy-stm32 = { version = \"0.6.0\", path = \"../../embassy-stm32\", features = [\"defmt\", \"stm32f777zi\", \"memory-x\", \"unstable-pac\", \"time-driver-any\", \"exti\", \"single-bank\"]  }\nembassy-sync = { version = \"0.8.0\", path = \"../../embassy-sync\", features = [\"defmt\"] }\nembassy-executor = { version = \"0.10.0\", path = \"../../embassy-executor\", features = [\"platform-cortex-m\", \"executor-thread\", \"defmt\"] }\nembassy-time = { version = \"0.5.1\", path = \"../../embassy-time\", features = [\"defmt\", \"defmt-timestamp-uptime\", \"tick-hz-32_768\"] }\nembassy-net = { version = \"0.9.0\", path = \"../../embassy-net\", features = [\"defmt\", \"tcp\", \"dhcpv4\", \"medium-ethernet\"] }\nembedded-io-async = { version = \"0.7.0\" }\nembassy-usb = { version = \"0.6.0\", path = \"../../embassy-usb\", features = [\"defmt\"] }\nembassy-futures = { version = \"0.1.0\", path = \"../../embassy-futures\" }\n\ndefmt = \"1.0.1\"\ndefmt-rtt = \"1.0.0\"\n\ncortex-m = { version = \"0.7.6\", features = [\"inline-asm\", \"critical-section-single-core\"] }\ncortex-m-rt = \"0.7.0\"\nembedded-hal = \"0.2.6\"\npanic-probe = { version = \"1.0.0\", features = [\"print-defmt\"] }\nheapless = { version = \"0.9\", default-features = false }\nnb = \"1.0.0\"\ncritical-section = \"1.1\"\nembedded-storage = \"0.3.1\"\nstatic_cell = \"2\"\nsha2 = { version = \"0.10.8\", default-features = false }\nhmac = \"0.12.1\"\naes-gcm = { version = \"0.10.3\", default-features = false, features = [\"aes\", \"heapless\"] }\n\n[profile.release]\ndebug = 2\n\n[package.metadata.embassy]\nbuild = [\n  { target = \"thumbv7em-none-eabi\", artifact-dir = \"out/examples/stm32f7\" }\n]\n"
  },
  {
    "path": "examples/stm32f7/build.rs",
    "content": "fn main() {\n    println!(\"cargo:rustc-link-arg-bins=--nmagic\");\n    println!(\"cargo:rustc-link-arg-bins=-Tlink.x\");\n    println!(\"cargo:rustc-link-arg-bins=-Tdefmt.x\");\n}\n"
  },
  {
    "path": "examples/stm32f7/src/bin/adc.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_stm32::adc::{Adc, SampleTime};\nuse embassy_time::Timer;\nuse {defmt_rtt as _, panic_probe as _};\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let p = embassy_stm32::init(Default::default());\n    info!(\"Hello World!\");\n\n    let mut adc = Adc::new(p.ADC1);\n    let mut pin = p.PA3;\n\n    let mut vrefint = adc.enable_vrefint();\n    let vrefint_sample = adc.blocking_read(&mut vrefint, SampleTime::CYCLES112);\n    let convert_to_millivolts = |sample| {\n        // From http://www.st.com/resource/en/datasheet/DM00273119.pdf\n        // 6.3.27 Reference voltage\n        const VREFINT_MV: u32 = 1210; // mV\n\n        (u32::from(sample) * VREFINT_MV / u32::from(vrefint_sample)) as u16\n    };\n\n    loop {\n        let v = adc.blocking_read(&mut pin, SampleTime::CYCLES112);\n        info!(\"--> {} - {} mV\", v, convert_to_millivolts(v));\n        Timer::after_millis(100).await;\n    }\n}\n"
  },
  {
    "path": "examples/stm32f7/src/bin/blinky.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_stm32::gpio::{Level, Output, Speed};\nuse embassy_time::Timer;\nuse {defmt_rtt as _, panic_probe as _};\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let p = embassy_stm32::init(Default::default());\n    info!(\"Hello World!\");\n\n    let mut led = Output::new(p.PB7, Level::High, Speed::Low);\n\n    loop {\n        info!(\"high\");\n        led.set_high();\n        Timer::after_millis(300).await;\n\n        info!(\"low\");\n        led.set_low();\n        Timer::after_millis(300).await;\n    }\n}\n"
  },
  {
    "path": "examples/stm32f7/src/bin/button.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse cortex_m_rt::entry;\nuse defmt::*;\nuse embassy_stm32::gpio::{Input, Level, Output, Pull, Speed};\nuse {defmt_rtt as _, panic_probe as _};\n\n#[entry]\nfn main() -> ! {\n    info!(\"Hello World!\");\n\n    let p = embassy_stm32::init(Default::default());\n\n    let button = Input::new(p.PC13, Pull::Down);\n    let mut led1 = Output::new(p.PB0, Level::High, Speed::Low);\n    let _led2 = Output::new(p.PB7, Level::High, Speed::Low);\n    let mut led3 = Output::new(p.PB14, Level::High, Speed::Low);\n\n    loop {\n        if button.is_high() {\n            info!(\"high\");\n            led1.set_high();\n            led3.set_low();\n        } else {\n            info!(\"low\");\n            led1.set_low();\n            led3.set_high();\n        }\n    }\n}\n"
  },
  {
    "path": "examples/stm32f7/src/bin/button_exti.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_stm32::exti::{self, ExtiInput};\nuse embassy_stm32::gpio::Pull;\nuse embassy_stm32::{bind_interrupts, interrupt};\nuse {defmt_rtt as _, panic_probe as _};\n\nbind_interrupts!(\n    pub struct Irqs{\n        EXTI15_10 => exti::InterruptHandler<interrupt::typelevel::EXTI15_10>;\n});\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let p = embassy_stm32::init(Default::default());\n    info!(\"Hello World!\");\n\n    let mut button = ExtiInput::new(p.PC13, p.EXTI13, Pull::Down, Irqs);\n\n    info!(\"Press the USER button...\");\n\n    loop {\n        button.wait_for_rising_edge().await;\n        info!(\"Pressed!\");\n        button.wait_for_falling_edge().await;\n        info!(\"Released!\");\n    }\n}\n"
  },
  {
    "path": "examples/stm32f7/src/bin/can.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse core::num::{NonZeroU8, NonZeroU16};\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_stm32::can::filter::Mask32;\nuse embassy_stm32::can::{\n    Can, CanTx, Fifo, Frame, Rx0InterruptHandler, Rx1InterruptHandler, SceInterruptHandler, StandardId,\n    TxInterruptHandler,\n};\nuse embassy_stm32::gpio::{Input, Pull};\nuse embassy_stm32::peripherals::CAN3;\nuse embassy_stm32::{bind_interrupts, can};\nuse static_cell::StaticCell;\nuse {defmt_rtt as _, panic_probe as _};\n\nbind_interrupts!(struct Irqs {\n    CAN3_RX0 => Rx0InterruptHandler<CAN3>;\n    CAN3_RX1 => Rx1InterruptHandler<CAN3>;\n    CAN3_SCE => SceInterruptHandler<CAN3>;\n    CAN3_TX => TxInterruptHandler<CAN3>;\n});\n\n#[embassy_executor::task]\npub async fn send_can_message(tx: &'static mut CanTx<'static>) {\n    loop {\n        let frame = Frame::new_data(unwrap!(StandardId::new(0 as _)), &[0]).unwrap();\n        tx.write(&frame).await;\n        embassy_time::Timer::after_secs(1).await;\n    }\n}\n\n#[embassy_executor::main]\nasync fn main(spawner: Spawner) {\n    info!(\"Hello World!\");\n\n    let mut p = embassy_stm32::init(Default::default());\n\n    // The next two lines are a workaround for testing without transceiver.\n    // To synchronise to the bus the RX input needs to see a high level.\n    // Use `mem::forget()` to release the borrow on the pin but keep the\n    // pull-up resistor enabled.\n    let rx_pin = Input::new(p.PA15.reborrow(), Pull::Up);\n    core::mem::forget(rx_pin);\n\n    static CAN: StaticCell<Can<'static>> = StaticCell::new();\n    let can = CAN.init(Can::new(p.CAN3, p.PA8, p.PA15, Irqs));\n    can.modify_filters().enable_bank(0, Fifo::Fifo0, Mask32::accept_all());\n\n    can.modify_config()\n        .set_bit_timing(can::util::NominalBitTiming {\n            prescaler: NonZeroU16::new(2).unwrap(),\n            seg1: NonZeroU8::new(13).unwrap(),\n            seg2: NonZeroU8::new(2).unwrap(),\n            sync_jump_width: NonZeroU8::new(1).unwrap(),\n        }) // http://www.bittiming.can-wiki.info/\n        .set_loopback(true);\n\n    can.enable().await;\n\n    let (tx, mut rx) = can.split();\n\n    static CAN_TX: StaticCell<CanTx<'static>> = StaticCell::new();\n    let tx = CAN_TX.init(tx);\n    spawner.spawn(send_can_message(tx).unwrap());\n\n    loop {\n        let envelope = rx.read().await.unwrap();\n        println!(\"Received: {:?}\", envelope);\n    }\n}\n"
  },
  {
    "path": "examples/stm32f7/src/bin/cryp.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse aes_gcm::Aes128Gcm;\nuse aes_gcm::aead::heapless::Vec;\nuse aes_gcm::aead::{AeadInPlace, KeyInit};\nuse defmt::info;\nuse embassy_executor::Spawner;\nuse embassy_stm32::cryp::{self, *};\nuse embassy_stm32::{Config, bind_interrupts, dma, peripherals};\nuse embassy_time::Instant;\nuse {defmt_rtt as _, panic_probe as _};\n\nbind_interrupts!(struct Irqs {\n    CRYP => cryp::InterruptHandler<peripherals::CRYP>;\n    DMA2_STREAM6 => dma::InterruptHandler<peripherals::DMA2_CH6>;\n    DMA2_STREAM5 => dma::InterruptHandler<peripherals::DMA2_CH5>;\n});\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) -> ! {\n    let config = Config::default();\n    let p = embassy_stm32::init(config);\n\n    let payload: &[u8] = b\"hello world\";\n    let aad: &[u8] = b\"additional data\";\n\n    let mut hw_cryp = Cryp::new(p.CRYP, p.DMA2_CH6, p.DMA2_CH5, Irqs);\n    let key: [u8; 16] = [0; 16];\n    let mut ciphertext: [u8; 11] = [0; 11];\n    let mut plaintext: [u8; 11] = [0; 11];\n    let iv: [u8; 12] = [0; 12];\n\n    let hw_start_time = Instant::now();\n\n    // Encrypt in hardware using AES-GCM 128-bit\n    let aes_gcm = AesGcm::new(&key, &iv);\n    let mut gcm_encrypt = hw_cryp.start(&aes_gcm, Direction::Encrypt).await;\n    hw_cryp.aad(&mut gcm_encrypt, aad, true).await;\n    hw_cryp.payload(&mut gcm_encrypt, payload, &mut ciphertext, true).await;\n    let encrypt_tag = hw_cryp.finish(gcm_encrypt).await;\n\n    // Decrypt in hardware using AES-GCM 128-bit\n    let mut gcm_decrypt = hw_cryp.start(&aes_gcm, Direction::Decrypt).await;\n    hw_cryp.aad(&mut gcm_decrypt, aad, true).await;\n    hw_cryp\n        .payload(&mut gcm_decrypt, &ciphertext, &mut plaintext, true)\n        .await;\n    let decrypt_tag = hw_cryp.finish(gcm_decrypt).await;\n\n    let hw_end_time = Instant::now();\n    let hw_execution_time = hw_end_time - hw_start_time;\n\n    info!(\"AES-GCM Ciphertext: {:?}\", ciphertext);\n    info!(\"AES-GCM Plaintext: {:?}\", plaintext);\n    assert_eq!(payload, plaintext);\n    assert_eq!(encrypt_tag, decrypt_tag);\n\n    let sw_start_time = Instant::now();\n\n    // Encrypt in software using AES-GCM 128-bit\n    let mut payload_vec: Vec<u8, 32> = Vec::from_slice(&payload).unwrap();\n    let cipher = Aes128Gcm::new(&key.into());\n    let _ = cipher.encrypt_in_place(&iv.into(), aad.into(), &mut payload_vec);\n\n    assert_eq!(ciphertext, payload_vec[0..ciphertext.len()]);\n    assert_eq!(\n        encrypt_tag,\n        payload_vec[ciphertext.len()..ciphertext.len() + encrypt_tag.len()]\n    );\n\n    // Decrypt in software using AES-GCM 128-bit\n    cipher\n        .decrypt_in_place(&iv.into(), aad.into(), &mut payload_vec)\n        .unwrap();\n\n    let sw_end_time = Instant::now();\n    let sw_execution_time = sw_end_time - sw_start_time;\n\n    info!(\"Hardware Execution Time: {:?}\", hw_execution_time);\n    info!(\"Software Execution Time: {:?}\", sw_execution_time);\n\n    loop {}\n}\n"
  },
  {
    "path": "examples/stm32f7/src/bin/eth.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_net::tcp::TcpSocket;\nuse embassy_net::{Ipv4Address, StackResources};\nuse embassy_stm32::eth::{Ethernet, GenericPhy, PacketQueue, Sma};\nuse embassy_stm32::peripherals::{ETH, ETH_SMA};\nuse embassy_stm32::rng::Rng;\nuse embassy_stm32::time::Hertz;\nuse embassy_stm32::{Config, bind_interrupts, eth, peripherals, rng};\nuse embassy_time::Timer;\nuse embedded_io_async::Write;\nuse static_cell::StaticCell;\nuse {defmt_rtt as _, panic_probe as _};\n\nbind_interrupts!(struct Irqs {\n    ETH => eth::InterruptHandler;\n    HASH_RNG => rng::InterruptHandler<peripherals::RNG>;\n});\n\ntype Device = Ethernet<'static, ETH, GenericPhy<Sma<'static, ETH_SMA>>>;\n\n#[embassy_executor::task]\nasync fn net_task(mut runner: embassy_net::Runner<'static, Device>) -> ! {\n    runner.run().await\n}\n\n#[embassy_executor::main]\nasync fn main(spawner: Spawner) -> ! {\n    let mut config = Config::default();\n    {\n        use embassy_stm32::rcc::*;\n        config.rcc.hse = Some(Hse {\n            freq: Hertz(8_000_000),\n            mode: HseMode::Bypass,\n        });\n        config.rcc.pll_src = PllSource::HSE;\n        config.rcc.pll = Some(Pll {\n            prediv: PllPreDiv::DIV4,\n            mul: PllMul::MUL216,\n            divp: Some(PllPDiv::DIV2), // 8mhz / 4 * 216 / 2 = 216Mhz\n            divq: None,\n            divr: None,\n        });\n        config.rcc.ahb_pre = AHBPrescaler::DIV1;\n        config.rcc.apb1_pre = APBPrescaler::DIV4;\n        config.rcc.apb2_pre = APBPrescaler::DIV2;\n        config.rcc.sys = Sysclk::PLL1_P;\n    }\n    let p = embassy_stm32::init(config);\n\n    info!(\"Hello World!\");\n\n    // Generate random seed.\n    let mut rng = Rng::new(p.RNG, Irqs);\n    let mut seed = [0; 8];\n    rng.fill_bytes(&mut seed);\n    let seed = u64::from_le_bytes(seed);\n\n    let mac_addr = [0x00, 0x00, 0xDE, 0xAD, 0xBE, 0xEF];\n\n    static PACKETS: StaticCell<PacketQueue<4, 4>> = StaticCell::new();\n    let device = Ethernet::new(\n        PACKETS.init(PacketQueue::<4, 4>::new()),\n        p.ETH,\n        Irqs,\n        p.PA1,\n        p.PA7,\n        p.PC4,\n        p.PC5,\n        p.PG13,\n        p.PB13,\n        p.PG11,\n        mac_addr,\n        p.ETH_SMA,\n        p.PA2,\n        p.PC1,\n    );\n\n    let config = embassy_net::Config::dhcpv4(Default::default());\n    //let config = embassy_net::Config::ipv4_static(embassy_net::StaticConfigV4 {\n    //    address: Ipv4Cidr::new(Ipv4Address::new(10, 42, 0, 61), 24),\n    //    dns_servers: Vec::new(),\n    //    gateway: Some(Ipv4Address::new(10, 42, 0, 1)),\n    //});\n\n    // Init network stack\n    static RESOURCES: StaticCell<StackResources<3>> = StaticCell::new();\n    let (stack, runner) = embassy_net::new(device, config, RESOURCES.init(StackResources::new()), seed);\n\n    // Launch network task\n    spawner.spawn(unwrap!(net_task(runner)));\n\n    // Ensure DHCP configuration is up before trying connect\n    stack.wait_config_up().await;\n\n    info!(\"Network task initialized\");\n\n    // Then we can use it!\n    let mut rx_buffer = [0; 4096];\n    let mut tx_buffer = [0; 4096];\n\n    loop {\n        let mut socket = TcpSocket::new(stack, &mut rx_buffer, &mut tx_buffer);\n\n        socket.set_timeout(Some(embassy_time::Duration::from_secs(10)));\n\n        let remote_endpoint = (Ipv4Address::new(10, 42, 0, 1), 8000);\n        info!(\"connecting...\");\n        let r = socket.connect(remote_endpoint).await;\n        if let Err(e) = r {\n            info!(\"connect error: {:?}\", e);\n            Timer::after_secs(1).await;\n            continue;\n        }\n        info!(\"connected!\");\n        let buf = [0; 1024];\n        loop {\n            let r = socket.write_all(&buf).await;\n            if let Err(e) = r {\n                info!(\"write error: {:?}\", e);\n                break;\n            }\n            Timer::after_secs(1).await;\n        }\n    }\n}\n"
  },
  {
    "path": "examples/stm32f7/src/bin/flash.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse defmt::{info, unwrap};\nuse embassy_executor::Spawner;\nuse embassy_stm32::flash::Flash;\nuse embassy_time::Timer;\nuse {defmt_rtt as _, panic_probe as _};\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let p = embassy_stm32::init(Default::default());\n    info!(\"Hello Flash!\");\n\n    const ADDR: u32 = 0x8_0000; // This is the offset into the third region, the absolute address is 4x32K + 128K + 0x8_0000.\n\n    // wait a bit before accessing the flash\n    Timer::after_millis(300).await;\n\n    let mut f = Flash::new_blocking(p.FLASH).into_blocking_regions().bank1_region3;\n\n    info!(\"Reading...\");\n    let mut buf = [0u8; 32];\n    unwrap!(f.blocking_read(ADDR, &mut buf));\n    info!(\"Read: {=[u8]:x}\", buf);\n\n    info!(\"Erasing...\");\n    unwrap!(f.blocking_erase(ADDR, ADDR + 256 * 1024));\n\n    info!(\"Reading...\");\n    let mut buf = [0u8; 32];\n    unwrap!(f.blocking_read(ADDR, &mut buf));\n    info!(\"Read after erase: {=[u8]:x}\", buf);\n\n    info!(\"Writing...\");\n    unwrap!(f.blocking_write(\n        ADDR,\n        &[\n            1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29,\n            30, 31, 32\n        ]\n    ));\n\n    info!(\"Reading...\");\n    let mut buf = [0u8; 32];\n    unwrap!(f.blocking_read(ADDR, &mut buf));\n    info!(\"Read: {=[u8]:x}\", buf);\n    assert_eq!(\n        &buf[..],\n        &[\n            1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29,\n            30, 31, 32\n        ]\n    );\n}\n"
  },
  {
    "path": "examples/stm32f7/src/bin/hash.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse defmt::info;\nuse embassy_executor::Spawner;\nuse embassy_stm32::hash::*;\nuse embassy_stm32::{Config, bind_interrupts, dma, hash, peripherals};\nuse embassy_time::Instant;\nuse hmac::{Hmac, Mac};\nuse sha2::{Digest, Sha256};\nuse {defmt_rtt as _, panic_probe as _};\n\ntype HmacSha256 = Hmac<Sha256>;\n\nbind_interrupts!(struct Irqs {\n    HASH_RNG => hash::InterruptHandler<peripherals::HASH>;\n    DMA2_STREAM7 => dma::InterruptHandler<peripherals::DMA2_CH7>;\n});\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) -> ! {\n    let config = Config::default();\n    let p = embassy_stm32::init(config);\n\n    let test_1: &[u8] = b\"as;dfhaslfhas;oifvnasd;nifvnhasd;nifvhndlkfghsd;nvfnahssdfgsdafgsasdfasdfasdfasdfasdfghjklmnbvcalskdjghalskdjgfbaslkdjfgbalskdjgbalskdjbdfhsdfhsfghsfghfgh\";\n    let test_2: &[u8] = b\"fdhalksdjfhlasdjkfhalskdjfhgal;skdjfgalskdhfjgalskdjfglafgadfgdfgdafgaadsfgfgdfgadrgsyfthxfgjfhklhjkfgukhulkvhlvhukgfhfsrghzdhxyfufynufyuszeradrtydyytserr\";\n\n    let mut hw_hasher = Hash::new(p.HASH, p.DMA2_CH7, Irqs);\n\n    let hw_start_time = Instant::now();\n\n    // Compute a digest in hardware.\n    let mut context = hw_hasher.start(Algorithm::SHA256, DataType::Width8, None);\n    hw_hasher.update(&mut context, test_1).await;\n    hw_hasher.update(&mut context, test_2).await;\n    let mut hw_digest: [u8; 32] = [0; 32];\n    hw_hasher.finish(context, &mut hw_digest).await;\n\n    let hw_end_time = Instant::now();\n    let hw_execution_time = hw_end_time - hw_start_time;\n\n    let sw_start_time = Instant::now();\n\n    // Compute a digest in software.\n    let mut sw_hasher = Sha256::new();\n    sw_hasher.update(test_1);\n    sw_hasher.update(test_2);\n    let sw_digest = sw_hasher.finalize();\n\n    let sw_end_time = Instant::now();\n    let sw_execution_time = sw_end_time - sw_start_time;\n\n    info!(\"Hardware Digest: {:?}\", hw_digest);\n    info!(\"Software Digest: {:?}\", sw_digest[..]);\n    info!(\"Hardware Execution Time: {:?}\", hw_execution_time);\n    info!(\"Software Execution Time: {:?}\", sw_execution_time);\n    assert_eq!(hw_digest, sw_digest[..]);\n\n    let hmac_key: [u8; 64] = [0x55; 64];\n\n    // Compute HMAC in hardware.\n    let mut sha256hmac_context = hw_hasher.start(Algorithm::SHA256, DataType::Width8, Some(&hmac_key));\n    hw_hasher.update(&mut sha256hmac_context, test_1).await;\n    hw_hasher.update(&mut sha256hmac_context, test_2).await;\n    let mut hw_hmac: [u8; 32] = [0; 32];\n    hw_hasher.finish(sha256hmac_context, &mut hw_hmac).await;\n\n    // Compute HMAC in software.\n    let mut sw_mac = HmacSha256::new_from_slice(&hmac_key).unwrap();\n    sw_mac.update(test_1);\n    sw_mac.update(test_2);\n    let sw_hmac = sw_mac.finalize().into_bytes();\n\n    info!(\"Hardware HMAC: {:?}\", hw_hmac);\n    info!(\"Software HMAC: {:?}\", sw_hmac[..]);\n    assert_eq!(hw_hmac, sw_hmac[..]);\n\n    loop {}\n}\n"
  },
  {
    "path": "examples/stm32f7/src/bin/hello.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse defmt::info;\nuse embassy_executor::Spawner;\nuse embassy_stm32::Config;\nuse embassy_time::Timer;\nuse {defmt_rtt as _, panic_probe as _};\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) -> ! {\n    let config = Config::default();\n    let _p = embassy_stm32::init(config);\n\n    loop {\n        info!(\"Hello World!\");\n        Timer::after_secs(1).await;\n    }\n}\n"
  },
  {
    "path": "examples/stm32f7/src/bin/pwm.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_stm32::Config;\nuse embassy_stm32::gpio::OutputType;\nuse embassy_stm32::time::{Hertz, mhz};\nuse embassy_stm32::timer::simple_pwm::{PwmPin, SimplePwm};\nuse embassy_time::Timer;\nuse {defmt_rtt as _, panic_probe as _};\n\n// If you are trying this and your USB device doesn't connect, the most\n// common issues are the RCC config and vbus_detection\n//\n// See https://embassy.dev/book/#_the_usb_examples_are_not_working_on_my_board_is_there_anything_else_i_need_to_configure\n// for more information.\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    info!(\"Hello World!\");\n\n    let mut config = Config::default();\n    {\n        use embassy_stm32::rcc::*;\n        config.rcc.hse = Some(Hse {\n            freq: Hertz(8_000_000),\n            mode: HseMode::Bypass,\n        });\n        config.rcc.pll_src = PllSource::HSE;\n        config.rcc.pll = Some(Pll {\n            prediv: PllPreDiv::DIV4,\n            mul: PllMul::MUL200,\n            divp: Some(PllPDiv::DIV2), // 8mhz / 4 * 200 / 2 = 200Mhz\n            divq: Some(PllQDiv::DIV4), // 8mhz / 4 * 200 / 4 = 100Mhz\n            divr: None,\n        });\n        config.rcc.ahb_pre = AHBPrescaler::DIV1;\n        config.rcc.apb1_pre = APBPrescaler::DIV4;\n        config.rcc.apb2_pre = APBPrescaler::DIV2;\n        config.rcc.sys = Sysclk::PLL1_P;\n    }\n    let p = embassy_stm32::init(config);\n    let ch1_pin = PwmPin::new(p.PE9, OutputType::PushPull);\n    let mut pwm = SimplePwm::new(p.TIM1, Some(ch1_pin), None, None, None, mhz(1), Default::default());\n    let mut ch1 = pwm.ch1();\n    ch1.enable();\n\n    info!(\"PWM initialized\");\n    info!(\"PWM max duty {}\", ch1.max_duty_cycle());\n\n    loop {\n        ch1.set_duty_cycle_fully_off();\n        Timer::after_millis(300).await;\n        ch1.set_duty_cycle_fraction(1, 4);\n        Timer::after_millis(300).await;\n        ch1.set_duty_cycle_fraction(1, 2);\n        Timer::after_millis(300).await;\n        ch1.set_duty_cycle(ch1.max_duty_cycle() - 1);\n        Timer::after_millis(300).await;\n    }\n}\n"
  },
  {
    "path": "examples/stm32f7/src/bin/pwm_ringbuffer.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_stm32::gpio::OutputType;\nuse embassy_stm32::time::mhz;\nuse embassy_stm32::timer::simple_pwm::{PwmPin, SimplePwm};\nuse embassy_stm32::{Config, bind_interrupts, dma, peripherals};\nuse embassy_time::Timer;\nuse {defmt_rtt as _, panic_probe as _};\n\nbind_interrupts!(struct Irqs {\n    DMA2_STREAM5 => dma::InterruptHandler<peripherals::DMA2_CH5>;\n});\n\n// If you are trying this and your USB device doesn't connect, the most\n// common issues are the RCC config and vbus_detection\n//\n// See https://embassy.dev/book/#_the_usb_examples_are_not_working_on_my_board_is_there_anything_else_i_need_to_configure\n// for more information.\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    info!(\"PWM Ring Buffer Example\");\n\n    let mut config = Config::default();\n    {\n        use embassy_stm32::rcc::*;\n        use embassy_stm32::time::Hertz;\n        config.rcc.hse = Some(Hse {\n            freq: Hertz(8_000_000),\n            mode: HseMode::Bypass,\n        });\n        config.rcc.pll_src = PllSource::HSE;\n        config.rcc.pll = Some(Pll {\n            prediv: PllPreDiv::DIV4,\n            mul: PllMul::MUL200,\n            divp: Some(PllPDiv::DIV2), // 8mhz / 4 * 200 / 2 = 200Mhz\n            divq: Some(PllQDiv::DIV4), // 8mhz / 4 * 200 / 4 = 100Mhz\n            divr: None,\n        });\n        config.rcc.ahb_pre = AHBPrescaler::DIV1;\n        config.rcc.apb1_pre = APBPrescaler::DIV4;\n        config.rcc.apb2_pre = APBPrescaler::DIV2;\n        config.rcc.sys = Sysclk::PLL1_P;\n    }\n    let p = embassy_stm32::init(config);\n\n    // Initialize PWM on TIM1\n    let ch1_pin = PwmPin::new(p.PE9, OutputType::PushPull);\n    let ch2_pin = PwmPin::new(p.PE11, OutputType::PushPull);\n    let mut pwm = SimplePwm::new(\n        p.TIM1,\n        Some(ch1_pin),\n        Some(ch2_pin),\n        None,\n        None,\n        mhz(1),\n        Default::default(),\n    );\n\n    let max_duty = {\n        // Use channel 1 for static PWM at 50%\n        let mut ch1 = pwm.ch1();\n        ch1.enable();\n        ch1.set_duty_cycle_fraction(1, 2);\n        info!(\"Channel 1 (PE9/D6): Static 50% duty cycle\");\n\n        // Get max duty from channel 1 before converting channel 2\n        ch1.max_duty_cycle()\n    };\n\n    info!(\"PWM max duty: {}\", max_duty);\n\n    // Create a DMA ring buffer for channel 2\n    const BUFFER_SIZE: usize = 128;\n    static mut DMA_BUFFER: [u16; BUFFER_SIZE] = [0u16; BUFFER_SIZE];\n    let dma_buffer = unsafe { &mut *core::ptr::addr_of_mut!(DMA_BUFFER) };\n\n    // Pre-fill buffer with initial sine wave using lookup table approach\n    for i in 0..BUFFER_SIZE {\n        // Simple sine approximation using triangle wave\n        let phase = (i * 256) / BUFFER_SIZE;\n        let sine_approx = if phase < 128 {\n            phase as u16 * 2\n        } else {\n            (255 - phase) as u16 * 2\n        };\n        dma_buffer[i] = (sine_approx as u32 * max_duty as u32 / 256) as u16;\n    }\n\n    // Convert channel 2 to ring-buffered PWM\n    let mut ring_pwm = pwm.ch2().into_ring_buffered_channel(p.DMA2_CH5, dma_buffer, Irqs);\n\n    info!(\"Ring buffer capacity: {}\", ring_pwm.capacity());\n\n    // Pre-write some initial data to the buffer before starting\n    info!(\"Pre-writing initial waveform data...\");\n\n    ring_pwm.write(&[0; BUFFER_SIZE]).unwrap();\n\n    // Enable the PWM channel output\n    ring_pwm.enable();\n\n    // Start the DMA ring buffer\n    ring_pwm.start();\n    info!(\"Channel 2 (PE11/D5): Ring buffered sine wave started\");\n\n    // Give DMA time to start consuming\n    Timer::after_millis(10).await;\n\n    // Continuously update the waveform\n    let mut phase: f32 = 0.0;\n    let mut amplitude: f32 = 1.0;\n    let mut amplitude_direction = -0.05;\n\n    loop {\n        // Generate new waveform data with varying amplitude\n        let mut new_data = [0u16; 32];\n        for i in 0..new_data.len() {\n            // Triangle wave approximation for sine\n            let pos = ((i as u32 + phase as u32) * 4) % 256;\n            let sine_approx = if pos < 128 {\n                pos as u16 * 2\n            } else {\n                (255 - pos) as u16 * 2\n            };\n            let scaled = (sine_approx as u32 * (amplitude * 256.0) as u32) / (256 * 256);\n            new_data[i] = ((scaled * max_duty as u32) / 256) as u16;\n        }\n\n        // Write new data to the ring buffer\n        match ring_pwm.write_exact(&new_data).await {\n            Ok(_remaining) => {}\n            Err(e) => {\n                info!(\"Write error: {:?}\", e);\n            }\n        }\n\n        // Update phase for animation effect\n        phase += 2.0;\n        if phase >= 64.0 {\n            phase = 0.0;\n        }\n\n        // Vary amplitude for breathing effect\n        amplitude += amplitude_direction;\n        if amplitude <= 0.2 || amplitude >= 1.0 {\n            amplitude_direction = -amplitude_direction;\n        }\n\n        // Log buffer status periodically\n        if (phase as u32) % 10 == 0 {\n            match ring_pwm.len() {\n                Ok(len) => info!(\"Ring buffer fill: {}/{}\", len, ring_pwm.capacity()),\n                Err(_) => info!(\"Error reading buffer length\"),\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "examples/stm32f7/src/bin/qspi.rs",
    "content": "#![no_std]\n#![no_main]\n#![allow(dead_code)] // Allow dead code as not all commands are used in the example\n\nuse defmt::info;\nuse embassy_executor::Spawner;\nuse embassy_stm32::mode::Async;\nuse embassy_stm32::qspi::enums::{AddressSize, ChipSelectHighTime, FIFOThresholdLevel, MemorySize, *};\nuse embassy_stm32::qspi::{self, Config as QspiCfg, Instance, Qspi, TransferConfig};\nuse embassy_stm32::time::mhz;\nuse embassy_stm32::{Config as StmCfg, bind_interrupts, dma, peripherals};\nuse {defmt_rtt as _, panic_probe as _};\n\nconst MEMORY_PAGE_SIZE: usize = 256;\n\nconst CMD_READ: u8 = 0x03;\nconst CMD_HS_READ: u8 = 0x0B;\nconst CMD_QUAD_READ: u8 = 0x6B;\n\nconst CMD_WRITE_PG: u8 = 0xF2;\nconst CMD_QUAD_WRITE_PG: u8 = 0x32;\n\nconst CMD_READ_ID: u8 = 0x9F;\nconst CMD_READ_UUID: u8 = 0x4B;\n\nconst CMD_ENABLE_RESET: u8 = 0x66;\nconst CMD_RESET: u8 = 0x99;\n\nconst CMD_WRITE_ENABLE: u8 = 0x06;\nconst CMD_WRITE_DISABLE: u8 = 0x04;\n\nconst CMD_CHIP_ERASE: u8 = 0xC7;\nconst CMD_SECTOR_ERASE: u8 = 0x20;\nconst CMD_BLOCK_ERASE_32K: u8 = 0x52;\nconst CMD_BLOCK_ERASE_64K: u8 = 0xD8;\n\nconst CMD_READ_SR: u8 = 0x05;\nconst CMD_READ_CR: u8 = 0x35;\n\nconst CMD_WRITE_SR: u8 = 0x01;\nconst CMD_WRITE_CR: u8 = 0x31;\nconst MEMORY_ADDR: u32 = 0x00000000u32;\n\n/// Implementation of access to flash chip.\n/// Chip commands are hardcoded as it depends on used chip.\n/// This implementation is using chip GD25Q64C from Giga Device\npub struct FlashMemory<I: Instance> {\n    qspi: Qspi<'static, I, Async>,\n}\n\nimpl<I: Instance> FlashMemory<I> {\n    pub fn new(qspi: Qspi<'static, I, Async>) -> Self {\n        let mut memory = Self { qspi };\n\n        memory.reset_memory();\n        memory.enable_quad();\n\n        memory\n    }\n\n    fn enable_quad(&mut self) {\n        let cr = self.read_cr();\n        self.write_cr(cr | 0x02);\n    }\n\n    fn exec_command(&mut self, cmd: u8) {\n        let transaction = TransferConfig {\n            iwidth: QspiWidth::SING,\n            awidth: QspiWidth::NONE,\n            dwidth: QspiWidth::NONE,\n            instruction: cmd,\n            address: None,\n            dummy: DummyCycles::_0,\n        };\n        self.qspi.blocking_command(transaction);\n    }\n\n    pub fn reset_memory(&mut self) {\n        self.exec_command(CMD_ENABLE_RESET);\n        self.exec_command(CMD_RESET);\n        self.wait_write_finish();\n    }\n\n    pub fn enable_write(&mut self) {\n        self.exec_command(CMD_WRITE_ENABLE);\n    }\n\n    pub fn read_id(&mut self) -> [u8; 3] {\n        let mut buffer = [0; 3];\n        let transaction: TransferConfig = TransferConfig {\n            iwidth: QspiWidth::SING,\n            awidth: QspiWidth::NONE,\n            dwidth: QspiWidth::SING,\n            instruction: CMD_READ_ID,\n            address: None,\n            dummy: DummyCycles::_0,\n        };\n        self.qspi.blocking_read(&mut buffer, transaction);\n        buffer\n    }\n\n    pub fn read_uuid(&mut self) -> [u8; 16] {\n        let mut buffer = [0; 16];\n        let transaction: TransferConfig = TransferConfig {\n            iwidth: QspiWidth::SING,\n            awidth: QspiWidth::SING,\n            dwidth: QspiWidth::SING,\n            instruction: CMD_READ_UUID,\n            address: Some(0),\n            dummy: DummyCycles::_8,\n        };\n        self.qspi.blocking_read(&mut buffer, transaction);\n        buffer\n    }\n\n    pub fn read_memory(&mut self, addr: u32, buffer: &mut [u8], use_dma: bool) {\n        let transaction = TransferConfig {\n            iwidth: QspiWidth::SING,\n            awidth: QspiWidth::SING,\n            dwidth: QspiWidth::QUAD,\n            instruction: CMD_QUAD_READ,\n            address: Some(addr),\n            dummy: DummyCycles::_8,\n        };\n        if use_dma {\n            self.qspi.blocking_read_dma(buffer, transaction);\n        } else {\n            self.qspi.blocking_read(buffer, transaction);\n        }\n    }\n\n    fn wait_write_finish(&mut self) {\n        while (self.read_sr() & 0x01) != 0 {}\n    }\n\n    fn perform_erase(&mut self, addr: u32, cmd: u8) {\n        let transaction = TransferConfig {\n            iwidth: QspiWidth::SING,\n            awidth: QspiWidth::SING,\n            dwidth: QspiWidth::NONE,\n            instruction: cmd,\n            address: Some(addr),\n            dummy: DummyCycles::_0,\n        };\n        self.enable_write();\n        self.qspi.blocking_command(transaction);\n        self.wait_write_finish();\n    }\n\n    pub fn erase_sector(&mut self, addr: u32) {\n        self.perform_erase(addr, CMD_SECTOR_ERASE);\n    }\n\n    pub fn erase_block_32k(&mut self, addr: u32) {\n        self.perform_erase(addr, CMD_BLOCK_ERASE_32K);\n    }\n\n    pub fn erase_block_64k(&mut self, addr: u32) {\n        self.perform_erase(addr, CMD_BLOCK_ERASE_64K);\n    }\n\n    pub fn erase_chip(&mut self) {\n        self.exec_command(CMD_CHIP_ERASE);\n    }\n\n    fn write_page(&mut self, addr: u32, buffer: &[u8], len: usize, use_dma: bool) {\n        assert!(\n            (len as u32 + (addr & 0x000000ff)) <= MEMORY_PAGE_SIZE as u32,\n            \"write_page(): page write length exceeds page boundary (len = {}, addr = {:X}\",\n            len,\n            addr\n        );\n\n        let transaction = TransferConfig {\n            iwidth: QspiWidth::SING,\n            awidth: QspiWidth::SING,\n            dwidth: QspiWidth::QUAD,\n            instruction: CMD_QUAD_WRITE_PG,\n            address: Some(addr),\n            dummy: DummyCycles::_0,\n        };\n        self.enable_write();\n        if use_dma {\n            self.qspi.blocking_write_dma(buffer, transaction);\n        } else {\n            self.qspi.blocking_write(buffer, transaction);\n        }\n        self.wait_write_finish();\n    }\n\n    pub fn write_memory(&mut self, addr: u32, buffer: &[u8], use_dma: bool) {\n        let mut left = buffer.len();\n        let mut place = addr;\n        let mut chunk_start = 0;\n\n        while left > 0 {\n            let max_chunk_size = MEMORY_PAGE_SIZE - (place & 0x000000ff) as usize;\n            let chunk_size = if left >= max_chunk_size { max_chunk_size } else { left };\n            let chunk = &buffer[chunk_start..(chunk_start + chunk_size)];\n            self.write_page(place, chunk, chunk_size, use_dma);\n            place += chunk_size as u32;\n            left -= chunk_size;\n            chunk_start += chunk_size;\n        }\n    }\n\n    fn read_register(&mut self, cmd: u8) -> u8 {\n        let mut buffer = [0; 1];\n        let transaction: TransferConfig = TransferConfig {\n            iwidth: QspiWidth::SING,\n            awidth: QspiWidth::NONE,\n            dwidth: QspiWidth::SING,\n            instruction: cmd,\n            address: None,\n            dummy: DummyCycles::_0,\n        };\n        self.qspi.blocking_read(&mut buffer, transaction);\n        buffer[0]\n    }\n\n    fn write_register(&mut self, cmd: u8, value: u8) {\n        let buffer = [value; 1];\n        let transaction: TransferConfig = TransferConfig {\n            iwidth: QspiWidth::SING,\n            awidth: QspiWidth::NONE,\n            dwidth: QspiWidth::SING,\n            instruction: cmd,\n            address: None,\n            dummy: DummyCycles::_0,\n        };\n        self.qspi.blocking_write(&buffer, transaction);\n    }\n\n    pub fn read_sr(&mut self) -> u8 {\n        self.read_register(CMD_READ_SR)\n    }\n\n    pub fn read_cr(&mut self) -> u8 {\n        self.read_register(CMD_READ_CR)\n    }\n\n    pub fn write_sr(&mut self, value: u8) {\n        self.write_register(CMD_WRITE_SR, value);\n    }\n\n    pub fn write_cr(&mut self, value: u8) {\n        self.write_register(CMD_WRITE_CR, value);\n    }\n}\n\nbind_interrupts!(struct Irqs {\n    DMA2_STREAM7 => dma::InterruptHandler<peripherals::DMA2_CH7>;\n    QUADSPI => qspi::InterruptHandler<peripherals::QUADSPI>;\n});\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) -> ! {\n    let mut config = StmCfg::default();\n    {\n        use embassy_stm32::rcc::*;\n        config.rcc.hse = Some(Hse {\n            freq: mhz(8),\n            mode: HseMode::Oscillator,\n        });\n        config.rcc.pll_src = PllSource::HSE;\n        config.rcc.pll = Some(Pll {\n            prediv: PllPreDiv::DIV4,\n            mul: PllMul::MUL216,\n            divp: Some(PllPDiv::DIV2), // 8mhz / 4 * 216 / 2 = 216Mhz\n            divq: None,\n            divr: None,\n        });\n        config.rcc.ahb_pre = AHBPrescaler::DIV1;\n        config.rcc.apb1_pre = APBPrescaler::DIV4;\n        config.rcc.apb2_pre = APBPrescaler::DIV2;\n        config.rcc.sys = Sysclk::PLL1_P;\n    }\n    let p = embassy_stm32::init(config);\n    info!(\"Embassy initialized\");\n\n    let mut config = QspiCfg::default();\n    config.memory_size = MemorySize::_8MiB;\n    config.address_size = AddressSize::_24bit;\n    config.prescaler = 16;\n    config.cs_high_time = ChipSelectHighTime::_1Cycle;\n    config.fifo_threshold = FIFOThresholdLevel::_16Bytes;\n    config.sample_shifting = SampleShifting::None;\n\n    let driver = Qspi::new_bank1(\n        p.QUADSPI, p.PF8, p.PF9, p.PE2, p.PF6, p.PF10, p.PB10, p.DMA2_CH7, Irqs, config,\n    );\n    let mut flash = FlashMemory::new(driver);\n    let flash_id = flash.read_id();\n    info!(\"FLASH ID: {:?}\", flash_id);\n    let mut wr_buf = [0u8; 256];\n    for i in 0..256 {\n        wr_buf[i] = i as u8;\n    }\n    let mut rd_buf = [0u8; 256];\n    flash.erase_sector(MEMORY_ADDR);\n    flash.write_memory(MEMORY_ADDR, &wr_buf, true);\n    flash.read_memory(MEMORY_ADDR, &mut rd_buf, true);\n    info!(\"WRITE BUF: {:?}\", wr_buf);\n    info!(\"READ BUF: {:?}\", rd_buf);\n    info!(\"End of Program, proceed to empty endless loop\");\n    loop {}\n}\n"
  },
  {
    "path": "examples/stm32f7/src/bin/sdmmc.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_stm32::sdmmc::Sdmmc;\nuse embassy_stm32::sdmmc::sd::{CmdBlock, StorageDevice};\nuse embassy_stm32::time::{Hertz, mhz};\nuse embassy_stm32::{Config, bind_interrupts, dma, peripherals, sdmmc};\nuse {defmt_rtt as _, panic_probe as _};\n\nbind_interrupts!(struct Irqs {\n    SDMMC1 => sdmmc::InterruptHandler<peripherals::SDMMC1>;\n    DMA2_STREAM3 => dma::InterruptHandler<peripherals::DMA2_CH3>;\n});\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let mut config = Config::default();\n    {\n        use embassy_stm32::rcc::*;\n        config.rcc.hse = Some(Hse {\n            freq: Hertz(8_000_000),\n            mode: HseMode::Bypass,\n        });\n        config.rcc.pll_src = PllSource::HSE;\n        config.rcc.pll = Some(Pll {\n            prediv: PllPreDiv::DIV4,\n            mul: PllMul::MUL216,\n            divp: Some(PllPDiv::DIV2), // 8mhz / 4 * 216 / 2 = 216Mhz\n            divq: Some(PllQDiv::DIV9), // 8mhz / 4 * 216 / 9 = 48Mhz\n            divr: None,\n        });\n        config.rcc.ahb_pre = AHBPrescaler::DIV1;\n        config.rcc.apb1_pre = APBPrescaler::DIV4;\n        config.rcc.apb2_pre = APBPrescaler::DIV2;\n        config.rcc.sys = Sysclk::PLL1_P;\n    }\n    let p = embassy_stm32::init(config);\n\n    info!(\"Hello World!\");\n\n    let mut sdmmc = Sdmmc::new_4bit(\n        p.SDMMC1,\n        p.DMA2_CH3,\n        Irqs,\n        p.PC12,\n        p.PD2,\n        p.PC8,\n        p.PC9,\n        p.PC10,\n        p.PC11,\n        Default::default(),\n    );\n\n    let mut cmd_block = CmdBlock::new();\n\n    let storage = StorageDevice::new_sd_card(&mut sdmmc, &mut cmd_block, mhz(25))\n        .await\n        .unwrap();\n\n    let card = storage.card();\n\n    info!(\"Card: {:#?}\", Debug2Format(&card));\n}\n"
  },
  {
    "path": "examples/stm32f7/src/bin/usart_dma.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse core::fmt::Write;\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_stm32::usart::{Config, Uart};\nuse embassy_stm32::{bind_interrupts, dma, peripherals, usart};\nuse heapless::String;\nuse {defmt_rtt as _, panic_probe as _};\n\nbind_interrupts!(struct Irqs {\n    UART7 => usart::InterruptHandler<peripherals::UART7>;\n    DMA1_STREAM1 => dma::InterruptHandler<peripherals::DMA1_CH1>;\n    DMA1_STREAM3 => dma::InterruptHandler<peripherals::DMA1_CH3>;\n});\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let p = embassy_stm32::init(Default::default());\n    let config = Config::default();\n    let mut usart = Uart::new(p.UART7, p.PA8, p.PA15, p.DMA1_CH1, p.DMA1_CH3, Irqs, config).unwrap();\n\n    for n in 0u32.. {\n        let mut s: String<128> = String::new();\n        core::write!(&mut s, \"Hello DMA World {}!\\r\\n\", n).unwrap();\n\n        unwrap!(usart.write(s.as_bytes()).await);\n\n        info!(\"wrote DMA\");\n    }\n}\n"
  },
  {
    "path": "examples/stm32f7/src/bin/usb_serial.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse defmt::{panic, *};\nuse embassy_executor::Spawner;\nuse embassy_futures::join::join;\nuse embassy_stm32::time::Hertz;\nuse embassy_stm32::usb::{Driver, Instance};\nuse embassy_stm32::{Config, bind_interrupts, peripherals, usb};\nuse embassy_usb::Builder;\nuse embassy_usb::class::cdc_acm::{CdcAcmClass, State};\nuse embassy_usb::driver::EndpointError;\nuse {defmt_rtt as _, panic_probe as _};\n\nbind_interrupts!(struct Irqs {\n    OTG_FS => usb::InterruptHandler<peripherals::USB_OTG_FS>;\n});\n\n// If you are trying this and your USB device doesn't connect, the most\n// common issues are the RCC config and vbus_detection\n//\n// See https://embassy.dev/book/#_the_usb_examples_are_not_working_on_my_board_is_there_anything_else_i_need_to_configure\n// for more information.\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    info!(\"Hello World!\");\n\n    let mut config = Config::default();\n    {\n        use embassy_stm32::rcc::*;\n        config.rcc.hse = Some(Hse {\n            freq: Hertz(8_000_000),\n            mode: HseMode::Bypass,\n        });\n        config.rcc.pll_src = PllSource::HSE;\n        config.rcc.pll = Some(Pll {\n            prediv: PllPreDiv::DIV4,\n            mul: PllMul::MUL216,\n            divp: Some(PllPDiv::DIV2), // 8mhz / 4 * 216 / 2 = 216Mhz\n            divq: Some(PllQDiv::DIV9), // 8mhz / 4 * 216 / 9 = 48Mhz\n            divr: None,\n        });\n        config.rcc.ahb_pre = AHBPrescaler::DIV1;\n        config.rcc.apb1_pre = APBPrescaler::DIV4;\n        config.rcc.apb2_pre = APBPrescaler::DIV2;\n        config.rcc.sys = Sysclk::PLL1_P;\n        config.rcc.mux.clk48sel = mux::Clk48sel::PLL1_Q;\n    }\n    let p = embassy_stm32::init(config);\n\n    // Create the driver, from the HAL.\n    let mut ep_out_buffer = [0u8; 256];\n    let mut config = embassy_stm32::usb::Config::default();\n\n    // Do not enable vbus_detection. This is a safe default that works in all boards.\n    // However, if your USB device is self-powered (can stay powered on if USB is unplugged), you need\n    // to enable vbus_detection to comply with the USB spec. If you enable it, the board\n    // has to support it or USB won't work at all. See docs on `vbus_detection` for details.\n    config.vbus_detection = false;\n\n    let driver = Driver::new_fs(p.USB_OTG_FS, Irqs, p.PA12, p.PA11, &mut ep_out_buffer, config);\n\n    // Create embassy-usb Config\n    let mut config = embassy_usb::Config::new(0xc0de, 0xcafe);\n    config.manufacturer = Some(\"Embassy\");\n    config.product = Some(\"USB-serial example\");\n    config.serial_number = Some(\"12345678\");\n\n    // Create embassy-usb DeviceBuilder using the driver and config.\n    // It needs some buffers for building the descriptors.\n    let mut config_descriptor = [0; 256];\n    let mut bos_descriptor = [0; 256];\n    let mut control_buf = [0; 64];\n\n    let mut state = State::new();\n\n    let mut builder = Builder::new(\n        driver,\n        config,\n        &mut config_descriptor,\n        &mut bos_descriptor,\n        &mut [], // no msos descriptors\n        &mut control_buf,\n    );\n\n    // Create classes on the builder.\n    let mut class = CdcAcmClass::new(&mut builder, &mut state, 64);\n\n    // Build the builder.\n    let mut usb = builder.build();\n\n    // Run the USB device.\n    let usb_fut = usb.run();\n\n    // Do stuff with the class!\n    let echo_fut = async {\n        loop {\n            class.wait_connection().await;\n            info!(\"Connected\");\n            let _ = echo(&mut class).await;\n            info!(\"Disconnected\");\n        }\n    };\n\n    // Run everything concurrently.\n    // If we had made everything `'static` above instead, we could do this using separate tasks instead.\n    join(usb_fut, echo_fut).await;\n}\n\nstruct Disconnected {}\n\nimpl From<EndpointError> for Disconnected {\n    fn from(val: EndpointError) -> Self {\n        match val {\n            EndpointError::BufferOverflow => panic!(\"Buffer overflow\"),\n            EndpointError::Disabled => Disconnected {},\n        }\n    }\n}\n\nasync fn echo<'d, T: Instance + 'd>(class: &mut CdcAcmClass<'d, Driver<'d, T>>) -> Result<(), Disconnected> {\n    let mut buf = [0; 64];\n    loop {\n        let n = class.read_packet(&mut buf).await?;\n        let data = &buf[..n];\n        info!(\"data: {:x}\", data);\n        class.write_packet(data).await?;\n    }\n}\n"
  },
  {
    "path": "examples/stm32g0/.cargo/config.toml",
    "content": "[target.'cfg(all(target_arch = \"arm\", target_os = \"none\"))']\n# replace STM32G0B1RETx with your chip as listed in `probe-rs chip list`\nrunner = \"probe-rs run --chip STM32G0B1RETx\"\n\n[build]\ntarget = \"thumbv6m-none-eabi\"\n\n[env]\nDEFMT_LOG = \"trace\"\n"
  },
  {
    "path": "examples/stm32g0/Cargo.toml",
    "content": "[package]\nedition = \"2024\"\nname = \"embassy-stm32g0-examples\"\nversion = \"0.1.0\"\nlicense = \"MIT OR Apache-2.0\"\npublish = false\n\n[dependencies]\n# Change stm32g0b1re to your chip name, if necessary.\nembassy-stm32 = { version = \"0.6.0\", path = \"../../embassy-stm32\", features = [ \"defmt\", \"time-driver-any\", \"stm32g0b1re\", \"memory-x\", \"unstable-pac\", \"exti\"]  }\nembassy-sync = { version = \"0.8.0\", path = \"../../embassy-sync\", features = [\"defmt\"] }\nembassy-executor = { version = \"0.10.0\", path = \"../../embassy-executor\", features = [\"platform-cortex-m\", \"executor-thread\", \"defmt\"] }\nembassy-time = { version = \"0.5.1\", path = \"../../embassy-time\", features = [\"defmt\", \"defmt-timestamp-uptime\", \"tick-hz-32_768\"] }\nembassy-usb = { version = \"0.6.0\", path = \"../../embassy-usb\", default-features = false, features = [\"defmt\"] }\nembassy-futures = { version = \"0.1.2\", path = \"../../embassy-futures\" }\n\ndefmt = \"1.0.1\"\ndefmt-rtt = \"1.0.0\"\n\ncortex-m = { version = \"0.7.6\", features = [\"inline-asm\", \"critical-section-single-core\"] }\ncortex-m-rt = \"0.7.0\"\nembedded-hal = \"0.2.6\"\npanic-probe = { version = \"1.0.0\", features = [\"print-defmt\"] }\nheapless = { version = \"0.9\", default-features = false }\nportable-atomic = { version = \"1.5\", features = [\"unsafe-assume-single-core\"] }\n\nembedded-io-async = { version = \"0.7.0\" }\n\n[profile.release]\ndebug = 2\n\n[package.metadata.embassy]\nbuild = [\n  { target = \"thumbv6m-none-eabi\", artifact-dir = \"out/examples/stm32g0\" }\n]\n"
  },
  {
    "path": "examples/stm32g0/build.rs",
    "content": "fn main() {\n    println!(\"cargo:rustc-link-arg-bins=--nmagic\");\n    println!(\"cargo:rustc-link-arg-bins=-Tlink.x\");\n    println!(\"cargo:rustc-link-arg-bins=-Tdefmt.x\");\n}\n"
  },
  {
    "path": "examples/stm32g0/src/bin/adc.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_stm32::adc::{Adc, Clock, Presc, SampleTime};\nuse embassy_time::Timer;\nuse {defmt_rtt as _, panic_probe as _};\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let p = embassy_stm32::init(Default::default());\n    info!(\"Hello World!\");\n\n    let mut adc = Adc::new_with_clock(p.ADC1, Clock::Async { div: Presc::DIV1 });\n    let mut pin = p.PA1;\n\n    let mut vrefint = adc.enable_vrefint();\n    let vrefint_sample = adc.blocking_read(&mut vrefint, SampleTime::CYCLES79_5);\n    let convert_to_millivolts = |sample| {\n        // From https://www.st.com/resource/en/datasheet/stm32g031g8.pdf\n        // 6.3.3 Embedded internal reference voltage\n        const VREFINT_MV: u32 = 1212; // mV\n\n        (u32::from(sample) * VREFINT_MV / u32::from(vrefint_sample)) as u16\n    };\n\n    loop {\n        let v = adc.blocking_read(&mut pin, SampleTime::CYCLES79_5);\n        info!(\"--> {} - {} mV\", v, convert_to_millivolts(v));\n        Timer::after_millis(100).await;\n    }\n}\n"
  },
  {
    "path": "examples/stm32g0/src/bin/adc_dma.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_stm32::adc::{Adc, AdcChannel as _, Clock, Presc, SampleTime};\nuse embassy_stm32::{bind_interrupts, dma, peripherals};\nuse embassy_time::Timer;\nuse {defmt_rtt as _, panic_probe as _};\n\nstatic mut DMA_BUF: [u16; 2] = [0; 2];\n\nbind_interrupts!(struct Irqs {\n    DMA1_CHANNEL1 => dma::InterruptHandler<peripherals::DMA1_CH1>;\n});\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let mut read_buffer = unsafe { &mut DMA_BUF[..] };\n\n    let p = embassy_stm32::init(Default::default());\n\n    info!(\"Hello World!\");\n\n    let mut adc = Adc::new_with_clock(p.ADC1, Clock::Async { div: Presc::DIV1 });\n\n    let mut dma = p.DMA1_CH1;\n    let mut vrefint_channel = adc.enable_vrefint().degrade_adc();\n    let mut pa0 = p.PA0.degrade_adc();\n\n    loop {\n        adc.read(\n            dma.reborrow(),\n            Irqs,\n            [\n                (&mut vrefint_channel, SampleTime::CYCLES160_5),\n                (&mut pa0, SampleTime::CYCLES160_5),\n            ]\n            .into_iter(),\n            &mut read_buffer,\n        )\n        .await;\n\n        let vrefint = read_buffer[0];\n        let measured = read_buffer[1];\n        info!(\"vrefint: {}\", vrefint);\n        info!(\"measured: {}\", measured);\n        Timer::after_millis(500).await;\n    }\n}\n"
  },
  {
    "path": "examples/stm32g0/src/bin/adc_oversampling.rs",
    "content": "//! adc oversampling example\n//!\n//! This example uses adc oversampling to achieve 16bit data\n\n#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_stm32::adc::{Adc, AdcConfig, Clock, Ovsr, Ovss, Presc, SampleTime};\nuse embassy_time::Timer;\nuse {defmt_rtt as _, panic_probe as _};\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let p = embassy_stm32::init(Default::default());\n    info!(\"Adc oversample test\");\n\n    let mut config = AdcConfig::default();\n    config.clock = Some(Clock::Async { div: Presc::DIV1 });\n    config.oversampling_ratio = Some(Ovsr::MUL16);\n    config.oversampling_shift = Some(Ovss::NO_SHIFT);\n    config.oversampling_enable = Some(true);\n\n    let mut adc = Adc::new_with_config(p.ADC1, config);\n    let mut pin = p.PA1;\n\n    loop {\n        let v = adc.blocking_read(&mut pin, SampleTime::CYCLES1_5);\n        info!(\"--> {} \", v); //max 65520 = 0xFFF0\n        Timer::after_millis(100).await;\n    }\n}\n"
  },
  {
    "path": "examples/stm32g0/src/bin/adc_ring_buffered_timer.rs",
    "content": "//! ADC with DMA ring buffer triggered by timer\n//!\n//! This example demonstrates periodic ADC sampling using a timer trigger and DMA ring buffer.\n//! The timer generates regular ADC conversions at a controlled rate, and DMA automatically\n//! stores the samples in a circular buffer for efficient data acquisition.\n//!\n//! Hardware setup:\n//! - PA0: ADC input (connect to analog signal)\n//! - Internal VrefInt and Temperature sensors also monitored\n\n#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_stm32::adc::{Adc, AdcChannel as _, Clock, Presc, SampleTime};\nuse embassy_stm32::pac::adc::vals::Exten;\nuse embassy_stm32::peripherals::DMA1_CH1;\nuse embassy_stm32::time::Hertz;\nuse embassy_stm32::timer::complementary_pwm::{ComplementaryPwm, Mms2};\nuse embassy_stm32::timer::low_level::CountingMode;\nuse embassy_stm32::triggers::TIM1_TRGO2;\nuse embassy_stm32::{bind_interrupts, dma};\nuse {defmt_rtt as _, panic_probe as _};\n\nbind_interrupts!(struct Irqs {\n    DMA1_CHANNEL1 => dma::InterruptHandler<DMA1_CH1>;\n});\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let config = Default::default();\n    let p = embassy_stm32::init(config);\n\n    info!(\"ADC Ring Buffer with Timer Trigger example for STM32G0\");\n\n    // Configure TIM1 to generate TRGO2 events at 10 Hz\n    // This will trigger ADC conversions periodically\n    let tim1 = p.TIM1;\n    let mut pwm = ComplementaryPwm::new(\n        tim1,\n        None,          // CH1\n        None,          // CH1N\n        None,          // CH2\n        None,          // CH2N\n        None,          // CH3\n        None,          // CH3N\n        None,          // CH4\n        None,          // CH4N\n        Hertz::hz(10), // 10 Hz trigger rate\n        CountingMode::EdgeAlignedUp,\n    );\n\n    // Configure TRGO2 to trigger on update event\n    pwm.set_mms2(Mms2::UPDATE);\n\n    // Configure ADC with DMA ring buffer\n    let adc = Adc::new_with_clock(p.ADC1, Clock::Async { div: Presc::DIV1 });\n\n    // Setup channels to measure\n    let vrefint_channel = adc.enable_vrefint().degrade_adc();\n    let temp_channel = adc.enable_temperature().degrade_adc();\n    let pa0 = p.PA0.degrade_adc();\n\n    let sequence = [\n        (vrefint_channel, SampleTime::CYCLES79_5),\n        (temp_channel, SampleTime::CYCLES79_5),\n        (pa0, SampleTime::CYCLES79_5),\n    ]\n    .into_iter();\n\n    // DMA buffer - using double size allows reading one half while DMA fills the other\n    // Buffer holds continuous samples\n    let mut dma_buf = [0u16; 12]; // 4 complete samples (3 channels each)\n\n    // Create ring-buffered ADC with timer trigger\n    let mut ring_buffered_adc = adc.into_ring_buffered(\n        p.DMA1_CH1,\n        &mut dma_buf,\n        Irqs,\n        sequence,\n        TIM1_TRGO2,         // Timer 1 TRGO2 as trigger source\n        Exten::RISING_EDGE, // Trigger on rising edge (can also use FALLING_EDGE or BOTH_EDGES)\n    );\n\n    // Start ADC conversions and DMA transfer\n    ring_buffered_adc.start();\n\n    info!(\"ADC configured with TIM1 trigger at 10 Hz\");\n    info!(\"Reading 3 channels: VrefInt, Temperature, PA0\");\n\n    // Buffer to read samples - must be half the size of dma_buf\n    let mut data = [0u16; 6]; // 2 complete samples\n\n    loop {\n        match ring_buffered_adc.read(&mut data).await {\n            Ok(remaining) => {\n                // Data contains interleaved samples: [vref0, temp0, pa0_0, vref1, temp1, pa0_1]\n                info!(\"Sample 1: VrefInt={}, Temp={}, PA0={}\", data[0], data[1], data[2]);\n                info!(\"Sample 2: VrefInt={}, Temp={}, PA0={}\", data[3], data[4], data[5]);\n                info!(\"Remaining samples in buffer: {}\", remaining);\n\n                // Convert VrefInt to millivolts (example calculation)\n                let vdda_mv = (3300 * 1210) / data[0] as u32; // Assuming VREFINT_CAL = 1210\n                info!(\"Estimated VDDA: {} mV\", vdda_mv);\n            }\n            Err(e) => {\n                error!(\"DMA error: {:?}\", e);\n                ring_buffered_adc.clear();\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "examples/stm32g0/src/bin/blinky.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_stm32::gpio::{Level, Output, Speed};\nuse embassy_time::Timer;\nuse {defmt_rtt as _, panic_probe as _};\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let p = embassy_stm32::init(Default::default());\n    info!(\"Hello World!\");\n\n    let mut led = Output::new(p.PB7, Level::High, Speed::Low);\n\n    loop {\n        info!(\"high\");\n        led.set_high();\n        Timer::after_millis(300).await;\n\n        info!(\"low\");\n        led.set_low();\n        Timer::after_millis(300).await;\n    }\n}\n"
  },
  {
    "path": "examples/stm32g0/src/bin/button.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse cortex_m_rt::entry;\nuse defmt::*;\nuse embassy_stm32::gpio::{Input, Pull};\nuse {defmt_rtt as _, panic_probe as _};\n\n#[entry]\nfn main() -> ! {\n    info!(\"Hello World!\");\n\n    let p = embassy_stm32::init(Default::default());\n\n    let button = Input::new(p.PC13, Pull::Up);\n\n    loop {\n        if button.is_high() {\n            info!(\"high\");\n        } else {\n            info!(\"low\");\n        }\n    }\n}\n"
  },
  {
    "path": "examples/stm32g0/src/bin/button_exti.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_stm32::exti::{self, ExtiInput};\nuse embassy_stm32::gpio::Pull;\nuse embassy_stm32::{bind_interrupts, interrupt};\nuse {defmt_rtt as _, panic_probe as _};\n\nbind_interrupts!(\n    pub struct Irqs{\n        EXTI4_15 => exti::InterruptHandler<interrupt::typelevel::EXTI4_15>;\n});\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let p = embassy_stm32::init(Default::default());\n    info!(\"Hello World!\");\n\n    let mut button = ExtiInput::new(p.PC13, p.EXTI13, Pull::Up, Irqs);\n\n    info!(\"Press the USER button...\");\n\n    loop {\n        button.wait_for_falling_edge().await;\n        info!(\"Pressed!\");\n        button.wait_for_rising_edge().await;\n        info!(\"Released!\");\n    }\n}\n"
  },
  {
    "path": "examples/stm32g0/src/bin/flash.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_stm32::flash::Flash;\nuse {defmt_rtt as _, panic_probe as _};\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let p = embassy_stm32::init(Default::default());\n    info!(\"Hello World!\");\n\n    let addr: u32 = 0x8000;\n\n    let mut f = Flash::new_blocking(p.FLASH).into_blocking_regions().bank1_region;\n\n    info!(\"Reading...\");\n    let mut buf = [0u8; 32];\n    unwrap!(f.blocking_read(addr, &mut buf));\n    info!(\"Read: {=[u8]:x}\", buf);\n    info!(\"Erasing...\");\n    unwrap!(f.blocking_erase(addr, addr + 2 * 1024));\n\n    info!(\"Reading...\");\n    let mut buf = [0u8; 32];\n    unwrap!(f.blocking_read(addr, &mut buf));\n    info!(\"Read after erase: {=[u8]:x}\", buf);\n\n    info!(\"Writing...\");\n    unwrap!(f.blocking_write(\n        addr,\n        &[\n            1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29,\n            30, 31, 32\n        ]\n    ));\n\n    info!(\"Reading...\");\n    let mut buf = [0u8; 32];\n    unwrap!(f.blocking_read(addr, &mut buf));\n    info!(\"Read: {=[u8]:x}\", buf);\n}\n"
  },
  {
    "path": "examples/stm32g0/src/bin/flash_async.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse defmt::{info, unwrap};\nuse embassy_executor::Spawner;\nuse embassy_stm32::flash::{Flash, InterruptHandler};\nuse embassy_stm32::gpio::{AnyPin, Level, Output, Speed};\nuse embassy_stm32::{Peri, bind_interrupts};\nuse embassy_time::Timer;\nuse {defmt_rtt as _, panic_probe as _};\n\nbind_interrupts!(struct Irqs {\n    FLASH => InterruptHandler;\n});\n\n#[embassy_executor::main]\nasync fn main(spawner: Spawner) {\n    let p = embassy_stm32::init(Default::default());\n    info!(\"Hello Flash!\");\n\n    let mut f = Flash::new(p.FLASH, Irqs);\n\n    // Led should blink uninterrupted during erase operation\n    spawner.spawn(unwrap!(blinky(p.PB7.into())));\n\n    // Test on bank 2 so the CPU doesn't stall (code runs from bank 1).\n    // G0B1RE has 512KB dual-bank flash: bank 2 starts at 256KB offset.\n    // Erase 4KB (2 pages at 2KB each).\n    test_flash(&mut f, 256 * 1024, 4 * 1024).await;\n}\n\n#[embassy_executor::task]\nasync fn blinky(p: Peri<'static, AnyPin>) {\n    let mut led = Output::new(p, Level::High, Speed::Low);\n\n    loop {\n        info!(\"high\");\n        led.set_high();\n        Timer::after_millis(300).await;\n\n        info!(\"low\");\n        led.set_low();\n        Timer::after_millis(300).await;\n    }\n}\n\nasync fn test_flash(f: &mut Flash<'_>, offset: u32, size: u32) {\n    info!(\"Testing offset: {=u32:#X}, size: {=u32:#X}\", offset, size);\n\n    info!(\"Reading...\");\n    let mut buf = [0u8; 32];\n    unwrap!(f.blocking_read(offset, &mut buf));\n    info!(\"Read: {=[u8]:x}\", buf);\n\n    info!(\"Erasing...\");\n    unwrap!(f.erase(offset, offset + size).await);\n\n    info!(\"Reading...\");\n    let mut buf = [0u8; 32];\n    unwrap!(f.blocking_read(offset, &mut buf));\n    info!(\"Read after erase: {=[u8]:x}\", buf);\n\n    info!(\"Writing...\");\n    // G0 write size is 8 bytes (double-word)\n    unwrap!(\n        f.write(\n            offset,\n            &[\n                1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28,\n                29, 30, 31, 32\n            ]\n        )\n        .await\n    );\n\n    info!(\"Reading...\");\n    let mut buf = [0u8; 32];\n    unwrap!(f.blocking_read(offset, &mut buf));\n    info!(\"Read: {=[u8]:x}\", buf);\n    assert_eq!(\n        &buf[..],\n        &[\n            1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29,\n            30, 31, 32\n        ]\n    );\n\n    info!(\"Flash async test passed!\");\n}\n"
  },
  {
    "path": "examples/stm32g0/src/bin/hf_timer.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse defmt::info;\nuse embassy_executor::Spawner;\nuse embassy_stm32::Config as PeripheralConfig;\nuse embassy_stm32::gpio::OutputType;\nuse embassy_stm32::time::khz;\nuse embassy_stm32::timer::Channel;\nuse embassy_stm32::timer::complementary_pwm::{ComplementaryPwm, ComplementaryPwmPin};\nuse embassy_stm32::timer::simple_pwm::PwmPin;\nuse {defmt_rtt as _, panic_probe as _};\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let mut config = PeripheralConfig::default();\n    {\n        use embassy_stm32::rcc::*;\n        config.rcc.hsi = Some(Hsi {\n            sys_div: HsiSysDiv::DIV1,\n        });\n        config.rcc.pll = Some(Pll {\n            source: PllSource::HSI,\n            prediv: PllPreDiv::DIV1,\n            mul: PllMul::MUL16,\n            divp: None,\n            divq: Some(PllQDiv::DIV2), // 16 / 1 * 16 / 2 = 128 Mhz\n            divr: Some(PllRDiv::DIV4), // 16 / 1 * 16 / 4 = 64 Mhz\n        });\n        config.rcc.sys = Sysclk::PLL1_R;\n\n        // configure TIM1 mux to select PLLQ as clock source\n        // https://www.st.com/resource/en/reference_manual/rm0444-stm32g0x1-advanced-armbased-32bit-mcus-stmicroelectronics.pdf\n        // RM0444 page 210\n        // RCC - Peripherals Independent Clock Control Register - bit 22 -> 1\n        config.rcc.mux.tim1sel = embassy_stm32::rcc::mux::Tim1sel::PLL1_Q;\n    }\n    let p = embassy_stm32::init(config);\n\n    let ch1 = PwmPin::new(p.PA8, OutputType::PushPull);\n    let ch1n = ComplementaryPwmPin::new(p.PA7, OutputType::PushPull);\n\n    let mut pwm = ComplementaryPwm::new(\n        p.TIM1,\n        Some(ch1),\n        Some(ch1n),\n        None,\n        None,\n        None,\n        None,\n        None,\n        None,\n        khz(512),\n        Default::default(),\n    );\n\n    let max = pwm.get_max_duty();\n    info!(\"Max duty: {}\", max);\n\n    pwm.set_duty(Channel::Ch1, max / 2);\n    pwm.enable(Channel::Ch1);\n\n    loop {}\n}\n"
  },
  {
    "path": "examples/stm32g0/src/bin/i2c_async.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_stm32::i2c::{self, I2c};\nuse embassy_stm32::{bind_interrupts, dma, peripherals};\nuse embassy_time::{Duration, Timer};\nuse {defmt_rtt as _, panic_probe as _};\n\nbind_interrupts!(struct Irqs {\n    I2C1 => i2c::EventInterruptHandler<peripherals::I2C1>, i2c::ErrorInterruptHandler<peripherals::I2C1>;\n    DMA1_CHANNEL1 => dma::InterruptHandler<peripherals::DMA1_CH1>;\n    DMA1_CHANNEL2_3 => dma::InterruptHandler<peripherals::DMA1_CH2>;\n});\n\nconst TMP117_ADDR: u8 = 0x48;\nconst TMP117_TEMP_RESULT: u8 = 0x00;\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    info!(\"Hello world\");\n\n    let p = embassy_stm32::init(Default::default());\n\n    let mut data = [0u8; 2];\n    let mut i2c = I2c::new(p.I2C1, p.PB8, p.PB9, p.DMA1_CH1, p.DMA1_CH2, Irqs, Default::default());\n\n    loop {\n        match i2c.write_read(TMP117_ADDR, &[TMP117_TEMP_RESULT], &mut data).await {\n            Ok(()) => {\n                let temp = f32::from(i16::from_be_bytes(data)) * 7.8125 / 1000.0;\n                info!(\"Temperature {}\", temp);\n            }\n            Err(_) => error!(\"I2C Error\"),\n        }\n\n        Timer::after(Duration::from_millis(1000)).await;\n    }\n}\n"
  },
  {
    "path": "examples/stm32g0/src/bin/input_capture.rs",
    "content": "//! Input capture example\n//!\n//! This example showcases how to use the input capture feature of the timer peripheral.\n//! Connect PB1 and PA6 with a 1k Ohm resistor or Connect PB1 and PA8 with a 1k Ohm resistor\n//! to see the output.\n//! When connecting PB1 (software pwm) and PA6 the output is around 10000 (it will be a bit bigger, around 10040)\n//! Output is 1000 when connecting PB1 (PWMOUT) and PA6.\n//!\n#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_stm32::gpio::{Level, Output, OutputType, Pull, Speed};\nuse embassy_stm32::time::khz;\nuse embassy_stm32::timer::Channel;\nuse embassy_stm32::timer::input_capture::{CapturePin, InputCapture};\nuse embassy_stm32::timer::simple_pwm::{PwmPin, SimplePwm};\nuse embassy_stm32::{Peri, bind_interrupts, peripherals, timer};\nuse embassy_time::Timer;\nuse {defmt_rtt as _, panic_probe as _};\n\n// Connect PB1 and PA6 with a 1k Ohm resistor\n\n#[embassy_executor::task]\nasync fn blinky(led: Peri<'static, peripherals::PB1>) {\n    let mut led = Output::new(led, Level::High, Speed::Low);\n\n    loop {\n        led.set_high();\n        Timer::after_millis(50).await;\n\n        led.set_low();\n        Timer::after_millis(50).await;\n    }\n}\n\nbind_interrupts!(struct Irqs {\n    TIM2 => timer::CaptureCompareInterruptHandler<peripherals::TIM2>;\n});\n\n#[embassy_executor::main]\nasync fn main(spawner: Spawner) {\n    let p = embassy_stm32::init(Default::default());\n    info!(\"Hello World!\");\n\n    spawner.spawn(unwrap!(blinky(p.PB1)));\n\n    // Connect PB1 and PA8 with a 1k Ohm resistor\n    let ch1_pin = PwmPin::new(p.PA8, OutputType::PushPull);\n    let mut pwm = SimplePwm::new(p.TIM1, Some(ch1_pin), None, None, None, khz(1), Default::default());\n    pwm.ch1().enable();\n    pwm.ch1().set_duty_cycle(50);\n\n    let ch1 = CapturePin::new(p.PA0, Pull::None);\n    let mut ic = InputCapture::new(p.TIM2, Some(ch1), None, None, None, Irqs, khz(1000), Default::default());\n\n    let mut old_capture = 0;\n\n    loop {\n        ic.wait_for_rising_edge(Channel::Ch1).await;\n\n        let capture_value = ic.get_capture_value(Channel::Ch1);\n        info!(\"{}\", capture_value - old_capture);\n        old_capture = capture_value;\n    }\n}\n"
  },
  {
    "path": "examples/stm32g0/src/bin/onewire_ds18b20.rs",
    "content": "//! This examples shows how you can use buffered or DMA UART to read a DS18B20 temperature sensor on 1-Wire bus.\n//! Connect 5k pull-up resistor between PA9 and 3.3V.\n#![no_std]\n#![no_main]\n\nuse cortex_m::singleton;\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_stm32::mode::Async;\nuse embassy_stm32::usart::{\n    BufferedUartRx, BufferedUartTx, Config, ConfigError, OutputConfig, RingBufferedUartRx, UartTx,\n};\nuse embassy_stm32::{bind_interrupts, dma, peripherals, usart};\nuse embassy_time::{Duration, Timer};\nuse {defmt_rtt as _, panic_probe as _};\n\n/// Create onewire bus using DMA USART\nfn create_onewire(p: embassy_stm32::Peripherals) -> OneWire<UartTx<'static, Async>, RingBufferedUartRx<'static>> {\n    use embassy_stm32::usart::Uart;\n    bind_interrupts!(struct Irqs {\n        USART1 => usart::InterruptHandler<peripherals::USART1>;\n        DMA1_CHANNEL1 => dma::InterruptHandler<peripherals::DMA1_CH1>;\n        DMA1_CHANNEL2_3 => dma::InterruptHandler<peripherals::DMA1_CH2>;\n    });\n\n    let mut config = Config::default();\n    config.tx_config = OutputConfig::OpenDrain;\n\n    let usart = Uart::new_half_duplex(\n        p.USART1,\n        p.PA9,\n        p.DMA1_CH1,\n        p.DMA1_CH2,\n        Irqs,\n        config,\n        // Enable readback so we can read sensor pulling data low while transmission is in progress\n        usart::HalfDuplexReadback::Readback,\n    )\n    .unwrap();\n\n    const BUFFER_SIZE: usize = 16;\n    let (tx, rx) = usart.split();\n    let rx_buf: &mut [u8; BUFFER_SIZE] = singleton!(TX_BUF: [u8; BUFFER_SIZE] = [0; BUFFER_SIZE]).unwrap();\n    let rx = rx.into_ring_buffered(rx_buf);\n    OneWire::new(tx, rx)\n}\n\n/*\n/// Create onewire bus using buffered USART\nfn create_onewire(p: embassy_stm32::Peripherals) -> OneWire<BufferedUartTx<'static>, BufferedUartRx<'static>> {\n    use embassy_stm32::usart::BufferedUart;\n    bind_interrupts!(struct Irqs {\n        USART1 => usart::BufferedInterruptHandler<peripherals::USART1>;\n    });\n\n    const BUFFER_SIZE: usize = 16;\n    let mut config = Confi::default();\n    config.tx_config = OutputConfig::OpenDrain;\n    let tx_buf: &mut [u8; BUFFER_SIZE] = singleton!(TX_BUF: [u8; BUFFER_SIZE] = [0; BUFFER_SIZE]).unwrap();\n    let rx_buf: &mut [u8; BUFFER_SIZE] = singleton!(RX_BUF: [u8; BUFFER_SIZE] = [0; BUFFER_SIZE]).unwrap();\n    let usart = BufferedUart::new_half_duplex(\n        p.USART1,\n        p.PA9,\n        Irqs,\n        tx_buf,\n        rx_buf,\n        config,\n        // Enable readback so we can read sensor pulling data low while transmission is in progress\n        usart::HalfDuplexReadback::Readback,\n    )\n    .unwrap();\n    let (tx, rx) = usart.split();\n    OneWire::new(tx, rx)\n}\n*/\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let p = embassy_stm32::init(Default::default());\n\n    let onewire = create_onewire(p);\n    let mut sensor = Ds18b20::new(onewire);\n\n    loop {\n        // Start a new temperature measurement\n        sensor.start().await;\n        // Wait for the measurement to finish\n        Timer::after(Duration::from_secs(1)).await;\n        match sensor.temperature().await {\n            Ok(temp) => info!(\"temp = {:?} deg C\", temp),\n            _ => error!(\"sensor error\"),\n        }\n        Timer::after(Duration::from_secs(1)).await;\n    }\n}\n\npub trait SetBaudrate {\n    fn set_baudrate(&mut self, baudrate: u32) -> Result<(), ConfigError>;\n}\nimpl SetBaudrate for BufferedUartTx<'_> {\n    fn set_baudrate(&mut self, baudrate: u32) -> Result<(), ConfigError> {\n        BufferedUartTx::set_baudrate(self, baudrate)\n    }\n}\nimpl SetBaudrate for BufferedUartRx<'_> {\n    fn set_baudrate(&mut self, baudrate: u32) -> Result<(), ConfigError> {\n        BufferedUartRx::set_baudrate(self, baudrate)\n    }\n}\nimpl SetBaudrate for RingBufferedUartRx<'_> {\n    fn set_baudrate(&mut self, baudrate: u32) -> Result<(), ConfigError> {\n        RingBufferedUartRx::set_baudrate(self, baudrate)\n    }\n}\nimpl SetBaudrate for UartTx<'_, Async> {\n    fn set_baudrate(&mut self, baudrate: u32) -> Result<(), ConfigError> {\n        UartTx::set_baudrate(self, baudrate)\n    }\n}\n\n/// Simplified OneWire bus driver\npub struct OneWire<TX, RX>\nwhere\n    TX: embedded_io_async::Write + SetBaudrate,\n    RX: embedded_io_async::Read + SetBaudrate,\n{\n    tx: TX,\n    rx: RX,\n}\n\nimpl<TX, RX> OneWire<TX, RX>\nwhere\n    TX: embedded_io_async::Write + SetBaudrate,\n    RX: embedded_io_async::Read + SetBaudrate,\n{\n    // bitrate with one bit taking ~104 us\n    const RESET_BUADRATE: u32 = 9600;\n    // bitrate with one bit taking ~8.7 us\n    const BAUDRATE: u32 = 115200;\n\n    // startbit + 8 low bits = 9 * 1/115200 = 78 us low pulse\n    const LOGIC_1_CHAR: u8 = 0xFF;\n    // startbit only = 1/115200 = 8.7 us low pulse\n    const LOGIC_0_CHAR: u8 = 0x00;\n\n    // Address all devices on the bus\n    const COMMAND_SKIP_ROM: u8 = 0xCC;\n\n    pub fn new(tx: TX, rx: RX) -> Self {\n        Self { tx, rx }\n    }\n\n    fn set_baudrate(&mut self, baudrate: u32) -> Result<(), ConfigError> {\n        self.tx.set_baudrate(baudrate)?;\n        self.rx.set_baudrate(baudrate)\n    }\n\n    /// Reset the bus by at least 480 us low pulse.\n    pub async fn reset(&mut self) {\n        // Switch to 9600 baudrate, so one bit takes ~104 us\n        self.set_baudrate(Self::RESET_BUADRATE).expect(\"set_baudrate failed\");\n        // Low USART start bit + 4x low bits = 5 * 104 us = 520 us low pulse\n        self.tx.write(&[0xF0]).await.expect(\"write failed\");\n\n        // Read the value on the bus\n        let mut buffer = [0; 1];\n        self.rx.read_exact(&mut buffer).await.expect(\"read failed\");\n\n        // Switch back to 115200 baudrate, so one bit takes ~8.7 us\n        self.set_baudrate(Self::BAUDRATE).expect(\"set_baudrate failed\");\n\n        // read and expect sensor pulled some high bits to low (device present)\n        if buffer[0] & 0xF != 0 || buffer[0] & 0xF0 == 0xF0 {\n            warn!(\"No device present\");\n        }\n    }\n\n    /// Send byte and read response on the bus.\n    pub async fn write_read_byte(&mut self, byte: u8) -> u8 {\n        // One byte is sent as 8 UART characters\n        let mut tx = [0; 8];\n        for (pos, char) in tx.iter_mut().enumerate() {\n            *char = if (byte >> pos) & 0x1 == 0x1 {\n                Self::LOGIC_1_CHAR\n            } else {\n                Self::LOGIC_0_CHAR\n            };\n        }\n        self.tx.write_all(&tx).await.expect(\"write failed\");\n\n        // Readback the value on the bus, sensors can pull logic 1 to 0\n        let mut rx = [0; 8];\n        self.rx.read_exact(&mut rx).await.expect(\"read failed\");\n        let mut bus_byte = 0;\n        for (pos, char) in rx.iter().enumerate() {\n            // if its 0xFF, sensor didnt pull the bus to low level\n            if *char == 0xFF {\n                bus_byte |= 1 << pos;\n            }\n        }\n\n        bus_byte\n    }\n\n    /// Read a byte from the bus.\n    pub async fn read_byte(&mut self) -> u8 {\n        self.write_read_byte(0xFF).await\n    }\n}\n\n/// DS18B20 temperature sensor driver\npub struct Ds18b20<TX, RX>\nwhere\n    TX: embedded_io_async::Write + SetBaudrate,\n    RX: embedded_io_async::Read + SetBaudrate,\n{\n    bus: OneWire<TX, RX>,\n}\n\nimpl<TX, RX> Ds18b20<TX, RX>\nwhere\n    TX: embedded_io_async::Write + SetBaudrate,\n    RX: embedded_io_async::Read + SetBaudrate,\n{\n    /// Start a temperature conversion.\n    const FN_CONVERT_T: u8 = 0x44;\n    /// Read contents of the scratchpad containing the temperature.\n    const FN_READ_SCRATCHPAD: u8 = 0xBE;\n\n    pub fn new(bus: OneWire<TX, RX>) -> Self {\n        Self { bus }\n    }\n\n    /// Start a new measurement. Allow at least 1000ms before getting `temperature`.\n    pub async fn start(&mut self) {\n        self.bus.reset().await;\n        self.bus.write_read_byte(OneWire::<TX, RX>::COMMAND_SKIP_ROM).await;\n        self.bus.write_read_byte(Self::FN_CONVERT_T).await;\n    }\n\n    /// Calculate CRC8 of the data\n    fn crc8(data: &[u8]) -> u8 {\n        let mut temp;\n        let mut data_byte;\n        let mut crc = 0;\n        for b in data {\n            data_byte = *b;\n            for _ in 0..8 {\n                temp = (crc ^ data_byte) & 0x01;\n                crc >>= 1;\n                if temp != 0 {\n                    crc ^= 0x8C;\n                }\n                data_byte >>= 1;\n            }\n        }\n        crc\n    }\n\n    /// Read the temperature. Ensure >1000ms has passed since `start` before calling this.\n    pub async fn temperature(&mut self) -> Result<f32, ()> {\n        self.bus.reset().await;\n        self.bus.write_read_byte(OneWire::<TX, RX>::COMMAND_SKIP_ROM).await;\n        self.bus.write_read_byte(Self::FN_READ_SCRATCHPAD).await;\n\n        let mut data = [0; 9];\n        for byte in data.iter_mut() {\n            *byte = self.bus.read_byte().await;\n        }\n\n        match Self::crc8(&data) == 0 {\n            true => Ok(((data[1] as i16) << 8 | data[0] as i16) as f32 / 16.),\n            false => Err(()),\n        }\n    }\n}\n"
  },
  {
    "path": "examples/stm32g0/src/bin/pwm_complementary.rs",
    "content": "//! PWM complementary example\n//!\n//! This example uses two complementary pwm outputs from TIM1 with different duty cycles\n//!   ___           ___\n//!      |_________|   |_________|    PA8\n//!       _________     _________\n//!   ___|         |___|         |    PA7\n//!   _________     _________\n//!            |___|         |___|    PB3\n//!             ___           ___\n//!   _________|   |_________|   |    PB0\n\n#![no_std]\n#![no_main]\n\nuse defmt::info;\nuse embassy_executor::Spawner;\nuse embassy_stm32::gpio::OutputType;\nuse embassy_stm32::time::khz;\nuse embassy_stm32::timer::Channel;\nuse embassy_stm32::timer::complementary_pwm::{ComplementaryPwm, ComplementaryPwmPin};\nuse embassy_stm32::timer::simple_pwm::PwmPin;\nuse {defmt_rtt as _, panic_probe as _};\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let p = embassy_stm32::init(Default::default());\n\n    let ch1 = PwmPin::new(p.PA8, OutputType::PushPull);\n    let ch1n = ComplementaryPwmPin::new(p.PA7, OutputType::PushPull);\n    let ch2 = PwmPin::new(p.PB3, OutputType::PushPull);\n    let ch2n = ComplementaryPwmPin::new(p.PB0, OutputType::PushPull);\n\n    let mut pwm = ComplementaryPwm::new(\n        p.TIM1,\n        Some(ch1),\n        Some(ch1n),\n        Some(ch2),\n        Some(ch2n),\n        None,\n        None,\n        None,\n        None,\n        khz(100),\n        Default::default(),\n    );\n\n    let max = pwm.get_max_duty();\n    info!(\"Max duty: {}\", max);\n\n    pwm.set_duty(Channel::Ch1, max / 4);\n    pwm.enable(Channel::Ch1);\n    pwm.set_duty(Channel::Ch2, max * 3 / 4);\n    pwm.enable(Channel::Ch2);\n\n    loop {}\n}\n"
  },
  {
    "path": "examples/stm32g0/src/bin/pwm_input.rs",
    "content": "//! PWM input example\n//!\n//! This program demonstrates how to capture the parameters of the input waveform (frequency, width and duty cycle)\n//! Connect PB1 and PA6 with a 1k Ohm resistor or Connect PB1 and PA8 with a 1k Ohm resistor\n//! to see the output.\n//!\n\n#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_stm32::gpio::{Level, Output, OutputType, Pull, Speed};\nuse embassy_stm32::time::khz;\nuse embassy_stm32::timer::pwm_input::PwmInput;\nuse embassy_stm32::timer::simple_pwm::{PwmPin, SimplePwm};\nuse embassy_stm32::{Peri, bind_interrupts, peripherals, timer};\nuse embassy_time::Timer;\nuse {defmt_rtt as _, panic_probe as _};\n\n// Connect PB1 and PA6 with a 1k Ohm resistor\n#[embassy_executor::task]\nasync fn blinky(led: Peri<'static, peripherals::PB1>) {\n    let mut led = Output::new(led, Level::High, Speed::Low);\n\n    loop {\n        led.set_high();\n        Timer::after_millis(50).await;\n\n        led.set_low();\n        Timer::after_millis(50).await;\n    }\n}\n\nbind_interrupts!(struct Irqs {\n    TIM2 => timer::CaptureCompareInterruptHandler<peripherals::TIM2>;\n});\n\n#[embassy_executor::main]\nasync fn main(spawner: Spawner) {\n    let p = embassy_stm32::init(Default::default());\n\n    spawner.spawn(unwrap!(blinky(p.PB1)));\n    // Connect PA8 and PA6 with a 1k Ohm resistor\n    let ch1_pin = PwmPin::new(p.PA8, OutputType::PushPull);\n    let mut pwm = SimplePwm::new(p.TIM1, Some(ch1_pin), None, None, None, khz(1), Default::default());\n    pwm.ch1().set_duty_cycle_fraction(1, 4);\n    pwm.ch1().enable();\n\n    let mut pwm_input = PwmInput::new_ch1(p.TIM2, p.PA0, Irqs, Pull::None, khz(1000));\n    pwm_input.enable();\n\n    loop {\n        Timer::after_millis(500).await;\n        let period = pwm_input.get_period_ticks();\n        let width = pwm_input.get_width_ticks();\n        let duty_cycle = pwm_input.get_duty_cycle();\n        info!(\n            \"period ticks: {} width ticks: {} duty cycle: {}\",\n            period, width, duty_cycle\n        );\n    }\n}\n"
  },
  {
    "path": "examples/stm32g0/src/bin/rtc.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_stm32::Config;\nuse embassy_stm32::rtc::{DateTime, DayOfWeek, Rtc, RtcConfig};\nuse embassy_time::Timer;\nuse {defmt_rtt as _, panic_probe as _};\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let config = Config::default();\n    let p = embassy_stm32::init(config);\n\n    info!(\"Hello World!\");\n\n    let now = DateTime::from(2023, 6, 14, DayOfWeek::Friday, 15, 59, 10, 0);\n\n    let (mut rtc, time_provider) = Rtc::new(p.RTC, RtcConfig::default());\n\n    rtc.set_datetime(now.unwrap()).expect(\"datetime not set\");\n\n    loop {\n        let now: DateTime = time_provider.now().unwrap().into();\n\n        info!(\"{}:{}:{}\", now.hour(), now.minute(), now.second());\n\n        Timer::after_millis(1000).await;\n    }\n}\n"
  },
  {
    "path": "examples/stm32g0/src/bin/spi_neopixel.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_stm32::dma::word::U5;\nuse embassy_stm32::dma::{self};\nuse embassy_stm32::spi::{Config, Spi};\nuse embassy_stm32::time::Hertz;\nuse embassy_stm32::{bind_interrupts, peripherals};\nuse embassy_time::Timer;\nuse {defmt_rtt as _, panic_probe as _};\n\nconst NR_PIXELS: usize = 15;\nconst BITS_PER_PIXEL: usize = 24; // 24 for rgb, 32 for rgbw\nconst TOTAL_BITS: usize = NR_PIXELS * BITS_PER_PIXEL;\n\nstruct RGB {\n    r: u8,\n    g: u8,\n    b: u8,\n}\nimpl Default for RGB {\n    fn default() -> RGB {\n        RGB { r: 0, g: 0, b: 0 }\n    }\n}\npub struct Ws2812 {\n    // Note that the U5 type controls the selection of 5 bits to output\n    bitbuffer: [U5; TOTAL_BITS],\n}\n\nimpl Ws2812 {\n    pub fn new() -> Ws2812 {\n        Ws2812 {\n            bitbuffer: [U5(0); TOTAL_BITS],\n        }\n    }\n    fn len(&self) -> usize {\n        return NR_PIXELS;\n    }\n    fn set(&mut self, idx: usize, rgb: RGB) {\n        self.render_color(idx, 0, rgb.g);\n        self.render_color(idx, 8, rgb.r);\n        self.render_color(idx, 16, rgb.b);\n    }\n    // transform one color byte into an array of 8 byte. Each byte in the array does represent 1 neopixel bit pattern\n    fn render_color(&mut self, pixel_idx: usize, offset: usize, color: u8) {\n        let mut bits = color as usize;\n        let mut idx = pixel_idx * BITS_PER_PIXEL + offset;\n\n        // render one bit in one spi byte. High time first, then the low time\n        // clock should be 4 Mhz, 5 bits, each bit is 0.25 us.\n        // a one bit is send as a pulse of 0.75 high -- 0.50 low\n        // a zero bit is send as a pulse of 0.50 high -- 0.75 low\n        // clock frequency for the neopixel is exact 800 khz\n        // note that the mosi output should have a resistor to ground of 10k,\n        // to assure that between the bursts the line is low\n        for _i in 0..8 {\n            if idx >= TOTAL_BITS {\n                return;\n            }\n            let pattern = match bits & 0x80 {\n                0x80 => 0b0000_1110,\n                _ => 0b000_1100,\n            };\n            bits = bits << 1;\n            self.bitbuffer[idx] = U5(pattern);\n            idx += 1;\n        }\n    }\n}\n\nbind_interrupts!(struct Irqs {\n    DMA1_CHANNEL2_3 => dma::InterruptHandler<peripherals::DMA1_CH3>;\n});\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let p = embassy_stm32::init(Default::default());\n    info!(\"Start test using spi as neopixel driver\");\n\n    let mut config = Config::default();\n    config.frequency = Hertz(4_000_000);\n    let mut spi = Spi::new_txonly(p.SPI1, p.PB3, p.PB5, p.DMA1_CH3, Irqs, config); // SCK is unused.\n\n    let mut neopixels = Ws2812::new();\n\n    loop {\n        let mut cnt: usize = 0;\n        for _i in 0..10 {\n            for idx in 0..neopixels.len() {\n                let color = match (cnt + idx) % 3 {\n                    0 => RGB { r: 0x21, g: 0, b: 0 },\n                    1 => RGB { r: 0, g: 0x31, b: 0 },\n                    _ => RGB { r: 0, g: 0, b: 0x41 },\n                };\n                neopixels.set(idx, color);\n            }\n            cnt += 1;\n            // start sending the neopixel bit patters over spi to the neopixel string\n            spi.write(&neopixels.bitbuffer).await.ok();\n            Timer::after_millis(500).await;\n        }\n        Timer::after_millis(1000).await;\n    }\n}\n"
  },
  {
    "path": "examples/stm32g0/src/bin/usart.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_stm32::usart::{Config, Uart};\nuse {defmt_rtt as _, panic_probe as _};\n\n#[cortex_m_rt::entry]\nfn main() -> ! {\n    info!(\"Hello World!\");\n\n    let p = embassy_stm32::init(Default::default());\n\n    let config = Config::default();\n    let mut usart = Uart::new_blocking(p.USART2, p.PA3, p.PA2, config).unwrap();\n\n    unwrap!(usart.blocking_write(b\"Hello Embassy World!\\r\\n\"));\n    info!(\"wrote Hello, starting echo\");\n\n    let mut buf = [0u8; 1];\n    loop {\n        unwrap!(usart.blocking_read(&mut buf));\n        unwrap!(usart.blocking_write(&buf));\n    }\n}\n"
  },
  {
    "path": "examples/stm32g0/src/bin/usart_buffered.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_stm32::usart::{BufferedUart, Config};\nuse embassy_stm32::{bind_interrupts, peripherals, usart};\nuse embedded_io_async::{Read, Write};\nuse {defmt_rtt as _, panic_probe as _};\n\nbind_interrupts!(struct Irqs {\n    USART1 => usart::BufferedInterruptHandler<peripherals::USART1>;\n});\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let p = embassy_stm32::init(Default::default());\n    info!(\"Hi!\");\n\n    let mut config = Config::default();\n    config.baudrate = 115200;\n    let mut tx_buf = [0u8; 256];\n    let mut rx_buf = [0u8; 256];\n    let mut usart = BufferedUart::new(p.USART1, p.PB7, p.PB6, &mut tx_buf, &mut rx_buf, Irqs, config).unwrap();\n\n    usart.write_all(b\"Hello Embassy World!\\r\\n\").await.unwrap();\n    info!(\"wrote Hello, starting echo\");\n\n    let mut buf = [0; 4];\n    loop {\n        usart.read_exact(&mut buf[..]).await.unwrap();\n        usart.write_all(&buf[..]).await.unwrap();\n    }\n}\n"
  },
  {
    "path": "examples/stm32g0/src/bin/usart_dma.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_stm32::usart::{Config, Uart};\nuse embassy_stm32::{bind_interrupts, dma, peripherals, usart};\nuse {defmt_rtt as _, panic_probe as _};\n\nbind_interrupts!(struct Irqs {\n    USART1 => usart::InterruptHandler<peripherals::USART1>;\n    DMA1_CHANNEL2_3 => dma::InterruptHandler<peripherals::DMA1_CH2>, dma::InterruptHandler<peripherals::DMA1_CH3>;\n});\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let p = embassy_stm32::init(Default::default());\n    let mut usart = Uart::new(p.USART1, p.PB7, p.PB6, p.DMA1_CH2, p.DMA1_CH3, Irqs, Config::default()).unwrap();\n\n    usart.write(b\"Hello Embassy World!\\r\\n\").await.unwrap();\n    info!(\"wrote Hello, starting echo\");\n\n    let mut buf = [0; 5];\n    loop {\n        usart.read(&mut buf[..]).await.unwrap();\n        usart.write(&buf[..]).await.unwrap();\n    }\n}\n"
  },
  {
    "path": "examples/stm32g0/src/bin/usb_serial.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse defmt::{panic, *};\nuse embassy_executor::Spawner;\nuse embassy_futures::join::join;\nuse embassy_stm32::usb::{Driver, Instance};\nuse embassy_stm32::{Config, bind_interrupts, peripherals, usb};\nuse embassy_usb::Builder;\nuse embassy_usb::class::cdc_acm::{CdcAcmClass, State};\nuse embassy_usb::driver::EndpointError;\nuse {defmt_rtt as _, panic_probe as _};\n\nbind_interrupts!(struct Irqs {\n    USB_UCPD1_2 => usb::InterruptHandler<peripherals::USB>;\n});\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let mut config = Config::default();\n    {\n        use embassy_stm32::rcc::*;\n        config.rcc.hsi48 = Some(Hsi48Config { sync_from_usb: true });\n        config.rcc.mux.usbsel = mux::Usbsel::HSI48;\n    }\n    let p = embassy_stm32::init(config);\n\n    info!(\"Hello World!\");\n\n    // Create the driver, from the HAL.\n    let driver = Driver::new(p.USB, Irqs, p.PA12, p.PA11);\n\n    // Create embassy-usb Config\n    let config = embassy_usb::Config::new(0xc0de, 0xcafe);\n    //config.max_packet_size_0 = 64;\n\n    // Create embassy-usb DeviceBuilder using the driver and config.\n    // It needs some buffers for building the descriptors.\n    let mut config_descriptor = [0; 256];\n    let mut bos_descriptor = [0; 256];\n    let mut control_buf = [0; 7];\n\n    let mut state = State::new();\n\n    let mut builder = Builder::new(\n        driver,\n        config,\n        &mut config_descriptor,\n        &mut bos_descriptor,\n        &mut [], // no msos descriptors\n        &mut control_buf,\n    );\n\n    // Create classes on the builder.\n    let mut class = CdcAcmClass::new(&mut builder, &mut state, 64);\n\n    // Build the builder.\n    let mut usb = builder.build();\n\n    // Run the USB device.\n    let usb_fut = usb.run();\n\n    // Do stuff with the class!\n    let echo_fut = async {\n        loop {\n            class.wait_connection().await;\n            info!(\"Connected\");\n            let _ = echo(&mut class).await;\n            info!(\"Disconnected\");\n        }\n    };\n\n    // Run everything concurrently.\n    // If we had made everything `'static` above instead, we could do this using separate tasks instead.\n    join(usb_fut, echo_fut).await;\n}\n\nstruct Disconnected {}\n\nimpl From<EndpointError> for Disconnected {\n    fn from(val: EndpointError) -> Self {\n        match val {\n            EndpointError::BufferOverflow => panic!(\"Buffer overflow\"),\n            EndpointError::Disabled => Disconnected {},\n        }\n    }\n}\n\nasync fn echo<'d, T: Instance + 'd>(class: &mut CdcAcmClass<'d, Driver<'d, T>>) -> Result<(), Disconnected> {\n    let mut buf = [0; 64];\n    loop {\n        let n = class.read_packet(&mut buf).await?;\n        let data = &buf[..n];\n        info!(\"data: {:x}\", data);\n        class.write_packet(data).await?;\n    }\n}\n"
  },
  {
    "path": "examples/stm32g4/.cargo/config.toml",
    "content": "[target.'cfg(all(target_arch = \"arm\", target_os = \"none\"))']\n# replace STM32G071C8Rx with your chip as listed in `probe-rs chip list`\nrunner = \"probe-rs run --chip STM32G484VETx\"\n\n[build]\ntarget = \"thumbv7em-none-eabi\"\n\n[env]\nDEFMT_LOG = \"trace\"\n"
  },
  {
    "path": "examples/stm32g4/Cargo.toml",
    "content": "[package]\nedition = \"2024\"\nname = \"embassy-stm32g4-examples\"\nversion = \"0.1.0\"\nlicense = \"MIT OR Apache-2.0\"\npublish = false\n\n[dependencies]\n# Change stm32g491re to your chip name, if necessary.\nembassy-stm32 = { path = \"../../embassy-stm32\", features = [ \"defmt\", \"time-driver-any\", \"stm32g491re\", \"memory-x\", \"unstable-pac\", \"exti\"]  }\nembassy-sync = { path = \"../../embassy-sync\", features = [\"defmt\"] }\nembassy-executor = { path = \"../../embassy-executor\", features = [\"platform-cortex-m\", \"executor-thread\", \"defmt\"] }\nembassy-time = { path = \"../../embassy-time\", features = [\"defmt\", \"defmt-timestamp-uptime\", \"tick-hz-32_768\"] }\nembassy-usb = { path = \"../../embassy-usb\", features = [\"defmt\"] }\nembassy-futures = { path = \"../../embassy-futures\" }\nusbd-hid = \"0.10.0\"\n\ndefmt = \"1.0.1\"\ndefmt-rtt = \"1.0.0\"\n\ncortex-m = { version = \"0.7.6\", features = [\"critical-section-single-core\"] }\ncortex-m-rt = \"0.7.0\"\nembedded-hal = \"0.2.6\"\nembedded-can = { version = \"0.4\" }\npanic-probe = { version = \"1.0.0\", features = [\"print-defmt\"] }\nheapless = { version = \"0.9\", default-features = false }\nstatic_cell = \"2.0.0\"\ncritical-section = \"1.1\"\n\n[profile.release]\ndebug = 2\n\n[package.metadata.embassy]\nbuild = [\n  { target = \"thumbv7em-none-eabi\", artifact-dir = \"out/examples/stm32g4\" }\n]\n"
  },
  {
    "path": "examples/stm32g4/build.rs",
    "content": "fn main() {\n    println!(\"cargo:rustc-link-arg-bins=--nmagic\");\n    println!(\"cargo:rustc-link-arg-bins=-Tlink.x\");\n    println!(\"cargo:rustc-link-arg-bins=-Tdefmt.x\");\n}\n"
  },
  {
    "path": "examples/stm32g4/src/bin/adc.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_stm32::Config;\nuse embassy_stm32::adc::{Adc, SampleTime};\nuse embassy_time::Timer;\nuse {defmt_rtt as _, panic_probe as _};\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let mut config = Config::default();\n    {\n        use embassy_stm32::rcc::*;\n        config.rcc.pll = Some(Pll {\n            source: PllSource::HSI,\n            prediv: PllPreDiv::DIV4,\n            mul: PllMul::MUL85,\n            divp: None,\n            divq: None,\n            // Main system clock at 170 MHz\n            divr: Some(PllRDiv::DIV2),\n        });\n        config.rcc.mux.adc12sel = mux::Adcsel::SYS;\n        config.rcc.sys = Sysclk::PLL1_R;\n    }\n    let mut p = embassy_stm32::init(config);\n    info!(\"Hello World!\");\n\n    let mut adc = Adc::new(p.ADC2, Default::default());\n\n    let mut adc_temp = Adc::new(p.ADC1, Default::default());\n    let mut temperature = adc_temp.enable_temperature();\n\n    loop {\n        let measured = adc.blocking_read(&mut p.PA7, SampleTime::CYCLES24_5);\n        let temperature = adc_temp.blocking_read(&mut temperature, SampleTime::CYCLES24_5);\n        info!(\"measured: {}\", measured);\n        info!(\"temperature: {}\", temperature);\n        Timer::after_millis(500).await;\n    }\n}\n"
  },
  {
    "path": "examples/stm32g4/src/bin/adc_differential.rs",
    "content": "//! adc differential mode example\n//!\n//! This example uses adc1 in differential mode\n//! p:pa0 n:pa1\n\n#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_stm32::Config;\nuse embassy_stm32::adc::{Adc, SampleTime};\nuse embassy_time::Timer;\nuse {defmt_rtt as _, panic_probe as _};\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let mut config = Config::default();\n    {\n        use embassy_stm32::rcc::*;\n        config.rcc.pll = Some(Pll {\n            source: PllSource::HSI,\n            prediv: PllPreDiv::DIV4,\n            mul: PllMul::MUL85,\n            divp: None,\n            divq: None,\n            // Main system clock at 170 MHz\n            divr: Some(PllRDiv::DIV2),\n        });\n        config.rcc.mux.adc12sel = mux::Adcsel::SYS;\n        config.rcc.sys = Sysclk::PLL1_R;\n    }\n    let p = embassy_stm32::init(config);\n\n    let mut adc = Adc::new(p.ADC1, Default::default());\n    let mut differential_channel = (p.PA0, p.PA1);\n\n    // can also use\n    // adc.set_differential_channel(1, true);\n    info!(\"adc initialized\");\n    loop {\n        let measured = adc.blocking_read(&mut differential_channel, SampleTime::CYCLES247_5);\n        info!(\"data: {}\", measured);\n        Timer::after_millis(500).await;\n    }\n}\n"
  },
  {
    "path": "examples/stm32g4/src/bin/adc_dma.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_stm32::adc::{Adc, AdcChannel as _, SampleTime};\nuse embassy_stm32::{Config, bind_interrupts, dma, peripherals};\nuse embassy_time::Timer;\nuse {defmt_rtt as _, panic_probe as _};\n\nstatic mut DMA_BUF: [u16; 2] = [0; 2];\n\nbind_interrupts!(struct Irqs {\n    DMA1_CHANNEL1 => dma::InterruptHandler<peripherals::DMA1_CH1>;\n});\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let read_buffer = unsafe { &mut DMA_BUF[..] };\n\n    let mut config = Config::default();\n    {\n        use embassy_stm32::rcc::*;\n        config.rcc.pll = Some(Pll {\n            source: PllSource::HSI,\n            prediv: PllPreDiv::DIV4,\n            mul: PllMul::MUL85,\n            divp: None,\n            divq: None,\n            // Main system clock at 170 MHz\n            divr: Some(PllRDiv::DIV2),\n        });\n        config.rcc.mux.adc12sel = mux::Adcsel::SYS;\n        config.rcc.sys = Sysclk::PLL1_R;\n    }\n    let p = embassy_stm32::init(config);\n\n    info!(\"Hello World!\");\n\n    let mut adc = Adc::new(p.ADC1, Default::default());\n\n    let mut dma = p.DMA1_CH1;\n    let mut vrefint_channel = adc.enable_vrefint().degrade_adc();\n    let mut pa0 = p.PA0.degrade_adc();\n\n    loop {\n        adc.read(\n            dma.reborrow(),\n            Irqs,\n            [\n                (&mut vrefint_channel, SampleTime::CYCLES247_5),\n                (&mut pa0, SampleTime::CYCLES247_5),\n            ]\n            .into_iter(),\n            read_buffer,\n        )\n        .await;\n\n        let vrefint = read_buffer[0];\n        let measured = read_buffer[1];\n        info!(\"vrefint: {}\", vrefint);\n        info!(\"measured: {}\", measured);\n        Timer::after_millis(500).await;\n    }\n}\n"
  },
  {
    "path": "examples/stm32g4/src/bin/adc_injected_and_regular.rs",
    "content": "//! adc injected and regular conversions\n//!\n//! This example both regular and injected ADC conversions at the same time\n//! p:pa0 n:pa2\n\n#![no_std]\n#![no_main]\n\nuse core::cell::RefCell;\n\nuse defmt::info;\nuse embassy_stm32::adc::{Adc, AdcChannel as _, Exten, InjectedAdc, SampleTime};\nuse embassy_stm32::interrupt::typelevel::{ADC1_2, Interrupt};\nuse embassy_stm32::peripherals::ADC1;\nuse embassy_stm32::time::Hertz;\nuse embassy_stm32::timer::complementary_pwm::{ComplementaryPwm, Mms2};\nuse embassy_stm32::timer::low_level::CountingMode;\nuse embassy_stm32::triggers::TIM1_TRGO2;\nuse embassy_stm32::{Config, bind_interrupts, dma, interrupt, peripherals};\nuse embassy_sync::blocking_mutex::CriticalSectionMutex;\nuse {defmt_rtt as _, panic_probe as _};\n\nstatic ADC1_HANDLE: CriticalSectionMutex<RefCell<Option<InjectedAdc<ADC1, 1>>>> =\n    CriticalSectionMutex::new(RefCell::new(None));\n\nbind_interrupts!(struct Irqs {\n    DMA1_CHANNEL1 => dma::InterruptHandler<peripherals::DMA1_CH1>;\n});\n\n/// This example showcases how to use both regular ADC conversions with DMA and injected ADC\n/// conversions with ADC interrupt simultaneously. Both conversion types can be configured with\n/// different triggers and thanks to DMA it is possible to use the measurements in different task\n/// without needing to access the ADC peripheral.\n///\n/// If you don't need both regular and injected conversions the example code can easily be reworked\n/// to only include one of the ADC conversion types.\n#[embassy_executor::main]\nasync fn main(_spawner: embassy_executor::Spawner) {\n    // --- RCC config ---\n    let mut config = Config::default();\n    {\n        use embassy_stm32::rcc::*;\n        config.rcc.pll = Some(Pll {\n            source: PllSource::HSI,\n            prediv: PllPreDiv::DIV4,\n            mul: PllMul::MUL85,\n            divp: None,\n            divq: None,\n            divr: Some(PllRDiv::DIV2),\n        });\n        config.rcc.mux.adc12sel = mux::Adcsel::SYS;\n        config.rcc.sys = Sysclk::PLL1_R;\n    }\n    let p = embassy_stm32::init(config);\n\n    // In this example we use tim1_trgo2 event to trigger the ADC conversions\n    let tim1 = p.TIM1;\n    let pwm_freq = 1;\n    let mut pwm = ComplementaryPwm::new(\n        tim1,\n        None,\n        None,\n        None,\n        None,\n        None,\n        None,\n        None,\n        None,\n        Hertz::hz(pwm_freq),\n        CountingMode::EdgeAlignedUp,\n    );\n    pwm.set_master_output_enable(false);\n    // Mms2 is used to configure which timer event that is connected to tim1_trgo2.\n    // In this case we use the update event of the timer.\n    pwm.set_mms2(Mms2::UPDATE);\n\n    // Configure regular conversions with DMA\n    let adc1 = Adc::new(p.ADC1, Default::default());\n\n    let vrefint_channel = adc1.enable_vrefint().degrade_adc();\n    let pa0 = p.PC1.degrade_adc();\n    let regular_sequence = [\n        (vrefint_channel, SampleTime::CYCLES247_5),\n        (pa0, SampleTime::CYCLES247_5),\n    ]\n    .into_iter();\n\n    // Configurations of Injected ADC measurements\n    let pa2 = p.PA2.degrade_adc();\n    let injected_sequence = [(pa2, SampleTime::CYCLES247_5)];\n\n    // Configure DMA for retrieving regular ADC measurements\n    let dma1_ch1 = p.DMA1_CH1;\n    // Using buffer of double size means the half-full interrupts will generate at the expected rate\n    let mut readings = [0u16; 4];\n\n    let (mut ring_buffered_adc, injected_adc) = adc1.into_ring_buffered_and_injected(\n        dma1_ch1,\n        &mut readings,\n        Irqs,\n        regular_sequence,\n        TIM1_TRGO2,\n        Exten::RISING_EDGE,\n        injected_sequence,\n        TIM1_TRGO2,\n        Exten::RISING_EDGE,\n        true,\n    );\n\n    // Store ADC globally to allow access from ADC interrupt\n    critical_section::with(|cs| {\n        ADC1_HANDLE.borrow(cs).replace(Some(injected_adc));\n    });\n    // Enable interrupt for ADC1_2\n    unsafe { ADC1_2::enable() };\n\n    // Main loop for reading regular ADC measurements periodically\n    let mut data = [0u16; 2];\n    loop {\n        {\n            match ring_buffered_adc.read(&mut data).await {\n                Ok(n) => {\n                    defmt::info!(\"Regular ADC reading, VrefInt: {}, PA0: {}\", data[0], data[1]);\n                    defmt::info!(\"Remaining samples: {}\", n,);\n                }\n                Err(e) => {\n                    defmt::error!(\"DMA error: {:?}\", e);\n                    ring_buffered_adc.clear();\n                }\n            }\n        }\n    }\n}\n\n/// Use ADC1_2 interrupt to retrieve injected ADC measurements\n/// Interrupt must be unsafe as hardware can invoke it any-time. Critical sections ensure safety\n/// within the interrupt.\n#[interrupt]\nunsafe fn ADC1_2() {\n    critical_section::with(|cs| {\n        if let Some(injected_adc) = ADC1_HANDLE.borrow(cs).borrow_mut().as_mut() {\n            let injected_data = injected_adc.read_injected_samples();\n            info!(\"Injected reading of PA2: {}\", injected_data[0]);\n        }\n    });\n}\n"
  },
  {
    "path": "examples/stm32g4/src/bin/adc_oversampling.rs",
    "content": "//! adc oversampling example\n//!\n//! This example uses adc oversampling to achieve 16bit data\n\n#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_stm32::Config;\nuse embassy_stm32::adc::vals::{Rovsm, Trovs};\nuse embassy_stm32::adc::{Adc, AdcConfig, SampleTime};\nuse embassy_time::Timer;\nuse {defmt_rtt as _, panic_probe as _};\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let mut config = Config::default();\n    {\n        use embassy_stm32::rcc::*;\n        config.rcc.pll = Some(Pll {\n            source: PllSource::HSI,\n            prediv: PllPreDiv::DIV4,\n            mul: PllMul::MUL85,\n            divp: None,\n            divq: None,\n            // Main system clock at 170 MHz\n            divr: Some(PllRDiv::DIV2),\n        });\n        config.rcc.mux.adc12sel = mux::Adcsel::SYS;\n        config.rcc.sys = Sysclk::PLL1_R;\n    }\n    let mut p = embassy_stm32::init(config);\n\n    let mut config = AdcConfig::default();\n\n    // From https://www.st.com/resource/en/reference_manual/rm0440-stm32g4-series-advanced-armbased-32bit-mcus-stmicroelectronics.pdf\n    // page652 Oversampler\n    // Table 172. Maximum output results vs N and M. Grayed values indicates truncation\n    // 0x00 oversampling ratio X2\n    // 0x01 oversampling ratio X4\n    // 0x02 oversampling ratio X8\n    // 0x03 oversampling ratio X16\n    // 0x04 oversampling ratio X32\n    // 0x05 oversampling ratio X64\n    // 0x06 oversampling ratio X128\n    // 0x07 oversampling ratio X256\n    config.oversampling_ratio = Some(0x03); // ratio X3\n    config.oversampling_shift = Some(0b0000); // no shift\n    config.oversampling_mode = Some((Rovsm::RESUMED, Trovs::AUTOMATIC, true));\n\n    let mut adc = Adc::new(p.ADC1, config);\n\n    loop {\n        let measured = adc.blocking_read(&mut p.PA0, SampleTime::CYCLES6_5);\n        info!(\"data: 0x{:X}\", measured); //max 0xFFF0 -> 65520\n        Timer::after_millis(500).await;\n    }\n}\n"
  },
  {
    "path": "examples/stm32g4/src/bin/blinky.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_stm32::gpio::{Level, Output, Speed};\nuse embassy_time::Timer;\nuse {defmt_rtt as _, panic_probe as _};\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let p = embassy_stm32::init(Default::default());\n    info!(\"Hello World!\");\n\n    let mut led = Output::new(p.PA5, Level::High, Speed::Low);\n\n    loop {\n        info!(\"high\");\n        led.set_high();\n        Timer::after_millis(300).await;\n\n        info!(\"low\");\n        led.set_low();\n        Timer::after_millis(300).await;\n    }\n}\n"
  },
  {
    "path": "examples/stm32g4/src/bin/button.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse cortex_m_rt::entry;\nuse defmt::*;\nuse embassy_stm32::gpio::{Input, Pull};\nuse {defmt_rtt as _, panic_probe as _};\n\n#[entry]\nfn main() -> ! {\n    info!(\"Hello World!\");\n\n    let p = embassy_stm32::init(Default::default());\n\n    let button = Input::new(p.PC13, Pull::Down);\n\n    loop {\n        if button.is_high() {\n            info!(\"high\");\n        } else {\n            info!(\"low\");\n        }\n    }\n}\n"
  },
  {
    "path": "examples/stm32g4/src/bin/button_exti.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_stm32::exti::{self, ExtiInput};\nuse embassy_stm32::gpio::Pull;\nuse embassy_stm32::{bind_interrupts, interrupt};\nuse {defmt_rtt as _, panic_probe as _};\n\nbind_interrupts!(\n    pub struct Irqs{\n        EXTI15_10 => exti::InterruptHandler<interrupt::typelevel::EXTI15_10>;\n});\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let p = embassy_stm32::init(Default::default());\n    info!(\"Hello World!\");\n\n    let mut button = ExtiInput::new(p.PC13, p.EXTI13, Pull::Down, Irqs);\n\n    info!(\"Press the USER button...\");\n\n    loop {\n        button.wait_for_rising_edge().await;\n        info!(\"Pressed!\");\n        button.wait_for_falling_edge().await;\n        info!(\"Released!\");\n    }\n}\n"
  },
  {
    "path": "examples/stm32g4/src/bin/can.rs",
    "content": "#![no_std]\n#![no_main]\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_stm32::peripherals::*;\nuse embassy_stm32::time::Hertz;\nuse embassy_stm32::{Config, bind_interrupts, can};\nuse embassy_time::Timer;\nuse static_cell::StaticCell;\nuse {defmt_rtt as _, panic_probe as _};\n\nbind_interrupts!(struct Irqs {\n    FDCAN1_IT0 => can::IT0InterruptHandler<FDCAN1>;\n    FDCAN1_IT1 => can::IT1InterruptHandler<FDCAN1>;\n});\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let mut config = Config::default();\n    {\n        use embassy_stm32::rcc::*;\n        config.rcc.hse = Some(Hse {\n            freq: Hertz(24_000_000),\n            mode: HseMode::Oscillator,\n        });\n        config.rcc.pll = Some(Pll {\n            source: PllSource::HSE,\n            prediv: PllPreDiv::DIV6,\n            mul: PllMul::MUL85,\n            divp: None,\n            divq: Some(PllQDiv::DIV8), // 42.5 Mhz for fdcan.\n            divr: Some(PllRDiv::DIV2), // Main system clock at 170 MHz\n        });\n        config.rcc.mux.fdcansel = mux::Fdcansel::PLL1_Q;\n        config.rcc.sys = Sysclk::PLL1_R;\n    }\n    let peripherals = embassy_stm32::init(config);\n\n    let mut can = can::CanConfigurator::new(peripherals.FDCAN1, peripherals.PA11, peripherals.PA12, Irqs);\n\n    can.properties().set_extended_filter(\n        can::filter::ExtendedFilterSlot::_0,\n        can::filter::ExtendedFilter::accept_all_into_fifo1(),\n    );\n\n    // 250k bps\n    can.set_bitrate(250_000);\n\n    let use_fd = false;\n\n    // 1M bps\n    if use_fd {\n        can.set_fd_data_bitrate(1_000_000, false);\n    }\n\n    info!(\"Configured\");\n\n    let mut can = can.start(match use_fd {\n        true => can::OperatingMode::InternalLoopbackMode,\n        false => can::OperatingMode::NormalOperationMode,\n    });\n\n    let mut i = 0;\n    let mut last_read_ts = embassy_time::Instant::now();\n\n    loop {\n        let frame = can::frame::Frame::new_extended(0x123456F, &[i; 8]).unwrap();\n        info!(\"Writing frame\");\n\n        _ = can.write(&frame).await;\n\n        match can.read().await {\n            Ok(envelope) => {\n                let (ts, rx_frame) = (envelope.ts, envelope.frame);\n                let delta = (ts - last_read_ts).as_millis();\n                last_read_ts = ts;\n                info!(\n                    \"Rx: {} {:02x} --- {}ms\",\n                    rx_frame.header().len(),\n                    rx_frame.data()[0..rx_frame.header().len() as usize],\n                    delta,\n                )\n            }\n            Err(_err) => error!(\"Error in frame\"),\n        }\n\n        Timer::after_millis(250).await;\n\n        i += 1;\n        if i > 2 {\n            break;\n        }\n    }\n\n    // Use the FD API's even if we don't get FD packets.\n\n    loop {\n        if use_fd {\n            let frame = can::frame::FdFrame::new_extended(0x123456F, &[i; 16]).unwrap();\n            info!(\"Writing frame using FD API\");\n            _ = can.write_fd(&frame).await;\n        } else {\n            let frame = can::frame::FdFrame::new_extended(0x123456F, &[i; 8]).unwrap();\n            info!(\"Writing frame using FD API\");\n            _ = can.write_fd(&frame).await;\n        }\n\n        match can.read_fd().await {\n            Ok(envelope) => {\n                let (ts, rx_frame) = (envelope.ts, envelope.frame);\n                let delta = (ts - last_read_ts).as_millis();\n                last_read_ts = ts;\n                info!(\n                    \"Rx: {} {:02x} --- using FD API {}ms\",\n                    rx_frame.header().len(),\n                    rx_frame.data()[0..rx_frame.header().len() as usize],\n                    delta,\n                )\n            }\n            Err(_err) => error!(\"Error in frame\"),\n        }\n\n        Timer::after_millis(250).await;\n\n        i += 1;\n        if i > 4 {\n            break;\n        }\n    }\n    i = 0;\n    let (mut tx, mut rx, _props) = can.split();\n    // With split\n    loop {\n        let frame = can::frame::Frame::new_extended(0x123456F, &[i; 8]).unwrap();\n        info!(\"Writing frame\");\n        _ = tx.write(&frame).await;\n\n        match rx.read().await {\n            Ok(envelope) => {\n                let (ts, rx_frame) = (envelope.ts, envelope.frame);\n                let delta = (ts - last_read_ts).as_millis();\n                last_read_ts = ts;\n                info!(\n                    \"Rx: {} {:02x} --- {}ms\",\n                    rx_frame.header().len(),\n                    rx_frame.data()[0..rx_frame.header().len() as usize],\n                    delta,\n                )\n            }\n            Err(_err) => error!(\"Error in frame\"),\n        }\n\n        Timer::after_millis(250).await;\n\n        i += 1;\n\n        if i > 2 {\n            break;\n        }\n    }\n\n    let can = can::Can::join(tx, rx);\n\n    info!(\"\\n\\n\\nBuffered\\n\");\n    if use_fd {\n        static TX_BUF: StaticCell<can::TxFdBuf<8>> = StaticCell::new();\n        static RX_BUF: StaticCell<can::RxFdBuf<10>> = StaticCell::new();\n        let mut can = can.buffered_fd(\n            TX_BUF.init(can::TxFdBuf::<8>::new()),\n            RX_BUF.init(can::RxFdBuf::<10>::new()),\n        );\n        loop {\n            let frame = can::frame::FdFrame::new_extended(0x123456F, &[i; 16]).unwrap();\n            info!(\"Writing frame\");\n\n            _ = can.write(frame).await;\n\n            match can.read().await {\n                Ok(envelope) => {\n                    let (ts, rx_frame) = (envelope.ts, envelope.frame);\n                    let delta = (ts - last_read_ts).as_millis();\n                    last_read_ts = ts;\n                    info!(\n                        \"Rx: {} {:02x} --- {}ms\",\n                        rx_frame.header().len(),\n                        rx_frame.data()[0..rx_frame.header().len() as usize],\n                        delta,\n                    )\n                }\n                Err(_err) => error!(\"Error in frame\"),\n            }\n\n            Timer::after_millis(250).await;\n\n            i = i.wrapping_add(1);\n        }\n    } else {\n        static TX_BUF: StaticCell<can::TxBuf<8>> = StaticCell::new();\n        static RX_BUF: StaticCell<can::RxBuf<10>> = StaticCell::new();\n        let mut can = can.buffered(\n            TX_BUF.init(can::TxBuf::<8>::new()),\n            RX_BUF.init(can::RxBuf::<10>::new()),\n        );\n        loop {\n            let frame = can::frame::Frame::new_extended(0x123456F, &[i; 8]).unwrap();\n            info!(\"Writing frame\");\n\n            // You can use any of these approaches to send. The writer makes it\n            // easy to share sending from multiple tasks.\n            //_ = can.write(frame).await;\n            //can.writer().try_write(frame).unwrap();\n            can.writer().write(frame).await;\n\n            match can.read().await {\n                Ok(envelope) => {\n                    let (ts, rx_frame) = (envelope.ts, envelope.frame);\n                    let delta = (ts - last_read_ts).as_millis();\n                    last_read_ts = ts;\n                    info!(\n                        \"Rx: {} {:02x} --- {}ms\",\n                        rx_frame.header().len(),\n                        rx_frame.data()[0..rx_frame.header().len() as usize],\n                        delta,\n                    )\n                }\n                Err(_err) => error!(\"Error in frame\"),\n            }\n\n            Timer::after_millis(250).await;\n\n            i = i.wrapping_add(1);\n        }\n    }\n}\n"
  },
  {
    "path": "examples/stm32g4/src/bin/i2c_slave.rs",
    "content": "//! This example shows how to use an stm32 as both a master and a slave.\n#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_stm32::i2c::{Address, OwnAddresses, SlaveCommandKind};\nuse embassy_stm32::mode::Async;\nuse embassy_stm32::time::Hertz;\nuse embassy_stm32::{bind_interrupts, dma, i2c, peripherals};\nuse embassy_time::Timer;\nuse {defmt_rtt as _, panic_probe as _};\n\nbind_interrupts!(struct Irqs {\n    I2C1_ER => i2c::ErrorInterruptHandler<peripherals::I2C1>;\n    I2C1_EV => i2c::EventInterruptHandler<peripherals::I2C1>;\n    I2C2_ER => i2c::ErrorInterruptHandler<peripherals::I2C2>;\n    I2C2_EV => i2c::EventInterruptHandler<peripherals::I2C2>;\n    DMA1_CHANNEL1 => dma::InterruptHandler<peripherals::DMA1_CH1>;\n    DMA1_CHANNEL2 => dma::InterruptHandler<peripherals::DMA1_CH2>;\n    DMA1_CHANNEL3 => dma::InterruptHandler<peripherals::DMA1_CH3>;\n    DMA1_CHANNEL4 => dma::InterruptHandler<peripherals::DMA1_CH4>;\n});\n\nconst DEV_ADDR: u8 = 0x42;\n\n#[embassy_executor::task]\nasync fn device_task(mut dev: i2c::I2c<'static, Async, i2c::MultiMaster>) -> ! {\n    info!(\"Device start\");\n\n    let mut state = 0;\n\n    loop {\n        let mut buf = [0u8; 128];\n        match dev.listen().await {\n            Ok(i2c::SlaveCommand {\n                kind: SlaveCommandKind::Read,\n                address: Address::SevenBit(DEV_ADDR),\n            }) => match dev.respond_to_read(&[state]).await {\n                Ok(i2c::SendStatus::LeftoverBytes(x)) => info!(\"tried to write {} extra bytes\", x),\n                Ok(i2c::SendStatus::Done) => {}\n                Err(e) => error!(\"error while responding {}\", e),\n            },\n            Ok(i2c::SlaveCommand {\n                kind: SlaveCommandKind::Write,\n                address: Address::SevenBit(DEV_ADDR),\n            }) => match dev.respond_to_write(&mut buf).await {\n                Ok(len) => {\n                    info!(\"Device received write: {}\", buf[..len]);\n\n                    if match buf[0] {\n                        // Set the state\n                        0xC2 => {\n                            state = buf[1];\n                            true\n                        }\n                        // Reset State\n                        0xC8 => {\n                            state = 0;\n                            true\n                        }\n                        x => {\n                            error!(\"Invalid Write Read {:x}\", x);\n                            false\n                        }\n                    } {\n                        match dev.respond_to_read(&[state]).await {\n                            Ok(read_status) => info!(\n                                \"This read is part of a write/read transaction. The response read status {}\",\n                                read_status\n                            ),\n                            Err(i2c::Error::Timeout) => {\n                                info!(\"The device only performed a write and it not also do a read\")\n                            }\n                            Err(e) => error!(\"error while responding {}\", e),\n                        }\n                    }\n                }\n                Err(e) => error!(\"error while receiving {}\", e),\n            },\n            Ok(i2c::SlaveCommand { address, .. }) => {\n                defmt::unreachable!(\n                    \"The slave matched address: {}, which it was not configured for\",\n                    address\n                );\n            }\n            Err(e) => error!(\"{}\", e),\n        }\n    }\n}\n\n#[embassy_executor::task]\nasync fn controller_task(mut con: i2c::I2c<'static, Async, i2c::Master>) {\n    info!(\"Controller start\");\n\n    loop {\n        let mut resp_buff = [0u8; 1];\n        for i in 0..10 {\n            match con.write_read(DEV_ADDR, &[0xC2, i], &mut resp_buff).await {\n                Ok(_) => {\n                    info!(\"write_read response: {}\", resp_buff);\n                    defmt::assert_eq!(resp_buff[0], i);\n                }\n                Err(e) => error!(\"Error writing {}\", e),\n            }\n\n            Timer::after_millis(100).await;\n        }\n        match con.read(DEV_ADDR, &mut resp_buff).await {\n            Ok(_) => {\n                info!(\"read response: {}\", resp_buff);\n                // assert that the state is the last index that was written\n                defmt::assert_eq!(resp_buff[0], 9);\n            }\n            Err(e) => error!(\"Error writing {}\", e),\n        }\n        match con.write_read(DEV_ADDR, &[0xC8], &mut resp_buff).await {\n            Ok(_) => {\n                info!(\"write_read response: {}\", resp_buff);\n                // assert that the state has been reset\n                defmt::assert_eq!(resp_buff[0], 0);\n            }\n            Err(e) => error!(\"Error writing {}\", e),\n        }\n        Timer::after_millis(100).await;\n    }\n}\n\n#[embassy_executor::main]\nasync fn main(spawner: Spawner) {\n    let p = embassy_stm32::init(Default::default());\n    info!(\"Hello World!\");\n\n    let mut config = i2c::Config::default();\n    config.frequency = Hertz::khz(400);\n\n    let d_addr_config = i2c::SlaveAddrConfig {\n        addr: OwnAddresses::OA1(Address::SevenBit(DEV_ADDR)),\n        general_call: false,\n    };\n    let d_sda = p.PA8;\n    let d_scl = p.PA9;\n    let device =\n        i2c::I2c::new(p.I2C2, d_scl, d_sda, p.DMA1_CH1, p.DMA1_CH2, Irqs, config).into_slave_multimaster(d_addr_config);\n\n    spawner.spawn(unwrap!(device_task(device)));\n\n    let c_sda = p.PB8;\n    let c_scl = p.PB7;\n    let controller = i2c::I2c::new(p.I2C1, c_sda, c_scl, p.DMA1_CH3, p.DMA1_CH4, Irqs, config);\n\n    spawner.spawn(unwrap!(controller_task(controller)));\n}\n"
  },
  {
    "path": "examples/stm32g4/src/bin/i2s.rs",
    "content": "// This example is written for a nucleo-g491re board\n//\n// NOTE: This example outputs potentially loud audio. Please run responsibly.\n\n#![no_std]\n#![no_main]\n\nuse embassy_executor::Spawner;\nuse embassy_stm32::i2s::{Config, Format, I2S};\nuse embassy_stm32::{bind_interrupts, dma, peripherals};\nuse {defmt_rtt as _, panic_probe as _};\n\nbind_interrupts!(struct Irqs {\n    DMA1_CHANNEL1 => dma::InterruptHandler<peripherals::DMA1_CH1>;\n});\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let p = embassy_stm32::init(Default::default());\n\n    // stereo wavetable generation\n    let mut wavetable = [0u16; 1200];\n    for (i, frame) in wavetable.chunks_mut(2).enumerate() {\n        frame[0] = ((((i / 150) % 2) * 2048) as i16 - 1024) as u16;\n        frame[1] = ((((i / 100) % 2) * 2048) as i16 - 1024) as u16;\n    }\n\n    // i2s configuration\n    let mut dma_buffer = [0u16; 2400];\n\n    let mut i2s_config = Config::default();\n    i2s_config.format = Format::Data16Channel32;\n    let mut i2s = I2S::new_txonly(\n        p.SPI2,\n        p.PB15, // sd\n        p.PB12, // ws\n        p.PB13, // ck\n        p.PC6,\n        p.DMA1_CH1,\n        &mut dma_buffer,\n        Irqs,\n        i2s_config,\n    );\n    i2s.start();\n\n    loop {\n        i2s.write(&wavetable).await.ok();\n    }\n}\n"
  },
  {
    "path": "examples/stm32g4/src/bin/pll.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_stm32::Config;\nuse embassy_time::Timer;\nuse {defmt_rtt as _, panic_probe as _};\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let mut config = Config::default();\n    {\n        use embassy_stm32::rcc::*;\n        config.rcc.hsi = true;\n        config.rcc.pll = Some(Pll {\n            source: PllSource::HSI,\n            prediv: PllPreDiv::DIV4,\n            mul: PllMul::MUL85,\n            divp: None,\n            divq: None,\n            // Main system clock at 170 MHz\n            divr: Some(PllRDiv::DIV2),\n        });\n        config.rcc.sys = Sysclk::PLL1_R;\n    }\n    let _p = embassy_stm32::init(config);\n    info!(\"Hello World!\");\n\n    loop {\n        Timer::after_millis(1000).await;\n        info!(\"1s elapsed\");\n    }\n}\n"
  },
  {
    "path": "examples/stm32g4/src/bin/pwm.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_stm32::gpio::OutputType;\nuse embassy_stm32::time::khz;\nuse embassy_stm32::timer::simple_pwm::{PwmPin, SimplePwm};\nuse embassy_time::Timer;\nuse {defmt_rtt as _, panic_probe as _};\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let p = embassy_stm32::init(Default::default());\n    info!(\"Hello World!\");\n\n    let ch1_pin = PwmPin::new(p.PC0, OutputType::PushPull);\n    let mut pwm = SimplePwm::new(p.TIM1, Some(ch1_pin), None, None, None, khz(10), Default::default());\n    let mut ch1 = pwm.ch1();\n    ch1.enable();\n\n    info!(\"PWM initialized\");\n    info!(\"PWM max duty {}\", ch1.max_duty_cycle());\n\n    loop {\n        ch1.set_duty_cycle_fully_off();\n        Timer::after_millis(300).await;\n        ch1.set_duty_cycle_fraction(1, 4);\n        Timer::after_millis(300).await;\n        ch1.set_duty_cycle_fraction(1, 2);\n        Timer::after_millis(300).await;\n        ch1.set_duty_cycle(ch1.max_duty_cycle() - 1);\n        Timer::after_millis(300).await;\n    }\n}\n"
  },
  {
    "path": "examples/stm32g4/src/bin/usb_c_pd.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse defmt::{Format, error, info};\nuse embassy_executor::Spawner;\nuse embassy_stm32::ucpd::{self, CcPhy, CcPull, CcSel, CcVState, Ucpd};\nuse embassy_stm32::{Config, bind_interrupts, dma, peripherals};\nuse embassy_time::{Duration, with_timeout};\nuse {defmt_rtt as _, panic_probe as _};\n\nbind_interrupts!(struct Irqs {\n    UCPD1 => ucpd::InterruptHandler<peripherals::UCPD1>;\n    DMA1_CHANNEL1 => dma::InterruptHandler<peripherals::DMA1_CH1>;\n    DMA1_CHANNEL2 => dma::InterruptHandler<peripherals::DMA1_CH2>;\n});\n\n#[derive(Debug, Format)]\nenum CableOrientation {\n    Normal,\n    Flipped,\n    DebugAccessoryMode,\n}\n\n// Returns true when the cable\nasync fn wait_attached<T: ucpd::Instance>(cc_phy: &mut CcPhy<'_, T>) -> CableOrientation {\n    loop {\n        let (cc1, cc2) = cc_phy.vstate();\n        if cc1 == CcVState::LOWEST && cc2 == CcVState::LOWEST {\n            // Detached, wait until attached by monitoring the CC lines.\n            cc_phy.wait_for_vstate_change().await;\n            continue;\n        }\n\n        // Attached, wait for CC lines to be stable for tCCDebounce (100..200ms).\n        if with_timeout(Duration::from_millis(100), cc_phy.wait_for_vstate_change())\n            .await\n            .is_ok()\n        {\n            // State has changed, restart detection procedure.\n            continue;\n        };\n\n        // State was stable for the complete debounce period, check orientation.\n        return match (cc1, cc2) {\n            (_, CcVState::LOWEST) => CableOrientation::Normal,  // CC1 connected\n            (CcVState::LOWEST, _) => CableOrientation::Flipped, // CC2 connected\n            _ => CableOrientation::DebugAccessoryMode,          // Both connected (special cable)\n        };\n    }\n}\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let mut config = Config::default();\n    config.enable_ucpd1_dead_battery = true;\n    let p = embassy_stm32::init(config);\n\n    info!(\"Hello World!\");\n\n    let mut ucpd = Ucpd::new(p.UCPD1, Irqs {}, p.PB6, p.PB4, Default::default());\n    ucpd.cc_phy().set_pull(CcPull::Sink);\n\n    info!(\"Waiting for USB connection...\");\n    let cable_orientation = wait_attached(ucpd.cc_phy()).await;\n    info!(\"USB cable connected, orientation: {}\", cable_orientation);\n\n    let cc_sel = match cable_orientation {\n        CableOrientation::Normal => {\n            info!(\"Starting PD communication on CC1 pin\");\n            CcSel::CC1\n        }\n        CableOrientation::Flipped => {\n            info!(\"Starting PD communication on CC2 pin\");\n            CcSel::CC2\n        }\n        CableOrientation::DebugAccessoryMode => panic!(\"No PD communication in DAM\"),\n    };\n    let (_cc_phy, mut pd_phy) = ucpd.split_pd_phy(p.DMA1_CH1, p.DMA1_CH2, Irqs, cc_sel);\n\n    loop {\n        // Enough space for the longest non-extended data message.\n        let mut buf = [0_u8; 30];\n        match pd_phy.receive(buf.as_mut()).await {\n            Ok(n) => info!(\"USB PD RX: {=[u8]:?}\", &buf[..n]),\n            Err(e) => error!(\"USB PD RX: {}\", e),\n        }\n    }\n}\n"
  },
  {
    "path": "examples/stm32g4/src/bin/usb_serial.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse defmt::{panic, *};\nuse embassy_executor::Spawner;\nuse embassy_futures::join::join;\nuse embassy_stm32::time::Hertz;\nuse embassy_stm32::usb::{self, Driver, Instance};\nuse embassy_stm32::{Config, bind_interrupts, peripherals};\nuse embassy_usb::Builder;\nuse embassy_usb::class::cdc_acm::{CdcAcmClass, State};\nuse embassy_usb::driver::EndpointError;\nuse {defmt_rtt as _, panic_probe as _};\n\nbind_interrupts!(struct Irqs {\n    USB_LP => usb::InterruptHandler<peripherals::USB>;\n});\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let mut config = Config::default();\n    {\n        use embassy_stm32::rcc::*;\n        // Sets up the Clock Recovery System (CRS) to use the USB SOF to trim the HSI48 oscillator.\n        config.rcc.hsi48 = Some(Hsi48Config { sync_from_usb: true });\n        config.rcc.hse = Some(Hse {\n            freq: Hertz(8_000_000),\n            mode: HseMode::Oscillator,\n        });\n        config.rcc.pll = Some(Pll {\n            source: PllSource::HSE,\n            prediv: PllPreDiv::DIV2,\n            mul: PllMul::MUL72,\n            divp: None,\n            divq: Some(PllQDiv::DIV6), // 48mhz\n            divr: Some(PllRDiv::DIV2), // Main system clock at 144 MHz\n        });\n        config.rcc.sys = Sysclk::PLL1_R;\n        config.rcc.boost = true; // BOOST!\n        config.rcc.mux.clk48sel = mux::Clk48sel::HSI48;\n        //config.rcc.mux.clk48sel = mux::Clk48sel::PLL1_Q; // uncomment to use PLL1_Q instead.\n    }\n    let p = embassy_stm32::init(config);\n\n    info!(\"Hello World!\");\n\n    let driver = Driver::new(p.USB, Irqs, p.PA12, p.PA11);\n\n    let mut config = embassy_usb::Config::new(0xc0de, 0xcafe);\n    config.manufacturer = Some(\"Embassy\");\n    config.product = Some(\"USB-Serial Example\");\n    config.serial_number = Some(\"123456\");\n\n    let mut config_descriptor = [0; 256];\n    let mut bos_descriptor = [0; 256];\n    let mut control_buf = [0; 64];\n\n    let mut state = State::new();\n\n    let mut builder = Builder::new(\n        driver,\n        config,\n        &mut config_descriptor,\n        &mut bos_descriptor,\n        &mut [], // no msos descriptors\n        &mut control_buf,\n    );\n\n    let mut class = CdcAcmClass::new(&mut builder, &mut state, 64);\n\n    let mut usb = builder.build();\n\n    let usb_fut = usb.run();\n\n    let echo_fut = async {\n        loop {\n            class.wait_connection().await;\n            info!(\"Connected\");\n            let _ = echo(&mut class).await;\n            info!(\"Disconnected\");\n        }\n    };\n\n    join(usb_fut, echo_fut).await;\n}\n\nstruct Disconnected {}\n\nimpl From<EndpointError> for Disconnected {\n    fn from(val: EndpointError) -> Self {\n        match val {\n            EndpointError::BufferOverflow => panic!(\"Buffer overflow\"),\n            EndpointError::Disabled => Disconnected {},\n        }\n    }\n}\n\nasync fn echo<'d, T: Instance + 'd>(class: &mut CdcAcmClass<'d, Driver<'d, T>>) -> Result<(), Disconnected> {\n    let mut buf = [0; 64];\n    loop {\n        let n = class.read_packet(&mut buf).await?;\n        let data = &buf[..n];\n        info!(\"data: {:x}\", data);\n        class.write_packet(data).await?;\n    }\n}\n"
  },
  {
    "path": "examples/stm32g474/.cargo/config.toml",
    "content": "[target.'cfg(all(target_arch = \"arm\", target_os = \"none\"))']\nrunner = \"probe-rs run --chip STM32G474RETx\"\n\n[build]\ntarget = \"thumbv7em-none-eabi\"\n\n[env]\nDEFMT_LOG = \"trace\"\n"
  },
  {
    "path": "examples/stm32g474/Cargo.toml",
    "content": "[package]\nedition = \"2024\"\nname = \"embassy-stm32g474-examples\"\nversion = \"0.1.0\"\nlicense = \"MIT OR Apache-2.0\"\npublish = false\n\n[dependencies]\nembassy-stm32 = { path = \"../../embassy-stm32\", features = [ \"defmt\", \"time-driver-any\", \"stm32g474re\", \"memory-x\", \"unstable-pac\", \"exti\", \"dual-bank\", \"stm32-hrtim\"]  }\nembassy-sync = { path = \"../../embassy-sync\", features = [\"defmt\"] }\nembassy-executor = { path = \"../../embassy-executor\", features = [\"platform-cortex-m\", \"executor-thread\", \"defmt\"] }\nembassy-time = { path = \"../../embassy-time\", features = [\"defmt\", \"defmt-timestamp-uptime\", \"tick-hz-32_768\"] }\nembassy-futures = { path = \"../../embassy-futures\" }\n\ndefmt = \"1.0.1\"\ndefmt-rtt = \"1.0.0\"\n\ncortex-m = { version = \"0.7.6\", features = [\"critical-section-single-core\"] }\ncortex-m-rt = \"0.7.0\"\npanic-probe = { version = \"1.0.0\", features = [\"print-defmt\"] }\n\n[profile.release]\ndebug = 2\n\n[package.metadata.embassy]\nbuild = [\n  { target = \"thumbv7em-none-eabi\", artifact-dir = \"out/examples/stm32g474\" }\n]\n"
  },
  {
    "path": "examples/stm32g474/build.rs",
    "content": "fn main() {\n    println!(\"cargo:rustc-link-arg-bins=--nmagic\");\n    println!(\"cargo:rustc-link-arg-bins=-Tlink.x\");\n    println!(\"cargo:rustc-link-arg-bins=-Tdefmt.x\");\n}\n"
  },
  {
    "path": "examples/stm32g474/src/bin/comp.rs",
    "content": "//! Comparator (COMP) example for STM32G474.\n//!\n//! Demonstrates using the comparator peripheral to compare an analog input\n//! against an internal reference (Vref ~ 1.2V).\n//!\n//! Hardware setup:\n//! - Connect a variable voltage (0-3.3V) to PA7 (COMP2 INP0)\n//!\n//! The comparator will read HIGH when PA7 > Vref, LOW otherwise.\n\n#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_stm32::comp::{self, Comp, Config, InvertingInput};\nuse embassy_stm32::{Config as SysConfig, bind_interrupts};\nuse embassy_time::Timer;\nuse {defmt_rtt as _, panic_probe as _};\n\nbind_interrupts!(struct Irqs {\n    COMP1_2_3 => comp::InterruptHandler<embassy_stm32::peripherals::COMP2>;\n});\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let p = embassy_stm32::init(SysConfig::default());\n    info!(\"COMP example starting!\");\n\n    let mut cfg = Config::default();\n    cfg.inverting_input = InvertingInput::Vref;\n\n    let mut comp2 = Comp::new(p.COMP2, p.PA7, Irqs, cfg);\n    comp2.enable();\n\n    info!(\"Comparator configured. Comparing PA7 against Vref (~1.2V)\");\n\n    // Polling\n    for _ in 0..5 {\n        info!(\"output: {}\", comp2.output_level());\n        Timer::after_millis(500).await;\n    }\n\n    // Async edge detection\n    info!(\"Waiting for edges...\");\n    loop {\n        comp2.wait_for_rising_edge().await;\n        info!(\"Rising edge!\");\n        Timer::after_millis(100).await;\n\n        comp2.wait_for_falling_edge().await;\n        info!(\"Falling edge!\");\n        Timer::after_millis(100).await;\n    }\n}\n"
  },
  {
    "path": "examples/stm32g474/src/bin/flash_async.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse defmt::{info, unwrap};\nuse embassy_executor::Spawner;\nuse embassy_stm32::flash::{Flash, InterruptHandler};\nuse embassy_stm32::gpio::{AnyPin, Level, Output, Speed};\nuse embassy_stm32::{Peri, bind_interrupts};\nuse embassy_time::Timer;\nuse {defmt_rtt as _, panic_probe as _};\n\nbind_interrupts!(struct Irqs {\n    FLASH => InterruptHandler;\n});\n\n#[embassy_executor::main]\nasync fn main(spawner: Spawner) {\n    let p = embassy_stm32::init(Default::default());\n    info!(\"Hello Flash!\");\n\n    let mut f = Flash::new(p.FLASH, Irqs);\n\n    // Led should blink uninterrupted during erase operation\n    spawner.spawn(unwrap!(blinky(p.PA5.into())));\n\n    // Test on bank 2 so the CPU doesn't stall (code runs from bank 1).\n    // G474RE in dual-bank mode: bank 2 starts at 256KB offset.\n    // Erase 4KB (2 pages at 2KB each in dual-bank mode).\n    test_flash(&mut f, 256 * 1024, 4 * 1024).await;\n}\n\n#[embassy_executor::task]\nasync fn blinky(p: Peri<'static, AnyPin>) {\n    let mut led = Output::new(p, Level::High, Speed::Low);\n\n    loop {\n        info!(\"high\");\n        led.set_high();\n        Timer::after_millis(300).await;\n\n        info!(\"low\");\n        led.set_low();\n        Timer::after_millis(300).await;\n    }\n}\n\nasync fn test_flash(f: &mut Flash<'_>, offset: u32, size: u32) {\n    info!(\"Testing offset: {=u32:#X}, size: {=u32:#X}\", offset, size);\n\n    info!(\"Reading...\");\n    let mut buf = [0u8; 32];\n    unwrap!(f.blocking_read(offset, &mut buf));\n    info!(\"Read: {=[u8]:x}\", buf);\n\n    info!(\"Erasing...\");\n    unwrap!(f.erase(offset, offset + size).await);\n\n    info!(\"Reading...\");\n    let mut buf = [0u8; 32];\n    unwrap!(f.blocking_read(offset, &mut buf));\n    info!(\"Read after erase: {=[u8]:x}\", buf);\n\n    info!(\"Writing...\");\n    unwrap!(\n        f.write(\n            offset,\n            &[\n                1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28,\n                29, 30, 31, 32\n            ]\n        )\n        .await\n    );\n\n    info!(\"Reading...\");\n    let mut buf = [0u8; 32];\n    unwrap!(f.blocking_read(offset, &mut buf));\n    info!(\"Read: {=[u8]:x}\", buf);\n    assert_eq!(\n        &buf[..],\n        &[\n            1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29,\n            30, 31, 32\n        ]\n    );\n\n    info!(\"Flash async test passed!\");\n}\n"
  },
  {
    "path": "examples/stm32g474/src/bin/hrtim.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_stm32::gpio::Speed;\nuse embassy_stm32::hrtim::stm32_hrtim::compare_register::HrCompareRegister;\nuse embassy_stm32::hrtim::stm32_hrtim::output::HrOutput;\nuse embassy_stm32::hrtim::stm32_hrtim::timer::HrTimer;\nuse embassy_stm32::hrtim::stm32_hrtim::{HrParts, HrPwmAdvExt, PreloadSource};\nuse embassy_stm32::hrtim::{HrControltExt, HrPwmBuilderExt, Parts};\nuse embassy_stm32::{Config, hrtim};\nuse embassy_time::Timer;\nuse {defmt_rtt as _, panic_probe as _};\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let mut config = Config::default();\n    {\n        // Set system frequency to 16MHz * 15/1/2 = 120MHz\n        // This would lead to HrTim running at 120MHz * 32 = 3.84GHz...\n        use embassy_stm32::rcc::*;\n        config.rcc.hsi = true;\n        config.rcc.pll = Some(Pll {\n            source: PllSource::HSI,\n            divp: None,\n            divq: None,\n            divr: Some(PllRDiv::DIV2),\n            prediv: PllPreDiv::DIV1,\n            mul: PllMul::MUL15,\n        });\n        config.rcc.sys = Sysclk::PLL1_R;\n    }\n    let p = embassy_stm32::init(config);\n\n    info!(\"Hello World!\");\n\n    let pin1 = hrtim::Pin {\n        pin: p.PA8,\n        speed: Speed::Low,\n    };\n    let pin2 = hrtim::Pin {\n        pin: p.PA9,\n        speed: Speed::Low,\n    };\n\n    // ...with a prescaler of 4 this gives us a HrTimer with a tick rate of 960MHz\n    // With max the max period set, this would be 960MHz/2^16 ~= 14.6kHz...\n    let prescaler = hrtim::Pscl4;\n\n    let Parts { control, tima, .. } = p.HRTIM1.hr_control();\n    let (control, ..) = control.wait_for_calibration();\n    let mut control = control.constrain();\n\n    //        .               .               .               .\n    //        .  30%          .               .               .\n    //        .----           .               .----           .\n    //pin1    |    |          .               |    |          .\n    //        |    |          .               |    |          .\n    // --------    ----------------------------    --------------------\n    //        .               .----           .               .----\n    //pin2    .               |    |          .               |    |\n    //        .               |    |          .               |    |\n    // ------------------------    ----------------------------    ----\n    //        .               .               .               .\n    //        .               .               .               .\n\n    let HrParts {\n        mut timer,\n        mut cr1,\n        mut out1,\n        mut out2,\n        ..\n    } = tima\n        .pwm_advanced(pin1, pin2)\n        .prescaler(prescaler)\n        .period(0xFFFF)\n        .push_pull_mode(true) // Set push pull mode, out1 and out2 are\n        // alternated every period with one being\n        // inactive and the other getting to output its wave form\n        // as normal\n        .preload(PreloadSource::OnRepetitionUpdate)\n        .finalize(&mut control);\n\n    out1.enable_rst_event(&cr1); // Set low on compare match with cr1\n    out2.enable_rst_event(&cr1);\n\n    out1.enable_set_event(&timer); // Set high at new period\n    out2.enable_set_event(&timer);\n\n    info!(\"pwm constructed\");\n\n    out1.enable();\n    out2.enable();\n    timer.start(&mut control.control);\n\n    // Step frequency from 14.6kHz to about 146kHz(half of that when only looking at one pin)\n    for i in 1..=10 {\n        let new_period = u16::MAX / i;\n\n        cr1.set_duty(new_period / 3);\n        timer.set_period(new_period);\n\n        Timer::after_millis(1).await;\n    }\n\n    out1.disable();\n    out2.disable();\n\n    info!(\"end program\");\n\n    cortex_m::asm::bkpt();\n}\n"
  },
  {
    "path": "examples/stm32g474/src/bin/hrtim_master.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_stm32::gpio::Speed;\nuse embassy_stm32::hrtim::stm32_hrtim::compare_register::HrCompareRegister;\nuse embassy_stm32::hrtim::stm32_hrtim::output::{self, HrOutput};\nuse embassy_stm32::hrtim::stm32_hrtim::timer::{HrSlaveTimer, HrTimer};\nuse embassy_stm32::hrtim::stm32_hrtim::{HrPwmAdvExt, MasterPreloadSource, PreloadSource};\nuse embassy_stm32::hrtim::{self, HrControltExt, HrPwmBuilderExt};\nuse {defmt_rtt as _, panic_probe as _};\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let mut config = embassy_stm32::Config::default();\n    {\n        // Set system frequency to 16MHz * 15/1/2 = 120MHz\n        // This would lead to HrTim running at 120MHz * 32 = 3.84GHz...\n        use embassy_stm32::rcc::*;\n        config.rcc.hsi = true;\n        config.rcc.pll = Some(Pll {\n            source: PllSource::HSI,\n            divp: None,\n            divq: None,\n            divr: Some(PllRDiv::DIV2),\n            prediv: PllPreDiv::DIV1,\n            mul: PllMul::MUL15,\n        });\n        config.rcc.sys = Sysclk::PLL1_R;\n    }\n    let p = embassy_stm32::init(config);\n\n    info!(\"Hello World!\");\n\n    let pin_lh_hs = hrtim::Pin {\n        pin: p.PB14,\n        speed: Speed::Low,\n    };\n    let pin_lh_ls = hrtim::Pin {\n        pin: p.PB15,\n        speed: Speed::Low,\n    };\n    let pin_rh_hs = hrtim::Pin {\n        pin: p.PB12,\n        speed: Speed::Low,\n    };\n    let pin_rh_ls = hrtim::Pin {\n        pin: p.PB13,\n        speed: Speed::Low,\n    };\n\n    let prescaler = hrtim::Pscl4;\n\n    let hrtim::Parts {\n        control,\n        master,\n        timc,\n        timd,\n        ..\n    } = p.HRTIM1.hr_control();\n    let (control, ..) = control.wait_for_calibration();\n    let mut control = control.constrain();\n\n    let mut master_timer = master\n        .pwm_advanced(output::NoPin, output::NoPin)\n        .prescaler(prescaler)\n        .preload(MasterPreloadSource::OnMasterRepetitionUpdate)\n        .period(0xFFFF)\n        .finalize(&mut control);\n\n    let mut bridge_left = timd\n        .pwm_advanced(pin_lh_hs, pin_lh_ls)\n        .prescaler(prescaler)\n        .period(0xFFFF)\n        .preload(PreloadSource::OnMasterTimerUpdate)\n        .finalize(&mut control);\n\n    let mut bridge_right = timc\n        .pwm_advanced(pin_rh_hs, pin_rh_ls)\n        .prescaler(prescaler)\n        .period(0xFFFF)\n        .preload(PreloadSource::OnMasterTimerUpdate)\n        .finalize(&mut control);\n\n    //            .               .               .               .\n    //            .  33%          .               .               .\n    //            .----           .----           .----           .----\n    //pin_lh_hs   |    |          |    |          |    |          |    |\n    //            |    |          |    |          |    |          |    |\n    //            -    ------------    ------------    ------------    ----\n    //            .     ----------.     ----------.     ----------.     ---\n    //pin_lh_ls   .    |          |    |          |    |          |    |\n    //            .    |          |    |          |    |          |    |\n    //            ------          ------          ------          ------\n    //            .     ----------.     ----------.     ----------.     ---\n    //pin_rh_hs   .    |          |    |          |    |          |    |\n    //            .    |          |    |          |    |          |    |\n    //            ------          ------          ------          ------\n    //            .----           .----           .----           .----\n    //pin_rh_ls   |    |          |    |          |    |          |    |\n    //            |    |          |    |          |    |          |    |\n    //            -    ------------    ------------    ------------    ----\n    //            .               .               .               .\n    //            .               .               .               .\n\n    bridge_left.timer.enable_reset_event(&master_timer.timer);\n    bridge_right.timer.enable_reset_event(&master_timer.timer);\n\n    bridge_left.out1.enable_set_event(&bridge_left.timer);\n    bridge_left.out2.enable_set_event(&bridge_left.cr1);\n    bridge_left.out1.enable_rst_event(&bridge_left.cr1);\n    bridge_left.out2.enable_rst_event(&bridge_left.timer);\n\n    bridge_right.out1.enable_set_event(&bridge_right.cr1);\n    bridge_right.out2.enable_set_event(&bridge_right.timer);\n    bridge_right.out1.enable_rst_event(&bridge_right.timer);\n    bridge_right.out2.enable_rst_event(&bridge_right.cr1);\n\n    info!(\"pwm constructed\");\n\n    bridge_left.timer.set_period(u16::MAX);\n    bridge_left.cr1.set_duty(0);\n    bridge_right.timer.set_period(u16::MAX);\n    bridge_right.cr1.set_duty(0);\n\n    bridge_left.out1.enable();\n    bridge_left.out2.enable();\n    bridge_right.out1.enable();\n    bridge_right.out2.enable();\n\n    control.control.start_stop_timers(|w| {\n        w.start(&mut master_timer.timer)\n            .start(&mut bridge_left.timer)\n            .start(&mut bridge_right.timer)\n    });\n\n    loop {\n        // Step duty cycle\n        for i in 1..=u16::MAX {\n            bridge_left.cr1.set_duty(i);\n            bridge_right.cr1.set_duty(i);\n        }\n        for i in (1..=u16::MAX).rev() {\n            bridge_left.cr1.set_duty(i);\n            bridge_right.cr1.set_duty(i);\n        }\n    }\n}\n"
  },
  {
    "path": "examples/stm32g474/src/bin/pwm_input_async.rs",
    "content": "#![no_std]\n#![no_main]\n\n//! PWM Input capture example\n//!\n//! This example reads the period and duty cycle of a square signal on PA6 using TIM3.\n//!\n//! On the NUCLEO-G474RE board, connect PA5 and PA6. (SCK/D13 on CN5 and pin 13 on CN10)\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_stm32::gpio::{AnyPin, Level, Output, Pull, Speed};\nuse embassy_stm32::time::khz;\nuse embassy_stm32::timer::pwm_input::PwmInput;\nuse embassy_stm32::{Peri, bind_interrupts, peripherals, timer};\nuse embassy_time::Timer;\nuse {defmt_rtt as _, panic_probe as _};\n\nbind_interrupts!(struct Irqs {\n    TIM3 => timer::CaptureCompareInterruptHandler<peripherals::TIM3>;\n});\n\n#[embassy_executor::main]\nasync fn main(spawner: Spawner) {\n    let p = embassy_stm32::init(Default::default());\n    info!(\"Hello World!\");\n\n    spawner.spawn(unwrap!(blinky(p.PA5.into())));\n\n    let mut pwm_input = PwmInput::new_ch1(p.TIM3, p.PA6, Irqs, Pull::None, khz(100));\n    pwm_input.enable();\n\n    loop {\n        let period_ticks = pwm_input.wait_for_period().await;\n        let period = period_ticks as f32 / 100000.0;\n        let width_ticks = pwm_input.get_width_ticks();\n        let duty_cycle = pwm_input.get_duty_cycle();\n\n        info!(\n            \"period: {} period ticks: {}, width ticks: {}, duty cycle: {}\",\n            period, period_ticks, width_ticks, duty_cycle\n        );\n    }\n}\n\n#[embassy_executor::task]\nasync fn blinky(p: Peri<'static, AnyPin>) {\n    let mut led = Output::new(p, Level::High, Speed::Low);\n\n    loop {\n        info!(\"high\");\n        led.set_high();\n        Timer::after_millis(300).await;\n\n        info!(\"low\");\n        led.set_low();\n        Timer::after_millis(300).await;\n    }\n}\n"
  },
  {
    "path": "examples/stm32h5/.cargo/config.toml",
    "content": "[target.thumbv8m.main-none-eabihf]\nrunner = 'probe-rs run --chip STM32H563ZITx'\n\n[build]\ntarget = \"thumbv8m.main-none-eabihf\"\n\n[env]\nDEFMT_LOG = \"trace\"\n"
  },
  {
    "path": "examples/stm32h5/Cargo.toml",
    "content": "[package]\nedition = \"2024\"\nname = \"embassy-stm32h5-examples\"\nversion = \"0.1.0\"\nlicense = \"MIT OR Apache-2.0\"\npublish = false\n\n[dependencies]\n# Change stm32h563zi to your chip name, if necessary.\nembassy-stm32 = { version = \"0.6.0\", path = \"../../embassy-stm32\", features = [\"defmt\", \"stm32h563zi\", \"memory-x\", \"time-driver-any\", \"exti\", \"unstable-pac\", \"cyw43\"] }\nembassy-sync = { version = \"0.8.0\", path = \"../../embassy-sync\", features = [\"defmt\"] }\nembassy-executor = { version = \"0.10.0\", path = \"../../embassy-executor\", features = [\"platform-cortex-m\", \"executor-thread\", \"defmt\"] }\nembassy-time = { version = \"0.5.1\", path = \"../../embassy-time\", features = [\"defmt\", \"defmt-timestamp-uptime\", \"tick-hz-32_768\"] }\nembassy-net = { version = \"0.9.0\", path = \"../../embassy-net\", features = [\"defmt\", \"tcp\", \"dhcpv4\", \"medium-ethernet\", \"proto-ipv6\"] }\nembassy-usb = { version = \"0.6.0\", path = \"../../embassy-usb\", features = [\"defmt\"] }\nembassy-futures = { version = \"0.1.2\", path = \"../../embassy-futures\" }\ncyw43 = { version = \"0.7.0\", path = \"../../cyw43\", features = [\"defmt\"] }\n\ndefmt = \"1.0.1\"\ndefmt-rtt = \"1.0.0\"\n\ncortex-m = { version = \"0.7.6\", features = [\"inline-asm\", \"critical-section-single-core\"] }\ncortex-m-rt = \"0.7.0\"\nembedded-hal = \"0.2.6\"\nembedded-hal-1 = { package = \"embedded-hal\", version = \"1.0\" }\nembedded-hal-async = { version = \"1.0\" }\nembedded-io-async = { version = \"0.7.0\" }\nembedded-nal-async = \"0.9.0\"\npanic-probe = { version = \"1.0.0\", features = [\"print-defmt\"] }\nheapless = { version = \"0.9\", default-features = false }\ncritical-section = \"1.1\"\nmicromath = \"2.0.0\"\nstm32-fmc = \"0.4.0\"\nembedded-storage = \"0.3.1\"\nstatic_cell = \"2\"\n\n# cargo build/run\n[profile.dev]\ncodegen-units = 1\ndebug = 2\ndebug-assertions = true # <-\nincremental = false\nopt-level = 3 # <-\noverflow-checks = true # <-\n\n# cargo test\n[profile.test]\ncodegen-units = 1\ndebug = 2\ndebug-assertions = true # <-\nincremental = false\nopt-level = 3 # <-\noverflow-checks = true # <-\n\n# cargo build/run --release\n[profile.release]\ncodegen-units = 1\ndebug = 2\ndebug-assertions = false # <-\nincremental = false\nlto = 'fat'\nopt-level = 3 # <-\noverflow-checks = false # <-\n\n# cargo test --release\n[profile.bench]\ncodegen-units = 1\ndebug = 2\ndebug-assertions = false # <-\nincremental = false\nlto = 'fat'\nopt-level = 3 # <-\noverflow-checks = false # <-\n\n[package.metadata.embassy]\nbuild = [\n  { target = \"thumbv8m.main-none-eabihf\", artifact-dir = \"out/examples/stm32h5\" }\n]\n"
  },
  {
    "path": "examples/stm32h5/build.rs",
    "content": "fn main() {\n    println!(\"cargo:rustc-link-arg-bins=--nmagic\");\n    println!(\"cargo:rustc-link-arg-bins=-Tlink.x\");\n    println!(\"cargo:rustc-link-arg-bins=-Tdefmt.x\");\n}\n"
  },
  {
    "path": "examples/stm32h5/src/bin/adc.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_stm32::Config;\nuse embassy_stm32::adc::{Adc, SampleTime};\nuse embassy_time::Timer;\nuse {defmt_rtt as _, panic_probe as _};\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let mut config = Config::default();\n    {\n        use embassy_stm32::rcc::*;\n        config.rcc.hsi = Some(HSIPrescaler::DIV1);\n        config.rcc.csi = true;\n        config.rcc.pll1 = Some(Pll {\n            source: PllSource::HSI,\n            prediv: PllPreDiv::DIV4,\n            mul: PllMul::MUL25,\n            divp: Some(PllDiv::DIV2),\n            divq: Some(PllDiv::DIV4), // SPI1 cksel defaults to pll1_q\n            divr: None,\n        });\n        config.rcc.pll2 = Some(Pll {\n            source: PllSource::HSI,\n            prediv: PllPreDiv::DIV4,\n            mul: PllMul::MUL25,\n            divp: None,\n            divq: None,\n            divr: Some(PllDiv::DIV4), // 100mhz\n        });\n        config.rcc.sys = Sysclk::PLL1_P; // 200 Mhz\n        config.rcc.ahb_pre = AHBPrescaler::DIV1; // 200 Mhz\n        config.rcc.apb1_pre = APBPrescaler::DIV2; // 100 Mhz\n        config.rcc.apb2_pre = APBPrescaler::DIV2; // 100 Mhz\n        config.rcc.apb3_pre = APBPrescaler::DIV2; // 100 Mhz\n        config.rcc.voltage_scale = VoltageScale::Scale1;\n        config.rcc.mux.adcdacsel = mux::Adcdacsel::PLL2_R;\n    }\n    let mut p = embassy_stm32::init(config);\n\n    info!(\"Hello World!\");\n\n    let mut adc = Adc::new(p.ADC1);\n\n    let mut vrefint_channel = adc.enable_vrefint();\n\n    loop {\n        let vrefint = adc.blocking_read(&mut vrefint_channel, SampleTime::CYCLES24_5);\n        info!(\"vrefint: {}\", vrefint);\n        let measured = adc.blocking_read(&mut p.PA0, SampleTime::CYCLES24_5);\n        info!(\"measured: {}\", measured);\n        Timer::after_millis(500).await;\n    }\n}\n"
  },
  {
    "path": "examples/stm32h5/src/bin/adc_dma.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_stm32::adc::{self, Adc, AdcChannel, RxDma, SampleTime};\nuse embassy_stm32::peripherals::{ADC1, ADC2, GPDMA1_CH0, GPDMA1_CH1, PA0, PA1, PA2, PA3};\nuse embassy_stm32::{Config, Peri, bind_interrupts, dma, interrupt};\nuse embassy_time::{Duration, Instant, Ticker};\nuse {defmt_rtt as _, panic_probe as _};\n\nbind_interrupts!(struct Irqs {\n    GPDMA1_CHANNEL0 => dma::InterruptHandler<GPDMA1_CH0>;\n    GPDMA1_CHANNEL1 => dma::InterruptHandler<GPDMA1_CH1>;\n});\n\n#[embassy_executor::main]\nasync fn main(spawner: Spawner) {\n    let mut config = Config::default();\n    {\n        use embassy_stm32::rcc::*;\n        config.rcc.hsi = Some(HSIPrescaler::DIV1);\n        config.rcc.csi = true;\n        config.rcc.pll1 = Some(Pll {\n            source: PllSource::HSI,\n            prediv: PllPreDiv::DIV4,\n            mul: PllMul::MUL25,\n            divp: Some(PllDiv::DIV2),\n            divq: Some(PllDiv::DIV4), // SPI1 cksel defaults to pll1_q\n            divr: None,\n        });\n        config.rcc.pll2 = Some(Pll {\n            source: PllSource::HSI,\n            prediv: PllPreDiv::DIV4,\n            mul: PllMul::MUL25,\n            divp: None,\n            divq: None,\n            divr: Some(PllDiv::DIV4), // 100mhz\n        });\n        config.rcc.sys = Sysclk::PLL1_P; // 200 Mhz\n        config.rcc.ahb_pre = AHBPrescaler::DIV1; // 200 Mhz\n        config.rcc.apb1_pre = APBPrescaler::DIV2; // 100 Mhz\n        config.rcc.apb2_pre = APBPrescaler::DIV2; // 100 Mhz\n        config.rcc.apb3_pre = APBPrescaler::DIV2; // 100 Mhz\n        config.rcc.voltage_scale = VoltageScale::Scale1;\n        config.rcc.mux.adcdacsel = mux::Adcdacsel::PLL2_R;\n    }\n    let p = embassy_stm32::init(config);\n\n    spawner.spawn(unwrap!(adc1_task(p.ADC1, p.GPDMA1_CH0, p.PA0, p.PA2)));\n    spawner.spawn(unwrap!(adc2_task(p.ADC2, p.GPDMA1_CH1, p.PA1, p.PA3)));\n}\n\n#[embassy_executor::task]\nasync fn adc1_task(\n    adc: Peri<'static, ADC1>,\n    dma: Peri<'static, GPDMA1_CH0>,\n    pin1: Peri<'static, PA0>,\n    pin2: Peri<'static, PA2>,\n) {\n    adc_task(adc, dma, Irqs, pin1, pin2).await;\n}\n\n#[embassy_executor::task]\nasync fn adc2_task(\n    adc: Peri<'static, ADC2>,\n    dma: Peri<'static, GPDMA1_CH1>,\n    pin1: Peri<'static, PA1>,\n    pin2: Peri<'static, PA3>,\n) {\n    adc_task(adc, dma, Irqs, pin1, pin2).await;\n}\n\nasync fn adc_task<'a, T, D, I>(\n    adc: Peri<'a, T>,\n    mut dma: Peri<'a, D>,\n    irq: I,\n    pin1: impl AdcChannel<T>,\n    pin2: impl AdcChannel<T>,\n) where\n    T: adc::DefaultInstance,\n    D: RxDma<T>,\n    I: interrupt::typelevel::Binding<D::Interrupt, dma::InterruptHandler<D>> + Copy,\n{\n    let mut adc = Adc::new(adc);\n    let mut pin1 = pin1.degrade_adc();\n    let mut pin2 = pin2.degrade_adc();\n\n    info!(\"adc init\");\n\n    let mut ticker = Ticker::every(Duration::from_millis(500));\n    let mut tic = Instant::now();\n    let mut buffer = [0u16; 512];\n    loop {\n        // This is not a true continuous read as there is downtime between each\n        // call to `Adc::read` where the ADC is sitting idle.\n        adc.read(\n            dma.reborrow(),\n            irq,\n            [(&mut pin1, SampleTime::CYCLES2_5), (&mut pin2, SampleTime::CYCLES2_5)].into_iter(),\n            &mut buffer[0..2],\n        )\n        .await;\n        let toc = Instant::now();\n        info!(\"\\n adc: {} dt = {}\", buffer[0..16], (toc - tic).as_micros());\n        tic = toc;\n\n        ticker.next().await;\n    }\n}\n"
  },
  {
    "path": "examples/stm32h5/src/bin/backup_sram.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_stm32::Config;\nuse embassy_stm32::backup_sram::BackupMemory;\nuse embassy_time::Timer;\nuse {defmt_rtt as _, panic_probe as _};\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let mut config = Config::default();\n    config.rcc.ls.enable_backup_sram = true;\n\n    let p = embassy_stm32::init(config);\n    info!(\"Started!\");\n\n    let (bytes, status) = BackupMemory::new(p.BKPSRAM);\n\n    match status {\n        false => info!(\"BKPSRAM just enabled\"),\n        true => info!(\"BKPSRAM already enabled\"),\n    }\n\n    loop {\n        info!(\"byte0: {}\", bytes[0]);\n        bytes[0] = bytes[0].wrapping_add(1);\n        Timer::after_millis(500).await;\n    }\n}\n"
  },
  {
    "path": "examples/stm32h5/src/bin/blinky.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_stm32::gpio::{Level, Output, Speed};\nuse embassy_time::Timer;\nuse {defmt_rtt as _, panic_probe as _};\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let p = embassy_stm32::init(Default::default());\n    info!(\"Hello World!\");\n\n    let mut led = Output::new(p.PB0, Level::High, Speed::Low);\n\n    loop {\n        info!(\"high\");\n        led.set_high();\n        Timer::after_millis(500).await;\n\n        info!(\"low\");\n        led.set_low();\n        Timer::after_millis(500).await;\n    }\n}\n"
  },
  {
    "path": "examples/stm32h5/src/bin/button_exti.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_stm32::exti::{self, ExtiInput};\nuse embassy_stm32::gpio::Pull;\nuse embassy_stm32::{bind_interrupts, interrupt};\nuse {defmt_rtt as _, panic_probe as _};\n\nbind_interrupts!(\n    pub struct Irqs{\n        EXTI13 => exti::InterruptHandler<interrupt::typelevel::EXTI13>;\n});\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let p = embassy_stm32::init(Default::default());\n    info!(\"Hello World!\");\n\n    let mut button = ExtiInput::new(p.PC13, p.EXTI13, Pull::Down, Irqs);\n\n    info!(\"Press the USER button...\");\n\n    loop {\n        button.wait_for_rising_edge().await;\n        info!(\"Pressed!\");\n        button.wait_for_falling_edge().await;\n        info!(\"Released!\");\n    }\n}\n"
  },
  {
    "path": "examples/stm32h5/src/bin/can.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_stm32::peripherals::*;\nuse embassy_stm32::{Config, bind_interrupts, can, rcc};\nuse embassy_time::Timer;\nuse {defmt_rtt as _, panic_probe as _};\n\nbind_interrupts!(struct Irqs {\n    FDCAN1_IT0 => can::IT0InterruptHandler<FDCAN1>;\n    FDCAN1_IT1 => can::IT1InterruptHandler<FDCAN1>;\n});\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let mut config = Config::default();\n    config.rcc.hse = Some(rcc::Hse {\n        freq: embassy_stm32::time::Hertz(25_000_000),\n        mode: rcc::HseMode::Oscillator,\n    });\n    config.rcc.mux.fdcan12sel = rcc::mux::Fdcansel::HSE;\n\n    let peripherals = embassy_stm32::init(config);\n\n    let mut can = can::CanConfigurator::new(peripherals.FDCAN1, peripherals.PA11, peripherals.PA12, Irqs);\n\n    // 250k bps\n    can.set_bitrate(250_000);\n\n    //let mut can = can.into_internal_loopback_mode();\n    let mut can = can.into_normal_mode();\n\n    info!(\"CAN Configured\");\n\n    let mut i = 0;\n    let mut last_read_ts = embassy_time::Instant::now();\n\n    loop {\n        let frame = can::frame::Frame::new_extended(0x123456F, &[i; 8]).unwrap();\n        info!(\"Writing frame\");\n        _ = can.write(&frame).await;\n\n        match can.read().await {\n            Ok(envelope) => {\n                let (rx_frame, ts) = envelope.parts();\n                let delta = (ts - last_read_ts).as_millis();\n                last_read_ts = ts;\n                info!(\n                    \"Rx: {:x} {:x} {:x} {:x} --- NEW {}\",\n                    rx_frame.data()[0],\n                    rx_frame.data()[1],\n                    rx_frame.data()[2],\n                    rx_frame.data()[3],\n                    delta,\n                )\n            }\n            Err(_err) => error!(\"Error in frame\"),\n        }\n\n        Timer::after_millis(250).await;\n\n        i += 1;\n        if i > 3 {\n            break;\n        }\n    }\n\n    let (mut tx, mut rx, _props) = can.split();\n    // With split\n    loop {\n        let frame = can::frame::Frame::new_extended(0x123456F, &[i; 8]).unwrap();\n        info!(\"Writing frame\");\n        _ = tx.write(&frame).await;\n\n        match rx.read().await {\n            Ok(envelope) => {\n                let (rx_frame, ts) = envelope.parts();\n                let delta = (ts - last_read_ts).as_millis();\n                last_read_ts = ts;\n                info!(\n                    \"Rx: {:x} {:x} {:x} {:x} --- NEW {}\",\n                    rx_frame.data()[0],\n                    rx_frame.data()[1],\n                    rx_frame.data()[2],\n                    rx_frame.data()[3],\n                    delta,\n                )\n            }\n            Err(_err) => error!(\"Error in frame\"),\n        }\n\n        Timer::after_millis(250).await;\n\n        i = i.wrapping_add(1);\n    }\n}\n"
  },
  {
    "path": "examples/stm32h5/src/bin/cordic.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_stm32::cordic::{self, utils};\nuse embassy_stm32::{bind_interrupts, dma, peripherals};\nuse {defmt_rtt as _, panic_probe as _};\n\nbind_interrupts!(struct Irqs {\n    GPDMA1_CHANNEL0 => dma::InterruptHandler<peripherals::GPDMA1_CH0>;\n    GPDMA1_CHANNEL1 => dma::InterruptHandler<peripherals::GPDMA1_CH1>;\n});\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let mut dp = embassy_stm32::init(Default::default());\n\n    // Create CORDIC with 2-arg, 2-result config for the initial call (sets ARG2)\n    let mut cordic = cordic::Cordic::new(\n        dp.CORDIC.reborrow(),\n        unwrap!(cordic::Config::new(\n            cordic::Function::Sin,\n            Default::default(),\n            Default::default(),\n        ))\n        .arg_count(cordic::AccessCount::Two)\n        .res_count(cordic::AccessCount::Two),\n    );\n\n    // for output buf, the length is not that strict, larger than minimal required is ok.\n    let mut output_f64 = [0f64; 19];\n    let mut output_u32 = [0u32; 21];\n\n    // tips:\n    // CORDIC peripheral has some strict on input value, you can also use \".check_argX_fXX()\" methods\n    // to make sure your input values are compatible with current CORDIC setup.\n    let arg1 = [-1.0, -0.5, 0.0, 0.5, 1.0]; // for trigonometric function, the ARG1 value [-pi, pi] should be map to [-1, 1]\n    let arg2 = [0.5]; // and for Sin function, ARG2 should be in [0, 1]\n\n    let mut input_buf = [0u32; 9];\n\n    // convert input from floating point to fixed point\n    input_buf[0] = unwrap!(utils::f64_to_q1_31(arg1[0]));\n    input_buf[1] = unwrap!(utils::f64_to_q1_31(arg2[0]));\n\n    // If input length is small, blocking mode can be used to minimize overhead.\n    let cnt0 = unwrap!(cordic.blocking_calc_32bit(\n        &input_buf[..2], // input length is strict, since driver use its length to detect calculation count\n        &mut output_u32,\n    ));\n\n    // convert result from fixed point into floating point\n    for (&u32_val, f64_val) in output_u32[..cnt0].iter().zip(output_f64.iter_mut()) {\n        *f64_val = utils::q1_31_to_f64(u32_val);\n    }\n\n    // convert input from floating point to fixed point\n    //\n    // first value from arg1 is used, so truncate to arg1[1..]\n    for (&f64_val, u32_val) in arg1[1..].iter().zip(input_buf.iter_mut()) {\n        *u32_val = unwrap!(utils::f64_to_q1_31(f64_val));\n    }\n\n    // Switch to 1-arg mode (reuse ARG2 set above) without resetting ARG2\n    cordic.set_access_counts(cordic::AccessCount::One, cordic::AccessCount::Two);\n\n    // If calculation is a little longer, async mode can make use of DMA, and let core do some other stuff.\n    let cnt1 = unwrap!(\n        cordic\n            .async_calc_32bit(\n                dp.GPDMA1_CH0.reborrow(),\n                dp.GPDMA1_CH1.reborrow(),\n                Irqs,\n                &input_buf[..arg1.len() - 1], // limit input buf to its actual length\n                &mut output_u32,\n            )\n            .await\n    );\n\n    // convert result from fixed point into floating point\n    for (&u32_val, f64_val) in output_u32[..cnt1].iter().zip(output_f64[cnt0..cnt0 + cnt1].iter_mut()) {\n        *f64_val = utils::q1_31_to_f64(u32_val);\n    }\n\n    println!(\"result: {}\", output_f64[..cnt0 + cnt1]);\n}\n"
  },
  {
    "path": "examples/stm32h5/src/bin/dts.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_stm32::dts::{Dts, InterruptHandler, SampleTime};\nuse embassy_stm32::peripherals::DTS;\nuse embassy_stm32::rcc::frequency;\nuse embassy_stm32::{Config, bind_interrupts, dts};\nuse embassy_time::Timer;\nuse {defmt_rtt as _, panic_probe as _};\n\nbind_interrupts!(struct Irqs {\n    DTS => InterruptHandler;\n});\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let mut config = Config::default();\n    {\n        use embassy_stm32::rcc::*;\n        config.rcc.hsi = Some(HSIPrescaler::DIV1);\n        config.rcc.csi = true;\n        config.rcc.pll1 = Some(Pll {\n            source: PllSource::HSI,\n            prediv: PllPreDiv::DIV4,\n            mul: PllMul::MUL25,\n            divp: Some(PllDiv::DIV2),\n            divq: Some(PllDiv::DIV4), // SPI1 cksel defaults to pll1_q\n            divr: None,\n        });\n        config.rcc.pll2 = Some(Pll {\n            source: PllSource::HSI,\n            prediv: PllPreDiv::DIV4,\n            mul: PllMul::MUL25,\n            divp: None,\n            divq: None,\n            divr: Some(PllDiv::DIV4), // 100mhz\n        });\n        config.rcc.sys = Sysclk::PLL1_P; // 200 Mhz\n        config.rcc.ahb_pre = AHBPrescaler::DIV1; // 200 Mhz\n        config.rcc.apb1_pre = APBPrescaler::DIV2; // 100 Mhz\n        config.rcc.apb2_pre = APBPrescaler::DIV2; // 100 Mhz\n        config.rcc.apb3_pre = APBPrescaler::DIV2; // 100 Mhz\n        config.rcc.voltage_scale = VoltageScale::Scale1;\n        config.rcc.mux.adcdacsel = mux::Adcdacsel::PLL2_R;\n    }\n    let p = embassy_stm32::init(config);\n\n    info!(\"Hello World!\");\n\n    let mut config = dts::Config::default();\n    config.sample_time = SampleTime::ClockCycles15;\n    let mut dts = Dts::new(p.DTS, Irqs, config);\n\n    let cal = Dts::factory_calibration();\n    let convert_to_celsius = |raw_temp: u16| {\n        let raw_temp = raw_temp as f32;\n        let sample_time = (config.sample_time as u8) as f32;\n\n        let f = frequency::<DTS>().0 as f32;\n\n        let t0 = cal.t0 as f32;\n        let fmt0 = cal.fmt0.0 as f32;\n        let ramp_coeff = cal.ramp_coeff as f32;\n\n        ((f * sample_time / raw_temp) - fmt0) / ramp_coeff + t0\n    };\n\n    loop {\n        let temp = dts.read().await;\n        info!(\"Temp: {} degrees\", convert_to_celsius(temp));\n        Timer::after_millis(500).await;\n    }\n}\n"
  },
  {
    "path": "examples/stm32h5/src/bin/eth.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_net::tcp::TcpSocket;\nuse embassy_net::{Ipv4Address, StackResources};\nuse embassy_stm32::eth::{Ethernet, GenericPhy, PacketQueue, Sma};\nuse embassy_stm32::peripherals::{ETH, ETH_SMA};\nuse embassy_stm32::rcc::{\n    AHBPrescaler, APBPrescaler, Hse, HseMode, Pll, PllDiv, PllMul, PllPreDiv, PllSource, Sysclk, VoltageScale,\n};\nuse embassy_stm32::rng::Rng;\nuse embassy_stm32::time::Hertz;\nuse embassy_stm32::{Config, bind_interrupts, eth, peripherals, rng};\nuse embassy_time::Timer;\nuse embedded_io_async::Write;\nuse static_cell::StaticCell;\nuse {defmt_rtt as _, panic_probe as _};\n\nbind_interrupts!(struct Irqs {\n    ETH => eth::InterruptHandler;\n    RNG => rng::InterruptHandler<peripherals::RNG>;\n});\n\ntype Device = Ethernet<'static, ETH, GenericPhy<Sma<'static, ETH_SMA>>>;\n\n#[embassy_executor::task]\nasync fn net_task(mut runner: embassy_net::Runner<'static, Device>) -> ! {\n    runner.run().await\n}\n\n#[embassy_executor::main]\nasync fn main(spawner: Spawner) -> ! {\n    let mut config = Config::default();\n    config.rcc.hsi = None;\n    config.rcc.hsi48 = Some(Default::default()); // needed for RNG\n    config.rcc.hse = Some(Hse {\n        freq: Hertz(8_000_000),\n        mode: HseMode::BypassDigital,\n    });\n    config.rcc.pll1 = Some(Pll {\n        source: PllSource::HSE,\n        prediv: PllPreDiv::DIV2,\n        mul: PllMul::MUL125,\n        divp: Some(PllDiv::DIV2),\n        divq: Some(PllDiv::DIV2),\n        divr: None,\n    });\n    config.rcc.ahb_pre = AHBPrescaler::DIV1;\n    config.rcc.apb1_pre = APBPrescaler::DIV1;\n    config.rcc.apb2_pre = APBPrescaler::DIV1;\n    config.rcc.apb3_pre = APBPrescaler::DIV1;\n    config.rcc.sys = Sysclk::PLL1_P;\n    config.rcc.voltage_scale = VoltageScale::Scale0;\n    let p = embassy_stm32::init(config);\n    info!(\"Hello World!\");\n\n    // Generate random seed.\n    let mut rng = Rng::new(p.RNG, Irqs);\n    let mut seed = [0; 8];\n    rng.fill_bytes(&mut seed);\n    let seed = u64::from_le_bytes(seed);\n\n    let mac_addr = [0x00, 0x00, 0xDE, 0xAD, 0xBE, 0xEF];\n\n    static PACKETS: StaticCell<PacketQueue<4, 4>> = StaticCell::new();\n    let device = Ethernet::new(\n        PACKETS.init(PacketQueue::<4, 4>::new()),\n        p.ETH,\n        Irqs,\n        p.PA1,\n        p.PA7,\n        p.PC4,\n        p.PC5,\n        p.PG13,\n        p.PB15,\n        p.PG11,\n        mac_addr,\n        p.ETH_SMA,\n        p.PA2,\n        p.PC1,\n    );\n\n    let config = embassy_net::Config::dhcpv4(Default::default());\n    //let config = embassy_net::Config::ipv4_static(embassy_net::StaticConfigV4 {\n    //    address: Ipv4Cidr::new(Ipv4Address::new(10, 42, 0, 61), 24),\n    //    dns_servers: Vec::new(),\n    //    gateway: Some(Ipv4Address::new(10, 42, 0, 1)),\n    //});\n\n    // Init network stack\n    static RESOURCES: StaticCell<StackResources<3>> = StaticCell::new();\n    let (stack, runner) = embassy_net::new(device, config, RESOURCES.init(StackResources::new()), seed);\n\n    // Launch network task\n    spawner.spawn(unwrap!(net_task(runner)));\n\n    // Ensure DHCP configuration is up before trying connect\n    stack.wait_config_up().await;\n\n    info!(\"Network task initialized\");\n\n    // Then we can use it!\n    let mut rx_buffer = [0; 1024];\n    let mut tx_buffer = [0; 1024];\n\n    loop {\n        let mut socket = TcpSocket::new(stack, &mut rx_buffer, &mut tx_buffer);\n\n        socket.set_timeout(Some(embassy_time::Duration::from_secs(10)));\n\n        let remote_endpoint = (Ipv4Address::new(10, 42, 0, 1), 8000);\n        info!(\"connecting...\");\n        let r = socket.connect(remote_endpoint).await;\n        if let Err(e) = r {\n            info!(\"connect error: {:?}\", e);\n            Timer::after_secs(3).await;\n            continue;\n        }\n        info!(\"connected!\");\n        loop {\n            let r = socket.write_all(b\"Hello\\n\").await;\n            if let Err(e) = r {\n                info!(\"write error: {:?}\", e);\n                break;\n            }\n            Timer::after_secs(1).await;\n        }\n    }\n}\n"
  },
  {
    "path": "examples/stm32h5/src/bin/i2c.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_stm32::i2c::{Error, I2c};\nuse embassy_stm32::{bind_interrupts, dma, i2c, peripherals};\nuse {defmt_rtt as _, panic_probe as _};\n\nconst ADDRESS: u8 = 0x5F;\nconst WHOAMI: u8 = 0x0F;\n\nbind_interrupts!(struct Irqs {\n    I2C2_EV => i2c::EventInterruptHandler<peripherals::I2C2>;\n    I2C2_ER => i2c::ErrorInterruptHandler<peripherals::I2C2>;\n    GPDMA1_CHANNEL4 => dma::InterruptHandler<peripherals::GPDMA1_CH4>;\n    GPDMA1_CHANNEL5 => dma::InterruptHandler<peripherals::GPDMA1_CH5>;\n});\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    info!(\"Hello world!\");\n    let p = embassy_stm32::init(Default::default());\n\n    let mut i2c = I2c::new(\n        p.I2C2,\n        p.PB10,\n        p.PB11,\n        p.GPDMA1_CH4,\n        p.GPDMA1_CH5,\n        Irqs,\n        Default::default(),\n    );\n\n    let mut data = [0u8; 1];\n\n    match i2c.blocking_write_read(ADDRESS, &[WHOAMI], &mut data) {\n        Ok(()) => info!(\"Whoami: {}\", data[0]),\n        Err(Error::Timeout) => error!(\"Operation timed out\"),\n        Err(e) => error!(\"I2c Error: {:?}\", e),\n    }\n}\n"
  },
  {
    "path": "examples/stm32h5/src/bin/mco.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_stm32::gpio::Speed;\nuse embassy_stm32::rcc::{Mco, Mco2Source, McoConfig};\nuse {defmt_rtt as _, panic_probe as _};\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let p = embassy_stm32::init(Default::default());\n\n    /* Default \"VeryHigh\" drive strength and prescaler DIV1 */\n    // let _mco = Mco::new(p.MCO2, p.PC9, Mco2Source::SYS, McoConfig::default());\n\n    /* Choose Speed::Low drive strength */\n    let config = {\n        let mut config = McoConfig::default();\n        config.speed = Speed::Low;\n        config\n    };\n\n    let _mco = Mco::new(p.MCO2, p.PC9, Mco2Source::SYS, config);\n\n    info!(\"Clock out with low drive strength set on Master Clock Out 2 pin as AF on PC9\");\n\n    loop {}\n}\n"
  },
  {
    "path": "examples/stm32h5/src/bin/rng.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_stm32::rng::Rng;\nuse embassy_stm32::{bind_interrupts, peripherals, rng};\nuse {defmt_rtt as _, panic_probe as _};\n\nbind_interrupts!(struct Irqs {\n    RNG => rng::InterruptHandler<peripherals::RNG>;\n});\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let p = embassy_stm32::init(Default::default());\n    info!(\"Hello World!\");\n\n    let mut rng = Rng::new(p.RNG, Irqs);\n\n    let mut buf = [0u8; 16];\n    unwrap!(rng.async_fill_bytes(&mut buf).await);\n    info!(\"random bytes: {:02x}\", buf);\n}\n"
  },
  {
    "path": "examples/stm32h5/src/bin/sai.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse defmt::info;\nuse embassy_executor::Spawner;\nuse embassy_stm32::{Config, bind_interrupts, dma, peripherals, sai};\nuse {defmt_rtt as _, panic_probe as _};\n\nbind_interrupts!(struct Irqs {\n    GPDMA1_CHANNEL0 => dma::InterruptHandler<peripherals::GPDMA1_CH0>;\n});\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    info!(\"Hello world.\");\n\n    let mut config = Config::default();\n    {\n        use embassy_stm32::rcc::*;\n\n        config.rcc.pll2 = Some(Pll {\n            source: PllSource::HSI,\n            prediv: PllPreDiv::DIV16,\n            mul: PllMul::MUL32,\n            divp: Some(PllDiv::DIV16), // 8 MHz SAI clock\n            divq: None,\n            divr: None,\n        });\n\n        config.rcc.mux.sai1sel = mux::Saisel::PLL2_P;\n    }\n    let p = embassy_stm32::init(config);\n\n    let mut write_buffer = [0u16; 1024];\n    let (_, sai_b) = sai::split_subblocks(p.SAI1);\n\n    let mut sai_b = sai::Sai::new_asynchronous(\n        sai_b,\n        p.PF8,\n        p.PE3,\n        p.PF9,\n        p.GPDMA1_CH0,\n        &mut write_buffer,\n        Irqs,\n        Default::default(),\n    );\n\n    // Populate arbitrary data.\n    let mut data = [0u16; 256];\n    for (index, sample) in data.iter_mut().enumerate() {\n        *sample = index as u16;\n    }\n\n    loop {\n        sai_b.write(&data).await.unwrap();\n    }\n}\n"
  },
  {
    "path": "examples/stm32h5/src/bin/usart.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse cortex_m_rt::entry;\nuse defmt::*;\nuse embassy_executor::Executor;\nuse embassy_stm32::usart::{Config, Uart};\nuse static_cell::StaticCell;\nuse {defmt_rtt as _, panic_probe as _};\n\n#[embassy_executor::task]\nasync fn main_task() {\n    let p = embassy_stm32::init(Default::default());\n\n    let config = Config::default();\n    let mut usart = Uart::new_blocking(p.UART7, p.PF6, p.PF7, config).unwrap();\n\n    unwrap!(usart.blocking_write(b\"Hello Embassy World!\\r\\n\"));\n    info!(\"wrote Hello, starting echo\");\n\n    let mut buf = [0u8; 1];\n    loop {\n        unwrap!(usart.blocking_read(&mut buf));\n        unwrap!(usart.blocking_write(&buf));\n    }\n}\n\nstatic EXECUTOR: StaticCell<Executor> = StaticCell::new();\n\n#[entry]\nfn main() -> ! {\n    info!(\"Hello World!\");\n\n    let executor = EXECUTOR.init(Executor::new());\n\n    executor.run(|spawner| {\n        spawner.spawn(unwrap!(main_task()));\n    })\n}\n"
  },
  {
    "path": "examples/stm32h5/src/bin/usart_dma.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse core::fmt::Write;\n\nuse cortex_m_rt::entry;\nuse defmt::*;\nuse embassy_executor::Executor;\nuse embassy_stm32::usart::{Config, Uart};\nuse embassy_stm32::{bind_interrupts, dma, peripherals, usart};\nuse heapless::String;\nuse static_cell::StaticCell;\nuse {defmt_rtt as _, panic_probe as _};\n\nbind_interrupts!(struct Irqs {\n    UART7 => usart::InterruptHandler<peripherals::UART7>;\n    GPDMA1_CHANNEL0 => dma::InterruptHandler<peripherals::GPDMA1_CH0>;\n    GPDMA1_CHANNEL1 => dma::InterruptHandler<peripherals::GPDMA1_CH1>;\n});\n\n#[embassy_executor::task]\nasync fn main_task() {\n    let p = embassy_stm32::init(Default::default());\n\n    let config = Config::default();\n    let mut usart = Uart::new(p.UART7, p.PF6, p.PF7, p.GPDMA1_CH0, p.GPDMA1_CH1, Irqs, config).unwrap();\n\n    for n in 0u32.. {\n        let mut s: String<128> = String::new();\n        core::write!(&mut s, \"Hello DMA World {}!\\r\\n\", n).unwrap();\n\n        usart.write(s.as_bytes()).await.ok();\n\n        info!(\"wrote DMA\");\n    }\n}\n\nstatic EXECUTOR: StaticCell<Executor> = StaticCell::new();\n\n#[entry]\nfn main() -> ! {\n    info!(\"Hello World!\");\n\n    let executor = EXECUTOR.init(Executor::new());\n\n    executor.run(|spawner| {\n        spawner.spawn(unwrap!(main_task()));\n    })\n}\n"
  },
  {
    "path": "examples/stm32h5/src/bin/usart_split.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_stm32::mode::Async;\nuse embassy_stm32::usart::{Config, Uart, UartRx};\nuse embassy_stm32::{bind_interrupts, dma, peripherals, usart};\nuse embassy_sync::blocking_mutex::raw::ThreadModeRawMutex;\nuse embassy_sync::channel::Channel;\nuse {defmt_rtt as _, panic_probe as _};\n\nbind_interrupts!(struct Irqs {\n    UART7 => usart::InterruptHandler<peripherals::UART7>;\n    GPDMA1_CHANNEL0 => dma::InterruptHandler<peripherals::GPDMA1_CH0>;\n    GPDMA1_CHANNEL1 => dma::InterruptHandler<peripherals::GPDMA1_CH1>;\n});\n\nstatic CHANNEL: Channel<ThreadModeRawMutex, [u8; 8], 1> = Channel::new();\n\n#[embassy_executor::main]\nasync fn main(spawner: Spawner) -> ! {\n    let p = embassy_stm32::init(Default::default());\n    info!(\"Hello World!\");\n\n    let config = Config::default();\n    let mut usart = Uart::new(p.UART7, p.PF6, p.PF7, p.GPDMA1_CH0, p.GPDMA1_CH1, Irqs, config).unwrap();\n    unwrap!(usart.blocking_write(b\"Type 8 chars to echo!\\r\\n\"));\n\n    let (mut tx, rx) = usart.split();\n\n    spawner.spawn(unwrap!(reader(rx)));\n\n    loop {\n        let buf = CHANNEL.receive().await;\n        info!(\"writing...\");\n        unwrap!(tx.write(&buf).await);\n    }\n}\n\n#[embassy_executor::task]\nasync fn reader(mut rx: UartRx<'static, Async>) {\n    let mut buf = [0; 8];\n    loop {\n        info!(\"reading...\");\n        unwrap!(rx.read(&mut buf).await);\n        CHANNEL.send(buf).await;\n    }\n}\n"
  },
  {
    "path": "examples/stm32h5/src/bin/usb_c_pd.rs",
    "content": "//! This example targets the NUCLEO-H563ZI platform.\n//! USB-C CC lines are protected by a TCPP01-M12 chipset.\n#![no_std]\n#![no_main]\n\nuse defmt::{Format, error, info};\nuse embassy_executor::Spawner;\nuse embassy_stm32::gpio::Output;\nuse embassy_stm32::ucpd::{self, CcPhy, CcPull, CcSel, CcVState, Ucpd};\nuse embassy_stm32::{Config, bind_interrupts, dma, peripherals};\nuse embassy_time::{Duration, with_timeout};\nuse {defmt_rtt as _, panic_probe as _};\n\nbind_interrupts!(struct Irqs {\n    UCPD1 => ucpd::InterruptHandler<peripherals::UCPD1>;\n    GPDMA1_CHANNEL0 => dma::InterruptHandler<peripherals::GPDMA1_CH0>;\n    GPDMA1_CHANNEL1 => dma::InterruptHandler<peripherals::GPDMA1_CH1>;\n});\n\n#[derive(Debug, Format)]\nenum CableOrientation {\n    Normal,\n    Flipped,\n    DebugAccessoryMode,\n}\n\n// Returns true when the cable\nasync fn wait_attached<T: ucpd::Instance>(cc_phy: &mut CcPhy<'_, T>) -> CableOrientation {\n    loop {\n        let (cc1, cc2) = cc_phy.vstate();\n        if cc1 == CcVState::LOWEST && cc2 == CcVState::LOWEST {\n            // Detached, wait until attached by monitoring the CC lines.\n            cc_phy.wait_for_vstate_change().await;\n            continue;\n        }\n\n        // Attached, wait for CC lines to be stable for tCCDebounce (100..200ms).\n        if with_timeout(Duration::from_millis(100), cc_phy.wait_for_vstate_change())\n            .await\n            .is_ok()\n        {\n            // State has changed, restart detection procedure.\n            continue;\n        };\n\n        // State was stable for the complete debounce period, check orientation.\n        return match (cc1, cc2) {\n            (_, CcVState::LOWEST) => CableOrientation::Normal,  // CC1 connected\n            (CcVState::LOWEST, _) => CableOrientation::Flipped, // CC2 connected\n            _ => CableOrientation::DebugAccessoryMode,          // Both connected (special cable)\n        };\n    }\n}\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let config = Config::default();\n    let p = embassy_stm32::init(config);\n\n    info!(\"Hello World!\");\n\n    let mut ucpd = Ucpd::new(p.UCPD1, Irqs {}, p.PB13, p.PB14, Default::default());\n    ucpd.cc_phy().set_pull(CcPull::Sink);\n\n    // This pin controls the dead-battery mode on the attached TCPP01-M12.\n    // If low, TCPP01-M12 disconnects CC lines and presents dead-battery resistance on CC lines, thus set high.\n    // Must only be set after the CC pull is established.\n    let _tcpp01_m12_ndb = Output::new(p.PA9, embassy_stm32::gpio::Level::High, embassy_stm32::gpio::Speed::Low);\n\n    info!(\"Waiting for USB connection...\");\n    let cable_orientation = wait_attached(ucpd.cc_phy()).await;\n    info!(\"USB cable connected, orientation: {}\", cable_orientation);\n\n    let cc_sel = match cable_orientation {\n        CableOrientation::Normal => {\n            info!(\"Starting PD communication on CC1 pin\");\n            CcSel::CC1\n        }\n        CableOrientation::Flipped => {\n            info!(\"Starting PD communication on CC2 pin\");\n            CcSel::CC2\n        }\n        CableOrientation::DebugAccessoryMode => panic!(\"No PD communication in DAM\"),\n    };\n    let (_cc_phy, mut pd_phy) = ucpd.split_pd_phy(p.GPDMA1_CH0, p.GPDMA1_CH1, Irqs, cc_sel);\n\n    loop {\n        // Enough space for the longest non-extended data message.\n        let mut buf = [0_u8; 30];\n        match pd_phy.receive(buf.as_mut()).await {\n            Ok(n) => info!(\"USB PD RX: {=[u8]:?}\", &buf[..n]),\n            Err(e) => error!(\"USB PD RX: {}\", e),\n        }\n    }\n}\n"
  },
  {
    "path": "examples/stm32h5/src/bin/usb_serial.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse defmt::{panic, *};\nuse embassy_executor::Spawner;\nuse embassy_futures::join::join;\nuse embassy_stm32::time::Hertz;\nuse embassy_stm32::usb::{Driver, Instance};\nuse embassy_stm32::{Config, bind_interrupts, peripherals, usb};\nuse embassy_usb::Builder;\nuse embassy_usb::class::cdc_acm::{CdcAcmClass, State};\nuse embassy_usb::driver::EndpointError;\nuse {defmt_rtt as _, panic_probe as _};\n\nbind_interrupts!(struct Irqs {\n    USB_DRD_FS => usb::InterruptHandler<peripherals::USB>;\n});\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let mut config = Config::default();\n    {\n        use embassy_stm32::rcc::*;\n        config.rcc.hsi = None;\n        config.rcc.hsi48 = Some(Hsi48Config { sync_from_usb: true }); // needed for USB\n        config.rcc.hse = Some(Hse {\n            freq: Hertz(8_000_000),\n            mode: HseMode::BypassDigital,\n        });\n        config.rcc.pll1 = Some(Pll {\n            source: PllSource::HSE,\n            prediv: PllPreDiv::DIV2,\n            mul: PllMul::MUL125,\n            divp: Some(PllDiv::DIV2), // 250mhz\n            divq: None,\n            divr: None,\n        });\n        config.rcc.ahb_pre = AHBPrescaler::DIV2;\n        config.rcc.apb1_pre = APBPrescaler::DIV4;\n        config.rcc.apb2_pre = APBPrescaler::DIV2;\n        config.rcc.apb3_pre = APBPrescaler::DIV4;\n        config.rcc.sys = Sysclk::PLL1_P;\n        config.rcc.voltage_scale = VoltageScale::Scale0;\n        config.rcc.mux.usbsel = mux::Usbsel::HSI48;\n    }\n    let p = embassy_stm32::init(config);\n\n    info!(\"Hello World!\");\n\n    // Create the driver, from the HAL.\n    let driver = Driver::new(p.USB, Irqs, p.PA12, p.PA11);\n\n    // Create embassy-usb Config\n    let mut config = embassy_usb::Config::new(0xc0de, 0xcafe);\n    config.manufacturer = Some(\"Embassy\");\n    config.product = Some(\"USB-serial example\");\n    config.serial_number = Some(\"12345678\");\n\n    // Create embassy-usb DeviceBuilder using the driver and config.\n    // It needs some buffers for building the descriptors.\n    let mut config_descriptor = [0; 256];\n    let mut bos_descriptor = [0; 256];\n    let mut control_buf = [0; 64];\n\n    let mut state = State::new();\n\n    let mut builder = Builder::new(\n        driver,\n        config,\n        &mut config_descriptor,\n        &mut bos_descriptor,\n        &mut [], // no msos descriptors\n        &mut control_buf,\n    );\n\n    // Create classes on the builder.\n    let mut class = CdcAcmClass::new(&mut builder, &mut state, 64);\n\n    // Build the builder.\n    let mut usb = builder.build();\n\n    // Run the USB device.\n    let usb_fut = usb.run();\n\n    // Do stuff with the class!\n    let echo_fut = async {\n        loop {\n            class.wait_connection().await;\n            info!(\"Connected\");\n            let _ = echo(&mut class).await;\n            info!(\"Disconnected\");\n        }\n    };\n\n    // Run everything concurrently.\n    // If we had made everything `'static` above instead, we could do this using separate tasks instead.\n    join(usb_fut, echo_fut).await;\n}\n\nstruct Disconnected {}\n\nimpl From<EndpointError> for Disconnected {\n    fn from(val: EndpointError) -> Self {\n        match val {\n            EndpointError::BufferOverflow => panic!(\"Buffer overflow\"),\n            EndpointError::Disabled => Disconnected {},\n        }\n    }\n}\n\nasync fn echo<'d, T: Instance + 'd>(class: &mut CdcAcmClass<'d, Driver<'d, T>>) -> Result<(), Disconnected> {\n    let mut buf = [0; 64];\n    loop {\n        let n = class.read_packet(&mut buf).await?;\n        let data = &buf[..n];\n        info!(\"data: {:x}\", data);\n        class.write_packet(data).await?;\n    }\n}\n"
  },
  {
    "path": "examples/stm32h5/src/bin/usb_uac_speaker.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse core::cell::{Cell, RefCell};\n\nuse defmt::{panic, *};\nuse embassy_executor::Spawner;\nuse embassy_stm32::time::Hertz;\nuse embassy_stm32::{Config, bind_interrupts, interrupt, peripherals, timer, usb};\nuse embassy_sync::blocking_mutex::Mutex;\nuse embassy_sync::blocking_mutex::raw::{CriticalSectionRawMutex, NoopRawMutex};\nuse embassy_sync::signal::Signal;\nuse embassy_sync::zerocopy_channel;\nuse embassy_usb::class::uac1;\nuse embassy_usb::class::uac1::speaker::{self, Speaker};\nuse embassy_usb::driver::EndpointError;\nuse heapless::Vec;\nuse micromath::F32Ext;\nuse static_cell::StaticCell;\nuse {defmt_rtt as _, panic_probe as _};\n\nbind_interrupts!(struct Irqs {\n    USB_DRD_FS => usb::InterruptHandler<peripherals::USB>;\n});\n\nstatic TIMER: Mutex<CriticalSectionRawMutex, RefCell<Option<timer::low_level::Timer<peripherals::TIM5>>>> =\n    Mutex::new(RefCell::new(None));\n\n// A counter signal that is written by the feedback timer, once every `FEEDBACK_REFRESH_PERIOD`.\n// At that point, a feedback value is sent to the host.\npub static FEEDBACK_SIGNAL: Signal<CriticalSectionRawMutex, u32> = Signal::new();\n\n// Stereo input\npub const INPUT_CHANNEL_COUNT: usize = 2;\n\n// This example uses a fixed sample rate of 48 kHz.\npub const SAMPLE_RATE_HZ: u32 = 48_000;\npub const FEEDBACK_COUNTER_TICK_RATE: u32 = 31_250_000;\n\n// Use 32 bit samples, which allow for a lot of (software) volume adjustment without degradation of quality.\npub const SAMPLE_WIDTH: uac1::SampleWidth = uac1::SampleWidth::Width4Byte;\npub const SAMPLE_WIDTH_BIT: usize = SAMPLE_WIDTH.in_bit();\npub const SAMPLE_SIZE: usize = SAMPLE_WIDTH as usize;\npub const SAMPLE_SIZE_PER_S: usize = (SAMPLE_RATE_HZ as usize) * INPUT_CHANNEL_COUNT * SAMPLE_SIZE;\n\n// Size of audio samples per 1 ms - for the full-speed USB frame period of 1 ms.\npub const USB_FRAME_SIZE: usize = SAMPLE_SIZE_PER_S.div_ceil(1000);\n\n// Select front left and right audio channels.\npub const AUDIO_CHANNELS: [uac1::Channel; INPUT_CHANNEL_COUNT] = [uac1::Channel::LeftFront, uac1::Channel::RightFront];\n\n// Factor of two as a margin for feedback (this is an excessive amount)\npub const USB_MAX_PACKET_SIZE: usize = 2 * USB_FRAME_SIZE;\npub const USB_MAX_SAMPLE_COUNT: usize = USB_MAX_PACKET_SIZE / SAMPLE_SIZE;\n\n// The data type that is exchanged via the zero-copy channel (a sample vector).\npub type SampleBlock = Vec<u32, USB_MAX_SAMPLE_COUNT>;\n\n// Feedback is provided in 10.14 format for full-speed endpoints.\npub const FEEDBACK_REFRESH_PERIOD: uac1::FeedbackRefresh = uac1::FeedbackRefresh::Period8Frames;\nconst FEEDBACK_SHIFT: usize = 14;\n\nconst TICKS_PER_SAMPLE: f32 = (FEEDBACK_COUNTER_TICK_RATE as f32) / (SAMPLE_RATE_HZ as f32);\n\nstruct Disconnected {}\n\nimpl From<EndpointError> for Disconnected {\n    fn from(val: EndpointError) -> Self {\n        match val {\n            EndpointError::BufferOverflow => panic!(\"Buffer overflow\"),\n            EndpointError::Disabled => Disconnected {},\n        }\n    }\n}\n\n/// Sends feedback messages to the host.\nasync fn feedback_handler<'d, T: usb::Instance + 'd>(\n    feedback: &mut speaker::Feedback<'d, usb::Driver<'d, T>>,\n    feedback_factor: f32,\n) -> Result<(), Disconnected> {\n    let mut packet: Vec<u8, 4> = Vec::new();\n\n    // Collects the fractional component of the feedback value that is lost by rounding.\n    let mut rest = 0.0_f32;\n\n    loop {\n        let counter = FEEDBACK_SIGNAL.wait().await;\n\n        packet.clear();\n\n        let raw_value = counter as f32 * feedback_factor + rest;\n        let value = raw_value.round();\n        rest = raw_value - value;\n\n        let value = value as u32;\n\n        debug!(\"Feedback value: {}\", value);\n\n        packet.push(value as u8).unwrap();\n        packet.push((value >> 8) as u8).unwrap();\n        packet.push((value >> 16) as u8).unwrap();\n\n        feedback.write_packet(&packet).await?;\n    }\n}\n\n/// Handles streaming of audio data from the host.\nasync fn stream_handler<'d, T: usb::Instance + 'd>(\n    stream: &mut speaker::Stream<'d, usb::Driver<'d, T>>,\n    sender: &mut zerocopy_channel::Sender<'static, NoopRawMutex, SampleBlock>,\n) -> Result<(), Disconnected> {\n    loop {\n        let mut usb_data = [0u8; USB_MAX_PACKET_SIZE];\n        let data_size = stream.read_packet(&mut usb_data).await?;\n\n        let word_count = data_size / SAMPLE_SIZE;\n\n        if word_count * SAMPLE_SIZE == data_size {\n            // Obtain a buffer from the channel\n            let samples = sender.send().await;\n            samples.clear();\n\n            for w in 0..word_count {\n                let byte_offset = w * SAMPLE_SIZE;\n                let sample = u32::from_le_bytes(usb_data[byte_offset..byte_offset + SAMPLE_SIZE].try_into().unwrap());\n\n                // Fill the sample buffer with data.\n                samples.push(sample).unwrap();\n            }\n\n            sender.send_done();\n        } else {\n            debug!(\"Invalid USB buffer size of {}, skipped.\", data_size);\n        }\n    }\n}\n\n/// Receives audio samples from the USB streaming task and can play them back.\n#[embassy_executor::task]\nasync fn audio_receiver_task(mut usb_audio_receiver: zerocopy_channel::Receiver<'static, NoopRawMutex, SampleBlock>) {\n    loop {\n        let _samples = usb_audio_receiver.receive().await;\n        // Use the samples, for example play back via the SAI peripheral.\n\n        // Notify the channel that the buffer is now ready to be reused\n        usb_audio_receiver.receive_done();\n    }\n}\n\n/// Receives audio samples from the host.\n#[embassy_executor::task]\nasync fn usb_streaming_task(\n    mut stream: speaker::Stream<'static, usb::Driver<'static, peripherals::USB>>,\n    mut sender: zerocopy_channel::Sender<'static, NoopRawMutex, SampleBlock>,\n) {\n    loop {\n        stream.wait_connection().await;\n        info!(\"USB connected.\");\n        _ = stream_handler(&mut stream, &mut sender).await;\n        info!(\"USB disconnected.\");\n    }\n}\n\n/// Sends sample rate feedback to the host.\n///\n/// The `feedback_factor` scales the feedback timer's counter value so that the result is the number of samples that\n/// this device played back or \"consumed\" during one SOF period (1 ms) - in 10.14 format.\n///\n/// Ideally, the `feedback_factor` that is calculated below would be an integer for avoiding numerical errors.\n/// This is achieved by having `TICKS_PER_SAMPLE` be a power of two. For audio applications at a sample rate of 48 kHz,\n/// 24.576 MHz would be one such option.\n#[embassy_executor::task]\nasync fn usb_feedback_task(mut feedback: speaker::Feedback<'static, usb::Driver<'static, peripherals::USB>>) {\n    let feedback_factor =\n        ((1 << FEEDBACK_SHIFT) as f32 / TICKS_PER_SAMPLE) / FEEDBACK_REFRESH_PERIOD.frame_count() as f32;\n\n    loop {\n        feedback.wait_connection().await;\n        _ = feedback_handler(&mut feedback, feedback_factor).await;\n    }\n}\n\n#[embassy_executor::task]\nasync fn usb_task(mut usb_device: embassy_usb::UsbDevice<'static, usb::Driver<'static, peripherals::USB>>) {\n    usb_device.run().await;\n}\n\n/// Checks for changes on the control monitor of the class.\n///\n/// In this case, monitor changes of volume or mute state.\n#[embassy_executor::task]\nasync fn usb_control_task(control_monitor: speaker::ControlMonitor<'static>) {\n    loop {\n        control_monitor.changed().await;\n\n        for channel in AUDIO_CHANNELS {\n            let volume = control_monitor.volume(channel).unwrap();\n            info!(\"Volume changed to {} on channel {}.\", volume, channel);\n        }\n    }\n}\n\n/// Feedback value measurement and calculation\n///\n/// Used for measuring/calculating the number of samples that were received from the host during the\n/// `FEEDBACK_REFRESH_PERIOD`.\n///\n/// Configured in this example with\n/// - a refresh period of 8 ms, and\n/// - a tick rate of 42 MHz.\n///\n/// This gives an (ideal) counter value of 336.000 for every update of the `FEEDBACK_SIGNAL`.\n#[interrupt]\nfn TIM5() {\n    static LAST_TICKS: Mutex<CriticalSectionRawMutex, Cell<u32>> = Mutex::new(Cell::new(0));\n    static FRAME_COUNT: Mutex<CriticalSectionRawMutex, Cell<usize>> = Mutex::new(Cell::new(0));\n\n    critical_section::with(|cs| {\n        // Read timer counter.\n        let timer = TIMER.borrow(cs).borrow().as_ref().unwrap().regs_gp32();\n\n        let status = timer.sr().read();\n\n        const CHANNEL_INDEX: usize = 0;\n        if status.ccif(CHANNEL_INDEX) {\n            let ticks = timer.ccr(CHANNEL_INDEX).read();\n\n            let frame_count = FRAME_COUNT.borrow(cs);\n            let last_ticks = LAST_TICKS.borrow(cs);\n\n            frame_count.set(frame_count.get() + 1);\n            if frame_count.get() >= FEEDBACK_REFRESH_PERIOD.frame_count() {\n                frame_count.set(0);\n                FEEDBACK_SIGNAL.signal(ticks.wrapping_sub(last_ticks.get()));\n                last_ticks.set(ticks);\n            }\n        };\n\n        // Clear trigger interrupt flag.\n        timer.sr().modify(|r| r.set_tif(false));\n    });\n}\n\n// If you are trying this and your USB device doesn't connect, the most\n// common issues are the RCC config and vbus_detection\n//\n// See https://embassy.dev/book/#_the_usb_examples_are_not_working_on_my_board_is_there_anything_else_i_need_to_configure\n// for more information.\n#[embassy_executor::main]\nasync fn main(spawner: Spawner) {\n    let mut config = Config::default();\n    {\n        use embassy_stm32::rcc::*;\n        config.rcc.hsi = None;\n        config.rcc.hsi48 = Some(Hsi48Config { sync_from_usb: true }); // needed for USB\n        config.rcc.hse = Some(Hse {\n            freq: Hertz(8_000_000),\n            mode: HseMode::BypassDigital,\n        });\n        config.rcc.pll1 = Some(Pll {\n            source: PllSource::HSE,\n            prediv: PllPreDiv::DIV2,\n            mul: PllMul::MUL125,\n            divp: Some(PllDiv::DIV2), // 250 Mhz\n            divq: None,\n            divr: None,\n        });\n        config.rcc.pll2 = Some(Pll {\n            source: PllSource::HSE,\n            prediv: PllPreDiv::DIV4,\n            mul: PllMul::MUL123,\n            divp: Some(PllDiv::DIV20), // 12.3 Mhz, close to 12.288 MHz for 48 kHz audio\n            divq: None,\n            divr: None,\n        });\n        config.rcc.ahb_pre = AHBPrescaler::DIV2;\n        config.rcc.apb1_pre = APBPrescaler::DIV4;\n        config.rcc.apb2_pre = APBPrescaler::DIV2;\n        config.rcc.apb3_pre = APBPrescaler::DIV4;\n        config.rcc.sys = Sysclk::PLL1_P;\n        config.rcc.voltage_scale = VoltageScale::Scale0;\n        config.rcc.mux.usbsel = mux::Usbsel::HSI48;\n        config.rcc.mux.sai2sel = mux::Saisel::PLL2_P;\n    }\n    let p = embassy_stm32::init(config);\n\n    info!(\"Hello World!\");\n\n    // Configure all required buffers in a static way.\n    debug!(\"USB packet size is {} byte\", USB_MAX_PACKET_SIZE);\n    static CONFIG_DESCRIPTOR: StaticCell<[u8; 256]> = StaticCell::new();\n    let config_descriptor = CONFIG_DESCRIPTOR.init([0; 256]);\n\n    static BOS_DESCRIPTOR: StaticCell<[u8; 32]> = StaticCell::new();\n    let bos_descriptor = BOS_DESCRIPTOR.init([0; 32]);\n\n    const CONTROL_BUF_SIZE: usize = 64;\n    static CONTROL_BUF: StaticCell<[u8; CONTROL_BUF_SIZE]> = StaticCell::new();\n    let control_buf = CONTROL_BUF.init([0; CONTROL_BUF_SIZE]);\n\n    static STATE: StaticCell<speaker::State> = StaticCell::new();\n    let state = STATE.init(speaker::State::new());\n\n    let usb_driver = usb::Driver::new(p.USB, Irqs, p.PA12, p.PA11);\n\n    // Basic USB device configuration\n    let mut config = embassy_usb::Config::new(0xc0de, 0xcafe);\n    config.manufacturer = Some(\"Embassy\");\n    config.product = Some(\"USB-audio-speaker example\");\n    config.serial_number = Some(\"12345678\");\n\n    let mut builder = embassy_usb::Builder::new(\n        usb_driver,\n        config,\n        config_descriptor,\n        bos_descriptor,\n        &mut [], // no msos descriptors\n        control_buf,\n    );\n\n    // Create the UAC1 Speaker class components\n    let (stream, feedback, control_monitor) = Speaker::new(\n        &mut builder,\n        state,\n        USB_MAX_PACKET_SIZE as u16,\n        uac1::SampleWidth::Width4Byte,\n        &[SAMPLE_RATE_HZ],\n        &AUDIO_CHANNELS,\n        FEEDBACK_REFRESH_PERIOD,\n    );\n\n    // Create the USB device\n    let usb_device = builder.build();\n\n    // Establish a zero-copy channel for transferring received audio samples between tasks\n    static SAMPLE_BLOCKS: StaticCell<[SampleBlock; 2]> = StaticCell::new();\n    let sample_blocks = SAMPLE_BLOCKS.init([Vec::new(), Vec::new()]);\n\n    static CHANNEL: StaticCell<zerocopy_channel::Channel<'_, NoopRawMutex, SampleBlock>> = StaticCell::new();\n    let channel = CHANNEL.init(zerocopy_channel::Channel::new(sample_blocks));\n    let (sender, receiver) = channel.split();\n\n    // Run a timer for counting between SOF interrupts.\n    let mut tim5 = timer::low_level::Timer::new(p.TIM5);\n    tim5.set_tick_freq(Hertz(FEEDBACK_COUNTER_TICK_RATE));\n    tim5.set_trigger_source(timer::low_level::TriggerSource::ITR12); // The USB SOF signal.\n\n    const TIMER_CHANNEL: timer::Channel = timer::Channel::Ch1;\n    tim5.set_input_ti_selection(TIMER_CHANNEL, timer::low_level::InputTISelection::TRC);\n    tim5.set_input_capture_prescaler(TIMER_CHANNEL, 0);\n    tim5.set_input_capture_filter(TIMER_CHANNEL, timer::low_level::FilterValue::FCK_INT_N2);\n\n    // Reset all interrupt flags.\n    tim5.regs_gp32().sr().write(|r| r.0 = 0);\n\n    tim5.enable_channel(TIMER_CHANNEL, true);\n    tim5.enable_input_interrupt(TIMER_CHANNEL, true);\n\n    tim5.start();\n\n    TIMER.lock(|p| p.borrow_mut().replace(tim5));\n\n    // Unmask the TIM5 interrupt.\n    unsafe {\n        cortex_m::peripheral::NVIC::unmask(interrupt::TIM5);\n    }\n\n    // Launch USB audio tasks.\n    spawner.spawn(unwrap!(usb_control_task(control_monitor)));\n    spawner.spawn(unwrap!(usb_streaming_task(stream, sender)));\n    spawner.spawn(unwrap!(usb_feedback_task(feedback)));\n    spawner.spawn(unwrap!(usb_task(usb_device)));\n    spawner.spawn(unwrap!(audio_receiver_task(receiver)));\n}\n"
  },
  {
    "path": "examples/stm32h5/src/bin/wifi_scan.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse cyw43::aligned_bytes;\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_stm32::exti::ExtiInput;\nuse embassy_stm32::gpio::{Level, Output, Pull, Speed};\nuse embassy_stm32::sdmmc::Sdmmc;\nuse embassy_stm32::sdmmc::sdio::SerialDataInterface;\nuse embassy_stm32::time::mhz;\nuse embassy_stm32::{Config, bind_interrupts, exti, interrupt, peripherals, sdmmc};\nuse embassy_time::Timer;\nuse static_cell::StaticCell;\nuse {defmt_rtt as _, panic_probe as _};\n\nbind_interrupts!(struct Irqs {\n    SDMMC1 => sdmmc::InterruptHandler<peripherals::SDMMC1>;\n    EXTI1 => exti::InterruptHandler<interrupt::typelevel::EXTI1>;\n});\n\n#[embassy_executor::task]\nasync fn cyw43_task(runner: cyw43::Runner<'static, cyw43::SdioBus<SerialDataInterface<'static, 'static>>>) -> ! {\n    runner.run().await\n}\n\n#[embassy_executor::main]\nasync fn main(spawner: Spawner) {\n    info!(\"Hello world!\");\n    let mut config = Config::default();\n    {\n        use embassy_stm32::rcc::*;\n        config.rcc.hsi = Some(HSIPrescaler::DIV1);\n        config.rcc.csi = true;\n        config.rcc.pll1 = Some(Pll {\n            source: PllSource::HSI,\n            prediv: PllPreDiv::DIV4,\n            mul: PllMul::MUL25,\n            divp: Some(PllDiv::DIV2),\n            divq: Some(PllDiv::DIV4), // SPI1 cksel defaults to pll1_q\n            divr: None,\n        });\n        config.rcc.pll2 = Some(Pll {\n            source: PllSource::HSI,\n            prediv: PllPreDiv::DIV4,\n            mul: PllMul::MUL25,\n            divp: None,\n            divq: None,\n            divr: Some(PllDiv::DIV4), // 100mhz\n        });\n        config.rcc.sys = Sysclk::PLL1_P; // 200 Mhz\n        config.rcc.ahb_pre = AHBPrescaler::DIV1; // 200 Mhz\n        config.rcc.apb1_pre = APBPrescaler::DIV2; // 100 Mhz\n        config.rcc.apb2_pre = APBPrescaler::DIV2; // 100 Mhz\n        config.rcc.apb3_pre = APBPrescaler::DIV2; // 100 Mhz\n        config.rcc.voltage_scale = VoltageScale::Scale1;\n        config.rcc.mux.adcdacsel = mux::Adcdacsel::PLL2_R;\n    }\n    let p = embassy_stm32::init(config);\n\n    let mut wl_reg = Output::new(p.PD0, Level::Low, Speed::High);\n    let mut _bt_reg = Output::new(p.PG3, Level::Low, Speed::High);\n    let mut _sdio_reset = Output::new(p.PD11, Level::Low, Speed::High);\n\n    let _wl_wake_host = ExtiInput::new(p.PD1, p.EXTI1, Pull::Down, Irqs);\n\n    // let sdio_clk = Input::new(unsafe { p.PC12.clone_unchecked() }, Pull::None);\n    // let sdio_cmd = Input::new(unsafe { p.PD2.clone_unchecked() }, Pull::None);\n    // let sdio_data0 = Input::new(unsafe { p.PC8.clone_unchecked() }, Pull::None);\n    // let sdio_data1 = Input::new(unsafe { p.PC9.clone_unchecked() }, Pull::None);\n    // let sdio_data2 = Input::new(unsafe { p.PC10.clone_unchecked() }, Pull::None);\n    // let sdio_data3 = Input::new(unsafe { p.PC11.clone_unchecked() }, Pull::None);\n\n    let fw = aligned_bytes!(\"../../../../cyw43-firmware/43439A0.bin\");\n    let clm = aligned_bytes!(\"../../../../cyw43-firmware/43439A0_clm.bin\");\n    let nvram = aligned_bytes!(\"../../../../cyw43-firmware/nvram_sterling_lwb+.bin\");\n\n    let sdmmc = Sdmmc::new_4bit(\n        p.SDMMC1,\n        Irqs,\n        p.PC12,\n        p.PD2,\n        p.PC8,\n        p.PC9,\n        p.PC10,\n        p.PC11,\n        Default::default(),\n    );\n\n    {\n        // let _out1 = Output::new(p.PC12.reborrow(), Level::Low, Speed::High);\n        // let _out2 = Output::new(p.PD2.reborrow(), Level::High, Speed::High);\n        // let _out3 = Output::new(p.PC8.reborrow(), Level::High, Speed::High);\n        // let _out4 = Output::new(p.PC9.reborrow(), Level::High, Speed::High);\n        // let _out5 = Output::new(p.PC10.reborrow(), Level::High, Speed::High);\n        // let _out6 = Output::new(p.PC11.reborrow(), Level::High, Speed::High);\n\n        //        if sdio_clk.is_high() {\n        //            trace!(\"sdio_clk is high\");\n        //        } else {\n        //            trace!(\"sdio_clk is not high\");\n        //        }\n        //        if sdio_cmd.is_high() {\n        //            trace!(\"sdio_cmd is high\");\n        //        } else {\n        //            trace!(\"sdio_cmd is not high\");\n        //        }\n        //\n        //        if sdio_data0.is_high() {\n        //            trace!(\"sdio_data0 is high\");\n        //        } else {\n        //            trace!(\"sdio_data0 is not high\");\n        //        }\n        //        if sdio_data1.is_high() {\n        //            trace!(\"sdio_data1 is high\");\n        //        } else {\n        //            trace!(\"sdio_data1 is not high\");\n        //        }\n        //\n        //        if sdio_data2.is_high() {\n        //            trace!(\"sdio_data2 is high\");\n        //        } else {\n        //            trace!(\"sdio_data2 is not high\");\n        //        }\n        //\n        //        if sdio_data3.is_high() {\n        //            trace!(\"sdio_data3 is high\");\n        //        } else {\n        //            trace!(\"sdio_data3 is not high\");\n        //        }\n\n        trace!(\"WL_REG off/on\");\n        wl_reg.set_low();\n        Timer::after_millis(250).await;\n        wl_reg.set_high();\n        Timer::after_millis(10).await;\n    }\n\n    static SDMMC: StaticCell<Sdmmc<'static>> = StaticCell::new();\n    static STATE: StaticCell<cyw43::State> = StaticCell::new();\n\n    let sdmmc = SDMMC.init(sdmmc);\n    let state = STATE.init(cyw43::State::new());\n\n    let sdio = SerialDataInterface::new(sdmmc, mhz(50)).await.unwrap();\n\n    info!(\"new sdio\");\n\n    let (_net_device, mut control, runner) = cyw43::new_sdio(state, sdio, fw, nvram).await;\n\n    info!(\"spawn task\");\n\n    spawner.spawn(unwrap!(cyw43_task(runner)));\n\n    info!(\"init control\");\n\n    control.init(clm).await;\n\n    control\n        .set_power_management(cyw43::PowerManagementMode::PowerSave)\n        .await;\n\n    let mut scanner = control.scan(Default::default()).await;\n    while let Some(bss) = scanner.next().await {\n        if let Ok(ssid_str) = str::from_utf8(&bss.ssid) {\n            info!(\"scanned {} == {:x}\", ssid_str, bss.bssid);\n        }\n    }\n\n    cortex_m::asm::bkpt();\n}\n"
  },
  {
    "path": "examples/stm32h7/.cargo/config.toml",
    "content": "[target.thumbv7em-none-eabihf]\nrunner = 'probe-rs run --chip STM32H743ZITx'\n\n[build]\ntarget = \"thumbv7em-none-eabihf\" # Cortex-M4F and Cortex-M7F (with FPU)\n\n[env]\nDEFMT_LOG = \"trace\"\n"
  },
  {
    "path": "examples/stm32h7/Cargo.toml",
    "content": "[package]\nedition = \"2024\"\nname = \"embassy-stm32h7-examples\"\nversion = \"0.1.0\"\nlicense = \"MIT OR Apache-2.0\"\npublish = false\n\n[dependencies]\n# Change stm32h743bi to your chip name, if necessary.\nembassy-stm32 = { version = \"0.6.0\", path = \"../../embassy-stm32\", features = [\"defmt\", \"stm32h743bi\", \"time-driver-tim2\", \"exti\", \"memory-x\", \"unstable-pac\", \"chrono\"] }\nembassy-sync = { version = \"0.8.0\", path = \"../../embassy-sync\", features = [\"defmt\"] }\nembassy-embedded-hal = { version = \"0.6.0\", path = \"../../embassy-embedded-hal\" }\nembassy-executor = { version = \"0.10.0\", path = \"../../embassy-executor\", features = [\"platform-cortex-m\", \"executor-thread\", \"executor-interrupt\", \"defmt\"] }\nembassy-time = { version = \"0.5.1\", path = \"../../embassy-time\", features = [\"defmt\", \"defmt-timestamp-uptime\", \"tick-hz-32_768\"] }\nembassy-net = { version = \"0.9.0\", path = \"../../embassy-net\", features = [\"defmt\", \"tcp\", \"dhcpv4\", \"medium-ethernet\", \"proto-ipv6\", \"dns\"] }\nembassy-usb = { version = \"0.6.0\", path = \"../../embassy-usb\", features = [\"defmt\"] }\nembassy-futures = { version = \"0.1.2\", path = \"../../embassy-futures\" }\n\ndefmt = \"1.0.1\"\ndefmt-rtt = \"1.0.0\"\n\ncortex-m = { version = \"0.7.6\", features = [\"inline-asm\", \"critical-section-single-core\"] }\ncortex-m-rt = \"0.7.0\"\nembedded-hal = \"0.2.6\"\nembedded-hal-1 = { package = \"embedded-hal\", version = \"1.0\" }\nembedded-hal-async = { version = \"1.0\" }\nembedded-nal-async = \"0.9.0\"\nembedded-io-async = { version = \"0.7.0\" }\npanic-probe = { version = \"1.0.0\", features = [\"print-defmt\"] }\nheapless = { version = \"0.9\", default-features = false }\ncritical-section = \"1.1\"\nmicromath = \"2.0.0\"\nstm32-fmc = \"0.4.0\"\nembedded-storage = \"0.3.1\"\nstatic_cell = \"2\"\nchrono = { version = \"^0.4\", default-features = false }\ngrounded = \"0.2.0\"\n\n# cargo build/run\n[profile.dev]\ncodegen-units = 1\ndebug = 2\ndebug-assertions = true # <-\nincremental = false\nopt-level = 3 # <-\noverflow-checks = true # <-\n\n# cargo test\n[profile.test]\ncodegen-units = 1\ndebug = 2\ndebug-assertions = true # <-\nincremental = false\nopt-level = 3 # <-\noverflow-checks = true # <-\n\n# cargo build/run --release\n[profile.release]\ncodegen-units = 1\ndebug = 2\ndebug-assertions = false # <-\nincremental = false\nlto = 'fat'\nopt-level = 3 # <-\noverflow-checks = false # <-\n\n# cargo test --release\n[profile.bench]\ncodegen-units = 1\ndebug = 2\ndebug-assertions = false # <-\nincremental = false\nlto = 'fat'\nopt-level = 3 # <-\noverflow-checks = false # <-\n\n[package.metadata.embassy]\nbuild = [\n  { target = \"thumbv7em-none-eabi\", artifact-dir = \"out/examples/stm32h7\" }\n]\n"
  },
  {
    "path": "examples/stm32h7/build.rs",
    "content": "//! This build script copies the `memory.x` file from the crate root into\n//! a directory where the linker can always find it at build time.\n//! For many projects this is optional, as the linker always searches the\n//! project root directory -- wherever `Cargo.toml` is. However, if you\n//! are using a workspace or have a more complicated build setup, this\n//! build script becomes required. Additionally, by requesting that\n//! Cargo re-run the build script whenever `memory.x` is changed,\n//! updating `memory.x` ensures a rebuild of the application with the\n//! new memory settings.\n\nuse std::env;\nuse std::fs::File;\nuse std::io::Write;\nuse std::path::PathBuf;\n\nfn main() {\n    // Put `memory.x` in our output directory and ensure it's\n    // on the linker search path.\n    let out = &PathBuf::from(env::var_os(\"OUT_DIR\").unwrap());\n    File::create(out.join(\"memory.x\"))\n        .unwrap()\n        .write_all(include_bytes!(\"memory.x\"))\n        .unwrap();\n    println!(\"cargo:rustc-link-search={}\", out.display());\n\n    // By default, Cargo will re-run a build script whenever\n    // any file in the project changes. By specifying `memory.x`\n    // here, we ensure the build script is only re-run when\n    // `memory.x` is changed.\n    println!(\"cargo:rerun-if-changed=memory.x\");\n\n    println!(\"cargo:rustc-link-arg-bins=--nmagic\");\n    println!(\"cargo:rustc-link-arg-bins=-Tlink.x\");\n    println!(\"cargo:rustc-link-arg-bins=-Tdefmt.x\");\n}\n"
  },
  {
    "path": "examples/stm32h7/memory.x",
    "content": "MEMORY\n{\n    FLASH    : ORIGIN = 0x08000000, LENGTH = 2048K /* BANK_1 + BANK_2 */\n    RAM      : ORIGIN = 0x24000000, LENGTH = 512K  /* SRAM */\n    RAM_D3   : ORIGIN = 0x38000000, LENGTH = 64K   /* SRAM4 */\n}\n\nSECTIONS\n{\n    .ram_d3 :\n    {\n        *(.ram_d3)\n    } > RAM_D3\n}"
  },
  {
    "path": "examples/stm32h7/src/bin/adc.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_stm32::Config;\nuse embassy_stm32::adc::{Adc, SampleTime};\nuse embassy_time::Timer;\nuse {defmt_rtt as _, panic_probe as _};\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let mut config = Config::default();\n    {\n        use embassy_stm32::rcc::*;\n        config.rcc.hsi = Some(HSIPrescaler::DIV1);\n        config.rcc.csi = true;\n        config.rcc.pll1 = Some(Pll {\n            source: PllSource::HSI,\n            prediv: PllPreDiv::DIV4,\n            mul: PllMul::MUL50,\n            fracn: None,\n            divp: Some(PllDiv::DIV2),\n            divq: Some(PllDiv::DIV8), // SPI1 cksel defaults to pll1_q\n            divr: None,\n        });\n        config.rcc.pll2 = Some(Pll {\n            source: PllSource::HSI,\n            prediv: PllPreDiv::DIV4,\n            mul: PllMul::MUL50,\n            fracn: None,\n            divp: Some(PllDiv::DIV8), // 100mhz\n            divq: None,\n            divr: None,\n        });\n        config.rcc.sys = Sysclk::PLL1_P; // 400 Mhz\n        config.rcc.ahb_pre = AHBPrescaler::DIV2; // 200 Mhz\n        config.rcc.apb1_pre = APBPrescaler::DIV2; // 100 Mhz\n        config.rcc.apb2_pre = APBPrescaler::DIV2; // 100 Mhz\n        config.rcc.apb3_pre = APBPrescaler::DIV2; // 100 Mhz\n        config.rcc.apb4_pre = APBPrescaler::DIV2; // 100 Mhz\n        config.rcc.voltage_scale = VoltageScale::Scale1;\n        config.rcc.mux.adcsel = mux::Adcsel::PLL2_P;\n    }\n    let mut p = embassy_stm32::init(config);\n\n    info!(\"Hello World!\");\n\n    let mut adc = Adc::new(p.ADC3);\n\n    let mut vrefint_channel = adc.enable_vrefint();\n\n    loop {\n        let vrefint = adc.blocking_read(&mut vrefint_channel, SampleTime::CYCLES32_5);\n        info!(\"vrefint: {}\", vrefint);\n        let measured = adc.blocking_read(&mut p.PC0, SampleTime::CYCLES32_5);\n        info!(\"measured: {}\", measured);\n        Timer::after_millis(500).await;\n    }\n}\n"
  },
  {
    "path": "examples/stm32h7/src/bin/adc_dma.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_stm32::adc::{Adc, AdcChannel as _, SampleTime};\nuse embassy_stm32::{Config, bind_interrupts, dma, peripherals};\nuse embassy_time::Timer;\nuse {defmt_rtt as _, panic_probe as _};\n\n#[unsafe(link_section = \".ram_d3\")]\nstatic mut DMA_BUF: [u16; 2] = [0; 2];\n\nbind_interrupts!(struct Irqs {\n    DMA1_STREAM1 => dma::InterruptHandler<peripherals::DMA1_CH1>;\n});\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let mut read_buffer = unsafe { &mut DMA_BUF[..] };\n\n    let mut config = Config::default();\n    {\n        use embassy_stm32::rcc::*;\n        config.rcc.hsi = Some(HSIPrescaler::DIV1);\n        config.rcc.csi = true;\n        config.rcc.pll1 = Some(Pll {\n            source: PllSource::HSI,\n            prediv: PllPreDiv::DIV4,\n            mul: PllMul::MUL50,\n            fracn: None,\n            divp: Some(PllDiv::DIV2),\n            divq: Some(PllDiv::DIV8), // SPI1 cksel defaults to pll1_q\n            divr: None,\n        });\n        config.rcc.pll2 = Some(Pll {\n            source: PllSource::HSI,\n            prediv: PllPreDiv::DIV4,\n            mul: PllMul::MUL50,\n            fracn: None,\n            divp: Some(PllDiv::DIV8), // 100mhz\n            divq: None,\n            divr: None,\n        });\n        config.rcc.sys = Sysclk::PLL1_P; // 400 Mhz\n        config.rcc.ahb_pre = AHBPrescaler::DIV2; // 200 Mhz\n        config.rcc.apb1_pre = APBPrescaler::DIV2; // 100 Mhz\n        config.rcc.apb2_pre = APBPrescaler::DIV2; // 100 Mhz\n        config.rcc.apb3_pre = APBPrescaler::DIV2; // 100 Mhz\n        config.rcc.apb4_pre = APBPrescaler::DIV2; // 100 Mhz\n        config.rcc.voltage_scale = VoltageScale::Scale1;\n        config.rcc.mux.adcsel = mux::Adcsel::PLL2_P;\n    }\n    let p = embassy_stm32::init(config);\n\n    info!(\"Hello World!\");\n\n    let mut adc = Adc::new(p.ADC3);\n\n    let mut dma = p.DMA1_CH1;\n    let mut vrefint_channel = adc.enable_vrefint().degrade_adc();\n    let mut pc0 = p.PC0.degrade_adc();\n\n    loop {\n        adc.read(\n            dma.reborrow(),\n            Irqs,\n            [\n                (&mut vrefint_channel, SampleTime::CYCLES387_5),\n                (&mut pc0, SampleTime::CYCLES810_5),\n            ]\n            .into_iter(),\n            &mut read_buffer,\n        )\n        .await;\n\n        let vrefint = read_buffer[0];\n        let measured = read_buffer[1];\n        info!(\"vrefint: {}\", vrefint);\n        info!(\"measured: {}\", measured);\n        Timer::after_millis(500).await;\n    }\n}\n"
  },
  {
    "path": "examples/stm32h7/src/bin/blinky.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_stm32::gpio::{Level, Output, Speed};\nuse embassy_time::Timer;\nuse {defmt_rtt as _, panic_probe as _};\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let p = embassy_stm32::init(Default::default());\n    info!(\"Hello World!\");\n\n    let mut led = Output::new(p.PB14, Level::High, Speed::Low);\n\n    loop {\n        info!(\"high\");\n        led.set_high();\n        Timer::after_millis(500).await;\n\n        info!(\"low\");\n        led.set_low();\n        Timer::after_millis(500).await;\n    }\n}\n"
  },
  {
    "path": "examples/stm32h7/src/bin/button_exti.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_stm32::exti::{self, ExtiInput};\nuse embassy_stm32::gpio::Pull;\nuse embassy_stm32::{bind_interrupts, interrupt};\nuse {defmt_rtt as _, panic_probe as _};\n\nbind_interrupts!(\n    pub struct Irqs{\n        EXTI15_10 => exti::InterruptHandler<interrupt::typelevel::EXTI15_10>;\n});\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let p = embassy_stm32::init(Default::default());\n    info!(\"Hello World!\");\n\n    let mut button = ExtiInput::new(p.PC13, p.EXTI13, Pull::Down, Irqs);\n\n    info!(\"Press the USER button...\");\n\n    loop {\n        button.wait_for_rising_edge().await;\n        info!(\"Pressed!\");\n        button.wait_for_falling_edge().await;\n        info!(\"Released!\");\n    }\n}\n"
  },
  {
    "path": "examples/stm32h7/src/bin/camera.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse embassy_executor::Spawner;\nuse embassy_stm32::dcmi::{self, *};\nuse embassy_stm32::gpio::{Level, Output, Speed};\nuse embassy_stm32::i2c::I2c;\nuse embassy_stm32::rcc::{Mco, Mco1Source, McoConfig, McoPrescaler};\nuse embassy_stm32::{Config, bind_interrupts, dma, i2c, peripherals};\nuse embassy_time::Timer;\nuse ov7725::*;\nuse {defmt_rtt as _, panic_probe as _};\n\nconst WIDTH: usize = 100;\nconst HEIGHT: usize = 100;\n\nstatic mut FRAME: [u32; WIDTH * HEIGHT / 2] = [0u32; WIDTH * HEIGHT / 2];\n\nbind_interrupts!(struct Irqs {\n    I2C1_EV => i2c::EventInterruptHandler<peripherals::I2C1>;\n    I2C1_ER => i2c::ErrorInterruptHandler<peripherals::I2C1>;\n    DCMI => dcmi::InterruptHandler<peripherals::DCMI>;\n    DMA1_STREAM0 => dma::InterruptHandler<peripherals::DMA1_CH0>;\n    DMA1_STREAM1 => dma::InterruptHandler<peripherals::DMA1_CH1>;\n    DMA1_STREAM2 => dma::InterruptHandler<peripherals::DMA1_CH2>;\n});\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let mut config = Config::default();\n    {\n        use embassy_stm32::rcc::*;\n        config.rcc.hsi = Some(HSIPrescaler::DIV1);\n        config.rcc.csi = true;\n        config.rcc.pll1 = Some(Pll {\n            source: PllSource::HSI,\n            prediv: PllPreDiv::DIV4,\n            mul: PllMul::MUL50,\n            fracn: None,\n            divp: Some(PllDiv::DIV2),\n            divq: Some(PllDiv::DIV8), // 100mhz\n            divr: None,\n        });\n        config.rcc.sys = Sysclk::PLL1_P; // 400 Mhz\n        config.rcc.ahb_pre = AHBPrescaler::DIV2; // 200 Mhz\n        config.rcc.apb1_pre = APBPrescaler::DIV2; // 100 Mhz\n        config.rcc.apb2_pre = APBPrescaler::DIV2; // 100 Mhz\n        config.rcc.apb3_pre = APBPrescaler::DIV2; // 100 Mhz\n        config.rcc.apb4_pre = APBPrescaler::DIV2; // 100 Mhz\n        config.rcc.voltage_scale = VoltageScale::Scale1;\n    }\n    let p = embassy_stm32::init(config);\n\n    defmt::info!(\"Hello World!\");\n\n    let mco_config = {\n        let mut config = McoConfig::default();\n        config.prescaler = McoPrescaler::DIV3;\n        config\n    };\n\n    let mco = Mco::new(p.MCO1, p.PA8, Mco1Source::HSI, mco_config);\n\n    let mut led = Output::new(p.PE3, Level::High, Speed::Low);\n    let cam_i2c = I2c::new(p.I2C1, p.PB8, p.PB9, p.DMA1_CH1, p.DMA1_CH2, Irqs, Default::default());\n\n    let mut camera = Ov7725::new(cam_i2c, mco);\n\n    defmt::unwrap!(camera.init().await);\n\n    let manufacturer_id = defmt::unwrap!(camera.read_manufacturer_id().await);\n    let camera_id = defmt::unwrap!(camera.read_product_id().await);\n\n    defmt::info!(\"manufacturer: 0x{:x}, pid: 0x{:x}\", manufacturer_id, camera_id);\n\n    let config = dcmi::Config::default();\n    let mut dcmi = Dcmi::new_8bit(\n        p.DCMI, p.DMA1_CH0, Irqs, p.PC6, p.PC7, p.PE0, p.PE1, p.PE4, p.PD3, p.PE5, p.PE6, p.PB7, p.PA4, p.PA6, config,\n    );\n\n    defmt::info!(\"attempting capture\");\n    defmt::unwrap!(dcmi.capture(unsafe { &mut *core::ptr::addr_of_mut!(FRAME) }).await);\n\n    defmt::info!(\"captured frame: {:x}\", unsafe { &*core::ptr::addr_of!(FRAME) });\n\n    defmt::info!(\"main loop running\");\n    loop {\n        defmt::info!(\"high\");\n        led.set_high();\n        Timer::after_millis(500).await;\n\n        defmt::info!(\"low\");\n        led.set_low();\n        Timer::after_millis(500).await;\n    }\n}\n\nmod ov7725 {\n    use core::marker::PhantomData;\n\n    use defmt::Format;\n    use embassy_stm32::rcc::{Mco, McoInstance};\n    use embassy_time::Timer;\n    use embedded_hal_async::i2c::I2c;\n\n    #[repr(u8)]\n    pub enum RgbFormat {\n        Gbr422 = 0,\n        RGB565 = 1,\n        RGB555 = 2,\n        RGB444 = 3,\n    }\n    pub enum PixelFormat {\n        Yuv,\n        ProcessedRawBayer,\n        Rgb(RgbFormat),\n        RawBayer,\n    }\n\n    impl From<PixelFormat> for u8 {\n        fn from(raw: PixelFormat) -> Self {\n            match raw {\n                PixelFormat::Yuv => 0,\n                PixelFormat::ProcessedRawBayer => 1,\n                PixelFormat::Rgb(mode) => 2 | ((mode as u8) << 2),\n                PixelFormat::RawBayer => 3,\n            }\n        }\n    }\n\n    #[derive(Clone, Copy)]\n    #[repr(u8)]\n    #[allow(unused)]\n    pub enum Register {\n        Gain = 0x00,\n        Blue = 0x01,\n        Red = 0x02,\n        Green = 0x03,\n        BAvg = 0x05,\n        GAvg = 0x06,\n        RAvg = 0x07,\n        Aech = 0x08,\n        Com2 = 0x09,\n        PId = 0x0a,\n        Ver = 0x0b,\n        Com3 = 0x0c,\n        Com4 = 0x0d,\n        Com5 = 0x0e,\n        Com6 = 0x0f,\n        Aec = 0x10,\n        ClkRc = 0x11,\n        Com7 = 0x12,\n        Com8 = 0x13,\n        Com9 = 0x14,\n        Com10 = 0x15,\n        Reg16 = 0x16,\n        HStart = 0x17,\n        HSize = 0x18,\n        VStart = 0x19,\n        VSize = 0x1a,\n        PShift = 0x1b,\n        MidH = 0x1c,\n        MidL = 0x1d,\n        Laec = 0x1f,\n        Com11 = 0x20,\n        BdBase = 0x22,\n        BdMStep = 0x23,\n        Aew = 0x24,\n        Aeb = 0x25,\n        Vpt = 0x26,\n        Reg28 = 0x28,\n        HOutSize = 0x29,\n        EXHCH = 0x2a,\n        EXHCL = 0x2b,\n        VOutSize = 0x2c,\n        Advfl = 0x2d,\n        Advfh = 0x2e,\n        Yave = 0x2f,\n        LumHTh = 0x30,\n        LumLTh = 0x31,\n        HRef = 0x32,\n        DspCtrl4 = 0x67,\n        DspAuto = 0xac,\n    }\n\n    const CAM_ADDR: u8 = 0x21;\n\n    #[derive(Format, PartialEq, Eq)]\n    pub enum Error<I2cError: Format> {\n        I2c(I2cError),\n    }\n\n    pub struct Ov7725<'d, Bus: I2c> {\n        phantom: PhantomData<&'d ()>,\n        bus: Bus,\n    }\n\n    impl<'d, Bus> Ov7725<'d, Bus>\n    where\n        Bus: I2c,\n        Bus::Error: Format,\n    {\n        pub fn new<T>(bus: Bus, _mco: Mco<T>) -> Self\n        where\n            T: McoInstance,\n        {\n            Self {\n                phantom: PhantomData,\n                bus,\n            }\n        }\n\n        pub async fn init(&mut self) -> Result<(), Error<Bus::Error>> {\n            Timer::after_millis(500).await;\n            self.reset_regs().await?;\n            Timer::after_millis(500).await;\n            self.set_pixformat().await?;\n            self.set_resolution().await?;\n            Ok(())\n        }\n\n        pub async fn read_manufacturer_id(&mut self) -> Result<u16, Error<Bus::Error>> {\n            Ok(u16::from_le_bytes([\n                self.read(Register::MidL).await?,\n                self.read(Register::MidH).await?,\n            ]))\n        }\n\n        pub async fn read_product_id(&mut self) -> Result<u16, Error<Bus::Error>> {\n            Ok(u16::from_le_bytes([\n                self.read(Register::Ver).await?,\n                self.read(Register::PId).await?,\n            ]))\n        }\n\n        async fn reset_regs(&mut self) -> Result<(), Error<Bus::Error>> {\n            self.write(Register::Com7, 0x80).await\n        }\n\n        async fn set_pixformat(&mut self) -> Result<(), Error<Bus::Error>> {\n            self.write(Register::DspCtrl4, 0).await?;\n            let mut com7 = self.read(Register::Com7).await?;\n            com7 |= u8::from(PixelFormat::Rgb(RgbFormat::RGB565));\n            self.write(Register::Com7, com7).await?;\n            Ok(())\n        }\n\n        async fn set_resolution(&mut self) -> Result<(), Error<Bus::Error>> {\n            let horizontal: u16 = super::WIDTH as u16;\n            let vertical: u16 = super::HEIGHT as u16;\n\n            let h_high = (horizontal >> 2) as u8;\n            let v_high = (vertical >> 1) as u8;\n            let h_low = (horizontal & 0x03) as u8;\n            let v_low = (vertical & 0x01) as u8;\n\n            self.write(Register::HOutSize, h_high).await?;\n            self.write(Register::VOutSize, v_high).await?;\n            self.write(Register::EXHCH, h_low | (v_low << 2)).await?;\n\n            self.write(Register::Com3, 0xd1).await?;\n\n            let com3 = self.read(Register::Com3).await?;\n            let vflip = com3 & 0x80 > 0;\n\n            self.modify(Register::HRef, |reg| reg & 0xbf | if vflip { 0x40 } else { 0x40 })\n                .await?;\n\n            if horizontal <= 320 || vertical <= 240 {\n                self.write(Register::HStart, 0x3f).await?;\n                self.write(Register::HSize, 0x50).await?;\n                self.write(Register::VStart, 0x02).await?; // TODO vflip is subtracted in the original code\n                self.write(Register::VSize, 0x78).await?;\n            } else {\n                defmt::panic!(\"VGA resolutions not yet supported.\");\n            }\n\n            Ok(())\n        }\n\n        async fn read(&mut self, register: Register) -> Result<u8, Error<Bus::Error>> {\n            let mut buffer = [0u8; 1];\n            self.bus\n                .write_read(CAM_ADDR, &[register as u8], &mut buffer[..1])\n                .await\n                .map_err(Error::I2c)?;\n            Ok(buffer[0])\n        }\n\n        async fn write(&mut self, register: Register, value: u8) -> Result<(), Error<Bus::Error>> {\n            self.bus\n                .write(CAM_ADDR, &[register as u8, value])\n                .await\n                .map_err(Error::I2c)\n        }\n\n        async fn modify<F: FnOnce(u8) -> u8>(&mut self, register: Register, f: F) -> Result<(), Error<Bus::Error>> {\n            let value = self.read(register).await?;\n            let value = f(value);\n            self.write(register, value).await\n        }\n    }\n}\n"
  },
  {
    "path": "examples/stm32h7/src/bin/can.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_stm32::peripherals::*;\nuse embassy_stm32::{Config, bind_interrupts, can, rcc};\nuse embassy_time::Timer;\nuse {defmt_rtt as _, panic_probe as _};\n\nbind_interrupts!(struct Irqs {\n    FDCAN1_IT0 => can::IT0InterruptHandler<FDCAN1>;\n    FDCAN1_IT1 => can::IT1InterruptHandler<FDCAN1>;\n});\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let mut config = Config::default();\n    config.rcc.hse = Some(rcc::Hse {\n        freq: embassy_stm32::time::Hertz(25_000_000),\n        mode: rcc::HseMode::Oscillator,\n    });\n    config.rcc.mux.fdcansel = rcc::mux::Fdcansel::HSE;\n\n    let peripherals = embassy_stm32::init(config);\n\n    let mut can = can::CanConfigurator::new(peripherals.FDCAN1, peripherals.PA11, peripherals.PA12, Irqs);\n\n    // 250k bps\n    can.set_bitrate(250_000);\n\n    //let mut can = can.into_internal_loopback_mode();\n    let mut can = can.into_normal_mode();\n\n    info!(\"CAN Configured\");\n\n    let mut i = 0;\n    let mut last_read_ts = embassy_time::Instant::now();\n\n    loop {\n        let frame = can::frame::Frame::new_extended(0x123456F, &[i; 8]).unwrap();\n        info!(\"Writing frame\");\n        _ = can.write(&frame).await;\n\n        match can.read().await {\n            Ok(envelope) => {\n                let (rx_frame, ts) = envelope.parts();\n                let delta = (ts - last_read_ts).as_millis();\n                last_read_ts = ts;\n                info!(\n                    \"Rx: {:x} {:x} {:x} {:x} --- NEW {}\",\n                    rx_frame.data()[0],\n                    rx_frame.data()[1],\n                    rx_frame.data()[2],\n                    rx_frame.data()[3],\n                    delta,\n                )\n            }\n            Err(_err) => error!(\"Error in frame\"),\n        }\n\n        Timer::after_millis(250).await;\n\n        i += 1;\n        if i > 3 {\n            break;\n        }\n    }\n\n    let (mut tx, mut rx, _props) = can.split();\n    // With split\n    loop {\n        let frame = can::frame::Frame::new_extended(0x123456F, &[i; 8]).unwrap();\n        info!(\"Writing frame\");\n        _ = tx.write(&frame).await;\n\n        match rx.read().await {\n            Ok(envelope) => {\n                let (rx_frame, ts) = envelope.parts();\n                let delta = (ts - last_read_ts).as_millis();\n                last_read_ts = ts;\n                info!(\n                    \"Rx: {:x} {:x} {:x} {:x} --- NEW {}\",\n                    rx_frame.data()[0],\n                    rx_frame.data()[1],\n                    rx_frame.data()[2],\n                    rx_frame.data()[3],\n                    delta,\n                )\n            }\n            Err(_err) => error!(\"Error in frame\"),\n        }\n\n        Timer::after_millis(250).await;\n\n        i = i.wrapping_add(1);\n    }\n}\n"
  },
  {
    "path": "examples/stm32h7/src/bin/dac.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse cortex_m_rt::entry;\nuse defmt::*;\nuse embassy_stm32::Config;\nuse embassy_stm32::dac::{DacChannel, Value};\nuse {defmt_rtt as _, panic_probe as _};\n\n#[entry]\nfn main() -> ! {\n    info!(\"Hello World, dude!\");\n\n    let mut config = Config::default();\n    {\n        use embassy_stm32::rcc::*;\n        config.rcc.hsi = Some(HSIPrescaler::DIV1);\n        config.rcc.csi = true;\n        config.rcc.pll1 = Some(Pll {\n            source: PllSource::HSI,\n            prediv: PllPreDiv::DIV4,\n            mul: PllMul::MUL50,\n            fracn: None,\n            divp: Some(PllDiv::DIV2),\n            divq: Some(PllDiv::DIV8), // 100mhz\n            divr: None,\n        });\n        config.rcc.pll2 = Some(Pll {\n            source: PllSource::HSI,\n            prediv: PllPreDiv::DIV4,\n            mul: PllMul::MUL50,\n            fracn: None,\n            divp: Some(PllDiv::DIV8), // 100mhz\n            divq: None,\n            divr: None,\n        });\n        config.rcc.sys = Sysclk::PLL1_P; // 400 Mhz\n        config.rcc.ahb_pre = AHBPrescaler::DIV2; // 200 Mhz\n        config.rcc.apb1_pre = APBPrescaler::DIV2; // 100 Mhz\n        config.rcc.apb2_pre = APBPrescaler::DIV2; // 100 Mhz\n        config.rcc.apb3_pre = APBPrescaler::DIV2; // 100 Mhz\n        config.rcc.apb4_pre = APBPrescaler::DIV2; // 100 Mhz\n        config.rcc.voltage_scale = VoltageScale::Scale1;\n        config.rcc.mux.adcsel = mux::Adcsel::PLL2_P;\n    }\n    let p = embassy_stm32::init(config);\n\n    let mut dac = DacChannel::new_blocking(p.DAC1, p.PA4);\n\n    loop {\n        for v in 0..=255 {\n            dac.set(Value::Bit8(to_sine_wave(v)));\n        }\n    }\n}\n\nuse micromath::F32Ext;\n\nfn to_sine_wave(v: u8) -> u8 {\n    if v >= 128 {\n        // top half\n        let r = 3.14 * ((v - 128) as f32 / 128.0);\n        (r.sin() * 128.0 + 127.0) as u8\n    } else {\n        // bottom half\n        let r = 3.14 + 3.14 * (v as f32 / 128.0);\n        (r.sin() * 128.0 + 127.0) as u8\n    }\n}\n"
  },
  {
    "path": "examples/stm32h7/src/bin/dac_dma.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_stm32::dac::{DacChannel, ValueArray};\nuse embassy_stm32::mode::Async;\nuse embassy_stm32::pac::timer::vals::Mms;\nuse embassy_stm32::peripherals::{DMA1_CH3, DMA1_CH4, TIM6, TIM7};\nuse embassy_stm32::rcc::frequency;\nuse embassy_stm32::time::Hertz;\nuse embassy_stm32::timer::low_level::Timer;\nuse embassy_stm32::triggers::{TIM6_TRGO, TIM7_TRGO};\nuse embassy_stm32::{Peri, bind_interrupts, dma};\nuse micromath::F32Ext;\nuse {defmt_rtt as _, panic_probe as _};\n\nbind_interrupts!(struct Irqs {\n    DMA1_STREAM3 => dma::InterruptHandler<DMA1_CH3>;\n    DMA1_STREAM4 => dma::InterruptHandler<DMA1_CH4>;\n});\n\n#[embassy_executor::main]\nasync fn main(spawner: Spawner) {\n    let mut config = embassy_stm32::Config::default();\n    {\n        use embassy_stm32::rcc::*;\n        config.rcc.hsi = Some(HSIPrescaler::DIV1);\n        config.rcc.csi = true;\n        config.rcc.pll1 = Some(Pll {\n            source: PllSource::HSI,\n            prediv: PllPreDiv::DIV4,\n            mul: PllMul::MUL50,\n            fracn: None,\n            divp: Some(PllDiv::DIV2),\n            divq: Some(PllDiv::DIV8), // 100mhz\n            divr: None,\n        });\n        config.rcc.pll2 = Some(Pll {\n            source: PllSource::HSI,\n            prediv: PllPreDiv::DIV4,\n            mul: PllMul::MUL50,\n            fracn: None,\n            divp: Some(PllDiv::DIV8), // 100mhz\n            divq: None,\n            divr: None,\n        });\n        config.rcc.sys = Sysclk::PLL1_P; // 400 Mhz\n        config.rcc.ahb_pre = AHBPrescaler::DIV2; // 200 Mhz\n        config.rcc.apb1_pre = APBPrescaler::DIV2; // 100 Mhz\n        config.rcc.apb2_pre = APBPrescaler::DIV2; // 100 Mhz\n        config.rcc.apb3_pre = APBPrescaler::DIV2; // 100 Mhz\n        config.rcc.apb4_pre = APBPrescaler::DIV2; // 100 Mhz\n        config.rcc.voltage_scale = VoltageScale::Scale1;\n        config.rcc.mux.adcsel = mux::Adcsel::PLL2_P;\n    }\n\n    // Initialize the board and obtain a Peripherals instance\n    let p: embassy_stm32::Peripherals = embassy_stm32::init(config);\n\n    // Obtain two independent channels (p.DAC1 can only be consumed once, though!)\n    let (dac_ch1, dac_ch2) = embassy_stm32::dac::Dac::new_triggered(\n        p.DAC1, p.DMA1_CH3, p.DMA1_CH4, TIM6_TRGO, TIM7_TRGO, Irqs, p.PA4, p.PA5,\n    )\n    .split();\n\n    spawner.spawn(dac_task1(p.TIM6, dac_ch1).unwrap());\n    spawner.spawn(dac_task2(p.TIM7, dac_ch2).unwrap());\n}\n\n#[embassy_executor::task]\nasync fn dac_task1(tim: Peri<'static, TIM6>, mut dac: DacChannel<'static, Async>) {\n    let data: &[u8; 256] = &calculate_array::<256>();\n\n    info!(\"TIM6 frequency is {}\", frequency::<TIM6>());\n    const FREQUENCY: Hertz = Hertz::hz(200);\n\n    // Compute the reload value such that we obtain the FREQUENCY for the sine\n    let reload: u32 = (frequency::<TIM6>().0 / FREQUENCY.0) / data.len() as u32;\n\n    // Depends on your clock and on the specific chip used, you may need higher or lower values here\n    if reload < 10 {\n        error!(\"Reload value {} below threshold!\", reload);\n    }\n\n    dac.set_triggering(true);\n    dac.enable();\n\n    let tim = Timer::new(tim);\n    tim.regs_basic().arr().modify(|w| w.set_arr(reload as u16 - 1));\n    tim.regs_basic().cr2().modify(|w| w.set_mms(Mms::UPDATE));\n    tim.regs_basic().cr1().modify(|w| {\n        w.set_opm(false);\n        w.set_cen(true);\n    });\n\n    debug!(\n        \"TIM6 Frequency {}, Target Frequency {}, Reload {}, Reload as u16 {}, Samples {}\",\n        frequency::<TIM6>(),\n        FREQUENCY,\n        reload,\n        reload as u16,\n        data.len()\n    );\n\n    // Loop technically not necessary if DMA circular mode is enabled\n    loop {\n        info!(\"Loop DAC1\");\n        dac.write(ValueArray::Bit8(data), true).await;\n    }\n}\n\n#[embassy_executor::task]\nasync fn dac_task2(tim: Peri<'static, TIM7>, mut dac: DacChannel<'static, Async>) {\n    let data: &[u8; 256] = &calculate_array::<256>();\n\n    info!(\"TIM7 frequency is {}\", frequency::<TIM6>());\n\n    const FREQUENCY: Hertz = Hertz::hz(600);\n    let reload: u32 = (frequency::<TIM7>().0 / FREQUENCY.0) / data.len() as u32;\n\n    if reload < 10 {\n        error!(\"Reload value {} below threshold!\", reload);\n    }\n\n    let tim = Timer::new(tim);\n    tim.regs_basic().arr().modify(|w| w.set_arr(reload as u16 - 1));\n    tim.regs_basic().cr2().modify(|w| w.set_mms(Mms::UPDATE));\n    tim.regs_basic().cr1().modify(|w| {\n        w.set_opm(false);\n        w.set_cen(true);\n    });\n\n    dac.set_triggering(true);\n    dac.enable();\n\n    debug!(\n        \"TIM7 Frequency {}, Target Frequency {}, Reload {}, Reload as u16 {}, Samples {}\",\n        frequency::<TIM7>(),\n        FREQUENCY,\n        reload,\n        reload as u16,\n        data.len()\n    );\n\n    dac.write(ValueArray::Bit8(data), true).await;\n}\n\nfn to_sine_wave(v: u8) -> u8 {\n    if v >= 128 {\n        // top half\n        let r = 3.14 * ((v - 128) as f32 / 128.0);\n        (r.sin() * 128.0 + 127.0) as u8\n    } else {\n        // bottom half\n        let r = 3.14 + 3.14 * (v as f32 / 128.0);\n        (r.sin() * 128.0 + 127.0) as u8\n    }\n}\n\nfn calculate_array<const N: usize>() -> [u8; N] {\n    let mut res = [0; N];\n    let mut i = 0;\n    while i < N {\n        res[i] = to_sine_wave(i as u8);\n        i += 1;\n    }\n    res\n}\n"
  },
  {
    "path": "examples/stm32h7/src/bin/eth.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_net::tcp::TcpSocket;\nuse embassy_net::{Ipv4Address, StackResources};\nuse embassy_stm32::eth::{Ethernet, GenericPhy, PacketQueue, Sma};\nuse embassy_stm32::peripherals::{ETH, ETH_SMA};\nuse embassy_stm32::rng::Rng;\nuse embassy_stm32::{Config, bind_interrupts, eth, peripherals, rng};\nuse embassy_time::Timer;\nuse embedded_io_async::Write;\nuse static_cell::StaticCell;\nuse {defmt_rtt as _, panic_probe as _};\n\nbind_interrupts!(struct Irqs {\n    ETH => eth::InterruptHandler;\n    RNG => rng::InterruptHandler<peripherals::RNG>;\n});\n\ntype Device = Ethernet<'static, ETH, GenericPhy<Sma<'static, ETH_SMA>>>;\n\n#[embassy_executor::task]\nasync fn net_task(mut runner: embassy_net::Runner<'static, Device>) -> ! {\n    runner.run().await\n}\n\n#[embassy_executor::main]\nasync fn main(spawner: Spawner) -> ! {\n    let mut config = Config::default();\n    {\n        use embassy_stm32::rcc::*;\n        config.rcc.hsi = Some(HSIPrescaler::DIV1);\n        config.rcc.csi = true;\n        config.rcc.hsi48 = Some(Default::default()); // needed for RNG\n        config.rcc.pll1 = Some(Pll {\n            source: PllSource::HSI,\n            prediv: PllPreDiv::DIV4,\n            mul: PllMul::MUL50,\n            fracn: None,\n            divp: Some(PllDiv::DIV2),\n            divq: None,\n            divr: None,\n        });\n        config.rcc.sys = Sysclk::PLL1_P; // 400 Mhz\n        config.rcc.ahb_pre = AHBPrescaler::DIV2; // 200 Mhz\n        config.rcc.apb1_pre = APBPrescaler::DIV2; // 100 Mhz\n        config.rcc.apb2_pre = APBPrescaler::DIV2; // 100 Mhz\n        config.rcc.apb3_pre = APBPrescaler::DIV2; // 100 Mhz\n        config.rcc.apb4_pre = APBPrescaler::DIV2; // 100 Mhz\n        config.rcc.voltage_scale = VoltageScale::Scale1;\n    }\n    let p = embassy_stm32::init(config);\n    info!(\"Hello World!\");\n\n    // Generate random seed.\n    let mut rng = Rng::new(p.RNG, Irqs);\n    let mut seed = [0; 8];\n    rng.fill_bytes(&mut seed);\n    let seed = u64::from_le_bytes(seed);\n\n    let mac_addr = [0x00, 0x00, 0xDE, 0xAD, 0xBE, 0xEF];\n\n    static PACKETS: StaticCell<PacketQueue<4, 4>> = StaticCell::new();\n    // warning: Not all STM32H7 devices have the exact same pins here\n    // for STM32H747XIH, replace p.PB13 for PG12\n    let device = Ethernet::new(\n        PACKETS.init(PacketQueue::<4, 4>::new()),\n        p.ETH,\n        Irqs,\n        p.PA1,  // ref_clk\n        p.PA7,  // CRS_DV: Carrier Sense\n        p.PC4,  // RX_D0: Received Bit 0\n        p.PC5,  // RX_D1: Received Bit 1\n        p.PG13, // TX_D0: Transmit Bit 0\n        p.PB13, // TX_D1: Transmit Bit 1\n        p.PG11, // TX_EN: Transmit Enable\n        mac_addr,\n        p.ETH_SMA,\n        p.PA2, // mdio\n        p.PC1, // mdc\n    );\n\n    let config = embassy_net::Config::dhcpv4(Default::default());\n    //let config = embassy_net::Config::ipv4_static(embassy_net::StaticConfigV4 {\n    //    address: Ipv4Cidr::new(Ipv4Address::new(10, 42, 0, 61), 24),\n    //    dns_servers: Vec::new(),\n    //    gateway: Some(Ipv4Address::new(10, 42, 0, 1)),\n    //});\n\n    // Init network stack\n    static RESOURCES: StaticCell<StackResources<3>> = StaticCell::new();\n    let (stack, runner) = embassy_net::new(device, config, RESOURCES.init(StackResources::new()), seed);\n\n    // Launch network task\n    spawner.spawn(unwrap!(net_task(runner)));\n\n    // Ensure DHCP configuration is up before trying connect\n    stack.wait_config_up().await;\n\n    info!(\"Network task initialized\");\n\n    // Then we can use it!\n    let mut rx_buffer = [0; 1024];\n    let mut tx_buffer = [0; 1024];\n\n    loop {\n        let mut socket = TcpSocket::new(stack, &mut rx_buffer, &mut tx_buffer);\n\n        socket.set_timeout(Some(embassy_time::Duration::from_secs(10)));\n\n        // You need to start a server on the host machine, for example: `nc -l 8000`\n        let remote_endpoint = (Ipv4Address::new(10, 42, 0, 1), 8000);\n        info!(\"connecting...\");\n        let r = socket.connect(remote_endpoint).await;\n        if let Err(e) = r {\n            info!(\"connect error: {:?}\", e);\n            Timer::after_secs(1).await;\n            continue;\n        }\n        info!(\"connected!\");\n        loop {\n            let r = socket.write_all(b\"Hello\\n\").await;\n            if let Err(e) = r {\n                info!(\"write error: {:?}\", e);\n                break;\n            }\n            Timer::after_secs(1).await;\n        }\n    }\n}\n"
  },
  {
    "path": "examples/stm32h7/src/bin/eth_client.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse core::net::{Ipv4Addr, SocketAddr, SocketAddrV4};\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_net::StackResources;\nuse embassy_net::tcp::client::{TcpClient, TcpClientState};\nuse embassy_stm32::eth::{Ethernet, GenericPhy, PacketQueue, Sma};\nuse embassy_stm32::peripherals::{ETH, ETH_SMA};\nuse embassy_stm32::rng::Rng;\nuse embassy_stm32::{Config, bind_interrupts, eth, peripherals, rng};\nuse embassy_time::Timer;\nuse embedded_io_async::Write;\nuse embedded_nal_async::TcpConnect;\nuse static_cell::StaticCell;\nuse {defmt_rtt as _, panic_probe as _};\n\nbind_interrupts!(struct Irqs {\n    ETH => eth::InterruptHandler;\n    RNG => rng::InterruptHandler<peripherals::RNG>;\n});\n\ntype Device = Ethernet<'static, ETH, GenericPhy<Sma<'static, ETH_SMA>>>;\n\n#[embassy_executor::task]\nasync fn net_task(mut runner: embassy_net::Runner<'static, Device>) -> ! {\n    runner.run().await\n}\n\n#[embassy_executor::main]\nasync fn main(spawner: Spawner) -> ! {\n    let mut config = Config::default();\n    {\n        use embassy_stm32::rcc::*;\n        config.rcc.hsi = Some(HSIPrescaler::DIV1);\n        config.rcc.csi = true;\n        config.rcc.hsi48 = Some(Default::default()); // needed for RNG\n        config.rcc.pll1 = Some(Pll {\n            source: PllSource::HSI,\n            prediv: PllPreDiv::DIV4,\n            mul: PllMul::MUL50,\n            fracn: None,\n            divp: Some(PllDiv::DIV2),\n            divq: None,\n            divr: None,\n        });\n        config.rcc.sys = Sysclk::PLL1_P; // 400 Mhz\n        config.rcc.ahb_pre = AHBPrescaler::DIV2; // 200 Mhz\n        config.rcc.apb1_pre = APBPrescaler::DIV2; // 100 Mhz\n        config.rcc.apb2_pre = APBPrescaler::DIV2; // 100 Mhz\n        config.rcc.apb3_pre = APBPrescaler::DIV2; // 100 Mhz\n        config.rcc.apb4_pre = APBPrescaler::DIV2; // 100 Mhz\n        config.rcc.voltage_scale = VoltageScale::Scale1;\n    }\n    let p = embassy_stm32::init(config);\n    info!(\"Hello World!\");\n\n    // Generate random seed.\n    let mut rng = Rng::new(p.RNG, Irqs);\n    let mut seed = [0; 8];\n    rng.fill_bytes(&mut seed);\n    let seed = u64::from_le_bytes(seed);\n\n    let mac_addr = [0x00, 0x00, 0xDE, 0xAD, 0xBE, 0xEF];\n\n    static PACKETS: StaticCell<PacketQueue<4, 4>> = StaticCell::new();\n\n    let device = Ethernet::new(\n        PACKETS.init(PacketQueue::<4, 4>::new()),\n        p.ETH,\n        Irqs,\n        p.PA1,\n        p.PA7,\n        p.PC4,\n        p.PC5,\n        p.PG13,\n        p.PB13,\n        p.PG11,\n        mac_addr,\n        p.ETH_SMA,\n        p.PA2,\n        p.PC1,\n    );\n\n    let config = embassy_net::Config::dhcpv4(Default::default());\n    //let config = embassy_net::Config::ipv4_static(embassy_net::StaticConfigV4 {\n    //    address: Ipv4Cidr::new(Ipv4Address::new(10, 42, 0, 61), 24),\n    //    dns_servers: Vec::new(),\n    //    gateway: Some(Ipv4Address::new(10, 42, 0, 1)),\n    //});\n\n    // Init network stack\n    static RESOURCES: StaticCell<StackResources<3>> = StaticCell::new();\n    let (stack, runner) = embassy_net::new(device, config, RESOURCES.init(StackResources::new()), seed);\n\n    // Launch network task\n    spawner.spawn(unwrap!(net_task(runner)));\n\n    // Ensure DHCP configuration is up before trying connect\n    stack.wait_config_up().await;\n\n    info!(\"Network task initialized\");\n\n    let state: TcpClientState<1, 1024, 1024> = TcpClientState::new();\n    let client = TcpClient::new(stack, &state);\n\n    loop {\n        // You need to start a server on the host machine, for example: `nc -l 8000`\n        let addr = SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::new(10, 42, 0, 1), 8000));\n\n        info!(\"connecting...\");\n        let r = client.connect(addr).await;\n        if let Err(e) = r {\n            info!(\"connect error: {:?}\", e);\n            Timer::after_secs(1).await;\n            continue;\n        }\n        let mut connection = r.unwrap();\n        info!(\"connected!\");\n        loop {\n            let r = connection.write_all(b\"Hello\\n\").await;\n            if let Err(e) = r {\n                info!(\"write error: {:?}\", e);\n                break;\n            }\n            Timer::after_secs(1).await;\n        }\n    }\n}\n"
  },
  {
    "path": "examples/stm32h7/src/bin/eth_client_mii.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse core::net::{Ipv4Addr, SocketAddr, SocketAddrV4};\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_net::StackResources;\nuse embassy_net::tcp::client::{TcpClient, TcpClientState};\nuse embassy_stm32::eth::{Ethernet, GenericPhy, PacketQueue, Sma};\nuse embassy_stm32::peripherals::{ETH, ETH_SMA};\nuse embassy_stm32::rng::Rng;\nuse embassy_stm32::{Config, bind_interrupts, eth, peripherals, rng};\nuse embassy_time::Timer;\nuse embedded_io_async::Write;\nuse embedded_nal_async::TcpConnect;\nuse static_cell::StaticCell;\nuse {defmt_rtt as _, panic_probe as _};\n\nbind_interrupts!(struct Irqs {\n    ETH => eth::InterruptHandler;\n    RNG => rng::InterruptHandler<peripherals::RNG>;\n});\n\ntype Device = Ethernet<'static, ETH, GenericPhy<Sma<'static, ETH_SMA>>>;\n\n#[embassy_executor::task]\nasync fn net_task(mut runner: embassy_net::Runner<'static, Device>) -> ! {\n    runner.run().await\n}\n\n#[embassy_executor::main]\nasync fn main(spawner: Spawner) -> ! {\n    let mut config = Config::default();\n    {\n        use embassy_stm32::rcc::*;\n        config.rcc.hsi = Some(HSIPrescaler::DIV1);\n        config.rcc.csi = true;\n        config.rcc.hsi48 = Some(Default::default()); // needed for RNG\n        config.rcc.pll1 = Some(Pll {\n            source: PllSource::HSI,\n            prediv: PllPreDiv::DIV4,\n            mul: PllMul::MUL50,\n            fracn: None,\n            divp: Some(PllDiv::DIV2),\n            divq: None,\n            divr: None,\n        });\n        config.rcc.sys = Sysclk::PLL1_P; // 400 Mhz\n        config.rcc.ahb_pre = AHBPrescaler::DIV2; // 200 Mhz\n        config.rcc.apb1_pre = APBPrescaler::DIV2; // 100 Mhz\n        config.rcc.apb2_pre = APBPrescaler::DIV2; // 100 Mhz\n        config.rcc.apb3_pre = APBPrescaler::DIV2; // 100 Mhz\n        config.rcc.apb4_pre = APBPrescaler::DIV2; // 100 Mhz\n        config.rcc.voltage_scale = VoltageScale::Scale1;\n    }\n    let p = embassy_stm32::init(config);\n    info!(\"Hello World!\");\n\n    // Generate random seed.\n    let mut rng = Rng::new(p.RNG, Irqs);\n    let mut seed = [0; 8];\n    rng.fill_bytes(&mut seed);\n    let seed = u64::from_le_bytes(seed);\n\n    let mac_addr = [0x00, 0x00, 0xDE, 0xAD, 0xBE, 0xEF];\n\n    static PACKETS: StaticCell<PacketQueue<4, 4>> = StaticCell::new();\n\n    let device = Ethernet::new_mii(\n        PACKETS.init(PacketQueue::<4, 4>::new()),\n        p.ETH,\n        Irqs,\n        p.PA1,\n        p.PC3,\n        p.PA7,\n        p.PC4,\n        p.PC5,\n        p.PB0,\n        p.PB1,\n        p.PG13,\n        p.PG12,\n        p.PC2,\n        p.PE2,\n        p.PG11,\n        mac_addr,\n        p.ETH_SMA,\n        p.PA2,\n        p.PC1,\n    );\n    info!(\"Device created\");\n\n    let config = embassy_net::Config::dhcpv4(Default::default());\n    //let config = embassy_net::Config::ipv4_static(embassy_net::StaticConfigV4 {\n    //    address: Ipv4Cidr::new(Ipv4Address::new(10, 42, 0, 61), 24),\n    //    dns_servers: Vec::new(),\n    //    gateway: Some(Ipv4Address::new(10, 42, 0, 1)),\n    //});\n\n    // Init network stack\n    static RESOURCES: StaticCell<StackResources<3>> = StaticCell::new();\n    let (stack, runner) = embassy_net::new(device, config, RESOURCES.init(StackResources::new()), seed);\n\n    // Launch network task\n    spawner.spawn(unwrap!(net_task(runner)));\n\n    // Ensure DHCP configuration is up before trying connect\n    stack.wait_config_up().await;\n\n    info!(\"Network task initialized\");\n\n    let state: TcpClientState<1, 1024, 1024> = TcpClientState::new();\n    let client = TcpClient::new(stack, &state);\n\n    loop {\n        // You need to start a server on the host machine, for example: `nc -l 8000`\n        let addr = SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::new(192, 168, 100, 1), 8000));\n\n        info!(\"connecting...\");\n        let r = client.connect(addr).await;\n        if let Err(e) = r {\n            info!(\"connect error: {:?}\", e);\n            Timer::after_secs(1).await;\n            continue;\n        }\n        let mut connection = r.unwrap();\n        info!(\"connected!\");\n        loop {\n            let r = connection.write_all(b\"Hello\\n\").await;\n            if let Err(e) = r {\n                info!(\"write error: {:?}\", e);\n                break;\n            }\n            Timer::after_secs(1).await;\n        }\n    }\n}\n"
  },
  {
    "path": "examples/stm32h7/src/bin/flash.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse defmt::{info, unwrap};\nuse embassy_executor::Spawner;\nuse embassy_stm32::flash::Flash;\nuse embassy_time::Timer;\nuse {defmt_rtt as _, panic_probe as _};\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let p = embassy_stm32::init(Default::default());\n    info!(\"Hello Flash!\");\n\n    const ADDR: u32 = 0; // This is the offset into bank 2, the absolute address is 0x8_0000\n\n    // wait a bit before accessing the flash\n    Timer::after_millis(300).await;\n\n    let mut f = Flash::new_blocking(p.FLASH).into_blocking_regions().bank2_region;\n\n    info!(\"Reading...\");\n    let mut buf = [0u8; 32];\n    unwrap!(f.blocking_read(ADDR, &mut buf));\n    info!(\"Read: {=[u8]:x}\", buf);\n\n    info!(\"Erasing...\");\n    unwrap!(f.blocking_erase(ADDR, ADDR + 128 * 1024));\n\n    info!(\"Reading...\");\n    let mut buf = [0u8; 32];\n    unwrap!(f.blocking_read(ADDR, &mut buf));\n    info!(\"Read after erase: {=[u8]:x}\", buf);\n\n    info!(\"Writing...\");\n    unwrap!(f.blocking_write(\n        ADDR,\n        &[\n            1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29,\n            30, 31, 32\n        ]\n    ));\n\n    info!(\"Reading...\");\n    let mut buf = [0u8; 32];\n    unwrap!(f.blocking_read(ADDR, &mut buf));\n    info!(\"Read: {=[u8]:x}\", buf);\n    assert_eq!(\n        &buf[..],\n        &[\n            1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29,\n            30, 31, 32\n        ]\n    );\n}\n"
  },
  {
    "path": "examples/stm32h7/src/bin/flash_async.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse defmt::{info, unwrap};\nuse embassy_executor::Spawner;\nuse embassy_stm32::flash::{Flash, InterruptHandler};\nuse embassy_stm32::gpio::{AnyPin, Level, Output, Speed};\nuse embassy_stm32::{Peri, bind_interrupts};\nuse embassy_time::Timer;\nuse {defmt_rtt as _, panic_probe as _};\n\nbind_interrupts!(struct Irqs {\n    FLASH => InterruptHandler;\n});\n\n#[embassy_executor::main]\nasync fn main(spawner: Spawner) {\n    let p = embassy_stm32::init(Default::default());\n    info!(\"Hello Flash!\");\n\n    let mut f = Flash::new(p.FLASH, Irqs);\n\n    // Led should blink uninterrupted during ~2sec erase operation\n    spawner.spawn(blinky(p.PB14.into()).unwrap());\n\n    // Test on bank 2 in order not to stall CPU.\n    test_flash(&mut f, 1024 * 1024, 128 * 1024).await;\n}\n\n#[embassy_executor::task]\nasync fn blinky(p: Peri<'static, AnyPin>) {\n    let mut led = Output::new(p, Level::High, Speed::Low);\n\n    loop {\n        info!(\"high\");\n        led.set_high();\n        Timer::after_millis(300).await;\n\n        info!(\"low\");\n        led.set_low();\n        Timer::after_millis(300).await;\n    }\n}\n\nasync fn test_flash<'a>(f: &mut Flash<'a>, offset: u32, size: u32) {\n    info!(\"Testing offset: {=u32:#X}, size: {=u32:#X}\", offset, size);\n\n    info!(\"Reading...\");\n    let mut buf = [0u8; 32];\n    unwrap!(f.blocking_read(offset, &mut buf));\n    info!(\"Read: {=[u8]:x}\", buf);\n\n    info!(\"Erasing...\");\n    unwrap!(f.erase(offset, offset + size).await);\n\n    info!(\"Reading...\");\n    let mut buf = [0u8; 32];\n    unwrap!(f.blocking_read(offset, &mut buf));\n    info!(\"Read after erase: {=[u8]:x}\", buf);\n\n    info!(\"Writing...\");\n    unwrap!(\n        f.write(\n            offset,\n            &[\n                1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28,\n                29, 30, 31, 32\n            ]\n        )\n        .await\n    );\n\n    info!(\"Reading...\");\n    let mut buf = [0u8; 32];\n    unwrap!(f.blocking_read(offset, &mut buf));\n    info!(\"Read: {=[u8]:x}\", buf);\n    assert_eq!(\n        &buf[..],\n        &[\n            1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29,\n            30, 31, 32\n        ]\n    );\n}\n"
  },
  {
    "path": "examples/stm32h7/src/bin/fmc.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_stm32::Config;\nuse embassy_stm32::fmc::Fmc;\nuse embassy_time::{Delay, Timer};\nuse {defmt_rtt as _, panic_probe as _};\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let mut config = Config::default();\n    {\n        use embassy_stm32::rcc::*;\n        config.rcc.hsi = Some(HSIPrescaler::DIV1);\n        config.rcc.csi = true;\n        config.rcc.pll1 = Some(Pll {\n            source: PllSource::HSI,\n            prediv: PllPreDiv::DIV4,\n            mul: PllMul::MUL50,\n            fracn: None,\n            divp: Some(PllDiv::DIV2),\n            divq: Some(PllDiv::DIV8), // 100mhz\n            divr: None,\n        });\n        config.rcc.sys = Sysclk::PLL1_P; // 400 Mhz\n        config.rcc.ahb_pre = AHBPrescaler::DIV2; // 200 Mhz\n        config.rcc.apb1_pre = APBPrescaler::DIV2; // 100 Mhz\n        config.rcc.apb2_pre = APBPrescaler::DIV2; // 100 Mhz\n        config.rcc.apb3_pre = APBPrescaler::DIV2; // 100 Mhz\n        config.rcc.apb4_pre = APBPrescaler::DIV2; // 100 Mhz\n        config.rcc.voltage_scale = VoltageScale::Scale1;\n    }\n    let p = embassy_stm32::init(config);\n\n    info!(\"Hello World!\");\n\n    let mut core_peri = cortex_m::Peripherals::take().unwrap();\n\n    // taken from stm32h7xx-hal\n    core_peri.SCB.enable_icache();\n    // See Errata Sheet 2.2.1\n    // core_peri.SCB.enable_dcache(&mut core_peri.CPUID);\n    core_peri.DWT.enable_cycle_counter();\n    // ----------------------------------------------------------\n    // Configure MPU for external SDRAM\n    // MPU config for SDRAM write-through\n    let sdram_size = 32 * 1024 * 1024;\n\n    {\n        let mpu = core_peri.MPU;\n        let scb = &mut core_peri.SCB;\n        let size = sdram_size;\n        // Refer to ARM®v7-M Architecture Reference Manual ARM DDI 0403\n        // Version E.b Section B3.5\n        const MEMFAULTENA: u32 = 1 << 16;\n\n        unsafe {\n            /* Make sure outstanding transfers are done */\n            cortex_m::asm::dmb();\n\n            scb.shcsr.modify(|r| r & !MEMFAULTENA);\n\n            /* Disable the MPU and clear the control register*/\n            mpu.ctrl.write(0);\n        }\n\n        const REGION_NUMBER0: u32 = 0x00;\n        const REGION_BASE_ADDRESS: u32 = 0xD000_0000;\n\n        const REGION_FULL_ACCESS: u32 = 0x03;\n        const REGION_CACHEABLE: u32 = 0x01;\n        const REGION_WRITE_BACK: u32 = 0x01;\n        const REGION_ENABLE: u32 = 0x01;\n\n        crate::assert_eq!(size & (size - 1), 0, \"SDRAM memory region size must be a power of 2\");\n        crate::assert_eq!(size & 0x1F, 0, \"SDRAM memory region size must be 32 bytes or more\");\n        fn log2minus1(sz: u32) -> u32 {\n            for i in 5..=31 {\n                if sz == (1 << i) {\n                    return i - 1;\n                }\n            }\n            crate::panic!(\"Unknown SDRAM memory region size!\");\n        }\n\n        //info!(\"SDRAM Memory Size 0x{:x}\", log2minus1(size as u32));\n\n        // Configure region 0\n        //\n        // Cacheable, outer and inner write-back, no write allocate. So\n        // reads are cached, but writes always write all the way to SDRAM\n        unsafe {\n            mpu.rnr.write(REGION_NUMBER0);\n            mpu.rbar.write(REGION_BASE_ADDRESS);\n            mpu.rasr.write(\n                (REGION_FULL_ACCESS << 24)\n                    | (REGION_CACHEABLE << 17)\n                    | (REGION_WRITE_BACK << 16)\n                    | (log2minus1(size as u32) << 1)\n                    | REGION_ENABLE,\n            );\n        }\n\n        const MPU_ENABLE: u32 = 0x01;\n        const MPU_DEFAULT_MMAP_FOR_PRIVILEGED: u32 = 0x04;\n\n        // Enable\n        unsafe {\n            mpu.ctrl.modify(|r| r | MPU_DEFAULT_MMAP_FOR_PRIVILEGED | MPU_ENABLE);\n\n            scb.shcsr.modify(|r| r | MEMFAULTENA);\n\n            // Ensure MPU settings take effect\n            cortex_m::asm::dsb();\n            cortex_m::asm::isb();\n        }\n    }\n\n    let mut sdram = Fmc::sdram_a12bits_d32bits_4banks_bank2(\n        p.FMC,\n        // A0-A11\n        p.PF0,\n        p.PF1,\n        p.PF2,\n        p.PF3,\n        p.PF4,\n        p.PF5,\n        p.PF12,\n        p.PF13,\n        p.PF14,\n        p.PF15,\n        p.PG0,\n        p.PG1,\n        // BA0-BA1\n        p.PG4,\n        p.PG5,\n        // D0-D31\n        p.PD14,\n        p.PD15,\n        p.PD0,\n        p.PD1,\n        p.PE7,\n        p.PE8,\n        p.PE9,\n        p.PE10,\n        p.PE11,\n        p.PE12,\n        p.PE13,\n        p.PE14,\n        p.PE15,\n        p.PD8,\n        p.PD9,\n        p.PD10,\n        p.PH8,\n        p.PH9,\n        p.PH10,\n        p.PH11,\n        p.PH12,\n        p.PH13,\n        p.PH14,\n        p.PH15,\n        p.PI0,\n        p.PI1,\n        p.PI2,\n        p.PI3,\n        p.PI6,\n        p.PI7,\n        p.PI9,\n        p.PI10,\n        // NBL0 - NBL3\n        p.PE0,\n        p.PE1,\n        p.PI4,\n        p.PI5,\n        p.PH7,  // SDCKE1\n        p.PG8,  // SDCLK\n        p.PG15, // SDNCAS\n        p.PH6,  // SDNE1 (!CS)\n        p.PF11, // SDRAS\n        p.PC0,  // SDNWE, change to p.PH5 for EVAL boards\n        stm32_fmc::devices::is42s32800g_6::Is42s32800g {},\n    );\n\n    let mut delay = Delay;\n\n    let ram_slice = unsafe {\n        // Initialise controller and SDRAM\n        let ram_ptr: *mut u32 = sdram.init(&mut delay) as *mut _;\n\n        // Convert raw pointer to slice\n        core::slice::from_raw_parts_mut(ram_ptr, sdram_size / core::mem::size_of::<u32>())\n    };\n\n    // // ----------------------------------------------------------\n    // // Use memory in SDRAM\n    info!(\"RAM contents before writing: {:x}\", ram_slice[..10]);\n\n    ram_slice[0] = 1;\n    ram_slice[1] = 2;\n    ram_slice[2] = 3;\n    ram_slice[3] = 4;\n\n    info!(\"RAM contents after writing: {:x}\", ram_slice[..10]);\n\n    crate::assert_eq!(ram_slice[0], 1);\n    crate::assert_eq!(ram_slice[1], 2);\n    crate::assert_eq!(ram_slice[2], 3);\n    crate::assert_eq!(ram_slice[3], 4);\n\n    info!(\"Assertions succeeded.\");\n\n    loop {\n        Timer::after_millis(1000).await;\n    }\n}\n"
  },
  {
    "path": "examples/stm32h7/src/bin/i2c.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_stm32::i2c::{Error, I2c};\nuse embassy_stm32::{bind_interrupts, dma, i2c, peripherals};\nuse {defmt_rtt as _, panic_probe as _};\n\nconst ADDRESS: u8 = 0x5F;\nconst WHOAMI: u8 = 0x0F;\n\nbind_interrupts!(struct Irqs {\n    I2C2_EV => i2c::EventInterruptHandler<peripherals::I2C2>;\n    I2C2_ER => i2c::ErrorInterruptHandler<peripherals::I2C2>;\n    DMA1_STREAM4 => dma::InterruptHandler<peripherals::DMA1_CH4>;\n    DMA1_STREAM5 => dma::InterruptHandler<peripherals::DMA1_CH5>;\n});\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    info!(\"Hello world!\");\n    let p = embassy_stm32::init(Default::default());\n\n    let mut i2c = I2c::new(p.I2C2, p.PB10, p.PB11, p.DMA1_CH4, p.DMA1_CH5, Irqs, Default::default());\n\n    let mut data = [0u8; 1];\n\n    match i2c.blocking_write_read(ADDRESS, &[WHOAMI], &mut data) {\n        Ok(()) => info!(\"Whoami: {}\", data[0]),\n        Err(Error::Timeout) => error!(\"Operation timed out\"),\n        Err(e) => error!(\"I2c Error: {:?}\", e),\n    }\n}\n"
  },
  {
    "path": "examples/stm32h7/src/bin/i2c_shared.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse core::cell::RefCell;\n\nuse defmt::*;\nuse embassy_embedded_hal::shared_bus::blocking::i2c::I2cDevice;\nuse embassy_executor::Spawner;\nuse embassy_stm32::i2c::{self, I2c};\nuse embassy_stm32::mode::Async;\nuse embassy_stm32::{bind_interrupts, dma, peripherals};\nuse embassy_sync::blocking_mutex::NoopMutex;\nuse embassy_sync::blocking_mutex::raw::NoopRawMutex;\nuse embassy_time::{Duration, Timer};\nuse embedded_hal_1::i2c::I2c as _;\nuse static_cell::StaticCell;\nuse {defmt_rtt as _, panic_probe as _};\n\nconst TMP117_ADDR: u8 = 0x48;\nconst TMP117_TEMP_RESULT: u8 = 0x00;\n\nconst SHTC3_ADDR: u8 = 0x70;\nconst SHTC3_WAKEUP: [u8; 2] = [0x35, 0x17];\nconst SHTC3_MEASURE_RH_FIRST: [u8; 2] = [0x5c, 0x24];\nconst SHTC3_SLEEP: [u8; 2] = [0xb0, 0x98];\n\nstatic I2C_BUS: StaticCell<NoopMutex<RefCell<I2c<'static, Async, i2c::Master>>>> = StaticCell::new();\n\nbind_interrupts!(struct Irqs {\n    I2C1_EV => i2c::EventInterruptHandler<peripherals::I2C1>;\n    I2C1_ER => i2c::ErrorInterruptHandler<peripherals::I2C1>;\n    DMA1_STREAM4 => dma::InterruptHandler<peripherals::DMA1_CH4>;\n    DMA1_STREAM5 => dma::InterruptHandler<peripherals::DMA1_CH5>;\n});\n\n#[embassy_executor::task]\nasync fn temperature(mut i2c: I2cDevice<'static, NoopRawMutex, I2c<'static, Async, i2c::Master>>) {\n    let mut data = [0u8; 2];\n\n    loop {\n        match i2c.write_read(TMP117_ADDR, &[TMP117_TEMP_RESULT], &mut data) {\n            Ok(()) => {\n                let temp = f32::from(i16::from_be_bytes(data)) * 7.8125 / 1000.0;\n                info!(\"Temperature {}\", temp);\n            }\n            Err(_) => error!(\"I2C Error\"),\n        }\n\n        Timer::after(Duration::from_millis(1000)).await;\n    }\n}\n\n#[embassy_executor::task]\nasync fn humidity(mut i2c: I2cDevice<'static, NoopRawMutex, I2c<'static, Async, i2c::Master>>) {\n    let mut data = [0u8; 6];\n\n    loop {\n        // Wakeup\n        match i2c.write(SHTC3_ADDR, &SHTC3_WAKEUP) {\n            Ok(()) => Timer::after(Duration::from_millis(20)).await,\n            Err(_) => error!(\"I2C Error\"),\n        }\n\n        // Measurement\n        match i2c.write(SHTC3_ADDR, &SHTC3_MEASURE_RH_FIRST) {\n            Ok(()) => Timer::after(Duration::from_millis(5)).await,\n            Err(_) => error!(\"I2C Error\"),\n        }\n\n        // Result\n        match i2c.read(SHTC3_ADDR, &mut data) {\n            Ok(()) => Timer::after(Duration::from_millis(5)).await,\n            Err(_) => error!(\"I2C Error\"),\n        }\n\n        // Sleep\n        match i2c.write(SHTC3_ADDR, &SHTC3_SLEEP) {\n            Ok(()) => {\n                let (bytes, _) = data.split_at(core::mem::size_of::<i16>());\n                let rh = f32::from(u16::from_be_bytes(bytes.try_into().unwrap())) * 100.0 / 65536.0;\n                info!(\"Humidity: {}\", rh);\n            }\n            Err(_) => error!(\"I2C Error\"),\n        }\n\n        Timer::after(Duration::from_millis(1000)).await;\n    }\n}\n\n#[embassy_executor::main]\nasync fn main(spawner: Spawner) {\n    let p = embassy_stm32::init(Default::default());\n\n    let i2c = I2c::new(p.I2C1, p.PB8, p.PB9, p.DMA1_CH4, p.DMA1_CH5, Irqs, Default::default());\n    let i2c_bus = NoopMutex::new(RefCell::new(i2c));\n    let i2c_bus = I2C_BUS.init(i2c_bus);\n\n    // Device 1, using embedded-hal-async compatible driver for TMP117\n    let i2c_dev1 = I2cDevice::new(i2c_bus);\n    spawner.spawn(temperature(i2c_dev1).unwrap());\n\n    // Device 2, using embedded-hal-async compatible driver for SHTC3\n    let i2c_dev2 = I2cDevice::new(i2c_bus);\n    spawner.spawn(humidity(i2c_dev2).unwrap());\n}\n"
  },
  {
    "path": "examples/stm32h7/src/bin/low_level_timer_api.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_stm32::gpio::{AfType, Flex, OutputType, Speed};\nuse embassy_stm32::time::{Hertz, khz};\nuse embassy_stm32::timer::low_level::{OutputCompareMode, RoundTo, Timer as LLTimer};\nuse embassy_stm32::timer::{Ch1, Ch2, Ch3, Ch4, Channel, GeneralInstance32bit4Channel, TimerPin};\nuse embassy_stm32::{Config, Peri};\nuse embassy_time::Timer;\nuse {defmt_rtt as _, panic_probe as _};\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let mut config = Config::default();\n    {\n        use embassy_stm32::rcc::*;\n        config.rcc.hsi = Some(HSIPrescaler::DIV1);\n        config.rcc.csi = true;\n        config.rcc.pll1 = Some(Pll {\n            source: PllSource::HSI,\n            prediv: PllPreDiv::DIV4,\n            mul: PllMul::MUL50,\n            fracn: None,\n            divp: Some(PllDiv::DIV2),\n            divq: Some(PllDiv::DIV8), // 100mhz\n            divr: None,\n        });\n        config.rcc.sys = Sysclk::PLL1_P; // 400 Mhz\n        config.rcc.ahb_pre = AHBPrescaler::DIV2; // 200 Mhz\n        config.rcc.apb1_pre = APBPrescaler::DIV2; // 100 Mhz\n        config.rcc.apb2_pre = APBPrescaler::DIV2; // 100 Mhz\n        config.rcc.apb3_pre = APBPrescaler::DIV2; // 100 Mhz\n        config.rcc.apb4_pre = APBPrescaler::DIV2; // 100 Mhz\n        config.rcc.voltage_scale = VoltageScale::Scale1;\n    }\n    let p = embassy_stm32::init(config);\n\n    info!(\"Hello World!\");\n\n    let mut pwm = SimplePwm32::new(p.TIM5, p.PA0, p.PA1, p.PA2, p.PA3, khz(10));\n    let max = pwm.get_max_duty();\n    pwm.enable(Channel::Ch1);\n\n    info!(\"PWM initialized\");\n    info!(\"PWM max duty {}\", max);\n\n    loop {\n        pwm.set_duty(Channel::Ch1, 0);\n        Timer::after_millis(300).await;\n        pwm.set_duty(Channel::Ch1, max / 4);\n        Timer::after_millis(300).await;\n        pwm.set_duty(Channel::Ch1, max / 2);\n        Timer::after_millis(300).await;\n        pwm.set_duty(Channel::Ch1, max - 1);\n        Timer::after_millis(300).await;\n    }\n}\npub struct SimplePwm32<'d, T: GeneralInstance32bit4Channel> {\n    tim: LLTimer<'d, T>,\n    _ch1: Flex<'d>,\n    _ch2: Flex<'d>,\n    _ch3: Flex<'d>,\n    _ch4: Flex<'d>,\n}\n\nimpl<'d, T: GeneralInstance32bit4Channel> SimplePwm32<'d, T> {\n    pub fn new(\n        tim: Peri<'d, T>,\n        ch1: Peri<'d, impl TimerPin<T, Ch1>>,\n        ch2: Peri<'d, impl TimerPin<T, Ch2>>,\n        ch3: Peri<'d, impl TimerPin<T, Ch3>>,\n        ch4: Peri<'d, impl TimerPin<T, Ch4>>,\n        freq: Hertz,\n    ) -> Self {\n        let af1 = ch1.af_num();\n        let af2 = ch2.af_num();\n        let af3 = ch3.af_num();\n        let af4 = ch4.af_num();\n        let mut ch1 = Flex::new(ch1);\n        let mut ch2 = Flex::new(ch2);\n        let mut ch3 = Flex::new(ch3);\n        let mut ch4 = Flex::new(ch4);\n        ch1.set_as_af_unchecked(af1, AfType::output(OutputType::PushPull, Speed::VeryHigh));\n        ch2.set_as_af_unchecked(af2, AfType::output(OutputType::PushPull, Speed::VeryHigh));\n        ch3.set_as_af_unchecked(af3, AfType::output(OutputType::PushPull, Speed::VeryHigh));\n        ch4.set_as_af_unchecked(af4, AfType::output(OutputType::PushPull, Speed::VeryHigh));\n\n        let mut this = Self {\n            tim: LLTimer::new(tim),\n            _ch1: ch1,\n            _ch2: ch2,\n            _ch3: ch3,\n            _ch4: ch4,\n        };\n\n        this.set_frequency(freq);\n        this.tim.start();\n\n        let r = this.tim.regs_gp32();\n        r.ccmr_output(0)\n            .modify(|w| w.set_ocm(0, OutputCompareMode::PwmMode1.into()));\n        r.ccmr_output(0)\n            .modify(|w| w.set_ocm(1, OutputCompareMode::PwmMode1.into()));\n        r.ccmr_output(1)\n            .modify(|w| w.set_ocm(0, OutputCompareMode::PwmMode1.into()));\n        r.ccmr_output(1)\n            .modify(|w| w.set_ocm(1, OutputCompareMode::PwmMode1.into()));\n\n        this\n    }\n\n    pub fn enable(&mut self, channel: Channel) {\n        self.tim.regs_gp32().ccer().modify(|w| w.set_cce(channel.index(), true));\n    }\n\n    pub fn disable(&mut self, channel: Channel) {\n        self.tim\n            .regs_gp32()\n            .ccer()\n            .modify(|w| w.set_cce(channel.index(), false));\n    }\n\n    pub fn set_frequency(&mut self, freq: Hertz) {\n        self.tim.set_frequency(freq, RoundTo::Slower);\n    }\n\n    pub fn get_max_duty(&self) -> u32 {\n        self.tim.regs_gp32().arr().read()\n    }\n\n    pub fn set_duty(&mut self, channel: Channel, duty: u32) {\n        defmt::assert!(duty < self.get_max_duty());\n        self.tim.regs_gp32().ccr(channel.index()).write_value(duty)\n    }\n}\n"
  },
  {
    "path": "examples/stm32h7/src/bin/mco.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_stm32::gpio::{Level, Output, Speed};\nuse embassy_stm32::rcc::{Mco, Mco1Source, McoConfig, McoPrescaler};\nuse embassy_time::Timer;\nuse {defmt_rtt as _, panic_probe as _};\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let p = embassy_stm32::init(Default::default());\n    info!(\"Hello World!\");\n\n    let mut led = Output::new(p.PB14, Level::High, Speed::Low);\n\n    let config = {\n        let mut config = McoConfig::default();\n        config.prescaler = McoPrescaler::DIV8;\n        config\n    };\n\n    let _mco = Mco::new(p.MCO1, p.PA8, Mco1Source::HSI, config);\n\n    loop {\n        info!(\"high\");\n        led.set_high();\n        Timer::after_millis(500).await;\n\n        info!(\"low\");\n        led.set_low();\n        Timer::after_millis(500).await;\n    }\n}\n"
  },
  {
    "path": "examples/stm32h7/src/bin/multiprio.rs",
    "content": "//! This example showcases how to create multiple Executor instances to run tasks at\n//! different priority levels.\n//!\n//! Low priority executor runs in thread mode (not interrupt), and uses `sev` for signaling\n//! there's work in the queue, and `wfe` for waiting for work.\n//!\n//! Medium and high priority executors run in two interrupts with different priorities.\n//! Signaling work is done by pending the interrupt. No \"waiting\" needs to be done explicitly, since\n//! when there's work the interrupt will trigger and run the executor.\n//!\n//! Sample output below. Note that high priority ticks can interrupt everything else, and\n//! medium priority computations can interrupt low priority computations, making them to appear\n//! to take significantly longer time.\n//!\n//! ```not_rust\n//!     [med] Starting long computation\n//!     [med] done in 992 ms\n//!         [high] tick!\n//! [low] Starting long computation\n//!     [med] Starting long computation\n//!         [high] tick!\n//!         [high] tick!\n//!     [med] done in 993 ms\n//!     [med] Starting long computation\n//!         [high] tick!\n//!         [high] tick!\n//!     [med] done in 993 ms\n//! [low] done in 3972 ms\n//!     [med] Starting long computation\n//!         [high] tick!\n//!         [high] tick!\n//!     [med] done in 993 ms\n//! ```\n//!\n//! For comparison, try changing the code so all 3 tasks get spawned on the low priority executor.\n//! You will get an output like the following. Note that no computation is ever interrupted.\n//!\n//! ```not_rust\n//!         [high] tick!\n//!     [med] Starting long computation\n//!     [med] done in 496 ms\n//! [low] Starting long computation\n//! [low] done in 992 ms\n//!     [med] Starting long computation\n//!     [med] done in 496 ms\n//!         [high] tick!\n//! [low] Starting long computation\n//! [low] done in 992 ms\n//!         [high] tick!\n//!     [med] Starting long computation\n//!     [med] done in 496 ms\n//!         [high] tick!\n//! ```\n//!\n\n#![no_std]\n#![no_main]\n\nuse cortex_m_rt::entry;\nuse defmt::*;\nuse embassy_executor::{Executor, InterruptExecutor};\nuse embassy_stm32::interrupt;\nuse embassy_stm32::interrupt::{InterruptExt, Priority};\nuse embassy_time::{Instant, Timer};\nuse static_cell::StaticCell;\nuse {defmt_rtt as _, panic_probe as _};\n\n#[embassy_executor::task]\nasync fn run_high() {\n    loop {\n        info!(\"        [high] tick!\");\n        Timer::after_ticks(27374).await;\n    }\n}\n\n#[embassy_executor::task]\nasync fn run_med() {\n    loop {\n        let start = Instant::now();\n        info!(\"    [med] Starting long computation\");\n\n        // Spin-wait to simulate a long CPU computation\n        embassy_time::block_for(embassy_time::Duration::from_secs(1)); // ~1 second\n\n        let end = Instant::now();\n        let ms = end.duration_since(start).as_ticks() / 33;\n        info!(\"    [med] done in {} ms\", ms);\n\n        Timer::after_ticks(23421).await;\n    }\n}\n\n#[embassy_executor::task]\nasync fn run_low() {\n    loop {\n        let start = Instant::now();\n        info!(\"[low] Starting long computation\");\n\n        // Spin-wait to simulate a long CPU computation\n        embassy_time::block_for(embassy_time::Duration::from_secs(2)); // ~2 seconds\n\n        let end = Instant::now();\n        let ms = end.duration_since(start).as_ticks() / 33;\n        info!(\"[low] done in {} ms\", ms);\n\n        Timer::after_ticks(32983).await;\n    }\n}\n\nstatic EXECUTOR_HIGH: InterruptExecutor = InterruptExecutor::new();\nstatic EXECUTOR_MED: InterruptExecutor = InterruptExecutor::new();\nstatic EXECUTOR_LOW: StaticCell<Executor> = StaticCell::new();\n\n#[interrupt]\nunsafe fn UART4() {\n    unsafe { EXECUTOR_HIGH.on_interrupt() }\n}\n\n#[interrupt]\nunsafe fn UART5() {\n    unsafe { EXECUTOR_MED.on_interrupt() }\n}\n\n#[entry]\nfn main() -> ! {\n    info!(\"Hello World!\");\n\n    let _p = embassy_stm32::init(Default::default());\n\n    // STM32s don’t have any interrupts exclusively for software use, but they can all be triggered by software as well as\n    // by the peripheral, so we can just use any free interrupt vectors which aren’t used by the rest of your application.\n    // In this case we’re using UART4 and UART5, but there’s nothing special about them. Any otherwise unused interrupt\n    // vector would work exactly the same.\n\n    // High-priority executor: UART4, priority level 6\n    interrupt::UART4.set_priority(Priority::P6);\n    let spawner = EXECUTOR_HIGH.start(interrupt::UART4);\n    spawner.spawn(unwrap!(run_high()));\n\n    // Medium-priority executor: UART5, priority level 7\n    interrupt::UART5.set_priority(Priority::P7);\n    let spawner = EXECUTOR_MED.start(interrupt::UART5);\n    spawner.spawn(unwrap!(run_med()));\n\n    // Low priority executor: runs in thread mode, using WFE/SEV\n    let executor = EXECUTOR_LOW.init(Executor::new());\n    executor.run(|spawner| {\n        spawner.spawn(unwrap!(run_low()));\n    });\n}\n"
  },
  {
    "path": "examples/stm32h7/src/bin/pwm.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_stm32::Config;\nuse embassy_stm32::gpio::OutputType;\nuse embassy_stm32::time::khz;\nuse embassy_stm32::timer::simple_pwm::{PwmPin, SimplePwm};\nuse embassy_time::Timer;\nuse {defmt_rtt as _, panic_probe as _};\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let mut config = Config::default();\n    {\n        use embassy_stm32::rcc::*;\n        config.rcc.hsi = Some(HSIPrescaler::DIV1);\n        config.rcc.csi = true;\n        config.rcc.pll1 = Some(Pll {\n            source: PllSource::HSI,\n            prediv: PllPreDiv::DIV4,\n            mul: PllMul::MUL50,\n            fracn: None,\n            divp: Some(PllDiv::DIV2),\n            divq: None,\n            divr: None,\n        });\n        config.rcc.sys = Sysclk::PLL1_P; // 400 Mhz\n        config.rcc.ahb_pre = AHBPrescaler::DIV2; // 200 Mhz\n        config.rcc.apb1_pre = APBPrescaler::DIV2; // 100 Mhz\n        config.rcc.apb2_pre = APBPrescaler::DIV2; // 100 Mhz\n        config.rcc.apb3_pre = APBPrescaler::DIV2; // 100 Mhz\n        config.rcc.apb4_pre = APBPrescaler::DIV2; // 100 Mhz\n        config.rcc.voltage_scale = VoltageScale::Scale1;\n    }\n    let p = embassy_stm32::init(config);\n    info!(\"Hello World!\");\n\n    let ch1_pin = PwmPin::new(p.PA6, OutputType::PushPull);\n    let mut pwm = SimplePwm::new(p.TIM3, Some(ch1_pin), None, None, None, khz(10), Default::default());\n    let mut ch1 = pwm.ch1();\n    ch1.enable();\n\n    info!(\"PWM initialized\");\n    info!(\"PWM max duty {}\", ch1.max_duty_cycle());\n\n    loop {\n        ch1.set_duty_cycle_fully_off();\n        Timer::after_millis(300).await;\n        ch1.set_duty_cycle_fraction(1, 4);\n        Timer::after_millis(300).await;\n        ch1.set_duty_cycle_fraction(1, 2);\n        Timer::after_millis(300).await;\n        ch1.set_duty_cycle(ch1.max_duty_cycle() - 1);\n        Timer::after_millis(300).await;\n    }\n}\n"
  },
  {
    "path": "examples/stm32h7/src/bin/qspi_mdma.rs",
    "content": "//! Example driver for IS25LP064 Flash chip connected via QSPI. Tested on Daisy Seed.\n#![no_std]\n#![no_main]\n#![allow(unused)]\n\nuse defmt::{error, info};\nuse embassy_executor::Spawner;\nuse embassy_stm32::interrupt::typelevel::Binding;\nuse embassy_stm32::mode::{Async, Blocking, Mode};\nuse embassy_stm32::peripherals::*;\nuse embassy_stm32::qspi::enums::{\n    AddressSize, ChipSelectHighTime, DummyCycles, FIFOThresholdLevel, MemorySize, QspiWidth,\n};\nuse embassy_stm32::qspi::{self, Instance, InterruptHandler, MatchMode, Qspi, QuadDma, TransferConfig};\nuse embassy_stm32::{Peri, bind_interrupts, dma};\nuse embassy_time::{Duration, WithTimeout};\nuse {defmt_rtt as _, panic_probe as _};\n\n// Commands from IS25LP064 datasheet.\nconst WRITE_CMD: u8 = 0x32; // PPQ\nconst WRITE_ENABLE_CMD: u8 = 0x06; // WREN\nconst SECTOR_ERASE_CMD: u8 = 0xD7; // SER\nconst FAST_READ_QUAD_IO_CMD: u8 = 0xEB; // FRQIO\nconst RESET_ENABLE_CMD: u8 = 0x66;\nconst RESET_MEMORY_CMD: u8 = 0x99;\n\nconst WRITE_STATUS_REGISTER_CMD: u8 = 0x01; // WRSR\nconst READ_STATUS_REGISTER_CMD: u8 = 0x05; // RDSR\nconst STATUS_BIT_WIP: u8 = 1 << 0;\nconst STATUS_BIT_WEL: u8 = 1 << 1;\nconst STATUS_BIT_BP0: u8 = 1 << 2;\nconst STATUS_BIT_BP1: u8 = 1 << 3;\nconst STATUS_BIT_BP2: u8 = 1 << 4;\nconst STATUS_BIT_BP3: u8 = 1 << 5;\nconst STATUS_BIT_QE: u8 = 1 << 6;\nconst STATUS_BIT_SRWD: u8 = 1 << 7;\n\nconst SET_READ_PARAMETERS_CMD: u8 = 0xC0; // SRP\nconst READ_PARAMS_BIT_BL0: u8 = 1 << 0;\nconst READ_PARAMS_BIT_BL1: u8 = 1 << 1;\nconst READ_PARAMS_BIT_WE: u8 = 1 << 2;\nconst READ_PARAMS_BIT_DC0: u8 = 1 << 3;\nconst READ_PARAMS_BIT_DC1: u8 = 1 << 4;\nconst READ_PARAMS_BIT_ODS0: u8 = 1 << 5;\nconst READ_PARAMS_BIT_ODS1: u8 = 1 << 6;\nconst READ_PARAMS_BIT_ODS2: u8 = 1 << 7;\n\n// Memory array specifications as defined in the datasheet.\nconst SECTOR_SIZE: u32 = 4096;\nconst PAGE_SIZE: u32 = 256;\nconst MAX_ADDRESS: u32 = 0x7FFFFF;\n\n// Max Sector Erase time is 300ms\nconst SECTOR_ERASE_TIMEOUT: Duration = Duration::from_millis(600);\n\n// Max Page Write time is 0.8ms\nconst PAGE_WRITE_TIMEOUT: Duration = Duration::from_micros(1600);\n\n#[allow(non_snake_case)]\npub struct FlashPins<'a> {\n    pub IO0: Peri<'a, PF8>, // (SI)\n    pub IO1: Peri<'a, PF9>, // (SO)\n    pub IO2: Peri<'a, PF7>,\n    pub IO3: Peri<'a, PF6>,\n    pub SCK: Peri<'a, PF10>,\n    pub CS: Peri<'a, PG6>,\n}\n\npub struct Flash<'a, MODE: Mode> {\n    qspi: Qspi<'a, QUADSPI, MODE>,\n}\n\nimpl<MODE: Mode> Flash<'_, MODE> {\n    fn config() -> qspi::Config {\n        let mut config = qspi::Config::default();\n\n        config.memory_size = MemorySize::_8MiB;\n        config.address_size = AddressSize::_24bit;\n        config.prescaler = 1;\n        config.cs_high_time = ChipSelectHighTime::_2Cycle;\n        config.fifo_threshold = FIFOThresholdLevel::_1Bytes;\n        config\n    }\n\n    pub fn read(&mut self, address: u32, buffer: &mut [u8]) {\n        assert!(address + buffer.len() as u32 <= MAX_ADDRESS);\n\n        let transaction = TransferConfig {\n            iwidth: QspiWidth::SING,\n            awidth: QspiWidth::QUAD,\n            dwidth: QspiWidth::QUAD,\n            instruction: FAST_READ_QUAD_IO_CMD,\n            address: Some(address),\n            dummy: DummyCycles::_8,\n        };\n        self.qspi.blocking_read(buffer, transaction);\n    }\n\n    pub fn read_uuid(&mut self) -> [u8; 16] {\n        let mut buffer = [0; 16];\n        let transaction: TransferConfig = TransferConfig {\n            iwidth: QspiWidth::SING,\n            awidth: QspiWidth::SING,\n            dwidth: QspiWidth::SING,\n            instruction: 0x4B,\n            address: Some(0x00),\n            dummy: DummyCycles::_8,\n        };\n        self.qspi.blocking_read(&mut buffer, transaction);\n        buffer\n    }\n\n    pub fn write(&mut self, mut address: u32, data: &[u8]) {\n        assert!(address <= MAX_ADDRESS);\n        assert!(!data.is_empty());\n        self.erase(address, data.len() as u32);\n\n        let mut length = data.len() as u32;\n        let mut start_cursor = 0;\n\n        //WRITE_CMD(or PPQ) allows to write up to 256 bytes, which is as much as PAGE_SIZE.\n        //Let's divide the data into chunks of page size to write to flash\n        loop {\n            // Calculate number of bytes between address and end of the page.\n            let page_remainder = PAGE_SIZE - (address & (PAGE_SIZE - 1));\n            let size = page_remainder.min(length) as usize;\n            self.enable_write();\n            let transaction = TransferConfig {\n                iwidth: QspiWidth::SING,\n                awidth: QspiWidth::SING,\n                dwidth: QspiWidth::QUAD,\n                instruction: WRITE_CMD,\n                address: Some(address),\n                dummy: DummyCycles::_0,\n            };\n\n            self.qspi\n                .blocking_write(&data[start_cursor..start_cursor + size], transaction);\n            self.wait_for_write();\n            start_cursor += size;\n\n            // Stop if this was the last needed page.\n            if length <= page_remainder {\n                break;\n            }\n            length -= page_remainder;\n\n            // Jump to the next page.\n            address += page_remainder;\n            address %= MAX_ADDRESS;\n        }\n    }\n\n    pub fn erase(&mut self, mut address: u32, mut length: u32) {\n        assert!(address <= MAX_ADDRESS);\n        assert!(length > 0);\n\n        loop {\n            // Erase the sector.\n            self.enable_write();\n            let transaction = TransferConfig {\n                iwidth: QspiWidth::SING,\n                awidth: QspiWidth::SING,\n                dwidth: QspiWidth::NONE,\n                instruction: SECTOR_ERASE_CMD,\n                address: Some(address),\n                dummy: DummyCycles::_0,\n            };\n\n            self.qspi.blocking_command(transaction);\n            self.wait_for_write();\n\n            // Calculate number of bytes between address and end of the sector.\n            let sector_remainder = SECTOR_SIZE - (address & (SECTOR_SIZE - 1));\n\n            // Stop if this was the last affected sector.\n            if length <= sector_remainder {\n                break;\n            }\n            length -= sector_remainder;\n\n            // Jump to the next sector.\n            address += sector_remainder;\n            address %= MAX_ADDRESS;\n        }\n    }\n\n    fn enable_write(&mut self) {\n        let transaction = TransferConfig {\n            iwidth: QspiWidth::SING,\n            awidth: QspiWidth::NONE,\n            dwidth: QspiWidth::NONE,\n            instruction: WRITE_ENABLE_CMD,\n            address: None,\n            dummy: DummyCycles::_0,\n        };\n        self.qspi.blocking_command(transaction);\n    }\n\n    fn wait_for_write(&mut self) {\n        loop {\n            if self.read_status() & STATUS_BIT_WIP == 0 {\n                break;\n            }\n        }\n    }\n\n    fn read_status(&mut self) -> u8 {\n        let mut status: [u8; 1] = [0xFF; 1];\n        let transaction = TransferConfig {\n            iwidth: QspiWidth::SING,\n            awidth: QspiWidth::NONE,\n            dwidth: QspiWidth::SING,\n            instruction: READ_STATUS_REGISTER_CMD,\n            address: None,\n            dummy: DummyCycles::_0,\n        };\n        self.qspi.blocking_read(&mut status, transaction);\n        status[0]\n    }\n\n    fn reset_memory(&mut self) {\n        let transaction = TransferConfig {\n            iwidth: QspiWidth::SING,\n            awidth: QspiWidth::NONE,\n            dwidth: QspiWidth::NONE,\n            instruction: RESET_ENABLE_CMD,\n            address: None,\n            dummy: DummyCycles::_0,\n        };\n        self.qspi.blocking_command(transaction);\n\n        let transaction = TransferConfig {\n            iwidth: QspiWidth::SING,\n            awidth: QspiWidth::NONE,\n            dwidth: QspiWidth::NONE,\n            instruction: RESET_MEMORY_CMD,\n            address: None,\n            dummy: DummyCycles::_0,\n        };\n        self.qspi.blocking_command(transaction);\n    }\n\n    /// Reset status registers into driver's defaults. This makes sure that the\n    /// peripheral is configured as expected.\n    fn reset_status_register(&mut self) {\n        self.enable_write();\n        let value = STATUS_BIT_QE;\n        let transaction = TransferConfig {\n            iwidth: QspiWidth::SING,\n            awidth: QspiWidth::NONE,\n            dwidth: QspiWidth::SING,\n            instruction: WRITE_STATUS_REGISTER_CMD,\n            address: None,\n            dummy: DummyCycles::_0,\n        };\n        self.qspi.blocking_write(&[value], transaction);\n        self.wait_for_write();\n    }\n\n    /// Reset read registers into driver's defaults. This makes sure that the\n    /// peripheral is configured as expected.\n    fn reset_read_register(&mut self) {\n        let value = READ_PARAMS_BIT_ODS2 | READ_PARAMS_BIT_ODS1 | READ_PARAMS_BIT_ODS0 | READ_PARAMS_BIT_DC1;\n        let transaction = TransferConfig {\n            iwidth: QspiWidth::SING,\n            awidth: QspiWidth::NONE,\n            dwidth: QspiWidth::SING,\n            instruction: SET_READ_PARAMETERS_CMD,\n            address: None,\n            dummy: DummyCycles::_0,\n        };\n        self.qspi.blocking_write(&[value], transaction);\n        self.wait_for_write();\n    }\n\n    fn reset(&mut self) {\n        self.reset_memory();\n        self.reset_status_register();\n        self.reset_read_register();\n    }\n}\n\nimpl<'a> Flash<'a, Blocking> {\n    pub fn new_blocking(peri: Peri<'a, QUADSPI>, pins: FlashPins<'a>) -> Self {\n        let qspi = Qspi::new_blocking_bank1(\n            peri,\n            pins.IO0,\n            pins.IO1,\n            pins.IO2,\n            pins.IO3,\n            pins.SCK,\n            pins.CS,\n            Self::config(),\n        );\n        let mut flash = Flash { qspi };\n        flash.reset();\n        flash\n    }\n}\n\nimpl<'a> Flash<'a, Async> {\n    pub fn new_async<D, I>(peri: Peri<'a, QUADSPI>, pins: FlashPins<'a>, ch: Peri<'a, D>, irq: I) -> Flash<'a, Async>\n    where\n        D: QuadDma<QUADSPI>,\n        I: Binding<D::Interrupt, dma::InterruptHandler<D>>\n            + Binding<<QUADSPI as Instance>::Interrupt, InterruptHandler<QUADSPI>>\n            + 'a,\n    {\n        let qspi = Qspi::new_bank1(\n            peri,\n            pins.IO0,\n            pins.IO1,\n            pins.IO2,\n            pins.IO3,\n            pins.SCK,\n            pins.CS,\n            ch,\n            irq,\n            Self::config(),\n        );\n        let mut flash = Flash { qspi };\n        flash.reset();\n        flash\n    }\n\n    pub async fn read_async(&mut self, address: u32, buffer: &mut [u8]) {\n        assert!(address + buffer.len() as u32 <= MAX_ADDRESS);\n\n        let transaction = TransferConfig {\n            iwidth: QspiWidth::SING,\n            awidth: QspiWidth::QUAD,\n            dwidth: QspiWidth::QUAD,\n            instruction: FAST_READ_QUAD_IO_CMD,\n            address: Some(address),\n            dummy: DummyCycles::_8,\n        };\n        self.qspi.read_dma(buffer, transaction).await;\n    }\n\n    pub async fn write_async(&mut self, mut address: u32, data: &[u8]) {\n        assert!(address <= MAX_ADDRESS);\n        assert!(!data.is_empty());\n        self.erase_async(address, data.len() as u32).await;\n        let mut length = data.len() as u32;\n        let mut start_cursor = 0;\n\n        //WRITE_CMD(or PPQ) allows to write up to 256 bytes, which is as much as PAGE_SIZE.\n        //Let's divide the data into chunks of page size to write to flash\n        loop {\n            // Calculate number of bytes between address and end of the page.\n            let page_remainder = PAGE_SIZE - (address & (PAGE_SIZE - 1));\n            let size = page_remainder.min(length) as usize;\n            self.enable_write();\n            let transaction = TransferConfig {\n                iwidth: QspiWidth::SING,\n                awidth: QspiWidth::SING,\n                dwidth: QspiWidth::QUAD,\n                instruction: WRITE_CMD,\n                address: Some(address),\n                dummy: DummyCycles::_0,\n            };\n\n            self.qspi\n                .write_dma(&data[start_cursor..start_cursor + size], transaction)\n                .await;\n            self.wait_for_write_async(PAGE_WRITE_TIMEOUT).await;\n            start_cursor += size;\n\n            // Stop if this was the last needed page.\n            if length <= page_remainder {\n                break;\n            }\n            length -= page_remainder;\n\n            // Jump to the next page.\n            address += page_remainder;\n            address %= MAX_ADDRESS;\n        }\n    }\n\n    pub async fn erase_async(&mut self, mut address: u32, mut length: u32) {\n        assert!(address <= MAX_ADDRESS);\n        assert!(length > 0);\n\n        loop {\n            // Erase the sector.\n            self.enable_write();\n            let transaction = TransferConfig {\n                iwidth: QspiWidth::SING,\n                awidth: QspiWidth::SING,\n                dwidth: QspiWidth::NONE,\n                instruction: SECTOR_ERASE_CMD,\n                address: Some(address),\n                dummy: DummyCycles::_0,\n            };\n            self.qspi.blocking_command(transaction);\n\n            self.wait_for_write_async(SECTOR_ERASE_TIMEOUT).await;\n\n            // Calculate number of bytes between address and end of the sector.\n            let sector_remainder = SECTOR_SIZE - (address & (SECTOR_SIZE - 1));\n\n            // Stop if this was the last affected sector.\n            if length <= sector_remainder {\n                break;\n            }\n            length -= sector_remainder;\n\n            // Jump to the next sector.\n            address += sector_remainder;\n            address %= MAX_ADDRESS;\n        }\n    }\n\n    async fn wait_for_write_async(&mut self, timeout: Duration) {\n        let transaction = TransferConfig {\n            iwidth: QspiWidth::SING,\n            awidth: QspiWidth::NONE,\n            dwidth: QspiWidth::SING,\n            instruction: READ_STATUS_REGISTER_CMD,\n            address: None,\n            dummy: DummyCycles::_0,\n        };\n\n        self.qspi\n            .auto_poll(transaction, 0x10, STATUS_BIT_WIP as u32, 0, 1, MatchMode::AND)\n            .with_timeout(timeout)\n            .await\n            .expect(\"Flash Timed out waiting for status match\")\n    }\n}\n\nbind_interrupts!(pub struct Irqs {\n    QUADSPI => qspi::InterruptHandler<embassy_stm32::peripherals::QUADSPI>;\n    MDMA => dma::InterruptHandler<embassy_stm32::peripherals::MDMA_CH0>;\n});\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) -> ! {\n    let p = embassy_stm32::init(Default::default());\n\n    const ADDRESS: u32 = 0x00;\n    const SIZE: usize = 8000;\n\n    let pins = FlashPins {\n        IO0: p.PF8,\n        IO1: p.PF9,\n        IO2: p.PF7,\n        IO3: p.PF6,\n        SCK: p.PF10,\n        CS: p.PG6,\n    };\n\n    let mut flash = Flash::new_async(p.QUADSPI, pins, p.MDMA_CH0, Irqs);\n\n    info!(\"uuid: {}\", flash.read_uuid());\n    // Create an array of data to write.\n    let mut data: [u8; SIZE] = [0; SIZE];\n    for (i, x) in data.iter_mut().enumerate() {\n        *x = (i % 256) as u8;\n    }\n    info!(\"Write buffer: {:?}\", data[0..32]);\n\n    // Write it to the flash memory.\n    info!(\"Writting to flash\");\n    flash.write_async(ADDRESS, &data).await;\n\n    // Read it back.\n    info!(\"Reading from flash\");\n    let mut buffer: [u8; SIZE] = [0; SIZE];\n    flash.read_async(ADDRESS, &mut buffer).await;\n    info!(\"Read buffer: {:?}\", buffer[0..32]);\n\n    if data == buffer {\n        info!(\"Everything went as expected\");\n    } else {\n        error!(\"Read value does not match what was written\");\n    }\n\n    loop {}\n}\n"
  },
  {
    "path": "examples/stm32h7/src/bin/rng.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_stm32::rng::Rng;\nuse embassy_stm32::{Config, bind_interrupts, peripherals, rng};\nuse {defmt_rtt as _, panic_probe as _};\n\nbind_interrupts!(struct Irqs {\n    RNG => rng::InterruptHandler<peripherals::RNG>;\n});\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let mut config = Config::default();\n    config.rcc.hsi48 = Some(Default::default()); // needed for RNG\n    let p = embassy_stm32::init(config);\n    info!(\"Hello World!\");\n\n    let mut rng = Rng::new(p.RNG, Irqs);\n\n    let mut buf = [0u8; 16];\n    unwrap!(rng.async_fill_bytes(&mut buf).await);\n    info!(\"random bytes: {:02x}\", buf);\n}\n"
  },
  {
    "path": "examples/stm32h7/src/bin/rtc.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse chrono::{NaiveDate, NaiveDateTime};\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_stm32::Config;\nuse embassy_stm32::rcc::LsConfig;\nuse embassy_stm32::rtc::{Rtc, RtcConfig};\nuse embassy_time::Timer;\nuse {defmt_rtt as _, panic_probe as _};\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let mut config = Config::default();\n    config.rcc.ls = LsConfig::default_lse();\n\n    let p = embassy_stm32::init(config);\n    info!(\"Hello World!\");\n\n    let now = NaiveDate::from_ymd_opt(2020, 5, 15)\n        .unwrap()\n        .and_hms_opt(10, 30, 15)\n        .unwrap();\n\n    let (mut rtc, time_provider) = Rtc::new(p.RTC, RtcConfig::default());\n    info!(\"Got RTC! {:?}\", now.and_utc().timestamp());\n\n    rtc.set_datetime(now.into()).expect(\"datetime not set\");\n\n    // In reality the delay would be much longer\n    Timer::after_millis(20000).await;\n\n    let then: NaiveDateTime = time_provider.now().unwrap().into();\n    info!(\"Got RTC! {:?}\", then.and_utc().timestamp());\n}\n"
  },
  {
    "path": "examples/stm32h7/src/bin/sai.rs",
    "content": "//! Daisy Seed rev.7(with PCM3060 codec)\n//! https://electro-smith.com/products/daisy-seed\n#![no_std]\n#![no_main]\n\nuse embassy_executor::Spawner;\nuse grounded::uninit::GroundedArrayCell;\nuse hal::rcc::*;\nuse hal::sai::*;\nuse hal::time::Hertz;\nuse hal::{bind_interrupts, dma, peripherals};\nuse {defmt_rtt as _, embassy_stm32 as hal, panic_probe as _};\n\nconst BLOCK_LENGTH: usize = 32; // 32 samples\nconst HALF_DMA_BUFFER_LENGTH: usize = BLOCK_LENGTH * 2; //  2 channels\nconst DMA_BUFFER_LENGTH: usize = HALF_DMA_BUFFER_LENGTH * 2; //  2 half-blocks\nconst SAMPLE_RATE: u32 = 48000;\n\n//DMA buffer must be in special region. Refer https://embassy.dev/book/#_stm32_bdma_only_working_out_of_some_ram_regions\n#[unsafe(link_section = \".sram1_bss\")]\nstatic mut TX_BUFFER: GroundedArrayCell<u32, DMA_BUFFER_LENGTH> = GroundedArrayCell::uninit();\n#[unsafe(link_section = \".sram1_bss\")]\nstatic mut RX_BUFFER: GroundedArrayCell<u32, DMA_BUFFER_LENGTH> = GroundedArrayCell::uninit();\n\nbind_interrupts!(struct Irqs {\n    DMA1_STREAM0 => dma::InterruptHandler<peripherals::DMA1_CH0>;\n    DMA1_STREAM1 => dma::InterruptHandler<peripherals::DMA1_CH1>;\n});\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let mut config = hal::Config::default();\n    config.rcc.pll1 = Some(Pll {\n        source: PllSource::HSE,\n        prediv: PllPreDiv::DIV4,\n        mul: PllMul::MUL200,\n        fracn: None,\n        divp: Some(PllDiv::DIV2),\n        divq: Some(PllDiv::DIV5),\n        divr: Some(PllDiv::DIV2),\n    });\n    config.rcc.pll3 = Some(Pll {\n        source: PllSource::HSE,\n        prediv: PllPreDiv::DIV6,\n        mul: PllMul::MUL295,\n        fracn: None,\n        divp: Some(PllDiv::DIV16),\n        divq: Some(PllDiv::DIV4),\n        divr: Some(PllDiv::DIV32),\n    });\n    config.rcc.sys = Sysclk::PLL1_P;\n    config.rcc.mux.sai1sel = hal::pac::rcc::vals::Saisel::PLL3_P;\n    config.rcc.ahb_pre = AHBPrescaler::DIV2; // 200 Mhz\n    config.rcc.apb1_pre = APBPrescaler::DIV2; // 100 Mhz\n    config.rcc.apb2_pre = APBPrescaler::DIV2; // 100 Mhz\n    config.rcc.apb3_pre = APBPrescaler::DIV2; // 100 Mhz\n    config.rcc.apb4_pre = APBPrescaler::DIV2; // 100 Mhz\n    config.rcc.hse = Some(Hse {\n        freq: Hertz::mhz(16),\n        mode: HseMode::Oscillator,\n    });\n\n    let p = hal::init(config);\n\n    let (sub_block_tx, sub_block_rx) = hal::sai::split_subblocks(p.SAI1);\n    let kernel_clock = hal::rcc::frequency::<hal::peripherals::SAI1>().0;\n    let mclk_div = mclk_div_from_u8((kernel_clock / (SAMPLE_RATE * 256)) as u8);\n\n    let mut tx_config = hal::sai::Config::default();\n    tx_config.mode = Mode::Master;\n    tx_config.tx_rx = TxRx::Transmitter;\n    tx_config.sync_output = true;\n    tx_config.clock_strobe = ClockStrobe::Falling;\n    tx_config.master_clock_divider = mclk_div;\n    tx_config.stereo_mono = StereoMono::Stereo;\n    tx_config.data_size = DataSize::Data24;\n    tx_config.bit_order = BitOrder::MsbFirst;\n    tx_config.frame_sync_polarity = FrameSyncPolarity::ActiveHigh;\n    tx_config.frame_sync_offset = FrameSyncOffset::OnFirstBit;\n    tx_config.frame_length = 64;\n    tx_config.frame_sync_active_level_length = embassy_stm32::sai::word::U7(32);\n    tx_config.fifo_threshold = FifoThreshold::Quarter;\n\n    let mut rx_config = tx_config.clone();\n    rx_config.mode = Mode::Slave;\n    rx_config.tx_rx = TxRx::Receiver;\n    rx_config.sync_input = SyncInput::Internal;\n    rx_config.clock_strobe = ClockStrobe::Rising;\n    rx_config.sync_output = false;\n\n    let tx_buffer: &mut [u32] = unsafe {\n        let buf = &mut *core::ptr::addr_of_mut!(TX_BUFFER);\n        buf.initialize_all_copied(0);\n        let (ptr, len) = buf.get_ptr_len();\n        core::slice::from_raw_parts_mut(ptr, len)\n    };\n\n    let mut sai_transmitter = Sai::new_asynchronous_with_mclk(\n        sub_block_tx,\n        p.PE5,\n        p.PE6,\n        p.PE4,\n        p.PE2,\n        p.DMA1_CH0,\n        tx_buffer,\n        Irqs,\n        tx_config,\n    );\n\n    let rx_buffer: &mut [u32] = unsafe {\n        let buf = &mut *core::ptr::addr_of_mut!(RX_BUFFER);\n        buf.initialize_all_copied(0);\n        let (ptr, len) = buf.get_ptr_len();\n        core::slice::from_raw_parts_mut(ptr, len)\n    };\n\n    let mut sai_receiver = Sai::new_synchronous(sub_block_rx, p.PE3, p.DMA1_CH1, rx_buffer, Irqs, rx_config);\n\n    sai_receiver.start().unwrap();\n\n    let mut buf = [0u32; HALF_DMA_BUFFER_LENGTH];\n\n    loop {\n        // write() must be called before read() to start the master (transmitter)\n        // clock used by the receiver\n        sai_transmitter.write(&buf).await.unwrap();\n        sai_receiver.read(&mut buf).await.unwrap();\n    }\n}\n\nfn mclk_div_from_u8(v: u8) -> MasterClockDivider {\n    assert!((1..=63).contains(&v));\n    MasterClockDivider::from_bits(v)\n}\n"
  },
  {
    "path": "examples/stm32h7/src/bin/sdmmc.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_stm32::sdmmc::Sdmmc;\nuse embassy_stm32::sdmmc::sd::{CmdBlock, StorageDevice};\nuse embassy_stm32::time::mhz;\nuse embassy_stm32::{Config, bind_interrupts, peripherals, sdmmc};\nuse {defmt_rtt as _, panic_probe as _};\n\nbind_interrupts!(struct Irqs {\n    SDMMC1 => sdmmc::InterruptHandler<peripherals::SDMMC1>;\n});\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let mut config = Config::default();\n    {\n        use embassy_stm32::rcc::*;\n        config.rcc.hsi = Some(HSIPrescaler::DIV1);\n        config.rcc.csi = true;\n        config.rcc.pll1 = Some(Pll {\n            source: PllSource::HSI,\n            prediv: PllPreDiv::DIV4,\n            mul: PllMul::MUL50,\n            fracn: None,\n            divp: Some(PllDiv::DIV2),\n            divq: Some(PllDiv::DIV4), // default clock chosen by SDMMCSEL. 200 Mhz\n            divr: None,\n        });\n        config.rcc.sys = Sysclk::PLL1_P; // 400 Mhz\n        config.rcc.ahb_pre = AHBPrescaler::DIV2; // 200 Mhz\n        config.rcc.apb1_pre = APBPrescaler::DIV2; // 100 Mhz\n        config.rcc.apb2_pre = APBPrescaler::DIV2; // 100 Mhz\n        config.rcc.apb3_pre = APBPrescaler::DIV2; // 100 Mhz\n        config.rcc.apb4_pre = APBPrescaler::DIV2; // 100 Mhz\n        config.rcc.voltage_scale = VoltageScale::Scale1;\n    }\n    let p = embassy_stm32::init(config);\n    info!(\"Hello World!\");\n\n    let mut sdmmc = Sdmmc::new_4bit(\n        p.SDMMC1,\n        Irqs,\n        p.PC12,\n        p.PD2,\n        p.PC8,\n        p.PC9,\n        p.PC10,\n        p.PC11,\n        Default::default(),\n    );\n\n    let mut cmd_block = CmdBlock::new();\n\n    let storage = StorageDevice::new_sd_card(&mut sdmmc, &mut cmd_block, mhz(25))\n        .await\n        .unwrap();\n\n    let card = storage.card();\n\n    info!(\"Card: {:#?}\", Debug2Format(&card));\n}\n"
  },
  {
    "path": "examples/stm32h7/src/bin/signal.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse defmt::{info, unwrap};\nuse embassy_executor::Spawner;\nuse embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;\nuse embassy_sync::signal::Signal;\nuse embassy_time::Timer;\nuse {defmt_rtt as _, panic_probe as _};\n\nstatic SIGNAL: Signal<CriticalSectionRawMutex, u32> = Signal::new();\n\n#[embassy_executor::task]\nasync fn my_sending_task() {\n    let mut counter: u32 = 0;\n\n    loop {\n        Timer::after_secs(1).await;\n\n        SIGNAL.signal(counter);\n\n        counter = counter.wrapping_add(1);\n    }\n}\n\n#[embassy_executor::main]\nasync fn main(spawner: Spawner) {\n    let _p = embassy_stm32::init(Default::default());\n    spawner.spawn(unwrap!(my_sending_task()));\n\n    loop {\n        let received_counter = SIGNAL.wait().await;\n\n        info!(\"signalled, counter: {}\", received_counter);\n    }\n}\n"
  },
  {
    "path": "examples/stm32h7/src/bin/spi.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse core::fmt::Write;\nuse core::str::from_utf8;\n\nuse cortex_m_rt::entry;\nuse defmt::*;\nuse embassy_executor::Executor;\nuse embassy_stm32::mode::Blocking;\nuse embassy_stm32::time::mhz;\nuse embassy_stm32::{Config, spi};\nuse heapless::String;\nuse static_cell::StaticCell;\nuse {defmt_rtt as _, panic_probe as _};\n\n#[embassy_executor::task]\nasync fn main_task(mut spi: spi::Spi<'static, Blocking, spi::mode::Master>) {\n    for n in 0u32.. {\n        let mut write: String<128> = String::new();\n        core::write!(&mut write, \"Hello DMA World {}!\\r\\n\", n).unwrap();\n        unsafe {\n            let result = spi.blocking_transfer_in_place(write.as_bytes_mut());\n            if let Err(_) = result {\n                defmt::panic!(\"crap\");\n            }\n        }\n        info!(\"read via spi: {}\", from_utf8(write.as_bytes()).unwrap());\n    }\n}\n\nstatic EXECUTOR: StaticCell<Executor> = StaticCell::new();\n\n#[entry]\nfn main() -> ! {\n    info!(\"Hello World!\");\n\n    let mut config = Config::default();\n    {\n        use embassy_stm32::rcc::*;\n        config.rcc.hsi = Some(HSIPrescaler::DIV1);\n        config.rcc.csi = true;\n        config.rcc.pll1 = Some(Pll {\n            source: PllSource::HSI,\n            prediv: PllPreDiv::DIV4,\n            mul: PllMul::MUL50,\n            fracn: None,\n            divp: Some(PllDiv::DIV2),\n            divq: Some(PllDiv::DIV8), // used by SPI3. 100Mhz.\n            divr: None,\n        });\n        config.rcc.sys = Sysclk::PLL1_P; // 400 Mhz\n        config.rcc.ahb_pre = AHBPrescaler::DIV2; // 200 Mhz\n        config.rcc.apb1_pre = APBPrescaler::DIV2; // 100 Mhz\n        config.rcc.apb2_pre = APBPrescaler::DIV2; // 100 Mhz\n        config.rcc.apb3_pre = APBPrescaler::DIV2; // 100 Mhz\n        config.rcc.apb4_pre = APBPrescaler::DIV2; // 100 Mhz\n        config.rcc.voltage_scale = VoltageScale::Scale1;\n    }\n    let p = embassy_stm32::init(config);\n\n    let mut spi_config = spi::Config::default();\n    spi_config.frequency = mhz(1);\n\n    let spi = spi::Spi::new_blocking(p.SPI3, p.PB3, p.PB5, p.PB4, spi_config);\n\n    let executor = EXECUTOR.init(Executor::new());\n\n    executor.run(|spawner| {\n        spawner.spawn(unwrap!(main_task(spi)));\n    })\n}\n"
  },
  {
    "path": "examples/stm32h7/src/bin/spi_bdma.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse core::fmt::Write;\nuse core::str::from_utf8;\n\nuse cortex_m_rt::entry;\nuse defmt::*;\nuse embassy_executor::Executor;\nuse embassy_stm32::mode::Async;\nuse embassy_stm32::time::mhz;\nuse embassy_stm32::{Config, bind_interrupts, dma, peripherals, spi};\nuse grounded::uninit::GroundedArrayCell;\nuse heapless::String;\nuse static_cell::StaticCell;\nuse {defmt_rtt as _, panic_probe as _};\n\n// Defined in memory.x\n#[unsafe(link_section = \".ram_d3\")]\nstatic mut RAM_D3: GroundedArrayCell<u8, 256> = GroundedArrayCell::uninit();\n\nbind_interrupts!(struct Irqs {\n    BDMA_CHANNEL0 => dma::InterruptHandler<peripherals::BDMA_CH0>;\n    BDMA_CHANNEL1 => dma::InterruptHandler<peripherals::BDMA_CH1>;\n});\n\n#[embassy_executor::task]\nasync fn main_task(mut spi: spi::Spi<'static, Async, spi::mode::Master>) {\n    let (read_buffer, write_buffer) = unsafe {\n        let ram = &mut *core::ptr::addr_of_mut!(RAM_D3);\n        ram.initialize_all_copied(0);\n        (\n            ram.get_subslice_mut_unchecked(0, 128),\n            ram.get_subslice_mut_unchecked(128, 128),\n        )\n    };\n\n    for n in 0u32.. {\n        let mut write: String<128> = String::new();\n        core::write!(&mut write, \"Hello DMA World {}!\\r\\n\", n).unwrap();\n        let read_buffer = &mut read_buffer[..write.len()];\n        let write_buffer = &mut write_buffer[..write.len()];\n        // copy data to write_buffer which is located in D3 domain, accessable by BDMA\n        write_buffer.clone_from_slice(write.as_bytes());\n\n        spi.transfer(read_buffer, write_buffer).await.ok();\n        info!(\"read via spi+dma: {}\", from_utf8(read_buffer).unwrap());\n    }\n}\n\nstatic EXECUTOR: StaticCell<Executor> = StaticCell::new();\n\n#[entry]\nfn main() -> ! {\n    info!(\"Hello World!\");\n\n    let mut config = Config::default();\n    {\n        use embassy_stm32::rcc::*;\n        config.rcc.hsi = Some(HSIPrescaler::DIV1);\n        config.rcc.csi = true;\n        config.rcc.pll1 = Some(Pll {\n            source: PllSource::HSI,\n            prediv: PllPreDiv::DIV4,\n            mul: PllMul::MUL50,\n            fracn: None,\n            divp: Some(PllDiv::DIV2),\n            divq: Some(PllDiv::DIV8), // used by SPI3. 100Mhz.\n            divr: None,\n        });\n        config.rcc.sys = Sysclk::PLL1_P; // 400 Mhz\n        config.rcc.ahb_pre = AHBPrescaler::DIV2; // 200 Mhz\n        config.rcc.apb1_pre = APBPrescaler::DIV2; // 100 Mhz\n        config.rcc.apb2_pre = APBPrescaler::DIV2; // 100 Mhz\n        config.rcc.apb3_pre = APBPrescaler::DIV2; // 100 Mhz\n        config.rcc.apb4_pre = APBPrescaler::DIV2; // 100 Mhz\n        config.rcc.voltage_scale = VoltageScale::Scale1;\n    }\n    let p = embassy_stm32::init(config);\n\n    let mut spi_config = spi::Config::default();\n    spi_config.frequency = mhz(1);\n\n    let spi = spi::Spi::new(p.SPI6, p.PA5, p.PA7, p.PA6, p.BDMA_CH1, p.BDMA_CH0, Irqs, spi_config);\n\n    let executor = EXECUTOR.init(Executor::new());\n\n    executor.run(|spawner| {\n        spawner.spawn(unwrap!(main_task(spi)));\n    })\n}\n"
  },
  {
    "path": "examples/stm32h7/src/bin/spi_dma.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse core::fmt::Write;\nuse core::str::from_utf8;\n\nuse cortex_m_rt::entry;\nuse defmt::*;\nuse embassy_executor::Executor;\nuse embassy_stm32::mode::Async;\nuse embassy_stm32::time::mhz;\nuse embassy_stm32::{Config, bind_interrupts, dma, peripherals, spi};\nuse heapless::String;\nuse static_cell::StaticCell;\nuse {defmt_rtt as _, panic_probe as _};\n\nbind_interrupts!(struct Irqs {\n    DMA1_STREAM3 => dma::InterruptHandler<peripherals::DMA1_CH3>;\n    DMA1_STREAM4 => dma::InterruptHandler<peripherals::DMA1_CH4>;\n});\n\n#[embassy_executor::task]\nasync fn main_task(mut spi: spi::Spi<'static, Async, spi::mode::Master>) {\n    for n in 0u32.. {\n        let mut write: String<128> = String::new();\n        let mut read = [0; 128];\n        core::write!(&mut write, \"Hello DMA World {}!\\r\\n\", n).unwrap();\n        // transfer will slice the &mut read down to &write's actual length.\n        spi.transfer(&mut read, write.as_bytes()).await.ok();\n        info!(\"read via spi+dma: {}\", from_utf8(&read).unwrap());\n    }\n}\n\nstatic EXECUTOR: StaticCell<Executor> = StaticCell::new();\n\n#[entry]\nfn main() -> ! {\n    info!(\"Hello World!\");\n\n    let mut config = Config::default();\n    {\n        use embassy_stm32::rcc::*;\n        config.rcc.hsi = Some(HSIPrescaler::DIV1);\n        config.rcc.csi = true;\n        config.rcc.pll1 = Some(Pll {\n            source: PllSource::HSI,\n            prediv: PllPreDiv::DIV4,\n            mul: PllMul::MUL50,\n            fracn: None,\n            divp: Some(PllDiv::DIV2),\n            divq: Some(PllDiv::DIV8), // used by SPI3. 100Mhz.\n            divr: None,\n        });\n        config.rcc.sys = Sysclk::PLL1_P; // 400 Mhz\n        config.rcc.ahb_pre = AHBPrescaler::DIV2; // 200 Mhz\n        config.rcc.apb1_pre = APBPrescaler::DIV2; // 100 Mhz\n        config.rcc.apb2_pre = APBPrescaler::DIV2; // 100 Mhz\n        config.rcc.apb3_pre = APBPrescaler::DIV2; // 100 Mhz\n        config.rcc.apb4_pre = APBPrescaler::DIV2; // 100 Mhz\n        config.rcc.voltage_scale = VoltageScale::Scale1;\n    }\n    let p = embassy_stm32::init(config);\n\n    let mut spi_config = spi::Config::default();\n    spi_config.frequency = mhz(1);\n\n    let spi = spi::Spi::new(p.SPI3, p.PB3, p.PB5, p.PB4, p.DMA1_CH3, p.DMA1_CH4, Irqs, spi_config);\n\n    let executor = EXECUTOR.init(Executor::new());\n\n    executor.run(|spawner| {\n        spawner.spawn(unwrap!(main_task(spi)));\n    })\n}\n"
  },
  {
    "path": "examples/stm32h7/src/bin/usart.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse cortex_m_rt::entry;\nuse defmt::*;\nuse embassy_executor::Executor;\nuse embassy_stm32::usart::{Config, Uart};\nuse static_cell::StaticCell;\nuse {defmt_rtt as _, panic_probe as _};\n\n#[embassy_executor::task]\nasync fn main_task() {\n    let p = embassy_stm32::init(Default::default());\n\n    let config = Config::default();\n    let mut usart = Uart::new_blocking(p.UART7, p.PF6, p.PF7, config).unwrap();\n\n    unwrap!(usart.blocking_write(b\"Hello Embassy World!\\r\\n\"));\n    info!(\"wrote Hello, starting echo\");\n\n    let mut buf = [0u8; 1];\n    loop {\n        unwrap!(usart.blocking_read(&mut buf));\n        unwrap!(usart.blocking_write(&buf));\n    }\n}\n\nstatic EXECUTOR: StaticCell<Executor> = StaticCell::new();\n\n#[entry]\nfn main() -> ! {\n    info!(\"Hello World!\");\n\n    let executor = EXECUTOR.init(Executor::new());\n\n    executor.run(|spawner| {\n        spawner.spawn(unwrap!(main_task()));\n    })\n}\n"
  },
  {
    "path": "examples/stm32h7/src/bin/usart_dma.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse core::fmt::Write;\n\nuse cortex_m_rt::entry;\nuse defmt::*;\nuse embassy_executor::Executor;\nuse embassy_stm32::usart::{Config, Uart};\nuse embassy_stm32::{bind_interrupts, dma, peripherals, usart};\nuse heapless::String;\nuse static_cell::StaticCell;\nuse {defmt_rtt as _, panic_probe as _};\n\nbind_interrupts!(struct Irqs {\n    UART7 => usart::InterruptHandler<peripherals::UART7>;\n    DMA1_STREAM0 => dma::InterruptHandler<peripherals::DMA1_CH0>;\n    DMA1_STREAM1 => dma::InterruptHandler<peripherals::DMA1_CH1>;\n});\n\n#[embassy_executor::task]\nasync fn main_task() {\n    let p = embassy_stm32::init(Default::default());\n\n    let config = Config::default();\n    let mut usart = Uart::new(p.UART7, p.PF6, p.PF7, p.DMA1_CH0, p.DMA1_CH1, Irqs, config).unwrap();\n\n    for n in 0u32.. {\n        let mut s: String<128> = String::new();\n        core::write!(&mut s, \"Hello DMA World {}!\\r\\n\", n).unwrap();\n\n        usart.write(s.as_bytes()).await.ok();\n\n        info!(\"wrote DMA\");\n    }\n}\n\nstatic EXECUTOR: StaticCell<Executor> = StaticCell::new();\n\n#[entry]\nfn main() -> ! {\n    info!(\"Hello World!\");\n\n    let executor = EXECUTOR.init(Executor::new());\n\n    executor.run(|spawner| {\n        spawner.spawn(unwrap!(main_task()));\n    })\n}\n"
  },
  {
    "path": "examples/stm32h7/src/bin/usart_split.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_stm32::mode::Async;\nuse embassy_stm32::usart::{Config, Uart, UartRx};\nuse embassy_stm32::{bind_interrupts, dma, peripherals, usart};\nuse embassy_sync::blocking_mutex::raw::ThreadModeRawMutex;\nuse embassy_sync::channel::Channel;\nuse {defmt_rtt as _, panic_probe as _};\n\nbind_interrupts!(struct Irqs {\n    UART7 => usart::InterruptHandler<peripherals::UART7>;\n    DMA1_STREAM0 => dma::InterruptHandler<peripherals::DMA1_CH0>;\n    DMA1_STREAM1 => dma::InterruptHandler<peripherals::DMA1_CH1>;\n});\n\nstatic CHANNEL: Channel<ThreadModeRawMutex, [u8; 8], 1> = Channel::new();\n\n#[embassy_executor::main]\nasync fn main(spawner: Spawner) -> ! {\n    let p = embassy_stm32::init(Default::default());\n    info!(\"Hello World!\");\n\n    let config = Config::default();\n    let mut usart = Uart::new(p.UART7, p.PF6, p.PF7, p.DMA1_CH0, p.DMA1_CH1, Irqs, config).unwrap();\n    unwrap!(usart.blocking_write(b\"Type 8 chars to echo!\\r\\n\"));\n\n    let (mut tx, rx) = usart.split();\n\n    spawner.spawn(unwrap!(reader(rx)));\n\n    loop {\n        let buf = CHANNEL.receive().await;\n        info!(\"writing...\");\n        unwrap!(tx.write(&buf).await);\n    }\n}\n\n#[embassy_executor::task]\nasync fn reader(mut rx: UartRx<'static, Async>) {\n    let mut buf = [0; 8];\n    loop {\n        info!(\"reading...\");\n        unwrap!(rx.read(&mut buf).await);\n        CHANNEL.send(buf).await;\n    }\n}\n"
  },
  {
    "path": "examples/stm32h7/src/bin/usb_serial.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse defmt::{panic, *};\nuse embassy_executor::Spawner;\nuse embassy_futures::join::join;\nuse embassy_stm32::usb::{Driver, Instance};\nuse embassy_stm32::{Config, bind_interrupts, peripherals, usb};\nuse embassy_usb::Builder;\nuse embassy_usb::class::cdc_acm::{CdcAcmClass, State};\nuse embassy_usb::driver::EndpointError;\nuse {defmt_rtt as _, panic_probe as _};\n\nbind_interrupts!(struct Irqs {\n    OTG_FS => usb::InterruptHandler<peripherals::USB_OTG_FS>;\n});\n\n// If you are trying this and your USB device doesn't connect, the most\n// common issues are the RCC config and vbus_detection\n//\n// See https://embassy.dev/book/#_the_usb_examples_are_not_working_on_my_board_is_there_anything_else_i_need_to_configure\n// for more information.\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    info!(\"Hello World!\");\n\n    let mut config = Config::default();\n    {\n        use embassy_stm32::rcc::*;\n        config.rcc.hsi = Some(HSIPrescaler::DIV1);\n        config.rcc.csi = true;\n        config.rcc.hsi48 = Some(Hsi48Config { sync_from_usb: true }); // needed for USB\n        config.rcc.pll1 = Some(Pll {\n            source: PllSource::HSI,\n            prediv: PllPreDiv::DIV4,\n            mul: PllMul::MUL50,\n            fracn: None,\n            divp: Some(PllDiv::DIV2),\n            divq: None,\n            divr: None,\n        });\n        config.rcc.sys = Sysclk::PLL1_P; // 400 Mhz\n        config.rcc.ahb_pre = AHBPrescaler::DIV2; // 200 Mhz\n        config.rcc.apb1_pre = APBPrescaler::DIV2; // 100 Mhz\n        config.rcc.apb2_pre = APBPrescaler::DIV2; // 100 Mhz\n        config.rcc.apb3_pre = APBPrescaler::DIV2; // 100 Mhz\n        config.rcc.apb4_pre = APBPrescaler::DIV2; // 100 Mhz\n        config.rcc.voltage_scale = VoltageScale::Scale1;\n        config.rcc.mux.usbsel = mux::Usbsel::HSI48;\n    }\n    let p = embassy_stm32::init(config);\n\n    // Create the driver, from the HAL.\n    let mut ep_out_buffer = [0u8; 256];\n    let mut config = embassy_stm32::usb::Config::default();\n\n    // Do not enable vbus_detection. This is a safe default that works in all boards.\n    // However, if your USB device is self-powered (can stay powered on if USB is unplugged), you need\n    // to enable vbus_detection to comply with the USB spec. If you enable it, the board\n    // has to support it or USB won't work at all. See docs on `vbus_detection` for details.\n    config.vbus_detection = false;\n\n    let driver = Driver::new_fs(p.USB_OTG_FS, Irqs, p.PA12, p.PA11, &mut ep_out_buffer, config);\n\n    // Create embassy-usb Config\n    let mut config = embassy_usb::Config::new(0xc0de, 0xcafe);\n    config.manufacturer = Some(\"Embassy\");\n    config.product = Some(\"USB-serial example\");\n    config.serial_number = Some(\"12345678\");\n\n    // Create embassy-usb DeviceBuilder using the driver and config.\n    // It needs some buffers for building the descriptors.\n    let mut config_descriptor = [0; 256];\n    let mut bos_descriptor = [0; 256];\n    let mut control_buf = [0; 64];\n\n    let mut state = State::new();\n\n    let mut builder = Builder::new(\n        driver,\n        config,\n        &mut config_descriptor,\n        &mut bos_descriptor,\n        &mut [], // no msos descriptors\n        &mut control_buf,\n    );\n\n    // Create classes on the builder.\n    let mut class = CdcAcmClass::new(&mut builder, &mut state, 64);\n\n    // Build the builder.\n    let mut usb = builder.build();\n\n    // Run the USB device.\n    let usb_fut = usb.run();\n\n    // Do stuff with the class!\n    let echo_fut = async {\n        loop {\n            class.wait_connection().await;\n            info!(\"Connected\");\n            let _ = echo(&mut class).await;\n            info!(\"Disconnected\");\n        }\n    };\n\n    // Run everything concurrently.\n    // If we had made everything `'static` above instead, we could do this using separate tasks instead.\n    join(usb_fut, echo_fut).await;\n}\n\nstruct Disconnected {}\n\nimpl From<EndpointError> for Disconnected {\n    fn from(val: EndpointError) -> Self {\n        match val {\n            EndpointError::BufferOverflow => panic!(\"Buffer overflow\"),\n            EndpointError::Disabled => Disconnected {},\n        }\n    }\n}\n\nasync fn echo<'d, T: Instance + 'd>(class: &mut CdcAcmClass<'d, Driver<'d, T>>) -> Result<(), Disconnected> {\n    let mut buf = [0; 64];\n    loop {\n        let n = class.read_packet(&mut buf).await?;\n        let data = &buf[..n];\n        info!(\"data: {:x}\", data);\n        class.write_packet(data).await?;\n    }\n}\n"
  },
  {
    "path": "examples/stm32h7/src/bin/wdg.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_stm32::wdg::IndependentWatchdog;\nuse embassy_time::Timer;\nuse {defmt_rtt as _, panic_probe as _};\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let p = embassy_stm32::init(Default::default());\n    info!(\"Hello World!\");\n\n    let mut wdg = IndependentWatchdog::new(p.IWDG1, 20_000_000);\n\n    wdg.unleash();\n\n    loop {\n        Timer::after_secs(1).await;\n        wdg.pet();\n    }\n}\n"
  },
  {
    "path": "examples/stm32h723/.cargo/config.toml",
    "content": "[target.thumbv7em-none-eabihf]\nrunner = 'probe-rs run --chip STM32H723ZGTx'\n\n[build]\ntarget = \"thumbv7em-none-eabihf\" # Cortex-M4F and Cortex-M7F (with FPU)\n\n[env]\nDEFMT_LOG = \"trace\"\n"
  },
  {
    "path": "examples/stm32h723/Cargo.toml",
    "content": "[package]\nedition = \"2024\"\nname = \"embassy-stm32h723-examples\"\nversion = \"0.1.0\"\nlicense = \"MIT OR Apache-2.0\"\npublish = false\n\n[dependencies]\n# Change stm32h723zg to your chip name, if necessary.\nembassy-stm32 = { version = \"0.6.0\", path = \"../../embassy-stm32\", features = [\"defmt\", \"stm32h723zg\", \"time-driver-tim2\", \"exti\", \"memory-x\", \"unstable-pac\", \"chrono\"] }\nembassy-sync = { version = \"0.8.0\", path = \"../../embassy-sync\", features = [\"defmt\"] }\nembassy-executor = { version = \"0.10.0\", path = \"../../embassy-executor\", features = [\"platform-cortex-m\", \"executor-thread\", \"executor-interrupt\", \"defmt\"] }\nembassy-time = { version = \"0.5.1\", path = \"../../embassy-time\", features = [\"defmt\", \"defmt-timestamp-uptime\", \"tick-hz-32_768\"] }\nembassy-futures = { version = \"0.1.2\", path = \"../../embassy-futures\" }\n\ndefmt = \"1.0.1\"\ndefmt-rtt = \"1.0.0\"\n\ncortex-m = { version = \"0.7.6\", features = [\"inline-asm\", \"critical-section-single-core\"] }\ncortex-m-rt = \"0.7.0\"\nembedded-hal = \"0.2.6\"\nembedded-hal-1 = { package = \"embedded-hal\", version = \"1.0\" }\nembedded-hal-async = { version = \"1.0\" }\nembedded-nal-async = \"0.9.0\"\nembedded-io-async = { version = \"0.7.0\" }\npanic-probe = { version = \"1.0.0\", features = [\"print-defmt\"] }\nheapless = { version = \"0.9\", default-features = false }\ncritical-section = \"1.1\"\nstatic_cell = \"2\"\nchrono = { version = \"^0.4\", default-features = false }\ngrounded = \"0.2.0\"\n\n# cargo build/run\n[profile.dev]\ncodegen-units = 1\ndebug = 2\ndebug-assertions = true # <-\nincremental = false\nopt-level = 3 # <-\noverflow-checks = true # <-\n\n# cargo test\n[profile.test]\ncodegen-units = 1\ndebug = 2\ndebug-assertions = true # <-\nincremental = false\nopt-level = 3 # <-\noverflow-checks = true # <-\n\n# cargo build/run --release\n[profile.release]\ncodegen-units = 1\ndebug = 2\ndebug-assertions = false # <-\nincremental = false\nlto = 'fat'\nopt-level = 3 # <-\noverflow-checks = false # <-\n\n# cargo test --release\n[profile.bench]\ncodegen-units = 1\ndebug = 2\ndebug-assertions = false # <-\nincremental = false\nlto = 'fat'\nopt-level = 3 # <-\noverflow-checks = false # <-\n\n[package.metadata.embassy]\nbuild = [\n  { target = \"thumbv7em-none-eabi\", artifact-dir = \"out/examples/stm32h723\" }\n]\n"
  },
  {
    "path": "examples/stm32h723/build.rs",
    "content": "//! This build script copies the `memory.x` file from the crate root into\n//! a directory where the linker can always find it at build time.\n//! For many projects this is optional, as the linker always searches the\n//! project root directory -- wherever `Cargo.toml` is. However, if you\n//! are using a workspace or have a more complicated build setup, this\n//! build script becomes required. Additionally, by requesting that\n//! Cargo re-run the build script whenever `memory.x` is changed,\n//! updating `memory.x` ensures a rebuild of the application with the\n//! new memory settings.\n\nuse std::env;\nuse std::fs::File;\nuse std::io::Write;\nuse std::path::PathBuf;\n\nfn main() {\n    // Put `memory.x` in our output directory and ensure it's\n    // on the linker search path.\n    let out = &PathBuf::from(env::var_os(\"OUT_DIR\").unwrap());\n    File::create(out.join(\"memory.x\"))\n        .unwrap()\n        .write_all(include_bytes!(\"memory.x\"))\n        .unwrap();\n    println!(\"cargo:rustc-link-search={}\", out.display());\n\n    // By default, Cargo will re-run a build script whenever\n    // any file in the project changes. By specifying `memory.x`\n    // here, we ensure the build script is only re-run when\n    // `memory.x` is changed.\n    println!(\"cargo:rerun-if-changed=memory.x\");\n\n    println!(\"cargo:rustc-link-arg-bins=--nmagic\");\n    println!(\"cargo:rustc-link-arg-bins=-Tlink.x\");\n    println!(\"cargo:rustc-link-arg-bins=-Tdefmt.x\");\n}\n"
  },
  {
    "path": "examples/stm32h723/memory.x",
    "content": "MEMORY\n{\n  /* This file is intended for parts in the STM32H723 family. (RM0468)      */\n  /* - FLASH and RAM are mandatory memory sections.                         */\n  /* - The sum of all non-FLASH sections must add to 564k total device RAM. */\n  /* - The FLASH section size must match your device, see table below.      */\n\n  /* FLASH */\n  /* Select the appropriate FLASH size for your device. */\n  /* - STM32H730xB                                 128K */\n  /* - STM32H723xE/725xE                           512K */\n  /* - STM32H723xG/725xG/733xG/735xG                 1M */\n  FLASH1  : ORIGIN = 0x08000000, LENGTH = 1M\n\n  /* Data TCM  */\n  /* - Two contiguous 64KB RAMs.                                     */\n  /* - Used for interrupt handlers, stacks and general RAM.          */\n  /* - Zero wait-states.                                             */\n  /* - The DTCM is taken as the origin of the base ram. (See below.) */\n  /*   This is also where the interrupt table and such will live,    */\n  /*   which is required for deterministic performance.              */\n  DTCM    : ORIGIN = 0x20000000, LENGTH = 128K\n\n  /* Instruction TCM */\n  /* - More memory can be assigned to ITCM. See AXI SRAM notes, below. */\n  /* - Used for latency-critical interrupt handlers etc.               */\n  /* - Zero wait-states.                                               */\n  ITCM    : ORIGIN = 0x00000000, LENGTH = 64K + 0K\n\n  /* AXI SRAM */\n  /* - AXISRAM is in D1 and accessible by all system masters except BDMA.         */\n  /* - Suitable for application data not stored in DTCM.                          */\n  /* - Zero wait-states.                                                          */\n  /* - The 192k of extra shared RAM is fully allotted to the AXI SRAM by default. */\n  /*   As a result: 64k (64k + 0k) for ITCM and 320k (128k + 192k) for AXI SRAM.  */\n  /*   This can be re-configured via the TCM_AXI_SHARED[1,0] register when more   */\n  /*   ITCM is required.                                                          */\n  AXISRAM : ORIGIN = 0x24000000, LENGTH = 128K + 192K\n\n  /* AHB SRAM */\n  /* - SRAM1-2 are in D2 and accessible by all system masters except BDMA, LTDC */\n  /*   and SDMMC1. Suitable for use as DMA buffers.                             */\n  /* - SRAM4 is in D3 and additionally accessible by the BDMA. Used for BDMA    */\n  /*   buffers, for storing application data in lower-power modes.              */\n  /* - Zero wait-states.                                                        */\n  SRAM1   : ORIGIN = 0x30000000, LENGTH = 16K\n  SRAM2   : ORIGIN = 0x30040000, LENGTH = 16K\n  SRAM4   : ORIGIN = 0x38000000, LENGTH = 16K\n\n  /* Backup SRAM */\n  /* Used to store data during low-power sleeps. */\n  BSRAM   : ORIGIN = 0x38800000, LENGTH = 4K\n}\n\n/*\n/* Assign the memory regions defined above for use. */\n/*\n\n/* Provide the mandatory FLASH and RAM definitions for cortex-m-rt's linker script. */\nREGION_ALIAS(FLASH, FLASH1);\nREGION_ALIAS(RAM,   DTCM);\n\n/* The location of the stack can be overridden using the `_stack_start` symbol. */\n/* - Set the stack location at the end of RAM, using all remaining space.       */\n_stack_start = ORIGIN(RAM) + LENGTH(RAM);\n\n/* The location of the .text section can be overridden using the  */\n/* `_stext` symbol. By default it will place after .vector_table. */\n/* _stext = ORIGIN(FLASH) + 0x40c; */\n\n/* Define sections for placing symbols into the extra memory regions above.   */\n/* This makes them accessible from code.                                      */\n/* - ITCM, DTCM and AXISRAM connect to a 64-bit wide bus -> align to 8 bytes. */\n/* - All other memories     connect to a 32-bit wide bus -> align to 4 bytes. */\nSECTIONS {\n  .itcm (NOLOAD) : ALIGN(8) {\n    *(.itcm .itcm.*);\n    . = ALIGN(8);\n    } > ITCM\n\n  .axisram (NOLOAD) : ALIGN(8) {\n    *(.axisram .axisram.*);\n    . = ALIGN(8);\n    } > AXISRAM\n\n  .sram1 (NOLOAD) : ALIGN(4) {\n    *(.sram1 .sram1.*);\n    . = ALIGN(4);\n    } > SRAM1\n\n  .sram2 (NOLOAD) : ALIGN(4) {\n    *(.sram2 .sram2.*);\n    . = ALIGN(4);\n    } > SRAM2\n\n  .sram4 (NOLOAD) : ALIGN(4) {\n    *(.sram4 .sram4.*);\n    . = ALIGN(4);\n    } > SRAM4\n\n  .bsram (NOLOAD) : ALIGN(4) {\n    *(.bsram .bsram.*);\n    . = ALIGN(4);\n    } > BSRAM\n\n};\n"
  },
  {
    "path": "examples/stm32h723/src/bin/spdifrx.rs",
    "content": "//! This example receives inputs on SPDIFRX and outputs on SAI4.\n//!\n//! Only very few controllers connect the SPDIFRX symbol clock to a SAI peripheral's clock input.\n//! However, this is necessary for synchronizing the symbol rates and avoiding glitches.\n#![no_std]\n#![no_main]\n\nuse defmt::{info, trace};\nuse embassy_executor::Spawner;\nuse embassy_futures::select::{Either, select};\nuse embassy_stm32::spdifrx::{self, Spdifrx};\nuse embassy_stm32::{Peri, bind_interrupts, dma, peripherals, sai};\nuse grounded::uninit::GroundedArrayCell;\nuse hal::sai::*;\nuse {defmt_rtt as _, embassy_stm32 as hal, panic_probe as _};\n\nbind_interrupts!(struct Irqs {\n    SPDIF_RX => spdifrx::GlobalInterruptHandler<peripherals::SPDIFRX1>;\n    DMA2_STREAM7 => dma::InterruptHandler<peripherals::DMA2_CH7>;\n    BDMA_CHANNEL0 => dma::InterruptHandler<peripherals::BDMA_CH0>;\n});\n\nconst CHANNEL_COUNT: usize = 2;\nconst BLOCK_LENGTH: usize = 64;\nconst HALF_DMA_BUFFER_LENGTH: usize = BLOCK_LENGTH * CHANNEL_COUNT;\nconst DMA_BUFFER_LENGTH: usize = HALF_DMA_BUFFER_LENGTH * 2; //  2 half-blocks\n\n// DMA buffers must be in special regions. Refer https://embassy.dev/book/#_stm32_bdma_only_working_out_of_some_ram_regions\n#[unsafe(link_section = \".sram1\")]\nstatic SPDIFRX_BUFFER: GroundedArrayCell<u32, DMA_BUFFER_LENGTH> = GroundedArrayCell::uninit();\n\n#[unsafe(link_section = \".sram4\")]\nstatic SAI_BUFFER: GroundedArrayCell<u32, DMA_BUFFER_LENGTH> = GroundedArrayCell::uninit();\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let mut peripheral_config = embassy_stm32::Config::default();\n    {\n        use embassy_stm32::rcc::*;\n        peripheral_config.rcc.hsi = Some(HSIPrescaler::DIV1);\n        peripheral_config.rcc.pll1 = Some(Pll {\n            source: PllSource::HSI,\n            prediv: PllPreDiv::DIV16,\n            mul: PllMul::MUL200,\n            divp: Some(PllDiv::DIV2), // 400 MHz\n            divq: Some(PllDiv::DIV2),\n            divr: Some(PllDiv::DIV2),\n        });\n        peripheral_config.rcc.sys = Sysclk::PLL1_P;\n        peripheral_config.rcc.ahb_pre = AHBPrescaler::DIV2;\n        peripheral_config.rcc.apb1_pre = APBPrescaler::DIV2;\n        peripheral_config.rcc.apb2_pre = APBPrescaler::DIV2;\n        peripheral_config.rcc.apb3_pre = APBPrescaler::DIV2;\n        peripheral_config.rcc.apb4_pre = APBPrescaler::DIV2;\n\n        peripheral_config.rcc.mux.spdifrxsel = mux::Spdifrxsel::PLL1_Q;\n    }\n    let mut p = embassy_stm32::init(peripheral_config);\n\n    info!(\"SPDIFRX to SAI4 bridge\");\n\n    // Use SPDIFRX clock for SAI.\n    // This ensures equal rates of sample production and consumption.\n    let clk_source = embassy_stm32::pac::rcc::vals::Saiasel::_RESERVED_5;\n    embassy_stm32::pac::RCC.d3ccipr().modify(|w| {\n        w.set_sai4asel(clk_source);\n    });\n\n    let sai_buffer: &mut [u32] = unsafe {\n        SAI_BUFFER.initialize_all_copied(0);\n        let (ptr, len) = SAI_BUFFER.get_ptr_len();\n        core::slice::from_raw_parts_mut(ptr, len)\n    };\n\n    let spdifrx_buffer: &mut [u32] = unsafe {\n        SPDIFRX_BUFFER.initialize_all_copied(0);\n        let (ptr, len) = SPDIFRX_BUFFER.get_ptr_len();\n        core::slice::from_raw_parts_mut(ptr, len)\n    };\n\n    let mut sai_transmitter = new_sai_transmitter(\n        p.SAI4.reborrow(),\n        p.PD13.reborrow(),\n        p.PC1.reborrow(),\n        p.PD12.reborrow(),\n        p.BDMA_CH0.reborrow(),\n        sai_buffer,\n    );\n    let mut spdif_receiver = new_spdif_receiver(\n        p.SPDIFRX1.reborrow(),\n        p.PD7.reborrow(),\n        p.DMA2_CH7.reborrow(),\n        spdifrx_buffer,\n    );\n    spdif_receiver.start();\n\n    let mut renew_sai = false;\n    loop {\n        let mut buf = [0u32; HALF_DMA_BUFFER_LENGTH];\n\n        if renew_sai {\n            renew_sai = false;\n            trace!(\"Renew SAI.\");\n            drop(sai_transmitter);\n            sai_transmitter = new_sai_transmitter(\n                p.SAI4.reborrow(),\n                p.PD13.reborrow(),\n                p.PC1.reborrow(),\n                p.PD12.reborrow(),\n                p.BDMA_CH0.reborrow(),\n                sai_buffer,\n            );\n        }\n\n        match select(spdif_receiver.read(&mut buf), sai_transmitter.wait_write_error()).await {\n            Either::First(spdif_read_result) => match spdif_read_result {\n                Ok(_) => (),\n                Err(spdifrx::Error::RingbufferError(_)) => {\n                    trace!(\"SPDIFRX ringbuffer error. Renew.\");\n                    drop(spdif_receiver);\n                    spdif_receiver = new_spdif_receiver(\n                        p.SPDIFRX1.reborrow(),\n                        p.PD7.reborrow(),\n                        p.DMA2_CH7.reborrow(),\n                        spdifrx_buffer,\n                    );\n                    spdif_receiver.start();\n                    continue;\n                }\n                Err(spdifrx::Error::ChannelSyncError) => {\n                    trace!(\"SPDIFRX channel sync (left/right assignment) error.\");\n                    continue;\n                }\n            },\n            Either::Second(_) => {\n                renew_sai = true;\n                continue;\n            }\n        };\n\n        renew_sai = sai_transmitter.write(&buf).await.is_err();\n    }\n}\n\n/// Creates a new SPDIFRX instance for receiving sample data.\n///\n/// Used (again) after dropping the SPDIFRX instance, in case of errors (e.g. source disconnect).\nfn new_spdif_receiver<'d>(\n    spdifrx: Peri<'d, peripherals::SPDIFRX1>,\n    input_pin: Peri<'d, peripherals::PD7>,\n    dma: Peri<'d, peripherals::DMA2_CH7>,\n    buf: &'d mut [u32],\n) -> Spdifrx<'d, peripherals::SPDIFRX1> {\n    Spdifrx::new(spdifrx, Irqs, spdifrx::Config::default(), input_pin, dma, buf)\n}\n\n/// Creates a new SAI4 instance for transmitting sample data.\n///\n/// Used (again) after dropping the SAI4 instance, in case of errors (e.g. buffer overrun).\nfn new_sai_transmitter<'d>(\n    sai: Peri<'d, peripherals::SAI4>,\n    sck: Peri<'d, peripherals::PD13>,\n    sd: Peri<'d, peripherals::PC1>,\n    fs: Peri<'d, peripherals::PD12>,\n    dma: Peri<'d, peripherals::BDMA_CH0>,\n    buf: &'d mut [u32],\n) -> Sai<'d, peripherals::SAI4, u32> {\n    let mut sai_config = hal::sai::Config::default();\n    sai_config.slot_count = hal::sai::word::U4(CHANNEL_COUNT as u8);\n    sai_config.slot_enable = 0xFFFF; // All slots\n    sai_config.data_size = sai::DataSize::Data32;\n    sai_config.frame_length = (CHANNEL_COUNT * 32) as u16;\n\n    let (sub_block_tx, _) = hal::sai::split_subblocks(sai);\n    Sai::new_asynchronous(sub_block_tx, sck, sd, fs, dma, buf, Irqs, sai_config)\n}\n"
  },
  {
    "path": "examples/stm32h735/.cargo/config.toml",
    "content": "[target.thumbv7em-none-eabihf]\nrunner = 'probe-rs run --chip STM32H735IGKx'\n\n[build]\ntarget = \"thumbv7em-none-eabihf\" # Cortex-M4F and Cortex-M7F (with FPU)\n\n[env]\nDEFMT_LOG = \"trace\"\n"
  },
  {
    "path": "examples/stm32h735/Cargo.toml",
    "content": "[package]\nedition = \"2024\"\nname = \"embassy-stm32h735-examples\"\nversion = \"0.1.0\"\nlicense = \"MIT OR Apache-2.0\"\npublish = false\n\n[dependencies]\nembassy-stm32 = { version = \"0.6.0\", path = \"../../embassy-stm32\", features = [\"defmt\", \"stm32h735ig\", \"time-driver-tim2\", \"exti\", \"memory-x\", \"unstable-pac\", \"chrono\"] }\nembassy-sync = { version = \"0.8.0\", path = \"../../embassy-sync\", features = [\"defmt\"] }\nembassy-embedded-hal = { version = \"0.6.0\", path = \"../../embassy-embedded-hal\" }\nembassy-executor = { version = \"0.10.0\", path = \"../../embassy-executor\", features = [\"platform-cortex-m\", \"executor-thread\", \"executor-interrupt\", \"defmt\"] }\nembassy-time = { version = \"0.5.1\", path = \"../../embassy-time\", features = [\"defmt\", \"defmt-timestamp-uptime\", \"tick-hz-32_768\"] }\nembassy-futures = { version = \"0.1.2\", path = \"../../embassy-futures\" }\n\ndefmt = \"1.0.1\"\ndefmt-rtt = \"1.0.0\"\n\ncortex-m = { version = \"0.7.6\", features = [\"inline-asm\", \"critical-section-single-core\"] }\ncortex-m-rt = \"0.7.0\"\npanic-probe = { version = \"1.0.0\", features = [\"print-defmt\"] }\nheapless = { version = \"0.9\", default-features = false }\nembedded-graphics = { version = \"0.8.1\" }\ntinybmp = { version = \"0.5\" }\n\n# cargo build/run\n[profile.dev]\ncodegen-units = 1\ndebug = 2\ndebug-assertions = true # <-\nincremental = false\nopt-level = 3 # <-\noverflow-checks = true # <-\n\n# cargo test\n[profile.test]\ncodegen-units = 1\ndebug = 2\ndebug-assertions = true # <-\nincremental = false\nopt-level = 3 # <-\noverflow-checks = true # <-\n\n# cargo build/run --release\n[profile.release]\ncodegen-units = 1\ndebug = 2\ndebug-assertions = false # <-\nincremental = false\nlto = 'fat'\nopt-level = 3 # <-\noverflow-checks = false # <-\n\n# cargo test --release\n[profile.bench]\ncodegen-units = 1\ndebug = 2\ndebug-assertions = false # <-\nincremental = false\nlto = 'fat'\nopt-level = 3 # <-\noverflow-checks = false # <-\n\n[package.metadata.embassy]\nbuild = [\n  { target = \"thumbv7em-none-eabi\", artifact-dir = \"out/examples/stm32h735\" }\n]\n"
  },
  {
    "path": "examples/stm32h735/build.rs",
    "content": "//! This build script copies the `memory.x` file from the crate root into\n//! a directory where the linker can always find it at build time.\n//! For many projects this is optional, as the linker always searches the\n//! project root directory -- wherever `Cargo.toml` is. However, if you\n//! are using a workspace or have a more complicated build setup, this\n//! build script becomes required. Additionally, by requesting that\n//! Cargo re-run the build script whenever `memory.x` is changed,\n//! updating `memory.x` ensures a rebuild of the application with the\n//! new memory settings.\n\nuse std::env;\nuse std::fs::File;\nuse std::io::Write;\nuse std::path::PathBuf;\n\nfn main() {\n    // Put `memory.x` in our output directory and ensure it's\n    // on the linker search path.\n    let out = &PathBuf::from(env::var_os(\"OUT_DIR\").unwrap());\n    File::create(out.join(\"memory.x\"))\n        .unwrap()\n        .write_all(include_bytes!(\"memory.x\"))\n        .unwrap();\n    println!(\"cargo:rustc-link-search={}\", out.display());\n\n    // By default, Cargo will re-run a build script whenever\n    // any file in the project changes. By specifying `memory.x`\n    // here, we ensure the build script is only re-run when\n    // `memory.x` is changed.\n    println!(\"cargo:rerun-if-changed=memory.x\");\n\n    println!(\"cargo:rustc-link-arg-bins=--nmagic\");\n    println!(\"cargo:rustc-link-arg-bins=-Tlink.x\");\n    println!(\"cargo:rustc-link-arg-bins=-Tdefmt.x\");\n}\n"
  },
  {
    "path": "examples/stm32h735/memory.x",
    "content": "MEMORY\n{\n    FLASH    : ORIGIN = 0x08000000, LENGTH = 1024K\n    RAM      : ORIGIN = 0x24000000, LENGTH = 320K\n}"
  },
  {
    "path": "examples/stm32h735/src/bin/ltdc.rs",
    "content": "#![no_std]\n#![no_main]\n#![macro_use]\n#![allow(static_mut_refs)]\n\n/// This example demonstrates the LTDC lcd display peripheral and was tested to run on an stm32h735g-dk (embassy-stm32 feature \"stm32h735ig\" and probe-rs chip \"STM32H735IGKx\")\n/// Even though the dev kit has 16MB of attached PSRAM this example uses the 320KB of internal AXIS RAM found on the mcu itself to make the example more standalone and portable.\n/// For this reason a 256 color lookup table had to be used to keep the memory requirement down to an acceptable level.\n/// The example bounces a ferris crab bitmap around the screen while blinking an led on another task\n///\nuse bouncy_box::BouncyBox;\nuse defmt::{info, unwrap};\nuse embassy_executor::Spawner;\nuse embassy_stm32::gpio::{Level, Output, Speed};\nuse embassy_stm32::ltdc::{self, Ltdc, LtdcConfiguration, LtdcLayer, LtdcLayerConfig, PolarityActive, PolarityEdge};\nuse embassy_stm32::{bind_interrupts, peripherals};\nuse embassy_time::{Duration, Timer};\nuse embedded_graphics::Pixel;\nuse embedded_graphics::draw_target::DrawTarget;\nuse embedded_graphics::geometry::{OriginDimensions, Point, Size};\nuse embedded_graphics::image::Image;\nuse embedded_graphics::pixelcolor::Rgb888;\nuse embedded_graphics::pixelcolor::raw::RawU24;\nuse embedded_graphics::prelude::*;\nuse embedded_graphics::primitives::Rectangle;\nuse heapless::index_map::{Entry, FnvIndexMap};\nuse tinybmp::Bmp;\nuse {defmt_rtt as _, panic_probe as _};\n\nconst DISPLAY_WIDTH: usize = 480;\nconst DISPLAY_HEIGHT: usize = 272;\nconst MY_TASK_POOL_SIZE: usize = 2;\n\n// the following two display buffers consume 261120 bytes that just about fits into axis ram found on the mcu\npub static mut FB1: [TargetPixelType; DISPLAY_WIDTH * DISPLAY_HEIGHT] = [0; DISPLAY_WIDTH * DISPLAY_HEIGHT];\npub static mut FB2: [TargetPixelType; DISPLAY_WIDTH * DISPLAY_HEIGHT] = [0; DISPLAY_WIDTH * DISPLAY_HEIGHT];\n\nbind_interrupts!(struct Irqs {\n    LTDC => ltdc::InterruptHandler<peripherals::LTDC>;\n});\n\nconst NUM_COLORS: usize = 256;\n\n#[embassy_executor::main]\nasync fn main(spawner: Spawner) {\n    let p = rcc_setup::stm32h735g_init();\n\n    // blink the led on another task\n    let led = Output::new(p.PC3, Level::High, Speed::Low);\n    spawner.spawn(unwrap!(led_task(led)));\n\n    // numbers from STMicroelectronics/STM32CubeH7 STM32H735G-DK C-based example\n    const RK043FN48H_HSYNC: u16 = 41; // Horizontal synchronization\n    const RK043FN48H_HBP: u16 = 13; // Horizontal back porch\n    const RK043FN48H_HFP: u16 = 32; // Horizontal front porch\n    const RK043FN48H_VSYNC: u16 = 10; // Vertical synchronization\n    const RK043FN48H_VBP: u16 = 2; // Vertical back porch\n    const RK043FN48H_VFP: u16 = 2; // Vertical front porch\n\n    let ltdc_config = LtdcConfiguration {\n        active_width: DISPLAY_WIDTH as _,\n        active_height: DISPLAY_HEIGHT as _,\n        h_back_porch: RK043FN48H_HBP - 11, // -11 from MX_LTDC_Init\n        h_front_porch: RK043FN48H_HFP,\n        v_back_porch: RK043FN48H_VBP,\n        v_front_porch: RK043FN48H_VFP,\n        h_sync: RK043FN48H_HSYNC,\n        v_sync: RK043FN48H_VSYNC,\n        h_sync_polarity: PolarityActive::ActiveLow,\n        v_sync_polarity: PolarityActive::ActiveLow,\n        data_enable_polarity: PolarityActive::ActiveHigh,\n        pixel_clock_polarity: PolarityEdge::FallingEdge,\n    };\n\n    info!(\"init ltdc\");\n    let mut ltdc = Ltdc::new_with_pins(\n        p.LTDC, Irqs, p.PG7, p.PC6, p.PA4, p.PG14, p.PD0, p.PD6, p.PA8, p.PE12, p.PA3, p.PB8, p.PB9, p.PB1, p.PB0,\n        p.PA6, p.PE11, p.PH15, p.PH4, p.PC7, p.PD3, p.PE0, p.PH3, p.PH8, p.PH9, p.PH10, p.PH11, p.PE1, p.PE15,\n    );\n    ltdc.init(&ltdc_config);\n\n    // we only need to draw on one layer for this example (not to be confused with the double buffer)\n    info!(\"enable bottom layer\");\n    let layer_config = LtdcLayerConfig {\n        pixel_format: ltdc::PixelFormat::L8, // 1 byte per pixel\n        layer: LtdcLayer::Layer1,\n        window_x0: 0,\n        window_x1: DISPLAY_WIDTH as _,\n        window_y0: 0,\n        window_y1: DISPLAY_HEIGHT as _,\n    };\n\n    let ferris_bmp: Bmp<Rgb888> = Bmp::from_slice(include_bytes!(\"./ferris.bmp\")).unwrap();\n    let color_map = build_color_lookup_map(&ferris_bmp);\n    let clut = build_clut(&color_map);\n\n    // enable the bottom layer with a 256 color lookup table\n    ltdc.init_layer(&layer_config, Some(&clut));\n\n    // Safety: the DoubleBuffer controls access to the statically allocated frame buffers\n    // and it is the only thing that mutates their content\n    let mut double_buffer = DoubleBuffer::new(\n        unsafe { FB1.as_mut() },\n        unsafe { FB2.as_mut() },\n        layer_config,\n        color_map,\n    );\n\n    // this allows us to perform some simple animation for every frame\n    let mut bouncy_box = BouncyBox::new(\n        ferris_bmp.bounding_box(),\n        Rectangle::new(Point::zero(), Size::new(DISPLAY_WIDTH as u32, DISPLAY_HEIGHT as u32)),\n        2,\n    );\n\n    loop {\n        // cpu intensive drawing to the buffer that is NOT currently being copied to the LCD screen\n        double_buffer.clear();\n        let position = bouncy_box.next_point();\n        let ferris = Image::new(&ferris_bmp, position);\n        unwrap!(ferris.draw(&mut double_buffer));\n\n        // perform async dma data transfer to the lcd screen\n        unwrap!(double_buffer.swap(&mut ltdc).await);\n    }\n}\n\n/// builds the color look-up table from all unique colors found in the bitmap. This should be a 256 color indexed bitmap to work.\nfn build_color_lookup_map(bmp: &Bmp<Rgb888>) -> FnvIndexMap<u32, u8, NUM_COLORS> {\n    let mut color_map: FnvIndexMap<u32, u8, NUM_COLORS> = FnvIndexMap::new();\n    let mut counter: u8 = 0;\n\n    // add black to position 0\n    color_map.insert(Rgb888::new(0, 0, 0).into_storage(), counter).unwrap();\n    counter += 1;\n\n    for Pixel(_point, color) in bmp.pixels() {\n        let raw = color.into_storage();\n        if let Entry::Vacant(v) = color_map.entry(raw) {\n            v.insert(counter).expect(\"more than 256 colors detected\");\n            counter += 1;\n        }\n    }\n    color_map\n}\n\n/// builds the color look-up table from the color map provided\nfn build_clut(color_map: &FnvIndexMap<u32, u8, NUM_COLORS>) -> [ltdc::RgbColor; NUM_COLORS] {\n    let mut clut = [ltdc::RgbColor::default(); NUM_COLORS];\n    for (color, index) in color_map.iter() {\n        let color = Rgb888::from(RawU24::new(*color));\n        clut[*index as usize] = ltdc::RgbColor {\n            red: color.r(),\n            green: color.g(),\n            blue: color.b(),\n        };\n    }\n\n    clut\n}\n\n#[embassy_executor::task(pool_size = MY_TASK_POOL_SIZE)]\nasync fn led_task(mut led: Output<'static>) {\n    let mut counter = 0;\n    loop {\n        info!(\"blink: {}\", counter);\n        counter += 1;\n\n        // on\n        led.set_low();\n        Timer::after(Duration::from_millis(50)).await;\n\n        // off\n        led.set_high();\n        Timer::after(Duration::from_millis(450)).await;\n    }\n}\n\npub type TargetPixelType = u8;\n\n// A simple double buffer\npub struct DoubleBuffer {\n    buf0: &'static mut [TargetPixelType],\n    buf1: &'static mut [TargetPixelType],\n    is_buf0: bool,\n    layer_config: LtdcLayerConfig,\n    color_map: FnvIndexMap<u32, u8, NUM_COLORS>,\n}\n\nimpl DoubleBuffer {\n    pub fn new(\n        buf0: &'static mut [TargetPixelType],\n        buf1: &'static mut [TargetPixelType],\n        layer_config: LtdcLayerConfig,\n        color_map: FnvIndexMap<u32, u8, NUM_COLORS>,\n    ) -> Self {\n        Self {\n            buf0,\n            buf1,\n            is_buf0: true,\n            layer_config,\n            color_map,\n        }\n    }\n\n    pub fn current(&mut self) -> (&FnvIndexMap<u32, u8, NUM_COLORS>, &mut [TargetPixelType]) {\n        if self.is_buf0 {\n            (&self.color_map, self.buf0)\n        } else {\n            (&self.color_map, self.buf1)\n        }\n    }\n\n    pub async fn swap<T: ltdc::Instance>(&mut self, ltdc: &mut Ltdc<'_, T>) -> Result<(), ltdc::Error> {\n        let (_, buf) = self.current();\n        let frame_buffer = buf.as_ptr();\n        self.is_buf0 = !self.is_buf0;\n        ltdc.set_buffer(self.layer_config.layer, frame_buffer as *const _).await\n    }\n\n    /// Clears the buffer\n    pub fn clear(&mut self) {\n        let (color_map, buf) = self.current();\n        let black = Rgb888::new(0, 0, 0).into_storage();\n        let color_index = color_map.get(&black).expect(\"no black found in the color map\");\n\n        for a in buf.iter_mut() {\n            *a = *color_index; // solid black\n        }\n    }\n}\n\n// Implement DrawTarget for\nimpl DrawTarget for DoubleBuffer {\n    type Color = Rgb888;\n    type Error = ();\n\n    /// Draw a pixel\n    fn draw_iter<I>(&mut self, pixels: I) -> Result<(), Self::Error>\n    where\n        I: IntoIterator<Item = Pixel<Self::Color>>,\n    {\n        let size = self.size();\n        let width = size.width as i32;\n        let height = size.height as i32;\n        let (color_map, buf) = self.current();\n\n        for pixel in pixels {\n            let Pixel(point, color) = pixel;\n\n            if point.x >= 0 && point.y >= 0 && point.x < width && point.y < height {\n                let index = point.y * width + point.x;\n                let raw_color = color.into_storage();\n\n                match color_map.get(&raw_color) {\n                    Some(x) => {\n                        buf[index as usize] = *x;\n                    }\n                    None => panic!(\"color not found in color map: {}\", raw_color),\n                };\n            } else {\n                // Ignore invalid points\n            }\n        }\n\n        Ok(())\n    }\n}\n\nimpl OriginDimensions for DoubleBuffer {\n    /// Return the size of the display\n    fn size(&self) -> Size {\n        Size::new(\n            (self.layer_config.window_x1 - self.layer_config.window_x0) as _,\n            (self.layer_config.window_y1 - self.layer_config.window_y0) as _,\n        )\n    }\n}\n\nmod rcc_setup {\n\n    use embassy_stm32::rcc::{Hse, HseMode, *};\n    use embassy_stm32::time::Hertz;\n    use embassy_stm32::{Config, Peripherals};\n\n    /// Sets up clocks for the stm32h735g mcu\n    /// change this if you plan to use a different microcontroller\n    pub fn stm32h735g_init() -> Peripherals {\n        /*\n         https://github.com/STMicroelectronics/STM32CubeH7/blob/master/Projects/STM32H735G-DK/Examples/GPIO/GPIO_EXTI/Src/main.c\n         @brief  System Clock Configuration\n            The system Clock is configured as follow :\n            System Clock source            = PLL (HSE)\n            SYSCLK(Hz)                     = 520000000 (CPU Clock)\n            HCLK(Hz)                       = 260000000 (AXI and AHBs Clock)\n            AHB Prescaler                  = 2\n            D1 APB3 Prescaler              = 2 (APB3 Clock  130MHz)\n            D2 APB1 Prescaler              = 2 (APB1 Clock  130MHz)\n            D2 APB2 Prescaler              = 2 (APB2 Clock  130MHz)\n            D3 APB4 Prescaler              = 2 (APB4 Clock  130MHz)\n            HSE Frequency(Hz)              = 25000000\n            PLL_M                          = 5\n            PLL_N                          = 104\n            PLL_P                          = 1\n            PLL_Q                          = 4\n            PLL_R                          = 2\n            VDD(V)                         = 3.3\n            Flash Latency(WS)              = 3\n        */\n\n        // setup power and clocks for an stm32h735g-dk run from an external 25 Mhz external oscillator\n        let mut config = Config::default();\n        config.rcc.hse = Some(Hse {\n            freq: Hertz::mhz(25),\n            mode: HseMode::Oscillator,\n        });\n        config.rcc.hsi = None;\n        config.rcc.csi = false;\n        config.rcc.pll1 = Some(Pll {\n            source: PllSource::HSE,\n            prediv: PllPreDiv::DIV5, // PLL_M\n            mul: PllMul::MUL104,     // PLL_N\n            divp: Some(PllDiv::DIV1),\n            divq: Some(PllDiv::DIV4),\n            divr: Some(PllDiv::DIV2),\n        });\n        // numbers adapted from Drivers/BSP/STM32H735G-DK/stm32h735g_discovery_ospi.c\n        // MX_OSPI_ClockConfig\n        config.rcc.pll2 = Some(Pll {\n            source: PllSource::HSE,\n            prediv: PllPreDiv::DIV5, // PLL_M\n            mul: PllMul::MUL80,      // PLL_N\n            divp: Some(PllDiv::DIV5),\n            divq: Some(PllDiv::DIV2),\n            divr: Some(PllDiv::DIV2),\n        });\n        // numbers adapted from Drivers/BSP/STM32H735G-DK/stm32h735g_discovery_lcd.c\n        // MX_LTDC_ClockConfig\n        config.rcc.pll3 = Some(Pll {\n            source: PllSource::HSE,\n            prediv: PllPreDiv::DIV5, // PLL_M\n            mul: PllMul::MUL160,     // PLL_N\n            divp: Some(PllDiv::DIV2),\n            divq: Some(PllDiv::DIV2),\n            divr: Some(PllDiv::DIV83),\n        });\n        config.rcc.voltage_scale = VoltageScale::Scale0;\n        config.rcc.supply_config = SupplyConfig::DirectSMPS;\n        config.rcc.sys = Sysclk::PLL1_P;\n        config.rcc.ahb_pre = AHBPrescaler::DIV2;\n        config.rcc.apb1_pre = APBPrescaler::DIV2;\n        config.rcc.apb2_pre = APBPrescaler::DIV2;\n        config.rcc.apb3_pre = APBPrescaler::DIV2;\n        config.rcc.apb4_pre = APBPrescaler::DIV2;\n        embassy_stm32::init(config)\n    }\n}\n\nmod bouncy_box {\n    use embedded_graphics::geometry::Point;\n    use embedded_graphics::primitives::Rectangle;\n\n    enum Direction {\n        DownLeft,\n        DownRight,\n        UpLeft,\n        UpRight,\n    }\n\n    pub struct BouncyBox {\n        direction: Direction,\n        child_rect: Rectangle,\n        parent_rect: Rectangle,\n        current_point: Point,\n        move_by: usize,\n    }\n\n    // This calculates the coordinates of a chile rectangle bounced around inside a parent bounded box\n    impl BouncyBox {\n        pub fn new(child_rect: Rectangle, parent_rect: Rectangle, move_by: usize) -> Self {\n            let center_box = parent_rect.center();\n            let center_img = child_rect.center();\n            let current_point = Point::new(center_box.x - center_img.x / 2, center_box.y - center_img.y / 2);\n            Self {\n                direction: Direction::DownRight,\n                child_rect,\n                parent_rect,\n                current_point,\n                move_by,\n            }\n        }\n\n        pub fn next_point(&mut self) -> Point {\n            let direction = &self.direction;\n            let img_height = self.child_rect.size.height as i32;\n            let box_height = self.parent_rect.size.height as i32;\n            let img_width = self.child_rect.size.width as i32;\n            let box_width = self.parent_rect.size.width as i32;\n            let move_by = self.move_by as i32;\n\n            match direction {\n                Direction::DownLeft => {\n                    self.current_point.x -= move_by;\n                    self.current_point.y += move_by;\n\n                    let x_out_of_bounds = self.current_point.x < 0;\n                    let y_out_of_bounds = (self.current_point.y + img_height) > box_height;\n\n                    if x_out_of_bounds && y_out_of_bounds {\n                        self.direction = Direction::UpRight\n                    } else if x_out_of_bounds && !y_out_of_bounds {\n                        self.direction = Direction::DownRight\n                    } else if !x_out_of_bounds && y_out_of_bounds {\n                        self.direction = Direction::UpLeft\n                    }\n                }\n                Direction::DownRight => {\n                    self.current_point.x += move_by;\n                    self.current_point.y += move_by;\n\n                    let x_out_of_bounds = (self.current_point.x + img_width) > box_width;\n                    let y_out_of_bounds = (self.current_point.y + img_height) > box_height;\n\n                    if x_out_of_bounds && y_out_of_bounds {\n                        self.direction = Direction::UpLeft\n                    } else if x_out_of_bounds && !y_out_of_bounds {\n                        self.direction = Direction::DownLeft\n                    } else if !x_out_of_bounds && y_out_of_bounds {\n                        self.direction = Direction::UpRight\n                    }\n                }\n                Direction::UpLeft => {\n                    self.current_point.x -= move_by;\n                    self.current_point.y -= move_by;\n\n                    let x_out_of_bounds = self.current_point.x < 0;\n                    let y_out_of_bounds = self.current_point.y < 0;\n\n                    if x_out_of_bounds && y_out_of_bounds {\n                        self.direction = Direction::DownRight\n                    } else if x_out_of_bounds && !y_out_of_bounds {\n                        self.direction = Direction::UpRight\n                    } else if !x_out_of_bounds && y_out_of_bounds {\n                        self.direction = Direction::DownLeft\n                    }\n                }\n                Direction::UpRight => {\n                    self.current_point.x += move_by;\n                    self.current_point.y -= move_by;\n\n                    let x_out_of_bounds = (self.current_point.x + img_width) > box_width;\n                    let y_out_of_bounds = self.current_point.y < 0;\n\n                    if x_out_of_bounds && y_out_of_bounds {\n                        self.direction = Direction::DownLeft\n                    } else if x_out_of_bounds && !y_out_of_bounds {\n                        self.direction = Direction::UpLeft\n                    } else if !x_out_of_bounds && y_out_of_bounds {\n                        self.direction = Direction::DownRight\n                    }\n                }\n            }\n\n            self.current_point\n        }\n    }\n}\n"
  },
  {
    "path": "examples/stm32h742/.cargo/config.toml",
    "content": "[target.'cfg(all(target_arch = \"arm\", target_os = \"none\"))']\n# replace STM32H742VITx with your chip as listed in `probe-rs chip list`\nrunner = \"probe-rs run --chip STM32H742VITx\"\n\n[build]\ntarget = \"thumbv7em-none-eabihf\"\n\n[env]\nDEFMT_LOG = \"trace\"\n"
  },
  {
    "path": "examples/stm32h742/Cargo.toml",
    "content": "[package]\nedition = \"2024\"\nname = \"embassy-stm32h742-examples\"\nversion = \"0.1.0\"\nlicense = \"MIT OR Apache-2.0\"\npublish = false\n\n[dependencies]\nembassy-stm32 = { version = \"0.6.0\", path = \"../../embassy-stm32\", features = [\n    \"defmt\",\n    \"stm32h742vi\",\n    \"memory-x\",\n    \"unstable-pac\",\n    \"time-driver-any\",\n    \"exti\",\n] }\nembassy-sync = { version = \"0.8.0\", path = \"../../embassy-sync\", features = [\n    \"defmt\",\n] }\nembassy-executor = { version = \"0.10.0\", path = \"../../embassy-executor\", features = [\n    \"platform-cortex-m\",\n    \"executor-thread\",\n    \"defmt\",\n] }\nembassy-time = { version = \"0.5.1\", path = \"../../embassy-time\", features = [\n    \"defmt\",\n    \"defmt-timestamp-uptime\",\n    \"tick-hz-32_768\",\n] }\nembassy-net = { version = \"0.9.0\", path = \"../../embassy-net\", features = [\n    \"defmt\",\n    \"tcp\",\n    \"dhcpv4\",\n    \"medium-ethernet\",\n] }\nembedded-io-async = { version = \"0.7.0\" }\nembassy-usb = { version = \"0.6.0\", path = \"../../embassy-usb\", features = [\n    \"defmt\",\n] }\nembassy-futures = { version = \"0.1.2\", path = \"../../embassy-futures\" }\n\ndefmt = \"1.0.1\"\ndefmt-rtt = \"1.0.0\"\n\ncortex-m = { version = \"0.7.6\", features = [\n    \"inline-asm\",\n    \"critical-section-single-core\",\n] }\ncortex-m-rt = \"0.7.0\"\nembedded-hal = \"0.2.6\"\npanic-probe = { version = \"1.0.0\", features = [\"print-defmt\"] }\nheapless = { version = \"0.9\", default-features = false }\nnb = \"1.0.0\"\ncritical-section = \"1.1\"\nembedded-storage = \"0.3.1\"\nstatic_cell = \"2\"\nsha2 = { version = \"0.10.8\", default-features = false }\nhmac = \"0.12.1\"\naes-gcm = { version = \"0.10.3\", default-features = false, features = [\n    \"aes\",\n    \"heapless\",\n] }\n\n[profile.release]\ndebug = 2\n\n[package.metadata.embassy]\nbuild = [\n  { target = \"thumbv7em-none-eabi\", artifact-dir = \"out/examples/stm32h742\" }\n]\n"
  },
  {
    "path": "examples/stm32h742/build.rs",
    "content": "fn main() {\n    println!(\"cargo:rustc-link-arg-bins=--nmagic\");\n    println!(\"cargo:rustc-link-arg-bins=-Tlink.x\");\n    println!(\"cargo:rustc-link-arg-bins=-Tdefmt.x\");\n}\n"
  },
  {
    "path": "examples/stm32h742/src/bin/qspi.rs",
    "content": "#![no_std]\n#![no_main]\n#![allow(dead_code)] // Allow dead code as not all commands are used in the example\n\nuse defmt::info;\nuse embassy_executor::Spawner;\nuse embassy_stm32::Config as StmCfg;\nuse embassy_stm32::mode::Blocking;\nuse embassy_stm32::qspi::enums::{AddressSize, ChipSelectHighTime, FIFOThresholdLevel, MemorySize, *};\nuse embassy_stm32::qspi::{Config as QspiCfg, Instance, Qspi, TransferConfig};\nuse {defmt_rtt as _, panic_probe as _};\n\nconst MEMORY_PAGE_SIZE: usize = 256;\n\nconst CMD_READ: u8 = 0x03;\nconst CMD_HS_READ: u8 = 0x0B;\nconst CMD_QUAD_READ: u8 = 0x6B;\n\nconst CMD_WRITE_PG: u8 = 0xF2;\nconst CMD_QUAD_WRITE_PG: u8 = 0x32;\n\nconst CMD_READ_ID: u8 = 0x9F;\nconst CMD_READ_UUID: u8 = 0x4B;\n\nconst CMD_ENABLE_RESET: u8 = 0x66;\nconst CMD_RESET: u8 = 0x99;\n\nconst CMD_WRITE_ENABLE: u8 = 0x06;\nconst CMD_WRITE_DISABLE: u8 = 0x04;\n\nconst CMD_CHIP_ERASE: u8 = 0xC7;\nconst CMD_SECTOR_ERASE: u8 = 0x20;\nconst CMD_BLOCK_ERASE_32K: u8 = 0x52;\nconst CMD_BLOCK_ERASE_64K: u8 = 0xD8;\n\nconst CMD_READ_SR: u8 = 0x05;\nconst CMD_READ_CR: u8 = 0x35;\n\nconst CMD_WRITE_SR: u8 = 0x01;\nconst CMD_WRITE_CR: u8 = 0x31;\nconst MEMORY_ADDR: u32 = 0x00000001u32;\n\n/// Implementation of access to flash chip.\n/// Chip commands are hardcoded as it depends on used chip.\n/// This implementation is using chip GD25Q64C from Giga Device\npub struct FlashMemory<I: Instance> {\n    qspi: Qspi<'static, I, Blocking>,\n}\n\nimpl<I: Instance> FlashMemory<I> {\n    pub fn new(qspi: Qspi<'static, I, Blocking>) -> Self {\n        let mut memory = Self { qspi };\n\n        memory.reset_memory();\n        memory.enable_quad();\n\n        memory\n    }\n\n    fn enable_quad(&mut self) {\n        let cr = self.read_cr();\n        self.write_cr(cr | 0x02);\n    }\n\n    fn exec_command(&mut self, cmd: u8) {\n        let transaction = TransferConfig {\n            iwidth: QspiWidth::SING,\n            awidth: QspiWidth::NONE,\n            dwidth: QspiWidth::NONE,\n            instruction: cmd,\n            address: None,\n            dummy: DummyCycles::_0,\n        };\n        self.qspi.blocking_command(transaction);\n    }\n\n    pub fn reset_memory(&mut self) {\n        self.exec_command(CMD_ENABLE_RESET);\n        self.exec_command(CMD_RESET);\n        self.wait_write_finish();\n    }\n\n    pub fn enable_write(&mut self) {\n        self.exec_command(CMD_WRITE_ENABLE);\n    }\n\n    pub fn read_id(&mut self) -> [u8; 3] {\n        let mut buffer = [0; 3];\n        let transaction: TransferConfig = TransferConfig {\n            iwidth: QspiWidth::SING,\n            awidth: QspiWidth::NONE,\n            dwidth: QspiWidth::SING,\n            instruction: CMD_READ_ID,\n            address: None,\n            dummy: DummyCycles::_0,\n        };\n        self.qspi.blocking_read(&mut buffer, transaction);\n        buffer\n    }\n\n    pub fn read_uuid(&mut self) -> [u8; 16] {\n        let mut buffer = [0; 16];\n        let transaction: TransferConfig = TransferConfig {\n            iwidth: QspiWidth::SING,\n            awidth: QspiWidth::SING,\n            dwidth: QspiWidth::SING,\n            instruction: CMD_READ_UUID,\n            address: Some(0),\n            dummy: DummyCycles::_8,\n        };\n        self.qspi.blocking_read(&mut buffer, transaction);\n        buffer\n    }\n\n    pub fn read_memory(&mut self, addr: u32, buffer: &mut [u8]) {\n        let transaction = TransferConfig {\n            iwidth: QspiWidth::SING,\n            awidth: QspiWidth::SING,\n            dwidth: QspiWidth::QUAD,\n            instruction: CMD_QUAD_READ,\n            address: Some(addr),\n            dummy: DummyCycles::_8,\n        };\n        self.qspi.blocking_read(buffer, transaction);\n    }\n\n    fn wait_write_finish(&mut self) {\n        while (self.read_sr() & 0x01) != 0 {}\n    }\n\n    fn perform_erase(&mut self, addr: u32, cmd: u8) {\n        let transaction = TransferConfig {\n            iwidth: QspiWidth::SING,\n            awidth: QspiWidth::SING,\n            dwidth: QspiWidth::NONE,\n            instruction: cmd,\n            address: Some(addr),\n            dummy: DummyCycles::_0,\n        };\n        self.enable_write();\n        self.qspi.blocking_command(transaction);\n        self.wait_write_finish();\n    }\n\n    pub fn erase_sector(&mut self, addr: u32) {\n        self.perform_erase(addr, CMD_SECTOR_ERASE);\n    }\n\n    pub fn erase_block_32k(&mut self, addr: u32) {\n        self.perform_erase(addr, CMD_BLOCK_ERASE_32K);\n    }\n\n    pub fn erase_block_64k(&mut self, addr: u32) {\n        self.perform_erase(addr, CMD_BLOCK_ERASE_64K);\n    }\n\n    pub fn erase_chip(&mut self) {\n        self.exec_command(CMD_CHIP_ERASE);\n    }\n\n    fn write_page(&mut self, addr: u32, buffer: &[u8], len: usize) {\n        assert!(\n            (len as u32 + (addr & 0x000000ff)) <= MEMORY_PAGE_SIZE as u32,\n            \"write_page(): page write length exceeds page boundary (len = {}, addr = {:X}\",\n            len,\n            addr\n        );\n\n        let transaction = TransferConfig {\n            iwidth: QspiWidth::SING,\n            awidth: QspiWidth::SING,\n            dwidth: QspiWidth::QUAD,\n            instruction: CMD_QUAD_WRITE_PG,\n            address: Some(addr),\n            dummy: DummyCycles::_0,\n        };\n        self.enable_write();\n        self.qspi.blocking_write(buffer, transaction);\n        self.wait_write_finish();\n    }\n\n    pub fn write_memory(&mut self, addr: u32, buffer: &[u8]) {\n        let mut left = buffer.len();\n        let mut place = addr;\n        let mut chunk_start = 0;\n\n        while left > 0 {\n            let max_chunk_size = MEMORY_PAGE_SIZE - (place & 0x000000ff) as usize;\n            let chunk_size = if left >= max_chunk_size { max_chunk_size } else { left };\n            let chunk = &buffer[chunk_start..(chunk_start + chunk_size)];\n            self.write_page(place, chunk, chunk_size);\n            place += chunk_size as u32;\n            left -= chunk_size;\n            chunk_start += chunk_size;\n        }\n    }\n\n    fn read_register(&mut self, cmd: u8) -> u8 {\n        let mut buffer = [0; 1];\n        let transaction: TransferConfig = TransferConfig {\n            iwidth: QspiWidth::SING,\n            awidth: QspiWidth::NONE,\n            dwidth: QspiWidth::SING,\n            instruction: cmd,\n            address: None,\n            dummy: DummyCycles::_0,\n        };\n        self.qspi.blocking_read(&mut buffer, transaction);\n        buffer[0]\n    }\n\n    fn write_register(&mut self, cmd: u8, value: u8) {\n        let buffer = [value; 1];\n        let transaction: TransferConfig = TransferConfig {\n            iwidth: QspiWidth::SING,\n            awidth: QspiWidth::NONE,\n            dwidth: QspiWidth::SING,\n            instruction: cmd,\n            address: None,\n            dummy: DummyCycles::_0,\n        };\n        self.qspi.blocking_write(&buffer, transaction);\n    }\n\n    pub fn read_sr(&mut self) -> u8 {\n        self.read_register(CMD_READ_SR)\n    }\n\n    pub fn read_cr(&mut self) -> u8 {\n        self.read_register(CMD_READ_CR)\n    }\n\n    pub fn write_sr(&mut self, value: u8) {\n        self.write_register(CMD_WRITE_SR, value);\n    }\n\n    pub fn write_cr(&mut self, value: u8) {\n        self.write_register(CMD_WRITE_CR, value);\n    }\n}\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) -> ! {\n    let mut config = StmCfg::default();\n    {\n        use embassy_stm32::rcc::*;\n        config.rcc.hsi = Some(HSIPrescaler::DIV1);\n        config.rcc.csi = true;\n        config.rcc.hsi48 = Some(Default::default()); // needed for RNG\n        config.rcc.pll1 = Some(Pll {\n            source: PllSource::HSI,\n            prediv: PllPreDiv::DIV4,\n            mul: PllMul::MUL50,\n            divp: Some(PllDiv::DIV2),\n            divq: None,\n            divr: None,\n        });\n        config.rcc.sys = Sysclk::PLL1_P; // 400 Mhz\n        config.rcc.ahb_pre = AHBPrescaler::DIV2; // 200 Mhz\n        config.rcc.apb1_pre = APBPrescaler::DIV2; // 100 Mhz\n        config.rcc.apb2_pre = APBPrescaler::DIV2; // 100 Mhz\n        config.rcc.apb3_pre = APBPrescaler::DIV2; // 100 Mhz\n        config.rcc.apb4_pre = APBPrescaler::DIV2; // 100 Mhz\n        config.rcc.voltage_scale = VoltageScale::Scale1;\n    }\n    let p = embassy_stm32::init(config);\n    info!(\"Embassy initialized\");\n\n    let mut config = QspiCfg::default();\n    config.memory_size = MemorySize::_8MiB;\n    config.address_size = AddressSize::_24bit;\n    config.prescaler = 16;\n    config.cs_high_time = ChipSelectHighTime::_1Cycle;\n    config.fifo_threshold = FIFOThresholdLevel::_16Bytes;\n    config.sample_shifting = SampleShifting::None;\n\n    let driver = Qspi::new_blocking_bank1(p.QUADSPI, p.PD11, p.PD12, p.PE2, p.PD13, p.PB2, p.PB10, config);\n    let mut flash = FlashMemory::new(driver);\n    let flash_id = flash.read_id();\n    info!(\"FLASH ID: {:?}\", flash_id);\n    let mut wr_buf = [0u8; 256];\n    for i in 0..256 {\n        wr_buf[i] = i as u8;\n    }\n    let mut rd_buf = [0u8; 256];\n    flash.erase_sector(MEMORY_ADDR);\n    flash.write_memory(MEMORY_ADDR, &wr_buf);\n    flash.read_memory(MEMORY_ADDR, &mut rd_buf);\n    info!(\"WRITE BUF: {:?}\", wr_buf);\n    info!(\"READ BUF: {:?}\", rd_buf);\n    info!(\"End of Program, proceed to empty endless loop\");\n    loop {}\n}\n"
  },
  {
    "path": "examples/stm32h755cm4/.cargo/config.toml",
    "content": "[target.thumbv7em-none-eabihf]\nrunner = 'probe-rs run --chip STM32H755ZITx --catch-hardfault --always-print-stacktrace'\n\n[build]\ntarget = \"thumbv7em-none-eabihf\" # Cortex-M4F and Cortex-M7F (with FPU)\n\n[env]\nDEFMT_LOG = \"trace\"\n"
  },
  {
    "path": "examples/stm32h755cm4/Cargo.toml",
    "content": "[package]\nedition = \"2024\"\nname = \"embassy-stm32h755cm4-examples\"\nversion = \"0.1.0\"\nlicense = \"MIT OR Apache-2.0\"\npublish = false\n\n[dependencies]\n# Change stm32h755zi-cm4 to your chip name, if necessary.\nembassy-stm32 = { version = \"0.6.0\", path = \"../../embassy-stm32\", features = [\"defmt\", \"stm32h755zi-cm4\", \"time-driver-tim2\", \"exti\", \"memory-x\", \"unstable-pac\", \"chrono\"] }\nembassy-embedded-hal = { version = \"0.6.0\", path = \"../../embassy-embedded-hal\" }\nembassy-executor = { version = \"0.10.0\", path = \"../../embassy-executor\", features = [\"platform-cortex-m\", \"executor-thread\", \"executor-interrupt\", \"defmt\"] }\nembassy-time = { version = \"0.5.1\", path = \"../../embassy-time\", features = [\"defmt\", \"defmt-timestamp-uptime\", \"tick-hz-32_768\"] }\nembassy-futures = { version = \"0.1.2\", path = \"../../embassy-futures\" }\n\ndefmt = \"1.0.1\"\ndefmt-rtt = \"1.0.0\"\n\ncortex-m = { version = \"0.7.6\", features = [\"inline-asm\", \"critical-section-single-core\"] }\ncortex-m-rt = \"0.7.0\"\nembedded-hal = \"0.2.6\"\nembedded-hal-1 = { package = \"embedded-hal\", version = \"1.0\" }\nembedded-hal-async = { version = \"1.0\" }\npanic-probe = { version = \"1.0.0\", features = [\"print-defmt\"] }\ncritical-section = \"1.1\"\n\n# cargo build/run\n\n# cargo test\n[profile.test]\ncodegen-units = 1\ndebug = 2\ndebug-assertions = true # <-\nincremental = false\nopt-level = 3 # <-\noverflow-checks = true # <-\n\n# cargo build/run --release\n[profile.release]\ncodegen-units = 16\ndebug = 2\ndebug-assertions = false # <-\nincremental = false\nopt-level = 3 # <-\noverflow-checks = false # <-\n\n# cargo test --release\n[profile.bench]\ncodegen-units = 1\ndebug = 2\ndebug-assertions = false # <-\nincremental = false\nlto = 'fat'\nopt-level = 3 # <-\noverflow-checks = false # <-\n\n[package.metadata.embassy]\nbuild = [\n  { target = \"thumbv7em-none-eabi\", artifact-dir = \"out/examples/stm32h755cm4\" }\n]\n"
  },
  {
    "path": "examples/stm32h755cm4/build.rs",
    "content": "//! This build script copies the `memory.x` file from the crate root into\n//! a directory where the linker can always find it at build time.\n//! For many projects this is optional, as the linker always searches the\n//! project root directory -- wherever `Cargo.toml` is. However, if you\n//! are using a workspace or have a more complicated build setup, this\n//! build script becomes required. Additionally, by requesting that\n//! Cargo re-run the build script whenever `memory.x` is changed,\n//! updating `memory.x` ensures a rebuild of the application with the\n//! new memory settings.\n\nuse std::env;\nuse std::fs::File;\nuse std::io::Write;\nuse std::path::PathBuf;\n\nfn main() {\n    // Put `memory.x` in our output directory and ensure it's\n    // on the linker search path.\n    let out = &PathBuf::from(env::var_os(\"OUT_DIR\").unwrap());\n    File::create(out.join(\"memory.x\"))\n        .unwrap()\n        .write_all(include_bytes!(\"memory.x\"))\n        .unwrap();\n    println!(\"cargo:rustc-link-search={}\", out.display());\n\n    // By default, Cargo will re-run a build script whenever\n    // any file in the project changes. By specifying `memory.x`\n    // here, we ensure the build script is only re-run when\n    // `memory.x` is changed.\n    println!(\"cargo:rerun-if-changed=memory.x\");\n\n    println!(\"cargo:rustc-link-arg-bins=--nmagic\");\n    println!(\"cargo:rustc-link-arg-bins=-Tlink.x\");\n    println!(\"cargo:rustc-link-arg-bins=-Tdefmt.x\");\n}\n"
  },
  {
    "path": "examples/stm32h755cm4/memory.x",
    "content": "MEMORY\n{\n    FLASH    : ORIGIN = 0x08100000, LENGTH = 1024K /* BANK_2 */\n    RAM      : ORIGIN = 0x10000000, LENGTH = 128K  /* SRAM1 */\n    RAM_D3   : ORIGIN = 0x38000000, LENGTH = 64K   /* SRAM4 */\n}\n\nSECTIONS\n{\n    .ram_d3 :\n    {\n        *(.ram_d3.shared_data)\n        *(.ram_d3)\n    } > RAM_D3\n}"
  },
  {
    "path": "examples/stm32h755cm4/src/bin/blinky.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse core::mem::MaybeUninit;\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_stm32::SharedData;\nuse embassy_stm32::gpio::{Level, Output, Speed};\nuse embassy_time::Timer;\nuse {defmt_rtt as _, panic_probe as _};\n\n#[unsafe(link_section = \".ram_d3.shared_data\")]\nstatic SHARED_DATA: MaybeUninit<SharedData> = MaybeUninit::uninit();\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let p = embassy_stm32::init_secondary(&SHARED_DATA);\n    info!(\"Hello World!\");\n\n    let mut led = Output::new(p.PE1, Level::High, Speed::Low);\n\n    loop {\n        info!(\"high\");\n        led.set_high();\n        Timer::after_millis(250).await;\n\n        info!(\"low\");\n        led.set_low();\n        Timer::after_millis(250).await;\n    }\n}\n"
  },
  {
    "path": "examples/stm32h755cm4/src/bin/intercore.rs",
    "content": "#![no_std]\n#![no_main]\n\n//! STM32H7 Secondary Core (CM4) Intercore Communication Example\n//!\n//! This example demonstrates reliable communication between the Cortex-M7 and\n//! Cortex-M4 cores. This secondary core monitors shared memory for LED state\n//! changes and updates the physical LEDs accordingly.\n//!\n//! The CM4 core handles:\n//! - Responding to state changes from CM7\n//! - Controlling the physical green and yellow LEDs\n//! - Providing visual feedback via a heartbeat on the red LED\n//!\n//! Usage:\n//! 1. Flash this CM4 (secondary) core binary first\n//! 2. Then flash the CM7 (primary) core binary\n//! 3. The red LED should blink continuously as a heartbeat\n//! 4. Green and yellow LEDs should toggle according to CM7 core signals\n\n/// Module providing shared memory constructs for intercore communication\nmod shared {\n    use core::sync::atomic::{AtomicU32, Ordering};\n\n    /// State shared between CM7 and CM4 cores for LED control\n    #[repr(C, align(4))]\n    pub struct SharedLedState {\n        pub magic: AtomicU32,\n        pub counter: AtomicU32,\n        pub led_states: AtomicU32,\n    }\n\n    // Bit positions in led_states\n    pub const GREEN_LED_BIT: u32 = 0;\n    pub const YELLOW_LED_BIT: u32 = 1;\n\n    impl SharedLedState {\n        pub const fn new() -> Self {\n            Self {\n                magic: AtomicU32::new(0xDEADBEEF),\n                counter: AtomicU32::new(0),\n                led_states: AtomicU32::new(0),\n            }\n        }\n\n        /// Set LED state by manipulating the appropriate bit in the led_states field\n        #[inline(never)]\n        #[allow(dead_code)]\n        pub fn set_led(&self, is_green: bool, state: bool) {\n            let bit = if is_green { GREEN_LED_BIT } else { YELLOW_LED_BIT };\n            let current = self.led_states.load(Ordering::SeqCst);\n\n            let new_value = if state {\n                current | (1 << bit) // Set bit\n            } else {\n                current & !(1 << bit) // Clear bit\n            };\n            self.led_states.store(new_value, Ordering::SeqCst);\n        }\n\n        /// Get current LED state\n        #[inline(never)]\n        pub fn get_led(&self, is_green: bool) -> bool {\n            let bit = if is_green { GREEN_LED_BIT } else { YELLOW_LED_BIT };\n\n            let value = self.led_states.load(Ordering::SeqCst);\n            (value & (1 << bit)) != 0\n        }\n\n        /// Increment counter and return new value\n        #[inline(never)]\n        #[allow(dead_code)]\n        pub fn increment_counter(&self) -> u32 {\n            let current = self.counter.load(Ordering::SeqCst);\n            let new_value = current.wrapping_add(1);\n            self.counter.store(new_value, Ordering::SeqCst);\n            new_value\n        }\n\n        /// Get current counter value\n        #[inline(never)]\n        pub fn get_counter(&self) -> u32 {\n            let value = self.counter.load(Ordering::SeqCst);\n            value\n        }\n    }\n\n    #[unsafe(link_section = \".ram_d3\")]\n    pub static SHARED_LED_STATE: SharedLedState = SharedLedState::new();\n}\n\nuse core::mem::MaybeUninit;\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_stm32::SharedData;\nuse embassy_stm32::gpio::{Level, Output, Speed};\nuse embassy_time::Timer;\nuse shared::SHARED_LED_STATE;\nuse {defmt_rtt as _, panic_probe as _};\n\n#[unsafe(link_section = \".ram_d3\")]\nstatic SHARED_DATA: MaybeUninit<SharedData> = MaybeUninit::uninit();\n\n/// Task that continuously blinks the red LED as a heartbeat indicator\n#[embassy_executor::task]\nasync fn blink_heartbeat(mut led: Output<'static>) {\n    loop {\n        led.toggle();\n        info!(\"CM4 heartbeat\");\n        Timer::after_millis(500).await;\n    }\n}\n\n#[embassy_executor::main]\nasync fn main(spawner: Spawner) -> ! {\n    // Initialize the secondary core\n    let p = embassy_stm32::init_secondary(&SHARED_DATA);\n    info!(\"CM4 core initialized!\");\n\n    // Verify shared memory is accessible\n    let magic = SHARED_LED_STATE.magic.load(core::sync::atomic::Ordering::SeqCst);\n    info!(\"CM4: Magic value = 0x{:X}\", magic);\n\n    // Set up LEDs\n    let mut green_led = Output::new(p.PB0, Level::Low, Speed::Low); // LD1\n    let mut yellow_led = Output::new(p.PE1, Level::Low, Speed::Low); // LD2\n    let red_led = Output::new(p.PB14, Level::Low, Speed::Low); // LD3 (heartbeat)\n\n    // Start heartbeat task\n    spawner.spawn(unwrap!(blink_heartbeat(red_led)));\n\n    // Track previous values to detect changes\n    let mut prev_green = false;\n    let mut prev_yellow = false;\n    let mut prev_counter = 0;\n\n    info!(\"CM4: Starting main loop\");\n    loop {\n        // Read current values from shared memory\n        let green_state = SHARED_LED_STATE.get_led(true);\n        let yellow_state = SHARED_LED_STATE.get_led(false);\n        let counter = SHARED_LED_STATE.get_counter();\n\n        // Detect changes\n        let green_changed = green_state != prev_green;\n        let yellow_changed = yellow_state != prev_yellow;\n        let counter_changed = counter != prev_counter;\n\n        // Update LEDs and logs when values change\n        if green_changed || yellow_changed || counter_changed {\n            if counter_changed {\n                info!(\"CM4: Counter = {}\", counter);\n                prev_counter = counter;\n            }\n\n            if green_changed {\n                if green_state {\n                    green_led.set_high();\n                    info!(\"CM4: Green LED ON\");\n                } else {\n                    green_led.set_low();\n                    info!(\"CM4: Green LED OFF\");\n                }\n                prev_green = green_state;\n            }\n\n            if yellow_changed {\n                if yellow_state {\n                    yellow_led.set_high();\n                    info!(\"CM4: Yellow LED ON\");\n                } else {\n                    yellow_led.set_low();\n                    info!(\"CM4: Yellow LED OFF\");\n                }\n                prev_yellow = yellow_state;\n            }\n        }\n\n        Timer::after_millis(10).await;\n    }\n}\n"
  },
  {
    "path": "examples/stm32h755cm7/.cargo/config.toml",
    "content": "[target.thumbv7em-none-eabihf]\nrunner = 'probe-rs run --chip STM32H755ZITx --catch-hardfault --always-print-stacktrace'\n\n[build]\ntarget = \"thumbv7em-none-eabihf\" # Cortex-M4F and Cortex-M7F (with FPU)\n\n[env]\nDEFMT_LOG = \"trace\"\n"
  },
  {
    "path": "examples/stm32h755cm7/Cargo.toml",
    "content": "[package]\nedition = \"2024\"\nname = \"embassy-stm32h755cm7-examples\"\nversion = \"0.1.0\"\nlicense = \"MIT OR Apache-2.0\"\npublish = false\n\n[dependencies]\n# Change stm32h743bi to your chip name, if necessary.\nembassy-stm32 = { version = \"0.6.0\", path = \"../../embassy-stm32\", features = [\"defmt\", \"stm32h755zi-cm7\", \"time-driver-tim3\", \"exti\", \"memory-x\", \"unstable-pac\", \"chrono\"] }\nembassy-sync = { version = \"0.8.0\", path = \"../../embassy-sync\", features = [\"defmt\"] }\nembassy-embedded-hal = { version = \"0.6.0\", path = \"../../embassy-embedded-hal\" }\nembassy-executor = { version = \"0.10.0\", path = \"../../embassy-executor\", features = [\"platform-cortex-m\", \"executor-thread\", \"executor-interrupt\", \"defmt\"] }\nembassy-time = { version = \"0.5.1\", path = \"../../embassy-time\", features = [\"defmt\", \"defmt-timestamp-uptime\", \"tick-hz-32_768\"] }\nembassy-net = { version = \"0.9.0\", path = \"../../embassy-net\", features = [\"defmt\", \"tcp\", \"dhcpv4\", \"medium-ethernet\", \"proto-ipv6\", \"dns\"] }\nembassy-usb = { version = \"0.6.0\", path = \"../../embassy-usb\", features = [\"defmt\"] }\nembassy-futures = { version = \"0.1.2\", path = \"../../embassy-futures\" }\n\ndefmt = \"1.0.1\"\ndefmt-rtt = \"1.0.0\"\n\ncortex-m = { version = \"0.7.6\", features = [\"inline-asm\", \"critical-section-single-core\"] }\ncortex-m-rt = \"0.7.0\"\nembedded-hal = \"0.2.6\"\nembedded-hal-1 = { package = \"embedded-hal\", version = \"1.0\" }\nembedded-hal-async = { version = \"1.0\" }\nembedded-nal-async = \"0.9.0\"\nembedded-io-async = { version = \"0.7.0\" }\npanic-probe = { version = \"1.0.0\", features = [\"print-defmt\"] }\nheapless = { version = \"0.9\", default-features = false }\ncritical-section = \"1.1\"\nmicromath = \"2.0.0\"\nstm32-fmc = \"0.4.0\"\nembedded-storage = \"0.3.1\"\nstatic_cell = \"2\"\nchrono = { version = \"^0.4\", default-features = false }\ngrounded = \"0.2.0\"\n\n# cargo test\n[profile.test]\ncodegen-units = 1\ndebug = 2\ndebug-assertions = true # <-\nincremental = false\nopt-level = 3 # <-\noverflow-checks = true # <-\n\n# cargo build/run --release\n[profile.release]\ncodegen-units = 16\ndebug = 2\ndebug-assertions = false # <-\nincremental = false\nopt-level = 3 # <-\noverflow-checks = false # <-\n\n# cargo test --release\n[profile.bench]\ncodegen-units = 1\ndebug = 2\ndebug-assertions = false # <-\nincremental = false\nlto = 'fat'\nopt-level = 3 # <-\noverflow-checks = false # <-\n\n[package.metadata.embassy]\nbuild = [\n  { target = \"thumbv7em-none-eabi\", artifact-dir = \"out/examples/stm32h755cm7\" }\n]\n"
  },
  {
    "path": "examples/stm32h755cm7/build.rs",
    "content": "//! This build script copies the `memory.x` file from the crate root into\n//! a directory where the linker can always find it at build time.\n//! For many projects this is optional, as the linker always searches the\n//! project root directory -- wherever `Cargo.toml` is. However, if you\n//! are using a workspace or have a more complicated build setup, this\n//! build script becomes required. Additionally, by requesting that\n//! Cargo re-run the build script whenever `memory.x` is changed,\n//! updating `memory.x` ensures a rebuild of the application with the\n//! new memory settings.\n\nuse std::env;\nuse std::fs::File;\nuse std::io::Write;\nuse std::path::PathBuf;\n\nfn main() {\n    // Put `memory.x` in our output directory and ensure it's\n    // on the linker search path.\n    let out = &PathBuf::from(env::var_os(\"OUT_DIR\").unwrap());\n    File::create(out.join(\"memory.x\"))\n        .unwrap()\n        .write_all(include_bytes!(\"memory.x\"))\n        .unwrap();\n    println!(\"cargo:rustc-link-search={}\", out.display());\n\n    // By default, Cargo will re-run a build script whenever\n    // any file in the project changes. By specifying `memory.x`\n    // here, we ensure the build script is only re-run when\n    // `memory.x` is changed.\n    println!(\"cargo:rerun-if-changed=memory.x\");\n\n    println!(\"cargo:rustc-link-arg-bins=--nmagic\");\n    println!(\"cargo:rustc-link-arg-bins=-Tlink.x\");\n    println!(\"cargo:rustc-link-arg-bins=-Tdefmt.x\");\n}\n"
  },
  {
    "path": "examples/stm32h755cm7/memory.x",
    "content": "MEMORY\n{\n    FLASH    : ORIGIN = 0x08000000, LENGTH = 1024K /* BANK_1 */\n    RAM      : ORIGIN = 0x24000000, LENGTH = 512K  /* AXIRAM */\n    RAM_D3   : ORIGIN = 0x38000000, LENGTH = 64K   /* SRAM4 */\n}\n\nSECTIONS\n{\n    .ram_d3 :\n    {\n        *(.ram_d3.shared_data)\n        *(.ram_d3)\n    } > RAM_D3\n}"
  },
  {
    "path": "examples/stm32h755cm7/src/bin/blinky.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse core::mem::MaybeUninit;\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_stm32::SharedData;\nuse embassy_stm32::gpio::{Level, Output, Speed};\nuse embassy_time::Timer;\nuse {defmt_rtt as _, panic_probe as _};\n\n#[unsafe(link_section = \".ram_d3.shared_data\")]\nstatic SHARED_DATA: MaybeUninit<SharedData> = MaybeUninit::uninit();\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let mut config = embassy_stm32::Config::default();\n    {\n        use embassy_stm32::rcc::*;\n        config.rcc.hsi = Some(HSIPrescaler::DIV1);\n        config.rcc.csi = true;\n        config.rcc.pll1 = Some(Pll {\n            source: PllSource::HSI,\n            prediv: PllPreDiv::DIV4,\n            mul: PllMul::MUL50,\n            divp: Some(PllDiv::DIV2),\n            divq: Some(PllDiv::DIV8), // 100mhz\n            divr: None,\n        });\n        config.rcc.sys = Sysclk::PLL1_P; // 400 Mhz\n        config.rcc.ahb_pre = AHBPrescaler::DIV2; // 200 Mhz\n        config.rcc.apb1_pre = APBPrescaler::DIV2; // 100 Mhz\n        config.rcc.apb2_pre = APBPrescaler::DIV2; // 100 Mhz\n        config.rcc.apb3_pre = APBPrescaler::DIV2; // 100 Mhz\n        config.rcc.apb4_pre = APBPrescaler::DIV2; // 100 Mhz\n        config.rcc.voltage_scale = VoltageScale::Scale1;\n        config.rcc.supply_config = SupplyConfig::DirectSMPS;\n    }\n    let p = embassy_stm32::init_primary(config, &SHARED_DATA);\n    info!(\"Hello World!\");\n\n    let mut led = Output::new(p.PB14, Level::High, Speed::Low);\n\n    loop {\n        info!(\"high\");\n        led.set_high();\n        Timer::after_millis(500).await;\n\n        info!(\"low\");\n        led.set_low();\n        Timer::after_millis(500).await;\n    }\n}\n"
  },
  {
    "path": "examples/stm32h755cm7/src/bin/intercore.rs",
    "content": "#![no_std]\n#![no_main]\n\n//! STM32H7 Primary Core (CM7) Intercore Communication Example\n//!\n//! This example demonstrates reliable communication between the Cortex-M7 and\n//! Cortex-M4 cores using a shared memory region configured as non-cacheable\n//! via MPU settings.\n//!\n//! The CM7 core handles:\n//! - MPU configuration to make shared memory non-cacheable\n//! - Clock initialization\n//! - Toggling LED states in shared memory\n//!\n//! Usage:\n//! 1. Flash the CM4 (secondary) core binary first\n//! 2. Then flash this CM7 (primary) core binary\n//! 3. The system will start with CM7 toggling LED states and CM4 responding by\n//!    physically toggling the LEDs\n\nuse core::mem::MaybeUninit;\n\nuse cortex_m::asm;\nuse cortex_m::peripheral::MPU;\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_stm32::{Config, SharedData};\nuse embassy_time::Timer;\nuse shared::{SHARED_LED_STATE, SRAM4_BASE_ADDRESS, SRAM4_REGION_NUMBER, SRAM4_SIZE_LOG2};\nuse {defmt_rtt as _, panic_probe as _};\n\n/// Module providing shared memory constructs for intercore communication\nmod shared {\n    use core::sync::atomic::{AtomicU32, Ordering};\n\n    /// State shared between CM7 and CM4 cores for LED control\n    #[repr(C, align(4))]\n    pub struct SharedLedState {\n        pub magic: AtomicU32,\n        pub counter: AtomicU32,\n        pub led_states: AtomicU32,\n    }\n\n    // Bit positions in led_states\n    pub const GREEN_LED_BIT: u32 = 0;\n    pub const YELLOW_LED_BIT: u32 = 1;\n\n    impl SharedLedState {\n        pub const fn new() -> Self {\n            Self {\n                magic: AtomicU32::new(0xDEADBEEF),\n                counter: AtomicU32::new(0),\n                led_states: AtomicU32::new(0),\n            }\n        }\n\n        /// Set LED state by manipulating the appropriate bit in the led_states field\n        #[inline(never)]\n        pub fn set_led(&self, is_green: bool, state: bool) {\n            let bit = if is_green { GREEN_LED_BIT } else { YELLOW_LED_BIT };\n            let current = self.led_states.load(Ordering::SeqCst);\n\n            let new_value = if state {\n                current | (1 << bit) // Set bit\n            } else {\n                current & !(1 << bit) // Clear bit\n            };\n\n            self.led_states.store(new_value, Ordering::SeqCst);\n        }\n\n        /// Get current LED state\n        #[inline(never)]\n        #[allow(dead_code)]\n        pub fn get_led(&self, is_green: bool) -> bool {\n            let bit = if is_green { GREEN_LED_BIT } else { YELLOW_LED_BIT };\n\n            let value = self.led_states.load(Ordering::SeqCst);\n            (value & (1 << bit)) != 0\n        }\n\n        /// Increment counter and return new value\n        #[inline(never)]\n        pub fn increment_counter(&self) -> u32 {\n            let current = self.counter.load(Ordering::SeqCst);\n            let new_value = current.wrapping_add(1);\n            self.counter.store(new_value, Ordering::SeqCst);\n            new_value\n        }\n\n        /// Get current counter value\n        #[inline(never)]\n        #[allow(dead_code)]\n        pub fn get_counter(&self) -> u32 {\n            let value = self.counter.load(Ordering::SeqCst);\n            value\n        }\n    }\n\n    #[unsafe(link_section = \".ram_d3\")]\n    pub static SHARED_LED_STATE: SharedLedState = SharedLedState::new();\n\n    // Memory region constants for MPU configuration\n    pub const SRAM4_BASE_ADDRESS: u32 = 0x38000000;\n    pub const SRAM4_SIZE_LOG2: u32 = 15; // 64KB = 2^(15+1)\n    pub const SRAM4_REGION_NUMBER: u8 = 0;\n}\n\n#[unsafe(link_section = \".ram_d3\")]\nstatic SHARED_DATA: MaybeUninit<SharedData> = MaybeUninit::uninit();\n\n/// Configure MPU to make SRAM4 region non-cacheable\nfn configure_mpu_non_cacheable(mpu: &mut MPU) {\n    asm::dmb();\n    unsafe {\n        // Disable MPU\n        mpu.ctrl.write(0);\n\n        // Configure SRAM4 as non-cacheable\n        mpu.rnr.write(SRAM4_REGION_NUMBER as u32);\n\n        // Set base address with region number\n        mpu.rbar.write(SRAM4_BASE_ADDRESS | (1 << 4));\n\n        // Configure region attributes\n        let rasr_value: u32 = (SRAM4_SIZE_LOG2 << 1) | // SIZE=15 (64KB)\n            (1 << 0) |                                // ENABLE=1\n            (3 << 24) |                               // AP=3 (Full access)\n            (1 << 19) |                               // TEX=1\n            (1 << 18); // S=1 (Shareable)\n\n        mpu.rasr.write(rasr_value);\n\n        // Enable MPU with default memory map as background\n        mpu.ctrl.write(1 | (1 << 2)); // MPU_ENABLE | PRIVDEFENA\n    }\n\n    asm::dsb();\n    asm::isb();\n\n    info!(\"MPU configured - SRAM4 set as non-cacheable\");\n}\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) -> ! {\n    // Set up MPU and cache configuration\n    {\n        let mut cp = cortex_m::Peripherals::take().unwrap();\n        let scb = &mut cp.SCB;\n\n        // First disable caches\n        scb.disable_icache();\n        scb.disable_dcache(&mut cp.CPUID);\n\n        // Configure MPU\n        configure_mpu_non_cacheable(&mut cp.MPU);\n\n        // Re-enable caches\n        scb.enable_icache();\n        scb.enable_dcache(&mut cp.CPUID);\n        asm::dsb();\n        asm::isb();\n    }\n\n    // Configure the clock system\n    let mut config = Config::default();\n    {\n        use embassy_stm32::rcc::*;\n        config.rcc.hsi = Some(HSIPrescaler::DIV1);\n        config.rcc.csi = true;\n        config.rcc.hsi48 = Some(Default::default());\n        config.rcc.pll1 = Some(Pll {\n            source: PllSource::HSI,\n            prediv: PllPreDiv::DIV4,\n            mul: PllMul::MUL50,\n            divp: Some(PllDiv::DIV2),\n            divq: Some(PllDiv::DIV8),\n            divr: None,\n        });\n        config.rcc.sys = Sysclk::PLL1_P;\n        config.rcc.ahb_pre = AHBPrescaler::DIV2;\n        config.rcc.apb1_pre = APBPrescaler::DIV2;\n        config.rcc.apb2_pre = APBPrescaler::DIV2;\n        config.rcc.apb3_pre = APBPrescaler::DIV2;\n        config.rcc.apb4_pre = APBPrescaler::DIV2;\n        config.rcc.voltage_scale = VoltageScale::Scale1;\n        config.rcc.supply_config = SupplyConfig::DirectSMPS;\n    }\n\n    // Initialize the CM7 core\n    let _p = embassy_stm32::init_primary(config, &SHARED_DATA);\n    info!(\"CM7 core initialized with non-cacheable SRAM4!\");\n\n    // Verify shared memory is accessible\n    let magic = SHARED_LED_STATE.magic.load(core::sync::atomic::Ordering::SeqCst);\n    info!(\"CM7: Magic value = 0x{:X}\", magic);\n\n    // Initialize LED states\n    SHARED_LED_STATE.set_led(true, false); // Green LED off\n    SHARED_LED_STATE.set_led(false, false); // Yellow LED off\n\n    // Main loop - periodically toggle LED states\n    let mut green_state = false;\n    let mut yellow_state = false;\n    let mut loop_count = 0;\n\n    info!(\"CM7: Starting main loop\");\n    loop {\n        loop_count += 1;\n        let counter = SHARED_LED_STATE.increment_counter();\n\n        // Toggle green LED every second\n        if loop_count % 10 == 0 {\n            green_state = !green_state;\n            SHARED_LED_STATE.set_led(true, green_state);\n            info!(\"CM7: Counter = {}, Set green LED to {}\", counter, green_state);\n        }\n\n        // Toggle yellow LED every 3 seconds\n        if loop_count % 30 == 0 {\n            yellow_state = !yellow_state;\n            SHARED_LED_STATE.set_led(false, yellow_state);\n            info!(\"CM7: Counter = {}, Set yellow LED to {}\", counter, yellow_state);\n        }\n\n        Timer::after_millis(100).await;\n    }\n}\n"
  },
  {
    "path": "examples/stm32h7b0/.cargo/config.toml",
    "content": "[target.thumbv7em-none-eabihf]\nrunner = 'probe-rs run --chip STM32H7B0VBTx'\n\n[build]\ntarget = \"thumbv7em-none-eabihf\" # Cortex-M4F and Cortex-M7F (with FPU)\n\n[env]\nDEFMT_LOG = \"trace\"\n"
  },
  {
    "path": "examples/stm32h7b0/Cargo.toml",
    "content": "[package]\nedition = \"2024\"\nname = \"embassy-stm32h7b0-examples\"\nversion = \"0.1.0\"\nlicense = \"MIT OR Apache-2.0\"\npublish = false\n\n[dependencies]\nembassy-stm32 = { version = \"0.6.0\", path = \"../../embassy-stm32\", features = [\"defmt\", \"stm32h7b0vb\", \"time-driver-tim2\", \"exti\", \"memory-x\", \"unstable-pac\", \"chrono\"] }\nembassy-sync = { version = \"0.8.0\", path = \"../../embassy-sync\", features = [\"defmt\"] }\nembassy-embedded-hal = { version = \"0.6.0\", path = \"../../embassy-embedded-hal\" }\nembassy-executor = { version = \"0.10.0\", path = \"../../embassy-executor\", features = [\"platform-cortex-m\", \"executor-thread\", \"executor-interrupt\", \"defmt\"] }\nembassy-time = { version = \"0.5.1\", path = \"../../embassy-time\", features = [\"defmt\", \"defmt-timestamp-uptime\", \"tick-hz-32_768\"] }\nembassy-net = { version = \"0.9.0\", path = \"../../embassy-net\", features = [\"defmt\", \"tcp\", \"dhcpv4\", \"medium-ethernet\", \"proto-ipv6\", \"dns\"] }\nembassy-usb = { version = \"0.6.0\", path = \"../../embassy-usb\", features = [\"defmt\"] }\nembassy-futures = { version = \"0.1.2\", path = \"../../embassy-futures\" }\n\ndefmt = \"1.0.1\"\ndefmt-rtt = \"1.0.0\"\n\ncortex-m = { version = \"0.7.6\", features = [\"inline-asm\", \"critical-section-single-core\"] }\ncortex-m-rt = \"0.7.0\"\nembedded-hal = \"0.2.6\"\nembedded-hal-1 = { package = \"embedded-hal\", version = \"1.0\" }\nembedded-hal-async = { version = \"1.0\" }\nembedded-nal-async = \"0.9.0\"\nembedded-io-async = { version = \"0.7.0\" }\npanic-probe = { version = \"1.0.0\", features = [\"print-defmt\"] }\nheapless = { version = \"0.9\", default-features = false }\ncritical-section = \"1.1\"\nmicromath = \"2.0.0\"\nstm32-fmc = \"0.4.0\"\nembedded-storage = \"0.3.1\"\nstatic_cell = \"2\"\nchrono = { version = \"^0.4\", default-features = false }\ngrounded = \"0.2.0\"\n\n# cargo build/run\n[profile.dev]\ncodegen-units = 1\ndebug = 2\ndebug-assertions = true # <-\nincremental = false\nopt-level = 3 # <-\noverflow-checks = true # <-\n\n# cargo test\n[profile.test]\ncodegen-units = 1\ndebug = 2\ndebug-assertions = true # <-\nincremental = false\nopt-level = 3 # <-\noverflow-checks = true # <-\n\n# cargo build/run --release\n[profile.release]\ncodegen-units = 1\ndebug = 2\ndebug-assertions = false # <-\nincremental = false\nlto = 'fat'\nopt-level = 3 # <-\noverflow-checks = false # <-\n\n# cargo test --release\n[profile.bench]\ncodegen-units = 1\ndebug = 2\ndebug-assertions = false # <-\nincremental = false\nlto = 'fat'\nopt-level = 3 # <-\noverflow-checks = false # <-\n\n[package.metadata.embassy]\nbuild = [\n  { target = \"thumbv7em-none-eabi\", artifact-dir = \"out/examples/stm32h7b0\" }\n]\n"
  },
  {
    "path": "examples/stm32h7b0/build.rs",
    "content": "//! This build script copies the `memory.x` file from the crate root into\n//! a directory where the linker can always find it at build time.\n//! For many projects this is optional, as the linker always searches the\n//! project root directory -- wherever `Cargo.toml` is. However, if you\n//! are using a workspace or have a more complicated build setup, this\n//! build script becomes required. Additionally, by requesting that\n//! Cargo re-run the build script whenever `memory.x` is changed,\n//! updating `memory.x` ensures a rebuild of the application with the\n//! new memory settings.\n\nuse std::env;\nuse std::fs::File;\nuse std::io::Write;\nuse std::path::PathBuf;\n\nfn main() {\n    // Put `memory.x` in our output directory and ensure it's\n    // on the linker search path.\n    let out = &PathBuf::from(env::var_os(\"OUT_DIR\").unwrap());\n    File::create(out.join(\"memory.x\"))\n        .unwrap()\n        .write_all(include_bytes!(\"memory.x\"))\n        .unwrap();\n    println!(\"cargo:rustc-link-search={}\", out.display());\n\n    // By default, Cargo will re-run a build script whenever\n    // any file in the project changes. By specifying `memory.x`\n    // here, we ensure the build script is only re-run when\n    // `memory.x` is changed.\n    println!(\"cargo:rerun-if-changed=memory.x\");\n\n    println!(\"cargo:rustc-link-arg-bins=--nmagic\");\n    println!(\"cargo:rustc-link-arg-bins=-Tlink.x\");\n    println!(\"cargo:rustc-link-arg-bins=-Tdefmt.x\");\n}\n"
  },
  {
    "path": "examples/stm32h7b0/memory.x",
    "content": "MEMORY\n{\n    FLASH    : ORIGIN = 0x08000000, LENGTH = 128K /* BANK_1 */\n    RAM      : ORIGIN = 0x24000000, LENGTH = 512K  /* SRAM */\n}"
  },
  {
    "path": "examples/stm32h7b0/src/bin/ospi_memory_mapped.rs",
    "content": "#![no_main]\n#![no_std]\n\n// Tested on weact stm32h7b0 board + w25q64 spi flash\n\nuse defmt::info;\nuse embassy_executor::Spawner;\nuse embassy_stm32::Config;\nuse embassy_stm32::gpio::{Level, Output, Speed};\nuse embassy_stm32::mode::Blocking;\nuse embassy_stm32::ospi::{\n    AddressSize, ChipSelectHighTime, DummyCycles, FIFOThresholdLevel, Instance, MemorySize, MemoryType, Ospi,\n    OspiWidth, TransferConfig, WrapSize,\n};\nuse embassy_stm32::time::Hertz;\nuse embassy_time::Timer;\nuse {defmt_rtt as _, panic_probe as _};\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    // RCC config\n    let mut config = Config::default();\n    info!(\"START\");\n    {\n        use embassy_stm32::rcc::*;\n        config.rcc.hsi = Some(HSIPrescaler::DIV1);\n        config.rcc.csi = true;\n        // Needed for USB\n        config.rcc.hsi48 = Some(Hsi48Config { sync_from_usb: true });\n        // External oscillator 25MHZ\n        config.rcc.hse = Some(Hse {\n            freq: Hertz(25_000_000),\n            mode: HseMode::Oscillator,\n        });\n        config.rcc.pll1 = Some(Pll {\n            source: PllSource::HSE,\n            prediv: PllPreDiv::DIV5,\n            mul: PllMul::MUL112,\n            divp: Some(PllDiv::DIV2),\n            divq: Some(PllDiv::DIV2),\n            divr: Some(PllDiv::DIV2),\n        });\n        config.rcc.sys = Sysclk::PLL1_P;\n        config.rcc.ahb_pre = AHBPrescaler::DIV2;\n        config.rcc.apb1_pre = APBPrescaler::DIV2;\n        config.rcc.apb2_pre = APBPrescaler::DIV2;\n        config.rcc.apb3_pre = APBPrescaler::DIV2;\n        config.rcc.apb4_pre = APBPrescaler::DIV2;\n        config.rcc.voltage_scale = VoltageScale::Scale0;\n    }\n\n    // Initialize peripherals\n    let p = embassy_stm32::init(config);\n\n    let qspi_config = embassy_stm32::ospi::Config {\n        fifo_threshold: FIFOThresholdLevel::_16Bytes,\n        memory_type: MemoryType::Micron,\n        device_size: MemorySize::_8MiB,\n        chip_select_high_time: ChipSelectHighTime::_1Cycle,\n        free_running_clock: false,\n        clock_mode: false,\n        wrap_size: WrapSize::None,\n        clock_prescaler: 4,\n        sample_shifting: true,\n        delay_hold_quarter_cycle: false,\n        chip_select_boundary: 0,\n        delay_block_bypass: true,\n        max_transfer: 0,\n        refresh: 0,\n    };\n    let ospi = embassy_stm32::ospi::Ospi::new_blocking_quadspi(\n        p.OCTOSPI1,\n        p.PB2,\n        p.PD11,\n        p.PD12,\n        p.PE2,\n        p.PD13,\n        p.PB6,\n        qspi_config,\n    );\n\n    let mut flash = FlashMemory::new(ospi).await;\n\n    let flash_id = flash.read_id();\n    info!(\"FLASH ID: {=[u8]:x}\", flash_id);\n    let mut wr_buf = [0u8; 8];\n    for i in 0..8 {\n        wr_buf[i] = i as u8;\n    }\n    let mut rd_buf = [0u8; 8];\n    flash.erase_sector(0).await;\n    flash.write_memory(0, &wr_buf, true).await;\n    flash.read_memory(0, &mut rd_buf, true);\n    info!(\"WRITE BUF: {=[u8]:#X}\", wr_buf);\n    info!(\"READ BUF: {=[u8]:#X}\", rd_buf);\n    flash.enable_mm().await;\n    info!(\"Enabled memory mapped mode\");\n\n    let first_u32 = unsafe { *(0x90000000 as *const u32) };\n    assert_eq!(first_u32, 0x03020100);\n\n    let second_u32 = unsafe { *(0x90000004 as *const u32) };\n    assert_eq!(second_u32, 0x07060504);\n    flash.disable_mm().await;\n\n    info!(\"DONE\");\n    // Output pin PE3\n    let mut led = Output::new(p.PE3, Level::Low, Speed::Low);\n\n    loop {\n        led.toggle();\n        Timer::after_millis(1000).await;\n    }\n}\n\nconst MEMORY_PAGE_SIZE: usize = 8;\n\nconst CMD_QUAD_READ: u8 = 0x6B;\n\nconst CMD_QUAD_WRITE_PG: u8 = 0x32;\n\nconst CMD_READ_ID: u8 = 0x9F;\n\nconst CMD_ENABLE_RESET: u8 = 0x66;\nconst CMD_RESET: u8 = 0x99;\n\nconst CMD_WRITE_ENABLE: u8 = 0x06;\n\nconst CMD_CHIP_ERASE: u8 = 0xC7;\nconst CMD_SECTOR_ERASE: u8 = 0x20;\nconst CMD_BLOCK_ERASE_32K: u8 = 0x52;\nconst CMD_BLOCK_ERASE_64K: u8 = 0xD8;\n\nconst CMD_READ_SR: u8 = 0x05;\nconst CMD_READ_CR: u8 = 0x35;\n\nconst CMD_WRITE_SR: u8 = 0x01;\nconst CMD_WRITE_CR: u8 = 0x31;\n\n/// Implementation of access to flash chip.\n/// Chip commands are hardcoded as it depends on used chip.\n/// This implementation is using chip GD25Q64C from Giga Device\npub struct FlashMemory<I: Instance> {\n    ospi: Ospi<'static, I, Blocking>,\n}\n\nimpl<I: Instance> FlashMemory<I> {\n    pub async fn new(ospi: Ospi<'static, I, Blocking>) -> Self {\n        let mut memory = Self { ospi };\n\n        memory.reset_memory().await;\n        memory.enable_quad();\n        memory\n    }\n\n    async fn qpi_mode(&mut self) {\n        // Enter qpi mode\n        self.exec_command(0x38).await;\n\n        // Set read param\n        let transaction = TransferConfig {\n            iwidth: OspiWidth::QUAD,\n            dwidth: OspiWidth::QUAD,\n            instruction: Some(0xC0),\n            ..Default::default()\n        };\n        self.enable_write().await;\n        self.ospi.blocking_write(&[0x30_u8], transaction).unwrap();\n        self.wait_write_finish();\n    }\n\n    pub async fn disable_mm(&mut self) {\n        self.ospi.disable_memory_mapped_mode();\n    }\n\n    pub async fn enable_mm(&mut self) {\n        self.qpi_mode().await;\n\n        let read_config = TransferConfig {\n            iwidth: OspiWidth::QUAD,\n            isize: AddressSize::_8Bit,\n            adwidth: OspiWidth::QUAD,\n            adsize: AddressSize::_24bit,\n            dwidth: OspiWidth::QUAD,\n            instruction: Some(0x0B), // Fast read in QPI mode\n            dummy: DummyCycles::_8,\n            ..Default::default()\n        };\n\n        let write_config = TransferConfig {\n            iwidth: OspiWidth::SING,\n            isize: AddressSize::_8Bit,\n            adwidth: OspiWidth::SING,\n            adsize: AddressSize::_24bit,\n            dwidth: OspiWidth::QUAD,\n            instruction: Some(0x32), // Write config\n            dummy: DummyCycles::_0,\n            ..Default::default()\n        };\n        self.ospi.enable_memory_mapped_mode(read_config, write_config).unwrap();\n    }\n\n    fn enable_quad(&mut self) {\n        let cr = self.read_cr();\n        // info!(\"Read cr: {:x}\", cr);\n        self.write_cr(cr | 0x02);\n        // info!(\"Read cr after writing: {:x}\", cr);\n    }\n\n    pub fn disable_quad(&mut self) {\n        let cr = self.read_cr();\n        self.write_cr(cr & (!(0x02)));\n    }\n\n    async fn exec_command_4(&mut self, cmd: u8) {\n        let transaction = TransferConfig {\n            iwidth: OspiWidth::QUAD,\n            adwidth: OspiWidth::NONE,\n            // adsize: AddressSize::_24bit,\n            dwidth: OspiWidth::NONE,\n            instruction: Some(cmd as u32),\n            address: None,\n            dummy: DummyCycles::_0,\n            ..Default::default()\n        };\n        self.ospi.blocking_command(&transaction).unwrap();\n    }\n\n    async fn exec_command(&mut self, cmd: u8) {\n        let transaction = TransferConfig {\n            iwidth: OspiWidth::SING,\n            adwidth: OspiWidth::NONE,\n            // adsize: AddressSize::_24bit,\n            dwidth: OspiWidth::NONE,\n            instruction: Some(cmd as u32),\n            address: None,\n            dummy: DummyCycles::_0,\n            ..Default::default()\n        };\n        // info!(\"Excuting command: {:x}\", transaction.instruction);\n        self.ospi.blocking_command(&transaction).unwrap();\n    }\n\n    pub async fn reset_memory(&mut self) {\n        self.exec_command_4(CMD_ENABLE_RESET).await;\n        self.exec_command_4(CMD_RESET).await;\n        self.exec_command(CMD_ENABLE_RESET).await;\n        self.exec_command(CMD_RESET).await;\n        self.wait_write_finish();\n    }\n\n    pub async fn enable_write(&mut self) {\n        self.exec_command(CMD_WRITE_ENABLE).await;\n    }\n\n    pub fn read_id(&mut self) -> [u8; 3] {\n        let mut buffer = [0; 3];\n        let transaction: TransferConfig = TransferConfig {\n            iwidth: OspiWidth::SING,\n            isize: AddressSize::_8Bit,\n            adwidth: OspiWidth::NONE,\n            // adsize: AddressSize::_24bit,\n            dwidth: OspiWidth::SING,\n            instruction: Some(CMD_READ_ID as u32),\n            ..Default::default()\n        };\n        // info!(\"Reading id: 0x{:X}\", transaction.instruction);\n        self.ospi.blocking_read(&mut buffer, transaction).unwrap();\n        buffer\n    }\n\n    pub fn read_id_4(&mut self) -> [u8; 3] {\n        let mut buffer = [0; 3];\n        let transaction: TransferConfig = TransferConfig {\n            iwidth: OspiWidth::SING,\n            isize: AddressSize::_8Bit,\n            adwidth: OspiWidth::NONE,\n            dwidth: OspiWidth::QUAD,\n            instruction: Some(CMD_READ_ID as u32),\n            ..Default::default()\n        };\n        info!(\"Reading id: 0x{:X}\", transaction.instruction);\n        self.ospi.blocking_read(&mut buffer, transaction).unwrap();\n        buffer\n    }\n\n    pub fn read_memory(&mut self, addr: u32, buffer: &mut [u8], use_dma: bool) {\n        let transaction = TransferConfig {\n            iwidth: OspiWidth::SING,\n            adwidth: OspiWidth::SING,\n            adsize: AddressSize::_24bit,\n            dwidth: OspiWidth::QUAD,\n            instruction: Some(CMD_QUAD_READ as u32),\n            address: Some(addr),\n            dummy: DummyCycles::_8,\n            ..Default::default()\n        };\n        if use_dma {\n            self.ospi.blocking_read(buffer, transaction).unwrap();\n        } else {\n            self.ospi.blocking_read(buffer, transaction).unwrap();\n        }\n    }\n\n    fn wait_write_finish(&mut self) {\n        while (self.read_sr() & 0x01) != 0 {}\n    }\n\n    async fn perform_erase(&mut self, addr: u32, cmd: u8) {\n        let transaction = TransferConfig {\n            iwidth: OspiWidth::SING,\n            adwidth: OspiWidth::SING,\n            adsize: AddressSize::_24bit,\n            dwidth: OspiWidth::NONE,\n            instruction: Some(cmd as u32),\n            address: Some(addr),\n            dummy: DummyCycles::_0,\n            ..Default::default()\n        };\n        self.enable_write().await;\n        self.ospi.blocking_command(&transaction).unwrap();\n        self.wait_write_finish();\n    }\n\n    pub async fn erase_sector(&mut self, addr: u32) {\n        self.perform_erase(addr, CMD_SECTOR_ERASE).await;\n    }\n\n    pub async fn erase_block_32k(&mut self, addr: u32) {\n        self.perform_erase(addr, CMD_BLOCK_ERASE_32K).await;\n    }\n\n    pub async fn erase_block_64k(&mut self, addr: u32) {\n        self.perform_erase(addr, CMD_BLOCK_ERASE_64K).await;\n    }\n\n    pub async fn erase_chip(&mut self) {\n        self.exec_command(CMD_CHIP_ERASE).await;\n    }\n\n    async fn write_page(&mut self, addr: u32, buffer: &[u8], len: usize, use_dma: bool) {\n        assert!(\n            (len as u32 + (addr & 0x000000ff)) <= MEMORY_PAGE_SIZE as u32,\n            \"write_page(): page write length exceeds page boundary (len = {}, addr = {:X}\",\n            len,\n            addr\n        );\n\n        let transaction = TransferConfig {\n            iwidth: OspiWidth::SING,\n            adsize: AddressSize::_24bit,\n            adwidth: OspiWidth::SING,\n            dwidth: OspiWidth::QUAD,\n            instruction: Some(CMD_QUAD_WRITE_PG as u32),\n            address: Some(addr),\n            dummy: DummyCycles::_0,\n            ..Default::default()\n        };\n        self.enable_write().await;\n        if use_dma {\n            self.ospi.blocking_write(buffer, transaction).unwrap();\n        } else {\n            self.ospi.blocking_write(buffer, transaction).unwrap();\n        }\n        self.wait_write_finish();\n    }\n\n    pub async fn write_memory(&mut self, addr: u32, buffer: &[u8], use_dma: bool) {\n        let mut left = buffer.len();\n        let mut place = addr;\n        let mut chunk_start = 0;\n\n        while left > 0 {\n            let max_chunk_size = MEMORY_PAGE_SIZE - (place & 0x000000ff) as usize;\n            let chunk_size = if left >= max_chunk_size { max_chunk_size } else { left };\n            let chunk = &buffer[chunk_start..(chunk_start + chunk_size)];\n            self.write_page(place, chunk, chunk_size, use_dma).await;\n            place += chunk_size as u32;\n            left -= chunk_size;\n            chunk_start += chunk_size;\n        }\n    }\n\n    fn read_register(&mut self, cmd: u8) -> u8 {\n        let mut buffer = [0; 1];\n        let transaction: TransferConfig = TransferConfig {\n            iwidth: OspiWidth::SING,\n            isize: AddressSize::_8Bit,\n            adwidth: OspiWidth::NONE,\n            adsize: AddressSize::_24bit,\n            dwidth: OspiWidth::SING,\n            instruction: Some(cmd as u32),\n            address: None,\n            dummy: DummyCycles::_0,\n            ..Default::default()\n        };\n        self.ospi.blocking_read(&mut buffer, transaction).unwrap();\n        // info!(\"Read w25q64 register: 0x{:x}\", buffer[0]);\n        buffer[0]\n    }\n\n    fn write_register(&mut self, cmd: u8, value: u8) {\n        let buffer = [value; 1];\n        let transaction: TransferConfig = TransferConfig {\n            iwidth: OspiWidth::SING,\n            isize: AddressSize::_8Bit,\n            instruction: Some(cmd as u32),\n            adsize: AddressSize::_24bit,\n            adwidth: OspiWidth::NONE,\n            dwidth: OspiWidth::SING,\n            address: None,\n            dummy: DummyCycles::_0,\n            ..Default::default()\n        };\n        self.ospi.blocking_write(&buffer, transaction).unwrap();\n    }\n\n    pub fn read_sr(&mut self) -> u8 {\n        self.read_register(CMD_READ_SR)\n    }\n\n    pub fn read_cr(&mut self) -> u8 {\n        self.read_register(CMD_READ_CR)\n    }\n\n    pub fn write_sr(&mut self, value: u8) {\n        self.write_register(CMD_WRITE_SR, value);\n    }\n\n    pub fn write_cr(&mut self, value: u8) {\n        self.write_register(CMD_WRITE_CR, value);\n    }\n}\n"
  },
  {
    "path": "examples/stm32h7rs/.cargo/config.toml",
    "content": "[target.thumbv7em-none-eabihf]\nrunner = 'probe-rs run --chip STM32H7S3L8Hx'\n\n[build]\ntarget = \"thumbv7em-none-eabihf\" # Cortex-M4F and Cortex-M7F (with FPU)\n\n[env]\nDEFMT_LOG = \"trace\"\n"
  },
  {
    "path": "examples/stm32h7rs/Cargo.toml",
    "content": "[package]\nedition = \"2024\"\nname = \"embassy-stm32h7rs-examples\"\nversion = \"0.1.0\"\nlicense = \"MIT OR Apache-2.0\"\npublish = false\n\n[dependencies]\n# Change stm32h743bi to your chip name, if necessary.\nembassy-stm32 = { version = \"0.6.0\", path = \"../../embassy-stm32\", features = [\"defmt\", \"stm32h7s3l8\", \"time-driver-tim2\", \"exti\", \"memory-x\", \"unstable-pac\", \"chrono\"] }\nembassy-sync = { version = \"0.8.0\", path = \"../../embassy-sync\", features = [\"defmt\"] }\nembassy-executor = { version = \"0.10.0\", path = \"../../embassy-executor\", features = [\"platform-cortex-m\", \"executor-thread\", \"executor-interrupt\", \"defmt\"] }\nembassy-time = { version = \"0.5.1\", path = \"../../embassy-time\", features = [\"defmt\", \"defmt-timestamp-uptime\", \"tick-hz-32_768\"] }\nembassy-net = { version = \"0.9.0\", path = \"../../embassy-net\", features = [\"defmt\", \"udp\", \"medium-ethernet\", \"medium-ip\", \"proto-ipv4\"] }\nembassy-usb = { version = \"0.6.0\", path = \"../../embassy-usb\", features = [\"defmt\"] }\nembassy-futures = { version = \"0.1.2\", path = \"../../embassy-futures\" }\n\ndefmt = \"1.0.1\"\ndefmt-rtt = \"1.0.0\"\n\ncortex-m = { version = \"0.7.6\", features = [\"inline-asm\", \"critical-section-single-core\"] }\ncortex-m-rt = \"0.7.0\"\nembedded-hal = \"0.2.6\"\nembedded-hal-1 = { package = \"embedded-hal\", version = \"1.0\" }\nembedded-hal-async = { version = \"1.0\" }\nembedded-nal-async = \"0.9.0\"\nembedded-io-async = { version = \"0.7.0\" }\npanic-probe = { version = \"1.0.0\", features = [\"print-defmt\"] }\nheapless = { version = \"0.9\", default-features = false }\ncritical-section = \"1.1\"\nmicromath = \"2.0.0\"\nstm32-fmc = \"0.4.0\"\nembedded-storage = \"0.3.1\"\nstatic_cell = \"2\"\nchrono = { version = \"^0.4\", default-features = false }\n\n# cargo build/run\n[profile.dev]\ncodegen-units = 1\ndebug = 2\ndebug-assertions = true # <-\nincremental = false\nopt-level = 3 # <-\noverflow-checks = true # <-\n\n# cargo test\n[profile.test]\ncodegen-units = 1\ndebug = 2\ndebug-assertions = true # <-\nincremental = false\nopt-level = 3 # <-\noverflow-checks = true # <-\n\n# cargo build/run --release\n[profile.release]\ncodegen-units = 1\ndebug = 2\ndebug-assertions = false # <-\nincremental = false\nlto = 'fat'\nopt-level = 3 # <-\noverflow-checks = false # <-\n\n# cargo test --release\n[profile.bench]\ncodegen-units = 1\ndebug = 2\ndebug-assertions = false # <-\nincremental = false\nlto = 'fat'\nopt-level = 3 # <-\noverflow-checks = false # <-\n\n[package.metadata.embassy]\nbuild = [\n  { target = \"thumbv7em-none-eabi\", artifact-dir = \"out/examples/stm32h7rs\" }\n]\n"
  },
  {
    "path": "examples/stm32h7rs/build.rs",
    "content": "fn main() {\n    println!(\"cargo:rustc-link-arg-bins=--nmagic\");\n    println!(\"cargo:rustc-link-arg-bins=-Tlink.x\");\n    println!(\"cargo:rustc-link-arg-bins=-Tdefmt.x\");\n}\n"
  },
  {
    "path": "examples/stm32h7rs/src/bin/blinky.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_stm32::Config;\nuse embassy_stm32::gpio::{Level, Output, Speed};\nuse embassy_stm32::time::Hertz;\nuse embassy_time::Timer;\nuse {defmt_rtt as _, panic_probe as _};\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let mut config = Config::default();\n    {\n        use embassy_stm32::rcc::*;\n        config.rcc.hse = Some(Hse {\n            freq: Hertz(24_000_000),\n            mode: HseMode::Oscillator,\n        });\n        config.rcc.pll1 = Some(Pll {\n            source: PllSource::HSE,\n            prediv: PllPreDiv::DIV3,\n            mul: PllMul::MUL150,\n            divp: Some(PllDiv::DIV2),\n            divq: None,\n            divr: None,\n            divs: None,\n            divt: None,\n        });\n        config.rcc.sys = Sysclk::PLL1_P; // 600 Mhz\n        config.rcc.ahb_pre = AHBPrescaler::DIV2; // 300 Mhz\n        config.rcc.apb1_pre = APBPrescaler::DIV2; // 150 Mhz\n        config.rcc.apb2_pre = APBPrescaler::DIV2; // 150 Mhz\n        config.rcc.apb4_pre = APBPrescaler::DIV2; // 150 Mhz\n        config.rcc.apb5_pre = APBPrescaler::DIV2; // 150 Mhz\n        config.rcc.voltage_scale = VoltageScale::HIGH;\n    }\n    let p = embassy_stm32::init(config);\n    info!(\"Hello World!\");\n\n    let mut led = Output::new(p.PD10, Level::High, Speed::Low);\n\n    loop {\n        info!(\"high\");\n        led.set_high();\n        Timer::after_millis(500).await;\n\n        info!(\"low\");\n        led.set_low();\n        Timer::after_millis(500).await;\n    }\n}\n"
  },
  {
    "path": "examples/stm32h7rs/src/bin/button_exti.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_stm32::exti::{self, ExtiInput};\nuse embassy_stm32::gpio::Pull;\nuse embassy_stm32::{bind_interrupts, interrupt};\nuse {defmt_rtt as _, panic_probe as _};\n\nbind_interrupts!(\n    pub struct Irqs{\n        EXTI13 => exti::InterruptHandler<interrupt::typelevel::EXTI13>;\n});\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let p = embassy_stm32::init(Default::default());\n    info!(\"Hello World!\");\n\n    let mut button = ExtiInput::new(p.PC13, p.EXTI13, Pull::Up, Irqs);\n\n    info!(\"Press the USER button...\");\n\n    loop {\n        button.wait_for_falling_edge().await;\n        info!(\"Pressed!\");\n        button.wait_for_rising_edge().await;\n        info!(\"Released!\");\n    }\n}\n"
  },
  {
    "path": "examples/stm32h7rs/src/bin/can.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_stm32::peripherals::*;\nuse embassy_stm32::{Config, bind_interrupts, can, rcc};\nuse embassy_time::Timer;\nuse {defmt_rtt as _, panic_probe as _};\n\nbind_interrupts!(struct Irqs {\n    FDCAN1_IT0 => can::IT0InterruptHandler<FDCAN1>;\n    FDCAN1_IT1 => can::IT1InterruptHandler<FDCAN1>;\n});\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let mut config = Config::default();\n    config.rcc.hse = Some(rcc::Hse {\n        freq: embassy_stm32::time::Hertz(25_000_000),\n        mode: rcc::HseMode::Oscillator,\n    });\n    config.rcc.mux.fdcansel = rcc::mux::Fdcansel::HSE;\n\n    let peripherals = embassy_stm32::init(config);\n\n    let mut can = can::CanConfigurator::new(peripherals.FDCAN1, peripherals.PA11, peripherals.PA12, Irqs);\n\n    // 250k bps\n    can.set_bitrate(250_000);\n\n    //let mut can = can.into_internal_loopback_mode();\n    let mut can = can.into_normal_mode();\n\n    info!(\"CAN Configured\");\n\n    let mut i = 0;\n    let mut last_read_ts = embassy_time::Instant::now();\n\n    loop {\n        let frame = can::frame::Frame::new_extended(0x123456F, &[i; 8]).unwrap();\n        info!(\"Writing frame\");\n        _ = can.write(&frame).await;\n\n        match can.read().await {\n            Ok(envelope) => {\n                let (rx_frame, ts) = envelope.parts();\n                let delta = (ts - last_read_ts).as_millis();\n                last_read_ts = ts;\n                info!(\n                    \"Rx: {:x} {:x} {:x} {:x} --- NEW {}\",\n                    rx_frame.data()[0],\n                    rx_frame.data()[1],\n                    rx_frame.data()[2],\n                    rx_frame.data()[3],\n                    delta,\n                )\n            }\n            Err(_err) => error!(\"Error in frame\"),\n        }\n\n        Timer::after_millis(250).await;\n\n        i += 1;\n        if i > 3 {\n            break;\n        }\n    }\n\n    let (mut tx, mut rx, _props) = can.split();\n    // With split\n    loop {\n        let frame = can::frame::Frame::new_extended(0x123456F, &[i; 8]).unwrap();\n        info!(\"Writing frame\");\n        _ = tx.write(&frame).await;\n\n        match rx.read().await {\n            Ok(envelope) => {\n                let (rx_frame, ts) = envelope.parts();\n                let delta = (ts - last_read_ts).as_millis();\n                last_read_ts = ts;\n                info!(\n                    \"Rx: {:x} {:x} {:x} {:x} --- NEW {}\",\n                    rx_frame.data()[0],\n                    rx_frame.data()[1],\n                    rx_frame.data()[2],\n                    rx_frame.data()[3],\n                    delta,\n                )\n            }\n            Err(_err) => error!(\"Error in frame\"),\n        }\n\n        Timer::after_millis(250).await;\n\n        i = i.wrapping_add(1);\n    }\n}\n"
  },
  {
    "path": "examples/stm32h7rs/src/bin/eth.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_net::udp::{PacketMetadata, UdpSocket};\nuse embassy_net::{Ipv4Address, Ipv4Cidr, StackResources};\nuse embassy_stm32::eth::{Ethernet, GenericPhy, PacketQueue, Sma};\nuse embassy_stm32::peripherals::{ETH, ETH_SMA};\nuse embassy_stm32::rng::Rng;\nuse embassy_stm32::{Config, bind_interrupts, eth, peripherals, rng};\nuse embassy_time::Timer;\nuse heapless::Vec;\nuse static_cell::StaticCell;\nuse {defmt_rtt as _, panic_probe as _};\n\nbind_interrupts!(struct Irqs {\n    ETH => eth::InterruptHandler;\n    RNG => rng::InterruptHandler<peripherals::RNG>;\n});\n\ntype Device = Ethernet<'static, ETH, GenericPhy<Sma<'static, ETH_SMA>>>;\n\n#[embassy_executor::task]\nasync fn net_task(mut runner: embassy_net::Runner<'static, Device>) -> ! {\n    runner.run().await\n}\n\n#[embassy_executor::main]\nasync fn main(spawner: Spawner) -> ! {\n    let mut config = Config::default();\n    {\n        use embassy_stm32::rcc::*;\n        config.rcc.hsi = Some(HSIPrescaler::DIV1);\n        config.rcc.csi = true;\n        config.rcc.hsi48 = Some(Default::default()); // needed for RNG\n        config.rcc.pll1 = Some(Pll {\n            source: PllSource::HSI,\n            prediv: PllPreDiv::DIV4,\n            mul: PllMul::MUL50,\n            divp: Some(PllDiv::DIV2),\n            divq: None,\n            divr: None,\n            divs: None,\n            divt: None,\n        });\n        config.rcc.sys = Sysclk::PLL1_P; // 400 Mhz\n        config.rcc.ahb_pre = AHBPrescaler::DIV2; // 200 Mhz\n        config.rcc.apb1_pre = APBPrescaler::DIV2; // 100 Mhz\n        config.rcc.apb2_pre = APBPrescaler::DIV2; // 100 Mhz\n        config.rcc.apb4_pre = APBPrescaler::DIV2; // 100 Mhz\n        config.rcc.apb5_pre = APBPrescaler::DIV2; // 100 Mhz\n        config.rcc.voltage_scale = VoltageScale::HIGH;\n    }\n    let p = embassy_stm32::init(config);\n    info!(\"Hello World!\");\n\n    // Generate random seed.\n    let mut rng = Rng::new(p.RNG, Irqs);\n    let mut seed = [0; 8];\n    rng.fill_bytes(&mut seed);\n    let seed = u64::from_le_bytes(seed);\n\n    let mac_addr = [0x00, 0x00, 0xDE, 0xAD, 0xBE, 0xEF];\n\n    static PACKETS: StaticCell<PacketQueue<4, 4>> = StaticCell::new();\n    let device = Ethernet::new(\n        PACKETS.init(PacketQueue::<4, 4>::new()),\n        p.ETH,\n        Irqs,\n        p.PB6,\n        p.PA7,\n        p.PG4,\n        p.PG5,\n        p.PG13,\n        p.PG12,\n        p.PG11,\n        mac_addr,\n        p.ETH_SMA,\n        p.PA2,\n        p.PG6,\n    );\n\n    // Have to use UDP w/ static config to fit in internal flash\n    // let config = embassy_net::Config::dhcpv4(Default::default());\n    let config = embassy_net::Config::ipv4_static(embassy_net::StaticConfigV4 {\n        address: Ipv4Cidr::new(Ipv4Address::new(10, 42, 0, 61), 24),\n        dns_servers: Vec::new(),\n        gateway: Some(Ipv4Address::new(10, 42, 0, 1)),\n    });\n\n    // Init network stack\n    static RESOURCES: StaticCell<StackResources<3>> = StaticCell::new();\n    let (stack, runner) = embassy_net::new(device, config, RESOURCES.init(StackResources::new()), seed);\n\n    // Launch network task\n    spawner.spawn(unwrap!(net_task(runner)));\n\n    // Ensure DHCP configuration is up before trying connect\n    //stack.wait_config_up().await;\n\n    info!(\"Network task initialized\");\n\n    // Then we can use it!\n    let mut rx_meta = [PacketMetadata::EMPTY; 16];\n    let mut rx_buffer = [0; 1024];\n    let mut tx_meta = [PacketMetadata::EMPTY; 16];\n    let mut tx_buffer = [0; 1024];\n\n    let remote_endpoint = (Ipv4Address::new(10, 42, 0, 1), 8000);\n    let socket = UdpSocket::new(stack, &mut rx_meta, &mut rx_buffer, &mut tx_meta, &mut tx_buffer);\n    loop {\n        // You need to start a server on the host machine, for example: `nc -lu 8000`\n        socket\n            .send_to(b\"Hello, world\", remote_endpoint)\n            .await\n            .expect(\"Buffer sent\");\n        Timer::after_secs(1).await;\n    }\n}\n"
  },
  {
    "path": "examples/stm32h7rs/src/bin/i2c.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_stm32::i2c::{Error, I2c};\nuse embassy_stm32::{bind_interrupts, dma, i2c, peripherals};\nuse {defmt_rtt as _, panic_probe as _};\n\nconst ADDRESS: u8 = 0x5F;\nconst WHOAMI: u8 = 0x0F;\n\nbind_interrupts!(struct Irqs {\n    I2C2_EV => i2c::EventInterruptHandler<peripherals::I2C2>;\n    I2C2_ER => i2c::ErrorInterruptHandler<peripherals::I2C2>;\n    GPDMA1_CHANNEL4 => dma::InterruptHandler<peripherals::GPDMA1_CH4>;\n    GPDMA1_CHANNEL5 => dma::InterruptHandler<peripherals::GPDMA1_CH5>;\n});\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    info!(\"Hello world!\");\n    let p = embassy_stm32::init(Default::default());\n\n    let mut i2c = I2c::new(\n        p.I2C2,\n        p.PB10,\n        p.PB11,\n        p.GPDMA1_CH4,\n        p.GPDMA1_CH5,\n        Irqs,\n        Default::default(),\n    );\n\n    let mut data = [0u8; 1];\n\n    match i2c.blocking_write_read(ADDRESS, &[WHOAMI], &mut data) {\n        Ok(()) => info!(\"Whoami: {}\", data[0]),\n        Err(Error::Timeout) => error!(\"Operation timed out\"),\n        Err(e) => error!(\"I2c Error: {:?}\", e),\n    }\n}\n"
  },
  {
    "path": "examples/stm32h7rs/src/bin/mco.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_stm32::gpio::{Level, Output, Speed};\nuse embassy_stm32::rcc::{Mco, Mco1Source, McoConfig, McoPrescaler};\nuse embassy_time::Timer;\nuse {defmt_rtt as _, panic_probe as _};\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let p = embassy_stm32::init(Default::default());\n    info!(\"Hello World!\");\n\n    let mut led = Output::new(p.PB14, Level::High, Speed::Low);\n\n    let config = {\n        let mut config = McoConfig::default();\n        config.prescaler = McoPrescaler::DIV8;\n        config\n    };\n\n    let _mco = Mco::new(p.MCO1, p.PA8, Mco1Source::HSI, config);\n\n    loop {\n        info!(\"high\");\n        led.set_high();\n        Timer::after_millis(500).await;\n\n        info!(\"low\");\n        led.set_low();\n        Timer::after_millis(500).await;\n    }\n}\n"
  },
  {
    "path": "examples/stm32h7rs/src/bin/multiprio.rs",
    "content": "//! This example showcases how to create multiple Executor instances to run tasks at\n//! different priority levels.\n//!\n//! Low priority executor runs in thread mode (not interrupt), and uses `sev` for signaling\n//! there's work in the queue, and `wfe` for waiting for work.\n//!\n//! Medium and high priority executors run in two interrupts with different priorities.\n//! Signaling work is done by pending the interrupt. No \"waiting\" needs to be done explicitly, since\n//! when there's work the interrupt will trigger and run the executor.\n//!\n//! Sample output below. Note that high priority ticks can interrupt everything else, and\n//! medium priority computations can interrupt low priority computations, making them to appear\n//! to take significantly longer time.\n//!\n//! ```not_rust\n//!     [med] Starting long computation\n//!     [med] done in 992 ms\n//!         [high] tick!\n//! [low] Starting long computation\n//!     [med] Starting long computation\n//!         [high] tick!\n//!         [high] tick!\n//!     [med] done in 993 ms\n//!     [med] Starting long computation\n//!         [high] tick!\n//!         [high] tick!\n//!     [med] done in 993 ms\n//! [low] done in 3972 ms\n//!     [med] Starting long computation\n//!         [high] tick!\n//!         [high] tick!\n//!     [med] done in 993 ms\n//! ```\n//!\n//! For comparison, try changing the code so all 3 tasks get spawned on the low priority executor.\n//! You will get an output like the following. Note that no computation is ever interrupted.\n//!\n//! ```not_rust\n//!         [high] tick!\n//!     [med] Starting long computation\n//!     [med] done in 496 ms\n//! [low] Starting long computation\n//! [low] done in 992 ms\n//!     [med] Starting long computation\n//!     [med] done in 496 ms\n//!         [high] tick!\n//! [low] Starting long computation\n//! [low] done in 992 ms\n//!         [high] tick!\n//!     [med] Starting long computation\n//!     [med] done in 496 ms\n//!         [high] tick!\n//! ```\n//!\n\n#![no_std]\n#![no_main]\n\nuse cortex_m_rt::entry;\nuse defmt::*;\nuse embassy_executor::{Executor, InterruptExecutor};\nuse embassy_stm32::interrupt;\nuse embassy_stm32::interrupt::{InterruptExt, Priority};\nuse embassy_time::{Instant, Timer};\nuse static_cell::StaticCell;\nuse {defmt_rtt as _, panic_probe as _};\n\n#[embassy_executor::task]\nasync fn run_high() {\n    loop {\n        info!(\"        [high] tick!\");\n        Timer::after_ticks(27374).await;\n    }\n}\n\n#[embassy_executor::task]\nasync fn run_med() {\n    loop {\n        let start = Instant::now();\n        info!(\"    [med] Starting long computation\");\n\n        // Spin-wait to simulate a long CPU computation\n        embassy_time::block_for(embassy_time::Duration::from_secs(1)); // ~1 second\n\n        let end = Instant::now();\n        let ms = end.duration_since(start).as_ticks() / 33;\n        info!(\"    [med] done in {} ms\", ms);\n\n        Timer::after_ticks(23421).await;\n    }\n}\n\n#[embassy_executor::task]\nasync fn run_low() {\n    loop {\n        let start = Instant::now();\n        info!(\"[low] Starting long computation\");\n\n        // Spin-wait to simulate a long CPU computation\n        embassy_time::block_for(embassy_time::Duration::from_secs(2)); // ~2 seconds\n\n        let end = Instant::now();\n        let ms = end.duration_since(start).as_ticks() / 33;\n        info!(\"[low] done in {} ms\", ms);\n\n        Timer::after_ticks(32983).await;\n    }\n}\n\nstatic EXECUTOR_HIGH: InterruptExecutor = InterruptExecutor::new();\nstatic EXECUTOR_MED: InterruptExecutor = InterruptExecutor::new();\nstatic EXECUTOR_LOW: StaticCell<Executor> = StaticCell::new();\n\n#[interrupt]\nunsafe fn UART4() {\n    unsafe { EXECUTOR_HIGH.on_interrupt() }\n}\n\n#[interrupt]\nunsafe fn UART5() {\n    unsafe { EXECUTOR_MED.on_interrupt() }\n}\n\n#[entry]\nfn main() -> ! {\n    info!(\"Hello World!\");\n\n    let _p = embassy_stm32::init(Default::default());\n\n    // STM32s don’t have any interrupts exclusively for software use, but they can all be triggered by software as well as\n    // by the peripheral, so we can just use any free interrupt vectors which aren’t used by the rest of your application.\n    // In this case we’re using UART4 and UART5, but there’s nothing special about them. Any otherwise unused interrupt\n    // vector would work exactly the same.\n\n    // High-priority executor: UART4, priority level 6\n    interrupt::UART4.set_priority(Priority::P6);\n    let spawner = EXECUTOR_HIGH.start(interrupt::UART4);\n    spawner.spawn(unwrap!(run_high()));\n\n    // Medium-priority executor: UART5, priority level 7\n    interrupt::UART5.set_priority(Priority::P7);\n    let spawner = EXECUTOR_MED.start(interrupt::UART5);\n    spawner.spawn(unwrap!(run_med()));\n\n    // Low priority executor: runs in thread mode, using WFE/SEV\n    let executor = EXECUTOR_LOW.init(Executor::new());\n    executor.run(|spawner| {\n        spawner.spawn(unwrap!(run_low()));\n    });\n}\n"
  },
  {
    "path": "examples/stm32h7rs/src/bin/rng.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_stm32::rng::Rng;\nuse embassy_stm32::{Config, bind_interrupts, peripherals, rng};\nuse {defmt_rtt as _, panic_probe as _};\n\nbind_interrupts!(struct Irqs {\n    RNG => rng::InterruptHandler<peripherals::RNG>;\n});\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let mut config = Config::default();\n    config.rcc.hsi48 = Some(Default::default()); // needed for RNG\n    let p = embassy_stm32::init(config);\n    info!(\"Hello World!\");\n\n    let mut rng = Rng::new(p.RNG, Irqs);\n\n    let mut buf = [0u8; 16];\n    unwrap!(rng.async_fill_bytes(&mut buf).await);\n    info!(\"random bytes: {:02x}\", buf);\n}\n"
  },
  {
    "path": "examples/stm32h7rs/src/bin/rtc.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse chrono::{NaiveDate, NaiveDateTime};\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_stm32::Config;\nuse embassy_stm32::rcc::LsConfig;\nuse embassy_stm32::rtc::{Rtc, RtcConfig};\nuse embassy_time::Timer;\nuse {defmt_rtt as _, panic_probe as _};\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let mut config = Config::default();\n    config.rcc.ls = LsConfig::default_lse();\n\n    let p = embassy_stm32::init(config);\n    info!(\"Hello World!\");\n\n    let now = NaiveDate::from_ymd_opt(2020, 5, 15)\n        .unwrap()\n        .and_hms_opt(10, 30, 15)\n        .unwrap();\n\n    let (mut rtc, time_provider) = Rtc::new(p.RTC, RtcConfig::default());\n    info!(\"Got RTC! {:?}\", now.and_utc().timestamp());\n\n    rtc.set_datetime(now.into()).expect(\"datetime not set\");\n\n    // In reality the delay would be much longer\n    Timer::after_millis(20000).await;\n\n    let then: NaiveDateTime = time_provider.now().unwrap().into();\n    info!(\"Got RTC! {:?}\", then.and_utc().timestamp());\n}\n"
  },
  {
    "path": "examples/stm32h7rs/src/bin/signal.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse defmt::{info, unwrap};\nuse embassy_executor::Spawner;\nuse embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;\nuse embassy_sync::signal::Signal;\nuse embassy_time::Timer;\nuse {defmt_rtt as _, panic_probe as _};\n\nstatic SIGNAL: Signal<CriticalSectionRawMutex, u32> = Signal::new();\n\n#[embassy_executor::task]\nasync fn my_sending_task() {\n    let mut counter: u32 = 0;\n\n    loop {\n        Timer::after_secs(1).await;\n\n        SIGNAL.signal(counter);\n\n        counter = counter.wrapping_add(1);\n    }\n}\n\n#[embassy_executor::main]\nasync fn main(spawner: Spawner) {\n    let _p = embassy_stm32::init(Default::default());\n    spawner.spawn(unwrap!(my_sending_task()));\n\n    loop {\n        let received_counter = SIGNAL.wait().await;\n\n        info!(\"signalled, counter: {}\", received_counter);\n    }\n}\n"
  },
  {
    "path": "examples/stm32h7rs/src/bin/spi.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse core::fmt::Write;\nuse core::str::from_utf8;\n\nuse cortex_m_rt::entry;\nuse defmt::*;\nuse embassy_executor::Executor;\nuse embassy_stm32::mode::Blocking;\nuse embassy_stm32::spi;\nuse embassy_stm32::time::mhz;\nuse heapless::String;\nuse static_cell::StaticCell;\nuse {defmt_rtt as _, panic_probe as _};\n\n#[embassy_executor::task]\nasync fn main_task(mut spi: spi::Spi<'static, Blocking, spi::mode::Master>) {\n    for n in 0u32.. {\n        let mut write: String<128> = String::new();\n        core::write!(&mut write, \"Hello DMA World {}!\\r\\n\", n).unwrap();\n        unsafe {\n            let result = spi.blocking_transfer_in_place(write.as_bytes_mut());\n            if let Err(_) = result {\n                defmt::panic!(\"crap\");\n            }\n        }\n        info!(\"read via spi: {}\", from_utf8(write.as_bytes()).unwrap());\n    }\n}\n\nstatic EXECUTOR: StaticCell<Executor> = StaticCell::new();\n\n#[entry]\nfn main() -> ! {\n    info!(\"Hello World!\");\n    let p = embassy_stm32::init(Default::default());\n\n    let mut spi_config = spi::Config::default();\n    spi_config.frequency = mhz(1);\n\n    let spi = spi::Spi::new_blocking(p.SPI3, p.PB3, p.PB5, p.PB4, spi_config);\n\n    let executor = EXECUTOR.init(Executor::new());\n\n    executor.run(|spawner| {\n        spawner.spawn(unwrap!(main_task(spi)));\n    })\n}\n"
  },
  {
    "path": "examples/stm32h7rs/src/bin/spi_dma.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse core::fmt::Write;\nuse core::str::from_utf8;\n\nuse cortex_m_rt::entry;\nuse defmt::*;\nuse embassy_executor::Executor;\nuse embassy_stm32::mode::Async;\nuse embassy_stm32::time::mhz;\nuse embassy_stm32::{bind_interrupts, dma, peripherals, spi};\nuse heapless::String;\nuse static_cell::StaticCell;\nuse {defmt_rtt as _, panic_probe as _};\n\nbind_interrupts!(struct Irqs {\n    GPDMA1_CHANNEL0 => dma::InterruptHandler<peripherals::GPDMA1_CH0>;\n    GPDMA1_CHANNEL1 => dma::InterruptHandler<peripherals::GPDMA1_CH1>;\n});\n\n#[embassy_executor::task]\nasync fn main_task(mut spi: spi::Spi<'static, Async, spi::mode::Master>) {\n    for n in 0u32.. {\n        let mut write: String<128> = String::new();\n        let mut read = [0; 128];\n        core::write!(&mut write, \"Hello DMA World {}!\\r\\n\", n).unwrap();\n        // transfer will slice the &mut read down to &write's actual length.\n        spi.transfer(&mut read, write.as_bytes()).await.ok();\n        info!(\"read via spi+dma: {}\", from_utf8(&read).unwrap());\n    }\n}\n\nstatic EXECUTOR: StaticCell<Executor> = StaticCell::new();\n\n#[entry]\nfn main() -> ! {\n    info!(\"Hello World!\");\n    let p = embassy_stm32::init(Default::default());\n\n    let mut spi_config = spi::Config::default();\n    spi_config.frequency = mhz(1);\n\n    let spi = spi::Spi::new(\n        p.SPI3,\n        p.PB3,\n        p.PB5,\n        p.PB4,\n        p.GPDMA1_CH0,\n        p.GPDMA1_CH1,\n        Irqs,\n        spi_config,\n    );\n\n    let executor = EXECUTOR.init(Executor::new());\n\n    executor.run(|spawner| {\n        spawner.spawn(unwrap!(main_task(spi)));\n    })\n}\n"
  },
  {
    "path": "examples/stm32h7rs/src/bin/usart.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse cortex_m_rt::entry;\nuse defmt::*;\nuse embassy_executor::Executor;\nuse embassy_stm32::usart::{Config, Uart};\nuse static_cell::StaticCell;\nuse {defmt_rtt as _, panic_probe as _};\n\n#[embassy_executor::task]\nasync fn main_task() {\n    let p = embassy_stm32::init(Default::default());\n\n    let config = Config::default();\n    let mut usart = Uart::new_blocking(p.UART7, p.PF6, p.PF7, config).unwrap();\n\n    unwrap!(usart.blocking_write(b\"Hello Embassy World!\\r\\n\"));\n    info!(\"wrote Hello, starting echo\");\n\n    let mut buf = [0u8; 1];\n    loop {\n        unwrap!(usart.blocking_read(&mut buf));\n        unwrap!(usart.blocking_write(&buf));\n    }\n}\n\nstatic EXECUTOR: StaticCell<Executor> = StaticCell::new();\n\n#[entry]\nfn main() -> ! {\n    info!(\"Hello World!\");\n\n    let executor = EXECUTOR.init(Executor::new());\n\n    executor.run(|spawner| {\n        spawner.spawn(unwrap!(main_task()));\n    })\n}\n"
  },
  {
    "path": "examples/stm32h7rs/src/bin/usart_dma.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse core::fmt::Write;\n\nuse cortex_m_rt::entry;\nuse defmt::*;\nuse embassy_executor::Executor;\nuse embassy_stm32::usart::{Config, Uart};\nuse embassy_stm32::{bind_interrupts, dma, peripherals, usart};\nuse heapless::String;\nuse static_cell::StaticCell;\nuse {defmt_rtt as _, panic_probe as _};\n\nbind_interrupts!(struct Irqs {\n    UART7 => usart::InterruptHandler<peripherals::UART7>;\n    GPDMA1_CHANNEL0 => dma::InterruptHandler<peripherals::GPDMA1_CH0>;\n    GPDMA1_CHANNEL1 => dma::InterruptHandler<peripherals::GPDMA1_CH1>;\n});\n\n#[embassy_executor::task]\nasync fn main_task() {\n    let p = embassy_stm32::init(Default::default());\n\n    let config = Config::default();\n    let mut usart = Uart::new(p.UART7, p.PF6, p.PF7, p.GPDMA1_CH0, p.GPDMA1_CH1, Irqs, config).unwrap();\n\n    for n in 0u32.. {\n        let mut s: String<128> = String::new();\n        core::write!(&mut s, \"Hello DMA World {}!\\r\\n\", n).unwrap();\n\n        usart.write(s.as_bytes()).await.ok();\n\n        info!(\"wrote DMA\");\n    }\n}\n\nstatic EXECUTOR: StaticCell<Executor> = StaticCell::new();\n\n#[entry]\nfn main() -> ! {\n    info!(\"Hello World!\");\n\n    let executor = EXECUTOR.init(Executor::new());\n\n    executor.run(|spawner| {\n        spawner.spawn(unwrap!(main_task()));\n    })\n}\n"
  },
  {
    "path": "examples/stm32h7rs/src/bin/usart_split.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_stm32::mode::Async;\nuse embassy_stm32::usart::{Config, Uart, UartRx};\nuse embassy_stm32::{bind_interrupts, dma, peripherals, usart};\nuse embassy_sync::blocking_mutex::raw::ThreadModeRawMutex;\nuse embassy_sync::channel::Channel;\nuse {defmt_rtt as _, panic_probe as _};\n\nbind_interrupts!(struct Irqs {\n    UART7 => usart::InterruptHandler<peripherals::UART7>;\n    GPDMA1_CHANNEL0 => dma::InterruptHandler<peripherals::GPDMA1_CH0>;\n    GPDMA1_CHANNEL1 => dma::InterruptHandler<peripherals::GPDMA1_CH1>;\n});\n\nstatic CHANNEL: Channel<ThreadModeRawMutex, [u8; 8], 1> = Channel::new();\n\n#[embassy_executor::main]\nasync fn main(spawner: Spawner) -> ! {\n    let p = embassy_stm32::init(Default::default());\n    info!(\"Hello World!\");\n\n    let config = Config::default();\n    let mut usart = Uart::new(p.UART7, p.PF6, p.PF7, p.GPDMA1_CH0, p.GPDMA1_CH1, Irqs, config).unwrap();\n    unwrap!(usart.blocking_write(b\"Type 8 chars to echo!\\r\\n\"));\n\n    let (mut tx, rx) = usart.split();\n\n    spawner.spawn(unwrap!(reader(rx)));\n\n    loop {\n        let buf = CHANNEL.receive().await;\n        info!(\"writing...\");\n        unwrap!(tx.write(&buf).await);\n    }\n}\n\n#[embassy_executor::task]\nasync fn reader(mut rx: UartRx<'static, Async>) {\n    let mut buf = [0; 8];\n    loop {\n        info!(\"reading...\");\n        unwrap!(rx.read(&mut buf).await);\n        CHANNEL.send(buf).await;\n    }\n}\n"
  },
  {
    "path": "examples/stm32h7rs/src/bin/usb_serial.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse defmt::{panic, *};\nuse embassy_executor::Spawner;\nuse embassy_futures::join::join;\nuse embassy_stm32::time::Hertz;\nuse embassy_stm32::usb::{Driver, Instance};\nuse embassy_stm32::{Config, bind_interrupts, peripherals, usb};\nuse embassy_usb::Builder;\nuse embassy_usb::class::cdc_acm::{CdcAcmClass, State};\nuse embassy_usb::driver::EndpointError;\nuse {defmt_rtt as _, panic_probe as _};\n\nbind_interrupts!(struct Irqs {\n    OTG_HS => usb::InterruptHandler<peripherals::USB_OTG_HS>;\n});\n\n// If you are trying this and your USB device doesn't connect, the most\n// common issues are the RCC config and vbus_detection\n//\n// See https://embassy.dev/book/#_the_usb_examples_are_not_working_on_my_board_is_there_anything_else_i_need_to_configure\n// for more information.\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    info!(\"Hello World!\");\n\n    let mut config = Config::default();\n\n    {\n        use embassy_stm32::rcc::*;\n        config.rcc.hse = Some(Hse {\n            freq: Hertz(24_000_000),\n            mode: HseMode::Oscillator,\n        });\n        config.rcc.pll1 = Some(Pll {\n            source: PllSource::HSE,\n            prediv: PllPreDiv::DIV12,\n            mul: PllMul::MUL300,\n            divp: Some(PllDiv::DIV1), //600 MHz\n            divq: Some(PllDiv::DIV2), // 300 MHz\n            divr: Some(PllDiv::DIV2), // 300 MHz\n            divs: None,\n            divt: None,\n        });\n        config.rcc.sys = Sysclk::PLL1_P; // 600 MHz\n        config.rcc.ahb_pre = AHBPrescaler::DIV2; // 300 MHz\n        config.rcc.apb1_pre = APBPrescaler::DIV2; // 150 MHz\n        config.rcc.apb2_pre = APBPrescaler::DIV2; // 150 MHz\n        config.rcc.apb4_pre = APBPrescaler::DIV2; // 150 MHz\n        config.rcc.apb5_pre = APBPrescaler::DIV2; // 150 MHz\n        config.rcc.voltage_scale = VoltageScale::HIGH;\n        config.rcc.mux.usbphycsel = mux::Usbphycsel::HSE;\n    }\n\n    let p = embassy_stm32::init(config);\n\n    // Create the driver, from the HAL.\n    let mut ep_out_buffer = [0u8; 256];\n    let mut config = embassy_stm32::usb::Config::default();\n\n    // Do not enable vbus_detection. This is a safe default that works in all boards.\n    // However, if your USB device is self-powered (can stay powered on if USB is unplugged), you need\n    // to enable vbus_detection to comply with the USB spec. If you enable it, the board\n    // has to support it or USB won't work at all. See docs on `vbus_detection` for details.\n    config.vbus_detection = false;\n\n    let driver = Driver::new_hs(p.USB_OTG_HS, Irqs, p.PM6, p.PM5, &mut ep_out_buffer, config);\n\n    // Create embassy-usb Config\n    let mut config = embassy_usb::Config::new(0xc0de, 0xcafe);\n    config.manufacturer = Some(\"Embassy\");\n    config.product = Some(\"USB-serial example\");\n    config.serial_number = Some(\"12345678\");\n\n    // Create embassy-usb DeviceBuilder using the driver and config.\n    // It needs some buffers for building the descriptors.\n    let mut config_descriptor = [0; 256];\n    let mut bos_descriptor = [0; 256];\n    let mut control_buf = [0; 64];\n\n    let mut state = State::new();\n\n    let mut builder = Builder::new(\n        driver,\n        config,\n        &mut config_descriptor,\n        &mut bos_descriptor,\n        &mut [], // no msos descriptors\n        &mut control_buf,\n    );\n\n    // Create classes on the builder.\n    let mut class = CdcAcmClass::new(&mut builder, &mut state, 64);\n\n    // Build the builder.\n    let mut usb = builder.build();\n\n    // Run the USB device.\n    let usb_fut = usb.run();\n\n    // Do stuff with the class!\n    let echo_fut = async {\n        loop {\n            class.wait_connection().await;\n            info!(\"Connected\");\n            let _ = echo(&mut class).await;\n            info!(\"Disconnected\");\n        }\n    };\n\n    // Run everything concurrently.\n    // If we had made everything `'static` above instead, we could do this using separate tasks instead.\n    join(usb_fut, echo_fut).await;\n}\n\nstruct Disconnected {}\n\nimpl From<EndpointError> for Disconnected {\n    fn from(val: EndpointError) -> Self {\n        match val {\n            EndpointError::BufferOverflow => panic!(\"Buffer overflow\"),\n            EndpointError::Disabled => Disconnected {},\n        }\n    }\n}\n\nasync fn echo<'d, T: Instance + 'd>(class: &mut CdcAcmClass<'d, Driver<'d, T>>) -> Result<(), Disconnected> {\n    let mut buf = [0; 64];\n    loop {\n        let n = class.read_packet(&mut buf).await?;\n        let data = &buf[..n];\n        info!(\"data: {:x}\", data);\n        class.write_packet(data).await?;\n    }\n}\n"
  },
  {
    "path": "examples/stm32h7rs/src/bin/xspi_memory_mapped.rs",
    "content": "#![no_main]\n#![no_std]\n\n//! For Nucleo STM32H7S3L8 MB1737, has MX25UW25645GXDI00\n//!\n\nuse core::cmp::min;\n\nuse defmt::info;\nuse embassy_executor::Spawner;\nuse embassy_stm32::Config;\nuse embassy_stm32::gpio::{Level, Output, Speed};\nuse embassy_stm32::mode::Blocking;\nuse embassy_stm32::time::Hertz;\nuse embassy_stm32::xspi::{\n    AddressSize, ChipSelectHighTime, DummyCycles, FIFOThresholdLevel, Instance, MemorySize, MemoryType, TransferConfig,\n    WrapSize, Xspi, XspiWidth,\n};\nuse embassy_time::Timer;\nuse {defmt_rtt as _, panic_probe as _};\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    // RCC config\n    let mut config = Config::default();\n    {\n        use embassy_stm32::rcc::*;\n        config.rcc.hse = Some(Hse {\n            freq: Hertz(24_000_000),\n            mode: HseMode::Oscillator,\n        });\n        config.rcc.pll1 = Some(Pll {\n            source: PllSource::HSE,\n            prediv: PllPreDiv::DIV3,\n            mul: PllMul::MUL150,\n            divp: Some(PllDiv::DIV2),\n            divq: None,\n            divr: None,\n            divs: None,\n            divt: None,\n        });\n        config.rcc.sys = Sysclk::PLL1_P; // 600 Mhz\n        config.rcc.ahb_pre = AHBPrescaler::DIV2; // 300 Mhz\n        config.rcc.apb1_pre = APBPrescaler::DIV2; // 150 Mhz\n        config.rcc.apb2_pre = APBPrescaler::DIV2; // 150 Mhz\n        config.rcc.apb4_pre = APBPrescaler::DIV2; // 150 Mhz\n        config.rcc.apb5_pre = APBPrescaler::DIV2; // 150 Mhz\n        config.rcc.voltage_scale = VoltageScale::HIGH;\n    }\n\n    // Initialize peripherals\n    let p = embassy_stm32::init(config);\n\n    let spi_config = embassy_stm32::xspi::Config {\n        fifo_threshold: FIFOThresholdLevel::_4Bytes,\n        memory_type: MemoryType::Macronix,\n        delay_hold_quarter_cycle: true,\n        device_size: MemorySize::_32MiB,\n        chip_select_high_time: ChipSelectHighTime::_2Cycle,\n        free_running_clock: false,\n        clock_mode: false,\n        wrap_size: WrapSize::None,\n        // 300 MHz clock / (3 + 1) = 75 MHz. This is above the max for READ instructions so the\n        // FAST READ must be used. The nucleo board's flash  can run at up to 133 MHz in SPI mode\n        // and 200 MHz in OPI mode. This clock prescaler must be even otherwise the clock will not\n        // have symmetric high and low times.\n        // The clock can also be fed by one of the PLLs to allow for more flexible clock rates.\n        clock_prescaler: 3,\n        sample_shifting: false,\n        chip_select_boundary: 0,\n        max_transfer: 0,\n        refresh: 0,\n    };\n\n    let mut cor = cortex_m::Peripherals::take().unwrap();\n\n    // Not necessary, but recommended if using XIP\n    cor.SCB.enable_icache();\n    // Note: Enabling data cache can cause issues with DMA transfers.\n    cor.SCB.enable_dcache(&mut cor.CPUID);\n\n    let xspi = embassy_stm32::xspi::Xspi::new_blocking_xspi(\n        p.XSPI2, p.PN6, p.PN2, p.PN3, p.PN4, p.PN5, p.PN8, p.PN9, p.PN10, p.PN11, p.PN1, spi_config,\n    );\n\n    let mut flash = SpiFlashMemory::new(xspi);\n\n    let flash_id = flash.read_id();\n    info!(\"FLASH ID: {=[u8]:x}\", flash_id);\n\n    // Erase the first sector\n    flash.erase_sector(0);\n\n    // Write some data into the flash. This writes more than one page to test that functionality.\n    let mut wr_buf = [0u8; 512];\n    let base_number: u8 = 0x90;\n    for i in 0..512 {\n        wr_buf[i] = base_number.wrapping_add(i as u8);\n    }\n    flash.write_memory(0, &wr_buf);\n\n    // Read the data back and verify it.\n    let mut rd_buf = [0u8; 512];\n    let start_time = embassy_time::Instant::now();\n    flash.read_memory(0, &mut rd_buf);\n    let elapsed = start_time.elapsed();\n    info!(\"Read 512 bytes in {} us in SPI mode\", elapsed.as_micros());\n    info!(\"WRITE BUF: {=[u8]:#X}\", wr_buf[0..32]);\n    info!(\"READ BUF: {=[u8]:#X}\", rd_buf[0..32]);\n\n    assert_eq!(wr_buf, rd_buf, \"Read buffer does not match write buffer\");\n\n    flash.enable_mm();\n    info!(\"Enabled memory mapped mode\");\n\n    let first_u32 = unsafe { *(0x70000000 as *const u32) };\n    assert_eq!(first_u32, 0x93929190);\n    info!(\"first_u32 {:08x}\", first_u32);\n\n    let second_u32 = unsafe { *(0x70000004 as *const u32) };\n    assert_eq!(second_u32, 0x97969594);\n    info!(\"second_u32 {:08x}\", first_u32);\n\n    flash.disable_mm();\n    info!(\"Disabled memory mapped mode\");\n\n    let flash_id = flash.read_id();\n    info!(\"FLASH ID: {=[u8]:x}\", flash_id);\n\n    let mut flash = flash.into_octo();\n\n    Timer::after_millis(100).await;\n\n    let flash_id = flash.read_id();\n    info!(\"FLASH ID in OPI mode: {=[u8]:x}\", flash_id);\n\n    flash.erase_sector(0);\n\n    let mut rd_buf = [0u8; 512];\n    flash.read_memory(0, &mut rd_buf);\n    info!(\"READ BUF after erase: {=[u8]:#X}\", rd_buf[0..32]);\n\n    assert_eq!(rd_buf, [0xFF; 512], \"Read buffer is not all 0xFF after erase\");\n\n    flash.write_memory(0, &wr_buf);\n    let start = embassy_time::Instant::now();\n    flash.read_memory(0, &mut rd_buf);\n    let elapsed = start.elapsed();\n    info!(\"Read 512 bytes in {} us in OPI mode\", elapsed.as_micros());\n    info!(\"READ BUF after write: {=[u8]:#X}\", rd_buf[0..32]);\n    assert_eq!(wr_buf, rd_buf, \"Read buffer does not match write buffer in OPI mode\");\n\n    flash.enable_mm();\n    info!(\"Enabled memory mapped mode in OPI mode\");\n    let first_u32 = unsafe { *(0x70000000 as *const u32) };\n    assert_eq!(first_u32, 0x93929190);\n    info!(\"first_u32 {:08x}\", first_u32);\n    let second_u32 = unsafe { *(0x70000004 as *const u32) };\n    assert_eq!(second_u32, 0x97969594);\n    info!(\"second_u32 {:08x}\", first_u32);\n    flash.disable_mm();\n    info!(\"Disabled memory mapped mode in OPI mode\");\n\n    // Reset back to SPI mode\n    let mut flash = flash.into_spi();\n    let flash_id = flash.read_id();\n    info!(\"FLASH ID back in SPI mode: {=[u8]:x}\", flash_id);\n\n    info!(\"DONE\");\n\n    // Output pin PE3\n    let mut led = Output::new(p.PE3, Level::Low, Speed::Low);\n\n    loop {\n        led.toggle();\n        Timer::after_millis(1000).await;\n    }\n}\n\nconst MEMORY_PAGE_SIZE: usize = 256;\n\n/// Implementation of access to flash chip using SPI.\n///\n/// Chip commands are hardcoded as it depends on used chip.\n/// This targets a MX25UW25645GXDI00.\npub struct SpiFlashMemory<I: Instance> {\n    xspi: Xspi<'static, I, Blocking>,\n}\n\n/// Implementation of access to flash chip using Octo SPI.\n///\n/// Chip commands are hardcoded as it depends on used chip.\n/// This targets a MX25UW25645GXDI00.\npub struct OpiFlashMemory<I: Instance> {\n    xspi: Xspi<'static, I, Blocking>,\n}\n\n/// SPI mode commands for MX25UW25645G flash memory\n#[allow(dead_code)]\n#[repr(u8)]\nenum SpiCommand {\n    // Array access commands\n    /// Read data bytes using 3-byte address (up to 50 MHz)\n    Read3B = 0x03,\n    /// Fast read data bytes using 3-byte address with 8 dummy cycles (up to 133 MHz)\n    FastRead3B = 0x0B,\n    /// Program 1-256 bytes of data using 3-byte address\n    PageProgram3B = 0x02,\n    /// Erase 4KB sector using 3-byte address\n    SectorErase3B = 0x20,\n    /// Erase 64KB block using 3-byte address\n    BlockErase3B = 0xD8,\n    /// Read data bytes using 4-byte address (up to 50 MHz)\n    Read4B = 0x13,\n    /// Fast read data bytes using 4-byte address with 8 dummy cycles (up to 133 MHz)\n    FastRead4B = 0x0C,\n    /// Program 1-256 bytes of data using 4-byte address\n    PageProgram4B = 0x12,\n    /// Erase 4KB sector using 4-byte address\n    SectorErase4B = 0x21,\n    /// Erase 64KB block using 4-byte address\n    BlockErase4B = 0xDC,\n    /// Erase entire chip (only if no blocks are protected)\n    ChipErase = 0x60,\n\n    // Write Buffer Access commands\n    /// Read data from the 256-byte page buffer\n    ReadBuffer = 0x25,\n    /// Initialize write-to-buffer sequence, clears buffer and writes initial data\n    WriteBufferInitial = 0x22,\n    /// Continue writing data to buffer (used between WRBI and WRCF)\n    WriteBufferContinue = 0x24,\n    /// Confirm write operation, programs buffer contents to flash array\n    WriteBufferConfirm = 0x31,\n\n    // Device operation commands\n    /// Set Write Enable Latch (WEL) bit, required before write/program/erase operations\n    WriteEnable = 0x06,\n    /// Clear Write Enable Latch (WEL) bit\n    WriteDisable = 0x04,\n    /// Select write protection mode (BP mode or Advanced Sector Protection)\n    WriteProtectSelection = 0x68,\n    /// Suspend ongoing program or erase operation to allow read access\n    ProgramEraseSuspend = 0xB0,\n    /// Resume suspended program or erase operation\n    ProgramEraseResume = 0x30,\n    /// Enter deep power-down mode for minimum power consumption\n    DeepPowerDown = 0xB9,\n    /// Exit deep power-down mode and return to standby\n    ReleaseFromDeepPowerDown = 0xAB,\n    /// No operation, can terminate Reset Enable command\n    NoOperation = 0x00,\n    /// Enable reset operation (must precede Reset Memory command)\n    ResetEnable = 0x66,\n    /// Reset device to power-on state (requires prior Reset Enable)\n    ResetMemory = 0x99,\n    /// Protect all sectors using Dynamic Protection Bits (DPB)\n    GangBlockLock = 0x7E,\n    /// Unprotect all sectors by clearing Dynamic Protection Bits (DPB)\n    GangBlockUnlock = 0x98,\n\n    // Register Access commands\n    /// Read 3-byte device identification (manufacturer ID + device ID)\n    ReadIdentification = 0x9F,\n    /// Read Serial Flash Discoverable Parameters (SFDP) table\n    ReadSFDP = 0x5A,\n    /// Read 8-bit Status Register (WIP, WEL, BP bits, etc.)\n    ReadStatusRegister = 0x05,\n    /// Read 8-bit Configuration Register (ODS, TB, PBE bits)\n    ReadConfigurationRegister = 0x15,\n    /// Write Status and/or Configuration Register (1-2 bytes)\n    WriteStatusConfigurationRegister = 0x01,\n    /// Read Configuration Register 2 from specified 4-byte address\n    ReadConfigurationRegister2 = 0x71,\n    /// Write Configuration Register 2 to specified 4-byte address\n    WriteConfigurationRegister2 = 0x72,\n    /// Read 8-bit Security Register (protection status, suspend bits)\n    ReadSecurityRegister = 0x2B,\n    /// Write Security Register to set customer lock-down bit\n    WriteSecurityRegister = 0x2F,\n    /// Read 32-bit Fast Boot Register (boot address and configuration)\n    ReadFastBootRegister = 0x16,\n    /// Write 32-bit Fast Boot Register\n    WriteFastBootRegister = 0x17,\n    /// Erase Fast Boot Register (disable fast boot feature)\n    EraseFastBootRegister = 0x18,\n    /// Set burst/wrap length for read operations (16/32/64 bytes)\n    SetBurstLength = 0xC0,\n    /// Enter 8K-bit secured OTP mode for programming unique identifiers\n    EnterSecuredOTP = 0xB1,\n    /// Exit secured OTP mode and return to main array access\n    ExitSecuredOTP = 0xC1,\n    /// Write Lock Register to control SPB protection mode\n    WriteLockRegister = 0x2C,\n    /// Read Lock Register status\n    ReadLockRegister = 0x2D,\n    /// Program Solid Protection Bit (SPB) for specified sector/block\n    WriteSPB = 0xE3,\n    /// Erase all Solid Protection Bits (SPB)\n    EraseSPB = 0xE4,\n    /// Read Solid Protection Bit (SPB) status for specified sector/block\n    ReadSPB = 0xE2,\n    /// Write Dynamic Protection Bit (DPB) for specified sector\n    WriteDPB = 0xE1,\n    /// Read Dynamic Protection Bit (DPB) status for specified sector\n    ReadDPB = 0xE0,\n    /// Read 64-bit password register (only in Solid Protection mode)\n    ReadPassword = 0x27,\n    /// Write 64-bit password register\n    WritePassword = 0x28,\n    /// Unlock SPB operations using 64-bit password\n    PasswordUnlock = 0x29,\n}\n\n/// OPI mode commands for MX25UW25645G flash memory\n#[allow(dead_code)]\n#[repr(u16)]\nenum OpiCommand {\n    // Array access commands\n    /// Read data using 8 I/O lines in STR mode with configurable dummy cycles (up to 200 MHz)\n    OctaRead = 0xEC13,\n    /// Read data using 8 I/O lines in DTR mode with configurable dummy cycles (up to 200 MHz)\n    OctaDTRRead = 0xEE11,\n    /// Program 1-256 bytes using 4-byte address and 8 I/O lines\n    PageProgram4B = 0x12ED,\n    /// Erase 4KB sector using 4-byte address\n    SectorErase4B = 0x21DE,\n    /// Erase 64KB block using 4-byte address\n    BlockErase4B = 0xDC23,\n    /// Erase entire chip (only if no blocks are protected)\n    ChipErase = 0x609F,\n\n    // Write Buffer Access commands\n    /// Read data from the 256-byte page buffer using 4-byte address\n    ReadBuffer = 0x25DA,\n    /// Initialize interruptible write-to-buffer sequence with 4-byte address\n    WriteBufferInitial = 0x22DD,\n    /// Continue writing data to buffer during interruptible sequence\n    WriteBufferContinue = 0x24DB,\n    /// Confirm and execute write operation from buffer to flash array\n    WriteBufferConfirm = 0x31CE,\n\n    // Device operation commands\n    /// Set Write Enable Latch (WEL) bit, required before write/program/erase operations\n    WriteEnable = 0x06F9,\n    /// Clear Write Enable Latch (WEL) bit, aborts write-to-buffer sequence\n    WriteDisable = 0x04FB,\n    /// Select write protection mode (BP mode or Advanced Sector Protection) - OTP bit\n    WriteProtectSelection = 0x6897,\n    /// Suspend ongoing program or erase operation to allow read from other banks\n    ProgramEraseSuspend = 0xB04F,\n    /// Resume suspended program or erase operation\n    ProgramEraseResume = 0x30CF,\n    /// Enter deep power-down mode for minimum power consumption\n    DeepPowerDown = 0xB946,\n    /// Exit deep power-down mode and return to standby\n    ReleaseFromDeepPowerDown = 0xAB54,\n    /// No operation, can terminate Reset Enable command\n    NoOperation = 0x00FF,\n    /// Enable reset operation (must precede Reset Memory command)\n    ResetEnable = 0x6699,\n    /// Reset device to power-on state, clears volatile settings\n    ResetMemory = 0x9966,\n    /// Protect all sectors using Dynamic Protection Bits (DPB)\n    GangBlockLock = 0x7E81,\n    /// Unprotect all sectors by clearing Dynamic Protection Bits (DPB)\n    GangBlockUnlock = 0x9867,\n\n    // Register Access commands\n    /// Read 3-byte device identification with 4-byte dummy address\n    ReadIdentification = 0x9F60,\n    /// Read Serial Flash Discoverable Parameters (SFDP) table with 4-byte address\n    ReadSFDP = 0x5AA5,\n    /// Read 8-bit Status Register with 4-byte dummy address\n    ReadStatusRegister = 0x05FA,\n    /// Read 8-bit Configuration Register with specific address (00000001h)\n    ReadConfigurationRegister = 0x15EA,\n    /// Write 8-bit Status Register with specific address (00000000h) or Configuration Register with address (00000001h)\n    WriteStatusConfigurationRegister = 0x01FE,\n    /// Read Configuration Register 2 from specified 4-byte address\n    ReadConfigurationRegister2 = 0x718E,\n    /// Write Configuration Register 2 to specified 4-byte address\n    WriteConfigurationRegister2 = 0x728D,\n    /// Read 8-bit Security Register with 4-byte dummy address\n    ReadSecurityRegister = 0x2BD4,\n    /// Write Security Register to set customer lock-down bit\n    WriteSecurityRegister = 0x2FD0,\n    /// Set burst/wrap length for read operations with 4-byte dummy address\n    SetBurstLength = 0xC03F,\n    /// Read 32-bit Fast Boot Register with 4-byte dummy address\n    ReadFastBootRegister = 0x16E9,\n    /// Write 32-bit Fast Boot Register with 4-byte dummy address\n    WriteFastBootRegister = 0x17E8,\n    /// Erase Fast Boot Register (disable fast boot feature)\n    EraseFastBootRegister = 0x18E7,\n    /// Enter 8K-bit secured OTP mode for programming unique identifiers\n    EnterSecuredOTP = 0xB14E,\n    /// Exit secured OTP mode and return to main array access\n    ExitSecuredOTP = 0xC13E,\n    /// Write Lock Register to control SPB protection mode with 4-byte dummy address\n    WriteLockRegister = 0x2CD3,\n    /// Read Lock Register status with 4-byte dummy address\n    ReadLockRegister = 0x2DD2,\n    /// Program Solid Protection Bit (SPB) for specified 4-byte address\n    WriteSPB = 0xE31C,\n    /// Erase all Solid Protection Bits (SPB)\n    EraseSPB = 0xE41B,\n    /// Read Solid Protection Bit (SPB) status for specified 4-byte address\n    ReadSPB = 0xE21D,\n    /// Write Dynamic Protection Bit (DPB) for specified 4-byte address\n    WriteDPB = 0xE11E,\n    /// Read Dynamic Protection Bit (DPB) status for specified 4-byte address\n    ReadDPB = 0xE01F,\n    /// Read 64-bit password register with 4-byte dummy address and 20 dummy cycles\n    ReadPassword = 0x27D8,\n    /// Write 64-bit password register with 4-byte dummy address\n    WritePassword = 0x28D7,\n    /// Unlock SPB operations using 64-bit password with 4-byte dummy address\n    PasswordUnlock = 0x29D6,\n}\n\nimpl<I: Instance> SpiFlashMemory<I> {\n    pub fn new(xspi: Xspi<'static, I, Blocking>) -> Self {\n        let mut memory = Self { xspi };\n\n        memory.reset_memory();\n        memory\n    }\n\n    pub fn disable_mm(&mut self) {\n        self.xspi.disable_memory_mapped_mode();\n    }\n\n    pub fn enable_mm(&mut self) {\n        let read_config = TransferConfig {\n            iwidth: XspiWidth::SING,\n            isize: AddressSize::_8bit,\n            adwidth: XspiWidth::SING,\n            adsize: AddressSize::_32bit,\n            dwidth: XspiWidth::SING,\n            instruction: Some(SpiCommand::FastRead4B as u32),\n            dummy: DummyCycles::_8,\n            ..Default::default()\n        };\n\n        let write_config = TransferConfig {\n            iwidth: XspiWidth::SING,\n            isize: AddressSize::_8bit,\n            adwidth: XspiWidth::SING,\n            adsize: AddressSize::_32bit,\n            dwidth: XspiWidth::SING,\n            instruction: Some(SpiCommand::PageProgram4B as u32),\n            dummy: DummyCycles::_0,\n            ..Default::default()\n        };\n        self.xspi.enable_memory_mapped_mode(read_config, write_config).unwrap();\n    }\n\n    fn into_octo(mut self) -> OpiFlashMemory<I> {\n        self.enable_opi_mode();\n        OpiFlashMemory { xspi: self.xspi }\n    }\n\n    fn enable_opi_mode(&mut self) {\n        let cr2_0 = self.read_cr2(0);\n        info!(\"Read CR2 at 0x0: {:x}\", cr2_0);\n        self.enable_write();\n        self.write_cr2(0, cr2_0 | 0x01); // Set bit 0 to enable octo SPI in STR\n    }\n\n    fn exec_command(&mut self, cmd: u8) {\n        let transaction = TransferConfig {\n            iwidth: XspiWidth::SING,\n            adwidth: XspiWidth::NONE,\n            // adsize: AddressSize::_24bit,\n            dwidth: XspiWidth::NONE,\n            instruction: Some(cmd as u32),\n            address: None,\n            dummy: DummyCycles::_0,\n            ..Default::default()\n        };\n        // info!(\"Excuting command: {:x}\", transaction.instruction);\n        self.xspi.blocking_command(&transaction).unwrap();\n    }\n\n    pub fn reset_memory(&mut self) {\n        self.exec_command(SpiCommand::ResetEnable as u8);\n        self.exec_command(SpiCommand::ResetMemory as u8);\n        self.wait_write_finish();\n    }\n\n    pub fn enable_write(&mut self) {\n        self.exec_command(SpiCommand::WriteEnable as u8);\n    }\n\n    pub fn read_id(&mut self) -> [u8; 3] {\n        let mut buffer = [0; 3];\n        let transaction: TransferConfig = TransferConfig {\n            iwidth: XspiWidth::SING,\n            isize: AddressSize::_8bit,\n            adwidth: XspiWidth::NONE,\n            dwidth: XspiWidth::SING,\n            instruction: Some(SpiCommand::ReadIdentification as u32),\n            ..Default::default()\n        };\n        self.xspi.blocking_read(&mut buffer, transaction).unwrap();\n        buffer\n    }\n\n    pub fn read_memory(&mut self, addr: u32, buffer: &mut [u8]) {\n        let transaction = TransferConfig {\n            iwidth: XspiWidth::SING,\n            adwidth: XspiWidth::SING,\n            adsize: AddressSize::_32bit,\n            dwidth: XspiWidth::SING,\n            instruction: Some(SpiCommand::FastRead4B as u32),\n            dummy: DummyCycles::_8,\n            address: Some(addr),\n            ..Default::default()\n        };\n\n        self.xspi.blocking_read(buffer, transaction).unwrap();\n    }\n\n    fn wait_write_finish(&mut self) {\n        while (self.read_sr() & 0x01) != 0 {}\n    }\n\n    fn perform_erase(&mut self, addr: u32, cmd: u8) {\n        let transaction = TransferConfig {\n            iwidth: XspiWidth::SING,\n            adwidth: XspiWidth::SING,\n            adsize: AddressSize::_32bit,\n            dwidth: XspiWidth::NONE,\n            instruction: Some(cmd as u32),\n            address: Some(addr),\n            dummy: DummyCycles::_0,\n            ..Default::default()\n        };\n        self.enable_write();\n        self.xspi.blocking_command(&transaction).unwrap();\n        self.wait_write_finish();\n    }\n\n    pub fn erase_sector(&mut self, addr: u32) {\n        self.perform_erase(addr, SpiCommand::SectorErase4B as u8);\n    }\n\n    pub fn erase_block_64k(&mut self, addr: u32) {\n        self.perform_erase(addr, SpiCommand::BlockErase4B as u8);\n    }\n\n    pub fn erase_chip(&mut self) {\n        self.enable_write();\n        self.exec_command(SpiCommand::ChipErase as u8);\n        self.wait_write_finish();\n    }\n\n    fn write_page(&mut self, addr: u32, buffer: &[u8], len: usize) {\n        assert!(\n            (len as u32 + (addr & 0x000000ff)) <= MEMORY_PAGE_SIZE as u32,\n            \"write_page(): page write length exceeds page boundary (len = {}, addr = {:X}\",\n            len,\n            addr\n        );\n\n        let transaction = TransferConfig {\n            iwidth: XspiWidth::SING,\n            adsize: AddressSize::_32bit,\n            adwidth: XspiWidth::SING,\n            dwidth: XspiWidth::SING,\n            instruction: Some(SpiCommand::PageProgram4B as u32),\n            address: Some(addr),\n            dummy: DummyCycles::_0,\n            ..Default::default()\n        };\n        self.enable_write();\n        self.xspi.blocking_write(buffer, transaction).unwrap();\n        self.wait_write_finish();\n    }\n\n    pub fn write_memory(&mut self, addr: u32, buffer: &[u8]) {\n        let mut left = buffer.len();\n        let mut place = addr;\n        let mut chunk_start = 0;\n\n        while left > 0 {\n            let max_chunk_size = MEMORY_PAGE_SIZE - (place & 0x000000ff) as usize;\n            let chunk_size = min(max_chunk_size, left);\n            let chunk = &buffer[chunk_start..(chunk_start + chunk_size)];\n            self.write_page(place, chunk, chunk_size);\n            place += chunk_size as u32;\n            left -= chunk_size;\n            chunk_start += chunk_size;\n        }\n    }\n\n    // Note: read_register cannot be used to read the configuration register 2 since there is an\n    // address required for that read.\n    fn read_register(&mut self, cmd: u8) -> u8 {\n        let mut buffer = [0; 1];\n        let transaction: TransferConfig = TransferConfig {\n            iwidth: XspiWidth::SING,\n            isize: AddressSize::_8bit,\n            adwidth: XspiWidth::NONE,\n            dwidth: XspiWidth::SING,\n            instruction: Some(cmd as u32),\n            address: None,\n            dummy: DummyCycles::_0,\n            ..Default::default()\n        };\n        self.xspi.blocking_read(&mut buffer, transaction).unwrap();\n        buffer[0]\n    }\n\n    pub fn read_sr(&mut self) -> u8 {\n        self.read_register(SpiCommand::ReadStatusRegister as u8)\n    }\n\n    pub fn read_cr(&mut self) -> u8 {\n        self.read_register(SpiCommand::ReadConfigurationRegister as u8)\n    }\n\n    pub fn write_sr_cr(&mut self, sr: u8, cr: u8) {\n        let buffer = [sr, cr];\n        let transaction: TransferConfig = TransferConfig {\n            iwidth: XspiWidth::SING,\n            isize: AddressSize::_8bit,\n            instruction: Some(SpiCommand::WriteStatusConfigurationRegister as u32),\n            adwidth: XspiWidth::NONE,\n            dwidth: XspiWidth::SING,\n            address: None,\n            dummy: DummyCycles::_0,\n            ..Default::default()\n        };\n        self.enable_write();\n        self.xspi.blocking_write(&buffer, transaction).unwrap();\n        self.wait_write_finish();\n    }\n\n    pub fn read_cr2(&mut self, address: u32) -> u8 {\n        let mut buffer = [0; 1];\n        let transaction: TransferConfig = TransferConfig {\n            iwidth: XspiWidth::SING,\n            isize: AddressSize::_8bit,\n            instruction: Some(SpiCommand::ReadConfigurationRegister2 as u32),\n            adsize: AddressSize::_32bit,\n            adwidth: XspiWidth::SING,\n            dwidth: XspiWidth::SING,\n            address: Some(address),\n            dummy: DummyCycles::_0,\n            ..Default::default()\n        };\n        self.xspi.blocking_read(&mut buffer, transaction).unwrap();\n        buffer[0]\n    }\n\n    pub fn write_cr2(&mut self, address: u32, value: u8) {\n        let buffer = [value; 1];\n        let transaction: TransferConfig = TransferConfig {\n            iwidth: XspiWidth::SING,\n            isize: AddressSize::_8bit,\n            instruction: Some(SpiCommand::WriteConfigurationRegister2 as u32),\n            adsize: AddressSize::_32bit,\n            adwidth: XspiWidth::SING,\n            dwidth: XspiWidth::SING,\n            address: Some(address),\n            dummy: DummyCycles::_0,\n            ..Default::default()\n        };\n        self.xspi.blocking_write(&buffer, transaction).unwrap();\n        self.wait_write_finish();\n    }\n}\n\nimpl<I: Instance> OpiFlashMemory<I> {\n    pub fn into_spi(mut self) -> SpiFlashMemory<I> {\n        self.disable_opi_mode();\n        SpiFlashMemory { xspi: self.xspi }\n    }\n\n    /// Disable OPI mode and return to SPI\n    pub fn disable_opi_mode(&mut self) {\n        // Clear SOPI and DOPI bits in CR2 volatile register\n        let cr2_0 = self.read_cr2(0x00000000);\n        self.write_cr2(0x00000000, cr2_0 & 0xFC); // Clear bits 0 and 1\n    }\n\n    /// Enable memory-mapped mode for OPI\n    pub fn enable_mm(&mut self) {\n        let read_config = TransferConfig {\n            iwidth: XspiWidth::OCTO,\n            isize: AddressSize::_16bit, // 2-byte command for OPI\n            adwidth: XspiWidth::OCTO,\n            adsize: AddressSize::_32bit,\n            dwidth: XspiWidth::OCTO,\n            instruction: Some(OpiCommand::OctaRead as u32),\n            dummy: DummyCycles::_20, // Default dummy cycles for OPI\n            ..Default::default()\n        };\n\n        let write_config = TransferConfig {\n            iwidth: XspiWidth::OCTO,\n            isize: AddressSize::_16bit,\n            adwidth: XspiWidth::OCTO,\n            adsize: AddressSize::_32bit,\n            dwidth: XspiWidth::OCTO,\n            instruction: Some(OpiCommand::PageProgram4B as u32),\n            dummy: DummyCycles::_0,\n            ..Default::default()\n        };\n\n        self.xspi.enable_memory_mapped_mode(read_config, write_config).unwrap();\n    }\n\n    pub fn disable_mm(&mut self) {\n        self.xspi.disable_memory_mapped_mode();\n    }\n\n    /// Execute OPI command (2-byte command)\n    fn exec_command(&mut self, cmd: OpiCommand) {\n        let transaction = TransferConfig {\n            iwidth: XspiWidth::OCTO,\n            isize: AddressSize::_16bit, // 2-byte command\n            adwidth: XspiWidth::NONE,\n            dwidth: XspiWidth::NONE,\n            instruction: Some(cmd as u32),\n            address: None,\n            dummy: DummyCycles::_0,\n            ..Default::default()\n        };\n        self.xspi.blocking_command(&transaction).unwrap();\n    }\n\n    /// Reset memory using OPI commands\n    pub fn reset_memory(&mut self) {\n        self.exec_command(OpiCommand::ResetEnable);\n        self.exec_command(OpiCommand::ResetMemory);\n        self.wait_write_finish();\n    }\n\n    /// Enable write using OPI command\n    pub fn enable_write(&mut self) {\n        self.exec_command(OpiCommand::WriteEnable);\n    }\n\n    /// Read device ID in OPI mode\n    pub fn read_id(&mut self) -> [u8; 3] {\n        let mut buffer = [0; 3];\n        let transaction = TransferConfig {\n            iwidth: XspiWidth::OCTO,\n            isize: AddressSize::_16bit,\n            adwidth: XspiWidth::OCTO,\n            adsize: AddressSize::_32bit,\n            dwidth: XspiWidth::OCTO,\n            instruction: Some(OpiCommand::ReadIdentification as u32),\n            address: Some(0x00000000), // Dummy address required\n            dummy: DummyCycles::_4,\n            ..Default::default()\n        };\n        self.xspi.blocking_read(&mut buffer, transaction).unwrap();\n        buffer\n    }\n\n    /// Read memory using OPI mode\n    pub fn read_memory(&mut self, addr: u32, buffer: &mut [u8]) {\n        let transaction = TransferConfig {\n            iwidth: XspiWidth::OCTO,\n            isize: AddressSize::_16bit,\n            adwidth: XspiWidth::OCTO,\n            adsize: AddressSize::_32bit,\n            dwidth: XspiWidth::OCTO,\n            instruction: Some(OpiCommand::OctaRead as u32),\n            address: Some(addr),\n            dummy: DummyCycles::_20, // Default for 200MHz operation\n            ..Default::default()\n        };\n        self.xspi.blocking_read(buffer, transaction).unwrap();\n    }\n\n    /// Wait for write completion using OPI status read\n    fn wait_write_finish(&mut self) {\n        while (self.read_sr() & 0x01) != 0 {}\n    }\n\n    /// Perform erase operation using OPI command\n    fn perform_erase(&mut self, addr: u32, cmd: OpiCommand) {\n        let transaction = TransferConfig {\n            iwidth: XspiWidth::OCTO,\n            isize: AddressSize::_16bit,\n            adwidth: XspiWidth::OCTO,\n            adsize: AddressSize::_32bit,\n            dwidth: XspiWidth::NONE,\n            instruction: Some(cmd as u32),\n            address: Some(addr),\n            dummy: DummyCycles::_0,\n            ..Default::default()\n        };\n        self.enable_write();\n        self.xspi.blocking_command(&transaction).unwrap();\n        self.wait_write_finish();\n    }\n\n    /// Erase 4KB sector using OPI\n    pub fn erase_sector(&mut self, addr: u32) {\n        self.perform_erase(addr, OpiCommand::SectorErase4B);\n    }\n\n    /// Erase 64KB block using OPI\n    pub fn erase_block_64k(&mut self, addr: u32) {\n        self.perform_erase(addr, OpiCommand::BlockErase4B);\n    }\n\n    /// Erase entire chip using OPI\n    pub fn erase_chip(&mut self) {\n        self.enable_write();\n        self.exec_command(OpiCommand::ChipErase);\n        self.wait_write_finish();\n    }\n\n    /// Write single page using OPI\n    fn write_page(&mut self, addr: u32, buffer: &[u8], len: usize) {\n        assert!(\n            (len as u32 + (addr & 0x000000ff)) <= MEMORY_PAGE_SIZE as u32,\n            \"write_page(): page write length exceeds page boundary (len = {}, addr = {:X})\",\n            len,\n            addr\n        );\n\n        let transaction = TransferConfig {\n            iwidth: XspiWidth::OCTO,\n            isize: AddressSize::_16bit,\n            adwidth: XspiWidth::OCTO,\n            adsize: AddressSize::_32bit,\n            dwidth: XspiWidth::OCTO,\n            instruction: Some(OpiCommand::PageProgram4B as u32),\n            address: Some(addr),\n            dummy: DummyCycles::_0,\n            ..Default::default()\n        };\n        self.enable_write();\n        self.xspi.blocking_write(buffer, transaction).unwrap();\n        self.wait_write_finish();\n    }\n\n    /// Write memory using OPI (handles page boundaries)\n    pub fn write_memory(&mut self, addr: u32, buffer: &[u8]) {\n        let mut left = buffer.len();\n        let mut place = addr;\n        let mut chunk_start = 0;\n\n        while left > 0 {\n            let max_chunk_size = MEMORY_PAGE_SIZE - (place & 0x000000ff) as usize;\n            let chunk_size = min(max_chunk_size, left);\n            let chunk = &buffer[chunk_start..(chunk_start + chunk_size)];\n            self.write_page(place, chunk, chunk_size);\n            place += chunk_size as u32;\n            left -= chunk_size;\n            chunk_start += chunk_size;\n        }\n    }\n\n    /// Read register using OPI mode\n    fn read_register(&mut self, cmd: OpiCommand, dummy_addr: u32, dummy_cycles: DummyCycles) -> u8 {\n        let mut buffer = [0; 1];\n        let transaction = TransferConfig {\n            iwidth: XspiWidth::OCTO,\n            isize: AddressSize::_16bit,\n            adwidth: XspiWidth::OCTO,\n            adsize: AddressSize::_32bit,\n            dwidth: XspiWidth::OCTO,\n            instruction: Some(cmd as u32),\n            address: Some(dummy_addr),\n            dummy: dummy_cycles,\n            ..Default::default()\n        };\n        self.xspi.blocking_read(&mut buffer, transaction).unwrap();\n        buffer[0]\n    }\n\n    /// Read Status Register using OPI\n    pub fn read_sr(&mut self) -> u8 {\n        self.read_register(\n            OpiCommand::ReadStatusRegister,\n            0x00000000, // Dummy address\n            DummyCycles::_4,\n        )\n    }\n\n    /// Read Configuration Register using OPI\n    pub fn read_cr(&mut self) -> u8 {\n        self.read_register(\n            OpiCommand::ReadConfigurationRegister,\n            0x00000001, // Address for CR\n            DummyCycles::_4,\n        )\n    }\n\n    /// Write Status/Configuration Register using OPI\n    pub fn write_sr_cr(&mut self, sr: u8, cr: u8) {\n        let transaction = TransferConfig {\n            iwidth: XspiWidth::OCTO,\n            isize: AddressSize::_16bit,\n            adwidth: XspiWidth::OCTO,\n            adsize: AddressSize::_32bit,\n            dwidth: XspiWidth::OCTO,\n            instruction: Some(OpiCommand::WriteStatusConfigurationRegister as u32),\n            address: Some(0x00000000),\n            dummy: DummyCycles::_0,\n            ..Default::default()\n        };\n\n        self.enable_write();\n        self.xspi.blocking_write(&[sr, cr], transaction).unwrap();\n        self.wait_write_finish();\n    }\n\n    /// Read Configuration Register 2 using OPI\n    pub fn read_cr2(&mut self, address: u32) -> u8 {\n        let mut buffer = [0; 1];\n        let transaction = TransferConfig {\n            iwidth: XspiWidth::OCTO,\n            isize: AddressSize::_16bit,\n            adwidth: XspiWidth::OCTO,\n            adsize: AddressSize::_32bit,\n            dwidth: XspiWidth::OCTO,\n            instruction: Some(OpiCommand::ReadConfigurationRegister2 as u32),\n            address: Some(address),\n            dummy: DummyCycles::_4,\n            ..Default::default()\n        };\n        self.xspi.blocking_read(&mut buffer, transaction).unwrap();\n        buffer[0]\n    }\n\n    /// Write Configuration Register 2 using OPI\n    pub fn write_cr2(&mut self, address: u32, value: u8) {\n        let transaction = TransferConfig {\n            iwidth: XspiWidth::OCTO,\n            isize: AddressSize::_16bit,\n            adwidth: XspiWidth::OCTO,\n            adsize: AddressSize::_32bit,\n            dwidth: XspiWidth::OCTO,\n            instruction: Some(OpiCommand::WriteConfigurationRegister2 as u32),\n            address: Some(address),\n            dummy: DummyCycles::_0,\n            ..Default::default()\n        };\n\n        self.enable_write();\n        self.xspi.blocking_write(&[value], transaction).unwrap();\n        self.wait_write_finish();\n    }\n}\n"
  },
  {
    "path": "examples/stm32l0/.cargo/config.toml",
    "content": "[target.'cfg(all(target_arch = \"arm\", target_os = \"none\"))']\n# replace your chip as listed in `probe-rs chip list`\nrunner = \"probe-rs run --chip STM32L073RZTx\"\n\n[build]\ntarget = \"thumbv6m-none-eabi\"\n\n[env]\nDEFMT_LOG = \"trace\"\n"
  },
  {
    "path": "examples/stm32l0/Cargo.toml",
    "content": "[package]\nedition = \"2024\"\nname = \"embassy-stm32l0-examples\"\nversion = \"0.1.0\"\nlicense = \"MIT OR Apache-2.0\"\npublish = false\n\n[dependencies]\n# Change stm32l072cz to your chip name, if necessary.\nembassy-stm32 = { version = \"0.6.0\", path = \"../../embassy-stm32\", features = [\"defmt\", \"stm32l073rz\", \"unstable-pac\", \"time-driver-any\", \"exti\", \"memory-x\"]  }\nembassy-sync = { version = \"0.8.0\", path = \"../../embassy-sync\", features = [\"defmt\"] }\nembassy-executor = { version = \"0.10.0\", path = \"../../embassy-executor\", features = [\"platform-cortex-m\", \"executor-thread\", \"defmt\"] }\nembassy-time = { version = \"0.5.1\", path = \"../../embassy-time\", features = [\"defmt\", \"defmt-timestamp-uptime\", \"tick-hz-32_768\"] }\nembassy-usb = { version = \"0.6.0\", path = \"../../embassy-usb\", features = [\"defmt\"] }\nembassy-futures = { version = \"0.1.2\", path = \"../../embassy-futures\" }\n\ndefmt = \"1.0.1\"\ndefmt-rtt = \"1.0.0\"\n\nembedded-storage = \"0.3.1\"\nembedded-io = { version = \"0.7.1\" }\nembedded-io-async = { version = \"0.7.0\" }\n\ncortex-m = { version = \"0.7.6\", features = [\"inline-asm\", \"critical-section-single-core\"] }\ncortex-m-rt = \"0.7.0\"\npanic-probe = { version = \"1.0.0\", features = [\"print-defmt\"] }\nheapless = { version = \"0.9\", default-features = false }\nembedded-hal = \"0.2.6\"\nstatic_cell = { version = \"2\" }\nportable-atomic = { version = \"1.5\", features = [\"unsafe-assume-single-core\"] }\n\n[profile.release]\ndebug = 2\n\n[package.metadata.embassy]\nbuild = [\n  { target = \"thumbv6m-none-eabi\", artifact-dir = \"out/examples/stm32l0\" }\n]\n"
  },
  {
    "path": "examples/stm32l0/README.md",
    "content": "# Examples for STM32L0 family\nRun individual examples with\n```\ncargo run --bin <module-name>\n```\nfor example\n```\ncargo run --bin blinky\n```\n\n## Checklist before running examples\nYou might need to adjust `.cargo/config.toml`, `Cargo.toml` and possibly update pin numbers or peripherals to match the specific MCU or board you are using.\n\n* [ ] Update .cargo/config.toml with the correct probe-rs command to use your specific MCU. For example for L073RZ it should be `probe-rs run --chip STM32L073RZTx`. (use `probe-rs chip list` to find your chip)\n* [ ] Update Cargo.toml to have the correct `embassy-stm32` feature. For example for L073RZ it should be `stm32l073rz`. Look in the `Cargo.toml` file of the `embassy-stm32` project to find the correct feature flag for your chip.\n* [ ] If your board has a special clock or power configuration, make sure that it is set up appropriately.\n* [ ] If your board has different pin mapping, update any pin numbers or peripherals in the given example code to match your schematic\n\nIf you are unsure, please drop by the Embassy Matrix chat for support, and let us know:\n\n* Which example you are trying to run\n* Which chip and board you are using\n\nEmbassy Chat: https://matrix.to/#/#embassy-rs:matrix.org\n"
  },
  {
    "path": "examples/stm32l0/build.rs",
    "content": "fn main() {\n    println!(\"cargo:rustc-link-arg-bins=--nmagic\");\n    println!(\"cargo:rustc-link-arg-bins=-Tlink.x\");\n    println!(\"cargo:rustc-link-arg-bins=-Tdefmt.x\");\n}\n"
  },
  {
    "path": "examples/stm32l0/src/bin/adc.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_stm32::adc::{Adc, SampleTime};\nuse embassy_stm32::peripherals::ADC1;\nuse embassy_stm32::{adc, bind_interrupts};\nuse embassy_time::Timer;\nuse {defmt_rtt as _, panic_probe as _};\n\nbind_interrupts!(struct Irqs {\n    ADC1_COMP => adc::InterruptHandler<ADC1>;\n});\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let p = embassy_stm32::init(Default::default());\n    info!(\"Hello World!\");\n\n    let mut adc = Adc::new(p.ADC1, Irqs);\n    let mut pin = p.PA1;\n\n    let mut vrefint = adc.enable_vref();\n    let vrefint_sample = adc.read(&mut vrefint, SampleTime::CYCLES79_5).await;\n    let convert_to_millivolts = |sample| {\n        // From https://www.st.com/resource/en/datasheet/stm32l051c6.pdf\n        // 6.3.3 Embedded internal reference voltage\n        const VREFINT_MV: u32 = 1224; // mV\n\n        (u32::from(sample) * VREFINT_MV / u32::from(vrefint_sample)) as u16\n    };\n\n    loop {\n        let v = adc.read(&mut pin, SampleTime::CYCLES79_5).await;\n        info!(\"--> {} - {} mV\", v, convert_to_millivolts(v));\n        Timer::after_millis(100).await;\n    }\n}\n"
  },
  {
    "path": "examples/stm32l0/src/bin/blinky.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_stm32::gpio::{Level, Output, Speed};\nuse embassy_time::Timer;\nuse {defmt_rtt as _, panic_probe as _};\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let p = embassy_stm32::init(Default::default());\n    info!(\"Hello World!\");\n\n    let mut led = Output::new(p.PB5, Level::High, Speed::Low);\n\n    loop {\n        info!(\"high\");\n        led.set_high();\n        Timer::after_millis(300).await;\n\n        info!(\"low\");\n        led.set_low();\n        Timer::after_millis(300).await;\n    }\n}\n"
  },
  {
    "path": "examples/stm32l0/src/bin/button.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_stm32::gpio::{Input, Level, Output, Pull, Speed};\nuse {defmt_rtt as _, panic_probe as _};\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let p = embassy_stm32::init(Default::default());\n    info!(\"Hello World!\");\n\n    let button = Input::new(p.PB2, Pull::Up);\n    let mut led1 = Output::new(p.PA5, Level::High, Speed::Low);\n    let mut led2 = Output::new(p.PB5, Level::High, Speed::Low);\n\n    loop {\n        if button.is_high() {\n            info!(\"high\");\n            led1.set_high();\n            led2.set_low();\n        } else {\n            info!(\"low\");\n            led1.set_low();\n            led2.set_high();\n        }\n    }\n}\n"
  },
  {
    "path": "examples/stm32l0/src/bin/button_exti.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_stm32::exti::{self, ExtiInput};\nuse embassy_stm32::gpio::Pull;\nuse embassy_stm32::{Config, bind_interrupts, interrupt};\nuse {defmt_rtt as _, panic_probe as _};\n\nbind_interrupts!(\n    pub struct Irqs{\n        EXTI2_3 => exti::InterruptHandler<interrupt::typelevel::EXTI2_3>;\n});\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let config = Config::default();\n    let p = embassy_stm32::init(config);\n\n    let mut button = ExtiInput::new(p.PB2, p.EXTI2, Pull::Up, Irqs);\n\n    info!(\"Press the USER button...\");\n\n    loop {\n        button.wait_for_falling_edge().await;\n        info!(\"Pressed!\");\n        button.wait_for_rising_edge().await;\n        info!(\"Released!\");\n    }\n}\n"
  },
  {
    "path": "examples/stm32l0/src/bin/dds.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse core::option::Option::Some;\n\nuse defmt::info;\nuse defmt_rtt as _; // global logger\nuse embassy_executor::Spawner;\nuse embassy_stm32::gpio::OutputType;\nuse embassy_stm32::rcc::*;\nuse embassy_stm32::time::hz;\nuse embassy_stm32::timer::low_level::{Timer as LLTimer, *};\nuse embassy_stm32::timer::simple_pwm::PwmPin;\nuse embassy_stm32::timer::{Ch3, Channel};\nuse embassy_stm32::{Config, interrupt, pac};\nuse panic_probe as _;\n\nconst DDS_SINE_DATA: [u8; 256] = [\n    0x80, 0x83, 0x86, 0x89, 0x8c, 0x8f, 0x92, 0x95, 0x98, 0x9c, 0x9f, 0xa2, 0xa5, 0xa8, 0xab, 0xae, 0xb0, 0xb3, 0xb6,\n    0xb9, 0xbc, 0xbf, 0xc1, 0xc4, 0xc7, 0xc9, 0xcc, 0xce, 0xd1, 0xd3, 0xd5, 0xd8, 0xda, 0xdc, 0xde, 0xe0, 0xe2, 0xe4,\n    0xe6, 0xe8, 0xea, 0xec, 0xed, 0xef, 0xf0, 0xf2, 0xf3, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfc, 0xfd,\n    0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfd, 0xfc, 0xfc, 0xfb,\n    0xfa, 0xf9, 0xf8, 0xf7, 0xf6, 0xf5, 0xf3, 0xf2, 0xf0, 0xef, 0xed, 0xec, 0xea, 0xe8, 0xe6, 0xe4, 0xe2, 0xe0, 0xde,\n    0xdc, 0xda, 0xd8, 0xd5, 0xd3, 0xd1, 0xce, 0xcc, 0xc9, 0xc7, 0xc4, 0xc1, 0xbf, 0xbc, 0xb9, 0xb6, 0xb3, 0xb0, 0xae,\n    0xab, 0xa8, 0xa5, 0xa2, 0x9f, 0x9c, 0x98, 0x95, 0x92, 0x8f, 0x8c, 0x89, 0x86, 0x83, 0x80, 0x7c, 0x79, 0x76, 0x73,\n    0x70, 0x6d, 0x6a, 0x67, 0x63, 0x60, 0x5d, 0x5a, 0x57, 0x54, 0x51, 0x4f, 0x4c, 0x49, 0x46, 0x43, 0x40, 0x3e, 0x3b,\n    0x38, 0x36, 0x33, 0x31, 0x2e, 0x2c, 0x2a, 0x27, 0x25, 0x23, 0x21, 0x1f, 0x1d, 0x1b, 0x19, 0x17, 0x15, 0x13, 0x12,\n    0x10, 0x0f, 0x0d, 0x0c, 0x0a, 0x09, 0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x03, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01,\n    0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x03, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09,\n    0x0a, 0x0c, 0x0d, 0x0f, 0x10, 0x12, 0x13, 0x15, 0x17, 0x19, 0x1b, 0x1d, 0x1f, 0x21, 0x23, 0x25, 0x27, 0x2a, 0x2c,\n    0x2e, 0x31, 0x33, 0x36, 0x38, 0x3b, 0x3e, 0x40, 0x43, 0x46, 0x49, 0x4c, 0x4f, 0x51, 0x54, 0x57, 0x5a, 0x5d, 0x60,\n    0x63, 0x67, 0x6a, 0x6d, 0x70, 0x73, 0x76, 0x79, 0x7c,\n];\n\n// frequency: 15625/(256/(DDS_INCR/2**24)) = 999,99999Hz\nstatic mut DDS_INCR: u32 = 0x10624DD2;\n\n// fractional phase accumulator\nstatic mut DDS_AKKU: u32 = 0x00000000;\n\n#[interrupt]\nfn TIM2() {\n    unsafe {\n        // get next value of DDS\n        DDS_AKKU = DDS_AKKU.wrapping_add(DDS_INCR);\n        let value = (DDS_SINE_DATA[(DDS_AKKU >> 24) as usize] as u16) << 3;\n\n        // set new output compare value\n        pac::TIM2.ccr(2).modify(|w| w.set_ccr(value));\n\n        // reset interrupt flag\n        pac::TIM2.sr().modify(|r| r.set_uif(false));\n    }\n}\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    info!(\"Hello World!\");\n\n    // configure for 32MHz (HSI16 * 6 / 3)\n    let mut config = Config::default();\n    config.rcc.sys = Sysclk::PLL1_R;\n    config.rcc.hsi = true;\n    config.rcc.pll = Some(Pll {\n        source: PllSource::HSI,\n        div: PllDiv::DIV3,\n        mul: PllMul::MUL6,\n    });\n\n    let p = embassy_stm32::init(config);\n\n    // setup PWM pin in AF mode\n    let _ch3 = PwmPin::<_, Ch3>::new(p.PA2, OutputType::PushPull);\n\n    // initialize timer\n    // we cannot use SimplePWM here because the Time is privately encapsulated\n    let timer = LLTimer::new(p.TIM2);\n\n    // set counting mode\n    timer.set_counting_mode(CountingMode::EdgeAlignedUp);\n\n    // set pwm sample frequency\n    timer.set_frequency(hz(15625), RoundTo::Slower);\n\n    // enable outputs\n    timer.enable_outputs();\n\n    // start timer\n    timer.start();\n\n    // set output compare mode\n    timer.set_output_compare_mode(Channel::Ch3, OutputCompareMode::PwmMode1);\n\n    // set output compare preload\n    timer.set_output_compare_preload(Channel::Ch3, true);\n\n    // set output polarity\n    timer.set_output_polarity(Channel::Ch3, OutputPolarity::ActiveHigh);\n\n    // set compare value\n    timer.set_compare_value(Channel::Ch3, timer.get_max_compare_value() / 2);\n\n    // enable pwm channel\n    timer.enable_channel(Channel::Ch3, true);\n\n    // enable timer interrupts\n    timer.enable_update_interrupt(true);\n    unsafe { cortex_m::peripheral::NVIC::unmask(interrupt::TIM2) };\n\n    async {\n        loop {\n            embassy_time::Timer::after_millis(5000).await;\n        }\n    }\n    .await;\n}\n"
  },
  {
    "path": "examples/stm32l0/src/bin/eeprom.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse defmt::{info, unwrap};\nuse embassy_executor::Spawner;\nuse embassy_stm32::flash::{EEPROM_BASE, EEPROM_SIZE, Flash};\nuse {defmt_rtt as _, panic_probe as _};\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let p = embassy_stm32::init(Default::default());\n\n    info!(\"Hello Eeprom! Start: {}, Size: {}\", EEPROM_BASE, EEPROM_SIZE);\n\n    const ADDR: u32 = 0x0;\n\n    let mut f = Flash::new_blocking(p.FLASH);\n\n    info!(\"Reading...\");\n    let mut buf = [0u8; 8];\n    unwrap!(f.eeprom_read_slice(ADDR, &mut buf));\n    info!(\"Read: {=[u8]:x}\", buf);\n\n    info!(\"Writing...\");\n    unwrap!(f.eeprom_write_slice(ADDR, &[1, 2, 3, 4, 5, 6, 7, 8]));\n\n    info!(\"Reading...\");\n    let mut buf = [0u8; 8];\n    unwrap!(f.eeprom_read_slice(ADDR, &mut buf));\n    info!(\"Read: {=[u8]:x}\", buf);\n    assert_eq!(&buf[..], &[1, 2, 3, 4, 5, 6, 7, 8]);\n}\n"
  },
  {
    "path": "examples/stm32l0/src/bin/flash.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse defmt::{info, unwrap};\nuse embassy_executor::Spawner;\nuse embassy_stm32::flash::Flash;\nuse {defmt_rtt as _, panic_probe as _};\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let p = embassy_stm32::init(Default::default());\n    info!(\"Hello Flash!\");\n\n    const ADDR: u32 = 0x26000;\n\n    let mut f = Flash::new_blocking(p.FLASH).into_blocking_regions().bank1_region;\n\n    info!(\"Reading...\");\n    let mut buf = [0u8; 8];\n    unwrap!(f.blocking_read(ADDR, &mut buf));\n    info!(\"Read: {=[u8]:x}\", buf);\n\n    info!(\"Erasing...\");\n    unwrap!(f.blocking_erase(ADDR, ADDR + 128));\n\n    info!(\"Reading...\");\n    let mut buf = [0u8; 8];\n    unwrap!(f.blocking_read(ADDR, &mut buf));\n    info!(\"Read after erase: {=[u8]:x}\", buf);\n\n    info!(\"Writing...\");\n    unwrap!(f.blocking_write(ADDR, &[1, 2, 3, 4, 5, 6, 7, 8]));\n\n    info!(\"Reading...\");\n    let mut buf = [0u8; 8];\n    unwrap!(f.blocking_read(ADDR, &mut buf));\n    info!(\"Read: {=[u8]:x}\", buf);\n    assert_eq!(&buf[..], &[1, 2, 3, 4, 5, 6, 7, 8]);\n}\n"
  },
  {
    "path": "examples/stm32l0/src/bin/raw_spawn.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse core::mem;\n\nuse cortex_m_rt::entry;\nuse defmt::*;\nuse embassy_executor::Executor;\nuse embassy_executor::raw::TaskStorage;\nuse embassy_time::Timer;\nuse static_cell::StaticCell;\nuse {defmt_rtt as _, panic_probe as _};\n\nasync fn run1() {\n    loop {\n        info!(\"BIG INFREQUENT TICK\");\n        Timer::after_ticks(64000).await;\n    }\n}\n\nasync fn run2() {\n    loop {\n        info!(\"tick\");\n        Timer::after_ticks(13000).await;\n    }\n}\n\nstatic EXECUTOR: StaticCell<Executor> = StaticCell::new();\n\n#[entry]\nfn main() -> ! {\n    info!(\"Hello World!\");\n\n    let _p = embassy_stm32::init(Default::default());\n    let executor = EXECUTOR.init(Executor::new());\n\n    let run1_task = TaskStorage::new();\n    let run2_task = TaskStorage::new();\n\n    // Safety: these variables do live forever if main never returns.\n    let run1_task = unsafe { make_static(&run1_task) };\n    let run2_task = unsafe { make_static(&run2_task) };\n\n    executor.run(|spawner| {\n        spawner.spawn(unwrap!(run1_task.spawn(|| run1())));\n        spawner.spawn(unwrap!(run2_task.spawn(|| run2())));\n    });\n}\n\nunsafe fn make_static<T>(t: &T) -> &'static T {\n    unsafe { mem::transmute(t) }\n}\n"
  },
  {
    "path": "examples/stm32l0/src/bin/spi.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_stm32::gpio::{Level, Output, Speed};\nuse embassy_stm32::spi::{Config, Spi};\nuse embassy_stm32::time::Hertz;\nuse {defmt_rtt as _, panic_probe as _};\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let p = embassy_stm32::init(Default::default());\n    info!(\"Hello World, folks!\");\n\n    let mut spi_config = Config::default();\n    spi_config.frequency = Hertz(1_000_000);\n\n    let mut spi = Spi::new_blocking(p.SPI1, p.PB3, p.PA7, p.PA6, spi_config);\n\n    let mut cs = Output::new(p.PA15, Level::High, Speed::VeryHigh);\n\n    loop {\n        let mut buf = [0x0Au8; 4];\n        cs.set_low();\n        unwrap!(spi.blocking_transfer_in_place(&mut buf));\n        cs.set_high();\n        info!(\"xfer {=[u8]:x}\", buf);\n    }\n}\n"
  },
  {
    "path": "examples/stm32l0/src/bin/tsc_async.rs",
    "content": "// Example of async TSC (Touch Sensing Controller) that lights an LED when touch is detected.\n//\n// This example demonstrates:\n// 1. Configuring a single TSC channel pin\n// 2. Using the blocking TSC interface with polling\n// 3. Waiting for acquisition completion using `poll_for_acquisition`\n// 4. Reading touch values and controlling an LED based on the results\n//\n// Suggested physical setup on STM32L073RZ Nucleo board:\n// - Connect a 1000pF capacitor between pin PA0 and GND. This is your sampling capacitor.\n// - Connect one end of a 1K resistor to pin PA1 and leave the other end loose.\n//   The loose end will act as the touch sensor which will register your touch.\n//\n// The example uses two pins from Group 1 of the TSC on the STM32L073RZ Nucleo board:\n// - PA0 as the sampling capacitor, TSC group 1 IO1 (label A0)\n// - PA1 as the channel pin, TSC group 1 IO2 (label A1)\n//\n// The program continuously reads the touch sensor value:\n// - It starts acquisition, waits for completion using `poll_for_acquisition`, and reads the value.\n// - The LED is turned on when touch is detected (sensor value < 25).\n// - Touch values are logged to the console.\n//\n// Troubleshooting:\n// - If touch is not detected, try adjusting the SENSOR_THRESHOLD value.\n// - Experiment with different values for ct_pulse_high_length, ct_pulse_low_length,\n//   pulse_generator_prescaler, max_count_value, and discharge_delay to optimize sensitivity.\n//\n// Note: Configuration values and sampling capacitor value have been determined experimentally.\n// Optimal values may vary based on your specific hardware setup.\n// Pins have been chosen for their convenient locations on the STM32L073RZ board. Refer to the\n// official relevant STM32 datasheets and nucleo-board user manuals to find suitable\n// alternative pins.\n//\n// Beware for STM32L073RZ nucleo-board, that PA2 and PA3 is used for the uart connection to\n// the programmer chip. If you try to use these two pins for TSC, you will get strange\n// readings, unless you somehow reconfigure/re-wire your nucleo-board.\n// No errors or warnings will be emitted, they will just silently not work as expected.\n// (see nucleo user manual UM1724, Rev 14, page 25)\n\n#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_stm32::gpio::{Level, Output, Speed};\nuse embassy_stm32::tsc::{self, *};\nuse embassy_stm32::{bind_interrupts, peripherals};\nuse embassy_time::Timer;\nuse {defmt_rtt as _, panic_probe as _};\n\nbind_interrupts!(struct Irqs {\n    TSC => InterruptHandler<embassy_stm32::peripherals::TSC>;\n});\nconst SENSOR_THRESHOLD: u16 = 25; // Adjust this value based on your setup\n\n#[embassy_executor::main]\nasync fn main(_spawner: embassy_executor::Spawner) {\n    let device_config = embassy_stm32::Config::default();\n    let context = embassy_stm32::init(device_config);\n\n    let mut pin_group: PinGroupWithRoles<peripherals::TSC, G1> = PinGroupWithRoles::default();\n    pin_group.set_io1::<tsc::pin_roles::Sample>(context.PA0);\n    let sensor = pin_group.set_io2::<tsc::pin_roles::Channel>(context.PA1);\n\n    let tsc_conf = Config {\n        ct_pulse_high_length: ChargeTransferPulseCycle::_4,\n        ct_pulse_low_length: ChargeTransferPulseCycle::_4,\n        spread_spectrum: false,\n        spread_spectrum_deviation: SSDeviation::new(2).unwrap(),\n        spread_spectrum_prescaler: false,\n        pulse_generator_prescaler: PGPrescalerDivider::_16,\n        max_count_value: MaxCount::_255,\n        io_default_mode: false,\n        synchro_pin_polarity: false,\n        acquisition_mode: false,\n        max_count_interrupt: false,\n    };\n\n    let pin_groups: PinGroups<peripherals::TSC> = PinGroups {\n        g1: Some(pin_group.pin_group),\n        ..Default::default()\n    };\n\n    let mut touch_controller = tsc::Tsc::new_async(context.TSC, pin_groups, tsc_conf, Irqs).unwrap();\n\n    // Check if TSC is ready\n    if touch_controller.get_state() != State::Ready {\n        info!(\"TSC not ready!\");\n        return;\n    }\n    info!(\"TSC initialized successfully\");\n\n    // LED2 on the STM32L073RZ nucleo-board (PA5)\n    let mut led = Output::new(context.PA5, Level::Low, Speed::Low);\n\n    let discharge_delay = 5; // ms\n\n    info!(\"Starting touch_controller interface\");\n    loop {\n        touch_controller.set_active_channels_mask(sensor.pin.into());\n        touch_controller.start();\n        touch_controller.pend_for_acquisition().await;\n        touch_controller.discharge_io(true);\n        Timer::after_millis(discharge_delay).await;\n\n        let group_val = touch_controller.group_get_value(sensor.pin.group());\n        info!(\"Touch value: {}\", group_val);\n\n        if group_val < SENSOR_THRESHOLD {\n            led.set_high();\n        } else {\n            led.set_low();\n        }\n\n        Timer::after_millis(100).await;\n    }\n}\n"
  },
  {
    "path": "examples/stm32l0/src/bin/tsc_blocking.rs",
    "content": "// Example of blocking TSC (Touch Sensing Controller) that lights an LED when touch is detected.\n//\n// This example demonstrates:\n// 1. Configuring a single TSC channel pin\n// 2. Using the blocking TSC interface with polling\n// 3. Waiting for acquisition completion using `poll_for_acquisition`\n// 4. Reading touch values and controlling an LED based on the results\n//\n// Suggested physical setup on STM32L073RZ Nucleo board:\n// - Connect a 1000pF capacitor between pin PA0 and GND. This is your sampling capacitor.\n// - Connect one end of a 1K resistor to pin PA1 and leave the other end loose.\n//   The loose end will act as the touch sensor which will register your touch.\n//\n// The example uses two pins from Group 1 of the TSC on the STM32L073RZ Nucleo board:\n// - PA0 as the sampling capacitor, TSC group 1 IO1 (label A0)\n// - PA1 as the channel pin, TSC group 1 IO2 (label A1)\n//\n// The program continuously reads the touch sensor value:\n// - It starts acquisition, waits for completion using `poll_for_acquisition`, and reads the value.\n// - The LED is turned on when touch is detected (sensor value < 25).\n// - Touch values are logged to the console.\n//\n// Troubleshooting:\n// - If touch is not detected, try adjusting the SENSOR_THRESHOLD value.\n// - Experiment with different values for ct_pulse_high_length, ct_pulse_low_length,\n//   pulse_generator_prescaler, max_count_value, and discharge_delay to optimize sensitivity.\n//\n// Note: Configuration values and sampling capacitor value have been determined experimentally.\n// Optimal values may vary based on your specific hardware setup.\n// Pins have been chosen for their convenient locations on the STM32L073RZ board. Refer to the\n// official relevant STM32 datasheets and nucleo-board user manuals to find suitable\n// alternative pins.\n//\n// Beware for STM32L073RZ nucleo-board, that PA2 and PA3 is used for the uart connection to\n// the programmer chip. If you try to use these two pins for TSC, you will get strange\n// readings, unless you somehow reconfigure/re-wire your nucleo-board.\n// No errors or warnings will be emitted, they will just silently not work as expected.\n// (see nucleo user manual UM1724, Rev 14, page 25)\n\n#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_stm32::gpio::{Level, Output, Speed};\nuse embassy_stm32::tsc::{self, *};\nuse embassy_stm32::{mode, peripherals};\nuse embassy_time::Timer;\nuse {defmt_rtt as _, panic_probe as _};\n\nconst SENSOR_THRESHOLD: u16 = 25; // Adjust this value based on your setup\n\n#[embassy_executor::main]\nasync fn main(_spawner: embassy_executor::Spawner) {\n    let device_config = embassy_stm32::Config::default();\n    let context = embassy_stm32::init(device_config);\n\n    let tsc_conf = Config {\n        ct_pulse_high_length: ChargeTransferPulseCycle::_4,\n        ct_pulse_low_length: ChargeTransferPulseCycle::_4,\n        spread_spectrum: false,\n        spread_spectrum_deviation: SSDeviation::new(2).unwrap(),\n        spread_spectrum_prescaler: false,\n        pulse_generator_prescaler: PGPrescalerDivider::_16,\n        max_count_value: MaxCount::_255,\n        io_default_mode: false,\n        synchro_pin_polarity: false,\n        acquisition_mode: false,\n        max_count_interrupt: false,\n    };\n\n    let mut g1: PinGroupWithRoles<peripherals::TSC, G1> = PinGroupWithRoles::default();\n    g1.set_io1::<tsc::pin_roles::Sample>(context.PA0);\n    let tsc_sensor = g1.set_io2::<tsc::pin_roles::Channel>(context.PA1);\n\n    let pin_groups: PinGroups<peripherals::TSC> = PinGroups {\n        g1: Some(g1.pin_group),\n        ..Default::default()\n    };\n\n    let mut touch_controller = tsc::Tsc::new_blocking(context.TSC, pin_groups, tsc_conf).unwrap();\n\n    // Check if TSC is ready\n    if touch_controller.get_state() != State::Ready {\n        crate::panic!(\"TSC not ready!\");\n    }\n    info!(\"TSC initialized successfully\");\n\n    // LED2 on the STM32L073RZ nucleo-board (PA5)\n    let mut led = Output::new(context.PA5, Level::High, Speed::Low);\n\n    // smaller sample capacitor discharge faster and can be used with shorter delay.\n    let discharge_delay = 5; // ms\n\n    // the interval at which the loop polls for new touch sensor values\n    let polling_interval = 100; // ms\n\n    info!(\"polling for touch\");\n    loop {\n        touch_controller.set_active_channels_mask(tsc_sensor.pin.into());\n        touch_controller.start();\n        touch_controller.poll_for_acquisition();\n        touch_controller.discharge_io(true);\n        Timer::after_millis(discharge_delay).await;\n\n        match read_touch_value(&mut touch_controller, tsc_sensor.pin).await {\n            Some(v) => {\n                info!(\"sensor value {}\", v);\n                if v < SENSOR_THRESHOLD {\n                    led.set_high();\n                } else {\n                    led.set_low();\n                }\n            }\n            None => led.set_low(),\n        }\n\n        Timer::after_millis(polling_interval).await;\n    }\n}\n\nconst MAX_GROUP_STATUS_READ_ATTEMPTS: usize = 10;\n\n// attempt to read group status and delay when still ongoing\nasync fn read_touch_value(\n    touch_controller: &mut tsc::Tsc<'_, peripherals::TSC, mode::Blocking>,\n    sensor_pin: tsc::IOPin,\n) -> Option<u16> {\n    for _ in 0..MAX_GROUP_STATUS_READ_ATTEMPTS {\n        match touch_controller.group_get_status(sensor_pin.group()) {\n            GroupStatus::Complete => {\n                return Some(touch_controller.group_get_value(sensor_pin.group()));\n            }\n            GroupStatus::Ongoing => {\n                // if you end up here a lot, then you prob need to increase discharge_delay\n                // or consider changing the code to adjust the discharge_delay dynamically\n                info!(\"Acquisition still ongoing\");\n                Timer::after_millis(1).await;\n            }\n        }\n    }\n    None\n}\n"
  },
  {
    "path": "examples/stm32l0/src/bin/tsc_multipin.rs",
    "content": "// Example of TSC (Touch Sensing Controller) using multiple pins from the same tsc-group.\n//\n// What is special about using multiple TSC pins as sensor channels from the same TSC group,\n// is that only one TSC pin for each TSC group can be acquired and read at the time.\n// To control which channel pins are acquired and read, we must write a mask before initiating an\n// acquisition. To help manage and abstract all this business away, we can organize our channel\n// pins into acquisition banks. Each acquisition bank can contain exactly one channel pin per TSC\n// group and it will contain the relevant mask.\n//\n// This example demonstrates how to:\n// 1. Configure multiple channel pins within a single TSC group\n// 2. Use the set_active_channels_bank method to switch between sets of different channels (acquisition banks)\n// 3. Read and interpret touch values from multiple channels in the same group\n//\n// Suggested physical setup on STM32L073RZ Nucleo board:\n// - Connect a 1000pF capacitor between pin PA0 (label A0) and GND. This is the sampling capacitor for TSC\n//   group 1.\n// - Connect one end of a 1K resistor to pin PA1 (label A1) and leave the other end loose.\n//   The loose end will act as a touch sensor.\n//\n// - Connect a 1000pF capacitor between pin PB3 (label D3) and GND. This is the sampling capacitor for TSC\n//   group 5.\n// - Connect one end of another 1K resistor to pin PB4 and leave the other end loose.\n//   The loose end will act as a touch sensor.\n// - Connect one end of another 1K resistor to pin PB6 and leave the other end loose.\n//   The loose end will act as a touch sensor.\n//\n// The example uses pins from two TSC groups.\n// - PA0 as sampling capacitor, TSC group 1 IO1 (label A0)\n// - PA1 as channel, TSC group 1 IO2 (label A1)\n// - PB3 as sampling capacitor, TSC group 5 IO1 (label D3)\n// - PB4 as channel, TSC group 5 IO2 (label D10)\n// - PB6 as channel, TSC group 5 IO3 (label D5)\n//\n// The pins have been chosen to make it easy to simply add capacitors directly onto the board and\n// connect one leg to GND, and to easily add resistors to the board with no special connectors,\n// breadboards, special wires or soldering required. All you need is the capacitors and resistors.\n//\n// The program reads the designated channel pins and adjusts the LED blinking\n// pattern based on which sensor(s) are touched:\n// - No touch: LED off\n// - one sensor touched: Slow blinking\n// - two sensors touched: Fast blinking\n// - three sensors touched: LED constantly on\n//\n// Troubleshooting:\n// - If touch is not detected, try adjusting the SENSOR_THRESHOLD value.\n// - Experiment with different values for ct_pulse_high_length, ct_pulse_low_length,\n//   pulse_generator_prescaler, max_count_value, and discharge_delay to optimize sensitivity.\n//\n// Note: Configuration values and sampling capacitor value have been determined experimentally.\n// Optimal values may vary based on your specific hardware setup.\n// Pins have been chosen for their convenient locations on the STM32L073RZ board. Refer to the\n// official relevant STM32 datasheets and nucleo-board user manuals to find suitable\n// alternative pins.\n//\n// Beware for STM32L073RZ nucleo-board, that PA2 and PA3 is used for the uart connection to\n// the programmer chip. If you try to use these two pins for TSC, you will get strange\n// readings, unless you somehow reconfigure/re-wire your nucleo-board.\n// No errors or warnings will be emitted, they will just silently not work as expected.\n// (see nucleo user manual UM1724, Rev 14, page 25)\n\n#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_stm32::gpio::{Level, Output, Speed};\nuse embassy_stm32::tsc::{self, *};\nuse embassy_stm32::{bind_interrupts, mode, peripherals};\nuse embassy_time::Timer;\nuse {defmt_rtt as _, panic_probe as _};\n\nbind_interrupts!(struct Irqs {\n    TSC => InterruptHandler<embassy_stm32::peripherals::TSC>;\n});\n\nconst SENSOR_THRESHOLD: u16 = 35;\n\nasync fn acquire_sensors(\n    touch_controller: &mut Tsc<'static, peripherals::TSC, mode::Async>,\n    tsc_acquisition_bank: &AcquisitionBank,\n) {\n    touch_controller.set_active_channels_bank(tsc_acquisition_bank);\n    touch_controller.start();\n    touch_controller.pend_for_acquisition().await;\n    touch_controller.discharge_io(true);\n    let discharge_delay = 5; // ms\n    Timer::after_millis(discharge_delay).await;\n}\n\n#[embassy_executor::main]\nasync fn main(_spawner: embassy_executor::Spawner) {\n    let device_config = embassy_stm32::Config::default();\n    let context = embassy_stm32::init(device_config);\n\n    // ---------- initial configuration of TSC ----------\n    let mut pin_group1: PinGroupWithRoles<peripherals::TSC, G1> = PinGroupWithRoles::default();\n    pin_group1.set_io1::<tsc::pin_roles::Sample>(context.PA0);\n    let tsc_sensor0 = pin_group1.set_io2(context.PA1);\n\n    let mut pin_group5: PinGroupWithRoles<peripherals::TSC, G5> = PinGroupWithRoles::default();\n    pin_group5.set_io1::<tsc::pin_roles::Sample>(context.PB3);\n    let tsc_sensor1 = pin_group5.set_io2(context.PB4);\n    let tsc_sensor2 = pin_group5.set_io3(context.PB6);\n\n    let config = tsc::Config {\n        ct_pulse_high_length: ChargeTransferPulseCycle::_16,\n        ct_pulse_low_length: ChargeTransferPulseCycle::_16,\n        spread_spectrum: false,\n        spread_spectrum_deviation: SSDeviation::new(2).unwrap(),\n        spread_spectrum_prescaler: false,\n        pulse_generator_prescaler: PGPrescalerDivider::_16,\n        max_count_value: MaxCount::_255,\n        io_default_mode: false,\n        synchro_pin_polarity: false,\n        acquisition_mode: false,\n        max_count_interrupt: false,\n    };\n\n    let pin_groups: PinGroups<peripherals::TSC> = PinGroups {\n        g1: Some(pin_group1.pin_group),\n        g5: Some(pin_group5.pin_group),\n        ..Default::default()\n    };\n\n    let mut touch_controller = tsc::Tsc::new_async(context.TSC, pin_groups, config, Irqs).unwrap();\n\n    // ---------- setting up acquisition banks ----------\n    // sensor0 and sensor1 in this example belong to different TSC-groups,\n    // therefore we can acquire and read them both in one go.\n    let bank1 = touch_controller.create_acquisition_bank(AcquisitionBankPins {\n        g1_pin: Some(tsc_sensor0),\n        g5_pin: Some(tsc_sensor1),\n        ..Default::default()\n    });\n    // `sensor1` and `sensor2` belongs to the same TSC-group, therefore we must make sure to\n    // acquire them one at the time. Therefore, we organize them into different acquisition banks.\n    let bank2 = touch_controller.create_acquisition_bank(AcquisitionBankPins {\n        g5_pin: Some(tsc_sensor2),\n        ..Default::default()\n    });\n\n    // Check if TSC is ready\n    if touch_controller.get_state() != State::Ready {\n        crate::panic!(\"TSC not ready!\");\n    }\n\n    info!(\"TSC initialized successfully\");\n\n    // LED2 on the STM32L073RZ nucleo-board (PA5)\n    let mut led = Output::new(context.PA5, Level::High, Speed::Low);\n\n    let mut led_state = false;\n\n    loop {\n        acquire_sensors(&mut touch_controller, &bank1).await;\n        let readings1 = touch_controller.get_acquisition_bank_values(&bank1);\n        acquire_sensors(&mut touch_controller, &bank2).await;\n        let readings2 = touch_controller.get_acquisition_bank_values(&bank2);\n\n        let mut touched_sensors_count = 0;\n        for reading in readings1.iter() {\n            info!(\"{}\", reading);\n            if reading.sensor_value < SENSOR_THRESHOLD {\n                touched_sensors_count += 1;\n            }\n        }\n        for reading in readings2.iter() {\n            info!(\"{}\", reading);\n            if reading.sensor_value < SENSOR_THRESHOLD {\n                touched_sensors_count += 1;\n            }\n        }\n\n        match touched_sensors_count {\n            0 => {\n                // No sensors touched, turn off the LED\n                led.set_low();\n                led_state = false;\n            }\n            1 => {\n                // One sensor touched, blink slowly\n                led_state = !led_state;\n                if led_state {\n                    led.set_high();\n                } else {\n                    led.set_low();\n                }\n                Timer::after_millis(200).await;\n            }\n            2 => {\n                // Two sensors touched, blink faster\n                led_state = !led_state;\n                if led_state {\n                    led.set_high();\n                } else {\n                    led.set_low();\n                }\n                Timer::after_millis(50).await;\n            }\n            3 => {\n                // All three sensors touched, LED constantly on\n                led.set_high();\n                led_state = true;\n            }\n            _ => crate::unreachable!(), // This case should never occur with 3 sensors\n        }\n    }\n}\n"
  },
  {
    "path": "examples/stm32l0/src/bin/usart_dma.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_stm32::usart::{Config, Uart};\nuse embassy_stm32::{bind_interrupts, dma, peripherals, usart};\nuse {defmt_rtt as _, panic_probe as _};\n\nbind_interrupts!(struct Irqs {\n    USART1 => usart::InterruptHandler<peripherals::USART1>;\n    DMA1_CHANNEL2_3 => dma::InterruptHandler<peripherals::DMA1_CH2>, dma::InterruptHandler<peripherals::DMA1_CH3>;\n});\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let p = embassy_stm32::init(Default::default());\n    let mut usart = Uart::new(p.USART1, p.PB7, p.PB6, p.DMA1_CH2, p.DMA1_CH3, Irqs, Config::default()).unwrap();\n\n    usart.write(b\"Hello Embassy World!\\r\\n\").await.unwrap();\n    info!(\"wrote Hello, starting echo\");\n\n    let mut buf = [0; 1];\n    loop {\n        usart.read(&mut buf[..]).await.unwrap();\n        usart.write(&buf[..]).await.unwrap();\n    }\n}\n"
  },
  {
    "path": "examples/stm32l0/src/bin/usart_irq.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_stm32::usart::{BufferedUart, Config};\nuse embassy_stm32::{bind_interrupts, peripherals, usart};\nuse embedded_io_async::{Read, Write};\nuse {defmt_rtt as _, panic_probe as _};\n\nbind_interrupts!(struct Irqs {\n    USART2 => usart::BufferedInterruptHandler<peripherals::USART2>;\n});\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let p = embassy_stm32::init(Default::default());\n    info!(\"Hi!\");\n\n    let mut config = Config::default();\n    config.baudrate = 9600;\n    let mut tx_buf = [0u8; 256];\n    let mut rx_buf = [0u8; 256];\n    let mut usart = BufferedUart::new(p.USART2, p.PA3, p.PA2, &mut tx_buf, &mut rx_buf, Irqs, config).unwrap();\n\n    usart.write_all(b\"Hello Embassy World!\\r\\n\").await.unwrap();\n    info!(\"wrote Hello, starting echo\");\n\n    let mut buf = [0; 4];\n    loop {\n        usart.read_exact(&mut buf[..]).await.unwrap();\n        usart.write_all(&buf[..]).await.unwrap();\n    }\n}\n"
  },
  {
    "path": "examples/stm32l0/src/bin/usb_serial.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse defmt::{panic, *};\nuse embassy_executor::Spawner;\nuse embassy_futures::join::join;\nuse embassy_stm32::usb::{self, Driver, Instance};\nuse embassy_stm32::{bind_interrupts, peripherals};\nuse embassy_usb::Builder;\nuse embassy_usb::class::cdc_acm::{CdcAcmClass, State};\nuse embassy_usb::driver::EndpointError;\nuse {defmt_rtt as _, panic_probe as _};\n\nbind_interrupts!(struct Irqs {\n    USB => usb::InterruptHandler<peripherals::USB>;\n});\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let mut config = embassy_stm32::Config::default();\n    {\n        use embassy_stm32::rcc::*;\n        config.rcc.hsi = true;\n        config.rcc.pll = Some(Pll {\n            source: PllSource::HSI,\n            mul: PllMul::MUL6, // PLLVCO = 16*6 = 96Mhz\n            div: PllDiv::DIV3, // 32Mhz clock (16 * 6 / 3)\n        });\n        config.rcc.sys = Sysclk::PLL1_R;\n    }\n\n    let p = embassy_stm32::init(config);\n\n    info!(\"Hello World!\");\n\n    let driver = Driver::new(p.USB, Irqs, p.PA12, p.PA11);\n\n    let mut config = embassy_usb::Config::new(0xc0de, 0xcafe);\n    config.manufacturer = Some(\"Embassy\");\n    config.product = Some(\"USB-Serial Example\");\n    config.serial_number = Some(\"123456\");\n\n    let mut config_descriptor = [0; 256];\n    let mut bos_descriptor = [0; 256];\n    let mut control_buf = [0; 64];\n\n    let mut state = State::new();\n\n    let mut builder = Builder::new(\n        driver,\n        config,\n        &mut config_descriptor,\n        &mut bos_descriptor,\n        &mut [], // no msos descriptors\n        &mut control_buf,\n    );\n\n    let mut class = CdcAcmClass::new(&mut builder, &mut state, 64);\n\n    let mut usb = builder.build();\n\n    let usb_fut = usb.run();\n\n    let echo_fut = async {\n        loop {\n            class.wait_connection().await;\n            info!(\"Connected\");\n            let _ = echo(&mut class).await;\n            info!(\"Disconnected\");\n        }\n    };\n\n    join(usb_fut, echo_fut).await;\n}\n\nstruct Disconnected {}\n\nimpl From<EndpointError> for Disconnected {\n    fn from(val: EndpointError) -> Self {\n        match val {\n            EndpointError::BufferOverflow => panic!(\"Buffer overflow\"),\n            EndpointError::Disabled => Disconnected {},\n        }\n    }\n}\n\nasync fn echo<'d, T: Instance + 'd>(class: &mut CdcAcmClass<'d, Driver<'d, T>>) -> Result<(), Disconnected> {\n    let mut buf = [0; 64];\n    loop {\n        let n = class.read_packet(&mut buf).await?;\n        let data = &buf[..n];\n        info!(\"data: {:x}\", data);\n        class.write_packet(data).await?;\n    }\n}\n"
  },
  {
    "path": "examples/stm32l1/.cargo/config.toml",
    "content": "[target.'cfg(all(target_arch = \"arm\", target_os = \"none\"))']\n# replace your chip as listed in `probe-rs chip list`\nrunner = \"probe-rs run --chip STM32L151CBxxA\"\n\n[build]\ntarget = \"thumbv7m-none-eabi\"\n\n[env]\nDEFMT_LOG = \"trace\"\n"
  },
  {
    "path": "examples/stm32l1/Cargo.toml",
    "content": "[package]\nedition = \"2024\"\nname = \"embassy-stm32l1-examples\"\nversion = \"0.1.0\"\nlicense = \"MIT OR Apache-2.0\"\npublish = false\n\n[dependencies]\nembassy-sync = { version = \"0.8.0\", path = \"../../embassy-sync\", features = [\"defmt\"] }\nembassy-executor = { version = \"0.10.0\", path = \"../../embassy-executor\", features = [\"platform-cortex-m\", \"executor-thread\", \"defmt\"] }\nembassy-time = { version = \"0.5.1\", path = \"../../embassy-time\", features = [\"defmt\", \"defmt-timestamp-uptime\", \"tick-hz-32_768\"] }\nembassy-stm32 = { version = \"0.6.0\", path = \"../../embassy-stm32\", features = [ \"defmt\", \"stm32l151cb-a\", \"time-driver-any\", \"memory-x\"]  }\nembassy-usb = { version = \"0.6.0\", path = \"../../embassy-usb\", features = [\"defmt\"] }\nembassy-futures = { version = \"0.1.2\", path = \"../../embassy-futures\" }\n\ndefmt = \"1.0.1\"\ndefmt-rtt = \"1.0.0\"\n\ncortex-m = { version = \"0.7.6\", features = [\"inline-asm\", \"critical-section-single-core\"] }\ncortex-m-rt = \"0.7.0\"\nembedded-hal = \"0.2.6\"\npanic-probe = { version = \"1.0.0\", features = [\"print-defmt\"] }\nheapless = { version = \"0.9\", default-features = false }\nembedded-storage = \"0.3.1\"\n\n[profile.release]\ndebug = 2\n\n[package.metadata.embassy]\nbuild = [\n  { target = \"thumbv7m-none-eabi\", artifact-dir = \"out/examples/stm32l1\" }\n]\n"
  },
  {
    "path": "examples/stm32l1/build.rs",
    "content": "fn main() {\n    println!(\"cargo:rustc-link-arg-bins=--nmagic\");\n    println!(\"cargo:rustc-link-arg-bins=-Tlink.x\");\n    println!(\"cargo:rustc-link-arg-bins=-Tdefmt.x\");\n}\n"
  },
  {
    "path": "examples/stm32l1/src/bin/blinky.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_stm32::gpio::{Level, Output, Speed};\nuse embassy_time::Timer;\nuse {defmt_rtt as _, panic_probe as _};\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let p = embassy_stm32::init(Default::default());\n    info!(\"Hello World!\");\n\n    let mut led = Output::new(p.PA12, Level::High, Speed::Low);\n\n    loop {\n        info!(\"high\");\n        led.set_high();\n        Timer::after_millis(1000).await;\n\n        info!(\"low\");\n        led.set_low();\n        Timer::after_millis(1000).await;\n    }\n}\n"
  },
  {
    "path": "examples/stm32l1/src/bin/eeprom.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse defmt::{info, unwrap};\nuse embassy_executor::Spawner;\nuse embassy_stm32::flash::{EEPROM_BASE, EEPROM_SIZE, Flash};\nuse {defmt_rtt as _, panic_probe as _};\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let p = embassy_stm32::init(Default::default());\n\n    info!(\"Hello Eeprom! Start: {}, Size: {}\", EEPROM_BASE, EEPROM_SIZE);\n\n    const ADDR: u32 = 0x0;\n\n    let mut f = Flash::new_blocking(p.FLASH);\n\n    info!(\"Reading...\");\n    let mut buf = [0u8; 8];\n    unwrap!(f.eeprom_read_slice(ADDR, &mut buf));\n    info!(\"Read: {=[u8]:x}\", buf);\n\n    info!(\"Writing...\");\n    unwrap!(f.eeprom_write_slice(ADDR, &[1, 2, 3, 4, 5, 6, 7, 8]));\n\n    info!(\"Reading...\");\n    let mut buf = [0u8; 8];\n    unwrap!(f.eeprom_read_slice(ADDR, &mut buf));\n    info!(\"Read: {=[u8]:x}\", buf);\n    assert_eq!(&buf[..], &[1, 2, 3, 4, 5, 6, 7, 8]);\n}\n"
  },
  {
    "path": "examples/stm32l1/src/bin/flash.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse defmt::{info, unwrap};\nuse embassy_executor::Spawner;\nuse embassy_stm32::flash::Flash;\nuse {defmt_rtt as _, panic_probe as _};\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let p = embassy_stm32::init(Default::default());\n    info!(\"Hello Flash!\");\n\n    const ADDR: u32 = 0x26000;\n\n    let mut f = Flash::new_blocking(p.FLASH).into_blocking_regions().bank1_region;\n\n    info!(\"Reading...\");\n    let mut buf = [0u8; 8];\n    unwrap!(f.blocking_read(ADDR, &mut buf));\n    info!(\"Read: {=[u8]:x}\", buf);\n\n    info!(\"Erasing...\");\n    unwrap!(f.blocking_erase(ADDR, ADDR + 256));\n\n    info!(\"Reading...\");\n    let mut buf = [0u8; 8];\n    unwrap!(f.blocking_read(ADDR, &mut buf));\n    info!(\"Read after erase: {=[u8]:x}\", buf);\n\n    info!(\"Writing...\");\n    unwrap!(f.blocking_write(ADDR, &[1, 2, 3, 4, 5, 6, 7, 8]));\n\n    info!(\"Reading...\");\n    let mut buf = [0u8; 8];\n    unwrap!(f.blocking_read(ADDR, &mut buf));\n    info!(\"Read: {=[u8]:x}\", buf);\n    assert_eq!(&buf[..], &[1, 2, 3, 4, 5, 6, 7, 8]);\n}\n"
  },
  {
    "path": "examples/stm32l1/src/bin/spi.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_stm32::gpio::{Level, Output, Speed};\nuse embassy_stm32::spi::{Config, Spi};\nuse embassy_stm32::time::Hertz;\nuse {defmt_rtt as _, panic_probe as _};\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let p = embassy_stm32::init(Default::default());\n    info!(\"Hello World, folks!\");\n\n    let mut spi_config = Config::default();\n    spi_config.frequency = Hertz(1_000_000);\n\n    let mut spi = Spi::new_blocking(p.SPI1, p.PA5, p.PA7, p.PA6, spi_config);\n\n    let mut cs = Output::new(p.PA4, Level::High, Speed::VeryHigh);\n\n    loop {\n        let mut buf = [0x0Au8; 4];\n        cs.set_low();\n        unwrap!(spi.blocking_transfer_in_place(&mut buf));\n        cs.set_high();\n        info!(\"xfer {=[u8]:x}\", buf);\n    }\n}\n"
  },
  {
    "path": "examples/stm32l1/src/bin/usart.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse cortex_m_rt::entry;\nuse defmt::*;\nuse embassy_stm32::usart::{Config, Uart};\nuse embassy_stm32::{bind_interrupts, peripherals, usart};\nuse {defmt_rtt as _, panic_probe as _};\n\nbind_interrupts!(struct Irqs {\n    USART2 => usart::InterruptHandler<peripherals::USART2>;\n});\n\n#[entry]\nfn main() -> ! {\n    info!(\"Hello World!\");\n\n    let p = embassy_stm32::init(Default::default());\n\n    let config = Config::default();\n    let mut usart = Uart::new_blocking(p.USART2, p.PA3, p.PA2, config).unwrap();\n    let desired_baudrate = 9600; // Default is 115200 and 9600 is used as example\n\n    match usart.set_baudrate(desired_baudrate) {\n        Ok(_) => info!(\"Baud rate set to {}\", desired_baudrate),\n        Err(err) => error!(\"Error setting baudrate to {}: {}\", desired_baudrate, err),\n    }\n\n    unwrap!(usart.blocking_write(b\"Hello Embassy World!\\r\\n\"));\n    info!(\"wrote Hello, starting echo\");\n\n    let mut buf = [0u8; 1];\n    loop {\n        unwrap!(usart.blocking_read(&mut buf));\n        unwrap!(usart.blocking_write(&buf));\n    }\n}\n"
  },
  {
    "path": "examples/stm32l1/src/bin/usb_serial.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse defmt::{panic, *};\nuse embassy_executor::Spawner;\nuse embassy_futures::join::join;\nuse embassy_stm32::usb::{self, Driver, Instance};\nuse embassy_stm32::{bind_interrupts, peripherals};\nuse embassy_usb::Builder;\nuse embassy_usb::class::cdc_acm::{CdcAcmClass, State};\nuse embassy_usb::driver::EndpointError;\nuse {defmt_rtt as _, panic_probe as _};\n\nbind_interrupts!(struct Irqs {\n    USB_LP => usb::InterruptHandler<peripherals::USB>;\n\n});\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let mut config = embassy_stm32::Config::default();\n    {\n        use embassy_stm32::rcc::*;\n        config.rcc.hsi = true;\n        config.rcc.pll = Some(Pll {\n            source: PllSource::HSI,\n            mul: PllMul::MUL6, // PLLVCO = 16*6 = 96Mhz\n            div: PllDiv::DIV3, // 32Mhz clock (16 * 6 / 3)\n        });\n        config.rcc.sys = Sysclk::PLL1_R;\n    }\n\n    let p = embassy_stm32::init(config);\n\n    info!(\"Hello World!\");\n\n    let driver = Driver::new(p.USB, Irqs, p.PA12, p.PA11);\n\n    let mut config = embassy_usb::Config::new(0xc0de, 0xcafe);\n    config.manufacturer = Some(\"Embassy\");\n    config.product = Some(\"USB-Serial Example\");\n    config.serial_number = Some(\"123456\");\n\n    let mut config_descriptor = [0; 256];\n    let mut bos_descriptor = [0; 256];\n    let mut control_buf = [0; 64];\n\n    let mut state = State::new();\n\n    let mut builder = Builder::new(\n        driver,\n        config,\n        &mut config_descriptor,\n        &mut bos_descriptor,\n        &mut [], // no msos descriptors\n        &mut control_buf,\n    );\n\n    let mut class = CdcAcmClass::new(&mut builder, &mut state, 64);\n\n    let mut usb = builder.build();\n\n    let usb_fut = usb.run();\n\n    let echo_fut = async {\n        loop {\n            class.wait_connection().await;\n            info!(\"Connected\");\n            let _ = echo(&mut class).await;\n            info!(\"Disconnected\");\n        }\n    };\n\n    join(usb_fut, echo_fut).await;\n}\n\nstruct Disconnected {}\n\nimpl From<EndpointError> for Disconnected {\n    fn from(val: EndpointError) -> Self {\n        match val {\n            EndpointError::BufferOverflow => panic!(\"Buffer overflow\"),\n            EndpointError::Disabled => Disconnected {},\n        }\n    }\n}\n\nasync fn echo<'d, T: Instance + 'd>(class: &mut CdcAcmClass<'d, Driver<'d, T>>) -> Result<(), Disconnected> {\n    let mut buf = [0; 64];\n    loop {\n        let n = class.read_packet(&mut buf).await?;\n        let data = &buf[..n];\n        info!(\"data: {:x}\", data);\n        class.write_packet(data).await?;\n    }\n}\n"
  },
  {
    "path": "examples/stm32l4/.cargo/config.toml",
    "content": "[target.'cfg(all(target_arch = \"arm\", target_os = \"none\"))']\n# replace STM32F429ZITx with your chip as listed in `probe-rs chip list`\n#runner = \"probe-rs run --chip STM32L475VGT6\"\n#runner = \"probe-rs run --chip STM32L475VG\"\n#runner = \"probe-rs run --chip STM32L4S5QI\"\nrunner = \"probe-rs run --chip STM32L4R5ZITxP\"\n\n[build]\ntarget = \"thumbv7em-none-eabi\"\n\n[env]\nDEFMT_LOG = \"trace\"\n"
  },
  {
    "path": "examples/stm32l4/Cargo.toml",
    "content": "[package]\nedition = \"2024\"\nname = \"embassy-stm32l4-examples\"\nversion = \"0.1.1\"\nlicense = \"MIT OR Apache-2.0\"\npublish = false\n\n[dependencies]\n# Change stm32l4s5vi to your chip name, if necessary.\nembassy-stm32 = { version = \"0.6.0\", path = \"../../embassy-stm32\", features = [ \"defmt\", \"unstable-pac\", \"stm32l4r5zi\", \"memory-x\", \"time-driver-any\", \"exti\", \"chrono\", \"dual-bank\"]  }\nembassy-sync = { version = \"0.8.0\", path = \"../../embassy-sync\", features = [\"defmt\"] }\nembassy-executor = { version = \"0.10.0\", path = \"../../embassy-executor\", features = [\"platform-cortex-m\", \"executor-thread\", \"defmt\"] }\nembassy-time = { version = \"0.5.1\", path = \"../../embassy-time\", features = [\"defmt\", \"defmt-timestamp-uptime\", \"tick-hz-32_768\", ] }\nembassy-embedded-hal = { version = \"0.6.0\", path = \"../../embassy-embedded-hal\" }\nembassy-usb = { version = \"0.6.0\", path = \"../../embassy-usb\", features = [\"defmt\"] }\nembassy-net-adin1110 = { version = \"0.4.0\", path = \"../../embassy-net-adin1110\" }\nembassy-net = { version = \"0.9.0\", path = \"../../embassy-net\", features = [\"defmt\",  \"udp\", \"tcp\", \"dhcpv4\", \"medium-ethernet\"] }\nembassy-futures = { version = \"0.1.2\", path = \"../../embassy-futures\" }\nembedded-io-async = { version = \"0.7.0\", features = [\"defmt\"] }\nembedded-io = { version = \"0.7.1\", features = [\"defmt\"] }\n\ndefmt = \"1.0.1\"\ndefmt-rtt = \"1.0.0\"\n\ncortex-m = { version = \"0.7.6\", features = [\"critical-section-single-core\"] }\ncortex-m-rt = \"0.7.0\"\nembedded-hal = \"0.2.6\"\nembedded-hal-1 = { package = \"embedded-hal\", version = \"1.0\" }\nembedded-hal-async = { version = \"1.0\" }\nembedded-hal-bus = { version = \"0.1\", features = [\"async\"] }\npanic-probe = { version = \"1.0.0\", features = [\"print-defmt\"] }\nheapless = { version = \"0.9\", default-features = false }\nchrono = { version = \"^0.4\", default-features = false }\nstatic_cell = \"2\"\n\nmicromath = \"2.0.0\"\n\n[profile.release]\ndebug = 2\n\n[package.metadata.embassy]\nbuild = [\n  { target = \"thumbv7em-none-eabi\", artifact-dir = \"out/examples/stm32l4\" }\n]\n"
  },
  {
    "path": "examples/stm32l4/README.md",
    "content": "# Examples for STM32L4 family\nRun individual examples with\n```\ncargo run --bin <module-name>\n```\nfor example\n```\ncargo run --bin blinky\n```\n\n## Checklist before running examples\nYou might need to adjust `.cargo/config.toml`, `Cargo.toml` and possibly update pin numbers or peripherals to match the specific MCU or board you are using.\n\n* [ ] Update .cargo/config.toml with the correct probe-rs command to use your specific MCU. For example for L4R5ZI-P it should be `probe-rs run --chip STM32L4R5ZITxP`. (use `probe-rs chip list` to find your chip)\n* [ ] Update Cargo.toml to have the correct `embassy-stm32` feature. For example for L4R5ZI-P it should be `stm32l4r5zi`. Look in the `Cargo.toml` file of the `embassy-stm32` project to find the correct feature flag for your chip.\n* [ ] If your board has a special clock or power configuration, make sure that it is set up appropriately.\n* [ ] If your board has different pin mapping, update any pin numbers or peripherals in the given example code to match your schematic\n\nIf you are unsure, please drop by the Embassy Matrix chat for support, and let us know:\n\n* Which example you are trying to run\n* Which chip and board you are using\n\nEmbassy Chat: https://matrix.to/#/#embassy-rs:matrix.org\n"
  },
  {
    "path": "examples/stm32l4/build.rs",
    "content": "fn main() {\n    println!(\"cargo:rustc-link-arg-bins=--nmagic\");\n    println!(\"cargo:rustc-link-arg-bins=-Tlink.x\");\n    println!(\"cargo:rustc-link-arg-bins=-Tdefmt.x\");\n}\n"
  },
  {
    "path": "examples/stm32l4/src/bin/adc.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_stm32::Config;\nuse embassy_stm32::adc::{Adc, AdcConfig, Resolution, SampleTime};\nuse {defmt_rtt as _, panic_probe as _};\n\n#[cortex_m_rt::entry]\nfn main() -> ! {\n    info!(\"Hello World!\");\n\n    let mut config = Config::default();\n    {\n        use embassy_stm32::rcc::*;\n        config.rcc.mux.adcsel = mux::Adcsel::SYS;\n    }\n    let p = embassy_stm32::init(config);\n\n    let mut config = AdcConfig::default();\n    config.resolution = Some(Resolution::BITS8);\n\n    let mut adc = Adc::new_with_config(p.ADC1, config);\n    //adc.enable_vref();\n\n    let mut channel = p.PC0;\n\n    loop {\n        let v = adc.blocking_read(&mut channel, SampleTime::from_bits(0));\n        info!(\"--> {}\", v);\n    }\n}\n"
  },
  {
    "path": "examples/stm32l4/src/bin/adc_dma.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_stm32::adc::{Adc, AdcChannel, CONTINUOUS, SampleTime};\nuse embassy_stm32::{Config, bind_interrupts, dma, peripherals};\nuse {defmt_rtt as _, panic_probe as _};\n\nconst DMA_BUF_LEN: usize = 512;\n\nbind_interrupts!(struct Irqs {\n    DMA1_CHANNEL1 => dma::InterruptHandler<peripherals::DMA1_CH1>;\n});\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    info!(\"Hello World!\");\n\n    let mut config = Config::default();\n    {\n        use embassy_stm32::rcc::*;\n        config.rcc.mux.adcsel = mux::Adcsel::SYS;\n    }\n    let p = embassy_stm32::init(config);\n\n    let adc = Adc::new(p.ADC1);\n    let adc_pin0 = p.PA0.degrade_adc();\n    let adc_pin1 = p.PA1.degrade_adc();\n    let mut adc_dma_buf = [0u16; DMA_BUF_LEN];\n    let mut measurements = [0u16; DMA_BUF_LEN / 2];\n    let mut ring_buffered_adc = adc.into_ring_buffered(\n        p.DMA1_CH1,\n        &mut adc_dma_buf,\n        Irqs,\n        [(adc_pin0, SampleTime::CYCLES640_5), (adc_pin1, SampleTime::CYCLES640_5)].into_iter(),\n        CONTINUOUS,\n    );\n\n    info!(\"starting measurement loop\");\n    loop {\n        match ring_buffered_adc.read(&mut measurements).await {\n            Ok(_) => {\n                //note: originally there was a print here showing all the samples,\n                //but even that takes too much time and would cause adc overruns\n                info!(\"adc1 first 10 samples: {}\", measurements[0..10]);\n            }\n            Err(e) => {\n                warn!(\"Error: {:?}\", e);\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "examples/stm32l4/src/bin/blinky.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_stm32::gpio::{Level, Output, Speed};\nuse embassy_time::Timer;\nuse {defmt_rtt as _, panic_probe as _};\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let p = embassy_stm32::init(Default::default());\n    info!(\"Hello World!\");\n\n    let mut led = Output::new(p.PB14, Level::High, Speed::Low);\n\n    loop {\n        led.set_high();\n        Timer::after_millis(300).await;\n        led.set_low();\n        Timer::after_millis(300).await;\n    }\n}\n"
  },
  {
    "path": "examples/stm32l4/src/bin/button.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_stm32::gpio::{Input, Pull};\nuse {defmt_rtt as _, panic_probe as _};\n\n#[cortex_m_rt::entry]\nfn main() -> ! {\n    info!(\"Hello World!\");\n\n    let p = embassy_stm32::init(Default::default());\n\n    let button = Input::new(p.PC13, Pull::Up);\n\n    loop {\n        if button.is_high() {\n            info!(\"high\");\n        } else {\n            info!(\"low\");\n        }\n    }\n}\n"
  },
  {
    "path": "examples/stm32l4/src/bin/button_exti.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_stm32::exti::{self, ExtiInput};\nuse embassy_stm32::gpio::Pull;\nuse embassy_stm32::{bind_interrupts, interrupt};\nuse {defmt_rtt as _, panic_probe as _};\n\nbind_interrupts!(\n    pub struct Irqs{\n        EXTI15_10 => exti::InterruptHandler<interrupt::typelevel::EXTI15_10>;\n});\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let p = embassy_stm32::init(Default::default());\n    info!(\"Hello World!\");\n\n    let mut button = ExtiInput::new(p.PC13, p.EXTI13, Pull::Down, Irqs);\n\n    info!(\"Press the USER button...\");\n\n    loop {\n        button.wait_for_falling_edge().await;\n        info!(\"Pressed!\");\n        button.wait_for_rising_edge().await;\n        info!(\"Released!\");\n    }\n}\n"
  },
  {
    "path": "examples/stm32l4/src/bin/can.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_stm32::can::filter::Mask32;\nuse embassy_stm32::can::{\n    Can, Fifo, Frame, Rx0InterruptHandler, Rx1InterruptHandler, SceInterruptHandler, TxInterruptHandler,\n};\nuse embassy_stm32::peripherals::CAN1;\nuse embassy_stm32::{Config, bind_interrupts};\nuse embassy_time::Timer;\nuse {defmt_rtt as _, panic_probe as _};\n\nbind_interrupts!(struct Irqs {\n    CAN1_RX0 => Rx0InterruptHandler<CAN1>;\n    CAN1_RX1 => Rx1InterruptHandler<CAN1>;\n    CAN1_SCE => SceInterruptHandler<CAN1>;\n    CAN1_TX => TxInterruptHandler<CAN1>;\n});\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let p = embassy_stm32::init(Config::default());\n\n    let mut can = Can::new(p.CAN1, p.PA11, p.PA12, Irqs);\n\n    can.modify_filters().enable_bank(0, Fifo::Fifo0, Mask32::accept_all());\n\n    can.modify_config()\n        .set_loopback(true) // Receive own frames\n        .set_silent(true)\n        .set_bitrate(250_000);\n\n    can.enable().await;\n    println!(\"CAN enabled\");\n\n    let mut i = 0;\n    let mut last_read_ts = embassy_time::Instant::now();\n    loop {\n        let frame = Frame::new_extended(0x123456F, &[i; 8]).unwrap();\n        info!(\"Writing frame\");\n\n        _ = can.write(&frame).await;\n\n        match can.read().await {\n            Ok(envelope) => {\n                let (ts, rx_frame) = (envelope.ts, envelope.frame);\n                let delta = (ts - last_read_ts).as_millis();\n                last_read_ts = ts;\n                info!(\n                    \"Rx: {} {:02x} --- {}ms\",\n                    rx_frame.header().len(),\n                    rx_frame.data()[0..rx_frame.header().len() as usize],\n                    delta,\n                )\n            }\n            Err(err) => error!(\"Error in frame: {}\", err),\n        }\n\n        Timer::after_millis(250).await;\n\n        i += 1;\n        if i > 2 {\n            break;\n        }\n    }\n}\n"
  },
  {
    "path": "examples/stm32l4/src/bin/dac.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_stm32::dac::{DacChannel, Value};\nuse {defmt_rtt as _, panic_probe as _};\n\n#[cortex_m_rt::entry]\nfn main() -> ! {\n    let p = embassy_stm32::init(Default::default());\n    info!(\"Hello World!\");\n\n    let mut dac = DacChannel::new_blocking(p.DAC1, p.PA4);\n\n    loop {\n        for v in 0..=255 {\n            dac.set(Value::Bit8(to_sine_wave(v)));\n        }\n    }\n}\n\nuse micromath::F32Ext;\n\nfn to_sine_wave(v: u8) -> u8 {\n    if v >= 128 {\n        // top half\n        let r = 3.14 * ((v - 128) as f32 / 128.0);\n        (r.sin() * 128.0 + 127.0) as u8\n    } else {\n        // bottom half\n        let r = 3.14 + 3.14 * (v as f32 / 128.0);\n        (r.sin() * 128.0 + 127.0) as u8\n    }\n}\n"
  },
  {
    "path": "examples/stm32l4/src/bin/dac_dma.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_stm32::dac::{DacChannel, ValueArray};\nuse embassy_stm32::mode::Async;\nuse embassy_stm32::pac::timer::vals::Mms;\nuse embassy_stm32::peripherals::{DMA1_CH3, DMA1_CH4, TIM6, TIM7};\nuse embassy_stm32::rcc::frequency;\nuse embassy_stm32::time::Hertz;\nuse embassy_stm32::timer::low_level::Timer;\nuse embassy_stm32::triggers::{TIM6_TRGO, TIM7_TRGO};\nuse embassy_stm32::{Peri, bind_interrupts, dma};\nuse micromath::F32Ext;\nuse {defmt_rtt as _, panic_probe as _};\n\nbind_interrupts!(struct Irqs {\n    DMA1_CHANNEL3 => dma::InterruptHandler<DMA1_CH3>;\n    DMA1_CHANNEL4 => dma::InterruptHandler<DMA1_CH4>;\n});\n\n#[embassy_executor::main]\nasync fn main(spawner: Spawner) {\n    let config = embassy_stm32::Config::default();\n\n    // Initialize the board and obtain a Peripherals instance\n    let p: embassy_stm32::Peripherals = embassy_stm32::init(config);\n\n    // Obtain two independent channels (p.DAC1 can only be consumed once, though!)\n    let (dac_ch1, dac_ch2) = embassy_stm32::dac::Dac::new_triggered(\n        p.DAC1, p.DMA1_CH3, p.DMA1_CH4, TIM6_TRGO, TIM7_TRGO, Irqs, p.PA4, p.PA5,\n    )\n    .split();\n\n    spawner.spawn(dac_task1(p.TIM6, dac_ch1).unwrap());\n    spawner.spawn(dac_task2(p.TIM7, dac_ch2).unwrap());\n}\n\n#[embassy_executor::task]\nasync fn dac_task1(tim: Peri<'static, TIM6>, mut dac: DacChannel<'static, Async>) {\n    let data: &[u8; 256] = &calculate_array::<256>();\n\n    info!(\"TIM6 frequency is {}\", frequency::<TIM6>());\n    const FREQUENCY: Hertz = Hertz::hz(200);\n\n    // Compute the reload value such that we obtain the FREQUENCY for the sine\n    let reload: u32 = (frequency::<TIM6>().0 / FREQUENCY.0) / data.len() as u32;\n\n    // Depends on your clock and on the specific chip used, you may need higher or lower values here\n    if reload < 10 {\n        error!(\"Reload value {} below threshold!\", reload);\n    }\n\n    dac.set_triggering(true);\n    dac.enable();\n\n    let tim = Timer::new(tim);\n    tim.regs_basic().arr().modify(|w| w.set_arr(reload as u16 - 1));\n    tim.regs_basic().cr2().modify(|w| w.set_mms(Mms::UPDATE));\n    tim.regs_basic().cr1().modify(|w| {\n        w.set_opm(false);\n        w.set_cen(true);\n    });\n\n    debug!(\n        \"TIM6 Frequency {}, Target Frequency {}, Reload {}, Reload as u16 {}, Samples {}\",\n        frequency::<TIM6>(),\n        FREQUENCY,\n        reload,\n        reload as u16,\n        data.len()\n    );\n\n    // Loop technically not necessary if DMA circular mode is enabled\n    loop {\n        info!(\"Loop DAC1\");\n        dac.write(ValueArray::Bit8(data), true).await;\n    }\n}\n\n#[embassy_executor::task]\nasync fn dac_task2(tim: Peri<'static, TIM7>, mut dac: DacChannel<'static, Async>) {\n    let data: &[u8; 256] = &calculate_array::<256>();\n\n    info!(\"TIM7 frequency is {}\", frequency::<TIM7>());\n\n    const FREQUENCY: Hertz = Hertz::hz(600);\n    let reload: u32 = (frequency::<TIM7>().0 / FREQUENCY.0) / data.len() as u32;\n\n    if reload < 10 {\n        error!(\"Reload value {} below threshold!\", reload);\n    }\n\n    let tim = Timer::new(tim);\n    tim.regs_basic().arr().modify(|w| w.set_arr(reload as u16 - 1));\n    tim.regs_basic().cr2().modify(|w| w.set_mms(Mms::UPDATE));\n    tim.regs_basic().cr1().modify(|w| {\n        w.set_opm(false);\n        w.set_cen(true);\n    });\n\n    dac.set_triggering(true);\n    dac.enable();\n\n    debug!(\n        \"TIM7 Frequency {}, Target Frequency {}, Reload {}, Reload as u16 {}, Samples {}\",\n        frequency::<TIM7>(),\n        FREQUENCY,\n        reload,\n        reload as u16,\n        data.len()\n    );\n\n    dac.write(ValueArray::Bit8(data), true).await;\n}\n\nfn to_sine_wave(v: u8) -> u8 {\n    if v >= 128 {\n        // top half\n        let r = 3.14 * ((v - 128) as f32 / 128.0);\n        (r.sin() * 128.0 + 127.0) as u8\n    } else {\n        // bottom half\n        let r = 3.14 + 3.14 * (v as f32 / 128.0);\n        (r.sin() * 128.0 + 127.0) as u8\n    }\n}\n\nfn calculate_array<const N: usize>() -> [u8; N] {\n    let mut res = [0; N];\n    let mut i = 0;\n    while i < N {\n        res[i] = to_sine_wave(i as u8);\n        i += 1;\n    }\n    res\n}\n"
  },
  {
    "path": "examples/stm32l4/src/bin/flash_async.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse defmt::{info, unwrap};\nuse embassy_executor::Spawner;\nuse embassy_stm32::flash::{FLASH_SIZE, Flash, InterruptHandler};\nuse embassy_stm32::gpio::{AnyPin, Level, Output, Speed};\nuse embassy_stm32::{Peri, bind_interrupts};\nuse embassy_time::Timer;\nuse {defmt_rtt as _, panic_probe as _};\n\nbind_interrupts!(struct Irqs {\n    FLASH => InterruptHandler;\n});\n\n#[embassy_executor::main]\nasync fn main(spawner: Spawner) {\n    let p = embassy_stm32::init(Default::default());\n    info!(\"Hello Flash!\");\n\n    let mut f = Flash::new(p.FLASH, Irqs);\n\n    // Led should blink uninterrupted during erase operation, demonstrating\n    // that the async executor is not blocked.\n    spawner.spawn(unwrap!(blinky(p.PB7.into())));\n\n    // Test on bank 2 so the CPU doesn't stall (code runs from bank 1).\n    // Bank 2 always starts at FLASH_SIZE/2 for dual-bank L4 chips:\n    //   - STM32L496RG (1MB flash): bank 2 offset = 512KB\n    //   - STM32L4R5ZI (2MB flash): bank 2 offset = 1MB\n    // Erase 2 pages (2 * 4KB = 8KB).\n    test_flash(&mut f, (FLASH_SIZE / 2) as u32, 8 * 1024).await;\n}\n\n#[embassy_executor::task]\nasync fn blinky(p: Peri<'static, AnyPin>) {\n    let mut led = Output::new(p, Level::High, Speed::Low);\n\n    loop {\n        info!(\"high\");\n        led.set_high();\n        Timer::after_millis(300).await;\n\n        info!(\"low\");\n        led.set_low();\n        Timer::after_millis(300).await;\n    }\n}\n\nasync fn test_flash(f: &mut Flash<'_>, offset: u32, size: u32) {\n    info!(\"Testing offset: {=u32:#X}, size: {=u32:#X}\", offset, size);\n\n    info!(\"Reading...\");\n    let mut buf = [0u8; 32];\n    unwrap!(f.blocking_read(offset, &mut buf));\n    info!(\"Read: {=[u8]:x}\", buf);\n\n    info!(\"Erasing...\");\n    unwrap!(f.erase(offset, offset + size).await);\n\n    info!(\"Reading after erase...\");\n    let mut buf = [0u8; 32];\n    unwrap!(f.blocking_read(offset, &mut buf));\n    info!(\"Read: {=[u8]:x}\", buf);\n    assert!(buf.iter().all(|&b| b == 0xFF));\n\n    info!(\"Writing...\");\n    // L4 write size is 8 bytes (double-word)\n    unwrap!(\n        f.write(\n            offset,\n            &[\n                1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28,\n                29, 30, 31, 32\n            ]\n        )\n        .await\n    );\n\n    info!(\"Reading after write...\");\n    let mut buf = [0u8; 32];\n    unwrap!(f.blocking_read(offset, &mut buf));\n    info!(\"Read: {=[u8]:x}\", buf);\n    assert_eq!(\n        &buf[..],\n        &[\n            1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29,\n            30, 31, 32\n        ]\n    );\n\n    info!(\"Flash async test passed!\");\n}\n"
  },
  {
    "path": "examples/stm32l4/src/bin/i2c.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_stm32::i2c::I2c;\nuse {defmt_rtt as _, panic_probe as _};\n\nconst ADDRESS: u8 = 0x5F;\nconst WHOAMI: u8 = 0x0F;\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let p = embassy_stm32::init(Default::default());\n    let mut i2c = I2c::new_blocking(p.I2C2, p.PB10, p.PB11, Default::default());\n\n    let mut data = [0u8; 1];\n    unwrap!(i2c.blocking_write_read(ADDRESS, &[WHOAMI], &mut data));\n    info!(\"Whoami: {}\", data[0]);\n}\n"
  },
  {
    "path": "examples/stm32l4/src/bin/i2c_blocking_async.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_embedded_hal::adapter::BlockingAsync;\nuse embassy_executor::Spawner;\nuse embassy_stm32::i2c::I2c;\nuse embedded_hal_async::i2c::I2c as I2cTrait;\nuse {defmt_rtt as _, panic_probe as _};\n\nconst ADDRESS: u8 = 0x5F;\nconst WHOAMI: u8 = 0x0F;\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let p = embassy_stm32::init(Default::default());\n    let i2c = I2c::new_blocking(p.I2C2, p.PB10, p.PB11, Default::default());\n    let mut i2c = BlockingAsync::new(i2c);\n\n    let mut data = [0u8; 1];\n    unwrap!(i2c.write_read(ADDRESS, &[WHOAMI], &mut data).await);\n    info!(\"Whoami: {}\", data[0]);\n}\n"
  },
  {
    "path": "examples/stm32l4/src/bin/i2c_dma.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_stm32::i2c::I2c;\nuse embassy_stm32::{bind_interrupts, dma, i2c, peripherals};\nuse {defmt_rtt as _, panic_probe as _};\n\nconst ADDRESS: u8 = 0x5F;\nconst WHOAMI: u8 = 0x0F;\n\nbind_interrupts!(struct Irqs {\n    I2C2_EV => i2c::EventInterruptHandler<peripherals::I2C2>;\n    I2C2_ER => i2c::ErrorInterruptHandler<peripherals::I2C2>;\n    DMA1_CHANNEL4 => dma::InterruptHandler<peripherals::DMA1_CH4>;\n    DMA1_CHANNEL5 => dma::InterruptHandler<peripherals::DMA1_CH5>;\n});\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let p = embassy_stm32::init(Default::default());\n    let mut i2c = I2c::new(p.I2C2, p.PB10, p.PB11, p.DMA1_CH4, p.DMA1_CH5, Irqs, Default::default());\n\n    let mut data = [0u8; 1];\n    unwrap!(i2c.write_read(ADDRESS, &[WHOAMI], &mut data).await);\n    info!(\"Whoami: {}\", data[0]);\n}\n"
  },
  {
    "path": "examples/stm32l4/src/bin/mco.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_stm32::gpio::{Level, Output, Speed};\nuse embassy_stm32::rcc::{Mco, McoConfig, McoPrescaler, McoSource};\nuse embassy_time::Timer;\nuse {defmt_rtt as _, panic_probe as _};\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let p = embassy_stm32::init(Default::default());\n    info!(\"Hello World!\");\n\n    let config = {\n        let mut config = McoConfig::default();\n        config.prescaler = McoPrescaler::DIV1;\n        config\n    };\n\n    let _mco = Mco::new(p.MCO, p.PA8, McoSource::HSI, config);\n\n    let mut led = Output::new(p.PB14, Level::High, Speed::Low);\n\n    loop {\n        led.set_high();\n        Timer::after_millis(300).await;\n        led.set_low();\n        Timer::after_millis(300).await;\n    }\n}\n"
  },
  {
    "path": "examples/stm32l4/src/bin/rng.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_stm32::rcc::{Pll, PllMul, PllPreDiv, PllQDiv, PllRDiv, PllSource, Sysclk};\nuse embassy_stm32::rng::Rng;\nuse embassy_stm32::{Config, bind_interrupts, peripherals, rng};\nuse {defmt_rtt as _, panic_probe as _};\n\nbind_interrupts!(struct Irqs {\n    RNG => rng::InterruptHandler<peripherals::RNG>;\n});\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let mut config = Config::default();\n    config.rcc.sys = Sysclk::PLL1_R;\n    config.rcc.hsi = true;\n    config.rcc.pll = Some(Pll {\n        source: PllSource::HSI,\n        prediv: PllPreDiv::DIV1,\n        mul: PllMul::MUL18,\n        divp: None,\n        divq: Some(PllQDiv::DIV6), // 48Mhz (16 / 1 * 18 / 6)\n        divr: Some(PllRDiv::DIV4), // sysclk 72Mhz clock (16 / 1 * 18 / 4)\n    });\n    let p = embassy_stm32::init(config);\n\n    info!(\"Hello World!\");\n\n    let mut rng = Rng::new(p.RNG, Irqs);\n\n    let mut buf = [0u8; 16];\n    unwrap!(rng.async_fill_bytes(&mut buf).await);\n    info!(\"random bytes: {:02x}\", buf);\n}\n"
  },
  {
    "path": "examples/stm32l4/src/bin/rtc.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse chrono::{NaiveDate, NaiveDateTime};\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_stm32::Config;\nuse embassy_stm32::rtc::{Rtc, RtcConfig};\nuse embassy_stm32::time::Hertz;\nuse embassy_time::Timer;\nuse {defmt_rtt as _, panic_probe as _};\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let mut config = Config::default();\n    {\n        use embassy_stm32::rcc::*;\n        config.rcc.sys = Sysclk::PLL1_R;\n        config.rcc.hse = Some(Hse {\n            freq: Hertz::mhz(8),\n            mode: HseMode::Oscillator,\n        });\n        config.rcc.pll = Some(Pll {\n            source: PllSource::HSE,\n            prediv: PllPreDiv::DIV1,\n            mul: PllMul::MUL20,\n            divp: None,\n            divq: None,\n            divr: Some(PllRDiv::DIV2), // sysclk 80Mhz clock (8 / 1 * 20 / 2)\n        });\n        config.rcc.ls = LsConfig::default_lse();\n    }\n    let p = embassy_stm32::init(config);\n\n    info!(\"Hello World!\");\n\n    let now = NaiveDate::from_ymd_opt(2020, 5, 15)\n        .unwrap()\n        .and_hms_opt(10, 30, 15)\n        .unwrap();\n\n    let (mut rtc, time_provider) = Rtc::new(p.RTC, RtcConfig::default());\n    info!(\"Got RTC! {:?}\", now.and_utc().timestamp());\n\n    rtc.set_datetime(now.into()).expect(\"datetime not set\");\n\n    // In reality the delay would be much longer\n    Timer::after_millis(20000).await;\n\n    let then: NaiveDateTime = time_provider.now().unwrap().into();\n    info!(\"Got RTC! {:?}\", then.and_utc().timestamp());\n}\n"
  },
  {
    "path": "examples/stm32l4/src/bin/spe_adin1110_http_server.rs",
    "content": "#![no_main]\n#![no_std]\n#![deny(clippy::pedantic)]\n#![allow(clippy::doc_markdown)]\n#![allow(clippy::missing_errors_doc)]\n\n// This example works on a ANALOG DEVICE EVAL-ADIN110EBZ board.\n// Settings switch S201 \"HW CFG\":\n//  - Without SPI CRC: OFF-ON-OFF-OFF-OFF\n//  -    With SPI CRC: ON -ON-OFF-OFF-OFF\n// Settings switch S303 \"uC CFG\":\n// - CFG0: On = static ip, Off = Dhcp\n// - CFG1: Ethernet `FCS` on TX path: On, Off\n// The webserver shows the actual temperature of the onboard i2c temp sensor.\n\nuse core::marker::PhantomData;\nuse core::sync::atomic::{AtomicI32, Ordering};\n\nuse defmt::{Format, error, info, println, unwrap};\nuse defmt_rtt as _; // global logger\nuse embassy_executor::Spawner;\nuse embassy_futures::select::{Either, select};\nuse embassy_futures::yield_now;\nuse embassy_net::tcp::TcpSocket;\nuse embassy_net::{Ipv4Address, Ipv4Cidr, Stack, StackResources, StaticConfigV4};\nuse embassy_net_adin1110::{ADIN1110, Device, Runner};\nuse embassy_stm32::exti::ExtiInput;\nuse embassy_stm32::gpio::{Input, Level, Output, Pull, Speed};\nuse embassy_stm32::i2c::{self, Config as I2C_Config, I2c};\nuse embassy_stm32::mode::Async;\nuse embassy_stm32::rng::{self, Rng};\nuse embassy_stm32::spi::mode::Master;\nuse embassy_stm32::spi::{Config as SPI_Config, Spi};\nuse embassy_stm32::time::Hertz;\nuse embassy_stm32::{bind_interrupts, dma, exti, interrupt, pac, peripherals};\nuse embassy_time::{Delay, Duration, Ticker, Timer};\nuse embedded_hal_async::i2c::I2c as I2cBus;\nuse embedded_hal_bus::spi::ExclusiveDevice;\nuse embedded_io::Write as bWrite;\nuse embedded_io_async::Write;\nuse heapless::Vec;\nuse panic_probe as _;\nuse static_cell::StaticCell;\n\nbind_interrupts!(struct Irqs {\n    I2C3_EV => i2c::EventInterruptHandler<peripherals::I2C3>;\n    I2C3_ER => i2c::ErrorInterruptHandler<peripherals::I2C3>;\n    RNG => rng::InterruptHandler<peripherals::RNG>;\n    EXTI15_10 => exti::InterruptHandler<interrupt::typelevel::EXTI15_10>;\n    DMA1_CHANNEL1 => dma::InterruptHandler<peripherals::DMA1_CH1>;\n    DMA1_CHANNEL2 => dma::InterruptHandler<peripherals::DMA1_CH2>;\n    DMA1_CHANNEL6 => dma::InterruptHandler<peripherals::DMA1_CH6>;\n    DMA1_CHANNEL7 => dma::InterruptHandler<peripherals::DMA1_CH7>;\n});\n\n// Basic settings\n// MAC-address used by the adin1110\nconst MAC: [u8; 6] = [0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff];\n// Static IP settings\nconst IP_ADDRESS: Ipv4Cidr = Ipv4Cidr::new(Ipv4Address::new(192, 168, 1, 5), 24);\n// Listen port for the webserver\nconst HTTP_LISTEN_PORT: u16 = 80;\n\npub type SpeSpi = Spi<'static, Async, Master>;\npub type SpeSpiCs = ExclusiveDevice<SpeSpi, Output<'static>, Delay>;\npub type SpeInt = exti::ExtiInput<'static, Async>;\npub type SpeRst = Output<'static>;\npub type Adin1110T = ADIN1110<SpeSpiCs>;\npub type TempSensI2c = I2c<'static, Async, i2c::Master>;\n\nstatic TEMP: AtomicI32 = AtomicI32::new(0);\n\n#[embassy_executor::main]\nasync fn main(spawner: Spawner) {\n    defmt::println!(\"Start main()\");\n\n    let mut config = embassy_stm32::Config::default();\n    {\n        use embassy_stm32::rcc::*;\n        // 80Mhz clock (Source: 8 / SrcDiv: 1 * PllMul 20 / ClkDiv 2)\n        // 80MHz highest frequency for flash 0 wait.\n        config.rcc.sys = Sysclk::PLL1_R;\n        config.rcc.hse = Some(Hse {\n            freq: Hertz::mhz(8),\n            mode: HseMode::Oscillator,\n        });\n        config.rcc.pll = Some(Pll {\n            source: PllSource::HSE,\n            prediv: PllPreDiv::DIV1,\n            mul: PllMul::MUL20,\n            divp: None,\n            divq: None,\n            divr: Some(PllRDiv::DIV2), // sysclk 80Mhz clock (8 / 1 * 20 / 2)\n        });\n        config.rcc.hsi48 = Some(Default::default()); // needed for RNG\n    }\n\n    let dp = embassy_stm32::init(config);\n\n    let reset_status = pac::RCC.bdcr().read().0;\n    defmt::println!(\"bdcr before: 0x{:X}\", reset_status);\n\n    defmt::println!(\"Setup IO pins\");\n\n    // Setup LEDs\n    let _led_uc1_green = Output::new(dp.PC13, Level::Low, Speed::Low);\n    let mut led_uc2_red = Output::new(dp.PE2, Level::High, Speed::Low);\n    let led_uc3_yellow = Output::new(dp.PE6, Level::High, Speed::Low);\n    let led_uc4_blue = Output::new(dp.PG15, Level::High, Speed::Low);\n\n    // Read the uc_cfg switches\n    let uc_cfg0 = Input::new(dp.PB2, Pull::None);\n    let uc_cfg1 = Input::new(dp.PF11, Pull::None);\n    let _uc_cfg2 = Input::new(dp.PG6, Pull::None);\n    let _uc_cfg3 = Input::new(dp.PG11, Pull::None);\n\n    // Setup I2C pins\n    let temp_sens_i2c = I2c::new(\n        dp.I2C3,\n        dp.PG7,\n        dp.PG8,\n        dp.DMA1_CH6,\n        dp.DMA1_CH7,\n        Irqs,\n        I2C_Config::default(),\n    );\n\n    // Setup IO and SPI for the SPE chip\n    let spe_reset_n = Output::new(dp.PC7, Level::Low, Speed::Low);\n    let spe_cfg0 = Input::new(dp.PC8, Pull::None);\n    let spe_cfg1 = Input::new(dp.PC9, Pull::None);\n    let _spe_ts_capt = Output::new(dp.PC6, Level::Low, Speed::Low);\n\n    let spe_int = ExtiInput::new(dp.PB11, dp.EXTI11, Pull::None, Irqs);\n\n    let spe_spi_cs_n = Output::new(dp.PB12, Level::High, Speed::High);\n    let spe_spi_sclk = dp.PB13;\n    let spe_spi_miso = dp.PB14;\n    let spe_spi_mosi = dp.PB15;\n\n    // Don't turn the clock to high, clock must fit within the system clock as we get a runtime panic.\n    let mut spi_config = SPI_Config::default();\n    spi_config.frequency = Hertz(25_000_000);\n\n    let spe_spi: SpeSpi = Spi::new(\n        dp.SPI2,\n        spe_spi_sclk,\n        spe_spi_mosi,\n        spe_spi_miso,\n        dp.DMA1_CH1,\n        dp.DMA1_CH2,\n        Irqs,\n        spi_config,\n    );\n    let spe_spi = SpeSpiCs::new(spe_spi, spe_spi_cs_n, Delay);\n\n    let cfg0_without_crc = spe_cfg0.is_high();\n    let cfg1_spi_mode = spe_cfg1.is_high();\n    let uc_cfg1_fcs_en = uc_cfg1.is_low();\n\n    defmt::println!(\n        \"ADIN1110: CFG SPI-MODE 1-{}, CRC-bit 0-{} FCS-{}\",\n        cfg1_spi_mode,\n        cfg0_without_crc,\n        uc_cfg1_fcs_en\n    );\n\n    // Check the SPI mode selected with the \"HW CFG\" dip-switch\n    if !cfg1_spi_mode {\n        error!(\n            \"Driver doesn´t support SPI Protolcol \\\"OPEN Alliance\\\".\\nplease use the \\\"Generic SPI\\\"! Turn On \\\"HW CFG\\\": \\\"SPI_CFG1\\\"\"\n        );\n        loop {\n            led_uc2_red.toggle();\n            Timer::after(Duration::from_hz(10)).await;\n        }\n    };\n\n    static STATE: StaticCell<embassy_net_adin1110::State<8, 8>> = StaticCell::new();\n    let state = STATE.init(embassy_net_adin1110::State::<8, 8>::new());\n\n    let (device, runner) = embassy_net_adin1110::new(\n        MAC,\n        state,\n        spe_spi,\n        spe_int,\n        spe_reset_n,\n        !cfg0_without_crc,\n        uc_cfg1_fcs_en,\n    )\n    .await;\n\n    // Start task blink_led\n    spawner.spawn(unwrap!(heartbeat_led(led_uc3_yellow)));\n    // Start task temperature measurement\n    spawner.spawn(unwrap!(temp_task(temp_sens_i2c, led_uc4_blue)));\n    // Start ethernet task\n    spawner.spawn(unwrap!(ethernet_task(runner)));\n\n    let mut rng = Rng::new(dp.RNG, Irqs);\n    // Generate random seed\n    let seed = rng.next_u64();\n\n    let ip_cfg = if uc_cfg0.is_low() {\n        println!(\"Waiting for DHCP...\");\n        let dhcp4_config = embassy_net::DhcpConfig::default();\n        embassy_net::Config::dhcpv4(dhcp4_config)\n    } else {\n        embassy_net::Config::ipv4_static(StaticConfigV4 {\n            address: IP_ADDRESS,\n            gateway: None,\n            dns_servers: Vec::new(),\n        })\n    };\n\n    // Init network stack\n    static RESOURCES: StaticCell<StackResources<3>> = StaticCell::new();\n    let (stack, runner) = embassy_net::new(device, ip_cfg, RESOURCES.init(StackResources::new()), seed);\n\n    // Launch network task\n    spawner.spawn(unwrap!(net_task(runner)));\n\n    let cfg = wait_for_config(stack).await;\n    let local_addr = cfg.address.address();\n\n    // Then we can use it!\n    let mut rx_buffer = [0; 4096];\n    let mut tx_buffer = [0; 4096];\n    let mut mb_buf = [0; 4096];\n    loop {\n        let mut socket = TcpSocket::new(stack, &mut rx_buffer, &mut tx_buffer);\n        socket.set_timeout(Some(Duration::from_secs(1)));\n\n        info!(\"Listening on http://{}:{}...\", local_addr, HTTP_LISTEN_PORT);\n        if let Err(e) = socket.accept(HTTP_LISTEN_PORT).await {\n            defmt::error!(\"accept error: {:?}\", e);\n            continue;\n        }\n\n        loop {\n            let _n = match socket.read(&mut mb_buf).await {\n                Ok(0) => {\n                    defmt::info!(\"read EOF\");\n                    break;\n                }\n                Ok(n) => n,\n                Err(e) => {\n                    defmt::error!(\"{:?}\", e);\n                    break;\n                }\n            };\n            led_uc2_red.set_low();\n\n            let status_line = \"HTTP/1.1 200 OK\";\n            let contents = PAGE;\n            let length = contents.len();\n\n            let _ = write!(\n                &mut mb_buf[..],\n                \"{status_line}\\r\\nContent-Length: {length}\\r\\n\\r\\n{contents}\\r\\n\\0\"\n            );\n            let loc = mb_buf.iter().position(|v| *v == b'#').unwrap();\n\n            let temp = TEMP.load(Ordering::Relaxed);\n            let cel = temp / 1000;\n            let mcel = temp % 1000;\n\n            info!(\"{}.{}\", cel, mcel);\n\n            let _ = write!(&mut mb_buf[loc..loc + 7], \"{cel}.{mcel}\");\n\n            let n = mb_buf.iter().position(|v| *v == 0).unwrap();\n\n            if let Err(e) = socket.write_all(&mb_buf[..n]).await {\n                error!(\"write error: {:?}\", e);\n                break;\n            }\n\n            led_uc2_red.set_high();\n        }\n    }\n}\n\nasync fn wait_for_config(stack: Stack<'static>) -> embassy_net::StaticConfigV4 {\n    loop {\n        if let Some(config) = stack.config_v4() {\n            return config;\n        }\n        yield_now().await;\n    }\n}\n\n#[embassy_executor::task]\nasync fn heartbeat_led(mut led: Output<'static>) {\n    let mut tmr = Ticker::every(Duration::from_hz(3));\n    loop {\n        led.toggle();\n        tmr.next().await;\n    }\n}\n\n// ADT7422\n#[embassy_executor::task]\nasync fn temp_task(temp_dev_i2c: TempSensI2c, mut led: Output<'static>) -> ! {\n    let mut tmr = Ticker::every(Duration::from_hz(1));\n    let mut temp_sens = ADT7422::new(temp_dev_i2c, 0x48).unwrap();\n\n    loop {\n        led.set_low();\n        match select(temp_sens.read_temp(), Timer::after_millis(500)).await {\n            Either::First(i2c_ret) => match i2c_ret {\n                Ok(value) => {\n                    led.set_high();\n                    let temp = i32::from(value);\n                    println!(\"TEMP: {:04x}, {}\", temp, temp * 78 / 10);\n                    TEMP.store(temp * 78 / 10, Ordering::Relaxed);\n                }\n                Err(e) => defmt::println!(\"ADT7422: {}\", e),\n            },\n            Either::Second(_) => println!(\"Timeout\"),\n        }\n\n        tmr.next().await;\n    }\n}\n\n#[embassy_executor::task]\nasync fn ethernet_task(runner: Runner<'static, SpeSpiCs, SpeInt, SpeRst>) -> ! {\n    runner.run().await\n}\n\n#[embassy_executor::task]\nasync fn net_task(mut runner: embassy_net::Runner<'static, Device<'static>>) -> ! {\n    runner.run().await\n}\n\n// same panicking *behavior* as `panic-probe` but doesn't print a panic message\n// this prevents the panic message being printed *twice* when `defmt::panic` is invoked\n#[defmt::panic_handler]\nfn panic() -> ! {\n    cortex_m::asm::udf()\n}\n\n#[allow(non_camel_case_types)]\n#[repr(C)]\npub enum Registers {\n    Temp_MSB = 0x00,\n    Temp_LSB,\n    Status,\n    Cfg,\n    T_HIGH_MSB,\n    T_HIGH_LSB,\n    T_LOW_MSB,\n    T_LOW_LSB,\n    T_CRIT_MSB,\n    T_CRIT_LSB,\n    T_HYST,\n    ID,\n    SW_RESET = 0x2F,\n}\n\npub struct ADT7422<'d, BUS: I2cBus> {\n    addr: u8,\n    phantom: PhantomData<&'d ()>,\n    bus: BUS,\n}\n\n#[derive(Debug, Format, PartialEq, Eq)]\npub enum Error<I2cError: Format> {\n    I2c(I2cError),\n    Address,\n}\n\nimpl<'d, BUS> ADT7422<'d, BUS>\nwhere\n    BUS: I2cBus,\n    BUS::Error: Format,\n{\n    pub fn new(bus: BUS, addr: u8) -> Result<Self, Error<BUS::Error>> {\n        if !(0x48..=0x4A).contains(&addr) {\n            return Err(Error::Address);\n        }\n\n        Ok(Self {\n            bus,\n            phantom: PhantomData,\n            addr,\n        })\n    }\n\n    pub async fn init(&mut self) -> Result<(), Error<BUS::Error>> {\n        let mut cfg = 0b000_0000;\n        // if self.int.is_some() {\n        //     // Set 1 SPS mode\n        //     cfg |= 0b10 << 5;\n        // } else {\n        // One shot mode\n        cfg |= 0b01 << 5;\n        // }\n\n        self.write_cfg(cfg).await\n    }\n\n    pub async fn read(&mut self, reg: Registers) -> Result<u8, Error<BUS::Error>> {\n        let mut buffer = [0u8; 1];\n        self.bus\n            .write_read(self.addr, &[reg as u8], &mut buffer)\n            .await\n            .map_err(Error::I2c)?;\n        Ok(buffer[0])\n    }\n\n    pub async fn write_cfg(&mut self, cfg: u8) -> Result<(), Error<BUS::Error>> {\n        let buf = [Registers::Cfg as u8, cfg];\n        self.bus.write(self.addr, &buf).await.map_err(Error::I2c)\n    }\n\n    pub async fn read_temp(&mut self) -> Result<i16, Error<BUS::Error>> {\n        let mut buffer = [0u8; 2];\n\n        // if let Some(int) = &mut self.int {\n        //     // Wait for interrupt\n        //     int.wait_for_low().await.unwrap();\n        // } else {\n        // Start: One shot\n        let cfg = 0b01 << 5;\n        self.write_cfg(cfg).await?;\n        Timer::after_millis(250).await;\n        self.bus\n            .write_read(self.addr, &[Registers::Temp_MSB as u8], &mut buffer)\n            .await\n            .map_err(Error::I2c)?;\n        Ok(i16::from_be_bytes(buffer))\n    }\n}\n\n// Web page\nconst PAGE: &str = r#\"<!DOCTYPE html>\n<html lang=\"en\">\n  <head>\n    <meta charset=\"utf-8\">\n    <meta http-equiv=\"refresh\" content=\"1\" >\n    <title>ADIN1110 with Rust</title>\n  </head>\n  <body>\n    <p>EVAL-ADIN1110EBZ</p>\n    <table><td>Temp Sensor ADT7422:</td><td> #00.00  &deg;C</td></table>\n  </body>\n</html>\"#;\n"
  },
  {
    "path": "examples/stm32l4/src/bin/spi.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_stm32::gpio::{Level, Output, Speed};\nuse embassy_stm32::spi::{Config, Spi};\nuse embassy_stm32::time::Hertz;\nuse {defmt_rtt as _, panic_probe as _};\n\n#[cortex_m_rt::entry]\nfn main() -> ! {\n    info!(\"Hello World!\");\n\n    let p = embassy_stm32::init(Default::default());\n\n    let mut spi_config = Config::default();\n    spi_config.frequency = Hertz(1_000_000);\n\n    let mut spi = Spi::new_blocking(p.SPI3, p.PC10, p.PC12, p.PC11, spi_config);\n\n    let mut cs = Output::new(p.PE0, Level::High, Speed::VeryHigh);\n\n    loop {\n        let mut buf = [0x0Au8; 4];\n        cs.set_low();\n        unwrap!(spi.blocking_transfer_in_place(&mut buf));\n        cs.set_high();\n        info!(\"xfer {=[u8]:x}\", buf);\n    }\n}\n"
  },
  {
    "path": "examples/stm32l4/src/bin/spi_blocking_async.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_embedded_hal::adapter::BlockingAsync;\nuse embassy_executor::Spawner;\nuse embassy_stm32::gpio::{Input, Level, Output, Pull, Speed};\nuse embassy_stm32::spi::{Config, Spi};\nuse embassy_stm32::time::Hertz;\nuse embedded_hal_async::spi::SpiBus;\nuse {defmt_rtt as _, panic_probe as _};\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let p = embassy_stm32::init(Default::default());\n    info!(\"Hello World!\");\n\n    let mut spi_config = Config::default();\n    spi_config.frequency = Hertz(1_000_000);\n\n    let spi = Spi::new_blocking(p.SPI3, p.PC10, p.PC12, p.PC11, spi_config);\n\n    let mut spi = BlockingAsync::new(spi);\n\n    // These are the pins for the Inventek eS-Wifi SPI Wifi Adapter.\n\n    let _boot = Output::new(p.PB12, Level::Low, Speed::VeryHigh);\n    let _wake = Output::new(p.PB13, Level::Low, Speed::VeryHigh);\n    let mut reset = Output::new(p.PE8, Level::Low, Speed::VeryHigh);\n    let mut cs = Output::new(p.PE0, Level::High, Speed::VeryHigh);\n    let ready = Input::new(p.PE1, Pull::Up);\n\n    cortex_m::asm::delay(100_000);\n    reset.set_high();\n    cortex_m::asm::delay(100_000);\n\n    while ready.is_low() {\n        info!(\"waiting for ready\");\n    }\n\n    let write: [u8; 10] = [0x0A; 10];\n    let mut read: [u8; 10] = [0; 10];\n    cs.set_low();\n    spi.transfer(&mut read, &write).await.ok();\n    cs.set_high();\n    info!(\"xfer {=[u8]:x}\", read);\n}\n"
  },
  {
    "path": "examples/stm32l4/src/bin/spi_dma.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_stm32::gpio::{Input, Level, Output, Pull, Speed};\nuse embassy_stm32::spi::{Config, Spi};\nuse embassy_stm32::time::Hertz;\nuse embassy_stm32::{bind_interrupts, dma, peripherals};\nuse {defmt_rtt as _, panic_probe as _};\n\nbind_interrupts!(struct Irqs {\n    DMA1_CHANNEL1 => dma::InterruptHandler<peripherals::DMA1_CH1>;\n    DMA1_CHANNEL2 => dma::InterruptHandler<peripherals::DMA1_CH2>;\n});\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let p = embassy_stm32::init(Default::default());\n    info!(\"Hello World!\");\n\n    let mut spi_config = Config::default();\n    spi_config.frequency = Hertz(1_000_000);\n\n    let mut spi = Spi::new(p.SPI3, p.PC10, p.PC12, p.PC11, p.DMA1_CH1, p.DMA1_CH2, Irqs, spi_config);\n\n    // These are the pins for the Inventek eS-Wifi SPI Wifi Adapter.\n\n    let _boot = Output::new(p.PB12, Level::Low, Speed::VeryHigh);\n    let _wake = Output::new(p.PB13, Level::Low, Speed::VeryHigh);\n    let mut reset = Output::new(p.PE8, Level::Low, Speed::VeryHigh);\n    let mut cs = Output::new(p.PE0, Level::High, Speed::VeryHigh);\n    let ready = Input::new(p.PE1, Pull::Up);\n\n    cortex_m::asm::delay(100_000);\n    reset.set_high();\n    cortex_m::asm::delay(100_000);\n\n    while ready.is_low() {\n        info!(\"waiting for ready\");\n    }\n\n    let write = [0x0Au8; 10];\n    let mut read = [0u8; 10];\n    cs.set_low();\n    spi.transfer(&mut read, &write).await.ok();\n    cs.set_high();\n    info!(\"xfer {=[u8]:x}\", read);\n}\n"
  },
  {
    "path": "examples/stm32l4/src/bin/tsc_async.rs",
    "content": "// Example of async TSC (Touch Sensing Controller) that lights an LED when touch is detected.\n//\n// This example demonstrates:\n// 1. Configuring a single TSC channel pin\n// 2. Using the async TSC interface\n// 3. Waiting for acquisition completion using `pend_for_acquisition`\n// 4. Reading touch values and controlling an LED based on the results\n//\n// Suggested physical setup on STM32L4R5ZI-P board:\n// - Connect a 1000pF capacitor between pin PB4 (D25) and GND. This is your sampling capacitor.\n// - Connect one end of a 1K resistor to pin PB5 (D21) and leave the other end loose.\n//   The loose end will act as the touch sensor which will register your touch.\n//\n// The example uses two pins from Group 2 of the TSC:\n// - PB4 (D25) as the sampling capacitor, TSC group 2 IO1\n// - PB5 (D21) as the channel pin, TSC group 2 IO2\n//\n// The program continuously reads the touch sensor value:\n// - It starts acquisition, waits for completion using `pend_for_acquisition`, and reads the value.\n// - The LED (connected to PB14) is turned on when touch is detected (sensor value < SENSOR_THRESHOLD).\n// - Touch values are logged to the console.\n//\n// Troubleshooting:\n// - If touch is not detected, try adjusting the SENSOR_THRESHOLD value.\n// - Experiment with different values for ct_pulse_high_length, ct_pulse_low_length,\n//   pulse_generator_prescaler, max_count_value, and discharge_delay to optimize sensitivity.\n//\n// Note: Configuration values and sampling capacitor value have been determined experimentally.\n// Optimal values may vary based on your specific hardware setup.\n\n#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_stm32::gpio::{Level, Output, Speed};\nuse embassy_stm32::tsc::{self, *};\nuse embassy_stm32::{bind_interrupts, peripherals};\nuse embassy_time::Timer;\nuse {defmt_rtt as _, panic_probe as _};\n\nbind_interrupts!(struct Irqs {\n    TSC => InterruptHandler<embassy_stm32::peripherals::TSC>;\n});\nconst SENSOR_THRESHOLD: u16 = 25; // Adjust this value based on your setup\n\n#[embassy_executor::main]\nasync fn main(_spawner: embassy_executor::Spawner) {\n    let device_config = embassy_stm32::Config::default();\n    let context = embassy_stm32::init(device_config);\n\n    let mut pin_group: PinGroupWithRoles<peripherals::TSC, G2> = PinGroupWithRoles::default();\n    // D25\n    pin_group.set_io1::<tsc::pin_roles::Sample>(context.PB4);\n    // D21\n    let tsc_sensor = pin_group.set_io2::<tsc::pin_roles::Channel>(context.PB5);\n\n    let pin_groups: PinGroups<peripherals::TSC> = PinGroups {\n        g2: Some(pin_group.pin_group),\n        ..Default::default()\n    };\n\n    let tsc_conf = Config {\n        ct_pulse_high_length: ChargeTransferPulseCycle::_4,\n        ct_pulse_low_length: ChargeTransferPulseCycle::_4,\n        spread_spectrum: false,\n        spread_spectrum_deviation: SSDeviation::new(2).unwrap(),\n        spread_spectrum_prescaler: false,\n        pulse_generator_prescaler: PGPrescalerDivider::_16,\n        max_count_value: MaxCount::_255,\n        io_default_mode: false,\n        synchro_pin_polarity: false,\n        acquisition_mode: false,\n        max_count_interrupt: false,\n    };\n\n    let mut touch_controller = tsc::Tsc::new_async(context.TSC, pin_groups, tsc_conf, Irqs).unwrap();\n\n    // Check if TSC is ready\n    if touch_controller.get_state() != State::Ready {\n        info!(\"TSC not ready!\");\n        return;\n    }\n    info!(\"TSC initialized successfully\");\n\n    let mut led = Output::new(context.PB14, Level::High, Speed::Low);\n\n    let discharge_delay = 1; // ms\n\n    info!(\"Starting touch_controller interface\");\n    loop {\n        touch_controller.set_active_channels_mask(tsc_sensor.pin.into());\n        touch_controller.start();\n        touch_controller.pend_for_acquisition().await;\n        touch_controller.discharge_io(true);\n        Timer::after_millis(discharge_delay).await;\n\n        let group_val = touch_controller.group_get_value(tsc_sensor.pin.group());\n        info!(\"Touch value: {}\", group_val);\n\n        if group_val < SENSOR_THRESHOLD {\n            led.set_high();\n        } else {\n            led.set_low();\n        }\n\n        Timer::after_millis(100).await;\n    }\n}\n"
  },
  {
    "path": "examples/stm32l4/src/bin/tsc_blocking.rs",
    "content": "// # Example of blocking TSC (Touch Sensing Controller) that lights an LED when touch is detected\n//\n// This example demonstrates how to use the Touch Sensing Controller (TSC) in blocking mode on an STM32L4R5ZI-P board.\n//\n// ## This example demonstrates:\n//\n// 1. Configuring a single TSC channel pin\n// 2. Using the blocking TSC interface with polling\n// 3. Waiting for acquisition completion using `poll_for_acquisition`\n// 4. Reading touch values and controlling an LED based on the results\n//\n// ## Suggested physical setup on STM32L4R5ZI-P board:\n//\n// - Connect a 1000pF capacitor between pin PB4 (D25) and GND. This is your sampling capacitor.\n// - Connect one end of a 1K resistor to pin PB5 (D21) and leave the other end loose.\n//   The loose end will act as the touch sensor which will register your touch.\n//\n// ## Pin Configuration:\n//\n// The example uses two pins from Group 2 of the TSC:\n// - PB4 (D25) as the sampling capacitor, TSC group 2 IO1\n// - PB5 (D21) as the channel pin, TSC group 2 IO2\n//\n// ## Program Behavior:\n//\n// The program continuously reads the touch sensor value:\n// - It starts acquisition, waits for completion using `poll_for_acquisition`, and reads the value.\n// - The LED (connected to PB14) is turned on when touch is detected (sensor value < SENSOR_THRESHOLD).\n// - Touch values are logged to the console.\n//\n// ## Troubleshooting:\n//\n// - If touch is not detected, try adjusting the SENSOR_THRESHOLD value (currently set to 25).\n// - Experiment with different values for ct_pulse_high_length, ct_pulse_low_length,\n//   pulse_generator_prescaler, max_count_value, and discharge_delay to optimize sensitivity.\n// - Be aware that for some boards, there might be overlapping concerns between some pins,\n//   such as UART connections for the programmer. No errors or warnings will be emitted if you\n//   try to use such a pin for TSC, but you may get strange sensor readings.\n//\n// Note: Configuration values and sampling capacitor value have been determined experimentally.\n// Optimal values may vary based on your specific hardware setup. Refer to the official\n// STM32L4R5ZI-P datasheet and user manuals for more information on pin configurations and TSC functionality.\n\n#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_stm32::gpio::{Level, Output, Speed};\nuse embassy_stm32::tsc::{self, *};\nuse embassy_stm32::{mode, peripherals};\nuse embassy_time::Timer;\nuse {defmt_rtt as _, panic_probe as _};\n\nconst SENSOR_THRESHOLD: u16 = 25; // Adjust this value based on your setup\n\n#[embassy_executor::main]\nasync fn main(_spawner: embassy_executor::Spawner) {\n    let device_config = embassy_stm32::Config::default();\n    let context = embassy_stm32::init(device_config);\n\n    let tsc_conf = Config {\n        ct_pulse_high_length: ChargeTransferPulseCycle::_4,\n        ct_pulse_low_length: ChargeTransferPulseCycle::_4,\n        spread_spectrum: false,\n        spread_spectrum_deviation: SSDeviation::new(2).unwrap(),\n        spread_spectrum_prescaler: false,\n        pulse_generator_prescaler: PGPrescalerDivider::_16,\n        max_count_value: MaxCount::_255,\n        io_default_mode: false,\n        synchro_pin_polarity: false,\n        acquisition_mode: false,\n        max_count_interrupt: false,\n    };\n\n    let mut g2: PinGroupWithRoles<peripherals::TSC, G2> = PinGroupWithRoles::default();\n    // D25\n    g2.set_io1::<tsc::pin_roles::Sample>(context.PB4);\n    // D21\n    let tsc_sensor = g2.set_io2::<tsc::pin_roles::Channel>(context.PB5);\n\n    let pin_groups: PinGroups<peripherals::TSC> = PinGroups {\n        g2: Some(g2.pin_group),\n        ..Default::default()\n    };\n\n    let mut touch_controller = tsc::Tsc::new_blocking(context.TSC, pin_groups, tsc_conf).unwrap();\n\n    // Check if TSC is ready\n    if touch_controller.get_state() != State::Ready {\n        crate::panic!(\"TSC not ready!\");\n    }\n    info!(\"TSC initialized successfully\");\n\n    let mut led = Output::new(context.PB14, Level::High, Speed::Low);\n\n    // smaller sample capacitor discharge faster and can be used with shorter delay.\n    let discharge_delay = 5; // ms\n\n    // the interval at which the loop polls for new touch sensor values\n    let polling_interval = 100; // ms\n\n    info!(\"polling for touch\");\n    loop {\n        touch_controller.set_active_channels_mask(tsc_sensor.pin.into());\n        touch_controller.start();\n        touch_controller.poll_for_acquisition();\n        touch_controller.discharge_io(true);\n        Timer::after_millis(discharge_delay).await;\n\n        match read_touch_value(&mut touch_controller, tsc_sensor.pin).await {\n            Some(v) => {\n                info!(\"sensor value {}\", v);\n                if v < SENSOR_THRESHOLD {\n                    led.set_high();\n                } else {\n                    led.set_low();\n                }\n            }\n            None => led.set_low(),\n        }\n\n        Timer::after_millis(polling_interval).await;\n    }\n}\n\nconst MAX_GROUP_STATUS_READ_ATTEMPTS: usize = 10;\n\n// attempt to read group status and delay when still ongoing\nasync fn read_touch_value(\n    touch_controller: &mut tsc::Tsc<'_, peripherals::TSC, mode::Blocking>,\n    sensor_pin: tsc::IOPin,\n) -> Option<u16> {\n    for _ in 0..MAX_GROUP_STATUS_READ_ATTEMPTS {\n        match touch_controller.group_get_status(sensor_pin.group()) {\n            GroupStatus::Complete => {\n                return Some(touch_controller.group_get_value(sensor_pin.group()));\n            }\n            GroupStatus::Ongoing => {\n                // if you end up here a lot, then you prob need to increase discharge_delay\n                // or consider changing the code to adjust the discharge_delay dynamically\n                info!(\"Acquisition still ongoing\");\n                Timer::after_millis(1).await;\n            }\n        }\n    }\n    None\n}\n"
  },
  {
    "path": "examples/stm32l4/src/bin/tsc_multipin.rs",
    "content": "// # Example of TSC (Touch Sensing Controller) using multiple pins from the same TSC group\n//\n// This example demonstrates how to use the Touch Sensing Controller (TSC) with multiple pins, including pins from the same TSC group, on an STM32L4R5ZI-P board.\n//\n// ## Key Concepts\n//\n// - Only one TSC pin for each TSC group can be acquired and read at a time.\n// - To control which channel pins are acquired and read, we must write a mask before initiating an acquisition.\n// - We organize channel pins into acquisition banks to manage this process efficiently.\n// - Each acquisition bank can contain exactly one channel pin per TSC group and will contain the relevant mask.\n//\n// ## This example demonstrates how to:\n//\n// 1. Configure multiple channel pins within a single TSC group\n// 2. Use the set_active_channels_bank method to switch between sets of different channels (acquisition banks)\n// 3. Read and interpret touch values from multiple channels in the same group\n//\n// ## Suggested physical setup on STM32L4R5ZI-P board:\n//\n// - Connect a 1000pF capacitor between pin PB12 (D19) and GND. This is the sampling capacitor for TSC group 1.\n// - Connect one end of a 1K resistor to pin PB13 (D18) and leave the other end loose. This will act as a touch sensor.\n// - Connect a 1000pF capacitor between pin PB4 (D25) and GND. This is the sampling capacitor for TSC group 2.\n// - Connect one end of a 1K resistor to pin PB5 (D22) and leave the other end loose. This will act as a touch sensor.\n// - Connect one end of another 1K resistor to pin PB6 (D71) and leave the other end loose. This will act as a touch sensor.\n//\n// ## Pin Configuration:\n//\n// The example uses pins from two TSC groups:\n//\n// - Group 1:\n//   - PB12 (D19) as sampling capacitor (TSC group 1 IO1)\n//   - PB13 (D18) as channel (TSC group 1 IO2)\n// - Group 2:\n//   - PB4 (D25) as sampling capacitor (TSC group 2 IO1)\n//   - PB5 (D22) as channel (TSC group 2 IO2)\n//   - PB6 (D71) as channel (TSC group 2 IO3)\n//\n// The pins have been chosen for their convenient locations on the STM32L4R5ZI-P board, making it easy to add capacitors and resistors directly to the board without special connectors, breadboards, or soldering.\n//\n// ## Program Behavior:\n//\n// The program reads the designated channel pins and adjusts the LED (connected to PB14) blinking pattern based on which sensor(s) are touched:\n//\n// - No touch: LED off\n// - One sensor touched: Slow blinking\n// - Two sensors touched: Fast blinking\n// - Three sensors touched: LED constantly on\n//\n// ## Troubleshooting:\n//\n// - If touch is not detected, try adjusting the SENSOR_THRESHOLD value (currently set to 20).\n// - Experiment with different values for ct_pulse_high_length, ct_pulse_low_length, pulse_generator_prescaler, max_count_value, and discharge_delay to optimize sensitivity.\n// - Be aware that for some boards there will be overlapping concerns between some pins, for\n//  example UART connection for the programmer to the MCU and a TSC pin. No errors or warning will\n//  be emitted if you try to use such a pin for TSC, but you will get strange sensor readings.\n//\n// Note: Configuration values and sampling capacitor values have been determined experimentally. Optimal values may vary based on your specific hardware setup. Refer to the official STM32L4R5ZI-P datasheet and user manuals for more information on pin configurations and TSC functionality.\n\n#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_stm32::gpio::{Level, Output, Speed};\nuse embassy_stm32::tsc::{self, *};\nuse embassy_stm32::{bind_interrupts, mode, peripherals};\nuse embassy_time::Timer;\nuse {defmt_rtt as _, panic_probe as _};\n\nbind_interrupts!(struct Irqs {\n    TSC => InterruptHandler<embassy_stm32::peripherals::TSC>;\n});\n\nconst SENSOR_THRESHOLD: u16 = 20;\n\nasync fn acquire_sensors(\n    touch_controller: &mut Tsc<'static, peripherals::TSC, mode::Async>,\n    tsc_acquisition_bank: &AcquisitionBank,\n) {\n    touch_controller.set_active_channels_bank(tsc_acquisition_bank);\n    touch_controller.start();\n    touch_controller.pend_for_acquisition().await;\n    touch_controller.discharge_io(true);\n    let discharge_delay = 1; // ms\n    Timer::after_millis(discharge_delay).await;\n}\n\n#[embassy_executor::main]\nasync fn main(_spawner: embassy_executor::Spawner) {\n    let device_config = embassy_stm32::Config::default();\n    let context = embassy_stm32::init(device_config);\n\n    // ---------- initial configuration of TSC ----------\n    let mut g1: PinGroupWithRoles<peripherals::TSC, G1> = PinGroupWithRoles::default();\n    g1.set_io1::<tsc::pin_roles::Sample>(context.PB12);\n    let sensor0 = g1.set_io2::<tsc::pin_roles::Channel>(context.PB13);\n\n    let mut g2: PinGroupWithRoles<peripherals::TSC, G2> = PinGroupWithRoles::default();\n    g2.set_io1::<tsc::pin_roles::Sample>(context.PB4);\n    let sensor1 = g2.set_io2(context.PB5);\n    let sensor2 = g2.set_io3(context.PB6);\n\n    let config = tsc::Config {\n        ct_pulse_high_length: ChargeTransferPulseCycle::_16,\n        ct_pulse_low_length: ChargeTransferPulseCycle::_16,\n        spread_spectrum: false,\n        spread_spectrum_deviation: SSDeviation::new(2).unwrap(),\n        spread_spectrum_prescaler: false,\n        pulse_generator_prescaler: PGPrescalerDivider::_16,\n        max_count_value: MaxCount::_255,\n        io_default_mode: false,\n        synchro_pin_polarity: false,\n        acquisition_mode: false,\n        max_count_interrupt: false,\n    };\n\n    let pin_groups: PinGroups<peripherals::TSC> = PinGroups {\n        g1: Some(g1.pin_group),\n        g2: Some(g2.pin_group),\n        ..Default::default()\n    };\n\n    let mut touch_controller = tsc::Tsc::new_async(context.TSC, pin_groups, config, Irqs).unwrap();\n\n    // ---------- setting up acquisition banks ----------\n    // sensor0 and sensor1 belong to different TSC-groups, therefore we can acquire and\n    // read them both in one go.\n    let bank1 = touch_controller.create_acquisition_bank(AcquisitionBankPins {\n        g1_pin: Some(sensor0),\n        g2_pin: Some(sensor1),\n        ..Default::default()\n    });\n    // `sensor1` and `sensor2` belongs to the same TSC-group, therefore we must make sure to\n    // acquire them one at the time. We do this by organizing them into different acquisition banks.\n    let bank2 = touch_controller.create_acquisition_bank(AcquisitionBankPins {\n        g2_pin: Some(sensor2),\n        ..Default::default()\n    });\n\n    // Check if TSC is ready\n    if touch_controller.get_state() != State::Ready {\n        crate::panic!(\"TSC not ready!\");\n    }\n\n    info!(\"TSC initialized successfully\");\n\n    let mut led = Output::new(context.PB14, Level::High, Speed::Low);\n\n    let mut led_state = false;\n\n    loop {\n        acquire_sensors(&mut touch_controller, &bank1).await;\n        let readings1 = touch_controller.get_acquisition_bank_values(&bank1);\n        acquire_sensors(&mut touch_controller, &bank2).await;\n        let readings2 = touch_controller.get_acquisition_bank_values(&bank2);\n\n        let mut touched_sensors_count = 0;\n        for reading in readings1.iter().chain(readings2.iter()) {\n            info!(\"{}\", reading);\n            if reading.sensor_value < SENSOR_THRESHOLD {\n                touched_sensors_count += 1;\n            }\n        }\n\n        match touched_sensors_count {\n            0 => {\n                // No sensors touched, turn off the LED\n                led.set_low();\n                led_state = false;\n            }\n            1 => {\n                // One sensor touched, blink slowly\n                led_state = !led_state;\n                if led_state {\n                    led.set_high();\n                } else {\n                    led.set_low();\n                }\n                Timer::after_millis(200).await;\n            }\n            2 => {\n                // Two sensors touched, blink faster\n                led_state = !led_state;\n                if led_state {\n                    led.set_high();\n                } else {\n                    led.set_low();\n                }\n                Timer::after_millis(50).await;\n            }\n            3 => {\n                // All three sensors touched, LED constantly on\n                led.set_high();\n                led_state = true;\n            }\n            _ => crate::unreachable!(), // This case should never occur with 3 sensors\n        }\n    }\n}\n"
  },
  {
    "path": "examples/stm32l4/src/bin/usart.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_stm32::usart::{Config, Uart};\nuse embassy_stm32::{bind_interrupts, peripherals, usart};\nuse {defmt_rtt as _, panic_probe as _};\n\nbind_interrupts!(struct Irqs {\n    UART4 => usart::InterruptHandler<peripherals::UART4>;\n});\n\n#[cortex_m_rt::entry]\nfn main() -> ! {\n    info!(\"Hello World!\");\n\n    let p = embassy_stm32::init(Default::default());\n\n    let config = Config::default();\n    let mut usart = Uart::new_blocking(p.UART4, p.PA1, p.PA0, config).unwrap();\n\n    unwrap!(usart.blocking_write(b\"Hello Embassy World!\\r\\n\"));\n    info!(\"wrote Hello, starting echo\");\n\n    let mut buf = [0u8; 1];\n    loop {\n        unwrap!(usart.blocking_read(&mut buf));\n        unwrap!(usart.blocking_write(&buf));\n    }\n}\n"
  },
  {
    "path": "examples/stm32l4/src/bin/usart_dma.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse core::fmt::Write;\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_stm32::usart::{Config, Uart};\nuse embassy_stm32::{bind_interrupts, dma, peripherals, usart};\nuse heapless::String;\nuse {defmt_rtt as _, panic_probe as _};\n\nbind_interrupts!(struct Irqs {\n    UART4 => usart::InterruptHandler<peripherals::UART4>;\n    DMA1_CHANNEL3 => dma::InterruptHandler<peripherals::DMA1_CH3>;\n    DMA1_CHANNEL4 => dma::InterruptHandler<peripherals::DMA1_CH4>;\n});\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let p = embassy_stm32::init(Default::default());\n    info!(\"Hello World!\");\n\n    let config = Config::default();\n    let mut usart = Uart::new(p.UART4, p.PA1, p.PA0, p.DMA1_CH3, p.DMA1_CH4, Irqs, config).unwrap();\n\n    for n in 0u32.. {\n        let mut s: String<128> = String::new();\n        core::write!(&mut s, \"Hello DMA World {}!\\r\\n\", n).unwrap();\n\n        info!(\"Writing...\");\n        usart.write(s.as_bytes()).await.ok();\n\n        info!(\"wrote DMA\");\n    }\n}\n"
  },
  {
    "path": "examples/stm32l4/src/bin/usb_serial.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse defmt::{panic, *};\nuse defmt_rtt as _; // global logger\nuse embassy_executor::Spawner;\nuse embassy_futures::join::join;\nuse embassy_stm32::usb::{Driver, Instance};\nuse embassy_stm32::{Config, bind_interrupts, peripherals, usb};\nuse embassy_usb::Builder;\nuse embassy_usb::class::cdc_acm::{CdcAcmClass, State};\nuse embassy_usb::driver::EndpointError;\nuse panic_probe as _;\n\nbind_interrupts!(struct Irqs {\n    OTG_FS => usb::InterruptHandler<peripherals::USB_OTG_FS>;\n});\n\n// If you are trying this and your USB device doesn't connect, the most\n// common issues are the RCC config and vbus_detection\n//\n// See https://embassy.dev/book/#_the_usb_examples_are_not_working_on_my_board_is_there_anything_else_i_need_to_configure\n// for more information.\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    info!(\"Hello World!\");\n\n    let mut config = Config::default();\n    {\n        use embassy_stm32::rcc::*;\n        config.rcc.hsi48 = Some(Hsi48Config { sync_from_usb: true }); // needed for USB\n        config.rcc.sys = Sysclk::PLL1_R;\n        config.rcc.hsi = true;\n        config.rcc.pll = Some(Pll {\n            source: PllSource::HSI,\n            prediv: PllPreDiv::DIV1,\n            mul: PllMul::MUL10,\n            divp: None,\n            divq: None,\n            divr: Some(PllRDiv::DIV2), // sysclk 80Mhz (16 / 1 * 10 / 2)\n        });\n        config.rcc.mux.clk48sel = mux::Clk48sel::HSI48;\n    }\n    let p = embassy_stm32::init(config);\n\n    // Create the driver, from the HAL.\n    let mut ep_out_buffer = [0u8; 256];\n    let mut config = embassy_stm32::usb::Config::default();\n\n    // Do not enable vbus_detection. This is a safe default that works in all boards.\n    // However, if your USB device is self-powered (can stay powered on if USB is unplugged), you need\n    // to enable vbus_detection to comply with the USB spec. If you enable it, the board\n    // has to support it or USB won't work at all. See docs on `vbus_detection` for details.\n    config.vbus_detection = false;\n\n    let driver = Driver::new_fs(p.USB_OTG_FS, Irqs, p.PA12, p.PA11, &mut ep_out_buffer, config);\n\n    // Create embassy-usb Config\n    let mut config = embassy_usb::Config::new(0xc0de, 0xcafe);\n    config.max_packet_size_0 = 64;\n    config.manufacturer = Some(\"Embassy\");\n    config.product = Some(\"USB-serial example\");\n    config.serial_number = Some(\"12345678\");\n\n    // Create embassy-usb DeviceBuilder using the driver and config.\n    // It needs some buffers for building the descriptors.\n    let mut config_descriptor = [0; 256];\n    let mut bos_descriptor = [0; 256];\n    let mut control_buf = [0; 64];\n\n    let mut state = State::new();\n\n    let mut builder = Builder::new(\n        driver,\n        config,\n        &mut config_descriptor,\n        &mut bos_descriptor,\n        &mut [], // no msos descriptors\n        &mut control_buf,\n    );\n\n    // Create classes on the builder.\n    let mut class = CdcAcmClass::new(&mut builder, &mut state, 64);\n\n    // Build the builder.\n    let mut usb = builder.build();\n\n    // Run the USB device.\n    let usb_fut = usb.run();\n\n    // Do stuff with the class!\n    let echo_fut = async {\n        loop {\n            class.wait_connection().await;\n            info!(\"Connected\");\n            let _ = echo(&mut class).await;\n            info!(\"Disconnected\");\n        }\n    };\n\n    // Run everything concurrently.\n    // If we had made everything `'static` above instead, we could do this using separate tasks instead.\n    join(usb_fut, echo_fut).await;\n}\n\nstruct Disconnected {}\n\nimpl From<EndpointError> for Disconnected {\n    fn from(val: EndpointError) -> Self {\n        match val {\n            EndpointError::BufferOverflow => panic!(\"Buffer overflow\"),\n            EndpointError::Disabled => Disconnected {},\n        }\n    }\n}\n\nasync fn echo<'d, T: Instance + 'd>(class: &mut CdcAcmClass<'d, Driver<'d, T>>) -> Result<(), Disconnected> {\n    let mut buf = [0; 64];\n    loop {\n        let n = class.read_packet(&mut buf).await?;\n        let data = &buf[..n];\n        info!(\"data: {:x}\", data);\n        class.write_packet(data).await?;\n    }\n}\n"
  },
  {
    "path": "examples/stm32l4-rtic/.cargo/config.toml",
    "content": "[target.'cfg(all(target_arch = \"arm\", target_os = \"none\"))']\n# replace STM32F429ZITx with your chip as listed in `probe-rs chip list`\nrunner = \"probe-rs run --chip STM32L4R5ZITxP\"\n\n[build]\ntarget = \"thumbv7em-none-eabi\"\n\n[env]\nDEFMT_LOG = \"trace\"\n"
  },
  {
    "path": "examples/stm32l4-rtic/Cargo.toml",
    "content": "[package]\nedition = \"2024\"\nname = \"stm32l4-rtic\"\nversion = \"0.1.0\"\nlicense = \"MIT OR Apache-2.0\"\npublish = false\n\n[dependencies]\n\n# Change stm32l4r5zi to your chip name, if necessary.\nembassy-stm32 = { version = \"0.6.0\", path = \"../../embassy-stm32\", features = [ \"defmt\", \"unstable-pac\", \"stm32l4r5zi\", \"memory-x\", \"exti\", \"single-bank\"]  }\n\n# rtic = { version = \"2.1.2\", features = [ \"thumbv7-backend\" ] }\nrtic = { version = \"2.2.0\", features = [ \"thumbv7-backend\" ] }\n# rtic-monotonics = { version = \"2.0.3\", features = [ \"cortex-m-systick\", \"stm32_tim2\", \"stm32l4r5zi\" ] }\nrtic-monotonics = { version = \"2.1.0\", features = [ \"cortex-m-systick\", \"stm32_tim2\", \"stm32l4r5zi\" ] }\n\ncortex-m = { version = \"0.7.7\", features = [\"critical-section-single-core\"] }\ncortex-m-rt = \"0.7\"\n\ndefmt = \"1.0.1\"\ndefmt-rtt = \"1.0.0\"\npanic-probe = { version = \"1.0.0\", features = [\"print-defmt\"] }\n\n[profile.release]\ndebug = 2\n\n[package.metadata.embassy]\nbuild = [\n  { target = \"thumbv7em-none-eabi\", artifact-dir = \"out/examples/stm32l4-rtic\" }\n]\n"
  },
  {
    "path": "examples/stm32l4-rtic/README.md",
    "content": "# Examples for STM32L4 family using Embassy with RTIC\nExamples in this project demonstrates how to use Embassy with [RTIC](https://rtic.rs/2/book/en/).\n\n## How to run examples\nRun individual examples with\n```\ncargo run --bin <module-name>\n```\nfor example\n```\ncargo run --bin blinky\n```\n\n## Checklist before running examples\nYou might need to adjust `.cargo/config.toml`, `Cargo.toml` and possibly update pin numbers or peripherals to match the specific MCU or board you are using.\n\n* [ ] Update .cargo/config.toml with the correct probe-rs command to use your specific MCU. For example for L4R5ZI-P it should be `probe-rs run --chip STM32L4R5ZITxP`. (use `probe-rs chip list` to find your chip)\n* [ ] Update Cargo.toml to have the correct `embassy-stm32` feature. For example for L4R5ZI-P it should be `stm32l4r5zi`. Look in the `Cargo.toml` file of the `embassy-stm32` project to find the correct feature flag for your chip.\n* [ ] If your board has a special clock or power configuration, make sure that it is set up appropriately.\n* [ ] If your board has different pin mapping, update any pin numbers or peripherals in the given example code to match your schematic\n\nIf you are unsure, please drop by the Embassy Matrix chat for support, and let us know:\n\n* Which example you are trying to run\n* Which chip and board you are using\n\nEmbassy Chat: https://matrix.to/#/#embassy-rs:matrix.org\n"
  },
  {
    "path": "examples/stm32l4-rtic/build.rs",
    "content": "fn main() {\n    println!(\"cargo:rustc-link-arg-bins=--nmagic\");\n    println!(\"cargo:rustc-link-arg-bins=-Tlink.x\");\n    println!(\"cargo:rustc-link-arg-bins=-Tdefmt.x\");\n}\n"
  },
  {
    "path": "examples/stm32l4-rtic/src/bin/blinky.rs",
    "content": "// A simple example of using RTIC and Embassy together\n#![no_std]\n#![no_main]\n\nuse rtic_monotonics::stm32::prelude::*;\nuse {defmt_rtt as _, panic_probe as _};\n\n// Define rtic-monotick type as `Mono` using macro from rtic_monotonics, using the TIM2 clock and\n// with a tick rate of 1MHz\nstm32_tim2_monotonic!(Mono, 1_000_000);\n\n// setting up the RTIC application with a `software task` using the SPI1 HW interrupt\n#[rtic::app(device = embassy_stm32, peripherals = true, dispatchers = [SPI1])]\nmod app {\n    use defmt::info;\n    use embassy_stm32::gpio::{Level, Output, Speed};\n\n    use super::*;\n\n    #[shared]\n    struct Shared {}\n\n    #[local]\n    struct Local {}\n\n    #[init]\n    fn init(_cx: init::Context) -> (Shared, Local) {\n        let device_config = embassy_stm32::Config::default();\n        let stm32_peripherals = embassy_stm32::init(device_config);\n\n        let timer2_frequency = embassy_stm32::rcc::frequency::<embassy_stm32::peripherals::TIM2>();\n        info!(\"Timer2 clock frequency: {} Hz\", timer2_frequency);\n\n        // start the monotick timer\n        Mono::start(timer2_frequency.0);\n\n        let led = Output::new(stm32_peripherals.PB14, Level::High, Speed::Low);\n\n        blink::spawn(led).ok();\n\n        (Shared {}, Local {})\n    }\n\n    // Using a 'software task' to blink the LED\n    #[task(priority = 1)]\n    async fn blink(_cx: blink::Context, mut led: Output<'static>) {\n        loop {\n            led.toggle();\n            Mono::delay(100.millis()).await;\n        }\n    }\n}\n"
  },
  {
    "path": "examples/stm32l4-rtic/src/bin/blinky_timer_interrupt.rs",
    "content": "// A simple example of using RTIC and Embassy together with a timer HW interrupt\n#![no_std]\n#![no_main]\n\nuse {defmt_rtt as _, panic_probe as _};\n\n#[rtic::app(device = embassy_stm32, peripherals = true)]\nmod app {\n    use embassy_stm32::gpio::{Level, Output, Speed};\n    use embassy_stm32::pac;\n    use embassy_stm32::peripherals::TIM2;\n    use embassy_stm32::time::Hertz;\n    use embassy_stm32::timer::low_level::{RoundTo, Timer};\n\n    #[shared]\n    struct Shared {}\n\n    #[local]\n    struct Local {\n        led: Output<'static>,\n        timer: Timer<'static, TIM2>,\n    }\n\n    #[init]\n    fn init(_ctx: init::Context) -> (Shared, Local) {\n        let device_config = embassy_stm32::Config::default();\n        let stm32_peripherals = embassy_stm32::init(device_config);\n\n        // Configure LED\n        let led = Output::new(stm32_peripherals.PB14, Level::High, Speed::Low);\n\n        // setup hw timer interrupt using the low_level timer API\n        let timer = Timer::new(stm32_peripherals.TIM2);\n        // 10Hz = 10 times per second = every 100ms\n        timer.set_frequency(Hertz(10), RoundTo::Slower);\n        timer.enable_update_interrupt(true);\n        timer.set_autoreload_preload(true);\n        timer.start();\n\n        unsafe {\n            // enable the timer interrupt in NVIC\n            cortex_m::peripheral::NVIC::unmask(pac::Interrupt::TIM2);\n        }\n\n        (Shared {}, Local { led, timer })\n    }\n\n    // using a 'hardware task' to trigger blinking of an LED\n    #[task(binds = TIM2, local = [led, timer])]\n    fn tim2_handler(ctx: tim2_handler::Context) {\n        // Clear the interrupt flag\n        ctx.local.timer.clear_update_interrupt();\n        ctx.local.led.toggle();\n    }\n}\n"
  },
  {
    "path": "examples/stm32l4-rtic/src/bin/button_exti.rs",
    "content": "// An example of using RTIC and Embassy together with HW EXTI interrupts\n#![no_std]\n#![no_main]\n\nuse {defmt_rtt as _, panic_probe as _};\n\n#[rtic::app(device = embassy_stm32, peripherals = true)]\nmod app {\n    use defmt::info;\n    use embassy_stm32::exti::blocking::ExtiGroupMask;\n    use embassy_stm32::exti::{ExtiInput, TriggerEdge};\n    use embassy_stm32::gpio::{Level, Output, Pull, Speed};\n    use embassy_stm32::mode::Blocking;\n\n    #[shared]\n    struct Shared {\n        led1: Output<'static>,\n        led2: Output<'static>,\n    }\n\n    #[local]\n    struct Local {\n        button1: ExtiInput<'static, Blocking>,\n        button2: ExtiInput<'static, Blocking>,\n        button3: ExtiInput<'static, Blocking>,\n        exti_pending_mask_15_10: ExtiGroupMask, // Pre-computed mask for bulk clearing\n    }\n\n    #[init]\n    fn init(_ctx: init::Context) -> (Shared, Local) {\n        let device_config = embassy_stm32::Config::default();\n        let stm32_peripherals = embassy_stm32::init(device_config);\n\n        // setting up the user-leds on the nucleo board\n        let led1 = Output::new(stm32_peripherals.PC7, Level::Low, Speed::Low);\n        let led2 = Output::new(stm32_peripherals.PB7, Level::Low, Speed::Low);\n\n        // setting up the user-button on the nucleo board (shared exti irq line 10-15)\n        let button1 = ExtiInput::new_blocking(\n            stm32_peripherals.PC13,\n            stm32_peripherals.EXTI13,\n            Pull::Down,\n            TriggerEdge::Rising,\n        );\n\n        // setting up an external button connected to the nucleo board (shared exti irq line 10-15)\n        let button2 = ExtiInput::new_blocking(\n            stm32_peripherals.PB10,\n            stm32_peripherals.EXTI10,\n            Pull::Up,\n            TriggerEdge::Falling,\n        );\n\n        // Computing the mask for clearing pending interrupts on exti 15_10\n        let exti_pending_mask_15_10 = ExtiGroupMask::new(&[&button1, &button2]);\n\n        // setting up an external button connected to the nucleo board (shared exti irq line 5-9)\n        let button3 = ExtiInput::new_blocking(\n            stm32_peripherals.PC8,\n            stm32_peripherals.EXTI8,\n            Pull::Up,\n            TriggerEdge::Any,\n        );\n\n        (\n            Shared { led1, led2 },\n            Local {\n                button1,\n                button2,\n                button3,\n                exti_pending_mask_15_10,\n            },\n        )\n    }\n\n    // Setting up `hardware task` to handle interrupts in the exti group 15-10\n    #[task(binds = EXTI15_10, local = [button1, button2, exti_pending_mask_15_10], shared = [led1])]\n    fn button1_exti_handler(mut ctx: button1_exti_handler::Context) {\n        if ctx.local.button1.is_pending() {\n            info!(\"button1 triggered\");\n        }\n        if ctx.local.button2.is_pending() {\n            info!(\"button2 triggered\");\n        }\n\n        ctx.local.exti_pending_mask_15_10.clear();\n\n        ctx.shared.led1.lock(|led| led.toggle());\n    }\n\n    // Setting up `hardware task` to handle interrupts in the exti group 9-5\n    #[task(binds = EXTI9_5, local = [button3], shared = [led2])]\n    fn button3_exti_handler(mut ctx: button3_exti_handler::Context) {\n        let button = ctx.local.button3;\n        // clear the interrupt flag\n        button.clear_pending();\n\n        let l = button.get_level();\n        info!(\"button3 triggered, {}\", l);\n\n        ctx.shared.led2.lock(|led| led.toggle());\n    }\n}\n"
  },
  {
    "path": "examples/stm32l432/.cargo/config.toml",
    "content": "[target.'cfg(all(target_arch = \"arm\", target_os = \"none\"))']\n# replace STM32F429ZITx with your chip as listed in `probe-rs chip list`\n#runner = \"probe-rs run --chip STM32L475VGT6\"\n#runner = \"probe-rs run --chip STM32L475VG\"\n#runner = \"probe-rs run --chip STM32L4S5QI\"\nrunner = \"probe-rs run --chip STM32L432KCUx --connect-under-reset --speed 3300\"\n\n\n[build]\ntarget = \"thumbv7em-none-eabi\"\n\n[env]\nDEFMT_LOG = \"trace\"\n"
  },
  {
    "path": "examples/stm32l432/Cargo.toml",
    "content": "[package]\nedition = \"2024\"\nname = \"embassy-stm32l432-examples\"\nversion = \"0.1.1\"\nlicense = \"MIT OR Apache-2.0\"\npublish = false\n\n[dependencies]\n# Change stm32l4s5vi to your chip name, if necessary.\nembassy-stm32 = { version = \"0.6.0\", path = \"../../embassy-stm32\", features = [ \"defmt\", \"unstable-pac\", \"stm32l432kc\", \"memory-x\", \"time-driver-any\", \"exti\", \"chrono\"] }\nembassy-sync = { version = \"0.8.0\", path = \"../../embassy-sync\", features = [ \"defmt\" ] }\nembassy-executor = { version = \"0.10.0\", path = \"../../embassy-executor\", features = [ \"platform-cortex-m\", \"executor-thread\", \"defmt\" ] }\nembassy-time = { version = \"0.5.1\", path = \"../../embassy-time\", features = [ \"defmt\", \"defmt-timestamp-uptime\", \"tick-hz-32_768\" ] }\ndefmt = \"1.0.1\"\ndefmt-rtt = \"1.0.0\"\n\ncortex-m = { version = \"0.7.6\", features = [\"critical-section-single-core\"] }\ncortex-m-rt = \"0.7.0\"\nembedded-hal = \"0.2.6\"\nembedded-hal-1 = { package = \"embedded-hal\", version = \"1.0\" }\nembedded-hal-async = { version = \"1.0\" }\nembedded-hal-bus = { version = \"0.1\", features = [\"async\"] }\npanic-probe = { version = \"1.0.0\", features = [\"print-defmt\"] }\n\n[profile.release]\ndebug = 2\n\n[[bin]]\nname = \"qspi_mmap\"\npath = \"src/bin/qspi_mmap.rs\"\ntest = false\n\n[package.metadata.embassy]\nbuild = [\n  { target = \"thumbv7em-none-eabi\", artifact-dir = \"out/examples/stm32l432\" }\n]\n"
  },
  {
    "path": "examples/stm32l432/README.md",
    "content": "\n# Examples for STM32L432 family\n\nExamples in this repo should work with [NUCLEO-L432KC](https://www.st.com/en/evaluation-tools/nucleo-l432kc.html) board.\n\nRun individual examples with\n```\ncargo run --bin <module-name>\n```\nfor example\n```\ncargo run --bin blinky\n```\n\n\n\n## Checklist before running examples\nYou might need to adjust `.cargo/config.toml`, `Cargo.toml` and possibly update pin numbers or peripherals to match the specific MCU or board you are using.\n\n* [ ] Update .cargo/config.toml with the correct probe-rs command to use your specific MCU. For example for L432KCU6 it should be `probe-rs run --chip STM32L432KCUx`. (use `probe-rs chip list` to find your chip)\n* [ ] Update Cargo.toml to have the correct `embassy-stm32` feature. For example for L432KCU6 it should be `stm32l432kc`. Look in the `Cargo.toml` file of the `embassy-stm32` project to find the correct feature flag for your chip.\n* [ ] If your board has a special clock or power configuration, make sure that it is set up appropriately.\n* [ ] If your board has different pin mapping, update any pin numbers or peripherals in the given example code to match your schematic\n\nIf you are unsure, please drop by the Embassy Matrix chat for support, and let us know:\n\n* Which example you are trying to run\n* Which chip and board you are using\n\nEmbassy Chat: https://matrix.to/#/#embassy-rs:matrix.org\n"
  },
  {
    "path": "examples/stm32l432/build.rs",
    "content": "fn main() {\n    println!(\"cargo:rustc-link-arg-bins=--nmagic\");\n    println!(\"cargo:rustc-link-arg-bins=-Tlink.x\");\n    println!(\"cargo:rustc-link-arg-bins=-Tdefmt.x\");\n}\n"
  },
  {
    "path": "examples/stm32l432/src/bin/qspi_mmap.rs",
    "content": "#![no_std]\n#![no_main]\n#![allow(dead_code)]\n/// This example demonstrates how to use the QSPI peripheral in both indirect-mode and memory-mapped mode.\n/// If you want to test this example, please pay attention to flash pins and check flash device datasheet\n/// to make sure operations in this example are compatible with your device, especially registers I/O operations.\nuse defmt::info;\nuse embassy_stm32::qspi::enums::{\n    AddressSize, ChipSelectHighTime, DummyCycles, FIFOThresholdLevel, MemorySize, QspiWidth, SampleShifting,\n};\nuse embassy_stm32::qspi::{self, Instance, TransferConfig};\nuse embassy_stm32::{bind_interrupts, dma, mode, peripherals};\npub struct FlashMemory<I: Instance> {\n    qspi: qspi::Qspi<'static, I, mode::Async>,\n}\nuse embassy_executor::Spawner;\nuse embassy_time::Timer;\nuse {defmt_rtt as _, panic_probe as _};\n\nconst MEMORY_PAGE_SIZE: usize = 256;\nconst CMD_READ_SR: u8 = 0x05;\nconst CMD_READ_CR: u8 = 0x35;\nconst CMD_QUAD_READ: u8 = 0x6B;\nconst CMD_QUAD_WRITE_PG: u8 = 0x32;\nconst CMD_READ_ID: u8 = 0x9F;\nconst CMD_READ_MID: u8 = 0x90;\nconst CMD_READ_UUID: u8 = 0x4B;\nconst CMD_ENABLE_RESET: u8 = 0x66;\nconst CMD_RESET: u8 = 0x99;\nconst CMD_WRITE_ENABLE: u8 = 0x06;\nconst CMD_SECTOR_ERASE: u8 = 0x20;\n\nconst CMD_WRITE_SR: u8 = 0x01;\n\nimpl<I: Instance> FlashMemory<I> {\n    pub fn new(qspi: qspi::Qspi<'static, I, mode::Async>) -> Self {\n        let mut memory = Self { qspi };\n\n        memory.reset_memory();\n        memory.enable_quad();\n\n        memory\n    }\n    fn enable_quad(&mut self) {\n        let sr = self.read_sr_lsb();\n        let cr = self.read_sr_msb();\n\n        self.write_sr(sr, cr | 0x02);\n    }\n    fn read_register(&mut self, cmd: u8) -> u8 {\n        let mut buffer = [0; 1];\n        let transaction = TransferConfig {\n            iwidth: QspiWidth::SING,\n            awidth: QspiWidth::NONE,\n            dwidth: QspiWidth::SING,\n            instruction: cmd,\n            address: None,\n            dummy: DummyCycles::_0,\n        };\n        self.qspi.blocking_read(&mut buffer, transaction);\n        buffer[0]\n    }\n\n    fn write_register(&mut self, cmd: u8, value: u8) {\n        let buffer = [value; 1];\n        let transaction: TransferConfig = TransferConfig {\n            iwidth: QspiWidth::SING,\n            awidth: QspiWidth::NONE,\n            dwidth: QspiWidth::SING,\n            instruction: cmd,\n            address: None,\n            dummy: DummyCycles::_0,\n        };\n        self.qspi.blocking_write(&buffer, transaction);\n    }\n    pub fn write_sr(&mut self, lsb: u8, msb: u8) {\n        let buffer = [lsb, msb];\n        let transaction: TransferConfig = TransferConfig {\n            iwidth: QspiWidth::SING,\n            awidth: QspiWidth::NONE,\n            dwidth: QspiWidth::SING,\n            instruction: CMD_WRITE_SR,\n            address: None,\n            dummy: DummyCycles::_0,\n        };\n        self.qspi.blocking_write(&buffer, transaction);\n    }\n\n    pub fn read_sr_lsb(&mut self) -> u8 {\n        self.read_register(CMD_READ_SR)\n    }\n    pub fn read_sr_msb(&mut self) -> u8 {\n        self.read_register(CMD_READ_CR)\n    }\n\n    pub fn reset_memory(&mut self) {\n        self.exec_command(CMD_ENABLE_RESET);\n        self.exec_command(CMD_RESET);\n        self.wait_write_finish();\n    }\n    fn exec_command(&mut self, cmd: u8) {\n        let transaction = TransferConfig {\n            iwidth: QspiWidth::SING,\n            awidth: QspiWidth::NONE,\n            dwidth: QspiWidth::NONE,\n            instruction: cmd,\n            address: None,\n            dummy: DummyCycles::_0,\n        };\n        self.qspi.blocking_command(transaction);\n    }\n    fn wait_write_finish(&mut self) {\n        while (self.read_sr_lsb() & 0x01) != 0 {}\n    }\n\n    pub fn read_mid(&mut self) -> [u8; 2] {\n        let mut buffer = [0; 2];\n        let transaction: TransferConfig = TransferConfig {\n            iwidth: QspiWidth::SING,\n            awidth: QspiWidth::SING,\n            dwidth: QspiWidth::SING,\n            instruction: CMD_READ_MID,\n            address: Some(0),\n            dummy: DummyCycles::_0,\n        };\n        self.qspi.blocking_read(&mut buffer, transaction);\n        buffer\n    }\n    pub fn read_uuid(&mut self) -> [u8; 16] {\n        let mut buffer = [0; 16];\n        let transaction: TransferConfig = TransferConfig {\n            iwidth: QspiWidth::SING,\n            awidth: QspiWidth::SING,\n            dwidth: QspiWidth::SING,\n            instruction: CMD_READ_UUID,\n            address: Some(0),\n            dummy: DummyCycles::_8,\n        };\n        self.qspi.blocking_read(&mut buffer, transaction);\n        buffer\n    }\n    pub fn read_id(&mut self) -> [u8; 3] {\n        let mut buffer = [0; 3];\n        let transaction: TransferConfig = TransferConfig {\n            iwidth: QspiWidth::SING,\n            awidth: QspiWidth::NONE,\n            dwidth: QspiWidth::SING,\n            instruction: CMD_READ_ID,\n            address: None,\n            dummy: DummyCycles::_0,\n        };\n        self.qspi.blocking_read(&mut buffer, transaction);\n        buffer\n    }\n\n    pub fn enable_mmap(&mut self) {\n        let transaction: TransferConfig = TransferConfig {\n            iwidth: QspiWidth::SING,\n            awidth: QspiWidth::SING,\n            dwidth: QspiWidth::QUAD,\n            instruction: CMD_QUAD_READ,\n            address: Some(0),\n            dummy: DummyCycles::_8,\n        };\n        self.qspi.enable_memory_map(&transaction);\n    }\n    fn perform_erase(&mut self, addr: u32, cmd: u8) {\n        let transaction = TransferConfig {\n            iwidth: QspiWidth::SING,\n            awidth: QspiWidth::SING,\n            dwidth: QspiWidth::NONE,\n            instruction: cmd,\n            address: Some(addr),\n            dummy: DummyCycles::_0,\n        };\n        self.enable_write();\n        self.qspi.blocking_command(transaction);\n        self.wait_write_finish();\n    }\n    pub fn enable_write(&mut self) {\n        self.exec_command(CMD_WRITE_ENABLE);\n    }\n    pub fn erase_sector(&mut self, addr: u32) {\n        self.perform_erase(addr, CMD_SECTOR_ERASE);\n    }\n    fn write_page(&mut self, addr: u32, buffer: &[u8], len: usize, use_dma: bool) {\n        assert!(\n            (len as u32 + (addr & 0x000000ff)) <= MEMORY_PAGE_SIZE as u32,\n            \"write_page(): page write length exceeds page boundary (len = {}, addr = {:X}\",\n            len,\n            addr\n        );\n\n        let transaction = TransferConfig {\n            iwidth: QspiWidth::SING,\n            awidth: QspiWidth::SING,\n            dwidth: QspiWidth::QUAD,\n            instruction: CMD_QUAD_WRITE_PG,\n            address: Some(addr),\n            dummy: DummyCycles::_0,\n        };\n        self.enable_write();\n        if use_dma {\n            self.qspi.blocking_write_dma(buffer, transaction);\n        } else {\n            self.qspi.blocking_write(buffer, transaction);\n        }\n        self.wait_write_finish();\n    }\n    pub fn write_memory(&mut self, addr: u32, buffer: &[u8], use_dma: bool) {\n        let mut left = buffer.len();\n        let mut place = addr;\n        let mut chunk_start = 0;\n\n        while left > 0 {\n            let max_chunk_size = MEMORY_PAGE_SIZE - (place & 0x000000ff) as usize;\n            let chunk_size = if left >= max_chunk_size { max_chunk_size } else { left };\n            let chunk = &buffer[chunk_start..(chunk_start + chunk_size)];\n            self.write_page(place, chunk, chunk_size, use_dma);\n            place += chunk_size as u32;\n            left -= chunk_size;\n            chunk_start += chunk_size;\n        }\n    }\n\n    pub fn read_memory(&mut self, addr: u32, buffer: &mut [u8], use_dma: bool) {\n        let transaction = TransferConfig {\n            iwidth: QspiWidth::SING,\n            awidth: QspiWidth::SING,\n            dwidth: QspiWidth::QUAD,\n            instruction: CMD_QUAD_READ,\n            address: Some(addr),\n            dummy: DummyCycles::_8,\n        };\n        if use_dma {\n            self.qspi.blocking_read_dma(buffer, transaction);\n        } else {\n            self.qspi.blocking_read(buffer, transaction);\n        }\n    }\n}\n\nconst MEMORY_ADDR: u32 = 0x00000000 as u32;\n\nbind_interrupts!(struct Irqs {\n    DMA2_CHANNEL7 => dma::InterruptHandler<peripherals::DMA2_CH7>;\n    QUADSPI => qspi::InterruptHandler<peripherals::QUADSPI>;\n});\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let p = embassy_stm32::init(Default::default());\n\n    let mut config = qspi::Config::default();\n    config.memory_size = MemorySize::_16MiB;\n    config.address_size = AddressSize::_24bit;\n    config.prescaler = 200;\n    config.cs_high_time = ChipSelectHighTime::_1Cycle;\n    config.fifo_threshold = FIFOThresholdLevel::_16Bytes;\n    config.sample_shifting = SampleShifting::None;\n\n    let driver = qspi::Qspi::new_bank1(\n        p.QUADSPI, p.PB1, p.PB0, p.PA7, p.PA6, p.PA3, p.PA2, p.DMA2_CH7, Irqs, config,\n    );\n    let mut flash = FlashMemory::new(driver);\n    let mut wr_buf = [0u8; 256];\n    for i in 0..32 {\n        wr_buf[i] = i as u8;\n    }\n    let mut rd_buf = [0u8; 32];\n    flash.erase_sector(MEMORY_ADDR);\n    flash.write_memory(MEMORY_ADDR, &wr_buf, false);\n    flash.read_memory(MEMORY_ADDR, &mut rd_buf, false);\n\n    info!(\"data read from indirect mode: {}\", rd_buf);\n    flash.enable_mmap();\n    let qspi_base = unsafe { core::slice::from_raw_parts(0x9000_0000 as *const u8, 32) };\n    info!(\"data read from mmap: {}\", qspi_base);\n    loop {\n        Timer::after_millis(1000).await;\n    }\n}\n"
  },
  {
    "path": "examples/stm32l5/.cargo/config.toml",
    "content": "[target.'cfg(all(target_arch = \"arm\", target_os = \"none\"))']\n# replace STM32L552ZETxQ with your chip as listed in `probe-rs chip list`\nrunner = \"probe-rs run --chip STM32L552ZETxQ\"\n\n[build]\ntarget = \"thumbv8m.main-none-eabihf\"\n\n[env]\nDEFMT_LOG = \"trace\"\n"
  },
  {
    "path": "examples/stm32l5/Cargo.toml",
    "content": "[package]\nedition = \"2024\"\nname = \"embassy-stm32l5-examples\"\nversion = \"0.1.0\"\nlicense = \"MIT OR Apache-2.0\"\npublish = false\n\n[dependencies]\n# Change stm32l552ze to your chip name, if necessary.\nembassy-stm32 = { version = \"0.6.0\", path = \"../../embassy-stm32\", features = [ \"defmt\", \"unstable-pac\", \"stm32l552ze\", \"time-driver-any\", \"exti\", \"memory-x\", \"dual-bank\"]  }\nembassy-sync = { version = \"0.8.0\", path = \"../../embassy-sync\", features = [\"defmt\"] }\nembassy-executor = { version = \"0.10.0\", path = \"../../embassy-executor\", features = [\"platform-cortex-m\", \"executor-thread\", \"defmt\"] }\nembassy-time = { version = \"0.5.1\", path = \"../../embassy-time\", features = [\"defmt\", \"defmt-timestamp-uptime\", \"tick-hz-32_768\"] }\nembassy-usb = { version = \"0.6.0\", path = \"../../embassy-usb\", features = [\"defmt\"] }\nembassy-net = { version = \"0.9.0\", path = \"../../embassy-net\", features = [\"defmt\",  \"tcp\", \"dhcpv4\", \"medium-ethernet\"] }\nembassy-futures = { version = \"0.1.2\", path = \"../../embassy-futures\" }\nusbd-hid = \"0.10.0\"\n\ndefmt = \"1.0.1\"\ndefmt-rtt = \"1.0.0\"\npanic-probe = { version = \"1.0.0\", features = [\"print-defmt\"] }\n\ncortex-m = { version = \"0.7.6\", features = [\"inline-asm\", \"critical-section-single-core\"] }\ncortex-m-rt = \"0.7.0\"\nembedded-hal = \"0.2.6\"\nheapless = { version = \"0.9\", default-features = false }\nembedded-io-async = { version = \"0.7.0\" }\nstatic_cell = \"2\"\n\n[profile.release]\ndebug = 2\n\n[package.metadata.embassy]\nbuild = [\n  { target = \"thumbv8m.main-none-eabihf\", artifact-dir = \"out/examples/stm32l5\" }\n]\n"
  },
  {
    "path": "examples/stm32l5/build.rs",
    "content": "fn main() {\n    println!(\"cargo:rustc-link-arg-bins=--nmagic\");\n    println!(\"cargo:rustc-link-arg-bins=-Tlink.x\");\n    println!(\"cargo:rustc-link-arg-bins=-Tdefmt.x\");\n}\n"
  },
  {
    "path": "examples/stm32l5/src/bin/button_exti.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_stm32::exti::{self, ExtiInput};\nuse embassy_stm32::gpio::Pull;\nuse embassy_stm32::{bind_interrupts, interrupt};\nuse {defmt_rtt as _, panic_probe as _};\n\nbind_interrupts!(\n    pub struct Irqs{\n        EXTI13 => exti::InterruptHandler<interrupt::typelevel::EXTI13>;\n});\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let p = embassy_stm32::init(Default::default());\n    info!(\"Hello World!\");\n\n    let mut button = ExtiInput::new(p.PC13, p.EXTI13, Pull::Down, Irqs);\n\n    info!(\"Press the USER button...\");\n\n    loop {\n        button.wait_for_falling_edge().await;\n        info!(\"Pressed!\");\n        button.wait_for_rising_edge().await;\n        info!(\"Released!\");\n    }\n}\n"
  },
  {
    "path": "examples/stm32l5/src/bin/rng.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_stm32::rcc::{Pll, PllMul, PllPreDiv, PllRDiv, PllSource, Sysclk};\nuse embassy_stm32::rng::Rng;\nuse embassy_stm32::{Config, bind_interrupts, peripherals, rng};\nuse {defmt_rtt as _, panic_probe as _};\n\nbind_interrupts!(struct Irqs {\n    RNG => rng::InterruptHandler<peripherals::RNG>;\n});\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let mut config = Config::default();\n    config.rcc.hsi = true;\n    config.rcc.sys = Sysclk::PLL1_R;\n    config.rcc.pll = Some(Pll {\n        // 64Mhz clock (16 / 1 * 8 / 2)\n        source: PllSource::HSI,\n        prediv: PllPreDiv::DIV1,\n        mul: PllMul::MUL8,\n        divp: None,\n        divq: None,\n        divr: Some(PllRDiv::DIV2),\n    });\n    let p = embassy_stm32::init(config);\n\n    info!(\"Hello World!\");\n\n    let mut rng = Rng::new(p.RNG, Irqs);\n\n    let mut buf = [0u8; 16];\n    unwrap!(rng.async_fill_bytes(&mut buf).await);\n    info!(\"random bytes: {:02x}\", buf);\n}\n"
  },
  {
    "path": "examples/stm32l5/src/bin/usb_ethernet.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_net::StackResources;\nuse embassy_net::tcp::TcpSocket;\nuse embassy_stm32::rng::Rng;\nuse embassy_stm32::usb::Driver;\nuse embassy_stm32::{Config, bind_interrupts, peripherals, rng, usb};\nuse embassy_usb::class::cdc_ncm::embassy_net::{Device, Runner, State as NetState};\nuse embassy_usb::class::cdc_ncm::{CdcNcmClass, State};\nuse embassy_usb::{Builder, UsbDevice};\nuse embedded_io_async::Write;\nuse static_cell::StaticCell;\nuse {defmt_rtt as _, panic_probe as _};\n\ntype MyDriver = Driver<'static, embassy_stm32::peripherals::USB>;\n\nconst MTU: usize = 1514;\n\nbind_interrupts!(struct Irqs {\n    USB_FS => usb::InterruptHandler<peripherals::USB>;\n    RNG => rng::InterruptHandler<peripherals::RNG>;\n});\n\n#[embassy_executor::task]\nasync fn usb_task(mut device: UsbDevice<'static, MyDriver>) -> ! {\n    device.run().await\n}\n\n#[embassy_executor::task]\nasync fn usb_ncm_task(class: Runner<'static, MyDriver, MTU>) -> ! {\n    class.run().await\n}\n\n#[embassy_executor::task]\nasync fn net_task(mut runner: embassy_net::Runner<'static, Device<'static, MTU>>) -> ! {\n    runner.run().await\n}\n\n#[embassy_executor::main]\nasync fn main(spawner: Spawner) {\n    let mut config = Config::default();\n    {\n        use embassy_stm32::rcc::*;\n        config.rcc.hsi = true;\n        config.rcc.sys = Sysclk::PLL1_R;\n        config.rcc.pll = Some(Pll {\n            // 80Mhz clock (16 / 1 * 10 / 2)\n            source: PllSource::HSI,\n            prediv: PllPreDiv::DIV1,\n            mul: PllMul::MUL10,\n            divp: None,\n            divq: None,\n            divr: Some(PllRDiv::DIV2),\n        });\n        config.rcc.hsi48 = Some(Hsi48Config { sync_from_usb: true }); // needed for USB\n        config.rcc.mux.clk48sel = mux::Clk48sel::HSI48;\n    }\n    let p = embassy_stm32::init(config);\n\n    // Create the driver, from the HAL.\n    let driver = Driver::new(p.USB, Irqs, p.PA12, p.PA11);\n\n    // Create embassy-usb Config\n    let mut config = embassy_usb::Config::new(0xc0de, 0xcafe);\n    config.manufacturer = Some(\"Embassy\");\n    config.product = Some(\"USB-Ethernet example\");\n    config.serial_number = Some(\"12345678\");\n    config.max_power = 100;\n    config.max_packet_size_0 = 64;\n\n    // Create embassy-usb DeviceBuilder using the driver and config.\n    static CONFIG_DESC: StaticCell<[u8; 256]> = StaticCell::new();\n    static BOS_DESC: StaticCell<[u8; 256]> = StaticCell::new();\n    static CONTROL_BUF: StaticCell<[u8; 128]> = StaticCell::new();\n    let mut builder = Builder::new(\n        driver,\n        config,\n        &mut CONFIG_DESC.init([0; 256])[..],\n        &mut BOS_DESC.init([0; 256])[..],\n        &mut [], // no msos descriptors\n        &mut CONTROL_BUF.init([0; 128])[..],\n    );\n\n    // Our MAC addr.\n    let our_mac_addr = [0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC];\n    // Host's MAC addr. This is the MAC the host \"thinks\" its USB-to-ethernet adapter has.\n    let host_mac_addr = [0x88, 0x88, 0x88, 0x88, 0x88, 0x88];\n\n    // Create classes on the builder.\n    static STATE: StaticCell<State> = StaticCell::new();\n    let class = CdcNcmClass::new(&mut builder, STATE.init(State::new()), host_mac_addr, 64);\n\n    // Build the builder.\n    let usb = builder.build();\n\n    spawner.spawn(unwrap!(usb_task(usb)));\n\n    static NET_STATE: StaticCell<NetState<MTU, 4, 4>> = StaticCell::new();\n    let (runner, device) = class.into_embassy_net_device::<MTU, 4, 4>(NET_STATE.init(NetState::new()), our_mac_addr);\n    spawner.spawn(unwrap!(usb_ncm_task(runner)));\n\n    let config = embassy_net::Config::dhcpv4(Default::default());\n    //let config = embassy_net::Config::ipv4_static(embassy_net::StaticConfigV4 {\n    //    address: Ipv4Cidr::new(Ipv4Address::new(10, 42, 0, 61), 24),\n    //    dns_servers: Vec::new(),\n    //    gateway: Some(Ipv4Address::new(10, 42, 0, 1)),\n    //});\n\n    // Generate random seed\n    let mut rng = Rng::new(p.RNG, Irqs);\n    let seed = rng.next_u64();\n\n    // Init network stack\n    static RESOURCES: StaticCell<StackResources<3>> = StaticCell::new();\n    let (stack, runner) = embassy_net::new(device, config, RESOURCES.init(StackResources::new()), seed);\n\n    spawner.spawn(unwrap!(net_task(runner)));\n\n    // And now we can use it!\n\n    let mut rx_buffer = [0; 4096];\n    let mut tx_buffer = [0; 4096];\n    let mut buf = [0; 4096];\n\n    loop {\n        let mut socket = TcpSocket::new(stack, &mut rx_buffer, &mut tx_buffer);\n        socket.set_timeout(Some(embassy_time::Duration::from_secs(10)));\n\n        info!(\"Listening on TCP:1234...\");\n        if let Err(e) = socket.accept(1234).await {\n            warn!(\"accept error: {:?}\", e);\n            continue;\n        }\n\n        info!(\"Received connection from {:?}\", socket.remote_endpoint());\n\n        loop {\n            let n = match socket.read(&mut buf).await {\n                Ok(0) => {\n                    warn!(\"read EOF\");\n                    break;\n                }\n                Ok(n) => n,\n                Err(e) => {\n                    warn!(\"read error: {:?}\", e);\n                    break;\n                }\n            };\n\n            info!(\"rxd {:02x}\", &buf[..n]);\n\n            match socket.write_all(&buf[..n]).await {\n                Ok(()) => {}\n                Err(e) => {\n                    warn!(\"write error: {:?}\", e);\n                    break;\n                }\n            };\n        }\n    }\n}\n"
  },
  {
    "path": "examples/stm32l5/src/bin/usb_hid_mouse.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse core::sync::atomic::{AtomicU8, Ordering};\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_futures::join::join;\nuse embassy_stm32::usb::Driver;\nuse embassy_stm32::{Config, bind_interrupts, peripherals, usb};\nuse embassy_time::Timer;\nuse embassy_usb::Builder;\nuse embassy_usb::class::hid::{\n    HidBootProtocol, HidProtocolMode, HidSubclass, HidWriter, ReportId, RequestHandler, State,\n};\nuse embassy_usb::control::OutResponse;\nuse usbd_hid::descriptor::{MouseReport, SerializedDescriptor};\nuse {defmt_rtt as _, panic_probe as _};\n\nbind_interrupts!(struct Irqs {\n    USB_FS => usb::InterruptHandler<peripherals::USB>;\n});\n\nstatic HID_PROTOCOL_MODE: AtomicU8 = AtomicU8::new(HidProtocolMode::Boot as u8);\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let mut config = Config::default();\n    {\n        use embassy_stm32::rcc::*;\n        config.rcc.hsi = true;\n        config.rcc.sys = Sysclk::PLL1_R;\n        config.rcc.pll = Some(Pll {\n            // 80Mhz clock (16 / 1 * 10 / 2)\n            source: PllSource::HSI,\n            prediv: PllPreDiv::DIV1,\n            mul: PllMul::MUL10,\n            divp: None,\n            divq: None,\n            divr: Some(PllRDiv::DIV2),\n        });\n        config.rcc.hsi48 = Some(Hsi48Config { sync_from_usb: true }); // needed for USB\n        config.rcc.mux.clk48sel = mux::Clk48sel::HSI48;\n    }\n    let p = embassy_stm32::init(config);\n\n    // Create the driver, from the HAL.\n    let driver = Driver::new(p.USB, Irqs, p.PA12, p.PA11);\n\n    // Create embassy-usb Config\n    let mut config = embassy_usb::Config::new(0xc0de, 0xcafe);\n    config.manufacturer = Some(\"Embassy\");\n    config.product = Some(\"HID mouse example\");\n    config.serial_number = Some(\"12345678\");\n    config.max_power = 100;\n    config.max_packet_size_0 = 64;\n    config.composite_with_iads = false;\n    config.device_class = 0;\n    config.device_sub_class = 0;\n    config.device_protocol = 0;\n\n    // Create embassy-usb DeviceBuilder using the driver and config.\n    // It needs some buffers for building the descriptors.\n    let mut config_descriptor = [0; 256];\n    let mut bos_descriptor = [0; 256];\n    let mut control_buf = [0; 64];\n    let mut request_handler = MyRequestHandler {};\n\n    let mut state = State::new();\n\n    let mut builder = Builder::new(\n        driver,\n        config,\n        &mut config_descriptor,\n        &mut bos_descriptor,\n        &mut [], // no msos descriptors\n        &mut control_buf,\n    );\n\n    // Create classes on the builder.\n    let config = embassy_usb::class::hid::Config {\n        report_descriptor: MouseReport::desc(),\n        request_handler: Some(&mut request_handler),\n        poll_ms: 60,\n        max_packet_size: 8,\n        hid_subclass: HidSubclass::Boot,\n        hid_boot_protocol: HidBootProtocol::Mouse,\n    };\n\n    let mut writer = HidWriter::<_, 5>::new(&mut builder, &mut state, config);\n\n    // Build the builder.\n    let mut usb = builder.build();\n\n    // Run the USB device.\n    let usb_fut = usb.run();\n\n    // Do stuff with the class!\n    let hid_fut = async {\n        let mut y: i8 = 5;\n        loop {\n            Timer::after_millis(500).await;\n\n            y = -y;\n\n            if HID_PROTOCOL_MODE.load(Ordering::Relaxed) == HidProtocolMode::Boot as u8 {\n                let buttons = 0u8;\n                let x = 0i8;\n                match writer.write(&[buttons, x as u8, y as u8]).await {\n                    Ok(()) => {}\n                    Err(e) => warn!(\"Failed to send boot report: {:?}\", e),\n                }\n            } else {\n                let report = MouseReport {\n                    buttons: 0,\n                    x: 0,\n                    y,\n                    wheel: 0,\n                    pan: 0,\n                };\n                match writer.write_serialize(&report).await {\n                    Ok(()) => {}\n                    Err(e) => warn!(\"Failed to send report: {:?}\", e),\n                }\n            }\n        }\n    };\n\n    // Run everything concurrently.\n    // If we had made everything `'static` above instead, we could do this using separate tasks instead.\n    join(usb_fut, hid_fut).await;\n}\n\nstruct MyRequestHandler {}\n\nimpl RequestHandler for MyRequestHandler {\n    fn get_report(&mut self, id: ReportId, _buf: &mut [u8]) -> Option<usize> {\n        info!(\"Get report for {:?}\", id);\n        None\n    }\n\n    fn set_report(&mut self, id: ReportId, data: &[u8]) -> OutResponse {\n        info!(\"Set report for {:?}: {=[u8]}\", id, data);\n        OutResponse::Accepted\n    }\n\n    fn get_protocol(&self) -> HidProtocolMode {\n        let protocol = HidProtocolMode::from(HID_PROTOCOL_MODE.load(Ordering::Relaxed));\n        info!(\"The current HID protocol mode is: {}\", protocol);\n        protocol\n    }\n\n    fn set_protocol(&mut self, protocol: HidProtocolMode) -> OutResponse {\n        info!(\"Switching to HID protocol mode: {}\", protocol);\n        HID_PROTOCOL_MODE.store(protocol as u8, Ordering::Relaxed);\n        OutResponse::Accepted\n    }\n\n    fn set_idle_ms(&mut self, id: Option<ReportId>, dur: u32) {\n        info!(\"Set idle rate for {:?} to {:?}\", id, dur);\n    }\n\n    fn get_idle_ms(&mut self, id: Option<ReportId>) -> Option<u32> {\n        info!(\"Get idle rate for {:?}\", id);\n        None\n    }\n}\n"
  },
  {
    "path": "examples/stm32l5/src/bin/usb_serial.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse defmt::{panic, *};\nuse embassy_executor::Spawner;\nuse embassy_futures::join::join;\nuse embassy_stm32::usb::{Driver, Instance};\nuse embassy_stm32::{Config, bind_interrupts, peripherals, usb};\nuse embassy_usb::Builder;\nuse embassy_usb::class::cdc_acm::{CdcAcmClass, State};\nuse embassy_usb::driver::EndpointError;\nuse {defmt_rtt as _, panic_probe as _};\n\nbind_interrupts!(struct Irqs {\n    USB_FS => usb::InterruptHandler<peripherals::USB>;\n});\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let mut config = Config::default();\n    {\n        use embassy_stm32::rcc::*;\n        config.rcc.hsi = true;\n        config.rcc.sys = Sysclk::PLL1_R;\n        config.rcc.pll = Some(Pll {\n            // 80Mhz clock (16 / 1 * 10 / 2)\n            source: PllSource::HSI,\n            prediv: PllPreDiv::DIV1,\n            mul: PllMul::MUL10,\n            divp: None,\n            divq: None,\n            divr: Some(PllRDiv::DIV2),\n        });\n        config.rcc.hsi48 = Some(Hsi48Config { sync_from_usb: true }); // needed for USB\n        config.rcc.mux.clk48sel = mux::Clk48sel::HSI48;\n    }\n    let p = embassy_stm32::init(config);\n\n    info!(\"Hello World!\");\n\n    // Create the driver, from the HAL.\n    let driver = Driver::new(p.USB, Irqs, p.PA12, p.PA11);\n\n    // Create embassy-usb Config\n    let config = embassy_usb::Config::new(0xc0de, 0xcafe);\n    //config.max_packet_size_0 = 64;\n\n    // Create embassy-usb DeviceBuilder using the driver and config.\n    // It needs some buffers for building the descriptors.\n    let mut config_descriptor = [0; 256];\n    let mut bos_descriptor = [0; 256];\n    let mut control_buf = [0; 7];\n\n    let mut state = State::new();\n\n    let mut builder = Builder::new(\n        driver,\n        config,\n        &mut config_descriptor,\n        &mut bos_descriptor,\n        &mut [], // no msos descriptors\n        &mut control_buf,\n    );\n\n    // Create classes on the builder.\n    let mut class = CdcAcmClass::new(&mut builder, &mut state, 64);\n\n    // Build the builder.\n    let mut usb = builder.build();\n\n    // Run the USB device.\n    let usb_fut = usb.run();\n\n    // Do stuff with the class!\n    let echo_fut = async {\n        loop {\n            class.wait_connection().await;\n            info!(\"Connected\");\n            let _ = echo(&mut class).await;\n            info!(\"Disconnected\");\n        }\n    };\n\n    // Run everything concurrently.\n    // If we had made everything `'static` above instead, we could do this using separate tasks instead.\n    join(usb_fut, echo_fut).await;\n}\n\nstruct Disconnected {}\n\nimpl From<EndpointError> for Disconnected {\n    fn from(val: EndpointError) -> Self {\n        match val {\n            EndpointError::BufferOverflow => panic!(\"Buffer overflow\"),\n            EndpointError::Disabled => Disconnected {},\n        }\n    }\n}\n\nasync fn echo<'d, T: Instance + 'd>(class: &mut CdcAcmClass<'d, Driver<'d, T>>) -> Result<(), Disconnected> {\n    let mut buf = [0; 64];\n    loop {\n        let n = class.read_packet(&mut buf).await?;\n        let data = &buf[..n];\n        info!(\"data: {:x}\", data);\n        class.write_packet(data).await?;\n    }\n}\n"
  },
  {
    "path": "examples/stm32l5-lp/.cargo/config.toml",
    "content": "[target.'cfg(all(target_arch = \"arm\", target_os = \"none\"))']\n# replace STM32L552ZETxQ with your chip as listed in `probe-rs chip list`\nrunner = \"probe-rs run --chip STM32L552ZETxQ\"\n\n[build]\ntarget = \"thumbv8m.main-none-eabihf\"\n\n[env]\nDEFMT_LOG = \"trace\"\n"
  },
  {
    "path": "examples/stm32l5-lp/Cargo.toml",
    "content": "[package]\nedition = \"2024\"\nname = \"embassy-stm32l5-lp-examples\"\nversion = \"0.1.0\"\nlicense = \"MIT OR Apache-2.0\"\npublish = false\n\n[dependencies]\n# Change stm32l552ze to your chip name, if necessary.\nembassy-stm32 = { version = \"0.6.0\", path = \"../../embassy-stm32\", features = [ \"defmt\", \"unstable-pac\", \"stm32l552ze\", \"time-driver-any\", \"exti\", \"memory-x\", \"dual-bank\", \"low-power\", \"executor-thread\"]  }\nembassy-sync = { version = \"0.8.0\", path = \"../../embassy-sync\", features = [\"defmt\"] }\nembassy-executor = { version = \"0.10.0\", path = \"../../embassy-executor\", features = [\"defmt\"] }\nembassy-time = { version = \"0.5.1\", path = \"../../embassy-time\", features = [\"defmt\", \"defmt-timestamp-uptime\", \"tick-hz-32_768\"] }\nembassy-usb = { version = \"0.6.0\", path = \"../../embassy-usb\", features = [\"defmt\"] }\nembassy-net = { version = \"0.9.0\", path = \"../../embassy-net\", features = [\"defmt\",  \"tcp\", \"dhcpv4\", \"medium-ethernet\"] }\nembassy-futures = { version = \"0.1.2\", path = \"../../embassy-futures\" }\nusbd-hid = \"0.9.0\"\n\ndefmt = \"1.0.1\"\ndefmt-rtt = \"1.0.0\"\npanic-probe = { version = \"1.0.0\", features = [\"print-defmt\"] }\n\ncortex-m = { version = \"0.7.6\", features = [\"inline-asm\", \"critical-section-single-core\"] }\ncortex-m-rt = \"0.7.0\"\nembedded-hal = \"0.2.6\"\nheapless = { version = \"0.9\", default-features = false }\nembedded-io-async = { version = \"0.7.0\" }\nstatic_cell = \"2\"\n\n[profile.release]\ndebug = 2\n\n[package.metadata.embassy]\nbuild = [\n  { target = \"thumbv8m.main-none-eabihf\", artifact-dir = \"out/examples/stm32l5-lp\" }\n]\n"
  },
  {
    "path": "examples/stm32l5-lp/build.rs",
    "content": "fn main() {\n    println!(\"cargo:rustc-link-arg-bins=--nmagic\");\n    println!(\"cargo:rustc-link-arg-bins=-Tlink.x\");\n    println!(\"cargo:rustc-link-arg-bins=-Tdefmt.x\");\n}\n"
  },
  {
    "path": "examples/stm32l5-lp/src/bin/stop.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_stm32::gpio::{AnyPin, Level, Output, Speed};\nuse embassy_stm32::rcc::LsConfig;\nuse embassy_stm32::{Config, Peri};\nuse embassy_time::Timer;\nuse {defmt_rtt as _, panic_probe as _};\n\n#[embassy_executor::main(executor = \"embassy_stm32::executor::Executor\", entry = \"cortex_m_rt::entry\")]\nasync fn async_main(spawner: Spawner) {\n    let mut config = Config::default();\n    config.rcc.ls = LsConfig::default_lsi();\n    // when enabled the power-consumption is much higher during stop, but debugging and RTT is working\n    // if you wan't to measure the power-consumption, or for production: uncomment this line\n    // config.enable_debug_during_sleep = false;\n    let p = embassy_stm32::init(config);\n\n    spawner.spawn(unwrap!(blinky(p.PC7.into())));\n    spawner.spawn(unwrap!(timeout()));\n}\n\n#[embassy_executor::task]\nasync fn blinky(led: Peri<'static, AnyPin>) -> ! {\n    let mut led = Output::new(led, Level::Low, Speed::Low);\n    loop {\n        info!(\"high\");\n        led.set_high();\n        Timer::after_millis(300).await;\n\n        info!(\"low\");\n        led.set_low();\n        Timer::after_millis(300).await;\n    }\n}\n\n// when enable_debug_during_sleep is false, it is more difficult to reprogram the MCU\n// therefore we block the MCU after 30s to be able to reprogram it easily\n#[embassy_executor::task]\nasync fn timeout() -> ! {\n    Timer::after_secs(30).await;\n    loop {}\n}\n"
  },
  {
    "path": "examples/stm32n6/.cargo/config.toml",
    "content": "[target.thumbv8m.main-none-eabihf]\nrunner = 'probe-rs run --chip STM32N657'\n\n[build]\ntarget = \"thumbv8m.main-none-eabihf\"\n\n[env]\nDEFMT_LOG = \"trace\"\n"
  },
  {
    "path": "examples/stm32n6/Cargo.toml",
    "content": "[package]\nedition = \"2024\"\nname = \"embassy-stm32n6-examples\"\nversion = \"0.1.0\"\nlicense = \"MIT OR Apache-2.0\"\npublish = false\n\n[dependencies]\n# Change stm32h563zi to your chip name, if necessary.\nembassy-stm32 = { version = \"0.6.0\", path = \"../../embassy-stm32\", features = [\"defmt\", \"stm32n657x0\", \"time-driver-any\", \"exti\", \"unstable-pac\"] }\nembassy-sync = { version = \"0.8.0\", path = \"../../embassy-sync\", features = [\"defmt\"] }\nembassy-executor = { version = \"0.10.0\", path = \"../../embassy-executor\", features = [\"platform-cortex-m\", \"executor-thread\", \"defmt\"] }\nembassy-time = { version = \"0.5.1\", path = \"../../embassy-time\", features = [\"defmt\", \"defmt-timestamp-uptime\", \"tick-hz-32_768\"] }\nembassy-net = { version = \"0.9.0\", path = \"../../embassy-net\", features = [\"defmt\", \"tcp\", \"dhcpv4\", \"medium-ethernet\", \"proto-ipv6\"] }\nembassy-usb = { version = \"0.6.0\", path = \"../../embassy-usb\", features = [\"defmt\"] }\nembassy-futures = { version = \"0.1.2\", path = \"../../embassy-futures\" }\n\ndefmt = \"1.0.1\"\ndefmt-rtt = \"1.0.0\"\n\ncortex-m = { version = \"0.7.6\", features = [\"inline-asm\", \"critical-section-single-core\"] }\ncortex-m-rt = \"0.7.0\"\nembedded-hal = \"0.2.6\"\nembedded-hal-1 = { package = \"embedded-hal\", version = \"1.0\" }\nembedded-hal-async = { version = \"1.0\" }\nembedded-io-async = { version = \"0.7.0\" }\nembedded-nal-async = \"0.8.0\"\npanic-probe = { version = \"1.0.0\", features = [\"print-defmt\"] }\nheapless = { version = \"0.9\", default-features = false }\ncritical-section = \"1.1\"\nmicromath = \"2.0.0\"\nstm32-fmc = \"0.3.0\"\nembedded-storage = \"0.3.1\"\nstatic_cell = \"2\"\nhmac = \"0.12.1\"\nsha2 = { version = \"0.10.9\", default-features = false }\n\n\n# cargo build/run\n[profile.dev]\ncodegen-units = 1\ndebug = 2\ndebug-assertions = true # <-\nincremental = false\nopt-level = 3 # <-\noverflow-checks = true # <-\n\n# cargo test\n[profile.test]\ncodegen-units = 1\ndebug = 2\ndebug-assertions = true # <-\nincremental = false\nopt-level = 3 # <-\noverflow-checks = true # <-\n\n# cargo build/run --release\n[profile.release]\ncodegen-units = 1\ndebug = 2\ndebug-assertions = false # <-\nincremental = false\nlto = 'fat'\nopt-level = 3 # <-\noverflow-checks = false # <-\n\n# cargo test --release\n[profile.bench]\ncodegen-units = 1\ndebug = 2\ndebug-assertions = false # <-\nincremental = false\nlto = 'fat'\nopt-level = 3 # <-\noverflow-checks = false # <-\n\n[package.metadata.embassy]\nbuild = [\n  { target = \"thumbv8m.main-none-eabihf\", artifact-dir = \"out/examples/stm32n6\" }\n]\n"
  },
  {
    "path": "examples/stm32n6/README.md",
    "content": "# Booting the N6 from flash\n\nNote: A copy of this is located here: https://github.com/embassy-rs/embassy/wiki/Booting-the-N6-from-flash\n\nA Guide to how I currently boot from flash on the N6. My repo is laid out as a workspace with a bootloader and firmware. Unfortunately I'm not able to share that repo, but I can provide relevant snippets.\n\n# Build instructions\n\n1. Build the bootloader as a binary and sign it\n```console\ncd bootloader\ncargo objcopy -- -O binary ../fsbl.bin\n/usr/local/STMicroelectronics/STM32Cube/STM32CubeProgrammer/bin/STM32_SigningTool_CLI -bin ../fsbl.bin -nk -of 0x80000000 -t fsbl -o ../fsbl-trusted.bin -hv 2.3 -dump ../fsbl-trusted.bin -s\n```\n2. Use the STM32_Programmer or STM32_Programmer_CLI to flash `fsbl-trusted.bin` to 0x70000000\n3. Build the firmware as a binary and sign it\n```console\ncd firmware\ncargo objcopy -- -O binary ../app.bin\n/usr/local/STMicroelectronics/STM32Cube/STM32CubeProgrammer/bin/STM32_SigningTool_CLI -bin ../app.bin -nk -of 0x80000000 -t fsbl -o ../app-trusted.bin -hv 2.3 -dump ../app-trusted.bin -s\n```\n4. Use the STM32_Programmer or STM32_Programmer_CLI to flash `app-trusted.bin` to 0x70100000\n5. Configure the boot switches to both be low (if on the Disco kit) and click the reset button to boot into your code\n\n# Memory layout\n\n**Bootloader**\n\nI have yet to test updating (if that's even supported for the N6), but this is how it is so far\n```x\n/* DFU must be at least one page (4K) more than Active */\nMEMORY\n{\n  RAM                         (rwx) : ORIGIN = 0x34100000, LENGTH = 513K\n  FLASH                             : ORIGIN = 0x34180400, LENGTH = 507K\n  BOOTLOADER_STATE                  : ORIGIN = 0x341ff000, LENGTH = 4K\n  BOOTLOADER                        : ORIGIN = 0x70000000, LENGTH = 1024K\n  ACTIVE                            : ORIGIN = 0x70100400, LENGTH = 2044K\n  DFU                               : ORIGIN = 0x70300400, LENGTH = 2048K\n}\nSECTIONS\n{\n  .application_flash (NOLOAD) :\n    {\n      *(.application_flash);\n    } > ACTIVE\n}\n\n__bootloader_flash_address = ORIGIN(FLASH);\n__bootloader_active_address = ORIGIN(ACTIVE);\n\n__bootloader_state_start = ORIGIN(BOOTLOADER_STATE) - ORIGIN(FLASH);\n__bootloader_state_end = ORIGIN(BOOTLOADER_STATE) + LENGTH(BOOTLOADER_STATE) - ORIGIN(FLASH);\n\n__bootloader_active_start = ORIGIN(ACTIVE) - ORIGIN(FLASH);\n__bootloader_active_end = ORIGIN(ACTIVE) + LENGTH(ACTIVE) - ORIGIN(FLASH);\n\n__bootloader_dfu_start = ORIGIN(DFU) - ORIGIN(DFU);\n__bootloader_dfu_end = ORIGIN(DFU) + LENGTH(DFU) - ORIGIN(DFU);\n```\n\n**Firmware**\n```x\nMEMORY\n{\n  BACKUP_RAM      : ORIGIN = 0x3C000000,   LENGTH = 8K\n  RAM             : ORIGIN = 0x34100000,   LENGTH = 2816K\n  FLASH           : ORIGIN = 0x70100400,   LENGTH = 128M\n  XSPI_RAM        : ORIGIN = 0x90000000,   LENGTH = 32M\n}\nSECTIONS\n{\n  .external_ram (NOLOAD) :\n    {\n      *(.external_ram);\n    } > XSPI_RAM\n\n  .external_flash (NOLOAD) :\n    {\n      *(.external_flash);\n    } > XSPI_FLASH\n}\n\n__backup_ram = ORIGIN(BACKUP_RAM);\n```\n\n# Bootloader snippets\n\nThe bootloader is built using embassy-boot-stm32. Not all steps may be relevant, but this is what the bootloader does:\n1. Enable debugging mode\n```Rust\nunsafe {\n    // Open the debug access port to the Cortex-M55\n    BSEC.as_ptr()\n        .cast::<u32>()\n        .byte_add(0xE90)\n        .write(0xB451_B400);\n    // Enable the non-secure/secure debug\n    BSEC.as_ptr()\n        .cast::<u32>()\n        .byte_add(0xE8C)\n        .write(0xB451_B400);\n};\n```\n2. Set the vector table (otherwise it can hardfault)\n```Rust\nlet mut core_peri = unsafe { cortex_m::Peripherals::steal() };\nunsafe { core_peri.SCB.vtor.write(0x34180400) }; // __bootloader_flash_address\n```\n3. Configure the clocks\n```Rust\n// Configure the system without any PLL, dropping to 64 MHz to allow the firmware to configure\n// its own clocks\nlet mut config = Config::default();\n// The initial settings must match what PLL1 is already set to. We'll disable it later\nconfig.rcc.pll1 = Some(rcc::Pll::Oscillator {\n    source: Pllsel::HSI,\n    divm: Plldivm::DIV4,\n    fractional: 0,\n    divn: 75,\n    divp1: Pllpdiv::DIV1,\n    divp2: Pllpdiv::DIV1,\n});\nconfig.rcc.sys = SysClk::Hsi;\nconfig.rcc.cpu = CpuClk::Hsi;\n// Place the flash on the HSI bus to allow the firmware to configure the clocks without causing issues\nconfig.rcc.mux.xspi2sel = Xspisel::PER;\nlet p = embassy_stm32::init(config);\n// Disable PLL1\nRCC.pllcfgr3(0).modify(|w| w.set_pllpdiven(false));\nRCC.ccr().write(|w| w.set_pllonc(0, true));\nwhile RCC.sr().read().pllrdy(0) {} // wait till disabled\nRCC.pllcfgr1(0).modify(|w| w.set_pllbyp(false)); // clear bypass mode\n```\n4. Initialize external flash in memory map mode\n5. Enable all RAM banks\n6. Load firmware\n```Rust\ncore_peri.SCB.invalidate_icache(); // Clear instruction cache\nunsafe {\n    core_peri.SCB.vtor.write(0x70100400); // Set new vector table (__bootloader_active_address)\n    cortex_m::asm::bootload(0x70100400 as *const u32); // Jump\n}\n```\n\n# Firmware snippets\n\nNothing too special here, I just reconfigure the clocks for peripherals and speed\n\n```Rust\nlet mut config = Config::default();\n// Configure the clocks as desired...\nlet mut periph = unsafe { Peripherals::steal() };\nrcc::reinit(config, &mut periph.RCC);\n```"
  },
  {
    "path": "examples/stm32n6/build.rs",
    "content": "fn main() {\n    println!(\"cargo:rustc-link-arg-bins=--nmagic\");\n    println!(\"cargo:rustc-link-arg-bins=-Tlink.x\");\n    println!(\"cargo:rustc-link-arg-bins=-Tdefmt.x\");\n}\n"
  },
  {
    "path": "examples/stm32n6/memory.x",
    "content": "MEMORY\n{\n  FLASH       : ORIGIN = 0x34180400,   LENGTH = 255K\n  RAM         : ORIGIN = 0x341C0000,   LENGTH = 256K\n}\n"
  },
  {
    "path": "examples/stm32n6/src/bin/blinky.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_stm32::exti::{self, ExtiInput};\nuse embassy_stm32::gpio::{Level, Output, Pull, Speed};\nuse embassy_stm32::mode::Async;\nuse embassy_stm32::{bind_interrupts, interrupt};\nuse embassy_time::Timer;\nuse {defmt_rtt as _, panic_probe as _};\n\nbind_interrupts!(\n    pub struct Irqs{\n        EXTI13 => exti::InterruptHandler<interrupt::typelevel::EXTI13>;\n});\n\n#[embassy_executor::task]\nasync fn button_task(mut button: ExtiInput<'static, Async>) {\n    loop {\n        button.wait_for_any_edge().await;\n        info!(\"button pressed!\");\n    }\n}\n\n#[embassy_executor::main]\nasync fn main(spawner: Spawner) {\n    let p = embassy_stm32::init(Default::default());\n    info!(\"Hello World!\");\n\n    let mut led = Output::new(p.PG10, Level::High, Speed::Low);\n    // Note: On STM32N6570-DK, the USER button (BP1) connects to 3.3V when pressed\n    // (active high), so we need Pull::Down\n    let button = ExtiInput::new(p.PC13, p.EXTI13, Pull::Down, Irqs);\n\n    spawner.spawn(button_task(button).unwrap());\n\n    loop {\n        led.set_high();\n        Timer::after_millis(500).await;\n\n        led.set_low();\n        Timer::after_millis(500).await;\n    }\n}\n"
  },
  {
    "path": "examples/stm32n6/src/bin/crc.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_stm32::crc::{Config, Crc, InputReverseConfig, PolySize};\nuse {defmt_rtt as _, panic_probe as _};\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let p = embassy_stm32::init(Default::default());\n    info!(\"Hello World!\");\n\n    // Setup for: https://crccalc.com/?crc=Life, it never dieWomen are my favorite guy&method=crc32&datatype=ascii&outtype=0\n    let mut crc = Crc::new(\n        p.CRC,\n        unwrap!(Config::new(\n            InputReverseConfig::Byte,\n            true,\n            PolySize::Width32,\n            0xFFFFFFFF,\n            0x04C11DB7\n        )),\n    );\n\n    crc.feed_bytes(b\"Life, it never die\\nWomen are my favorite guy\");\n    let output = crc.read() ^ 0xFFFFFFFF;\n\n    defmt::assert_eq!(output, 0x33F0E26B);\n\n    cortex_m::asm::bkpt();\n}\n"
  },
  {
    "path": "examples/stm32n6/src/bin/hash.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse defmt::info;\nuse embassy_executor::Spawner;\nuse embassy_stm32::hash::*;\nuse embassy_stm32::{Config, bind_interrupts, hash, peripherals};\nuse embassy_time::Instant;\nuse hmac::{Hmac, Mac};\nuse sha2::{Digest, Sha256};\nuse {defmt_rtt as _, panic_probe as _};\n\ntype HmacSha256 = Hmac<Sha256>;\n\nbind_interrupts!(struct Irqs {\n    HASH => hash::InterruptHandler<peripherals::HASH>;\n});\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) -> ! {\n    let config = Config::default();\n    let p = embassy_stm32::init(config);\n\n    let test_1: &[u8] = b\"as;dfhaslfhas;oifvnasd;nifvnhasd;nifvhndlkfghsd;nvfnahssdfgsdafgsasdfasdfasdfasdfasdfghjklmnbvcalskdjghalskdjgfbaslkdjfgbalskdjgbalskdjbdfhsdfhsfghsfghfgh\";\n    let test_2: &[u8] = b\"fdhalksdjfhlasdjkfhalskdjfhgal;skdjfgalskdhfjgalskdjfglafgadfgdfgdafgaadsfgfgdfgadrgsyfthxfgjfhklhjkfgukhulkvhlvhukgfhfsrghzdhxyfufynufyuszeradrtydyytserr\";\n\n    let mut hw_hasher = Hash::new_blocking(p.HASH, Irqs);\n\n    let hw_start_time = Instant::now();\n\n    // Compute a digest in hardware.\n    let mut context = hw_hasher.start(Algorithm::SHA256, DataType::Width8, None);\n    hw_hasher.update_blocking(&mut context, test_1);\n    hw_hasher.update_blocking(&mut context, test_2);\n    let mut hw_digest: [u8; 32] = [0; 32];\n    hw_hasher.finish_blocking(context, &mut hw_digest);\n\n    let hw_end_time = Instant::now();\n    let hw_execution_time = hw_end_time - hw_start_time;\n\n    let sw_start_time = Instant::now();\n\n    // Compute a digest in software.\n    let mut sw_hasher = Sha256::new();\n    sw_hasher.update(test_1);\n    sw_hasher.update(test_2);\n    let sw_digest = sw_hasher.finalize();\n\n    let sw_end_time = Instant::now();\n    let sw_execution_time = sw_end_time - sw_start_time;\n\n    info!(\"Hardware Digest: {:?}\", hw_digest);\n    info!(\"Software Digest: {:?}\", sw_digest[..]);\n    info!(\"Hardware Execution Time: {:?}\", hw_execution_time);\n    info!(\"Software Execution Time: {:?}\", sw_execution_time);\n    assert_eq!(hw_digest, sw_digest[..]);\n\n    let hmac_key: [u8; 64] = [0x55; 64];\n\n    // Compute HMAC in hardware.\n    let mut sha256hmac_context = hw_hasher.start(Algorithm::SHA256, DataType::Width8, Some(&hmac_key));\n    hw_hasher.update_blocking(&mut sha256hmac_context, test_1);\n    hw_hasher.update_blocking(&mut sha256hmac_context, test_2);\n    let mut hw_hmac: [u8; 32] = [0; 32];\n    hw_hasher.finish_blocking(sha256hmac_context, &mut hw_hmac);\n\n    // Compute HMAC in software.\n    let mut sw_mac = HmacSha256::new_from_slice(&hmac_key).unwrap();\n    sw_mac.update(test_1);\n    sw_mac.update(test_2);\n    let sw_hmac = sw_mac.finalize().into_bytes();\n\n    info!(\"Hardware HMAC: {:?}\", hw_hmac);\n    info!(\"Software HMAC: {:?}\", sw_hmac[..]);\n    assert_eq!(hw_hmac, sw_hmac[..]);\n\n    loop {}\n}\n"
  },
  {
    "path": "examples/stm32n6/src/bin/xspi_flash.rs",
    "content": "#![no_std]\n#![no_main]\n\n//! XSPI External Flash Example for STM32N6570-DK\n//!\n//! This example demonstrates how to use the XSPI peripheral to communicate with\n//! the external NOR flash (MX66UW1G45G) on the STM32N6570-DK board.\n//!\n//! The flash is connected via XSPI2 using Octo-SPI interface:\n//! - Memory-mapped address: 0x70000000\n//! - Size: 1 Gbit (128 MB)\n//! - Interface: Octo-SPI (8-bit data width)\n//!\n//! Pin mapping (XSPIM Port 2):\n//! - PN2: IO0, PN3: IO1, PN4: IO2, PN5: IO3\n//! - PN8: IO4, PN9: IO5, PN10: IO6, PN11: IO7\n//! - PN6: CLK, PN7: NCLK (optional)\n//! - PN1: NCS1, PN0: DQS0 (optional)\n\nuse core::cmp::min;\n\nuse defmt::{error, info, warn};\nuse embassy_executor::Spawner;\nuse embassy_stm32::gpio::{Level, Output, Speed};\nuse embassy_stm32::mode::Blocking;\nuse embassy_stm32::rcc::{IcConfig, Icint, Icsel, XspiClkSrc};\nuse embassy_stm32::xspi::{\n    AddressSize, ChipSelectHighTime, DummyCycles, FIFOThresholdLevel, Instance, MemorySize, MemoryType, TransferConfig,\n    WrapSize, Xspi, XspiWidth,\n};\nuse embassy_time::Timer;\nuse {defmt_rtt as _, panic_probe as _};\n\nconst MEMORY_PAGE_SIZE: usize = 256;\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    // Configure RCC with IC4 for XSPI2 kernel clock\n    let mut config = embassy_stm32::Config::default();\n    config.rcc.ic4 = Some(IcConfig {\n        source: Icsel::PLL2,          // PLL2 in bypass mode = HSI 64 MHz\n        divider: Icint::from_bits(0), // DIV1 = no division\n    });\n    config.rcc.mux.xspi2sel = XspiClkSrc::IC4; // Select IC4 for XSPI2 kernel clock\n    config.rcc.vddio3_1v8 = true;\n    let p = embassy_stm32::init(config);\n    info!(\"XSPI Flash Example for STM32N6570-DK\");\n\n    // Configure XSPI for the external NOR flash\n    let spi_config = embassy_stm32::xspi::Config {\n        fifo_threshold: FIFOThresholdLevel::_4Bytes,\n        memory_type: MemoryType::Macronix, // MX66UW1G45G is Macronix\n        delay_hold_quarter_cycle: true,\n        device_size: MemorySize::_128MiB, // 1 Gbit = 128 MiB\n        chip_select_high_time: ChipSelectHighTime::_2Cycle,\n        free_running_clock: false,\n        clock_mode: false,\n        wrap_size: WrapSize::None,\n        clock_prescaler: 8,    // Conservative prescaler for debugging (64MHz / 8 = 8MHz)\n        sample_shifting: true, // Enable sample shifting for better signal capture\n        chip_select_boundary: 0,\n        max_transfer: 0,\n        refresh: 0,\n    };\n\n    // Create XSPI driver for flash (XSPI2, Port P2 pins)\n    // Pin mapping: CLK=PN6, D0=PN2, D1=PN3, D2=PN4, D3=PN5, D4=PN8, D5=PN9, D6=PN10, D7=PN11, NCS=PN1\n    let xspi = Xspi::new_blocking_xspi(\n        p.XSPI2, p.PN6, p.PN2, p.PN3, p.PN4, p.PN5, p.PN8, p.PN9, p.PN10, p.PN11, p.PN1, spi_config,\n    );\n\n    // Wait for flash to be ready after power-on\n    info!(\"Waiting for flash to initialize...\");\n    Timer::after_millis(100).await;\n\n    let mut flash = SpiFlashMemory::new(xspi);\n\n    // Read flash ID multiple times to check consistency\n    let flash_id = flash.read_id();\n    info!(\"FLASH ID: {=[u8]:x}\", flash_id);\n\n    // Read status register to check if flash is responding\n    let sr = flash.read_sr();\n    info!(\"Status Register: 0x{:02x}\", sr);\n\n    // If ID is all zeros, the flash is not responding\n    if flash_id == [0, 0, 0] {\n        warn!(\"Flash not responding! Check pin connections and clock configuration.\");\n        warn!(\"Expected ID for MX66UW1G45G: [0xC2, 0x85, 0x3B]\");\n\n        // Try reading ID again after delay\n        Timer::after_millis(100).await;\n        let flash_id2 = flash.read_id();\n        info!(\"FLASH ID (retry): {=[u8]:x}\", flash_id2);\n\n        if flash_id2 == [0, 0, 0] {\n            error!(\"Flash still not responding. Aborting to prevent crash.\");\n            // Blink LED fast to indicate error\n            let mut led = Output::new(p.PG10, Level::High, Speed::Low);\n            loop {\n                led.toggle();\n                Timer::after_millis(100).await;\n            }\n        }\n    }\n\n    // Flash size: 128 MiB = 0x08000000 bytes\n    // Sector size: 4 KiB = 0x1000 bytes\n    // Test at last sector: 0x08000000 - 0x1000 = 0x07FFF000\n    const TEST_ADDR: u32 = 0x07FFF000;\n\n    // 1. Read original data and save to RAM\n    let mut original_data = [0u8; 256];\n    info!(\"Reading original data at address 0x{:08x}...\", TEST_ADDR);\n    flash.read_memory(TEST_ADDR, &mut original_data);\n    info!(\"Original data: {=[u8]:x}\", &original_data[0..16]);\n\n    // 2. Erase the sector at test address\n    info!(\"Erasing sector at 0x{:08x}...\", TEST_ADDR);\n    flash.erase_sector(TEST_ADDR);\n\n    // 3. Write test pattern\n    let mut test_data = [0u8; 256];\n    for i in 0..256 {\n        test_data[i] = i as u8;\n    }\n    info!(\"Writing test pattern...\");\n    flash.write_memory(TEST_ADDR, &test_data);\n\n    // 4. Read back test data\n    let mut read_data = [0u8; 256];\n    flash.read_memory(TEST_ADDR, &mut read_data);\n    info!(\"Read back: {=[u8]:x}\", &read_data[0..16]);\n\n    // 5. Verify test data\n    if test_data == read_data {\n        info!(\"Verification PASSED!\");\n    } else {\n        error!(\"Verification FAILED!\");\n    }\n\n    // 6. Restore original data\n    info!(\"Restoring original data...\");\n    flash.erase_sector(TEST_ADDR);\n    flash.write_memory(TEST_ADDR, &original_data);\n\n    // Verify restoration\n    let mut verify_restore = [0u8; 256];\n    flash.read_memory(TEST_ADDR, &mut verify_restore);\n    if original_data == verify_restore {\n        info!(\"Original data restored successfully!\");\n    } else {\n        error!(\"Failed to restore original data!\");\n    }\n\n    // =========================================================================\n    // Memory-Mapped Mode Demonstration\n    // =========================================================================\n    // NOR flash in memory-mapped mode has limitations:\n    // - Reads: Work seamlessly - just dereference pointers to the memory-mapped address space\n    // - Writes: NOR flash requires erase-before-write and page programming. Memory-mapped\n    //   writes don't bypass this - you still need indirect mode for erase/program cycles.\n\n    info!(\"Enabling memory-mapped mode...\");\n    flash.enable_mm();\n\n    const MM_BASE: u32 = 0x70000000;\n    let mm_base_ptr = MM_BASE as *const u8;\n\n    // --- Memory-Mapped READS ---\n\n    // 1. Single u32 read (basic demonstration)\n    let test_u32 = unsafe { *((MM_BASE + TEST_ADDR) as *const u32) };\n    info!(\"MM read u32 at 0x{:08x}: 0x{:08x}\", MM_BASE + TEST_ADDR, test_u32);\n\n    // 2. Read multiple sequential u32s\n    info!(\"Reading multiple u32s in memory-mapped mode...\");\n    let mut mm_values = [0u32; 4];\n    for i in 0..4 {\n        mm_values[i] = unsafe { core::ptr::read_volatile((MM_BASE + TEST_ADDR + i as u32 * 4) as *const u32) };\n    }\n    info!(\n        \"MM read 4 x u32: {:08x} {:08x} {:08x} {:08x}\",\n        mm_values[0], mm_values[1], mm_values[2], mm_values[3]\n    );\n\n    // 3. Read a buffer using volatile reads\n    let mut mm_buffer = [0u8; 16];\n    for i in 0..16 {\n        mm_buffer[i] = unsafe { core::ptr::read_volatile(mm_base_ptr.add(TEST_ADDR as usize + i)) };\n    }\n    info!(\"MM read 16 bytes: {=[u8]:x}\", mm_buffer);\n\n    // 4. Compare with previously read indirect mode data\n    if mm_buffer == original_data[0..16] {\n        info!(\"Memory-mapped read matches indirect mode read!\");\n    } else {\n        error!(\"Memory-mapped read differs from indirect mode!\");\n    }\n\n    flash.disable_mm();\n    info!(\"Memory-mapped mode disabled\");\n\n    // --- Memory-Mapped WRITE demonstration ---\n    // NOR flash requires erase+program cycle. Demonstrate the pattern:\n    // 1. Write new data in indirect mode (requires erase first)\n    // 2. Re-enable memory-mapped mode\n    // 3. Verify via memory-mapped reads\n    info!(\"Demonstrating memory-mapped write pattern for NOR flash...\");\n\n    // Write new test data (requires leaving MM mode for erase/program)\n    let mut new_test_data = [0u8; 256];\n    for i in 0..256 {\n        new_test_data[i] = (255 - i) as u8; // Inverse pattern\n    }\n\n    // Erase and write in indirect mode\n    flash.erase_sector(TEST_ADDR);\n    flash.write_memory(TEST_ADDR, &new_test_data);\n    info!(\"Wrote new pattern in indirect mode\");\n\n    // Re-enable memory-mapped mode and verify\n    flash.enable_mm();\n    info!(\"Re-enabled memory-mapped mode\");\n\n    // Read back via memory-mapped mode\n    let mut mm_verify = [0u8; 16];\n    for i in 0..16 {\n        mm_verify[i] = unsafe { core::ptr::read_volatile(mm_base_ptr.add(TEST_ADDR as usize + i)) };\n    }\n    info!(\"MM verify read: {=[u8]:x}\", mm_verify);\n\n    if mm_verify == new_test_data[0..16] {\n        info!(\"Memory-mapped verify PASSED!\");\n    } else {\n        error!(\"Memory-mapped verify FAILED!\");\n    }\n\n    flash.disable_mm();\n\n    // Restore original data\n    flash.erase_sector(TEST_ADDR);\n    flash.write_memory(TEST_ADDR, &original_data);\n    info!(\"Original data restored\");\n\n    info!(\"Example complete!\");\n\n    // Blink LED to indicate success\n    let mut led = Output::new(p.PG10, Level::High, Speed::Low);\n    loop {\n        led.toggle();\n        Timer::after_millis(500).await;\n    }\n}\n\n/// SPI Flash Memory driver for MX66UW1G45G\npub struct SpiFlashMemory<I: Instance> {\n    xspi: Xspi<'static, I, Blocking>,\n}\n\n/// SPI mode commands for MX66UW1G45G flash memory\n#[allow(dead_code)]\n#[repr(u8)]\nenum SpiCommand {\n    Read3B = 0x03,\n    FastRead3B = 0x0B,\n    PageProgram3B = 0x02,\n    SectorErase3B = 0x20,\n    Read4B = 0x13,\n    FastRead4B = 0x0C,\n    PageProgram4B = 0x12,\n    SectorErase4B = 0x21,\n    BlockErase4B = 0xDC,\n    ChipErase = 0x60,\n    WriteEnable = 0x06,\n    WriteDisable = 0x04,\n    ResetEnable = 0x66,\n    ResetMemory = 0x99,\n    ReadIdentification = 0x9F,\n    ReadStatusRegister = 0x05,\n}\n\nimpl<I: Instance> SpiFlashMemory<I> {\n    pub fn new(xspi: Xspi<'static, I, Blocking>) -> Self {\n        let mut memory = Self { xspi };\n        memory.reset_memory();\n        memory\n    }\n\n    pub fn reset_memory(&mut self) {\n        self.exec_command(SpiCommand::ResetEnable as u8);\n        self.exec_command(SpiCommand::ResetMemory as u8);\n        self.wait_write_finish();\n    }\n\n    fn exec_command(&mut self, cmd: u8) {\n        let transaction = TransferConfig {\n            iwidth: XspiWidth::SING,\n            adwidth: XspiWidth::NONE,\n            dwidth: XspiWidth::NONE,\n            instruction: Some(cmd as u32),\n            address: None,\n            dummy: DummyCycles::_0,\n            ..Default::default()\n        };\n        self.xspi.blocking_command(&transaction).unwrap();\n    }\n\n    pub fn enable_write(&mut self) {\n        self.exec_command(SpiCommand::WriteEnable as u8);\n    }\n\n    pub fn read_id(&mut self) -> [u8; 3] {\n        let mut buffer = [0; 3];\n        let transaction = TransferConfig {\n            iwidth: XspiWidth::SING,\n            isize: AddressSize::_8bit,\n            adwidth: XspiWidth::NONE,\n            dwidth: XspiWidth::SING,\n            instruction: Some(SpiCommand::ReadIdentification as u32),\n            ..Default::default()\n        };\n        self.xspi.blocking_read(&mut buffer, transaction).unwrap();\n        buffer\n    }\n\n    pub fn read_sr(&mut self) -> u8 {\n        let mut buffer = [0; 1];\n        let transaction = TransferConfig {\n            iwidth: XspiWidth::SING,\n            isize: AddressSize::_8bit,\n            adwidth: XspiWidth::NONE,\n            dwidth: XspiWidth::SING,\n            instruction: Some(SpiCommand::ReadStatusRegister as u32),\n            ..Default::default()\n        };\n        self.xspi.blocking_read(&mut buffer, transaction).unwrap();\n        buffer[0]\n    }\n\n    fn wait_write_finish(&mut self) {\n        while (self.read_sr() & 0x01) != 0 {}\n    }\n\n    pub fn read_memory(&mut self, addr: u32, buffer: &mut [u8]) {\n        let transaction = TransferConfig {\n            iwidth: XspiWidth::SING,\n            adwidth: XspiWidth::SING,\n            adsize: AddressSize::_32bit,\n            dwidth: XspiWidth::SING,\n            instruction: Some(SpiCommand::FastRead4B as u32),\n            dummy: DummyCycles::_8,\n            address: Some(addr),\n            ..Default::default()\n        };\n        self.xspi.blocking_read(buffer, transaction).unwrap();\n    }\n\n    pub fn erase_sector(&mut self, addr: u32) {\n        let transaction = TransferConfig {\n            iwidth: XspiWidth::SING,\n            adwidth: XspiWidth::SING,\n            adsize: AddressSize::_32bit,\n            dwidth: XspiWidth::NONE,\n            instruction: Some(SpiCommand::SectorErase4B as u32),\n            address: Some(addr),\n            dummy: DummyCycles::_0,\n            ..Default::default()\n        };\n        self.enable_write();\n        self.xspi.blocking_command(&transaction).unwrap();\n        self.wait_write_finish();\n    }\n\n    fn write_page(&mut self, addr: u32, buffer: &[u8], len: usize) {\n        assert!(\n            (len as u32 + (addr & 0x000000ff)) <= MEMORY_PAGE_SIZE as u32,\n            \"write_page(): page write length exceeds page boundary\"\n        );\n\n        let transaction = TransferConfig {\n            iwidth: XspiWidth::SING,\n            adsize: AddressSize::_32bit,\n            adwidth: XspiWidth::SING,\n            dwidth: XspiWidth::SING,\n            instruction: Some(SpiCommand::PageProgram4B as u32),\n            address: Some(addr),\n            dummy: DummyCycles::_0,\n            ..Default::default()\n        };\n        self.enable_write();\n        self.xspi.blocking_write(buffer, transaction).unwrap();\n        self.wait_write_finish();\n    }\n\n    pub fn write_memory(&mut self, addr: u32, buffer: &[u8]) {\n        let mut left = buffer.len();\n        let mut place = addr;\n        let mut chunk_start = 0;\n\n        while left > 0 {\n            let max_chunk_size = MEMORY_PAGE_SIZE - (place & 0x000000ff) as usize;\n            let chunk_size = min(max_chunk_size, left);\n            let chunk = &buffer[chunk_start..(chunk_start + chunk_size)];\n            self.write_page(place, chunk, chunk_size);\n            place += chunk_size as u32;\n            left -= chunk_size;\n            chunk_start += chunk_size;\n        }\n    }\n\n    pub fn enable_mm(&mut self) {\n        let read_config = TransferConfig {\n            iwidth: XspiWidth::SING,\n            isize: AddressSize::_8bit,\n            adwidth: XspiWidth::SING,\n            adsize: AddressSize::_32bit,\n            dwidth: XspiWidth::SING,\n            instruction: Some(SpiCommand::FastRead4B as u32),\n            dummy: DummyCycles::_8,\n            ..Default::default()\n        };\n\n        let write_config = TransferConfig {\n            iwidth: XspiWidth::SING,\n            isize: AddressSize::_8bit,\n            adwidth: XspiWidth::SING,\n            adsize: AddressSize::_32bit,\n            dwidth: XspiWidth::SING,\n            instruction: Some(SpiCommand::PageProgram4B as u32),\n            dummy: DummyCycles::_0,\n            ..Default::default()\n        };\n        self.xspi.enable_memory_mapped_mode(read_config, write_config).unwrap();\n    }\n\n    pub fn disable_mm(&mut self) {\n        self.xspi.disable_memory_mapped_mode();\n    }\n}\n"
  },
  {
    "path": "examples/stm32n6/src/bin/xspi_psram.rs",
    "content": "#![no_std]\n#![no_main]\n\n//! XSPI PSRAM Example for STM32N6570-DK\n//!\n//! This example demonstrates how to use the XSPI peripheral to communicate with\n//! the external APS256XX PSRAM (32 MB) on the STM32N6570-DK board.\n//!\n//! The PSRAM is connected via XSPI1 using Hexadeca-SPI interface (16-bit data width):\n//! - Memory-mapped address: 0x90000000\n//! - Size: 256 Mbit (32 MiB)\n//! - Interface: Hexadeca-SPI (16-bit data width, DTR mode)\n//!\n//! Pin mapping (XSPIM Port 1):\n//! - PO4: CLK\n//! - PO0: NCS1\n//! - PO2: DQS0\n//! - PO3: DQS1\n//! - PP0-PP15: D0-D15\n//!\n//! Note: Unlike NOR flash, PSRAM supports both read AND write directly via\n//! memory-mapped mode without needing indirect mode for data transfers.\n//!\n//! IMPORTANT: Sub-word (byte) writes to PSRAM don't work correctly in memory-mapped\n//! mode. The exact cause is not yet fully researched (maybe AXI bus or XSPI\n//! peripheral behavior). Use the `psram_write_byte()` helper which performs a\n//! 64-bit read-modify-write as a workaround.\n//!\n//! NOTE: Some test failures are expected in DEBUG 2/2b/2c tests - these demonstrate\n//! the sub-word write issue. The 64-bit R-M-W workaround tests (DEBUG 6, FINAL TEST)\n//! should pass.\n\nuse defmt::{error, info};\nuse embassy_executor::Spawner;\nuse embassy_stm32::gpio::{Level, Output, Speed};\nuse embassy_stm32::mode::Blocking;\nuse embassy_stm32::rcc::{CpuClk, IcConfig, Icint, Icsel, Pll, Plldivm, Pllpdiv, Pllsel, SysClk, XspiClkSrc};\nuse embassy_stm32::xspi::{\n    AddressSize, ChipSelectHighTime, DummyCycles, FIFOThresholdLevel, Instance, MemorySize, MemoryType, TransferConfig,\n    WrapSize, Xspi, XspiWidth,\n};\nuse embassy_time::Timer;\nuse {defmt_rtt as _, panic_probe as _};\n\n/// APS256XX Commands\n#[allow(dead_code)]\nmod aps256_cmd {\n    pub const READ: u32 = 0x00; // Synchronous Read (used by C HAL for memory-mapped)\n    pub const READ_LINEAR_BURST: u32 = 0x20; // Linear Burst Read\n    pub const WRITE: u32 = 0x80; // Synchronous Write (used by C HAL for memory-mapped)\n    pub const WRITE_LINEAR_BURST: u32 = 0xA0; // Linear Burst Write\n    pub const READ_REG: u32 = 0x40;\n    pub const WRITE_REG: u32 = 0xC0;\n    pub const RESET: u32 = 0xFF;\n}\n\n/// APS256XX Mode Register Addresses\nmod aps256_mr {\n    pub const MR0: u32 = 0x00000000;\n    pub const MR4: u32 = 0x00000004;\n    pub const MR8: u32 = 0x00000008;\n}\n\n/// Memory-mapped base address for XSPI1\nconst MM_BASE: u32 = 0x90000000;\n\n/// Write a single byte to PSRAM via 64-bit read-modify-write.\n///\n/// Workaround for sub-word write corruption in memory-mapped mode. The exact cause\n/// is not yet fully researched - possibly related to AXI bus behavior or XSPI\n/// peripheral quirks. This helper reads the full 64-bit word, modifies the target\n/// byte, and writes back the complete 64-bit value.\n\n#[inline(never)]\nunsafe fn psram_write_byte(addr: *mut u8, val: u8) {\n    let aligned_addr = (addr as usize & !7) as *mut u64; // 8-byte align\n    let byte_offset = addr as usize & 7;\n\n    // SAFETY: caller guarantees addr is valid for read/write in PSRAM memory-mapped region\n    unsafe {\n        let current = core::ptr::read_volatile(aligned_addr);\n        let shift = byte_offset * 8;\n        let mask = !(0xFFu64 << shift);\n        let new_val = (current & mask) | ((val as u64) << shift);\n        core::ptr::write_volatile(aligned_addr, new_val);\n    }\n}\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    // Configure RCC with full clock tree for 200MHz XSPI operation\n    let mut config = embassy_stm32::Config::default();\n\n    // Configure PLL1: HSI 64MHz / 4 * 75 = 1200MHz\n    config.rcc.pll1 = Some(Pll::Oscillator {\n        source: Pllsel::HSI,\n        divm: Plldivm::from_bits(4), // 64MHz / 4 = 16MHz VCO input\n        fractional: 0,\n        divn: 75,                     // 16MHz * 75 = 1200MHz VCO\n        divp1: Pllpdiv::from_bits(1), // 1200MHz / 1 = 1200MHz output\n        divp2: Pllpdiv::from_bits(1),\n    });\n\n    // Configure IC1: CPU clock = PLL1 / 2 = 600 MHz\n    config.rcc.ic1 = Some(IcConfig {\n        source: Icsel::PLL1,\n        divider: Icint::from_bits(1), // DIV2: (1+1) = 2\n    });\n    config.rcc.cpu = CpuClk::Ic1;\n\n    // Configure IC2/IC6/IC11: System bus = PLL1 / 3 = 400 MHz\n    config.rcc.ic2 = Some(IcConfig {\n        source: Icsel::PLL1,\n        divider: Icint::from_bits(2), // DIV3: (2+1) = 3\n    });\n    config.rcc.ic6 = Some(IcConfig {\n        source: Icsel::PLL1,\n        divider: Icint::from_bits(3), // DIV4: (3+1) = 4 (C HAL uses /4)\n    });\n    config.rcc.ic11 = Some(IcConfig {\n        source: Icsel::PLL1,\n        divider: Icint::from_bits(3), // DIV4: (3+1) = 4 (C HAL uses /4)\n    });\n    config.rcc.sys = SysClk::Ic2;\n\n    // Configure IC3: XSPI1 kernel clock = PLL1 / 6 = 200 MHz\n    config.rcc.ic3 = Some(IcConfig {\n        source: Icsel::PLL1,\n        divider: Icint::from_bits(5), // DIV6: (5+1) = 6\n    });\n\n    config.rcc.mux.xspi1sel = XspiClkSrc::IC3;\n    config.rcc.vddio2_1v8 = true; // Critical: GPIOO/GPIOP require 1.8V for PSRAM\n    let p = embassy_stm32::init(config);\n\n    info!(\"XSPI PSRAM Example for STM32N6570-DK\");\n\n    // === DCACHE DISABLE TEST ===\n    // Disable D-cache entirely to test if byte write corruption is cache-related\n    info!(\"Disabling D-cache for testing...\");\n    let mut cp = unsafe { cortex_m::Peripherals::steal() };\n    cp.SCB.disable_dcache(&mut cp.CPUID);\n    info!(\"D-cache disabled\");\n\n    // Configure LED for status indication\n    let mut led = Output::new(p.PG10, Level::Low, Speed::Low);\n\n    // Configure XSPI for the external PSRAM (APS256XX)\n    let spi_config = embassy_stm32::xspi::Config {\n        fifo_threshold: FIFOThresholdLevel::_4Bytes,\n        memory_type: MemoryType::APMemory16Bits, // AP Memory 16-bit mode for PSRAM\n        delay_hold_quarter_cycle: false,         // Match C HAL: disabled for PSRAM\n        device_size: MemorySize::_32MiB,         // 256 Mbit = 32 MiB\n        chip_select_high_time: ChipSelectHighTime::_5Cycle,\n        free_running_clock: false,\n        clock_mode: false,\n        wrap_size: WrapSize::None,\n        clock_prescaler: 1,       // Start at 100MHz for initialization (C HAL approach)\n        sample_shifting: false,   // Match C HAL: no sample shifting\n        chip_select_boundary: 14, // 16KB boundary for row crossing\n        max_transfer: 0,\n        refresh: 0,\n    };\n    // Create XSPI driver for PSRAM (XSPI1, Port 1 pins)\n    // Pin mapping:\n    // CLK=PO4, D0-D15=PP0-PP15, NCS=PO0, DQS0=PO2, DQS1=PO3\n    let xspi = Xspi::new_blocking_xspi_hexa_dqs_dual(\n        p.XSPI1, p.PO4, // CLK\n        p.PP0, p.PP1, p.PP2, p.PP3, // D0-D3\n        p.PP4, p.PP5, p.PP6, p.PP7, // D4-D7\n        p.PP8, p.PP9, p.PP10, p.PP11, // D8-D11\n        p.PP12, p.PP13, p.PP14, p.PP15, // D12-D15\n        p.PO0,  // NCS1\n        p.PO2,  // DQS0\n        p.PO3,  // DQS1\n        spi_config,\n    );\n\n    let mut psram = Aps256Psram::new(xspi);\n\n    // Configure PSRAM mode registers (at 100MHz for reliable initialization)\n    info!(\"Configuring PSRAM mode registers...\");\n    psram.configure_mode_registers();\n    info!(\"Mode registers configured\");\n\n    // Switch to full 200MHz speed after configuration (mirrors C HAL BSP approach)\n    info!(\"Switching to full speed (200MHz)...\");\n    psram.set_full_speed();\n    // Enable memory-mapped mode for PSRAM read/write\n    info!(\"Enabling memory-mapped mode...\");\n    psram.enable_memory_mapped_mode();\n    info!(\"Memory-mapped mode enabled\");\n\n    // === DEBUG 1: Dump XSPI registers after memory-mapped enable ===\n    {\n        let xspi1 = embassy_stm32::pac::XSPI1;\n        info!(\"XSPI1 registers after MM enable:\");\n        info!(\"  CR:   0x{:08x}\", xspi1.cr().read().0);\n        info!(\"  CCR:  0x{:08x}\", xspi1.ccr().read().0);\n        info!(\"  TCR:  0x{:08x}\", xspi1.tcr().read().0);\n        info!(\"  WCCR: 0x{:08x}\", xspi1.wccr().read().0);\n        info!(\"  WTCR: 0x{:08x}\", xspi1.wtcr().read().0);\n        info!(\"  WIR:  0x{:08x}\", xspi1.wir().read().0);\n    }\n\n    // Test address at 1 MB offset\n    const TEST_ADDR: u32 = 0x00100000;\n    const TEST_SIZE: usize = 256;\n    let mut total_errors: usize = 0;\n\n    // Note: D-cache disabled at start, so no cache operations needed\n\n    // Test single byte write and readback\n    info!(\"Testing single byte write...\");\n    let mm_ptr = (MM_BASE + TEST_ADDR) as *mut u8;\n    unsafe { core::ptr::write_volatile(mm_ptr, 0xAA) };\n\n    info!(\"Testing single byte read...\");\n    let mm_ptr_read = (MM_BASE + TEST_ADDR) as *const u8;\n    let val = unsafe { core::ptr::read_volatile(mm_ptr_read) };\n    info!(\"Single read completed, value=0x{:02x}\", val);\n\n    // === DEBUG 2: Raw 8-bit writes (some failures expected) ===\n    // This test demonstrates sub-word write corruption. The exact cause is not yet\n    // fully researched. Some byte offsets will fail.\n    info!(\"DEBUG 2: Raw 8-bit writes with immediate readback (first 32 bytes)...\");\n    let mut raw8_errors = 0;\n    for i in 0..32 {\n        let val = i as u8;\n        let ptr_i = unsafe { mm_ptr.add(i) };\n        let ptr_i_read = unsafe { mm_ptr_read.add(i) };\n        unsafe { core::ptr::write_volatile(ptr_i, val) };\n        cortex_m::asm::dsb();\n        let readback = unsafe { core::ptr::read_volatile(ptr_i_read) };\n        if val != readback {\n            raw8_errors += 1;\n            error!(\"RAW fail: offset {} wrote 0x{:02x} read 0x{:02x}\", i, val, readback);\n        } else {\n            info!(\"RAW ok:   offset {} = 0x{:02x}\", i, val);\n        }\n    }\n    info!(\"DEBUG 2: Raw 8-bit writes: {} errors out of 32\", raw8_errors);\n    total_errors += raw8_errors;\n\n    // === DEBUG 2b: Raw 16-bit writes with error count ===\n    info!(\"DEBUG 2b: Testing raw 16-bit writes (32 bytes = 16 u16)...\");\n    let mm_ptr_raw16 = (MM_BASE + TEST_ADDR + 0x0800) as *mut u16;\n    let mm_ptr_raw16_read = mm_ptr_raw16 as *const u16;\n    for i in 0..16usize {\n        let val = ((i * 2) as u16) | (((i * 2 + 1) as u16) << 8);\n        unsafe { core::ptr::write_volatile(mm_ptr_raw16.add(i), val) };\n    }\n\n    let mut raw16_errors = 0;\n    for i in 0..16usize {\n        let expected = ((i * 2) as u16) | (((i * 2 + 1) as u16) << 8);\n        let actual = unsafe { core::ptr::read_volatile(mm_ptr_raw16_read.add(i)) };\n        if actual != expected {\n            raw16_errors += 1;\n            error!(\"RAW16 fail idx {}: expected 0x{:04x} got 0x{:04x}\", i, expected, actual);\n        }\n    }\n    info!(\"DEBUG 2b: Raw 16-bit writes: {} errors out of 16\", raw16_errors);\n    total_errors += raw16_errors;\n\n    // === DEBUG 2c: Raw 32-bit writes with error count ===\n    info!(\"DEBUG 2c: Testing raw 32-bit writes (32 bytes = 8 u32)...\");\n    let mm_ptr_raw32 = (MM_BASE + TEST_ADDR + 0x0C00) as *mut u32;\n    let mm_ptr_raw32_read = mm_ptr_raw32 as *const u32;\n    for i in 0..8usize {\n        let val = ((i * 4) as u32)\n            | (((i * 4 + 1) as u32) << 8)\n            | (((i * 4 + 2) as u32) << 16)\n            | (((i * 4 + 3) as u32) << 24);\n        unsafe { core::ptr::write_volatile(mm_ptr_raw32.add(i), val) };\n    }\n\n    let mut raw32_errors = 0;\n    for i in 0..8usize {\n        let expected = ((i * 4) as u32)\n            | (((i * 4 + 1) as u32) << 8)\n            | (((i * 4 + 2) as u32) << 16)\n            | (((i * 4 + 3) as u32) << 24);\n        let actual = unsafe { core::ptr::read_volatile(mm_ptr_raw32_read.add(i)) };\n        if actual != expected {\n            raw32_errors += 1;\n            error!(\"RAW32 fail idx {}: expected 0x{:08x} got 0x{:08x}\", i, expected, actual);\n        }\n    }\n    info!(\"DEBUG 2c: Raw 32-bit writes: {} errors out of 8\", raw32_errors);\n    total_errors += raw32_errors;\n\n    // === DEBUG 3: Test 16-bit aligned writes ===\n    info!(\"DEBUG 3: Testing 16-bit aligned writes...\");\n    let mm_ptr_u16 = (MM_BASE + TEST_ADDR + 0x1000) as *mut u16; // Different offset to avoid overlap\n    let mm_ptr_u16_read = (MM_BASE + TEST_ADDR + 0x1000) as *const u16;\n    for i in 0..16 {\n        let val = ((i * 2) as u16) | (((i * 2 + 1) as u16) << 8); // e.g. 0x0100, 0x0302...\n        unsafe { core::ptr::write_volatile(mm_ptr_u16.add(i), val) };\n    }\n\n    info!(\"Reading back 16-bit aligned writes...\");\n    let mut u16_errors = 0;\n    for i in 0..16 {\n        let expected = ((i * 2) as u16) | (((i * 2 + 1) as u16) << 8);\n        let actual = unsafe { core::ptr::read_volatile(mm_ptr_u16_read.add(i)) };\n        if actual != expected {\n            u16_errors += 1;\n            error!(\n                \"U16 mismatch at idx {}: expected 0x{:04x} got 0x{:04x}\",\n                i, expected, actual\n            );\n        } else {\n            info!(\"U16 ok: idx {} = 0x{:04x}\", i, actual);\n        }\n    }\n    info!(\"DEBUG 3: 16-bit writes: {} errors out of 16\", u16_errors);\n    total_errors += u16_errors;\n\n    // === DEBUG 4: Test 32-bit aligned writes ===\n    info!(\"DEBUG 4: Testing 32-bit aligned writes...\");\n    let mm_ptr_u32 = (MM_BASE + TEST_ADDR + 0x2000) as *mut u32; // Different offset\n    let mm_ptr_u32_read = (MM_BASE + TEST_ADDR + 0x2000) as *const u32;\n    for i in 0..8 {\n        // Write pattern: bytes i*4, i*4+1, i*4+2, i*4+3 packed into u32\n        let val = ((i * 4) as u32)\n            | (((i * 4 + 1) as u32) << 8)\n            | (((i * 4 + 2) as u32) << 16)\n            | (((i * 4 + 3) as u32) << 24);\n        unsafe { core::ptr::write_volatile(mm_ptr_u32.add(i), val) };\n    }\n\n    info!(\"Reading back 32-bit aligned writes...\");\n    let mut u32_errors = 0;\n    for i in 0..8 {\n        let expected = ((i * 4) as u32)\n            | (((i * 4 + 1) as u32) << 8)\n            | (((i * 4 + 2) as u32) << 16)\n            | (((i * 4 + 3) as u32) << 24);\n        let actual = unsafe { core::ptr::read_volatile(mm_ptr_u32_read.add(i)) };\n        if actual != expected {\n            u32_errors += 1;\n            error!(\n                \"U32 mismatch at idx {}: expected 0x{:08x} got 0x{:08x}\",\n                i, expected, actual\n            );\n        } else {\n            info!(\"U32 ok: idx {} = 0x{:08x}\", i, actual);\n        }\n    }\n    info!(\"DEBUG 4: 32-bit writes: {} errors out of 8\", u32_errors);\n    total_errors += u32_errors;\n\n    // === DEBUG 5: Test 64-bit aligned writes ===\n    // Hypothesis: Native 64-bit writes should work at all alignments\n    info!(\"DEBUG 5: Testing 64-bit aligned writes...\");\n    let mm_ptr_u64 = (MM_BASE + TEST_ADDR + 0x3000) as *mut u64; // Different offset\n    let mm_ptr_u64_read = (MM_BASE + TEST_ADDR + 0x3000) as *const u64;\n    for i in 0..4usize {\n        // Write pattern: bytes i*8..i*8+7 packed into u64\n        let val = ((i * 8) as u64)\n            | (((i * 8 + 1) as u64) << 8)\n            | (((i * 8 + 2) as u64) << 16)\n            | (((i * 8 + 3) as u64) << 24)\n            | (((i * 8 + 4) as u64) << 32)\n            | (((i * 8 + 5) as u64) << 40)\n            | (((i * 8 + 6) as u64) << 48)\n            | (((i * 8 + 7) as u64) << 56);\n        unsafe { core::ptr::write_volatile(mm_ptr_u64.add(i), val) };\n    }\n\n    info!(\"Reading back 64-bit aligned writes...\");\n    let mut u64_errors = 0;\n    for i in 0..4usize {\n        let expected = ((i * 8) as u64)\n            | (((i * 8 + 1) as u64) << 8)\n            | (((i * 8 + 2) as u64) << 16)\n            | (((i * 8 + 3) as u64) << 24)\n            | (((i * 8 + 4) as u64) << 32)\n            | (((i * 8 + 5) as u64) << 40)\n            | (((i * 8 + 6) as u64) << 48)\n            | (((i * 8 + 7) as u64) << 56);\n        let actual = unsafe { core::ptr::read_volatile(mm_ptr_u64_read.add(i)) };\n        if actual != expected {\n            u64_errors += 1;\n            error!(\n                \"U64 mismatch at idx {}: expected 0x{:016x} got 0x{:016x}\",\n                i, expected, actual\n            );\n        } else {\n            info!(\"U64 ok: idx {} = 0x{:016x}\", i, actual);\n        }\n    }\n    info!(\"DEBUG 5: 64-bit writes: {} errors out of 4\", u64_errors);\n    total_errors += u64_errors;\n\n    // === DEBUG 6: Test 64-bit R-M-W byte writes (workaround) ===\n    // This test uses the psram_write_byte() helper which performs 64-bit read-modify-write\n    info!(\"DEBUG 6: Testing 64-bit R-M-W byte writes (workaround)...\");\n    let mm_ptr_rmw = (MM_BASE + TEST_ADDR + 0x4000) as *mut u8; // Different offset\n    let mm_ptr_rmw_read = (MM_BASE + TEST_ADDR + 0x4000) as *const u8;\n\n    // First, zero out the region with 64-bit writes\n    let mm_ptr_rmw_u64 = mm_ptr_rmw as *mut u64;\n    for i in 0..4 {\n        unsafe { core::ptr::write_volatile(mm_ptr_rmw_u64.add(i), 0u64) };\n    }\n\n    // Now write bytes using 64-bit R-M-W helper\n    for i in 0..32 {\n        unsafe { psram_write_byte(mm_ptr_rmw.add(i), i as u8) };\n    }\n\n    // Verify\n    let mut rmw_errors = 0;\n    for i in 0..32 {\n        let expected = i as u8;\n        let actual = unsafe { core::ptr::read_volatile(mm_ptr_rmw_read.add(i)) };\n        if actual != expected {\n            rmw_errors += 1;\n            error!(\n                \"RMW fail: offset {} expected 0x{:02x} got 0x{:02x}\",\n                i, expected, actual\n            );\n        } else {\n            info!(\"RMW ok:   offset {} = 0x{:02x}\", i, actual);\n        }\n    }\n    info!(\"DEBUG 6: 64-bit R-M-W byte writes: {} errors out of 32\", rmw_errors);\n    total_errors += rmw_errors;\n\n    // === FINAL TEST: Full 256-byte write using 64-bit R-M-W helper ===\n    info!(\"FINAL TEST: Writing {} bytes using 64-bit R-M-W helper...\", TEST_SIZE);\n    let mm_ptr_final = (MM_BASE + TEST_ADDR + 0x5000) as *mut u8;\n    let mm_ptr_final_read = (MM_BASE + TEST_ADDR + 0x5000) as *const u8;\n\n    // First, zero out the region with 64-bit writes\n    let mm_ptr_final_u64 = mm_ptr_final as *mut u64;\n    for i in 0..(TEST_SIZE / 8) {\n        unsafe { core::ptr::write_volatile(mm_ptr_final_u64.add(i), 0u64) };\n    }\n\n    // Write using R-M-W helper\n    for i in 0..TEST_SIZE {\n        unsafe { psram_write_byte(mm_ptr_final.add(i), i as u8) };\n    }\n\n    // Read back and verify\n    info!(\"Verifying {} bytes...\", TEST_SIZE);\n    let mut errors = 0;\n    let mut first_error_positions: [u16; 16] = [0xFFFF; 16];\n    for i in 0..TEST_SIZE {\n        let expected = i as u8;\n        let actual = unsafe { core::ptr::read_volatile(mm_ptr_final_read.add(i)) };\n        if actual != expected {\n            if errors < 16 {\n                first_error_positions[errors] = i as u16;\n            }\n            errors += 1;\n        }\n    }\n\n    info!(\"FINAL TEST: {} errors out of {} bytes\", errors, TEST_SIZE);\n    if errors > 0 {\n        let num_to_show = core::cmp::min(errors, 16);\n        for j in 0..num_to_show {\n            let pos = first_error_positions[j] as usize;\n            let expected = pos as u8;\n            let actual = unsafe { core::ptr::read_volatile(mm_ptr_final_read.add(pos)) };\n            error!(\"  offset {}: expected 0x{:02x}, got 0x{:02x}\", pos, expected, actual);\n        }\n    }\n\n    // Show first 16 bytes read back\n    let mut read_sample = [0u8; 16];\n    for i in 0..16 {\n        read_sample[i] = unsafe { core::ptr::read_volatile(mm_ptr_final_read.add(i)) };\n    }\n    info!(\"Read back first 16 bytes: {=[u8]:x}\", read_sample);\n\n    let all_errors = total_errors + errors;\n    info!(\"=== SUMMARY ===\");\n    info!(\"Total errors across all tests: {}\", all_errors);\n\n    if all_errors == 0 {\n        info!(\"PSRAM test PASSED!\");\n        // Blink LED slowly to indicate success\n        loop {\n            led.toggle();\n            Timer::after_millis(500).await;\n        }\n    } else {\n        error!(\"PSRAM test FAILED with {} total errors!\", all_errors);\n        // Blink LED fast to indicate error\n        loop {\n            led.toggle();\n            Timer::after_millis(100).await;\n        }\n    }\n}\n\n/// APS256XX PSRAM driver\npub struct Aps256Psram<I: Instance> {\n    xspi: Xspi<'static, I, Blocking>,\n}\n\nimpl<I: Instance> Aps256Psram<I> {\n    pub fn new(xspi: Xspi<'static, I, Blocking>) -> Self {\n        Self { xspi }\n    }\n\n    /// Reset the PSRAM\n    pub fn reset(&mut self) {\n        // Reset command: 8-line instruction STR, 8-line address STR (24-bit), no data\n        let transaction = TransferConfig {\n            iwidth: XspiWidth::OCTO,\n            isize: AddressSize::_8bit,\n            idtr: false,\n            adwidth: XspiWidth::OCTO,\n            adsize: AddressSize::_24bit,\n            addtr: false,\n            dwidth: XspiWidth::NONE,\n            instruction: Some(aps256_cmd::RESET),\n            address: Some(0),\n            dummy: DummyCycles::_0,\n            dqse: false,\n            ..Default::default()\n        };\n        self.xspi.blocking_command(&transaction).unwrap();\n    }\n\n    /// Configure PSRAM mode registers for optimal operation\n    /// Following C HAL pattern: write configuration first, then read to verify\n    pub fn configure_mode_registers(&mut self) {\n        // C HAL regW_MR0[2]={0x30,0x8D} - LT=Fixed, RLC=4 (latency 6), Full drive\n        // DTR mode writes to MRn and MRn+1 simultaneously\n        info!(\"Writing MR0/MR1...\");\n        self.write_register(aps256_mr::MR0, 0x30, 0x8D);\n        info!(\"MR0/MR1 written\");\n\n        // C HAL regW_MR4[2]={0x20,0xF0} - WLC=7 for up to 200MHz\n        info!(\"Writing MR4/MR5...\");\n        self.write_register(aps256_mr::MR4, 0x20, 0xF0);\n        info!(\"MR4/MR5 written\");\n\n        // C HAL regW_MR8[2]={0x4B,0x08} - X16 mode, RBX, 2K burst\n        info!(\"Writing MR8/MR9...\");\n        self.write_register(aps256_mr::MR8, 0x4B, 0x08);\n        info!(\"MR8/MR9 written\");\n\n        // Now verify by reading back\n        info!(\"Reading MR0...\");\n        let mr0 = self.read_register(aps256_mr::MR0);\n        info!(\"MR0 read: 0x{:02x}\", mr0);\n\n        info!(\"Reading MR4...\");\n        let mr4 = self.read_register(aps256_mr::MR4);\n        info!(\"MR4 read: 0x{:02x}\", mr4);\n\n        info!(\"Reading MR8...\");\n        let mr8 = self.read_register(aps256_mr::MR8);\n        info!(\"MR8 read: 0x{:02x}\", mr8);\n\n        info!(\n            \"All registers configured: MR0=0x{:02x}, MR4=0x{:02x}, MR8=0x{:02x}\",\n            mr0, mr4, mr8\n        );\n    }\n\n    /// Switch to full speed (200MHz) after PSRAM configuration\n    /// This mirrors the C HAL BSP approach: init at 100MHz, then switch to 200MHz\n    pub fn set_full_speed(&mut self) {\n        self.xspi.set_clock_prescaler(0);\n    }\n\n    /// Read a mode register\n    pub fn read_register(&mut self, address: u32) -> u8 {\n        let mut buffer = [0u8; 2]; // DTR mode returns 2 bytes\n\n        // Register read: OCTO instruction (8-bit, STR), OCTO address (32-bit, DTR),\n        // OCTO data (DTR), dummy cycles = latency - 1, DQS enabled\n        let transaction = TransferConfig {\n            iwidth: XspiWidth::OCTO,\n            isize: AddressSize::_8bit,\n            idtr: false, // Instruction is STR\n            adwidth: XspiWidth::OCTO,\n            adsize: AddressSize::_32bit,\n            addtr: true, // Address is DTR\n            dwidth: XspiWidth::OCTO,\n            ddtr: true, // Data is DTR\n            instruction: Some(aps256_cmd::READ_REG),\n            address: Some(address),\n            dummy: DummyCycles::_5, // C HAL uses (latency-1) = 5 for register access\n            dqse: true,\n            ..Default::default()\n        };\n        self.xspi.blocking_read(&mut buffer, transaction).unwrap();\n        buffer[0]\n    }\n\n    /// Write a mode register pair (MRn and MRn+1)\n    /// In DTR mode, 2 bytes are sent to consecutive register addresses\n    pub fn write_register(&mut self, address: u32, value0: u8, value1: u8) {\n        let buffer = [value0, value1]; // DTR mode sends 2 bytes to MRn and MRn+1\n\n        // Register write: OCTO instruction (8-bit, STR), OCTO address (32-bit, DTR),\n        // OCTO data (DTR), no dummy cycles, DQS disabled\n        let transaction = TransferConfig {\n            iwidth: XspiWidth::OCTO,\n            isize: AddressSize::_8bit,\n            idtr: false, // Instruction is STR\n            adwidth: XspiWidth::OCTO,\n            adsize: AddressSize::_32bit,\n            addtr: true, // Address is DTR\n            dwidth: XspiWidth::OCTO,\n            ddtr: true, // Data is DTR\n            instruction: Some(aps256_cmd::WRITE_REG),\n            address: Some(address),\n            dummy: DummyCycles::_0,\n            dqse: false,\n            ..Default::default()\n        };\n        self.xspi.blocking_write(&buffer, transaction).unwrap();\n    }\n\n    /// Enable memory-mapped mode\n    pub fn enable_memory_mapped_mode(&mut self) {\n        // Read configuration for memory-mapped mode\n        let read_config = TransferConfig {\n            iwidth: XspiWidth::OCTO,\n            isize: AddressSize::_8bit,\n            idtr: false,\n            adwidth: XspiWidth::OCTO,\n            adsize: AddressSize::_32bit,\n            addtr: true,\n            dwidth: XspiWidth::HEXA,\n            ddtr: true,\n            instruction: Some(aps256_cmd::READ), // C HAL uses 0x00 (Synchronous Read)\n            dummy: DummyCycles::_6,              // Match C HAL: 6 dummy cycles for reads\n            dqse: true,\n            ..Default::default()\n        };\n\n        // Write configuration for memory-mapped mode\n        let write_config = TransferConfig {\n            iwidth: XspiWidth::OCTO,\n            isize: AddressSize::_8bit,\n            idtr: false,\n            adwidth: XspiWidth::OCTO,\n            adsize: AddressSize::_32bit,\n            addtr: true,\n            dwidth: XspiWidth::HEXA,\n            ddtr: true,\n            instruction: Some(aps256_cmd::WRITE), // C HAL uses 0x80 (Synchronous Write)\n            dummy: DummyCycles::_6,               // Match C HAL: 6 dummy cycles for writes\n            dqse: true,\n            ..Default::default()\n        };\n\n        self.xspi.enable_memory_mapped_mode(read_config, write_config).unwrap();\n    }\n}\n"
  },
  {
    "path": "examples/stm32u0/.cargo/config.toml",
    "content": "[target.'cfg(all(target_arch = \"arm\", target_os = \"none\"))']\n# replace stm32u083mctx with your chip as listed in `probe-rs chip list`\nrunner = \"probe-rs run --chip stm32u083mctx\"\n\n[build]\ntarget = \"thumbv6m-none-eabi\"\n\n[env]\nDEFMT_LOG = \"trace\"\n"
  },
  {
    "path": "examples/stm32u0/Cargo.toml",
    "content": "[package]\nedition = \"2024\"\nname = \"embassy-stm32u0-examples\"\nversion = \"0.1.0\"\nlicense = \"MIT OR Apache-2.0\"\npublish = false\n\n[dependencies]\n# Change stm32u083mc to your chip name, if necessary.\nembassy-stm32 = { version = \"0.6.0\", path = \"../../embassy-stm32\", features = [ \"defmt\", \"time-driver-any\", \"stm32u083mc\", \"memory-x\", \"unstable-pac\", \"exti\", \"chrono\"]  }\nembassy-sync = { version = \"0.8.0\", path = \"../../embassy-sync\", features = [\"defmt\"] }\nembassy-executor = { version = \"0.10.0\", path = \"../../embassy-executor\", features = [\"platform-cortex-m\", \"executor-thread\", \"defmt\"] }\nembassy-time = { version = \"0.5.1\", path = \"../../embassy-time\", features = [\"defmt\", \"defmt-timestamp-uptime\", \"tick-hz-32_768\"] }\nembassy-usb = { version = \"0.6.0\", path = \"../../embassy-usb\", default-features = false, features = [\"defmt\"] }\nembassy-futures = { version = \"0.1.2\", path = \"../../embassy-futures\" }\n\ndefmt = \"1.0.1\"\ndefmt-rtt = \"1.0.0\"\n\ncortex-m = { version = \"0.7.6\", features = [\"critical-section-single-core\"] }\ncortex-m-rt = \"0.7.0\"\npanic-probe = { version = \"1.0.0\", features = [\"print-defmt\"] }\n\nmicromath = \"2.0.0\"\nchrono = { version = \"0.4.38\", default-features = false }\n\n[profile.release]\ndebug = 2\n\n[package.metadata.embassy]\nbuild = [\n  { target = \"thumbv6m-none-eabi\", artifact-dir = \"out/examples/stm32u0\" }\n]\n"
  },
  {
    "path": "examples/stm32u0/build.rs",
    "content": "fn main() {\n    println!(\"cargo:rustc-link-arg-bins=--nmagic\");\n    println!(\"cargo:rustc-link-arg-bins=-Tlink.x\");\n    println!(\"cargo:rustc-link-arg-bins=-Tdefmt.x\");\n}\n"
  },
  {
    "path": "examples/stm32u0/src/bin/adc.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_stm32::Config;\nuse embassy_stm32::adc::{Adc, AdcConfig, Resolution, SampleTime};\nuse embassy_time::Duration;\nuse {defmt_rtt as _, panic_probe as _};\n\n#[cortex_m_rt::entry]\nfn main() -> ! {\n    info!(\"Hello World!\");\n\n    let mut config = Config::default();\n    {\n        use embassy_stm32::rcc::*;\n        config.rcc.mux.adcsel = mux::Adcsel::SYS;\n    }\n    let p = embassy_stm32::init(config);\n\n    let mut config = AdcConfig::default();\n    config.resolution = Some(Resolution::BITS8);\n    let mut adc = Adc::new_with_config(p.ADC1, config);\n    let mut channel = p.PC0;\n\n    loop {\n        let v = adc.blocking_read(&mut channel, SampleTime::CYCLES12_5);\n        info!(\"--> {}\", v);\n        embassy_time::block_for(Duration::from_millis(200));\n    }\n}\n"
  },
  {
    "path": "examples/stm32u0/src/bin/blinky.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_stm32::gpio::{Level, Output, Speed};\nuse embassy_time::Timer;\nuse {defmt_rtt as _, panic_probe as _};\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let p = embassy_stm32::init(Default::default());\n    info!(\"Hello World!\");\n\n    let mut led = Output::new(p.PA5, Level::High, Speed::Low);\n\n    loop {\n        info!(\"high\");\n        led.set_high();\n        Timer::after_millis(300).await;\n\n        info!(\"low\");\n        led.set_low();\n        Timer::after_millis(300).await;\n    }\n}\n"
  },
  {
    "path": "examples/stm32u0/src/bin/button.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse cortex_m_rt::entry;\nuse defmt::*;\nuse embassy_stm32::gpio::{Input, Pull};\nuse {defmt_rtt as _, panic_probe as _};\n\n#[entry]\nfn main() -> ! {\n    info!(\"Hello World!\");\n\n    let p = embassy_stm32::init(Default::default());\n\n    let button = Input::new(p.PC13, Pull::Up);\n\n    loop {\n        if button.is_high() {\n            info!(\"high\");\n        } else {\n            info!(\"low\");\n        }\n    }\n}\n"
  },
  {
    "path": "examples/stm32u0/src/bin/button_exti.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_stm32::exti::{self, ExtiInput};\nuse embassy_stm32::gpio::Pull;\nuse embassy_stm32::{bind_interrupts, interrupt};\nuse {defmt_rtt as _, panic_probe as _};\n\nbind_interrupts!(\n    pub struct Irqs{\n        EXTI4_15 => exti::InterruptHandler<interrupt::typelevel::EXTI4_15>;\n});\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let p = embassy_stm32::init(Default::default());\n    info!(\"Hello World!\");\n\n    let mut button = ExtiInput::new(p.PC13, p.EXTI13, Pull::Up, Irqs);\n\n    info!(\"Press the USER button...\");\n\n    loop {\n        button.wait_for_falling_edge().await;\n        info!(\"Pressed!\");\n        button.wait_for_rising_edge().await;\n        info!(\"Released!\");\n    }\n}\n"
  },
  {
    "path": "examples/stm32u0/src/bin/crc.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_stm32::crc::{Config, Crc, InputReverseConfig, PolySize};\nuse {defmt_rtt as _, panic_probe as _};\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let p = embassy_stm32::init(Default::default());\n    info!(\"Hello World!\");\n\n    // Setup for: https://crccalc.com/?crc=Life, it never dieWomen are my favorite guy&method=crc32&datatype=ascii&outtype=0\n    let mut crc = Crc::new(\n        p.CRC,\n        unwrap!(Config::new(\n            InputReverseConfig::Byte,\n            true,\n            PolySize::Width32,\n            0xFFFFFFFF,\n            0x04C11DB7\n        )),\n    );\n\n    crc.feed_bytes(b\"Life, it never die\\nWomen are my favorite guy\");\n    let output = crc.read() ^ 0xFFFFFFFF;\n\n    defmt::assert_eq!(output, 0x33F0E26B);\n\n    cortex_m::asm::bkpt();\n}\n"
  },
  {
    "path": "examples/stm32u0/src/bin/dac.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_stm32::dac::{DacChannel, Value};\nuse {defmt_rtt as _, panic_probe as _};\n\n#[cortex_m_rt::entry]\nfn main() -> ! {\n    let p = embassy_stm32::init(Default::default());\n    info!(\"Hello World!\");\n\n    let mut dac = DacChannel::new_blocking(p.DAC1, p.PA4);\n\n    loop {\n        for v in 0..=255 {\n            dac.set(Value::Bit8(to_sine_wave(v)));\n        }\n    }\n}\n\nuse micromath::F32Ext;\n\nfn to_sine_wave(v: u8) -> u8 {\n    if v >= 128 {\n        // top half\n        let r = 3.14 * ((v - 128) as f32 / 128.0);\n        (r.sin() * 128.0 + 127.0) as u8\n    } else {\n        // bottom half\n        let r = 3.14 + 3.14 * (v as f32 / 128.0);\n        (r.sin() * 128.0 + 127.0) as u8\n    }\n}\n"
  },
  {
    "path": "examples/stm32u0/src/bin/flash.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_stm32::flash::Flash;\nuse {defmt_rtt as _, panic_probe as _};\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let p = embassy_stm32::init(Default::default());\n    info!(\"Hello World!\");\n\n    let addr: u32 = 0x40000 - 2 * 1024;\n\n    let mut f = Flash::new_blocking(p.FLASH).into_blocking_regions().bank1_region;\n\n    info!(\"Reading...\");\n    let mut buf = [0u8; 32];\n    unwrap!(f.blocking_read(addr, &mut buf));\n    info!(\"Read: {=[u8]:x}\", buf);\n    info!(\"Erasing...\");\n    unwrap!(f.blocking_erase(addr, addr + 2 * 1024));\n\n    info!(\"Reading...\");\n    let mut buf = [0u8; 32];\n    unwrap!(f.blocking_read(addr, &mut buf));\n    info!(\"Read after erase: {=[u8]:x}\", buf);\n\n    info!(\"Writing...\");\n    unwrap!(f.blocking_write(\n        addr,\n        &[\n            1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29,\n            30, 31, 32\n        ]\n    ));\n\n    info!(\"Reading...\");\n    let mut buf = [0u8; 32];\n    unwrap!(f.blocking_read(addr, &mut buf));\n    info!(\"Read: {=[u8]:x}\", buf);\n}\n"
  },
  {
    "path": "examples/stm32u0/src/bin/i2c.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_stm32::i2c::I2c;\nuse {defmt_rtt as _, panic_probe as _};\n\nconst ADDRESS: u8 = 0x5F;\nconst WHOAMI: u8 = 0x0F;\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let p = embassy_stm32::init(Default::default());\n    let mut i2c = I2c::new_blocking(p.I2C2, p.PB10, p.PB11, Default::default());\n\n    let mut data = [0u8; 1];\n    unwrap!(i2c.blocking_write_read(ADDRESS, &[WHOAMI], &mut data));\n    info!(\"Whoami: {}\", data[0]);\n}\n"
  },
  {
    "path": "examples/stm32u0/src/bin/lcd.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_stm32::lcd::{Bias, BlinkFreq, BlinkSelector, Config, Duty, Lcd, LcdPin};\nuse embassy_stm32::peripherals::LCD;\nuse embassy_stm32::time::Hertz;\nuse embassy_time::Duration;\nuse {defmt_rtt as _, panic_probe as _};\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let mut config = embassy_stm32::Config::default();\n    // The RTC clock = the LCD clock and must be running\n    {\n        use embassy_stm32::rcc::*;\n        config.rcc.sys = Sysclk::PLL1_R;\n        config.rcc.hsi = true;\n        config.rcc.pll = Some(Pll {\n            source: PllSource::HSI, // 16 MHz\n            prediv: PllPreDiv::DIV1,\n            mul: PllMul::MUL7, // 16 * 7 = 112 MHz\n            divp: None,\n            divq: None,\n            divr: Some(PllRDiv::DIV2), // 112 / 2 = 56 MHz\n        });\n        config.rcc.ls = LsConfig::default_lsi();\n    }\n\n    let p = embassy_stm32::init(config);\n    info!(\"Hello World!\");\n\n    let mut config = Config::default();\n    config.bias = Bias::Third;\n    config.duty = Duty::Quarter;\n    config.target_fps = Hertz(100);\n\n    let mut lcd = Lcd::new(\n        p.LCD,\n        config,\n        p.PC3,\n        [\n            LcdPin::new_com(p.PA8),\n            LcdPin::new_com(p.PA9),\n            LcdPin::new_com(p.PA10),\n            LcdPin::new_seg(p.PB1),\n            LcdPin::new_com(p.PB9),\n            LcdPin::new_seg(p.PB11),\n            LcdPin::new_seg(p.PB14),\n            LcdPin::new_seg(p.PB15),\n            LcdPin::new_seg(p.PC4),\n            LcdPin::new_seg(p.PC5),\n            LcdPin::new_seg(p.PC6),\n            LcdPin::new_seg(p.PC8),\n            LcdPin::new_seg(p.PC9),\n            LcdPin::new_seg(p.PC10),\n            LcdPin::new_seg(p.PC11),\n            LcdPin::new_seg(p.PD8),\n            LcdPin::new_seg(p.PD9),\n            LcdPin::new_seg(p.PD12),\n            LcdPin::new_seg(p.PD13),\n            LcdPin::new_seg(p.PD0),\n            LcdPin::new_seg(p.PD1),\n            LcdPin::new_seg(p.PD3),\n            LcdPin::new_seg(p.PD4),\n            LcdPin::new_seg(p.PD5),\n            LcdPin::new_seg(p.PD6),\n            LcdPin::new_seg(p.PE7),\n            LcdPin::new_seg(p.PE8),\n            LcdPin::new_seg(p.PE9),\n        ],\n    );\n\n    lcd.set_blink(BlinkSelector::All, BlinkFreq::Hz4);\n    {\n        let mut buffer = DisplayBuffer::new();\n        for i in 0..4 {\n            buffer.write_colon(i);\n            buffer.write(&mut lcd);\n            embassy_time::Timer::after_millis(200).await;\n            buffer.write_dot(i);\n            buffer.write(&mut lcd);\n            embassy_time::Timer::after_millis(200).await;\n        }\n        for i in 0..4 {\n            buffer.write_bar(i);\n            buffer.write(&mut lcd);\n            embassy_time::Timer::after_millis(200).await;\n        }\n    }\n\n    embassy_time::Timer::after_millis(1000).await;\n\n    lcd.set_blink(BlinkSelector::None, BlinkFreq::Hz4);\n\n    const MESSAGE: &str = \"Hello embassy people. Hope you like this LCD demo :}      \";\n    loop {\n        print_message(MESSAGE, &mut lcd, Duration::from_millis(250)).await;\n        print_message(characters::ALL_CHARS, &mut lcd, Duration::from_millis(500)).await;\n    }\n}\n\nasync fn print_message(message: &str, lcd: &mut Lcd<'_, LCD>, delay: Duration) {\n    let mut display_buffer = DisplayBuffer::new();\n\n    let mut char_buffer = [' '; 6];\n    for char in message.chars() {\n        char_buffer.copy_within(1.., 0);\n        char_buffer[5] = char;\n\n        display_buffer.clear();\n        for (i, char) in char_buffer.iter().enumerate() {\n            display_buffer.write_char(i, *char);\n        }\n        display_buffer.write(lcd);\n\n        embassy_time::Timer::after(delay).await;\n    }\n}\n\n/// Display layout for the U0-DK\nmod display_layout {\n    // Character layout. There are 6 characters, left-to-right\n    //         T\n    //     ─────────\n    //    │    N    │\n    //    │ │  │  │ │\n    // TL │ └┐ │ ┌┘ │ TR\n    //    │NW│ │ │NE│\n    //    │    │    │\n    //     W─── ───E\n    //    │    │    │\n    //    │SW│ │ │SE│\n    // BL │ ┌┘ │ └┐ │ BR\n    //    │ │  │  │ │\n    //    │    S    │\n    //     ─────────\n    //         B\n\n    pub const CHAR_N_COM: u8 = 3;\n    pub const CHAR_N_SEG: [u8; 6] = [39, 37, 35, 48, 26, 33];\n    pub const CHAR_NW_COM: u8 = 3;\n    pub const CHAR_NW_SEG: [u8; 6] = [49, 38, 36, 34, 27, 24];\n    pub const CHAR_W_COM: u8 = 0;\n    pub const CHAR_W_SEG: [u8; 6] = CHAR_NW_SEG;\n    pub const CHAR_SW_COM: u8 = 2;\n    pub const CHAR_SW_SEG: [u8; 6] = CHAR_NW_SEG;\n    pub const CHAR_S_COM: u8 = 2;\n    pub const CHAR_S_SEG: [u8; 6] = [22, 6, 46, 11, 15, 29];\n    pub const CHAR_SE_COM: u8 = 3;\n    pub const CHAR_SE_SEG: [u8; 6] = CHAR_S_SEG;\n    pub const CHAR_E_COM: u8 = 0;\n    pub const CHAR_E_SEG: [u8; 6] = [23, 45, 47, 14, 28, 32];\n    pub const CHAR_NE_COM: u8 = 2;\n    pub const CHAR_NE_SEG: [u8; 6] = CHAR_N_SEG;\n    pub const CHAR_T_COM: u8 = 1;\n    pub const CHAR_T_SEG: [u8; 6] = CHAR_N_SEG;\n    pub const CHAR_TL_COM: u8 = 1;\n    pub const CHAR_TL_SEG: [u8; 6] = CHAR_NW_SEG;\n    pub const CHAR_BL_COM: u8 = 0;\n    pub const CHAR_BL_SEG: [u8; 6] = CHAR_S_SEG;\n    pub const CHAR_B_COM: u8 = 1;\n    pub const CHAR_B_SEG: [u8; 6] = CHAR_S_SEG;\n    pub const CHAR_BR_COM: u8 = 1;\n    pub const CHAR_BR_SEG: [u8; 6] = CHAR_E_SEG;\n    pub const CHAR_TR_COM: u8 = 0;\n    pub const CHAR_TR_SEG: [u8; 6] = CHAR_N_SEG;\n\n    pub const COLON_COM: u8 = 2;\n    pub const COLON_SEG: [u8; 4] = [23, 45, 47, 14];\n    pub const DOT_COM: u8 = 3;\n    pub const DOT_SEG: [u8; 4] = COLON_SEG;\n    /// COM + SEG, bar from top to bottom\n    pub const BAR: [(u8, u8); 4] = [(2, 28), (3, 28), (2, 32), (3, 32)];\n}\n\nmod characters {\n    use super::CharSegment::{self, *};\n\n    pub const CHAR_0: &[CharSegment] = &[T, TL, BL, B, BR, TR, NW, SE];\n    pub const CHAR_1: &[CharSegment] = &[NE, TR, BR];\n    pub const CHAR_2: &[CharSegment] = &[T, BL, B, TR, E, W];\n    pub const CHAR_3: &[CharSegment] = &[T, B, BR, TR, E];\n    pub const CHAR_4: &[CharSegment] = &[TL, BR, TR, E, W];\n    pub const CHAR_5: &[CharSegment] = &[T, TL, B, BR, E, W];\n    pub const CHAR_6: &[CharSegment] = &[T, TL, BL, B, BR, E, W];\n    pub const CHAR_7: &[CharSegment] = &[T, NE, S];\n    pub const CHAR_8: &[CharSegment] = &[T, TL, BL, B, BR, TR, E, W];\n    pub const CHAR_9: &[CharSegment] = &[T, TL, BR, TR, E, W];\n\n    pub const CHAR_COLON: &[CharSegment] = &[N, S];\n    pub const CHAR_SEMICOLON: &[CharSegment] = &[N, SW];\n    pub const CHAR_EQUALS: &[CharSegment] = &[E, W, B];\n    pub const CHAR_SLASH: &[CharSegment] = &[SW, NE];\n    pub const CHAR_BACKSLASH: &[CharSegment] = &[SE, NW];\n    pub const CHAR_PLUS: &[CharSegment] = &[N, E, S, W];\n    pub const CHAR_STAR: &[CharSegment] = &[NE, N, NW, SE, S, SW];\n    pub const CHAR_QUOTE: &[CharSegment] = &[N];\n    pub const CHAR_BACKTICK: &[CharSegment] = &[NW];\n    pub const CHAR_DASH: &[CharSegment] = &[W, E];\n    pub const CHAR_COMMA: &[CharSegment] = &[SW];\n    pub const CHAR_DOT: &[CharSegment] = &[S];\n    pub const CHAR_CURLYOPEN: &[CharSegment] = &[T, NW, W, SW, B];\n    pub const CHAR_CURLYCLOSE: &[CharSegment] = &[T, NE, E, SE, B];\n    pub const CHAR_AMPERSAND: &[CharSegment] = &[T, NE, NW, W, BL, B, SE];\n\n    pub const CHAR_A: &[CharSegment] = &[T, TL, TR, E, W, BL, BR];\n    pub const CHAR_B: &[CharSegment] = &[T, TR, BR, B, N, S, E];\n    pub const CHAR_C: &[CharSegment] = &[T, TL, BL, B];\n    pub const CHAR_D: &[CharSegment] = &[T, TR, BR, B, N, S];\n    pub const CHAR_E: &[CharSegment] = &[T, TL, BL, B, W];\n    pub const CHAR_F: &[CharSegment] = &[T, TL, BL, W];\n    pub const CHAR_G: &[CharSegment] = &[T, TL, BL, B, BR, E];\n    pub const CHAR_H: &[CharSegment] = &[TL, BL, E, W, TR, BR];\n    pub const CHAR_I: &[CharSegment] = &[T, N, S, B];\n    pub const CHAR_J: &[CharSegment] = &[TR, BR, B, BL];\n    pub const CHAR_K: &[CharSegment] = &[TL, BL, W, NE, SE];\n    pub const CHAR_L: &[CharSegment] = &[TL, BL, B];\n    pub const CHAR_M: &[CharSegment] = &[BL, TL, NW, NE, TR, BR];\n    pub const CHAR_N: &[CharSegment] = &[BL, TL, NW, SE, BR, TR];\n    pub const CHAR_O: &[CharSegment] = &[T, TL, BL, B, BR, TR];\n    pub const CHAR_P: &[CharSegment] = &[BL, TL, T, TR, E, W];\n    pub const CHAR_Q: &[CharSegment] = &[T, TL, BL, B, BR, TR, SE];\n    pub const CHAR_R: &[CharSegment] = &[BL, TL, T, TR, E, W, SE];\n    pub const CHAR_S: &[CharSegment] = &[T, NW, E, BR, B];\n    pub const CHAR_T: &[CharSegment] = &[T, N, S];\n    pub const CHAR_U: &[CharSegment] = &[TL, BL, B, BR, TR];\n    pub const CHAR_V: &[CharSegment] = &[TL, BL, SW, NE];\n    pub const CHAR_W: &[CharSegment] = &[TL, BL, SW, SE, BR, TR];\n    pub const CHAR_X: &[CharSegment] = &[NE, NW, SE, SW];\n    pub const CHAR_Y: &[CharSegment] = &[NE, NW, S];\n    pub const CHAR_Z: &[CharSegment] = &[T, NE, SW, B];\n\n    pub const CHAR_UNKNOWN: &[CharSegment] = &[N, NW, W, SW, S, SE, E, NE, T, TL, BL, B, BR, TR];\n\n    pub const ALL_CHARS: &str =\n        \"0 1 2 3 4 5 6 7 8 9 : ; = / \\\\ + * ' ` - , . { } & A B C D E F G H I J K L M N O P Q R S T U V W X Y Z � \";\n\n    pub fn get_char_segments(val: char) -> &'static [CharSegment] {\n        match val {\n            val if val.is_whitespace() => &[],\n\n            '0' => CHAR_0,\n            '1' => CHAR_1,\n            '2' => CHAR_2,\n            '3' => CHAR_3,\n            '4' => CHAR_4,\n            '5' => CHAR_5,\n            '6' => CHAR_6,\n            '7' => CHAR_7,\n            '8' => CHAR_8,\n            '9' => CHAR_9,\n\n            ':' => CHAR_COLON,\n            ';' => CHAR_SEMICOLON,\n            '=' => CHAR_EQUALS,\n            '/' => CHAR_SLASH,\n            '\\\\' => CHAR_BACKSLASH,\n            '+' => CHAR_PLUS,\n            '*' => CHAR_STAR,\n            '\\'' => CHAR_QUOTE,\n            '`' => CHAR_BACKTICK,\n            '-' => CHAR_DASH,\n            ',' => CHAR_COMMA,\n            '.' => CHAR_DOT,\n            '{' => CHAR_CURLYOPEN,\n            '}' => CHAR_CURLYCLOSE,\n            '&' => CHAR_AMPERSAND,\n\n            'A' | 'a' => CHAR_A,\n            'B' | 'b' => CHAR_B,\n            'C' | 'c' => CHAR_C,\n            'D' | 'd' => CHAR_D,\n            'E' | 'e' => CHAR_E,\n            'F' | 'f' => CHAR_F,\n            'G' | 'g' => CHAR_G,\n            'H' | 'h' => CHAR_H,\n            'I' | 'i' => CHAR_I,\n            'J' | 'j' => CHAR_J,\n            'K' | 'k' => CHAR_K,\n            'L' | 'l' => CHAR_L,\n            'M' | 'm' => CHAR_M,\n            'N' | 'n' => CHAR_N,\n            'O' | 'o' => CHAR_O,\n            'P' | 'p' => CHAR_P,\n            'Q' | 'q' => CHAR_Q,\n            'R' | 'r' => CHAR_R,\n            'S' | 's' => CHAR_S,\n            'T' | 't' => CHAR_T,\n            'U' | 'u' => CHAR_U,\n            'V' | 'v' => CHAR_V,\n            'W' | 'w' => CHAR_W,\n            'X' | 'x' => CHAR_X,\n            'Y' | 'y' => CHAR_Y,\n            'Z' | 'z' => CHAR_Z,\n\n            _ => CHAR_UNKNOWN,\n        }\n    }\n}\n\npub struct DisplayBuffer {\n    pixels: [u64; 4],\n}\n\nimpl DisplayBuffer {\n    pub const fn new() -> Self {\n        Self { pixels: [0; 4] }\n    }\n\n    pub fn clear(&mut self) {\n        *self = Self::new();\n    }\n\n    fn write_char_segment(&mut self, index: usize, value: CharSegment) {\n        defmt::assert!(index < 6);\n        let (com, segments) = value.get_com_seg();\n        self.pixels[com as usize] |= 1 << segments[index];\n    }\n\n    pub fn write_char(&mut self, index: usize, val: char) {\n        let segments = characters::get_char_segments(val);\n\n        for segment in segments {\n            self.write_char_segment(index, *segment);\n        }\n    }\n\n    pub fn write(&self, lcd: &mut Lcd<'_, LCD>) {\n        lcd.write_com_segments(0, self.pixels[0]);\n        lcd.write_com_segments(1, self.pixels[1]);\n        lcd.write_com_segments(2, self.pixels[2]);\n        lcd.write_com_segments(3, self.pixels[3]);\n        lcd.submit_frame();\n    }\n\n    pub fn write_colon(&mut self, index: usize) {\n        defmt::assert!(index < 4);\n        self.pixels[display_layout::COLON_COM as usize] |= 1 << display_layout::COLON_SEG[index];\n    }\n\n    pub fn write_dot(&mut self, index: usize) {\n        defmt::assert!(index < 4);\n        self.pixels[display_layout::DOT_COM as usize] |= 1 << display_layout::DOT_SEG[index];\n    }\n\n    pub fn write_bar(&mut self, index: usize) {\n        defmt::assert!(index < 4);\n        let (bar_com, bar_seg) = display_layout::BAR[index];\n        self.pixels[bar_com as usize] |= 1 << bar_seg;\n    }\n}\n\nimpl Default for DisplayBuffer {\n    fn default() -> Self {\n        Self::new()\n    }\n}\n\n#[derive(Debug, Clone, Copy)]\nenum CharSegment {\n    /// North\n    N,\n    /// North west\n    NW,\n    /// West\n    W,\n    /// South west\n    SW,\n    /// South\n    S,\n    /// South East\n    SE,\n    /// East\n    E,\n    /// North East\n    NE,\n    /// Top\n    T,\n    /// Top left\n    TL,\n    /// Bottom left\n    BL,\n    /// Bottom\n    B,\n    /// Bottom right\n    BR,\n    /// Top right\n    TR,\n}\n\nimpl CharSegment {\n    fn get_com_seg(&self) -> (u8, [u8; 6]) {\n        match self {\n            CharSegment::N => (display_layout::CHAR_N_COM, display_layout::CHAR_N_SEG),\n            CharSegment::NW => (display_layout::CHAR_NW_COM, display_layout::CHAR_NW_SEG),\n            CharSegment::W => (display_layout::CHAR_W_COM, display_layout::CHAR_W_SEG),\n            CharSegment::SW => (display_layout::CHAR_SW_COM, display_layout::CHAR_SW_SEG),\n            CharSegment::S => (display_layout::CHAR_S_COM, display_layout::CHAR_S_SEG),\n            CharSegment::SE => (display_layout::CHAR_SE_COM, display_layout::CHAR_SE_SEG),\n            CharSegment::E => (display_layout::CHAR_E_COM, display_layout::CHAR_E_SEG),\n            CharSegment::NE => (display_layout::CHAR_NE_COM, display_layout::CHAR_NE_SEG),\n            CharSegment::T => (display_layout::CHAR_T_COM, display_layout::CHAR_T_SEG),\n            CharSegment::TL => (display_layout::CHAR_TL_COM, display_layout::CHAR_TL_SEG),\n            CharSegment::BL => (display_layout::CHAR_BL_COM, display_layout::CHAR_BL_SEG),\n            CharSegment::B => (display_layout::CHAR_B_COM, display_layout::CHAR_B_SEG),\n            CharSegment::BR => (display_layout::CHAR_BR_COM, display_layout::CHAR_BR_SEG),\n            CharSegment::TR => (display_layout::CHAR_TR_COM, display_layout::CHAR_TR_SEG),\n        }\n    }\n}\n"
  },
  {
    "path": "examples/stm32u0/src/bin/rng.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_stm32::rcc::mux::Clk48sel;\nuse embassy_stm32::rng::Rng;\nuse embassy_stm32::{Config, bind_interrupts, peripherals, rng};\nuse {defmt_rtt as _, panic_probe as _};\n\nbind_interrupts!(struct Irqs {\n    RNG_CRYP => rng::InterruptHandler<peripherals::RNG>;\n});\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let mut config = Config::default();\n    {\n        use embassy_stm32::rcc::*;\n        config.rcc.hsi = true;\n        config.rcc.pll = Some(Pll {\n            source: PllSource::HSI, // 16 MHz\n            prediv: PllPreDiv::DIV1,\n            mul: PllMul::MUL7, // 16 * 7 = 112 MHz\n            divp: None,\n            divq: None,\n            divr: Some(PllRDiv::DIV2), // 112 / 2 = 56 MHz\n        });\n        config.rcc.sys = Sysclk::PLL1_R;\n        config.rcc.hsi48 = Some(Hsi48Config { sync_from_usb: false }); // needed for RNG\n        config.rcc.mux.clk48sel = Clk48sel::HSI48; // needed for RNG (or use MSI or PLLQ if you want)\n    }\n\n    let p = embassy_stm32::init(config);\n\n    info!(\"Hello World!\");\n\n    let mut rng = Rng::new(p.RNG, Irqs);\n\n    let mut buf = [0u8; 16];\n    unwrap!(rng.async_fill_bytes(&mut buf).await);\n    info!(\"random bytes: {:02x}\", buf);\n}\n"
  },
  {
    "path": "examples/stm32u0/src/bin/rtc.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse chrono::{NaiveDate, NaiveDateTime};\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_stm32::Config;\nuse embassy_stm32::rtc::{Rtc, RtcConfig};\nuse embassy_time::Timer;\nuse {defmt_rtt as _, panic_probe as _};\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let mut config = Config::default();\n    {\n        use embassy_stm32::rcc::*;\n        config.rcc.sys = Sysclk::PLL1_R;\n        config.rcc.hsi = true;\n        config.rcc.pll = Some(Pll {\n            source: PllSource::HSI, // 16 MHz\n            prediv: PllPreDiv::DIV1,\n            mul: PllMul::MUL7, // 16 * 7 = 112 MHz\n            divp: None,\n            divq: None,\n            divr: Some(PllRDiv::DIV2), // 112 / 2 = 56 MHz\n        });\n        config.rcc.ls = LsConfig::default();\n    }\n\n    let p = embassy_stm32::init(config);\n\n    info!(\"Hello World!\");\n\n    let now = NaiveDate::from_ymd_opt(2020, 5, 15)\n        .unwrap()\n        .and_hms_opt(10, 30, 15)\n        .unwrap();\n\n    let (mut rtc, time_provider) = Rtc::new(p.RTC, RtcConfig::default());\n    info!(\"Got RTC! {:?}\", now.and_utc().timestamp());\n\n    rtc.set_datetime(now.into()).expect(\"datetime not set\");\n\n    // In reality the delay would be much longer\n    Timer::after_millis(20000).await;\n\n    let then: NaiveDateTime = time_provider.now().unwrap().into();\n    info!(\"Got RTC! {:?}\", then.and_utc().timestamp());\n}\n"
  },
  {
    "path": "examples/stm32u0/src/bin/spi.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_stm32::gpio::{Level, Output, Speed};\nuse embassy_stm32::spi::{Config, Spi};\nuse embassy_stm32::time::Hertz;\nuse {defmt_rtt as _, panic_probe as _};\n\n#[cortex_m_rt::entry]\nfn main() -> ! {\n    info!(\"Hello World!\");\n\n    let p = embassy_stm32::init(Default::default());\n\n    let mut spi_config = Config::default();\n    spi_config.frequency = Hertz(1_000_000);\n\n    let mut spi = Spi::new_blocking(p.SPI3, p.PC10, p.PC12, p.PC11, spi_config);\n\n    let mut cs = Output::new(p.PC13, Level::High, Speed::VeryHigh);\n\n    loop {\n        let mut buf = [0x0Au8; 4];\n        cs.set_low();\n        unwrap!(spi.blocking_transfer_in_place(&mut buf));\n        cs.set_high();\n        info!(\"xfer {=[u8]:x}\", buf);\n    }\n}\n"
  },
  {
    "path": "examples/stm32u0/src/bin/usart.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_stm32::usart::{Config, Uart};\nuse {defmt_rtt as _, panic_probe as _};\n\n#[cortex_m_rt::entry]\nfn main() -> ! {\n    info!(\"Hello World!\");\n\n    let p = embassy_stm32::init(Default::default());\n\n    let config = Config::default();\n    let mut usart = Uart::new_blocking(p.USART2, p.PA3, p.PA2, config).unwrap();\n\n    unwrap!(usart.blocking_write(b\"Hello Embassy World!\\r\\n\"));\n    info!(\"wrote Hello, starting echo\");\n\n    let mut buf = [0u8; 1];\n    loop {\n        unwrap!(usart.blocking_read(&mut buf));\n        unwrap!(usart.blocking_write(&buf));\n    }\n}\n"
  },
  {
    "path": "examples/stm32u0/src/bin/usb_serial.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse defmt::{panic, *};\nuse defmt_rtt as _; // global logger\nuse embassy_executor::Spawner;\nuse embassy_futures::join::join;\nuse embassy_stm32::usb::{Driver, Instance};\nuse embassy_stm32::{Config, bind_interrupts, peripherals, usb};\nuse embassy_usb::Builder;\nuse embassy_usb::class::cdc_acm::{CdcAcmClass, State};\nuse embassy_usb::driver::EndpointError;\nuse panic_probe as _;\n\nbind_interrupts!(struct Irqs {\n    USB_DRD_FS => usb::InterruptHandler<peripherals::USB>;\n});\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let mut config = Config::default();\n    {\n        use embassy_stm32::rcc::*;\n        config.rcc.hsi = true;\n        config.rcc.pll = Some(Pll {\n            source: PllSource::HSI, // 16 MHz\n            prediv: PllPreDiv::DIV1,\n            mul: PllMul::MUL7,\n            divp: None,\n            divq: None,\n            divr: Some(PllRDiv::DIV2), // 56 MHz\n        });\n        config.rcc.sys = Sysclk::PLL1_R;\n        config.rcc.hsi48 = Some(Hsi48Config { sync_from_usb: true }); // needed for USB\n        config.rcc.mux.clk48sel = mux::Clk48sel::HSI48; // USB uses ICLK\n    }\n\n    let p = embassy_stm32::init(config);\n\n    info!(\"Hello World!\");\n\n    // Create the driver, from the HAL.\n    let driver = Driver::new(p.USB, Irqs, p.PA12, p.PA11);\n\n    // Create embassy-usb Config\n    let config = embassy_usb::Config::new(0xc0de, 0xcafe);\n    //config.max_packet_size_0 = 64;\n\n    // Create embassy-usb DeviceBuilder using the driver and config.\n    // It needs some buffers for building the descriptors.\n    let mut config_descriptor = [0; 256];\n    let mut bos_descriptor = [0; 256];\n    let mut control_buf = [0; 7];\n\n    let mut state = State::new();\n\n    let mut builder = Builder::new(\n        driver,\n        config,\n        &mut config_descriptor,\n        &mut bos_descriptor,\n        &mut [], // no msos descriptors\n        &mut control_buf,\n    );\n\n    // Create classes on the builder.\n    let mut class = CdcAcmClass::new(&mut builder, &mut state, 64);\n\n    // Build the builder.\n    let mut usb = builder.build();\n\n    // Run the USB device.\n    let usb_fut = usb.run();\n\n    // Do stuff with the class!\n    let echo_fut = async {\n        loop {\n            class.wait_connection().await;\n            info!(\"Connected\");\n            let _ = echo(&mut class).await;\n            info!(\"Disconnected\");\n        }\n    };\n\n    // Run everything concurrently.\n    // If we had made everything `'static` above instead, we could do this using separate tasks instead.\n    join(usb_fut, echo_fut).await;\n}\n\nstruct Disconnected {}\n\nimpl From<EndpointError> for Disconnected {\n    fn from(val: EndpointError) -> Self {\n        match val {\n            EndpointError::BufferOverflow => panic!(\"Buffer overflow\"),\n            EndpointError::Disabled => Disconnected {},\n        }\n    }\n}\n\nasync fn echo<'d, T: Instance + 'd>(class: &mut CdcAcmClass<'d, Driver<'d, T>>) -> Result<(), Disconnected> {\n    let mut buf = [0; 64];\n    loop {\n        let n = class.read_packet(&mut buf).await?;\n        let data = &buf[..n];\n        info!(\"data: {:x}\", data);\n        class.write_packet(data).await?;\n    }\n}\n"
  },
  {
    "path": "examples/stm32u0/src/bin/wdt.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_stm32::gpio::{Level, Output, Speed};\nuse embassy_stm32::wdg::IndependentWatchdog;\nuse embassy_time::Timer;\nuse {defmt_rtt as _, panic_probe as _};\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let p = embassy_stm32::init(Default::default());\n    info!(\"Hello World!\");\n\n    let mut led = Output::new(p.PA5, Level::High, Speed::Low);\n\n    let mut wdt = IndependentWatchdog::new(p.IWDG, 1_000_000);\n    wdt.unleash();\n\n    let mut i = 0;\n\n    loop {\n        info!(\"high\");\n        led.set_high();\n        Timer::after_millis(300).await;\n\n        info!(\"low\");\n        led.set_low();\n        Timer::after_millis(300).await;\n\n        // Pet watchdog for 5 iterations and then stop.\n        // MCU should restart in 1 second after the last pet.\n        if i < 5 {\n            info!(\"Petting watchdog\");\n            wdt.pet();\n        }\n\n        i += 1;\n    }\n}\n"
  },
  {
    "path": "examples/stm32u3/.cargo/config.toml",
    "content": "[target.'cfg(all(target_arch = \"arm\", target_os = \"none\"))']\n# replace STM32U5G9ZJTxQ with your chip as listed in `probe-rs chip list`\nrunner = \"probe-rs run --chip STM32U385RGTxQ\"\n\n[build]\ntarget = \"thumbv8m.main-none-eabihf\"\n\n[env]\nDEFMT_LOG = \"trace\"\n"
  },
  {
    "path": "examples/stm32u3/Cargo.toml",
    "content": "[package]\nedition = \"2024\"\nname = \"embassy-stm32u3-examples\"\nversion = \"0.1.0\"\nlicense = \"MIT OR Apache-2.0\"\npublish = false\n\n[dependencies]\nembassy-stm32 = { version = \"0.6.0\", path = \"../../embassy-stm32\", features = [\"defmt\", \"unstable-pac\", \"stm32u385rg\", \"time-driver-any\", \"memory-x\", \"exti\", \"chrono\"]  }\nembassy-sync = { version = \"0.8.0\", path = \"../../embassy-sync\", features = [\"defmt\"] }\nembassy-executor = { version = \"0.10.0\", path = \"../../embassy-executor\", features = [\"platform-cortex-m\", \"executor-thread\", \"defmt\"] }\nembassy-time = { version = \"0.5.1\", path = \"../../embassy-time\", features = [\"defmt\", \"defmt-timestamp-uptime\", \"tick-hz-32_768\"] }\nembassy-usb = { version = \"0.6.0\", path = \"../../embassy-usb\", features = [\"defmt\"] }\nembassy-futures = { version = \"0.1.2\", path = \"../../embassy-futures\" }\ncritical-section = \"1.2\"\n\ndefmt = \"1.0.1\"\ndefmt-rtt = \"1.0.0\"\n\ncortex-m = { version = \"0.7.6\", features = [\"inline-asm\", \"critical-section-single-core\"] }\ncortex-m-rt = \"0.7.0\"\nembedded-hal = \"0.2.6\"\npanic-probe = { version = \"1.0.0\", features = [\"print-defmt\"] }\nheapless = { version = \"0.9\", default-features = false }\n\nmicromath = \"2.0.0\"\nchrono = { version = \"^0.4\", default-features = false }\n\n[features]\n## Use secure registers when TrustZone is enabled\ntrustzone-secure = [\"embassy-stm32/trustzone-secure\"]\n\n[profile.release]\ndebug = 2\n\n[package.metadata.embassy]\nbuild = [\n  { target = \"thumbv8m.main-none-eabihf\", artifact-dir = \"out/examples/stm32u3\" }\n]\n"
  },
  {
    "path": "examples/stm32u3/build.rs",
    "content": "fn main() {\n    println!(\"cargo:rustc-link-arg-bins=--nmagic\");\n    println!(\"cargo:rustc-link-arg-bins=-Tlink.x\");\n    println!(\"cargo:rustc-link-arg-bins=-Tdefmt.x\");\n}\n"
  },
  {
    "path": "examples/stm32u3/src/bin/blinky.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse embassy_executor::Spawner;\nuse embassy_stm32::gpio::{Level, Output, Speed};\nuse embassy_time::Timer;\nuse {defmt_rtt as _, panic_probe as _};\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) -> ! {\n    let p = embassy_stm32::init(Default::default());\n    defmt::info!(\"Hello World!\");\n\n    // replace PC13 with the right pin for your board.\n    let mut led = Output::new(p.PA5, Level::High, Speed::Medium);\n\n    loop {\n        defmt::info!(\"on!\");\n        led.set_low();\n        Timer::after_millis(1000).await;\n\n        defmt::info!(\"off!\");\n        led.set_high();\n        Timer::after_millis(1000).await;\n    }\n}\n"
  },
  {
    "path": "examples/stm32u3/src/bin/button_exti.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_stm32::exti::{self, ExtiInput};\nuse embassy_stm32::gpio::Pull;\nuse embassy_stm32::{bind_interrupts, interrupt};\nuse {defmt_rtt as _, panic_probe as _};\n\nbind_interrupts!(\n    pub struct Irqs{\n        EXTI13 => exti::InterruptHandler<interrupt::typelevel::EXTI13>;\n});\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let p = embassy_stm32::init(Default::default());\n    info!(\"Hello World!\");\n\n    let mut button = ExtiInput::new(p.PC13, p.EXTI13, Pull::Down, Irqs);\n\n    info!(\"Press the USER button...\");\n\n    loop {\n        button.wait_for_rising_edge().await;\n        info!(\"Pressed!\");\n        button.wait_for_falling_edge().await;\n        info!(\"Released!\");\n    }\n}\n"
  },
  {
    "path": "examples/stm32u3/src/bin/mco.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse embassy_executor::Spawner;\n// use embassy_stm32::gpio::{Level, Output, Speed};\nuse embassy_stm32::rcc::{Mco, McoConfig, McoSource};\nuse embassy_time::Timer;\nuse {defmt_rtt as _, panic_probe as _};\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) -> ! {\n    let p = embassy_stm32::init(Default::default());\n    defmt::info!(\"Hello World!\");\n\n    // replace PC13 with the right pin for your board.\n    let _mco = Mco::new(p.MCO, p.PA8, McoSource::SYS, McoConfig::default());\n    loop {\n        defmt::info!(\"on!\");\n        Timer::after_millis(1000).await;\n\n        defmt::info!(\"off!\");\n        Timer::after_millis(1000).await;\n    }\n}\n"
  },
  {
    "path": "examples/stm32u3/src/bin/rtc.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse chrono::{NaiveDate, NaiveDateTime};\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_stm32::Config;\nuse embassy_stm32::rtc::{Rtc, RtcConfig};\nuse embassy_time::Timer;\nuse {defmt_rtt as _, panic_probe as _};\n\n#[embassy_executor::main()]\nasync fn main(_spawner: Spawner) {\n    let p = embassy_stm32::init(Config::default());\n\n    info!(\"Hello World!\");\n\n    let now = NaiveDate::from_ymd_opt(2020, 5, 15)\n        .unwrap()\n        .and_hms_opt(10, 30, 15)\n        .unwrap();\n\n    let (mut rtc, time_provider) = Rtc::new(p.RTC, RtcConfig::default());\n    info!(\"Got RTC! {:?}\", now.and_utc().timestamp());\n\n    rtc.set_datetime(now.into()).expect(\"datetime not set\");\n\n    // In reality the delay would be much longer\n    Timer::after_millis(20000).await;\n\n    let then: NaiveDateTime = time_provider.now().unwrap().into();\n    info!(\"Got RTC! {:?}\", then.and_utc().timestamp());\n}\n"
  },
  {
    "path": "examples/stm32u3/src/bin/usb_serial.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse defmt::{panic, *};\nuse defmt_rtt as _; // global logger\nuse embassy_executor::Spawner;\nuse embassy_futures::join::join;\nuse embassy_stm32::usb::{Driver, Instance};\nuse embassy_stm32::{Config, bind_interrupts, peripherals, usb};\nuse embassy_usb::Builder;\nuse embassy_usb::class::cdc_acm::{CdcAcmClass, State};\nuse embassy_usb::driver::EndpointError;\nuse panic_probe as _;\n\nbind_interrupts!(struct Irqs {\n    USB_FS => usb::InterruptHandler<peripherals::USB>;\n});\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    info!(\"Hello World!\");\n\n    let mut config = Config::default();\n    {\n        use embassy_stm32::rcc::*;\n        config.rcc.hsi = true;\n        config.rcc.sys = Sysclk::MSIS;\n        config.rcc.voltage_range = VoltageScale::RANGE1;\n        config.rcc.hsi48 = Some(Hsi48Config { sync_from_usb: true }); // needed for USB\n        config.rcc.mux.iclksel = mux::Iclksel::HSI48; // USB uses ICLK\n    }\n\n    let p = embassy_stm32::init(config);\n\n    // Create the driver, from the HAL.\n    // let mut ep_out_buffer = [0u8; 256];\n    // Do not enable vbus_detection. This is a safe default that works in all boards.\n    // However, if your USB device is self-powered (can stay powered on if USB is unplugged), you need\n    // to enable vbus_detection to comply with the USB spec. If you enable it, the board\n    // has to support it or USB won't work at all. See docs on `vbus_detection` for details.\n    // config.vbus_detection = false;\n    let driver = Driver::new(p.USB, Irqs, p.PA12, p.PA11);\n\n    // Create embassy-usb Config\n    let mut config = embassy_usb::Config::new(0xc0de, 0xcafe);\n    config.manufacturer = Some(\"Embassy\");\n    config.product = Some(\"USB-serial example\");\n    config.serial_number = Some(\"12345678\");\n\n    // Create embassy-usb DeviceBuilder using the driver and config.\n    // It needs some buffers for building the descriptors.\n    let mut config_descriptor = [0; 256];\n    let mut bos_descriptor = [0; 256];\n    let mut control_buf = [0; 64];\n\n    let mut state = State::new();\n\n    let mut builder = Builder::new(\n        driver,\n        config,\n        &mut config_descriptor,\n        &mut bos_descriptor,\n        &mut [], // no msos descriptors\n        &mut control_buf,\n    );\n\n    // Create classes on the builder.\n    let mut class = CdcAcmClass::new(&mut builder, &mut state, 64);\n\n    // Build the builder.\n    let mut usb = builder.build();\n\n    // Run the USB device.\n    let usb_fut = usb.run();\n\n    // Do stuff with the class!\n    let echo_fut = async {\n        loop {\n            class.wait_connection().await;\n            info!(\"Connected\");\n            let _ = echo(&mut class).await;\n            info!(\"Disconnected\");\n        }\n    };\n\n    // Run everything concurrently.\n    // If we had made everything `'static` above instead, we could do this using separate tasks instead.\n    join(usb_fut, echo_fut).await;\n}\n\nstruct Disconnected {}\n\nimpl From<EndpointError> for Disconnected {\n    fn from(val: EndpointError) -> Self {\n        match val {\n            EndpointError::BufferOverflow => panic!(\"Buffer overflow\"),\n            EndpointError::Disabled => Disconnected {},\n        }\n    }\n}\n\nasync fn echo<'d, T: Instance + 'd>(class: &mut CdcAcmClass<'d, Driver<'d, T>>) -> Result<(), Disconnected> {\n    let mut buf = [0; 64];\n    loop {\n        let n = class.read_packet(&mut buf).await?;\n        let data = &buf[..n];\n        info!(\"data: {:x}\", data);\n        class.write_packet(data).await?;\n    }\n}\n"
  },
  {
    "path": "examples/stm32u5/.cargo/config.toml",
    "content": "[target.'cfg(all(target_arch = \"arm\", target_os = \"none\"))']\n# replace STM32U5G9ZJTxQ with your chip as listed in `probe-rs chip list`\nrunner = \"probe-rs run --chip STM32U5G9ZJTxQ\"\n\n[build]\ntarget = \"thumbv8m.main-none-eabihf\"\n\n[env]\nDEFMT_LOG = \"trace\"\n"
  },
  {
    "path": "examples/stm32u5/Cargo.toml",
    "content": "[package]\nedition = \"2024\"\nname = \"embassy-stm32u5-examples\"\nversion = \"0.1.0\"\nlicense = \"MIT OR Apache-2.0\"\npublish = false\n\n[dependencies]\n# Change stm32u5g9zj to your chip name, if necessary.\nembassy-stm32 = { version = \"0.6.0\", path = \"../../embassy-stm32\", features = [\"defmt\", \"unstable-pac\", \"stm32u5g9zj\", \"time-driver-any\", \"memory-x\" ]  }\nembassy-sync = { version = \"0.8.0\", path = \"../../embassy-sync\", features = [\"defmt\"] }\nembassy-executor = { version = \"0.10.0\", path = \"../../embassy-executor\", features = [\"platform-cortex-m\", \"executor-thread\", \"defmt\"] }\nembassy-time = { version = \"0.5.1\", path = \"../../embassy-time\", features = [\"defmt\", \"defmt-timestamp-uptime\", \"tick-hz-32_768\"] }\nembassy-usb = { version = \"0.6.0\", path = \"../../embassy-usb\", features = [\"defmt\"] }\nembassy-futures = { version = \"0.1.2\", path = \"../../embassy-futures\" }\n\ndefmt = \"1.0.1\"\ndefmt-rtt = \"1.0.0\"\n\ncortex-m = { version = \"0.7.6\", features = [\"inline-asm\", \"critical-section-single-core\"] }\ncortex-m-rt = \"0.7.0\"\nembedded-hal = \"0.2.6\"\npanic-probe = { version = \"1.0.0\", features = [\"print-defmt\"] }\nheapless = { version = \"0.9\", default-features = false }\nembedded-graphics = { version = \"0.8.1\" }\ntinybmp = { version = \"0.6.0\" }\n\nmicromath = \"2.0.0\"\n\n[features]\n## Use secure registers when TrustZone is enabled\ntrustzone-secure = [\"embassy-stm32/trustzone-secure\"]\n\n[profile.release]\ndebug = 2\n\n[package.metadata.embassy]\nbuild = [\n  { target = \"thumbv8m.main-none-eabihf\", artifact-dir = \"out/examples/stm32u5\" }\n]\n"
  },
  {
    "path": "examples/stm32u5/build.rs",
    "content": "fn main() {\n    println!(\"cargo:rustc-link-arg-bins=--nmagic\");\n    println!(\"cargo:rustc-link-arg-bins=-Tlink.x\");\n    println!(\"cargo:rustc-link-arg-bins=-Tdefmt.x\");\n}\n"
  },
  {
    "path": "examples/stm32u5/src/bin/adc.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_stm32::adc::{self, Adc, AdcChannel, AdcConfig, SampleTime, adc4};\nuse embassy_stm32::{bind_interrupts, dma, peripherals};\nuse {defmt_rtt as _, panic_probe as _};\n\nbind_interrupts!(struct Irqs {\n    GPDMA1_CHANNEL0 => dma::InterruptHandler<peripherals::GPDMA1_CH0>;\n    GPDMA1_CHANNEL1 => dma::InterruptHandler<peripherals::GPDMA1_CH1>;\n});\n\n#[embassy_executor::main]\nasync fn main(_spawner: embassy_executor::Spawner) {\n    let config = embassy_stm32::Config::default();\n\n    let mut p = embassy_stm32::init(config);\n\n    // **** ADC1 init ****\n    let mut config = AdcConfig::default();\n    config.averaging = Some(adc::Averaging::Samples1024);\n    config.resolution = Some(adc::Resolution::BITS14);\n    let mut adc1 = Adc::new_with_config(p.ADC1, config);\n    let mut adc1_pin1 = p.PA3; // A0 on nucleo u5a5\n    let mut adc1_pin2 = p.PA2; // A1\n    let max1 = adc::resolution_to_max_count(adc::Resolution::BITS14);\n\n    // **** ADC2 init ****\n    let mut config = AdcConfig::default();\n    config.averaging = Some(adc::Averaging::Samples1024);\n    config.resolution = Some(adc::Resolution::BITS14);\n    let mut adc2 = Adc::new_with_config(p.ADC2, config);\n    let mut adc2_pin1 = p.PC3; // A2\n    let mut adc2_pin2 = p.PB0; // A3\n    let max2 = adc::resolution_to_max_count(adc::Resolution::BITS14);\n\n    // **** ADC4 init ****\n    let mut adc4 = Adc::new_adc4(p.ADC4);\n    let mut adc4_pin1 = p.PC1.degrade_adc(); // A4\n    let mut adc4_pin2 = p.PC0; // A5\n    adc4.set_resolution_adc4(adc4::Resolution::BITS12);\n    adc4.set_averaging_adc4(adc4::Averaging::Samples256);\n    let max4 = adc4::resolution_to_max_count(adc4::Resolution::BITS12);\n\n    // **** ADC1 blocking read ****\n    let raw: u16 = adc1.blocking_read(&mut adc1_pin1, SampleTime::CYCLES160_5);\n    let volt: f32 = 3.3 * raw as f32 / max1 as f32;\n    info!(\"Read adc1 pin 1 {}\", volt);\n\n    let raw: u16 = adc1.blocking_read(&mut adc1_pin2, SampleTime::CYCLES160_5);\n    let volt: f32 = 3.3 * raw as f32 / max1 as f32;\n    info!(\"Read adc1 pin 2 {}\", volt);\n\n    // **** ADC2 blocking read ****\n    let raw: u16 = adc2.blocking_read(&mut adc2_pin1, SampleTime::CYCLES160_5);\n    let volt: f32 = 3.3 * raw as f32 / max2 as f32;\n    info!(\"Read adc2 pin 1 {}\", volt);\n\n    let raw: u16 = adc2.blocking_read(&mut adc2_pin2, SampleTime::CYCLES160_5);\n    let volt: f32 = 3.3 * raw as f32 / max2 as f32;\n    info!(\"Read adc2 pin 2 {}\", volt);\n\n    // **** ADC4 blocking read ****\n    let raw: u16 = adc4.blocking_read(&mut adc4_pin1, adc4::SampleTime::CYCLES1_5);\n    let volt: f32 = 3.3 * raw as f32 / max4 as f32;\n    info!(\"Read adc4 pin 1 {}\", volt);\n\n    let raw: u16 = adc4.blocking_read(&mut adc4_pin2, adc4::SampleTime::CYCLES1_5);\n    let volt: f32 = 3.3 * raw as f32 / max4 as f32;\n    info!(\"Read adc4 pin 2 {}\", volt);\n\n    // **** ADC1 async read ****\n    let mut degraded11 = adc1_pin1.degrade_adc();\n    let mut degraded12 = adc1_pin2.degrade_adc();\n    let mut measurements = [0u16; 2];\n\n    adc1.read(\n        p.GPDMA1_CH0.reborrow(),\n        Irqs,\n        [\n            (&mut degraded11, adc::SampleTime::CYCLES160_5),\n            (&mut degraded12, adc::SampleTime::CYCLES160_5),\n        ]\n        .into_iter(),\n        &mut measurements,\n    )\n    .await;\n    let volt1: f32 = 3.3 * measurements[0] as f32 / max1 as f32;\n    let volt2: f32 = 3.3 * measurements[1] as f32 / max1 as f32;\n\n    info!(\"Async read 1 pin 1 {}\", volt1);\n    info!(\"Async read 1 pin 2 {}\", volt2);\n\n    // **** ADC2 does not support async read ****\n\n    // **** ADC4 async read ****\n    let mut degraded41 = adc4_pin1.degrade_adc();\n    let mut degraded42 = adc4_pin2.degrade_adc();\n    let mut measurements = [0u16; 2];\n\n    // The channels must be in ascending order and can't repeat for ADC4\n    adc4.read(\n        p.GPDMA1_CH1.reborrow(),\n        Irqs,\n        [\n            (&mut degraded42, adc4::SampleTime::CYCLES1_5),\n            (&mut degraded41, adc4::SampleTime::CYCLES1_5),\n        ]\n        .into_iter(),\n        &mut measurements,\n    )\n    .await;\n    let volt2: f32 = 3.3 * measurements[0] as f32 / max4 as f32;\n    let volt1: f32 = 3.3 * measurements[1] as f32 / max4 as f32;\n    info!(\"Async read 4 pin 1 {}\", volt1);\n    info!(\"Async read 4 pin 2 {}\", volt2);\n}\n"
  },
  {
    "path": "examples/stm32u5/src/bin/blinky.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_stm32::gpio::{Level, Output, Speed};\nuse embassy_time::Timer;\nuse {defmt_rtt as _, panic_probe as _};\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) -> ! {\n    let p = embassy_stm32::init(Default::default());\n    info!(\"Hello World!\");\n\n    // replace PC13 with the right pin for your board.\n    let mut led = Output::new(p.PC13, Level::Low, Speed::Medium);\n\n    loop {\n        defmt::info!(\"on!\");\n        led.set_low();\n        Timer::after_millis(200).await;\n\n        defmt::info!(\"off!\");\n        led.set_high();\n        Timer::after_millis(200).await;\n    }\n}\n"
  },
  {
    "path": "examples/stm32u5/src/bin/boot.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse {defmt_rtt as _, embassy_stm32 as _, panic_probe as _};\n\n#[cortex_m_rt::entry]\nfn main() -> ! {\n    info!(\"Hello World!\");\n\n    loop {\n        //defmt::info!(\"loop!\");\n    }\n}\n"
  },
  {
    "path": "examples/stm32u5/src/bin/flash.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse defmt::{info, unwrap};\nuse embassy_executor::Spawner;\nuse embassy_stm32::flash::Flash;\nuse embassy_time::Timer;\nuse {defmt_rtt as _, panic_probe as _};\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let p = embassy_stm32::init(Default::default());\n    info!(\"Hello Flash!\");\n\n    const ADDR: u32 = 0x8_0000; // This is the offset into the third region, the absolute address is 4x32K + 128K + 0x8_0000.\n\n    // wait a bit before accessing the flash\n    Timer::after_millis(300).await;\n\n    let mut f = Flash::new_blocking(p.FLASH).into_blocking_regions().bank1_region;\n\n    info!(\"Reading...\");\n    let mut buf = [0u8; 32];\n    unwrap!(f.blocking_read(ADDR, &mut buf));\n    info!(\"Read: {=[u8]:x}\", buf);\n\n    info!(\"Erasing...\");\n    unwrap!(f.blocking_erase(ADDR, ADDR + 256 * 1024));\n\n    info!(\"Reading...\");\n    let mut buf = [0u8; 32];\n    unwrap!(f.blocking_read(ADDR, &mut buf));\n    info!(\"Read after erase: {=[u8]:x}\", buf);\n\n    info!(\"Writing...\");\n    unwrap!(f.blocking_write(\n        ADDR,\n        &[\n            1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29,\n            30, 31, 32\n        ]\n    ));\n\n    info!(\"Reading...\");\n    let mut buf = [0u8; 32];\n    unwrap!(f.blocking_read(ADDR, &mut buf));\n    info!(\"Read: {=[u8]:x}\", buf);\n    assert_eq!(\n        &buf[..],\n        &[\n            1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29,\n            30, 31, 32\n        ]\n    );\n}\n"
  },
  {
    "path": "examples/stm32u5/src/bin/hspi_memory_mapped.rs",
    "content": "#![no_main]\n#![no_std]\n\n// Tested on an STM32U5G9J-DK2 demo board using the on-board MX66LM1G45G flash memory\n// The flash is connected to the HSPI1 port as an OCTA-DTR device\n//\n// Use embassy-stm32 feature \"stm32u5g9zj\" and probe-rs chip \"STM32U5G9ZJTxQ\"\n\nuse defmt::info;\nuse embassy_executor::Spawner;\nuse embassy_stm32::hspi::{\n    AddressSize, ChipSelectHighTime, DummyCycles, FIFOThresholdLevel, Hspi, HspiWidth, Instance, MemorySize,\n    MemoryType, TransferConfig, WrapSize,\n};\nuse embassy_stm32::mode::Async;\nuse embassy_stm32::time::Hertz;\nuse embassy_stm32::{bind_interrupts, dma, peripherals, rcc};\nuse {defmt_rtt as _, panic_probe as _};\n\nbind_interrupts!(struct Irqs {\n    GPDMA1_CHANNEL7 => dma::InterruptHandler<peripherals::GPDMA1_CH7>;\n});\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    info!(\"Start hspi_memory_mapped\");\n\n    // RCC config\n    let mut config = embassy_stm32::Config::default();\n    config.rcc.hse = Some(rcc::Hse {\n        freq: Hertz(16_000_000),\n        mode: rcc::HseMode::Oscillator,\n    });\n    config.rcc.pll1 = Some(rcc::Pll {\n        source: rcc::PllSource::HSE,\n        prediv: rcc::PllPreDiv::DIV1,\n        mul: rcc::PllMul::MUL10,\n        divp: None,\n        divq: None,\n        divr: Some(rcc::PllDiv::DIV1),\n    });\n    config.rcc.sys = rcc::Sysclk::PLL1_R; // 160 Mhz\n    config.rcc.pll2 = Some(rcc::Pll {\n        source: rcc::PllSource::HSE,\n        prediv: rcc::PllPreDiv::DIV4,\n        mul: rcc::PllMul::MUL66,\n        divp: None,\n        divq: Some(rcc::PllDiv::DIV2),\n        divr: None,\n    });\n    config.rcc.mux.hspi1sel = rcc::mux::Hspisel::PLL2_Q; // 132 MHz\n\n    // Initialize peripherals\n    let p = embassy_stm32::init(config);\n\n    let flash_config = embassy_stm32::hspi::Config {\n        fifo_threshold: FIFOThresholdLevel::_4Bytes,\n        memory_type: MemoryType::Macronix,\n        device_size: MemorySize::_1GiB,\n        chip_select_high_time: ChipSelectHighTime::_2Cycle,\n        free_running_clock: false,\n        clock_mode: false,\n        wrap_size: WrapSize::None,\n        clock_prescaler: 0,\n        sample_shifting: false,\n        delay_hold_quarter_cycle: false,\n        chip_select_boundary: 0,\n        delay_block_bypass: false,\n        max_transfer: 0,\n        refresh: 0,\n    };\n\n    let use_dma = true;\n\n    info!(\"Testing flash in OCTA DTR mode and memory mapped mode\");\n\n    let hspi = Hspi::new_octospi(\n        p.HSPI1,\n        p.PI3,\n        p.PH10,\n        p.PH11,\n        p.PH12,\n        p.PH13,\n        p.PH14,\n        p.PH15,\n        p.PI0,\n        p.PI1,\n        p.PH9,\n        p.PI2,\n        p.GPDMA1_CH7,\n        Irqs,\n        flash_config,\n    );\n\n    let mut flash = OctaDtrFlashMemory::new(hspi).await;\n\n    let flash_id = flash.read_id();\n    info!(\"FLASH ID: {=[u8]:x}\", flash_id);\n\n    let mut rd_buf = [0u8; 16];\n    flash.read_memory(0, &mut rd_buf, use_dma).await;\n    info!(\"READ BUF: {=[u8]:#X}\", rd_buf);\n\n    flash.erase_sector(0).await;\n    flash.read_memory(0, &mut rd_buf, use_dma).await;\n    info!(\"READ BUF: {=[u8]:#X}\", rd_buf);\n    assert_eq!(rd_buf[0], 0xFF);\n    assert_eq!(rd_buf[15], 0xFF);\n\n    let mut wr_buf = [0u8; 16];\n    for i in 0..wr_buf.len() {\n        wr_buf[i] = i as u8;\n    }\n    info!(\"WRITE BUF: {=[u8]:#X}\", wr_buf);\n    flash.write_memory(0, &wr_buf, use_dma).await;\n    flash.read_memory(0, &mut rd_buf, use_dma).await;\n    info!(\"READ BUF: {=[u8]:#X}\", rd_buf);\n    assert_eq!(rd_buf[0], 0x00);\n    assert_eq!(rd_buf[15], 0x0F);\n\n    flash.enable_mm().await;\n    info!(\"Enabled memory mapped mode\");\n\n    let first_u32 = unsafe { *(0xA0000000 as *const u32) };\n    info!(\"first_u32: 0x{=u32:X}\", first_u32);\n    assert_eq!(first_u32, 0x03020100);\n\n    let second_u32 = unsafe { *(0xA0000004 as *const u32) };\n    assert_eq!(second_u32, 0x07060504);\n    info!(\"second_u32: 0x{=u32:X}\", second_u32);\n\n    let first_u8 = unsafe { *(0xA0000000 as *const u8) };\n    assert_eq!(first_u8, 00);\n    info!(\"first_u8: 0x{=u8:X}\", first_u8);\n\n    let second_u8 = unsafe { *(0xA0000001 as *const u8) };\n    assert_eq!(second_u8, 0x01);\n    info!(\"second_u8: 0x{=u8:X}\", second_u8);\n\n    let third_u8 = unsafe { *(0xA0000002 as *const u8) };\n    assert_eq!(third_u8, 0x02);\n    info!(\"third_u8: 0x{=u8:X}\", third_u8);\n\n    let fourth_u8 = unsafe { *(0xA0000003 as *const u8) };\n    assert_eq!(fourth_u8, 0x03);\n    info!(\"fourth_u8: 0x{=u8:X}\", fourth_u8);\n\n    info!(\"DONE\");\n}\n\n// Custom implementation for MX66UW1G45G NOR flash memory from Macronix.\n// Chip commands are hardcoded as they depend on the chip used.\n// This implementation enables Octa I/O (OPI) and Double Transfer Rate (DTR)\n\npub struct OctaDtrFlashMemory<'d, I: Instance> {\n    hspi: Hspi<'d, I, Async>,\n}\n\nimpl<'d, I: Instance> OctaDtrFlashMemory<'d, I> {\n    const MEMORY_PAGE_SIZE: usize = 256;\n\n    const CMD_READ_OCTA_DTR: u16 = 0xEE11;\n    const CMD_PAGE_PROGRAM_OCTA_DTR: u16 = 0x12ED;\n\n    const CMD_READ_ID_OCTA_DTR: u16 = 0x9F60;\n\n    const CMD_RESET_ENABLE: u8 = 0x66;\n    const CMD_RESET_ENABLE_OCTA_DTR: u16 = 0x6699;\n    const CMD_RESET: u8 = 0x99;\n    const CMD_RESET_OCTA_DTR: u16 = 0x9966;\n\n    const CMD_WRITE_ENABLE: u8 = 0x06;\n    const CMD_WRITE_ENABLE_OCTA_DTR: u16 = 0x06F9;\n\n    const CMD_SECTOR_ERASE_OCTA_DTR: u16 = 0x21DE;\n    const CMD_BLOCK_ERASE_OCTA_DTR: u16 = 0xDC23;\n\n    const CMD_READ_SR: u8 = 0x05;\n    const CMD_READ_SR_OCTA_DTR: u16 = 0x05FA;\n\n    const CMD_READ_CR2: u8 = 0x71;\n    const CMD_WRITE_CR2: u8 = 0x72;\n\n    const CR2_REG1_ADDR: u32 = 0x00000000;\n    const CR2_OCTA_DTR: u8 = 0x02;\n\n    const CR2_REG3_ADDR: u32 = 0x00000300;\n    const CR2_DC_6_CYCLES: u8 = 0x07;\n\n    pub async fn new(hspi: Hspi<'d, I, Async>) -> Self {\n        let mut memory = Self { hspi };\n\n        memory.reset_memory().await;\n        memory.enable_octa_dtr().await;\n        memory\n    }\n\n    async fn enable_octa_dtr(&mut self) {\n        self.write_enable_spi().await;\n        self.write_cr2_spi(Self::CR2_REG3_ADDR, Self::CR2_DC_6_CYCLES);\n        self.write_enable_spi().await;\n        self.write_cr2_spi(Self::CR2_REG1_ADDR, Self::CR2_OCTA_DTR);\n    }\n\n    pub async fn enable_mm(&mut self) {\n        let read_config = TransferConfig {\n            iwidth: HspiWidth::OCTO,\n            instruction: Some(Self::CMD_READ_OCTA_DTR as u32),\n            isize: AddressSize::_16Bit,\n            idtr: true,\n            adwidth: HspiWidth::OCTO,\n            adsize: AddressSize::_32Bit,\n            addtr: true,\n            dwidth: HspiWidth::OCTO,\n            ddtr: true,\n            dummy: DummyCycles::_6,\n            ..Default::default()\n        };\n\n        let write_config = TransferConfig {\n            iwidth: HspiWidth::OCTO,\n            isize: AddressSize::_16Bit,\n            idtr: true,\n            adwidth: HspiWidth::OCTO,\n            adsize: AddressSize::_32Bit,\n            addtr: true,\n            dwidth: HspiWidth::OCTO,\n            ddtr: true,\n            ..Default::default()\n        };\n        self.hspi.enable_memory_mapped_mode(read_config, write_config).unwrap();\n    }\n\n    async fn exec_command_spi(&mut self, cmd: u8) {\n        let transaction = TransferConfig {\n            iwidth: HspiWidth::SING,\n            instruction: Some(cmd as u32),\n            ..Default::default()\n        };\n        info!(\"Excuting command: 0x{:X}\", transaction.instruction.unwrap());\n        self.hspi.blocking_command(&transaction).unwrap();\n    }\n\n    async fn exec_command_octa_dtr(&mut self, cmd: u16) {\n        let transaction = TransferConfig {\n            iwidth: HspiWidth::OCTO,\n            instruction: Some(cmd as u32),\n            isize: AddressSize::_16Bit,\n            idtr: true,\n            ..Default::default()\n        };\n        info!(\"Excuting command: 0x{:X}\", transaction.instruction.unwrap());\n        self.hspi.blocking_command(&transaction).unwrap();\n    }\n\n    fn wait_write_finish_spi(&mut self) {\n        while (self.read_sr_spi() & 0x01) != 0 {}\n    }\n\n    fn wait_write_finish_octa_dtr(&mut self) {\n        while (self.read_sr_octa_dtr() & 0x01) != 0 {}\n    }\n\n    pub async fn reset_memory(&mut self) {\n        // servono entrambi i comandi?\n        self.exec_command_octa_dtr(Self::CMD_RESET_ENABLE_OCTA_DTR).await;\n        self.exec_command_octa_dtr(Self::CMD_RESET_OCTA_DTR).await;\n        self.exec_command_spi(Self::CMD_RESET_ENABLE).await;\n        self.exec_command_spi(Self::CMD_RESET).await;\n        self.wait_write_finish_spi();\n    }\n\n    async fn write_enable_spi(&mut self) {\n        self.exec_command_spi(Self::CMD_WRITE_ENABLE).await;\n    }\n\n    async fn write_enable_octa_dtr(&mut self) {\n        self.exec_command_octa_dtr(Self::CMD_WRITE_ENABLE_OCTA_DTR).await;\n    }\n\n    pub fn read_id(&mut self) -> [u8; 3] {\n        let mut buffer = [0; 6];\n        let transaction: TransferConfig = TransferConfig {\n            iwidth: HspiWidth::OCTO,\n            instruction: Some(Self::CMD_READ_ID_OCTA_DTR as u32),\n            isize: AddressSize::_16Bit,\n            idtr: true,\n            adwidth: HspiWidth::OCTO,\n            address: Some(0),\n            adsize: AddressSize::_32Bit,\n            addtr: true,\n            dwidth: HspiWidth::OCTO,\n            ddtr: true,\n            dummy: DummyCycles::_5,\n            ..Default::default()\n        };\n        info!(\"Reading flash id: 0x{:X}\", transaction.instruction.unwrap());\n        self.hspi.blocking_read(&mut buffer, transaction).unwrap();\n        [buffer[0], buffer[2], buffer[4]]\n    }\n\n    pub async fn read_memory(&mut self, addr: u32, buffer: &mut [u8], use_dma: bool) {\n        let transaction = TransferConfig {\n            iwidth: HspiWidth::OCTO,\n            instruction: Some(Self::CMD_READ_OCTA_DTR as u32),\n            isize: AddressSize::_16Bit,\n            idtr: true,\n            adwidth: HspiWidth::OCTO,\n            address: Some(addr),\n            adsize: AddressSize::_32Bit,\n            addtr: true,\n            dwidth: HspiWidth::OCTO,\n            ddtr: true,\n            dummy: DummyCycles::_6,\n            ..Default::default()\n        };\n        if use_dma {\n            self.hspi.read(buffer, transaction).await.unwrap();\n        } else {\n            self.hspi.blocking_read(buffer, transaction).unwrap();\n        }\n    }\n\n    async fn perform_erase_octa_dtr(&mut self, addr: u32, cmd: u16) {\n        let transaction = TransferConfig {\n            iwidth: HspiWidth::OCTO,\n            instruction: Some(cmd as u32),\n            isize: AddressSize::_16Bit,\n            idtr: true,\n            adwidth: HspiWidth::OCTO,\n            address: Some(addr),\n            adsize: AddressSize::_32Bit,\n            addtr: true,\n            ..Default::default()\n        };\n        self.write_enable_octa_dtr().await;\n        self.hspi.blocking_command(&transaction).unwrap();\n        self.wait_write_finish_octa_dtr();\n    }\n\n    pub async fn erase_sector(&mut self, addr: u32) {\n        info!(\"Erasing 4K sector at address: 0x{:X}\", addr);\n        self.perform_erase_octa_dtr(addr, Self::CMD_SECTOR_ERASE_OCTA_DTR).await;\n    }\n\n    pub async fn erase_block(&mut self, addr: u32) {\n        info!(\"Erasing 64K block at address: 0x{:X}\", addr);\n        self.perform_erase_octa_dtr(addr, Self::CMD_BLOCK_ERASE_OCTA_DTR).await;\n    }\n\n    async fn write_page_octa_dtr(&mut self, addr: u32, buffer: &[u8], len: usize, use_dma: bool) {\n        assert!(\n            (len as u32 + (addr & 0x000000ff)) <= Self::MEMORY_PAGE_SIZE as u32,\n            \"write_page(): page write length exceeds page boundary (len = {}, addr = {:X}\",\n            len,\n            addr\n        );\n\n        let transaction = TransferConfig {\n            iwidth: HspiWidth::OCTO,\n            instruction: Some(Self::CMD_PAGE_PROGRAM_OCTA_DTR as u32),\n            isize: AddressSize::_16Bit,\n            idtr: true,\n            adwidth: HspiWidth::OCTO,\n            address: Some(addr),\n            adsize: AddressSize::_32Bit,\n            addtr: true,\n            dwidth: HspiWidth::OCTO,\n            ddtr: true,\n            ..Default::default()\n        };\n        self.write_enable_octa_dtr().await;\n        if use_dma {\n            self.hspi.write(buffer, transaction).await.unwrap();\n        } else {\n            self.hspi.blocking_write(buffer, transaction).unwrap();\n        }\n        self.wait_write_finish_octa_dtr();\n    }\n\n    pub async fn write_memory(&mut self, addr: u32, buffer: &[u8], use_dma: bool) {\n        let mut left = buffer.len();\n        let mut place = addr;\n        let mut chunk_start = 0;\n\n        while left > 0 {\n            let max_chunk_size = Self::MEMORY_PAGE_SIZE - (place & 0x000000ff) as usize;\n            let chunk_size = if left >= max_chunk_size { max_chunk_size } else { left };\n            let chunk = &buffer[chunk_start..(chunk_start + chunk_size)];\n            self.write_page_octa_dtr(place, chunk, chunk_size, use_dma).await;\n            place += chunk_size as u32;\n            left -= chunk_size;\n            chunk_start += chunk_size;\n        }\n    }\n\n    pub fn read_sr_spi(&mut self) -> u8 {\n        let mut buffer = [0; 1];\n        let transaction: TransferConfig = TransferConfig {\n            iwidth: HspiWidth::SING,\n            instruction: Some(Self::CMD_READ_SR as u32),\n            dwidth: HspiWidth::SING,\n            ..Default::default()\n        };\n        self.hspi.blocking_read(&mut buffer, transaction).unwrap();\n        // info!(\"Read MX66LM1G45G SR register: 0x{:x}\", buffer[0]);\n        buffer[0]\n    }\n\n    pub fn read_sr_octa_dtr(&mut self) -> u8 {\n        let mut buffer = [0; 2];\n        let transaction: TransferConfig = TransferConfig {\n            iwidth: HspiWidth::OCTO,\n            instruction: Some(Self::CMD_READ_SR_OCTA_DTR as u32),\n            isize: AddressSize::_16Bit,\n            idtr: true,\n            adwidth: HspiWidth::OCTO,\n            address: Some(0),\n            adsize: AddressSize::_32Bit,\n            addtr: true,\n            dwidth: HspiWidth::OCTO,\n            ddtr: true,\n            dummy: DummyCycles::_5,\n            ..Default::default()\n        };\n        self.hspi.blocking_read(&mut buffer, transaction).unwrap();\n        // info!(\"Read MX66LM1G45G SR register: 0x{:x}\", buffer[0]);\n        buffer[0]\n    }\n\n    pub fn read_cr2_spi(&mut self, addr: u32) -> u8 {\n        let mut buffer = [0; 1];\n        let transaction: TransferConfig = TransferConfig {\n            iwidth: HspiWidth::SING,\n            instruction: Some(Self::CMD_READ_CR2 as u32),\n            adwidth: HspiWidth::SING,\n            address: Some(addr),\n            adsize: AddressSize::_32Bit,\n            dwidth: HspiWidth::SING,\n            ..Default::default()\n        };\n        self.hspi.blocking_read(&mut buffer, transaction).unwrap();\n        // info!(\"Read MX66LM1G45G CR2[0x{:X}] register: 0x{:x}\", addr, buffer[0]);\n        buffer[0]\n    }\n\n    pub fn write_cr2_spi(&mut self, addr: u32, value: u8) {\n        let buffer = [value; 1];\n        let transaction: TransferConfig = TransferConfig {\n            iwidth: HspiWidth::SING,\n            instruction: Some(Self::CMD_WRITE_CR2 as u32),\n            adwidth: HspiWidth::SING,\n            address: Some(addr),\n            adsize: AddressSize::_32Bit,\n            dwidth: HspiWidth::SING,\n            ..Default::default()\n        };\n        self.hspi.blocking_write(&buffer, transaction).unwrap();\n    }\n}\n"
  },
  {
    "path": "examples/stm32u5/src/bin/i2c.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse defmt::{info, unwrap};\nuse embassy_executor::Spawner;\nuse embassy_stm32::i2c::I2c;\nuse {defmt_rtt as _, panic_probe as _};\n\nconst HTS221_ADDRESS: u8 = 0x5F;\nconst WHOAMI: u8 = 0x0F;\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let p = embassy_stm32::init(Default::default());\n    let mut i2c = I2c::new_blocking(p.I2C2, p.PF1, p.PF0, Default::default());\n\n    let mut data = [0u8; 1];\n    unwrap!(i2c.blocking_write_read(HTS221_ADDRESS, &[WHOAMI], &mut data));\n\n    // HTS221 data sheet is here: https://www.st.com/resource/en/datasheet/hts221.pdf\n    // 7.1 WHO_AM_I command is x0F which expected response xBC.\n    info!(\"Whoami: 0x{:02x}\", data[0]);\n    assert_eq!(0xBC, data[0]);\n}\n"
  },
  {
    "path": "examples/stm32u5/src/bin/ltdc.rs",
    "content": "#![no_std]\n#![no_main]\n#![macro_use]\n#![allow(static_mut_refs)]\n\n/// This example was derived from examples\\stm32h735\\src\\bin\\ltdc.rs\n/// It demonstrates the LTDC lcd display peripheral and was tested on an STM32U5G9J-DK2 demo board (embassy-stm32 feature \"stm32u5g9zj\" and probe-rs chip \"STM32U5G9ZJTxQ\")\n///\nuse bouncy_box::BouncyBox;\nuse defmt::{info, unwrap};\nuse embassy_executor::Spawner;\nuse embassy_stm32::gpio::{Level, Output, Speed};\nuse embassy_stm32::ltdc::{self, Ltdc, LtdcConfiguration, LtdcLayer, LtdcLayerConfig, PolarityActive, PolarityEdge};\nuse embassy_stm32::{bind_interrupts, peripherals};\nuse embassy_time::{Duration, Timer};\nuse embedded_graphics::Pixel;\nuse embedded_graphics::draw_target::DrawTarget;\nuse embedded_graphics::geometry::{OriginDimensions, Point, Size};\nuse embedded_graphics::image::Image;\nuse embedded_graphics::pixelcolor::Rgb888;\nuse embedded_graphics::pixelcolor::raw::RawU24;\nuse embedded_graphics::prelude::*;\nuse embedded_graphics::primitives::Rectangle;\nuse heapless::index_map::{Entry, FnvIndexMap};\nuse tinybmp::Bmp;\nuse {defmt_rtt as _, panic_probe as _};\n\nconst DISPLAY_WIDTH: usize = 800;\nconst DISPLAY_HEIGHT: usize = 480;\nconst MY_TASK_POOL_SIZE: usize = 2;\n\n// the following two display buffers consume 261120 bytes that just about fits into axis ram found on the mcu\npub static mut FB1: [TargetPixelType; DISPLAY_WIDTH * DISPLAY_HEIGHT] = [0; DISPLAY_WIDTH * DISPLAY_HEIGHT];\npub static mut FB2: [TargetPixelType; DISPLAY_WIDTH * DISPLAY_HEIGHT] = [0; DISPLAY_WIDTH * DISPLAY_HEIGHT];\n\nbind_interrupts!(struct Irqs {\n    LTDC => ltdc::InterruptHandler<peripherals::LTDC>;\n});\n\nconst NUM_COLORS: usize = 256;\n\n#[embassy_executor::main]\nasync fn main(spawner: Spawner) {\n    let p = rcc_setup::stm32u5g9zj_init();\n\n    // enable ICACHE\n    embassy_stm32::pac::ICACHE.cr().write(|w| {\n        w.set_en(true);\n    });\n\n    // blink the led on another task\n    let led = Output::new(p.PD2, Level::High, Speed::Low);\n    spawner.spawn(unwrap!(led_task(led)));\n\n    // numbers from STM32U5G9J-DK2.ioc\n    const RK050HR18H_HSYNC: u16 = 5; // Horizontal synchronization\n    const RK050HR18H_HBP: u16 = 8; // Horizontal back porch\n    const RK050HR18H_HFP: u16 = 8; // Horizontal front porch\n    const RK050HR18H_VSYNC: u16 = 5; // Vertical synchronization\n    const RK050HR18H_VBP: u16 = 8; // Vertical back porch\n    const RK050HR18H_VFP: u16 = 8; // Vertical front porch\n\n    // NOTE: all polarities have to be reversed with respect to the STM32U5G9J-DK2 CubeMX parametrization\n    let ltdc_config = LtdcConfiguration {\n        active_width: DISPLAY_WIDTH as _,\n        active_height: DISPLAY_HEIGHT as _,\n        h_back_porch: RK050HR18H_HBP,\n        h_front_porch: RK050HR18H_HFP,\n        v_back_porch: RK050HR18H_VBP,\n        v_front_porch: RK050HR18H_VFP,\n        h_sync: RK050HR18H_HSYNC,\n        v_sync: RK050HR18H_VSYNC,\n        h_sync_polarity: PolarityActive::ActiveHigh,\n        v_sync_polarity: PolarityActive::ActiveHigh,\n        data_enable_polarity: PolarityActive::ActiveHigh,\n        pixel_clock_polarity: PolarityEdge::RisingEdge,\n    };\n\n    info!(\"init ltdc\");\n    let mut ltdc_de = Output::new(p.PD6, Level::Low, Speed::High);\n    let mut ltdc_disp_ctrl = Output::new(p.PE4, Level::Low, Speed::High);\n    let mut ltdc_bl_ctrl = Output::new(p.PE6, Level::Low, Speed::High);\n    let mut ltdc = Ltdc::new_with_pins(\n        p.LTDC, // PERIPHERAL\n        Irqs,   // IRQS\n        p.PD3,  // CLK\n        p.PE0,  // HSYNC\n        p.PD13, // VSYNC\n        p.PB9,  // B0\n        p.PB2,  // B1\n        p.PD14, // B2\n        p.PD15, // B3\n        p.PD0,  // B4\n        p.PD1,  // B5\n        p.PE7,  // B6\n        p.PE8,  // B7\n        p.PC8,  // G0\n        p.PC9,  // G1\n        p.PE9,  // G2\n        p.PE10, // G3\n        p.PE11, // G4\n        p.PE12, // G5\n        p.PE13, // G6\n        p.PE14, // G7\n        p.PC6,  // R0\n        p.PC7,  // R1\n        p.PE15, // R2\n        p.PD8,  // R3\n        p.PD9,  // R4\n        p.PD10, // R5\n        p.PD11, // R6\n        p.PD12, // R7\n    );\n    ltdc.init(&ltdc_config);\n    ltdc_de.set_low();\n    ltdc_bl_ctrl.set_high();\n    ltdc_disp_ctrl.set_high();\n\n    // we only need to draw on one layer for this example (not to be confused with the double buffer)\n    info!(\"enable bottom layer\");\n    let layer_config = LtdcLayerConfig {\n        pixel_format: ltdc::PixelFormat::L8, // 1 byte per pixel\n        layer: LtdcLayer::Layer1,\n        window_x0: 0,\n        window_x1: DISPLAY_WIDTH as _,\n        window_y0: 0,\n        window_y1: DISPLAY_HEIGHT as _,\n    };\n\n    let ferris_bmp: Bmp<Rgb888> = Bmp::from_slice(include_bytes!(\"./ferris.bmp\")).unwrap();\n    let color_map = build_color_lookup_map(&ferris_bmp);\n    let clut = build_clut(&color_map);\n\n    // enable the bottom layer with a 256 color lookup table\n    ltdc.init_layer(&layer_config, Some(&clut));\n\n    // Safety: the DoubleBuffer controls access to the statically allocated frame buffers\n    // and it is the only thing that mutates their content\n    let mut double_buffer = DoubleBuffer::new(\n        unsafe { FB1.as_mut() },\n        unsafe { FB2.as_mut() },\n        layer_config,\n        color_map,\n    );\n\n    // this allows us to perform some simple animation for every frame\n    let mut bouncy_box = BouncyBox::new(\n        ferris_bmp.bounding_box(),\n        Rectangle::new(Point::zero(), Size::new(DISPLAY_WIDTH as u32, DISPLAY_HEIGHT as u32)),\n        2,\n    );\n\n    loop {\n        // cpu intensive drawing to the buffer that is NOT currently being copied to the LCD screen\n        double_buffer.clear();\n        let position = bouncy_box.next_point();\n        let ferris = Image::new(&ferris_bmp, position);\n        unwrap!(ferris.draw(&mut double_buffer));\n\n        // perform async dma data transfer to the lcd screen\n        unwrap!(double_buffer.swap(&mut ltdc).await);\n    }\n}\n\n/// builds the color look-up table from all unique colors found in the bitmap. This should be a 256 color indexed bitmap to work.\nfn build_color_lookup_map(bmp: &Bmp<Rgb888>) -> FnvIndexMap<u32, u8, NUM_COLORS> {\n    let mut color_map: FnvIndexMap<u32, u8, NUM_COLORS> = FnvIndexMap::new();\n    let mut counter: u8 = 0;\n\n    // add black to position 0\n    color_map.insert(Rgb888::new(0, 0, 0).into_storage(), counter).unwrap();\n    counter += 1;\n\n    for Pixel(_point, color) in bmp.pixels() {\n        let raw = color.into_storage();\n        if let Entry::Vacant(v) = color_map.entry(raw) {\n            v.insert(counter).expect(\"more than 256 colors detected\");\n            counter += 1;\n        }\n    }\n    color_map\n}\n\n/// builds the color look-up table from the color map provided\nfn build_clut(color_map: &FnvIndexMap<u32, u8, NUM_COLORS>) -> [ltdc::RgbColor; NUM_COLORS] {\n    let mut clut = [ltdc::RgbColor::default(); NUM_COLORS];\n    for (color, index) in color_map.iter() {\n        let color = Rgb888::from(RawU24::new(*color));\n        clut[*index as usize] = ltdc::RgbColor {\n            red: color.r(),\n            green: color.g(),\n            blue: color.b(),\n        };\n    }\n\n    clut\n}\n\n#[embassy_executor::task(pool_size = MY_TASK_POOL_SIZE)]\nasync fn led_task(mut led: Output<'static>) {\n    let mut counter = 0;\n    loop {\n        info!(\"blink: {}\", counter);\n        counter += 1;\n\n        // on\n        led.set_low();\n        Timer::after(Duration::from_millis(50)).await;\n\n        // off\n        led.set_high();\n        Timer::after(Duration::from_millis(450)).await;\n    }\n}\n\npub type TargetPixelType = u8;\n\n// A simple double buffer\npub struct DoubleBuffer {\n    buf0: &'static mut [TargetPixelType],\n    buf1: &'static mut [TargetPixelType],\n    is_buf0: bool,\n    layer_config: LtdcLayerConfig,\n    color_map: FnvIndexMap<u32, u8, NUM_COLORS>,\n}\n\nimpl DoubleBuffer {\n    pub fn new(\n        buf0: &'static mut [TargetPixelType],\n        buf1: &'static mut [TargetPixelType],\n        layer_config: LtdcLayerConfig,\n        color_map: FnvIndexMap<u32, u8, NUM_COLORS>,\n    ) -> Self {\n        Self {\n            buf0,\n            buf1,\n            is_buf0: true,\n            layer_config,\n            color_map,\n        }\n    }\n\n    pub fn current(&mut self) -> (&FnvIndexMap<u32, u8, NUM_COLORS>, &mut [TargetPixelType]) {\n        if self.is_buf0 {\n            (&self.color_map, self.buf0)\n        } else {\n            (&self.color_map, self.buf1)\n        }\n    }\n\n    pub async fn swap<T: ltdc::Instance>(&mut self, ltdc: &mut Ltdc<'_, T>) -> Result<(), ltdc::Error> {\n        let (_, buf) = self.current();\n        let frame_buffer = buf.as_ptr();\n        self.is_buf0 = !self.is_buf0;\n        ltdc.set_buffer(self.layer_config.layer, frame_buffer as *const _).await\n    }\n\n    /// Clears the buffer\n    pub fn clear(&mut self) {\n        let (color_map, buf) = self.current();\n        let black = Rgb888::new(0, 0, 0).into_storage();\n        let color_index = color_map.get(&black).expect(\"no black found in the color map\");\n\n        for a in buf.iter_mut() {\n            *a = *color_index; // solid black\n        }\n    }\n}\n\n// Implement DrawTarget for\nimpl DrawTarget for DoubleBuffer {\n    type Color = Rgb888;\n    type Error = ();\n\n    /// Draw a pixel\n    fn draw_iter<I>(&mut self, pixels: I) -> Result<(), Self::Error>\n    where\n        I: IntoIterator<Item = Pixel<Self::Color>>,\n    {\n        let size = self.size();\n        let width = size.width as i32;\n        let height = size.height as i32;\n        let (color_map, buf) = self.current();\n\n        for pixel in pixels {\n            let Pixel(point, color) = pixel;\n\n            if point.x >= 0 && point.y >= 0 && point.x < width && point.y < height {\n                let index = point.y * width + point.x;\n                let raw_color = color.into_storage();\n\n                match color_map.get(&raw_color) {\n                    Some(x) => {\n                        buf[index as usize] = *x;\n                    }\n                    None => panic!(\"color not found in color map: {}\", raw_color),\n                };\n            } else {\n                // Ignore invalid points\n            }\n        }\n\n        Ok(())\n    }\n}\n\nimpl OriginDimensions for DoubleBuffer {\n    /// Return the size of the display\n    fn size(&self) -> Size {\n        Size::new(\n            (self.layer_config.window_x1 - self.layer_config.window_x0) as _,\n            (self.layer_config.window_y1 - self.layer_config.window_y0) as _,\n        )\n    }\n}\n\nmod rcc_setup {\n\n    use embassy_stm32::time::Hertz;\n    use embassy_stm32::{Config, Peripherals, rcc};\n\n    /// Sets up clocks for the stm32u5g9zj mcu\n    /// change this if you plan to use a different microcontroller\n    pub fn stm32u5g9zj_init() -> Peripherals {\n        // setup power and clocks for an STM32U5G9J-DK2 run from an external 16 Mhz external oscillator\n        let mut config = Config::default();\n        config.rcc.hse = Some(rcc::Hse {\n            freq: Hertz(16_000_000),\n            mode: rcc::HseMode::Oscillator,\n        });\n        config.rcc.pll1 = Some(rcc::Pll {\n            source: rcc::PllSource::HSE,\n            prediv: rcc::PllPreDiv::DIV1,\n            mul: rcc::PllMul::MUL10,\n            divp: None,\n            divq: None,\n            divr: Some(rcc::PllDiv::DIV1),\n        });\n        config.rcc.sys = rcc::Sysclk::PLL1_R; // 160 Mhz\n        config.rcc.pll3 = Some(rcc::Pll {\n            source: rcc::PllSource::HSE,\n            prediv: rcc::PllPreDiv::DIV4, // PLL_M\n            mul: rcc::PllMul::MUL125,     // PLL_N\n            divp: None,\n            divq: None,\n            divr: Some(rcc::PllDiv::DIV20),\n        });\n        config.rcc.mux.ltdcsel = rcc::mux::Ltdcsel::PLL3_R; // 25 MHz\n        embassy_stm32::init(config)\n    }\n}\n\nmod bouncy_box {\n    use embedded_graphics::geometry::Point;\n    use embedded_graphics::primitives::Rectangle;\n\n    enum Direction {\n        DownLeft,\n        DownRight,\n        UpLeft,\n        UpRight,\n    }\n\n    pub struct BouncyBox {\n        direction: Direction,\n        child_rect: Rectangle,\n        parent_rect: Rectangle,\n        current_point: Point,\n        move_by: usize,\n    }\n\n    // This calculates the coordinates of a chile rectangle bounced around inside a parent bounded box\n    impl BouncyBox {\n        pub fn new(child_rect: Rectangle, parent_rect: Rectangle, move_by: usize) -> Self {\n            let center_box = parent_rect.center();\n            let center_img = child_rect.center();\n            let current_point = Point::new(center_box.x - center_img.x / 2, center_box.y - center_img.y / 2);\n            Self {\n                direction: Direction::DownRight,\n                child_rect,\n                parent_rect,\n                current_point,\n                move_by,\n            }\n        }\n\n        pub fn next_point(&mut self) -> Point {\n            let direction = &self.direction;\n            let img_height = self.child_rect.size.height as i32;\n            let box_height = self.parent_rect.size.height as i32;\n            let img_width = self.child_rect.size.width as i32;\n            let box_width = self.parent_rect.size.width as i32;\n            let move_by = self.move_by as i32;\n\n            match direction {\n                Direction::DownLeft => {\n                    self.current_point.x -= move_by;\n                    self.current_point.y += move_by;\n\n                    let x_out_of_bounds = self.current_point.x < 0;\n                    let y_out_of_bounds = (self.current_point.y + img_height) > box_height;\n\n                    if x_out_of_bounds && y_out_of_bounds {\n                        self.direction = Direction::UpRight\n                    } else if x_out_of_bounds && !y_out_of_bounds {\n                        self.direction = Direction::DownRight\n                    } else if !x_out_of_bounds && y_out_of_bounds {\n                        self.direction = Direction::UpLeft\n                    }\n                }\n                Direction::DownRight => {\n                    self.current_point.x += move_by;\n                    self.current_point.y += move_by;\n\n                    let x_out_of_bounds = (self.current_point.x + img_width) > box_width;\n                    let y_out_of_bounds = (self.current_point.y + img_height) > box_height;\n\n                    if x_out_of_bounds && y_out_of_bounds {\n                        self.direction = Direction::UpLeft\n                    } else if x_out_of_bounds && !y_out_of_bounds {\n                        self.direction = Direction::DownLeft\n                    } else if !x_out_of_bounds && y_out_of_bounds {\n                        self.direction = Direction::UpRight\n                    }\n                }\n                Direction::UpLeft => {\n                    self.current_point.x -= move_by;\n                    self.current_point.y -= move_by;\n\n                    let x_out_of_bounds = self.current_point.x < 0;\n                    let y_out_of_bounds = self.current_point.y < 0;\n\n                    if x_out_of_bounds && y_out_of_bounds {\n                        self.direction = Direction::DownRight\n                    } else if x_out_of_bounds && !y_out_of_bounds {\n                        self.direction = Direction::UpRight\n                    } else if !x_out_of_bounds && y_out_of_bounds {\n                        self.direction = Direction::DownLeft\n                    }\n                }\n                Direction::UpRight => {\n                    self.current_point.x += move_by;\n                    self.current_point.y -= move_by;\n\n                    let x_out_of_bounds = (self.current_point.x + img_width) > box_width;\n                    let y_out_of_bounds = self.current_point.y < 0;\n\n                    if x_out_of_bounds && y_out_of_bounds {\n                        self.direction = Direction::DownLeft\n                    } else if x_out_of_bounds && !y_out_of_bounds {\n                        self.direction = Direction::UpLeft\n                    } else if !x_out_of_bounds && y_out_of_bounds {\n                        self.direction = Direction::DownRight\n                    }\n                }\n            }\n\n            self.current_point\n        }\n    }\n}\n"
  },
  {
    "path": "examples/stm32u5/src/bin/rng.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_stm32::rng::Rng;\nuse embassy_stm32::{bind_interrupts, peripherals, rng};\nuse {defmt_rtt as _, panic_probe as _};\n\nbind_interrupts!(struct Irqs {\n    RNG => rng::InterruptHandler<peripherals::RNG>;\n});\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let p = embassy_stm32::init(Default::default());\n\n    info!(\"Hello World!\");\n\n    let mut rng = Rng::new(p.RNG, Irqs);\n\n    let mut buf = [0u8; 16];\n    unwrap!(rng.async_fill_bytes(&mut buf).await);\n    info!(\"random bytes: {:02x}\", buf);\n}\n"
  },
  {
    "path": "examples/stm32u5/src/bin/tsc.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_stm32::tsc::{self, *};\nuse embassy_stm32::{bind_interrupts, peripherals};\nuse embassy_time::Timer;\nuse {defmt_rtt as _, panic_probe as _};\n\nbind_interrupts!(struct Irqs {\n    TSC => InterruptHandler<embassy_stm32::peripherals::TSC>;\n});\n\n#[cortex_m_rt::exception]\nunsafe fn HardFault(_: &cortex_m_rt::ExceptionFrame) -> ! {\n    cortex_m::peripheral::SCB::sys_reset();\n}\n\n#[embassy_executor::main]\nasync fn main(_spawner: embassy_executor::Spawner) {\n    let device_config = embassy_stm32::Config::default();\n    let context = embassy_stm32::init(device_config);\n\n    let config = tsc::Config {\n        ct_pulse_high_length: ChargeTransferPulseCycle::_2,\n        ct_pulse_low_length: ChargeTransferPulseCycle::_2,\n        spread_spectrum: false,\n        spread_spectrum_deviation: SSDeviation::new(2).unwrap(),\n        spread_spectrum_prescaler: false,\n        pulse_generator_prescaler: PGPrescalerDivider::_4,\n        max_count_value: MaxCount::_8191,\n        io_default_mode: false,\n        synchro_pin_polarity: false,\n        acquisition_mode: false,\n        max_count_interrupt: false,\n    };\n\n    let mut g1: PinGroupWithRoles<peripherals::TSC, G1> = PinGroupWithRoles::default();\n    g1.set_io2::<tsc::pin_roles::Sample>(context.PB13);\n    g1.set_io3::<tsc::pin_roles::Shield>(context.PB14);\n\n    let mut g2: PinGroupWithRoles<peripherals::TSC, G2> = PinGroupWithRoles::default();\n    g2.set_io1::<tsc::pin_roles::Sample>(context.PB4);\n    let sensor0 = g2.set_io2(context.PB5);\n\n    let mut g7: PinGroupWithRoles<peripherals::TSC, G7> = PinGroupWithRoles::default();\n    g7.set_io2::<tsc::pin_roles::Sample>(context.PE3);\n    let sensor1 = g7.set_io3(context.PE4);\n\n    let pin_groups: PinGroups<peripherals::TSC> = PinGroups {\n        g1: Some(g1.pin_group),\n        g2: Some(g2.pin_group),\n        g7: Some(g7.pin_group),\n        ..Default::default()\n    };\n\n    let mut touch_controller = tsc::Tsc::new_async(context.TSC, pin_groups, config, Irqs).unwrap();\n\n    let acquisition_bank = touch_controller.create_acquisition_bank(AcquisitionBankPins {\n        g2_pin: Some(sensor0),\n        g7_pin: Some(sensor1),\n        ..Default::default()\n    });\n\n    touch_controller.set_active_channels_bank(&acquisition_bank);\n\n    info!(\"Starting touch_controller interface\");\n    loop {\n        touch_controller.start();\n        touch_controller.pend_for_acquisition().await;\n        touch_controller.discharge_io(true);\n        Timer::after_millis(1).await;\n\n        let status = touch_controller.get_acquisition_bank_status(&acquisition_bank);\n\n        if status.all_complete() {\n            let read_values = touch_controller.get_acquisition_bank_values(&acquisition_bank);\n            let group2_reading = read_values.get_group_reading(Group::Two).unwrap();\n            let group7_reading = read_values.get_group_reading(Group::Seven).unwrap();\n            info!(\"group 2 value: {}\", group2_reading.sensor_value);\n            info!(\"group 7 value: {}\", group7_reading.sensor_value);\n        }\n    }\n}\n"
  },
  {
    "path": "examples/stm32u5/src/bin/usb_hs_serial.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse defmt::{panic, *};\nuse defmt_rtt as _; // global logger\nuse embassy_executor::Spawner;\nuse embassy_futures::join::join;\nuse embassy_stm32::usb::{Driver, Instance};\nuse embassy_stm32::{Config, bind_interrupts, peripherals, usb};\nuse embassy_usb::Builder;\nuse embassy_usb::class::cdc_acm::{CdcAcmClass, State};\nuse embassy_usb::driver::EndpointError;\nuse panic_probe as _;\n\nbind_interrupts!(struct Irqs {\n    OTG_HS => usb::InterruptHandler<peripherals::USB_OTG_HS>;\n});\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    info!(\"Hello World!\");\n\n    let mut config = Config::default();\n    {\n        use embassy_stm32::rcc::*;\n        use embassy_stm32::time::Hertz;\n        config.rcc.hse = Some(Hse {\n            freq: Hertz(16_000_000),\n            mode: HseMode::Oscillator,\n        });\n        config.rcc.pll1 = Some(Pll {\n            source: PllSource::HSE,\n            prediv: PllPreDiv::DIV2,   // HSE / 2 = 8MHz\n            mul: PllMul::MUL60,        // 8MHz * 60 = 480MHz\n            divr: Some(PllDiv::DIV3),  // 480MHz / 3 = 160MHz (sys_ck)\n            divq: Some(PllDiv::DIV10), // 480MHz / 10 = 48MHz (USB)\n            divp: Some(PllDiv::DIV15), // 480MHz / 15 = 32MHz (USBOTG)\n        });\n        config.rcc.mux.otghssel = mux::Otghssel::PLL1_P;\n        config.rcc.voltage_range = VoltageScale::RANGE1;\n        config.rcc.sys = Sysclk::PLL1_R;\n    }\n\n    let p = embassy_stm32::init(config);\n\n    // Create the driver, from the HAL.\n    let mut ep_out_buffer = [0u8; 256];\n    let mut config = embassy_stm32::usb::Config::default();\n    // Do not enable vbus_detection. This is a safe default that works in all boards.\n    // However, if your USB device is self-powered (can stay powered on if USB is unplugged), you need\n    // to enable vbus_detection to comply with the USB spec. If you enable it, the board\n    // has to support it or USB won't work at all. See docs on `vbus_detection` for details.\n    config.vbus_detection = false;\n    let driver = Driver::new_hs(p.USB_OTG_HS, Irqs, p.PA12, p.PA11, &mut ep_out_buffer, config);\n\n    // Create embassy-usb Config\n    let mut config = embassy_usb::Config::new(0xc0de, 0xcafe);\n    config.manufacturer = Some(\"Embassy\");\n    config.product = Some(\"USB-serial example\");\n    config.serial_number = Some(\"12345678\");\n\n    // Create embassy-usb DeviceBuilder using the driver and config.\n    // It needs some buffers for building the descriptors.\n    let mut config_descriptor = [0; 256];\n    let mut bos_descriptor = [0; 256];\n    let mut control_buf = [0; 64];\n\n    let mut state = State::new();\n\n    let mut builder = Builder::new(\n        driver,\n        config,\n        &mut config_descriptor,\n        &mut bos_descriptor,\n        &mut [], // no msos descriptors\n        &mut control_buf,\n    );\n\n    // Create classes on the builder.\n    let mut class = CdcAcmClass::new(&mut builder, &mut state, 64);\n\n    // Build the builder.\n    let mut usb = builder.build();\n\n    // Run the USB device.\n    let usb_fut = usb.run();\n\n    // Do stuff with the class!\n    let echo_fut = async {\n        loop {\n            class.wait_connection().await;\n            info!(\"Connected\");\n            let _ = echo(&mut class).await;\n            info!(\"Disconnected\");\n        }\n    };\n\n    // Run everything concurrently.\n    // If we had made everything `'static` above instead, we could do this using separate tasks instead.\n    join(usb_fut, echo_fut).await;\n}\n\nstruct Disconnected {}\n\nimpl From<EndpointError> for Disconnected {\n    fn from(val: EndpointError) -> Self {\n        match val {\n            EndpointError::BufferOverflow => panic!(\"Buffer overflow\"),\n            EndpointError::Disabled => Disconnected {},\n        }\n    }\n}\n\nasync fn echo<'d, T: Instance + 'd>(class: &mut CdcAcmClass<'d, Driver<'d, T>>) -> Result<(), Disconnected> {\n    let mut buf = [0; 64];\n    loop {\n        let n = class.read_packet(&mut buf).await?;\n        let data = &buf[..n];\n        info!(\"data: {:x}\", data);\n        class.write_packet(data).await?;\n    }\n}\n"
  },
  {
    "path": "examples/stm32u5/src/bin/usb_serial.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse defmt::{panic, *};\nuse defmt_rtt as _; // global logger\nuse embassy_executor::Spawner;\nuse embassy_futures::join::join;\nuse embassy_stm32::usb::{Driver, Instance};\nuse embassy_stm32::{Config, bind_interrupts, peripherals, usb};\nuse embassy_usb::Builder;\nuse embassy_usb::class::cdc_acm::{CdcAcmClass, State};\nuse embassy_usb::driver::EndpointError;\nuse panic_probe as _;\n\nbind_interrupts!(struct Irqs {\n    OTG_HS => usb::InterruptHandler<peripherals::USB_OTG_HS>;\n});\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    info!(\"Hello World!\");\n\n    let mut config = Config::default();\n    {\n        use embassy_stm32::rcc::*;\n        config.rcc.hsi = true;\n        config.rcc.pll1 = Some(Pll {\n            source: PllSource::HSI, // 16 MHz\n            prediv: PllPreDiv::DIV1,\n            mul: PllMul::MUL10,\n            divp: None,\n            divq: None,\n            divr: Some(PllDiv::DIV1), // 160 MHz\n        });\n        config.rcc.sys = Sysclk::PLL1_R;\n        config.rcc.voltage_range = VoltageScale::RANGE1;\n        config.rcc.hsi48 = Some(Hsi48Config { sync_from_usb: true }); // needed for USB\n        config.rcc.mux.iclksel = mux::Iclksel::HSI48; // USB uses ICLK\n    }\n\n    let p = embassy_stm32::init(config);\n\n    // Create the driver, from the HAL.\n    let mut ep_out_buffer = [0u8; 256];\n    let mut config = embassy_stm32::usb::Config::default();\n    // Do not enable vbus_detection. This is a safe default that works in all boards.\n    // However, if your USB device is self-powered (can stay powered on if USB is unplugged), you need\n    // to enable vbus_detection to comply with the USB spec. If you enable it, the board\n    // has to support it or USB won't work at all. See docs on `vbus_detection` for details.\n    config.vbus_detection = false;\n    let driver = Driver::new_hs(p.USB_OTG_HS, Irqs, p.PA12, p.PA11, &mut ep_out_buffer, config);\n\n    // Create embassy-usb Config\n    let mut config = embassy_usb::Config::new(0xc0de, 0xcafe);\n    config.manufacturer = Some(\"Embassy\");\n    config.product = Some(\"USB-serial example\");\n    config.serial_number = Some(\"12345678\");\n\n    // Create embassy-usb DeviceBuilder using the driver and config.\n    // It needs some buffers for building the descriptors.\n    let mut config_descriptor = [0; 256];\n    let mut bos_descriptor = [0; 256];\n    let mut control_buf = [0; 64];\n\n    let mut state = State::new();\n\n    let mut builder = Builder::new(\n        driver,\n        config,\n        &mut config_descriptor,\n        &mut bos_descriptor,\n        &mut [], // no msos descriptors\n        &mut control_buf,\n    );\n\n    // Create classes on the builder.\n    let mut class = CdcAcmClass::new(&mut builder, &mut state, 64);\n\n    // Build the builder.\n    let mut usb = builder.build();\n\n    // Run the USB device.\n    let usb_fut = usb.run();\n\n    // Do stuff with the class!\n    let echo_fut = async {\n        loop {\n            class.wait_connection().await;\n            info!(\"Connected\");\n            let _ = echo(&mut class).await;\n            info!(\"Disconnected\");\n        }\n    };\n\n    // Run everything concurrently.\n    // If we had made everything `'static` above instead, we could do this using separate tasks instead.\n    join(usb_fut, echo_fut).await;\n}\n\nstruct Disconnected {}\n\nimpl From<EndpointError> for Disconnected {\n    fn from(val: EndpointError) -> Self {\n        match val {\n            EndpointError::BufferOverflow => panic!(\"Buffer overflow\"),\n            EndpointError::Disabled => Disconnected {},\n        }\n    }\n}\n\nasync fn echo<'d, T: Instance + 'd>(class: &mut CdcAcmClass<'d, Driver<'d, T>>) -> Result<(), Disconnected> {\n    let mut buf = [0; 64];\n    loop {\n        let n = class.read_packet(&mut buf).await?;\n        let data = &buf[..n];\n        info!(\"data: {:x}\", data);\n        class.write_packet(data).await?;\n    }\n}\n"
  },
  {
    "path": "examples/stm32wb/.cargo/config.toml",
    "content": "[target.'cfg(all(target_arch = \"arm\", target_os = \"none\"))']\n# replace STM32WB55CCUx with your chip as listed in `probe-rs chip list`\n# runner = \"probe-rs run --chip STM32WB55RGVx --speed 1000 --connect-under-reset\"\nrunner = \"teleprobe local run --chip STM32WB55RG --elf\"\n\n[build]\ntarget = \"thumbv7em-none-eabihf\"\n\n[env]\nDEFMT_LOG = \"trace\"\n"
  },
  {
    "path": "examples/stm32wb/Cargo.toml",
    "content": "[package]\nedition = \"2024\"\nname = \"embassy-stm32wb-examples\"\nversion = \"0.1.0\"\nlicense = \"MIT OR Apache-2.0\"\npublish = false\n\n[dependencies]\n# Change stm32wb55rg to your chip name in both dependencies, if necessary.\nembassy-stm32 = { version = \"0.6.0\", path = \"../../embassy-stm32\", features = [ \"defmt\", \"stm32wb55rg\", \"time-driver-any\", \"memory-x\", \"exti\", \"low-power\", \"executor-thread\"]  }\nembassy-stm32-wpan = { version = \"0.1.0\", path = \"../../embassy-stm32-wpan\", features = [\"defmt\", \"stm32wb55rg\"] }\nembassy-sync = { version = \"0.8.0\", path = \"../../embassy-sync\", features = [\"defmt\"] }\nembassy-executor = { version = \"0.10.0\", path = \"../../embassy-executor\", features = [\"defmt\"] }\nembassy-time = { version = \"0.5.1\", path = \"../../embassy-time\", features = [\"defmt\", \"defmt-timestamp-uptime\", \"tick-hz-32_768\"] }\nembassy-net = { version = \"0.9.0\", path = \"../../embassy-net\", features = [\"defmt\", \"udp\", \"proto-ipv6\", \"medium-ieee802154\", ], optional = true }\n\ndefmt = \"1.0.1\"\ndefmt-rtt = \"1.0.0\"\n\ncortex-m = { version = \"0.7.6\", features = [\"inline-asm\", \"critical-section-single-core\"] }\ncortex-m-rt = \"0.7.0\"\nembedded-hal = \"1.0.0\"\npanic-probe = { version = \"1.0.0\", features = [\"print-defmt\"] }\nheapless = { version = \"0.9\", default-features = false }\nstatic_cell = \"2\"\n\n[features]\ndefault = [\"ble\", \"mac\"]\nmac = [\"embassy-stm32-wpan/wb55_mac\", \"dep:embassy-net\"]\nble = [\"embassy-stm32-wpan/wb55_ble\"]\n\n[[bin]] \nname = \"tl_mbox_ble\"\nrequired-features = [\"ble\"]\n\n[[bin]] \nname = \"tl_mbox_mac\"\nrequired-features = [\"mac\"]\n\n[[bin]] \nname = \"mac_ffd\"\nrequired-features = [\"mac\"]\n\n[[bin]] \nname = \"mac_ffd_net\"\nrequired-features = [\"mac\"]\n\n[[bin]] \nname = \"eddystone_beacon\"\nrequired-features = [\"ble\"]\n\n[[bin]] \nname = \"gatt_server\"\nrequired-features = [\"ble\"]\n\n[profile.release]\ndebug = 2\n\n[package.metadata.embassy]\nbuild = [\n  { target = \"thumbv7em-none-eabi\", artifact-dir = \"out/examples/stm32wb\" }\n]\n"
  },
  {
    "path": "examples/stm32wb/build.rs",
    "content": "use std::error::Error;\n\nfn main() -> Result<(), Box<dyn Error>> {\n    println!(\"cargo:rustc-link-arg-bins=--nmagic\");\n    println!(\"cargo:rustc-link-arg-bins=-Tlink.x\");\n    println!(\"cargo:rustc-link-arg-bins=-Ttl_mbox.x\");\n    println!(\"cargo:rustc-link-arg-bins=-Tdefmt.x\");\n\n    Ok(())\n}\n"
  },
  {
    "path": "examples/stm32wb/src/bin/blinky.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_stm32::gpio::{Level, Output, Speed};\nuse embassy_time::Timer;\nuse {defmt_rtt as _, panic_probe as _};\n\n#[embassy_executor::main(executor = \"embassy_stm32::executor::Executor\", entry = \"cortex_m_rt::entry\")]\nasync fn main(_spawner: Spawner) {\n    let p = embassy_stm32::init(Default::default());\n    info!(\"Hello World!\");\n\n    let mut led = Output::new(p.PB0, Level::High, Speed::Low);\n\n    loop {\n        info!(\"high\");\n        led.set_high();\n        Timer::after_millis(500).await;\n\n        info!(\"low\");\n        led.set_low();\n        Timer::after_millis(500).await;\n    }\n}\n"
  },
  {
    "path": "examples/stm32wb/src/bin/button_exti.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_stm32::exti::{self, ExtiInput};\nuse embassy_stm32::gpio::Pull;\nuse embassy_stm32::{bind_interrupts, interrupt};\nuse {defmt_rtt as _, panic_probe as _};\n\nbind_interrupts!(\n    pub struct Irqs{\n        EXTI4 => exti::InterruptHandler<interrupt::typelevel::EXTI4>;\n});\n\n#[embassy_executor::main(executor = \"embassy_stm32::executor::Executor\", entry = \"cortex_m_rt::entry\")]\nasync fn main(_spawner: Spawner) {\n    let p = embassy_stm32::init(Default::default());\n    info!(\"Hello World!\");\n\n    let mut button = ExtiInput::new(p.PC4, p.EXTI4, Pull::Up, Irqs);\n\n    info!(\"Press the USER button...\");\n\n    loop {\n        button.wait_for_falling_edge().await;\n        info!(\"Pressed!\");\n        button.wait_for_rising_edge().await;\n        info!(\"Released!\");\n    }\n}\n"
  },
  {
    "path": "examples/stm32wb/src/bin/eddystone_beacon.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse core::time::Duration;\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_stm32::bind_interrupts;\nuse embassy_stm32::ipcc::{Config, ReceiveInterruptHandler, TransmitInterruptHandler};\nuse embassy_stm32::rcc::WPAN_DEFAULT;\nuse embassy_stm32_wpan::TlMbox;\nuse embassy_stm32_wpan::hci::BdAddr;\nuse embassy_stm32_wpan::hci::host::uart::UartHci;\nuse embassy_stm32_wpan::hci::host::{AdvertisingFilterPolicy, EncryptionKey, HostHci, OwnAddressType};\nuse embassy_stm32_wpan::hci::types::AdvertisingType;\nuse embassy_stm32_wpan::hci::vendor::command::gap::{AdvertisingDataType, DiscoverableParameters, GapCommands, Role};\nuse embassy_stm32_wpan::hci::vendor::command::gatt::GattCommands;\nuse embassy_stm32_wpan::hci::vendor::command::hal::{ConfigData, HalCommands, PowerLevel};\nuse embassy_stm32_wpan::lhci::LhciC1DeviceInformationCcrp;\nuse {defmt_rtt as _, panic_probe as _};\n\nbind_interrupts!(struct Irqs{\n    IPCC_C1_RX => ReceiveInterruptHandler;\n    IPCC_C1_TX => TransmitInterruptHandler;\n});\n\nconst BLE_GAP_DEVICE_NAME_LENGTH: u8 = 7;\n\n#[embassy_executor::main(executor = \"embassy_stm32::executor::Executor\", entry = \"cortex_m_rt::entry\")]\nasync fn main(_spawner: Spawner) {\n    /*\n        How to make this work:\n\n        - Obtain a NUCLEO-STM32WB55 from your preferred supplier.\n        - Download and Install STM32CubeProgrammer.\n        - Download stm32wb5x_FUS_fw.bin, stm32wb5x_BLE_Mac_802_15_4_fw.bin, and Release_Notes.html from\n          gh:STMicroelectronics/STM32CubeWB@2234d97/Projects/STM32WB_Copro_Wireless_Binaries/STM32WB5x\n        - Open STM32CubeProgrammer\n        - On the right-hand pane, click \"firmware upgrade\" to upgrade the st-link firmware.\n        - Once complete, click connect to connect to the device.\n        - On the left hand pane, click the RSS signal icon to open \"Firmware Upgrade Services\".\n        - In the Release_Notes.html, find the memory address that corresponds to your device for the stm32wb5x_FUS_fw.bin file\n        - Select that file, the memory address, \"verify download\", and then \"Firmware Upgrade\".\n        - Once complete, in the Release_Notes.html, find the memory address that corresponds to your device for the\n          stm32wb5x_BLE_Mac_802_15_4_fw.bin file. It should not be the same memory address.\n        - Select that file, the memory address, \"verify download\", and then \"Firmware Upgrade\".\n        - Select \"Start Wireless Stack\".\n        - Disconnect from the device.\n        - Run this example.\n\n        Note: extended stack versions are not supported at this time. Do not attempt to install a stack with \"extended\" in the name.\n    */\n\n    let mut config = embassy_stm32::Config::default();\n    config.rcc = WPAN_DEFAULT;\n    let p = embassy_stm32::init(config);\n    info!(\"Hello World!\");\n\n    let config = Config::default();\n    let mbox = TlMbox::init(p.IPCC, Irqs, config).await.unwrap();\n    let mut sys = mbox.sys_subsystem;\n    let mut ble = mbox.ble_subsystem;\n\n    let _ = sys.shci_c2_ble_init(Default::default()).await;\n\n    info!(\"resetting BLE...\");\n    ble.reset().await;\n    let response = ble.read().await.unwrap();\n    defmt::info!(\"{}\", response);\n\n    info!(\"config public address...\");\n    ble.write_config_data(&ConfigData::public_address(get_bd_addr()).build())\n        .await;\n    let response = ble.read().await.unwrap();\n    defmt::info!(\"{}\", response);\n\n    info!(\"config random address...\");\n    ble.write_config_data(&ConfigData::random_address(get_random_addr()).build())\n        .await;\n    let response = ble.read().await.unwrap();\n    defmt::info!(\"{}\", response);\n\n    info!(\"config identity root...\");\n    ble.write_config_data(&ConfigData::identity_root(&get_irk()).build())\n        .await;\n    let response = ble.read().await.unwrap();\n    defmt::info!(\"{}\", response);\n\n    info!(\"config encryption root...\");\n    ble.write_config_data(&ConfigData::encryption_root(&get_erk()).build())\n        .await;\n    let response = ble.read().await.unwrap();\n    defmt::info!(\"{}\", response);\n\n    info!(\"config tx power level...\");\n    ble.set_tx_power_level(PowerLevel::ZerodBm).await;\n    let response = ble.read().await.unwrap();\n    defmt::info!(\"{}\", response);\n\n    info!(\"GATT init...\");\n    ble.init_gatt().await;\n    let response = ble.read().await.unwrap();\n    defmt::info!(\"{}\", response);\n\n    info!(\"GAP init...\");\n    ble.init_gap(Role::PERIPHERAL, false, BLE_GAP_DEVICE_NAME_LENGTH).await;\n    let response = ble.read().await.unwrap();\n    defmt::info!(\"{}\", response);\n\n    // info!(\"set scan response...\");\n    // ble.le_set_scan_response_data(&[]).await.unwrap();\n    // let response = ble.read().await.unwrap();\n    // defmt::info!(\"{}\", response);\n\n    info!(\"set discoverable...\");\n    ble.set_discoverable(&DiscoverableParameters {\n        advertising_type: AdvertisingType::NonConnectableUndirected,\n        advertising_interval: Some((Duration::from_millis(250), Duration::from_millis(250))),\n        address_type: OwnAddressType::Public,\n        filter_policy: AdvertisingFilterPolicy::AllowConnectionAndScan,\n        local_name: None,\n        advertising_data: &[],\n        conn_interval: (None, None),\n    })\n    .await\n    .unwrap();\n\n    let response = ble.read().await;\n    defmt::info!(\"{}\", response);\n\n    // remove some advertisement to decrease the packet size\n    info!(\"delete tx power ad type...\");\n    ble.delete_ad_type(AdvertisingDataType::TxPowerLevel).await;\n    let response = ble.read().await.unwrap();\n    defmt::info!(\"{}\", response);\n\n    info!(\"delete conn interval ad type...\");\n    ble.delete_ad_type(AdvertisingDataType::PeripheralConnectionInterval)\n        .await;\n    let response = ble.read().await.unwrap();\n    defmt::info!(\"{}\", response);\n\n    info!(\"update advertising data...\");\n    ble.update_advertising_data(&eddystone_advertising_data())\n        .await\n        .unwrap();\n    let response = ble.read().await.unwrap();\n    defmt::info!(\"{}\", response);\n\n    info!(\"update advertising data type...\");\n    ble.update_advertising_data(&[3, AdvertisingDataType::UuidCompleteList16 as u8, 0xaa, 0xfe])\n        .await\n        .unwrap();\n    let response = ble.read().await.unwrap();\n    defmt::info!(\"{}\", response);\n\n    info!(\"update advertising data flags...\");\n    ble.update_advertising_data(&[\n        2,\n        AdvertisingDataType::Flags as u8,\n        (0x02 | 0x04) as u8, // BLE general discoverable, without BR/EDR support\n    ])\n    .await\n    .unwrap();\n    let response = ble.read().await.unwrap();\n    defmt::info!(\"{}\", response);\n\n    cortex_m::asm::wfi();\n}\n\nfn get_bd_addr() -> BdAddr {\n    let mut bytes = [0u8; 6];\n\n    let lhci_info = LhciC1DeviceInformationCcrp::new();\n    bytes[0] = (lhci_info.uid64 & 0xff) as u8;\n    bytes[1] = ((lhci_info.uid64 >> 8) & 0xff) as u8;\n    bytes[2] = ((lhci_info.uid64 >> 16) & 0xff) as u8;\n    bytes[3] = lhci_info.device_type_id;\n    bytes[4] = (lhci_info.st_company_id & 0xff) as u8;\n    bytes[5] = (lhci_info.st_company_id >> 8 & 0xff) as u8;\n\n    BdAddr(bytes)\n}\n\nfn get_random_addr() -> BdAddr {\n    let mut bytes = [0u8; 6];\n\n    let lhci_info = LhciC1DeviceInformationCcrp::new();\n    bytes[0] = (lhci_info.uid64 & 0xff) as u8;\n    bytes[1] = ((lhci_info.uid64 >> 8) & 0xff) as u8;\n    bytes[2] = ((lhci_info.uid64 >> 16) & 0xff) as u8;\n    bytes[3] = 0;\n    bytes[4] = 0x6E;\n    bytes[5] = 0xED;\n\n    BdAddr(bytes)\n}\n\nconst BLE_CFG_IRK: [u8; 16] = [\n    0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0,\n];\nconst BLE_CFG_ERK: [u8; 16] = [\n    0xfe, 0xdc, 0xba, 0x09, 0x87, 0x65, 0x43, 0x21, 0xfe, 0xdc, 0xba, 0x09, 0x87, 0x65, 0x43, 0x21,\n];\n\nfn get_irk() -> EncryptionKey {\n    EncryptionKey(BLE_CFG_IRK)\n}\n\nfn get_erk() -> EncryptionKey {\n    EncryptionKey(BLE_CFG_ERK)\n}\n\nfn eddystone_advertising_data() -> [u8; 24] {\n    const EDDYSTONE_URL: &[u8] = b\"www.rust-lang.com\";\n\n    let mut service_data = [0u8; 24];\n    let url_len = EDDYSTONE_URL.len();\n\n    service_data[0] = 6 + url_len as u8;\n    service_data[1] = AdvertisingDataType::ServiceData as u8;\n\n    // 16-bit eddystone uuid\n    service_data[2] = 0xaa;\n    service_data[3] = 0xFE;\n\n    service_data[4] = 0x10; // URL frame type\n    service_data[5] = 22_i8 as u8; // calibrated TX power at 0m\n    service_data[6] = 0x03; // eddystone url prefix = https\n\n    service_data[7..(7 + url_len)].copy_from_slice(EDDYSTONE_URL);\n\n    service_data\n}\n"
  },
  {
    "path": "examples/stm32wb/src/bin/fus_update.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse cortex_m::peripheral::SCB;\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_stm32::bind_interrupts;\nuse embassy_stm32::ipcc::{Config, ReceiveInterruptHandler, TransmitInterruptHandler};\nuse embassy_stm32::rcc::WPAN_DEFAULT;\nuse embassy_stm32::rtc::Rtc;\nuse embassy_stm32_wpan::TlMbox;\nuse embassy_stm32_wpan::fus::FirmwareUpgrader;\nuse embassy_time::{Duration, Timer};\nuse {defmt_rtt as _, panic_probe as _};\n\nbind_interrupts!(struct Irqs{\n    IPCC_C1_RX => ReceiveInterruptHandler;\n    IPCC_C1_TX => TransmitInterruptHandler;\n});\n\n#[embassy_executor::main(executor = \"embassy_stm32::executor::Executor\", entry = \"cortex_m_rt::entry\")]\nasync fn main(_spawner: Spawner) {\n    /*\n        How to make this work:\n\n        - Obtain a NUCLEO-STM32WB55 from your preferred supplier.\n        - Download and Install STM32CubeProgrammer.\n        - Download stm32wb5x_FUS_fw.bin, stm32wb5x_BLE_Mac_802_15_4_fw.bin, and Release_Notes.html from\n          gh:STMicroelectronics/STM32CubeWB@2234d97/Projects/STM32WB_Copro_Wireless_Binaries/STM32WB5x\n        - Open STM32CubeProgrammer\n        - On the right-hand pane, click \"firmware upgrade\" to upgrade the st-link firmware.\n        - Once complete, click connect to connect to the device.\n        - On the left hand pane, click the RSS signal icon to open \"Firmware Upgrade Services\".\n        - In the Release_Notes.html, find the memory address that corresponds to your device for the stm32wb5x_FUS_fw.bin file\n        - Select that file, the memory address, \"verify download\", and then \"Firmware Upgrade\".\n        - Once complete, in the Release_Notes.html, find the memory address that corresponds to your device for the\n          stm32wb5x_BLE_Mac_802_15_4_fw.bin file. It should not be the same memory address.\n        - Select that file, the memory address, \"verify download\", and then \"Firmware Upgrade\".\n        - Select \"Start Wireless Stack\".\n        - Disconnect from the device.\n        - Run this example.\n\n        Note: extended stack versions are not supported at this time. Do not attempt to install a stack with \"extended\" in the name.\n    */\n\n    let mut config = embassy_stm32::Config::default();\n    config.rcc = WPAN_DEFAULT;\n    let p = embassy_stm32::init(config);\n    info!(\"Hello World!\");\n\n    let (rtc, _time_provider) = Rtc::new(p.RTC);\n\n    let config = Config::default();\n    let mut mbox = TlMbox::init(p.IPCC, Irqs, config).await.unwrap();\n\n    match mbox.sys_subsystem.wireless_fw_info() {\n        None => info!(\"not yet initialized\"),\n        Some(fw_info) => {\n            let version_major = fw_info.version_major();\n            let version_minor = fw_info.version_minor();\n            let subversion = fw_info.subversion();\n\n            let sram2a_size = fw_info.sram2a_size();\n            let sram2b_size = fw_info.sram2b_size();\n\n            info!(\n                \"version {}.{}.{} - SRAM2a {} - SRAM2b {}\",\n                version_major, version_minor, subversion, sram2a_size, sram2b_size\n            );\n        }\n    }\n\n    let mut updater = FirmwareUpgrader::new(rtc, 15);\n\n    updater.boot(mbox.sys_event, &mut mbox.sys_subsystem).await.unwrap();\n\n    Timer::after(Duration::from_secs(3)).await;\n\n    info!(\"System Reset\");\n    defmt::flush();\n    SCB::sys_reset();\n}\n"
  },
  {
    "path": "examples/stm32wb/src/bin/gatt_server.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse core::time::Duration;\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_stm32::bind_interrupts;\nuse embassy_stm32::ipcc::{Config, ReceiveInterruptHandler, TransmitInterruptHandler};\nuse embassy_stm32::rcc::WPAN_DEFAULT;\nuse embassy_stm32_wpan::TlMbox;\nuse embassy_stm32_wpan::hci::event::command::{CommandComplete, ReturnParameters};\nuse embassy_stm32_wpan::hci::host::uart::{Packet, UartHci};\nuse embassy_stm32_wpan::hci::host::{AdvertisingFilterPolicy, EncryptionKey, HostHci, OwnAddressType};\nuse embassy_stm32_wpan::hci::types::AdvertisingType;\nuse embassy_stm32_wpan::hci::vendor::command::gap::{\n    AddressType, AuthenticationRequirements, DiscoverableParameters, GapCommands, IoCapability, LocalName, Pin, Role,\n    SecureConnectionSupport,\n};\nuse embassy_stm32_wpan::hci::vendor::command::gatt::{\n    AddCharacteristicParameters, AddServiceParameters, CharacteristicEvent, CharacteristicPermission,\n    CharacteristicProperty, EncryptionKeySize, GattCommands, ServiceType, UpdateCharacteristicValueParameters, Uuid,\n    WriteResponseParameters,\n};\nuse embassy_stm32_wpan::hci::vendor::command::hal::{ConfigData, HalCommands, PowerLevel};\nuse embassy_stm32_wpan::hci::vendor::event::command::VendorReturnParameters;\nuse embassy_stm32_wpan::hci::vendor::event::{self, AttributeHandle, VendorEvent};\nuse embassy_stm32_wpan::hci::{BdAddr, Event};\nuse embassy_stm32_wpan::lhci::LhciC1DeviceInformationCcrp;\nuse embassy_stm32_wpan::sub::ble::Ble;\nuse embassy_stm32_wpan::sub::mm;\nuse {defmt_rtt as _, panic_probe as _};\n\nbind_interrupts!(struct Irqs{\n    IPCC_C1_RX => ReceiveInterruptHandler;\n    IPCC_C1_TX => TransmitInterruptHandler;\n});\n\nconst BLE_GAP_DEVICE_NAME_LENGTH: u8 = 7;\n\n#[embassy_executor::main(executor = \"embassy_stm32::executor::Executor\", entry = \"cortex_m_rt::entry\")]\nasync fn main(spawner: Spawner) {\n    /*\n        How to make this work:\n\n        - Obtain a NUCLEO-STM32WB55 from your preferred supplier.\n        - Download and Install STM32CubeProgrammer.\n        - Download stm32wb5x_FUS_fw.bin, stm32wb5x_BLE_Mac_802_15_4_fw.bin, and Release_Notes.html from\n          gh:STMicroelectronics/STM32CubeWB@2234d97/Projects/STM32WB_Copro_Wireless_Binaries/STM32WB5x\n        - Open STM32CubeProgrammer\n        - On the right-hand pane, click \"firmware upgrade\" to upgrade the st-link firmware.\n        - Once complete, click connect to connect to the device.\n        - On the left hand pane, click the RSS signal icon to open \"Firmware Upgrade Services\".\n        - In the Release_Notes.html, find the memory address that corresponds to your device for the stm32wb5x_FUS_fw.bin file\n        - Select that file, the memory address, \"verify download\", and then \"Firmware Upgrade\".\n        - Once complete, in the Release_Notes.html, find the memory address that corresponds to your device for the\n          stm32wb5x_BLE_Mac_802_15_4_fw.bin file. It should not be the same memory address.\n        - Select that file, the memory address, \"verify download\", and then \"Firmware Upgrade\".\n        - Select \"Start Wireless Stack\".\n        - Disconnect from the device.\n        - Run this example.\n\n        Note: extended stack versions are not supported at this time. Do not attempt to install a stack with \"extended\" in the name.\n    */\n\n    let mut config = embassy_stm32::Config::default();\n    config.rcc = WPAN_DEFAULT;\n    let p = embassy_stm32::init(config);\n    info!(\"Hello World!\");\n\n    let config = Config::default();\n    let mbox = TlMbox::init(p.IPCC, Irqs, config).await.unwrap();\n    let mut sys = mbox.sys_subsystem;\n    let mut ble = mbox.ble_subsystem;\n\n    spawner.spawn(run_mm_queue(mbox.mm_subsystem).unwrap());\n\n    let _ = sys.shci_c2_ble_init(Default::default()).await;\n\n    info!(\"resetting BLE...\");\n    ble.reset().await;\n    let response = ble.read().await;\n    defmt::debug!(\"{}\", response);\n\n    info!(\"config public address...\");\n    ble.write_config_data(&ConfigData::public_address(get_bd_addr()).build())\n        .await;\n    let response = ble.read().await;\n    defmt::debug!(\"{}\", response);\n\n    info!(\"config random address...\");\n    ble.write_config_data(&ConfigData::random_address(get_random_addr()).build())\n        .await;\n    let response = ble.read().await;\n    defmt::debug!(\"{}\", response);\n\n    info!(\"config identity root...\");\n    ble.write_config_data(&ConfigData::identity_root(&get_irk()).build())\n        .await;\n    let response = ble.read().await;\n    defmt::debug!(\"{}\", response);\n\n    info!(\"config encryption root...\");\n    ble.write_config_data(&ConfigData::encryption_root(&get_erk()).build())\n        .await;\n    let response = ble.read().await;\n    defmt::debug!(\"{}\", response);\n\n    info!(\"config tx power level...\");\n    ble.set_tx_power_level(PowerLevel::ZerodBm).await;\n    let response = ble.read().await;\n    defmt::debug!(\"{}\", response);\n\n    info!(\"GATT init...\");\n    ble.init_gatt().await;\n    let response = ble.read().await;\n    defmt::debug!(\"{}\", response);\n\n    info!(\"GAP init...\");\n    ble.init_gap(Role::PERIPHERAL, false, BLE_GAP_DEVICE_NAME_LENGTH).await;\n    let response = ble.read().await;\n    defmt::debug!(\"{}\", response);\n\n    info!(\"set IO capabilities...\");\n    ble.set_io_capability(IoCapability::DisplayConfirm).await;\n    let response = ble.read().await;\n    defmt::debug!(\"{}\", response);\n\n    info!(\"set authentication requirements...\");\n    ble.set_authentication_requirement(&AuthenticationRequirements {\n        bonding_required: false,\n        keypress_notification_support: false,\n        mitm_protection_required: false,\n        encryption_key_size_range: (8, 16),\n        fixed_pin: Pin::Requested,\n        identity_address_type: AddressType::Public,\n        secure_connection_support: SecureConnectionSupport::Optional,\n    })\n    .await\n    .unwrap();\n    let response = ble.read().await;\n    defmt::debug!(\"{}\", response);\n\n    info!(\"set scan response data...\");\n    ble.le_set_scan_response_data(b\"TXTX\").await.unwrap();\n    let response = ble.read().await;\n    defmt::debug!(\"{}\", response);\n\n    defmt::info!(\"initializing services and characteristics...\");\n    let mut ble_context = init_gatt_services(&mut ble).await.unwrap();\n    defmt::info!(\"{}\", ble_context);\n\n    let discovery_params = DiscoverableParameters {\n        advertising_type: AdvertisingType::ConnectableUndirected,\n        advertising_interval: Some((Duration::from_millis(100), Duration::from_millis(100))),\n        address_type: OwnAddressType::Public,\n        filter_policy: AdvertisingFilterPolicy::AllowConnectionAndScan,\n        local_name: Some(LocalName::Complete(b\"TXTX\")),\n        advertising_data: &[],\n        conn_interval: (None, None),\n    };\n\n    info!(\"set discoverable...\");\n    ble.set_discoverable(&discovery_params).await.unwrap();\n    let response = ble.read().await;\n    defmt::debug!(\"{}\", response);\n\n    loop {\n        let response = ble.read().await;\n        defmt::debug!(\"{}\", response);\n\n        if let Ok(Packet::Event(event)) = response {\n            match event {\n                Event::LeConnectionComplete(_) => {\n                    defmt::info!(\"connected\");\n                }\n                Event::DisconnectionComplete(_) => {\n                    defmt::info!(\"disconnected\");\n                    ble_context.is_subscribed = false;\n                    ble.set_discoverable(&discovery_params).await.unwrap();\n                }\n                Event::Vendor(vendor_event) => match vendor_event {\n                    VendorEvent::AttReadPermitRequest(read_req) => {\n                        defmt::info!(\"read request received {}, allowing\", read_req);\n                        ble.allow_read(read_req.conn_handle).await\n                    }\n                    VendorEvent::AttWritePermitRequest(write_req) => {\n                        defmt::info!(\"write request received {}, allowing\", write_req);\n                        ble.write_response(&WriteResponseParameters {\n                            conn_handle: write_req.conn_handle,\n                            attribute_handle: write_req.attribute_handle,\n                            status: Ok(()),\n                            value: write_req.value(),\n                        })\n                        .await\n                        .unwrap()\n                    }\n                    VendorEvent::GattAttributeModified(attribute) => {\n                        defmt::info!(\"{}\", ble_context);\n                        if attribute.attr_handle.0 == ble_context.chars.notify.0 + 2 {\n                            if attribute.data()[0] == 0x01 {\n                                defmt::info!(\"subscribed\");\n                                ble_context.is_subscribed = true;\n                            } else {\n                                defmt::info!(\"unsubscribed\");\n                                ble_context.is_subscribed = false;\n                            }\n                        }\n                    }\n                    _ => {}\n                },\n                _ => {}\n            }\n        }\n    }\n}\n\n#[embassy_executor::task]\nasync fn run_mm_queue(mut memory_manager: mm::MemoryManager<'static>) {\n    memory_manager.run_queue().await;\n}\n\nfn get_bd_addr() -> BdAddr {\n    let mut bytes = [0u8; 6];\n\n    let lhci_info = LhciC1DeviceInformationCcrp::new();\n    bytes[0] = (lhci_info.uid64 & 0xff) as u8;\n    bytes[1] = ((lhci_info.uid64 >> 8) & 0xff) as u8;\n    bytes[2] = ((lhci_info.uid64 >> 16) & 0xff) as u8;\n    bytes[3] = lhci_info.device_type_id;\n    bytes[4] = (lhci_info.st_company_id & 0xff) as u8;\n    bytes[5] = (lhci_info.st_company_id >> 8 & 0xff) as u8;\n\n    BdAddr(bytes)\n}\n\nfn get_random_addr() -> BdAddr {\n    let mut bytes = [0u8; 6];\n\n    let lhci_info = LhciC1DeviceInformationCcrp::new();\n    bytes[0] = (lhci_info.uid64 & 0xff) as u8;\n    bytes[1] = ((lhci_info.uid64 >> 8) & 0xff) as u8;\n    bytes[2] = ((lhci_info.uid64 >> 16) & 0xff) as u8;\n    bytes[3] = 0;\n    bytes[4] = 0x6E;\n    bytes[5] = 0xED;\n\n    BdAddr(bytes)\n}\n\nconst BLE_CFG_IRK: [u8; 16] = [\n    0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0,\n];\nconst BLE_CFG_ERK: [u8; 16] = [\n    0xfe, 0xdc, 0xba, 0x09, 0x87, 0x65, 0x43, 0x21, 0xfe, 0xdc, 0xba, 0x09, 0x87, 0x65, 0x43, 0x21,\n];\n\nfn get_irk() -> EncryptionKey {\n    EncryptionKey(BLE_CFG_IRK)\n}\n\nfn get_erk() -> EncryptionKey {\n    EncryptionKey(BLE_CFG_ERK)\n}\n\n#[derive(defmt::Format)]\npub struct BleContext {\n    pub service_handle: AttributeHandle,\n    pub chars: CharHandles,\n    pub is_subscribed: bool,\n}\n\n#[derive(defmt::Format)]\npub struct CharHandles {\n    pub read: AttributeHandle,\n    pub write: AttributeHandle,\n    pub notify: AttributeHandle,\n}\n\npub async fn init_gatt_services<'a>(ble_subsystem: &mut Ble<'a>) -> Result<BleContext, ()> {\n    let service_handle = gatt_add_service(ble_subsystem, Uuid::Uuid16(0x500)).await?;\n\n    let read = gatt_add_char(\n        ble_subsystem,\n        service_handle,\n        Uuid::Uuid16(0x501),\n        CharacteristicProperty::READ,\n        Some(b\"Hello from embassy!\"),\n    )\n    .await?;\n\n    let write = gatt_add_char(\n        ble_subsystem,\n        service_handle,\n        Uuid::Uuid16(0x502),\n        CharacteristicProperty::WRITE_WITHOUT_RESPONSE | CharacteristicProperty::WRITE | CharacteristicProperty::READ,\n        None,\n    )\n    .await?;\n\n    let notify = gatt_add_char(\n        ble_subsystem,\n        service_handle,\n        Uuid::Uuid16(0x503),\n        CharacteristicProperty::NOTIFY | CharacteristicProperty::READ,\n        None,\n    )\n    .await?;\n\n    Ok(BleContext {\n        service_handle,\n        is_subscribed: false,\n        chars: CharHandles { read, write, notify },\n    })\n}\n\nasync fn gatt_add_service<'a>(ble_subsystem: &mut Ble<'a>, uuid: Uuid) -> Result<AttributeHandle, ()> {\n    ble_subsystem\n        .add_service(&AddServiceParameters {\n            uuid,\n            service_type: ServiceType::Primary,\n            max_attribute_records: 8,\n        })\n        .await;\n    let response = ble_subsystem.read().await;\n    defmt::debug!(\"{}\", response);\n\n    if let Ok(Packet::Event(Event::CommandComplete(CommandComplete {\n        return_params:\n            ReturnParameters::Vendor(VendorReturnParameters::GattAddService(event::command::GattService {\n                service_handle,\n                ..\n            })),\n        ..\n    }))) = response\n    {\n        Ok(service_handle)\n    } else {\n        Err(())\n    }\n}\n\nasync fn gatt_add_char<'a>(\n    ble_subsystem: &mut Ble<'a>,\n    service_handle: AttributeHandle,\n    characteristic_uuid: Uuid,\n    characteristic_properties: CharacteristicProperty,\n    default_value: Option<&[u8]>,\n) -> Result<AttributeHandle, ()> {\n    ble_subsystem\n        .add_characteristic(&AddCharacteristicParameters {\n            service_handle,\n            characteristic_uuid,\n            characteristic_properties,\n            characteristic_value_len: 32,\n            security_permissions: CharacteristicPermission::empty(),\n            gatt_event_mask: CharacteristicEvent::all(),\n            encryption_key_size: EncryptionKeySize::with_value(7).unwrap(),\n            is_variable: true,\n        })\n        .await;\n    let response = ble_subsystem.read().await;\n    defmt::debug!(\"{}\", response);\n\n    if let Ok(Packet::Event(Event::CommandComplete(CommandComplete {\n        return_params:\n            ReturnParameters::Vendor(VendorReturnParameters::GattAddCharacteristic(event::command::GattCharacteristic {\n                characteristic_handle,\n                ..\n            })),\n        ..\n    }))) = response\n    {\n        if let Some(value) = default_value {\n            ble_subsystem\n                .update_characteristic_value(&UpdateCharacteristicValueParameters {\n                    service_handle,\n                    characteristic_handle,\n                    offset: 0,\n                    value,\n                })\n                .await\n                .unwrap();\n\n            let response = ble_subsystem.read().await;\n            defmt::debug!(\"{}\", response);\n        }\n        Ok(characteristic_handle)\n    } else {\n        Err(())\n    }\n}\n"
  },
  {
    "path": "examples/stm32wb/src/bin/mac_ffd.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_stm32::bind_interrupts;\nuse embassy_stm32::ipcc::{Config, ReceiveInterruptHandler, TransmitInterruptHandler};\nuse embassy_stm32::rcc::WPAN_DEFAULT;\nuse embassy_stm32_wpan::TlMbox;\nuse embassy_stm32_wpan::mac::commands::{AssociateResponse, ResetRequest, SetRequest, StartRequest};\nuse embassy_stm32_wpan::mac::event::MacEvent;\nuse embassy_stm32_wpan::mac::typedefs::{MacChannel, MacStatus, PanId, PibId, SecurityLevel};\nuse embassy_stm32_wpan::sub::mm;\nuse {defmt_rtt as _, panic_probe as _};\n\nbind_interrupts!(struct Irqs{\n    IPCC_C1_RX => ReceiveInterruptHandler;\n    IPCC_C1_TX => TransmitInterruptHandler;\n});\n\n#[embassy_executor::task]\nasync fn run_mm_queue(mut memory_manager: mm::MemoryManager<'static>) {\n    memory_manager.run_queue().await;\n}\n\n#[embassy_executor::main(executor = \"embassy_stm32::executor::Executor\", entry = \"cortex_m_rt::entry\")]\nasync fn main(spawner: Spawner) {\n    /*\n        How to make this work:\n\n        - Obtain a NUCLEO-STM32WB55 from your preferred supplier.\n        - Download and Install STM32CubeProgrammer.\n        - Download stm32wb5x_FUS_fw.bin, stm32wb5x_BLE_Mac_802_15_4_fw.bin, and Release_Notes.html from\n          gh:STMicroelectronics/STM32CubeWB@2234d97/Projects/STM32WB_Copro_Wireless_Binaries/STM32WB5x\n        - Open STM32CubeProgrammer\n        - On the right-hand pane, click \"firmware upgrade\" to upgrade the st-link firmware.\n        - Once complete, click connect to connect to the device.\n        - On the left hand pane, click the RSS signal icon to open \"Firmware Upgrade Services\".\n        - In the Release_Notes.html, find the memory address that corresponds to your device for the stm32wb5x_FUS_fw.bin file\n        - Select that file, the memory address, \"verify download\", and then \"Firmware Upgrade\".\n        - Once complete, in the Release_Notes.html, find the memory address that corresponds to your device for the\n          stm32wb5x_BLE_Mac_802_15_4_fw.bin file. It should not be the same memory address.\n        - Select that file, the memory address, \"verify download\", and then \"Firmware Upgrade\".\n        - Select \"Start Wireless Stack\".\n        - Disconnect from the device.\n        - Run this example.\n\n        Note: extended stack versions are not supported at this time. Do not attempt to install a stack with \"extended\" in the name.\n    */\n\n    let mut config = embassy_stm32::Config::default();\n    config.rcc = WPAN_DEFAULT;\n    let p = embassy_stm32::init(config);\n    info!(\"Hello World!\");\n\n    let config = Config::default();\n    let mbox = TlMbox::init(p.IPCC, Irqs, config).await.unwrap();\n    let mut sys = mbox.sys_subsystem;\n\n    spawner.spawn(run_mm_queue(mbox.mm_subsystem).unwrap());\n\n    let result = sys.shci_c2_mac_802_15_4_init().await;\n    info!(\"initialized mac: {}\", result);\n\n    let (mut mac_rx, mut mac_tx) = mbox.mac_subsystem.split();\n\n    info!(\"resetting\");\n    mac_tx\n        .send_command(&ResetRequest {\n            set_default_pib: true,\n            ..Default::default()\n        })\n        .await\n        .unwrap();\n    defmt::info!(\"{:#x}\", mac_rx.read().await.unwrap());\n\n    info!(\"setting extended address\");\n    let extended_address: u64 = 0xACDE480000000001;\n    mac_tx\n        .send_command(&SetRequest {\n            pib_attribute_ptr: &extended_address as *const _ as *const u8,\n            pib_attribute: PibId::ExtendedAddress,\n        })\n        .await\n        .unwrap();\n    defmt::info!(\"{:#x}\", mac_rx.read().await.unwrap());\n\n    info!(\"setting short address\");\n    let short_address: u16 = 0x1122;\n    mac_tx\n        .send_command(&SetRequest {\n            pib_attribute_ptr: &short_address as *const _ as *const u8,\n            pib_attribute: PibId::ShortAddress,\n        })\n        .await\n        .unwrap();\n    defmt::info!(\"{:#x}\", mac_rx.read().await.unwrap());\n\n    info!(\"setting association permit\");\n    let association_permit: bool = true;\n    mac_tx\n        .send_command(&SetRequest {\n            pib_attribute_ptr: &association_permit as *const _ as *const u8,\n            pib_attribute: PibId::AssociationPermit,\n        })\n        .await\n        .unwrap();\n    defmt::info!(\"{:#x}\", mac_rx.read().await.unwrap());\n\n    info!(\"setting TX power\");\n    let transmit_power: i8 = 2;\n    mac_tx\n        .send_command(&SetRequest {\n            pib_attribute_ptr: &transmit_power as *const _ as *const u8,\n            pib_attribute: PibId::TransmitPower,\n        })\n        .await\n        .unwrap();\n    defmt::info!(\"{:#x}\", mac_rx.read().await.unwrap());\n\n    info!(\"starting FFD device\");\n    mac_tx\n        .send_command(&StartRequest {\n            pan_id: PanId([0x1A, 0xAA]),\n            channel_number: MacChannel::Channel16,\n            beacon_order: 0x0F,\n            superframe_order: 0x0F,\n            pan_coordinator: true,\n            battery_life_extension: false,\n            ..Default::default()\n        })\n        .await\n        .unwrap();\n    defmt::info!(\"{:#x}\", mac_rx.read().await.unwrap());\n\n    info!(\"setting RX on when idle\");\n    let rx_on_while_idle: bool = true;\n    mac_tx\n        .send_command(&SetRequest {\n            pib_attribute_ptr: &rx_on_while_idle as *const _ as *const u8,\n            pib_attribute: PibId::RxOnWhenIdle,\n        })\n        .await\n        .unwrap();\n    defmt::info!(\"{:#x}\", mac_rx.read().await.unwrap());\n\n    loop {\n        let evt = mac_rx.read().await;\n        if let Ok(evt) = evt {\n            defmt::info!(\"parsed mac event\");\n            defmt::info!(\"{:#x}\", evt);\n\n            match evt {\n                MacEvent::MlmeAssociateInd(association) => mac_tx\n                    .send_command(&AssociateResponse {\n                        device_address: association.device_address,\n                        assoc_short_address: [0x33, 0x44],\n                        status: MacStatus::Success,\n                        security_level: SecurityLevel::Unsecure,\n                        ..Default::default()\n                    })\n                    .await\n                    .unwrap(),\n                MacEvent::McpsDataInd(data_ind) => {\n                    let payload = data_ind.payload();\n                    let ref_payload = b\"Hello from embassy!\";\n                    info!(\"{}\", payload);\n\n                    if payload == ref_payload {\n                        info!(\"success\");\n                    } else {\n                        info!(\"ref payload: {}\", ref_payload);\n                    }\n                }\n                _ => {\n                    defmt::info!(\"other mac event\");\n                }\n            }\n        } else {\n            defmt::info!(\"failed to parse mac event\");\n        }\n    }\n}\n"
  },
  {
    "path": "examples/stm32wb/src/bin/mac_ffd_net.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse core::net::Ipv6Addr;\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_net::udp::{PacketMetadata, UdpSocket};\nuse embassy_net::{Ipv6Cidr, StackResources, StaticConfigV6};\nuse embassy_stm32::bind_interrupts;\nuse embassy_stm32::ipcc::{Config, ReceiveInterruptHandler, TransmitInterruptHandler};\nuse embassy_stm32::peripherals::RNG;\nuse embassy_stm32::rcc::WPAN_DEFAULT;\nuse embassy_stm32::rng::InterruptHandler as RngInterruptHandler;\nuse embassy_stm32_wpan::TlMbox;\nuse embassy_stm32_wpan::mac::{Driver, DriverState, Runner};\nuse embassy_stm32_wpan::sub::mm;\nuse embassy_time::{Duration, Timer};\nuse heapless::Vec;\nuse static_cell::StaticCell;\nuse {defmt_rtt as _, panic_probe as _};\n\nbind_interrupts!(struct Irqs{\n    IPCC_C1_RX => ReceiveInterruptHandler;\n    IPCC_C1_TX => TransmitInterruptHandler;\n    RNG => RngInterruptHandler<RNG>;\n});\n\n#[embassy_executor::task]\nasync fn run_mm_queue(mut memory_manager: mm::MemoryManager<'static>) -> ! {\n    memory_manager.run_queue().await\n}\n\n#[embassy_executor::task]\nasync fn run_mac(runner: &'static Runner<'static>) -> ! {\n    runner.run().await\n}\n\n#[embassy_executor::task]\nasync fn run_net(mut runner: embassy_net::Runner<'static, Driver<'static>>) -> ! {\n    runner.run().await\n}\n\n#[embassy_executor::main(executor = \"embassy_stm32::executor::Executor\", entry = \"cortex_m_rt::entry\")]\nasync fn main(spawner: Spawner) {\n    /*\n        How to make this work:\n\n        - Obtain a NUCLEO-STM32WB55 from your preferred supplier.\n        - Download and Install STM32CubeProgrammer.\n        - Download stm32wb5x_FUS_fw.bin, stm32wb5x_BLE_Mac_802_15_4_fw.bin, and Release_Notes.html from\n          gh:STMicroelectronics/STM32CubeWB@2234d97/Projects/STM32WB_Copro_Wireless_Binaries/STM32WB5x\n        - Open STM32CubeProgrammer\n        - On the right-hand pane, click \"firmware upgrade\" to upgrade the st-link firmware.\n        - Once complete, click connect to connect to the device.\n        - On the left hand pane, click the RSS signal icon to open \"Firmware Upgrade Services\".\n        - In the Release_Notes.html, find the memory address that corresponds to your device for the stm32wb5x_FUS_fw.bin file\n        - Select that file, the memory address, \"verify download\", and then \"Firmware Upgrade\".\n        - Once complete, in the Release_Notes.html, find the memory address that corresponds to your device for the\n          stm32wb5x_BLE_Mac_802_15_4_fw.bin file. It should not be the same memory address.\n        - Select that file, the memory address, \"verify download\", and then \"Firmware Upgrade\".\n        - Select \"Start Wireless Stack\".\n        - Disconnect from the device.\n        - Run this example.\n\n        Note: extended stack versions are not supported at this time. Do not attempt to install a stack with \"extended\" in the name.\n    */\n\n    let mut config = embassy_stm32::Config::default();\n    config.rcc = WPAN_DEFAULT;\n    let p = embassy_stm32::init(config);\n    info!(\"Hello World!\");\n\n    let config = Config::default();\n    let mut mbox = TlMbox::init(p.IPCC, Irqs, config).await.unwrap();\n\n    spawner.spawn(run_mm_queue(mbox.mm_subsystem).unwrap());\n\n    let result = mbox.sys_subsystem.shci_c2_mac_802_15_4_init().await;\n    info!(\"initialized mac: {}\", result);\n\n    static DRIVER_STATE: StaticCell<DriverState> = StaticCell::new();\n    static RUNNER: StaticCell<Runner> = StaticCell::new();\n    static RESOURCES: StaticCell<StackResources<3>> = StaticCell::new();\n\n    let driver_state = DRIVER_STATE.init(DriverState::new(mbox.mac_subsystem));\n\n    let (driver, mac_runner, mut control) = Driver::new(\n        driver_state,\n        0x1122u16.to_be_bytes().try_into().unwrap(),\n        0xACDE480000000001u64.to_be_bytes().try_into().unwrap(),\n    );\n\n    // TODO: rng does not work for some reason\n    // Generate random seed.\n    // let mut rng = Rng::new(p.RNG, Irqs);\n    let seed = [0; 8];\n    // let _ = rng.async_fill_bytes(&mut seed).await;\n    let seed = u64::from_le_bytes(seed);\n\n    info!(\"seed generated\");\n\n    // Init network stack\n    let ipv6_addr = Ipv6Addr::new(0, 0, 0, 0, 0, 0xffff, 0xc00a, 0x2ff);\n\n    let config = embassy_net::Config::ipv6_static(StaticConfigV6 {\n        address: Ipv6Cidr::new(ipv6_addr, 104),\n        gateway: None,\n        dns_servers: Vec::new(),\n    });\n\n    let (stack, eth_runner) = embassy_net::new(driver, config, RESOURCES.init(StackResources::new()), seed);\n\n    // wpan runner\n    spawner.spawn(run_mac(RUNNER.init(mac_runner)).unwrap());\n\n    // Launch network task\n    spawner.spawn(unwrap!(run_net(eth_runner)));\n\n    info!(\"Network task initialized\");\n\n    control.init_link([0x1A, 0xAA]).await;\n\n    // Ensure DHCP configuration is up before trying connect\n    stack.wait_config_up().await;\n\n    info!(\"Network up\");\n\n    // Then we can use it!\n    let mut rx_meta = [PacketMetadata::EMPTY];\n    let mut rx_buffer = [0; 4096];\n    let mut tx_meta = [PacketMetadata::EMPTY];\n    let mut tx_buffer = [0; 4096];\n\n    let mut socket = UdpSocket::new(stack, &mut rx_meta, &mut rx_buffer, &mut tx_meta, &mut tx_buffer);\n\n    let remote_endpoint = (Ipv6Addr::new(0, 0, 0, 0, 0, 0xffff, 0xc00a, 0x2fb), 8000);\n\n    let send_buf = [0u8; 20];\n\n    socket.bind((ipv6_addr, 8000)).unwrap();\n    socket.send_to(&send_buf, remote_endpoint).await.unwrap();\n\n    Timer::after(Duration::from_secs(2)).await;\n\n    cortex_m::asm::bkpt();\n}\n"
  },
  {
    "path": "examples/stm32wb/src/bin/mac_rfd.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_stm32::bind_interrupts;\nuse embassy_stm32::ipcc::{Config, ReceiveInterruptHandler, TransmitInterruptHandler};\nuse embassy_stm32::rcc::WPAN_DEFAULT;\nuse embassy_stm32_wpan::TlMbox;\nuse embassy_stm32_wpan::mac::commands::{AssociateRequest, DataRequest, GetRequest, ResetRequest, SetRequest};\nuse embassy_stm32_wpan::mac::event::MacEvent;\nuse embassy_stm32_wpan::mac::typedefs::{\n    AddressMode, Capabilities, KeyIdMode, MacAddress, MacChannel, PanId, PibId, SecurityLevel,\n};\nuse embassy_stm32_wpan::sub::mm;\nuse {defmt_rtt as _, panic_probe as _};\n\nbind_interrupts!(struct Irqs{\n    IPCC_C1_RX => ReceiveInterruptHandler;\n    IPCC_C1_TX => TransmitInterruptHandler;\n});\n\n#[embassy_executor::task]\nasync fn run_mm_queue(mut memory_manager: mm::MemoryManager<'static>) {\n    memory_manager.run_queue().await;\n}\n\n#[embassy_executor::main(executor = \"embassy_stm32::executor::Executor\", entry = \"cortex_m_rt::entry\")]\nasync fn main(spawner: Spawner) {\n    /*\n        How to make this work:\n\n        - Obtain a NUCLEO-STM32WB55 from your preferred supplier.\n        - Download and Install STM32CubeProgrammer.\n        - Download stm32wb5x_FUS_fw.bin, stm32wb5x_BLE_Mac_802_15_4_fw.bin, and Release_Notes.html from\n          gh:STMicroelectronics/STM32CubeWB@2234d97/Projects/STM32WB_Copro_Wireless_Binaries/STM32WB5x\n        - Open STM32CubeProgrammer\n        - On the right-hand pane, click \"firmware upgrade\" to upgrade the st-link firmware.\n        - Once complete, click connect to connect to the device.\n        - On the left hand pane, click the RSS signal icon to open \"Firmware Upgrade Services\".\n        - In the Release_Notes.html, find the memory address that corresponds to your device for the stm32wb5x_FUS_fw.bin file\n        - Select that file, the memory address, \"verify download\", and then \"Firmware Upgrade\".\n        - Once complete, in the Release_Notes.html, find the memory address that corresponds to your device for the\n          stm32wb5x_BLE_Mac_802_15_4_fw.bin file. It should not be the same memory address.\n        - Select that file, the memory address, \"verify download\", and then \"Firmware Upgrade\".\n        - Select \"Start Wireless Stack\".\n        - Disconnect from the device.\n        - Run this example.\n\n        Note: extended stack versions are not supported at this time. Do not attempt to install a stack with \"extended\" in the name.\n    */\n\n    let mut config = embassy_stm32::Config::default();\n    config.rcc = WPAN_DEFAULT;\n    let p = embassy_stm32::init(config);\n    info!(\"Hello World!\");\n\n    let config = Config::default();\n    let mbox = TlMbox::init(p.IPCC, Irqs, config).await.unwrap();\n    let mut sys = mbox.sys_subsystem;\n\n    spawner.spawn(run_mm_queue(mbox.mm_subsystem).unwrap());\n\n    let result = sys.shci_c2_mac_802_15_4_init().await;\n    info!(\"initialized mac: {}\", result);\n\n    let (mut mac_rx, mut mac_tx) = mbox.mac_subsystem.split();\n\n    info!(\"resetting\");\n    mac_tx\n        .send_command(&ResetRequest {\n            set_default_pib: true,\n            ..Default::default()\n        })\n        .await\n        .unwrap();\n    defmt::info!(\"{:#x}\", mac_rx.read().await.unwrap());\n\n    info!(\"setting extended address\");\n    let extended_address: u64 = 0xACDE480000000002;\n    mac_tx\n        .send_command(&SetRequest {\n            pib_attribute_ptr: &extended_address as *const _ as *const u8,\n            pib_attribute: PibId::ExtendedAddress,\n        })\n        .await\n        .unwrap();\n    defmt::info!(\"{:#x}\", mac_rx.read().await.unwrap());\n\n    info!(\"getting extended address\");\n    mac_tx\n        .send_command(&GetRequest {\n            pib_attribute: PibId::ExtendedAddress,\n            ..Default::default()\n        })\n        .await\n        .unwrap();\n\n    {\n        let evt = mac_rx.read().await.unwrap();\n        info!(\"{:#x}\", evt);\n\n        if let MacEvent::MlmeGetCnf(evt) = evt {\n            if evt.pib_attribute_value_len == 8 {\n                let value = unsafe { core::ptr::read_unaligned(evt.pib_attribute_value_ptr as *const u64) };\n\n                info!(\"value {:#x}\", value)\n            }\n        }\n    }\n\n    info!(\"assocation request\");\n    let a = AssociateRequest {\n        channel_number: MacChannel::Channel16,\n        channel_page: 0,\n        coord_addr_mode: AddressMode::Short,\n        coord_address: MacAddress { short: [34, 17] },\n        capability_information: Capabilities::ALLOCATE_ADDRESS,\n        coord_pan_id: PanId([0x1A, 0xAA]),\n        security_level: SecurityLevel::Unsecure,\n        key_id_mode: KeyIdMode::Implicite,\n        key_source: [0; 8],\n        key_index: 152,\n    };\n    info!(\"{}\", a);\n    mac_tx.send_command(&a).await.unwrap();\n    let short_addr = {\n        let evt = mac_rx.read().await.unwrap();\n        info!(\"{:#x}\", evt);\n\n        if let MacEvent::MlmeAssociateCnf(conf) = evt {\n            conf.assoc_short_address\n        } else {\n            defmt::panic!()\n        }\n    };\n\n    info!(\"setting short address\");\n    mac_tx\n        .send_command(&SetRequest {\n            pib_attribute_ptr: &short_addr as *const _ as *const u8,\n            pib_attribute: PibId::ShortAddress,\n        })\n        .await\n        .unwrap();\n    {\n        let evt = mac_rx.read().await.unwrap();\n        info!(\"{:#x}\", evt);\n    }\n\n    info!(\"sending data\");\n    let data = b\"Hello from embassy!\";\n    mac_tx\n        .send_command(\n            DataRequest {\n                src_addr_mode: AddressMode::Short,\n                dst_addr_mode: AddressMode::Short,\n                dst_pan_id: PanId([0x1A, 0xAA]),\n                dst_address: MacAddress::BROADCAST,\n                msdu_handle: 0x02,\n                ack_tx: 0x00,\n                gts_tx: false,\n                security_level: SecurityLevel::Unsecure,\n                ..Default::default()\n            }\n            .set_buffer(data),\n        )\n        .await\n        .unwrap();\n    {\n        let evt = mac_rx.read().await.unwrap();\n        info!(\"{:#x}\", evt);\n    }\n\n    loop {\n        match mac_rx.read().await {\n            Ok(evt) => info!(\"{:#x}\", evt),\n            _ => continue,\n        };\n    }\n}\n"
  },
  {
    "path": "examples/stm32wb/src/bin/tl_mbox.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_stm32::bind_interrupts;\nuse embassy_stm32::ipcc::{Config, ReceiveInterruptHandler, TransmitInterruptHandler};\nuse embassy_stm32::rcc::WPAN_DEFAULT;\nuse embassy_stm32_wpan::TlMbox;\nuse embassy_time::Timer;\nuse {defmt_rtt as _, panic_probe as _};\n\nbind_interrupts!(struct Irqs{\n    IPCC_C1_RX => ReceiveInterruptHandler;\n    IPCC_C1_TX => TransmitInterruptHandler;\n});\n\n#[embassy_executor::main(executor = \"embassy_stm32::executor::Executor\", entry = \"cortex_m_rt::entry\")]\nasync fn main(_spawner: Spawner) {\n    /*\n        How to make this work:\n\n        - Obtain a NUCLEO-STM32WB55 from your preferred supplier.\n        - Download and Install STM32CubeProgrammer.\n        - Download stm32wb5x_FUS_fw.bin, stm32wb5x_BLE_Mac_802_15_4_fw.bin, and Release_Notes.html from\n          gh:STMicroelectronics/STM32CubeWB@2234d97/Projects/STM32WB_Copro_Wireless_Binaries/STM32WB5x\n        - Open STM32CubeProgrammer\n        - On the right-hand pane, click \"firmware upgrade\" to upgrade the st-link firmware.\n        - Once complete, click connect to connect to the device.\n        - On the left hand pane, click the RSS signal icon to open \"Firmware Upgrade Services\".\n        - In the Release_Notes.html, find the memory address that corresponds to your device for the stm32wb5x_FUS_fw.bin file\n        - Select that file, the memory address, \"verify download\", and then \"Firmware Upgrade\".\n        - Once complete, in the Release_Notes.html, find the memory address that corresponds to your device for the\n          stm32wb5x_BLE_Mac_802_15_4_fw.bin file. It should not be the same memory address.\n        - Select that file, the memory address, \"verify download\", and then \"Firmware Upgrade\".\n        - Select \"Start Wireless Stack\".\n        - Disconnect from the device.\n        - Run this example.\n\n        Note: extended stack versions are not supported at this time. Do not attempt to install a stack with \"extended\" in the name.\n    */\n\n    let mut config = embassy_stm32::Config::default();\n    config.rcc = WPAN_DEFAULT;\n    let p = embassy_stm32::init(config);\n    info!(\"Hello World!\");\n\n    let config = Config::default();\n    let mbox = TlMbox::init(p.IPCC, Irqs, config).await.unwrap();\n\n    loop {\n        let wireless_fw_info = mbox.sys_subsystem.wireless_fw_info();\n        match wireless_fw_info {\n            None => info!(\"not yet initialized\"),\n            Some(fw_info) => {\n                let version_major = fw_info.version_major();\n                let version_minor = fw_info.version_minor();\n                let subversion = fw_info.subversion();\n\n                let sram2a_size = fw_info.sram2a_size();\n                let sram2b_size = fw_info.sram2b_size();\n\n                info!(\n                    \"version {}.{}.{} - SRAM2a {} - SRAM2b {}\",\n                    version_major, version_minor, subversion, sram2a_size, sram2b_size\n                );\n\n                break;\n            }\n        }\n\n        Timer::after_millis(50).await;\n    }\n\n    info!(\"Test OK\");\n    cortex_m::asm::bkpt();\n}\n"
  },
  {
    "path": "examples/stm32wb/src/bin/tl_mbox_ble.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_stm32::bind_interrupts;\nuse embassy_stm32::ipcc::{Config, ReceiveInterruptHandler, TransmitInterruptHandler};\nuse embassy_stm32::rcc::WPAN_DEFAULT;\nuse embassy_stm32_wpan::TlMbox;\nuse embassy_stm32_wpan::sub::mm;\nuse {defmt_rtt as _, panic_probe as _};\n\nbind_interrupts!(struct Irqs{\n    IPCC_C1_RX => ReceiveInterruptHandler;\n    IPCC_C1_TX => TransmitInterruptHandler;\n});\n\n#[embassy_executor::task]\nasync fn run_mm_queue(mut memory_manager: mm::MemoryManager<'static>) {\n    memory_manager.run_queue().await;\n}\n\n#[embassy_executor::main(executor = \"embassy_stm32::executor::Executor\", entry = \"cortex_m_rt::entry\")]\nasync fn main(spawner: Spawner) {\n    /*\n        How to make this work:\n\n        - Obtain a NUCLEO-STM32WB55 from your preferred supplier.\n        - Download and Install STM32CubeProgrammer.\n        - Download stm32wb5x_FUS_fw.bin, stm32wb5x_BLE_Mac_802_15_4_fw.bin, and Release_Notes.html from\n          gh:STMicroelectronics/STM32CubeWB@2234d97/Projects/STM32WB_Copro_Wireless_Binaries/STM32WB5x\n        - Open STM32CubeProgrammer\n        - On the right-hand pane, click \"firmware upgrade\" to upgrade the st-link firmware.\n        - Once complete, click connect to connect to the device.\n        - On the left hand pane, click the RSS signal icon to open \"Firmware Upgrade Services\".\n        - In the Release_Notes.html, find the memory address that corresponds to your device for the stm32wb5x_FUS_fw.bin file\n        - Select that file, the memory address, \"verify download\", and then \"Firmware Upgrade\".\n        - Once complete, in the Release_Notes.html, find the memory address that corresponds to your device for the\n          stm32wb5x_BLE_Mac_802_15_4_fw.bin file. It should not be the same memory address.\n        - Select that file, the memory address, \"verify download\", and then \"Firmware Upgrade\".\n        - Select \"Start Wireless Stack\".\n        - Disconnect from the device.\n        - Run this example.\n\n        Note: extended stack versions are not supported at this time. Do not attempt to install a stack with \"extended\" in the name.\n    */\n\n    let mut config = embassy_stm32::Config::default();\n    config.rcc = WPAN_DEFAULT;\n    let p = embassy_stm32::init(config);\n    info!(\"Hello World!\");\n\n    let config = Config::default();\n    let mbox = TlMbox::init(p.IPCC, Irqs, config).await.unwrap();\n    let mut sys = mbox.sys_subsystem;\n    let mut ble = mbox.ble_subsystem;\n\n    spawner.spawn(run_mm_queue(mbox.mm_subsystem).unwrap());\n\n    let _ = sys.shci_c2_ble_init(Default::default()).await;\n\n    info!(\"starting ble...\");\n    ble.tl_write(0x0c, &[]).await;\n\n    info!(\"waiting for ble...\");\n    let ble_event = ble.tl_read().await;\n\n    info!(\"ble event: {}\", ble_event.payload());\n\n    info!(\"Test OK\");\n    cortex_m::asm::bkpt();\n}\n"
  },
  {
    "path": "examples/stm32wb/src/bin/tl_mbox_mac.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_stm32::bind_interrupts;\nuse embassy_stm32::ipcc::{Config, ReceiveInterruptHandler, TransmitInterruptHandler};\nuse embassy_stm32::rcc::WPAN_DEFAULT;\nuse embassy_stm32_wpan::TlMbox;\nuse embassy_stm32_wpan::sub::mm;\nuse {defmt_rtt as _, panic_probe as _};\n\nbind_interrupts!(struct Irqs{\n    IPCC_C1_RX => ReceiveInterruptHandler;\n    IPCC_C1_TX => TransmitInterruptHandler;\n});\n\n#[embassy_executor::task]\nasync fn run_mm_queue(mut memory_manager: mm::MemoryManager<'static>) {\n    memory_manager.run_queue().await;\n}\n\n#[embassy_executor::main(executor = \"embassy_stm32::executor::Executor\", entry = \"cortex_m_rt::entry\")]\nasync fn main(spawner: Spawner) {\n    /*\n        How to make this work:\n\n        - Obtain a NUCLEO-STM32WB55 from your preferred supplier.\n        - Download and Install STM32CubeProgrammer.\n        - Download stm32wb5x_FUS_fw.bin, stm32wb5x_BLE_Mac_802_15_4_fw.bin, and Release_Notes.html from\n          gh:STMicroelectronics/STM32CubeWB@2234d97/Projects/STM32WB_Copro_Wireless_Binaries/STM32WB5x\n        - Open STM32CubeProgrammer\n        - On the right-hand pane, click \"firmware upgrade\" to upgrade the st-link firmware.\n        - Once complete, click connect to connect to the device.\n        - On the left hand pane, click the RSS signal icon to open \"Firmware Upgrade Services\".\n        - In the Release_Notes.html, find the memory address that corresponds to your device for the stm32wb5x_FUS_fw.bin file\n        - Select that file, the memory address, \"verify download\", and then \"Firmware Upgrade\".\n        - Once complete, in the Release_Notes.html, find the memory address that corresponds to your device for the\n          stm32wb5x_BLE_Mac_802_15_4_fw.bin file. It should not be the same memory address.\n        - Select that file, the memory address, \"verify download\", and then \"Firmware Upgrade\".\n        - Select \"Start Wireless Stack\".\n        - Disconnect from the device.\n        - Run this example.\n\n        Note: extended stack versions are not supported at this time. Do not attempt to install a stack with \"extended\" in the name.\n    */\n\n    let mut config = embassy_stm32::Config::default();\n    config.rcc = WPAN_DEFAULT;\n    let p = embassy_stm32::init(config);\n    info!(\"Hello World!\");\n\n    let config = Config::default();\n    let mbox = TlMbox::init(p.IPCC, Irqs, config).await.unwrap();\n    let mut sys = mbox.sys_subsystem;\n\n    spawner.spawn(run_mm_queue(mbox.mm_subsystem).unwrap());\n\n    let result = sys.shci_c2_mac_802_15_4_init().await;\n    info!(\"initialized mac: {}\", result);\n\n    //\n    //    info!(\"starting ble...\");\n    //    mbox.ble_subsystem.t_write(0x0c, &[]).await;\n    //\n    //    info!(\"waiting for ble...\");\n    //    let ble_event = mbox.ble_subsystem.tl_read().await;\n    //\n    //    info!(\"ble event: {}\", ble_event.payload());\n\n    info!(\"Test OK\");\n    cortex_m::asm::bkpt();\n}\n"
  },
  {
    "path": "examples/stm32wba/.cargo/config.toml",
    "content": "[target.'cfg(all(target_arch = \"arm\", target_os = \"none\"))']\nrunner = \"probe-rs run --chip STM32WBA55CGUx\"\n\n[build]\ntarget = \"thumbv8m.main-none-eabihf\"\n\n[env]\nDEFMT_LOG = \"trace\"\n"
  },
  {
    "path": "examples/stm32wba/Cargo.toml",
    "content": "[package]\nedition = \"2024\"\nname = \"embassy-stm32wba-examples\"\nversion = \"0.1.0\"\nlicense = \"MIT OR Apache-2.0\"\npublish = false\n\n[dependencies]\nembassy-stm32 = { version = \"0.6.0\", path = \"../../embassy-stm32\", features = [ \"defmt\", \"stm32wba52cg\", \"time-driver-any\", \"memory-x\", \"exti\"]  }\nstm32-metapac = { version = \"21\", features = [\"stm32wba52cg\"] }\nembassy-stm32-wpan = { version = \"0.1.0\", path = \"../../embassy-stm32-wpan\", features = [\"defmt\", \"stm32wba52cg\", \"ble-stack-basic\", \"linklayer-ble-basic\"] }\nembassy-sync = { version = \"0.8.0\", path = \"../../embassy-sync\", features = [\"defmt\"] }\nembassy-executor = { version = \"0.10.0\", path = \"../../embassy-executor\", features = [\"platform-cortex-m\", \"executor-thread\", \"defmt\"] }\nembassy-time = { version = \"0.5.1\", path = \"../../embassy-time\", features = [\"defmt\", \"defmt-timestamp-uptime\", \"tick-hz-32_768\"] }\nembassy-net = { version = \"0.9.0\", path = \"../../embassy-net\", features = [\"defmt\", \"udp\", \"proto-ipv6\", \"medium-ieee802154\", ], optional = true }\n\ndefmt = \"1.0.1\"\ndefmt-rtt = \"1.0.0\"\n\ncortex-m = { version = \"0.7.6\", features = [\"inline-asm\", \"critical-section-single-core\"] }\ncortex-m-rt = \"0.7.0\"\nembedded-hal = \"1.0.0\"\npanic-probe = { version = \"1.0.0\", features = [\"print-defmt\"] }\nheapless = { version = \"0.9\", default-features = false }\nstatic_cell = \"2\"\n\n[features]\ndefault = [\"ble\"]\nmac = [\"embassy-stm32-wpan/wba_mac\", \"dep:embassy-net\"]\nble = [\"embassy-stm32-wpan/wba_ble\"]\n\n[[bin]]\nname = \"mac_ffd\"\nrequired-features = [\"mac\"]\n\n[[bin]]\nname = \"ble_advertiser\"\nrequired-features = [\"ble\"]\n\n[profile.release]\ndebug = 2\n\n[package.metadata.embassy]\nbuild = [\n  { target = \"thumbv8m.main-none-eabihf\", artifact-dir = \"out/examples/stm32wba\" }\n]\n"
  },
  {
    "path": "examples/stm32wba/build.rs",
    "content": "use std::error::Error;\n\nfn main() -> Result<(), Box<dyn Error>> {\n    println!(\"cargo:rustc-link-arg-bins=--nmagic\");\n    println!(\"cargo:rustc-link-arg-bins=-Tlink.x\");\n    println!(\"cargo:rustc-link-arg-bins=-Tdefmt.x\");\n\n    Ok(())\n}\n"
  },
  {
    "path": "examples/stm32wba/src/bin/adc.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_stm32::adc::{Adc, AdcChannel, SampleTime, adc4};\nuse embassy_stm32::{bind_interrupts, dma, peripherals};\nuse {defmt_rtt as _, panic_probe as _};\n\nbind_interrupts!(struct Irqs {\n    GPDMA1_CHANNEL1 => dma::InterruptHandler<peripherals::GPDMA1_CH1>;\n});\n\n#[embassy_executor::main]\nasync fn main(_spawner: embassy_executor::Spawner) {\n    let config = embassy_stm32::Config::default();\n\n    let mut p = embassy_stm32::init(config);\n\n    // **** ADC4 init ****\n    let mut adc4 = Adc::new_adc4(p.ADC4);\n    let mut adc4_pin1 = p.PA0; // A4\n    let mut adc4_pin2 = p.PA1; // A5\n    adc4.set_resolution_adc4(adc4::Resolution::BITS12);\n    adc4.set_averaging_adc4(adc4::Averaging::Samples256);\n\n    let max4 = adc4::resolution_to_max_count(adc4::Resolution::BITS12);\n\n    // **** ADC4 blocking read ****\n    let raw: u16 = adc4.blocking_read(&mut adc4_pin1, adc4::SampleTime::CYCLES1_5);\n    let volt: f32 = 3.0 * raw as f32 / max4 as f32;\n    info!(\"Read adc4 pin 1 {}\", volt);\n\n    let raw: u16 = adc4.blocking_read(&mut adc4_pin2, adc4::SampleTime::CYCLES1_5);\n    let volt: f32 = 3.3 * raw as f32 / max4 as f32;\n    info!(\"Read adc4 pin 2 {}\", volt);\n\n    // **** ADC4 async read ****\n    let mut degraded41 = adc4_pin1.degrade_adc();\n    let mut degraded42 = adc4_pin2.degrade_adc();\n    let mut measurements = [0u16; 2];\n\n    // The channels must be in ascending order and can't repeat for ADC4\n    adc4.read(\n        p.GPDMA1_CH1.reborrow(),\n        Irqs,\n        [\n            (&mut degraded42, SampleTime::CYCLES12_5),\n            (&mut degraded41, SampleTime::CYCLES12_5),\n        ]\n        .into_iter(),\n        &mut measurements,\n    )\n    .await;\n    let volt2: f32 = 3.3 * measurements[0] as f32 / max4 as f32;\n    let volt1: f32 = 3.0 * measurements[1] as f32 / max4 as f32;\n    info!(\"Async read 4 pin 1 {}\", volt1);\n    info!(\"Async read 4 pin 2 {}\", volt2);\n}\n"
  },
  {
    "path": "examples/stm32wba/src/bin/adc_ring_buffered.rs",
    "content": "//! ADC Ring Buffered Example - True Circular DMA for ADC4 Internal Channels\n//!\n//! This example demonstrates continuous ADC sampling using circular DMA with\n//! GPDMA linked-list mode. The ADC continuously samples internal channels\n//! (VREFINT, VCORE, Temperature) at 5000 samples/sec per channel.\n//!\n//! Temperature is calculated using factory calibration values (TS_CAL1, TS_CAL2)\n//! from the DESIG peripheral for accurate readings.\n//!\n//! Sample rate calculation (48 MHz ADC clock):\n//! - CYCLES12_5 sample time + 12.5 conversion = 25 cycles per raw sample\n//! - Samples128 averaging: 128 × 25 = 3200 cycles per averaged result\n//! - 3 channels: 3200 × 3 = 9600 cycles per complete set\n//! - Rate: 48 MHz / 9600 = 5000 sets/sec\n\n#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_stm32::adc::adc4::Calibration;\nuse embassy_stm32::adc::{Adc, AdcChannel, CONTINUOUS, RingBufferedAdc, adc4};\nuse embassy_stm32::peripherals::{ADC4, GPDMA1_CH1};\nuse embassy_stm32::rcc::{\n    AHB5Prescaler, AHBPrescaler, APBPrescaler, PllDiv, PllMul, PllPreDiv, PllSource, Sysclk, VoltageScale,\n};\nuse embassy_stm32::{Config, bind_interrupts, dma};\nuse {defmt_rtt as _, panic_probe as _};\n\n// DMA buffer size - must be large enough to prevent overruns\n// Buffer holds: [vrefint, vcore, temp, vrefint, vcore, temp, ...]\n// Size should be a multiple of number of channels (3) and large enough for processing\nconst DMA_BUF_LEN: usize = 3 * 256; // 256 samples per channel\n\nbind_interrupts!(struct Irqs {\n    GPDMA1_CHANNEL1 => dma::InterruptHandler<GPDMA1_CH1>;\n});\n\n#[embassy_executor::main]\nasync fn main(_spawner: embassy_executor::Spawner) {\n    // Configure RCC with PLL1 - required for ADC4 clock\n    let mut config = Config::default();\n    config.rcc.pll1 = Some(embassy_stm32::rcc::Pll {\n        source: PllSource::HSI,\n        prediv: PllPreDiv::DIV1,  // PLLM = 1 → HSI / 1 = 16 MHz\n        mul: PllMul::MUL30,       // PLLN = 30 → 16 MHz * 30 = 480 MHz VCO\n        divr: Some(PllDiv::DIV5), // PLLR = 5 → 96 MHz (Sysclk)\n        divq: None,\n        divp: Some(PllDiv::DIV30), // PLLP = 30 → 16 MHz (ADC4 clock source)\n        frac: Some(0),\n    });\n\n    config.rcc.ahb_pre = AHBPrescaler::DIV1;\n    config.rcc.apb1_pre = APBPrescaler::DIV1;\n    config.rcc.apb2_pre = APBPrescaler::DIV1;\n    config.rcc.apb7_pre = APBPrescaler::DIV1;\n    config.rcc.ahb5_pre = AHB5Prescaler::DIV4;\n    config.rcc.voltage_scale = VoltageScale::RANGE1;\n    config.rcc.sys = Sysclk::PLL1_R;\n\n    let p = embassy_stm32::init(config);\n\n    info!(\"STM32WBA ADC4 Ring Buffered Example - Circular DMA @ 5000 samples/sec with Calibrated Temperature\");\n\n    // Read factory calibration values\n    let calibration = Calibration::read();\n    info!(\n        \"Calibration values: TS_CAL1={} (30C), TS_CAL2={} (130C), VREFINT_CAL={}\",\n        calibration.ts_cal1, calibration.ts_cal2, calibration.vrefint_cal\n    );\n\n    // Initialize ADC4 with appropriate settings\n    // Samples128 averaging with CYCLES12_5 = 5000 samples/sec per channel\n    let mut adc = Adc::new_adc4(p.ADC4);\n    adc.set_resolution_adc4(adc4::Resolution::BITS12);\n    adc.set_averaging_adc4(adc4::Averaging::Samples128);\n\n    let max_count = adc4::resolution_to_max_count(adc4::Resolution::BITS12);\n\n    // Enable internal channels\n    let vrefint = adc.enable_vrefint_adc4();\n    let temperature = adc.enable_temperature_adc4();\n    let vcore = adc.enable_vcore_adc4();\n\n    // Degrade to AnyAdcChannel for use with DMA\n    // IMPORTANT: Order matters for ADC4 - must be ascending channel numbers\n    // VrefInt: Channel 0, VCORE: Channel 12, Temperature: Channel 13\n    let vrefint_ch = vrefint.degrade_adc();\n    let vcore_ch = vcore.degrade_adc();\n    let temp_ch = temperature.degrade_adc();\n\n    info!(\"Internal channels enabled, setting up ring buffer...\");\n\n    // Create DMA buffer - must be static for ring-buffered operation\n    static mut DMA_BUF: [u16; DMA_BUF_LEN] = [0u16; DMA_BUF_LEN];\n\n    // Create the ring-buffered ADC with continuous mode\n    // Channels must be in ascending order for ADC4\n    // CYCLES12_5 + Samples128 averaging = 5000 samples/sec per channel\n    let mut ring_adc: RingBufferedAdc<ADC4> = adc.into_ring_buffered(\n        p.GPDMA1_CH1,\n        unsafe { &mut *core::ptr::addr_of_mut!(DMA_BUF) },\n        Irqs,\n        [\n            (vrefint_ch, adc4::SampleTime::CYCLES12_5), // Channel 0\n            (vcore_ch, adc4::SampleTime::CYCLES12_5),   // Channel 12\n            (temp_ch, adc4::SampleTime::CYCLES12_5),    // Channel 13\n        ]\n        .into_iter(),\n        CONTINUOUS,\n    );\n\n    info!(\"Ring buffer configured, starting continuous sampling...\");\n\n    // Read buffer - must be half the size of DMA buffer\n    let mut measurements = [0u16; DMA_BUF_LEN / 2];\n\n    // Track iterations and accumulated values for periodic logging\n    let mut iteration_count: u32 = 0;\n    let mut accumulated_vrefint: u32 = 0;\n    let mut accumulated_vcore: u32 = 0;\n    let mut accumulated_temp: u32 = 0;\n    let mut total_samples: u32 = 0;\n\n    // Log every N iterations (~1 second at 5000 samples/sec)\n    // Half buffer = 128 sample sets, 5000/128 ≈ 39 reads/sec\n    const LOG_INTERVAL: u32 = 39;\n\n    loop {\n        // Read from ring buffer - this will return when half buffer is filled\n        // IMPORTANT: Read continuously without delay to prevent overruns\n        match ring_adc.read(&mut measurements).await {\n            Ok(_) => {\n                // Process measurements - they come in channel order repeated:\n                // [vrefint, vcore, temp, vrefint, vcore, temp, ...]\n                let num_samples = measurements.len() / 3;\n\n                // Accumulate samples for each channel\n                for i in 0..num_samples {\n                    accumulated_vrefint += measurements[i * 3] as u32;\n                    accumulated_vcore += measurements[i * 3 + 1] as u32;\n                    accumulated_temp += measurements[i * 3 + 2] as u32;\n                }\n                total_samples += num_samples as u32;\n\n                iteration_count += 1;\n\n                // Log periodically to avoid flooding output\n                if iteration_count >= LOG_INTERVAL {\n                    let vrefint_avg = accumulated_vrefint / total_samples;\n                    let vcore_avg = accumulated_vcore / total_samples;\n                    let temp_avg = accumulated_temp / total_samples;\n\n                    // Calculate actual VDDA and convert readings to millivolts\n                    let vdda_mv = calibration.calculate_vdda_mv(vrefint_avg);\n                    let vcore_mv = (vdda_mv * vcore_avg) / max_count;\n\n                    // Convert temperature using factory calibration with VDDA compensation\n                    let temp_mc = calibration.convert_to_millicelsius(temp_avg, vrefint_avg);\n                    let temp_c = temp_mc / 1000;\n                    let temp_frac = (temp_mc % 1000).unsigned_abs();\n\n                    info!(\n                        \"Averaged {} samples: VDDA={} mV | VCORE={} mV | Temp={}.{:03} C\",\n                        total_samples, vdda_mv, vcore_mv, temp_c, temp_frac\n                    );\n\n                    // Reset accumulators\n                    iteration_count = 0;\n                    accumulated_vrefint = 0;\n                    accumulated_vcore = 0;\n                    accumulated_temp = 0;\n                    total_samples = 0;\n                }\n            }\n            Err(_e) => {\n                warn!(\"DMA overrun error - consider increasing buffer size or reducing sample rate\");\n                // Reset accumulators on error\n                iteration_count = 0;\n                accumulated_vrefint = 0;\n                accumulated_vcore = 0;\n                accumulated_temp = 0;\n                total_samples = 0;\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "examples/stm32wba/src/bin/aes_cbc.rs",
    "content": "//! AES-CBC (Cipher Block Chaining) Mode Example\n//!\n//! Demonstrates AES encryption/decryption using CBC mode with initialization vectors.\n//!\n//! # Cipher Mode: CBC (Cipher Block Chaining)\n//! - Each plaintext block is XORed with previous ciphertext block before encryption\n//! - Requires initialization vector (IV) - must be random and unique per message\n//! - Requires 16-byte aligned data (padding necessary)\n//! - Common for file and disk encryption\n//!\n//! # What This Example Shows\n//! - CBC mode encryption/decryption with IV\n//! - Multi-block processing (streaming)\n//! - 128-bit and 256-bit keys\n//! - Processing data in multiple calls\n//! - Error handling for unaligned data\n//! - NIST test vector validation\n//!\n//! # Security Notes\n//! - IV must be random and unpredictable\n//! - Never reuse the same IV with the same key\n//! - CBC provides confidentiality but NOT authentication\n//! - Consider GCM mode for authenticated encryption\n\n#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_stm32::aes::{Aes, AesCbc, Direction};\nuse embassy_stm32::{bind_interrupts, peripherals};\nuse {defmt_rtt as _, panic_probe as _};\n\nbind_interrupts!(struct Irqs {\n    AES => embassy_stm32::aes::InterruptHandler<peripherals::AES>;\n});\n\n#[embassy_executor::main]\nasync fn main(_spawner: embassy_executor::Spawner) {\n    let p = embassy_stm32::init(Default::default());\n    info!(\"AES-CBC Example\");\n\n    let mut aes = Aes::new_blocking(p.AES, Irqs);\n\n    // Test vectors from NIST SP 800-38A\n    let key = [\n        0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6, 0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf, 0x4f, 0x3c,\n    ];\n\n    let iv = [\n        0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,\n    ];\n\n    // Two blocks of plaintext\n    let plaintext = [\n        // Block 1\n        0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96, 0xe9, 0x3d, 0x7e, 0x11, 0x73, 0x93, 0x17, 0x2a,\n        // Block 2\n        0xae, 0x2d, 0x8a, 0x57, 0x1e, 0x03, 0xac, 0x9c, 0x9e, 0xb7, 0x6f, 0xac, 0x45, 0xaf, 0x8e, 0x51,\n    ];\n\n    let expected_ciphertext = [\n        // Block 1\n        0x76, 0x49, 0xab, 0xac, 0x81, 0x19, 0xb2, 0x46, 0xce, 0xe9, 0x8e, 0x9b, 0x12, 0xe9, 0x19, 0x7d,\n        // Block 2\n        0x50, 0x86, 0xcb, 0x9b, 0x50, 0x72, 0x19, 0xee, 0x95, 0xdb, 0x11, 0x3a, 0x91, 0x76, 0x78, 0xb2,\n    ];\n\n    // ========== CBC Encryption ==========\n    info!(\"=== AES-CBC 128-bit Encryption ===\");\n    info!(\"Key: {:02x}\", key);\n    info!(\"IV:  {:02x}\", iv);\n\n    let cipher = AesCbc::new(&key, &iv);\n    let mut ctx = aes.start(&cipher, Direction::Encrypt);\n\n    let mut ciphertext = [0u8; 32];\n    match aes.payload_blocking(&mut ctx, &plaintext, &mut ciphertext, true) {\n        Ok(()) => {\n            info!(\"Plaintext:  {:02x}\", plaintext);\n            info!(\"Ciphertext: {:02x}\", ciphertext);\n            info!(\"Expected:   {:02x}\", expected_ciphertext);\n\n            if ciphertext == expected_ciphertext {\n                info!(\"✓ CBC Encryption PASSED!\");\n            } else {\n                error!(\"✗ CBC Encryption FAILED!\");\n                // Show which bytes differ\n                for i in 0..32 {\n                    if ciphertext[i] != expected_ciphertext[i] {\n                        error!(\n                            \"  Byte {}: got {:02x}, expected {:02x}\",\n                            i, ciphertext[i], expected_ciphertext[i]\n                        );\n                    }\n                }\n            }\n        }\n        Err(e) => {\n            error!(\"Encryption error: {:?}\", e);\n        }\n    }\n\n    aes.finish_blocking(ctx).ok();\n\n    // ========== CBC Decryption ==========\n    info!(\"=== AES-CBC 128-bit Decryption ===\");\n\n    let cipher = AesCbc::new(&key, &iv);\n    let mut ctx = aes.start(&cipher, Direction::Decrypt);\n\n    let mut decrypted = [0u8; 32];\n    match aes.payload_blocking(&mut ctx, &ciphertext, &mut decrypted, true) {\n        Ok(()) => {\n            info!(\"Ciphertext: {:02x}\", ciphertext);\n            info!(\"Decrypted:  {:02x}\", decrypted);\n            info!(\"Expected:   {:02x}\", plaintext);\n\n            if decrypted == plaintext {\n                info!(\"✓ CBC Decryption PASSED!\");\n            } else {\n                error!(\"✗ CBC Decryption FAILED!\");\n            }\n        }\n        Err(e) => {\n            error!(\"Decryption error: {:?}\", e);\n        }\n    }\n\n    aes.finish_blocking(ctx).ok();\n\n    // ========== Multi-block Processing ==========\n    info!(\"=== AES-CBC Multi-block Processing ===\");\n\n    // Encrypt in multiple calls (simulating streaming)\n    let cipher = AesCbc::new(&key, &iv);\n    let mut ctx = aes.start(&cipher, Direction::Encrypt);\n\n    let mut ciphertext_multi = [0u8; 32];\n\n    // First block\n    match aes.payload_blocking(&mut ctx, &plaintext[..16], &mut ciphertext_multi[..16], false) {\n        Ok(()) => info!(\"✓ Block 1 encrypted\"),\n        Err(e) => error!(\"✗ Block 1 failed: {:?}\", e),\n    }\n\n    // Second block (last=true)\n    match aes.payload_blocking(&mut ctx, &plaintext[16..32], &mut ciphertext_multi[16..32], true) {\n        Ok(()) => info!(\"✓ Block 2 encrypted\"),\n        Err(e) => error!(\"✗ Block 2 failed: {:?}\", e),\n    }\n\n    aes.finish_blocking(ctx).ok();\n\n    if ciphertext_multi == expected_ciphertext {\n        info!(\"✓ Multi-block encryption PASSED!\");\n    } else {\n        error!(\"✗ Multi-block encryption FAILED!\");\n    }\n\n    // ========== 256-bit Key Test ==========\n    info!(\"=== AES-CBC 256-bit Test ===\");\n\n    let key_256 = [\n        0x60, 0x3d, 0xeb, 0x10, 0x15, 0xca, 0x71, 0xbe, 0x2b, 0x73, 0xae, 0xf0, 0x85, 0x7d, 0x77, 0x81, 0x1f, 0x35,\n        0x2c, 0x07, 0x3b, 0x61, 0x08, 0xd7, 0x2d, 0x98, 0x10, 0xa3, 0x09, 0x14, 0xdf, 0xf4,\n    ];\n\n    let iv_256 = [\n        0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,\n    ];\n\n    let plaintext_256 = [\n        0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96, 0xe9, 0x3d, 0x7e, 0x11, 0x73, 0x93, 0x17, 0x2a,\n    ];\n\n    let expected_256 = [\n        0xf5, 0x8c, 0x4c, 0x04, 0xd6, 0xe5, 0xf1, 0xba, 0x77, 0x9e, 0xab, 0xfb, 0x5f, 0x7b, 0xfb, 0xd6,\n    ];\n\n    let cipher = AesCbc::new(&key_256, &iv_256);\n    let mut ctx = aes.start(&cipher, Direction::Encrypt);\n\n    let mut ciphertext_256 = [0u8; 16];\n    match aes.payload_blocking(&mut ctx, &plaintext_256, &mut ciphertext_256, true) {\n        Ok(()) => {\n            info!(\"256-bit Ciphertext: {:02x}\", ciphertext_256);\n            info!(\"Expected:           {:02x}\", expected_256);\n\n            if ciphertext_256 == expected_256 {\n                info!(\"✓ 256-bit CBC Encryption PASSED!\");\n            } else {\n                error!(\"✗ 256-bit CBC Encryption FAILED!\");\n            }\n        }\n        Err(e) => {\n            error!(\"256-bit encryption error: {:?}\", e);\n        }\n    }\n\n    aes.finish_blocking(ctx).ok();\n\n    // ========== Error Handling Example ==========\n    info!(\"=== Testing Error Handling ===\");\n\n    let cipher = AesCbc::new(&key, &iv);\n    let mut ctx = aes.start(&cipher, Direction::Encrypt);\n\n    // Try to encrypt non-aligned data (should fail with REQUIRES_PADDING modes)\n    let unaligned_data = [0u8; 15]; // Not a multiple of 16\n    let mut output = [0u8; 15];\n\n    match aes.payload_blocking(&mut ctx, &unaligned_data, &mut output, false) {\n        Ok(()) => {\n            warn!(\"Unexpected success with unaligned data\");\n        }\n        Err(e) => {\n            info!(\"✓ Correctly rejected unaligned data: {:?}\", e);\n        }\n    }\n\n    aes.finish_blocking(ctx).ok();\n\n    info!(\"=== All AES-CBC tests complete ===\");\n\n    loop {\n        cortex_m::asm::wfi();\n    }\n}\n"
  },
  {
    "path": "examples/stm32wba/src/bin/aes_ccm.rs",
    "content": "//! AES-CCM (Counter with CBC-MAC) - Authenticated Encryption Example\n//!\n//! Demonstrates authenticated encryption using AES-CCM mode.\n//! CCM is an alternative to GCM that's commonly used in constrained environments.\n//!\n//! # Cipher Mode: CCM (Counter with CBC-MAC)\n//! - Combines CTR encryption with CBC-MAC authentication\n//! - Provides both confidentiality AND authenticity\n//! - Requires knowing data lengths in advance (AAD and payload)\n//! - Used in IEEE 802.15.4 (Zigbee), Bluetooth LE, IPsec\n//!\n//! # Key Differences from GCM\n//! - CCM requires knowing payload/AAD lengths before encryption\n//! - CCM uses CBC-MAC for authentication (vs GHASH in GCM)\n//! - CCM may have variable tag sizes (4-16 bytes)\n//! - CCM AAD has a specific format with length prefix\n//!\n//! # B0 Block Format (computed by AesCcm::new)\n//! - Byte 0: Flags (tag size, AAD present, L value)\n//! - Bytes 1-N: Nonce\n//! - Bytes N+1-15: Payload length (big-endian)\n//!\n//! # AAD Format for CCM\n//! For CCM, AAD must be formatted with a length prefix:\n//! - If AAD_len < 2^16-2^8: 2-byte length || AAD || padding\n//! - If AAD_len < 2^32: 0xFFFE || 4-byte length || AAD || padding\n//!\n//! # Security Notes\n//! - Nonce must be unique for each message with same key\n//! - Tag verification failure means: reject the data\n//! - Variable tag size allows security/overhead tradeoff\n\n#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_stm32::aes::{Aes, AesCcm, Direction};\nuse embassy_stm32::{bind_interrupts, peripherals};\nuse {defmt_rtt as _, panic_probe as _};\n\nbind_interrupts!(struct Irqs {\n    AES => embassy_stm32::aes::InterruptHandler<peripherals::AES>;\n});\n\n#[embassy_executor::main]\nasync fn main(_spawner: embassy_executor::Spawner) {\n    let p = embassy_stm32::init(Default::default());\n    info!(\"AES-CCM Authenticated Encryption Example\");\n\n    let mut aes = Aes::new_blocking(p.AES, Irqs);\n\n    // ========== CCM Test Case (from ST HAL) ==========\n    // Note: CCM requires pre-formatted AAD with length prefix\n\n    // 256-bit key\n    let key: [u8; 32] = [\n        0xD3, 0x46, 0xD1, 0x1A, 0x71, 0x17, 0xCE, 0x04, 0x08, 0x08, 0x95, 0x70, 0x77, 0x78, 0x28, 0x7C, 0x40, 0xF5,\n        0xF4, 0x73, 0xA9, 0xA8, 0xF2, 0xB1, 0x57, 0x0F, 0x61, 0x37, 0x46, 0x69, 0x75, 0x1A,\n    ];\n\n    // 12-byte nonce (extracted from ST HAL B0 block bytes 1-12)\n    // Note: The first byte of B0 (0x7A) is the FLAGS byte, computed by AesCcm::new()\n    // The nonce is bytes 1-12 of the B0 block\n    let nonce: [u8; 12] = [0x05, 0xC8, 0xCC, 0x77, 0x32, 0xB3, 0xB4, 0x7F, 0x08, 0xAF, 0x1D, 0xAF];\n\n    // Actual AAD (7 bytes): 0x34, 0x21, 0x5F, 0x03, 0x25, 0x67, 0x0B\n    // For CCM, AAD must be formatted with length prefix\n    // Formatted AAD (B1 block): 2-byte length (0x0007) || 7 bytes AAD || 7 bytes padding\n    let formatted_aad: [u8; 16] = [\n        0x00, 0x07, // 2-byte length encoding (7 bytes of AAD)\n        0x34, 0x21, 0x5F, 0x03, 0x25, 0x67, 0x0B, // Actual AAD (7 bytes)\n        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Zero padding\n    ];\n\n    // Plaintext: 17 bytes (not block-aligned)\n    let plaintext: [u8; 17] = [\n        0xBB, 0xD8, 0x83, 0x34, 0x00, 0x00, 0x75, 0xF6, 0xF4, 0xE8, 0x9F, 0x9D, 0xDA, 0x50, 0xF5, 0xEA, 0xB1,\n    ];\n\n    // Expected ciphertext: 17 bytes\n    let expected_ciphertext: [u8; 17] = [\n        0xA7, 0xB7, 0x65, 0x3C, 0x5D, 0x60, 0x0A, 0xF3, 0x9C, 0xA0, 0xDB, 0x48, 0x0F, 0x4F, 0x5C, 0xCE, 0x99,\n    ];\n\n    // Expected tag (16 bytes)\n    let expected_tag: [u8; 16] = [\n        0x35, 0x2C, 0x36, 0xD3, 0x93, 0x5B, 0x88, 0x94, 0x04, 0x26, 0xA0, 0x04, 0x3B, 0xBA, 0xB7, 0xEE,\n    ];\n\n    // AAD length and payload length must be known in advance for CCM\n    let aad_len = 7; // Actual AAD length (not including format prefix)\n    let payload_len = 17;\n\n    info!(\"=== AES-CCM Encryption ===\");\n    info!(\"Key (256-bit): {:02x}\", key[..16]);\n    info!(\"Nonce (12 bytes): {:02x}\", nonce);\n    info!(\"AAD length: {} bytes\", aad_len);\n    info!(\"Payload length: {} bytes\", payload_len);\n\n    // Create CCM cipher\n    // Parameters: key, nonce, AAD length, payload length\n    // IV_SIZE = 12, TAG_SIZE = 16 (full tag)\n    let cipher: AesCcm<32, 12, 16> = AesCcm::new(&key, &nonce, aad_len, payload_len);\n    let mut ctx = aes.start(&cipher, Direction::Encrypt);\n\n    // Process formatted AAD (with length prefix for CCM)\n    // Note: CCM requires AAD to be pre-formatted with length encoding\n    match aes.aad_blocking(&mut ctx, &formatted_aad, true) {\n        Ok(()) => info!(\"AAD processed\"),\n        Err(e) => {\n            error!(\"AAD processing failed: {:?}\", e);\n            loop {\n                cortex_m::asm::wfi();\n            }\n        }\n    }\n\n    // Encrypt payload\n    let mut ciphertext = [0u8; 17];\n    match aes.payload_blocking(&mut ctx, &plaintext, &mut ciphertext, true) {\n        Ok(()) => {\n            info!(\"Payload encrypted\");\n            info!(\"Plaintext:  {:02x}\", plaintext);\n            info!(\"Ciphertext: {:02x}\", ciphertext);\n            info!(\"Expected:   {:02x}\", expected_ciphertext);\n\n            if ciphertext == expected_ciphertext {\n                info!(\"Ciphertext MATCHES!\");\n            } else {\n                error!(\"Ciphertext mismatch!\");\n            }\n        }\n        Err(e) => {\n            error!(\"Encryption failed: {:?}\", e);\n            loop {\n                cortex_m::asm::wfi();\n            }\n        }\n    }\n\n    // Get authentication tag\n    match aes.finish_blocking(ctx) {\n        Ok(Some(tag)) => {\n            info!(\"Auth Tag:     {:02x}\", tag);\n            info!(\"Expected Tag: {:02x}\", expected_tag);\n\n            if tag == expected_tag {\n                info!(\"CCM Encryption PASSED!\");\n            } else {\n                error!(\"Tag mismatch!\");\n            }\n        }\n        Ok(None) => {\n            error!(\"No tag returned!\");\n        }\n        Err(e) => {\n            error!(\"Finish failed: {:?}\", e);\n        }\n    }\n\n    // ========== CCM Decryption ==========\n    info!(\"=== AES-CCM Decryption ===\");\n\n    let cipher: AesCcm<32, 12, 16> = AesCcm::new(&key, &nonce, aad_len, payload_len);\n    let mut ctx = aes.start(&cipher, Direction::Decrypt);\n\n    // Process AAD\n    match aes.aad_blocking(&mut ctx, &formatted_aad, true) {\n        Ok(()) => info!(\"AAD processed\"),\n        Err(e) => {\n            error!(\"AAD processing failed: {:?}\", e);\n            loop {\n                cortex_m::asm::wfi();\n            }\n        }\n    }\n\n    // Decrypt ciphertext\n    let mut decrypted = [0u8; 17];\n    match aes.payload_blocking(&mut ctx, &ciphertext, &mut decrypted, true) {\n        Ok(()) => {\n            info!(\"Payload decrypted\");\n            info!(\"Decrypted: {:02x}\", decrypted);\n\n            if decrypted == plaintext {\n                info!(\"Decryption matches plaintext!\");\n            } else {\n                error!(\"Decryption mismatch!\");\n            }\n        }\n        Err(e) => {\n            error!(\"Decryption failed: {:?}\", e);\n        }\n    }\n\n    // Verify tag\n    match aes.finish_blocking(ctx) {\n        Ok(Some(tag)) => {\n            info!(\"Computed Tag: {:02x}\", tag);\n\n            if tag == expected_tag {\n                info!(\"CCM Decryption and Verification PASSED!\");\n            } else {\n                error!(\"Tag verification FAILED - data may be tampered!\");\n            }\n        }\n        Ok(None) => {\n            error!(\"No tag returned!\");\n        }\n        Err(e) => {\n            error!(\"Finish failed: {:?}\", e);\n        }\n    }\n\n    info!(\"=== All CCM tests complete ===\");\n\n    loop {\n        cortex_m::asm::wfi();\n    }\n}\n"
  },
  {
    "path": "examples/stm32wba/src/bin/aes_ctr.rs",
    "content": "//! AES-CTR (Counter) Mode Example - Stream Cipher\n//!\n//! Demonstrates AES in Counter mode, which converts the block cipher into a stream cipher.\n//!\n//! # Cipher Mode: CTR (Counter)\n//! - Turns AES into a stream cipher by encrypting sequential counter values\n//! - No padding required - works with any data length\n//! - Encryption and decryption are identical operations\n//! - Allows parallel processing and random access\n//! - Ideal for streaming data\n//!\n//! # What This Example Shows\n//! - Stream cipher operation (no padding needed)\n//! - Partial block handling (13 bytes, not aligned to 16)\n//! - Streaming with multiple calls of varying sizes\n//! - 128-bit and 256-bit keys\n//! - Encryption/decryption symmetry (same operation)\n//! - NIST test vector validation\n//!\n//! # Advantages of CTR Mode\n//! - Process any length data without padding overhead\n//! - Can decrypt specific blocks without decrypting entire stream\n//! - Encryption/decryption use the same hardware operation\n//! - Suitable for real-time streaming applications\n//!\n//! # Security Notes\n//! - Counter/nonce must NEVER be reused with the same key\n//! - Typically: nonce (random) + counter (incremental)\n//! - CTR provides confidentiality but NOT authentication\n//! - Consider GCM mode for authenticated encryption\n\n#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_stm32::aes::{Aes, AesCtr, Direction};\nuse embassy_stm32::{bind_interrupts, peripherals};\nuse {defmt_rtt as _, panic_probe as _};\n\nbind_interrupts!(struct Irqs {\n    AES => embassy_stm32::aes::InterruptHandler<peripherals::AES>;\n});\n\n#[embassy_executor::main]\nasync fn main(_spawner: embassy_executor::Spawner) {\n    let p = embassy_stm32::init(Default::default());\n    info!(\"AES-CTR Stream Cipher Example\");\n\n    let mut aes = Aes::new_blocking(p.AES, Irqs);\n\n    // Test vectors from NIST SP 800-38A\n    let key = [\n        0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6, 0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf, 0x4f, 0x3c,\n    ];\n\n    // Counter/IV (nonce + counter)\n    let counter = [\n        0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff,\n    ];\n\n    let plaintext = [\n        0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96, 0xe9, 0x3d, 0x7e, 0x11, 0x73, 0x93, 0x17, 0x2a, 0xae, 0x2d,\n        0x8a, 0x57, 0x1e, 0x03, 0xac, 0x9c, 0x9e, 0xb7, 0x6f, 0xac, 0x45, 0xaf, 0x8e, 0x51,\n    ];\n\n    let expected_ciphertext = [\n        0x87, 0x4d, 0x61, 0x91, 0xb6, 0x20, 0xe3, 0x26, 0x1b, 0xef, 0x68, 0x64, 0x99, 0x0d, 0xb6, 0xce, 0x98, 0x06,\n        0xf6, 0x6b, 0x79, 0x70, 0xfd, 0xff, 0x86, 0x17, 0x18, 0x7b, 0xb9, 0xff, 0xfd, 0xff,\n    ];\n\n    // ========== CTR Encryption ==========\n    info!(\"=== AES-CTR 128-bit Encryption ===\");\n    info!(\"Key:     {:02x}\", key);\n    info!(\"Counter: {:02x}\", counter);\n\n    let cipher = AesCtr::new(&key, &counter);\n    let mut ctx = aes.start(&cipher, Direction::Encrypt);\n\n    let mut ciphertext = [0u8; 32];\n    match aes.payload_blocking(&mut ctx, &plaintext, &mut ciphertext, true) {\n        Ok(()) => {\n            info!(\"Plaintext:  {:02x}\", plaintext);\n            info!(\"Ciphertext: {:02x}\", ciphertext);\n            info!(\"Expected:   {:02x}\", expected_ciphertext);\n\n            if ciphertext == expected_ciphertext {\n                info!(\"✓ CTR Encryption PASSED!\");\n            } else {\n                error!(\"✗ CTR Encryption FAILED!\");\n            }\n        }\n        Err(e) => {\n            error!(\"Encryption error: {:?}\", e);\n        }\n    }\n\n    aes.finish_blocking(ctx).ok();\n\n    // ========== CTR Decryption (same as encryption for CTR mode) ==========\n    info!(\"=== AES-CTR 128-bit Decryption ===\");\n\n    // In CTR mode, encryption and decryption are the same operation\n    let cipher = AesCtr::new(&key, &counter);\n    let mut ctx = aes.start(&cipher, Direction::Encrypt); // Note: can use Encrypt for both\n\n    let mut decrypted = [0u8; 32];\n    match aes.payload_blocking(&mut ctx, &ciphertext, &mut decrypted, true) {\n        Ok(()) => {\n            info!(\"Ciphertext: {:02x}\", ciphertext);\n            info!(\"Decrypted:  {:02x}\", decrypted);\n            info!(\"Expected:   {:02x}\", plaintext);\n\n            if decrypted == plaintext {\n                info!(\"✓ CTR Decryption PASSED!\");\n            } else {\n                error!(\"✗ CTR Decryption FAILED!\");\n            }\n        }\n        Err(e) => {\n            error!(\"Decryption error: {:?}\", e);\n        }\n    }\n\n    aes.finish_blocking(ctx).ok();\n\n    // ========== Stream Cipher Property - Partial Block ==========\n    info!(\"=== AES-CTR Stream Cipher - Partial Block ===\");\n\n    // CTR mode can handle any data length (no padding required)\n    let partial_data = [\n        0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96, 0xe9, 0x3d, 0x7e, 0x11, 0x73, // 13 bytes\n    ];\n\n    let expected_partial = [\n        0x87, 0x4d, 0x61, 0x91, 0xb6, 0x20, 0xe3, 0x26, 0x1b, 0xef, 0x68, 0x64, 0x99, // 13 bytes\n    ];\n\n    let cipher = AesCtr::new(&key, &counter);\n    let mut ctx = aes.start(&cipher, Direction::Encrypt);\n\n    let mut ciphertext_partial = [0u8; 13];\n    match aes.payload_blocking(&mut ctx, &partial_data, &mut ciphertext_partial, true) {\n        Ok(()) => {\n            info!(\"Partial input (13 bytes): {:02x}\", partial_data);\n            info!(\"Output:                   {:02x}\", ciphertext_partial);\n            info!(\"Expected:                 {:02x}\", expected_partial);\n\n            if ciphertext_partial == expected_partial {\n                info!(\"✓ Partial block encryption PASSED!\");\n            } else {\n                error!(\"✗ Partial block encryption FAILED!\");\n            }\n        }\n        Err(e) => {\n            error!(\"Partial block error: {:?}\", e);\n        }\n    }\n\n    aes.finish_blocking(ctx).ok();\n\n    // ========== Streaming Multiple Calls ==========\n    info!(\"=== AES-CTR Streaming Processing ===\");\n    // Note: Intermediate chunks must be block-aligned (16 bytes).\n    // Only the final chunk (last=true) can be a partial block.\n\n    let cipher = AesCtr::new(&key, &counter);\n    let mut ctx = aes.start(&cipher, Direction::Encrypt);\n\n    let mut ciphertext_stream = [0u8; 32];\n\n    // Encrypt first 16 bytes (block-aligned)\n    match aes.payload_blocking(&mut ctx, &plaintext[..16], &mut ciphertext_stream[..16], false) {\n        Ok(()) => info!(\"✓ Stream block 1 encrypted (16 bytes)\"),\n        Err(e) => error!(\"✗ Stream block 1 failed: {:?}\", e),\n    }\n\n    // Encrypt final 16 bytes (last=true)\n    match aes.payload_blocking(&mut ctx, &plaintext[16..32], &mut ciphertext_stream[16..32], true) {\n        Ok(()) => info!(\"✓ Stream block 2 encrypted (16 bytes, final)\"),\n        Err(e) => error!(\"✗ Stream block 2 failed: {:?}\", e),\n    }\n\n    aes.finish_blocking(ctx).ok();\n\n    if ciphertext_stream == expected_ciphertext {\n        info!(\"✓ Streaming encryption PASSED!\");\n    } else {\n        error!(\"✗ Streaming encryption FAILED!\");\n        info!(\"Got:      {:02x}\", ciphertext_stream);\n        info!(\"Expected: {:02x}\", expected_ciphertext);\n    }\n\n    // ========== 256-bit Key Test ==========\n    info!(\"=== AES-CTR 256-bit Test ===\");\n\n    let key_256 = [\n        0x60, 0x3d, 0xeb, 0x10, 0x15, 0xca, 0x71, 0xbe, 0x2b, 0x73, 0xae, 0xf0, 0x85, 0x7d, 0x77, 0x81, 0x1f, 0x35,\n        0x2c, 0x07, 0x3b, 0x61, 0x08, 0xd7, 0x2d, 0x98, 0x10, 0xa3, 0x09, 0x14, 0xdf, 0xf4,\n    ];\n\n    let counter_256 = [\n        0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff,\n    ];\n\n    let plaintext_256 = [\n        0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96, 0xe9, 0x3d, 0x7e, 0x11, 0x73, 0x93, 0x17, 0x2a,\n    ];\n\n    let expected_256 = [\n        0x60, 0x1e, 0xc3, 0x13, 0x77, 0x57, 0x89, 0xa5, 0xb7, 0xa7, 0xf5, 0x04, 0xbb, 0xf3, 0xd2, 0x28,\n    ];\n\n    let cipher = AesCtr::new(&key_256, &counter_256);\n    let mut ctx = aes.start(&cipher, Direction::Encrypt);\n\n    let mut ciphertext_256 = [0u8; 16];\n    match aes.payload_blocking(&mut ctx, &plaintext_256, &mut ciphertext_256, true) {\n        Ok(()) => {\n            info!(\"256-bit Ciphertext: {:02x}\", ciphertext_256);\n            info!(\"Expected:           {:02x}\", expected_256);\n\n            if ciphertext_256 == expected_256 {\n                info!(\"✓ 256-bit CTR Encryption PASSED!\");\n            } else {\n                error!(\"✗ 256-bit CTR Encryption FAILED!\");\n            }\n        }\n        Err(e) => {\n            error!(\"256-bit encryption error: {:?}\", e);\n        }\n    }\n\n    aes.finish_blocking(ctx).ok();\n\n    // ========== Demonstrate Stream Cipher Advantage ==========\n    info!(\"=== CTR Mode Advantages ===\");\n    info!(\"✓ No padding required - works with any data length\");\n    info!(\"✓ Encryption and decryption use same operation\");\n    info!(\"✓ Parallel processing possible (not shown in blocking mode)\");\n    info!(\"✓ Random access to encrypted data (can decrypt any block independently)\");\n\n    info!(\"=== All AES-CTR tests complete ===\");\n\n    loop {\n        cortex_m::asm::wfi();\n    }\n}\n"
  },
  {
    "path": "examples/stm32wba/src/bin/aes_ecb.rs",
    "content": "//! AES-ECB (Electronic Codebook) Mode Example\n//!\n//! Demonstrates basic AES encryption/decryption using ECB mode.\n//!\n//! # Cipher Mode: ECB (Electronic Codebook)\n//! - Simplest AES mode - each block encrypted independently\n//! - Requires 16-byte aligned data (padding necessary)\n//! - WARNING: Not recommended for most data - identical plaintext blocks produce\n//!   identical ciphertext blocks, revealing patterns\n//! - Use only for encrypting random data like keys\n//!\n//! # What This Example Shows\n//! - Basic AES peripheral initialization\n//! - 128-bit and 256-bit key encryption\n//! - Encryption and decryption operations\n//! - NIST test vector validation\n//!\n//! # Test Vectors\n//! Uses official NIST SP 800-38A test vectors for validation.\n\n#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_stm32::aes::{Aes, AesEcb, Direction};\nuse embassy_stm32::{bind_interrupts, peripherals};\nuse {defmt_rtt as _, panic_probe as _};\n\nbind_interrupts!(struct Irqs {\n    AES => embassy_stm32::aes::InterruptHandler<peripherals::AES>;\n});\n\n#[embassy_executor::main]\nasync fn main(_spawner: embassy_executor::Spawner) {\n    let p = embassy_stm32::init(Default::default());\n    info!(\"AES-ECB Example\");\n\n    let mut aes = Aes::new_blocking(p.AES, Irqs);\n\n    // Test vector from NIST SP 800-38A\n    let key_128 = [\n        0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6, 0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf, 0x4f, 0x3c,\n    ];\n\n    let plaintext = [\n        0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96, 0xe9, 0x3d, 0x7e, 0x11, 0x73, 0x93, 0x17, 0x2a,\n    ];\n\n    let expected_ciphertext = [\n        0x3a, 0xd7, 0x7b, 0xb4, 0x0d, 0x7a, 0x36, 0x60, 0xa8, 0x9e, 0xca, 0xf3, 0x24, 0x66, 0xef, 0x97,\n    ];\n\n    // ========== Encryption Test ==========\n    info!(\"=== AES-ECB 128-bit Encryption ===\");\n    let cipher = AesEcb::new(&key_128);\n    let mut ctx = aes.start(&cipher, Direction::Encrypt);\n\n    let mut ciphertext = [0u8; 16];\n    match aes.payload_blocking(&mut ctx, &plaintext, &mut ciphertext, true) {\n        Ok(()) => {\n            info!(\"Plaintext:  {:02x}\", plaintext);\n            info!(\"Ciphertext: {:02x}\", ciphertext);\n            info!(\"Expected:   {:02x}\", expected_ciphertext);\n\n            if ciphertext == expected_ciphertext {\n                info!(\"✓ Encryption PASSED!\");\n            } else {\n                error!(\"✗ Encryption FAILED!\");\n            }\n        }\n        Err(e) => {\n            error!(\"Encryption error: {:?}\", e);\n        }\n    }\n\n    aes.finish_blocking(ctx).ok();\n\n    // ========== Decryption Test ==========\n    info!(\"=== AES-ECB 128-bit Decryption ===\");\n    let cipher = AesEcb::new(&key_128);\n    let mut ctx = aes.start(&cipher, Direction::Decrypt);\n\n    let mut decrypted = [0u8; 16];\n    match aes.payload_blocking(&mut ctx, &ciphertext, &mut decrypted, true) {\n        Ok(()) => {\n            info!(\"Ciphertext: {:02x}\", ciphertext);\n            info!(\"Decrypted:  {:02x}\", decrypted);\n            info!(\"Expected:   {:02x}\", plaintext);\n\n            if decrypted == plaintext {\n                info!(\"✓ Decryption PASSED!\");\n            } else {\n                error!(\"✗ Decryption FAILED!\");\n            }\n        }\n        Err(e) => {\n            error!(\"Decryption error: {:?}\", e);\n        }\n    }\n\n    aes.finish_blocking(ctx).ok();\n\n    // ========== 256-bit Key Test ==========\n    info!(\"=== AES-ECB 256-bit Test ===\");\n    let key_256 = [\n        0x60, 0x3d, 0xeb, 0x10, 0x15, 0xca, 0x71, 0xbe, 0x2b, 0x73, 0xae, 0xf0, 0x85, 0x7d, 0x77, 0x81, 0x1f, 0x35,\n        0x2c, 0x07, 0x3b, 0x61, 0x08, 0xd7, 0x2d, 0x98, 0x10, 0xa3, 0x09, 0x14, 0xdf, 0xf4,\n    ];\n\n    let plaintext_256 = [\n        0xf6, 0x9f, 0x24, 0x45, 0xdf, 0x4f, 0x9b, 0x17, 0xad, 0x2b, 0x41, 0x7b, 0xe6, 0x6c, 0x37, 0x10,\n    ];\n\n    let expected_256 = [\n        0xb4, 0x7b, 0xd7, 0x3a, 0x60, 0x36, 0x7a, 0x0d, 0xf3, 0xca, 0x9e, 0xa8, 0x97, 0xef, 0x66, 0x24,\n    ];\n\n    let cipher = AesEcb::new(&key_256);\n    let mut ctx = aes.start(&cipher, Direction::Encrypt);\n\n    let mut ciphertext_256 = [0u8; 16];\n    match aes.payload_blocking(&mut ctx, &plaintext_256, &mut ciphertext_256, true) {\n        Ok(()) => {\n            info!(\"256-bit Ciphertext: {:02x}\", ciphertext_256);\n            info!(\"Expected:           {:02x}\", expected_256);\n\n            if ciphertext_256 == expected_256 {\n                info!(\"✓ 256-bit Encryption PASSED!\");\n            } else {\n                error!(\"✗ 256-bit Encryption FAILED!\");\n            }\n        }\n        Err(e) => {\n            error!(\"256-bit Encryption error: {:?}\", e);\n        }\n    }\n\n    aes.finish_blocking(ctx).ok();\n\n    info!(\"=== All AES-ECB tests complete ===\");\n\n    loop {\n        cortex_m::asm::wfi();\n    }\n}\n"
  },
  {
    "path": "examples/stm32wba/src/bin/aes_gcm.rs",
    "content": "//! AES-GCM (Galois/Counter Mode) - Authenticated Encryption Example\n//!\n//! Demonstrates modern authenticated encryption using AES-GCM mode.\n//! This is the RECOMMENDED mode for new applications.\n//!\n//! # Cipher Mode: GCM (Galois/Counter Mode)\n//! - Combines encryption + authentication in one operation\n//! - No padding required - works with any data length\n//! - Generates authentication tag to detect tampering\n//! - Supports Additional Authenticated Data (AAD) - metadata that's authenticated but not encrypted\n//! - Industry standard for TLS, IPsec, disk encryption\n//!\n//! # What This Example Shows\n//! - Complete GCM encryption with AAD\n//! - Tag generation and verification\n//! - Decryption with authentication check\n//! - GCM without AAD (optional AAD)\n//! - NIST GCM test vector validation\n//!\n//! # Three-Phase GCM Process\n//! 1. AAD Phase: Process metadata to authenticate (optional)\n//! 2. Payload Phase: Encrypt/decrypt data\n//! 3. Final Phase: Generate/verify authentication tag\n//!\n//! # Why Use GCM\n//! - Provides both confidentiality AND authenticity\n//! - Detects any tampering or corruption\n//! - AAD protects metadata without encryption overhead\n//! - Single-pass operation (efficient)\n//! - Widely supported and standardized\n//!\n//! # Security Notes\n//! - IV/nonce must be unique for each encryption with same key\n//! - Recommended: 96-bit (12-byte) random nonce\n//! - ALWAYS verify tag before using decrypted data\n//! - Tag verification failure means: reject the data (tampering detected)\n\n#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_stm32::aes::{Aes, AesGcm, Direction};\nuse embassy_stm32::{bind_interrupts, peripherals};\nuse {defmt_rtt as _, panic_probe as _};\n\nbind_interrupts!(struct Irqs {\n    AES => embassy_stm32::aes::InterruptHandler<peripherals::AES>;\n});\n\n#[embassy_executor::main]\nasync fn main(_spawner: embassy_executor::Spawner) {\n    let p = embassy_stm32::init(Default::default());\n    info!(\"AES-GCM Authenticated Encryption Example\");\n\n    let mut aes = Aes::new_blocking(p.AES, Irqs);\n\n    // NIST SP 800-38D Test Case 4 (60 bytes plaintext + 20 bytes AAD)\n    // Key: feffe9928665731c6d6a8f9467308308\n    let key = [\n        0xfe, 0xff, 0xe9, 0x92, 0x86, 0x65, 0x73, 0x1c, 0x6d, 0x6a, 0x8f, 0x94, 0x67, 0x30, 0x83, 0x08,\n    ];\n\n    // IV: cafebabefacedbaddecaf888 (12 bytes)\n    let iv = [0xca, 0xfe, 0xba, 0xbe, 0xfa, 0xce, 0xdb, 0xad, 0xde, 0xca, 0xf8, 0x88];\n\n    // AAD: feedfacedeadbeeffeedfacedeadbeefabaddad2 (20 bytes)\n    let aad = [\n        0xfe, 0xed, 0xfa, 0xce, 0xde, 0xad, 0xbe, 0xef, 0xfe, 0xed, 0xfa, 0xce, 0xde, 0xad, 0xbe, 0xef, 0xab, 0xad,\n        0xda, 0xd2,\n    ];\n\n    // Plaintext: 60 bytes (NOT 64 - this is important!)\n    let plaintext = [\n        0xd9, 0x31, 0x32, 0x25, 0xf8, 0x84, 0x06, 0xe5, 0xa5, 0x59, 0x09, 0xc5, 0xaf, 0xf5, 0x26, 0x9a, 0x86, 0xa7,\n        0xa9, 0x53, 0x15, 0x34, 0xf7, 0xda, 0x2e, 0x4c, 0x30, 0x3d, 0x8a, 0x31, 0x8a, 0x72, 0x1c, 0x3c, 0x0c, 0x95,\n        0x95, 0x68, 0x09, 0x53, 0x2f, 0xcf, 0x0e, 0x24, 0x49, 0xa6, 0xb5, 0x25, 0xb1, 0x6a, 0xed, 0xf5, 0xaa, 0x0d,\n        0xe6, 0x57, 0xba, 0x63, 0x7b, 0x39,\n    ];\n\n    // Expected ciphertext: 60 bytes\n    let expected_ciphertext = [\n        0x42, 0x83, 0x1e, 0xc2, 0x21, 0x77, 0x74, 0x24, 0x4b, 0x72, 0x21, 0xb7, 0x84, 0xd0, 0xd4, 0x9c, 0xe3, 0xaa,\n        0x21, 0x2f, 0x2c, 0x02, 0xa4, 0xe0, 0x35, 0xc1, 0x7e, 0x23, 0x29, 0xac, 0xa1, 0x2e, 0x21, 0xd5, 0x14, 0xb2,\n        0x54, 0x66, 0x93, 0x1c, 0x7d, 0x8f, 0x6a, 0x5a, 0xac, 0x84, 0xaa, 0x05, 0x1b, 0xa3, 0x0b, 0x39, 0x6a, 0x0a,\n        0xac, 0x97, 0x3d, 0x58, 0xe0, 0x91,\n    ];\n\n    // Expected tag for NIST Test Case 4 (60 bytes plaintext, 20 bytes AAD): 5bc94fbc3221a5db94fae95ae7121a47\n    let expected_tag = [\n        0x5b, 0xc9, 0x4f, 0xbc, 0x32, 0x21, 0xa5, 0xdb, 0x94, 0xfa, 0xe9, 0x5a, 0xe7, 0x12, 0x1a, 0x47,\n    ];\n\n    // ========== GCM Encryption with AAD ==========\n    info!(\"=== AES-GCM Encryption ===\");\n    info!(\"Key:       {:02x}\", key);\n    info!(\"IV (12b):  {:02x}\", iv);\n    info!(\"AAD:       {:02x}\", aad);\n    info!(\"Plaintext: {} bytes\", plaintext.len());\n\n    let cipher = AesGcm::new(&key, &iv);\n    let mut ctx = aes.start(&cipher, Direction::Encrypt);\n\n    // Process AAD (Additional Authenticated Data)\n    match aes.aad_blocking(&mut ctx, &aad, true) {\n        Ok(()) => info!(\"✓ AAD processed\"),\n        Err(e) => {\n            error!(\"✗ AAD processing failed: {:?}\", e);\n            loop {\n                cortex_m::asm::wfi();\n            }\n        }\n    }\n\n    // Encrypt payload\n    let mut ciphertext = [0u8; 60];\n    match aes.payload_blocking(&mut ctx, &plaintext, &mut ciphertext, true) {\n        Ok(()) => {\n            info!(\"✓ Payload encrypted\");\n            info!(\"Ciphertext: {:02x}\", ciphertext);\n        }\n        Err(e) => {\n            error!(\"✗ Encryption failed: {:?}\", e);\n            loop {\n                cortex_m::asm::wfi();\n            }\n        }\n    }\n\n    // Get authentication tag\n    match aes.finish_blocking(ctx) {\n        Ok(Some(tag)) => {\n            info!(\"Auth Tag:     {:02x}\", tag);\n            info!(\"Expected Tag: {:02x}\", expected_tag);\n\n            // Verify results\n            let ciphertext_ok = ciphertext == expected_ciphertext;\n            let tag_ok = tag == expected_tag;\n\n            if ciphertext_ok && tag_ok {\n                info!(\"✓ GCM Encryption PASSED!\");\n            } else {\n                if !ciphertext_ok {\n                    error!(\"✗ Ciphertext mismatch!\");\n                }\n                if !tag_ok {\n                    error!(\"✗ Tag mismatch!\");\n                }\n            }\n        }\n        Ok(None) => {\n            error!(\"✗ No tag returned!\");\n        }\n        Err(e) => {\n            error!(\"✗ Finish failed: {:?}\", e);\n        }\n    }\n\n    // ========== GCM Decryption and Verification ==========\n    info!(\"=== AES-GCM Decryption ===\");\n\n    let cipher = AesGcm::new(&key, &iv);\n    let mut ctx = aes.start(&cipher, Direction::Decrypt);\n\n    // Process AAD\n    match aes.aad_blocking(&mut ctx, &aad, true) {\n        Ok(()) => info!(\"✓ AAD processed\"),\n        Err(e) => {\n            error!(\"✗ AAD processing failed: {:?}\", e);\n            loop {\n                cortex_m::asm::wfi();\n            }\n        }\n    }\n\n    // Decrypt payload\n    let mut decrypted = [0u8; 60];\n    match aes.payload_blocking(&mut ctx, &ciphertext, &mut decrypted, true) {\n        Ok(()) => {\n            info!(\"✓ Payload decrypted\");\n        }\n        Err(e) => {\n            error!(\"✗ Decryption failed: {:?}\", e);\n            loop {\n                cortex_m::asm::wfi();\n            }\n        }\n    }\n\n    // Get and verify authentication tag\n    match aes.finish_blocking(ctx) {\n        Ok(Some(tag)) => {\n            info!(\"Computed Tag: {:02x}\", tag);\n\n            // In real applications, you would compare the computed tag with the received tag\n            if tag == expected_tag {\n                info!(\"✓ Tag verification PASSED!\");\n\n                // Check decrypted plaintext\n                if decrypted == plaintext {\n                    info!(\"✓ Decryption PASSED!\");\n                    info!(\"Decrypted: {:02x}\", decrypted[..16]);\n                } else {\n                    error!(\"✗ Decryption mismatch!\");\n                }\n            } else {\n                error!(\"✗ Tag verification FAILED - message authentication failed!\");\n                // In real code, you would reject the decrypted data\n            }\n        }\n        Ok(None) => {\n            error!(\"✗ No tag returned!\");\n        }\n        Err(e) => {\n            error!(\"✗ Finish failed: {:?}\", e);\n        }\n    }\n\n    // ========== NIST Test Case 2 (no AAD) ==========\n    info!(\"=== NIST GCM Test Case 2 (no AAD) ===\");\n\n    // NIST SP 800-38D Test Case 2\n    let nist_key = [0u8; 16]; // All zeros\n    let nist_iv = [0u8; 12]; // All zeros\n    let nist_plaintext = [0u8; 16]; // All zeros\n    // Expected ciphertext: 0388dace60b6a392f328c2b971b2fe78\n    let nist_expected_ct = [\n        0x03, 0x88, 0xda, 0xce, 0x60, 0xb6, 0xa3, 0x92, 0xf3, 0x28, 0xc2, 0xb9, 0x71, 0xb2, 0xfe, 0x78,\n    ];\n    // Expected tag: ab6e47d42cec13bdf53a67b21257bddf\n    let nist_expected_tag = [\n        0xab, 0x6e, 0x47, 0xd4, 0x2c, 0xec, 0x13, 0xbd, 0xf5, 0x3a, 0x67, 0xb2, 0x12, 0x57, 0xbd, 0xdf,\n    ];\n\n    let cipher = AesGcm::new(&nist_key, &nist_iv);\n    let mut ctx = aes.start(&cipher, Direction::Encrypt);\n\n    // No AAD - skip aad_blocking call\n\n    let mut nist_ciphertext = [0u8; 16];\n    match aes.payload_blocking(&mut ctx, &nist_plaintext, &mut nist_ciphertext, true) {\n        Ok(()) => {\n            info!(\"Plaintext:  {:02x}\", nist_plaintext);\n            info!(\"Ciphertext: {:02x}\", nist_ciphertext);\n            info!(\"Expected:   {:02x}\", nist_expected_ct);\n            if nist_ciphertext == nist_expected_ct {\n                info!(\"✓ NIST ciphertext MATCHES!\");\n            } else {\n                error!(\"✗ NIST ciphertext MISMATCH!\");\n            }\n        }\n        Err(e) => {\n            error!(\"✗ NIST encryption failed: {:?}\", e);\n        }\n    }\n\n    if let Ok(Some(tag)) = aes.finish_blocking(ctx) {\n        info!(\"Tag:      {:02x}\", tag);\n        info!(\"Expected: {:02x}\", nist_expected_tag);\n        if tag == nist_expected_tag {\n            info!(\"✓ NIST tag MATCHES!\");\n        } else {\n            error!(\"✗ NIST tag MISMATCH!\");\n        }\n    }\n\n    info!(\"=== All AES-GCM tests complete ===\");\n\n    loop {\n        cortex_m::asm::wfi();\n    }\n}\n"
  },
  {
    "path": "examples/stm32wba/src/bin/aes_gmac.rs",
    "content": "//! AES-GMAC (Galois Message Authentication Code) Example\n//!\n//! Demonstrates message authentication without encryption using AES-GMAC mode.\n//! GMAC authenticates data integrity without providing confidentiality.\n//!\n//! # Cipher Mode: GMAC (Galois Message Authentication Code)\n//! - Provides authentication WITHOUT encryption\n//! - Data remains in plaintext but tampering is detected\n//! - Generates authentication tag for integrity verification\n//! - Uses the same GHASH operation as GCM\n//!\n//! # Use Cases\n//! - Authenticating packet headers in network protocols\n//! - Verifying integrity of publicly-readable metadata\n//! - Any scenario requiring authentication without encryption\n//! - Protocol message authentication\n//!\n//! # How It Works\n//! 1. Initialize with key and unique IV\n//! 2. Process header data (AAD only - no payload)\n//! 3. Generate authentication tag\n//! 4. Use tag to verify data integrity\n//!\n//! # Security Notes\n//! - IV/nonce must be unique for each authentication with same key\n//! - GMAC alone does NOT encrypt data - use GCM if confidentiality needed\n//! - Tag verification detects any tampering with the authenticated data\n\n#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_stm32::aes::{Aes, AesGmac, Direction};\nuse embassy_stm32::{bind_interrupts, peripherals};\nuse {defmt_rtt as _, panic_probe as _};\n\nbind_interrupts!(struct Irqs {\n    AES => embassy_stm32::aes::InterruptHandler<peripherals::AES>;\n});\n\n#[embassy_executor::main]\nasync fn main(_spawner: embassy_executor::Spawner) {\n    let p = embassy_stm32::init(Default::default());\n    info!(\"AES-GMAC Message Authentication Example\");\n\n    let mut aes = Aes::new_blocking(p.AES, Irqs);\n\n    // ========== GMAC Test Case (from ST HAL) ==========\n    // This test authenticates a 68-byte header message\n\n    // 256-bit key\n    let key: [u8; 32] = [\n        0x69, 0x1D, 0x3E, 0xE9, 0x09, 0xD7, 0xF5, 0x41, 0x67, 0xFD, 0x1C, 0xA0, 0xB5, 0xD7, 0x69, 0x08, 0x1F, 0x2B,\n        0xDE, 0x1A, 0xEE, 0x65, 0x5F, 0xDB, 0xAB, 0x80, 0xBD, 0x52, 0x95, 0xAE, 0x6B, 0xE7,\n    ];\n\n    // 12-byte IV (nonce)\n    let iv: [u8; 12] = [0xF0, 0x76, 0x1E, 0x8D, 0xCD, 0x3D, 0x00, 0x01, 0x76, 0xD4, 0x57, 0xED];\n\n    // Header message to authenticate (68 bytes - not block aligned)\n    let header: [u8; 68] = [\n        0xE2, 0x01, 0x06, 0xD7, 0xCD, 0x0D, 0xF0, 0x76, 0x1E, 0x8D, 0xCD, 0x3D, 0x88, 0xE5, 0x40, 0x00, 0x76, 0xD4,\n        0x57, 0xED, 0x08, 0x00, 0x0F, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C,\n        0x1D, 0x1E, 0x1F, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E,\n        0x2F, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x00, 0x03,\n    ];\n\n    // Expected tag from ST HAL test\n    let expected_tag: [u8; 16] = [\n        0x35, 0x21, 0x7C, 0x77, 0x4B, 0xBC, 0x31, 0xB6, 0x31, 0x66, 0xBC, 0xF9, 0xD4, 0xAB, 0xED, 0x07,\n    ];\n\n    info!(\"=== AES-GMAC Authentication ===\");\n    info!(\"Key (256-bit): {:02x}\", key[..16]);\n    info!(\"IV (12 bytes): {:02x}\", iv);\n    info!(\"Header: {} bytes\", header.len());\n\n    // Create GMAC cipher\n    let cipher = AesGmac::new(&key, &iv);\n    let mut ctx = aes.start(&cipher, Direction::Encrypt);\n\n    // Process header data (this is the data we're authenticating)\n    match aes.aad_blocking(&mut ctx, &header, true) {\n        Ok(()) => info!(\"Header processed for authentication\"),\n        Err(e) => {\n            error!(\"Header processing failed: {:?}\", e);\n            loop {\n                cortex_m::asm::wfi();\n            }\n        }\n    }\n\n    // No payload phase for GMAC - we're just authenticating, not encrypting\n\n    // Get authentication tag\n    match aes.finish_blocking(ctx) {\n        Ok(Some(tag)) => {\n            info!(\"Computed Tag: {:02x}\", tag);\n            info!(\"Expected Tag: {:02x}\", expected_tag);\n\n            if tag == expected_tag {\n                info!(\"GMAC Authentication PASSED!\");\n            } else {\n                error!(\"GMAC Tag mismatch!\");\n            }\n        }\n        Ok(None) => {\n            error!(\"No tag returned!\");\n        }\n        Err(e) => {\n            error!(\"Finish failed: {:?}\", e);\n        }\n    }\n\n    // ========== Simple GMAC Example ==========\n    info!(\"=== Simple GMAC Example ===\");\n\n    // 128-bit key for simplicity\n    let simple_key: [u8; 16] = [\n        0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,\n    ];\n\n    let simple_iv: [u8; 12] = [0xca, 0xfe, 0xba, 0xbe, 0xfa, 0xce, 0xdb, 0xad, 0xde, 0xca, 0xf8, 0x88];\n\n    // Message to authenticate\n    let message = b\"This message will be authenticated but NOT encrypted\";\n\n    info!(\"Message: \\\"{}\\\"\", core::str::from_utf8(message).unwrap_or(\"\"));\n    info!(\"Message length: {} bytes\", message.len());\n\n    let cipher = AesGmac::new(&simple_key, &simple_iv);\n    let mut ctx = aes.start(&cipher, Direction::Encrypt);\n\n    match aes.aad_blocking(&mut ctx, message, true) {\n        Ok(()) => info!(\"Message authenticated\"),\n        Err(e) => {\n            error!(\"Authentication failed: {:?}\", e);\n        }\n    }\n\n    if let Ok(Some(tag)) = aes.finish_blocking(ctx) {\n        info!(\"Auth Tag: {:02x}\", tag);\n        info!(\"Note: This tag can be sent with the message to verify integrity\");\n    }\n\n    info!(\"=== All GMAC tests complete ===\");\n\n    loop {\n        cortex_m::asm::wfi();\n    }\n}\n"
  },
  {
    "path": "examples/stm32wba/src/bin/ble_advertiser.rs",
    "content": "//! BLE Advertiser Example\n//!\n//! This example demonstrates the Phase 1 BLE stack implementation:\n//! - Initializes the BLE stack\n//! - Creates a simple GATT service with a characteristic\n//! - Starts BLE advertising\n//! - The device will appear as \"Embassy-WBA\" in BLE scanner apps\n//!\n//! Hardware: STM32WBA52 or compatible\n//!\n//! To test:\n//! 1. Flash this example to your STM32WBA board\n//! 2. Use a BLE scanner app (nRF Connect, LightBlue, etc.)\n//! 3. Look for \"Embassy-WBA\" in the scan results\n//! 4. Connect to see the GATT service\n\n#![no_std]\n#![no_main]\n\nuse core::cell::RefCell;\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_stm32::peripherals::RNG;\nuse embassy_stm32::rcc::{\n    AHB5Prescaler, AHBPrescaler, APBPrescaler, PllDiv, PllMul, PllPreDiv, PllSource, Sysclk, VoltageScale, mux,\n};\nuse embassy_stm32::rng::{self, Rng};\nuse embassy_stm32::{Config, bind_interrupts};\nuse embassy_stm32_wpan::Ble;\nuse embassy_stm32_wpan::gap::{AdvData, AdvParams, AdvType};\nuse embassy_stm32_wpan::gatt::{CharProperties, GattEventMask, GattServer, SecurityPermissions, ServiceType, Uuid};\nuse embassy_sync::blocking_mutex::Mutex;\nuse embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;\nuse static_cell::StaticCell;\nuse {defmt_rtt as _, panic_probe as _};\n\nbind_interrupts!(struct Irqs {\n    RNG => rng::InterruptHandler<embassy_stm32::peripherals::RNG>;\n});\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let mut config = Config::default();\n\n    // Configure PLL1 (required on WBA)\n    config.rcc.pll1 = Some(embassy_stm32::rcc::Pll {\n        source: PllSource::HSI,\n        prediv: PllPreDiv::DIV1,  // PLLM = 1 → HSI / 1 = 16 MHz\n        mul: PllMul::MUL30,       // PLLN = 30 → 16 MHz * 30 = 480 MHz VCO\n        divr: Some(PllDiv::DIV5), // PLLR = 5 → 96 MHz (Sysclk)\n        divq: None,\n        divp: Some(PllDiv::DIV30), // PLLP = 30 → 16 MHz (required for SAI)\n        frac: Some(0),\n    });\n\n    config.rcc.ahb_pre = AHBPrescaler::DIV1;\n    config.rcc.apb1_pre = APBPrescaler::DIV1;\n    config.rcc.apb2_pre = APBPrescaler::DIV1;\n    config.rcc.apb7_pre = APBPrescaler::DIV1;\n    config.rcc.ahb5_pre = AHB5Prescaler::DIV4;\n    config.rcc.voltage_scale = VoltageScale::RANGE1;\n    config.rcc.sys = Sysclk::PLL1_R;\n\n    // Configure RNG clock source to HSI (required for WBA)\n    config.rcc.mux.rngsel = mux::Rngsel::HSI;\n\n    let p = embassy_stm32::init(config);\n    info!(\"Embassy STM32WBA BLE Advertiser Example\");\n\n    // Initialize RNG (required by BLE stack)\n    static RNG: StaticCell<Mutex<CriticalSectionRawMutex, RefCell<Rng<'static, RNG>>>> = StaticCell::new();\n    let rng = RNG.init(Mutex::new(RefCell::new(Rng::new(p.RNG, Irqs))));\n    info!(\"RNG initialized\");\n\n    // Initialize BLE stack\n    let mut ble = Ble::new(rng);\n    ble.init().expect(\"BLE initialization failed\");\n    info!(\"BLE stack initialized\");\n\n    // Initialize GATT server\n    let mut gatt = GattServer::new();\n    gatt.init().expect(\"GATT initialization failed\");\n    info!(\"GATT server initialized\");\n\n    // Create a custom service (UUID: 0x1234)\n    let service_uuid = Uuid::from_u16(0x1234);\n    let service_handle = gatt\n        .add_service(service_uuid, ServiceType::Primary, 5)\n        .expect(\"Failed to add service\");\n    info!(\"Created service with handle: 0x{:04X}\", service_handle.0);\n\n    // Add a read/write characteristic (UUID: 0x5678)\n    let char_uuid = Uuid::from_u16(0x5678);\n    let char_properties = CharProperties::READ | CharProperties::WRITE | CharProperties::NOTIFY;\n    let char_handle = gatt\n        .add_characteristic(\n            service_handle,\n            char_uuid,\n            20, // Max 20 bytes\n            char_properties,\n            SecurityPermissions::NONE,\n            GattEventMask::ATTRIBUTE_MODIFIED,\n            0,    // No encryption\n            true, // Variable length\n        )\n        .expect(\"Failed to add characteristic\");\n    info!(\"Created characteristic with handle: 0x{:04X}\", char_handle.0);\n\n    // Set initial characteristic value\n    let initial_value = b\"Hello BLE!\";\n    gatt.update_characteristic_value(service_handle, char_handle, 0, initial_value)\n        .expect(\"Failed to set characteristic value\");\n    info!(\"Set initial characteristic value\");\n\n    // Create advertising data\n    let mut adv_data = AdvData::new();\n    adv_data\n        .add_flags(0x06) // General discoverable, BR/EDR not supported\n        .expect(\"Failed to add flags\");\n    adv_data.add_name(\"Embassy-WBA\").expect(\"Failed to add name\");\n    adv_data\n        .add_service_uuid_16(0x1234) // Advertise our custom service\n        .expect(\"Failed to add service UUID\");\n\n    info!(\"Advertising data created ({} bytes)\", adv_data.len());\n\n    // Configure advertising parameters\n    let adv_params = AdvParams {\n        interval_min: 0x0080, // 80 ms\n        interval_max: 0x0080, // 80 ms\n        adv_type: AdvType::ConnectableUndirected,\n        ..AdvParams::default()\n    };\n\n    // Start advertising\n    let mut advertiser = ble.advertiser();\n    advertiser\n        .start(adv_params, adv_data, None)\n        .expect(\"Failed to start advertising\");\n\n    info!(\"BLE advertising started!\");\n    info!(\"Device is visible as 'Embassy-WBA'\");\n    info!(\"Use a BLE scanner app to discover and connect\");\n\n    // Main loop - handle BLE events\n    loop {\n        let event = ble.read_event().await;\n        info!(\"BLE Event: {:?}\", event);\n\n        // In a real application, you would handle connection events,\n        // characteristic writes, etc. here\n    }\n}\n"
  },
  {
    "path": "examples/stm32wba/src/bin/ble_central.rs",
    "content": "//! BLE Central Example\n//!\n//! This example demonstrates BLE central role functionality:\n//! - Scans for nearby BLE peripherals\n//! - Connects to a device (either specific address or first discovered)\n//! - Handles connection and disconnection events\n//! - Demonstrates connection parameter management\n//!\n//! Hardware: STM32WBA52 or compatible\n//!\n//! To test:\n//! 1. Flash this example to your STM32WBA board\n//! 2. Have a BLE peripheral device advertising nearby\n//! 3. Observe the scan, connection, and event handling\n\n#![no_std]\n#![no_main]\n\nuse core::cell::RefCell;\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_stm32::peripherals::RNG;\nuse embassy_stm32::rcc::{\n    AHB5Prescaler, AHBPrescaler, APBPrescaler, PllDiv, PllMul, PllPreDiv, PllSource, Sysclk, VoltageScale, mux,\n};\nuse embassy_stm32::rng::{self, Rng};\nuse embassy_stm32::{Config, bind_interrupts};\nuse embassy_stm32_wpan::gap::{ConnectionInitParams, GapEvent, ParsedAdvData, ScanParams, ScanType};\nuse embassy_stm32_wpan::hci::event::EventParams;\nuse embassy_stm32_wpan::{Ble, ble_runner};\nuse embassy_sync::blocking_mutex::Mutex;\nuse embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;\nuse static_cell::StaticCell;\nuse {defmt_rtt as _, panic_probe as _};\n\nbind_interrupts!(struct Irqs {\n    RNG => rng::InterruptHandler<embassy_stm32::peripherals::RNG>;\n});\n\n/// BLE runner task - drives the BLE stack sequencer\n#[embassy_executor::task]\nasync fn ble_runner_task() {\n    ble_runner().await\n}\n\n/// Target device name to connect to (set to None to connect to first discovered device)\nconst TARGET_DEVICE_NAME: Option<&str> = None; // e.g., Some(\"Embassy-Peripheral\")\n\n/// Minimum RSSI to consider a device (helps filter out far away devices)\nconst MIN_RSSI: i8 = -80;\n\n#[embassy_executor::main]\nasync fn main(spawner: Spawner) {\n    let mut config = Config::default();\n\n    // Configure PLL1 (required on WBA)\n    config.rcc.pll1 = Some(embassy_stm32::rcc::Pll {\n        source: PllSource::HSI,\n        prediv: PllPreDiv::DIV1,\n        mul: PllMul::MUL30,\n        divr: Some(PllDiv::DIV5),\n        divq: None,\n        divp: Some(PllDiv::DIV30),\n        frac: Some(0),\n    });\n\n    config.rcc.ahb_pre = AHBPrescaler::DIV1;\n    config.rcc.apb1_pre = APBPrescaler::DIV1;\n    config.rcc.apb2_pre = APBPrescaler::DIV1;\n    config.rcc.apb7_pre = APBPrescaler::DIV1;\n    config.rcc.ahb5_pre = AHB5Prescaler::DIV4;\n    config.rcc.voltage_scale = VoltageScale::RANGE1;\n    config.rcc.sys = Sysclk::PLL1_R;\n    config.rcc.mux.rngsel = mux::Rngsel::HSI;\n\n    let p = embassy_stm32::init(config);\n    info!(\"Embassy STM32WBA BLE Central Example\");\n\n    // Initialize RNG (required by BLE stack)\n    static RNG: StaticCell<Mutex<CriticalSectionRawMutex, RefCell<Rng<'static, RNG>>>> = StaticCell::new();\n    let rng = RNG.init(Mutex::new(RefCell::new(Rng::new(p.RNG, Irqs))));\n    info!(\"RNG initialized\");\n\n    // Initialize BLE stack\n    let mut ble = Ble::new(rng);\n    ble.init().expect(\"BLE initialization failed\");\n    info!(\"BLE stack initialized\");\n\n    // Spawn the BLE runner task (required for proper BLE operation)\n    spawner.spawn(ble_runner_task().expect(\"Failed to create BLE runner task\"));\n\n    // State machine for central role\n    let mut state = CentralState::Scanning;\n\n    // Configure scan parameters - active scanning for names\n    let scan_params = ScanParams::new()\n        .with_scan_type(ScanType::Active)\n        .with_interval(0x0050) // 50ms\n        .with_window(0x0030) // 30ms\n        .with_filter_duplicates(false); // Want to see devices multiple times to catch scan responses\n\n    // Start scanning\n    {\n        let mut scanner = ble.scanner();\n        scanner.start(scan_params.clone()).expect(\"Failed to start scanning\");\n    }\n\n    info!(\"=== BLE Central Started ===\");\n    if let Some(name) = TARGET_DEVICE_NAME {\n        info!(\"Looking for device: \\\"{}\\\"\", name);\n    } else {\n        info!(\"Will connect to first suitable device found\");\n    }\n    info!(\"\");\n\n    // Main event loop\n    loop {\n        let event = ble.read_event().await;\n\n        match state {\n            CentralState::Scanning => {\n                // Process advertising reports\n                if let EventParams::LeAdvertisingReport { reports } = &event.params {\n                    for report in reports.iter() {\n                        // Skip weak signals\n                        if report.rssi < MIN_RSSI {\n                            continue;\n                        }\n\n                        // Parse advertising data\n                        let parsed = ParsedAdvData::parse(&report.data);\n\n                        // Check if this device matches our criteria\n                        let should_connect = if let Some(target_name) = TARGET_DEVICE_NAME {\n                            // Looking for specific name\n                            parsed.name == Some(target_name)\n                        } else {\n                            // Connect to any device that has a name (likely a real peripheral)\n                            parsed.name.is_some()\n                        };\n\n                        if should_connect {\n                            info!(\"=== Found Target Device ===\");\n                            info!(\n                                \"  Address: {:02X}:{:02X}:{:02X}:{:02X}:{:02X}:{:02X}\",\n                                report.address.0[5],\n                                report.address.0[4],\n                                report.address.0[3],\n                                report.address.0[2],\n                                report.address.0[1],\n                                report.address.0[0]\n                            );\n                            if let Some(name) = parsed.name {\n                                info!(\"  Name: \\\"{}\\\"\", name);\n                            }\n                            info!(\"  RSSI: {} dBm\", report.rssi);\n\n                            // Stop scanning\n                            {\n                                let mut scanner = ble.scanner();\n                                scanner.stop().expect(\"Failed to stop scanning\");\n                            }\n                            info!(\"Scanning stopped\");\n\n                            // Initiate connection\n                            let conn_params = ConnectionInitParams {\n                                peer_address_type: report.address_type,\n                                peer_address: report.address,\n                                ..ConnectionInitParams::default()\n                            };\n\n                            info!(\"Initiating connection...\");\n                            if let Err(e) = ble.connect(&conn_params) {\n                                error!(\"Failed to initiate connection: {:?}\", e);\n                                // Restart scanning on failure\n                                let mut scanner = ble.scanner();\n                                scanner.start(scan_params.clone()).expect(\"Failed to restart scanning\");\n                            } else {\n                                state = CentralState::Connecting;\n                            }\n                            break;\n                        } else if parsed.name.is_some() {\n                            // Log other named devices we see\n                            debug!(\n                                \"Found device: \\\"{}\\\" at {} dBm\",\n                                parsed.name.unwrap_or(\"?\"),\n                                report.rssi\n                            );\n                        }\n                    }\n                }\n            }\n\n            CentralState::Connecting => {\n                // Wait for connection complete event\n                if let Some(gap_event) = ble.process_event(&event) {\n                    match gap_event {\n                        GapEvent::Connected(conn) => {\n                            info!(\"=== CONNECTED ===\");\n                            info!(\"  Handle: 0x{:04X}\", conn.handle.0);\n                            info!(\n                                \"  Role: {}\",\n                                match conn.role {\n                                    embassy_stm32_wpan::gap::ConnectionRole::Central => \"Central\",\n                                    embassy_stm32_wpan::gap::ConnectionRole::Peripheral => \"Peripheral\",\n                                }\n                            );\n                            info!(\n                                \"  Interval: {} ({}ms)\",\n                                conn.params.interval,\n                                (conn.params.interval as u32 * 125) / 100\n                            );\n                            info!(\"  Latency: {}\", conn.params.latency);\n                            info!(\n                                \"  Timeout: {} ({}ms)\",\n                                conn.params.supervision_timeout,\n                                conn.params.supervision_timeout as u32 * 10\n                            );\n\n                            state = CentralState::Connected;\n                            info!(\"\");\n                            info!(\"Connection established! As a central, you can now:\");\n                            info!(\"  - Discover services (not implemented in this example)\");\n                            info!(\"  - Read/write characteristics\");\n                            info!(\"  - Subscribe to notifications\");\n                        }\n\n                        GapEvent::Disconnected { handle, reason } => {\n                            error!(\"Connection failed or disconnected during setup\");\n                            error!(\"  Handle: 0x{:04X}, Reason: 0x{:02X}\", handle.0, reason);\n\n                            // Go back to scanning\n                            let mut scanner = ble.scanner();\n                            scanner.start(scan_params.clone()).expect(\"Failed to restart scanning\");\n                            state = CentralState::Scanning;\n                            info!(\"Restarted scanning...\");\n                        }\n\n                        _ => {}\n                    }\n                }\n            }\n\n            CentralState::Connected => {\n                // Handle events while connected\n                if let Some(gap_event) = ble.process_event(&event) {\n                    match gap_event {\n                        GapEvent::Disconnected { handle, reason } => {\n                            info!(\"=== DISCONNECTED ===\");\n                            info!(\"  Handle: 0x{:04X}\", handle.0);\n                            info!(\"  Reason: 0x{:02X} ({})\", reason, disconnect_reason_str(reason));\n\n                            // Go back to scanning\n                            let mut scanner = ble.scanner();\n                            scanner.start(scan_params.clone()).expect(\"Failed to restart scanning\");\n                            state = CentralState::Scanning;\n                            info!(\"Restarted scanning...\");\n                        }\n\n                        GapEvent::ConnectionParamsUpdated {\n                            handle,\n                            interval,\n                            latency,\n                            supervision_timeout,\n                        } => {\n                            info!(\"=== CONNECTION PARAMS UPDATED ===\");\n                            info!(\"  Handle: 0x{:04X}\", handle.0);\n                            info!(\"  New Interval: {} ({}ms)\", interval, (interval as u32 * 125) / 100);\n                            info!(\"  New Latency: {}\", latency);\n                            info!(\n                                \"  New Timeout: {} ({}ms)\",\n                                supervision_timeout,\n                                supervision_timeout as u32 * 10\n                            );\n                        }\n\n                        GapEvent::PhyUpdated { handle, tx_phy, rx_phy } => {\n                            info!(\"=== PHY UPDATED ===\");\n                            info!(\"  Handle: 0x{:04X}\", handle.0);\n                            info!(\"  TX PHY: {:?}\", tx_phy);\n                            info!(\"  RX PHY: {:?}\", rx_phy);\n                        }\n\n                        GapEvent::DataLengthChanged {\n                            handle,\n                            max_tx_octets,\n                            max_rx_octets,\n                            ..\n                        } => {\n                            info!(\"=== DATA LENGTH CHANGED ===\");\n                            info!(\"  Handle: 0x{:04X}\", handle.0);\n                            info!(\"  Max TX: {} bytes\", max_tx_octets);\n                            info!(\"  Max RX: {} bytes\", max_rx_octets);\n                        }\n\n                        _ => {}\n                    }\n                }\n\n                // Log other interesting events\n                match &event.params {\n                    EventParams::AttExchangeMtuResponse {\n                        conn_handle,\n                        server_mtu,\n                    } => {\n                        info!(\"MTU Exchange: conn 0x{:04X}, MTU={}\", conn_handle.0, server_mtu);\n                    }\n                    _ => {}\n                }\n            }\n        }\n    }\n}\n\n/// State machine for central role\n#[derive(Debug, Clone, Copy, PartialEq, Eq)]\nenum CentralState {\n    /// Scanning for devices\n    Scanning,\n    /// Connection initiated, waiting for connection complete\n    Connecting,\n    /// Connected to a peripheral\n    Connected,\n}\n\n/// Convert disconnect reason code to human-readable string\nfn disconnect_reason_str(reason: u8) -> &'static str {\n    match reason {\n        0x08 => \"Connection Timeout\",\n        0x13 => \"Remote User Terminated\",\n        0x14 => \"Remote Low Resources\",\n        0x15 => \"Remote Power Off\",\n        0x16 => \"Local Host Terminated\",\n        0x1A => \"Unsupported Remote Feature\",\n        0x3B => \"Unacceptable Connection Parameters\",\n        0x3D => \"MIC Failure\",\n        0x3E => \"Connection Failed to Establish\",\n        _ => \"Unknown\",\n    }\n}\n"
  },
  {
    "path": "examples/stm32wba/src/bin/ble_gatt_server.rs",
    "content": "//! BLE GATT Server Example with Notifications\n//!\n//! This example demonstrates full GATT server functionality:\n//! - Creates a custom service with read/write/notify characteristics\n//! - Handles characteristic writes from clients\n//! - Sends notifications when values change\n//! - Tracks CCCD (notification enable/disable) state\n//!\n//! Hardware: STM32WBA52 or compatible\n//!\n//! To test:\n//! 1. Flash this example to your STM32WBA board\n//! 2. Connect with nRF Connect or similar app\n//! 3. Enable notifications on the characteristic\n//! 4. Write values to the characteristic\n//! 5. Observe notifications being sent back\n\n#![no_std]\n#![no_main]\n\nuse core::cell::RefCell;\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_stm32::peripherals::RNG;\nuse embassy_stm32::rcc::{\n    AHB5Prescaler, AHBPrescaler, APBPrescaler, PllDiv, PllMul, PllPreDiv, PllSource, Sysclk, VoltageScale, mux,\n};\nuse embassy_stm32::rng::{self, Rng};\nuse embassy_stm32::{Config, bind_interrupts};\nuse embassy_stm32_wpan::gap::{AdvData, AdvParams, AdvType, GapEvent};\nuse embassy_stm32_wpan::gatt::{\n    CHAR_VALUE_HANDLE_OFFSET, CccdValue, CharProperties, CharacteristicHandle, GattEventMask, GattServer,\n    SecurityPermissions, ServiceHandle, ServiceType, Uuid, is_cccd_handle, is_value_handle,\n};\nuse embassy_stm32_wpan::hci::event::EventParams;\nuse embassy_stm32_wpan::{Ble, ble_runner};\nuse embassy_sync::blocking_mutex::Mutex;\nuse embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;\nuse static_cell::StaticCell;\nuse {defmt_rtt as _, panic_probe as _};\n\nbind_interrupts!(struct Irqs {\n    RNG => rng::InterruptHandler<embassy_stm32::peripherals::RNG>;\n});\n\n/// BLE runner task - drives the BLE stack sequencer\n#[embassy_executor::task]\nasync fn ble_runner_task() {\n    ble_runner().await\n}\n\n/// Custom service UUID (use your own for production)\nconst CUSTOM_SERVICE_UUID: u16 = 0xABCD;\n/// Read/Write/Notify characteristic UUID\nconst DATA_CHAR_UUID: u16 = 0xABCE;\n\n/// Application state for tracking GATT handles and CCCD state\nstruct AppState {\n    service_handle: ServiceHandle,\n    data_char_handle: CharacteristicHandle,\n    notifications_enabled: bool,\n    current_conn_handle: Option<u16>,\n    counter: u8,\n}\n\n#[embassy_executor::main]\nasync fn main(spawner: Spawner) {\n    let mut config = Config::default();\n\n    // Configure PLL1 (required on WBA)\n    config.rcc.pll1 = Some(embassy_stm32::rcc::Pll {\n        source: PllSource::HSI,\n        prediv: PllPreDiv::DIV1,\n        mul: PllMul::MUL30,\n        divr: Some(PllDiv::DIV5),\n        divq: None,\n        divp: Some(PllDiv::DIV30),\n        frac: Some(0),\n    });\n\n    config.rcc.ahb_pre = AHBPrescaler::DIV1;\n    config.rcc.apb1_pre = APBPrescaler::DIV1;\n    config.rcc.apb2_pre = APBPrescaler::DIV1;\n    config.rcc.apb7_pre = APBPrescaler::DIV1;\n    config.rcc.ahb5_pre = AHB5Prescaler::DIV4;\n    config.rcc.voltage_scale = VoltageScale::RANGE1;\n    config.rcc.sys = Sysclk::PLL1_R;\n    config.rcc.mux.rngsel = mux::Rngsel::HSI;\n\n    let p = embassy_stm32::init(config);\n    info!(\"Embassy STM32WBA GATT Server Example\");\n\n    // Initialize RNG (required by BLE stack)\n    static RNG: StaticCell<Mutex<CriticalSectionRawMutex, RefCell<Rng<'static, RNG>>>> = StaticCell::new();\n    let rng = RNG.init(Mutex::new(RefCell::new(Rng::new(p.RNG, Irqs))));\n    info!(\"RNG initialized\");\n\n    // Initialize BLE stack\n    let mut ble = Ble::new(rng);\n    ble.init().expect(\"BLE initialization failed\");\n    info!(\"BLE stack initialized\");\n\n    // Spawn the BLE runner task (required for proper BLE operation)\n    spawner.spawn(ble_runner_task().expect(\"Failed to create BLE runner task\"));\n\n    // Initialize GATT server\n    let mut gatt = GattServer::new();\n    gatt.init().expect(\"GATT initialization failed\");\n\n    // Create custom service\n    let service_uuid = Uuid::from_u16(CUSTOM_SERVICE_UUID);\n    let service_handle = gatt\n        .add_service(service_uuid, ServiceType::Primary, 6)\n        .expect(\"Failed to add service\");\n    info!(\"Service created: handle 0x{:04X}\", service_handle.0);\n\n    // Add data characteristic with read/write/notify\n    let char_uuid = Uuid::from_u16(DATA_CHAR_UUID);\n    let char_properties = CharProperties::READ | CharProperties::WRITE | CharProperties::NOTIFY;\n    let data_char_handle = gatt\n        .add_characteristic(\n            service_handle,\n            char_uuid,\n            32, // Max 32 bytes\n            char_properties,\n            SecurityPermissions::NONE,\n            GattEventMask::ATTRIBUTE_MODIFIED,\n            0,\n            true, // Variable length\n        )\n        .expect(\"Failed to add characteristic\");\n    info!(\"Characteristic created: handle 0x{:04X}\", data_char_handle.0);\n    info!(\n        \"  Value handle: 0x{:04X}\",\n        data_char_handle.0 + CHAR_VALUE_HANDLE_OFFSET\n    );\n    info!(\"  CCCD handle: 0x{:04X}\", data_char_handle.0 + 2);\n\n    // Set initial value\n    let initial_value = b\"Hello!\";\n    gatt.update_characteristic_value(service_handle, data_char_handle, 0, initial_value)\n        .expect(\"Failed to set initial value\");\n\n    // Application state\n    let mut state = AppState {\n        service_handle,\n        data_char_handle,\n        notifications_enabled: false,\n        current_conn_handle: None,\n        counter: 0,\n    };\n\n    // Create advertising data\n    let mut adv_data = AdvData::new();\n    adv_data.add_flags(0x06).expect(\"Failed to add flags\");\n    adv_data.add_name(\"Embassy-GATT\").expect(\"Failed to add name\");\n    adv_data\n        .add_service_uuid_16(CUSTOM_SERVICE_UUID)\n        .expect(\"Failed to add service UUID\");\n\n    let adv_params = AdvParams {\n        interval_min: 0x0050,\n        interval_max: 0x0050,\n        adv_type: AdvType::ConnectableUndirected,\n        ..AdvParams::default()\n    };\n\n    // Start advertising\n    {\n        let mut advertiser = ble.advertiser();\n        advertiser\n            .start(adv_params.clone(), adv_data.clone(), None)\n            .expect(\"Failed to start advertising\");\n    }\n\n    info!(\"GATT Server started as 'Embassy-GATT'\");\n    info!(\"Waiting for connections...\");\n\n    // Main event loop\n    loop {\n        let event = ble.read_event().await;\n\n        // Process GAP events (connections)\n        if let Some(gap_event) = ble.process_event(&event) {\n            match gap_event {\n                GapEvent::Connected(conn) => {\n                    info!(\"Connected: handle 0x{:04X}\", conn.handle.0);\n                    state.current_conn_handle = Some(conn.handle.0);\n                    state.notifications_enabled = false; // Reset on new connection\n                }\n                GapEvent::Disconnected { handle, reason } => {\n                    info!(\"Disconnected: handle 0x{:04X}, reason 0x{:02X}\", handle.0, reason);\n                    state.current_conn_handle = None;\n                    state.notifications_enabled = false;\n\n                    // Restart advertising\n                    let mut advertiser = ble.advertiser();\n                    let _ = advertiser.start(adv_params.clone(), adv_data.clone(), None);\n                    info!(\"Advertising restarted\");\n                }\n                _ => {}\n            }\n        }\n\n        // Process GATT events\n        match &event.params {\n            EventParams::GattAttributeModified {\n                conn_handle,\n                attr_handle,\n                data,\n                ..\n            } => {\n                info!(\n                    \"Attribute modified: conn 0x{:04X}, attr 0x{:04X}, {} bytes\",\n                    conn_handle.0,\n                    attr_handle,\n                    data.len()\n                );\n\n                // Check if this is a CCCD write (notification enable/disable)\n                if is_cccd_handle(state.data_char_handle.0, *attr_handle) {\n                    let cccd = CccdValue::from_bytes(data);\n                    state.notifications_enabled = cccd.notifications;\n                    info!(\n                        \"CCCD updated: notifications={}, indications={}\",\n                        cccd.notifications, cccd.indications\n                    );\n\n                    if state.notifications_enabled {\n                        info!(\"Notifications ENABLED - will send updates\");\n                    } else {\n                        info!(\"Notifications DISABLED\");\n                    }\n                }\n                // Check if this is a characteristic value write\n                else if is_value_handle(state.data_char_handle.0, *attr_handle) {\n                    info!(\"Characteristic value written: {:?}\", data.as_slice());\n\n                    // Echo the data back as a notification if enabled\n                    if state.notifications_enabled {\n                        if let Some(conn) = state.current_conn_handle {\n                            // Increment counter and append to response\n                            state.counter = state.counter.wrapping_add(1);\n                            let mut response: heapless::Vec<u8, 33> = heapless::Vec::new();\n                            let _ = response.extend_from_slice(data);\n                            let _ = response.push(state.counter);\n\n                            match gatt.notify(conn, state.service_handle, state.data_char_handle, &response) {\n                                Ok(()) => {\n                                    info!(\"Notification sent: {} bytes\", response.len());\n                                }\n                                Err(e) => {\n                                    error!(\"Failed to send notification: {:?}\", e);\n                                }\n                            }\n                        }\n                    }\n                }\n            }\n\n            EventParams::GattNotificationComplete {\n                conn_handle,\n                attr_handle,\n            } => {\n                info!(\n                    \"Notification complete: conn 0x{:04X}, attr 0x{:04X}\",\n                    conn_handle.0, attr_handle\n                );\n            }\n\n            EventParams::AttExchangeMtuResponse {\n                conn_handle,\n                server_mtu,\n            } => {\n                info!(\"MTU exchanged: conn 0x{:04X}, MTU={}\", conn_handle.0, server_mtu);\n                // Update connection MTU\n                if let Some(conn) = ble.get_connection_mut(*conn_handle) {\n                    conn.update_mtu(*server_mtu);\n                }\n            }\n\n            _ => {\n                // Log other events at debug level\n                debug!(\"Event: {:?}\", event);\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "examples/stm32wba/src/bin/ble_peripheral_connect.rs",
    "content": "//! BLE Peripheral with Connection Handling Example\n//!\n//! This example demonstrates BLE connection management:\n//! - Advertises as a connectable peripheral\n//! - Handles connection and disconnection events\n//! - Tracks active connections\n//! - Allows disconnection via button press (if available)\n//!\n//! Hardware: STM32WBA52 or compatible\n//!\n//! To test:\n//! 1. Flash this example to your STM32WBA board\n//! 2. Use a BLE scanner app (nRF Connect, LightBlue, etc.)\n//! 3. Connect to \"Embassy-Peripheral\"\n//! 4. Observe connection/disconnection events in logs\n\n#![no_std]\n#![no_main]\n\nuse core::cell::RefCell;\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_stm32::peripherals::RNG;\nuse embassy_stm32::rcc::{\n    AHB5Prescaler, AHBPrescaler, APBPrescaler, PllDiv, PllMul, PllPreDiv, PllSource, Sysclk, VoltageScale, mux,\n};\nuse embassy_stm32::rng::{self, Rng};\nuse embassy_stm32::{Config, bind_interrupts};\nuse embassy_stm32_wpan::gap::{AdvData, AdvParams, AdvType, GapEvent};\nuse embassy_stm32_wpan::gatt::{CharProperties, GattEventMask, GattServer, SecurityPermissions, ServiceType, Uuid};\nuse embassy_stm32_wpan::{Ble, ble_runner};\nuse embassy_sync::blocking_mutex::Mutex;\nuse embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;\nuse static_cell::StaticCell;\nuse {defmt_rtt as _, panic_probe as _};\n\nbind_interrupts!(struct Irqs {\n    RNG => rng::InterruptHandler<embassy_stm32::peripherals::RNG>;\n});\n\n/// BLE runner task - drives the BLE stack sequencer\n#[embassy_executor::task]\nasync fn ble_runner_task() {\n    ble_runner().await\n}\n\n#[embassy_executor::main]\nasync fn main(spawner: Spawner) {\n    let mut config = Config::default();\n\n    // Configure PLL1 (required on WBA)\n    config.rcc.pll1 = Some(embassy_stm32::rcc::Pll {\n        source: PllSource::HSI,\n        prediv: PllPreDiv::DIV1,\n        mul: PllMul::MUL30,\n        divr: Some(PllDiv::DIV5),\n        divq: None,\n        divp: Some(PllDiv::DIV30),\n        frac: Some(0),\n    });\n\n    config.rcc.ahb_pre = AHBPrescaler::DIV1;\n    config.rcc.apb1_pre = APBPrescaler::DIV1;\n    config.rcc.apb2_pre = APBPrescaler::DIV1;\n    config.rcc.apb7_pre = APBPrescaler::DIV1;\n    config.rcc.ahb5_pre = AHB5Prescaler::DIV4;\n    config.rcc.voltage_scale = VoltageScale::RANGE1;\n    config.rcc.sys = Sysclk::PLL1_R;\n    config.rcc.mux.rngsel = mux::Rngsel::HSI;\n\n    let p = embassy_stm32::init(config);\n    info!(\"Embassy STM32WBA BLE Peripheral Connection Example\");\n\n    // Initialize RNG (required by BLE stack)\n    static RNG: StaticCell<Mutex<CriticalSectionRawMutex, RefCell<Rng<'static, RNG>>>> = StaticCell::new();\n    let rng = RNG.init(Mutex::new(RefCell::new(Rng::new(p.RNG, Irqs))));\n    info!(\"RNG initialized\");\n\n    // Initialize BLE stack\n    let mut ble = Ble::new(rng);\n    ble.init().expect(\"BLE initialization failed\");\n    info!(\"BLE stack initialized\");\n\n    // Spawn the BLE runner task (required for proper BLE operation)\n    spawner.spawn(ble_runner_task().expect(\"Failed to create BLE runner task\"));\n\n    // Initialize GATT server with a simple service\n    let mut gatt = GattServer::new();\n    gatt.init().expect(\"GATT initialization failed\");\n\n    // Create a simple service for demonstration\n    let service_uuid = Uuid::from_u16(0x180F); // Battery Service UUID\n    let service_handle = gatt\n        .add_service(service_uuid, ServiceType::Primary, 4)\n        .expect(\"Failed to add service\");\n\n    // Add battery level characteristic\n    let char_uuid = Uuid::from_u16(0x2A19); // Battery Level UUID\n    let char_handle = gatt\n        .add_characteristic(\n            service_handle,\n            char_uuid,\n            1, // 1 byte for battery level\n            CharProperties::READ | CharProperties::NOTIFY,\n            SecurityPermissions::NONE,\n            GattEventMask::ATTRIBUTE_MODIFIED,\n            0,\n            false,\n        )\n        .expect(\"Failed to add characteristic\");\n\n    // Set initial battery level (100%)\n    gatt.update_characteristic_value(service_handle, char_handle, 0, &[100])\n        .expect(\"Failed to set battery level\");\n\n    info!(\"GATT service created (Battery Service)\");\n\n    // Create advertising data\n    let mut adv_data = AdvData::new();\n    adv_data.add_flags(0x06).expect(\"Failed to add flags\");\n    adv_data.add_name(\"Embassy-Peripheral\").expect(\"Failed to add name\");\n    adv_data\n        .add_service_uuid_16(0x180F)\n        .expect(\"Failed to add service UUID\");\n\n    // Configure advertising parameters for connectable advertising\n    let adv_params = AdvParams {\n        interval_min: 0x0050, // 50 ms\n        interval_max: 0x0050,\n        adv_type: AdvType::ConnectableUndirected,\n        ..AdvParams::default()\n    };\n\n    // Start advertising\n    {\n        let mut advertiser = ble.advertiser();\n        advertiser\n            .start(adv_params.clone(), adv_data.clone(), None)\n            .expect(\"Failed to start advertising\");\n    }\n\n    info!(\"BLE advertising started as 'Embassy-Peripheral'\");\n    info!(\"Waiting for connections...\");\n\n    // Main event loop\n    loop {\n        let event = ble.read_event().await;\n\n        // Process the event and update connection state\n        if let Some(gap_event) = ble.process_event(&event) {\n            match gap_event {\n                GapEvent::Connected(conn) => {\n                    info!(\"=== CONNECTION ESTABLISHED ===\");\n                    info!(\"  Handle: 0x{:04X}\", conn.handle.0);\n                    info!(\n                        \"  Role: {}\",\n                        match conn.role {\n                            embassy_stm32_wpan::gap::ConnectionRole::Central => \"Central\",\n                            embassy_stm32_wpan::gap::ConnectionRole::Peripheral => \"Peripheral\",\n                        }\n                    );\n                    info!(\n                        \"  Peer Address: {:02X}:{:02X}:{:02X}:{:02X}:{:02X}:{:02X}\",\n                        conn.peer_address.0[5],\n                        conn.peer_address.0[4],\n                        conn.peer_address.0[3],\n                        conn.peer_address.0[2],\n                        conn.peer_address.0[1],\n                        conn.peer_address.0[0]\n                    );\n                    info!(\n                        \"  Interval: {} ({}ms)\",\n                        conn.params.interval,\n                        (conn.params.interval as u32 * 125) / 100\n                    );\n                    info!(\"  Latency: {}\", conn.params.latency);\n                    info!(\n                        \"  Timeout: {} ({}ms)\",\n                        conn.params.supervision_timeout,\n                        conn.params.supervision_timeout as u32 * 10\n                    );\n                    info!(\"  Active connections: {}\", ble.connections().count());\n\n                    // Note: Advertising typically stops automatically on connection\n                    // If you want to support multiple connections, restart advertising here\n                }\n\n                GapEvent::Disconnected { handle, reason } => {\n                    info!(\"=== DISCONNECTION ===\");\n                    info!(\"  Handle: 0x{:04X}\", handle.0);\n                    info!(\"  Reason: 0x{:02X} ({})\", reason, disconnect_reason_str(reason));\n                    info!(\"  Active connections: {}\", ble.connections().count());\n\n                    // Restart advertising after disconnection\n                    info!(\"Restarting advertising...\");\n                    let mut advertiser = ble.advertiser();\n                    if let Err(e) = advertiser.start(adv_params.clone(), adv_data.clone(), None) {\n                        error!(\"Failed to restart advertising: {:?}\", e);\n                    } else {\n                        info!(\"Advertising restarted\");\n                    }\n                }\n\n                GapEvent::ConnectionParamsUpdated {\n                    handle,\n                    interval,\n                    latency,\n                    supervision_timeout,\n                } => {\n                    info!(\"=== CONNECTION PARAMS UPDATED ===\");\n                    info!(\"  Handle: 0x{:04X}\", handle.0);\n                    info!(\"  New Interval: {} ({}ms)\", interval, (interval as u32 * 125) / 100);\n                    info!(\"  New Latency: {}\", latency);\n                    info!(\n                        \"  New Timeout: {} ({}ms)\",\n                        supervision_timeout,\n                        supervision_timeout as u32 * 10\n                    );\n                }\n\n                GapEvent::PhyUpdated { handle, tx_phy, rx_phy } => {\n                    info!(\"=== PHY UPDATED ===\");\n                    info!(\"  Handle: 0x{:04X}\", handle.0);\n                    info!(\"  TX PHY: {:?}\", tx_phy);\n                    info!(\"  RX PHY: {:?}\", rx_phy);\n                }\n\n                GapEvent::DataLengthChanged {\n                    handle,\n                    max_tx_octets,\n                    max_rx_octets,\n                    ..\n                } => {\n                    info!(\"=== DATA LENGTH CHANGED ===\");\n                    info!(\"  Handle: 0x{:04X}\", handle.0);\n                    info!(\"  Max TX: {} bytes\", max_tx_octets);\n                    info!(\"  Max RX: {} bytes\", max_rx_octets);\n                }\n            }\n        } else {\n            // Log other events for debugging\n            debug!(\"Other BLE Event: {:?}\", event);\n        }\n    }\n}\n\n/// Convert disconnect reason code to human-readable string\nfn disconnect_reason_str(reason: u8) -> &'static str {\n    match reason {\n        0x08 => \"Connection Timeout\",\n        0x13 => \"Remote User Terminated\",\n        0x14 => \"Remote Low Resources\",\n        0x15 => \"Remote Power Off\",\n        0x16 => \"Local Host Terminated\",\n        0x1A => \"Unsupported Remote Feature\",\n        0x3B => \"Unacceptable Connection Parameters\",\n        0x3D => \"MIC Failure\",\n        0x3E => \"Connection Failed to Establish\",\n        _ => \"Unknown\",\n    }\n}\n"
  },
  {
    "path": "examples/stm32wba/src/bin/ble_scanner.rs",
    "content": "//! BLE Scanner Example\n//!\n//! This example demonstrates BLE scanning (observer role):\n//! - Scans for nearby BLE devices\n//! - Parses advertising data (name, UUIDs, manufacturer data)\n//! - Displays discovered devices with RSSI\n//!\n//! Hardware: STM32WBA52 or compatible\n//!\n//! To test:\n//! 1. Flash this example to your STM32WBA board\n//! 2. Have nearby BLE devices advertising (phones, beacons, etc.)\n//! 3. Observe discovered devices in the logs\n\n#![no_std]\n#![no_main]\n\nuse core::cell::RefCell;\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_stm32::peripherals::RNG;\nuse embassy_stm32::rcc::{\n    AHB5Prescaler, AHBPrescaler, APBPrescaler, Hse, HsePrescaler, PllDiv, PllMul, PllPreDiv, PllSource, Sysclk,\n    VoltageScale, mux,\n};\nuse embassy_stm32::rng::{self, Rng};\nuse embassy_stm32::{Config, bind_interrupts};\nuse embassy_stm32_wpan::gap::{ParsedAdvData, ScanParams, ScanType};\nuse embassy_stm32_wpan::hci::event::EventParams;\nuse embassy_stm32_wpan::{Ble, ble_runner};\nuse embassy_sync::blocking_mutex::Mutex;\nuse embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;\nuse static_cell::StaticCell;\nuse {defmt_rtt as _, panic_probe as _};\n\nbind_interrupts!(struct Irqs {\n    RNG => rng::InterruptHandler<embassy_stm32::peripherals::RNG>;\n});\n\n/// BLE runner task - drives the BLE stack sequencer\n#[embassy_executor::task]\nasync fn ble_runner_task() {\n    ble_runner().await\n}\n\n#[embassy_executor::main]\nasync fn main(spawner: Spawner) {\n    let mut config = Config::default();\n\n    config.rcc.hse = Some(Hse {\n        prescaler: HsePrescaler::DIV2,\n    });\n\n    // Configure PLL1 (required on WBA)\n    config.rcc.pll1 = Some(embassy_stm32::rcc::Pll {\n        source: PllSource::HSE,\n        prediv: PllPreDiv::DIV1,\n        mul: PllMul::MUL30,\n        divr: Some(PllDiv::DIV5),\n        divq: None,\n        divp: Some(PllDiv::DIV30),\n        frac: Some(0),\n    });\n\n    config.rcc.ahb_pre = AHBPrescaler::DIV1;\n    config.rcc.apb1_pre = APBPrescaler::DIV1;\n    config.rcc.apb2_pre = APBPrescaler::DIV1;\n    config.rcc.apb7_pre = APBPrescaler::DIV1;\n    config.rcc.ahb5_pre = AHB5Prescaler::DIV4;\n    config.rcc.voltage_scale = VoltageScale::RANGE1;\n    config.rcc.sys = Sysclk::PLL1_R;\n    config.rcc.mux.rngsel = mux::Rngsel::HSI;\n\n    let p = embassy_stm32::init(config);\n    info!(\"Embassy STM32WBA BLE Scanner Example\");\n\n    // Initialize RNG (required by BLE stack)\n    static RNG: StaticCell<Mutex<CriticalSectionRawMutex, RefCell<Rng<'static, RNG>>>> = StaticCell::new();\n    let rng = RNG.init(Mutex::new(RefCell::new(Rng::new(p.RNG, Irqs))));\n\n    // Initialize BLE stack\n    let mut ble = Ble::new(rng);\n    ble.init().expect(\"BLE initialization failed\");\n    info!(\"BLE stack initialized\");\n\n    // Spawn the BLE runner task (required for proper BLE operation)\n    spawner.spawn(ble_runner_task().expect(\"Failed to create BLE runner task\"));\n\n    // Configure scan parameters\n    // Using active scanning to get scan response data (device names)\n    let scan_params = ScanParams::new()\n        .with_scan_type(ScanType::Active)\n        .with_interval(0x0050) // 50ms\n        .with_window(0x0030) // 30ms\n        .with_filter_duplicates(true);\n\n    // Start scanning\n    let mut scanner = ble.scanner();\n    scanner.start(scan_params).expect(\"Failed to start scanning\");\n\n    info!(\"=== BLE Scanning Started ===\");\n    info!(\"Looking for nearby devices...\");\n    info!(\"\");\n\n    let mut device_count = 0u32;\n\n    // Main event loop - process advertising reports\n    loop {\n        let event = ble.read_event().await;\n\n        // Check for advertising reports\n        if let EventParams::LeAdvertisingReport { reports } = &event.params {\n            for report in reports.iter() {\n                device_count += 1;\n\n                // Parse the advertising data\n                let parsed = ParsedAdvData::parse(&report.data);\n\n                info!(\"--- Device #{} ---\", device_count);\n\n                // Display device address\n                info!(\n                    \"  Address: {:02X}:{:02X}:{:02X}:{:02X}:{:02X}:{:02X} ({})\",\n                    report.address.0[5],\n                    report.address.0[4],\n                    report.address.0[3],\n                    report.address.0[2],\n                    report.address.0[1],\n                    report.address.0[0],\n                    address_type_str(report.address_type)\n                );\n\n                // Display RSSI\n                info!(\"  RSSI: {} dBm\", report.rssi);\n\n                // Display event type\n                info!(\"  Type: {}\", adv_event_type_str(report.event_type));\n\n                // Display parsed name if available\n                if let Some(name) = parsed.name {\n                    info!(\"  Name: \\\"{}\\\"\", name);\n                }\n\n                // Display flags if available\n                if let Some(flags) = parsed.flags {\n                    info!(\"  Flags: 0x{:02X} ({})\", flags, flags_str(flags));\n                }\n\n                // Display TX power if available\n                if let Some(tx_power) = parsed.tx_power {\n                    info!(\"  TX Power: {} dBm\", tx_power);\n                }\n\n                // Display 16-bit service UUIDs\n                if !parsed.service_uuids_16.is_empty() {\n                    for uuid in parsed.service_uuids_16.iter() {\n                        info!(\"  Service UUID: 0x{:04X} ({})\", uuid, service_uuid_str(*uuid));\n                    }\n                }\n\n                // Display 128-bit service UUIDs\n                if !parsed.service_uuids_128.is_empty() {\n                    for uuid in parsed.service_uuids_128.iter() {\n                        info!(\n                            \"  Service UUID (128): {:02X}{:02X}{:02X}{:02X}-{:02X}{:02X}-{:02X}{:02X}-{:02X}{:02X}-{:02X}{:02X}{:02X}{:02X}{:02X}{:02X}\",\n                            uuid[15],\n                            uuid[14],\n                            uuid[13],\n                            uuid[12],\n                            uuid[11],\n                            uuid[10],\n                            uuid[9],\n                            uuid[8],\n                            uuid[7],\n                            uuid[6],\n                            uuid[5],\n                            uuid[4],\n                            uuid[3],\n                            uuid[2],\n                            uuid[1],\n                            uuid[0]\n                        );\n                    }\n                }\n\n                // Display manufacturer data\n                if let Some((company_id, data)) = parsed.manufacturer_data {\n                    info!(\n                        \"  Manufacturer: 0x{:04X} ({}) - {} bytes\",\n                        company_id,\n                        company_id_str(company_id),\n                        data.len()\n                    );\n                }\n\n                info!(\"\");\n            }\n        }\n    }\n}\n\n/// Convert address type to string\nfn address_type_str(addr_type: embassy_stm32_wpan::hci::types::AddressType) -> &'static str {\n    match addr_type {\n        embassy_stm32_wpan::hci::types::AddressType::Public => \"Public\",\n        embassy_stm32_wpan::hci::types::AddressType::Random => \"Random\",\n        embassy_stm32_wpan::hci::types::AddressType::PublicIdentity => \"Public Identity\",\n        embassy_stm32_wpan::hci::types::AddressType::RandomIdentity => \"Random Identity\",\n    }\n}\n\n/// Convert advertising event type to string\nfn adv_event_type_str(event_type: u8) -> &'static str {\n    match event_type {\n        0x00 => \"Connectable Undirected\",\n        0x01 => \"Connectable Directed\",\n        0x02 => \"Scannable Undirected\",\n        0x03 => \"Non-Connectable Undirected\",\n        0x04 => \"Scan Response\",\n        _ => \"Unknown\",\n    }\n}\n\n/// Convert flags byte to description\nfn flags_str(flags: u8) -> &'static str {\n    match flags {\n        0x06 => \"LE General Discoverable\",\n        0x04 => \"BR/EDR Not Supported\",\n        0x02 => \"LE General Discoverable Only\",\n        0x01 => \"LE Limited Discoverable\",\n        0x05 => \"LE Limited + BR/EDR Not Supported\",\n        _ => \"Other\",\n    }\n}\n\n/// Convert common 16-bit service UUID to name\nfn service_uuid_str(uuid: u16) -> &'static str {\n    match uuid {\n        0x1800 => \"Generic Access\",\n        0x1801 => \"Generic Attribute\",\n        0x180A => \"Device Information\",\n        0x180D => \"Heart Rate\",\n        0x180F => \"Battery\",\n        0x1810 => \"Blood Pressure\",\n        0x1816 => \"Cycling Speed and Cadence\",\n        0x181A => \"Environmental Sensing\",\n        0x181C => \"User Data\",\n        0xFE9F => \"Google Fast Pair\",\n        0xFD6F => \"Apple Exposure Notification\",\n        _ => \"Unknown\",\n    }\n}\n\n/// Convert common company IDs to names\nfn company_id_str(company_id: u16) -> &'static str {\n    match company_id {\n        0x004C => \"Apple\",\n        0x0006 => \"Microsoft\",\n        0x00E0 => \"Google\",\n        0x0075 => \"Samsung\",\n        0x0059 => \"Nordic Semiconductor\",\n        0x0030 => \"STMicroelectronics\",\n        0x00D2 => \"Huawei\",\n        0x038F => \"Xiaomi\",\n        _ => \"Unknown\",\n    }\n}\n"
  },
  {
    "path": "examples/stm32wba/src/bin/ble_secure.rs",
    "content": "//! BLE Secure Peripheral Example\n//!\n//! This example demonstrates BLE security features:\n//! - Configures security parameters (bonding, MITM protection)\n//! - Handles pairing requests and passkey entry\n//! - Supports numeric comparison for Secure Connections\n//! - Demonstrates bond management\n//!\n//! Hardware: STM32WBA52 or compatible\n//!\n//! To test:\n//! 1. Flash this example to your STM32WBA board\n//! 2. Connect with nRF Connect or similar app\n//! 3. Initiate pairing from the app\n//! 4. Observe pairing events in the logs\n//! 5. For passkey entry: enter the displayed passkey on your phone\n//! 6. For numeric comparison: confirm the displayed numbers match\n\n#![no_std]\n#![no_main]\n\nuse core::cell::RefCell;\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_stm32::peripherals::RNG;\nuse embassy_stm32::rcc::{\n    AHB5Prescaler, AHBPrescaler, APBPrescaler, PllDiv, PllMul, PllPreDiv, PllSource, Sysclk, VoltageScale, mux,\n};\nuse embassy_stm32::rng::{self, Rng};\nuse embassy_stm32::{Config, bind_interrupts};\nuse embassy_stm32_wpan::gap::{AdvData, AdvParams, AdvType, GapEvent};\nuse embassy_stm32_wpan::gatt::{CharProperties, GattEventMask, GattServer, SecurityPermissions, ServiceType, Uuid};\nuse embassy_stm32_wpan::hci::event::EventParams;\nuse embassy_stm32_wpan::security::{\n    PairingFailureReason, PairingStatus, SecureConnectionsSupport, SecurityManager, SecurityParams,\n};\nuse embassy_stm32_wpan::{Ble, ble_runner};\nuse embassy_sync::blocking_mutex::Mutex;\nuse embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;\nuse static_cell::StaticCell;\nuse {defmt_rtt as _, panic_probe as _};\n\nbind_interrupts!(struct Irqs {\n    RNG => rng::InterruptHandler<embassy_stm32::peripherals::RNG>;\n});\n\n/// Custom service UUID\nconst SECURE_SERVICE_UUID: u16 = 0xABCD;\n/// Characteristic that requires encryption\nconst SECURE_CHAR_UUID: u16 = 0xABCE;\n\n/// BLE runner task - drives the BLE stack sequencer\n#[embassy_executor::task]\nasync fn ble_runner_task() {\n    ble_runner().await\n}\n\n#[embassy_executor::main]\nasync fn main(spawner: Spawner) {\n    let mut config = Config::default();\n\n    // Configure PLL1 (required on WBA)\n    config.rcc.pll1 = Some(embassy_stm32::rcc::Pll {\n        source: PllSource::HSI,\n        prediv: PllPreDiv::DIV1,\n        mul: PllMul::MUL30,\n        divr: Some(PllDiv::DIV5),\n        divq: None,\n        divp: Some(PllDiv::DIV30),\n        frac: Some(0),\n    });\n\n    config.rcc.ahb_pre = AHBPrescaler::DIV1;\n    config.rcc.apb1_pre = APBPrescaler::DIV1;\n    config.rcc.apb2_pre = APBPrescaler::DIV1;\n    config.rcc.apb7_pre = APBPrescaler::DIV1;\n    config.rcc.ahb5_pre = AHB5Prescaler::DIV4;\n    config.rcc.voltage_scale = VoltageScale::RANGE1;\n    config.rcc.sys = Sysclk::PLL1_R;\n    config.rcc.mux.rngsel = mux::Rngsel::HSI;\n\n    let p = embassy_stm32::init(config);\n    info!(\"Embassy STM32WBA BLE Secure Peripheral Example\");\n\n    // Initialize RNG (required by BLE stack)\n    static RNG: StaticCell<Mutex<CriticalSectionRawMutex, RefCell<Rng<'static, RNG>>>> = StaticCell::new();\n    let rng = RNG.init(Mutex::new(RefCell::new(Rng::new(p.RNG, Irqs))));\n\n    // Initialize BLE stack\n    let mut ble = Ble::new(rng);\n    ble.init().expect(\"BLE initialization failed\");\n    info!(\"BLE stack initialized\");\n\n    // Spawn the BLE runner task (required for proper BLE operation)\n    spawner.spawn(ble_runner_task().expect(\"Failed to create BLE runner task\"));\n\n    // ===== Configure Security =====\n    let mut security = SecurityManager::new();\n\n    // Configure security parameters:\n    // - Enable bonding (store keys)\n    // - Require MITM protection (passkey entry or numeric comparison)\n    // - Support Secure Connections (LE Secure Connections pairing)\n    let security_params = SecurityParams::new()\n        .with_bonding(true)\n        .with_mitm_protection(true)\n        .with_secure_connections(SecureConnectionsSupport::Optional)\n        .with_key_size_range(7, 16);\n\n    security\n        .set_authentication_requirements(security_params)\n        .expect(\"Failed to set security parameters\");\n    info!(\"Security configured: Bonding + MITM + SC\");\n\n    // Enable address resolution (for bonded devices using RPA)\n    security\n        .set_address_resolution_enable(true)\n        .expect(\"Failed to enable address resolution\");\n\n    // Initialize GATT server\n    let mut gatt = GattServer::new();\n    gatt.init().expect(\"GATT initialization failed\");\n\n    // Create a service with a secure characteristic\n    let service_uuid = Uuid::from_u16(SECURE_SERVICE_UUID);\n    let service_handle = gatt\n        .add_service(service_uuid, ServiceType::Primary, 4)\n        .expect(\"Failed to add service\");\n\n    // Add characteristic that requires authenticated encryption\n    let char_uuid = Uuid::from_u16(SECURE_CHAR_UUID);\n    let char_handle = gatt\n        .add_characteristic(\n            service_handle,\n            char_uuid,\n            20,\n            CharProperties::READ | CharProperties::WRITE,\n            SecurityPermissions::AUTHEN_READ | SecurityPermissions::AUTHEN_WRITE,\n            GattEventMask::ATTRIBUTE_MODIFIED,\n            0,\n            true,\n        )\n        .expect(\"Failed to add characteristic\");\n\n    // Set initial value\n    gatt.update_characteristic_value(service_handle, char_handle, 0, b\"Secure!\")\n        .expect(\"Failed to set value\");\n\n    info!(\"GATT service created with secure characteristic\");\n\n    // Advertising parameters (reused for restarts)\n    let adv_params = AdvParams {\n        interval_min: 0x0050,\n        interval_max: 0x0050,\n        adv_type: AdvType::ConnectableUndirected,\n        ..AdvParams::default()\n    };\n\n    // Helper to create advertising data\n    fn create_adv_data() -> AdvData {\n        let mut adv_data = AdvData::new();\n        adv_data.add_flags(0x06).expect(\"Failed to add flags\");\n        adv_data.add_name(\"Embassy-Secure\").expect(\"Failed to add name\");\n        adv_data\n            .add_service_uuid_16(SECURE_SERVICE_UUID)\n            .expect(\"Failed to add UUID\");\n        adv_data\n    }\n\n    // Start advertising\n    {\n        let mut advertiser = ble.advertiser();\n        advertiser\n            .start(adv_params.clone(), create_adv_data(), None)\n            .expect(\"Failed to start advertising\");\n    }\n\n    info!(\"=== BLE Secure Peripheral Started ===\");\n    info!(\"Device name: 'Embassy-Secure'\");\n    info!(\"Connect and pair to access secure characteristic\");\n    info!(\"\");\n\n    // Main event loop\n    loop {\n        let event = ble.read_event().await;\n\n        // Process GAP events (connections)\n        if let Some(gap_event) = ble.process_event(&event) {\n            match gap_event {\n                GapEvent::Connected(conn) => {\n                    info!(\"=== CONNECTED ===\");\n                    info!(\"  Handle: 0x{:04X}\", conn.handle.0);\n                    info!(\n                        \"  Peer: {:02X}:{:02X}:{:02X}:{:02X}:{:02X}:{:02X}\",\n                        conn.peer_address.0[5],\n                        conn.peer_address.0[4],\n                        conn.peer_address.0[3],\n                        conn.peer_address.0[2],\n                        conn.peer_address.0[1],\n                        conn.peer_address.0[0]\n                    );\n\n                    info!(\"Waiting for pairing request...\");\n                    info!(\"(Try to read the secure characteristic to trigger pairing)\");\n                }\n\n                GapEvent::Disconnected { handle, reason } => {\n                    info!(\"=== DISCONNECTED ===\");\n                    info!(\"  Handle: 0x{:04X}, Reason: 0x{:02X}\", handle.0, reason);\n\n                    // Restart advertising\n                    let mut advertiser = ble.advertiser();\n                    let _ = advertiser.start(adv_params.clone(), create_adv_data(), None);\n                    info!(\"Advertising restarted\");\n                }\n\n                _ => {}\n            }\n        }\n\n        // Process security events\n        match &event.params {\n            EventParams::GapPairingComplete {\n                conn_handle,\n                status,\n                reason,\n            } => {\n                let pairing_status = PairingStatus::from_u8(*status);\n                info!(\"=== PAIRING COMPLETE ===\");\n                info!(\"  Connection: 0x{:04X}\", conn_handle.0);\n\n                match pairing_status {\n                    PairingStatus::Success => {\n                        info!(\"  Status: SUCCESS\");\n                        info!(\"  Device is now bonded and can access secure characteristics\");\n                    }\n                    PairingStatus::Timeout => {\n                        info!(\"  Status: TIMEOUT\");\n                        info!(\"  Pairing timed out - please try again\");\n                    }\n                    PairingStatus::Failed => {\n                        let failure_reason = PairingFailureReason::from_u8(*reason);\n                        info!(\"  Status: FAILED\");\n                        info!(\"  Reason: 0x{:02X} ({})\", reason, failure_reason.description());\n                    }\n                }\n            }\n\n            EventParams::GapPasskeyRequest { conn_handle } => {\n                info!(\"=== PASSKEY REQUEST ===\");\n                info!(\"  Connection: 0x{:04X}\", conn_handle.0);\n\n                // Generate a random passkey (in production, display this to user)\n                // For this example, we use a fixed passkey\n                let passkey: u32 = 123456;\n                info!(\"  Passkey: {:06}\", passkey);\n                info!(\"  Enter this passkey on your phone/device!\");\n\n                if let Err(e) = security.pass_key_response(conn_handle.as_u16(), passkey) {\n                    error!(\"Failed to send passkey response: {:?}\", e);\n                }\n            }\n\n            EventParams::GapNumericComparisonRequest {\n                conn_handle,\n                numeric_value,\n            } => {\n                info!(\"=== NUMERIC COMPARISON ===\");\n                info!(\"  Connection: 0x{:04X}\", conn_handle.0);\n                info!(\"  Displayed value: {:06}\", numeric_value);\n                info!(\"  Confirm this matches the value on your phone!\");\n\n                // Auto-confirm for this example (in production, wait for user input)\n                // Set to true to accept, false to reject\n                let confirm = true;\n                info!(\"  Auto-confirming: {}\", if confirm { \"YES\" } else { \"NO\" });\n\n                if let Err(e) = security.numeric_comparison_response(conn_handle.as_u16(), confirm) {\n                    error!(\"Failed to send numeric comparison response: {:?}\", e);\n                }\n            }\n\n            EventParams::GapBondLost { conn_handle } => {\n                info!(\"=== BOND LOST ===\");\n                info!(\"  Connection: 0x{:04X}\", conn_handle.0);\n                info!(\"  Previous bond invalid, allowing rebond...\");\n\n                if let Err(e) = security.allow_rebond(conn_handle.as_u16()) {\n                    error!(\"Failed to allow rebond: {:?}\", e);\n                }\n            }\n\n            EventParams::GapPairingRequest { conn_handle, is_bonded } => {\n                info!(\"=== PAIRING REQUEST ===\");\n                info!(\"  Connection: 0x{:04X}\", conn_handle.0);\n                info!(\"  Previously bonded: {}\", is_bonded);\n                // The stack will handle the pairing process automatically\n            }\n\n            EventParams::GattAttributeModified {\n                conn_handle,\n                attr_handle,\n                data,\n                ..\n            } => {\n                info!(\"=== SECURE WRITE RECEIVED ===\");\n                info!(\"  Connection: 0x{:04X}, Attr: 0x{:04X}\", conn_handle.0, attr_handle);\n                info!(\"  Data ({} bytes): {:?}\", data.len(), data.as_slice());\n                info!(\"  (This write succeeded because device is paired!)\");\n            }\n\n            EventParams::EncryptionChange {\n                status,\n                handle,\n                enabled,\n                ..\n            } => {\n                info!(\"=== ENCRYPTION CHANGE ===\");\n                info!(\"  Connection: 0x{:04X}\", handle.0);\n                info!(\"  Status: {:?}\", status);\n                info!(\"  Encrypted: {}\", enabled);\n            }\n\n            _ => {\n                // Log other events at debug level\n                debug!(\"Event: {:?}\", event);\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "examples/stm32wba/src/bin/blinky.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_stm32::gpio::{Level, Output, Speed};\nuse embassy_time::Timer;\nuse {defmt_rtt as _, panic_probe as _};\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let p = embassy_stm32::init(Default::default());\n    info!(\"Hello World!\");\n\n    let mut led = Output::new(p.PB4, Level::High, Speed::Low);\n\n    loop {\n        info!(\"high\");\n        led.set_high();\n        Timer::after_millis(500).await;\n\n        info!(\"low\");\n        led.set_low();\n        Timer::after_millis(500).await;\n    }\n}\n"
  },
  {
    "path": "examples/stm32wba/src/bin/button_exti.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_stm32::exti::{self, ExtiInput};\nuse embassy_stm32::gpio::Pull;\nuse embassy_stm32::{bind_interrupts, interrupt};\nuse {defmt_rtt as _, panic_probe as _};\n\nbind_interrupts!(\n    pub struct Irqs{\n        EXTI13 => exti::InterruptHandler<interrupt::typelevel::EXTI13>;\n});\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let p = embassy_stm32::init(Default::default());\n    info!(\"Hello World!\");\n\n    let mut button = ExtiInput::new(p.PC13, p.EXTI13, Pull::Up, Irqs);\n\n    info!(\"Press the USER button...\");\n\n    loop {\n        button.wait_for_falling_edge().await;\n        info!(\"Pressed!\");\n        button.wait_for_rising_edge().await;\n        info!(\"Released!\");\n    }\n}\n"
  },
  {
    "path": "examples/stm32wba/src/bin/device_info.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_stm32::uid;\nuse stm32_metapac::DESIG;\nuse {defmt_rtt as _, panic_probe as _};\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let _p = embassy_stm32::init(Default::default());\n    info!(\"Device info example\");\n\n    // 96-bit unique ID\n    let uid_bytes = uid::uid();\n    let uid_hex = uid::uid_hex();\n    info!(\"UID (bytes): {:02x}\", uid_bytes);\n    info!(\"UID (hex):   {}\", uid_hex);\n\n    // Flash and RAM size\n    let flashsizer = DESIG.flashsizer().read();\n    info!(\"Flash size:  {} KB\", flashsizer.flash_size());\n    info!(\"RAM size:    {} KB\", flashsizer.ram_size());\n\n    // Package type\n    let pkgr = DESIG.pkgr().read();\n    info!(\"Package:     0x{:02x}\", pkgr.pkg().to_bits());\n\n    // IEEE 64-bit unique ID\n    let uid64r1 = DESIG.uid64r1().read();\n    let uid64r2 = DESIG.uid64r2().read();\n    info!(\"UID64 devnum: 0x{:08x}\", uid64r1.devnum());\n    info!(\"UID64 devid:  0x{:02x}\", uid64r2.devid().to_bits());\n    info!(\"UID64 stid:   0x{:06x}\", uid64r2.stid().to_bits());\n}\n"
  },
  {
    "path": "examples/stm32wba/src/bin/flash.rs",
    "content": "//! STM32WBA blocking flash example.\n//!\n//! Demonstrates quad-word (16-byte) programming with WDW/BSY handling:\n//! - Erase one page (8 KB) in bank 1\n//! - Write 32 bytes (2 quad-words) at 16-byte-aligned offset\n//! - Read back and verify\n\n#![no_std]\n#![no_main]\n\nuse defmt::{info, unwrap};\nuse embassy_executor::Spawner;\nuse embassy_stm32::flash::Flash;\nuse embassy_time::Timer;\nuse {defmt_rtt as _, panic_probe as _};\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let p = embassy_stm32::init(Default::default());\n    info!(\"STM32WBA Flash example (quad-word programming)\");\n\n    // Use high offset in bank 1 to avoid overwriting program (page-aligned: 8 KB).\n    const PAGE_SIZE: u32 = 8 * 1024;\n    const ADDR: u32 = 0x7_0000; // 448 KB offset in bank 1\n\n    Timer::after_millis(100).await;\n\n    let mut f = Flash::new_blocking(p.FLASH).into_blocking_regions().bank1_region;\n\n    info!(\"Reading before erase...\");\n    let mut buf = [0u8; 32];\n    unwrap!(f.blocking_read(ADDR, &mut buf));\n    info!(\"Read: {=[u8]:x}\", buf);\n\n    info!(\"Erasing one page ({} bytes)...\", PAGE_SIZE);\n    unwrap!(f.blocking_erase(ADDR, ADDR + PAGE_SIZE));\n\n    info!(\"Reading after erase...\");\n    unwrap!(f.blocking_read(ADDR, &mut buf));\n    info!(\"Read after erase: {=[u8]:x}\", buf);\n\n    info!(\"Writing 32 bytes (2 quad-words)...\");\n    unwrap!(f.blocking_write(\n        ADDR,\n        &[\n            1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29,\n            30, 31, 32,\n        ]\n    ));\n\n    info!(\"Reading after write...\");\n    unwrap!(f.blocking_read(ADDR, &mut buf));\n    info!(\"Read: {=[u8]:x}\", buf);\n\n    assert_eq!(\n        &buf[..],\n        &[\n            1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29,\n            30, 31, 32,\n        ],\n        \"Flash read-back mismatch\"\n    );\n    info!(\"Flash verify OK\");\n}\n"
  },
  {
    "path": "examples/stm32wba/src/bin/mac_ffd.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_stm32::Config;\nuse embassy_stm32::rcc::{Sysclk, mux};\nuse embassy_stm32_wpan::bindings::mac::{ST_MAC_callbacks_t, ST_MAC_init};\nuse {defmt_rtt as _, panic_probe as _};\n\nstatic _MAC_CALLBACKS: ST_MAC_callbacks_t = ST_MAC_callbacks_t {\n    mlmeAssociateCnfCb: None,       // ST_MAC_MLMEAssociateCnfCbPtr,\n    mlmeAssociateIndCb: None,       // ST_MAC_MLMEAssociateIndCbPtr,\n    mlmeBeaconNotifyIndCb: None,    // ST_MAC_MLMEBeaconNotifyIndCbPtr,\n    mlmeCalibrateCnfCb: None,       // ST_MAC_MLMECalibrateCnfCbPtr,\n    mlmeCommStatusIndCb: None,      // ST_MAC_MLMECommStatusIndCbPtr,\n    mlmeDisassociateCnfCb: None,    // ST_MAC_MLMEDisassociateCnfCbPtr,\n    mlmeDisassociateIndCb: None,    // ST_MAC_MLMEDisassociateIndCbPtr,\n    mlmeDpsCnfCb: None,             // ST_MAC_MLMEDpsCnfCbPtr,\n    mlmeDpsIndCb: None,             // ST_MAC_MLMEDpsIndCbPtr,\n    mlmeGetCnfCb: None,             // ST_MAC_MLMEGetCnfCbPtr,\n    mlmeGtsCnfCb: None,             // ST_MAC_MLMEGtsCnfCbPtr,\n    mlmeGtsIndCb: None,             // ST_MAC_MLMEGtsIndCbPtr,\n    mlmeOrphanIndCb: None,          // ST_MAC_MLMEOrphanIndCbPtr,\n    mlmePollCnfCb: None,            // ST_MAC_MLMEPollCnfCbPtr,\n    mlmeResetCnfCb: None,           // ST_MAC_MLMEResetCnfCbPtr,\n    mlmeRxEnableCnfCb: None,        // ST_MAC_MLMERxEnableCnfCbPtr,\n    mlmeScanCnfCb: None,            // ST_MAC_MLMEScanCnfCbPtr,\n    mlmeSetCnfCb: None,             // ST_MAC_MLMESetCnfCbPtr,\n    mlmeSoundingCnfCb: None,        // ST_MAC_MLMESoundingCnfCbPtr,\n    mlmeStartCnfCb: None,           // ST_MAC_MLMEStartCnfCbPtr,\n    mlmeSyncLossIndCb: None,        // ST_MAC_MLMESyncLossIndCbPtr,\n    mcpsDataIndCb: None,            // ST_MAC_MCPSDataIndCbPtr,\n    mcpsDataCnfCb: None,            // ST_MAC_MCPSDataCnfCbPtr,\n    mcpsPurgeCnfCb: None,           // ST_MAC_MCPSPurgeCnfCbPtr,\n    mlmePollIndCb: None,            // ST_MAC_MLMEPollIndCbPtr,\n    mlmeBeaconReqIndCb: None,       // ST_MAC_MLMEBeaconReqIndCbPtr,\n    mlmeBeaconCnfCb: None,          // ST_MAC_MLMEBeaconCnfCbPtr,\n    mlmeGetPwrInfoTableCnfCb: None, // ST_MAC_MLMEGetPwrInfoTableCnfCbPtr,\n    mlmeSetPwrInfoTableCnfCb: None, // ST_MAC_MLMESetPwrInfoTableCnfCbPtr,\n};\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let mut config = Config::default();\n\n    config.rcc.sys = Sysclk::HSI;\n    config.rcc.mux.rngsel = mux::Rngsel::HSI;\n\n    let _p = embassy_stm32::init(config);\n    info!(\"Hello World!\");\n\n    let status = unsafe { ST_MAC_init(&_MAC_CALLBACKS as *const _ as *mut _) };\n\n    info!(\"mac init: {}\", status);\n\n    cortex_m::asm::bkpt();\n}\n"
  },
  {
    "path": "examples/stm32wba/src/bin/pka_ecdh.rs",
    "content": "//! PKA ECDH Key Agreement Example\n//!\n//! Demonstrates Elliptic Curve Diffie-Hellman (ECDH) key agreement using the\n//! PKA hardware accelerator.\n//!\n//! # What This Example Shows\n//! - Generating key pairs (private + public key)\n//! - Computing shared secrets between two parties\n//! - Verifying that both parties derive the same shared secret\n//! - Point validation for security\n//!\n//! # ECDH Key Agreement Process\n//! 1. Alice generates private key (a) and public key (A = a*G)\n//! 2. Bob generates private key (b) and public key (B = b*G)\n//! 3. Alice computes shared secret: S = a * B = a * b * G\n//! 4. Bob computes shared secret: S = b * A = b * a * G\n//! 5. Both parties now have the same shared secret S\n//!\n//! # Security Notes\n//! - Always validate received public keys (use point_check)\n//! - Use the x-coordinate of the shared point as the shared secret\n//! - Derive session keys from the shared secret using a KDF (HKDF, SHA-256)\n//! - Private keys must be randomly generated and kept secret\n\n#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_stm32::pka::{EccPoint, EcdsaCurveParams, Pka};\nuse embassy_stm32::rcc::{\n    AHB5Prescaler, AHBPrescaler, APBPrescaler, PllDiv, PllMul, PllPreDiv, PllSource, Sysclk, VoltageScale, mux,\n};\nuse embassy_stm32::rng::Rng;\nuse embassy_stm32::{Config, bind_interrupts, peripherals};\nuse {defmt_rtt as _, panic_probe as _};\n\nbind_interrupts!(struct Irqs {\n    PKA => embassy_stm32::pka::InterruptHandler<peripherals::PKA>;\n    RNG => embassy_stm32::rng::InterruptHandler<peripherals::RNG>;\n});\n\n#[embassy_executor::main]\nasync fn main(_spawner: embassy_executor::Spawner) {\n    let mut config = Config::default();\n    config.rcc.pll1 = Some(embassy_stm32::rcc::Pll {\n        source: PllSource::HSI,\n        prediv: PllPreDiv::DIV1,\n        mul: PllMul::MUL30,\n        divr: Some(PllDiv::DIV5),\n        divq: None,\n        divp: Some(PllDiv::DIV30),\n        frac: Some(0),\n    });\n    config.rcc.ahb_pre = AHBPrescaler::DIV1;\n    config.rcc.apb1_pre = APBPrescaler::DIV1;\n    config.rcc.apb2_pre = APBPrescaler::DIV1;\n    config.rcc.apb7_pre = APBPrescaler::DIV1;\n    config.rcc.ahb5_pre = AHB5Prescaler::DIV4;\n    config.rcc.voltage_scale = VoltageScale::RANGE1;\n    config.rcc.sys = Sysclk::PLL1_R;\n    // RNG requires HSI clock source on WBA\n    config.rcc.mux.rngsel = mux::Rngsel::HSI;\n\n    let p = embassy_stm32::init(config);\n    info!(\"PKA ECDH Key Agreement Example\");\n\n    let mut pka = Pka::new_blocking(p.PKA, Irqs);\n    let mut rng = Rng::new(p.RNG, Irqs);\n\n    // Use NIST P-256 curve parameters\n    let curve = EcdsaCurveParams::nist_p256();\n\n    // ========== Generate Alice's Key Pair ==========\n    info!(\"=== Generating Alice's Key Pair ===\");\n\n    // Generate Alice's private key\n    let mut alice_private = [0u8; 32];\n    if let Err(e) = rng.async_fill_bytes(&mut alice_private).await {\n        error!(\"Failed to generate Alice's private key: {:?}\", e);\n        loop {\n            cortex_m::asm::wfi();\n        }\n    }\n    // Ensure private key is in valid range (1 < d < n)\n    alice_private[0] &= 0x7F;\n    alice_private[31] |= 0x01;\n\n    info!(\"Alice private key: {:02x}\", alice_private);\n\n    // Compute Alice's public key: A = alice_private * G\n    let mut alice_public = EccPoint::new(32);\n    match pka.ecc_mul(\n        &curve,\n        &alice_private,\n        curve.generator_x,\n        curve.generator_y,\n        &mut alice_public,\n    ) {\n        Ok(()) => {\n            info!(\"Alice public key X: {:02x}\", alice_public.x[..32]);\n            info!(\"Alice public key Y: {:02x}\", alice_public.y[..32]);\n        }\n        Err(e) => {\n            error!(\"Failed to generate Alice's public key: {:?}\", e);\n            loop {\n                cortex_m::asm::wfi();\n            }\n        }\n    }\n\n    // ========== Generate Bob's Key Pair ==========\n    info!(\"=== Generating Bob's Key Pair ===\");\n\n    // Generate Bob's private key\n    let mut bob_private = [0u8; 32];\n    if let Err(e) = rng.async_fill_bytes(&mut bob_private).await {\n        error!(\"Failed to generate Bob's private key: {:?}\", e);\n        loop {\n            cortex_m::asm::wfi();\n        }\n    }\n    // Ensure private key is in valid range\n    bob_private[0] &= 0x7F;\n    bob_private[31] |= 0x01;\n\n    info!(\"Bob private key: {:02x}\", bob_private);\n\n    // Compute Bob's public key: B = bob_private * G\n    let mut bob_public = EccPoint::new(32);\n    match pka.ecc_mul(\n        &curve,\n        &bob_private,\n        curve.generator_x,\n        curve.generator_y,\n        &mut bob_public,\n    ) {\n        Ok(()) => {\n            info!(\"Bob public key X: {:02x}\", bob_public.x[..32]);\n            info!(\"Bob public key Y: {:02x}\", bob_public.y[..32]);\n        }\n        Err(e) => {\n            error!(\"Failed to generate Bob's public key: {:?}\", e);\n            loop {\n                cortex_m::asm::wfi();\n            }\n        }\n    }\n\n    // ========== Validate Public Keys ==========\n    info!(\"=== Validating Public Keys ===\");\n\n    // Alice validates Bob's public key\n    match pka.point_check(&curve, &bob_public.x[..32], &bob_public.y[..32]) {\n        Ok(true) => {\n            info!(\"Bob's public key is valid (on curve)\");\n        }\n        Ok(false) => {\n            error!(\"Bob's public key is INVALID (not on curve)!\");\n            loop {\n                cortex_m::asm::wfi();\n            }\n        }\n        Err(e) => {\n            error!(\"Point check failed: {:?}\", e);\n            loop {\n                cortex_m::asm::wfi();\n            }\n        }\n    }\n\n    // Bob validates Alice's public key\n    match pka.point_check(&curve, &alice_public.x[..32], &alice_public.y[..32]) {\n        Ok(true) => {\n            info!(\"Alice's public key is valid (on curve)\");\n        }\n        Ok(false) => {\n            error!(\"Alice's public key is INVALID (not on curve)!\");\n            loop {\n                cortex_m::asm::wfi();\n            }\n        }\n        Err(e) => {\n            error!(\"Point check failed: {:?}\", e);\n            loop {\n                cortex_m::asm::wfi();\n            }\n        }\n    }\n\n    // ========== Compute Shared Secrets ==========\n    info!(\"=== Computing Shared Secrets ===\");\n\n    // Alice computes shared secret: S_alice = alice_private * bob_public\n    let mut alice_shared = EccPoint::new(32);\n    match pka.ecc_mul(\n        &curve,\n        &alice_private,\n        &bob_public.x[..32],\n        &bob_public.y[..32],\n        &mut alice_shared,\n    ) {\n        Ok(()) => {\n            info!(\"Alice's shared secret X: {:02x}\", alice_shared.x[..32]);\n        }\n        Err(e) => {\n            error!(\"Alice failed to compute shared secret: {:?}\", e);\n            loop {\n                cortex_m::asm::wfi();\n            }\n        }\n    }\n\n    // Bob computes shared secret: S_bob = bob_private * alice_public\n    let mut bob_shared = EccPoint::new(32);\n    match pka.ecc_mul(\n        &curve,\n        &bob_private,\n        &alice_public.x[..32],\n        &alice_public.y[..32],\n        &mut bob_shared,\n    ) {\n        Ok(()) => {\n            info!(\"Bob's shared secret X: {:02x}\", bob_shared.x[..32]);\n        }\n        Err(e) => {\n            error!(\"Bob failed to compute shared secret: {:?}\", e);\n            loop {\n                cortex_m::asm::wfi();\n            }\n        }\n    }\n\n    // ========== Verify Shared Secrets Match ==========\n    info!(\"=== Verifying Key Agreement ===\");\n\n    if alice_shared.x[..32] == bob_shared.x[..32] && alice_shared.y[..32] == bob_shared.y[..32] {\n        info!(\"SUCCESS: Both parties derived the SAME shared secret!\");\n        info!(\"Shared secret (x-coord): {:02x}\", alice_shared.x[..32]);\n        info!(\"\");\n        info!(\"This shared secret can now be used to derive:\");\n        info!(\"- AES encryption keys (using SHA-256 or HKDF)\");\n        info!(\"- HMAC authentication keys\");\n        info!(\"- Session keys for secure communication\");\n    } else {\n        error!(\"FAILURE: Shared secrets do not match!\");\n        error!(\"Alice X: {:02x}\", alice_shared.x[..32]);\n        error!(\"Bob X:   {:02x}\", bob_shared.x[..32]);\n    }\n\n    // ========== Example with Pre-defined Test Vectors ==========\n    info!(\"=== NIST ECDH Test Vector ===\");\n\n    // NIST CAVP test vector for P-256 ECDH\n    let test_private: [u8; 32] = [\n        0xc9, 0xaf, 0xa9, 0xd8, 0x45, 0xba, 0x75, 0x16, 0x6b, 0x5c, 0x21, 0x57, 0x67, 0xb1, 0xd6, 0x93, 0x4e, 0x50,\n        0xc3, 0xdb, 0x36, 0xe8, 0x9b, 0x12, 0x7b, 0x8a, 0x62, 0x2b, 0x12, 0x0f, 0x67, 0x21,\n    ];\n\n    let test_peer_pub_x: [u8; 32] = [\n        0x70, 0x04, 0x0a, 0xcd, 0x89, 0x8e, 0xb2, 0x3d, 0xfa, 0x85, 0x9a, 0x16, 0x53, 0x31, 0x9c, 0xa8, 0xd1, 0xb0,\n        0x81, 0xf6, 0x0f, 0x3e, 0x05, 0x97, 0xc7, 0xfd, 0xd6, 0x29, 0x32, 0x4b, 0xe6, 0x2c,\n    ];\n\n    let test_peer_pub_y: [u8; 32] = [\n        0x5f, 0x67, 0x94, 0x7f, 0x9c, 0x63, 0x8f, 0x63, 0xd7, 0xba, 0x35, 0x73, 0xb8, 0xbd, 0xb5, 0x5a, 0x83, 0x62,\n        0xb3, 0x9c, 0x23, 0x4e, 0x7d, 0x36, 0x7f, 0xc1, 0xd5, 0xcd, 0x8c, 0x82, 0xc9, 0x25,\n    ];\n\n    info!(\"Computing shared secret with test vector...\");\n    let mut test_shared = EccPoint::new(32);\n    match pka.ecc_mul(\n        &curve,\n        &test_private,\n        &test_peer_pub_x,\n        &test_peer_pub_y,\n        &mut test_shared,\n    ) {\n        Ok(()) => {\n            info!(\"Test shared secret X: {:02x}\", test_shared.x[..32]);\n            info!(\"Test shared secret Y: {:02x}\", test_shared.y[..32]);\n        }\n        Err(e) => {\n            error!(\"Test vector computation failed: {:?}\", e);\n        }\n    }\n\n    info!(\"=== ECDH key agreement example complete ===\");\n\n    loop {\n        cortex_m::asm::wfi();\n    }\n}\n"
  },
  {
    "path": "examples/stm32wba/src/bin/pka_ecdsa_sign.rs",
    "content": "//! PKA ECDSA Signature Generation Example\n//!\n//! Demonstrates ECDSA signature generation using the PKA hardware accelerator.\n//!\n//! # What This Example Shows\n//! - Generating an ECDSA signature with a private key\n//! - Using the hardware RNG for the random nonce (k value)\n//! - Verifying the generated signature\n//!\n//! # ECDSA Signing Process\n//! 1. Hash the message (SHA-256 for P-256)\n//! 2. Generate a random nonce k (CRITICAL: must be unique per signature!)\n//! 3. Compute signature (r, s) = Sign(hash, private_key, k)\n//!\n//! # Security Notes\n//! - The k value MUST be cryptographically random and unique for EVERY signature\n//! - Reusing k values completely compromises the private key!\n//! - Use hardware RNG for k generation\n//! - Private keys should be stored securely\n\n#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_stm32::pka::{EccPoint, EcdsaCurveParams, EcdsaPublicKey, EcdsaSignature, Pka};\nuse embassy_stm32::rcc::{\n    AHB5Prescaler, AHBPrescaler, APBPrescaler, PllDiv, PllMul, PllPreDiv, PllSource, Sysclk, VoltageScale, mux,\n};\nuse embassy_stm32::rng::Rng;\nuse embassy_stm32::{Config, bind_interrupts, peripherals};\nuse {defmt_rtt as _, panic_probe as _};\n\nbind_interrupts!(struct Irqs {\n    PKA => embassy_stm32::pka::InterruptHandler<peripherals::PKA>;\n    RNG => embassy_stm32::rng::InterruptHandler<peripherals::RNG>;\n});\n\n#[embassy_executor::main]\nasync fn main(_spawner: embassy_executor::Spawner) {\n    let mut config = Config::default();\n    config.rcc.pll1 = Some(embassy_stm32::rcc::Pll {\n        source: PllSource::HSI,\n        prediv: PllPreDiv::DIV1,\n        mul: PllMul::MUL30,\n        divr: Some(PllDiv::DIV5),\n        divq: None,\n        divp: Some(PllDiv::DIV30),\n        frac: Some(0),\n    });\n    config.rcc.ahb_pre = AHBPrescaler::DIV1;\n    config.rcc.apb1_pre = APBPrescaler::DIV1;\n    config.rcc.apb2_pre = APBPrescaler::DIV1;\n    config.rcc.apb7_pre = APBPrescaler::DIV1;\n    config.rcc.ahb5_pre = AHB5Prescaler::DIV4;\n    config.rcc.voltage_scale = VoltageScale::RANGE1;\n    config.rcc.sys = Sysclk::PLL1_R;\n    // RNG requires HSI clock source on WBA\n    config.rcc.mux.rngsel = mux::Rngsel::HSI;\n\n    let p = embassy_stm32::init(config);\n    info!(\"PKA ECDSA Signature Generation Example\");\n\n    let mut pka = Pka::new_blocking(p.PKA, Irqs);\n    let mut rng = Rng::new(p.RNG, Irqs);\n\n    // Use NIST P-256 curve parameters\n    let curve = EcdsaCurveParams::nist_p256();\n\n    // Test private key (32 bytes, big-endian)\n    // In a real application, this would be securely stored\n    // WARNING: Never use this key in production!\n    let private_key: [u8; 32] = [\n        0xc9, 0xaf, 0xa9, 0xd8, 0x45, 0xba, 0x75, 0x16, 0x6b, 0x5c, 0x21, 0x57, 0x67, 0xb1, 0xd6, 0x93, 0x4e, 0x50,\n        0xc3, 0xdb, 0x36, 0xe8, 0x9b, 0x12, 0x7b, 0x8a, 0x62, 0x2b, 0x12, 0x0f, 0x67, 0x21,\n    ];\n\n    // Corresponding public key (derived from private key)\n    let pub_key_x: [u8; 32] = [\n        0x60, 0xfe, 0xd4, 0xba, 0x25, 0x5a, 0x9d, 0x31, 0xc9, 0x61, 0xeb, 0x74, 0xc6, 0x35, 0x6d, 0x68, 0xc0, 0x49,\n        0xb8, 0x92, 0x3b, 0x61, 0xfa, 0x6c, 0xe6, 0x69, 0x62, 0x2e, 0x60, 0xf2, 0x9f, 0xb6,\n    ];\n    let pub_key_y: [u8; 32] = [\n        0x79, 0x03, 0xfe, 0x10, 0x08, 0xb8, 0xbc, 0x99, 0xa4, 0x1a, 0xe9, 0xe9, 0x56, 0x28, 0xbc, 0x64, 0xf2, 0xf1,\n        0xb2, 0x0c, 0x2d, 0x7e, 0x9f, 0x51, 0x77, 0xa3, 0xc2, 0x94, 0xd4, 0x46, 0x22, 0x99,\n    ];\n\n    // Message hash (SHA-256 of the message)\n    // In a real application, you would compute this from the message\n    let message_hash: [u8; 32] = [\n        0xaf, 0x2b, 0xdb, 0xe1, 0xaa, 0x9b, 0x6e, 0xc1, 0xe2, 0xad, 0xe1, 0xd6, 0x94, 0xf4, 0x1f, 0xc7, 0x1a, 0x83,\n        0x1d, 0x02, 0x68, 0xe9, 0x89, 0x15, 0x62, 0x11, 0x3d, 0x8a, 0x62, 0xad, 0xd1, 0xbf,\n    ];\n\n    info!(\"=== ECDSA Signature Generation ===\");\n    info!(\"Curve: NIST P-256 (secp256r1)\");\n    info!(\"Message Hash: {:02x}\", message_hash);\n\n    // Generate random k value using hardware RNG\n    // CRITICAL: k must be random and unique for every signature!\n    let mut k = [0u8; 32];\n    if let Err(e) = rng.async_fill_bytes(&mut k).await {\n        error!(\"Failed to generate random k: {:?}\", e);\n        loop {\n            cortex_m::asm::wfi();\n        }\n    }\n\n    // Ensure k is in valid range (1 < k < n)\n    // For simplicity, we set the MSB to ensure it's less than n\n    k[0] &= 0x7F;\n    // Ensure k is not zero\n    k[31] |= 0x01;\n\n    info!(\"Random k:     {:02x}\", k);\n\n    // Generate signature\n    let mut sig_r = [0u8; 32];\n    let mut sig_s = [0u8; 32];\n\n    info!(\"Signing message...\");\n    match pka.ecdsa_sign(&curve, &private_key, &k, &message_hash, &mut sig_r, &mut sig_s) {\n        Ok(()) => {\n            info!(\"Signature generated successfully!\");\n            info!(\"Signature R: {:02x}\", sig_r);\n            info!(\"Signature S: {:02x}\", sig_s);\n        }\n        Err(e) => {\n            error!(\"Signing failed: {:?}\", e);\n            loop {\n                cortex_m::asm::wfi();\n            }\n        }\n    }\n\n    // Verify the signature we just generated\n    info!(\"=== Verifying Generated Signature ===\");\n\n    let public_key = EcdsaPublicKey {\n        x: &pub_key_x,\n        y: &pub_key_y,\n    };\n\n    let signature = EcdsaSignature { r: &sig_r, s: &sig_s };\n\n    match pka.ecdsa_verify(&curve, &public_key, &signature, &message_hash) {\n        Ok(true) => {\n            info!(\"Generated signature verified successfully!\");\n        }\n        Ok(false) => {\n            error!(\"Generated signature verification FAILED!\");\n        }\n        Err(e) => {\n            error!(\"Verification error: {:?}\", e);\n        }\n    }\n\n    // Demonstrate public key derivation from private key using scalar multiplication\n    info!(\"=== Deriving Public Key from Private Key ===\");\n\n    // Get generator point from curve parameters\n    let generator_x = curve.generator_x;\n    let generator_y = curve.generator_y;\n\n    let mut derived_pub = EccPoint::new(32);\n    match pka.ecc_mul(&curve, &private_key, generator_x, generator_y, &mut derived_pub) {\n        Ok(()) => {\n            info!(\"Public key derived from private key:\");\n            info!(\"Derived X:  {:02x}\", derived_pub.x[..32]);\n            info!(\"Expected X: {:02x}\", pub_key_x);\n            info!(\"Derived Y:  {:02x}\", derived_pub.y[..32]);\n            info!(\"Expected Y: {:02x}\", pub_key_y);\n\n            // Compare derived with expected\n            if derived_pub.x[..32] == pub_key_x && derived_pub.y[..32] == pub_key_y {\n                info!(\"Derived public key matches expected!\");\n            } else {\n                warn!(\"Derived public key does not match!\");\n            }\n        }\n        Err(e) => {\n            error!(\"Public key derivation failed: {:?}\", e);\n        }\n    }\n\n    info!(\"=== ECDSA signing example complete ===\");\n\n    loop {\n        cortex_m::asm::wfi();\n    }\n}\n"
  },
  {
    "path": "examples/stm32wba/src/bin/pka_ecdsa_verify.rs",
    "content": "//! PKA ECDSA Signature Verification Example\n//!\n//! Demonstrates ECDSA signature verification using the PKA hardware accelerator.\n//!\n//! # What This Example Shows\n//! - Loading NIST P-256 curve parameters\n//! - Verifying an ECDSA signature over a message hash\n//! - Using NIST test vectors for validation\n//!\n//! # ECDSA Verification Process\n//! The PKA hardware verifies that the signature (r, s) is valid for the given\n//! message hash and public key on the specified elliptic curve.\n//!\n//! # Security Notes\n//! - Always validate public keys before use (point_check)\n//! - Hash the message before verification (PKA expects the hash, not raw data)\n//! - Use approved hash functions (SHA-256 for P-256)\n\n#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_stm32::pka::{EcdsaCurveParams, EcdsaPublicKey, EcdsaSignature, Pka};\nuse embassy_stm32::rcc::{\n    AHB5Prescaler, AHBPrescaler, APBPrescaler, PllDiv, PllMul, PllPreDiv, PllSource, Sysclk, VoltageScale, mux,\n};\nuse embassy_stm32::rng::Rng;\nuse embassy_stm32::{Config, bind_interrupts, peripherals};\nuse {defmt_rtt as _, panic_probe as _};\n\nbind_interrupts!(struct Irqs {\n    PKA => embassy_stm32::pka::InterruptHandler<peripherals::PKA>;\n    RNG => embassy_stm32::rng::InterruptHandler<peripherals::RNG>;\n});\n\n#[embassy_executor::main]\nasync fn main(_spawner: embassy_executor::Spawner) {\n    let mut config = Config::default();\n    config.rcc.pll1 = Some(embassy_stm32::rcc::Pll {\n        source: PllSource::HSI,\n        prediv: PllPreDiv::DIV1,\n        mul: PllMul::MUL30,\n        divr: Some(PllDiv::DIV5),\n        divq: None,\n        divp: Some(PllDiv::DIV30),\n        frac: Some(0),\n    });\n    config.rcc.ahb_pre = AHBPrescaler::DIV1;\n    config.rcc.apb1_pre = APBPrescaler::DIV1;\n    config.rcc.apb2_pre = APBPrescaler::DIV1;\n    config.rcc.apb7_pre = APBPrescaler::DIV1;\n    config.rcc.ahb5_pre = AHB5Prescaler::DIV4;\n    config.rcc.voltage_scale = VoltageScale::RANGE1;\n    config.rcc.sys = Sysclk::PLL1_R;\n    // RNG clock is required for PKA to initialize properly on WBA\n    config.rcc.mux.rngsel = mux::Rngsel::HSI;\n\n    let p = embassy_stm32::init(config);\n    info!(\"PKA ECDSA Signature Verification Example\");\n\n    // Initialize RNG to enable its clock - PKA requires RNG clock on AHB5\n    let _rng = Rng::new(p.RNG, Irqs);\n\n    let mut pka = Pka::new_blocking(p.PKA, Irqs);\n\n    // Use NIST P-256 curve parameters\n    let curve = EcdsaCurveParams::nist_p256();\n\n    // NIST CAVP test vector for P-256 ECDSA verification\n    // From NIST CAVP SigVer.rsp [P-256,SHA-256]\n    // This test vector is verified to work with the ST-HAL\n\n    // Public key Qx, Qy (big-endian)\n    let pub_key_x: [u8; 32] = [\n        0xe4, 0x24, 0xdc, 0x61, 0xd4, 0xbb, 0x3c, 0xb7, 0xef, 0x43, 0x44, 0xa7, 0xf8, 0x95, 0x7a, 0x0c, 0x51, 0x34,\n        0xe1, 0x6f, 0x7a, 0x67, 0xc0, 0x74, 0xf8, 0x2e, 0x6e, 0x12, 0xf4, 0x9a, 0xbf, 0x3c,\n    ];\n    let pub_key_y: [u8; 32] = [\n        0x97, 0x0e, 0xed, 0x7a, 0xa2, 0xbc, 0x48, 0x65, 0x15, 0x45, 0x94, 0x9d, 0xe1, 0xdd, 0xda, 0xf0, 0x12, 0x7e,\n        0x59, 0x65, 0xac, 0x85, 0xd1, 0x24, 0x3d, 0x6f, 0x60, 0xe7, 0xdf, 0xae, 0xe9, 0x27,\n    ];\n\n    // SHA-256 hash of the test message (pre-computed)\n    let message_hash: [u8; 32] = [\n        0xd1, 0xb8, 0xef, 0x21, 0xeb, 0x41, 0x82, 0xee, 0x27, 0x06, 0x38, 0x06, 0x10, 0x63, 0xa3, 0xf3, 0xc1, 0x6c,\n        0x11, 0x4e, 0x33, 0x93, 0x7f, 0x69, 0xfb, 0x23, 0x2c, 0xc8, 0x33, 0x96, 0x5a, 0x94,\n    ];\n\n    // ECDSA signature (r, s) - valid signature for the above hash and public key\n    let sig_r: [u8; 32] = [\n        0xbf, 0x96, 0xb9, 0x9a, 0xa4, 0x9c, 0x70, 0x5c, 0x91, 0x0b, 0xe3, 0x31, 0x42, 0x01, 0x7c, 0x64, 0x2f, 0xf5,\n        0x40, 0xc7, 0x63, 0x49, 0xb9, 0xda, 0xb7, 0x2f, 0x98, 0x1f, 0xd9, 0x34, 0x7f, 0x4f,\n    ];\n    let sig_s: [u8; 32] = [\n        0x17, 0xc5, 0x50, 0x95, 0x81, 0x90, 0x89, 0xc2, 0xe0, 0x3b, 0x9c, 0xd4, 0x15, 0xab, 0xdf, 0x12, 0x44, 0x4e,\n        0x32, 0x30, 0x75, 0xd9, 0x8f, 0x31, 0x92, 0x0b, 0x9e, 0x0f, 0x57, 0xec, 0x87, 0x1c,\n    ];\n\n    info!(\"=== ECDSA Signature Verification ===\");\n    info!(\"Curve: NIST P-256 (secp256r1)\");\n    info!(\"Public Key X: {:02x}\", pub_key_x);\n    info!(\"Public Key Y: {:02x}\", pub_key_y);\n    info!(\"Message Hash: {:02x}\", message_hash);\n    info!(\"Signature R:  {:02x}\", sig_r);\n    info!(\"Signature S:  {:02x}\", sig_s);\n\n    let public_key = EcdsaPublicKey {\n        x: &pub_key_x,\n        y: &pub_key_y,\n    };\n\n    let signature = EcdsaSignature { r: &sig_r, s: &sig_s };\n\n    // Verify the signature\n    info!(\"Verifying signature...\");\n    match pka.ecdsa_verify(&curve, &public_key, &signature, &message_hash) {\n        Ok(true) => {\n            info!(\"Signature is VALID\");\n        }\n        Ok(false) => {\n            info!(\"Signature is INVALID\");\n        }\n        Err(e) => {\n            error!(\"Verification failed with error: {:?}\", e);\n        }\n    }\n\n    // Test with a tampered signature (modify one byte)\n    info!(\"=== Testing with tampered signature ===\");\n    let mut tampered_sig_r = sig_r;\n    tampered_sig_r[0] ^= 0x01; // Flip one bit\n\n    let tampered_signature = EcdsaSignature {\n        r: &tampered_sig_r,\n        s: &sig_s,\n    };\n\n    match pka.ecdsa_verify(&curve, &public_key, &tampered_signature, &message_hash) {\n        Ok(true) => {\n            error!(\"Tampered signature incorrectly verified as VALID!\");\n        }\n        Ok(false) => {\n            info!(\"Tampered signature correctly detected as INVALID\");\n        }\n        Err(e) => {\n            error!(\"Verification failed with error: {:?}\", e);\n        }\n    }\n\n    info!(\"=== ECDSA verification example complete ===\");\n\n    loop {\n        cortex_m::asm::wfi();\n    }\n}\n"
  },
  {
    "path": "examples/stm32wba/src/bin/pka_rsa.rs",
    "content": "//! PKA RSA Encryption/Decryption Example\n//!\n//! Demonstrates RSA encryption and decryption using the PKA hardware accelerator.\n//!\n//! # What This Example Shows\n//! - RSA encryption: ciphertext = plaintext^e mod n\n//! - RSA decryption: plaintext = ciphertext^d mod n\n//! - Using pre-computed Montgomery parameter for faster operations\n//!\n//! # RSA Basics\n//! - Public key: (n, e) where n is the modulus and e is the public exponent\n//! - Private key: d where d*e ≡ 1 (mod φ(n))\n//! - Encryption: C = M^e mod n\n//! - Decryption: M = C^d mod n\n//!\n//! # Note\n//! This example uses a small 512-bit key for demonstration purposes.\n//! In production, use at least 2048-bit keys!\n\n#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_stm32::pka::Pka;\nuse embassy_stm32::rcc::{\n    AHB5Prescaler, AHBPrescaler, APBPrescaler, PllDiv, PllMul, PllPreDiv, PllSource, Sysclk, VoltageScale,\n};\nuse embassy_stm32::{Config, bind_interrupts, peripherals};\nuse {defmt_rtt as _, panic_probe as _};\n\nbind_interrupts!(struct Irqs {\n    PKA => embassy_stm32::pka::InterruptHandler<peripherals::PKA>;\n});\n\n// RSA-2048 test key from STM32Cube HAL (production-grade key size)\n\n// Modulus n (2048 bits = 256 bytes)\nconst RSA_N: [u8; 256] = [\n    0xd7, 0x59, 0xdf, 0x92, 0x03, 0x12, 0x5a, 0x8b, 0xea, 0x06, 0xe2, 0x6d, 0x20, 0xcf, 0x50, 0x4f, 0x45, 0xe6, 0x3d,\n    0x04, 0x7e, 0x0b, 0xed, 0x8b, 0xd7, 0x3d, 0x74, 0xa1, 0x2e, 0xa7, 0xb6, 0x2a, 0xdc, 0xa4, 0x22, 0xbb, 0xac, 0xc8,\n    0xe7, 0x61, 0x2c, 0x5a, 0xbb, 0x01, 0x0d, 0xa0, 0x66, 0x94, 0x53, 0xe9, 0x01, 0xcf, 0x3f, 0xb3, 0x03, 0x05, 0x31,\n    0x00, 0x77, 0xbc, 0xb8, 0x1d, 0x1e, 0x45, 0xe4, 0x2b, 0x95, 0xf1, 0x35, 0xa5, 0x80, 0xc8, 0xeb, 0x3e, 0x5d, 0x5f,\n    0x93, 0x70, 0x6e, 0x5f, 0xad, 0x2c, 0x34, 0xf0, 0xb2, 0xd5, 0xec, 0x31, 0x19, 0x3e, 0x5f, 0xc7, 0xc1, 0x06, 0x9f,\n    0x91, 0x30, 0xea, 0x2b, 0xbe, 0x23, 0xed, 0x71, 0xb7, 0x3f, 0x04, 0xd2, 0xb5, 0x33, 0x8d, 0x5d, 0x48, 0x84, 0x39,\n    0x59, 0xc9, 0xd4, 0x98, 0xbb, 0x7a, 0x5c, 0xd1, 0x90, 0x58, 0x07, 0x68, 0xfa, 0xec, 0x02, 0x92, 0x5a, 0xe3, 0x97,\n    0x6d, 0xca, 0x39, 0x7c, 0x80, 0x34, 0xa5, 0x51, 0xb8, 0x3d, 0x15, 0x7a, 0x82, 0x85, 0x02, 0x54, 0xab, 0x6d, 0x55,\n    0x6a, 0xfd, 0x06, 0xf5, 0x46, 0x38, 0x2e, 0x70, 0x3e, 0x63, 0x19, 0x72, 0xf1, 0xa3, 0xa4, 0x8e, 0xa0, 0xf1, 0x45,\n    0xa1, 0x6f, 0x79, 0xd8, 0xff, 0x3c, 0x5b, 0x51, 0x83, 0x06, 0x3b, 0x11, 0x2c, 0x95, 0xe3, 0x12, 0x4f, 0x39, 0x3a,\n    0xc9, 0x12, 0x3c, 0x39, 0x7b, 0x5c, 0xaf, 0x34, 0x58, 0xc4, 0x17, 0x57, 0xf1, 0x7f, 0x77, 0xe0, 0x94, 0x6a, 0x57,\n    0x16, 0x47, 0x64, 0xea, 0x7e, 0xd8, 0xd3, 0x95, 0x5b, 0xe4, 0x7e, 0x93, 0x9e, 0xef, 0x47, 0x8c, 0x0b, 0x2e, 0x3a,\n    0x7f, 0x79, 0x4e, 0xc8, 0xa7, 0x5a, 0xb8, 0x41, 0xd4, 0xa8, 0x9b, 0xde, 0x52, 0xf7, 0x53, 0xd3, 0xa3, 0x6e, 0x23,\n    0xbb, 0xc4, 0x10, 0x4f, 0x32, 0x9a, 0x03, 0x3c, 0x31,\n];\n\n// Public exponent e (typically 65537 = 0x10001)\nconst RSA_E: [u8; 3] = [0x01, 0x00, 0x01];\n\n// Private exponent d (2048 bits = 256 bytes)\nconst RSA_D: [u8; 256] = [\n    0xa9, 0x60, 0x9c, 0xc1, 0xa0, 0xfc, 0xdc, 0x8e, 0xd3, 0x60, 0xda, 0xd2, 0x6e, 0x4d, 0xe0, 0xa2, 0x99, 0x1d, 0xbf,\n    0xbc, 0x3a, 0xcf, 0x72, 0xe4, 0xdc, 0x44, 0x0f, 0xe9, 0x7e, 0x62, 0x96, 0x9b, 0x1b, 0xb3, 0x55, 0x46, 0x3b, 0x5e,\n    0x40, 0xee, 0x63, 0x0e, 0x71, 0xab, 0x20, 0x66, 0x9a, 0x87, 0xeb, 0x7f, 0x86, 0xd6, 0xd5, 0x09, 0x1d, 0x45, 0x06,\n    0x07, 0x92, 0x25, 0xb2, 0xc1, 0xe4, 0x3f, 0xa0, 0x78, 0xcf, 0x94, 0x4a, 0x57, 0x83, 0xf5, 0x83, 0x61, 0x27, 0xdb,\n    0xb6, 0x81, 0x65, 0xae, 0x86, 0xec, 0x10, 0x2f, 0x88, 0xd9, 0x4c, 0xce, 0x49, 0x46, 0x8f, 0xda, 0xf2, 0xed, 0x1c,\n    0xaf, 0xfb, 0xc3, 0x12, 0xe8, 0x98, 0x25, 0x77, 0x9d, 0x63, 0x49, 0x8d, 0xd8, 0xcb, 0x55, 0x52, 0x9b, 0x68, 0xb4,\n    0x1a, 0xf4, 0xed, 0xeb, 0xba, 0xf9, 0x40, 0xeb, 0xeb, 0x15, 0xf1, 0xae, 0x16, 0x3b, 0xfc, 0x7e, 0x89, 0x90, 0x86,\n    0x66, 0x37, 0xa3, 0xdb, 0xcd, 0x73, 0x68, 0x3f, 0x6d, 0x9c, 0x85, 0x45, 0x5c, 0x21, 0xc6, 0xfe, 0x2d, 0x41, 0x67,\n    0x5c, 0x2f, 0x97, 0x3b, 0x03, 0x17, 0x81, 0x11, 0xd7, 0x1c, 0xf6, 0x6e, 0xf2, 0xd8, 0x90, 0x89, 0x9d, 0xbb, 0x75,\n    0xa7, 0x9f, 0x09, 0x0d, 0x83, 0x13, 0x28, 0xf1, 0x88, 0xe1, 0x48, 0x37, 0x1b, 0x20, 0x8c, 0x86, 0x2f, 0x27, 0x17,\n    0x0b, 0xe6, 0x70, 0x83, 0x1e, 0x69, 0x88, 0x8f, 0x72, 0x18, 0xd7, 0x83, 0x2b, 0xca, 0xab, 0xfe, 0x3c, 0x65, 0x23,\n    0xfe, 0xd0, 0xea, 0xd3, 0x2c, 0xc7, 0x9a, 0x2a, 0x54, 0xef, 0x0b, 0x12, 0xcd, 0x4d, 0xab, 0xf9, 0x9b, 0xa7, 0x66,\n    0x88, 0x3c, 0x7c, 0xfa, 0x2f, 0x19, 0x62, 0x8b, 0x1b, 0xb1, 0xbf, 0x18, 0xb2, 0x1b, 0xc7, 0x64, 0x0e, 0x00, 0xdd,\n    0xf1, 0x01, 0x83, 0x42, 0x9b, 0x0c, 0xe7, 0x57, 0xc9,\n];\n\n#[embassy_executor::main]\nasync fn main(_spawner: embassy_executor::Spawner) {\n    let mut config = Config::default();\n    config.rcc.pll1 = Some(embassy_stm32::rcc::Pll {\n        source: PllSource::HSI,\n        prediv: PllPreDiv::DIV1,\n        mul: PllMul::MUL30,\n        divr: Some(PllDiv::DIV5),\n        divq: None,\n        divp: Some(PllDiv::DIV30),\n        frac: Some(0),\n    });\n    config.rcc.ahb_pre = AHBPrescaler::DIV1;\n    config.rcc.apb1_pre = APBPrescaler::DIV1;\n    config.rcc.apb2_pre = APBPrescaler::DIV1;\n    config.rcc.apb7_pre = APBPrescaler::DIV1;\n    config.rcc.ahb5_pre = AHB5Prescaler::DIV4;\n    config.rcc.voltage_scale = VoltageScale::RANGE1;\n    config.rcc.sys = Sysclk::PLL1_R;\n\n    let p = embassy_stm32::init(config);\n    info!(\"PKA RSA Encryption/Decryption Example\");\n\n    let mut pka = Pka::new_blocking(p.PKA, Irqs);\n\n    // Plaintext message (padded to modulus size)\n    // In real applications, use proper padding like OAEP or PKCS#1 v1.5\n    let mut plaintext = [0u8; 256];\n    // Simple message: \"Hello RSA!\" followed by zeros\n    plaintext[246..256].copy_from_slice(b\"Hello RSA!\");\n\n    info!(\"=== RSA Encryption/Decryption Example ===\");\n    info!(\"Key size: 2048 bits\");\n    info!(\"Plaintext (last 10 bytes): {:02x}\", &plaintext[246..]);\n\n    // Step 1: Compute Montgomery parameter for the modulus\n    // This speeds up subsequent operations with the same modulus\n    info!(\"Computing Montgomery parameter...\");\n    let mut montgomery_param = [0u32; 64]; // 256 bytes / 4 = 64 words\n    match pka.montgomery_param(&RSA_N, &mut montgomery_param) {\n        Ok(()) => {\n            info!(\"Montgomery parameter computed successfully\");\n        }\n        Err(e) => {\n            error!(\"Failed to compute Montgomery parameter: {:?}\", e);\n            loop {\n                cortex_m::asm::wfi();\n            }\n        }\n    }\n\n    // Step 2: Encrypt using public key (n, e)\n    // C = M^e mod n\n    info!(\"=== RSA Encryption ===\");\n    info!(\"Computing ciphertext = plaintext^e mod n\");\n\n    let mut ciphertext = [0u8; 256];\n    match pka.modular_exp(&plaintext, &RSA_E, &RSA_N, &mut ciphertext) {\n        Ok(()) => {\n            info!(\"Encryption successful!\");\n            info!(\"Ciphertext (first 16 bytes): {:02x}\", &ciphertext[..16]);\n        }\n        Err(e) => {\n            error!(\"Encryption failed: {:?}\", e);\n            loop {\n                cortex_m::asm::wfi();\n            }\n        }\n    }\n\n    // Step 3: Decrypt using private key d\n    // M = C^d mod n\n    info!(\"=== RSA Decryption ===\");\n    info!(\"Computing plaintext = ciphertext^d mod n\");\n\n    let mut decrypted = [0u8; 256];\n    match pka.modular_exp(&ciphertext, &RSA_D, &RSA_N, &mut decrypted) {\n        Ok(()) => {\n            info!(\"Decryption successful!\");\n            info!(\"Decrypted (last 10 bytes): {:02x}\", &decrypted[246..]);\n\n            // Verify decryption\n            if decrypted == plaintext {\n                info!(\"Decryption verified: plaintext matches!\");\n            } else {\n                error!(\"Decryption verification FAILED!\");\n            }\n        }\n        Err(e) => {\n            error!(\"Decryption failed: {:?}\", e);\n            loop {\n                cortex_m::asm::wfi();\n            }\n        }\n    }\n\n    // Step 4: Demonstrate fast decryption with pre-computed Montgomery parameter\n    info!(\"=== RSA Fast Decryption (with Montgomery param) ===\");\n\n    let mut decrypted_fast = [0u8; 256];\n    match pka.modular_exp_fast(&ciphertext, &RSA_D, &RSA_N, &montgomery_param, &mut decrypted_fast) {\n        Ok(()) => {\n            info!(\"Fast decryption successful!\");\n            if decrypted_fast == plaintext {\n                info!(\"Fast decryption verified: plaintext matches!\");\n            } else {\n                error!(\"Fast decryption verification FAILED!\");\n            }\n        }\n        Err(e) => {\n            error!(\"Fast decryption failed: {:?}\", e);\n        }\n    }\n\n    info!(\"=== RSA example complete ===\");\n\n    loop {\n        cortex_m::asm::wfi();\n    }\n}\n"
  },
  {
    "path": "examples/stm32wba/src/bin/pka_rsa_crt.rs",
    "content": "//! PKA RSA-CRT Decryption Example\n//!\n//! Demonstrates RSA decryption using the Chinese Remainder Theorem (CRT)\n//! optimization with the PKA hardware accelerator.\n//!\n//! # What This Example Shows\n//! - CRT-based RSA decryption (approximately 4x faster than standard RSA)\n//! - Computing and using CRT parameters (dp, dq, qinv)\n//! - Comparing performance with standard RSA decryption\n//!\n//! # CRT-RSA Basics\n//! Instead of computing M = C^d mod n directly, CRT computes:\n//! - M1 = C^dp mod p (where dp = d mod (p-1))\n//! - M2 = C^dq mod q (where dq = d mod (q-1))\n//! - M = M2 + q * (qinv * (M1 - M2) mod p)\n//!\n//! This is faster because:\n//! - Exponents dp and dq are half the size of d\n//! - Modular operations use smaller moduli p and q\n//!\n//! # CRT Parameters\n//! - p, q: Prime factors of n\n//! - dp = d mod (p-1)\n//! - dq = d mod (q-1)\n//! - qinv = q^(-1) mod p\n//!\n//! # Note\n//! This example uses a small 512-bit key for demonstration.\n//! In production, use at least 2048-bit keys!\n\n#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_stm32::pka::{Pka, RsaCrtParams};\nuse embassy_stm32::rcc::{\n    AHB5Prescaler, AHBPrescaler, APBPrescaler, PllDiv, PllMul, PllPreDiv, PllSource, Sysclk, VoltageScale,\n};\nuse embassy_stm32::{Config, bind_interrupts, peripherals};\nuse {defmt_rtt as _, panic_probe as _};\n\nbind_interrupts!(struct Irqs {\n    PKA => embassy_stm32::pka::InterruptHandler<peripherals::PKA>;\n});\n\n// RSA-2048 test key from STM32Cube HAL\n\n// Modulus n (2048 bits = 256 bytes)\nconst RSA_N: [u8; 256] = [\n    0xd7, 0x59, 0xdf, 0x92, 0x03, 0x12, 0x5a, 0x8b, 0xea, 0x06, 0xe2, 0x6d, 0x20, 0xcf, 0x50, 0x4f, 0x45, 0xe6, 0x3d,\n    0x04, 0x7e, 0x0b, 0xed, 0x8b, 0xd7, 0x3d, 0x74, 0xa1, 0x2e, 0xa7, 0xb6, 0x2a, 0xdc, 0xa4, 0x22, 0xbb, 0xac, 0xc8,\n    0xe7, 0x61, 0x2c, 0x5a, 0xbb, 0x01, 0x0d, 0xa0, 0x66, 0x94, 0x53, 0xe9, 0x01, 0xcf, 0x3f, 0xb3, 0x03, 0x05, 0x31,\n    0x00, 0x77, 0xbc, 0xb8, 0x1d, 0x1e, 0x45, 0xe4, 0x2b, 0x95, 0xf1, 0x35, 0xa5, 0x80, 0xc8, 0xeb, 0x3e, 0x5d, 0x5f,\n    0x93, 0x70, 0x6e, 0x5f, 0xad, 0x2c, 0x34, 0xf0, 0xb2, 0xd5, 0xec, 0x31, 0x19, 0x3e, 0x5f, 0xc7, 0xc1, 0x06, 0x9f,\n    0x91, 0x30, 0xea, 0x2b, 0xbe, 0x23, 0xed, 0x71, 0xb7, 0x3f, 0x04, 0xd2, 0xb5, 0x33, 0x8d, 0x5d, 0x48, 0x84, 0x39,\n    0x59, 0xc9, 0xd4, 0x98, 0xbb, 0x7a, 0x5c, 0xd1, 0x90, 0x58, 0x07, 0x68, 0xfa, 0xec, 0x02, 0x92, 0x5a, 0xe3, 0x97,\n    0x6d, 0xca, 0x39, 0x7c, 0x80, 0x34, 0xa5, 0x51, 0xb8, 0x3d, 0x15, 0x7a, 0x82, 0x85, 0x02, 0x54, 0xab, 0x6d, 0x55,\n    0x6a, 0xfd, 0x06, 0xf5, 0x46, 0x38, 0x2e, 0x70, 0x3e, 0x63, 0x19, 0x72, 0xf1, 0xa3, 0xa4, 0x8e, 0xa0, 0xf1, 0x45,\n    0xa1, 0x6f, 0x79, 0xd8, 0xff, 0x3c, 0x5b, 0x51, 0x83, 0x06, 0x3b, 0x11, 0x2c, 0x95, 0xe3, 0x12, 0x4f, 0x39, 0x3a,\n    0xc9, 0x12, 0x3c, 0x39, 0x7b, 0x5c, 0xaf, 0x34, 0x58, 0xc4, 0x17, 0x57, 0xf1, 0x7f, 0x77, 0xe0, 0x94, 0x6a, 0x57,\n    0x16, 0x47, 0x64, 0xea, 0x7e, 0xd8, 0xd3, 0x95, 0x5b, 0xe4, 0x7e, 0x93, 0x9e, 0xef, 0x47, 0x8c, 0x0b, 0x2e, 0x3a,\n    0x7f, 0x79, 0x4e, 0xc8, 0xa7, 0x5a, 0xb8, 0x41, 0xd4, 0xa8, 0x9b, 0xde, 0x52, 0xf7, 0x53, 0xd3, 0xa3, 0x6e, 0x23,\n    0xbb, 0xc4, 0x10, 0x4f, 0x32, 0x9a, 0x03, 0x3c, 0x31,\n];\n\n// Public exponent e (typically 65537 = 0x10001)\nconst RSA_E: [u8; 3] = [0x01, 0x00, 0x01];\n\n// Private exponent d (2048 bits = 256 bytes)\nconst RSA_D: [u8; 256] = [\n    0xa9, 0x60, 0x9c, 0xc1, 0xa0, 0xfc, 0xdc, 0x8e, 0xd3, 0x60, 0xda, 0xd2, 0x6e, 0x4d, 0xe0, 0xa2, 0x99, 0x1d, 0xbf,\n    0xbc, 0x3a, 0xcf, 0x72, 0xe4, 0xdc, 0x44, 0x0f, 0xe9, 0x7e, 0x62, 0x96, 0x9b, 0x1b, 0xb3, 0x55, 0x46, 0x3b, 0x5e,\n    0x40, 0xee, 0x63, 0x0e, 0x71, 0xab, 0x20, 0x66, 0x9a, 0x87, 0xeb, 0x7f, 0x86, 0xd6, 0xd5, 0x09, 0x1d, 0x45, 0x06,\n    0x07, 0x92, 0x25, 0xb2, 0xc1, 0xe4, 0x3f, 0xa0, 0x78, 0xcf, 0x94, 0x4a, 0x57, 0x83, 0xf5, 0x83, 0x61, 0x27, 0xdb,\n    0xb6, 0x81, 0x65, 0xae, 0x86, 0xec, 0x10, 0x2f, 0x88, 0xd9, 0x4c, 0xce, 0x49, 0x46, 0x8f, 0xda, 0xf2, 0xed, 0x1c,\n    0xaf, 0xfb, 0xc3, 0x12, 0xe8, 0x98, 0x25, 0x77, 0x9d, 0x63, 0x49, 0x8d, 0xd8, 0xcb, 0x55, 0x52, 0x9b, 0x68, 0xb4,\n    0x1a, 0xf4, 0xed, 0xeb, 0xba, 0xf9, 0x40, 0xeb, 0xeb, 0x15, 0xf1, 0xae, 0x16, 0x3b, 0xfc, 0x7e, 0x89, 0x90, 0x86,\n    0x66, 0x37, 0xa3, 0xdb, 0xcd, 0x73, 0x68, 0x3f, 0x6d, 0x9c, 0x85, 0x45, 0x5c, 0x21, 0xc6, 0xfe, 0x2d, 0x41, 0x67,\n    0x5c, 0x2f, 0x97, 0x3b, 0x03, 0x17, 0x81, 0x11, 0xd7, 0x1c, 0xf6, 0x6e, 0xf2, 0xd8, 0x90, 0x89, 0x9d, 0xbb, 0x75,\n    0xa7, 0x9f, 0x09, 0x0d, 0x83, 0x13, 0x28, 0xf1, 0x88, 0xe1, 0x48, 0x37, 0x1b, 0x20, 0x8c, 0x86, 0x2f, 0x27, 0x17,\n    0x0b, 0xe6, 0x70, 0x83, 0x1e, 0x69, 0x88, 0x8f, 0x72, 0x18, 0xd7, 0x83, 0x2b, 0xca, 0xab, 0xfe, 0x3c, 0x65, 0x23,\n    0xfe, 0xd0, 0xea, 0xd3, 0x2c, 0xc7, 0x9a, 0x2a, 0x54, 0xef, 0x0b, 0x12, 0xcd, 0x4d, 0xab, 0xf9, 0x9b, 0xa7, 0x66,\n    0x88, 0x3c, 0x7c, 0xfa, 0x2f, 0x19, 0x62, 0x8b, 0x1b, 0xb1, 0xbf, 0x18, 0xb2, 0x1b, 0xc7, 0x64, 0x0e, 0x00, 0xdd,\n    0xf1, 0x01, 0x83, 0x42, 0x9b, 0x0c, 0xe7, 0x57, 0xc9,\n];\n\n// CRT Parameters\n\n// Prime p (1024 bits = 128 bytes)\nconst RSA_P: [u8; 128] = [\n    0xfb, 0xe4, 0x71, 0xf9, 0x77, 0x3d, 0xaa, 0x8d, 0xc5, 0x38, 0xdb, 0x2d, 0xa8, 0x9a, 0x4b, 0x94, 0xdb, 0xf5, 0x99,\n    0xf4, 0x89, 0x02, 0x94, 0xbb, 0xc5, 0x74, 0x55, 0x1b, 0xa4, 0x03, 0x62, 0x5d, 0xc5, 0xf0, 0x1b, 0x49, 0xa3, 0xf3,\n    0x1b, 0x47, 0xec, 0x59, 0xf5, 0x1f, 0x01, 0x6b, 0x8a, 0x72, 0xe8, 0x78, 0x9c, 0xc7, 0xa1, 0x12, 0xcc, 0xc1, 0xb3,\n    0x5c, 0xcb, 0xad, 0xa0, 0xca, 0xf5, 0x66, 0x30, 0x2b, 0x01, 0xe9, 0x14, 0x93, 0xf9, 0xd2, 0xb3, 0xd1, 0x50, 0x3d,\n    0xda, 0x47, 0xbe, 0xa9, 0x45, 0x61, 0x52, 0x6c, 0x3d, 0x5a, 0xee, 0x83, 0x72, 0x92, 0xbb, 0x52, 0x22, 0xb5, 0xf8,\n    0xad, 0x03, 0x4d, 0xd1, 0xb4, 0xe0, 0x5a, 0xd3, 0xc4, 0x28, 0xff, 0x1d, 0xcc, 0x67, 0x4e, 0xf3, 0x55, 0xd6, 0x0e,\n    0xd9, 0x0b, 0xd5, 0x72, 0xcc, 0xc0, 0x8e, 0x0e, 0xad, 0x50, 0xd8, 0x93, 0xd6, 0xf3,\n];\n\n// Prime q (1024 bits = 128 bytes)\nconst RSA_Q: [u8; 128] = [\n    0xda, 0xdc, 0xe1, 0xd1, 0x34, 0x4e, 0xec, 0xe4, 0xbe, 0x4b, 0x74, 0xa7, 0xbb, 0x48, 0xee, 0xe0, 0x31, 0xad, 0xde,\n    0xac, 0x04, 0x3c, 0x69, 0x35, 0xb9, 0x53, 0x51, 0x7f, 0x8c, 0xff, 0x3a, 0x75, 0x9e, 0x9b, 0x78, 0xb6, 0xae, 0x81,\n    0xfb, 0xe1, 0x7c, 0xf3, 0xa2, 0x38, 0xcd, 0xd3, 0x48, 0x70, 0x55, 0x60, 0x1a, 0xe7, 0x06, 0xb5, 0xc7, 0x6c, 0xec,\n    0x3d, 0x97, 0xe9, 0xde, 0xe3, 0xc4, 0xb4, 0xad, 0x3d, 0x50, 0x32, 0x75, 0x9a, 0x0c, 0xe0, 0x46, 0xc0, 0x13, 0x3f,\n    0x2c, 0x56, 0x7d, 0xca, 0xae, 0xa8, 0x0c, 0xb1, 0x60, 0xb0, 0x5c, 0xbe, 0xb8, 0x0a, 0xa9, 0x4c, 0x37, 0x93, 0x59,\n    0x78, 0x19, 0xb6, 0x3c, 0x0a, 0x60, 0x51, 0x7f, 0xeb, 0xca, 0x46, 0xe5, 0x24, 0x3c, 0x11, 0xa9, 0x32, 0x4b, 0x5c,\n    0x4f, 0x32, 0x23, 0x99, 0x3e, 0x33, 0x0b, 0xde, 0xab, 0x04, 0xc0, 0x66, 0x71, 0x4b,\n];\n\n// dp = d mod (p-1) (1024 bits = 128 bytes)\nconst RSA_DP: [u8; 128] = [\n    0x80, 0xb0, 0x43, 0x72, 0x59, 0xf3, 0x0d, 0x51, 0x84, 0xb2, 0xf9, 0x77, 0x28, 0x2e, 0x3b, 0xf2, 0xff, 0x35, 0x48,\n    0xc4, 0x5f, 0xc3, 0x0b, 0xcd, 0xaa, 0xa1, 0x36, 0x61, 0xfa, 0xb7, 0x27, 0xe8, 0x14, 0x9a, 0x08, 0xb6, 0xe4, 0xa5,\n    0xed, 0x08, 0x1f, 0xbe, 0x0d, 0xb7, 0x1b, 0x78, 0x9f, 0xb0, 0xf9, 0x07, 0xb5, 0xc1, 0x5f, 0x8f, 0x45, 0x40, 0xa8,\n    0xab, 0xfd, 0xfa, 0xe4, 0xad, 0x0c, 0x16, 0x7b, 0x01, 0x5d, 0xe6, 0x80, 0x76, 0xe1, 0x29, 0x3e, 0x68, 0xef, 0x7f,\n    0xca, 0x26, 0xe1, 0x47, 0x85, 0x84, 0xa5, 0x21, 0x5b, 0x6f, 0x3b, 0x6f, 0xb8, 0x77, 0x32, 0x70, 0x51, 0xff, 0x79,\n    0xde, 0x9a, 0x53, 0x85, 0x91, 0xcd, 0x15, 0x5a, 0x1f, 0x5b, 0x7a, 0x8a, 0xf4, 0xc4, 0xca, 0xd7, 0x12, 0xc1, 0x5b,\n    0xb0, 0x93, 0x95, 0x27, 0x23, 0x68, 0x34, 0xab, 0x56, 0xec, 0x78, 0xd9, 0x7e, 0xf5,\n];\n\n// dq = d mod (q-1) (1024 bits = 128 bytes)\nconst RSA_DQ: [u8; 128] = [\n    0x12, 0x02, 0x95, 0x2e, 0x93, 0x00, 0x5f, 0xac, 0x1f, 0x20, 0xb4, 0x73, 0xcd, 0x0c, 0x9e, 0x63, 0xa2, 0x92, 0xed,\n    0x3c, 0xf8, 0x88, 0x44, 0x1c, 0x20, 0xa9, 0x03, 0x8e, 0xdc, 0x7a, 0x70, 0x44, 0x17, 0x8e, 0x31, 0xab, 0xce, 0xc6,\n    0x71, 0x84, 0xc7, 0xb4, 0x80, 0xc7, 0xed, 0xe0, 0x12, 0x18, 0xf4, 0x5d, 0x99, 0x39, 0x23, 0xab, 0x37, 0xc2, 0xf5,\n    0xd9, 0xc7, 0xb3, 0x7e, 0x1c, 0xfe, 0x25, 0xe4, 0x0f, 0xa4, 0x96, 0xd2, 0x68, 0x9f, 0xe0, 0xa0, 0xd1, 0xd3, 0x83,\n    0xa2, 0x51, 0x67, 0xbe, 0x93, 0x0a, 0xcf, 0x28, 0x95, 0x8d, 0x4d, 0xc4, 0x7f, 0xfe, 0x98, 0x99, 0xe6, 0x04, 0xe1,\n    0x1a, 0xe9, 0xfa, 0xbe, 0x0c, 0x18, 0x8a, 0xfc, 0x5c, 0xd9, 0xe3, 0x65, 0x9d, 0xca, 0xb7, 0xa5, 0x55, 0xb7, 0x2f,\n    0xdc, 0x70, 0x82, 0xcf, 0x6c, 0x77, 0xe4, 0xe5, 0x28, 0xeb, 0x96, 0x2d, 0x97, 0xeb,\n];\n\n// qinv = q^(-1) mod p (1024 bits = 128 bytes)\nconst RSA_QINV: [u8; 128] = [\n    0x94, 0x26, 0x76, 0x23, 0xe9, 0x0e, 0x73, 0xcc, 0xd5, 0x3b, 0x18, 0xa9, 0xaa, 0x3c, 0xb4, 0x1d, 0x71, 0x69, 0x7a,\n    0x8d, 0x5f, 0xb2, 0xfe, 0x90, 0x46, 0x55, 0x4f, 0x71, 0x78, 0x11, 0x90, 0x5f, 0xe5, 0x7f, 0x5b, 0xb3, 0xc3, 0x56,\n    0xf5, 0x9a, 0xeb, 0x09, 0xc2, 0x7f, 0x4c, 0x91, 0xa8, 0x11, 0x60, 0x28, 0x04, 0xce, 0xf7, 0x34, 0x06, 0xc9, 0x22,\n    0xd1, 0x3c, 0x9b, 0x99, 0x72, 0x8c, 0xfb, 0x74, 0x1a, 0x12, 0x9a, 0xf7, 0x9f, 0x59, 0x30, 0xe9, 0x63, 0x7e, 0x05,\n    0x21, 0x03, 0x4c, 0x82, 0x20, 0x58, 0xbc, 0xb3, 0x28, 0xa6, 0x16, 0xb1, 0x99, 0x41, 0x81, 0xe4, 0x32, 0xde, 0x1e,\n    0xa4, 0x2e, 0x2b, 0xff, 0xf5, 0x1b, 0xe3, 0x89, 0xc5, 0x1e, 0xa6, 0x74, 0x8d, 0xa1, 0x49, 0x68, 0x8d, 0x8a, 0xc0,\n    0x7e, 0x87, 0x6c, 0x2d, 0x74, 0x36, 0x4f, 0x36, 0x76, 0xbe, 0x3d, 0xc6, 0x50, 0x7a,\n];\n\n#[embassy_executor::main]\nasync fn main(_spawner: embassy_executor::Spawner) {\n    let mut config = Config::default();\n    config.rcc.pll1 = Some(embassy_stm32::rcc::Pll {\n        source: PllSource::HSI,\n        prediv: PllPreDiv::DIV1,\n        mul: PllMul::MUL30,\n        divr: Some(PllDiv::DIV5),\n        divq: None,\n        divp: Some(PllDiv::DIV30),\n        frac: Some(0),\n    });\n    config.rcc.ahb_pre = AHBPrescaler::DIV1;\n    config.rcc.apb1_pre = APBPrescaler::DIV1;\n    config.rcc.apb2_pre = APBPrescaler::DIV1;\n    config.rcc.apb7_pre = APBPrescaler::DIV1;\n    config.rcc.ahb5_pre = AHB5Prescaler::DIV4;\n    config.rcc.voltage_scale = VoltageScale::RANGE1;\n    config.rcc.sys = Sysclk::PLL1_R;\n\n    let p = embassy_stm32::init(config);\n    info!(\"PKA RSA-CRT Decryption Example\");\n\n    let mut pka = Pka::new_blocking(p.PKA, Irqs);\n\n    info!(\"=== RSA-CRT Decryption Example ===\");\n    info!(\"Key size: 2048 bits\");\n    info!(\"CRT provides ~4x speedup over standard RSA decryption\");\n\n    // Create plaintext message\n    let mut plaintext = [0u8; 256];\n    // Simple test message\n    plaintext[246..256].copy_from_slice(b\"CRT-RSA!!!\");\n\n    info!(\"Original message (last 10 bytes): {:02x}\", &plaintext[246..]);\n\n    // Step 1: Encrypt with public key (standard RSA encryption)\n    info!(\"=== Step 1: RSA Encryption (Public Key) ===\");\n\n    let mut ciphertext = [0u8; 256];\n    match pka.modular_exp(&plaintext, &RSA_E, &RSA_N, &mut ciphertext) {\n        Ok(()) => {\n            info!(\"Encryption successful\");\n            info!(\"Ciphertext (first 16 bytes): {:02x}\", &ciphertext[..16]);\n        }\n        Err(e) => {\n            error!(\"Encryption failed: {:?}\", e);\n            loop {\n                cortex_m::asm::wfi();\n            }\n        }\n    }\n\n    // Step 2: Decrypt using standard RSA (for comparison)\n    info!(\"=== Step 2: Standard RSA Decryption ===\");\n    info!(\"M = C^d mod n (full exponent)\");\n\n    let mut decrypted_standard = [0u8; 256];\n    match pka.modular_exp(&ciphertext, &RSA_D, &RSA_N, &mut decrypted_standard) {\n        Ok(()) => {\n            info!(\"Standard decryption complete\");\n            if decrypted_standard == plaintext {\n                info!(\"Standard decryption verified!\");\n            } else {\n                error!(\"Standard decryption FAILED!\");\n            }\n        }\n        Err(e) => {\n            error!(\"Standard decryption failed: {:?}\", e);\n        }\n    }\n\n    // Step 3: Decrypt using RSA-CRT (faster!)\n    info!(\"=== Step 3: RSA-CRT Decryption ===\");\n    info!(\"Uses smaller exponents dp, dq for ~4x speedup\");\n    info!(\"CRT parameters (all 1024 bits each):\");\n    info!(\"  - p: {} bytes\", RSA_P.len());\n    info!(\"  - q: {} bytes\", RSA_Q.len());\n    info!(\"  - dp = d mod (p-1): {} bytes\", RSA_DP.len());\n    info!(\"  - dq = d mod (q-1): {} bytes\", RSA_DQ.len());\n    info!(\"  - qinv = q^(-1) mod p: {} bytes\", RSA_QINV.len());\n\n    let crt_params = RsaCrtParams {\n        prime_p: &RSA_P,\n        prime_q: &RSA_Q,\n        dp: &RSA_DP,\n        dq: &RSA_DQ,\n        qinv: &RSA_QINV,\n    };\n\n    let mut decrypted_crt = [0u8; 256];\n    match pka.rsa_crt_exp(&ciphertext, &crt_params, &mut decrypted_crt) {\n        Ok(()) => {\n            info!(\"CRT decryption complete\");\n            info!(\"Decrypted (last 10 bytes): {:02x}\", &decrypted_crt[246..]);\n\n            if decrypted_crt == plaintext {\n                info!(\"CRT decryption verified!\");\n                info!(\"CRT result matches original plaintext\");\n            } else {\n                error!(\"CRT decryption verification FAILED!\");\n            }\n        }\n        Err(e) => {\n            error!(\"CRT decryption failed: {:?}\", e);\n            loop {\n                cortex_m::asm::wfi();\n            }\n        }\n    }\n\n    // Step 4: Verify both methods produce the same result\n    info!(\"=== Step 4: Comparing Standard vs CRT Results ===\");\n\n    if decrypted_standard == decrypted_crt {\n        info!(\"Both decryption methods produce identical results!\");\n    } else {\n        error!(\"Standard and CRT results differ!\");\n    }\n\n    // Step 5: Multiple decryption demonstration\n    info!(\"=== Step 5: Multiple CRT Decryptions ===\");\n    info!(\"Demonstrating CRT decryption with different messages\");\n\n    for i in 0..3u8 {\n        // Create different test messages\n        let mut msg = [0u8; 256];\n        msg[255] = i + 1;\n\n        // Encrypt\n        let mut ct = [0u8; 256];\n        if pka.modular_exp(&msg, &RSA_E, &RSA_N, &mut ct).is_ok() {\n            // Decrypt with CRT\n            let mut pt = [0u8; 256];\n            if pka.rsa_crt_exp(&ct, &crt_params, &mut pt).is_ok() {\n                if pt == msg {\n                    info!(\"Message {}: CRT decryption OK\", i + 1);\n                } else {\n                    error!(\"Message {}: CRT decryption FAILED\", i + 1);\n                }\n            }\n        }\n    }\n\n    info!(\"=== RSA-CRT Example Complete ===\");\n    info!(\"Summary:\");\n    info!(\"  - Standard RSA: C^d mod n (2048-bit exponent)\");\n    info!(\"  - CRT RSA: Uses dp, dq (1024-bit exponents)\");\n    info!(\"  - CRT is ~4x faster for private key operations\");\n    info!(\"  - Both methods produce identical results\");\n\n    loop {\n        cortex_m::asm::wfi();\n    }\n}\n"
  },
  {
    "path": "examples/stm32wba/src/bin/pka_rsa_keygen.rs",
    "content": "//! PKA RSA Key Finalization Example\n//!\n//! Demonstrates RSA key preparation and validation using PKA hardware.\n//!\n//! # What This Example Shows\n//! - Computing the Montgomery parameter R² mod n (required for fast operations)\n//! - Validating RSA key relationships using modular arithmetic\n//! - Using comparison operations for big integer validation\n//!\n//! # RSA Key Components\n//! - n = p * q (modulus, product of two primes)\n//! - e (public exponent, typically 65537)\n//! - d (private exponent, where e*d ≡ 1 mod φ(n))\n//! - φ(n) = (p-1)(q-1) (Euler's totient)\n//!\n//! # Key Finalization Steps\n//! 1. Compute Montgomery parameter R² mod n for fast modular operations\n//! 2. Optionally verify e*d ≡ 1 (mod φ(n))\n//! 3. Pre-compute CRT parameters if using CRT decryption\n//!\n//! # Note\n//! This example uses a small 512-bit key for demonstration.\n//! In production, use at least 2048-bit keys!\n\n#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_stm32::pka::{ComparisonResult, Pka};\nuse embassy_stm32::rcc::{\n    AHB5Prescaler, AHBPrescaler, APBPrescaler, PllDiv, PllMul, PllPreDiv, PllSource, Sysclk, VoltageScale,\n};\nuse embassy_stm32::{Config, bind_interrupts, peripherals};\nuse {defmt_rtt as _, panic_probe as _};\n\nbind_interrupts!(struct Irqs {\n    PKA => embassy_stm32::pka::InterruptHandler<peripherals::PKA>;\n});\n\n// RSA-2048 test key from STM32Cube HAL\n// n = p * q\nconst RSA_N: [u8; 256] = [\n    0xd7, 0x59, 0xdf, 0x92, 0x03, 0x12, 0x5a, 0x8b, 0xea, 0x06, 0xe2, 0x6d, 0x20, 0xcf, 0x50, 0x4f, 0x45, 0xe6, 0x3d,\n    0x04, 0x7e, 0x0b, 0xed, 0x8b, 0xd7, 0x3d, 0x74, 0xa1, 0x2e, 0xa7, 0xb6, 0x2a, 0xdc, 0xa4, 0x22, 0xbb, 0xac, 0xc8,\n    0xe7, 0x61, 0x2c, 0x5a, 0xbb, 0x01, 0x0d, 0xa0, 0x66, 0x94, 0x53, 0xe9, 0x01, 0xcf, 0x3f, 0xb3, 0x03, 0x05, 0x31,\n    0x00, 0x77, 0xbc, 0xb8, 0x1d, 0x1e, 0x45, 0xe4, 0x2b, 0x95, 0xf1, 0x35, 0xa5, 0x80, 0xc8, 0xeb, 0x3e, 0x5d, 0x5f,\n    0x93, 0x70, 0x6e, 0x5f, 0xad, 0x2c, 0x34, 0xf0, 0xb2, 0xd5, 0xec, 0x31, 0x19, 0x3e, 0x5f, 0xc7, 0xc1, 0x06, 0x9f,\n    0x91, 0x30, 0xea, 0x2b, 0xbe, 0x23, 0xed, 0x71, 0xb7, 0x3f, 0x04, 0xd2, 0xb5, 0x33, 0x8d, 0x5d, 0x48, 0x84, 0x39,\n    0x59, 0xc9, 0xd4, 0x98, 0xbb, 0x7a, 0x5c, 0xd1, 0x90, 0x58, 0x07, 0x68, 0xfa, 0xec, 0x02, 0x92, 0x5a, 0xe3, 0x97,\n    0x6d, 0xca, 0x39, 0x7c, 0x80, 0x34, 0xa5, 0x51, 0xb8, 0x3d, 0x15, 0x7a, 0x82, 0x85, 0x02, 0x54, 0xab, 0x6d, 0x55,\n    0x6a, 0xfd, 0x06, 0xf5, 0x46, 0x38, 0x2e, 0x70, 0x3e, 0x63, 0x19, 0x72, 0xf1, 0xa3, 0xa4, 0x8e, 0xa0, 0xf1, 0x45,\n    0xa1, 0x6f, 0x79, 0xd8, 0xff, 0x3c, 0x5b, 0x51, 0x83, 0x06, 0x3b, 0x11, 0x2c, 0x95, 0xe3, 0x12, 0x4f, 0x39, 0x3a,\n    0xc9, 0x12, 0x3c, 0x39, 0x7b, 0x5c, 0xaf, 0x34, 0x58, 0xc4, 0x17, 0x57, 0xf1, 0x7f, 0x77, 0xe0, 0x94, 0x6a, 0x57,\n    0x16, 0x47, 0x64, 0xea, 0x7e, 0xd8, 0xd3, 0x95, 0x5b, 0xe4, 0x7e, 0x93, 0x9e, 0xef, 0x47, 0x8c, 0x0b, 0x2e, 0x3a,\n    0x7f, 0x79, 0x4e, 0xc8, 0xa7, 0x5a, 0xb8, 0x41, 0xd4, 0xa8, 0x9b, 0xde, 0x52, 0xf7, 0x53, 0xd3, 0xa3, 0x6e, 0x23,\n    0xbb, 0xc4, 0x10, 0x4f, 0x32, 0x9a, 0x03, 0x3c, 0x31,\n];\n\n// Prime p (1024 bits = 128 bytes)\nconst RSA_P: [u8; 128] = [\n    0xfb, 0xe4, 0x71, 0xf9, 0x77, 0x3d, 0xaa, 0x8d, 0xc5, 0x38, 0xdb, 0x2d, 0xa8, 0x9a, 0x4b, 0x94, 0xdb, 0xf5, 0x99,\n    0xf4, 0x89, 0x02, 0x94, 0xbb, 0xc5, 0x74, 0x55, 0x1b, 0xa4, 0x03, 0x62, 0x5d, 0xc5, 0xf0, 0x1b, 0x49, 0xa3, 0xf3,\n    0x1b, 0x47, 0xec, 0x59, 0xf5, 0x1f, 0x01, 0x6b, 0x8a, 0x72, 0xe8, 0x78, 0x9c, 0xc7, 0xa1, 0x12, 0xcc, 0xc1, 0xb3,\n    0x5c, 0xcb, 0xad, 0xa0, 0xca, 0xf5, 0x66, 0x30, 0x2b, 0x01, 0xe9, 0x14, 0x93, 0xf9, 0xd2, 0xb3, 0xd1, 0x50, 0x3d,\n    0xda, 0x47, 0xbe, 0xa9, 0x45, 0x61, 0x52, 0x6c, 0x3d, 0x5a, 0xee, 0x83, 0x72, 0x92, 0xbb, 0x52, 0x22, 0xb5, 0xf8,\n    0xad, 0x03, 0x4d, 0xd1, 0xb4, 0xe0, 0x5a, 0xd3, 0xc4, 0x28, 0xff, 0x1d, 0xcc, 0x67, 0x4e, 0xf3, 0x55, 0xd6, 0x0e,\n    0xd9, 0x0b, 0xd5, 0x72, 0xcc, 0xc0, 0x8e, 0x0e, 0xad, 0x50, 0xd8, 0x93, 0xd6, 0xf3,\n];\n\n// Prime q (1024 bits = 128 bytes)\nconst RSA_Q: [u8; 128] = [\n    0xda, 0xdc, 0xe1, 0xd1, 0x34, 0x4e, 0xec, 0xe4, 0xbe, 0x4b, 0x74, 0xa7, 0xbb, 0x48, 0xee, 0xe0, 0x31, 0xad, 0xde,\n    0xac, 0x04, 0x3c, 0x69, 0x35, 0xb9, 0x53, 0x51, 0x7f, 0x8c, 0xff, 0x3a, 0x75, 0x9e, 0x9b, 0x78, 0xb6, 0xae, 0x81,\n    0xfb, 0xe1, 0x7c, 0xf3, 0xa2, 0x38, 0xcd, 0xd3, 0x48, 0x70, 0x55, 0x60, 0x1a, 0xe7, 0x06, 0xb5, 0xc7, 0x6c, 0xec,\n    0x3d, 0x97, 0xe9, 0xde, 0xe3, 0xc4, 0xb4, 0xad, 0x3d, 0x50, 0x32, 0x75, 0x9a, 0x0c, 0xe0, 0x46, 0xc0, 0x13, 0x3f,\n    0x2c, 0x56, 0x7d, 0xca, 0xae, 0xa8, 0x0c, 0xb1, 0x60, 0xb0, 0x5c, 0xbe, 0xb8, 0x0a, 0xa9, 0x4c, 0x37, 0x93, 0x59,\n    0x78, 0x19, 0xb6, 0x3c, 0x0a, 0x60, 0x51, 0x7f, 0xeb, 0xca, 0x46, 0xe5, 0x24, 0x3c, 0x11, 0xa9, 0x32, 0x4b, 0x5c,\n    0x4f, 0x32, 0x23, 0x99, 0x3e, 0x33, 0x0b, 0xde, 0xab, 0x04, 0xc0, 0x66, 0x71, 0x4b,\n];\n\n// Public exponent e = 65537\nconst RSA_E: [u8; 3] = [0x01, 0x00, 0x01];\n\n// Private exponent d\nconst RSA_D: [u8; 256] = [\n    0xa9, 0x60, 0x9c, 0xc1, 0xa0, 0xfc, 0xdc, 0x8e, 0xd3, 0x60, 0xda, 0xd2, 0x6e, 0x4d, 0xe0, 0xa2, 0x99, 0x1d, 0xbf,\n    0xbc, 0x3a, 0xcf, 0x72, 0xe4, 0xdc, 0x44, 0x0f, 0xe9, 0x7e, 0x62, 0x96, 0x9b, 0x1b, 0xb3, 0x55, 0x46, 0x3b, 0x5e,\n    0x40, 0xee, 0x63, 0x0e, 0x71, 0xab, 0x20, 0x66, 0x9a, 0x87, 0xeb, 0x7f, 0x86, 0xd6, 0xd5, 0x09, 0x1d, 0x45, 0x06,\n    0x07, 0x92, 0x25, 0xb2, 0xc1, 0xe4, 0x3f, 0xa0, 0x78, 0xcf, 0x94, 0x4a, 0x57, 0x83, 0xf5, 0x83, 0x61, 0x27, 0xdb,\n    0xb6, 0x81, 0x65, 0xae, 0x86, 0xec, 0x10, 0x2f, 0x88, 0xd9, 0x4c, 0xce, 0x49, 0x46, 0x8f, 0xda, 0xf2, 0xed, 0x1c,\n    0xaf, 0xfb, 0xc3, 0x12, 0xe8, 0x98, 0x25, 0x77, 0x9d, 0x63, 0x49, 0x8d, 0xd8, 0xcb, 0x55, 0x52, 0x9b, 0x68, 0xb4,\n    0x1a, 0xf4, 0xed, 0xeb, 0xba, 0xf9, 0x40, 0xeb, 0xeb, 0x15, 0xf1, 0xae, 0x16, 0x3b, 0xfc, 0x7e, 0x89, 0x90, 0x86,\n    0x66, 0x37, 0xa3, 0xdb, 0xcd, 0x73, 0x68, 0x3f, 0x6d, 0x9c, 0x85, 0x45, 0x5c, 0x21, 0xc6, 0xfe, 0x2d, 0x41, 0x67,\n    0x5c, 0x2f, 0x97, 0x3b, 0x03, 0x17, 0x81, 0x11, 0xd7, 0x1c, 0xf6, 0x6e, 0xf2, 0xd8, 0x90, 0x89, 0x9d, 0xbb, 0x75,\n    0xa7, 0x9f, 0x09, 0x0d, 0x83, 0x13, 0x28, 0xf1, 0x88, 0xe1, 0x48, 0x37, 0x1b, 0x20, 0x8c, 0x86, 0x2f, 0x27, 0x17,\n    0x0b, 0xe6, 0x70, 0x83, 0x1e, 0x69, 0x88, 0x8f, 0x72, 0x18, 0xd7, 0x83, 0x2b, 0xca, 0xab, 0xfe, 0x3c, 0x65, 0x23,\n    0xfe, 0xd0, 0xea, 0xd3, 0x2c, 0xc7, 0x9a, 0x2a, 0x54, 0xef, 0x0b, 0x12, 0xcd, 0x4d, 0xab, 0xf9, 0x9b, 0xa7, 0x66,\n    0x88, 0x3c, 0x7c, 0xfa, 0x2f, 0x19, 0x62, 0x8b, 0x1b, 0xb1, 0xbf, 0x18, 0xb2, 0x1b, 0xc7, 0x64, 0x0e, 0x00, 0xdd,\n    0xf1, 0x01, 0x83, 0x42, 0x9b, 0x0c, 0xe7, 0x57, 0xc9,\n];\n\n#[embassy_executor::main]\nasync fn main(_spawner: embassy_executor::Spawner) {\n    let mut config = Config::default();\n    config.rcc.pll1 = Some(embassy_stm32::rcc::Pll {\n        source: PllSource::HSI,\n        prediv: PllPreDiv::DIV1,\n        mul: PllMul::MUL30,\n        divr: Some(PllDiv::DIV5),\n        divq: None,\n        divp: Some(PllDiv::DIV30),\n        frac: Some(0),\n    });\n    config.rcc.ahb_pre = AHBPrescaler::DIV1;\n    config.rcc.apb1_pre = APBPrescaler::DIV1;\n    config.rcc.apb2_pre = APBPrescaler::DIV1;\n    config.rcc.apb7_pre = APBPrescaler::DIV1;\n    config.rcc.ahb5_pre = AHB5Prescaler::DIV4;\n    config.rcc.voltage_scale = VoltageScale::RANGE1;\n    config.rcc.sys = Sysclk::PLL1_R;\n\n    let p = embassy_stm32::init(config);\n    info!(\"PKA RSA Key Finalization Example\");\n\n    let mut pka = Pka::new_blocking(p.PKA, Irqs);\n\n    info!(\"=== RSA Key Finalization Example ===\");\n    info!(\"Key size: 2048 bits\");\n\n    // Step 1: Compute Montgomery parameter R² mod n\n    // This is essential for fast modular exponentiation\n    info!(\"=== Step 1: Computing Montgomery Parameter ===\");\n    info!(\"R² mod n is required for fast modular operations\");\n\n    let mut montgomery_param = [0u32; 64]; // 64 bytes / 4 = 16 words\n    match pka.montgomery_param(&RSA_N, &mut montgomery_param) {\n        Ok(()) => {\n            info!(\"Montgomery parameter computed successfully\");\n            info!(\n                \"First 4 words: {:08x} {:08x} {:08x} {:08x}\",\n                montgomery_param[0], montgomery_param[1], montgomery_param[2], montgomery_param[3]\n            );\n        }\n        Err(e) => {\n            error!(\"Failed to compute Montgomery parameter: {:?}\", e);\n            loop {\n                cortex_m::asm::wfi();\n            }\n        }\n    }\n\n    // Step 2: Verify n = p * q using arithmetic multiplication\n    info!(\"=== Step 2: Verifying n = p * q ===\");\n\n    let mut computed_n = [0u8; 256];\n    match pka.arithmetic_mul(&RSA_P, &RSA_Q, &mut computed_n) {\n        Ok(()) => {\n            info!(\"Computed p * q\");\n\n            // Compare with known n\n            match pka.comparison(&computed_n, &RSA_N) {\n                Ok(ComparisonResult::Equal) => {\n                    info!(\"Verified: p * q = n\");\n                }\n                Ok(result) => {\n                    error!(\"Verification FAILED: p * q != n (result: {:?})\", result);\n                }\n                Err(e) => {\n                    error!(\"Comparison failed: {:?}\", e);\n                }\n            }\n        }\n        Err(e) => {\n            error!(\"Multiplication failed: {:?}\", e);\n        }\n    }\n\n    // Step 3: Validate that d < n (private exponent must be less than modulus)\n    info!(\"=== Step 3: Validating d < n ===\");\n\n    match pka.comparison(&RSA_D, &RSA_N) {\n        Ok(ComparisonResult::Less) => {\n            info!(\"Verified: d < n (private exponent is valid)\");\n        }\n        Ok(ComparisonResult::Equal) => {\n            error!(\"Invalid: d = n (should not happen)\");\n        }\n        Ok(ComparisonResult::Greater) => {\n            error!(\"Invalid: d > n (private exponent too large)\");\n        }\n        Err(e) => {\n            error!(\"Comparison failed: {:?}\", e);\n        }\n    }\n\n    // Step 4: Test encryption/decryption to verify key pair\n    info!(\"=== Step 4: Verifying Key Pair with Test Encryption ===\");\n\n    // Small test message\n    let mut test_message = [0u8; 256];\n    test_message[63] = 0x42; // Simple test value\n\n    // Encrypt with public key\n    let mut ciphertext = [0u8; 256];\n    match pka.modular_exp_fast(&test_message, &RSA_E, &RSA_N, &montgomery_param, &mut ciphertext) {\n        Ok(()) => {\n            info!(\"Test encryption successful\");\n        }\n        Err(e) => {\n            error!(\"Test encryption failed: {:?}\", e);\n            loop {\n                cortex_m::asm::wfi();\n            }\n        }\n    }\n\n    // Decrypt with private key\n    let mut decrypted = [0u8; 256];\n    match pka.modular_exp_fast(&ciphertext, &RSA_D, &RSA_N, &montgomery_param, &mut decrypted) {\n        Ok(()) => {\n            if decrypted == test_message {\n                info!(\"Key pair verification PASSED!\");\n                info!(\"Encryption and decryption work correctly\");\n            } else {\n                error!(\"Key pair verification FAILED!\");\n                error!(\"Decrypted message does not match original\");\n            }\n        }\n        Err(e) => {\n            error!(\"Test decryption failed: {:?}\", e);\n        }\n    }\n\n    // Step 5: Demonstrate modular inverse (useful for computing d from e)\n    info!(\"=== Step 5: Modular Inverse Demonstration ===\");\n    info!(\"Computing modular inverse of a test value\");\n\n    // Compute inverse of 7 mod 11 (should be 8, since 7*8 = 56 = 5*11 + 1)\n    let a: [u8; 1] = [7];\n    let modulus: [u8; 1] = [11];\n    let mut inverse = [0u8; 1];\n\n    match pka.modular_inv(&a, &modulus, &mut inverse) {\n        Ok(()) => {\n            info!(\"Inverse of 7 mod 11 = {}\", inverse[0]);\n            if inverse[0] == 8 {\n                info!(\"Modular inverse verified correct!\");\n            }\n        }\n        Err(e) => {\n            error!(\"Modular inverse failed: {:?}\", e);\n        }\n    }\n\n    // Step 6: Demonstrate modular reduction\n    info!(\"=== Step 6: Modular Reduction ===\");\n\n    // Reduce a large number mod n\n    let large_num = [0xFFu8; 64]; // All 1s\n    let mut reduced = [0u8; 256];\n\n    match pka.modular_red(&large_num, &RSA_N, &mut reduced) {\n        Ok(()) => {\n            info!(\"Modular reduction successful\");\n            info!(\"Result (first 8 bytes): {:02x}\", &reduced[..8]);\n        }\n        Err(e) => {\n            error!(\"Modular reduction failed: {:?}\", e);\n        }\n    }\n\n    info!(\"=== RSA Key Finalization Example Complete ===\");\n    info!(\"Summary:\");\n    info!(\"  - Montgomery parameter: computed\");\n    info!(\"  - n = p * q: verified\");\n    info!(\"  - d < n: verified\");\n    info!(\"  - Key pair: verified\");\n\n    loop {\n        cortex_m::asm::wfi();\n    }\n}\n"
  },
  {
    "path": "examples/stm32wba/src/bin/pwm.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse defmt_rtt as _; // global logger\nuse embassy_executor::Spawner;\nuse embassy_stm32::Config;\nuse embassy_stm32::gpio::OutputType;\nuse embassy_stm32::rcc::{\n    AHB5Prescaler, AHBPrescaler, APBPrescaler, PllDiv, PllMul, PllPreDiv, PllSource, Sysclk, VoltageScale,\n};\nuse embassy_stm32::time::khz;\nuse embassy_stm32::timer::simple_pwm::{PwmPin, SimplePwm};\nuse embassy_time::Timer;\nuse panic_probe as _;\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    info!(\"Hello World!\");\n\n    let mut config = Config::default();\n    // Fine-tune PLL1 dividers/multipliers\n    config.rcc.pll1 = Some(embassy_stm32::rcc::Pll {\n        source: PllSource::HSI,\n        prediv: PllPreDiv::DIV1,  // PLLM = 1 → HSI / 1 = 16 MHz\n        mul: PllMul::MUL30,       // PLLN = 30 → 16 MHz * 30 = 480 MHz VCO\n        divr: Some(PllDiv::DIV5), // PLLR = 5 → 96 MHz (Sysclk)\n        // divq: Some(PllDiv::DIV10), // PLLQ = 10 → 48 MHz (NOT USED)\n        divq: None,\n        divp: Some(PllDiv::DIV30), // PLLP = 30 → 16 MHz (USBOTG)\n        frac: Some(0),             // Fractional part (enabled)\n    });\n\n    config.rcc.ahb_pre = AHBPrescaler::DIV1;\n    config.rcc.apb1_pre = APBPrescaler::DIV1;\n    config.rcc.apb2_pre = APBPrescaler::DIV1;\n    config.rcc.apb7_pre = APBPrescaler::DIV1;\n    config.rcc.ahb5_pre = AHB5Prescaler::DIV4;\n\n    // voltage scale for max performance\n    config.rcc.voltage_scale = VoltageScale::RANGE1;\n    // route PLL1_P into the USB‐OTG‐HS block\n    config.rcc.sys = Sysclk::PLL1_R;\n\n    let p = embassy_stm32::init(config);\n\n    let ch1_pin = PwmPin::new(p.PB8, OutputType::PushPull);\n    let mut pwm = SimplePwm::new(p.TIM1, Some(ch1_pin), None, None, None, khz(10), Default::default());\n    let mut ch1 = pwm.ch1();\n    ch1.enable();\n\n    info!(\"PWM initialized\");\n    info!(\"PWM max duty {}\", ch1.max_duty_cycle());\n\n    loop {\n        ch1.set_duty_cycle_fully_off();\n        Timer::after_millis(300).await;\n        ch1.set_duty_cycle_fraction(1, 4);\n        Timer::after_millis(300).await;\n        ch1.set_duty_cycle_fraction(1, 2);\n        Timer::after_millis(300).await;\n        ch1.set_duty_cycle(ch1.max_duty_cycle() - 1);\n        Timer::after_millis(300).await;\n    }\n}\n"
  },
  {
    "path": "examples/stm32wba/src/bin/rng.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_stm32::rcc::{\n    AHB5Prescaler, AHBPrescaler, APBPrescaler, PllDiv, PllMul, PllPreDiv, PllSource, Sysclk, VoltageScale, mux,\n};\nuse embassy_stm32::rng::Rng;\nuse embassy_stm32::{Config, bind_interrupts, peripherals, rng};\nuse embassy_time::Timer;\nuse {defmt_rtt as _, panic_probe as _};\n\nbind_interrupts!(struct Irqs {\n    RNG => rng::InterruptHandler<peripherals::RNG>;\n});\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let mut config = Config::default();\n\n    // Configure PLL1 (required on WBA)\n    config.rcc.pll1 = Some(embassy_stm32::rcc::Pll {\n        source: PllSource::HSI,\n        prediv: PllPreDiv::DIV1,  // PLLM = 1 → HSI / 1 = 16 MHz\n        mul: PllMul::MUL30,       // PLLN = 30 → 16 MHz * 30 = 480 MHz VCO\n        divr: Some(PllDiv::DIV5), // PLLR = 5 → 96 MHz (Sysclk)\n        divq: None,\n        divp: Some(PllDiv::DIV30), // PLLP = 30 → 16 MHz (required for SAI)\n        frac: Some(0),\n    });\n\n    config.rcc.ahb_pre = AHBPrescaler::DIV1;\n    config.rcc.apb1_pre = APBPrescaler::DIV1;\n    config.rcc.apb2_pre = APBPrescaler::DIV1;\n    config.rcc.apb7_pre = APBPrescaler::DIV1;\n    config.rcc.ahb5_pre = AHB5Prescaler::DIV4;\n    config.rcc.voltage_scale = VoltageScale::RANGE1;\n    config.rcc.sys = Sysclk::PLL1_R;\n\n    // Configure RNG clock source to HSI (required for WBA)\n    config.rcc.mux.rngsel = mux::Rngsel::HSI;\n\n    let p = embassy_stm32::init(config);\n    info!(\"STM32WBA65 RNG Test\");\n    info!(\"Initializing RNG...\");\n\n    // Initialize RNG - this will trigger the reset() function\n    // which previously hung at line 144\n    let mut rng = Rng::new(p.RNG, Irqs);\n    info!(\"RNG initialized successfully!\");\n\n    // Test 1: Generate random bytes using async method\n    info!(\"\\n=== Test 1: Async random bytes ===\");\n    let mut buf = [0u8; 16];\n    match rng.async_fill_bytes(&mut buf).await {\n        Ok(_) => info!(\"Generated 16 random bytes: {:02x}\", buf),\n        Err(e) => error!(\"Error generating random bytes: {:?}\", e),\n    }\n\n    // Test 2: Generate multiple u32 values using blocking method\n    info!(\"\\n=== Test 2: Blocking u32 generation ===\");\n    for i in 0..5 {\n        let random = rng.next_u32();\n        info!(\"Random u32 #{}: 0x{:08x} ({})\", i + 1, random, random);\n        Timer::after_millis(100).await;\n    }\n\n    // Test 3: Generate u64 values\n    info!(\"\\n=== Test 3: u64 generation ===\");\n    for i in 0..3 {\n        let random = rng.next_u64();\n        info!(\"Random u64 #{}: 0x{:016x}\", i + 1, random);\n        Timer::after_millis(100).await;\n    }\n\n    // Test 4: Fill buffer using blocking method\n    info!(\"\\n=== Test 4: Blocking buffer fill ===\");\n    let mut buf2 = [0u8; 32];\n    rng.fill_bytes(&mut buf2);\n    info!(\"Generated 32 random bytes:\");\n    info!(\"  {:02x}\", &buf2[0..16]);\n    info!(\"  {:02x}\", &buf2[16..32]);\n\n    // Test 5: Continuous generation loop\n    info!(\"\\n=== Test 5: Continuous generation (10 samples) ===\");\n    for i in 0..10 {\n        let random = rng.next_u32();\n        info!(\"Sample #{}: 0x{:08x}\", i + 1, random);\n        Timer::after_millis(200).await;\n    }\n\n    info!(\"\\nAll RNG tests completed successfully!\");\n    info!(\"The RNG is working correctly on STM32WBA65.\");\n\n    cortex_m::asm::bkpt();\n}\n"
  },
  {
    "path": "examples/stm32wba/src/bin/rtc.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_stm32::Config;\nuse embassy_stm32::rcc::*;\nuse embassy_stm32::rtc::{DateTime, DayOfWeek, Rtc, RtcConfig};\nuse embassy_time::Timer;\nuse {defmt_rtt as _, panic_probe as _};\n\npub fn pll_init(config: &mut Config) {\n    config.rcc.pll1 = Some(embassy_stm32::rcc::Pll {\n        source: PllSource::HSI,\n        prediv: PllPreDiv::DIV1,  // PLLM = 1 → HSI / 1 = 16 MHz\n        mul: PllMul::MUL30,       // PLLN = 30 → 16 MHz * 30 = 480 MHz VCO\n        divr: Some(PllDiv::DIV5), // PLLR = 5 → 96 MHz (Sysclk)\n        // divq: Some(PllDiv::DIV10), // PLLQ = 10 → 48 MHz (NOT USED)\n        divq: None,\n        divp: Some(PllDiv::DIV30), // PLLP = 30 → 16 MHz (USBOTG)\n        frac: Some(0),             // Fractional part (enabled)\n    });\n\n    config.rcc.ahb_pre = AHBPrescaler::DIV1;\n    config.rcc.apb1_pre = APBPrescaler::DIV1;\n    config.rcc.apb2_pre = APBPrescaler::DIV1;\n    config.rcc.apb7_pre = APBPrescaler::DIV1;\n    config.rcc.ahb5_pre = AHB5Prescaler::DIV4;\n\n    // voltage scale for max performance\n    config.rcc.voltage_scale = VoltageScale::RANGE1;\n    // route PLL1_P into the USB‐OTG‐HS block\n    config.rcc.sys = Sysclk::PLL1_R;\n}\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let mut config = Config::default();\n\n    pll_init(&mut config);\n\n    let p = embassy_stm32::init(config);\n\n    let mut rtc = Rtc::new(p.RTC, RtcConfig::default());\n\n    // Setting datetime\n    let initial_datetime = DateTime::from(1970, 1, 1, DayOfWeek::Thursday, 0, 00, 00, 0).unwrap();\n    match rtc.0.set_datetime(initial_datetime) {\n        Ok(()) => info!(\"RTC set successfully.\"),\n        Err(e) => error!(\"Failed to set RTC date/time: {:?}\", e),\n    }\n\n    // Reading datetime every 1s\n    loop {\n        match rtc.1.now() {\n            Ok(result) => info!(\"{}\", result),\n            Err(e) => error!(\"Failed to set RTC date/time: {:?}\", e),\n        }\n\n        Timer::after_millis(1000).await;\n    }\n}\n"
  },
  {
    "path": "examples/stm32wba/src/bin/saes_ecb.rs",
    "content": "//! SAES-ECB (Secure AES - Electronic Codebook) Mode Example\n//!\n//! Demonstrates SAES-ECB encryption and decryption.\n//!\n//! # SAES Key Protection\n//!\n//! SAES applies a hardware RNG mask to stored key registers so that software\n//! cannot read back the key from memory. When doing a crypto operation the\n//! hardware removes the mask transparently before using the key.\n//!\n//! Implication for test vectors:\n//! - 128-bit: the mask is typically all-zeros immediately after reset (the RNG\n//!   has not yet warmed up), so SAES-128 coincidentally matches AES-128 NIST\n//!   vectors. This is not guaranteed across all devices or boot conditions.\n//! - 256-bit: the mask for the extended key registers (KEYR4-7) is non-zero\n//!   once the RNG has accumulated entropy, so SAES-256 output is DEVICE-SPECIFIC\n//!   and will NOT match AES-256 NIST vectors. Encrypt/decrypt are still correct\n//!   inverses of each other on the same device.\n//!\n//! # What This Example Exercises\n//! - SAES peripheral initialisation (busy-wait + RNG-error check on startup)\n//! - Key loading and KEYVALID wait\n//! - 128-bit ECB encrypt/decrypt round-trip + coincidental NIST vector check\n//! - 256-bit ECB encrypt/decrypt round-trip (device-specific ciphertext)\n//! - CBC decrypt, which exercises the key-derivation (MODE=1) path\n\n#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_stm32::saes::{AesCbc, AesEcb, Direction, Saes};\nuse embassy_stm32::{bind_interrupts, peripherals};\nuse {defmt_rtt as _, panic_probe as _};\n\nbind_interrupts!(struct Irqs {\n    SAES => embassy_stm32::saes::InterruptHandler<peripherals::SAES>;\n});\n\n#[embassy_executor::main]\nasync fn main(_spawner: embassy_executor::Spawner) {\n    let p = embassy_stm32::init(Default::default());\n    info!(\"SAES-ECB Example\");\n\n    let mut saes = Saes::new_blocking(p.SAES, Irqs);\n\n    // ─── AES-128-ECB round-trip + NIST sanity check ──────────────────────────\n    // NIST SP 800-38A F.1.1: key = 2b7e151628aed2a6abf7158809cf4f3c\n    info!(\"=== SAES-ECB 128-bit ===\");\n    let key_128 = [\n        0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6, 0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf, 0x4f, 0x3c,\n    ];\n    let plaintext_128 = [\n        0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96, 0xe9, 0x3d, 0x7e, 0x11, 0x73, 0x93, 0x17, 0x2a,\n    ];\n    // Expected if RNG mask is all-zeros (normal at early boot)\n    let nist_ct_128 = [\n        0x3a, 0xd7, 0x7b, 0xb4, 0x0d, 0x7a, 0x36, 0x60, 0xa8, 0x9e, 0xca, 0xf3, 0x24, 0x66, 0xef, 0x97,\n    ];\n\n    let cipher = AesEcb::new(&key_128);\n    let mut ctx = saes.start(&cipher, Direction::Encrypt);\n    let mut ct_128 = [0u8; 16];\n    match saes.payload_blocking(&mut ctx, &plaintext_128, &mut ct_128, true) {\n        Ok(()) => {\n            info!(\"Plaintext:  {:02x}\", plaintext_128);\n            info!(\"Ciphertext: {:02x}\", ct_128);\n            if ct_128 == nist_ct_128 {\n                info!(\"✓ Matches NIST vector (RNG mask = zero at boot)\");\n            } else {\n                info!(\"  No NIST match — RNG mask non-zero; round-trip test continues\");\n            }\n        }\n        Err(e) => error!(\"Encrypt error: {:?}\", e),\n    }\n    saes.finish_blocking(ctx).ok();\n\n    // Decrypt: exercises the key-derivation fix (MODE=KEY_DERIVATION for ECB decrypt)\n    let cipher = AesEcb::new(&key_128);\n    let mut ctx = saes.start(&cipher, Direction::Decrypt);\n    let mut recovered_128 = [0u8; 16];\n    match saes.payload_blocking(&mut ctx, &ct_128, &mut recovered_128, true) {\n        Ok(()) => {\n            info!(\"Recovered:  {:02x}\", recovered_128);\n            if recovered_128 == plaintext_128 {\n                info!(\"✓ 128-bit round-trip PASSED\");\n            } else {\n                error!(\"✗ 128-bit round-trip FAILED\");\n            }\n        }\n        Err(e) => error!(\"Decrypt error: {:?}\", e),\n    }\n    saes.finish_blocking(ctx).ok();\n\n    // ─── AES-256-ECB round-trip (device-specific ciphertext) ─────────────────\n    // SAES-256 output is device-specific — the RNG mask for KEYR4-7 is non-zero\n    // once the RNG has warmed up. We verify encrypt/decrypt consistency, not NIST.\n    info!(\"=== SAES-ECB 256-bit ===\");\n    let key_256 = [\n        0x60, 0x3d, 0xeb, 0x10, 0x15, 0xca, 0x71, 0xbe, 0x2b, 0x73, 0xae, 0xf0, 0x85, 0x7d, 0x77, 0x81u8, 0x1f, 0x35,\n        0x2c, 0x07, 0x3b, 0x61, 0x08, 0xd7, 0x2d, 0x98, 0x10, 0xa3, 0x09, 0x14, 0xdf, 0xf4,\n    ];\n    let plaintext_256 = [\n        0xf6, 0x9f, 0x24, 0x45, 0xdf, 0x4f, 0x9b, 0x17, 0xad, 0x2b, 0x41, 0x7b, 0xe6, 0x6c, 0x37, 0x10u8,\n    ];\n\n    let cipher = AesEcb::new(&key_256);\n    let mut ctx = saes.start(&cipher, Direction::Encrypt);\n    let mut ct_256 = [0u8; 16];\n    match saes.payload_blocking(&mut ctx, &plaintext_256, &mut ct_256, true) {\n        Ok(()) => info!(\"Ciphertext: {:02x}\", ct_256),\n        Err(e) => error!(\"Encrypt error: {:?}\", e),\n    }\n    saes.finish_blocking(ctx).ok();\n\n    // Decrypt: exercises 256-bit key derivation\n    let cipher = AesEcb::new(&key_256);\n    let mut ctx = saes.start(&cipher, Direction::Decrypt);\n    let mut recovered_256 = [0u8; 16];\n    match saes.payload_blocking(&mut ctx, &ct_256, &mut recovered_256, true) {\n        Ok(()) => {\n            info!(\"Recovered:  {:02x}\", recovered_256);\n            if recovered_256 == plaintext_256 {\n                info!(\"✓ 256-bit round-trip PASSED\");\n            } else {\n                error!(\"✗ 256-bit round-trip FAILED\");\n            }\n        }\n        Err(e) => error!(\"Decrypt error: {:?}\", e),\n    }\n    saes.finish_blocking(ctx).ok();\n\n    // ─── AES-128-CBC round-trip (also exercises key derivation for decrypt) ───\n    info!(\"=== SAES-CBC 128-bit ===\");\n    let iv = [\n        0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0fu8,\n    ];\n    let plaintext_cbc = [\n        0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96, 0xe9, 0x3d, 0x7e, 0x11, 0x73, 0x93, 0x17, 0x2au8,\n    ];\n\n    let cipher = AesCbc::new(&key_128, &iv);\n    let mut ctx = saes.start(&cipher, Direction::Encrypt);\n    let mut ct_cbc = [0u8; 16];\n    saes.payload_blocking(&mut ctx, &plaintext_cbc, &mut ct_cbc, true).ok();\n    saes.finish_blocking(ctx).ok();\n    info!(\"Ciphertext: {:02x}\", ct_cbc);\n\n    let cipher = AesCbc::new(&key_128, &iv);\n    let mut ctx = saes.start(&cipher, Direction::Decrypt);\n    let mut recovered_cbc = [0u8; 16];\n    match saes.payload_blocking(&mut ctx, &ct_cbc, &mut recovered_cbc, true) {\n        Ok(()) => {\n            info!(\"Recovered:  {:02x}\", recovered_cbc);\n            if recovered_cbc == plaintext_cbc {\n                info!(\"✓ CBC 128-bit round-trip PASSED\");\n            } else {\n                error!(\"✗ CBC 128-bit round-trip FAILED\");\n            }\n        }\n        Err(e) => error!(\"CBC Decrypt error: {:?}\", e),\n    }\n    saes.finish_blocking(ctx).ok();\n\n    info!(\"=== SAES-ECB complete ===\");\n    loop {\n        cortex_m::asm::wfi();\n    }\n}\n"
  },
  {
    "path": "examples/stm32wba/src/bin/saes_gcm.rs",
    "content": "//! SAES-GCM (Secure AES - Galois/Counter Mode) Example\n//!\n//! # Known Limitation: GCM with AAD on SAES v1a (WBA5x/WBA6x)\n//!\n//! SAES GCM without AAD produces consistent encrypt/decrypt authentication tags\n//! and is tested here. However, when AAD (Additional Authenticated Data) is\n//! provided, encrypt and decrypt produce different authentication tags on\n//! SAES v1a devices (STM32WBA5x/WBA6x). The plaintext is recovered correctly\n//! but the AEAD authentication property does not hold.\n//!\n//! **If you need authenticated GCM with AAD, use the plain AES peripheral.**\n//!\n//! # What This Example Exercises\n//! - SAES peripheral initialisation (busy-wait + RNG-error check on startup)\n//! - GCM mode detection via chmod_bits() (the IV-length heuristic fix)\n//! - GCMPH sequencing: init (0) → payload (2) → final (3) (no AAD)\n//! - CCF polling via ISR register\n//! - No-AAD GCM round-trip: encrypt tag == decrypt tag ✓\n\n#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_stm32::saes::{AesGcm, Direction, Saes};\nuse embassy_stm32::{bind_interrupts, peripherals};\nuse {defmt_rtt as _, panic_probe as _};\n\nbind_interrupts!(struct Irqs {\n    SAES => embassy_stm32::saes::InterruptHandler<peripherals::SAES>;\n});\n\n#[embassy_executor::main]\nasync fn main(_spawner: embassy_executor::Spawner) {\n    let p = embassy_stm32::init(Default::default());\n    info!(\"SAES-GCM Example\");\n\n    let mut saes = Saes::new_blocking(p.SAES, Irqs);\n\n    // ─── GCM round-trip: no AAD, 16-byte plaintext ───────────────────────────\n    info!(\"=== SAES-GCM 128-bit (no AAD) ===\");\n    let key = [0u8; 16];\n    let iv = [0u8; 12];\n    let pt = [0u8; 16];\n\n    let cipher = AesGcm::new(&key, &iv);\n    let mut ctx = saes.start(&cipher, Direction::Encrypt);\n    let mut ct = [0u8; 16];\n    match saes.payload_blocking(&mut ctx, &pt, &mut ct, true) {\n        Ok(()) => info!(\"Ciphertext: {:02x}\", ct),\n        Err(e) => {\n            error!(\"✗ Encrypt failed: {:?}\", e);\n            loop {\n                cortex_m::asm::wfi();\n            }\n        }\n    }\n    let enc_tag = match saes.finish_blocking(ctx) {\n        Ok(Some(tag)) => {\n            info!(\"Encrypt tag: {:02x}\", tag);\n            tag\n        }\n        _ => {\n            error!(\"✗ No tag\");\n            loop {\n                cortex_m::asm::wfi();\n            }\n        }\n    };\n\n    let cipher = AesGcm::new(&key, &iv);\n    let mut ctx = saes.start(&cipher, Direction::Decrypt);\n    let mut recovered = [0u8; 16];\n    match saes.payload_blocking(&mut ctx, &ct, &mut recovered, true) {\n        Ok(()) => info!(\"Recovered:  {:02x}\", recovered),\n        Err(e) => error!(\"✗ Decrypt failed: {:?}\", e),\n    }\n    match saes.finish_blocking(ctx) {\n        Ok(Some(dec_tag)) => {\n            info!(\"Decrypt tag: {:02x}\", dec_tag);\n            if recovered == pt && dec_tag == enc_tag {\n                info!(\"✓ GCM no-AAD round-trip PASSED (plaintext + tag both match)\");\n            } else {\n                if recovered != pt {\n                    error!(\"✗ Plaintext mismatch\");\n                }\n                if dec_tag != enc_tag {\n                    error!(\"✗ Tag mismatch\");\n                }\n            }\n        }\n        Ok(None) => error!(\"✗ No tag returned\"),\n        Err(e) => error!(\"✗ Finish failed: {:?}\", e),\n    }\n\n    // ─── GCM round-trip: larger plaintext, no AAD ────────────────────────────\n    info!(\"=== SAES-GCM 128-bit (no AAD, 60-byte PT) ===\");\n    let key2 = [\n        0xfe, 0xff, 0xe9, 0x92, 0x86, 0x65, 0x73, 0x1c, 0x6d, 0x6a, 0x8f, 0x94, 0x67, 0x30, 0x83, 0x08u8,\n    ];\n    let iv2 = [0xca, 0xfe, 0xba, 0xbe, 0xfa, 0xce, 0xdb, 0xad, 0xde, 0xca, 0xf8, 0x88u8];\n    let pt2 = [\n        0xd9, 0x31, 0x32, 0x25, 0xf8, 0x84, 0x06, 0xe5, 0xa5, 0x59, 0x09, 0xc5, 0xaf, 0xf5, 0x26, 0x9a, 0x86, 0xa7,\n        0xa9, 0x53, 0x15, 0x34, 0xf7, 0xda, 0x2e, 0x4c, 0x30, 0x3d, 0x8a, 0x31, 0x8a, 0x72, 0x1c, 0x3c, 0x0c, 0x95,\n        0x95, 0x68, 0x09, 0x53, 0x2f, 0xcf, 0x0e, 0x24, 0x49, 0xa6, 0xb5, 0x25, 0xb1, 0x6a, 0xed, 0xf5, 0xaa, 0x0d,\n        0xe6, 0x57, 0xba, 0x63, 0x7b, 0x39u8,\n    ];\n\n    let cipher = AesGcm::new(&key2, &iv2);\n    let mut ctx = saes.start(&cipher, Direction::Encrypt);\n    let mut ct2 = [0u8; 60];\n    saes.payload_blocking(&mut ctx, &pt2, &mut ct2, true).ok();\n    let enc_tag2 = match saes.finish_blocking(ctx) {\n        Ok(Some(tag)) => {\n            info!(\"Encrypt tag: {:02x}\", tag);\n            tag\n        }\n        _ => {\n            error!(\"✗ Encrypt failed\");\n            loop {\n                cortex_m::asm::wfi();\n            }\n        }\n    };\n\n    let cipher = AesGcm::new(&key2, &iv2);\n    let mut ctx = saes.start(&cipher, Direction::Decrypt);\n    let mut recovered2 = [0u8; 60];\n    saes.payload_blocking(&mut ctx, &ct2, &mut recovered2, true).ok();\n    match saes.finish_blocking(ctx) {\n        Ok(Some(dec_tag)) => {\n            if recovered2 == pt2 && dec_tag == enc_tag2 {\n                info!(\"✓ GCM 60-byte no-AAD round-trip PASSED\");\n            } else {\n                if recovered2 != pt2 {\n                    error!(\"✗ Plaintext mismatch\");\n                }\n                if dec_tag != enc_tag2 {\n                    error!(\"✗ Tag mismatch\");\n                }\n            }\n        }\n        _ => error!(\"✗ Decrypt failed\"),\n    }\n\n    info!(\"=== SAES-GCM complete ===\");\n    loop {\n        cortex_m::asm::wfi();\n    }\n}\n"
  },
  {
    "path": "examples/stm32wba-lp/.cargo/config.toml",
    "content": "[target.'cfg(all(target_arch = \"arm\", target_os = \"none\"))']\nrunner = \"probe-rs run --chip STM32WBA55CGUx\"\n\n[build]\ntarget = \"thumbv8m.main-none-eabihf\"\n\n[env]\nDEFMT_LOG = \"trace\"\n"
  },
  {
    "path": "examples/stm32wba-lp/Cargo.toml",
    "content": "[package]\nedition = \"2024\"\nname = \"embassy-stm32wba-lp-examples\"\nversion = \"0.1.0\"\nlicense = \"MIT OR Apache-2.0\"\npublish = false\n\n[dependencies]\nembassy-stm32 = { version = \"0.6.0\", path = \"../../embassy-stm32\", features = [\"defmt\", \"stm32wba52cg\", \"time-driver-any\", \"memory-x\", \"unstable-pac\", \"exti\", \"low-power\", \"executor-thread\"] }\nembassy-sync = { version = \"0.8.0\", path = \"../../embassy-sync\", features = [\"defmt\"] }\nembassy-executor = { version = \"0.10.0\", path = \"../../embassy-executor\", features = [\"defmt\"] }\nembassy-time = { version = \"0.5.1\", path = \"../../embassy-time\", features = [\"defmt\", \"defmt-timestamp-uptime\", \"tick-hz-32_768\"] }\n\ndefmt = \"1.0.1\"\ndefmt-rtt = \"1.1.0\"\n\ncortex-m = { version = \"0.7.6\", features = [\"inline-asm\", \"critical-section-single-core\"] }\ncortex-m-rt = \"0.7.0\"\npanic-probe = { version = \"1.0.0\", features = [\"print-defmt\"] }\n\n[profile.release]\ndebug = 2\n\n[package.metadata.embassy]\nbuild = [\n  { target = \"thumbv8m.main-none-eabihf\", artifact-dir = \"out/examples/stm32wba-lp\" }\n]\n"
  },
  {
    "path": "examples/stm32wba-lp/build.rs",
    "content": "use std::error::Error;\n\nfn main() -> Result<(), Box<dyn Error>> {\n    println!(\"cargo:rustc-link-arg-bins=--nmagic\");\n    println!(\"cargo:rustc-link-arg-bins=-Tlink.x\");\n    println!(\"cargo:rustc-link-arg-bins=-Tdefmt.x\");\n\n    Ok(())\n}\n"
  },
  {
    "path": "examples/stm32wba-lp/src/bin/blinky.rs",
    "content": "//! Blinky example with STOP mode.\n//!\n//! The MCU enters STOP mode between LED toggles (5 s intervals).\n//! Current draw drops to ~1 µA while sleeping; the RTC wakeup alarm\n//! brings the core back to run mode for the next toggle.\n\n#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_stm32::gpio::{Level, Output, Speed};\nuse embassy_stm32::rcc::*;\nuse embassy_time::Timer;\nuse {defmt_rtt as _, panic_probe as _};\n\n#[embassy_executor::main(executor = \"embassy_stm32::executor::Executor\", entry = \"cortex_m_rt::entry\")]\nasync fn main(_spawner: Spawner) {\n    let mut config = embassy_stm32::Config::default();\n\n    // HSI 16 MHz as sysclk — no PLL needed for a blinky demo.\n    config.rcc.sys = Sysclk::HSI;\n\n    // LSI 32 kHz for the RTC — the time driver uses the RTC wakeup\n    // alarm to bring the core back from STOP mode.\n    config.rcc.ls = LsConfig {\n        rtc: RtcClockSource::LSI,\n        lsi: true,\n        lse: None,\n    };\n\n    // Disable debug peripherals during STOP to minimise leakage.\n    // Set to `true` when debugging with probe-rs / RTT.\n    config.enable_debug_during_sleep = false;\n\n    let p = embassy_stm32::init(config);\n    info!(\"Hello from STM32WBA low-power blinky!\");\n\n    let mut led = Output::new(p.PB4, Level::High, Speed::Low);\n\n    loop {\n        info!(\"led on — sleeping 5 s\");\n        led.set_high();\n        Timer::after_millis(5000).await; // MCU enters STOP here\n\n        info!(\"led off — sleeping 5 s\");\n        led.set_low();\n        Timer::after_millis(5000).await; // MCU enters STOP here\n    }\n}\n"
  },
  {
    "path": "examples/stm32wba-lp/src/bin/button_exti.rs",
    "content": "//! EXTI button example with STOP mode.\n//!\n//! The MCU enters STOP mode while waiting for a button press on PC13.\n//! The EXTI line wakes the core from STOP — no polling required.\n//! Current draw drops to ~1 µA between presses.\n\n#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_stm32::exti::{self, ExtiInput};\nuse embassy_stm32::gpio::Pull;\nuse embassy_stm32::rcc::*;\nuse embassy_stm32::{bind_interrupts, interrupt};\nuse {defmt_rtt as _, panic_probe as _};\n\nbind_interrupts!(\n    pub struct Irqs {\n        EXTI13 => exti::InterruptHandler<interrupt::typelevel::EXTI13>;\n    }\n);\n\n#[embassy_executor::main(executor = \"embassy_stm32::executor::Executor\", entry = \"cortex_m_rt::entry\")]\nasync fn main(_spawner: Spawner) {\n    let mut config = embassy_stm32::Config::default();\n\n    // HSI 16 MHz as sysclk.\n    config.rcc.sys = Sysclk::HSI;\n\n    // LSI 32 kHz for the RTC — the time driver uses the RTC wakeup\n    // alarm to bring the core back from STOP mode.\n    config.rcc.ls = LsConfig {\n        rtc: RtcClockSource::LSI,\n        lsi: true,\n        lse: None,\n    };\n\n    // Disable debug peripherals during STOP to minimise leakage.\n    // Set to `true` when debugging with probe-rs / RTT.\n    config.enable_debug_during_sleep = false;\n\n    let p = embassy_stm32::init(config);\n    info!(\"Hello from STM32WBA low-power button example!\");\n    info!(\"Press the USER button (PC13)...\");\n\n    let mut button = ExtiInput::new(p.PC13, p.EXTI13, Pull::Up, Irqs);\n\n    loop {\n        // MCU enters STOP while waiting for the falling edge.\n        button.wait_for_falling_edge().await;\n        info!(\"Pressed!\");\n\n        button.wait_for_rising_edge().await;\n        info!(\"Released!\");\n    }\n}\n"
  },
  {
    "path": "examples/stm32wba6/.cargo/config.toml",
    "content": "[target.'cfg(all(target_arch = \"arm\", target_os = \"none\"))']\nrunner = \"probe-rs run --chip STM32WBA65RI\"\n\n[build]\ntarget = \"thumbv8m.main-none-eabihf\"\n\n[env]\nDEFMT_LOG = \"trace\"\n"
  },
  {
    "path": "examples/stm32wba6/Cargo.toml",
    "content": "[package]\nedition = \"2024\"\nname = \"embassy-stm32wba6-examples\"\nversion = \"0.1.0\"\nlicense = \"MIT OR Apache-2.0\"\npublish = false\n\n[dependencies]\nembassy-stm32 = { version = \"0.6.0\", path = \"../../embassy-stm32\", features = [ \"defmt\", \"stm32wba65ri\", \"time-driver-any\", \"memory-x\", \"exti\"]  }\nstm32-metapac = { version = \"21\", features = [\"stm32wba65ri\"] }\nembassy-stm32-wpan = { version = \"0.1.0\", path = \"../../embassy-stm32-wpan\", features = [\"defmt\", \"stm32wba65ri\"], optional = true }\nembassy-sync = { version = \"0.8.0\", path = \"../../embassy-sync\", features = [\"defmt\"] }\nembassy-executor = { version = \"0.10.0\", path = \"../../embassy-executor\", features = [\"platform-cortex-m\", \"executor-thread\", \"defmt\"] }\nembassy-time = { version = \"0.5.1\", path = \"../../embassy-time\", features = [\"defmt\", \"defmt-timestamp-uptime\", \"tick-hz-32_768\"] }\nembassy-net = { version = \"0.9.0\", path = \"../../embassy-net\", features = [\"defmt\", \"udp\", \"proto-ipv6\", \"medium-ieee802154\", ], optional = true }\nembassy-usb = { version = \"0.6.0\", path = \"../../embassy-usb\", features = [\"defmt\"] }\nembassy-futures = { version = \"0.1.2\", path = \"../../embassy-futures\" }\nembedded-sdmmc = \"0.9.0\"\nembedded-hal-bus = \"0.3.0\"\n\ndefmt = \"1.0.1\"\ndefmt-rtt = \"1.0.0\"\n\ncortex-m = { version = \"0.7.6\", features = [\"inline-asm\", \"critical-section-single-core\"] }\ncortex-m-rt = \"0.7.0\"\nembedded-hal = \"1.0.0\"\npanic-probe = { version = \"1.0.0\", features = [\"print-defmt\"] }\nheapless = { version = \"0.9\", default-features = false }\nstatic_cell = \"2\"\n\n[features]\ndefault = [\"ble\"]\nmac = [\"dep:embassy-stm32-wpan\", \"embassy-stm32-wpan/wba_mac\", \"dep:embassy-net\"]\nble = [\"dep:embassy-stm32-wpan\", \"embassy-stm32-wpan/wba_ble\", \"embassy-stm32-wpan/ble-stack-basic\", \"embassy-stm32-wpan/linklayer6-ble-basic\"]\n\n[profile.release]\ndebug = 2\n\n[[bin]]\nname = \"mac_ffd\"\nrequired-features = [\"mac\"]\n\n[[bin]]\nname = \"ble_advertiser\"\nrequired-features = [\"ble\"]\n\n[[bin]]\nname = \"ble_scanner\"\nrequired-features = [\"ble\"]\n\n[[bin]]\nname = \"ble_peripheral_connect\"\nrequired-features = [\"ble\"]\n\n[[bin]]\nname = \"ble_secure\"\nrequired-features = [\"ble\"]\n\n[[bin]]\nname = \"ble_gatt_server\"\nrequired-features = [\"ble\"]\n\n[[bin]]\nname = \"ble_central\"\nrequired-features = [\"ble\"]\n\n[package.metadata.embassy]\nbuild = [\n  { target = \"thumbv8m.main-none-eabihf\", artifact-dir = \"out/examples/stm32wba6\" }\n]\n"
  },
  {
    "path": "examples/stm32wba6/README_PCM.md",
    "content": "# SD to SAI PCM Streaming\n\nThis example streams audio from SD card to SAI using raw PCM files.\n\n## File format\n\nRaw PCM files (.pcm) must be:\n- Unsigned 16-bit little-endian\n- 48 kHz\n- Mono\n\n## Convert WAV to PCM\n\nUse the Python helper:\n\n```bash\npython3 convert_wav.py input.wav output.pcm\n```\n\nIf your WAV is not 48 kHz, resample it first (for example with ffmpeg):\n\n```bash\nffmpeg -i input.wav -ar 48000 -ac 1 temp.wav\n```\n\nThen convert `temp.wav` to PCM with the script above.\n\n## Usage\n\n1. Copy a `.pcm` or `.wav` file to the root of the SD card.\n2. Flash and run:\n\n```bash\ncargo run --bin sdmmc_sai --release\n```\n\nThe example plays the first `.pcm` or `.wav` file found in the SD card root.\n"
  },
  {
    "path": "examples/stm32wba6/build.rs",
    "content": "use std::error::Error;\n\nfn main() -> Result<(), Box<dyn Error>> {\n    println!(\"cargo:rustc-link-arg-bins=--nmagic\");\n    println!(\"cargo:rustc-link-arg-bins=-Tlink.x\");\n    println!(\"cargo:rustc-link-arg-bins=-Tdefmt.x\");\n\n    Ok(())\n}\n"
  },
  {
    "path": "examples/stm32wba6/convert_wav.py",
    "content": "#!/usr/bin/env python3\n\nimport os\nimport struct\nimport sys\nimport wave\n\n\ndef convert_wav_to_pcm(input_file: str, output_file: str) -> bool:\n    \"\"\"Convert a WAV file to raw unsigned 16-bit PCM.\"\"\"\n\n    print(f\"Converting {input_file} -> {output_file}\")\n\n    try:\n        with wave.open(input_file, \"rb\") as wav_file:\n            channels = wav_file.getnchannels()\n            sample_width = wav_file.getsampwidth()\n            sample_rate = wav_file.getframerate()\n            frames = wav_file.getnframes()\n\n            print(\n                f\"WAV: {channels}ch, {sample_width * 8}bit, {sample_rate}Hz, {frames} frames\"\n            )\n\n            if sample_width != 2:\n                print(\"Error: only 16-bit WAV files are supported\")\n                return False\n\n            if sample_rate != 48000:\n                print(\"Warning: SAI example expects 48 kHz PCM\")\n\n            data = wav_file.readframes(frames)\n\n        with open(output_file, \"wb\") as pcm_file:\n            if channels == 1:\n                for (sample,) in struct.iter_unpack(\"<h\", data):\n                    unsigned = (sample + 0x8000) & 0xFFFF\n                    pcm_file.write(struct.pack(\"<H\", unsigned))\n            else:\n                step = channels\n                samples = struct.iter_unpack(\"<h\", data)\n                index = 0\n                for (sample,) in samples:\n                    if index % step == 0:\n                        unsigned = (sample + 0x8000) & 0xFFFF\n                        pcm_file.write(struct.pack(\"<H\", unsigned))\n                    index += 1\n\n        print(\"Done\")\n        print(f\"Size: {os.path.getsize(output_file)} bytes\")\n        return True\n    except Exception as exc:\n        print(f\"Error: {exc}\")\n        return False\n\n\ndef main() -> None:\n    if len(sys.argv) != 3:\n        print(\"Usage: convert_wav.py input.wav output.pcm\")\n        sys.exit(1)\n\n    input_file = sys.argv[1]\n    output_file = sys.argv[2]\n\n    if not os.path.exists(input_file):\n        print(f\"Error: input file '{input_file}' not found\")\n        sys.exit(1)\n\n    ok = convert_wav_to_pcm(input_file, output_file)\n    sys.exit(0 if ok else 1)\n\n\nif __name__ == \"__main__\":\n    main()\n"
  },
  {
    "path": "examples/stm32wba6/convert_wav.sh",
    "content": "#!/bin/sh\n\n# Thin wrapper around convert_wav.py so one workflow works for bash users too.\n\nSCRIPT_DIR=$(CDPATH= cd -- \"$(dirname -- \"$0\")\" && pwd)\n\nexec python3 \"$SCRIPT_DIR/convert_wav.py\" \"$@\"\n"
  },
  {
    "path": "examples/stm32wba6/src/bin/adc.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_stm32::adc::{Adc, AdcChannel, SampleTime, adc4};\nuse embassy_stm32::rcc::{\n    AHB5Prescaler, AHBPrescaler, APBPrescaler, PllDiv, PllMul, PllPreDiv, PllSource, Sysclk, VoltageScale,\n};\nuse embassy_stm32::{Config, bind_interrupts, dma, peripherals};\nuse {defmt_rtt as _, panic_probe as _};\n\nbind_interrupts!(struct Irqs {\n    GPDMA1_CHANNEL1 => dma::InterruptHandler<peripherals::GPDMA1_CH1>;\n});\n\n#[embassy_executor::main]\nasync fn main(_spawner: embassy_executor::Spawner) {\n    let mut config = Config::default();\n    // Fine-tune PLL1 dividers/multipliers\n    config.rcc.pll1 = Some(embassy_stm32::rcc::Pll {\n        source: PllSource::HSI,\n        prediv: PllPreDiv::DIV1,  // PLLM = 1 → HSI / 1 = 16 MHz\n        mul: PllMul::MUL30,       // PLLN = 30 → 16 MHz * 30 = 480 MHz VCO\n        divr: Some(PllDiv::DIV5), // PLLR = 5 → 96 MHz (Sysclk)\n        // divq: Some(PllDiv::DIV10), // PLLQ = 10 → 48 MHz (NOT USED)\n        divq: None,\n        divp: Some(PllDiv::DIV30), // PLLP = 30 → 16 MHz (USBOTG)\n        frac: Some(0),             // Fractional part (enabled)\n    });\n\n    config.rcc.ahb_pre = AHBPrescaler::DIV1;\n    config.rcc.apb1_pre = APBPrescaler::DIV1;\n    config.rcc.apb2_pre = APBPrescaler::DIV1;\n    config.rcc.apb7_pre = APBPrescaler::DIV1;\n    config.rcc.ahb5_pre = AHB5Prescaler::DIV4;\n\n    // voltage scale for max performance\n    config.rcc.voltage_scale = VoltageScale::RANGE1;\n    // route PLL1_P into the USB‐OTG‐HS block\n    config.rcc.sys = Sysclk::PLL1_R;\n\n    let mut p = embassy_stm32::init(config);\n\n    // **** ADC4 init ****\n    let mut adc4 = Adc::new_adc4(p.ADC4);\n    let mut adc4_pin1 = p.PA0; // A4\n    let mut adc4_pin2 = p.PA1; // A5\n    adc4.set_resolution_adc4(adc4::Resolution::BITS12);\n    adc4.set_averaging_adc4(adc4::Averaging::Samples256);\n    let max4 = adc4::resolution_to_max_count(adc4::Resolution::BITS12);\n\n    // **** ADC4 blocking read ****\n    let raw: u16 = adc4.blocking_read(&mut adc4_pin1, adc4::SampleTime::CYCLES1_5);\n    let volt: f32 = 3.0 * raw as f32 / max4 as f32;\n    info!(\"Read adc4 pin 1 {}\", volt);\n\n    let raw: u16 = adc4.blocking_read(&mut adc4_pin2, adc4::SampleTime::CYCLES1_5);\n    let volt: f32 = 3.3 * raw as f32 / max4 as f32;\n    info!(\"Read adc4 pin 2 {}\", volt);\n\n    // **** ADC4 async read ****\n    let mut degraded41 = adc4_pin1.degrade_adc();\n    let mut degraded42 = adc4_pin2.degrade_adc();\n    let mut measurements = [0u16; 2];\n\n    // The channels must be in ascending order and can't repeat for ADC4\n    adc4.read(\n        p.GPDMA1_CH1.reborrow(),\n        Irqs,\n        [\n            (&mut degraded42, SampleTime::CYCLES12_5),\n            (&mut degraded41, SampleTime::CYCLES12_5),\n        ]\n        .into_iter(),\n        &mut measurements,\n    )\n    .await;\n    let volt2: f32 = 3.3 * measurements[0] as f32 / max4 as f32;\n    let volt1: f32 = 3.0 * measurements[1] as f32 / max4 as f32;\n    info!(\"Async read 4 pin 1 {}\", volt1);\n    info!(\"Async read 4 pin 2 {}\", volt2);\n}\n"
  },
  {
    "path": "examples/stm32wba6/src/bin/adc_ring_buffered.rs",
    "content": "//! ADC Ring Buffered Example - True Circular DMA for ADC4 Internal Channels\n//!\n//! This example demonstrates continuous ADC sampling using circular DMA with\n//! GPDMA linked-list mode. The ADC continuously samples internal channels\n//! (VREFINT, VCORE, Temperature) with maximum quality settings.\n//!\n//! Temperature is calculated using factory calibration values (TS_CAL1, TS_CAL2)\n//! from the DESIG peripheral for accurate readings.\n//!\n//! Sample rate calculation (48 MHz ADC clock):\n//! - CYCLES79_5 sample time + 12.5 conversion = 92 cycles per raw sample\n//! - Samples256 averaging: 256 × 92 = 23,552 cycles per averaged result\n//! - 3 channels: 23,552 × 3 = 70,656 cycles per complete set\n//! - Rate: 48 MHz / 70,656 ≈ 679 sets/sec\n//!\n//! Using longest available sample time (1.66 µs) and maximum averaging for\n//! excellent accuracy on internal channels (VREFINT, Temperature).\n\n#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_stm32::adc::adc4::Calibration;\nuse embassy_stm32::adc::{Adc, AdcChannel, CONTINUOUS, RingBufferedAdc, adc4};\nuse embassy_stm32::peripherals::{ADC4, GPDMA1_CH1};\nuse embassy_stm32::rcc::{\n    AHB5Prescaler, AHBPrescaler, APBPrescaler, PllDiv, PllMul, PllPreDiv, PllSource, Sysclk, VoltageScale,\n};\nuse embassy_stm32::{Config, bind_interrupts, dma};\nuse {defmt_rtt as _, panic_probe as _};\n\n// DMA buffer size - must be large enough to prevent overruns\n// Buffer holds: [vrefint, vcore, temp, vrefint, vcore, temp, ...]\n// Size should be a multiple of number of channels (3) and large enough for processing\nconst DMA_BUF_LEN: usize = 3 * 256; // 256 samples per channel\n\nbind_interrupts!(struct Irqs {\n    GPDMA1_CHANNEL1 => dma::InterruptHandler<GPDMA1_CH1>;\n});\n\n#[embassy_executor::main]\nasync fn main(_spawner: embassy_executor::Spawner) {\n    // Configure RCC with PLL1 - required for ADC4 clock\n    let mut config = Config::default();\n    config.rcc.pll1 = Some(embassy_stm32::rcc::Pll {\n        source: PllSource::HSI,\n        prediv: PllPreDiv::DIV1,  // PLLM = 1 → HSI / 1 = 16 MHz\n        mul: PllMul::MUL30,       // PLLN = 30 → 16 MHz * 30 = 480 MHz VCO\n        divr: Some(PllDiv::DIV5), // PLLR = 5 → 96 MHz (Sysclk)\n        divq: None,\n        divp: Some(PllDiv::DIV30), // PLLP = 30 → 16 MHz (ADC4 clock source)\n        frac: Some(0),\n    });\n\n    config.rcc.ahb_pre = AHBPrescaler::DIV1;\n    config.rcc.apb1_pre = APBPrescaler::DIV1;\n    config.rcc.apb2_pre = APBPrescaler::DIV1;\n    config.rcc.apb7_pre = APBPrescaler::DIV1;\n    config.rcc.ahb5_pre = AHB5Prescaler::DIV4;\n    config.rcc.voltage_scale = VoltageScale::RANGE1;\n    config.rcc.sys = Sysclk::PLL1_R;\n\n    let p = embassy_stm32::init(config);\n\n    info!(\"STM32WBA6 ADC4 Ring Buffered Example - Circular DMA with Calibrated Temperature\");\n\n    // Read factory calibration values\n    let calibration = Calibration::read();\n    info!(\n        \"Calibration values: TS_CAL1={} (30C), TS_CAL2={} (130C), VREFINT_CAL={}\",\n        calibration.ts_cal1, calibration.ts_cal2, calibration.vrefint_cal\n    );\n\n    // Initialize ADC4 with appropriate settings\n    // Maximum averaging (Samples256) with longest sample time for best accuracy\n    let mut adc = Adc::new_adc4(p.ADC4);\n    adc.set_resolution_adc4(adc4::Resolution::BITS12);\n    adc.set_averaging_adc4(adc4::Averaging::Samples256);\n\n    let max_count = adc4::resolution_to_max_count(adc4::Resolution::BITS12);\n\n    // Enable internal channels\n    let vrefint = adc.enable_vrefint_adc4();\n    let temperature = adc.enable_temperature_adc4();\n    let vcore = adc.enable_vcore_adc4();\n\n    // Degrade to AnyAdcChannel for use with DMA\n    // IMPORTANT: Order matters for ADC4 - must be ascending channel numbers\n    // VrefInt: Channel 0, VCORE: Channel 12, Temperature: Channel 13\n    let vrefint_ch = vrefint.degrade_adc();\n    let vcore_ch = vcore.degrade_adc();\n    let temp_ch = temperature.degrade_adc();\n\n    info!(\"Internal channels enabled, setting up ring buffer...\");\n\n    // Create DMA buffer - must be static for ring-buffered operation\n    static mut DMA_BUF: [u16; DMA_BUF_LEN] = [0u16; DMA_BUF_LEN];\n\n    // Create the ring-buffered ADC with continuous mode\n    // Channels must be in ascending order for ADC4\n    // CYCLES79_5 (1.66 µs) - longest available sample time for internal channels\n    let mut ring_adc: RingBufferedAdc<ADC4> = adc.into_ring_buffered(\n        p.GPDMA1_CH1,\n        unsafe { &mut *core::ptr::addr_of_mut!(DMA_BUF) },\n        Irqs,\n        [\n            (vrefint_ch, adc4::SampleTime::CYCLES79_5), // Channel 0 - VREFINT\n            (vcore_ch, adc4::SampleTime::CYCLES79_5),   // Channel 12 - VCORE\n            (temp_ch, adc4::SampleTime::CYCLES79_5),    // Channel 13 - Temperature\n        ]\n        .into_iter(),\n        CONTINUOUS,\n    );\n\n    info!(\"Ring buffer configured, starting continuous sampling...\");\n\n    // Read buffer - must be half the size of DMA buffer\n    let mut measurements = [0u16; DMA_BUF_LEN / 2];\n\n    // Track iterations and accumulated values for periodic logging\n    let mut iteration_count: u32 = 0;\n    let mut accumulated_vrefint: u32 = 0;\n    let mut accumulated_vcore: u32 = 0;\n    let mut accumulated_temp: u32 = 0;\n    let mut total_samples: u32 = 0;\n\n    // Log every N iterations (~1 second at ~679 samples/sec)\n    // Half buffer = 128 sample sets, 679/128 ≈ 5.3 reads/sec\n    const LOG_INTERVAL: u32 = 5;\n\n    loop {\n        // Read from ring buffer - this will return when half buffer is filled\n        // IMPORTANT: Read continuously without delay to prevent overruns\n        match ring_adc.read(&mut measurements).await {\n            Ok(_) => {\n                // Process measurements - they come in channel order repeated:\n                // [vrefint, vcore, temp, vrefint, vcore, temp, ...]\n                let num_samples = measurements.len() / 3;\n\n                // Accumulate samples for each channel\n                for i in 0..num_samples {\n                    accumulated_vrefint += measurements[i * 3] as u32;\n                    accumulated_vcore += measurements[i * 3 + 1] as u32;\n                    accumulated_temp += measurements[i * 3 + 2] as u32;\n                }\n                total_samples += num_samples as u32;\n\n                iteration_count += 1;\n\n                // Log periodically to avoid flooding output\n                if iteration_count >= LOG_INTERVAL {\n                    let vrefint_avg = accumulated_vrefint / total_samples;\n                    let vcore_avg = accumulated_vcore / total_samples;\n                    let temp_avg = accumulated_temp / total_samples;\n\n                    // Calculate actual VDDA and convert readings to millivolts\n                    let vdda_mv = calibration.calculate_vdda_mv(vrefint_avg);\n                    let vcore_mv = (vdda_mv * vcore_avg) / max_count;\n\n                    // Convert temperature using factory calibration with VDDA compensation\n                    let temp_mc = calibration.convert_to_millicelsius(temp_avg, vrefint_avg);\n                    let temp_c = temp_mc / 1000;\n                    let temp_frac = (temp_mc % 1000).unsigned_abs();\n\n                    info!(\n                        \"Averaged {} samples: VDDA={} mV | VCORE={} mV | Temp={}.{:03} C\",\n                        total_samples, vdda_mv, vcore_mv, temp_c, temp_frac\n                    );\n\n                    // Reset accumulators\n                    iteration_count = 0;\n                    accumulated_vrefint = 0;\n                    accumulated_vcore = 0;\n                    accumulated_temp = 0;\n                    total_samples = 0;\n                }\n            }\n            Err(_e) => {\n                warn!(\"DMA overrun error - consider increasing buffer size or reducing sample rate\");\n                // Reset accumulators on error\n                iteration_count = 0;\n                accumulated_vrefint = 0;\n                accumulated_vcore = 0;\n                accumulated_temp = 0;\n                total_samples = 0;\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "examples/stm32wba6/src/bin/aes_cbc.rs",
    "content": "//! AES-CBC (Cipher Block Chaining) Mode Example\n//!\n//! Demonstrates AES encryption/decryption using CBC mode with initialization vectors.\n//!\n//! # Cipher Mode: CBC (Cipher Block Chaining)\n//! - Each plaintext block is XORed with previous ciphertext block before encryption\n//! - Requires initialization vector (IV) - must be random and unique per message\n//! - Requires 16-byte aligned data (padding necessary)\n//! - Common for file and disk encryption\n//!\n//! # What This Example Shows\n//! - CBC mode encryption/decryption with IV\n//! - Multi-block processing (streaming)\n//! - 128-bit and 256-bit keys\n//! - Processing data in multiple calls\n//! - Error handling for unaligned data\n//! - NIST test vector validation\n//!\n//! # Security Notes\n//! - IV must be random and unpredictable\n//! - Never reuse the same IV with the same key\n//! - CBC provides confidentiality but NOT authentication\n//! - Consider GCM mode for authenticated encryption\n\n#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_stm32::aes::{Aes, AesCbc, Direction};\nuse embassy_stm32::rcc::{\n    AHB5Prescaler, AHBPrescaler, APBPrescaler, PllDiv, PllMul, PllPreDiv, PllSource, Sysclk, VoltageScale,\n};\nuse embassy_stm32::{Config, bind_interrupts, peripherals};\nuse {defmt_rtt as _, panic_probe as _};\n\nbind_interrupts!(struct Irqs {\n    AES => embassy_stm32::aes::InterruptHandler<peripherals::AES>;\n});\n\n#[embassy_executor::main]\nasync fn main(_spawner: embassy_executor::Spawner) {\n    let mut config = Config::default();\n    config.rcc.pll1 = Some(embassy_stm32::rcc::Pll {\n        source: PllSource::HSI,\n        prediv: PllPreDiv::DIV1,\n        mul: PllMul::MUL30,\n        divr: Some(PllDiv::DIV5),\n        divq: None,\n        divp: Some(PllDiv::DIV30),\n        frac: Some(0),\n    });\n    config.rcc.ahb_pre = AHBPrescaler::DIV1;\n    config.rcc.apb1_pre = APBPrescaler::DIV1;\n    config.rcc.apb2_pre = APBPrescaler::DIV1;\n    config.rcc.apb7_pre = APBPrescaler::DIV1;\n    config.rcc.ahb5_pre = AHB5Prescaler::DIV4;\n    config.rcc.voltage_scale = VoltageScale::RANGE1;\n    config.rcc.sys = Sysclk::PLL1_R;\n\n    let p = embassy_stm32::init(config);\n    info!(\"AES-CBC Example\");\n\n    let mut aes = Aes::new_blocking(p.AES, Irqs);\n\n    // Test vectors from NIST SP 800-38A (F.2.1 CBC-AES128.Encrypt)\n    // Using NO_SWAP mode for direct byte array compatibility\n    let key = [\n        0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6, 0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf, 0x4f, 0x3c,\n    ];\n\n    let iv = [\n        0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,\n    ];\n\n    // Two blocks of plaintext from NIST\n    let plaintext = [\n        // Block 1\n        0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96, 0xe9, 0x3d, 0x7e, 0x11, 0x73, 0x93, 0x17, 0x2a,\n        // Block 2\n        0xae, 0x2d, 0x8a, 0x57, 0x1e, 0x03, 0xac, 0x9c, 0x9e, 0xb7, 0x6f, 0xac, 0x45, 0xaf, 0x8e, 0x51,\n    ];\n\n    // Expected ciphertext from NIST SP 800-38A (big-endian, NO_SWAP mode)\n    let expected_ciphertext = [\n        // Block 1\n        0x76, 0x49, 0xab, 0xac, 0x81, 0x19, 0xb2, 0x46, 0xce, 0xe9, 0x8e, 0x9b, 0x12, 0xe9, 0x19, 0x7d,\n        // Block 2\n        0x50, 0x86, 0xcb, 0x9b, 0x50, 0x72, 0x19, 0xee, 0x95, 0xdb, 0x11, 0x3a, 0x91, 0x76, 0x78, 0xb2,\n    ];\n\n    // ========== CBC Encryption ==========\n    info!(\"=== AES-CBC 128-bit Encryption ===\");\n    info!(\"Key: {:02x}\", key);\n    info!(\"IV:  {:02x}\", iv);\n\n    let cipher = AesCbc::new(&key, &iv);\n    let mut ctx = aes.start(&cipher, Direction::Encrypt);\n\n    let mut ciphertext = [0u8; 32];\n    match aes.payload_blocking(&mut ctx, &plaintext, &mut ciphertext, true) {\n        Ok(()) => {\n            info!(\"Plaintext:  {:02x}\", plaintext);\n            info!(\"Ciphertext: {:02x}\", ciphertext);\n            info!(\"Expected:   {:02x}\", expected_ciphertext);\n\n            if ciphertext == expected_ciphertext {\n                info!(\"✓ CBC Encryption PASSED!\");\n            } else {\n                error!(\"✗ CBC Encryption FAILED!\");\n                // Show which bytes differ\n                for i in 0..32 {\n                    if ciphertext[i] != expected_ciphertext[i] {\n                        error!(\n                            \"  Byte {}: got {:02x}, expected {:02x}\",\n                            i, ciphertext[i], expected_ciphertext[i]\n                        );\n                    }\n                }\n            }\n        }\n        Err(e) => {\n            error!(\"Encryption error: {:?}\", e);\n        }\n    }\n\n    aes.finish_blocking(ctx).ok();\n\n    // ========== CBC Decryption ==========\n    info!(\"=== AES-CBC 128-bit Decryption ===\");\n\n    let cipher = AesCbc::new(&key, &iv);\n    let mut ctx = aes.start(&cipher, Direction::Decrypt);\n\n    let mut decrypted = [0u8; 32];\n    match aes.payload_blocking(&mut ctx, &ciphertext, &mut decrypted, true) {\n        Ok(()) => {\n            info!(\"Ciphertext: {:02x}\", ciphertext);\n            info!(\"Decrypted:  {:02x}\", decrypted);\n            info!(\"Expected:   {:02x}\", plaintext);\n\n            if decrypted == plaintext {\n                info!(\"✓ CBC Decryption PASSED!\");\n            } else {\n                error!(\"✗ CBC Decryption FAILED!\");\n            }\n        }\n        Err(e) => {\n            error!(\"Decryption error: {:?}\", e);\n        }\n    }\n\n    aes.finish_blocking(ctx).ok();\n\n    // ========== Multi-block Processing ==========\n    info!(\"=== AES-CBC Multi-block Processing ===\");\n\n    // Encrypt in multiple calls (simulating streaming)\n    let cipher = AesCbc::new(&key, &iv);\n    let mut ctx = aes.start(&cipher, Direction::Encrypt);\n\n    let mut ciphertext_multi = [0u8; 32];\n\n    // First block\n    match aes.payload_blocking(&mut ctx, &plaintext[..16], &mut ciphertext_multi[..16], false) {\n        Ok(()) => info!(\"✓ Block 1 encrypted\"),\n        Err(e) => error!(\"✗ Block 1 failed: {:?}\", e),\n    }\n\n    // Second block (last=true)\n    match aes.payload_blocking(&mut ctx, &plaintext[16..32], &mut ciphertext_multi[16..32], true) {\n        Ok(()) => info!(\"✓ Block 2 encrypted\"),\n        Err(e) => error!(\"✗ Block 2 failed: {:?}\", e),\n    }\n\n    aes.finish_blocking(ctx).ok();\n\n    if ciphertext_multi == expected_ciphertext {\n        info!(\"✓ Multi-block encryption PASSED!\");\n    } else {\n        error!(\"✗ Multi-block encryption FAILED!\");\n    }\n\n    // ========== 256-bit Key Test ==========\n    info!(\"=== AES-CBC 256-bit Test ===\");\n\n    // NIST SP 800-38A F.2.5 CBC-AES256.Encrypt\n    let key_256 = [\n        0x60, 0x3d, 0xeb, 0x10, 0x15, 0xca, 0x71, 0xbe, 0x2b, 0x73, 0xae, 0xf0, 0x85, 0x7d, 0x77, 0x81, 0x1f, 0x35,\n        0x2c, 0x07, 0x3b, 0x61, 0x08, 0xd7, 0x2d, 0x98, 0x10, 0xa3, 0x09, 0x14, 0xdf, 0xf4,\n    ];\n\n    let iv_256 = [\n        0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,\n    ];\n\n    let plaintext_256 = [\n        0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96, 0xe9, 0x3d, 0x7e, 0x11, 0x73, 0x93, 0x17, 0x2a,\n    ];\n\n    // Expected from NIST (NO_SWAP mode, big-endian)\n    let expected_256 = [\n        0xf5, 0x8c, 0x4c, 0x04, 0xd6, 0xe5, 0xf1, 0xba, 0x77, 0x9e, 0xab, 0xfb, 0x5f, 0x7b, 0xfb, 0xd6,\n    ];\n\n    let cipher = AesCbc::new(&key_256, &iv_256);\n    let mut ctx = aes.start(&cipher, Direction::Encrypt);\n\n    let mut ciphertext_256 = [0u8; 16];\n    match aes.payload_blocking(&mut ctx, &plaintext_256, &mut ciphertext_256, true) {\n        Ok(()) => {\n            info!(\"256-bit Ciphertext: {:02x}\", ciphertext_256);\n            info!(\"Expected:           {:02x}\", expected_256);\n\n            if ciphertext_256 == expected_256 {\n                info!(\"✓ 256-bit CBC Encryption PASSED!\");\n            } else {\n                error!(\"✗ 256-bit CBC Encryption FAILED!\");\n            }\n        }\n        Err(e) => {\n            error!(\"256-bit encryption error: {:?}\", e);\n        }\n    }\n\n    aes.finish_blocking(ctx).ok();\n\n    // ========== Error Handling Example ==========\n    info!(\"=== Testing Error Handling ===\");\n\n    let cipher = AesCbc::new(&key, &iv);\n    let mut ctx = aes.start(&cipher, Direction::Encrypt);\n\n    // Try to encrypt non-aligned data (should fail with REQUIRES_PADDING modes)\n    let unaligned_data = [0u8; 15]; // Not a multiple of 16\n    let mut output = [0u8; 15];\n\n    match aes.payload_blocking(&mut ctx, &unaligned_data, &mut output, false) {\n        Ok(()) => {\n            warn!(\"Unexpected success with unaligned data\");\n        }\n        Err(e) => {\n            info!(\"✓ Correctly rejected unaligned data: {:?}\", e);\n        }\n    }\n\n    aes.finish_blocking(ctx).ok();\n\n    info!(\"=== All AES-CBC tests complete ===\");\n\n    loop {\n        cortex_m::asm::wfi();\n    }\n}\n"
  },
  {
    "path": "examples/stm32wba6/src/bin/aes_ccm.rs",
    "content": "//! AES-CCM (Counter with CBC-MAC) - Authenticated Encryption Example\n//!\n//! Demonstrates authenticated encryption using AES-CCM mode.\n//! CCM is an alternative to GCM that's commonly used in constrained environments.\n//!\n//! # Cipher Mode: CCM (Counter with CBC-MAC)\n//! - Combines CTR encryption with CBC-MAC authentication\n//! - Provides both confidentiality AND authenticity\n//! - Requires knowing data lengths in advance (AAD and payload)\n//! - Used in IEEE 802.15.4 (Zigbee), Bluetooth LE, IPsec\n//!\n//! # Key Differences from GCM\n//! - CCM requires knowing payload/AAD lengths before encryption\n//! - CCM uses CBC-MAC for authentication (vs GHASH in GCM)\n//! - CCM may have variable tag sizes (4-16 bytes)\n//! - CCM AAD has a specific format with length prefix\n//!\n//! # B0 Block Format (computed by AesCcm::new)\n//! - Byte 0: Flags (tag size, AAD present, L value)\n//! - Bytes 1-N: Nonce\n//! - Bytes N+1-15: Payload length (big-endian)\n//!\n//! # AAD Format for CCM\n//! For CCM, AAD must be formatted with a length prefix:\n//! - If AAD_len < 2^16-2^8: 2-byte length || AAD || padding\n//! - If AAD_len < 2^32: 0xFFFE || 4-byte length || AAD || padding\n//!\n//! # Security Notes\n//! - Nonce must be unique for each message with same key\n//! - Tag verification failure means: reject the data\n//! - Variable tag size allows security/overhead tradeoff\n\n#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_stm32::aes::{Aes, AesCcm, Direction};\nuse embassy_stm32::rcc::{\n    AHB5Prescaler, AHBPrescaler, APBPrescaler, PllDiv, PllMul, PllPreDiv, PllSource, Sysclk, VoltageScale,\n};\nuse embassy_stm32::{Config, bind_interrupts, peripherals};\nuse {defmt_rtt as _, panic_probe as _};\n\nbind_interrupts!(struct Irqs {\n    AES => embassy_stm32::aes::InterruptHandler<peripherals::AES>;\n});\n\n#[embassy_executor::main]\nasync fn main(_spawner: embassy_executor::Spawner) {\n    let mut config = Config::default();\n    config.rcc.pll1 = Some(embassy_stm32::rcc::Pll {\n        source: PllSource::HSI,\n        prediv: PllPreDiv::DIV1,\n        mul: PllMul::MUL30,\n        divr: Some(PllDiv::DIV5),\n        divq: None,\n        divp: Some(PllDiv::DIV30),\n        frac: Some(0),\n    });\n    config.rcc.ahb_pre = AHBPrescaler::DIV1;\n    config.rcc.apb1_pre = APBPrescaler::DIV1;\n    config.rcc.apb2_pre = APBPrescaler::DIV1;\n    config.rcc.apb7_pre = APBPrescaler::DIV1;\n    config.rcc.ahb5_pre = AHB5Prescaler::DIV4;\n    config.rcc.voltage_scale = VoltageScale::RANGE1;\n    config.rcc.sys = Sysclk::PLL1_R;\n\n    let p = embassy_stm32::init(config);\n    info!(\"AES-CCM Authenticated Encryption Example\");\n\n    let mut aes = Aes::new_blocking(p.AES, Irqs);\n\n    // ========== CCM Test Case (from ST HAL) ==========\n    // Note: CCM requires pre-formatted AAD with length prefix\n\n    // 256-bit key\n    let key: [u8; 32] = [\n        0xD3, 0x46, 0xD1, 0x1A, 0x71, 0x17, 0xCE, 0x04, 0x08, 0x08, 0x95, 0x70, 0x77, 0x78, 0x28, 0x7C, 0x40, 0xF5,\n        0xF4, 0x73, 0xA9, 0xA8, 0xF2, 0xB1, 0x57, 0x0F, 0x61, 0x37, 0x46, 0x69, 0x75, 0x1A,\n    ];\n\n    // 12-byte nonce (extracted from ST HAL B0 block bytes 1-12)\n    // Note: The first byte of B0 (0x7A) is the FLAGS byte, computed by AesCcm::new()\n    // The nonce is bytes 1-12 of the B0 block\n    let nonce: [u8; 12] = [0x05, 0xC8, 0xCC, 0x77, 0x32, 0xB3, 0xB4, 0x7F, 0x08, 0xAF, 0x1D, 0xAF];\n\n    // Actual AAD (7 bytes): 0x34, 0x21, 0x5F, 0x03, 0x25, 0x67, 0x0B\n    // For CCM, AAD must be formatted with length prefix\n    // Formatted AAD (B1 block): 2-byte length (0x0007) || 7 bytes AAD || 7 bytes padding\n    let formatted_aad: [u8; 16] = [\n        0x00, 0x07, // 2-byte length encoding (7 bytes of AAD)\n        0x34, 0x21, 0x5F, 0x03, 0x25, 0x67, 0x0B, // Actual AAD (7 bytes)\n        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Zero padding\n    ];\n\n    // Plaintext: 17 bytes (not block-aligned)\n    let plaintext: [u8; 17] = [\n        0xBB, 0xD8, 0x83, 0x34, 0x00, 0x00, 0x75, 0xF6, 0xF4, 0xE8, 0x9F, 0x9D, 0xDA, 0x50, 0xF5, 0xEA, 0xB1,\n    ];\n\n    // Expected ciphertext: 17 bytes\n    let expected_ciphertext: [u8; 17] = [\n        0xA7, 0xB7, 0x65, 0x3C, 0x5D, 0x60, 0x0A, 0xF3, 0x9C, 0xA0, 0xDB, 0x48, 0x0F, 0x4F, 0x5C, 0xCE, 0x99,\n    ];\n\n    // Expected tag (16 bytes)\n    let expected_tag: [u8; 16] = [\n        0x35, 0x2C, 0x36, 0xD3, 0x93, 0x5B, 0x88, 0x94, 0x04, 0x26, 0xA0, 0x04, 0x3B, 0xBA, 0xB7, 0xEE,\n    ];\n\n    // AAD length and payload length must be known in advance for CCM\n    let aad_len = 7; // Actual AAD length (not including format prefix)\n    let payload_len = 17;\n\n    info!(\"=== AES-CCM Encryption ===\");\n    info!(\"Key (256-bit): {:02x}\", key[..16]);\n    info!(\"Nonce (12 bytes): {:02x}\", nonce);\n    info!(\"AAD length: {} bytes\", aad_len);\n    info!(\"Payload length: {} bytes\", payload_len);\n\n    // Create CCM cipher\n    // Parameters: key, nonce, AAD length, payload length\n    // IV_SIZE = 12, TAG_SIZE = 16 (full tag)\n    let cipher: AesCcm<32, 12, 16> = AesCcm::new(&key, &nonce, aad_len, payload_len);\n    let mut ctx = aes.start(&cipher, Direction::Encrypt);\n\n    // Process formatted AAD (with length prefix for CCM)\n    // Note: CCM requires AAD to be pre-formatted with length encoding\n    match aes.aad_blocking(&mut ctx, &formatted_aad, true) {\n        Ok(()) => info!(\"✓ AAD processed\"),\n        Err(e) => {\n            error!(\"✗ AAD processing failed: {:?}\", e);\n            loop {\n                cortex_m::asm::wfi();\n            }\n        }\n    }\n\n    // Encrypt payload\n    let mut ciphertext = [0u8; 17];\n    match aes.payload_blocking(&mut ctx, &plaintext, &mut ciphertext, true) {\n        Ok(()) => {\n            info!(\"✓ Payload encrypted\");\n            info!(\"Plaintext:  {:02x}\", plaintext);\n            info!(\"Ciphertext: {:02x}\", ciphertext);\n            info!(\"Expected:   {:02x}\", expected_ciphertext);\n\n            if ciphertext == expected_ciphertext {\n                info!(\"✓ Ciphertext MATCHES!\");\n            } else {\n                error!(\"✗ Ciphertext mismatch!\");\n            }\n        }\n        Err(e) => {\n            error!(\"✗ Encryption failed: {:?}\", e);\n            loop {\n                cortex_m::asm::wfi();\n            }\n        }\n    }\n\n    // Get authentication tag\n    match aes.finish_blocking(ctx) {\n        Ok(Some(tag)) => {\n            info!(\"Auth Tag:     {:02x}\", tag);\n            info!(\"Expected Tag: {:02x}\", expected_tag);\n\n            if tag == expected_tag {\n                info!(\"✓ CCM Encryption PASSED!\");\n            } else {\n                error!(\"✗ Tag mismatch!\");\n            }\n        }\n        Ok(None) => {\n            error!(\"✗ No tag returned!\");\n        }\n        Err(e) => {\n            error!(\"✗ Finish failed: {:?}\", e);\n        }\n    }\n\n    // ========== CCM Decryption ==========\n    info!(\"=== AES-CCM Decryption ===\");\n\n    let cipher: AesCcm<32, 12, 16> = AesCcm::new(&key, &nonce, aad_len, payload_len);\n    let mut ctx = aes.start(&cipher, Direction::Decrypt);\n\n    // Process AAD\n    match aes.aad_blocking(&mut ctx, &formatted_aad, true) {\n        Ok(()) => info!(\"✓ AAD processed\"),\n        Err(e) => {\n            error!(\"✗ AAD processing failed: {:?}\", e);\n            loop {\n                cortex_m::asm::wfi();\n            }\n        }\n    }\n\n    // Decrypt ciphertext\n    let mut decrypted = [0u8; 17];\n    match aes.payload_blocking(&mut ctx, &ciphertext, &mut decrypted, true) {\n        Ok(()) => {\n            info!(\"✓ Payload decrypted\");\n            info!(\"Decrypted: {:02x}\", decrypted);\n\n            if decrypted == plaintext {\n                info!(\"✓ Decryption matches plaintext!\");\n            } else {\n                error!(\"✗ Decryption mismatch!\");\n            }\n        }\n        Err(e) => {\n            error!(\"✗ Decryption failed: {:?}\", e);\n        }\n    }\n\n    // Verify tag\n    match aes.finish_blocking(ctx) {\n        Ok(Some(tag)) => {\n            info!(\"Computed Tag: {:02x}\", tag);\n\n            if tag == expected_tag {\n                info!(\"✓ CCM Decryption and Verification PASSED!\");\n            } else {\n                error!(\"✗ Tag verification FAILED - data may be tampered!\");\n            }\n        }\n        Ok(None) => {\n            error!(\"✗ No tag returned!\");\n        }\n        Err(e) => {\n            error!(\"✗ Finish failed: {:?}\", e);\n        }\n    }\n\n    info!(\"=== All CCM tests complete ===\");\n\n    loop {\n        cortex_m::asm::wfi();\n    }\n}\n"
  },
  {
    "path": "examples/stm32wba6/src/bin/aes_ctr.rs",
    "content": "//! AES-CTR (Counter) Mode Example - Stream Cipher\n//!\n//! Demonstrates AES in Counter mode, which converts the block cipher into a stream cipher.\n//!\n//! # Cipher Mode: CTR (Counter)\n//! - Turns AES into a stream cipher by encrypting sequential counter values\n//! - No padding required - works with any data length\n//! - Encryption and decryption are identical operations\n//! - Allows parallel processing and random access\n//! - Ideal for streaming data\n//!\n//! # What This Example Shows\n//! - Stream cipher operation (no padding needed)\n//! - Partial block handling (13 bytes, not aligned to 16)\n//! - Streaming with multiple calls of varying sizes\n//! - 128-bit and 256-bit keys\n//! - Encryption/decryption symmetry (same operation)\n//! - NIST test vector validation\n//!\n//! # Advantages of CTR Mode\n//! - Process any length data without padding overhead\n//! - Can decrypt specific blocks without decrypting entire stream\n//! - Encryption/decryption use the same hardware operation\n//! - Suitable for real-time streaming applications\n//!\n//! # Security Notes\n//! - Counter/nonce must NEVER be reused with the same key\n//! - Typically: nonce (random) + counter (incremental)\n//! - CTR provides confidentiality but NOT authentication\n//! - Consider GCM mode for authenticated encryption\n\n#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_stm32::aes::{Aes, AesCtr, Direction};\nuse embassy_stm32::rcc::{\n    AHB5Prescaler, AHBPrescaler, APBPrescaler, PllDiv, PllMul, PllPreDiv, PllSource, Sysclk, VoltageScale,\n};\nuse embassy_stm32::{Config, bind_interrupts, peripherals};\nuse {defmt_rtt as _, panic_probe as _};\n\nbind_interrupts!(struct Irqs {\n    AES => embassy_stm32::aes::InterruptHandler<peripherals::AES>;\n});\n\n#[embassy_executor::main]\nasync fn main(_spawner: embassy_executor::Spawner) {\n    let mut config = Config::default();\n    config.rcc.pll1 = Some(embassy_stm32::rcc::Pll {\n        source: PllSource::HSI,\n        prediv: PllPreDiv::DIV1,\n        mul: PllMul::MUL30,\n        divr: Some(PllDiv::DIV5),\n        divq: None,\n        divp: Some(PllDiv::DIV30),\n        frac: Some(0),\n    });\n    config.rcc.ahb_pre = AHBPrescaler::DIV1;\n    config.rcc.apb1_pre = APBPrescaler::DIV1;\n    config.rcc.apb2_pre = APBPrescaler::DIV1;\n    config.rcc.apb7_pre = APBPrescaler::DIV1;\n    config.rcc.ahb5_pre = AHB5Prescaler::DIV4;\n    config.rcc.voltage_scale = VoltageScale::RANGE1;\n    config.rcc.sys = Sysclk::PLL1_R;\n\n    let p = embassy_stm32::init(config);\n    info!(\"AES-CTR Stream Cipher Example\");\n\n    let mut aes = Aes::new_blocking(p.AES, Irqs);\n\n    // Test vectors from NIST SP 800-38A\n    let key = [\n        0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6, 0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf, 0x4f, 0x3c,\n    ];\n\n    // Counter/IV (nonce + counter)\n    let counter = [\n        0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff,\n    ];\n\n    let plaintext = [\n        0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96, 0xe9, 0x3d, 0x7e, 0x11, 0x73, 0x93, 0x17, 0x2a, 0xae, 0x2d,\n        0x8a, 0x57, 0x1e, 0x03, 0xac, 0x9c, 0x9e, 0xb7, 0x6f, 0xac, 0x45, 0xaf, 0x8e, 0x51,\n    ];\n\n    let expected_ciphertext = [\n        0x87, 0x4d, 0x61, 0x91, 0xb6, 0x20, 0xe3, 0x26, 0x1b, 0xef, 0x68, 0x64, 0x99, 0x0d, 0xb6, 0xce, 0x98, 0x06,\n        0xf6, 0x6b, 0x79, 0x70, 0xfd, 0xff, 0x86, 0x17, 0x18, 0x7b, 0xb9, 0xff, 0xfd, 0xff,\n    ];\n\n    // ========== CTR Encryption ==========\n    info!(\"=== AES-CTR 128-bit Encryption ===\");\n    info!(\"Key:     {:02x}\", key);\n    info!(\"Counter: {:02x}\", counter);\n\n    let cipher = AesCtr::new(&key, &counter);\n    let mut ctx = aes.start(&cipher, Direction::Encrypt);\n\n    let mut ciphertext = [0u8; 32];\n    match aes.payload_blocking(&mut ctx, &plaintext, &mut ciphertext, true) {\n        Ok(()) => {\n            info!(\"Plaintext:  {:02x}\", plaintext);\n            info!(\"Ciphertext: {:02x}\", ciphertext);\n            info!(\"Expected:   {:02x}\", expected_ciphertext);\n\n            if ciphertext == expected_ciphertext {\n                info!(\"✓ CTR Encryption PASSED!\");\n            } else {\n                error!(\"✗ CTR Encryption FAILED!\");\n            }\n        }\n        Err(e) => {\n            error!(\"Encryption error: {:?}\", e);\n        }\n    }\n\n    aes.finish_blocking(ctx).ok();\n\n    // ========== CTR Decryption (same as encryption for CTR mode) ==========\n    info!(\"=== AES-CTR 128-bit Decryption ===\");\n\n    // In CTR mode, encryption and decryption are the same operation\n    let cipher = AesCtr::new(&key, &counter);\n    let mut ctx = aes.start(&cipher, Direction::Encrypt); // Note: can use Encrypt for both\n\n    let mut decrypted = [0u8; 32];\n    match aes.payload_blocking(&mut ctx, &ciphertext, &mut decrypted, true) {\n        Ok(()) => {\n            info!(\"Ciphertext: {:02x}\", ciphertext);\n            info!(\"Decrypted:  {:02x}\", decrypted);\n            info!(\"Expected:   {:02x}\", plaintext);\n\n            if decrypted == plaintext {\n                info!(\"✓ CTR Decryption PASSED!\");\n            } else {\n                error!(\"✗ CTR Decryption FAILED!\");\n            }\n        }\n        Err(e) => {\n            error!(\"Decryption error: {:?}\", e);\n        }\n    }\n\n    aes.finish_blocking(ctx).ok();\n\n    // ========== Stream Cipher Property - Partial Block ==========\n    info!(\"=== AES-CTR Stream Cipher - Partial Block ===\");\n\n    // CTR mode can handle any data length (no padding required)\n    let partial_data = [\n        0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96, 0xe9, 0x3d, 0x7e, 0x11, 0x73, // 13 bytes\n    ];\n\n    let expected_partial = [\n        0x87, 0x4d, 0x61, 0x91, 0xb6, 0x20, 0xe3, 0x26, 0x1b, 0xef, 0x68, 0x64, 0x99, // 13 bytes\n    ];\n\n    let cipher = AesCtr::new(&key, &counter);\n    let mut ctx = aes.start(&cipher, Direction::Encrypt);\n\n    let mut ciphertext_partial = [0u8; 13];\n    match aes.payload_blocking(&mut ctx, &partial_data, &mut ciphertext_partial, true) {\n        Ok(()) => {\n            info!(\"Partial input (13 bytes): {:02x}\", partial_data);\n            info!(\"Output:                   {:02x}\", ciphertext_partial);\n            info!(\"Expected:                 {:02x}\", expected_partial);\n\n            if ciphertext_partial == expected_partial {\n                info!(\"✓ Partial block encryption PASSED!\");\n            } else {\n                error!(\"✗ Partial block encryption FAILED!\");\n            }\n        }\n        Err(e) => {\n            error!(\"Partial block error: {:?}\", e);\n        }\n    }\n\n    aes.finish_blocking(ctx).ok();\n\n    // ========== Streaming Multiple Calls ==========\n    info!(\"=== AES-CTR Streaming Processing ===\");\n    // Note: Intermediate chunks must be block-aligned (16 bytes).\n    // Only the final chunk (last=true) can be a partial block.\n\n    let cipher = AesCtr::new(&key, &counter);\n    let mut ctx = aes.start(&cipher, Direction::Encrypt);\n\n    let mut ciphertext_stream = [0u8; 32];\n\n    // Encrypt first 16 bytes (block-aligned)\n    match aes.payload_blocking(&mut ctx, &plaintext[..16], &mut ciphertext_stream[..16], false) {\n        Ok(()) => info!(\"✓ Stream block 1 encrypted (16 bytes)\"),\n        Err(e) => error!(\"✗ Stream block 1 failed: {:?}\", e),\n    }\n\n    // Encrypt final 16 bytes (last=true)\n    match aes.payload_blocking(&mut ctx, &plaintext[16..32], &mut ciphertext_stream[16..32], true) {\n        Ok(()) => info!(\"✓ Stream block 2 encrypted (16 bytes, final)\"),\n        Err(e) => error!(\"✗ Stream block 2 failed: {:?}\", e),\n    }\n\n    aes.finish_blocking(ctx).ok();\n\n    if ciphertext_stream == expected_ciphertext {\n        info!(\"✓ Streaming encryption PASSED!\");\n    } else {\n        error!(\"✗ Streaming encryption FAILED!\");\n        info!(\"Got:      {:02x}\", ciphertext_stream);\n        info!(\"Expected: {:02x}\", expected_ciphertext);\n    }\n\n    // ========== 256-bit Key Test ==========\n    info!(\"=== AES-CTR 256-bit Test ===\");\n\n    let key_256 = [\n        0x60, 0x3d, 0xeb, 0x10, 0x15, 0xca, 0x71, 0xbe, 0x2b, 0x73, 0xae, 0xf0, 0x85, 0x7d, 0x77, 0x81, 0x1f, 0x35,\n        0x2c, 0x07, 0x3b, 0x61, 0x08, 0xd7, 0x2d, 0x98, 0x10, 0xa3, 0x09, 0x14, 0xdf, 0xf4,\n    ];\n\n    let counter_256 = [\n        0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff,\n    ];\n\n    let plaintext_256 = [\n        0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96, 0xe9, 0x3d, 0x7e, 0x11, 0x73, 0x93, 0x17, 0x2a,\n    ];\n\n    let expected_256 = [\n        0x60, 0x1e, 0xc3, 0x13, 0x77, 0x57, 0x89, 0xa5, 0xb7, 0xa7, 0xf5, 0x04, 0xbb, 0xf3, 0xd2, 0x28,\n    ];\n\n    let cipher = AesCtr::new(&key_256, &counter_256);\n    let mut ctx = aes.start(&cipher, Direction::Encrypt);\n\n    let mut ciphertext_256 = [0u8; 16];\n    match aes.payload_blocking(&mut ctx, &plaintext_256, &mut ciphertext_256, true) {\n        Ok(()) => {\n            info!(\"256-bit Ciphertext: {:02x}\", ciphertext_256);\n            info!(\"Expected:           {:02x}\", expected_256);\n\n            if ciphertext_256 == expected_256 {\n                info!(\"✓ 256-bit CTR Encryption PASSED!\");\n            } else {\n                error!(\"✗ 256-bit CTR Encryption FAILED!\");\n            }\n        }\n        Err(e) => {\n            error!(\"256-bit encryption error: {:?}\", e);\n        }\n    }\n\n    aes.finish_blocking(ctx).ok();\n\n    // ========== Demonstrate Stream Cipher Advantage ==========\n    info!(\"=== CTR Mode Advantages ===\");\n    info!(\"✓ No padding required - works with any data length\");\n    info!(\"✓ Encryption and decryption use same operation\");\n    info!(\"✓ Parallel processing possible (not shown in blocking mode)\");\n    info!(\"✓ Random access to encrypted data (can decrypt any block independently)\");\n\n    info!(\"=== All AES-CTR tests complete ===\");\n\n    loop {\n        cortex_m::asm::wfi();\n    }\n}\n"
  },
  {
    "path": "examples/stm32wba6/src/bin/aes_ecb.rs",
    "content": "//! AES-ECB (Electronic Codebook) Mode Example\n//!\n//! Demonstrates basic AES encryption/decryption using ECB mode.\n//!\n//! # Cipher Mode: ECB (Electronic Codebook)\n//! - Simplest AES mode - each block encrypted independently\n//! - Requires 16-byte aligned data (padding necessary)\n//! - WARNING: Not recommended for most data - identical plaintext blocks produce\n//!   identical ciphertext blocks, revealing patterns\n//! - Use only for encrypting random data like keys\n//!\n//! # What This Example Shows\n//! - Basic AES peripheral initialization\n//! - 128-bit and 256-bit key encryption\n//! - Encryption and decryption operations\n//! - NIST test vector validation\n//!\n//! # Test Vectors\n//! Uses official NIST SP 800-38A test vectors for validation.\n\n#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_stm32::aes::{Aes, AesEcb, Direction};\nuse embassy_stm32::rcc::{\n    AHB5Prescaler, AHBPrescaler, APBPrescaler, PllDiv, PllMul, PllPreDiv, PllSource, Sysclk, VoltageScale,\n};\nuse embassy_stm32::{Config, bind_interrupts, peripherals};\nuse {defmt_rtt as _, panic_probe as _};\n\nbind_interrupts!(struct Irqs {\n    AES => embassy_stm32::aes::InterruptHandler<peripherals::AES>;\n});\n\n#[embassy_executor::main]\nasync fn main(_spawner: embassy_executor::Spawner) {\n    let mut config = Config::default();\n    config.rcc.pll1 = Some(embassy_stm32::rcc::Pll {\n        source: PllSource::HSI,\n        prediv: PllPreDiv::DIV1,\n        mul: PllMul::MUL30,\n        divr: Some(PllDiv::DIV5),\n        divq: None,\n        divp: Some(PllDiv::DIV30),\n        frac: Some(0),\n    });\n    config.rcc.ahb_pre = AHBPrescaler::DIV1;\n    config.rcc.apb1_pre = APBPrescaler::DIV1;\n    config.rcc.apb2_pre = APBPrescaler::DIV1;\n    config.rcc.apb7_pre = APBPrescaler::DIV1;\n    config.rcc.ahb5_pre = AHB5Prescaler::DIV4;\n    config.rcc.voltage_scale = VoltageScale::RANGE1;\n    config.rcc.sys = Sysclk::PLL1_R;\n\n    let p = embassy_stm32::init(config);\n    info!(\"AES-ECB Example\");\n\n    let mut aes = Aes::new_blocking(p.AES, Irqs);\n\n    // Test vectors from ST HAL CRYP_AESModes example\n    // Key: uint32_t pKeyAES[4] = {0x2B7E1516, 0x28AED2A6, 0xABF71588, 0x09CF4F3C}\n    let key_128 = [\n        0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6, 0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf, 0x4f, 0x3c,\n    ];\n\n    // Plaintext: uint32_t aPlaintextECB[] = {0x6BC1BEE2, 0x2E409F96, 0xE93D7E11, 0x7393172A}\n    let plaintext = [\n        0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96, 0xe9, 0x3d, 0x7e, 0x11, 0x73, 0x93, 0x17, 0x2a,\n    ];\n\n    // Expected: uint32_t aEncryptedtextECB128[] = {0x3AD77BB4, 0x0D7A3660, 0xA89ECAF3, 0x2466EF97}\n    let expected_ciphertext = [\n        0x3a, 0xd7, 0x7b, 0xb4, 0x0d, 0x7a, 0x36, 0x60, 0xa8, 0x9e, 0xca, 0xf3, 0x24, 0x66, 0xef, 0x97,\n    ];\n\n    // ========== Encryption Test ==========\n    info!(\"=== AES-ECB 128-bit Encryption ===\");\n    let cipher = AesEcb::new(&key_128);\n    let mut ctx = aes.start(&cipher, Direction::Encrypt);\n\n    let mut ciphertext = [0u8; 16];\n    match aes.payload_blocking(&mut ctx, &plaintext, &mut ciphertext, true) {\n        Ok(()) => {\n            info!(\"Plaintext:  {:02x}\", plaintext);\n            info!(\"Ciphertext: {:02x}\", ciphertext);\n            info!(\"Expected:   {:02x}\", expected_ciphertext);\n\n            if ciphertext == expected_ciphertext {\n                info!(\"✓ Encryption PASSED!\");\n            } else {\n                error!(\"✗ Encryption FAILED!\");\n            }\n        }\n        Err(e) => {\n            error!(\"Encryption error: {:?}\", e);\n        }\n    }\n\n    aes.finish_blocking(ctx).ok();\n\n    // ========== Decryption Test ==========\n    info!(\"=== AES-ECB 128-bit Decryption ===\");\n    let cipher = AesEcb::new(&key_128);\n    let mut ctx = aes.start(&cipher, Direction::Decrypt);\n\n    let mut decrypted = [0u8; 16];\n    match aes.payload_blocking(&mut ctx, &ciphertext, &mut decrypted, true) {\n        Ok(()) => {\n            info!(\"Ciphertext: {:02x}\", ciphertext);\n            info!(\"Decrypted:  {:02x}\", decrypted);\n            info!(\"Expected:   {:02x}\", plaintext);\n\n            if decrypted == plaintext {\n                info!(\"✓ Decryption PASSED!\");\n            } else {\n                error!(\"✗ Decryption FAILED!\");\n            }\n        }\n        Err(e) => {\n            error!(\"Decryption error: {:?}\", e);\n        }\n    }\n\n    aes.finish_blocking(ctx).ok();\n\n    // ========== 256-bit Key Test ==========\n    info!(\"=== AES-ECB 256-bit Test ===\");\n    // Key: uint32_t aAES256key[] = {0x603DEB10, 0x15CA71BE, 0x2B73AEF0, 0x857D7781, 0x1F352C07, 0x3B6108D7, 0x2D9810A3, 0x0914DFF4}\n    let key_256 = [\n        0x60, 0x3d, 0xeb, 0x10, 0x15, 0xca, 0x71, 0xbe, 0x2b, 0x73, 0xae, 0xf0, 0x85, 0x7d, 0x77, 0x81, 0x1f, 0x35,\n        0x2c, 0x07, 0x3b, 0x61, 0x08, 0xd7, 0x2d, 0x98, 0x10, 0xa3, 0x09, 0x14, 0xdf, 0xf4,\n    ];\n\n    // Use same plaintext as 128-bit test (block 0 of aPlaintextECB)\n    let plaintext_256 = [\n        0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96, 0xe9, 0x3d, 0x7e, 0x11, 0x73, 0x93, 0x17, 0x2a,\n    ];\n\n    // Expected: uint32_t aEncryptedtextECB256[0] = {0xF3EED1BD, 0xB5D2A03C, 0x064B5A7E, 0x3DB181F8}\n    // In big-endian byte order (NO_SWAP mode outputs big-endian)\n    let expected_256 = [\n        0xf3, 0xee, 0xd1, 0xbd, 0xb5, 0xd2, 0xa0, 0x3c, 0x06, 0x4b, 0x5a, 0x7e, 0x3d, 0xb1, 0x81, 0xf8,\n    ];\n\n    let cipher = AesEcb::new(&key_256);\n    let mut ctx = aes.start(&cipher, Direction::Encrypt);\n\n    let mut ciphertext_256 = [0u8; 16];\n    match aes.payload_blocking(&mut ctx, &plaintext_256, &mut ciphertext_256, true) {\n        Ok(()) => {\n            info!(\"256-bit Ciphertext: {:02x}\", ciphertext_256);\n            info!(\"Expected:           {:02x}\", expected_256);\n\n            if ciphertext_256 == expected_256 {\n                info!(\"✓ 256-bit Encryption PASSED!\");\n            } else {\n                error!(\"✗ 256-bit Encryption FAILED!\");\n            }\n        }\n        Err(e) => {\n            error!(\"256-bit Encryption error: {:?}\", e);\n        }\n    }\n\n    aes.finish_blocking(ctx).ok();\n\n    info!(\"=== All AES-ECB tests complete ===\");\n\n    loop {\n        cortex_m::asm::wfi();\n    }\n}\n"
  },
  {
    "path": "examples/stm32wba6/src/bin/aes_gcm.rs",
    "content": "//! AES-GCM (Galois/Counter Mode) - Authenticated Encryption Example\n//!\n//! Demonstrates modern authenticated encryption using AES-GCM mode.\n//! This is the RECOMMENDED mode for new applications.\n//!\n//! # Cipher Mode: GCM (Galois/Counter Mode)\n//! - Combines encryption + authentication in one operation\n//! - No padding required - works with any data length\n//! - Generates authentication tag to detect tampering\n//! - Supports Additional Authenticated Data (AAD) - metadata that's authenticated but not encrypted\n//! - Industry standard for TLS, IPsec, disk encryption\n//!\n//! # What This Example Shows\n//! - Complete GCM encryption with AAD\n//! - Tag generation and verification\n//! - Decryption with authentication check\n//! - GCM without AAD (optional AAD)\n//! - NIST GCM test vector validation\n//!\n//! # Three-Phase GCM Process\n//! 1. AAD Phase: Process metadata to authenticate (optional)\n//! 2. Payload Phase: Encrypt/decrypt data\n//! 3. Final Phase: Generate/verify authentication tag\n//!\n//! # Why Use GCM\n//! - Provides both confidentiality AND authenticity\n//! - Detects any tampering or corruption\n//! - AAD protects metadata without encryption overhead\n//! - Single-pass operation (efficient)\n//! - Widely supported and standardized\n//!\n//! # Security Notes\n//! - IV/nonce must be unique for each encryption with same key\n//! - Recommended: 96-bit (12-byte) random nonce\n//! - ALWAYS verify tag before using decrypted data\n//! - Tag verification failure means: reject the data (tampering detected)\n\n#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_stm32::aes::{Aes, AesGcm, Direction};\nuse embassy_stm32::rcc::{\n    AHB5Prescaler, AHBPrescaler, APBPrescaler, PllDiv, PllMul, PllPreDiv, PllSource, Sysclk, VoltageScale,\n};\nuse embassy_stm32::{Config, bind_interrupts, peripherals};\nuse {defmt_rtt as _, panic_probe as _};\n\nbind_interrupts!(struct Irqs {\n    AES => embassy_stm32::aes::InterruptHandler<peripherals::AES>;\n});\n\n#[embassy_executor::main]\nasync fn main(_spawner: embassy_executor::Spawner) {\n    let mut config = Config::default();\n    config.rcc.pll1 = Some(embassy_stm32::rcc::Pll {\n        source: PllSource::HSI,\n        prediv: PllPreDiv::DIV1,\n        mul: PllMul::MUL30,\n        divr: Some(PllDiv::DIV5),\n        divq: None,\n        divp: Some(PllDiv::DIV30),\n        frac: Some(0),\n    });\n    config.rcc.ahb_pre = AHBPrescaler::DIV1;\n    config.rcc.apb1_pre = APBPrescaler::DIV1;\n    config.rcc.apb2_pre = APBPrescaler::DIV1;\n    config.rcc.apb7_pre = APBPrescaler::DIV1;\n    config.rcc.ahb5_pre = AHB5Prescaler::DIV4;\n    config.rcc.voltage_scale = VoltageScale::RANGE1;\n    config.rcc.sys = Sysclk::PLL1_R;\n\n    let p = embassy_stm32::init(config);\n    info!(\"AES-GCM Authenticated Encryption Example\");\n\n    let mut aes = Aes::new_blocking(p.AES, Irqs);\n\n    // NIST SP 800-38D Test Case 4 (60 bytes plaintext + 20 bytes AAD)\n    // Key: feffe9928665731c6d6a8f9467308308\n    let key = [\n        0xfe, 0xff, 0xe9, 0x92, 0x86, 0x65, 0x73, 0x1c, 0x6d, 0x6a, 0x8f, 0x94, 0x67, 0x30, 0x83, 0x08,\n    ];\n\n    // IV: cafebabefacedbaddecaf888 (12 bytes)\n    let iv = [0xca, 0xfe, 0xba, 0xbe, 0xfa, 0xce, 0xdb, 0xad, 0xde, 0xca, 0xf8, 0x88];\n\n    // AAD: feedfacedeadbeeffeedfacedeadbeefabaddad2 (20 bytes)\n    let aad = [\n        0xfe, 0xed, 0xfa, 0xce, 0xde, 0xad, 0xbe, 0xef, 0xfe, 0xed, 0xfa, 0xce, 0xde, 0xad, 0xbe, 0xef, 0xab, 0xad,\n        0xda, 0xd2,\n    ];\n\n    // Plaintext: 60 bytes (NOT 64 - this is important!)\n    let plaintext = [\n        0xd9, 0x31, 0x32, 0x25, 0xf8, 0x84, 0x06, 0xe5, 0xa5, 0x59, 0x09, 0xc5, 0xaf, 0xf5, 0x26, 0x9a, 0x86, 0xa7,\n        0xa9, 0x53, 0x15, 0x34, 0xf7, 0xda, 0x2e, 0x4c, 0x30, 0x3d, 0x8a, 0x31, 0x8a, 0x72, 0x1c, 0x3c, 0x0c, 0x95,\n        0x95, 0x68, 0x09, 0x53, 0x2f, 0xcf, 0x0e, 0x24, 0x49, 0xa6, 0xb5, 0x25, 0xb1, 0x6a, 0xed, 0xf5, 0xaa, 0x0d,\n        0xe6, 0x57, 0xba, 0x63, 0x7b, 0x39,\n    ];\n\n    // Expected ciphertext: 60 bytes\n    let expected_ciphertext = [\n        0x42, 0x83, 0x1e, 0xc2, 0x21, 0x77, 0x74, 0x24, 0x4b, 0x72, 0x21, 0xb7, 0x84, 0xd0, 0xd4, 0x9c, 0xe3, 0xaa,\n        0x21, 0x2f, 0x2c, 0x02, 0xa4, 0xe0, 0x35, 0xc1, 0x7e, 0x23, 0x29, 0xac, 0xa1, 0x2e, 0x21, 0xd5, 0x14, 0xb2,\n        0x54, 0x66, 0x93, 0x1c, 0x7d, 0x8f, 0x6a, 0x5a, 0xac, 0x84, 0xaa, 0x05, 0x1b, 0xa3, 0x0b, 0x39, 0x6a, 0x0a,\n        0xac, 0x97, 0x3d, 0x58, 0xe0, 0x91,\n    ];\n\n    // Expected tag for NIST Test Case 4 (60 bytes plaintext, 20 bytes AAD): 5bc94fbc3221a5db94fae95ae7121a47\n    let expected_tag = [\n        0x5b, 0xc9, 0x4f, 0xbc, 0x32, 0x21, 0xa5, 0xdb, 0x94, 0xfa, 0xe9, 0x5a, 0xe7, 0x12, 0x1a, 0x47,\n    ];\n\n    // ========== GCM Encryption with AAD ==========\n    info!(\"=== AES-GCM Encryption ===\");\n    info!(\"Key:       {:02x}\", key);\n    info!(\"IV (12b):  {:02x}\", iv);\n    info!(\"AAD:       {:02x}\", aad);\n    info!(\"Plaintext: {} bytes\", plaintext.len());\n\n    let cipher = AesGcm::new(&key, &iv);\n    let mut ctx = aes.start(&cipher, Direction::Encrypt);\n\n    // Process AAD (Additional Authenticated Data)\n    match aes.aad_blocking(&mut ctx, &aad, true) {\n        Ok(()) => info!(\"✓ AAD processed\"),\n        Err(e) => {\n            error!(\"✗ AAD processing failed: {:?}\", e);\n            loop {\n                cortex_m::asm::wfi();\n            }\n        }\n    }\n\n    // Encrypt payload\n    let mut ciphertext = [0u8; 60];\n    match aes.payload_blocking(&mut ctx, &plaintext, &mut ciphertext, true) {\n        Ok(()) => {\n            info!(\"✓ Payload encrypted\");\n            info!(\"Ciphertext: {:02x}\", ciphertext);\n        }\n        Err(e) => {\n            error!(\"✗ Encryption failed: {:?}\", e);\n            loop {\n                cortex_m::asm::wfi();\n            }\n        }\n    }\n\n    // Get authentication tag\n    match aes.finish_blocking(ctx) {\n        Ok(Some(tag)) => {\n            info!(\"Auth Tag:     {:02x}\", tag);\n            info!(\"Expected Tag: {:02x}\", expected_tag);\n\n            // Verify results\n            let ciphertext_ok = ciphertext == expected_ciphertext;\n            let tag_ok = tag == expected_tag;\n\n            if ciphertext_ok && tag_ok {\n                info!(\"✓ GCM Encryption PASSED!\");\n            } else {\n                if !ciphertext_ok {\n                    error!(\"✗ Ciphertext mismatch!\");\n                }\n                if !tag_ok {\n                    error!(\"✗ Tag mismatch!\");\n                }\n            }\n        }\n        Ok(None) => {\n            error!(\"✗ No tag returned!\");\n        }\n        Err(e) => {\n            error!(\"✗ Finish failed: {:?}\", e);\n        }\n    }\n\n    // ========== GCM Decryption and Verification ==========\n    info!(\"=== AES-GCM Decryption ===\");\n\n    let cipher = AesGcm::new(&key, &iv);\n    let mut ctx = aes.start(&cipher, Direction::Decrypt);\n\n    // Process AAD\n    match aes.aad_blocking(&mut ctx, &aad, true) {\n        Ok(()) => info!(\"✓ AAD processed\"),\n        Err(e) => {\n            error!(\"✗ AAD processing failed: {:?}\", e);\n            loop {\n                cortex_m::asm::wfi();\n            }\n        }\n    }\n\n    // Decrypt payload\n    let mut decrypted = [0u8; 60];\n    match aes.payload_blocking(&mut ctx, &ciphertext, &mut decrypted, true) {\n        Ok(()) => {\n            info!(\"✓ Payload decrypted\");\n        }\n        Err(e) => {\n            error!(\"✗ Decryption failed: {:?}\", e);\n            loop {\n                cortex_m::asm::wfi();\n            }\n        }\n    }\n\n    // Get and verify authentication tag\n    match aes.finish_blocking(ctx) {\n        Ok(Some(tag)) => {\n            info!(\"Computed Tag: {:02x}\", tag);\n\n            // In real applications, you would compare the computed tag with the received tag\n            if tag == expected_tag {\n                info!(\"✓ Tag verification PASSED!\");\n\n                // Check decrypted plaintext\n                if decrypted == plaintext {\n                    info!(\"✓ Decryption PASSED!\");\n                    info!(\"Decrypted: {:02x}\", decrypted[..16]);\n                } else {\n                    error!(\"✗ Decryption mismatch!\");\n                }\n            } else {\n                error!(\"✗ Tag verification FAILED - message authentication failed!\");\n                // In real code, you would reject the decrypted data\n            }\n        }\n        Ok(None) => {\n            error!(\"✗ No tag returned!\");\n        }\n        Err(e) => {\n            error!(\"✗ Finish failed: {:?}\", e);\n        }\n    }\n\n    // ========== NIST Test Case 2 (no AAD) ==========\n    info!(\"=== NIST GCM Test Case 2 (no AAD) ===\");\n\n    // NIST SP 800-38D Test Case 2\n    let nist_key = [0u8; 16]; // All zeros\n    let nist_iv = [0u8; 12]; // All zeros\n    let nist_plaintext = [0u8; 16]; // All zeros\n    // Expected ciphertext: 0388dace60b6a392f328c2b971b2fe78\n    let nist_expected_ct = [\n        0x03, 0x88, 0xda, 0xce, 0x60, 0xb6, 0xa3, 0x92, 0xf3, 0x28, 0xc2, 0xb9, 0x71, 0xb2, 0xfe, 0x78,\n    ];\n    // Expected tag: ab6e47d42cec13bdf53a67b21257bddf\n    let nist_expected_tag = [\n        0xab, 0x6e, 0x47, 0xd4, 0x2c, 0xec, 0x13, 0xbd, 0xf5, 0x3a, 0x67, 0xb2, 0x12, 0x57, 0xbd, 0xdf,\n    ];\n\n    let cipher = AesGcm::new(&nist_key, &nist_iv);\n    let mut ctx = aes.start(&cipher, Direction::Encrypt);\n\n    // No AAD - skip aad_blocking call\n\n    let mut nist_ciphertext = [0u8; 16];\n    match aes.payload_blocking(&mut ctx, &nist_plaintext, &mut nist_ciphertext, true) {\n        Ok(()) => {\n            info!(\"Plaintext:  {:02x}\", nist_plaintext);\n            info!(\"Ciphertext: {:02x}\", nist_ciphertext);\n            info!(\"Expected:   {:02x}\", nist_expected_ct);\n            if nist_ciphertext == nist_expected_ct {\n                info!(\"✓ NIST ciphertext MATCHES!\");\n            } else {\n                error!(\"✗ NIST ciphertext MISMATCH!\");\n            }\n        }\n        Err(e) => {\n            error!(\"✗ NIST encryption failed: {:?}\", e);\n        }\n    }\n\n    if let Ok(Some(tag)) = aes.finish_blocking(ctx) {\n        info!(\"Tag:      {:02x}\", tag);\n        info!(\"Expected: {:02x}\", nist_expected_tag);\n        if tag == nist_expected_tag {\n            info!(\"✓ NIST tag MATCHES!\");\n        } else {\n            error!(\"✗ NIST tag MISMATCH!\");\n        }\n    }\n\n    info!(\"=== All AES-GCM tests complete ===\");\n\n    loop {\n        cortex_m::asm::wfi();\n    }\n}\n"
  },
  {
    "path": "examples/stm32wba6/src/bin/aes_gmac.rs",
    "content": "//! AES-GMAC (Galois Message Authentication Code) Example\n//!\n//! Demonstrates message authentication without encryption using AES-GMAC mode.\n//! GMAC authenticates data integrity without providing confidentiality.\n//!\n//! # Cipher Mode: GMAC (Galois Message Authentication Code)\n//! - Provides authentication WITHOUT encryption\n//! - Data remains in plaintext but tampering is detected\n//! - Generates authentication tag for integrity verification\n//! - Uses the same GHASH operation as GCM\n//!\n//! # Use Cases\n//! - Authenticating packet headers in network protocols\n//! - Verifying integrity of publicly-readable metadata\n//! - Any scenario requiring authentication without encryption\n//! - Protocol message authentication\n//!\n//! # How It Works\n//! 1. Initialize with key and unique IV\n//! 2. Process header data (AAD only - no payload)\n//! 3. Generate authentication tag\n//! 4. Use tag to verify data integrity\n//!\n//! # Security Notes\n//! - IV/nonce must be unique for each authentication with same key\n//! - GMAC alone does NOT encrypt data - use GCM if confidentiality needed\n//! - Tag verification detects any tampering with the authenticated data\n\n#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_stm32::aes::{Aes, AesGmac, Direction};\nuse embassy_stm32::rcc::{\n    AHB5Prescaler, AHBPrescaler, APBPrescaler, PllDiv, PllMul, PllPreDiv, PllSource, Sysclk, VoltageScale,\n};\nuse embassy_stm32::{Config, bind_interrupts, peripherals};\nuse {defmt_rtt as _, panic_probe as _};\n\nbind_interrupts!(struct Irqs {\n    AES => embassy_stm32::aes::InterruptHandler<peripherals::AES>;\n});\n\n#[embassy_executor::main]\nasync fn main(_spawner: embassy_executor::Spawner) {\n    let mut config = Config::default();\n    config.rcc.pll1 = Some(embassy_stm32::rcc::Pll {\n        source: PllSource::HSI,\n        prediv: PllPreDiv::DIV1,\n        mul: PllMul::MUL30,\n        divr: Some(PllDiv::DIV5),\n        divq: None,\n        divp: Some(PllDiv::DIV30),\n        frac: Some(0),\n    });\n    config.rcc.ahb_pre = AHBPrescaler::DIV1;\n    config.rcc.apb1_pre = APBPrescaler::DIV1;\n    config.rcc.apb2_pre = APBPrescaler::DIV1;\n    config.rcc.apb7_pre = APBPrescaler::DIV1;\n    config.rcc.ahb5_pre = AHB5Prescaler::DIV4;\n    config.rcc.voltage_scale = VoltageScale::RANGE1;\n    config.rcc.sys = Sysclk::PLL1_R;\n\n    let p = embassy_stm32::init(config);\n    info!(\"AES-GMAC Message Authentication Example\");\n\n    let mut aes = Aes::new_blocking(p.AES, Irqs);\n\n    // ========== GMAC Test Case (from ST HAL) ==========\n    // This test authenticates a 68-byte header message\n\n    // 256-bit key\n    let key: [u8; 32] = [\n        0x69, 0x1D, 0x3E, 0xE9, 0x09, 0xD7, 0xF5, 0x41, 0x67, 0xFD, 0x1C, 0xA0, 0xB5, 0xD7, 0x69, 0x08, 0x1F, 0x2B,\n        0xDE, 0x1A, 0xEE, 0x65, 0x5F, 0xDB, 0xAB, 0x80, 0xBD, 0x52, 0x95, 0xAE, 0x6B, 0xE7,\n    ];\n\n    // 12-byte IV (nonce)\n    let iv: [u8; 12] = [0xF0, 0x76, 0x1E, 0x8D, 0xCD, 0x3D, 0x00, 0x01, 0x76, 0xD4, 0x57, 0xED];\n\n    // Header message to authenticate (68 bytes - not block aligned)\n    let header: [u8; 68] = [\n        0xE2, 0x01, 0x06, 0xD7, 0xCD, 0x0D, 0xF0, 0x76, 0x1E, 0x8D, 0xCD, 0x3D, 0x88, 0xE5, 0x40, 0x00, 0x76, 0xD4,\n        0x57, 0xED, 0x08, 0x00, 0x0F, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C,\n        0x1D, 0x1E, 0x1F, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E,\n        0x2F, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x00, 0x03,\n    ];\n\n    // Expected tag from ST HAL test\n    let expected_tag: [u8; 16] = [\n        0x35, 0x21, 0x7C, 0x77, 0x4B, 0xBC, 0x31, 0xB6, 0x31, 0x66, 0xBC, 0xF9, 0xD4, 0xAB, 0xED, 0x07,\n    ];\n\n    info!(\"=== AES-GMAC Authentication ===\");\n    info!(\"Key (256-bit): {:02x}\", key[..16]);\n    info!(\"IV (12 bytes): {:02x}\", iv);\n    info!(\"Header: {} bytes\", header.len());\n\n    // Create GMAC cipher\n    let cipher = AesGmac::new(&key, &iv);\n    let mut ctx = aes.start(&cipher, Direction::Encrypt);\n\n    // Process header data (this is the data we're authenticating)\n    match aes.aad_blocking(&mut ctx, &header, true) {\n        Ok(()) => info!(\"✓ Header processed for authentication\"),\n        Err(e) => {\n            error!(\"✗ Header processing failed: {:?}\", e);\n            loop {\n                cortex_m::asm::wfi();\n            }\n        }\n    }\n\n    // No payload phase for GMAC - we're just authenticating, not encrypting\n\n    // Get authentication tag\n    match aes.finish_blocking(ctx) {\n        Ok(Some(tag)) => {\n            info!(\"Computed Tag: {:02x}\", tag);\n            info!(\"Expected Tag: {:02x}\", expected_tag);\n\n            if tag == expected_tag {\n                info!(\"✓ GMAC Authentication PASSED!\");\n            } else {\n                error!(\"✗ GMAC Tag mismatch!\");\n            }\n        }\n        Ok(None) => {\n            error!(\"✗ No tag returned!\");\n        }\n        Err(e) => {\n            error!(\"✗ Finish failed: {:?}\", e);\n        }\n    }\n\n    // ========== Simple GMAC Example ==========\n    info!(\"=== Simple GMAC Example ===\");\n\n    // 128-bit key for simplicity\n    let simple_key: [u8; 16] = [\n        0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,\n    ];\n\n    let simple_iv: [u8; 12] = [0xca, 0xfe, 0xba, 0xbe, 0xfa, 0xce, 0xdb, 0xad, 0xde, 0xca, 0xf8, 0x88];\n\n    // Message to authenticate\n    let message = b\"This message will be authenticated but NOT encrypted\";\n\n    info!(\"Message: \\\"{}\\\"\", core::str::from_utf8(message).unwrap_or(\"\"));\n    info!(\"Message length: {} bytes\", message.len());\n\n    let cipher = AesGmac::new(&simple_key, &simple_iv);\n    let mut ctx = aes.start(&cipher, Direction::Encrypt);\n\n    match aes.aad_blocking(&mut ctx, message, true) {\n        Ok(()) => info!(\"✓ Message authenticated\"),\n        Err(e) => {\n            error!(\"✗ Authentication failed: {:?}\", e);\n        }\n    }\n\n    if let Ok(Some(tag)) = aes.finish_blocking(ctx) {\n        info!(\"Auth Tag: {:02x}\", tag);\n        info!(\"Note: This tag can be sent with the message to verify integrity\");\n    }\n\n    info!(\"=== All GMAC tests complete ===\");\n\n    loop {\n        cortex_m::asm::wfi();\n    }\n}\n"
  },
  {
    "path": "examples/stm32wba6/src/bin/ble_advertiser.rs",
    "content": "//! BLE Advertiser Example\n//!\n//! This example demonstrates the Phase 1 BLE stack implementation:\n//! - Initializes the BLE stack\n//! - Creates a simple GATT service with a characteristic\n//! - Starts BLE advertising\n//! - The device will appear as \"Embassy-WBA6\" in BLE scanner apps\n//!\n//! Hardware: STM32WBA65 or compatible\n//!\n//! To test:\n//! 1. Flash this example to your STM32WBA6 board\n//! 2. Use a BLE scanner app (nRF Connect, LightBlue, etc.)\n//! 3. Look for \"Embassy-WBA6\" in the scan results\n//! 4. Connect to see the GATT service\n\n#![no_std]\n#![no_main]\n\nuse core::cell::RefCell;\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_stm32::peripherals::RNG;\nuse embassy_stm32::rcc::{\n    AHB5Prescaler, AHBPrescaler, APBPrescaler, PllDiv, PllMul, PllPreDiv, PllSource, Sysclk, VoltageScale, mux,\n};\nuse embassy_stm32::rng::{self, Rng};\nuse embassy_stm32::{Config, bind_interrupts};\nuse embassy_stm32_wpan::Ble;\nuse embassy_stm32_wpan::gap::{AdvData, AdvParams, AdvType};\nuse embassy_stm32_wpan::gatt::{CharProperties, GattEventMask, GattServer, SecurityPermissions, ServiceType, Uuid};\nuse embassy_sync::blocking_mutex::Mutex;\nuse embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;\nuse static_cell::StaticCell;\nuse {defmt_rtt as _, panic_probe as _};\n\nbind_interrupts!(struct Irqs {\n    RNG => rng::InterruptHandler<embassy_stm32::peripherals::RNG>;\n});\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let mut config = Config::default();\n\n    // Configure PLL1 (required on WBA)\n    config.rcc.pll1 = Some(embassy_stm32::rcc::Pll {\n        source: PllSource::HSI,\n        prediv: PllPreDiv::DIV1,  // PLLM = 1 → HSI / 1 = 16 MHz\n        mul: PllMul::MUL30,       // PLLN = 30 → 16 MHz * 30 = 480 MHz VCO\n        divr: Some(PllDiv::DIV5), // PLLR = 5 → 96 MHz (Sysclk)\n        divq: None,\n        divp: Some(PllDiv::DIV30), // PLLP = 30 → 16 MHz (required for SAI)\n        frac: Some(0),\n    });\n\n    config.rcc.ahb_pre = AHBPrescaler::DIV1;\n    config.rcc.apb1_pre = APBPrescaler::DIV1;\n    config.rcc.apb2_pre = APBPrescaler::DIV1;\n    config.rcc.apb7_pre = APBPrescaler::DIV1;\n    config.rcc.ahb5_pre = AHB5Prescaler::DIV4;\n    config.rcc.voltage_scale = VoltageScale::RANGE1;\n    config.rcc.sys = Sysclk::PLL1_R;\n\n    // Configure RNG clock source to HSI (required for WBA)\n    config.rcc.mux.rngsel = mux::Rngsel::HSI;\n\n    let p = embassy_stm32::init(config);\n    info!(\"Embassy STM32WBA6 BLE Advertiser Example\");\n\n    // Initialize RNG (required by BLE stack)\n    static RNG: StaticCell<Mutex<CriticalSectionRawMutex, RefCell<Rng<'static, RNG>>>> = StaticCell::new();\n    let rng = RNG.init(Mutex::new(RefCell::new(Rng::new(p.RNG, Irqs))));\n    info!(\"RNG initialized\");\n\n    // Initialize BLE stack\n    let mut ble = Ble::new(rng);\n    ble.init().expect(\"BLE initialization failed\");\n    info!(\"BLE stack initialized\");\n\n    // Initialize GATT server\n    let mut gatt = GattServer::new();\n    gatt.init().expect(\"GATT initialization failed\");\n    info!(\"GATT server initialized\");\n\n    // Create a custom service (UUID: 0x1234)\n    let service_uuid = Uuid::from_u16(0x1234);\n    let service_handle = gatt\n        .add_service(service_uuid, ServiceType::Primary, 5)\n        .expect(\"Failed to add service\");\n    info!(\"Created service with handle: 0x{:04X}\", service_handle.0);\n\n    // Add a read/write characteristic (UUID: 0x5678)\n    let char_uuid = Uuid::from_u16(0x5678);\n    let char_properties = CharProperties::READ | CharProperties::WRITE | CharProperties::NOTIFY;\n    let char_handle = gatt\n        .add_characteristic(\n            service_handle,\n            char_uuid,\n            20, // Max 20 bytes\n            char_properties,\n            SecurityPermissions::NONE,\n            GattEventMask::ATTRIBUTE_MODIFIED,\n            0,    // No encryption\n            true, // Variable length\n        )\n        .expect(\"Failed to add characteristic\");\n    info!(\"Created characteristic with handle: 0x{:04X}\", char_handle.0);\n\n    // Set initial characteristic value\n    let initial_value = b\"Hello BLE!\";\n    gatt.update_characteristic_value(service_handle, char_handle, 0, initial_value)\n        .expect(\"Failed to set characteristic value\");\n    info!(\"Set initial characteristic value\");\n\n    // Create advertising data\n    let mut adv_data = AdvData::new();\n    adv_data\n        .add_flags(0x06) // General discoverable, BR/EDR not supported\n        .expect(\"Failed to add flags\");\n    adv_data.add_name(\"Embassy-WBA6\").expect(\"Failed to add name\");\n    adv_data\n        .add_service_uuid_16(0x1234) // Advertise our custom service\n        .expect(\"Failed to add service UUID\");\n\n    info!(\"Advertising data created ({} bytes)\", adv_data.len());\n\n    // Configure advertising parameters\n    let adv_params = AdvParams {\n        interval_min: 0x0080, // 80 ms\n        interval_max: 0x0080, // 80 ms\n        adv_type: AdvType::ConnectableUndirected,\n        ..AdvParams::default()\n    };\n\n    // Start advertising\n    let mut advertiser = ble.advertiser();\n    advertiser\n        .start(adv_params, adv_data, None)\n        .expect(\"Failed to start advertising\");\n\n    info!(\"BLE advertising started!\");\n    info!(\"Device is visible as 'Embassy-WBA6'\");\n    info!(\"Use a BLE scanner app to discover and connect\");\n\n    // Main loop - handle BLE events\n    loop {\n        let event = ble.read_event().await;\n        info!(\"BLE Event: {:?}\", event);\n\n        // In a real application, you would handle connection events,\n        // characteristic writes, etc. here\n    }\n}\n"
  },
  {
    "path": "examples/stm32wba6/src/bin/ble_central.rs",
    "content": "//! BLE Central Example\n//!\n//! This example demonstrates BLE central role functionality:\n//! - Scans for nearby BLE peripherals\n//! - Connects to a device (either specific address or first discovered)\n//! - Handles connection and disconnection events\n//! - Demonstrates connection parameter management\n//!\n//! Hardware: STM32WBA65 or compatible\n//!\n//! To test:\n//! 1. Flash this example to your STM32WBA6 board\n//! 2. Have a BLE peripheral device advertising nearby\n//! 3. Observe the scan, connection, and event handling\n\n#![no_std]\n#![no_main]\n\nuse core::cell::RefCell;\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_stm32::peripherals::RNG;\nuse embassy_stm32::rcc::{\n    AHB5Prescaler, AHBPrescaler, APBPrescaler, PllDiv, PllMul, PllPreDiv, PllSource, Sysclk, VoltageScale, mux,\n};\nuse embassy_stm32::rng::{self, Rng};\nuse embassy_stm32::{Config, bind_interrupts};\nuse embassy_stm32_wpan::gap::{ConnectionInitParams, GapEvent, ParsedAdvData, ScanParams, ScanType};\nuse embassy_stm32_wpan::hci::event::EventParams;\nuse embassy_stm32_wpan::{Ble, ble_runner};\nuse embassy_sync::blocking_mutex::Mutex;\nuse embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;\nuse static_cell::StaticCell;\nuse {defmt_rtt as _, panic_probe as _};\n\nbind_interrupts!(struct Irqs {\n    RNG => rng::InterruptHandler<embassy_stm32::peripherals::RNG>;\n});\n\n/// BLE runner task - drives the BLE stack sequencer\n#[embassy_executor::task]\nasync fn ble_runner_task() {\n    ble_runner().await\n}\n\n/// Target device name to connect to (set to None to connect to first discovered device)\nconst TARGET_DEVICE_NAME: Option<&str> = None; // e.g., Some(\"Embassy-Peripheral\")\n\n/// Minimum RSSI to consider a device (helps filter out far away devices)\nconst MIN_RSSI: i8 = -80;\n\n#[embassy_executor::main]\nasync fn main(spawner: Spawner) {\n    let mut config = Config::default();\n\n    // Configure PLL1 (required on WBA)\n    config.rcc.pll1 = Some(embassy_stm32::rcc::Pll {\n        source: PllSource::HSI,\n        prediv: PllPreDiv::DIV1,\n        mul: PllMul::MUL30,\n        divr: Some(PllDiv::DIV5),\n        divq: None,\n        divp: Some(PllDiv::DIV30),\n        frac: Some(0),\n    });\n\n    config.rcc.ahb_pre = AHBPrescaler::DIV1;\n    config.rcc.apb1_pre = APBPrescaler::DIV1;\n    config.rcc.apb2_pre = APBPrescaler::DIV1;\n    config.rcc.apb7_pre = APBPrescaler::DIV1;\n    config.rcc.ahb5_pre = AHB5Prescaler::DIV4;\n    config.rcc.voltage_scale = VoltageScale::RANGE1;\n    config.rcc.sys = Sysclk::PLL1_R;\n    config.rcc.mux.rngsel = mux::Rngsel::HSI;\n\n    let p = embassy_stm32::init(config);\n    info!(\"Embassy STM32WBA6 BLE Central Example\");\n\n    // Initialize RNG (required by BLE stack)\n    static RNG: StaticCell<Mutex<CriticalSectionRawMutex, RefCell<Rng<'static, RNG>>>> = StaticCell::new();\n    let rng = RNG.init(Mutex::new(RefCell::new(Rng::new(p.RNG, Irqs))));\n    info!(\"RNG initialized\");\n\n    // Initialize BLE stack\n    let mut ble = Ble::new(rng);\n    ble.init().expect(\"BLE initialization failed\");\n    info!(\"BLE stack initialized\");\n\n    // Spawn the BLE runner task (required for proper BLE operation)\n    spawner.spawn(ble_runner_task().unwrap());\n\n    // State machine for central role\n    let mut state = CentralState::Scanning;\n\n    // Configure scan parameters - active scanning for names\n    let scan_params = ScanParams::new()\n        .with_scan_type(ScanType::Active)\n        .with_interval(0x0050) // 50ms\n        .with_window(0x0030) // 30ms\n        .with_filter_duplicates(false); // Want to see devices multiple times to catch scan responses\n\n    // Start scanning\n    {\n        let mut scanner = ble.scanner();\n        scanner.start(scan_params.clone()).expect(\"Failed to start scanning\");\n    }\n\n    info!(\"=== BLE Central Started ===\");\n    if let Some(name) = TARGET_DEVICE_NAME {\n        info!(\"Looking for device: \\\"{}\\\"\", name);\n    } else {\n        info!(\"Will connect to first suitable device found\");\n    }\n    info!(\"\");\n\n    // Main event loop\n    loop {\n        let event = ble.read_event().await;\n\n        match state {\n            CentralState::Scanning => {\n                // Process advertising reports\n                if let EventParams::LeAdvertisingReport { reports } = &event.params {\n                    for report in reports.iter() {\n                        // Skip weak signals\n                        if report.rssi < MIN_RSSI {\n                            continue;\n                        }\n\n                        // Parse advertising data\n                        let parsed = ParsedAdvData::parse(&report.data);\n\n                        // Check if this device matches our criteria\n                        let should_connect = if let Some(target_name) = TARGET_DEVICE_NAME {\n                            // Looking for specific name\n                            parsed.name == Some(target_name)\n                        } else {\n                            // Connect to any device that has a name (likely a real peripheral)\n                            parsed.name.is_some()\n                        };\n\n                        if should_connect {\n                            info!(\"=== Found Target Device ===\");\n                            info!(\n                                \"  Address: {:02X}:{:02X}:{:02X}:{:02X}:{:02X}:{:02X}\",\n                                report.address.0[5],\n                                report.address.0[4],\n                                report.address.0[3],\n                                report.address.0[2],\n                                report.address.0[1],\n                                report.address.0[0]\n                            );\n                            if let Some(name) = parsed.name {\n                                info!(\"  Name: \\\"{}\\\"\", name);\n                            }\n                            info!(\"  RSSI: {} dBm\", report.rssi);\n\n                            // Stop scanning\n                            {\n                                let mut scanner = ble.scanner();\n                                scanner.stop().expect(\"Failed to stop scanning\");\n                            }\n                            info!(\"Scanning stopped\");\n\n                            // Initiate connection\n                            let conn_params = ConnectionInitParams {\n                                peer_address_type: report.address_type,\n                                peer_address: report.address,\n                                ..ConnectionInitParams::default()\n                            };\n\n                            info!(\"Initiating connection...\");\n                            if let Err(e) = ble.connect(&conn_params) {\n                                error!(\"Failed to initiate connection: {:?}\", e);\n                                // Restart scanning on failure\n                                let mut scanner = ble.scanner();\n                                scanner.start(scan_params.clone()).expect(\"Failed to restart scanning\");\n                            } else {\n                                state = CentralState::Connecting;\n                            }\n                            break;\n                        } else if parsed.name.is_some() {\n                            // Log other named devices we see\n                            debug!(\n                                \"Found device: \\\"{}\\\" at {} dBm\",\n                                parsed.name.unwrap_or(\"?\"),\n                                report.rssi\n                            );\n                        }\n                    }\n                }\n            }\n\n            CentralState::Connecting => {\n                // Wait for connection complete event\n                if let Some(gap_event) = ble.process_event(&event) {\n                    match gap_event {\n                        GapEvent::Connected(conn) => {\n                            info!(\"=== CONNECTED ===\");\n                            info!(\"  Handle: 0x{:04X}\", conn.handle.0);\n                            info!(\n                                \"  Role: {}\",\n                                match conn.role {\n                                    embassy_stm32_wpan::gap::ConnectionRole::Central => \"Central\",\n                                    embassy_stm32_wpan::gap::ConnectionRole::Peripheral => \"Peripheral\",\n                                }\n                            );\n                            info!(\n                                \"  Interval: {} ({}ms)\",\n                                conn.params.interval,\n                                (conn.params.interval as u32 * 125) / 100\n                            );\n                            info!(\"  Latency: {}\", conn.params.latency);\n                            info!(\n                                \"  Timeout: {} ({}ms)\",\n                                conn.params.supervision_timeout,\n                                conn.params.supervision_timeout as u32 * 10\n                            );\n\n                            state = CentralState::Connected;\n                            info!(\"\");\n                            info!(\"Connection established! As a central, you can now:\");\n                            info!(\"  - Discover services (not implemented in this example)\");\n                            info!(\"  - Read/write characteristics\");\n                            info!(\"  - Subscribe to notifications\");\n                        }\n\n                        GapEvent::Disconnected { handle, reason } => {\n                            error!(\"Connection failed or disconnected during setup\");\n                            error!(\"  Handle: 0x{:04X}, Reason: 0x{:02X}\", handle.0, reason);\n\n                            // Go back to scanning\n                            let mut scanner = ble.scanner();\n                            scanner.start(scan_params.clone()).expect(\"Failed to restart scanning\");\n                            state = CentralState::Scanning;\n                            info!(\"Restarted scanning...\");\n                        }\n\n                        _ => {}\n                    }\n                }\n            }\n\n            CentralState::Connected => {\n                // Handle events while connected\n                if let Some(gap_event) = ble.process_event(&event) {\n                    match gap_event {\n                        GapEvent::Disconnected { handle, reason } => {\n                            info!(\"=== DISCONNECTED ===\");\n                            info!(\"  Handle: 0x{:04X}\", handle.0);\n                            info!(\"  Reason: 0x{:02X} ({})\", reason, disconnect_reason_str(reason));\n\n                            // Go back to scanning\n                            let mut scanner = ble.scanner();\n                            scanner.start(scan_params.clone()).expect(\"Failed to restart scanning\");\n                            state = CentralState::Scanning;\n                            info!(\"Restarted scanning...\");\n                        }\n\n                        GapEvent::ConnectionParamsUpdated {\n                            handle,\n                            interval,\n                            latency,\n                            supervision_timeout,\n                        } => {\n                            info!(\"=== CONNECTION PARAMS UPDATED ===\");\n                            info!(\"  Handle: 0x{:04X}\", handle.0);\n                            info!(\"  New Interval: {} ({}ms)\", interval, (interval as u32 * 125) / 100);\n                            info!(\"  New Latency: {}\", latency);\n                            info!(\n                                \"  New Timeout: {} ({}ms)\",\n                                supervision_timeout,\n                                supervision_timeout as u32 * 10\n                            );\n                        }\n\n                        GapEvent::PhyUpdated { handle, tx_phy, rx_phy } => {\n                            info!(\"=== PHY UPDATED ===\");\n                            info!(\"  Handle: 0x{:04X}\", handle.0);\n                            info!(\"  TX PHY: {:?}\", tx_phy);\n                            info!(\"  RX PHY: {:?}\", rx_phy);\n                        }\n\n                        GapEvent::DataLengthChanged {\n                            handle,\n                            max_tx_octets,\n                            max_rx_octets,\n                            ..\n                        } => {\n                            info!(\"=== DATA LENGTH CHANGED ===\");\n                            info!(\"  Handle: 0x{:04X}\", handle.0);\n                            info!(\"  Max TX: {} bytes\", max_tx_octets);\n                            info!(\"  Max RX: {} bytes\", max_rx_octets);\n                        }\n\n                        _ => {}\n                    }\n                }\n\n                // Log other interesting events\n                match &event.params {\n                    EventParams::AttExchangeMtuResponse {\n                        conn_handle,\n                        server_mtu,\n                    } => {\n                        info!(\"MTU Exchange: conn 0x{:04X}, MTU={}\", conn_handle.0, server_mtu);\n                    }\n                    _ => {}\n                }\n            }\n        }\n    }\n}\n\n/// State machine for central role\n#[derive(Debug, Clone, Copy, PartialEq, Eq)]\nenum CentralState {\n    /// Scanning for devices\n    Scanning,\n    /// Connection initiated, waiting for connection complete\n    Connecting,\n    /// Connected to a peripheral\n    Connected,\n}\n\n/// Convert disconnect reason code to human-readable string\nfn disconnect_reason_str(reason: u8) -> &'static str {\n    match reason {\n        0x08 => \"Connection Timeout\",\n        0x13 => \"Remote User Terminated\",\n        0x14 => \"Remote Low Resources\",\n        0x15 => \"Remote Power Off\",\n        0x16 => \"Local Host Terminated\",\n        0x1A => \"Unsupported Remote Feature\",\n        0x3B => \"Unacceptable Connection Parameters\",\n        0x3D => \"MIC Failure\",\n        0x3E => \"Connection Failed to Establish\",\n        _ => \"Unknown\",\n    }\n}\n"
  },
  {
    "path": "examples/stm32wba6/src/bin/ble_gatt_server.rs",
    "content": "//! BLE GATT Server Example with Notifications\n//!\n//! This example demonstrates full GATT server functionality:\n//! - Creates a custom service with read/write/notify characteristics\n//! - Handles characteristic writes from clients\n//! - Sends notifications when values change\n//! - Tracks CCCD (notification enable/disable) state\n//!\n//! Hardware: STM32WBA65 or compatible\n//!\n//! To test:\n//! 1. Flash this example to your STM32WBA6 board\n//! 2. Connect with nRF Connect or similar app\n//! 3. Enable notifications on the characteristic\n//! 4. Write values to the characteristic\n//! 5. Observe notifications being sent back\n\n#![no_std]\n#![no_main]\n\nuse core::cell::RefCell;\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_stm32::peripherals::RNG;\nuse embassy_stm32::rcc::{\n    AHB5Prescaler, AHBPrescaler, APBPrescaler, PllDiv, PllMul, PllPreDiv, PllSource, Sysclk, VoltageScale, mux,\n};\nuse embassy_stm32::rng::{self, Rng};\nuse embassy_stm32::{Config, bind_interrupts};\nuse embassy_stm32_wpan::gap::{AdvData, AdvParams, AdvType, GapEvent};\nuse embassy_stm32_wpan::gatt::{\n    CHAR_VALUE_HANDLE_OFFSET, CccdValue, CharProperties, CharacteristicHandle, GattEventMask, GattServer,\n    SecurityPermissions, ServiceHandle, ServiceType, Uuid, is_cccd_handle, is_value_handle,\n};\nuse embassy_stm32_wpan::hci::event::EventParams;\nuse embassy_stm32_wpan::{Ble, ble_runner};\nuse embassy_sync::blocking_mutex::Mutex;\nuse embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;\nuse static_cell::StaticCell;\nuse {defmt_rtt as _, panic_probe as _};\n\nbind_interrupts!(struct Irqs {\n    RNG => rng::InterruptHandler<embassy_stm32::peripherals::RNG>;\n});\n\n/// BLE runner task - drives the BLE stack sequencer\n#[embassy_executor::task]\nasync fn ble_runner_task() {\n    ble_runner().await\n}\n\n/// Custom service UUID (use your own for production)\nconst CUSTOM_SERVICE_UUID: u16 = 0xABCD;\n/// Read/Write/Notify characteristic UUID\nconst DATA_CHAR_UUID: u16 = 0xABCE;\n\n/// Application state for tracking GATT handles and CCCD state\nstruct AppState {\n    service_handle: ServiceHandle,\n    data_char_handle: CharacteristicHandle,\n    notifications_enabled: bool,\n    current_conn_handle: Option<u16>,\n    counter: u8,\n}\n\n#[embassy_executor::main]\nasync fn main(spawner: Spawner) {\n    let mut config = Config::default();\n\n    // Configure PLL1 (required on WBA)\n    config.rcc.pll1 = Some(embassy_stm32::rcc::Pll {\n        source: PllSource::HSI,\n        prediv: PllPreDiv::DIV1,\n        mul: PllMul::MUL30,\n        divr: Some(PllDiv::DIV5),\n        divq: None,\n        divp: Some(PllDiv::DIV30),\n        frac: Some(0),\n    });\n\n    config.rcc.ahb_pre = AHBPrescaler::DIV1;\n    config.rcc.apb1_pre = APBPrescaler::DIV1;\n    config.rcc.apb2_pre = APBPrescaler::DIV1;\n    config.rcc.apb7_pre = APBPrescaler::DIV1;\n    config.rcc.ahb5_pre = AHB5Prescaler::DIV4;\n    config.rcc.voltage_scale = VoltageScale::RANGE1;\n    config.rcc.sys = Sysclk::PLL1_R;\n    config.rcc.mux.rngsel = mux::Rngsel::HSI;\n\n    let p = embassy_stm32::init(config);\n    info!(\"Embassy STM32WBA6 GATT Server Example\");\n\n    // Initialize RNG (required by BLE stack)\n    static RNG: StaticCell<Mutex<CriticalSectionRawMutex, RefCell<Rng<'static, RNG>>>> = StaticCell::new();\n    let rng = RNG.init(Mutex::new(RefCell::new(Rng::new(p.RNG, Irqs))));\n    info!(\"RNG initialized\");\n\n    // Initialize BLE stack\n    let mut ble = Ble::new(rng);\n    ble.init().expect(\"BLE initialization failed\");\n    info!(\"BLE stack initialized\");\n\n    // Spawn the BLE runner task (required for proper BLE operation)\n    spawner.spawn(ble_runner_task().unwrap());\n\n    // Initialize GATT server\n    let mut gatt = GattServer::new();\n    gatt.init().expect(\"GATT initialization failed\");\n\n    // Create custom service\n    let service_uuid = Uuid::from_u16(CUSTOM_SERVICE_UUID);\n    let service_handle = gatt\n        .add_service(service_uuid, ServiceType::Primary, 6)\n        .expect(\"Failed to add service\");\n    info!(\"Service created: handle 0x{:04X}\", service_handle.0);\n\n    // Add data characteristic with read/write/notify\n    let char_uuid = Uuid::from_u16(DATA_CHAR_UUID);\n    let char_properties = CharProperties::READ | CharProperties::WRITE | CharProperties::NOTIFY;\n    let data_char_handle = gatt\n        .add_characteristic(\n            service_handle,\n            char_uuid,\n            32, // Max 32 bytes\n            char_properties,\n            SecurityPermissions::NONE,\n            GattEventMask::ATTRIBUTE_MODIFIED,\n            0,\n            true, // Variable length\n        )\n        .expect(\"Failed to add characteristic\");\n    info!(\"Characteristic created: handle 0x{:04X}\", data_char_handle.0);\n    info!(\n        \"  Value handle: 0x{:04X}\",\n        data_char_handle.0 + CHAR_VALUE_HANDLE_OFFSET\n    );\n    info!(\"  CCCD handle: 0x{:04X}\", data_char_handle.0 + 2);\n\n    // Set initial value\n    let initial_value = b\"Hello!\";\n    gatt.update_characteristic_value(service_handle, data_char_handle, 0, initial_value)\n        .expect(\"Failed to set initial value\");\n\n    // Application state\n    let mut state = AppState {\n        service_handle,\n        data_char_handle,\n        notifications_enabled: false,\n        current_conn_handle: None,\n        counter: 0,\n    };\n\n    // Create advertising data\n    let mut adv_data = AdvData::new();\n    adv_data.add_flags(0x06).expect(\"Failed to add flags\");\n    adv_data.add_name(\"Embassy-GATT\").expect(\"Failed to add name\");\n    adv_data\n        .add_service_uuid_16(CUSTOM_SERVICE_UUID)\n        .expect(\"Failed to add service UUID\");\n\n    let adv_params = AdvParams {\n        interval_min: 0x0050,\n        interval_max: 0x0050,\n        adv_type: AdvType::ConnectableUndirected,\n        ..AdvParams::default()\n    };\n\n    // Start advertising\n    {\n        let mut advertiser = ble.advertiser();\n        advertiser\n            .start(adv_params.clone(), adv_data.clone(), None)\n            .expect(\"Failed to start advertising\");\n    }\n\n    info!(\"GATT Server started as 'Embassy-GATT'\");\n    info!(\"Waiting for connections...\");\n\n    // Main event loop\n    loop {\n        let event = ble.read_event().await;\n\n        // Process GAP events (connections)\n        if let Some(gap_event) = ble.process_event(&event) {\n            match gap_event {\n                GapEvent::Connected(conn) => {\n                    info!(\"Connected: handle 0x{:04X}\", conn.handle.0);\n                    state.current_conn_handle = Some(conn.handle.0);\n                    state.notifications_enabled = false; // Reset on new connection\n                }\n                GapEvent::Disconnected { handle, reason } => {\n                    info!(\"Disconnected: handle 0x{:04X}, reason 0x{:02X}\", handle.0, reason);\n                    state.current_conn_handle = None;\n                    state.notifications_enabled = false;\n\n                    // Restart advertising\n                    let mut advertiser = ble.advertiser();\n                    let _ = advertiser.start(adv_params.clone(), adv_data.clone(), None);\n                    info!(\"Advertising restarted\");\n                }\n                _ => {}\n            }\n        }\n\n        // Process GATT events\n        match &event.params {\n            EventParams::GattAttributeModified {\n                conn_handle,\n                attr_handle,\n                data,\n                ..\n            } => {\n                info!(\n                    \"Attribute modified: conn 0x{:04X}, attr 0x{:04X}, {} bytes\",\n                    conn_handle.0,\n                    attr_handle,\n                    data.len()\n                );\n\n                // Check if this is a CCCD write (notification enable/disable)\n                if is_cccd_handle(state.data_char_handle.0, *attr_handle) {\n                    let cccd = CccdValue::from_bytes(data);\n                    state.notifications_enabled = cccd.notifications;\n                    info!(\n                        \"CCCD updated: notifications={}, indications={}\",\n                        cccd.notifications, cccd.indications\n                    );\n\n                    if state.notifications_enabled {\n                        info!(\"Notifications ENABLED - will send updates\");\n                    } else {\n                        info!(\"Notifications DISABLED\");\n                    }\n                }\n                // Check if this is a characteristic value write\n                else if is_value_handle(state.data_char_handle.0, *attr_handle) {\n                    info!(\"Characteristic value written: {:?}\", data.as_slice());\n\n                    // Echo the data back as a notification if enabled\n                    if state.notifications_enabled {\n                        if let Some(conn) = state.current_conn_handle {\n                            // Increment counter and append to response\n                            state.counter = state.counter.wrapping_add(1);\n                            let mut response: heapless::Vec<u8, 33> = heapless::Vec::new();\n                            let _ = response.extend_from_slice(data);\n                            let _ = response.push(state.counter);\n\n                            match gatt.notify(conn, state.service_handle, state.data_char_handle, &response) {\n                                Ok(()) => {\n                                    info!(\"Notification sent: {} bytes\", response.len());\n                                }\n                                Err(e) => {\n                                    error!(\"Failed to send notification: {:?}\", e);\n                                }\n                            }\n                        }\n                    }\n                }\n            }\n\n            EventParams::GattNotificationComplete {\n                conn_handle,\n                attr_handle,\n            } => {\n                info!(\n                    \"Notification complete: conn 0x{:04X}, attr 0x{:04X}\",\n                    conn_handle.0, attr_handle\n                );\n            }\n\n            EventParams::AttExchangeMtuResponse {\n                conn_handle,\n                server_mtu,\n            } => {\n                info!(\"MTU exchanged: conn 0x{:04X}, MTU={}\", conn_handle.0, server_mtu);\n                // Update connection MTU\n                if let Some(conn) = ble.get_connection_mut(*conn_handle) {\n                    conn.update_mtu(*server_mtu);\n                }\n            }\n\n            _ => {\n                // Log other events at debug level\n                debug!(\"Event: {:?}\", event);\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "examples/stm32wba6/src/bin/ble_peripheral_connect.rs",
    "content": "//! BLE Peripheral with Connection Handling Example\n//!\n//! This example demonstrates BLE connection management:\n//! - Advertises as a connectable peripheral\n//! - Handles connection and disconnection events\n//! - Tracks active connections\n//! - Allows disconnection via button press (if available)\n//!\n//! Hardware: STM32WBA65 or compatible\n//!\n//! To test:\n//! 1. Flash this example to your STM32WBA6 board\n//! 2. Use a BLE scanner app (nRF Connect, LightBlue, etc.)\n//! 3. Connect to \"Embassy-Peripheral\"\n//! 4. Observe connection/disconnection events in logs\n\n#![no_std]\n#![no_main]\n\nuse core::cell::RefCell;\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_stm32::peripherals::RNG;\nuse embassy_stm32::rcc::{\n    AHB5Prescaler, AHBPrescaler, APBPrescaler, Hse, HsePrescaler, PllDiv, PllMul, PllPreDiv, PllSource, Sysclk,\n    VoltageScale, mux,\n};\nuse embassy_stm32::rng::{self, Rng};\nuse embassy_stm32::{Config, bind_interrupts, interrupt};\nuse embassy_stm32_wpan::gap::{AdvData, AdvParams, AdvType, GapEvent};\nuse embassy_stm32_wpan::gatt::{CharProperties, GattEventMask, GattServer, SecurityPermissions, ServiceType, Uuid};\nuse embassy_stm32_wpan::{Ble, ble_runner, run_radio_high_isr, run_radio_sw_low_isr};\nuse embassy_sync::blocking_mutex::Mutex;\nuse embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;\nuse static_cell::StaticCell;\nuse {defmt_rtt as _, panic_probe as _};\n\nbind_interrupts!(struct Irqs {\n    RNG => rng::InterruptHandler<embassy_stm32::peripherals::RNG>;\n});\n\n// RADIO interrupt handler - required for BLE stack operation\n#[interrupt]\nunsafe fn RADIO() {\n    unsafe { run_radio_high_isr() };\n}\n\n// WKUP interrupt handler - used as SW low priority interrupt\n#[interrupt]\nunsafe fn WKUP() {\n    unsafe { run_radio_sw_low_isr() };\n}\n\n/// BLE runner task - drives the BLE stack sequencer\n#[embassy_executor::task]\nasync fn ble_runner_task() {\n    ble_runner().await\n}\n\n#[embassy_executor::main]\nasync fn main(spawner: Spawner) {\n    let mut config = Config::default();\n\n    // Enable HSE (32MHz external crystal) - required for BLE radio timing\n    config.rcc.hse = Some(Hse {\n        prescaler: HsePrescaler::DIV1,\n    });\n\n    // Configure PLL1 from HSE for system clock\n    // HSE = 32MHz (fixed for WBA), using prescaler DIV1 gives 32MHz to PLL\n    config.rcc.pll1 = Some(embassy_stm32::rcc::Pll {\n        source: PllSource::HSE,   // Use HSE as PLL source\n        prediv: PllPreDiv::DIV2,  // 32MHz / 2 = 16MHz to PLL input (must be 4-16MHz)\n        mul: PllMul::MUL12,       // 16MHz * 12 = 192MHz VCO\n        divr: Some(PllDiv::DIV2), // 192MHz / 2 = 96MHz system clock\n        divq: None,\n        divp: Some(PllDiv::DIV12), // 192MHz / 12 = 16MHz for peripherals\n        frac: Some(0),\n    });\n\n    config.rcc.ahb_pre = AHBPrescaler::DIV1;\n    config.rcc.apb1_pre = APBPrescaler::DIV1;\n    config.rcc.apb2_pre = APBPrescaler::DIV1;\n    config.rcc.apb7_pre = APBPrescaler::DIV1;\n    config.rcc.ahb5_pre = AHB5Prescaler::DIV4;\n    config.rcc.voltage_scale = VoltageScale::RANGE1;\n    config.rcc.sys = Sysclk::PLL1_R;\n    config.rcc.mux.rngsel = mux::Rngsel::HSI; // RNG can still use HSI\n\n    let p = embassy_stm32::init(config);\n    info!(\"Embassy STM32WBA6 BLE Peripheral Connection Example\");\n\n    // Configure the radio sleep timer clock source (required for BLE stack)\n    // For STM32WBA65xx: RCC base = 0x4602_0C00, BDCR1 offset = 0x0F0\n    // RADIOSTSEL bits are at position 18-19\n    // Value 0b11 = HSE/1000 (32MHz / 1000 = 32kHz)\n    unsafe {\n        const RCC_BDCR1: *mut u32 = 0x4602_0CF0 as *mut u32;\n        const RADIOSTSEL_MASK: u32 = 0x000C0000; // bits 18-19\n        const RADIOSTSEL_HSE_DIV1000: u32 = 0x000C0000; // 0b11 << 18\n\n        let mut val = core::ptr::read_volatile(RCC_BDCR1);\n        val &= !RADIOSTSEL_MASK; // Clear RADIOSTSEL bits\n        val |= RADIOSTSEL_HSE_DIV1000; // Set to HSE/1000\n        core::ptr::write_volatile(RCC_BDCR1, val);\n    }\n    info!(\"Radio sleep timer clock configured to HSE/1000\");\n\n    // Initialize RNG (required by BLE stack)\n    static RNG: StaticCell<Mutex<CriticalSectionRawMutex, RefCell<Rng<'static, RNG>>>> = StaticCell::new();\n    let rng = RNG.init(Mutex::new(RefCell::new(Rng::new(p.RNG, Irqs))));\n    info!(\"RNG initialized\");\n\n    // Initialize BLE stack\n    let mut ble = Ble::new(rng);\n    ble.init().expect(\"BLE initialization failed\");\n    info!(\"BLE stack initialized\");\n\n    // Spawn the BLE runner task (required for proper BLE operation)\n    spawner.spawn(ble_runner_task().unwrap());\n\n    // Initialize GATT server with a simple service\n    let mut gatt = GattServer::new();\n    gatt.init().expect(\"GATT initialization failed\");\n\n    // Create a simple service for demonstration\n    let service_uuid = Uuid::from_u16(0x180F); // Battery Service UUID\n    let service_handle = gatt\n        .add_service(service_uuid, ServiceType::Primary, 4)\n        .expect(\"Failed to add service\");\n\n    // Add battery level characteristic\n    let char_uuid = Uuid::from_u16(0x2A19); // Battery Level UUID\n    let char_handle = gatt\n        .add_characteristic(\n            service_handle,\n            char_uuid,\n            1, // 1 byte for battery level\n            CharProperties::READ | CharProperties::NOTIFY,\n            SecurityPermissions::NONE,\n            GattEventMask::ATTRIBUTE_MODIFIED,\n            0,\n            false,\n        )\n        .expect(\"Failed to add characteristic\");\n\n    // Set initial battery level (100%)\n    gatt.update_characteristic_value(service_handle, char_handle, 0, &[100])\n        .expect(\"Failed to set battery level\");\n\n    info!(\"GATT service created (Battery Service)\");\n\n    // Create advertising data\n    let mut adv_data = AdvData::new();\n    adv_data.add_flags(0x06).expect(\"Failed to add flags\");\n    adv_data.add_name(\"Embassy-Peripheral\").expect(\"Failed to add name\");\n    adv_data\n        .add_service_uuid_16(0x180F)\n        .expect(\"Failed to add service UUID\");\n\n    // Configure advertising parameters for connectable advertising\n    let adv_params = AdvParams {\n        interval_min: 0x0050, // 50 ms\n        interval_max: 0x0050,\n        adv_type: AdvType::ConnectableUndirected,\n        ..AdvParams::default()\n    };\n\n    // Start advertising\n    {\n        let mut advertiser = ble.advertiser();\n        advertiser\n            .start(adv_params.clone(), adv_data.clone(), None)\n            .expect(\"Failed to start advertising\");\n    }\n\n    info!(\"BLE advertising started as 'Embassy-Peripheral'\");\n    info!(\"Waiting for connections...\");\n\n    // Main event loop\n    loop {\n        let event = ble.read_event().await;\n\n        // Process the event and update connection state\n        if let Some(gap_event) = ble.process_event(&event) {\n            match gap_event {\n                GapEvent::Connected(conn) => {\n                    info!(\"=== CONNECTION ESTABLISHED ===\");\n                    info!(\"  Handle: 0x{:04X}\", conn.handle.0);\n                    info!(\n                        \"  Role: {}\",\n                        match conn.role {\n                            embassy_stm32_wpan::gap::ConnectionRole::Central => \"Central\",\n                            embassy_stm32_wpan::gap::ConnectionRole::Peripheral => \"Peripheral\",\n                        }\n                    );\n                    info!(\n                        \"  Peer Address: {:02X}:{:02X}:{:02X}:{:02X}:{:02X}:{:02X}\",\n                        conn.peer_address.0[5],\n                        conn.peer_address.0[4],\n                        conn.peer_address.0[3],\n                        conn.peer_address.0[2],\n                        conn.peer_address.0[1],\n                        conn.peer_address.0[0]\n                    );\n                    info!(\n                        \"  Interval: {} ({}ms)\",\n                        conn.params.interval,\n                        (conn.params.interval as u32 * 125) / 100\n                    );\n                    info!(\"  Latency: {}\", conn.params.latency);\n                    info!(\n                        \"  Timeout: {} ({}ms)\",\n                        conn.params.supervision_timeout,\n                        conn.params.supervision_timeout as u32 * 10\n                    );\n                    info!(\"  Active connections: {}\", ble.connections().count());\n\n                    // Note: Advertising typically stops automatically on connection\n                    // If you want to support multiple connections, restart advertising here\n                }\n\n                GapEvent::Disconnected { handle, reason } => {\n                    info!(\"=== DISCONNECTION ===\");\n                    info!(\"  Handle: 0x{:04X}\", handle.0);\n                    info!(\"  Reason: 0x{:02X} ({})\", reason, disconnect_reason_str(reason));\n                    info!(\"  Active connections: {}\", ble.connections().count());\n\n                    // Restart advertising after disconnection\n                    info!(\"Restarting advertising...\");\n                    let mut advertiser = ble.advertiser();\n                    if let Err(e) = advertiser.start(adv_params.clone(), adv_data.clone(), None) {\n                        error!(\"Failed to restart advertising: {:?}\", e);\n                    } else {\n                        info!(\"Advertising restarted\");\n                    }\n                }\n\n                GapEvent::ConnectionParamsUpdated {\n                    handle,\n                    interval,\n                    latency,\n                    supervision_timeout,\n                } => {\n                    info!(\"=== CONNECTION PARAMS UPDATED ===\");\n                    info!(\"  Handle: 0x{:04X}\", handle.0);\n                    info!(\"  New Interval: {} ({}ms)\", interval, (interval as u32 * 125) / 100);\n                    info!(\"  New Latency: {}\", latency);\n                    info!(\n                        \"  New Timeout: {} ({}ms)\",\n                        supervision_timeout,\n                        supervision_timeout as u32 * 10\n                    );\n                }\n\n                GapEvent::PhyUpdated { handle, tx_phy, rx_phy } => {\n                    info!(\"=== PHY UPDATED ===\");\n                    info!(\"  Handle: 0x{:04X}\", handle.0);\n                    info!(\"  TX PHY: {:?}\", tx_phy);\n                    info!(\"  RX PHY: {:?}\", rx_phy);\n                }\n\n                GapEvent::DataLengthChanged {\n                    handle,\n                    max_tx_octets,\n                    max_rx_octets,\n                    ..\n                } => {\n                    info!(\"=== DATA LENGTH CHANGED ===\");\n                    info!(\"  Handle: 0x{:04X}\", handle.0);\n                    info!(\"  Max TX: {} bytes\", max_tx_octets);\n                    info!(\"  Max RX: {} bytes\", max_rx_octets);\n                }\n            }\n        } else {\n            // Log other events for debugging\n            debug!(\"Other BLE Event: {:?}\", event);\n        }\n    }\n}\n\n/// Convert disconnect reason code to human-readable string\nfn disconnect_reason_str(reason: u8) -> &'static str {\n    match reason {\n        0x08 => \"Connection Timeout\",\n        0x13 => \"Remote User Terminated\",\n        0x14 => \"Remote Low Resources\",\n        0x15 => \"Remote Power Off\",\n        0x16 => \"Local Host Terminated\",\n        0x1A => \"Unsupported Remote Feature\",\n        0x3B => \"Unacceptable Connection Parameters\",\n        0x3D => \"MIC Failure\",\n        0x3E => \"Connection Failed to Establish\",\n        _ => \"Unknown\",\n    }\n}\n"
  },
  {
    "path": "examples/stm32wba6/src/bin/ble_scanner.rs",
    "content": "//! BLE Scanner Example\n//!\n//! This example demonstrates BLE scanning (observer role):\n//! - Scans for nearby BLE devices\n//! - Parses advertising data (name, UUIDs, manufacturer data)\n//! - Displays discovered devices with RSSI\n//!\n//! Hardware: STM32WBA65 or compatible\n//!\n//! To test:\n//! 1. Flash this example to your STM32WBA6 board\n//! 2. Have nearby BLE devices advertising (phones, beacons, etc.)\n//! 3. Observe discovered devices in the logs\n\n#![no_std]\n#![no_main]\n\nuse core::cell::RefCell;\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_stm32::peripherals::RNG;\nuse embassy_stm32::rcc::{\n    AHB5Prescaler, AHBPrescaler, APBPrescaler, PllDiv, PllMul, PllPreDiv, PllSource, Sysclk, VoltageScale, mux,\n};\nuse embassy_stm32::rng::{self, Rng};\nuse embassy_stm32::{Config, bind_interrupts};\nuse embassy_stm32_wpan::gap::{ParsedAdvData, ScanParams, ScanType};\nuse embassy_stm32_wpan::hci::event::EventParams;\nuse embassy_stm32_wpan::{Ble, ble_runner};\nuse embassy_sync::blocking_mutex::Mutex;\nuse embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;\nuse static_cell::StaticCell;\nuse {defmt_rtt as _, panic_probe as _};\n\nbind_interrupts!(struct Irqs {\n    RNG => rng::InterruptHandler<embassy_stm32::peripherals::RNG>;\n});\n\n/// BLE runner task - drives the BLE stack sequencer\n#[embassy_executor::task]\nasync fn ble_runner_task() {\n    ble_runner().await\n}\n\n#[embassy_executor::main]\nasync fn main(spawner: Spawner) {\n    let mut config = Config::default();\n\n    // Configure PLL1 (required on WBA)\n    config.rcc.pll1 = Some(embassy_stm32::rcc::Pll {\n        source: PllSource::HSI,\n        prediv: PllPreDiv::DIV1,\n        mul: PllMul::MUL30,\n        divr: Some(PllDiv::DIV5),\n        divq: None,\n        divp: Some(PllDiv::DIV30),\n        frac: Some(0),\n    });\n\n    config.rcc.ahb_pre = AHBPrescaler::DIV1;\n    config.rcc.apb1_pre = APBPrescaler::DIV1;\n    config.rcc.apb2_pre = APBPrescaler::DIV1;\n    config.rcc.apb7_pre = APBPrescaler::DIV1;\n    config.rcc.ahb5_pre = AHB5Prescaler::DIV4;\n    config.rcc.voltage_scale = VoltageScale::RANGE1;\n    config.rcc.sys = Sysclk::PLL1_R;\n    config.rcc.mux.rngsel = mux::Rngsel::HSI;\n\n    let p = embassy_stm32::init(config);\n    info!(\"Embassy STM32WBA6 BLE Scanner Example\");\n\n    // Initialize RNG (required by BLE stack)\n    static RNG: StaticCell<Mutex<CriticalSectionRawMutex, RefCell<Rng<'static, RNG>>>> = StaticCell::new();\n    let rng = RNG.init(Mutex::new(RefCell::new(Rng::new(p.RNG, Irqs))));\n    info!(\"RNG initialized\");\n\n    // Initialize BLE stack\n    let mut ble = Ble::new(rng);\n    ble.init().expect(\"BLE initialization failed\");\n    info!(\"BLE stack initialized\");\n\n    // Spawn the BLE runner task (required for proper BLE operation)\n    spawner.spawn(ble_runner_task().unwrap());\n\n    // Configure scan parameters\n    // Using active scanning to get scan response data (device names)\n    let scan_params = ScanParams::new()\n        .with_scan_type(ScanType::Active)\n        .with_interval(0x0050) // 50ms\n        .with_window(0x0030) // 30ms\n        .with_filter_duplicates(true);\n\n    // Start scanning\n    let mut scanner = ble.scanner();\n    scanner.start(scan_params).expect(\"Failed to start scanning\");\n\n    info!(\"=== BLE Scanning Started ===\");\n    info!(\"Looking for nearby devices...\");\n    info!(\"\");\n\n    let mut device_count = 0u32;\n\n    // Main event loop - process advertising reports\n    loop {\n        let event = ble.read_event().await;\n\n        // Check for advertising reports\n        if let EventParams::LeAdvertisingReport { reports } = &event.params {\n            for report in reports.iter() {\n                device_count += 1;\n\n                // Parse the advertising data\n                let parsed = ParsedAdvData::parse(&report.data);\n\n                info!(\"--- Device #{} ---\", device_count);\n\n                // Display device address\n                info!(\n                    \"  Address: {:02X}:{:02X}:{:02X}:{:02X}:{:02X}:{:02X} ({})\",\n                    report.address.0[5],\n                    report.address.0[4],\n                    report.address.0[3],\n                    report.address.0[2],\n                    report.address.0[1],\n                    report.address.0[0],\n                    address_type_str(report.address_type)\n                );\n\n                // Display RSSI\n                info!(\"  RSSI: {} dBm\", report.rssi);\n\n                // Display event type\n                info!(\"  Type: {}\", adv_event_type_str(report.event_type));\n\n                // Display parsed name if available\n                if let Some(name) = parsed.name {\n                    info!(\"  Name: \\\"{}\\\"\", name);\n                }\n\n                // Display flags if available\n                if let Some(flags) = parsed.flags {\n                    info!(\"  Flags: 0x{:02X} ({})\", flags, flags_str(flags));\n                }\n\n                // Display TX power if available\n                if let Some(tx_power) = parsed.tx_power {\n                    info!(\"  TX Power: {} dBm\", tx_power);\n                }\n\n                // Display 16-bit service UUIDs\n                if !parsed.service_uuids_16.is_empty() {\n                    for uuid in parsed.service_uuids_16.iter() {\n                        info!(\"  Service UUID: 0x{:04X} ({})\", uuid, service_uuid_str(*uuid));\n                    }\n                }\n\n                // Display 128-bit service UUIDs\n                if !parsed.service_uuids_128.is_empty() {\n                    for uuid in parsed.service_uuids_128.iter() {\n                        info!(\n                            \"  Service UUID (128): {:02X}{:02X}{:02X}{:02X}-{:02X}{:02X}-{:02X}{:02X}-{:02X}{:02X}-{:02X}{:02X}{:02X}{:02X}{:02X}{:02X}\",\n                            uuid[15],\n                            uuid[14],\n                            uuid[13],\n                            uuid[12],\n                            uuid[11],\n                            uuid[10],\n                            uuid[9],\n                            uuid[8],\n                            uuid[7],\n                            uuid[6],\n                            uuid[5],\n                            uuid[4],\n                            uuid[3],\n                            uuid[2],\n                            uuid[1],\n                            uuid[0]\n                        );\n                    }\n                }\n\n                // Display manufacturer data\n                if let Some((company_id, data)) = parsed.manufacturer_data {\n                    info!(\n                        \"  Manufacturer: 0x{:04X} ({}) - {} bytes\",\n                        company_id,\n                        company_id_str(company_id),\n                        data.len()\n                    );\n                }\n\n                info!(\"\");\n            }\n        }\n    }\n}\n\n/// Convert address type to string\nfn address_type_str(addr_type: embassy_stm32_wpan::hci::types::AddressType) -> &'static str {\n    match addr_type {\n        embassy_stm32_wpan::hci::types::AddressType::Public => \"Public\",\n        embassy_stm32_wpan::hci::types::AddressType::Random => \"Random\",\n        embassy_stm32_wpan::hci::types::AddressType::PublicIdentity => \"Public Identity\",\n        embassy_stm32_wpan::hci::types::AddressType::RandomIdentity => \"Random Identity\",\n    }\n}\n\n/// Convert advertising event type to string\nfn adv_event_type_str(event_type: u8) -> &'static str {\n    match event_type {\n        0x00 => \"Connectable Undirected\",\n        0x01 => \"Connectable Directed\",\n        0x02 => \"Scannable Undirected\",\n        0x03 => \"Non-Connectable Undirected\",\n        0x04 => \"Scan Response\",\n        _ => \"Unknown\",\n    }\n}\n\n/// Convert flags byte to description\nfn flags_str(flags: u8) -> &'static str {\n    match flags {\n        0x06 => \"LE General Discoverable\",\n        0x04 => \"BR/EDR Not Supported\",\n        0x02 => \"LE General Discoverable Only\",\n        0x01 => \"LE Limited Discoverable\",\n        0x05 => \"LE Limited + BR/EDR Not Supported\",\n        _ => \"Other\",\n    }\n}\n\n/// Convert common 16-bit service UUID to name\nfn service_uuid_str(uuid: u16) -> &'static str {\n    match uuid {\n        0x1800 => \"Generic Access\",\n        0x1801 => \"Generic Attribute\",\n        0x180A => \"Device Information\",\n        0x180D => \"Heart Rate\",\n        0x180F => \"Battery\",\n        0x1810 => \"Blood Pressure\",\n        0x1816 => \"Cycling Speed and Cadence\",\n        0x181A => \"Environmental Sensing\",\n        0x181C => \"User Data\",\n        0xFE9F => \"Google Fast Pair\",\n        0xFD6F => \"Apple Exposure Notification\",\n        _ => \"Unknown\",\n    }\n}\n\n/// Convert common company IDs to names\nfn company_id_str(company_id: u16) -> &'static str {\n    match company_id {\n        0x004C => \"Apple\",\n        0x0006 => \"Microsoft\",\n        0x00E0 => \"Google\",\n        0x0075 => \"Samsung\",\n        0x0059 => \"Nordic Semiconductor\",\n        0x0030 => \"STMicroelectronics\",\n        0x00D2 => \"Huawei\",\n        0x038F => \"Xiaomi\",\n        _ => \"Unknown\",\n    }\n}\n"
  },
  {
    "path": "examples/stm32wba6/src/bin/ble_secure.rs",
    "content": "//! BLE Secure Peripheral Example\n//!\n//! This example demonstrates BLE security features:\n//! - Configures security parameters (bonding, MITM protection)\n//! - Handles pairing requests and passkey entry\n//! - Supports numeric comparison for Secure Connections\n//! - Demonstrates bond management\n//!\n//! Hardware: STM32WBA65 or compatible\n//!\n//! To test:\n//! 1. Flash this example to your STM32WBA6 board\n//! 2. Connect with nRF Connect or similar app\n//! 3. Initiate pairing from the app\n//! 4. Observe pairing events in the logs\n//! 5. For passkey entry: enter the displayed passkey on your phone\n//! 6. For numeric comparison: confirm the displayed numbers match\n\n#![no_std]\n#![no_main]\n\nuse core::cell::RefCell;\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_stm32::peripherals::RNG;\nuse embassy_stm32::rcc::{\n    AHB5Prescaler, AHBPrescaler, APBPrescaler, PllDiv, PllMul, PllPreDiv, PllSource, Sysclk, VoltageScale, mux,\n};\nuse embassy_stm32::rng::{self, Rng};\nuse embassy_stm32::{Config, bind_interrupts};\nuse embassy_stm32_wpan::gap::{AdvData, AdvParams, AdvType, GapEvent};\nuse embassy_stm32_wpan::gatt::{CharProperties, GattEventMask, GattServer, SecurityPermissions, ServiceType, Uuid};\nuse embassy_stm32_wpan::hci::event::EventParams;\nuse embassy_stm32_wpan::security::{\n    PairingFailureReason, PairingStatus, SecureConnectionsSupport, SecurityManager, SecurityParams,\n};\nuse embassy_stm32_wpan::{Ble, ble_runner};\nuse embassy_sync::blocking_mutex::Mutex;\nuse embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;\nuse static_cell::StaticCell;\nuse {defmt_rtt as _, panic_probe as _};\n\nbind_interrupts!(struct Irqs {\n    RNG => rng::InterruptHandler<embassy_stm32::peripherals::RNG>;\n});\n\n/// Custom service UUID\nconst SECURE_SERVICE_UUID: u16 = 0xABCD;\n/// Characteristic that requires encryption\nconst SECURE_CHAR_UUID: u16 = 0xABCE;\n\n/// BLE runner task - drives the BLE stack sequencer\n#[embassy_executor::task]\nasync fn ble_runner_task() {\n    ble_runner().await\n}\n\n#[embassy_executor::main]\nasync fn main(spawner: Spawner) {\n    let mut config = Config::default();\n\n    // Configure PLL1 (required on WBA)\n    config.rcc.pll1 = Some(embassy_stm32::rcc::Pll {\n        source: PllSource::HSI,\n        prediv: PllPreDiv::DIV1,\n        mul: PllMul::MUL30,\n        divr: Some(PllDiv::DIV5),\n        divq: None,\n        divp: Some(PllDiv::DIV30),\n        frac: Some(0),\n    });\n\n    config.rcc.ahb_pre = AHBPrescaler::DIV1;\n    config.rcc.apb1_pre = APBPrescaler::DIV1;\n    config.rcc.apb2_pre = APBPrescaler::DIV1;\n    config.rcc.apb7_pre = APBPrescaler::DIV1;\n    config.rcc.ahb5_pre = AHB5Prescaler::DIV4;\n    config.rcc.voltage_scale = VoltageScale::RANGE1;\n    config.rcc.sys = Sysclk::PLL1_R;\n    config.rcc.mux.rngsel = mux::Rngsel::HSI;\n\n    let p = embassy_stm32::init(config);\n    info!(\"Embassy STM32WBA6 BLE Secure Peripheral Example\");\n\n    // Initialize RNG (required by BLE stack)\n    static RNG: StaticCell<Mutex<CriticalSectionRawMutex, RefCell<Rng<'static, RNG>>>> = StaticCell::new();\n    let rng = RNG.init(Mutex::new(RefCell::new(Rng::new(p.RNG, Irqs))));\n    info!(\"RNG initialized\");\n\n    // Initialize BLE stack\n    let mut ble = Ble::new(rng);\n    ble.init().expect(\"BLE initialization failed\");\n    info!(\"BLE stack initialized\");\n\n    // Spawn the BLE runner task (required for proper BLE operation)\n    spawner.spawn(ble_runner_task().unwrap());\n\n    // ===== Configure Security =====\n    let mut security = SecurityManager::new();\n\n    // Configure security parameters:\n    // - Enable bonding (store keys)\n    // - Require MITM protection (passkey entry or numeric comparison)\n    // - Support Secure Connections (LE Secure Connections pairing)\n    let security_params = SecurityParams::new()\n        .with_bonding(true)\n        .with_mitm_protection(true)\n        .with_secure_connections(SecureConnectionsSupport::Optional)\n        .with_key_size_range(7, 16);\n\n    security\n        .set_authentication_requirements(security_params)\n        .expect(\"Failed to set security parameters\");\n    info!(\"Security configured: Bonding + MITM + SC\");\n\n    // Enable address resolution (for bonded devices using RPA)\n    security\n        .set_address_resolution_enable(true)\n        .expect(\"Failed to enable address resolution\");\n\n    // Initialize GATT server\n    let mut gatt = GattServer::new();\n    gatt.init().expect(\"GATT initialization failed\");\n\n    // Create a service with a secure characteristic\n    let service_uuid = Uuid::from_u16(SECURE_SERVICE_UUID);\n    let service_handle = gatt\n        .add_service(service_uuid, ServiceType::Primary, 4)\n        .expect(\"Failed to add service\");\n\n    // Add characteristic that requires authenticated encryption\n    let char_uuid = Uuid::from_u16(SECURE_CHAR_UUID);\n    let char_handle = gatt\n        .add_characteristic(\n            service_handle,\n            char_uuid,\n            20,\n            CharProperties::READ | CharProperties::WRITE,\n            SecurityPermissions::AUTHEN_READ | SecurityPermissions::AUTHEN_WRITE,\n            GattEventMask::ATTRIBUTE_MODIFIED,\n            0,\n            true,\n        )\n        .expect(\"Failed to add characteristic\");\n\n    // Set initial value\n    gatt.update_characteristic_value(service_handle, char_handle, 0, b\"Secure!\")\n        .expect(\"Failed to set value\");\n\n    info!(\"GATT service created with secure characteristic\");\n\n    // Advertising parameters (reused for restarts)\n    let adv_params = AdvParams {\n        interval_min: 0x0050,\n        interval_max: 0x0050,\n        adv_type: AdvType::ConnectableUndirected,\n        ..AdvParams::default()\n    };\n\n    // Helper to create advertising data\n    fn create_adv_data() -> AdvData {\n        let mut adv_data = AdvData::new();\n        adv_data.add_flags(0x06).expect(\"Failed to add flags\");\n        adv_data.add_name(\"Embassy-Secure\").expect(\"Failed to add name\");\n        adv_data\n            .add_service_uuid_16(SECURE_SERVICE_UUID)\n            .expect(\"Failed to add UUID\");\n        adv_data\n    }\n\n    // Start advertising\n    {\n        let mut advertiser = ble.advertiser();\n        advertiser\n            .start(adv_params.clone(), create_adv_data(), None)\n            .expect(\"Failed to start advertising\");\n    }\n\n    info!(\"=== BLE Secure Peripheral Started ===\");\n    info!(\"Device name: 'Embassy-Secure'\");\n    info!(\"Connect and pair to access secure characteristic\");\n    info!(\"\");\n\n    // Main event loop\n    loop {\n        let event = ble.read_event().await;\n\n        // Process GAP events (connections)\n        if let Some(gap_event) = ble.process_event(&event) {\n            match gap_event {\n                GapEvent::Connected(conn) => {\n                    info!(\"=== CONNECTED ===\");\n                    info!(\"  Handle: 0x{:04X}\", conn.handle.0);\n                    info!(\n                        \"  Peer: {:02X}:{:02X}:{:02X}:{:02X}:{:02X}:{:02X}\",\n                        conn.peer_address.0[5],\n                        conn.peer_address.0[4],\n                        conn.peer_address.0[3],\n                        conn.peer_address.0[2],\n                        conn.peer_address.0[1],\n                        conn.peer_address.0[0]\n                    );\n\n                    info!(\"Waiting for pairing request...\");\n                    info!(\"(Try to read the secure characteristic to trigger pairing)\");\n                }\n\n                GapEvent::Disconnected { handle, reason } => {\n                    info!(\"=== DISCONNECTED ===\");\n                    info!(\"  Handle: 0x{:04X}, Reason: 0x{:02X}\", handle.0, reason);\n\n                    // Restart advertising\n                    let mut advertiser = ble.advertiser();\n                    let _ = advertiser.start(adv_params.clone(), create_adv_data(), None);\n                    info!(\"Advertising restarted\");\n                }\n\n                _ => {}\n            }\n        }\n\n        // Process security events\n        match &event.params {\n            EventParams::GapPairingComplete {\n                conn_handle,\n                status,\n                reason,\n            } => {\n                let pairing_status = PairingStatus::from_u8(*status);\n                info!(\"=== PAIRING COMPLETE ===\");\n                info!(\"  Connection: 0x{:04X}\", conn_handle.0);\n\n                match pairing_status {\n                    PairingStatus::Success => {\n                        info!(\"  Status: SUCCESS\");\n                        info!(\"  Device is now bonded and can access secure characteristics\");\n                    }\n                    PairingStatus::Timeout => {\n                        info!(\"  Status: TIMEOUT\");\n                        info!(\"  Pairing timed out - please try again\");\n                    }\n                    PairingStatus::Failed => {\n                        let failure_reason = PairingFailureReason::from_u8(*reason);\n                        info!(\"  Status: FAILED\");\n                        info!(\"  Reason: 0x{:02X} ({})\", reason, failure_reason.description());\n                    }\n                }\n            }\n\n            EventParams::GapPasskeyRequest { conn_handle } => {\n                info!(\"=== PASSKEY REQUEST ===\");\n                info!(\"  Connection: 0x{:04X}\", conn_handle.0);\n\n                // Generate a random passkey (in production, display this to user)\n                // For this example, we use a fixed passkey\n                let passkey: u32 = 123456;\n                info!(\"  Passkey: {:06}\", passkey);\n                info!(\"  Enter this passkey on your phone/device!\");\n\n                if let Err(e) = security.pass_key_response(conn_handle.as_u16(), passkey) {\n                    error!(\"Failed to send passkey response: {:?}\", e);\n                }\n            }\n\n            EventParams::GapNumericComparisonRequest {\n                conn_handle,\n                numeric_value,\n            } => {\n                info!(\"=== NUMERIC COMPARISON ===\");\n                info!(\"  Connection: 0x{:04X}\", conn_handle.0);\n                info!(\"  Displayed value: {:06}\", numeric_value);\n                info!(\"  Confirm this matches the value on your phone!\");\n\n                // Auto-confirm for this example (in production, wait for user input)\n                // Set to true to accept, false to reject\n                let confirm = true;\n                info!(\"  Auto-confirming: {}\", if confirm { \"YES\" } else { \"NO\" });\n\n                if let Err(e) = security.numeric_comparison_response(conn_handle.as_u16(), confirm) {\n                    error!(\"Failed to send numeric comparison response: {:?}\", e);\n                }\n            }\n\n            EventParams::GapBondLost { conn_handle } => {\n                info!(\"=== BOND LOST ===\");\n                info!(\"  Connection: 0x{:04X}\", conn_handle.0);\n                info!(\"  Previous bond invalid, allowing rebond...\");\n\n                if let Err(e) = security.allow_rebond(conn_handle.as_u16()) {\n                    error!(\"Failed to allow rebond: {:?}\", e);\n                }\n            }\n\n            EventParams::GapPairingRequest { conn_handle, is_bonded } => {\n                info!(\"=== PAIRING REQUEST ===\");\n                info!(\"  Connection: 0x{:04X}\", conn_handle.0);\n                info!(\"  Previously bonded: {}\", is_bonded);\n                // The stack will handle the pairing process automatically\n            }\n\n            EventParams::GattAttributeModified {\n                conn_handle,\n                attr_handle,\n                data,\n                ..\n            } => {\n                info!(\"=== SECURE WRITE RECEIVED ===\");\n                info!(\"  Connection: 0x{:04X}, Attr: 0x{:04X}\", conn_handle.0, attr_handle);\n                info!(\"  Data ({} bytes): {:?}\", data.len(), data.as_slice());\n                info!(\"  (This write succeeded because device is paired!)\");\n            }\n\n            EventParams::EncryptionChange {\n                status,\n                handle,\n                enabled,\n                ..\n            } => {\n                info!(\"=== ENCRYPTION CHANGE ===\");\n                info!(\"  Connection: 0x{:04X}\", handle.0);\n                info!(\"  Status: {:?}\", status);\n                info!(\"  Encrypted: {}\", enabled);\n            }\n\n            _ => {\n                // Log other events at debug level\n                debug!(\"Event: {:?}\", event);\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "examples/stm32wba6/src/bin/blinky.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_stm32::Config;\nuse embassy_stm32::gpio::{Level, Output, Speed};\nuse embassy_stm32::rcc::{\n    AHB5Prescaler, AHBPrescaler, APBPrescaler, PllDiv, PllMul, PllPreDiv, PllSource, Sysclk, VoltageScale,\n};\nuse embassy_time::Timer;\nuse {defmt_rtt as _, panic_probe as _};\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let mut config = Config::default();\n    // Fine-tune PLL1 dividers/multipliers\n    config.rcc.pll1 = Some(embassy_stm32::rcc::Pll {\n        source: PllSource::HSI,\n        prediv: PllPreDiv::DIV1,  // PLLM = 1 → HSI / 1 = 16 MHz\n        mul: PllMul::MUL30,       // PLLN = 30 → 16 MHz * 30 = 480 MHz VCO\n        divr: Some(PllDiv::DIV5), // PLLR = 5 → 96 MHz (Sysclk)\n        // divq: Some(PllDiv::DIV10), // PLLQ = 10 → 48 MHz (NOT USED)\n        divq: None,\n        divp: Some(PllDiv::DIV30), // PLLP = 30 → 16 MHz (USBOTG)\n        frac: Some(0),             // Fractional part (enabled)\n    });\n\n    config.rcc.ahb_pre = AHBPrescaler::DIV1;\n    config.rcc.apb1_pre = APBPrescaler::DIV1;\n    config.rcc.apb2_pre = APBPrescaler::DIV1;\n    config.rcc.apb7_pre = APBPrescaler::DIV1;\n    config.rcc.ahb5_pre = AHB5Prescaler::DIV4;\n\n    // voltage scale for max performance\n    config.rcc.voltage_scale = VoltageScale::RANGE1;\n    // route PLL1_P into the USB‐OTG‐HS block\n    config.rcc.sys = Sysclk::PLL1_R;\n\n    let p = embassy_stm32::init(config);\n    info!(\"Hello World!\");\n\n    // LD2 - PC4\n    let mut led = Output::new(p.PC4, Level::High, Speed::Low);\n\n    loop {\n        info!(\"high\");\n        led.set_high();\n        Timer::after_millis(500).await;\n\n        info!(\"low\");\n        led.set_low();\n        Timer::after_millis(500).await;\n    }\n}\n"
  },
  {
    "path": "examples/stm32wba6/src/bin/button_exti.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_stm32::exti::{self, ExtiInput};\nuse embassy_stm32::gpio::Pull;\nuse embassy_stm32::{bind_interrupts, interrupt};\nuse {defmt_rtt as _, panic_probe as _};\n\nbind_interrupts!(\n    pub struct Irqs{\n        EXTI13 => exti::InterruptHandler<interrupt::typelevel::EXTI13>;\n});\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let p = embassy_stm32::init(Default::default());\n    info!(\"Hello World!\");\n\n    let mut button = ExtiInput::new(p.PC13, p.EXTI13, Pull::Up, Irqs);\n\n    info!(\"Press the USER button...\");\n\n    loop {\n        button.wait_for_falling_edge().await;\n        info!(\"Pressed!\");\n        button.wait_for_rising_edge().await;\n        info!(\"Released!\");\n    }\n}\n"
  },
  {
    "path": "examples/stm32wba6/src/bin/comp.rs",
    "content": "//! Comparator (COMP) example for STM32WBA6.\n//!\n//! This example demonstrates how to use the comparator peripheral to compare\n//! an analog input voltage against an internal reference (half Vref).\n//!\n//! Hardware setup:\n//! - Connect a variable voltage (0-3.3V) to PA2 (COMP1 INP)\n//! - The comparator will compare PA2 against 1/2 Vref (~1.65V)\n//!\n//! The example shows:\n//! - Polling the comparator output level\n//! - Using async edge detection to wait for transitions\n\n#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_stm32::comp::{Comp, Config, Hysteresis, InvertingInput, OutputPolarity, PowerMode};\nuse embassy_stm32::rcc::{\n    AHB5Prescaler, AHBPrescaler, APBPrescaler, PllDiv, PllMul, PllPreDiv, PllSource, Sysclk, VoltageScale,\n};\nuse embassy_stm32::{bind_interrupts, comp};\nuse embassy_time::Timer;\nuse {defmt_rtt as _, panic_probe as _};\n\nbind_interrupts!(struct Irqs {\n    COMP => comp::InterruptHandler<embassy_stm32::peripherals::COMP1>;\n});\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let mut config = embassy_stm32::Config::default();\n\n    // Configure PLL for system clock\n    config.rcc.pll1 = Some(embassy_stm32::rcc::Pll {\n        source: PllSource::HSI,\n        prediv: PllPreDiv::DIV1,\n        mul: PllMul::MUL30,\n        divr: Some(PllDiv::DIV5),\n        divq: None,\n        divp: Some(PllDiv::DIV30),\n        frac: Some(0),\n    });\n\n    config.rcc.ahb_pre = AHBPrescaler::DIV1;\n    config.rcc.apb1_pre = APBPrescaler::DIV1;\n    config.rcc.apb2_pre = APBPrescaler::DIV1;\n    config.rcc.apb7_pre = APBPrescaler::DIV1;\n    config.rcc.ahb5_pre = AHB5Prescaler::DIV4;\n    config.rcc.voltage_scale = VoltageScale::RANGE1;\n    config.rcc.sys = Sysclk::PLL1_R;\n\n    let p = embassy_stm32::init(config);\n    info!(\"COMP example starting!\");\n\n    // Configure comparator:\n    // - Non-inverting input (INP): PA2\n    // - Inverting input (INM): Internal reference (1/2 Vref)\n    // - High speed power mode\n    // - Low hysteresis for noise immunity\n    let comp_config = Config {\n        power_mode: PowerMode::HighSpeed,\n        hysteresis: Hysteresis::Low,\n        output_polarity: OutputPolarity::NotInverted,\n        inverting_input: InvertingInput::HalfVref,\n        ..Default::default()\n    };\n\n    let mut comp1 = Comp::new(p.COMP1, p.PA2, Irqs, comp_config);\n\n    info!(\"Comparator configured. Comparing PA2 against 1/2 Vref (~1.65V)\");\n    info!(\"Output HIGH when PA2 > 1.65V, LOW when PA2 < 1.65V\");\n\n    // Enable the comparator\n    comp1.enable();\n\n    // Polling example: read the comparator output every second\n    info!(\"Starting polling mode - reading comparator output every second...\");\n    for _ in 0..5 {\n        let output = comp1.output_level();\n        if output {\n            info!(\"Comparator output: HIGH (PA2 > 1/2 Vref)\");\n        } else {\n            info!(\"Comparator output: LOW (PA2 < 1/2 Vref)\");\n        }\n        Timer::after_millis(1000).await;\n    }\n\n    // Async edge detection example\n    info!(\"Switching to async mode - waiting for edges...\");\n\n    loop {\n        info!(\"Waiting for rising edge (PA2 going above 1/2 Vref)...\");\n        comp1.wait_for_rising_edge().await;\n        info!(\"Rising edge detected!\");\n\n        // Small delay to debounce\n        Timer::after_millis(100).await;\n\n        info!(\"Waiting for falling edge (PA2 going below 1/2 Vref)...\");\n        comp1.wait_for_falling_edge().await;\n        info!(\"Falling edge detected!\");\n\n        // Small delay to debounce\n        Timer::after_millis(100).await;\n    }\n}\n"
  },
  {
    "path": "examples/stm32wba6/src/bin/comp_window.rs",
    "content": "//! Comparator Window Mode example for STM32WBA6.\n//!\n//! This example demonstrates how to use two comparators in window mode to detect\n//! when an analog input voltage is within a specific voltage window.\n//!\n//! Window mode configuration:\n//! - COMP1: Upper threshold at 3/4 Vref (~2.48V)\n//! - COMP2: Lower threshold at 1/4 Vref (~0.83V)\n//! - Both comparators share the same input signal\n//!\n//! The window detection works as follows:\n//! - Signal BELOW window (< 1/4 Vref): COMP1=LOW, COMP2=LOW\n//! - Signal WITHIN window (1/4 to 3/4 Vref): COMP1=LOW, COMP2=HIGH\n//! - Signal ABOVE window (> 3/4 Vref): COMP1=HIGH, COMP2=HIGH\n//!\n//! Hardware setup:\n//! - Connect a variable voltage (0-3.3V) to PA2 (COMP1 INP)\n//! - COMP2 will use COMP1's input in window mode\n\n#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_stm32::comp::{\n    Comp, Config, Hysteresis, InvertingInput, OutputPolarity, PowerMode, WindowMode, WindowOutput,\n};\nuse embassy_stm32::rcc::{\n    AHB5Prescaler, AHBPrescaler, APBPrescaler, PllDiv, PllMul, PllPreDiv, PllSource, Sysclk, VoltageScale,\n};\nuse embassy_stm32::{bind_interrupts, comp};\nuse embassy_time::Timer;\nuse {defmt_rtt as _, panic_probe as _};\n\nbind_interrupts!(struct Irqs {\n    COMP => comp::InterruptHandler<embassy_stm32::peripherals::COMP1>,\n            comp::InterruptHandler<embassy_stm32::peripherals::COMP2>;\n});\n\n/// Represents the position of the signal relative to the voltage window.\n#[derive(Debug, Clone, Copy, PartialEq, Eq, defmt::Format)]\nenum WindowPosition {\n    /// Signal is below the window (< lower threshold)\n    BelowWindow,\n    /// Signal is within the window (between thresholds)\n    WithinWindow,\n    /// Signal is above the window (> upper threshold)\n    AboveWindow,\n}\n\n/// Determine the window position based on both comparator outputs.\nfn get_window_position(comp1_high: bool, comp2_high: bool) -> WindowPosition {\n    match (comp1_high, comp2_high) {\n        (false, false) => WindowPosition::BelowWindow,\n        (false, true) => WindowPosition::WithinWindow,\n        (true, true) => WindowPosition::AboveWindow,\n        // This state shouldn't happen with proper thresholds\n        (true, false) => WindowPosition::BelowWindow,\n    }\n}\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let mut config = embassy_stm32::Config::default();\n\n    // Configure PLL for system clock\n    config.rcc.pll1 = Some(embassy_stm32::rcc::Pll {\n        source: PllSource::HSI,\n        prediv: PllPreDiv::DIV1,\n        mul: PllMul::MUL30,\n        divr: Some(PllDiv::DIV5),\n        divq: None,\n        divp: Some(PllDiv::DIV30),\n        frac: Some(0),\n    });\n\n    config.rcc.ahb_pre = AHBPrescaler::DIV1;\n    config.rcc.apb1_pre = APBPrescaler::DIV1;\n    config.rcc.apb2_pre = APBPrescaler::DIV1;\n    config.rcc.apb7_pre = APBPrescaler::DIV1;\n    config.rcc.ahb5_pre = AHB5Prescaler::DIV4;\n    config.rcc.voltage_scale = VoltageScale::RANGE1;\n    config.rcc.sys = Sysclk::PLL1_R;\n\n    let p = embassy_stm32::init(config);\n    info!(\"COMP Window Mode example starting!\");\n\n    // Configure COMP1 as the upper threshold comparator\n    // - Non-inverting input (INP): PA2 (the signal to monitor)\n    // - Inverting input (INM): 3/4 Vref (~2.48V) - upper threshold\n    // - Window mode disabled (this is the primary comparator)\n    let comp1_config = Config {\n        power_mode: PowerMode::HighSpeed,\n        hysteresis: Hysteresis::Low,\n        output_polarity: OutputPolarity::NotInverted,\n        inverting_input: InvertingInput::ThreeQuarterVref,\n        window_mode: WindowMode::Disabled,\n        window_output: WindowOutput::OwnValue,\n        ..Default::default()\n    };\n\n    // Configure COMP2 as the lower threshold comparator\n    // - Non-inverting input: Uses COMP1's input (PA2) via window mode\n    // - Inverting input (INM): 1/4 Vref (~0.83V) - lower threshold\n    // - Window mode enabled to share COMP1's input\n    let comp2_config = Config {\n        power_mode: PowerMode::HighSpeed,\n        hysteresis: Hysteresis::Low,\n        output_polarity: OutputPolarity::NotInverted,\n        inverting_input: InvertingInput::OneQuarterVref,\n        window_mode: WindowMode::Enabled, // Use COMP1's INP\n        window_output: WindowOutput::OwnValue,\n        ..Default::default()\n    };\n\n    // Create COMP1 with its input pin (PA2)\n    let mut comp1 = Comp::new(p.COMP1, p.PA2, Irqs, comp1_config);\n\n    // Create COMP2 - in window mode, it uses COMP1's input\n    // We still need to provide a pin for the constructor, but it won't be used\n    // since window mode routes COMP1's input to COMP2\n    let mut comp2 = Comp::new(p.COMP2, p.PA0, Irqs, comp2_config);\n\n    info!(\"Window comparator configured:\");\n    info!(\"  - Upper threshold: 3/4 Vref (~2.48V)\");\n    info!(\"  - Lower threshold: 1/4 Vref (~0.83V)\");\n    info!(\"  - Input signal: PA2\");\n    info!(\"\");\n    info!(\"Window detection:\");\n    info!(\"  - BELOW:  Signal < 0.83V\");\n    info!(\"  - WITHIN: 0.83V < Signal < 2.48V\");\n    info!(\"  - ABOVE:  Signal > 2.48V\");\n\n    // Enable both comparators\n    comp1.enable();\n    comp2.enable();\n\n    // Allow comparators to stabilize\n    Timer::after_millis(10).await;\n\n    info!(\"\");\n    info!(\"Starting window detection - monitoring every 500ms...\");\n\n    let mut last_position = None;\n\n    loop {\n        // Read both comparator outputs\n        let comp1_output = comp1.output_level();\n        let comp2_output = comp2.output_level();\n\n        // Determine window position\n        let position = get_window_position(comp1_output, comp2_output);\n\n        // Only log when position changes to reduce output noise\n        if last_position != Some(position) {\n            match position {\n                WindowPosition::BelowWindow => {\n                    info!(\n                        \"BELOW WINDOW - Signal < 0.83V (COMP1={}, COMP2={})\",\n                        comp1_output, comp2_output\n                    );\n                }\n                WindowPosition::WithinWindow => {\n                    info!(\n                        \"WITHIN WINDOW - 0.83V < Signal < 2.48V (COMP1={}, COMP2={})\",\n                        comp1_output, comp2_output\n                    );\n                }\n                WindowPosition::AboveWindow => {\n                    info!(\n                        \"ABOVE WINDOW - Signal > 2.48V (COMP1={}, COMP2={})\",\n                        comp1_output, comp2_output\n                    );\n                }\n            }\n            last_position = Some(position);\n        }\n\n        Timer::after_millis(500).await;\n    }\n}\n"
  },
  {
    "path": "examples/stm32wba6/src/bin/device_info.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_stm32::rcc::{\n    AHB5Prescaler, AHBPrescaler, APBPrescaler, PllDiv, PllMul, PllPreDiv, PllSource, Sysclk, VoltageScale,\n};\nuse embassy_stm32::{Config, uid};\nuse stm32_metapac::DESIG;\nuse {defmt_rtt as _, panic_probe as _};\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let mut config = Config::default();\n    config.rcc.pll1 = Some(embassy_stm32::rcc::Pll {\n        source: PllSource::HSI,\n        prediv: PllPreDiv::DIV1,\n        mul: PllMul::MUL30,\n        divr: Some(PllDiv::DIV5),\n        divq: None,\n        divp: Some(PllDiv::DIV30),\n        frac: Some(0),\n    });\n\n    config.rcc.ahb_pre = AHBPrescaler::DIV1;\n    config.rcc.apb1_pre = APBPrescaler::DIV1;\n    config.rcc.apb2_pre = APBPrescaler::DIV1;\n    config.rcc.apb7_pre = APBPrescaler::DIV1;\n    config.rcc.ahb5_pre = AHB5Prescaler::DIV4;\n    config.rcc.voltage_scale = VoltageScale::RANGE1;\n    config.rcc.sys = Sysclk::PLL1_R;\n\n    let _p = embassy_stm32::init(config);\n    info!(\"Device info example\");\n\n    // 96-bit unique ID\n    let uid_bytes = uid::uid();\n    let uid_hex = uid::uid_hex();\n    info!(\"UID (bytes): {:02x}\", uid_bytes);\n    info!(\"UID (hex):   {}\", uid_hex);\n\n    // Flash and RAM size\n    let flashsizer = DESIG.flashsizer().read();\n    info!(\"Flash size:  {} KB\", flashsizer.flash_size());\n    info!(\"RAM size:    {} KB\", flashsizer.ram_size());\n\n    // Package type\n    let pkgr = DESIG.pkgr().read();\n    info!(\"Package:     0x{:02x}\", pkgr.pkg().to_bits());\n\n    // IEEE 64-bit unique ID\n    let uid64r1 = DESIG.uid64r1().read();\n    let uid64r2 = DESIG.uid64r2().read();\n    info!(\"UID64 devnum: 0x{:08x}\", uid64r1.devnum());\n    info!(\"UID64 devid:  0x{:02x}\", uid64r2.devid().to_bits());\n    info!(\"UID64 stid:   0x{:06x}\", uid64r2.stid().to_bits());\n}\n"
  },
  {
    "path": "examples/stm32wba6/src/bin/flash.rs",
    "content": "//! STM32WBA6 blocking flash example.\n//!\n//! Demonstrates quad-word (16-byte) programming with WDW/BSY handling:\n//! - Erase one page (8 KB) in bank 1\n//! - Write 32 bytes (2 quad-words) at 16-byte-aligned offset\n//! - Read back and verify\n\n#![no_std]\n#![no_main]\n\nuse defmt::{info, unwrap};\nuse embassy_executor::Spawner;\nuse embassy_stm32::Config;\nuse embassy_stm32::flash::Flash;\nuse embassy_stm32::rcc::{\n    AHB5Prescaler, AHBPrescaler, APBPrescaler, PllDiv, PllMul, PllPreDiv, PllSource, Sysclk, VoltageScale, mux,\n};\nuse embassy_time::Timer;\nuse {defmt_rtt as _, panic_probe as _};\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let mut config = Config::default();\n    config.rcc.pll1 = Some(embassy_stm32::rcc::Pll {\n        source: PllSource::HSI,\n        prediv: PllPreDiv::DIV1,\n        mul: PllMul::MUL30,\n        divr: Some(PllDiv::DIV5),\n        divq: None,\n        divp: Some(PllDiv::DIV30),\n        frac: Some(0),\n    });\n    config.rcc.ahb_pre = AHBPrescaler::DIV1;\n    config.rcc.apb1_pre = APBPrescaler::DIV1;\n    config.rcc.apb2_pre = APBPrescaler::DIV1;\n    config.rcc.apb7_pre = APBPrescaler::DIV1;\n    config.rcc.ahb5_pre = AHB5Prescaler::DIV4;\n    config.rcc.voltage_scale = VoltageScale::RANGE1;\n    config.rcc.sys = Sysclk::PLL1_R;\n\n    // SAI clock source: HSI (flash example does not use SAI; avoids requiring PLL1.P)\n    config.rcc.mux.sai1sel = mux::Sai1sel::HSI;\n\n    let p = embassy_stm32::init(config);\n    info!(\"STM32WBA6 Flash example (quad-word programming)\");\n\n    // Use high offset in bank 1 to avoid overwriting program (page-aligned: 8 KB).\n    const PAGE_SIZE: u32 = 8 * 1024;\n    const ADDR: u32 = 0x7_0000; // 448 KB offset in bank 1\n\n    Timer::after_millis(100).await;\n\n    let mut f = Flash::new_blocking(p.FLASH).into_blocking_regions().bank1_region;\n\n    info!(\"Reading before erase...\");\n    let mut buf = [0u8; 32];\n    unwrap!(f.blocking_read(ADDR, &mut buf));\n    info!(\"Read: {=[u8]:x}\", buf);\n\n    info!(\"Erasing one page ({} bytes)...\", PAGE_SIZE);\n    unwrap!(f.blocking_erase(ADDR, ADDR + PAGE_SIZE));\n\n    info!(\"Reading after erase...\");\n    unwrap!(f.blocking_read(ADDR, &mut buf));\n    info!(\"Read after erase: {=[u8]:x}\", buf);\n\n    info!(\"Writing 32 bytes (2 quad-words)...\");\n    unwrap!(f.blocking_write(\n        ADDR,\n        &[\n            1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29,\n            30, 31, 32,\n        ]\n    ));\n\n    info!(\"Reading after write...\");\n    unwrap!(f.blocking_read(ADDR, &mut buf));\n    info!(\"Read: {=[u8]:x}\", buf);\n\n    assert_eq!(\n        &buf[..],\n        &[\n            1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29,\n            30, 31, 32,\n        ],\n        \"Flash read-back mismatch\"\n    );\n    info!(\"Flash verify OK\");\n}\n"
  },
  {
    "path": "examples/stm32wba6/src/bin/mac_ffd.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_stm32::Config;\nuse embassy_stm32::rcc::{\n    AHB5Prescaler, AHBPrescaler, APBPrescaler, PllDiv, PllMul, PllPreDiv, PllSource, Sysclk, VoltageScale, mux,\n};\nuse embassy_stm32_wpan::bindings::mac::{ST_MAC_callbacks_t, ST_MAC_init};\nuse {defmt_rtt as _, panic_probe as _};\n\nstatic _MAC_CALLBACKS: ST_MAC_callbacks_t = ST_MAC_callbacks_t {\n    mlmeAssociateCnfCb: None,       // ST_MAC_MLMEAssociateCnfCbPtr,\n    mlmeAssociateIndCb: None,       // ST_MAC_MLMEAssociateIndCbPtr,\n    mlmeBeaconNotifyIndCb: None,    // ST_MAC_MLMEBeaconNotifyIndCbPtr,\n    mlmeCalibrateCnfCb: None,       // ST_MAC_MLMECalibrateCnfCbPtr,\n    mlmeCommStatusIndCb: None,      // ST_MAC_MLMECommStatusIndCbPtr,\n    mlmeDisassociateCnfCb: None,    // ST_MAC_MLMEDisassociateCnfCbPtr,\n    mlmeDisassociateIndCb: None,    // ST_MAC_MLMEDisassociateIndCbPtr,\n    mlmeDpsCnfCb: None,             // ST_MAC_MLMEDpsCnfCbPtr,\n    mlmeDpsIndCb: None,             // ST_MAC_MLMEDpsIndCbPtr,\n    mlmeGetCnfCb: None,             // ST_MAC_MLMEGetCnfCbPtr,\n    mlmeGtsCnfCb: None,             // ST_MAC_MLMEGtsCnfCbPtr,\n    mlmeGtsIndCb: None,             // ST_MAC_MLMEGtsIndCbPtr,\n    mlmeOrphanIndCb: None,          // ST_MAC_MLMEOrphanIndCbPtr,\n    mlmePollCnfCb: None,            // ST_MAC_MLMEPollCnfCbPtr,\n    mlmeResetCnfCb: None,           // ST_MAC_MLMEResetCnfCbPtr,\n    mlmeRxEnableCnfCb: None,        // ST_MAC_MLMERxEnableCnfCbPtr,\n    mlmeScanCnfCb: None,            // ST_MAC_MLMEScanCnfCbPtr,\n    mlmeSetCnfCb: None,             // ST_MAC_MLMESetCnfCbPtr,\n    mlmeSoundingCnfCb: None,        // ST_MAC_MLMESoundingCnfCbPtr,\n    mlmeStartCnfCb: None,           // ST_MAC_MLMEStartCnfCbPtr,\n    mlmeSyncLossIndCb: None,        // ST_MAC_MLMESyncLossIndCbPtr,\n    mcpsDataIndCb: None,            // ST_MAC_MCPSDataIndCbPtr,\n    mcpsDataCnfCb: None,            // ST_MAC_MCPSDataCnfCbPtr,\n    mcpsPurgeCnfCb: None,           // ST_MAC_MCPSPurgeCnfCbPtr,\n    mlmePollIndCb: None,            // ST_MAC_MLMEPollIndCbPtr,\n    mlmeBeaconReqIndCb: None,       // ST_MAC_MLMEBeaconReqIndCbPtr,\n    mlmeBeaconCnfCb: None,          // ST_MAC_MLMEBeaconCnfCbPtr,\n    mlmeGetPwrInfoTableCnfCb: None, // ST_MAC_MLMEGetPwrInfoTableCnfCbPtr,\n    mlmeSetPwrInfoTableCnfCb: None, // ST_MAC_MLMESetPwrInfoTableCnfCbPtr,\n};\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let mut config = Config::default();\n    // Fine-tune PLL1 dividers/multipliers\n    config.rcc.pll1 = Some(embassy_stm32::rcc::Pll {\n        source: PllSource::HSI,\n        prediv: PllPreDiv::DIV1,  // PLLM = 1 → HSI / 1 = 16 MHz\n        mul: PllMul::MUL30,       // PLLN = 30 → 16 MHz * 30 = 480 MHz VCO\n        divr: Some(PllDiv::DIV5), // PLLR = 5 → 96 MHz (Sysclk)\n        // divq: Some(PllDiv::DIV10), // PLLQ = 10 → 48 MHz (NOT USED)\n        divq: None,\n        divp: Some(PllDiv::DIV30), // PLLP = 30 → 16 MHz (USBOTG)\n        frac: Some(0),             // Fractional part (enabled)\n    });\n\n    config.rcc.ahb_pre = AHBPrescaler::DIV1;\n    config.rcc.apb1_pre = APBPrescaler::DIV1;\n    config.rcc.apb2_pre = APBPrescaler::DIV1;\n    config.rcc.apb7_pre = APBPrescaler::DIV1;\n    config.rcc.ahb5_pre = AHB5Prescaler::DIV4;\n\n    // voltage scale for max performance\n    config.rcc.voltage_scale = VoltageScale::RANGE1;\n    // route PLL1_P into the USB‐OTG‐HS block\n    config.rcc.sys = Sysclk::PLL1_R;\n\n    // let p = embassy_stm32::init(config);\n\n    // config.rcc.sys = Sysclk::HSI;\n    config.rcc.mux.rngsel = mux::Rngsel::HSI;\n\n    let _p = embassy_stm32::init(config);\n    info!(\"Hello World!\");\n\n    let status = unsafe { ST_MAC_init(&_MAC_CALLBACKS as *const _ as *mut _) };\n\n    info!(\"mac init: {}\", status);\n\n    cortex_m::asm::bkpt();\n}\n"
  },
  {
    "path": "examples/stm32wba6/src/bin/pka_ecdh.rs",
    "content": "//! PKA ECDH Key Agreement Example\n//!\n//! Demonstrates Elliptic Curve Diffie-Hellman (ECDH) key agreement using the\n//! PKA hardware accelerator.\n//!\n//! # What This Example Shows\n//! - Generating key pairs (private + public key)\n//! - Computing shared secrets between two parties\n//! - Verifying that both parties derive the same shared secret\n//! - Point validation for security\n//!\n//! # ECDH Key Agreement Process\n//! 1. Alice generates private key (a) and public key (A = a*G)\n//! 2. Bob generates private key (b) and public key (B = b*G)\n//! 3. Alice computes shared secret: S = a * B = a * b * G\n//! 4. Bob computes shared secret: S = b * A = b * a * G\n//! 5. Both parties now have the same shared secret S\n//!\n//! # Security Notes\n//! - Always validate received public keys (use point_check)\n//! - Use the x-coordinate of the shared point as the shared secret\n//! - Derive session keys from the shared secret using a KDF (HKDF, SHA-256)\n//! - Private keys must be randomly generated and kept secret\n\n#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_stm32::pka::{EccPoint, EcdsaCurveParams, Pka};\nuse embassy_stm32::rcc::{\n    AHB5Prescaler, AHBPrescaler, APBPrescaler, PllDiv, PllMul, PllPreDiv, PllSource, Sysclk, VoltageScale, mux,\n};\nuse embassy_stm32::rng::Rng;\nuse embassy_stm32::{Config, bind_interrupts, peripherals};\nuse {defmt_rtt as _, panic_probe as _};\n\nbind_interrupts!(struct Irqs {\n    PKA => embassy_stm32::pka::InterruptHandler<peripherals::PKA>;\n    RNG => embassy_stm32::rng::InterruptHandler<peripherals::RNG>;\n});\n\n#[embassy_executor::main]\nasync fn main(_spawner: embassy_executor::Spawner) {\n    let mut config = Config::default();\n    config.rcc.pll1 = Some(embassy_stm32::rcc::Pll {\n        source: PllSource::HSI,\n        prediv: PllPreDiv::DIV1,\n        mul: PllMul::MUL30,\n        divr: Some(PllDiv::DIV5),\n        divq: None,\n        divp: Some(PllDiv::DIV30),\n        frac: Some(0),\n    });\n    config.rcc.ahb_pre = AHBPrescaler::DIV1;\n    config.rcc.apb1_pre = APBPrescaler::DIV1;\n    config.rcc.apb2_pre = APBPrescaler::DIV1;\n    config.rcc.apb7_pre = APBPrescaler::DIV1;\n    config.rcc.ahb5_pre = AHB5Prescaler::DIV4;\n    config.rcc.voltage_scale = VoltageScale::RANGE1;\n    config.rcc.sys = Sysclk::PLL1_R;\n    // RNG requires HSI clock source on WBA\n    config.rcc.mux.rngsel = mux::Rngsel::HSI;\n\n    let p = embassy_stm32::init(config);\n    info!(\"PKA ECDH Key Agreement Example\");\n\n    let mut pka = Pka::new_blocking(p.PKA, Irqs);\n    let mut rng = Rng::new(p.RNG, Irqs);\n\n    // Use NIST P-256 curve parameters\n    let curve = EcdsaCurveParams::nist_p256();\n\n    // ========== Generate Alice's Key Pair ==========\n    info!(\"=== Generating Alice's Key Pair ===\");\n\n    // Generate Alice's private key\n    let mut alice_private = [0u8; 32];\n    if let Err(e) = rng.async_fill_bytes(&mut alice_private).await {\n        error!(\"Failed to generate Alice's private key: {:?}\", e);\n        loop {\n            cortex_m::asm::wfi();\n        }\n    }\n    // Ensure private key is in valid range (1 < d < n)\n    alice_private[0] &= 0x7F;\n    alice_private[31] |= 0x01;\n\n    info!(\"Alice private key: {:02x}\", alice_private);\n\n    // Compute Alice's public key: A = alice_private * G\n    let mut alice_public = EccPoint::new(32);\n    match pka.ecc_mul(\n        &curve,\n        &alice_private,\n        curve.generator_x,\n        curve.generator_y,\n        &mut alice_public,\n    ) {\n        Ok(()) => {\n            info!(\"Alice public key X: {:02x}\", alice_public.x[..32]);\n            info!(\"Alice public key Y: {:02x}\", alice_public.y[..32]);\n        }\n        Err(e) => {\n            error!(\"Failed to generate Alice's public key: {:?}\", e);\n            loop {\n                cortex_m::asm::wfi();\n            }\n        }\n    }\n\n    // ========== Generate Bob's Key Pair ==========\n    info!(\"=== Generating Bob's Key Pair ===\");\n\n    // Generate Bob's private key\n    let mut bob_private = [0u8; 32];\n    if let Err(e) = rng.async_fill_bytes(&mut bob_private).await {\n        error!(\"Failed to generate Bob's private key: {:?}\", e);\n        loop {\n            cortex_m::asm::wfi();\n        }\n    }\n    // Ensure private key is in valid range\n    bob_private[0] &= 0x7F;\n    bob_private[31] |= 0x01;\n\n    info!(\"Bob private key: {:02x}\", bob_private);\n\n    // Compute Bob's public key: B = bob_private * G\n    let mut bob_public = EccPoint::new(32);\n    match pka.ecc_mul(\n        &curve,\n        &bob_private,\n        curve.generator_x,\n        curve.generator_y,\n        &mut bob_public,\n    ) {\n        Ok(()) => {\n            info!(\"Bob public key X: {:02x}\", bob_public.x[..32]);\n            info!(\"Bob public key Y: {:02x}\", bob_public.y[..32]);\n        }\n        Err(e) => {\n            error!(\"Failed to generate Bob's public key: {:?}\", e);\n            loop {\n                cortex_m::asm::wfi();\n            }\n        }\n    }\n\n    // ========== Validate Public Keys ==========\n    info!(\"=== Validating Public Keys ===\");\n\n    // Alice validates Bob's public key\n    match pka.point_check(&curve, &bob_public.x[..32], &bob_public.y[..32]) {\n        Ok(true) => {\n            info!(\"Bob's public key is valid (on curve)\");\n        }\n        Ok(false) => {\n            error!(\"Bob's public key is INVALID (not on curve)!\");\n            loop {\n                cortex_m::asm::wfi();\n            }\n        }\n        Err(e) => {\n            error!(\"Point check failed: {:?}\", e);\n            loop {\n                cortex_m::asm::wfi();\n            }\n        }\n    }\n\n    // Bob validates Alice's public key\n    match pka.point_check(&curve, &alice_public.x[..32], &alice_public.y[..32]) {\n        Ok(true) => {\n            info!(\"Alice's public key is valid (on curve)\");\n        }\n        Ok(false) => {\n            error!(\"Alice's public key is INVALID (not on curve)!\");\n            loop {\n                cortex_m::asm::wfi();\n            }\n        }\n        Err(e) => {\n            error!(\"Point check failed: {:?}\", e);\n            loop {\n                cortex_m::asm::wfi();\n            }\n        }\n    }\n\n    // ========== Compute Shared Secrets ==========\n    info!(\"=== Computing Shared Secrets ===\");\n\n    // Alice computes shared secret: S_alice = alice_private * bob_public\n    let mut alice_shared = EccPoint::new(32);\n    match pka.ecc_mul(\n        &curve,\n        &alice_private,\n        &bob_public.x[..32],\n        &bob_public.y[..32],\n        &mut alice_shared,\n    ) {\n        Ok(()) => {\n            info!(\"Alice's shared secret X: {:02x}\", alice_shared.x[..32]);\n        }\n        Err(e) => {\n            error!(\"Alice failed to compute shared secret: {:?}\", e);\n            loop {\n                cortex_m::asm::wfi();\n            }\n        }\n    }\n\n    // Bob computes shared secret: S_bob = bob_private * alice_public\n    let mut bob_shared = EccPoint::new(32);\n    match pka.ecc_mul(\n        &curve,\n        &bob_private,\n        &alice_public.x[..32],\n        &alice_public.y[..32],\n        &mut bob_shared,\n    ) {\n        Ok(()) => {\n            info!(\"Bob's shared secret X: {:02x}\", bob_shared.x[..32]);\n        }\n        Err(e) => {\n            error!(\"Bob failed to compute shared secret: {:?}\", e);\n            loop {\n                cortex_m::asm::wfi();\n            }\n        }\n    }\n\n    // ========== Verify Shared Secrets Match ==========\n    info!(\"=== Verifying Key Agreement ===\");\n\n    if alice_shared.x[..32] == bob_shared.x[..32] && alice_shared.y[..32] == bob_shared.y[..32] {\n        info!(\"SUCCESS: Both parties derived the SAME shared secret!\");\n        info!(\"Shared secret (x-coord): {:02x}\", alice_shared.x[..32]);\n        info!(\"\");\n        info!(\"This shared secret can now be used to derive:\");\n        info!(\"- AES encryption keys (using SHA-256 or HKDF)\");\n        info!(\"- HMAC authentication keys\");\n        info!(\"- Session keys for secure communication\");\n    } else {\n        error!(\"FAILURE: Shared secrets do not match!\");\n        error!(\"Alice X: {:02x}\", alice_shared.x[..32]);\n        error!(\"Bob X:   {:02x}\", bob_shared.x[..32]);\n    }\n\n    // ========== Example with Pre-defined Test Vectors ==========\n    info!(\"=== ECDH Test with Known Public Key ===\");\n\n    // Use the ST HAL example's ECDSA key pair for testing\n    // Private key from ST HAL PKA_ECCscalarMultiplication example\n    let test_private: [u8; 32] = [\n        0xfe, 0x22, 0xd2, 0xa5, 0xe9, 0xe8, 0x1f, 0x92, 0xb0, 0xbb, 0x42, 0xc2, 0xfe, 0xde, 0x3e, 0x63, 0xab, 0x0a,\n        0xb1, 0x0f, 0x14, 0x9a, 0xa8, 0x3f, 0x76, 0xda, 0x44, 0x69, 0xd3, 0xbe, 0x69, 0x57,\n    ];\n\n    // Expected public key (computed from private key * G)\n    let expected_pub_x: [u8; 32] = [\n        0xdd, 0x79, 0x95, 0xda, 0x1f, 0xa1, 0xc0, 0x25, 0xf3, 0xe7, 0xaa, 0x6b, 0x62, 0x2c, 0x9d, 0x78, 0x4a, 0x37,\n        0x22, 0xdc, 0x8d, 0x64, 0x6b, 0x1b, 0x14, 0xf5, 0xc3, 0xa0, 0x3c, 0xa9, 0x70, 0x19,\n    ];\n    let expected_pub_y: [u8; 32] = [\n        0xc6, 0xd8, 0x7e, 0xb5, 0x78, 0x43, 0xff, 0x15, 0xa0, 0x77, 0x92, 0x55, 0x86, 0x8e, 0x5b, 0xb4, 0x0e, 0xb0,\n        0x79, 0xc8, 0xe3, 0x42, 0xca, 0xc4, 0x55, 0xf7, 0x2c, 0xf4, 0x04, 0xb1, 0x99, 0x82,\n    ];\n\n    info!(\"Computing public key from known private key...\");\n    let mut test_public = EccPoint::new(32);\n    match pka.ecc_mul(\n        &curve,\n        &test_private,\n        curve.generator_x,\n        curve.generator_y,\n        &mut test_public,\n    ) {\n        Ok(()) => {\n            info!(\"Computed public key X: {:02x}\", test_public.x[..32]);\n            info!(\"Computed public key Y: {:02x}\", test_public.y[..32]);\n\n            // Verify it matches the expected value from ST HAL\n            if test_public.x[..32] == expected_pub_x && test_public.y[..32] == expected_pub_y {\n                info!(\"SUCCESS: Public key matches ST HAL expected value!\");\n            } else {\n                error!(\"Public key does not match expected value\");\n                error!(\"Expected X: {:02x}\", expected_pub_x);\n                error!(\"Expected Y: {:02x}\", expected_pub_y);\n            }\n        }\n        Err(e) => {\n            error!(\"Test vector computation failed: {:?}\", e);\n        }\n    }\n\n    info!(\"=== ECDH key agreement example complete ===\");\n\n    loop {\n        cortex_m::asm::wfi();\n    }\n}\n"
  },
  {
    "path": "examples/stm32wba6/src/bin/pka_ecdsa_sign.rs",
    "content": "//! PKA ECDSA Signature Generation Example\n//!\n//! Demonstrates ECDSA signature generation using the PKA hardware accelerator.\n//!\n//! # What This Example Shows\n//! - Generating an ECDSA signature with a private key\n//! - Using the hardware RNG for the random nonce (k value)\n//! - Verifying the generated signature\n//!\n//! # ECDSA Signing Process\n//! 1. Hash the message (SHA-256 for P-256)\n//! 2. Generate a random nonce k (CRITICAL: must be unique per signature!)\n//! 3. Compute signature (r, s) = Sign(hash, private_key, k)\n//!\n//! # Security Notes\n//! - The k value MUST be cryptographically random and unique for EVERY signature\n//! - Reusing k values completely compromises the private key!\n//! - Use hardware RNG for k generation\n//! - Private keys should be stored securely\n\n#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_stm32::pka::{EccPoint, EcdsaCurveParams, EcdsaPublicKey, EcdsaSignature, Pka};\nuse embassy_stm32::rcc::{\n    AHB5Prescaler, AHBPrescaler, APBPrescaler, PllDiv, PllMul, PllPreDiv, PllSource, Sysclk, VoltageScale, mux,\n};\nuse embassy_stm32::rng::Rng;\nuse embassy_stm32::{Config, bind_interrupts, peripherals};\nuse {defmt_rtt as _, panic_probe as _};\n\nbind_interrupts!(struct Irqs {\n    PKA => embassy_stm32::pka::InterruptHandler<peripherals::PKA>;\n    RNG => embassy_stm32::rng::InterruptHandler<peripherals::RNG>;\n});\n\n#[embassy_executor::main]\nasync fn main(_spawner: embassy_executor::Spawner) {\n    let mut config = Config::default();\n    config.rcc.pll1 = Some(embassy_stm32::rcc::Pll {\n        source: PllSource::HSI,\n        prediv: PllPreDiv::DIV1,\n        mul: PllMul::MUL30,\n        divr: Some(PllDiv::DIV5),\n        divq: None,\n        divp: Some(PllDiv::DIV30),\n        frac: Some(0),\n    });\n    config.rcc.ahb_pre = AHBPrescaler::DIV1;\n    config.rcc.apb1_pre = APBPrescaler::DIV1;\n    config.rcc.apb2_pre = APBPrescaler::DIV1;\n    config.rcc.apb7_pre = APBPrescaler::DIV1;\n    config.rcc.ahb5_pre = AHB5Prescaler::DIV4;\n    config.rcc.voltage_scale = VoltageScale::RANGE1;\n    config.rcc.sys = Sysclk::PLL1_R;\n    // RNG requires HSI clock source on WBA\n    config.rcc.mux.rngsel = mux::Rngsel::HSI;\n\n    let p = embassy_stm32::init(config);\n    info!(\"PKA ECDSA Signature Generation Example\");\n\n    let mut pka = Pka::new_blocking(p.PKA, Irqs);\n    let mut rng = Rng::new(p.RNG, Irqs);\n\n    // Use NIST P-256 curve parameters\n    let curve = EcdsaCurveParams::nist_p256();\n\n    // Test private key (32 bytes, big-endian)\n    // In a real application, this would be securely stored\n    // WARNING: Never use this key in production!\n    let private_key: [u8; 32] = [\n        0xc9, 0xaf, 0xa9, 0xd8, 0x45, 0xba, 0x75, 0x16, 0x6b, 0x5c, 0x21, 0x57, 0x67, 0xb1, 0xd6, 0x93, 0x4e, 0x50,\n        0xc3, 0xdb, 0x36, 0xe8, 0x9b, 0x12, 0x7b, 0x8a, 0x62, 0x2b, 0x12, 0x0f, 0x67, 0x21,\n    ];\n\n    // Corresponding public key (derived from private key)\n    let pub_key_x: [u8; 32] = [\n        0x60, 0xfe, 0xd4, 0xba, 0x25, 0x5a, 0x9d, 0x31, 0xc9, 0x61, 0xeb, 0x74, 0xc6, 0x35, 0x6d, 0x68, 0xc0, 0x49,\n        0xb8, 0x92, 0x3b, 0x61, 0xfa, 0x6c, 0xe6, 0x69, 0x62, 0x2e, 0x60, 0xf2, 0x9f, 0xb6,\n    ];\n    let pub_key_y: [u8; 32] = [\n        0x79, 0x03, 0xfe, 0x10, 0x08, 0xb8, 0xbc, 0x99, 0xa4, 0x1a, 0xe9, 0xe9, 0x56, 0x28, 0xbc, 0x64, 0xf2, 0xf1,\n        0xb2, 0x0c, 0x2d, 0x7e, 0x9f, 0x51, 0x77, 0xa3, 0xc2, 0x94, 0xd4, 0x46, 0x22, 0x99,\n    ];\n\n    // Message hash (SHA-256 of the message)\n    // In a real application, you would compute this from the message\n    let message_hash: [u8; 32] = [\n        0xaf, 0x2b, 0xdb, 0xe1, 0xaa, 0x9b, 0x6e, 0xc1, 0xe2, 0xad, 0xe1, 0xd6, 0x94, 0xf4, 0x1f, 0xc7, 0x1a, 0x83,\n        0x1d, 0x02, 0x68, 0xe9, 0x89, 0x15, 0x62, 0x11, 0x3d, 0x8a, 0x62, 0xad, 0xd1, 0xbf,\n    ];\n\n    info!(\"=== ECDSA Signature Generation ===\");\n    info!(\"Curve: NIST P-256 (secp256r1)\");\n    info!(\"Message Hash: {:02x}\", message_hash);\n\n    // Generate random k value using hardware RNG\n    // CRITICAL: k must be random and unique for every signature!\n    let mut k = [0u8; 32];\n    if let Err(e) = rng.async_fill_bytes(&mut k).await {\n        error!(\"Failed to generate random k: {:?}\", e);\n        loop {\n            cortex_m::asm::wfi();\n        }\n    }\n\n    // Ensure k is in valid range (1 < k < n)\n    // For simplicity, we set the MSB to ensure it's less than n\n    k[0] &= 0x7F;\n    // Ensure k is not zero\n    k[31] |= 0x01;\n\n    info!(\"Random k:     {:02x}\", k);\n\n    // Generate signature\n    let mut sig_r = [0u8; 32];\n    let mut sig_s = [0u8; 32];\n\n    info!(\"Signing message...\");\n    match pka.ecdsa_sign(&curve, &private_key, &k, &message_hash, &mut sig_r, &mut sig_s) {\n        Ok(()) => {\n            info!(\"Signature generated successfully!\");\n            info!(\"Signature R: {:02x}\", sig_r);\n            info!(\"Signature S: {:02x}\", sig_s);\n        }\n        Err(e) => {\n            error!(\"Signing failed: {:?}\", e);\n            loop {\n                cortex_m::asm::wfi();\n            }\n        }\n    }\n\n    // Verify the signature we just generated\n    info!(\"=== Verifying Generated Signature ===\");\n\n    let public_key = EcdsaPublicKey {\n        x: &pub_key_x,\n        y: &pub_key_y,\n    };\n\n    let signature = EcdsaSignature { r: &sig_r, s: &sig_s };\n\n    match pka.ecdsa_verify(&curve, &public_key, &signature, &message_hash) {\n        Ok(true) => {\n            info!(\"Generated signature verified successfully!\");\n        }\n        Ok(false) => {\n            error!(\"Generated signature verification FAILED!\");\n        }\n        Err(e) => {\n            error!(\"Verification error: {:?}\", e);\n        }\n    }\n\n    // Demonstrate public key derivation from private key using scalar multiplication\n    info!(\"=== Deriving Public Key from Private Key ===\");\n\n    // Get generator point from curve parameters\n    let generator_x = curve.generator_x;\n    let generator_y = curve.generator_y;\n\n    let mut derived_pub = EccPoint::new(32);\n    match pka.ecc_mul(&curve, &private_key, generator_x, generator_y, &mut derived_pub) {\n        Ok(()) => {\n            info!(\"Public key derived from private key:\");\n            info!(\"Derived X:  {:02x}\", derived_pub.x[..32]);\n            info!(\"Expected X: {:02x}\", pub_key_x);\n            info!(\"Derived Y:  {:02x}\", derived_pub.y[..32]);\n            info!(\"Expected Y: {:02x}\", pub_key_y);\n\n            // Compare derived with expected\n            if derived_pub.x[..32] == pub_key_x && derived_pub.y[..32] == pub_key_y {\n                info!(\"Derived public key matches expected!\");\n            } else {\n                warn!(\"Derived public key does not match!\");\n            }\n        }\n        Err(e) => {\n            error!(\"Public key derivation failed: {:?}\", e);\n        }\n    }\n\n    info!(\"=== ECDSA signing example complete ===\");\n\n    loop {\n        cortex_m::asm::wfi();\n    }\n}\n"
  },
  {
    "path": "examples/stm32wba6/src/bin/pka_ecdsa_verify.rs",
    "content": "//! PKA ECDSA Signature Verification Example\n//!\n//! Demonstrates ECDSA signature verification using the PKA hardware accelerator.\n//!\n//! # What This Example Shows\n//! - Loading NIST P-256 curve parameters\n//! - Verifying an ECDSA signature over a message hash\n//! - Using NIST test vectors for validation\n//!\n//! # ECDSA Verification Process\n//! The PKA hardware verifies that the signature (r, s) is valid for the given\n//! message hash and public key on the specified elliptic curve.\n//!\n//! # Security Notes\n//! - Always validate public keys before use (point_check)\n//! - Hash the message before verification (PKA expects the hash, not raw data)\n//! - Use approved hash functions (SHA-256 for P-256)\n\n#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_stm32::pka::{EcdsaCurveParams, EcdsaPublicKey, EcdsaSignature, Pka};\nuse embassy_stm32::rcc::{\n    AHB5Prescaler, AHBPrescaler, APBPrescaler, PllDiv, PllMul, PllPreDiv, PllSource, Sysclk, VoltageScale, mux,\n};\nuse embassy_stm32::rng::Rng;\nuse embassy_stm32::{Config, bind_interrupts, peripherals};\nuse {defmt_rtt as _, panic_probe as _};\n\nbind_interrupts!(struct Irqs {\n    PKA => embassy_stm32::pka::InterruptHandler<peripherals::PKA>;\n    RNG => embassy_stm32::rng::InterruptHandler<peripherals::RNG>;\n});\n\n#[embassy_executor::main]\nasync fn main(_spawner: embassy_executor::Spawner) {\n    let mut config = Config::default();\n    config.rcc.pll1 = Some(embassy_stm32::rcc::Pll {\n        source: PllSource::HSI,\n        prediv: PllPreDiv::DIV1,\n        mul: PllMul::MUL30,\n        divr: Some(PllDiv::DIV5),\n        divq: None,\n        divp: Some(PllDiv::DIV30),\n        frac: Some(0),\n    });\n    config.rcc.ahb_pre = AHBPrescaler::DIV1;\n    config.rcc.apb1_pre = APBPrescaler::DIV1;\n    config.rcc.apb2_pre = APBPrescaler::DIV1;\n    config.rcc.apb7_pre = APBPrescaler::DIV1;\n    config.rcc.ahb5_pre = AHB5Prescaler::DIV4;\n    config.rcc.voltage_scale = VoltageScale::RANGE1;\n    config.rcc.sys = Sysclk::PLL1_R;\n    // RNG clock is required for PKA to initialize properly on WBA\n    config.rcc.mux.rngsel = mux::Rngsel::HSI;\n\n    let p = embassy_stm32::init(config);\n    info!(\"PKA ECDSA Signature Verification Example\");\n\n    // Initialize RNG to enable its clock - PKA requires RNG clock on AHB5\n    let _rng = Rng::new(p.RNG, Irqs);\n\n    let mut pka = Pka::new_blocking(p.PKA, Irqs);\n\n    // Use NIST P-256 curve parameters\n    let curve = EcdsaCurveParams::nist_p256();\n\n    // NIST CAVP test vector for P-256 ECDSA verification\n    // From NIST CAVP SigVer.rsp [P-256,SHA-256]\n    // This test vector is verified to work with the ST-HAL\n\n    // Public key Qx, Qy (big-endian)\n    let pub_key_x: [u8; 32] = [\n        0xe4, 0x24, 0xdc, 0x61, 0xd4, 0xbb, 0x3c, 0xb7, 0xef, 0x43, 0x44, 0xa7, 0xf8, 0x95, 0x7a, 0x0c, 0x51, 0x34,\n        0xe1, 0x6f, 0x7a, 0x67, 0xc0, 0x74, 0xf8, 0x2e, 0x6e, 0x12, 0xf4, 0x9a, 0xbf, 0x3c,\n    ];\n    let pub_key_y: [u8; 32] = [\n        0x97, 0x0e, 0xed, 0x7a, 0xa2, 0xbc, 0x48, 0x65, 0x15, 0x45, 0x94, 0x9d, 0xe1, 0xdd, 0xda, 0xf0, 0x12, 0x7e,\n        0x59, 0x65, 0xac, 0x85, 0xd1, 0x24, 0x3d, 0x6f, 0x60, 0xe7, 0xdf, 0xae, 0xe9, 0x27,\n    ];\n\n    // SHA-256 hash of the test message (pre-computed)\n    let message_hash: [u8; 32] = [\n        0xd1, 0xb8, 0xef, 0x21, 0xeb, 0x41, 0x82, 0xee, 0x27, 0x06, 0x38, 0x06, 0x10, 0x63, 0xa3, 0xf3, 0xc1, 0x6c,\n        0x11, 0x4e, 0x33, 0x93, 0x7f, 0x69, 0xfb, 0x23, 0x2c, 0xc8, 0x33, 0x96, 0x5a, 0x94,\n    ];\n\n    // ECDSA signature (r, s) - valid signature for the above hash and public key\n    let sig_r: [u8; 32] = [\n        0xbf, 0x96, 0xb9, 0x9a, 0xa4, 0x9c, 0x70, 0x5c, 0x91, 0x0b, 0xe3, 0x31, 0x42, 0x01, 0x7c, 0x64, 0x2f, 0xf5,\n        0x40, 0xc7, 0x63, 0x49, 0xb9, 0xda, 0xb7, 0x2f, 0x98, 0x1f, 0xd9, 0x34, 0x7f, 0x4f,\n    ];\n    let sig_s: [u8; 32] = [\n        0x17, 0xc5, 0x50, 0x95, 0x81, 0x90, 0x89, 0xc2, 0xe0, 0x3b, 0x9c, 0xd4, 0x15, 0xab, 0xdf, 0x12, 0x44, 0x4e,\n        0x32, 0x30, 0x75, 0xd9, 0x8f, 0x31, 0x92, 0x0b, 0x9e, 0x0f, 0x57, 0xec, 0x87, 0x1c,\n    ];\n\n    info!(\"=== ECDSA Signature Verification ===\");\n    info!(\"Curve: NIST P-256 (secp256r1)\");\n    info!(\"Public Key X: {:02x}\", pub_key_x);\n    info!(\"Public Key Y: {:02x}\", pub_key_y);\n    info!(\"Message Hash: {:02x}\", message_hash);\n    info!(\"Signature R:  {:02x}\", sig_r);\n    info!(\"Signature S:  {:02x}\", sig_s);\n\n    let public_key = EcdsaPublicKey {\n        x: &pub_key_x,\n        y: &pub_key_y,\n    };\n\n    let signature = EcdsaSignature { r: &sig_r, s: &sig_s };\n\n    // Verify the signature\n    info!(\"Verifying signature...\");\n    match pka.ecdsa_verify(&curve, &public_key, &signature, &message_hash) {\n        Ok(true) => {\n            info!(\"Signature is VALID\");\n        }\n        Ok(false) => {\n            info!(\"Signature is INVALID\");\n        }\n        Err(e) => {\n            error!(\"Verification failed with error: {:?}\", e);\n        }\n    }\n\n    // Test with a tampered signature (modify one byte)\n    info!(\"=== Testing with tampered signature ===\");\n    let mut tampered_sig_r = sig_r;\n    tampered_sig_r[0] ^= 0x01; // Flip one bit\n\n    let tampered_signature = EcdsaSignature {\n        r: &tampered_sig_r,\n        s: &sig_s,\n    };\n\n    match pka.ecdsa_verify(&curve, &public_key, &tampered_signature, &message_hash) {\n        Ok(true) => {\n            error!(\"Tampered signature incorrectly verified as VALID!\");\n        }\n        Ok(false) => {\n            info!(\"Tampered signature correctly detected as INVALID\");\n        }\n        Err(e) => {\n            error!(\"Verification failed with error: {:?}\", e);\n        }\n    }\n\n    info!(\"=== ECDSA verification example complete ===\");\n\n    loop {\n        cortex_m::asm::wfi();\n    }\n}\n"
  },
  {
    "path": "examples/stm32wba6/src/bin/pka_rsa.rs",
    "content": "//! PKA RSA Encryption/Decryption Example\n//!\n//! Demonstrates RSA encryption and decryption using the PKA hardware accelerator.\n//!\n//! # What This Example Shows\n//! - RSA encryption: ciphertext = plaintext^e mod n\n//! - RSA decryption: plaintext = ciphertext^d mod n\n//! - Using pre-computed Montgomery parameter for faster operations\n//!\n//! # RSA Basics\n//! - Public key: (n, e) where n is the modulus and e is the public exponent\n//! - Private key: d where d*e ≡ 1 (mod φ(n))\n//! - Encryption: C = M^e mod n\n//! - Decryption: M = C^d mod n\n//!\n//! # Note\n//! This example uses a small 512-bit key for demonstration purposes.\n//! In production, use at least 2048-bit keys!\n\n#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_stm32::pka::Pka;\nuse embassy_stm32::rcc::{\n    AHB5Prescaler, AHBPrescaler, APBPrescaler, PllDiv, PllMul, PllPreDiv, PllSource, Sysclk, VoltageScale,\n};\nuse embassy_stm32::{Config, bind_interrupts, peripherals};\nuse {defmt_rtt as _, panic_probe as _};\n\nbind_interrupts!(struct Irqs {\n    PKA => embassy_stm32::pka::InterruptHandler<peripherals::PKA>;\n});\n\n// RSA-2048 test key from STM32Cube HAL (production-grade key size)\n\n// Modulus n (2048 bits = 256 bytes)\nconst RSA_N: [u8; 256] = [\n    0xd7, 0x59, 0xdf, 0x92, 0x03, 0x12, 0x5a, 0x8b, 0xea, 0x06, 0xe2, 0x6d, 0x20, 0xcf, 0x50, 0x4f, 0x45, 0xe6, 0x3d,\n    0x04, 0x7e, 0x0b, 0xed, 0x8b, 0xd7, 0x3d, 0x74, 0xa1, 0x2e, 0xa7, 0xb6, 0x2a, 0xdc, 0xa4, 0x22, 0xbb, 0xac, 0xc8,\n    0xe7, 0x61, 0x2c, 0x5a, 0xbb, 0x01, 0x0d, 0xa0, 0x66, 0x94, 0x53, 0xe9, 0x01, 0xcf, 0x3f, 0xb3, 0x03, 0x05, 0x31,\n    0x00, 0x77, 0xbc, 0xb8, 0x1d, 0x1e, 0x45, 0xe4, 0x2b, 0x95, 0xf1, 0x35, 0xa5, 0x80, 0xc8, 0xeb, 0x3e, 0x5d, 0x5f,\n    0x93, 0x70, 0x6e, 0x5f, 0xad, 0x2c, 0x34, 0xf0, 0xb2, 0xd5, 0xec, 0x31, 0x19, 0x3e, 0x5f, 0xc7, 0xc1, 0x06, 0x9f,\n    0x91, 0x30, 0xea, 0x2b, 0xbe, 0x23, 0xed, 0x71, 0xb7, 0x3f, 0x04, 0xd2, 0xb5, 0x33, 0x8d, 0x5d, 0x48, 0x84, 0x39,\n    0x59, 0xc9, 0xd4, 0x98, 0xbb, 0x7a, 0x5c, 0xd1, 0x90, 0x58, 0x07, 0x68, 0xfa, 0xec, 0x02, 0x92, 0x5a, 0xe3, 0x97,\n    0x6d, 0xca, 0x39, 0x7c, 0x80, 0x34, 0xa5, 0x51, 0xb8, 0x3d, 0x15, 0x7a, 0x82, 0x85, 0x02, 0x54, 0xab, 0x6d, 0x55,\n    0x6a, 0xfd, 0x06, 0xf5, 0x46, 0x38, 0x2e, 0x70, 0x3e, 0x63, 0x19, 0x72, 0xf1, 0xa3, 0xa4, 0x8e, 0xa0, 0xf1, 0x45,\n    0xa1, 0x6f, 0x79, 0xd8, 0xff, 0x3c, 0x5b, 0x51, 0x83, 0x06, 0x3b, 0x11, 0x2c, 0x95, 0xe3, 0x12, 0x4f, 0x39, 0x3a,\n    0xc9, 0x12, 0x3c, 0x39, 0x7b, 0x5c, 0xaf, 0x34, 0x58, 0xc4, 0x17, 0x57, 0xf1, 0x7f, 0x77, 0xe0, 0x94, 0x6a, 0x57,\n    0x16, 0x47, 0x64, 0xea, 0x7e, 0xd8, 0xd3, 0x95, 0x5b, 0xe4, 0x7e, 0x93, 0x9e, 0xef, 0x47, 0x8c, 0x0b, 0x2e, 0x3a,\n    0x7f, 0x79, 0x4e, 0xc8, 0xa7, 0x5a, 0xb8, 0x41, 0xd4, 0xa8, 0x9b, 0xde, 0x52, 0xf7, 0x53, 0xd3, 0xa3, 0x6e, 0x23,\n    0xbb, 0xc4, 0x10, 0x4f, 0x32, 0x9a, 0x03, 0x3c, 0x31,\n];\n\n// Public exponent e (typically 65537 = 0x10001)\nconst RSA_E: [u8; 3] = [0x01, 0x00, 0x01];\n\n// Private exponent d (2048 bits = 256 bytes)\nconst RSA_D: [u8; 256] = [\n    0xa9, 0x60, 0x9c, 0xc1, 0xa0, 0xfc, 0xdc, 0x8e, 0xd3, 0x60, 0xda, 0xd2, 0x6e, 0x4d, 0xe0, 0xa2, 0x99, 0x1d, 0xbf,\n    0xbc, 0x3a, 0xcf, 0x72, 0xe4, 0xdc, 0x44, 0x0f, 0xe9, 0x7e, 0x62, 0x96, 0x9b, 0x1b, 0xb3, 0x55, 0x46, 0x3b, 0x5e,\n    0x40, 0xee, 0x63, 0x0e, 0x71, 0xab, 0x20, 0x66, 0x9a, 0x87, 0xeb, 0x7f, 0x86, 0xd6, 0xd5, 0x09, 0x1d, 0x45, 0x06,\n    0x07, 0x92, 0x25, 0xb2, 0xc1, 0xe4, 0x3f, 0xa0, 0x78, 0xcf, 0x94, 0x4a, 0x57, 0x83, 0xf5, 0x83, 0x61, 0x27, 0xdb,\n    0xb6, 0x81, 0x65, 0xae, 0x86, 0xec, 0x10, 0x2f, 0x88, 0xd9, 0x4c, 0xce, 0x49, 0x46, 0x8f, 0xda, 0xf2, 0xed, 0x1c,\n    0xaf, 0xfb, 0xc3, 0x12, 0xe8, 0x98, 0x25, 0x77, 0x9d, 0x63, 0x49, 0x8d, 0xd8, 0xcb, 0x55, 0x52, 0x9b, 0x68, 0xb4,\n    0x1a, 0xf4, 0xed, 0xeb, 0xba, 0xf9, 0x40, 0xeb, 0xeb, 0x15, 0xf1, 0xae, 0x16, 0x3b, 0xfc, 0x7e, 0x89, 0x90, 0x86,\n    0x66, 0x37, 0xa3, 0xdb, 0xcd, 0x73, 0x68, 0x3f, 0x6d, 0x9c, 0x85, 0x45, 0x5c, 0x21, 0xc6, 0xfe, 0x2d, 0x41, 0x67,\n    0x5c, 0x2f, 0x97, 0x3b, 0x03, 0x17, 0x81, 0x11, 0xd7, 0x1c, 0xf6, 0x6e, 0xf2, 0xd8, 0x90, 0x89, 0x9d, 0xbb, 0x75,\n    0xa7, 0x9f, 0x09, 0x0d, 0x83, 0x13, 0x28, 0xf1, 0x88, 0xe1, 0x48, 0x37, 0x1b, 0x20, 0x8c, 0x86, 0x2f, 0x27, 0x17,\n    0x0b, 0xe6, 0x70, 0x83, 0x1e, 0x69, 0x88, 0x8f, 0x72, 0x18, 0xd7, 0x83, 0x2b, 0xca, 0xab, 0xfe, 0x3c, 0x65, 0x23,\n    0xfe, 0xd0, 0xea, 0xd3, 0x2c, 0xc7, 0x9a, 0x2a, 0x54, 0xef, 0x0b, 0x12, 0xcd, 0x4d, 0xab, 0xf9, 0x9b, 0xa7, 0x66,\n    0x88, 0x3c, 0x7c, 0xfa, 0x2f, 0x19, 0x62, 0x8b, 0x1b, 0xb1, 0xbf, 0x18, 0xb2, 0x1b, 0xc7, 0x64, 0x0e, 0x00, 0xdd,\n    0xf1, 0x01, 0x83, 0x42, 0x9b, 0x0c, 0xe7, 0x57, 0xc9,\n];\n\n#[embassy_executor::main]\nasync fn main(_spawner: embassy_executor::Spawner) {\n    let mut config = Config::default();\n    config.rcc.pll1 = Some(embassy_stm32::rcc::Pll {\n        source: PllSource::HSI,\n        prediv: PllPreDiv::DIV1,\n        mul: PllMul::MUL30,\n        divr: Some(PllDiv::DIV5),\n        divq: None,\n        divp: Some(PllDiv::DIV30),\n        frac: Some(0),\n    });\n    config.rcc.ahb_pre = AHBPrescaler::DIV1;\n    config.rcc.apb1_pre = APBPrescaler::DIV1;\n    config.rcc.apb2_pre = APBPrescaler::DIV1;\n    config.rcc.apb7_pre = APBPrescaler::DIV1;\n    config.rcc.ahb5_pre = AHB5Prescaler::DIV4;\n    config.rcc.voltage_scale = VoltageScale::RANGE1;\n    config.rcc.sys = Sysclk::PLL1_R;\n\n    let p = embassy_stm32::init(config);\n    info!(\"PKA RSA Encryption/Decryption Example\");\n\n    let mut pka = Pka::new_blocking(p.PKA, Irqs);\n\n    // Plaintext message (padded to modulus size)\n    // In real applications, use proper padding like OAEP or PKCS#1 v1.5\n    let mut plaintext = [0u8; 256];\n    // Simple message: \"Hello RSA!\" followed by zeros\n    plaintext[246..256].copy_from_slice(b\"Hello RSA!\");\n\n    info!(\"=== RSA Encryption/Decryption Example ===\");\n    info!(\"Key size: 2048 bits\");\n    info!(\"Plaintext (last 10 bytes): {:02x}\", &plaintext[246..]);\n\n    // Step 1: Compute Montgomery parameter for the modulus\n    // This speeds up subsequent operations with the same modulus\n    info!(\"Computing Montgomery parameter...\");\n    let mut montgomery_param = [0u32; 64]; // 256 bytes / 4 = 64 words\n    match pka.montgomery_param(&RSA_N, &mut montgomery_param) {\n        Ok(()) => {\n            info!(\"Montgomery parameter computed successfully\");\n        }\n        Err(e) => {\n            error!(\"Failed to compute Montgomery parameter: {:?}\", e);\n            loop {\n                cortex_m::asm::wfi();\n            }\n        }\n    }\n\n    // Step 2: Encrypt using public key (n, e)\n    // C = M^e mod n\n    info!(\"=== RSA Encryption ===\");\n    info!(\"Computing ciphertext = plaintext^e mod n\");\n\n    let mut ciphertext = [0u8; 256];\n    match pka.modular_exp(&plaintext, &RSA_E, &RSA_N, &mut ciphertext) {\n        Ok(()) => {\n            info!(\"Encryption successful!\");\n            info!(\"Ciphertext (first 16 bytes): {:02x}\", &ciphertext[..16]);\n        }\n        Err(e) => {\n            error!(\"Encryption failed: {:?}\", e);\n            loop {\n                cortex_m::asm::wfi();\n            }\n        }\n    }\n\n    // Step 3: Decrypt using private key d\n    // M = C^d mod n\n    info!(\"=== RSA Decryption ===\");\n    info!(\"Computing plaintext = ciphertext^d mod n\");\n\n    let mut decrypted = [0u8; 256];\n    match pka.modular_exp(&ciphertext, &RSA_D, &RSA_N, &mut decrypted) {\n        Ok(()) => {\n            info!(\"Decryption successful!\");\n            info!(\"Decrypted (last 10 bytes): {:02x}\", &decrypted[246..]);\n\n            // Verify decryption\n            if decrypted == plaintext {\n                info!(\"Decryption verified: plaintext matches!\");\n            } else {\n                error!(\"Decryption verification FAILED!\");\n            }\n        }\n        Err(e) => {\n            error!(\"Decryption failed: {:?}\", e);\n            loop {\n                cortex_m::asm::wfi();\n            }\n        }\n    }\n\n    // Step 4: Demonstrate fast decryption with pre-computed Montgomery parameter\n    info!(\"=== RSA Fast Decryption (with Montgomery param) ===\");\n\n    let mut decrypted_fast = [0u8; 256];\n    match pka.modular_exp_fast(&ciphertext, &RSA_D, &RSA_N, &montgomery_param, &mut decrypted_fast) {\n        Ok(()) => {\n            info!(\"Fast decryption successful!\");\n            if decrypted_fast == plaintext {\n                info!(\"Fast decryption verified: plaintext matches!\");\n            } else {\n                error!(\"Fast decryption verification FAILED!\");\n            }\n        }\n        Err(e) => {\n            error!(\"Fast decryption failed: {:?}\", e);\n        }\n    }\n\n    info!(\"=== RSA example complete ===\");\n\n    loop {\n        cortex_m::asm::wfi();\n    }\n}\n"
  },
  {
    "path": "examples/stm32wba6/src/bin/pka_rsa_crt.rs",
    "content": "//! PKA RSA-CRT Decryption Example\n//!\n//! Demonstrates RSA decryption using the Chinese Remainder Theorem (CRT)\n//! optimization with the PKA hardware accelerator.\n//!\n//! # What This Example Shows\n//! - CRT-based RSA decryption (approximately 4x faster than standard RSA)\n//! - Computing and using CRT parameters (dp, dq, qinv)\n//! - Comparing performance with standard RSA decryption\n//!\n//! # CRT-RSA Basics\n//! Instead of computing M = C^d mod n directly, CRT computes:\n//! - M1 = C^dp mod p (where dp = d mod (p-1))\n//! - M2 = C^dq mod q (where dq = d mod (q-1))\n//! - M = M2 + q * (qinv * (M1 - M2) mod p)\n//!\n//! This is faster because:\n//! - Exponents dp and dq are half the size of d\n//! - Modular operations use smaller moduli p and q\n//!\n//! # CRT Parameters\n//! - p, q: Prime factors of n\n//! - dp = d mod (p-1)\n//! - dq = d mod (q-1)\n//! - qinv = q^(-1) mod p\n//!\n//! # Note\n//! This example uses a small 512-bit key for demonstration.\n//! In production, use at least 2048-bit keys!\n\n#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_stm32::pka::{Pka, RsaCrtParams};\nuse embassy_stm32::rcc::{\n    AHB5Prescaler, AHBPrescaler, APBPrescaler, PllDiv, PllMul, PllPreDiv, PllSource, Sysclk, VoltageScale,\n};\nuse embassy_stm32::{Config, bind_interrupts, peripherals};\nuse {defmt_rtt as _, panic_probe as _};\n\nbind_interrupts!(struct Irqs {\n    PKA => embassy_stm32::pka::InterruptHandler<peripherals::PKA>;\n});\n\n// RSA-2048 test key from STM32Cube HAL\n\n// Modulus n (2048 bits = 256 bytes)\nconst RSA_N: [u8; 256] = [\n    0xd7, 0x59, 0xdf, 0x92, 0x03, 0x12, 0x5a, 0x8b, 0xea, 0x06, 0xe2, 0x6d, 0x20, 0xcf, 0x50, 0x4f, 0x45, 0xe6, 0x3d,\n    0x04, 0x7e, 0x0b, 0xed, 0x8b, 0xd7, 0x3d, 0x74, 0xa1, 0x2e, 0xa7, 0xb6, 0x2a, 0xdc, 0xa4, 0x22, 0xbb, 0xac, 0xc8,\n    0xe7, 0x61, 0x2c, 0x5a, 0xbb, 0x01, 0x0d, 0xa0, 0x66, 0x94, 0x53, 0xe9, 0x01, 0xcf, 0x3f, 0xb3, 0x03, 0x05, 0x31,\n    0x00, 0x77, 0xbc, 0xb8, 0x1d, 0x1e, 0x45, 0xe4, 0x2b, 0x95, 0xf1, 0x35, 0xa5, 0x80, 0xc8, 0xeb, 0x3e, 0x5d, 0x5f,\n    0x93, 0x70, 0x6e, 0x5f, 0xad, 0x2c, 0x34, 0xf0, 0xb2, 0xd5, 0xec, 0x31, 0x19, 0x3e, 0x5f, 0xc7, 0xc1, 0x06, 0x9f,\n    0x91, 0x30, 0xea, 0x2b, 0xbe, 0x23, 0xed, 0x71, 0xb7, 0x3f, 0x04, 0xd2, 0xb5, 0x33, 0x8d, 0x5d, 0x48, 0x84, 0x39,\n    0x59, 0xc9, 0xd4, 0x98, 0xbb, 0x7a, 0x5c, 0xd1, 0x90, 0x58, 0x07, 0x68, 0xfa, 0xec, 0x02, 0x92, 0x5a, 0xe3, 0x97,\n    0x6d, 0xca, 0x39, 0x7c, 0x80, 0x34, 0xa5, 0x51, 0xb8, 0x3d, 0x15, 0x7a, 0x82, 0x85, 0x02, 0x54, 0xab, 0x6d, 0x55,\n    0x6a, 0xfd, 0x06, 0xf5, 0x46, 0x38, 0x2e, 0x70, 0x3e, 0x63, 0x19, 0x72, 0xf1, 0xa3, 0xa4, 0x8e, 0xa0, 0xf1, 0x45,\n    0xa1, 0x6f, 0x79, 0xd8, 0xff, 0x3c, 0x5b, 0x51, 0x83, 0x06, 0x3b, 0x11, 0x2c, 0x95, 0xe3, 0x12, 0x4f, 0x39, 0x3a,\n    0xc9, 0x12, 0x3c, 0x39, 0x7b, 0x5c, 0xaf, 0x34, 0x58, 0xc4, 0x17, 0x57, 0xf1, 0x7f, 0x77, 0xe0, 0x94, 0x6a, 0x57,\n    0x16, 0x47, 0x64, 0xea, 0x7e, 0xd8, 0xd3, 0x95, 0x5b, 0xe4, 0x7e, 0x93, 0x9e, 0xef, 0x47, 0x8c, 0x0b, 0x2e, 0x3a,\n    0x7f, 0x79, 0x4e, 0xc8, 0xa7, 0x5a, 0xb8, 0x41, 0xd4, 0xa8, 0x9b, 0xde, 0x52, 0xf7, 0x53, 0xd3, 0xa3, 0x6e, 0x23,\n    0xbb, 0xc4, 0x10, 0x4f, 0x32, 0x9a, 0x03, 0x3c, 0x31,\n];\n\n// Public exponent e (typically 65537 = 0x10001)\nconst RSA_E: [u8; 3] = [0x01, 0x00, 0x01];\n\n// Private exponent d (2048 bits = 256 bytes)\nconst RSA_D: [u8; 256] = [\n    0xa9, 0x60, 0x9c, 0xc1, 0xa0, 0xfc, 0xdc, 0x8e, 0xd3, 0x60, 0xda, 0xd2, 0x6e, 0x4d, 0xe0, 0xa2, 0x99, 0x1d, 0xbf,\n    0xbc, 0x3a, 0xcf, 0x72, 0xe4, 0xdc, 0x44, 0x0f, 0xe9, 0x7e, 0x62, 0x96, 0x9b, 0x1b, 0xb3, 0x55, 0x46, 0x3b, 0x5e,\n    0x40, 0xee, 0x63, 0x0e, 0x71, 0xab, 0x20, 0x66, 0x9a, 0x87, 0xeb, 0x7f, 0x86, 0xd6, 0xd5, 0x09, 0x1d, 0x45, 0x06,\n    0x07, 0x92, 0x25, 0xb2, 0xc1, 0xe4, 0x3f, 0xa0, 0x78, 0xcf, 0x94, 0x4a, 0x57, 0x83, 0xf5, 0x83, 0x61, 0x27, 0xdb,\n    0xb6, 0x81, 0x65, 0xae, 0x86, 0xec, 0x10, 0x2f, 0x88, 0xd9, 0x4c, 0xce, 0x49, 0x46, 0x8f, 0xda, 0xf2, 0xed, 0x1c,\n    0xaf, 0xfb, 0xc3, 0x12, 0xe8, 0x98, 0x25, 0x77, 0x9d, 0x63, 0x49, 0x8d, 0xd8, 0xcb, 0x55, 0x52, 0x9b, 0x68, 0xb4,\n    0x1a, 0xf4, 0xed, 0xeb, 0xba, 0xf9, 0x40, 0xeb, 0xeb, 0x15, 0xf1, 0xae, 0x16, 0x3b, 0xfc, 0x7e, 0x89, 0x90, 0x86,\n    0x66, 0x37, 0xa3, 0xdb, 0xcd, 0x73, 0x68, 0x3f, 0x6d, 0x9c, 0x85, 0x45, 0x5c, 0x21, 0xc6, 0xfe, 0x2d, 0x41, 0x67,\n    0x5c, 0x2f, 0x97, 0x3b, 0x03, 0x17, 0x81, 0x11, 0xd7, 0x1c, 0xf6, 0x6e, 0xf2, 0xd8, 0x90, 0x89, 0x9d, 0xbb, 0x75,\n    0xa7, 0x9f, 0x09, 0x0d, 0x83, 0x13, 0x28, 0xf1, 0x88, 0xe1, 0x48, 0x37, 0x1b, 0x20, 0x8c, 0x86, 0x2f, 0x27, 0x17,\n    0x0b, 0xe6, 0x70, 0x83, 0x1e, 0x69, 0x88, 0x8f, 0x72, 0x18, 0xd7, 0x83, 0x2b, 0xca, 0xab, 0xfe, 0x3c, 0x65, 0x23,\n    0xfe, 0xd0, 0xea, 0xd3, 0x2c, 0xc7, 0x9a, 0x2a, 0x54, 0xef, 0x0b, 0x12, 0xcd, 0x4d, 0xab, 0xf9, 0x9b, 0xa7, 0x66,\n    0x88, 0x3c, 0x7c, 0xfa, 0x2f, 0x19, 0x62, 0x8b, 0x1b, 0xb1, 0xbf, 0x18, 0xb2, 0x1b, 0xc7, 0x64, 0x0e, 0x00, 0xdd,\n    0xf1, 0x01, 0x83, 0x42, 0x9b, 0x0c, 0xe7, 0x57, 0xc9,\n];\n\n// CRT Parameters\n\n// Prime p (1024 bits = 128 bytes)\nconst RSA_P: [u8; 128] = [\n    0xfb, 0xe4, 0x71, 0xf9, 0x77, 0x3d, 0xaa, 0x8d, 0xc5, 0x38, 0xdb, 0x2d, 0xa8, 0x9a, 0x4b, 0x94, 0xdb, 0xf5, 0x99,\n    0xf4, 0x89, 0x02, 0x94, 0xbb, 0xc5, 0x74, 0x55, 0x1b, 0xa4, 0x03, 0x62, 0x5d, 0xc5, 0xf0, 0x1b, 0x49, 0xa3, 0xf3,\n    0x1b, 0x47, 0xec, 0x59, 0xf5, 0x1f, 0x01, 0x6b, 0x8a, 0x72, 0xe8, 0x78, 0x9c, 0xc7, 0xa1, 0x12, 0xcc, 0xc1, 0xb3,\n    0x5c, 0xcb, 0xad, 0xa0, 0xca, 0xf5, 0x66, 0x30, 0x2b, 0x01, 0xe9, 0x14, 0x93, 0xf9, 0xd2, 0xb3, 0xd1, 0x50, 0x3d,\n    0xda, 0x47, 0xbe, 0xa9, 0x45, 0x61, 0x52, 0x6c, 0x3d, 0x5a, 0xee, 0x83, 0x72, 0x92, 0xbb, 0x52, 0x22, 0xb5, 0xf8,\n    0xad, 0x03, 0x4d, 0xd1, 0xb4, 0xe0, 0x5a, 0xd3, 0xc4, 0x28, 0xff, 0x1d, 0xcc, 0x67, 0x4e, 0xf3, 0x55, 0xd6, 0x0e,\n    0xd9, 0x0b, 0xd5, 0x72, 0xcc, 0xc0, 0x8e, 0x0e, 0xad, 0x50, 0xd8, 0x93, 0xd6, 0xf3,\n];\n\n// Prime q (1024 bits = 128 bytes)\nconst RSA_Q: [u8; 128] = [\n    0xda, 0xdc, 0xe1, 0xd1, 0x34, 0x4e, 0xec, 0xe4, 0xbe, 0x4b, 0x74, 0xa7, 0xbb, 0x48, 0xee, 0xe0, 0x31, 0xad, 0xde,\n    0xac, 0x04, 0x3c, 0x69, 0x35, 0xb9, 0x53, 0x51, 0x7f, 0x8c, 0xff, 0x3a, 0x75, 0x9e, 0x9b, 0x78, 0xb6, 0xae, 0x81,\n    0xfb, 0xe1, 0x7c, 0xf3, 0xa2, 0x38, 0xcd, 0xd3, 0x48, 0x70, 0x55, 0x60, 0x1a, 0xe7, 0x06, 0xb5, 0xc7, 0x6c, 0xec,\n    0x3d, 0x97, 0xe9, 0xde, 0xe3, 0xc4, 0xb4, 0xad, 0x3d, 0x50, 0x32, 0x75, 0x9a, 0x0c, 0xe0, 0x46, 0xc0, 0x13, 0x3f,\n    0x2c, 0x56, 0x7d, 0xca, 0xae, 0xa8, 0x0c, 0xb1, 0x60, 0xb0, 0x5c, 0xbe, 0xb8, 0x0a, 0xa9, 0x4c, 0x37, 0x93, 0x59,\n    0x78, 0x19, 0xb6, 0x3c, 0x0a, 0x60, 0x51, 0x7f, 0xeb, 0xca, 0x46, 0xe5, 0x24, 0x3c, 0x11, 0xa9, 0x32, 0x4b, 0x5c,\n    0x4f, 0x32, 0x23, 0x99, 0x3e, 0x33, 0x0b, 0xde, 0xab, 0x04, 0xc0, 0x66, 0x71, 0x4b,\n];\n\n// dp = d mod (p-1) (1024 bits = 128 bytes)\nconst RSA_DP: [u8; 128] = [\n    0x80, 0xb0, 0x43, 0x72, 0x59, 0xf3, 0x0d, 0x51, 0x84, 0xb2, 0xf9, 0x77, 0x28, 0x2e, 0x3b, 0xf2, 0xff, 0x35, 0x48,\n    0xc4, 0x5f, 0xc3, 0x0b, 0xcd, 0xaa, 0xa1, 0x36, 0x61, 0xfa, 0xb7, 0x27, 0xe8, 0x14, 0x9a, 0x08, 0xb6, 0xe4, 0xa5,\n    0xed, 0x08, 0x1f, 0xbe, 0x0d, 0xb7, 0x1b, 0x78, 0x9f, 0xb0, 0xf9, 0x07, 0xb5, 0xc1, 0x5f, 0x8f, 0x45, 0x40, 0xa8,\n    0xab, 0xfd, 0xfa, 0xe4, 0xad, 0x0c, 0x16, 0x7b, 0x01, 0x5d, 0xe6, 0x80, 0x76, 0xe1, 0x29, 0x3e, 0x68, 0xef, 0x7f,\n    0xca, 0x26, 0xe1, 0x47, 0x85, 0x84, 0xa5, 0x21, 0x5b, 0x6f, 0x3b, 0x6f, 0xb8, 0x77, 0x32, 0x70, 0x51, 0xff, 0x79,\n    0xde, 0x9a, 0x53, 0x85, 0x91, 0xcd, 0x15, 0x5a, 0x1f, 0x5b, 0x7a, 0x8a, 0xf4, 0xc4, 0xca, 0xd7, 0x12, 0xc1, 0x5b,\n    0xb0, 0x93, 0x95, 0x27, 0x23, 0x68, 0x34, 0xab, 0x56, 0xec, 0x78, 0xd9, 0x7e, 0xf5,\n];\n\n// dq = d mod (q-1) (1024 bits = 128 bytes)\nconst RSA_DQ: [u8; 128] = [\n    0x12, 0x02, 0x95, 0x2e, 0x93, 0x00, 0x5f, 0xac, 0x1f, 0x20, 0xb4, 0x73, 0xcd, 0x0c, 0x9e, 0x63, 0xa2, 0x92, 0xed,\n    0x3c, 0xf8, 0x88, 0x44, 0x1c, 0x20, 0xa9, 0x03, 0x8e, 0xdc, 0x7a, 0x70, 0x44, 0x17, 0x8e, 0x31, 0xab, 0xce, 0xc6,\n    0x71, 0x84, 0xc7, 0xb4, 0x80, 0xc7, 0xed, 0xe0, 0x12, 0x18, 0xf4, 0x5d, 0x99, 0x39, 0x23, 0xab, 0x37, 0xc2, 0xf5,\n    0xd9, 0xc7, 0xb3, 0x7e, 0x1c, 0xfe, 0x25, 0xe4, 0x0f, 0xa4, 0x96, 0xd2, 0x68, 0x9f, 0xe0, 0xa0, 0xd1, 0xd3, 0x83,\n    0xa2, 0x51, 0x67, 0xbe, 0x93, 0x0a, 0xcf, 0x28, 0x95, 0x8d, 0x4d, 0xc4, 0x7f, 0xfe, 0x98, 0x99, 0xe6, 0x04, 0xe1,\n    0x1a, 0xe9, 0xfa, 0xbe, 0x0c, 0x18, 0x8a, 0xfc, 0x5c, 0xd9, 0xe3, 0x65, 0x9d, 0xca, 0xb7, 0xa5, 0x55, 0xb7, 0x2f,\n    0xdc, 0x70, 0x82, 0xcf, 0x6c, 0x77, 0xe4, 0xe5, 0x28, 0xeb, 0x96, 0x2d, 0x97, 0xeb,\n];\n\n// qinv = q^(-1) mod p (1024 bits = 128 bytes)\nconst RSA_QINV: [u8; 128] = [\n    0x94, 0x26, 0x76, 0x23, 0xe9, 0x0e, 0x73, 0xcc, 0xd5, 0x3b, 0x18, 0xa9, 0xaa, 0x3c, 0xb4, 0x1d, 0x71, 0x69, 0x7a,\n    0x8d, 0x5f, 0xb2, 0xfe, 0x90, 0x46, 0x55, 0x4f, 0x71, 0x78, 0x11, 0x90, 0x5f, 0xe5, 0x7f, 0x5b, 0xb3, 0xc3, 0x56,\n    0xf5, 0x9a, 0xeb, 0x09, 0xc2, 0x7f, 0x4c, 0x91, 0xa8, 0x11, 0x60, 0x28, 0x04, 0xce, 0xf7, 0x34, 0x06, 0xc9, 0x22,\n    0xd1, 0x3c, 0x9b, 0x99, 0x72, 0x8c, 0xfb, 0x74, 0x1a, 0x12, 0x9a, 0xf7, 0x9f, 0x59, 0x30, 0xe9, 0x63, 0x7e, 0x05,\n    0x21, 0x03, 0x4c, 0x82, 0x20, 0x58, 0xbc, 0xb3, 0x28, 0xa6, 0x16, 0xb1, 0x99, 0x41, 0x81, 0xe4, 0x32, 0xde, 0x1e,\n    0xa4, 0x2e, 0x2b, 0xff, 0xf5, 0x1b, 0xe3, 0x89, 0xc5, 0x1e, 0xa6, 0x74, 0x8d, 0xa1, 0x49, 0x68, 0x8d, 0x8a, 0xc0,\n    0x7e, 0x87, 0x6c, 0x2d, 0x74, 0x36, 0x4f, 0x36, 0x76, 0xbe, 0x3d, 0xc6, 0x50, 0x7a,\n];\n\n#[embassy_executor::main]\nasync fn main(_spawner: embassy_executor::Spawner) {\n    let mut config = Config::default();\n    config.rcc.pll1 = Some(embassy_stm32::rcc::Pll {\n        source: PllSource::HSI,\n        prediv: PllPreDiv::DIV1,\n        mul: PllMul::MUL30,\n        divr: Some(PllDiv::DIV5),\n        divq: None,\n        divp: Some(PllDiv::DIV30),\n        frac: Some(0),\n    });\n    config.rcc.ahb_pre = AHBPrescaler::DIV1;\n    config.rcc.apb1_pre = APBPrescaler::DIV1;\n    config.rcc.apb2_pre = APBPrescaler::DIV1;\n    config.rcc.apb7_pre = APBPrescaler::DIV1;\n    config.rcc.ahb5_pre = AHB5Prescaler::DIV4;\n    config.rcc.voltage_scale = VoltageScale::RANGE1;\n    config.rcc.sys = Sysclk::PLL1_R;\n\n    let p = embassy_stm32::init(config);\n    info!(\"PKA RSA-CRT Decryption Example\");\n\n    let mut pka = Pka::new_blocking(p.PKA, Irqs);\n\n    info!(\"=== RSA-CRT Decryption Example ===\");\n    info!(\"Key size: 2048 bits\");\n    info!(\"CRT provides ~4x speedup over standard RSA decryption\");\n\n    // Create plaintext message\n    let mut plaintext = [0u8; 256];\n    // Simple test message\n    plaintext[246..256].copy_from_slice(b\"CRT-RSA!!!\");\n\n    info!(\"Original message (last 10 bytes): {:02x}\", &plaintext[246..]);\n\n    // Step 1: Encrypt with public key (standard RSA encryption)\n    info!(\"=== Step 1: RSA Encryption (Public Key) ===\");\n\n    let mut ciphertext = [0u8; 256];\n    match pka.modular_exp(&plaintext, &RSA_E, &RSA_N, &mut ciphertext) {\n        Ok(()) => {\n            info!(\"Encryption successful\");\n            info!(\"Ciphertext (first 16 bytes): {:02x}\", &ciphertext[..16]);\n        }\n        Err(e) => {\n            error!(\"Encryption failed: {:?}\", e);\n            loop {\n                cortex_m::asm::wfi();\n            }\n        }\n    }\n\n    // Step 2: Decrypt using standard RSA (for comparison)\n    info!(\"=== Step 2: Standard RSA Decryption ===\");\n    info!(\"M = C^d mod n (full exponent)\");\n\n    let mut decrypted_standard = [0u8; 256];\n    match pka.modular_exp(&ciphertext, &RSA_D, &RSA_N, &mut decrypted_standard) {\n        Ok(()) => {\n            info!(\"Standard decryption complete\");\n            if decrypted_standard == plaintext {\n                info!(\"Standard decryption verified!\");\n            } else {\n                error!(\"Standard decryption FAILED!\");\n            }\n        }\n        Err(e) => {\n            error!(\"Standard decryption failed: {:?}\", e);\n        }\n    }\n\n    // Step 3: Decrypt using RSA-CRT (faster!)\n    info!(\"=== Step 3: RSA-CRT Decryption ===\");\n    info!(\"Uses smaller exponents dp, dq for ~4x speedup\");\n    info!(\"CRT parameters (all 1024 bits each):\");\n    info!(\"  - p: {} bytes\", RSA_P.len());\n    info!(\"  - q: {} bytes\", RSA_Q.len());\n    info!(\"  - dp = d mod (p-1): {} bytes\", RSA_DP.len());\n    info!(\"  - dq = d mod (q-1): {} bytes\", RSA_DQ.len());\n    info!(\"  - qinv = q^(-1) mod p: {} bytes\", RSA_QINV.len());\n\n    let crt_params = RsaCrtParams {\n        prime_p: &RSA_P,\n        prime_q: &RSA_Q,\n        dp: &RSA_DP,\n        dq: &RSA_DQ,\n        qinv: &RSA_QINV,\n    };\n\n    let mut decrypted_crt = [0u8; 256];\n    match pka.rsa_crt_exp(&ciphertext, &crt_params, &mut decrypted_crt) {\n        Ok(()) => {\n            info!(\"CRT decryption complete\");\n            info!(\"Decrypted (last 10 bytes): {:02x}\", &decrypted_crt[246..]);\n\n            if decrypted_crt == plaintext {\n                info!(\"CRT decryption verified!\");\n                info!(\"CRT result matches original plaintext\");\n            } else {\n                error!(\"CRT decryption verification FAILED!\");\n            }\n        }\n        Err(e) => {\n            error!(\"CRT decryption failed: {:?}\", e);\n            loop {\n                cortex_m::asm::wfi();\n            }\n        }\n    }\n\n    // Step 4: Verify both methods produce the same result\n    info!(\"=== Step 4: Comparing Standard vs CRT Results ===\");\n\n    if decrypted_standard == decrypted_crt {\n        info!(\"Both decryption methods produce identical results!\");\n    } else {\n        error!(\"Standard and CRT results differ!\");\n    }\n\n    // Step 5: Multiple decryption demonstration\n    info!(\"=== Step 5: Multiple CRT Decryptions ===\");\n    info!(\"Demonstrating CRT decryption with different messages\");\n\n    for i in 0..3u8 {\n        // Create different test messages\n        let mut msg = [0u8; 256];\n        msg[255] = i + 1;\n\n        // Encrypt\n        let mut ct = [0u8; 256];\n        if pka.modular_exp(&msg, &RSA_E, &RSA_N, &mut ct).is_ok() {\n            // Decrypt with CRT\n            let mut pt = [0u8; 256];\n            if pka.rsa_crt_exp(&ct, &crt_params, &mut pt).is_ok() {\n                if pt == msg {\n                    info!(\"Message {}: CRT decryption OK\", i + 1);\n                } else {\n                    error!(\"Message {}: CRT decryption FAILED\", i + 1);\n                }\n            }\n        }\n    }\n\n    info!(\"=== RSA-CRT Example Complete ===\");\n    info!(\"Summary:\");\n    info!(\"  - Standard RSA: C^d mod n (2048-bit exponent)\");\n    info!(\"  - CRT RSA: Uses dp, dq (1024-bit exponents)\");\n    info!(\"  - CRT is ~4x faster for private key operations\");\n    info!(\"  - Both methods produce identical results\");\n\n    loop {\n        cortex_m::asm::wfi();\n    }\n}\n"
  },
  {
    "path": "examples/stm32wba6/src/bin/pka_rsa_keygen.rs",
    "content": "//! PKA RSA Key Finalization Example\n//!\n//! Demonstrates RSA key preparation and validation using PKA hardware.\n//!\n//! # What This Example Shows\n//! - Computing the Montgomery parameter R² mod n (required for fast operations)\n//! - Validating RSA key relationships using modular arithmetic\n//! - Using comparison operations for big integer validation\n//!\n//! # RSA Key Components\n//! - n = p * q (modulus, product of two primes)\n//! - e (public exponent, typically 65537)\n//! - d (private exponent, where e*d ≡ 1 mod φ(n))\n//! - φ(n) = (p-1)(q-1) (Euler's totient)\n//!\n//! # Key Finalization Steps\n//! 1. Compute Montgomery parameter R² mod n for fast modular operations\n//! 2. Optionally verify e*d ≡ 1 (mod φ(n))\n//! 3. Pre-compute CRT parameters if using CRT decryption\n//!\n//! # Note\n//! This example uses a small 512-bit key for demonstration.\n//! In production, use at least 2048-bit keys!\n\n#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_stm32::pka::{ComparisonResult, Pka};\nuse embassy_stm32::rcc::{\n    AHB5Prescaler, AHBPrescaler, APBPrescaler, PllDiv, PllMul, PllPreDiv, PllSource, Sysclk, VoltageScale,\n};\nuse embassy_stm32::{Config, bind_interrupts, peripherals};\nuse {defmt_rtt as _, panic_probe as _};\n\nbind_interrupts!(struct Irqs {\n    PKA => embassy_stm32::pka::InterruptHandler<peripherals::PKA>;\n});\n\n// RSA-2048 test key from STM32Cube HAL\n// n = p * q\nconst RSA_N: [u8; 256] = [\n    0xd7, 0x59, 0xdf, 0x92, 0x03, 0x12, 0x5a, 0x8b, 0xea, 0x06, 0xe2, 0x6d, 0x20, 0xcf, 0x50, 0x4f, 0x45, 0xe6, 0x3d,\n    0x04, 0x7e, 0x0b, 0xed, 0x8b, 0xd7, 0x3d, 0x74, 0xa1, 0x2e, 0xa7, 0xb6, 0x2a, 0xdc, 0xa4, 0x22, 0xbb, 0xac, 0xc8,\n    0xe7, 0x61, 0x2c, 0x5a, 0xbb, 0x01, 0x0d, 0xa0, 0x66, 0x94, 0x53, 0xe9, 0x01, 0xcf, 0x3f, 0xb3, 0x03, 0x05, 0x31,\n    0x00, 0x77, 0xbc, 0xb8, 0x1d, 0x1e, 0x45, 0xe4, 0x2b, 0x95, 0xf1, 0x35, 0xa5, 0x80, 0xc8, 0xeb, 0x3e, 0x5d, 0x5f,\n    0x93, 0x70, 0x6e, 0x5f, 0xad, 0x2c, 0x34, 0xf0, 0xb2, 0xd5, 0xec, 0x31, 0x19, 0x3e, 0x5f, 0xc7, 0xc1, 0x06, 0x9f,\n    0x91, 0x30, 0xea, 0x2b, 0xbe, 0x23, 0xed, 0x71, 0xb7, 0x3f, 0x04, 0xd2, 0xb5, 0x33, 0x8d, 0x5d, 0x48, 0x84, 0x39,\n    0x59, 0xc9, 0xd4, 0x98, 0xbb, 0x7a, 0x5c, 0xd1, 0x90, 0x58, 0x07, 0x68, 0xfa, 0xec, 0x02, 0x92, 0x5a, 0xe3, 0x97,\n    0x6d, 0xca, 0x39, 0x7c, 0x80, 0x34, 0xa5, 0x51, 0xb8, 0x3d, 0x15, 0x7a, 0x82, 0x85, 0x02, 0x54, 0xab, 0x6d, 0x55,\n    0x6a, 0xfd, 0x06, 0xf5, 0x46, 0x38, 0x2e, 0x70, 0x3e, 0x63, 0x19, 0x72, 0xf1, 0xa3, 0xa4, 0x8e, 0xa0, 0xf1, 0x45,\n    0xa1, 0x6f, 0x79, 0xd8, 0xff, 0x3c, 0x5b, 0x51, 0x83, 0x06, 0x3b, 0x11, 0x2c, 0x95, 0xe3, 0x12, 0x4f, 0x39, 0x3a,\n    0xc9, 0x12, 0x3c, 0x39, 0x7b, 0x5c, 0xaf, 0x34, 0x58, 0xc4, 0x17, 0x57, 0xf1, 0x7f, 0x77, 0xe0, 0x94, 0x6a, 0x57,\n    0x16, 0x47, 0x64, 0xea, 0x7e, 0xd8, 0xd3, 0x95, 0x5b, 0xe4, 0x7e, 0x93, 0x9e, 0xef, 0x47, 0x8c, 0x0b, 0x2e, 0x3a,\n    0x7f, 0x79, 0x4e, 0xc8, 0xa7, 0x5a, 0xb8, 0x41, 0xd4, 0xa8, 0x9b, 0xde, 0x52, 0xf7, 0x53, 0xd3, 0xa3, 0x6e, 0x23,\n    0xbb, 0xc4, 0x10, 0x4f, 0x32, 0x9a, 0x03, 0x3c, 0x31,\n];\n\n// Prime p (1024 bits = 128 bytes)\nconst RSA_P: [u8; 128] = [\n    0xfb, 0xe4, 0x71, 0xf9, 0x77, 0x3d, 0xaa, 0x8d, 0xc5, 0x38, 0xdb, 0x2d, 0xa8, 0x9a, 0x4b, 0x94, 0xdb, 0xf5, 0x99,\n    0xf4, 0x89, 0x02, 0x94, 0xbb, 0xc5, 0x74, 0x55, 0x1b, 0xa4, 0x03, 0x62, 0x5d, 0xc5, 0xf0, 0x1b, 0x49, 0xa3, 0xf3,\n    0x1b, 0x47, 0xec, 0x59, 0xf5, 0x1f, 0x01, 0x6b, 0x8a, 0x72, 0xe8, 0x78, 0x9c, 0xc7, 0xa1, 0x12, 0xcc, 0xc1, 0xb3,\n    0x5c, 0xcb, 0xad, 0xa0, 0xca, 0xf5, 0x66, 0x30, 0x2b, 0x01, 0xe9, 0x14, 0x93, 0xf9, 0xd2, 0xb3, 0xd1, 0x50, 0x3d,\n    0xda, 0x47, 0xbe, 0xa9, 0x45, 0x61, 0x52, 0x6c, 0x3d, 0x5a, 0xee, 0x83, 0x72, 0x92, 0xbb, 0x52, 0x22, 0xb5, 0xf8,\n    0xad, 0x03, 0x4d, 0xd1, 0xb4, 0xe0, 0x5a, 0xd3, 0xc4, 0x28, 0xff, 0x1d, 0xcc, 0x67, 0x4e, 0xf3, 0x55, 0xd6, 0x0e,\n    0xd9, 0x0b, 0xd5, 0x72, 0xcc, 0xc0, 0x8e, 0x0e, 0xad, 0x50, 0xd8, 0x93, 0xd6, 0xf3,\n];\n\n// Prime q (1024 bits = 128 bytes)\nconst RSA_Q: [u8; 128] = [\n    0xda, 0xdc, 0xe1, 0xd1, 0x34, 0x4e, 0xec, 0xe4, 0xbe, 0x4b, 0x74, 0xa7, 0xbb, 0x48, 0xee, 0xe0, 0x31, 0xad, 0xde,\n    0xac, 0x04, 0x3c, 0x69, 0x35, 0xb9, 0x53, 0x51, 0x7f, 0x8c, 0xff, 0x3a, 0x75, 0x9e, 0x9b, 0x78, 0xb6, 0xae, 0x81,\n    0xfb, 0xe1, 0x7c, 0xf3, 0xa2, 0x38, 0xcd, 0xd3, 0x48, 0x70, 0x55, 0x60, 0x1a, 0xe7, 0x06, 0xb5, 0xc7, 0x6c, 0xec,\n    0x3d, 0x97, 0xe9, 0xde, 0xe3, 0xc4, 0xb4, 0xad, 0x3d, 0x50, 0x32, 0x75, 0x9a, 0x0c, 0xe0, 0x46, 0xc0, 0x13, 0x3f,\n    0x2c, 0x56, 0x7d, 0xca, 0xae, 0xa8, 0x0c, 0xb1, 0x60, 0xb0, 0x5c, 0xbe, 0xb8, 0x0a, 0xa9, 0x4c, 0x37, 0x93, 0x59,\n    0x78, 0x19, 0xb6, 0x3c, 0x0a, 0x60, 0x51, 0x7f, 0xeb, 0xca, 0x46, 0xe5, 0x24, 0x3c, 0x11, 0xa9, 0x32, 0x4b, 0x5c,\n    0x4f, 0x32, 0x23, 0x99, 0x3e, 0x33, 0x0b, 0xde, 0xab, 0x04, 0xc0, 0x66, 0x71, 0x4b,\n];\n\n// Public exponent e = 65537\nconst RSA_E: [u8; 3] = [0x01, 0x00, 0x01];\n\n// Private exponent d\nconst RSA_D: [u8; 256] = [\n    0xa9, 0x60, 0x9c, 0xc1, 0xa0, 0xfc, 0xdc, 0x8e, 0xd3, 0x60, 0xda, 0xd2, 0x6e, 0x4d, 0xe0, 0xa2, 0x99, 0x1d, 0xbf,\n    0xbc, 0x3a, 0xcf, 0x72, 0xe4, 0xdc, 0x44, 0x0f, 0xe9, 0x7e, 0x62, 0x96, 0x9b, 0x1b, 0xb3, 0x55, 0x46, 0x3b, 0x5e,\n    0x40, 0xee, 0x63, 0x0e, 0x71, 0xab, 0x20, 0x66, 0x9a, 0x87, 0xeb, 0x7f, 0x86, 0xd6, 0xd5, 0x09, 0x1d, 0x45, 0x06,\n    0x07, 0x92, 0x25, 0xb2, 0xc1, 0xe4, 0x3f, 0xa0, 0x78, 0xcf, 0x94, 0x4a, 0x57, 0x83, 0xf5, 0x83, 0x61, 0x27, 0xdb,\n    0xb6, 0x81, 0x65, 0xae, 0x86, 0xec, 0x10, 0x2f, 0x88, 0xd9, 0x4c, 0xce, 0x49, 0x46, 0x8f, 0xda, 0xf2, 0xed, 0x1c,\n    0xaf, 0xfb, 0xc3, 0x12, 0xe8, 0x98, 0x25, 0x77, 0x9d, 0x63, 0x49, 0x8d, 0xd8, 0xcb, 0x55, 0x52, 0x9b, 0x68, 0xb4,\n    0x1a, 0xf4, 0xed, 0xeb, 0xba, 0xf9, 0x40, 0xeb, 0xeb, 0x15, 0xf1, 0xae, 0x16, 0x3b, 0xfc, 0x7e, 0x89, 0x90, 0x86,\n    0x66, 0x37, 0xa3, 0xdb, 0xcd, 0x73, 0x68, 0x3f, 0x6d, 0x9c, 0x85, 0x45, 0x5c, 0x21, 0xc6, 0xfe, 0x2d, 0x41, 0x67,\n    0x5c, 0x2f, 0x97, 0x3b, 0x03, 0x17, 0x81, 0x11, 0xd7, 0x1c, 0xf6, 0x6e, 0xf2, 0xd8, 0x90, 0x89, 0x9d, 0xbb, 0x75,\n    0xa7, 0x9f, 0x09, 0x0d, 0x83, 0x13, 0x28, 0xf1, 0x88, 0xe1, 0x48, 0x37, 0x1b, 0x20, 0x8c, 0x86, 0x2f, 0x27, 0x17,\n    0x0b, 0xe6, 0x70, 0x83, 0x1e, 0x69, 0x88, 0x8f, 0x72, 0x18, 0xd7, 0x83, 0x2b, 0xca, 0xab, 0xfe, 0x3c, 0x65, 0x23,\n    0xfe, 0xd0, 0xea, 0xd3, 0x2c, 0xc7, 0x9a, 0x2a, 0x54, 0xef, 0x0b, 0x12, 0xcd, 0x4d, 0xab, 0xf9, 0x9b, 0xa7, 0x66,\n    0x88, 0x3c, 0x7c, 0xfa, 0x2f, 0x19, 0x62, 0x8b, 0x1b, 0xb1, 0xbf, 0x18, 0xb2, 0x1b, 0xc7, 0x64, 0x0e, 0x00, 0xdd,\n    0xf1, 0x01, 0x83, 0x42, 0x9b, 0x0c, 0xe7, 0x57, 0xc9,\n];\n\n#[embassy_executor::main]\nasync fn main(_spawner: embassy_executor::Spawner) {\n    let mut config = Config::default();\n    config.rcc.pll1 = Some(embassy_stm32::rcc::Pll {\n        source: PllSource::HSI,\n        prediv: PllPreDiv::DIV1,\n        mul: PllMul::MUL30,\n        divr: Some(PllDiv::DIV5),\n        divq: None,\n        divp: Some(PllDiv::DIV30),\n        frac: Some(0),\n    });\n    config.rcc.ahb_pre = AHBPrescaler::DIV1;\n    config.rcc.apb1_pre = APBPrescaler::DIV1;\n    config.rcc.apb2_pre = APBPrescaler::DIV1;\n    config.rcc.apb7_pre = APBPrescaler::DIV1;\n    config.rcc.ahb5_pre = AHB5Prescaler::DIV4;\n    config.rcc.voltage_scale = VoltageScale::RANGE1;\n    config.rcc.sys = Sysclk::PLL1_R;\n\n    let p = embassy_stm32::init(config);\n    info!(\"PKA RSA Key Finalization Example\");\n\n    let mut pka = Pka::new_blocking(p.PKA, Irqs);\n\n    info!(\"=== RSA Key Finalization Example ===\");\n    info!(\"Key size: 2048 bits\");\n\n    // Step 1: Compute Montgomery parameter R² mod n\n    // This is essential for fast modular exponentiation\n    info!(\"=== Step 1: Computing Montgomery Parameter ===\");\n    info!(\"R² mod n is required for fast modular operations\");\n\n    let mut montgomery_param = [0u32; 64]; // 64 bytes / 4 = 16 words\n    match pka.montgomery_param(&RSA_N, &mut montgomery_param) {\n        Ok(()) => {\n            info!(\"Montgomery parameter computed successfully\");\n            info!(\n                \"First 4 words: {:08x} {:08x} {:08x} {:08x}\",\n                montgomery_param[0], montgomery_param[1], montgomery_param[2], montgomery_param[3]\n            );\n        }\n        Err(e) => {\n            error!(\"Failed to compute Montgomery parameter: {:?}\", e);\n            loop {\n                cortex_m::asm::wfi();\n            }\n        }\n    }\n\n    // Step 2: Verify n = p * q using arithmetic multiplication\n    info!(\"=== Step 2: Verifying n = p * q ===\");\n\n    let mut computed_n = [0u8; 256];\n    match pka.arithmetic_mul(&RSA_P, &RSA_Q, &mut computed_n) {\n        Ok(()) => {\n            info!(\"Computed p * q\");\n\n            // Compare with known n\n            match pka.comparison(&computed_n, &RSA_N) {\n                Ok(ComparisonResult::Equal) => {\n                    info!(\"Verified: p * q = n\");\n                }\n                Ok(result) => {\n                    error!(\"Verification FAILED: p * q != n (result: {:?})\", result);\n                }\n                Err(e) => {\n                    error!(\"Comparison failed: {:?}\", e);\n                }\n            }\n        }\n        Err(e) => {\n            error!(\"Multiplication failed: {:?}\", e);\n        }\n    }\n\n    // Step 3: Validate that d < n (private exponent must be less than modulus)\n    info!(\"=== Step 3: Validating d < n ===\");\n\n    match pka.comparison(&RSA_D, &RSA_N) {\n        Ok(ComparisonResult::Less) => {\n            info!(\"Verified: d < n (private exponent is valid)\");\n        }\n        Ok(ComparisonResult::Equal) => {\n            error!(\"Invalid: d = n (should not happen)\");\n        }\n        Ok(ComparisonResult::Greater) => {\n            error!(\"Invalid: d > n (private exponent too large)\");\n        }\n        Err(e) => {\n            error!(\"Comparison failed: {:?}\", e);\n        }\n    }\n\n    // Step 4: Test encryption/decryption to verify key pair\n    info!(\"=== Step 4: Verifying Key Pair with Test Encryption ===\");\n\n    // Small test message\n    let mut test_message = [0u8; 256];\n    test_message[63] = 0x42; // Simple test value\n\n    // Encrypt with public key\n    let mut ciphertext = [0u8; 256];\n    match pka.modular_exp_fast(&test_message, &RSA_E, &RSA_N, &montgomery_param, &mut ciphertext) {\n        Ok(()) => {\n            info!(\"Test encryption successful\");\n        }\n        Err(e) => {\n            error!(\"Test encryption failed: {:?}\", e);\n            loop {\n                cortex_m::asm::wfi();\n            }\n        }\n    }\n\n    // Decrypt with private key\n    let mut decrypted = [0u8; 256];\n    match pka.modular_exp_fast(&ciphertext, &RSA_D, &RSA_N, &montgomery_param, &mut decrypted) {\n        Ok(()) => {\n            if decrypted == test_message {\n                info!(\"Key pair verification PASSED!\");\n                info!(\"Encryption and decryption work correctly\");\n            } else {\n                error!(\"Key pair verification FAILED!\");\n                error!(\"Decrypted message does not match original\");\n            }\n        }\n        Err(e) => {\n            error!(\"Test decryption failed: {:?}\", e);\n        }\n    }\n\n    // Step 5: Demonstrate modular inverse (useful for computing d from e)\n    info!(\"=== Step 5: Modular Inverse Demonstration ===\");\n    info!(\"Computing modular inverse of a test value\");\n\n    // Compute inverse of 7 mod 11 (should be 8, since 7*8 = 56 = 5*11 + 1)\n    let a: [u8; 1] = [7];\n    let modulus: [u8; 1] = [11];\n    let mut inverse = [0u8; 1];\n\n    match pka.modular_inv(&a, &modulus, &mut inverse) {\n        Ok(()) => {\n            info!(\"Inverse of 7 mod 11 = {}\", inverse[0]);\n            if inverse[0] == 8 {\n                info!(\"Modular inverse verified correct!\");\n            }\n        }\n        Err(e) => {\n            error!(\"Modular inverse failed: {:?}\", e);\n        }\n    }\n\n    // Step 6: Demonstrate modular reduction\n    info!(\"=== Step 6: Modular Reduction ===\");\n\n    // Reduce a large number mod n\n    let large_num = [0xFFu8; 64]; // All 1s\n    let mut reduced = [0u8; 256];\n\n    match pka.modular_red(&large_num, &RSA_N, &mut reduced) {\n        Ok(()) => {\n            info!(\"Modular reduction successful\");\n            info!(\"Result (first 8 bytes): {:02x}\", &reduced[..8]);\n        }\n        Err(e) => {\n            error!(\"Modular reduction failed: {:?}\", e);\n        }\n    }\n\n    info!(\"=== RSA Key Finalization Example Complete ===\");\n    info!(\"Summary:\");\n    info!(\"  - Montgomery parameter: computed\");\n    info!(\"  - n = p * q: verified\");\n    info!(\"  - d < n: verified\");\n    info!(\"  - Key pair: verified\");\n\n    loop {\n        cortex_m::asm::wfi();\n    }\n}\n"
  },
  {
    "path": "examples/stm32wba6/src/bin/pwm.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse defmt_rtt as _; // global logger\nuse embassy_executor::Spawner;\nuse embassy_stm32::Config;\nuse embassy_stm32::gpio::OutputType;\nuse embassy_stm32::rcc::{\n    AHB5Prescaler, AHBPrescaler, APBPrescaler, PllDiv, PllMul, PllPreDiv, PllSource, Sysclk, VoltageScale,\n};\nuse embassy_stm32::time::khz;\nuse embassy_stm32::timer::simple_pwm::{PwmPin, SimplePwm};\nuse embassy_time::Timer;\nuse panic_probe as _;\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    info!(\"Hello World!\");\n\n    let mut config = Config::default();\n    // Fine-tune PLL1 dividers/multipliers\n    config.rcc.pll1 = Some(embassy_stm32::rcc::Pll {\n        source: PllSource::HSI,\n        prediv: PllPreDiv::DIV1,  // PLLM = 1 → HSI / 1 = 16 MHz\n        mul: PllMul::MUL30,       // PLLN = 30 → 16 MHz * 30 = 480 MHz VCO\n        divr: Some(PllDiv::DIV5), // PLLR = 5 → 96 MHz (Sysclk)\n        // divq: Some(PllDiv::DIV10), // PLLQ = 10 → 48 MHz (NOT USED)\n        divq: None,\n        divp: Some(PllDiv::DIV30), // PLLP = 30 → 16 MHz (USBOTG)\n        frac: Some(0),             // Fractional part (enabled)\n    });\n\n    config.rcc.ahb_pre = AHBPrescaler::DIV1;\n    config.rcc.apb1_pre = APBPrescaler::DIV1;\n    config.rcc.apb2_pre = APBPrescaler::DIV1;\n    config.rcc.apb7_pre = APBPrescaler::DIV1;\n    config.rcc.ahb5_pre = AHB5Prescaler::DIV4;\n\n    // voltage scale for max performance\n    config.rcc.voltage_scale = VoltageScale::RANGE1;\n    // route PLL1_P into the USB‐OTG‐HS block\n    config.rcc.sys = Sysclk::PLL1_R;\n\n    let p = embassy_stm32::init(config);\n\n    let ch1_pin = PwmPin::new(p.PA2, OutputType::PushPull);\n    let mut pwm = SimplePwm::new(p.TIM3, Some(ch1_pin), None, None, None, khz(10), Default::default());\n    let mut ch1 = pwm.ch1();\n    ch1.enable();\n\n    info!(\"PWM initialized\");\n    info!(\"PWM max duty {}\", ch1.max_duty_cycle());\n\n    loop {\n        ch1.set_duty_cycle_fully_off();\n        Timer::after_millis(300).await;\n        ch1.set_duty_cycle_fraction(1, 4);\n        Timer::after_millis(300).await;\n        ch1.set_duty_cycle_fraction(1, 2);\n        Timer::after_millis(300).await;\n        ch1.set_duty_cycle(ch1.max_duty_cycle() - 1);\n        Timer::after_millis(300).await;\n    }\n}\n"
  },
  {
    "path": "examples/stm32wba6/src/bin/rng.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_stm32::rcc::{\n    AHB5Prescaler, AHBPrescaler, APBPrescaler, PllDiv, PllMul, PllPreDiv, PllSource, Sysclk, VoltageScale, mux,\n};\nuse embassy_stm32::rng::Rng;\nuse embassy_stm32::{Config, bind_interrupts, peripherals, rng};\nuse embassy_time::Timer;\nuse {defmt_rtt as _, panic_probe as _};\n\nbind_interrupts!(struct Irqs {\n    RNG => rng::InterruptHandler<peripherals::RNG>;\n});\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let mut config = Config::default();\n\n    // Configure PLL1 (required on WBA)\n    config.rcc.pll1 = Some(embassy_stm32::rcc::Pll {\n        source: PllSource::HSI,\n        prediv: PllPreDiv::DIV1,  // PLLM = 1 → HSI / 1 = 16 MHz\n        mul: PllMul::MUL30,       // PLLN = 30 → 16 MHz * 30 = 480 MHz VCO\n        divr: Some(PllDiv::DIV5), // PLLR = 5 → 96 MHz (Sysclk)\n        divq: None,\n        divp: Some(PllDiv::DIV30), // PLLP = 30 → 16 MHz (required for SAI)\n        frac: Some(0),\n    });\n\n    config.rcc.ahb_pre = AHBPrescaler::DIV1;\n    config.rcc.apb1_pre = APBPrescaler::DIV1;\n    config.rcc.apb2_pre = APBPrescaler::DIV1;\n    config.rcc.apb7_pre = APBPrescaler::DIV1;\n    config.rcc.ahb5_pre = AHB5Prescaler::DIV4;\n    config.rcc.voltage_scale = VoltageScale::RANGE1;\n    config.rcc.sys = Sysclk::PLL1_R;\n\n    // Configure RNG clock source to HSI (required for WBA)\n    config.rcc.mux.rngsel = mux::Rngsel::HSI;\n\n    let p = embassy_stm32::init(config);\n    info!(\"STM32WBA65 RNG Test\");\n    info!(\"Initializing RNG...\");\n\n    // Initialize RNG - this will trigger the reset() function\n    // which previously hung at line 144\n    let mut rng = Rng::new(p.RNG, Irqs);\n    info!(\"RNG initialized successfully!\");\n\n    // Test 1: Generate random bytes using async method\n    info!(\"\\n=== Test 1: Async random bytes ===\");\n    let mut buf = [0u8; 16];\n    match rng.async_fill_bytes(&mut buf).await {\n        Ok(_) => info!(\"Generated 16 random bytes: {:02x}\", buf),\n        Err(e) => error!(\"Error generating random bytes: {:?}\", e),\n    }\n\n    // Test 2: Generate multiple u32 values using blocking method\n    info!(\"\\n=== Test 2: Blocking u32 generation ===\");\n    for i in 0..5 {\n        let random = rng.next_u32();\n        info!(\"Random u32 #{}: 0x{:08x} ({})\", i + 1, random, random);\n        Timer::after_millis(100).await;\n    }\n\n    // Test 3: Generate u64 values\n    info!(\"\\n=== Test 3: u64 generation ===\");\n    for i in 0..3 {\n        let random = rng.next_u64();\n        info!(\"Random u64 #{}: 0x{:016x}\", i + 1, random);\n        Timer::after_millis(100).await;\n    }\n\n    // Test 4: Fill buffer using blocking method\n    info!(\"\\n=== Test 4: Blocking buffer fill ===\");\n    let mut buf2 = [0u8; 32];\n    rng.fill_bytes(&mut buf2);\n    info!(\"Generated 32 random bytes:\");\n    info!(\"  {:02x}\", &buf2[0..16]);\n    info!(\"  {:02x}\", &buf2[16..32]);\n\n    // Test 5: Continuous generation loop\n    info!(\"\\n=== Test 5: Continuous generation (10 samples) ===\");\n    for i in 0..10 {\n        let random = rng.next_u32();\n        info!(\"Sample #{}: 0x{:08x}\", i + 1, random);\n        Timer::after_millis(200).await;\n    }\n\n    info!(\"\\nAll RNG tests completed successfully!\");\n    info!(\"The RNG is working correctly on STM32WBA65.\");\n\n    loop {\n        Timer::after_secs(1).await;\n    }\n}\n"
  },
  {
    "path": "examples/stm32wba6/src/bin/rtc.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_stm32::Config;\nuse embassy_stm32::rcc::*;\nuse embassy_stm32::rtc::{DateTime, DayOfWeek, Rtc, RtcConfig};\nuse embassy_time::Timer;\nuse {defmt_rtt as _, panic_probe as _};\n\npub fn pll_init(config: &mut Config) {\n    config.rcc.pll1 = Some(embassy_stm32::rcc::Pll {\n        source: PllSource::HSI,\n        prediv: PllPreDiv::DIV1,  // PLLM = 1 → HSI / 1 = 16 MHz\n        mul: PllMul::MUL30,       // PLLN = 30 → 16 MHz * 30 = 480 MHz VCO\n        divr: Some(PllDiv::DIV5), // PLLR = 5 → 96 MHz (Sysclk)\n        // divq: Some(PllDiv::DIV10), // PLLQ = 10 → 48 MHz (NOT USED)\n        divq: None,\n        divp: Some(PllDiv::DIV30), // PLLP = 30 → 16 MHz (USBOTG)\n        frac: Some(0),             // Fractional part (enabled)\n    });\n\n    config.rcc.ahb_pre = AHBPrescaler::DIV1;\n    config.rcc.apb1_pre = APBPrescaler::DIV1;\n    config.rcc.apb2_pre = APBPrescaler::DIV1;\n    config.rcc.apb7_pre = APBPrescaler::DIV1;\n    config.rcc.ahb5_pre = AHB5Prescaler::DIV4;\n\n    // voltage scale for max performance\n    config.rcc.voltage_scale = VoltageScale::RANGE1;\n    // route PLL1_P into the USB‐OTG‐HS block\n    config.rcc.sys = Sysclk::PLL1_R;\n}\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let mut config = Config::default();\n\n    pll_init(&mut config);\n\n    let p = embassy_stm32::init(config);\n\n    let mut rtc = Rtc::new(p.RTC, RtcConfig::default());\n\n    // Setting datetime\n    let initial_datetime = DateTime::from(1970, 1, 1, DayOfWeek::Thursday, 0, 00, 00, 0).unwrap();\n    match rtc.0.set_datetime(initial_datetime) {\n        Ok(()) => info!(\"RTC set successfully.\"),\n        Err(e) => error!(\"Failed to set RTC date/time: {:?}\", e),\n    }\n\n    // Reading datetime every 1s\n    loop {\n        match rtc.1.now() {\n            Ok(result) => info!(\"{}\", result),\n            Err(e) => error!(\"Failed to set RTC date/time: {:?}\", e),\n        }\n\n        Timer::after_millis(1000).await;\n    }\n}\n"
  },
  {
    "path": "examples/stm32wba6/src/bin/saes_ecb.rs",
    "content": "//! SAES-ECB (Secure AES - Electronic Codebook) Mode Example\n//!\n//! Demonstrates SAES-ECB encryption and decryption.\n//!\n//! # SAES Key Protection\n//!\n//! SAES applies a hardware RNG mask to stored key registers so that software\n//! cannot read back the key from memory. When doing a crypto operation the\n//! hardware removes the mask transparently before using the key.\n//!\n//! Implication for test vectors:\n//! - 128-bit: the mask is typically all-zeros immediately after reset (the RNG\n//!   has not yet warmed up), so SAES-128 coincidentally matches AES-128 NIST\n//!   vectors. This is not guaranteed across all devices or boot conditions.\n//! - 256-bit: the mask for the extended key registers (KEYR4-7) is non-zero\n//!   once the RNG has accumulated entropy, so SAES-256 output is DEVICE-SPECIFIC\n//!   and will NOT match AES-256 NIST vectors. Encrypt/decrypt are still correct\n//!   inverses of each other on the same device.\n//!\n//! # What This Example Exercises\n//! - SAES peripheral initialisation (busy-wait + RNG auto-enable on WBA6)\n//! - Key loading and KEYVALID wait\n//! - 128-bit ECB encrypt/decrypt round-trip + coincidental NIST vector check\n//! - 256-bit ECB encrypt/decrypt round-trip (device-specific ciphertext)\n//! - CBC decrypt, which exercises the key-derivation (MODE=1) path\n\n#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_stm32::rcc::{\n    AHB5Prescaler, AHBPrescaler, APBPrescaler, PllDiv, PllMul, PllPreDiv, PllSource, Sysclk, VoltageScale,\n};\nuse embassy_stm32::saes::{AesCbc, AesEcb, Direction, Saes};\nuse embassy_stm32::{Config, bind_interrupts, peripherals};\nuse {defmt_rtt as _, panic_probe as _};\n\nbind_interrupts!(struct Irqs {\n    SAES => embassy_stm32::saes::InterruptHandler<peripherals::SAES>;\n});\n\n#[embassy_executor::main]\nasync fn main(_spawner: embassy_executor::Spawner) {\n    let mut config = Config::default();\n    config.rcc.pll1 = Some(embassy_stm32::rcc::Pll {\n        source: PllSource::HSI,\n        prediv: PllPreDiv::DIV1,\n        mul: PllMul::MUL30,\n        divr: Some(PllDiv::DIV5),\n        divq: None,\n        divp: Some(PllDiv::DIV30),\n        frac: Some(0),\n    });\n    config.rcc.ahb_pre = AHBPrescaler::DIV1;\n    config.rcc.apb1_pre = APBPrescaler::DIV1;\n    config.rcc.apb2_pre = APBPrescaler::DIV1;\n    config.rcc.apb7_pre = APBPrescaler::DIV1;\n    config.rcc.ahb5_pre = AHB5Prescaler::DIV4;\n    config.rcc.voltage_scale = VoltageScale::RANGE1;\n    config.rcc.sys = Sysclk::PLL1_R;\n\n    let p = embassy_stm32::init(config);\n    info!(\"SAES-ECB Example\");\n\n    let mut saes = Saes::new_blocking(p.SAES, Irqs);\n\n    // ─── AES-128-ECB round-trip + NIST sanity check ──────────────────────────\n    // NIST SP 800-38A F.1.1: key = 2b7e151628aed2a6abf7158809cf4f3c\n    info!(\"=== SAES-ECB 128-bit ===\");\n    let key_128 = [\n        0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6, 0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf, 0x4f, 0x3c,\n    ];\n    let plaintext_128 = [\n        0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96, 0xe9, 0x3d, 0x7e, 0x11, 0x73, 0x93, 0x17, 0x2a,\n    ];\n    // Expected if RNG mask is all-zeros (normal at early boot)\n    let nist_ct_128 = [\n        0x3a, 0xd7, 0x7b, 0xb4, 0x0d, 0x7a, 0x36, 0x60, 0xa8, 0x9e, 0xca, 0xf3, 0x24, 0x66, 0xef, 0x97,\n    ];\n\n    let cipher = AesEcb::new(&key_128);\n    let mut ctx = saes.start(&cipher, Direction::Encrypt);\n    let mut ct_128 = [0u8; 16];\n    match saes.payload_blocking(&mut ctx, &plaintext_128, &mut ct_128, true) {\n        Ok(()) => {\n            info!(\"Plaintext:  {:02x}\", plaintext_128);\n            info!(\"Ciphertext: {:02x}\", ct_128);\n            if ct_128 == nist_ct_128 {\n                info!(\"✓ Matches NIST vector (RNG mask = zero at boot)\");\n            } else {\n                info!(\"  No NIST match — RNG mask non-zero; round-trip test continues\");\n            }\n        }\n        Err(e) => error!(\"Encrypt error: {:?}\", e),\n    }\n    saes.finish_blocking(ctx).ok();\n\n    // Decrypt: exercises the key-derivation fix (MODE=KEY_DERIVATION for ECB decrypt)\n    let cipher = AesEcb::new(&key_128);\n    let mut ctx = saes.start(&cipher, Direction::Decrypt);\n    let mut recovered_128 = [0u8; 16];\n    match saes.payload_blocking(&mut ctx, &ct_128, &mut recovered_128, true) {\n        Ok(()) => {\n            info!(\"Recovered:  {:02x}\", recovered_128);\n            if recovered_128 == plaintext_128 {\n                info!(\"✓ 128-bit round-trip PASSED\");\n            } else {\n                error!(\"✗ 128-bit round-trip FAILED\");\n            }\n        }\n        Err(e) => error!(\"Decrypt error: {:?}\", e),\n    }\n    saes.finish_blocking(ctx).ok();\n\n    // ─── AES-256-ECB round-trip (device-specific ciphertext) ─────────────────\n    // SAES-256 output is device-specific — the RNG mask for KEYR4-7 is non-zero\n    // once the RNG has warmed up. We verify encrypt/decrypt consistency, not NIST.\n    info!(\"=== SAES-ECB 256-bit ===\");\n    let key_256 = [\n        0x60, 0x3d, 0xeb, 0x10, 0x15, 0xca, 0x71, 0xbe, 0x2b, 0x73, 0xae, 0xf0, 0x85, 0x7d, 0x77, 0x81u8, 0x1f, 0x35,\n        0x2c, 0x07, 0x3b, 0x61, 0x08, 0xd7, 0x2d, 0x98, 0x10, 0xa3, 0x09, 0x14, 0xdf, 0xf4,\n    ];\n    let plaintext_256 = [\n        0xf6, 0x9f, 0x24, 0x45, 0xdf, 0x4f, 0x9b, 0x17, 0xad, 0x2b, 0x41, 0x7b, 0xe6, 0x6c, 0x37, 0x10u8,\n    ];\n\n    let cipher = AesEcb::new(&key_256);\n    let mut ctx = saes.start(&cipher, Direction::Encrypt);\n    let mut ct_256 = [0u8; 16];\n    match saes.payload_blocking(&mut ctx, &plaintext_256, &mut ct_256, true) {\n        Ok(()) => info!(\"Ciphertext: {:02x}\", ct_256),\n        Err(e) => error!(\"Encrypt error: {:?}\", e),\n    }\n    saes.finish_blocking(ctx).ok();\n\n    // Decrypt: exercises 256-bit key derivation\n    let cipher = AesEcb::new(&key_256);\n    let mut ctx = saes.start(&cipher, Direction::Decrypt);\n    let mut recovered_256 = [0u8; 16];\n    match saes.payload_blocking(&mut ctx, &ct_256, &mut recovered_256, true) {\n        Ok(()) => {\n            info!(\"Recovered:  {:02x}\", recovered_256);\n            if recovered_256 == plaintext_256 {\n                info!(\"✓ 256-bit round-trip PASSED\");\n            } else {\n                error!(\"✗ 256-bit round-trip FAILED\");\n            }\n        }\n        Err(e) => error!(\"Decrypt error: {:?}\", e),\n    }\n    saes.finish_blocking(ctx).ok();\n\n    // ─── AES-128-CBC round-trip (also exercises key derivation for decrypt) ───\n    info!(\"=== SAES-CBC 128-bit ===\");\n    let iv = [\n        0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0fu8,\n    ];\n    let plaintext_cbc = [\n        0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96, 0xe9, 0x3d, 0x7e, 0x11, 0x73, 0x93, 0x17, 0x2au8,\n    ];\n\n    let cipher = AesCbc::new(&key_128, &iv);\n    let mut ctx = saes.start(&cipher, Direction::Encrypt);\n    let mut ct_cbc = [0u8; 16];\n    saes.payload_blocking(&mut ctx, &plaintext_cbc, &mut ct_cbc, true).ok();\n    saes.finish_blocking(ctx).ok();\n    info!(\"Ciphertext: {:02x}\", ct_cbc);\n\n    let cipher = AesCbc::new(&key_128, &iv);\n    let mut ctx = saes.start(&cipher, Direction::Decrypt);\n    let mut recovered_cbc = [0u8; 16];\n    match saes.payload_blocking(&mut ctx, &ct_cbc, &mut recovered_cbc, true) {\n        Ok(()) => {\n            info!(\"Recovered:  {:02x}\", recovered_cbc);\n            if recovered_cbc == plaintext_cbc {\n                info!(\"✓ CBC 128-bit round-trip PASSED\");\n            } else {\n                error!(\"✗ CBC 128-bit round-trip FAILED\");\n            }\n        }\n        Err(e) => error!(\"CBC Decrypt error: {:?}\", e),\n    }\n    saes.finish_blocking(ctx).ok();\n\n    info!(\"=== SAES-ECB complete ===\");\n    loop {\n        cortex_m::asm::wfi();\n    }\n}\n"
  },
  {
    "path": "examples/stm32wba6/src/bin/saes_gcm.rs",
    "content": "//! SAES-GCM (Secure AES - Galois/Counter Mode) Example\n//!\n//! # Known Limitation: GCM with AAD on SAES v1a (WBA6x)\n//!\n//! SAES GCM without AAD produces consistent encrypt/decrypt authentication tags\n//! and is tested here. However, when AAD (Additional Authenticated Data) is\n//! provided, encrypt and decrypt produce different authentication tags on\n//! SAES v1a devices (STM32WBA6x). The plaintext is recovered correctly but\n//! the AEAD authentication property does not hold.\n//!\n//! **If you need authenticated GCM with AAD, use the plain AES peripheral.**\n//!\n//! The root cause appears to be a SAES v1a hardware behaviour in the GCM\n//! header phase that has not been documented by ST and cannot be worked around\n//! in the driver.\n//!\n//! # What This Example Exercises\n//! - SAES peripheral initialisation (busy-wait + RNG auto-enable on WBA6)\n//! - GCM mode detection via chmod_bits() (the IV-length heuristic fix)\n//! - GCMPH sequencing: init (0) → payload (2) → final (3) (no AAD)\n//! - CCF polling via ISR register\n//! - No-AAD GCM round-trip: encrypt tag == decrypt tag ✓\n\n#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_stm32::rcc::{\n    AHB5Prescaler, AHBPrescaler, APBPrescaler, PllDiv, PllMul, PllPreDiv, PllSource, Sysclk, VoltageScale,\n};\nuse embassy_stm32::saes::{AesGcm, Direction, Saes};\nuse embassy_stm32::{Config, bind_interrupts, peripherals};\nuse {defmt_rtt as _, panic_probe as _};\n\nbind_interrupts!(struct Irqs {\n    SAES => embassy_stm32::saes::InterruptHandler<peripherals::SAES>;\n});\n\n#[embassy_executor::main]\nasync fn main(_spawner: embassy_executor::Spawner) {\n    let mut config = Config::default();\n    config.rcc.pll1 = Some(embassy_stm32::rcc::Pll {\n        source: PllSource::HSI,\n        prediv: PllPreDiv::DIV1,\n        mul: PllMul::MUL30,\n        divr: Some(PllDiv::DIV5),\n        divq: None,\n        divp: Some(PllDiv::DIV30),\n        frac: Some(0),\n    });\n    config.rcc.ahb_pre = AHBPrescaler::DIV1;\n    config.rcc.apb1_pre = APBPrescaler::DIV1;\n    config.rcc.apb2_pre = APBPrescaler::DIV1;\n    config.rcc.apb7_pre = APBPrescaler::DIV1;\n    config.rcc.ahb5_pre = AHB5Prescaler::DIV4;\n    config.rcc.voltage_scale = VoltageScale::RANGE1;\n    config.rcc.sys = Sysclk::PLL1_R;\n\n    let p = embassy_stm32::init(config);\n    info!(\"SAES-GCM Example\");\n\n    let mut saes = Saes::new_blocking(p.SAES, Irqs);\n\n    // ─── GCM round-trip: no AAD, 16-byte plaintext ───────────────────────────\n    // Uses zero key, IV, and plaintext for a clean baseline test.\n    // Ciphertext and tag are device-specific (SAES key mask is non-zero).\n    info!(\"=== SAES-GCM 128-bit (no AAD) ===\");\n    let key = [0u8; 16];\n    let iv = [0u8; 12];\n    let pt = [0u8; 16];\n\n    let cipher = AesGcm::new(&key, &iv);\n    let mut ctx = saes.start(&cipher, Direction::Encrypt);\n    let mut ct = [0u8; 16];\n    match saes.payload_blocking(&mut ctx, &pt, &mut ct, true) {\n        Ok(()) => info!(\"Ciphertext: {:02x}\", ct),\n        Err(e) => {\n            error!(\"✗ Encrypt failed: {:?}\", e);\n            loop {\n                cortex_m::asm::wfi();\n            }\n        }\n    }\n    let enc_tag = match saes.finish_blocking(ctx) {\n        Ok(Some(tag)) => {\n            info!(\"Encrypt tag: {:02x}\", tag);\n            tag\n        }\n        _ => {\n            error!(\"✗ No tag\");\n            loop {\n                cortex_m::asm::wfi();\n            }\n        }\n    };\n\n    let cipher = AesGcm::new(&key, &iv);\n    let mut ctx = saes.start(&cipher, Direction::Decrypt);\n    let mut recovered = [0u8; 16];\n    match saes.payload_blocking(&mut ctx, &ct, &mut recovered, true) {\n        Ok(()) => info!(\"Recovered:  {:02x}\", recovered),\n        Err(e) => error!(\"✗ Decrypt failed: {:?}\", e),\n    }\n    match saes.finish_blocking(ctx) {\n        Ok(Some(dec_tag)) => {\n            info!(\"Decrypt tag: {:02x}\", dec_tag);\n            if recovered == pt && dec_tag == enc_tag {\n                info!(\"✓ GCM no-AAD round-trip PASSED (plaintext + tag both match)\");\n            } else {\n                if recovered != pt {\n                    error!(\"✗ Plaintext mismatch\");\n                }\n                if dec_tag != enc_tag {\n                    error!(\"✗ Tag mismatch\");\n                }\n            }\n        }\n        Ok(None) => error!(\"✗ No tag returned\"),\n        Err(e) => error!(\"✗ Finish failed: {:?}\", e),\n    }\n\n    // ─── GCM round-trip: larger plaintext, no AAD ────────────────────────────\n    info!(\"=== SAES-GCM 128-bit (no AAD, 60-byte PT) ===\");\n    let key2 = [\n        0xfe, 0xff, 0xe9, 0x92, 0x86, 0x65, 0x73, 0x1c, 0x6d, 0x6a, 0x8f, 0x94, 0x67, 0x30, 0x83, 0x08u8,\n    ];\n    let iv2 = [0xca, 0xfe, 0xba, 0xbe, 0xfa, 0xce, 0xdb, 0xad, 0xde, 0xca, 0xf8, 0x88u8];\n    let pt2 = [\n        0xd9, 0x31, 0x32, 0x25, 0xf8, 0x84, 0x06, 0xe5, 0xa5, 0x59, 0x09, 0xc5, 0xaf, 0xf5, 0x26, 0x9a, 0x86, 0xa7,\n        0xa9, 0x53, 0x15, 0x34, 0xf7, 0xda, 0x2e, 0x4c, 0x30, 0x3d, 0x8a, 0x31, 0x8a, 0x72, 0x1c, 0x3c, 0x0c, 0x95,\n        0x95, 0x68, 0x09, 0x53, 0x2f, 0xcf, 0x0e, 0x24, 0x49, 0xa6, 0xb5, 0x25, 0xb1, 0x6a, 0xed, 0xf5, 0xaa, 0x0d,\n        0xe6, 0x57, 0xba, 0x63, 0x7b, 0x39u8,\n    ];\n\n    let cipher = AesGcm::new(&key2, &iv2);\n    let mut ctx = saes.start(&cipher, Direction::Encrypt);\n    let mut ct2 = [0u8; 60];\n    saes.payload_blocking(&mut ctx, &pt2, &mut ct2, true).ok();\n    let enc_tag2 = match saes.finish_blocking(ctx) {\n        Ok(Some(tag)) => {\n            info!(\"Encrypt tag: {:02x}\", tag);\n            tag\n        }\n        _ => {\n            error!(\"✗ Encrypt failed\");\n            loop {\n                cortex_m::asm::wfi();\n            }\n        }\n    };\n\n    let cipher = AesGcm::new(&key2, &iv2);\n    let mut ctx = saes.start(&cipher, Direction::Decrypt);\n    let mut recovered2 = [0u8; 60];\n    saes.payload_blocking(&mut ctx, &ct2, &mut recovered2, true).ok();\n    match saes.finish_blocking(ctx) {\n        Ok(Some(dec_tag)) => {\n            if recovered2 == pt2 && dec_tag == enc_tag2 {\n                info!(\"✓ GCM 60-byte no-AAD round-trip PASSED\");\n            } else {\n                if recovered2 != pt2 {\n                    error!(\"✗ Plaintext mismatch\");\n                }\n                if dec_tag != enc_tag2 {\n                    error!(\"✗ Tag mismatch\");\n                }\n            }\n        }\n        _ => error!(\"✗ Decrypt failed\"),\n    }\n\n    info!(\"=== SAES-GCM complete ===\");\n    loop {\n        cortex_m::asm::wfi();\n    }\n}\n"
  },
  {
    "path": "examples/stm32wba6/src/bin/sdmmc_sai.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_stm32::gpio::{Level, Output, Speed};\nuse embassy_stm32::sai::{self, Sai};\nuse embassy_stm32::spi::{self, Spi};\nuse embassy_stm32::time::Hertz;\nuse embassy_stm32::{Config, bind_interrupts, dma, peripherals};\nuse embedded_hal_bus::spi::{ExclusiveDevice, NoDelay};\nuse embedded_sdmmc::filesystem::ShortFileName;\nuse embedded_sdmmc::{BlockDevice, RawFile, SdCard, TimeSource, VolumeIdx, VolumeManager};\nuse static_cell::StaticCell;\nuse {defmt_rtt as _, panic_probe as _};\n\n// Simple SD card audio streaming example for SAI.\n// - Supports raw unsigned 16-bit PCM (.pcm)\n// - Supports 16-bit mono WAV at 48 kHz (.wav)\n\nconst VOLUME_NUM: u32 = 3;\nconst VOLUME_DEN: u32 = 4;\n\nfn scale_u16(sample: u16) -> u16 {\n    ((sample as u32) * VOLUME_NUM / VOLUME_DEN) as u16\n}\n\ntype SdSpiDev = ExclusiveDevice<\n    Spi<'static, embassy_stm32::mode::Async, embassy_stm32::spi::mode::Master>,\n    Output<'static>,\n    NoDelay,\n>;\n\ntype SdCardDev = SdCard<SdSpiDev, embassy_time::Delay>;\n\nstruct DummyTimesource;\nimpl embedded_sdmmc::TimeSource for DummyTimesource {\n    fn get_timestamp(&self) -> embedded_sdmmc::Timestamp {\n        embedded_sdmmc::Timestamp {\n            year_since_1970: 0,\n            zero_indexed_month: 0,\n            zero_indexed_day: 0,\n            hours: 0,\n            minutes: 0,\n            seconds: 0,\n        }\n    }\n}\n\nstruct WavInfo {\n    sample_rate: u32,\n    channels: u16,\n    bits_per_sample: u16,\n    data_offset: usize,\n}\n\nfn parse_wav_header(buf: &[u8]) -> Option<WavInfo> {\n    if buf.len() < 44 {\n        return None;\n    }\n    if &buf[0..4] != b\"RIFF\" || &buf[8..12] != b\"WAVE\" {\n        return None;\n    }\n\n    let mut fmt: Option<(u16, u16, u32, u16)> = None;\n    let mut data_offset: Option<usize> = None;\n\n    let mut off = 12usize;\n    while off + 8 <= buf.len() {\n        let chunk_id = &buf[off..off + 4];\n        let chunk_size = u32::from_le_bytes([buf[off + 4], buf[off + 5], buf[off + 6], buf[off + 7]]) as usize;\n        let chunk_data = off + 8;\n\n        if chunk_id == b\"fmt \" && chunk_data + 16 <= buf.len() {\n            let audio_format = u16::from_le_bytes([buf[chunk_data], buf[chunk_data + 1]]);\n            let channels = u16::from_le_bytes([buf[chunk_data + 2], buf[chunk_data + 3]]);\n            let sample_rate = u32::from_le_bytes([\n                buf[chunk_data + 4],\n                buf[chunk_data + 5],\n                buf[chunk_data + 6],\n                buf[chunk_data + 7],\n            ]);\n            let bits_per_sample = u16::from_le_bytes([buf[chunk_data + 14], buf[chunk_data + 15]]);\n            fmt = Some((audio_format, channels, sample_rate, bits_per_sample));\n        }\n\n        if chunk_id == b\"data\" {\n            data_offset = Some(chunk_data);\n            break;\n        }\n\n        let mut next = chunk_data.saturating_add(chunk_size);\n        if next % 2 != 0 {\n            next += 1;\n        }\n        if next <= off {\n            break;\n        }\n        off = next;\n    }\n\n    let (audio_format, channels, sample_rate, bits_per_sample) = fmt?;\n    let data_offset = data_offset?;\n\n    if audio_format != 1 {\n        return None;\n    }\n\n    Some(WavInfo {\n        sample_rate,\n        channels,\n        bits_per_sample,\n        data_offset,\n    })\n}\n\nasync fn write_samples(sai_tx: &mut Sai<'static, peripherals::SAI1, u16>, samples: &[u16]) {\n    if samples.is_empty() {\n        return;\n    }\n    if let Err(e) = sai_tx.write(samples).await {\n        warn!(\"SAI write error: {:?}\", defmt::Debug2Format(&e));\n    }\n}\n\nasync fn play_pcm<D, T, const MAX_DIRS: usize, const MAX_FILES: usize, const MAX_VOLUMES: usize>(\n    sai_tx: &mut Sai<'static, peripherals::SAI1, u16>,\n    volume_mgr: &mut VolumeManager<D, T, MAX_DIRS, MAX_FILES, MAX_VOLUMES>,\n    file: RawFile,\n) where\n    D: BlockDevice,\n    T: TimeSource,\n{\n    info!(\"Playing PCM file\");\n\n    let mut buf = [0u8; 512];\n    let mut out = [0u16; 256];\n\n    loop {\n        let n = match volume_mgr.read(file, &mut buf) {\n            Ok(n) => n,\n            Err(e) => {\n                warn!(\"SD read error: {:?}\", defmt::Debug2Format(&e));\n                break;\n            }\n        };\n        if n == 0 {\n            break;\n        }\n\n        let mut count = 0usize;\n        let mut i = 0usize;\n        while i + 1 < n && count < out.len() {\n            let sample = u16::from_le_bytes([buf[i], buf[i + 1]]);\n            out[count] = scale_u16(sample);\n            count += 1;\n            i += 2;\n        }\n\n        write_samples(sai_tx, &out[..count]).await;\n    }\n}\n\nasync fn play_wav<D, T, const MAX_DIRS: usize, const MAX_FILES: usize, const MAX_VOLUMES: usize>(\n    sai_tx: &mut Sai<'static, peripherals::SAI1, u16>,\n    volume_mgr: &mut VolumeManager<D, T, MAX_DIRS, MAX_FILES, MAX_VOLUMES>,\n    file: RawFile,\n) where\n    D: BlockDevice,\n    T: TimeSource,\n{\n    let mut header = [0u8; 512];\n    let header_len = match volume_mgr.read(file, &mut header) {\n        Ok(n) => n,\n        Err(e) => {\n            warn!(\"SD read error: {:?}\", defmt::Debug2Format(&e));\n            return;\n        }\n    };\n\n    let info = match parse_wav_header(&header[..header_len]) {\n        Some(info) => info,\n        None => {\n            warn!(\"Invalid WAV header\");\n            return;\n        }\n    };\n\n    if info.sample_rate != 48_000 || info.channels != 1 || info.bits_per_sample != 16 {\n        warn!(\n            \"Unsupported WAV format: {} Hz, {} ch, {} bits\",\n            info.sample_rate, info.channels, info.bits_per_sample\n        );\n        return;\n    }\n\n    info!(\"Playing WAV file: 48 kHz mono 16-bit\");\n\n    let mut out = [0u16; 256];\n    let mut buf = [0u8; 512];\n\n    if info.data_offset > header_len {\n        let mut remaining = info.data_offset - header_len;\n        while remaining > 0 {\n            let to_read = remaining.min(buf.len());\n            let n = match volume_mgr.read(file, &mut buf[..to_read]) {\n                Ok(n) => n,\n                Err(e) => {\n                    warn!(\"SD read error: {:?}\", defmt::Debug2Format(&e));\n                    return;\n                }\n            };\n            if n == 0 {\n                warn!(\"Unexpected end of file while seeking data\");\n                return;\n            }\n            remaining -= n;\n        }\n    } else if info.data_offset < header_len {\n        let mut i = info.data_offset;\n        let mut count = 0usize;\n        while i + 1 < header_len && count < out.len() {\n            let signed = i16::from_le_bytes([header[i], header[i + 1]]);\n            let unsigned = (signed as i32 + 0x8000) as u16;\n            out[count] = scale_u16(unsigned);\n            count += 1;\n            i += 2;\n        }\n        write_samples(sai_tx, &out[..count]).await;\n    }\n\n    loop {\n        let n = match volume_mgr.read(file, &mut buf) {\n            Ok(n) => n,\n            Err(e) => {\n                warn!(\"SD read error: {:?}\", defmt::Debug2Format(&e));\n                break;\n            }\n        };\n        if n == 0 {\n            break;\n        }\n\n        let mut count = 0usize;\n        let mut i = 0usize;\n        while i + 1 < n && count < out.len() {\n            let signed = i16::from_le_bytes([buf[i], buf[i + 1]]);\n            let unsigned = (signed as i32 + 0x8000) as u16;\n            out[count] = scale_u16(unsigned);\n            count += 1;\n            i += 2;\n        }\n\n        write_samples(sai_tx, &out[..count]).await;\n    }\n}\n\nbind_interrupts!(struct Irqs {\n    GPDMA1_CHANNEL0 => dma::InterruptHandler<peripherals::GPDMA1_CH0>;\n    GPDMA1_CHANNEL1 => dma::InterruptHandler<peripherals::GPDMA1_CH1>;\n    GPDMA1_CHANNEL2 => dma::InterruptHandler<peripherals::GPDMA1_CH2>;\n});\n\n#[embassy_executor::main]\nasync fn main(spawner: Spawner) {\n    let mut config = Config::default();\n    {\n        use embassy_stm32::rcc::*;\n        config.rcc.pll1 = Some(Pll {\n            source: PllSource::HSI,\n            prediv: PllPreDiv::DIV1,\n            mul: PllMul::MUL12,\n            divq: Some(PllDiv::DIV4),\n            divr: Some(PllDiv::DIV5),\n            divp: Some(PllDiv::DIV30),\n            frac: Some(2363),\n        });\n\n        config.rcc.ahb_pre = AHBPrescaler::DIV1;\n        config.rcc.apb1_pre = APBPrescaler::DIV1;\n        config.rcc.apb2_pre = APBPrescaler::DIV1;\n        config.rcc.apb7_pre = APBPrescaler::DIV1;\n        config.rcc.ahb5_pre = AHB5Prescaler::DIV2;\n        config.rcc.voltage_scale = VoltageScale::RANGE1;\n\n        config.rcc.mux.sai1sel = mux::Sai1sel::PLL1_Q;\n    }\n\n    let p = embassy_stm32::init(config);\n\n    info!(\"SDMMC SAI example\");\n\n    let (sai_a, _sai_b) = sai::split_subblocks(p.SAI1);\n\n    static SAI_DMA_BUF: StaticCell<[u16; 4096]> = StaticCell::new();\n    let sai_dma_buf = SAI_DMA_BUF.init([0u16; 4096]);\n\n    let mut sai_cfg = sai::Config::default();\n    sai_cfg.mode = sai::Mode::Master;\n    sai_cfg.tx_rx = sai::TxRx::Transmitter;\n    sai_cfg.stereo_mono = sai::StereoMono::Mono;\n    sai_cfg.data_size = sai::DataSize::Data16;\n    sai_cfg.bit_order = sai::BitOrder::MsbFirst;\n    sai_cfg.slot_size = sai::SlotSize::Channel32;\n    sai_cfg.slot_count = sai::word::U4(2);\n    sai_cfg.slot_enable = 0b11;\n    sai_cfg.first_bit_offset = sai::word::U5(0);\n    sai_cfg.frame_sync_polarity = sai::FrameSyncPolarity::ActiveLow;\n    sai_cfg.frame_sync_offset = sai::FrameSyncOffset::BeforeFirstBit;\n    sai_cfg.frame_length = 32;\n    sai_cfg.frame_sync_active_level_length = sai::word::U7(16);\n    sai_cfg.fifo_threshold = sai::FifoThreshold::Quarter;\n    sai_cfg.master_clock_divider = sai::MasterClockDivider::DIV4;\n\n    let mut sai_tx = Sai::new_asynchronous(sai_a, p.PA7, p.PB14, p.PA8, p.GPDMA1_CH2, sai_dma_buf, Irqs, sai_cfg);\n\n    let _max98357a_sd = Output::new(p.PA1, Level::High, Speed::Low);\n\n    let mut spi_cfg = spi::Config::default();\n    spi_cfg.frequency = Hertz(400_000);\n    let spi = Spi::new(p.SPI1, p.PB4, p.PA15, p.PB3, p.GPDMA1_CH0, p.GPDMA1_CH1, Irqs, spi_cfg);\n    let cs = Output::new(p.PA6, Level::High, Speed::VeryHigh);\n    let spi_dev: SdSpiDev = ExclusiveDevice::new_no_delay(spi, cs).unwrap();\n    let sd: SdCardDev = SdCard::new(spi_dev, embassy_time::Delay);\n\n    embassy_time::Timer::after_millis(50).await;\n    sd.spi(|dev| {\n        let dummy = [0xFFu8; 10];\n        let _ = dev.bus_mut().write(&dummy);\n    });\n\n    info!(\"SD size {} bytes\", sd.num_bytes().unwrap_or(0));\n\n    let mut spi_cfg2 = spi::Config::default();\n    spi_cfg2.frequency = Hertz(8_000_000);\n    let _ = sd.spi(|dev| dev.bus_mut().set_config(&spi_cfg2));\n\n    static VOLUME_MANAGER: StaticCell<VolumeManager<SdCardDev, DummyTimesource>> = StaticCell::new();\n    let vol_mgr: &'static mut VolumeManager<SdCardDev, DummyTimesource> =\n        VOLUME_MANAGER.init(VolumeManager::new(sd, DummyTimesource));\n\n    let raw_vol = match vol_mgr.open_raw_volume(VolumeIdx(0)) {\n        Ok(v) => v,\n        Err(e) => {\n            warn!(\"SD open_raw_volume error: {:?}\", defmt::Debug2Format(&e));\n            return;\n        }\n    };\n    let raw_root = match vol_mgr.open_root_dir(raw_vol) {\n        Ok(r) => r,\n        Err(e) => {\n            warn!(\"SD open_root_dir error: {:?}\", defmt::Debug2Format(&e));\n            return;\n        }\n    };\n\n    let mut names: heapless::Vec<ShortFileName, 16> = heapless::Vec::new();\n    let _ = vol_mgr.iterate_dir(raw_root, |de| {\n        if !de.attributes.is_directory() {\n            let ext = de.name.extension();\n            if ext == b\"PCM\" || ext == b\"WAV\" {\n                let _ = names.push(de.name.clone());\n            }\n        }\n    });\n\n    if names.is_empty() {\n        warn!(\"No .pcm or .wav files in SD root\");\n        return;\n    }\n\n    let name = &names[0];\n    info!(\"Playing {}\", defmt::Debug2Format(name));\n\n    let file = match vol_mgr.open_file_in_dir(raw_root, name, embedded_sdmmc::Mode::ReadOnly) {\n        Ok(f) => f,\n        Err(e) => {\n            warn!(\"SD open_file error: {:?}\", defmt::Debug2Format(&e));\n            return;\n        }\n    };\n\n    let raw_file = file;\n    if name.extension() == b\"PCM\" {\n        play_pcm(&mut sai_tx, vol_mgr, raw_file).await;\n    } else {\n        play_wav(&mut sai_tx, vol_mgr, raw_file).await;\n    }\n\n    let _ = vol_mgr.close_file(file);\n\n    let _ = spawner;\n}\n"
  },
  {
    "path": "examples/stm32wba6/src/bin/usb_hs_serial.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse defmt::{panic, *};\nuse embassy_executor::Spawner;\nuse embassy_futures::join::join;\nuse embassy_stm32::usb::{Driver, Instance};\nuse embassy_stm32::{Config, bind_interrupts, peripherals, usb};\nuse embassy_usb::Builder;\nuse embassy_usb::class::cdc_acm::{CdcAcmClass, State};\nuse embassy_usb::driver::EndpointError;\nuse {defmt_rtt as _, panic_probe as _};\n\nbind_interrupts!(struct Irqs {\n    USB_OTG_HS => usb::InterruptHandler<peripherals::USB_OTG_HS>;\n});\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    info!(\"Hello World!\");\n\n    let mut config = Config::default();\n\n    {\n        use embassy_stm32::rcc::*;\n        config.rcc.pll1 = Some(Pll {\n            source: PllSource::HSI,\n            prediv: PllPreDiv::DIV1,   // PLLM = 1 → HSI / 1 = 16 MHz\n            mul: PllMul::MUL30,        // PLLN = 30 → 16 MHz * 30 = 480 MHz VCO\n            divr: Some(PllDiv::DIV5),  // PLLR = 5 → 96 MHz (Sysclk)\n            divq: Some(PllDiv::DIV10), // PLLQ = 10 → 48 MHz\n            divp: Some(PllDiv::DIV30), // PLLP = 30 → 16 MHz (USB_OTG_HS)\n            frac: Some(0),             // Fractional part (disabled)\n        });\n\n        config.rcc.ahb_pre = AHBPrescaler::DIV1;\n        config.rcc.apb1_pre = APBPrescaler::DIV1;\n        config.rcc.apb2_pre = APBPrescaler::DIV1;\n        config.rcc.apb7_pre = APBPrescaler::DIV1;\n        config.rcc.ahb5_pre = AHB5Prescaler::DIV4;\n\n        config.rcc.voltage_scale = VoltageScale::RANGE1;\n        config.rcc.mux.otghssel = mux::Otghssel::PLL1_P;\n        config.rcc.sys = Sysclk::PLL1_R;\n    }\n\n    let p = embassy_stm32::init(config);\n\n    // Create the driver, from the HAL.\n    let mut ep_out_buffer = [0u8; 256];\n    let mut config = embassy_stm32::usb::Config::default();\n    // Do not enable vbus_detection. This is a safe default that works in all boards.\n    // However, if your USB device is self-powered (can stay powered on if USB is unplugged), you need\n    // to enable vbus_detection to comply with the USB spec. If you enable it, the board\n    // has to support it or USB won't work at all. See docs on `vbus_detection` for details.\n    config.vbus_detection = false;\n    let driver = Driver::new_hs(p.USB_OTG_HS, Irqs, p.PD6, p.PD7, &mut ep_out_buffer, config);\n\n    // Create embassy-usb Config\n    let mut config = embassy_usb::Config::new(0xc0de, 0xcafe);\n    config.manufacturer = Some(\"Embassy\");\n    config.product = Some(\"USB-serial example\");\n    config.serial_number = Some(\"12345678\");\n\n    // Create embassy-usb DeviceBuilder using the driver and config.\n    // It needs some buffers for building the descriptors.\n    let mut config_descriptor = [0; 256];\n    let mut bos_descriptor = [0; 256];\n    let mut control_buf = [0; 64];\n\n    let mut state = State::new();\n\n    let mut builder = Builder::new(\n        driver,\n        config,\n        &mut config_descriptor,\n        &mut bos_descriptor,\n        &mut [], // no msos descriptors\n        &mut control_buf,\n    );\n\n    // Create classes on the builder.\n    let mut class = CdcAcmClass::new(&mut builder, &mut state, 64);\n\n    // Build the builder.\n    let mut usb = builder.build();\n\n    // Run the USB device.\n    let usb_fut = usb.run();\n\n    // Do stuff with the class!\n    let echo_fut = async {\n        loop {\n            class.wait_connection().await;\n            info!(\"Connected\");\n            let _ = echo(&mut class).await;\n            info!(\"Disconnected\");\n        }\n    };\n\n    // Run everything concurrently.\n    // If we had made everything `'static` above instead, we could do this using separate tasks instead.\n    join(usb_fut, echo_fut).await;\n}\n\nstruct Disconnected {}\n\nimpl From<EndpointError> for Disconnected {\n    fn from(val: EndpointError) -> Self {\n        match val {\n            EndpointError::BufferOverflow => panic!(\"Buffer overflow\"),\n            EndpointError::Disabled => Disconnected {},\n        }\n    }\n}\n\nasync fn echo<'d, T: Instance + 'd>(class: &mut CdcAcmClass<'d, Driver<'d, T>>) -> Result<(), Disconnected> {\n    let mut buf = [0; 64];\n    loop {\n        let n = class.read_packet(&mut buf).await?;\n        let data = &buf[..n];\n        info!(\"data: {:x}\", data);\n        class.write_packet(data).await?;\n    }\n}\n"
  },
  {
    "path": "examples/stm32wba6/src/bin/wwdg.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_stm32::Config;\nuse embassy_stm32::rcc::{\n    AHB5Prescaler, AHBPrescaler, APBPrescaler, PllDiv, PllMul, PllPreDiv, PllSource, Sysclk, VoltageScale,\n};\nuse embassy_stm32::wdg::WindowWatchdog;\nuse embassy_time::Timer;\nuse {defmt_rtt as _, panic_probe as _};\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let mut config = Config::default();\n    // Fine-tune PLL1 dividers/multipliers\n    config.rcc.pll1 = Some(embassy_stm32::rcc::Pll {\n        source: PllSource::HSI,\n        prediv: PllPreDiv::DIV1,  // PLLM = 1 → HSI / 1 = 16 MHz\n        mul: PllMul::MUL30,       // PLLN = 30 → 16 MHz * 30 = 480 MHz VCO\n        divr: Some(PllDiv::DIV5), // PLLR = 5 → 96 MHz (Sysclk)\n        // divq: Some(PllDiv::DIV10), // PLLQ = 10 → 48 MHz (NOT USED)\n        divq: None,\n        divp: Some(PllDiv::DIV30), // PLLP = 30 → 16 MHz (USBOTG)\n        frac: Some(0),             // Fractional part (enabled)\n    });\n\n    config.rcc.ahb_pre = AHBPrescaler::DIV1;\n    config.rcc.apb1_pre = APBPrescaler::DIV1;\n    config.rcc.apb2_pre = APBPrescaler::DIV1;\n    config.rcc.apb7_pre = APBPrescaler::DIV1;\n    config.rcc.ahb5_pre = AHB5Prescaler::DIV4;\n\n    // voltage scale for max performance\n    config.rcc.voltage_scale = VoltageScale::RANGE1;\n    // route PLL1_R into Sysclk\n    config.rcc.sys = Sysclk::PLL1_R;\n\n    let p = embassy_stm32::init(config);\n    info!(\"WWDG example\");\n\n    // 200 ms total period; the first 100 ms is the closed window.\n    // Petting the watchdog within 100 ms of the last reload causes an immediate reset.\n    let mut wdg = WindowWatchdog::new(p.WWDG, 200_000, 100_000);\n\n    loop {\n        // Wait until we are inside the open window (100–200 ms after last reload).\n        Timer::after_millis(150).await;\n        info!(\"pet\");\n        wdg.pet();\n    }\n}\n"
  },
  {
    "path": "examples/stm32wba6-lp/.cargo/config.toml",
    "content": "[target.'cfg(all(target_arch = \"arm\", target_os = \"none\"))']\nrunner = \"probe-rs run --chip STM32WBA65RI\"\n\n[build]\ntarget = \"thumbv8m.main-none-eabihf\"\n\n[env]\nDEFMT_LOG = \"trace\"\n"
  },
  {
    "path": "examples/stm32wba6-lp/Cargo.toml",
    "content": "[package]\nedition = \"2024\"\nname = \"embassy-stm32wba6-lp-examples\"\nversion = \"0.1.0\"\nlicense = \"MIT OR Apache-2.0\"\npublish = false\n\n[dependencies]\nembassy-stm32 = { version = \"0.6.0\", path = \"../../embassy-stm32\", features = [\"defmt\", \"stm32wba65ri\", \"time-driver-any\", \"memory-x\", \"unstable-pac\", \"exti\", \"low-power\", \"executor-thread\"] }\nembassy-sync = { version = \"0.8.0\", path = \"../../embassy-sync\", features = [\"defmt\"] }\nembassy-executor = { version = \"0.10.0\", path = \"../../embassy-executor\", features = [\"defmt\"] }\nembassy-time = { version = \"0.5.1\", path = \"../../embassy-time\", features = [\"defmt\", \"defmt-timestamp-uptime\", \"tick-hz-32_768\"] }\n\ndefmt = \"1.0.1\"\ndefmt-rtt = \"1.1.0\"\n\ncortex-m = { version = \"0.7.6\", features = [\"inline-asm\", \"critical-section-single-core\"] }\ncortex-m-rt = \"0.7.0\"\npanic-probe = { version = \"1.0.0\", features = [\"print-defmt\"] }\n\n[profile.release]\ndebug = 2\n\n[package.metadata.embassy]\nbuild = [\n  { target = \"thumbv8m.main-none-eabihf\", artifact-dir = \"out/examples/stm32wba6-lp\" }\n]\n"
  },
  {
    "path": "examples/stm32wba6-lp/build.rs",
    "content": "use std::error::Error;\n\nfn main() -> Result<(), Box<dyn Error>> {\n    println!(\"cargo:rustc-link-arg-bins=--nmagic\");\n    println!(\"cargo:rustc-link-arg-bins=-Tlink.x\");\n    println!(\"cargo:rustc-link-arg-bins=-Tdefmt.x\");\n\n    Ok(())\n}\n"
  },
  {
    "path": "examples/stm32wba6-lp/src/bin/blinky.rs",
    "content": "//! Blinky example with STOP mode.\n//!\n//! The MCU enters STOP mode between LED toggles (5 s intervals).\n//! Current draw drops to ~1 µA while sleeping; the RTC wakeup alarm\n//! brings the core back to run mode for the next toggle.\n\n#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_stm32::gpio::{Level, Output, Speed};\nuse embassy_stm32::rcc::*;\nuse embassy_time::Timer;\nuse {defmt_rtt as _, panic_probe as _};\n\n#[embassy_executor::main(executor = \"embassy_stm32::executor::Executor\", entry = \"cortex_m_rt::entry\")]\nasync fn main(_spawner: Spawner) {\n    let mut config = embassy_stm32::Config::default();\n\n    // HSI 16 MHz as sysclk — no PLL needed for a blinky demo.\n    config.rcc.sys = Sysclk::HSI;\n\n    // LSI 32 kHz for the RTC — the time driver uses the RTC wakeup\n    // alarm to bring the core back from STOP mode.\n    config.rcc.ls = LsConfig {\n        rtc: RtcClockSource::LSI,\n        lsi: true,\n        lse: None,\n    };\n\n    // SAI1 clock mux defaults to PLL1_P — override to HSI since\n    // PLL1 is not configured in this demo.\n    config.rcc.mux.sai1sel = Sai1sel::HSI;\n\n    // Disable debug peripherals during STOP to minimise leakage.\n    // Set to `true` when debugging with probe-rs / RTT.\n    config.enable_debug_during_sleep = false;\n\n    let p = embassy_stm32::init(config);\n    info!(\"Hello from STM32WBA6 low-power blinky!\");\n\n    let mut led = Output::new(p.PB4, Level::High, Speed::Low);\n\n    loop {\n        info!(\"led on — sleeping 5 s\");\n        led.set_high();\n        Timer::after_millis(5000).await; // MCU enters STOP here\n\n        info!(\"led off — sleeping 5 s\");\n        led.set_low();\n        Timer::after_millis(5000).await; // MCU enters STOP here\n    }\n}\n"
  },
  {
    "path": "examples/stm32wba6-lp/src/bin/button_exti.rs",
    "content": "//! EXTI button example with STOP mode.\n//!\n//! The MCU enters STOP mode while waiting for a button press on PC13.\n//! The EXTI line wakes the core from STOP — no polling required.\n//! Current draw drops to ~1 µA between presses.\n\n#![no_std]\n#![no_main]\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_stm32::exti::{self, ExtiInput};\nuse embassy_stm32::gpio::Pull;\nuse embassy_stm32::rcc::*;\nuse embassy_stm32::{bind_interrupts, interrupt};\nuse {defmt_rtt as _, panic_probe as _};\n\nbind_interrupts!(\n    pub struct Irqs {\n        EXTI13 => exti::InterruptHandler<interrupt::typelevel::EXTI13>;\n    }\n);\n\n#[embassy_executor::main(executor = \"embassy_stm32::executor::Executor\", entry = \"cortex_m_rt::entry\")]\nasync fn main(_spawner: Spawner) {\n    let mut config = embassy_stm32::Config::default();\n\n    // HSI 16 MHz as sysclk.\n    config.rcc.sys = Sysclk::HSI;\n\n    // LSI 32 kHz for the RTC — the time driver uses the RTC wakeup\n    // alarm to bring the core back from STOP mode.\n    config.rcc.ls = LsConfig {\n        rtc: RtcClockSource::LSI,\n        lsi: true,\n        lse: None,\n    };\n\n    // SAI1 clock mux defaults to PLL1_P — override to HSI since\n    // PLL1 is not configured in this demo.\n    config.rcc.mux.sai1sel = Sai1sel::HSI;\n\n    // Disable debug peripherals during STOP to minimise leakage.\n    // Set to `true` when debugging with probe-rs / RTT.\n    config.enable_debug_during_sleep = false;\n\n    let p = embassy_stm32::init(config);\n    info!(\"Hello from STM32WBA6 low-power button example!\");\n    info!(\"Press the USER button (PC13)...\");\n\n    let mut button = ExtiInput::new(p.PC13, p.EXTI13, Pull::Up, Irqs);\n\n    loop {\n        // MCU enters STOP while waiting for the falling edge.\n        button.wait_for_falling_edge().await;\n        info!(\"Pressed!\");\n\n        button.wait_for_rising_edge().await;\n        info!(\"Released!\");\n    }\n}\n"
  },
  {
    "path": "examples/stm32wl/.cargo/config.toml",
    "content": "[target.'cfg(all(target_arch = \"arm\", target_os = \"none\"))']\n# replace your chip as listed in `probe-rs chip list`\nrunner = \"probe-rs run --chip STM32WLE5JCIx\"\n\n[build]\ntarget = \"thumbv7em-none-eabi\"\n\n[env]\nDEFMT_LOG = \"trace\"\n"
  },
  {
    "path": "examples/stm32wl/Cargo.toml",
    "content": "[package]\nedition = \"2024\"\nname = \"embassy-stm32wl-examples\"\nversion = \"0.1.0\"\nlicense = \"MIT OR Apache-2.0\"\npublish = false\n\n[dependencies]\n# Change stm32wl55jc-cm4 to your chip name, if necessary.\nembassy-stm32 = { version = \"0.6.0\", path = \"../../embassy-stm32\", features = [\"defmt\", \"stm32wl55jc-cm4\", \"time-driver-any\", \"memory-x\", \"unstable-pac\", \"exti\", \"chrono\"] }\nembassy-sync = { version = \"0.8.0\", path = \"../../embassy-sync\", features = [\"defmt\"] }\nembassy-executor = { version = \"0.10.0\", path = \"../../embassy-executor\", features = [\"platform-cortex-m\", \"executor-thread\", \"defmt\"] }\nembassy-time = { version = \"0.5.1\", path = \"../../embassy-time\", features = [\"defmt\", \"defmt-timestamp-uptime\", \"tick-hz-32_768\"] }\nembassy-embedded-hal = { version = \"0.6.0\", path = \"../../embassy-embedded-hal\" }\n\ndefmt = \"1.0.1\"\ndefmt-rtt = \"1.0.0\"\n\ncortex-m = { version = \"0.7.6\", features = [\"inline-asm\", \"critical-section-single-core\"] }\ncortex-m-rt = \"0.7.0\"\nembedded-hal = \"0.2.6\"\nembedded-storage = \"0.3.1\"\npanic-probe = { version = \"1.0.0\", features = [\"print-defmt\"] }\nheapless = { version = \"0.9\", default-features = false }\nchrono = { version = \"^0.4\", default-features = false }\n\n[profile.release]\ndebug = 2\n\n[package.metadata.embassy]\nbuild = [\n  { target = \"thumbv7em-none-eabi\", artifact-dir = \"out/examples/stm32wl\" }\n]\n"
  },
  {
    "path": "examples/stm32wl/build.rs",
    "content": "fn main() {\n    println!(\"cargo:rustc-link-arg-bins=--nmagic\");\n    println!(\"cargo:rustc-link-arg-bins=-Tlink.x\");\n    println!(\"cargo:rustc-link-arg-bins=-Tdefmt.x\");\n}\n"
  },
  {
    "path": "examples/stm32wl/memory.x",
    "content": "MEMORY\n{\n  /* NOTE 1 K = 1 KiBi = 1024 bytes */\n  FLASH                             : ORIGIN = 0x08000000, LENGTH = 256K\n  SHARED_RAM                  (rwx) : ORIGIN = 0x20000000, LENGTH = 128\n  RAM                         (rwx) : ORIGIN = 0x20000080, LENGTH = 64K - 128\n}\n\nSECTIONS\n{\n    .shared_data :\n    {\n        *(.shared_data)\n    } > SHARED_RAM\n}\n"
  },
  {
    "path": "examples/stm32wl/src/bin/adc.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse core::mem::MaybeUninit;\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_stm32::SharedData;\nuse embassy_stm32::adc::{Adc, CkModePclk, Clock, SampleTime};\nuse embassy_time::Timer;\nuse {defmt_rtt as _, panic_probe as _};\n\nstatic SHARED_DATA: MaybeUninit<SharedData> = MaybeUninit::uninit();\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let p = embassy_stm32::init_primary(Default::default(), &SHARED_DATA);\n    info!(\"Hello World!\");\n\n    let mut adc = Adc::new_with_clock(p.ADC1, Clock::Sync { div: CkModePclk::DIV1 });\n\n    let mut pin = p.PB2;\n\n    let mut vrefint = adc.enable_vrefint();\n    let vrefint_sample = adc.blocking_read(&mut vrefint, SampleTime::CYCLES79_5);\n    let convert_to_millivolts = |sample| {\n        // From https://www.st.com/resource/en/datasheet/stm32g031g8.pdf\n        // 6.3.3 Embedded internal reference voltage\n        const VREFINT_MV: u32 = 1212; // mV\n\n        (u32::from(sample) * VREFINT_MV / u32::from(vrefint_sample)) as u16\n    };\n\n    loop {\n        let v = adc.blocking_read(&mut pin, SampleTime::CYCLES79_5);\n        info!(\"--> {} - {} mV\", v, convert_to_millivolts(v));\n        Timer::after_millis(100).await;\n    }\n}\n"
  },
  {
    "path": "examples/stm32wl/src/bin/blinky.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse core::mem::MaybeUninit;\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_stm32::SharedData;\nuse embassy_stm32::gpio::{Level, Output, Speed};\nuse embassy_time::Timer;\nuse {defmt_rtt as _, panic_probe as _};\n\n#[unsafe(link_section = \".shared_data\")]\nstatic SHARED_DATA: MaybeUninit<SharedData> = MaybeUninit::uninit();\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let p = embassy_stm32::init_primary(Default::default(), &SHARED_DATA);\n    info!(\"Hello World!\");\n\n    let mut led = Output::new(p.PB15, Level::High, Speed::Low);\n\n    loop {\n        info!(\"high\");\n        led.set_high();\n        Timer::after_millis(500).await;\n\n        info!(\"low\");\n        led.set_low();\n        Timer::after_millis(500).await;\n    }\n}\n"
  },
  {
    "path": "examples/stm32wl/src/bin/button.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse core::mem::MaybeUninit;\n\nuse cortex_m_rt::entry;\nuse defmt::*;\nuse embassy_stm32::SharedData;\nuse embassy_stm32::gpio::{Input, Level, Output, Pull, Speed};\nuse {defmt_rtt as _, panic_probe as _};\n\n#[unsafe(link_section = \".shared_data\")]\nstatic SHARED_DATA: MaybeUninit<SharedData> = MaybeUninit::uninit();\n\n#[entry]\nfn main() -> ! {\n    info!(\"Hello World!\");\n\n    let p = embassy_stm32::init_primary(Default::default(), &SHARED_DATA);\n\n    let button = Input::new(p.PA0, Pull::Up);\n    let mut led1 = Output::new(p.PB15, Level::High, Speed::Low);\n    let mut led2 = Output::new(p.PB9, Level::High, Speed::Low);\n\n    loop {\n        if button.is_high() {\n            led1.set_high();\n            led2.set_low();\n        } else {\n            led1.set_low();\n            led2.set_high();\n        }\n    }\n}\n"
  },
  {
    "path": "examples/stm32wl/src/bin/button_exti.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse core::mem::MaybeUninit;\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_stm32::exti::{self, ExtiInput};\nuse embassy_stm32::gpio::Pull;\nuse embassy_stm32::{SharedData, bind_interrupts, interrupt};\nuse {defmt_rtt as _, panic_probe as _};\n\nbind_interrupts!(\n    pub struct Irqs{\n        EXTI0 => exti::InterruptHandler<interrupt::typelevel::EXTI0>;\n});\n\n#[unsafe(link_section = \".shared_data\")]\nstatic SHARED_DATA: MaybeUninit<SharedData> = MaybeUninit::uninit();\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let p = embassy_stm32::init_primary(Default::default(), &SHARED_DATA);\n    info!(\"Hello World!\");\n\n    let mut button = ExtiInput::new(p.PA0, p.EXTI0, Pull::Up, Irqs);\n\n    info!(\"Press the USER button...\");\n\n    loop {\n        button.wait_for_falling_edge().await;\n        info!(\"Pressed!\");\n        button.wait_for_rising_edge().await;\n        info!(\"Released!\");\n    }\n}\n"
  },
  {
    "path": "examples/stm32wl/src/bin/flash.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse core::mem::MaybeUninit;\n\nuse defmt::{info, unwrap};\nuse embassy_executor::Spawner;\nuse embassy_stm32::SharedData;\nuse embassy_stm32::flash::Flash;\nuse {defmt_rtt as _, panic_probe as _};\n\n#[unsafe(link_section = \".shared_data\")]\nstatic SHARED_DATA: MaybeUninit<SharedData> = MaybeUninit::uninit();\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let p = embassy_stm32::init_primary(Default::default(), &SHARED_DATA);\n    info!(\"Hello Flash!\");\n\n    const ADDR: u32 = 0x36000;\n\n    let mut f = Flash::new_blocking(p.FLASH).into_blocking_regions().bank1_region;\n\n    info!(\"Reading...\");\n    let mut buf = [0u8; 8];\n    unwrap!(f.blocking_read(ADDR, &mut buf));\n    info!(\"Read: {=[u8]:x}\", buf);\n\n    info!(\"Erasing...\");\n    unwrap!(f.blocking_erase(ADDR, ADDR + 2048));\n\n    info!(\"Reading...\");\n    let mut buf = [0u8; 8];\n    unwrap!(f.blocking_read(ADDR, &mut buf));\n    info!(\"Read: {=[u8]:x}\", buf);\n\n    info!(\"Writing...\");\n    unwrap!(f.blocking_write(ADDR, &[1, 2, 3, 4, 5, 6, 7, 8]));\n\n    info!(\"Reading...\");\n    let mut buf = [0u8; 8];\n    unwrap!(f.blocking_read(ADDR, &mut buf));\n    info!(\"Read: {=[u8]:x}\", buf);\n    assert_eq!(&buf[..], &[1, 2, 3, 4, 5, 6, 7, 8]);\n}\n"
  },
  {
    "path": "examples/stm32wl/src/bin/random.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse core::mem::MaybeUninit;\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_stm32::rng::{self, Rng};\nuse embassy_stm32::time::Hertz;\nuse embassy_stm32::{SharedData, bind_interrupts, peripherals};\nuse {defmt_rtt as _, panic_probe as _};\n\nbind_interrupts!(struct Irqs{\n    RNG => rng::InterruptHandler<peripherals::RNG>;\n});\n\n#[unsafe(link_section = \".shared_data\")]\nstatic SHARED_DATA: MaybeUninit<SharedData> = MaybeUninit::uninit();\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let mut config = embassy_stm32::Config::default();\n    {\n        use embassy_stm32::rcc::*;\n        config.rcc.hse = Some(Hse {\n            freq: Hertz(32_000_000),\n            mode: HseMode::Bypass,\n            prescaler: HsePrescaler::DIV1,\n        });\n        config.rcc.sys = Sysclk::PLL1_R;\n        config.rcc.pll = Some(Pll {\n            source: PllSource::HSE,\n            prediv: PllPreDiv::DIV2,\n            mul: PllMul::MUL6,\n            divp: None,\n            divq: Some(PllQDiv::DIV2), // PLL1_Q clock (32 / 2 * 6 / 2), used for RNG\n            divr: Some(PllRDiv::DIV2), // sysclk 48Mhz clock (32 / 2 * 6 / 2)\n        });\n    }\n    let p = embassy_stm32::init_primary(config, &SHARED_DATA);\n\n    info!(\"Hello World!\");\n\n    let mut rng = Rng::new(p.RNG, Irqs);\n\n    let mut buf = [0u8; 16];\n    unwrap!(rng.async_fill_bytes(&mut buf).await);\n    info!(\"random bytes: {:02x}\", buf);\n\n    loop {}\n}\n"
  },
  {
    "path": "examples/stm32wl/src/bin/rtc.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse core::mem::MaybeUninit;\n\nuse chrono::{NaiveDate, NaiveDateTime};\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_stm32::rtc::{Rtc, RtcConfig};\nuse embassy_stm32::time::Hertz;\nuse embassy_stm32::{Config, SharedData};\nuse embassy_time::Timer;\nuse {defmt_rtt as _, panic_probe as _};\n\n#[unsafe(link_section = \".shared_data\")]\nstatic SHARED_DATA: MaybeUninit<SharedData> = MaybeUninit::uninit();\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let mut config = Config::default();\n    {\n        use embassy_stm32::rcc::*;\n        config.rcc.ls = LsConfig::default_lse();\n        config.rcc.hse = Some(Hse {\n            freq: Hertz(32_000_000),\n            mode: HseMode::Bypass,\n            prescaler: HsePrescaler::DIV1,\n        });\n        config.rcc.sys = Sysclk::PLL1_R;\n        config.rcc.pll = Some(Pll {\n            source: PllSource::HSE,\n            prediv: PllPreDiv::DIV2,\n            mul: PllMul::MUL6,\n            divp: None,\n            divq: Some(PllQDiv::DIV2), // PLL1_Q clock (32 / 2 * 6 / 2), used for RNG\n            divr: Some(PllRDiv::DIV2), // sysclk 48Mhz clock (32 / 2 * 6 / 2)\n        });\n    }\n    let p = embassy_stm32::init_primary(config, &SHARED_DATA);\n    info!(\"Hello World!\");\n\n    let now = NaiveDate::from_ymd_opt(2020, 5, 15)\n        .unwrap()\n        .and_hms_opt(10, 30, 15)\n        .unwrap();\n\n    let (mut rtc, time_provider) = Rtc::new(p.RTC, RtcConfig::default());\n    info!(\"Got RTC! {:?}\", now.and_utc().timestamp());\n\n    rtc.set_datetime(now.into()).expect(\"datetime not set\");\n\n    // In reality the delay would be much longer\n    Timer::after_millis(20000).await;\n\n    let then: NaiveDateTime = time_provider.now().unwrap().into();\n    info!(\"Got RTC! {:?}\", then.and_utc().timestamp());\n}\n"
  },
  {
    "path": "examples/stm32wl/src/bin/uart_async.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse core::mem::MaybeUninit;\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_stm32::usart::{Config, InterruptHandler, Uart};\nuse embassy_stm32::{SharedData, bind_interrupts, dma, peripherals};\nuse {defmt_rtt as _, panic_probe as _};\n\nbind_interrupts!(struct Irqs{\n    USART1 => InterruptHandler<peripherals::USART1>;\n    LPUART1 => InterruptHandler<peripherals::LPUART1>;\n    DMA1_CHANNEL3 => dma::InterruptHandler<peripherals::DMA1_CH3>;\n    DMA1_CHANNEL4 => dma::InterruptHandler<peripherals::DMA1_CH4>;\n    DMA1_CHANNEL5 => dma::InterruptHandler<peripherals::DMA1_CH5>;\n    DMA1_CHANNEL6 => dma::InterruptHandler<peripherals::DMA1_CH6>;\n});\n\n#[unsafe(link_section = \".shared_data\")]\nstatic SHARED_DATA: MaybeUninit<SharedData> = MaybeUninit::uninit();\n\n/*\nPass Incoming data from LPUART1 to USART1\nExample is written for the LoRa-E5 mini v1.0,\nbut can be surely changed for your needs.\n*/\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let mut config = embassy_stm32::Config::default();\n    config.rcc.sys = embassy_stm32::rcc::Sysclk::HSE;\n    let p = embassy_stm32::init_primary(config, &SHARED_DATA);\n\n    defmt::info!(\"Starting system\");\n\n    let mut config1 = Config::default();\n    config1.baudrate = 9600;\n\n    let mut config2 = Config::default();\n    config2.baudrate = 9600;\n\n    //RX/TX connected to USB/UART Bridge on LoRa-E5 mini v1.0\n    let mut usart1 = Uart::new(p.USART1, p.PB7, p.PB6, p.DMA1_CH3, p.DMA1_CH4, Irqs, config1).unwrap();\n\n    //RX1/TX1 (LPUART) on LoRa-E5 mini v1.0\n    let mut usart2 = Uart::new(p.LPUART1, p.PC0, p.PC1, p.DMA1_CH5, p.DMA1_CH6, Irqs, config2).unwrap();\n\n    unwrap!(usart1.write(b\"Hello Embassy World!\\r\\n\").await);\n    unwrap!(usart2.write(b\"Hello Embassy World!\\r\\n\").await);\n\n    let mut buf = [0u8; 300];\n    loop {\n        let result = usart2.read_until_idle(&mut buf).await;\n        match result {\n            Ok(size) => {\n                match usart1.write(&buf[0..size]).await {\n                    Ok(()) => {\n                        //Write suc.\n                    }\n                    Err(..) => {\n                        //Wasn't able to write\n                    }\n                }\n            }\n            Err(_err) => {\n                //Ignore eg. framing errors\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "examples/stm32wl55cm0p/.cargo/config.toml",
    "content": "[target.thumbv6m-none-eabi]\nrunner = 'probe-rs run --chip STM32WL55JC --catch-hardfault --always-print-stacktrace'\n\n[build]\ntarget = \"thumbv6m-none-eabi\" # Cortex-M0+\n\n[env]\nDEFMT_LOG = \"info\"\n"
  },
  {
    "path": "examples/stm32wl55cm0p/Cargo.toml",
    "content": "[package]\nedition = \"2024\"\nname = \"embassy-stm32wl55cm0p-examples\"\nversion = \"0.1.0\"\nlicense = \"MIT OR Apache-2.0\"\npublish = false\n\n[dependencies]\n# Change stm32wl55jc-cm0 to your chip name, if necessary.\nembassy-stm32 = { version = \"0.6.0\", path = \"../../embassy-stm32\", features = [\n  \"defmt\",\n  \"stm32wl55jc-cm0p\",\n  \"time-driver-tim1\",\n  \"exti\",\n  \"unstable-pac\",\n  \"chrono\",\n] }\nembassy-sync = { version = \"0.8.0\", path = \"../../embassy-sync\", features = [\n  \"defmt\",\n] }\nembassy-embedded-hal = { version = \"0.6.0\", path = \"../../embassy-embedded-hal\" }\nembassy-executor = { version = \"0.10.0\", path = \"../../embassy-executor\", features = [\n  \"platform-cortex-m\",\n  \"executor-thread\",\n  \"executor-interrupt\",\n  \"defmt\",\n] }\nembassy-time = { version = \"0.5.1\", path = \"../../embassy-time\", features = [\n  \"defmt\",\n  \"defmt-timestamp-uptime\",\n  \"tick-hz-32_768\",\n] }\nembassy-net = { version = \"0.9.0\", path = \"../../embassy-net\", features = [\n  \"defmt\",\n  \"tcp\",\n  \"dhcpv4\",\n  \"medium-ethernet\",\n  \"proto-ipv6\",\n  \"dns\",\n] }\nembassy-usb = { version = \"0.6.0\", path = \"../../embassy-usb\", features = [\n  \"defmt\",\n] }\nembassy-futures = { version = \"0.1.2\", path = \"../../embassy-futures\" }\n\ndefmt = \"1.0.1\"\ndefmt-rtt = { version = \"1.0.0\", optional = true }\ndefmt-serial = { git = \"https://github.com/gauteh/defmt-serial\", rev = \"411ae7fa909b4fd2667885aff687e009b9108190\", optional = true }\n\ncortex-m = { version = \"0.7.6\", features = [\n  \"inline-asm\",\n  \"critical-section-single-core\",\n] }\ncortex-m-rt = \"0.7.0\"\nembedded-hal = \"1.0.0\"\nembedded-hal-async = { version = \"1.0\" }\nembedded-nal-async = \"0.9.0\"\nembedded-io-async = { version = \"0.7.0\" }\npanic-probe = { version = \"1.0.0\", features = [\"print-defmt\"] }\nheapless = { version = \"0.9\", default-features = false }\nportable-atomic = { version = \"1.5\", features = [\"unsafe-assume-single-core\"] }\nmicromath = \"2.0.0\"\nstm32-fmc = \"0.4.0\"\nembedded-storage = \"0.3.1\"\nstatic_cell = \"2\"\nchrono = { version = \"^0.4\", default-features = false }\ngrounded = \"0.2.0\"\n\n[features]\ndefault = [\"defmt-serial\"]\ndefmt-serial = [\"dep:defmt-serial\"]\ndefmt-rtt = [\"dep:defmt-rtt\"]\n# cargo build/run\n\n# cargo test\n[profile.test]\ncodegen-units = 1\ndebug = 2\ndebug-assertions = true # <-\nincremental = false\nopt-level = 3           # <-\noverflow-checks = true  # <-\n\n# cargo build/run --release\n[profile.release]\ncodegen-units = 16\ndebug = 2\ndebug-assertions = false # <-\nincremental = false\nopt-level = 3            # <-\noverflow-checks = false  # <-\n\n# cargo test --release\n[profile.bench]\ncodegen-units = 1\ndebug = 2\ndebug-assertions = false # <-\nincremental = false\nlto = 'fat'\nopt-level = 3            # <-\noverflow-checks = false  # <-\n\n[package.metadata.embassy]\nbuild = [\n  { target = \"thumbv6m-none-eabi\", artifact-dir = \"out/examples/stm32wl55cm0p\" },\n]\n"
  },
  {
    "path": "examples/stm32wl55cm0p/build.rs",
    "content": "fn main() {\n    println!(\"cargo:rustc-link-arg-bins=--nmagic\");\n    println!(\"cargo:rustc-link-arg-bins=-Tlink.x\");\n    println!(\"cargo:rustc-link-arg-bins=-Tdefmt.x\");\n}\n"
  },
  {
    "path": "examples/stm32wl55cm0p/memory.x",
    "content": "MEMORY\n{\n  /* NOTE 1 K = 1 KiBi = 1024 bytes */\n  /* Use the second half of flash */\n  FLASH                             : ORIGIN = 0x08020000, LENGTH = 128K\n  /* use the first 1K of flash from teh first half of ram for shared data */\n  SHARED_RAM                  (rwx) : ORIGIN = 0x20000000, LENGTH = 1K\n  /* use the second half of flash */\n  RAM                         (rwx) : ORIGIN = 0x20008000, LENGTH = 32K\n}\n\nSECTIONS\n{\n    .shared_data :\n    {\n        *(.shared_data)\n    } > SHARED_RAM\n}\n"
  },
  {
    "path": "examples/stm32wl55cm0p/src/bin/intercore.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse core::mem::MaybeUninit;\nuse core::sync::atomic::{AtomicBool, Ordering};\n\nuse defmt::*;\n#[cfg(feature = \"defmt-rtt\")]\nuse defmt_rtt as _;\nuse embassy_executor::Spawner;\nuse embassy_stm32::gpio::{Level, Output, Speed};\nuse embassy_stm32::ipcc::{Config as IPCCConfig, InterruptHandler, Ipcc};\nuse embassy_stm32::{SharedData, bind_interrupts};\nuse embassy_time::Timer;\nuse panic_probe as _;\n\nbind_interrupts!(struct Irqs{\n    IPCC_C2_RX_C2_TX => InterruptHandler;\n});\n\n#[unsafe(link_section = \".shared_data\")]\nstatic SHARED_DATA: MaybeUninit<SharedData> = MaybeUninit::uninit();\n#[unsafe(link_section = \".shared_data\")]\nstatic LED_STATE: AtomicBool = AtomicBool::new(false);\n\n#[embassy_executor::task]\nasync fn blink_heartbeat(mut led: Output<'static>) {\n    loop {\n        info!(\"CM0+ heartbeat\");\n        led.set_level(Level::High);\n        Timer::after_millis(100).await;\n        led.set_level(Level::Low);\n        Timer::after_millis(900).await;\n    }\n}\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) -> ! {\n    // Initialize the secondary core\n    let p = embassy_stm32::init_secondary(&SHARED_DATA);\n    #[cfg(feature = \"defmt-serial\")]\n    {\n        use embassy_stm32::mode::Blocking;\n        use embassy_stm32::usart::Uart;\n        use static_cell::StaticCell;\n        let config = embassy_stm32::usart::Config::default();\n        let uart = Uart::new_blocking(p.LPUART1, p.PA3, p.PA2, config).expect(\"failed to configure UART!\");\n        static SERIAL: StaticCell<Uart<'static, Blocking>> = StaticCell::new();\n        defmt_serial::defmt_serial(SERIAL.init(uart));\n    }\n    info!(\"CM0+ core initialized!\");\n\n    let ipcc = Ipcc::new(p.IPCC, Irqs, IPCCConfig::default());\n    let [ch1, _ch2, _ch3, _ch4, _ch5, _ch6] = ipcc.split();\n    let (mut _tx, mut rx) = ch1;\n\n    // Set up LED\n    let mut blue_led = Output::new(p.PB15, Level::Low, Speed::Low); // LD3 (heartbeat)\n    let red_led = Output::new(p.PB11, Level::High, Speed::Low);\n    _spawner.spawn(blink_heartbeat(red_led).unwrap());\n\n    loop {\n        let state = rx.receive(|| Some(LED_STATE.load(Ordering::Relaxed))).await;\n        info!(\"CM0+ Recieve: {}\", state);\n        blue_led.set_level(if state { Level::High } else { Level::Low });\n    }\n}\n"
  },
  {
    "path": "examples/stm32wl55cm0p-lp/.cargo/config.toml",
    "content": "[target.thumbv6m-none-eabi]\nrunner = 'probe-rs run --chip STM32WL55JC --catch-hardfault --always-print-stacktrace'\n\n[build]\ntarget = \"thumbv6m-none-eabi\" # Cortex-M0+\n\n[env]\nDEFMT_LOG = \"info\"\n"
  },
  {
    "path": "examples/stm32wl55cm0p-lp/Cargo.toml",
    "content": "[package]\nedition = \"2024\"\nname = \"embassy-stm32wl55cm0-lp-examples\"\nversion = \"0.1.0\"\nlicense = \"MIT OR Apache-2.0\"\npublish = false\n\n[dependencies]\n# Change stm32wl55jc-cm0 to your chip name, if necessary.\nembassy-stm32 = { version = \"0.6.0\", path = \"../../embassy-stm32\", features = [\n  \"stm32wl55jc-cm0p\",\n  \"time-driver-lptim1\",\n  \"exti\",\n  \"unstable-pac\",\n  \"chrono\",\n  \"low-power\",\n  \"executor-thread\"\n] }\nembassy-sync = { version = \"0.8.0\", path = \"../../embassy-sync\", features = [] }\nembassy-executor = { version = \"0.10.0\", path = \"../../embassy-executor\", features = [] }\nembassy-time = { version = \"0.5.1\", path = \"../../embassy-time\", features = [\n  \"tick-hz-1_000\",\n] }\nembassy-futures = { version = \"0.1.2\", path = \"../../embassy-futures\" }\n\ndefmt = {version = \"1.0.1\", optional = true }\ndefmt-rtt = { version = \"1.0.0\", optional = true }\ndefmt-serial = { git = \"https://github.com/gauteh/defmt-serial\", rev = \"411ae7fa909b4fd2667885aff687e009b9108190\", optional = true }\n\ncortex-m = { version = \"0.7.6\", features = [\n  \"inline-asm\",\n  \"critical-section-single-core\",\n] }\ncortex-m-rt = \"0.7.0\"\npanic-probe = { version = \"1.0.0\", features = [] }\nportable-atomic = { version = \"1.13\", features = [\"unsafe-assume-single-core\"] }\nstatic_cell = \"2\"\n\n[features]\ndefault = [\"defmt-serial\"]\ndefmt = [\n  \"dep:defmt\",\n  \"embassy-stm32/defmt\",\n  \"embassy-sync/defmt\",\n  \"embassy-executor/defmt\",\n  \"embassy-time/defmt\",\n  \"embassy-time/defmt-timestamp-uptime\",\n  \"embassy-futures/defmt\",\n  \"panic-probe/print-defmt\",\n]\ndefmt-serial = [\"dep:defmt-serial\", \"defmt\"]\ndefmt-rtt = [\"dep:defmt-rtt\", \"defmt\"]\n\n[profile.dev]\ncodegen-units = 1\ndebug = 2\ndebug-assertions = true # <-\nincremental = false\nopt-level = \"s\" # <-\noverflow-checks = true # <-\n\n# cargo test\n[profile.test]\ncodegen-units = 1\ndebug = 2\ndebug-assertions = true # <-\nincremental = false\nopt-level = 3           # <-\noverflow-checks = true  # <-\n\n# cargo build/run --release\n[profile.release]\ncodegen-units = 16\ndebug = 2\ndebug-assertions = false # <-\nincremental = false\nopt-level = 3            # <-\noverflow-checks = false  # <-\n\n# cargo test --release\n[profile.bench]\ncodegen-units = 1\ndebug = 2\ndebug-assertions = false # <-\nincremental = false\nlto = 'fat'\nopt-level = 3            # <-\noverflow-checks = false  # <-\n\n[package.metadata.embassy]\nbuild = [\n  { target = \"thumbv6m-none-eabi\", artifact-dir = \"out/examples/stm32wl55cm0p-lp\" },\n]\n"
  },
  {
    "path": "examples/stm32wl55cm0p-lp/build.rs",
    "content": "fn main() {\n    println!(\"cargo:rustc-link-arg-bins=--nmagic\");\n    println!(\"cargo:rustc-link-arg-bins=-Tlink.x\");\n    #[cfg(feature = \"defmt\")]\n    println!(\"cargo:rustc-link-arg-bins=-Tdefmt.x\");\n}\n"
  },
  {
    "path": "examples/stm32wl55cm0p-lp/memory.x",
    "content": "MEMORY\n{\n  /* NOTE 1 K = 1 KiBi = 1024 bytes */\n  /* Use the second half of flash */\n  FLASH                             : ORIGIN = 0x08020000, LENGTH = 128K\n  /* use the first 1K of flash from teh first half of ram for shared data */\n  SHARED_RAM                  (rwx) : ORIGIN = 0x20000000, LENGTH = 1K\n  /* use the second half of flash */\n  RAM                         (rwx) : ORIGIN = 0x20008000, LENGTH = 32K\n}\n\nSECTIONS\n{\n    .shared_data :\n    {\n        /* force ordering of SHARED_DATA followed by LED_STATE */\n        *(.shared_data.0)\n        *(.shared_data.1)\n        *(.shared_data)\n    } > SHARED_RAM\n}\n"
  },
  {
    "path": "examples/stm32wl55cm0p-lp/src/bin/blinky.rs",
    "content": "// This example is configured for the nucleo-wl55jc board. Curret monitor should show just a few microamps when the device is in stop2 mode.\n#![no_std]\n#![no_main]\n\nuse core::mem::MaybeUninit;\n\nuse defmt::*;\n#[cfg(feature = \"defmt-rtt\")]\nuse defmt_rtt as _;\nuse embassy_executor::Spawner;\nuse embassy_stm32::SharedData;\nuse embassy_stm32::gpio::{Level, Output, Speed};\nuse embassy_time::Timer;\nuse panic_probe as _;\n\n#[unsafe(link_section = \".shared_data.0\")]\nstatic SHARED_DATA: MaybeUninit<SharedData> = MaybeUninit::uninit();\n\n#[embassy_executor::main(executor = \"embassy_stm32::executor::Executor\", entry = \"cortex_m_rt::entry\")]\nasync fn async_main(_spawner: Spawner) {\n    let p = embassy_stm32::init_secondary(&SHARED_DATA);\n\n    #[cfg(feature = \"defmt-serial\")]\n    {\n        use embassy_stm32::mode::Blocking;\n        use embassy_stm32::usart::Uart;\n        use static_cell::StaticCell;\n        let config = embassy_stm32::usart::Config::default();\n        let uart = Uart::new_blocking(p.LPUART1, p.PA3, p.PA2, config).expect(\"failed to configure UART!\");\n        static SERIAL: StaticCell<Uart<'static, Blocking>> = StaticCell::new();\n        defmt_serial::defmt_serial(SERIAL.init(uart));\n    }\n\n    info!(\"Hello World!\");\n\n    let mut led = Output::new(p.PB11, Level::High, Speed::Low);\n\n    loop {\n        info!(\"high\");\n        led.set_high();\n        Timer::after_millis(100).await;\n\n        info!(\"low\");\n        led.set_low();\n        Timer::after_millis(4900).await;\n    }\n}\n"
  },
  {
    "path": "examples/stm32wl55cm0p-lp/src/bin/intercore.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse core::mem::MaybeUninit;\nuse core::sync::atomic::{AtomicBool, Ordering};\n\n#[cfg(feature = \"defmt\")]\nuse defmt::*;\n#[cfg(feature = \"defmt-rtt\")]\nuse defmt_rtt as _;\nuse embassy_executor::Spawner;\nuse embassy_stm32::gpio::{Level, Output, Speed};\nuse embassy_stm32::ipcc::{Config as IPCCConfig, InterruptHandler, Ipcc};\nuse embassy_stm32::{SharedData, bind_interrupts};\nuse embassy_time::Timer;\nuse panic_probe as _;\n\nbind_interrupts!(struct Irqs{\n    IPCC_C2_RX_C2_TX => InterruptHandler;\n});\n\n#[unsafe(link_section = \".shared_data.0\")]\nstatic SHARED_DATA: MaybeUninit<SharedData> = MaybeUninit::uninit();\n#[unsafe(link_section = \".shared_data.1\")]\n#[unsafe(no_mangle)] // make sure the symbol is not optimized out!\nstatic LED_STATE: AtomicBool = AtomicBool::new(false);\n\n#[embassy_executor::task]\nasync fn blink_heartbeat(mut led: Output<'static>) {\n    loop {\n        #[cfg(feature = \"defmt\")]\n        info!(\"CM0+ heartbeat\");\n        led.set_level(Level::High);\n        Timer::after_millis(100).await;\n        led.set_level(Level::Low);\n        Timer::after_millis(4900).await;\n    }\n}\n\n#[embassy_executor::main(executor = \"embassy_stm32::executor::Executor\", entry = \"cortex_m_rt::entry\")]\n// #[embassy_executor::main]\nasync fn main(_spawner: Spawner) -> ! {\n    #[cfg(feature = \"defmt\")]\n    // Initialize the secondary core\n    let p = embassy_stm32::init_secondary(&SHARED_DATA);\n    #[cfg(feature = \"defmt-serial\")]\n    {\n        use embassy_stm32::mode::Blocking;\n        use embassy_stm32::usart::Uart;\n        use static_cell::StaticCell;\n        let config = embassy_stm32::usart::Config::default();\n        let uart = Uart::new_blocking(p.LPUART1, p.PA3, p.PA2, config).expect(\"failed to configure UART!\");\n        static SERIAL: StaticCell<Uart<'static, Blocking>> = StaticCell::new();\n        defmt_serial::defmt_serial(SERIAL.init(uart));\n    }\n    #[cfg(feature = \"defmt\")]\n    info!(\"CM0+ core initialized!\");\n\n    let ipcc = Ipcc::new(p.IPCC, Irqs, IPCCConfig::default());\n    let [ch1, _ch2, _ch3, _ch4, _ch5, _ch6] = ipcc.split();\n    let (mut _tx, mut rx) = ch1;\n\n    // Set up LED\n    let mut green_led = Output::new(p.PB9, Level::Low, Speed::Low); // LD3 (heartbeat)\n    let red_led = Output::new(p.PB11, Level::High, Speed::Low);\n    _spawner.spawn(blink_heartbeat(red_led).unwrap());\n\n    loop {\n        let state = rx.receive(|| Some(LED_STATE.load(Ordering::SeqCst))).await;\n        #[cfg(feature = \"defmt\")]\n        info!(\"CM0+ Recieve: {}\", state);\n        green_led.set_level(if state { Level::High } else { Level::Low });\n    }\n}\n"
  },
  {
    "path": "examples/stm32wl55cm4/.cargo/config.toml",
    "content": "[target.thumbv7em-none-eabi]\nrunner = 'probe-rs run --chip STM32WL55JC --catch-hardfault --always-print-stacktrace'\n\n[build]\ntarget = \"thumbv7em-none-eabi\" # Cortex-M4\n\n[env]\nDEFMT_LOG = \"info\"\n"
  },
  {
    "path": "examples/stm32wl55cm4/Cargo.toml",
    "content": "[package]\nedition = \"2024\"\nname = \"embassy-stm32h755cm4-examples\"\nversion = \"0.1.0\"\nlicense = \"MIT OR Apache-2.0\"\npublish = false\n\n[dependencies]\n# Change stm32h743bi to your chip name, if necessary.\nembassy-stm32 = { version = \"0.6.0\", path = \"../../embassy-stm32\", features = [\"defmt\", \"stm32wl55jc-cm4\", \"time-driver-tim2\", \"exti\", \"memory-x\", \"unstable-pac\", \"chrono\"] }\nembassy-sync = { version = \"0.8.0\", path = \"../../embassy-sync\", features = [\"defmt\"] }\nembassy-embedded-hal = { version = \"0.6.0\", path = \"../../embassy-embedded-hal\" }\nembassy-executor = { version = \"0.10.0\", path = \"../../embassy-executor\", features = [\"platform-cortex-m\", \"executor-thread\", \"executor-interrupt\", \"defmt\"] }\nembassy-time = { version = \"0.5.1\", path = \"../../embassy-time\", features = [\"defmt\", \"defmt-timestamp-uptime\", \"tick-hz-32_768\"] }\nembassy-net = { version = \"0.9.0\", path = \"../../embassy-net\", features = [\"defmt\", \"tcp\", \"dhcpv4\", \"medium-ethernet\", \"proto-ipv6\", \"dns\"] }\nembassy-usb = { version = \"0.6.0\", path = \"../../embassy-usb\", features = [\"defmt\"] }\nembassy-futures = { version = \"0.1.2\", path = \"../../embassy-futures\" }\n\ndefmt = \"1.0.1\"\ndefmt-rtt = \"1.0.0\"\n\ncortex-m = { version = \"0.7.6\", features = [\"inline-asm\", \"critical-section-single-core\"] }\ncortex-m-rt = \"0.7.0\"\nembedded-hal = \"1.0.0\"\nembedded-hal-async = { version = \"1.0\" }\nembedded-nal-async = \"0.9.0\"\nembedded-io-async = { version = \"0.7.0\" }\npanic-probe = { version = \"1.0.0\", features = [\"print-defmt\"] }\nheapless = { version = \"0.9\", default-features = false }\ncritical-section = \"1.1\"\nmicromath = \"2.0.0\"\nstm32-fmc = \"0.4.0\"\nembedded-storage = \"0.3.1\"\nstatic_cell = \"2\"\nchrono = { version = \"^0.4\", default-features = false }\ngrounded = \"0.2.0\"\n\n# cargo test\n[profile.test]\ncodegen-units = 1\ndebug = 2\ndebug-assertions = true # <-\nincremental = false\nopt-level = 3 # <-\noverflow-checks = true # <-\n\n# cargo build/run --release\n[profile.release]\ncodegen-units = 16\ndebug = 2\ndebug-assertions = false # <-\nincremental = false\nopt-level = 3 # <-\noverflow-checks = false # <-\n\n# cargo test --release\n[profile.bench]\ncodegen-units = 1\ndebug = 2\ndebug-assertions = false # <-\nincremental = false\nlto = 'fat'\nopt-level = 3 # <-\noverflow-checks = false # <-\n\n[package.metadata.embassy]\nbuild = [\n  { target = \"thumbv7em-none-eabi\", artifact-dir = \"out/examples/stm32h755cm4\" }\n]\n"
  },
  {
    "path": "examples/stm32wl55cm4/build.rs",
    "content": "fn main() {\n    println!(\"cargo:rustc-link-arg-bins=--nmagic\");\n    println!(\"cargo:rustc-link-arg-bins=-Tlink.x\");\n    println!(\"cargo:rustc-link-arg-bins=-Tdefmt.x\");\n}\n"
  },
  {
    "path": "examples/stm32wl55cm4/memory.x",
    "content": "MEMORY\n{\n  /* NOTE 1 K = 1 KiBi = 1024 bytes */\n  /* Use the first half of flash */\n  FLASH                             : ORIGIN = 0x08000000, LENGTH = 128K\n  /* use the first 1K of flash for shared data */\n  SHARED_RAM                  (rwx) : ORIGIN = 0x20000000, LENGTH = 1K\n  /* use the first half of RAM */\n  RAM                         (rwx) : ORIGIN = 0x20000400, LENGTH = 32K - 1K\n}\n\nSECTIONS\n{\n    .shared_data :\n    {\n        *(.shared_data)\n    } > SHARED_RAM\n}\n"
  },
  {
    "path": "examples/stm32wl55cm4/src/bin/intercore.rs",
    "content": "#![no_std]\n#![no_main]\n\n//! STM32H7 Primary Core (CM4) Intercore Communication Example\n//!\n//! This example demonstrates reliable communication between the Cortex-M7 and\n//! Cortex-M4 cores using a shared memory region\n//!\n//! The CM4 core handles:\n//! - MPU configuration to make shared memory non-cacheable\n//! - Clock initialization\n//! - Toggling LED state in shared memory\n//!\n//! Usage:\n//! 1. Flash the CM0+ (secondary) core binary first\n//! 2. Then flash this CM4 (primary) core binary\n//! 3. The system will start with CM4 toggling LED state and CM0+ responding by\n//!    physically toggling the LED\n\nuse core::mem::MaybeUninit;\nuse core::sync::atomic::{AtomicBool, Ordering};\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_stm32::ipcc::{Config as IPCCConfig, Ipcc, ReceiveInterruptHandler, TransmitInterruptHandler};\nuse embassy_stm32::{Config, SharedData, bind_interrupts};\nuse embassy_time::Timer;\nuse {defmt_rtt as _, panic_probe as _};\n\nbind_interrupts!(struct Irqs{\n    IPCC_C1_RX => ReceiveInterruptHandler;\n    IPCC_C1_TX => TransmitInterruptHandler;\n});\n\n#[unsafe(link_section = \".shared_data\")]\nstatic SHARED_DATA: MaybeUninit<SharedData> = MaybeUninit::uninit();\n#[unsafe(link_section = \".shared_data\")]\nstatic LED_STATE: AtomicBool = AtomicBool::new(false);\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) -> ! {\n    // Initialize the CM4 core\n    let p = embassy_stm32::init_primary(Config::default(), &SHARED_DATA);\n    info!(\"CM4 core initialized\");\n\n    let ipcc = Ipcc::new(p.IPCC, Irqs, IPCCConfig::default());\n    let [ch1, _ch2, _ch3, _ch4, _ch5, _ch6] = ipcc.split();\n    let (mut tx, mut _rx) = ch1;\n\n    info!(\"CM4: Starting main loop\");\n    loop {\n        Timer::after_millis(1500).await;\n        tx.send(|| {\n            let new_led_state = !LED_STATE.load(Ordering::Relaxed);\n            info!(\"CM4: Send! New LED state: {}\", new_led_state);\n            LED_STATE.store(new_led_state, Ordering::Relaxed);\n        })\n        .await;\n    }\n}\n"
  },
  {
    "path": "examples/stm32wl55cm4-lp/.cargo/config.toml",
    "content": "[target.thumbv7em-none-eabi]\nrunner = 'probe-rs run --chip STM32WL55JC --catch-hardfault --always-print-stacktrace'\n\n[build]\ntarget = \"thumbv7em-none-eabi\" # Cortex-M4\n\n[env]\nDEFMT_LOG = \"info\"\n"
  },
  {
    "path": "examples/stm32wl55cm4-lp/Cargo.toml",
    "content": "[package]\nedition = \"2024\"\nname = \"embassy-stm32wl55cm4-lp-examples\"\nversion = \"0.1.0\"\nlicense = \"MIT OR Apache-2.0\"\npublish = false\n\n[dependencies]\n# Change stm32h743bi to your chip name, if necessary.\nembassy-stm32 = { version = \"0.6.0\", path = \"../../embassy-stm32\", features = [\n  \"defmt\",\n  \"stm32wl55jc-cm4\",\n  \"time-driver-tim2\",\n  \"exti\",\n  \"memory-x\",\n  \"unstable-pac\",\n  \"chrono\",\n  \"low-power\",\n  \"executor-thread\"\n]}\nembassy-sync = { version = \"0.8.0\", path = \"../../embassy-sync\", features = [\"defmt\"] }\nembassy-executor = { version = \"0.10.0\", path = \"../../embassy-executor\", features = [\n  \"defmt\",\n]}\nembassy-time = { version = \"0.5.1\", path = \"../../embassy-time\", features = [\"defmt\", \"defmt-timestamp-uptime\", \"tick-hz-1_000_000\"] }\n\ndefmt = \"1.0.1\"\ndefmt-rtt = { version = \"1.0.0\", optional = true }\ndefmt-serial = { git = \"https://github.com/gauteh/defmt-serial\", rev = \"411ae7fa909b4fd2667885aff687e009b9108190\", optional = true }\n\ncortex-m = { version = \"0.7.6\", features = [\"inline-asm\", \"critical-section-single-core\"] }\ncortex-m-rt = \"0.7.0\"\npanic-probe = { version = \"1.0.0\", features = [\"print-defmt\"] }\ncritical-section = \"1.1\"\nstatic_cell = \"2\"\n\n[features]\ndefault = [\"defmt-rtt\"]\ndefmt-serial = [\"dep:defmt-serial\"]\ndefmt-rtt = [\"dep:defmt-rtt\"]\n\n[profile.dev]\ncodegen-units = 1\ndebug = 2\ndebug-assertions = true # <-\nincremental = false\nopt-level = \"s\" # <-\noverflow-checks = true # <-\n\n# cargo test\n[profile.test]\ncodegen-units = 1\ndebug = 2\ndebug-assertions = true # <-\nincremental = false\nopt-level = 3 # <-\noverflow-checks = true # <-\n\n# cargo build/run --release\n[profile.release]\ncodegen-units = 16\ndebug = 2\ndebug-assertions = false # <-\nincremental = false\nopt-level = 3 # <-\noverflow-checks = false # <-\n\n# cargo test --release\n[profile.bench]\ncodegen-units = 1\ndebug = 2\ndebug-assertions = false # <-\nincremental = false\nlto = 'fat'\nopt-level = 3 # <-\noverflow-checks = false # <-\n\n[package.metadata.embassy]\nbuild = [\n  { target = \"thumbv7em-none-eabi\", artifact-dir = \"out/examples/stm32wl55cm4-lp\" }\n]\n"
  },
  {
    "path": "examples/stm32wl55cm4-lp/build.rs",
    "content": "fn main() {\n    println!(\"cargo:rustc-link-arg-bins=--nmagic\");\n    println!(\"cargo:rustc-link-arg-bins=-Tlink.x\");\n    println!(\"cargo:rustc-link-arg-bins=-Tdefmt.x\");\n}\n"
  },
  {
    "path": "examples/stm32wl55cm4-lp/memory.x",
    "content": "MEMORY\n{\n  /* NOTE 1 K = 1 KiBi = 1024 bytes */\n  /* Use the first half of flash */\n  FLASH                             : ORIGIN = 0x08000000, LENGTH = 128K\n  /* use the first 1K of flash for shared data */\n  SHARED_RAM                  (rwx) : ORIGIN = 0x20000000, LENGTH = 1K\n  /* use the first half of RAM */\n  RAM                         (rwx) : ORIGIN = 0x20000400, LENGTH = 32K - 1K\n}\n\nSECTIONS\n{\n    .shared_data :\n    {\n        /* force ordering of SHARED_DATA followed by LED_STATE */\n        *(.shared_data.0)\n        *(.shared_data.1)\n        *(.shared_data)\n    } > SHARED_RAM\n}\n"
  },
  {
    "path": "examples/stm32wl55cm4-lp/src/bin/blinky.rs",
    "content": "// This example is configured for the nucleo-wl55jc board. Curret monitor should show just a few microamps when the device is in stop2 mode.\n#![no_std]\n#![no_main]\n\nuse core::mem::MaybeUninit;\n\nuse defmt::*;\n#[cfg(feature = \"defmt-rtt\")]\nuse defmt_rtt as _;\nuse embassy_executor::Spawner;\nuse embassy_stm32::SharedData;\nuse embassy_stm32::gpio::{Level, Output, Speed};\nuse embassy_time::Timer;\nuse panic_probe as _;\n\n#[unsafe(link_section = \".shared_data.0\")]\nstatic SHARED_DATA: MaybeUninit<SharedData> = MaybeUninit::uninit();\n\n#[embassy_executor::main(executor = \"embassy_stm32::executor::Executor\", entry = \"cortex_m_rt::entry\")]\nasync fn async_main(_spawner: Spawner) {\n    let mut config = embassy_stm32::Config::default();\n    config.rcc.ls = embassy_stm32::rcc::LsConfig::default_lsi();\n    config.rcc.msi = Some(embassy_stm32::rcc::MSIRange::RANGE4M);\n    config.rcc.sys = embassy_stm32::rcc::Sysclk::MSI;\n    #[cfg(feature = \"defmt-serial\")]\n    {\n        // disable debug during sleep to reduce power consumption since we are\n        // using defmt-serial on LPUART1.\n        config.enable_debug_during_sleep = false;\n    }\n    let p = embassy_stm32::init_primary(config, &SHARED_DATA);\n\n    #[cfg(feature = \"defmt-serial\")]\n    {\n        use embassy_stm32::mode::Blocking;\n        use embassy_stm32::usart::Uart;\n        use static_cell::StaticCell;\n        let config = embassy_stm32::usart::Config::default();\n        let uart = Uart::new_blocking(p.LPUART1, p.PA3, p.PA2, config).expect(\"failed to configure UART!\");\n        static SERIAL: StaticCell<Uart<'static, Blocking>> = StaticCell::new();\n        defmt_serial::defmt_serial(SERIAL.init(uart));\n    }\n\n    // start the second core\n    embassy_stm32::pac::PWR.cr4().modify(|w| w.set_c2boot(true));\n\n    info!(\"Hello World!\");\n\n    let mut led = Output::new(p.PB15, Level::High, Speed::Low);\n\n    loop {\n        info!(\"high\");\n        led.set_high();\n        Timer::after_millis(100).await;\n\n        info!(\"low\");\n        led.set_low();\n        Timer::after_millis(4900).await;\n    }\n}\n"
  },
  {
    "path": "examples/stm32wl55cm4-lp/src/bin/intercore.rs",
    "content": "#![no_std]\n#![no_main]\n\n//! STM32H7 Primary Core (CM4) Intercore Communication Example\n//!\n//! This example demonstrates reliable communication between the Cortex-M7 and\n//! Cortex-M4 cores using a shared memory region\n//!\n//! The CM4 core handles:\n//! - MPU configuration to make shared memory non-cacheable\n//! - Clock initialization\n//! - Toggling LED state in shared memory\n//!\n//! Usage:\n//! 1. Flash the CM0+ (secondary) core binary first\n//! 2. Then flash this CM4 (primary) core binary\n//! 3. The system will start with CM4 toggling LED state and CM0+ responding by\n//!    physically toggling the LED\n\nuse core::mem::MaybeUninit;\nuse core::sync::atomic::{AtomicBool, Ordering};\n\nuse defmt::*;\n#[cfg(feature = \"defmt-rtt\")]\nuse defmt_rtt as _;\nuse embassy_executor::Spawner;\nuse embassy_stm32::gpio::{Level, Output, Speed};\nuse embassy_stm32::ipcc::{Config as IPCCConfig, Ipcc, ReceiveInterruptHandler, TransmitInterruptHandler};\nuse embassy_stm32::{Config, SharedData, bind_interrupts};\nuse embassy_time::{Duration, Timer};\nuse panic_probe as _;\n\nbind_interrupts!(struct Irqs{\n    IPCC_C1_RX => ReceiveInterruptHandler;\n    IPCC_C1_TX => TransmitInterruptHandler;\n});\n\n#[unsafe(link_section = \".shared_data.0\")]\nstatic SHARED_DATA: MaybeUninit<SharedData> = MaybeUninit::uninit();\n#[unsafe(link_section = \".shared_data.1\")]\n#[unsafe(no_mangle)] // make sure the symbol is not optimized out!\nstatic LED_STATE: AtomicBool = AtomicBool::new(false);\n\n#[embassy_executor::task]\nasync fn blink_heartbeat(mut led: Output<'static>) {\n    loop {\n        info!(\"CM4 heartbeat\");\n        led.set_level(Level::High);\n        Timer::after_millis(100).await;\n        led.set_level(Level::Low);\n        Timer::after_millis(5900).await;\n    }\n}\n#[embassy_executor::main(executor = \"embassy_stm32::executor::Executor\", entry = \"cortex_m_rt::entry\")]\n// #[embassy_executor::main]\nasync fn main(_spawner: Spawner) -> ! {\n    // Initialize the CM4 core\n    let mut config = Config::default();\n    config.min_stop_pause = Duration::from_millis(50);\n    // Set the LPTIM1 clock source to LSI & enable for secondary core time driver\n    config.rcc.ls.lsi = true;\n    config.rcc.mux.lptim1sel = embassy_stm32::pac::rcc::vals::Lptimsel::LSI;\n    let _p = embassy_stm32::init_primary(config, &SHARED_DATA);\n    #[cfg(feature = \"defmt-serial\")]\n    {\n        use embassy_stm32::mode::Blocking;\n        use embassy_stm32::usart::Uart;\n        use static_cell::StaticCell;\n        let config = embassy_stm32::usart::Config::default();\n        let uart = Uart::new_blocking(_p.LPUART1, _p.PA3, _p.PA2, config).expect(\"failed to configure UART!\");\n        static SERIAL: StaticCell<Uart<'static, Blocking>> = StaticCell::new();\n        defmt_serial::defmt_serial(SERIAL.init(uart));\n    }\n    info!(\"CM4 core initialized\");\n    let ipcc = Ipcc::new(_p.IPCC, Irqs, IPCCConfig::default());\n    let [ch1, _ch2, _ch3, _ch4, _ch5, _ch6] = ipcc.split();\n    let (mut tx, mut _rx) = ch1;\n\n    let blue_led = Output::new(_p.PB15, Level::High, Speed::Low);\n    _spawner.spawn(blink_heartbeat(blue_led).unwrap());\n\n    loop {\n        info!(\"CM4: Sending message!!!\");\n        tx.send(|| {\n            let new_led_state = !LED_STATE.load(Ordering::SeqCst);\n            info!(\"CM4: Send! New LED state: {}\", new_led_state);\n            LED_STATE.store(new_led_state, Ordering::SeqCst);\n        })\n        .await;\n        Timer::after_secs(10).await;\n    }\n}\n"
  },
  {
    "path": "examples/stm32wle5-lp/.cargo/config.toml",
    "content": "[target.'cfg(all(target_arch = \"arm\", target_os = \"none\"))']\n# replace your chip as listed in `probe-rs chip list`\nrunner = \"probe-rs run --chip STM32WLE5JCIx --connect-under-reset\"\n\n[build]\ntarget = \"thumbv7em-none-eabi\"\n\n[env]\nDEFMT_LOG = \"info\"\n"
  },
  {
    "path": "examples/stm32wle5-lp/Cargo.toml",
    "content": "[package]\nedition = \"2024\"\nname = \"embassy-stm32wle5-lp-examples\"\nversion = \"0.1.0\"\nlicense = \"MIT OR Apache-2.0\"\npublish = false\n\n[dependencies]\n# Change stm32wle5jc to your chip name, if necessary.\nembassy-stm32 = { version = \"0.6.0\", path = \"../../embassy-stm32\", features = [\"defmt\", \"stm32wle5jc\", \"time-driver-lptim1\", \"memory-x\", \"unstable-pac\", \"exti\", \"low-power\", \"executor-thread\"] }\nembassy-sync = { version = \"0.8.0\", path = \"../../embassy-sync\", features = [\"defmt\"] }\nembassy-executor = { version = \"0.10.0\", path = \"../../embassy-executor\", features = [\"defmt\"] }\nembassy-time = { version = \"0.5.1\", path = \"../../embassy-time\", features = [\"defmt\", \"defmt-timestamp-uptime\", \"tick-hz-1_000\"] }\nembassy-embedded-hal = { version = \"0.6.0\", path = \"../../embassy-embedded-hal\" }\n\ndefmt = \"1.0.1\"\ndefmt-rtt = { version = \"1.1.0\", optional = true }\ndefmt-serial = { git = \"https://github.com/gauteh/defmt-serial\", rev = \"411ae7fa909b4fd2667885aff687e009b9108190\", optional = true }\n\ncortex-m = { version = \"0.7.6\", features = [\"inline-asm\", \"critical-section-single-core\"] }\ncortex-m-rt = \"0.7.0\"\nembedded-hal = \"1.0.0\"\nembedded-storage = \"0.3.1\"\npanic-probe = { version = \"1.0.0\", features = [\"print-defmt\"] }\nstatic_cell = { version = \"2.1.1\", default-features = false }\n\n[profile.release]\ndebug = 2\n\n[package.metadata.embassy]\nbuild = [\n  { target = \"thumbv7em-none-eabi\", artifact-dir = \"out/examples/stm32wle5-lp\" }\n]\n\n[features]\ndefault = [\"defmt-serial\"]\ndefmt-rtt = [\"dep:defmt-rtt\"]\ndefmt-serial = [\"dep:defmt-serial\"]\n"
  },
  {
    "path": "examples/stm32wle5-lp/README.md",
    "content": "# Low Power Examples for STM32WLEx family\n\nExamples in this repo should work with [LoRa-E5 Dev Board](https://www.st.com/en/partner-products-and-services/lora-e5-development-kit.html) board.\n\n## Prerequsits\n\n- Connect a usb serial adapter to LPUart1 (this is where ALL logging will go)\n- Optional: Connect an amp meter that ran measure down to 0.1uA to the power test pins\n- `cargo install defmt-print` so you can print log messahes from LPUart1\n\n## Example Notes\n\nAll examples will set all pins to analog mode before configuring pins for the example, if any. This saves about 500uA!!!!\n\n- the `adc` example will sleep in STOP1 betwen samples and the chip will only draw about 13uA while sleeping\n- the `blinky` example will sleep in STOP2 and the chip will only draw 1uA or less while sleeping\n- the `button_exti` example will sleep in STOP2 and the chip will only draw 1uA or less while sleeping\n- the `i2c` examples will sleep in STOP1 between reads and the chip only draw about 10uA while sleeping\n\nFor each example you will need to start `defmt-print` with the example binary and the correct serial port in a seperate terminal.  Example:\n```\ndefmt-print -w -v -e target/thumbv7em-none-eabi/debug/<module-name> serial --path /dev/cu.usbserial-00000000 --baud 115200\n```\n\nRun individual examples with\n```\ncargo flash --chip STM32WLE5JCIx --connect-under-reset --bin <module-name>\n```\nfor example\n```\ncargo flash --chip STM32WLE5JCIx --connect-under-reset --bin blinky\n```\n\nYou can also run them with with `run`.  However in this case expect probe-rs to be disconnected as soon as flashing is done as all IO pins are set to analog input!\n```\ncargo run --bin blinky\n```\n\n## Checklist before running examples\nYou might need to adjust `.cargo/config.toml`, `Cargo.toml` and possibly update pin numbers or peripherals to match the specific MCU or board you are using.\n\n* [ ] Update .cargo/config.toml with the correct probe-rs command to use your specific MCU. For example for L432KCU6 it should be `probe-rs run --chip STM32L432KCUx`. (use `probe-rs chip list` to find your chip)\n* [ ] Update Cargo.toml to have the correct `embassy-stm32` feature. For example for L432KCU6 it should be `stm32l432kc`. Look in the `Cargo.toml` file of the `embassy-stm32` project to find the correct feature flag for your chip.\n* [ ] If your board has a special clock or power configuration, make sure that it is set up appropriately.\n* [ ] If your board has different pin mapping, update any pin numbers or peripherals in the given example code to match your schematic\n\nIf you are unsure, please drop by the Embassy Matrix chat for support, and let us know:\n\n* Which example you are trying to run\n* Which chip and board you are using\n\nEmbassy Chat: https://matrix.to/#/#embassy-rs:matrix.org\n"
  },
  {
    "path": "examples/stm32wle5-lp/build.rs",
    "content": "fn main() {\n    println!(\"cargo:rustc-link-arg-bins=--nmagic\");\n    println!(\"cargo:rustc-link-arg-bins=-Tlink.x\");\n    println!(\"cargo:rustc-link-arg-bins=-Tdefmt.x\");\n}\n"
  },
  {
    "path": "examples/stm32wle5-lp/src/bin/adc.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse defmt::*;\n#[cfg(feature = \"defmt-rtt\")]\nuse defmt_rtt as _;\nuse embassy_executor::Spawner;\nuse embassy_stm32::adc::{Adc, SampleTime};\nuse embassy_time::Timer;\nuse panic_probe as _;\nuse static_cell::StaticCell;\n\n#[embassy_executor::main(executor = \"embassy_stm32::executor::Executor\", entry = \"cortex_m_rt::entry\")]\nasync fn async_main(_spawner: Spawner) {\n    let mut config = embassy_stm32::Config::default();\n    // enable HSI clock\n    config.rcc.hsi = true;\n    // enable LSI clock for RTC\n    config.rcc.ls = embassy_stm32::rcc::LsConfig::default_lsi();\n    config.rcc.msi = Some(embassy_stm32::rcc::MSIRange::RANGE4M);\n    config.rcc.sys = embassy_stm32::rcc::Sysclk::MSI;\n    // enable ADC with HSI clock\n    config.rcc.mux.adcsel = embassy_stm32::pac::rcc::vals::Adcsel::HSI;\n    #[cfg(feature = \"defmt-serial\")]\n    {\n        // disable debug during sleep to reduce power consumption since we are\n        // using defmt-serial on LPUART1.\n        config.enable_debug_during_sleep = false;\n        // if we are using defmt-serial on LPUART1, we need to use HSI for the clock\n        // so that its registers are preserved during STOP modes.\n        config.rcc.mux.lpuart1sel = embassy_stm32::pac::rcc::vals::Lpuart1sel::HSI;\n    }\n    // Initialize STM32WL peripherals (use default config like wio-e5-async example)\n    let p = embassy_stm32::init(config);\n\n    #[cfg(feature = \"defmt-serial\")]\n    {\n        use embassy_stm32::mode::Blocking;\n        use embassy_stm32::usart::Uart;\n        let config = embassy_stm32::usart::Config::default();\n        let uart = Uart::new_blocking(p.LPUART1, p.PC0, p.PC1, config).expect(\"failed to configure UART!\");\n        static SERIAL: StaticCell<Uart<'static, Blocking>> = StaticCell::new();\n        defmt_serial::defmt_serial(SERIAL.init(uart));\n    }\n\n    info!(\"Hello World!\");\n\n    let mut adc = Adc::new(p.ADC1);\n    let mut pin = p.PA10;\n\n    let mut vrefint = adc.enable_vrefint();\n    let vrefint_sample = adc.blocking_read(&mut vrefint, SampleTime::CYCLES79_5);\n    let convert_to_millivolts = |sample| {\n        // From https://www.st.com/resource/en/datasheet/stm32g031g8.pdf\n        // 6.3.3 Embedded internal reference voltage\n        const VREFINT_MV: u32 = 1212; // mV\n\n        (u32::from(sample) * VREFINT_MV / u32::from(vrefint_sample)) as u16\n    };\n\n    loop {\n        let v = adc.blocking_read(&mut pin, SampleTime::CYCLES79_5);\n        info!(\"--> {} - {} mV\", v, convert_to_millivolts(v));\n        Timer::after_secs(1).await;\n    }\n}\n"
  },
  {
    "path": "examples/stm32wle5-lp/src/bin/blinky.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse defmt::*;\n#[cfg(feature = \"defmt-rtt\")]\nuse defmt_rtt as _;\nuse embassy_executor::Spawner;\nuse embassy_stm32::gpio::{Level, Output, Speed};\nuse embassy_time::Timer;\nuse panic_probe as _;\nuse static_cell::StaticCell;\n\n#[embassy_executor::main(executor = \"embassy_stm32::executor::Executor\", entry = \"cortex_m_rt::entry\")]\nasync fn async_main(_spawner: Spawner) {\n    let mut config = embassy_stm32::Config::default();\n    config.rcc.msi = Some(embassy_stm32::rcc::MSIRange::RANGE4M);\n    config.rcc.sys = embassy_stm32::rcc::Sysclk::MSI;\n    #[cfg(feature = \"defmt-serial\")]\n    {\n        // enable HSI clock\n        config.rcc.hsi = true;\n        // disable debug during sleep to reduce power consumption since we are\n        // using defmt-serial on LPUART1.\n        config.enable_debug_during_sleep = false;\n        // if we are using defmt-serial on LPUART1, we need to use HSI for the clock\n        // so that its registers are preserved during STOP modes.\n        config.rcc.mux.lpuart1sel = embassy_stm32::pac::rcc::vals::Lpuart1sel::HSI;\n    }\n    // Initialize STM32WL peripherals (use default config like wio-e5-async example)\n    let p = embassy_stm32::init(config);\n\n    #[cfg(feature = \"defmt-serial\")]\n    {\n        use embassy_stm32::mode::Blocking;\n        use embassy_stm32::usart::Uart;\n        let config = embassy_stm32::usart::Config::default();\n        let uart = Uart::new_blocking(p.LPUART1, p.PC0, p.PC1, config).expect(\"failed to configure UART!\");\n        static SERIAL: StaticCell<Uart<'static, Blocking>> = StaticCell::new();\n        defmt_serial::defmt_serial(SERIAL.init(uart));\n    }\n\n    info!(\"Hello World!\");\n\n    let mut led = Output::new(p.PB5, Level::High, Speed::Low);\n\n    loop {\n        info!(\"low\");\n        led.set_low();\n        Timer::after_millis(5000).await;\n\n        info!(\"high\");\n        led.set_high();\n        Timer::after_millis(5000).await;\n    }\n}\n"
  },
  {
    "path": "examples/stm32wle5-lp/src/bin/button_exti.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse defmt::*;\n#[cfg(feature = \"defmt-rtt\")]\nuse defmt_rtt as _;\nuse embassy_executor::Spawner;\nuse embassy_stm32::exti::{self, ExtiInput};\nuse embassy_stm32::gpio::Pull;\nuse embassy_stm32::{bind_interrupts, interrupt};\nuse panic_probe as _;\nuse static_cell::StaticCell;\n\nbind_interrupts!(\n    pub struct Irqs{\n        EXTI0 => exti::InterruptHandler<interrupt::typelevel::EXTI0>;\n});\n\n#[embassy_executor::main(executor = \"embassy_stm32::executor::Executor\", entry = \"cortex_m_rt::entry\")]\nasync fn async_main(_spawner: Spawner) {\n    // delay to allow probe-rs to connect for flashing\n    cortex_m::asm::delay(1_000_000);\n    let mut config = embassy_stm32::Config::default();\n    config.rcc.msi = Some(embassy_stm32::rcc::MSIRange::RANGE4M);\n    config.rcc.sys = embassy_stm32::rcc::Sysclk::MSI;\n    #[cfg(feature = \"defmt-serial\")]\n    {\n        // enable HSI clock\n        config.rcc.hsi = true;\n        // disable debug during sleep to reduce power consumption since we are\n        // using defmt-serial on LPUART1.\n        config.enable_debug_during_sleep = false;\n        // if we are using defmt-serial on LPUART1, we need to use HSI for the clock\n        // so that its registers are preserved during STOP modes.\n        config.rcc.mux.lpuart1sel = embassy_stm32::pac::rcc::vals::Lpuart1sel::HSI;\n    }\n    // Initialize STM32WL peripherals (use default config like wio-e5-async example)\n    let p = embassy_stm32::init(config);\n\n    #[cfg(feature = \"defmt-serial\")]\n    {\n        use embassy_stm32::mode::Blocking;\n        use embassy_stm32::usart::Uart;\n        let config = embassy_stm32::usart::Config::default();\n        let uart = Uart::new_blocking(p.LPUART1, p.PC0, p.PC1, config).expect(\"failed to configure UART!\");\n        static SERIAL: StaticCell<Uart<'static, Blocking>> = StaticCell::new();\n        defmt_serial::defmt_serial(SERIAL.init(uart));\n    }\n\n    info!(\"Hello World!\");\n\n    let mut button = ExtiInput::new(p.PA0, p.EXTI0, Pull::Up, Irqs);\n\n    info!(\"Press the USER button...\");\n\n    loop {\n        button.wait_for_falling_edge().await;\n        info!(\"Pressed!\");\n        button.wait_for_rising_edge().await;\n        info!(\"Released!\");\n    }\n}\n"
  },
  {
    "path": "examples/stm32wle5-lp/src/bin/i2c.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse defmt::*;\n#[cfg(feature = \"defmt-rtt\")]\nuse defmt_rtt as _;\nuse embassy_executor::Spawner;\nuse embassy_stm32::i2c::I2c;\nuse embassy_stm32::time::Hertz;\nuse embassy_stm32::{bind_interrupts, dma, i2c, peripherals};\nuse embassy_time::{Duration, Timer};\nuse panic_probe as _;\nuse static_cell::StaticCell;\n\nbind_interrupts!(struct Irqs{\n    I2C2_EV => i2c::EventInterruptHandler<peripherals::I2C2>;\n    I2C2_ER => i2c::ErrorInterruptHandler<peripherals::I2C2>;\n    DMA1_CHANNEL6 => dma::InterruptHandler<peripherals::DMA1_CH6>;\n    DMA1_CHANNEL7 => dma::InterruptHandler<peripherals::DMA1_CH7>;\n});\n\n#[embassy_executor::main(executor = \"embassy_stm32::executor::Executor\", entry = \"cortex_m_rt::entry\")]\nasync fn async_main(_spawner: Spawner) {\n    let mut config = embassy_stm32::Config::default();\n    // enable HSI clock\n    config.rcc.hsi = true;\n    // enable LSI clock for RTC\n    config.rcc.ls = embassy_stm32::rcc::LsConfig::default_lsi();\n    config.rcc.msi = Some(embassy_stm32::rcc::MSIRange::RANGE4M);\n    config.rcc.sys = embassy_stm32::rcc::Sysclk::MSI;\n    // enable ADC with HSI clock\n    config.rcc.mux.i2c2sel = embassy_stm32::pac::rcc::vals::I2c2sel::HSI;\n    #[cfg(feature = \"defmt-serial\")]\n    {\n        // disable debug during sleep to reduce power consumption since we are\n        // using defmt-serial on LPUART1.\n        config.enable_debug_during_sleep = false;\n        // if we are using defmt-serial on LPUART1, we need to use HSI for the clock\n        // so that its registers are preserved during STOP modes.\n        config.rcc.mux.lpuart1sel = embassy_stm32::pac::rcc::vals::Lpuart1sel::HSI;\n    }\n    // Initialize STM32WL peripherals (use default config like wio-e5-async example)\n    let p = embassy_stm32::init(config);\n\n    #[cfg(feature = \"defmt-serial\")]\n    {\n        use embassy_stm32::mode::Blocking;\n        use embassy_stm32::usart::Uart;\n        let config = embassy_stm32::usart::Config::default();\n        let uart = Uart::new_blocking(p.LPUART1, p.PC0, p.PC1, config).expect(\"failed to configure UART!\");\n        static SERIAL: StaticCell<Uart<'static, Blocking>> = StaticCell::new();\n        defmt_serial::defmt_serial(SERIAL.init(uart));\n    }\n\n    info!(\"Hello World!\");\n    let en3v3 = embassy_stm32::gpio::Output::new(\n        p.PA9,\n        embassy_stm32::gpio::Level::High,\n        embassy_stm32::gpio::Speed::High,\n    );\n    core::mem::forget(en3v3); // keep the output pin enabled\n\n    let mut i2c = I2c::new(p.I2C2, p.PB15, p.PA15, p.DMA1_CH6, p.DMA1_CH7, Irqs, {\n        let mut config = i2c::Config::default();\n        config.frequency = Hertz::khz(100);\n        config.timeout = Duration::from_millis(1000);\n        config\n    });\n\n    loop {\n        let mut buffer = [0; 2];\n        // read the temperature register of the onboard lm75\n        match i2c.read(0x48, &mut buffer).await {\n            Ok(_) => info!(\"--> {:?}\", buffer),\n            Err(e) => info!(\"--> Error: {:?}\", e),\n        }\n        Timer::after_secs(5).await;\n    }\n}\n"
  },
  {
    "path": "examples/stm32wle5-lp/stm32wle5.code-workspace",
    "content": "{\n\t\"folders\": [\n\t\t{\n\t\t\t\"path\": \".\"\n\t\t}\n\t],\n\t\"settings\": {\n\t\t\"rust-analyzer.cargo.target\": \"thumbv7em-none-eabi\",\n\t\t\"rust-analyzer.cargo.allTargets\": false,\n\t\t\"rust-analyzer.cargo.targetDir\": \"target/rust-analyzer\",\n\t\t\"rust-analyzer.checkOnSave\": true,\n\t}\n}\n"
  },
  {
    "path": "examples/wasm/Cargo.toml",
    "content": "[package]\nedition = \"2024\"\nname = \"embassy-wasm-example\"\nversion = \"0.1.0\"\nlicense = \"MIT OR Apache-2.0\"\npublish = false\n\n[lib]\ncrate-type = [\"cdylib\"]\n\n[dependencies]\nembassy-sync = { version = \"0.8.0\", path = \"../../embassy-sync\", features = [\"log\"] }\nembassy-executor = { version = \"0.10.0\", path = \"../../embassy-executor\", features = [\"platform-wasm\", \"executor-thread\", \"log\"] }\nembassy-time = { version = \"0.5.1\", path = \"../../embassy-time\", features = [\"log\", \"wasm\", ] }\n\nwasm-logger = \"0.2.0\"\nwasm-bindgen = \"0.2\"\nweb-sys = { version = \"0.3\", features = [\"Document\", \"Element\", \"HtmlElement\", \"Node\", \"Window\" ] }\nlog = \"0.4.11\"\n\n[profile.release]\ndebug = 2\n\n[package.metadata.embassy]\nbuild = [\n  { target = \"wasm32-unknown-unknown\", artifact-dir = \"out/examples/wasm\" }\n]\n"
  },
  {
    "path": "examples/wasm/README.md",
    "content": "# WASM example\n\nExamples use a CLI tool named `wasm-pack` to build this example:\n\n```\ncargo install wasm-pack --version 0.12.1\n```\n\n## Building\n\nTo build the example, run:\n\n```\nwasm-pack build --target web\n```\n\n## Running\n\nTo run the example, start a webserver server the local folder:\n\n\n```\npython -m http.server\n```\n\nThen, open a browser at http://127.0.0.1:8000 and watch the ticker print entries to the window.\n"
  },
  {
    "path": "examples/wasm/index.html",
    "content": "<!DOCTYPE html>\n<html>\n  <head>\n    <meta content=\"text/html;charset=utf-8\" http-equiv=\"Content-Type\"/>\n  </head>\n  <body>\n    <!-- Note the usage of `type=module` here as this is an ES6 module -->\n    <script type=\"module\">\n      // Use ES module import syntax to import functionality from the module\n      // that we have compiled.\n      //\n      // Note that the `default` import is an initialization function which\n      // will \"boot\" the module and make it ready to use. Currently browsers\n      // don't support natively imported WebAssembly as an ES module, but\n      // eventually the manual initialization won't be required!\n      import init from './pkg/embassy_wasm_example.js';\n      await init();\n    </script>\n    <h1>Log</h1>\n    <div>\n        <ul id=\"log\">\n        </ul>\n    </div>\n  </body>\n</html>\n"
  },
  {
    "path": "examples/wasm/src/lib.rs",
    "content": "use embassy_executor::Spawner;\nuse embassy_time::Timer;\n\n#[embassy_executor::task]\nasync fn ticker() {\n    let window = web_sys::window().expect(\"no global `window` exists\");\n\n    let mut counter = 0;\n    loop {\n        let document = window.document().expect(\"should have a document on window\");\n        let list = document.get_element_by_id(\"log\").expect(\"should have a log element\");\n\n        let li = document.create_element(\"li\").expect(\"error creating list item element\");\n        li.set_text_content(Some(&format!(\"tick {}\", counter)));\n\n        list.append_child(&li).expect(\"error appending list item\");\n        log::info!(\"tick {}\", counter);\n        counter += 1;\n\n        Timer::after_secs(1).await;\n    }\n}\n\n#[embassy_executor::main]\nasync fn main(spawner: Spawner) {\n    wasm_logger::init(wasm_logger::Config::default());\n    spawner.spawn(ticker().unwrap());\n}\n"
  },
  {
    "path": "fmtall.sh",
    "content": "#!/bin/bash\n\nset -euo pipefail\n\n# We need the nightly toolchain for this\nmv rust-toolchain-nightly.toml rust-toolchain.toml\n\n# Similar to the CI workflow, but don't just CHECK, actualy DO the formatting\nfind . -name '*.rs' -not -path '*target*' | xargs rustfmt --skip-children --unstable-features --edition 2024\n\n# Put the toolchains back, copy back to nightly and do a clean checkout of rust-toolchain\nmv rust-toolchain.toml rust-toolchain-nightly.toml\ngit checkout -- rust-toolchain.toml\n"
  },
  {
    "path": "release/bump-dependency.sh",
    "content": "#!/usr/bin/env bash\n# A helper script to bump version dependencies of a crate to a particular version. It does\n# not bump the version of the crate itself, only its entries in dependency lists.\n#\n# Usage (from the embassy repo folder): ./release/bump-dependency.sh embassy-time 0.4.0\n#\n# As a sanity check, after running this script, grep for old versions.\n#\nCRATE=$1\nTARGET_VER=$2\nfind . -name \"Cargo.toml\" | xargs sed -ri \"s/($CRATE = \\{.*version = \\\")[0-9]+.[0-9]+.?[0-9]*(\\\".*)/\\1$TARGET_VER\\2/g\"\n"
  },
  {
    "path": "release/release.toml",
    "content": "pre-release-replacements = [\n    {file=\"CHANGELOG.md\", search=\"Unreleased\", replace=\"{{version}}\", min=1},\n    {file=\"CHANGELOG.md\", search=\"ReleaseDate\", replace=\"{{date}}\", min=1},\n    {file=\"CHANGELOG.md\", search=\"<!-- next-header -->\", replace=\"<!-- next-header -->\\n## Unreleased - ReleaseDate\\n\", exactly=1},\n]\n"
  },
  {
    "path": "rust-toolchain-nightly.toml",
    "content": "[toolchain]\nchannel = \"nightly-2025-12-11\"\ncomponents = [ \"rust-src\", \"rustfmt\", \"llvm-tools\", \"miri\" ]\ntargets = [\n    \"thumbv7em-none-eabi\",\n    \"thumbv7m-none-eabi\",\n    \"thumbv6m-none-eabi\",\n    \"thumbv7em-none-eabihf\",\n    \"thumbv8m.main-none-eabihf\",\n    \"riscv32imac-unknown-none-elf\",\n    \"wasm32-unknown-unknown\",\n    \"armv7a-none-eabi\",\n]\n"
  },
  {
    "path": "rust-toolchain.toml",
    "content": "[toolchain]\nchannel = \"1.92\"\ncomponents = [ \"rust-src\", \"rustfmt\", \"llvm-tools\" ]\ntargets = [\n    \"thumbv7em-none-eabi\",\n    \"thumbv7m-none-eabi\",\n    \"thumbv6m-none-eabi\",\n    \"thumbv7em-none-eabihf\",\n    \"thumbv8m.main-none-eabihf\",\n    \"riscv32imac-unknown-none-elf\",\n    \"wasm32-unknown-unknown\",\n    \"armv7a-none-eabi\",\n    \"armv7r-none-eabi\",\n    \"armv7r-none-eabihf\",\n]\n"
  },
  {
    "path": "rustfmt.toml",
    "content": "group_imports = \"StdExternalCrate\"\nimports_granularity = \"Module\"\nedition = \"2024\"\nmax_width = 120\n"
  },
  {
    "path": "tests/link_ram_cortex_m.x",
    "content": "/* ##### EMBASSY NOTE\n    Originally from https://github.com/rust-embedded/cortex-m/blob/master/cortex-m-rt/link.x.in\n    Adjusted to put everything in RAM\n*/\n\n/* # Developer notes\n\n- Symbols that start with a double underscore (__) are considered \"private\"\n\n- Symbols that start with a single underscore (_) are considered \"semi-public\"; they can be\n  overridden in a user linker script, but should not be referred from user code (e.g. `extern \"C\" {\n  static mut __sbss }`).\n\n- `EXTERN` forces the linker to keep a symbol in the final binary. We use this to make sure a\n  symbol if not dropped if it appears in or near the front of the linker arguments and \"it's not\n  needed\" by any of the preceding objects (linker arguments)\n\n- `PROVIDE` is used to provide default values that can be overridden by a user linker script\n\n- On alignment: it's important for correctness that the VMA boundaries of both .bss and .data *and*\n  the LMA of .data are all 4-byte aligned. These alignments are assumed by the RAM initialization\n  routine. There's also a second benefit: 4-byte aligned boundaries means that you won't see\n  \"Address (..) is out of bounds\" in the disassembly produced by `objdump`.\n*/\n\n/* Provides information about the memory layout of the device */\n/* This will be provided by the user (see `memory.x`) or by a Board Support Crate */\nINCLUDE memory.x\n\n/* # Entry point = reset vector */\nEXTERN(__RESET_VECTOR);\nEXTERN(Reset);\nENTRY(Reset);\n\n/* # Exception vectors */\n/* This is effectively weak aliasing at the linker level */\n/* The user can override any of these aliases by defining the corresponding symbol themselves (cf.\n   the `exception!` macro) */\nEXTERN(__EXCEPTIONS); /* depends on all the these PROVIDED symbols */\n\nEXTERN(DefaultHandler);\n\nPROVIDE(NonMaskableInt = DefaultHandler);\nEXTERN(HardFaultTrampoline);\nPROVIDE(MemoryManagement = DefaultHandler);\nPROVIDE(BusFault = DefaultHandler);\nPROVIDE(UsageFault = DefaultHandler);\nPROVIDE(SecureFault = DefaultHandler);\nPROVIDE(SVCall = DefaultHandler);\nPROVIDE(DebugMonitor = DefaultHandler);\nPROVIDE(PendSV = DefaultHandler);\nPROVIDE(SysTick = DefaultHandler);\n\nPROVIDE(DefaultHandler = DefaultHandler_);\nPROVIDE(HardFault = HardFault_);\n\n/* # Interrupt vectors */\nEXTERN(__INTERRUPTS); /* `static` variable similar to `__EXCEPTIONS` */\n\n/* # Pre-initialization function */\n/* If the user overrides this using the `pre_init!` macro or by creating a `__pre_init` function,\n   then the function this points to will be called before the RAM is initialized. */\nPROVIDE(__pre_init = DefaultPreInit);\n\n/* # Sections */\nSECTIONS\n{\n  PROVIDE(_ram_start = ORIGIN(RAM));\n  PROVIDE(_ram_end = ORIGIN(RAM) + LENGTH(RAM));\n  PROVIDE(_stack_start = _ram_end);\n\n  /* ## Sections in RAM */\n  /* ### Vector table */\n  .vector_table ORIGIN(RAM) :\n  {\n    __vector_table = .;\n\n    /* Initial Stack Pointer (SP) value.\n     * We mask the bottom three bits to force 8-byte alignment.\n     * Despite having an assert for this later, it's possible that a separate\n     * linker script could override _stack_start after the assert is checked.\n     */\n    LONG(_stack_start & 0xFFFFFFF8);\n\n    /* Reset vector */\n    KEEP(*(.vector_table.reset_vector)); /* this is the `__RESET_VECTOR` symbol */\n\n    /* Exceptions */\n    __exceptions = .; /* start of exceptions */\n    KEEP(*(.vector_table.exceptions)); /* this is the `__EXCEPTIONS` symbol */\n    __eexceptions = .; /* end of exceptions */\n\n    /* Device specific interrupts */\n    KEEP(*(.vector_table.interrupts)); /* this is the `__INTERRUPTS` symbol */\n  } > RAM\n\n  PROVIDE(_stext = ADDR(.vector_table) + SIZEOF(.vector_table));\n\n  /* ### .text */\n  .text _stext :\n  {\n    __stext = .;\n    *(.Reset);\n\n    *(.text .text.*);\n\n    /* The HardFaultTrampoline uses the `b` instruction to enter `HardFault`,\n       so must be placed close to it. */\n    *(.HardFaultTrampoline);\n    *(.HardFault.*);\n\n    . = ALIGN(4); /* Pad .text to the alignment to workaround overlapping load section bug in old lld */\n    __etext = .;\n  } > RAM\n\n  /* ### .rodata */\n  .rodata : ALIGN(4)\n  {\n    . = ALIGN(4);\n    __srodata = .;\n    *(.rodata .rodata.*);\n\n    /* 4-byte align the end (VMA) of this section.\n       This is required by LLD to ensure the LMA of the following .data\n       section will have the correct alignment. */\n    . = ALIGN(4);\n    __erodata = .;\n  } > RAM\n\n  /* ## Sections in RAM */\n  /* ### .data */\n  .data : ALIGN(4)\n  {\n    . = ALIGN(4);\n    __sdata = .;\n    __edata = .; /* RAM: By setting __sdata=__edata cortex-m-rt has to copy 0 bytes as .data is already in RAM */\n\n    *(.data .data.*);\n    . = ALIGN(4); /* 4-byte align the end (VMA) of this section */\n  } > RAM\n  /* Allow sections from user `memory.x` injected using `INSERT AFTER .data` to\n   * use the .data loading mechanism by pushing __edata. Note: do not change\n   * output region or load region in those user sections! */\n  /* Link from RAM: Disabled, now __sdata == __edata\n  . = ALIGN(4);\n  __edata = .;\n  */\n\n  /* LMA of .data */\n  __sidata = LOADADDR(.data);\n\n  /* ### .gnu.sgstubs\n     This section contains the TrustZone-M veneers put there by the Arm GNU linker. */\n  /* Security Attribution Unit blocks must be 32 bytes aligned. */\n  /* Note that this pads the RAM usage to 32 byte alignment. */\n  .gnu.sgstubs : ALIGN(32)\n  {\n    . = ALIGN(32);\n    __veneer_base = .;\n    *(.gnu.sgstubs*)\n    . = ALIGN(32);\n  } > RAM\n  /* Place `__veneer_limit` outside the `.gnu.sgstubs` section because veneers are\n   * always inserted last in the section, which would otherwise be _after_ the `__veneer_limit` symbol.\n   */\n  . = ALIGN(32);\n  __veneer_limit = .;\n\n  /* ### .bss */\n  .bss (NOLOAD) : ALIGN(4)\n  {\n    . = ALIGN(4);\n    __sbss = .;\n    *(.bss .bss.*);\n    *(COMMON); /* Uninitialized C statics */\n    . = ALIGN(4); /* 4-byte align the end (VMA) of this section */\n  } > RAM\n  /* Allow sections from user `memory.x` injected using `INSERT AFTER .bss` to\n   * use the .bss zeroing mechanism by pushing __ebss. Note: do not change\n   * output region or load region in those user sections! */\n  . = ALIGN(4);\n  __ebss = .;\n\n  /* ### .uninit */\n  .uninit (NOLOAD) : ALIGN(4)\n  {\n    . = ALIGN(4);\n    __suninit = .;\n    *(.uninit .uninit.*);\n    . = ALIGN(4);\n    __euninit = .;\n  } > RAM\n\n  /* Place the heap right after `.uninit` in RAM */\n  PROVIDE(__sheap = __euninit);\n\n  /* ## .got */\n  /* Dynamic relocations are unsupported. This section is only used to detect relocatable code in\n     the input files and raise an error if relocatable code is found */\n  .got (NOLOAD) :\n  {\n    KEEP(*(.got .got.*));\n  }\n\n  /* ## Discarded sections */\n  /DISCARD/ :\n  {\n    /* Unused exception related info that only wastes space */\n    *(.ARM.exidx);\n    *(.ARM.exidx.*);\n    *(.ARM.extab.*);\n  }\n}\n\n/* Do not exceed this mark in the error messages below                                    | */\n/* # Alignment checks */\nASSERT(ORIGIN(RAM) % 4 == 0, \"\nERROR(cortex-m-rt): the start of the RAM region must be 4-byte aligned\");\n\nASSERT(__sdata % 4 == 0 && __edata % 4 == 0, \"\nBUG(cortex-m-rt): .data is not 4-byte aligned\");\n\nASSERT(__sidata % 4 == 0, \"\nBUG(cortex-m-rt): the LMA of .data is not 4-byte aligned\");\n\nASSERT(__sbss % 4 == 0 && __ebss % 4 == 0, \"\nBUG(cortex-m-rt): .bss is not 4-byte aligned\");\n\nASSERT(__sheap % 4 == 0, \"\nBUG(cortex-m-rt): start of .heap is not 4-byte aligned\");\n\nASSERT(_stack_start % 8 == 0, \"\nERROR(cortex-m-rt): stack start address is not 8-byte aligned.\nIf you have set _stack_start, check it's set to an address which is a multiple of 8 bytes.\nIf you haven't, stack starts at the end of RAM by default. Check that both RAM\norigin and length are set to multiples of 8 in the `memory.x` file.\");\n\n/* # Position checks */\n\n/* ## .vector_table\n *\n * If the *start* of exception vectors is not 8 bytes past the start of the\n * vector table, then we somehow did not place the reset vector, which should\n * live 4 bytes past the start of the vector table.\n */\nASSERT(__exceptions == ADDR(.vector_table) + 0x8, \"\nBUG(cortex-m-rt): the reset vector is missing\");\n\nASSERT(__eexceptions == ADDR(.vector_table) + 0x40, \"\nBUG(cortex-m-rt): the exception vectors are missing\");\n\nASSERT(SIZEOF(.vector_table) > 0x40, \"\nERROR(cortex-m-rt): The interrupt vectors are missing.\nPossible solutions, from most likely to less likely:\n- Link to a svd2rust generated device crate\n- Check that you actually use the device/hal/bsp crate in your code\n- Disable the 'device' feature of cortex-m-rt to build a generic application (a dependency\nmay be enabling it)\n- Supply the interrupt handlers yourself. Check the documentation for details.\");\n\n/* ## .text */\nASSERT(ADDR(.vector_table) + SIZEOF(.vector_table) <= _stext, \"\nERROR(cortex-m-rt): The .text section can't be placed inside the .vector_table section\nSet _stext to an address greater than the end of .vector_table (See output of `nm`)\");\n\nASSERT(_stext + SIZEOF(.text) < ORIGIN(RAM) + LENGTH(RAM), \"\nERROR(cortex-m-rt): The .text section must be placed inside the RAM memory.\nSet _stext to an address smaller than 'ORIGIN(RAM) + LENGTH(RAM)'\");\n\n/* # Other checks */\nASSERT(SIZEOF(.got) == 0, \"\nERROR(cortex-m-rt): .got section detected in the input object files\nDynamic relocations are not supported. If you are linking to C code compiled using\nthe 'cc' crate then modify your build script to compile the C code _without_\nthe -fPIC flag. See the documentation of the `cc::Build.pic` method for details.\");\n/* Do not exceed this mark in the error messages above                                    | */\n\n/* Provides weak aliases (cf. PROVIDED) for device specific interrupt handlers */\n/* This will usually be provided by a device crate generated using svd2rust (see `device.x`) */\nINCLUDE device.x\n"
  },
  {
    "path": "tests/mcxa2xx/.cargo/config.toml",
    "content": "[target.'cfg(all(target_arch = \"arm\", target_os = \"none\"))']\nrunner = \"teleprobe local run --chip MCXA276 --protocol swd --elf\"\n\n[build]\ntarget = \"thumbv8m.main-none-eabihf\" # Cortex-M33\n\n[env]\nDEFMT_LOG = \"trace\"\n"
  },
  {
    "path": "tests/mcxa2xx/Cargo.toml",
    "content": "[package]\nname = \"embassy-mcxa2xx-hil\"\nversion = \"0.1.0\"\nedition = \"2024\"\nlicense = \"MIT OR Apache-2.0\"\npublish = false\n\n[dependencies]\nteleprobe-meta = \"1.1\"\n\ncortex-m = { version = \"0.7\", features = [\"critical-section-single-core\"] }\ncortex-m-rt = { version = \"0.7\", features = [\"set-sp\", \"set-vtor\"] }\ncrc = \"3.4.0\"\ncritical-section = \"1.2.0\"\ndefmt = \"1.0\"\ndefmt-rtt = \"1.0\"\n\nembassy-embedded-hal = { version = \"0.6.0\", path = \"../../embassy-embedded-hal\" }\nembassy-executor = { version = \"0.10.0\", path = \"../../embassy-executor\", features = [\"platform-cortex-m\", \"executor-interrupt\", \"executor-thread\"], default-features = false }\nembassy-mcxa = { version = \"0.1.0\", path = \"../../embassy-mcxa\", features = [\"defmt\", \"unstable-pac\", \"mcxa2xx\"] }\nembassy-sync = { version = \"0.8.0\", path = \"../../embassy-sync\" }\nembassy-time = { version = \"0.5.1\", path = \"../../embassy-time\", features = [\"defmt\", \"defmt-timestamp-uptime\"] }\nembassy-time-driver = { version = \"0.2.2\", path = \"../../embassy-time-driver\", optional = true }\n\nembedded-io-async = \"0.7.0\"\npanic-probe = { version = \"1.0\", features = [\"print-defmt\"] }\nstatic_cell = \"2.1.1\"\nrand_core = \"0.9\"\n\n[profile.dev]\ndebug = 2\ndebug-assertions = true\nopt-level = 's'\noverflow-checks = true\n\n[profile.release]\ncodegen-units = 1\ndebug = 2\ndebug-assertions = false\nincremental = false\nlto = \"fat\"\nopt-level = 's'\noverflow-checks = false\n\n# do not optimize proc-macro crates = faster builds from scratch\n[profile.dev.build-override]\ncodegen-units = 8\ndebug = false\ndebug-assertions = false\nopt-level = 0\noverflow-checks = false\n\n[profile.release.build-override]\ncodegen-units = 8\ndebug = false\ndebug-assertions = false\nopt-level = 0\noverflow-checks = false\n\n[package.metadata.embassy]\nbuild = [\n  { target = \"thumbv8m.main-none-eabihf\", artifact-dir = \"out/tests/frdm-mcx-a266\" }\n]\n"
  },
  {
    "path": "tests/mcxa2xx/README.md",
    "content": "# HIL setup\n\n## Implemented:\n- [x] GPIO + interrupts\n- [x] I2C controller + target\n- [x] Uart\n- [x] ADC\n- [x] I3C\n- [x] CRC\n- [x] Watchdog\n- [ ] Flash\n- [x] CTimer PWM\n- [x] CTimer capture\n- [ ] SPI\n\n## Wiring:\n- `I2C controller` + `target`: `LPI2C1` on arduino connector `J4` to `LPI2C2` on camera connector `J9`\n  - `SDA`: `P1_0` -> `P1_8`\n  - `SCL`: `P1_1` -> `P1_9`\n- `Uart`: `LPUART3` on mikroBUS connector `J5` to `LPUART2` on arduino connector `J1`\n  - `P4_5` (TX) -> `P2_3` (RX)\n  - `P4_2` (RX) -> `P2_2` (TX)\n- `ADC` + `GPIO` + `CTimer capture` + `CTimer PWM`: `ADC0_A1` on arduino connector `J2` to `CT_INP8` + `CT0_MAT2` on arduino connector `J2`\n  - `P2_4` -> `P1_8` (overlaps with I2C SDA but on different connector)\n- `I3C`: Only one peripheral, so no loopback tests. But it's connected to the `P3T1755DP` temperature sensor\n- `SPI`: `LPSPI0` on mikroBUS connector `J6` to `LPSPI1` on arduino connector `J2`\n  - `CS`: `P1_3` -> `P3_11`\n  - `SCK`: `P1_1` -> `P3_10` (overlaps with I2C SCL but on different connector)\n  - `MISO`/`SDI`: `P1_2` -> `P3_9`\n  - `MOSI`/`SDO`: `P1_0` -> `P3_8`\n\nThat's 9 wire connections in total.\n"
  },
  {
    "path": "tests/mcxa2xx/build.rs",
    "content": "use std::path::PathBuf;\nuse std::{env, fs};\n\nfn main() {\n    let out = PathBuf::from(env::var(\"OUT_DIR\").unwrap());\n\n    let memory_x = include_bytes!(\"memory.x\");\n    fs::write(out.join(\"memory.x\"), memory_x).unwrap();\n\n    // copy main linker script.\n    fs::write(out.join(\"link_ram.x\"), include_bytes!(\"../link_ram_cortex_m.x\")).unwrap();\n    println!(\"cargo:rustc-link-search={}\", out.display());\n    println!(\"cargo:rerun-if-changed=../link_ram_cortex_m.x\");\n    println!(\"cargo:rustc-link-arg-bins=--nmagic\");\n    println!(\"cargo:rustc-link-arg-bins=-Tlink_ram.x\");\n    println!(\"cargo:rustc-link-arg-bins=-Tdefmt.x\");\n    println!(\"cargo:rustc-link-arg-bins=-Tteleprobe.x\");\n}\n"
  },
  {
    "path": "tests/mcxa2xx/memory.x",
    "content": "MEMORY\n{\n    FLASH : ORIGIN = 0x00000000, LENGTH = 1M\n    RAM   : ORIGIN = 0x20000000, LENGTH = 128K\n}\n"
  },
  {
    "path": "tests/mcxa2xx/src/bin/adc.rs",
    "content": "#![no_std]\n#![no_main]\n\nteleprobe_meta::target!(b\"frdm-mcx-a266\");\n\nuse embassy_executor::Spawner;\nuse embassy_mcxa::adc::{Command, CommandConfig, CommandId, Trigger};\nuse embassy_mcxa::bind_interrupts;\nuse embassy_mcxa::gpio::Output;\nuse hal::adc::{self, Adc};\nuse hal::clocks::config::Div8;\nuse hal::config::Config;\nuse hal::pac::adc::vals::Mode;\nuse hal::peripherals::ADC0;\nuse {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _};\n\nbind_interrupts!(struct Irqs {\n    ADC0 => adc::InterruptHandler<ADC0>;\n});\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let mut config = Config::default();\n    config.clock_cfg.sirc.fro_lf_div = Div8::from_divisor(1);\n\n    let p = hal::init(config);\n\n    let mut output = Output::new(\n        p.P1_8,\n        embassy_mcxa::gpio::Level::Low,\n        embassy_mcxa::gpio::DriveStrength::Normal,\n        embassy_mcxa::gpio::SlewRate::Slow,\n    );\n\n    let commands = &[Command::new_single(\n        p.P2_4,\n        CommandConfig {\n            resolution: Mode::DATA_16_BITS,\n            ..Default::default()\n        },\n    )];\n    let mut adc = Adc::new_async(\n        p.ADC0,\n        Irqs,\n        commands,\n        &[\n            Trigger {\n                target_command_id: CommandId::Cmd1,\n                ..Default::default()\n            },\n            Trigger {\n                target_command_id: CommandId::Cmd1,\n                ..Default::default()\n            },\n        ],\n        adc::Config::default(),\n    )\n    .unwrap();\n\n    adc.do_offset_calibration();\n    adc.do_auto_calibration();\n\n    // Set output low. ADC should measure (close to) GND\n\n    output.set_low();\n    embassy_time::Timer::after_millis(10).await;\n\n    adc.do_software_trigger(0b0001).unwrap();\n    let val = adc.wait_get_conversion().await.unwrap();\n    assert!(val.conv_value < 0x1000);\n    assert_eq!(val.command, CommandId::Cmd1);\n    assert_eq!(val.loop_channel_index, 0);\n    assert_eq!(val.trigger_id_source, 0);\n\n    // Set output high, so ADC should measure (close to) VDD\n\n    output.set_high();\n    embassy_time::Timer::after_millis(10).await;\n\n    adc.do_software_trigger(0b0010).unwrap();\n    let val = adc.wait_get_conversion().await.unwrap();\n    assert!(val.conv_value > 0xE000);\n    assert_eq!(val.command, CommandId::Cmd1);\n    assert_eq!(val.loop_channel_index, 0);\n    assert_eq!(val.trigger_id_source, 1);\n\n    defmt::info!(\"Test OK\");\n    cortex_m::asm::bkpt();\n}\n"
  },
  {
    "path": "tests/mcxa2xx/src/bin/adc_compare.rs",
    "content": "#![no_std]\n#![no_main]\n\nteleprobe_meta::target!(b\"frdm-mcx-a266\");\n\nuse embassy_executor::Spawner;\nuse embassy_mcxa::adc::{Command, CommandConfig, CommandId, Trigger};\nuse embassy_mcxa::bind_interrupts;\nuse embassy_mcxa::gpio::Output;\nuse hal::adc::{self, Adc};\nuse hal::clocks::config::Div8;\nuse hal::config::Config;\nuse hal::pac::adc::vals::Mode;\nuse hal::peripherals::ADC0;\nuse {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _};\n\nbind_interrupts!(struct Irqs {\n    ADC0 => adc::InterruptHandler<ADC0>;\n});\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let mut config = Config::default();\n    config.clock_cfg.sirc.fro_lf_div = Div8::from_divisor(1);\n\n    let mut p = hal::init(config);\n\n    let mut output = Output::new(\n        p.P1_8,\n        embassy_mcxa::gpio::Level::Low,\n        embassy_mcxa::gpio::DriveStrength::Normal,\n        embassy_mcxa::gpio::SlewRate::Slow,\n    );\n\n    {\n        defmt::info!(\"Store if\");\n\n        let commands = &[Command::new_single(\n            p.P2_4.reborrow(),\n            CommandConfig {\n                resolution: Mode::DATA_16_BITS,\n                compare: adc::Compare::StoreIf(adc::CompareFunction::GreaterThan(0x8000)),\n                ..Default::default()\n            },\n        )];\n        let mut adc = Adc::new_async(\n            p.ADC0.reborrow(),\n            Irqs,\n            commands,\n            &[Trigger {\n                target_command_id: CommandId::Cmd1,\n                ..Default::default()\n            }],\n            adc::Config::default(),\n        )\n        .unwrap();\n\n        adc.do_offset_calibration();\n        adc.do_auto_calibration();\n\n        defmt::info!(\"Low, None\");\n        // Output is low, so a trigger should not result in a store to fifo\n        adc.do_software_trigger(0b0001).unwrap();\n        assert!(adc.wait_get_conversion().await.is_none());\n\n        output.set_high();\n\n        // Await for the GPIO pin voltage to rise\n        embassy_time::Timer::after_millis(1).await;\n\n        defmt::info!(\"High, Some\");\n        // Output is high, so a trigger should result in a store to fifo\n        adc.do_software_trigger(0b0001).unwrap();\n        assert!(adc.wait_get_conversion().await.is_some());\n    }\n\n    output.set_low();\n\n    {\n        defmt::info!(\"Skip until\");\n\n        let commands = &[Command::new_single(\n            p.P2_4.reborrow(),\n            CommandConfig {\n                resolution: Mode::DATA_16_BITS,\n                compare: adc::Compare::SkipUntil(adc::CompareFunction::GreaterThan(0x8000)),\n                ..Default::default()\n            },\n        )];\n        let mut adc = Adc::new_async(\n            p.ADC0.reborrow(),\n            Irqs,\n            commands,\n            &[Trigger {\n                target_command_id: CommandId::Cmd1,\n                ..Default::default()\n            }],\n            adc::Config::default(),\n        )\n        .unwrap();\n\n        adc.do_offset_calibration();\n        adc.do_auto_calibration();\n\n        defmt::info!(\"Low, Error\");\n        // Output is low, so we're never getting a result\n        adc.do_software_trigger(0b0001).unwrap();\n        embassy_time::Timer::after_millis(100).await;\n        assert_eq!(adc.try_get_conversion(), Err(adc::Error::FifoPending));\n\n        defmt::info!(\"High, Some\");\n        // Set output high, and we should get a value without new trigger\n        output.set_high();\n\n        // Await for the GPIO pin voltage to rise\n        embassy_time::Timer::after_millis(1).await;\n\n        assert!(adc.wait_get_conversion().await.is_some());\n    }\n\n    defmt::info!(\"Test OK\");\n    cortex_m::asm::bkpt();\n}\n"
  },
  {
    "path": "tests/mcxa2xx/src/bin/cdog.rs",
    "content": "#![no_std]\n#![no_main]\n\nteleprobe_meta::target!(b\"frdm-mcx-a266\");\n\nuse embassy_executor::Spawner;\nuse embassy_time::Timer;\nuse hal::bind_interrupts;\nuse hal::cdog::{FaultControl, InterruptHandler, LockControl, PauseControl, Watchdog};\nuse hal::config::Config;\nuse {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _};\n\nbind_interrupts!(\n    struct Irqs {\n        CDOG0 => InterruptHandler;\n    }\n);\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let config = Config::default();\n    let p = hal::init(config);\n\n    let cdog_config = hal::cdog::Config {\n        timeout: FaultControl::EnableInterrupt,\n        miscompare: FaultControl::EnableInterrupt,\n        sequence: FaultControl::EnableInterrupt,\n        state: FaultControl::EnableInterrupt,\n        address: FaultControl::EnableInterrupt,\n        irq_pause: PauseControl::PauseTimer,\n        debug_halt: PauseControl::PauseTimer,\n        lock: LockControl::Unlocked,\n    };\n\n    let mut watchdog = Watchdog::new(p.CDOG0, Irqs, cdog_config).unwrap();\n\n    // First part of the example is to demonstrate how the secure counter feature of the cdog works.\n    watchdog.start(0xFFFFFF, 0);\n    watchdog.add(42);\n    watchdog.check(42);\n    watchdog.sub(2);\n    watchdog.check(40);\n    watchdog.start(0xFFFFFFFF, 0);\n    watchdog.check(0);\n\n    // Next check should generate an interrupt as checked value (=1) is different than the secure counter (=0).\n    watchdog.check(1);\n\n    // Now demonstrating how the instruction timer feature of the cdog works.\n    watchdog.start(0xFFF, 0);\n    assert_ne!(watchdog.get_instruction_timer(), 0);\n\n    while watchdog.get_instruction_timer() != 0 {\n        Timer::after_millis(1).await;\n    }\n\n    defmt::info!(\"Test OK\");\n    cortex_m::asm::bkpt();\n}\n"
  },
  {
    "path": "tests/mcxa2xx/src/bin/crc.rs",
    "content": "#![no_std]\n#![no_main]\n\nteleprobe_meta::target!(b\"frdm-mcx-a266\");\n\nuse embassy_executor::Spawner;\nuse hal::config::Config;\nuse hal::crc::Crc;\nuse {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _};\n\nconst CCITT_FALSE: crc::Algorithm<u16> = crc::Algorithm {\n    width: 16,\n    poly: 0x1021,\n    init: 0xffff,\n    refin: false,\n    refout: false,\n    xorout: 0,\n    check: 0x29b1,\n    residue: 0x0000,\n};\n\nconst POSIX: crc::Algorithm<u32> = crc::Algorithm {\n    width: 32,\n    poly: 0x04c1_1db7,\n    init: 0,\n    refin: false,\n    refout: false,\n    xorout: 0xffff_ffff,\n    check: 0x765e_7680,\n    residue: 0x0000,\n};\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let config = Config::default();\n    let mut p = hal::init(config);\n\n    defmt::info!(\"CRC example\");\n\n    let buf_u8 = [0x00u8, 0x11, 0x22, 0x33];\n    let buf_u16 = [0x0000u16, 0x1111, 0x2222, 0x3333];\n    let buf_u32 = [0x0000_0000u32, 0x1111_1111, 0x2222_2222, 0x3333_3333];\n\n    // CCITT False\n\n    let sw_crc = crc::Crc::<u16>::new(&CCITT_FALSE);\n    let mut digest = sw_crc.digest();\n    digest.update(&buf_u8);\n    let sw_sum = digest.finalize();\n\n    let mut crc = Crc::new_ccitt_false(p.CRC0.reborrow());\n    crc.feed(&buf_u8);\n    let sum = crc.finalize();\n    assert_eq!(sum, sw_sum);\n\n    let mut crc = Crc::new_ccitt_false(p.CRC0.reborrow());\n    crc.feed(&buf_u16);\n    let sum = crc.finalize();\n    assert_eq!(sum, 0xa467);\n\n    let mut crc = Crc::new_ccitt_false(p.CRC0.reborrow());\n    crc.feed(&buf_u32);\n    let sum = crc.finalize();\n    assert_eq!(sum, 0xe5c7);\n\n    // Maxim\n\n    let sw_crc = crc::Crc::<u16>::new(&crc::CRC_16_MAXIM_DOW);\n    let mut digest = sw_crc.digest();\n    digest.update(&buf_u8);\n    let sw_sum = digest.finalize();\n\n    let mut crc = Crc::new_maxim(p.CRC0.reborrow());\n    crc.feed(&buf_u8);\n    let sum = crc.finalize();\n    assert_eq!(sum, sw_sum);\n\n    let mut crc = Crc::new_maxim(p.CRC0.reborrow());\n    crc.feed(&buf_u16);\n    let sum = crc.finalize();\n    assert_eq!(sum, 0x2afe);\n\n    let mut crc = Crc::new_maxim(p.CRC0.reborrow());\n    crc.feed(&buf_u32);\n    let sum = crc.finalize();\n    assert_eq!(sum, 0x17d7);\n\n    // Kermit\n\n    let sw_crc = crc::Crc::<u16>::new(&crc::CRC_16_KERMIT);\n    let mut digest = sw_crc.digest();\n    digest.update(&buf_u8);\n    let sw_sum = digest.finalize();\n\n    let mut crc = Crc::new_kermit(p.CRC0.reborrow());\n    crc.feed(&buf_u8);\n    let sum = crc.finalize();\n    assert_eq!(sum, sw_sum);\n\n    let mut crc = Crc::new_kermit(p.CRC0.reborrow());\n    crc.feed(&buf_u16);\n    let sum = crc.finalize();\n    assert_eq!(sum, 0x66eb);\n\n    let mut crc = Crc::new_kermit(p.CRC0.reborrow());\n    crc.feed(&buf_u32);\n    let sum = crc.finalize();\n    assert_eq!(sum, 0x75ea);\n\n    // ISO HDLC\n\n    let sw_crc = crc::Crc::<u32>::new(&crc::CRC_32_ISO_HDLC);\n    let mut digest = sw_crc.digest();\n    digest.update(&buf_u8);\n    let sw_sum = digest.finalize();\n\n    let mut crc = Crc::new_iso_hdlc(p.CRC0.reborrow());\n    crc.feed(&buf_u8);\n    let sum = crc.finalize();\n    assert_eq!(sum, sw_sum);\n\n    let mut crc = Crc::new_iso_hdlc(p.CRC0.reborrow());\n    crc.feed(&buf_u16);\n    let sum = crc.finalize();\n    assert_eq!(sum, 0x8a61_4178);\n\n    let mut crc = Crc::new_iso_hdlc(p.CRC0.reborrow());\n    crc.feed(&buf_u32);\n    let sum = crc.finalize();\n    assert_eq!(sum, 0xfab5_d04e);\n\n    // POSIX\n\n    let sw_crc = crc::Crc::<u32>::new(&POSIX);\n    let mut digest = sw_crc.digest();\n    digest.update(&buf_u8);\n    let sw_sum = digest.finalize();\n\n    let mut crc = Crc::new_posix(p.CRC0.reborrow());\n    crc.feed(&buf_u8);\n    let sum = crc.finalize();\n\n    assert_eq!(sum, sw_sum);\n\n    let mut crc = Crc::new_posix(p.CRC0.reborrow());\n    crc.feed(&buf_u16);\n    let sum = crc.finalize();\n    assert_eq!(sum, 0x6d76_4f58);\n\n    let mut crc = Crc::new_posix(p.CRC0.reborrow());\n    crc.feed(&buf_u32);\n    let sum = crc.finalize();\n    assert_eq!(sum, 0x2a5b_cb90);\n\n    defmt::info!(\"Test OK\");\n    cortex_m::asm::bkpt();\n}\n"
  },
  {
    "path": "tests/mcxa2xx/src/bin/ctimer_capture.rs",
    "content": "//! CTimer Capture\n\n#![no_std]\n#![no_main]\n\nteleprobe_meta::target!(b\"frdm-mcx-a266\");\n\nuse embassy_executor::Spawner;\nuse embassy_time::Timer;\nuse hal::bind_interrupts;\nuse hal::clocks::config::Div8;\nuse hal::config::Config;\nuse hal::ctimer::CTimer;\nuse hal::ctimer::capture::{self, Capture, Edge, InterruptHandler};\nuse hal::gpio::{DriveStrength, Level, Output, SlewRate};\nuse hal::peripherals::CTIMER2;\nuse {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _};\n\nbind_interrupts!(struct Irqs {\n    CTIMER2 => InterruptHandler<CTIMER2>;\n});\n\nfn within(x: f32, target: f32, epsilon: f32) -> bool {\n    (x - target).abs() <= epsilon\n}\n\n#[embassy_executor::main]\nasync fn main(spawner: Spawner) {\n    let mut config = Config::default();\n    config.clock_cfg.sirc.fro_lf_div = Div8::from_divisor(1);\n\n    let p = hal::init(config);\n\n    let pin = Output::new(p.P1_8, Level::High, DriveStrength::Normal, SlewRate::Fast);\n\n    let ctimer = CTimer::new(p.CTIMER2, Default::default()).unwrap();\n    let mut config = capture::Config::default();\n    config.edge = Edge::RisingEdge;\n    let mut capture = Capture::new_with_input_pin(ctimer, p.CTIMER2_CH0, p.P2_4, Irqs, config).unwrap();\n\n    spawner.spawn(pin_task(pin).unwrap());\n\n    let one = capture.capture().await.unwrap();\n    let two = capture.capture().await.unwrap();\n    let diff = two - one;\n    let freq = diff.to_period(capture.frequency());\n    assert!(within(freq, 1.0, 1e-3));\n\n    defmt::info!(\"Test OK\");\n    cortex_m::asm::bkpt();\n}\n\n#[embassy_executor::task]\nasync fn pin_task(mut pin: Output<'static>) {\n    Timer::after_secs(1).await;\n\n    pin.set_high();\n    pin.set_low();\n    Timer::after_secs(1).await;\n    pin.set_high();\n    pin.set_low();\n\n    Timer::after_secs(1).await;\n}\n"
  },
  {
    "path": "tests/mcxa2xx/src/bin/dma.rs",
    "content": "#![no_std]\n#![no_main]\n\nteleprobe_meta::target!(b\"frdm-mcx-a266\");\n\nuse embassy_executor::Spawner;\nuse embassy_mcxa::clocks::config::Div8;\nuse embassy_mcxa::dma::{DmaChannel, TransferOptions};\nuse static_cell::ConstStaticCell;\nuse {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _};\n\nconst BUFFER_LENGTH: usize = 4;\n\nstatic SRC_BUFFER: ConstStaticCell<[u32; BUFFER_LENGTH]> = ConstStaticCell::new([1, 2, 3, 4]);\nstatic DEST_BUFFER: ConstStaticCell<[u32; BUFFER_LENGTH]> = ConstStaticCell::new([0; BUFFER_LENGTH]);\nstatic MEMSET_BUFFER: ConstStaticCell<[u32; BUFFER_LENGTH]> = ConstStaticCell::new([0; BUFFER_LENGTH]);\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let mut cfg = hal::config::Config::default();\n    cfg.clock_cfg.sirc.fro_12m_enabled = true;\n    cfg.clock_cfg.sirc.fro_lf_div = Some(Div8::no_div());\n    let p = hal::init(cfg);\n\n    let src = SRC_BUFFER.take();\n    let dst = DEST_BUFFER.take();\n    let mst = MEMSET_BUFFER.take();\n\n    let mut dma_ch0 = DmaChannel::new(p.DMA_CH0);\n    let transfer = dma_ch0\n        .mem_to_mem(src, dst, TransferOptions::COMPLETE_INTERRUPT)\n        .unwrap();\n    transfer.await.unwrap();\n\n    assert_eq!(src, dst);\n\n    let pattern: u32 = 0xDEADBEEF;\n    let transfer = dma_ch0\n        .memset(&pattern, mst, TransferOptions::COMPLETE_INTERRUPT)\n        .unwrap();\n    transfer.await.unwrap();\n\n    assert!(mst.iter().all(|&v| v == pattern));\n\n    defmt::info!(\"Test OK\");\n    cortex_m::asm::bkpt();\n}\n"
  },
  {
    "path": "tests/mcxa2xx/src/bin/gpio.rs",
    "content": "#![no_std]\n#![no_main]\n\nteleprobe_meta::target!(b\"frdm-mcx-a266\");\n\nuse embassy_executor::Spawner;\nuse embassy_mcxa::gpio::{self, Async, Input, Output};\nuse embassy_mcxa::{bind_interrupts, peripherals};\nuse hal::config::Config;\nuse {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _};\n\nbind_interrupts!(struct Irqs {\n    GPIO2 => gpio::InterruptHandler<peripherals::GPIO2>;\n    }\n);\n\n#[embassy_executor::main]\nasync fn main(spawner: Spawner) {\n    let config = Config::default();\n    let p = hal::init(config);\n\n    defmt::info!(\"Gpio test\");\n\n    let mut output = Output::new(\n        p.P1_8,\n        embassy_mcxa::gpio::Level::Low,\n        embassy_mcxa::gpio::DriveStrength::Normal,\n        embassy_mcxa::gpio::SlewRate::Slow,\n    );\n\n    spawner.spawn(wait(Input::new_async(p.P2_4, Irqs, embassy_mcxa::gpio::Pull::Down)).unwrap());\n\n    embassy_time::Timer::after_millis(40).await;\n    output.set_high();\n    assert!(output.is_set_high());\n    embassy_time::Timer::after_millis(40).await;\n    output.set_low();\n    assert!(output.is_set_low());\n    embassy_time::Timer::after_millis(40).await;\n    output.set_high();\n    assert!(output.is_set_high());\n    embassy_time::Timer::after_millis(40).await;\n    output.set_low();\n    assert!(output.is_set_low());\n    embassy_time::Timer::after_millis(40).await;\n    output.set_high();\n    assert!(output.is_set_high());\n    embassy_time::Timer::after_millis(40).await;\n\n    unreachable!(\"The wait task failed to see the output values\");\n}\n\n#[embassy_executor::task]\nasync fn wait(mut input: Input<'static, Async>) {\n    assert!(input.is_low());\n\n    input.wait_for_high().await;\n\n    embassy_time::Timer::after_millis(10).await;\n    assert!(input.is_high());\n    embassy_time::Timer::after_millis(10).await;\n\n    input.wait_for_low().await;\n\n    embassy_time::Timer::after_millis(10).await;\n    assert!(input.is_low());\n    embassy_time::Timer::after_millis(10).await;\n\n    input.wait_for_rising_edge().await;\n\n    embassy_time::Timer::after_millis(10).await;\n    assert!(input.is_high());\n    embassy_time::Timer::after_millis(10).await;\n\n    input.wait_for_falling_edge().await;\n\n    embassy_time::Timer::after_millis(10).await;\n    assert!(input.is_low());\n    embassy_time::Timer::after_millis(10).await;\n\n    input.wait_for_any_edge().await;\n\n    embassy_time::Timer::after_millis(10).await;\n    assert!(input.is_high());\n\n    defmt::info!(\"Test OK\");\n    cortex_m::asm::bkpt();\n}\n"
  },
  {
    "path": "tests/mcxa2xx/src/bin/i2c.rs",
    "content": "//! I2c controller + target\n\n#![no_std]\n#![no_main]\n\nteleprobe_meta::target!(b\"frdm-mcx-a266\");\n\nuse embassy_executor::Spawner;\nuse embassy_mcxa::i2c::Async;\nuse hal::bind_interrupts;\nuse hal::clocks::config::Div8;\nuse hal::config::Config;\nuse hal::i2c::{controller, target};\nuse hal::peripherals::{LPI2C1, LPI2C2};\nuse {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _};\n\nbind_interrupts!(\n    struct Irqs {\n        LPI2C1 => target::InterruptHandler<LPI2C1>;\n        LPI2C2 => controller::InterruptHandler<LPI2C2>;\n    }\n);\n\n#[embassy_executor::main]\nasync fn main(spawner: Spawner) {\n    let mut config = Config::default();\n    config.clock_cfg.sirc.fro_lf_div = Div8::from_divisor(1);\n\n    let p = hal::init(config);\n\n    defmt::info!(\"I2C controller + target test\");\n\n    let mut config = target::Config::default();\n    config.address = target::Address::Dual(0x13, 0x37);\n\n    let target = target::I2c::new_async(p.LPI2C1, p.P1_1, p.P1_0, Irqs, config).unwrap();\n\n    spawner.spawn(target_task(target).unwrap());\n\n    // Await for the target task to have started listening.\n    embassy_time::Timer::after_millis(10).await;\n\n    let config = controller::Config::default();\n    let mut i2c = controller::I2c::new_async(p.LPI2C2, p.P1_9, p.P1_8, Irqs, config).unwrap();\n\n    let mut buf = [0];\n\n    defmt::info!(\"Write read 0x13\");\n    i2c.async_write_read(0x13, &[13], &mut buf).await.unwrap();\n    assert_eq!(buf[0], 13);\n    defmt::info!(\"Write read 0x37\");\n    i2c.async_write_read(0x37, &[37], &mut buf).await.unwrap();\n    assert_eq!(buf[0], 37);\n\n    defmt::info!(\"Read 0x13\");\n    i2c.async_read(0x13, &mut buf).await.unwrap();\n    assert_eq!(buf[0], 13);\n    defmt::info!(\"Read 0x37\");\n    i2c.async_read(0x37, &mut buf).await.unwrap();\n    assert_eq!(buf[0], 37);\n\n    defmt::info!(\"Write 0x01\");\n    let error = i2c.async_write(0x01, &[0]).await.unwrap_err();\n    assert_eq!(error, controller::IOError::AddressNack);\n\n    defmt::info!(\"Test OK\");\n    cortex_m::asm::bkpt();\n}\n\n#[embassy_executor::task]\nasync fn target_task(mut target: target::I2c<'static, Async>) {\n    let mut addr0_value = [0];\n    let mut addr1_value = [0];\n\n    loop {\n        defmt::debug!(\"Target listen\");\n        let request = target.async_listen().await.unwrap();\n        defmt::info!(\"Received event {}\", request);\n\n        match request {\n            target::Request::Read(0x13) => {\n                defmt::dbg!(target.async_respond_to_read(&addr0_value).await.unwrap());\n            }\n            target::Request::Read(0x37) => {\n                target.async_respond_to_read(&addr1_value).await.unwrap();\n            }\n            target::Request::Write(0x13) => {\n                target.async_respond_to_write(&mut addr0_value).await.unwrap();\n            }\n            target::Request::Write(0x37) => {\n                target.async_respond_to_write(&mut addr1_value).await.unwrap();\n            }\n            _ => {}\n        }\n    }\n}\n"
  },
  {
    "path": "tests/mcxa2xx/src/bin/i3c.rs",
    "content": "#![no_std]\n#![no_main]\n\nteleprobe_meta::target!(b\"frdm-mcx-a266\");\n\nuse embassy_executor::Spawner;\nuse embassy_mcxa::bind_interrupts;\nuse embassy_mcxa::clocks::config::Div8;\nuse embassy_mcxa::i3c::controller::{self, BusType, I3c};\nuse embassy_time::Timer;\nuse hal::config::Config;\nuse hal::peripherals::I3C0;\nuse {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _};\n\nbind_interrupts!(\n    struct Irqs {\n        I3C0 => embassy_mcxa::i3c::InterruptHandler<I3C0>;\n    }\n);\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let mut config = Config::default();\n    config.clock_cfg.sirc.fro_lf_div = Div8::from_divisor(1);\n\n    let p = hal::init(config);\n\n    defmt::info!(\"I3C test\");\n\n    let config = controller::Config::default();\n    let mut i3c = I3c::new_async(p.I3C0, p.P1_9, p.P1_8, Irqs, config).unwrap();\n    let mut buf = [0u8; 2];\n\n    // ~~~~~~~~ //\n    // I2C mode //\n    // ~~~~~~~~ //\n\n    // RSTDAA: reset first to make sure device responds to I2c requests.\n    i3c.async_write(0x7e, &[0x06], BusType::I3cSdr).await.unwrap();\n\n    i3c.async_write_read(0x48, &[0x00], &mut buf, BusType::I2c)\n        .await\n        .unwrap();\n    let raw = f32::from(i16::from_be_bytes(buf) / 16);\n    let temp_i2c = raw * 0.0625;\n    defmt::info!(\"P3T1755 via I2C: {}C\", temp_i2c);\n    Timer::after_millis(10).await;\n\n    // ~~~~~~~~~~~~ //\n    // I3C SDR mode //\n    // ~~~~~~~~~~~~ //\n\n    // RSTDAA\n    i3c.async_write(0x7e, &[0x06], BusType::I3cSdr).await.unwrap();\n\n    Timer::after_micros(100).await;\n\n    // ENTDAA\n    i3c.async_write(0x7e, &[0x07], BusType::I3cSdr).await.unwrap();\n\n    // P3T1755 temperature register = 0x00\n    i3c.async_write_read(0x48, &[0x00], &mut buf, BusType::I3cSdr)\n        .await\n        .unwrap();\n    let raw = f32::from(i16::from_be_bytes(buf) / 16);\n    let temp_i3c_sdr = raw * 0.0625;\n    defmt::info!(\"P3T1755 via I3C SDR: {}C\", temp_i3c_sdr);\n\n    assert!((-40.0..120.0).contains(&temp_i2c));\n    assert!((-40.0..120.0).contains(&temp_i3c_sdr));\n\n    defmt::info!(\"Test OK\");\n    cortex_m::asm::bkpt();\n}\n"
  },
  {
    "path": "tests/mcxa2xx/src/bin/lpuart.rs",
    "content": "#![no_std]\n#![no_main]\n\n// TODO: Also test ringbuffered uart\n// NOTE: Blocking uart is hard to test, so maybe todo?\n\nteleprobe_meta::target!(b\"frdm-mcx-a266\");\n\nuse embassy_executor::Spawner;\nuse embassy_mcxa::bind_interrupts;\nuse embassy_mcxa::clocks::config::Div8;\nuse embassy_mcxa::lpuart::{Buffered, Lpuart};\nuse embassy_time::{Duration, WithTimeout as _};\nuse embedded_io_async::{Read, Write};\nuse hal::config::Config;\nuse static_cell::ConstStaticCell;\nuse {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _};\n\nconst MESSAGE_SIZE: usize = 69;\nconst MESSAGE: [u8; MESSAGE_SIZE] = *b\"You've found the HIL tests for MCXA! Hope you have a wonderful day :)\";\n\nbind_interrupts!(struct Irqs {\n    LPUART3 => hal::lpuart::BufferedInterruptHandler::<hal::peripherals::LPUART3>;\n});\n\n#[embassy_executor::main]\nasync fn main(spawner: Spawner) {\n    let mut config = Config::default();\n    config.clock_cfg.sirc.fro_12m_enabled = true;\n    config.clock_cfg.sirc.fro_lf_div = Some(Div8::no_div());\n\n    let p = hal::init(config);\n\n    defmt::info!(\"lpuart test\");\n\n    let config = hal::lpuart::Config {\n        baudrate_bps: 115_200,\n        rx_fifo_watermark: 0,\n        tx_fifo_watermark: 0,\n        ..Default::default()\n    };\n\n    static TX_BUF: ConstStaticCell<[u8; 256]> = ConstStaticCell::new([0u8; 256]);\n    static RX_BUF: ConstStaticCell<[u8; 256]> = ConstStaticCell::new([0u8; 256]);\n\n    let mut echo_uart = Lpuart::new_buffered(\n        p.LPUART3,\n        p.P4_5, // TX pin\n        p.P4_2, // RX pin\n        Irqs,\n        TX_BUF.take(),\n        RX_BUF.take(),\n        config,\n    )\n    .unwrap();\n\n    let mut dma_uart = Lpuart::new_async_with_dma(\n        p.LPUART2, // Peripheral\n        p.P2_2,    // TX pin\n        p.P2_3,    // RX pin\n        p.DMA_CH0, // TX DMA channel\n        p.DMA_CH1, // RX DMA channel\n        config,\n    )\n    .unwrap();\n\n    // Drain all the receivers in case there were little hiccups on creation\n    let mut buf = [0u8; 16];\n    while (echo_uart.read(&mut buf).with_timeout(Duration::from_millis(1)).await).is_ok() {}\n    while (dma_uart.read(&mut buf).with_timeout(Duration::from_millis(1)).await).is_ok() {}\n\n    spawner.spawn(echo_plus_1(echo_uart).unwrap());\n\n    let mut rx_buffer = [0; MESSAGE_SIZE];\n    embassy_time::Timer::after_millis(1).await;\n\n    defmt::info!(\"Sending message\");\n    dma_uart.write(&MESSAGE).await.unwrap();\n    defmt::info!(\"Done, waiting for response\");\n    dma_uart.read(&mut rx_buffer).await.unwrap();\n    assert_eq!(rx_buffer, MESSAGE);\n\n    defmt::info!(\"Test OK\");\n    cortex_m::asm::bkpt();\n}\n\n#[embassy_executor::task]\nasync fn echo_plus_1(mut uart: Lpuart<'static, Buffered>) {\n    let mut buf = [0u8; MESSAGE_SIZE];\n\n    uart.read_exact(&mut buf).await.unwrap();\n    defmt::info!(\"Received the message\");\n\n    assert_eq!(buf, MESSAGE);\n    embassy_time::Timer::after_millis(1).await;\n\n    defmt::info!(\"Sending back\");\n    uart.write_all(&buf).await.unwrap();\n    uart.flush().await.unwrap();\n\n    defmt::info!(\"Done\");\n}\n"
  },
  {
    "path": "tests/mcxa2xx/src/bin/pwm.rs",
    "content": "#![no_std]\n#![no_main]\n\nteleprobe_meta::target!(b\"frdm-mcx-a266\");\n\nuse embassy_executor::Spawner;\nuse hal::bind_interrupts;\nuse hal::clocks::config::Div8;\nuse hal::clocks::periph_helpers::CTimerClockSel;\nuse hal::config::Config;\nuse hal::ctimer::CTimer;\nuse hal::ctimer::capture::{self, Capture, Edge, InterruptHandler};\nuse hal::ctimer::pwm::{SetDutyCycle, SinglePwm};\nuse hal::peripherals::CTIMER2;\nuse {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _};\n\nbind_interrupts!(struct Irqs {\n    CTIMER2 => InterruptHandler<CTIMER2>;\n});\n\nfn within(x: f32, target: f32, epsilon: f32) -> bool {\n    (x - target).abs() <= epsilon\n}\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let mut config = Config::default();\n    config.clock_cfg.sirc.fro_lf_div = Div8::from_divisor(1);\n\n    let mut p = hal::init(config);\n\n    let mut config: hal::ctimer::Config = Default::default();\n    config.source = CTimerClockSel::Clk1M;\n    let pin_ctimer = CTimer::new(p.CTIMER1.reborrow(), config).unwrap();\n    let mut config: hal::ctimer::pwm::Config = Default::default();\n    config.freq = 20_000;\n    let mut pin_pwm = SinglePwm::new(pin_ctimer, p.CTIMER1_CH0, p.CTIMER1_CH2, p.P2_4, config).unwrap();\n\n    pin_pwm.set_duty_cycle_percent(50).unwrap();\n\n    let ctimer = CTimer::new(p.CTIMER2, Default::default()).unwrap();\n    let mut config = capture::Config::default();\n    config.edge = Edge::RisingEdge;\n    let mut capture = Capture::new_with_input_pin(ctimer, p.CTIMER2_CH0, p.P1_8, Irqs, config).unwrap();\n\n    let one = capture.capture().await.unwrap();\n    let two = capture.capture().await.unwrap();\n    let diff = two - one;\n    let freq = diff.to_frequency(capture.frequency());\n    assert!(within(freq, 20_000.0, 0.1));\n\n    pin_pwm.set_duty_cycle_percent(75).unwrap();\n\n    let one = capture.capture().await.unwrap();\n    let two = capture.capture().await.unwrap();\n    let diff = two - one;\n    let freq = diff.to_frequency(capture.frequency());\n    assert!(within(freq, 20_000.0, 0.1));\n\n    defmt::info!(\"Test OK\");\n    cortex_m::asm::bkpt();\n}\n"
  },
  {
    "path": "tests/mcxa2xx/src/bin/rtc_alarm.rs",
    "content": "#![no_std]\n#![no_main]\n\nteleprobe_meta::target!(b\"frdm-mcx-a266\");\n\nuse embassy_executor::Spawner;\nuse embassy_mcxa::bind_interrupts;\nuse hal::peripherals::RTC0;\nuse hal::rtc::{DateTime, InterruptHandler, Rtc};\nuse {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _};\n\nbind_interrupts!(struct Irqs {\n    RTC => InterruptHandler<RTC0>;\n});\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let p = hal::init(hal::config::Config::default());\n\n    let mut rtc = Rtc::new(p.RTC0, Irqs, Default::default());\n\n    let now = DateTime {\n        year: 2025,\n        month: 10,\n        day: 15,\n        hour: 14,\n        minute: 30,\n        second: 0,\n    };\n\n    rtc.stop();\n\n    rtc.set_datetime(now);\n\n    let mut alarm = now;\n    alarm.second += 1;\n\n    rtc.wait_for_alarm(alarm).await;\n    defmt::info!(\"Test OK\");\n    cortex_m::asm::bkpt();\n}\n"
  },
  {
    "path": "tests/mcxa2xx/src/bin/trng.rs",
    "content": "#![no_std]\n#![no_main]\n\nteleprobe_meta::target!(b\"frdm-mcx-a266\");\n\nuse embassy_executor::Spawner;\nuse hal::bind_interrupts;\nuse hal::config::Config;\nuse hal::peripherals::TRNG0;\nuse hal::trng::{self, InterruptHandler, Trng};\nuse rand_core::RngCore;\nuse {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _};\n\nbind_interrupts!(\n    struct Irqs {\n        TRNG0 => InterruptHandler<TRNG0>;\n    }\n);\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let config = Config::default();\n    let mut p = hal::init(config);\n\n    // Note: this test might fail once every ~2^28 or so.\n\n    // Static\n    let mut trng = Trng::new_blocking_128(p.TRNG0.reborrow());\n    let rand1 = trng.blocking_next_u32();\n    let rand2 = trng.blocking_next_u32();\n    assert_ne!(rand1, rand2);\n    drop(trng);\n\n    let mut trng = Trng::new_blocking_256(p.TRNG0.reborrow());\n    let rand1 = trng.blocking_next_u32();\n    let rand2 = trng.blocking_next_u32();\n    assert_ne!(rand1, rand2);\n    drop(trng);\n\n    let mut trng = Trng::new_blocking_512(p.TRNG0.reborrow());\n    let rand1 = trng.blocking_next_u32();\n    let rand2 = trng.blocking_next_u32();\n    assert_ne!(rand1, rand2);\n    drop(trng);\n\n    // Blocking\n    let config = trng::Config::default();\n    let mut trng = Trng::new_blocking_with_custom_config(p.TRNG0.reborrow(), config);\n\n    let rand1 = trng.blocking_next_u32();\n    let rand2 = trng.blocking_next_u32();\n    assert_ne!(rand1, rand2);\n\n    let rand1 = trng.blocking_next_u64();\n    let rand2 = trng.blocking_next_u64();\n    assert_ne!(rand1, rand2);\n\n    let rand1 = trng.next_u32();\n    let rand2 = trng.next_u32();\n    assert_ne!(rand1, rand2);\n\n    let rand1 = trng.next_u64();\n    let rand2 = trng.next_u64();\n    assert_ne!(rand1, rand2);\n    drop(trng);\n\n    // Async\n    let mut trng = Trng::new_with_custom_config(p.TRNG0.reborrow(), Irqs, config);\n\n    let rand1 = trng.async_next_u32().await.unwrap();\n    let rand2 = trng.async_next_u32().await.unwrap();\n    assert_ne!(rand1, rand2);\n\n    let rand1 = trng.async_next_u64().await.unwrap();\n    let rand2 = trng.async_next_u64().await.unwrap();\n    assert_ne!(rand1, rand2);\n\n    let mut rand1 = [0u8; 10];\n    let mut rand2 = [0u8; 10];\n    trng.async_fill_bytes(&mut rand1).await.unwrap();\n    trng.async_fill_bytes(&mut rand2).await.unwrap();\n\n    let mut rand1 = [0u32; 8];\n    let mut rand2 = [0u32; 8];\n    trng.async_next_block(&mut rand1).await.unwrap();\n    trng.async_next_block(&mut rand2).await.unwrap();\n    assert_ne!(rand1, rand2);\n    drop(trng);\n\n    defmt::info!(\"Test OK\");\n    cortex_m::asm::bkpt();\n}\n"
  },
  {
    "path": "tests/mcxa2xx/src/bin/wwdt_interrupt.rs",
    "content": "#![no_std]\n#![no_main]\n\nteleprobe_meta::target!(b\"frdm-mcx-a266\");\n\nuse core::marker::PhantomData;\nuse core::sync::atomic::{AtomicBool, Ordering};\n\nuse embassy_executor::Spawner;\nuse embassy_time::{Duration, Timer};\nuse hal::bind_interrupts;\nuse hal::config::Config;\nuse hal::interrupt::typelevel::{self, Handler};\nuse hal::peripherals::WWDT0;\nuse hal::wwdt::{Instance, InterruptHandler, Watchdog};\nuse {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _};\n\nbind_interrupts!(\n    struct Irqs {\n        WWDT0 => InterruptHandler<WWDT0>, TestInterruptHandler<WWDT0>;\n    }\n);\n\nstatic INTERRUPT_TRIGGERED: AtomicBool = AtomicBool::new(false);\n\npub struct TestInterruptHandler<T: Instance> {\n    _phantom: PhantomData<T>,\n}\n\nimpl<T: Instance> Handler<typelevel::WWDT0> for TestInterruptHandler<T> {\n    unsafe fn on_interrupt() {\n        INTERRUPT_TRIGGERED.store(true, Ordering::Relaxed);\n    }\n}\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let config = Config::default();\n    let p = hal::init(config);\n\n    let wwdt_config = hal::wwdt::Config {\n        timeout: Duration::from_millis(50),\n        warning: Some(Duration::from_micros(4000)),\n    };\n\n    let mut watchdog = Watchdog::new(p.WWDT0, Irqs, wwdt_config).unwrap();\n\n    assert!(!INTERRUPT_TRIGGERED.load(Ordering::Relaxed));\n\n    // Set to watchdog to generate interrupt if it's not fed within 50ms, and start it.\n    // The warning interrupt will trigger 4ms before the timeout.\n    watchdog.start();\n\n    for _ in 1..=5 {\n        assert!(!INTERRUPT_TRIGGERED.load(Ordering::Relaxed));\n        Timer::after_millis(10).await;\n        watchdog.feed();\n    }\n\n    for _ in 1..=5 {\n        assert!(!INTERRUPT_TRIGGERED.load(Ordering::Relaxed));\n        Timer::after_millis(10).await;\n    }\n\n    assert!(INTERRUPT_TRIGGERED.load(Ordering::Relaxed));\n    defmt::info!(\"Test OK\");\n    cortex_m::asm::bkpt();\n}\n"
  },
  {
    "path": "tests/mspm0/.cargo/config.toml",
    "content": "[target.'cfg(all(target_arch = \"arm\", target_os = \"none\"))']\nrunner = \"teleprobe local run --chip MSPM0G3507 --protocol swd --elf\"\n\n[build]\ntarget = \"thumbv6m-none-eabi\"\n\n[env]\nDEFMT_LOG = \"trace,embassy_hal_internal=debug\"\n"
  },
  {
    "path": "tests/mspm0/Cargo.toml",
    "content": "[package]\nedition = \"2024\"\nname = \"embassy-mspm0-tests\"\nversion = \"0.1.0\"\nlicense = \"MIT OR Apache-2.0\"\npublish = false\n\n[features]\nmspm0g3507 = [ \"embassy-mspm0/mspm0g3507pm\" ]\nmspm0g3519 = [ \"embassy-mspm0/mspm0g3519pz\" ]\n\n[dependencies]\nteleprobe-meta = \"1.1\"\n\nembassy-sync = { version = \"0.8.0\", path = \"../../embassy-sync\", features = [ \"defmt\" ] }\nembassy-executor = { version = \"0.10.0\", path = \"../../embassy-executor\", features = [ \"platform-cortex-m\", \"executor-thread\", \"defmt\" ] }\nembassy-futures = { version = \"0.1.2\", path = \"../../embassy-futures\" }\nembassy-time = { version = \"0.5.1\", path = \"../../embassy-time\", features = [ \"defmt\" ] }\nembassy-mspm0 = { version = \"0.1.0\", path = \"../../embassy-mspm0\", features = [ \"rt\", \"defmt\", \"unstable-pac\", \"time-driver-any\" ]  }\nembassy-embedded-hal = { version = \"0.6.0\", path = \"../../embassy-embedded-hal/\"}\n\ndefmt = \"1.0.1\"\ndefmt-rtt = \"1.0.0\"\n\ncortex-m = { version = \"0.7.6\", features = [ \"inline-asm\", \"critical-section-single-core\" ]}\ncortex-m-rt = \"0.7.0\"\nembedded-hal = { package = \"embedded-hal\", version = \"1.0\" }\nembedded-hal-async = { version = \"1.0\" }\nembedded-io = { version = \"0.7.1\", features = [\"defmt\"] }\nembedded-io-async = { version = \"0.7.0\", features = [\"defmt\"] }\npanic-probe = { version = \"1.0.0\", features = [\"print-defmt\"] }\nstatic_cell = \"2\"\nportable-atomic = { version = \"1.5\", features = [\"critical-section\"] }\n\n[profile.dev]\ndebug = 2\ndebug-assertions = true\nopt-level = 's'\noverflow-checks = true\n\n[profile.release]\ncodegen-units = 1\ndebug = 2\ndebug-assertions = false\nincremental = false\nlto = \"fat\"\nopt-level = 's'\noverflow-checks = false\n\n# do not optimize proc-macro crates = faster builds from scratch\n[profile.dev.build-override]\ncodegen-units = 8\ndebug = false\ndebug-assertions = false\nopt-level = 0\noverflow-checks = false\n\n[profile.release.build-override]\ncodegen-units = 8\ndebug = false\ndebug-assertions = false\nopt-level = 0\noverflow-checks = false\n\n[package.metadata.embassy]\nbuild = [\n  { target = \"thumbv6m-none-eabi\", features = [\"mspm0g3507\"], artifact-dir = \"out/tests/mspm0g3507\" }\n]\n"
  },
  {
    "path": "tests/mspm0/build.rs",
    "content": "use std::error::Error;\nuse std::path::PathBuf;\nuse std::{env, fs};\n\nfn main() -> Result<(), Box<dyn Error>> {\n    let out = PathBuf::from(env::var(\"OUT_DIR\").unwrap());\n\n    #[cfg(feature = \"mspm0g3507\")]\n    let memory_x = include_bytes!(\"memory_g3507.x\");\n\n    #[cfg(feature = \"mspm0g3519\")]\n    let memory_x = include_bytes!(\"memory_g3519.x\");\n\n    fs::write(out.join(\"memory.x\"), memory_x).unwrap();\n\n    println!(\"cargo:rustc-link-search={}\", out.display());\n    println!(\"cargo:rerun-if-changed=../link_ram_cortex_m.x\");\n    // copy main linker script.\n    fs::write(out.join(\"link_ram.x\"), include_bytes!(\"../link_ram_cortex_m.x\")).unwrap();\n\n    println!(\"cargo:rustc-link-arg-bins=--nmagic\");\n    println!(\"cargo:rustc-link-arg-bins=-Tlink_ram.x\");\n    println!(\"cargo:rustc-link-arg-bins=-Tdefmt.x\");\n    println!(\"cargo:rustc-link-arg-bins=-Tteleprobe.x\");\n    // You must tell cargo to link interrupt groups if the rt feature is enabled.\n    println!(\"cargo:rustc-link-arg-bins=-Tinterrupt_group.x\");\n\n    Ok(())\n}\n"
  },
  {
    "path": "tests/mspm0/memory_g3507.x",
    "content": "MEMORY\n{\n  FLASH : ORIGIN = 0x00000000, LENGTH = 128K\n  /* Select non-parity range of SRAM due to SRAM_ERR_01 errata in SLAZ758 */\n  RAM   : ORIGIN = 0x20200000, LENGTH = 32K\n}\n"
  },
  {
    "path": "tests/mspm0/memory_g3519.x",
    "content": "MEMORY\n{\n  FLASH : ORIGIN = 0x00000000, LENGTH = 128K\n  /* Select non-parity range of SRAM due to SRAM_ERR_01 errata in SLAZ758 */\n  RAM   : ORIGIN = 0x20200000, LENGTH = 64K\n}\n"
  },
  {
    "path": "tests/mspm0/src/bin/dma.rs",
    "content": "#![no_std]\n#![no_main]\n\n#[cfg(feature = \"mspm0g3507\")]\nteleprobe_meta::target!(b\"lp-mspm0g3507\");\n\n#[cfg(feature = \"mspm0g3519\")]\nteleprobe_meta::target!(b\"lp-mspm0g3519\");\n\nuse core::slice;\n\nuse defmt::{assert, assert_eq, *};\nuse embassy_executor::Spawner;\nuse embassy_mspm0::Peri;\nuse embassy_mspm0::dma::{Channel, Transfer, TransferMode, TransferOptions, Word};\nuse {defmt_rtt as _, panic_probe as _};\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let mut p = embassy_mspm0::init(Default::default());\n    info!(\"Hello World!\");\n\n    {\n        info!(\"Single u8 read (blocking)\");\n        single_read(p.DMA_CH0.reborrow(), 0x41_u8);\n\n        info!(\"Single u16 read (blocking)\");\n        single_read(p.DMA_CH0.reborrow(), 0xFF41_u16);\n\n        info!(\"Single u32 read (blocking)\");\n        single_read(p.DMA_CH0.reborrow(), 0xFFEE_FF41_u32);\n\n        info!(\"Single u64 read (blocking)\");\n        single_read(p.DMA_CH0.reborrow(), 0x0011_2233_FFEE_FF41_u64);\n    }\n\n    // Widening transfers\n    {\n        info!(\"Single u8 read to u16\");\n        widening_single_read::<u8, u16>(p.DMA_CH0.reborrow(), 0x41);\n\n        info!(\"Single u8 read to u32\");\n        widening_single_read::<u8, u32>(p.DMA_CH0.reborrow(), 0x43);\n\n        info!(\"Single u8 read to u64\");\n        widening_single_read::<u8, u64>(p.DMA_CH0.reborrow(), 0x47);\n\n        info!(\"Single u16 read to u32\");\n        widening_single_read::<u16, u32>(p.DMA_CH0.reborrow(), 0xAE43);\n\n        info!(\"Single u16 read to u64\");\n        widening_single_read::<u16, u64>(p.DMA_CH0.reborrow(), 0xAF47);\n\n        info!(\"Single u32 read to u64\");\n        widening_single_read::<u32, u64>(p.DMA_CH0.reborrow(), 0xDEAD_AF47);\n    }\n\n    // Narrowing transfers.\n    {\n        info!(\"Single u16 read to u8\");\n        narrowing_single_read::<u16, u8>(p.DMA_CH0.reborrow(), 0x4142);\n\n        info!(\"Single u32 read to u8\");\n        narrowing_single_read::<u32, u8>(p.DMA_CH0.reborrow(), 0x4142_2414);\n\n        info!(\"Single u64 read to u8\");\n        narrowing_single_read::<u64, u8>(p.DMA_CH0.reborrow(), 0x4142_2414_5153_7776);\n\n        info!(\"Single u32 read to u16\");\n        narrowing_single_read::<u32, u16>(p.DMA_CH0.reborrow(), 0x4142_2414);\n\n        info!(\"Single u64 read to u16\");\n        narrowing_single_read::<u64, u16>(p.DMA_CH0.reborrow(), 0x4142_2414_5153_7776);\n\n        info!(\"Single u64 read to u32\");\n        narrowing_single_read::<u64, u32>(p.DMA_CH0.reborrow(), 0x4142_2414_5153_7776);\n    }\n\n    {\n        info!(\"Single u8 read (async)\");\n        async_single_read(p.DMA_CH0.reborrow(), 0x42_u8).await;\n\n        info!(\"Single u16 read (async)\");\n        async_single_read(p.DMA_CH0.reborrow(), 0xAE42_u16).await;\n\n        info!(\"Single u32 read (async)\");\n        async_single_read(p.DMA_CH0.reborrow(), 0xFE44_1500_u32).await;\n\n        info!(\"Single u64 read (async)\");\n        async_single_read(p.DMA_CH0.reborrow(), 0x8F7F_6F5F_4F3F_2F1F_u64).await;\n    }\n\n    {\n        info!(\"Multiple u8 reads (blocking)\");\n        block_read::<_, 16>(p.DMA_CH0.reborrow(), 0x98_u8);\n\n        info!(\"Multiple u16 reads (blocking)\");\n        block_read::<_, 2>(p.DMA_CH0.reborrow(), 0x9801_u16);\n\n        info!(\"Multiple u32 reads (blocking)\");\n        block_read::<_, 4>(p.DMA_CH0.reborrow(), 0x9821_9801_u32);\n\n        info!(\"Multiple u64 reads (blocking)\");\n        block_read::<_, 4>(p.DMA_CH0.reborrow(), 0xABCD_EF01_2345_6789_u64);\n    }\n\n    {\n        info!(\"Multiple u8 reads (async)\");\n        async_block_read::<_, 8>(p.DMA_CH0.reborrow(), 0x86_u8).await;\n\n        info!(\"Multiple u16 reads (async)\");\n        async_block_read::<_, 6>(p.DMA_CH0.reborrow(), 0x7777_u16).await;\n\n        info!(\"Multiple u32 reads (async)\");\n        async_block_read::<_, 3>(p.DMA_CH0.reborrow(), 0xA5A5_A5A5_u32).await;\n\n        info!(\"Multiple u64 reads (async)\");\n        async_block_read::<_, 14>(p.DMA_CH0.reborrow(), 0x5A5A_5A5A_A5A5_A5A5_u64).await;\n    }\n\n    // Intentionally skip testing multiple reads in single transfer mode.\n    //\n    // If the destination length is greater than 1 and single transfer mode is used then two transfers\n    // are performed in a trigger. Similarly with any other length of destination above 2, only 2 transfers\n    // are performed. Issuing another trigger (resume) results in no further progress. More than likely\n    // the test does not work due to some combination of a hardware bug and the datasheet being unclear\n    // regarding what ends a software trigger.\n    //\n    // However this case works fine with a hardware trigger (such as the ADC hardware trigger).\n\n    {\n        info!(\"Single u8 write (blocking)\");\n        single_write(p.DMA_CH0.reborrow(), 0x41_u8);\n\n        info!(\"Single u16 write (blocking)\");\n        single_write(p.DMA_CH0.reborrow(), 0x4142_u16);\n\n        info!(\"Single u32 write (blocking)\");\n        single_write(p.DMA_CH0.reborrow(), 0x4142_4344_u32);\n\n        info!(\"Single u64 write (blocking)\");\n        single_write(p.DMA_CH0.reborrow(), 0x4142_4344_4546_4748_u64);\n    }\n\n    {\n        info!(\"Single u8 write (async)\");\n        async_single_write(p.DMA_CH0.reborrow(), 0xAA_u8).await;\n\n        info!(\"Single u16 write (async)\");\n        async_single_write(p.DMA_CH0.reborrow(), 0xBBBB_u16).await;\n\n        info!(\"Single u32 write (async)\");\n        async_single_write(p.DMA_CH0.reborrow(), 0xCCCC_CCCC_u32).await;\n\n        info!(\"Single u64 write (async)\");\n        async_single_write(p.DMA_CH0.reborrow(), 0xDDDD_DDDD_DDDD_DDDD_u64).await;\n    }\n\n    {\n        info!(\"Multiple u8 writes (blocking)\");\n        block_write(p.DMA_CH0.reborrow(), &[0xFF_u8, 0x7F, 0x3F, 0x1F]);\n\n        info!(\"Multiple u16 writes (blocking)\");\n        block_write(p.DMA_CH0.reborrow(), &[0xFFFF_u16, 0xFF7F, 0xFF3F, 0xFF1F]);\n\n        info!(\"Multiple u32 writes (blocking)\");\n        block_write(\n            p.DMA_CH0.reborrow(),\n            &[0xFF00_00FF_u32, 0xFF00_007F, 0x0000_FF3F, 0xFF1F_0000],\n        );\n\n        info!(\"Multiple u64 writes (blocking)\");\n        block_write(\n            p.DMA_CH0.reborrow(),\n            &[\n                0xFF00_0000_0000_00FF_u64,\n                0x0000_FF00_007F_0000,\n                0x0000_FF3F_0000_0000,\n                0xFF1F_0000_1111_837A,\n            ],\n        );\n    }\n\n    {\n        info!(\"Multiple u8 writes (async)\");\n        async_block_write(p.DMA_CH0.reborrow(), &[0u8, 1, 2, 3]).await;\n\n        info!(\"Multiple u16 writes (async)\");\n        async_block_write(p.DMA_CH0.reborrow(), &[0x9801u16, 0x9802, 0x9803, 0x9800, 0x9000]).await;\n\n        info!(\"Multiple u32 writes (async)\");\n        async_block_write(p.DMA_CH0.reborrow(), &[0x9801_ABCDu32, 0xFFAC_9802, 0xDEAD_9803]).await;\n\n        info!(\"Multiple u64 writes (async)\");\n        async_block_write(\n            p.DMA_CH0.reborrow(),\n            &[\n                0xA55A_1111_3333_5555_u64,\n                0x1111_A55A_3333_5555,\n                0x5555_A55A_3333_1111,\n                0x01234_5678_89AB_CDEF,\n            ],\n        )\n        .await;\n    }\n\n    // TODO: Mixed byte and word transfers.\n\n    info!(\"Test OK\");\n    cortex_m::asm::bkpt();\n}\n\nfn single_read<W: Word + Copy + Default + Eq + defmt::Format>(mut channel: Peri<'_, impl Channel>, mut src: W) {\n    let options = TransferOptions::default();\n    let mut dst = W::default();\n\n    // SAFETY: src and dst outlive the transfer.\n    let transfer = unsafe {\n        unwrap!(Transfer::new_read(\n            channel.reborrow(),\n            Transfer::SOFTWARE_TRIGGER,\n            &mut src,\n            slice::from_mut(&mut dst),\n            options,\n        ))\n    };\n    transfer.blocking_wait();\n\n    assert_eq!(src, dst);\n}\n\nasync fn async_single_read<W: Word + Copy + Default + Eq + defmt::Format>(\n    mut channel: Peri<'_, impl Channel>,\n    mut src: W,\n) {\n    let options = TransferOptions::default();\n    let mut dst = W::default();\n\n    // SAFETY: src and dst outlive the transfer.\n    let transfer = unsafe {\n        unwrap!(Transfer::new_read(\n            channel.reborrow(),\n            Transfer::SOFTWARE_TRIGGER,\n            &mut src,\n            slice::from_mut(&mut dst),\n            options,\n        ))\n    };\n    transfer.await;\n\n    assert_eq!(src, dst);\n}\n\nfn block_read<W: Word + Copy + Default + Eq + defmt::Format, const N: usize>(\n    mut channel: Peri<'_, impl Channel>,\n    mut src: W,\n) {\n    let mut options = TransferOptions::default();\n    // Complete the entire transfer.\n    options.mode = TransferMode::Block;\n\n    let mut dst = [W::default(); N];\n\n    // SAFETY: src and dst outlive the transfer.\n    let transfer = unsafe {\n        unwrap!(Transfer::new_read(\n            channel.reborrow(),\n            Transfer::SOFTWARE_TRIGGER,\n            &mut src,\n            &mut dst[..],\n            options,\n        ))\n    };\n    transfer.blocking_wait();\n\n    assert_eq!(dst, [src; N]);\n}\n\nasync fn async_block_read<W: Word + Copy + Default + Eq + defmt::Format, const N: usize>(\n    mut channel: Peri<'_, impl Channel>,\n    mut src: W,\n) {\n    let mut options = TransferOptions::default();\n    // Complete the entire transfer.\n    options.mode = TransferMode::Block;\n\n    let mut dst = [W::default(); N];\n\n    // SAFETY: src and dst outlive the transfer.\n    let transfer = unsafe {\n        unwrap!(Transfer::new_read(\n            channel.reborrow(),\n            Transfer::SOFTWARE_TRIGGER,\n            &mut src,\n            &mut dst[..],\n            options,\n        ))\n    };\n    transfer.await;\n\n    assert_eq!(dst, [src; N]);\n}\n\nfn single_write<W: Word + Default + Eq + defmt::Format>(mut channel: Peri<'_, impl Channel>, src: W) {\n    let options = TransferOptions::default();\n    let mut dst = W::default();\n\n    // SAFETY: src and dst outlive the transfer.\n    let transfer = unsafe {\n        unwrap!(Transfer::new_write(\n            channel.reborrow(),\n            Transfer::SOFTWARE_TRIGGER,\n            slice::from_ref(&src),\n            &mut dst,\n            options,\n        ))\n    };\n    transfer.blocking_wait();\n\n    assert_eq!(src, dst);\n}\n\nasync fn async_single_write<W: Word + Default + Eq + defmt::Format>(mut channel: Peri<'_, impl Channel>, src: W) {\n    let options = TransferOptions::default();\n    let mut dst = W::default();\n\n    // SAFETY: src and dst outlive the transfer.\n    let transfer = unsafe {\n        unwrap!(Transfer::new_write(\n            channel.reborrow(),\n            Transfer::SOFTWARE_TRIGGER,\n            slice::from_ref(&src),\n            &mut dst,\n            options,\n        ))\n    };\n    transfer.await;\n\n    assert_eq!(src, dst);\n}\n\nfn block_write<W: Word + Default + Eq + defmt::Format>(mut channel: Peri<'_, impl Channel>, src: &[W]) {\n    let mut options = TransferOptions::default();\n    // Complete the entire transfer.\n    options.mode = TransferMode::Block;\n\n    let mut dst = W::default();\n\n    // Starting from 1 because a zero length transfer does nothing.\n    for i in 1..src.len() {\n        info!(\"-> {} write(s)\", i);\n\n        // SAFETY: src and dst outlive the transfer.\n        let transfer = unsafe {\n            unwrap!(Transfer::new_write(\n                channel.reborrow(),\n                Transfer::SOFTWARE_TRIGGER,\n                &src[..i],\n                &mut dst,\n                options,\n            ))\n        };\n        transfer.blocking_wait();\n\n        // The result will be the last value written.\n        assert_eq!(dst, src[i - 1]);\n    }\n}\n\nasync fn async_block_write<W: Word + Default + Eq + defmt::Format>(mut channel: Peri<'_, impl Channel>, src: &[W]) {\n    let mut options = TransferOptions::default();\n    // Complete the entire transfer.\n    options.mode = TransferMode::Block;\n\n    let mut dst = W::default();\n\n    // Starting from 1 because a zero length transfer does nothing.\n    for i in 1..src.len() {\n        info!(\"-> {} write(s)\", i);\n        // SAFETY: src and dst outlive the transfer.\n        let transfer = unsafe {\n            unwrap!(Transfer::new_write(\n                channel.reborrow(),\n                Transfer::SOFTWARE_TRIGGER,\n                &src[..i],\n                &mut dst,\n                options,\n            ))\n        };\n        transfer.await;\n\n        // The result will be the last value written.\n        assert_eq!(dst, src[i - 1]);\n    }\n}\n\n/// [`single_read`], but testing when the destination is wider than the source.\n///\n/// The MSPM0 DMA states that the upper bytes when the destination is longer than the source are zeroed.\n/// This matches the behavior in Rust for all unsigned integer types.\nfn widening_single_read<SW, DW>(mut channel: Peri<'_, impl Channel>, mut src: SW)\nwhere\n    SW: Word + Copy + Default + Eq + defmt::Format,\n    DW: Word + Copy + Default + Eq + defmt::Format + From<SW>,\n{\n    assert!(\n        DW::size() > SW::size(),\n        \"This test only works when the destination is larger than the source\"\n    );\n\n    let options = TransferOptions::default();\n    let mut dst = DW::default();\n\n    // SAFETY: src and dst outlive the transfer.\n    let transfer = unsafe {\n        unwrap!(Transfer::new_read(\n            channel.reborrow(),\n            Transfer::SOFTWARE_TRIGGER,\n            &mut src,\n            slice::from_mut(&mut dst),\n            options,\n        ))\n    };\n    transfer.blocking_wait();\n\n    assert_eq!(DW::from(src), dst);\n}\n\n/// [`single_read`], but testing when the destination is narrower than the source.\n///\n/// The MSPM0 DMA states that the upper bytes when the source is longer than the destination are dropped.\n/// This matches the behavior in Rust for all unsigned integer types.\nfn narrowing_single_read<SW, DW>(mut channel: Peri<'_, impl Channel>, mut src: SW)\nwhere\n    SW: Word + Copy + Default + Eq + defmt::Format + From<DW>,\n    DW: Word + Copy + Default + Eq + defmt::Format + Narrow<SW>,\n{\n    assert!(\n        SW::size() > DW::size(),\n        \"This test only works when the source is larger than the destination\"\n    );\n\n    let options = TransferOptions::default();\n    let mut dst = DW::default();\n\n    // SAFETY: src and dst outlive the transfer.\n    let transfer = unsafe {\n        unwrap!(Transfer::new_read(\n            channel.reborrow(),\n            Transfer::SOFTWARE_TRIGGER,\n            &mut src,\n            slice::from_mut(&mut dst),\n            options,\n        ))\n    };\n    transfer.blocking_wait();\n\n    // The expected value is the source value masked by the maximum destination value.\n    // This is effectively `src as DW as SW` to drop the upper byte(s).\n    let expect = SW::from(DW::narrow(src));\n    assert_eq!(expect, dst.into());\n}\n\n/// A pseudo `as` trait to allow downcasting integer types (TryFrom could fail).\ntrait Narrow<T> {\n    fn narrow(value: T) -> Self;\n}\n\nimpl Narrow<u16> for u8 {\n    fn narrow(value: u16) -> Self {\n        value as u8\n    }\n}\n\nimpl Narrow<u32> for u8 {\n    fn narrow(value: u32) -> Self {\n        value as u8\n    }\n}\n\nimpl Narrow<u64> for u8 {\n    fn narrow(value: u64) -> Self {\n        value as u8\n    }\n}\n\nimpl Narrow<u32> for u16 {\n    fn narrow(value: u32) -> Self {\n        value as u16\n    }\n}\n\nimpl Narrow<u64> for u16 {\n    fn narrow(value: u64) -> Self {\n        value as u16\n    }\n}\n\nimpl Narrow<u64> for u32 {\n    fn narrow(value: u64) -> Self {\n        value as u32\n    }\n}\n"
  },
  {
    "path": "tests/mspm0/src/bin/uart.rs",
    "content": "#![no_std]\n#![no_main]\n\n#[cfg(feature = \"mspm0g3507\")]\nteleprobe_meta::target!(b\"lp-mspm0g3507\");\n\n#[cfg(feature = \"mspm0g3519\")]\nteleprobe_meta::target!(b\"lp-mspm0g3519\");\n\nuse defmt::{assert_eq, unwrap, *};\nuse embassy_executor::Spawner;\nuse embassy_mspm0::mode::Blocking;\nuse embassy_mspm0::uart::{ClockSel, Config, Error, Uart};\nuse {defmt_rtt as _, panic_probe as _};\n\nfn read<const N: usize>(uart: &mut Uart<'_, Blocking>) -> Result<[u8; N], Error> {\n    let mut buf = [255; N];\n    uart.blocking_read(&mut buf)?;\n    Ok(buf)\n}\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let p = embassy_mspm0::init(Default::default());\n    info!(\"Hello World!\");\n\n    // TODO: Allow creating a looped-back UART (so pins are not needed).\n    // Do not select default UART since the virtual COM port is attached to UART0.\n    #[cfg(any(feature = \"mspm0g3507\", feature = \"mspm0g3519\"))]\n    let (mut tx, mut rx, mut uart) = (p.PA8, p.PA9, p.UART1);\n\n    const MFCLK_BUAD_RATES: &[u32] = &[1200, 2400, 4800, 9600, 19200, 38400, 57600, 115200];\n\n    for &rate in MFCLK_BUAD_RATES {\n        info!(\"{} baud using MFCLK\", rate);\n\n        let mut config = Config::default();\n        // MSPM0 hardware supports a loopback mode to allow self test.\n        config.loop_back_enable = true;\n        config.baudrate = rate;\n\n        let mut uart = unwrap!(Uart::new_blocking(\n            uart.reborrow(),\n            rx.reborrow(),\n            tx.reborrow(),\n            config\n        ));\n\n        // We can't send too many bytes, they have to fit in the FIFO.\n        // This is because we aren't sending+receiving at the same time.\n\n        let data = [0xC0, 0xDE];\n        unwrap!(uart.blocking_write(&data));\n        assert_eq!(unwrap!(read(&mut uart)), data);\n    }\n\n    // 9600 is the maximum possible value for 32.768 kHz.\n    const LFCLK_BAUD_RATES: &[u32] = &[1200, 2400, 4800, 9600];\n\n    for &rate in LFCLK_BAUD_RATES {\n        info!(\"{} baud using LFCLK\", rate);\n\n        let mut config = Config::default();\n        // MSPM0 hardware supports a loopback mode to allow self test.\n        config.loop_back_enable = true;\n        config.baudrate = rate;\n        config.clock_source = ClockSel::LfClk;\n\n        let mut uart = expect!(Uart::new_blocking(\n            uart.reborrow(),\n            rx.reborrow(),\n            tx.reborrow(),\n            config,\n        ));\n\n        // We can't send too many bytes, they have to fit in the FIFO.\n        // This is because we aren't sending+receiving at the same time.\n\n        let data = [0xC0, 0xDE];\n        unwrap!(uart.blocking_write(&data));\n        assert_eq!(unwrap!(read(&mut uart)), data);\n    }\n\n    info!(\"Test OK\");\n    cortex_m::asm::bkpt();\n}\n"
  },
  {
    "path": "tests/mspm0/src/bin/uart_buffered.rs",
    "content": "#![no_std]\n#![no_main]\n\n#[cfg(feature = \"mspm0g3507\")]\nteleprobe_meta::target!(b\"lp-mspm0g3507\");\n\nuse defmt::{assert_eq, unwrap, *};\nuse embassy_executor::Spawner;\nuse embassy_mspm0::uart::{BufferedInterruptHandler, BufferedUart, Config};\nuse embassy_mspm0::{bind_interrupts, peripherals};\nuse {defmt_rtt as _, panic_probe as _};\n\nbind_interrupts!(struct Irqs {\n    UART1 => BufferedInterruptHandler<peripherals::UART1>;\n});\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let p = embassy_mspm0::init(Default::default());\n    info!(\"Hello World!\");\n\n    // TODO: Allow creating a looped-back UART (so pins are not needed).\n    // Do not select default UART since the virtual COM port is attached to UART0.\n    #[cfg(any(feature = \"mspm0g3507\"))]\n    let (mut tx, mut rx, mut uart) = (p.PA8, p.PA9, p.UART1);\n\n    {\n        use embedded_io_async::{Read, Write};\n\n        let mut config = Config::default();\n        config.loop_back_enable = true;\n        config.fifo_enable = false;\n\n        let tx_buf = &mut [0u8; 16];\n        let rx_buf = &mut [0u8; 16];\n        let mut uart = unwrap!(BufferedUart::new(\n            uart.reborrow(),\n            tx.reborrow(),\n            rx.reborrow(),\n            Irqs,\n            tx_buf,\n            rx_buf,\n            config\n        ));\n\n        let mut buf = [0; 16];\n        for (j, b) in buf.iter_mut().enumerate() {\n            *b = j as u8;\n        }\n\n        unwrap!(uart.write_all(&buf).await);\n        unwrap!(uart.flush().await);\n\n        unwrap!(uart.read_exact(&mut buf).await);\n        for (j, b) in buf.iter().enumerate() {\n            assert_eq!(*b, j as u8);\n        }\n\n        // Buffer is unclogged, should be able to write again.\n        unwrap!(uart.write_all(&buf).await);\n        unwrap!(uart.flush().await);\n\n        unwrap!(uart.read_exact(&mut buf).await);\n        for (j, b) in buf.iter().enumerate() {\n            assert_eq!(*b, j as u8);\n        }\n    }\n\n    info!(\"Blocking buffered\");\n    {\n        use embedded_io::{Read, Write};\n\n        let mut config = Config::default();\n        config.loop_back_enable = true;\n        config.fifo_enable = false;\n\n        let tx_buf = &mut [0u8; 16];\n        let rx_buf = &mut [0u8; 16];\n        let mut uart = unwrap!(BufferedUart::new(\n            uart.reborrow(),\n            tx.reborrow(),\n            rx.reborrow(),\n            Irqs,\n            tx_buf,\n            rx_buf,\n            config\n        ));\n\n        let mut buf = [0; 16];\n\n        for (j, b) in buf.iter_mut().enumerate() {\n            *b = j as u8;\n        }\n\n        unwrap!(uart.write_all(&buf));\n        unwrap!(uart.blocking_flush());\n        unwrap!(uart.read_exact(&mut buf));\n\n        for (j, b) in buf.iter().enumerate() {\n            assert_eq!(*b, j as u8);\n        }\n\n        // Buffer is unclogged, should be able to write again.\n        unwrap!(uart.write_all(&buf));\n        unwrap!(uart.blocking_flush());\n        unwrap!(uart.read_exact(&mut buf));\n\n        for (j, b) in buf.iter().enumerate() {\n            assert_eq!(*b, j as u8, \"at {}\", j);\n        }\n    }\n\n    info!(\"Test OK\");\n    cortex_m::asm::bkpt();\n}\n"
  },
  {
    "path": "tests/nrf/.cargo/config.toml",
    "content": "[target.'cfg(all(target_arch = \"arm\", target_os = \"none\"))']\nrunner = \"teleprobe local run --chip nRF52840_xxAA --elf\"\n#runner = \"teleprobe client run\"\n\n[build]\n#target = \"thumbv6m-none-eabi\"\ntarget = \"thumbv7em-none-eabi\"\n#target = \"thumbv8m.main-none-eabihf\"\n\n[env]\nDEFMT_LOG = \"trace,embassy_hal_internal=debug,embassy_net_esp_hosted=debug,smoltcp=info\"\n"
  },
  {
    "path": "tests/nrf/Cargo.toml",
    "content": "[package]\nedition = \"2024\"\nname = \"embassy-nrf-examples\"\nversion = \"0.1.0\"\nlicense = \"MIT OR Apache-2.0\"\npublish = false\n\n[dependencies]\nteleprobe-meta = \"1\"\n\nembassy-futures = { version = \"0.1.2\", path = \"../../embassy-futures\" }\nembassy-sync = { version = \"0.8.0\", path = \"../../embassy-sync\", features = [\"defmt\", ] }\nembassy-executor = { version = \"0.10.0\", path = \"../../embassy-executor\", features = [\"platform-cortex-m\", \"executor-thread\", \"defmt\"] }\nembassy-time = { version = \"0.5.1\", path = \"../../embassy-time\", features = [\"defmt\",  \"defmt-timestamp-uptime\"] }\nembassy-nrf = { version = \"0.10.0\", path = \"../../embassy-nrf\", features = [\"defmt\",  \"time-driver-rtc1\", \"gpiote\", \"unstable-pac\"] }\nembedded-io-async = { version = \"0.7.0\", features = [\"defmt\"] }\nembassy-net = { version = \"0.9.0\", path = \"../../embassy-net\", features = [\"defmt\", \"tcp\", \"dhcpv4\", \"medium-ethernet\", ] }\nembassy-net-esp-hosted = { version = \"0.3.0\", path = \"../../embassy-net-esp-hosted\", features = [\"defmt\"] }\nembassy-net-enc28j60 = { version = \"0.3.0\", path = \"../../embassy-net-enc28j60\", features = [\"defmt\"] }\nembedded-hal-async = { version = \"1.0\" }\nembedded-hal-bus = { version = \"0.1\", features = [\"async\"] }\nstatic_cell = \"2\"\nperf-client = { path = \"../perf-client\" }\n\ndefmt = \"1.0.1\"\ndefmt-rtt = \"1.0.0\"\n\ncortex-m = { version = \"0.7.6\", features = [\"critical-section-single-core\"] }\ncortex-m-rt = \"0.7.0\"\npanic-probe = { version = \"1.0.0\", features = [\"print-defmt\"] }\nportable-atomic = { version = \"1.6.0\" }\n\n[features]\nnrf51422 = [\"embassy-nrf/nrf51\",         \"portable-atomic/unsafe-assume-single-core\"]\nnrf52832 = [\"embassy-nrf/nrf52832\",      \"easydma\"]\nnrf52833 = [\"embassy-nrf/nrf52833\",      \"easydma\", \"two-uarts\"]\nnrf52840 = [\"embassy-nrf/nrf52840\",      \"easydma\", \"two-uarts\"]\nnrf5340  = [\"embassy-nrf/nrf5340-app-s\", \"easydma\", \"two-uarts\"]\nnrf9160  = [\"embassy-nrf/nrf9160-s\",     \"easydma\", \"two-uarts\"]\n\neasydma = []\ntwo-uarts = []\n\n[profile.release]\ncodegen-units = 1\ndebug = 2\ndebug-assertions = false\nincremental = false\nlto = \"fat\"\nopt-level = 's'\noverflow-checks = false\n\n# BEGIN TESTS\n# Generated by gen_test.py. DO NOT EDIT.\n[[bin]]\nname = \"buffered_uart\"\npath = \"src/bin/buffered_uart.rs\"\nrequired-features = [ \"easydma\",]\n\n[[bin]]\nname = \"buffered_uart_full\"\npath = \"src/bin/buffered_uart_full.rs\"\nrequired-features = [ \"easydma\",]\n\n[[bin]]\nname = \"buffered_uart_halves\"\npath = \"src/bin/buffered_uart_halves.rs\"\nrequired-features = [ \"two-uarts\",]\n\n[[bin]]\nname = \"buffered_uart_spam\"\npath = \"src/bin/buffered_uart_spam.rs\"\nrequired-features = [ \"two-uarts\",]\n\n[[bin]]\nname = \"ethernet_enc28j60_perf\"\npath = \"src/bin/ethernet_enc28j60_perf.rs\"\nrequired-features = [ \"nrf52840\",]\n\n[[bin]]\nname = \"gpio\"\npath = \"src/bin/gpio.rs\"\nrequired-features = []\n\n[[bin]]\nname = \"gpiote\"\npath = \"src/bin/gpiote.rs\"\nrequired-features = []\n\n[[bin]]\nname = \"spim\"\npath = \"src/bin/spim.rs\"\nrequired-features = [ \"easydma\",]\n\n[[bin]]\nname = \"timer\"\npath = \"src/bin/timer.rs\"\nrequired-features = []\n\n[[bin]]\nname = \"uart_halves\"\npath = \"src/bin/uart_halves.rs\"\nrequired-features = [ \"two-uarts\",]\n\n[[bin]]\nname = \"uart_split\"\npath = \"src/bin/uart_split.rs\"\nrequired-features = [ \"easydma\",]\n\n[[bin]]\nname = \"wifi_esp_hosted_perf\"\npath = \"src/bin/wifi_esp_hosted_perf.rs\"\nrequired-features = [ \"nrf52840\",]\n\n# END TESTS\n\n[package.metadata.embassy]\nbuild = [\n  { target = \"thumbv6m-none-eabi\", features = [\"nrf51422\"], artifact-dir = \"out/tests/nrf51422-dk\" },\n  { target = \"thumbv7em-none-eabi\", features = [\"nrf52832\"], artifact-dir = \"out/tests/nrf52832-dk\" },\n  { target = \"thumbv7em-none-eabi\", features = [\"nrf52833\"], artifact-dir = \"out/tests/nrf52833-dk\" },\n  { target = \"thumbv7em-none-eabi\", features = [\"nrf52840\"], artifact-dir = \"out/tests/nrf52840-dk\" },\n  { target = \"thumbv8m.main-none-eabihf\", features = [\"nrf5340\"], artifact-dir = \"out/tests/nrf5340-dk\" },\n  { target = \"thumbv8m.main-none-eabihf\", features = [\"nrf9160\"], artifact-dir = \"out/tests/nrf9160-dk\" }\n]\n"
  },
  {
    "path": "tests/nrf/build.rs",
    "content": "use std::error::Error;\nuse std::path::PathBuf;\nuse std::{env, fs};\n\nfn main() -> Result<(), Box<dyn Error>> {\n    let out = PathBuf::from(env::var(\"OUT_DIR\").unwrap());\n\n    // copy the right memory.x\n    #[cfg(feature = \"nrf51422\")]\n    let memory_x = include_bytes!(\"memory-nrf51422.x\");\n    #[cfg(feature = \"nrf52832\")]\n    let memory_x = include_bytes!(\"memory-nrf52832.x\");\n    #[cfg(feature = \"nrf52833\")]\n    let memory_x = include_bytes!(\"memory-nrf52833.x\");\n    #[cfg(feature = \"nrf52840\")]\n    let memory_x = include_bytes!(\"memory-nrf52840.x\");\n    #[cfg(feature = \"nrf5340\")]\n    let memory_x = include_bytes!(\"memory-nrf5340.x\");\n    #[cfg(feature = \"nrf9160\")]\n    let memory_x = include_bytes!(\"memory-nrf9160.x\");\n    fs::write(out.join(\"memory.x\"), memory_x).unwrap();\n\n    // copy main linker script.\n    fs::write(out.join(\"link_ram.x\"), include_bytes!(\"../link_ram_cortex_m.x\")).unwrap();\n    println!(\"cargo:rustc-link-search={}\", out.display());\n    println!(\"cargo:rerun-if-changed=../link_ram_cortex_m.x\");\n\n    println!(\"cargo:rustc-link-arg-bins=--nmagic\");\n    #[cfg(feature = \"nrf51422\")]\n    println!(\"cargo:rustc-link-arg-bins=-Tlink.x\");\n    #[cfg(not(feature = \"nrf51422\"))]\n    println!(\"cargo:rustc-link-arg-bins=-Tlink_ram.x\");\n    println!(\"cargo:rustc-link-arg-bins=-Tdefmt.x\");\n    println!(\"cargo:rustc-link-arg-bins=-Tteleprobe.x\");\n\n    Ok(())\n}\n"
  },
  {
    "path": "tests/nrf/gen_test.py",
    "content": "import os\nimport toml\nfrom glob import glob\n\nabspath = os.path.abspath(__file__)\ndname = os.path.dirname(abspath)\nos.chdir(dname)\n\n# ======= load test list\ntests = {}\nfor f in sorted(glob('./src/bin/*.rs')):\n    name = os.path.splitext(os.path.basename(f))[0]\n    features = []\n    with open(f, 'r') as f:\n        for line in f:\n            if line.startswith('// required-features:'):\n                features = [feature.strip() for feature in line.split(':', 2)[1].strip().split(',')]\n\n    tests[name] = features\n\n# ========= Update Cargo.toml\n\nthings = {\n    'bin': [\n        {\n            'name': f'{name}',\n            'path': f'src/bin/{name}.rs',\n            'required-features': features,\n        }\n        for name, features in tests.items()\n    ]\n}\n\nSEPARATOR_START = '# BEGIN TESTS\\n'\nSEPARATOR_END = '# END TESTS\\n'\nHELP = '# Generated by gen_test.py. DO NOT EDIT.\\n'\nwith open('Cargo.toml', 'r') as f:\n    data = f.read()\nbefore, data = data.split(SEPARATOR_START, maxsplit=1)\n_, after = data.split(SEPARATOR_END, maxsplit=1)\ndata = before + SEPARATOR_START + HELP + \\\n    toml.dumps(things) + SEPARATOR_END + after\nwith open('Cargo.toml', 'w') as f:\n    f.write(data)\n"
  },
  {
    "path": "tests/nrf/memory-nrf51422.x",
    "content": "MEMORY\n{\n  FLASH : ORIGIN = 0x00000000, LENGTH = 256K\n  RAM : ORIGIN = 0x20000000, LENGTH = 32K\n}\n"
  },
  {
    "path": "tests/nrf/memory-nrf52832.x",
    "content": "MEMORY\n{\n  FLASH : ORIGIN = 0x00000000, LENGTH = 256K\n  RAM : ORIGIN = 0x20000000, LENGTH = 32K\n}\n"
  },
  {
    "path": "tests/nrf/memory-nrf52833.x",
    "content": "MEMORY\n{\n  FLASH : ORIGIN = 0x00000000, LENGTH = 512K\n  RAM : ORIGIN = 0x20000000, LENGTH = 64K\n}\n"
  },
  {
    "path": "tests/nrf/memory-nrf52840.x",
    "content": "MEMORY\n{\n  FLASH : ORIGIN = 0x00000000, LENGTH = 1024K\n  RAM : ORIGIN = 0x20000000, LENGTH = 256K\n}\n"
  },
  {
    "path": "tests/nrf/memory-nrf5340.x",
    "content": "MEMORY\n{\n  FLASH : ORIGIN = 0x00000000, LENGTH = 1024K\n  RAM : ORIGIN = 0x20000000, LENGTH = 256K\n}\n"
  },
  {
    "path": "tests/nrf/memory-nrf9160.x",
    "content": "MEMORY\n{\n  FLASH : ORIGIN = 0x00000000, LENGTH = 1024K\n  RAM : ORIGIN = 0x20000000, LENGTH = 256K\n}\n"
  },
  {
    "path": "tests/nrf/src/bin/buffered_uart.rs",
    "content": "// required-features: easydma\n#![no_std]\n#![no_main]\n\n#[path = \"../common.rs\"]\nmod common;\n\nuse defmt::{panic, *};\nuse embassy_executor::Spawner;\nuse embassy_futures::join::join;\nuse embassy_nrf::buffered_uarte::{self, BufferedUarte};\nuse embassy_nrf::{peripherals, uarte};\nuse {defmt_rtt as _, panic_probe as _};\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let mut p = embassy_nrf::init(Default::default());\n    let mut config = uarte::Config::default();\n    config.parity = uarte::Parity::EXCLUDED;\n    config.baudrate = uarte::Baudrate::BAUD1M;\n\n    let mut tx_buffer = [0u8; 1024];\n    let mut rx_buffer = [0u8; 1024];\n\n    // test teardown + recreate of the buffereduarte works fine.\n    for _ in 0..2 {\n        let u = BufferedUarte::new(\n            peri!(p, UART0).reborrow(),\n            p.TIMER0.reborrow(),\n            p.PPI_CH0.reborrow(),\n            p.PPI_CH1.reborrow(),\n            p.PPI_GROUP0.reborrow(),\n            peri!(p, PIN_A).reborrow(),\n            peri!(p, PIN_B).reborrow(),\n            irqs!(UART0_BUFFERED),\n            config.clone(),\n            &mut rx_buffer,\n            &mut tx_buffer,\n        );\n\n        info!(\"uarte initialized!\");\n\n        let (mut rx, mut tx) = u.split();\n\n        const COUNT: usize = 40_000;\n\n        let tx_fut = async {\n            let mut tx_buf = [0; 215];\n            let mut i = 0;\n            while i < COUNT {\n                let n = tx_buf.len().min(COUNT - i);\n                let tx_buf = &mut tx_buf[..n];\n                for (j, b) in tx_buf.iter_mut().enumerate() {\n                    *b = (i + j) as u8;\n                }\n                let n = unwrap!(tx.write(tx_buf).await);\n                i += n;\n            }\n        };\n        let rx_fut = async {\n            let mut i = 0;\n            while i < COUNT {\n                let buf = unwrap!(rx.fill_buf().await);\n\n                for &b in buf {\n                    if b != i as u8 {\n                        panic!(\"mismatch {} vs {}, index {}\", b, i as u8, i);\n                    }\n                    i = i + 1;\n                }\n\n                let n = buf.len();\n                rx.consume(n);\n            }\n        };\n\n        join(rx_fut, tx_fut).await;\n    }\n\n    info!(\"Test OK\");\n    cortex_m::asm::bkpt();\n}\n"
  },
  {
    "path": "tests/nrf/src/bin/buffered_uart_full.rs",
    "content": "// required-features: easydma\n#![no_std]\n#![no_main]\n\n#[path = \"../common.rs\"]\nmod common;\n\nuse defmt::{assert_eq, *};\nuse embassy_executor::Spawner;\nuse embassy_nrf::buffered_uarte::{self, BufferedUarte};\nuse embassy_nrf::{peripherals, uarte};\nuse embedded_io_async::{Read, Write};\nuse {defmt_rtt as _, panic_probe as _};\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let p = embassy_nrf::init(Default::default());\n    let mut config = uarte::Config::default();\n    config.parity = uarte::Parity::EXCLUDED;\n    config.baudrate = uarte::Baudrate::BAUD1M;\n\n    let mut tx_buffer = [0u8; 500];\n    let mut rx_buffer = [0u8; 500];\n\n    let u = BufferedUarte::new(\n        peri!(p, UART0),\n        p.TIMER0,\n        p.PPI_CH0,\n        p.PPI_CH1,\n        p.PPI_GROUP0,\n        peri!(p, PIN_A),\n        peri!(p, PIN_B),\n        irqs!(UART0_BUFFERED),\n        config.clone(),\n        &mut rx_buffer,\n        &mut tx_buffer,\n    );\n\n    info!(\"uarte initialized!\");\n\n    let (mut rx, mut tx) = u.split();\n\n    let mut buf = [0; 500];\n    for (j, b) in buf.iter_mut().enumerate() {\n        *b = j as u8;\n    }\n\n    // Write 500b. This causes the rx buffer to get exactly full.\n    unwrap!(tx.write_all(&buf).await);\n    unwrap!(tx.flush().await);\n\n    // Read those 500b.\n    unwrap!(rx.read_exact(&mut buf).await);\n    for (j, b) in buf.iter().enumerate() {\n        assert_eq!(*b, j as u8);\n    }\n\n    // The buffer should now be unclogged. Write 500b again.\n    unwrap!(tx.write_all(&buf).await);\n    unwrap!(tx.flush().await);\n\n    // Read should work again.\n    unwrap!(rx.read_exact(&mut buf).await);\n    for (j, b) in buf.iter().enumerate() {\n        assert_eq!(*b, j as u8);\n    }\n\n    info!(\"Test OK\");\n    cortex_m::asm::bkpt();\n}\n"
  },
  {
    "path": "tests/nrf/src/bin/buffered_uart_halves.rs",
    "content": "// required-features: two-uarts\n#![no_std]\n#![no_main]\n\n#[path = \"../common.rs\"]\nmod common;\n\nuse defmt::{assert_eq, *};\nuse embassy_executor::Spawner;\nuse embassy_futures::join::join;\nuse embassy_nrf::buffered_uarte::{self, BufferedUarteRx, BufferedUarteTx};\nuse embassy_nrf::{peripherals, uarte};\nuse {defmt_rtt as _, panic_probe as _};\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let mut p = embassy_nrf::init(Default::default());\n    let mut config = uarte::Config::default();\n    config.parity = uarte::Parity::EXCLUDED;\n    config.baudrate = uarte::Baudrate::BAUD1M;\n\n    let mut tx_buffer = [0u8; 1024];\n    let mut rx_buffer = [0u8; 1024];\n\n    // test teardown + recreate of the buffereduarte works fine.\n    for _ in 0..2 {\n        const COUNT: usize = 40_000;\n\n        let mut tx = BufferedUarteTx::new(\n            peri!(p, UART1).reborrow(),\n            peri!(p, PIN_A).reborrow(),\n            irqs!(UART1_BUFFERED),\n            config.clone(),\n            &mut tx_buffer,\n        );\n\n        let mut rx = BufferedUarteRx::new(\n            peri!(p, UART0).reborrow(),\n            p.TIMER0.reborrow(),\n            p.PPI_CH0.reborrow(),\n            p.PPI_CH1.reborrow(),\n            p.PPI_GROUP0.reborrow(),\n            irqs!(UART0_BUFFERED),\n            peri!(p, PIN_B).reborrow(),\n            config.clone(),\n            &mut rx_buffer,\n        );\n\n        let tx_fut = async {\n            info!(\"tx initialized!\");\n\n            let mut tx_buf = [0; 215];\n            let mut i = 0;\n            while i < COUNT {\n                let n = tx_buf.len().min(COUNT - i);\n                let tx_buf = &mut tx_buf[..n];\n                for (j, b) in tx_buf.iter_mut().enumerate() {\n                    *b = (i + j) as u8;\n                }\n                let n = unwrap!(tx.write(tx_buf).await);\n                i += n;\n            }\n        };\n        let rx_fut = async {\n            info!(\"rx initialized!\");\n\n            let mut i = 0;\n            while i < COUNT {\n                let buf = unwrap!(rx.fill_buf().await);\n\n                for &b in buf {\n                    assert_eq!(b, i as u8);\n                    i = i + 1;\n                }\n\n                let n = buf.len();\n                rx.consume(n);\n            }\n        };\n\n        join(rx_fut, tx_fut).await;\n    }\n\n    info!(\"Test OK\");\n    cortex_m::asm::bkpt();\n}\n"
  },
  {
    "path": "tests/nrf/src/bin/buffered_uart_spam.rs",
    "content": "// required-features: two-uarts\n#![no_std]\n#![no_main]\n\n#[path = \"../common.rs\"]\nmod common;\n\nuse core::mem;\nuse core::ptr::NonNull;\n\nuse defmt::{assert_eq, *};\nuse embassy_executor::Spawner;\nuse embassy_nrf::buffered_uarte::{self, BufferedUarteRx};\nuse embassy_nrf::gpio::{Level, Output, OutputDrive};\nuse embassy_nrf::ppi::{Event, Ppi, Task};\nuse embassy_nrf::uarte::UarteTx;\nuse embassy_nrf::{pac, peripherals, uarte};\nuse embassy_time::Timer;\nuse {defmt_rtt as _, panic_probe as _};\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let mut p = embassy_nrf::init(Default::default());\n    let mut config = uarte::Config::default();\n    config.parity = uarte::Parity::EXCLUDED;\n    config.baudrate = uarte::Baudrate::BAUD1M;\n\n    let mut rx_buffer = [0u8; 1024];\n\n    mem::forget(Output::new(\n        peri!(p, PIN_A).reborrow(),\n        Level::High,\n        OutputDrive::Standard,\n    ));\n\n    let mut u = BufferedUarteRx::new(\n        peri!(p, UART0),\n        p.TIMER0,\n        p.PPI_CH0,\n        p.PPI_CH1,\n        p.PPI_GROUP0,\n        irqs!(UART0_BUFFERED),\n        peri!(p, PIN_B),\n        config.clone(),\n        &mut rx_buffer,\n    );\n\n    info!(\"uarte initialized!\");\n\n    // uarte needs some quiet time to start rxing properly.\n    Timer::after_millis(10).await;\n\n    // Tx spam in a loop.\n    const NSPAM: usize = 17;\n    static mut TX_BUF: [u8; NSPAM] = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16];\n    let _spam = UarteTx::new(peri!(p, UART1), irqs!(UART1), peri!(p, PIN_A), config.clone());\n    let spam_peri = pac::UARTE1;\n    let event = unsafe { Event::new_unchecked(NonNull::new_unchecked(spam_peri.events_dma().tx().end().as_ptr())) };\n    let task = unsafe { Task::new_unchecked(NonNull::new_unchecked(spam_peri.tasks_dma().tx().start().as_ptr())) };\n    let mut spam_ppi = Ppi::new_one_to_one(p.PPI_CH2, event, task);\n    spam_ppi.enable();\n    let p = (&raw mut TX_BUF) as *mut u8;\n    spam_peri.dma().tx().ptr().write_value(p as u32);\n    spam_peri.dma().tx().maxcnt().write(|w| w.set_maxcnt(NSPAM as _));\n    spam_peri.tasks_dma().tx().start().write_value(1);\n\n    let mut i = 0;\n    let mut total = 0;\n    while total < 256 * 1024 {\n        let buf = unwrap!(u.fill_buf().await);\n        //info!(\"rx {}\", buf);\n\n        for &b in buf {\n            assert_eq!(b, unsafe { TX_BUF[i] });\n\n            i = i + 1;\n            if i == NSPAM {\n                i = 0;\n            }\n        }\n\n        // Read bytes have to be explicitly consumed, otherwise fill_buf() will return them again\n        let n = buf.len();\n        u.consume(n);\n        total += n;\n    }\n\n    info!(\"Test OK\");\n    cortex_m::asm::bkpt();\n}\n"
  },
  {
    "path": "tests/nrf/src/bin/ethernet_enc28j60_perf.rs",
    "content": "// required-features: nrf52840\n#![no_std]\n#![no_main]\nteleprobe_meta::target!(b\"ak-gwe-r7\");\nteleprobe_meta::timeout!(120);\n\nuse defmt::{info, unwrap};\nuse embassy_executor::Spawner;\nuse embassy_net::StackResources;\nuse embassy_net_enc28j60::Enc28j60;\nuse embassy_nrf::gpio::{Level, Output, OutputDrive};\nuse embassy_nrf::rng::Rng;\nuse embassy_nrf::spim::{self, Spim};\nuse embassy_nrf::{bind_interrupts, peripherals};\nuse embassy_time::Delay;\nuse embedded_hal_bus::spi::ExclusiveDevice;\nuse static_cell::StaticCell;\nuse {defmt_rtt as _, panic_probe as _};\n\nbind_interrupts!(struct Irqs {\n    SPIM3 => spim::InterruptHandler<peripherals::SPI3>;\n    RNG => embassy_nrf::rng::InterruptHandler<peripherals::RNG>;\n});\n\ntype MyDriver = Enc28j60<ExclusiveDevice<Spim<'static>, Output<'static>, Delay>, Output<'static>>;\n\n#[embassy_executor::task]\nasync fn net_task(mut runner: embassy_net::Runner<'static, MyDriver>) -> ! {\n    runner.run().await\n}\n\n#[embassy_executor::main]\nasync fn main(spawner: Spawner) {\n    let p = embassy_nrf::init(Default::default());\n    info!(\"running!\");\n\n    let eth_sck = p.P0_20;\n    let eth_mosi = p.P0_22;\n    let eth_miso = p.P0_24;\n    let eth_cs = p.P0_15;\n    let eth_rst = p.P0_13;\n    let _eth_irq = p.P0_12;\n\n    let mut config = spim::Config::default();\n    config.frequency = spim::Frequency::M16;\n    let spi = spim::Spim::new(p.SPI3, Irqs, eth_sck, eth_miso, eth_mosi, config);\n    let cs = Output::new(eth_cs, Level::High, OutputDrive::Standard);\n    let spi = ExclusiveDevice::new(spi, cs, Delay);\n\n    let rst = Output::new(eth_rst, Level::High, OutputDrive::Standard);\n    let mac_addr = [2, 3, 4, 5, 6, 7];\n    let device = Enc28j60::new(spi, Some(rst), mac_addr);\n\n    let config = embassy_net::Config::dhcpv4(Default::default());\n    // let config = embassy_net::Config::ipv4_static(embassy_net::StaticConfigV4 {\n    //    address: Ipv4Cidr::new(Ipv4Address::new(10, 42, 0, 61), 24),\n    //    dns_servers: Vec::new(),\n    //    gateway: Some(Ipv4Address::new(10, 42, 0, 1)),\n    // });\n\n    // Generate random seed\n    let mut rng = Rng::new(p.RNG, Irqs);\n    let mut seed = [0; 8];\n    rng.blocking_fill_bytes(&mut seed);\n    let seed = u64::from_le_bytes(seed);\n\n    // Init network stack\n    static RESOURCES: StaticCell<StackResources<2>> = StaticCell::new();\n    let (stack, runner) = embassy_net::new(device, config, RESOURCES.init(StackResources::new()), seed);\n\n    spawner.spawn(unwrap!(net_task(runner)));\n\n    perf_client::run(\n        stack,\n        perf_client::Expected {\n            down_kbps: 200,\n            up_kbps: 200,\n            updown_kbps: 150,\n        },\n    )\n    .await;\n\n    info!(\"Test OK\");\n    cortex_m::asm::bkpt();\n}\n"
  },
  {
    "path": "tests/nrf/src/bin/gpio.rs",
    "content": "#![no_std]\n#![no_main]\n\n#[path = \"../common.rs\"]\nmod common;\n\nuse defmt::{assert, info};\nuse embassy_executor::Spawner;\nuse embassy_nrf::gpio::{Input, Level, Output, OutputDrive, Pull};\nuse embassy_time::Timer;\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let p = embassy_nrf::init(Default::default());\n\n    let input = Input::new(peri!(p, PIN_A), Pull::Up);\n    let mut output = Output::new(peri!(p, PIN_B), Level::Low, OutputDrive::Standard);\n\n    output.set_low();\n    assert!(output.is_set_low());\n    Timer::after_millis(10).await;\n    assert!(input.is_low());\n\n    output.set_high();\n    assert!(output.is_set_high());\n    Timer::after_millis(10).await;\n    assert!(input.is_high());\n\n    info!(\"Test OK\");\n    cortex_m::asm::bkpt();\n}\n"
  },
  {
    "path": "tests/nrf/src/bin/gpiote.rs",
    "content": "#![no_std]\n#![no_main]\n\n#[path = \"../common.rs\"]\nmod common;\n\nuse defmt::{assert, info};\nuse embassy_executor::Spawner;\nuse embassy_futures::join::join;\nuse embassy_nrf::gpio::{Input, Level, Output, OutputDrive, Pull};\nuse embassy_time::{Duration, Instant, Timer};\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let p = embassy_nrf::init(Default::default());\n\n    let mut input = Input::new(peri!(p, PIN_A), Pull::Up);\n    let mut output = Output::new(peri!(p, PIN_B), Level::Low, OutputDrive::Standard);\n\n    let fut1 = async {\n        Timer::after_millis(100).await;\n        output.set_high();\n    };\n    let fut2 = async {\n        let start = Instant::now();\n        input.wait_for_high().await;\n        let dur = Instant::now() - start;\n        info!(\"took {} ms\", dur.as_millis());\n        assert!((Duration::from_millis(90)..Duration::from_millis(110)).contains(&dur));\n    };\n\n    join(fut1, fut2).await;\n\n    let fut1 = async {\n        Timer::after_millis(100).await;\n        output.set_low();\n    };\n    let fut2 = async {\n        let start = Instant::now();\n        input.wait_for_low().await;\n        let dur = Instant::now() - start;\n        info!(\"took {} ms\", dur.as_millis());\n        assert!((Duration::from_millis(90)..Duration::from_millis(110)).contains(&dur));\n    };\n\n    join(fut1, fut2).await;\n\n    info!(\"Test OK\");\n    cortex_m::asm::bkpt();\n}\n"
  },
  {
    "path": "tests/nrf/src/bin/spim.rs",
    "content": "// required-features: easydma\n#![no_std]\n#![no_main]\n\n#[path = \"../common.rs\"]\nmod common;\n\nuse defmt::{assert_eq, *};\nuse embassy_executor::Spawner;\nuse embassy_nrf::spim::Spim;\nuse embassy_nrf::{peripherals, spim};\nuse {defmt_rtt as _, panic_probe as _};\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let mut p = embassy_nrf::init(Default::default());\n    let mut config = spim::Config::default();\n    config.frequency = spim::Frequency::M1;\n    let mut spim = Spim::new(\n        peri!(p, SPIM0).reborrow(),\n        irqs!(SPIM0),\n        peri!(p, PIN_X).reborrow(),\n        peri!(p, PIN_A).reborrow(), // MISO\n        peri!(p, PIN_B).reborrow(), // MOSI\n        config.clone(),\n    );\n    let data = [\n        0x42, 0x43, 0x44, 0x45, 0x66, 0x12, 0x23, 0x34, 0x45, 0x19, 0x91, 0xaa, 0xff, 0xa5, 0x5a, 0x77,\n    ];\n    let mut buf = [0u8; 16];\n\n    buf.fill(0);\n    spim.blocking_transfer(&mut buf, &data).unwrap();\n    assert_eq!(data, buf);\n\n    buf.fill(0);\n    spim.transfer(&mut buf, &data).await.unwrap();\n    assert_eq!(data, buf);\n\n    info!(\"Test OK\");\n    cortex_m::asm::bkpt();\n}\n"
  },
  {
    "path": "tests/nrf/src/bin/timer.rs",
    "content": "#![no_std]\n#![no_main]\n\n#[path = \"../common.rs\"]\nmod common;\n\nuse defmt::{assert, info};\nuse embassy_executor::Spawner;\nuse embassy_time::{Instant, Timer};\nuse {defmt_rtt as _, panic_probe as _};\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let _p = embassy_nrf::init(Default::default());\n    info!(\"Hello World!\");\n\n    let start = Instant::now();\n    Timer::after_millis(100).await;\n    let end = Instant::now();\n    let ms = (end - start).as_millis();\n    info!(\"slept for {} ms\", ms);\n    assert!(ms >= 99);\n\n    info!(\"Test OK\");\n    cortex_m::asm::bkpt();\n}\n"
  },
  {
    "path": "tests/nrf/src/bin/uart_halves.rs",
    "content": "// required-features: two-uarts\n#![no_std]\n#![no_main]\n\n#[path = \"../common.rs\"]\nmod common;\n\nuse defmt::{assert_eq, *};\nuse embassy_executor::Spawner;\nuse embassy_futures::join::join;\nuse embassy_nrf::uarte::{UarteRx, UarteTx};\nuse embassy_nrf::{peripherals, uarte};\nuse {defmt_rtt as _, panic_probe as _};\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let mut p = embassy_nrf::init(Default::default());\n    let mut config = uarte::Config::default();\n    config.parity = uarte::Parity::EXCLUDED;\n    config.baudrate = uarte::Baudrate::BAUD1M;\n\n    let mut tx = UarteTx::new(\n        peri!(p, UART0).reborrow(),\n        irqs!(UART0),\n        peri!(p, PIN_A).reborrow(),\n        config.clone(),\n    );\n    let mut rx = UarteRx::new(\n        peri!(p, UART1).reborrow(),\n        irqs!(UART1),\n        peri!(p, PIN_B).reborrow(),\n        config.clone(),\n    );\n\n    let data = [\n        0x42, 0x43, 0x44, 0x45, 0x66, 0x12, 0x23, 0x34, 0x45, 0x19, 0x91, 0xaa, 0xff, 0xa5, 0x5a, 0x77,\n    ];\n\n    let tx_fut = async {\n        tx.write(&data).await.unwrap();\n    };\n    let rx_fut = async {\n        let mut buf = [0u8; 16];\n        rx.read(&mut buf).await.unwrap();\n        assert_eq!(data, buf);\n    };\n    join(rx_fut, tx_fut).await;\n\n    info!(\"Test OK\");\n    cortex_m::asm::bkpt();\n}\n"
  },
  {
    "path": "tests/nrf/src/bin/uart_split.rs",
    "content": "// required-features: easydma\n#![no_std]\n#![no_main]\n\n#[path = \"../common.rs\"]\nmod common;\n\nuse defmt::{assert_eq, *};\nuse embassy_executor::Spawner;\nuse embassy_futures::join::join;\nuse embassy_nrf::uarte::Uarte;\nuse embassy_nrf::{peripherals, uarte};\nuse embassy_time::Timer;\nuse {defmt_rtt as _, panic_probe as _};\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let mut p = embassy_nrf::init(Default::default());\n    let mut config = uarte::Config::default();\n    config.parity = uarte::Parity::EXCLUDED;\n    config.baudrate = uarte::Baudrate::BAUD9600;\n\n    let uarte = Uarte::new(\n        peri!(p, UART0).reborrow(),\n        peri!(p, PIN_A).reborrow(),\n        peri!(p, PIN_B).reborrow(),\n        irqs!(UART0),\n        config.clone(),\n    );\n    let (mut tx, mut rx) = uarte.split();\n\n    let data = [\n        0x42, 0x43, 0x44, 0x45, 0x66, 0x12, 0x23, 0x34, 0x45, 0x19, 0x91, 0xaa, 0xff, 0xa5, 0x5a, 0x77,\n    ];\n\n    let tx_fut = async {\n        Timer::after_millis(10).await;\n        tx.write(&data).await.unwrap();\n    };\n    let rx_fut = async {\n        let mut buf = [0u8; 16];\n        rx.read(&mut buf).await.unwrap();\n        assert_eq!(data, buf);\n    };\n    join(rx_fut, tx_fut).await;\n\n    info!(\"Test OK\");\n    cortex_m::asm::bkpt();\n}\n"
  },
  {
    "path": "tests/nrf/src/bin/wifi_esp_hosted_perf.rs",
    "content": "// required-features: nrf52840\n#![no_std]\n#![no_main]\nteleprobe_meta::target!(b\"nrf52840-dk\");\nteleprobe_meta::timeout!(120);\n\nuse defmt::{info, unwrap};\nuse embassy_executor::Spawner;\nuse embassy_net::{Config, StackResources};\nuse embassy_nrf::gpio::{Input, Level, Output, OutputDrive, Pull};\nuse embassy_nrf::rng::Rng;\nuse embassy_nrf::spim::{self, Spim};\nuse embassy_nrf::{bind_interrupts, peripherals};\nuse embassy_time::Delay;\nuse embedded_hal_bus::spi::ExclusiveDevice;\nuse static_cell::StaticCell;\nuse {defmt_rtt as _, embassy_net_esp_hosted as hosted, panic_probe as _};\n\nbind_interrupts!(struct Irqs {\n    SPIM3 => spim::InterruptHandler<peripherals::SPI3>;\n    RNG => embassy_nrf::rng::InterruptHandler<peripherals::RNG>;\n});\n\n// Test-only wifi network, no internet access!\nconst WIFI_NETWORK: &str = \"EmbassyTest\";\nconst WIFI_PASSWORD: &str = \"V8YxhKt5CdIAJFud\";\n\n#[embassy_executor::task]\nasync fn wifi_task(\n    runner: hosted::Runner<\n        'static,\n        hosted::SpiInterface<ExclusiveDevice<Spim<'static>, Output<'static>, Delay>, Input<'static>>,\n        Output<'static>,\n    >,\n) -> ! {\n    runner.run().await\n}\n\ntype MyDriver = hosted::NetDriver<'static>;\n\n#[embassy_executor::task]\nasync fn net_task(mut runner: embassy_net::Runner<'static, MyDriver>) -> ! {\n    runner.run().await\n}\n\n#[embassy_executor::main]\nasync fn main(spawner: Spawner) {\n    info!(\"Hello World!\");\n\n    let p = embassy_nrf::init(Default::default());\n\n    let miso = p.P0_28;\n    let sck = p.P0_29;\n    let mosi = p.P0_30;\n    let cs = Output::new(p.P0_31, Level::High, OutputDrive::HighDrive);\n    let handshake = Input::new(p.P1_01, Pull::Up);\n    let ready = Input::new(p.P1_04, Pull::None);\n    let reset = Output::new(p.P1_05, Level::Low, OutputDrive::Standard);\n\n    let mut config = spim::Config::default();\n    config.frequency = spim::Frequency::M32;\n    config.mode = spim::MODE_2; // !!!\n    let spi = spim::Spim::new(p.SPI3, Irqs, sck, miso, mosi, config);\n    let spi = ExclusiveDevice::new(spi, cs, Delay);\n\n    let iface = hosted::SpiInterface::new(spi, handshake, ready);\n\n    static STATE: StaticCell<embassy_net_esp_hosted::State> = StaticCell::new();\n    let (device, mut control, runner) =\n        embassy_net_esp_hosted::new(STATE.init(embassy_net_esp_hosted::State::new()), iface, reset).await;\n\n    spawner.spawn(unwrap!(wifi_task(runner)));\n\n    unwrap!(control.init().await);\n    unwrap!(control.connect(WIFI_NETWORK, WIFI_PASSWORD).await);\n\n    // Generate random seed\n    let mut rng = Rng::new(p.RNG, Irqs);\n    let mut seed = [0; 8];\n    rng.blocking_fill_bytes(&mut seed);\n    let seed = u64::from_le_bytes(seed);\n\n    // Init network stack\n    static RESOURCES: StaticCell<StackResources<2>> = StaticCell::new();\n    let (stack, runner) = embassy_net::new(\n        device,\n        Config::dhcpv4(Default::default()),\n        RESOURCES.init(StackResources::new()),\n        seed,\n    );\n\n    spawner.spawn(unwrap!(net_task(runner)));\n\n    perf_client::run(\n        stack,\n        perf_client::Expected {\n            down_kbps: 50,\n            up_kbps: 50,\n            updown_kbps: 50,\n        },\n    )\n    .await;\n\n    info!(\"Test OK\");\n    cortex_m::asm::bkpt();\n}\n"
  },
  {
    "path": "tests/nrf/src/common.rs",
    "content": "#![macro_use]\n\nuse {defmt_rtt as _, panic_probe as _};\n\n#[cfg(feature = \"nrf52832\")]\nteleprobe_meta::target!(b\"nrf52832-dk\");\n#[cfg(feature = \"nrf52840\")]\nteleprobe_meta::target!(b\"nrf52840-dk\");\n#[cfg(feature = \"nrf52833\")]\nteleprobe_meta::target!(b\"nrf52833-dk\");\n#[cfg(feature = \"nrf5340\")]\nteleprobe_meta::target!(b\"nrf5340-dk\");\n#[cfg(feature = \"nrf9160\")]\nteleprobe_meta::target!(b\"nrf9160-dk\");\n#[cfg(feature = \"nrf51422\")]\nteleprobe_meta::target!(b\"nrf51-dk\");\n\nmacro_rules! define_peris {\n    ($($name:ident = $peri:ident,)* $(@irq $irq_name:ident = $irq_code:tt,)*) => {\n        #[allow(unused_macros)]\n        macro_rules! peri {\n            $(\n                ($p:expr, $name) => {\n                    $p.$peri\n                };\n            )*\n        }\n        #[allow(unused_macros)]\n        macro_rules! irqs {\n            $(\n                ($irq_name) => {{\n                    embassy_nrf::bind_interrupts!(struct Irqs $irq_code);\n                    Irqs\n                }};\n            )*\n            ( @ dummy ) => {};\n        }\n\n        #[allow(unused)]\n        #[allow(non_camel_case_types)]\n        pub mod peris {\n            $(\n                pub type $name = embassy_nrf::peripherals::$peri;\n            )*\n        }\n    };\n}\n\n#[cfg(feature = \"nrf51422\")]\ndefine_peris!(PIN_A = P0_13, PIN_B = P0_14,);\n\n#[cfg(feature = \"nrf52832\")]\ndefine_peris!(\n    PIN_A = P0_11, PIN_B = P0_12,\n    PIN_X = P0_13,\n    UART0 = UARTE0,\n    SPIM0 = TWISPI0,\n    @irq UART0 = {UARTE0 => uarte::InterruptHandler<peripherals::UARTE0>;},\n    @irq UART0_BUFFERED = {UARTE0 => buffered_uarte::InterruptHandler<peripherals::UARTE0>;},\n    @irq SPIM0 = {TWISPI0 => spim::InterruptHandler<peripherals::TWISPI0>;},\n);\n\n#[cfg(feature = \"nrf52833\")]\ndefine_peris!(\n    PIN_A = P1_01, PIN_B = P1_02,\n    PIN_X = P1_03,\n    UART0 = UARTE0,\n    UART1 = UARTE1,\n    SPIM0 = TWISPI0,\n    @irq UART0 = {UARTE0 => uarte::InterruptHandler<peripherals::UARTE0>;},\n    @irq UART1 = {UARTE1 => uarte::InterruptHandler<peripherals::UARTE1>;},\n    @irq UART0_BUFFERED = {UARTE0 => buffered_uarte::InterruptHandler<peripherals::UARTE0>;},\n    @irq UART1_BUFFERED = {UARTE1 => buffered_uarte::InterruptHandler<peripherals::UARTE1>;},\n    @irq SPIM0 = {TWISPI0 => spim::InterruptHandler<peripherals::TWISPI0>;},\n);\n\n#[cfg(feature = \"nrf52840\")]\ndefine_peris!(\n    PIN_A = P1_02, PIN_B = P1_03,\n    PIN_X = P1_04,\n    UART0 = UARTE0,\n    UART1 = UARTE1,\n    SPIM0 = TWISPI0,\n    @irq UART0 = {UARTE0 => uarte::InterruptHandler<peripherals::UARTE0>;},\n    @irq UART1 = {UARTE1 => uarte::InterruptHandler<peripherals::UARTE1>;},\n    @irq UART0_BUFFERED = {UARTE0 => buffered_uarte::InterruptHandler<peripherals::UARTE0>;},\n    @irq UART1_BUFFERED = {UARTE1 => buffered_uarte::InterruptHandler<peripherals::UARTE1>;},\n    @irq SPIM0 = {TWISPI0 => spim::InterruptHandler<peripherals::TWISPI0>;},\n);\n\n#[cfg(feature = \"nrf5340\")]\ndefine_peris!(\n    PIN_A = P1_08, PIN_B = P1_09,\n    PIN_X = P1_10,\n    UART0 = SERIAL0,\n    UART1 = SERIAL1,\n    SPIM0 = SERIAL0,\n    @irq UART0 = {SERIAL0 => uarte::InterruptHandler<peripherals::SERIAL0>;},\n    @irq UART1 = {SERIAL1 => uarte::InterruptHandler<peripherals::SERIAL1>;},\n    @irq UART0_BUFFERED = {SERIAL0 => buffered_uarte::InterruptHandler<peripherals::SERIAL0>;},\n    @irq UART1_BUFFERED = {SERIAL1 => buffered_uarte::InterruptHandler<peripherals::SERIAL1>;},\n    @irq SPIM0 = {SERIAL0 => spim::InterruptHandler<peripherals::SERIAL0>;},\n);\n\n#[cfg(feature = \"nrf9160\")]\ndefine_peris!(\n    PIN_A = P0_00, PIN_B = P0_01,\n    PIN_X = P0_02,\n    UART0 = SERIAL0,\n    UART1 = SERIAL1,\n    SPIM0 = SERIAL0,\n    @irq UART0 = {SERIAL0 => uarte::InterruptHandler<peripherals::SERIAL0>;},\n    @irq UART1 = {SERIAL1 => uarte::InterruptHandler<peripherals::SERIAL1>;},\n    @irq UART0_BUFFERED = {SERIAL0 => buffered_uarte::InterruptHandler<peripherals::SERIAL0>;},\n    @irq UART1_BUFFERED = {SERIAL1 => buffered_uarte::InterruptHandler<peripherals::SERIAL1>;},\n    @irq SPIM0 = {SERIAL0 => spim::InterruptHandler<peripherals::SERIAL0>;},\n);\n"
  },
  {
    "path": "tests/perf-client/Cargo.toml",
    "content": "[package]\nname = \"perf-client\"\nversion = \"0.1.0\"\nedition = \"2024\"\nlicense = \"MIT OR Apache-2.0\"\npublish = false\n\n[dependencies]\nembassy-net = { version = \"0.9.0\", path = \"../../embassy-net\", features = [\"defmt\", \"tcp\", \"dhcpv4\"] }\nembassy-time = { version = \"0.5.1\", path = \"../../embassy-time\", features = [\"defmt\", ] }\nembassy-futures = { version = \"0.1.2\", path = \"../../embassy-futures\" }\ndefmt = \"1.0.1\"\n"
  },
  {
    "path": "tests/perf-client/src/lib.rs",
    "content": "#![no_std]\n\nuse defmt::{assert, *};\nuse embassy_futures::join::join;\nuse embassy_net::tcp::TcpSocket;\nuse embassy_net::{Ipv4Address, Stack};\nuse embassy_time::{Duration, Timer, with_timeout};\n\npub struct Expected {\n    pub down_kbps: usize,\n    pub up_kbps: usize,\n    pub updown_kbps: usize,\n}\n\npub async fn run(stack: Stack<'_>, expected: Expected) {\n    info!(\"Waiting for DHCP up...\");\n    while stack.config_v4().is_none() {\n        Timer::after_millis(100).await;\n    }\n    info!(\"IP addressing up!\");\n\n    let down = test_download(stack).await;\n    let up = test_upload(stack).await;\n    let updown = test_upload_download(stack).await;\n\n    assert!(down > expected.down_kbps);\n    assert!(up > expected.up_kbps);\n    assert!(updown > expected.updown_kbps);\n}\n\nconst TEST_DURATION: usize = 10;\nconst IO_BUFFER_SIZE: usize = 1024;\nconst RX_BUFFER_SIZE: usize = 4096;\nconst TX_BUFFER_SIZE: usize = 4096;\nconst SERVER_ADDRESS: Ipv4Address = Ipv4Address::new(192, 168, 2, 2);\nconst DOWNLOAD_PORT: u16 = 4321;\nconst UPLOAD_PORT: u16 = 4322;\nconst UPLOAD_DOWNLOAD_PORT: u16 = 4323;\n\nasync fn test_download(stack: Stack<'_>) -> usize {\n    info!(\"Testing download...\");\n\n    let mut rx_buffer = [0; RX_BUFFER_SIZE];\n    let mut tx_buffer = [0; TX_BUFFER_SIZE];\n    let mut socket = TcpSocket::new(stack, &mut rx_buffer, &mut tx_buffer);\n    socket.set_timeout(Some(Duration::from_secs(10)));\n\n    info!(\"connecting to {:?}:{}...\", SERVER_ADDRESS, DOWNLOAD_PORT);\n    if let Err(e) = socket.connect((SERVER_ADDRESS, DOWNLOAD_PORT)).await {\n        error!(\"connect error: {:?}\", e);\n        return 0;\n    }\n    info!(\"connected, testing...\");\n\n    let mut rx_buf = [0; IO_BUFFER_SIZE];\n    let mut total: usize = 0;\n    with_timeout(Duration::from_secs(TEST_DURATION as _), async {\n        loop {\n            match socket.read(&mut rx_buf).await {\n                Ok(0) => {\n                    error!(\"read EOF\");\n                    return 0;\n                }\n                Ok(n) => total += n,\n                Err(e) => {\n                    error!(\"read error: {:?}\", e);\n                    return 0;\n                }\n            }\n        }\n    })\n    .await\n    .ok();\n\n    let kbps = (total + 512) / 1024 / TEST_DURATION;\n    info!(\"download: {} kB/s\", kbps);\n    kbps\n}\n\nasync fn test_upload(stack: Stack<'_>) -> usize {\n    info!(\"Testing upload...\");\n\n    let mut rx_buffer = [0; RX_BUFFER_SIZE];\n    let mut tx_buffer = [0; TX_BUFFER_SIZE];\n    let mut socket = TcpSocket::new(stack, &mut rx_buffer, &mut tx_buffer);\n    socket.set_timeout(Some(Duration::from_secs(10)));\n\n    info!(\"connecting to {:?}:{}...\", SERVER_ADDRESS, UPLOAD_PORT);\n    if let Err(e) = socket.connect((SERVER_ADDRESS, UPLOAD_PORT)).await {\n        error!(\"connect error: {:?}\", e);\n        return 0;\n    }\n    info!(\"connected, testing...\");\n\n    let buf = [0; IO_BUFFER_SIZE];\n    let mut total: usize = 0;\n    with_timeout(Duration::from_secs(TEST_DURATION as _), async {\n        loop {\n            match socket.write(&buf).await {\n                Ok(0) => {\n                    error!(\"write zero?!??!?!\");\n                    return 0;\n                }\n                Ok(n) => total += n,\n                Err(e) => {\n                    error!(\"write error: {:?}\", e);\n                    return 0;\n                }\n            }\n        }\n    })\n    .await\n    .ok();\n\n    let kbps = (total + 512) / 1024 / TEST_DURATION;\n    info!(\"upload: {} kB/s\", kbps);\n    kbps\n}\n\nasync fn test_upload_download(stack: Stack<'_>) -> usize {\n    info!(\"Testing upload+download...\");\n\n    let mut rx_buffer = [0; RX_BUFFER_SIZE];\n    let mut tx_buffer = [0; TX_BUFFER_SIZE];\n    let mut socket = TcpSocket::new(stack, &mut rx_buffer, &mut tx_buffer);\n    socket.set_timeout(Some(Duration::from_secs(10)));\n\n    info!(\"connecting to {:?}:{}...\", SERVER_ADDRESS, UPLOAD_DOWNLOAD_PORT);\n    if let Err(e) = socket.connect((SERVER_ADDRESS, UPLOAD_DOWNLOAD_PORT)).await {\n        error!(\"connect error: {:?}\", e);\n        return 0;\n    }\n    info!(\"connected, testing...\");\n\n    let (mut reader, mut writer) = socket.split();\n\n    let tx_buf = [0; IO_BUFFER_SIZE];\n    let mut rx_buf = [0; IO_BUFFER_SIZE];\n    let mut total: usize = 0;\n    let tx_fut = async {\n        loop {\n            match writer.write(&tx_buf).await {\n                Ok(0) => {\n                    error!(\"write zero?!??!?!\");\n                    return 0;\n                }\n                Ok(_) => {}\n                Err(e) => {\n                    error!(\"write error: {:?}\", e);\n                    return 0;\n                }\n            }\n        }\n    };\n\n    let rx_fut = async {\n        loop {\n            match reader.read(&mut rx_buf).await {\n                Ok(0) => {\n                    error!(\"read EOF\");\n                    return 0;\n                }\n                Ok(n) => total += n,\n                Err(e) => {\n                    error!(\"read error: {:?}\", e);\n                    return 0;\n                }\n            }\n        }\n    };\n\n    with_timeout(Duration::from_secs(TEST_DURATION as _), join(tx_fut, rx_fut))\n        .await\n        .ok();\n\n    let kbps = (total + 512) / 1024 / TEST_DURATION;\n    info!(\"upload+download: {} kB/s\", kbps);\n    kbps\n}\n"
  },
  {
    "path": "tests/perf-server/Cargo.toml",
    "content": "[package]\nname = \"perf-server\"\nversion = \"0.1.0\"\nedition = \"2024\"\nlicense = \"MIT OR Apache-2.0\"\npublish = false\n\n[dependencies]\nlog = \"0.4.17\"\npretty_env_logger = \"0.4.0\"\n"
  },
  {
    "path": "tests/perf-server/deploy.sh",
    "content": "#!/bin/bash\n\nset -euxo pipefail\n\nHOST=root@192.168.1.3\n\ncargo build --release\nssh $HOST -- systemctl stop perf-server\nscp target/release/perf-server $HOST:/root\nscp perf-server.service $HOST:/etc/systemd/system/\nssh $HOST -- 'systemctl daemon-reload; systemctl restart perf-server'"
  },
  {
    "path": "tests/perf-server/perf-server.service",
    "content": "[Unit]\nDescription=perf-server\nAfter=network.target\nStartLimitIntervalSec=0\n\n[Service]\nType=simple\nRestart=always\nRestartSec=1\nUser=root\nExecStart=/root/perf-server\nEnvironment=RUST_BACKTRACE=1\nEnvironment=RUST_LOG=info\n\n[Install]\nWantedBy=multi-user.target\n"
  },
  {
    "path": "tests/perf-server/src/main.rs",
    "content": "use std::io::{Read, Write};\nuse std::net::{TcpListener, TcpStream};\nuse std::thread::spawn;\nuse std::time::Duration;\n\nuse log::info;\n\nfn main() {\n    pretty_env_logger::init();\n    spawn(|| rx_listen());\n    spawn(|| rxtx_listen());\n    tx_listen();\n}\n\nfn tx_listen() {\n    info!(\"tx: listening on 0.0.0.0:4321\");\n    let listener = TcpListener::bind(\"0.0.0.0:4321\").unwrap();\n    loop {\n        let (socket, addr) = listener.accept().unwrap();\n        info!(\"tx: received connection from: {}\", addr);\n        spawn(|| tx_conn(socket));\n    }\n}\n\nfn tx_conn(mut socket: TcpStream) {\n    socket.set_read_timeout(Some(Duration::from_secs(30))).unwrap();\n    socket.set_write_timeout(Some(Duration::from_secs(30))).unwrap();\n\n    let buf = [0; 1024];\n    loop {\n        if let Err(e) = socket.write_all(&buf) {\n            info!(\"tx: failed to write to socket; err = {:?}\", e);\n            return;\n        }\n    }\n}\n\nfn rx_listen() {\n    info!(\"rx: listening on 0.0.0.0:4322\");\n    let listener = TcpListener::bind(\"0.0.0.0:4322\").unwrap();\n    loop {\n        let (socket, addr) = listener.accept().unwrap();\n        info!(\"rx: received connection from: {}\", addr);\n        spawn(|| rx_conn(socket));\n    }\n}\n\nfn rx_conn(mut socket: TcpStream) {\n    socket.set_read_timeout(Some(Duration::from_secs(30))).unwrap();\n    socket.set_write_timeout(Some(Duration::from_secs(30))).unwrap();\n\n    let mut buf = [0; 1024];\n    loop {\n        if let Err(e) = socket.read_exact(&mut buf) {\n            info!(\"rx: failed to read from socket; err = {:?}\", e);\n            return;\n        }\n    }\n}\n\nfn rxtx_listen() {\n    info!(\"rxtx: listening on 0.0.0.0:4323\");\n    let listener = TcpListener::bind(\"0.0.0.0:4323\").unwrap();\n    loop {\n        let (socket, addr) = listener.accept().unwrap();\n        info!(\"rxtx: received connection from: {}\", addr);\n        spawn(|| rxtx_conn(socket));\n    }\n}\n\nfn rxtx_conn(mut socket: TcpStream) {\n    socket.set_read_timeout(Some(Duration::from_secs(30))).unwrap();\n    socket.set_write_timeout(Some(Duration::from_secs(30))).unwrap();\n\n    let mut buf = [0; 1024];\n    loop {\n        match socket.read(&mut buf) {\n            Ok(n) => {\n                if let Err(e) = socket.write_all(&buf[..n]) {\n                    info!(\"rxtx: failed to write to socket; err = {:?}\", e);\n                    return;\n                }\n            }\n            Err(e) => {\n                info!(\"rxtx: failed to read from socket; err = {:?}\", e);\n                return;\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "tests/riscv32/.cargo/config.toml",
    "content": "[target.riscv32imac-unknown-none-elf]\nrunner = \"true\"\n\n[build]\ntarget = \"riscv32imac-unknown-none-elf\"\n"
  },
  {
    "path": "tests/riscv32/Cargo.toml",
    "content": "[package]\nedition = \"2024\"\nname = \"embassy-riscv-tests\"\nversion = \"0.1.0\"\nlicense = \"MIT OR Apache-2.0\"\npublish = false\n\n[dependencies]\ncritical-section = { version = \"1.1.1\", features = [\"restore-state-bool\"] }\nembassy-sync = { version = \"0.8.0\", path = \"../../embassy-sync\" }\nembassy-executor = { version = \"0.10.0\", path = \"../../embassy-executor\", features = [\"platform-riscv32\", \"executor-thread\"] }\nembassy-time = { version = \"0.5.1\", path = \"../../embassy-time\" }\nembassy-futures = { version = \"0.1.2\", path = \"../../embassy-futures\" }\n\nriscv-rt = \"0.12.2\"\nriscv = { version = \"0.11.1\", features = [\"critical-section-single-hart\"] }\n\n\n[profile.dev]\ndebug = 2\ndebug-assertions = true\nopt-level = 's'\noverflow-checks = true\n\n[profile.release]\ncodegen-units = 1\ndebug = 2\ndebug-assertions = false\nincremental = false\nlto = \"fat\"\nopt-level = 's'\noverflow-checks = false\n\n# do not optimize proc-macro crates = faster builds from scratch\n[profile.dev.build-override]\ncodegen-units = 8\ndebug = false\ndebug-assertions = false\nopt-level = 0\noverflow-checks = false\n\n[profile.release.build-override]\ncodegen-units = 8\ndebug = false\ndebug-assertions = false\nopt-level = 0\noverflow-checks = false\n\n[package.metadata.embassy]\nbuild = [\n  { target = \"riscv32imac-unknown-none-elf\" }\n]\n"
  },
  {
    "path": "tests/riscv32/build.rs",
    "content": "use std::error::Error;\n\nfn main() -> Result<(), Box<dyn Error>> {\n    println!(\"cargo:rustc-link-arg-bins=-Tmemory.x\");\n    println!(\"cargo:rustc-link-arg-bins=-Tlink.x\");\n\n    Ok(())\n}\n"
  },
  {
    "path": "tests/riscv32/link.x",
    "content": "/* # EMBASSY notes\n  This file is a workaround for https://github.com/rust-embedded/riscv/issues/196\n  Remove when fixed upstream.\n*/\n/* # Developer notes\n\n- Symbols that start with a double underscore (__) are considered \"private\"\n\n- Symbols that start with a single underscore (_) are considered \"semi-public\"; they can be\n  overridden in a user linker script, but should not be referred from user code (e.g. `extern \"C\" {\n  static mut _heap_size }`).\n\n- `EXTERN` forces the linker to keep a symbol in the final binary. We use this to make sure a\n  symbol if not dropped if it appears in or near the front of the linker arguments and \"it's not\n  needed\" by any of the preceding objects (linker arguments)\n\n- `PROVIDE` is used to provide default values that can be overridden by a user linker script\n\n- In this linker script, you may find symbols that look like `${...}` (e.g., `4`).\n  These are wildcards used by the `build.rs` script to adapt to different target particularities.\n  Check `build.rs` for more details about these symbols.\n\n- On alignment: it's important for correctness that the VMA boundaries of both .bss and .data *and*\n  the LMA of .data are all `4`-byte aligned. These alignments are assumed by the RAM\n  initialization routine. There's also a second benefit: `4`-byte aligned boundaries\n  means that you won't see \"Address (..) is out of bounds\" in the disassembly produced by `objdump`.\n*/\n\nPROVIDE(_stext = ORIGIN(REGION_TEXT));\nPROVIDE(_stack_start = ORIGIN(REGION_STACK) + LENGTH(REGION_STACK));\nPROVIDE(_max_hart_id = 0);\nPROVIDE(_hart_stack_size = 2K);\nPROVIDE(_heap_size = 0);\n\nPROVIDE(InstructionMisaligned = ExceptionHandler);\nPROVIDE(InstructionFault = ExceptionHandler);\nPROVIDE(IllegalInstruction = ExceptionHandler);\nPROVIDE(Breakpoint = ExceptionHandler);\nPROVIDE(LoadMisaligned = ExceptionHandler);\nPROVIDE(LoadFault = ExceptionHandler);\nPROVIDE(StoreMisaligned = ExceptionHandler);\nPROVIDE(StoreFault = ExceptionHandler);;\nPROVIDE(UserEnvCall = ExceptionHandler);\nPROVIDE(SupervisorEnvCall = ExceptionHandler);\nPROVIDE(MachineEnvCall = ExceptionHandler);\nPROVIDE(InstructionPageFault = ExceptionHandler);\nPROVIDE(LoadPageFault = ExceptionHandler);\nPROVIDE(StorePageFault = ExceptionHandler);\n\nPROVIDE(SupervisorSoft = DefaultHandler);\nPROVIDE(MachineSoft = DefaultHandler);\nPROVIDE(SupervisorTimer = DefaultHandler);\nPROVIDE(MachineTimer = DefaultHandler);\nPROVIDE(SupervisorExternal = DefaultHandler);\nPROVIDE(MachineExternal = DefaultHandler);\n\nPROVIDE(DefaultHandler = DefaultInterruptHandler);\nPROVIDE(ExceptionHandler = DefaultExceptionHandler);\n\n/* # Pre-initialization function */\n/* If the user overrides this using the `#[pre_init]` attribute or by creating a `__pre_init` function,\n   then the function this points to will be called before the RAM is initialized. */\nPROVIDE(__pre_init = default_pre_init);\n\n/* A PAC/HAL defined routine that should initialize custom interrupt controller if needed. */\nPROVIDE(_setup_interrupts = default_setup_interrupts);\n\n/* # Multi-processing hook function\n   fn _mp_hook() -> bool;\n\n   This function is called from all the harts and must return true only for one hart,\n   which will perform memory initialization. For other harts it must return false\n   and implement wake-up in platform-dependent way (e.g. after waiting for a user interrupt).\n*/\nPROVIDE(_mp_hook = default_mp_hook);\n\n/* # Start trap function override\n  By default uses the riscv crates default trap handler\n  but by providing the `_start_trap` symbol external crates can override.\n*/\nPROVIDE(_start_trap = default_start_trap);\n\nSECTIONS\n{\n  .text.dummy (NOLOAD) :\n  {\n    /* This section is intended to make _stext address work */\n    . = ABSOLUTE(_stext);\n  } > REGION_TEXT\n\n  .text _stext :\n  {\n    /* Put reset handler first in .text section so it ends up as the entry */\n    /* point of the program. */\n    KEEP(*(.init));\n    KEEP(*(.init.rust));\n    . = ALIGN(4);\n    *(.trap);\n    *(.trap.rust);\n    *(.text.abort);\n    *(.text .text.*);\n  } > REGION_TEXT\n\n  .eh_frame : { KEEP(*(.eh_frame)) } > REGION_TEXT\n  .eh_frame_hdr : { *(.eh_frame_hdr) } > REGION_TEXT\n\n  .rodata : ALIGN(4)\n  {\n    *(.srodata .srodata.*);\n    *(.rodata .rodata.*);\n\n    /* 4-byte align the end (VMA) of this section.\n       This is required by LLD to ensure the LMA of the following .data\n       section will have the correct alignment. */\n    . = ALIGN(4);\n  } > REGION_RODATA\n\n  .data : ALIGN(4)\n  {\n    _sidata = LOADADDR(.data);\n    _sdata = .;\n    /* Must be called __global_pointer$ for linker relaxations to work. */\n    PROVIDE(__global_pointer$ = . + 0x800);\n    *(.sdata .sdata.* .sdata2 .sdata2.*);\n    *(.data .data.*);\n    . = ALIGN(4);\n    _edata = .;\n  } > REGION_DATA AT > REGION_RODATA\n\n  .bss (NOLOAD) : ALIGN(4)\n  {\n    _sbss = .;\n    *(.sbss .sbss.* .bss .bss.*);\n    . = ALIGN(4);\n    _ebss = .;\n  } > REGION_BSS\n\n  /* fictitious region that represents the memory available for the heap */\n  .heap (NOLOAD) :\n  {\n    _sheap = .;\n    . += _heap_size;\n    . = ALIGN(4);\n    _eheap = .;\n  } > REGION_HEAP\n\n  /* fictitious region that represents the memory available for the stack */\n  .stack (NOLOAD) :\n  {\n    _estack = .;\n    . = ABSOLUTE(_stack_start);\n    _sstack = .;\n  } > REGION_STACK\n\n  /* fake output .got section */\n  /* Dynamic relocations are unsupported. This section is only used to detect\n     relocatable code in the input files and raise an error if relocatable code\n     is found */\n  .got (INFO) :\n  {\n    KEEP(*(.got .got.*));\n  }\n}\n\n/* Do not exceed this mark in the error messages above                                    | */\nASSERT(ORIGIN(REGION_TEXT) % 4 == 0, \"\nERROR(riscv-rt): the start of the REGION_TEXT must be 4-byte aligned\");\n\nASSERT(ORIGIN(REGION_RODATA) % 4 == 0, \"\nERROR(riscv-rt): the start of the REGION_RODATA must be 4-byte aligned\");\n\nASSERT(ORIGIN(REGION_DATA) % 4 == 0, \"\nERROR(riscv-rt): the start of the REGION_DATA must be 4-byte aligned\");\n\nASSERT(ORIGIN(REGION_HEAP) % 4 == 0, \"\nERROR(riscv-rt): the start of the REGION_HEAP must be 4-byte aligned\");\n\nASSERT(ORIGIN(REGION_TEXT) % 4 == 0, \"\nERROR(riscv-rt): the start of the REGION_TEXT must be 4-byte aligned\");\n\nASSERT(ORIGIN(REGION_STACK) % 4 == 0, \"\nERROR(riscv-rt): the start of the REGION_STACK must be 4-byte aligned\");\n\nASSERT(_stext % 4 == 0, \"\nERROR(riscv-rt): `_stext` must be 4-byte aligned\");\n\nASSERT(_sdata % 4 == 0 && _edata % 4 == 0, \"\nBUG(riscv-rt): .data is not 4-byte aligned\");\n\nASSERT(_sidata % 4 == 0, \"\nBUG(riscv-rt): the LMA of .data is not 4-byte aligned\");\n\nASSERT(_sbss % 4 == 0 && _ebss % 4 == 0, \"\nBUG(riscv-rt): .bss is not 4-byte aligned\");\n\nASSERT(_sheap % 4 == 0, \"\nBUG(riscv-rt): start of .heap is not 4-byte aligned\");\n\nASSERT(_stext + SIZEOF(.text) < ORIGIN(REGION_TEXT) + LENGTH(REGION_TEXT), \"\nERROR(riscv-rt): The .text section must be placed inside the REGION_TEXT region.\nSet _stext to an address smaller than 'ORIGIN(REGION_TEXT) + LENGTH(REGION_TEXT)'\");\n\nASSERT(SIZEOF(.stack) > (_max_hart_id + 1) * _hart_stack_size, \"\nERROR(riscv-rt): .stack section is too small for allocating stacks for all the harts.\nConsider changing `_max_hart_id` or `_hart_stack_size`.\");\n\nASSERT(SIZEOF(.got) == 0, \"\n.got section detected in the input files. Dynamic relocations are not\nsupported. If you are linking to C code compiled using the `gcc` crate\nthen modify your build script to compile the C code _without_ the\n-fPIC flag. See the documentation of the `gcc::Config.fpic` method for\ndetails.\");\n\n/* Do not exceed this mark in the error messages above                                    | */\n"
  },
  {
    "path": "tests/riscv32/memory.x",
    "content": "MEMORY\n{\n  ROM : ORIGIN = 0x80000000, LENGTH = 0x00020000\n  RAM : ORIGIN = 0x84000000, LENGTH = 0x00008000\n}\n\nREGION_ALIAS(\"REGION_TEXT\", ROM);\nREGION_ALIAS(\"REGION_RODATA\", ROM);\nREGION_ALIAS(\"REGION_DATA\", RAM);\nREGION_ALIAS(\"REGION_BSS\", RAM);\nREGION_ALIAS(\"REGION_HEAP\", RAM);\nREGION_ALIAS(\"REGION_STACK\", RAM);\n\n_stack_start = ORIGIN(RAM) + LENGTH(RAM) - 4;\n"
  },
  {
    "path": "tests/riscv32/src/bin/empty.rs",
    "content": "#![no_std]\n#![no_main]\n\nuse embassy_executor::Spawner;\n\n#[panic_handler]\nfn panic(_info: &core::panic::PanicInfo) -> ! {\n    loop {}\n}\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    // Don't do anything, just make sure it compiles.\n    loop {}\n}\n"
  },
  {
    "path": "tests/rp/.cargo/config.toml",
    "content": "[unstable]\n# enabling these breaks the float tests during linking, with intrinsics\n# duplicated between embassy-rp and compilter_builtins\n#build-std = [\"core\"]\n#build-std-features = [\"panic_immediate_abort\"]\n\n[target.'cfg(all(target_arch = \"arm\", target_os = \"none\"))']\n#runner = \"teleprobe client run\"\n#runner = \"teleprobe local run --chip RP2040 --elf\"\nrunner = \"teleprobe local run --chip RP235X --elf\"\n\nrustflags = [\n  # Code-size optimizations.\n  #\"-Z\", \"trap-unreachable=no\",\n  \"-C\", \"no-vectorize-loops\",\n]\n\n[build]\n#target = \"thumbv6m-none-eabi\"\ntarget = \"thumbv8m.main-none-eabihf\"\n\n[env]\nDEFMT_LOG = \"trace,embassy_hal_internal=debug,embassy_net_esp_hosted=debug,cyw43=info,cyw43_pio=info,smoltcp=info\"\n"
  },
  {
    "path": "tests/rp/Cargo.toml",
    "content": "[package]\nedition = \"2024\"\nname = \"embassy-rp-tests\"\nversion = \"0.1.0\"\nlicense = \"MIT OR Apache-2.0\"\npublish = false\n\n[features]\nrp2040 = [\"embassy-rp/rp2040\"]\nrp235xa = [\"embassy-rp/rp235xa\"]\nrp235xb = [\"embassy-rp/rp235xb\"]\n\n[dependencies]\nteleprobe-meta = \"1.1\"\n\nembassy-sync = { version = \"0.8.0\", path = \"../../embassy-sync\", features = [\"defmt\"] }\nembassy-executor = { version = \"0.10.0\", path = \"../../embassy-executor\", features = [\"platform-cortex-m\", \"executor-thread\", \"defmt\"] }\nembassy-time = { version = \"0.5.1\", path = \"../../embassy-time\", features = [\"defmt\", ] }\nembassy-rp = { version = \"0.10.0\", path = \"../../embassy-rp\", features = [ \"defmt\", \"unstable-pac\", \"time-driver\", \"critical-section-impl\", \"intrinsics\", \"rom-v2-intrinsics\", \"run-from-ram\"]  }\nembassy-futures = { version = \"0.1.2\", path = \"../../embassy-futures\" }\nembassy-net = { version = \"0.9.0\", path = \"../../embassy-net\", features = [\"defmt\",  \"tcp\", \"udp\", \"dhcpv4\", \"medium-ethernet\"] }\nembassy-net-wiznet = { version = \"0.3.0\", path = \"../../embassy-net-wiznet\", features = [\"defmt\"] }\nembassy-embedded-hal = { version = \"0.6.0\", path = \"../../embassy-embedded-hal/\"}\ncyw43 = { path = \"../../cyw43\", features = [\"defmt\", \"firmware-logs\"] }\ncyw43-pio = { path = \"../../cyw43-pio\", features = [\"defmt\"] }\nperf-client = { path = \"../perf-client\" }\n\ndefmt = \"1.0.1\"\ndefmt-rtt = \"1.0.0\"\n\ncortex-m = { version = \"0.7.6\" }\ncortex-m-rt = \"0.7.0\"\nembedded-hal = \"0.2.6\"\nembedded-hal-1 = { package = \"embedded-hal\", version = \"1.0\" }\nembedded-hal-async = { version = \"1.0\" }\nembedded-hal-bus = { version = \"0.1\", features = [\"async\"] }\npanic-probe = { version = \"1.0.0\", features = [\"print-defmt\"] }\nembedded-io-async = { version = \"0.7.0\" }\nembedded-storage = { version = \"0.3\" }\nstatic_cell = \"2\"\nportable-atomic = { version = \"1.5\", features = [\"critical-section\"] }\n\n# bootsel not currently supported on 2350\n[[bin]]\nname = \"bootsel\"\npath = \"src/bin/bootsel.rs\"\nrequired-features = [ \"rp2040\",]\n\n# 2350 devboard isn't a W\n[[bin]]\nname = \"cyw43-perf\"\npath = \"src/bin/cyw43-perf.rs\"\nrequired-features = [ \"rp2040\",]\n\n# Eth test only for the w5100s-evb-pico\n[[bin]]\nname = \"ethernet_w5100s_perf\"\npath = \"src/bin/ethernet_w5100s_perf.rs\"\nrequired-features = [ \"rp2040\",]\n\n# Float intrinsics are only relevant for the 2040\n[[bin]]\nname = \"float\"\npath = \"src/bin/float.rs\"\nrequired-features = [ \"rp2040\",]\n\n# RTC is only available on RP2040\n[[bin]]\nname = \"rtc\"\npath = \"src/bin/rtc.rs\"\nrequired-features = [ \"rp2040\",]\n\n# AON Timer is only available on RP2350\n[[bin]]\nname = \"aon_timer\"\npath = \"src/bin/aon_timer.rs\"\nrequired-features = [ \"rp235xb\",]\n\n[profile.dev]\ndebug = 2\ndebug-assertions = true\nopt-level = 's'\noverflow-checks = true\n\n[profile.release]\ncodegen-units = 1\ndebug = 2\ndebug-assertions = false\nincremental = false\nlto = \"fat\"\nopt-level = 's'\noverflow-checks = false\n\n# do not optimize proc-macro crates = faster builds from scratch\n[profile.dev.build-override]\ncodegen-units = 8\ndebug = false\ndebug-assertions = false\nopt-level = 0\noverflow-checks = false\n\n[profile.release.build-override]\ncodegen-units = 8\ndebug = false\ndebug-assertions = false\nopt-level = 0\noverflow-checks = false\n\n[package.metadata.embassy]\nbuild = [\n  { target = \"thumbv6m-none-eabi\", features = [\"rp2040\"], artifact-dir = \"out/tests/rpi-pico\" },\n  { target = \"thumbv8m.main-none-eabihf\", features = [\"rp235xb\"], artifact-dir = \"out/tests/pimoroni-pico-plus-2\" }\n]\n"
  },
  {
    "path": "tests/rp/build.rs",
    "content": "use std::error::Error;\nuse std::path::PathBuf;\nuse std::{env, fs};\n\nfn main() -> Result<(), Box<dyn Error>> {\n    let out = PathBuf::from(env::var(\"OUT_DIR\").unwrap());\n    fs::write(out.join(\"link_ram.x\"), include_bytes!(\"../link_ram_cortex_m.x\")).unwrap();\n    println!(\"cargo:rustc-link-search={}\", out.display());\n    println!(\"cargo:rerun-if-changed=../link_ram_cortex_m.x\");\n\n    println!(\"cargo:rustc-link-arg-bins=--nmagic\");\n    println!(\"cargo:rustc-link-arg-bins=-Tlink_ram.x\");\n    println!(\"cargo:rustc-link-arg-bins=-Tdefmt.x\");\n    println!(\"cargo:rustc-link-arg-bins=-Tteleprobe.x\");\n\n    Ok(())\n}\n"
  },
  {
    "path": "tests/rp/memory.x",
    "content": "/* Provides information about the memory layout of the device */\nMEMORY {\n    RAM   : ORIGIN = 0x20000000, LENGTH = 256K\n}\n"
  },
  {
    "path": "tests/rp/readme.md",
    "content": "# Pico and Pico 2 Plus connections\n\nGP0-GP1\nGP3-GP4\nGP6-GP9\nGP7-GP11\nGP18-GP20 with 10k pullup\nGP19-GP21 with 10k pullup\n"
  },
  {
    "path": "tests/rp/src/bin/adc.rs",
    "content": "#![no_std]\n#![no_main]\n#[cfg(feature = \"rp2040\")]\nteleprobe_meta::target!(b\"rpi-pico\");\n#[cfg(feature = \"rp235xb\")]\nteleprobe_meta::target!(b\"pimoroni-pico-plus-2\");\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_rp::adc::{Adc, Channel, Config, InterruptHandler, Sample};\nuse embassy_rp::gpio::{Level, Output, Pull};\nuse embassy_rp::peripherals::DMA_CH0;\nuse embassy_rp::{bind_interrupts, dma};\nuse {defmt_rtt as _, panic_probe as _};\n\nbind_interrupts!(struct Irqs {\n    ADC_IRQ_FIFO => InterruptHandler;\n    DMA_IRQ_0 => dma::InterruptHandler<DMA_CH0>;\n});\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let mut p = embassy_rp::init(Default::default());\n    let _power_reg_pwm_mode = Output::new(p.PIN_23, Level::High);\n    let _wifi_off = Output::new(p.PIN_25, Level::High);\n    let mut adc = Adc::new(p.ADC, Irqs, Config::default());\n    let mut dma_ch = dma::Channel::new(p.DMA_CH0, Irqs);\n\n    #[cfg(any(feature = \"rp2040\", feature = \"rp235xa\"))]\n    let (mut a, mut b, mut c, mut d) = (p.PIN_26, p.PIN_27, p.PIN_28, p.PIN_29);\n    #[cfg(feature = \"rp235xb\")]\n    let (mut a, mut b, mut c, mut d) = (p.PIN_44, p.PIN_45, p.PIN_46, p.PIN_47);\n\n    {\n        {\n            let mut p = Channel::new_pin(a.reborrow(), Pull::Down);\n            defmt::assert!(adc.blocking_read(&mut p).unwrap() < 0b01_0000_0000);\n            defmt::assert!(adc.read(&mut p).await.unwrap() < 0b01_0000_0000);\n        }\n        {\n            let mut p = Channel::new_pin(a.reborrow(), Pull::Up);\n            defmt::assert!(adc.blocking_read(&mut p).unwrap() > 0b11_0000_0000);\n            defmt::assert!(adc.read(&mut p).await.unwrap() > 0b11_0000_0000);\n        }\n    }\n    // not bothering with async reads from now on\n    {\n        {\n            let mut p = Channel::new_pin(b.reborrow(), Pull::Down);\n            defmt::assert!(adc.blocking_read(&mut p).unwrap() < 0b01_0000_0000);\n        }\n        {\n            let mut p = Channel::new_pin(b.reborrow(), Pull::Up);\n            defmt::assert!(adc.blocking_read(&mut p).unwrap() > 0b11_0000_0000);\n        }\n    }\n    {\n        {\n            let mut p = Channel::new_pin(c.reborrow(), Pull::Down);\n            defmt::assert!(adc.blocking_read(&mut p).unwrap() < 0b01_0000_0000);\n        }\n        {\n            let mut p = Channel::new_pin(c.reborrow(), Pull::Up);\n            defmt::assert!(adc.blocking_read(&mut p).unwrap() > 0b11_0000_0000);\n        }\n    }\n    {\n        // gp29 is connected to vsys through a 200k/100k divider,\n        // adding pulls should change the value\n        let low = {\n            let mut p = Channel::new_pin(d.reborrow(), Pull::Down);\n            adc.blocking_read(&mut p).unwrap()\n        };\n        let none = {\n            let mut p = Channel::new_pin(d.reborrow(), Pull::None);\n            adc.blocking_read(&mut p).unwrap()\n        };\n        let up = {\n            let mut p = Channel::new_pin(d.reborrow(), Pull::Up);\n            adc.blocking_read(&mut p).unwrap()\n        };\n        defmt::assert!(low < none);\n        defmt::assert!(none < up);\n    }\n    {\n        let temp = convert_to_celsius(\n            adc.read(&mut Channel::new_temp_sensor(p.ADC_TEMP_SENSOR.reborrow()))\n                .await\n                .unwrap(),\n        );\n        defmt::assert!(temp > 0.0);\n        defmt::assert!(temp < 60.0);\n    }\n\n    // run a bunch of conversions. we'll only check gp29 and the temp\n    // sensor here for brevity, if those two work the rest will too.\n    {\n        // gp29 is connected to vsys through a 200k/100k divider,\n        // adding pulls should change the value\n        let mut low = [0u16; 16];\n        let mut none = [0u8; 16];\n        let mut up = [Sample::default(); 16];\n        adc.read_many(\n            &mut Channel::new_pin(d.reborrow(), Pull::Down),\n            &mut low,\n            1,\n            &mut dma_ch,\n        )\n        .await\n        .unwrap();\n        adc.read_many(\n            &mut Channel::new_pin(d.reborrow(), Pull::None),\n            &mut none,\n            1,\n            &mut dma_ch,\n        )\n        .await\n        .unwrap();\n        adc.read_many_raw(&mut Channel::new_pin(d.reborrow(), Pull::Up), &mut up, 1, &mut dma_ch)\n            .await;\n        defmt::assert!(low.iter().zip(none.iter()).all(|(l, n)| *l >> 4 < *n as u16));\n        defmt::assert!(up.iter().all(|s| s.good()));\n        defmt::assert!(none.iter().zip(up.iter()).all(|(n, u)| (*n as u16) < u.value()));\n    }\n    {\n        let mut temp = [0u16; 16];\n        adc.read_many(\n            &mut Channel::new_temp_sensor(p.ADC_TEMP_SENSOR.reborrow()),\n            &mut temp,\n            1,\n            &mut dma_ch,\n        )\n        .await\n        .unwrap();\n        let temp = temp.map(convert_to_celsius);\n        defmt::assert!(temp.iter().all(|t| *t > 0.0));\n        defmt::assert!(temp.iter().all(|t| *t < 60.0));\n    }\n    {\n        let mut multi = [0u16; 2];\n        let mut channels = [\n            Channel::new_pin(a.reborrow(), Pull::Up),\n            Channel::new_temp_sensor(p.ADC_TEMP_SENSOR.reborrow()),\n        ];\n        adc.read_many_multichannel(&mut channels, &mut multi, 1, &mut dma_ch)\n            .await\n            .unwrap();\n        defmt::assert!(multi[0] > 3_000);\n        let temp = convert_to_celsius(multi[1]);\n        defmt::assert!(temp > 0.0 && temp < 60.0);\n    }\n\n    info!(\"Test OK\");\n    cortex_m::asm::bkpt();\n}\n\nfn convert_to_celsius(raw_temp: u16) -> f32 {\n    // According to chapter 4.9.5. Temperature Sensor in RP2040 datasheet\n    27.0 - (raw_temp as f32 * 3.3 / 4096.0 - 0.706) / 0.001721 as f32\n}\n"
  },
  {
    "path": "tests/rp/src/bin/aon_timer.rs",
    "content": "#![no_std]\n#![no_main]\n#[cfg(feature = \"rp235xa\")]\nteleprobe_meta::target!(b\"rpi-pico-2\");\n#[cfg(feature = \"rp235xb\")]\nteleprobe_meta::target!(b\"pimoroni-pico-plus-2\");\n\nuse defmt::{assert, assert_eq, *};\nuse embassy_executor::Spawner;\nuse embassy_rp::aon_timer::{AlarmWakeMode, AonTimer, ClockSource, Config, DateTime, DayOfWeek, Error};\nuse embassy_rp::bind_interrupts;\nuse embassy_time::{Duration, Instant, Timer};\nuse {defmt_rtt as _, panic_probe as _};\n\nbind_interrupts!(struct Irqs {\n    POWMAN_IRQ_TIMER => embassy_rp::aon_timer::InterruptHandler;\n});\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let mut p = embassy_rp::init(Default::default());\n    info!(\"Hello World!\");\n\n    // Basic timer operations\n    {\n        let mut timer = AonTimer::new(p.POWMAN.reborrow(), Irqs, Config::default());\n        assert!(!timer.is_running());\n\n        timer.set_counter(0);\n        timer.start();\n        assert!(timer.is_running());\n\n        Timer::after_millis(100).await;\n        let val = timer.now();\n        assert!(val >= 90 && val <= 120);\n\n        timer.stop();\n        assert!(!timer.is_running());\n    }\n\n    // Counter precision\n    {\n        let mut timer = AonTimer::new(p.POWMAN.reborrow(), Irqs, Config::default());\n        timer.set_counter(0);\n        timer.start();\n\n        Timer::after_millis(50).await;\n        let first = timer.now();\n\n        Timer::after_millis(100).await;\n        let second = timer.now();\n\n        let elapsed = second - first;\n        assert!(elapsed >= 90 && elapsed <= 120);\n\n        timer.stop();\n    }\n\n    // Set alarm at specific time\n    {\n        let mut timer = AonTimer::new(p.POWMAN.reborrow(), Irqs, Config::default());\n        timer.set_counter(0);\n        timer.start();\n\n        Timer::after_millis(50).await;\n\n        let current = timer.now();\n        let alarm_time = current + 200;\n        timer.set_alarm(alarm_time).unwrap();\n\n        Timer::after_millis(250).await;\n        assert!(timer.alarm_fired());\n\n        timer.stop();\n    }\n\n    // Set alarm relative to now\n    {\n        let mut timer = AonTimer::new(p.POWMAN.reborrow(), Irqs, Config::default());\n        timer.set_counter(0);\n        timer.start();\n\n        Timer::after_millis(50).await;\n\n        timer.set_alarm_after(Duration::from_millis(150)).unwrap();\n\n        Timer::after_millis(200).await;\n        assert!(timer.alarm_fired());\n\n        timer.stop();\n    }\n\n    // Alarm in past error\n    {\n        let mut timer = AonTimer::new(p.POWMAN.reborrow(), Irqs, Config::default());\n        timer.set_counter(1000);\n        timer.start();\n\n        Timer::after_millis(50).await;\n\n        let result = timer.set_alarm(500);\n        assert!(matches!(result, Err(Error::AlarmInPast)));\n\n        timer.stop();\n    }\n\n    // Clear alarm\n    {\n        let mut timer = AonTimer::new(p.POWMAN.reborrow(), Irqs, Config::default());\n        timer.set_counter(0);\n        timer.start();\n\n        Timer::after_millis(50).await;\n\n        timer.set_alarm_after(Duration::from_millis(100)).unwrap();\n        Timer::after_millis(150).await;\n\n        assert!(timer.alarm_fired());\n        timer.clear_alarm();\n        assert!(!timer.alarm_fired());\n\n        timer.stop();\n    }\n\n    // Disable/enable alarm\n    {\n        let mut timer = AonTimer::new(p.POWMAN.reborrow(), Irqs, Config::default());\n        timer.set_counter(0);\n        timer.start();\n\n        Timer::after_millis(50).await;\n\n        timer.set_alarm_after(Duration::from_millis(100)).unwrap();\n        timer.disable_alarm();\n\n        Timer::after_millis(150).await;\n        assert!(!timer.alarm_fired());\n\n        timer.stop();\n    }\n\n    // Async alarm wait\n    {\n        let mut timer = AonTimer::new(p.POWMAN.reborrow(), Irqs, Config::default());\n        timer.set_counter(0);\n        timer.start();\n\n        Timer::after_millis(50).await;\n\n        timer.set_alarm_after(Duration::from_millis(200)).unwrap();\n        timer.wait_for_alarm().await;\n\n        timer.stop();\n    }\n\n    // LPOSC clock source\n    {\n        let config = Config {\n            clock_source: ClockSource::Lposc,\n            clock_freq_khz: 32,\n            alarm_wake_mode: AlarmWakeMode::WfiOnly,\n        };\n        let mut timer = AonTimer::new(p.POWMAN.reborrow(), Irqs, config);\n        timer.set_counter(0);\n        timer.start();\n\n        Timer::after_millis(100).await;\n        let value = timer.now();\n        assert!(value >= 50 && value <= 150);\n\n        timer.stop();\n    }\n\n    // Counter overflow edge case\n    {\n        let mut timer = AonTimer::new(p.POWMAN.reborrow(), Irqs, Config::default());\n\n        let near_max = 0xFFFF_FFFF_FFFF_F000u64;\n        timer.set_counter(near_max);\n        timer.start();\n\n        Timer::after_millis(50).await;\n\n        let read1 = timer.now();\n        assert!(read1 >= near_max);\n\n        Timer::after_millis(100).await;\n\n        let read2 = timer.now();\n        assert!(read2 > read1);\n\n        timer.stop();\n    }\n\n    // Rapid alarms\n    {\n        let mut timer = AonTimer::new(p.POWMAN.reborrow(), Irqs, Config::default());\n        timer.set_counter(0);\n        timer.start();\n\n        Timer::after_millis(50).await;\n\n        for _ in 0..100 {\n            timer.set_alarm_after(Duration::from_millis(10)).unwrap();\n            timer.wait_for_alarm().await;\n        }\n\n        timer.stop();\n    }\n\n    // Long-running stability\n    {\n        let mut timer = AonTimer::new(p.POWMAN.reborrow(), Irqs, Config::default());\n        timer.set_counter(0);\n        timer.start();\n\n        Timer::after_millis(50).await;\n\n        let start_time = timer.now();\n        let wall_start = Instant::now();\n\n        Timer::after_secs(5).await;\n\n        let timer_elapsed = timer.now() - start_time;\n        let wall_elapsed = wall_start.elapsed().as_millis();\n        let drift = (timer_elapsed as i64) - (wall_elapsed as i64);\n\n        assert!(drift.abs() < 100);\n\n        timer.stop();\n    }\n\n    // DateTime tests\n    {\n        info!(\"DateTime: Set and read\");\n        let mut timer = AonTimer::new(p.POWMAN.reborrow(), Irqs, Config::default());\n\n        let dt = DateTime {\n            year: 2024,\n            month: 1,\n            day: 1,\n            day_of_week: DayOfWeek::Monday,\n            hour: 0,\n            minute: 0,\n            second: 0,\n        };\n\n        timer.set_datetime(dt).unwrap();\n        timer.start();\n\n        Timer::after_millis(10).await;\n\n        let read_dt = timer.now_as_datetime().unwrap();\n        assert_eq!(read_dt.year, 2024);\n        assert_eq!(read_dt.month, 1);\n        assert_eq!(read_dt.day, 1);\n\n        timer.stop();\n    }\n\n    {\n        info!(\"DateTime: Alarm\");\n        let mut timer = AonTimer::new(p.POWMAN.reborrow(), Irqs, Config::default());\n\n        let start = DateTime {\n            year: 2024,\n            month: 6,\n            day: 15,\n            day_of_week: DayOfWeek::Saturday,\n            hour: 10,\n            minute: 30,\n            second: 0,\n        };\n\n        timer.set_datetime(start).unwrap();\n        timer.start();\n\n        Timer::after_millis(50).await;\n\n        let alarm = DateTime {\n            year: 2024,\n            month: 6,\n            day: 15,\n            day_of_week: DayOfWeek::Saturday,\n            hour: 10,\n            minute: 30,\n            second: 1,\n        };\n\n        timer.set_alarm_at_datetime(alarm).unwrap();\n        timer.wait_for_alarm().await;\n\n        let final_dt = timer.now_as_datetime().unwrap();\n        assert!(final_dt.second >= 1);\n\n        timer.stop();\n    }\n\n    {\n        info!(\"DateTime: Epoch boundary\");\n        let mut timer = AonTimer::new(p.POWMAN.reborrow(), Irqs, Config::default());\n\n        let epoch = DateTime {\n            year: 1970,\n            month: 1,\n            day: 1,\n            day_of_week: DayOfWeek::Thursday,\n            hour: 0,\n            minute: 0,\n            second: 0,\n        };\n\n        timer.set_datetime(epoch).unwrap();\n        timer.start();\n\n        let counter = timer.now();\n        assert!(counter < 100);\n\n        timer.stop();\n    }\n\n    {\n        info!(\"DateTime: Leap year\");\n        let mut timer = AonTimer::new(p.POWMAN.reborrow(), Irqs, Config::default());\n\n        let leap_day = DateTime {\n            year: 2024,\n            month: 2,\n            day: 29,\n            day_of_week: DayOfWeek::Thursday,\n            hour: 0,\n            minute: 0,\n            second: 0,\n        };\n\n        timer.set_datetime(leap_day).unwrap();\n        timer.start();\n\n        Timer::after_millis(10).await;\n\n        let read = timer.now_as_datetime().unwrap();\n        assert_eq!(read.year, 2024);\n        assert_eq!(read.month, 2);\n        assert_eq!(read.day, 29);\n\n        timer.stop();\n    }\n\n    info!(\"Test OK\");\n    cortex_m::asm::bkpt();\n}\n"
  },
  {
    "path": "tests/rp/src/bin/bootsel.rs",
    "content": "#![no_std]\n#![no_main]\nteleprobe_meta::target!(b\"rpi-pico\");\n\nuse defmt::{assert_eq, *};\nuse embassy_executor::Spawner;\nuse embassy_rp::bootsel::is_bootsel_pressed;\nuse embassy_time::Timer;\nuse {defmt_rtt as _, panic_probe as _};\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let p = embassy_rp::init(Default::default());\n    info!(\"Hello World!\");\n\n    // add some delay to give an attached debug probe time to parse the\n    // defmt RTT header. Reading that header might touch flash memory, which\n    // interferes with flash write operations.\n    // https://github.com/knurling-rs/defmt/pull/683\n    Timer::after_millis(10).await;\n\n    assert_eq!(is_bootsel_pressed(p.BOOTSEL), false);\n\n    info!(\"Test OK\");\n    cortex_m::asm::bkpt();\n}\n"
  },
  {
    "path": "tests/rp/src/bin/cyw43-perf.rs",
    "content": "#![no_std]\n#![no_main]\nteleprobe_meta::target!(b\"rpi-pico\");\n\nuse cyw43::{JoinOptions, SpiBus, aligned_bytes};\nuse cyw43_pio::{DEFAULT_CLOCK_DIVIDER, PioSpi};\nuse defmt::{panic, *};\nuse embassy_executor::Spawner;\nuse embassy_net::{Config, StackResources};\nuse embassy_rp::dma::{self, Channel};\nuse embassy_rp::gpio::{Level, Output};\nuse embassy_rp::peripherals::{DMA_CH0, PIO0};\nuse embassy_rp::pio::{InterruptHandler, Pio};\nuse embassy_rp::{bind_interrupts, rom_data};\nuse static_cell::StaticCell;\nuse {defmt_rtt as _, panic_probe as _};\n\nbind_interrupts!(struct Irqs {\n    PIO0_IRQ_0 => InterruptHandler<PIO0>;\n    DMA_IRQ_0 => dma::InterruptHandler<DMA_CH0>;\n});\n\nteleprobe_meta::timeout!(120);\n\n// Test-only wifi network, no internet access!\nconst WIFI_NETWORK: &str = \"EmbassyTestWPA2\";\nconst WIFI_PASSWORD: &str = \"V8YxhKt5CdIAJFud\";\n\n#[embassy_executor::task]\nasync fn wifi_task(runner: cyw43::Runner<'static, SpiBus<Output<'static>, PioSpi<'static, PIO0, 0>>>) -> ! {\n    runner.run().await\n}\n\n#[embassy_executor::task]\nasync fn net_task(mut runner: embassy_net::Runner<'static, cyw43::NetDriver<'static>>) -> ! {\n    runner.run().await\n}\n\n#[embassy_executor::main]\nasync fn main(spawner: Spawner) {\n    info!(\"Hello World!\");\n    let p = embassy_rp::init(Default::default());\n\n    // needed for reading the firmware from flash via XIP.\n    unsafe {\n        rom_data::flash_exit_xip();\n        rom_data::flash_enter_cmd_xip();\n    }\n\n    // cyw43 firmware needs to be flashed manually:\n    //     probe-rs download 43439A0.bin --binary-format bin --chip RP2040 --base-address 0x101b0000\n    //     probe-rs download 43439A0_btfw.bin --binary-format bin --chip RP2040 --base-address 0x101f0000\n    //     probe-rs download 43439A0_clm.bin --binary-format bin --chip RP2040 --base-address 0x101f8000\n    let fw = unsafe { core::slice::from_raw_parts(0x101b0000 as *const u8, 231077) };\n    let _btfw = unsafe { core::slice::from_raw_parts(0x101f0000 as *const u8, 6164) };\n    let clm = unsafe { core::slice::from_raw_parts(0x101f8000 as *const u8, 984) };\n    let nvram = aligned_bytes!(\"../../../../cyw43-firmware/nvram_rp2040.bin\");\n\n    let pwr = Output::new(p.PIN_23, Level::Low);\n    let cs = Output::new(p.PIN_25, Level::High);\n    let mut pio = Pio::new(p.PIO0, Irqs);\n    let spi = PioSpi::new(\n        &mut pio.common,\n        pio.sm0,\n        DEFAULT_CLOCK_DIVIDER,\n        pio.irq0,\n        cs,\n        p.PIN_24,\n        p.PIN_29,\n        Channel::new(p.DMA_CH0, Irqs),\n    );\n\n    static STATE: StaticCell<cyw43::State> = StaticCell::new();\n    let state = STATE.init(cyw43::State::new());\n    let (net_device, mut control, runner) =\n        cyw43::new(state, pwr, spi, unsafe { core::mem::transmute(fw) }, nvram).await;\n    spawner.spawn(unwrap!(wifi_task(runner)));\n\n    control.init(clm).await;\n    control\n        .set_power_management(cyw43::PowerManagementMode::PowerSave)\n        .await;\n\n    // Generate random seed\n    let seed = 0x0123_4567_89ab_cdef; // chosen by fair dice roll. guarenteed to be random.\n\n    // Init network stack\n    static RESOURCES: StaticCell<StackResources<2>> = StaticCell::new();\n    let (stack, runner) = embassy_net::new(\n        net_device,\n        Config::dhcpv4(Default::default()),\n        RESOURCES.init(StackResources::new()),\n        seed,\n    );\n\n    spawner.spawn(unwrap!(net_task(runner)));\n\n    loop {\n        match control\n            .join(WIFI_NETWORK, JoinOptions::new(WIFI_PASSWORD.as_bytes()))\n            .await\n        {\n            Ok(_) => break,\n            Err(err) => {\n                panic!(\"join failed: {:?}\", err);\n            }\n        }\n    }\n\n    perf_client::run(\n        stack,\n        perf_client::Expected {\n            down_kbps: 200,\n            up_kbps: 200,\n            updown_kbps: 200,\n        },\n    )\n    .await;\n\n    info!(\"Test OK\");\n    cortex_m::asm::bkpt();\n}\n"
  },
  {
    "path": "tests/rp/src/bin/dma_copy_async.rs",
    "content": "#![no_std]\n#![no_main]\n#[cfg(feature = \"rp2040\")]\nteleprobe_meta::target!(b\"rpi-pico\");\n#[cfg(feature = \"rp235xb\")]\nteleprobe_meta::target!(b\"pimoroni-pico-plus-2\");\n\nuse defmt::{assert_eq, *};\nuse embassy_executor::Spawner;\nuse embassy_rp::peripherals::{DMA_CH0, DMA_CH1, DMA_CH2};\nuse embassy_rp::{bind_interrupts, dma};\nuse {defmt_rtt as _, panic_probe as _};\n\nbind_interrupts!(struct Irqs {\n    DMA_IRQ_0 => dma::InterruptHandler<DMA_CH0>, dma::InterruptHandler<DMA_CH1>, dma::InterruptHandler<DMA_CH2>;\n});\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let p = embassy_rp::init(Default::default());\n    info!(\"Hello World!\");\n\n    // Check `u8` copy\n    {\n        let data: [u8; 2] = [0xC0, 0xDE];\n        let mut buf = [0; 2];\n        let mut ch = dma::Channel::new(p.DMA_CH0, Irqs);\n        unsafe { ch.copy(&data, &mut buf).await };\n        assert_eq!(buf, data);\n    }\n\n    // Check `u16` copy\n    {\n        let data: [u16; 2] = [0xC0BE, 0xDEAD];\n        let mut buf = [0; 2];\n        let mut ch = dma::Channel::new(p.DMA_CH1, Irqs);\n        unsafe { ch.copy(&data, &mut buf).await };\n        assert_eq!(buf, data);\n    }\n\n    // Check `u32` copy\n    {\n        let data: [u32; 2] = [0xC0BEDEAD, 0xDEADAAFF];\n        let mut buf = [0; 2];\n        let mut ch = dma::Channel::new(p.DMA_CH2, Irqs);\n        unsafe { ch.copy(&data, &mut buf).await };\n        assert_eq!(buf, data);\n    }\n\n    info!(\"Test OK\");\n    cortex_m::asm::bkpt();\n}\n"
  },
  {
    "path": "tests/rp/src/bin/ethernet_w5100s_perf.rs",
    "content": "#![no_std]\n#![no_main]\nteleprobe_meta::target!(b\"w5100s-evb-pico\");\nteleprobe_meta::timeout!(120);\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_net::StackResources;\nuse embassy_net_wiznet::chip::W5100S;\nuse embassy_net_wiznet::*;\nuse embassy_rp::clocks::RoscRng;\nuse embassy_rp::gpio::{Input, Level, Output, Pull};\nuse embassy_rp::peripherals::{DMA_CH0, DMA_CH1, SPI0};\nuse embassy_rp::spi::{Async, Config as SpiConfig, Spi};\nuse embassy_rp::{bind_interrupts, dma};\nuse embassy_time::Delay;\nuse embedded_hal_bus::spi::ExclusiveDevice;\nuse static_cell::StaticCell;\nuse {defmt_rtt as _, panic_probe as _};\n\nbind_interrupts!(struct Irqs {\n    DMA_IRQ_0 => dma::InterruptHandler<DMA_CH0>, dma::InterruptHandler<DMA_CH1>;\n});\n\n#[embassy_executor::task]\nasync fn ethernet_task(\n    runner: Runner<\n        'static,\n        W5100S,\n        ExclusiveDevice<Spi<'static, SPI0, Async>, Output<'static>, Delay>,\n        Input<'static>,\n        Output<'static>,\n    >,\n) -> ! {\n    runner.run().await\n}\n\n#[embassy_executor::task]\nasync fn net_task(mut runner: embassy_net::Runner<'static, Device<'static>>) -> ! {\n    runner.run().await\n}\n\n#[embassy_executor::main]\nasync fn main(spawner: Spawner) {\n    let p = embassy_rp::init(Default::default());\n    let mut rng = RoscRng;\n\n    let mut spi_cfg = SpiConfig::default();\n    spi_cfg.frequency = 50_000_000;\n    let (miso, mosi, clk) = (p.PIN_16, p.PIN_19, p.PIN_18);\n    let spi = Spi::new(p.SPI0, clk, mosi, miso, p.DMA_CH0, p.DMA_CH1, Irqs, spi_cfg);\n    let cs = Output::new(p.PIN_17, Level::High);\n    let w5500_int = Input::new(p.PIN_21, Pull::Up);\n    let w5500_reset = Output::new(p.PIN_20, Level::High);\n\n    let mac_addr = [0x02, 0x00, 0x00, 0x00, 0x00, 0x00];\n    static STATE: StaticCell<State<8, 8>> = StaticCell::new();\n    let state = STATE.init(State::<8, 8>::new());\n    let (device, runner) = embassy_net_wiznet::new(\n        mac_addr,\n        state,\n        ExclusiveDevice::new(spi, cs, Delay),\n        w5500_int,\n        w5500_reset,\n    )\n    .await\n    .unwrap();\n    spawner.spawn(unwrap!(ethernet_task(runner)));\n\n    // Generate random seed\n    let seed = rng.next_u64();\n\n    // Init network stack\n    static RESOURCES: StaticCell<StackResources<2>> = StaticCell::new();\n    let (stack, runner) = embassy_net::new(\n        device,\n        embassy_net::Config::dhcpv4(Default::default()),\n        RESOURCES.init(StackResources::new()),\n        seed,\n    );\n\n    // Launch network task\n    spawner.spawn(unwrap!(net_task(runner)));\n\n    perf_client::run(\n        stack,\n        perf_client::Expected {\n            down_kbps: 500,\n            up_kbps: 500,\n            updown_kbps: 300,\n        },\n    )\n    .await;\n\n    info!(\"Test OK\");\n    cortex_m::asm::bkpt();\n}\n"
  },
  {
    "path": "tests/rp/src/bin/flash.rs",
    "content": "#![no_std]\n#![no_main]\n#[cfg(feature = \"rp2040\")]\nteleprobe_meta::target!(b\"rpi-pico\");\n#[cfg(feature = \"rp235xb\")]\nteleprobe_meta::target!(b\"pimoroni-pico-plus-2\");\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_rp::flash::{Async, ERASE_SIZE, FLASH_BASE};\nuse embassy_rp::peripherals::DMA_CH0;\nuse embassy_rp::{bind_interrupts, dma};\nuse embassy_time::Timer;\nuse {defmt_rtt as _, panic_probe as _};\n\nbind_interrupts!(struct Irqs {\n    DMA_IRQ_0 => dma::InterruptHandler<DMA_CH0>;\n});\n\nconst ADDR_OFFSET: u32 = 0x8000;\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let p = embassy_rp::init(Default::default());\n    info!(\"Hello World!\");\n\n    // add some delay to give an attached debug probe time to parse the\n    // defmt RTT header. Reading that header might touch flash memory, which\n    // interferes with flash write operations.\n    // https://github.com/knurling-rs/defmt/pull/683\n    Timer::after_millis(10).await;\n\n    let mut flash = embassy_rp::flash::Flash::<_, Async, { 2 * 1024 * 1024 }>::new(p.FLASH, p.DMA_CH0, Irqs);\n\n    // Get JEDEC id\n    #[cfg(feature = \"rp2040\")]\n    {\n        let jedec = defmt::unwrap!(flash.blocking_jedec_id());\n        info!(\"jedec id: 0x{:x}\", jedec);\n    }\n\n    // Get unique id\n    #[cfg(feature = \"rp2040\")]\n    {\n        let mut uid = [0; 8];\n        defmt::unwrap!(flash.blocking_unique_id(&mut uid));\n        info!(\"unique id: {:?}\", uid);\n    }\n\n    let mut buf = [0u8; ERASE_SIZE];\n    defmt::unwrap!(flash.blocking_read(ADDR_OFFSET, &mut buf));\n\n    info!(\"Addr of flash block is {:x}\", ADDR_OFFSET + FLASH_BASE as u32);\n    info!(\"Contents start with {=[u8]}\", buf[0..4]);\n\n    defmt::unwrap!(flash.blocking_erase(ADDR_OFFSET, ADDR_OFFSET + ERASE_SIZE as u32));\n\n    defmt::unwrap!(flash.blocking_read(ADDR_OFFSET, &mut buf));\n    info!(\"Contents after erase starts with {=[u8]}\", buf[0..4]);\n    if buf.iter().any(|x| *x != 0xFF) {\n        defmt::panic!(\"unexpected\");\n    }\n\n    for b in buf.iter_mut() {\n        *b = 0xDA;\n    }\n\n    defmt::unwrap!(flash.blocking_write(ADDR_OFFSET, &mut buf));\n\n    defmt::unwrap!(flash.blocking_read(ADDR_OFFSET, &mut buf));\n    info!(\"Contents after write starts with {=[u8]}\", buf[0..4]);\n    if buf.iter().any(|x| *x != 0xDA) {\n        defmt::panic!(\"unexpected\");\n    }\n\n    let mut buf = [0u32; ERASE_SIZE / 4];\n\n    defmt::unwrap!(flash.background_read(ADDR_OFFSET, &mut buf)).await;\n    info!(\"Contents after write starts with {=u32:x}\", buf[0]);\n    if buf.iter().any(|x| *x != 0xDADADADA) {\n        defmt::panic!(\"unexpected\");\n    }\n\n    info!(\"Test OK\");\n    cortex_m::asm::bkpt();\n}\n"
  },
  {
    "path": "tests/rp/src/bin/float.rs",
    "content": "#![no_std]\n#![no_main]\nteleprobe_meta::target!(b\"rpi-pico\");\n\nuse defmt::*;\nuse embassy_executor::Spawner;\nuse embassy_rp::pac;\nuse embassy_time::Timer;\nuse {defmt_rtt as _, panic_probe as _};\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    embassy_rp::init(Default::default());\n    info!(\"Hello World!\");\n\n    const PI_F: f32 = 3.1415926535f32;\n    const PI_D: f64 = 3.14159265358979323846f64;\n\n    pac::BUSCTRL\n        .perfsel(0)\n        .write(|r| r.set_perfsel(pac::busctrl::vals::Perfsel::ROM));\n\n    for i in 0..=360 {\n        let rad_f = (i as f32) * PI_F / 180.0;\n        info!(\n            \"{}° float: {=f32} / {=f32} / {=f32} / {=f32}\",\n            i,\n            rad_f,\n            rad_f - PI_F,\n            rad_f + PI_F,\n            rad_f % PI_F\n        );\n        let rad_d = (i as f64) * PI_D / 180.0;\n        info!(\n            \"{}° double: {=f64} / {=f64} / {=f64} / {=f64}\",\n            i,\n            rad_d,\n            rad_d - PI_D,\n            rad_d + PI_D,\n            rad_d % PI_D\n        );\n        Timer::after_millis(10).await;\n    }\n\n    let rom_accesses = pac::BUSCTRL.perfctr(0).read().perfctr();\n    // every float operation used here uses at least 10 cycles\n    defmt::assert!(rom_accesses >= 360 * 12 * 10);\n\n    info!(\"Test OK\");\n    cortex_m::asm::bkpt();\n}\n"
  },
  {
    "path": "tests/rp/src/bin/gpio.rs",
    "content": "#![no_std]\n#![no_main]\n#[cfg(feature = \"rp2040\")]\nteleprobe_meta::target!(b\"rpi-pico\");\n#[cfg(feature = \"rp235xb\")]\nteleprobe_meta::target!(b\"pimoroni-pico-plus-2\");\n\nuse defmt::{assert, *};\nuse embassy_executor::Spawner;\n#[cfg(feature = \"rp2040\")]\nuse embassy_rp::gpio::OutputOpenDrain;\nuse embassy_rp::gpio::{Flex, Input, Level, Output, Pull};\nuse {defmt_rtt as _, panic_probe as _};\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let p = embassy_rp::init(Default::default());\n    info!(\"Hello World!\");\n\n    let (mut a, mut b) = (p.PIN_0, p.PIN_1);\n\n    // Test initial output\n    {\n        let b = Input::new(b.reborrow(), Pull::None);\n\n        {\n            let a = Output::new(a.reborrow(), Level::Low);\n            delay();\n            assert!(b.is_low());\n            assert!(!b.is_high());\n            assert!(a.is_set_low());\n            assert!(!a.is_set_high());\n        }\n        {\n            let mut a = Output::new(a.reborrow(), Level::High);\n            delay();\n            assert!(!b.is_low());\n            assert!(b.is_high());\n            assert!(!a.is_set_low());\n            assert!(a.is_set_high());\n\n            // Test is_set_low / is_set_high\n            a.set_low();\n            delay();\n            assert!(b.is_low());\n            assert!(a.is_set_low());\n            assert!(!a.is_set_high());\n\n            a.set_high();\n            delay();\n            assert!(b.is_high());\n            assert!(!a.is_set_low());\n            assert!(a.is_set_high());\n\n            // Test toggle\n            a.toggle();\n            delay();\n            assert!(b.is_low());\n            assert!(a.is_set_low());\n            assert!(!a.is_set_high());\n\n            a.toggle();\n            delay();\n            assert!(b.is_high());\n            assert!(!a.is_set_low());\n            assert!(a.is_set_high());\n        }\n    }\n\n    // Test input inversion\n    {\n        let mut b = Input::new(b.reborrow(), Pull::None);\n        b.set_inversion(true);\n        // no pull, the status is undefined\n\n        let mut a = Output::new(a.reborrow(), Level::Low);\n        delay();\n        assert!(b.is_high());\n        a.set_high();\n        delay();\n        assert!(b.is_low());\n\n        b.set_inversion(false);\n        a.set_inversion(true);\n\n        a.set_low();\n        delay();\n        assert!(b.is_high());\n\n        a.set_high();\n        delay();\n        assert!(b.is_low());\n\n        b.set_inversion(true);\n        a.set_high();\n        delay();\n        assert!(b.is_high());\n\n        a.set_high();\n        delay();\n        assert!(b.is_high());\n    }\n\n    // Test input no pull\n    {\n        let b = Input::new(b.reborrow(), Pull::None);\n        // no pull, the status is undefined\n\n        let mut a = Output::new(a.reborrow(), Level::Low);\n        delay();\n        assert!(b.is_low());\n        a.set_high();\n        delay();\n        assert!(b.is_high());\n    }\n\n    // Test input pulldown\n    #[cfg(feature = \"rp2040\")]\n    {\n        let b = Input::new(b.reborrow(), Pull::Down);\n        delay();\n        assert!(b.is_low());\n\n        let mut a = Output::new(a.reborrow(), Level::Low);\n        delay();\n        assert!(b.is_low());\n        a.set_high();\n        delay();\n        assert!(b.is_high());\n    }\n\n    // Test input pullup\n    {\n        let b = Input::new(b.reborrow(), Pull::Up);\n        delay();\n        assert!(b.is_high());\n\n        let mut a = Output::new(a.reborrow(), Level::Low);\n        delay();\n        assert!(b.is_low());\n        a.set_high();\n        delay();\n        assert!(b.is_high());\n    }\n\n    // OUTPUT OPEN DRAIN\n    #[cfg(feature = \"rp2040\")]\n    {\n        let mut b = OutputOpenDrain::new(b.reborrow(), Level::High);\n        let mut a = Flex::new(a.reborrow());\n        a.set_as_input();\n\n        // When an OutputOpenDrain is high, it doesn't drive the pin.\n        b.set_high();\n        a.set_pull(Pull::Up);\n        delay();\n        assert!(a.is_high());\n        a.set_pull(Pull::Down);\n        delay();\n        assert!(a.is_low());\n\n        // When an OutputOpenDrain is low, it drives the pin low.\n        b.set_low();\n        a.set_pull(Pull::Up);\n        delay();\n        assert!(a.is_low());\n        a.set_pull(Pull::Down);\n        delay();\n        assert!(a.is_low());\n\n        // Check high again\n        b.set_high();\n        a.set_pull(Pull::Up);\n        delay();\n        assert!(a.is_high());\n        a.set_pull(Pull::Down);\n        delay();\n        assert!(a.is_low());\n\n        // When an OutputOpenDrain is high, it reads the input value in the pin.\n        b.set_high();\n        a.set_as_input();\n        a.set_pull(Pull::Up);\n        delay();\n        assert!(b.is_high());\n        a.set_as_output();\n        a.set_low();\n        delay();\n        assert!(b.is_low());\n\n        // When an OutputOpenDrain is low, it always reads low.\n        b.set_low();\n        a.set_as_input();\n        a.set_pull(Pull::Up);\n        delay();\n        assert!(b.is_low());\n        a.set_as_output();\n        a.set_low();\n        delay();\n        assert!(b.is_low());\n    }\n\n    // FLEX\n    // Test initial output\n    {\n        //Flex pin configured as input\n        let mut b = Flex::new(b.reborrow());\n        b.set_as_input();\n\n        {\n            //Flex pin configured as output\n            let mut a = Flex::new(a.reborrow()); //Flex pin configured as output\n            a.set_low(); // Pin state must be set before configuring the pin, thus we avoid unknown state\n            a.set_as_output();\n            delay();\n            assert!(b.is_low());\n        }\n        {\n            //Flex pin configured as output\n            let mut a = Flex::new(a.reborrow());\n            a.set_high();\n            a.set_as_output();\n\n            delay();\n            assert!(b.is_high());\n        }\n    }\n\n    // Test input no pull\n    {\n        let mut b = Flex::new(b.reborrow());\n        b.set_as_input(); // no pull by default.\n\n        let mut a = Flex::new(a.reborrow());\n        a.set_low();\n        a.set_as_output();\n\n        delay();\n        assert!(b.is_low());\n        a.set_high();\n        delay();\n        assert!(b.is_high());\n    }\n\n    // Test input pulldown\n    #[cfg(feature = \"rp2040\")]\n    {\n        let mut b = Flex::new(b.reborrow());\n        b.set_as_input();\n        b.set_pull(Pull::Down);\n        delay();\n        assert!(b.is_low());\n\n        let mut a = Flex::new(a.reborrow());\n        a.set_low();\n        a.set_as_output();\n        delay();\n        assert!(b.is_low());\n        a.set_high();\n        delay();\n        assert!(b.is_high());\n    }\n\n    // Test input pullup\n    {\n        let mut b = Flex::new(b.reborrow());\n        b.set_as_input();\n        b.set_pull(Pull::Up);\n        delay();\n        assert!(b.is_high());\n\n        let mut a = Flex::new(a.reborrow());\n        a.set_high();\n        a.set_as_output();\n        delay();\n        assert!(b.is_high());\n        a.set_low();\n        delay();\n        assert!(b.is_low());\n    }\n\n    info!(\"Test OK\");\n    cortex_m::asm::bkpt();\n}\n\nfn delay() {\n    cortex_m::asm::delay(10000);\n}\n"
  },
  {
    "path": "tests/rp/src/bin/gpio_async.rs",
    "content": "#![no_std]\n#![no_main]\n#[cfg(feature = \"rp2040\")]\nteleprobe_meta::target!(b\"rpi-pico\");\n#[cfg(feature = \"rp235xb\")]\nteleprobe_meta::target!(b\"pimoroni-pico-plus-2\");\n\nuse defmt::{assert, *};\nuse embassy_executor::Spawner;\nuse embassy_futures::join::join;\nuse embassy_rp::gpio::{Input, Level, Output, Pull};\nuse embassy_time::{Duration, Instant, Timer};\nuse {defmt_rtt as _, panic_probe as _};\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let p = embassy_rp::init(Default::default());\n    info!(\"embassy-rp gpio_async test\");\n\n    // On the CI device the following pins are connected with each other.\n    let (mut output_pin, mut input_pin) = (p.PIN_0, p.PIN_1);\n\n    {\n        info!(\"test wait_for_high\");\n        let mut output = Output::new(output_pin.reborrow(), Level::Low);\n        let mut input = Input::new(input_pin.reborrow(), Pull::None);\n\n        assert!(input.is_low(), \"input was expected to be low\");\n\n        let set_high_future = async {\n            // Allow time for wait_for_high_future to await wait_for_high().\n            Timer::after_millis(10).await;\n            output.set_high();\n        };\n        let wait_for_high_future = async {\n            let start = Instant::now();\n            input.wait_for_high().await;\n            assert_duration(start);\n        };\n        join(set_high_future, wait_for_high_future).await;\n        info!(\"test wait_for_high: OK\\n\");\n    }\n\n    {\n        info!(\"test wait_for_low\");\n        let mut output = Output::new(output_pin.reborrow(), Level::High);\n        let mut input = Input::new(input_pin.reborrow(), Pull::None);\n\n        assert!(input.is_high(), \"input was expected to be high\");\n\n        let set_low_future = async {\n            Timer::after_millis(10).await;\n            output.set_low();\n        };\n        let wait_for_low_future = async {\n            let start = Instant::now();\n            input.wait_for_low().await;\n            assert_duration(start);\n        };\n        join(set_low_future, wait_for_low_future).await;\n        info!(\"test wait_for_low: OK\\n\");\n    }\n\n    {\n        info!(\"test wait_for_rising_edge\");\n        let mut output = Output::new(output_pin.reborrow(), Level::Low);\n        let mut input = Input::new(input_pin.reborrow(), Pull::None);\n\n        assert!(input.is_low(), \"input was expected to be low\");\n\n        let set_high_future = async {\n            Timer::after_millis(10).await;\n            output.set_high();\n        };\n        let wait_for_rising_edge_future = async {\n            let start = Instant::now();\n            input.wait_for_rising_edge().await;\n            assert_duration(start);\n        };\n        join(set_high_future, wait_for_rising_edge_future).await;\n        info!(\"test wait_for_rising_edge: OK\\n\");\n    }\n\n    {\n        info!(\"test wait_for_falling_edge\");\n        let mut output = Output::new(output_pin.reborrow(), Level::High);\n        let mut input = Input::new(input_pin.reborrow(), Pull::None);\n\n        assert!(input.is_high(), \"input was expected to be high\");\n\n        let set_low_future = async {\n            Timer::after_millis(10).await;\n            output.set_low();\n        };\n        let wait_for_falling_edge_future = async {\n            let start = Instant::now();\n            input.wait_for_falling_edge().await;\n            assert_duration(start);\n        };\n        join(set_low_future, wait_for_falling_edge_future).await;\n        info!(\"test wait_for_falling_edge: OK\\n\");\n    }\n\n    {\n        info!(\"test wait_for_any_edge (falling)\");\n        let mut output = Output::new(output_pin.reborrow(), Level::High);\n        let mut input = Input::new(input_pin.reborrow(), Pull::None);\n\n        assert!(input.is_high(), \"input was expected to be high\");\n\n        let set_low_future = async {\n            Timer::after_millis(10).await;\n            output.set_low();\n        };\n        let wait_for_any_edge_future = async {\n            let start = Instant::now();\n            input.wait_for_any_edge().await;\n            assert_duration(start);\n        };\n        join(set_low_future, wait_for_any_edge_future).await;\n        info!(\"test wait_for_any_edge (falling): OK\\n\");\n    }\n\n    {\n        info!(\"test wait_for_any_edge (rising)\");\n        let mut output = Output::new(output_pin.reborrow(), Level::Low);\n        let mut input = Input::new(input_pin.reborrow(), Pull::None);\n\n        assert!(input.is_low(), \"input was expected to be low\");\n\n        let set_high_future = async {\n            Timer::after_millis(10).await;\n            output.set_high();\n        };\n        let wait_for_any_edge_future = async {\n            let start = Instant::now();\n            input.wait_for_any_edge().await;\n            assert_duration(start);\n        };\n        join(set_high_future, wait_for_any_edge_future).await;\n        info!(\"test wait_for_any_edge (rising): OK\\n\");\n    }\n\n    info!(\"Test OK\");\n    cortex_m::asm::bkpt();\n\n    fn assert_duration(start: Instant) {\n        let dur = Instant::now() - start;\n        assert!(dur >= Duration::from_millis(10) && dur < Duration::from_millis(11));\n    }\n}\n"
  },
  {
    "path": "tests/rp/src/bin/gpio_multicore.rs",
    "content": "#![no_std]\n#![no_main]\n#[cfg(feature = \"rp2040\")]\nteleprobe_meta::target!(b\"rpi-pico\");\n#[cfg(feature = \"rp235xb\")]\nteleprobe_meta::target!(b\"pimoroni-pico-plus-2\");\n\nuse defmt::{info, unwrap};\nuse embassy_executor::Executor;\nuse embassy_rp::Peri;\nuse embassy_rp::gpio::{Input, Level, Output, Pull};\nuse embassy_rp::multicore::{Stack, spawn_core1};\nuse embassy_rp::peripherals::{PIN_0, PIN_1};\nuse embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;\nuse embassy_sync::channel::Channel;\nuse static_cell::StaticCell;\nuse {defmt_rtt as _, panic_probe as _};\n\nstatic mut CORE1_STACK: Stack<1024> = Stack::new();\nstatic EXECUTOR0: StaticCell<Executor> = StaticCell::new();\nstatic EXECUTOR1: StaticCell<Executor> = StaticCell::new();\nstatic CHANNEL0: Channel<CriticalSectionRawMutex, (), 1> = Channel::new();\nstatic CHANNEL1: Channel<CriticalSectionRawMutex, (), 1> = Channel::new();\n\n#[cortex_m_rt::entry]\nfn main() -> ! {\n    let p = embassy_rp::init(Default::default());\n    spawn_core1(\n        p.CORE1,\n        unsafe { &mut *core::ptr::addr_of_mut!(CORE1_STACK) },\n        move || {\n            let executor1 = EXECUTOR1.init(Executor::new());\n            executor1.run(|spawner| spawner.spawn(unwrap!(core1_task(p.PIN_1))));\n        },\n    );\n    let executor0 = EXECUTOR0.init(Executor::new());\n    executor0.run(|spawner| spawner.spawn(unwrap!(core0_task(p.PIN_0))));\n}\n\n#[embassy_executor::task]\nasync fn core0_task(p: Peri<'static, PIN_0>) {\n    info!(\"CORE0 is running\");\n\n    let mut pin = Output::new(p, Level::Low);\n\n    CHANNEL0.send(()).await;\n    CHANNEL1.receive().await;\n\n    pin.set_high();\n\n    CHANNEL1.receive().await;\n\n    info!(\"Test OK\");\n    cortex_m::asm::bkpt();\n}\n\n#[embassy_executor::task]\nasync fn core1_task(p: Peri<'static, PIN_1>) {\n    info!(\"CORE1 is running\");\n\n    CHANNEL0.receive().await;\n\n    let mut pin = Input::new(p, Pull::None);\n    let wait = pin.wait_for_rising_edge();\n\n    CHANNEL1.send(()).await;\n\n    wait.await;\n\n    CHANNEL1.send(()).await;\n}\n"
  },
  {
    "path": "tests/rp/src/bin/i2c.rs",
    "content": "#![no_std]\n#![no_main]\n#[cfg(feature = \"rp2040\")]\nteleprobe_meta::target!(b\"rpi-pico\");\n#[cfg(feature = \"rp235xb\")]\nteleprobe_meta::target!(b\"pimoroni-pico-plus-2\");\n\nuse defmt::{assert_eq, info, panic};\nuse embassy_embedded_hal::SetConfig;\nuse embassy_executor::Spawner;\nuse embassy_rp::clocks::{PllConfig, XoscConfig};\nuse embassy_rp::config::Config as rpConfig;\nuse embassy_rp::peripherals::{I2C0, I2C1};\nuse embassy_rp::{bind_interrupts, i2c, i2c_slave};\nuse embedded_hal_1::i2c::Operation;\nuse embedded_hal_async::i2c::I2c;\nuse {defmt_rtt as _, panic_probe as _, panic_probe as _, panic_probe as _};\n\nuse crate::i2c::AbortReason;\n\nbind_interrupts!(struct Irqs {\n    I2C0_IRQ => i2c::InterruptHandler<I2C0>;\n    I2C1_IRQ => i2c::InterruptHandler<I2C1>;\n});\n\nconst DEV_ADDR: u8 = 0x42;\n\n#[embassy_executor::task]\nasync fn device_task(mut dev: i2c_slave::I2cSlave<'static, I2C1>) -> ! {\n    info!(\"Device start\");\n\n    let mut count = 0xD0;\n\n    loop {\n        let mut buf = [0u8; 128];\n        match dev.listen(&mut buf).await {\n            Ok(i2c_slave::Command::GeneralCall(len)) => {\n                assert_eq!(buf[..len], [0xCA, 0x11], \"recieving the general call failed\");\n                info!(\"General Call - OK\");\n            }\n            Ok(i2c_slave::Command::Read) => {\n                loop {\n                    match dev.respond_to_read(&[count]).await {\n                        Ok(x) => match x {\n                            i2c_slave::ReadStatus::Done => break,\n                            i2c_slave::ReadStatus::NeedMoreBytes => count += 1,\n                            i2c_slave::ReadStatus::LeftoverBytes(x) => panic!(\"tried to write {} extra bytes\", x),\n                        },\n                        Err(e) => match e {\n                            embassy_rp::i2c_slave::Error::Abort(AbortReason::Other(n)) => panic!(\"Other {:b}\", n),\n                            _ => panic!(\"{}\", e),\n                        },\n                    }\n                }\n                count += 1;\n            }\n            Ok(i2c_slave::Command::Write(len)) => match len {\n                1 => {\n                    assert_eq!(buf[..len], [0xAA], \"recieving a single byte failed\");\n                    info!(\"Single Byte Write - OK\")\n                }\n                4 => {\n                    assert_eq!(buf[..len], [0xAA, 0xBB, 0xCC, 0xDD], \"recieving 4 bytes failed\");\n                    info!(\"4 Byte Write - OK\")\n                }\n                32 => {\n                    assert_eq!(\n                        buf[..len],\n                        [\n                            0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,\n                            25, 26, 27, 28, 29, 30, 31\n                        ],\n                        \"recieving 32 bytes failed\"\n                    );\n                    info!(\"32 Byte Write - OK\")\n                }\n                _ => panic!(\"Invalid write length {}\", len),\n            },\n            Ok(i2c_slave::Command::WriteRead(len)) => {\n                info!(\"device received write read: {:x}\", buf[..len]);\n                match buf[0] {\n                    0xC2 => {\n                        let resp_buff = [0xD1, 0xD2, 0xD3, 0xD4];\n                        dev.respond_to_read(&resp_buff).await.unwrap();\n                    }\n                    0xC8 => {\n                        let mut resp_buff = [0u8; 32];\n                        for i in 0..32 {\n                            resp_buff[i] = i as u8;\n                        }\n                        dev.respond_to_read(&resp_buff).await.unwrap();\n                        // reset count for next round of tests\n                        count = 0xD0;\n                    }\n                    x => panic!(\"Invalid Write Read {:x}\", x),\n                }\n            }\n            Err(e) => match e {\n                embassy_rp::i2c_slave::Error::Abort(AbortReason::Other(n)) => panic!(\"Other {:b}\", n),\n                _ => panic!(\"{}\", e),\n            },\n        }\n    }\n}\n\nasync fn controller_task(con: &mut i2c::I2c<'static, I2C0, i2c::Async>) {\n    info!(\"Controller start\");\n\n    {\n        let buf = [0xCA, 0x11];\n        con.write(0u16, &buf).await.unwrap();\n        info!(\"Controler general call write\");\n        embassy_futures::yield_now().await;\n    }\n\n    {\n        let mut buf = [0u8];\n        con.read(DEV_ADDR, &mut buf).await.unwrap();\n        assert_eq!(buf, [0xD0], \"single byte read failed\");\n        info!(\"single byte read - OK\");\n        embassy_futures::yield_now().await;\n    }\n\n    {\n        let mut buf = [0u8; 4];\n        con.read(DEV_ADDR, &mut buf).await.unwrap();\n        assert_eq!(buf, [0xD1, 0xD2, 0xD3, 0xD4], \"single byte read failed\");\n        info!(\"4 byte read - OK\");\n        embassy_futures::yield_now().await;\n    }\n\n    {\n        let buf = [0xAA];\n        con.write(DEV_ADDR, &buf).await.unwrap();\n        info!(\"Controler single byte write\");\n        embassy_futures::yield_now().await;\n    }\n\n    {\n        let buf = [0xAA, 0xBB, 0xCC, 0xDD];\n        con.write(DEV_ADDR, &buf).await.unwrap();\n        info!(\"Controler 4 byte write\");\n        embassy_futures::yield_now().await;\n    }\n\n    {\n        let mut buf = [0u8; 32];\n        for i in 0..32 {\n            buf[i] = i as u8;\n        }\n        con.write(DEV_ADDR, &buf).await.unwrap();\n        info!(\"Controler 32 byte write\");\n        embassy_futures::yield_now().await;\n    }\n\n    {\n        let mut buf = [0u8; 4];\n        let mut ops = [Operation::Write(&[0xC2]), Operation::Read(&mut buf)];\n        con.transaction(DEV_ADDR, &mut ops).await.unwrap();\n        assert_eq!(buf, [0xD1, 0xD2, 0xD3, 0xD4], \"write_read failed\");\n        info!(\"write_read - OK\");\n        embassy_futures::yield_now().await;\n    }\n\n    {\n        let mut buf = [0u8; 32];\n        let mut ops = [Operation::Write(&[0xC8]), Operation::Read(&mut buf)];\n        con.transaction(DEV_ADDR, &mut ops).await.unwrap();\n        assert_eq!(\n            buf,\n            [\n                0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27,\n                28, 29, 30, 31\n            ],\n            \"write_read of 32 bytes failed\"\n        );\n        info!(\"large write_read - OK\")\n    }\n\n    #[embassy_executor::main]\n    async fn main(spawner: Spawner) {\n        let mut config = rpConfig::default();\n        // Configure clk_sys to 48MHz to support 1kHz scl.\n        // In theory it can go lower, but we won't bother to test below 1kHz.\n        config.clocks.xosc = Some(XoscConfig {\n            hz: 12_000_000,\n            delay_multiplier: 128,\n            sys_pll: Some(PllConfig {\n                refdiv: 1,\n                fbdiv: 120,\n                post_div1: 6,\n                post_div2: 5,\n            }),\n            usb_pll: Some(PllConfig {\n                refdiv: 1,\n                fbdiv: 120,\n                post_div1: 6,\n                post_div2: 5,\n            }),\n        });\n\n        let p = embassy_rp::init(config);\n        info!(\"Hello World!\");\n\n        let d_sda = p.PIN_19;\n        let d_scl = p.PIN_18;\n        let mut config = i2c_slave::Config::default();\n        config.addr = DEV_ADDR as u16;\n        let device = i2c_slave::I2cSlave::new(p.I2C1, d_sda, d_scl, Irqs, config);\n\n        spawner.spawn(device_task(device).unwrap());\n\n        let c_sda = p.PIN_21;\n        let c_scl = p.PIN_20;\n        let mut controller = i2c::I2c::new_async(p.I2C0, c_sda, c_scl, Irqs, Default::default());\n\n        for freq in [1000, 100_000, 400_000, 1_000_000] {\n            info!(\"testing at {}hz\", freq);\n            let mut config = i2c::Config::default();\n            config.frequency = freq;\n            controller.set_config(&config).unwrap();\n            controller_task(&mut controller).await;\n        }\n\n        info!(\"Test OK\");\n        cortex_m::asm::bkpt();\n    }\n}\n"
  },
  {
    "path": "tests/rp/src/bin/multicore.rs",
    "content": "#![no_std]\n#![no_main]\n#[cfg(feature = \"rp2040\")]\nteleprobe_meta::target!(b\"rpi-pico\");\n#[cfg(feature = \"rp235xb\")]\nteleprobe_meta::target!(b\"pimoroni-pico-plus-2\");\n\nuse defmt::{info, unwrap};\nuse embassy_executor::Executor;\nuse embassy_rp::multicore::{Stack, spawn_core1};\nuse embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;\nuse embassy_sync::channel::Channel;\nuse static_cell::StaticCell;\nuse {defmt_rtt as _, panic_probe as _};\n\nstatic mut CORE1_STACK: Stack<1024> = Stack::new();\nstatic EXECUTOR0: StaticCell<Executor> = StaticCell::new();\nstatic EXECUTOR1: StaticCell<Executor> = StaticCell::new();\nstatic CHANNEL0: Channel<CriticalSectionRawMutex, bool, 1> = Channel::new();\nstatic CHANNEL1: Channel<CriticalSectionRawMutex, bool, 1> = Channel::new();\n\n#[cortex_m_rt::entry]\nfn main() -> ! {\n    let p = embassy_rp::init(Default::default());\n    spawn_core1(\n        p.CORE1,\n        unsafe { &mut *core::ptr::addr_of_mut!(CORE1_STACK) },\n        move || {\n            let executor1 = EXECUTOR1.init(Executor::new());\n            executor1.run(|spawner| spawner.spawn(unwrap!(core1_task())));\n        },\n    );\n    let executor0 = EXECUTOR0.init(Executor::new());\n    executor0.run(|spawner| spawner.spawn(unwrap!(core0_task())));\n}\n\n#[embassy_executor::task]\nasync fn core0_task() {\n    info!(\"CORE0 is running\");\n    let ping = true;\n    CHANNEL0.send(ping).await;\n    let pong = CHANNEL1.receive().await;\n    assert_eq!(ping, pong);\n\n    info!(\"Test OK\");\n    cortex_m::asm::bkpt();\n}\n\n#[embassy_executor::task]\nasync fn core1_task() {\n    info!(\"CORE1 is running\");\n    let ping = CHANNEL0.receive().await;\n    CHANNEL1.send(ping).await;\n}\n"
  },
  {
    "path": "tests/rp/src/bin/overclock.rs",
    "content": "#![no_std]\n#![no_main]\n\n#[cfg(feature = \"rp2040\")]\nteleprobe_meta::target!(b\"rpi-pico\");\n#[cfg(feature = \"rp235xb\")]\nteleprobe_meta::target!(b\"pimoroni-pico-plus-2\");\n\nuse defmt::info;\nuse embassy_executor::Spawner;\nuse embassy_rp::clocks::{ClockConfig, CoreVoltage, clk_sys_freq, core_voltage};\nuse embassy_rp::config::Config;\nuse embassy_time::Instant;\nuse {defmt_rtt as _, panic_probe as _};\n\nconst COUNT_TO: i64 = 10_000_000;\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let mut config = Config::default();\n\n    // Initialize with 200MHz clock configuration\n    config.clocks = ClockConfig::system_freq(200_000_000).unwrap();\n\n    // if we are rp235x, we need to manually set the core voltage. rp2040 should do this automatically\n    #[cfg(feature = \"rp235xb\")]\n    {\n        config.clocks.core_voltage = CoreVoltage::V1_15;\n    }\n\n    let _p = embassy_rp::init(config);\n\n    // We should be at core voltage of 1.15V\n    assert_eq!(core_voltage().unwrap(), CoreVoltage::V1_15, \"Core voltage is not 1.15V\");\n    // We should be at 200MHz\n    assert_eq!(clk_sys_freq(), 200_000_000, \"System clock frequency is not 200MHz\");\n\n    // Test the system speed\n    let time_elapsed = {\n        let mut counter = 0;\n        let start = Instant::now();\n        while counter < COUNT_TO {\n            counter += 1;\n        }\n        let elapsed = Instant::now() - start;\n\n        elapsed.as_millis()\n    };\n\n    // Tests will fail if unused variables are detected:\n    // Report the elapsed time, so that the compiler doesn't optimize it away for the chip not on test\n    info!(\n        \"At {}Mhz: Elapsed time to count to {}: {}ms\",\n        clk_sys_freq() / 1_000_000,\n        COUNT_TO,\n        time_elapsed\n    );\n\n    // Check if the elapsed time is within expected limits\n    // for rp2040 we expect about 600ms\n    #[cfg(feature = \"rp2040\")]\n    // allow 1% error\n    assert!(time_elapsed < 606, \"Elapsed time is too long\");\n    // for rp235x we expect about 450ms\n    #[cfg(feature = \"rp235xb\")]\n    assert!(time_elapsed < 455, \"Elapsed time is too long\");\n\n    cortex_m::asm::bkpt();\n}\n"
  },
  {
    "path": "tests/rp/src/bin/pio_irq.rs",
    "content": "#![no_std]\n#![no_main]\n#[cfg(feature = \"rp2040\")]\nteleprobe_meta::target!(b\"rpi-pico\");\n#[cfg(feature = \"rp235xb\")]\nteleprobe_meta::target!(b\"pimoroni-pico-plus-2\");\n\nuse defmt::info;\nuse embassy_executor::Spawner;\nuse embassy_rp::bind_interrupts;\nuse embassy_rp::peripherals::PIO0;\nuse embassy_rp::pio::program::pio_asm;\nuse embassy_rp::pio::{Config, InterruptHandler, Pio};\nuse {defmt_rtt as _, panic_probe as _};\n\nbind_interrupts!(struct Irqs {\n    PIO0_IRQ_0 => InterruptHandler<PIO0>;\n});\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let p = embassy_rp::init(Default::default());\n    let pio = p.PIO0;\n    let Pio {\n        mut common,\n        sm0: mut sm,\n        irq_flags,\n        ..\n    } = Pio::new(pio, Irqs);\n\n    let prg = pio_asm!(\n        \"irq set 0\",\n        \"irq wait 0\",\n        \"irq set 1\",\n        // pause execution here\n        \"irq wait 1\",\n    );\n\n    let mut cfg = Config::default();\n    cfg.use_program(&common.load_program(&prg.program), &[]);\n    sm.set_config(&cfg);\n    sm.set_enable(true);\n\n    // not using the wait futures on purpose because they clear the irq bits,\n    // and we want to see in which order they are set.\n    while !irq_flags.check(0) {}\n    cortex_m::asm::nop();\n    assert!(!irq_flags.check(1));\n    irq_flags.clear(0);\n    cortex_m::asm::nop();\n    assert!(irq_flags.check(1));\n\n    info!(\"Test OK\");\n    cortex_m::asm::bkpt();\n}\n"
  },
  {
    "path": "tests/rp/src/bin/pio_multi_load.rs",
    "content": "#![no_std]\n#![no_main]\n#[cfg(feature = \"rp2040\")]\nteleprobe_meta::target!(b\"rpi-pico\");\n#[cfg(feature = \"rp235xb\")]\nteleprobe_meta::target!(b\"pimoroni-pico-plus-2\");\n\nuse defmt::info;\nuse embassy_executor::Spawner;\nuse embassy_rp::bind_interrupts;\nuse embassy_rp::peripherals::PIO0;\nuse embassy_rp::pio::program::pio_asm;\nuse embassy_rp::pio::{Config, InterruptHandler, LoadError, Pio};\nuse {defmt_rtt as _, panic_probe as _};\n\nbind_interrupts!(struct Irqs {\n    PIO0_IRQ_0 => InterruptHandler<PIO0>;\n});\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let p = embassy_rp::init(Default::default());\n    let pio = p.PIO0;\n    let Pio {\n        mut common,\n        mut sm0,\n        mut sm1,\n        mut sm2,\n        irq_flags,\n        ..\n    } = Pio::new(pio, Irqs);\n\n    // load with explicit origin works\n    let prg1 = pio_asm!(\n        \".origin 4\"\n        \"nop\",\n        \"nop\",\n        \"nop\",\n        \"nop\",\n        \"nop\",\n        \"nop\",\n        \"nop\",\n        \"irq 0\",\n        \"nop\",\n        \"nop\",\n    );\n    let loaded1 = common.load_program(&prg1.program);\n    assert_eq!(loaded1.origin, 4);\n    assert_eq!(loaded1.wrap.source, 13);\n    assert_eq!(loaded1.wrap.target, 4);\n\n    // load without origin chooses a free space\n    let prg2 = pio_asm!(\"nop\", \"nop\", \"nop\", \"nop\", \"nop\", \"nop\", \"nop\", \"irq 1\", \"nop\", \"nop\",);\n    let loaded2 = common.load_program(&prg2.program);\n    assert_eq!(loaded2.origin, 14);\n    assert_eq!(loaded2.wrap.source, 23);\n    assert_eq!(loaded2.wrap.target, 14);\n\n    // wrapping around the end of program space automatically works\n    let prg3 = pio_asm!(\n        \"nop\", \"nop\", \"nop\", \"nop\", \"nop\", \"nop\", \"nop\", \"nop\", \"nop\", \"nop\", \"nop\", \"irq 2\",\n    );\n    let loaded3 = common.load_program(&prg3.program);\n    assert_eq!(loaded3.origin, 24);\n    assert_eq!(loaded3.wrap.source, 3);\n    assert_eq!(loaded3.wrap.target, 24);\n\n    // check that the programs actually work\n    {\n        let mut cfg = Config::default();\n        cfg.use_program(&loaded1, &[]);\n        sm0.set_config(&cfg);\n        sm0.set_enable(true);\n        while !irq_flags.check(0) {}\n        sm0.set_enable(false);\n    }\n    {\n        let mut cfg = Config::default();\n        cfg.use_program(&loaded2, &[]);\n        sm1.set_config(&cfg);\n        sm1.set_enable(true);\n        while !irq_flags.check(1) {}\n        sm1.set_enable(false);\n    }\n    {\n        let mut cfg = Config::default();\n        cfg.use_program(&loaded3, &[]);\n        sm2.set_config(&cfg);\n        sm2.set_enable(true);\n        while !irq_flags.check(2) {}\n        sm2.set_enable(false);\n    }\n\n    // instruction memory is full now. all loads should fail.\n    {\n        let prg = pio_asm!(\".origin 0\", \"nop\");\n        match common.try_load_program(&prg.program) {\n            Err(LoadError::AddressInUse(0)) => (),\n            _ => panic!(\"program loaded when it shouldn't\"),\n        };\n\n        let prg = pio_asm!(\"nop\");\n        match common.try_load_program(&prg.program) {\n            Err(LoadError::InsufficientSpace) => (),\n            _ => panic!(\"program loaded when it shouldn't\"),\n        };\n    }\n\n    // freeing some memory should allow further loads though.\n    unsafe {\n        common.free_instr(loaded3.used_memory);\n    }\n    {\n        let prg = pio_asm!(\".origin 0\", \"nop\");\n        match common.try_load_program(&prg.program) {\n            Ok(_) => (),\n            _ => panic!(\"program didn't loaded when it shouldn\"),\n        };\n\n        let prg = pio_asm!(\"nop\");\n        match common.try_load_program(&prg.program) {\n            Ok(_) => (),\n            _ => panic!(\"program didn't loaded when it shouldn\"),\n        };\n    }\n\n    info!(\"Test OK\");\n    cortex_m::asm::bkpt();\n}\n"
  },
  {
    "path": "tests/rp/src/bin/pwm.rs",
    "content": "#![no_std]\n#![no_main]\n#[cfg(feature = \"rp2040\")]\nteleprobe_meta::target!(b\"rpi-pico\");\n#[cfg(feature = \"rp235xb\")]\nteleprobe_meta::target!(b\"pimoroni-pico-plus-2\");\n\nuse defmt::{assert, assert_eq, assert_ne, *};\nuse embassy_executor::Spawner;\nuse embassy_rp::gpio::{Input, Pull};\n#[cfg(feature = \"rp2040\")]\nuse embassy_rp::gpio::{Level, Output};\nuse embassy_rp::pwm::{Config, InputMode, Pwm};\nuse embassy_time::Timer;\nuse {defmt_rtt as _, panic_probe as _};\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let mut p = embassy_rp::init(Default::default());\n    info!(\"Hello World!\");\n\n    // Connections on CI device: 6 -> 9, 7 -> 11\n    let (mut p6, mut p7, mut p9, mut p11) = (p.PIN_6, p.PIN_7, p.PIN_9, p.PIN_11);\n\n    let cfg = {\n        let mut c = Config::default();\n        c.divider = 125.into();\n        c.top = 10000;\n        c.compare_a = 5000;\n        c.compare_b = 5000;\n        c\n    };\n\n    // Test free-running clock\n    {\n        let pwm = Pwm::new_free(p.PWM_SLICE3.reborrow(), cfg.clone());\n        cortex_m::asm::delay(125);\n        let ctr = pwm.counter();\n        assert!(ctr > 0);\n        assert!(ctr < 100);\n        cortex_m::asm::delay(125);\n        assert!(ctr < pwm.counter());\n    }\n\n    for invert_a in [false, true] {\n        info!(\"free-running, invert A: {}\", invert_a);\n        let mut cfg = cfg.clone();\n        cfg.invert_a = invert_a;\n        cfg.invert_b = !invert_a;\n\n        // Test output from A\n        {\n            let pin1 = Input::new(p9.reborrow(), Pull::None);\n            let _pwm = Pwm::new_output_a(p.PWM_SLICE3.reborrow(), p6.reborrow(), cfg.clone());\n            Timer::after_millis(1).await;\n            assert_eq!(pin1.is_low(), invert_a);\n            Timer::after_millis(5).await;\n            assert_eq!(pin1.is_high(), invert_a);\n            Timer::after_millis(5).await;\n            assert_eq!(pin1.is_low(), invert_a);\n            Timer::after_millis(5).await;\n            assert_eq!(pin1.is_high(), invert_a);\n        }\n\n        // Test output from B\n        {\n            let pin2 = Input::new(p11.reborrow(), Pull::None);\n            let _pwm = Pwm::new_output_b(p.PWM_SLICE3.reborrow(), p7.reborrow(), cfg.clone());\n            Timer::after_millis(1).await;\n            assert_ne!(pin2.is_low(), invert_a);\n            Timer::after_millis(5).await;\n            assert_ne!(pin2.is_high(), invert_a);\n            Timer::after_millis(5).await;\n            assert_ne!(pin2.is_low(), invert_a);\n            Timer::after_millis(5).await;\n            assert_ne!(pin2.is_high(), invert_a);\n        }\n\n        // Test output from A+B\n        {\n            let pin1 = Input::new(p9.reborrow(), Pull::None);\n            let pin2 = Input::new(p11.reborrow(), Pull::None);\n            let _pwm = Pwm::new_output_ab(p.PWM_SLICE3.reborrow(), p6.reborrow(), p7.reborrow(), cfg.clone());\n            Timer::after_millis(1).await;\n            assert_eq!(pin1.is_low(), invert_a);\n            assert_ne!(pin2.is_low(), invert_a);\n            Timer::after_millis(5).await;\n            assert_eq!(pin1.is_high(), invert_a);\n            assert_ne!(pin2.is_high(), invert_a);\n            Timer::after_millis(5).await;\n            assert_eq!(pin1.is_low(), invert_a);\n            assert_ne!(pin2.is_low(), invert_a);\n            Timer::after_millis(5).await;\n            assert_eq!(pin1.is_high(), invert_a);\n            assert_ne!(pin2.is_high(), invert_a);\n        }\n    }\n\n    // Test level-gated\n    #[cfg(feature = \"rp2040\")]\n    {\n        let mut pin2 = Output::new(p11.reborrow(), Level::Low);\n        let pwm = Pwm::new_input(\n            p.PWM_SLICE3.reborrow(),\n            p7.reborrow(),\n            Pull::None,\n            InputMode::Level,\n            cfg.clone(),\n        );\n        assert_eq!(pwm.counter(), 0);\n        Timer::after_millis(5).await;\n        assert_eq!(pwm.counter(), 0);\n        pin2.set_high();\n        Timer::after_millis(1).await;\n        pin2.set_low();\n        let ctr = pwm.counter();\n        info!(\"ctr: {}\", ctr);\n        assert!(ctr >= 1000);\n        Timer::after_millis(1).await;\n        assert_eq!(pwm.counter(), ctr);\n    }\n\n    // Test rising-gated\n    #[cfg(feature = \"rp2040\")]\n    {\n        let mut pin2 = Output::new(p11.reborrow(), Level::Low);\n        let pwm = Pwm::new_input(\n            p.PWM_SLICE3.reborrow(),\n            p7.reborrow(),\n            Pull::None,\n            InputMode::RisingEdge,\n            cfg.clone(),\n        );\n        assert_eq!(pwm.counter(), 0);\n        Timer::after_millis(5).await;\n        assert_eq!(pwm.counter(), 0);\n        pin2.set_high();\n        Timer::after_millis(1).await;\n        pin2.set_low();\n        assert_eq!(pwm.counter(), 1);\n        Timer::after_millis(1).await;\n        assert_eq!(pwm.counter(), 1);\n    }\n\n    // Test falling-gated\n    #[cfg(feature = \"rp2040\")]\n    {\n        let mut pin2 = Output::new(p11.reborrow(), Level::High);\n        let pwm = Pwm::new_input(\n            p.PWM_SLICE3.reborrow(),\n            p7.reborrow(),\n            Pull::None,\n            InputMode::FallingEdge,\n            cfg.clone(),\n        );\n        assert_eq!(pwm.counter(), 0);\n        Timer::after_millis(5).await;\n        assert_eq!(pwm.counter(), 0);\n        pin2.set_low();\n        Timer::after_millis(1).await;\n        pin2.set_high();\n        assert_eq!(pwm.counter(), 1);\n        Timer::after_millis(1).await;\n        assert_eq!(pwm.counter(), 1);\n    }\n\n    // pull-down\n    {\n        let pin2 = Input::new(p11.reborrow(), Pull::None);\n        Pwm::new_input(\n            p.PWM_SLICE3.reborrow(),\n            p7.reborrow(),\n            Pull::Down,\n            InputMode::FallingEdge,\n            cfg.clone(),\n        );\n        Timer::after_millis(1).await;\n        assert!(pin2.is_low());\n    }\n\n    // pull-up\n    {\n        let pin2 = Input::new(p11.reborrow(), Pull::None);\n        Pwm::new_input(\n            p.PWM_SLICE3.reborrow(),\n            p7.reborrow(),\n            Pull::Up,\n            InputMode::FallingEdge,\n            cfg.clone(),\n        );\n        Timer::after_millis(1).await;\n        assert!(pin2.is_high());\n    }\n\n    info!(\"Test OK\");\n    cortex_m::asm::bkpt();\n}\n"
  },
  {
    "path": "tests/rp/src/bin/rtc.rs",
    "content": "#![no_std]\n#![no_main]\n#[cfg(feature = \"rp2040\")]\nteleprobe_meta::target!(b\"rpi-pico\");\n\nuse defmt::{assert, *};\nuse embassy_executor::Spawner;\nuse embassy_futures::select::{Either, select};\nuse embassy_rp::bind_interrupts;\nuse embassy_rp::rtc::{DateTime, DateTimeFilter, DayOfWeek, Rtc};\nuse embassy_time::{Duration, Instant, Timer};\nuse {defmt_rtt as _, panic_probe as _};\n\n// Bind the RTC interrupt to the handler\nbind_interrupts!(struct Irqs {\n    RTC_IRQ => embassy_rp::rtc::InterruptHandler;\n});\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let p = embassy_rp::init(Default::default());\n    let mut rtc = Rtc::new(p.RTC, Irqs);\n\n    info!(\"RTC test started\");\n\n    // Initialize RTC if not running\n    if !rtc.is_running() {\n        info!(\"Starting RTC\");\n        let now = DateTime {\n            year: 2000,\n            month: 1,\n            day: 1,\n            day_of_week: DayOfWeek::Saturday,\n            hour: 0,\n            minute: 0,\n            second: 0,\n        };\n        rtc.set_datetime(now).unwrap();\n        Timer::after_millis(100).await;\n    }\n\n    // Test 1: Basic RTC functionality - read current time\n    let initial_time = rtc.now().unwrap();\n    info!(\n        \"Initial time: {}-{:02}-{:02} {}:{:02}:{:02}\",\n        initial_time.year,\n        initial_time.month,\n        initial_time.day,\n        initial_time.hour,\n        initial_time.minute,\n        initial_time.second\n    );\n\n    // Test 2: Schedule and wait for alarm\n    info!(\"Testing alarm scheduling\");\n\n    // Wait until we're at a predictable second, then schedule for a future second\n    loop {\n        let current = rtc.now().unwrap();\n        if current.second <= 55 {\n            break;\n        }\n        Timer::after_millis(100).await;\n    }\n\n    // Now schedule alarm for 3 seconds from current time\n    let current_time = rtc.now().unwrap();\n    let alarm_second = (current_time.second + 3) % 60;\n    let alarm_filter = DateTimeFilter::default().second(alarm_second);\n\n    info!(\"Scheduling alarm for second: {}\", alarm_second);\n    rtc.schedule_alarm(alarm_filter);\n\n    // Verify alarm is scheduled\n    let scheduled = rtc.alarm_scheduled();\n    assert!(scheduled.is_some(), \"Alarm should be scheduled\");\n    info!(\"Alarm scheduled successfully: {}\", scheduled.unwrap());\n\n    // Wait for alarm with timeout\n    let alarm_start = Instant::now();\n    match select(Timer::after_secs(5), rtc.wait_for_alarm()).await {\n        Either::First(_) => {\n            core::panic!(\"Alarm timeout - alarm should have triggered by now\");\n        }\n        Either::Second(_) => {\n            let alarm_duration = Instant::now() - alarm_start;\n            info!(\"ALARM TRIGGERED after {:?}\", alarm_duration);\n\n            // Verify timing is reasonable (should be around 3 seconds)\n            assert!(\n                alarm_duration >= Duration::from_secs(2) && alarm_duration <= Duration::from_secs(4),\n                \"Alarm timing incorrect: {:?}\",\n                alarm_duration\n            );\n        }\n    }\n\n    // Test 3: Verify RTC is still running and time has advanced\n    let final_time = rtc.now().unwrap();\n    info!(\n        \"Final time: {}-{:02}-{:02} {}:{:02}:{:02}\",\n        final_time.year, final_time.month, final_time.day, final_time.hour, final_time.minute, final_time.second\n    );\n\n    // Verify time has advanced (allowing for minute/hour rollover)\n    let time_diff = if final_time.second >= initial_time.second {\n        final_time.second - initial_time.second\n    } else {\n        60 - initial_time.second + final_time.second\n    };\n\n    assert!(time_diff >= 3, \"RTC should have advanced by at least 3 seconds\");\n    info!(\"Time advanced by {} seconds\", time_diff);\n\n    // Test 4: Verify alarm is no longer scheduled after triggering\n    let post_alarm_scheduled = rtc.alarm_scheduled();\n    assert!(\n        post_alarm_scheduled.is_none(),\n        \"Alarm should not be scheduled after triggering\"\n    );\n    info!(\"Alarm correctly cleared after triggering\");\n\n    info!(\"Test OK\");\n    cortex_m::asm::bkpt();\n}\n"
  },
  {
    "path": "tests/rp/src/bin/spi.rs",
    "content": "#![no_std]\n#![no_main]\n#[cfg(feature = \"rp2040\")]\nteleprobe_meta::target!(b\"rpi-pico\");\n#[cfg(feature = \"rp235xb\")]\nteleprobe_meta::target!(b\"pimoroni-pico-plus-2\");\n\nuse defmt::{assert_eq, *};\nuse embassy_executor::Spawner;\nuse embassy_rp::spi::{Config, Spi};\nuse {defmt_rtt as _, panic_probe as _};\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let p = embassy_rp::init(Default::default());\n    info!(\"Hello World!\");\n\n    let clk = p.PIN_2;\n    let mosi = p.PIN_3;\n    let miso = p.PIN_4;\n\n    let mut spi = Spi::new_blocking(p.SPI0, clk, mosi, miso, Config::default());\n\n    let tx_buf = [1_u8, 2, 3, 4, 5, 6];\n    let mut rx_buf = [0_u8; 6];\n    spi.blocking_transfer(&mut rx_buf, &tx_buf).unwrap();\n    assert_eq!(rx_buf, tx_buf);\n\n    info!(\"Test OK\");\n    cortex_m::asm::bkpt();\n}\n"
  },
  {
    "path": "tests/rp/src/bin/spi_async.rs",
    "content": "//! Make sure to connect GPIO pins 3 (`PIN_3`) and 4 (`PIN_4`) together\n//! to run this test.\n//!\n#![no_std]\n#![no_main]\n#[cfg(feature = \"rp2040\")]\nteleprobe_meta::target!(b\"rpi-pico\");\n#[cfg(feature = \"rp235xb\")]\nteleprobe_meta::target!(b\"pimoroni-pico-plus-2\");\n\nuse defmt::{assert_eq, *};\nuse embassy_executor::Spawner;\nuse embassy_rp::peripherals::{DMA_CH0, DMA_CH1};\nuse embassy_rp::spi::{Config, Spi};\nuse embassy_rp::{bind_interrupts, dma};\nuse {defmt_rtt as _, panic_probe as _};\n\nbind_interrupts!(struct Irqs {\n    DMA_IRQ_0 => dma::InterruptHandler<DMA_CH0>, dma::InterruptHandler<DMA_CH1>;\n});\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let p = embassy_rp::init(Default::default());\n    info!(\"Hello World!\");\n\n    let clk = p.PIN_2;\n    let mosi = p.PIN_3;\n    let miso = p.PIN_4;\n\n    let mut spi = Spi::new(p.SPI0, clk, mosi, miso, p.DMA_CH0, p.DMA_CH1, Irqs, Config::default());\n\n    // equal rx & tx buffers\n    {\n        let tx_buf = [1_u8, 2, 3, 4, 5, 6];\n        let mut rx_buf = [0_u8; 6];\n        spi.transfer(&mut rx_buf, &tx_buf).await.unwrap();\n        assert_eq!(rx_buf, tx_buf);\n    }\n\n    // tx > rx buffer\n    {\n        let tx_buf = [7_u8, 8, 9, 10, 11, 12];\n\n        let mut rx_buf = [0_u8; 3];\n        spi.transfer(&mut rx_buf, &tx_buf).await.unwrap();\n        assert_eq!(rx_buf, tx_buf[..3]);\n\n        defmt::info!(\"tx > rx buffer - OK\");\n    }\n\n    // we make sure to that clearing FIFO works after the uneven buffers\n\n    // equal rx & tx buffers\n    {\n        let tx_buf = [13_u8, 14, 15, 16, 17, 18];\n        let mut rx_buf = [0_u8; 6];\n        spi.transfer(&mut rx_buf, &tx_buf).await.unwrap();\n        assert_eq!(rx_buf, tx_buf);\n\n        defmt::info!(\"buffer rx length == tx length - OK\");\n    }\n\n    // rx > tx buffer\n    {\n        let tx_buf = [19_u8, 20, 21];\n        let mut rx_buf = [0_u8; 6];\n\n        // we should have written dummy data to tx buffer to sync clock.\n        spi.transfer(&mut rx_buf, &tx_buf).await.unwrap();\n\n        assert_eq!(\n            rx_buf[..3],\n            tx_buf,\n            \"only the first 3 TX bytes should have been received in the RX buffer\"\n        );\n        assert_eq!(rx_buf[3..], [0, 0, 0], \"the rest of the RX bytes should be empty\");\n        defmt::info!(\"buffer rx length > tx length - OK\");\n    }\n\n    // equal rx & tx buffers\n    {\n        let tx_buf = [22_u8, 23, 24, 25, 26, 27];\n        let mut rx_buf = [0_u8; 6];\n        spi.transfer(&mut rx_buf, &tx_buf).await.unwrap();\n\n        assert_eq!(rx_buf, tx_buf);\n        defmt::info!(\"buffer rx length = tx length - OK\");\n    }\n\n    info!(\"Test OK\");\n    cortex_m::asm::bkpt();\n}\n"
  },
  {
    "path": "tests/rp/src/bin/spinlock_mutex_multicore.rs",
    "content": "#![no_std]\n#![no_main]\n#[cfg(feature = \"rp2040\")]\nteleprobe_meta::target!(b\"rpi-pico\");\n#[cfg(feature = \"rp235xb\")]\nteleprobe_meta::target!(b\"pimoroni-pico-plus-2\");\n\nuse defmt::{info, unwrap};\nuse embassy_executor::Executor;\nuse embassy_rp::multicore::{Stack, spawn_core1};\nuse embassy_rp::spinlock_mutex::SpinlockRawMutex;\nuse embassy_sync::channel::Channel;\nuse static_cell::StaticCell;\nuse {defmt_rtt as _, panic_probe as _};\n\nstatic mut CORE1_STACK: Stack<1024> = Stack::new();\nstatic EXECUTOR0: StaticCell<Executor> = StaticCell::new();\nstatic EXECUTOR1: StaticCell<Executor> = StaticCell::new();\nstatic CHANNEL0: Channel<SpinlockRawMutex<0>, bool, 1> = Channel::new();\nstatic CHANNEL1: Channel<SpinlockRawMutex<1>, bool, 1> = Channel::new();\n\n#[cortex_m_rt::entry]\nfn main() -> ! {\n    let p = embassy_rp::init(Default::default());\n    spawn_core1(\n        p.CORE1,\n        unsafe { &mut *core::ptr::addr_of_mut!(CORE1_STACK) },\n        move || {\n            let executor1 = EXECUTOR1.init(Executor::new());\n            executor1.run(|spawner| spawner.spawn(unwrap!(core1_task())));\n        },\n    );\n    let executor0 = EXECUTOR0.init(Executor::new());\n    executor0.run(|spawner| spawner.spawn(unwrap!(core0_task())));\n}\n\n#[embassy_executor::task]\nasync fn core0_task() {\n    info!(\"CORE0 is running\");\n    let ping = true;\n    CHANNEL0.send(ping).await;\n    let pong = CHANNEL1.receive().await;\n    assert_eq!(ping, pong);\n\n    info!(\"Test OK\");\n    cortex_m::asm::bkpt();\n}\n\n#[embassy_executor::task]\nasync fn core1_task() {\n    info!(\"CORE1 is running\");\n    let ping = CHANNEL0.receive().await;\n    CHANNEL1.send(ping).await;\n}\n"
  },
  {
    "path": "tests/rp/src/bin/timer.rs",
    "content": "#![no_std]\n#![no_main]\n#[cfg(feature = \"rp2040\")]\nteleprobe_meta::target!(b\"rpi-pico\");\n#[cfg(feature = \"rp235xb\")]\nteleprobe_meta::target!(b\"pimoroni-pico-plus-2\");\n\nuse defmt::{assert, *};\nuse embassy_executor::Spawner;\nuse embassy_time::{Instant, Timer};\nuse {defmt_rtt as _, panic_probe as _};\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let _p = embassy_rp::init(Default::default());\n    info!(\"Hello World!\");\n\n    let start = Instant::now();\n    Timer::after_millis(100).await;\n    let end = Instant::now();\n    let ms = (end - start).as_millis();\n    info!(\"slept for {} ms\", ms);\n    assert!(ms >= 99);\n    assert!(ms < 110);\n\n    info!(\"Test OK\");\n    cortex_m::asm::bkpt();\n}\n"
  },
  {
    "path": "tests/rp/src/bin/uart.rs",
    "content": "#![no_std]\n#![no_main]\n#[cfg(feature = \"rp2040\")]\nteleprobe_meta::target!(b\"rpi-pico\");\n#[cfg(feature = \"rp235xb\")]\nteleprobe_meta::target!(b\"pimoroni-pico-plus-2\");\n\nuse defmt::{assert_eq, *};\nuse embassy_executor::Spawner;\nuse embassy_rp::gpio::{Level, Output};\nuse embassy_rp::uart::{Blocking, Config, Error, Parity, Uart, UartRx};\nuse embassy_time::Timer;\nuse {defmt_rtt as _, panic_probe as _};\n\nfn read<const N: usize>(uart: &mut Uart<'_, Blocking>) -> Result<[u8; N], Error> {\n    let mut buf = [255; N];\n    uart.blocking_read(&mut buf)?;\n    Ok(buf)\n}\n\nfn read1<const N: usize>(uart: &mut UartRx<'_, Blocking>) -> Result<[u8; N], Error> {\n    let mut buf = [255; N];\n    uart.blocking_read(&mut buf)?;\n    Ok(buf)\n}\n\nasync fn send(pin: &mut Output<'_>, v: u8, parity: Option<bool>) {\n    pin.set_low();\n    Timer::after_millis(1).await;\n    for i in 0..8 {\n        if v & (1 << i) == 0 {\n            pin.set_low();\n        } else {\n            pin.set_high();\n        }\n        Timer::after_millis(1).await;\n    }\n    if let Some(b) = parity {\n        if b {\n            pin.set_high();\n        } else {\n            pin.set_low();\n        }\n        Timer::after_millis(1).await;\n    }\n    pin.set_high();\n    Timer::after_millis(1).await;\n}\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let p = embassy_rp::init(Default::default());\n    info!(\"Hello World!\");\n\n    let (mut tx, mut rx, mut uart) = (p.PIN_0, p.PIN_1, p.UART0);\n\n    {\n        let config = Config::default();\n        let mut uart = Uart::new_blocking(uart.reborrow(), tx.reborrow(), rx.reborrow(), config);\n\n        // We can't send too many bytes, they have to fit in the FIFO.\n        // This is because we aren't sending+receiving at the same time.\n\n        let data = [0xC0, 0xDE];\n        uart.blocking_write(&data).unwrap();\n        assert_eq!(read(&mut uart).unwrap(), data);\n    }\n\n    info!(\"test overflow detection\");\n    {\n        let config = Config::default();\n        let mut uart = Uart::new_blocking(uart.reborrow(), tx.reborrow(), rx.reborrow(), config);\n\n        let data = [\n            1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29,\n            30, 31, 32,\n        ];\n        let overflow = [\n            101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116,\n        ];\n        uart.blocking_write(&data).unwrap();\n        uart.blocking_write(&overflow).unwrap();\n        while uart.busy() {}\n\n        // prefix in fifo is valid\n        assert_eq!(read(&mut uart).unwrap(), data);\n        // next received character causes overrun error and is discarded\n        uart.blocking_write(&[1, 2, 3]).unwrap();\n        assert_eq!(read::<1>(&mut uart).unwrap_err(), Error::Overrun);\n        assert_eq!(read(&mut uart).unwrap(), [2, 3]);\n    }\n\n    info!(\"test break detection\");\n    {\n        let config = Config::default();\n        let mut uart = Uart::new_blocking(uart.reborrow(), tx.reborrow(), rx.reborrow(), config);\n\n        // break on empty fifo\n        uart.send_break(20).await;\n        uart.blocking_write(&[64]).unwrap();\n        assert_eq!(read::<1>(&mut uart).unwrap_err(), Error::Break);\n        assert_eq!(read(&mut uart).unwrap(), [64]);\n\n        // break on partially filled fifo\n        uart.blocking_write(&[65; 2]).unwrap();\n        uart.send_break(20).await;\n        uart.blocking_write(&[66]).unwrap();\n        assert_eq!(read(&mut uart).unwrap(), [65; 2]);\n        assert_eq!(read::<1>(&mut uart).unwrap_err(), Error::Break);\n        assert_eq!(read(&mut uart).unwrap(), [66]);\n    }\n\n    // parity detection. here we bitbang to not require two uarts.\n    info!(\"test parity error detection\");\n    {\n        let mut pin = Output::new(tx.reborrow(), Level::High);\n        let mut config = Config::default();\n        config.baudrate = 1000;\n        config.parity = Parity::ParityEven;\n        let mut uart = UartRx::new_blocking(uart.reborrow(), rx.reborrow(), config);\n\n        async fn chr(pin: &mut Output<'_>, v: u8, parity: u8) {\n            send(pin, v, Some(parity != 0)).await;\n        }\n\n        // first check that we can send correctly\n        chr(&mut pin, 64, 1).await;\n        assert_eq!(read1(&mut uart).unwrap(), [64]);\n\n        // all good, check real errors\n        chr(&mut pin, 2, 1).await;\n        chr(&mut pin, 3, 0).await;\n        chr(&mut pin, 4, 0).await;\n        chr(&mut pin, 5, 0).await;\n        assert_eq!(read1(&mut uart).unwrap(), [2, 3]);\n        assert_eq!(read1::<1>(&mut uart).unwrap_err(), Error::Parity);\n        assert_eq!(read1(&mut uart).unwrap(), [5]);\n    }\n\n    // framing error detection. here we bitbang because there's no other way.\n    info!(\"test framing error detection\");\n    {\n        let mut pin = Output::new(tx.reborrow(), Level::High);\n        let mut config = Config::default();\n        config.baudrate = 1000;\n        let mut uart = UartRx::new_blocking(uart.reborrow(), rx.reborrow(), config);\n\n        async fn chr(pin: &mut Output<'_>, v: u8, good: bool) {\n            if good {\n                send(pin, v, None).await;\n            } else {\n                send(pin, v, Some(false)).await;\n            }\n        }\n\n        // first check that we can send correctly\n        chr(&mut pin, 64, true).await;\n        assert_eq!(read1(&mut uart).unwrap(), [64]);\n\n        // all good, check real errors\n        chr(&mut pin, 2, true).await;\n        chr(&mut pin, 3, true).await;\n        chr(&mut pin, 4, false).await;\n        chr(&mut pin, 5, true).await;\n        assert_eq!(read1(&mut uart).unwrap(), [2, 3]);\n        assert_eq!(read1::<1>(&mut uart).unwrap_err(), Error::Framing);\n        assert_eq!(read1(&mut uart).unwrap(), [5]);\n    }\n\n    info!(\"Test OK\");\n    cortex_m::asm::bkpt();\n}\n"
  },
  {
    "path": "tests/rp/src/bin/uart_buffered.rs",
    "content": "#![no_std]\n#![no_main]\n#[cfg(feature = \"rp2040\")]\nteleprobe_meta::target!(b\"rpi-pico\");\n#[cfg(feature = \"rp235xb\")]\nteleprobe_meta::target!(b\"pimoroni-pico-plus-2\");\n\nuse defmt::{assert_eq, panic, *};\nuse embassy_executor::Spawner;\nuse embassy_rp::bind_interrupts;\nuse embassy_rp::gpio::{Level, Output};\nuse embassy_rp::peripherals::UART0;\nuse embassy_rp::uart::{BufferedInterruptHandler, BufferedUart, BufferedUartRx, Config, Error, Parity};\nuse embassy_time::Timer;\nuse embedded_io_async::{Read, ReadExactError, Write};\nuse {defmt_rtt as _, panic_probe as _};\n\nbind_interrupts!(struct Irqs {\n    UART0_IRQ => BufferedInterruptHandler<UART0>;\n});\n\nasync fn read<const N: usize>(uart: &mut BufferedUart) -> Result<[u8; N], Error> {\n    let mut buf = [255; N];\n    match uart.read_exact(&mut buf).await {\n        Ok(()) => Ok(buf),\n        // we should not ever produce an Eof condition\n        Err(ReadExactError::UnexpectedEof) => panic!(),\n        Err(ReadExactError::Other(e)) => Err(e),\n    }\n}\n\nasync fn read1<const N: usize>(uart: &mut BufferedUartRx) -> Result<[u8; N], Error> {\n    let mut buf = [255; N];\n    match uart.read_exact(&mut buf).await {\n        Ok(()) => Ok(buf),\n        // we should not ever produce an Eof condition\n        Err(ReadExactError::UnexpectedEof) => panic!(),\n        Err(ReadExactError::Other(e)) => Err(e),\n    }\n}\n\nasync fn send(pin: &mut Output<'_>, v: u8, parity: Option<bool>) {\n    pin.set_low();\n    Timer::after_millis(1).await;\n    for i in 0..8 {\n        if v & (1 << i) == 0 {\n            pin.set_low();\n        } else {\n            pin.set_high();\n        }\n        Timer::after_millis(1).await;\n    }\n    if let Some(b) = parity {\n        if b {\n            pin.set_high();\n        } else {\n            pin.set_low();\n        }\n        Timer::after_millis(1).await;\n    }\n    pin.set_high();\n    Timer::after_millis(1).await;\n}\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let p = embassy_rp::init(Default::default());\n    info!(\"Hello World!\");\n\n    let (mut tx, mut rx, mut uart) = (p.PIN_0, p.PIN_1, p.UART0);\n\n    {\n        let config = Config::default();\n        let tx_buf = &mut [0u8; 16];\n        let rx_buf = &mut [0u8; 16];\n        let mut uart = BufferedUart::new(\n            uart.reborrow(),\n            tx.reborrow(),\n            rx.reborrow(),\n            Irqs,\n            tx_buf,\n            rx_buf,\n            config,\n        );\n\n        // Make sure we send more bytes than fits in the FIFO, to test the actual\n        // bufferedUart.\n\n        let data = [\n            1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29,\n            30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48,\n        ];\n        uart.write_all(&data).await.unwrap();\n        info!(\"Done writing\");\n\n        assert_eq!(read(&mut uart).await.unwrap(), data);\n    }\n\n    info!(\"test overflow detection\");\n    {\n        let config = Config::default();\n        let tx_buf = &mut [0u8; 16];\n        let rx_buf = &mut [0u8; 16];\n        let mut uart = BufferedUart::new(\n            uart.reborrow(),\n            tx.reborrow(),\n            rx.reborrow(),\n            Irqs,\n            tx_buf,\n            rx_buf,\n            config,\n        );\n\n        // Make sure we send more bytes than fits in the FIFO, to test the actual\n        // bufferedUart.\n\n        let data = [\n            1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29,\n            30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48,\n        ];\n        let overflow = [\n            101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116,\n        ];\n        // give each block time to settle into the fifo. we want the overrun to occur at a well-defined point.\n        uart.write_all(&data).await.unwrap();\n        uart.blocking_flush().unwrap();\n        while uart.busy() {}\n        uart.write_all(&overflow).await.unwrap();\n        uart.blocking_flush().unwrap();\n        while uart.busy() {}\n\n        // already buffered/fifod prefix is valid\n        assert_eq!(read(&mut uart).await.unwrap(), data);\n        // next received character causes overrun error and is discarded\n        uart.write_all(&[1, 2, 3]).await.unwrap();\n        uart.blocking_flush().unwrap();\n        assert_eq!(read::<1>(&mut uart).await.unwrap_err(), Error::Overrun);\n        assert_eq!(read(&mut uart).await.unwrap(), [2, 3]);\n    }\n\n    info!(\"test break detection\");\n    {\n        let mut config = Config::default();\n        config.baudrate = 1000;\n        let tx_buf = &mut [0u8; 16];\n        let rx_buf = &mut [0u8; 16];\n        let mut uart = BufferedUart::new(\n            uart.reborrow(),\n            tx.reborrow(),\n            rx.reborrow(),\n            Irqs,\n            tx_buf,\n            rx_buf,\n            config,\n        );\n\n        // break on empty buffer\n        uart.send_break(20).await;\n        assert_eq!(read::<1>(&mut uart).await.unwrap_err(), Error::Break);\n        uart.write_all(&[64]).await.unwrap();\n        assert_eq!(read(&mut uart).await.unwrap(), [64]);\n\n        // break on partially filled buffer\n        uart.write_all(&[65; 2]).await.unwrap();\n        uart.send_break(20).await;\n        uart.write_all(&[66]).await.unwrap();\n        assert_eq!(read(&mut uart).await.unwrap(), [65; 2]);\n        assert_eq!(read::<1>(&mut uart).await.unwrap_err(), Error::Break);\n        assert_eq!(read(&mut uart).await.unwrap(), [66]);\n\n        // break on full buffer\n        uart.write_all(&[64; 16]).await.unwrap();\n        uart.send_break(20).await;\n        uart.write_all(&[65]).await.unwrap();\n        assert_eq!(read(&mut uart).await.unwrap(), [64; 16]);\n        assert_eq!(read::<1>(&mut uart).await.unwrap_err(), Error::Break);\n        assert_eq!(read(&mut uart).await.unwrap(), [65]);\n    }\n\n    // parity detection. here we bitbang to not require two uarts.\n    info!(\"test parity error detection\");\n    {\n        let mut pin = Output::new(tx.reborrow(), Level::High);\n        // choose a very slow baud rate to make tests reliable even with O0\n        let mut config = Config::default();\n        config.baudrate = 1000;\n        config.parity = Parity::ParityEven;\n        let rx_buf = &mut [0u8; 16];\n        let mut uart = BufferedUartRx::new(uart.reborrow(), Irqs, rx.reborrow(), rx_buf, config);\n\n        async fn chr(pin: &mut Output<'_>, v: u8, parity: u32) {\n            send(pin, v, Some(parity != 0)).await;\n        }\n\n        // first check that we can send correctly\n        chr(&mut pin, 64, 1).await;\n        assert_eq!(read1(&mut uart).await.unwrap(), [64]);\n\n        // parity on empty buffer\n        chr(&mut pin, 64, 0).await;\n        chr(&mut pin, 4, 1).await;\n        assert_eq!(read1::<1>(&mut uart).await.unwrap_err(), Error::Parity);\n        assert_eq!(read1(&mut uart).await.unwrap(), [4]);\n\n        // parity on partially filled buffer\n        chr(&mut pin, 64, 1).await;\n        chr(&mut pin, 32, 1).await;\n        chr(&mut pin, 64, 0).await;\n        chr(&mut pin, 65, 0).await;\n        assert_eq!(read1(&mut uart).await.unwrap(), [64, 32]);\n        assert_eq!(read1::<1>(&mut uart).await.unwrap_err(), Error::Parity);\n        assert_eq!(read1(&mut uart).await.unwrap(), [65]);\n\n        // parity on full buffer\n        for i in 0..16 {\n            chr(&mut pin, i, i.count_ones() % 2).await;\n        }\n        chr(&mut pin, 64, 0).await;\n        chr(&mut pin, 65, 0).await;\n        assert_eq!(\n            read1(&mut uart).await.unwrap(),\n            [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]\n        );\n        assert_eq!(read1::<1>(&mut uart).await.unwrap_err(), Error::Parity);\n        assert_eq!(read1(&mut uart).await.unwrap(), [65]);\n    }\n\n    // framing error detection. here we bitbang because there's no other way.\n    info!(\"test framing error detection\");\n    {\n        let mut pin = Output::new(tx.reborrow(), Level::High);\n        // choose a very slow baud rate to make tests reliable even with O0\n        let mut config = Config::default();\n        config.baudrate = 1000;\n        let rx_buf = &mut [0u8; 16];\n        let mut uart = BufferedUartRx::new(uart.reborrow(), Irqs, rx.reborrow(), rx_buf, config);\n\n        async fn chr(pin: &mut Output<'_>, v: u8, good: bool) {\n            if good {\n                send(pin, v, None).await;\n            } else {\n                send(pin, v, Some(false)).await;\n            }\n        }\n\n        // first check that we can send correctly\n        chr(&mut pin, 64, true).await;\n        assert_eq!(read1(&mut uart).await.unwrap(), [64]);\n\n        // framing on empty buffer\n        chr(&mut pin, 64, false).await;\n        assert_eq!(read1::<1>(&mut uart).await.unwrap_err(), Error::Framing);\n        chr(&mut pin, 65, true).await;\n        assert_eq!(read1(&mut uart).await.unwrap(), [65]);\n\n        // framing on partially filled buffer\n        chr(&mut pin, 64, true).await;\n        chr(&mut pin, 32, true).await;\n        chr(&mut pin, 64, false).await;\n        chr(&mut pin, 65, true).await;\n        assert_eq!(read1(&mut uart).await.unwrap(), [64, 32]);\n        assert_eq!(read1::<1>(&mut uart).await.unwrap_err(), Error::Framing);\n        assert_eq!(read1(&mut uart).await.unwrap(), [65]);\n\n        // framing on full buffer\n        for i in 0..16 {\n            chr(&mut pin, i, true).await;\n        }\n        chr(&mut pin, 64, false).await;\n        chr(&mut pin, 65, true).await;\n        assert_eq!(\n            read1(&mut uart).await.unwrap(),\n            [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]\n        );\n        assert_eq!(read1::<1>(&mut uart).await.unwrap_err(), Error::Framing);\n        assert_eq!(read1(&mut uart).await.unwrap(), [65]);\n    }\n\n    info!(\"Test OK\");\n    cortex_m::asm::bkpt();\n}\n"
  },
  {
    "path": "tests/rp/src/bin/uart_dma.rs",
    "content": "#![no_std]\n#![no_main]\n#[cfg(feature = \"rp2040\")]\nteleprobe_meta::target!(b\"rpi-pico\");\n#[cfg(feature = \"rp235xb\")]\nteleprobe_meta::target!(b\"pimoroni-pico-plus-2\");\n\nuse defmt::{assert_eq, *};\nuse embassy_executor::Spawner;\nuse embassy_rp::gpio::{Level, Output};\nuse embassy_rp::peripherals::{DMA_CH0, DMA_CH1, UART0};\nuse embassy_rp::uart::{Async, Config, Error, InterruptHandler, Parity, Uart, UartRx};\nuse embassy_rp::{bind_interrupts, dma};\nuse embassy_time::Timer;\nuse {defmt_rtt as _, panic_probe as _};\n\nbind_interrupts!(struct Irqs {\n    UART0_IRQ => InterruptHandler<UART0>;\n    DMA_IRQ_0 => dma::InterruptHandler<DMA_CH0>, dma::InterruptHandler<DMA_CH1>;\n});\n\nasync fn read<const N: usize>(uart: &mut Uart<'_, Async>) -> Result<[u8; N], Error> {\n    let mut buf = [255; N];\n    uart.read(&mut buf).await?;\n    Ok(buf)\n}\n\nasync fn read1<const N: usize>(uart: &mut UartRx<'_, Async>) -> Result<[u8; N], Error> {\n    let mut buf = [255; N];\n    uart.read(&mut buf).await?;\n    Ok(buf)\n}\n\nasync fn send(pin: &mut Output<'_>, v: u8, parity: Option<bool>) {\n    pin.set_low();\n    Timer::after_millis(1).await;\n    for i in 0..8 {\n        if v & (1 << i) == 0 {\n            pin.set_low();\n        } else {\n            pin.set_high();\n        }\n        Timer::after_millis(1).await;\n    }\n    if let Some(b) = parity {\n        if b {\n            pin.set_high();\n        } else {\n            pin.set_low();\n        }\n        Timer::after_millis(1).await;\n    }\n    pin.set_high();\n    Timer::after_millis(1).await;\n}\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let mut p = embassy_rp::init(Default::default());\n    info!(\"Hello World!\");\n\n    let (mut tx, mut rx, mut uart) = (p.PIN_0, p.PIN_1, p.UART0);\n\n    // We can't send too many bytes, they have to fit in the FIFO.\n    // This is because we aren't sending+receiving at the same time.\n    {\n        let config = Config::default();\n        let mut uart = Uart::new(\n            uart.reborrow(),\n            tx.reborrow(),\n            rx.reborrow(),\n            Irqs,\n            p.DMA_CH0.reborrow(),\n            p.DMA_CH1.reborrow(),\n            config,\n        );\n\n        let data = [0xC0, 0xDE];\n        uart.write(&data).await.unwrap();\n\n        let mut buf = [0; 2];\n        uart.read(&mut buf).await.unwrap();\n        assert_eq!(buf, data);\n    }\n\n    info!(\"test overflow detection\");\n    {\n        let config = Config::default();\n        let mut uart = Uart::new(\n            uart.reborrow(),\n            tx.reborrow(),\n            rx.reborrow(),\n            Irqs,\n            p.DMA_CH0.reborrow(),\n            p.DMA_CH1.reborrow(),\n            config,\n        );\n\n        uart.blocking_write(&[42; 32]).unwrap();\n        uart.blocking_write(&[1, 2, 3]).unwrap();\n        uart.blocking_flush().unwrap();\n\n        // can receive regular fifo contents\n        assert_eq!(read(&mut uart).await, Ok([42; 16]));\n        assert_eq!(read(&mut uart).await, Ok([42; 16]));\n        // receiving the rest fails with overrun\n        assert_eq!(read::<16>(&mut uart).await, Err(Error::Overrun));\n        // new data is accepted, latest overrunning byte first\n        assert_eq!(read(&mut uart).await, Ok([3]));\n        uart.blocking_write(&[8, 9]).unwrap();\n        Timer::after_millis(1).await;\n        assert_eq!(read(&mut uart).await, Ok([8, 9]));\n    }\n\n    info!(\"test break detection\");\n    {\n        let config = Config::default();\n        let (mut tx, mut rx) = Uart::new(\n            uart.reborrow(),\n            tx.reborrow(),\n            rx.reborrow(),\n            Irqs,\n            p.DMA_CH0.reborrow(),\n            p.DMA_CH1.reborrow(),\n            config,\n        )\n        .split();\n\n        // break before read\n        tx.send_break(20).await;\n        tx.write(&[64]).await.unwrap();\n        assert_eq!(read1::<1>(&mut rx).await.unwrap_err(), Error::Break);\n        assert_eq!(read1(&mut rx).await.unwrap(), [64]);\n\n        // break during read\n        {\n            let r = read1::<2>(&mut rx);\n            tx.write(&[2]).await.unwrap();\n            tx.send_break(20).await;\n            tx.write(&[3]).await.unwrap();\n            assert_eq!(r.await.unwrap_err(), Error::Break);\n            assert_eq!(read1(&mut rx).await.unwrap(), [3]);\n        }\n\n        // break after read\n        {\n            let r = read1(&mut rx);\n            tx.write(&[2]).await.unwrap();\n            tx.send_break(20).await;\n            tx.write(&[3]).await.unwrap();\n            assert_eq!(r.await.unwrap(), [2]);\n            assert_eq!(read1::<1>(&mut rx).await.unwrap_err(), Error::Break);\n            assert_eq!(read1(&mut rx).await.unwrap(), [3]);\n        }\n    }\n\n    // parity detection. here we bitbang to not require two uarts.\n    info!(\"test parity error detection\");\n    {\n        let mut pin = Output::new(tx.reborrow(), Level::High);\n        // choose a very slow baud rate to make tests reliable even with O0\n        let mut config = Config::default();\n        config.baudrate = 1000;\n        config.parity = Parity::ParityEven;\n        let mut uart = UartRx::new(uart.reborrow(), rx.reborrow(), Irqs, p.DMA_CH0.reborrow(), config);\n\n        async fn chr(pin: &mut Output<'_>, v: u8, parity: u32) {\n            send(pin, v, Some(parity != 0)).await;\n        }\n\n        // first check that we can send correctly\n        chr(&mut pin, 32, 1).await;\n        assert_eq!(read1(&mut uart).await.unwrap(), [32]);\n\n        // parity error before read\n        chr(&mut pin, 32, 0).await;\n        chr(&mut pin, 31, 1).await;\n        assert_eq!(read1::<1>(&mut uart).await.unwrap_err(), Error::Parity);\n        assert_eq!(read1(&mut uart).await.unwrap(), [31]);\n\n        // parity error during read\n        {\n            let r = read1::<2>(&mut uart);\n            chr(&mut pin, 2, 1).await;\n            chr(&mut pin, 32, 0).await;\n            chr(&mut pin, 3, 0).await;\n            assert_eq!(r.await.unwrap_err(), Error::Parity);\n            assert_eq!(read1(&mut uart).await.unwrap(), [3]);\n        }\n\n        // parity error after read\n        {\n            let r = read1(&mut uart);\n            chr(&mut pin, 2, 1).await;\n            chr(&mut pin, 32, 0).await;\n            chr(&mut pin, 3, 0).await;\n            assert_eq!(r.await.unwrap(), [2]);\n            assert_eq!(read1::<1>(&mut uart).await.unwrap_err(), Error::Parity);\n            assert_eq!(read1(&mut uart).await.unwrap(), [3]);\n        }\n    }\n\n    // framing error detection. here we bitbang because there's no other way.\n    info!(\"test framing error detection\");\n    {\n        let mut pin = Output::new(tx.reborrow(), Level::High);\n        // choose a very slow baud rate to make tests reliable even with O0\n        let mut config = Config::default();\n        config.baudrate = 1000;\n        let mut uart = UartRx::new(uart.reborrow(), rx.reborrow(), Irqs, p.DMA_CH0.reborrow(), config);\n\n        async fn chr(pin: &mut Output<'_>, v: u8, good: bool) {\n            if good {\n                send(pin, v, None).await;\n            } else {\n                send(pin, v, Some(false)).await;\n            }\n        }\n\n        // first check that we can send correctly\n        chr(&mut pin, 32, true).await;\n        assert_eq!(read1(&mut uart).await.unwrap(), [32]);\n\n        // parity error before read\n        chr(&mut pin, 32, false).await;\n        chr(&mut pin, 31, true).await;\n        assert_eq!(read1::<1>(&mut uart).await.unwrap_err(), Error::Framing);\n        assert_eq!(read1(&mut uart).await.unwrap(), [31]);\n\n        // parity error during read\n        {\n            let r = read1::<2>(&mut uart);\n            chr(&mut pin, 2, true).await;\n            chr(&mut pin, 32, false).await;\n            chr(&mut pin, 3, true).await;\n            assert_eq!(r.await.unwrap_err(), Error::Framing);\n            assert_eq!(read1(&mut uart).await.unwrap(), [3]);\n        }\n\n        // parity error after read\n        {\n            let r = read1(&mut uart);\n            chr(&mut pin, 2, true).await;\n            chr(&mut pin, 32, false).await;\n            chr(&mut pin, 3, true).await;\n            assert_eq!(r.await.unwrap(), [2]);\n            assert_eq!(read1::<1>(&mut uart).await.unwrap_err(), Error::Framing);\n            assert_eq!(read1(&mut uart).await.unwrap(), [3]);\n        }\n    }\n\n    info!(\"Test OK\");\n    cortex_m::asm::bkpt();\n}\n"
  },
  {
    "path": "tests/rp/src/bin/uart_upgrade.rs",
    "content": "#![no_std]\n#![no_main]\n#[cfg(feature = \"rp2040\")]\nteleprobe_meta::target!(b\"rpi-pico\");\n#[cfg(feature = \"rp235xb\")]\nteleprobe_meta::target!(b\"pimoroni-pico-plus-2\");\n\nuse defmt::{assert_eq, *};\nuse embassy_executor::Spawner;\nuse embassy_rp::bind_interrupts;\nuse embassy_rp::peripherals::UART0;\nuse embassy_rp::uart::{BufferedInterruptHandler, Config, Uart};\nuse embedded_io_async::{Read, Write};\nuse {defmt_rtt as _, panic_probe as _};\n\nbind_interrupts!(struct Irqs {\n    UART0_IRQ => BufferedInterruptHandler<UART0>;\n});\n\n#[embassy_executor::main]\nasync fn main(_spawner: Spawner) {\n    let p = embassy_rp::init(Default::default());\n    info!(\"Hello World!\");\n\n    let (tx, rx, uart) = (p.PIN_0, p.PIN_1, p.UART0);\n\n    let config = Config::default();\n    let mut uart = Uart::new_blocking(uart, tx, rx, config);\n\n    // We can't send too many bytes, they have to fit in the FIFO.\n    // This is because we aren't sending+receiving at the same time.\n\n    let data = [0xC0, 0xDE];\n    uart.blocking_write(&data).unwrap();\n\n    let mut buf = [0; 2];\n    uart.blocking_read(&mut buf).unwrap();\n    assert_eq!(buf, data);\n\n    let tx_buf = &mut [0u8; 16];\n    let rx_buf = &mut [0u8; 16];\n\n    let mut uart = uart.into_buffered(Irqs, tx_buf, rx_buf);\n\n    // Make sure we send more bytes than fits in the FIFO, to test the actual\n    // bufferedUart.\n\n    let data = [\n        1u8, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29,\n        30, 31,\n    ];\n    uart.write_all(&data).await.unwrap();\n    info!(\"Done writing\");\n\n    let mut buf = [0; 31];\n    uart.read_exact(&mut buf).await.unwrap();\n    assert_eq!(buf, data);\n\n    info!(\"Test OK\");\n    cortex_m::asm::bkpt();\n}\n"
  },
  {
    "path": "tests/stm32/.cargo/config.toml",
    "content": "[unstable]\n#build-std = [\"core\"]\n#build-std-features = [\"panic_immediate_abort\"]\n\n[target.'cfg(all(target_arch = \"arm\", target_os = \"none\"))']\nrunner = \"teleprobe client run\"\n#runner = \"teleprobe local run --chip STM32H7S3L8Hx --elf\"\n\nrustflags = [\n  # Code-size optimizations.\n  #\"-Z\", \"trap-unreachable=no\",\n  \"-C\", \"no-vectorize-loops\",\n]\n\n[build]\n#target = \"thumbv6m-none-eabi\"\n#target = \"thumbv7m-none-eabi\"\ntarget = \"thumbv7em-none-eabi\"\n#target = \"thumbv8m.main-none-eabihf\"\n\n[env]\nDEFMT_LOG = \"trace,embassy_hal_internal=debug,embassy_net_esp_hosted=debug,smoltcp=info\"\n"
  },
  {
    "path": "tests/stm32/Cargo.toml",
    "content": "[package]\nedition = \"2024\"\nname = \"embassy-stm32-tests\"\nversion = \"0.1.0\"\nlicense = \"MIT OR Apache-2.0\"\nautobins = false\npublish = false\n\n[features]\nstm32c031c6 = [\"embassy-stm32/stm32c031c6\", \"not-stop\", \"cm0\", \"not-gpdma\"]\nstm32c071rb = [\"embassy-stm32/stm32c071rb\", \"not-stop\", \"cm0\", \"not-gpdma\"]\nstm32f100rd = [\"embassy-stm32/stm32f100rd\", \"not-stop\", \"spi-v1\", \"not-gpdma\", \"afio\", \"afio-value-line\"]\nstm32f103c8 = [\"embassy-stm32/stm32f103c8\", \"not-stop\", \"spi-v1\", \"not-gpdma\", \"afio\"]\nstm32f107vc = [\"embassy-stm32/stm32f107vc\", \"not-stop\", \"spi-v1\", \"not-gpdma\", \"afio\", \"afio-connectivity-line\"]\nstm32f207zg = [\"embassy-stm32/stm32f207zg\", \"not-stop\", \"spi-v1\", \"chrono\", \"not-gpdma\", \"eth\", \"rng\"]\nstm32f303ze = [\"embassy-stm32/stm32f303ze\", \"not-stop\", \"chrono\", \"not-gpdma\"]\nstm32f429zi = [\"embassy-stm32/stm32f429zi\", \"stop\", \"spi-v1\", \"chrono\", \"eth\", \"can\", \"not-gpdma\", \"dac\", \"rng\"]\nstm32f446re = [\"embassy-stm32/stm32f446re\", \"stop\", \"spi-v1\", \"chrono\", \"can\", \"not-gpdma\", \"dac\", \"sdmmc\"]\nstm32f767zi = [\"embassy-stm32/stm32f767zi\", \"not-stop\", \"chrono\", \"not-gpdma\", \"eth\", \"rng\", \"single-bank\"]\nstm32g071rb = [\"embassy-stm32/stm32g071rb\", \"not-stop\", \"cm0\", \"not-gpdma\", \"dac\", \"ucpd\"]\nstm32g491re = [\"embassy-stm32/stm32g491re\", \"stop\", \"chrono\", \"not-gpdma\", \"rng\", \"fdcan\", \"cordic\"]\nstm32h563zi = [\"embassy-stm32/stm32h563zi\", \"stop\", \"spi-v345\", \"chrono\", \"eth\", \"rng\", \"fdcan\", \"hash-v34\", \"cordic\"]\nstm32h753zi = [\"embassy-stm32/stm32h753zi\", \"not-stop\", \"spi-v345\", \"chrono\", \"not-gpdma\", \"eth\", \"rng\", \"fdcan\", \"hash\", \"cryp\"]\nstm32h755zi = [\"embassy-stm32/stm32h755zi-cm7\", \"not-stop\", \"spi-v345\", \"chrono\", \"not-gpdma\", \"eth\", \"dac\", \"rng\", \"fdcan\", \"hash\", \"cryp\"]\nstm32h7a3zi = [\"embassy-stm32/stm32h7a3zi\", \"not-stop\", \"spi-v345\", \"not-gpdma\", \"rng\", \"fdcan\"]\nstm32l073rz = [\"embassy-stm32/stm32l073rz\", \"not-stop\", \"cm0\", \"not-gpdma\", \"rng\", \"eeprom\"]\nstm32l152re = [\"embassy-stm32/stm32l152re\", \"not-stop\", \"spi-v1\", \"chrono\", \"not-gpdma\", \"eeprom\"]\nstm32l496zg = [\"embassy-stm32/stm32l496zg\", \"not-stop\", \"not-gpdma\", \"rng\"]\nstm32l4a6zg = [\"embassy-stm32/stm32l4a6zg\", \"not-stop\", \"chrono\", \"not-gpdma\", \"rng\", \"hash\"]\nstm32l4r5zi = [\"embassy-stm32/stm32l4r5zi\", \"not-stop\", \"chrono\", \"not-gpdma\", \"rng\", \"dual-bank\"]\nstm32l552ze = [\"embassy-stm32/stm32l552ze\", \"not-stop\", \"not-gpdma\", \"rng\", \"hash\", \"dual-bank\"]\nstm32u585ai = [\"embassy-stm32/stm32u585ai\", \"not-stop\", \"spi-v345\", \"chrono\", \"rng\", \"hash\", \"cordic\"]\nstm32u5a5zj = [\"embassy-stm32/stm32u5a5zj\", \"not-stop\", \"spi-v345\", \"chrono\", \"rng\", \"hash\", \"cordic\"]\nstm32wb55rg = [\"embassy-stm32/stm32wb55rg\", \"stop\", \"chrono\", \"not-gpdma\", \"ble\", \"mac\" , \"rng\", \"hsem\"]\nstm32wba52cg = [\"embassy-stm32/stm32wba52cg\", \"not-stop\", \"spi-v345\", \"chrono\", \"rng\", \"hash\", \"adc\"]\nstm32wba65ri = [\"embassy-stm32/stm32wba65ri\", \"not-stop\", \"spi-v345\", \"chrono\", \"rng\", \"hash\", \"adc\"]\nstm32wl55jc = [\"embassy-stm32/stm32wl55jc-cm4\", \"stop\", \"not-gpdma\", \"rng\", \"chrono\", \"hsem\"]\nstm32f091rc = [\"embassy-stm32/stm32f091rc\", \"not-stop\", \"cm0\", \"not-gpdma\", \"chrono\"]\nstm32h503rb = [\"embassy-stm32/stm32h503rb\", \"stop\", \"spi-v345\", \"rng\"]\nstm32h7s3l8 = [\"embassy-stm32/stm32h7s3l8\", \"not-stop\", \"spi-v345\", \"rng\", \"cordic\", \"hash-v34\"] # TODO: fdcan crashes, cryp dma hangs.\nstm32u083rc = [\"embassy-stm32/stm32u083rc\", \"not-stop\", \"cm0\", \"rng\", \"chrono\"]\n\nspi-v1 = []\nspi-v345 = []\ncryp = []\nhash = []\nhash-v34 = [\"hash\"]\neth = []\nrng = []\nsdmmc = []\nstop = [\"embassy-stm32/low-power\", \"embassy-stm32/low-power-debug-with-sleep\", \"embassy-stm32/executor-thread\"]\nnot-stop = [\"embassy-executor/platform-cortex-m\", \"embassy-executor/executor-thread\"]\nchrono = [\"embassy-stm32/chrono\", \"dep:chrono\"]\ncan = []\nfdcan = []\nble = [\"dep:embassy-stm32-wpan\", \"embassy-stm32-wpan/wb55_ble\"]\nmac = [\"dep:embassy-stm32-wpan\", \"embassy-stm32-wpan/wb55_mac\"]\nembassy-stm32-wpan = []\nnot-gpdma = []\ndac = []\nadc = []\nucpd = []\ncordic = [\"dep:num-traits\"]\nhsem = []\ndual-bank = [\"embassy-stm32/dual-bank\"]\nsingle-bank = [\"embassy-stm32/single-bank\"]\neeprom = []\nafio = []\nafio-connectivity-line = []\nafio-value-line = []\n\ncm0 = [\"portable-atomic/unsafe-assume-single-core\"]\n\n[dependencies]\nteleprobe-meta = \"1\"\n\nembassy-sync = { version = \"0.8.0\", path = \"../../embassy-sync\", features = [\"defmt\"] }\nembassy-executor = { version = \"0.10.0\", path = \"../../embassy-executor\", features = [\"defmt\"] }\nembassy-time = { version = \"0.5.1\", path = \"../../embassy-time\", features = [\"defmt\", \"tick-hz-131_072\", \"defmt-timestamp-uptime\"] }\nembassy-stm32 = { version = \"0.6.0\", path = \"../../embassy-stm32\", features = [ \"defmt\", \"unstable-pac\", \"memory-x\", \"time-driver-any\"]  }\nembassy-futures = { version = \"0.1.2\", path = \"../../embassy-futures\" }\nembassy-stm32-wpan = { version = \"0.1.0\", path = \"../../embassy-stm32-wpan\", optional = true, features = [\"defmt\", \"stm32wb55rg\", \"wb55_ble\"] }\nembassy-net = { version = \"0.9.0\", path = \"../../embassy-net\", features = [\"defmt\",  \"tcp\", \"udp\", \"dhcpv4\", \"medium-ethernet\"] }\nperf-client = { path = \"../perf-client\" }\n\ndefmt = \"1.0.1\"\ndefmt-rtt = \"1.0.0\"\n\ncortex-m = { version = \"0.7.6\", features = [\"critical-section-single-core\"] }\ncortex-m-rt = \"0.7.0\"\nembedded-hal = \"0.2.6\"\nembedded-hal-1 = { package = \"embedded-hal\", version = \"1.0\" }\nembedded-hal-async = { version = \"1.0\" }\nembedded-can = { version = \"0.4\" }\nmicromath = \"2.0.0\"\npanic-probe = { version = \"1.0.0\", features = [\"print-defmt\"] }\nrand_core = { version = \"0.9.1\", default-features = false }\nrand_chacha = { version = \"0.9.0\", default-features = false }\nstatic_cell = \"2\"\nportable-atomic = { version = \"1.5\", features = [] }\ncritical-section = \"1.1\"\n\nchrono = { version = \"^0.4\", default-features = false, optional = true}\nsha2 = { version = \"0.10.8\", default-features = false }\nhmac = \"0.12.1\"\naes-gcm = { version = \"0.10.3\", default-features = false, features = [\"aes\", \"heapless\"] }\nnum-traits = { version=\"0.2\", default-features = false,features = [\"libm\"], optional = true}\n\n# BEGIN TESTS\n# Generated by gen_test.py. DO NOT EDIT.\n[[bin]]\nname = \"afio\"\npath = \"src/bin/afio.rs\"\nrequired-features = [ \"afio\",]\n\n[[bin]]\nname = \"adc\"\npath = \"src/bin/adc.rs\"\nrequired-features = [ \"adc\",]\n\n[[bin]]\nname = \"can\"\npath = \"src/bin/can.rs\"\nrequired-features = [ \"can\",]\n\n[[bin]]\nname = \"cordic\"\npath = \"src/bin/cordic.rs\"\nrequired-features = [ \"rng\", \"cordic\",]\n\n[[bin]]\nname = \"cryp\"\npath = \"src/bin/cryp.rs\"\nrequired-features = [ \"cryp\",]\n\n[[bin]]\nname = \"dac\"\npath = \"src/bin/dac.rs\"\nrequired-features = [ \"dac\",]\n\n[[bin]]\nname = \"dac_l1\"\npath = \"src/bin/dac_l1.rs\"\nrequired-features = [ \"stm32l152re\",]\n\n[[bin]]\nname = \"eeprom\"\npath = \"src/bin/eeprom.rs\"\nrequired-features = [ \"eeprom\",]\n\n[[bin]]\nname = \"eth\"\npath = \"src/bin/eth.rs\"\nrequired-features = [ \"eth\",]\n\n[[bin]]\nname = \"fdcan\"\npath = \"src/bin/fdcan.rs\"\nrequired-features = [ \"fdcan\",]\n\n[[bin]]\nname = \"gpio\"\npath = \"src/bin/gpio.rs\"\nrequired-features = []\n\n[[bin]]\nname = \"hash\"\npath = \"src/bin/hash.rs\"\nrequired-features = [ \"hash\",]\n\n[[bin]]\nname = \"rng\"\npath = \"src/bin/rng.rs\"\nrequired-features = [ \"rng\",]\n\n[[bin]]\nname = \"rtc\"\npath = \"src/bin/rtc.rs\"\nrequired-features = [ \"chrono\",]\n\n[[bin]]\nname = \"sdmmc\"\npath = \"src/bin/sdmmc.rs\"\nrequired-features = [ \"sdmmc\",]\n\n[[bin]]\nname = \"spi\"\npath = \"src/bin/spi.rs\"\nrequired-features = []\n\n[[bin]]\nname = \"spi_dma\"\npath = \"src/bin/spi_dma.rs\"\nrequired-features = []\n\n[[bin]]\nname = \"stop\"\npath = \"src/bin/stop.rs\"\nrequired-features = [ \"stop\", \"chrono\",]\n\n[[bin]]\nname = \"timer\"\npath = \"src/bin/timer.rs\"\nrequired-features = []\n\n[[bin]]\nname = \"ucpd\"\npath = \"src/bin/ucpd.rs\"\nrequired-features = [ \"ucpd\",]\n\n[[bin]]\nname = \"usart\"\npath = \"src/bin/usart.rs\"\nrequired-features = []\n\n[[bin]]\nname = \"usart_dma\"\npath = \"src/bin/usart_dma.rs\"\nrequired-features = []\n\n[[bin]]\nname = \"usart_rx_ringbuffered\"\npath = \"src/bin/usart_rx_ringbuffered.rs\"\nrequired-features = [ \"not-gpdma\",]\n\n[[bin]]\nname = \"wpan_ble\"\npath = \"src/bin/wpan_ble.rs\"\nrequired-features = [ \"ble\",]\n\n[[bin]]\nname = \"wpan_mac\"\npath = \"src/bin/wpan_mac.rs\"\nrequired-features = [ \"mac\",]\n\n[[bin]]\nname = \"hsem\"\npath = \"src/bin/hsem.rs\"\nrequired-features = [ \"hsem\",]\n\n# END TESTS\n\n[profile.dev]\ndebug = 2\ndebug-assertions = true\nopt-level = 's'\noverflow-checks = true\n\n[profile.release]\ncodegen-units = 1\ndebug = 2\ndebug-assertions = false\nincremental = false\nlto = \"fat\"\nopt-level = 's'\noverflow-checks = false\n\n# do not optimize proc-macro crates = faster builds from scratch\n[profile.dev.build-override]\ncodegen-units = 8\ndebug = false\ndebug-assertions = false\nopt-level = 0\noverflow-checks = false\n\n[profile.release.build-override]\ncodegen-units = 8\ndebug = false\ndebug-assertions = false\nopt-level = 0\noverflow-checks = false\n\n[package.metadata.embassy]\nbuild = [\n  { target = \"thumbv7m-none-eabi\", features = [\"stm32f103c8\"], artifact-dir = \"out/tests/stm32f103c8\" },\n  { target = \"thumbv7em-none-eabi\", features = [\"stm32f429zi\"], artifact-dir = \"out/tests/stm32f429zi\" },\n  { target = \"thumbv7em-none-eabi\", features = [\"stm32f446re\"], artifact-dir = \"out/tests/stm32f446re\" },\n  { target = \"thumbv7em-none-eabi\", features = [\"stm32g491re\"], artifact-dir = \"out/tests/stm32g491re\" },\n  { target = \"thumbv6m-none-eabi\", features = [\"stm32g071rb\"], artifact-dir = \"out/tests/stm32g071rb\" },\n  { target = \"thumbv6m-none-eabi\", features = [\"stm32c031c6\"], artifact-dir = \"out/tests/stm32c031c6\" },\n  { target = \"thumbv6m-none-eabi\", features = [\"stm32c071rb\"], artifact-dir = \"out/tests/stm32c071rb\" },\n  { target = \"thumbv7em-none-eabi\", features = [\"stm32h755zi\"], artifact-dir = \"out/tests/stm32h755zi\" },\n  { target = \"thumbv7em-none-eabi\", features = [\"stm32h753zi\"], artifact-dir = \"out/tests/stm32h753zi\" },\n  { target = \"thumbv7em-none-eabi\", features = [\"stm32h7a3zi\"], artifact-dir = \"out/tests/stm32h7a3zi\" },\n  { target = \"thumbv7em-none-eabi\", features = [\"stm32wb55rg\"], artifact-dir = \"out/tests/stm32wb55rg\" },\n  { target = \"thumbv8m.main-none-eabihf\", features = [\"stm32h563zi\"], artifact-dir = \"out/tests/stm32h563zi\" },\n  { target = \"thumbv8m.main-none-eabihf\", features = [\"stm32u585ai\"], artifact-dir = \"out/tests/stm32u585ai\" },\n  { target = \"thumbv8m.main-none-eabihf\", features = [\"stm32u5a5zj\"], artifact-dir = \"out/tests/stm32u5a5zj\" },\n  { target = \"thumbv8m.main-none-eabihf\", features = [\"stm32wba52cg\"], artifact-dir = \"out/tests/stm32wba52cg\" },\n  { target = \"thumbv8m.main-none-eabihf\", features = [\"stm32wba65ri\"], artifact-dir = \"out/tests/stm32wba65ri\" },\n  { target = \"thumbv6m-none-eabi\", features = [\"stm32l073rz\"], artifact-dir = \"out/tests/stm32l073rz\" },\n  { target = \"thumbv7m-none-eabi\", features = [\"stm32l152re\"], artifact-dir = \"out/tests/stm32l152re\" },\n  { target = \"thumbv7em-none-eabi\", features = [\"stm32l4a6zg\"], artifact-dir = \"out/tests/stm32l4a6zg\" },\n  { target = \"thumbv7em-none-eabi\", features = [\"stm32l4r5zi\"], artifact-dir = \"out/tests/stm32l4r5zi\" },\n  { target = \"thumbv8m.main-none-eabihf\", features = [\"stm32l552ze\"], artifact-dir = \"out/tests/stm32l552ze\" },\n  { target = \"thumbv7em-none-eabi\", features = [\"stm32f767zi\"], artifact-dir = \"out/tests/stm32f767zi\" },\n  { target = \"thumbv7m-none-eabi\", features = [\"stm32f207zg\"], artifact-dir = \"out/tests/stm32f207zg\" },\n  { target = \"thumbv7em-none-eabi\", features = [\"stm32f303ze\"], artifact-dir = \"out/tests/stm32f303ze\" },\n  { target = \"thumbv7em-none-eabi\", features = [\"stm32l496zg\"], artifact-dir = \"out/tests/stm32l496zg\" },\n  { target = \"thumbv7em-none-eabi\", features = [\"stm32wl55jc\"], artifact-dir = \"out/tests/stm32wl55jc\" },\n  { target = \"thumbv7em-none-eabi\", features = [\"stm32h7s3l8\"], artifact-dir = \"out/tests/stm32h7s3l8\" },\n  { target = \"thumbv6m-none-eabi\", features = [\"stm32f091rc\"], artifact-dir = \"out/tests/stm32f091rc\" },\n  { target = \"thumbv8m.main-none-eabihf\", features = [\"stm32h503rb\"], artifact-dir = \"out/tests/stm32h503rb\" },\n  { target = \"thumbv6m-none-eabi\", features = [\"stm32u083rc\"], artifact-dir = \"out/tests/stm32u083rc\" }\n]\n"
  },
  {
    "path": "tests/stm32/build.rs",
    "content": "use std::error::Error;\nuse std::path::PathBuf;\nuse std::{env, fs};\n\nfn main() -> Result<(), Box<dyn Error>> {\n    let out = PathBuf::from(env::var(\"OUT_DIR\").unwrap());\n    fs::write(out.join(\"link_ram.x\"), include_bytes!(\"../link_ram_cortex_m.x\")).unwrap();\n    println!(\"cargo:rustc-link-search={}\", out.display());\n    println!(\"cargo:rustc-link-arg-bins=--nmagic\");\n\n    if cfg!(any(\n        // too little RAM to run from RAM.\n        feature = \"stm32f103c8\", // 20 kb\n        feature = \"stm32c031c6\", // 6 kb\n        feature = \"stm32c071rb\", // 24 kb\n        feature = \"stm32l073rz\", // 20 kb\n        feature = \"stm32h503rb\", // 32 kb\n        // no VTOR, so interrupts can't work when running from RAM\n        feature = \"stm32f091rc\",\n    )) {\n        println!(\"cargo:rustc-link-arg-bins=-Tlink.x\");\n    } else {\n        println!(\"cargo:rustc-link-arg-bins=-Tlink_ram.x\");\n        println!(\"cargo:rerun-if-changed=../link_ram_cortex_m.x\");\n    }\n\n    if cfg!(feature = \"stm32wb55rg\") {\n        println!(\"cargo:rustc-link-arg-bins=-Ttl_mbox.x\");\n    }\n\n    println!(\"cargo:rustc-link-arg-bins=-Tdefmt.x\");\n    println!(\"cargo:rustc-link-arg-bins=-Tteleprobe.x\");\n\n    Ok(())\n}\n"
  },
  {
    "path": "tests/stm32/gen_test.py",
    "content": "import os\nimport toml\nfrom glob import glob\n\nabspath = os.path.abspath(__file__)\ndname = os.path.dirname(abspath)\nos.chdir(dname)\n\n# ======= load test list\ntests = {}\nfor f in sorted(glob('./src/bin/*.rs')):\n    name = os.path.splitext(os.path.basename(f))[0]\n    features = []\n    with open(f, 'r') as f:\n        for line in f:\n            if line.startswith('// required-features:'):\n                features = [feature.strip() for feature in line.split(':', 2)[1].strip().split(',')]\n\n    tests[name] = features\n\n# ========= Update Cargo.toml\n\nthings = {\n    'bin': [\n        {\n            'name': f'{name}',\n            'path': f'src/bin/{name}.rs',\n            'required-features': features,\n        }\n        for name, features in tests.items()\n    ]\n}\n\nSEPARATOR_START = '# BEGIN TESTS\\n'\nSEPARATOR_END = '# END TESTS\\n'\nHELP = '# Generated by gen_test.py. DO NOT EDIT.\\n'\nwith open('Cargo.toml', 'r') as f:\n    data = f.read()\nbefore, data = data.split(SEPARATOR_START, maxsplit=1)\n_, after = data.split(SEPARATOR_END, maxsplit=1)\ndata = before + SEPARATOR_START + HELP + \\\n    toml.dumps(things) + SEPARATOR_END + after\nwith open('Cargo.toml', 'w') as f:\n    f.write(data)\n"
  },
  {
    "path": "tests/stm32/src/bin/adc.rs",
    "content": "#![no_std]\n#![no_main]\n\n// required-features: dac\n\n#[path = \"../common.rs\"]\nmod common;\n\nuse common::*;\nuse embassy_executor::Spawner;\nuse embassy_stm32::adc::{Adc, SampleTime};\nuse embassy_time::Timer;\nuse {defmt_rtt as _, panic_probe as _};\n\n#[cfg_attr(\n    feature = \"stop\",\n    embassy_executor::main(executor = \"embassy_stm32::executor::Executor\", entry = \"cortex_m_rt::entry\")\n)]\n#[cfg_attr(not(feature = \"stop\"), embassy_executor::main)]\nasync fn main(_spawner: Spawner) {\n    // Initialize the board and obtain a Peripherals instance\n    let p: embassy_stm32::Peripherals = init();\n\n    let adc = peri!(p, ADC);\n    let mut adc_pin = peri!(p, DAC_PIN);\n\n    let mut adc = Adc::new_adc4(adc);\n\n    // Now wait a little to obtain a stable value\n    Timer::after_millis(30).await;\n    let _ = adc.blocking_read(&mut adc_pin, SampleTime::from_bits(0));\n\n    for _ in 0..=255 {\n        // Now wait a little to obtain a stable value\n        Timer::after_millis(30).await;\n\n        // Need to steal the peripherals here because PA4 is obviously in use already\n        let _ = adc.blocking_read(&mut adc_pin, SampleTime::from_bits(0));\n    }\n\n    info!(\"Test OK\");\n    cortex_m::asm::bkpt();\n}\n"
  },
  {
    "path": "tests/stm32/src/bin/afio.rs",
    "content": "// required-features: afio\n#![no_std]\n#![no_main]\n#[path = \"../common.rs\"]\nmod common;\n\nuse common::*;\nuse embassy_executor::Spawner;\nuse embassy_stm32::gpio::{AfioRemap, OutputType, Pull};\nuse embassy_stm32::pac::AFIO;\nuse embassy_stm32::time::khz;\nuse embassy_stm32::timer::complementary_pwm::{ComplementaryPwm, ComplementaryPwmPin};\nuse embassy_stm32::timer::input_capture::{CapturePin, InputCapture};\nuse embassy_stm32::timer::pwm_input::PwmInput;\nuse embassy_stm32::timer::qei::Qei;\nuse embassy_stm32::timer::simple_pwm::{PwmPin, SimplePwm};\nuse embassy_stm32::timer::{Ch1, Ch2};\nuse embassy_stm32::usart::{Uart, UartRx, UartTx};\nuse embassy_stm32::{Peripherals, bind_interrupts};\n\n#[cfg(not(feature = \"afio-connectivity-line\"))]\nbind_interrupts!(struct Irqs {\n    USART3 => embassy_stm32::usart::InterruptHandler<embassy_stm32::peripherals::USART3>;\n    TIM1_CC => embassy_stm32::timer::CaptureCompareInterruptHandler<embassy_stm32::peripherals::TIM1>;\n\n    DMA1_CHANNEL2 => embassy_stm32::dma::InterruptHandler<embassy_stm32::peripherals::DMA1_CH2>;\n    DMA1_CHANNEL3 => embassy_stm32::dma::InterruptHandler<embassy_stm32::peripherals::DMA1_CH3>;\n});\n\n#[cfg(feature = \"afio-connectivity-line\")]\nbind_interrupts!(struct Irqs {\n    CAN1_RX0 => embassy_stm32::can::Rx0InterruptHandler<embassy_stm32::peripherals::CAN1>;\n    CAN1_RX1 => embassy_stm32::can::Rx1InterruptHandler<embassy_stm32::peripherals::CAN1>;\n    CAN1_SCE => embassy_stm32::can::SceInterruptHandler<embassy_stm32::peripherals::CAN1>;\n    CAN1_TX => embassy_stm32::can::TxInterruptHandler<embassy_stm32::peripherals::CAN1>;\n\n    CAN2_RX0 => embassy_stm32::can::Rx0InterruptHandler<embassy_stm32::peripherals::CAN2>;\n    CAN2_RX1 => embassy_stm32::can::Rx1InterruptHandler<embassy_stm32::peripherals::CAN2>;\n    CAN2_SCE => embassy_stm32::can::SceInterruptHandler<embassy_stm32::peripherals::CAN2>;\n    CAN2_TX => embassy_stm32::can::TxInterruptHandler<embassy_stm32::peripherals::CAN2>;\n\n    ETH => embassy_stm32::eth::InterruptHandler;\n    USART3 => embassy_stm32::usart::InterruptHandler<embassy_stm32::peripherals::USART3>;\n    TIM1_CC => embassy_stm32::timer::CaptureCompareInterruptHandler<embassy_stm32::peripherals::TIM1>;\n\n    DMA1_CHANNEL2 => embassy_stm32::dma::InterruptHandler<embassy_stm32::peripherals::DMA1_CH2>;\n    DMA1_CHANNEL3 => embassy_stm32::dma::InterruptHandler<embassy_stm32::peripherals::DMA1_CH3>;\n    DMA2_CHANNEL1 => embassy_stm32::dma::InterruptHandler<embassy_stm32::peripherals::DMA2_CH1>;\n    DMA2_CHANNEL2 => embassy_stm32::dma::InterruptHandler<embassy_stm32::peripherals::DMA2_CH2>;\n});\n\n#[cfg_attr(\n    feature = \"stop\",\n    embassy_executor::main(executor = \"embassy_stm32::executor::Executor\", entry = \"cortex_m_rt::entry\")\n)]\n#[cfg_attr(not(feature = \"stop\"), embassy_executor::main)]\nasync fn main(_spawner: Spawner) {\n    let mut p = init();\n    info!(\"Hello World!\");\n\n    // USART3\n    {\n        // no remap RX/TX/RTS/CTS\n        afio_registers_set_remap();\n        Uart::new_blocking_with_rtscts(\n            p.USART3.reborrow(),\n            p.PB11.reborrow(),\n            p.PB10.reborrow(),\n            p.PB14.reborrow(),\n            p.PB13.reborrow(),\n            Default::default(),\n        )\n        .unwrap();\n        defmt::assert_eq!(AFIO.mapr().read().usart3_remap(), 0);\n    }\n    {\n        // no remap RX/TX\n        afio_registers_set_remap();\n        Uart::new_blocking(\n            p.USART3.reborrow(),\n            p.PB11.reborrow(),\n            p.PB10.reborrow(),\n            Default::default(),\n        )\n        .unwrap();\n        defmt::assert_eq!(AFIO.mapr().read().usart3_remap(), 0);\n    }\n    {\n        // no remap TX\n        afio_registers_set_remap();\n        Uart::new_blocking_half_duplex(\n            p.USART3.reborrow(),\n            p.PB10.reborrow(),\n            Default::default(),\n            embassy_stm32::usart::HalfDuplexReadback::NoReadback,\n        )\n        .unwrap();\n        defmt::assert_eq!(AFIO.mapr().read().usart3_remap(), 0);\n    }\n    {\n        // no remap TX async\n        afio_registers_set_remap();\n        UartTx::new(\n            p.USART3.reborrow(),\n            p.PB10.reborrow(),\n            p.DMA1_CH2.reborrow(),\n            Irqs,\n            Default::default(),\n        )\n        .unwrap();\n        defmt::assert_eq!(AFIO.mapr().read().usart3_remap(), 0);\n    }\n    {\n        // no remap TX/CTS async\n        afio_registers_set_remap();\n        UartTx::new_with_cts(\n            p.USART3.reborrow(),\n            p.PB10.reborrow(),\n            p.PB13.reborrow(),\n            p.DMA1_CH2.reborrow(),\n            Irqs,\n            Default::default(),\n        )\n        .unwrap();\n        defmt::assert_eq!(AFIO.mapr().read().usart3_remap(), 0);\n    }\n    {\n        // no remap RX async\n        afio_registers_set_remap();\n        UartRx::new(\n            p.USART3.reborrow(),\n            p.PB11.reborrow(),\n            p.DMA1_CH3.reborrow(),\n            Irqs,\n            Default::default(),\n        )\n        .unwrap();\n        defmt::assert_eq!(AFIO.mapr().read().usart3_remap(), 0);\n    }\n    {\n        // no remap RX async\n        afio_registers_set_remap();\n        UartRx::new_with_rts(\n            p.USART3.reborrow(),\n            p.PB11.reborrow(),\n            p.PB14.reborrow(),\n            p.DMA1_CH3.reborrow(),\n            Irqs,\n            Default::default(),\n        )\n        .unwrap();\n        defmt::assert_eq!(AFIO.mapr().read().usart3_remap(), 0);\n    }\n    {\n        // no remap RX/TX async\n        afio_registers_set_remap();\n        Uart::new(\n            p.USART3.reborrow(),\n            p.PB11.reborrow(),\n            p.PB10.reborrow(),\n            p.DMA1_CH2.reborrow(),\n            p.DMA1_CH3.reborrow(),\n            Irqs,\n            Default::default(),\n        )\n        .unwrap();\n        defmt::assert_eq!(AFIO.mapr().read().usart3_remap(), 0);\n    }\n    {\n        // no remap RX/TX/RTS/CTS async\n        afio_registers_set_remap();\n        Uart::new_with_rtscts(\n            p.USART3.reborrow(),\n            p.PB11.reborrow(),\n            p.PB10.reborrow(),\n            p.PB14.reborrow(),\n            p.PB13.reborrow(),\n            p.DMA1_CH2.reborrow(),\n            p.DMA1_CH3.reborrow(),\n            Irqs,\n            Default::default(),\n        )\n        .unwrap();\n        defmt::assert_eq!(AFIO.mapr().read().usart3_remap(), 0);\n    }\n\n    // TIM1\n    {\n        // no remap\n        afio_registers_set_remap();\n        SimplePwm::new::<AfioRemap<0>>(\n            p.TIM1.reborrow(),\n            Some(PwmPin::new(p.PA8.reborrow(), OutputType::PushPull)),\n            Some(PwmPin::new(p.PA9.reborrow(), OutputType::PushPull)),\n            Some(PwmPin::new(p.PA10.reborrow(), OutputType::PushPull)),\n            Some(PwmPin::new(p.PA11.reborrow(), OutputType::PushPull)),\n            khz(10),\n            Default::default(),\n        );\n        defmt::assert_eq!(AFIO.mapr().read().tim1_remap(), 0);\n    }\n    {\n        // no remap\n        afio_registers_set_remap();\n        SimplePwm::new::<AfioRemap<0>>(\n            p.TIM1.reborrow(),\n            Some(PwmPin::new(p.PA8.reborrow(), OutputType::PushPull)),\n            None,\n            None,\n            None,\n            khz(10),\n            Default::default(),\n        );\n        defmt::assert_eq!(AFIO.mapr().read().tim1_remap(), 0);\n    }\n    {\n        // partial remap\n        reset_afio_registers();\n        ComplementaryPwm::new::<AfioRemap<1>>(\n            p.TIM1.reborrow(),\n            Some(PwmPin::new(p.PA8.reborrow(), OutputType::PushPull)),\n            None,\n            None,\n            None,\n            None,\n            None,\n            None,\n            None,\n            khz(10),\n            Default::default(),\n        );\n        defmt::assert_eq!(AFIO.mapr().read().tim1_remap(), 1);\n    }\n    {\n        // partial remap\n        reset_afio_registers();\n        ComplementaryPwm::new::<AfioRemap<1>>(\n            p.TIM1.reborrow(),\n            Some(PwmPin::new(p.PA8.reborrow(), OutputType::PushPull)),\n            Some(ComplementaryPwmPin::new(p.PA7.reborrow(), OutputType::PushPull)),\n            Some(PwmPin::new(p.PA9.reborrow(), OutputType::PushPull)),\n            Some(ComplementaryPwmPin::new(p.PB0.reborrow(), OutputType::PushPull)),\n            Some(PwmPin::new(p.PA10.reborrow(), OutputType::PushPull)),\n            None, // pin does not exist on medium-density devices\n            Some(PwmPin::new(p.PA11.reborrow(), OutputType::PushPull)),\n            None, // signal does not exist\n            khz(10),\n            Default::default(),\n        );\n        defmt::assert_eq!(AFIO.mapr().read().tim1_remap(), 1);\n    }\n    {\n        // partial remap\n        reset_afio_registers();\n        InputCapture::new::<AfioRemap<1>>(\n            p.TIM1.reborrow(),\n            Some(CapturePin::new(p.PA8.reborrow(), Pull::Down)),\n            None,\n            None,\n            None,\n            Irqs,\n            khz(10),\n            Default::default(),\n        );\n        defmt::assert_eq!(AFIO.mapr().read().tim1_remap(), 1);\n    }\n    {\n        // partial remap\n        reset_afio_registers();\n        PwmInput::new_ch1::<AfioRemap<1>>(p.TIM1.reborrow(), p.PA8.reborrow(), Irqs, Pull::Down, khz(10));\n        defmt::assert_eq!(AFIO.mapr().read().tim1_remap(), 1);\n    }\n    {\n        // partial remap\n        reset_afio_registers();\n        Qei::new::<Ch1, Ch2, AfioRemap<1>>(\n            p.TIM1.reborrow(),\n            p.PA8.reborrow(),\n            p.PA9.reborrow(),\n            Default::default(),\n        );\n        defmt::assert_eq!(AFIO.mapr().read().tim1_remap(), 1);\n    }\n\n    // TIM2\n    {\n        // no remap\n        afio_registers_set_remap();\n        SimplePwm::new::<AfioRemap<0>>(\n            p.TIM2.reborrow(),\n            Some(PwmPin::new(p.PA0.reborrow(), OutputType::PushPull)),\n            Some(PwmPin::new(p.PA1.reborrow(), OutputType::PushPull)),\n            Some(PwmPin::new(p.PA2.reborrow(), OutputType::PushPull)),\n            Some(PwmPin::new(p.PA3.reborrow(), OutputType::PushPull)),\n            khz(10),\n            Default::default(),\n        );\n        defmt::assert_eq!(AFIO.mapr().read().tim2_remap(), 0);\n    }\n    {\n        // partial remap 1\n        reset_afio_registers();\n        SimplePwm::new::<AfioRemap<1>>(\n            p.TIM2.reborrow(),\n            Some(PwmPin::new(p.PA15.reborrow(), OutputType::PushPull)),\n            Some(PwmPin::new(p.PB3.reborrow(), OutputType::PushPull)),\n            Some(PwmPin::new(p.PA2.reborrow(), OutputType::PushPull)),\n            Some(PwmPin::new(p.PA3.reborrow(), OutputType::PushPull)),\n            khz(10),\n            Default::default(),\n        );\n        defmt::assert_eq!(AFIO.mapr().read().tim2_remap(), 1);\n    }\n    {\n        // partial remap 2\n        reset_afio_registers();\n        SimplePwm::new::<AfioRemap<2>>(\n            p.TIM2.reborrow(),\n            Some(PwmPin::new(p.PA0.reborrow(), OutputType::PushPull)),\n            Some(PwmPin::new(p.PA1.reborrow(), OutputType::PushPull)),\n            Some(PwmPin::new(p.PB10.reborrow(), OutputType::PushPull)),\n            Some(PwmPin::new(p.PB11.reborrow(), OutputType::PushPull)),\n            khz(10),\n            Default::default(),\n        );\n        defmt::assert_eq!(AFIO.mapr().read().tim2_remap(), 2);\n    }\n    {\n        // full remap\n        reset_afio_registers();\n        SimplePwm::new::<AfioRemap<3>>(\n            p.TIM2.reborrow(),\n            Some(PwmPin::new(p.PA15.reborrow(), OutputType::PushPull)),\n            Some(PwmPin::new(p.PB3.reborrow(), OutputType::PushPull)),\n            Some(PwmPin::new(p.PB10.reborrow(), OutputType::PushPull)),\n            Some(PwmPin::new(p.PB11.reborrow(), OutputType::PushPull)),\n            khz(10),\n            Default::default(),\n        );\n        defmt::assert_eq!(AFIO.mapr().read().tim2_remap(), 3);\n    }\n\n    connectivity_line::run(&mut p);\n    value_line::run(&mut p);\n\n    info!(\"Test OK\");\n    cortex_m::asm::bkpt();\n}\n\n#[cfg(feature = \"afio-connectivity-line\")]\nmod connectivity_line {\n    use embassy_stm32::can::Can;\n    use embassy_stm32::eth::{Ethernet, PacketQueue};\n    use embassy_stm32::i2s::I2S;\n    use embassy_stm32::spi::Spi;\n\n    use super::*;\n\n    pub fn run(p: &mut Peripherals) {\n        // USART3\n        {\n            // partial remap RX/TX/RTS/CTS\n            reset_afio_registers();\n            Uart::new_blocking_with_rtscts(\n                p.USART3.reborrow(),\n                p.PC11.reborrow(),\n                p.PC10.reborrow(),\n                p.PB14.reborrow(),\n                p.PB13.reborrow(),\n                Default::default(),\n            )\n            .unwrap();\n            defmt::assert_eq!(AFIO.mapr().read().usart3_remap(), 1);\n        }\n        {\n            // partial remap RX/TX\n            reset_afio_registers();\n            Uart::new_blocking(\n                p.USART3.reborrow(),\n                p.PC11.reborrow(),\n                p.PC10.reborrow(),\n                Default::default(),\n            )\n            .unwrap();\n            defmt::assert_eq!(AFIO.mapr().read().usart3_remap(), 1);\n        }\n        {\n            // partial remap TX\n            reset_afio_registers();\n            Uart::new_blocking_half_duplex(\n                p.USART3.reborrow(),\n                p.PC10.reborrow(),\n                Default::default(),\n                embassy_stm32::usart::HalfDuplexReadback::NoReadback,\n            )\n            .unwrap();\n            defmt::assert_eq!(AFIO.mapr().read().usart3_remap(), 1);\n        }\n        {\n            // partial remap TX async\n            reset_afio_registers();\n            UartTx::new(\n                p.USART3.reborrow(),\n                p.PC10.reborrow(),\n                p.DMA1_CH2.reborrow(),\n                Irqs,\n                Default::default(),\n            )\n            .unwrap();\n            defmt::assert_eq!(AFIO.mapr().read().usart3_remap(), 1);\n        }\n        {\n            // partial remap TX/CTS async\n            reset_afio_registers();\n            UartTx::new_with_cts(\n                p.USART3.reborrow(),\n                p.PC10.reborrow(),\n                p.PB13.reborrow(),\n                p.DMA1_CH2.reborrow(),\n                Irqs,\n                Default::default(),\n            )\n            .unwrap();\n            defmt::assert_eq!(AFIO.mapr().read().usart3_remap(), 1);\n        }\n        {\n            // partial remap RX async\n            reset_afio_registers();\n            UartRx::new(\n                p.USART3.reborrow(),\n                p.PC11.reborrow(),\n                p.DMA1_CH3.reborrow(),\n                Irqs,\n                Default::default(),\n            )\n            .unwrap();\n            defmt::assert_eq!(AFIO.mapr().read().usart3_remap(), 1);\n        }\n        {\n            // partial remap RX async\n            reset_afio_registers();\n            UartRx::new_with_rts(\n                p.USART3.reborrow(),\n                p.PC11.reborrow(),\n                p.PB14.reborrow(),\n                p.DMA1_CH3.reborrow(),\n                Irqs,\n                Default::default(),\n            )\n            .unwrap();\n            defmt::assert_eq!(AFIO.mapr().read().usart3_remap(), 1);\n        }\n        {\n            // partial remap RX/TX async\n            reset_afio_registers();\n            Uart::new(\n                p.USART3.reborrow(),\n                p.PC11.reborrow(),\n                p.PC10.reborrow(),\n                p.DMA1_CH2.reborrow(),\n                p.DMA1_CH3.reborrow(),\n                Irqs,\n                Default::default(),\n            )\n            .unwrap();\n            defmt::assert_eq!(AFIO.mapr().read().usart3_remap(), 1);\n        }\n        {\n            // partial remap RX/TX/RTS/CTS async\n            reset_afio_registers();\n            Uart::new_with_rtscts(\n                p.USART3.reborrow(),\n                p.PC11.reborrow(),\n                p.PC10.reborrow(),\n                p.PB14.reborrow(),\n                p.PB13.reborrow(),\n                p.DMA1_CH2.reborrow(),\n                p.DMA1_CH3.reborrow(),\n                Irqs,\n                Default::default(),\n            )\n            .unwrap();\n            defmt::assert_eq!(AFIO.mapr().read().usart3_remap(), 1);\n        }\n        {\n            // full remap RX/TX/RTS/CTS\n            reset_afio_registers();\n            Uart::new_blocking_with_rtscts(\n                p.USART3.reborrow(),\n                p.PD9.reborrow(),\n                p.PD8.reborrow(),\n                p.PD12.reborrow(),\n                p.PD11.reborrow(),\n                Default::default(),\n            )\n            .unwrap();\n            defmt::assert_eq!(AFIO.mapr().read().usart3_remap(), 3);\n        }\n        {\n            // full remap RX/TX\n            reset_afio_registers();\n            Uart::new_blocking(\n                p.USART3.reborrow(),\n                p.PD9.reborrow(),\n                p.PD8.reborrow(),\n                Default::default(),\n            )\n            .unwrap();\n            defmt::assert_eq!(AFIO.mapr().read().usart3_remap(), 3);\n        }\n        {\n            // full remap TX\n            reset_afio_registers();\n            Uart::new_blocking_half_duplex(\n                p.USART3.reborrow(),\n                p.PD8.reborrow(),\n                Default::default(),\n                embassy_stm32::usart::HalfDuplexReadback::NoReadback,\n            )\n            .unwrap();\n            defmt::assert_eq!(AFIO.mapr().read().usart3_remap(), 3);\n        }\n        {\n            // full remap TX async\n            reset_afio_registers();\n            UartTx::new(\n                p.USART3.reborrow(),\n                p.PD8.reborrow(),\n                p.DMA1_CH2.reborrow(),\n                Irqs,\n                Default::default(),\n            )\n            .unwrap();\n            defmt::assert_eq!(AFIO.mapr().read().usart3_remap(), 3);\n        }\n        {\n            // full remap TX/CTS async\n            reset_afio_registers();\n            UartTx::new_with_cts(\n                p.USART3.reborrow(),\n                p.PD8.reborrow(),\n                p.PD11.reborrow(),\n                p.DMA1_CH2.reborrow(),\n                Irqs,\n                Default::default(),\n            )\n            .unwrap();\n            defmt::assert_eq!(AFIO.mapr().read().usart3_remap(), 3);\n        }\n        {\n            // full remap RX async\n            reset_afio_registers();\n            UartRx::new(\n                p.USART3.reborrow(),\n                p.PD9.reborrow(),\n                p.DMA1_CH3.reborrow(),\n                Irqs,\n                Default::default(),\n            )\n            .unwrap();\n            defmt::assert_eq!(AFIO.mapr().read().usart3_remap(), 3);\n        }\n        {\n            // full remap RX async\n            reset_afio_registers();\n            UartRx::new_with_rts(\n                p.USART3.reborrow(),\n                p.PD9.reborrow(),\n                p.PD12.reborrow(),\n                p.DMA1_CH3.reborrow(),\n                Irqs,\n                Default::default(),\n            )\n            .unwrap();\n            defmt::assert_eq!(AFIO.mapr().read().usart3_remap(), 3);\n        }\n        {\n            // full remap RX/TX async\n            reset_afio_registers();\n            Uart::new(\n                p.USART3.reborrow(),\n                p.PD9.reborrow(),\n                p.PD8.reborrow(),\n                p.DMA1_CH2.reborrow(),\n                p.DMA1_CH3.reborrow(),\n                Irqs,\n                Default::default(),\n            )\n            .unwrap();\n            defmt::assert_eq!(AFIO.mapr().read().usart3_remap(), 3);\n        }\n        {\n            // full remap RX/TX/RTS/CTS async\n            reset_afio_registers();\n            Uart::new_with_rtscts(\n                p.USART3.reborrow(),\n                p.PD9.reborrow(),\n                p.PD8.reborrow(),\n                p.PD12.reborrow(),\n                p.PD11.reborrow(),\n                p.DMA1_CH2.reborrow(),\n                p.DMA1_CH3.reborrow(),\n                Irqs,\n                Default::default(),\n            )\n            .unwrap();\n            defmt::assert_eq!(AFIO.mapr().read().usart3_remap(), 3);\n        }\n\n        // SPI3\n        {\n            // no remap SCK/MISO/MOSI\n            afio_registers_set_remap();\n            Spi::new_blocking(\n                p.SPI3.reborrow(),\n                p.PB3.reborrow(),\n                p.PB5.reborrow(),\n                p.PB4.reborrow(),\n                Default::default(),\n            );\n            defmt::assert_eq!(AFIO.mapr().read().spi3_remap(), false);\n        }\n        {\n            // no remap SCK/MOSI\n            afio_registers_set_remap();\n            Spi::new_blocking_txonly(\n                p.SPI3.reborrow(),\n                p.PB3.reborrow(),\n                p.PB5.reborrow(),\n                Default::default(),\n            );\n            defmt::assert_eq!(AFIO.mapr().read().spi3_remap(), false);\n        }\n        {\n            // no remap MOSI\n            afio_registers_set_remap();\n            Spi::new_blocking_txonly_nosck(p.SPI3.reborrow(), p.PB5.reborrow(), Default::default());\n            defmt::assert_eq!(AFIO.mapr().read().spi3_remap(), false);\n        }\n        {\n            // no remap SCK/MISO\n            afio_registers_set_remap();\n            Spi::new_blocking_rxonly(\n                p.SPI3.reborrow(),\n                p.PB3.reborrow(),\n                p.PB4.reborrow(),\n                Default::default(),\n            );\n            defmt::assert_eq!(AFIO.mapr().read().spi3_remap(), false);\n        }\n        {\n            // remap SCK/MISO/MOSI\n            reset_afio_registers();\n            Spi::new_blocking(\n                p.SPI3.reborrow(),\n                p.PC10.reborrow(),\n                p.PC12.reborrow(),\n                p.PC11.reborrow(),\n                Default::default(),\n            );\n\n            defmt::assert_eq!(AFIO.mapr().read().spi3_remap(), true);\n        }\n        {\n            // remap SCK/MOSI\n            reset_afio_registers();\n            Spi::new_blocking_txonly(\n                p.SPI3.reborrow(),\n                p.PC10.reborrow(),\n                p.PC12.reborrow(),\n                Default::default(),\n            );\n            defmt::assert_eq!(AFIO.mapr().read().spi3_remap(), true);\n        }\n        {\n            // remap MOSI\n            reset_afio_registers();\n            Spi::new_blocking_txonly_nosck(p.SPI3.reborrow(), p.PB5.reborrow(), Default::default());\n            defmt::assert_eq!(AFIO.mapr().read().spi3_remap(), true);\n        }\n        {\n            // remap SCK/MISO\n            reset_afio_registers();\n            Spi::new_blocking_rxonly(\n                p.SPI3.reborrow(),\n                p.PC10.reborrow(),\n                p.PC11.reborrow(),\n                Default::default(),\n            );\n            defmt::assert_eq!(AFIO.mapr().read().spi3_remap(), true);\n        }\n\n        // I2S3\n        {\n            // no remap SD/WS/CK/MCK\n            afio_registers_set_remap();\n            I2S::new_txonly(\n                p.SPI3.reborrow(),\n                p.PB5.reborrow(),\n                p.PA15.reborrow(),\n                p.PB3.reborrow(),\n                p.PC7.reborrow(),\n                p.DMA2_CH2.reborrow(),\n                &mut [0u16; 0],\n                Irqs,\n                Default::default(),\n            );\n            defmt::assert_eq!(AFIO.mapr().read().spi3_remap(), false);\n        }\n        {\n            // no remap SD/WS/CK\n            afio_registers_set_remap();\n            I2S::new_txonly_nomck(\n                p.SPI3.reborrow(),\n                p.PB5.reborrow(),\n                p.PA15.reborrow(),\n                p.PB3.reborrow(),\n                p.DMA2_CH2.reborrow(),\n                &mut [0u16; 0],\n                Irqs,\n                Default::default(),\n            );\n            defmt::assert_eq!(AFIO.mapr().read().spi3_remap(), false);\n        }\n        {\n            // no remap SD/WS/CK/MCK\n            afio_registers_set_remap();\n            I2S::new_rxonly(\n                p.SPI3.reborrow(),\n                p.PB4.reborrow(),\n                p.PA15.reborrow(),\n                p.PB3.reborrow(),\n                p.PC7.reborrow(),\n                p.DMA2_CH1.reborrow(),\n                &mut [0u16; 0],\n                Irqs,\n                Default::default(),\n            );\n            defmt::assert_eq!(AFIO.mapr().read().spi3_remap(), true);\n        }\n        {\n            // remap SD/WS/CK/MCK\n            reset_afio_registers();\n            I2S::new_txonly(\n                p.SPI3.reborrow(),\n                p.PC12.reborrow(),\n                p.PA4.reborrow(),\n                p.PC10.reborrow(),\n                p.PC7.reborrow(),\n                p.DMA2_CH2.reborrow(),\n                &mut [0u16; 0],\n                Irqs,\n                Default::default(),\n            );\n            defmt::assert_eq!(AFIO.mapr().read().spi3_remap(), true);\n        }\n        {\n            // remap SD/WS/CK\n            reset_afio_registers();\n            I2S::new_txonly_nomck(\n                p.SPI3.reborrow(),\n                p.PC12.reborrow(),\n                p.PA4.reborrow(),\n                p.PC10.reborrow(),\n                p.DMA2_CH2.reborrow(),\n                &mut [0u16; 0],\n                Irqs,\n                Default::default(),\n            );\n            defmt::assert_eq!(AFIO.mapr().read().spi3_remap(), true);\n        }\n        {\n            // remap SD/WS/CK/MCK\n            reset_afio_registers();\n            I2S::new_rxonly(\n                p.SPI3.reborrow(),\n                p.PC11.reborrow(),\n                p.PA4.reborrow(),\n                p.PC10.reborrow(),\n                p.PC7.reborrow(),\n                p.DMA2_CH1.reborrow(),\n                &mut [0u16; 0],\n                Irqs,\n                Default::default(),\n            );\n            defmt::assert_eq!(AFIO.mapr().read().spi3_remap(), true);\n        }\n\n        // CAN2\n        {\n            // no remap\n            afio_registers_set_remap();\n            Can::new(p.CAN2.reborrow(), p.PB12.reborrow(), p.PB13.reborrow(), Irqs);\n            defmt::assert_eq!(AFIO.mapr().read().can2_remap(), false);\n        }\n        {\n            // remap\n            reset_afio_registers();\n            Can::new(p.CAN2.reborrow(), p.PB5.reborrow(), p.PB6.reborrow(), Irqs);\n            defmt::assert_eq!(AFIO.mapr().read().can2_remap(), true);\n        }\n\n        // Ethernet\n        {\n            // no remap RMII\n            afio_registers_set_remap();\n            Ethernet::new(\n                &mut PacketQueue::<1, 1>::new(),\n                p.ETH.reborrow(),\n                Irqs,\n                p.PA1.reborrow(),\n                p.PA7.reborrow(),\n                p.PC4.reborrow(),\n                p.PC5.reborrow(),\n                p.PB12.reborrow(),\n                p.PB13.reborrow(),\n                p.PB11.reborrow(),\n                [0x00, 0x00, 0xDE, 0xAD, 0xBE, 0xEF],\n                p.ETH_SMA.reborrow(),\n                p.PA2.reborrow(),\n                p.PC1.reborrow(),\n            );\n            defmt::assert_eq!(AFIO.mapr().read().eth_remap(), false);\n        }\n        {\n            // no remap MII\n            afio_registers_set_remap();\n            Ethernet::new_mii(\n                &mut PacketQueue::<1, 1>::new(),\n                p.ETH.reborrow(),\n                Irqs,\n                p.PA1.reborrow(),\n                p.PC3.reborrow(),\n                p.PA7.reborrow(),\n                p.PC4.reborrow(),\n                p.PC5.reborrow(),\n                p.PB0.reborrow(),\n                p.PB1.reborrow(),\n                p.PB12.reborrow(),\n                p.PB13.reborrow(),\n                p.PC2.reborrow(),\n                p.PB8.reborrow(),\n                p.PB11.reborrow(),\n                [0x00, 0x00, 0xDE, 0xAD, 0xBE, 0xEF],\n                p.ETH_SMA.reborrow(),\n                p.PA2.reborrow(),\n                p.PC1.reborrow(),\n            );\n            defmt::assert_eq!(AFIO.mapr().read().eth_remap(), false);\n        }\n        {\n            // remap RMII\n            reset_afio_registers();\n            Ethernet::new(\n                &mut PacketQueue::<1, 1>::new(),\n                p.ETH.reborrow(),\n                Irqs,\n                p.PA1.reborrow(),\n                p.PD8.reborrow(),\n                p.PD9.reborrow(),\n                p.PD10.reborrow(),\n                p.PB12.reborrow(),\n                p.PB13.reborrow(),\n                p.PB11.reborrow(),\n                [0x00, 0x00, 0xDE, 0xAD, 0xBE, 0xEF],\n                p.ETH_SMA.reborrow(),\n                p.PA2.reborrow(),\n                p.PC1.reborrow(),\n            );\n            defmt::assert_eq!(AFIO.mapr().read().eth_remap(), true);\n        }\n        {\n            // remap MII\n            reset_afio_registers();\n            Ethernet::new_mii(\n                &mut PacketQueue::<1, 1>::new(),\n                p.ETH.reborrow(),\n                Irqs,\n                p.PA1.reborrow(),\n                p.PC3.reborrow(),\n                p.PD8.reborrow(),\n                p.PD9.reborrow(),\n                p.PD10.reborrow(),\n                p.PD11.reborrow(),\n                p.PD12.reborrow(),\n                p.PB12.reborrow(),\n                p.PB13.reborrow(),\n                p.PC2.reborrow(),\n                p.PB8.reborrow(),\n                p.PB11.reborrow(),\n                [0x00, 0x00, 0xDE, 0xAD, 0xBE, 0xEF],\n                p.ETH_SMA.reborrow(),\n                p.PA2.reborrow(),\n                p.PC1.reborrow(),\n            );\n            defmt::assert_eq!(AFIO.mapr().read().eth_remap(), true);\n        }\n\n        // CAN1\n        {\n            // no remap\n            afio_registers_set_remap();\n            Can::new(p.CAN1.reborrow(), p.PA11.reborrow(), p.PA12.reborrow(), Irqs);\n            defmt::assert_eq!(AFIO.mapr().read().can1_remap(), 0);\n        }\n        {\n            // partial remap\n            reset_afio_registers();\n            Can::new(p.CAN1.reborrow(), p.PB8.reborrow(), p.PB9.reborrow(), Irqs);\n            defmt::assert_eq!(AFIO.mapr().read().can1_remap(), 2);\n        }\n        {\n            // full remap\n            reset_afio_registers();\n            Can::new(p.CAN1.reborrow(), p.PD0.reborrow(), p.PD1.reborrow(), Irqs);\n            defmt::assert_eq!(AFIO.mapr().read().can1_remap(), 3);\n        }\n\n        // USART2\n        {\n            // no remap RX/TX/RTS/CTS\n            afio_registers_set_remap();\n            Uart::new_blocking_with_rtscts(\n                p.USART2.reborrow(),\n                p.PA3.reborrow(),\n                p.PA2.reborrow(),\n                p.PA1.reborrow(),\n                p.PA0.reborrow(),\n                Default::default(),\n            )\n            .unwrap();\n            defmt::assert_eq!(AFIO.mapr().read().usart2_remap(), false);\n        }\n        {\n            // no remap RX/TX\n            afio_registers_set_remap();\n            Uart::new_blocking(\n                p.USART2.reborrow(),\n                p.PA3.reborrow(),\n                p.PA2.reborrow(),\n                Default::default(),\n            )\n            .unwrap();\n            defmt::assert_eq!(AFIO.mapr().read().usart2_remap(), false);\n        }\n        {\n            // no remap TX\n            afio_registers_set_remap();\n            Uart::new_blocking_half_duplex(\n                p.USART2.reborrow(),\n                p.PA2.reborrow(),\n                Default::default(),\n                embassy_stm32::usart::HalfDuplexReadback::NoReadback,\n            )\n            .unwrap();\n            defmt::assert_eq!(AFIO.mapr().read().usart2_remap(), false);\n        }\n        {\n            // full remap RX/TX/RTS/CTS\n            reset_afio_registers();\n            Uart::new_blocking_with_rtscts(\n                p.USART2.reborrow(),\n                p.PD6.reborrow(),\n                p.PD5.reborrow(),\n                p.PD4.reborrow(),\n                p.PD3.reborrow(),\n                Default::default(),\n            )\n            .unwrap();\n            defmt::assert_eq!(AFIO.mapr().read().usart2_remap(), false);\n        }\n        {\n            // full remap RX/TX\n            reset_afio_registers();\n            Uart::new_blocking(\n                p.USART2.reborrow(),\n                p.PD6.reborrow(),\n                p.PD5.reborrow(),\n                Default::default(),\n            )\n            .unwrap();\n            defmt::assert_eq!(AFIO.mapr().read().usart2_remap(), false);\n        }\n        {\n            // full remap TX\n            reset_afio_registers();\n            Uart::new_blocking_half_duplex(\n                p.USART2.reborrow(),\n                p.PD5.reborrow(),\n                Default::default(),\n                embassy_stm32::usart::HalfDuplexReadback::NoReadback,\n            )\n            .unwrap();\n            defmt::assert_eq!(AFIO.mapr().read().usart2_remap(), true);\n        }\n\n        // USART1\n        {\n            // no remap RX/TX/RTS/CTS\n            afio_registers_set_remap();\n            Uart::new_blocking_with_rtscts(\n                p.USART1.reborrow(),\n                p.PA10.reborrow(),\n                p.PA9.reborrow(),\n                p.PA12.reborrow(),\n                p.PA11.reborrow(),\n                Default::default(),\n            )\n            .unwrap();\n            defmt::assert_eq!(AFIO.mapr().read().usart1_remap(), false);\n        }\n        {\n            // no remap RX/TX\n            afio_registers_set_remap();\n            Uart::new_blocking(\n                p.USART1.reborrow(),\n                p.PA10.reborrow(),\n                p.PA9.reborrow(),\n                Default::default(),\n            )\n            .unwrap();\n            defmt::assert_eq!(AFIO.mapr().read().usart1_remap(), false);\n        }\n        {\n            // no remap TX\n            afio_registers_set_remap();\n            Uart::new_blocking_half_duplex(\n                p.USART1.reborrow(),\n                p.PA9.reborrow(),\n                Default::default(),\n                embassy_stm32::usart::HalfDuplexReadback::NoReadback,\n            )\n            .unwrap();\n            defmt::assert_eq!(AFIO.mapr().read().usart1_remap(), false);\n        }\n        {\n            // remap RX/TX/RTS/CTS\n            reset_afio_registers();\n            Uart::new_blocking_with_rtscts(\n                p.USART1.reborrow(),\n                p.PB7.reborrow(),\n                p.PB6.reborrow(),\n                p.PA12.reborrow(),\n                p.PA11.reborrow(),\n                Default::default(),\n            )\n            .unwrap();\n            defmt::assert_eq!(AFIO.mapr().read().usart1_remap(), true);\n        }\n        {\n            // remap RX/TX\n            reset_afio_registers();\n            Uart::new_blocking(\n                p.USART1.reborrow(),\n                p.PB7.reborrow(),\n                p.PB6.reborrow(),\n                Default::default(),\n            )\n            .unwrap();\n            defmt::assert_eq!(AFIO.mapr().read().usart1_remap(), true);\n        }\n        {\n            // remap TX\n            reset_afio_registers();\n            Uart::new_blocking_half_duplex(\n                p.USART1.reborrow(),\n                p.PB6.reborrow(),\n                Default::default(),\n                embassy_stm32::usart::HalfDuplexReadback::NoReadback,\n            )\n            .unwrap();\n            defmt::assert_eq!(AFIO.mapr().read().usart1_remap(), true);\n        }\n\n        // TIM1\n        {\n            // full remap\n            reset_afio_registers();\n            SimplePwm::new(\n                p.TIM1.reborrow(),\n                Some(PwmPin::new(p.PE9.reborrow(), OutputType::PushPull)),\n                Some(PwmPin::new(p.PE11.reborrow(), OutputType::PushPull)),\n                None,\n                None,\n                khz(10),\n                Default::default(),\n            );\n            defmt::assert_eq!(AFIO.mapr().read().tim1_remap(), 3);\n        }\n    }\n}\n\n#[cfg(feature = \"afio-value-line\")]\nmod value_line {\n    use super::*;\n\n    pub fn run(p: &mut Peripherals) {\n        // TIM13\n        {\n            // no remap\n            reset_afio_registers();\n            SimplePwm::new(\n                p.TIM13.reborrow(),\n                Some(PwmPin::new(p.PC8.reborrow(), OutputType::PushPull)),\n                None,\n                None,\n                None,\n                khz(10),\n                Default::default(),\n            );\n            defmt::assert_eq!(AFIO.mapr2().read().tim13_remap(), false);\n        }\n        {\n            // remap\n            reset_afio_registers();\n            SimplePwm::new(\n                p.TIM13.reborrow(),\n                Some(PwmPin::new(p.PB0.reborrow(), OutputType::PushPull)),\n                None,\n                None,\n                None,\n                khz(10),\n                Default::default(),\n            );\n            defmt::assert_eq!(AFIO.mapr2().read().tim13_remap(), true);\n        }\n    }\n}\n\n#[cfg(not(feature = \"afio-connectivity-line\"))]\nmod connectivity_line {\n    use super::*;\n\n    pub fn run(_: &mut Peripherals) {}\n}\n\n#[cfg(not(feature = \"afio-value-line\"))]\nmod value_line {\n    use super::*;\n\n    pub fn run(_: &mut Peripherals) {}\n}\n\nfn reset_afio_registers() {\n    set_afio_registers(false, 0);\n}\n\nfn afio_registers_set_remap() {\n    set_afio_registers(true, 1);\n}\n\nfn set_afio_registers(bool_val: bool, num_val: u8) {\n    AFIO.mapr().modify(|w| {\n        w.set_swj_cfg(embassy_stm32::pac::afio::vals::SwjCfg::NO_OP);\n        w.set_can1_remap(num_val);\n        w.set_can2_remap(bool_val);\n        w.set_eth_remap(bool_val);\n        w.set_i2c1_remap(bool_val);\n        w.set_spi1_remap(bool_val);\n        w.set_spi3_remap(bool_val);\n        w.set_tim1_remap(num_val);\n        w.set_tim2_remap(num_val);\n        w.set_tim3_remap(num_val);\n        w.set_tim4_remap(bool_val);\n        w.set_usart1_remap(bool_val);\n        w.set_usart2_remap(bool_val);\n        w.set_usart3_remap(num_val);\n    });\n\n    AFIO.mapr2().modify(|w| {\n        w.set_cec_remap(bool_val);\n        w.set_tim9_remap(bool_val);\n        w.set_tim10_remap(bool_val);\n        w.set_tim11_remap(bool_val);\n        w.set_tim12_remap(bool_val);\n        w.set_tim13_remap(bool_val);\n        w.set_tim14_remap(bool_val);\n        w.set_tim15_remap(bool_val);\n        w.set_tim16_remap(bool_val);\n        w.set_tim17_remap(bool_val);\n    });\n}\n"
  },
  {
    "path": "tests/stm32/src/bin/can.rs",
    "content": "#![no_std]\n#![no_main]\n\n// required-features: can\n\n#[path = \"../common.rs\"]\nmod common;\nuse common::*;\nuse embassy_executor::Spawner;\nuse embassy_stm32::bind_interrupts;\nuse embassy_stm32::can::filter::Mask32;\nuse embassy_stm32::can::{Fifo, Rx0InterruptHandler, Rx1InterruptHandler, SceInterruptHandler, TxInterruptHandler};\nuse embassy_stm32::gpio::{Input, Pull};\nuse embassy_stm32::peripherals::CAN1;\nuse embassy_time::Duration;\nuse {defmt_rtt as _, panic_probe as _};\n\n#[path = \"../can_common.rs\"]\nmod can_common;\nuse can_common::*;\n\nbind_interrupts!(struct Irqs {\n    CAN1_RX0 => Rx0InterruptHandler<CAN1>;\n    CAN1_RX1 => Rx1InterruptHandler<CAN1>;\n    CAN1_SCE => SceInterruptHandler<CAN1>;\n    CAN1_TX => TxInterruptHandler<CAN1>;\n});\n\n#[cfg_attr(\n    feature = \"stop\",\n    embassy_executor::main(executor = \"embassy_stm32::executor::Executor\", entry = \"cortex_m_rt::entry\")\n)]\n#[cfg_attr(not(feature = \"stop\"), embassy_executor::main)]\nasync fn main(_spawner: Spawner) {\n    let p = init();\n    info!(\"Hello World!\");\n\n    let options = TestOptions {\n        max_latency: Duration::from_micros(1200),\n        max_buffered: 2,\n    };\n\n    let can = peri!(p, CAN);\n    let tx = peri!(p, CAN_TX);\n    let mut rx = peri!(p, CAN_RX);\n\n    // The next two lines are a workaround for testing without transceiver.\n    // To synchronise to the bus the RX input needs to see a high level.\n    // Use `mem::forget()` to release the borrow on the pin but keep the\n    // pull-up resistor enabled.\n    let rx_pin = Input::new(rx.reborrow(), Pull::Up);\n    core::mem::forget(rx_pin);\n\n    let mut can = embassy_stm32::can::Can::new(can, rx, tx, Irqs);\n\n    info!(\"Configuring can...\");\n\n    can.modify_filters().enable_bank(0, Fifo::Fifo0, Mask32::accept_all());\n\n    can.modify_config()\n        .set_loopback(true) // Receive own frames\n        .set_silent(true)\n        // .set_bit_timing(0x001c0003)\n        .set_bitrate(1_000_000);\n\n    can.enable().await;\n\n    info!(\"Can configured\");\n\n    run_can_tests(&mut can, &options).await;\n\n    // Test again with a split\n    let (mut tx, mut rx) = can.split();\n    run_split_can_tests(&mut tx, &mut rx, &options).await;\n\n    info!(\"Test OK\");\n\n    cortex_m::asm::bkpt();\n}\n"
  },
  {
    "path": "tests/stm32/src/bin/cordic.rs",
    "content": "// required-features: rng, cordic\n\n// Test Cordic driver, with Q1.31 format, Sin function, at 24 iterations (aka PRECISION = 6)\n\n#![no_std]\n#![no_main]\n\n#[path = \"../common.rs\"]\nmod common;\nuse common::*;\nuse embassy_executor::Spawner;\nuse embassy_stm32::cordic::utils;\nuse embassy_stm32::{cordic, rng};\nuse num_traits::Float;\nuse {defmt_rtt as _, panic_probe as _};\n\n/* input value control, can be changed */\n\nconst INPUT_U32_COUNT: usize = 9;\nconst INPUT_U8_COUNT: usize = 4 * INPUT_U32_COUNT;\n\n// Assume first calculation needs 2 arguments, the rest needs 1 argument.\n// And all calculations generate 2 results.\nconst OUTPUT_LENGTH: usize = (INPUT_U32_COUNT - 1) * 2;\n\n#[cfg_attr(\n    feature = \"stop\",\n    embassy_executor::main(executor = \"embassy_stm32::executor::Executor\", entry = \"cortex_m_rt::entry\")\n)]\n#[cfg_attr(not(feature = \"stop\"), embassy_executor::main)]\nasync fn main(_spawner: Spawner) {\n    let dp = init();\n\n    let irq = irqs!(UART);\n\n    //\n    // use RNG generate random Q1.31 value\n    //\n    // we don't generate floating-point value, since not all binary value are valid floating-point value,\n    // and Q1.31 only accept a fixed range of value.\n\n    let mut rng = rng::Rng::new(dp.RNG, irq);\n\n    let mut input_buf_u8 = [0u8; INPUT_U8_COUNT];\n    defmt::unwrap!(rng.async_fill_bytes(&mut input_buf_u8).await);\n\n    // convert every [u8; 4] to a u32, for a Q1.31 value\n    let mut input_q1_31 = unsafe { core::mem::transmute::<[u8; INPUT_U8_COUNT], [u32; INPUT_U32_COUNT]>(input_buf_u8) };\n\n    // ARG2 for Sin function should be inside [0, 1], set MSB to 0 of a Q1.31 value, will make sure it's no less than 0.\n    input_q1_31[1] &= !(1u32 << 31);\n\n    //\n    // CORDIC calculation\n    //\n\n    let mut output_q1_31 = [0u32; OUTPUT_LENGTH];\n\n    // setup Cordic driver with 2-arg, 2-result config for the initial call\n    let mut cordic = cordic::Cordic::new(\n        dp.CORDIC,\n        defmt::unwrap!(cordic::Config::new(\n            cordic::Function::Sin,\n            Default::default(),\n            Default::default(),\n        ))\n        .arg_count(cordic::AccessCount::Two)\n        .res_count(cordic::AccessCount::Two),\n    );\n\n    // calculate first result using blocking mode (2 args: ARG1 + ARG2)\n    let cnt0 = defmt::unwrap!(cordic.blocking_calc_32bit(&input_q1_31[..2], &mut output_q1_31));\n\n    // switch to 1-arg mode without resetting ARG2\n    cordic.set_access_counts(cordic::AccessCount::One, cordic::AccessCount::Two);\n\n    #[cfg(feature = \"stm32g491re\")]\n    let (mut write_dma, mut read_dma) = (dp.DMA1_CH4, dp.DMA1_CH5);\n\n    #[cfg(any(\n        feature = \"stm32h563zi\",\n        feature = \"stm32u585ai\",\n        feature = \"stm32u5a5zj\",\n        feature = \"stm32h7s3l8\"\n    ))]\n    let (mut write_dma, mut read_dma) = (dp.GPDMA1_CH0, dp.GPDMA1_CH1);\n\n    // calculate rest results using async mode (1 arg, reusing ARG2)\n    let cnt1 = defmt::unwrap!(\n        cordic\n            .async_calc_32bit(\n                write_dma.reborrow(),\n                read_dma.reborrow(),\n                irq,\n                &input_q1_31[2..],\n                &mut output_q1_31[cnt0..],\n            )\n            .await\n    );\n\n    // all output value length should be the same as our output buffer size\n    defmt::assert_eq!(cnt0 + cnt1, output_q1_31.len());\n\n    let mut cordic_result_f64 = [0.0f64; OUTPUT_LENGTH];\n\n    for (f64_val, u32_val) in cordic_result_f64.iter_mut().zip(output_q1_31) {\n        *f64_val = utils::q1_31_to_f64(u32_val);\n    }\n\n    //\n    // software calculation\n    //\n\n    let mut software_result_f64 = [0.0f64; OUTPUT_LENGTH];\n\n    let arg2 = utils::q1_31_to_f64(input_q1_31[1]);\n\n    for (&arg1, res) in input_q1_31\n        .iter()\n        .enumerate()\n        .filter_map(|(idx, val)| if idx != 1 { Some(val) } else { None })\n        .zip(software_result_f64.chunks_mut(2))\n    {\n        let arg1 = utils::q1_31_to_f64(arg1);\n\n        let (raw_res1, raw_res2) = (arg1 * core::f64::consts::PI).sin_cos();\n        (res[0], res[1]) = (raw_res1 * arg2, raw_res2 * arg2);\n    }\n\n    //\n    // check result are the same\n    //\n\n    for (cordic_res, software_res) in cordic_result_f64[..cnt0 + cnt1]\n        .chunks(2)\n        .zip(software_result_f64.chunks(2))\n    {\n        for (cord_res, soft_res) in cordic_res.iter().zip(software_res.iter()) {\n            // 2.0.powi(-19) is the max residual error for Sin function, in q1.31 format, with 24 iterations (aka PRECISION = 6)\n            defmt::assert!((cord_res - soft_res).abs() <= 2.0.powi(-19));\n        }\n    }\n\n    info!(\"Test OK\");\n    cortex_m::asm::bkpt();\n}\n"
  },
  {
    "path": "tests/stm32/src/bin/cryp.rs",
    "content": "// required-features: cryp\n#![no_std]\n#![no_main]\n\n#[path = \"../common.rs\"]\nmod common;\n\nuse aes_gcm::Aes128Gcm;\nuse aes_gcm::aead::heapless::Vec;\nuse aes_gcm::aead::{AeadInPlace, KeyInit};\nuse common::*;\nuse embassy_executor::Spawner;\nuse embassy_stm32::cryp::*;\nuse {defmt_rtt as _, panic_probe as _};\n\n#[cfg_attr(\n    feature = \"stop\",\n    embassy_executor::main(executor = \"embassy_stm32::executor::Executor\", entry = \"cortex_m_rt::entry\")\n)]\n#[cfg_attr(not(feature = \"stop\"), embassy_executor::main)]\nasync fn main(_spawner: Spawner) {\n    let p: embassy_stm32::Peripherals = init();\n\n    const PAYLOAD1: &[u8] = b\"payload data 1 ;zdfhzdfhS;GKJASBDG;ASKDJBAL,zdfhzdfhzdfhzdfhvljhb,jhbjhb,sdhsdghsdhsfhsghzdfhzdfhzdfhzdfdhsdthsthsdhsgaadfhhgkdgfuoyguoft6783567\";\n    const PAYLOAD2: &[u8] = b\"payload data 2 ;SKEzdfhzdfhzbhgvljhb,jhbjhb,sdhsdghsdhsfhsghshsfhshstsdthadfhsdfjhsfgjsfgjxfgjzdhgDFghSDGHjtfjtjszftjzsdtjhstdsdhsdhsdhsdhsdthsthsdhsgfh\";\n    const AAD1: &[u8] = b\"additional data 1 stdargadrhaethaethjatjatjaetjartjstrjsfkk;'jopofyuisrteytweTASTUIKFUKIXTRDTEREharhaeryhaterjartjarthaethjrtjarthaetrhartjatejatrjsrtjartjyt1\";\n    const AAD2: &[u8] = b\"additional data 2 stdhthsthsthsrthsrthsrtjdykjdukdyuldadfhsdghsdghsdghsadghjk'hioethjrtjarthaetrhartjatecfgjhzdfhgzdfhzdfghzdfhzdfhzfhjatrjsrtjartjytjfytjfyg\";\n\n    let in_dma = peri!(p, CRYP_IN_DMA);\n    let out_dma = peri!(p, CRYP_OUT_DMA);\n    let irq = irqs!(UART);\n\n    let mut hw_cryp = Cryp::new(p.CRYP, in_dma, out_dma, irq);\n    let key: [u8; 16] = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16];\n    let mut ciphertext: [u8; PAYLOAD1.len() + PAYLOAD2.len()] = [0; PAYLOAD1.len() + PAYLOAD2.len()];\n    let mut plaintext: [u8; PAYLOAD1.len() + PAYLOAD2.len()] = [0; PAYLOAD1.len() + PAYLOAD2.len()];\n    let iv: [u8; 12] = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12];\n\n    // Encrypt in hardware using AES-GCM 128-bit in blocking mode.\n    let aes_gcm = AesGcm::new(&key, &iv);\n    let mut gcm_encrypt = hw_cryp.start_blocking(&aes_gcm, Direction::Encrypt);\n    hw_cryp.aad_blocking(&mut gcm_encrypt, AAD1, false);\n    hw_cryp.aad_blocking(&mut gcm_encrypt, AAD2, true);\n    hw_cryp.payload_blocking(&mut gcm_encrypt, PAYLOAD1, &mut ciphertext[..PAYLOAD1.len()], false);\n    hw_cryp.payload_blocking(&mut gcm_encrypt, PAYLOAD2, &mut ciphertext[PAYLOAD1.len()..], true);\n    let encrypt_tag = hw_cryp.finish_blocking(gcm_encrypt);\n\n    // Decrypt in hardware using AES-GCM 128-bit in async (DMA) mode.\n    let mut gcm_decrypt = hw_cryp.start(&aes_gcm, Direction::Decrypt).await;\n    hw_cryp.aad(&mut gcm_decrypt, AAD1, false).await;\n    hw_cryp.aad(&mut gcm_decrypt, AAD2, true).await;\n    hw_cryp\n        .payload(&mut gcm_decrypt, &ciphertext, &mut plaintext, true)\n        .await;\n    let decrypt_tag = hw_cryp.finish(gcm_decrypt).await;\n\n    info!(\"AES-GCM Ciphertext: {:?}\", ciphertext);\n    info!(\"AES-GCM Plaintext: {:?}\", plaintext);\n    defmt::assert!(PAYLOAD1 == &plaintext[..PAYLOAD1.len()]);\n    defmt::assert!(PAYLOAD2 == &plaintext[PAYLOAD1.len()..]);\n    defmt::assert!(encrypt_tag == decrypt_tag);\n\n    // Encrypt in software using AES-GCM 128-bit\n    let mut payload_vec: Vec<u8, { PAYLOAD1.len() + PAYLOAD2.len() + 16 }> = Vec::from_slice(&PAYLOAD1).unwrap();\n    payload_vec.extend_from_slice(&PAYLOAD2).unwrap();\n    let cipher = Aes128Gcm::new(&key.into());\n    let mut aad: Vec<u8, { AAD1.len() + AAD2.len() }> = Vec::from_slice(&AAD1).unwrap();\n    aad.extend_from_slice(&AAD2).unwrap();\n    let _ = cipher.encrypt_in_place(&iv.into(), &aad, &mut payload_vec);\n\n    defmt::assert!(ciphertext == payload_vec[0..ciphertext.len()]);\n    defmt::assert!(encrypt_tag == payload_vec[ciphertext.len()..ciphertext.len() + encrypt_tag.len()]);\n\n    // Decrypt in software using AES-GCM 128-bit\n    cipher.decrypt_in_place(&iv.into(), &aad, &mut payload_vec).unwrap();\n\n    info!(\"Test OK\");\n    cortex_m::asm::bkpt();\n}\n"
  },
  {
    "path": "tests/stm32/src/bin/dac.rs",
    "content": "#![no_std]\n#![no_main]\n\n// required-features: dac\n\n#[path = \"../common.rs\"]\nmod common;\nuse core::f32::consts::PI;\n\nuse common::*;\nuse defmt::assert;\nuse embassy_executor::Spawner;\nuse embassy_stm32::adc::{Adc, SampleTime};\nuse embassy_stm32::dac::{DacChannel, Value};\nuse embassy_time::Timer;\nuse micromath::F32Ext;\nuse {defmt_rtt as _, panic_probe as _};\n\n#[cfg_attr(\n    feature = \"stop\",\n    embassy_executor::main(executor = \"embassy_stm32::executor::Executor\", entry = \"cortex_m_rt::entry\")\n)]\n#[cfg_attr(not(feature = \"stop\"), embassy_executor::main)]\nasync fn main(_spawner: Spawner) {\n    // Initialize the board and obtain a Peripherals instance\n    let p: embassy_stm32::Peripherals = init();\n\n    let adc = peri!(p, ADC);\n    let dac = peri!(p, DAC);\n    let dac_pin = peri!(p, DAC_PIN);\n    let mut adc_pin = unsafe { core::ptr::read(&dac_pin) };\n\n    let mut dac = DacChannel::new_blocking(dac, dac_pin);\n    let mut adc = Adc::new(adc);\n\n    #[cfg(feature = \"stm32h755zi\")]\n    let normalization_factor = 256;\n    #[cfg(any(feature = \"stm32f429zi\", feature = \"stm32f446re\", feature = \"stm32g071rb\"))]\n    let normalization_factor: i32 = 16;\n\n    dac.set(Value::Bit8(0));\n    // Now wait a little to obtain a stable value\n    Timer::after_millis(30).await;\n    let offset = adc.blocking_read(&mut adc_pin, SampleTime::from_bits(0));\n\n    for v in 0..=255 {\n        // First set the DAC output value\n        let dac_output_val = to_sine_wave(v);\n        dac.set(Value::Bit8(dac_output_val));\n\n        // Now wait a little to obtain a stable value\n        Timer::after_millis(30).await;\n\n        // Need to steal the peripherals here because PA4 is obviously in use already\n        let measured = adc.blocking_read(\n            &mut unsafe { embassy_stm32::Peripherals::steal() }.PA4,\n            SampleTime::from_bits(0),\n        );\n        // Calibrate and normalize the measurement to get close to the dac_output_val\n        let measured_normalized = ((measured as i32 - offset as i32) / normalization_factor) as i16;\n\n        //info!(\"value / measured: {} / {}\", dac_output_val, measured_normalized);\n\n        // The deviations are quite enormous but that does not matter since this is only a quick test\n        assert!((dac_output_val as i16 - measured_normalized).abs() < 15);\n    }\n\n    info!(\"Test OK\");\n    cortex_m::asm::bkpt();\n}\n\nfn to_sine_wave(v: u8) -> u8 {\n    if v >= 128 {\n        // top half\n        let r = PI * ((v - 128) as f32 / 128.0);\n        (r.sin() * 128.0 + 127.0) as u8\n    } else {\n        // bottom half\n        let r = PI + PI * (v as f32 / 128.0);\n        (r.sin() * 128.0 + 127.0) as u8\n    }\n}\n"
  },
  {
    "path": "tests/stm32/src/bin/dac_l1.rs",
    "content": "#![no_std]\n#![no_main]\n\n// required-features: stm32l152re\n\n#[path = \"../common.rs\"]\nmod common;\nuse core::f32::consts::PI;\n\nuse common::*;\nuse defmt::assert;\nuse embassy_executor::Spawner;\nuse embassy_stm32::adc::{Adc, SampleTime};\nuse embassy_stm32::dac::{DacChannel, Value};\nuse embassy_stm32::{bind_interrupts, peripherals};\nuse embassy_time::Timer;\nuse micromath::F32Ext;\nuse {defmt_rtt as _, panic_probe as _};\n\nbind_interrupts!(struct Irqs {\n    ADC1 => embassy_stm32::adc::InterruptHandler<peripherals::ADC1>;\n});\n\n#[cfg_attr(\n    feature = \"stop\",\n    embassy_executor::main(executor = \"embassy_stm32::executor::Executor\", entry = \"cortex_m_rt::entry\")\n)]\n#[cfg_attr(not(feature = \"stop\"), embassy_executor::main)]\nasync fn main(_spawner: Spawner) {\n    // Initialize the board and obtain a Peripherals instance\n    let p: embassy_stm32::Peripherals = init();\n\n    let adc = peri!(p, ADC);\n    let dac = peri!(p, DAC);\n    let dac_pin = peri!(p, DAC_PIN);\n    let mut adc_pin = unsafe { core::ptr::read(&dac_pin) };\n\n    let mut dac = DacChannel::new_blocking(dac, dac_pin);\n    let mut adc = Adc::new(adc, Irqs);\n\n    #[cfg(feature = \"stm32h755zi\")]\n    let normalization_factor = 256;\n    #[cfg(any(\n        feature = \"stm32f429zi\",\n        feature = \"stm32f446re\",\n        feature = \"stm32g071rb\",\n        feature = \"stm32l152re\",\n    ))]\n    let normalization_factor: i32 = 16;\n\n    dac.set(Value::Bit8(0));\n    // Now wait a little to obtain a stable value\n    Timer::after_millis(30).await;\n    let offset = adc.read(&mut adc_pin, SampleTime::from_bits(0)).await;\n\n    for v in 0..=255 {\n        // First set the DAC output value\n        let dac_output_val = to_sine_wave(v);\n        dac.set(Value::Bit8(dac_output_val));\n\n        // Now wait a little to obtain a stable value\n        Timer::after_millis(30).await;\n\n        // Need to steal the peripherals here because PA4 is obviously in use already\n        let measured = adc\n            .read(\n                &mut unsafe { embassy_stm32::Peripherals::steal() }.PA4,\n                SampleTime::from_bits(0),\n            )\n            .await;\n        // Calibrate and normalize the measurement to get close to the dac_output_val\n        let measured_normalized = ((measured as i32 - offset as i32) / normalization_factor) as i16;\n\n        info!(\"value / measured: {} / {}\", dac_output_val, measured_normalized);\n\n        // The deviations are quite enormous but that does not matter since this is only a quick test\n        assert!((dac_output_val as i16 - measured_normalized).abs() < 15);\n    }\n\n    info!(\"Test OK\");\n    cortex_m::asm::bkpt();\n}\n\nfn to_sine_wave(v: u8) -> u8 {\n    if v >= 128 {\n        // top half\n        let r = PI * ((v - 128) as f32 / 128.0);\n        (r.sin() * 128.0 + 127.0) as u8\n    } else {\n        // bottom half\n        let r = PI + PI * (v as f32 / 128.0);\n        (r.sin() * 128.0 + 127.0) as u8\n    }\n}\n"
  },
  {
    "path": "tests/stm32/src/bin/eeprom.rs",
    "content": "#![no_std]\n#![no_main]\n\n// required-features: eeprom\n\n#[path = \"../common.rs\"]\nmod common;\n\nuse common::*;\nuse defmt::assert_eq;\nuse embassy_executor::Spawner;\nuse embassy_stm32::flash::Flash;\nuse {defmt_rtt as _, panic_probe as _};\n\n#[cfg_attr(\n    feature = \"stop\",\n    embassy_executor::main(executor = \"embassy_stm32::executor::Executor\", entry = \"cortex_m_rt::entry\")\n)]\n#[cfg_attr(not(feature = \"stop\"), embassy_executor::main)]\nasync fn main(_spawner: Spawner) {\n    // Initialize the board and obtain a Peripherals instance\n    let p: embassy_stm32::Peripherals = init();\n\n    let mut f = Flash::new_blocking(p.FLASH);\n    const ADDR: u32 = 0x0;\n\n    unwrap!(f.eeprom_write_slice(ADDR, &[1, 2, 3, 4, 5, 6, 7, 8]));\n    let mut buf = [0u8; 8];\n    unwrap!(f.eeprom_read_slice(ADDR, &mut buf));\n    assert_eq!(&buf[..], &[1, 2, 3, 4, 5, 6, 7, 8]);\n\n    info!(\"Test OK\");\n    cortex_m::asm::bkpt();\n}\n"
  },
  {
    "path": "tests/stm32/src/bin/eth.rs",
    "content": "// required-features: eth\n#![no_std]\n#![no_main]\n\n#[path = \"../common.rs\"]\nmod common;\nuse common::*;\nuse embassy_executor::Spawner;\nuse embassy_net::StackResources;\nuse embassy_stm32::eth::{Ethernet, GenericPhy, PacketQueue, Sma};\nuse embassy_stm32::peripherals::{ETH, ETH_SMA};\nuse embassy_stm32::rng::Rng;\nuse embassy_stm32::{bind_interrupts, eth, peripherals, rng};\nuse static_cell::StaticCell;\nuse {defmt_rtt as _, panic_probe as _};\n\nteleprobe_meta::timeout!(120);\n\n#[cfg(not(any(feature = \"stm32h563zi\", feature = \"stm32f767zi\", feature = \"stm32f207zg\")))]\nbind_interrupts!(struct Irqs {\n    ETH => eth::InterruptHandler;\n    HASH_RNG => rng::InterruptHandler<peripherals::RNG>;\n});\n#[cfg(any(feature = \"stm32h563zi\", feature = \"stm32f767zi\", feature = \"stm32f207zg\"))]\nbind_interrupts!(struct Irqs {\n    ETH => eth::InterruptHandler;\n    RNG => rng::InterruptHandler<peripherals::RNG>;\n});\n\ntype Device = Ethernet<'static, ETH, GenericPhy<Sma<'static, ETH_SMA>>>;\n\n#[embassy_executor::task]\nasync fn net_task(mut runner: embassy_net::Runner<'static, Device>) -> ! {\n    runner.run().await\n}\n\n#[cfg_attr(\n    feature = \"stop\",\n    embassy_executor::main(executor = \"embassy_stm32::executor::Executor\", entry = \"cortex_m_rt::entry\")\n)]\n#[cfg_attr(not(feature = \"stop\"), embassy_executor::main)]\nasync fn main(spawner: Spawner) {\n    let p = init();\n    info!(\"Hello World!\");\n\n    // Generate random seed.\n    let mut rng = Rng::new(p.RNG, Irqs);\n    let mut seed = [0; 8];\n    rng.fill_bytes(&mut seed);\n    let seed = u64::from_le_bytes(seed);\n\n    // Ensure different boards get different MAC\n    // so running tests concurrently doesn't break (they're all in the same LAN)\n    #[cfg(feature = \"stm32f429zi\")]\n    let n = 1;\n    #[cfg(feature = \"stm32h755zi\")]\n    let n = 2;\n    #[cfg(feature = \"stm32h563zi\")]\n    let n = 3;\n    #[cfg(feature = \"stm32f767zi\")]\n    let n = 4;\n    #[cfg(feature = \"stm32f207zg\")]\n    let n = 5;\n    #[cfg(feature = \"stm32h753zi\")]\n    let n = 6;\n\n    let mac_addr = [0x00, n, 0xDE, 0xAD, 0xBE, 0xEF];\n\n    // F2 runs out of RAM\n    #[cfg(feature = \"stm32f207zg\")]\n    const PACKET_QUEUE_SIZE: usize = 2;\n    #[cfg(not(feature = \"stm32f207zg\"))]\n    const PACKET_QUEUE_SIZE: usize = 4;\n\n    static PACKETS: StaticCell<PacketQueue<PACKET_QUEUE_SIZE, PACKET_QUEUE_SIZE>> = StaticCell::new();\n\n    let device = Ethernet::new(\n        PACKETS.init(PacketQueue::<PACKET_QUEUE_SIZE, PACKET_QUEUE_SIZE>::new()),\n        p.ETH,\n        Irqs,\n        p.PA1,\n        p.PA7,\n        p.PC4,\n        p.PC5,\n        p.PG13,\n        #[cfg(not(feature = \"stm32h563zi\"))]\n        p.PB13,\n        #[cfg(feature = \"stm32h563zi\")]\n        p.PB15,\n        p.PG11,\n        mac_addr,\n        p.ETH_SMA,\n        p.PA2,\n        p.PC1,\n    );\n\n    let config = embassy_net::Config::dhcpv4(Default::default());\n    //let config = embassy_net::Config::ipv4_static(embassy_net::StaticConfigV4 {\n    //    address: Ipv4Cidr::new(Ipv4Address::new(10, 42, 0, 61), 24),\n    //    dns_servers: Vec::new(),\n    //    gateway: Some(Ipv4Address::new(10, 42, 0, 1)),\n    //});\n\n    // Init network stack\n    static RESOURCES: StaticCell<StackResources<2>> = StaticCell::new();\n    let (stack, runner) = embassy_net::new(device, config, RESOURCES.init(StackResources::new()), seed);\n\n    // Launch network task\n    spawner.spawn(unwrap!(net_task(runner)));\n\n    perf_client::run(\n        stack,\n        perf_client::Expected {\n            down_kbps: 1000,\n            up_kbps: 1000,\n            updown_kbps: 1000,\n        },\n    )\n    .await;\n\n    info!(\"Test OK\");\n    cortex_m::asm::bkpt();\n}\n"
  },
  {
    "path": "tests/stm32/src/bin/fdcan.rs",
    "content": "#![no_std]\n#![no_main]\n\n// required-features: fdcan\n\n#[path = \"../common.rs\"]\nmod common;\nuse common::*;\nuse embassy_executor::Spawner;\nuse embassy_stm32::peripherals::*;\nuse embassy_stm32::{Config, bind_interrupts, can};\nuse embassy_time::Duration;\nuse {defmt_rtt as _, panic_probe as _};\n\n#[path = \"../can_common.rs\"]\nmod can_common;\nuse can_common::*;\n\nbind_interrupts!(struct Irqs2 {\n    FDCAN2_IT0 => can::IT0InterruptHandler<FDCAN2>;\n    FDCAN2_IT1 => can::IT1InterruptHandler<FDCAN2>;\n});\nbind_interrupts!(struct Irqs1 {\n    FDCAN1_IT0 => can::IT0InterruptHandler<FDCAN1>;\n    FDCAN1_IT1 => can::IT1InterruptHandler<FDCAN1>;\n});\n\n#[cfg(feature = \"stm32h563zi\")]\nfn options() -> (Config, TestOptions) {\n    info!(\"H563 config\");\n    (\n        config(),\n        TestOptions {\n            max_latency: Duration::from_micros(1200),\n            max_buffered: 3,\n        },\n    )\n}\n\n#[cfg(any(feature = \"stm32h755zi\", feature = \"stm32h753zi\"))]\nfn options() -> (Config, TestOptions) {\n    use embassy_stm32::rcc;\n    info!(\"H75 config\");\n    let mut c = config();\n    c.rcc.hse = Some(rcc::Hse {\n        freq: embassy_stm32::time::Hertz(25_000_000),\n        mode: rcc::HseMode::Oscillator,\n    });\n    c.rcc.mux.fdcansel = rcc::mux::Fdcansel::HSE;\n    (\n        c,\n        TestOptions {\n            max_latency: Duration::from_micros(1200),\n            max_buffered: 3,\n        },\n    )\n}\n\n#[cfg(any(feature = \"stm32h7a3zi\"))]\nfn options() -> (Config, TestOptions) {\n    use embassy_stm32::rcc;\n    info!(\"H7a config\");\n    let mut c = config();\n    c.rcc.hse = Some(rcc::Hse {\n        freq: embassy_stm32::time::Hertz(25_000_000),\n        mode: rcc::HseMode::Oscillator,\n    });\n    c.rcc.mux.fdcansel = rcc::mux::Fdcansel::HSE;\n    (\n        c,\n        TestOptions {\n            max_latency: Duration::from_micros(1200),\n            max_buffered: 3,\n        },\n    )\n}\n\n#[cfg(any(feature = \"stm32h7s3l8\"))]\nfn options() -> (Config, TestOptions) {\n    use embassy_stm32::rcc;\n    let mut c = config();\n    c.rcc.mux.fdcansel = rcc::mux::Fdcansel::HSE;\n    (\n        c,\n        TestOptions {\n            max_latency: Duration::from_micros(1200),\n            max_buffered: 3,\n        },\n    )\n}\n\n#[cfg(any(feature = \"stm32g491re\"))]\nfn options() -> (Config, TestOptions) {\n    info!(\"G4 config\");\n    (\n        config(),\n        TestOptions {\n            max_latency: Duration::from_micros(500),\n            max_buffered: 6,\n        },\n    )\n}\n\n#[cfg_attr(\n    feature = \"stop\",\n    embassy_executor::main(executor = \"embassy_stm32::executor::Executor\", entry = \"cortex_m_rt::entry\")\n)]\n#[cfg_attr(not(feature = \"stop\"), embassy_executor::main)]\nasync fn main(_spawner: Spawner) {\n    //let peripherals = init();\n\n    let (config, options) = options();\n    let peripherals = init_with_config(config);\n\n    let mut can = can::CanConfigurator::new(peripherals.FDCAN1, peripherals.PB8, peripherals.PB9, Irqs1);\n    let mut can2 = can::CanConfigurator::new(peripherals.FDCAN2, peripherals.PB12, peripherals.PB13, Irqs2);\n\n    // 250k bps\n    can.set_bitrate(250_000);\n    can2.set_bitrate(250_000);\n\n    can.properties().set_extended_filter(\n        can::filter::ExtendedFilterSlot::_0,\n        can::filter::ExtendedFilter::accept_all_into_fifo1(),\n    );\n    can2.properties().set_extended_filter(\n        can::filter::ExtendedFilterSlot::_0,\n        can::filter::ExtendedFilter::accept_all_into_fifo1(),\n    );\n\n    let mut can = can.into_internal_loopback_mode();\n    let mut can2 = can2.into_internal_loopback_mode();\n\n    run_can_tests(&mut can, &options).await;\n    run_can_tests(&mut can2, &options).await;\n\n    info!(\"CAN Configured\");\n\n    // Test again with a split\n    let (mut tx, mut rx, _props) = can.split();\n    let (mut tx2, mut rx2, _props) = can2.split();\n    run_split_can_tests(&mut tx, &mut rx, &options).await;\n    run_split_can_tests(&mut tx2, &mut rx2, &options).await;\n\n    info!(\"Test OK\");\n    cortex_m::asm::bkpt();\n}\n"
  },
  {
    "path": "tests/stm32/src/bin/gpio.rs",
    "content": "#![no_std]\n#![no_main]\n#[path = \"../common.rs\"]\nmod common;\n\nuse common::*;\nuse defmt::assert;\nuse embassy_executor::Spawner;\nuse embassy_stm32::gpio::{Flex, Input, Level, Output, OutputOpenDrain, Pull, Speed};\n\n#[cfg_attr(\n    feature = \"stop\",\n    embassy_executor::main(executor = \"embassy_stm32::executor::Executor\", entry = \"cortex_m_rt::entry\")\n)]\n#[cfg_attr(not(feature = \"stop\"), embassy_executor::main)]\nasync fn main(_spawner: Spawner) {\n    let p = init();\n    info!(\"Hello World!\");\n\n    // Arduino pins D0 and D1\n    // They're connected together with a 1K resistor.\n    let mut a = peri!(p, UART_RX);\n    let mut b = peri!(p, UART_TX);\n\n    // Test initial output\n    {\n        let b = Input::new(b.reborrow(), Pull::None);\n\n        {\n            let a = Output::new(a.reborrow(), Level::Low, Speed::Low);\n            delay();\n            assert!(b.is_low());\n            assert!(!b.is_high());\n            assert!(a.is_set_low());\n            assert!(!a.is_set_high());\n        }\n        {\n            let mut a = Output::new(a.reborrow(), Level::High, Speed::Low);\n            delay();\n            assert!(!b.is_low());\n            assert!(b.is_high());\n            assert!(!a.is_set_low());\n            assert!(a.is_set_high());\n\n            // Test is_set_low / is_set_high\n            a.set_low();\n            delay();\n            assert!(b.is_low());\n            assert!(a.is_set_low());\n            assert!(!a.is_set_high());\n\n            a.set_high();\n            delay();\n            assert!(b.is_high());\n            assert!(!a.is_set_low());\n            assert!(a.is_set_high());\n\n            // Test toggle\n            a.toggle();\n            delay();\n            assert!(b.is_low());\n            assert!(a.is_set_low());\n            assert!(!a.is_set_high());\n\n            a.toggle();\n            delay();\n            assert!(b.is_high());\n            assert!(!a.is_set_low());\n            assert!(a.is_set_high());\n        }\n    }\n\n    // Test input no pull\n    {\n        let b = Input::new(b.reborrow(), Pull::None);\n        // no pull, the status is undefined\n\n        let mut a = Output::new(a.reborrow(), Level::Low, Speed::Low);\n        delay();\n        assert!(b.is_low());\n        a.set_high();\n        delay();\n        assert!(b.is_high());\n    }\n\n    // Test input pulldown\n    {\n        let b = Input::new(b.reborrow(), Pull::Down);\n        delay();\n        assert!(b.is_low());\n\n        let mut a = Output::new(a.reborrow(), Level::Low, Speed::Low);\n        delay();\n        assert!(b.is_low());\n        a.set_high();\n        delay();\n        assert!(b.is_high());\n    }\n\n    // Test input pullup\n    {\n        let b = Input::new(b.reborrow(), Pull::Up);\n        delay();\n        assert!(b.is_high());\n\n        let mut a = Output::new(a.reborrow(), Level::Low, Speed::Low);\n        delay();\n        assert!(b.is_low());\n        a.set_high();\n        delay();\n        assert!(b.is_high());\n    }\n\n    // Test output open drain\n    {\n        let b = Input::new(b.reborrow(), Pull::Down);\n        // no pull, the status is undefined\n\n        let mut a = OutputOpenDrain::new(a.reborrow(), Level::Low, Speed::Low);\n        delay();\n        assert!(b.is_low());\n        a.set_high(); // High-Z output\n        delay();\n        assert!(b.is_low());\n    }\n\n    // FLEX\n    // Test initial output\n    {\n        //Flex pin configured as input\n        let mut b = Flex::new(b.reborrow());\n        b.set_as_input(Pull::None);\n\n        {\n            //Flex pin configured as output\n            let mut a = Flex::new(a.reborrow()); //Flex pin configured as output\n            a.set_low(); // Pin state must be set before configuring the pin, thus we avoid unknown state\n            a.set_as_output(Speed::Low);\n            delay();\n            assert!(b.is_low());\n        }\n        {\n            //Flex pin configured as output\n            let mut a = Flex::new(a.reborrow());\n            a.set_high();\n            a.set_as_output(Speed::Low);\n\n            delay();\n            assert!(b.is_high());\n        }\n    }\n\n    // Test input no pull\n    {\n        let mut b = Flex::new(b.reborrow());\n        b.set_as_input(Pull::None); // no pull, the status is undefined\n\n        let mut a = Flex::new(a.reborrow());\n        a.set_low();\n        a.set_as_output(Speed::Low);\n\n        delay();\n        assert!(b.is_low());\n        a.set_high();\n        delay();\n        assert!(b.is_high());\n    }\n\n    // Test input pulldown\n    {\n        let mut b = Flex::new(b.reborrow());\n        b.set_as_input(Pull::Down);\n        delay();\n        assert!(b.is_low());\n\n        let mut a = Flex::new(a.reborrow());\n        a.set_low();\n        a.set_as_output(Speed::Low);\n        delay();\n        assert!(b.is_low());\n        a.set_high();\n        delay();\n        assert!(b.is_high());\n    }\n\n    // Test input pullup\n    {\n        let mut b = Flex::new(b.reborrow());\n        b.set_as_input(Pull::Up);\n        delay();\n        assert!(b.is_high());\n\n        let mut a = Flex::new(a.reborrow());\n        a.set_high();\n        a.set_as_output(Speed::Low);\n        delay();\n        assert!(b.is_high());\n        a.set_low();\n        delay();\n        assert!(b.is_low());\n    }\n\n    // Test output open drain\n    {\n        let mut b = Flex::new(b.reborrow());\n        b.set_as_input(Pull::Down);\n\n        let mut a = Flex::new(a.reborrow());\n        a.set_low();\n        a.set_as_input_output(Speed::Low);\n        delay();\n        assert!(b.is_low());\n        a.set_high(); // High-Z output\n        delay();\n        assert!(b.is_low());\n    }\n\n    info!(\"Test OK\");\n    cortex_m::asm::bkpt();\n}\n\nfn delay() {\n    #[cfg(any(\n        feature = \"stm32h755zi\",\n        feature = \"stm32h753zi\",\n        feature = \"stm32h7a3zi\",\n        feature = \"stm32h7s3l8\"\n    ))]\n    cortex_m::asm::delay(9000);\n    cortex_m::asm::delay(1000);\n}\n"
  },
  {
    "path": "tests/stm32/src/bin/hash.rs",
    "content": "// required-features: hash\n#![no_std]\n#![no_main]\n\n#[path = \"../common.rs\"]\nmod common;\nuse common::*;\nuse embassy_executor::Spawner;\nuse embassy_stm32::hash::*;\nuse embassy_stm32::mode::Blocking;\nuse embassy_stm32::{bind_interrupts, hash, peripherals};\nuse hmac::{Hmac, Mac};\nuse sha2::{Digest, Sha224, Sha256};\nuse {defmt_rtt as _, panic_probe as _};\n\ntype HmacSha256 = Hmac<Sha256>;\n\n#[cfg(any(feature = \"stm32l4a6zg\", feature = \"stm32h755zi\", feature = \"stm32h753zi\"))]\nbind_interrupts!(struct Irqs {\n   HASH_RNG => hash::InterruptHandler<peripherals::HASH>;\n});\n\n#[cfg(any(\n    feature = \"stm32wba52cg\",\n    feature = \"stm32wba65ri\",\n    feature = \"stm32l552ze\",\n    feature = \"stm32h563zi\",\n    feature = \"stm32h503rb\",\n    feature = \"stm32u5a5zj\",\n    feature = \"stm32u585ai\",\n    feature = \"stm32h7s3l8\"\n))]\nbind_interrupts!(struct Irqs {\n    HASH => hash::InterruptHandler<peripherals::HASH>;\n});\n\nfn test_interrupt(hw_hasher: &mut Hash<'_, peripherals::HASH, Blocking>) {\n    let test_1: &[u8] = b\"as;dfhaslfhas;oifvnasd;nifvnhasd;nifvhndlkfghsd;nvfnahssdfgsdafgsasdfasdfasdfasdfasdfghjklmnbvcalskdjghalskdjgfbaslkdjfgbalskdjgbalskdjbdfhsdfhsfghsfghfgh\";\n    let test_2: &[u8] = b\"fdhalksdjfhlasdjkfhalskdjfhgal;skdjfgalskdhfjgalskdjfglafgadfgdfgdafgaadsfgfgdfgadrgsyfthxfgjfhklhjkfgukhulkvhlvhukgfhfsrghzdhxyfufynufyuszeradrtydyytserr\";\n    let test_3: &[u8] = b\"a.ewtkluGWEBR.KAJRBTA,RMNRBG,FDMGB.kger.tkasjrbt.akrjtba.krjtba.ktmyna,nmbvtyliasd;gdrtba,sfvs.kgjzshd.gkbsr.tksejb.SDkfBSE.gkfgb>ESkfbSE>gkJSBESE>kbSE>fk\";\n\n    // Start an SHA-256 digest.\n    let mut sha256context = hw_hasher.start(Algorithm::SHA256, DataType::Width8, None);\n    hw_hasher.update_blocking(&mut sha256context, test_1);\n\n    // Interrupt the SHA-256 digest to compute an SHA-224 digest.\n    let mut sha224context = hw_hasher.start(Algorithm::SHA224, DataType::Width8, None);\n    hw_hasher.update_blocking(&mut sha224context, test_3);\n    let mut sha224_digest_buffer: [u8; 28] = [0; 28];\n    let _ = hw_hasher.finish_blocking(sha224context, &mut sha224_digest_buffer);\n\n    // Finish the SHA-256 digest.\n    hw_hasher.update_blocking(&mut sha256context, test_2);\n    let mut sha256_digest_buffer: [u8; 32] = [0; 32];\n    let _ = hw_hasher.finish_blocking(sha256context, &mut sha256_digest_buffer);\n\n    // Compute the SHA-256 digest in software.\n    let mut sw_sha256_hasher = Sha256::new();\n    sw_sha256_hasher.update(test_1);\n    sw_sha256_hasher.update(test_2);\n    let sw_sha256_digest = sw_sha256_hasher.finalize();\n\n    //Compute the SHA-224 digest in software.\n    let mut sw_sha224_hasher = Sha224::new();\n    sw_sha224_hasher.update(test_3);\n    let sw_sha224_digest = sw_sha224_hasher.finalize();\n\n    // Compare the SHA-256 digests.\n    info!(\"Hardware SHA-256 Digest: {:?}\", sha256_digest_buffer);\n    info!(\"Software SHA-256 Digest: {:?}\", sw_sha256_digest[..]);\n    defmt::assert!(sha256_digest_buffer == sw_sha256_digest[..]);\n\n    // Compare the SHA-224 digests.\n    info!(\"Hardware SHA-256 Digest: {:?}\", sha224_digest_buffer);\n    info!(\"Software SHA-256 Digest: {:?}\", sw_sha224_digest[..]);\n    defmt::assert!(sha224_digest_buffer == sw_sha224_digest[..]);\n\n    let hmac_key: [u8; 64] = [0x55; 64];\n\n    // Compute HMAC in hardware.\n    let mut sha256hmac_context = hw_hasher.start(Algorithm::SHA256, DataType::Width8, Some(&hmac_key));\n    hw_hasher.update_blocking(&mut sha256hmac_context, test_1);\n    hw_hasher.update_blocking(&mut sha256hmac_context, test_2);\n    let mut hw_hmac: [u8; 32] = [0; 32];\n    hw_hasher.finish_blocking(sha256hmac_context, &mut hw_hmac);\n\n    // Compute HMAC in software.\n    let mut sw_mac = HmacSha256::new_from_slice(&hmac_key).unwrap();\n    sw_mac.update(test_1);\n    sw_mac.update(test_2);\n    let sw_hmac = sw_mac.finalize().into_bytes();\n\n    info!(\"Hardware HMAC: {:?}\", hw_hmac);\n    info!(\"Software HMAC: {:?}\", sw_hmac[..]);\n    defmt::assert!(hw_hmac == sw_hmac[..]);\n}\n\n// This uses sha512, so only supported on hash_v3 and up\n#[cfg(feature = \"hash-v34\")]\nfn test_sizes(hw_hasher: &mut Hash<'_, peripherals::HASH, Blocking>) {\n    let in1 = b\"4BPuGudaDK\";\n    let in2 = b\"cfFIGf0XSNhFBQ5LaIqzjnRKDRkoWweJI06HLUcicIUGjpuDNfOTQNSrRxDoveDPlazeZtt06SIYO5CvHvsJ98XSfO9yJEMHoDpDAmNQtwZOPlKmdiagRXsJ7w7IjdKpQH6I2t\";\n\n    for i in 1..10 {\n        // sha512 block size is 128, so test around there\n        for j in [1, 1, 2, 3, 4, 5, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133] {\n            info!(\"test_sizes i {} j {}\", i, j);\n            let mut sw = sha2::Sha512::new();\n            let mut ctx = hw_hasher.start(Algorithm::SHA512, DataType::Width8, None);\n\n            sw.update(&in1[..i]);\n            sw.update(&in2[..j]);\n            hw_hasher.update_blocking(&mut ctx, &in1[..i]);\n            hw_hasher.update_blocking(&mut ctx, &in2[..j]);\n\n            let sw_digest = sw.finalize();\n            let mut hw_digest = [0u8; 64];\n            hw_hasher.finish_blocking(ctx, &mut hw_digest);\n            info!(\"Hardware: {:?}\", hw_digest);\n            info!(\"Software: {:?}\", sw_digest[..]);\n            defmt::assert!(hw_digest == *sw_digest);\n        }\n    }\n}\n\n#[cfg_attr(\n    feature = \"stop\",\n    embassy_executor::main(executor = \"embassy_stm32::executor::Executor\", entry = \"cortex_m_rt::entry\")\n)]\n#[cfg_attr(not(feature = \"stop\"), embassy_executor::main)]\nasync fn main(_spawner: Spawner) {\n    let p: embassy_stm32::Peripherals = init();\n    let mut hw_hasher = Hash::new_blocking(p.HASH, Irqs);\n\n    test_interrupt(&mut hw_hasher);\n    // Run it a second time to check hash-after-hmac\n    test_interrupt(&mut hw_hasher);\n\n    #[cfg(feature = \"hash-v34\")]\n    test_sizes(&mut hw_hasher);\n\n    info!(\"Test OK\");\n    cortex_m::asm::bkpt();\n}\n"
  },
  {
    "path": "tests/stm32/src/bin/hsem.rs",
    "content": "// required-features: hsem\n#![no_std]\n#![no_main]\n\n#[path = \"../common.rs\"]\nmod common;\n\nuse common::*;\nuse embassy_executor::Spawner;\nuse embassy_stm32::bind_interrupts;\nuse embassy_stm32::hsem::{HardwareSemaphore, HardwareSemaphoreInterruptHandler};\nuse embassy_stm32::peripherals::HSEM;\n\nbind_interrupts!(struct Irqs{\n    HSEM => HardwareSemaphoreInterruptHandler<HSEM>;\n});\n\n#[cfg_attr(\n    feature = \"stop\",\n    embassy_executor::main(executor = \"embassy_stm32::executor::Executor\", entry = \"cortex_m_rt::entry\")\n)]\n#[cfg_attr(not(feature = \"stop\"), embassy_executor::main)]\nasync fn main(_spawner: Spawner) {\n    let p: embassy_stm32::Peripherals = init();\n\n    let hsem = HardwareSemaphore::new(p.HSEM, Irqs);\n\n    //    if hsem.channel_for(SemaphoreNumber::Channel5).is_semaphore_locked() {\n    //        defmt::panic!(\"Semaphore 5 already locked!\")\n    //    }\n    //\n    //    hsem.channel_for(SemaphoreNumber::Channel5).one_step_lock().unwrap();\n    //    hsem.channel_for(SemaphoreNumber::Channel1).two_step_lock(0).unwrap();\n    //\n    //    hsem.channel_for(SemaphoreNumber::Channel5).unlock(0);\n\n    #[cfg(feature = \"stm32wb55rg\")]\n    let [_channel1, _channel2, mut channel5, _channel6] = hsem.split();\n    #[cfg(feature = \"stm32wl55jc\")]\n    let [_channel1, _channel2, _channel4, mut channel5, _channel6] = hsem.split();\n    #[cfg(not(any(feature = \"stm32wb55rg\", feature = \"stm32wl55jc\")))]\n    let [_channel1, _channel2, _channel3, _channel4, mut channel5, _channel6] = hsem.split();\n\n    info!(\"Locking channel 5\");\n\n    let mutex = channel5.lock(0).await;\n\n    info!(\"Locked channel 5\");\n\n    drop(mutex);\n\n    info!(\"Unlocked channel 5\");\n\n    info!(\"Test OK\");\n    cortex_m::asm::bkpt();\n}\n"
  },
  {
    "path": "tests/stm32/src/bin/rng.rs",
    "content": "// required-features: rng\n#![no_std]\n#![no_main]\n\n#[path = \"../common.rs\"]\nmod common;\nuse common::*;\nuse embassy_executor::Spawner;\nuse embassy_stm32::rng::Rng;\nuse embassy_stm32::{bind_interrupts, peripherals, rng};\nuse {defmt_rtt as _, panic_probe as _};\n\n#[cfg(any(\n    feature = \"stm32l4a6zg\",\n    feature = \"stm32h755zi\",\n    feature = \"stm32h753zi\",\n    feature = \"stm32f429zi\"\n))]\nbind_interrupts!(struct Irqs {\n   HASH_RNG => rng::InterruptHandler<peripherals::RNG>;\n});\n#[cfg(any(feature = \"stm32l073rz\"))]\nbind_interrupts!(struct Irqs {\n   RNG_LPUART1 => rng::InterruptHandler<peripherals::RNG>;\n});\n#[cfg(any(feature = \"stm32u083rc\"))]\nbind_interrupts!(struct Irqs {\n   RNG_CRYP => rng::InterruptHandler<peripherals::RNG>;\n});\n#[cfg(not(any(\n    feature = \"stm32l4a6zg\",\n    feature = \"stm32l073rz\",\n    feature = \"stm32h755zi\",\n    feature = \"stm32h753zi\",\n    feature = \"stm32f429zi\",\n    feature = \"stm32u083rc\"\n)))]\nbind_interrupts!(struct Irqs {\n   RNG => rng::InterruptHandler<peripherals::RNG>;\n});\n\n#[cfg_attr(\n    feature = \"stop\",\n    embassy_executor::main(executor = \"embassy_stm32::executor::Executor\", entry = \"cortex_m_rt::entry\")\n)]\n#[cfg_attr(not(feature = \"stop\"), embassy_executor::main)]\nasync fn main(_spawner: Spawner) {\n    let p: embassy_stm32::Peripherals = init();\n\n    let mut rng = Rng::new(p.RNG, Irqs);\n\n    let mut buf1 = [0u8; 16];\n    unwrap!(rng.async_fill_bytes(&mut buf1).await);\n    info!(\"random bytes: {:02x}\", buf1);\n\n    let mut buf2 = [0u8; 16];\n    unwrap!(rng.async_fill_bytes(&mut buf2).await);\n    info!(\"random bytes: {:02x}\", buf2);\n\n    defmt::assert!(buf1 != buf2);\n\n    info!(\"Test OK\");\n    cortex_m::asm::bkpt();\n}\n"
  },
  {
    "path": "tests/stm32/src/bin/rtc.rs",
    "content": "// required-features: chrono\n\n#![no_std]\n#![no_main]\n#[path = \"../common.rs\"]\nmod common;\n\nuse chrono::{NaiveDate, NaiveDateTime};\nuse common::*;\nuse defmt::assert;\nuse embassy_executor::Spawner;\nuse embassy_stm32::rcc::LsConfig;\n#[cfg(feature = \"stop\")]\nuse embassy_stm32::rtc::Rtc;\n#[cfg(not(feature = \"stop\"))]\nuse embassy_stm32::rtc::{Rtc, RtcConfig};\nuse embassy_time::Timer;\n#[cfg_attr(\n    feature = \"stop\",\n    embassy_executor::main(executor = \"embassy_stm32::executor::Executor\", entry = \"cortex_m_rt::entry\")\n)]\n#[cfg_attr(not(feature = \"stop\"), embassy_executor::main)]\nasync fn main(_spawner: Spawner) {\n    let mut config = config();\n    config.rcc.ls = LsConfig::default_lse();\n\n    let p = init_with_config(config);\n    info!(\"Hello World!\");\n\n    let now = NaiveDate::from_ymd_opt(2020, 5, 15)\n        .unwrap()\n        .and_hms_opt(10, 30, 15)\n        .unwrap();\n\n    #[cfg(not(feature = \"stop\"))]\n    let (mut rtc, time_provider) = Rtc::new(p.RTC, RtcConfig::default());\n\n    #[cfg(feature = \"stop\")]\n    let (rtc, time_provider) = Rtc::new(p.RTC);\n\n    #[cfg(not(feature = \"stop\"))]\n    rtc.set_datetime(now.into()).expect(\"datetime not set\");\n\n    #[cfg(feature = \"stop\")]\n    critical_section::with(|cs| {\n        rtc.borrow_mut(cs).set_datetime(now.into()).expect(\"datetime not set\");\n    });\n\n    info!(\"Waiting 5 seconds\");\n    Timer::after_millis(5000).await;\n\n    let then: NaiveDateTime = time_provider.now().unwrap().into();\n\n    let seconds = (then - now).num_seconds();\n\n    info!(\"measured = {}\", seconds);\n\n    assert!(seconds > 3 && seconds < 7);\n\n    info!(\"Test OK\");\n    cortex_m::asm::bkpt();\n}\n"
  },
  {
    "path": "tests/stm32/src/bin/sdmmc.rs",
    "content": "// required-features: sdmmc\n#![no_std]\n#![no_main]\n#[path = \"../common.rs\"]\nmod common;\n\nuse common::*;\nuse defmt::assert_eq;\nuse embassy_executor::Spawner;\nuse embassy_stm32::sdmmc::Sdmmc;\nuse embassy_stm32::sdmmc::sd::{CmdBlock, DataBlock, StorageDevice};\nuse embassy_stm32::time::mhz;\nuse embassy_stm32::{bind_interrupts, peripherals, sdmmc};\nuse {defmt_rtt as _, panic_probe as _};\n\nbind_interrupts!(struct Irqs {\n    SDIO => sdmmc::InterruptHandler<peripherals::SDIO>;\n    DMA2_STREAM3 => embassy_stm32::dma::InterruptHandler<peripherals::DMA2_CH3>;\n});\n\n#[cfg_attr(\n    feature = \"stop\",\n    embassy_executor::main(executor = \"embassy_stm32::executor::Executor\", entry = \"cortex_m_rt::entry\")\n)]\n#[cfg_attr(not(feature = \"stop\"), embassy_executor::main)]\nasync fn main(_spawner: Spawner) {\n    info!(\"Hello World!\");\n\n    let p = init();\n\n    let (mut sdmmc, mut dma, mut clk, mut cmd, mut d0, mut d1, mut d2, mut d3) =\n        (p.SDIO, p.DMA2_CH3, p.PC12, p.PD2, p.PC8, p.PC9, p.PC10, p.PC11);\n\n    // Arbitrary block index\n    let block_idx = 16;\n\n    let mut pattern1 = DataBlock::new();\n    let mut pattern2 = DataBlock::new();\n    for i in 0..512 {\n        pattern1[i] = i as u8;\n        pattern2[i] = !i as u8;\n    }\n    let patterns = [pattern1.clone(), pattern2.clone()];\n\n    let mut block = DataBlock::new();\n    let mut blocks = [DataBlock::new(), DataBlock::new()];\n    {\n        // ======== Try 4bit. ==============\n        info!(\"initializing in 4-bit mode...\");\n        let mut s = Sdmmc::new_4bit(\n            sdmmc.reborrow(),\n            dma.reborrow(),\n            Irqs,\n            clk.reborrow(),\n            cmd.reborrow(),\n            d0.reborrow(),\n            d1.reborrow(),\n            d2.reborrow(),\n            d3.reborrow(),\n            Default::default(),\n        );\n\n        {\n            let mut cmd_block = CmdBlock::new();\n\n            let mut storage = loop {\n                if let Ok(storage) = StorageDevice::new_sd_card(&mut s, &mut cmd_block, mhz(24)).await {\n                    break storage;\n                }\n            };\n\n            let card = storage.card();\n\n            info!(\"Card: {:#?}\", Debug2Format(&card));\n\n            // card_type: HighCapacity,\n            // ocr: OCR: Operation Conditions Register {\n            // Voltage Window (mV): (2700, 3600),\n            // S18A (UHS-I only): true,\n            // Over 2TB flag (SDUC only): false,\n            // UHS-II Card: false,\n            // Card Capacity Status (CSS): \\\"SDHC/SDXC/SDUC\\\",\n            // Busy: false },\n            // rca: 43690,\n            // cid: CID: Card Identification { Manufacturer ID: 3,\n            // OEM ID: \\\"SD\\\",\n            // Product Name: \\\"SL08G\\\",\n            // Product Revision: 128,\n            // Product Serial Number: 701445767,\n            // Manufacturing Date: (9,\n            // 2015) },\n            // csd: CSD: Card Specific Data { Transfer Rate: 50,\n            // Block Count: 15523840,\n            // Card Size (bytes): 7948206080,\n            // Read I (@min VDD): 100 mA,\n            // Write I (@min VDD): 10 mA,\n            // Read I (@max VDD): 5 mA,\n            // Write I (@max VDD): 45 mA,\n            // Erase Size (Blocks): 1 },\n            // scr: SCR: SD CARD Configuration Register { Version: Unknown,\n            // 1-bit width: false,\n            // 4-bit width: true },\n            // status: SD Status { Bus Width: One,\n            // Secured Mode: false,\n            // SD Memory Card Type: 0,\n            // Protected Area Size (B): 0,\n            // Speed Class: 0,\n            // Video Speed Class: 0,\n            // Application Performance Class: 0,\n            // Move Performance (MB/s): 0,\n            // AU Size: 0,\n            // Erase Size (units of AU): 0,\n            // Erase Timeout (s): 0,\n            // Discard Support: false } }\n\n            defmt::assert!(card.scr.bus_width_four());\n\n            info!(\"writing pattern1...\");\n            storage.write_block(block_idx, &pattern1).await.unwrap();\n\n            info!(\"reading...\");\n            storage.read_block(block_idx, &mut block).await.unwrap();\n            assert_eq!(block, pattern1);\n\n            info!(\"writing pattern2...\");\n            storage.write_block(block_idx, &pattern2).await.unwrap();\n\n            info!(\"reading...\");\n            storage.read_block(block_idx, &mut block).await.unwrap();\n            assert_eq!(block, pattern2);\n\n            info!(\"writing blocks [pattern1, pattern2]...\");\n            storage.write_blocks(block_idx, &patterns).await.unwrap();\n\n            info!(\"reading blocks...\");\n            storage.read_blocks(block_idx, &mut blocks).await.unwrap();\n            assert_eq!(&blocks, &patterns);\n        }\n    }\n\n    {\n        // FIXME: this hangs on Rust 1.86 and higher.\n        // I haven't been able to figure out why.\n\n        // ======== Try 1bit. ==============\n        info!(\"initializing in 1-bit mode...\");\n        let mut s = Sdmmc::new_1bit(\n            sdmmc.reborrow(),\n            dma.reborrow(),\n            Irqs,\n            clk.reborrow(),\n            cmd.reborrow(),\n            d0.reborrow(),\n            Default::default(),\n        );\n\n        {\n            let mut cmd_block = CmdBlock::new();\n\n            let mut storage = loop {\n                if let Ok(storage) = StorageDevice::new_sd_card(&mut s, &mut cmd_block, mhz(15)).await {\n                    break storage;\n                }\n            };\n\n            let card = storage.card();\n\n            info!(\"Card: {:#?}\", Debug2Format(&card));\n\n            // card_type: HighCapacity,\n            // ocr: OCR: Operation Conditions Register {\n            // Voltage Window (mV): (2700, 3600),\n            // S18A (UHS-I only): true,\n            // Over 2TB flag (SDUC only): false,\n            // UHS-II Card: false,\n            // Card Capacity Status (CSS): \\\"SDHC/SDXC/SDUC\\\",\n            // Busy: false },\n            // rca: 43690,\n            // cid: CID: Card Identification { Manufacturer ID: 3,\n            // OEM ID: \\\"SD\\\",\n            // Product Name: \\\"SL08G\\\",\n            // Product Revision: 128,\n            // Product Serial Number: 701445767,\n            // Manufacturing Date: (9,\n            // 2015) },\n            // csd: CSD: Card Specific Data { Transfer Rate: 50,\n            // Block Count: 15523840,\n            // Card Size (bytes): 7948206080,\n            // Read I (@min VDD): 100 mA,\n            // Write I (@min VDD): 10 mA,\n            // Read I (@max VDD): 5 mA,\n            // Write I (@max VDD): 45 mA,\n            // Erase Size (Blocks): 1 },\n            // scr: SCR: SD CARD Configuration Register { Version: Unknown,\n            // 1-bit width: false,\n            // 4-bit width: true },\n            // status: SD Status { Bus Width: One,\n            // Secured Mode: false,\n            // SD Memory Card Type: 0,\n            // Protected Area Size (B): 0,\n            // Speed Class: 0,\n            // Video Speed Class: 0,\n            // Application Performance Class: 0,\n            // Move Performance (MB/s): 0,\n            // AU Size: 0,\n            // Erase Size (units of AU): 0,\n            // Erase Timeout (s): 0,\n            // Discard Support: false } }\n\n            defmt::assert!(card.scr.bus_width_four());\n\n            info!(\"writing pattern1...\");\n            storage.write_block(block_idx, &pattern1).await.unwrap();\n\n            info!(\"reading...\");\n            storage.read_block(block_idx, &mut block).await.unwrap();\n            assert_eq!(block, pattern1);\n\n            info!(\"writing pattern2...\");\n            storage.write_block(block_idx, &pattern2).await.unwrap();\n\n            info!(\"reading...\");\n            storage.read_block(block_idx, &mut block).await.unwrap();\n            assert_eq!(block, pattern2);\n\n            info!(\"writing blocks [pattern1, pattern2]...\");\n            storage.write_blocks(block_idx, &patterns).await.unwrap();\n\n            info!(\"reading blocks...\");\n            storage.read_blocks(block_idx, &mut blocks).await.unwrap();\n            assert_eq!(&blocks, &patterns);\n        }\n    }\n\n    info!(\"Test OK\");\n    cortex_m::asm::bkpt();\n}\n"
  },
  {
    "path": "tests/stm32/src/bin/spi.rs",
    "content": "#![no_std]\n#![no_main]\n#[path = \"../common.rs\"]\nmod common;\n\nuse common::*;\nuse defmt::assert_eq;\nuse embassy_executor::Spawner;\nuse embassy_stm32::gpio::{Level, Output, Speed};\nuse embassy_stm32::mode::Blocking;\nuse embassy_stm32::spi::mode::Master;\nuse embassy_stm32::spi::{self, Spi, Word};\nuse embassy_stm32::time::Hertz;\n\n#[cfg_attr(\n    feature = \"stop\",\n    embassy_executor::main(executor = \"embassy_stm32::executor::Executor\", entry = \"cortex_m_rt::entry\")\n)]\n#[cfg_attr(not(feature = \"stop\"), embassy_executor::main)]\nasync fn main(_spawner: Spawner) {\n    let p = init();\n    info!(\"Hello World!\");\n\n    let mut spi_peri = peri!(p, SPI);\n    let mut sck = peri!(p, SPI_SCK);\n    let mut mosi = peri!(p, SPI_MOSI);\n    let mut miso = peri!(p, SPI_MISO);\n\n    let mut spi_config = spi::Config::default();\n    spi_config.frequency = Hertz(1_000_000);\n\n    let mut spi = Spi::new_blocking(\n        spi_peri.reborrow(),\n        sck.reborrow(),  // Arduino D13\n        mosi.reborrow(), // Arduino D11\n        miso.reborrow(), // Arduino D12\n        spi_config,\n    );\n\n    test_txrx::<u8>(&mut spi);\n    test_txrx::<u16>(&mut spi);\n\n    // Assert the RCC bit gets disabled on drop.\n    #[cfg(feature = \"stm32f429zi\")]\n    defmt::assert!(embassy_stm32::pac::RCC.apb2enr().read().spi1en());\n    drop(spi);\n    #[cfg(feature = \"stm32f429zi\")]\n    defmt::assert!(!embassy_stm32::pac::RCC.apb2enr().read().spi1en());\n\n    // test rx-only configuration\n    let mut spi = Spi::new_blocking_rxonly(spi_peri.reborrow(), sck.reborrow(), miso.reborrow(), spi_config);\n    let mut mosi_out = Output::new(mosi.reborrow(), Level::Low, Speed::VeryHigh);\n\n    test_rx::<u8>(&mut spi, &mut mosi_out);\n    test_rx::<u16>(&mut spi, &mut mosi_out);\n    drop(spi);\n    drop(mosi_out);\n\n    let mut spi = Spi::new_blocking_txonly(spi_peri.reborrow(), sck.reborrow(), mosi.reborrow(), spi_config);\n    test_tx::<u8>(&mut spi);\n    test_tx::<u16>(&mut spi);\n    drop(spi);\n\n    let mut spi = Spi::new_blocking_txonly_nosck(spi_peri.reborrow(), mosi.reborrow(), spi_config);\n    test_tx::<u8>(&mut spi);\n    test_tx::<u16>(&mut spi);\n    drop(spi);\n\n    info!(\"Test OK\");\n    cortex_m::asm::bkpt();\n}\n\nfn test_txrx<W: Word + From<u8> + defmt::Format + Eq>(spi: &mut Spi<'_, Blocking, Master>)\nwhere\n    W: core::ops::Not<Output = W>,\n{\n    let data: [W; 9] = [\n        0x00u8.into(),\n        0xFFu8.into(),\n        0xAAu8.into(),\n        0x55u8.into(),\n        0xC0u8.into(),\n        0xFFu8.into(),\n        0xEEu8.into(),\n        0xC0u8.into(),\n        0xDEu8.into(),\n    ];\n\n    // Arduino pins D11 and D12 (MOSI-MISO) are connected together with a 1K resistor.\n    // so we should get the data we sent back.\n    let mut buf = [W::default(); 9];\n    spi.blocking_transfer(&mut buf, &data).unwrap();\n    assert_eq!(buf, data);\n\n    spi.blocking_transfer_in_place(&mut buf).unwrap();\n    assert_eq!(buf, data);\n\n    // Check read/write don't hang. We can't check they transfer the right data\n    // without fancier test mechanisms.\n    spi.blocking_write(&buf).unwrap();\n    spi.blocking_read(&mut buf).unwrap();\n    spi.blocking_write(&buf).unwrap();\n    spi.blocking_read(&mut buf).unwrap();\n    spi.blocking_write(&buf).unwrap();\n\n    // Check transfer doesn't break after having done a write, due to garbage in the FIFO\n    spi.blocking_transfer(&mut buf, &data).unwrap();\n    assert_eq!(buf, data);\n\n    // Check zero-length operations, these should be noops.\n    spi.blocking_transfer::<u8>(&mut [], &[]).unwrap();\n    spi.blocking_transfer_in_place::<u8>(&mut []).unwrap();\n    spi.blocking_read::<u8>(&mut []).unwrap();\n    spi.blocking_write::<u8>(&[]).unwrap();\n}\n\nfn test_rx<W: Word + From<u8> + defmt::Format + Eq>(spi: &mut Spi<'_, Blocking, Master>, mosi_out: &mut Output<'_>)\nwhere\n    W: core::ops::Not<Output = W>,\n{\n    let mut buf = [W::default(); 9];\n\n    mosi_out.set_high();\n    spi.blocking_read(&mut buf).unwrap();\n    assert_eq!(buf, [!W::default(); 9]);\n    mosi_out.set_low();\n    spi.blocking_read(&mut buf).unwrap();\n    assert_eq!(buf, [W::default(); 9]);\n    spi.blocking_read::<u8>(&mut []).unwrap();\n    spi.blocking_read::<u8>(&mut []).unwrap();\n}\n\nfn test_tx<W: Word + From<u8> + defmt::Format + Eq>(spi: &mut Spi<'_, Blocking, Master>)\nwhere\n    W: core::ops::Not<Output = W>,\n{\n    let buf = [W::default(); 9];\n\n    // Test tx-only. Just check it doesn't hang, not much else we can do without using SPI slave.\n    spi.blocking_write(&buf).unwrap();\n    spi.blocking_write::<u8>(&[]).unwrap();\n    spi.blocking_write(&buf).unwrap();\n    spi.blocking_write::<u8>(&[]).unwrap();\n}\n"
  },
  {
    "path": "tests/stm32/src/bin/spi_dma.rs",
    "content": "#![no_std]\n#![no_main]\n#[path = \"../common.rs\"]\nmod common;\n\nuse common::*;\nuse defmt::assert_eq;\nuse embassy_executor::Spawner;\nuse embassy_stm32::gpio::{Level, Output, Speed};\nuse embassy_stm32::mode::Async;\nuse embassy_stm32::spi::mode::Master;\nuse embassy_stm32::spi::{self, Spi, Word};\nuse embassy_stm32::time::Hertz;\n\n#[cfg_attr(\n    feature = \"stop\",\n    embassy_executor::main(executor = \"embassy_stm32::executor::Executor\", entry = \"cortex_m_rt::entry\")\n)]\n#[cfg_attr(not(feature = \"stop\"), embassy_executor::main)]\nasync fn main(_spawner: Spawner) {\n    let p = init();\n    info!(\"Hello World!\");\n\n    let mut spi_peri = peri!(p, SPI);\n    let mut sck = peri!(p, SPI_SCK);\n    let mut mosi = peri!(p, SPI_MOSI);\n    let mut miso = peri!(p, SPI_MISO);\n    let mut tx_dma = peri!(p, SPI_TX_DMA);\n    let mut rx_dma = peri!(p, SPI_RX_DMA);\n    let irq = irqs!(UART);\n\n    let mut spi_config = spi::Config::default();\n    spi_config.frequency = Hertz(1_000_000);\n\n    let mut spi = Spi::new(\n        spi_peri.reborrow(),\n        sck.reborrow(),  // Arduino D13\n        mosi.reborrow(), // Arduino D11\n        miso.reborrow(), // Arduino D12\n        tx_dma.reborrow(),\n        rx_dma.reborrow(),\n        irq,\n        spi_config,\n    );\n\n    test_txrx::<u8>(&mut spi).await;\n    test_txrx::<u16>(&mut spi).await;\n    drop(spi);\n\n    // test rx-only configuration\n    let mut spi = Spi::new_rxonly(\n        spi_peri.reborrow(),\n        sck.reborrow(),\n        miso.reborrow(),\n        // SPIv1/f1 requires txdma even if rxonly.\n        #[cfg(not(feature = \"spi-v345\"))]\n        tx_dma.reborrow(),\n        rx_dma.reborrow(),\n        irq,\n        spi_config,\n    );\n    let mut mosi_out = Output::new(mosi.reborrow(), Level::Low, Speed::VeryHigh);\n\n    test_rx::<u8>(&mut spi, &mut mosi_out).await;\n    test_rx::<u16>(&mut spi, &mut mosi_out).await;\n    drop(spi);\n    drop(mosi_out);\n\n    let mut spi = Spi::new_txonly(\n        spi_peri.reborrow(),\n        sck.reborrow(),\n        mosi.reborrow(),\n        tx_dma.reborrow(),\n        irq,\n        spi_config,\n    );\n    test_tx::<u8>(&mut spi).await;\n    test_tx::<u16>(&mut spi).await;\n    drop(spi);\n\n    let mut spi = Spi::new_txonly_nosck(spi_peri.reborrow(), mosi.reborrow(), tx_dma.reborrow(), irq, spi_config);\n    test_tx::<u8>(&mut spi).await;\n    test_tx::<u16>(&mut spi).await;\n    drop(spi);\n\n    info!(\"Test OK\");\n    cortex_m::asm::bkpt();\n}\n\nasync fn test_txrx<W: Word + From<u8> + defmt::Format + Eq>(spi: &mut Spi<'_, Async, Master>)\nwhere\n    W: core::ops::Not<Output = W>,\n{\n    let data: [W; 9] = [\n        0x00u8.into(),\n        0xFFu8.into(),\n        0xAAu8.into(),\n        0x55u8.into(),\n        0xC0u8.into(),\n        0xFFu8.into(),\n        0xEEu8.into(),\n        0xC0u8.into(),\n        0xDEu8.into(),\n    ];\n\n    // Arduino pins D11 and D12 (MOSI-MISO) are connected together with a 1K resistor.\n    // so we should get the data we sent back.\n    let mut buf = [W::default(); 9];\n    spi.transfer(&mut buf, &data).await.unwrap();\n    assert_eq!(buf, data);\n\n    spi.transfer_in_place(&mut buf).await.unwrap();\n    assert_eq!(buf, data);\n\n    // Check read/write don't hang. We can't check they transfer the right data\n    // without fancier test mechanisms.\n    spi.write(&buf).await.unwrap();\n    spi.read(&mut buf).await.unwrap();\n    spi.write(&buf).await.unwrap();\n    spi.read(&mut buf).await.unwrap();\n    spi.write(&buf).await.unwrap();\n\n    // Check transfer doesn't break after having done a write, due to garbage in the FIFO\n    spi.transfer(&mut buf, &data).await.unwrap();\n    assert_eq!(buf, data);\n\n    // Check zero-length operations, these should be noops.\n    spi.transfer::<u8>(&mut [], &[]).await.unwrap();\n    spi.transfer_in_place::<u8>(&mut []).await.unwrap();\n    spi.read::<u8>(&mut []).await.unwrap();\n    spi.write::<u8>(&[]).await.unwrap();\n    spi.blocking_transfer::<u8>(&mut [], &[]).unwrap();\n    spi.blocking_transfer_in_place::<u8>(&mut []).unwrap();\n    spi.blocking_read::<u8>(&mut []).unwrap();\n    spi.blocking_write::<u8>(&[]).unwrap();\n\n    // Check mixing blocking with async.\n    spi.blocking_transfer(&mut buf, &data).unwrap();\n    assert_eq!(buf, data);\n    spi.transfer(&mut buf, &data).await.unwrap();\n    assert_eq!(buf, data);\n    spi.blocking_write(&buf).unwrap();\n    spi.transfer(&mut buf, &data).await.unwrap();\n    assert_eq!(buf, data);\n    spi.blocking_read(&mut buf).unwrap();\n    spi.blocking_write(&buf).unwrap();\n    spi.write(&buf).await.unwrap();\n    spi.read(&mut buf).await.unwrap();\n    spi.blocking_write(&buf).unwrap();\n    spi.blocking_read(&mut buf).unwrap();\n    spi.write(&buf).await.unwrap();\n}\n\nasync fn test_rx<W: Word + From<u8> + defmt::Format + Eq>(spi: &mut Spi<'_, Async, Master>, mosi_out: &mut Output<'_>)\nwhere\n    W: core::ops::Not<Output = W>,\n{\n    let mut buf = [W::default(); 9];\n\n    mosi_out.set_high();\n    spi.read(&mut buf).await.unwrap();\n    assert_eq!(buf, [!W::default(); 9]);\n    spi.blocking_read(&mut buf).unwrap();\n    assert_eq!(buf, [!W::default(); 9]);\n    spi.read(&mut buf).await.unwrap();\n    assert_eq!(buf, [!W::default(); 9]);\n    spi.read(&mut buf).await.unwrap();\n    assert_eq!(buf, [!W::default(); 9]);\n    spi.blocking_read(&mut buf).unwrap();\n    assert_eq!(buf, [!W::default(); 9]);\n    spi.blocking_read(&mut buf).unwrap();\n    assert_eq!(buf, [!W::default(); 9]);\n    mosi_out.set_low();\n    spi.read(&mut buf).await.unwrap();\n    assert_eq!(buf, [W::default(); 9]);\n    spi.read::<u8>(&mut []).await.unwrap();\n    spi.blocking_read::<u8>(&mut []).unwrap();\n}\n\nasync fn test_tx<W: Word + From<u8> + defmt::Format + Eq>(spi: &mut Spi<'_, Async, Master>)\nwhere\n    W: core::ops::Not<Output = W>,\n{\n    let buf = [W::default(); 9];\n\n    // Test tx-only. Just check it doesn't hang, not much else we can do without using SPI slave.\n    spi.blocking_write(&buf).unwrap();\n    spi.write(&buf).await.unwrap();\n    spi.blocking_write(&buf).unwrap();\n    spi.blocking_write(&buf).unwrap();\n    spi.write(&buf).await.unwrap();\n    spi.write(&buf).await.unwrap();\n    spi.write::<u8>(&[]).await.unwrap();\n    spi.blocking_write::<u8>(&[]).unwrap();\n}\n"
  },
  {
    "path": "tests/stm32/src/bin/stop.rs",
    "content": "// required-features: stop,chrono\n\n#![no_std]\n#![no_main]\n#[path = \"../common.rs\"]\nmod common;\n\nuse chrono::NaiveDate;\nuse common::*;\nuse embassy_executor::Spawner;\nuse embassy_stm32::Config;\nuse embassy_stm32::rcc::{LsConfig, StopMode, get_stop_mode};\nuse embassy_stm32::rtc::Rtc;\nuse embassy_time::Timer;\n\n/// Get whether the core is ready to enter the given stop mode.\n///\n/// This will return false if some peripheral driver is in use that\n/// prevents entering the given stop mode.\nfn stop_ready(stop_mode: StopMode) -> bool {\n    critical_section::with(|cs| match get_stop_mode(cs) {\n        Some(mode) => mode.at_least(stop_mode),\n        None => false,\n    })\n}\n\n#[embassy_executor::task]\nasync fn task_1() {\n    for _ in 0..9 {\n        info!(\"task 1: waiting for 500ms...\");\n        defmt::assert!(stop_ready(StopMode::Stop2));\n        Timer::after_millis(500).await;\n    }\n}\n\n#[embassy_executor::task]\nasync fn task_2() {\n    for _ in 0..5 {\n        info!(\"task 2: waiting for 1000ms...\");\n        defmt::assert!(stop_ready(StopMode::Stop2));\n        Timer::after_millis(1000).await;\n    }\n\n    info!(\"Test OK\");\n    cortex_m::asm::bkpt();\n}\n\n#[embassy_executor::main(executor = \"embassy_stm32::executor::Executor\", entry = \"cortex_m_rt::entry\")]\nasync fn async_main(spawner: Spawner) {\n    let _ = config();\n\n    let mut config = Config::default();\n    config.rcc.ls = LsConfig::default_lse();\n\n    // System Clock seems cannot be greater than 16 MHz\n    #[cfg(any(feature = \"stm32h563zi\", feature = \"stm32h503rb\"))]\n    {\n        use embassy_stm32::rcc::HSIPrescaler;\n        config.rcc.hsi = Some(HSIPrescaler::DIV4); // 64 MHz HSI will need a /4\n    }\n\n    let p = init_with_config(config);\n    info!(\"Hello World!\");\n\n    let now = NaiveDate::from_ymd_opt(2020, 5, 15)\n        .unwrap()\n        .and_hms_opt(10, 30, 15)\n        .unwrap();\n\n    let (rtc, _time_provider) = Rtc::new(p.RTC);\n\n    info!(\"set datetime\");\n    critical_section::with(|cs| {\n        rtc.borrow_mut(cs).set_datetime(now.into()).expect(\"datetime not set\");\n    });\n\n    info!(\"spawn\");\n    spawner.spawn(task_1().unwrap());\n    spawner.spawn(task_2().unwrap());\n}\n"
  },
  {
    "path": "tests/stm32/src/bin/timer.rs",
    "content": "#![no_std]\n#![no_main]\n#[path = \"../common.rs\"]\nmod common;\n\nuse common::*;\nuse defmt::assert;\nuse embassy_executor::Spawner;\nuse embassy_futures::select::{Either, select};\nuse embassy_futures::yield_now;\nuse embassy_time::{Instant, Timer};\n\n#[cfg_attr(\n    feature = \"stop\",\n    embassy_executor::main(executor = \"embassy_stm32::executor::Executor\", entry = \"cortex_m_rt::entry\")\n)]\n#[cfg_attr(not(feature = \"stop\"), embassy_executor::main)]\nasync fn main(_spawner: Spawner) {\n    let _p = init();\n    info!(\"Hello World!\");\n\n    let start = Instant::now();\n    Timer::after_millis(100).await;\n    let end = Instant::now();\n    let ms = (end - start).as_millis();\n    info!(\"slept for {} ms\", ms);\n    assert!(ms >= 99);\n    assert!(ms < 110);\n\n    let start = Instant::now();\n    match select(Timer::at(Instant::MAX), yield_now()).await {\n        Either::First(_) => assert!(false),\n        Either::Second(_) => (),\n    }\n    info!(\"Testing timer after never-ending timer\");\n    Timer::after_millis(100).await;\n    let end = Instant::now();\n    let ms = (end - start).as_millis();\n    info!(\"slept for {} ms\", ms);\n    assert!(ms >= 99);\n    assert!(ms < 110);\n\n    info!(\"Test OK\");\n    cortex_m::asm::bkpt();\n}\n"
  },
  {
    "path": "tests/stm32/src/bin/ucpd.rs",
    "content": "// required-features: ucpd\n#![no_std]\n#![no_main]\n#[path = \"../common.rs\"]\nmod common;\n\nuse common::*;\nuse defmt::{assert, assert_eq};\nuse embassy_executor::Spawner;\nuse embassy_futures::join::join;\nuse embassy_stm32::ucpd::{self, CcPhy, CcPull, CcSel, CcVState, RxError, Ucpd};\nuse embassy_stm32::{Peri, bind_interrupts, peripherals};\nuse embassy_time::Timer;\n\nbind_interrupts!(struct Irqs {\n    UCPD1_2 => ucpd::InterruptHandler<peripherals::UCPD1>, ucpd::InterruptHandler<peripherals::UCPD2>;\n    DMA1_CHANNEL1 => embassy_stm32::dma::InterruptHandler<embassy_stm32::peripherals::DMA1_CH1>;\n    DMA1_CHANNEL2_3 => embassy_stm32::dma::InterruptHandler<embassy_stm32::peripherals::DMA1_CH2>,\n    embassy_stm32::dma::InterruptHandler<embassy_stm32::peripherals::DMA1_CH3>;\n    DMA1_CH4_7_DMAMUX1_OVR => embassy_stm32::dma::InterruptHandler<embassy_stm32::peripherals::DMA1_CH4>;\n});\n\nstatic SRC_TO_SNK: [u8; 6] = [0, 1, 2, 3, 4, 5];\nstatic SNK_TO_SRC: [u8; 4] = [9, 8, 7, 6];\n\nasync fn wait_for_vstate<T: ucpd::Instance>(cc_phy: &mut CcPhy<'_, T>, vstate: CcVState) {\n    let (mut cc1, mut _cc2) = cc_phy.vstate();\n    while cc1 != vstate {\n        (cc1, _cc2) = cc_phy.wait_for_vstate_change().await;\n    }\n}\n\nasync fn source(\n    mut ucpd: Ucpd<'static, peripherals::UCPD1>,\n    rx_dma: Peri<'static, peripherals::DMA1_CH1>,\n    tx_dma: Peri<'static, peripherals::DMA1_CH2>,\n) {\n    debug!(\"source: setting default current pull-up\");\n    ucpd.cc_phy().set_pull(CcPull::SourceDefaultUsb);\n\n    // Wait for default sink.\n    debug!(\"source: wait for sink\");\n    wait_for_vstate(ucpd.cc_phy(), CcVState::LOW).await;\n\n    // Advertise a higher current by changing the pull-up resistor.\n    debug!(\"source: sink detected, setting 3.0A current pull-up\");\n    ucpd.cc_phy().set_pull(CcPull::Source3_0A);\n\n    let (_, mut pd_phy) = ucpd.split_pd_phy(rx_dma, tx_dma, Irqs, CcSel::CC1);\n\n    // Listen for an incoming message\n    debug!(\"source: wait for message from sink\");\n    let mut snk_to_src_buf = [0_u8; 30];\n    let n = unwrap!(pd_phy.receive(snk_to_src_buf.as_mut()).await);\n    assert_eq!(n, SNK_TO_SRC.len());\n    assert_eq!(&snk_to_src_buf[..n], SNK_TO_SRC.as_slice());\n\n    // Send message\n    debug!(\"source: message received, sending message\");\n    unwrap!(pd_phy.transmit(SRC_TO_SNK.as_slice()).await);\n\n    // Wait for hard-reset\n    debug!(\"source: message sent, waiting for hard-reset\");\n    assert!(matches!(\n        pd_phy.receive(snk_to_src_buf.as_mut()).await,\n        Err(RxError::HardReset)\n    ));\n}\n\nasync fn sink(\n    mut ucpd: Ucpd<'static, peripherals::UCPD2>,\n    rx_dma: Peri<'static, peripherals::DMA1_CH3>,\n    tx_dma: Peri<'static, peripherals::DMA1_CH4>,\n) {\n    debug!(\"sink: setting pull down\");\n    ucpd.cc_phy().set_pull(CcPull::Sink);\n\n    // Wait for default source.\n    debug!(\"sink: waiting for default vstate\");\n    wait_for_vstate(ucpd.cc_phy(), CcVState::LOW).await;\n\n    // Wait higher current pull-up.\n    //debug!(\"sink: source default vstate detected, waiting for 3.0A vstate\");\n    //wait_for_vstate(ucpd.cc_phy(), CcVState::HIGHEST).await;\n    //debug!(\"sink: source 3.0A vstate detected\");\n    // TODO: not working yet, why? no idea, replace with timer for now\n    Timer::after_millis(100).await;\n\n    let (_, mut pd_phy) = ucpd.split_pd_phy(rx_dma, tx_dma, Irqs, CcSel::CC1);\n\n    // Send message\n    debug!(\"sink: sending message\");\n    unwrap!(pd_phy.transmit(SNK_TO_SRC.as_slice()).await);\n\n    // Listen for an incoming message\n    debug!(\"sink: message sent, waiting for message from source\");\n    let mut src_to_snk_buf = [0_u8; 30];\n    let n = unwrap!(pd_phy.receive(src_to_snk_buf.as_mut()).await);\n    assert_eq!(n, SRC_TO_SNK.len());\n    assert_eq!(&src_to_snk_buf[..n], SRC_TO_SNK.as_slice());\n\n    // Send hard reset\n    debug!(\"sink: message received, sending hard-reset\");\n    unwrap!(pd_phy.transmit_hardreset().await);\n}\n\n#[cfg_attr(\n    feature = \"stop\",\n    embassy_executor::main(executor = \"embassy_stm32::executor::Executor\", entry = \"cortex_m_rt::entry\")\n)]\n#[cfg_attr(not(feature = \"stop\"), embassy_executor::main)]\nasync fn main(_spawner: Spawner) {\n    let p = init();\n    info!(\"Hello World!\");\n\n    // Wire between PD0 and PA8\n    let ucpd1 = Ucpd::new(p.UCPD1, Irqs, p.PA8, p.PB15, Default::default());\n    let ucpd2 = Ucpd::new(p.UCPD2, Irqs, p.PD0, p.PD2, Default::default());\n\n    join(\n        source(ucpd1, p.DMA1_CH1, p.DMA1_CH2),\n        sink(ucpd2, p.DMA1_CH3, p.DMA1_CH4),\n    )\n    .await;\n\n    info!(\"Test OK\");\n    cortex_m::asm::bkpt();\n}\n"
  },
  {
    "path": "tests/stm32/src/bin/usart.rs",
    "content": "#![no_std]\n#![no_main]\n#[path = \"../common.rs\"]\nmod common;\n\nuse common::*;\nuse defmt::{assert, assert_eq, unreachable};\nuse embassy_executor::Spawner;\nuse embassy_stm32::mode::Blocking;\nuse embassy_stm32::usart::{Config, ConfigError, Error, Uart};\nuse embassy_time::{Duration, Instant, block_for};\n\n#[cfg_attr(\n    feature = \"stop\",\n    embassy_executor::main(executor = \"embassy_stm32::executor::Executor\", entry = \"cortex_m_rt::entry\")\n)]\n#[cfg_attr(not(feature = \"stop\"), embassy_executor::main)]\nasync fn main(_spawner: Spawner) {\n    let p = init();\n    info!(\"Hello World!\");\n\n    // Arduino pins D0 and D1\n    // They're connected together with a 1K resistor.\n    let mut usart = peri!(p, UART);\n    let mut rx = peri!(p, UART_RX);\n    let mut tx = peri!(p, UART_TX);\n\n    {\n        let config = Config::default();\n        let mut usart = Uart::new_blocking(usart.reborrow(), rx.reborrow(), tx.reborrow(), config).unwrap();\n\n        let test_usart = async |usart: &mut Uart<'_, Blocking>| -> Result<(), Error> {\n            // We can't send too many bytes, they have to fit in the FIFO.\n            // This is because we aren't sending+receiving at the same time.\n\n            let data = [0xC0, 0xDE];\n            usart.blocking_write(&data)?;\n\n            let mut buf = [0; 2];\n            usart.blocking_read(&mut buf)?;\n            assert_eq!(buf, data);\n\n            // Test flush doesn't hang.\n            usart.blocking_write(&data)?;\n            usart.blocking_flush()?;\n\n            // Test flush doesn't hang if there's nothing to flush\n            usart.blocking_flush()?;\n\n            Ok(())\n        };\n\n        let mut is_ok = false;\n        for _ in 0..3 {\n            match test_usart(&mut usart).await {\n                Ok(()) => is_ok = true,\n                Err(Error::Noise) => is_ok = false,\n                Err(e) => defmt::panic!(\"{}\", e),\n            }\n\n            if is_ok {\n                break;\n            }\n        }\n\n        assert!(is_ok);\n    }\n\n    // Test error handling with with an overflow error\n    {\n        let config = Config::default();\n        let mut usart = Uart::new_blocking(usart.reborrow(), rx.reborrow(), tx.reborrow(), config).unwrap();\n\n        // Send enough bytes to fill the RX FIFOs off all USART versions.\n        let data = [0; 64];\n        usart.blocking_write(&data).unwrap();\n        usart.blocking_flush().unwrap();\n\n        // USART can still take up to 1 bit time (?) to receive the last byte\n        // that we just flushed, so wait a bit.\n        // otherwise, we might clear the overrun flag from an *earlier* byte and\n        // it gets set again when receiving the last byte is done.\n        block_for(Duration::from_millis(1));\n\n        // The error should be reported first.\n        let mut buf = [0; 1];\n        let err = usart.blocking_read(&mut buf);\n        assert_eq!(err, Err(Error::Overrun));\n\n        // At least the first data byte should still be available on all USART versions.\n        usart.blocking_read(&mut buf).unwrap();\n        assert_eq!(buf[0], data[0]);\n    }\n\n    // Test that baudrate divider is calculated correctly.\n    // Do it by comparing the time it takes to send a known number of bytes.\n    for baudrate in [300, 9600, 115200, 250_000, 337_934, 1_000_000, 2_000_000] {\n        info!(\"testing baudrate {}\", baudrate);\n\n        let mut config = Config::default();\n        config.baudrate = baudrate;\n        let mut usart = match Uart::new_blocking(usart.reborrow(), rx.reborrow(), tx.reborrow(), config) {\n            Ok(x) => x,\n            Err(ConfigError::BaudrateTooHigh) => {\n                info!(\"baudrate too high\");\n                assert!(baudrate >= 1_000_000);\n                continue;\n            }\n            Err(ConfigError::BaudrateTooLow) => {\n                info!(\"baudrate too low\");\n                assert!(baudrate <= 300);\n                continue;\n            }\n            Err(_) => unreachable!(),\n        };\n\n        let n = (baudrate as usize / 100).max(64);\n\n        let start = Instant::now();\n        for _ in 0..n {\n            usart.blocking_write(&[0x00]).unwrap();\n        }\n        usart.blocking_flush().unwrap();\n        let dur = Instant::now() - start;\n        let want_dur = Duration::from_micros(n as u64 * 10 * 1_000_000 / (baudrate as u64));\n        let fuzz = want_dur / 5;\n        if dur < want_dur - fuzz || dur > want_dur + fuzz {\n            defmt::panic!(\n                \"bad duration for baudrate {}: got {:?} want {:?}\",\n                baudrate,\n                dur,\n                want_dur\n            );\n        }\n    }\n\n    info!(\"Test OK\");\n    cortex_m::asm::bkpt();\n}\n"
  },
  {
    "path": "tests/stm32/src/bin/usart_dma.rs",
    "content": "#![no_std]\n#![no_main]\n#[path = \"../common.rs\"]\nmod common;\n\nuse common::*;\nuse defmt::assert_eq;\nuse embassy_executor::Spawner;\nuse embassy_futures::join::join;\nuse embassy_stm32::usart::{Config, Error, Uart};\n\n#[cfg_attr(\n    feature = \"stop\",\n    embassy_executor::main(executor = \"embassy_stm32::executor::Executor\", entry = \"cortex_m_rt::entry\")\n)]\n#[cfg_attr(not(feature = \"stop\"), embassy_executor::main)]\nasync fn main(_spawner: Spawner) {\n    let p = init();\n    info!(\"Hello World!\");\n\n    // Arduino pins D0 and D1\n    // They're connected together with a 1K resistor.\n    let usart = peri!(p, UART);\n    let rx = peri!(p, UART_RX);\n    let tx = peri!(p, UART_TX);\n    let rx_dma = peri!(p, UART_RX_DMA);\n    let tx_dma = peri!(p, UART_TX_DMA);\n    let irq = irqs!(UART);\n\n    let config = Config::default();\n    let usart = Uart::new(usart, rx, tx, tx_dma, rx_dma, irq, config).unwrap();\n\n    const LEN: usize = 128;\n    let mut tx_buf = [0; LEN];\n    let mut rx_buf = [0; LEN];\n\n    let (mut tx, mut rx) = usart.split();\n\n    let mut noise_count = 0;\n    for n in 0..42 {\n        for i in 0..LEN {\n            tx_buf[i] = (i ^ n) as u8;\n        }\n\n        let tx_fut = async {\n            tx.write(&tx_buf).await.unwrap();\n        };\n\n        let mut is_noisy = false;\n        let rx_fut = async {\n            match rx.read(&mut rx_buf).await {\n                Ok(()) => {}\n                Err(Error::Noise) => is_noisy = true,\n                _ => defmt::panic!(),\n            }\n        };\n\n        // note: rx needs to be polled first, to workaround this bug:\n        // https://github.com/embassy-rs/embassy/issues/1426\n        join(rx_fut, tx_fut).await;\n\n        if is_noisy {\n            noise_count += 1;\n            continue;\n        }\n\n        assert_eq!(tx_buf, rx_buf);\n    }\n\n    defmt::assert!(noise_count < 3);\n\n    // Test flush doesn't hang. Check multiple combinations of async+blocking.\n    tx.write(&tx_buf).await.unwrap();\n    tx.flush().await.unwrap();\n    tx.flush().await.unwrap();\n\n    tx.write(&tx_buf).await.unwrap();\n    tx.blocking_flush().unwrap();\n    tx.flush().await.unwrap();\n\n    tx.blocking_write(&tx_buf).unwrap();\n    tx.blocking_flush().unwrap();\n    tx.flush().await.unwrap();\n\n    tx.blocking_write(&tx_buf).unwrap();\n    tx.flush().await.unwrap();\n    tx.blocking_flush().unwrap();\n\n    info!(\"Test OK\");\n    cortex_m::asm::bkpt();\n}\n"
  },
  {
    "path": "tests/stm32/src/bin/usart_rx_ringbuffered.rs",
    "content": "// required-features: not-gpdma\n\n#![no_std]\n#![no_main]\n#[path = \"../common.rs\"]\nmod common;\n\nuse common::*;\nuse defmt::{assert_eq, panic};\nuse embassy_executor::Spawner;\nuse embassy_stm32::mode::Async;\nuse embassy_stm32::usart::{Config, DataBits, Parity, RingBufferedUartRx, StopBits, Uart, UartTx};\nuse embassy_time::Timer;\nuse rand_chacha::ChaCha8Rng;\nuse rand_core::{RngCore, SeedableRng};\n\nconst DMA_BUF_SIZE: usize = 256;\n\n#[cfg_attr(\n    feature = \"stop\",\n    embassy_executor::main(executor = \"embassy_stm32::executor::Executor\", entry = \"cortex_m_rt::entry\")\n)]\n#[cfg_attr(not(feature = \"stop\"), embassy_executor::main)]\nasync fn main(spawner: Spawner) {\n    let p = init();\n    info!(\"Hello World!\");\n\n    // Arduino pins D0 and D1\n    // They're connected together with a 1K resistor.\n    let usart = peri!(p, UART);\n    let rx = peri!(p, UART_RX);\n    let tx = peri!(p, UART_TX);\n    let rx_dma = peri!(p, UART_RX_DMA);\n    let tx_dma = peri!(p, UART_TX_DMA);\n    let irq = irqs!(UART);\n\n    // To run this test, use the saturating_serial test utility to saturate the serial port\n\n    let mut config = Config::default();\n    // this is the fastest we can go without tuning RCC\n    // some chips have default pclk=8mhz, and uart can run at max pclk/16\n    config.baudrate = 500_000;\n    config.data_bits = DataBits::DataBits8;\n    config.stop_bits = StopBits::STOP1;\n    config.parity = Parity::ParityNone;\n\n    let usart = Uart::new(usart, rx, tx, tx_dma, rx_dma, irq, config).unwrap();\n    let (tx, rx) = usart.split();\n    static mut DMA_BUF: [u8; DMA_BUF_SIZE] = [0; DMA_BUF_SIZE];\n    let rx = rx.into_ring_buffered(unsafe { &mut *core::ptr::addr_of_mut!(DMA_BUF) });\n\n    info!(\"Spawning tasks\");\n    spawner.spawn(transmit_task(tx).unwrap());\n    spawner.spawn(receive_task(rx).unwrap());\n}\n\n#[embassy_executor::task]\nasync fn transmit_task(mut tx: UartTx<'static, Async>) {\n    // workaround https://github.com/embassy-rs/embassy/issues/1426\n    Timer::after_millis(100).await;\n\n    let mut rng = ChaCha8Rng::seed_from_u64(1337);\n\n    info!(\"Starting random transmissions into void...\");\n\n    let mut i: u8 = 0;\n    loop {\n        let mut buf = [0; 256];\n        let len = 1 + (rng.next_u32() as usize % buf.len());\n        for b in &mut buf[..len] {\n            *b = i;\n            i = i.wrapping_add(1);\n        }\n\n        tx.write(&buf[..len]).await.unwrap();\n        Timer::after_micros((rng.next_u32() % 1000) as _).await;\n    }\n}\n\n#[embassy_executor::task]\nasync fn receive_task(mut rx: RingBufferedUartRx<'static>) {\n    info!(\"Ready to receive...\");\n\n    let mut rng = ChaCha8Rng::seed_from_u64(1337);\n\n    let mut i = 0;\n    let mut expected = 0;\n    loop {\n        let mut buf = [0; 256];\n        let max_len = 1 + (rng.next_u32() as usize % buf.len());\n        let received = match rx.read(&mut buf[..max_len]).await {\n            Ok(r) => r,\n            Err(e) => {\n                panic!(\"Test fail! read error: {:?}\", e);\n            }\n        };\n\n        for byte in &buf[..received] {\n            assert_eq!(*byte, expected);\n            expected = expected.wrapping_add(1);\n        }\n\n        if received < max_len {\n            Timer::after_micros((rng.next_u32() % 1000) as _).await;\n        }\n\n        i += received;\n\n        if i > 100000 {\n            info!(\"Test OK!\");\n            cortex_m::asm::bkpt();\n        }\n    }\n}\n"
  },
  {
    "path": "tests/stm32/src/bin/wpan_ble.rs",
    "content": "// required-features: ble\n\n#![no_std]\n#![no_main]\n#[path = \"../common.rs\"]\nmod common;\n\nuse core::time::Duration;\n\nuse common::*;\nuse embassy_executor::Spawner;\nuse embassy_stm32::bind_interrupts;\nuse embassy_stm32::ipcc::{Config, ReceiveInterruptHandler, TransmitInterruptHandler};\nuse embassy_stm32::rcc::WPAN_DEFAULT;\nuse embassy_stm32_wpan::TlMbox;\nuse embassy_stm32_wpan::hci::BdAddr;\nuse embassy_stm32_wpan::hci::host::uart::UartHci;\nuse embassy_stm32_wpan::hci::host::{AdvertisingFilterPolicy, EncryptionKey, HostHci, OwnAddressType};\nuse embassy_stm32_wpan::hci::types::AdvertisingType;\nuse embassy_stm32_wpan::hci::vendor::command::gap::{AdvertisingDataType, DiscoverableParameters, GapCommands, Role};\nuse embassy_stm32_wpan::hci::vendor::command::gatt::GattCommands;\nuse embassy_stm32_wpan::hci::vendor::command::hal::{ConfigData, HalCommands, PowerLevel};\nuse embassy_stm32_wpan::lhci::LhciC1DeviceInformationCcrp;\nuse embassy_stm32_wpan::sub::mm;\nuse {defmt_rtt as _, panic_probe as _};\n\nbind_interrupts!(struct Irqs{\n    IPCC_C1_RX => ReceiveInterruptHandler;\n    IPCC_C1_TX => TransmitInterruptHandler;\n});\n\nconst BLE_GAP_DEVICE_NAME_LENGTH: u8 = 7;\n\n#[embassy_executor::task]\nasync fn run_mm_queue(mut memory_manager: mm::MemoryManager<'static>) {\n    memory_manager.run_queue().await;\n}\n\n#[cfg_attr(\n    feature = \"stop\",\n    embassy_executor::main(executor = \"embassy_stm32::executor::Executor\", entry = \"cortex_m_rt::entry\")\n)]\n#[cfg_attr(not(feature = \"stop\"), embassy_executor::main)]\nasync fn main(spawner: Spawner) {\n    let mut config = config();\n    config.rcc = WPAN_DEFAULT;\n\n    let p = init_with_config(config);\n    info!(\"Hello World!\");\n\n    let config = Config::default();\n    let mbox = TlMbox::init(p.IPCC, Irqs, config).await.unwrap();\n    let mut sys = mbox.sys_subsystem;\n    let mut ble = mbox.ble_subsystem;\n\n    spawner.spawn(run_mm_queue(mbox.mm_subsystem).unwrap());\n\n    let fw_info = sys.wireless_fw_info().unwrap();\n    let version_major = fw_info.version_major();\n    let version_minor = fw_info.version_minor();\n    let subversion = fw_info.subversion();\n\n    let sram2a_size = fw_info.sram2a_size();\n    let sram2b_size = fw_info.sram2b_size();\n\n    info!(\n        \"version {}.{}.{} - SRAM2a {} - SRAM2b {}\",\n        version_major, version_minor, subversion, sram2a_size, sram2b_size\n    );\n\n    let _ = sys.shci_c2_ble_init(Default::default()).await;\n\n    info!(\"resetting BLE...\");\n    ble.reset().await;\n    let response = ble.read().await.unwrap();\n    info!(\"{}\", response);\n\n    info!(\"config public address...\");\n    ble.write_config_data(&ConfigData::public_address(get_bd_addr()).build())\n        .await;\n    let response = ble.read().await.unwrap();\n    info!(\"{}\", response);\n\n    info!(\"config random address...\");\n    ble.write_config_data(&ConfigData::random_address(get_random_addr()).build())\n        .await;\n    let response = ble.read().await.unwrap();\n    info!(\"{}\", response);\n\n    info!(\"config identity root...\");\n    ble.write_config_data(&ConfigData::identity_root(&get_irk()).build())\n        .await;\n    let response = ble.read().await.unwrap();\n    info!(\"{}\", response);\n\n    info!(\"config encryption root...\");\n    ble.write_config_data(&ConfigData::encryption_root(&get_erk()).build())\n        .await;\n    let response = ble.read().await.unwrap();\n    info!(\"{}\", response);\n\n    info!(\"config tx power level...\");\n    ble.set_tx_power_level(PowerLevel::ZerodBm).await;\n    let response = ble.read().await.unwrap();\n    info!(\"{}\", response);\n\n    info!(\"GATT init...\");\n    ble.init_gatt().await;\n    let response = ble.read().await.unwrap();\n    info!(\"{}\", response);\n\n    info!(\"GAP init...\");\n    ble.init_gap(Role::PERIPHERAL, false, BLE_GAP_DEVICE_NAME_LENGTH).await;\n    let response = ble.read().await.unwrap();\n    info!(\"{}\", response);\n\n    // info!(\"set scan response...\");\n    // ble.le_set_scan_response_data(&[]).await.unwrap();\n    // let response = ble.read().await.unwrap();\n    // info!(\"{}\", response);\n\n    info!(\"set discoverable...\");\n    ble.set_discoverable(&DiscoverableParameters {\n        advertising_type: AdvertisingType::NonConnectableUndirected,\n        advertising_interval: Some((Duration::from_millis(250), Duration::from_millis(250))),\n        address_type: OwnAddressType::Public,\n        filter_policy: AdvertisingFilterPolicy::AllowConnectionAndScan,\n        local_name: None,\n        advertising_data: &[],\n        conn_interval: (None, None),\n    })\n    .await\n    .unwrap();\n\n    let response = ble.read().await;\n    info!(\"{}\", response);\n\n    // remove some advertisement to decrease the packet size\n    info!(\"delete tx power ad type...\");\n    ble.delete_ad_type(AdvertisingDataType::TxPowerLevel).await;\n    let response = ble.read().await.unwrap();\n    info!(\"{}\", response);\n\n    info!(\"delete conn interval ad type...\");\n    ble.delete_ad_type(AdvertisingDataType::PeripheralConnectionInterval)\n        .await;\n    let response = ble.read().await.unwrap();\n    info!(\"{}\", response);\n\n    info!(\"update advertising data...\");\n    ble.update_advertising_data(&eddystone_advertising_data())\n        .await\n        .unwrap();\n    let response = ble.read().await.unwrap();\n    info!(\"{}\", response);\n\n    info!(\"update advertising data type...\");\n    ble.update_advertising_data(&[3, AdvertisingDataType::UuidCompleteList16 as u8, 0xaa, 0xfe])\n        .await\n        .unwrap();\n    let response = ble.read().await.unwrap();\n    info!(\"{}\", response);\n\n    info!(\"update advertising data flags...\");\n    ble.update_advertising_data(&[\n        2,\n        AdvertisingDataType::Flags as u8,\n        (0x02 | 0x04) as u8, // BLE general discoverable, without BR/EDR support\n    ])\n    .await\n    .unwrap();\n    let response = ble.read().await.unwrap();\n    info!(\"{}\", response);\n\n    info!(\"Test OK\");\n    cortex_m::asm::bkpt();\n}\n\nfn get_bd_addr() -> BdAddr {\n    let mut bytes = [0u8; 6];\n\n    let lhci_info = LhciC1DeviceInformationCcrp::new();\n    bytes[0] = (lhci_info.uid64 & 0xff) as u8;\n    bytes[1] = ((lhci_info.uid64 >> 8) & 0xff) as u8;\n    bytes[2] = ((lhci_info.uid64 >> 16) & 0xff) as u8;\n    bytes[3] = lhci_info.device_type_id;\n    bytes[4] = (lhci_info.st_company_id & 0xff) as u8;\n    bytes[5] = (lhci_info.st_company_id >> 8 & 0xff) as u8;\n\n    BdAddr(bytes)\n}\n\nfn get_random_addr() -> BdAddr {\n    let mut bytes = [0u8; 6];\n\n    let lhci_info = LhciC1DeviceInformationCcrp::new();\n    bytes[0] = (lhci_info.uid64 & 0xff) as u8;\n    bytes[1] = ((lhci_info.uid64 >> 8) & 0xff) as u8;\n    bytes[2] = ((lhci_info.uid64 >> 16) & 0xff) as u8;\n    bytes[3] = 0;\n    bytes[4] = 0x6E;\n    bytes[5] = 0xED;\n\n    BdAddr(bytes)\n}\n\nconst BLE_CFG_IRK: [u8; 16] = [\n    0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0,\n];\nconst BLE_CFG_ERK: [u8; 16] = [\n    0xfe, 0xdc, 0xba, 0x09, 0x87, 0x65, 0x43, 0x21, 0xfe, 0xdc, 0xba, 0x09, 0x87, 0x65, 0x43, 0x21,\n];\n\nfn get_irk() -> EncryptionKey {\n    EncryptionKey(BLE_CFG_IRK)\n}\n\nfn get_erk() -> EncryptionKey {\n    EncryptionKey(BLE_CFG_ERK)\n}\n\nfn eddystone_advertising_data() -> [u8; 24] {\n    const EDDYSTONE_URL: &[u8] = b\"www.rust-lang.com\";\n\n    let mut service_data = [0u8; 24];\n    let url_len = EDDYSTONE_URL.len();\n\n    service_data[0] = 6 + url_len as u8;\n    service_data[1] = AdvertisingDataType::ServiceData as u8;\n\n    // 16-bit eddystone uuid\n    service_data[2] = 0xaa;\n    service_data[3] = 0xFE;\n\n    service_data[4] = 0x10; // URL frame type\n    service_data[5] = 22_i8 as u8; // calibrated TX power at 0m\n    service_data[6] = 0x03; // eddystone url prefix = https\n\n    service_data[7..(7 + url_len)].copy_from_slice(EDDYSTONE_URL);\n\n    service_data\n}\n"
  },
  {
    "path": "tests/stm32/src/bin/wpan_mac.rs",
    "content": "// required-features: mac\n\n#![no_std]\n#![no_main]\n#[path = \"../common.rs\"]\nmod common;\n\nuse common::*;\nuse embassy_executor::Spawner;\nuse embassy_stm32::bind_interrupts;\nuse embassy_stm32::ipcc::{Config, ReceiveInterruptHandler, TransmitInterruptHandler};\nuse embassy_stm32::rcc::WPAN_DEFAULT;\nuse embassy_stm32_wpan::TlMbox;\nuse embassy_stm32_wpan::mac::commands::{AssociateRequest, GetRequest, ResetRequest, SetRequest};\nuse embassy_stm32_wpan::mac::event::MacEvent;\nuse embassy_stm32_wpan::mac::typedefs::{\n    AddressMode, Capabilities, KeyIdMode, MacAddress, MacChannel, PanId, PibId, SecurityLevel,\n};\nuse embassy_stm32_wpan::sub::mm;\nuse {defmt_rtt as _, panic_probe as _};\n\nbind_interrupts!(struct Irqs{\n    IPCC_C1_RX => ReceiveInterruptHandler;\n    IPCC_C1_TX => TransmitInterruptHandler;\n});\n\n#[embassy_executor::task]\nasync fn run_mm_queue(mut memory_manager: mm::MemoryManager<'static>) {\n    memory_manager.run_queue().await;\n}\n\n#[cfg_attr(\n    feature = \"stop\",\n    embassy_executor::main(executor = \"embassy_stm32::executor::Executor\", entry = \"cortex_m_rt::entry\")\n)]\n#[cfg_attr(not(feature = \"stop\"), embassy_executor::main)]\nasync fn main(spawner: Spawner) {\n    let mut config = config();\n    config.rcc = WPAN_DEFAULT;\n\n    let p = init_with_config(config);\n    info!(\"Hello World!\");\n\n    let config = Config::default();\n    let mbox = TlMbox::init(p.IPCC, Irqs, config).await.unwrap();\n    let mut sys = mbox.sys_subsystem;\n    let (mut mac_rx, mut mac_tx) = mbox.mac_subsystem.split();\n\n    spawner.spawn(run_mm_queue(mbox.mm_subsystem).unwrap());\n\n    let result = sys.shci_c2_mac_802_15_4_init().await;\n    info!(\"initialized mac: {}\", result);\n\n    info!(\"resetting\");\n    mac_tx\n        .send_command(&ResetRequest {\n            set_default_pib: true,\n            ..Default::default()\n        })\n        .await\n        .unwrap();\n    {\n        let evt = mac_rx.read().await.unwrap();\n        info!(\"{:#x}\", evt);\n    }\n\n    info!(\"setting extended address\");\n    let extended_address: u64 = 0xACDE480000000002;\n    mac_tx\n        .send_command(&SetRequest {\n            pib_attribute_ptr: &extended_address as *const _ as *const u8,\n            pib_attribute: PibId::ExtendedAddress,\n        })\n        .await\n        .unwrap();\n    {\n        let evt = mac_rx.read().await.unwrap();\n        info!(\"{:#x}\", evt);\n    }\n\n    info!(\"getting extended address\");\n    mac_tx\n        .send_command(&GetRequest {\n            pib_attribute: PibId::ExtendedAddress,\n            ..Default::default()\n        })\n        .await\n        .unwrap();\n\n    {\n        let evt = mac_rx.read().await.unwrap();\n        info!(\"{:#x}\", evt);\n\n        if let MacEvent::MlmeGetCnf(evt) = evt {\n            if evt.pib_attribute_value_len == 8 {\n                let value = unsafe { core::ptr::read_unaligned(evt.pib_attribute_value_ptr as *const u64) };\n\n                info!(\"value {:#x}\", value)\n            }\n        }\n    }\n\n    info!(\"assocation request\");\n    let a = AssociateRequest {\n        channel_number: MacChannel::Channel16,\n        channel_page: 0,\n        coord_addr_mode: AddressMode::Short,\n        coord_address: MacAddress { short: [34, 17] },\n        capability_information: Capabilities::ALLOCATE_ADDRESS,\n        coord_pan_id: PanId([0x1A, 0xAA]),\n        security_level: SecurityLevel::Unsecure,\n        key_id_mode: KeyIdMode::Implicite,\n        key_source: [0; 8],\n        key_index: 152,\n    };\n    info!(\"{}\", a);\n    mac_tx.send_command(&a).await.unwrap();\n    let short_addr = if let MacEvent::MlmeAssociateCnf(conf) = mac_rx.read().await.unwrap() {\n        conf.assoc_short_address\n    } else {\n        defmt::panic!()\n    };\n\n    info!(\"{}\", short_addr);\n\n    info!(\"Test OK\");\n    cortex_m::asm::bkpt();\n}\n"
  },
  {
    "path": "tests/stm32/src/can_common.rs",
    "content": "use defmt::{assert, *};\nuse embassy_stm32::can;\nuse embassy_time::{Duration, Instant};\n\n#[derive(Clone, Copy, Debug)]\npub struct TestOptions {\n    pub max_latency: Duration,\n    pub max_buffered: u8,\n}\n\npub async fn run_can_tests<'d>(can: &mut can::Can<'d>, options: &TestOptions) {\n    //pub async fn run_can_tests<'d, T: can::Instance>(can: &mut can::Can<'d, T>, options: &TestOptions) {\n    let mut i: u8 = 0;\n    loop {\n        //let tx_frame = can::frame::Frame::new_standard(0x123, &[i, 0x12 as u8, 0x34 as u8, 0x56 as u8, 0x78 as u8, 0x9A as u8, 0xBC as u8 ]).unwrap();\n        let tx_frame = can::frame::Frame::new_standard(0x123, &[i; 1]).unwrap();\n\n        //info!(\"Transmitting frame...\");\n        let tx_ts = Instant::now();\n        can.write(&tx_frame).await;\n\n        let (frame, timestamp) = can.read().await.unwrap().parts();\n        //info!(\"Frame received!\");\n\n        // Check data.\n        assert!(i == frame.data()[0], \"{} == {}\", i, frame.data()[0]);\n\n        //info!(\"loopback time {}\", timestamp);\n        //info!(\"loopback frame {=u8}\", frame.data()[0]);\n        let latency = timestamp.saturating_duration_since(tx_ts);\n        info!(\"loopback latency {} us\", latency.as_micros());\n\n        // Theoretical minimum latency is 55us, actual is usually ~80us\n        const MIN_LATENCY: Duration = Duration::from_micros(50);\n        // Was failing at 150 but we are not getting a real time stamp. I'm not\n        // sure if there are other delays\n        assert!(\n            MIN_LATENCY <= latency && latency <= options.max_latency,\n            \"{} <= {} <= {}\",\n            MIN_LATENCY,\n            latency,\n            options.max_latency\n        );\n\n        i += 1;\n        if i > 5 {\n            break;\n        }\n    }\n\n    // Below here, check that we can receive from both FIFO0 and FIFO1\n    // Above we configured FIFO1 for extended ID packets. There are only 3 slots\n    // in each FIFO so make sure we write enough to fill them both up before reading.\n    for i in 0..options.max_buffered {\n        // Try filling up the RX FIFO0 buffers\n        //let tx_frame = if 0 != (i & 0x01) {\n        let tx_frame = if i < options.max_buffered / 2 {\n            info!(\"Transmitting standard frame {}\", i);\n            can::frame::Frame::new_standard(0x123, &[i; 1]).unwrap()\n        } else {\n            info!(\"Transmitting extended frame {}\", i);\n            can::frame::Frame::new_extended(0x1232344, &[i; 1]).unwrap()\n        };\n        can.write(&tx_frame).await;\n    }\n\n    // Try and receive all 6 packets\n    for _i in 0..options.max_buffered {\n        let (frame, _ts) = can.read().await.unwrap().parts();\n        match frame.id() {\n            embedded_can::Id::Extended(_id) => {\n                info!(\"Extended received! {}\", frame.data()[0]);\n                //info!(\"Extended received! {:x} {} {}\", id.as_raw(), frame.data()[0], i);\n            }\n            embedded_can::Id::Standard(_id) => {\n                info!(\"Standard received! {}\", frame.data()[0]);\n                //info!(\"Standard received! {:x} {} {}\", id.as_raw(), frame.data()[0], i);\n            }\n        }\n    }\n}\n\npub async fn run_split_can_tests<'d>(tx: &mut can::CanTx<'d>, rx: &mut can::CanRx<'d>, options: &TestOptions) {\n    for i in 0..options.max_buffered {\n        // Try filling up the RX FIFO0 buffers\n        //let tx_frame = if 0 != (i & 0x01) {\n        let tx_frame = if i < options.max_buffered / 2 {\n            info!(\"Transmitting standard frame {}\", i);\n            can::frame::Frame::new_standard(0x123, &[i; 1]).unwrap()\n        } else {\n            info!(\"Transmitting extended frame {}\", i);\n            can::frame::Frame::new_extended(0x1232344, &[i; 1]).unwrap()\n        };\n        tx.write(&tx_frame).await;\n    }\n\n    // Try and receive all 6 packets\n    for _i in 0..options.max_buffered {\n        let (frame, _ts) = rx.read().await.unwrap().parts();\n        match frame.id() {\n            embedded_can::Id::Extended(_id) => {\n                info!(\"Extended received! {}\", frame.data()[0]);\n            }\n            embedded_can::Id::Standard(_id) => {\n                info!(\"Standard received! {}\", frame.data()[0]);\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "tests/stm32/src/common.rs",
    "content": "#![macro_use]\n\npub use defmt::*;\nuse embassy_stm32::Config;\n#[allow(unused)]\nuse embassy_stm32::rcc::*;\n#[allow(unused)]\nuse embassy_stm32::time::Hertz;\nuse {defmt_rtt as _, panic_probe as _};\n\n#[cfg(feature = \"stm32f103c8\")]\nteleprobe_meta::target!(b\"bluepill-stm32f103c8\");\n#[cfg(feature = \"stm32g491re\")]\nteleprobe_meta::target!(b\"nucleo-stm32g491re\");\n#[cfg(feature = \"stm32g071rb\")]\nteleprobe_meta::target!(b\"nucleo-stm32g071rb\");\n#[cfg(feature = \"stm32f429zi\")]\nteleprobe_meta::target!(b\"nucleo-stm32f429zi\");\n#[cfg(feature = \"stm32f446re\")]\nteleprobe_meta::target!(b\"weact-stm32f446re\");\n#[cfg(feature = \"stm32wb55rg\")]\nteleprobe_meta::target!(b\"nucleo-stm32wb55rg\");\n#[cfg(feature = \"stm32h755zi\")]\nteleprobe_meta::target!(b\"nucleo-stm32h755zi\");\n#[cfg(feature = \"stm32h753zi\")]\nteleprobe_meta::target!(b\"nucleo-stm32h753zi\");\n#[cfg(feature = \"stm32h7a3zi\")]\nteleprobe_meta::target!(b\"nucleo-stm32h7a3zi\");\n#[cfg(feature = \"stm32u585ai\")]\nteleprobe_meta::target!(b\"iot-stm32u585ai\");\n#[cfg(feature = \"stm32u5a5zj\")]\nteleprobe_meta::target!(b\"nucleo-stm32u5a5zj\");\n#[cfg(feature = \"stm32h563zi\")]\nteleprobe_meta::target!(b\"nucleo-stm32h563zi\");\n#[cfg(feature = \"stm32c031c6\")]\nteleprobe_meta::target!(b\"nucleo-stm32c031c6\");\n#[cfg(feature = \"stm32c071rb\")]\nteleprobe_meta::target!(b\"nucleo-stm32c071rb\");\n#[cfg(feature = \"stm32l073rz\")]\nteleprobe_meta::target!(b\"nucleo-stm32l073rz\");\n#[cfg(feature = \"stm32l152re\")]\nteleprobe_meta::target!(b\"nucleo-stm32l152re\");\n#[cfg(feature = \"stm32l4a6zg\")]\nteleprobe_meta::target!(b\"nucleo-stm32l4a6zg\");\n#[cfg(feature = \"stm32l4r5zi\")]\nteleprobe_meta::target!(b\"nucleo-stm32l4r5zi\");\n#[cfg(feature = \"stm32l552ze\")]\nteleprobe_meta::target!(b\"nucleo-stm32l552ze\");\n#[cfg(feature = \"stm32f767zi\")]\nteleprobe_meta::target!(b\"nucleo-stm32f767zi\");\n#[cfg(feature = \"stm32f207zg\")]\nteleprobe_meta::target!(b\"nucleo-stm32f207zg\");\n#[cfg(feature = \"stm32f303ze\")]\nteleprobe_meta::target!(b\"nucleo-stm32f303ze\");\n#[cfg(feature = \"stm32l496zg\")]\nteleprobe_meta::target!(b\"nucleo-stm32l496zg\");\n#[cfg(feature = \"stm32wl55jc\")]\nteleprobe_meta::target!(b\"nucleo-stm32wl55jc\");\n#[cfg(feature = \"stm32wba52cg\")]\nteleprobe_meta::target!(b\"nucleo-stm32wba52cg\");\n#[cfg(feature = \"stm32wba65ri\")]\nteleprobe_meta::target!(b\"nucleo-stm32wba65ri\");\n#[cfg(feature = \"stm32f091rc\")]\nteleprobe_meta::target!(b\"nucleo-stm32f091rc\");\n#[cfg(feature = \"stm32h503rb\")]\nteleprobe_meta::target!(b\"nucleo-stm32h503rb\");\n#[cfg(feature = \"stm32h7s3l8\")]\nteleprobe_meta::target!(b\"nucleo-stm32h7s3l8\");\n#[cfg(feature = \"stm32u083rc\")]\nteleprobe_meta::target!(b\"nucleo-stm32u083rc\");\n\nmacro_rules! define_peris {\n    ($($name:ident = $peri:ident,)* $(@irq $irq_name:ident = $irq_code:tt,)*) => {\n        #[allow(unused_macros)]\n        macro_rules! peri {\n            $(\n                ($p:expr, $name) => {\n                    $p.$peri\n                };\n            )*\n        }\n        #[allow(unused_macros)]\n        macro_rules! irqs {\n            $(\n                ($irq_name) => {{\n                    embassy_stm32::bind_interrupts!(struct Irqs $irq_code);\n                    Irqs\n                }};\n            )*\n        }\n\n        #[allow(unused)]\n        #[allow(non_camel_case_types)]\n        pub mod peris {\n            $(\n                pub type $name = embassy_stm32::peripherals::$peri;\n            )*\n        }\n    };\n}\n\n#[cfg(feature = \"stm32f091rc\")]\ndefine_peris!(\n    UART = USART1, UART_TX = PA9, UART_RX = PA10, UART_TX_DMA = DMA1_CH4, UART_RX_DMA = DMA1_CH5,\n    SPI = SPI1, SPI_SCK = PA5, SPI_MOSI = PA7, SPI_MISO = PA6, SPI_TX_DMA = DMA1_CH3, SPI_RX_DMA = DMA1_CH2,\n    @irq UART = {\n        USART1 => embassy_stm32::usart::InterruptHandler<embassy_stm32::peripherals::USART1>;\n        DMA1_CH1 => embassy_stm32::dma::InterruptHandler<embassy_stm32::peripherals::DMA1_CH1>;\n        DMA1_CH2_3_DMA2_CH1_2 => embassy_stm32::dma::InterruptHandler<embassy_stm32::peripherals::DMA1_CH2>,\n        embassy_stm32::dma::InterruptHandler<embassy_stm32::peripherals::DMA1_CH3>;\n        DMA1_CH4_7_DMA2_CH3_5 => embassy_stm32::dma::InterruptHandler<embassy_stm32::peripherals::DMA1_CH4>,\n        embassy_stm32::dma::InterruptHandler<embassy_stm32::peripherals::DMA1_CH5>;\n    },\n);\n#[cfg(any(feature = \"stm32f100rd\", feature = \"stm32f103c8\", feature = \"stm32f107vc\"))]\ndefine_peris!(\n    UART = USART1, UART_TX = PA9, UART_RX = PA10, UART_TX_DMA = DMA1_CH4, UART_RX_DMA = DMA1_CH5,\n    SPI = SPI1, SPI_SCK = PA5, SPI_MOSI = PA7, SPI_MISO = PA6, SPI_TX_DMA = DMA1_CH3, SPI_RX_DMA = DMA1_CH2,\n    @irq UART = {\n        USART1 => embassy_stm32::usart::InterruptHandler<embassy_stm32::peripherals::USART1>;\n        DMA1_CHANNEL2 => embassy_stm32::dma::InterruptHandler<embassy_stm32::peripherals::DMA1_CH2>;\n        DMA1_CHANNEL3 => embassy_stm32::dma::InterruptHandler<embassy_stm32::peripherals::DMA1_CH3>;\n        DMA1_CHANNEL4 => embassy_stm32::dma::InterruptHandler<embassy_stm32::peripherals::DMA1_CH4>;\n        DMA1_CHANNEL5 => embassy_stm32::dma::InterruptHandler<embassy_stm32::peripherals::DMA1_CH5>;\n    },\n);\n#[cfg(feature = \"stm32g491re\")]\ndefine_peris!(\n    UART = USART1, UART_TX = PC4, UART_RX = PC5, UART_TX_DMA = DMA1_CH1, UART_RX_DMA = DMA1_CH2,\n    SPI = SPI1, SPI_SCK = PA5, SPI_MOSI = PA7, SPI_MISO = PA6, SPI_TX_DMA = DMA1_CH1, SPI_RX_DMA = DMA1_CH2,\n    @irq UART = {\n        USART1 => embassy_stm32::usart::InterruptHandler<embassy_stm32::peripherals::USART1>;\n        DMA1_CHANNEL1 => embassy_stm32::dma::InterruptHandler<embassy_stm32::peripherals::DMA1_CH1>;\n        DMA1_CHANNEL2 => embassy_stm32::dma::InterruptHandler<embassy_stm32::peripherals::DMA1_CH2>;\n        DMA1_CHANNEL4 => embassy_stm32::dma::InterruptHandler<embassy_stm32::peripherals::DMA1_CH4>;\n        DMA1_CHANNEL5 => embassy_stm32::dma::InterruptHandler<embassy_stm32::peripherals::DMA1_CH5>;\n        RNG => embassy_stm32::rng::InterruptHandler<embassy_stm32::peripherals::RNG>;\n    },\n);\n#[cfg(feature = \"stm32g071rb\")]\ndefine_peris!(\n    UART = USART1, UART_TX = PC4, UART_RX = PC5, UART_TX_DMA = DMA1_CH1, UART_RX_DMA = DMA1_CH2,\n    SPI = SPI1, SPI_SCK = PA5, SPI_MOSI = PA7, SPI_MISO = PA6, SPI_TX_DMA = DMA1_CH1, SPI_RX_DMA = DMA1_CH2,\n    ADC = ADC1, DAC = DAC1, DAC_PIN = PA4,\n    @irq UART = {\n        USART1 => embassy_stm32::usart::InterruptHandler<embassy_stm32::peripherals::USART1>;\n        DMA1_CHANNEL1 => embassy_stm32::dma::InterruptHandler<embassy_stm32::peripherals::DMA1_CH1>;\n        DMA1_CHANNEL2_3 => embassy_stm32::dma::InterruptHandler<embassy_stm32::peripherals::DMA1_CH2>,\n        embassy_stm32::dma::InterruptHandler<embassy_stm32::peripherals::DMA1_CH3>;\n        DMA1_CH4_7_DMAMUX1_OVR => embassy_stm32::dma::InterruptHandler<embassy_stm32::peripherals::DMA1_CH4>;\n    },\n);\n#[cfg(feature = \"stm32f429zi\")]\ndefine_peris!(\n    UART = USART6, UART_TX = PG14, UART_RX = PG9, UART_TX_DMA = DMA2_CH6, UART_RX_DMA = DMA2_CH1,\n    SPI = SPI1, SPI_SCK = PA5, SPI_MOSI = PA7, SPI_MISO = PA6, SPI_TX_DMA = DMA2_CH3, SPI_RX_DMA = DMA2_CH2,\n    ADC = ADC1, DAC = DAC1, DAC_PIN = PA4,\n    CAN = CAN1, CAN_RX = PD0, CAN_TX = PD1,\n    @irq UART = {\n        USART6 => embassy_stm32::usart::InterruptHandler<embassy_stm32::peripherals::USART6>;\n        DMA2_STREAM1 => embassy_stm32::dma::InterruptHandler<embassy_stm32::peripherals::DMA2_CH1>;\n        DMA2_STREAM6 => embassy_stm32::dma::InterruptHandler<embassy_stm32::peripherals::DMA2_CH6>;\n        DMA2_STREAM2 => embassy_stm32::dma::InterruptHandler<embassy_stm32::peripherals::DMA2_CH2>;\n        DMA2_STREAM3 => embassy_stm32::dma::InterruptHandler<embassy_stm32::peripherals::DMA2_CH3>;\n    },\n);\n#[cfg(feature = \"stm32f446re\")]\ndefine_peris!(\n    UART = USART1, UART_TX = PA9, UART_RX = PA10, UART_TX_DMA = DMA2_CH7, UART_RX_DMA = DMA2_CH5,\n    SPI = SPI1, SPI_SCK = PA5, SPI_MOSI = PA7, SPI_MISO = PA6, SPI_TX_DMA = DMA2_CH3, SPI_RX_DMA = DMA2_CH2,\n    ADC = ADC1, DAC = DAC1, DAC_PIN = PA4,\n    CAN = CAN1, CAN_RX = PA11, CAN_TX = PA12,\n    @irq UART = {\n        USART1 => embassy_stm32::usart::InterruptHandler<embassy_stm32::peripherals::USART1>;\n        DMA2_STREAM5 => embassy_stm32::dma::InterruptHandler<embassy_stm32::peripherals::DMA2_CH5>;\n        DMA2_STREAM7 => embassy_stm32::dma::InterruptHandler<embassy_stm32::peripherals::DMA2_CH7>;\n        DMA2_STREAM2 => embassy_stm32::dma::InterruptHandler<embassy_stm32::peripherals::DMA2_CH2>;\n        DMA2_STREAM3 => embassy_stm32::dma::InterruptHandler<embassy_stm32::peripherals::DMA2_CH3>;\n    },\n);\n#[cfg(feature = \"stm32wb55rg\")]\ndefine_peris!(\n    UART = LPUART1, UART_TX = PA2, UART_RX = PA3, UART_TX_DMA = DMA1_CH1, UART_RX_DMA = DMA1_CH2,\n    SPI = SPI1, SPI_SCK = PA5, SPI_MOSI = PA7, SPI_MISO = PA6, SPI_TX_DMA = DMA1_CH1, SPI_RX_DMA = DMA1_CH2,\n    @irq UART = {\n        LPUART1 => embassy_stm32::usart::InterruptHandler<embassy_stm32::peripherals::LPUART1>;\n        DMA1_CHANNEL1 => embassy_stm32::dma::InterruptHandler<embassy_stm32::peripherals::DMA1_CH1>;\n        DMA1_CHANNEL2 => embassy_stm32::dma::InterruptHandler<embassy_stm32::peripherals::DMA1_CH2>;\n    },\n);\n#[cfg(any(feature = \"stm32h755zi\", feature = \"stm32h753zi\"))]\ndefine_peris!(\n    CRYP_IN_DMA = DMA1_CH0, CRYP_OUT_DMA = DMA1_CH1,\n    UART = USART1, UART_TX = PB6, UART_RX = PB7, UART_TX_DMA = DMA1_CH0, UART_RX_DMA = DMA1_CH1,\n    SPI = SPI1, SPI_SCK = PA5, SPI_MOSI = PB5, SPI_MISO = PA6, SPI_TX_DMA = DMA1_CH0, SPI_RX_DMA = DMA1_CH1,\n    ADC = ADC1, DAC = DAC1, DAC_PIN = PA4,\n    @irq UART = {\n        CRYP => embassy_stm32::cryp::InterruptHandler<embassy_stm32::peripherals::CRYP>;\n        USART1 => embassy_stm32::usart::InterruptHandler<embassy_stm32::peripherals::USART1>;\n        DMA1_STREAM0 => embassy_stm32::dma::InterruptHandler<embassy_stm32::peripherals::DMA1_CH0>;\n        DMA1_STREAM1 => embassy_stm32::dma::InterruptHandler<embassy_stm32::peripherals::DMA1_CH1>;\n    },\n);\n#[cfg(feature = \"stm32h7a3zi\")]\ndefine_peris!(\n    UART = USART1, UART_TX = PB6, UART_RX = PB7, UART_TX_DMA = DMA1_CH0, UART_RX_DMA = DMA1_CH1,\n    SPI = SPI1, SPI_SCK = PA5, SPI_MOSI = PA7, SPI_MISO = PA6, SPI_TX_DMA = DMA1_CH0, SPI_RX_DMA = DMA1_CH1,\n    @irq UART = {\n        USART1 => embassy_stm32::usart::InterruptHandler<embassy_stm32::peripherals::USART1>;\n        DMA1_STREAM0 => embassy_stm32::dma::InterruptHandler<embassy_stm32::peripherals::DMA1_CH0>;\n        DMA1_STREAM1 => embassy_stm32::dma::InterruptHandler<embassy_stm32::peripherals::DMA1_CH1>;\n    },\n);\n#[cfg(feature = \"stm32u585ai\")]\ndefine_peris!(\n    UART = USART3, UART_TX = PD8, UART_RX = PD9, UART_TX_DMA = GPDMA1_CH0, UART_RX_DMA = GPDMA1_CH1,\n    SPI = SPI1, SPI_SCK = PE13, SPI_MOSI = PE15, SPI_MISO = PE14, SPI_TX_DMA = GPDMA1_CH0, SPI_RX_DMA = GPDMA1_CH1,\n    @irq UART = {\n        RNG => embassy_stm32::rng::InterruptHandler<embassy_stm32::peripherals::RNG>;\n        USART3 => embassy_stm32::usart::InterruptHandler<embassy_stm32::peripherals::USART3>;\n        GPDMA1_CHANNEL0 => embassy_stm32::dma::InterruptHandler<embassy_stm32::peripherals::GPDMA1_CH0>;\n        GPDMA1_CHANNEL1 => embassy_stm32::dma::InterruptHandler<embassy_stm32::peripherals::GPDMA1_CH1>;\n    },\n);\n#[cfg(feature = \"stm32u5a5zj\")]\ndefine_peris!(\n    UART = LPUART1, UART_TX = PG7, UART_RX = PG8, UART_TX_DMA = GPDMA1_CH0, UART_RX_DMA = GPDMA1_CH1,\n    SPI = SPI1, SPI_SCK = PA5, SPI_MOSI = PA7, SPI_MISO = PA6, SPI_TX_DMA = GPDMA1_CH0, SPI_RX_DMA = GPDMA1_CH1,\n    @irq UART = {\n        RNG => embassy_stm32::rng::InterruptHandler<embassy_stm32::peripherals::RNG>;\n        LPUART1 => embassy_stm32::usart::InterruptHandler<embassy_stm32::peripherals::LPUART1>;\n        GPDMA1_CHANNEL0 => embassy_stm32::dma::InterruptHandler<embassy_stm32::peripherals::GPDMA1_CH0>;\n        GPDMA1_CHANNEL1 => embassy_stm32::dma::InterruptHandler<embassy_stm32::peripherals::GPDMA1_CH1>;\n    },\n);\n#[cfg(feature = \"stm32h563zi\")]\ndefine_peris!(\n    UART = LPUART1, UART_TX = PB6, UART_RX = PB7, UART_TX_DMA = GPDMA1_CH0, UART_RX_DMA = GPDMA1_CH1,\n    SPI = SPI4, SPI_SCK = PE12, SPI_MOSI = PE14, SPI_MISO = PE13, SPI_TX_DMA = GPDMA1_CH0, SPI_RX_DMA = GPDMA1_CH1,\n    @irq UART = {\n        RNG => embassy_stm32::rng::InterruptHandler<embassy_stm32::peripherals::RNG>;\n        LPUART1 => embassy_stm32::usart::InterruptHandler<embassy_stm32::peripherals::LPUART1>;\n        GPDMA1_CHANNEL0 => embassy_stm32::dma::InterruptHandler<embassy_stm32::peripherals::GPDMA1_CH0>;\n        GPDMA1_CHANNEL1 => embassy_stm32::dma::InterruptHandler<embassy_stm32::peripherals::GPDMA1_CH1>;\n        GPDMA1_CHANNEL5 => embassy_stm32::dma::InterruptHandler<embassy_stm32::peripherals::GPDMA1_CH5>;\n        GPDMA1_CHANNEL4 => embassy_stm32::dma::InterruptHandler<embassy_stm32::peripherals::GPDMA1_CH4>;\n    },\n);\n#[cfg(feature = \"stm32h503rb\")]\ndefine_peris!(\n    UART = USART1, UART_TX = PB14, UART_RX = PB15, UART_TX_DMA = GPDMA1_CH0, UART_RX_DMA = GPDMA1_CH1,\n    SPI = SPI1, SPI_SCK = PA5, SPI_MOSI = PA7, SPI_MISO = PA6, SPI_TX_DMA = GPDMA1_CH0, SPI_RX_DMA = GPDMA1_CH1,\n    @irq UART = {\n        USART1 => embassy_stm32::usart::InterruptHandler<embassy_stm32::peripherals::USART1>;\n        GPDMA1_CHANNEL0 => embassy_stm32::dma::InterruptHandler<embassy_stm32::peripherals::GPDMA1_CH0>;\n        GPDMA1_CHANNEL1 => embassy_stm32::dma::InterruptHandler<embassy_stm32::peripherals::GPDMA1_CH1>;\n    },\n);\n#[cfg(feature = \"stm32c031c6\")]\ndefine_peris!(\n    UART = USART1, UART_TX = PB6, UART_RX = PB7, UART_TX_DMA = DMA1_CH1, UART_RX_DMA = DMA1_CH2,\n    SPI = SPI1, SPI_SCK = PA5, SPI_MOSI = PA7, SPI_MISO = PA6, SPI_TX_DMA = DMA1_CH1, SPI_RX_DMA = DMA1_CH2,\n    @irq UART = {\n        USART1 => embassy_stm32::usart::InterruptHandler<embassy_stm32::peripherals::USART1>;\n        DMA1_CHANNEL1 => embassy_stm32::dma::InterruptHandler<embassy_stm32::peripherals::DMA1_CH1>;\n        DMA1_CHANNEL2_3 => embassy_stm32::dma::InterruptHandler<embassy_stm32::peripherals::DMA1_CH2>;\n    },\n);\n#[cfg(feature = \"stm32c071rb\")]\ndefine_peris!(\n    UART = USART1, UART_TX = PB6, UART_RX = PB7, UART_TX_DMA = DMA1_CH1, UART_RX_DMA = DMA1_CH2,\n    SPI = SPI1, SPI_SCK = PA5, SPI_MOSI = PA7, SPI_MISO = PA6, SPI_TX_DMA = DMA1_CH1, SPI_RX_DMA = DMA1_CH2,\n    @irq UART = {\n        USART1 => embassy_stm32::usart::InterruptHandler<embassy_stm32::peripherals::USART1>;\n        DMA1_CHANNEL1 => embassy_stm32::dma::InterruptHandler<embassy_stm32::peripherals::DMA1_CH1>;\n        DMA1_CHANNEL2_3 => embassy_stm32::dma::InterruptHandler<embassy_stm32::peripherals::DMA1_CH2>;\n    },\n);\n#[cfg(feature = \"stm32l496zg\")]\ndefine_peris!(\n    UART = USART3, UART_TX = PD8, UART_RX = PD9, UART_TX_DMA = DMA1_CH2, UART_RX_DMA = DMA1_CH3,\n    SPI = SPI1, SPI_SCK = PA5, SPI_MOSI = PA7, SPI_MISO = PA6, SPI_TX_DMA = DMA1_CH3, SPI_RX_DMA = DMA1_CH2,\n    @irq UART = {\n        USART3 => embassy_stm32::usart::InterruptHandler<embassy_stm32::peripherals::USART3>;\n        DMA1_CHANNEL2 => embassy_stm32::dma::InterruptHandler<embassy_stm32::peripherals::DMA1_CH2>;\n        DMA1_CHANNEL3 => embassy_stm32::dma::InterruptHandler<embassy_stm32::peripherals::DMA1_CH3>;\n    },\n);\n#[cfg(feature = \"stm32l4a6zg\")]\ndefine_peris!(\n    UART = USART3, UART_TX = PD8, UART_RX = PD9, UART_TX_DMA = DMA1_CH2, UART_RX_DMA = DMA1_CH3,\n    SPI = SPI1, SPI_SCK = PA5, SPI_MOSI = PA7, SPI_MISO = PA6, SPI_TX_DMA = DMA1_CH3, SPI_RX_DMA = DMA1_CH2,\n    @irq UART = {\n        USART3 => embassy_stm32::usart::InterruptHandler<embassy_stm32::peripherals::USART3>;\n        DMA1_CHANNEL2 => embassy_stm32::dma::InterruptHandler<embassy_stm32::peripherals::DMA1_CH2>;\n        DMA1_CHANNEL3 => embassy_stm32::dma::InterruptHandler<embassy_stm32::peripherals::DMA1_CH3>;\n    },\n);\n#[cfg(feature = \"stm32l4r5zi\")]\ndefine_peris!(\n    UART = USART3, UART_TX = PD8, UART_RX = PD9, UART_TX_DMA = DMA1_CH1, UART_RX_DMA = DMA1_CH2,\n    SPI = SPI1, SPI_SCK = PA5, SPI_MOSI = PA7, SPI_MISO = PA6, SPI_TX_DMA = DMA1_CH1, SPI_RX_DMA = DMA1_CH2,\n    @irq UART = {\n        USART3 => embassy_stm32::usart::InterruptHandler<embassy_stm32::peripherals::USART3>;\n        DMA1_CHANNEL1 => embassy_stm32::dma::InterruptHandler<embassy_stm32::peripherals::DMA1_CH1>;\n        DMA1_CHANNEL2 => embassy_stm32::dma::InterruptHandler<embassy_stm32::peripherals::DMA1_CH2>;\n    },\n);\n#[cfg(feature = \"stm32l073rz\")]\ndefine_peris!(\n    UART = USART4, UART_TX = PA0, UART_RX = PA1, UART_TX_DMA = DMA1_CH3, UART_RX_DMA = DMA1_CH2,\n    SPI = SPI1, SPI_SCK = PA5, SPI_MOSI = PA7, SPI_MISO = PA6, SPI_TX_DMA = DMA1_CH3, SPI_RX_DMA = DMA1_CH2,\n    @irq UART = {\n        USART4_5 => embassy_stm32::usart::InterruptHandler<embassy_stm32::peripherals::USART4>;\n        DMA1_CHANNEL2_3 => embassy_stm32::dma::InterruptHandler<embassy_stm32::peripherals::DMA1_CH2>,\n        embassy_stm32::dma::InterruptHandler<embassy_stm32::peripherals::DMA1_CH3>;\n    },\n);\n#[cfg(feature = \"stm32l152re\")]\ndefine_peris!(\n    UART = USART3, UART_TX = PB10, UART_RX = PB11, UART_TX_DMA = DMA1_CH2, UART_RX_DMA = DMA1_CH3,\n    SPI = SPI1, SPI_SCK = PA5, SPI_MOSI = PA7, SPI_MISO = PA6, SPI_TX_DMA = DMA1_CH3, SPI_RX_DMA = DMA1_CH2,\n    ADC = ADC1, DAC = DAC1, DAC_PIN = PA4,\n    @irq UART = {\n        USART3 => embassy_stm32::usart::InterruptHandler<embassy_stm32::peripherals::USART3>;\n        DMA1_CHANNEL3 => embassy_stm32::dma::InterruptHandler<embassy_stm32::peripherals::DMA1_CH3>;\n        DMA1_CHANNEL2 => embassy_stm32::dma::InterruptHandler<embassy_stm32::peripherals::DMA1_CH2>;\n    },\n);\n#[cfg(feature = \"stm32l552ze\")]\ndefine_peris!(\n    UART = USART3, UART_TX = PD8, UART_RX = PD9, UART_TX_DMA = DMA1_CH1, UART_RX_DMA = DMA1_CH2,\n    SPI = SPI1, SPI_SCK = PA5, SPI_MOSI = PA7, SPI_MISO = PA6, SPI_TX_DMA = DMA1_CH1, SPI_RX_DMA = DMA1_CH2,\n    @irq UART = {\n        USART3 => embassy_stm32::usart::InterruptHandler<embassy_stm32::peripherals::USART3>;\n        DMA1_CHANNEL1 => embassy_stm32::dma::InterruptHandler<embassy_stm32::peripherals::DMA1_CH1>;\n        DMA1_CHANNEL2 => embassy_stm32::dma::InterruptHandler<embassy_stm32::peripherals::DMA1_CH2>;\n    },\n);\n#[cfg(feature = \"stm32f767zi\")]\ndefine_peris!(\n    UART = USART6, UART_TX = PG14, UART_RX = PG9, UART_TX_DMA = DMA2_CH6, UART_RX_DMA = DMA2_CH1,\n    SPI = SPI1, SPI_SCK = PA5, SPI_MOSI = PA7, SPI_MISO = PA6, SPI_TX_DMA = DMA2_CH3, SPI_RX_DMA = DMA2_CH2,\n    @irq UART = {\n        USART6 => embassy_stm32::usart::InterruptHandler<embassy_stm32::peripherals::USART6>;\n        DMA2_STREAM3 => embassy_stm32::dma::InterruptHandler<embassy_stm32::peripherals::DMA2_CH3>;\n        DMA2_STREAM2 => embassy_stm32::dma::InterruptHandler<embassy_stm32::peripherals::DMA2_CH2>;\n        DMA2_STREAM1 => embassy_stm32::dma::InterruptHandler<embassy_stm32::peripherals::DMA2_CH1>;\n        DMA2_STREAM6 => embassy_stm32::dma::InterruptHandler<embassy_stm32::peripherals::DMA2_CH6>;\n    },\n);\n#[cfg(feature = \"stm32f207zg\")]\ndefine_peris!(\n    UART = USART6, UART_TX = PG14, UART_RX = PG9, UART_TX_DMA = DMA2_CH6, UART_RX_DMA = DMA2_CH1,\n    SPI = SPI1, SPI_SCK = PA5, SPI_MOSI = PA7, SPI_MISO = PA6, SPI_TX_DMA = DMA2_CH3, SPI_RX_DMA = DMA2_CH2,\n    @irq UART = {\n        USART6 => embassy_stm32::usart::InterruptHandler<embassy_stm32::peripherals::USART6>;\n        DMA2_STREAM1 => embassy_stm32::dma::InterruptHandler<embassy_stm32::peripherals::DMA2_CH1>;\n        DMA2_STREAM2 => embassy_stm32::dma::InterruptHandler<embassy_stm32::peripherals::DMA2_CH2>;\n        DMA2_STREAM3 => embassy_stm32::dma::InterruptHandler<embassy_stm32::peripherals::DMA2_CH3>;\n        DMA2_STREAM6 => embassy_stm32::dma::InterruptHandler<embassy_stm32::peripherals::DMA2_CH6>;\n    },\n);\n#[cfg(feature = \"stm32f303ze\")]\ndefine_peris!(\n    UART = USART1, UART_TX = PC4, UART_RX = PC5, UART_TX_DMA = DMA1_CH4, UART_RX_DMA = DMA1_CH5,\n    SPI = SPI1, SPI_SCK = PA5, SPI_MOSI = PA7, SPI_MISO = PA6, SPI_TX_DMA = DMA1_CH3, SPI_RX_DMA = DMA1_CH2,\n    @irq UART = {\n        USART1 => embassy_stm32::usart::InterruptHandler<embassy_stm32::peripherals::USART1>;\n        DMA1_CHANNEL4 => embassy_stm32::dma::InterruptHandler<embassy_stm32::peripherals::DMA1_CH4>;\n        DMA1_CHANNEL5 => embassy_stm32::dma::InterruptHandler<embassy_stm32::peripherals::DMA1_CH5>;\n        DMA1_CHANNEL2 => embassy_stm32::dma::InterruptHandler<embassy_stm32::peripherals::DMA1_CH2>;\n        DMA1_CHANNEL3 => embassy_stm32::dma::InterruptHandler<embassy_stm32::peripherals::DMA1_CH3>;\n    },\n);\n#[cfg(feature = \"stm32wl55jc\")]\ndefine_peris!(\n    UART = USART1, UART_TX = PB6, UART_RX = PB7, UART_TX_DMA = DMA1_CH4, UART_RX_DMA = DMA1_CH5,\n    SPI = SPI1, SPI_SCK = PA5, SPI_MOSI = PA7, SPI_MISO = PA6, SPI_TX_DMA = DMA1_CH3, SPI_RX_DMA = DMA1_CH2,\n    @irq UART = {\n        USART1 => embassy_stm32::usart::InterruptHandler<embassy_stm32::peripherals::USART1>;\n        DMA1_CHANNEL4 => embassy_stm32::dma::InterruptHandler<embassy_stm32::peripherals::DMA1_CH4>;\n        DMA1_CHANNEL5 => embassy_stm32::dma::InterruptHandler<embassy_stm32::peripherals::DMA1_CH5>;\n        DMA1_CHANNEL2 => embassy_stm32::dma::InterruptHandler<embassy_stm32::peripherals::DMA1_CH2>;\n        DMA1_CHANNEL3 => embassy_stm32::dma::InterruptHandler<embassy_stm32::peripherals::DMA1_CH3>;\n    },\n);\n#[cfg(feature = \"stm32wba52cg\")]\ndefine_peris!(\n    UART = LPUART1, UART_TX = PB5, UART_RX = PA10, UART_TX_DMA = GPDMA1_CH0, UART_RX_DMA = GPDMA1_CH1,\n    SPI = SPI1, SPI_SCK = PB4, SPI_MOSI = PA15, SPI_MISO = PB3, SPI_TX_DMA = GPDMA1_CH0, SPI_RX_DMA = GPDMA1_CH1,\n    ADC = ADC4, DAC_PIN = PA0,\n    @irq UART = {\n        LPUART1 => embassy_stm32::usart::InterruptHandler<embassy_stm32::peripherals::LPUART1>;\n        GPDMA1_CHANNEL0 => embassy_stm32::dma::InterruptHandler<embassy_stm32::peripherals::GPDMA1_CH0>;\n        GPDMA1_CHANNEL1 => embassy_stm32::dma::InterruptHandler<embassy_stm32::peripherals::GPDMA1_CH1>;\n    },\n);\n#[cfg(feature = \"stm32wba65ri\")]\ndefine_peris!(\n    UART = LPUART1, UART_TX = PB11, UART_RX = PA10, UART_TX_DMA = GPDMA1_CH0, UART_RX_DMA = GPDMA1_CH1,\n    SPI = SPI2, SPI_SCK = PB10, SPI_MOSI = PC3, SPI_MISO = PA9, SPI_TX_DMA = GPDMA1_CH0, SPI_RX_DMA = GPDMA1_CH1,\n    ADC = ADC4, DAC_PIN = PA0,\n    @irq UART = {\n        LPUART1 => embassy_stm32::usart::InterruptHandler<embassy_stm32::peripherals::LPUART1>;\n        GPDMA1_CHANNEL0 => embassy_stm32::dma::InterruptHandler<embassy_stm32::peripherals::GPDMA1_CH0>;\n        GPDMA1_CHANNEL1 => embassy_stm32::dma::InterruptHandler<embassy_stm32::peripherals::GPDMA1_CH1>;\n    },\n);\n#[cfg(feature = \"stm32h7s3l8\")]\ndefine_peris!(\n    CRYP_IN_DMA = GPDMA1_CH0, CRYP_OUT_DMA = GPDMA1_CH1,\n    UART = USART1, UART_TX = PB14, UART_RX = PA10, UART_TX_DMA = GPDMA1_CH0, UART_RX_DMA = GPDMA1_CH1,\n    SPI = SPI1, SPI_SCK = PA5, SPI_MOSI = PB5, SPI_MISO = PA6, SPI_TX_DMA = GPDMA1_CH0, SPI_RX_DMA = GPDMA1_CH1,\n    @irq UART = {\n        RNG => embassy_stm32::rng::InterruptHandler<embassy_stm32::peripherals::RNG>;\n        USART1 => embassy_stm32::usart::InterruptHandler<embassy_stm32::peripherals::USART1>;\n        GPDMA1_CHANNEL0 => embassy_stm32::dma::InterruptHandler<embassy_stm32::peripherals::GPDMA1_CH0>;\n        GPDMA1_CHANNEL1 => embassy_stm32::dma::InterruptHandler<embassy_stm32::peripherals::GPDMA1_CH1>;\n    },\n);\n#[cfg(feature = \"stm32u083rc\")]\ndefine_peris!(\n    UART = USART1, UART_TX = PA9, UART_RX = PA10, UART_TX_DMA = DMA1_CH1, UART_RX_DMA = DMA1_CH2,\n    SPI = SPI1, SPI_SCK = PA5, SPI_MOSI = PA7, SPI_MISO = PA6, SPI_TX_DMA = DMA1_CH1, SPI_RX_DMA = DMA1_CH2,\n    @irq UART = {\n        USART1 => embassy_stm32::usart::InterruptHandler<embassy_stm32::peripherals::USART1>;\n        DMA1_CHANNEL1 => embassy_stm32::dma::InterruptHandler<embassy_stm32::peripherals::DMA1_CH1>;\n        DMA1_CHANNEL2_3 => embassy_stm32::dma::InterruptHandler<embassy_stm32::peripherals::DMA1_CH2>;\n    },\n);\n\npub fn config() -> Config {\n    #[allow(unused_mut)]\n    let mut config = Config::default();\n\n    #[cfg(any(feature = \"stm32c031c6\", feature = \"stm32c071rb\"))]\n    {\n        config.rcc.hsi = Some(Hsi {\n            sys_div: HsiSysDiv::DIV1, // 48Mhz\n            ker_div: HsiKerDiv::DIV3, // 16Mhz\n        });\n        config.rcc.sys = Sysclk::HSISYS;\n        config.rcc.ahb_pre = AHBPrescaler::DIV1;\n        config.rcc.apb1_pre = APBPrescaler::DIV1;\n    }\n\n    #[cfg(feature = \"stm32g071rb\")]\n    {\n        config.rcc.hsi = Some(Hsi {\n            sys_div: HsiSysDiv::DIV1,\n        });\n        config.rcc.pll = Some(Pll {\n            source: PllSource::HSI,\n            prediv: PllPreDiv::DIV1,\n            mul: PllMul::MUL16,\n            divp: None,\n            divq: None,\n            divr: Some(PllRDiv::DIV4), // 16 / 1 * 16 / 4 = 64 Mhz\n        });\n        config.rcc.sys = Sysclk::PLL1_R;\n    }\n    #[cfg(feature = \"stm32wb55rg\")]\n    {\n        config.rcc = embassy_stm32::rcc::WPAN_DEFAULT;\n    }\n\n    #[cfg(feature = \"stm32f091rc\")]\n    {\n        config.rcc.hse = Some(Hse {\n            freq: Hertz(8_000_000),\n            mode: HseMode::Bypass,\n        });\n        config.rcc.pll = Some(Pll {\n            src: PllSource::HSE,\n            prediv: PllPreDiv::DIV1,\n            mul: PllMul::MUL6,\n        });\n        config.rcc.sys = Sysclk::PLL1_P;\n        config.rcc.ahb_pre = AHBPrescaler::DIV1;\n        config.rcc.apb1_pre = APBPrescaler::DIV1;\n    }\n    #[cfg(feature = \"stm32f103c8\")]\n    {\n        config.rcc.hse = Some(Hse {\n            freq: Hertz(8_000_000),\n            mode: HseMode::Oscillator,\n        });\n        config.rcc.pll = Some(Pll {\n            src: PllSource::HSE,\n            prediv: PllPreDiv::DIV1,\n            mul: PllMul::MUL9,\n        });\n        config.rcc.sys = Sysclk::PLL1_P;\n        config.rcc.ahb_pre = AHBPrescaler::DIV1;\n        config.rcc.apb1_pre = APBPrescaler::DIV2;\n        config.rcc.apb2_pre = APBPrescaler::DIV1;\n    }\n    #[cfg(feature = \"stm32f207zg\")]\n    {\n        // By default, HSE on the board comes from a 8 MHz clock signal (not a crystal)\n        config.rcc.hse = Some(Hse {\n            freq: Hertz(8_000_000),\n            mode: HseMode::Bypass,\n        });\n        // PLL uses HSE as the clock source\n        config.rcc.pll_src = PllSource::HSE;\n        config.rcc.pll = Some(Pll {\n            // 8 MHz clock source / 8 = 1 MHz PLL input\n            prediv: unwrap!(PllPreDiv::try_from(8)),\n            // 1 MHz PLL input * 240 = 240 MHz PLL VCO\n            mul: unwrap!(PllMul::try_from(240)),\n            // 240 MHz PLL VCO / 2 = 120 MHz main PLL output\n            divp: Some(PllPDiv::DIV2),\n            // 240 MHz PLL VCO / 5 = 48 MHz PLL48 output\n            divq: Some(PllQDiv::DIV5),\n            divr: None,\n        });\n        // System clock comes from PLL (= the 120 MHz main PLL output)\n        config.rcc.sys = Sysclk::PLL1_P;\n        // 120 MHz / 4 = 30 MHz APB1 frequency\n        config.rcc.apb1_pre = APBPrescaler::DIV4;\n        // 120 MHz / 2 = 60 MHz APB2 frequency\n        config.rcc.apb2_pre = APBPrescaler::DIV2;\n    }\n\n    #[cfg(feature = \"stm32f303ze\")]\n    {\n        config.rcc.hse = Some(Hse {\n            freq: Hertz(8_000_000),\n            mode: HseMode::Bypass,\n        });\n        config.rcc.pll = Some(Pll {\n            src: PllSource::HSE,\n            prediv: PllPreDiv::DIV1,\n            mul: PllMul::MUL9,\n        });\n        config.rcc.sys = Sysclk::PLL1_P;\n        config.rcc.ahb_pre = AHBPrescaler::DIV1;\n        config.rcc.apb1_pre = APBPrescaler::DIV2;\n        config.rcc.apb2_pre = APBPrescaler::DIV1;\n    }\n\n    #[cfg(feature = \"stm32f429zi\")]\n    {\n        config.rcc.hse = Some(Hse {\n            freq: Hertz(8_000_000),\n            mode: HseMode::Bypass,\n        });\n        config.rcc.pll_src = PllSource::HSE;\n        config.rcc.pll = Some(Pll {\n            prediv: PllPreDiv::DIV4,\n            mul: PllMul::MUL180,\n            divp: Some(PllPDiv::DIV2), // 8mhz / 4 * 180 / 2 = 180Mhz.\n            divq: None,\n            divr: None,\n        });\n        config.rcc.ahb_pre = AHBPrescaler::DIV1;\n        config.rcc.apb1_pre = APBPrescaler::DIV4;\n        config.rcc.apb2_pre = APBPrescaler::DIV2;\n        config.rcc.sys = Sysclk::PLL1_P;\n    }\n\n    #[cfg(feature = \"stm32f446re\")]\n    {\n        config.rcc.hse = Some(Hse {\n            freq: Hertz(8_000_000),\n            mode: HseMode::Oscillator,\n        });\n        config.rcc.pll_src = PllSource::HSE;\n        config.rcc.pll = Some(Pll {\n            prediv: PllPreDiv::DIV4,\n            mul: PllMul::MUL168,\n            divp: Some(PllPDiv::DIV2), // 8mhz / 4 * 168 / 2 = 168 Mhz.\n            divq: Some(PllQDiv::DIV7), // 8mhz / 4 * 168 / 7 = 48 Mhz.\n            divr: None,\n        });\n        config.rcc.ahb_pre = AHBPrescaler::DIV1;\n        config.rcc.apb1_pre = APBPrescaler::DIV4;\n        config.rcc.apb2_pre = APBPrescaler::DIV2;\n        config.rcc.sys = Sysclk::PLL1_P;\n    }\n\n    #[cfg(feature = \"stm32f767zi\")]\n    {\n        config.rcc.hse = Some(Hse {\n            freq: Hertz(8_000_000),\n            mode: HseMode::Bypass,\n        });\n        config.rcc.pll_src = PllSource::HSE;\n        config.rcc.pll = Some(Pll {\n            prediv: PllPreDiv::DIV4,\n            mul: PllMul::MUL216,\n            divp: Some(PllPDiv::DIV2), // 8mhz / 4 * 216 / 2 = 216Mhz.\n            divq: None,\n            divr: None,\n        });\n        config.rcc.ahb_pre = AHBPrescaler::DIV1;\n        config.rcc.apb1_pre = APBPrescaler::DIV4;\n        config.rcc.apb2_pre = APBPrescaler::DIV2;\n        config.rcc.sys = Sysclk::PLL1_P;\n    }\n\n    #[cfg(feature = \"stm32h563zi\")]\n    {\n        config.rcc.hsi = None;\n        config.rcc.hsi48 = Some(Default::default()); // needed for RNG\n        config.rcc.hse = Some(Hse {\n            freq: Hertz(8_000_000),\n            mode: HseMode::BypassDigital,\n        });\n        config.rcc.pll1 = Some(Pll {\n            source: PllSource::HSE,\n            prediv: PllPreDiv::DIV2,\n            mul: PllMul::MUL125,\n            divp: Some(PllDiv::DIV2),\n            divq: Some(PllDiv::DIV2),\n            divr: None,\n        });\n        config.rcc.ahb_pre = AHBPrescaler::DIV1;\n        config.rcc.apb1_pre = APBPrescaler::DIV1;\n        config.rcc.apb2_pre = APBPrescaler::DIV1;\n        config.rcc.apb3_pre = APBPrescaler::DIV1;\n        config.rcc.sys = Sysclk::PLL1_P;\n        config.rcc.voltage_scale = VoltageScale::Scale0;\n    }\n\n    #[cfg(feature = \"stm32h503rb\")]\n    {\n        config.rcc.hsi = None;\n        config.rcc.hsi48 = Some(Default::default()); // needed for RNG\n        config.rcc.hse = Some(Hse {\n            freq: Hertz(24_000_000),\n            mode: HseMode::Oscillator,\n        });\n        config.rcc.pll1 = Some(Pll {\n            source: PllSource::HSE,\n            prediv: PllPreDiv::DIV6,\n            mul: PllMul::MUL125,\n            divp: Some(PllDiv::DIV2),\n            divq: Some(PllDiv::DIV2),\n            divr: None,\n        });\n        config.rcc.ahb_pre = AHBPrescaler::DIV1;\n        config.rcc.apb1_pre = APBPrescaler::DIV1;\n        config.rcc.apb2_pre = APBPrescaler::DIV1;\n        config.rcc.apb3_pre = APBPrescaler::DIV1;\n        config.rcc.sys = Sysclk::PLL1_P;\n        config.rcc.voltage_scale = VoltageScale::Scale0;\n    }\n\n    #[cfg(feature = \"stm32g491re\")]\n    {\n        config.rcc.hse = Some(Hse {\n            freq: Hertz(24_000_000),\n            mode: HseMode::Oscillator,\n        });\n        config.rcc.pll = Some(Pll {\n            source: PllSource::HSE,\n            prediv: PllPreDiv::DIV6,\n            mul: PllMul::MUL85,\n            divp: None,\n            divq: Some(PllQDiv::DIV8), // 42.5 Mhz for fdcan.\n            divr: Some(PllRDiv::DIV2), // Main system clock at 170 MHz\n        });\n        config.rcc.mux.fdcansel = mux::Fdcansel::PLL1_Q;\n        config.rcc.sys = Sysclk::PLL1_R;\n    }\n\n    #[cfg(any(feature = \"stm32h755zi\", feature = \"stm32h753zi\"))]\n    {\n        config.rcc.hsi = Some(HSIPrescaler::DIV1);\n        config.rcc.csi = true;\n        config.rcc.hsi48 = Some(Default::default()); // needed for RNG\n        config.rcc.pll1 = Some(Pll {\n            source: PllSource::HSI,\n            prediv: PllPreDiv::DIV4,\n            mul: PllMul::MUL50,\n            divp: Some(PllDiv::DIV2),\n            divq: Some(PllDiv::DIV8), // SPI1 cksel defaults to pll1_q\n            divr: None,\n        });\n        config.rcc.pll2 = Some(Pll {\n            source: PllSource::HSI,\n            prediv: PllPreDiv::DIV4,\n            mul: PllMul::MUL50,\n            divp: Some(PllDiv::DIV8), // 100mhz\n            divq: None,\n            divr: None,\n        });\n        config.rcc.sys = Sysclk::PLL1_P; // 400 Mhz\n        config.rcc.ahb_pre = AHBPrescaler::DIV2; // 200 Mhz\n        config.rcc.apb1_pre = APBPrescaler::DIV2; // 100 Mhz\n        config.rcc.apb2_pre = APBPrescaler::DIV2; // 100 Mhz\n        config.rcc.apb3_pre = APBPrescaler::DIV2; // 100 Mhz\n        config.rcc.apb4_pre = APBPrescaler::DIV2; // 100 Mhz\n        config.rcc.voltage_scale = VoltageScale::Scale1;\n        config.rcc.mux.adcsel = mux::Adcsel::PLL2_P;\n        #[cfg(any(feature = \"stm32h755zi\"))]\n        {\n            config.rcc.supply_config = SupplyConfig::DirectSMPS;\n        }\n    }\n\n    #[cfg(any(feature = \"stm32h7a3zi\"))]\n    {\n        config.rcc.hsi = Some(HSIPrescaler::DIV1);\n        config.rcc.csi = true;\n        config.rcc.hsi48 = Some(Default::default()); // needed for RNG\n        config.rcc.pll1 = Some(Pll {\n            source: PllSource::HSI,\n            prediv: PllPreDiv::DIV4,\n            mul: PllMul::MUL35,\n            divp: Some(PllDiv::DIV2), // 280 Mhz\n            divq: Some(PllDiv::DIV8), // SPI1 cksel defaults to pll1_q\n            divr: None,\n        });\n        config.rcc.pll2 = Some(Pll {\n            source: PllSource::HSI,\n            prediv: PllPreDiv::DIV4,\n            mul: PllMul::MUL35,\n            divp: Some(PllDiv::DIV8), // 70 Mhz\n            divq: None,\n            divr: None,\n        });\n        config.rcc.sys = Sysclk::PLL1_P; // 280 Mhz\n        config.rcc.ahb_pre = AHBPrescaler::DIV1; // 280 Mhz\n        config.rcc.apb1_pre = APBPrescaler::DIV2; // 140 Mhz\n        config.rcc.apb2_pre = APBPrescaler::DIV2; // 140 Mhz\n        config.rcc.apb3_pre = APBPrescaler::DIV2; // 140 Mhz\n        config.rcc.apb4_pre = APBPrescaler::DIV2; // 140 Mhz\n        config.rcc.voltage_scale = VoltageScale::Scale0;\n        config.rcc.mux.adcsel = mux::Adcsel::PLL2_P;\n    }\n\n    #[cfg(any(feature = \"stm32l496zg\", feature = \"stm32l4a6zg\", feature = \"stm32l4r5zi\"))]\n    {\n        config.rcc.sys = Sysclk::PLL1_R;\n        config.rcc.hsi = true;\n        config.rcc.pll = Some(Pll {\n            source: PllSource::HSI,\n            prediv: PllPreDiv::DIV1,\n            mul: PllMul::MUL18,\n            divp: None,\n            divq: Some(PllQDiv::DIV6), // 48Mhz (16 / 1 * 18 / 6)\n            divr: Some(PllRDiv::DIV4), // sysclk 72Mhz clock (16 / 1 * 18 / 4)\n        });\n    }\n\n    #[cfg(feature = \"stm32wl55jc\")]\n    {\n        config.rcc.hse = Some(Hse {\n            freq: Hertz(32_000_000),\n            mode: HseMode::Bypass,\n            prescaler: HsePrescaler::DIV1,\n        });\n        config.rcc.sys = Sysclk::PLL1_R;\n        config.rcc.pll = Some(Pll {\n            source: PllSource::HSE,\n            prediv: PllPreDiv::DIV2,\n            mul: PllMul::MUL6,\n            divp: None,\n            divq: Some(PllQDiv::DIV2), // PLL1_Q clock (32 / 2 * 6 / 2), used for RNG\n            divr: Some(PllRDiv::DIV2), // sysclk 48Mhz clock (32 / 2 * 6 / 2)\n        });\n    }\n\n    #[cfg(any(feature = \"stm32l552ze\"))]\n    {\n        config.rcc.hsi = true;\n        config.rcc.sys = Sysclk::PLL1_R;\n        config.rcc.pll = Some(Pll {\n            // 110Mhz clock (16 / 4 * 55 / 2)\n            source: PllSource::HSI,\n            prediv: PllPreDiv::DIV4,\n            mul: PllMul::MUL55,\n            divp: None,\n            divq: None,\n            divr: Some(PllRDiv::DIV2),\n        });\n    }\n\n    #[cfg(any(feature = \"stm32u585ai\", feature = \"stm32u5a5zj\"))]\n    {\n        config.rcc.hsi = true;\n        config.rcc.pll1 = Some(Pll {\n            source: PllSource::HSI, // 16 MHz\n            prediv: PllPreDiv::DIV1,\n            mul: PllMul::MUL10,\n            divp: None,\n            divq: None,\n            divr: Some(PllDiv::DIV1), // 160 MHz\n        });\n        config.rcc.sys = Sysclk::PLL1_R;\n        config.rcc.voltage_range = VoltageScale::RANGE1;\n        config.rcc.hsi48 = Some(Hsi48Config { sync_from_usb: true }); // needed for USB\n    }\n\n    #[cfg(feature = \"stm32wba52cg\")]\n    {\n        config.rcc.sys = Sysclk::HSI;\n        config.rcc.mux.rngsel = mux::Rngsel::HSI;\n    }\n\n    #[cfg(feature = \"stm32wba65ri\")]\n    {\n        config.rcc.sys = Sysclk::HSI;\n        config.rcc.mux.rngsel = mux::Rngsel::HSI;\n        config.rcc.mux.sai1sel = mux::Sai1sel::HSI;\n    }\n\n    #[cfg(feature = \"stm32l073rz\")]\n    {\n        config.rcc.hsi = true;\n        config.rcc.pll = Some(Pll {\n            source: PllSource::HSI,\n            mul: PllMul::MUL4,\n            div: PllDiv::DIV2, // 32Mhz clock (16 * 4 / 2)\n        });\n        config.rcc.sys = Sysclk::PLL1_R;\n    }\n\n    #[cfg(any(feature = \"stm32l152re\"))]\n    {\n        config.rcc.hsi = true;\n        config.rcc.pll = Some(Pll {\n            source: PllSource::HSI,\n            mul: PllMul::MUL4,\n            div: PllDiv::DIV2, // 32Mhz clock (16 * 4 / 2)\n        });\n        config.rcc.sys = Sysclk::PLL1_R;\n    }\n    #[cfg(any(feature = \"stm32h7s3l8\"))]\n    {\n        config.rcc.hse = Some(Hse {\n            freq: Hertz(24_000_000),\n            mode: HseMode::Oscillator,\n        });\n        config.rcc.pll1 = Some(Pll {\n            source: PllSource::HSE,\n            prediv: PllPreDiv::DIV3,\n            mul: PllMul::MUL150,\n            divp: Some(PllDiv::DIV2),  // 600Mhz\n            divq: Some(PllDiv::DIV25), // 48Mhz\n            divr: None,\n            divs: None,\n            divt: None,\n        });\n        config.rcc.sys = Sysclk::PLL1_P; // 600 Mhz\n        config.rcc.ahb_pre = AHBPrescaler::DIV2; // 300 Mhz\n        config.rcc.apb1_pre = APBPrescaler::DIV2; // 150 Mhz\n        config.rcc.apb2_pre = APBPrescaler::DIV2; // 150 Mhz\n        config.rcc.apb4_pre = APBPrescaler::DIV2; // 150 Mhz\n        config.rcc.apb5_pre = APBPrescaler::DIV2; // 150 Mhz\n        config.rcc.voltage_scale = VoltageScale::HIGH;\n        config.rcc.mux.spi1sel = mux::Spi123sel::PLL1_Q;\n    }\n    #[cfg(any(feature = \"stm32u083rc\"))]\n    {\n        config.rcc.hsi = true;\n        config.rcc.pll = Some(Pll {\n            source: PllSource::HSI, // 16 MHz\n            prediv: PllPreDiv::DIV1,\n            mul: PllMul::MUL7,\n            divp: None,\n            divq: None,\n            divr: Some(PllRDiv::DIV2), // 56 MHz\n        });\n        config.rcc.sys = Sysclk::PLL1_R;\n        config.rcc.hsi48 = Some(Hsi48Config { sync_from_usb: true }); // needed for USB\n        config.rcc.mux.clk48sel = mux::Clk48sel::HSI48; // USB uses ICLK\n    }\n\n    config\n}\n\n#[allow(unused)]\npub fn init() -> embassy_stm32::Peripherals {\n    init_with_config(config())\n}\n\n#[allow(unused)]\npub fn init_with_config(config: Config) -> embassy_stm32::Peripherals {\n    #[cfg(any(feature = \"stm32wl55jc\", feature = \"stm32h755zi\"))]\n    {\n        // Not in shared memory, but we're not running the second core, so it's fine\n        static SHARED_DATA: core::mem::MaybeUninit<embassy_stm32::SharedData> = core::mem::MaybeUninit::uninit();\n        embassy_stm32::init_primary(config, &SHARED_DATA)\n    }\n\n    #[cfg(not(any(feature = \"stm32wl55jc\", feature = \"stm32h755zi\")))]\n    embassy_stm32::init(config)\n}\n"
  },
  {
    "path": "tests/utils/Cargo.toml",
    "content": "[package]\nname = \"test-utils\"\nversion = \"0.1.0\"\nedition = \"2024\"\nlicense = \"MIT OR Apache-2.0\"\npublish = false\n\n[dependencies]\nrand = \"0.9\"\nserial = \"0.4\"\n"
  },
  {
    "path": "tests/utils/src/bin/saturate_serial.rs",
    "content": "use std::path::Path;\nuse std::time::Duration;\nuse std::{env, io, process, thread};\n\nuse rand::{Rng, rng};\nuse serial::SerialPort;\n\npub fn main() {\n    if let Some(port_name) = env::args().nth(1) {\n        let idles = env::args().position(|x| x == \"--idles\").is_some();\n\n        println!(\"Saturating port {:?} with 115200 8N1\", port_name);\n        println!(\"Idles: {}\", idles);\n        println!(\"Process ID: {}\", process::id());\n        let mut port = serial::open(&port_name).unwrap();\n        if saturate(&mut port, idles).is_err() {\n            eprintln!(\"Unable to saturate port\");\n        }\n    } else {\n        let path = env::args().next().unwrap();\n        let basepath = Path::new(&path).with_extension(\"\");\n        let basename = basepath.file_name().unwrap();\n        eprintln!(\"USAGE: {} <port-name>\", basename.to_string_lossy());\n    }\n}\n\nfn saturate<T: SerialPort>(port: &mut T, idles: bool) -> io::Result<()> {\n    port.reconfigure(&|settings| {\n        settings.set_baud_rate(serial::Baud115200)?;\n        settings.set_char_size(serial::Bits8);\n        settings.set_parity(serial::ParityNone);\n        settings.set_stop_bits(serial::Stop1);\n        Ok(())\n    })?;\n\n    let mut written = 0;\n    let mut rng = rng();\n    loop {\n        let len = rng.random_range(1..=0x1000);\n        let buf: Vec<u8> = (written..written + len).map(|x| x as u8).collect();\n\n        port.write_all(&buf)?;\n\n        if idles {\n            let micros = rng.random_range(1..=1000) as u64;\n            println!(\"Sleeping {}us\", micros);\n            port.flush().unwrap();\n            thread::sleep(Duration::from_micros(micros));\n        }\n\n        written += len;\n        println!(\"Written: {}\", written);\n    }\n}\n"
  }
]